Repository: bonitasoft/bonita-engine Branch: dev Commit: 1734924ed557 Files: 5459 Total size: 24.0 MB Directory structure: gitextract_wsntd3xl/ ├── .github/ │ └── workflows/ │ └── build.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── bonita-engine/ │ └── build.gradle ├── bonita-engine-spring-boot-starter/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── spring/ │ │ │ └── autoconfigure/ │ │ │ ├── BonitaEngineCommonAutoConfiguration.java │ │ │ ├── BonitaEngineEventListener.java │ │ │ ├── BonitaEngineServerAutoConfiguration.java │ │ │ └── properties/ │ │ │ ├── BonitaDatabasesConfiguration.java │ │ │ └── BonitaEngineProperties.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring.factories │ └── test/ │ └── java/ │ └── org/ │ └── bonitasoft/ │ └── engine/ │ └── spring/ │ └── autoconfigure/ │ ├── BonitaEngineAutoConfigurationTest.java │ ├── BonitaEngineSpringBootStarterIT.java │ └── ClientTestApplication.java ├── bonita-engine-standalone/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ ├── BonitaDataSourceInitializer.java │ │ │ ├── BonitaDatabaseConfiguration.java │ │ │ ├── BonitaEngine.java │ │ │ ├── DatabaseUrlParser.java │ │ │ ├── DatasourceConfiguration.java │ │ │ ├── DefaultBonitaDatabaseConfigurations.java │ │ │ ├── MemoryJNDISetup.java │ │ │ ├── SimpleMemoryContext.java │ │ │ ├── SimpleMemoryContextFactory.java │ │ │ ├── SimpleNameParser.java │ │ │ └── xa/ │ │ │ ├── XAConnectionIsSameRMOverride.java │ │ │ ├── XADataSourceIsSameRMOverride.java │ │ │ └── XAResourceIsSameRMOverride.java │ │ └── resources/ │ │ └── jbossts-properties.xml │ └── test/ │ └── java/ │ └── org/ │ └── bonitasoft/ │ └── engine/ │ ├── BonitaDataSourceInitializerTest.java │ ├── BonitaEngineTest.java │ └── DatabaseUrlParserTest.java ├── bonita-integration-tests/ │ ├── benchmarks/ │ │ ├── README.md │ │ ├── build.gradle │ │ └── src/ │ │ └── jmh/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── benchmarks/ │ │ ├── PermissionCachingBenchmark.java │ │ └── TransactionSynchronizationBenchmark.java │ ├── bonita-integration-tests-client/ │ │ ├── bonita-client-http.properties │ │ ├── bonita-client-invalid.properties │ │ ├── bonita-client-local.properties │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ ├── CommonAPIIT.java │ │ │ │ ├── PrintTestsStatusRule.java │ │ │ │ ├── TestWithTechnicalUser.java │ │ │ │ ├── TestWithUser.java │ │ │ │ ├── business/ │ │ │ │ │ ├── application/ │ │ │ │ │ │ ├── TestWithCustomPage.java │ │ │ │ │ │ └── TestWithLivingApplication.java │ │ │ │ │ └── data/ │ │ │ │ │ ├── BusinessDataUpdateConnector.java │ │ │ │ │ └── ClassloaderRefresher.java │ │ │ │ ├── command/ │ │ │ │ │ └── helper/ │ │ │ │ │ ├── ProcessDeployer.java │ │ │ │ │ ├── designer/ │ │ │ │ │ │ ├── BoundaryEvent.java │ │ │ │ │ │ ├── Branch.java │ │ │ │ │ │ ├── ConditionalTransition.java │ │ │ │ │ │ ├── DefaultTransition.java │ │ │ │ │ │ ├── EndEvent.java │ │ │ │ │ │ ├── FlowNode.java │ │ │ │ │ │ ├── Fragment.java │ │ │ │ │ │ ├── Gateway.java │ │ │ │ │ │ ├── Signal.java │ │ │ │ │ │ ├── SimpleProcessDesigner.java │ │ │ │ │ │ ├── StartEvent.java │ │ │ │ │ │ ├── Transition.java │ │ │ │ │ │ ├── Trigger.java │ │ │ │ │ │ └── UserTask.java │ │ │ │ │ └── expectation/ │ │ │ │ │ ├── DocumentExpectation.java │ │ │ │ │ ├── ProcessExpectation.java │ │ │ │ │ ├── StepExpectation.java │ │ │ │ │ ├── TestUtils.java │ │ │ │ │ └── VariableExpectation.java │ │ │ │ ├── connectors/ │ │ │ │ │ ├── ConnectorExecutionIT.java │ │ │ │ │ ├── DoNothingConnector.java │ │ │ │ │ ├── FailingConnector.java │ │ │ │ │ ├── TestConnector.java │ │ │ │ │ ├── TestConnector2.java │ │ │ │ │ ├── TestConnector3.java │ │ │ │ │ ├── TestConnectorLongToExecute.java │ │ │ │ │ ├── TestConnectorThatThrowException.java │ │ │ │ │ ├── TestConnectorWithAPICall.java │ │ │ │ │ ├── TestConnectorWithConnectedResource.java │ │ │ │ │ ├── TestConnectorWithModifiedOutput.java │ │ │ │ │ ├── TestConnectorWithNotSerializableOutput.java │ │ │ │ │ ├── TestConnectorWithOutput.java │ │ │ │ │ ├── TestExternalConnector.java │ │ │ │ │ └── VariableStorage.java │ │ │ │ ├── event/ │ │ │ │ │ └── AbstractEventIT.java │ │ │ │ ├── filter/ │ │ │ │ │ └── user/ │ │ │ │ │ ├── GroupUserFilter.java │ │ │ │ │ ├── TestFilter.java │ │ │ │ │ ├── TestFilterThatThrowException.java │ │ │ │ │ ├── TestFilterThatThrowNoClassDef.java │ │ │ │ │ ├── TestFilterUsingActorName.java │ │ │ │ │ └── TestFilterWithAutoAssign.java │ │ │ │ ├── page/ │ │ │ │ │ └── PageAssert.java │ │ │ │ ├── process/ │ │ │ │ │ ├── Employee.java │ │ │ │ │ ├── Secretary.java │ │ │ │ │ └── SetDueDateConnector.java │ │ │ │ ├── search/ │ │ │ │ │ └── SlowConnector.java │ │ │ │ └── util/ │ │ │ │ ├── AssertionsUtils.java │ │ │ │ └── FunctionalMatcher.java │ │ │ └── resources/ │ │ │ ├── custom-0.1.jar.bak │ │ │ ├── mylibrary-jar.bak │ │ │ ├── mylibrary2-jar.bak │ │ │ ├── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ ├── business/ │ │ │ │ │ ├── application/ │ │ │ │ │ │ ├── applicationWithUnavailableInfo.xml │ │ │ │ │ │ └── applications.xml │ │ │ │ │ └── data/ │ │ │ │ │ ├── BusinessDataUpdateConnector.def │ │ │ │ │ ├── BusinessDataUpdateConnector.impl │ │ │ │ │ ├── findByFirstNameAndLastNameNewOrder.json │ │ │ │ │ ├── findByFirstNameFetchAddresses.json │ │ │ │ │ ├── getBusinessDataByIdAddress.json │ │ │ │ │ ├── getBusinessDataByIdEmployee.json │ │ │ │ │ └── getEmployeeByPhoneNumber.json │ │ │ │ ├── command/ │ │ │ │ │ ├── Employee.find.2.1.json │ │ │ │ │ └── web/ │ │ │ │ │ └── profile/ │ │ │ │ │ ├── AllProfiles.xml │ │ │ │ │ ├── RestoreDefaultProfiles.xml │ │ │ │ │ └── deleteExistingProfile.xml │ │ │ │ ├── connectors/ │ │ │ │ │ ├── APIAccessorConnector.impl │ │ │ │ │ ├── TestConnector.def │ │ │ │ │ ├── TestConnector.impl │ │ │ │ │ ├── TestConnector2.impl │ │ │ │ │ ├── TestConnector3.def │ │ │ │ │ ├── TestConnector3.impl │ │ │ │ │ ├── TestConnectorInJar.impl │ │ │ │ │ ├── TestConnectorLongToExecute.impl │ │ │ │ │ ├── TestConnectorThatThrowException.impl │ │ │ │ │ ├── TestConnectorWithAPICall.impl │ │ │ │ │ ├── TestConnectorWithConnectedResource.def │ │ │ │ │ ├── TestConnectorWithConnectedResource.impl │ │ │ │ │ ├── TestConnectorWithCustomType.impl │ │ │ │ │ ├── TestConnectorWithModifiedOutput.impl │ │ │ │ │ ├── TestConnectorWithNotSerializableOutput.def │ │ │ │ │ ├── TestConnectorWithNotSerializableOutput.impl │ │ │ │ │ ├── TestConnectorWithOutput.def │ │ │ │ │ ├── TestConnectorWithOutput.impl │ │ │ │ │ ├── TestExternalConnector.impl │ │ │ │ │ ├── UnknownClassConnector.impl │ │ │ │ │ ├── connector-in-jar.jar.bak │ │ │ │ │ └── connector-with-custom-type.bak │ │ │ │ ├── filter/ │ │ │ │ │ └── user/ │ │ │ │ │ ├── GroupUserFilter.impl │ │ │ │ │ ├── TestFilter.def │ │ │ │ │ ├── TestFilter.impl │ │ │ │ │ ├── TestFilterThatThrowException.impl │ │ │ │ │ ├── TestFilterThatThrowNoClassDef.impl │ │ │ │ │ ├── TestFilterUsingActorName.impl │ │ │ │ │ ├── TestFilterWithAutoAssign.impl │ │ │ │ │ └── TestFilterWithClassNotFound.impl │ │ │ │ ├── identity/ │ │ │ │ │ ├── ACME.xml │ │ │ │ │ ├── OrganizationWithSpecialCharacters.xml │ │ │ │ │ ├── complexOrganization.xml │ │ │ │ │ ├── complexOrganizationWithBadGroup.xml │ │ │ │ │ ├── hugeOrganization.xml │ │ │ │ │ ├── mixOrganization.xml │ │ │ │ │ ├── organizationFailOnDuplicates.xml │ │ │ │ │ ├── organizationWithCycle.xml │ │ │ │ │ ├── simpleOrganization.xml │ │ │ │ │ ├── simpleOrganizationDuplicates1.xml │ │ │ │ │ ├── simpleOrganizationDuplicates2.xml │ │ │ │ │ └── simpleOrganizationNoDuplicates.xml │ │ │ │ └── process/ │ │ │ │ └── actor/ │ │ │ │ ├── actorMappingWithException.xml │ │ │ │ ├── complexActorMapping.xml │ │ │ │ ├── complexActorMapping2.xml │ │ │ │ ├── complexActorMappingWithUnkownGroup.xml │ │ │ │ └── simpleActorMapping.xml │ │ │ ├── org.bonitasoft.complextypes.jar-bak │ │ │ ├── org.bonitasoft.dfgdfg.bak │ │ │ └── org.bonitasoft.plop.bak │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ ├── MultiThreadCallsIT.java │ │ │ ├── RemoteEngineIT.java │ │ │ ├── StringIndexIT.java │ │ │ ├── activity/ │ │ │ │ ├── CallActivityIT.java │ │ │ │ ├── ContractIT.java │ │ │ │ ├── GetPossibleUsersOfPendingHumanTaskIT.java │ │ │ │ ├── HumanTasksIT.java │ │ │ │ ├── LoopIT.java │ │ │ │ ├── ManualTasksIT.java │ │ │ │ ├── MultiInstanceIT.java │ │ │ │ ├── PendingTasksIT.java │ │ │ │ ├── ReceiveTasksIT.java │ │ │ │ ├── SendTaskIT.java │ │ │ │ ├── UserTaskAssignationIT.java │ │ │ │ └── work/ │ │ │ │ └── RetryWorkIT.java │ │ │ ├── application/ │ │ │ │ └── ApplicationIT.java │ │ │ ├── business/ │ │ │ │ ├── application/ │ │ │ │ │ ├── LivingApplicationIT.java │ │ │ │ │ ├── LivingApplicationImportExportIT.java │ │ │ │ │ ├── LivingApplicationMenuIT.java │ │ │ │ │ └── LivingApplicationPageIT.java │ │ │ │ └── data/ │ │ │ │ ├── BDMPostgreSQLTextFieldIT.java │ │ │ │ ├── BDMUpdateIT.java │ │ │ │ └── BDRepositoryIT.java │ │ │ ├── client/ │ │ │ │ └── BonitaClientXMLTest.java │ │ │ ├── command/ │ │ │ │ ├── AdvancedStartProcessCommandIT.java │ │ │ │ ├── CommandIT.java │ │ │ │ ├── ExecuteBDMQueryCommandIT.java │ │ │ │ ├── ExecutionExceptionCommand.java │ │ │ │ ├── IntegerCommand.java │ │ │ │ ├── MultipleStartPointsProcessCommandIT.java │ │ │ │ ├── NPECommand.java │ │ │ │ ├── ParameterizationExceptionCommand.java │ │ │ │ └── helper/ │ │ │ │ └── designer/ │ │ │ │ ├── BranchTest.java │ │ │ │ ├── DesignerTestUtils.java │ │ │ │ └── SimpleProcessDesignerTest.java │ │ │ ├── connectors/ │ │ │ │ └── RemoteConnectorExecutionIT.java │ │ │ ├── event/ │ │ │ │ ├── AbstractWaitingEventIT.java │ │ │ │ ├── EndEventIT.java │ │ │ │ ├── ErrorBoundaryEventIT.java │ │ │ │ ├── ErrorEventSubProcessIT.java │ │ │ │ ├── EventTriggerIT.java │ │ │ │ ├── InterruptingTimerBoundaryEventIT.java │ │ │ │ ├── MessageBoundaryEventIT.java │ │ │ │ ├── MessageEventIT.java │ │ │ │ ├── MessageEventSubProcessIT.java │ │ │ │ ├── NonInterruptingTimerBoundaryEventIT.java │ │ │ │ ├── SignalBoundaryEventIT.java │ │ │ │ ├── SignalEventIT.java │ │ │ │ ├── SignalEventSubProcessIT.java │ │ │ │ ├── StartEventIT.java │ │ │ │ ├── TimerBoundaryEventIT.java │ │ │ │ ├── TimerEventIT.java │ │ │ │ └── TimerEventSubProcessIT.java │ │ │ ├── filter/ │ │ │ │ └── user/ │ │ │ │ └── UserFilterIT.java │ │ │ ├── form/ │ │ │ │ └── FormMappingIT.java │ │ │ ├── identity/ │ │ │ │ ├── CustomUserInfoIT.java │ │ │ │ ├── GroupIT.java │ │ │ │ ├── MembershipIT.java │ │ │ │ ├── OrganizationIT.java │ │ │ │ ├── RoleIT.java │ │ │ │ └── UserIT.java │ │ │ ├── login/ │ │ │ │ └── PlatformLoginAPIIT.java │ │ │ ├── mdc/ │ │ │ │ ├── APICallLogIT.java │ │ │ │ ├── ConnectorExecutionLogIT.java │ │ │ │ ├── LogITUtil.java │ │ │ │ └── ProcessExecutionLogIT.java │ │ │ ├── operation/ │ │ │ │ └── OperationIT.java │ │ │ ├── page/ │ │ │ │ └── PageAPIIT.java │ │ │ ├── platform/ │ │ │ │ ├── PlatformIT.java │ │ │ │ ├── PlatformLoginIT.java │ │ │ │ └── command/ │ │ │ │ └── PlatformCommandIT.java │ │ │ ├── process/ │ │ │ │ ├── EvaluateExpressionIT.java │ │ │ │ ├── FlowPatternsIT.java │ │ │ │ ├── GatewayExecutionIT.java │ │ │ │ ├── GetProcessDefinitionIT.java │ │ │ │ ├── ProcessCategoryIT.java │ │ │ │ ├── ProcessDeletionIT.java │ │ │ │ ├── ProcessDeploymentIT.java │ │ │ │ ├── ProcessDescriptionIT.java │ │ │ │ ├── ProcessExecutionIT.java │ │ │ │ ├── ProcessManagementIT.java │ │ │ │ ├── ProcessParameterIT.java │ │ │ │ ├── ProcessResolutionIT.java │ │ │ │ ├── ProcessWithExpressionIT.java │ │ │ │ ├── StartProcessWithOperationsIT.java │ │ │ │ ├── actor/ │ │ │ │ │ ├── ExportActorMappingIT.java │ │ │ │ │ ├── ImportActorMappingIT.java │ │ │ │ │ └── ProcessActorIT.java │ │ │ │ ├── comment/ │ │ │ │ │ └── CommentIT.java │ │ │ │ ├── data/ │ │ │ │ │ ├── ActivityDataDefinitionIT.java │ │ │ │ │ ├── ActivityDataInstanceIT.java │ │ │ │ │ ├── ProcessDataDefinitionIT.java │ │ │ │ │ └── ProcessDataInstanceIT.java │ │ │ │ ├── document/ │ │ │ │ │ └── DocumentIT.java │ │ │ │ └── instance/ │ │ │ │ ├── AbortProcessInstanceIT.java │ │ │ │ ├── AbstractProcessInstanceIT.java │ │ │ │ ├── CancelProcessInstanceIT.java │ │ │ │ ├── InvolvedInProcessInstanceIT.java │ │ │ │ └── ProcessInstanceIT.java │ │ │ ├── profile/ │ │ │ │ ├── AbstractProfileIT.java │ │ │ │ ├── ProfileCommunityIT.java │ │ │ │ ├── ProfileIT.java │ │ │ │ └── ProfileMemberIT.java │ │ │ ├── search/ │ │ │ │ ├── SearchActivityInstanceIT.java │ │ │ │ ├── SearchCommentIT.java │ │ │ │ ├── SearchProcessDefinitionIT.java │ │ │ │ ├── SearchProcessDeploymentInfosCanBeStartedByIT.java │ │ │ │ ├── SearchProcessDeploymentInfosCanBeStartedByUsersManagedByIT.java │ │ │ │ ├── SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksForIT.java │ │ │ │ ├── SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksIT.java │ │ │ │ ├── SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedByIT.java │ │ │ │ ├── SearchProcessInstanceIT.java │ │ │ │ └── SearchUncategorizedProcessDeploymentInfosCanBeStartedByIT.java │ │ │ ├── supervisor/ │ │ │ │ ├── ProcessSupervisedIT.java │ │ │ │ └── SupervisorIT.java │ │ │ └── tenant/ │ │ │ └── TenantMaintenanceIT.java │ │ └── resources/ │ │ ├── application-to-test-permissions.xml │ │ ├── logback-test.xml │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ ├── application/ │ │ │ ├── superAdminApp.xml │ │ │ └── testApp.xml │ │ └── connectors/ │ │ ├── TestConnector.xml │ │ └── TestConnector_Implementation.xml │ ├── bonita-integration-tests-local/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ ├── bpm/ │ │ │ │ │ └── CommonBPMServicesTest.java │ │ │ │ └── test/ │ │ │ │ └── util/ │ │ │ │ └── TestUtil.java │ │ │ └── resources/ │ │ │ └── logback-test.xml │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ ├── CallableWithException.java │ │ │ ├── DeleteEventTriggerInstanceIT.java │ │ │ ├── MockQueriableLogSessionProviderImpl.java │ │ │ ├── MockQueriableLoggerStrategy.java │ │ │ ├── PageAPILocalIT.java │ │ │ ├── RunnableWithException.java │ │ │ ├── SQLUtils.java │ │ │ ├── application/ │ │ │ │ └── installer/ │ │ │ │ ├── ApplicationInstallerIT.java │ │ │ │ └── ApplicationInstallerUpdateIT.java │ │ │ ├── archive/ │ │ │ │ ├── ArchiveServiceIT.java │ │ │ │ └── model/ │ │ │ │ ├── TestLogBuilder.java │ │ │ │ └── TestLogBuilderFactory.java │ │ │ ├── authentication/ │ │ │ │ └── AuthenticationServiceIT.java │ │ │ ├── bpm/ │ │ │ │ ├── ActorMappingServiceIT.java │ │ │ │ ├── CategoryServiceIT.java │ │ │ │ ├── DocumentServiceIT.java │ │ │ │ ├── EventInstanceRepositoryIT.java │ │ │ │ ├── GatewayExecutionLocalIT.java │ │ │ │ ├── GatewayInstanceServiceIT.java │ │ │ │ ├── NodeConfigurationIT.java │ │ │ │ ├── OperationServiceIT.java │ │ │ │ ├── ProcessDefinitionServiceIT.java │ │ │ │ ├── ProcessInstanceServiceIT.java │ │ │ │ ├── RecoveryMechanismIT.java │ │ │ │ ├── SupervisorServiceIT.java │ │ │ │ ├── connector/ │ │ │ │ │ ├── ConnectorExecutionsLocalIT.java │ │ │ │ │ └── ConnectorInstanceServiceIT.java │ │ │ │ ├── event/ │ │ │ │ │ ├── EventInstanceServiceIT.java │ │ │ │ │ ├── LocalInterruptingTimerBoundaryEventIT.java │ │ │ │ │ └── LocalTimerEventIT.java │ │ │ │ ├── failure/ │ │ │ │ │ ├── FlowNodeFailureIT.java │ │ │ │ │ └── ProcessInstanceFailureIT.java │ │ │ │ ├── flownode/ │ │ │ │ │ └── FlowNodeInstanceServiceIT.java │ │ │ │ └── process/ │ │ │ │ ├── AddCommentConnector.java │ │ │ │ └── DeleteProcessInstancesIT.java │ │ │ ├── business/ │ │ │ │ ├── application/ │ │ │ │ │ └── importer/ │ │ │ │ │ └── ApplicationImporterIT.java │ │ │ │ └── data/ │ │ │ │ ├── BDRepositoryLocalIT.java │ │ │ │ ├── DataRetentionBdmTrackingIT.java │ │ │ │ └── TransactionTimeoutEntityManagerIT.java │ │ │ ├── cache/ │ │ │ │ └── ehcache/ │ │ │ │ └── CacheConfigurationIT.java │ │ │ ├── classloader/ │ │ │ │ ├── ClassLoaderIT.java │ │ │ │ ├── ClassLoaderServiceIT.java │ │ │ │ ├── GlobalClass1.java │ │ │ │ ├── GlobalClass2.java │ │ │ │ ├── GlobalClass3.java │ │ │ │ ├── LocalClass1.java │ │ │ │ ├── LocalClass2.java │ │ │ │ ├── LocalClass3.java │ │ │ │ ├── LocalClass4.java │ │ │ │ ├── OnlyInPathClass1.java │ │ │ │ └── SharedClass1.java │ │ │ ├── command/ │ │ │ │ └── CommandServiceIT.java │ │ │ ├── core/ │ │ │ │ └── form/ │ │ │ │ └── impl/ │ │ │ │ ├── FormMappingServiceIT.java │ │ │ │ └── custom/ │ │ │ │ ├── CustomAuthorizationRuleMappingImpl.java │ │ │ │ └── CustomIsProcessInitiatorRule.java │ │ │ ├── data/ │ │ │ │ ├── instance/ │ │ │ │ │ ├── DataInstanceIntegrationLocalIT.java │ │ │ │ │ ├── DataInstanceServiceIT.java │ │ │ │ │ ├── LightEmployee.java │ │ │ │ │ └── TransientDataInstanceServiceIT.java │ │ │ │ └── model/ │ │ │ │ ├── Address.java │ │ │ │ ├── Employee.java │ │ │ │ └── LightEmployee.java │ │ │ ├── dependency/ │ │ │ │ └── DependencyServiceIT.java │ │ │ ├── expression/ │ │ │ │ └── ExpressionServiceIT.java │ │ │ ├── identity/ │ │ │ │ └── IdentityServiceIT.java │ │ │ ├── job/ │ │ │ │ ├── AddJobCommand.java │ │ │ │ ├── JobExecutionIT.java │ │ │ │ └── ThrowsExceptionJob.java │ │ │ ├── login/ │ │ │ │ └── LoginAPIIT.java │ │ │ ├── page/ │ │ │ │ └── PageMappingServiceIT.java │ │ │ ├── persistence/ │ │ │ │ └── PersistenceIT.java │ │ │ ├── platform/ │ │ │ │ ├── auth/ │ │ │ │ │ └── PlatformAuthenticationServiceIT.java │ │ │ │ ├── command/ │ │ │ │ │ └── PlatformCommandServiceIT.java │ │ │ │ └── login/ │ │ │ │ └── PlatformLoginServiceIT.java │ │ │ ├── profile/ │ │ │ │ └── ProfileServiceIT.java │ │ │ ├── recorder/ │ │ │ │ └── RecorderIT.java │ │ │ ├── resources/ │ │ │ │ └── TenantResourcesServiceIT.java │ │ │ ├── scheduler/ │ │ │ │ ├── impl/ │ │ │ │ │ ├── JobThatMayThrowErrorOrJobException.java │ │ │ │ │ ├── SchedulerServiceIT.java │ │ │ │ │ └── WaitForIncrementJobToHaveValue.java │ │ │ │ ├── job/ │ │ │ │ │ ├── DoNothingJob.java │ │ │ │ │ ├── GroupJob.java │ │ │ │ │ ├── IncrementItselfJob.java │ │ │ │ │ ├── IncrementVariableJob.java │ │ │ │ │ ├── JobSemaphore.java │ │ │ │ │ ├── ReleaseWaitersJob.java │ │ │ │ │ ├── ThrowsExceptionJob.java │ │ │ │ │ ├── UpdateVariable.java │ │ │ │ │ └── VariableStorage.java │ │ │ │ └── trigger/ │ │ │ │ └── UnixCronTriggerForTest.java │ │ │ ├── session/ │ │ │ │ ├── PlatformSessionServiceIT.java │ │ │ │ └── SessionServiceIT.java │ │ │ ├── tenant/ │ │ │ │ └── TenantMaintenanceLocalIT.java │ │ │ ├── test/ │ │ │ │ ├── APIMethodLocalIT.java │ │ │ │ ├── BPMLocalIT.java │ │ │ │ ├── BlockingConnector.java │ │ │ │ ├── CommonAPILocalIT.java │ │ │ │ ├── PermissionAPIIT.java │ │ │ │ ├── ProcessArchiveIT.java │ │ │ │ ├── ProcessWithExpressionLocalIT.java │ │ │ │ └── TestHandler.java │ │ │ └── work/ │ │ │ └── WorkServiceIT.java │ │ └── resources/ │ │ ├── applications-importer/ │ │ │ ├── mixedLegacyAndApplicationLinks.xml │ │ │ ├── multipleApplicationLinks.xml │ │ │ └── oneApplicationLink.xml │ │ ├── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── classloader/ │ │ │ ├── NotInPathGlobal.jar │ │ │ ├── NotInPathLocal.jar │ │ │ ├── NotInPathShared.jar │ │ │ └── resource.txt │ │ └── simple-app-1.0.0-SNAPSHOT-local.bconf │ ├── bonita-integration-tests-web/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ ├── test/ │ │ │ │ │ └── toolkit/ │ │ │ │ │ ├── AbstractJUnitTest.java │ │ │ │ │ ├── bpm/ │ │ │ │ │ │ ├── AbstractManualTask.java │ │ │ │ │ │ ├── ProcessVariable.java │ │ │ │ │ │ ├── TestActor.java │ │ │ │ │ │ ├── TestCase.java │ │ │ │ │ │ ├── TestCaseFactory.java │ │ │ │ │ │ ├── TestCategory.java │ │ │ │ │ │ ├── TestCategoryFactory.java │ │ │ │ │ │ ├── TestHumanTask.java │ │ │ │ │ │ ├── TestProcess.java │ │ │ │ │ │ ├── TestProcessFactory.java │ │ │ │ │ │ └── process/ │ │ │ │ │ │ ├── TestActorMemberFactory.java │ │ │ │ │ │ ├── TestProcessConnector.java │ │ │ │ │ │ └── TestProcessConnectorFactory.java │ │ │ │ │ ├── exception/ │ │ │ │ │ │ ├── NextActivityIsNotAllowedStateException.java │ │ │ │ │ │ ├── NoActivityLeftException.java │ │ │ │ │ │ └── TestToolkitException.java │ │ │ │ │ ├── organization/ │ │ │ │ │ │ ├── AdminUser.java │ │ │ │ │ │ ├── IdentityAccessor.java │ │ │ │ │ │ ├── TestGroup.java │ │ │ │ │ │ ├── TestGroupFactory.java │ │ │ │ │ │ ├── TestMembership.java │ │ │ │ │ │ ├── TestMembershipFactory.java │ │ │ │ │ │ ├── TestRole.java │ │ │ │ │ │ ├── TestRoleFactory.java │ │ │ │ │ │ ├── TestToolkitCtx.java │ │ │ │ │ │ ├── TestUser.java │ │ │ │ │ │ └── TestUserFactory.java │ │ │ │ │ └── server/ │ │ │ │ │ ├── MockHttpServletRequest.java │ │ │ │ │ ├── MockHttpServletResponse.java │ │ │ │ │ └── MockHttpSession.java │ │ │ │ └── web/ │ │ │ │ ├── rest/ │ │ │ │ │ └── server/ │ │ │ │ │ └── api/ │ │ │ │ │ └── page/ │ │ │ │ │ └── builder/ │ │ │ │ │ └── PageItemBuilder.java │ │ │ │ └── test/ │ │ │ │ ├── AbstractConsoleTest.java │ │ │ │ └── AbstractJUnitWebTest.java │ │ │ └── resources/ │ │ │ ├── logback.xml │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── test/ │ │ │ └── toolkit/ │ │ │ └── connector/ │ │ │ ├── TestConnector.impl │ │ │ └── TestConnector.java │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ ├── console/ │ │ │ │ └── common/ │ │ │ │ └── server/ │ │ │ │ ├── auth/ │ │ │ │ │ └── impl/ │ │ │ │ │ └── standard/ │ │ │ │ │ └── StandardAuthenticationManagerImplIT.java │ │ │ │ └── filter/ │ │ │ │ └── PathTraversalProtectionIT.java │ │ │ └── web/ │ │ │ └── rest/ │ │ │ └── server/ │ │ │ ├── WaitUntil.java │ │ │ ├── api/ │ │ │ │ ├── application/ │ │ │ │ │ └── APIApplicationIT.java │ │ │ │ ├── bpm/ │ │ │ │ │ ├── cases/ │ │ │ │ │ │ ├── APIArchivedCaseIT.java │ │ │ │ │ │ ├── APIArchivedCommentIT.java │ │ │ │ │ │ ├── APICaseDocumentAnotherIT.java │ │ │ │ │ │ ├── APICaseDocumentIT.java │ │ │ │ │ │ ├── APICaseIT.java │ │ │ │ │ │ ├── APICaseVariableIT.java │ │ │ │ │ │ └── APICommentIT.java │ │ │ │ │ ├── flownode/ │ │ │ │ │ │ ├── APIActivityIT.java │ │ │ │ │ │ ├── APIHumanTaskIT.java │ │ │ │ │ │ ├── APITaskIT.java │ │ │ │ │ │ └── archive/ │ │ │ │ │ │ ├── APIArchivedActivityIT.java │ │ │ │ │ │ └── APIArchivedHumanTaskIT.java │ │ │ │ │ └── process/ │ │ │ │ │ ├── APIActorIntegrationIT.java │ │ │ │ │ ├── APIActorMemberIT.java │ │ │ │ │ ├── APICategoryIT.java │ │ │ │ │ ├── APIProcessCategoryIT.java │ │ │ │ │ ├── APIProcessConnectorDependencyIT.java │ │ │ │ │ ├── APIProcessConnectorIT.java │ │ │ │ │ ├── APIProcessIT.java │ │ │ │ │ └── APIProcessResolutionProblemIT.java │ │ │ │ ├── organization/ │ │ │ │ │ ├── APIGroupIT.java │ │ │ │ │ ├── APIMembershipIT.java │ │ │ │ │ ├── APIPersonalContactDataIT.java │ │ │ │ │ ├── APIProfessionalContactDataIT.java │ │ │ │ │ ├── APIRoleIT.java │ │ │ │ │ ├── APIUserAnotherIT.java │ │ │ │ │ └── APIUserIT.java │ │ │ │ └── page/ │ │ │ │ └── APIPageIT.java │ │ │ ├── datastore/ │ │ │ │ └── bpm/ │ │ │ │ ├── CommentDatastoreIntegrationIT.java │ │ │ │ ├── cases/ │ │ │ │ │ ├── ArchivedCaseDatastoreIT.java │ │ │ │ │ └── CaseDatastoreIT.java │ │ │ │ └── flownode/ │ │ │ │ └── HumanTaskDatastoreIT.java │ │ │ └── engineclient/ │ │ │ ├── CaseEngineClientIT.java │ │ │ ├── HumanTaskEngineClientIT.java │ │ │ └── ProcessEngineClientIT.java │ │ └── resources/ │ │ └── appLinkDescriptor.xml │ ├── bonita-query-tests/ │ │ ├── build.gradle │ │ └── src/ │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ ├── application/ │ │ │ │ └── ApplicationQueriesTest.java │ │ │ ├── core/ │ │ │ │ ├── contract/ │ │ │ │ │ └── data/ │ │ │ │ │ └── ContractDataTest.java │ │ │ │ ├── form/ │ │ │ │ │ └── FormMappingTest.java │ │ │ │ └── process/ │ │ │ │ └── instance/ │ │ │ │ └── model/ │ │ │ │ ├── ActorMappingTest.java │ │ │ │ ├── ArchiveFlowNodeInstanceTest.java │ │ │ │ ├── ArchiveProcessInstanceQueriesTest.java │ │ │ │ ├── BPMEventQueriesTest.java │ │ │ │ ├── CommentsTest.java │ │ │ │ ├── ConnectorInstanceQueriesTest.java │ │ │ │ ├── CustomUserInfoQueriesTest.java │ │ │ │ ├── EventTriggerInstanceQueriesTest.java │ │ │ │ ├── FlowNodeInstanceTest.java │ │ │ │ ├── ProcessDefinitionQueriesTest.java │ │ │ │ ├── ProcessDeploymentInfoQueriesTest.java │ │ │ │ ├── ProcessInstanceQueriesTest.java │ │ │ │ ├── RoleTest.java │ │ │ │ ├── SADataInstanceQueriesTest.java │ │ │ │ ├── SupervisorQueriesTest.java │ │ │ │ ├── UserMembershipTest.java │ │ │ │ └── UserTest.java │ │ │ ├── document/ │ │ │ │ └── DocumentQueryTest.java │ │ │ ├── page/ │ │ │ │ └── PageQueriesTest.java │ │ │ ├── parameter/ │ │ │ │ └── ParameterTest.java │ │ │ ├── platform/ │ │ │ │ ├── command/ │ │ │ │ │ └── model/ │ │ │ │ │ └── PlatformCommandTest.java │ │ │ │ └── model/ │ │ │ │ └── impl/ │ │ │ │ └── PlatformTest.java │ │ │ ├── profile/ │ │ │ │ └── ProfilesTest.java │ │ │ ├── queriablelogger/ │ │ │ │ └── model/ │ │ │ │ └── QueriableLogTest.java │ │ │ ├── resources/ │ │ │ │ ├── DependencyServiceQueriesTest.java │ │ │ │ ├── ProcessResourceServiceQueriesTest.java │ │ │ │ └── TenantResourceServiceQueriesTest.java │ │ │ ├── scheduler/ │ │ │ │ └── SchedulerQueryTest.java │ │ │ └── test/ │ │ │ └── persistence/ │ │ │ ├── TestLocalSessionFactoryBuilder.java │ │ │ ├── builder/ │ │ │ │ ├── ActivityInstanceBuilder.java │ │ │ │ ├── ActorBuilder.java │ │ │ │ ├── ActorMemberBuilder.java │ │ │ │ ├── ApplicationBuilder.java │ │ │ │ ├── ApplicationMenuBuilder.java │ │ │ │ ├── ApplicationPageBuilder.java │ │ │ │ ├── BARResourceBuilder.java │ │ │ │ ├── BoundaryInstanceBuilder.java │ │ │ │ ├── CallActivityInstanceBuilder.java │ │ │ │ ├── ConnectorInstanceBuilder.java │ │ │ │ ├── CustomUserInfoDefinitionBuilder.java │ │ │ │ ├── CustomUserInfoValueBuilder.java │ │ │ │ ├── FlowNodeInstanceBuilder.java │ │ │ │ ├── GatewayInstanceBuilder.java │ │ │ │ ├── GroupBuilder.java │ │ │ │ ├── JobDescriptorBuilder.java │ │ │ │ ├── JobLogBuilder.java │ │ │ │ ├── LoopActivityInstanceBuilder.java │ │ │ │ ├── MessageInstanceBuilder.java │ │ │ │ ├── PageBuilder.java │ │ │ │ ├── PendingActivityMappingBuilder.java │ │ │ │ ├── PersistentObjectBuilder.java │ │ │ │ ├── ProcessInstanceBuilder.java │ │ │ │ ├── ProfileBuilder.java │ │ │ │ ├── ProfileMemberBuilder.java │ │ │ │ ├── RoleBuilder.java │ │ │ │ ├── SupervisorBuilder.java │ │ │ │ ├── TenantResourceBuilder.java │ │ │ │ ├── UserBuilder.java │ │ │ │ ├── UserMembershipBuilder.java │ │ │ │ ├── UserTaskInstanceBuilder.java │ │ │ │ ├── WaitingMessageEventBuilder.java │ │ │ │ └── archive/ │ │ │ │ ├── ArchivedFlowNodeInstanceBuilder.java │ │ │ │ ├── ArchivedPersistentObjectBuilder.java │ │ │ │ └── ArchivedUserTaskInstanceBuilder.java │ │ │ ├── jdbc/ │ │ │ │ └── JdbcRowMapper.java │ │ │ └── repository/ │ │ │ ├── ApplicationRepository.java │ │ │ ├── BPMEventRepository.java │ │ │ ├── CommentRepository.java │ │ │ ├── ConnectorInstanceRepository.java │ │ │ ├── ContractDataRepository.java │ │ │ ├── CustomUserInfoRepository.java │ │ │ ├── DependencyRepository.java │ │ │ ├── DocumentRepository.java │ │ │ ├── FlowNodeInstanceRepository.java │ │ │ ├── JobRepository.java │ │ │ ├── PageRepository.java │ │ │ ├── PlatformRepository.java │ │ │ ├── ProcessDeploymentInfoRepository.java │ │ │ ├── ProcessInstanceRepository.java │ │ │ ├── ProcessResourceRepository.java │ │ │ ├── ProfileRepository.java │ │ │ ├── RoleRepository.java │ │ │ ├── SADataInstanceRepository.java │ │ │ ├── SupervisorRepository.java │ │ │ ├── TenantResourceRepository.java │ │ │ ├── TestRepository.java │ │ │ ├── UserMembershipRepository.java │ │ │ └── UserRepository.java │ │ └── resources/ │ │ ├── datasource/ │ │ │ ├── datasource-dependency-h2.xml │ │ │ └── datasource-dependency-postgres.xml │ │ ├── logback-test.xml │ │ └── testContext.xml │ └── bonita-test-utils/ │ ├── build.gradle │ └── src/ │ └── main/ │ ├── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ ├── connectors/ │ │ │ └── TestConnectorEngineExecutionContext.java │ │ ├── matchers/ │ │ │ └── NameMatcher.java │ │ └── test/ │ │ ├── APITestUtil.java │ │ ├── BDMTestUtil.java │ │ ├── BuildTestUtil.java │ │ ├── CommonTestUtil.java │ │ ├── PlatformTestUtil.java │ │ ├── Repeat.java │ │ ├── RepeatRule.java │ │ ├── StartProcessUntilStep.java │ │ ├── TestStates.java │ │ ├── WaitUntil.java │ │ ├── check/ │ │ │ ├── CheckNbAssignedTaskOf.java │ │ │ ├── CheckNbOfActivities.java │ │ │ ├── CheckNbOfArchivedActivities.java │ │ │ ├── CheckNbOfArchivedActivityInstances.java │ │ │ ├── CheckNbOfHumanTasks.java │ │ │ ├── CheckNbOfOpenActivities.java │ │ │ ├── CheckNbOfProcessInstances.java │ │ │ ├── CheckNbPendingTaskOf.java │ │ │ ├── CheckNbPendingTasksForUserUsingSearch.java │ │ │ └── CheckProcessInstanceIsArchived.java │ │ └── wait/ │ │ ├── WaitForActivity.java │ │ ├── WaitForArchivedActivity.java │ │ ├── WaitForAssignedStep.java │ │ ├── WaitForCompletedArchivedStep.java │ │ ├── WaitForDataValue.java │ │ ├── WaitForEvent.java │ │ ├── WaitForFinalArchivedActivity.java │ │ ├── WaitForFlowNode.java │ │ ├── WaitForPendingTasks.java │ │ ├── WaitForStep.java │ │ └── WaitProcessToFinishAndBeArchived.java │ └── resources/ │ └── org/ │ └── bonitasoft/ │ └── engine/ │ └── connectors/ │ ├── TestConnectorEngineExecutionContext.def │ └── TestConnectorEngineExecutionContext.impl ├── bonita-test-api/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── test/ │ │ │ ├── ClientEventUtil.java │ │ │ ├── TestDatabaseConfigurator.java │ │ │ ├── TestEngine.java │ │ │ ├── TestEngineImpl.java │ │ │ ├── http/ │ │ │ │ ├── BonitaHttpServer.java │ │ │ │ └── JettyServer.java │ │ │ ├── internal/ │ │ │ │ ├── EngineCommander.java │ │ │ │ └── EngineStarter.java │ │ │ └── junit/ │ │ │ └── BonitaEngineRule.java │ │ └── resources/ │ │ └── logback-test.xml │ └── test/ │ └── java/ │ └── org/ │ └── bonitasoft/ │ └── engine/ │ └── test/ │ ├── TestEngineImplTest.java │ ├── internal/ │ │ └── EngineStarterTest.java │ └── junit/ │ └── BonitaEngineRuleTest.java ├── bpm/ │ ├── bonita-api/ │ │ └── bonita-server-api-http/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── api/ │ │ │ └── internal/ │ │ │ └── servlet/ │ │ │ ├── EngineInitializerListener.java │ │ │ ├── HttpAPIServlet.java │ │ │ ├── HttpAPIServletCall.java │ │ │ ├── ServletCall.java │ │ │ └── impl/ │ │ │ └── XmlConverter.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── api/ │ │ └── internal/ │ │ └── servlet/ │ │ ├── EngineInitializerListenerTest.java │ │ ├── HttpAPIServletCallTest.java │ │ ├── HttpAPIServletTest.java │ │ ├── ServletCallDeserializationFilterTest.java │ │ └── impl/ │ │ └── XmlConverterTest.java │ ├── bonita-client/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ ├── api/ │ │ │ │ ├── APIClient.java │ │ │ │ ├── ApiAccessType.java │ │ │ │ ├── BonitaStackTraceElementConverter.java │ │ │ │ ├── HTTPServerAPI.java │ │ │ │ ├── PlatformAPIAccessor.java │ │ │ │ ├── TcpDestination.java │ │ │ │ ├── TenantAPIAccessor.java │ │ │ │ └── impl/ │ │ │ │ ├── LocalServerAPIFactory.java │ │ │ │ └── XmlConverter.java │ │ │ ├── bdm/ │ │ │ │ ├── BusinessObjectDAOFactory.java │ │ │ │ ├── BusinessObjectDaoCreationException.java │ │ │ │ └── package-info.java │ │ │ ├── exception/ │ │ │ │ ├── UnableToReadBonitaClientConfiguration.java │ │ │ │ └── UnknownAPITypeException.java │ │ │ └── util/ │ │ │ └── APITypeManager.java │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ ├── api/ │ │ │ │ ├── APIClientTest.java │ │ │ │ ├── BusinessObjectDAOUsingAPIClientTest.java │ │ │ │ ├── HTTPServerAPIIT.java │ │ │ │ ├── HTTPServerAPITest.java │ │ │ │ └── impl/ │ │ │ │ └── XmlConverterTest.java │ │ │ ├── bar/ │ │ │ │ └── BusinessArchiveTest.java │ │ │ ├── bdm/ │ │ │ │ ├── BusinessObjectDAOFactoryTest.java │ │ │ │ ├── DummyDAO.java │ │ │ │ └── DummyDAOImpl.java │ │ │ └── util/ │ │ │ └── APITypeManagerTest.java │ │ └── resources/ │ │ ├── myRealm.properties │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── bar/ │ │ ├── MyProcess--1.0.bar │ │ └── testBuy_a_mini_extended--6.1.bar │ ├── bonita-common/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ ├── InvalidFileFormatException.java │ │ │ │ ├── api/ │ │ │ │ │ ├── APIAccessor.java │ │ │ │ │ ├── ApplicationAPI.java │ │ │ │ │ ├── BusinessDataAPI.java │ │ │ │ │ ├── CommandAPI.java │ │ │ │ │ ├── CustomUserInfoAPI.java │ │ │ │ │ ├── DocumentAPI.java │ │ │ │ │ ├── Experimental.java │ │ │ │ │ ├── GroupAPI.java │ │ │ │ │ ├── IdentityAPI.java │ │ │ │ │ ├── ImportError.java │ │ │ │ │ ├── ImportStatus.java │ │ │ │ │ ├── Internal.java │ │ │ │ │ ├── Logger.java │ │ │ │ │ ├── LoginAPI.java │ │ │ │ │ ├── MaintenanceAPI.java │ │ │ │ │ ├── MembershipAPI.java │ │ │ │ │ ├── NoSessionRequired.java │ │ │ │ │ ├── OrganizationAPI.java │ │ │ │ │ ├── PageAPI.java │ │ │ │ │ ├── PermissionAPI.java │ │ │ │ │ ├── PlatformAPI.java │ │ │ │ │ ├── PlatformCommandAPI.java │ │ │ │ │ ├── PlatformLoginAPI.java │ │ │ │ │ ├── ProcessAPI.java │ │ │ │ │ ├── ProcessManagementAPI.java │ │ │ │ │ ├── ProcessRuntimeAPI.java │ │ │ │ │ ├── ProfileAPI.java │ │ │ │ │ ├── RoleAPI.java │ │ │ │ │ ├── TenantAdministrationAPI.java │ │ │ │ │ ├── UserAPI.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ └── ClientInterceptor.java │ │ │ │ │ ├── internal/ │ │ │ │ │ │ ├── ServerAPI.java │ │ │ │ │ │ ├── ServerWrappedException.java │ │ │ │ │ │ └── package-info.java │ │ │ │ │ ├── package-info.java │ │ │ │ │ ├── permission/ │ │ │ │ │ │ ├── APICallContext.java │ │ │ │ │ │ └── PermissionRule.java │ │ │ │ │ ├── platform/ │ │ │ │ │ │ └── PlatformInformationAPI.java │ │ │ │ │ └── result/ │ │ │ │ │ ├── ExecutionResult.java │ │ │ │ │ ├── Status.java │ │ │ │ │ ├── StatusCode.java │ │ │ │ │ └── StatusContext.java │ │ │ │ ├── bdm/ │ │ │ │ │ ├── BusinessObjectModelValidationException.java │ │ │ │ │ ├── Entity.java │ │ │ │ │ ├── dao/ │ │ │ │ │ │ └── BusinessObjectDAO.java │ │ │ │ │ ├── lazy/ │ │ │ │ │ │ └── LazyLoaded.java │ │ │ │ │ ├── serialization/ │ │ │ │ │ │ ├── BusinessDataObjectMapper.java │ │ │ │ │ │ ├── CustomLocalDateDeserializer.java │ │ │ │ │ │ ├── CustomLocalDateSerializer.java │ │ │ │ │ │ ├── CustomLocalDateTimeDeserializer.java │ │ │ │ │ │ ├── CustomLocalDateTimeSerializer.java │ │ │ │ │ │ ├── CustomOffsetDateTimeDeserializer.java │ │ │ │ │ │ └── CustomOffsetDateTimeSerializer.java │ │ │ │ │ └── validator/ │ │ │ │ │ ├── BusinessObjectModelValidator.java │ │ │ │ │ ├── UniqueNameValidator.java │ │ │ │ │ ├── ValidationStatus.java │ │ │ │ │ └── rule/ │ │ │ │ │ ├── BusinessObjectModelValidationRule.java │ │ │ │ │ ├── BusinessObjectValidationRule.java │ │ │ │ │ ├── FieldValidationRule.java │ │ │ │ │ ├── IndexValidationRule.java │ │ │ │ │ ├── MultipleAggregationToItselfValidationRule.java │ │ │ │ │ ├── QueryParameterValidationRule.java │ │ │ │ │ ├── QueryValidationRule.java │ │ │ │ │ ├── SimpleFieldValidationRule.java │ │ │ │ │ ├── UniqueConstraintValidationRule.java │ │ │ │ │ ├── UniqueNameValidationRule.java │ │ │ │ │ ├── UniqueSimpleNameValidationRule.java │ │ │ │ │ ├── ValidationRule.java │ │ │ │ │ └── composition/ │ │ │ │ │ ├── AggregationAndCompositionValidationRule.java │ │ │ │ │ ├── CyclicCompositionValidationRule.java │ │ │ │ │ └── UniquenessCompositionValidationRule.java │ │ │ │ ├── bpm/ │ │ │ │ │ ├── ArchivedElement.java │ │ │ │ │ ├── ArchivedRestElement.java │ │ │ │ │ ├── BaseRestElement.java │ │ │ │ │ ├── actor/ │ │ │ │ │ │ ├── ActorCriterion.java │ │ │ │ │ │ ├── ActorInstance.java │ │ │ │ │ │ ├── ActorMappingExportException.java │ │ │ │ │ │ ├── ActorMappingImportException.java │ │ │ │ │ │ ├── ActorNotFoundException.java │ │ │ │ │ │ ├── ActorUpdater.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ └── ActorInstanceImpl.java │ │ │ │ │ ├── businessdata/ │ │ │ │ │ │ ├── BusinessDataQueryMetadata.java │ │ │ │ │ │ ├── BusinessDataQueryResult.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── BusinessDataQueryMetadataImpl.java │ │ │ │ │ │ └── BusinessDataQueryResultImpl.java │ │ │ │ │ ├── category/ │ │ │ │ │ │ ├── Category.java │ │ │ │ │ │ ├── CategoryCriterion.java │ │ │ │ │ │ ├── CategoryNotFoundException.java │ │ │ │ │ │ ├── CategoryUpdater.java │ │ │ │ │ │ ├── impl/ │ │ │ │ │ │ │ └── CategoryImpl.java │ │ │ │ │ │ └── package-info.java │ │ │ │ │ ├── comment/ │ │ │ │ │ │ ├── ArchivedComment.java │ │ │ │ │ │ ├── ArchivedCommentsSearchDescriptor.java │ │ │ │ │ │ ├── Comment.java │ │ │ │ │ │ ├── SearchCommentsDescriptor.java │ │ │ │ │ │ ├── impl/ │ │ │ │ │ │ │ ├── ArchivedCommentImpl.java │ │ │ │ │ │ │ └── CommentImpl.java │ │ │ │ │ │ └── package-info.java │ │ │ │ │ ├── connector/ │ │ │ │ │ │ ├── ArchiveConnectorInstancesSearchDescriptor.java │ │ │ │ │ │ ├── ArchivedConnectorInstance.java │ │ │ │ │ │ ├── ConnectorCriterion.java │ │ │ │ │ │ ├── ConnectorDefinitionWithInputValues.java │ │ │ │ │ │ ├── ConnectorExecutionException.java │ │ │ │ │ │ ├── ConnectorImplementationDescriptor.java │ │ │ │ │ │ ├── ConnectorInstance.java │ │ │ │ │ │ ├── ConnectorInstanceCriterion.java │ │ │ │ │ │ ├── ConnectorInstanceNotFoundException.java │ │ │ │ │ │ ├── ConnectorInstanceWithFailureInfo.java │ │ │ │ │ │ ├── ConnectorInstancesSearchDescriptor.java │ │ │ │ │ │ ├── ConnectorNotFoundException.java │ │ │ │ │ │ ├── ConnectorState.java │ │ │ │ │ │ ├── ConnectorStateReset.java │ │ │ │ │ │ ├── InvalidConnectorImplementationException.java │ │ │ │ │ │ ├── InvalidEvaluationConnectorConditionException.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── ArchivedConnectorInstanceImpl.java │ │ │ │ │ │ ├── ConnectorDefinitionWithInputValuesImpl.java │ │ │ │ │ │ ├── ConnectorInstanceImpl.java │ │ │ │ │ │ └── ConnectorInstanceWithFailureInfoImpl.java │ │ │ │ │ ├── contract/ │ │ │ │ │ │ └── ContractViolationException.java │ │ │ │ │ ├── data/ │ │ │ │ │ │ ├── ArchivedDataInstance.java │ │ │ │ │ │ ├── ArchivedDataNotFoundException.java │ │ │ │ │ │ ├── DataInstance.java │ │ │ │ │ │ ├── DataNotFoundException.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── ArchivedDataInstanceImpl.java │ │ │ │ │ │ ├── BlobDataInstanceImpl.java │ │ │ │ │ │ ├── BooleanDataInstanceImpl.java │ │ │ │ │ │ ├── DataInstanceImpl.java │ │ │ │ │ │ ├── DateDataInstanceImpl.java │ │ │ │ │ │ ├── DoubleDataInstanceImpl.java │ │ │ │ │ │ ├── FloatDataInstanceImpl.java │ │ │ │ │ │ ├── IntegerDataInstanceImpl.java │ │ │ │ │ │ ├── LongDataInstanceImpl.java │ │ │ │ │ │ ├── LongTextDataInstanceImpl.java │ │ │ │ │ │ └── ShortTextDataInstanceImpl.java │ │ │ │ │ ├── document/ │ │ │ │ │ │ ├── ArchivedDocument.java │ │ │ │ │ │ ├── ArchivedDocumentNotFoundException.java │ │ │ │ │ │ ├── ArchivedDocumentsSearchDescriptor.java │ │ │ │ │ │ ├── DocumentAttachmentException.java │ │ │ │ │ │ ├── DocumentCriterion.java │ │ │ │ │ │ ├── DocumentException.java │ │ │ │ │ │ ├── DocumentNotFoundException.java │ │ │ │ │ │ ├── DocumentValue.java │ │ │ │ │ │ ├── DocumentsSearchDescriptor.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ └── ArchivedDocumentImpl.java │ │ │ │ │ ├── flownode/ │ │ │ │ │ │ ├── ActivityDefinitionNotFoundException.java │ │ │ │ │ │ ├── ActivityExecutionException.java │ │ │ │ │ │ ├── ActivityInstance.java │ │ │ │ │ │ ├── ActivityInstanceCriterion.java │ │ │ │ │ │ ├── ActivityInstanceNotFoundException.java │ │ │ │ │ │ ├── ActivityInstanceSearchDescriptor.java │ │ │ │ │ │ ├── ActivityStates.java │ │ │ │ │ │ ├── ArchivedActivityInstance.java │ │ │ │ │ │ ├── ArchivedActivityInstanceNotFoundException.java │ │ │ │ │ │ ├── ArchivedActivityInstanceSearchDescriptor.java │ │ │ │ │ │ ├── ArchivedAutomaticTaskInstance.java │ │ │ │ │ │ ├── ArchivedCallActivityInstance.java │ │ │ │ │ │ ├── ArchivedFlowElementInstance.java │ │ │ │ │ │ ├── ArchivedFlowNodeInstance.java │ │ │ │ │ │ ├── ArchivedFlowNodeInstanceNotFoundException.java │ │ │ │ │ │ ├── ArchivedFlowNodeInstanceSearchDescriptor.java │ │ │ │ │ │ ├── ArchivedGatewayInstance.java │ │ │ │ │ │ ├── ArchivedHumanTaskInstance.java │ │ │ │ │ │ ├── ArchivedHumanTaskInstanceSearchDescriptor.java │ │ │ │ │ │ ├── ArchivedLoopActivityInstance.java │ │ │ │ │ │ ├── ArchivedManualTaskInstance.java │ │ │ │ │ │ ├── ArchivedMultiInstanceActivityInstance.java │ │ │ │ │ │ ├── ArchivedReceiveTaskInstance.java │ │ │ │ │ │ ├── ArchivedSendTaskInstance.java │ │ │ │ │ │ ├── ArchivedSubProcessActivityInstance.java │ │ │ │ │ │ ├── ArchivedTaskInstance.java │ │ │ │ │ │ ├── ArchivedUserTaskInstance.java │ │ │ │ │ │ ├── AutomaticTaskInstance.java │ │ │ │ │ │ ├── BPMEventType.java │ │ │ │ │ │ ├── BoundaryEventInstance.java │ │ │ │ │ │ ├── CallActivityInstance.java │ │ │ │ │ │ ├── CatchEventInstance.java │ │ │ │ │ │ ├── EndEventInstance.java │ │ │ │ │ │ ├── EventCriterion.java │ │ │ │ │ │ ├── EventInstance.java │ │ │ │ │ │ ├── EventTriggerInstance.java │ │ │ │ │ │ ├── EventTriggerInstanceSearchDescriptor.java │ │ │ │ │ │ ├── FlowElementInstance.java │ │ │ │ │ │ ├── FlowElementInstanceSearchDescriptor.java │ │ │ │ │ │ ├── FlowNodeExecutionException.java │ │ │ │ │ │ ├── FlowNodeInstance.java │ │ │ │ │ │ ├── FlowNodeInstanceNotFoundException.java │ │ │ │ │ │ ├── FlowNodeInstanceSearchDescriptor.java │ │ │ │ │ │ ├── FlowNodeType.java │ │ │ │ │ │ ├── GatewayInstance.java │ │ │ │ │ │ ├── HumanTaskInstance.java │ │ │ │ │ │ ├── HumanTaskInstanceSearchDescriptor.java │ │ │ │ │ │ ├── IntermediateCatchEventInstance.java │ │ │ │ │ │ ├── IntermediateThrowEventInstance.java │ │ │ │ │ │ ├── LoopActivityInstance.java │ │ │ │ │ │ ├── ManualTaskInstance.java │ │ │ │ │ │ ├── MultiInstanceActivityInstance.java │ │ │ │ │ │ ├── ReceiveTaskInstance.java │ │ │ │ │ │ ├── SendEventException.java │ │ │ │ │ │ ├── SendTaskInstance.java │ │ │ │ │ │ ├── StartEventInstance.java │ │ │ │ │ │ ├── StateCategory.java │ │ │ │ │ │ ├── SubProcessActivityInstance.java │ │ │ │ │ │ ├── TaskInstance.java │ │ │ │ │ │ ├── ThrowEventInstance.java │ │ │ │ │ │ ├── TimerEventTriggerInstance.java │ │ │ │ │ │ ├── TimerEventTriggerInstanceNotFoundException.java │ │ │ │ │ │ ├── TimerEventTriggerInstanceSearchDescriptor.java │ │ │ │ │ │ ├── UserTaskInstance.java │ │ │ │ │ │ ├── UserTaskNotFoundException.java │ │ │ │ │ │ ├── WaitingErrorEvent.java │ │ │ │ │ │ ├── WaitingEvent.java │ │ │ │ │ │ ├── WaitingEventSearchDescriptor.java │ │ │ │ │ │ ├── WaitingMessageEvent.java │ │ │ │ │ │ ├── WaitingSignalEvent.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ └── internal/ │ │ │ │ │ │ ├── ActivityInstanceImpl.java │ │ │ │ │ │ ├── ArchivedActivityInstanceImpl.java │ │ │ │ │ │ ├── ArchivedAutomaticTaskInstanceImpl.java │ │ │ │ │ │ ├── ArchivedCallActivityInstanceImpl.java │ │ │ │ │ │ ├── ArchivedFlowElementInstanceImpl.java │ │ │ │ │ │ ├── ArchivedFlowNodeInstanceImpl.java │ │ │ │ │ │ ├── ArchivedGatewayInstanceImpl.java │ │ │ │ │ │ ├── ArchivedHumanTaskInstanceImpl.java │ │ │ │ │ │ ├── ArchivedLoopActivityInstanceImpl.java │ │ │ │ │ │ ├── ArchivedManualTaskInstanceImpl.java │ │ │ │ │ │ ├── ArchivedMultiInstanceActivityInstanceImpl.java │ │ │ │ │ │ ├── ArchivedReceiveTaskInstanceImpl.java │ │ │ │ │ │ ├── ArchivedSendTaskInstanceImpl.java │ │ │ │ │ │ ├── ArchivedSubProcessActivityInstanceImpl.java │ │ │ │ │ │ ├── ArchivedUserTaskInstanceImpl.java │ │ │ │ │ │ ├── AutomaticTaskInstanceImpl.java │ │ │ │ │ │ ├── BoundaryEventInstanceImpl.java │ │ │ │ │ │ ├── CallActivityInstanceImpl.java │ │ │ │ │ │ ├── CatchEventInstanceImpl.java │ │ │ │ │ │ ├── EndEventInstanceImpl.java │ │ │ │ │ │ ├── EventInstanceImpl.java │ │ │ │ │ │ ├── EventTriggerInstanceImpl.java │ │ │ │ │ │ ├── FlowElementInstanceImpl.java │ │ │ │ │ │ ├── FlowNodeInstanceImpl.java │ │ │ │ │ │ ├── GatewayInstanceImpl.java │ │ │ │ │ │ ├── HumanTaskInstanceImpl.java │ │ │ │ │ │ ├── IntermediateCatchEventInstanceImpl.java │ │ │ │ │ │ ├── IntermediateThrowEventInstanceImpl.java │ │ │ │ │ │ ├── LoopActivityInstanceImpl.java │ │ │ │ │ │ ├── ManualTaskInstanceImpl.java │ │ │ │ │ │ ├── MultiInstanceActivityInstanceImpl.java │ │ │ │ │ │ ├── ReceiveTaskInstanceImpl.java │ │ │ │ │ │ ├── SendTaskInstanceImpl.java │ │ │ │ │ │ ├── StartEventInstanceImpl.java │ │ │ │ │ │ ├── SubProcessActivityInstanceImpl.java │ │ │ │ │ │ ├── TaskInstanceImpl.java │ │ │ │ │ │ ├── ThrowEventInstanceImpl.java │ │ │ │ │ │ ├── TimerEventTriggerInstanceImpl.java │ │ │ │ │ │ ├── UserTaskInstanceImpl.java │ │ │ │ │ │ ├── WaitingErrorEventImpl.java │ │ │ │ │ │ ├── WaitingEventImpl.java │ │ │ │ │ │ ├── WaitingMessageEventImpl.java │ │ │ │ │ │ └── WaitingSignalEventImpl.java │ │ │ │ │ ├── parameter/ │ │ │ │ │ │ ├── ParameterCriterion.java │ │ │ │ │ │ ├── ParameterInstance.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ └── ParameterImpl.java │ │ │ │ │ ├── process/ │ │ │ │ │ │ ├── ActivationState.java │ │ │ │ │ │ ├── ArchivedProcessInstance.java │ │ │ │ │ │ ├── ArchivedProcessInstanceNotFoundException.java │ │ │ │ │ │ ├── ArchivedProcessInstancesSearchDescriptor.java │ │ │ │ │ │ ├── ConfigurationState.java │ │ │ │ │ │ ├── IllegalProcessStateException.java │ │ │ │ │ │ ├── Index.java │ │ │ │ │ │ ├── Problem.java │ │ │ │ │ │ ├── ProcessActivationException.java │ │ │ │ │ │ ├── ProcessDefinitionNotFoundException.java │ │ │ │ │ │ ├── ProcessDeployException.java │ │ │ │ │ │ ├── ProcessDeploymentInfo.java │ │ │ │ │ │ ├── ProcessDeploymentInfoCriterion.java │ │ │ │ │ │ ├── ProcessDeploymentInfoSearchDescriptor.java │ │ │ │ │ │ ├── ProcessDeploymentInfoUpdater.java │ │ │ │ │ │ ├── ProcessEnablementException.java │ │ │ │ │ │ ├── ProcessExecutionException.java │ │ │ │ │ │ ├── ProcessExportException.java │ │ │ │ │ │ ├── ProcessInstance.java │ │ │ │ │ │ ├── ProcessInstanceCriterion.java │ │ │ │ │ │ ├── ProcessInstanceNotFoundException.java │ │ │ │ │ │ ├── ProcessInstanceSearchDescriptor.java │ │ │ │ │ │ ├── ProcessInstanceState.java │ │ │ │ │ │ ├── ProcessResourceNotFoundException.java │ │ │ │ │ │ ├── V6FormDeployException.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── ProcessInstanceUpdater.java │ │ │ │ │ │ └── internal/ │ │ │ │ │ │ ├── ArchivedProcessInstanceImpl.java │ │ │ │ │ │ ├── ProblemImpl.java │ │ │ │ │ │ ├── ProcessDeploymentInfoImpl.java │ │ │ │ │ │ └── ProcessInstanceImpl.java │ │ │ │ │ └── supervisor/ │ │ │ │ │ ├── ProcessSupervisor.java │ │ │ │ │ ├── ProcessSupervisorSearchDescriptor.java │ │ │ │ │ ├── SupervisorNotFoundException.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ └── ProcessSupervisorImpl.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── business/ │ │ │ │ │ ├── application/ │ │ │ │ │ │ ├── AbstractApplicationCreator.java │ │ │ │ │ │ ├── AbstractApplicationUpdater.java │ │ │ │ │ │ ├── ApplicationCreator.java │ │ │ │ │ │ ├── ApplicationField.java │ │ │ │ │ │ ├── ApplicationImportPolicy.java │ │ │ │ │ │ ├── ApplicationLinkCreator.java │ │ │ │ │ │ ├── ApplicationLinkUpdater.java │ │ │ │ │ │ ├── ApplicationMenuCreator.java │ │ │ │ │ │ ├── ApplicationMenuField.java │ │ │ │ │ │ ├── ApplicationMenuNotFoundException.java │ │ │ │ │ │ ├── ApplicationMenuSearchDescriptor.java │ │ │ │ │ │ ├── ApplicationMenuUpdater.java │ │ │ │ │ │ ├── ApplicationNotFoundException.java │ │ │ │ │ │ ├── ApplicationPageNotFoundException.java │ │ │ │ │ │ ├── ApplicationPageSearchDescriptor.java │ │ │ │ │ │ ├── ApplicationSearchDescriptor.java │ │ │ │ │ │ ├── ApplicationUpdater.java │ │ │ │ │ │ ├── CheckedApplicationFieldMap.java │ │ │ │ │ │ └── InternalProfiles.java │ │ │ │ │ └── data/ │ │ │ │ │ ├── BusinessDataCrudOperationException.java │ │ │ │ │ ├── BusinessDataNotFoundException.java │ │ │ │ │ ├── BusinessDataReference.java │ │ │ │ │ ├── BusinessDataRepositoryDeploymentException.java │ │ │ │ │ ├── BusinessDataRepositoryException.java │ │ │ │ │ ├── InvalidBusinessDataModelException.java │ │ │ │ │ ├── MultipleBusinessDataReference.java │ │ │ │ │ ├── SimpleBusinessDataReference.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ ├── BusinessDataReferenceImpl.java │ │ │ │ │ │ ├── MultipleBusinessDataReferenceImpl.java │ │ │ │ │ │ └── SimpleBusinessDataReferenceImpl.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── command/ │ │ │ │ │ ├── CommandCriterion.java │ │ │ │ │ ├── CommandDescriptor.java │ │ │ │ │ ├── CommandDescriptorImpl.java │ │ │ │ │ ├── CommandExecutionException.java │ │ │ │ │ ├── CommandNotFoundException.java │ │ │ │ │ ├── CommandParameterizationException.java │ │ │ │ │ ├── CommandSearchDescriptor.java │ │ │ │ │ ├── CommandUpdater.java │ │ │ │ │ ├── DependencyNotFoundException.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── connector/ │ │ │ │ │ ├── AbstractConnector.java │ │ │ │ │ ├── Connector.java │ │ │ │ │ ├── ConnectorException.java │ │ │ │ │ ├── ConnectorValidationException.java │ │ │ │ │ ├── EngineExecutionContext.java │ │ │ │ │ └── sap/ │ │ │ │ │ └── SAPMonoDestinationDataProvider.java │ │ │ │ ├── digest/ │ │ │ │ │ └── DigestUtils.java │ │ │ │ ├── exception/ │ │ │ │ │ ├── APIImplementationNotFoundException.java │ │ │ │ │ ├── AlreadyExistsException.java │ │ │ │ │ ├── ApplicationInstallationException.java │ │ │ │ │ ├── BonitaContextException.java │ │ │ │ │ ├── BonitaException.java │ │ │ │ │ ├── BonitaHomeConfigurationException.java │ │ │ │ │ ├── BonitaHomeNotSetException.java │ │ │ │ │ ├── BonitaRuntimeException.java │ │ │ │ │ ├── ClassLoaderException.java │ │ │ │ │ ├── ContractDataNotFoundException.java │ │ │ │ │ ├── CreationException.java │ │ │ │ │ ├── DeletionException.java │ │ │ │ │ ├── ExceptionContext.java │ │ │ │ │ ├── ExecutionException.java │ │ │ │ │ ├── ExportException.java │ │ │ │ │ ├── FormMappingNotFoundException.java │ │ │ │ │ ├── ImportException.java │ │ │ │ │ ├── IncorrectParameterException.java │ │ │ │ │ ├── InvalidGroupNameException.java │ │ │ │ │ ├── InvalidPageTokenException.java │ │ │ │ │ ├── InvalidPageZipContentException.java │ │ │ │ │ ├── InvalidPageZipInconsistentException.java │ │ │ │ │ ├── InvalidPageZipMissingAPropertyException.java │ │ │ │ │ ├── InvalidPageZipMissingIndexException.java │ │ │ │ │ ├── InvalidPageZipMissingPropertiesException.java │ │ │ │ │ ├── InvalidXMLException.java │ │ │ │ │ ├── MissingServiceException.java │ │ │ │ │ ├── NotFoundException.java │ │ │ │ │ ├── NotSerializableException.java │ │ │ │ │ ├── ProcessInstanceHierarchicalDeletionException.java │ │ │ │ │ ├── RetrieveException.java │ │ │ │ │ ├── SearchException.java │ │ │ │ │ ├── ServerAPIException.java │ │ │ │ │ ├── StackTraceTransformer.java │ │ │ │ │ ├── TenantStatusException.java │ │ │ │ │ ├── UnauthorizedAccessException.java │ │ │ │ │ ├── UnavailableLockException.java │ │ │ │ │ ├── UnknownElementType.java │ │ │ │ │ ├── UpdateException.java │ │ │ │ │ ├── UpdatingWithInvalidPageTokenException.java │ │ │ │ │ ├── UpdatingWithInvalidPageZipContentException.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── expression/ │ │ │ │ │ └── ExpressionEvaluationException.java │ │ │ │ ├── filter/ │ │ │ │ │ ├── AbstractUserFilter.java │ │ │ │ │ ├── UserFilter.java │ │ │ │ │ └── UserFilterException.java │ │ │ │ ├── form/ │ │ │ │ │ ├── FormMapping.java │ │ │ │ │ └── FormMappingSearchDescriptor.java │ │ │ │ ├── home/ │ │ │ │ │ ├── BonitaHome.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── identity/ │ │ │ │ │ ├── ContactDataCreator.java │ │ │ │ │ ├── ContactDataUpdater.java │ │ │ │ │ ├── CustomUserInfoDefinitionCreator.java │ │ │ │ │ ├── CustomUserInfoValueSearchDescriptor.java │ │ │ │ │ ├── CustomUserInfoValueUpdater.java │ │ │ │ │ ├── GroupCreator.java │ │ │ │ │ ├── GroupCriterion.java │ │ │ │ │ ├── GroupNotFoundException.java │ │ │ │ │ ├── GroupSearchDescriptor.java │ │ │ │ │ ├── GroupUpdater.java │ │ │ │ │ ├── ImportPolicy.java │ │ │ │ │ ├── InvalidOrganizationFileFormatException.java │ │ │ │ │ ├── MembershipNotFoundException.java │ │ │ │ │ ├── OrganizationExportException.java │ │ │ │ │ ├── OrganizationImportException.java │ │ │ │ │ ├── RoleCreator.java │ │ │ │ │ ├── RoleCriterion.java │ │ │ │ │ ├── RoleNotFoundException.java │ │ │ │ │ ├── RoleSearchDescriptor.java │ │ │ │ │ ├── RoleUpdater.java │ │ │ │ │ ├── UserCreator.java │ │ │ │ │ ├── UserCriterion.java │ │ │ │ │ ├── UserMembershipCriterion.java │ │ │ │ │ ├── UserNotFoundException.java │ │ │ │ │ ├── UserSearchDescriptor.java │ │ │ │ │ ├── UserUpdater.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── io/ │ │ │ │ │ ├── FileContent.java │ │ │ │ │ ├── FileOperations.java │ │ │ │ │ ├── IOUtil.java │ │ │ │ │ ├── IOUtils.java │ │ │ │ │ └── PropertiesManager.java │ │ │ │ ├── job/ │ │ │ │ │ ├── FailedJob.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ └── FailedJobImpl.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── maintenance/ │ │ │ │ │ ├── MaintenanceDetails.java │ │ │ │ │ ├── MaintenanceDetailsNotFoundException.java │ │ │ │ │ └── impl/ │ │ │ │ │ └── MaintenanceDetailsImpl.java │ │ │ │ ├── page/ │ │ │ │ │ ├── AuthorizationRuleConstants.java │ │ │ │ │ ├── ContentType.java │ │ │ │ │ ├── Page.java │ │ │ │ │ ├── PageCreator.java │ │ │ │ │ ├── PageNotFoundException.java │ │ │ │ │ ├── PageSearchDescriptor.java │ │ │ │ │ ├── PageURL.java │ │ │ │ │ ├── PageUpdater.java │ │ │ │ │ ├── URLAdapterConstants.java │ │ │ │ │ └── impl/ │ │ │ │ │ └── PageImpl.java │ │ │ │ ├── platform/ │ │ │ │ │ ├── IllegalNodeStateException.java │ │ │ │ │ ├── InvalidPlatformCredentialsException.java │ │ │ │ │ ├── LoginException.java │ │ │ │ │ ├── LogoutException.java │ │ │ │ │ ├── NodeNotStartedException.java │ │ │ │ │ ├── Platform.java │ │ │ │ │ ├── PlatformLoginException.java │ │ │ │ │ ├── PlatformLogoutException.java │ │ │ │ │ ├── PlatformNotFoundException.java │ │ │ │ │ ├── PlatformState.java │ │ │ │ │ ├── StartNodeException.java │ │ │ │ │ ├── StopNodeException.java │ │ │ │ │ ├── UnknownUserException.java │ │ │ │ │ ├── command/ │ │ │ │ │ │ └── package-info.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ └── PlatformImpl.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── profile/ │ │ │ │ │ ├── ProfileCriterion.java │ │ │ │ │ ├── ProfileMemberCreator.java │ │ │ │ │ ├── ProfileMemberNotFoundException.java │ │ │ │ │ ├── ProfileMemberSearchDescriptor.java │ │ │ │ │ ├── ProfileNotFoundException.java │ │ │ │ │ └── ProfileSearchDescriptor.java │ │ │ │ ├── search/ │ │ │ │ │ ├── Order.java │ │ │ │ │ ├── SearchFilterOperation.java │ │ │ │ │ ├── SearchOptions.java │ │ │ │ │ ├── SearchOptionsBuilder.java │ │ │ │ │ ├── SearchResult.java │ │ │ │ │ ├── Sort.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ ├── SearchFilter.java │ │ │ │ │ │ ├── SearchOptionsImpl.java │ │ │ │ │ │ ├── SearchResultImpl.java │ │ │ │ │ │ └── package-info.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── session/ │ │ │ │ │ ├── APISession.java │ │ │ │ │ ├── InvalidSessionException.java │ │ │ │ │ ├── PlatformSession.java │ │ │ │ │ ├── Session.java │ │ │ │ │ ├── SessionNotFoundException.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ ├── APISessionImpl.java │ │ │ │ │ │ ├── PlatformSessionImpl.java │ │ │ │ │ │ └── SessionImpl.java │ │ │ │ │ └── package-info.java │ │ │ │ ├── tenant/ │ │ │ │ │ ├── TenantResource.java │ │ │ │ │ ├── TenantResourceState.java │ │ │ │ │ └── TenantResourceType.java │ │ │ │ ├── util/ │ │ │ │ │ └── package-info.java │ │ │ │ └── xml/ │ │ │ │ ├── DocumentManager.java │ │ │ │ └── XStreamDenyList.java │ │ │ └── javadoc/ │ │ │ ├── overview.html │ │ │ └── stylesheet.css │ │ ├── test/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ ├── api/ │ │ │ │ │ └── permission/ │ │ │ │ │ └── APICallContextTest.java │ │ │ │ ├── bdm/ │ │ │ │ │ ├── serialization/ │ │ │ │ │ │ ├── BusinessDataObjectMapperTest.java │ │ │ │ │ │ ├── CustomLocalDateDeserializerTest.java │ │ │ │ │ │ ├── CustomLocalDateSerializerTest.java │ │ │ │ │ │ ├── CustomLocalDateTimeDeserializerTest.java │ │ │ │ │ │ ├── CustomLocalDateTimeSerializerTest.java │ │ │ │ │ │ ├── CustomOffsetDateTimeDeserializerTest.java │ │ │ │ │ │ ├── CustomOffsetDateTimeSerializerTest.java │ │ │ │ │ │ └── model/ │ │ │ │ │ │ └── Invoice.java │ │ │ │ │ └── validator/ │ │ │ │ │ ├── BusinessObjectModelValidatorTest.java │ │ │ │ │ ├── UniqueNameValidatorTest.java │ │ │ │ │ ├── ValidationStatusTest.java │ │ │ │ │ └── rule/ │ │ │ │ │ ├── BusinessObjectModelValidationRuleTest.java │ │ │ │ │ ├── BusinessObjectValidationRuleTest.java │ │ │ │ │ ├── FieldValidationRuleTest.java │ │ │ │ │ ├── IndexValidationRuleTest.java │ │ │ │ │ ├── MultipleAggregationToItselfValidationRuleTest.java │ │ │ │ │ ├── QueryParameterValidationRuleTest.java │ │ │ │ │ ├── QueryValidationRuleTest.java │ │ │ │ │ ├── SimpleFieldValidationRuleTest.java │ │ │ │ │ ├── UniqueConstraintValidationRuleTest.java │ │ │ │ │ ├── UniqueNameValidationRuleTest.java │ │ │ │ │ ├── UniqueSimpleNameValidationRuleTest.java │ │ │ │ │ ├── ValidationRuleTest.java │ │ │ │ │ └── composition/ │ │ │ │ │ ├── AggregationAndCompositionValidationRuleTest.java │ │ │ │ │ ├── CyclicCompositionValidationRuleTest.java │ │ │ │ │ └── UniquenessCompositionValidationRuleTest.java │ │ │ │ ├── bpm/ │ │ │ │ │ └── document/ │ │ │ │ │ └── DocumentValueTest.java │ │ │ │ ├── business/ │ │ │ │ │ └── application/ │ │ │ │ │ ├── ApplicationLinkUpdaterTest.java │ │ │ │ │ └── ApplicationUpdaterTest.java │ │ │ │ ├── connector/ │ │ │ │ │ └── sap/ │ │ │ │ │ └── SAPMonoDestinationDataProviderTest.java │ │ │ │ ├── digest/ │ │ │ │ │ └── DigestUtilsTest.java │ │ │ │ ├── exception/ │ │ │ │ │ ├── BonitaExceptionTest.java │ │ │ │ │ └── StackTraceTransformerTest.java │ │ │ │ ├── expression/ │ │ │ │ │ └── ExpressionEvaluationExceptionTest.java │ │ │ │ ├── filter/ │ │ │ │ │ └── AbstractUserFilterTest.java │ │ │ │ ├── io/ │ │ │ │ │ └── FileOperationsTest.java │ │ │ │ ├── page/ │ │ │ │ │ ├── PageCreatorTest.java │ │ │ │ │ ├── PageUpdaterTest.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── PageImplAssert.java │ │ │ │ │ └── PageImplTest.java │ │ │ │ ├── search/ │ │ │ │ │ └── impl/ │ │ │ │ │ └── SearchOptionsImplTest.java │ │ │ │ └── util/ │ │ │ │ └── IOUtilTest.java │ │ │ └── resources/ │ │ │ ├── logback-test.xml │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── bdm/ │ │ │ └── serialization/ │ │ │ └── simpleInvoice.json │ │ └── testFixtures/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ ├── bdm/ │ │ │ └── validator/ │ │ │ └── assertion/ │ │ │ ├── RuleOfCondition.java │ │ │ └── ValidationStatusAssert.java │ │ └── io/ │ │ └── FileAndContentUtils.java │ ├── bonita-core/ │ │ ├── bonita-actor-mapping/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── bonitasoft/ │ │ │ │ │ └── engine/ │ │ │ │ │ └── actor/ │ │ │ │ │ └── mapping/ │ │ │ │ │ ├── ActorMappingService.java │ │ │ │ │ ├── SActorCreationException.java │ │ │ │ │ ├── SActorDeletionException.java │ │ │ │ │ ├── SActorMemberAlreadyExistsException.java │ │ │ │ │ ├── SActorMemberCreationException.java │ │ │ │ │ ├── SActorMemberDeletionException.java │ │ │ │ │ ├── SActorMemberNotFoundException.java │ │ │ │ │ ├── SActorNotFoundException.java │ │ │ │ │ ├── SActorUpdateException.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ ├── ActorMappingServiceImpl.java │ │ │ │ │ │ ├── SActorLogBuilderFactoryImpl.java │ │ │ │ │ │ └── SActorLogBuilderImpl.java │ │ │ │ │ ├── model/ │ │ │ │ │ │ ├── SActor.java │ │ │ │ │ │ ├── SActorFilter.java │ │ │ │ │ │ ├── SActorLogBuilder.java │ │ │ │ │ │ ├── SActorLogBuilderFactory.java │ │ │ │ │ │ ├── SActorMember.java │ │ │ │ │ │ ├── SActorUpdateBuilder.java │ │ │ │ │ │ ├── SActorUpdateBuilderFactory.java │ │ │ │ │ │ ├── SMemberType.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── SActorUpdateBuilderFactoryImpl.java │ │ │ │ │ │ └── SActorUpdateBuilderImpl.java │ │ │ │ │ └── persistence/ │ │ │ │ │ └── SelectDescriptorBuilder.java │ │ │ │ └── resources/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── actor/ │ │ │ │ └── mapping/ │ │ │ │ └── model/ │ │ │ │ └── impl/ │ │ │ │ └── hibernate/ │ │ │ │ └── actor.queries.hbm.xml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── actor/ │ │ │ └── mapping/ │ │ │ └── impl/ │ │ │ └── ActorMappingServiceImplTest.java │ │ ├── bonita-category/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── bonitasoft/ │ │ │ │ │ └── engine/ │ │ │ │ │ └── core/ │ │ │ │ │ └── category/ │ │ │ │ │ ├── CategoryService.java │ │ │ │ │ ├── SCategoryCriterion.java │ │ │ │ │ ├── exception/ │ │ │ │ │ │ ├── SCategoryAlreadyExistsException.java │ │ │ │ │ │ ├── SCategoryCreationException.java │ │ │ │ │ │ ├── SCategoryDeletionException.java │ │ │ │ │ │ ├── SCategoryException.java │ │ │ │ │ │ ├── SCategoryInProcessAlreadyExistsException.java │ │ │ │ │ │ ├── SCategoryNotFoundException.java │ │ │ │ │ │ ├── SIndexOutOfRangeException.java │ │ │ │ │ │ └── SPageOutOfRangeException.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ └── CategoryServiceImpl.java │ │ │ │ │ ├── model/ │ │ │ │ │ │ ├── SCategory.java │ │ │ │ │ │ ├── SProcessCategoryMapping.java │ │ │ │ │ │ └── builder/ │ │ │ │ │ │ ├── SCategoryLogBuilder.java │ │ │ │ │ │ ├── SCategoryLogBuilderFactory.java │ │ │ │ │ │ ├── SCategoryUpdateBuilder.java │ │ │ │ │ │ ├── SCategoryUpdateBuilderFactory.java │ │ │ │ │ │ ├── SProcessCategoryMappingBuilder.java │ │ │ │ │ │ ├── SProcessCategoryMappingBuilderFactory.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── SCategoryLogBuilderFactoryImpl.java │ │ │ │ │ │ ├── SCategoryLogBuilderImpl.java │ │ │ │ │ │ ├── SCategoryLogIndexesMapper.java │ │ │ │ │ │ ├── SCategoryUpdateBuilderFactoryImpl.java │ │ │ │ │ │ ├── SCategoryUpdateBuilderImpl.java │ │ │ │ │ │ ├── SProcessCategoryMappingBuilderFactoryImpl.java │ │ │ │ │ │ └── SProcessCategoryMappingBuilderImpl.java │ │ │ │ │ └── persistence/ │ │ │ │ │ └── SelectDescriptorBuilder.java │ │ │ │ └── resources/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── core/ │ │ │ │ └── category/ │ │ │ │ └── model/ │ │ │ │ └── impl/ │ │ │ │ └── hibernate/ │ │ │ │ └── category.queries.hbm.xml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── core/ │ │ │ └── category/ │ │ │ └── impl/ │ │ │ └── CategoryServiceImplTest.java │ │ ├── bonita-contract-data/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── bonitasoft/ │ │ │ │ │ └── engine/ │ │ │ │ │ └── core/ │ │ │ │ │ └── contract/ │ │ │ │ │ └── data/ │ │ │ │ │ ├── ContractDataService.java │ │ │ │ │ ├── ContractDataServiceImpl.java │ │ │ │ │ ├── SAContractData.java │ │ │ │ │ ├── SAProcessContractData.java │ │ │ │ │ ├── SATaskContractData.java │ │ │ │ │ ├── SContractData.java │ │ │ │ │ ├── SContractDataCreationException.java │ │ │ │ │ ├── SContractDataDeletionException.java │ │ │ │ │ ├── SContractDataLogBuilder.java │ │ │ │ │ ├── SContractDataNotFoundException.java │ │ │ │ │ ├── SProcessContractData.java │ │ │ │ │ └── STaskContractData.java │ │ │ │ └── resources/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── core/ │ │ │ │ └── contract/ │ │ │ │ └── data/ │ │ │ │ └── model/ │ │ │ │ └── impl/ │ │ │ │ └── hibernate/ │ │ │ │ └── contract.queries.hbm.xml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── core/ │ │ │ └── contract/ │ │ │ └── data/ │ │ │ ├── ContractDataServiceImplTest.java │ │ │ ├── SAProcessContractDataTest.java │ │ │ └── SATaskContractDataTest.java │ │ ├── bonita-core-data/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ └── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── core/ │ │ │ │ └── data/ │ │ │ │ └── instance/ │ │ │ │ ├── TransientDataService.java │ │ │ │ └── impl/ │ │ │ │ ├── TransientDataExpressionExecutorStrategy.java │ │ │ │ └── TransientDataServiceImpl.java │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── core/ │ │ │ └── data/ │ │ │ └── instance/ │ │ │ └── impl/ │ │ │ └── TransientDataServiceImplTest.java │ │ ├── bonita-form-mapping/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── bonitasoft/ │ │ │ │ │ └── engine/ │ │ │ │ │ └── core/ │ │ │ │ │ └── form/ │ │ │ │ │ ├── AuthorizationRuleMapping.java │ │ │ │ │ ├── FormMappingKeyGenerator.java │ │ │ │ │ ├── FormMappingService.java │ │ │ │ │ ├── SFormMapping.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── AuthorizationRuleMappingImpl.java │ │ │ │ │ ├── FormMappingKeyGeneratorImpl.java │ │ │ │ │ ├── FormMappingServiceImpl.java │ │ │ │ │ └── ManagerInvolvedAuthorizationRuleMappingImpl.java │ │ │ │ └── resources/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── core/ │ │ │ │ └── form/ │ │ │ │ └── impl/ │ │ │ │ └── form-mapping.queries.hbm.xml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── core/ │ │ │ └── form/ │ │ │ └── impl/ │ │ │ ├── FormMappingKeyGeneratorImplTest.java │ │ │ ├── FormMappingServiceImplTest.java │ │ │ └── ManagerInvolvedAuthorizationRuleMappingImplTest.java │ │ ├── bonita-home-server/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ └── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── home/ │ │ │ │ ├── BonitaHomeServer.java │ │ │ │ ├── BonitaResource.java │ │ │ │ ├── ProfileStorage.java │ │ │ │ └── Util.java │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── home/ │ │ │ ├── BonitaHomeServerTest.java │ │ │ ├── ProfileStorageTest.java │ │ │ └── UtilTest.java │ │ ├── bonita-login/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ └── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── core/ │ │ │ │ └── login/ │ │ │ │ ├── LoginService.java │ │ │ │ ├── SLoginException.java │ │ │ │ ├── SecuredLoginServiceImpl.java │ │ │ │ └── TechnicalUser.java │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── core/ │ │ │ └── login/ │ │ │ └── SecuredLoginServiceImplTest.java │ │ ├── bonita-parameter/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── bonitasoft/ │ │ │ │ │ └── engine/ │ │ │ │ │ └── parameter/ │ │ │ │ │ ├── OrderBy.java │ │ │ │ │ ├── ParameterService.java │ │ │ │ │ ├── ParameterServiceImpl.java │ │ │ │ │ ├── SOutOfBoundException.java │ │ │ │ │ ├── SParameter.java │ │ │ │ │ ├── SParameterNameNotFoundException.java │ │ │ │ │ └── SParameterProcessNotFoundException.java │ │ │ │ └── resources/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── parameter/ │ │ │ │ └── parameter.queries.hbm.xml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── parameter/ │ │ │ └── ParameterServiceImplTest.java │ │ ├── bonita-platform-login/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ └── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── core/ │ │ │ │ └── platform/ │ │ │ │ └── login/ │ │ │ │ ├── PlatformLoginService.java │ │ │ │ ├── SInvalidPlatformCredentialsException.java │ │ │ │ ├── SPlatformLoginException.java │ │ │ │ └── impl/ │ │ │ │ └── PlatformLoginServiceImpl.java │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── core/ │ │ │ └── platform/ │ │ │ └── login/ │ │ │ └── impl/ │ │ │ └── PlatformLoginServiceImplTest.java │ │ ├── bonita-process-comment/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── core/ │ │ │ │ └── process/ │ │ │ │ └── comment/ │ │ │ │ ├── api/ │ │ │ │ │ ├── SCommentAddException.java │ │ │ │ │ ├── SCommentDeletionException.java │ │ │ │ │ ├── SCommentException.java │ │ │ │ │ ├── SCommentNotFoundException.java │ │ │ │ │ ├── SCommentService.java │ │ │ │ │ ├── SystemCommentType.java │ │ │ │ │ └── impl/ │ │ │ │ │ └── SCommentServiceImpl.java │ │ │ │ └── model/ │ │ │ │ ├── SComment.java │ │ │ │ ├── SHumanComment.java │ │ │ │ ├── SSystemComment.java │ │ │ │ ├── archive/ │ │ │ │ │ └── SAComment.java │ │ │ │ └── builder/ │ │ │ │ ├── SCommentLogBuilderFactory.java │ │ │ │ └── impl/ │ │ │ │ └── SCommentLogBuilderFactoryImpl.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── core/ │ │ │ └── process/ │ │ │ └── comment/ │ │ │ └── model/ │ │ │ └── impl/ │ │ │ └── hibernate/ │ │ │ ├── archive.comment.queries.hbm.xml │ │ │ └── comment.queries.hbm.xml │ │ ├── bonita-process-definition/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── bonitasoft/ │ │ │ │ │ └── engine/ │ │ │ │ │ └── core/ │ │ │ │ │ ├── expression/ │ │ │ │ │ │ └── control/ │ │ │ │ │ │ ├── api/ │ │ │ │ │ │ │ ├── ExpressionResolverService.java │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ └── ExpressionResolverServiceImpl.java │ │ │ │ │ │ └── model/ │ │ │ │ │ │ └── SExpressionContext.java │ │ │ │ │ ├── operation/ │ │ │ │ │ │ ├── LeftOperandHandler.java │ │ │ │ │ │ ├── OperationExecutorStrategy.java │ │ │ │ │ │ ├── OperationExecutorStrategyProvider.java │ │ │ │ │ │ ├── OperationResult.java │ │ │ │ │ │ ├── OperationService.java │ │ │ │ │ │ ├── exception/ │ │ │ │ │ │ │ └── SOperationExecutionException.java │ │ │ │ │ │ ├── impl/ │ │ │ │ │ │ │ ├── AssignmentOperationExecutorStrategy.java │ │ │ │ │ │ │ ├── ExternalDataLeftOperandHandler.java │ │ │ │ │ │ │ ├── JavaMethodOperationExecutorStrategy.java │ │ │ │ │ │ │ ├── LeftOperandIndexes.java │ │ │ │ │ │ │ ├── LeftOperandUpdateStatus.java │ │ │ │ │ │ │ ├── OperationServiceImpl.java │ │ │ │ │ │ │ ├── OperationsAnalyzer.java │ │ │ │ │ │ │ ├── PersistRightOperandResolver.java │ │ │ │ │ │ │ └── XpathUpdateQueryOperationExecutorStrategy.java │ │ │ │ │ │ └── model/ │ │ │ │ │ │ ├── SLeftOperand.java │ │ │ │ │ │ ├── SOperation.java │ │ │ │ │ │ ├── SOperatorType.java │ │ │ │ │ │ ├── builder/ │ │ │ │ │ │ │ ├── SLeftOperandBuilder.java │ │ │ │ │ │ │ ├── SLeftOperandBuilderFactory.java │ │ │ │ │ │ │ ├── SOperationBuilder.java │ │ │ │ │ │ │ ├── SOperationBuilderFactory.java │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ ├── SLeftOperandBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SLeftOperandBuilderImpl.java │ │ │ │ │ │ │ ├── SOperationBuilderFactoryImpl.java │ │ │ │ │ │ │ └── SOperationBuilderImpl.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── SLeftOperandImpl.java │ │ │ │ │ │ └── SOperationImpl.java │ │ │ │ │ └── process/ │ │ │ │ │ └── definition/ │ │ │ │ │ ├── ProcessDefinitionService.java │ │ │ │ │ ├── ProcessDefinitionServiceImpl.java │ │ │ │ │ ├── exception/ │ │ │ │ │ │ ├── SDeletingEnabledProcessException.java │ │ │ │ │ │ ├── SProcessDefinitionException.java │ │ │ │ │ │ ├── SProcessDefinitionNotFoundException.java │ │ │ │ │ │ ├── SProcessDeletionException.java │ │ │ │ │ │ ├── SProcessDeploymentInfoUpdateException.java │ │ │ │ │ │ ├── SProcessDisablementException.java │ │ │ │ │ │ └── SProcessEnablementException.java │ │ │ │ │ └── model/ │ │ │ │ │ ├── SActivityDefinition.java │ │ │ │ │ ├── SActorDefinition.java │ │ │ │ │ ├── SAutomaticTaskDefinition.java │ │ │ │ │ ├── SBaseElement.java │ │ │ │ │ ├── SBoundaryEventNotFoundException.java │ │ │ │ │ ├── SBusinessDataDefinition.java │ │ │ │ │ ├── SCallActivityDefinition.java │ │ │ │ │ ├── SCallableElementType.java │ │ │ │ │ ├── SConnectorDefinition.java │ │ │ │ │ ├── SConstraintDefinition.java │ │ │ │ │ ├── SContextEntry.java │ │ │ │ │ ├── SContractDefinition.java │ │ │ │ │ ├── SDocumentDefinition.java │ │ │ │ │ ├── SDocumentListDefinition.java │ │ │ │ │ ├── SFlowElementContainerDefinition.java │ │ │ │ │ ├── SFlowNodeDefinition.java │ │ │ │ │ ├── SFlowNodeType.java │ │ │ │ │ ├── SGatewayDefinition.java │ │ │ │ │ ├── SGatewayType.java │ │ │ │ │ ├── SHumanTaskDefinition.java │ │ │ │ │ ├── SInputContainerDefinition.java │ │ │ │ │ ├── SInputDefinition.java │ │ │ │ │ ├── SLoopCharacteristics.java │ │ │ │ │ ├── SManualTaskDefinition.java │ │ │ │ │ ├── SMultiInstanceLoopCharacteristics.java │ │ │ │ │ ├── SNamedElement.java │ │ │ │ │ ├── SParameterDefinition.java │ │ │ │ │ ├── SProcessDefinition.java │ │ │ │ │ ├── SProcessDefinitionDeployInfo.java │ │ │ │ │ ├── SProcessDefinitionDesignContent.java │ │ │ │ │ ├── SReceiveTaskDefinition.java │ │ │ │ │ ├── SSendTaskDefinition.java │ │ │ │ │ ├── SStandardLoopCharacteristics.java │ │ │ │ │ ├── SSubProcessDefinition.java │ │ │ │ │ ├── STaskDefinition.java │ │ │ │ │ ├── STransitionDefinition.java │ │ │ │ │ ├── SType.java │ │ │ │ │ ├── SUserFilterDefinition.java │ │ │ │ │ ├── SUserTaskDefinition.java │ │ │ │ │ ├── TransitionState.java │ │ │ │ │ ├── builder/ │ │ │ │ │ │ ├── SActorLogBuilder.java │ │ │ │ │ │ ├── SActorLogBuilderFactory.java │ │ │ │ │ │ ├── SBusinessDataDefinitionBuilder.java │ │ │ │ │ │ ├── SBusinessDataDefinitionBuilderFactory.java │ │ │ │ │ │ ├── SProcessDefinitionBuilder.java │ │ │ │ │ │ ├── SProcessDefinitionBuilderFactory.java │ │ │ │ │ │ ├── SProcessDefinitionDeployInfoUpdateBuilder.java │ │ │ │ │ │ ├── SProcessDefinitionDeployInfoUpdateBuilderFactory.java │ │ │ │ │ │ ├── SProcessDefinitionLogBuilder.java │ │ │ │ │ │ ├── SProcessDefinitionLogBuilderFactory.java │ │ │ │ │ │ ├── ServerModelConvertor.java │ │ │ │ │ │ ├── event/ │ │ │ │ │ │ │ └── trigger/ │ │ │ │ │ │ │ ├── SEndEventDefinitionBuilder.java │ │ │ │ │ │ │ ├── SEndEventDefinitionBuilderFactory.java │ │ │ │ │ │ │ ├── SThrowErrorEventTriggerDefinitionBuilder.java │ │ │ │ │ │ │ ├── SThrowErrorEventTriggerDefinitionBuilderFactory.java │ │ │ │ │ │ │ ├── SThrowMessageEventTriggerDefinitionBuilder.java │ │ │ │ │ │ │ ├── SThrowMessageEventTriggerDefinitionBuilderFactory.java │ │ │ │ │ │ │ ├── SThrowSignalEventTriggerDefinitionBuilder.java │ │ │ │ │ │ │ ├── SThrowSignalEventTriggerDefinitionBuilderFactory.java │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ ├── SEndEventDefinitionBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SEndEventDefinitionBuilderImpl.java │ │ │ │ │ │ │ ├── SThrowErrorEventTriggerDefinitionBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SThrowErrorEventTriggerDefinitionBuilderImpl.java │ │ │ │ │ │ │ ├── SThrowMessageEventTriggerDefinitionBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SThrowMessageEventTriggerDefinitionBuilderImpl.java │ │ │ │ │ │ │ ├── SThrowSignalEventTriggerDefinitionBuilderFactoryImpl.java │ │ │ │ │ │ │ └── SThrowSignalEventTriggerDefinitionBuilderImpl.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── ProcessDefinitionLogIndexesMapper.java │ │ │ │ │ │ ├── SBusinessDataDefinitionBuilderFactoryImpl.java │ │ │ │ │ │ ├── SBusinessDataDefinitionBuilderImpl.java │ │ │ │ │ │ ├── SProcessDefinitionBuilderFactoryImpl.java │ │ │ │ │ │ ├── SProcessDefinitionBuilderImpl.java │ │ │ │ │ │ ├── SProcessDefinitionDeployInfoUpdateBuilderFactoryImpl.java │ │ │ │ │ │ ├── SProcessDefinitionDeployInfoUpdateBuilderImpl.java │ │ │ │ │ │ ├── SProcessDefinitionLogBuilderFactoryImpl.java │ │ │ │ │ │ └── SProcessDefinitionLogBuilderImpl.java │ │ │ │ │ ├── event/ │ │ │ │ │ │ ├── SBoundaryEventDefinition.java │ │ │ │ │ │ ├── SCatchEventDefinition.java │ │ │ │ │ │ ├── SEndEventDefinition.java │ │ │ │ │ │ ├── SEventDefinition.java │ │ │ │ │ │ ├── SImplicitThrowEventDefinition.java │ │ │ │ │ │ ├── SIntermediateCatchEventDefinition.java │ │ │ │ │ │ ├── SIntermediateThrowEventDefinition.java │ │ │ │ │ │ ├── SStartEventDefinition.java │ │ │ │ │ │ ├── SThrowEventDefinition.java │ │ │ │ │ │ ├── impl/ │ │ │ │ │ │ │ ├── SBoundaryEventDefinitionImpl.java │ │ │ │ │ │ │ ├── SCatchEventDefinitionImpl.java │ │ │ │ │ │ │ ├── SEndEventDefinitionImpl.java │ │ │ │ │ │ │ ├── SEventDefinitionImpl.java │ │ │ │ │ │ │ ├── SIntermediateCatchEventDefinitionImpl.java │ │ │ │ │ │ │ ├── SIntermediateThrowEventDefinitionImpl.java │ │ │ │ │ │ │ ├── SStartEventDefinitionImpl.java │ │ │ │ │ │ │ └── SThrowEventDefinitionImpl.java │ │ │ │ │ │ └── trigger/ │ │ │ │ │ │ ├── SCatchErrorEventTriggerDefinition.java │ │ │ │ │ │ ├── SCatchMessageEventTriggerDefinition.java │ │ │ │ │ │ ├── SCatchSignalEventTriggerDefinition.java │ │ │ │ │ │ ├── SCorrelationDefinition.java │ │ │ │ │ │ ├── SErrorEventTriggerDefinition.java │ │ │ │ │ │ ├── SEventTriggerDefinition.java │ │ │ │ │ │ ├── SEventTriggerType.java │ │ │ │ │ │ ├── SMessageEventTriggerDefinition.java │ │ │ │ │ │ ├── SSignalEventTriggerDefinition.java │ │ │ │ │ │ ├── STerminateEventTriggerDefinition.java │ │ │ │ │ │ ├── SThrowErrorEventTriggerDefinition.java │ │ │ │ │ │ ├── SThrowMessageEventTriggerDefinition.java │ │ │ │ │ │ ├── SThrowSignalEventTriggerDefinition.java │ │ │ │ │ │ ├── STimerEventTriggerDefinition.java │ │ │ │ │ │ ├── STimerType.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── SCatchErrorEventTriggerDefinitionImpl.java │ │ │ │ │ │ ├── SCatchMessageEventTriggerDefinitionImpl.java │ │ │ │ │ │ ├── SCatchSignalEventTriggerDefinitionImpl.java │ │ │ │ │ │ ├── SCorrelationDefinitionImpl.java │ │ │ │ │ │ ├── SErrorEventTriggerDefinitionImpl.java │ │ │ │ │ │ ├── SEventTriggerDefinitionImpl.java │ │ │ │ │ │ ├── SMessageEventTriggerDefinitionImpl.java │ │ │ │ │ │ ├── SSignalEventTriggerDefinitionImpl.java │ │ │ │ │ │ ├── STerminateEventTriggerDefinitionImpl.java │ │ │ │ │ │ ├── SThrowErrorEventTriggerDefinitionImpl.java │ │ │ │ │ │ ├── SThrowMessageEventTriggerDefinitionImpl.java │ │ │ │ │ │ ├── SThrowSignalEventTriggerDefinitionImpl.java │ │ │ │ │ │ └── STimerEventTriggerDefinitionImpl.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── SActivityDefinitionImpl.java │ │ │ │ │ ├── SActorDefinitionImpl.java │ │ │ │ │ ├── SAutomaticTaskDefinitionImpl.java │ │ │ │ │ ├── SBaseElementImpl.java │ │ │ │ │ ├── SBusinessDataDefinitionImpl.java │ │ │ │ │ ├── SCallActivityDefinitionImpl.java │ │ │ │ │ ├── SConnectorDefinitionImpl.java │ │ │ │ │ ├── SConstraintDefinitionImpl.java │ │ │ │ │ ├── SContextEntryImpl.java │ │ │ │ │ ├── SContractDefinitionImpl.java │ │ │ │ │ ├── SDocumentDefinitionImpl.java │ │ │ │ │ ├── SDocumentListDefinitionImpl.java │ │ │ │ │ ├── SFlowElementContainerDefinitionImpl.java │ │ │ │ │ ├── SFlowNodeDefinitionImpl.java │ │ │ │ │ ├── SGatewayDefinitionImpl.java │ │ │ │ │ ├── SHumanTaskDefinitionImpl.java │ │ │ │ │ ├── SInputDefinitionImpl.java │ │ │ │ │ ├── SManualTaskDefinitionImpl.java │ │ │ │ │ ├── SMultiInstanceLoopCharacteristicsImpl.java │ │ │ │ │ ├── SNamedElementImpl.java │ │ │ │ │ ├── SParameterDefinitionImpl.java │ │ │ │ │ ├── SProcessDefinitionImpl.java │ │ │ │ │ ├── SReceiveTaskDefinitionImpl.java │ │ │ │ │ ├── SSendTaskDefinitionImpl.java │ │ │ │ │ ├── SStandardLoopCharacteristicsImpl.java │ │ │ │ │ ├── SSubProcessDefinitionImpl.java │ │ │ │ │ ├── STransitionDefinitionImpl.java │ │ │ │ │ ├── SUserFilterDefinitionImpl.java │ │ │ │ │ └── SUserTaskDefinitionImpl.java │ │ │ │ └── resources/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── core/ │ │ │ │ └── process/ │ │ │ │ └── definition/ │ │ │ │ └── model/ │ │ │ │ └── impl/ │ │ │ │ └── hibernate/ │ │ │ │ └── process.definition.queries.hbm.xml │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── core/ │ │ │ ├── expression/ │ │ │ │ └── control/ │ │ │ │ ├── api/ │ │ │ │ │ └── impl/ │ │ │ │ │ └── ExpressionResolverServiceImplTest.java │ │ │ │ └── model/ │ │ │ │ └── SExpressionContextTest.java │ │ │ ├── operation/ │ │ │ │ └── impl/ │ │ │ │ ├── AssignmentOperationExecutorStrategyTest.java │ │ │ │ ├── ExternalDataLeftOperandHandlerTest.java │ │ │ │ ├── JavaMethodOperationExecutorStrategyTest.java │ │ │ │ ├── LeftOperandIndexesTest.java │ │ │ │ ├── LeftOperandUpdateStatusTest.java │ │ │ │ ├── OperationMockBuilder.java │ │ │ │ ├── OperationServiceImplTest.java │ │ │ │ ├── OperationsAnalyzerTest.java │ │ │ │ ├── PersistRightOperandResolverTest.java │ │ │ │ └── XpathUpdateQueryOperationExecutorStrategyTest.java │ │ │ └── process/ │ │ │ └── definition/ │ │ │ ├── ProcessDefinitionServiceImplTest.java │ │ │ └── model/ │ │ │ ├── STypeBooleanValidationTest.java │ │ │ ├── STypeByteArrayValidationTest.java │ │ │ ├── STypeDateValidationTest.java │ │ │ ├── STypeDecimalValidationTest.java │ │ │ ├── STypeIntegerValidationTest.java │ │ │ ├── STypeLocalDateTest.java │ │ │ ├── STypeLocalDateTimeTest.java │ │ │ ├── STypeOffsetDateTimeTest.java │ │ │ ├── STypeTextValidationTest.java │ │ │ ├── builder/ │ │ │ │ ├── ServerModelConvertorTest.java │ │ │ │ └── impl/ │ │ │ │ └── SBusinessDataDefinitionBuilderFactoryImplTest.java │ │ │ ├── event/ │ │ │ │ └── impl/ │ │ │ │ ├── SBoundaryEventDefinitionImplTest.java │ │ │ │ └── SStartEventDefinitionImplTest.java │ │ │ └── impl/ │ │ │ ├── SCatchEventDefinitionImplTest.java │ │ │ ├── SConstraintDefinitionImplTest.java │ │ │ ├── SFlowNodeDefinitionImplTest.java │ │ │ ├── SGatewayDefinitionImplTest.java │ │ │ ├── SInputDefinitionImplTest.java │ │ │ ├── SSubProcessDefinitionImplTest.java │ │ │ ├── STransitionDefinitionImplTest.java │ │ │ └── SUserTaskDefinitionImplTest.java │ │ ├── bonita-process-engine/ │ │ │ ├── FlowStatesMapping.txt │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── groovy/ │ │ │ │ │ └── org/ │ │ │ │ │ └── bonitasoft/ │ │ │ │ │ └── permissions/ │ │ │ │ │ ├── ActorMemberPermissionRule.groovy │ │ │ │ │ ├── ActorPermissionRule.groovy │ │ │ │ │ ├── ApplicationMenuPermissionRule.groovy │ │ │ │ │ ├── ApplicationPermissionCommon.groovy │ │ │ │ │ ├── ApplicationPermissionRule.groovy │ │ │ │ │ ├── CaseContextPermissionRule.groovy │ │ │ │ │ ├── CasePermissionRule.groovy │ │ │ │ │ ├── CaseVariablePermissionRule.groovy │ │ │ │ │ ├── CommentPermissionRule.groovy │ │ │ │ │ ├── ConnectorInstancePermissionRule.groovy │ │ │ │ │ ├── DocumentPermissionRule.groovy │ │ │ │ │ ├── DownloadDocumentPermissionRule.groovy │ │ │ │ │ ├── ProcessConfigurationPermissionRule.groovy │ │ │ │ │ ├── ProcessConnectorDependencyPermissionRule.groovy │ │ │ │ │ ├── ProcessInstantiationPermissionRule.groovy │ │ │ │ │ ├── ProcessPermissionRule.groovy │ │ │ │ │ ├── ProcessResolutionProblemPermissionRule.groovy │ │ │ │ │ ├── ProcessSupervisorPermissionRule.groovy │ │ │ │ │ ├── ProfilePermissionRule.groovy │ │ │ │ │ ├── TaskExecutionPermissionRule.groovy │ │ │ │ │ ├── TaskPermissionRule.groovy │ │ │ │ │ └── UserPermissionRule.groovy │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── bonitasoft/ │ │ │ │ │ └── engine/ │ │ │ │ │ ├── EngineConfiguration.java │ │ │ │ │ ├── EngineInitializer.java │ │ │ │ │ ├── LocalLoginMechanism.java │ │ │ │ │ ├── SArchivingException.java │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── impl/ │ │ │ │ │ │ │ ├── APIAccessorImpl.java │ │ │ │ │ │ │ ├── APIUtils.java │ │ │ │ │ │ │ ├── ApplicationAPIImpl.java │ │ │ │ │ │ │ ├── AvailableInMaintenanceMode.java │ │ │ │ │ │ │ ├── AvailableOnStoppedNode.java │ │ │ │ │ │ │ ├── BusinessDataAPIImpl.java │ │ │ │ │ │ │ ├── CommandAPIImpl.java │ │ │ │ │ │ │ ├── CustomUserInfoAPIDelegate.java │ │ │ │ │ │ │ ├── CustomUserInfoDefinitionAPIDelegate.java │ │ │ │ │ │ │ ├── DocumentAPIImpl.java │ │ │ │ │ │ │ ├── IdentityAPIImpl.java │ │ │ │ │ │ │ ├── LoginAPIImpl.java │ │ │ │ │ │ │ ├── MaintenanceAPIImpl.java │ │ │ │ │ │ │ ├── OrderAndFields.java │ │ │ │ │ │ │ ├── OrganizationAPIImpl.java │ │ │ │ │ │ │ ├── PageAPIImpl.java │ │ │ │ │ │ │ ├── PermissionAPIImpl.java │ │ │ │ │ │ │ ├── PlatformAPIImpl.java │ │ │ │ │ │ │ ├── PlatformCommandAPIImpl.java │ │ │ │ │ │ │ ├── PlatformLoginAPIImpl.java │ │ │ │ │ │ │ ├── ProcessAPIImpl.java │ │ │ │ │ │ │ ├── ProcessConfigurationAPIImpl.java │ │ │ │ │ │ │ ├── ProcessDeploymentAPIDelegate.java │ │ │ │ │ │ │ ├── ProcessInvolvementDelegate.java │ │ │ │ │ │ │ ├── ProcessManagementAPIImplDelegate.java │ │ │ │ │ │ │ ├── ProcessStarter.java │ │ │ │ │ │ │ ├── ProfileAPIImpl.java │ │ │ │ │ │ │ ├── SCustomUserInfoValueAPI.java │ │ │ │ │ │ │ ├── ServerAPIFactory.java │ │ │ │ │ │ │ ├── ServerAPIImpl.java │ │ │ │ │ │ │ ├── ServerAPIRuntimeException.java │ │ │ │ │ │ │ ├── SessionInfos.java │ │ │ │ │ │ │ ├── StarterThread.java │ │ │ │ │ │ │ ├── TaskInvolvementDelegate.java │ │ │ │ │ │ │ ├── TenantAdministrationAPIImpl.java │ │ │ │ │ │ │ ├── WithLock.java │ │ │ │ │ │ │ ├── application/ │ │ │ │ │ │ │ │ └── installer/ │ │ │ │ │ │ │ │ ├── ApplicationArchive.java │ │ │ │ │ │ │ │ ├── ApplicationArchiveReader.java │ │ │ │ │ │ │ │ ├── ApplicationInstaller.java │ │ │ │ │ │ │ │ ├── ApplicationInstallerImpl.java │ │ │ │ │ │ │ │ ├── CustomOrDefaultApplicationInstaller.java │ │ │ │ │ │ │ │ └── detector/ │ │ │ │ │ │ │ │ ├── ArtifactDetector.java │ │ │ │ │ │ │ │ ├── ArtifactTypeDetector.java │ │ │ │ │ │ │ │ ├── BdmDetector.java │ │ │ │ │ │ │ │ ├── CustomPageDetector.java │ │ │ │ │ │ │ │ ├── IconDetector.java │ │ │ │ │ │ │ │ ├── LayoutDetector.java │ │ │ │ │ │ │ │ ├── LivingApplicationDetector.java │ │ │ │ │ │ │ │ ├── OrganizationDetector.java │ │ │ │ │ │ │ │ ├── PageAndFormDetector.java │ │ │ │ │ │ │ │ ├── ProcessDetector.java │ │ │ │ │ │ │ │ ├── ThemeDetector.java │ │ │ │ │ │ │ │ └── XmlDetector.java │ │ │ │ │ │ │ ├── connector/ │ │ │ │ │ │ │ │ ├── ConnectorResetStrategy.java │ │ │ │ │ │ │ │ ├── ConnectorReseter.java │ │ │ │ │ │ │ │ └── ResetAllFailedConnectorStrategy.java │ │ │ │ │ │ │ ├── converter/ │ │ │ │ │ │ │ │ ├── ApplicationMenuModelConverter.java │ │ │ │ │ │ │ │ ├── ApplicationModelConverter.java │ │ │ │ │ │ │ │ ├── ApplicationPageModelConverter.java │ │ │ │ │ │ │ │ └── PageModelConverter.java │ │ │ │ │ │ │ ├── flownode/ │ │ │ │ │ │ │ │ └── FlowNodeRetrier.java │ │ │ │ │ │ │ ├── livingapplication/ │ │ │ │ │ │ │ │ ├── LivingApplicationAPIDelegate.java │ │ │ │ │ │ │ │ ├── LivingApplicationExporterDelegate.java │ │ │ │ │ │ │ │ ├── LivingApplicationMenuAPIDelegate.java │ │ │ │ │ │ │ │ └── LivingApplicationPageAPIDelegate.java │ │ │ │ │ │ │ ├── organization/ │ │ │ │ │ │ │ │ └── OrganizationAPIDelegate.java │ │ │ │ │ │ │ ├── page/ │ │ │ │ │ │ │ │ └── PageAPIDelegate.java │ │ │ │ │ │ │ ├── platform/ │ │ │ │ │ │ │ │ └── PlatformInformationAPIImpl.java │ │ │ │ │ │ │ ├── profile/ │ │ │ │ │ │ │ │ └── ProfileAPIDelegate.java │ │ │ │ │ │ │ ├── resolver/ │ │ │ │ │ │ │ │ ├── ActorBusinessArchiveArtifactManager.java │ │ │ │ │ │ │ │ ├── BARResourceArtifactManager.java │ │ │ │ │ │ │ │ ├── BusinessArchiveArtifactManager.java │ │ │ │ │ │ │ │ ├── BusinessArchiveArtifactsManager.java │ │ │ │ │ │ │ │ ├── BusinessDataBusinessArchiveArtifactManager.java │ │ │ │ │ │ │ │ ├── ClasspathArtifactManager.java │ │ │ │ │ │ │ │ ├── ConnectorBusinessArchiveArtifactManager.java │ │ │ │ │ │ │ │ ├── DocumentInitialValueArtifactManager.java │ │ │ │ │ │ │ │ ├── ExternalResourceArtifactManager.java │ │ │ │ │ │ │ │ ├── FormMappingAndPageArtifactManager.java │ │ │ │ │ │ │ │ ├── ParameterBusinessArchiveArtifactManager.java │ │ │ │ │ │ │ │ └── UserFilterBusinessArchiveArtifactManager.java │ │ │ │ │ │ │ └── transaction/ │ │ │ │ │ │ │ ├── AbstractGetEntity.java │ │ │ │ │ │ │ ├── CustomTransactions.java │ │ │ │ │ │ │ ├── activity/ │ │ │ │ │ │ │ │ ├── GetActivityInstances.java │ │ │ │ │ │ │ │ ├── GetArchivedActivityInstance.java │ │ │ │ │ │ │ │ ├── GetArchivedActivityInstances.java │ │ │ │ │ │ │ │ ├── GetContractOfUserTaskInstance.java │ │ │ │ │ │ │ │ └── GetNumberOfActivityInstance.java │ │ │ │ │ │ │ ├── actor/ │ │ │ │ │ │ │ │ ├── AddActor.java │ │ │ │ │ │ │ │ ├── AddActorMember.java │ │ │ │ │ │ │ │ ├── ExportActorMapping.java │ │ │ │ │ │ │ │ ├── GetActor.java │ │ │ │ │ │ │ │ ├── GetActorInitiators.java │ │ │ │ │ │ │ │ ├── GetActorsByActorIds.java │ │ │ │ │ │ │ │ ├── GetNumberOfActorMembers.java │ │ │ │ │ │ │ │ ├── GetNumberOfActors.java │ │ │ │ │ │ │ │ ├── GetNumberOfGroupsOfActor.java │ │ │ │ │ │ │ │ ├── GetNumberOfMembershipsOfActor.java │ │ │ │ │ │ │ │ ├── GetNumberOfRolesOfActor.java │ │ │ │ │ │ │ │ ├── GetNumberOfUsersOfActor.java │ │ │ │ │ │ │ │ ├── ImportActorMapping.java │ │ │ │ │ │ │ │ └── RemoveActorMember.java │ │ │ │ │ │ │ ├── application/ │ │ │ │ │ │ │ │ ├── SearchApplicationMenus.java │ │ │ │ │ │ │ │ ├── SearchApplicationPages.java │ │ │ │ │ │ │ │ ├── SearchApplications.java │ │ │ │ │ │ │ │ └── SearchApplicationsOfUser.java │ │ │ │ │ │ │ ├── category/ │ │ │ │ │ │ │ │ ├── CreateCategory.java │ │ │ │ │ │ │ │ ├── DeleteSCategory.java │ │ │ │ │ │ │ │ ├── GetCategories.java │ │ │ │ │ │ │ │ ├── GetCategory.java │ │ │ │ │ │ │ │ ├── GetNumberOfCategories.java │ │ │ │ │ │ │ │ ├── GetNumberOfCategoriesOfProcess.java │ │ │ │ │ │ │ │ ├── GetNumberOfCategoriesUnrelatedToProcess.java │ │ │ │ │ │ │ │ ├── RemoveCategoriesFromProcessDefinition.java │ │ │ │ │ │ │ │ ├── RemoveProcessDefinitionsOfCategory.java │ │ │ │ │ │ │ │ └── UpdateCategory.java │ │ │ │ │ │ │ ├── command/ │ │ │ │ │ │ │ │ ├── DeleteSCommand.java │ │ │ │ │ │ │ │ └── GetCommands.java │ │ │ │ │ │ │ ├── connector/ │ │ │ │ │ │ │ │ └── GetConnectorImplementation.java │ │ │ │ │ │ │ ├── document/ │ │ │ │ │ │ │ │ └── GetDocumentByNameAtProcessInstantiation.java │ │ │ │ │ │ │ ├── event/ │ │ │ │ │ │ │ │ └── GetEventInstances.java │ │ │ │ │ │ │ ├── expression/ │ │ │ │ │ │ │ │ ├── AbstractEvaluateExpressionsInstance.java │ │ │ │ │ │ │ │ ├── EntityMerger.java │ │ │ │ │ │ │ │ ├── EvaluateExpression.java │ │ │ │ │ │ │ │ ├── EvaluateExpressionsDefinitionLevel.java │ │ │ │ │ │ │ │ ├── EvaluateExpressionsInstanceLevel.java │ │ │ │ │ │ │ │ └── EvaluateExpressionsInstanceLevelAndArchived.java │ │ │ │ │ │ │ ├── flownode/ │ │ │ │ │ │ │ │ ├── GetFlowNodeInstance.java │ │ │ │ │ │ │ │ ├── SearchFlowNodeInstances.java │ │ │ │ │ │ │ │ └── SetExpectedEndDate.java │ │ │ │ │ │ │ ├── identity/ │ │ │ │ │ │ │ │ ├── AddUserMembership.java │ │ │ │ │ │ │ │ ├── AddUserMemberships.java │ │ │ │ │ │ │ │ ├── DeleteGroup.java │ │ │ │ │ │ │ │ ├── DeleteGroups.java │ │ │ │ │ │ │ │ ├── DeleteRole.java │ │ │ │ │ │ │ │ ├── DeleteRoles.java │ │ │ │ │ │ │ │ ├── DeleteUser.java │ │ │ │ │ │ │ │ ├── DeleteUsers.java │ │ │ │ │ │ │ │ ├── DeleteWithActorMembers.java │ │ │ │ │ │ │ │ ├── GetGroups.java │ │ │ │ │ │ │ │ ├── GetNumberOfInstance.java │ │ │ │ │ │ │ │ ├── GetNumberOfUserMemberships.java │ │ │ │ │ │ │ │ ├── GetNumberOfUsersInType.java │ │ │ │ │ │ │ │ ├── GetRoles.java │ │ │ │ │ │ │ │ ├── GetSContactInfo.java │ │ │ │ │ │ │ │ ├── GetSUser.java │ │ │ │ │ │ │ │ ├── GetUserMembership.java │ │ │ │ │ │ │ │ ├── GetUserMembershipsOfGroup.java │ │ │ │ │ │ │ │ ├── GetUserMembershipsOfRole.java │ │ │ │ │ │ │ │ ├── UpdateGroup.java │ │ │ │ │ │ │ │ └── UpdateMembershipByRoleIdAndGroupId.java │ │ │ │ │ │ │ ├── page/ │ │ │ │ │ │ │ │ └── SearchPages.java │ │ │ │ │ │ │ ├── platform/ │ │ │ │ │ │ │ │ ├── DeleteSPlatformCommand.java │ │ │ │ │ │ │ │ ├── GetPlatformContent.java │ │ │ │ │ │ │ │ ├── GetSPlatformCommands.java │ │ │ │ │ │ │ │ └── UpdateSPlatformCommand.java │ │ │ │ │ │ │ ├── process/ │ │ │ │ │ │ │ │ ├── AbstractGetProcessDeploymentInfo.java │ │ │ │ │ │ │ │ ├── AddProcessDefinitionToCategory.java │ │ │ │ │ │ │ │ ├── DisableProcess.java │ │ │ │ │ │ │ │ ├── EnableProcess.java │ │ │ │ │ │ │ │ ├── GetArchivedProcessInstanceList.java │ │ │ │ │ │ │ │ ├── GetChildInstanceIdsOfProcessInstance.java │ │ │ │ │ │ │ │ ├── GetLastArchivedProcessInstance.java │ │ │ │ │ │ │ │ ├── GetLatestProcessDefinitionId.java │ │ │ │ │ │ │ │ ├── GetNumberOfProcessDeploymentInfos.java │ │ │ │ │ │ │ │ ├── GetNumberOfProcessDeploymentInfosUnrelatedToCategory.java │ │ │ │ │ │ │ │ ├── GetNumberOfProcessInstance.java │ │ │ │ │ │ │ │ ├── GetProcessDefinitionDeployInfos.java │ │ │ │ │ │ │ │ ├── GetProcessDefinitionDeployInfosWithActorOnlyForGroup.java │ │ │ │ │ │ │ │ ├── GetProcessDefinitionDeployInfosWithActorOnlyForGroups.java │ │ │ │ │ │ │ │ ├── GetProcessDefinitionDeployInfosWithActorOnlyForRole.java │ │ │ │ │ │ │ │ ├── GetProcessDefinitionDeployInfosWithActorOnlyForRoles.java │ │ │ │ │ │ │ │ ├── GetProcessDefinitionDeployInfosWithActorOnlyForUser.java │ │ │ │ │ │ │ │ ├── GetProcessDefinitionDeployInfosWithActorOnlyForUsers.java │ │ │ │ │ │ │ │ ├── SetProcessInstanceState.java │ │ │ │ │ │ │ │ └── UpdateProcessDeploymentInfo.java │ │ │ │ │ │ │ ├── profile/ │ │ │ │ │ │ │ │ ├── CreateProfileMember.java │ │ │ │ │ │ │ │ └── ProfileMemberUtils.java │ │ │ │ │ │ │ └── task/ │ │ │ │ │ │ │ ├── AssignOrUnassignUserTask.java │ │ │ │ │ │ │ ├── AssignUserTaskIfNotAssigned.java │ │ │ │ │ │ │ ├── GetAssignedTasks.java │ │ │ │ │ │ │ ├── GetHumanTaskInstance.java │ │ │ │ │ │ │ ├── GetNumberOfOpenTasksForUsers.java │ │ │ │ │ │ │ └── SetTaskPriority.java │ │ │ │ │ │ └── utils/ │ │ │ │ │ │ └── VisibleForTesting.java │ │ │ │ │ ├── authorization/ │ │ │ │ │ │ └── PermissionServiceImpl.java │ │ │ │ │ ├── bar/ │ │ │ │ │ │ ├── BusinessArchiveService.java │ │ │ │ │ │ └── BusinessArchiveServiceImpl.java │ │ │ │ │ ├── bpm/ │ │ │ │ │ │ ├── contract/ │ │ │ │ │ │ │ └── validation/ │ │ │ │ │ │ │ ├── ConstraintsDefinitionHelper.java │ │ │ │ │ │ │ ├── ContractConstraintsValidator.java │ │ │ │ │ │ │ ├── ContractStructureValidator.java │ │ │ │ │ │ │ ├── ContractTypeValidator.java │ │ │ │ │ │ │ ├── ContractValidator.java │ │ │ │ │ │ │ ├── ContractValidatorFactory.java │ │ │ │ │ │ │ ├── ContractVariableHelper.java │ │ │ │ │ │ │ ├── ErrorReporter.java │ │ │ │ │ │ │ └── InputValidationException.java │ │ │ │ │ │ ├── model/ │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ └── BPMInstancesCreator.java │ │ │ │ │ │ └── process/ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ └── ProcessInstanceBuilder.java │ │ │ │ │ ├── business/ │ │ │ │ │ │ ├── application/ │ │ │ │ │ │ │ ├── converter/ │ │ │ │ │ │ │ │ ├── ApplicationMenuToNodeConverter.java │ │ │ │ │ │ │ │ ├── ApplicationPageToNodeConverter.java │ │ │ │ │ │ │ │ ├── ApplicationToNodeConverter.java │ │ │ │ │ │ │ │ ├── ApplicationsToNodeContainerConverter.java │ │ │ │ │ │ │ │ ├── NodeToApplicationConverter.java │ │ │ │ │ │ │ │ ├── NodeToApplicationMenuConverter.java │ │ │ │ │ │ │ │ └── NodeToApplicationPageConverter.java │ │ │ │ │ │ │ ├── exporter/ │ │ │ │ │ │ │ │ ├── ApplicationContainerExporter.java │ │ │ │ │ │ │ │ └── ApplicationExporter.java │ │ │ │ │ │ │ └── importer/ │ │ │ │ │ │ │ ├── ApplicationImportStrategy.java │ │ │ │ │ │ │ ├── ApplicationImporter.java │ │ │ │ │ │ │ ├── ApplicationMenuImportResult.java │ │ │ │ │ │ │ ├── ApplicationMenuImporter.java │ │ │ │ │ │ │ ├── ApplicationPageImportResult.java │ │ │ │ │ │ │ ├── ApplicationPageImporter.java │ │ │ │ │ │ │ ├── ApplicationZipContent.java │ │ │ │ │ │ │ ├── DefaultLivingApplicationImporter.java │ │ │ │ │ │ │ ├── FailOnDuplicateApplicationImportStrategy.java │ │ │ │ │ │ │ ├── ImportResult.java │ │ │ │ │ │ │ ├── LivingApplicationImporter.java │ │ │ │ │ │ │ ├── MandatoryLivingApplicationImporter.java │ │ │ │ │ │ │ ├── ReplaceDuplicateApplicationImportStrategy.java │ │ │ │ │ │ │ ├── StrategySelector.java │ │ │ │ │ │ │ ├── UpdateNewerNonEditableApplicationStrategy.java │ │ │ │ │ │ │ └── validator/ │ │ │ │ │ │ │ ├── ApplicationImportValidator.java │ │ │ │ │ │ │ ├── ApplicationMenuCreatorValidator.java │ │ │ │ │ │ │ ├── ApplicationTokenValidator.java │ │ │ │ │ │ │ └── ValidationStatus.java │ │ │ │ │ │ └── data/ │ │ │ │ │ │ ├── BusinessDataRetriever.java │ │ │ │ │ │ ├── RefBusinessDataRetriever.java │ │ │ │ │ │ └── converter/ │ │ │ │ │ │ └── BusinessDataModelConverter.java │ │ │ │ │ ├── command/ │ │ │ │ │ │ ├── AbstractStartProcessCommand.java │ │ │ │ │ │ ├── AdvancedStartProcessCommand.java │ │ │ │ │ │ ├── BusinessDataCommandField.java │ │ │ │ │ │ ├── Command.java │ │ │ │ │ │ ├── DeletePlatformSessionCommand.java │ │ │ │ │ │ ├── ExecuteBDMQueryCommand.java │ │ │ │ │ │ ├── GetBusinessDataByIdCommand.java │ │ │ │ │ │ ├── GetBusinessDataByIdsCommand.java │ │ │ │ │ │ ├── GetBusinessDataByQueryCommand.java │ │ │ │ │ │ ├── MultipleStartPointsProcessCommand.java │ │ │ │ │ │ ├── RuntimeCommand.java │ │ │ │ │ │ ├── SCommandExecutionException.java │ │ │ │ │ │ ├── SCommandParameterizationException.java │ │ │ │ │ │ ├── SGroupProfileMemberAlreadyExistsException.java │ │ │ │ │ │ ├── SRoleProfileMemberAlreadyExistsException.java │ │ │ │ │ │ ├── SUserMembershipProfileMemberAlreadyExistsException.java │ │ │ │ │ │ ├── SUserProfileMemberAlreadyExistsException.java │ │ │ │ │ │ └── system/ │ │ │ │ │ │ └── SearchWaitingEventsCommand.java │ │ │ │ │ ├── connector/ │ │ │ │ │ │ ├── ConnectorAPIAccessorImpl.java │ │ │ │ │ │ └── ConnectorServiceDecorator.java │ │ │ │ │ ├── core/ │ │ │ │ │ │ ├── document/ │ │ │ │ │ │ │ └── api/ │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ └── DocumentHelper.java │ │ │ │ │ │ └── form/ │ │ │ │ │ │ ├── ExternalURLAdapter.java │ │ │ │ │ │ └── LegacyURLAdapter.java │ │ │ │ │ ├── data/ │ │ │ │ │ │ └── ParentContainerResolverImpl.java │ │ │ │ │ ├── event/ │ │ │ │ │ │ └── PlatformStartedEvent.java │ │ │ │ │ ├── execution/ │ │ │ │ │ │ ├── AdvancedStartProcessValidator.java │ │ │ │ │ │ ├── ContainerExecutor.java │ │ │ │ │ │ ├── ContainerRegistry.java │ │ │ │ │ │ ├── EvaluateExpression.java │ │ │ │ │ │ ├── Filter.java │ │ │ │ │ │ ├── FlowElementExecutor.java │ │ │ │ │ │ ├── FlowNodeExecutor.java │ │ │ │ │ │ ├── FlowNodeExecutorImpl.java │ │ │ │ │ │ ├── FlowNodeIdFilter.java │ │ │ │ │ │ ├── FlowNodeNameFilter.java │ │ │ │ │ │ ├── FlowNodeSelector.java │ │ │ │ │ │ ├── FlowNodeStateManagerImpl.java │ │ │ │ │ │ ├── ProcessExecutor.java │ │ │ │ │ │ ├── ProcessExecutorImpl.java │ │ │ │ │ │ ├── ProcessInstanceInterruptor.java │ │ │ │ │ │ ├── ProcessStarterVerifier.java │ │ │ │ │ │ ├── ProcessStarterVerifierImpl.java │ │ │ │ │ │ ├── SIllegalStateTransition.java │ │ │ │ │ │ ├── SNotSerializableException.java │ │ │ │ │ │ ├── SUnreleasableTaskException.java │ │ │ │ │ │ ├── StartFlowNodeFilter.java │ │ │ │ │ │ ├── StateBehaviors.java │ │ │ │ │ │ ├── TransitionEvaluator.java │ │ │ │ │ │ ├── WaitingEventsInterrupter.java │ │ │ │ │ │ ├── archive/ │ │ │ │ │ │ │ └── BPMArchiverService.java │ │ │ │ │ │ ├── event/ │ │ │ │ │ │ │ ├── CoupleEventHandlerStrategy.java │ │ │ │ │ │ │ ├── ErrorEventHandlerStrategy.java │ │ │ │ │ │ │ ├── EventHandlerStrategy.java │ │ │ │ │ │ │ ├── EventsHandler.java │ │ │ │ │ │ │ ├── MessageEventHandlerStrategy.java │ │ │ │ │ │ │ ├── OperationsWithContext.java │ │ │ │ │ │ │ ├── SBPMEventHandlerException.java │ │ │ │ │ │ │ ├── SignalEventHandlerStrategy.java │ │ │ │ │ │ │ ├── TerminateEventHandlerStrategy.java │ │ │ │ │ │ │ └── TimerEventHandlerStrategy.java │ │ │ │ │ │ ├── flowmerger/ │ │ │ │ │ │ │ └── FlowNodeTransitionsWrapper.java │ │ │ │ │ │ ├── handler/ │ │ │ │ │ │ │ ├── ArchiveProcessInstancesHandler.java │ │ │ │ │ │ │ └── SProcessInstanceHandler.java │ │ │ │ │ │ ├── job/ │ │ │ │ │ │ │ └── JobNameBuilder.java │ │ │ │ │ │ ├── state/ │ │ │ │ │ │ │ ├── AbortedFlowNodeState.java │ │ │ │ │ │ │ ├── AbortingActivityWithBoundaryState.java │ │ │ │ │ │ │ ├── AbortingBoundaryAndIntermediateCatchEventState.java │ │ │ │ │ │ │ ├── AbortingBoundaryEventsOnCompletingActivityState.java │ │ │ │ │ │ │ ├── AbortingCallActivityState.java │ │ │ │ │ │ │ ├── AbortingFlowNodeContainerState.java │ │ │ │ │ │ │ ├── AbortingFlowNodeState.java │ │ │ │ │ │ │ ├── AbortingReceiveTaskState.java │ │ │ │ │ │ │ ├── AbortingSubTaskState.java │ │ │ │ │ │ │ ├── CancelledFlowNodeState.java │ │ │ │ │ │ │ ├── CancellingActivityWithBoundaryState.java │ │ │ │ │ │ │ ├── CancellingBoundaryAndIntermediateCatchEventState.java │ │ │ │ │ │ │ ├── CancellingCallActivityState.java │ │ │ │ │ │ │ ├── CancellingFlowNodeContainerChildrenState.java │ │ │ │ │ │ │ ├── CancellingFlowNodeState.java │ │ │ │ │ │ │ ├── CancellingReceiveTaskState.java │ │ │ │ │ │ │ ├── CompletedActivityState.java │ │ │ │ │ │ │ ├── CompletingActivityState.java │ │ │ │ │ │ │ ├── CompletingCallActivityState.java │ │ │ │ │ │ │ ├── EndingCallActivityExceptionState.java │ │ │ │ │ │ │ ├── EndingFlowNodeContainerExceptionState.java │ │ │ │ │ │ │ ├── ExecutingAutomaticActivityState.java │ │ │ │ │ │ │ ├── ExecutingBoundaryEventState.java │ │ │ │ │ │ │ ├── ExecutingCallActivityState.java │ │ │ │ │ │ │ ├── ExecutingFlowNodeState.java │ │ │ │ │ │ │ ├── ExecutingLoopActivityState.java │ │ │ │ │ │ │ ├── ExecutingMultiInstanceActivityState.java │ │ │ │ │ │ │ ├── ExecutingThrowEventState.java │ │ │ │ │ │ │ ├── FailedActivityState.java │ │ │ │ │ │ │ ├── FlowNodeStateManager.java │ │ │ │ │ │ │ ├── InitializingActivityState.java │ │ │ │ │ │ │ ├── InitializingActivityWithBoundaryEventsState.java │ │ │ │ │ │ │ ├── InitializingAndExecutingFlowNodeState.java │ │ │ │ │ │ │ ├── InitializingBoundaryEventState.java │ │ │ │ │ │ │ ├── InitializingLoopActivityState.java │ │ │ │ │ │ │ ├── InitializingMultiInstanceActivityState.java │ │ │ │ │ │ │ ├── InterruptingBoundaryAndIntermediateCatchEventState.java │ │ │ │ │ │ │ ├── OnEnterAndFinishConnectorState.java │ │ │ │ │ │ │ ├── OnEnterConnectorState.java │ │ │ │ │ │ │ ├── OnEnterOrOnFinishConnectorState.java │ │ │ │ │ │ │ ├── OnFinishConnectorState.java │ │ │ │ │ │ │ ├── ReadyActivityState.java │ │ │ │ │ │ │ ├── SkippedFlowNodeState.java │ │ │ │ │ │ │ └── WaitingFlowNodeState.java │ │ │ │ │ │ ├── transition/ │ │ │ │ │ │ │ ├── AutomaticTaskStates.java │ │ │ │ │ │ │ ├── BoundaryEventStates.java │ │ │ │ │ │ │ ├── CallActivityTaskStates.java │ │ │ │ │ │ │ ├── DefaultTransitionGetter.java │ │ │ │ │ │ │ ├── EndEventStates.java │ │ │ │ │ │ │ ├── EvaluatedTransitions.java │ │ │ │ │ │ │ ├── ExclusiveGatewayTransitionEvaluationStrategy.java │ │ │ │ │ │ │ ├── FlowNodeStateSequences.java │ │ │ │ │ │ │ ├── GatewaysStates.java │ │ │ │ │ │ │ ├── ImplicitGatewayTransitionEvaluator.java │ │ │ │ │ │ │ ├── InclusiveExclusiveTransitionEvaluator.java │ │ │ │ │ │ │ ├── InclusiveGatewayTransitionEvaluationStrategy.java │ │ │ │ │ │ │ ├── IntermediateCatchEventStates.java │ │ │ │ │ │ │ ├── IntermediateThrowEventStates.java │ │ │ │ │ │ │ ├── LoopActivityStates.java │ │ │ │ │ │ │ ├── ManualTaskStates.java │ │ │ │ │ │ │ ├── MultiInstanceActivityStates.java │ │ │ │ │ │ │ ├── ParallelGatewayTransitionEvaluator.java │ │ │ │ │ │ │ ├── ReceiveTaskStates.java │ │ │ │ │ │ │ ├── STransitionConditionEvaluationException.java │ │ │ │ │ │ │ ├── SendTaskStates.java │ │ │ │ │ │ │ ├── StartEventStates.java │ │ │ │ │ │ │ ├── SubProcessActivityTaskStates.java │ │ │ │ │ │ │ ├── TransitionConditionEvaluator.java │ │ │ │ │ │ │ ├── TransitionEvaluationStrategy.java │ │ │ │ │ │ │ └── UserTaskStates.java │ │ │ │ │ │ └── work/ │ │ │ │ │ │ ├── BPMWorkFactory.java │ │ │ │ │ │ ├── ExecuteConnectorOfActivity.java │ │ │ │ │ │ ├── ExecuteConnectorOfProcess.java │ │ │ │ │ │ ├── ExecuteConnectorWork.java │ │ │ │ │ │ ├── ExecuteFlowNodeWork.java │ │ │ │ │ │ ├── ExecuteMessageCoupleWork.java │ │ │ │ │ │ ├── FailedStateSetter.java │ │ │ │ │ │ ├── InSessionBonitaWork.java │ │ │ │ │ │ ├── LockProcessInstanceWork.java │ │ │ │ │ │ ├── NotifyChildFinishedWork.java │ │ │ │ │ │ ├── RestartException.java │ │ │ │ │ │ ├── SetInFailCallable.java │ │ │ │ │ │ ├── TenantAwareBonitaWork.java │ │ │ │ │ │ ├── TxBonitaWork.java │ │ │ │ │ │ ├── WrappingBonitaWork.java │ │ │ │ │ │ └── failurewrapping/ │ │ │ │ │ │ ├── ConnectorDefinitionAndInstanceContextWork.java │ │ │ │ │ │ ├── FlowNodeDefinitionAndInstanceContextWork.java │ │ │ │ │ │ ├── MessageInstanceContextWork.java │ │ │ │ │ │ ├── ProcessDefinitionContextWork.java │ │ │ │ │ │ ├── ProcessInstanceContextWork.java │ │ │ │ │ │ ├── TriggerSignalWork.java │ │ │ │ │ │ └── TxInHandleFailureWrappingWork.java │ │ │ │ │ ├── expression/ │ │ │ │ │ │ ├── BusinessDataExpressionExecutorStrategy.java │ │ │ │ │ │ ├── BusinessDataReferenceExpressionExecutorStrategy.java │ │ │ │ │ │ ├── BusinessObjectDAOExpressionStrategy.java │ │ │ │ │ │ ├── CommonBusinessDataExpressionExecutorStrategy.java │ │ │ │ │ │ ├── ContractInputExpressionExecutorStrategy.java │ │ │ │ │ │ ├── DataExpressionExecutorStrategy.java │ │ │ │ │ │ ├── DocumentListReferenceExpressionExecutorStrategy.java │ │ │ │ │ │ ├── DocumentReferenceExpressionExecutorStrategy.java │ │ │ │ │ │ ├── EngineConstantExpressionBuilder.java │ │ │ │ │ │ ├── EngineConstantExpressionExecutorStrategy.java │ │ │ │ │ │ ├── ParameterExpressionExecutorStrategy.java │ │ │ │ │ │ └── QueryBusinessDataExpressionExecutorStrategy.java │ │ │ │ │ ├── handler/ │ │ │ │ │ │ └── SchedulerServiceRestartHandler.java │ │ │ │ │ ├── identity/ │ │ │ │ │ │ ├── CustomUserInfoDefinitionImporter.java │ │ │ │ │ │ ├── CustomUserInfoValueImporter.java │ │ │ │ │ │ ├── ExportOrganization.java │ │ │ │ │ │ ├── ImportDuplicateInOrganizationException.java │ │ │ │ │ │ ├── ImportOrganization.java │ │ │ │ │ │ ├── ImportOrganizationFailOnDuplicatesStrategy.java │ │ │ │ │ │ ├── ImportOrganizationIgnoreDuplicatesStrategy.java │ │ │ │ │ │ ├── ImportOrganizationMergeDuplicatesStrategy.java │ │ │ │ │ │ ├── ImportOrganizationStrategy.java │ │ │ │ │ │ ├── SImportOrganizationException.java │ │ │ │ │ │ └── UserImporter.java │ │ │ │ │ ├── jobs/ │ │ │ │ │ │ ├── InternalJob.java │ │ │ │ │ │ └── TriggerTimerEventJob.java │ │ │ │ │ ├── log/ │ │ │ │ │ │ └── LogMessageBuilder.java │ │ │ │ │ ├── message/ │ │ │ │ │ │ └── MessagesHandlingService.java │ │ │ │ │ ├── operation/ │ │ │ │ │ │ ├── AbstractDocumentLeftOperandHandler.java │ │ │ │ │ │ ├── BusinessDataAssignmentStrategy.java │ │ │ │ │ │ ├── BusinessDataContext.java │ │ │ │ │ │ ├── BusinessDataJavaMethodOperationExecutorStrategy.java │ │ │ │ │ │ ├── BusinessDataLeftOperandHandler.java │ │ │ │ │ │ ├── DataLeftOperandHandler.java │ │ │ │ │ │ ├── DocumentLeftOperandHandler.java │ │ │ │ │ │ ├── DocumentListLeftOperandHandler.java │ │ │ │ │ │ ├── EntitiesActionsExecutor.java │ │ │ │ │ │ ├── EntityAction.java │ │ │ │ │ │ ├── MergeEntityAction.java │ │ │ │ │ │ ├── SEntityActionExecutionException.java │ │ │ │ │ │ ├── StringIndexLeftOperandHandler.java │ │ │ │ │ │ ├── TransientDataLeftOperandHandler.java │ │ │ │ │ │ └── UpdateDataRefAction.java │ │ │ │ │ ├── page/ │ │ │ │ │ │ ├── AuthorizationRuleWithParameters.java │ │ │ │ │ │ ├── IsActorInitiatorRule.java │ │ │ │ │ │ ├── IsAdminRule.java │ │ │ │ │ │ ├── IsInvolvedInProcessInstanceRule.java │ │ │ │ │ │ ├── IsManagerOfUserInvolvedInProcessInstanceRule.java │ │ │ │ │ │ ├── IsProcessInitiatorRule.java │ │ │ │ │ │ ├── IsProcessOwnerRule.java │ │ │ │ │ │ ├── IsTaskAvailableForUserRule.java │ │ │ │ │ │ └── IsTaskPerformerRule.java │ │ │ │ │ ├── platform/ │ │ │ │ │ │ ├── BroadcastServiceLocal.java │ │ │ │ │ │ ├── PlatformManager.java │ │ │ │ │ │ ├── PlatformStateProvider.java │ │ │ │ │ │ ├── PlatformVersionChecker.java │ │ │ │ │ │ └── configuration/ │ │ │ │ │ │ ├── NodeConfiguration.java │ │ │ │ │ │ ├── NodeConfigurationImpl.java │ │ │ │ │ │ └── datasource/ │ │ │ │ │ │ ├── QuartzConnectionProvider.java │ │ │ │ │ │ ├── QuartzDataSourceAccessor.java │ │ │ │ │ │ └── QuartzDataSourceAccessorProvider.java │ │ │ │ │ ├── profile/ │ │ │ │ │ │ ├── DefaultProfilesUpdater.java │ │ │ │ │ │ ├── DeleteExistingImportStrategy.java │ │ │ │ │ │ ├── FailOnDuplicateImportStrategy.java │ │ │ │ │ │ ├── IgnoreDuplicateImportStrategy.java │ │ │ │ │ │ ├── ImportPolicy.java │ │ │ │ │ │ ├── ProfileImportStrategy.java │ │ │ │ │ │ ├── ProfilesExporter.java │ │ │ │ │ │ ├── ProfilesImporter.java │ │ │ │ │ │ ├── ReplaceDuplicateImportStrategy.java │ │ │ │ │ │ ├── UpdateDefaultsAndCreateNewImportStrategy.java │ │ │ │ │ │ └── UpdateDefaultsImportStrategy.java │ │ │ │ │ ├── search/ │ │ │ │ │ │ ├── AbstractActivityInstanceSearchEntity.java │ │ │ │ │ │ ├── AbstractArchiveActivityInstanceSearchEntity.java │ │ │ │ │ │ ├── AbstractArchivedCommentsSearchEntity.java │ │ │ │ │ │ ├── AbstractArchivedConnectorInstanceSearchEntity.java │ │ │ │ │ │ ├── AbstractArchivedDocumentSearchEntity.java │ │ │ │ │ │ ├── AbstractArchivedHumanTaskInstanceSearchEntity.java │ │ │ │ │ │ ├── AbstractArchivedProcessInstanceSearchEntity.java │ │ │ │ │ │ ├── AbstractCommandSearchEntity.java │ │ │ │ │ │ ├── AbstractCommentSearchEntity.java │ │ │ │ │ │ ├── AbstractDocumentSearchEntity.java │ │ │ │ │ │ ├── AbstractGroupSearchEntity.java │ │ │ │ │ │ ├── AbstractHumanTaskInstanceSearchEntity.java │ │ │ │ │ │ ├── AbstractProcessDeploymentInfoSearchEntity.java │ │ │ │ │ │ ├── AbstractProcessInstanceSearchEntity.java │ │ │ │ │ │ ├── AbstractProfileSearchEntity.java │ │ │ │ │ │ ├── AbstractRoleSearchEntity.java │ │ │ │ │ │ ├── AbstractSearchEntity.java │ │ │ │ │ │ ├── AbstractSupervisorSearchEntity.java │ │ │ │ │ │ ├── AbstractUserSearchEntity.java │ │ │ │ │ │ ├── BonitaReadFunction.java │ │ │ │ │ │ ├── SPageOutOfRangeException.java │ │ │ │ │ │ ├── SSearchException.java │ │ │ │ │ │ ├── SearchCommands.java │ │ │ │ │ │ ├── activity/ │ │ │ │ │ │ │ ├── SearchActivityInstances.java │ │ │ │ │ │ │ └── SearchArchivedActivityInstances.java │ │ │ │ │ │ ├── comment/ │ │ │ │ │ │ │ ├── SearchArchivedComments.java │ │ │ │ │ │ │ ├── SearchComments.java │ │ │ │ │ │ │ ├── SearchCommentsInvolvingUser.java │ │ │ │ │ │ │ └── SearchCommentsManagedBy.java │ │ │ │ │ │ ├── connector/ │ │ │ │ │ │ │ └── SearchArchivedConnectorInstance.java │ │ │ │ │ │ ├── descriptor/ │ │ │ │ │ │ │ ├── FieldDescriptor.java │ │ │ │ │ │ │ ├── SearchActivityInstanceDescriptor.java │ │ │ │ │ │ │ ├── SearchApplicationDescriptor.java │ │ │ │ │ │ │ ├── SearchApplicationMenuDescriptor.java │ │ │ │ │ │ │ ├── SearchApplicationPageDescriptor.java │ │ │ │ │ │ │ ├── SearchArchivedActivityInstanceDescriptor.java │ │ │ │ │ │ │ ├── SearchArchivedCommentsDescriptor.java │ │ │ │ │ │ │ ├── SearchArchivedConnectorInstanceDescriptor.java │ │ │ │ │ │ │ ├── SearchArchivedDocumentDescriptor.java │ │ │ │ │ │ │ ├── SearchArchivedFlowNodeInstanceDescriptor.java │ │ │ │ │ │ │ ├── SearchArchivedHumanTaskInstanceDescriptor.java │ │ │ │ │ │ │ ├── SearchArchivedProcessInstancesDescriptor.java │ │ │ │ │ │ │ ├── SearchCommandDescriptor.java │ │ │ │ │ │ │ ├── SearchCommentDescriptor.java │ │ │ │ │ │ │ ├── SearchConnectorInstanceDescriptor.java │ │ │ │ │ │ │ ├── SearchCustomUserInfoValueDescriptor.java │ │ │ │ │ │ │ ├── SearchDocumentDescriptor.java │ │ │ │ │ │ │ ├── SearchEntitiesDescriptor.java │ │ │ │ │ │ │ ├── SearchEntityDescriptor.java │ │ │ │ │ │ │ ├── SearchEventTriggerInstanceDescriptor.java │ │ │ │ │ │ │ ├── SearchFlowNodeInstanceDescriptor.java │ │ │ │ │ │ │ ├── SearchFormMappingDescriptor.java │ │ │ │ │ │ │ ├── SearchGroupDescriptor.java │ │ │ │ │ │ │ ├── SearchHumanTaskInstanceDescriptor.java │ │ │ │ │ │ │ ├── SearchMessageInstanceDescriptor.java │ │ │ │ │ │ │ ├── SearchPageDescriptor.java │ │ │ │ │ │ │ ├── SearchProcessDefinitionsDescriptor.java │ │ │ │ │ │ │ ├── SearchProcessInstanceDescriptor.java │ │ │ │ │ │ │ ├── SearchProcessSupervisorDescriptor.java │ │ │ │ │ │ │ ├── SearchProfileDescriptor.java │ │ │ │ │ │ │ ├── SearchProfileMemberGroupDescriptor.java │ │ │ │ │ │ │ ├── SearchProfileMemberRoleAndGroupDescriptor.java │ │ │ │ │ │ │ ├── SearchProfileMemberRoleDescriptor.java │ │ │ │ │ │ │ ├── SearchProfileMemberUserDescriptor.java │ │ │ │ │ │ │ ├── SearchRoleDescriptor.java │ │ │ │ │ │ │ ├── SearchUserDescriptor.java │ │ │ │ │ │ │ └── SearchWaitingEventSerchDescriptor.java │ │ │ │ │ │ ├── document/ │ │ │ │ │ │ │ ├── SearchArchivedDocuments.java │ │ │ │ │ │ │ ├── SearchArchivedDocumentsSupervisedBy.java │ │ │ │ │ │ │ ├── SearchDocuments.java │ │ │ │ │ │ │ └── SearchDocumentsSupervisedBy.java │ │ │ │ │ │ ├── events/ │ │ │ │ │ │ │ └── trigger/ │ │ │ │ │ │ │ ├── SearchTimerEventTriggerInstances.java │ │ │ │ │ │ │ └── SearchWaitingEvents.java │ │ │ │ │ │ ├── flownode/ │ │ │ │ │ │ │ ├── SearchArchivedFlowNodeInstances.java │ │ │ │ │ │ │ └── SearchFlowNodeInstances.java │ │ │ │ │ │ ├── form/ │ │ │ │ │ │ │ └── SearchFormMappings.java │ │ │ │ │ │ ├── identity/ │ │ │ │ │ │ │ ├── SearchCustomUserInfoValues.java │ │ │ │ │ │ │ ├── SearchGroups.java │ │ │ │ │ │ │ ├── SearchRoles.java │ │ │ │ │ │ │ ├── SearchUsers.java │ │ │ │ │ │ │ ├── SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo.java │ │ │ │ │ │ │ └── SearchUsersWhoCanStartProcessDeploymentInfo.java │ │ │ │ │ │ ├── process/ │ │ │ │ │ │ │ ├── SearchArchivedProcessInstances.java │ │ │ │ │ │ │ ├── SearchArchivedProcessInstancesInvolvingUser.java │ │ │ │ │ │ │ ├── SearchArchivedProcessInstancesSupervisedBy.java │ │ │ │ │ │ │ ├── SearchArchivedProcessInstancesWithoutSubProcess.java │ │ │ │ │ │ │ ├── SearchFailedProcessInstances.java │ │ │ │ │ │ │ ├── SearchFailedProcessInstancesSupervisedBy.java │ │ │ │ │ │ │ ├── SearchOpenProcessInstancesInvolvingUser.java │ │ │ │ │ │ │ ├── SearchOpenProcessInstancesInvolvingUsersManagedBy.java │ │ │ │ │ │ │ ├── SearchOpenProcessInstancesSupervisedBy.java │ │ │ │ │ │ │ ├── SearchProcessDeploymentInfos.java │ │ │ │ │ │ │ ├── SearchProcessDeploymentInfosCanBeStartedBy.java │ │ │ │ │ │ │ ├── SearchProcessDeploymentInfosCanBeStartedByUsersManagedBy.java │ │ │ │ │ │ │ ├── SearchProcessDeploymentInfosStartedBy.java │ │ │ │ │ │ │ ├── SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasks.java │ │ │ │ │ │ │ ├── SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor.java │ │ │ │ │ │ │ ├── SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy.java │ │ │ │ │ │ │ ├── SearchProcessInstances.java │ │ │ │ │ │ │ ├── SearchUncategorizedProcessDeploymentInfos.java │ │ │ │ │ │ │ ├── SearchUncategorizedProcessDeploymentInfosCanBeStartedBy.java │ │ │ │ │ │ │ └── SearchUncategorizedProcessDeploymentInfosSupervisedBy.java │ │ │ │ │ │ ├── profile/ │ │ │ │ │ │ │ ├── SearchProfileMembersForProfile.java │ │ │ │ │ │ │ └── SearchProfiles.java │ │ │ │ │ │ ├── supervisor/ │ │ │ │ │ │ │ ├── SearchArchivedActivityInstanceSupervisedBy.java │ │ │ │ │ │ │ ├── SearchArchivedFlowNodeInstanceSupervisedBy.java │ │ │ │ │ │ │ ├── SearchArchivedHumanTasksSupervisedBy.java │ │ │ │ │ │ │ ├── SearchFlowNodeInstanceSupervisedBy.java │ │ │ │ │ │ │ ├── SearchProcessDeploymentInfosSupervised.java │ │ │ │ │ │ │ └── SearchSupervisors.java │ │ │ │ │ │ └── task/ │ │ │ │ │ │ ├── SearchArchivedTasks.java │ │ │ │ │ │ └── SearchArchivedTasksManagedBy.java │ │ │ │ │ ├── service/ │ │ │ │ │ │ ├── APIAccessResolver.java │ │ │ │ │ │ ├── FormRequiredAnalyzer.java │ │ │ │ │ │ ├── InstallationFailedException.java │ │ │ │ │ │ ├── InstallationService.java │ │ │ │ │ │ ├── ModelConvertor.java │ │ │ │ │ │ ├── ProcessEngineServicesResolver.java │ │ │ │ │ │ ├── ServiceAccessor.java │ │ │ │ │ │ ├── ServiceAccessorSingleton.java │ │ │ │ │ │ ├── impl/ │ │ │ │ │ │ │ ├── APIAccessResolverImpl.java │ │ │ │ │ │ │ ├── BonitaSpringContext.java │ │ │ │ │ │ │ ├── CustomPropertySource.java │ │ │ │ │ │ │ ├── MapToPropertiesFactoryBean.java │ │ │ │ │ │ │ ├── PlatformAuthenticationChecker.java │ │ │ │ │ │ │ ├── ServerLoggerWrapper.java │ │ │ │ │ │ │ ├── ServiceAccessorFactory.java │ │ │ │ │ │ │ ├── ServiceAccessors.java │ │ │ │ │ │ │ ├── SpringBeanAccessor.java │ │ │ │ │ │ │ ├── SpringServiceAccessor.java │ │ │ │ │ │ │ ├── SpringServiceAccessors.java │ │ │ │ │ │ │ └── installation/ │ │ │ │ │ │ │ ├── ConfigurationArchive.java │ │ │ │ │ │ │ ├── InstallationServiceImpl.java │ │ │ │ │ │ │ └── ProcessConfiguration.java │ │ │ │ │ │ └── platform/ │ │ │ │ │ │ ├── PlatformInformationService.java │ │ │ │ │ │ └── PlatformInformationServiceImpl.java │ │ │ │ │ ├── tenant/ │ │ │ │ │ │ ├── ChangesServicesStateCallable.java │ │ │ │ │ │ ├── SingleNodeTaskCoordinator.java │ │ │ │ │ │ ├── SingleNodeTaskCoordinatorLocal.java │ │ │ │ │ │ ├── TenantElementsRestartSupervisor.java │ │ │ │ │ │ ├── TenantElementsRestartSupervisorLocal.java │ │ │ │ │ │ ├── TenantElementsRestarter.java │ │ │ │ │ │ ├── TenantRestarter.java │ │ │ │ │ │ ├── TenantServicesManager.java │ │ │ │ │ │ ├── TenantStateManager.java │ │ │ │ │ │ └── restart/ │ │ │ │ │ │ ├── ElementToRecover.java │ │ │ │ │ │ ├── FlowNodesRecover.java │ │ │ │ │ │ ├── MessagesRestartHandler.java │ │ │ │ │ │ ├── ProcessesRecover.java │ │ │ │ │ │ ├── RecoveryHandler.java │ │ │ │ │ │ ├── RecoveryMonitor.java │ │ │ │ │ │ ├── RecoveryScheduler.java │ │ │ │ │ │ ├── RecoveryService.java │ │ │ │ │ │ └── TenantRestartHandler.java │ │ │ │ │ └── userfilter/ │ │ │ │ │ └── UserFilterServiceDecorator.java │ │ │ │ └── resources/ │ │ │ │ ├── actorMapping.xsd │ │ │ │ ├── bonita-community.xml │ │ │ │ ├── bonita-platform-community.properties │ │ │ │ ├── bonita-platform-private-community.properties │ │ │ │ ├── bonita-tenant-community.properties │ │ │ │ └── profiles.xml │ │ │ └── test/ │ │ │ ├── groovy/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ ├── engine/ │ │ │ │ │ └── execution/ │ │ │ │ │ └── transition/ │ │ │ │ │ └── FlowNodeStateSequencesTest.java │ │ │ │ └── permissions/ │ │ │ │ ├── ActorMemberPermissionRuleTest.groovy │ │ │ │ ├── ActorPermissionRuleTest.groovy │ │ │ │ ├── ApplicationMenuPermissionRuleTest.groovy │ │ │ │ ├── ApplicationPermissionRuleTest.groovy │ │ │ │ ├── CaseContextPermissionRuleTest.groovy │ │ │ │ ├── CasePermissionRuleTest.groovy │ │ │ │ ├── CaseVariablePermissionRuleTest.groovy │ │ │ │ ├── CommentPermissionRuleTest.groovy │ │ │ │ ├── ConnectorInstancePermissionRuleTest.groovy │ │ │ │ ├── DocumentPermissionRuleTest.groovy │ │ │ │ ├── DownloadDocumentPermissionRuleTest.groovy │ │ │ │ ├── ProcessConfigurationPermissionRuleTest.groovy │ │ │ │ ├── ProcessConnectorDependencyPermissionRuleTest.groovy │ │ │ │ ├── ProcessInstantiationPermissionRuleTest.groovy │ │ │ │ ├── ProcessPermissionRuleTest.groovy │ │ │ │ ├── ProcessResolutionProblemPermissionRuleTest.groovy │ │ │ │ ├── ProcessSupervisorPermissionRuleTest.groovy │ │ │ │ ├── ProfilePermissionRuleTest.groovy │ │ │ │ ├── TaskExecutionPermissionRuleTest.groovy │ │ │ │ ├── TaskPermissionRuleTest.groovy │ │ │ │ └── UserPermissionRuleTest.groovy │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ ├── EngineInitializerTest.java │ │ │ │ ├── api/ │ │ │ │ │ └── impl/ │ │ │ │ │ ├── APIAccessorImplTest.java │ │ │ │ │ ├── BusinessDataAPIImplTest.java │ │ │ │ │ ├── CustomUserInfoAPIDelegateTest.java │ │ │ │ │ ├── CustomUserInfoDefinitionAPIDelegateTest.java │ │ │ │ │ ├── IdentityAPIImplTest.java │ │ │ │ │ ├── MaintenanceAPIImplTest.java │ │ │ │ │ ├── OrganizationAPIImplTest.java │ │ │ │ │ ├── PageAPIImplTest.java │ │ │ │ │ ├── PermissionAPIImplTest.java │ │ │ │ │ ├── PlatformAPIImplTest.java │ │ │ │ │ ├── ProcessAPIImplTest.java │ │ │ │ │ ├── ProcessDeploymentAPIDelegateTest.java │ │ │ │ │ ├── ProcessInvolvementDelegateTest.java │ │ │ │ │ ├── ProcessManagementAPIImplDelegateTest.java │ │ │ │ │ ├── ProfileAPIImplTest.java │ │ │ │ │ ├── SCustomUserInfoValueAPITest.java │ │ │ │ │ ├── ServerAPIFactoryTest.java │ │ │ │ │ ├── ServerAPIImplTest.java │ │ │ │ │ ├── StarterThreadTest.java │ │ │ │ │ ├── TaskInvolvementDelegateTest.java │ │ │ │ │ ├── TenantAdministrationAPIImplTest.java │ │ │ │ │ ├── TestImplemOfServerAPI.java │ │ │ │ │ ├── application/ │ │ │ │ │ │ └── installer/ │ │ │ │ │ │ ├── ApplicationArchiveReaderTest.java │ │ │ │ │ │ ├── ApplicationArchiveTest.java │ │ │ │ │ │ ├── ApplicationInstallerTest.java │ │ │ │ │ │ ├── ApplicationInstallerUpdateTest.java │ │ │ │ │ │ ├── CustomOrDefaultApplicationInstallerConfigTest.java │ │ │ │ │ │ ├── CustomOrDefaultApplicationInstallerTest.java │ │ │ │ │ │ └── detector/ │ │ │ │ │ │ ├── ArtifactTypeDetectorTest.java │ │ │ │ │ │ ├── IconDetectorTest.java │ │ │ │ │ │ ├── LayoutDetectorTest.java │ │ │ │ │ │ ├── PageAndFormDetectorTest.java │ │ │ │ │ │ └── ThemeDetectorTest.java │ │ │ │ │ ├── connector/ │ │ │ │ │ │ ├── ConnectorReseterTest.java │ │ │ │ │ │ └── ResetAllFailedConnectorStrategyTest.java │ │ │ │ │ ├── converter/ │ │ │ │ │ │ ├── ApplicationMenuModelConverterTest.java │ │ │ │ │ │ ├── ApplicationModelConverterTest.java │ │ │ │ │ │ ├── ApplicationPageModelConverterTest.java │ │ │ │ │ │ ├── PageAssert.java │ │ │ │ │ │ ├── PageModelConverterTest.java │ │ │ │ │ │ └── SPageAssert.java │ │ │ │ │ ├── flownode/ │ │ │ │ │ │ └── FlowNodeRetrierTest.java │ │ │ │ │ ├── livingapplication/ │ │ │ │ │ │ ├── LivingApplicationAPIDelegateTest.java │ │ │ │ │ │ ├── LivingApplicationExporterDelegateTest.java │ │ │ │ │ │ ├── LivingApplicationMenuAPIDelegateTest.java │ │ │ │ │ │ └── LivingApplicationPageAPIDelegateTest.java │ │ │ │ │ ├── page/ │ │ │ │ │ │ └── PageAPIDelegateTest.java │ │ │ │ │ ├── platform/ │ │ │ │ │ │ └── PlatformInformationAPIImplTest.java │ │ │ │ │ ├── profile/ │ │ │ │ │ │ └── ProfileAPIIDelegateTest.java │ │ │ │ │ ├── resolver/ │ │ │ │ │ │ ├── BusinessDataBusinessArchiveArtifactManagerTest.java │ │ │ │ │ │ ├── DocumentInitialValueArtifactManagerTest.java │ │ │ │ │ │ ├── FormMappingAndPageArtifactManagerTest.java │ │ │ │ │ │ ├── ParameterBusinessArchiveArtifactManagerTest.java │ │ │ │ │ │ ├── ProblemAssert.java │ │ │ │ │ │ └── ProblemCondition.java │ │ │ │ │ └── transaction/ │ │ │ │ │ ├── GetNumberOfActorsTest.java │ │ │ │ │ ├── actor/ │ │ │ │ │ │ ├── ActorMappingMarshallerTest.java │ │ │ │ │ │ └── ImportActorMappingTest.java │ │ │ │ │ ├── application/ │ │ │ │ │ │ ├── SearchApplicationMenusTest.java │ │ │ │ │ │ ├── SearchApplicationPagesTest.java │ │ │ │ │ │ ├── SearchApplicationsOfUsersTest.java │ │ │ │ │ │ └── SearchApplicationsTest.java │ │ │ │ │ ├── expression/ │ │ │ │ │ │ ├── EvaluateExpressionsDefinitionLevelTest.java │ │ │ │ │ │ └── bdm/ │ │ │ │ │ │ └── internal/ │ │ │ │ │ │ └── EntityMergerTest.java │ │ │ │ │ ├── flownode/ │ │ │ │ │ │ └── SetExpectedEndDateTest.java │ │ │ │ │ ├── process/ │ │ │ │ │ │ └── DisableProcessTest.java │ │ │ │ │ └── profile/ │ │ │ │ │ └── CreateProfileMemberTest.java │ │ │ │ ├── authorization/ │ │ │ │ │ ├── PermissionServiceConfigurationTest.java │ │ │ │ │ └── PermissionServiceImplTest.java │ │ │ │ ├── bpm/ │ │ │ │ │ ├── contract/ │ │ │ │ │ │ └── validation/ │ │ │ │ │ │ ├── ConstraintsDefinitionHelperTest.java │ │ │ │ │ │ ├── ContractConstraintsValidatorTest.java │ │ │ │ │ │ ├── ContractStructureValidatorTest.java │ │ │ │ │ │ ├── ContractTypeValidatorTest.java │ │ │ │ │ │ ├── ContractValidatorTest.java │ │ │ │ │ │ ├── ContractVariableHelperTest.java │ │ │ │ │ │ └── builder/ │ │ │ │ │ │ ├── MapBuilder.java │ │ │ │ │ │ ├── SComplexInputDefinitionBuilder.java │ │ │ │ │ │ ├── SConstraintDefinitionBuilder.java │ │ │ │ │ │ ├── SContractDefinitionBuilder.java │ │ │ │ │ │ └── SSimpleInputDefinitionBuilder.java │ │ │ │ │ └── model/ │ │ │ │ │ └── impl/ │ │ │ │ │ └── BPMInstancesCreatorTest.java │ │ │ │ ├── business/ │ │ │ │ │ ├── application/ │ │ │ │ │ │ ├── converter/ │ │ │ │ │ │ │ ├── ApplicationMenuToNodeConverterTest.java │ │ │ │ │ │ │ ├── ApplicationPageToNodeConverterTest.java │ │ │ │ │ │ │ ├── ApplicationToNodeConverterTest.java │ │ │ │ │ │ │ ├── NodeToApplicationConverterTest.java │ │ │ │ │ │ │ ├── NodeToApplicationPageConverterTest.java │ │ │ │ │ │ │ └── NodetoApplicationMenuConverterTest.java │ │ │ │ │ │ ├── exporter/ │ │ │ │ │ │ │ ├── ApplicationContainerExporterTest.java │ │ │ │ │ │ │ └── ApplicationExporterTest.java │ │ │ │ │ │ └── importer/ │ │ │ │ │ │ ├── ApplicationImporterTest.java │ │ │ │ │ │ ├── ApplicationMenuImporterTest.java │ │ │ │ │ │ ├── ApplicationPageImporterTest.java │ │ │ │ │ │ ├── DefaultLivingApplicationImporterTest.java │ │ │ │ │ │ ├── FailOnDuplicateApplicationImportStrategyTest.java │ │ │ │ │ │ ├── ImportResultTest.java │ │ │ │ │ │ ├── MandatoryLivingApplicationImporterTest.java │ │ │ │ │ │ ├── ReplaceDuplicateApplicationImportStrategyTest.java │ │ │ │ │ │ ├── StrategySelectorTest.java │ │ │ │ │ │ ├── UpdateNewerNonEditableApplicationStrategyTest.java │ │ │ │ │ │ └── validator/ │ │ │ │ │ │ ├── ApplicationImportValidatorTest.java │ │ │ │ │ │ ├── ApplicationMenuCreatorValidatorTest.java │ │ │ │ │ │ ├── ApplicationTokenValidatorTest.java │ │ │ │ │ │ ├── ValidationStatusAssert.java │ │ │ │ │ │ └── ValidationStatusTest.java │ │ │ │ │ └── data/ │ │ │ │ │ ├── BusinessDataRetrieverTest.java │ │ │ │ │ ├── DummyBusinessDataRefBuilder.java │ │ │ │ │ ├── RefBusinessDataRetrieverTest.java │ │ │ │ │ └── converter/ │ │ │ │ │ └── BusinessDataModelConverterTest.java │ │ │ │ ├── command/ │ │ │ │ │ ├── AdvancedStartProcessCommandTest.java │ │ │ │ │ ├── ExecuteBDMQueryCommandTest.java │ │ │ │ │ ├── GetBusinessDataByIdCommandTest.java │ │ │ │ │ ├── GetBusinessDataByIdsCommandTest.java │ │ │ │ │ └── GetBusinessDataByQueryCommandTest.java │ │ │ │ ├── configuration/ │ │ │ │ │ └── BonitaPropertyAnnotationProcessorTest.java │ │ │ │ ├── connector/ │ │ │ │ │ └── ConnectorServiceDecoratorTest.java │ │ │ │ ├── core/ │ │ │ │ │ ├── document/ │ │ │ │ │ │ └── api/ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ └── DocumentHelperTest.java │ │ │ │ │ └── form/ │ │ │ │ │ ├── ExternalURLAdapterTest.java │ │ │ │ │ └── LegacyURLAdapterTest.java │ │ │ │ ├── data/ │ │ │ │ │ └── ParentContainerResolverImplTest.java │ │ │ │ ├── execution/ │ │ │ │ │ ├── AdvancedStartProcessValidatorTest.java │ │ │ │ │ ├── CountAnswer.java │ │ │ │ │ ├── FlowNodeExecutorImplTest.java │ │ │ │ │ ├── FlowNodeIdFilterTest.java │ │ │ │ │ ├── FlowNodeNameFilterTest.java │ │ │ │ │ ├── FlowNodeSelectorTest.java │ │ │ │ │ ├── FlowNodeStateManagerImplTest.java │ │ │ │ │ ├── ProcessExecutorImplTest.java │ │ │ │ │ ├── ProcessInstanceInterruptorTest.java │ │ │ │ │ ├── ProcessStarterVerifierImplTest.java │ │ │ │ │ ├── StartFlowNodeFilterTest.java │ │ │ │ │ ├── StateBehaviorsTest.java │ │ │ │ │ ├── TestFlowNodeState.java │ │ │ │ │ ├── TransactionServiceMock.java │ │ │ │ │ ├── TransitionEvaluatorTest.java │ │ │ │ │ ├── WaitingEventsInterrupterTest.java │ │ │ │ │ ├── archive/ │ │ │ │ │ │ └── BPMArchiverServiceTest.java │ │ │ │ │ ├── event/ │ │ │ │ │ │ ├── ErrorEventHandlerStrategyTest.java │ │ │ │ │ │ └── TimerEventHandlerStrategyTest.java │ │ │ │ │ ├── flowmerger/ │ │ │ │ │ │ └── FlowNodeTransitionsWrapperTest.java │ │ │ │ │ ├── state/ │ │ │ │ │ │ ├── CancellingCallActivityStateTest.java │ │ │ │ │ │ ├── InitializingActivityStateTest.java │ │ │ │ │ │ ├── InitializingActivityWithBoundaryEventsStateTest.java │ │ │ │ │ │ ├── InitializingAndExecutingFlowNodeStateTest.java │ │ │ │ │ │ └── InitializingMultiInstanceActivityStateTest.java │ │ │ │ │ ├── transition/ │ │ │ │ │ │ ├── AbstractTransitionEvaluatorTest.java │ │ │ │ │ │ ├── DefaultTransitionGetterTest.java │ │ │ │ │ │ ├── ExclusiveGatewayTransitionEvaluationStrategyTest.java │ │ │ │ │ │ ├── ImplicitGatewayTransitionEvaluatorTest.java │ │ │ │ │ │ ├── InclusiveExclusiveTransitionEvaluatorTest.java │ │ │ │ │ │ ├── InclusiveGatewayTransitionEvaluationStrategyTest.java │ │ │ │ │ │ ├── ParallelGatewayTransitionEvaluatorTest.java │ │ │ │ │ │ └── TransitionConditionEvaluatorTest.java │ │ │ │ │ └── work/ │ │ │ │ │ ├── BPMWorkFactoryTest.java │ │ │ │ │ ├── ExecuteConnectorOfActivityTest.java │ │ │ │ │ ├── ExecuteConnectorWorkTest.java │ │ │ │ │ ├── ExecuteFlowNodeWorkTest.java │ │ │ │ │ ├── ExecuteMessageCoupleWorkTest.java │ │ │ │ │ ├── FailedStateSetterTest.java │ │ │ │ │ ├── InSessionBonitaWorkTest.java │ │ │ │ │ ├── LockProcessInstanceWorkTest.java │ │ │ │ │ ├── NotifyChildFinishedWorkTest.java │ │ │ │ │ ├── SetInFailCallableTest.java │ │ │ │ │ ├── TenantRestarterTest.java │ │ │ │ │ ├── TransactionServiceForTest.java │ │ │ │ │ ├── TxBonitaWorkTest.java │ │ │ │ │ └── failurewrapping/ │ │ │ │ │ ├── AbstractContextWorkTest.java │ │ │ │ │ ├── ConnectorDefinitionAndInstanceContextWorkTest.java │ │ │ │ │ ├── FlowNodeDefinitionAndInstanceContextWorkTest.java │ │ │ │ │ ├── MessageInstanceContextWorkTest.java │ │ │ │ │ ├── ProcessDefinitionContextWorkTest.java │ │ │ │ │ ├── ProcessInstanceContextWorkTest.java │ │ │ │ │ └── TriggerSignalWorkTest.java │ │ │ │ ├── expression/ │ │ │ │ │ ├── BusinessDataExpressionExecutorStrategyTest.java │ │ │ │ │ ├── BusinessDataReferenceExpressionExecutorStrategyTest.java │ │ │ │ │ ├── BusinessObjectDAOExpressionStrategyTest.java │ │ │ │ │ ├── ContractInputExpressionExecutorStrategyTest.java │ │ │ │ │ ├── DataExpressionExecutorStrategyTest.java │ │ │ │ │ ├── DocumentListReferenceExpressionExecutorStrategyTest.java │ │ │ │ │ ├── DocumentReferenceExpressionExecutorStrategyTest.java │ │ │ │ │ ├── DummyServerDAO.java │ │ │ │ │ ├── EngineConstantExpressionExecutorStrategyTest.java │ │ │ │ │ ├── ParameterExpressionExecutorStrategyTest.java │ │ │ │ │ ├── QueryBusinessDataExpressionExecutorStrategyTest.java │ │ │ │ │ ├── SimpleBizData.java │ │ │ │ │ └── SimpleBizDataAssert.java │ │ │ │ ├── handler/ │ │ │ │ │ └── SchedulerServiceRestartHandlerTest.java │ │ │ │ ├── identity/ │ │ │ │ │ ├── CustomUserInfoDefinitionImporterTest.java │ │ │ │ │ ├── CustomUserInfoValueImporterTest.java │ │ │ │ │ ├── ExportOrganizationTest.java │ │ │ │ │ ├── ImportOrganizationFailOnDuplicatesStrategyTest.java │ │ │ │ │ ├── ImportOrganizationMergeDuplicatesStrategyTest.java │ │ │ │ │ └── UserImporterTest.java │ │ │ │ ├── jobs/ │ │ │ │ │ └── TriggerTimerEventJobTest.java │ │ │ │ ├── message/ │ │ │ │ │ └── MessagesHandlingServiceTest.java │ │ │ │ ├── operation/ │ │ │ │ │ ├── Address.java │ │ │ │ │ ├── BusinessDataAssignmentStrategyTest.java │ │ │ │ │ ├── BusinessDataContextTest.java │ │ │ │ │ ├── BusinessDataJavaMethodOperationExecutorStrategyTest.java │ │ │ │ │ ├── BusinessDataLeftOperandHandlerTest.java │ │ │ │ │ ├── DataLeftOperandHandlerTest.java │ │ │ │ │ ├── DocumentLeftOperandHandlerTest.java │ │ │ │ │ ├── DocumentListLeftOperandHandlerTest.java │ │ │ │ │ ├── EntitiesActionsExecutorTest.java │ │ │ │ │ ├── MergeEntityActionTest.java │ │ │ │ │ ├── StringIndexLeftOperandHandlerTest.java │ │ │ │ │ ├── TransientDataLeftOperandHandlerTest.java │ │ │ │ │ ├── UpdateDataRefActionTest.java │ │ │ │ │ └── pojo/ │ │ │ │ │ ├── Employee.java │ │ │ │ │ ├── InvalidTravel.java │ │ │ │ │ └── Travel.java │ │ │ │ ├── page/ │ │ │ │ │ ├── IsActorInitiatorRuleTest.java │ │ │ │ │ ├── IsAdminRuleTest.java │ │ │ │ │ ├── IsInvolvedInProcessInstanceRuleTest.java │ │ │ │ │ ├── IsManagerOfUserInvolvedInProcessInstanceRuleTest.java │ │ │ │ │ ├── IsProcessInitiatorRuleTest.java │ │ │ │ │ ├── IsProcessOwnerRuleTest.java │ │ │ │ │ ├── IsTaskAvailableForUserRuleTest.java │ │ │ │ │ ├── IsTaskPerformerRuleTest.java │ │ │ │ │ └── RuleTest.java │ │ │ │ ├── platform/ │ │ │ │ │ ├── PlatformManagerTest.java │ │ │ │ │ ├── PlatformStateProviderTest.java │ │ │ │ │ └── PlatformVersionCheckerTest.java │ │ │ │ ├── profile/ │ │ │ │ │ ├── DefaultProfilesUpdaterTest.java │ │ │ │ │ ├── ProfileImportStrategyTest.java │ │ │ │ │ ├── ProfilesExporterTest.java │ │ │ │ │ ├── ProfilesImporterTest.java │ │ │ │ │ ├── UpdateDefaultsAndCreateNewImportStrategyTest.java │ │ │ │ │ └── UpdateDefaultsImportStrategyTest.java │ │ │ │ ├── search/ │ │ │ │ │ ├── AbstractActivityInstanceSearchEntityTest.java │ │ │ │ │ ├── AbstractArchiveActivityInstanceSearchEntityTest.java │ │ │ │ │ ├── AbstractSearchEntityTest.java │ │ │ │ │ ├── activity/ │ │ │ │ │ │ └── SearchActivityInstancesTest.java │ │ │ │ │ ├── descriptor/ │ │ │ │ │ │ ├── SearchArchivedHumanTaskInstanceDescriptorTest.java │ │ │ │ │ │ ├── SearchHumanTaskInstanceDescriptorTest.java │ │ │ │ │ │ ├── SearchPageDescriptorTest.java │ │ │ │ │ │ └── SearchProcessInstanceDescriptorTest.java │ │ │ │ │ └── identity/ │ │ │ │ │ └── SearchCustomUserInfoValuesTest.java │ │ │ │ ├── service/ │ │ │ │ │ ├── FormRequiredAnalyzerTest.java │ │ │ │ │ ├── ModelConvertorTest.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ ├── BonitaSpringContextTest.java │ │ │ │ │ │ ├── ServerLoggerWrapperTest.java │ │ │ │ │ │ ├── SpringBeanAccessorTest.java │ │ │ │ │ │ └── installation/ │ │ │ │ │ │ ├── ConfigurationArchiveTest.java │ │ │ │ │ │ └── InstallationServiceImplTest.java │ │ │ │ │ └── platform/ │ │ │ │ │ └── PlatformInformationServiceImplTest.java │ │ │ │ └── tenant/ │ │ │ │ ├── SingleNodeTaskCoordinatorLocalTest.java │ │ │ │ ├── TenantElementsRestartSupervisorLocalTest.java │ │ │ │ ├── TenantElementsRestarterTest.java │ │ │ │ ├── TenantServicesManagerTest.java │ │ │ │ ├── TenantStateManagerTest.java │ │ │ │ └── restart/ │ │ │ │ ├── FlowNodesRecoverTest.java │ │ │ │ ├── MessagesRestartHandlerTest.java │ │ │ │ ├── ProcessesRecoverTest.java │ │ │ │ ├── RecoveryHandlerTest.java │ │ │ │ ├── RecoverySchedulerTest.java │ │ │ │ └── RecoveryServiceTest.java │ │ │ └── resources/ │ │ │ ├── AllProfiles.xml │ │ │ ├── RequestLoan_conf_with_null_params.bconf │ │ │ ├── classpathresource1 │ │ │ ├── classpathresource2 │ │ │ ├── complexActorMapping.xml │ │ │ ├── icon.docx │ │ │ ├── logback-test.xml │ │ │ ├── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── api/ │ │ │ │ └── impl/ │ │ │ │ └── EmptyDocument--1.0.bar │ │ │ ├── test.bconf │ │ │ └── testThatShouldFail.xml │ │ ├── bonita-process-instance/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── bonitasoft/ │ │ │ │ │ └── engine/ │ │ │ │ │ └── core/ │ │ │ │ │ ├── connector/ │ │ │ │ │ │ ├── ConnectorInstanceService.java │ │ │ │ │ │ ├── ConnectorResult.java │ │ │ │ │ │ ├── ConnectorService.java │ │ │ │ │ │ ├── exception/ │ │ │ │ │ │ │ ├── SConnectorDefinitionNotFoundException.java │ │ │ │ │ │ │ ├── SConnectorException.java │ │ │ │ │ │ │ ├── SConnectorInstanceCreationException.java │ │ │ │ │ │ │ ├── SConnectorInstanceDeletionException.java │ │ │ │ │ │ │ ├── SConnectorInstanceModificationException.java │ │ │ │ │ │ │ ├── SConnectorInstanceNotFoundException.java │ │ │ │ │ │ │ ├── SConnectorInstanceReadException.java │ │ │ │ │ │ │ ├── SConnectorValidationException.java │ │ │ │ │ │ │ └── SInvalidConnectorImplementationException.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── ConnectorArchive.java │ │ │ │ │ │ ├── ConnectorExecutionTimeLogger.java │ │ │ │ │ │ ├── ConnectorInstanceServiceImpl.java │ │ │ │ │ │ ├── ConnectorServiceImpl.java │ │ │ │ │ │ └── SConnectorAdapter.java │ │ │ │ │ ├── delegation/ │ │ │ │ │ │ └── model/ │ │ │ │ │ │ ├── SDelegationRule.java │ │ │ │ │ │ └── SDelegationRuleProcess.java │ │ │ │ │ ├── document/ │ │ │ │ │ │ ├── DocumentCriterion.java │ │ │ │ │ │ ├── DocumentField.java │ │ │ │ │ │ ├── DocumentQueryBuilder.java │ │ │ │ │ │ ├── api/ │ │ │ │ │ │ │ ├── DocumentService.java │ │ │ │ │ │ │ ├── SDocumentMappingCriterion.java │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ ├── DocumentServiceImpl.java │ │ │ │ │ │ │ ├── SDocumentDownloadURLProvider.java │ │ │ │ │ │ │ └── SDocumentDownloadURLProviderImpl.java │ │ │ │ │ │ └── model/ │ │ │ │ │ │ ├── AbstractSDocument.java │ │ │ │ │ │ ├── AbstractSDocumentMapping.java │ │ │ │ │ │ ├── AbstractSMappedDocument.java │ │ │ │ │ │ ├── SDocument.java │ │ │ │ │ │ ├── SDocumentMapping.java │ │ │ │ │ │ ├── SLightDocument.java │ │ │ │ │ │ ├── SMappedDocument.java │ │ │ │ │ │ ├── archive/ │ │ │ │ │ │ │ ├── SADocumentMapping.java │ │ │ │ │ │ │ └── SAMappedDocument.java │ │ │ │ │ │ ├── builder/ │ │ │ │ │ │ │ ├── SDocumentBuilder.java │ │ │ │ │ │ │ ├── SDocumentBuilderFactory.java │ │ │ │ │ │ │ ├── SDocumentMappingBuilder.java │ │ │ │ │ │ │ ├── SDocumentMappingBuilderFactory.java │ │ │ │ │ │ │ ├── SDocumentUpdateBuilder.java │ │ │ │ │ │ │ └── SDocumentUpdateBuilderFactory.java │ │ │ │ │ │ └── recorder/ │ │ │ │ │ │ └── SelectDescriptorBuilder.java │ │ │ │ │ └── process/ │ │ │ │ │ └── instance/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── ActivityInstanceService.java │ │ │ │ │ │ ├── BPMFailureService.java │ │ │ │ │ │ ├── FlowNodeInstanceService.java │ │ │ │ │ │ ├── GatewayInstanceService.java │ │ │ │ │ │ ├── ProcessInstanceService.java │ │ │ │ │ │ ├── RefBusinessDataService.java │ │ │ │ │ │ ├── event/ │ │ │ │ │ │ │ ├── EventInstanceRepository.java │ │ │ │ │ │ │ └── EventInstanceService.java │ │ │ │ │ │ ├── exceptions/ │ │ │ │ │ │ │ ├── SAProcessInstanceNotFoundException.java │ │ │ │ │ │ │ ├── SActivityCreationException.java │ │ │ │ │ │ │ ├── SActivityExecutionException.java │ │ │ │ │ │ │ ├── SActivityExecutionFailedException.java │ │ │ │ │ │ │ ├── SActivityInstanceNotFoundException.java │ │ │ │ │ │ │ ├── SActivityModificationException.java │ │ │ │ │ │ │ ├── SActivityReadException.java │ │ │ │ │ │ │ ├── SActivityStateExecutionException.java │ │ │ │ │ │ │ ├── SContractViolationException.java │ │ │ │ │ │ │ ├── SFlowElementModificationException.java │ │ │ │ │ │ │ ├── SFlowNodeDeletionException.java │ │ │ │ │ │ │ ├── SFlowNodeExecutionException.java │ │ │ │ │ │ │ ├── SFlowNodeModificationException.java │ │ │ │ │ │ │ ├── SFlowNodeNotFoundException.java │ │ │ │ │ │ │ ├── SFlowNodeReadException.java │ │ │ │ │ │ │ ├── SGatewayCreationException.java │ │ │ │ │ │ │ ├── SGatewayModificationException.java │ │ │ │ │ │ │ ├── SGatewayNotFoundException.java │ │ │ │ │ │ │ ├── SGatewayReadException.java │ │ │ │ │ │ │ ├── SProcessInstanceCreationException.java │ │ │ │ │ │ │ ├── SProcessInstanceDeletionException.java │ │ │ │ │ │ │ ├── SProcessInstanceHierarchicalDeletionException.java │ │ │ │ │ │ │ ├── SProcessInstanceModificationException.java │ │ │ │ │ │ │ ├── SProcessInstanceNotFoundException.java │ │ │ │ │ │ │ ├── SProcessInstanceReadException.java │ │ │ │ │ │ │ ├── STaskVisibilityException.java │ │ │ │ │ │ │ ├── SUnknowStateCategory.java │ │ │ │ │ │ │ ├── SuserTaskSkipException.java │ │ │ │ │ │ │ ├── business/ │ │ │ │ │ │ │ │ ├── SHumanTaskAlreadyAssignedException.java │ │ │ │ │ │ │ │ └── data/ │ │ │ │ │ │ │ │ ├── SRefBusinessDataInstanceCreationException.java │ │ │ │ │ │ │ │ ├── SRefBusinessDataInstanceModificationException.java │ │ │ │ │ │ │ │ └── SRefBusinessDataInstanceNotFoundException.java │ │ │ │ │ │ │ └── event/ │ │ │ │ │ │ │ ├── SEventInstanceCreationException.java │ │ │ │ │ │ │ ├── SEventInstanceException.java │ │ │ │ │ │ │ ├── SEventInstanceNotFoundException.java │ │ │ │ │ │ │ ├── SEventInstanceReadException.java │ │ │ │ │ │ │ └── trigger/ │ │ │ │ │ │ │ ├── SEventTriggerInstanceCreationException.java │ │ │ │ │ │ │ ├── SEventTriggerInstanceDeletionException.java │ │ │ │ │ │ │ ├── SEventTriggerInstanceException.java │ │ │ │ │ │ │ ├── SEventTriggerInstanceModificationException.java │ │ │ │ │ │ │ ├── SEventTriggerInstanceNotFoundException.java │ │ │ │ │ │ │ ├── SEventTriggerInstanceReadException.java │ │ │ │ │ │ │ ├── SMessageInstanceCreationException.java │ │ │ │ │ │ │ ├── SMessageInstanceNotFoundException.java │ │ │ │ │ │ │ ├── SMessageInstanceReadException.java │ │ │ │ │ │ │ ├── SMessageModificationException.java │ │ │ │ │ │ │ ├── SWaitingEventCreationException.java │ │ │ │ │ │ │ ├── SWaitingEventModificationException.java │ │ │ │ │ │ │ ├── SWaitingEventNotFoundException.java │ │ │ │ │ │ │ └── SWaitingEventReadException.java │ │ │ │ │ │ └── states/ │ │ │ │ │ │ ├── FlowNodeState.java │ │ │ │ │ │ └── StateCode.java │ │ │ │ │ ├── event/ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── EventInstanceRepositoryImpl.java │ │ │ │ │ │ └── EventInstanceServiceImpl.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ ├── ActivityInstanceServiceImpl.java │ │ │ │ │ │ ├── BPMFailureServiceImpl.java │ │ │ │ │ │ ├── FlowNodeInstancesServiceImpl.java │ │ │ │ │ │ ├── GatewayInstanceServiceImpl.java │ │ │ │ │ │ ├── ProcessInstanceServiceImpl.java │ │ │ │ │ │ └── RefBusinessDataServiceImpl.java │ │ │ │ │ ├── model/ │ │ │ │ │ │ ├── SABPMFailure.java │ │ │ │ │ │ ├── SAbstractConnectorInstance.java │ │ │ │ │ │ ├── SActivityInstance.java │ │ │ │ │ │ ├── SAutomaticTaskInstance.java │ │ │ │ │ │ ├── SBPMFailure.java │ │ │ │ │ │ ├── SCallActivityInstance.java │ │ │ │ │ │ ├── SConnectorInstance.java │ │ │ │ │ │ ├── SConnectorInstanceWithFailureInfo.java │ │ │ │ │ │ ├── SFlowElementsContainerType.java │ │ │ │ │ │ ├── SFlowNodeInstance.java │ │ │ │ │ │ ├── SFlowNodeInstanceStateCounter.java │ │ │ │ │ │ ├── SGatewayInstance.java │ │ │ │ │ │ ├── SHumanTaskInstance.java │ │ │ │ │ │ ├── SLoopActivityInstance.java │ │ │ │ │ │ ├── SManualTaskInstance.java │ │ │ │ │ │ ├── SMultiInstanceActivityInstance.java │ │ │ │ │ │ ├── SPendingActivityMapping.java │ │ │ │ │ │ ├── SProcessInstance.java │ │ │ │ │ │ ├── SReceiveTaskInstance.java │ │ │ │ │ │ ├── SSendTaskInstance.java │ │ │ │ │ │ ├── SStateCategory.java │ │ │ │ │ │ ├── SSubProcessActivityInstance.java │ │ │ │ │ │ ├── STaskPriority.java │ │ │ │ │ │ ├── SUserTaskInstance.java │ │ │ │ │ │ ├── archive/ │ │ │ │ │ │ │ ├── SAActivityInstance.java │ │ │ │ │ │ │ ├── SAAutomaticTaskInstance.java │ │ │ │ │ │ │ ├── SACallActivityInstance.java │ │ │ │ │ │ │ ├── SAConnectorInstance.java │ │ │ │ │ │ │ ├── SAFlowNodeInstance.java │ │ │ │ │ │ │ ├── SAGatewayInstance.java │ │ │ │ │ │ │ ├── SAHumanTaskInstance.java │ │ │ │ │ │ │ ├── SALoopActivityInstance.java │ │ │ │ │ │ │ ├── SAManualTaskInstance.java │ │ │ │ │ │ │ ├── SAMultiInstanceActivityInstance.java │ │ │ │ │ │ │ ├── SAProcessInstance.java │ │ │ │ │ │ │ ├── SAReceiveTaskInstance.java │ │ │ │ │ │ │ ├── SASendTaskInstance.java │ │ │ │ │ │ │ ├── SASubProcessActivityInstance.java │ │ │ │ │ │ │ ├── SAUserTaskInstance.java │ │ │ │ │ │ │ ├── builder/ │ │ │ │ │ │ │ │ ├── SAActivityInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SAActivityInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SAAutomaticTaskInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SAAutomaticTaskInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SACallActivityInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SACallActivityInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SAConnectorInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SAConnectorInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SAFlowElementInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SAFlowElementInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SAFlowNodeInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SAFlowNodeInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SAGatewayInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SAGatewayInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SALoopActivityInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SALoopActivityInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SAManualTaskInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SAManualTaskInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SAMultiInstanceActivityInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SAMultiInstanceActivityInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SANamedElementBuilderFactory.java │ │ │ │ │ │ │ │ ├── SAProcessInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SAProcessInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SAReceiveTaskInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SAReceiveTaskInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SASendTaskInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SASendTaskInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SASubProcessActivityInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SASubProcessActivityInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SAUserTaskInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SAUserTaskInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── business/ │ │ │ │ │ │ │ │ │ └── data/ │ │ │ │ │ │ │ │ │ ├── SARefBusinessDataInstanceBuilder.java │ │ │ │ │ │ │ │ │ └── SARefBusinessDataInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── event/ │ │ │ │ │ │ │ │ │ ├── SAEndEventInstanceBuilder.java │ │ │ │ │ │ │ │ │ ├── SAEndEventInstanceBuilderFactory.java │ │ │ │ │ │ │ │ │ ├── SAStartEventInstanceBuilder.java │ │ │ │ │ │ │ │ │ ├── SAStartEventInstanceBuilderFactory.java │ │ │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ │ │ ├── SAEndEventInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ │ ├── SAEndEventInstanceBuilderImpl.java │ │ │ │ │ │ │ │ │ ├── SAStartEventInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ │ └── SAStartEventInstanceBuilderImpl.java │ │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ │ ├── SAActivityInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SAActivityInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SAAutomaticTaskInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SAAutomaticTaskInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SACallActivityInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SACallActivityInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SAConnectorInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SAConnectorInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SAFlowElementInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SAFlowNodeInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SAFlowNodeInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SAGatewayInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SAGatewayInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SALoopActivityInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SALoopActivityInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SAManualTaskInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SAManualTaskInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SAMultiInstanceActivityInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SAMultiInstanceActivityInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SANamedElementBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SAProcessInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SAProcessInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SAReceiveTaskInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SAReceiveTaskInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SASendTaskInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SASendTaskInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SASubProcessActivityInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SASubProcessActivityInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SAUserTaskInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SAUserTaskInstanceBuilderImpl.java │ │ │ │ │ │ │ │ └── business/ │ │ │ │ │ │ │ │ └── data/ │ │ │ │ │ │ │ │ ├── SARefBusinessDataInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ └── SARefBusinessDataInstanceBuilderImpl.java │ │ │ │ │ │ │ ├── business/ │ │ │ │ │ │ │ │ └── data/ │ │ │ │ │ │ │ │ ├── SAFlowNodeSimpleRefBusinessDataInstance.java │ │ │ │ │ │ │ │ ├── SAProcessMultiRefBusinessDataInstance.java │ │ │ │ │ │ │ │ ├── SAProcessSimpleRefBusinessDataInstance.java │ │ │ │ │ │ │ │ ├── SARefBusinessDataInstance.java │ │ │ │ │ │ │ │ └── SASimpleRefBusinessDataInstance.java │ │ │ │ │ │ │ └── event/ │ │ │ │ │ │ │ ├── SABoundaryEventInstance.java │ │ │ │ │ │ │ ├── SACatchEventInstance.java │ │ │ │ │ │ │ ├── SAEndEventInstance.java │ │ │ │ │ │ │ ├── SAEventInstance.java │ │ │ │ │ │ │ ├── SAIntermediateCatchEventInstance.java │ │ │ │ │ │ │ ├── SAIntermediateThrowEventInstance.java │ │ │ │ │ │ │ ├── SAStartEventInstance.java │ │ │ │ │ │ │ └── SAThrowEventInstance.java │ │ │ │ │ │ ├── builder/ │ │ │ │ │ │ │ ├── SActivityInstanceBuilder.java │ │ │ │ │ │ │ ├── SActivityInstanceBuilderFactory.java │ │ │ │ │ │ │ ├── SAutomaticTaskInstanceBuilder.java │ │ │ │ │ │ │ ├── SAutomaticTaskInstanceBuilderFactory.java │ │ │ │ │ │ │ ├── SCallActivityInstanceBuilder.java │ │ │ │ │ │ │ ├── SCallActivityInstanceBuilderFactory.java │ │ │ │ │ │ │ ├── SFlowElementInstanceBuilderFactory.java │ │ │ │ │ │ │ ├── SFlowNodeInstanceBuilder.java │ │ │ │ │ │ │ ├── SFlowNodeInstanceBuilderFactory.java │ │ │ │ │ │ │ ├── SGatewayInstanceBuilder.java │ │ │ │ │ │ │ ├── SGatewayInstanceBuilderFactory.java │ │ │ │ │ │ │ ├── SHumanTaskInstanceBuilder.java │ │ │ │ │ │ │ ├── SHumanTaskInstanceBuilderFactory.java │ │ │ │ │ │ │ ├── SLoopActivityInstanceBuilder.java │ │ │ │ │ │ │ ├── SLoopActivityInstanceBuilderFactory.java │ │ │ │ │ │ │ ├── SManualTaskInstanceBuilder.java │ │ │ │ │ │ │ ├── SManualTaskInstanceBuilderFactory.java │ │ │ │ │ │ │ ├── SMultiInstanceActivityInstanceBuilder.java │ │ │ │ │ │ │ ├── SMultiInstanceActivityInstanceBuilderFactory.java │ │ │ │ │ │ │ ├── SReceiveTaskInstanceBuilder.java │ │ │ │ │ │ │ ├── SReceiveTaskInstanceBuilderFactory.java │ │ │ │ │ │ │ ├── SSendTaskInstanceBuilder.java │ │ │ │ │ │ │ ├── SSendTaskInstanceBuilderFactory.java │ │ │ │ │ │ │ ├── SSubProcessActivityInstanceBuilder.java │ │ │ │ │ │ │ ├── SSubProcessActivityInstanceBuilderFactory.java │ │ │ │ │ │ │ ├── SUserTaskInstanceBuilder.java │ │ │ │ │ │ │ ├── SUserTaskInstanceBuilderFactory.java │ │ │ │ │ │ │ ├── business/ │ │ │ │ │ │ │ │ └── data/ │ │ │ │ │ │ │ │ ├── SRefBusinessDataInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SRefBusinessDataInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SRefBusinessDataInstanceLogBuilder.java │ │ │ │ │ │ │ │ └── SRefBusinessDataInstanceLogBuilderFactory.java │ │ │ │ │ │ │ ├── event/ │ │ │ │ │ │ │ │ ├── SBoundaryEventInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SBoundaryEventInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SEndEventInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SEndEventInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SEventInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SEventInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SIntermediateCatchEventInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SIntermediateCatchEventInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SIntermediateThrowEventInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SIntermediateThrowEventInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── SStartEventInstanceBuilder.java │ │ │ │ │ │ │ │ ├── SStartEventInstanceBuilderFactory.java │ │ │ │ │ │ │ │ ├── handling/ │ │ │ │ │ │ │ │ │ ├── SCorrelationContainerBuilder.java │ │ │ │ │ │ │ │ │ ├── SMessageInstanceBuilder.java │ │ │ │ │ │ │ │ │ ├── SMessageInstanceLogBuilder.java │ │ │ │ │ │ │ │ │ ├── SMessageInstanceLogBuilderFactory.java │ │ │ │ │ │ │ │ │ ├── SWaitingErrorEventBuilder.java │ │ │ │ │ │ │ │ │ ├── SWaitingErrorEventBuilderFactory.java │ │ │ │ │ │ │ │ │ ├── SWaitingEventKeyProviderBuilderFactory.java │ │ │ │ │ │ │ │ │ ├── SWaitingMessageEventBuilder.java │ │ │ │ │ │ │ │ │ ├── SWaitingMessageEventBuilderFactory.java │ │ │ │ │ │ │ │ │ ├── SWaitingSignalEventBuilder.java │ │ │ │ │ │ │ │ │ ├── SWaitingSignalEventBuilderFactory.java │ │ │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ │ │ ├── SWaitingErrorEventBuilderFactoryImpl.java │ │ │ │ │ │ │ │ │ ├── SWaitingErrorEventBuilderImpl.java │ │ │ │ │ │ │ │ │ ├── SWaitingEventKeyProviderBuilderFactoryImpl.java │ │ │ │ │ │ │ │ │ ├── SWaitingMessageEventBuilderFactoryImpl.java │ │ │ │ │ │ │ │ │ ├── SWaitingMessageEventBuilderImpl.java │ │ │ │ │ │ │ │ │ ├── SWaitingSignalEventBuilderFactoryImpl.java │ │ │ │ │ │ │ │ │ └── SWaitingSignalEventBuilderImpl.java │ │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ │ ├── SBoundaryEventInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SBoundaryEventInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SEndEventInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SEndEventInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SEventInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SEventInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SIntermediateCatchEventInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SIntermediateCatchEventInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SIntermediateThrowEventInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ ├── SIntermediateThrowEventInstanceBuilderImpl.java │ │ │ │ │ │ │ │ ├── SStartEventInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ │ └── SStartEventInstanceBuilderImpl.java │ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ │ ├── SActivityInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SActivityInstanceBuilderImpl.java │ │ │ │ │ │ │ ├── SAutomaticTaskInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SAutomaticTaskInstanceBuilderImpl.java │ │ │ │ │ │ │ ├── SCallActivityInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SCallActivityInstanceBuilderImpl.java │ │ │ │ │ │ │ ├── SFlowElementInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SFlowNodeInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SFlowNodeInstanceBuilderImpl.java │ │ │ │ │ │ │ ├── SGatewayInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SGatewayInstanceBuilderImpl.java │ │ │ │ │ │ │ ├── SHumanTaskInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SHumanTaskInstanceBuilderImpl.java │ │ │ │ │ │ │ ├── SLoopActivityInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SLoopActivityInstanceBuilderImpl.java │ │ │ │ │ │ │ ├── SManualTaskInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SManualTaskInstanceBuilderImpl.java │ │ │ │ │ │ │ ├── SMultiInstanceActivityInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SMultiInstanceActivityInstanceBuilderImpl.java │ │ │ │ │ │ │ ├── SProcessInstanceLogIndexesMapper.java │ │ │ │ │ │ │ ├── SReceiveTaskInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SReceiveTaskInstanceBuilderImpl.java │ │ │ │ │ │ │ ├── SSendTaskInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SSendTaskInstanceBuilderImpl.java │ │ │ │ │ │ │ ├── SSubProcessActivityInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SSubProcessActivityInstanceBuilderImpl.java │ │ │ │ │ │ │ ├── SUserTaskInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SUserTaskInstanceBuilderImpl.java │ │ │ │ │ │ │ └── business/ │ │ │ │ │ │ │ └── data/ │ │ │ │ │ │ │ ├── SRefBusinessDataInstanceBuilderFactoryImpl.java │ │ │ │ │ │ │ ├── SRefBusinessDataInstanceBuilderImpl.java │ │ │ │ │ │ │ ├── SRefBusinessDataInstanceLogBuilderFactoryImpl.java │ │ │ │ │ │ │ └── SRefBusinessDataInstanceLogBuilderImpl.java │ │ │ │ │ │ ├── business/ │ │ │ │ │ │ │ └── data/ │ │ │ │ │ │ │ ├── SFlowNodeSimpleRefBusinessDataInstance.java │ │ │ │ │ │ │ ├── SProcessMultiRefBusinessDataInstance.java │ │ │ │ │ │ │ ├── SProcessSimpleRefBusinessDataInstance.java │ │ │ │ │ │ │ ├── SRefBusinessDataInstance.java │ │ │ │ │ │ │ └── SSimpleRefBusinessDataInstance.java │ │ │ │ │ │ └── event/ │ │ │ │ │ │ ├── SBoundaryEventInstance.java │ │ │ │ │ │ ├── SCatchEventInstance.java │ │ │ │ │ │ ├── SEndEventInstance.java │ │ │ │ │ │ ├── SEventInstance.java │ │ │ │ │ │ ├── SIntermediateCatchEventInstance.java │ │ │ │ │ │ ├── SIntermediateThrowEventInstance.java │ │ │ │ │ │ ├── SStartEventInstance.java │ │ │ │ │ │ ├── SThrowEventInstance.java │ │ │ │ │ │ ├── handling/ │ │ │ │ │ │ │ ├── SBPMEventType.java │ │ │ │ │ │ │ ├── SMessageEventCouple.java │ │ │ │ │ │ │ ├── SMessageInstance.java │ │ │ │ │ │ │ ├── SWaitingErrorEvent.java │ │ │ │ │ │ │ ├── SWaitingEvent.java │ │ │ │ │ │ │ ├── SWaitingMessageEvent.java │ │ │ │ │ │ │ └── SWaitingSignalEvent.java │ │ │ │ │ │ └── trigger/ │ │ │ │ │ │ └── STimerEventTriggerInstance.java │ │ │ │ │ └── recorder/ │ │ │ │ │ ├── ProcessInstanceRecordType.java │ │ │ │ │ ├── SelectBusinessDataDescriptorBuilder.java │ │ │ │ │ └── SelectDescriptorBuilder.java │ │ │ │ └── resources/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── core/ │ │ │ │ ├── document/ │ │ │ │ │ └── model/ │ │ │ │ │ └── impl/ │ │ │ │ │ └── hibernate/ │ │ │ │ │ ├── archive.document.queries.hbm.xml │ │ │ │ │ └── document.queries.hbm.xml │ │ │ │ ├── failure/ │ │ │ │ │ └── model/ │ │ │ │ │ └── impl/ │ │ │ │ │ └── hibernate/ │ │ │ │ │ └── failure.queries.hbm.xml │ │ │ │ └── process/ │ │ │ │ └── instance/ │ │ │ │ └── model/ │ │ │ │ └── impl/ │ │ │ │ └── hibernate/ │ │ │ │ ├── archived.process.instance.queries.hbm.xml │ │ │ │ └── process.instance.queries.hbm.xml │ │ │ └── test/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── core/ │ │ │ │ ├── connector/ │ │ │ │ │ ├── ConnectorResultTest.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── ConnectorExecutionTimeLoggerTest.java │ │ │ │ │ ├── ConnectorInstanceServiceImplTest.java │ │ │ │ │ └── ConnectorServiceImplTest.java │ │ │ │ ├── document/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── DocumentServiceImplTest.java │ │ │ │ │ │ └── SDocumentDownloadURLProviderImplTest.java │ │ │ │ │ └── model/ │ │ │ │ │ └── builder/ │ │ │ │ │ └── impl/ │ │ │ │ │ └── SDocumentBuilderFactoryImplTest.java │ │ │ │ └── process/ │ │ │ │ └── instance/ │ │ │ │ ├── api/ │ │ │ │ │ └── exceptions/ │ │ │ │ │ ├── SContractViolationExceptionTest.java │ │ │ │ │ └── SProcessInstanceReadExceptionTest.java │ │ │ │ ├── event/ │ │ │ │ │ └── impl/ │ │ │ │ │ ├── EventInstanceRepositoryImplForEventTest.java │ │ │ │ │ ├── EventInstanceRepositoryImplForEventTriggerTest.java │ │ │ │ │ ├── EventInstanceRepositoryImplForMessageTest.java │ │ │ │ │ ├── EventInstanceRepositoryImplForWaitingTest.java │ │ │ │ │ ├── EventInstanceServiceImplForWaitingTest.java │ │ │ │ │ └── EventInstanceServiceImplTest.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── ActivityInstanceServiceImplTest.java │ │ │ │ │ ├── BPMFailureServiceImplTest.java │ │ │ │ │ ├── FlowNodeInstancesServiceImplTest.java │ │ │ │ │ ├── GatewayInstanceServiceImplTest.java │ │ │ │ │ ├── ProcessInstanceServiceImplTest.java │ │ │ │ │ └── RefBusinessDataServiceImplTest.java │ │ │ │ └── model/ │ │ │ │ ├── archive/ │ │ │ │ │ └── impl/ │ │ │ │ │ └── SAHumanTaskInstanceImplTest.java │ │ │ │ ├── builder/ │ │ │ │ │ └── impl/ │ │ │ │ │ └── SFlowNodeInstanceBuilderImplTest.java │ │ │ │ ├── event/ │ │ │ │ │ ├── handling/ │ │ │ │ │ │ └── SMessageInstanceTest.java │ │ │ │ │ └── impl/ │ │ │ │ │ └── SBoundaryEventInstanceImplTest.java │ │ │ │ └── impl/ │ │ │ │ ├── SGatewayInstanceImplTest.java │ │ │ │ ├── SLoopActivityInstanceImplTest.java │ │ │ │ ├── SMultiInstanceActivityInstanceImplTest.java │ │ │ │ ├── SProcessInstanceImplTest.java │ │ │ │ └── SUserTaskInstanceImplTest.java │ │ │ └── resources/ │ │ │ └── logback-test.xml │ │ ├── bonita-supervisor-mapping/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── supervisor/ │ │ │ │ └── mapping/ │ │ │ │ ├── SProcessDefinitionNotFoundException.java │ │ │ │ ├── SSupervisorAlreadyExistsException.java │ │ │ │ ├── SSupervisorCreationException.java │ │ │ │ ├── SSupervisorDeletionException.java │ │ │ │ ├── SSupervisorException.java │ │ │ │ ├── SSupervisorNotFoundException.java │ │ │ │ ├── SupervisorMappingService.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── SelectDescriptorBuilder.java │ │ │ │ │ └── SupervisorMappingServiceImpl.java │ │ │ │ └── model/ │ │ │ │ ├── SMemberType.java │ │ │ │ ├── SProcessSupervisor.java │ │ │ │ ├── SProcessSupervisorLogBuilder.java │ │ │ │ ├── SProcessSupervisorLogBuilderFactory.java │ │ │ │ └── impl/ │ │ │ │ ├── SProcessSupervisorLogBuilderFactoryImpl.java │ │ │ │ ├── SProcessSupervisorLogBuilderImpl.java │ │ │ │ └── SProcessSupervisorLogIndexesMapper.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── supervisor/ │ │ │ └── mapping/ │ │ │ └── model/ │ │ │ └── impl/ │ │ │ └── hibernate/ │ │ │ └── supervisor.queries.hbm.xml │ │ └── bonita-user-filter/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── core/ │ │ │ └── filter/ │ │ │ ├── FilterResult.java │ │ │ ├── UserFilterService.java │ │ │ ├── exception/ │ │ │ │ ├── SUserFilterExecutionException.java │ │ │ │ └── SUserFilterLoadingException.java │ │ │ └── impl/ │ │ │ ├── FilterResultImpl.java │ │ │ ├── SConnectorUserFilterAdapter.java │ │ │ └── UserFilterServiceImpl.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── core/ │ │ └── filter/ │ │ └── impl/ │ │ ├── FilterResultImplTest.java │ │ ├── SConnectorUserFilterAdapterTest.java │ │ └── UserFilterServiceImplTest.java │ ├── bonita-external/ │ │ ├── build.gradle │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── external/ │ │ └── comment/ │ │ ├── SearchCommentsSupervisedBy.java │ │ └── transaction/ │ │ └── SearchCommentsSupervisedByTransaction.java │ ├── bonita-sap-jco-connector-api/ │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── com/ │ │ └── sap/ │ │ └── conn/ │ │ └── jco/ │ │ └── ext/ │ │ ├── DestinationDataEventListener.java │ │ ├── DestinationDataProvider.java │ │ └── Environment.java │ ├── bonita-server/ │ │ └── build.gradle │ ├── bonita-synchro-repository/ │ │ ├── bonita-synchro-register/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── synchro/ │ │ │ ├── AbstractUpdateHandler.java │ │ │ ├── AddHandlerCommand.java │ │ │ ├── EventUtil.java │ │ │ ├── FlowNodeHandler.java │ │ │ ├── GatewayHandler.java │ │ │ ├── PerfEventUtil.java │ │ │ ├── ProcessInstanceHandler.java │ │ │ ├── WaitForEventSynchronization.java │ │ │ └── WaitServerCommand.java │ │ ├── bonita-synchro-service/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── synchro/ │ │ │ └── SynchroService.java │ │ └── bonita-synchro-service-impl/ │ │ ├── build.gradle │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── synchro/ │ │ ├── AbstractSynchroService.java │ │ └── SynchroServiceImpl.java │ ├── bonita-web-extensions/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── web/ │ │ │ └── extension/ │ │ │ ├── ResourceProvider.java │ │ │ ├── page/ │ │ │ │ ├── PageContext.java │ │ │ │ ├── PageController.java │ │ │ │ └── PageResourceProvider.java │ │ │ └── rest/ │ │ │ ├── RestAPIContext.java │ │ │ ├── RestApiController.java │ │ │ ├── RestApiResponse.java │ │ │ └── RestApiResponseBuilder.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── web/ │ │ └── extension/ │ │ └── rest/ │ │ ├── RestApiResponseAssert.java │ │ └── RestApiResponseTest.java │ └── bonita-web-server/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ ├── console/ │ │ │ │ ├── common/ │ │ │ │ │ └── server/ │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── CommandCaller.java │ │ │ │ │ │ └── token/ │ │ │ │ │ │ └── APIToken.java │ │ │ │ │ ├── auth/ │ │ │ │ │ │ ├── AuthenticationFailedException.java │ │ │ │ │ │ ├── AuthenticationManager.java │ │ │ │ │ │ ├── AuthenticationManagerFactory.java │ │ │ │ │ │ ├── AuthenticationManagerNotFoundException.java │ │ │ │ │ │ ├── AuthenticationManagerProperties.java │ │ │ │ │ │ ├── ConsumerNotFoundException.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── jaas/ │ │ │ │ │ │ │ ├── ConsoleCallbackHandler.java │ │ │ │ │ │ │ ├── ConsoleIdentityLoginModule.java │ │ │ │ │ │ │ └── ConsolePrincipal.java │ │ │ │ │ │ └── standard/ │ │ │ │ │ │ └── StandardAuthenticationManagerImpl.java │ │ │ │ │ ├── filter/ │ │ │ │ │ │ ├── CacheFilter.java │ │ │ │ │ │ ├── ExcludingPatternFilter.java │ │ │ │ │ │ ├── MultiReadHttpServletRequest.java │ │ │ │ │ │ ├── NoCacheFilter.java │ │ │ │ │ │ ├── PathSanitizer.java │ │ │ │ │ │ ├── RedirectFilter.java │ │ │ │ │ │ ├── RequestIdFilter.java │ │ │ │ │ │ ├── SanitizerFilter.java │ │ │ │ │ │ └── URLExcludePattern.java │ │ │ │ │ ├── form/ │ │ │ │ │ │ ├── ProcessFormService.java │ │ │ │ │ │ └── ProcessFormServlet.java │ │ │ │ │ ├── i18n/ │ │ │ │ │ │ ├── FileUtils.java │ │ │ │ │ │ ├── I18n.java │ │ │ │ │ │ └── POParser.java │ │ │ │ │ ├── jsp/ │ │ │ │ │ │ ├── JSPI18n.java │ │ │ │ │ │ └── JSPUtils.java │ │ │ │ │ ├── login/ │ │ │ │ │ │ ├── AccountLockedException.java │ │ │ │ │ │ ├── HttpServletRequestAccessor.java │ │ │ │ │ │ ├── LoginFailedException.java │ │ │ │ │ │ ├── LoginManager.java │ │ │ │ │ │ ├── PortalCookies.java │ │ │ │ │ │ ├── credentials/ │ │ │ │ │ │ │ ├── Credentials.java │ │ │ │ │ │ │ ├── LoginDatastore.java │ │ │ │ │ │ │ ├── StandardCredentials.java │ │ │ │ │ │ │ └── UserLogger.java │ │ │ │ │ │ ├── filter/ │ │ │ │ │ │ │ ├── AccountLockedRuntimeException.java │ │ │ │ │ │ │ ├── AlreadyLoggedInRule.java │ │ │ │ │ │ │ ├── AuthenticationFilter.java │ │ │ │ │ │ │ ├── AuthenticationRule.java │ │ │ │ │ │ │ ├── ContentTypeSecurityFilter.java │ │ │ │ │ │ │ ├── EngineUserNotFoundOrInactive.java │ │ │ │ │ │ │ ├── FrameSecurityFilter.java │ │ │ │ │ │ │ ├── PlatformUnderMaintenanceException.java │ │ │ │ │ │ │ ├── RestAPIAuthorizationFilter.java │ │ │ │ │ │ │ ├── TokenGenerator.java │ │ │ │ │ │ │ ├── TokenGeneratorFilter.java │ │ │ │ │ │ │ └── TokenValidatorFilter.java │ │ │ │ │ │ ├── servlet/ │ │ │ │ │ │ │ ├── LoginServlet.java │ │ │ │ │ │ │ ├── LogoutServlet.java │ │ │ │ │ │ │ ├── PlatformLoginServlet.java │ │ │ │ │ │ │ ├── PlatformLogoutServlet.java │ │ │ │ │ │ │ └── URLProtector.java │ │ │ │ │ │ └── utils/ │ │ │ │ │ │ ├── LoginUrl.java │ │ │ │ │ │ ├── RedirectUrl.java │ │ │ │ │ │ ├── RedirectUrlBuilder.java │ │ │ │ │ │ └── RedirectUrlHandler.java │ │ │ │ │ ├── page/ │ │ │ │ │ │ ├── ApplicationAuthorizationsHelper.java │ │ │ │ │ │ ├── BDMClientDependenciesResolver.java │ │ │ │ │ │ ├── CustomPageChildFirstClassLoader.java │ │ │ │ │ │ ├── CustomPageDependenciesResolver.java │ │ │ │ │ │ ├── CustomPageRequestModifier.java │ │ │ │ │ │ ├── CustomPageService.java │ │ │ │ │ │ ├── CustomPageServlet.java │ │ │ │ │ │ ├── MonoParentJarFileClassLoader.java │ │ │ │ │ │ ├── PageContextHelper.java │ │ │ │ │ │ ├── PageDownloadServlet.java │ │ │ │ │ │ ├── PageMappingService.java │ │ │ │ │ │ ├── PageReference.java │ │ │ │ │ │ ├── PageRenderer.java │ │ │ │ │ │ ├── PageServlet.java │ │ │ │ │ │ ├── ResourceRenderer.java │ │ │ │ │ │ ├── RestApiRenderer.java │ │ │ │ │ │ ├── VersionedClassloader.java │ │ │ │ │ │ └── extension/ │ │ │ │ │ │ ├── PageContextImpl.java │ │ │ │ │ │ ├── PageResourceProviderImpl.java │ │ │ │ │ │ └── RestAPIContextImpl.java │ │ │ │ │ ├── preferences/ │ │ │ │ │ │ ├── constants/ │ │ │ │ │ │ │ ├── WebBonitaConstants.java │ │ │ │ │ │ │ ├── WebBonitaConstantsImpl.java │ │ │ │ │ │ │ ├── WebBonitaConstantsTenancyImpl.java │ │ │ │ │ │ │ └── WebBonitaConstantsUtils.java │ │ │ │ │ │ └── properties/ │ │ │ │ │ │ ├── ConfigurationFile.java │ │ │ │ │ │ ├── ConfigurationFilesManager.java │ │ │ │ │ │ ├── ConsoleProperties.java │ │ │ │ │ │ ├── PropertiesFactory.java │ │ │ │ │ │ ├── PropertiesWithSet.java │ │ │ │ │ │ └── SecurityProperties.java │ │ │ │ │ ├── servlet/ │ │ │ │ │ │ ├── ApplicationIconServlet.java │ │ │ │ │ │ ├── DocumentDownloadServlet.java │ │ │ │ │ │ ├── DocumentImageServlet.java │ │ │ │ │ │ ├── ErrorPageServlet.java │ │ │ │ │ │ ├── FileUploadServlet.java │ │ │ │ │ │ ├── IconContent.java │ │ │ │ │ │ ├── IconServlet.java │ │ │ │ │ │ ├── OrganizationIconServlet.java │ │ │ │ │ │ ├── PageUploadServlet.java │ │ │ │ │ │ ├── PlatformFileUploadServlet.java │ │ │ │ │ │ ├── PlatformTenantListener.java │ │ │ │ │ │ ├── ResourceLocationReader.java │ │ │ │ │ │ ├── ResourceServlet.java │ │ │ │ │ │ └── TenantFileUploadServlet.java │ │ │ │ │ └── utils/ │ │ │ │ │ ├── BPMEngineAPIUtil.java │ │ │ │ │ ├── BPMEngineException.java │ │ │ │ │ ├── BonitaHomeFolderAccessor.java │ │ │ │ │ ├── CacheUtil.java │ │ │ │ │ ├── ContractTypeConverter.java │ │ │ │ │ ├── DefaultTenantIdException.java │ │ │ │ │ ├── DocumentUtil.java │ │ │ │ │ ├── IconDescriptor.java │ │ │ │ │ ├── ListUtil.java │ │ │ │ │ ├── LocaleUtils.java │ │ │ │ │ ├── PlatformManagementUtils.java │ │ │ │ │ ├── SessionUtil.java │ │ │ │ │ ├── TenantCacheUtil.java │ │ │ │ │ ├── TenantCacheUtilFactory.java │ │ │ │ │ ├── TenantsManagementUtils.java │ │ │ │ │ ├── UnauthorizedFolderException.java │ │ │ │ │ ├── UnzipUtil.java │ │ │ │ │ ├── UrlBuilder.java │ │ │ │ │ └── UrlValue.java │ │ │ │ └── server/ │ │ │ │ ├── ConsoleServiceFactory.java │ │ │ │ ├── ConsoleServiceServlet.java │ │ │ │ ├── service/ │ │ │ │ │ ├── ApplicationsImportService.java │ │ │ │ │ ├── BonitaImportService.java │ │ │ │ │ ├── ConsoleService.java │ │ │ │ │ ├── OrganizationImportService.java │ │ │ │ │ └── ProcessActorImportService.java │ │ │ │ └── servlet/ │ │ │ │ ├── ApplicationsExportServlet.java │ │ │ │ ├── BonitaExportServlet.java │ │ │ │ ├── ExportByIdsServlet.java │ │ │ │ ├── OrganizationExportServlet.java │ │ │ │ └── ProcessActorsExportServlet.java │ │ │ ├── livingapps/ │ │ │ │ ├── ApplicationModel.java │ │ │ │ ├── ApplicationModelFactory.java │ │ │ │ ├── ApplicationRouter.java │ │ │ │ ├── LivingApplicationPageServlet.java │ │ │ │ ├── LivingApplicationServlet.java │ │ │ │ ├── exception/ │ │ │ │ │ └── CreationException.java │ │ │ │ └── menu/ │ │ │ │ ├── ChildrenMenuCollector.java │ │ │ │ ├── Menu.java │ │ │ │ ├── MenuContainer.java │ │ │ │ ├── MenuFactory.java │ │ │ │ ├── MenuLink.java │ │ │ │ └── RootMenuCollector.java │ │ │ └── web/ │ │ │ ├── common/ │ │ │ │ └── model/ │ │ │ │ ├── ImportStatusMessage.java │ │ │ │ └── ImportStatusMessages.java │ │ │ ├── rest/ │ │ │ │ ├── model/ │ │ │ │ │ ├── ModelFactory.java │ │ │ │ │ ├── application/ │ │ │ │ │ │ ├── AbstractApplicationDefinition.java │ │ │ │ │ │ ├── AbstractApplicationItem.java │ │ │ │ │ │ ├── ApplicationDefinition.java │ │ │ │ │ │ ├── ApplicationItem.java │ │ │ │ │ │ ├── ApplicationLinkDefinition.java │ │ │ │ │ │ └── ApplicationLinkItem.java │ │ │ │ │ ├── applicationmenu/ │ │ │ │ │ │ ├── ApplicationMenuDefinition.java │ │ │ │ │ │ └── ApplicationMenuItem.java │ │ │ │ │ ├── applicationpage/ │ │ │ │ │ │ ├── ApplicationPageDefinition.java │ │ │ │ │ │ └── ApplicationPageItem.java │ │ │ │ │ ├── bdm/ │ │ │ │ │ │ └── BusinessDataModelItem.java │ │ │ │ │ ├── bpm/ │ │ │ │ │ │ ├── AbstractDocumentDefinition.java │ │ │ │ │ │ ├── AbstractDocumentItem.java │ │ │ │ │ │ ├── cases/ │ │ │ │ │ │ │ ├── ArchivedActivityVariable.java │ │ │ │ │ │ │ ├── ArchivedCaseDefinition.java │ │ │ │ │ │ │ ├── ArchivedCaseDocumentDefinition.java │ │ │ │ │ │ │ ├── ArchivedCaseDocumentItem.java │ │ │ │ │ │ │ ├── ArchivedCaseItem.java │ │ │ │ │ │ │ ├── ArchivedCaseVariable.java │ │ │ │ │ │ │ ├── ArchivedCommentDefinition.java │ │ │ │ │ │ │ ├── ArchivedCommentItem.java │ │ │ │ │ │ │ ├── ArchivedVariable.java │ │ │ │ │ │ │ ├── CaseDefinition.java │ │ │ │ │ │ │ ├── CaseDocumentDefinition.java │ │ │ │ │ │ │ ├── CaseDocumentItem.java │ │ │ │ │ │ │ ├── CaseItem.java │ │ │ │ │ │ │ ├── CaseVariableDefinition.java │ │ │ │ │ │ │ ├── CaseVariableItem.java │ │ │ │ │ │ │ ├── CommentDefinition.java │ │ │ │ │ │ │ └── CommentItem.java │ │ │ │ │ │ ├── connector/ │ │ │ │ │ │ │ ├── ArchivedConnectorInstanceDefinition.java │ │ │ │ │ │ │ ├── ArchivedConnectorInstanceItem.java │ │ │ │ │ │ │ ├── ConnectorInstanceDefinition.java │ │ │ │ │ │ │ └── ConnectorInstanceItem.java │ │ │ │ │ │ ├── flownode/ │ │ │ │ │ │ │ ├── ActivityDefinition.java │ │ │ │ │ │ │ ├── ActivityItem.java │ │ │ │ │ │ │ ├── ArchivedActivityDefinition.java │ │ │ │ │ │ │ ├── ArchivedActivityItem.java │ │ │ │ │ │ │ ├── ArchivedFlowNode.java │ │ │ │ │ │ │ ├── ArchivedFlowNodeDefinition.java │ │ │ │ │ │ │ ├── ArchivedFlowNodeItem.java │ │ │ │ │ │ │ ├── ArchivedHumanTaskDefinition.java │ │ │ │ │ │ │ ├── ArchivedHumanTaskItem.java │ │ │ │ │ │ │ ├── ArchivedTaskDefinition.java │ │ │ │ │ │ │ ├── ArchivedTaskItem.java │ │ │ │ │ │ │ ├── ArchivedUserTaskDefinition.java │ │ │ │ │ │ │ ├── ArchivedUserTaskItem.java │ │ │ │ │ │ │ ├── FlowNodeDefinition.java │ │ │ │ │ │ │ ├── FlowNodeItem.java │ │ │ │ │ │ │ ├── HumanTaskDefinition.java │ │ │ │ │ │ │ ├── HumanTaskItem.java │ │ │ │ │ │ │ ├── IActivityItem.java │ │ │ │ │ │ │ ├── IFlowNodeItem.java │ │ │ │ │ │ │ ├── IHumanTaskItem.java │ │ │ │ │ │ │ ├── ITaskItem.java │ │ │ │ │ │ │ ├── IUserTaskItem.java │ │ │ │ │ │ │ ├── TaskDefinition.java │ │ │ │ │ │ │ ├── TaskItem.java │ │ │ │ │ │ │ ├── UserTaskDefinition.java │ │ │ │ │ │ │ └── UserTaskItem.java │ │ │ │ │ │ └── process/ │ │ │ │ │ │ ├── ActorDefinition.java │ │ │ │ │ │ ├── ActorItem.java │ │ │ │ │ │ ├── ActorMemberDefinition.java │ │ │ │ │ │ ├── ActorMemberItem.java │ │ │ │ │ │ ├── CategoryDefinition.java │ │ │ │ │ │ ├── CategoryItem.java │ │ │ │ │ │ ├── ProcessCategoryDefinition.java │ │ │ │ │ │ ├── ProcessCategoryItem.java │ │ │ │ │ │ ├── ProcessConnectorDefinition.java │ │ │ │ │ │ ├── ProcessConnectorDependencyDefinition.java │ │ │ │ │ │ ├── ProcessConnectorDependencyItem.java │ │ │ │ │ │ ├── ProcessConnectorItem.java │ │ │ │ │ │ ├── ProcessDefinition.java │ │ │ │ │ │ ├── ProcessDocumentDefinition.java │ │ │ │ │ │ ├── ProcessDocumentItem.java │ │ │ │ │ │ ├── ProcessItem.java │ │ │ │ │ │ ├── ProcessParameterDefinition.java │ │ │ │ │ │ ├── ProcessParameterItem.java │ │ │ │ │ │ ├── ProcessResolutionProblemDefinition.java │ │ │ │ │ │ └── ProcessResolutionProblemItem.java │ │ │ │ │ ├── builder/ │ │ │ │ │ │ ├── bpm/ │ │ │ │ │ │ │ ├── cases/ │ │ │ │ │ │ │ │ └── CaseItemBuilder.java │ │ │ │ │ │ │ └── process/ │ │ │ │ │ │ │ ├── ActorItemBuilder.java │ │ │ │ │ │ │ ├── ActorMemberItemBuilder.java │ │ │ │ │ │ │ ├── CategoryItemBuilder.java │ │ │ │ │ │ │ └── ProcessCategoryItemBuilder.java │ │ │ │ │ │ ├── identity/ │ │ │ │ │ │ │ └── ContactDataBuilder.java │ │ │ │ │ │ ├── organisation/ │ │ │ │ │ │ │ └── GroupItemBuilder.java │ │ │ │ │ │ └── profile/ │ │ │ │ │ │ └── member/ │ │ │ │ │ │ ├── AbstractProfileMemberBuilder.java │ │ │ │ │ │ ├── EngineProfileMemberBuilder.java │ │ │ │ │ │ └── ProfileMemberItemBuilder.java │ │ │ │ │ ├── document/ │ │ │ │ │ │ ├── ArchivedDocumentDefinition.java │ │ │ │ │ │ ├── ArchivedDocumentItem.java │ │ │ │ │ │ ├── DocumentDefinition.java │ │ │ │ │ │ └── DocumentItem.java │ │ │ │ │ ├── identity/ │ │ │ │ │ │ ├── AbstractContactDataDefinition.java │ │ │ │ │ │ ├── AbstractContactDataItem.java │ │ │ │ │ │ ├── CustomUserInfoDefinition.java │ │ │ │ │ │ ├── CustomUserInfoDefinitionDefinition.java │ │ │ │ │ │ ├── CustomUserInfoDefinitionItem.java │ │ │ │ │ │ ├── CustomUserInfoItem.java │ │ │ │ │ │ ├── CustomUserInfoValueDefinition.java │ │ │ │ │ │ ├── GroupDefinition.java │ │ │ │ │ │ ├── GroupItem.java │ │ │ │ │ │ ├── MemberType.java │ │ │ │ │ │ ├── MembershipDefinition.java │ │ │ │ │ │ ├── MembershipItem.java │ │ │ │ │ │ ├── PersonalContactDataDefinition.java │ │ │ │ │ │ ├── PersonalContactDataItem.java │ │ │ │ │ │ ├── ProfessionalContactDataDefinition.java │ │ │ │ │ │ ├── ProfessionalContactDataItem.java │ │ │ │ │ │ ├── RoleDefinition.java │ │ │ │ │ │ ├── RoleItem.java │ │ │ │ │ │ ├── UserDefinition.java │ │ │ │ │ │ └── UserItem.java │ │ │ │ │ ├── platform/ │ │ │ │ │ │ ├── PlatformDefinition.java │ │ │ │ │ │ └── PlatformItem.java │ │ │ │ │ ├── portal/ │ │ │ │ │ │ ├── page/ │ │ │ │ │ │ │ ├── PageDefinition.java │ │ │ │ │ │ │ └── PageItem.java │ │ │ │ │ │ └── profile/ │ │ │ │ │ │ ├── AbstractMemberDefinition.java │ │ │ │ │ │ ├── AbstractMemberItem.java │ │ │ │ │ │ ├── ProfileDefinition.java │ │ │ │ │ │ ├── ProfileItem.java │ │ │ │ │ │ ├── ProfileMemberDefinition.java │ │ │ │ │ │ └── ProfileMemberItem.java │ │ │ │ │ ├── system/ │ │ │ │ │ │ └── MaintenanceDetailsClient.java │ │ │ │ │ ├── tenant/ │ │ │ │ │ │ ├── BusinessDataModelDefinition.java │ │ │ │ │ │ └── BusinessDataModelItem.java │ │ │ │ │ └── user/ │ │ │ │ │ └── User.java │ │ │ │ └── server/ │ │ │ │ ├── APIPaginationUtils.java │ │ │ │ ├── BonitaRestAPIFactory.java │ │ │ │ ├── BonitaRestAPIServlet.java │ │ │ │ ├── QueryParameterUtils.java │ │ │ │ ├── SpringWebConfiguration.java │ │ │ │ ├── api/ │ │ │ │ │ ├── APIPreconditions.java │ │ │ │ │ ├── AbstractRESTController.java │ │ │ │ │ ├── ConsoleAPI.java │ │ │ │ │ ├── FilterParameterBindingAdvice.java │ │ │ │ │ ├── PlatformAPI.java │ │ │ │ │ ├── SpringResponseEntityUtils.java │ │ │ │ │ ├── SpringRestResponseEntityExceptionHandler.java │ │ │ │ │ ├── application/ │ │ │ │ │ │ └── APIApplication.java │ │ │ │ │ ├── applicationmenu/ │ │ │ │ │ │ └── APIApplicationMenu.java │ │ │ │ │ ├── applicationpage/ │ │ │ │ │ │ ├── APIApplicationDataStoreFactory.java │ │ │ │ │ │ └── APIApplicationPage.java │ │ │ │ │ ├── bdm/ │ │ │ │ │ │ ├── BusinessDataController.java │ │ │ │ │ │ ├── BusinessDataFieldValue.java │ │ │ │ │ │ ├── BusinessDataModelController.java │ │ │ │ │ │ ├── BusinessDataReferenceClient.java │ │ │ │ │ │ ├── BusinessDataReferenceController.java │ │ │ │ │ │ ├── BusinessDataReferenceConverter.java │ │ │ │ │ │ ├── MultipleBusinessDataReferenceClient.java │ │ │ │ │ │ └── SimpleBusinessDataReferenceClient.java │ │ │ │ │ ├── bpm/ │ │ │ │ │ │ ├── cases/ │ │ │ │ │ │ │ ├── APIArchivedCase.java │ │ │ │ │ │ │ ├── APIArchivedCaseDocument.java │ │ │ │ │ │ │ ├── APIArchivedComment.java │ │ │ │ │ │ │ ├── APICase.java │ │ │ │ │ │ │ ├── APICaseDocument.java │ │ │ │ │ │ │ ├── APICaseVariable.java │ │ │ │ │ │ │ ├── APICaseVariableAttributeChecker.java │ │ │ │ │ │ │ ├── APIComment.java │ │ │ │ │ │ │ ├── ArchivedCaseContextController.java │ │ │ │ │ │ │ ├── ArchivedCaseVariableController.java │ │ │ │ │ │ │ └── CaseContextController.java │ │ │ │ │ │ ├── connector/ │ │ │ │ │ │ │ ├── APIArchivedConnectorInstance.java │ │ │ │ │ │ │ └── APIConnectorInstance.java │ │ │ │ │ │ ├── flownode/ │ │ │ │ │ │ │ ├── APIActivity.java │ │ │ │ │ │ │ ├── APIFlowNode.java │ │ │ │ │ │ │ ├── APIHumanTask.java │ │ │ │ │ │ │ ├── APITask.java │ │ │ │ │ │ │ ├── APIUserTask.java │ │ │ │ │ │ │ ├── AbstractAPIActivity.java │ │ │ │ │ │ │ ├── AbstractAPIFlowNode.java │ │ │ │ │ │ │ ├── AbstractAPIHumanTask.java │ │ │ │ │ │ │ ├── AbstractAPITask.java │ │ │ │ │ │ │ ├── AbstractAPIUserTask.java │ │ │ │ │ │ │ ├── ActivityVariableController.java │ │ │ │ │ │ │ ├── TimerEventTrigger.java │ │ │ │ │ │ │ ├── TimerEventTriggerController.java │ │ │ │ │ │ │ ├── UserTaskContextController.java │ │ │ │ │ │ │ ├── UserTaskContractController.java │ │ │ │ │ │ │ ├── UserTaskExecutionController.java │ │ │ │ │ │ │ └── archive/ │ │ │ │ │ │ │ ├── APIArchivedActivity.java │ │ │ │ │ │ │ ├── APIArchivedFlowNode.java │ │ │ │ │ │ │ ├── APIArchivedHumanTask.java │ │ │ │ │ │ │ ├── APIArchivedTask.java │ │ │ │ │ │ │ ├── APIArchivedUserTask.java │ │ │ │ │ │ │ ├── ArchivedActivityVariableController.java │ │ │ │ │ │ │ └── ArchivedUserTaskContextController.java │ │ │ │ │ │ ├── message/ │ │ │ │ │ │ │ ├── BPMMessage.java │ │ │ │ │ │ │ ├── BPMMessageController.java │ │ │ │ │ │ │ └── BPMMessageValue.java │ │ │ │ │ │ ├── process/ │ │ │ │ │ │ │ ├── APIActor.java │ │ │ │ │ │ │ ├── APIActorMember.java │ │ │ │ │ │ │ ├── APICategory.java │ │ │ │ │ │ │ ├── APIProcess.java │ │ │ │ │ │ │ ├── APIProcessCategory.java │ │ │ │ │ │ │ ├── APIProcessConnector.java │ │ │ │ │ │ │ ├── APIProcessConnectorDependency.java │ │ │ │ │ │ │ ├── APIProcessParameter.java │ │ │ │ │ │ │ ├── APIProcessResolutionProblem.java │ │ │ │ │ │ │ ├── ProcessContractController.java │ │ │ │ │ │ │ ├── ProcessDefinitionDesignController.java │ │ │ │ │ │ │ └── ProcessInstantiationController.java │ │ │ │ │ │ └── signal/ │ │ │ │ │ │ ├── BPMSignal.java │ │ │ │ │ │ └── BPMSignalController.java │ │ │ │ │ ├── deployer/ │ │ │ │ │ │ ├── ApplicationDeployer.java │ │ │ │ │ │ ├── ApplicationPageDeployer.java │ │ │ │ │ │ ├── DeployerFactory.java │ │ │ │ │ │ ├── GenericDeployer.java │ │ │ │ │ │ ├── PageDeployer.java │ │ │ │ │ │ └── UserDeployer.java │ │ │ │ │ ├── document/ │ │ │ │ │ │ ├── APIArchivedDocument.java │ │ │ │ │ │ ├── APIDocument.java │ │ │ │ │ │ └── api/ │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── DocumentDatastore.java │ │ │ │ │ │ └── DocumentUtil.java │ │ │ │ │ ├── extension/ │ │ │ │ │ │ ├── ApiExtensionController.java │ │ │ │ │ │ ├── ControllerClassName.java │ │ │ │ │ │ └── ResourceExtensionResolver.java │ │ │ │ │ ├── form/ │ │ │ │ │ │ ├── FormMappingController.java │ │ │ │ │ │ └── FormMappingItem.java │ │ │ │ │ ├── organization/ │ │ │ │ │ │ ├── APICustomUserInfoDefinition.java │ │ │ │ │ │ ├── APICustomUserInfoUser.java │ │ │ │ │ │ ├── APICustomUserInfoValue.java │ │ │ │ │ │ ├── APIGroup.java │ │ │ │ │ │ ├── APIMembership.java │ │ │ │ │ │ ├── APIPersonalContactData.java │ │ │ │ │ │ ├── APIProfessionalContactData.java │ │ │ │ │ │ ├── APIRole.java │ │ │ │ │ │ ├── APIUser.java │ │ │ │ │ │ ├── CustomUserInfoAttributeConverter.java │ │ │ │ │ │ ├── CustomUserInfoConverter.java │ │ │ │ │ │ └── password/ │ │ │ │ │ │ └── validator/ │ │ │ │ │ │ ├── DefaultPasswordValidator.java │ │ │ │ │ │ └── RobustnessPasswordValidator.java │ │ │ │ │ ├── page/ │ │ │ │ │ │ └── APIPage.java │ │ │ │ │ ├── platform/ │ │ │ │ │ │ ├── APIPlatform.java │ │ │ │ │ │ └── SystemInformationController.java │ │ │ │ │ ├── profile/ │ │ │ │ │ │ ├── APIProfile.java │ │ │ │ │ │ ├── APIProfileMember.java │ │ │ │ │ │ └── AbstractAPIMember.java │ │ │ │ │ ├── resource/ │ │ │ │ │ │ ├── ErrorMessage.java │ │ │ │ │ │ └── ErrorMessageWithExplanations.java │ │ │ │ │ ├── system/ │ │ │ │ │ │ ├── APII18nLocale.java │ │ │ │ │ │ ├── APISession.java │ │ │ │ │ │ ├── BonitaVersion.java │ │ │ │ │ │ ├── I18nTranslationController.java │ │ │ │ │ │ ├── MaintenanceController.java │ │ │ │ │ │ ├── Translation.java │ │ │ │ │ │ └── VersionFile.java │ │ │ │ │ └── tenant/ │ │ │ │ │ └── TenantResourceItem.java │ │ │ │ ├── datastore/ │ │ │ │ │ ├── CommonDatastore.java │ │ │ │ │ ├── ComposedDatastore.java │ │ │ │ │ ├── application/ │ │ │ │ │ │ ├── ApplicationDataStore.java │ │ │ │ │ │ ├── ApplicationDataStoreCreator.java │ │ │ │ │ │ ├── ApplicationFilterCreator.java │ │ │ │ │ │ ├── ApplicationItemConverter.java │ │ │ │ │ │ └── ApplicationSearchDescriptorConverter.java │ │ │ │ │ ├── applicationmenu/ │ │ │ │ │ │ ├── ApplicationMenuDataStore.java │ │ │ │ │ │ ├── ApplicationMenuDataStoreCreator.java │ │ │ │ │ │ ├── ApplicationMenuFilterCreator.java │ │ │ │ │ │ ├── ApplicationMenuItemConverter.java │ │ │ │ │ │ └── ApplicationMenuSearchDescriptorConverter.java │ │ │ │ │ ├── applicationpage/ │ │ │ │ │ │ ├── ApplicationPageDataStore.java │ │ │ │ │ │ ├── ApplicationPageDataStoreCreator.java │ │ │ │ │ │ ├── ApplicationPageFilterCreator.java │ │ │ │ │ │ ├── ApplicationPageItemConverter.java │ │ │ │ │ │ └── ApplicationPageSearchDescriptorConverter.java │ │ │ │ │ ├── bpm/ │ │ │ │ │ │ ├── cases/ │ │ │ │ │ │ │ ├── ArchivedCaseDatastore.java │ │ │ │ │ │ │ ├── ArchivedCaseDocumentDatastore.java │ │ │ │ │ │ │ ├── ArchivedCaseDocumentFilterCreator.java │ │ │ │ │ │ │ ├── ArchivedCaseDocumentItemConverter.java │ │ │ │ │ │ │ ├── ArchivedCaseDocumentSearchAttributeConverter.java │ │ │ │ │ │ │ ├── ArchivedCommentDatastore.java │ │ │ │ │ │ │ ├── CaseDatastore.java │ │ │ │ │ │ │ ├── CaseDocumentDatastore.java │ │ │ │ │ │ │ ├── CaseDocumentFilterCreator.java │ │ │ │ │ │ │ ├── CaseDocumentItemConverter.java │ │ │ │ │ │ │ ├── CaseDocumentSearchAttributeConverter.java │ │ │ │ │ │ │ ├── CaseItemConverter.java │ │ │ │ │ │ │ ├── CaseSarter.java │ │ │ │ │ │ │ ├── CaseVariableDatastore.java │ │ │ │ │ │ │ └── CommentDatastore.java │ │ │ │ │ │ ├── connector/ │ │ │ │ │ │ │ ├── ArchivedConnectorInstanceDatastore.java │ │ │ │ │ │ │ ├── ArchivedConnectorInstanceItemWrapper.java │ │ │ │ │ │ │ ├── ConnectorInstanceDatastore.java │ │ │ │ │ │ │ ├── ConnectorInstanceItemWrapper.java │ │ │ │ │ │ │ ├── ConnectorInstanceResetStateConverter.java │ │ │ │ │ │ │ └── ConnectorInstanceStateConverter.java │ │ │ │ │ │ ├── flownode/ │ │ │ │ │ │ │ ├── AbstractActivityDatastore.java │ │ │ │ │ │ │ ├── AbstractFlowNodeDatastore.java │ │ │ │ │ │ │ ├── AbstractHumanTaskDatastore.java │ │ │ │ │ │ │ ├── AbstractTaskDatastore.java │ │ │ │ │ │ │ ├── AbstractUserTaskDatastore.java │ │ │ │ │ │ │ ├── ActivityDatastore.java │ │ │ │ │ │ │ ├── FlowNodeConverter.java │ │ │ │ │ │ │ ├── FlowNodeDatastore.java │ │ │ │ │ │ │ ├── FlowNodeTypeConverter.java │ │ │ │ │ │ │ ├── HumanTaskDatastore.java │ │ │ │ │ │ │ ├── TaskDatastore.java │ │ │ │ │ │ │ ├── TaskFinder.java │ │ │ │ │ │ │ ├── UserTaskDatastore.java │ │ │ │ │ │ │ └── archive/ │ │ │ │ │ │ │ ├── AbstractArchivedActivityDatastore.java │ │ │ │ │ │ │ ├── AbstractArchivedFlowNodeDatastore.java │ │ │ │ │ │ │ ├── AbstractArchivedHumanTaskDatastore.java │ │ │ │ │ │ │ ├── AbstractArchivedTaskDatastore.java │ │ │ │ │ │ │ ├── AbstractArchivedUserTaskDatastore.java │ │ │ │ │ │ │ ├── ArchivedActivityDatastore.java │ │ │ │ │ │ │ ├── ArchivedFlowNodeDatastore.java │ │ │ │ │ │ │ ├── ArchivedFlowNodeFilterCreator.java │ │ │ │ │ │ │ ├── ArchivedHumanTaskDatastore.java │ │ │ │ │ │ │ ├── ArchivedTaskDatastore.java │ │ │ │ │ │ │ ├── ArchivedUserTaskDatastore.java │ │ │ │ │ │ │ └── converter/ │ │ │ │ │ │ │ ├── ArchivedActivitySearchDescriptorConverter.java │ │ │ │ │ │ │ ├── ArchivedFlowNodeSearchDescriptorConverter.java │ │ │ │ │ │ │ └── ArchivedHumanTaskSearchDescriptorConverter.java │ │ │ │ │ │ └── process/ │ │ │ │ │ │ ├── ActorDatastore.java │ │ │ │ │ │ ├── ActorMemberDatastore.java │ │ │ │ │ │ ├── CategoryDatastore.java │ │ │ │ │ │ ├── ProcessCategoryDatastore.java │ │ │ │ │ │ ├── ProcessConnectorDatastore.java │ │ │ │ │ │ ├── ProcessConnectorDependencyDatastore.java │ │ │ │ │ │ ├── ProcessDatastore.java │ │ │ │ │ │ ├── ProcessResolutionProblemDatastore.java │ │ │ │ │ │ └── helper/ │ │ │ │ │ │ ├── ProcessItemConverter.java │ │ │ │ │ │ ├── ProcessSearchDescriptorConverter.java │ │ │ │ │ │ ├── SearchProcessFilterCreator.java │ │ │ │ │ │ └── SearchProcessHelper.java │ │ │ │ │ ├── converter/ │ │ │ │ │ │ ├── ActivityAttributeConverter.java │ │ │ │ │ │ ├── AttributeConverter.java │ │ │ │ │ │ ├── BooleanValueConverter.java │ │ │ │ │ │ ├── EmptyAttributeConverter.java │ │ │ │ │ │ ├── ItemConverter.java │ │ │ │ │ │ ├── ItemSearchResultConverter.java │ │ │ │ │ │ ├── LongValueConverter.java │ │ │ │ │ │ ├── StringValueConverter.java │ │ │ │ │ │ └── ValueConverter.java │ │ │ │ │ ├── filter/ │ │ │ │ │ │ ├── ActivityFilterCreator.java │ │ │ │ │ │ ├── BooleanValue.java │ │ │ │ │ │ ├── Field.java │ │ │ │ │ │ ├── Filter.java │ │ │ │ │ │ ├── FilterAccessor.java │ │ │ │ │ │ ├── FilterCreator.java │ │ │ │ │ │ ├── Filters.java │ │ │ │ │ │ ├── FormMappingTypeCreator.java │ │ │ │ │ │ ├── GenericFilterCreator.java │ │ │ │ │ │ ├── LongValue.java │ │ │ │ │ │ ├── StrValue.java │ │ │ │ │ │ └── Value.java │ │ │ │ │ ├── organization/ │ │ │ │ │ │ ├── Avatars.java │ │ │ │ │ │ ├── ContactDataConverter.java │ │ │ │ │ │ ├── GroupCreatorConverter.java │ │ │ │ │ │ ├── GroupDatastore.java │ │ │ │ │ │ ├── GroupItemConverter.java │ │ │ │ │ │ ├── GroupUpdaterConverter.java │ │ │ │ │ │ ├── MembershipDatastore.java │ │ │ │ │ │ ├── PersonalContactDataDatastore.java │ │ │ │ │ │ ├── ProfessionalContactDataDatastore.java │ │ │ │ │ │ ├── RoleDatastore.java │ │ │ │ │ │ ├── UserCreatorConverter.java │ │ │ │ │ │ ├── UserDatastore.java │ │ │ │ │ │ ├── UserFilterCreator.java │ │ │ │ │ │ ├── UserItemConverter.java │ │ │ │ │ │ ├── UserSearchAttributeConverter.java │ │ │ │ │ │ └── UserUpdaterConverter.java │ │ │ │ │ ├── page/ │ │ │ │ │ │ ├── CustomPageContentValidator.java │ │ │ │ │ │ ├── InvalidPageZipContentException.java │ │ │ │ │ │ ├── PageDatastore.java │ │ │ │ │ │ ├── PageDatastoreFactory.java │ │ │ │ │ │ ├── PageFilterCreator.java │ │ │ │ │ │ ├── PageItemConverter.java │ │ │ │ │ │ └── PageSearchDescriptorConverter.java │ │ │ │ │ ├── profile/ │ │ │ │ │ │ ├── GetProfileHelper.java │ │ │ │ │ │ ├── ProfileItemConverter.java │ │ │ │ │ │ ├── ProfileSearchDescriptorConverter.java │ │ │ │ │ │ ├── SearchProfilesHelper.java │ │ │ │ │ │ └── member/ │ │ │ │ │ │ ├── AddProfileMemberHelper.java │ │ │ │ │ │ ├── DeleteProfileMemberHelper.java │ │ │ │ │ │ ├── MemberType.java │ │ │ │ │ │ ├── MemberTypeConverter.java │ │ │ │ │ │ ├── ProfileMemberItemConverter.java │ │ │ │ │ │ ├── ProfileMemberSearchDescriptorConverter.java │ │ │ │ │ │ └── SearchProfileMembersHelper.java │ │ │ │ │ └── utils/ │ │ │ │ │ ├── SearchOptionsCreator.java │ │ │ │ │ ├── Sort.java │ │ │ │ │ ├── Sorts.java │ │ │ │ │ ├── Variable.java │ │ │ │ │ ├── VariableMapper.java │ │ │ │ │ └── VariablesMapper.java │ │ │ │ ├── engineclient/ │ │ │ │ │ ├── ActivityEngineClient.java │ │ │ │ │ ├── CaseEngineClient.java │ │ │ │ │ ├── CustomUserInfoEngineClient.java │ │ │ │ │ ├── CustomUserInfoEngineClientCreator.java │ │ │ │ │ ├── EngineAPIAccessor.java │ │ │ │ │ ├── EngineClientFactory.java │ │ │ │ │ ├── GroupEngineClient.java │ │ │ │ │ ├── HumanTaskEngineClient.java │ │ │ │ │ ├── ProcessEngineClient.java │ │ │ │ │ ├── ProfileEngineClient.java │ │ │ │ │ ├── ProfileMemberEngineClient.java │ │ │ │ │ ├── TenantManagementEngineClient.java │ │ │ │ │ └── UserEngineClient.java │ │ │ │ ├── framework/ │ │ │ │ │ ├── API.java │ │ │ │ │ ├── APIServletCall.java │ │ │ │ │ ├── APIs.java │ │ │ │ │ ├── Deployer.java │ │ │ │ │ ├── RestAPIFactory.java │ │ │ │ │ ├── api/ │ │ │ │ │ │ ├── APIHasAdd.java │ │ │ │ │ │ ├── APIHasDelete.java │ │ │ │ │ │ ├── APIHasGet.java │ │ │ │ │ │ ├── APIHasSearch.java │ │ │ │ │ │ ├── APIHasUpdate.java │ │ │ │ │ │ ├── Datastore.java │ │ │ │ │ │ ├── DatastoreHasAdd.java │ │ │ │ │ │ ├── DatastoreHasDelete.java │ │ │ │ │ │ ├── DatastoreHasGet.java │ │ │ │ │ │ ├── DatastoreHasSearch.java │ │ │ │ │ │ ├── DatastoreHasUpdate.java │ │ │ │ │ │ └── EnumConverter.java │ │ │ │ │ ├── exception/ │ │ │ │ │ │ ├── APIAttributeException.java │ │ │ │ │ │ ├── APIAttributeMissingException.java │ │ │ │ │ │ ├── APIAttributesException.java │ │ │ │ │ │ ├── APIFileUploadNotFoundException.java │ │ │ │ │ │ ├── APIFilterEmptyException.java │ │ │ │ │ │ ├── APIFilterException.java │ │ │ │ │ │ ├── APIFilterMandatoryException.java │ │ │ │ │ │ ├── APIMissingIdException.java │ │ │ │ │ │ └── ForbiddenAttributesException.java │ │ │ │ │ ├── json/ │ │ │ │ │ │ ├── JSonSimpleDeserializer.java │ │ │ │ │ │ ├── JacksonDeserializer.java │ │ │ │ │ │ └── JacksonSerializer.java │ │ │ │ │ ├── search/ │ │ │ │ │ │ ├── ISearchDirection.java │ │ │ │ │ │ └── ItemSearchResult.java │ │ │ │ │ ├── servlet/ │ │ │ │ │ │ └── APIServlet.java │ │ │ │ │ └── utils/ │ │ │ │ │ ├── FilePathBuilder.java │ │ │ │ │ ├── ParsedRestRequestURI.java │ │ │ │ │ ├── RestRequestURIParser.java │ │ │ │ │ ├── SearchOptionsBuilderUtil.java │ │ │ │ │ └── converter/ │ │ │ │ │ ├── ConversionException.java │ │ │ │ │ ├── Converter.java │ │ │ │ │ ├── ConverterFactory.java │ │ │ │ │ ├── TypeConverter.java │ │ │ │ │ └── typed/ │ │ │ │ │ ├── BooleanConverter.java │ │ │ │ │ ├── DateConverter.java │ │ │ │ │ ├── DoubleConverter.java │ │ │ │ │ ├── IntegerConverter.java │ │ │ │ │ ├── LongConverter.java │ │ │ │ │ └── StringConverter.java │ │ │ │ └── utils/ │ │ │ │ ├── BonitaJacksonModuleProvider.java │ │ │ │ ├── DataInstanceSerializer.java │ │ │ │ ├── JacksonSerializerHelper.java │ │ │ │ ├── TimerEventTriggerInstanceSerializer.java │ │ │ │ └── UserMixIn.java │ │ │ ├── server/ │ │ │ │ └── login/ │ │ │ │ ├── LoginFailureTracker.java │ │ │ │ ├── LoginFailureTrackerAccessor.java │ │ │ │ ├── LoginFailureTrackerConfiguration.java │ │ │ │ └── MemoryLoginFailureTracker.java │ │ │ └── toolkit/ │ │ │ ├── client/ │ │ │ │ ├── ItemDefinitionFactory.java │ │ │ │ ├── common/ │ │ │ │ │ ├── AbstractTreeNode.java │ │ │ │ │ ├── CommonDateFormater.java │ │ │ │ │ ├── Tree.java │ │ │ │ │ ├── TreeIndexed.java │ │ │ │ │ ├── TreeLeaf.java │ │ │ │ │ ├── TreeNode.java │ │ │ │ │ ├── exception/ │ │ │ │ │ │ ├── KnownException.java │ │ │ │ │ │ ├── api/ │ │ │ │ │ │ │ ├── APIException.java │ │ │ │ │ │ │ ├── APIForbiddenException.java │ │ │ │ │ │ │ ├── APIIncorrectIdException.java │ │ │ │ │ │ │ ├── APIItemException.java │ │ │ │ │ │ │ ├── APIItemIdMalformedException.java │ │ │ │ │ │ │ ├── APIItemNotFoundException.java │ │ │ │ │ │ │ ├── APIMalformedUrlException.java │ │ │ │ │ │ │ ├── APIMethodNotAllowedException.java │ │ │ │ │ │ │ ├── APINotFoundException.java │ │ │ │ │ │ │ ├── APISearchIndexOutOfRange.java │ │ │ │ │ │ │ └── APITooManyRequestException.java │ │ │ │ │ │ └── http/ │ │ │ │ │ │ ├── HttpException.java │ │ │ │ │ │ ├── JsonExceptionSerializer.java │ │ │ │ │ │ └── ServerException.java │ │ │ │ │ ├── i18n/ │ │ │ │ │ │ ├── AbstractI18n.java │ │ │ │ │ │ ├── T_.java │ │ │ │ │ │ └── model/ │ │ │ │ │ │ ├── I18nLocaleDefinition.java │ │ │ │ │ │ ├── I18nLocaleItem.java │ │ │ │ │ │ ├── I18nTranslationDefinition.java │ │ │ │ │ │ └── I18nTranslationItem.java │ │ │ │ │ ├── json/ │ │ │ │ │ │ ├── JSonItemReader.java │ │ │ │ │ │ ├── JSonItemWriter.java │ │ │ │ │ │ ├── JSonSerializer.java │ │ │ │ │ │ ├── JSonUnserializer.java │ │ │ │ │ │ ├── JSonUtil.java │ │ │ │ │ │ └── JsonSerializable.java │ │ │ │ │ ├── session/ │ │ │ │ │ │ ├── SessionDefinition.java │ │ │ │ │ │ └── SessionItem.java │ │ │ │ │ ├── texttemplate/ │ │ │ │ │ │ ├── Arg.java │ │ │ │ │ │ └── TextTemplate.java │ │ │ │ │ └── util/ │ │ │ │ │ ├── MapUtil.java │ │ │ │ │ └── StringUtil.java │ │ │ │ ├── data/ │ │ │ │ │ ├── APIID.java │ │ │ │ │ └── item/ │ │ │ │ │ ├── Definitions.java │ │ │ │ │ ├── DiscriminatedItemDefinitionHelper.java │ │ │ │ │ ├── DummyItem.java │ │ │ │ │ ├── DummyItemDefinition.java │ │ │ │ │ ├── IItem.java │ │ │ │ │ ├── Item.java │ │ │ │ │ ├── ItemDefinition.java │ │ │ │ │ ├── attribute/ │ │ │ │ │ │ ├── ItemAttribute.java │ │ │ │ │ │ ├── ModifiableInput.java │ │ │ │ │ │ ├── ModifierEngine.java │ │ │ │ │ │ ├── ModifiersList.java │ │ │ │ │ │ ├── Validable.java │ │ │ │ │ │ ├── ValidationError.java │ │ │ │ │ │ ├── ValidationException.java │ │ │ │ │ │ ├── ValidatorEngine.java │ │ │ │ │ │ ├── ValidatorsList.java │ │ │ │ │ │ ├── modifier/ │ │ │ │ │ │ │ ├── AbstractStringModifier.java │ │ │ │ │ │ │ ├── DefaultValueModifier.java │ │ │ │ │ │ │ ├── Modifier.java │ │ │ │ │ │ │ └── ReplaceRegexpModifier.java │ │ │ │ │ │ └── validator/ │ │ │ │ │ │ ├── AbstractCollectionValidator.java │ │ │ │ │ │ ├── AbstractNumericValidator.java │ │ │ │ │ │ ├── AbstractStringFormatValidator.java │ │ │ │ │ │ ├── AbstractStringValidator.java │ │ │ │ │ │ ├── EnumValidator.java │ │ │ │ │ │ ├── FileExtensionAllowedValidator.java │ │ │ │ │ │ ├── FileExtensionForbiddenValidator.java │ │ │ │ │ │ ├── FileIsImageOrServletPathValidator.java │ │ │ │ │ │ ├── FileIsImageValidator.java │ │ │ │ │ │ ├── FileIsNoScript.java │ │ │ │ │ │ ├── IsBooleanValidator.java │ │ │ │ │ │ ├── IsIntegerValidator.java │ │ │ │ │ │ ├── IsNumericValidator.java │ │ │ │ │ │ ├── MandatoryValidator.java │ │ │ │ │ │ ├── StringFormatColorValidator.java │ │ │ │ │ │ ├── StringFormatEmailValidator.java │ │ │ │ │ │ ├── StringFormatURLValidator.java │ │ │ │ │ │ ├── StringMaxLengthValidator.java │ │ │ │ │ │ ├── StringRegexpValidator.java │ │ │ │ │ │ ├── StringSingleLineValidator.java │ │ │ │ │ │ └── Validator.java │ │ │ │ │ └── template/ │ │ │ │ │ ├── ItemHasCreator.java │ │ │ │ │ ├── ItemHasDualDescription.java │ │ │ │ │ ├── ItemHasDualName.java │ │ │ │ │ ├── ItemHasIcon.java │ │ │ │ │ ├── ItemHasLastUpdateDate.java │ │ │ │ │ ├── ItemHasLastUpdater.java │ │ │ │ │ └── ItemHasUniqueId.java │ │ │ │ └── ui/ │ │ │ │ └── utils/ │ │ │ │ ├── DateFormat.java │ │ │ │ └── ListUtils.java │ │ │ └── server/ │ │ │ ├── Service.java │ │ │ ├── ServiceException.java │ │ │ ├── ServiceFactory.java │ │ │ ├── ServiceNotFoundException.java │ │ │ ├── ServiceServletCall.java │ │ │ ├── ServletCall.java │ │ │ ├── servlet/ │ │ │ │ ├── ServiceServlet.java │ │ │ │ └── ToolkitHttpServlet.java │ │ │ └── utils/ │ │ │ └── ServerDateFormater.java │ │ ├── resources/ │ │ │ ├── VERSION │ │ │ └── i18n/ │ │ │ ├── portal-js_es.po │ │ │ ├── portal-js_fr.po │ │ │ ├── portal-js_ja.po │ │ │ ├── portal-js_pt_BR.po │ │ │ ├── portal_es.po │ │ │ ├── portal_fr.po │ │ │ ├── portal_ja.po │ │ │ ├── portal_pt_BR.po │ │ │ ├── resources_es.po │ │ │ ├── resources_fr.po │ │ │ ├── resources_ja.po │ │ │ └── resources_pt_BR.po │ │ └── webapp/ │ │ ├── 403.jsp │ │ ├── 404.jsp │ │ ├── 500.jsp │ │ ├── 503.jsp │ │ ├── WEB-INF/ │ │ │ ├── errors.html │ │ │ ├── urlrewrite.xml │ │ │ └── web.xml │ │ ├── css/ │ │ │ └── error-style.css │ │ ├── index.html │ │ └── login.jsp │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ ├── console/ │ │ │ ├── common/ │ │ │ │ ├── FakeI18n.java │ │ │ │ ├── model/ │ │ │ │ │ └── ImportStatusMessagesTest.java │ │ │ │ └── server/ │ │ │ │ ├── auth/ │ │ │ │ │ ├── AuthenticationManagerFactoryTest.java │ │ │ │ │ ├── AuthenticationManagerPropertiesTest.java │ │ │ │ │ └── impl/ │ │ │ │ │ └── standard/ │ │ │ │ │ └── StandardAuthenticationManagerImplTest.java │ │ │ │ ├── filter/ │ │ │ │ │ ├── CacheFilterTest.java │ │ │ │ │ ├── ExcludingPatternFilterTest.java │ │ │ │ │ ├── MultiReadHttpServletRequestTest.java │ │ │ │ │ ├── NoCacheFilterTest.java │ │ │ │ │ ├── PathSanitizerTest.java │ │ │ │ │ ├── RedirectFilterTest.java │ │ │ │ │ ├── RequestIdFilterTest.java │ │ │ │ │ └── SanitizerFilterTest.java │ │ │ │ ├── form/ │ │ │ │ │ ├── ProcessFormServiceTest.java │ │ │ │ │ └── ProcessFormServletTest.java │ │ │ │ ├── i18n/ │ │ │ │ │ ├── I18nTest.java │ │ │ │ │ └── POParserTest.java │ │ │ │ ├── login/ │ │ │ │ │ ├── LoginManagerTest.java │ │ │ │ │ ├── MemoryLoginFailureTrackerTest.java │ │ │ │ │ ├── PortalCookiesTest.java │ │ │ │ │ ├── filter/ │ │ │ │ │ │ ├── AlreadyLoggedInRuleTest.java │ │ │ │ │ │ ├── AuthenticationFilterTest.java │ │ │ │ │ │ ├── FakeAuthenticationManager.java │ │ │ │ │ │ ├── FrameSecurityFilterTest.java │ │ │ │ │ │ ├── RestAPIAuthorizationFilterTest.java │ │ │ │ │ │ ├── TokenGeneratorFilterTest.java │ │ │ │ │ │ ├── TokenGeneratorTest.java │ │ │ │ │ │ └── TokenValidatorFilterTest.java │ │ │ │ │ ├── servlet/ │ │ │ │ │ │ ├── LoginServletTest.java │ │ │ │ │ │ ├── LogoutServletTest.java │ │ │ │ │ │ ├── PlatformLoginServletTest.java │ │ │ │ │ │ └── URLProtectorTest.java │ │ │ │ │ └── utils/ │ │ │ │ │ ├── RedirectUrlBuilderTest.java │ │ │ │ │ └── RedirectUrlHandlerTest.java │ │ │ │ ├── page/ │ │ │ │ │ ├── ApplicationAuthorizationsHelperTest.java │ │ │ │ │ ├── CustomPageChildFirstClassLoaderTest.java │ │ │ │ │ ├── CustomPageDependenciesResolverTest.java │ │ │ │ │ ├── CustomPageRequestModifierTest.java │ │ │ │ │ ├── CustomPageServiceTest.java │ │ │ │ │ ├── CustomPageServletTest.java │ │ │ │ │ ├── PageContextAssert.java │ │ │ │ │ ├── PageContextHelperTest.java │ │ │ │ │ ├── PageMappingServiceTest.java │ │ │ │ │ ├── PageRendererTest.java │ │ │ │ │ ├── PageServletTest.java │ │ │ │ │ ├── ResourceRendererTest.java │ │ │ │ │ ├── RestApiResponseAssert.java │ │ │ │ │ └── extension/ │ │ │ │ │ ├── PageContextImplTest.java │ │ │ │ │ └── PageResourceProviderImplTest.java │ │ │ │ ├── preferences/ │ │ │ │ │ ├── constants/ │ │ │ │ │ │ └── WebBonitaConstantsImplTest.java │ │ │ │ │ └── properties/ │ │ │ │ │ ├── ConfigurationFileTest.java │ │ │ │ │ ├── ConfigurationFilesManagerTest.java │ │ │ │ │ ├── ConsolePropertiesTest.java │ │ │ │ │ └── SecurityPropertiesTest.java │ │ │ │ ├── servlet/ │ │ │ │ │ ├── ApplicationIconServletTest.java │ │ │ │ │ ├── DocumentDownloadServletTest.java │ │ │ │ │ ├── DocumentImageServletTest.java │ │ │ │ │ ├── ErrorPageServletTest.java │ │ │ │ │ ├── FileUploadServletTest.java │ │ │ │ │ ├── OrganizationIconServletTest.java │ │ │ │ │ ├── PageUploadServletTest.java │ │ │ │ │ ├── ResourceLocationReaderTest.java │ │ │ │ │ └── TenantFileUploadServletTest.java │ │ │ │ └── utils/ │ │ │ │ ├── BonitaHomeFolderAccessorTest.java │ │ │ │ ├── CacheUtilTest.java │ │ │ │ ├── ContractTypeConverterTest.java │ │ │ │ ├── LocaleUtilsTest.java │ │ │ │ ├── PlatformManagementUtilsTest.java │ │ │ │ ├── TenantCacheUtilTest.java │ │ │ │ └── UrlBuilderTest.java │ │ │ └── server/ │ │ │ ├── ConsoleServiceFactoryTest.java │ │ │ ├── service/ │ │ │ │ ├── ApplicationsImportServiceTest.java │ │ │ │ └── OrganizationImportServiceTest.java │ │ │ └── servlet/ │ │ │ ├── ApplicationsExportServletTest.java │ │ │ └── BonitaExportServletTest.java │ │ ├── livingapps/ │ │ │ ├── ApplicationModelFactoryTest.java │ │ │ ├── ApplicationModelTest.java │ │ │ ├── ApplicationRouterTest.java │ │ │ ├── LivingApplicationPageServletTest.java │ │ │ ├── LivingApplicationServletTest.java │ │ │ └── menu/ │ │ │ ├── ChildrenMenuCollectorTest.java │ │ │ ├── MenuFactoryTest.java │ │ │ └── RootMenuCollectorTest.java │ │ └── web/ │ │ ├── rest/ │ │ │ ├── model/ │ │ │ │ ├── ModelFactoryExtTest.java │ │ │ │ └── builder/ │ │ │ │ └── profile/ │ │ │ │ └── ProfileItemBuilder.java │ │ │ └── server/ │ │ │ ├── APITestWithMock.java │ │ │ ├── BonitaRestAPIFactoryTest.java │ │ │ ├── QueryParameterUtilsTest.java │ │ │ ├── ServletMappingValidationTest.java │ │ │ ├── UrlRewriteConfigurationTest.java │ │ │ ├── api/ │ │ │ │ ├── AbstractControllerTest.java │ │ │ │ ├── AbstractPlatformControllerTest.java │ │ │ │ ├── RestControllerUtils.java │ │ │ │ ├── application/ │ │ │ │ │ └── APIApplicationTest.java │ │ │ │ ├── applicationpage/ │ │ │ │ │ └── APIApplicationPageTest.java │ │ │ │ ├── bdm/ │ │ │ │ │ ├── BusinessDataControllerTest.java │ │ │ │ │ ├── BusinessDataModelControllerTest.java │ │ │ │ │ └── BusinessDataReferenceControllerTest.java │ │ │ │ ├── bpm/ │ │ │ │ │ ├── cases/ │ │ │ │ │ │ ├── APIArchivedCaseTest.java │ │ │ │ │ │ ├── APICaseDocumentTest.java │ │ │ │ │ │ ├── APICaseTest.java │ │ │ │ │ │ ├── APICaseVariableAttributeCheckerTest.java │ │ │ │ │ │ ├── APICaseVariableTest.java │ │ │ │ │ │ ├── ArchivedCaseContextControllerTest.java │ │ │ │ │ │ ├── ArchivedCaseVariableControllerTest.java │ │ │ │ │ │ ├── ArchivedDataInstanceBuilder.java │ │ │ │ │ │ └── CaseContextControllerTest.java │ │ │ │ │ ├── connector/ │ │ │ │ │ │ └── APIConnectorInstanceTest.java │ │ │ │ │ ├── flownode/ │ │ │ │ │ │ ├── APIActivityTest.java │ │ │ │ │ │ ├── APITaskTest.java │ │ │ │ │ │ ├── APIUserTaskTest.java │ │ │ │ │ │ ├── ActivityVariableControllerTest.java │ │ │ │ │ │ ├── Dummy.java │ │ │ │ │ │ ├── TimerEventTriggerControllerTest.java │ │ │ │ │ │ ├── UserTaskContextControllerTest.java │ │ │ │ │ │ ├── UserTaskContractControllerTest.java │ │ │ │ │ │ ├── UserTaskExecutionControllerTest.java │ │ │ │ │ │ └── archive/ │ │ │ │ │ │ ├── APIArchivedActivityTest.java │ │ │ │ │ │ ├── APIArchivedFlowNodeTest.java │ │ │ │ │ │ ├── APIArchivedHumanTaskTest.java │ │ │ │ │ │ ├── APIArchivedUserTaskTest.java │ │ │ │ │ │ ├── ArchivedActivityVariableControllerTest.java │ │ │ │ │ │ └── ArchivedUserTaskContextControllerTest.java │ │ │ │ │ ├── message/ │ │ │ │ │ │ └── BPMMessageControllerTest.java │ │ │ │ │ ├── process/ │ │ │ │ │ │ ├── APIProcessConnectorDependencyTest.java │ │ │ │ │ │ ├── APIProcessConnectorTest.java │ │ │ │ │ │ ├── APIProcessTest.java │ │ │ │ │ │ ├── ProcessContractControllerTest.java │ │ │ │ │ │ ├── ProcessDefinitionDesignControllerTest.java │ │ │ │ │ │ └── ProcessInstantiationControllerTest.java │ │ │ │ │ └── signal/ │ │ │ │ │ └── BPMSignalControllerTest.java │ │ │ │ ├── deployer/ │ │ │ │ │ └── UserDeployerTest.java │ │ │ │ ├── document/ │ │ │ │ │ ├── APIDocumentTest.java │ │ │ │ │ └── api/ │ │ │ │ │ └── impl/ │ │ │ │ │ └── DocumentDatastoreTest.java │ │ │ │ ├── extension/ │ │ │ │ │ ├── ApiExtensionControllerTest.java │ │ │ │ │ └── ResourceExtensionResolverTest.java │ │ │ │ ├── form/ │ │ │ │ │ ├── FormMappingControllerTest.java │ │ │ │ │ └── FormMappingItemTest.java │ │ │ │ ├── organization/ │ │ │ │ │ ├── APICustomUserInfoDefinitionTest.java │ │ │ │ │ ├── APICustomUserInfoUserTest.java │ │ │ │ │ ├── APICustomUserInfoValueTest.java │ │ │ │ │ ├── APIUserTest.java │ │ │ │ │ ├── CustomUserInfoConverterTest.java │ │ │ │ │ ├── EngineCustomUserInfoDefinition.java │ │ │ │ │ ├── TestValidator.java │ │ │ │ │ └── password/ │ │ │ │ │ └── validator/ │ │ │ │ │ ├── DefaultPasswordValidatorTest.java │ │ │ │ │ └── RobustnessPasswordValidatorTest.java │ │ │ │ ├── platform/ │ │ │ │ │ └── SystemInformationControllerTest.java │ │ │ │ ├── resource/ │ │ │ │ │ └── ErrorMessageTest.java │ │ │ │ ├── system/ │ │ │ │ │ ├── BonitaVersionTest.java │ │ │ │ │ ├── I18nTranslationControllerTest.java │ │ │ │ │ └── MaintenanceControllerTest.java │ │ │ │ └── tenant/ │ │ │ │ └── TenantResourceItemTest.java │ │ │ ├── datastore/ │ │ │ │ ├── CommonDatastoreTest.java │ │ │ │ ├── FakeCommonDatastore.java │ │ │ │ ├── application/ │ │ │ │ │ ├── ApplicationDataStoreTest.java │ │ │ │ │ ├── ApplicationItemConverterTest.java │ │ │ │ │ └── ApplicationSearchDescriptorConverterTest.java │ │ │ │ ├── applicationmenu/ │ │ │ │ │ ├── ApplicationMenuDataStoreTest.java │ │ │ │ │ ├── ApplicationMenuFilterCreatorTest.java │ │ │ │ │ ├── ApplicationMenuItemConverterTest.java │ │ │ │ │ └── ApplicationMenuSearchDescriptorConverterTest.java │ │ │ │ ├── applicationpage/ │ │ │ │ │ ├── ApplicationPageDataStoreTest.java │ │ │ │ │ ├── ApplicationPageFilterCreatorTest.java │ │ │ │ │ └── ApplicationPageSearchDescriptorConverterTest.java │ │ │ │ ├── bpm/ │ │ │ │ │ ├── cases/ │ │ │ │ │ │ ├── ArchivedCaseDatastoreTest.java │ │ │ │ │ │ ├── ArchivedCaseDocumentDatastoreTest.java │ │ │ │ │ │ ├── ArchivedCaseDocumentItemConverterTest.java │ │ │ │ │ │ ├── CaseDatastoreTest.java │ │ │ │ │ │ ├── CaseDocumentDatastoreTest.java │ │ │ │ │ │ ├── CaseDocumentItemConverterTest.java │ │ │ │ │ │ ├── CaseItemConverterTest.java │ │ │ │ │ │ └── CaseVariableDatastoreTest.java │ │ │ │ │ ├── connector/ │ │ │ │ │ │ ├── ConnectorInstanceDatastoreTest.java │ │ │ │ │ │ └── ConnectorInstanceItemWrapperTest.java │ │ │ │ │ ├── flownode/ │ │ │ │ │ │ ├── AbstractFlowNodeDatastoreTest.java │ │ │ │ │ │ ├── AbstractHumanTaskDatastoreTest.java │ │ │ │ │ │ ├── FlowNodeDatastoreTest.java │ │ │ │ │ │ ├── TaskFinderTest.java │ │ │ │ │ │ └── archive/ │ │ │ │ │ │ ├── AbstractArchivedFlowNodeDatastoreTest.java │ │ │ │ │ │ ├── ArchivedActivityDatastoreTest.java │ │ │ │ │ │ ├── ArchivedHumanTaskDatastoreTest.java │ │ │ │ │ │ └── ArchivedUserTaskDatastoreTest.java │ │ │ │ │ └── process/ │ │ │ │ │ ├── CategoryDatastoreTest.java │ │ │ │ │ ├── ProcessCategoryDatastoreTest.java │ │ │ │ │ ├── ProcessConnectorDatastoreTest.java │ │ │ │ │ ├── ProcessConnectorDependencyDatastoreTest.java │ │ │ │ │ ├── ProcessDatastoreTest.java │ │ │ │ │ └── helper/ │ │ │ │ │ └── ProcessItemConverterTest.java │ │ │ │ ├── converter/ │ │ │ │ │ ├── ItemSearchResultConverterTest.java │ │ │ │ │ ├── LongValueConverterTest.java │ │ │ │ │ └── StringValueConverterTest.java │ │ │ │ ├── filter/ │ │ │ │ │ ├── FieldTest.java │ │ │ │ │ ├── FilterAccessorTest.java │ │ │ │ │ ├── FilterTest.java │ │ │ │ │ ├── FiltersTest.java │ │ │ │ │ ├── GenericFilterCreatorTest.java │ │ │ │ │ ├── LongValueTest.java │ │ │ │ │ ├── StrValueTest.java │ │ │ │ │ └── ValueTest.java │ │ │ │ ├── organization/ │ │ │ │ │ ├── GroupUpdaterConverterTest.java │ │ │ │ │ ├── RoleDatastoreTest.java │ │ │ │ │ ├── UserDatastoreTest.java │ │ │ │ │ └── UserSearchAttributeConverterTest.java │ │ │ │ ├── page/ │ │ │ │ │ ├── CustomPageContentValidatorTest.java │ │ │ │ │ ├── PageDatastoreTest.java │ │ │ │ │ └── PageItemConverterTest.java │ │ │ │ ├── profile/ │ │ │ │ │ ├── EngineProfileBuilder.java │ │ │ │ │ ├── GetProfileHelperTest.java │ │ │ │ │ ├── ProfileItemConverterTest.java │ │ │ │ │ ├── ProfileSearchDescriptorConverterTest.java │ │ │ │ │ ├── SearchProfilesHelperTest.java │ │ │ │ │ └── member/ │ │ │ │ │ ├── AddProfileMemberHelperTest.java │ │ │ │ │ ├── MemberTypeConverterTest.java │ │ │ │ │ ├── MemberTypeTest.java │ │ │ │ │ ├── ProfileMemberItemConverterTest.java │ │ │ │ │ ├── ProfileMemberSearchDescriptorConverterTest.java │ │ │ │ │ └── SearchProfileMembersHelperTest.java │ │ │ │ └── utils/ │ │ │ │ ├── NotSerializableObject.java │ │ │ │ ├── SearchUtils.java │ │ │ │ ├── SortTest.java │ │ │ │ ├── SortsTest.java │ │ │ │ ├── VariableMapperTest.java │ │ │ │ └── VariablesMapperTest.java │ │ │ ├── engineclient/ │ │ │ │ ├── ActivityEngineClientTest.java │ │ │ │ ├── CaseEngineClientTest.java │ │ │ │ ├── CustomUserInfoEngineClientTest.java │ │ │ │ ├── GroupEngineClientTest.java │ │ │ │ ├── ProcessEngineClientTest.java │ │ │ │ ├── TenantManagementEngineClientTest.java │ │ │ │ └── UserEngineClientTest.java │ │ │ └── framework/ │ │ │ ├── APIServletCallTest.java │ │ │ ├── json/ │ │ │ │ ├── JSonSimpleDeserializerTest.java │ │ │ │ ├── JacksonDeserializerTest.java │ │ │ │ ├── JacksonSerializerTest.java │ │ │ │ └── model/ │ │ │ │ ├── Address.java │ │ │ │ ├── ProfileImportStatusMessageFake.java │ │ │ │ └── User.java │ │ │ └── utils/ │ │ │ ├── FilePathBuilderTest.java │ │ │ ├── RestRequestURIParserTest.java │ │ │ └── converter/ │ │ │ ├── ConverterFactoryTest.java │ │ │ ├── TypeConverterTest.java │ │ │ └── typed/ │ │ │ ├── BooleanConverterTest.java │ │ │ ├── DateConverterTest.java │ │ │ ├── DoubleConverterTest.java │ │ │ ├── IntegerConverterTest.java │ │ │ ├── LongConverterTest.java │ │ │ └── StringConverterTest.java │ │ ├── server/ │ │ │ └── login/ │ │ │ └── LoginFailureTrackerAccessorTest.java │ │ └── toolkit/ │ │ ├── client/ │ │ │ ├── common/ │ │ │ │ ├── exception/ │ │ │ │ │ └── http/ │ │ │ │ │ └── JsonExceptionSerializerTest.java │ │ │ │ ├── json/ │ │ │ │ │ ├── JSonItemReaderTest.java │ │ │ │ │ ├── JSonSerializerTest.java │ │ │ │ │ └── JSonUtilTest.java │ │ │ │ └── texttemplate/ │ │ │ │ └── TextTemplateTest.java │ │ │ └── data/ │ │ │ ├── APIIDTest.java │ │ │ └── item/ │ │ │ └── attribute/ │ │ │ └── validator/ │ │ │ └── StringFormatURLValidatorTest.java │ │ └── server/ │ │ └── servlet/ │ │ └── ToolkitHttpServletTest.java │ └── resources/ │ ├── ARootPageFolder/ │ │ ├── AbstractIndex.groovy │ │ ├── Index.groovy │ │ ├── lib/ │ │ │ ├── bdm-client.jar │ │ │ ├── bdm-dao.jar │ │ │ ├── javassist-3.18.1-GA.jar │ │ │ ├── subdir/ │ │ │ │ └── resource.properties │ │ │ └── util.jar │ │ └── org/ │ │ └── company/ │ │ └── test/ │ │ ├── Util.groovy │ │ └── config.properties │ ├── Index.groovy │ ├── IndexRestApi.groovy │ ├── bdmDependencies/ │ │ ├── bdm-client.jar │ │ ├── bdm-dao.jar │ │ └── javassist-3.18.1-GA.jar │ ├── compound-permissions-mapping.properties │ ├── custom-permissions-mapping.properties │ ├── custom_po_resource/ │ │ ├── added_custom_i18n_fr.po │ │ ├── test_es.po │ │ └── test_fr.po │ ├── i18n/ │ │ ├── other_translations_fr.po │ │ ├── portal_fr.po │ │ └── test_fr.po │ ├── invalidCustomPage/ │ │ ├── descriptor.properties │ │ └── resources/ │ │ └── style.css │ ├── invalidFormPage/ │ │ ├── page.properties │ │ └── resources/ │ │ └── index.js │ ├── invalidThemePage/ │ │ ├── page.properties │ │ └── resources/ │ │ └── style.css │ ├── logback-test.xml │ ├── myRestAPI-1.0.0-SNAPSHOT/ │ │ ├── lib/ │ │ │ └── myRestAPI-1.0.0-SNAPSHOT.jar │ │ └── page.properties │ ├── org/ │ │ └── bonitasoft/ │ │ ├── console/ │ │ │ └── common/ │ │ │ └── server/ │ │ │ └── page/ │ │ │ ├── file.css │ │ │ ├── my_html_page_v_6/ │ │ │ │ └── index.html │ │ │ ├── my_html_page_v_7/ │ │ │ │ └── resources/ │ │ │ │ └── index.html │ │ │ └── page.properties │ │ └── web/ │ │ └── rest/ │ │ └── server/ │ │ └── api/ │ │ ├── bpm/ │ │ │ ├── flownode/ │ │ │ │ ├── contract.json │ │ │ │ ├── timerEventTriggerInstance.json │ │ │ │ └── timerEventTriggerInstances.json │ │ │ └── process/ │ │ │ └── contract.json │ │ └── extension/ │ │ └── page.properties │ ├── resources-permissions-mapping.properties │ └── test.po ├── build.gradle ├── buildSrc/ │ ├── build.gradle │ ├── settings.gradle │ └── src/ │ └── main/ │ └── groovy/ │ └── org/ │ └── bonitasoft/ │ └── engine/ │ └── gradle/ │ ├── HttpTestPlugin.groovy │ ├── JVMModifier.groovy │ ├── PomUtils.groovy │ ├── SetupE2ETask.groovy │ ├── ShadeDependency.groovy │ ├── ShadeExtension.groovy │ ├── ShadePlugin.groovy │ ├── TestsExtension.groovy │ ├── TestsPlugin.groovy │ └── docker/ │ ├── DatabaseExtraConfiguration.groovy │ ├── DatabasePluginExtension.groovy │ ├── DbParser.groovy │ ├── DockerDatabaseContainerTasksCreator.groovy │ └── DockerDatabasePlugin.groovy ├── common.gradle ├── engine-settings.gradle ├── gradle/ │ ├── libs.versions.toml │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── license/ │ ├── license.txt │ └── licenseHeaderDefinition.xml ├── platform/ │ ├── platform-resources/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── platform/ │ │ │ │ ├── configuration/ │ │ │ │ │ ├── ConfigurationService.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ ├── BonitaAllConfigurationContentTypeCleaner.java │ │ │ │ │ │ ├── BonitaAllConfigurationPreparedStatementSetter.java │ │ │ │ │ │ ├── BonitaConfigurationCleaner.java │ │ │ │ │ │ ├── BonitaConfigurationContentTypeCleaner.java │ │ │ │ │ │ ├── BonitaConfigurationPreparedStatementCleaner.java │ │ │ │ │ │ ├── BonitaConfigurationPreparedStatementSetter.java │ │ │ │ │ │ ├── BonitaConfigurationRowMapper.java │ │ │ │ │ │ ├── BonitaConfigurationTenantUpdater.java │ │ │ │ │ │ ├── ConfigurationColumns.java │ │ │ │ │ │ ├── ConfigurationFields.java │ │ │ │ │ │ ├── ConfigurationServiceImpl.java │ │ │ │ │ │ ├── FolderResolver.java │ │ │ │ │ │ └── FullBonitaConfigurationRowMapper.java │ │ │ │ │ ├── model/ │ │ │ │ │ │ ├── BonitaConfiguration.java │ │ │ │ │ │ ├── FullBonitaConfiguration.java │ │ │ │ │ │ └── LightBonitaConfiguration.java │ │ │ │ │ ├── type/ │ │ │ │ │ │ └── ConfigurationType.java │ │ │ │ │ └── util/ │ │ │ │ │ ├── AllConfigurationResourceVisitor.java │ │ │ │ │ ├── AutoUpdateConfigurationVisitor.java │ │ │ │ │ ├── CleanAndStoreAllConfigurationInTransaction.java │ │ │ │ │ ├── CleanAndStoreConfigurationInTransaction.java │ │ │ │ │ ├── ConfigurationResourceVisitor.java │ │ │ │ │ ├── DeleteAllConfigurationInTransaction.java │ │ │ │ │ ├── FlattenFolderVisitor.java │ │ │ │ │ ├── GetAllConfigurationInTransaction.java │ │ │ │ │ ├── GetConfigurationInTransaction.java │ │ │ │ │ ├── GetConfigurationsInTransaction.java │ │ │ │ │ ├── GetMandatoryStructureConfiguration.java │ │ │ │ │ ├── LicensesResourceVisitor.java │ │ │ │ │ ├── StoreConfigurationInTransaction.java │ │ │ │ │ ├── StoreConfigurationsIfNotExist.java │ │ │ │ │ └── UpdateConfigurationInTransaction.java │ │ │ │ ├── database/ │ │ │ │ │ └── DatabaseVendor.java │ │ │ │ ├── exception/ │ │ │ │ │ └── PlatformException.java │ │ │ │ ├── setup/ │ │ │ │ │ ├── PlatformSetup.java │ │ │ │ │ ├── PlatformSetupAccessor.java │ │ │ │ │ ├── ScriptExecutor.java │ │ │ │ │ └── SimpleEncryptor.java │ │ │ │ └── version/ │ │ │ │ ├── VersionService.java │ │ │ │ └── impl/ │ │ │ │ └── VersionServiceImpl.java │ │ │ └── resources/ │ │ │ ├── PLATFORM_ENGINE_VERSION │ │ │ ├── platform_engine/ │ │ │ │ └── bonita-platform-custom.xml │ │ │ ├── platform_portal/ │ │ │ │ └── security-config.properties │ │ │ ├── sql/ │ │ │ │ ├── h2/ │ │ │ │ │ ├── cleanTables.sql │ │ │ │ │ ├── createQuartzTables.sql │ │ │ │ │ ├── createTables.sql │ │ │ │ │ ├── dropQuartzTables.sql │ │ │ │ │ ├── dropTables.sql │ │ │ │ │ ├── initTables.sql │ │ │ │ │ └── preDropStructure.sql │ │ │ │ └── postgres/ │ │ │ │ ├── cleanTables.sql │ │ │ │ ├── createQuartzTables.sql │ │ │ │ ├── createTables.sql │ │ │ │ ├── dropQuartzTables.sql │ │ │ │ ├── dropTables.sql │ │ │ │ ├── initTables.sql │ │ │ │ └── preDropStructure.sql │ │ │ ├── tenant_engine/ │ │ │ │ └── bonita-tenants-custom.xml │ │ │ └── tenant_portal/ │ │ │ ├── compound-permissions-mapping-custom.properties │ │ │ ├── compound-permissions-mapping-internal.properties │ │ │ ├── compound-permissions-mapping.properties │ │ │ ├── console-config.properties │ │ │ ├── custom-permissions-mapping.properties │ │ │ ├── resources-permissions-mapping-custom.properties │ │ │ ├── resources-permissions-mapping-internal.properties │ │ │ ├── resources-permissions-mapping.properties │ │ │ └── security-config.properties │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── platform/ │ │ │ ├── configuration/ │ │ │ │ ├── impl/ │ │ │ │ │ ├── BonitaConfigurationRowMapperTest.java │ │ │ │ │ ├── ConfigurationServiceImplTest.java │ │ │ │ │ └── FolderResolverTest.java │ │ │ │ ├── model/ │ │ │ │ │ ├── BonitaConfigurationAssert.java │ │ │ │ │ ├── BonitaConfigurationTest.java │ │ │ │ │ └── FullBonitaConfigurationTest.java │ │ │ │ └── util/ │ │ │ │ ├── AllConfigurationResourceVisitorTest.java │ │ │ │ ├── AutoUpdateConfigurationVisitorTest.java │ │ │ │ ├── ConfigurationResourceVisitorTest.java │ │ │ │ └── LicensesResourceVisitorTest.java │ │ │ └── version/ │ │ │ └── impl/ │ │ │ └── VersionServiceImplTest.java │ │ └── resources/ │ │ ├── allConfiguration/ │ │ │ ├── licenses/ │ │ │ │ └── license1.lic │ │ │ ├── platform_engine/ │ │ │ │ └── bonita-platform-community.properties │ │ │ ├── platform_portal/ │ │ │ │ └── security-config.properties │ │ │ ├── tenant_template_engine/ │ │ │ │ └── bonita-tenant-community.properties │ │ │ ├── tenant_template_security_scripts/ │ │ │ │ └── SamplePermissionRule.groovy.sample │ │ │ └── tenants/ │ │ │ └── 456/ │ │ │ ├── tenant_engine/ │ │ │ │ └── bonita-tenant-community.properties │ │ │ ├── tenant_portal/ │ │ │ │ └── compound-permissions-mapping.properties │ │ │ └── tenant_security_scripts/ │ │ │ └── SamplePermissionRule.groovy.sample │ │ └── conf/ │ │ ├── bonita-platform-community.properties │ │ └── bonita-platform-custom.xml │ ├── platform-setup/ │ │ ├── .gitignore │ │ ├── bin/ │ │ │ └── .gitignore │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── platform/ │ │ │ │ └── setup/ │ │ │ │ ├── ConfigurationChecker.java │ │ │ │ ├── PlatformSetupApplication.java │ │ │ │ ├── command/ │ │ │ │ │ ├── CommandException.java │ │ │ │ │ ├── CommandUtils.java │ │ │ │ │ ├── HelpCommand.java │ │ │ │ │ ├── InitCommand.java │ │ │ │ │ ├── PlatformSetupCommand.java │ │ │ │ │ ├── PullCommand.java │ │ │ │ │ ├── PushCommand.java │ │ │ │ │ └── configure/ │ │ │ │ │ ├── BundleConfigurator.java │ │ │ │ │ ├── BundleResolver.java │ │ │ │ │ ├── ConfigureCommand.java │ │ │ │ │ ├── DatabaseConfiguration.java │ │ │ │ │ ├── PropertyLoader.java │ │ │ │ │ ├── PropertyReader.java │ │ │ │ │ └── TomcatBundleConfigurator.java │ │ │ │ ├── dbconfig/ │ │ │ │ │ └── DataSourceConfig.java │ │ │ │ └── jndi/ │ │ │ │ ├── MemoryJNDISetup.java │ │ │ │ ├── SimpleMemoryContext.java │ │ │ │ ├── SimpleMemoryContextFactory.java │ │ │ │ └── SimpleNameParser.java │ │ │ ├── resources/ │ │ │ │ ├── application.properties │ │ │ │ ├── banner.txt │ │ │ │ ├── configure_footer.txt │ │ │ │ ├── configure_header.txt │ │ │ │ ├── global_usage_footer.txt │ │ │ │ ├── global_usage_header.txt │ │ │ │ ├── init_footer.txt │ │ │ │ ├── init_header.txt │ │ │ │ ├── pull.txt │ │ │ │ └── push.txt │ │ │ └── standalone/ │ │ │ ├── README.md │ │ │ ├── database.properties │ │ │ ├── internal.properties │ │ │ ├── lib/ │ │ │ │ └── PUT_YOUR_JDBC_DRIVER_HERE │ │ │ ├── logback.xml │ │ │ ├── setup.bat │ │ │ ├── setup.sh │ │ │ └── tomcat-templates/ │ │ │ ├── bonita.xml │ │ │ ├── setenv.bat │ │ │ └── setenv.sh │ │ └── test/ │ │ ├── e2e/ │ │ │ ├── clean-postgres.sh │ │ │ ├── e2e-distrib.sh │ │ │ └── e2e-postgres-bos.sh │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── platform/ │ │ │ ├── configuration/ │ │ │ │ ├── impl/ │ │ │ │ │ └── ConfigurationServiceImplIT.java │ │ │ │ └── util/ │ │ │ │ └── FolderComparator.java │ │ │ ├── setup/ │ │ │ │ ├── ConfigurationCheckerTest.java │ │ │ │ ├── PlatformSetupApplicationTest.java │ │ │ │ ├── PlatformSetupDistributionIT.java │ │ │ │ ├── PlatformSetupIT.java │ │ │ │ ├── PlatformSetupTest.java │ │ │ │ ├── ScriptExecutorIT.java │ │ │ │ ├── ScriptExecutorTest.java │ │ │ │ └── command/ │ │ │ │ ├── CommandTestUtils.java │ │ │ │ ├── HelpCommandTest.java │ │ │ │ ├── InitCommandTest.java │ │ │ │ ├── PullCommandTest.java │ │ │ │ ├── PushCommandTest.java │ │ │ │ └── configure/ │ │ │ │ ├── BundleConfiguratorTest.java │ │ │ │ ├── ConfigureCommandTest.java │ │ │ │ ├── DatabaseConfigurationTest.java │ │ │ │ ├── PropertyReaderTest.java │ │ │ │ └── TomcatBundleConfiguratorTest.java │ │ │ ├── util/ │ │ │ │ └── ConfigurationFolderUtilTest.java │ │ │ └── version/ │ │ │ └── impl/ │ │ │ └── VersionServiceImplIT.java │ │ └── resources/ │ │ ├── ConfigurationChecker/ │ │ │ └── lib/ │ │ │ └── postgres_drivers_1.2.3.4.jar │ │ ├── ConfigurationChecker_KO/ │ │ │ └── lib/ │ │ │ └── otherJar.jar │ │ ├── conf/ │ │ │ ├── bonita-platform-community.properties │ │ │ └── bonita-platform-custom.xml │ │ ├── database.properties │ │ ├── datasource-config/ │ │ │ ├── database.properties │ │ │ ├── database_with_space_values.properties │ │ │ ├── incomplete_database.properties │ │ │ ├── incomplete_internal.properties │ │ │ ├── internal.properties │ │ │ └── missingDriverClass_internal.properties │ │ ├── internal.properties │ │ ├── logback.xml │ │ └── tomcat_conf/ │ │ ├── server/ │ │ │ ├── bin/ │ │ │ │ ├── catalina.sh │ │ │ │ ├── setenv.bat │ │ │ │ └── setenv.sh │ │ │ ├── conf/ │ │ │ │ └── Catalina/ │ │ │ │ └── localhost/ │ │ │ │ └── bonita.xml │ │ │ └── lib/ │ │ │ └── bonita/ │ │ │ ├── .gitignore │ │ │ └── h2.jar │ │ └── setup/ │ │ ├── lib/ │ │ │ ├── drivers-h2-2.12.117.jar │ │ │ ├── ojdbc-6.jar │ │ │ └── postgres9.2-drivers.jar │ │ └── tomcat-templates/ │ │ ├── bonita.xml │ │ ├── setenv.bat │ │ └── setenv.sh │ └── platform-setup-test/ │ ├── build.gradle │ └── src/ │ └── main/ │ └── java/ │ └── org/ │ └── bonitasoft/ │ └── platform/ │ ├── setup/ │ │ └── PlatformSetupTestUtils.java │ └── util/ │ └── ConfigurationFolderUtil.java ├── services/ │ ├── bonita-archive/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── archive/ │ │ │ ├── ArchiveInsertRecord.java │ │ │ ├── ArchiveService.java │ │ │ ├── ArchivingStrategy.java │ │ │ └── impl/ │ │ │ ├── AbstractArchivingStrategy.java │ │ │ ├── ArchiveServiceImpl.java │ │ │ ├── BatchArchiveCallable.java │ │ │ └── DefaultArchivingStrategy.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── archive/ │ │ └── impl/ │ │ ├── ArchiveServiceImplTest.java │ │ └── BatchArchiveCallableTest.java │ ├── bonita-authentication/ │ │ ├── build.gradle │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── authentication/ │ │ ├── AuthenticationConstants.java │ │ ├── AuthenticationException.java │ │ ├── GenericAuthenticationService.java │ │ ├── GenericAuthenticationServiceAccessor.java │ │ └── impl/ │ │ └── AuthenticationServiceImpl.java │ ├── bonita-authorization/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── authorization/ │ │ │ │ ├── PermissionService.java │ │ │ │ ├── PermissionsBuilder.java │ │ │ │ └── properties/ │ │ │ │ ├── CompoundPermissionsMapping.java │ │ │ │ ├── ConfigurationFile.java │ │ │ │ ├── ConfigurationFilesManager.java │ │ │ │ ├── CustomPermissionsMapping.java │ │ │ │ ├── DynamicPermissionsChecks.java │ │ │ │ ├── PropertiesWithSet.java │ │ │ │ └── ResourcesPermissionsMapping.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── authorization/ │ │ │ └── properties/ │ │ │ └── dynamic-permissions-checks.properties │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── authorization/ │ │ │ ├── PermissionsBuilderTest.java │ │ │ └── properties/ │ │ │ ├── ConfigurationFileTest.java │ │ │ ├── ConfigurationFilesManagerTest.java │ │ │ └── ResourcesPermissionsMappingTest.java │ │ └── resources/ │ │ ├── compound-permissions-mapping.properties │ │ ├── custom-permissions-mapping.properties │ │ └── resources-permissions-mapping.properties │ ├── bonita-builder/ │ │ ├── build.gradle │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── builder/ │ │ │ └── BuilderFactory.java │ │ └── resources/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── builder/ │ │ └── builder-factories.properties │ ├── bonita-business-application/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── business/ │ │ │ │ └── application/ │ │ │ │ ├── ApplicationService.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── ApplicationServiceImpl.java │ │ │ │ │ ├── HomePageChecker.java │ │ │ │ │ ├── IndexManager.java │ │ │ │ │ ├── IndexUpdater.java │ │ │ │ │ ├── MenuIndex.java │ │ │ │ │ ├── MenuIndexValidator.java │ │ │ │ │ ├── cleaner/ │ │ │ │ │ │ ├── ApplicationDestructor.java │ │ │ │ │ │ ├── ApplicationMenuCleaner.java │ │ │ │ │ │ ├── ApplicationMenuDestructor.java │ │ │ │ │ │ └── ApplicationPageDestructor.java │ │ │ │ │ ├── converter/ │ │ │ │ │ │ └── MenuIndexConverter.java │ │ │ │ │ └── filter/ │ │ │ │ │ ├── ApplicationPageRelatedMenusFilterBuilder.java │ │ │ │ │ ├── ApplicationRelatedMenusFilterBuilder.java │ │ │ │ │ ├── ChildrenMenusFilterBuilder.java │ │ │ │ │ ├── FilterBuilder.java │ │ │ │ │ └── SelectRange.java │ │ │ │ └── model/ │ │ │ │ ├── AbstractSApplication.java │ │ │ │ ├── SApplication.java │ │ │ │ ├── SApplicationMenu.java │ │ │ │ ├── SApplicationPage.java │ │ │ │ ├── SApplicationState.java │ │ │ │ ├── SApplicationWithIcon.java │ │ │ │ └── builder/ │ │ │ │ ├── SApplicationLogBuilder.java │ │ │ │ ├── SApplicationLogBuilderFactory.java │ │ │ │ ├── SApplicationMenuLogBuilder.java │ │ │ │ ├── SApplicationMenuLogBuilderFactory.java │ │ │ │ ├── SApplicationMenuUpdateBuilder.java │ │ │ │ ├── SApplicationMenuUpdateBuilderFactory.java │ │ │ │ ├── SApplicationPageLogBuilder.java │ │ │ │ ├── SApplicationPageLogBuilderFactory.java │ │ │ │ ├── SApplicationUpdateBuilder.java │ │ │ │ └── impl/ │ │ │ │ ├── SApplicationLogBuilderFactoryImpl.java │ │ │ │ ├── SApplicationLogBuilderImpl.java │ │ │ │ ├── SApplicationMenuLogBuilderFactoryImpl.java │ │ │ │ ├── SApplicationMenuLogBuilderImpl.java │ │ │ │ ├── SApplicationPageLogBuilderFactoryImpl.java │ │ │ │ └── SApplicationPageLogBuilderImpl.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── business/ │ │ │ └── application/ │ │ │ └── impl/ │ │ │ └── hibernate/ │ │ │ └── application.queries.hbm.xml │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── business/ │ │ └── application/ │ │ ├── impl/ │ │ │ ├── ApplicationServiceImplTest.java │ │ │ ├── HomePageCheckerTest.java │ │ │ ├── IndexManagerTest.java │ │ │ ├── IndexUpdaterTest.java │ │ │ ├── MenuIndexValidatorTest.java │ │ │ ├── cleaner/ │ │ │ │ ├── ApplicationDestructorTest.java │ │ │ │ ├── ApplicationMenuCleanerTest.java │ │ │ │ ├── ApplicationMenuDestructorTest.java │ │ │ │ └── ApplicationPageDestructorTest.java │ │ │ ├── converter/ │ │ │ │ └── MenuIndexConverterTest.java │ │ │ └── filter/ │ │ │ ├── ApplicationPageRelatedMenusFilterBuilderTest.java │ │ │ ├── ApplicationRelatedMenusFilterBuilderTest.java │ │ │ ├── ChildrenMenusFilterBuilderTest.java │ │ │ └── SelectRangeTest.java │ │ └── model/ │ │ ├── SApplicationWithIconTest.java │ │ └── builder/ │ │ └── SApplicationUpdateBuilderTest.java │ ├── bonita-business-data/ │ │ ├── bonita-business-data-api/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ └── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── business/ │ │ │ │ └── data/ │ │ │ │ ├── BusinessDataModelRepository.java │ │ │ │ ├── BusinessDataRepository.java │ │ │ │ ├── BusinessDataService.java │ │ │ │ ├── DataRetentionBdmTrackingService.java │ │ │ │ ├── JsonBusinessDataSerializer.java │ │ │ │ ├── NonUniqueResultException.java │ │ │ │ ├── SBusinessDataCrudOperationException.java │ │ │ │ ├── SBusinessDataNotFoundException.java │ │ │ │ ├── SBusinessDataRepositoryDeploymentException.java │ │ │ │ ├── SBusinessDataRepositoryException.java │ │ │ │ ├── SBusinessDataRepositorySerializationException.java │ │ │ │ ├── SDataRetentionBdmTrackingException.java │ │ │ │ ├── SchemaManager.java │ │ │ │ └── proxy/ │ │ │ │ ├── Capitalizer.java │ │ │ │ ├── EntityGetter.java │ │ │ │ ├── ServerLazyLoader.java │ │ │ │ └── ServerProxyfier.java │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── business/ │ │ │ └── data/ │ │ │ └── proxy/ │ │ │ ├── A.java │ │ │ ├── Address.java │ │ │ ├── B.java │ │ │ ├── CapitalizerTest.java │ │ │ ├── Employee.java │ │ │ ├── EntityGetterTest.java │ │ │ ├── PersonEntity.java │ │ │ ├── ServerLazyLoaderTest.java │ │ │ └── ServerProxyfierTest.java │ │ ├── bonita-business-data-client-resources/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ └── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── bdm/ │ │ │ │ └── dao/ │ │ │ │ └── client/ │ │ │ │ └── resources/ │ │ │ │ ├── BusinessObjectDeserializer.java │ │ │ │ ├── proxy/ │ │ │ │ │ ├── LazyLoader.java │ │ │ │ │ └── Proxyfier.java │ │ │ │ └── utils/ │ │ │ │ ├── BDMQueryCommandParameters.java │ │ │ │ ├── Capitalizer.java │ │ │ │ └── EntityGetter.java │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ ├── bonita/ │ │ │ │ └── pojo/ │ │ │ │ ├── AddressForTesting.java │ │ │ │ └── EmployeeForTesting.java │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── bdm/ │ │ │ ├── dao/ │ │ │ │ └── client/ │ │ │ │ └── resources/ │ │ │ │ ├── BusinessObjectDeserializerTest.java │ │ │ │ ├── proxy/ │ │ │ │ │ ├── LazyLoaderTest.java │ │ │ │ │ ├── ProxyTest.java │ │ │ │ │ └── ProxyfierTest.java │ │ │ │ └── utils/ │ │ │ │ ├── BDMQueryCommandParametersTest.java │ │ │ │ ├── CapitalizerTest.java │ │ │ │ └── EntityGetterTest.java │ │ │ └── proxy/ │ │ │ ├── assertion/ │ │ │ │ └── ProxyAssert.java │ │ │ └── model/ │ │ │ ├── Address.java │ │ │ ├── Child.java │ │ │ ├── Employee.java │ │ │ ├── Parent.java │ │ │ └── TestEntity.java │ │ ├── bonita-business-data-generator/ │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── org/ │ │ │ │ │ └── bonitasoft/ │ │ │ │ │ └── engine/ │ │ │ │ │ └── business/ │ │ │ │ │ └── data/ │ │ │ │ │ └── generator/ │ │ │ │ │ ├── AbstractBDMCodeGenerator.java │ │ │ │ │ ├── AbstractBDMJarBuilder.java │ │ │ │ │ ├── BDMJarGenerationException.java │ │ │ │ │ ├── CodeGenerationException.java │ │ │ │ │ ├── CodeGenerator.java │ │ │ │ │ ├── DateAndTimeConverter.java │ │ │ │ │ ├── DateConverter.java │ │ │ │ │ ├── EntityCodeGenerator.java │ │ │ │ │ ├── ForeignKeyAnnotator.java │ │ │ │ │ ├── JExprHelper.java │ │ │ │ │ ├── OffsetDateTimeConverter.java │ │ │ │ │ ├── PersistenceUnitBuilder.java │ │ │ │ │ ├── RelationFieldAnnotator.java │ │ │ │ │ ├── client/ │ │ │ │ │ │ ├── ClientBDMCodeGenerator.java │ │ │ │ │ │ ├── ClientBDMJarBuilder.java │ │ │ │ │ │ └── ResourcesLoader.java │ │ │ │ │ ├── compiler/ │ │ │ │ │ │ ├── CompilationException.java │ │ │ │ │ │ ├── DummyCompilationProgress.java │ │ │ │ │ │ └── JDTCompiler.java │ │ │ │ │ ├── filter/ │ │ │ │ │ │ ├── OnlyDAOImplementationFileFilter.java │ │ │ │ │ │ └── WithoutDAOImplementationFileFilter.java │ │ │ │ │ └── server/ │ │ │ │ │ ├── ServerBDMCodeGenerator.java │ │ │ │ │ └── ServerBDMJarBuilder.java │ │ │ │ └── resources/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── business/ │ │ │ │ └── data/ │ │ │ │ └── generator/ │ │ │ │ └── persistence.xml │ │ │ └── test/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── business/ │ │ │ │ └── data/ │ │ │ │ └── generator/ │ │ │ │ ├── AbstractBDMCodeGeneratorTest.java │ │ │ │ ├── BOMBuilder.java │ │ │ │ ├── CodeGeneratorTest.java │ │ │ │ ├── CompilableCode.java │ │ │ │ ├── DateAndTimeConverterTest.java │ │ │ │ ├── DateConverterTest.java │ │ │ │ ├── EntityCodeGeneratorTest.java │ │ │ │ ├── EntityPojo.java │ │ │ │ ├── ForeignKeyAnnotatorTest.java │ │ │ │ ├── OffsetDateTimeConverterTest.java │ │ │ │ ├── PersistenceUnitBuilderTest.java │ │ │ │ ├── RelationFieldAnnotatorTest.java │ │ │ │ ├── client/ │ │ │ │ │ ├── ClientBDMCodeGeneratorTest.java │ │ │ │ │ ├── ClientBDMJarBuilderTest.java │ │ │ │ │ └── ResourcesLoaderTest.java │ │ │ │ ├── compiler/ │ │ │ │ │ └── JDTCompilerTest.java │ │ │ │ ├── filter/ │ │ │ │ │ ├── OnlyDAOImplementationFileFilterTest.java │ │ │ │ │ └── WithoutDAOImplementationFileFilterTest.java │ │ │ │ └── server/ │ │ │ │ ├── ServerBDMCodeGeneratorTest.java │ │ │ │ └── ServerBDMJarBuilderTest.java │ │ │ └── resources/ │ │ │ ├── logback-test.xml │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ ├── business/ │ │ │ │ └── data/ │ │ │ │ └── generator/ │ │ │ │ ├── client/ │ │ │ │ │ ├── AddressDAOImplWithLazyReferenceOnEmployee.java.txt │ │ │ │ │ ├── AddressDAOWithLazyReferenceOnEmployee.java.txt │ │ │ │ │ ├── Employee.java.txt │ │ │ │ │ ├── EmployeeListAggregation.java.txt │ │ │ │ │ ├── EmployeeListComposition.java.txt │ │ │ │ │ ├── EmployeeSimpleAggregation.java.txt │ │ │ │ │ ├── EmployeeSimpleComposition.java.txt │ │ │ │ │ ├── ForecastList.java.txt │ │ │ │ │ ├── PersonneDAOImpl.java.txt │ │ │ │ │ └── Skill.java.txt │ │ │ │ ├── compiler/ │ │ │ │ │ ├── CannotBeResolvedToATypeError.java │ │ │ │ │ ├── CompilableOne.java │ │ │ │ │ ├── CompilableTwo.java │ │ │ │ │ ├── DependenciesNeeded.java │ │ │ │ │ ├── employee.java │ │ │ │ │ └── external-lib.jar │ │ │ │ └── server/ │ │ │ │ └── ServerEmployee.java │ │ │ └── package/ │ │ │ ├── Test.java │ │ │ └── subfolder/ │ │ │ └── Other.java │ │ └── bonita-business-data-impl/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ ├── bdm/ │ │ │ │ │ ├── DateAndTimeConverter.java │ │ │ │ │ ├── DateConverter.java │ │ │ │ │ └── OffsetDateTimeConverter.java │ │ │ │ └── business/ │ │ │ │ └── data/ │ │ │ │ ├── DataRetentionBdmTrackingRepository.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── BdmFieldTypeConverter.java │ │ │ │ │ ├── BusinessDataModelRepositoryImpl.java │ │ │ │ │ ├── BusinessDataReloader.java │ │ │ │ │ ├── BusinessDataRepositoryEventAspect.java │ │ │ │ │ ├── BusinessDataServiceImpl.java │ │ │ │ │ ├── CountQueryProvider.java │ │ │ │ │ ├── DataRetentionBdmTrackingRepositoryImpl.java │ │ │ │ │ ├── DataRetentionBdmTrackingServiceImpl.java │ │ │ │ │ ├── EntityManagerFactoryAware.java │ │ │ │ │ ├── JPABusinessDataRepositoryImpl.java │ │ │ │ │ ├── JsonBusinessDataSerializerImpl.java │ │ │ │ │ ├── ProxyCacheManager.java │ │ │ │ │ ├── RemoveEntityManagerSynchronization.java │ │ │ │ │ ├── SchemaManagerReadOnly.java │ │ │ │ │ ├── SchemaManagerUpdate.java │ │ │ │ │ └── jackson/ │ │ │ │ │ ├── EntityBeanSerializerModifier.java │ │ │ │ │ ├── EntityJacksonAnnotationIntrospector.java │ │ │ │ │ ├── EntityMixin.java │ │ │ │ │ ├── serializer/ │ │ │ │ │ │ ├── ExtraPropertyStringAbstractSerializer.java │ │ │ │ │ │ ├── ExtraPropertyStringListSerializer.java │ │ │ │ │ │ └── ExtraPropertyStringSerializer.java │ │ │ │ │ ├── utils/ │ │ │ │ │ │ ├── ExtraPropertyUtils.java │ │ │ │ │ │ ├── Link.java │ │ │ │ │ │ └── LinkUtils.java │ │ │ │ │ └── writer/ │ │ │ │ │ ├── ExtraBeanPropertyWriter.java │ │ │ │ │ ├── IgnoredPropertyWriter.java │ │ │ │ │ └── LinkPropertyWriter.java │ │ │ │ └── model/ │ │ │ │ ├── SDataRetentionBdmTracking.java │ │ │ │ ├── SDataRetentionConfig.java │ │ │ │ └── SReferenceDate.java │ │ │ └── resources/ │ │ │ ├── README.md │ │ │ ├── example-pom.xml │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── business/ │ │ │ └── data/ │ │ │ └── model/ │ │ │ └── impl/ │ │ │ └── hibernate/ │ │ │ └── data-retention-bdm-tracking.queries.hbm.xml │ │ └── test/ │ │ ├── java/ │ │ │ ├── com/ │ │ │ │ └── company/ │ │ │ │ ├── model/ │ │ │ │ │ ├── Address.java │ │ │ │ │ ├── Person.java │ │ │ │ │ ├── PersonWithDetails.java │ │ │ │ │ ├── Phone.java │ │ │ │ │ └── javassist/ │ │ │ │ │ ├── MethodHandlerImpl.java │ │ │ │ │ ├── ProxyImpl.java │ │ │ │ │ └── ProxyObjectImpl.java │ │ │ │ └── pojo/ │ │ │ │ ├── ComplexInvoice.java │ │ │ │ ├── ConstrainedItem.java │ │ │ │ ├── Employee.java │ │ │ │ ├── EmployeeBuilder.java │ │ │ │ └── Person.java │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ ├── BOMBuilder.java │ │ │ └── business/ │ │ │ └── data/ │ │ │ ├── JpaTestConfiguration.java │ │ │ └── impl/ │ │ │ ├── AddNewEmployeeThread.java │ │ │ ├── AddressHibernateProxy.java │ │ │ ├── BdmFieldTypeConverterTest.java │ │ │ ├── BusinessDataModelRepositoryImplTest.java │ │ │ ├── BusinessDataReloaderTest.java │ │ │ ├── BusinessDataRepositoryEventAspectTest.java │ │ │ ├── BusinessDataServiceImplTest.java │ │ │ ├── ConcurrencyIT.java │ │ │ ├── CountQueryProviderTest.java │ │ │ ├── DataRetentionBdmTrackingServiceImplTest.java │ │ │ ├── EntityPojo.java │ │ │ ├── EntitySerializerPojo.java │ │ │ ├── FakeHibernateProxyEntity.java │ │ │ ├── HibernateProxySerializationTest.java │ │ │ ├── JPABusinessDataRepositoryImplIT.java │ │ │ ├── JPABusinessDataRepositoryImplTest.java │ │ │ ├── JsonBusinessDataSerializerImplTest.java │ │ │ ├── PersonWithProxyAddress.java │ │ │ ├── ProxyCacheManagerTest.java │ │ │ ├── RemoveEntityManagerSynchronizationTest.java │ │ │ ├── SchemaManagerUpdateIT.java │ │ │ └── jackson/ │ │ │ └── serializer/ │ │ │ └── ExtraPropertyStringListSerializerTest.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── persistence.xml │ │ ├── datasource/ │ │ │ ├── datasource-dependency-h2.xml │ │ │ └── datasource-dependency-postgres.xml │ │ ├── logback-test.xml │ │ ├── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── business/ │ │ │ └── data/ │ │ │ └── impl/ │ │ │ ├── EntitySerializerPojo.json │ │ │ ├── multiplePerson.json │ │ │ ├── personWithDetails.json │ │ │ └── singlePerson.json │ │ └── testContext.xml │ ├── bonita-cache/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── cache/ │ │ │ ├── CacheConfiguration.java │ │ │ ├── CacheService.java │ │ │ ├── SCacheException.java │ │ │ ├── configuration/ │ │ │ │ └── CacheConfigurationBeans.java │ │ │ └── ehcache/ │ │ │ └── EhCacheCacheService.java │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── cache/ │ │ │ ├── CacheServiceTest.java │ │ │ └── ehcache/ │ │ │ └── EhCacheCacheServiceTest.java │ │ └── resources/ │ │ └── logback-test.xml │ ├── bonita-classloader/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ ├── classloader/ │ │ │ │ │ ├── BonitaClassLoader.java │ │ │ │ │ ├── BonitaClassLoaderFactory.java │ │ │ │ │ ├── ClassLoaderIdentifier.java │ │ │ │ │ ├── ClassLoaderService.java │ │ │ │ │ ├── ClassLoaderServiceImpl.java │ │ │ │ │ ├── ClassLoaderUpdater.java │ │ │ │ │ ├── ParentClassLoaderResolver.java │ │ │ │ │ ├── PlatformClassLoaderListener.java │ │ │ │ │ ├── RefreshClassLoaderTask.java │ │ │ │ │ ├── RefreshClassloaderSynchronization.java │ │ │ │ │ ├── SClassLoaderException.java │ │ │ │ │ ├── SingleClassLoaderListener.java │ │ │ │ │ └── listeners/ │ │ │ │ │ ├── ClassReflectorClearer.java │ │ │ │ │ └── JacksonCacheClearer.java │ │ │ │ └── dependency/ │ │ │ │ ├── ArtifactAccessor.java │ │ │ │ ├── DependencyService.java │ │ │ │ ├── SDependencyAlreadyExistsException.java │ │ │ │ ├── SDependencyCreationException.java │ │ │ │ ├── SDependencyDeletionException.java │ │ │ │ ├── SDependencyException.java │ │ │ │ ├── SDependencyMappingNotFoundException.java │ │ │ │ ├── SDependencyNotFoundException.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── AbstractDependencyService.java │ │ │ │ │ ├── DependencyServiceRegistrator.java │ │ │ │ │ ├── PlatformDependencyService.java │ │ │ │ │ └── TenantDependencyService.java │ │ │ │ └── model/ │ │ │ │ ├── AbstractSDependency.java │ │ │ │ ├── DependencyContent.java │ │ │ │ ├── SAbstractDependencyMapping.java │ │ │ │ ├── SDependency.java │ │ │ │ ├── SDependencyMapping.java │ │ │ │ ├── SPlatformDependency.java │ │ │ │ ├── SPlatformDependencyMapping.java │ │ │ │ ├── ScopeType.java │ │ │ │ └── builder/ │ │ │ │ ├── SDependencyBuilderFactory.java │ │ │ │ ├── SDependencyLogBuilder.java │ │ │ │ ├── SDependencyLogBuilderFactory.java │ │ │ │ ├── SDependencyMappingLogBuilder.java │ │ │ │ ├── SDependencyMappingLogBuilderFactory.java │ │ │ │ ├── SPlatformDependencyBuilder.java │ │ │ │ ├── SPlatformDependencyLogBuilderFactory.java │ │ │ │ ├── SPlatformDependencyMappingLogBuilderFactory.java │ │ │ │ └── impl/ │ │ │ │ ├── SDependencyLogBuilderFactoryImpl.java │ │ │ │ ├── SDependencyLogBuilderImpl.java │ │ │ │ ├── SDependencyLogIndexesMapper.java │ │ │ │ ├── SDependencyMappingLogBuilderFactoryImpl.java │ │ │ │ ├── SDependencyMappingLogBuilderImpl.java │ │ │ │ ├── SPlatformDependencyLogBuilderFactoryImpl.java │ │ │ │ ├── SPlatformDependencyLogIndexesMapper.java │ │ │ │ └── SPlatformDependencyMappingLogBuilderFactoryImpl.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── dependency/ │ │ │ └── model/ │ │ │ └── impl/ │ │ │ └── hibernate/ │ │ │ ├── dependency.queries.hbm.xml │ │ │ └── platform-dependency.queries.hbm.xml │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ ├── classloader/ │ │ │ │ ├── BonitaClassLoaderTest.java │ │ │ │ ├── ClassLoaderServiceImplTest.java │ │ │ │ ├── ClassLoaderUpdaterTest.java │ │ │ │ ├── MyClassLoaderListener.java │ │ │ │ ├── ParentClassLoaderResolverTest.java │ │ │ │ ├── RefreshClassloaderSynchronizationTest.java │ │ │ │ └── listeners/ │ │ │ │ └── ClassReflectorClearerTest.java │ │ │ └── dependency/ │ │ │ └── impl/ │ │ │ └── TenantDependencyServiceTest.java │ │ └── resources/ │ │ └── UOSFaasApplication.jar │ ├── bonita-command/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── command/ │ │ │ │ ├── CommandService.java │ │ │ │ ├── SCommandAlreadyExistsException.java │ │ │ │ ├── SCommandCreationException.java │ │ │ │ ├── SCommandDeletionException.java │ │ │ │ ├── SCommandGettingException.java │ │ │ │ ├── SCommandNotFoundException.java │ │ │ │ ├── SCommandUpdateException.java │ │ │ │ ├── api/ │ │ │ │ │ ├── impl/ │ │ │ │ │ │ ├── CommandDeployment.java │ │ │ │ │ │ ├── CommandProvider.java │ │ │ │ │ │ └── CommandServiceImpl.java │ │ │ │ │ └── record/ │ │ │ │ │ └── SelectDescriptorBuilder.java │ │ │ │ └── model/ │ │ │ │ ├── CommandUpdateBuilder.java │ │ │ │ ├── SCommand.java │ │ │ │ ├── SCommandCriterion.java │ │ │ │ ├── SCommandLogBuilder.java │ │ │ │ ├── SCommandLogBuilderFactory.java │ │ │ │ ├── SCommandLogBuilderFactoryImpl.java │ │ │ │ ├── SCommandLogBuilderImpl.java │ │ │ │ ├── SCommandLogIndexesMapper.java │ │ │ │ ├── SCommandUpdateBuilder.java │ │ │ │ ├── SCommandUpdateBuilderFactory.java │ │ │ │ ├── SCommandUpdateBuilderFactoryImpl.java │ │ │ │ └── SCommandUpdateBuilderImpl.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── command/ │ │ │ └── model/ │ │ │ └── impl/ │ │ │ └── hibernate/ │ │ │ └── command.queries.hbm.xml │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── command/ │ │ ├── api/ │ │ │ └── impl/ │ │ │ └── CommandServiceImplTest.java │ │ └── comparator/ │ │ └── CommandComparator.java │ ├── bonita-commons/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ ├── DeepRegexFileFilter.java │ │ │ ├── Experimental.java │ │ │ ├── Profiles.java │ │ │ ├── commons/ │ │ │ │ ├── ClassDataUtil.java │ │ │ │ ├── ClassReflector.java │ │ │ │ ├── CollectionUtil.java │ │ │ │ ├── Container.java │ │ │ │ ├── EnumToObjectConvertible.java │ │ │ │ ├── ExceptionUtils.java │ │ │ │ ├── JavaMethodInvoker.java │ │ │ │ ├── LifecycleService.java │ │ │ │ ├── LogUtil.java │ │ │ │ ├── NullCheckingUtil.java │ │ │ │ ├── Pair.java │ │ │ │ ├── PlatformLifecycleService.java │ │ │ │ ├── PlatformRestartHandler.java │ │ │ │ ├── StringUtils.java │ │ │ │ ├── SystemOperationUtil.java │ │ │ │ ├── TenantLifecycleService.java │ │ │ │ ├── TypeConverterUtil.java │ │ │ │ ├── exceptions/ │ │ │ │ │ ├── ExceptionContext.java │ │ │ │ │ ├── SAlreadyExistsException.java │ │ │ │ │ ├── SBonitaException.java │ │ │ │ │ ├── SBonitaRuntimeException.java │ │ │ │ │ ├── SDeletionException.java │ │ │ │ │ ├── SExceptionContext.java │ │ │ │ │ ├── SExecutionException.java │ │ │ │ │ ├── SLifecycleException.java │ │ │ │ │ ├── SObjectAlreadyExistsException.java │ │ │ │ │ ├── SObjectCreationException.java │ │ │ │ │ ├── SObjectModificationException.java │ │ │ │ │ ├── SObjectNotFoundException.java │ │ │ │ │ ├── SObjectReadException.java │ │ │ │ │ ├── SReflectException.java │ │ │ │ │ ├── SRetryableException.java │ │ │ │ │ ├── SV6FormsDeployException.java │ │ │ │ │ └── ScopedException.java │ │ │ │ ├── io/ │ │ │ │ │ ├── IOUtil.java │ │ │ │ │ └── PropertiesManager.java │ │ │ │ ├── time/ │ │ │ │ │ ├── DefaultEngineClock.java │ │ │ │ │ ├── EngineClock.java │ │ │ │ │ └── FixedEngineClock.java │ │ │ │ └── transaction/ │ │ │ │ ├── TransactionContent.java │ │ │ │ └── TransactionContentWithResult.java │ │ │ ├── data/ │ │ │ │ └── instance/ │ │ │ │ └── model/ │ │ │ │ └── impl/ │ │ │ │ ├── OffsetDateTimeXStreamConverter.java │ │ │ │ └── XStreamFactory.java │ │ │ ├── home/ │ │ │ │ ├── Folder.java │ │ │ │ └── FolderMgr.java │ │ │ ├── mdc/ │ │ │ │ ├── AbstractMDC.java │ │ │ │ ├── FlowNodeInstanceMDC.java │ │ │ │ ├── MDCConstants.java │ │ │ │ ├── MDCHelper.java │ │ │ │ ├── MDCTransmitingThreadPoolExecutor.java │ │ │ │ ├── ProcessInstanceMDC.java │ │ │ │ └── UserIdMDC.java │ │ │ ├── monitoring/ │ │ │ │ ├── DefaultExecutorServiceMetricsProvider.java │ │ │ │ ├── ExecutorServiceMetricsProvider.java │ │ │ │ └── NoOpExecutorServiceMetricsProvider.java │ │ │ ├── properties/ │ │ │ │ ├── BonitaConfigProperty.java │ │ │ │ ├── BonitaProperty.java │ │ │ │ ├── BonitaPropertyAnnotationProcessor.java │ │ │ │ ├── BooleanProperty.java │ │ │ │ ├── CustomValueConfig.java │ │ │ │ └── StringProperty.java │ │ │ └── service/ │ │ │ ├── BonitaTaskExecutor.java │ │ │ ├── BroadcastService.java │ │ │ ├── InjectedService.java │ │ │ ├── RunnableWithException.java │ │ │ ├── ServicesLookup.java │ │ │ ├── ServicesResolver.java │ │ │ └── TaskResult.java │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ ├── DeepRegexFileFilterTest.java │ │ │ ├── commons/ │ │ │ │ ├── ClassReflectorTest.java │ │ │ │ ├── CollectionUtilTest.java │ │ │ │ ├── ContainerTest.java │ │ │ │ ├── ExceptionUtilsTest.java │ │ │ │ ├── JavaMethodInvokerTest.java │ │ │ │ ├── Pojo.java │ │ │ │ ├── StringUtilsTest.java │ │ │ │ ├── TypeConvertUtilTest.java │ │ │ │ ├── TypeConverterUtilTest.java │ │ │ │ └── io/ │ │ │ │ └── IOUtilTest.java │ │ │ ├── home/ │ │ │ │ ├── FolderMgrTest.java │ │ │ │ └── FolderTest.java │ │ │ ├── mdc/ │ │ │ │ └── AbstractMDCTest.java │ │ │ ├── monitoring/ │ │ │ │ └── DefaultExecutorServiceMetricsProviderTest.java │ │ │ ├── properties/ │ │ │ │ ├── BooleanPropertyTest.java │ │ │ │ └── StringPropertyTest.java │ │ │ └── service/ │ │ │ └── ServicesResolverTest.java │ │ └── resources/ │ │ ├── myFile.txt │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── commons/ │ │ └── io/ │ │ ├── bdr-jar.bak │ │ └── persistence.xml │ ├── bonita-connector-executor/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── connector/ │ │ │ ├── AbstractSConnector.java │ │ │ ├── BonitaConnectorExecutorFactory.java │ │ │ ├── ConnectorExecutionResult.java │ │ │ ├── ConnectorExecutor.java │ │ │ ├── SConnector.java │ │ │ ├── exception/ │ │ │ │ ├── SConnectorException.java │ │ │ │ ├── SConnectorValidationException.java │ │ │ │ └── SInvalidEvaluationConnectorConditionException.java │ │ │ └── impl/ │ │ │ ├── ConnectorExecutorImpl.java │ │ │ ├── ConnectorExecutorThreadFactory.java │ │ │ ├── ConnectorSingleThreadExecutorFactory.java │ │ │ └── InterruptibleCallable.java │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── connector/ │ │ │ └── impl/ │ │ │ ├── ConnectorExecutionTest.java │ │ │ ├── ConnectorExecutorImplTest.java │ │ │ ├── ConnectorExecutorSingleThreadTest.java │ │ │ ├── ResourceClassLoader.java │ │ │ └── ResourceConnector.java │ │ └── resources/ │ │ └── logback-test.xml │ ├── bonita-data-definition/ │ │ ├── build.gradle │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── data/ │ │ └── definition/ │ │ └── model/ │ │ ├── SDataDefinition.java │ │ ├── STextDataDefinition.java │ │ ├── SXMLDataDefinition.java │ │ ├── builder/ │ │ │ ├── AbstractSDataDefinitionBuilder.java │ │ │ ├── SDataDefinitionBuilder.java │ │ │ ├── SDataDefinitionBuilderFactory.java │ │ │ ├── SEnumationDataDefinitionBuilder.java │ │ │ ├── SEnumationDataDefinitionBuilderFactory.java │ │ │ ├── STextDataDefinitionBuilder.java │ │ │ ├── STextDataDefinitionBuilderFactory.java │ │ │ ├── SXMLDataDefinitionBuilder.java │ │ │ ├── SXMLDataDefinitionBuilderFactory.java │ │ │ └── impl/ │ │ │ ├── SDataDefinitionBuilderFactoryImpl.java │ │ │ ├── SDataDefinitionBuilderImpl.java │ │ │ ├── SXMLDataDefinitionBuilderFactoryImpl.java │ │ │ └── SXMLDataDefinitionBuilderImpl.java │ │ └── impl/ │ │ ├── SDataDefinitionImpl.java │ │ ├── STextDefinitionImpl.java │ │ └── SXMLDataDefinitionImpl.java │ ├── bonita-data-instance/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── data/ │ │ │ │ └── instance/ │ │ │ │ ├── api/ │ │ │ │ │ ├── DataContainer.java │ │ │ │ │ ├── DataInstanceContainer.java │ │ │ │ │ ├── DataInstanceService.java │ │ │ │ │ ├── ParentContainerResolver.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── ArchivedDataInContainersComparator.java │ │ │ │ │ ├── DataInContainersComparator.java │ │ │ │ │ └── DataInstanceServiceImpl.java │ │ │ │ ├── exception/ │ │ │ │ │ ├── SCreateDataInstanceException.java │ │ │ │ │ ├── SDataInstanceException.java │ │ │ │ │ ├── SDataInstanceNotFoundException.java │ │ │ │ │ ├── SDataInstanceReadException.java │ │ │ │ │ ├── SDeleteDataInstanceException.java │ │ │ │ │ └── SUpdateDataInstanceException.java │ │ │ │ └── model/ │ │ │ │ ├── SBlobDataInstance.java │ │ │ │ ├── SBooleanDataInstance.java │ │ │ │ ├── SDataInstance.java │ │ │ │ ├── SDataInstanceBuilder.java │ │ │ │ ├── SDateDataInstance.java │ │ │ │ ├── SDoubleDataInstance.java │ │ │ │ ├── SFloatDataInstance.java │ │ │ │ ├── SIntegerDataInstance.java │ │ │ │ ├── SLongDataInstance.java │ │ │ │ ├── SLongTextDataInstance.java │ │ │ │ ├── SShortTextDataInstance.java │ │ │ │ ├── SXMLDataInstance.java │ │ │ │ ├── SXMLObjectDataInstance.java │ │ │ │ ├── archive/ │ │ │ │ │ ├── SABlobDataInstance.java │ │ │ │ │ ├── SABooleanDataInstance.java │ │ │ │ │ ├── SADataInstance.java │ │ │ │ │ ├── SADateDataInstance.java │ │ │ │ │ ├── SADoubleDataInstance.java │ │ │ │ │ ├── SAFloatDataInstance.java │ │ │ │ │ ├── SAIntegerDataInstance.java │ │ │ │ │ ├── SALongDataInstance.java │ │ │ │ │ ├── SALongTextDataInstance.java │ │ │ │ │ ├── SAShortTextDataInstance.java │ │ │ │ │ ├── SAXMLDataInstance.java │ │ │ │ │ ├── SAXMLObjectDataInstance.java │ │ │ │ │ └── builder/ │ │ │ │ │ ├── SADataInstanceBuilder.java │ │ │ │ │ ├── SADataInstanceLogBuilder.java │ │ │ │ │ ├── SADataInstanceLogBuilderFactory.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── SADataInstanceLogBuilderFactoryImpl.java │ │ │ │ │ └── SADataInstanceLogBuilderImpl.java │ │ │ │ └── exceptions/ │ │ │ │ └── SDataInstanceNotWellFormedException.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── data/ │ │ │ └── instance/ │ │ │ └── model/ │ │ │ └── impl/ │ │ │ └── hibernate/ │ │ │ ├── archived.data.instance.queries.hbm.xml │ │ │ └── data.instance.queries.hbm.xml │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── data/ │ │ └── instance/ │ │ ├── api/ │ │ │ └── impl/ │ │ │ ├── ArchivedDataInContainersComparatorTest.java │ │ │ ├── DataInContainersComparatorTest.java │ │ │ └── DataInstanceServiceImplTest.java │ │ └── model/ │ │ ├── archive/ │ │ │ └── impl/ │ │ │ ├── SAXMLDataInstanceImplTest.java │ │ │ └── SAXMLObjectDataInstanceTest.java │ │ ├── builder/ │ │ │ └── impl/ │ │ │ └── SDataInstanceBuilderFactoryImplTest.java │ │ └── impl/ │ │ ├── OffsetDateTimeXStreamConverterTest.java │ │ ├── SXMLObjectDataInstanceTest.java │ │ └── XStreamFactoryTest.java │ ├── bonita-events/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── events/ │ │ │ ├── EventActionType.java │ │ │ ├── EventService.java │ │ │ ├── impl/ │ │ │ │ ├── AbstractEventServiceImpl.java │ │ │ │ └── EventServiceImpl.java │ │ │ └── model/ │ │ │ ├── HandlerRegistrationException.java │ │ │ ├── HandlerUnregistrationException.java │ │ │ ├── SDeleteEvent.java │ │ │ ├── SEvent.java │ │ │ ├── SFireEventException.java │ │ │ ├── SHandler.java │ │ │ ├── SHandlerExecutionException.java │ │ │ ├── SInsertEvent.java │ │ │ └── SUpdateEvent.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── events/ │ │ ├── EventServiceImplTest.java │ │ ├── TestEvent.java │ │ ├── TestHandler.java │ │ └── TestHandlerCallback.java │ ├── bonita-expression/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── expression/ │ │ │ ├── ContainerState.java │ │ │ ├── ExpressionExecutor.java │ │ │ ├── ExpressionExecutorStrategy.java │ │ │ ├── ExpressionExecutorStrategyProvider.java │ │ │ ├── ExpressionService.java │ │ │ ├── NonEmptyContentExpressionExecutorStrategy.java │ │ │ ├── exception/ │ │ │ │ ├── SExpressionDependencyMissingException.java │ │ │ │ ├── SExpressionEvaluationException.java │ │ │ │ ├── SExpressionException.java │ │ │ │ ├── SExpressionTypeUnknownException.java │ │ │ │ └── SInvalidExpressionException.java │ │ │ ├── impl/ │ │ │ │ ├── ConditionExpressionExecutorStrategy.java │ │ │ │ ├── ConstantExpressionExecutorStrategy.java │ │ │ │ ├── ExpressionServiceImpl.java │ │ │ │ ├── GroovyScriptConditionExpressionExecutorStrategy.java │ │ │ │ ├── GroovyScriptExpressionExecutorCacheStrategy.java │ │ │ │ ├── InputExpressionExecutorStrategy.java │ │ │ │ ├── JavaMethodCallExpressionExecutorStrategy.java │ │ │ │ ├── ListExpressionExecutorStrategy.java │ │ │ │ ├── PatternExpressionExecutorStrategy.java │ │ │ │ ├── ReturnTypeChecker.java │ │ │ │ ├── XPathReadExpressionExecutorStrategy.java │ │ │ │ └── condition/ │ │ │ │ ├── BinaryComparator.java │ │ │ │ ├── BinaryComparatorExecutor.java │ │ │ │ ├── BinaryComparatorMapper.java │ │ │ │ ├── DifferentComparator.java │ │ │ │ ├── EqualityComparator.java │ │ │ │ ├── EqualsComparator.java │ │ │ │ ├── GreaterThanComparator.java │ │ │ │ ├── GreaterThanOrEqualsComparator.java │ │ │ │ ├── InequalityComparator.java │ │ │ │ ├── LessThanComparator.java │ │ │ │ ├── LessThanOrEqualsComparator.java │ │ │ │ ├── LogicalComplementExecutor.java │ │ │ │ └── SComparisonException.java │ │ │ └── model/ │ │ │ ├── ExpressionKind.java │ │ │ ├── SExpression.java │ │ │ ├── SExpressionType.java │ │ │ ├── builder/ │ │ │ │ ├── SExpressionBuilder.java │ │ │ │ ├── SExpressionBuilderFactory.java │ │ │ │ └── impl/ │ │ │ │ ├── SExpressionBuilderFactoryImpl.java │ │ │ │ └── SExpressionBuilderImpl.java │ │ │ └── impl/ │ │ │ └── SExpressionImpl.java │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── expression/ │ │ │ ├── exception/ │ │ │ │ ├── SExpressionEvaluationExceptionTest.java │ │ │ │ └── SInvalidExpressionExceptionTest.java │ │ │ └── impl/ │ │ │ ├── ConditionExpressionExecutorStrategyTest.java │ │ │ ├── ConstantExpressionExecutorStrategyTest.java │ │ │ ├── ExpressionServiceImplTest.java │ │ │ ├── GroovyScriptConditionExpressionExecutorStrategyTest.java │ │ │ ├── GroovyScriptExpressionExecutorCacheStrategyTest.java │ │ │ ├── JavaMethodCallExpressionExecutorStrategyTest.java │ │ │ ├── Order.java │ │ │ ├── PatternExpressionExecutorStrategyTest.java │ │ │ ├── ReturnTypeCheckerTest.java │ │ │ ├── XPathReadExpressionExecutorStrategyTest.java │ │ │ └── condition/ │ │ │ ├── BinaryComparatorExecutorTest.java │ │ │ ├── BinaryComparatorMapperTest.java │ │ │ ├── BinaryComparatorTest.java │ │ │ ├── DifferentComparatorTest.java │ │ │ ├── EqualityComparatorTest.java │ │ │ ├── EqualsComparatorTest.java │ │ │ ├── GreaterThanComparatorTest.java │ │ │ ├── GreaterThanOrEqualsComparatorTest.java │ │ │ ├── InequalityComparatorTest.java │ │ │ ├── LessThanComparatorTest.java │ │ │ ├── LessThanOrEqualsComparatorTest.java │ │ │ ├── LogicalComplementExecutorTest.java │ │ │ └── NotComparableClass.java │ │ └── resources/ │ │ ├── authors.xml │ │ ├── books.xml │ │ └── logback-test.xml │ ├── bonita-identity/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── identity/ │ │ │ │ ├── IconService.java │ │ │ │ ├── IconServiceImpl.java │ │ │ │ ├── IdentityService.java │ │ │ │ ├── SCustomUserInfoDefinitionAlreadyExistsException.java │ │ │ │ ├── SCustomUserInfoDefinitionCreationException.java │ │ │ │ ├── SCustomUserInfoDefinitionNotFoundException.java │ │ │ │ ├── SCustomUserInfoDefinitionReadException.java │ │ │ │ ├── SCustomUserInfoValueNotFoundException.java │ │ │ │ ├── SCustomUserInfoValueReadException.java │ │ │ │ ├── SGroupCreationException.java │ │ │ │ ├── SGroupDeletionException.java │ │ │ │ ├── SGroupNotFoundException.java │ │ │ │ ├── SIcon.java │ │ │ │ ├── SIdentityException.java │ │ │ │ ├── SMembershipCreationException.java │ │ │ │ ├── SMembershipDeletionException.java │ │ │ │ ├── SRoleDeletionException.java │ │ │ │ ├── SRoleNotFoundException.java │ │ │ │ ├── SUserCreationException.java │ │ │ │ ├── SUserDeletionException.java │ │ │ │ ├── SUserMembershipCreationException.java │ │ │ │ ├── SUserMembershipNotFoundException.java │ │ │ │ ├── SUserNotFoundException.java │ │ │ │ ├── SUserUpdateException.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── CredentialsEncrypter.java │ │ │ │ │ ├── IdentityServiceImpl.java │ │ │ │ │ └── MD5CredentialsEncrypter.java │ │ │ │ ├── model/ │ │ │ │ │ ├── SContactInfo.java │ │ │ │ │ ├── SCustomUserInfoDefinition.java │ │ │ │ │ ├── SCustomUserInfoValue.java │ │ │ │ │ ├── SGroup.java │ │ │ │ │ ├── SHavingIcon.java │ │ │ │ │ ├── SRole.java │ │ │ │ │ ├── SUser.java │ │ │ │ │ ├── SUserLogin.java │ │ │ │ │ ├── SUserMembership.java │ │ │ │ │ └── builder/ │ │ │ │ │ ├── SContactInfoLogBuilder.java │ │ │ │ │ ├── SContactInfoLogBuilderFactory.java │ │ │ │ │ ├── SContactInfoUpdateBuilder.java │ │ │ │ │ ├── SContactInfoUpdateBuilderFactory.java │ │ │ │ │ ├── SCustomUserInfoDefinitionLogBuilder.java │ │ │ │ │ ├── SCustomUserInfoDefinitionLogBuilderFactory.java │ │ │ │ │ ├── SCustomUserInfoDefinitionUpdateBuilder.java │ │ │ │ │ ├── SCustomUserInfoDefinitionUpdateBuilderFactory.java │ │ │ │ │ ├── SCustomUserInfoValueUpdateBuilder.java │ │ │ │ │ ├── SCustomUserInfoValueUpdateBuilderFactory.java │ │ │ │ │ ├── SGroupLogBuilder.java │ │ │ │ │ ├── SGroupLogBuilderFactory.java │ │ │ │ │ ├── SGroupUpdateBuilder.java │ │ │ │ │ ├── SGroupUpdateBuilderFactory.java │ │ │ │ │ ├── SIdentityUpdateBuilder.java │ │ │ │ │ ├── SIdentityUpdateBuilderFactory.java │ │ │ │ │ ├── SMembershipLogBuilder.java │ │ │ │ │ ├── SMembershipLogBuilderFactory.java │ │ │ │ │ ├── SProfileMetadataValueLogBuilder.java │ │ │ │ │ ├── SProfileMetadataValueLogBuilderFactory.java │ │ │ │ │ ├── SRoleLogBuilder.java │ │ │ │ │ ├── SRoleLogBuilderFactory.java │ │ │ │ │ ├── SRoleUpdateBuilder.java │ │ │ │ │ ├── SRoleUpdateBuilderFactory.java │ │ │ │ │ ├── SUserLogBuilder.java │ │ │ │ │ ├── SUserLogBuilderFactory.java │ │ │ │ │ ├── SUserMembershipLogBuilder.java │ │ │ │ │ ├── SUserMembershipLogBuilderFactory.java │ │ │ │ │ ├── SUserMembershipUpdateBuilder.java │ │ │ │ │ ├── SUserMembershipUpdateBuilderFactory.java │ │ │ │ │ ├── SUserUpdateBuilder.java │ │ │ │ │ ├── SUserUpdateBuilderFactory.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── SContactInfoLogBuilderFactoryImpl.java │ │ │ │ │ ├── SContactInfoLogBuilderImpl.java │ │ │ │ │ ├── SContactInfoUpdateBuilderFactoryImpl.java │ │ │ │ │ ├── SContactInfoUpdateBuilderImpl.java │ │ │ │ │ ├── SCustomUserInfoDefinitionLogBuilderFactoryImpl.java │ │ │ │ │ ├── SCustomUserInfoDefinitionLogBuilderImpl.java │ │ │ │ │ ├── SCustomUserInfoDefinitionUpdateBuilderFactoryImpl.java │ │ │ │ │ ├── SCustomUserInfoDefinitionUpdateBuilderImpl.java │ │ │ │ │ ├── SCustomUserInfoValueUpdateBuilderFactoryImpl.java │ │ │ │ │ ├── SCustomUserInfoValueUpdateBuilderImpl.java │ │ │ │ │ ├── SGroupLogBuilderFactoryImpl.java │ │ │ │ │ ├── SGroupLogBuilderImpl.java │ │ │ │ │ ├── SGroupUpdateBuilderFactoryImpl.java │ │ │ │ │ ├── SGroupUpdateBuilderImpl.java │ │ │ │ │ ├── SRoleLogBuilderFactoryImpl.java │ │ │ │ │ ├── SRoleLogBuilderImpl.java │ │ │ │ │ ├── SRoleUpdateBuilderFactoryImpl.java │ │ │ │ │ ├── SRoleUpdateBuilderImpl.java │ │ │ │ │ ├── SUserLogBuilderFactoryImpl.java │ │ │ │ │ ├── SUserLogBuilderImpl.java │ │ │ │ │ ├── SUserMembershipLogBuilderFactoryImpl.java │ │ │ │ │ ├── SUserMembershipLogBuilderImpl.java │ │ │ │ │ ├── SUserMembershipUpdateBuilderFactoryImpl.java │ │ │ │ │ ├── SUserMembershipUpdateBuilderImpl.java │ │ │ │ │ ├── SUserUpdateBuilderFactoryImpl.java │ │ │ │ │ └── SUserUpdateBuilderImpl.java │ │ │ │ ├── package-info.java │ │ │ │ └── recorder/ │ │ │ │ ├── SelectDescriptorBuilder.java │ │ │ │ └── UserRecordType.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── identity/ │ │ │ └── model/ │ │ │ └── impl/ │ │ │ └── hibernate/ │ │ │ └── identity.queries.hbm.xml │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── identity/ │ │ ├── IconServiceImplTest.java │ │ ├── impl/ │ │ │ ├── IdentityServiceImplForCustomUserInfoTest.java │ │ │ ├── IdentityServiceImplForGroupTest.java │ │ │ ├── IdentityServiceImplForUserMembershipTest.java │ │ │ ├── IdentityServiceImplForUserTest.java │ │ │ └── MD5CredentialsEncrypterTest.java │ │ └── model/ │ │ └── SUserTest.java │ ├── bonita-incident/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── incident/ │ │ │ ├── FileLoggerIncidentHandler.java │ │ │ ├── Incident.java │ │ │ ├── IncidentHandler.java │ │ │ ├── IncidentService.java │ │ │ └── IncidentServiceImpl.java │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── incident/ │ │ │ ├── BeforeAllLoggerInitializer.java │ │ │ ├── FileLoggerIncidentHandlerTest.java │ │ │ └── IncidentServiceImplTest.java │ │ └── resources/ │ │ └── logback.xml │ ├── bonita-lock/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── lock/ │ │ │ ├── BonitaLock.java │ │ │ ├── LockService.java │ │ │ ├── MemoryLockService.java │ │ │ ├── SLockException.java │ │ │ └── SLockTimeoutException.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── lock/ │ │ └── MemoryLockServiceTest.java │ ├── bonita-log/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ ├── queriablelogger/ │ │ │ │ │ └── model/ │ │ │ │ │ ├── SQueriableLog.java │ │ │ │ │ ├── SQueriableLogSeverity.java │ │ │ │ │ └── builder/ │ │ │ │ │ ├── ActionType.java │ │ │ │ │ ├── HasCRUDEAction.java │ │ │ │ │ ├── HasCRUDEActionFactory.java │ │ │ │ │ ├── SLogBuilder.java │ │ │ │ │ ├── SLogBuilderFactory.java │ │ │ │ │ ├── SPersistenceLogBuilder.java │ │ │ │ │ ├── SPersistenceLogBuilderFactory.java │ │ │ │ │ ├── SQueriableLogBuilder.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── CRUDELogBuilder.java │ │ │ │ │ ├── CRUDELogBuilderFactory.java │ │ │ │ │ └── MissingMandatoryFieldsException.java │ │ │ │ └── services/ │ │ │ │ ├── IllegalIndexPositionException.java │ │ │ │ ├── QueriableLogSessionProvider.java │ │ │ │ ├── QueriableLoggerService.java │ │ │ │ ├── QueriableLoggerStrategy.java │ │ │ │ ├── SQueriableLogException.java │ │ │ │ ├── SQueriableLogNotFoundException.java │ │ │ │ └── impl/ │ │ │ │ ├── BatchLogSynchronization.java │ │ │ │ ├── QueriableLogSessionProviderImpl.java │ │ │ │ ├── QueriableLogUpdater.java │ │ │ │ ├── QueriableLoggerImpl.java │ │ │ │ └── SimpleQueriableLoggerStrategy.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── queriablelogger/ │ │ │ └── model/ │ │ │ └── impl/ │ │ │ └── hibernate/ │ │ │ └── queriablelogger.queries.hbm.xml │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ ├── queriablelogger/ │ │ │ │ └── model/ │ │ │ │ └── SQueriableLogTest.java │ │ │ └── services/ │ │ │ └── impl/ │ │ │ ├── QueriableLogSessionProviderImplTest.java │ │ │ ├── QueriableLogUpdaterTest.java │ │ │ ├── QueriableLoggerImplTest.java │ │ │ └── SimpleQueriableLoggerStrategyTest.java │ │ └── resources/ │ │ └── logback-test.xml │ ├── bonita-page/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── page/ │ │ │ │ ├── AbstractSPage.java │ │ │ │ ├── AuthorizationRule.java │ │ │ │ ├── PageMappingService.java │ │ │ │ ├── PageService.java │ │ │ │ ├── PageServiceListener.java │ │ │ │ ├── SAuthorizationException.java │ │ │ │ ├── SContentType.java │ │ │ │ ├── SInvalidPageTokenException.java │ │ │ │ ├── SInvalidPageZipException.java │ │ │ │ ├── SInvalidPageZipInconsistentException.java │ │ │ │ ├── SInvalidPageZipMissingAPropertyException.java │ │ │ │ ├── SInvalidPageZipMissingIndexException.java │ │ │ │ ├── SInvalidPageZipMissingPropertiesException.java │ │ │ │ ├── SPage.java │ │ │ │ ├── SPageLogBuilder.java │ │ │ │ ├── SPageLogBuilderFactory.java │ │ │ │ ├── SPageMapping.java │ │ │ │ ├── SPageURL.java │ │ │ │ ├── SPageUpdateBuilder.java │ │ │ │ ├── SPageUpdateBuilderFactory.java │ │ │ │ ├── SPageUpdateContentBuilder.java │ │ │ │ ├── SPageWithContent.java │ │ │ │ ├── URLAdapter.java │ │ │ │ └── impl/ │ │ │ │ ├── ApiExtensionPageServiceListenerImpl.java │ │ │ │ ├── AuthorizationRulesInjector.java │ │ │ │ ├── PageMappingServiceImpl.java │ │ │ │ ├── PageServiceImpl.java │ │ │ │ ├── SPageContentFields.java │ │ │ │ ├── SPageContentHelper.java │ │ │ │ ├── SPageFields.java │ │ │ │ ├── SPageLogBuilderFactoryImpl.java │ │ │ │ ├── SPageLogBuilderImpl.java │ │ │ │ ├── SPageUpdateBuilderFactoryImpl.java │ │ │ │ ├── SPageUpdateBuilderImpl.java │ │ │ │ ├── SPageUpdateContentBuilderImpl.java │ │ │ │ └── UrlAdapterInjector.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── page/ │ │ │ └── impl/ │ │ │ └── hibernate/ │ │ │ └── page.queries.hbm.xml │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── page/ │ │ └── impl/ │ │ ├── ApiExtensionPageServiceListenerImplTest.java │ │ ├── PageMappingServiceImplTest.java │ │ ├── PageServiceImplTest.java │ │ ├── SPageAssert.java │ │ ├── SPageImplAssert.java │ │ ├── SPageImplTest.java │ │ ├── SPageMappingImplTest.java │ │ └── SPageMappingServiceImplTest.java │ ├── bonita-persistence/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ ├── persistence/ │ │ │ │ │ ├── AbstractSelectDescriptor.java │ │ │ │ │ ├── AbstractSelectWithParametersDescriptor.java │ │ │ │ │ ├── ArchivedPersistentObject.java │ │ │ │ │ ├── Atomikos3HibernateJtaPlatform.java │ │ │ │ │ ├── ConfigurationException.java │ │ │ │ │ ├── CustomDataTypesRegistration.java │ │ │ │ │ ├── DateStoredAsLongUserType.java │ │ │ │ │ ├── DefaultOrderByBuilder.java │ │ │ │ │ ├── DefaultQueryOptions.java │ │ │ │ │ ├── FilterOption.java │ │ │ │ │ ├── HQLQueryBuilder.java │ │ │ │ │ ├── HibernateConfigurationProvider.java │ │ │ │ │ ├── HibernateConfigurationProviderImpl.java │ │ │ │ │ ├── HibernateMetricsBinder.java │ │ │ │ │ ├── HibernatePersistenceService.java │ │ │ │ │ ├── HibernateResourcesConfigurationProvider.java │ │ │ │ │ ├── HibernateResourcesConfigurationProviderImpl.java │ │ │ │ │ ├── HibernateResourcesProvider.java │ │ │ │ │ ├── JNDIBitronixJtaPlatform.java │ │ │ │ │ ├── Narayana5HibernateJtaPlatform.java │ │ │ │ │ ├── OrderAndField.java │ │ │ │ │ ├── OrderByBuilder.java │ │ │ │ │ ├── OrderByCheckingMode.java │ │ │ │ │ ├── OrderByOption.java │ │ │ │ │ ├── OrderByType.java │ │ │ │ │ ├── PersistentObject.java │ │ │ │ │ ├── PostgresInterceptor.java │ │ │ │ │ ├── PostgresMaterializedBlobType.java │ │ │ │ │ ├── PostgresMaterializedClobType.java │ │ │ │ │ ├── PostgresXMLType.java │ │ │ │ │ ├── QueryBuilder.java │ │ │ │ │ ├── QueryBuilderFactory.java │ │ │ │ │ ├── QueryGeneratorForFilters.java │ │ │ │ │ ├── QueryGeneratorForOrderBy.java │ │ │ │ │ ├── QueryGeneratorForSearchTerm.java │ │ │ │ │ ├── QueryOptions.java │ │ │ │ │ ├── ReadOnlySelectByIdDescriptor.java │ │ │ │ │ ├── ReadPersistenceService.java │ │ │ │ │ ├── SBonitaReadException.java │ │ │ │ │ ├── SQLQueryBuilder.java │ │ │ │ │ ├── SQLTransformer.java │ │ │ │ │ ├── SearchFields.java │ │ │ │ │ ├── SelectByIdDescriptor.java │ │ │ │ │ ├── SelectListDescriptor.java │ │ │ │ │ ├── SelectOneDescriptor.java │ │ │ │ │ ├── XMLType.java │ │ │ │ │ ├── XMLTypeDescriptor.java │ │ │ │ │ └── search/ │ │ │ │ │ └── FilterOperationType.java │ │ │ │ ├── recorder/ │ │ │ │ │ ├── Recorder.java │ │ │ │ │ ├── SRecorderException.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ └── RecorderImpl.java │ │ │ │ │ └── model/ │ │ │ │ │ ├── DeleteAllRecord.java │ │ │ │ │ ├── DeleteRecord.java │ │ │ │ │ ├── EntityUpdateDescriptor.java │ │ │ │ │ ├── InsertRecord.java │ │ │ │ │ ├── Record.java │ │ │ │ │ └── UpdateRecord.java │ │ │ │ ├── sequence/ │ │ │ │ │ ├── SequenceDAO.java │ │ │ │ │ ├── SequenceManager.java │ │ │ │ │ ├── SequenceManagerImpl.java │ │ │ │ │ ├── SequenceMapping.java │ │ │ │ │ ├── SequenceMappingProvider.java │ │ │ │ │ ├── SequenceRange.java │ │ │ │ │ └── exceptions/ │ │ │ │ │ └── SequenceManagerException.java │ │ │ │ └── services/ │ │ │ │ ├── PersistenceService.java │ │ │ │ ├── SPersistenceException.java │ │ │ │ ├── UpdateDescriptor.java │ │ │ │ └── Vendor.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── services/ │ │ │ └── org.hibernate.boot.spi.SessionFactoryBuilderFactory │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ ├── persistence/ │ │ │ │ ├── DefaultOrderByBuilderTest.java │ │ │ │ ├── OrderByTypeTest.java │ │ │ │ ├── QueryBuilderTest.java │ │ │ │ ├── QueryGeneratorForFiltersTest.java │ │ │ │ ├── QueryGeneratorForSearchTermTest.java │ │ │ │ ├── QueryOptionsTest.java │ │ │ │ ├── SQLQueryBuilderTest.java │ │ │ │ ├── SelectListDescriptorTest.java │ │ │ │ └── TestObject.java │ │ │ ├── recorder/ │ │ │ │ └── impl/ │ │ │ │ └── RecorderImplTest.java │ │ │ └── sequence/ │ │ │ ├── SequenceDAOTest.java │ │ │ ├── SequenceManagerImplTest.java │ │ │ └── SequenceRangeTest.java │ │ └── resources/ │ │ └── logback-test.xml │ ├── bonita-platform/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── platform/ │ │ │ │ ├── PlatformRetriever.java │ │ │ │ ├── PlatformService.java │ │ │ │ ├── exception/ │ │ │ │ │ ├── SDeletingActivatedTenantException.java │ │ │ │ │ ├── SPlatformDeletionException.java │ │ │ │ │ ├── SPlatformNotFoundException.java │ │ │ │ │ ├── SPlatformUpdateException.java │ │ │ │ │ ├── STenantActivationException.java │ │ │ │ │ ├── STenantAlreadyExistException.java │ │ │ │ │ ├── STenantCreationException.java │ │ │ │ │ ├── STenantDeactivationException.java │ │ │ │ │ ├── STenantDeletionException.java │ │ │ │ │ ├── STenantException.java │ │ │ │ │ ├── STenantNotFoundException.java │ │ │ │ │ └── STenantUpdateException.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── PlatformRetrieverImpl.java │ │ │ │ │ └── PlatformServiceImpl.java │ │ │ │ └── model/ │ │ │ │ ├── SPlatform.java │ │ │ │ ├── SPlatformProperties.java │ │ │ │ ├── builder/ │ │ │ │ │ ├── SPlatformUpdateBuilder.java │ │ │ │ │ └── impl/ │ │ │ │ │ └── SPlatformUpdateBuilderImpl.java │ │ │ │ └── impl/ │ │ │ │ └── SPlatformPropertiesImpl.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── platform/ │ │ │ └── model/ │ │ │ └── impl/ │ │ │ ├── hibernate/ │ │ │ │ └── platform.queries.hbm.xml │ │ │ └── platform.properties │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── platform/ │ │ ├── PlatformServiceImplTest.java │ │ └── impl/ │ │ └── PlatformRetrieverImplTest.java │ ├── bonita-platform-authentication/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── platform/ │ │ │ └── authentication/ │ │ │ ├── PlatformAuthenticationService.java │ │ │ ├── SInvalidPasswordException.java │ │ │ ├── SInvalidUserException.java │ │ │ ├── SPlatformAuthenticationException.java │ │ │ └── impl/ │ │ │ └── PlatformAuthenticationServiceImpl.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── platform/ │ │ └── authentication/ │ │ └── impl/ │ │ └── PlatformAuthenticationServiceImplTest.java │ ├── bonita-platform-command/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── platform/ │ │ │ │ └── command/ │ │ │ │ ├── PlatformCommandService.java │ │ │ │ ├── SPlatformCommandAlreadyExistsException.java │ │ │ │ ├── SPlatformCommandCreationException.java │ │ │ │ ├── SPlatformCommandDeletionException.java │ │ │ │ ├── SPlatformCommandGettingException.java │ │ │ │ ├── SPlatformCommandNotFoundException.java │ │ │ │ ├── SPlatformCommandUpdateException.java │ │ │ │ ├── impl/ │ │ │ │ │ └── PlatformCommandServiceImpl.java │ │ │ │ ├── model/ │ │ │ │ │ ├── SPlatformCommand.java │ │ │ │ │ ├── SPlatformCommandCriterion.java │ │ │ │ │ ├── SPlatformCommandLogBuilder.java │ │ │ │ │ ├── SPlatformCommandLogBuilderFactory.java │ │ │ │ │ ├── SPlatformCommandUpdateBuilder.java │ │ │ │ │ ├── SPlatformCommandUpdateBuilderFactory.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── SPlatformCommandLogBuilderFactoryImpl.java │ │ │ │ │ ├── SPlatformCommandLogBuilderImpl.java │ │ │ │ │ ├── SPlatformCommandLogIndexesMapper.java │ │ │ │ │ ├── SPlatformCommandUpdateBuilderFactoryImpl.java │ │ │ │ │ └── SPlatformCommandUpdateBuilderImpl.java │ │ │ │ └── recorder/ │ │ │ │ └── SelectDescriptorBuilder.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── platform/ │ │ │ └── command/ │ │ │ └── model/ │ │ │ └── impl/ │ │ │ └── hibernate/ │ │ │ └── platformCommand.queries.hbm.xml │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── platform/ │ │ └── command/ │ │ └── impl/ │ │ └── PlatformCommandServiceImplTest.java │ ├── bonita-platform-session/ │ │ ├── build.gradle │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── platform/ │ │ └── session/ │ │ ├── PlatformSessionProvider.java │ │ ├── PlatformSessionService.java │ │ ├── SSessionAlreadyExistsException.java │ │ ├── SSessionException.java │ │ ├── SSessionNotFoundException.java │ │ ├── impl/ │ │ │ ├── PlatformSessionIdGenerator.java │ │ │ ├── PlatformSessionProviderImpl.java │ │ │ └── PlatformSessionServiceImpl.java │ │ └── model/ │ │ ├── SPlatformSession.java │ │ ├── builder/ │ │ │ ├── SPlatformSessionBuilder.java │ │ │ ├── SPlatformSessionBuilderFactory.java │ │ │ └── impl/ │ │ │ ├── SPlatformSessionBuilderFactoryImpl.java │ │ │ └── SPlatformSessionBuilderImpl.java │ │ └── impl/ │ │ └── SPlatformSessionImpl.java │ ├── bonita-profile/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── profile/ │ │ │ │ ├── ProfileService.java │ │ │ │ ├── builder/ │ │ │ │ │ ├── SProfileMemberUpdateBuilder.java │ │ │ │ │ ├── SProfileMemberUpdateBuilderFactory.java │ │ │ │ │ ├── SProfileUpdateBuilder.java │ │ │ │ │ ├── SProfileUpdateBuilderFactory.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── SProfileLogBuilderFactoryImpl.java │ │ │ │ │ ├── SProfileLogBuilderImpl.java │ │ │ │ │ ├── SProfileMemberLogBuilderFactoryImpl.java │ │ │ │ │ ├── SProfileMemberLogBuilderImpl.java │ │ │ │ │ ├── SProfileMemberUpdateBuilderFactoryImpl.java │ │ │ │ │ ├── SProfileMemberUpdateBuilderImpl.java │ │ │ │ │ ├── SProfileUpdateBuilderFactoryImpl.java │ │ │ │ │ └── SProfileUpdateBuilderImpl.java │ │ │ │ ├── exception/ │ │ │ │ │ ├── profile/ │ │ │ │ │ │ ├── SProfileAlreadyExistsException.java │ │ │ │ │ │ ├── SProfileCreationException.java │ │ │ │ │ │ ├── SProfileDeletionException.java │ │ │ │ │ │ ├── SProfileException.java │ │ │ │ │ │ ├── SProfileGettingException.java │ │ │ │ │ │ ├── SProfileNotFoundException.java │ │ │ │ │ │ └── SProfileUpdateException.java │ │ │ │ │ └── profilemember/ │ │ │ │ │ ├── SProfileMemberCreationException.java │ │ │ │ │ ├── SProfileMemberDeletionException.java │ │ │ │ │ ├── SProfileMemberException.java │ │ │ │ │ └── SProfileMemberNotFoundException.java │ │ │ │ ├── impl/ │ │ │ │ │ └── ProfileServiceImpl.java │ │ │ │ ├── model/ │ │ │ │ │ ├── SProfile.java │ │ │ │ │ └── SProfileMember.java │ │ │ │ └── persistence/ │ │ │ │ └── SelectDescriptorBuilder.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── profile/ │ │ │ └── model/ │ │ │ └── impl/ │ │ │ └── hibernate/ │ │ │ └── profile.queries.hbm.xml │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── profile/ │ │ └── impl/ │ │ └── ProfileServiceImplForProfileTest.java │ ├── bonita-resources/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── resources/ │ │ │ │ ├── AbstractSBARResource.java │ │ │ │ ├── AbstractSTenantResource.java │ │ │ │ ├── BARResourceType.java │ │ │ │ ├── ProcessResourcesService.java │ │ │ │ ├── ProcessResourcesServiceImpl.java │ │ │ │ ├── SBARResource.java │ │ │ │ ├── SBARResourceLight.java │ │ │ │ ├── STenantResource.java │ │ │ │ ├── STenantResourceLight.java │ │ │ │ ├── STenantResourceState.java │ │ │ │ ├── TenantResourceType.java │ │ │ │ ├── TenantResourcesService.java │ │ │ │ └── TenantResourcesServiceImpl.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── resources/ │ │ │ └── hibernate/ │ │ │ └── resources.queries.hbm.xml │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── resources/ │ │ │ └── TenantResourcesServiceImplTest.java │ │ └── resources/ │ │ └── logback-test.xml │ ├── bonita-scheduler/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── scheduler/ │ │ │ │ ├── BonitaJobListener.java │ │ │ │ ├── JobChecker.java │ │ │ │ ├── JobIdentifier.java │ │ │ │ ├── JobService.java │ │ │ │ ├── SchedulerExecutor.java │ │ │ │ ├── SchedulerService.java │ │ │ │ ├── StatelessJob.java │ │ │ │ ├── exception/ │ │ │ │ │ ├── SJobConfigurationException.java │ │ │ │ │ ├── SJobExecutionException.java │ │ │ │ │ ├── SJobListenerExecutionException.java │ │ │ │ │ ├── SSchedulerException.java │ │ │ │ │ ├── failedJob/ │ │ │ │ │ │ └── SFailedJobReadException.java │ │ │ │ │ ├── jobDescriptor/ │ │ │ │ │ │ ├── SJobDescriptorCreationException.java │ │ │ │ │ │ ├── SJobDescriptorDeletionException.java │ │ │ │ │ │ ├── SJobDescriptorNotFoundException.java │ │ │ │ │ │ └── SJobDescriptorReadException.java │ │ │ │ │ ├── jobLog/ │ │ │ │ │ │ ├── SJobLogCreationException.java │ │ │ │ │ │ ├── SJobLogDeletionException.java │ │ │ │ │ │ ├── SJobLogNotFoundException.java │ │ │ │ │ │ ├── SJobLogReadException.java │ │ │ │ │ │ └── SJobLogUpdatingException.java │ │ │ │ │ └── jobParameter/ │ │ │ │ │ ├── SJobParameterCreationException.java │ │ │ │ │ ├── SJobParameterDeletionException.java │ │ │ │ │ ├── SJobParameterNotFoundException.java │ │ │ │ │ └── SJobParameterReadException.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── AbstractQuartzJob.java │ │ │ │ │ ├── BonitaJobStoreCMT.java │ │ │ │ │ ├── BonitaScheduler.java │ │ │ │ │ ├── BonitaSchedulerFactory.java │ │ │ │ │ ├── ConcurrentQuartzJob.java │ │ │ │ │ ├── JDBCJobListener.java │ │ │ │ │ ├── JobServiceImpl.java │ │ │ │ │ ├── JobWrapper.java │ │ │ │ │ ├── MonitoringJobListener.java │ │ │ │ │ ├── NonConcurrentQuartzJob.java │ │ │ │ │ ├── QuartzJobListener.java │ │ │ │ │ ├── QuartzSchedulerExecutor.java │ │ │ │ │ ├── SchedulerServiceImpl.java │ │ │ │ │ └── TransactionalSimpleJobFactory.java │ │ │ │ ├── model/ │ │ │ │ │ ├── SFailedJob.java │ │ │ │ │ ├── SJobData.java │ │ │ │ │ ├── SJobDescriptor.java │ │ │ │ │ ├── SJobLog.java │ │ │ │ │ ├── SJobParameter.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── SFailedJobImpl.java │ │ │ │ │ └── SJobDataImpl.java │ │ │ │ ├── recorder/ │ │ │ │ │ ├── JobDescriptorRecordType.java │ │ │ │ │ └── SelectDescriptorBuilder.java │ │ │ │ └── trigger/ │ │ │ │ ├── CronTrigger.java │ │ │ │ ├── OneShotTrigger.java │ │ │ │ ├── Trigger.java │ │ │ │ └── UnixCronTrigger.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── scheduler/ │ │ │ └── impl/ │ │ │ └── hibernate/ │ │ │ └── schedulerimpl.queries.hbm.xml │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── scheduler/ │ │ │ └── impl/ │ │ │ ├── AbstractQuartzJobTest.java │ │ │ ├── FailingJob.java │ │ │ ├── JDBCJobListenerTest.java │ │ │ ├── JobServiceImplForFailedJobTest.java │ │ │ ├── JobServiceImplForJobDescriptorTest.java │ │ │ ├── JobServiceImplForJobLogTest.java │ │ │ ├── JobServiceImplForJobParameterTest.java │ │ │ ├── JobUtils.java │ │ │ ├── JobWrapperTest.java │ │ │ ├── LogJob.java │ │ │ ├── MonitoringJobListenerTest.java │ │ │ ├── QuartzJobListenerTest.java │ │ │ ├── QuartzSchedulerExecutorTest.java │ │ │ ├── ReturnLogBuilder.java │ │ │ ├── SchedulerServiceImplTest.java │ │ │ └── TransactionalSimpleJobFactoryTest.java │ │ └── resources/ │ │ └── logback-test.xml │ ├── bonita-session/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ ├── session/ │ │ │ │ ├── SSessionAlreadyExistsException.java │ │ │ │ ├── SSessionException.java │ │ │ │ ├── SSessionNotFoundException.java │ │ │ │ ├── SessionProvider.java │ │ │ │ ├── SessionService.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── AbstractSessionProvider.java │ │ │ │ │ ├── SessionIdGenerator.java │ │ │ │ │ ├── SessionProviderImpl.java │ │ │ │ │ └── SessionServiceImpl.java │ │ │ │ └── model/ │ │ │ │ └── SSession.java │ │ │ └── sessionaccessor/ │ │ │ ├── ReadSessionAccessor.java │ │ │ ├── SessionAccessor.java │ │ │ ├── SessionIdNotSetException.java │ │ │ └── ThreadLocalSessionAccessor.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ ├── session/ │ │ │ └── impl/ │ │ │ ├── SessionProviderImplTest.java │ │ │ └── SessionServiceImplTest.java │ │ └── sessionaccessor/ │ │ └── ThreadLocalSessionAccessorTest.java │ ├── bonita-temporary-content/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── bonitasoft/ │ │ │ │ └── engine/ │ │ │ │ └── temporary/ │ │ │ │ └── content/ │ │ │ │ └── STemporaryContent.java │ │ │ └── resources/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── temporary/ │ │ │ └── content/ │ │ │ └── hibernate/ │ │ │ └── temporary.content.queries.hbm.xml │ │ └── test/ │ │ └── resources/ │ │ └── logback-test.xml │ ├── bonita-time-tracker/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── tracking/ │ │ │ ├── AbstractFlushEventListener.java │ │ │ ├── Clock.java │ │ │ ├── FlushEvent.java │ │ │ ├── FlushEventListener.java │ │ │ ├── FlushEventListenerResult.java │ │ │ ├── FlushResult.java │ │ │ ├── FlushThread.java │ │ │ ├── Record.java │ │ │ ├── ThreadSleepClockImpl.java │ │ │ ├── TimeTracker.java │ │ │ ├── TimeTrackerRecords.java │ │ │ ├── csv/ │ │ │ │ ├── CSVFlushEventListener.java │ │ │ │ ├── CSVFlushEventListenerResult.java │ │ │ │ └── CSVUtil.java │ │ │ └── memory/ │ │ │ ├── DayRecord.java │ │ │ └── MemoryFlushEventListener.java │ │ └── test/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── tracking/ │ │ ├── AbstractFlushEventListenerTest.java │ │ ├── AbstractTimeTrackerTest.java │ │ ├── FlushThreadTest.java │ │ ├── RecordAssert.java │ │ ├── ThreadSleepClockImplTest.java │ │ ├── TimeTrackerTest.java │ │ ├── csv/ │ │ │ └── CSVFlushEventListenerTest.java │ │ └── memory/ │ │ └── MemoryFlushEventListenerTest.java │ ├── bonita-transaction/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── transaction/ │ │ │ ├── BonitaTransactionSynchronization.java │ │ │ ├── JTATransactionServiceImpl.java │ │ │ ├── SBadTransactionStateException.java │ │ │ ├── STransactionCommitException.java │ │ │ ├── STransactionCreationException.java │ │ │ ├── STransactionException.java │ │ │ ├── STransactionNotFoundException.java │ │ │ ├── STransactionResourceException.java │ │ │ ├── STransactionRollbackException.java │ │ │ ├── TransactionService.java │ │ │ ├── UserTransactionService.java │ │ │ └── XAResourceRetriever.java │ │ └── test/ │ │ ├── java/ │ │ │ └── org/ │ │ │ └── bonitasoft/ │ │ │ └── engine/ │ │ │ └── transaction/ │ │ │ ├── JTATransactionServiceImplTest.java │ │ │ ├── TransactionLifeCycleTest.java │ │ │ ├── TransactionServiceTest.java │ │ │ ├── TransactionSynchronizationTest.java │ │ │ ├── XAResourceRetrieverTest.java │ │ │ └── synchronization/ │ │ │ ├── SimpleSynchronization.java │ │ │ ├── StaticSynchronization.java │ │ │ └── StaticSynchronizationResult.java │ │ └── resources/ │ │ └── logback-test.xml │ └── bonita-work/ │ ├── build.gradle │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── work/ │ │ ├── BonitaExecutorService.java │ │ ├── BonitaExecutorServiceFactory.java │ │ ├── BonitaRunnable.java │ │ ├── BonitaWork.java │ │ ├── BonitaWorkExecutorFactory.java │ │ ├── DefaultBonitaExecutorService.java │ │ ├── DefaultBonitaExecutorServiceFactory.java │ │ ├── DefaultExceptionRetryabilityEvaluator.java │ │ ├── ExceptionRetryabilityEvaluator.java │ │ ├── FailureCallback.java │ │ ├── LockException.java │ │ ├── LockTimeoutException.java │ │ ├── RetryingWorkExecutorService.java │ │ ├── SWorkException.java │ │ ├── SWorkPreconditionException.java │ │ ├── SWorkRegisterException.java │ │ ├── SuccessCallback.java │ │ ├── WorkDescriptor.java │ │ ├── WorkExecutionCallback.java │ │ ├── WorkExecutorService.java │ │ ├── WorkFactory.java │ │ ├── WorkService.java │ │ ├── WorkServiceImpl.java │ │ ├── WorkSingleThreadPoolExecutorFactory.java │ │ ├── WorkSynchronization.java │ │ ├── WorkerThreadFactory.java │ │ └── audit/ │ │ ├── AuditListener.java │ │ ├── ExecutionStatus.java │ │ └── WorkExecutionAuditor.java │ └── test/ │ ├── java/ │ │ └── org/ │ │ └── bonitasoft/ │ │ └── engine/ │ │ └── work/ │ │ ├── DefaultBonitaExecutorServiceFactoryTest.java │ │ ├── DefaultBonitaExecutorServiceTest.java │ │ ├── DefaultExceptionRetryabilityEvaluatorTest.java │ │ ├── RetryingWorkExecutorServiceTest.java │ │ ├── SingleThreadPoolExecutorTest.java │ │ ├── WorkServiceImplTest.java │ │ ├── WorkSynchronizationTest.java │ │ ├── WorkerThreadFactoryTest.java │ │ └── audit/ │ │ └── WorkExecutionAuditorTest.java │ └── resources/ │ └── logback-test.xml └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/build.yml ================================================ name: Build Bonita Community on: push: branches: - dev pull_request: types: [opened, synchronize, reopened] jobs: build: name: Build Bonita Community runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up JDK 17 uses: actions/setup-java@v4 with: distribution: temurin java-version: 17 - name: 🔧 Setup Gradle uses: gradle/actions/setup-gradle@v3 with: cache-read-only: false - name: ⚙️ Run Community build & ✔️ unit tests run: ./gradlew build - name: Run Integration tests run: ./gradlew iT - name: 🚀 Publish Test Report uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: files: '**/build/test-results/**/*.xml' ================================================ FILE: .gitignore ================================================ ### virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* ### OSX template .DS_Store .AppleDouble .LSOverride ### Maven template target pom.xml.tag pom.xml.releaseBackup pom.xml.versionsBackup pom.xml.next release.properties dependency-reduced-pom.xml buildNumber.properties .mvn/timing.properties ### Intellij *.iml .idea *.ipr *.iws .shelf ### Eclipse .project .settings .classpath ### Temporary files *.orig **/bin *.tmp *~ ### bonita template engine-sp-it.exec FILEPATH.*.db Temporary*.java generated h2databasedir ### gradle build .gradle ================================================ FILE: LICENSE.txt ================================================ GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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; either version 2.1 of the License, or (at your option) any later version. 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 library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! ================================================ FILE: README.md ================================================ # Bonita Engine [![Build](https://github.com/bonitasoft/bonita-engine/actions/workflows/build.yml/badge.svg)](https://github.com/bonitasoft/bonita-engine/actions/workflows/build.yml) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.bonitasoft.engine/bonita-server/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.bonitasoft.engine/bonita-server) Deploy, execute, manage applications made with Bonita Studio. ## Using the Engine The engine is included as part of either [Bonita Studio][downloads] or [Bonita Runtime][downloads], and executes the BPMN process logic. The engine can however be included as a standalone dependency in a custom Application, as explained [here][standalone] ## Running the Project ### Prerequisites > Java JDK 17 (to compile), and 17 (to run) This project bundles the [Gradle Wrapper][wrapper], so the `gradlew` script is available at the project root. ### Compiling Just run the following Gradle command: ``` ./gradlew build ``` To be able to successfully build other Bonita components that use the Engine, run: ``` ./gradlew publishToMavenLocal ``` Among other things, it also generates the javadoc used by Bonita Studio. The command above runs all unit tests. To skip them, add the `-x test` option. ### Version Version is declared in gradle.properties To override the current version on build, use the parameter **-Pversion** like: ``` ./gradlew -Pversion=7.9.3 ``` ### Extra repositories repositories can be added using comma separated list of repositories using property `extraRepositories` in format `repo_name::repo_url` credentials can be passed using properties `repo_nameUsername` and `repo_namePassword` it can be configured using `-PextraRepositories=` or gradle.properties file. example of gradle properties set in `~/.gradle/gradle.properties` ```properties extraRepositories=releases::https://repo1/releases,snapshots::https://repo2/snapshots/ releasesUsername=username releasesPassword=password snapshotsUsername=username snapshotsPassword=password ``` The same can be done for publishing repository (single repo) using property `altDeploymentRepository` ### Running unit / integration tests To run all **unit + integration tests** (on the default embedded H2 database), run the following command: ```bash ./gradlew test integrationTest ``` ### Test Retry Configuration The project uses the [Gradle Test Retry Plugin](https://plugins.gradle.org/plugin/org.gradle.test-retry) to automatically retry failed tests and detect flaky tests. #### Default Retry Behavior By default: - Failed tests are retried up to **2 more times** - Build fails if more than **6 tests** fail in one round - Flaky tests (pass on retry) fail the build locally but not in CI - Only applies to '*IT' test classes (tasks `integrationTest` and database's: `postgresDatabaseTest`, etc.) #### Overriding Retry Settings ```bash # Retry up to 5 times instead of 2 ./gradlew integrationTest -PtestRetryMaxRetries=5 # Retry and allow up to 20 failures before stopping ./gradlew integrationTest -PtestRetryMaxRetries=5 -PtestRetryMaxFailures=20 # Disable retry (set to 0) ./gradlew integrationTest -PtestRetryMaxRetries=0 # Don't fail on flaky tests (tests that pass on retry). Example for database tests: ./gradlew postgresDatabaseTest -PtestRetryFailOnFlaky=false ``` #### Important Note The test-retry plugin is designed to **detect** flaky tests, not to mask them. Always investigate and fix the root cause of flaky tests rather than just relying on retries. ## Project Structure The project is composed of several modules. Unit tests are contained in the modules, integration tests are regrouped in bonita-integration-tests. * `bonita-engine-spring-boot-starter` : Run the engine in standalone mode using Spring boot, see [documentation][standalone] * `bonita-engine-standalone` : Run the engine in standalone programmatically, see [documentation][standalone] * `bonita-test-api` : Junit Rule to include the engine in your tests * `bpm` : Services related to bpm process execution * `buildSrc` : Internal Gradle plugins used to build Bonita Engine * `platform` : Services that handle the platform creation/configuration * `services` : Generic services used by the engine ## How to contribute In order to contribute to the project, read the [guide][guide]. To report an issue use the official [bugtracker][bugtracker]. [downloads]: https://www.ofelia.com/downloads [standalone]: https://documentation.ofelia.com/bonita/latest/runtime/embed-engine [guide]: https://github.com/bonitasoft/bonita-developer-resources/blob/master/CONTRIBUTING.MD [wrapper]: https://docs.gradle.org/current/userguide/gradle_wrapper.html [bugtracker]: https://bonita.atlassian.net/projects/BBPMC/issues ================================================ FILE: bonita-engine/build.gradle ================================================ import org.bonitasoft.engine.gradle.PomUtils //this module is published to be imported as dependency management plugins { id "io.spring.dependency-management" version "1.1.0" } dependencyManagement { dependencies { imports { mavenBom libs.jacksonBom.get() as String mavenBom libs.groovyBom.get() as String mavenBom libs.bonitaArtifactsModelBom.get() as String } dependencySet(group: 'org.slf4j', version: libs.versions.slf4jVersion.get()) { entry 'slf4j-api' entry 'slf4j-jdk14' } dependency libs.h2.get() as String dependency libs.ehCache.get() as String dependencySet(group: 'org.springframework', version: libs.versions.springVersion.get()) { entry 'spring-context' entry 'spring-core' entry 'spring-web' } dependencySet(group: "org.springframework.boot", version: libs.versions.springBootVersion.get()) { entry "spring-boot-starter-jdbc" entry "spring-boot-autoconfigure" } dependency(libs.hibernateCore.get() as String) { exclude 'org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec' } dependency(libs.hibernateJCache.get() as String) dependency(libs.jcache.get() as String) dependency(libs.javaxPersistenceApi.get() as String) dependency libs.commonsIO.get() as String dependency libs.commonsFileUpload.get() as String dependency(libs.commonsBeanUtils.get() as String) { exclude 'commons-collections:commons-collections' } dependency(libs.commonsCLI.get() as String) dependency libs.commonsLang.get() as String dependency libs.commonsCollections.get() as String dependency libs.jakartaTransactionApi.get() as String dependency libs.javassist.get() as String dependencySet(group: 'org.apache.httpcomponents', version: libs.versions.httpComponentsVersion.get()) { entry 'httpclient' entry 'httpmime' } dependency libs.xstream.get() as String dependencySet(group: 'org.apache.tomcat', version: libs.versions.tomcatVersion.get()) { entry 'tomcat-dbcp' entry 'tomcat' entry 'tomcat-catalina' } dependencySet(group: 'org.apache.tomcat.embed', version: libs.versions.tomcatVersion.get()) { entry 'tomcat-embed-core' } dependency libs.narayanaJta.get() as String dependency libs.jakartaActivation.get() as String dependency(libs.quartz.get() as String) dependency(libs.eclipseCompiler.get() as String) dependency(libs.javaxAnnotations.get() as String) dependency(libs.micrometerCore.get() as String) dependency(libs.micrometerRegistryJmx.get() as String) dependency(libs.micrometerRegistryPrometheus.get() as String) dependency(libs.hazelcast.get() as String) dependency(libs.hazelcastSpring.get() as String) // declared here because it is used by web-extension and by distrib dependency(libs.jakartaServletApi.get() as String) // To be removed only when client projects (rest api extension) have all moved their deps to jakarta // Just keep a version managed to prevent client build breaks dependency(libs.javaxServletApi.get() as String) // Bonita engine dependencies: dependency "org.bonitasoft.engine:bonita-common:${project.version}" dependency "org.bonitasoft.engine:bonita-server:${project.version}" dependency "org.bonitasoft.engine:bonita-client:${project.version}" dependency "org.bonitasoft.engine:bonita-test-api:${project.version}" dependency "com.bonitasoft.engine:bonita-common-sp:${project.version}" dependency "com.bonitasoft.engine:bonita-server-sp:${project.version}" dependency "com.bonitasoft.engine:bonita-client-sp:${project.version}" // Web extensions dependencies: dependency "org.bonitasoft.console:bonita-web-server:${project.version}" dependency "org.bonitasoft.console:bonita-web-server-sp:${project.version}" dependency "org.bonitasoft.web:bonita-web-extensions:${project.version}" dependency "com.bonitasoft.web:bonita-web-extensions-sp:${project.version}" // Web layer specific dependencies: dependency(libs.xbeanClassloader.get() as String) dependency(libs.jakartaJstl.get() as String) dependency(libs.jakartaJstlApi.get() as String) dependency(libs.jgettext.get() as String) dependency(libs.urlrewritefilter.get() as String) dependency(libs.woodstoxCore.get() as String) dependency(libs.jsonSimple.get() as String) dependency(libs.keycloakSamlAdapterApiPublic.get() as String) dependency(libs.keycloakSamlServletFilterAdapter.get() as String) { exclude "org.bouncycastle:bcprov-jdk15on" exclude "org.bouncycastle:bcpkix-jdk15on" exclude "org.bouncycastle:bcutil-jdk15on" exclude "org.apache.santuario:xmlsec" } dependency(libs.keycloakAdapterCore.get() as String) { exclude "org.bouncycastle:bcprov-jdk15on" } dependency(libs.keycloakServletFilterAdapter.get() as String) { exclude "org.bouncycastle:bcprov-jdk15on" } dependency(libs.xmlsec.get() as String) dependency(libs.bouncyCastleBcprov.get() as String) dependency(libs.bouncyCastleBcpkix.get() as String) dependency(libs.bouncyCastleBcutil.get() as String) dependency(libs.spnego.get() as String) } } publishing { publications { maven(MavenPublication) { artifactId = 'bonita-engine' pom { pom -> name = "Bonita Engine" description = "Bonita Engine is a workflow engine which can be embedded inside your own applications" PomUtils.pomCommunityPublication(pom) } } } } ================================================ FILE: bonita-engine-spring-boot-starter/build.gradle ================================================ import org.bonitasoft.engine.gradle.PomUtils plugins { id 'java-library' id 'bonita-tests' } dependencies { api(project(":bpm:bonita-common")) api(project(":bpm:bonita-client")) api(libs.springBootStarter) { exclude(module: 'snakeyaml') } implementation(project(":bonita-engine-standalone")) implementation(project(":bpm:bonita-server")) annotationProcessor(libs.springBootConfigurationProcessor) testImplementation(libs.springBootTest) testImplementation(libs.assertj) } tasks.register("sourcesJar", Jar) { from sourceSets.main.allJava archiveClassifier = 'sources' } tasks.register("javadocJar", Jar) { from javadoc archiveClassifier = 'javadoc' } publishing { publications { mavenJava(MavenPublication) { from project.components.java artifact project.sourcesJar artifact project.javadocJar pom { pom -> name = "bonita-engine-spring-boot-starter" description = "bonita-engine-spring-boot-starter" PomUtils.pomCommunityPublication(pom) } } } } ================================================ FILE: bonita-engine-spring-boot-starter/src/main/java/org/bonitasoft/engine/spring/autoconfigure/BonitaEngineCommonAutoConfiguration.java ================================================ /** * 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.spring.autoconfigure; import org.bonitasoft.engine.spring.autoconfigure.properties.BonitaEngineProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan @EnableConfigurationProperties(BonitaEngineProperties.class) public class BonitaEngineCommonAutoConfiguration { } ================================================ FILE: bonita-engine-spring-boot-starter/src/main/java/org/bonitasoft/engine/spring/autoconfigure/BonitaEngineEventListener.java ================================================ /** * 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.spring.autoconfigure; import org.bonitasoft.engine.BonitaEngine; import org.bonitasoft.engine.event.PlatformStartedEvent; import org.springframework.beans.BeansException; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; /** * @author Emmanuel Duchastenier */ @Component public class BonitaEngineEventListener implements ApplicationContextAware { private ApplicationContext context; private BonitaEngine bonitaEngine; @EventListener public void handleApplicationReadyEvent(final ApplicationReadyEvent applicationReadyEvent) throws Exception { bonitaEngine = applicationReadyEvent.getApplicationContext().getBean(BonitaEngine.class); bonitaEngine.start(); // notify any Bonita application that the runtime is ready to accept requests: applicationReadyEvent.getApplicationContext().publishEvent(new PlatformStartedEvent()); } @EventListener public void handleContextStoppedEvent(final ContextClosedEvent contextClosedEvent) throws Exception { if (bonitaEngine != null && context == contextClosedEvent.getApplicationContext()) { bonitaEngine.stop(); } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } } ================================================ FILE: bonita-engine-spring-boot-starter/src/main/java/org/bonitasoft/engine/spring/autoconfigure/BonitaEngineServerAutoConfiguration.java ================================================ /** * 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.spring.autoconfigure; import org.bonitasoft.engine.BonitaEngine; import org.bonitasoft.engine.api.APIClient; import org.bonitasoft.engine.spring.autoconfigure.properties.BonitaEngineProperties; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; @Configuration @AutoConfigureBefore(BonitaEngineCommonAutoConfiguration.class) @AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE) public class BonitaEngineServerAutoConfiguration { @Bean @ConditionalOnMissingBean BonitaEngine bonitaEngine(BonitaEngineProperties properties) { BonitaEngine instance = new BonitaEngine(); instance.setBonitaDatabaseConfiguration(properties.getDatabase().getBonita()); instance.setBusinessDataDatabaseConfiguration(properties.getDatabase().getBusinessData()); return instance; } @Bean @ConditionalOnMissingBean APIClient bonitaClient() { return new APIClient(); } } ================================================ FILE: bonita-engine-spring-boot-starter/src/main/java/org/bonitasoft/engine/spring/autoconfigure/properties/BonitaDatabasesConfiguration.java ================================================ /** * 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.spring.autoconfigure.properties; import org.bonitasoft.engine.BonitaDatabaseConfiguration; import org.springframework.boot.context.properties.NestedConfigurationProperty; public class BonitaDatabasesConfiguration { @NestedConfigurationProperty private BonitaDatabaseConfiguration bonita = new BonitaDatabaseConfiguration(); @NestedConfigurationProperty private BonitaDatabaseConfiguration businessData = new BonitaDatabaseConfiguration(); public BonitaDatabaseConfiguration getBonita() { return bonita; } public BonitaDatabaseConfiguration getBusinessData() { return businessData; } } ================================================ FILE: bonita-engine-spring-boot-starter/src/main/java/org/bonitasoft/engine/spring/autoconfigure/properties/BonitaEngineProperties.java ================================================ /** * 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.spring.autoconfigure.properties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.NestedConfigurationProperty; @ConfigurationProperties(prefix = "org.bonitasoft.engine") public class BonitaEngineProperties { @NestedConfigurationProperty private BonitaDatabasesConfiguration database = new BonitaDatabasesConfiguration(); public BonitaDatabasesConfiguration getDatabase() { return database; } } ================================================ FILE: bonita-engine-spring-boot-starter/src/main/resources/META-INF/spring.factories ================================================ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.bonitasoft.engine.spring.autoconfigure.BonitaEngineServerAutoConfiguration,\ org.bonitasoft.engine.spring.autoconfigure.BonitaEngineCommonAutoConfiguration ================================================ FILE: bonita-engine-spring-boot-starter/src/test/java/org/bonitasoft/engine/spring/autoconfigure/BonitaEngineAutoConfigurationTest.java ================================================ /** * 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.spring.autoconfigure; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.BonitaDatabaseConfiguration; import org.bonitasoft.engine.BonitaEngine; import org.junit.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; public class BonitaEngineAutoConfigurationTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(BonitaEngineCommonAutoConfiguration.class, BonitaEngineServerAutoConfiguration.class)); @Test public void should_configure_database_using_properties() { this.contextRunner .withPropertyValues( "org.bonitasoft.engine.database.bonita.db-vendor=postgres", "org.bonitasoft.engine.database.bonita.url=myServerUrl", "org.bonitasoft.engine.database.bonita.driver-class-name=my.Driver", "org.bonitasoft.engine.database.bonita.user=myUser", "org.bonitasoft.engine.database.bonita.password=secret", "org.bonitasoft.engine.database.bonita.datasource.max-pool-size=3", "org.bonitasoft.engine.database.bonita.xa-datasource.max-pool-size=4", "org.bonitasoft.engine.database.business-data.datasource.max-pool-size=5", "org.bonitasoft.engine.database.business-data.xa-datasource.max-pool-size=6", "org.bonitasoft.engine.database.business-data.db-vendor=mysql") .run((context) -> { BonitaEngine engine = context.getBean(BonitaEngine.class); BonitaDatabaseConfiguration bonitaDatabaseConfiguration = engine.getBonitaDatabaseConfiguration(); assertThat(bonitaDatabaseConfiguration.getDbVendor()).isEqualTo("postgres"); assertThat(bonitaDatabaseConfiguration.getUrl()).isEqualTo("myServerUrl"); assertThat(bonitaDatabaseConfiguration.getDriverClassName()).isEqualTo("my.Driver"); assertThat(bonitaDatabaseConfiguration.getUser()).isEqualTo("myUser"); assertThat(bonitaDatabaseConfiguration.getPassword()).isEqualTo("secret"); assertThat(bonitaDatabaseConfiguration.getDatasource().getMaxPoolSize()).isEqualTo(3); assertThat(bonitaDatabaseConfiguration.getXaDatasource().getMaxPoolSize()).isEqualTo(4); BonitaDatabaseConfiguration businessDataDatabaseConfiguration = engine .getBusinessDataDatabaseConfiguration(); assertThat(businessDataDatabaseConfiguration.getDatasource().getMaxPoolSize()).isEqualTo(5); assertThat(businessDataDatabaseConfiguration.getXaDatasource().getMaxPoolSize()).isEqualTo(6); assertThat( businessDataDatabaseConfiguration.getDbVendor()) .isEqualTo("mysql"); }); } } ================================================ FILE: bonita-engine-spring-boot-starter/src/test/java/org/bonitasoft/engine/spring/autoconfigure/BonitaEngineSpringBootStarterIT.java ================================================ /** * 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.spring.autoconfigure; import org.bonitasoft.engine.api.APIClient; import org.bonitasoft.engine.platform.LoginException; import org.junit.Test; import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext; public class BonitaEngineSpringBootStarterIT { @Test public void should_start_engine_when_application_starts() throws LoginException { final ConfigurableApplicationContext context = SpringApplication.run(ClientTestApplication.class); context.getBean(APIClient.class).login("install", "install"); context.stop(); } } ================================================ FILE: bonita-engine-spring-boot-starter/src/test/java/org/bonitasoft/engine/spring/autoconfigure/ClientTestApplication.java ================================================ /** * 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.spring.autoconfigure; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @author Emmanuel Duchastenier */ @SpringBootApplication public class ClientTestApplication { } ================================================ FILE: bonita-engine-standalone/build.gradle ================================================ import org.bonitasoft.engine.gradle.PomUtils plugins { id "java-library" } dependencies { api(project(":bpm:bonita-client")) implementation(project(":bpm:bonita-server")) implementation(project(':platform:platform-resources')) implementation libs.springContext implementation libs.narayanaJta implementation libs.tomcatDbcp implementation libs.slf4jApi runtimeOnly libs.h2 annotationProcessor libs.lombok compileOnly libs.lombok testImplementation libs.assertj testImplementation libs.systemRules // These 3 drivers are used for some specific tests: testImplementation(libs.postgresql) testImplementation(libs.mysql) testImplementation(libs.h2) } tasks.register("sourcesJar", Jar) { from sourceSets.main.allJava archiveClassifier = 'sources' } tasks.register("javadocJar", Jar) { from javadoc archiveClassifier = 'javadoc' } publishing { publications { mavenJava(MavenPublication) { from project.components.java artifact project.sourcesJar artifact project.javadocJar pom { pom -> name = "Bonita Engine Standalone" description = "Bonita Engine Standalone is the library to easily embed Bonita Engine is your applications" PomUtils.pomCommunityPublication(pom) } } } } ================================================ FILE: bonita-engine-standalone/src/main/java/org/bonitasoft/engine/BonitaDataSourceInitializer.java ================================================ /** * 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; import static org.bonitasoft.engine.xa.XADataSourceIsSameRMOverride.overrideSameRM; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.naming.Reference; import javax.naming.StringRefAddr; import javax.naming.spi.ObjectFactory; import javax.sql.XADataSource; import javax.transaction.TransactionManager; import org.apache.tomcat.dbcp.dbcp2.BasicDataSource; import org.apache.tomcat.dbcp.dbcp2.managed.BasicManagedDataSource; class BonitaDataSourceInitializer { private static final String H2 = "h2"; private static final String MYSQL = "mysql"; private static final String ORACLE = "oracle"; private static final String POSTGRES = "postgres"; private static final String SQLSERVER = "sqlserver"; private static final Map defaultDriver; private static final Map defaultXADataSourceNames; private static final Map defaultXADataSourceFactories; private static final Map defaultTestQueries; static { HashMap drivers = new HashMap<>(); drivers.put(H2, "org.h2.Driver"); drivers.put(MYSQL, "com.mysql.cj.jdbc.Driver"); drivers.put(ORACLE, "oracle.jdbc.OracleDriver"); drivers.put(POSTGRES, "org.postgresql.Driver"); drivers.put(SQLSERVER, "com.microsoft.sqlserver.jdbc.SQLServerDriver"); defaultDriver = Collections.unmodifiableMap(drivers); HashMap xaDataSourceNames = new HashMap<>(); xaDataSourceNames.put(H2, "org.h2.jdbcx.JdbcDataSource"); xaDataSourceNames.put(MYSQL, "com.mysql.cj.jdbc.MysqlXADataSource"); xaDataSourceNames.put(ORACLE, "oracle.jdbc.xa.client.OracleXADataSource"); xaDataSourceNames.put(POSTGRES, "org.postgresql.xa.PGXADataSource"); xaDataSourceNames.put(SQLSERVER, "com.microsoft.sqlserver.jdbc.SQLServerXADataSource"); defaultXADataSourceNames = Collections.unmodifiableMap(xaDataSourceNames); HashMap xaDataSourceFactories = new HashMap<>(); xaDataSourceFactories.put(H2, "org.h2.jdbcx.JdbcDataSourceFactory"); xaDataSourceFactories.put(MYSQL, "com.mysql.cj.jdbc.MysqlDataSourceFactory"); xaDataSourceFactories.put(ORACLE, "oracle.jdbc.pool.OracleDataSourceFactory"); xaDataSourceFactories.put(POSTGRES, "org.postgresql.xa.PGXADataSourceFactory"); xaDataSourceFactories.put(SQLSERVER, "com.microsoft.sqlserver.jdbc.SQLServerDataSourceObjectFactory"); defaultXADataSourceFactories = Collections.unmodifiableMap(xaDataSourceFactories); HashMap testQueries = new HashMap<>(); testQueries.put(H2, "SELECT 1"); testQueries.put(MYSQL, "SELECT 1"); testQueries.put(ORACLE, "SELECT 1 FROM DUAL"); testQueries.put(POSTGRES, "SELECT 1"); testQueries.put(SQLSERVER, "SELECT 1"); defaultTestQueries = Collections.unmodifiableMap(testQueries); } BasicManagedDataSource createManagedDataSource(BonitaDatabaseConfiguration configuration, TransactionManager transactionManager) throws Exception { validate(configuration); String dbVendor = configuration.getDbVendor(); String xaDatasourceClass = defaultXADataSourceNames.get(dbVendor); String xaDatasourceFactoryClass = defaultXADataSourceFactories.get(dbVendor); //create a 'native' xa datasources ObjectFactory datasourceFactory = (ObjectFactory) Class.forName(xaDatasourceFactoryClass).getConstructor() .newInstance(); Reference reference = new Reference(xaDatasourceClass); String description = "RawDataSource of " + dbVendor; reference.add(new StringRefAddr("description", description)); reference.add(new StringRefAddr("closeMethod", "close")); reference.add(new StringRefAddr("loginTimeout", "0")); if (dbVendor.equals(POSTGRES)) { DatabaseUrlParser.DatabaseMetadata metadata = DatabaseUrlParser.parsePostgresUrl(configuration.getUrl()); reference.add(new StringRefAddr("serverName", metadata.getServerName())); reference.add(new StringRefAddr("portNumber", metadata.getPort())); reference.add(new StringRefAddr("databaseName", metadata.getDatabaseName())); } else if (dbVendor.equals(SQLSERVER)) { reference.add(new StringRefAddr("dataSourceURL", configuration.getUrl())); reference.add(new StringRefAddr("dataSourceDescription", description)); reference.add(new StringRefAddr("class", xaDatasourceClass)); } else { reference.add(new StringRefAddr("explicitUrl", "true")); reference.add(new StringRefAddr("url", configuration.getUrl())); } reference.add(new StringRefAddr("user", configuration.getUser())); reference.add(new StringRefAddr("password", configuration.getPassword())); XADataSource xaDataSource = (XADataSource) datasourceFactory.getObjectInstance(reference, null, null, null); BasicManagedDataSource bonitaDataSource = new BasicManagedDataSource(); bonitaDataSource.setDefaultAutoCommit(false); bonitaDataSource.setRemoveAbandonedOnBorrow(true); bonitaDataSource.setRemoveAbandonedOnMaintenance(true); bonitaDataSource.setLogAbandoned(false); bonitaDataSource.setTestOnBorrow(true); bonitaDataSource.setValidationQuery(defaultTestQueries.get(dbVendor)); bonitaDataSource.setTransactionManager(transactionManager); bonitaDataSource.setInitialSize(1); bonitaDataSource.setTestWhileIdle(false); bonitaDataSource.setTimeBetweenEvictionRunsMillis(60000); bonitaDataSource.setMinEvictableIdleTimeMillis(600000); if (dbVendor.equals(ORACLE)) { bonitaDataSource.setXaDataSourceInstance(overrideSameRM(xaDataSource)); } else { bonitaDataSource.setXaDataSourceInstance(xaDataSource); } configureDatasource(configuration.getXaDatasource(), bonitaDataSource); return bonitaDataSource; } private String getDriverClassName(BonitaDatabaseConfiguration configuration, String driver) { String driverClassName; if (driver == null || driver.isEmpty()) { driverClassName = defaultDriver.get(configuration.getDbVendor()); } else { driverClassName = driver; } return driverClassName; } private void validate(BonitaDatabaseConfiguration configuration) { checkNullOrEmpty(configuration.getDbVendor(), "dbVendor"); if (!defaultDriver.containsKey(configuration.getDbVendor())) { throw new IllegalArgumentException(String.format("Database db vendor %s is invalid ( should be one of %s )", configuration.getDbVendor(), defaultDriver.keySet())); } checkNullOrEmpty(configuration.getUrl(), "url"); checkNullOrEmpty(configuration.getUser(), "user"); } private static void checkNullOrEmpty(String field, String fieldName) { if (field == null || field.isEmpty()) { throw new IllegalArgumentException("Database " + fieldName + " not set"); } } BasicDataSource createDataSource(BonitaDatabaseConfiguration configuration) { validate(configuration); BasicDataSource dataSource = new BasicDataSource(); configureDatasource(configuration.getDatasource(), dataSource); dataSource.setInitialSize(1); dataSource.setDriverClassName(getDriverClassName(configuration, configuration.getDriverClassName())); dataSource.setUrl(configuration.getUrl()); dataSource.setUsername(configuration.getUser()); dataSource.setPassword(configuration.getPassword()); return dataSource; } private void configureDatasource(DatasourceConfiguration configuration, BasicDataSource dataSource) { if (configuration != null && configuration.getMaxPoolSize() > 0) { dataSource.setMaxTotal(configuration.getMaxPoolSize()); } else { dataSource.setMaxTotal(7); } } } ================================================ FILE: bonita-engine-standalone/src/main/java/org/bonitasoft/engine/BonitaDatabaseConfiguration.java ================================================ /** * 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; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; @Data @ToString(exclude = "password") @NoArgsConstructor @AllArgsConstructor @Builder(toBuilder = true) public class BonitaDatabaseConfiguration { private String driverClassName; private String url; private String dbVendor; private String user; private String password; private DatasourceConfiguration xaDatasource; private DatasourceConfiguration datasource; public boolean isEmpty() { return isNullOrEmpty(driverClassName) && isNullOrEmpty(url) && isNullOrEmpty(dbVendor) && isNullOrEmpty(user) && isNullOrEmpty(password); } private boolean isNullOrEmpty(String s) { return s == null || s.isEmpty(); } /** * The name of the driver class, if the default one does not suit your needs * * @param driverClassName the name of the driver class to use */ public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } /** * The URL to connect to the database * * @param url the URL to connect to the database */ public void setUrl(String url) { this.url = url; } /** * The database vendor to use with Bonita Engine. * Supported values are h2, mysql, postgres, sqlserver, oracle. Default value, if not specified, is h2. * * @param dbVendor the database vendor to use with Bonita Engine */ public void setDbVendor(String dbVendor) { this.dbVendor = dbVendor; } /** * The connection user name to use to access the Bonita database * * @param user the connection user name to use to access the Bonita database */ public void setUser(String user) { this.user = user; } /** * The connection password corresponding to the specified user name * * @param password the connection password corresponding to the specified user name */ public void setPassword(String password) { this.password = password; } } ================================================ FILE: bonita-engine-standalone/src/main/java/org/bonitasoft/engine/BonitaEngine.java ================================================ /** * 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; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.naming.NamingException; import com.arjuna.ats.jta.TransactionManager; import com.arjuna.ats.jta.UserTransaction; import lombok.extern.slf4j.Slf4j; import org.apache.tomcat.dbcp.dbcp2.BasicDataSource; import org.apache.tomcat.dbcp.dbcp2.managed.BasicManagedDataSource; import org.bonitasoft.engine.api.ApiAccessType; import org.bonitasoft.engine.api.PlatformAPI; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.platform.PlatformLoginException; import org.bonitasoft.engine.platform.PlatformLogoutException; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.engine.session.SessionNotFoundException; import org.bonitasoft.engine.util.APITypeManager; import org.bonitasoft.platform.setup.PlatformSetup; import org.bonitasoft.platform.setup.PlatformSetupAccessor; import org.springframework.jndi.JndiTemplate; @Slf4j public class BonitaEngine { private final BonitaDataSourceInitializer bonitaDataSourceInitializer = new BonitaDataSourceInitializer(); private boolean initialized; private BonitaDatabaseConfiguration bonitaDatabaseConfiguration; private BonitaDatabaseConfiguration businessDataDatabaseConfiguration; private MemoryJNDISetup memoryJNDISetup; private BasicManagedDataSource bonitaDataSource; private BasicManagedDataSource businessDataDataSource; private BasicDataSource bonitaSequenceManagerDataSource; private BasicDataSource notManagedBizDataSource; private javax.transaction.UserTransaction userTransaction; private javax.transaction.TransactionManager arjunaTransactionManager; public static final String BONITA_BDM_DB_VENDOR = PlatformSetup.BONITA_BDM_DB_VENDOR_PROPERTY; public static final String BONITA_DB_VENDOR = PlatformSetup.BONITA_DB_VENDOR_PROPERTY; public void initializeEnvironment() throws Exception { if (!initialized) { initialized = true; APITypeManager.setAPITypeAndParams(ApiAccessType.LOCAL, Collections.emptyMap()); initializeBonitaDatabaseConfiguration(); initializeBusinessDataDatabaseConfiguration(); arjunaTransactionManager = TransactionManager.transactionManager(); userTransaction = UserTransaction.userTransaction(); bonitaDataSource = bonitaDataSourceInitializer.createManagedDataSource(bonitaDatabaseConfiguration, arjunaTransactionManager); businessDataDataSource = bonitaDataSourceInitializer .createManagedDataSource(businessDataDatabaseConfiguration, arjunaTransactionManager); bonitaSequenceManagerDataSource = bonitaDataSourceInitializer.createDataSource(bonitaDatabaseConfiguration); notManagedBizDataSource = bonitaDataSourceInitializer.createDataSource(businessDataDatabaseConfiguration); initializeJNDI(); } } private void initializeBonitaDatabaseConfiguration() { if (bonitaDatabaseConfiguration == null || bonitaDatabaseConfiguration.isEmpty()) { bonitaDatabaseConfiguration = DefaultBonitaDatabaseConfigurations .defaultConfiguration(System.getProperty(BONITA_DB_VENDOR, "h2"), "bonita"); } setSystemPropertyIfNotSet(BONITA_DB_VENDOR, bonitaDatabaseConfiguration.getDbVendor()); log.info("Using database configuration for bonita {}", bonitaDatabaseConfiguration); } private void initializeBusinessDataDatabaseConfiguration() { if (businessDataDatabaseConfiguration == null || businessDataDatabaseConfiguration.isEmpty()) { businessDataDatabaseConfiguration = DefaultBonitaDatabaseConfigurations .defaultConfiguration(System.getProperty(BONITA_BDM_DB_VENDOR, "h2"), "business_data"); } setSystemPropertyIfNotSet(BONITA_BDM_DB_VENDOR, businessDataDatabaseConfiguration.getDbVendor()); log.info("Using database configuration for business data {}", businessDataDatabaseConfiguration); } private void setSystemPropertyIfNotSet(String systemPropertyName, String defaultValue) { String value = System.getProperty(systemPropertyName); if (value != null) { return; } System.setProperty(systemPropertyName, defaultValue); } private void initializeJNDI() throws NamingException { Map jndiMapping = new HashMap<>(); jndiMapping.put("java:comp/env/bonitaDS", bonitaDataSource); jndiMapping.put("java:comp/env/bonitaSequenceManagerDS", bonitaSequenceManagerDataSource); jndiMapping.put("java:comp/env/BusinessDataDS", businessDataDataSource); jndiMapping.put("java:comp/env/NotManagedBizDataDS", notManagedBizDataSource); jndiMapping.put("java:comp/env/TransactionManager", arjunaTransactionManager); jndiMapping.put("java:comp/UserTransaction", userTransaction); JndiTemplate jndiTemplate = new JndiTemplate(); memoryJNDISetup = new MemoryJNDISetup(jndiTemplate, jndiMapping); memoryJNDISetup.init(); } public void start() throws Exception { initializeEnvironment(); PlatformSetup platformSetup = getPlatformSetup(); platformSetup.init(); PlatformSession platformSession = loginOnPlatform(); final PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(platformSession); platformAPI.startNode(); logoutFromPlatform(platformSession); } protected PlatformSetup getPlatformSetup() throws NamingException { return PlatformSetupAccessor.getInstance().getPlatformSetup(); } private void logoutFromPlatform(PlatformSession platformSession) throws PlatformLogoutException, SessionNotFoundException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { PlatformAPIAccessor.getPlatformLoginAPI().logout(platformSession); } private PlatformSession loginOnPlatform() throws PlatformLoginException { return new LocalLoginMechanism().login(); } public void stop() throws Exception { PlatformSession platformSession = loginOnPlatform(); final PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(platformSession); if (platformAPI.isNodeStarted()) { platformAPI.stopNode(); } logoutFromPlatform(platformSession); memoryJNDISetup.clean(); } public BonitaDatabaseConfiguration getBonitaDatabaseConfiguration() { return bonitaDatabaseConfiguration; } public void setBonitaDatabaseConfiguration(BonitaDatabaseConfiguration database) { this.bonitaDatabaseConfiguration = database; } public BonitaDatabaseConfiguration getBusinessDataDatabaseConfiguration() { return businessDataDatabaseConfiguration; } public void setBusinessDataDatabaseConfiguration(BonitaDatabaseConfiguration businessDataDatabaseConfiguration) { this.businessDataDatabaseConfiguration = businessDataDatabaseConfiguration; } BasicManagedDataSource getBonitaDataSource() { return bonitaDataSource; } BasicManagedDataSource getBusinessDataDataSource() { return businessDataDataSource; } } ================================================ FILE: bonita-engine-standalone/src/main/java/org/bonitasoft/engine/DatabaseUrlParser.java ================================================ /** * Copyright (C) 2020 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; import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.AllArgsConstructor; import lombok.Data; public class DatabaseUrlParser { @Data @AllArgsConstructor public static class DatabaseMetadata { private String serverName; private String port; private String databaseName; } public static DatabaseMetadata parsePostgresUrl(String url) { String regex = "jdbc:postgresql://([\\w\\d\\.-]+):(\\d+)/([\\w\\-_\\d]+).*"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(url); if (!matcher.find()) { throw new IllegalArgumentException( "Unable to parse postgres url (no groups found): " + url + " using regex " + regex); } return new DatabaseMetadata(matcher.group(1), matcher.group(2), matcher.group(3)); } } ================================================ FILE: bonita-engine-standalone/src/main/java/org/bonitasoft/engine/DatasourceConfiguration.java ================================================ /** * 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; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; @Data @Builder @NoArgsConstructor @AllArgsConstructor public class DatasourceConfiguration { /** * Maximum number of connections in the pool * Must be more than 0 * Default value is 7 */ private int maxPoolSize; } ================================================ FILE: bonita-engine-standalone/src/main/java/org/bonitasoft/engine/DefaultBonitaDatabaseConfigurations.java ================================================ /** * 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; public class DefaultBonitaDatabaseConfigurations { private static BonitaDatabaseConfiguration defaultH2Configuration(String schemaName) { return BonitaDatabaseConfiguration.builder() .dbVendor("h2") .url("jdbc:h2:file:" + System.getProperty("org.bonitasoft.h2.database.dir", "./h2databasedir") + "/" + schemaName + ";DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE") .user("bonita") .password("bpm").build(); } private static BonitaDatabaseConfiguration defaultPostgresConfiguration(String schemaName) { return BonitaDatabaseConfiguration.builder() .dbVendor("postgres") .url("jdbc:postgresql://localhost:5432/" + schemaName) .user("bonita") .password("bpm").build(); } private static BonitaDatabaseConfiguration defaultMysqlConfiguration(String schemaName) { return BonitaDatabaseConfiguration.builder() .dbVendor("mysql") .url("jdbc:mysql://localhost:3306/" + schemaName + "?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true") .user("bonita") .password("bpm").build(); } private static BonitaDatabaseConfiguration defaultOracleConfiguration(String schemaName) { return BonitaDatabaseConfiguration.builder() .dbVendor("oracle") .url("jdbc:oracle:thin:@//localhost:1521/FREEPDB1?oracle.net.disableOob=true") .user(schemaName) .password("bpm").build(); } private static BonitaDatabaseConfiguration defaultSqlserverConfiguration(String schemaName) { return BonitaDatabaseConfiguration.builder() .dbVendor("sqlserver") .url("jdbc:sqlserver://localhost:1433;database=" + schemaName) .user("bonita") .password("bpm").build(); } public static BonitaDatabaseConfiguration defaultConfiguration(String dbVendor, String schemaName) { switch (dbVendor) { case "h2": //clone it return defaultH2Configuration(schemaName); case "postgres": return defaultPostgresConfiguration(schemaName); case "mysql": return defaultMysqlConfiguration(schemaName); case "oracle": return defaultOracleConfiguration(schemaName); case "sqlserver": return defaultSqlserverConfiguration(schemaName); default: throw new IllegalArgumentException("dbVendor " + dbVendor + " is not valid"); } } } ================================================ FILE: bonita-engine-standalone/src/main/java/org/bonitasoft/engine/MemoryJNDISetup.java ================================================ /** * Copyright (C) 2016 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; import java.util.Map; import javax.naming.Context; import javax.naming.NameAlreadyBoundException; import javax.naming.NamingException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jndi.JndiTemplate; public class MemoryJNDISetup { private final JndiTemplate jndiTemplate; private final Map jndiMapping; private final Logger logger = LoggerFactory.getLogger(MemoryJNDISetup.class.getSimpleName()); public MemoryJNDISetup(final JndiTemplate jndiTemplate, final Map jndiMapping) { super(); if (System.getProperty(Context.INITIAL_CONTEXT_FACTORY) == null) { System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.bonitasoft.engine.SimpleMemoryContextFactory"); } System.setProperty(Context.URL_PKG_PREFIXES, "org.bonitasoft.engine"); this.jndiTemplate = jndiTemplate; this.jndiMapping = jndiMapping; } public void init() throws NamingException { for (final Map.Entry addToJndi : jndiMapping.entrySet()) { logger.info("Binding " + addToJndi.getKey() + " @ " + addToJndi.getValue()); try { jndiTemplate.bind(addToJndi.getKey(), addToJndi.getValue()); } catch (NameAlreadyBoundException ignored) { logger.info(addToJndi.getKey() + " @ " + addToJndi.getValue() + " was already bound"); } } } public void clean() throws NamingException { for (final Map.Entry removeFromJndi : jndiMapping.entrySet()) { logger.info("Unbinding " + removeFromJndi.getKey()); jndiTemplate.unbind(removeFromJndi.getKey()); } } } ================================================ FILE: bonita-engine-standalone/src/main/java/org/bonitasoft/engine/SimpleMemoryContext.java ================================================ /** * Copyright (C) 2016 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; import java.util.Hashtable; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.naming.Binding; import javax.naming.Context; import javax.naming.Name; import javax.naming.NameAlreadyBoundException; import javax.naming.NameClassPair; import javax.naming.NameNotFoundException; import javax.naming.NameParser; import javax.naming.NamingEnumeration; import javax.naming.NamingException; /** * A JNDI context implementation that uses the memory as a dictionary of objects. */ public class SimpleMemoryContext implements Context { private static final String NOT_SUPPORTED_YET = "Not supported yet."; private final Map dictionary = new ConcurrentHashMap(); public void clear() { dictionary.clear(); } @Override public Object lookup(final Name name) throws NamingException { return lookup(name.toString()); } @Override public Object lookup(final String name) throws NamingException { if (dictionary.containsKey(name)) { return dictionary.get(name); } throw new NameNotFoundException("Name " + name + " is not bound !"); } @Override public void bind(final Name name, final Object o) throws NamingException { bind(name.toString(), o); } @Override public void bind(final String name, final Object o) throws NamingException { if (dictionary.containsKey(name)) { throw new NameAlreadyBoundException("Name " + name + " already bound!"); } rebind(name, o); } @Override public void rebind(final Name name, final Object o) { rebind(name.toString(), o); } @Override public void rebind(final String name, final Object o) { dictionary.put(name, o); } @Override public void unbind(final Name name) throws NamingException { unbind(name.toString()); } @Override public void unbind(final String name) throws NamingException { if (!dictionary.containsKey(name)) { throw new NameNotFoundException("No such name " + name + " is bound!"); } dictionary.remove(name); } @Override public void rename(final Name oldName, final Name newName) throws NamingException { rename(oldName.toString(), newName.toString()); } @Override public void rename(final String oldName, final String newName) throws NamingException { final Object object = lookup(oldName); bind(newName, object); unbind(oldName); } @Override public NamingEnumeration list(final Name name) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public NamingEnumeration list(final String string) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public NamingEnumeration listBindings(final Name name) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public NamingEnumeration listBindings(final String string) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public void destroySubcontext(final Name name) { destroySubcontext(name.toString()); } @Override public void destroySubcontext(final String name) { dictionary.remove(name); } @Override public Context createSubcontext(final Name name) throws NamingException { return createSubcontext(name.toString()); } @Override public Context createSubcontext(final String name) throws NamingException { final Context subContext = new SimpleMemoryContext(); bind(name, subContext); return subContext; } @Override public Object lookupLink(final Name name) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public Object lookupLink(final String string) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public NameParser getNameParser(final Name name) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public NameParser getNameParser(final String name) { return new SimpleNameParser(name); } @Override public Name composeName(final Name name, final Name name1) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public String composeName(final String string, final String string1) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public Object addToEnvironment(final String string, final Object o) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public Object removeFromEnvironment(final String string) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public Hashtable getEnvironment() { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public void close() { // Thread.dumpStack(); // clear(); } @Override public String getNameInNamespace() { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } } ================================================ FILE: bonita-engine-standalone/src/main/java/org/bonitasoft/engine/SimpleMemoryContextFactory.java ================================================ /** * Copyright (C) 2016 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; import java.util.Hashtable; import javax.naming.Context; import javax.naming.spi.InitialContextFactory; /** * A factory of a naming context that uses the memory as dictionary of objects. Useful to tests * objects using JNDI to get dependencies. */ public class SimpleMemoryContextFactory implements InitialContextFactory { private static final SimpleMemoryContext context = new SimpleMemoryContext(); @Override public Context getInitialContext(final Hashtable environment) { return context; } } ================================================ FILE: bonita-engine-standalone/src/main/java/org/bonitasoft/engine/SimpleNameParser.java ================================================ /** * Copyright (C) 2016 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; import javax.naming.CompositeName; import javax.naming.Name; import javax.naming.NameParser; import javax.naming.NamingException; public class SimpleNameParser implements NameParser { public SimpleNameParser(final String name) { } @Override public Name parse(final String name) throws NamingException { return new CompositeName(name); } } ================================================ FILE: bonita-engine-standalone/src/main/java/org/bonitasoft/engine/xa/XAConnectionIsSameRMOverride.java ================================================ /** * Copyright (C) 2020 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.xa; import java.sql.Connection; import java.sql.SQLException; import javax.sql.ConnectionEventListener; import javax.sql.StatementEventListener; import javax.sql.XAConnection; import javax.transaction.xa.XAResource; public class XAConnectionIsSameRMOverride implements XAConnection { private XAConnection xaConnection; private XAConnectionIsSameRMOverride(XAConnection xaConnection) { this.xaConnection = xaConnection; } static XAConnection overrideSameRM(XAConnection xaConnection) { return new XAConnectionIsSameRMOverride(xaConnection); } @Override public XAResource getXAResource() throws SQLException { return XAResourceIsSameRMOverride.overrideSameRM(xaConnection.getXAResource()); } @Override public Connection getConnection() throws SQLException { return xaConnection.getConnection(); } @Override public void close() throws SQLException { xaConnection.close(); } @Override public void addConnectionEventListener(ConnectionEventListener listener) { xaConnection.addConnectionEventListener(listener); } @Override public void removeConnectionEventListener(ConnectionEventListener listener) { xaConnection.removeConnectionEventListener(listener); } @Override public void addStatementEventListener(StatementEventListener listener) { xaConnection.addStatementEventListener(listener); } @Override public void removeStatementEventListener(StatementEventListener listener) { xaConnection.removeStatementEventListener(listener); } } ================================================ FILE: bonita-engine-standalone/src/main/java/org/bonitasoft/engine/xa/XADataSourceIsSameRMOverride.java ================================================ /** * Copyright (C) 2020 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.xa; import java.io.PrintWriter; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.logging.Logger; import javax.sql.XAConnection; import javax.sql.XADataSource; public class XADataSourceIsSameRMOverride implements XADataSource { private XADataSource xaDataSource; private XADataSourceIsSameRMOverride(XADataSource xaDataSource) { this.xaDataSource = xaDataSource; } public static XADataSource overrideSameRM(XADataSource xaDataSource) { return new XADataSourceIsSameRMOverride(xaDataSource); } @Override public XAConnection getXAConnection() throws SQLException { return XAConnectionIsSameRMOverride.overrideSameRM(xaDataSource.getXAConnection()); } @Override public XAConnection getXAConnection(String user, String password) throws SQLException { return XAConnectionIsSameRMOverride.overrideSameRM(xaDataSource.getXAConnection(user, password)); } @Override public PrintWriter getLogWriter() throws SQLException { return xaDataSource.getLogWriter(); } @Override public void setLogWriter(PrintWriter out) throws SQLException { xaDataSource.setLogWriter(out); } @Override public void setLoginTimeout(int seconds) throws SQLException { xaDataSource.setLoginTimeout(seconds); } @Override public int getLoginTimeout() throws SQLException { return xaDataSource.getLoginTimeout(); } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { return xaDataSource.getParentLogger(); } } ================================================ FILE: bonita-engine-standalone/src/main/java/org/bonitasoft/engine/xa/XAResourceIsSameRMOverride.java ================================================ /** * Copyright (C) 2020 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.xa; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; public class XAResourceIsSameRMOverride implements XAResource { private XAResource xaResource; private XAResourceIsSameRMOverride(XAResource xaResource) { this.xaResource = xaResource; } static XAResource overrideSameRM(XAResource xaResource) { return new XAResourceIsSameRMOverride(xaResource); } @Override public void commit(Xid xid, boolean b) throws XAException { xaResource.commit(xid, b); } @Override public void end(Xid xid, int i) throws XAException { xaResource.end(xid, i); } @Override public void forget(Xid xid) throws XAException { xaResource.forget(xid); } @Override public int getTransactionTimeout() throws XAException { return xaResource.getTransactionTimeout(); } @Override public boolean isSameRM(XAResource xaResource) throws XAException { // always returns false to make it work on oracle return false; } @Override public int prepare(Xid xid) throws XAException { return xaResource.prepare(xid); } @Override public Xid[] recover(int i) throws XAException { return xaResource.recover(i); } @Override public void rollback(Xid xid) throws XAException { xaResource.rollback(xid); } @Override public boolean setTransactionTimeout(int i) throws XAException { return xaResource.setTransactionTimeout(i); } @Override public void start(Xid xid, int i) throws XAException { xaResource.start(xid, i); } } ================================================ FILE: bonita-engine-standalone/src/main/resources/jbossts-properties.xml ================================================ YES ${bonita.runtime.transaction.xa-timeout:180} ${bonita.runtime.transaction.xa-store-directory:build/tx-object-store} ON 1 1 java:comp/UserTransaction com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter com.arjuna.ats.internal.jta.recovery.arjunacore.SubordinationManagerXAResourceOrphanFilter 0 com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule com.arjuna.ats.internal.txoj.recovery.TORecoveryModule com.arjuna.ats.internal.jta.recovery.arjunacore.SubordinateAtomicActionRecoveryModule com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner 4712 0 NO ================================================ FILE: bonita-engine-standalone/src/test/java/org/bonitasoft/engine/BonitaDataSourceInitializerTest.java ================================================ /** * 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; import static org.assertj.core.api.Assertions.assertThat; import org.apache.tomcat.dbcp.dbcp2.BasicDataSource; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class BonitaDataSourceInitializerTest { @Rule public ExpectedException expectedExceptions = ExpectedException.none(); private BonitaDataSourceInitializer bonitaDataSourceInitializer = new BonitaDataSourceInitializer(); @Test public void should_fail_to_create_datasource_if_some_field_are_not_set() { BonitaDatabaseConfiguration configuration = BonitaDatabaseConfiguration.builder() .dbVendor("postgres") .url("jdbc::postgres") .build(); expectedExceptions.expect(IllegalArgumentException.class); expectedExceptions.expectMessage("Database user not se"); bonitaDataSourceInitializer.createDataSource(configuration); } @Test public void should_fail_to_create_datasource_configuration_is_empty() { expectedExceptions.expect(IllegalArgumentException.class); expectedExceptions.expectMessage("Database dbVendor not set"); bonitaDataSourceInitializer.createDataSource(new BonitaDatabaseConfiguration()); } @Test public void should_fail_if_db_vendor_is_not_supported() { BonitaDatabaseConfiguration configuration = BonitaDatabaseConfiguration.builder() .dbVendor("toto") .url("jdbc::postgres") .build(); expectedExceptions.expect(IllegalArgumentException.class); expectedExceptions.expectMessage("Database db vendor toto is invalid"); bonitaDataSourceInitializer.createDataSource(configuration); } @Test public void should_create_datasource_with_configuration() { BonitaDatabaseConfiguration configuration = BonitaDatabaseConfiguration.builder() .dbVendor("postgres") .driverClassName("org.postgres.MyCustomDriver") .url("jdbc::postgres") .user("myUser") .password("secret") .build(); BasicDataSource dataSource = bonitaDataSourceInitializer.createDataSource(configuration); assertThat(dataSource.getUrl()).isEqualTo("jdbc::postgres"); assertThat(dataSource.getUsername()).isEqualTo("myUser"); assertThat(dataSource.getDriverClassName()).isEqualTo("org.postgres.MyCustomDriver"); assertThat(dataSource.getPassword()).isEqualTo("secret"); } @Test public void should_provide_default_driver_according_to_dbVendor() { BonitaDatabaseConfiguration configuration = BonitaDatabaseConfiguration.builder() .dbVendor("mysql") .url("jdbc::localhost") .user("myUser") .build(); BasicDataSource dataSource = bonitaDataSourceInitializer.createDataSource(configuration); assertThat(dataSource.getUrl()).isEqualTo("jdbc::localhost"); assertThat(dataSource.getDriverClassName()).isEqualTo("com.mysql.cj.jdbc.Driver"); } @Test public void should_set_pool_size_according_to_configuration() throws Exception { BonitaDatabaseConfiguration configuration = BonitaDatabaseConfiguration.builder() .dbVendor("mysql") .url("jdbc::localhost") .user("myUser") .xaDatasource(DatasourceConfiguration.builder().maxPoolSize(10).build()) .datasource(DatasourceConfiguration.builder().maxPoolSize(5).build()) .build(); BasicDataSource dataSource = bonitaDataSourceInitializer.createDataSource(configuration); BasicDataSource managedDataSource = bonitaDataSourceInitializer.createManagedDataSource(configuration, null); assertThat(dataSource.getMaxTotal()).isEqualTo(5); assertThat(managedDataSource.getMaxTotal()).isEqualTo(10); } } ================================================ FILE: bonita-engine-standalone/src/test/java/org/bonitasoft/engine/BonitaEngineTest.java ================================================ /** * 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; import static org.assertj.core.api.Assertions.assertThat; import org.h2.jdbcx.JdbcDataSource; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.rules.TestRule; public class BonitaEngineTest { @Rule public final TestRule restoreSystemProperties = new RestoreSystemProperties(); @Test public void should_set_db_vendor_system_property_according_to_database_configuration() throws Exception { BonitaEngine bonitaEngine = newBonitaEngine(); bonitaEngine.setBonitaDatabaseConfiguration(BonitaDatabaseConfiguration.builder() .dbVendor("postgres") .url("jdbc:postgresql://someServer:5433/bonitadb") .user("user") .build()); bonitaEngine.initializeEnvironment(); assertThat(System.getProperty(BonitaEngine.BONITA_DB_VENDOR)).isEqualTo("postgres"); } @Test public void should_set_db_vendor_system_property_to_h2_by_default() throws Exception { BonitaEngine bonitaEngine = newBonitaEngine(); bonitaEngine.initializeEnvironment(); assertThat(System.getProperty(BonitaEngine.BONITA_DB_VENDOR)).isEqualTo("h2"); } @Test public void should_configure_bonita_datasource_to_h2_by_default() throws Exception { BonitaEngine bonitaEngine = newBonitaEngine(); bonitaEngine.initializeEnvironment(); JdbcDataSource xaDataSourceInstance = (JdbcDataSource) bonitaEngine.getBonitaDataSource() .getXaDataSourceInstance(); assertThat(xaDataSourceInstance.getURL()).contains("h2"); assertThat(xaDataSourceInstance.getDescription()).contains("RawDataSource of h2"); } private BonitaEngine newBonitaEngine() { return new BonitaEngine(); } } ================================================ FILE: bonita-engine-standalone/src/test/java/org/bonitasoft/engine/DatabaseUrlParserTest.java ================================================ /** * Copyright (C) 2020 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; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class DatabaseUrlParserTest { @Test public void should_parse_postgres_url() { String url = "jdbc:postgresql://localhost:32782/bonita"; DatabaseUrlParser.DatabaseMetadata metadata = DatabaseUrlParser.parsePostgresUrl(url); assertThat(metadata.getDatabaseName()).isEqualTo("bonita"); assertThat(metadata.getPort()).isEqualTo("32782"); assertThat(metadata.getServerName()).isEqualTo("localhost"); } @Test(expected = IllegalArgumentException.class) public void should_throw_exception_when_there_is_no_match() { String url = "jdbc:otherDb://localhost:32782/bonita"; DatabaseUrlParser.parsePostgresUrl(url); } } ================================================ FILE: bonita-integration-tests/benchmarks/README.md ================================================ Micro benchmarks ================ This module contains micro benchmark tests that can be written and run to verify the performance of precise parts of the code. ## Write a test see example `org.bonitasoft.engine.benchmarks.TransactionSynchronizationBenchmark` ## Run tests ``` ./gradlew jmh ``` ================================================ FILE: bonita-integration-tests/benchmarks/build.gradle ================================================ plugins { id("me.champeau.jmh") version "0.7.3" } dependencies { api libs.assertj api libs.mockitoCore api project(':bonita-integration-tests:bonita-integration-tests-client') api project(':bpm:bonita-server') testRuntimeOnly libs.logback } jmh { duplicateClassesStrategy = DuplicatesStrategy.WARN iterations = 2 threads = 1 fork = 1 } ================================================ FILE: bonita-integration-tests/benchmarks/src/jmh/java/org/bonitasoft/engine/benchmarks/PermissionCachingBenchmark.java ================================================ /** * Copyright (C) 2020 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.benchmarks; import org.bonitasoft.engine.authorization.properties.ConfigurationFilesManager; import org.bonitasoft.engine.authorization.properties.ResourcesPermissionsMapping; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.engine.test.TestEngine; import org.bonitasoft.engine.test.TestEngineImpl; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; @State(Scope.Benchmark) public class PermissionCachingBenchmark { private TestEngine engine; private ResourcesPermissionsMapping resourcesPermissionsMapping; private CacheService cacheService; @Setup public void setup() throws Exception { engine = TestEngineImpl.getInstance(); engine.start(); cacheService = ServiceAccessorSingleton.getInstance().getCacheService(); resourcesPermissionsMapping = new ResourcesPermissionsMapping(cacheService, new ConfigurationFilesManager()); } @TearDown public void tearDown() throws Exception { engine.stop(); } @Benchmark public void callPermissionWithCache() { resourcesPermissionsMapping.getResourcePermissions("GET", "bpm", "case"); } @Benchmark public void callPermissionFromCache50times() { for (int i = 0; i < 50; i++) { resourcesPermissionsMapping.getResourcePermissions("GET", "bpm", "case"); } } } ================================================ FILE: bonita-integration-tests/benchmarks/src/jmh/java/org/bonitasoft/engine/benchmarks/TransactionSynchronizationBenchmark.java ================================================ /** * Copyright (C) 2020 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.benchmarks; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.bonitasoft.engine.execution.work.BPMWorkFactory; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.engine.test.TestEngine; import org.bonitasoft.engine.test.TestEngineImpl; import org.bonitasoft.engine.transaction.UserTransactionService; import org.bonitasoft.engine.work.BonitaWork; import org.bonitasoft.engine.work.WorkDescriptor; import org.bonitasoft.engine.work.WorkService; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.TearDown; @State(Scope.Benchmark) public class TransactionSynchronizationBenchmark { private TestEngine engine; private UserTransactionService userTransactionService; private WorkService workService; @Setup public void setup() throws Exception { engine = TestEngineImpl.getInstance(); engine.start(); final ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance(); userTransactionService = serviceAccessor.getUserTransactionService(); workService = serviceAccessor.getWorkService(); BPMWorkFactory bpmWorkFactory = serviceAccessor.getBPMWorkFactory(); bpmWorkFactory.addExtension("BENCHMARK_WORK", workDescriptor -> new BonitaWork() { @Override public String getDescription() { return "BENCHMARK_WORK"; } @Override public CompletableFuture work(Map context) throws Exception { return CompletableFuture.completedFuture(null); } @Override public void handleFailure(Throwable e, Map context) throws Exception { } }); } @TearDown public void tearDown() throws Exception { engine.stop(); } @Benchmark public void register1000Works() throws Exception { userTransactionService.executeInTransaction(() -> { for (int i = 0; i < 1000; i++) { workService.registerWork(WorkDescriptor.create("BENCHMARK_WORK")); } return null; }); } @Benchmark public void register1Works() throws Exception { userTransactionService.executeInTransaction(() -> { for (int i = 0; i < 1; i++) { workService.registerWork(WorkDescriptor.create("BENCHMARK_WORK")); } return null; }); } @Benchmark public void register2Works() throws Exception { userTransactionService.executeInTransaction(() -> { for (int i = 0; i < 2; i++) { workService.registerWork(WorkDescriptor.create("BENCHMARK_WORK")); } return null; }); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/bonita-client-http.properties ================================================ org.bonitasoft.engine.api-type=HTTP org.bonitasoft.engine.api-type.server.url=127.255.0.123 org.bonitasoft.engine.api-type.application.name=MyBonitaInstallation ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/bonita-client-invalid.properties ================================================ org.bonitasoft.engine.api-type = InvalidType ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/bonita-client-local.properties ================================================ org.bonitasoft.engine.api-type = LOCAL ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/build.gradle ================================================ plugins { id("bonita-http-test") id("bonita-docker-database") id("bonita-tests") } dependencies { api project(':bonita-test-api') api libs.commonsIO api project(':bpm:bonita-common') api project(':bpm:bonita-client') api project(':bonita-integration-tests:bonita-test-utils') api libs.assertj api(libs.jsonUnit) { exclude(group: "org.slf4j", module: "slf4j-api") } api libs.systemRules api libs.concurrentUnit runtimeOnly libs.logback testImplementation project(':bpm:bonita-server') testImplementation testFixtures(project(':bpm:bonita-common')) testImplementation libs.awaitility testImplementation libs.xmlunit testImplementation libs.mockitoCore testImplementation libs.logback } test.doFirst { def emptyFile = new File("$project.buildDir/bonita_home_client_HTTP/engine-client/conf/bonita-client-custom.properties") emptyFile.getParentFile().mkdirs() emptyFile.text = "" copy { from file("$projectDir/bonita-client-http.properties") into file("$project.buildDir/bonita_home_client_HTTP/engine-client/work") rename 'bonita-client-http.properties', 'bonita-client-community.properties' } def invalidFile = new File("$project.buildDir/bonita_home_client_invalidAPIType/engine-client/conf/bonita-client-custom.properties") invalidFile.getParentFile().mkdirs() invalidFile.text = "" copy { from file("$projectDir/bonita-client-invalid.properties") into file("$project.buildDir/bonita_home_client_invalidAPIType/engine-client/work") rename 'bonita-client-invalid.properties', 'bonita-client-community.properties' } } group = 'org.bonitasoft.engine.test' publishing { publications { mavenJava(MavenPublication) { from project.components.java } } } httpTests { integrationTestsSuite = "**/*IT.class" } databaseIntegrationTest { include "**/*IT.class" } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/CommonAPIIT.java ================================================ /** * 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; import static org.junit.Assert.assertNotNull; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.commons.io.IOUtils; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.connectors.TestConnector; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.filter.user.GroupUserFilter; import org.bonitasoft.engine.filter.user.TestFilter; import org.bonitasoft.engine.filter.user.TestFilterThatThrowException; import org.bonitasoft.engine.filter.user.TestFilterUsingActorName; import org.bonitasoft.engine.filter.user.TestFilterWithAutoAssign; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.APITestUtil; import org.bonitasoft.engine.test.junit.BonitaEngineRule; import org.junit.Rule; import org.junit.rules.TestRule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class CommonAPIIT extends APITestUtil { @Rule public BonitaEngineRule bonitaEngineRule = BonitaEngineRule.create(); private static final Logger LOGGER = LoggerFactory.getLogger(CommonAPIIT.class); @Rule public TestRule testWatcher = new PrintTestsStatusRule(LOGGER) { @Override public void clean() throws Exception { CommonAPIIT.this.clean(); } }; private void clean() throws BonitaException { loginWithTechnicalUser(); cleanCommands(); cleanProcessInstances(); cleanApplications(); cleanPages(); cleanArchiveProcessInstances(); cleanProcessDefinitions(); cleanCategories(); cleanUsers(); cleanGroups(); cleanRoles(); cleanSupervisors(); checkThereAreNoWaitingEventsLeft(); cleanBdm(); logout(); } private void checkThereAreNoWaitingEventsLeft() throws BonitaException { SearchResult searchResult = (SearchResult) getCommandAPI() .execute("searchWaitingEventsCommand", Collections.singletonMap("searchOptions", new SearchOptionsBuilder(0, 100).done())); Assertions.assertThat(searchResult.getResult()).hasSize(0); } public BarResource getResource(final String path, final String name) throws IOException { return getBarResource(path, name, CommonAPIIT.class); } public void addResource(final List resources, final String path, final String name) throws IOException { final BarResource barResource = getResource(path, name); resources.add(barResource); } protected ProcessDefinition deployProcessWithTestFilter(final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user, final String filterName) throws BonitaException, IOException { final List userFilters = generateFilterImplementations(filterName); final List generateFilterDependencies = generateFilterDependencies(); return deployAndEnableProcessWithActorAndUserFilter(processDefinitionBuilder, actorName, user, generateFilterDependencies, userFilters); } private List generateFilterImplementations(final String filterName) throws IOException { final List resources = new ArrayList<>(1); final InputStream inputStream = TestConnector.class.getClassLoader() .getResourceAsStream("org/bonitasoft/engine/filter/user/" + filterName + ".impl"); final byte[] data = IOUtil.getAllContentFrom(inputStream); inputStream.close(); resources.add(new BarResource(filterName + ".impl", data)); return resources; } private List generateFilterDependencies() throws IOException { final List resources = new ArrayList<>(1); byte[] data = IOUtil.generateJar(TestFilterThatThrowException.class); resources.add(new BarResource("TestFilterThatThrowException.jar", data)); data = IOUtil.generateJar(TestFilter.class); resources.add(new BarResource("TestFilter.jar", data)); data = IOUtil.generateJar(TestFilterWithAutoAssign.class); resources.add(new BarResource("TestFilterWithAutoAssign.jar", data)); data = IOUtil.generateJar(TestFilterUsingActorName.class); resources.add(new BarResource("TestFilterUsingActorName.jar", data)); data = IOUtil.generateJar(GroupUserFilter.class); resources.add(new BarResource("TestGroupUserFilter.jar", data)); return resources; } protected String getContentOfResource(final String name) { final InputStream stream = this.getClass().getResourceAsStream(name); assertNotNull(stream); try { try { return IOUtils.toString(stream); } finally { stream.close(); } } catch (final IOException e) { throw new RuntimeException(e); } } @Override public BarResource getBarResource(String path, String name, Class clazz) throws IOException { return super.getBarResource(path, name, clazz); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/PrintTestsStatusRule.java ================================================ /** * 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; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.junit.rules.TestWatcher; import org.junit.runner.Description; import org.slf4j.Logger; /** * @author Baptiste Mesta */ public abstract class PrintTestsStatusRule extends TestWatcher { private final Logger logger; public PrintTestsStatusRule(Logger logger) { this.logger = logger; } @Override public void starting(final Description d) { logger.warn("Starting test: {}.{}", d.getClassName(), d.getMethodName()); } @Override public void failed(final Throwable e, final Description d) { logger.warn("Failed test: {}.{}", d.getClassName(), d.getMethodName(), e); try { clean(); } catch (final Exception be) { logger.error("unable to clean db", be); } finally { logger.warn("------------------------------------------------------"); } } @Override public void succeeded(final Description d) { logger.warn("Succeeded test: {}.{}", d.getClassName(), d.getMethodName()); try { clean(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } finally { logger.warn("------------------------------------------------------"); } } public abstract void clean() throws Exception; } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/TestWithTechnicalUser.java ================================================ /** * 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; import org.junit.After; import org.junit.Before; /** * @author Celine Souchet * @version 6.4.1 * @since 6.4.1 */ public class TestWithTechnicalUser extends CommonAPIIT { @Before public void before() throws Exception { loginWithTechnicalUser(); } @After public void after() throws Exception { logout(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/TestWithUser.java ================================================ /** * 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; import org.bonitasoft.engine.identity.User; import org.junit.After; import org.junit.Before; /** * @author Celine Souchet * @version 6.4.1 * @since 6.4.1 */ public class TestWithUser extends TestWithTechnicalUser { protected User user; @Override @Before public void before() throws Exception { super.before(); user = createUser(USERNAME, PASSWORD); logout(); loginOnDefaultTenantWith(USERNAME, PASSWORD); } @Override @After public void after() throws Exception { logout(); loginWithTechnicalUser(); deleteUser(USERNAME); super.after(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/business/application/TestWithCustomPage.java ================================================ /** * 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.business.application; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.page.PageCreator; import org.junit.After; import org.junit.Before; /** * @author Elias Ricken de Medeiros */ public class TestWithCustomPage extends TestWithLivingApplication { private Page page; @Before public void setUp() throws Exception { super.setUp(); page = createPage("custompage_MyPage"); } @After public void tearDown() throws Exception { super.tearDown(); loginWithTechnicalUser(); if (page != null) { getPageAPI().deletePage(page.getId()); } logout(); } public Page getPage() { return page; } protected Page createPage(final String pageName) throws Exception { return getPageAPI().createPage(new PageCreator(pageName, "content.zip").setDisplayName(pageName), createTestPageContent(pageName, "no display name", "empty desc")); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/business/application/TestWithLivingApplication.java ================================================ /** * 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.business.application; import static org.assertj.core.api.Assertions.assertThat; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.page.PageCreator; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.profile.ProfileSearchDescriptor; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.After; import org.junit.Before; /** * @author Elias Ricken de Medeiros */ public class TestWithLivingApplication extends CommonAPIIT { static final String DEFAULT_LAYOUT_NAME = "custompage_layoutBonita"; static final String DEFAULT_THEME_NAME = "custompage_themeBonita"; private User user; @Before public void setUp() throws Exception { loginWithTechnicalUser(); user = createUser("john", "bpm"); logout(); loginOnDefaultTenantWith("john", "bpm"); } @After public void tearDown() throws Exception { final SearchResult searchResult = getLivingApplicationAPI() .searchIApplications(new SearchOptionsBuilder(0, 1000).done()); for (final IApplication app : searchResult.getResult()) { getLivingApplicationAPI().deleteApplication(app.getId()); } logoutThenlogin(); deleteUser(user); logout(); } protected Profile getProfileUser() throws SearchException { return getProfile("User"); } Profile getProfileAdmin() throws SearchException { return getProfile("Administrator"); } private Profile getProfile(String profileName) throws SearchException { SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1); builder.filter(ProfileSearchDescriptor.NAME, profileName); SearchResult profileSearchResult = getProfileAPI().searchProfiles(builder.done()); assertThat(profileSearchResult.getCount()).isEqualTo(1); Profile profile = profileSearchResult.getResult().get(0); return profile; } public User getUser() { return user; } protected Page createPage(final String pageName) throws Exception { return getPageAPI().createPage(new PageCreator(pageName, "content.zip").setDisplayName(pageName), createPageContent(pageName)); } private byte[] createPageContent(final String pageName) throws BonitaException { try { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ZipOutputStream zos = new ZipOutputStream(baos); zos.putNextEntry(new ZipEntry("Index.groovy")); zos.write("return \"\";".getBytes()); zos.putNextEntry(new ZipEntry("page.properties")); String s = "name=" + pageName + "\n" + "displayName=no display name\n" + "description=empty desc\n"; zos.write(s.getBytes("UTF-8")); zos.closeEntry(); return baos.toByteArray(); } catch (final IOException e) { throw new BonitaException(e); } } protected SearchOptionsBuilder getAppSearchBuilderOrderByToken(final int startIndex, final int maxResults) { final SearchOptionsBuilder builder = new SearchOptionsBuilder(startIndex, maxResults); builder.sort(ApplicationSearchDescriptor.TOKEN, Order.ASC); return builder; } protected void assertIsAddOkStatus(final ImportStatus importStatus, String expectedToken) { assertThat(importStatus.getName()).isEqualTo(expectedToken); assertThat(importStatus.getStatus()).isEqualTo(ImportStatus.Status.ADDED); assertThat(importStatus.getErrors()).isEmpty(); } protected SearchOptionsBuilder getAppSearchBuilderOrderById(final int startIndex, final int maxResults) { final SearchOptionsBuilder builder = new SearchOptionsBuilder(startIndex, maxResults); builder.sort(ApplicationSearchDescriptor.ID, Order.ASC); return builder; } protected void assertIsMarketingApplication(final Application app) { assertThat(app.getToken()).isEqualTo("My"); assertThat(app.getVersion()).isEqualTo("2.0"); assertThat(app.getDisplayName()).isEqualTo("Marketing"); assertThat(app.getDescription()).isNull(); assertThat(app.getIconPath()).isNull(); assertThat(app.getState()).isEqualTo("ACTIVATED"); assertThat(app.getProfileId()).isNull(); } protected void assertIsHRApplication(final Profile profile, final Page layout, final Page theme, final Application app) { assertThat(app.getToken()).isEqualTo("HR-dashboard"); assertThat(app.getVersion()).isEqualTo("2.0"); assertThat(app.getDisplayName()).isEqualTo("My HR dashboard"); assertThat(app.getDescription()).isEqualTo("This is the HR dashboard."); assertThat(app.getIconPath()).isEqualTo("/icon.jpg"); assertThat(app.getState()).isEqualTo("ACTIVATED"); assertThat(app.getProfileId()).isEqualTo(profile.getId()); assertThat(app.getLayoutId()).isEqualTo(layout.getId()); assertThat(app.getThemeId()).isEqualTo(theme.getId()); } protected void assertIsMyNewCustomPage(final Page myPage, final Application hrApp, final ApplicationPage applicationPage) { assertThat(applicationPage.getApplicationId()).isEqualTo(hrApp.getId()); assertThat(applicationPage.getToken()).isEqualTo("my-new-custom-page"); assertThat(applicationPage.getPageId()).isEqualTo(myPage.getId()); } protected void assertIsHrFollowUpMenu(final ApplicationMenu applicationMenu) { assertThat(applicationMenu.getParentId()).isNull(); assertThat(applicationMenu.getDisplayName()).isEqualTo("HR follow-up"); assertThat(applicationMenu.getApplicationPageId()).isNull(); } protected void assertIsDailyHrFollowUpMenu(final ApplicationMenu applicationMenu, ApplicationMenu hrFollowUpMenu, ApplicationPage myNewCustomPage) { assertThat(applicationMenu.getParentId()).isEqualTo(hrFollowUpMenu.getId()); assertThat(applicationMenu.getDisplayName()).isEqualTo("Daily HR follow-up"); assertThat(applicationMenu.getApplicationPageId()).isEqualTo(myNewCustomPage.getId()); } protected void assertIsEmptyMenu(final ApplicationMenu applicationMenu) { assertThat(applicationMenu.getParentId()).isNull(); assertThat(applicationMenu.getDisplayName()).isEqualTo("Empty menu"); assertThat(applicationMenu.getApplicationPageId()).isNull(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/business/data/BusinessDataUpdateConnector.java ================================================ /** * 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.business.data; import java.lang.reflect.Method; import org.bonitasoft.engine.connector.AbstractConnector; import org.bonitasoft.engine.connector.ConnectorException; public class BusinessDataUpdateConnector extends AbstractConnector { @Override public void validateInputParameters() { } @Override protected void executeBusinessLogic() throws ConnectorException { final Object bizData = getInputParameter("bizData"); try { Method method = bizData.getClass().getMethod("addToPhoneNumbers", String.class); method.invoke(bizData, "48665421"); method = bizData.getClass().getMethod("setLastName", String.class); method.invoke(bizData, "Hakkinen"); setOutputParameter("output1", bizData); } catch (final Exception e) { throw new ConnectorException(e); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/business/data/ClassloaderRefresher.java ================================================ /** * 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.business.data; import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.io.FileUtils; import org.bonitasoft.engine.io.IOUtils; /** * @author Emmanuel Duchastenier */ public class ClassloaderRefresher { /** * @param clientZipContent * @param contextClassLoader * @param modelClass * @param fsFolderToPutJars * @return the newly created classloader with newly loaded class, if found. * @throws java.io.IOException * @throws java.net.MalformedURLException */ public ClassLoader loadClientModelInClassloader(final byte[] clientZipContent, final ClassLoader contextClassLoader, final String modelClass, final File fsFolderToPutJars) throws IOException { final Map ressources = IOUtils.unzip(clientZipContent); final List urls = new ArrayList<>(); for (final Entry e : ressources.entrySet()) { final File file = new File(fsFolderToPutJars, e.getKey()); if (file.getName().endsWith(".jar")) { if (file.getName().contains("model")) { try { contextClassLoader.loadClass(modelClass); } catch (final ClassNotFoundException e1) { FileUtils.writeByteArrayToFile(file, e.getValue()); urls.add(file.toURI().toURL()); } } if (file.getName().contains("dao")) { try { contextClassLoader.loadClass(modelClass + "DAOImpl"); } catch (final ClassNotFoundException e1) { FileUtils.writeByteArrayToFile(file, e.getValue()); urls.add(file.toURI().toURL()); } } if (file.getName().contains("javassist")) { try { contextClassLoader.loadClass("javassist.util.proxy.MethodFilter"); } catch (final ClassNotFoundException e1) { FileUtils.writeByteArrayToFile(file, e.getValue()); urls.add(file.toURI().toURL()); } } } } ClassLoader classLoaderWithBDM = contextClassLoader; if (!urls.isEmpty()) { classLoaderWithBDM = new URLClassLoader(urls.toArray(new URL[0]), contextClassLoader); } return classLoaderWithBDM; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/ProcessDeployer.java ================================================ /** * 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.command.helper; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.command.helper.designer.SimpleProcessDesigner; import org.bonitasoft.engine.exception.BonitaException; /** * Created by Vincent Elcrin * Date: 16/12/13 * Time: 11:40 */ public abstract class ProcessDeployer { ProcessDefinition processDefinition; public ProcessDefinition deploy(SimpleProcessDesigner design) throws BonitaException { try { return processDefinition = deploy(design.done()); } catch (InvalidProcessDefinitionException e) { throw new BonitaException(e); } } public abstract ProcessDefinition deploy(DesignProcessDefinition design) throws BonitaException; public void clean() throws BonitaException { clean(processDefinition); } public abstract void clean(ProcessDefinition processDefinition) throws BonitaException; } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/BoundaryEvent.java ================================================ /** * 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.command.helper.designer; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; /** * @author Vincent Elcrin */ public class BoundaryEvent { private final String name; private final Fragment flowsOut; private Trigger trigger; Transition transition = new Transition(); public BoundaryEvent(String name, Fragment flowsOut) { this.name = name; this.flowsOut = flowsOut; } public void attach(UserTaskDefinitionBuilder task, ProcessDefinitionBuilder builder) { trigger.listen(task.addBoundaryEvent(name)); flowsOut.build(builder); transition.bind(name, flowsOut.getName(), builder); } public BoundaryEvent triggeredBy(Trigger trigger) { this.trigger = trigger; return this; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/Branch.java ================================================ /** * 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.command.helper.designer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; /** * @author Vincent Elcrin */ public class Branch extends Fragment { private List> sequence = new ArrayList>(); private Fragment origin; private String name; public Branch start(Fragment origin) { this.origin = origin; name = origin.getName(); return this; } public Branch then(Fragment... targets) { assert origin != null : "Branch need to be started by calling start method"; sequence.add(Arrays.asList(targets)); name = targets[0].getName(); return this; } @Override public String getName() { assert name != null; return name; } @Override public void bind(List sources, ProcessDefinitionBuilder builder) { super.bind(sources, builder); } @Override public void bind(String source, Transition transition, ProcessDefinitionBuilder builder) { transition.bind(source, origin.getName(), builder); } @Override public void build(ProcessDefinitionBuilder builder) { List sources = new ArrayList(); origin.build(builder); sources.add(origin); for (List targets : sequence) { link(sources, targets, builder); } } private void link(List sources, List targets, ProcessDefinitionBuilder builder) { for (Fragment target : targets) { target.build(builder); target.bind(sources, builder); } sources.clear(); sources.addAll(targets); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/ConditionalTransition.java ================================================ /** * 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.command.helper.designer; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; /** * Created by vince on 1/28/14. */ public class ConditionalTransition extends Transition { private Fragment otherwise; private Expression expression; public ConditionalTransition(Expression expression) { this.expression = expression; } @Override public void bind(String source, String target, ProcessDefinitionBuilder builder) { builder.addTransition(source, target, expression); if (otherwise != null) { otherwise.build(builder); otherwise.bind(source, new DefaultTransition(), builder); } } public ConditionalTransition otherwise(Fragment otherwise) { this.otherwise = otherwise; return this; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/DefaultTransition.java ================================================ /** * 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.command.helper.designer; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; public class DefaultTransition extends Transition { private ConditionalBranch branch; @Override public void bind(String source, String target, ProcessDefinitionBuilder builder) { builder.addDefaultTransition(source, target); if (branch != null) { branch.build(source, builder); } } public interface ConditionalBranch { void build(String source, ProcessDefinitionBuilder builder); DefaultTransition goingTo(Fragment then); } public ConditionalBranch toMeet(final Expression expression) { branch = new ConditionalBranch() { private Fragment then; public void build(String source, ProcessDefinitionBuilder builder) { then.build(builder); then.bind(source, new ConditionalTransition(expression), builder); } public DefaultTransition goingTo(Fragment then) { this.then = then; return DefaultTransition.this; } }; return branch; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/EndEvent.java ================================================ /** * 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.command.helper.designer; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; /** * Created by Vincent Elcrin * Date: 16/12/13 * Time: 11:46 */ public class EndEvent extends FlowNode { public EndEvent(String name) { super(name); } @Override public void build(ProcessDefinitionBuilder builder) { builder.addEndEvent(getName()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/FlowNode.java ================================================ /** * 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.command.helper.designer; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; /** * @author Vincent Elcrin */ public abstract class FlowNode extends Fragment { private String name; protected FlowNode(String name) { this.name = name; } @Override public void bind(String source, Transition transition, ProcessDefinitionBuilder builder) { transition.bind(source, name, builder); } @Override public String getName() { return name; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/Fragment.java ================================================ /** * 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.command.helper.designer; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; /** * Created by Vincent Elcrin * Date: 16/12/13 * Time: 14:20 */ public abstract class Fragment { private final Map transitions = new HashMap(); public Fragment when(final String step, final Transition transition) { transitions.put(step, transition); return this; } public void bind(final List sources, final ProcessDefinitionBuilder builder) { for (final Fragment source : sources) { Transition transition = transitions.get(source.getName()); if (transition == null) { transition = new Transition(); } bind(source.getName(), transition, builder); } } public abstract void bind(String source, Transition transition, ProcessDefinitionBuilder builder); public abstract void build(ProcessDefinitionBuilder builder); public abstract String getName(); } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/Gateway.java ================================================ /** * 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.command.helper.designer; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; /** * Created by Vincent Elcrin * Date: 16/12/13 * Time: 12:20 */ public class Gateway extends FlowNode { private GatewayType type; public Gateway(String name, GatewayType type) { super(name); this.type = type; } @Override public void build(ProcessDefinitionBuilder builder) { builder.addGateway(getName(), type); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/Signal.java ================================================ /** * 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.command.helper.designer; import org.bonitasoft.engine.bpm.process.impl.BoundaryEventDefinitionBuilder; /** * @author Vincent Elcrin */ public class Signal implements Trigger { private String name; public Signal(String name) { this.name = name; } @Override public void listen(BoundaryEventDefinitionBuilder builder) { builder.addSignalEventTrigger(name); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/SimpleProcessDesigner.java ================================================ /** * 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.command.helper.designer; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; /** * Created by Vincent Elcrin * Date: 16/12/13 * Time: 18:38 */ public class SimpleProcessDesigner { private final Branch branch; private ProcessDefinitionBuilder builder; public SimpleProcessDesigner(ProcessDefinitionBuilder builder) { this.builder = builder; this.branch = new Branch(); } public SimpleProcessDesigner start() { startWith(new StartEvent("start")); return this; } public SimpleProcessDesigner startWith(StartEvent start) { branch.start(start); return this; } public SimpleProcessDesigner then(Fragment... targets) { branch.then(targets); return this; } public SimpleProcessDesigner end() { return then(new EndEvent("end")); } public DesignProcessDefinition done() throws InvalidProcessDefinitionException { branch.build(builder); return builder.done(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/StartEvent.java ================================================ /** * 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.command.helper.designer; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; /** * Created by Vincent Elcrin * Date: 16/12/13 * Time: 11:45 */ public class StartEvent extends FlowNode { public StartEvent(String name) { super(name); } @Override public void build(ProcessDefinitionBuilder builder) { builder.addStartEvent(getName()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/Transition.java ================================================ /** * 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.command.helper.designer; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; /** * @author Vincent Elcrin */ public class Transition { public void bind(String source, String target, ProcessDefinitionBuilder builder) { builder.addTransition(source, target); } public static ConditionalTransition meet(Expression expression) { return new ConditionalTransition(expression); } public static DefaultTransition fails() { return new DefaultTransition(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/Trigger.java ================================================ /** * 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.command.helper.designer; import org.bonitasoft.engine.bpm.process.impl.BoundaryEventDefinitionBuilder; /** * @author Vincent Elcrin */ public interface Trigger { void listen(BoundaryEventDefinitionBuilder builder); } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/designer/UserTask.java ================================================ /** * 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.command.helper.designer; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; /** * Created by Vincent Elcrin * Date: 16/12/13 * Time: 11:43 */ public class UserTask extends FlowNode { private String actor = "actor"; private BoundaryEvent event; public UserTask(String name) { super(name); } @Override public void build(ProcessDefinitionBuilder builder) { UserTaskDefinitionBuilder task = builder.addUserTask(getName(), actor); if (event != null) { event.attach(task, builder); } } public UserTask with(BoundaryEvent event) { this.event = event; return this; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/expectation/DocumentExpectation.java ================================================ /** * 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.command.helper.expectation; import static org.junit.Assert.assertEquals; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.document.DocumentNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessInstance; /** * @author Elias Ricken de Medeiros */ public class DocumentExpectation { private CommonAPIIT testCase; private ProcessInstance process; private String name; public DocumentExpectation(CommonAPIIT testCase, ProcessInstance process, String name) { this.testCase = testCase; this.process = process; this.name = name; } public void toBe(String variable) throws DocumentNotFoundException { Document document = testCase.getProcessAPI().getLastDocument(process.getId(), name); String storageId = document.getContentStorageId(); assertEquals(variable, new String(testCase.getProcessAPI().getDocumentContent(storageId))); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/expectation/ProcessExpectation.java ================================================ /** * 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.command.helper.expectation; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.bpm.process.ProcessInstance; /** * @author Vincent Elcrin */ public class ProcessExpectation { private CommonAPIIT testCase; private ProcessInstance process; public ProcessExpectation(CommonAPIIT testCase, ProcessInstance process) { this.testCase = testCase; this.process = process; } public void toFinish() throws Exception { testCase.waitForProcessToFinish(process.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/expectation/StepExpectation.java ================================================ /** * 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.command.helper.expectation; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.util.concurrent.TimeoutException; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; /** * @author Vincent Elcrin */ public class StepExpectation { private CommonAPIIT testCase; private ProcessInstance process; private String[] steps; public StepExpectation(CommonAPIIT testCase, ProcessInstance process, String... steps) { this.testCase = testCase; this.process = process; this.steps = steps; } public void toBeReady() throws Exception { for (String step : steps) { isReady(step); } } public void toNotHaveArchives() throws Exception { for (String step : steps) { assertEquals(0, getArchives(step, false, null).getCount()); } } public void toBeExecuted(int times) throws SearchException { for (String step : steps) { assertEquals(times, getArchives(step, true, "completed").getCount()); } } public void toBeAborted() throws SearchException { for (String step : steps) { assertEquals(1, getArchives(step, true, "aborted").getCount()); } } private void isReady(String step) throws Exception { try { testCase.waitForFlowNodeInReadyState(process, step, false); } catch (TimeoutException e) { fail(step + " is expected to be started."); } } private SearchResult getArchives(String step, boolean terminal, String state) throws SearchException { SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 0); builder.filter(ArchivedFlowNodeInstanceSearchDescriptor.NAME, step); builder.filter(ArchivedFlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, process.getId()); if (terminal) { builder.filter(ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL, true); } if (state != null) { builder.filter(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, state); } return testCase.getProcessAPI().searchArchivedFlowNodeInstances(builder.done()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/expectation/TestUtils.java ================================================ /** * 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.command.helper.expectation; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.bpm.flownode.SendEventException; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.identity.User; /** * Created by Vincent Elcrin * Date: 17/12/13 * Time: 10:46 */ public class TestUtils { private final CommonAPIIT testCase; public TestUtils(final CommonAPIIT testCase) { this.testCase = testCase; } public Process wrap(final ProcessInstance process) { return new Process(process); } public class Process { private final ProcessInstance process; Process(final ProcessInstance process) { this.process = process; } public void execute(final User behalf, final String... steps) throws Exception { for (final String step : steps) { testCase.waitForUserTaskAndExecuteIt(process, step, behalf); } } public StepExpectation expect(final String... steps) { return new StepExpectation(testCase, process, steps); } public ProcessExpectation isExpected() { return new ProcessExpectation(testCase, process); } public VariableExpectation expectVariable(final String name) { return new VariableExpectation(testCase, process, name); } public DocumentExpectation expectDocument(final String name) { return new DocumentExpectation(testCase, process, name); } public void sendSignal(final String name) throws SendEventException { testCase.getProcessAPI().sendSignal(name); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/command/helper/expectation/VariableExpectation.java ================================================ /** * 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.command.helper.expectation; import static org.junit.Assert.assertEquals; import java.io.Serializable; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.bpm.data.DataNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessInstance; /** * @author Vincent Elcrin */ public class VariableExpectation { private CommonAPIIT testCase; private ProcessInstance process; private String name; public VariableExpectation(CommonAPIIT testCase, ProcessInstance process, String name) { this.testCase = testCase; this.process = process; this.name = name; } public void toBe(Serializable variable) throws DataNotFoundException { assertEquals(variable, testCase.getProcessAPI().getProcessDataInstance(name, process.getId()).getValue()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/ConnectorExecutionIT.java ================================================ /** * 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.connectors; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.filter.user.TestFilterWithAutoAssign; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Before; /** * @author Baptiste Mesta * @author Celine Souchet */ public abstract class ConnectorExecutionIT extends TestWithUser { protected long userId; public static final String DEFAULT_EXTERNAL_CONNECTOR_ID = "org.bonitasoft.connector.testExternalConnector"; public static final String DEFAULT_EXTERNAL_CONNECTOR_VERSION = "1.0"; @Before public void beforeTest() { userId = user.getId(); } @After public void afterTest() { VariableStorage.clearAll(); } private BarResource buildBarResourceForFilterWithAutoAssign() throws IOException { final InputStream inputStream = TestConnector.class.getClassLoader().getResourceAsStream( "org/bonitasoft/engine/filter/user/TestFilterWithAutoAssign.impl"); final byte[] data = IOUtil.getAllContentFrom(inputStream); inputStream.close(); return new BarResource("TestFilter.impl", data); } public ProcessDefinition deployProcessWithActorAndTestConnectorEngineExecutionContextAndFilterWithAutoAssign( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user) throws IOException, BonitaException { final List connectorImplementations = Arrays.asList(BuildTestUtil.getContentAndBuildBarResource( "TestConnectorEngineExecutionContext.impl", TestConnectorEngineExecutionContext.class)); final List userFilters = Arrays.asList(buildBarResourceForFilterWithAutoAssign()); final List generateConnectorDependencies = Arrays.asList( BuildTestUtil.generateJarAndBuildBarResource(TestConnectorEngineExecutionContext.class, "TestConnectorEngineExecutionContext.jar"), BuildTestUtil.generateJarAndBuildBarResource(TestFilterWithAutoAssign.class, "TestFilterWithAutoAssign.jar")); return deployAndEnableProcessWithActorAndConnectorAndUserFilter(processDefinitionBuilder, actorName, user, connectorImplementations, generateConnectorDependencies, userFilters); } public ProcessDefinition deployAndEnableProcessWithTestConnectorWithAPICall( final ProcessDefinitionBuilder processDefinitionBuilder) throws BonitaException, IOException { return deployAndEnableProcessWithConnector(processDefinitionBuilder, "TestConnectorWithAPICall.impl", TestConnectorWithAPICall.class, "TestConnectorWithAPICall.jar"); } public ProcessDefinition deployProcessWithExternalTestConnector( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnector(processDefinitionBuilder, actorName, user, "TestExternalConnector.impl", TestExternalConnector.class, "TestExternalConnector.jar"); } public ProcessDefinition deployProcessWithActorAndTestConnectorWithConnectedResource( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnector(processDefinitionBuilder, actorName, user, "TestConnectorWithConnectedResource.impl", TestConnectorWithConnectedResource.class, "TestConnectorWithConnectedResource.jar"); } public ProcessDefinition deployProcessWithActorAndTestConnectorWithNotSerializableOutput( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnector(processDefinitionBuilder, actorName, user, "TestConnectorWithNotSerializableOutput.impl", TestConnectorWithNotSerializableOutput.class, "TestConnectorWithNotSerializableOutput.jar"); } public ProcessDefinition deployProcessWithActorAndTestConnector3( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user) throws BonitaException, IOException { final List connectorImplementations = Collections .singletonList(BuildTestUtil.getContentAndBuildBarResource("TestConnector3.impl", TestConnector3.class)); final List generateConnectorDependencies = Arrays.asList( BuildTestUtil.generateJarAndBuildBarResource(TestConnector3.class, "TestConnector3.jar"), BuildTestUtil.generateJarAndBuildBarResource(VariableStorage.class, "VariableStorage.jar")); return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user, connectorImplementations, generateConnectorDependencies, null); } public ProcessDefinition deployProcessWithActorAndTestConnectorEngineExecutionContext( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnector(processDefinitionBuilder, actorName, user, "TestConnectorEngineExecutionContext.impl", TestConnectorEngineExecutionContext.class, "TestConnectorEngineExecutionContext.jar"); } public ProcessDefinition deployProcessWithActorAndTestConnectorThatThrowException( final ProcessDefinitionBuilder processDefinitionBuilder, final String actor, final User user) throws BonitaException, IOException { return deployProcessWithActorAndTestConnectorThatThrowExceptionAndParameter(processDefinitionBuilder, actor, user, null); } public ProcessDefinition deployProcessWithActorAndTestConnectorThatThrowExceptionAndParameter( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user, final Map parameters) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user, parameters, "TestConnectorThatThrowException.impl", TestConnectorThatThrowException.class, "TestConnectorThatThrowException.jar"); } public ProcessDefinition deployProcessWithActorAndTestConnector( final ProcessDefinitionBuilder processDefinitionBuilder, final String actor, final User user) throws BonitaException, IOException { return deployProcessWithActorAndTestConnectorAndParameter(processDefinitionBuilder, actor, user, null); } public ProcessDefinition deployProcessWithActorAndTestConnectorAndParameter( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user, final Map parameters) throws BonitaException, IOException { return deployProcessWithActorAndTestConnectorAndParameter(processDefinitionBuilder, actorName, user, parameters, "TestConnector.impl", "TestConnector.jar"); } private ProcessDefinition deployProcessWithActorAndTestConnectorAndParameter( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user, final Map parameters, final String name, final String jarName) throws IOException, BonitaException { final List connectorImplementations = Collections .singletonList(BuildTestUtil.getContentAndBuildBarResource(name, TestConnector.class)); final List generateConnectorDependencies = Arrays.asList( BuildTestUtil.generateJarAndBuildBarResource(TestConnector.class, jarName), BuildTestUtil.generateJarAndBuildBarResource(VariableStorage.class, "VariableStorage.jar")); return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user, connectorImplementations, generateConnectorDependencies, parameters); } public ProcessDefinition deployProcessWithActorAndTestConnectorWithOutput( final ProcessDefinitionBuilder processDefinitionBuilder, final String actor, final User user) throws BonitaException, IOException { return deployProcessWithActorAndTestConnectorWithOutputAndParameter(processDefinitionBuilder, actor, user, null); } public ProcessDefinition deployProcessWithActorAndTestConnectorWithOutputAndParameter( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user, final Map parameters) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user, parameters, "TestConnectorWithOutput.impl", TestConnectorWithOutput.class, "TestConnectorWithOutput.jar"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/DoNothingConnector.java ================================================ /** * 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.connectors; import org.bonitasoft.engine.connector.AbstractConnector; import org.bonitasoft.engine.connector.ConnectorException; import org.bonitasoft.engine.connector.ConnectorValidationException; public class DoNothingConnector extends AbstractConnector { @Override protected void executeBusinessLogic() throws ConnectorException { } @Override public void validateInputParameters() throws ConnectorValidationException { } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/FailingConnector.java ================================================ /** * 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.connectors; import org.bonitasoft.engine.connector.AbstractConnector; import org.bonitasoft.engine.connector.ConnectorException; import org.bonitasoft.engine.connector.ConnectorValidationException; public class FailingConnector extends AbstractConnector { @Override protected void executeBusinessLogic() throws ConnectorException { throw new ConnectorException(""); } @Override public void validateInputParameters() throws ConnectorValidationException { } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnector.java ================================================ /** * 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.connectors; import org.bonitasoft.engine.connector.AbstractConnector; /** * @author Baptiste Mesta * @author Celine Souchet */ public class TestConnector extends AbstractConnector { public static final String INPUT1 = "input1"; @Override public void validateInputParameters() { } @Override protected void executeBusinessLogic() { final String input1Value = (String) getInputParameter(INPUT1); VariableStorage.getInstance().setVariable(INPUT1, input1Value); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnector2.java ================================================ /** * 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.connectors; import org.bonitasoft.engine.connector.AbstractConnector; /** * @author Yanyan Liu */ public class TestConnector2 extends AbstractConnector { @Override public void validateInputParameters() { } @Override protected void executeBusinessLogic() { final String input1Value = (String) getInputParameter("input1"); VariableStorage.getInstance().setVariable("input1", input1Value + 2); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnector3.java ================================================ /** * 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.connectors; import org.bonitasoft.engine.connector.AbstractConnector; /** * @author Sebastien Chevassu * @author Celine Souchet */ public class TestConnector3 extends AbstractConnector { public static final String INPUT1 = "input1"; public static final String INPUT2 = "input2"; public static final String INPUT3 = "input3"; public static final String INPUT4 = "input4"; @Override public void validateInputParameters() { } @Override protected void executeBusinessLogic() { final String input1Value = (String) getInputParameter(INPUT1); if (input1Value != null) { VariableStorage.getInstance().setVariable(INPUT1, input1Value); } final String input2Value = (String) getInputParameter(INPUT2); if (input2Value != null) { VariableStorage.getInstance().setVariable(INPUT2, input2Value); } final String input3Value = (String) getInputParameter(INPUT3); if (input3Value != null) { VariableStorage.getInstance().setVariable(INPUT3, input3Value); } final String input4Value = (String) getInputParameter(INPUT4); if (input4Value != null) { VariableStorage.getInstance().setVariable(INPUT4, input4Value); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnectorLongToExecute.java ================================================ /** * 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.connectors; import org.bonitasoft.engine.connector.AbstractConnector; import org.bonitasoft.engine.connector.ConnectorException; /** * @author Baptiste Mesta */ public class TestConnectorLongToExecute extends AbstractConnector { @Override public void validateInputParameters() { } @Override protected void executeBusinessLogic() throws ConnectorException { final Long timeToWait = (Long) getInputParameter("timeout"); try { Thread.sleep(timeToWait); } catch (final InterruptedException e) { throw new ConnectorException(e); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnectorThatThrowException.java ================================================ /** * 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.connectors; import org.bonitasoft.engine.connector.AbstractConnector; import org.bonitasoft.engine.connector.ConnectorException; import org.bonitasoft.engine.connector.ConnectorValidationException; /** * @author Baptiste Mesta */ public class TestConnectorThatThrowException extends AbstractConnector { public static final String BUSINESS_LOGIC_EXCEPTION_MESSAGE = "unexpected ══════════════════════════ during connector execution"; public static final String DISCONNECT = "disconnect"; public static final String CONNECT = "connect"; public static final String NONE = "none"; public static final String RUNTIME = "runtime"; public static final String KIND = "kind"; public static final String NORMAL = "normal"; @Override public void validateInputParameters() throws ConnectorValidationException { final Object inputParameter = getInputParameter(KIND); if (!NORMAL.equals(inputParameter) && !RUNTIME.equals(inputParameter) && !NONE.equals(inputParameter) && !CONNECT.equals(inputParameter)) { throw new ConnectorValidationException("bad kind of exception"); } } @Override protected void executeBusinessLogic() throws ConnectorException { final String kind = (String) getInputParameter(KIND); if (kind.equals(NORMAL)) { throw new ConnectorException(BUSINESS_LOGIC_EXCEPTION_MESSAGE); } else if (kind.equals(RUNTIME)) { throw new RuntimeException("unexpected"); } } @Override public void connect() { final String kind = (String) getInputParameter(KIND); if (kind.equals(CONNECT)) { throw new RuntimeException("unexpected error in connect"); } } @Override public void disconnect() { final String kind = (String) getInputParameter(KIND); if (kind.equals(DISCONNECT)) { throw new RuntimeException("unexpected error in connect"); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnectorWithAPICall.java ================================================ /** * 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.connectors; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.connector.AbstractConnector; /** * @author Charles Souillard */ public class TestConnectorWithAPICall extends AbstractConnector { @Override public void validateInputParameters() { } @Override protected void executeBusinessLogic() { final String name = (String) getInputParameter("processName"); final String version = (String) getInputParameter("processVersion"); long processId = -1; try { processId = getAPIAccessor().getProcessAPI().getProcessDefinitionId(name, version); } catch (ProcessDefinitionNotFoundException e) { throw new RuntimeException("Unable to get Process with name and version: " + name + ", " + version); } finally { setOutputParameter("processId", processId); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnectorWithConnectedResource.java ================================================ /** * 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.connectors; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.connector.AbstractConnector; /** * @author Elias Ricken de Medeiros */ public class TestConnectorWithConnectedResource extends AbstractConnector { private final List output; public TestConnectorWithConnectedResource() { output = new ArrayList(2); } @Override public void validateInputParameters() { } @Override protected void executeBusinessLogic() { setOutputParameter("output", output); } @Override public void connect() { output.add("connect"); } @Override public void disconnect() { output.add("disconect"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnectorWithModifiedOutput.java ================================================ /** * 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.connectors; import org.bonitasoft.engine.connector.AbstractConnector; /** * @author Elias Ricken de Medeiros */ public class TestConnectorWithModifiedOutput extends AbstractConnector { @Override public void validateInputParameters() { } @Override protected void executeBusinessLogic() { final String input1Value = (String) getInputParameter("input1"); setOutputParameter("output1", input1Value + "->modified"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnectorWithNotSerializableOutput.java ================================================ /** * 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.connectors; import org.bonitasoft.engine.connector.AbstractConnector; /** * @author Celine Souchet */ public class TestConnectorWithNotSerializableOutput extends AbstractConnector { @Override public void validateInputParameters() { } @Override protected void executeBusinessLogic() { setOutputParameter("output1", new Object()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestConnectorWithOutput.java ================================================ /** * 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.connectors; import org.bonitasoft.engine.connector.AbstractConnector; /** * @author Baptiste Mesta */ public class TestConnectorWithOutput extends AbstractConnector { @Override public void validateInputParameters() { } @Override protected void executeBusinessLogic() { Object input1Value = getInputParameter("input1"); setOutputParameter("output1", input1Value); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/TestExternalConnector.java ================================================ /** * 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.connectors; import org.bonitasoft.engine.connector.AbstractConnector; /** * @author Nicolas Chabanoles */ public class TestExternalConnector extends AbstractConnector { private boolean hasBeenValidated = false; @Override public void validateInputParameters() { hasBeenValidated = true; } @Override protected void executeBusinessLogic() { final String input1Value = (String) getInputParameter("param1"); setOutputParameter("param1", input1Value); setOutputParameter("hasBeenValidated", hasBeenValidated); final Object returnNotSerializableOutput = getInputParameter("returnNotSerializableOutput"); if (returnNotSerializableOutput != null) { setOutputParameter("notSerializable", new Object()); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/connectors/VariableStorage.java ================================================ /** * 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.connectors; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; /** * @author Matthieu Chaffotte */ public final class VariableStorage implements Serializable { private static final long serialVersionUID = -4195221111626812999L; private final Map variables; private VariableStorage() { variables = new HashMap(); } private static class VariableStorageHolder { public static final VariableStorage INSTANCE = new VariableStorage(); public static final Map instances = new HashMap(); } public static VariableStorage getInstance() { return VariableStorageHolder.INSTANCE; } public static VariableStorage getInstance(final long tenantId) { VariableStorage variableStorage = VariableStorageHolder.instances.get(tenantId); if (variableStorage == null) { variableStorage = new VariableStorage(); VariableStorageHolder.instances.put(tenantId, variableStorage); } return variableStorage; } public synchronized void setVariable(final String name, final Object value) { variables.put(name, value); } public Object getVariableValue(final String name) { return variables.get(name); } public Map getVariables() { return variables; } public static void clearAll() { VariableStorageHolder.INSTANCE.clear(); for (final Entry entry : VariableStorageHolder.instances.entrySet()) { entry.getValue().clear(); } VariableStorageHolder.instances.clear(); } public void clear() { variables.clear(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/event/AbstractEventIT.java ================================================ /** * 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.event; import static org.junit.Assert.assertEquals; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.actor.ActorCriterion; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.flownode.TimerType; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder; import org.bonitasoft.engine.bpm.process.impl.CatchMessageEventTriggerDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.IntermediateThrowEventDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.StartEventDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ThrowMessageEventTriggerBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.connectors.TestConnectorThatThrowException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.BuildTestUtil; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ public abstract class AbstractEventIT extends TestWithUser { public static final String EXCEPTION_STEP = "exceptionStep"; public static final String DATE_FORMAT_WITH_MS = "yyyyy-mm-dd hh:mm:ss SSSSS"; public static final String BOUNDARY_NAME = "waitMessage"; public static final String INT_DATA_NAME = "count"; public static final String SHORT_DATA_NAME = "short_data_name"; public static final String SUB_PROCESS_START_NAME = "subProcessStart"; public static final String SUB_PROCESS_USER_TASK_NAME = "subStep"; public static final String SUB_PROCESS_NAME = "eventSubProcess"; public static final String PARENT_PROCESS_USER_TASK_NAME = "step1"; public static final String PARENT_END = "end"; public static final String THROW_MESSAGE_TASK_NAME = "messageTask"; public static final String MESSAGE_NAME = "canStart"; public static final String START_WITH_MESSAGE_STEP1_NAME = "userStart1"; public static final String START_WITH_MESSAGE_PROCESS_NAME = "Start from message"; public static final String CATCH_EVENT_NAME = "waitForMessage"; public static final String CATCH_MESSAGE_PROCESS_NAME = "Catch a message"; public static final String CATCH_MESSAGE_STEP1_NAME = "step1"; public static final String SEND_MESSAGE_PROCESS_NAME = "Send a message"; public static final String AFTER_SIGNAL = "after_signal"; public static final String SIGNAL_NAME = "canStart"; public static final String START_EVENT_NAME = "startEvent"; /** * Deploy and enable a process with a human task having a timer boundary event followed by another human task * without boundary * * @param timerValue * after how long time the boundary will be triggered * @param interrupting * define whether the boundary is interrupting or not * @param taskWithBoundaryName * the name of user task containing the boundary event * @param exceptionTaskName * the name of human task reached by exception flow * @param normalFlowTaskName * name of human task following the task containing the boundary (normal flow) * @return * @throws Exception * @throws InvalidProcessDefinitionException */ public ProcessDefinition deployAndEnableProcessWithBoundaryTimerEvent(final long timerValue, final boolean interrupting, final String taskWithBoundaryName, final String exceptionTaskName, final String normalFlowTaskName) throws Exception { return deployAndEnableProcessWithBoundaryTimerEvent(TimerType.DURATION, timerValue, interrupting, taskWithBoundaryName, exceptionTaskName, normalFlowTaskName); } /** * Deploy and enable a process with a human task having a timer boundary event followed by another human task * without boundary * * @param timerType * the timer time * @param timerValue * the timer value * @param interrupting * define whether the boundary is interrupting or not * @param taskWithBoundaryName * the name of user task containing the boundary event * @param exceptionTaskName * the name of human task reached by exception flow * @param normalFlowTaskName * name of human task following the task containing the boundary (normal flow) * @return * @throws InvalidProcessDefinitionException * @since 6.0 */ public ProcessDefinition deployAndEnableProcessWithBoundaryTimerEvent(final TimerType timerType, final long timerValue, final boolean interrupting, final String taskWithBoundaryName, final String exceptionTaskName, final String normalFlowTaskName) throws Exception { final Expression timerExpr = new ExpressionBuilder().createConstantLongExpression(timerValue); return deployAndEnableProcessWithBoundaryTimerEvent(timerType, timerExpr, interrupting, taskWithBoundaryName, exceptionTaskName, normalFlowTaskName); } /** * Deploy and enable a process with a human task having a timer boundary event followed by another human task * without boundary * * @param timerType * the timer time * @param timerExpr * the timer value * @param interrupting * define whether the boundary is interrupting or not * @param taskWithBoundaryName * the name of user task containing the boundary event * @param exceptionTaskName * the name of human task reached by exception flow * @param normalFlowTaskName * name of human task following the task containing the boundary (normal flow) * @return * @throws InvalidProcessDefinitionException * @since 6.0 */ public ProcessDefinition deployAndEnableProcessWithBoundaryTimerEvent(final TimerType timerType, final Expression timerExpr, final boolean interrupting, final String taskWithBoundaryName, final String exceptionTaskName, final String normalFlowTaskName) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("pTimerBoundary", "2.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("start"); final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder .addUserTask(taskWithBoundaryName, ACTOR_NAME); userTaskDefinitionBuilder.addBoundaryEvent("timer", interrupting).addTimerEventTriggerDefinition(timerType, timerExpr); processDefinitionBuilder.addUserTask(exceptionTaskName, ACTOR_NAME); processDefinitionBuilder.addUserTask(normalFlowTaskName, ACTOR_NAME); processDefinitionBuilder.addEndEvent("end"); processDefinitionBuilder.addTransition("start", taskWithBoundaryName); processDefinitionBuilder.addTransition(taskWithBoundaryName, normalFlowTaskName); processDefinitionBuilder.addTransition("timer", exceptionTaskName); return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithBoundaryTimerEventOnHumanTask(final long timerValue, final boolean withData) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("pTimerBoundary", "2.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("start"); final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); final Expression timerExpr; if (withData) { processDefinitionBuilder.addData("timer", Long.class.getName(), new ExpressionBuilder().createConstantLongExpression(timerValue)); timerExpr = new ExpressionBuilder().createDataExpression("timer", Long.class.getName()); } else { timerExpr = new ExpressionBuilder().createConstantLongExpression(timerValue); } userTaskDefinitionBuilder.addBoundaryEvent("Boundary timer").addTimerEventTriggerDefinition(TimerType.DURATION, timerExpr); userTaskDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME); userTaskDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addEndEvent("end"); processDefinitionBuilder.addTransition("start", "step1"); processDefinitionBuilder.addTransition("step1", "step2"); processDefinitionBuilder.addTransition("Boundary timer", EXCEPTION_STEP); processDefinitionBuilder.addTransition(EXCEPTION_STEP, "end"); return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); } /** * Deploy and enable a process containing two timer boundary events attached to a call activity * * @param timerDuration * after how long time the boundary will be triggered * @param interrupting * define whether the boundary is interrupting or not * @param targetProcessName * the name of called process * @return * @throws InvalidExpressionException * @throws InvalidProcessDefinitionException * @since 6.0 */ public ProcessDefinition deployAndEnableProcessWithBoundaryTimerEventOnCallActivity(final long timerDuration, final boolean interrupting, final String targetProcessName) throws Exception { final Expression timerExpr = new ExpressionBuilder().createConstantLongExpression(timerDuration); final Expression timerExpr2 = new ExpressionBuilder().createConstantLongExpression(timerDuration + 1000L); final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression(targetProcessName); final Expression targetProcessVersionExpr = new ExpressionBuilder().createConstantStringExpression("1.0"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "processWithCallActivityAndBoundaryEvent", "2.0"); processDefinitionBuilder.addActor(ACTOR_NAME).addStartEvent("start"); final CallActivityBuilder callActivityBuilder = processDefinitionBuilder.addCallActivity("callActivity", targetProcessNameExpr, targetProcessVersionExpr); callActivityBuilder.addBoundaryEvent("timer", interrupting).addTimerEventTriggerDefinition(TimerType.DURATION, timerExpr); callActivityBuilder.addBoundaryEvent("timer2", interrupting).addTimerEventTriggerDefinition(TimerType.DURATION, timerExpr2); processDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME) .addUserTask(PARENT_PROCESS_USER_TASK_NAME, ACTOR_NAME).addEndEvent("end") .addTransition("start", "callActivity").addTransition("callActivity", PARENT_PROCESS_USER_TASK_NAME) .addTransition(PARENT_PROCESS_USER_TASK_NAME, "end").addTransition("timer", EXCEPTION_STEP) .addTransition("timer2", EXCEPTION_STEP); return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); } /** * Deploy and enable a process with a timer boundary event attached to a multi-instance * * @param timerValue * after how long time the boundary will be triggered * @param interrupting * define whether the boundary is interrupting or not * @param multiTaskName * the multi-instance name * @param loopCardinality * the multi-instance cardinality * @param isSequential * define whether the multi-instance is sequential or parallel * @param normalFlowTaskName * the name of user task following the multi-instance in the normal flow * @param exceptionFlowTaskName * the name of the user task reached by the exception flow * @return * @throws InvalidProcessDefinitionException * @since 6.0 */ public ProcessDefinition deployAndEnableProcessMultiInstanceWithBoundaryEvent(final long timerValue, final boolean interrupting, final String multiTaskName, final int loopCardinality, final boolean isSequential, final String normalFlowTaskName, final String exceptionFlowTaskName) throws Exception { final Expression timerExpr = new ExpressionBuilder().createConstantLongExpression(timerValue); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithMultiInstanceAndBoundaryEvent", "1.0"); processBuilder.addActor(ACTOR_NAME).addStartEvent("start"); final UserTaskDefinitionBuilder userTaskBuilder = processBuilder.addUserTask(multiTaskName, ACTOR_NAME); userTaskBuilder.addMultiInstance(isSequential, new ExpressionBuilder().createConstantIntegerExpression(loopCardinality)); userTaskBuilder.addBoundaryEvent("timer", interrupting).addTimerEventTriggerDefinition(TimerType.DURATION, timerExpr); processBuilder.addUserTask(normalFlowTaskName, ACTOR_NAME).addUserTask(exceptionFlowTaskName, ACTOR_NAME) .addEndEvent("end"); processBuilder.addTransition("start", multiTaskName); processBuilder.addTransition(multiTaskName, normalFlowTaskName); processBuilder.addTransition(normalFlowTaskName, "end"); processBuilder.addTransition("timer", exceptionFlowTaskName); return deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); } /** * Deploy and enable a process with a timer boundary event attached to a loop activity * * @param timerValue * after how long time the boundary will be triggered * @param interrupting * define whether the boundary is interrupting or not * @param loopMax * how many loops will be executed * @param loopActivityName * the name of the loop activity * @param normalFlowStepName * the name of the user task following the loop activity in the normal flow * @param exceptionFlowStepName * the name of the user task reached by the exception flow * @return * @throws InvalidProcessDefinitionException * @since 6.0 */ public ProcessDefinition deployAndEnableProcessWithBoundaryTimerEventOnLoopActivity(final long timerValue, final boolean interrupting, final int loopMax, final String loopActivityName, final String normalFlowStepName, final String exceptionFlowStepName) throws Exception { final Expression timerExpr = new ExpressionBuilder().createConstantLongExpression(timerValue); final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithMultiInstanceAndBoundaryEvent", "1.0"); processBuilder.addActor(ACTOR_NAME).addStartEvent("start"); final UserTaskDefinitionBuilder userTaskBuilder = processBuilder.addUserTask(loopActivityName, ACTOR_NAME); userTaskBuilder.addLoop(false, condition, new ExpressionBuilder().createConstantIntegerExpression(loopMax)); userTaskBuilder.addBoundaryEvent("timer", interrupting).addTimerEventTriggerDefinition(TimerType.DURATION, timerExpr); processBuilder.addUserTask(normalFlowStepName, ACTOR_NAME).addUserTask(exceptionFlowStepName, ACTOR_NAME) .addEndEvent("end") .addTransition("start", loopActivityName).addTransition(loopActivityName, normalFlowStepName) .addTransition(normalFlowStepName, "end") .addTransition("timer", exceptionFlowStepName); return deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); } protected void waitForUserTasksAndExecuteIt(final String taskName, final ProcessInstance processInstance, final int nbOfRemainingInstances) throws Exception { for (int i = 0; i < nbOfRemainingInstances; i++) { waitForUserTaskAndExecuteIt(processInstance, taskName, user); } } protected void executeRemainingParallelMultiInstances(final String taskName, final ProcessInstance processInstance, final int nbOfRemainingInstances) throws Exception { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, Math.max(10, nbOfRemainingInstances)); builder.filter(ActivityInstanceSearchDescriptor.NAME, taskName); builder.filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()); builder.filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.USER_TASK); final SearchResult searchResult = getProcessAPI().searchActivities(builder.done()); assertEquals(nbOfRemainingInstances, searchResult.getCount()); for (final ActivityInstance activity : searchResult.getResult()) { assignAndExecuteStep(activity, user.getId()); } } public ProcessDefinition deployAndEnableProcessWithInterruptingAndNonInterruptingTimer(final long interruptTimer, final long nonInterruptingTimer, final String taskWithBoundaryName, final String interruptExceptionTaskName, final String nonInterruptExceptionTaskName, final String interruptTimerName, final String nonInterruptTimerName) throws Exception { final String normalFlowTaskName = "normalFlow"; final Expression interruptTimerExpr = new ExpressionBuilder().createConstantLongExpression(interruptTimer); final Expression nonInterruptTimerExpr = new ExpressionBuilder() .createConstantLongExpression(nonInterruptingTimer); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("pTimerBoundary", "2.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("start"); final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder .addUserTask(taskWithBoundaryName, ACTOR_NAME); userTaskDefinitionBuilder.addBoundaryEvent(interruptTimerName, true) .addTimerEventTriggerDefinition(TimerType.DURATION, interruptTimerExpr); userTaskDefinitionBuilder.addBoundaryEvent(nonInterruptTimerName, false) .addTimerEventTriggerDefinition(TimerType.DURATION, nonInterruptTimerExpr); processDefinitionBuilder.addUserTask(interruptExceptionTaskName, ACTOR_NAME); processDefinitionBuilder.addUserTask(nonInterruptExceptionTaskName, ACTOR_NAME); processDefinitionBuilder.addUserTask(normalFlowTaskName, ACTOR_NAME); processDefinitionBuilder.addEndEvent("end"); processDefinitionBuilder.addTransition("start", taskWithBoundaryName); processDefinitionBuilder.addTransition(taskWithBoundaryName, normalFlowTaskName); processDefinitionBuilder.addTransition(interruptTimerName, interruptExceptionTaskName); processDefinitionBuilder.addTransition(nonInterruptTimerName, nonInterruptExceptionTaskName); return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithEndThrowErrorEvent(final String processName, final String errorCode) throws Exception { final ProcessDefinitionBuilder calledProcess = new ProcessDefinitionBuilder().createNewInstance(processName, "1.0"); calledProcess.addActor(ACTOR_NAME); calledProcess.addStartEvent("start"); calledProcess.addUserTask("calledStep1", ACTOR_NAME); calledProcess.addUserTask("calledStep2", ACTOR_NAME); calledProcess.addEndEvent("endError").addErrorEventTrigger(errorCode); calledProcess.addEndEvent("end").addTerminateEventTrigger(); calledProcess.addTransition("start", "calledStep1"); calledProcess.addTransition("start", "calledStep2"); calledProcess.addTransition("calledStep1", "endError"); calledProcess.addTransition("calledStep2", "end"); return deployAndEnableProcessWithActor(calledProcess.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithBoundaryErrorEventOnCallActivity(final String processName, final String targetProcessName, final String callActivityName, final String errorCode, final String ACTOR_NAME) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(processName, "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("start"); final CallActivityBuilder callActivityBuilder = processDefinitionBuilder.addCallActivity(callActivityName, new ExpressionBuilder().createConstantStringExpression(targetProcessName), new ExpressionBuilder().createConstantStringExpression("1.0")); callActivityBuilder.addBoundaryEvent("error", true).addErrorEventTrigger(errorCode); processDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addEndEvent("end"); processDefinitionBuilder.addTransition("start", callActivityName); processDefinitionBuilder.addTransition(callActivityName, "step2"); processDefinitionBuilder.addTransition("error", EXCEPTION_STEP); processDefinitionBuilder.addTransition(EXCEPTION_STEP, "end"); processDefinitionBuilder.addTransition("step2", "end"); return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableSubProcessWhichThrowsAnErrorEvent(final String processName, final String errorCode) throws Exception { final ProcessDefinitionBuilder process = new ProcessDefinitionBuilder().createNewInstance(processName, "1.0"); process.addStartEvent("start"); process.addEndEvent("end").addErrorEventTrigger(errorCode); process.addTransition("start", "end"); return deployAndEnableProcess(process.done()); } public ProcessDefinition deployAndEnableMidProcessWhichContainsACallActivity(final String processName, final String targetProcessName) throws Exception { final ProcessDefinitionBuilder process = new ProcessDefinitionBuilder().createNewInstance(processName, "1.0"); process.addStartEvent("start"); process.addCallActivity("ca", new ExpressionBuilder().createConstantStringExpression(targetProcessName), new ExpressionBuilder().createConstantStringExpression("1.0")); process.addEndEvent("end"); process.addTransition("start", "ca"); process.addTransition("ca", "end"); return deployAndEnableProcess(process.done()); } public ProcessDefinition deployAndEnableProcessWithBoundaryErrorEventOnMICallActivity(final String processName, final String targetProcessName, final String errorCode, final String ACTOR_NAME) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(processName, "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("start"); final CallActivityBuilder callActivityBuilder = processDefinitionBuilder.addCallActivity("step1", new ExpressionBuilder().createConstantStringExpression(targetProcessName), new ExpressionBuilder().createConstantStringExpression("1.0")); callActivityBuilder.addBoundaryEvent("error", true).addErrorEventTrigger(errorCode); callActivityBuilder.addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(1)); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME); processDefinitionBuilder.addTransition("start", "step1"); processDefinitionBuilder.addTransition("step1", "step2"); processDefinitionBuilder.addTransition("error", EXCEPTION_STEP); return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithTimerEventSubProcess(final long timerDuration) throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithEventSubProcess", "1.0"); builder.addActor(ACTOR_NAME); builder.addStartEvent("start"); builder.addUserTask("step1", ACTOR_NAME); builder.addEndEvent("end"); builder.addTransition("start", "step1"); builder.addTransition("step1", "end"); final SubProcessDefinitionBuilder subProcessBuilder = builder.addSubProcess("eventSubProcess", true) .getSubProcessBuilder(); subProcessBuilder.addStartEvent("timerStart").addTimerEventTriggerDefinition(TimerType.DURATION, new ExpressionBuilder().createConstantLongExpression(timerDuration)); subProcessBuilder.addUserTask("subStep", ACTOR_NAME); subProcessBuilder.addEndEvent("endSubProcess"); subProcessBuilder.addTransition("timerStart", "subStep"); subProcessBuilder.addTransition("subStep", "endSubProcess"); return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithTimerEventSubProcessAndData(final long timerDuration) throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithEventSubProcess", "1.0"); builder.addShortTextData("content", new ExpressionBuilder().createConstantStringExpression("parentVar")); builder.addIntegerData("count", new ExpressionBuilder().createConstantIntegerExpression(1)); builder.addActor(ACTOR_NAME); builder.addStartEvent("start"); builder.addUserTask("step1", ACTOR_NAME); builder.addEndEvent("end"); builder.addTransition("start", "step1"); builder.addTransition("step1", "end"); final SubProcessDefinitionBuilder subProcessBuilder = builder.addSubProcess("eventSubProcess", true) .getSubProcessBuilder(); subProcessBuilder.addShortTextData("content", new ExpressionBuilder().createConstantStringExpression("childVar")); subProcessBuilder.addDoubleData("value", new ExpressionBuilder().createConstantDoubleExpression(10.0)); subProcessBuilder.addStartEvent("timerStart").addTimerEventTriggerDefinition(TimerType.DURATION, new ExpressionBuilder().createConstantLongExpression(timerDuration)); subProcessBuilder.addUserTask("subStep", ACTOR_NAME).addShortTextData("content", new ExpressionBuilder().createConstantStringExpression("childActivityVar")); subProcessBuilder.addEndEvent("endSubProcess"); subProcessBuilder.addTransition("timerStart", "subStep"); subProcessBuilder.addTransition("subStep", "endSubProcess"); final DesignProcessDefinition processDefinition = builder.done(); return deployAndEnableProcessWithActor(processDefinition, ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithErrorEventSubProcessAndData(final String catchErrorCode, final String throwErroCode, final String subProcStartEventName) throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithEventSubProcess", "1.0"); builder.addShortTextData("content", new ExpressionBuilder().createConstantStringExpression("parentVar")); builder.addIntegerData("count", new ExpressionBuilder().createConstantIntegerExpression(1)); builder.addActor(ACTOR_NAME); builder.addStartEvent("start"); builder.addUserTask("step1", ACTOR_NAME); builder.addUserTask("step2", ACTOR_NAME); builder.addEndEvent("end"); builder.addEndEvent("endError").addErrorEventTrigger(throwErroCode); builder.addTransition("start", "step1"); builder.addTransition("start", "step2"); builder.addTransition("step1", "end"); builder.addTransition("step2", "endError"); final SubProcessDefinitionBuilder subProcessBuilder = builder .addSubProcess(BuildTestUtil.EVENT_SUB_PROCESS_NAME, true).getSubProcessBuilder(); subProcessBuilder.addShortTextData("content", new ExpressionBuilder().createConstantStringExpression("childVar")); subProcessBuilder.addDoubleData("value", new ExpressionBuilder().createConstantDoubleExpression(10.0)); subProcessBuilder.addStartEvent(subProcStartEventName).addErrorEventTrigger(catchErrorCode); subProcessBuilder.addUserTask("subStep", ACTOR_NAME).addShortTextData("content", new ExpressionBuilder().createConstantStringExpression("childActivityVar")); subProcessBuilder.addEndEvent("endSubProcess"); subProcessBuilder.addTransition(subProcStartEventName, "subStep"); subProcessBuilder.addTransition("subStep", "endSubProcess"); return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithErrorEventSubProcessAndDataOnlyInRoot(final String errorCode, final String subProcStartEventName, final String rootUserTaskName, final String subProcUserTaskName, final String dataName, final String dataValue) throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithEventSubProcess", "1.0"); builder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression(dataValue)); builder.addActor(ACTOR_NAME); builder.addStartEvent("start"); builder.addUserTask(rootUserTaskName, ACTOR_NAME); builder.addEndEvent("end"); builder.addEndEvent("endError").addErrorEventTrigger(errorCode); builder.addTransition("start", rootUserTaskName); builder.addTransition(rootUserTaskName, "endError"); final SubProcessDefinitionBuilder subProcessBuilder = builder .addSubProcess(BuildTestUtil.EVENT_SUB_PROCESS_NAME, true).getSubProcessBuilder(); subProcessBuilder.addStartEvent(subProcStartEventName).addErrorEventTrigger(errorCode); subProcessBuilder.addUserTask(subProcUserTaskName, ACTOR_NAME); subProcessBuilder.addEndEvent("endSubProcess"); subProcessBuilder.addTransition(subProcStartEventName, subProcUserTaskName); subProcessBuilder.addTransition(subProcUserTaskName, "endSubProcess"); return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithErrorEventSubProcessAndDataOnlyInSubProc(final String errorCode, final String subProcStartEventName, final String rootUserTaskName, final String subProcUserTaskName, final String dataName, final String dataValue) throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithEventSubProcess", "1.0"); builder.addActor(ACTOR_NAME); builder.addStartEvent("start"); builder.addUserTask(rootUserTaskName, ACTOR_NAME); builder.addEndEvent("end"); builder.addEndEvent("endError").addErrorEventTrigger(errorCode); builder.addTransition("start", rootUserTaskName); builder.addTransition(rootUserTaskName, "endError"); final SubProcessDefinitionBuilder subProcessBuilder = builder .addSubProcess(BuildTestUtil.EVENT_SUB_PROCESS_NAME, true).getSubProcessBuilder(); subProcessBuilder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression(dataValue)); subProcessBuilder.addStartEvent(subProcStartEventName).addErrorEventTrigger(errorCode); subProcessBuilder.addUserTask(subProcUserTaskName, ACTOR_NAME); subProcessBuilder.addEndEvent("endSubProcess"); subProcessBuilder.addTransition(subProcStartEventName, subProcUserTaskName); subProcessBuilder.addTransition(subProcUserTaskName, "endSubProcess"); return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithErrorEventSubProcess(final String catchErrorCode, final String throwErrorCode, final String subProcStartEventName) throws Exception { final Expression transitionCondition = new ExpressionBuilder().createDataExpression("throwException", Boolean.class.getName()); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithEventSubProcess", "1.0"); builder.addActor(ACTOR_NAME); builder.addBooleanData("throwException", new ExpressionBuilder().createConstantBooleanExpression(true)); builder.addStartEvent("start"); builder.addUserTask("step1", ACTOR_NAME); builder.addUserTask("step2", ACTOR_NAME); builder.addEndEvent("end"); builder.addEndEvent("endError").addErrorEventTrigger(throwErrorCode); builder.addTransition("start", "step1"); builder.addTransition("start", "step2"); builder.addTransition("step1", "end"); builder.addTransition("step2", "endError", transitionCondition); builder.addDefaultTransition("step2", "end"); final SubProcessDefinitionBuilder subProcessBuilder = builder .addSubProcess(BuildTestUtil.EVENT_SUB_PROCESS_NAME, true).getSubProcessBuilder(); if (catchErrorCode == null) { subProcessBuilder.addStartEvent(subProcStartEventName).addErrorEventTrigger(); } else { subProcessBuilder.addStartEvent(subProcStartEventName).addErrorEventTrigger(catchErrorCode); } subProcessBuilder.addUserTask("subStep", ACTOR_NAME); subProcessBuilder.addEndEvent("endSubProcess"); subProcessBuilder.addTransition(subProcStartEventName, "subStep"); subProcessBuilder.addTransition("subStep", "endSubProcess"); return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithTestConnectorThatThrowException( final ProcessDefinitionBuilder processDefinitionBuilder) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnector(processDefinitionBuilder, ACTOR_NAME, user, "TestConnectorThatThrowException.impl", TestConnectorThatThrowException.class, "TestConnectorThatThrowException.jar"); } public ProcessDefinition deployAndEnableProcessWithBoundaryMessageEvent(final String message) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("pMessageBoundary", "2.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("start"); final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); userTaskDefinitionBuilder.addBoundaryEvent(BOUNDARY_NAME, true).addMessageEventTrigger(message); final String otherBoundaryNotTriggered = "otherBoundaryNotTriggered"; userTaskDefinitionBuilder.addBoundaryEvent(otherBoundaryNotTriggered, true).addSignalEventTrigger("bat signal"); userTaskDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME); userTaskDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addEndEvent("end"); processDefinitionBuilder.addTransition("start", "step1"); processDefinitionBuilder.addTransition("step1", "step2"); processDefinitionBuilder.addTransition(BOUNDARY_NAME, EXCEPTION_STEP); processDefinitionBuilder.addTransition(otherBoundaryNotTriggered, "end"); return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithBoundarySignalEvent(final String signalName) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("pSignalBoundary", "2.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("start"); final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); userTaskDefinitionBuilder.addBoundaryEvent("waitSignal", true).addSignalEventTrigger(signalName); userTaskDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME); userTaskDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addEndEvent("end"); processDefinitionBuilder.addTransition("start", "step1"); processDefinitionBuilder.addTransition("step1", "step2"); processDefinitionBuilder.addTransition("waitSignal", EXCEPTION_STEP); return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithBoundaryMessageEventOnLoopActivity(final int loopMax) throws Exception { final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithLoopActivityAndBoundaryEvent", "1.0"); processBuilder.addActor(ACTOR_NAME).addStartEvent("start"); final UserTaskDefinitionBuilder userTaskBuilder = processBuilder.addUserTask("step1", ACTOR_NAME); userTaskBuilder.addLoop(false, condition, new ExpressionBuilder().createConstantIntegerExpression(loopMax)); userTaskBuilder.addBoundaryEvent(BOUNDARY_NAME, true).addMessageEventTrigger("MyMessage"); processBuilder.addUserTask("step2", ACTOR_NAME).addUserTask(EXCEPTION_STEP, ACTOR_NAME).addEndEvent("end") .addTransition("start", "step1") .addTransition("step1", "step2").addTransition("step2", "end") .addTransition(BOUNDARY_NAME, EXCEPTION_STEP); return deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); } /** * Deploy and enable a simple process: start event -> user task -> end event * * @param processName * the process name * @param userTaskName * the user task name * @return * @throws InvalidProcessDefinitionException * @since 6.0 */ public ProcessDefinition deployAndEnableSimpleProcess(final String processName, final String userTaskName) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(processName, "1.0"); processBuilder.addActor(ACTOR_NAME).addStartEvent("startCA").addUserTask(userTaskName, ACTOR_NAME) .addEndEvent("endCA") .addTransition("startCA", userTaskName).addTransition(userTaskName, "endCA"); return deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithBoundaryMessageEventOnCallActivity() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("pMessageBoundary", "2.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("start"); final CallActivityBuilder callActivityBuilder = processDefinitionBuilder.addCallActivity("step1", new ExpressionBuilder().createConstantStringExpression("calledProcess"), new ExpressionBuilder().createConstantStringExpression("1.0")); callActivityBuilder.addBoundaryEvent(BOUNDARY_NAME, true).addMessageEventTrigger("MyMessage"); processDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addEndEvent("end"); processDefinitionBuilder.addTransition("start", "step1"); processDefinitionBuilder.addTransition("step1", "step2"); processDefinitionBuilder.addTransition(BOUNDARY_NAME, EXCEPTION_STEP); return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithBoundaryMessageEventOnMultiInstance(final int loopCardinality, final boolean isSequential) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance( "processWithBoundaryMessageEventAndMultiInstance", "1.0"); processBuilder.addActor(ACTOR_NAME).addStartEvent("start"); final UserTaskDefinitionBuilder userTaskBuilder = processBuilder.addUserTask("step1", ACTOR_NAME); userTaskBuilder.addMultiInstance(isSequential, new ExpressionBuilder().createConstantIntegerExpression(loopCardinality)); userTaskBuilder.addBoundaryEvent(BOUNDARY_NAME, true).addMessageEventTrigger("MyMessage"); processBuilder.addUserTask("step2", ACTOR_NAME).addUserTask(EXCEPTION_STEP, ACTOR_NAME).addEndEvent("end") .addTransition("start", "step1") .addTransition("step1", "step2").addTransition("step2", "end") .addTransition(BOUNDARY_NAME, EXCEPTION_STEP); return deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithCallActivity(final String targetProcessName, final String targetVersion) throws Exception { final Expression targetProcessExpr = new ExpressionBuilder().createConstantStringExpression(targetProcessName); final Expression targetVersionExpr = new ExpressionBuilder().createConstantStringExpression(targetVersion); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithCallActivity", "1.0"); builder.addActor(ACTOR_NAME); builder.addStartEvent("start"); builder.addCallActivity("callActivity", targetProcessExpr, targetVersionExpr); builder.addUserTask("step2", ACTOR_NAME); builder.addEndEvent(PARENT_END); builder.addTransition("start", "callActivity"); builder.addTransition("callActivity", "step2"); builder.addTransition("step2", PARENT_END); return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithMessageEventSubProcess() throws Exception { final ProcessDefinitionBuilder builder = buildParentProcessDefinition(false, false); buildSubProcessDefinition(builder, false, null, false); return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithMessageEventSubProcessAndData( final List> correlations) throws Exception { final ProcessDefinitionBuilder builder = buildParentProcessDefinition(false, true); buildSubProcessDefinition(builder, true, correlations, false); return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); } public ProcessDefinitionBuilder buildParentProcessDefinition(final boolean withIntermediateThrowEvent, final boolean withData) throws InvalidExpressionException { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithEventSubProcess", "1.0"); builder.addActor(ACTOR_NAME); builder.addStartEvent("start"); builder.addUserTask(PARENT_PROCESS_USER_TASK_NAME, ACTOR_NAME); builder.addEndEvent(PARENT_END); builder.addTransition("start", PARENT_PROCESS_USER_TASK_NAME); if (withIntermediateThrowEvent) { builder.addIntermediateThrowEvent(SIGNAL_NAME).addSignalEventTrigger(SIGNAL_NAME); builder.addUserTask(AFTER_SIGNAL, ACTOR_NAME); builder.addTransition(PARENT_PROCESS_USER_TASK_NAME, SIGNAL_NAME); builder.addTransition(SIGNAL_NAME, AFTER_SIGNAL); builder.addTransition(SIGNAL_NAME, PARENT_END); } else { builder.addTransition(PARENT_PROCESS_USER_TASK_NAME, PARENT_END); } if (withData) { builder.addShortTextData(SHORT_DATA_NAME, new ExpressionBuilder().createConstantStringExpression("parentVar")); builder.addIntegerData(INT_DATA_NAME, new ExpressionBuilder().createConstantIntegerExpression(1)); } return builder; } public void buildSubProcessDefinition(final ProcessDefinitionBuilder builder, final boolean withData, final List> correlations, final boolean isSignal) throws InvalidExpressionException { final SubProcessDefinitionBuilder subProcessBuilder = builder.addSubProcess(SUB_PROCESS_NAME, true) .getSubProcessBuilder(); final StartEventDefinitionBuilder startEventDefinitionBuilder = subProcessBuilder .addStartEvent(SUB_PROCESS_START_NAME); if (isSignal) { startEventDefinitionBuilder.addSignalEventTrigger(SIGNAL_NAME); } else { final CatchMessageEventTriggerDefinitionBuilder messageEventTrigger = startEventDefinitionBuilder .addMessageEventTrigger(MESSAGE_NAME); if (withData) { addCorrelations(messageEventTrigger, correlations); } } final UserTaskDefinitionBuilder userTask = subProcessBuilder.addUserTask(SUB_PROCESS_USER_TASK_NAME, ACTOR_NAME); subProcessBuilder.addEndEvent("endSubProcess"); subProcessBuilder.addTransition(SUB_PROCESS_START_NAME, SUB_PROCESS_USER_TASK_NAME); subProcessBuilder.addTransition(SUB_PROCESS_USER_TASK_NAME, "endSubProcess"); if (withData) { subProcessBuilder.addShortTextData(SHORT_DATA_NAME, new ExpressionBuilder().createConstantStringExpression("childVar")); subProcessBuilder.addDoubleData("value", new ExpressionBuilder().createConstantDoubleExpression(10.0)); userTask.addShortTextData(SHORT_DATA_NAME, new ExpressionBuilder().createConstantStringExpression("childActivityVar")); } } public void addCorrelations(final CatchMessageEventTriggerDefinitionBuilder messageEventTrigger, final List> correlations) { if (correlations != null) { for (final Entry correlation : correlations) { messageEventTrigger.addCorrelation(correlation.getKey(), correlation.getValue()); } } } public void addCorrelations(final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder, final List> correlations) { if (correlations != null) { for (final Entry entry : correlations) { throwMessageEventTriggerBuilder.addCorrelation(entry.getKey(), entry.getValue()); } } } public ProcessDefinition deployAndEnableProcessWithEndMessageEvent(final String targetProcess, final String targetFlowNode) throws Exception { return deployAndEnableProcessWithEndMessageEvent(targetProcess, targetFlowNode, null, null, null, null); } public ProcessDefinition deployAndEnableProcessWithEndMessageEvent(final String targetProcess, final String targetFlowNode, final List> correlations, final Map processData, final Map messageData, final Map dataInputMapping) throws Exception { return deployAndEnableProcessWithEndMessageEvent("Send message in the end", MESSAGE_NAME, targetProcess, targetFlowNode, correlations, processData, messageData, dataInputMapping); } public ProcessDefinition deployAndEnableProcessWithEndMessageEvent(final String processName, final String messageName, final String targetProcess, final String targetFlowNode, final List> correlations, final Map processData, final Map messageData, final Map dataInputMapping) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder(); processBuilder.createNewInstance(processName, PROCESS_VERSION); addProcessData(processData, processBuilder); processBuilder.addStartEvent(START_EVENT_NAME); processBuilder.addAutomaticTask("auto1"); // create expression for target process/flowNode final Expression targetProcessExpression = new ExpressionBuilder() .createConstantStringExpression(targetProcess); final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder; if (targetFlowNode != null) { final Expression targetFlowNodeExpression = new ExpressionBuilder() .createConstantStringExpression(targetFlowNode); throwMessageEventTriggerBuilder = processBuilder.addEndEvent("endEvent").addMessageEventTrigger(messageName, targetProcessExpression, targetFlowNodeExpression); } else { throwMessageEventTriggerBuilder = processBuilder.addEndEvent("endEvent").addMessageEventTrigger(messageName, targetProcessExpression); } addCorrelations(throwMessageEventTriggerBuilder, correlations); addMessageData(messageData, dataInputMapping, throwMessageEventTriggerBuilder); processBuilder.addTransition(START_EVENT_NAME, "auto1"); processBuilder.addTransition("auto1", "endEvent"); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final ProcessDefinition sendMessageProcess = deployAndEnableProcess(designProcessDefinition); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(sendMessageProcess.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); return sendMessageProcess; } public void addMessageData(final Map messageData, final Map dataInputMapping, final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder) throws InvalidExpressionException { if (messageData != null) { for (final Entry entry : messageData.entrySet()) { final Expression displayName = new ExpressionBuilder().createConstantStringExpression(entry.getKey()); Expression defaultValue = null; if (dataInputMapping.containsKey(entry.getKey())) { defaultValue = new ExpressionBuilder().createDataExpression(dataInputMapping.get(entry.getKey()), entry.getValue()); } throwMessageEventTriggerBuilder.addMessageContentExpression(displayName, defaultValue); } } } public void addProcessData(final Map data, final ProcessDefinitionBuilder processBuilder) { if (data != null) { for (final Entry entry : data.entrySet()) { processBuilder.addData(entry.getKey(), entry.getValue(), null); } } } public ProcessDefinition deployAndEnableProcessWithIntermediateThrowMessageEvent(final String targetProcess, final String targetFlowNode) throws Exception { return deployAndEnableProcessWithIntermediateThrowMessageEvent(Collections.singletonList(MESSAGE_NAME), Collections.singletonList(targetProcess), Collections.singletonList(targetFlowNode)); } public ProcessDefinition deployAndEnableProcessWithIntermediateThrowMessageEvent(final List messageNames, final List targetProcesses, final List targetFlowNodes) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder(); processBuilder.createNewInstance(SEND_MESSAGE_PROCESS_NAME, PROCESS_VERSION); processBuilder.addStartEvent(START_EVENT_NAME); processBuilder.addAutomaticTask("auto1"); final IntermediateThrowEventDefinitionBuilder intermediateThrowEvent = processBuilder .addIntermediateThrowEvent("sendMessage"); for (final String targetProcess : targetProcesses) { // create expression for target process/flowNode final Expression targetProcessExpression = new ExpressionBuilder() .createConstantStringExpression(targetProcess); final int indexOfTargetProcess = targetProcesses.indexOf(targetProcess); final Expression targetFlowNodeExpression = new ExpressionBuilder() .createConstantStringExpression(targetFlowNodes.get(indexOfTargetProcess)); intermediateThrowEvent.addMessageEventTrigger(messageNames.get(indexOfTargetProcess), targetProcessExpression, targetFlowNodeExpression); } processBuilder.addEndEvent("endEvent"); processBuilder.addTransition(START_EVENT_NAME, "auto1"); processBuilder.addTransition("auto1", "sendMessage"); processBuilder.addTransition("sendMessage", "endEvent"); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final ProcessDefinition sendMessageProcess = deployAndEnableProcess(designProcessDefinition); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(sendMessageProcess.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); return sendMessageProcess; } public ProcessDefinition deployAndEnableProcessWithStartMessageEvent(final Map data, final List catchMessageOperations) throws Exception { return deployAndEnableProcessWithStartMessageEvent(START_WITH_MESSAGE_PROCESS_NAME, MESSAGE_NAME, data, catchMessageOperations); } public ProcessDefinition deployAndEnableProcessWithStartMessageEvent(final String processName, final String messageName, final Map data, final List catchMessageOperations) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder(); processBuilder.createNewInstance(processName, PROCESS_VERSION); addProcessData(data, processBuilder); final CatchMessageEventTriggerDefinitionBuilder messageEventTrigger = processBuilder .addStartEvent(START_EVENT_NAME) .addMessageEventTrigger(messageName); addOperations(messageEventTrigger, catchMessageOperations); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask(START_WITH_MESSAGE_STEP1_NAME, ACTOR_NAME); processBuilder.addEndEvent("endEvent"); processBuilder.addTransition(START_EVENT_NAME, START_WITH_MESSAGE_STEP1_NAME); processBuilder.addTransition(START_WITH_MESSAGE_STEP1_NAME, "endEvent"); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder(); archiveBuilder.createNewBusinessArchive().setProcessDefinition(designProcessDefinition); final BusinessArchive receiveMessageArchive = archiveBuilder.done(); final ProcessDefinition receiveMessageProcess = deployProcess(receiveMessageArchive); final List actors = getProcessAPI().getActors(receiveMessageProcess.getId(), 0, 1, ActorCriterion.NAME_ASC); getProcessAPI().addUserToActor(actors.get(0).getId(), user.getId()); getProcessAPI().enableProcess(receiveMessageProcess.getId()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(receiveMessageProcess.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); return receiveMessageProcess; } public ProcessDefinition deployAndEnableProcessWithStartMessageEvent(final String processName, final String messageName) throws Exception { return deployAndEnableProcessWithStartMessageEvent(processName, messageName, Collections. emptyMap(), Collections. emptyList()); } public void addOperations(final CatchMessageEventTriggerDefinitionBuilder messageEventTrigger, final List catchMessageOperations) { if (catchMessageOperations != null) { for (final Operation operation : catchMessageOperations) { messageEventTrigger.addOperation(operation); } } } public ProcessDefinition deployAndEnableProcessWithIntermediateCatchMessageEvent( final List> correlations, final Map processData, final List operations) throws Exception { return deployAndEnableProcessWithIntermediateCatchMessageEvent(CATCH_MESSAGE_PROCESS_NAME, MESSAGE_NAME, correlations, processData, operations); } public ProcessDefinition deployAndEnableProcessWithIntermediateCatchMessageEvent(final String processName, final String messageName, final List> correlations, final Map processData, final List operations) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder(); processBuilder.createNewInstance(processName, PROCESS_VERSION); addProcessData(processData, processBuilder); processBuilder.addStartEvent(START_EVENT_NAME); processBuilder.addAutomaticTask("auto1"); final CatchMessageEventTriggerDefinitionBuilder catchMessageEventTriggerDefinitionBuilder = processBuilder .addIntermediateCatchEvent(CATCH_EVENT_NAME) .addMessageEventTrigger(messageName); addCorrelations(catchMessageEventTriggerDefinitionBuilder, correlations); addOperations(catchMessageEventTriggerDefinitionBuilder, operations); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask(CATCH_MESSAGE_STEP1_NAME, ACTOR_NAME); processBuilder.addEndEvent("endEvent"); processBuilder.addTransition(START_EVENT_NAME, "auto1"); processBuilder.addTransition("auto1", CATCH_EVENT_NAME); processBuilder.addTransition(CATCH_EVENT_NAME, CATCH_MESSAGE_STEP1_NAME); processBuilder.addTransition(CATCH_MESSAGE_STEP1_NAME, "endEvent"); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder(); archiveBuilder.createNewBusinessArchive().setProcessDefinition(designProcessDefinition); final BusinessArchive receiveMessaceArchive = archiveBuilder.done(); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithActor(receiveMessaceArchive, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(receiveMessageProcess.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); return receiveMessageProcess; } public ProcessDefinition deployAndEnableProcessWithIntraMessageEvent(final String targetProcess, final String targetFlowNode) throws Exception { return deployAndEnableProcessWithIntraMessageEvent("sendAndReceiveMessageProcess", MESSAGE_NAME, targetProcess, targetFlowNode); } public ProcessDefinition deployAndEnableProcessWithIntraMessageEvent(final String processName, final String messageName, final String targetProcess, final String targetFlowNode) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder(); // create expression for target process/flowNode final Expression targetProcessExpression = new ExpressionBuilder() .createConstantStringExpression(targetProcess); final Expression targetFlowNodeExpression = new ExpressionBuilder() .createConstantStringExpression(targetFlowNode); processBuilder.createNewInstance(processName, PROCESS_VERSION); processBuilder.addStartEvent(START_EVENT_NAME); processBuilder.addAutomaticTask("auto1"); processBuilder.addGateway("gateway1", GatewayType.PARALLEL); processBuilder.addIntermediateThrowEvent("sendMessage").addMessageEventTrigger(messageName, targetProcessExpression, targetFlowNodeExpression); processBuilder.addIntermediateCatchEvent(targetFlowNode).addMessageEventTrigger(messageName); processBuilder.addGateway("gateway2", GatewayType.PARALLEL); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask("userTask1", ACTOR_NAME).addUserTask("userTask2", ACTOR_NAME) .addUserTask("userTask3", ACTOR_NAME); processBuilder.addEndEvent("endEvent"); processBuilder.addTransition(START_EVENT_NAME, "auto1"); processBuilder.addTransition("auto1", "gateway1"); processBuilder.addTransition("gateway1", "userTask2"); processBuilder.addTransition("userTask2", "sendMessage"); processBuilder.addTransition("sendMessage", "gateway2"); processBuilder.addTransition("gateway1", "userTask1"); processBuilder.addTransition("userTask1", "gateway2"); processBuilder.addTransition("gateway2", "endEvent"); processBuilder.addTransition("auto1", targetFlowNode); processBuilder.addTransition(targetFlowNode, "userTask3"); processBuilder.addTransition("userTask3", "endEvent"); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder(); archiveBuilder.createNewBusinessArchive().setProcessDefinition(designProcessDefinition); final BusinessArchive sendAndReceiveMessaceArchive = archiveBuilder.done(); final ProcessDefinition receiveMessageProcess = getProcessAPI().deploy(sendAndReceiveMessaceArchive); final List actors = getProcessAPI().getActors(receiveMessageProcess.getId(), 0, 1, ActorCriterion.NAME_ASC); getProcessAPI().addUserToActor(actors.get(0).getId(), user.getId()); getProcessAPI().enableProcess(receiveMessageProcess.getId()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(receiveMessageProcess.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); return receiveMessageProcess; } public ProcessDefinition deployAndEnableProcessWithIntermediateCatchMessageEventAnd1Correlation() throws Exception { final Map data = new HashMap<>(); data.put("docRef", Integer.class.getName()); final ArrayList> correlations = new ArrayList>(1); final Expression docCorrelationKey = new ExpressionBuilder().createConstantStringExpression("docKey"); final Expression docCorrelationValue = new ExpressionBuilder().createDataExpression("docRef", Integer.class.getName()); correlations.add(Map.entry(docCorrelationKey, docCorrelationValue)); return deployAndEnableProcessWithIntermediateCatchMessageEvent(correlations, data, null); } public ProcessDefinition deployAndEnableProcessWithIntermediateCatchMessageEventAnd2Correlations() throws Exception { final Map data = new HashMap<>(); data.put("docRef", Integer.class.getName()); data.put("name", String.class.getName()); final Expression docCorrelationKey = new ExpressionBuilder().createConstantStringExpression("docKey"); final Expression docCorrelationValue = new ExpressionBuilder().createDataExpression("docRef", Integer.class.getName()); final Expression nameCorrelationKey = new ExpressionBuilder().createConstantStringExpression("nameKey"); final Expression nameCorrelationValue = new ExpressionBuilder().createDataExpression("name", String.class.getName()); final ArrayList> correlations = new ArrayList>(2); correlations.add(Map.entry(docCorrelationKey, docCorrelationValue)); correlations.add(Map.entry(nameCorrelationKey, nameCorrelationValue)); return deployAndEnableProcessWithIntermediateCatchMessageEvent(correlations, data, null); } public ProcessDefinition deployAndEnableProcessWithEndMessageEventAndCorrelation() throws Exception { final Map data = new HashMap<>(); data.put("docNumber", Integer.class.getName()); data.put("lastName", String.class.getName()); final ArrayList> correlations = new ArrayList>(2); final Expression docCorrelationKey = new ExpressionBuilder().createConstantStringExpression("docKey"); final Expression docCorrelationValue = new ExpressionBuilder().createDataExpression("docNumber", Integer.class.getName()); final Expression nameCorrelationKey = new ExpressionBuilder().createConstantStringExpression("nameKey"); final Expression nameCorrelationValue = new ExpressionBuilder().createDataExpression("lastName", String.class.getName()); correlations.add(Map.entry(docCorrelationKey, docCorrelationValue)); correlations.add(Map.entry(nameCorrelationKey, nameCorrelationValue)); return deployAndEnableProcessWithEndMessageEvent(CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, correlations, data, null, null); } public ProcessDefinition deployAndEnableProcessWithBoundarySignalEventOnLoopActivity(final int loopMax) throws Exception { final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithMultiInstanceAndBoundaryEvent", "1.0"); processBuilder.addActor(ACTOR_NAME).addStartEvent("start"); final UserTaskDefinitionBuilder userTaskBuilder = processBuilder.addUserTask("step1", ACTOR_NAME); userTaskBuilder.addLoop(false, condition, new ExpressionBuilder().createConstantIntegerExpression(loopMax)); userTaskBuilder.addBoundaryEvent("signal", true).addSignalEventTrigger("MySignal"); processBuilder.addUserTask("step2", ACTOR_NAME).addUserTask(EXCEPTION_STEP, ACTOR_NAME).addEndEvent("end") .addTransition("start", "step1") .addTransition("step1", "step2").addTransition("step2", "end").addTransition("signal", EXCEPTION_STEP); return deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithBoundarySignalEventOnCallActivity(final String signalName) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("pSignalBoundary", "2.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("start"); final CallActivityBuilder callActivityBuilder = processDefinitionBuilder.addCallActivity("step1", new ExpressionBuilder().createConstantStringExpression("calledProcess"), new ExpressionBuilder().createConstantStringExpression("1.0")); callActivityBuilder.addBoundaryEvent("signal", true).addSignalEventTrigger(signalName); processDefinitionBuilder.addUserTask(EXCEPTION_STEP, ACTOR_NAME); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addEndEvent("end"); processDefinitionBuilder.addTransition("start", "step1"); processDefinitionBuilder.addTransition("step1", "step2"); processDefinitionBuilder.addTransition("step2", "end"); processDefinitionBuilder.addTransition("signal", EXCEPTION_STEP); return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithBoundarySignalEventOnMultiInstance(final int loopCardinality, final boolean isSequential) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance( "processWithBoundarySignalEventAndMultiInstance", "1.0"); processBuilder.addActor(ACTOR_NAME).addStartEvent("start"); final UserTaskDefinitionBuilder userTaskBuilder = processBuilder.addUserTask("step1", ACTOR_NAME); userTaskBuilder.addMultiInstance(isSequential, new ExpressionBuilder().createConstantIntegerExpression(loopCardinality)); userTaskBuilder.addBoundaryEvent("signal", true).addSignalEventTrigger("MySignal"); processBuilder.addUserTask("step2", ACTOR_NAME).addUserTask(EXCEPTION_STEP, ACTOR_NAME).addEndEvent("end") .addTransition("start", "step1") .addTransition("step1", "step2").addTransition("step2", "end").addTransition("signal", EXCEPTION_STEP); return deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithSignalEventSubProcess(final boolean withIntermediateThrowEvent, final boolean withData) throws Exception { final ProcessDefinitionBuilder builder = buildParentProcessDefinition(withIntermediateThrowEvent, withData); buildSubProcessDefinition(builder, withData, null, true); final DesignProcessDefinition processDefinition = builder.done(); return deployAndEnableProcessWithActor(processDefinition, ACTOR_NAME, user); } public ProcessDefinition deployAndEnableProcessWithIntermediateCatchTimerEventAndUserTask(final TimerType timerType, final Expression timerValue, final String step1Name, final String step2Name) throws Exception { final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My Process with start event", "1.0") .addActor(ACTOR_NAME).addDescription("Delivery all day and night long").addStartEvent(START_EVENT_NAME) .addUserTask(step1Name, ACTOR_NAME) .addIntermediateCatchEvent("intermediateCatchEvent") .addTimerEventTriggerDefinition(timerType, timerValue).addUserTask(step2Name, ACTOR_NAME) .addEndEvent("endEvent").addTransition(START_EVENT_NAME, step1Name) .addTransition(step1Name, "intermediateCatchEvent") .addTransition("intermediateCatchEvent", step2Name).addTransition(step2Name, "endEvent").getProcess(); final ProcessDefinition definition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(definition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); return definition; } public ProcessDefinition deployAndEnableProcessWithStartTimerEventAndUserTask(final TimerType timerType, final Expression timerValue, final String stepName) throws Exception { final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My Process with start event", "1.0") .addActor(ACTOR_NAME).addDescription("Delivery all day and night long").addStartEvent(START_EVENT_NAME) .addTimerEventTriggerDefinition(timerType, timerValue).addUserTask(stepName, ACTOR_NAME) .addEndEvent("endEvent") .addTransition(START_EVENT_NAME, stepName).addTransition(stepName, "endEvent").getProcess(); final ProcessDefinition definition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(definition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); return definition; } public ProcessDefinition deployAndEnableProcessSendingMessageUsingVariableAsTarget(final String targetProcessName, final String targetFlowNode, final String messageName) throws Exception { Expression targetProcessExpr = new ExpressionBuilder().createConstantStringExpression(targetProcessName); Expression targetFlowNodeExpr = new ExpressionBuilder().createConstantStringExpression(targetFlowNode); Expression targetProcessVarExpr = new ExpressionBuilder().createDataExpression("targetProcess", String.class.getName()); Expression targetFlowNodeVarExpr = new ExpressionBuilder().createDataExpression("targetFlowNode", String.class.getName()); ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("sendMsgProcess", "5.1"); builder.addShortTextData("targetProcess", targetProcessExpr); builder.addShortTextData("targetFlowNode", targetFlowNodeExpr); builder.addStartEvent("start"); builder.addIntermediateThrowEvent("sendMsg").addMessageEventTrigger(messageName, targetProcessVarExpr, targetFlowNodeVarExpr); builder.addEndEvent("end"); builder.addTransition("start", "sendMsg"); builder.addTransition("sendMsg", "end"); return deployAndEnableProcess(builder.done()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/filter/user/GroupUserFilter.java ================================================ /** * 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.filter.user; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.filter.AbstractUserFilter; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserCriterion; public class GroupUserFilter extends AbstractUserFilter { @Override public void validateInputParameters() { // Nothing to validate } @Override public List filter(final String actorName) { final Long groupId = (Long) getInputParameter("groupId"); final List users = getAPIAccessor().getIdentityAPI().getUsersInGroup(groupId, 0, 10, UserCriterion.USER_NAME_DESC); final List userIds = new ArrayList(); for (final User user : users) { userIds.add(user.getId()); } return userIds; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/filter/user/TestFilter.java ================================================ /** * 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.filter.user; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.filter.AbstractUserFilter; /** * @author Baptiste Mesta */ public class TestFilter extends AbstractUserFilter { @Override public List filter(final String actorName) { return Collections.singletonList((Long) getInputParameter("userId")); } @Override public void validateInputParameters() { } @Override public boolean shouldAutoAssignTaskIfSingleResult() { return false; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/filter/user/TestFilterThatThrowException.java ================================================ /** * 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.filter.user; import java.util.List; import org.bonitasoft.engine.filter.AbstractUserFilter; import org.bonitasoft.engine.filter.UserFilterException; /** * @author Baptiste Mesta */ public class TestFilterThatThrowException extends AbstractUserFilter { public TestFilterThatThrowException() { } @Override public List filter(final String actorName) throws UserFilterException { if (getInputParameter("exception").equals("runtime")) { throw new RuntimeException("unexpected"); } throw new UserFilterException("unexpected"); } @Override public void validateInputParameters() { } @Override public boolean shouldAutoAssignTaskIfSingleResult() { return false; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/filter/user/TestFilterThatThrowNoClassDef.java ================================================ /** * 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.filter.user; import java.util.List; import org.bonitasoft.engine.filter.AbstractUserFilter; import org.bonitasoft.engine.filter.UserFilterException; /** * @author Baptiste Mesta */ public class TestFilterThatThrowNoClassDef extends AbstractUserFilter { public TestFilterThatThrowNoClassDef() { throw new NoClassDefFoundError(); } @Override public List filter(final String actorName) throws UserFilterException { // no need for real implementation as the constructor throws Exception: return null; } @Override public void validateInputParameters() { } @Override public boolean shouldAutoAssignTaskIfSingleResult() { return false; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/filter/user/TestFilterUsingActorName.java ================================================ /** * 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.filter.user; import java.util.Arrays; import java.util.List; import java.util.Map; import org.bonitasoft.engine.filter.AbstractUserFilter; /** * @author Baptiste Mesta */ public class TestFilterUsingActorName extends AbstractUserFilter { @Override public List filter(final String actorName) { @SuppressWarnings("unchecked") final Map map = (Map) getInputParameter("userIds"); return Arrays.asList(map.get(actorName)); } @Override public void validateInputParameters() { } @Override public boolean shouldAutoAssignTaskIfSingleResult() { return false; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/filter/user/TestFilterWithAutoAssign.java ================================================ /** * 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.filter.user; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.filter.AbstractUserFilter; /** * @author Baptiste Mesta */ public class TestFilterWithAutoAssign extends AbstractUserFilter { @Override public List filter(final String actorName) { return Collections.singletonList((Long) getInputParameter("userId")); } @Override public void validateInputParameters() { } @Override public boolean shouldAutoAssignTaskIfSingleResult() { return true; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/page/PageAssert.java ================================================ /** * 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.page; import static java.lang.String.format; import java.util.Date; import org.assertj.core.api.AbstractAssert; /** * {@link Page} specific assertions - Generated by CustomAssertionGenerator. */ public class PageAssert extends AbstractAssert { /** * Creates a new {@link PageAssert} to make assertions on actual Page. * * @param actual the Page we want to make assertions on. */ public PageAssert(Page actual) { super(actual, PageAssert.class); } /** * An entry point for PageAssert to follow AssertJ standard assertThat() statements.
* With a static import, one's can write directly : assertThat(myPage) and get specific assertion with * code completion. * * @param actual the Page we want to make assertions on. * @return a new {@link PageAssert} */ public static PageAssert assertThat(Page actual) { return new PageAssert(actual); } /** * Verifies that the actual Page's contentName is equal to the given one. * * @param contentName the given contentName to compare the actual Page's contentName to. * @return this assertion object. * @throws AssertionError - if the actual Page's contentName is not equal to the given one. */ public PageAssert hasContentName(String contentName) { // check that actual Page we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> contentName to be:\n <%s>\n but was:\n <%s>", actual, contentName, actual.getContentName()); // check if (!actual.getContentName().equals(contentName)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual Page's contentType is equal to the given one. * * @param contentType the given contentType to compare the actual Page's contentType to. * @return this assertion object. * @throws AssertionError - if the actual Page's contentType is not equal to the given one. */ public PageAssert hasContentType(String contentType) { // check that actual Page we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> contentType to be:\n <%s>\n but was:\n <%s>", actual, contentType, actual.getContentType()); // check if (!actual.getContentType().equals(contentType)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual Page's description is equal to the given one. * * @param description the given description to compare the actual Page's description to. * @return this assertion object. * @throws AssertionError - if the actual Page's description is not equal to the given one. */ public PageAssert hasDescription(String description) { // check that actual Page we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> description to be:\n <%s>\n but was:\n <%s>", actual, description, actual.getDescription()); // check if (!actual.getDescription().equals(description)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual Page's displayName is equal to the given one. * * @param displayName the given displayName to compare the actual Page's displayName to. * @return this assertion object. * @throws AssertionError - if the actual Page's displayName is not equal to the given one. */ public PageAssert hasDisplayName(String displayName) { // check that actual Page we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> displayName to be:\n <%s>\n but was:\n <%s>", actual, displayName, actual.getDisplayName()); // check if (!actual.getDisplayName().equals(displayName)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual Page's installationDate is equal to the given one. * * @param installationDate the given installationDate to compare the actual Page's installationDate to. * @return this assertion object. * @throws AssertionError - if the actual Page's installationDate is not equal to the given one. */ public PageAssert hasInstallationDate(Date installationDate) { // check that actual Page we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> installationDate to be:\n <%s>\n but was:\n <%s>", actual, installationDate, actual.getInstallationDate()); // check if (!actual.getInstallationDate().equals(installationDate)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual Page's installedBy is equal to the given one. * * @param installedBy the given installedBy to compare the actual Page's installedBy to. * @return this assertion object. * @throws AssertionError - if the actual Page's installedBy is not equal to the given one. */ public PageAssert hasInstalledBy(long installedBy) { // check that actual Page we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> installedBy to be:\n <%s>\n but was:\n <%s>", actual, installedBy, actual.getInstalledBy()); // check if (actual.getInstalledBy() != installedBy) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual Page's lastUpdatedBy is equal to the given one. * * @param lastUpdatedBy the given lastUpdatedBy to compare the actual Page's lastUpdatedBy to. * @return this assertion object. * @throws AssertionError - if the actual Page's lastUpdatedBy is not equal to the given one. */ public PageAssert hasLastUpdatedBy(long lastUpdatedBy) { // check that actual Page we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> lastUpdatedBy to be:\n <%s>\n but was:\n <%s>", actual, lastUpdatedBy, actual.getLastUpdatedBy()); // check if (actual.getLastUpdatedBy() != lastUpdatedBy) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual Page's name is equal to the given one. * * @param name the given name to compare the actual Page's name to. * @return this assertion object. * @throws AssertionError - if the actual Page's name is not equal to the given one. */ public PageAssert hasName(String name) { // check that actual Page we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> name to be:\n <%s>\n but was:\n <%s>", actual, name, actual.getName()); // check if (!actual.getName().equals(name)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual Page's processDefinitionId is equal to the given one. * * @param processDefinitionId the given processDefinitionId to compare the actual Page's processDefinitionId to. * @return this assertion object. * @throws AssertionError - if the actual Page's processDefinitionId is not equal to the given one. */ public PageAssert hasProcessDefinitionId(Long processDefinitionId) { // check that actual Page we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> processDefinitionId to be:\n <%s>\n but was:\n <%s>", actual, processDefinitionId, actual.getProcessDefinitionId()); // check if (!actual.getProcessDefinitionId().equals(processDefinitionId)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/process/Employee.java ================================================ /** * 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.process; import java.io.Serializable; public class Employee implements Comparable, Serializable { private static final long serialVersionUID = 1L; private final String name; private final String firstName; private final int experience; public Employee(final String name, final String firstName) { this.name = name; this.firstName = firstName; experience = 0; } public Employee(final String name, final String firstName, final int experience) { this.name = name; this.firstName = firstName; this.experience = experience; } public String getName() { return name; } public String getFirstName() { return firstName; } public int getExperience() { return experience; } @Override public int compareTo(final Object obj) { final Employee empl = (Employee) obj; if (experience != empl.experience) { return experience - empl.experience; } if (!name.equals(empl.name)) { return name.compareTo(empl.name); } return firstName.compareTo(empl.firstName); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + experience; result = prime * result + (firstName == null ? 0 : firstName.hashCode()); result = prime * result + (name == null ? 0 : name.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof Employee other)) { return false; } if (experience != other.experience) { return false; } if (firstName == null) { if (other.firstName != null) { return false; } } else if (!firstName.equals(other.firstName)) { return false; } if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/process/Secretary.java ================================================ /** * 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.process; public class Secretary extends Employee { private static final long serialVersionUID = 1L; public Secretary(String name, String firstName) { super(name, firstName); } public Secretary(String name, String firstName, int experience) { super(name, firstName, experience); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/process/SetDueDateConnector.java ================================================ /** * 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.process; import java.util.Date; import org.bonitasoft.engine.connector.AbstractConnector; import org.bonitasoft.engine.connector.ConnectorException; import org.bonitasoft.engine.connector.ConnectorValidationException; import org.bonitasoft.engine.exception.UpdateException; public class SetDueDateConnector extends AbstractConnector { static Date dueDate = new Date(1000000); @Override protected void executeBusinessLogic() throws ConnectorException { long activityInstanceId = getExecutionContext().getActivityInstanceId(); try { getAPIAccessor().getProcessAPI().updateDueDateOfTask(activityInstanceId, dueDate); } catch (UpdateException e) { throw new ConnectorException(e); } } @Override public void validateInputParameters() throws ConnectorValidationException { } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/search/SlowConnector.java ================================================ /** * 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.search; import java.util.concurrent.TimeUnit; import org.bonitasoft.engine.connector.AbstractConnector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SlowConnector extends AbstractConnector { private static final Logger LOG = LoggerFactory.getLogger(SlowConnector.class); @Override public void validateInputParameters() { // do nothing } @Override protected void executeBusinessLogic() { try { final long sleepingTime = 15; LOG.info("SlowConnector instance is going to sleep for {} seconds", sleepingTime); TimeUnit.SECONDS.sleep(sleepingTime); LOG.info("SlowConnector instance sleeping done"); } catch (InterruptedException e) { LOG.error("Interruption while sleeping", e); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/util/AssertionsUtils.java ================================================ /** * Copyright (C) 2020 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.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AssertionsUtils { private static final Logger logger = LoggerFactory.getLogger(AssertionsUtils.class); @FunctionalInterface public interface Assertion { void assertThat() throws Exception; } @FunctionalInterface public interface ExceptionHandler { void handle(Exception e) throws Exception; } public static void assertNoErrorAfterXAttemps(int attempts, Assertion assertThat, ExceptionHandler onError) throws Exception { for (int attempt = 1; attempt <= attempts; attempt++) { logger.info("Attempt {} on {}", attempt, attempts); try { assertThat.assertThat(); } catch (Exception e) { logger.error("Attempt {} on {} failed", attempt, attempts); onError.handle(e); throw e; } logger.info("Completed attempt {} on {}", attempt, attempts); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/java/org/bonitasoft/engine/util/FunctionalMatcher.java ================================================ /** * 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.util; import org.hamcrest.Description; import org.hamcrest.Matcher; public interface FunctionalMatcher extends Matcher { boolean isMatchting(T item); @Override default boolean matches(Object item) { return isMatchting(((T) item)); } @Override default void describeMismatch(Object item, Description mismatchDescription) { mismatchDescription.appendText("was ").appendValue(item); } @Override default void _dont_implement_Matcher___instead_extend_BaseMatcher_() { } @Override default void describeTo(Description description) { } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/application/applicationWithUnavailableInfo.xml ================================================ My HR dashboard This is the HR dashboard. /icon.jpg HR follow-up Wrong menu 1 Daily HR follow-up Wrong menu 2 Empty menu ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/application/applications.xml ================================================ My HR dashboard This is the HR dashboard. /icon.jpg HR follow-up Daily HR follow-up Empty menu Marketing ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/data/BusinessDataUpdateConnector.def ================================================ 1.0 org.bonitasoft.connector.BusinessDataUpdateConnector xxx.png other org/bonitasoft/engine/bdr/BusinessDataUpdateConnector.png bizData output1 ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/data/BusinessDataUpdateConnector.impl ================================================ org.bonitasoft.connector.BusinessDataUpdateConnector 1.0 org.bonitasoft.engine.business.data.BusinessDataUpdateConnector org.bonitasoft.connector.BusinessDataUpdateConnector 1.0 BusinessDataUpdateConnector.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/data/findByFirstNameAndLastNameNewOrder.json ================================================ [ { "persistenceId": 1, "persistenceVersion": 0, "persistenceId_string": "1", "persistenceVersion_string": "0", "firstName": "Alphonse", "lastName": "Dupond", "hireDate": "123456789", "birthDate": null, "phoneNumbers": [ "123456789" ], "addresses": [ { "persistenceId": 2, "persistenceVersion": 0, "persistenceId_string": "2", "persistenceVersion_string": "0", "street": "32, rue Gustave Eiffel", "city": "Grenoble", "links": [ { "rel": "country", "href": "/businessdata/com.company.model.Address/2/country" } ] } ], "booleanField": null, "dog": null, "links": [ { "rel": "address", "href": "/businessdata/com.company.model.Employee/1/address" }, { "rel": "Cat", "href": "/businessdata/com.company.model.Employee/1/Cat" } ] } ] ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/data/findByFirstNameFetchAddresses.json ================================================ [ { "persistenceId": 1, "persistenceVersion": 0, "persistenceId_string": "1", "persistenceVersion_string": "0", "firstName": "Alphonse", "lastName": "Dupond", "hireDate": "123456789", "booleanField": true, "birthDate":null, "phoneNumbers": [ "123456789" ], "addresses": [ { "persistenceId": 2, "persistenceVersion": 0, "persistenceId_string": "2", "persistenceVersion_string": "0", "street": "32, rue Gustave Eiffel", "city": "Grenoble", "links": [ { "rel": "country", "href": "/businessdata/org.bonita.pojo.Address/2/country" } ] } ], "dog": null, "links": [ { "rel": "address", "href": "/businessdata/org.bonita.pojo.Employee/1/address" }, { "rel": "cat", "href": "/businessdata/com.company.model.Employee/1/cat" } ] } ] ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/data/getBusinessDataByIdAddress.json ================================================ { "persistenceId": 2, "persistenceVersion": 0, "persistenceId_string": "2", "persistenceVersion_string": "0", "street": "32, rue Gustave Eiffel", "city": "Grenoble", "links": [ { "rel": "country", "href": "/businessdata/org.bonita.pojo.Address/2/country" } ] } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/data/getBusinessDataByIdEmployee.json ================================================ { "persistenceId": 1, "persistenceVersion": 0, "persistenceId_string": "1", "persistenceVersion_string": "0", "firstName": "Alphonse", "lastName": "Dupond", "hireDate": "123456789", "booleanField": true, "birthDate":null, "phoneNumbers": [ "123456789" ], "addresses": [ { "persistenceId": 2, "persistenceVersion": 0, "persistenceId_string": "2", "persistenceVersion_string": "0", "street": "32, rue Gustave Eiffel", "city": "Grenoble", "links": [ { "rel": "country", "href": "/businessdata/org.bonita.pojo.Address/2/country" } ] } ], "dog": null, "links": [ { "rel": "address", "href": "/businessdata/org.bonita.pojo.Employee/1/address" }, { "rel": "cat", "href": "/businessdata/com.company.model.Employee/1/cat" } ] } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/business/data/getEmployeeByPhoneNumber.json ================================================ [ { "persistenceId": 1, "persistenceVersion": 0, "persistenceId_string": "1", "persistenceVersion_string": "0", "firstName": "Alphonse", "lastName": "Dupond", "hireDate": "123456789", "booleanField": true, "birthDate":null, "phoneNumbers": [ "123456789" ], "addresses": [ { "persistenceId": 2, "persistenceVersion": 0, "persistenceId_string": "2", "persistenceVersion_string": "0", "street": "32, rue Gustave Eiffel", "city": "Grenoble", "links": [ { "rel": "country", "href": "/businessdata/com.company.model.Address/2/country" } ] } ], "dog": null, "links": [ { "rel": "address", "href": "/businessdata/org.bonita.pojo.Employee/1/address" }, { "rel": "cat", "href": "/businessdata/com.company.model.Employee/1/cat" } ] } ] ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/command/Employee.find.2.1.json ================================================ [ { "persistenceId": "${json-unit.ignore}", "persistenceId_string": "${json-unit.ignore}", "persistenceVersion": 0, "persistenceVersion_string": "0", "firstName": "Matthieu", "lastName": "Chaffotte", "birthdate": null, "links": [ { "rel": "addresses", "href": "${json-unit.ignore}" } ] } ] ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/command/web/profile/AllProfiles.xml ================================================ Administrator profile userName1 userName2 Team Manager profile userName3 userName4 userName5 Process owner profile User profile ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/command/web/profile/RestoreDefaultProfiles.xml ================================================ Administrator profile userName1 userName2 User profile ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/command/web/profile/deleteExistingProfile.xml ================================================ TM profile userName1 userName2 ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/APIAccessorConnector.impl ================================================ org.bonitasoft.connector.APIAccessorConnector 1.0 org.bonitasoft.engine.connectors.APIAccessorConnector APIAccessorConnector 1.0 APIAccessorConnector.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnector.def ================================================ 1.0 org.bonitasoft.connector.testConnector xxx.png other org/bonitasoft/engine/connectors/TestConnector.png input1 ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnector.impl ================================================ org.bonitasoft.connector.testConnector 1.0 org.bonitasoft.engine.connectors.TestConnector org.bonitasoft.connector.testConnector 1.0 TestConnector.jar VariableStorage.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnector2.impl ================================================ org.bonitasoft.connector.testConnector 1.0 org.bonitasoft.engine.connectors.TestConnector2 org.bonitasoft.connector.testConnector2 1.0 TestConnector2.jar VariableStorage.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnector3.def ================================================ 1.0 org.bonitasoft.connector.testConnector3 xxx.png other org/bonitasoft/engine/connectors/TestConnector3.png input1 input2 input3 input4 ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnector3.impl ================================================ org.bonitasoft.connector.testConnector3 1.0 org.bonitasoft.engine.connectors.TestConnector3 org.bonitasoft.connector.testConnector3 1.0 TestConnector3.jar VariableStorage.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorInJar.impl ================================================ connectorInJar 1.0.0 ConnectorInJar connectorInJarImpl 1.0.0 ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorLongToExecute.impl ================================================ testConnectorLongToExecute 1.0.0 org.bonitasoft.engine.connectors.TestConnectorLongToExecute testConnectorLongToExecuteImpl 1.0.0 ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorThatThrowException.impl ================================================ testConnectorThatThrowException 1.0 org.bonitasoft.engine.connectors.TestConnectorThatThrowException testConnectorThatThrowExceptionImpl 1.0 TestConnectorThatThrowException.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithAPICall.impl ================================================ org.bonitasoft.engine.connectors.TestConnectorWithAPICall 1.0 org.bonitasoft.engine.connectors.TestConnectorWithAPICall org.bonitasoft.engine.connectors.TestConnectorWithAPICall 1.0 TestConnectorWithAPICall.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithConnectedResource.def ================================================ 1.0 org.bonitasoft.connector.testConnectorWithConnectedResource xxx.png other org/bonitasoft/engine/connectors/TestConnectorWithConnectedResource.png output ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithConnectedResource.impl ================================================ org.bonitasoft.connector.testConnectorWithConnectedResource 1.0 org.bonitasoft.engine.connectors.TestConnectorWithConnectedResource org.bonitasoft.connector.testConnectorWithConnectedResource 1.0 TestConnectorWithConnectedResource.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithCustomType.impl ================================================ connectorWithCustomType 1.0.0 org.connector.custom.ConnectorWithCustomType connectorWithCustomTypeImpl 1.0.0 ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithModifiedOutput.impl ================================================ org.bonitasoft.connector.testConnectorWithOutput 1.0 org.bonitasoft.engine.connectors.TestConnectorWithModifiedOutput org.bonitasoft.connector.testConnectorWithModifiedOutput 1.0 TestConnectorWithModifiedOutput.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithNotSerializableOutput.def ================================================ 1.0 org.bonitasoft.connector.testConnectorWithNotSerializableOutput xxx.png other org/bonitasoft/engine/connectors/TestConnectorWithNotSerializableOutput.png ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithNotSerializableOutput.impl ================================================ org.bonitasoft.connector.testConnectorWithNotSerializableOutput 1.0 org.bonitasoft.engine.connectors.TestConnectorWithNotSerializableOutput org.bonitasoft.connector.testConnectorWithNotSerializableOutput 1.0 TestConnectorWithNotSerializableOutput.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithOutput.def ================================================ 1.0 org.bonitasoft.connector.testConnectorWithOutput xxx.png other org/bonitasoft/engine/connectors/TestConnectorWithOutput.png input1 output1 ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorWithOutput.impl ================================================ org.bonitasoft.connector.testConnectorWithOutput 1.0 org.bonitasoft.engine.connectors.TestConnectorWithOutput org.bonitasoft.connector.testConnectorWithOutput 1.0 TestConnectorWithOutput.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/TestExternalConnector.impl ================================================ org.bonitasoft.connector.testExternalConnector 1.0 org.bonitasoft.engine.connectors.TestExternalConnector org.bonitasoft.connector.testExternalConnector 1.0 TestExternalConnector.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/connectors/UnknownClassConnector.impl ================================================ unkownClassConnectorDef 1.0.0 org.unknown.MyUnknownClass unkownClassConnectorId 1.0.0 ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/GroupUserFilter.impl ================================================ org.bonitasoft.engine.filter.user.GroupUserFilter 1.0 org.bonitasoft.engine.filter.user.GroupUserFilter org.bonitasoft.engine.filter.user.GroupUserFilter 1.0 GroupUserFilter.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/TestFilter.def ================================================ 1.0 org.bonitasoft.engine.filter.user.testFilter xxx.png other org/bonitasoft/engine/filter/user/TestFilter.png userId ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/TestFilter.impl ================================================ org.bonitasoft.engine.filter.user.testFilter 1.0 org.bonitasoft.engine.filter.user.TestFilter org.bonitasoft.engine.filter.user.testFilter 1.0 TestFilter.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/TestFilterThatThrowException.impl ================================================ ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/TestFilterThatThrowNoClassDef.impl ================================================ ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/TestFilterUsingActorName.impl ================================================ org.bonitasoft.engine.filter.user.testFilterUsingActorName 1.0 org.bonitasoft.engine.filter.user.TestFilterUsingActorName org.bonitasoft.engine.filter.user.testFilterUsingActorName 1.0 TestFilterUsingActorName.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/TestFilterWithAutoAssign.impl ================================================ org.bonitasoft.engine.filter.user.testFilterWithAutoAssign 1.0 org.bonitasoft.engine.filter.user.TestFilterWithAutoAssign org.bonitasoft.engine.filter.user.testFilterWithAutoAssign 1.0 TestFilterWithAutoAssign.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/filter/user/TestFilterWithClassNotFound.impl ================================================ ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/ACME.xml ================================================ Office location Skills The user skills bpm William Jobs Mr Chief Executive Officer william.jobs@acme.com 484-302-5985 484-302-0985 70
Renwick Drive
19108 Philadelphia PA United States
Office location Engineering Skills Java
bpm April Sanchez Mrs Compensation specialist helen.kelly april.sanchez@acme.com 484-302-5561 484-302-0561 70
Renwick Drive
19108 Philadelphia PA United States
bpm Helen Kelly Mrs Human resource manager william.jobs helen.kelly@acme.com 484-302-5561 484-302-0561 70
Renwick Drive
19108 Philadelphia PA United States
bpm Walter Bates Mr Human resources benefits helen.kelly walter.bates@acme.com 484-302-5561 484-302-0561 70
Renwick Drive
19108 Philadelphia PA United States
bpm Zachary Williamson Mr Chief Financial Officer william.jobs zachary.williamson@acme.com 484-302-5921 484-302-0921 70
Renwick Drive
19108 Philadelphia PA United States
bpm Patrick Gardenier Mr Financial controller zachary.williamson patrick.gardenier@acme.com 484-302-5921 484-302-0921 70
Renwick Drive
19108 Philadelphia PA United States
bpm Virgine Jomphe Mrs Accountant zachary.williamson virginie.jomphe@acme.com 484-302-5921 484-302-0921 70
Renwick Drive
19108 Philadelphia PA United States
bpm Thorsten Hartmann Mr Financial planning manager zachary.williamson thorsten.hartmann@acme.com 484-302-5921 484-302-0921 70
Renwick Drive
19108 Philadelphia PA United States
bpm Jan Fisher Mr Infrastucture specialist favio.riviera jan.fisher@acme.com 484-302-5921 484-302-0921 70
Renwick Drive
19108 Philadelphia PA United States
bpm Isabel Bleasdale Mrs Product marketing manager favio.riviera isabel.bleasdale@acme.com 484-302-5921 484-302-0921 70
Renwick Drive
19108 Philadelphia PA United States
bpm Favio Rivera Mr Vice President of Marketing william.jobs favio.riviera@acme.com 484-302-5921 484-302-0921 70
Renwick Drive
19108 Philadelphia PA United States
bpm Michael Morrison Mr Chief Technical Officer william.jobs michael.morrison@acme.com 484-302-5921 484-302-0921 70
Renwick Drive
19108 Philadelphia PA United States
bpm Marc Marseau Mr Engineer michael.morrison marc.marseau@acme.com 484-302-5921 484-302-0921 70
Renwick Drive
19108 Philadelphia PA United States
bpm Joseph Hovell Mr Engineer michael.morrison joseph.hovell@acme.com 484-302-5921 484-302-0921 70
Renwick Drive
19108 Philadelphia PA United States
bpm Mauro Zetticci Mr Consultant michael.morrison mauro.zetticci@acme.com 484-302-5793 484-302-0793 70
Renwick Drive
19108 Philadelphia PA United States
bpm Thomas Wallis Mr Consultant michael.morrison thomas.wallis@acme.com 484-302-5793 484-302-0793 70
Renwick Drive
19108 Philadelphia PA United States
bpm Daniela Angelo Mrs Vice President of Sales william.jobs daniela.angelo@acme.com 484-302-5793 484-302-0793 70
Renwick Drive
19108 Philadelphia PA United States
bpm Anthony Nichols Mr Account manager daniela.angelo anthony.nichols@acme.com 484-302-5793 484-302-0793 70
Renwick Drive
19108 Philadelphia PA United States
bpm Misa Kumagai Mrs Account manager daniela.angelo misa.kumagai@acme.com 484-302-5793 484-302-0793 70
Renwick Drive
19108 Philadelphia PA United States
bpm Norio Yamazaki Mr Account manager daniela.angelo norio.yamazaki@acme.com 484-302-5793 484-302-0793 70
Renwick Drive
19108 Philadelphia PA United States
bpm Giovanna Almeida Mrs Account manager daniela.angelo giovanna.almeida@acme.com 484-302-5793 484-302-0793 70
Renwick Drive
19108 Philadelphia PA United States
Member Headquarters Human Resources Finance Infrastructure Marketing Production Sales Europe Asia Latin America North America Research & Development Services william.jobs member headquarters april.sanchez member hr /headquarters helen.kelly member hr /headquarters walter.bates member hr /headquarters zachary.williamson member finance /headquarters patrick.gardenier member finance /headquarters virginie.jomphe member finance /headquarters thorsten.hartmann member finance /headquarters jan.fisher member it /headquarters isabel.bleasdale member marketing /headquarters favio.riviera member marketing /headquarters michael.morrison member production /headquarters marc.marseau member rd /headquarters/production joseph.hovell member rd /headquarters/production mauro.zetticci member services /headquarters/production thomas.wallis member services /headquarters/production daniela.angelo member europe /headquarters/sales misa.kumagai member asia /headquarters/sales norio.yamazaki member asia /headquarters/sales giovanna.almeida member latin_america /headquarters/sales anthony.nichols member north_america /headquarters/sales
================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/OrganizationWithSpecialCharacters.xml ================================================ é éé ééééééééééééééééééééééééé false ééé éééé ééééééééé ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/complexOrganization.xml ================================================ bpm John Doe john.png /icons/ M jack Web Team Manager emailValue phoneNumberValue mobileNumberValue faxNumberValue buildingValue roomValue
addressValue
zipCodeValue cityValue stateValue countryValue websiteValue
emailProfessionalValue phoneNumberProfessionalValue mobileNumberProfessionalValue faxNumberProfessionalValue buildingProfessionalValue roomProfessionalValue
addressProfessionalValue
zipCodeProfessionalValue cityProfessionalValue stateProfessionalValue countryProfessionalValue websiteProfessionalValue
bpm Web Team Manager bpm
Bonita developer Bonita Manager Bonitasoft S.A. The best BPM compagny R&D team All Rd of BonitaSoft R&D team All Rd of all compagnies Support team john Developer RD /BonitaSoft jack 1331142448365 jack Developer RD
================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/complexOrganizationWithBadGroup.xml ================================================ bpm John Doe john.png /icons/ M jack Web Team Manager emailValue phoneNumberValue mobileNumberValue faxNumberValue buildingValue roomValue
addressValue
zipCodeValue cityValue stateValue countryValue websiteValue
emailProfessionalValue phoneNumberProfessionalValue mobileNumberProfessionalValue faxNumberProfessionalValue buildingProfessionalValue roomProfessionalValue
addressProfessionalValue
zipCodeProfessionalValue cityProfessionalValue stateProfessionalValue countryProfessionalValue websiteProfessionalValue
bpm Web Team Manager bpm
Bonita developer Bonita Manager Bonitasoft S.A. The best BPM compagny R&D team All Rd of BonitaSoft R&D team All Rd of all compagnies Support team john Developer RD /BonitaSoft jack 1331142448365 jack Developer RD
================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/hugeOrganization.xml ================================================ LcTkpvvquKf4KO+prsfXrQ== John Doe false john0.doe@anywhere.com john0.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john1.doe@anywhere.com john1.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john10.doe@anywhere.com john10.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john100.doe@anywhere.com john100.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john101.doe@anywhere.com john101.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john102.doe@anywhere.com john102.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john103.doe@anywhere.com john103.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john104.doe@anywhere.com john104.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john105.doe@anywhere.com john105.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john106.doe@anywhere.com john106.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john107.doe@anywhere.com john107.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john108.doe@anywhere.com john108.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john109.doe@anywhere.com john109.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john11.doe@anywhere.com john11.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john110.doe@anywhere.com john110.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john111.doe@anywhere.com john111.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john112.doe@anywhere.com john112.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john113.doe@anywhere.com john113.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john114.doe@anywhere.com john114.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john115.doe@anywhere.com john115.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john116.doe@anywhere.com john116.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john117.doe@anywhere.com john117.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john118.doe@anywhere.com john118.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john119.doe@anywhere.com john119.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john12.doe@anywhere.com john12.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john120.doe@anywhere.com john120.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john121.doe@anywhere.com john121.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john122.doe@anywhere.com john122.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john123.doe@anywhere.com john123.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john124.doe@anywhere.com john124.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john125.doe@anywhere.com john125.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john126.doe@anywhere.com john126.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john127.doe@anywhere.com john127.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john128.doe@anywhere.com john128.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john129.doe@anywhere.com john129.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john13.doe@anywhere.com john13.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john130.doe@anywhere.com john130.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john131.doe@anywhere.com john131.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john132.doe@anywhere.com john132.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john133.doe@anywhere.com john133.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john134.doe@anywhere.com john134.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john135.doe@anywhere.com john135.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john136.doe@anywhere.com john136.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john137.doe@anywhere.com john137.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john138.doe@anywhere.com john138.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john139.doe@anywhere.com john139.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john14.doe@anywhere.com john14.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john140.doe@anywhere.com john140.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john141.doe@anywhere.com john141.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john142.doe@anywhere.com john142.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john143.doe@anywhere.com john143.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john144.doe@anywhere.com john144.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john145.doe@anywhere.com john145.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john146.doe@anywhere.com john146.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john147.doe@anywhere.com john147.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john148.doe@anywhere.com john148.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john149.doe@anywhere.com john149.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john15.doe@anywhere.com john15.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john150.doe@anywhere.com john150.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john151.doe@anywhere.com john151.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john152.doe@anywhere.com john152.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john153.doe@anywhere.com john153.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john154.doe@anywhere.com john154.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john155.doe@anywhere.com john155.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john156.doe@anywhere.com john156.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john157.doe@anywhere.com john157.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john158.doe@anywhere.com john158.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john159.doe@anywhere.com john159.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john16.doe@anywhere.com john16.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john160.doe@anywhere.com john160.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john161.doe@anywhere.com john161.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john162.doe@anywhere.com john162.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john163.doe@anywhere.com john163.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john164.doe@anywhere.com john164.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john165.doe@anywhere.com john165.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john166.doe@anywhere.com john166.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john167.doe@anywhere.com john167.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john168.doe@anywhere.com john168.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john169.doe@anywhere.com john169.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john17.doe@anywhere.com john17.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john170.doe@anywhere.com john170.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john171.doe@anywhere.com john171.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john172.doe@anywhere.com john172.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john173.doe@anywhere.com john173.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john174.doe@anywhere.com john174.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john175.doe@anywhere.com john175.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john176.doe@anywhere.com john176.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john177.doe@anywhere.com john177.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john178.doe@anywhere.com john178.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john179.doe@anywhere.com john179.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john18.doe@anywhere.com john18.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john180.doe@anywhere.com john180.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john181.doe@anywhere.com john181.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john182.doe@anywhere.com john182.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john183.doe@anywhere.com john183.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john184.doe@anywhere.com john184.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john185.doe@anywhere.com john185.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john186.doe@anywhere.com john186.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john187.doe@anywhere.com john187.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john188.doe@anywhere.com john188.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john189.doe@anywhere.com john189.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john19.doe@anywhere.com john19.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john190.doe@anywhere.com john190.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john191.doe@anywhere.com john191.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john192.doe@anywhere.com john192.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john193.doe@anywhere.com john193.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john194.doe@anywhere.com john194.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john195.doe@anywhere.com john195.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john196.doe@anywhere.com john196.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john197.doe@anywhere.com john197.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john198.doe@anywhere.com john198.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john199.doe@anywhere.com john199.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john2.doe@anywhere.com john2.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john20.doe@anywhere.com john20.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john200.doe@anywhere.com john200.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john201.doe@anywhere.com john201.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john202.doe@anywhere.com john202.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john203.doe@anywhere.com john203.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john204.doe@anywhere.com john204.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john205.doe@anywhere.com john205.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john206.doe@anywhere.com john206.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john207.doe@anywhere.com john207.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john208.doe@anywhere.com john208.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john209.doe@anywhere.com john209.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john21.doe@anywhere.com john21.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john210.doe@anywhere.com john210.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john211.doe@anywhere.com john211.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john212.doe@anywhere.com john212.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john213.doe@anywhere.com john213.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john214.doe@anywhere.com john214.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john215.doe@anywhere.com john215.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john216.doe@anywhere.com john216.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john217.doe@anywhere.com john217.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john218.doe@anywhere.com john218.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john219.doe@anywhere.com john219.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john22.doe@anywhere.com john22.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john220.doe@anywhere.com john220.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john221.doe@anywhere.com john221.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john222.doe@anywhere.com john222.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john223.doe@anywhere.com john223.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john224.doe@anywhere.com john224.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john225.doe@anywhere.com john225.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john226.doe@anywhere.com john226.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john227.doe@anywhere.com john227.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john228.doe@anywhere.com john228.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john229.doe@anywhere.com john229.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john23.doe@anywhere.com john23.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john230.doe@anywhere.com john230.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john231.doe@anywhere.com john231.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john232.doe@anywhere.com john232.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john233.doe@anywhere.com john233.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john234.doe@anywhere.com john234.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john235.doe@anywhere.com john235.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john236.doe@anywhere.com john236.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john237.doe@anywhere.com john237.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john238.doe@anywhere.com john238.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john239.doe@anywhere.com john239.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john24.doe@anywhere.com john24.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john240.doe@anywhere.com john240.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john241.doe@anywhere.com john241.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john242.doe@anywhere.com john242.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john243.doe@anywhere.com john243.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john244.doe@anywhere.com john244.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john245.doe@anywhere.com john245.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john246.doe@anywhere.com john246.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john247.doe@anywhere.com john247.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john248.doe@anywhere.com john248.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john249.doe@anywhere.com john249.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john25.doe@anywhere.com john25.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john250.doe@anywhere.com john250.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john251.doe@anywhere.com john251.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john252.doe@anywhere.com john252.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john253.doe@anywhere.com john253.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john254.doe@anywhere.com john254.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john255.doe@anywhere.com john255.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john256.doe@anywhere.com john256.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john257.doe@anywhere.com john257.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john258.doe@anywhere.com john258.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john259.doe@anywhere.com john259.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john26.doe@anywhere.com john26.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john260.doe@anywhere.com john260.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john261.doe@anywhere.com john261.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john262.doe@anywhere.com john262.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john263.doe@anywhere.com john263.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john264.doe@anywhere.com john264.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john265.doe@anywhere.com john265.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john266.doe@anywhere.com john266.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john267.doe@anywhere.com john267.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john268.doe@anywhere.com john268.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john269.doe@anywhere.com john269.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john27.doe@anywhere.com john27.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john270.doe@anywhere.com john270.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john271.doe@anywhere.com john271.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john272.doe@anywhere.com john272.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john273.doe@anywhere.com john273.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john274.doe@anywhere.com john274.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john275.doe@anywhere.com john275.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john276.doe@anywhere.com john276.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john277.doe@anywhere.com john277.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john278.doe@anywhere.com john278.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john279.doe@anywhere.com john279.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john28.doe@anywhere.com john28.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john280.doe@anywhere.com john280.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john281.doe@anywhere.com john281.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john282.doe@anywhere.com john282.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john283.doe@anywhere.com john283.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john284.doe@anywhere.com john284.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john285.doe@anywhere.com john285.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john286.doe@anywhere.com john286.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john287.doe@anywhere.com john287.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john288.doe@anywhere.com john288.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john289.doe@anywhere.com john289.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john29.doe@anywhere.com john29.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john290.doe@anywhere.com john290.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john291.doe@anywhere.com john291.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john292.doe@anywhere.com john292.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john293.doe@anywhere.com john293.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john294.doe@anywhere.com john294.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john295.doe@anywhere.com john295.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john296.doe@anywhere.com john296.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john297.doe@anywhere.com john297.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john298.doe@anywhere.com john298.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john299.doe@anywhere.com john299.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john3.doe@anywhere.com john3.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john30.doe@anywhere.com john30.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john300.doe@anywhere.com john300.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john301.doe@anywhere.com john301.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john302.doe@anywhere.com john302.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john303.doe@anywhere.com john303.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john304.doe@anywhere.com john304.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john305.doe@anywhere.com john305.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john306.doe@anywhere.com john306.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john307.doe@anywhere.com john307.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john308.doe@anywhere.com john308.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john309.doe@anywhere.com john309.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john31.doe@anywhere.com john31.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john310.doe@anywhere.com john310.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john311.doe@anywhere.com john311.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john312.doe@anywhere.com john312.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john313.doe@anywhere.com john313.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john314.doe@anywhere.com john314.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john315.doe@anywhere.com john315.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john316.doe@anywhere.com john316.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john317.doe@anywhere.com john317.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john318.doe@anywhere.com john318.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john319.doe@anywhere.com john319.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john32.doe@anywhere.com john32.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john320.doe@anywhere.com john320.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john321.doe@anywhere.com john321.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john322.doe@anywhere.com john322.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john323.doe@anywhere.com john323.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john324.doe@anywhere.com john324.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john325.doe@anywhere.com john325.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john326.doe@anywhere.com john326.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john327.doe@anywhere.com john327.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john328.doe@anywhere.com john328.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john329.doe@anywhere.com john329.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john33.doe@anywhere.com john33.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john330.doe@anywhere.com john330.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john331.doe@anywhere.com john331.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john332.doe@anywhere.com john332.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john333.doe@anywhere.com john333.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john334.doe@anywhere.com john334.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john335.doe@anywhere.com john335.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john336.doe@anywhere.com john336.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john337.doe@anywhere.com john337.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john338.doe@anywhere.com john338.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john339.doe@anywhere.com john339.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john34.doe@anywhere.com john34.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john340.doe@anywhere.com john340.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john341.doe@anywhere.com john341.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john342.doe@anywhere.com john342.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john343.doe@anywhere.com john343.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john344.doe@anywhere.com john344.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john345.doe@anywhere.com john345.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john346.doe@anywhere.com john346.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john347.doe@anywhere.com john347.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john348.doe@anywhere.com john348.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john349.doe@anywhere.com john349.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john35.doe@anywhere.com john35.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john350.doe@anywhere.com john350.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john351.doe@anywhere.com john351.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john352.doe@anywhere.com john352.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john353.doe@anywhere.com john353.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john354.doe@anywhere.com john354.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john355.doe@anywhere.com john355.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john356.doe@anywhere.com john356.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john357.doe@anywhere.com john357.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john358.doe@anywhere.com john358.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john359.doe@anywhere.com john359.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john36.doe@anywhere.com john36.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john360.doe@anywhere.com john360.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john361.doe@anywhere.com john361.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john362.doe@anywhere.com john362.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john363.doe@anywhere.com john363.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john364.doe@anywhere.com john364.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john365.doe@anywhere.com john365.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john366.doe@anywhere.com john366.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john367.doe@anywhere.com john367.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john368.doe@anywhere.com john368.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john369.doe@anywhere.com john369.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john37.doe@anywhere.com john37.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john370.doe@anywhere.com john370.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john371.doe@anywhere.com john371.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john372.doe@anywhere.com john372.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john373.doe@anywhere.com john373.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john374.doe@anywhere.com john374.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john375.doe@anywhere.com john375.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john376.doe@anywhere.com john376.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john377.doe@anywhere.com john377.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john378.doe@anywhere.com john378.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john379.doe@anywhere.com john379.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john38.doe@anywhere.com john38.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john380.doe@anywhere.com john380.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john381.doe@anywhere.com john381.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john382.doe@anywhere.com john382.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john383.doe@anywhere.com john383.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john384.doe@anywhere.com john384.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john385.doe@anywhere.com john385.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john386.doe@anywhere.com john386.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john387.doe@anywhere.com john387.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john388.doe@anywhere.com john388.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john389.doe@anywhere.com john389.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john39.doe@anywhere.com john39.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john390.doe@anywhere.com john390.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john391.doe@anywhere.com john391.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john392.doe@anywhere.com john392.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john393.doe@anywhere.com john393.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john394.doe@anywhere.com john394.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john395.doe@anywhere.com john395.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john396.doe@anywhere.com john396.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john397.doe@anywhere.com john397.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john398.doe@anywhere.com john398.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john399.doe@anywhere.com john399.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john4.doe@anywhere.com john4.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john40.doe@anywhere.com john40.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john400.doe@anywhere.com john400.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john401.doe@anywhere.com john401.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john402.doe@anywhere.com john402.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john403.doe@anywhere.com john403.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john404.doe@anywhere.com john404.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john405.doe@anywhere.com john405.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john406.doe@anywhere.com john406.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john407.doe@anywhere.com john407.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john408.doe@anywhere.com john408.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john409.doe@anywhere.com john409.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john41.doe@anywhere.com john41.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john410.doe@anywhere.com john410.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john411.doe@anywhere.com john411.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john412.doe@anywhere.com john412.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john413.doe@anywhere.com john413.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john414.doe@anywhere.com john414.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john415.doe@anywhere.com john415.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john416.doe@anywhere.com john416.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john417.doe@anywhere.com john417.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john418.doe@anywhere.com john418.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john419.doe@anywhere.com john419.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john42.doe@anywhere.com john42.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john420.doe@anywhere.com john420.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john421.doe@anywhere.com john421.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john422.doe@anywhere.com john422.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john423.doe@anywhere.com john423.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john424.doe@anywhere.com john424.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john425.doe@anywhere.com john425.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john426.doe@anywhere.com john426.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john427.doe@anywhere.com john427.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john428.doe@anywhere.com john428.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john429.doe@anywhere.com john429.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john43.doe@anywhere.com john43.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john430.doe@anywhere.com john430.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john431.doe@anywhere.com john431.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john432.doe@anywhere.com john432.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john433.doe@anywhere.com john433.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john434.doe@anywhere.com john434.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john435.doe@anywhere.com john435.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john436.doe@anywhere.com john436.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john437.doe@anywhere.com john437.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john438.doe@anywhere.com john438.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john439.doe@anywhere.com john439.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john44.doe@anywhere.com john44.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john440.doe@anywhere.com john440.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john441.doe@anywhere.com john441.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john442.doe@anywhere.com john442.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john443.doe@anywhere.com john443.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john444.doe@anywhere.com john444.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john445.doe@anywhere.com john445.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john446.doe@anywhere.com john446.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john447.doe@anywhere.com john447.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john448.doe@anywhere.com john448.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john449.doe@anywhere.com john449.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john45.doe@anywhere.com john45.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john450.doe@anywhere.com john450.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john451.doe@anywhere.com john451.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john452.doe@anywhere.com john452.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john453.doe@anywhere.com john453.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john454.doe@anywhere.com john454.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john455.doe@anywhere.com john455.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john456.doe@anywhere.com john456.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john457.doe@anywhere.com john457.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john458.doe@anywhere.com john458.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john459.doe@anywhere.com john459.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john46.doe@anywhere.com john46.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john460.doe@anywhere.com john460.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john461.doe@anywhere.com john461.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john462.doe@anywhere.com john462.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john463.doe@anywhere.com john463.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john464.doe@anywhere.com john464.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john465.doe@anywhere.com john465.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john466.doe@anywhere.com john466.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john467.doe@anywhere.com john467.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john468.doe@anywhere.com john468.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john469.doe@anywhere.com john469.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john47.doe@anywhere.com john47.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john470.doe@anywhere.com john470.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john471.doe@anywhere.com john471.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john472.doe@anywhere.com john472.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john473.doe@anywhere.com john473.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john474.doe@anywhere.com john474.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john475.doe@anywhere.com john475.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john476.doe@anywhere.com john476.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john477.doe@anywhere.com john477.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john478.doe@anywhere.com john478.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john479.doe@anywhere.com john479.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john48.doe@anywhere.com john48.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john480.doe@anywhere.com john480.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john481.doe@anywhere.com john481.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john482.doe@anywhere.com john482.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john483.doe@anywhere.com john483.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john484.doe@anywhere.com john484.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john485.doe@anywhere.com john485.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john486.doe@anywhere.com john486.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john487.doe@anywhere.com john487.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john488.doe@anywhere.com john488.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john489.doe@anywhere.com john489.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john49.doe@anywhere.com john49.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john490.doe@anywhere.com john490.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john491.doe@anywhere.com john491.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john492.doe@anywhere.com john492.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john493.doe@anywhere.com john493.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john494.doe@anywhere.com john494.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john495.doe@anywhere.com john495.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john496.doe@anywhere.com john496.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john497.doe@anywhere.com john497.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john498.doe@anywhere.com john498.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john499.doe@anywhere.com john499.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john5.doe@anywhere.com john5.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john50.doe@anywhere.com john50.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john500.doe@anywhere.com john500.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john501.doe@anywhere.com john501.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john502.doe@anywhere.com john502.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john503.doe@anywhere.com john503.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john504.doe@anywhere.com john504.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john505.doe@anywhere.com john505.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john506.doe@anywhere.com john506.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john507.doe@anywhere.com john507.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john508.doe@anywhere.com john508.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john509.doe@anywhere.com john509.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john51.doe@anywhere.com john51.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john510.doe@anywhere.com john510.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john511.doe@anywhere.com john511.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john512.doe@anywhere.com john512.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john513.doe@anywhere.com john513.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john514.doe@anywhere.com john514.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john515.doe@anywhere.com john515.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john516.doe@anywhere.com john516.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john517.doe@anywhere.com john517.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john518.doe@anywhere.com john518.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john519.doe@anywhere.com john519.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john52.doe@anywhere.com john52.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john520.doe@anywhere.com john520.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john521.doe@anywhere.com john521.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john522.doe@anywhere.com john522.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john523.doe@anywhere.com john523.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john524.doe@anywhere.com john524.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john525.doe@anywhere.com john525.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john526.doe@anywhere.com john526.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john527.doe@anywhere.com john527.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john528.doe@anywhere.com john528.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john529.doe@anywhere.com john529.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john53.doe@anywhere.com john53.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john530.doe@anywhere.com john530.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john531.doe@anywhere.com john531.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john532.doe@anywhere.com john532.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john533.doe@anywhere.com john533.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john534.doe@anywhere.com john534.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john535.doe@anywhere.com john535.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john536.doe@anywhere.com john536.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john537.doe@anywhere.com john537.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john538.doe@anywhere.com john538.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john539.doe@anywhere.com john539.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john54.doe@anywhere.com john54.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john540.doe@anywhere.com john540.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john541.doe@anywhere.com john541.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john542.doe@anywhere.com john542.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john543.doe@anywhere.com john543.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john544.doe@anywhere.com john544.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john545.doe@anywhere.com john545.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john546.doe@anywhere.com john546.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john547.doe@anywhere.com john547.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john548.doe@anywhere.com john548.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john549.doe@anywhere.com john549.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john55.doe@anywhere.com john55.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john550.doe@anywhere.com john550.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john551.doe@anywhere.com john551.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john552.doe@anywhere.com john552.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john553.doe@anywhere.com john553.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john554.doe@anywhere.com john554.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john555.doe@anywhere.com john555.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john556.doe@anywhere.com john556.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john557.doe@anywhere.com john557.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john558.doe@anywhere.com john558.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john559.doe@anywhere.com john559.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john56.doe@anywhere.com john56.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john560.doe@anywhere.com john560.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john561.doe@anywhere.com john561.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john562.doe@anywhere.com john562.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john563.doe@anywhere.com john563.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john564.doe@anywhere.com john564.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john565.doe@anywhere.com john565.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john566.doe@anywhere.com john566.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john567.doe@anywhere.com john567.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john568.doe@anywhere.com john568.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john569.doe@anywhere.com john569.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john57.doe@anywhere.com john57.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john570.doe@anywhere.com john570.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john571.doe@anywhere.com john571.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john572.doe@anywhere.com john572.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john573.doe@anywhere.com john573.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john574.doe@anywhere.com john574.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john575.doe@anywhere.com john575.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john576.doe@anywhere.com john576.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john577.doe@anywhere.com john577.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john578.doe@anywhere.com john578.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john579.doe@anywhere.com john579.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john58.doe@anywhere.com john58.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john580.doe@anywhere.com john580.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john581.doe@anywhere.com john581.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john582.doe@anywhere.com john582.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john583.doe@anywhere.com john583.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john584.doe@anywhere.com john584.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john585.doe@anywhere.com john585.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john586.doe@anywhere.com john586.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john587.doe@anywhere.com john587.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john588.doe@anywhere.com john588.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john589.doe@anywhere.com john589.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john59.doe@anywhere.com john59.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john590.doe@anywhere.com john590.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john591.doe@anywhere.com john591.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john592.doe@anywhere.com john592.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john593.doe@anywhere.com john593.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john594.doe@anywhere.com john594.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john595.doe@anywhere.com john595.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john596.doe@anywhere.com john596.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john597.doe@anywhere.com john597.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john598.doe@anywhere.com john598.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john599.doe@anywhere.com john599.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john6.doe@anywhere.com john6.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john60.doe@anywhere.com john60.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john600.doe@anywhere.com john600.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john601.doe@anywhere.com john601.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john602.doe@anywhere.com john602.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john603.doe@anywhere.com john603.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john604.doe@anywhere.com john604.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john605.doe@anywhere.com john605.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john606.doe@anywhere.com john606.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john607.doe@anywhere.com john607.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john608.doe@anywhere.com john608.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john609.doe@anywhere.com john609.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john61.doe@anywhere.com john61.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john610.doe@anywhere.com john610.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john611.doe@anywhere.com john611.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john612.doe@anywhere.com john612.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john613.doe@anywhere.com john613.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john614.doe@anywhere.com john614.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john615.doe@anywhere.com john615.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john616.doe@anywhere.com john616.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john617.doe@anywhere.com john617.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john618.doe@anywhere.com john618.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john619.doe@anywhere.com john619.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john62.doe@anywhere.com john62.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john620.doe@anywhere.com john620.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john621.doe@anywhere.com john621.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john622.doe@anywhere.com john622.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john623.doe@anywhere.com john623.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john624.doe@anywhere.com john624.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john625.doe@anywhere.com john625.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john626.doe@anywhere.com john626.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john627.doe@anywhere.com john627.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john628.doe@anywhere.com john628.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john629.doe@anywhere.com john629.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john63.doe@anywhere.com john63.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john630.doe@anywhere.com john630.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john631.doe@anywhere.com john631.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john632.doe@anywhere.com john632.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john633.doe@anywhere.com john633.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john634.doe@anywhere.com john634.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john635.doe@anywhere.com john635.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john636.doe@anywhere.com john636.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john637.doe@anywhere.com john637.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john638.doe@anywhere.com john638.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john639.doe@anywhere.com john639.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john64.doe@anywhere.com john64.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john640.doe@anywhere.com john640.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john641.doe@anywhere.com john641.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john642.doe@anywhere.com john642.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john643.doe@anywhere.com john643.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john644.doe@anywhere.com john644.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john645.doe@anywhere.com john645.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john646.doe@anywhere.com john646.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john647.doe@anywhere.com john647.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john648.doe@anywhere.com john648.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john649.doe@anywhere.com john649.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john65.doe@anywhere.com john65.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john650.doe@anywhere.com john650.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john651.doe@anywhere.com john651.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john652.doe@anywhere.com john652.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john653.doe@anywhere.com john653.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john654.doe@anywhere.com john654.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john655.doe@anywhere.com john655.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john656.doe@anywhere.com john656.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john657.doe@anywhere.com john657.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john658.doe@anywhere.com john658.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john659.doe@anywhere.com john659.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john66.doe@anywhere.com john66.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john660.doe@anywhere.com john660.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john661.doe@anywhere.com john661.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john662.doe@anywhere.com john662.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john663.doe@anywhere.com john663.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john664.doe@anywhere.com john664.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john665.doe@anywhere.com john665.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john666.doe@anywhere.com john666.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john667.doe@anywhere.com john667.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john668.doe@anywhere.com john668.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john669.doe@anywhere.com john669.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john67.doe@anywhere.com john67.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john670.doe@anywhere.com john670.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john671.doe@anywhere.com john671.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john672.doe@anywhere.com john672.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john673.doe@anywhere.com john673.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john674.doe@anywhere.com john674.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john675.doe@anywhere.com john675.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john676.doe@anywhere.com john676.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john677.doe@anywhere.com john677.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john678.doe@anywhere.com john678.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john679.doe@anywhere.com john679.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john68.doe@anywhere.com john68.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john680.doe@anywhere.com john680.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john681.doe@anywhere.com john681.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john682.doe@anywhere.com john682.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john683.doe@anywhere.com john683.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john684.doe@anywhere.com john684.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john685.doe@anywhere.com john685.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john686.doe@anywhere.com john686.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john687.doe@anywhere.com john687.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john688.doe@anywhere.com john688.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john689.doe@anywhere.com john689.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john69.doe@anywhere.com john69.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john690.doe@anywhere.com john690.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john691.doe@anywhere.com john691.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john692.doe@anywhere.com john692.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john693.doe@anywhere.com john693.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john694.doe@anywhere.com john694.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john695.doe@anywhere.com john695.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john696.doe@anywhere.com john696.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john697.doe@anywhere.com john697.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john698.doe@anywhere.com john698.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john699.doe@anywhere.com john699.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john7.doe@anywhere.com john7.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john70.doe@anywhere.com john70.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john700.doe@anywhere.com john700.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john701.doe@anywhere.com john701.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john702.doe@anywhere.com john702.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john703.doe@anywhere.com john703.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john704.doe@anywhere.com john704.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john705.doe@anywhere.com john705.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john706.doe@anywhere.com john706.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john707.doe@anywhere.com john707.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john708.doe@anywhere.com john708.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john709.doe@anywhere.com john709.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john71.doe@anywhere.com john71.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john710.doe@anywhere.com john710.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john711.doe@anywhere.com john711.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john712.doe@anywhere.com john712.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john713.doe@anywhere.com john713.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john714.doe@anywhere.com john714.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john715.doe@anywhere.com john715.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john716.doe@anywhere.com john716.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john717.doe@anywhere.com john717.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john718.doe@anywhere.com john718.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john719.doe@anywhere.com john719.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john72.doe@anywhere.com john72.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john720.doe@anywhere.com john720.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john721.doe@anywhere.com john721.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john722.doe@anywhere.com john722.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john723.doe@anywhere.com john723.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john724.doe@anywhere.com john724.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john725.doe@anywhere.com john725.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john726.doe@anywhere.com john726.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john727.doe@anywhere.com john727.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john728.doe@anywhere.com john728.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john729.doe@anywhere.com john729.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john73.doe@anywhere.com john73.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john730.doe@anywhere.com john730.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john731.doe@anywhere.com john731.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john732.doe@anywhere.com john732.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john733.doe@anywhere.com john733.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john734.doe@anywhere.com john734.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john735.doe@anywhere.com john735.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john736.doe@anywhere.com john736.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john737.doe@anywhere.com john737.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john738.doe@anywhere.com john738.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john739.doe@anywhere.com john739.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john74.doe@anywhere.com john74.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john740.doe@anywhere.com john740.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john741.doe@anywhere.com john741.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john742.doe@anywhere.com john742.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john743.doe@anywhere.com john743.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john744.doe@anywhere.com john744.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john745.doe@anywhere.com john745.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john746.doe@anywhere.com john746.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john747.doe@anywhere.com john747.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john748.doe@anywhere.com john748.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john749.doe@anywhere.com john749.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john75.doe@anywhere.com john75.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john750.doe@anywhere.com john750.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john751.doe@anywhere.com john751.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john752.doe@anywhere.com john752.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john753.doe@anywhere.com john753.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john754.doe@anywhere.com john754.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john755.doe@anywhere.com john755.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john756.doe@anywhere.com john756.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john757.doe@anywhere.com john757.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john758.doe@anywhere.com john758.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john759.doe@anywhere.com john759.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john76.doe@anywhere.com john76.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john760.doe@anywhere.com john760.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john761.doe@anywhere.com john761.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john762.doe@anywhere.com john762.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john763.doe@anywhere.com john763.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john764.doe@anywhere.com john764.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john765.doe@anywhere.com john765.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john766.doe@anywhere.com john766.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john767.doe@anywhere.com john767.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john768.doe@anywhere.com john768.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john769.doe@anywhere.com john769.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john77.doe@anywhere.com john77.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john770.doe@anywhere.com john770.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john771.doe@anywhere.com john771.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john772.doe@anywhere.com john772.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john773.doe@anywhere.com john773.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john774.doe@anywhere.com john774.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john775.doe@anywhere.com john775.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john776.doe@anywhere.com john776.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john777.doe@anywhere.com john777.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john778.doe@anywhere.com john778.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john779.doe@anywhere.com john779.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john78.doe@anywhere.com john78.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john780.doe@anywhere.com john780.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john781.doe@anywhere.com john781.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john782.doe@anywhere.com john782.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john783.doe@anywhere.com john783.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john784.doe@anywhere.com john784.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john785.doe@anywhere.com john785.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john786.doe@anywhere.com john786.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john787.doe@anywhere.com john787.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john788.doe@anywhere.com john788.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john789.doe@anywhere.com john789.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john79.doe@anywhere.com john79.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john790.doe@anywhere.com john790.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john791.doe@anywhere.com john791.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john792.doe@anywhere.com john792.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john793.doe@anywhere.com john793.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john794.doe@anywhere.com john794.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john795.doe@anywhere.com john795.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john796.doe@anywhere.com john796.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john797.doe@anywhere.com john797.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john798.doe@anywhere.com john798.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john799.doe@anywhere.com john799.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john8.doe@anywhere.com john8.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john80.doe@anywhere.com john80.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john800.doe@anywhere.com john800.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john801.doe@anywhere.com john801.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john802.doe@anywhere.com john802.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john803.doe@anywhere.com john803.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john804.doe@anywhere.com john804.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john805.doe@anywhere.com john805.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john806.doe@anywhere.com john806.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john807.doe@anywhere.com john807.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john808.doe@anywhere.com john808.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john809.doe@anywhere.com john809.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john81.doe@anywhere.com john81.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john810.doe@anywhere.com john810.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john811.doe@anywhere.com john811.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john812.doe@anywhere.com john812.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john813.doe@anywhere.com john813.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john814.doe@anywhere.com john814.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john815.doe@anywhere.com john815.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john816.doe@anywhere.com john816.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john817.doe@anywhere.com john817.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john818.doe@anywhere.com john818.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john819.doe@anywhere.com john819.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john82.doe@anywhere.com john82.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john820.doe@anywhere.com john820.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john821.doe@anywhere.com john821.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john822.doe@anywhere.com john822.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john823.doe@anywhere.com john823.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john824.doe@anywhere.com john824.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john825.doe@anywhere.com john825.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john826.doe@anywhere.com john826.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john827.doe@anywhere.com john827.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john828.doe@anywhere.com john828.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john829.doe@anywhere.com john829.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john83.doe@anywhere.com john83.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john830.doe@anywhere.com john830.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john831.doe@anywhere.com john831.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john832.doe@anywhere.com john832.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john833.doe@anywhere.com john833.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john834.doe@anywhere.com john834.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john835.doe@anywhere.com john835.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john836.doe@anywhere.com john836.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john837.doe@anywhere.com john837.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john838.doe@anywhere.com john838.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john839.doe@anywhere.com john839.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john84.doe@anywhere.com john84.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john840.doe@anywhere.com john840.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john841.doe@anywhere.com john841.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john842.doe@anywhere.com john842.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john843.doe@anywhere.com john843.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john844.doe@anywhere.com john844.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john845.doe@anywhere.com john845.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john846.doe@anywhere.com john846.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john847.doe@anywhere.com john847.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john848.doe@anywhere.com john848.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john849.doe@anywhere.com john849.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john85.doe@anywhere.com john85.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john850.doe@anywhere.com john850.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john851.doe@anywhere.com john851.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john852.doe@anywhere.com john852.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john853.doe@anywhere.com john853.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john854.doe@anywhere.com john854.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john855.doe@anywhere.com john855.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john856.doe@anywhere.com john856.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john857.doe@anywhere.com john857.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john858.doe@anywhere.com john858.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john859.doe@anywhere.com john859.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john86.doe@anywhere.com john86.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john860.doe@anywhere.com john860.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john861.doe@anywhere.com john861.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john862.doe@anywhere.com john862.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john863.doe@anywhere.com john863.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john864.doe@anywhere.com john864.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john865.doe@anywhere.com john865.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john866.doe@anywhere.com john866.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john867.doe@anywhere.com john867.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john868.doe@anywhere.com john868.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john869.doe@anywhere.com john869.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john87.doe@anywhere.com john87.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john870.doe@anywhere.com john870.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john871.doe@anywhere.com john871.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john872.doe@anywhere.com john872.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john873.doe@anywhere.com john873.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john874.doe@anywhere.com john874.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john875.doe@anywhere.com john875.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john876.doe@anywhere.com john876.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john877.doe@anywhere.com john877.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john878.doe@anywhere.com john878.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john879.doe@anywhere.com john879.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john88.doe@anywhere.com john88.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john880.doe@anywhere.com john880.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john881.doe@anywhere.com john881.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john882.doe@anywhere.com john882.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john883.doe@anywhere.com john883.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john884.doe@anywhere.com john884.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john885.doe@anywhere.com john885.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john886.doe@anywhere.com john886.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john887.doe@anywhere.com john887.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john888.doe@anywhere.com john888.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john889.doe@anywhere.com john889.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john89.doe@anywhere.com john89.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john890.doe@anywhere.com john890.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john891.doe@anywhere.com john891.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john892.doe@anywhere.com john892.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john893.doe@anywhere.com john893.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john894.doe@anywhere.com john894.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john895.doe@anywhere.com john895.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john896.doe@anywhere.com john896.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john897.doe@anywhere.com john897.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john898.doe@anywhere.com john898.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john899.doe@anywhere.com john899.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john9.doe@anywhere.com john9.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john90.doe@anywhere.com john90.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john900.doe@anywhere.com john900.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john901.doe@anywhere.com john901.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john902.doe@anywhere.com john902.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john903.doe@anywhere.com john903.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john904.doe@anywhere.com john904.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john905.doe@anywhere.com john905.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john906.doe@anywhere.com john906.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john907.doe@anywhere.com john907.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john908.doe@anywhere.com john908.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john909.doe@anywhere.com john909.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john91.doe@anywhere.com john91.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john910.doe@anywhere.com john910.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john911.doe@anywhere.com john911.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john912.doe@anywhere.com john912.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john913.doe@anywhere.com john913.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john914.doe@anywhere.com john914.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john915.doe@anywhere.com john915.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john916.doe@anywhere.com john916.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john917.doe@anywhere.com john917.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john918.doe@anywhere.com john918.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john919.doe@anywhere.com john919.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john92.doe@anywhere.com john92.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john920.doe@anywhere.com john920.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john921.doe@anywhere.com john921.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john922.doe@anywhere.com john922.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john923.doe@anywhere.com john923.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john924.doe@anywhere.com john924.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john925.doe@anywhere.com john925.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john926.doe@anywhere.com john926.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john927.doe@anywhere.com john927.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john928.doe@anywhere.com john928.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john929.doe@anywhere.com john929.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john93.doe@anywhere.com john93.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john930.doe@anywhere.com john930.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john931.doe@anywhere.com john931.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john932.doe@anywhere.com john932.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john933.doe@anywhere.com john933.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john934.doe@anywhere.com john934.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john935.doe@anywhere.com john935.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john936.doe@anywhere.com john936.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john937.doe@anywhere.com john937.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john938.doe@anywhere.com john938.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john939.doe@anywhere.com john939.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john94.doe@anywhere.com john94.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john940.doe@anywhere.com john940.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john941.doe@anywhere.com john941.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john942.doe@anywhere.com john942.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john943.doe@anywhere.com john943.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john944.doe@anywhere.com john944.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john945.doe@anywhere.com john945.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john946.doe@anywhere.com john946.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john947.doe@anywhere.com john947.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john948.doe@anywhere.com john948.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john949.doe@anywhere.com john949.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john95.doe@anywhere.com john95.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john950.doe@anywhere.com john950.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john951.doe@anywhere.com john951.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john952.doe@anywhere.com john952.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john953.doe@anywhere.com john953.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john954.doe@anywhere.com john954.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john955.doe@anywhere.com john955.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john956.doe@anywhere.com john956.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john957.doe@anywhere.com john957.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john958.doe@anywhere.com john958.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john959.doe@anywhere.com john959.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john96.doe@anywhere.com john96.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john960.doe@anywhere.com john960.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john961.doe@anywhere.com john961.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john962.doe@anywhere.com john962.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john963.doe@anywhere.com john963.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john964.doe@anywhere.com john964.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john965.doe@anywhere.com john965.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john966.doe@anywhere.com john966.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john967.doe@anywhere.com john967.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john968.doe@anywhere.com john968.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john969.doe@anywhere.com john969.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john97.doe@anywhere.com john97.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john970.doe@anywhere.com john970.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john971.doe@anywhere.com john971.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john972.doe@anywhere.com john972.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john973.doe@anywhere.com john973.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john974.doe@anywhere.com john974.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john975.doe@anywhere.com john975.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john976.doe@anywhere.com john976.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john977.doe@anywhere.com john977.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john978.doe@anywhere.com john978.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john979.doe@anywhere.com john979.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john98.doe@anywhere.com john98.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john980.doe@anywhere.com john980.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john981.doe@anywhere.com john981.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john982.doe@anywhere.com john982.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john983.doe@anywhere.com john983.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john984.doe@anywhere.com john984.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john985.doe@anywhere.com john985.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john986.doe@anywhere.com john986.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john987.doe@anywhere.com john987.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john988.doe@anywhere.com john988.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john989.doe@anywhere.com john989.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john99.doe@anywhere.com john99.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john990.doe@anywhere.com john990.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john991.doe@anywhere.com john991.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john992.doe@anywhere.com john992.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john993.doe@anywhere.com john993.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john994.doe@anywhere.com john994.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john995.doe@anywhere.com john995.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john996.doe@anywhere.com john996.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john997.doe@anywhere.com john997.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john998.doe@anywhere.com john998.doe@bonitasoft.com LcTkpvvquKf4KO+prsfXrQ== John Doe false john999.doe@anywhere.com john999.doe@bonitasoft.com ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/mixOrganization.xml ================================================ bpm true bpm true ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/organizationFailOnDuplicates.xml ================================================ passwordOfJames bpm NotJohn ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/organizationWithCycle.xml ================================================ bpm user2 bpm user3 bpm user1 ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/simpleOrganization.xml ================================================ Office location Skills The user skills bpm Web Team Manager false Office location Engineering Skills Java bpm true Skills Java Bonita developer Bonita Manager engine team web team liuyanyan Developer Engine anthony.birembault Manager Web ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/simpleOrganizationDuplicates1.xml ================================================ Office location Skills The user skills bpm Web Team Manager Office location Engineering Skills Java bpm Skills Java Bonita developer Bonita Manager engine team web team liuyanyan Developer Engine anthony.birembault Manager Web ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/simpleOrganizationDuplicates2.xml ================================================ Office location The office location Skills The user skills were updated bpm QA Analyst false bpm2 true Office location Engineering Skills Java, Groovy Bonitasoft developer Bonita tester RD engine team QA team liuyanyan Developer Engine johnnyfootball Tester QA ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/identity/simpleOrganizationNoDuplicates.xml ================================================ bpm QA Analyst Bonita tester QA team johnnyfootball Tester QA ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/process/actor/actorMappingWithException.xml ================================================ john ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/process/actor/complexActorMapping.xml ================================================ john /RD dev dev /RD ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/process/actor/complexActorMapping2.xml ================================================ dev /RD dev /RD ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/process/actor/complexActorMappingWithUnkownGroup.xml ================================================ john dev dev /RD ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/main/resources/org/bonitasoft/engine/process/actor/simpleActorMapping.xml ================================================ william.jobs ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/MultiThreadCallsIT.java ================================================ /** * 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; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.test.APITestUtil; import org.junit.Assert; import org.junit.Test; public class MultiThreadCallsIT extends CommonAPIIT { class CallAPIMethodsThread extends Thread { private Exception exception; private final APITestUtil apiTestUtil = new APITestUtil(); public CallAPIMethodsThread() { } @Override public void run() { super.run(); try { apiTestUtil.loginWithTechnicalUser(); apiTestUtil.getIdentityAPI().getNumberOfUsers(); apiTestUtil.getIdentityAPI().getNumberOfGroups(); apiTestUtil.logout(); } catch (final Exception e) { exception = e; e.printStackTrace(); } } public Exception getException() { return exception; } public String getMessage() { if (exception != null) { return exception.getMessage(); } return ""; } } @Test public void supportMultiThreadingClients() throws Exception { final int nbOfThreads = 5; final List threads = new ArrayList<>(nbOfThreads); for (int i = 0; i < nbOfThreads; i++) { threads.add(new CallAPIMethodsThread()); } for (final CallAPIMethodsThread thread : threads) { thread.start(); } for (final CallAPIMethodsThread thread : threads) { thread.join(); } for (final CallAPIMethodsThread thread : threads) { Assert.assertNull(thread.getMessage(), thread.getException()); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/RemoteEngineIT.java ================================================ /** * 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; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; import org.bonitasoft.engine.api.ApiAccessType; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceNotFoundException; import org.bonitasoft.engine.util.APITypeManager; import org.junit.Test; /** * Test all things related to remote connection * * @author Baptiste Mesta */ public class RemoteEngineIT extends TestWithTechnicalUser { /* * check that we return the stack server exception but not the server exception itself that is not known to the * client */ @Test public void check_remote_exception_is_given_to_client() throws Exception { try { getProcessAPI().getFlowNodeInstance(123456789L); fail("should fail"); } catch (final FlowNodeInstanceNotFoundException e) { //in local, check root cause is here if (APITypeManager.getAPIType() == ApiAccessType.LOCAL) { Throwable rootCause = e; while (rootCause.getCause() != null) { rootCause = rootCause.getCause(); } assertThat(rootCause.getClass().getSimpleName()).isEqualTo("SFlowNodeNotFoundException"); } else { //in remote, check the stack trace is preserved assertThat(e.getStackTrace()).anyMatch(s -> s.getClassName().contains("SFlowNodeNotFoundException")); assertThat(e.getCause()).isNull(); } } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/StringIndexIT.java ================================================ /** * Copyright (C) 2020 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; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.operation.OperatorType.ASSIGNMENT; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.operation.LeftOperand; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.test.APITestUtil; import org.junit.Test; /** * @author Baptiste Mesta. */ public class StringIndexIT extends CommonAPIIT { @Test public void should_set_string_index_of_current_process_using_operation_in_called_process() throws Exception { loginWithTechnicalUser(); User user = createUser("john", "bpm"); ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("mainProcess", "1.0"); builder.addCallActivity("call", stringConstant("calledProcess"), stringConstant("1.0")); builder.setStringIndex(1, "index1", stringConstant("initial value")); builder.setStringIndex(2, "index2", stringConstant("initial value")); ProcessDefinition mainProcess = deployAndEnableProcess(builder.done()); builder = new ProcessDefinitionBuilder().createNewInstance("calledProcess", "1.0"); builder.addAutomaticTask("task1").addOperation(theStringIndex(1), ASSIGNMENT, "", stringConstant("value from called process")); builder.addUserTask("userTask", "john"); builder.addTransition("task1", "userTask"); builder.addActor("john"); builder.setStringIndex(1, "index1", stringConstant("initial value")); builder.setStringIndex(2, "index2", stringConstant("initial value")); SubProcessDefinitionBuilder subProcessBuilder = builder.addSubProcess("signalSubProcess", true) .getSubProcessBuilder(); subProcessBuilder.addStartEvent("signalStart").addSignalEventTrigger("mySignal") .addAutomaticTask("subTask") .addOperation(theStringIndex(2), ASSIGNMENT, "", stringConstant("value from sub process")) .addUserTask("subUserTask", "john") .addTransition("subTask", "subUserTask") .addTransition("signalStart", "subTask"); ProcessDefinition calledProcess = deployAndEnableProcessWithActor(builder.done(), "john", user); ProcessInstance processInstance = getProcessAPI().startProcess(mainProcess.getId()); HumanTaskInstance userTask = waitForUserTaskAndGetIt("userTask"); getProcessAPI().sendSignal("mySignal"); waitForUserTaskAndGetIt("subUserTask"); Thread.sleep(2000); Assertions.assertThat(getProcessAPI().getProcessInstance(processInstance.getId()).getStringIndex1()) .isEqualTo("initial value"); Assertions.assertThat(getProcessAPI().getProcessInstance(processInstance.getId()).getStringIndex2()) .isEqualTo("initial value"); Assertions .assertThat(getProcessAPI().getProcessInstance(userTask.getParentProcessInstanceId()).getStringIndex1()) .isEqualTo("value from called process"); Assertions .assertThat(getProcessAPI().getProcessInstance(userTask.getParentProcessInstanceId()).getStringIndex2()) .isEqualTo("value from sub process"); disableAndDeleteProcess(mainProcess, calledProcess); deleteUser(user); } private LeftOperand theStringIndex(int index) { return new LeftOperandBuilder().createSearchIndexLeftOperand(index); } @Test public void should_initialize_string_index_in_call_activity() throws Exception { loginWithTechnicalUser(); User user = createUser("john", "bpm"); ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("mainProcess", "1.0"); builder.addCallActivity("call", stringConstant("calledProcess"), stringConstant("1.0")); builder.setStringIndex(1, "index1", stringConstant("value from main process")); ProcessDefinition mainProcess = deployAndEnableProcess(builder.done()); builder = new ProcessDefinitionBuilder().createNewInstance("calledProcess", "1.0"); builder.addAutomaticTask("task1"); builder.addUserTask("userTask", "john"); builder.addTransition("task1", "userTask"); builder.addActor("john"); builder.setStringIndex(1, "index1", stringConstant("value from sub process")); ProcessDefinition calledProcess = deployAndEnableProcessWithActor(builder.done(), "john", user); ProcessInstance processInstance = getProcessAPI().startProcess(mainProcess.getId()); HumanTaskInstance userTask = waitForUserTaskAndGetIt("userTask"); Assertions.assertThat(getProcessAPI().getProcessInstance(processInstance.getId()).getStringIndex1()) .isEqualTo("value from main process"); Assertions .assertThat(getProcessAPI().getProcessInstance(userTask.getParentProcessInstanceId()).getStringIndex1()) .isEqualTo("value from sub process"); disableAndDeleteProcess(mainProcess, calledProcess); deleteUser(user); } private Expression stringConstant(String value) throws InvalidExpressionException { return new ExpressionBuilder().createConstantStringExpression(value); } @Test public void should_be_able_to_initialize_a_search_index_using_a_business_data() throws Exception { loginWithTechnicalUser(); final String qualifiedName = "com.company.test.Bo"; final BusinessObjectModel bom = buildSimpleBom(qualifiedName); assertThat(installBusinessDataModel(bom)).as("should have deployed BDM").isNotNull(); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("test", "1.2-alpha"); processDefinitionBuilder.addActor(APITestUtil.ACTOR_NAME); final String bizDataName = "myBizData"; final Expression defaultBizDataValue = new ExpressionBuilder().createGroovyScriptExpression("createNewBo", "new com.company.test.Bo(aField:'Julius')", qualifiedName); processDefinitionBuilder.addBusinessData(bizDataName, qualifiedName, defaultBizDataValue); Expression businessDataExpression = new ExpressionBuilder().createBusinessDataExpression(bizDataName, qualifiedName); Expression javaMethodCallExpression = new ExpressionBuilder().createJavaMethodCallExpression("call_java_method", "getAField", "java.lang.String", businessDataExpression); processDefinitionBuilder.setStringIndex(1, "param0", javaMethodCallExpression); processDefinitionBuilder.addUserTask("step1", APITestUtil.ACTOR_NAME); User testUser = createUser("john", "bpm"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), APITestUtil.ACTOR_NAME, testUser); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step1"); assertThat(processInstance.getStringIndex1()) .as("String index 1 should be initialized with the business data value").isEqualTo("Julius"); disableAndDeleteProcess(processDefinition); deleteUser(testUser); } private BusinessObjectModel buildSimpleBom(final String boQualifiedName) { final BusinessObject bo = new BusinessObject(); bo.setQualifiedName(boQualifiedName); final SimpleField field = new SimpleField(); field.setName("aField"); field.setType(FieldType.STRING); bo.addField(field); final BusinessObjectModel model = new BusinessObjectModel(); model.addBusinessObject(bo); return model; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/CallActivityIT.java ================================================ /** * 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.activity; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.contract.Type; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.data.DataNotFoundException; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion; import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder; import org.bonitasoft.engine.bpm.process.impl.EndEventDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.IntermediateCatchEventDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.connectors.TestConnectorWithOutput; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.ProcessInstanceHierarchicalDeletionException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.test.TestStates; import org.bonitasoft.engine.test.wait.WaitForFinalArchivedActivity; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ @SuppressWarnings("javadoc") public class CallActivityIT extends TestWithTechnicalUser { private static final String CONNECTOR_OUTPUT_NAME = "output1"; private User cebolinha; private User cascao; @Override @Before public void before() throws Exception { super.before(); cebolinha = createUser("cebolinha", "bpm"); cascao = createUser("cascao", "bpm"); } @Override @After public void after() throws Exception { deleteUsers(cebolinha, cascao); super.after(); } private ProcessDefinition getSimpleProcess(final String ACTOR_NAME, final String processName, final String processVersion, final boolean terminateEnd) throws Exception { final Expression clientNumberExpr = new ExpressionBuilder().createConstantIntegerExpression(10); final Expression protocolNumberExpr = new ExpressionBuilder().createConstantIntegerExpression(305020); final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(processName, processVersion); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addShortTextData("firstName", null); processDefBuilder.addShortTextData("lastName", null); processDefBuilder.addShortTextData("calledProcessData", null); processDefBuilder.addIntegerData("clientNumber", clientNumberExpr); processDefBuilder.addIntegerData("protocolNumber", protocolNumberExpr); processDefBuilder.addStartEvent("tStart"); processDefBuilder.addUserTask("tStep1", ACTOR_NAME); final EndEventDefinitionBuilder endEvent = processDefBuilder.addEndEvent("tEnd"); if (terminateEnd) { endEvent.addTerminateEventTrigger(); } processDefBuilder.addTransition("tStart", "tStep1"); processDefBuilder.addTransition("tStep1", "tEnd"); final ProcessDefinition targetProcessDefinition = deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, cebolinha); return targetProcessDefinition; } private ProcessDefinition buildProcessWithCallActivity(final boolean addInputOperations, final boolean addOutputOperations, final String processName, final String targetProcessName, final int loopNb, final String strTargetVersion) throws Exception { final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression(targetProcessName); Expression targetProcessVersionExpr = null; if (strTargetVersion != null) { targetProcessVersionExpr = new ExpressionBuilder().createConstantStringExpression(strTargetVersion); } final Expression expressionTrue = new ExpressionBuilder().createConstantBooleanExpression(true); final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(processName, PROCESS_VERSION); processDefBuilder.addShortTextData("fName", null); processDefBuilder.addShortTextData("lName", null); processDefBuilder.addIntegerData("cNumber", null); processDefBuilder.addIntegerData("pNumber", null); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addStartEvent("start"); final CallActivityBuilder callActivityBuilder = processDefBuilder.addCallActivity("callActivity", targetProcessNameExpr, targetProcessVersionExpr); addDataInputOperationsIfNeed(addInputOperations, callActivityBuilder); callActivityBuilder.addShortTextData("callActivityData", new ExpressionBuilder().createConstantStringExpression("defaultValue")); if (loopNb > 0) { callActivityBuilder.addLoop(false, expressionTrue, new ExpressionBuilder().createConstantIntegerExpression(loopNb)); } addDataOutputOperationIfNeed(addOutputOperations, callActivityBuilder); processDefBuilder.addUserTask("step1", ACTOR_NAME); processDefBuilder.addEndEvent("end"); processDefBuilder.addTransition("start", "callActivity"); processDefBuilder.addTransition("callActivity", "step1"); processDefBuilder.addTransition("step1", "end"); return deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, cascao); } private ProcessDefinition buildProcessWithCallActivity(final String processName, final String targetProcessName, final String strTargetVersion) throws Exception { return buildProcessWithCallActivity(false, false, processName, targetProcessName, 0, strTargetVersion); } private ProcessDefinition buildProcessWithCallActivity(final String processName, final String targetProcessName, int loopNb, final String strTargetVersion) throws Exception { return buildProcessWithCallActivity(false, false, processName, targetProcessName, loopNb, strTargetVersion); } private void addDataOutputOperationIfNeed(final boolean addOutputOperations, final CallActivityBuilder callActivityBuilder) throws InvalidExpressionException { if (addOutputOperations) { final Operation setClientNumber = BuildTestUtil.buildAssignOperation("cNumber", "clientNumber", ExpressionType.TYPE_VARIABLE, Integer.class.getName()); final Operation setProtocalNumber = BuildTestUtil.buildAssignOperation("pNumber", "protocolNumber", ExpressionType.TYPE_VARIABLE, Integer.class.getName()); callActivityBuilder.addDataOutputOperation(setClientNumber); callActivityBuilder.addDataOutputOperation(setProtocalNumber); } } private void addDataInputOperationsIfNeed(final boolean addInputOperations, final CallActivityBuilder callActivityBuilder) throws InvalidExpressionException { if (addInputOperations) { final Operation setFirstName = BuildTestUtil.buildAssignOperation("firstName", "fName", ExpressionType.TYPE_VARIABLE, String.class.getName()); final Operation setLastName = BuildTestUtil.buildAssignOperation("lastName", "lName", ExpressionType.TYPE_VARIABLE, String.class.getName()); final Operation mapFromCallActivity = BuildTestUtil.buildAssignOperation("calledProcessData", "callActivityData", ExpressionType.TYPE_VARIABLE, String.class.getName()); callActivityBuilder.addDataInputOperation(setFirstName); callActivityBuilder.addDataInputOperation(setLastName); callActivityBuilder.addDataInputOperation(mapFromCallActivity); } } /* * Most simple case : * No Inputs or Outputs for the callActivity * See executeCallAtivityUntilEndOfProcess for details. */ @Test public void callActivity() throws Exception { executeCallAtivityUntilEndOfProcess(false, false, PROCESS_VERSION, false); } /* * Only Inputs for the callActivity * See executeCallAtivityUntilEndOfProcess for details. */ @Test public void callActivityWithDataInputOperations() throws Exception { executeCallAtivityUntilEndOfProcess(true, false, PROCESS_VERSION, false); } /* * Only Outputs for the callActivity * See executeCallAtivityUntilEndOfProcess for details. */ @Test public void callActivityWithDataOutputOperations() throws Exception { executeCallAtivityUntilEndOfProcess(false, true, PROCESS_VERSION, false); } /* * Only Outputs for the callActivity * See executeCallAtivityUntilEndOfProcess for details. */ @Test public void callActivityWithDataOutputOperationsAndTerminateEnd() throws Exception { executeCallAtivityUntilEndOfProcess(false, true, PROCESS_VERSION, true); } /* * Only Outputs for the callActivity * See executeCallAtivityUntilEndOfProcess for details. */ @Test public void callActivityWithDataInputAndOutputOperationsAndVersion2() throws Exception { executeCallAtivityUntilEndOfProcess(true, true, "2.0", false); } @Test public void callActivityAndGatewayAndMessageAndIntermediateEvent() throws Exception { ProcessDefinition mainProcessDefinition = null; ProcessDefinition receiveProcessDefinition = null; ProcessDefinition sendProcessDefinition = null; try { final Expression processVersionExpr = new ExpressionBuilder() .createConstantStringExpression(PROCESS_VERSION); // Main process final ProcessDefinitionBuilder mainProcessDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("Main", PROCESS_VERSION); mainProcessDefinitionBuilder.addActor(ACTOR_NAME); mainProcessDefinitionBuilder.addStartEvent("MainStart").addEndEvent("MainEnd"); mainProcessDefinitionBuilder.addGateway("Gateway1", GatewayType.PARALLEL).addGateway("Gateway2", GatewayType.PARALLEL); // Send callActivity final Expression sendExpr = new ExpressionBuilder().createConstantStringExpression("Send"); mainProcessDefinitionBuilder.addCallActivity("Send", sendExpr, processVersionExpr); // Receive callActivity final Expression receiveExpr = new ExpressionBuilder().createConstantStringExpression("Receive"); mainProcessDefinitionBuilder.addCallActivity("Receive", receiveExpr, processVersionExpr); // Transitions mainProcessDefinitionBuilder.addTransition("MainStart", "Gateway1").addTransition("Gateway1", "Send") .addTransition("Gateway1", "Receive") .addTransition("Send", "Gateway2").addTransition("Receive", "Gateway2") .addTransition("Gateway2", "MainEnd"); mainProcessDefinition = deployAndEnableProcessWithActor(mainProcessDefinitionBuilder.done(), ACTOR_NAME, cascao); // Receive process final ProcessDefinitionBuilder receiveProcessDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("Receive", PROCESS_VERSION); receiveProcessDefinitionBuilder.addActor(ACTOR_NAME); receiveProcessDefinitionBuilder.addStartEvent("ReceiveStart").addEndEvent("ReceiveEnd"); receiveProcessDefinitionBuilder.addUserTask("Read", ACTOR_NAME); final Operation operation = BuildTestUtil.buildAssignOperation("copymsg", "MSG", ExpressionType.TYPE_VARIABLE, String.class.getName()); final IntermediateCatchEventDefinitionBuilder intermediateCatchEvent = receiveProcessDefinitionBuilder .addIntermediateCatchEvent("ReceiveMsg"); intermediateCatchEvent.addMessageEventTrigger("ping").addOperation(operation); intermediateCatchEvent.addLongTextData("copymsg", null); // Transitions receiveProcessDefinitionBuilder.addTransition("ReceiveStart", "ReceiveMsg") .addTransition("ReceiveMsg", "Read").addTransition("Read", "ReceiveEnd"); receiveProcessDefinition = deployAndEnableProcessWithActor(receiveProcessDefinitionBuilder.done(), ACTOR_NAME, cascao); // Send process final ProcessDefinitionBuilder sendProcessDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("Send", PROCESS_VERSION); sendProcessDefinitionBuilder.addActor(ACTOR_NAME); sendProcessDefinitionBuilder.addStartEvent("SendStart"); final Expression msgData = new ExpressionBuilder().createConstantStringExpression("data"); sendProcessDefinitionBuilder.addLongTextData("msg", msgData); sendProcessDefinitionBuilder.addUserTask("Step1", ACTOR_NAME); final Expression targetProcess = new ExpressionBuilder().createConstantStringExpression("Receive"); final Expression targetFlowNode = new ExpressionBuilder().createConstantStringExpression("ReceiveMsg"); final Expression displayName = new ExpressionBuilder().createConstantStringExpression("MSG"); final Expression messageContent = new ExpressionBuilder().createDataExpression("msg", "java.lang.String"); sendProcessDefinitionBuilder.addEndEvent("SendMsgEnd") .addMessageEventTrigger("ping", targetProcess, targetFlowNode) .addMessageContentExpression(displayName, messageContent); // Transitions sendProcessDefinitionBuilder.addTransition("SendStart", "Step1").addTransition("Step1", "SendMsgEnd"); sendProcessDefinition = deployAndEnableProcessWithActor(sendProcessDefinitionBuilder.done(), ACTOR_NAME, cascao); assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L); final ProcessInstance mainProcessInstance = getProcessAPI().startProcess(cascao.getId(), mainProcessDefinition.getId()); // No need to verify anything, if no exception, query exists getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, mainProcessInstance.getId()) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.CALL_ACTIVITY).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, mainProcessInstance.getId()) .filter(ProcessSupervisorSearchDescriptor.USER_ID, cascao.getId()).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.CALL_ACTIVITY) .filter(ProcessSupervisorSearchDescriptor.USER_ID, cascao.getId()).done()); waitForUserTaskAndExecuteIt("Step1", cascao); getProcessAPI().searchActivities(new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.CALL_ACTIVITY).done()); final ActivityInstance read = waitForUserTaskAndGetIt("Read"); final DataInstance copyMsg = getProcessAPI().getProcessDataInstance("copymsg", read.getParentProcessInstanceId()); assertThat(copyMsg.getValue()).isEqualTo("data"); assignAndExecuteStep(read, cascao.getId()); waitForProcessToFinish(mainProcessInstance); } finally { disableAndDeleteProcess(mainProcessDefinition, receiveProcessDefinition, sendProcessDefinition); } } @Test public void callActivityAndGatewayAndMessageAndTask() throws Exception { ProcessDefinition mainProcessDefinition = null; ProcessDefinition receiveProcessDefinition = null; ProcessDefinition sendProcessDefinition = null; try { final Expression processVersionExpr = new ExpressionBuilder() .createConstantStringExpression(PROCESS_VERSION); // Main process final ProcessDefinitionBuilder mainProcessDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("Main", PROCESS_VERSION); mainProcessDefinitionBuilder.addActor(ACTOR_NAME); mainProcessDefinitionBuilder.addStartEvent("MainStart").addEndEvent("MainEnd"); mainProcessDefinitionBuilder.addGateway("Gateway1", GatewayType.PARALLEL).addGateway("Gateway2", GatewayType.PARALLEL); // Send callActivity final Expression sendExpr = new ExpressionBuilder().createConstantStringExpression("Send"); mainProcessDefinitionBuilder.addCallActivity("Send", sendExpr, processVersionExpr); // Receive callActivity final Expression receiveExpr = new ExpressionBuilder().createConstantStringExpression("Receive"); mainProcessDefinitionBuilder.addCallActivity("Receive", receiveExpr, processVersionExpr); // Transitions mainProcessDefinitionBuilder.addTransition("MainStart", "Gateway1").addTransition("Gateway1", "Send") .addTransition("Gateway1", "Receive") .addTransition("Send", "Gateway2").addTransition("Receive", "Gateway2") .addTransition("Gateway2", "MainEnd"); mainProcessDefinition = deployAndEnableProcessWithActor(mainProcessDefinitionBuilder.done(), ACTOR_NAME, cascao); // Receive process final ProcessDefinitionBuilder receiveProcessDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("Receive", PROCESS_VERSION); receiveProcessDefinitionBuilder.addActor(ACTOR_NAME); receiveProcessDefinitionBuilder.addStartEvent("ReceiveStart").addEndEvent("ReceiveEnd"); receiveProcessDefinitionBuilder.addLongTextData("copymsg", null); receiveProcessDefinitionBuilder.addUserTask("Read", ACTOR_NAME); final Operation operation = BuildTestUtil.buildAssignOperation("copymsg", "MSG2", ExpressionType.TYPE_VARIABLE, String.class.getName()); receiveProcessDefinitionBuilder.addReceiveTask("ReceiveMsg", "ping2").addMessageOperation(operation); // Transitions receiveProcessDefinitionBuilder.addTransition("ReceiveStart", "ReceiveMsg") .addTransition("ReceiveMsg", "Read").addTransition("Read", "ReceiveEnd"); receiveProcessDefinition = deployAndEnableProcessWithActor(receiveProcessDefinitionBuilder.done(), ACTOR_NAME, cascao); // Send process final ProcessDefinitionBuilder sendProcessDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("Send", PROCESS_VERSION); sendProcessDefinitionBuilder.addActor(ACTOR_NAME); sendProcessDefinitionBuilder.addStartEvent("SendStart"); final Expression msgData = new ExpressionBuilder().createConstantStringExpression("data"); sendProcessDefinitionBuilder.addLongTextData("msg", msgData); sendProcessDefinitionBuilder.addUserTask("Step1", ACTOR_NAME); final Expression targetProcess = new ExpressionBuilder().createConstantStringExpression("Receive"); final Expression targetFlowNode = new ExpressionBuilder().createConstantStringExpression("ReceiveMsg"); final Expression displayName = new ExpressionBuilder().createConstantStringExpression("MSG2"); final Expression messageContent = new ExpressionBuilder().createDataExpression("msg", "java.lang.String"); sendProcessDefinitionBuilder.addSendTask("SendMsgEnd", "ping2", targetProcess) .setTargetFlowNode(targetFlowNode) .addMessageContentExpression(displayName, messageContent); // Transitions sendProcessDefinitionBuilder.addTransition("SendStart", "Step1").addTransition("Step1", "SendMsgEnd"); sendProcessDefinition = deployAndEnableProcessWithActor(sendProcessDefinitionBuilder.done(), ACTOR_NAME, cascao); assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L); final ProcessInstance mainProcessInstance = getProcessAPI().startProcess(cascao.getId(), mainProcessDefinition.getId()); waitForUserTaskAndExecuteIt("Step1", cascao); final ActivityInstance read = waitForUserTaskAndGetIt("Read"); final DataInstance copyMsg = getProcessAPI().getProcessDataInstance("copymsg", read.getParentProcessInstanceId()); assertThat(copyMsg.getValue()).isEqualTo("data"); assignAndExecuteStep(read, cascao.getId()); waitForProcessToFinish(mainProcessInstance); } finally { disableAndDeleteProcess(mainProcessDefinition, receiveProcessDefinition, sendProcessDefinition); } } /* * Create a call activity which map some data between child and parent. * Execute an operation using one of this data. * -> operation must be executed after the data mapping is executed */ @Test public void callActivityWithDataOutputAndOperationAreExecutedInTheGoodOrder() throws Exception { final ProcessDefinition targetProcessDef = getSimpleProcess(ACTOR_NAME, "targetProcess", PROCESS_VERSION, false); final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression("targetProcess"); final Expression targetProcessVersionExpr = new ExpressionBuilder() .createConstantStringExpression(PROCESS_VERSION); final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithCallActivity", PROCESS_VERSION); processDefBuilder.addIntegerData("cNumber", new ExpressionBuilder().createConstantIntegerExpression(125)); processDefBuilder.addIntegerData("pNumber", null); processDefBuilder.addIntegerData("dataInitWithCNumber", new ExpressionBuilder().createConstantIntegerExpression(12)); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addStartEvent("start"); final CallActivityBuilder callActivityBuilder = processDefBuilder.addCallActivity("callActivity", targetProcessNameExpr, targetProcessVersionExpr); addDataOutputOperationIfNeed(true, callActivityBuilder); callActivityBuilder.addOperation(new OperationBuilder().createSetDataOperation("dataInitWithCNumber", new ExpressionBuilder().createDataExpression("cNumber", Integer.class.getName()))); processDefBuilder.addUserTask("step1", ACTOR_NAME); processDefBuilder.addEndEvent("end"); processDefBuilder.addTransition("start", "callActivity"); processDefBuilder.addTransition("callActivity", "step1"); processDefBuilder.addTransition("step1", "end"); final ProcessDefinition callingProcessDef = deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, cascao); final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId()); // execute process until step1 waitForUserTaskAndExecuteIt(callingProcessInstance, "tStep1", cebolinha); waitForUserTask(callingProcessInstance, "step1"); /* * check the data have the value of the clientNumber of the called process * the set of data should be like this: * targetProcess:clientNumber(10) -> callingProcess:cNumber(was 125) * callingProcess:cNumber(10) -> callingProcess:dataInitWithCNumber(was 12) */ assertThat(getProcessAPI().getProcessDataInstance("dataInitWithCNumber", callingProcessInstance.getId()) .getValue()).isEqualTo(Integer.valueOf(10)); disableAndDeleteProcess(callingProcessDef, targetProcessDef); } /* * 1 parent process : callingProcess : startEvent(start) -> callActivity -> userTask(step1) -> endEvent(end). * 1 called process : targetProcess : startEvent(tStart) -> userTask(tStep1) -> endEvent(tEnd). * checks : No instances of a process exist, 2 process instances (callingProcess starts and set off targetProcess of * call activity), * callingProcess stopped on the call activity, targetProcess is executing, callingProcess is the root process of * targetProcess, * call activity called the callProcess, callingProcess continues and reaches user task, targetProcess is finished, * the user task is * the one from the callingProcess, calling Process is finished. */ private void executeCallAtivityUntilEndOfProcess(final boolean addInputOperations, final boolean addOutputOperations, final String strTargetVersion, final boolean terminateEnd) throws Exception { final ProcessDefinition targetProcessDef1 = getSimpleProcess(ACTOR_NAME, "targetProcess", PROCESS_VERSION, terminateEnd); final ProcessDefinition targetProcessDef3 = getSimpleProcess(ACTOR_NAME, "targetProcess", "3.0", terminateEnd); final ProcessDefinition targetProcessDef2 = getSimpleProcess(ACTOR_NAME, "targetProcess", "2.0", terminateEnd); final ProcessDefinition callingProcessDef = buildProcessWithCallActivity(addInputOperations, addOutputOperations, "callingProcess", "targetProcess", 0, strTargetVersion); assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L); final List operations = getStartOperations(); final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId(), operations, null); if (strTargetVersion != null && strTargetVersion.contentEquals("4.0")) { checkNbOfProcessInstances(2, ProcessInstanceCriterion.NAME_DESC); final List processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_DESC); assertThat(processInstances).hasSize(1); waitForFlowNodeInFailedState(callingProcessInstance, "callActivity"); return; } checkNbOfProcessInstances(2, ProcessInstanceCriterion.NAME_DESC); final List processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_DESC); assertThat(processInstances).hasSize(2); // two instances are expected calling and target processes final ProcessInstance targetPI = processInstances.get(0); // System.out.println("Version of the definition of targetProcess : " // + getProcessAPI().getProcessDefinition(targetPI.getProcessDefinitionId()).getVersion()); if (strTargetVersion == null || strTargetVersion.contentEquals("2.0")) { assertThat(targetPI.getProcessDefinitionId()).isEqualTo(targetProcessDef2.getId()); } else if (strTargetVersion.contentEquals(PROCESS_VERSION)) { assertThat(targetPI.getProcessDefinitionId()).isEqualTo(targetProcessDef1.getId()); } final FlowNodeInstance callActivityInstance = waitForFlowNodeInExecutingState(callingProcessInstance, "callActivity", true); assertThat(targetPI.getRootProcessInstanceId()).isEqualTo(callingProcessInstance.getId()); assertThat(targetPI.getCallerId()).isEqualTo(callActivityInstance.getId()); ActivityInstance activityInstance = waitForUserTaskAndGetIt(callingProcessInstance, "tStep1"); assertThat(activityInstance.getParentProcessInstanceId()).isEqualTo(targetPI.getId()); assertThat(activityInstance.getRootContainerId()).isEqualTo(callingProcessInstance.getId()); checkDataInputOperations(addInputOperations, targetPI); // execute step in the target process assignAndExecuteStep(activityInstance, cebolinha.getId()); activityInstance = waitForUserTaskAndGetIt(callingProcessInstance, "step1"); checkOutputOperations(addOutputOperations, callingProcessInstance); waitForProcessToFinish(targetPI); assertThat(activityInstance.getParentProcessInstanceId()).isEqualTo(callingProcessInstance.getId()); assignAndExecuteStep(activityInstance, cascao.getId()); waitForProcessToFinish(callingProcessInstance); disableAndDeleteProcess(callingProcessDef, targetProcessDef1, targetProcessDef2, targetProcessDef3); } private List getStartOperations() throws InvalidExpressionException { final ArrayList operations = new ArrayList<>(2); operations.add(BuildTestUtil.buildAssignOperation("fName", "Fulano", ExpressionType.TYPE_CONSTANT, String.class.getName())); operations.add(BuildTestUtil.buildAssignOperation("lName", "de Tal", ExpressionType.TYPE_CONSTANT, String.class.getName())); return operations; } private void checkOutputOperations(final boolean addOutputOperations, final ProcessInstance callingProcessInstance) throws Exception { if (addOutputOperations) { final DataInstance clientNumberData = getProcessAPI().getProcessDataInstance("cNumber", callingProcessInstance.getId()); final DataInstance protocolNumberData = getProcessAPI().getProcessDataInstance("pNumber", callingProcessInstance.getId()); assertThat(clientNumberData.getValue()).isEqualTo(10); assertThat(protocolNumberData.getValue()).isEqualTo(305020); } } private void checkDataInputOperations(final boolean addInputOperations, final ProcessInstance targetPI) throws DataNotFoundException { if (addInputOperations) { final DataInstance firstNameData = getProcessAPI().getProcessDataInstance("firstName", targetPI.getId()); final DataInstance lastNameData = getProcessAPI().getProcessDataInstance("lastName", targetPI.getId()); final DataInstance calledProcessData = getProcessAPI().getProcessDataInstance("calledProcessData", targetPI.getId()); assertThat(firstNameData.getValue()).isEqualTo("Fulano"); assertThat(lastNameData.getValue()).isEqualTo("de Tal"); assertThat(calledProcessData.getValue()).isEqualTo("defaultValue"); } } private void variableMultiLevelCallActivity(final int nbLevel) throws Exception { final ProcessDefinition[] processDefLevels = new ProcessDefinition[nbLevel]; for (int i = 0; i < nbLevel - 1; i++) { processDefLevels[i] = buildProcessWithCallActivity("processLevel" + i, "processLevel" + (i + 1), PROCESS_VERSION); } processDefLevels[nbLevel - 1] = getSimpleProcess(ACTOR_NAME, "processLevel" + (nbLevel - 1), PROCESS_VERSION, false); final List operations = getStartOperations(); assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L); final ProcessInstance[] procInstLevels = new ProcessInstance[nbLevel]; procInstLevels[0] = getProcessAPI().startProcess(processDefLevels[0].getId(), operations, null); checkNbOfProcessInstances(nbLevel, ProcessInstanceCriterion.NAME_DESC); final List processInstances = getProcessAPI().getProcessInstances(0, nbLevel, ProcessInstanceCriterion.NAME_ASC); // if nbLevel>10 use assertThat(processInstances).hasSize(nbLevel); for (int i = 0; i < nbLevel; i++) { procInstLevels[i] = processInstances.get(i); assertThat(procInstLevels[i].getProcessDefinitionId()).as("Level of process : " + i) .isEqualTo(processDefLevels[i].getId()); } waitForUserTaskAndExecuteIt(procInstLevels[0], "tStep1", cebolinha); waitForProcessToFinish(procInstLevels[nbLevel - 1]); for (int i = nbLevel - 2; i >= 0; i--) { waitForUserTaskAndExecuteIt(procInstLevels[0], "step1", cascao); waitForProcessToFinish(procInstLevels[i]); } disableAndDeleteProcess(processDefLevels); } /* * Tested until 200, works ! */ @Test public void multiLevelCallActivity() throws Exception { variableMultiLevelCallActivity(10); } @Test public void callUndeployedProcess() throws Exception { final ProcessDefinition processDef = buildProcessWithCallActivity("callingProcess", "unDeployedProcess", PROCESS_VERSION); assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L); final ProcessInstance processInstance = getProcessAPI().startProcess(processDef.getId()); final List processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_DESC); assertThat(processInstances).hasSize(1); final ActivityInstance waitForTaskToFail = waitForTaskToFail(processInstance); assertThat(waitForTaskToFail.getState()).isEqualTo(TestStates.FAILED.getStateName()); disableAndDeleteProcess(processDef); } private void callActivityInALoop(final int nbLoop) throws Exception { final ProcessDefinition targetProcessDef = getSimpleProcess(ACTOR_NAME, "targetProcess", PROCESS_VERSION, false); final ProcessDefinition callingProcessDef = buildProcessWithCallActivity("callingProcess", "targetProcess", nbLoop, PROCESS_VERSION); assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0); final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId(), null, null); waitForFlowNodeInExecutingState(callingProcessInstance, "callActivity", true); for (int i = 0; i < nbLoop; i++) { long tStep1 = waitForUserTask("tStep1"); FlowNodeInstance flowNodeInstance = getProcessAPI().getFlowNodeInstance(tStep1); assignAndExecuteStep(tStep1, cebolinha); waitForProcessToFinish(flowNodeInstance.getParentProcessInstanceId()); } waitForUserTaskAndExecuteIt(callingProcessInstance, "step1", cascao); waitForProcessToFinish(callingProcessInstance); disableAndDeleteProcess(callingProcessDef, targetProcessDef); } /* * Tested until 200, works ! * Don't use 0 as argument though. */ @Test public void callActivityInALoop() throws Exception { callActivityInALoop(10); } private ProcessInstance getTargetProcessInstance(final ProcessDefinition targetProcessDef) throws Exception { final List processInstances = checkNbOfProcessInstances(2, ProcessInstanceCriterion.NAME_DESC) .getResult(); assertThat(processInstances).hasSize(2);// two instances are expected calling and target processes final ProcessInstance targetPI = processInstances.get(0); assertThat(targetPI.getProcessDefinitionId()).isEqualTo(targetProcessDef.getId()); return targetPI; } @Test public void getArchivedCallActivityInstance() throws Exception { final ProcessDefinition targetProcessDef = getSimpleProcess(ACTOR_NAME, "targetProcess", PROCESS_VERSION, false); final ProcessDefinition callingProcessDef = buildProcessWithCallActivity("callingProcess", "targetProcess", PROCESS_VERSION); assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L); final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId()); final ProcessInstance targetPI = getTargetProcessInstance(targetProcessDef); final FlowNodeInstance callActivityInstance = waitForFlowNodeInExecutingState(callingProcessInstance, "callActivity", true); waitForUserTaskAndExecuteIt(callingProcessInstance, "tStep1", cebolinha); // first loop execution waitForProcessToFinish(targetPI); final WaitForFinalArchivedActivity waitForFinalArchivedActivity = waitForFinalArchivedActivity("callActivity", callingProcessInstance); assertThat(waitForFinalArchivedActivity.getResult().getType()).isEqualTo(FlowNodeType.CALL_ACTIVITY); final List archivedProcessInstanceList = getProcessAPI() .getArchivedProcessInstances(targetPI.getId(), 0, 20); final ArchivedProcessInstance firstProcessInstanceArchive = archivedProcessInstanceList.get(0); assertThat(firstProcessInstanceArchive.getRootProcessInstanceId()).isEqualTo(callingProcessInstance.getId()); assertThat(firstProcessInstanceArchive.getCallerId()).isEqualTo(callActivityInstance.getId()); disableAndDeleteProcess(callingProcessDef, targetProcessDef); } @Test public void callActivityUsingLastestVersion() throws Exception { executeCallAtivityUntilEndOfProcess(false, false, null, false); } @Test public void callActivityUsingUndeployedVersion() throws Exception { final ProcessDefinition callingProcessDef = buildProcessWithCallActivity("callingProcess", "targetProcess", "unexisting_version_4.0"); final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId()); final ActivityInstance failedTask = waitForTaskToFail(callingProcessInstance); assertThat(failedTask.getName()).isEqualTo("callActivity"); disableAndDeleteProcess(callingProcessDef); } @Test public void callActivityUsingUndeployedProcess() throws Exception { final ProcessDefinition callingProcessDef = buildProcessWithCallActivity("callingProcess", "targetProcess", null); final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId()); final ActivityInstance failedTask = waitForTaskToFail(callingProcessInstance); assertThat(failedTask.getName()).isEqualTo("callActivity"); disableAndDeleteProcess(callingProcessDef); } @Test public void callActivityUsingDisabledProcess() throws Exception { final ProcessDefinition targetProcessDef = getSimpleProcess(ACTOR_NAME, "targetProcess", PROCESS_VERSION, false); getProcessAPI().disableProcess(targetProcessDef.getId()); final ProcessDefinition callingProcessDef = buildProcessWithCallActivity("callingProcess", "targetProcess", PROCESS_VERSION); final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId()); final ActivityInstance failedTask = waitForTaskToFail(callingProcessInstance); assertThat(failedTask.getName()).isEqualTo("callActivity"); deleteProcess(targetProcessDef.getId()); disableAndDeleteProcess(callingProcessDef); } @Test public void deleteProcessInstanceThatIsCalledByCallActivity() throws Exception { final ProcessDefinition targetProcessDef1 = getSimpleProcess(ACTOR_NAME, "targetProcess", PROCESS_VERSION, false); final ProcessDefinition callingProcessDef = buildProcessWithCallActivity("callingProcess", "targetProcess", PROCESS_VERSION); assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L); final List operations = getStartOperations(); final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId(), operations, null); final ActivityInstance tStep1 = waitForUserTaskAndGetIt(callingProcessInstance, "tStep1"); try { getProcessAPI().deleteProcessInstance(tStep1.getParentProcessInstanceId()); fail("Should not be able to delete process instance that is called by an other process"); } catch (final ProcessInstanceHierarchicalDeletionException e) { getProcessAPI().deleteProcessInstance(e.getProcessInstanceId()); // should work now try { getProcessAPI().getProcessInstance(tStep1.getParentProcessInstanceId()); fail("process should be deleted"); } catch (final ProcessInstanceNotFoundException e1) { // ok } } finally { disableAndDeleteProcess(callingProcessDef, targetProcessDef1); } } @Test(expected = DeletionException.class) public void deleteProcessDefinitionWithProcessInstanceThatIsCalledByCallActivity() throws Exception { final ProcessDefinition targetProcessDef1 = getSimpleProcess(ACTOR_NAME, "targetProcess", PROCESS_VERSION, false); final ProcessDefinition callingProcessDef = buildProcessWithCallActivity("callingProcess", "targetProcess", PROCESS_VERSION); assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L); final List operations = getStartOperations(); final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId(), operations, null); waitForUserTask(callingProcessInstance, "tStep1"); getProcessAPI().disableProcess(targetProcessDef1.getId()); try { deleteProcess(targetProcessDef1); fail("Should not be able to delete process instance that is called by an other process"); } finally { disableAndDeleteProcess(callingProcessDef); deleteProcess(targetProcessDef1); } } @Test public void callActivityWithDependencies() throws Exception { ProcessDefinition targetProcessDef = null; ProcessDefinition callingProcessDef = null; try { targetProcessDef = getSimpleProcess(ACTOR_NAME, "targetProcess", PROCESS_VERSION, false); final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression("targetProcess"); final Expression vExpression = new ExpressionBuilder().createDataExpression("v", String.class.getName()); final List dependencies = new ArrayList<>(1); dependencies.add(vExpression); final Expression targetProcessVersionExpr = new ExpressionBuilder().createGroovyScriptExpression("version", "v", String.class.getName(), dependencies); final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("callingProcess", PROCESS_VERSION); processDefBuilder.addShortTextData("v", new ExpressionBuilder().createConstantStringExpression(PROCESS_VERSION)); processDefBuilder.addShortTextData("lName", null); processDefBuilder.addIntegerData("cNumber", null); processDefBuilder.addIntegerData("pNumber", null); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addStartEvent("start"); processDefBuilder.addCallActivity("callActivity", targetProcessNameExpr, targetProcessVersionExpr); processDefBuilder.addUserTask("step1", ACTOR_NAME); processDefBuilder.addEndEvent("end"); processDefBuilder.addTransition("start", "callActivity"); processDefBuilder.addTransition("callActivity", "step1"); processDefBuilder.addTransition("step1", "end"); callingProcessDef = deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, cascao); assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L); final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId(), null, null); checkNbOfProcessInstances(2, ProcessInstanceCriterion.NAME_DESC); final List processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_DESC); assertThat(processInstances).hasSize(2); // two instances are expected calling and target process final ProcessInstance targetPI = processInstances.get(0); assertThat(targetProcessDef.getId()).isEqualTo(targetPI.getProcessDefinitionId()); assertThat(targetProcessDef.getVersion()).isEqualTo(PROCESS_VERSION); final FlowNodeInstance callActivityInstance = waitForFlowNodeInExecutingState(callingProcessInstance, "callActivity", true); assertThat(targetPI.getRootProcessInstanceId()).isEqualTo(callingProcessInstance.getId()); assertThat(targetPI.getCallerId()).isEqualTo(callActivityInstance.getId()); final HumanTaskInstance tStep1 = waitForUserTaskAndGetIt(callingProcessInstance, "tStep1"); assertThat(tStep1.getParentProcessInstanceId()).isEqualTo(targetPI.getId()); assertThat(tStep1.getRootContainerId()).isEqualTo(callingProcessInstance.getId()); // execute step in the target process assignAndExecuteStep(tStep1, cebolinha.getId()); final HumanTaskInstance step1 = waitForUserTaskAndGetIt(callingProcessInstance, "step1"); waitForProcessToFinish(targetPI); assertThat(step1.getParentProcessInstanceId()).isEqualTo(callingProcessInstance.getId()); assignAndExecuteStep(step1, cascao.getId()); waitForProcessToFinish(callingProcessInstance); } finally { disableAndDeleteProcess(callingProcessDef, targetProcessDef); } } @Test public void callActivityCheckAttributes() throws Exception { ProcessDefinition targetProcessDef = null; ProcessDefinition callingProcessDef = null; try { targetProcessDef = getSimpleProcess(ACTOR_NAME, "targetProcess", PROCESS_VERSION, false); final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression("targetProcess"); final Expression targetProcessVersionExpr = new ExpressionBuilder() .createConstantStringExpression(PROCESS_VERSION); final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("callingProcess", PROCESS_VERSION); processDefBuilder.addShortTextData("lName", null); processDefBuilder.addIntegerData("cNumber", null); processDefBuilder.addIntegerData("pNumber", null); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addStartEvent("start"); processDefBuilder.addCallActivity("callActivity", targetProcessNameExpr, targetProcessVersionExpr) .addDisplayName(new ExpressionBuilder().createConstantStringExpression("callActivityDisplayName")) .addDescription("callActivityDescription") .addDisplayDescription( new ExpressionBuilder().createConstantStringExpression("callActivityDisplayDescription")); processDefBuilder.addUserTask("step1", ACTOR_NAME); processDefBuilder.addEndEvent("end"); processDefBuilder.addTransition("start", "callActivity"); processDefBuilder.addTransition("callActivity", "step1"); processDefBuilder.addTransition("step1", "end"); callingProcessDef = deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, cascao); assertThat(getProcessAPI().getNumberOfProcessInstances()).isEqualTo(0L); final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDef.getId(), null, null); checkNbOfProcessInstances(2, ProcessInstanceCriterion.NAME_DESC); final List processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_DESC); assertThat(processInstances).hasSize(2); // two instances are expected calling and target process final ProcessInstance targetPI = processInstances.get(0); final FlowNodeInstance callActivityInstance = waitForFlowNodeInExecutingState(callingProcessInstance, "callActivity", true); assertThat(targetPI.getRootProcessInstanceId()).isEqualTo(callingProcessInstance.getId()); assertThat(targetPI.getCallerId()).isEqualTo(callActivityInstance.getId()); assertThat(callActivityInstance.getDisplayName()).isEqualTo("callActivityDisplayName"); assertThat(callActivityInstance.getDescription()).isEqualTo("callActivityDescription"); assertThat(callActivityInstance.getDisplayDescription()).isEqualTo("callActivityDisplayDescription"); final HumanTaskInstance tStep1 = waitForUserTaskAndGetIt(callingProcessInstance, "tStep1"); assertThat(tStep1.getParentProcessInstanceId()).isEqualTo(targetPI.getId()); assertThat(tStep1.getRootContainerId()).isEqualTo(callingProcessInstance.getId()); // execute step in the target process assignAndExecuteStep(tStep1, cebolinha.getId()); final HumanTaskInstance step1 = waitForUserTaskAndGetIt(callingProcessInstance, "step1"); waitForProcessToFinish(targetPI); assertThat(step1.getParentProcessInstanceId()).isEqualTo(callingProcessInstance.getId()); assignAndExecuteStep(step1, cascao.getId()); waitForProcessToFinish(callingProcessInstance); } finally { disableAndDeleteProcess(callingProcessDef, targetProcessDef); } } @Test(expected = InvalidProcessDefinitionException.class) public void callActivityTargetProcessExprIsNull() throws Exception { final Expression targetProcessNameExpr = null; final Expression targetProcessVersionExpr = new ExpressionBuilder() .createConstantStringExpression(PROCESS_VERSION); final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("callingProcess", PROCESS_VERSION); processDefBuilder.addShortTextData("lName", null); processDefBuilder.addIntegerData("cNumber", null); processDefBuilder.addIntegerData("pNumber", null); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addStartEvent("start"); processDefBuilder.addCallActivity("callActivity", targetProcessNameExpr, targetProcessVersionExpr); processDefBuilder.addUserTask("step1", ACTOR_NAME); processDefBuilder.addEndEvent("end"); processDefBuilder.addTransition("start", "callActivity"); processDefBuilder.addTransition("callActivity", "step1"); processDefBuilder.addTransition("step1", "end"); deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, cascao); } @Test public void callActivityTargetProcessWithJustHumanTask() throws Exception { ProcessDefinition targetProcessDefinition = null; ProcessDefinition callingProcessDefinition = null; try { // Build target process final ProcessDefinitionBuilder targetProcessDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("targetProcess", PROCESS_VERSION); targetProcessDefBuilder.addActor(ACTOR_NAME); targetProcessDefBuilder.addStartEvent("tStart"); targetProcessDefBuilder.addUserTask("tStep1", ACTOR_NAME); targetProcessDefBuilder.addEndEvent("tEnd"); targetProcessDefBuilder.addTransition("tStart", "tStep1"); targetProcessDefBuilder.addTransition("tStep1", "tEnd"); targetProcessDefinition = deployAndEnableProcessWithActor(targetProcessDefBuilder.done(), ACTOR_NAME, cebolinha); // Build and start calling process final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression("targetProcess"); final Expression targetProcessVersionExpr = new ExpressionBuilder() .createConstantStringExpression(PROCESS_VERSION); final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("callingProcess", PROCESS_VERSION); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addStartEvent("start"); processDefBuilder.addCallActivity("callActivity", targetProcessNameExpr, targetProcessVersionExpr) .addDisplayName(new ExpressionBuilder().createConstantStringExpression("callActivityDisplayName")) .addDescription("callActivityDescription") .addDisplayDescription( new ExpressionBuilder().createConstantStringExpression("callActivityDisplayDescription")); processDefBuilder.addEndEvent("end"); processDefBuilder.addTransition("start", "callActivity"); processDefBuilder.addTransition("callActivity", "end"); callingProcessDefinition = deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, cascao); final ProcessInstance callingProcessInstance = getProcessAPI() .startProcess(callingProcessDefinition.getId()); // Execute step in the target process waitForUserTaskAndExecuteIt(callingProcessInstance, "tStep1", cebolinha); waitForProcessToFinish(callingProcessInstance); // Search archived process assertThat(getProcessAPI().getNumberOfArchivedProcessInstances()).isEqualTo(1L); final SearchOptionsBuilder searchOptions = new SearchOptionsBuilder(0, 10); searchOptions.sort(ArchivedProcessInstancesSearchDescriptor.NAME, Order.ASC); final List archivedProcessInstances = getProcessAPI() .searchArchivedProcessInstances(searchOptions.done()).getResult(); assertThat(archivedProcessInstances).hasSize(1); assertThat(archivedProcessInstances.get(0).getSourceObjectId()).isEqualTo(callingProcessInstance.getId()); } finally { disableAndDeleteProcess(callingProcessDefinition, targetProcessDefinition); } } @Test public void getProcessDefinitionIdFromActivityInstanceId() throws Exception { // check that real root process definition is retrieved (taken from parent process instance) // Build target process final ProcessDefinitionBuilder targetProcessDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("targetProcess", PROCESS_VERSION); targetProcessDefBuilder.addActor(ACTOR_NAME); targetProcessDefBuilder.addStartEvent("tStart"); targetProcessDefBuilder.addUserTask("tStep1", ACTOR_NAME); targetProcessDefBuilder.addEndEvent("tEnd"); targetProcessDefBuilder.addTransition("tStart", "tStep1"); targetProcessDefBuilder.addTransition("tStep1", "tEnd"); final ProcessDefinition targetProcessDefinition = deployAndEnableProcessWithActor( targetProcessDefBuilder.done(), ACTOR_NAME, cebolinha); // Build and start calling process final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression("targetProcess"); final Expression targetProcessVersionExpr = new ExpressionBuilder() .createConstantStringExpression(PROCESS_VERSION); final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("callingProcess", PROCESS_VERSION); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addStartEvent("start"); processDefBuilder.addCallActivity("callActivity", targetProcessNameExpr, targetProcessVersionExpr) .addDisplayName(new ExpressionBuilder().createConstantStringExpression("callActivityDisplayName")) .addDescription("callActivityDescription") .addDisplayDescription( new ExpressionBuilder().createConstantStringExpression("callActivityDisplayDescription")); processDefBuilder.addEndEvent("end"); processDefBuilder.addTransition("start", "callActivity"); processDefBuilder.addTransition("callActivity", "end"); final ProcessDefinition callingProcessDefinition = deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, cascao); final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDefinition.getId()); final long tStep1Id = waitForUserTask(callingProcessInstance.getId(), "tStep1"); final long processDefinitionId = getProcessAPI().getProcessDefinitionIdFromActivityInstanceId(tStep1Id); assertThat(processDefinitionId).isEqualTo(targetProcessDefinition.getId()); disableAndDeleteProcess(callingProcessDefinition, targetProcessDefinition); } @Test public void callActivityWithTaskUsingEngineExpressions() throws Exception { ProcessDefinition targetProcessDefinition = null; ProcessDefinition callingProcessDefinition = null; try { // Build target process final ProcessDefinitionBuilder targetProcessDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("targetProcess", PROCESS_VERSION); targetProcessDefBuilder.addActor(ACTOR_NAME); targetProcessDefBuilder.addStartEvent("tStart"); targetProcessDefBuilder.addUserTask("tStep1", ACTOR_NAME).addData("rootProcId", Long.class.getName(), new ExpressionBuilder().createEngineConstant(ExpressionConstants.ROOT_PROCESS_INSTANCE_ID)); targetProcessDefBuilder.addEndEvent("tEnd"); targetProcessDefBuilder.addTransition("tStart", "tStep1"); targetProcessDefBuilder.addTransition("tStep1", "tEnd"); targetProcessDefinition = deployAndEnableProcessWithActor(targetProcessDefBuilder.done(), ACTOR_NAME, cebolinha); // Build and start calling process final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression("targetProcess"); final Expression targetProcessVersionExpr = new ExpressionBuilder() .createConstantStringExpression(PROCESS_VERSION); final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("callingProcess", PROCESS_VERSION); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addStartEvent("start"); processDefBuilder.addCallActivity("callActivity", targetProcessNameExpr, targetProcessVersionExpr); processDefBuilder.addEndEvent("end"); processDefBuilder.addTransition("start", "callActivity"); processDefBuilder.addTransition("callActivity", "end"); callingProcessDefinition = deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, cascao); final ProcessInstance callingProcessInstance = getProcessAPI() .startProcess(callingProcessDefinition.getId()); final long tStep1Id = waitForUserTask(callingProcessInstance, "tStep1"); assertThat(getProcessAPI().getActivityDataInstance("rootProcId", tStep1Id).getValue()) .isEqualTo(callingProcessInstance.getId()); } finally { disableAndDeleteProcess(callingProcessDefinition, targetProcessDefinition); } } @Test public void callActivityWithDataMappingAndConnectors() throws Exception { ProcessDefinition targetProcessDefinition = null; ProcessDefinition callingProcessDefinition = null; try { // Build target process final ProcessDefinitionBuilder targetProcessDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("targetProcess", PROCESS_VERSION); targetProcessDefBuilder.addActor(ACTOR_NAME); targetProcessDefBuilder.addData("subProcessData", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("subDefault")); targetProcessDefBuilder.addAutomaticTask("tStep1").addOperation( new OperationBuilder().createSetDataOperation("subProcessData", new ExpressionBuilder().createConstantStringExpression("subModified"))); targetProcessDefinition = deployAndEnableProcessWithActor(targetProcessDefBuilder.done(), ACTOR_NAME, cebolinha); // Build and start calling process final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression("targetProcess"); final Expression targetProcessVersionExpr = new ExpressionBuilder() .createConstantStringExpression(PROCESS_VERSION); final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("callingProcess", PROCESS_VERSION); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addData("parentProcessData", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("parentDefault")); processDefBuilder.addData("valueOnCallOnEnter", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("none")); processDefBuilder.addData("valueOnCallOnFinish", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("none")); final CallActivityBuilder callActivityBuilder = processDefBuilder.addCallActivity("callActivity", targetProcessNameExpr, targetProcessVersionExpr); callActivityBuilder .addConnector("onEnterConnector", "org.bonitasoft.connector.testConnectorWithOutput", PROCESS_VERSION, ConnectorEvent.ON_ENTER) .addInput("input1", new ExpressionBuilder().createDataExpression("parentProcessData", String.class.getName())) .addOutput(new LeftOperandBuilder().createNewInstance().setName("valueOnCallOnEnter").done(), OperatorType.ASSIGNMENT, "=", "", new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME, String.class.getName())); callActivityBuilder .addConnector("onFinishConnector", "org.bonitasoft.connector.testConnectorWithOutput", PROCESS_VERSION, ConnectorEvent.ON_FINISH) .addInput("input1", new ExpressionBuilder().createDataExpression("parentProcessData", String.class.getName())) .addOutput(new LeftOperandBuilder().createNewInstance().setName("valueOnCallOnFinish").done(), OperatorType.ASSIGNMENT, "=", "", new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME, String.class.getName())); callActivityBuilder .addDataOutputOperation(new OperationBuilder().createSetDataOperation("parentProcessData", new ExpressionBuilder().createDataExpression("subProcessData", String.class.getName()))); processDefBuilder.addUserTask("end", ACTOR_NAME); processDefBuilder.addTransition("callActivity", "end"); final BusinessArchiveBuilder bizArchive = new BusinessArchiveBuilder(); bizArchive.createNewBusinessArchive(); bizArchive.setProcessDefinition(processDefBuilder.done()); bizArchive.addConnectorImplementation( new BarResource("TestConnectorWithOutput.impl", IOUtils.toByteArray(CommonAPIIT.class .getResourceAsStream("/org/bonitasoft/engine/connectors/TestConnectorWithOutput.impl")))); bizArchive.addClasspathResource( new BarResource("TestConnectorWithOutput.jar", IOUtil.generateJar(TestConnectorWithOutput.class))); callingProcessDefinition = deployAndEnableProcessWithActor(bizArchive.done(), ACTOR_NAME, cascao); final ProcessInstance callingProcessInstance = getProcessAPI() .startProcess(callingProcessDefinition.getId()); final long endId = waitForUserTask(callingProcessInstance, "end"); assertThat(getProcessAPI().getActivityDataInstance("valueOnCallOnEnter", endId).getValue()) .isEqualTo("parentDefault"); assertThat(getProcessAPI().getActivityDataInstance("valueOnCallOnFinish", endId).getValue()) .isEqualTo("subModified"); } finally { disableAndDeleteProcess(callingProcessDefinition, targetProcessDefinition); } } @Test public void transfert_a_custom_data_and_contract_input_from_a_parent_process_to_a_child_and_vice_versa() throws Exception { final ExpressionBuilder expressionBuilder = new ExpressionBuilder(); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("child", "1.0"); builder.addActor(ACTOR_NAME); builder.addContract().addInput("isOk", Type.BOOLEAN, "isOk is the process contract input"); builder.addBooleanData("dataOk", new ExpressionBuilder().createContractInputExpression("isOk", Boolean.class.getName())); builder.addData("data1", "org.bonitasoft.complextypes.MyType", null); builder.addUserTask("childTask", ACTOR_NAME).addOperation( new OperationBuilder().createJavaMethodOperation("data1", "setName", String.class.getName(), expressionBuilder.createConstantStringExpression("World"))); final BusinessArchive childBA = buildBusinessArchive(builder.done()); builder.createNewInstance("parent", "1.0"); builder.addActor(ACTOR_NAME); builder.addData("data1", "org.bonitasoft.complextypes.MyType", expressionBuilder.createGroovyScriptExpression("initMyType", "import org.bonitasoft.complextypes.MyType; new MyType()", "org.bonitasoft.complextypes.MyType")); builder.addShortTextData("name", null); final CallActivityBuilder callActivityBuilder = builder.addCallActivity("caTask", expressionBuilder.createConstantStringExpression("child"), expressionBuilder.createConstantStringExpression("1.0")); callActivityBuilder.addDataInputOperation( BuildTestUtil.buildAssignOperation("data1", "data1", ExpressionType.TYPE_VARIABLE, "org.bonitasoft.complextypes.MyType")); callActivityBuilder.addProcessStartContractInput("isOk", new ExpressionBuilder().createConstantBooleanExpression(true)); callActivityBuilder.addDataOutputOperation( BuildTestUtil.buildAssignOperation("data1", "data1", ExpressionType.TYPE_VARIABLE, "org.bonitasoft.complextypes.MyType")); final Expression initIndex = expressionBuilder.createGroovyScriptExpression("initIndex", "data1.getName()", String.class.getName(), expressionBuilder.createDataExpression("data1", "org.bonitasoft.complextypes.MyType")); builder.addAutomaticTask("setData") .addOperation(new OperationBuilder().createSetDataOperation("name", initIndex)); builder.addUserTask("parentTask", ACTOR_NAME); builder.addTransition("caTask", "setData"); builder.addTransition("setData", "parentTask"); final BusinessArchive parentBA = buildBusinessArchive(builder.done()); final ProcessDefinition childProcDefinition = deployAndEnableProcessWithActor(childBA, ACTOR_NAME, cascao); final ProcessDefinition parentProcDefinition = deployAndEnableProcessWithActor(parentBA, ACTOR_NAME, cascao); final ProcessInstance instance = getProcessAPI().startProcess(parentProcDefinition.getId()); // Check that contract input has been correctly passed by CallActivity: final long childTask = waitForUserTask("childTask"); final HumanTaskInstance humanTaskInstance = getProcessAPI().getHumanTaskInstance(childTask); final DataInstance dataOk = getProcessAPI().getActivityDataInstance("dataOk", humanTaskInstance.getId()); assertThat(((Boolean) dataOk.getValue())).isTrue(); assignAndExecuteStep(childTask, cascao); waitForUserTask("parentTask"); final DataInstance dataInstance = getProcessAPI().getProcessDataInstance("name", instance.getId()); assertThat(dataInstance.getValue()).isEqualTo("World"); disableAndDeleteProcess(parentProcDefinition, childProcDefinition); } private BusinessArchive buildBusinessArchive(final DesignProcessDefinition process) throws Exception { InputStream inputStream = null; try { inputStream = this.getClass().getResourceAsStream("/org.bonitasoft.complextypes.jar-bak"); final byte[] content = IOUtils.toByteArray(inputStream); final BarResource resource = new BarResource("org.bonitasoft.complextypes.jar", content); final BusinessArchiveBuilder baBuilder = new BusinessArchiveBuilder(); baBuilder.createNewBusinessArchive().setProcessDefinition(process).addClasspathResource(resource); return baBuilder.done(); } finally { if (inputStream != null) { inputStream.close(); } } } @Test public void callActivity_classloader_handling_on_child_process() throws Exception { /* * BS-12674: issue when archiving called process, it's done in the wrong classloader */ ProcessDefinition targetProcessDefinition = null; ProcessDefinition callingProcessDefinition = null; try { // Build target process final ProcessDefinitionBuilder targetProcessDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("targetProcess", PROCESS_VERSION); targetProcessDefBuilder.addActor(ACTOR_NAME); targetProcessDefBuilder.addData("subProcessData", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("subDefault")); targetProcessDefBuilder .addData( "dataActivity", java.lang.Object.class.getName(), new ExpressionBuilder().createGroovyScriptExpression("myScript", "new org.bonitasoft.dfgdfg.Restaurant()", java.lang.Object.class.getName())); targetProcessDefBuilder.addAutomaticTask("tStep1").addOperation( new OperationBuilder().createSetDataOperation("subProcessData", new ExpressionBuilder().createConstantStringExpression("subModified"))); BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchiveBuilder.setProcessDefinition(targetProcessDefBuilder.done()); //add a jar that is not known from the parent process businessArchiveBuilder .addClasspathResource(getResource("/org.bonitasoft.dfgdfg.bak", "org.bonitasoft.dfgdfg.jar")); targetProcessDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, cebolinha); // Build and start calling process final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression("targetProcess"); final Expression targetProcessVersionExpr = new ExpressionBuilder() .createConstantStringExpression(PROCESS_VERSION); final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("callingProcess", PROCESS_VERSION); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addData("parentProcessData", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("parentDefault")); final CallActivityBuilder callActivityBuilder = processDefBuilder.addCallActivity("callActivity", targetProcessNameExpr, targetProcessVersionExpr); callActivityBuilder .addDataOutputOperation(new OperationBuilder().createSetDataOperation("parentProcessData", new ExpressionBuilder().createDataExpression("subProcessData", String.class.getName()))); processDefBuilder.addUserTask("end", ACTOR_NAME); processDefBuilder.addTransition("callActivity", "end"); final BusinessArchiveBuilder bizArchive = new BusinessArchiveBuilder(); bizArchive.createNewBusinessArchive(); bizArchive.setProcessDefinition(processDefBuilder.done()); callingProcessDefinition = deployAndEnableProcessWithActor(bizArchive.done(), ACTOR_NAME, cascao); final ProcessInstance callingProcessInstance = getProcessAPI() .startProcess(callingProcessDefinition.getId()); waitForUserTask(callingProcessInstance, "end"); } finally { disableAndDeleteProcess(callingProcessDefinition); disableAndDeleteProcess(targetProcessDefinition); } } @Test public void should_abort_call_activity_when_sub_process_is_triggered() throws Exception { ProcessDefinition mainProcess = getProcessAPI() .deployAndEnableProcess( new ProcessDefinitionBuilder().createNewInstance("processWithCallActivityAndTimerESB", "1.0") .addStartEvent("start") .addCallActivity("call", new ExpressionBuilder().createConstantStringExpression("calledProcess"), new ExpressionBuilder().createConstantStringExpression("1.0")) .addTransition("start", "call") .getProcess()); ProcessDefinition calledProcess = getProcessAPI() .deployAndEnableProcess(new ProcessDefinitionBuilder().createNewInstance("calledProcess", "1.0") //not the normal flow: no elements started .addStartEvent("signalStart").addSignalEventTrigger("calledProcSignal") .addAutomaticTask("auto2") .addTransition("signalStart", "auto2").getProcess()); ProcessInstance processInstance = getProcessAPI().startProcess(mainProcess.getId()); waitForProcessToBeInState(processInstance, ProcessInstanceState.COMPLETED); disableAndDeleteProcess(mainProcess, calledProcess); } @Test public void should_be_able_to_cancel_process_with_call_activity_calling_unknown_process() throws Exception { //given ProcessDefinition processDefinition = getProcessAPI() .deployAndEnableProcess( new ProcessDefinitionBuilder().createNewInstance("processThatCallUnknownProcess", "1.0") .addStartEvent("start") .addCallActivity("call", new ExpressionBuilder().createConstantStringExpression("unknownProcess"), new ExpressionBuilder().createConstantStringExpression("1.0")) .addTransition("start", "call") .getProcess()); //when ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); ActivityInstance activityInstance = waitForTaskToFail(processInstance); assertThat(activityInstance.getName()).isEqualTo("call"); getProcessAPI().cancelProcessInstance(processInstance.getId()); waitForProcessToBeInState(processInstance, ProcessInstanceState.CANCELLED); //then try { getProcessAPI().getProcessInstance(processInstance.getId()); fail("process should not exist anymore"); } catch (ProcessInstanceNotFoundException ignored) { } //verify the call activity was archived in cancelled state: SearchResult archivedFlowNodeInstances = getProcessAPI() .searchArchivedFlowNodeInstances(new SearchOptionsBuilder(0, 10) .filter(ArchivedFlowNodeInstanceSearchDescriptor.NAME, "call") .filter(ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL, true).done()); assertThat(archivedFlowNodeInstances.getResult()).hasSize(1); assertThat(archivedFlowNodeInstances.getResult().get(0).getState()).isEqualTo("cancelled"); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/ContractIT.java ================================================ /** * 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.activity; import static org.assertj.core.api.Assertions.*; import java.io.IOException; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.contract.ConstraintDefinition; import org.bonitasoft.engine.bpm.contract.ContractDefinition; import org.bonitasoft.engine.bpm.contract.ContractViolationException; import org.bonitasoft.engine.bpm.contract.FileInputValue; import org.bonitasoft.engine.bpm.contract.InputDefinition; import org.bonitasoft.engine.bpm.contract.Type; import org.bonitasoft.engine.bpm.contract.impl.InputDefinitionImpl; import org.bonitasoft.engine.bpm.data.ArchivedDataInstance; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.flownode.FlowNodeExecutionException; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.UserTaskNotFoundException; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ContractDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.connectors.TestConnectorWithAPICall; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.operation.OperationBuilder; import org.junit.After; import org.junit.Before; import org.junit.Test; public class ContractIT extends CommonAPIIT { private static final String TASK2 = "task2"; private static final String TASK1 = "task1"; private User matti; @Before public void beforeTest() throws BonitaException { loginWithTechnicalUser(); matti = createUser("matti", "bpm"); logout(); loginOnDefaultTenantWith(matti.getUserName(), "bpm"); } @After public void afterTest() throws BonitaException { deleteUser(matti); logout(); } @Test public void shouldHandleContractInputsAtProcessLevel() throws Exception { //given final String numberOfDaysProcessContractData = "numberOfDaysProcessContractData"; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("contract", "1.0"); // have a initial data value using process input, so that we ensure inputs are treated before data at process instantiation: builder.addData("nbDaysProcessData", Integer.class.getName(), new ExpressionBuilder().createContractInputExpression(numberOfDaysProcessContractData, Integer.class.getName())); builder.addData("idData", Long.class.getName(), new ExpressionBuilder().createContractInputExpression("id", Long.class.getName())); builder.addData("multipleTextData", List.class.getName(), new ExpressionBuilder().createContractInputExpression("multipleText", List.class.getName())); builder.addData("complexData", Map.class.getName(), new ExpressionBuilder().createContractInputExpression("complex", Map.class.getName())); builder.addActor(ACTOR_NAME); builder.addUserTask(TASK1, ACTOR_NAME); final ContractDefinitionBuilder contract = builder.addContract(); contract.addInput(numberOfDaysProcessContractData, Type.INTEGER, null); contract.addInput("id", Type.LONG, null); contract.addInput("multipleText", Type.TEXT, "a multiple text", true); contract.addComplexInput("complex", "a complex input").addInput("text", Type.TEXT, "text in complex"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertThat(processDeploymentInfo.getConfigurationState()).isEqualTo(ConfigurationState.RESOLVED); assertThat(processDeploymentInfo.getActivationState()).isEqualTo(ActivationState.ENABLED); final Map inputs = new HashMap<>(1); final int value = 14; inputs.put(numberOfDaysProcessContractData, value); final ArrayList multiples = new ArrayList<>(Arrays.asList("String1", "String2")); inputs.put("multipleText", multiples); inputs.put("id", 1L); final HashMap map = new HashMap<>(); map.put("text", "textValue"); inputs.put("complex", map); //check exception on contract violation try { getProcessAPI().startProcessWithInputs(processDefinition.getId(), Collections. emptyMap()); fail("Should throw a contract violation exception"); } catch (final ContractViolationException e) { assertThat(e.getMessage()).contains("multipleText", "complex"); } //start with right inputs final ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(processDefinition.getId(), inputs); waitForUserTask(processInstance, TASK1); final DataInstance processDataValueInitializedFromIntInput = getProcessAPI() .getProcessDataInstance("nbDaysProcessData", processInstance.getId()); final DataInstance processDataValueInitializedFromLongInput = getProcessAPI().getProcessDataInstance("idData", processInstance.getId()); assertThat(getProcessAPI().getProcessDataInstance("multipleTextData", processInstance.getId()).getValue()) .isEqualTo(multiples); assertThat(getProcessAPI().getProcessDataInstance("complexData", processInstance.getId()).getValue()) .isEqualTo(map); assertThat(processDataValueInitializedFromIntInput.getValue()).isEqualTo(value); assertThat(processDataValueInitializedFromLongInput.getValue()).isEqualTo(1L); final Serializable processInstanciationInputValue = getProcessAPI().getProcessInputValueAfterInitialization( processInstance.getId(), numberOfDaysProcessContractData); assertThat(processInstanciationInputValue).isEqualTo(value); //clean up disableAndDeleteProcess(processDefinition); } @Test public void should_process_contract_work_with_event_sub_process() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("contractAndEventSub", "1.0"); builder.addShortTextData("name", new ExpressionBuilder().createContractInputExpression("nameInput", String.class.getName())); builder.addActor(ACTOR_NAME); builder.addUserTask(TASK1, ACTOR_NAME); final ContractDefinitionBuilder contract = builder.addContract(); contract.addInput("nameInput", Type.TEXT, "the name", false); final SubProcessDefinitionBuilder signalEventSubProcess = builder.addSubProcess("SignalEventSubProcess", true) .getSubProcessBuilder(); signalEventSubProcess.addStartEvent("signalStart").addSignalEventTrigger("*Bip Bip*"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti); final ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(processDefinition.getId(), Collections. singletonMap("nameInput", "john")); assertThat(getProcessAPI().getProcessDataInstance("name", processInstance.getId()).getValue()) .as("value of the data 'name' of the process") .isEqualTo("john"); waitForUserTask(TASK1); getProcessAPI().sendSignal("*Bip Bip*"); waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED); //clean up disableAndDeleteProcess(processDefinition); } @Test public void should_getUserTaskContract_return_the_contract() throws Exception { //given final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("contract", "1.0"); builder.addActor(ACTOR_NAME); builder.addUserTask(TASK1, ACTOR_NAME).addContract().addInput("numberOfDays", Type.INTEGER, null) .addConstraint("Mystical constraint", "true", null, "numberOfDays"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti); getProcessAPI().startProcess(processDefinition.getId()); final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1); //when final ContractDefinition contract = getProcessAPI().getUserTaskContract(userTask.getId()); //then assertThat(contract.getInputs()).hasSize(1); final InputDefinition input = contract.getInputs().get(0); assertThat(input.getName()).isEqualTo("numberOfDays"); assertThat(input.getType()).isEqualTo(Type.INTEGER); assertThat(input.getDescription()).isNull(); assertThat(contract.getConstraints()).hasSize(1); final ConstraintDefinition mysticRule = contract.getConstraints().get(0); assertThat(mysticRule.getName()).isEqualTo("Mystical constraint"); assertThat(mysticRule.getInputNames()).containsExactly("numberOfDays"); assertThat(mysticRule.getExplanation()).isNull(); //clean up disableAndDeleteProcess(processDefinition); } @Test public void should_getUserTaskContract_return_contract_with_complex_inputs() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("contract", "1.0"); builder.addActor(ACTOR_NAME); builder.addDocumentDefinition("myDoc") .addInitialValue(new ExpressionBuilder().createContractInputExpression("reportInit", FileInputValue.class.getName())); //given ContractDefinitionBuilder contractDefinitionBuilder = builder.addUserTask(TASK1, ACTOR_NAME).addContract(); contractDefinitionBuilder .addComplexInput("expenseLine", "expense report line", true) .addInput("date", Type.DATE, "expense date") .addInput("amount", Type.DECIMAL, "expense amount") .addComplexInput("date", "expense date").addInput("expenseType", Type.TEXT, "describe expense type"); contractDefinitionBuilder.addFileInput("report", "myReport"); contractDefinitionBuilder = builder.addContract(); contractDefinitionBuilder.addFileInput("reportInit", "myReport"); //when final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti); final ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(processDefinition.getId(), Collections. singletonMap("reportInit", new FileInputValue("theFile", "", "theContent".getBytes()))); final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1); //then final ContractDefinition contract = getProcessAPI().getUserTaskContract(userTask.getId()); assertThat(contract.getInputs()).hasSize(2); final InputDefinition complexInput = contract.getInputs().get(0); assertThat(complexInput.getName()).isEqualTo("expenseLine"); assertThat(complexInput.isMultiple()).as("should be multiple").isTrue(); assertThat(complexInput.getDescription()).isEqualTo("expense report line"); assertThat(complexInput.getInputs()).as("should have 3 inputs").hasSize(3); final InputDefinition fileInput = contract.getInputs().get(1); assertThat(fileInput.getInputs()).hasSize(2); assertThat(fileInput.getInputs()).containsExactly(new InputDefinitionImpl("filename", "Name of the file"), new InputDefinitionImpl("content", "Content of the file")); final Document myDoc = getProcessAPI().getLastDocument(processInstance.getId(), "myDoc"); final byte[] documentContent = getProcessAPI().getDocumentContent(myDoc.getContentStorageId()); assertThat(new String(documentContent)).isEqualTo("theContent"); //clean up disableAndDeleteProcess(processDefinition); } @Test public void should_create_a_contract_with_special_char() throws Exception { final long[] badValues = { 0, 366 }; for (final long badValue : badValues) { check_invalid_contract_with_special_char(badValue); } } @Test public void should_execute_a_contract_with_xml_tag_in_rule() throws Exception { //given final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("contract", "1.0"); builder.addActor(ACTOR_NAME); final String expectedExplanation = "numberOfDays must between one day and one year"; builder.addUserTask(TASK1, ACTOR_NAME).addContract().addInput("comment", Type.TEXT, null) .addConstraint("mandatory", "comment.equals(\"\")", expectedExplanation, "comment"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti); getProcessAPI().startProcess(processDefinition.getId()); //when final HumanTaskInstance task = waitForUserTaskAndGetIt(TASK1); getProcessAPI().assignUserTask(task.getId(), matti.getId()); //then no exceptions final Map map = new HashMap<>(); map.put("comment", ""); getProcessAPI().executeUserTask(task.getId(), map); disableAndDeleteProcess(processDefinition); } @Test public void should_execute_a_contract_with_integer_in_decimal_or_long() throws Exception { //given final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("contract", "1.0"); builder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder userTask = builder.addUserTask(TASK1, ACTOR_NAME); userTask.addContract().addInput("decimal", Type.DECIMAL, null); userTask.addContract().addInput("long", Type.LONG, null); userTask.addData("decimalVariable", Number.class.getName(), null); userTask.addData("longVariable", Number.class.getName(), null); userTask.addOperation(new OperationBuilder().createSetDataOperation("decimalVariable", new ExpressionBuilder().createContractInputExpression("decimal", Number.class.getName()))); userTask.addOperation(new OperationBuilder().createSetDataOperation("longVariable", new ExpressionBuilder().createContractInputExpression("long", Number.class.getName()))); //when final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti); getProcessAPI().startProcess(processDefinition.getId()); final HumanTaskInstance task = waitForUserTaskAndGetIt(TASK1); getProcessAPI().assignUserTask(task.getId(), matti.getId()); //then final Map map = new HashMap<>(); map.put("decimal", 2); map.put("long", 100); getProcessAPI().executeUserTask(task.getId(), map); //clean up disableAndDeleteProcess(processDefinition); } @Test public void should_invalid_contract_throw_ContractViolationException() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("contract", "1.0"); builder.addActor(ACTOR_NAME); builder.addUserTask(TASK1, ACTOR_NAME).addContract().addInput("numberOfDays", Type.INTEGER, null); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti); getProcessAPI().startProcess(processDefinition.getId()); final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1); try { getProcessAPI().assignAndExecuteUserTask(matti.getId(), userTask.getId(), new HashMap()); fail("The contract is not enforced"); } catch (final ContractViolationException e) { final String state = getProcessAPI().getActivityInstanceState(userTask.getId()); assertThat(state).isEqualTo("ready"); assertThat(e.getExplanations()).containsExactly("Expected input [numberOfDays] is missing"); assertThat(userTask.getAssigneeId()).isNotEqualTo(matti.getId()); } getProcessAPI().assignUserTask(userTask.getId(), matti.getId()); try { getProcessAPI().executeFlowNode(userTask.getId()); fail("The contract is not enforced"); } catch (final FlowNodeExecutionException e) { final String state = getProcessAPI().getActivityInstanceState(userTask.getId()); assertThat(state).isEqualTo("ready"); } disableAndDeleteProcess(processDefinition); } @Test public void should_valid_contract_with_invalid_operation_do_not_throw_exception() throws Exception { /* * the operation should be executed asynchronously, not in the same call as the contract input */ final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("contract", "1.0"); builder.addActor(ACTOR_NAME); builder.addShortTextData("dataName", null); UserTaskDefinitionBuilder userTaskDefinitionBuilder = builder.addUserTask(TASK1, ACTOR_NAME); userTaskDefinitionBuilder.addContract().addInput("numberOfDays", Type.INTEGER, null); userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDataOperation("dataName", new ExpressionBuilder().createGroovyScriptExpression("script", "throw new java.lang.RuntimeException()", String.class.getName()))); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1); getProcessAPI().assignUserTask(userTask.getId(), matti.getId()); getProcessAPI().executeUserTask(userTask.getId(), Collections. singletonMap("numberOfDays", 123)); waitForFlowNodeInFailedState(processInstance, TASK1); //no api method to check that the contract data are persisted (it only gets the archived version) disableAndDeleteProcess(processDefinition); } @SuppressWarnings("unchecked") @Test public void use_a_multiple_complex_input_in_user_tasks() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("contract", "1.0"); builder.addActor(ACTOR_NAME); builder.addDocumentDefinition("reportAsDoc"); builder.addDocumentListDefinition("receiptsAsDoc"); //given final UserTaskDefinitionBuilder userTaskDefinitionBuilder = builder.addUserTask(TASK1, ACTOR_NAME); final ContractDefinitionBuilder contractDefinitionBuilder = userTaskDefinitionBuilder.addContract(); contractDefinitionBuilder .addComplexInput("expenseReport", "expense report with several expense lines", true) .addInput("expenseType", Type.TEXT, "describe expense type") .addInput("expenseAmount", Type.DECIMAL, "expense amount") .addInput("expenseDate", Type.DATE, "expense date") .addInput("expenseProof", Type.BYTE_ARRAY, "expense proof"); contractDefinitionBuilder.addFileInput("report", "the report") .addFileInput("receipts", "the receipts", true) .addConstraint("report content not empty", "report.content.length > 1", "report content is empty", "report"); userTaskDefinitionBuilder.addData("expenseData", List.class.getName(), null); userTaskDefinitionBuilder.addData("reportData", List.class.getName(), null); userTaskDefinitionBuilder.addData("receiptsData", List.class.getName(), null); userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDataOperation("expenseData", new ExpressionBuilder().createContractInputExpression("expenseReport", List.class.getName()))); userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDataOperation("reportData", new ExpressionBuilder().createContractInputExpression("report", FileInputValue.class.getName()))); userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDataOperation("receiptsData", new ExpressionBuilder().createContractInputExpression("receipts", List.class.getName()))); userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDocument("reportAsDoc", new ExpressionBuilder().createContractInputExpression("report", FileInputValue.class.getName()))); userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDocumentList("receiptsAsDoc", new ExpressionBuilder().createContractInputExpression("receipts", List.class.getName()))); builder.addUserTask(TASK2, ACTOR_NAME).addTransition(TASK1, TASK2); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); //when final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1); getProcessAPI().assignUserTask(userTask.getId(), matti.getId()); final List> expenseReport = new ArrayList<>(); expenseReport.add(createExpenseLine("hotel", 150.3f, new Date(System.currentTimeMillis()), new byte[0])); expenseReport.add(createExpenseLine("taxi", 25, new Date(System.currentTimeMillis()), new byte[0])); expenseReport.add(createExpenseLine("plane", 500, new Date(System.currentTimeMillis()), new byte[] { 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1 })); final Map taskInput = new HashMap<>(); taskInput.put("expenseReport", (Serializable) expenseReport); final FileInputValue reportFile = new FileInputValue("report.pdf", "", new byte[] { 0, 1, 2, 3 }); taskInput.put("report", reportFile); final FileInputValue receipt1 = new FileInputValue("receipt1.pdf", "", new byte[] { 0, 1, 2, 4 }); final FileInputValue receipt2 = new FileInputValue("receipt2.pdf", "", new byte[] { 0, 1, 2, 5 }); taskInput.put("receipts", (Serializable) Arrays.asList(receipt1, receipt2)); getProcessAPI().executeUserTask(userTask.getId(), taskInput); //then waitForUserTaskAndGetIt(TASK2); assertThat((List>) getProcessAPI() .getArchivedActivityDataInstance("expenseData", userTask.getId()).getValue()).as( "should have my expense report data") .hasSize(3); final Serializable reportData = getProcessAPI().getArchivedActivityDataInstance("reportData", userTask.getId()) .getValue(); assertThat(reportData).as("should have single file").isEqualTo(reportFile); assertThat((List) getProcessAPI().getArchivedActivityDataInstance("receiptsData", userTask.getId()) .getValue()).as("should have multiple file") .containsExactly(receipt1, receipt2); final List receiptsAsDoc = getProcessAPI().getDocumentList(processInstance.getId(), "receiptsAsDoc", 0, 100); final Document reportAsDoc = getProcessAPI().getLastDocument(processInstance.getId(), "reportAsDoc"); assertThat(reportAsDoc.getContentFileName()).as("document file name").isEqualTo("report.pdf"); assertThat(getProcessAPI().getDocumentContent(reportAsDoc.getContentStorageId())).as("document content") .isEqualTo(new byte[] { 0, 1, 2, 3 }); assertThat(receiptsAsDoc).hasSize(2); assertThat(receiptsAsDoc).extracting("contentFileName", "index").containsExactly(tuple("receipt1.pdf", 0), tuple("receipt2.pdf", 1)); assertThat(getProcessAPI().getDocumentContent(receiptsAsDoc.get(0).getContentStorageId())) .as("document content").isEqualTo(new byte[] { 0, 1, 2, 4 }); assertThat(getProcessAPI().getDocumentContent(receiptsAsDoc.get(1).getContentStorageId())) .as("document content").isEqualTo(new byte[] { 0, 1, 2, 5 }); //clean up disableAndDeleteProcess(processDefinition); } private Map createExpenseLine(final String expenseType, final float expenseAmount, final Date expenseDate, final byte[] expenseProof) { final Map expenseLine = new HashMap<>(); expenseLine.put("expenseType", expenseType); expenseLine.put("expenseAmount", expenseAmount); expenseLine.put("expenseDate", expenseDate); expenseLine.put("expenseProof", expenseProof); return expenseLine; } @SuppressWarnings("unchecked") @Test public void use_a_multiple_simple_input_in_user_tasks() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("contract", "1.0"); builder.addActor(ACTOR_NAME); //given final UserTaskDefinitionBuilder userTaskDefinitionBuilder = builder.addUserTask(TASK1, ACTOR_NAME); userTaskDefinitionBuilder.addContract() .addInput("input", Type.TEXT, "multiple input", true); final List inputs = new ArrayList<>(); userTaskDefinitionBuilder.addData("inputListData", inputs.getClass().getName(), null); userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDataOperation("inputListData", new ExpressionBuilder().createContractInputExpression("input", inputs.getClass().getName()))); builder.addUserTask(TASK2, ACTOR_NAME).addTransition(TASK1, TASK2); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti); getProcessAPI().startProcess(processDefinition.getId()); //when final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1); getProcessAPI().assignUserTask(userTask.getId(), matti.getId()); final Map taskInputs = new HashMap<>(); final List inputList = new ArrayList<>(); inputList.add("input1"); inputList.add("input2"); inputList.add("input3"); inputList.add("input4"); inputList.add("input5"); taskInputs.put("input", (Serializable) inputList); getProcessAPI().executeUserTask(userTask.getId(), taskInputs); //then waitForUserTaskAndGetIt(TASK2); final ArchivedDataInstance archivedResult = getProcessAPI().getArchivedActivityDataInstance("inputListData", userTask.getId()); assertThat((List) archivedResult.getValue()).as("should have a list of simple input") .hasSameSizeAs(inputList); //clean up disableAndDeleteProcess(processDefinition); } @Test public void should_execute_user_task_when_contract_is_valid() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("contract", "1.0"); builder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder userTaskBuilder = builder.addUserTask(TASK1, ACTOR_NAME); userTaskBuilder.addData("result", BigDecimal.class.getName(), null); userTaskBuilder.addOperation(new OperationBuilder().createSetDataOperation("result", new ExpressionBuilder().createContractInputExpression("numberOfDays", Long.class.getName()))); builder.addUserTask(TASK2, ACTOR_NAME).addTransition(TASK1, TASK2); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti); getProcessAPI().startProcess(processDefinition.getId()); final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1); getProcessAPI().assignUserTask(userTask.getId(), matti.getId()); final long expectedValue = 8l; final Map inputs = new HashMap<>(); inputs.put("numberOfDays", expectedValue); getProcessAPI().executeUserTask(userTask.getId(), inputs); waitForUserTaskAndGetIt(TASK2); final ArchivedDataInstance archivedResult = getProcessAPI().getArchivedActivityDataInstance("result", userTask.getId()); assertThat(archivedResult.getValue()).isEqualTo(expectedValue); disableAndDeleteProcess(processDefinition); } @Test public void execute_user_task_should_throw_UserTaskNotFoundException() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("contract", "1.0"); builder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder userTaskBuilder = builder.addAutomaticTask("automaticTask").addUserTask(TASK1, ACTOR_NAME); userTaskBuilder.addContract().addInput("numberOfDays", Type.INTEGER, null) .addConstraint("mandatory", "numberOfDays != null", "numberOfDays must be set", "numberOfDays"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti); getProcessAPI().startProcess(processDefinition.getId()); final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1); getProcessAPI().assignUserTask(userTask.getId(), matti.getId()); try { final Map inputs = new HashMap<>(); inputs.put("numberOfDays", 8); //when getProcessAPI().executeUserTask(-1l, inputs); fail("should have a UserTaskNotFoundException "); } catch (final UserTaskNotFoundException e) { //then } finally { disableAndDeleteProcess(processDefinition); } } @Test public void should_ContractIsNotValidException_keep_task_ready() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("contract", "1.0"); builder.addActor(ACTOR_NAME); builder.addUserTask(TASK1, ACTOR_NAME).addContract().addInput("numberOfDays", Type.INTEGER, null) .addConstraint("mandatory", "numberOfDays != null", "numberOfDays must be set", "numberOfDays"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti); getProcessAPI().startProcess(processDefinition.getId()); final HumanTaskInstance userTask = waitForUserTaskAndGetIt(TASK1); getProcessAPI().assignUserTask(userTask.getId(), matti.getId()); try { final Map inputs = new HashMap<>(); inputs.put("numberOfDays", null); getProcessAPI().executeUserTask(userTask.getId(), inputs); fail("The contract is not enforced"); } catch (final ContractViolationException e) { final String state = getProcessAPI().getActivityInstanceState(userTask.getId()); assertThat(state).isEqualTo("ready"); assertThat(e.getExplanations()).as("should get explanations").isNotEmpty(); assertThat(e.getMessage()).contains("numberOfDays"); } disableAndDeleteProcess(processDefinition); } @Test public void should_connector_use_input_values() throws Exception { final Expression processNameExpression = new ExpressionBuilder().createConstantStringExpression(PROCESS_NAME); final Expression processVersionExpression = new ExpressionBuilder() .createContractInputExpression("inputVersion", String.class.getName()); final Expression outputExpression = new ExpressionBuilder().createContractInputExpression("processInputId", BigInteger.class.getName()); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); designProcessDefinition.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder userTaskBuilder = designProcessDefinition.addUserTask("task3", ACTOR_NAME); userTaskBuilder .addConnector("myConnector", "org.bonitasoft.engine.connectors.TestConnectorWithAPICall", "1.0", ConnectorEvent.ON_FINISH) .addInput("processName", processNameExpression).addInput("processVersion", processVersionExpression); userTaskBuilder.addContract().addInput("inputVersion", Type.TEXT, null) .addInput("processInputId", Type.INTEGER, null) .addInput("LocalDateInput", Type.LOCALDATE, null) .addInput("LocalDateTimeInput", Type.LOCALDATETIME, null) .addInput("LocalDateTimeNullInput", Type.LOCALDATETIME, null) .addInput("OffsetDateTimeInput", Type.OFFSETDATETIME, null); final ProcessDefinition processDefinition = deployAndEnableProcessWithTestConnectorWithAPICall( designProcessDefinition); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final HumanTaskInstance userTask = waitForUserTaskAndGetIt("task3"); getProcessAPI().assignUserTask(userTask.getId(), matti.getId()); final Map inputs = new HashMap<>(); inputs.put("inputVersion", PROCESS_VERSION); inputs.put("processInputId", BigInteger.valueOf(45L)); inputs.put("LocalDateInput", LocalDate.of(2017, 4, 12)); inputs.put("LocalDateTimeInput", LocalDateTime.of(2017, 4, 12, 19, 45, 22)); inputs.put("LocalDateTimeNullInput", null); inputs.put("OffsetDateTimeInput", OffsetDateTime.of(LocalDateTime.of(1973, 10, 17, 13, 42, 0), ZoneOffset.ofHours(-4))); getProcessAPI().executeUserTask(userTask.getId(), inputs); waitForProcessToBeInState(processInstance, ProcessInstanceState.COMPLETED); LocalDateTime localDateTime = (LocalDateTime) getProcessAPI().getUserTaskContractVariableValue(userTask.getId(), "LocalDateTimeInput"); LocalDate localDate = (LocalDate) getProcessAPI().getUserTaskContractVariableValue(userTask.getId(), "LocalDateInput"); LocalDateTime nullLocalDateTime = (LocalDateTime) getProcessAPI() .getUserTaskContractVariableValue(userTask.getId(), "LocalDateTimeNullInput"); OffsetDateTime offsetDateTime = (OffsetDateTime) getProcessAPI() .getUserTaskContractVariableValue(userTask.getId(), "OffsetDateTimeInput"); final BigInteger processInputId = (BigInteger) getProcessAPI() .getUserTaskContractVariableValue(userTask.getId(), "processInputId"); assertThat(processInputId).isEqualTo(BigInteger.valueOf(45L)); assertThat(localDate).isNotNull().isEqualTo(LocalDate.of(2017, 4, 12)); assertThat(localDateTime).isNotNull().isEqualTo(LocalDateTime.of(2017, 4, 12, 19, 45, 22)); assertThat(nullLocalDateTime).isNull(); // Dates must always be converted to UTC: assertThat(offsetDateTime) .isEqualTo(OffsetDateTime.of(LocalDateTime.of(1973, 10, 17, 17, 42, 0), ZoneOffset.UTC)); disableAndDeleteProcess(processDefinition); } private ProcessDefinition deployAndEnableProcessWithTestConnectorWithAPICall( final ProcessDefinitionBuilder processDefinitionBuilder) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnector(processDefinitionBuilder, ACTOR_NAME, matti, "TestConnectorWithAPICall.impl", TestConnectorWithAPICall.class, "TestConnectorWithAPICall.jar"); } private void check_invalid_contract_with_special_char(final long inputValue) throws Exception { //given final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("contract", "1.0"); builder.addActor(ACTOR_NAME); final String expectedExplanation = "numberOfDays must between one day and one year"; builder.addUserTask(TASK1, ACTOR_NAME).addContract().addInput("numberOfDays", Type.INTEGER, null) .addConstraint("mandatory", "numberOfDays>1 && numberOfDays<365", expectedExplanation, "numberOfDays"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, matti); getProcessAPI().startProcess(processDefinition.getId()); //when final HumanTaskInstance task = waitForUserTaskAndGetIt(TASK1); final Map inputs = new HashMap<>(); inputs.put("numberOfDays", inputValue); try { getProcessAPI().executeUserTask(task.getId(), inputs); fail("should throw ContractViolationException"); } catch (final ContractViolationException e) { //then assertThat(e.getExplanations()).as("rule should be violated").isNotEmpty().contains(expectedExplanation); } finally { disableAndDeleteProcess(processDefinition); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/GetPossibleUsersOfPendingHumanTaskIT.java ================================================ /** * 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.activity; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.connectors.VariableStorage; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; public class GetPossibleUsersOfPendingHumanTaskIT extends TestWithTechnicalUser { private static final String JOHN = "john"; private static final String JACK = "jack"; private User john; private User jack; private Group group; private Role role; @Override @Before public void before() throws Exception { super.before(); john = createUser(JOHN, "bpm"); jack = createUser(JACK, "bpm"); group = createGroup("group"); role = createRole("role"); loginOnDefaultTenantWith(JOHN, "bpm"); } @Override @After public void after() throws Exception { deleteUser(JOHN); deleteUser(JACK); deleteGroups(group); deleteRoles(role); VariableStorage.clearAll(); super.after(); } @Test public void getPossibleUsersOfTaskUserActor() throws Exception { final long userMembershipId = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId()) .getId(); final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, jack); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); final List possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 0, 10); assertEquals(1, possibleUsers.size()); assertEquals(jack, possibleUsers.get(0)); // cleanup: getIdentityAPI().deleteUserMembership(userMembershipId); disableAndDeleteProcess(processDefinition); } @Test public void getPossibleUsersOfTaskUserActorWithoutMembership() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, jack); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); final List possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 0, 10); assertEquals(1, possibleUsers.size()); assertEquals(jack, possibleUsers.get(0)); // cleanup: disableAndDeleteProcess(processDefinition); } @Test public void getPossibleUsersOfTaskRoleActor() throws Exception { final long userMembershipId = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId()) .getId(); final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, role); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); final List possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 0, 10); assertEquals(1, possibleUsers.size()); assertEquals(jack, possibleUsers.get(0)); // cleanup: getIdentityAPI().deleteUserMembership(userMembershipId); disableAndDeleteProcess(processDefinition); } @Test public void getPossibleUsersOfTaskGroupActor() throws Exception { final long userMembershipId = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId()) .getId(); final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, group); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); final List possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 0, 10); assertEquals(1, possibleUsers.size()); assertEquals(jack, possibleUsers.get(0)); // cleanup: getIdentityAPI().deleteUserMembership(userMembershipId); disableAndDeleteProcess(processDefinition); } @Test public void getPossibleUsersOfTaskShouldReturnAllUsersInThePaginationRange() throws Exception { final int nbUsers = 21; final List users = new ArrayList<>(nbUsers); final List userMembershipIds = new ArrayList<>(nbUsers); for (int i = 0; i < nbUsers; i++) { final User newUser = createUser("user_" + i, "pwd"); users.add(newUser); userMembershipIds.add(createUserMembership(newUser.getUserName(), role.getName(), group.getName()).getId()); } final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, users); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); final List possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 0, 30); // make sure the list is not limited to 20: assertEquals(21, possibleUsers.size()); // cleanup: disableAndDeleteProcess(processDefinition); for (final Long userMembershipId : userMembershipIds) { getIdentityAPI().deleteUserMembership(userMembershipId); } deleteUsers(users); } @Test public void getPossibleUsersOfTaskSubGroupActor() throws Exception { final Group group2 = createGroup("gr", group.getPath()); final long userMembershipId1 = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId()) .getId(); final long userMembershipId2 = getIdentityAPI().addUserMembership(john.getId(), group2.getId(), role.getId()) .getId(); final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, group); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); List possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 0, 10); assertEquals(2, possibleUsers.size()); assertEquals(jack, possibleUsers.get(0)); assertEquals(john, possibleUsers.get(1)); possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 1, 10); assertEquals(1, possibleUsers.size()); assertEquals(john, possibleUsers.get(0)); // cleanup: getIdentityAPI().deleteUserMembership(userMembershipId1); getIdentityAPI().deleteUserMembership(userMembershipId2); deleteGroups(group2); disableAndDeleteProcess(processDefinition); } @Test public void getPossibleUsersOfFilteredTask() throws Exception { final Group group2 = createGroup("gr", group.getPath()); final long userMembershipId1 = getIdentityAPI().addUserMembership(jack.getId(), group2.getId(), role.getId()) .getId(); final long userMembershipId2 = getIdentityAPI().addUserMembership(john.getId(), group2.getId(), role.getId()) .getId(); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("assign", "5.2"); designProcessDefinition.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder taskDefinitionBuilder = designProcessDefinition.addUserTask("step1", ACTOR_NAME); taskDefinitionBuilder.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilter", "1.0").addInput( "userId", new ExpressionBuilder().createConstantLongExpression(john.getId())); final UserTaskDefinitionBuilder definitionBuilder = designProcessDefinition.addUserTask("step2", ACTOR_NAME); definitionBuilder.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilter", "1.0").addInput( "userId", new ExpressionBuilder().createConstantLongExpression(jack.getId())); final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, ACTOR_NAME, john, "TestFilter"); getProcessAPI().addUserToActor(ACTOR_NAME, processDefinition, jack.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); waitForUserTask(processInstance, "step2"); final List possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(step1Id, 0, 2); assertEquals(1, possibleUsers.size()); assertEquals(john, possibleUsers.get(0)); // cleanup: getIdentityAPI().deleteUserMembership(userMembershipId1); getIdentityAPI().deleteUserMembership(userMembershipId2); deleteGroups(group2); disableAndDeleteProcess(processDefinition); } @Test public void getPossibleUsersOfUnknownTask() { final List possibleUsers = getProcessAPI().getPossibleUsersOfPendingHumanTask(-156L, 0, 10); assertEquals(0, possibleUsers.size()); } @Test public void getPossibleUsersOfTaskDefinitionUserActor() throws Exception { final long userMembershipId = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId()) .getId(); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("assign", "5.0"); designProcessDefinition.addActor(ACTOR_NAME); designProcessDefinition.addActor("emca"); designProcessDefinition.addUserTask("step1", ACTOR_NAME); designProcessDefinition.addUserTask("step2", "emca"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), Arrays.asList(ACTOR_NAME, "emca"), Arrays.asList(john, jack)); final List possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), "step1", 0, 10); assertEquals(1, possibleUsers.size()); assertEquals(john, possibleUsers.get(0)); // cleanup: getIdentityAPI().deleteUserMembership(userMembershipId); disableAndDeleteProcess(processDefinition); } @Test public void getPossibleUsersOfTaskDefinitionRoleActor() throws Exception { final long userMembershipId = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId()) .getId(); final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, role); final List possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), "step1", 0, 10); assertEquals(1, possibleUsers.size()); assertEquals(jack, possibleUsers.get(0)); // cleanup: getIdentityAPI().deleteUserMembership(userMembershipId); disableAndDeleteProcess(processDefinition); } @Test public void getPossibleUsersOfTaskDefinitionGroupActor() throws Exception { //given final long userMembershipId1 = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId()) .getId(); final long userMembershipId2 = getIdentityAPI().addUserMembership(john.getId(), group.getId(), role.getId()) .getId(); final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, group); //when final List possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), "step1", 0, 10); final List userIdsForActor = getProcessAPI().getUserIdsForActor(processDefinition.getId(), ACTOR_NAME, 0, 10); //then assertThat(possibleUsers).containsOnly(jack, john); assertThat(userIdsForActor).containsOnly(jack.getId(), john.getId()); // cleanup: getIdentityAPI().deleteUserMembership(userMembershipId1); getIdentityAPI().deleteUserMembership(userMembershipId2); disableAndDeleteProcess(processDefinition); } @Test public void getPossibleUsersOfTaskDefinitionSubGroupActor() throws Exception { final Group group2 = createGroup("gr", group.getPath()); final long userMembershipId1 = getIdentityAPI().addUserMembership(jack.getId(), group.getId(), role.getId()) .getId(); final long userMembershipId2 = getIdentityAPI().addUserMembership(john.getId(), group2.getId(), role.getId()) .getId(); final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, group); List possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), "step1", 0, 10); assertEquals(2, possibleUsers.size()); assertEquals(jack, possibleUsers.get(0)); assertEquals(john, possibleUsers.get(1)); possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), "step1", 1, 10); assertEquals(1, possibleUsers.size()); assertEquals(john, possibleUsers.get(0)); // cleanup: getIdentityAPI().deleteUserMembership(userMembershipId1); getIdentityAPI().deleteUserMembership(userMembershipId2); deleteGroups(group2); disableAndDeleteProcess(processDefinition); } @Test public void getPossibleUsersOfUnknownProcessDefinition() { final List possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(-156L, "step1", 0, 10); assertEquals(0, possibleUsers.size()); } @Test public void getPossibleUsersOfUnknownTaskDefinition() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, jack); final List possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), "step83", 0, 10); assertEquals(0, possibleUsers.size()); disableAndDeleteProcess(processDefinition); } @Test public void getPossibleUsersOfSystemTaskDefinition() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("auto"), Arrays.asList(false)); final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition); final List possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), "auto", 0, 10); assertEquals(0, possibleUsers.size()); disableAndDeleteProcess(processDefinition); } @Test public void getPossibleUsersOfTaskDefinitionWithAFilter() throws Exception { final Group group2 = createGroup("gr", group.getPath()); final long userMembershipId1 = getIdentityAPI().addUserMembership(john.getId(), group2.getId(), role.getId()) .getId(); final long userMembershipId2 = getIdentityAPI().addUserMembership(jack.getId(), group2.getId(), role.getId()) .getId(); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("assign", "5.2"); designProcessDefinition.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder taskDefinitionBuilder = designProcessDefinition.addUserTask("step1", ACTOR_NAME); taskDefinitionBuilder.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilter", "1.0").addInput( "userId", new ExpressionBuilder().createConstantLongExpression(john.getId())); final UserTaskDefinitionBuilder definitionBuilder = designProcessDefinition.addUserTask("step2", ACTOR_NAME); definitionBuilder.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilter", "1.0").addInput( "userId", new ExpressionBuilder().createConstantLongExpression(jack.getId())); final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, ACTOR_NAME, john, "TestFilter"); getProcessAPI().addUserToActor(ACTOR_NAME, processDefinition, jack.getId()); List possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), "step1", 0, 2); assertEquals(2, possibleUsers.size()); assertEquals(jack, possibleUsers.get(0)); assertEquals(john, possibleUsers.get(1)); possibleUsers = getProcessAPI().getPossibleUsersOfHumanTask(processDefinition.getId(), "step1", 2, 4); assertEquals(0, possibleUsers.size()); // cleanup: getIdentityAPI().deleteUserMembership(userMembershipId1); getIdentityAPI().deleteUserMembership(userMembershipId2); deleteGroups(group2); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/HumanTasksIT.java ================================================ /** * 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.activity; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bpm.flownode.EventCriterion.NAME_DESC; import static org.bonitasoft.engine.bpm.flownode.TimerType.DURATION; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.awaitility.Awaitility; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.ActivityStates; import org.bonitasoft.engine.bpm.flownode.EventInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeExecutionException; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.TaskPriority; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.Test; public class HumanTasksIT extends TestWithUser { final static int INITIALIZING_STATE_ID = 32; @Test public void cannotGetHumanTaskInstances() throws Exception { // First process def with 2 instances: final DesignProcessDefinition designProcessDef1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("initTask1"), Arrays.asList(true)); final ProcessDefinition processDef1 = deployAndEnableProcessWithActor(designProcessDef1, ACTOR_NAME, user); getProcessAPI().startProcess(processDef1.getId()); final ProcessInstance pi2 = getProcessAPI().startProcess(processDef1.getId()); final List humanTaskInstances = getProcessAPI().getHumanTaskInstances(pi2.getId(), "initTsk2", 0, 10); assertTrue(humanTaskInstances.isEmpty()); disableAndDeleteProcess(processDef1); } @Test public void can_creatte_FlowNodeInstance_with_several_non_ascii_characters() throws Exception { final String taskDisplayName = "àéò€çhahaאת ארץ בדפים מוסיקה לעברית. בקר גם טיפול פיסיקה, דת מתן בישול רומנית תחבורה. אל בידור מדויקים ואלקטרוניקה זאת נפלו.أملاً النزاع الصعداء بل الى. ان اتفاقية بالمطالبة ويكيبيديا، جُل. في كان بالجانب والديون الإتفاقية. لها المسرح وبولندا وبلجيكا، أي."; final String taskName = "paraiškos teikėjas Žcheck this out! 新フリぶ番高ホアリメ医意治せ羽装セヱ青済ルよじえ痛楽ぜは性位加嶋ケナ日者コ球量ミルシ異本たてふ火8在な日治じわあひ掲図トおぜ発審員ルをさレ。小スユヱイ写多キクオラ里数ンたレだ異分準ヲ念67券ド角続構ソ打政リレウテ社式築ワカエ川顔せー料開みドょわ北真メヲ齢記ヒチ山抱診露えっン。載ル定出へえぴ勝上ふのこ八抹なおぞ現負よッや新4省1開盛ょべせ東87令計のご導応ヘユレ対純霊真接スさほ。"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask(taskName, ACTOR_NAME) .addDisplayName(new ExpressionBuilder().createConstantStringExpression(taskDisplayName)) .addDescription("description"); final ProcessDefinition processDef1 = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); getProcessAPI().startProcess(processDef1.getId()); final HumanTaskInstance task1 = waitForUserTaskAndExecuteAndGetIt(taskName, user); assertEquals(taskDisplayName, task1.getDisplayName()); disableAndDeleteProcess(processDef1); } @Test public void getLastHumanTaskInstance() throws Exception { // First process def with 2 instances: final DesignProcessDefinition designProcessDef1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("initTask1"), Arrays.asList(true)); final ProcessDefinition processDef1 = deployAndEnableProcessWithActor(designProcessDef1, ACTOR_NAME, user); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDef1.getId()); waitForUserTask(processInstance1, "initTask1"); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDef1.getId()); waitForUserTask(processInstance2, "initTask1"); final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME + 2, PROCESS_VERSION); definitionBuilder.addStartEvent("start"); definitionBuilder.addActor(ACTOR_NAME); definitionBuilder.addUserTask("initTask2", ACTOR_NAME); definitionBuilder.addEndEvent("end"); definitionBuilder.addTransition("start", "initTask2"); definitionBuilder.addTransition("initTask2", "end"); final DesignProcessDefinition designProcessDef2 = definitionBuilder.done(); final ProcessDefinition processDef2 = deployAndEnableProcessWithActor(designProcessDef2, ACTOR_NAME, user); final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDef2.getId()); waitForUserTask(processInstance3, "initTask2"); final ProcessInstance processInstance4 = getProcessAPI().startProcess(processDef2.getId()); waitForUserTask(processInstance4, "initTask2"); final ProcessInstance processInstance5 = getProcessAPI().startProcess(processDef2.getId()); waitForUserTask(processInstance5, "initTask2"); final HumanTaskInstance taskInstance = getProcessAPI().getLastStateHumanTaskInstance(processInstance3.getId(), "initTask2"); assertNotNull(taskInstance); assertEquals("initTask2", taskInstance.getName()); disableAndDeleteProcess(processDef1); disableAndDeleteProcess(processDef2); } @Test public void taskExecutionFailureLogsPrettyMessage() throws Exception { final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("taskExecutionFailureLogsPrettyMessage", "1.01"); definitionBuilder.addStartEvent("start"); definitionBuilder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder userTaskDef = definitionBuilder.addUserTask("initTask", ACTOR_NAME); userTaskDef.addData("aData", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("initValue")); userTaskDef.addOperation(new OperationBuilder().createSetDataOperation("aData", new ExpressionBuilder().createConstantIntegerExpression(18))); final DesignProcessDefinition designProcessDef = definitionBuilder.done(); final ProcessDefinition processDef = deployAndEnableProcessWithActor(designProcessDef, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDef.getId()); logout(); loginOnDefaultTenantWith(USERNAME, PASSWORD); final ActivityInstance task = waitForUserTaskAndAssignIt(processInstance, "initTask", user); try { getProcessAPI().executeFlowNode(task.getId()); } catch (final FlowNodeExecutionException e) { assertTrue("wrong exception message", e.getMessage().contains("Incompatible assignment operation type")); } disableAndDeleteProcess(processDef); } @Test(expected = NotFoundException.class) public void cannotGetLastHumanTaskInstance() throws Exception { // First process def with 2 instances: final DesignProcessDefinition designProcessDef1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("initTask1"), Arrays.asList(true)); final ProcessDefinition processDef1 = deployAndEnableProcessWithActor(designProcessDef1, ACTOR_NAME, user); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDef1.getId()); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDef1.getId()); waitForUserTask(processInstance1, "initTask1"); waitForUserTask(processInstance2, "initTask1"); try { getProcessAPI().getLastStateHumanTaskInstance(processInstance2.getId(), "initTsk2"); } finally { // Clean up disableAndDeleteProcess(processDef1); } } @Test public void getHumanTaskInstances() throws Exception { // First process def with 2 instances: final DesignProcessDefinition designProcessDef1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("initTask1"), Arrays.asList(true)); final ProcessDefinition processDef1 = deployAndEnableProcessWithActor(designProcessDef1, ACTOR_NAME, user); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDef1.getId()); waitForUserTask(processInstance1, "initTask1"); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDef1.getId()); waitForUserTask(processInstance2, "initTask1"); final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME + 2, PROCESS_VERSION); definitionBuilder.addStartEvent("start"); definitionBuilder.addActor(ACTOR_NAME); definitionBuilder.addUserTask("initTask2", ACTOR_NAME); definitionBuilder.addEndEvent("end"); definitionBuilder.addTransition("start", "initTask2"); definitionBuilder.addTransition("initTask2", "end"); final DesignProcessDefinition designProcessDef2 = definitionBuilder.done(); final ProcessDefinition processDef2 = deployAndEnableProcessWithActor(designProcessDef2, ACTOR_NAME, user); final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDef2.getId()); waitForUserTask(processInstance3, "initTask2"); final ProcessInstance processInstance4 = getProcessAPI().startProcess(processDef2.getId()); waitForUserTask(processInstance4, "initTask2"); final ProcessInstance processInstance5 = getProcessAPI().startProcess(processDef2.getId()); waitForUserTask(processInstance5, "initTask2"); // No need to verify anything, if no exception, query exists getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance3.getId()) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.HUMAN_TASK).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance3.getId()) .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.HUMAN_TASK) .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done()); final List taskInstances = getProcessAPI().getHumanTaskInstances(processInstance3.getId(), "initTask2", 0, 10); assertEquals(1, taskInstances.size()); disableAndDeleteProcess(processDef1); disableAndDeleteProcess(processDef2); } @Test public void getOneAssignedHumanTaskInstance() throws Exception { final DesignProcessDefinition processDef = createProcessWithActorAndHumanTaskAndStringData(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); final ActivityInstance activityInstance = createProcessAndAssignUserTask(user, processDefinition); final HumanTaskInstance humanTask = getProcessAPI().getAssignedHumanTaskInstances(user.getId(), 0, 1, null) .get(0); assertEquals(activityInstance.getId(), humanTask.getId()); disableAndDeleteProcess(processDefinition); } @Test public void getAssignedHumanTaskInstances() throws Exception { final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); definitionBuilder.addStartEvent("start"); definitionBuilder.addActor(ACTOR_NAME); definitionBuilder.addUserTask("initTask1", ACTOR_NAME).addPriority(TaskPriority.NORMAL.name()); definitionBuilder.addUserTask("initTask2", ACTOR_NAME).addPriority(TaskPriority.LOWEST.name()); definitionBuilder.addUserTask("initTask3", ACTOR_NAME).addPriority(TaskPriority.HIGHEST.name()); definitionBuilder.addUserTask("initTask4", ACTOR_NAME).addPriority(TaskPriority.NORMAL.name()); definitionBuilder.addUserTask("initTask5", ACTOR_NAME); definitionBuilder.addEndEvent("end"); definitionBuilder.addTransition("start", "initTask1"); definitionBuilder.addTransition("start", "initTask2"); definitionBuilder.addTransition("start", "initTask3"); definitionBuilder.addTransition("start", "initTask4"); definitionBuilder.addTransition("initTask4", "initTask5"); final DesignProcessDefinition designProcessDef = definitionBuilder.done(); final ProcessDefinition processDef = deployAndEnableProcessWithActor(designProcessDef, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDef.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDef.getId()); waitForUserTaskAndAssignIt(processInstance, "initTask1", user); waitForUserTaskAndAssignIt(processInstance, "initTask2", user); waitForUserTaskAndAssignIt(processInstance, "initTask3", user); waitForUserTaskAndAssignIt(processInstance, "initTask4", user); // The task with the lowest priority is "initTask2" HumanTaskInstance humanTask = getProcessAPI() .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.PRIORITY_ASC).get(0); assertEquals("initTask2", humanTask.getName()); // The task with the highest priority is "initTask3" humanTask = getProcessAPI() .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.PRIORITY_DESC).get(0); assertEquals("initTask3", humanTask.getName()); // The task with the highest priority is "initTask3" humanTask = getProcessAPI().getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.DEFAULT) .get(0); assertEquals("initTask3", humanTask.getName()); // The task expected is "initTask1" humanTask = getProcessAPI() .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.NAME_ASC).get(0); assertEquals("initTask1", humanTask.getName()); // The task expected is "initTask4" humanTask = getProcessAPI() .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.NAME_DESC).get(0); assertEquals("initTask4", humanTask.getName()); disableAndDeleteProcess(processDef); } @Test public void getAssignedHumanTaskInstancesOrderByDates() throws Exception { final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); definitionBuilder.addStartEvent("start"); definitionBuilder.addActor(ACTOR_NAME); definitionBuilder.addUserTask("initTask1", ACTOR_NAME); definitionBuilder.addUserTask("initTask4", ACTOR_NAME); definitionBuilder.addUserTask("initTask5", ACTOR_NAME); definitionBuilder.addEndEvent("end"); definitionBuilder.addTransition("start", "initTask1"); definitionBuilder.addTransition("start", "initTask4"); definitionBuilder.addTransition("initTask4", "initTask5"); final DesignProcessDefinition designProcessDef = definitionBuilder.done(); final ProcessDefinition processDef = deployAndEnableProcessWithActor(designProcessDef, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDef.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDef.getId()); waitForUserTaskAndAssignIt(processInstance, "initTask1", user); waitForUserTaskAndAssignIt(processInstance, "initTask4", user); HumanTaskInstance humanTask = getProcessAPI() .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.NAME_DESC).get(0); assertEquals("initTask4", humanTask.getName()); assignAndExecuteStep(humanTask, user.getId()); waitForUserTaskAndAssignIt(processInstance, "initTask5", user); humanTask = getProcessAPI() .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.REACHED_STATE_DATE_ASC) .get(0); assertEquals("initTask1", humanTask.getName()); humanTask = getProcessAPI() .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.REACHED_STATE_DATE_DESC) .get(0); assertEquals("initTask5", humanTask.getName()); humanTask = getProcessAPI() .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.LAST_UPDATE_ASC).get(0); assertEquals("initTask1", humanTask.getName()); humanTask = getProcessAPI() .getAssignedHumanTaskInstances(user.getId(), 0, 1, ActivityInstanceCriterion.LAST_UPDATE_DESC).get(0); assertEquals("initTask5", humanTask.getName()); disableAndDeleteProcess(processDef); } @Test public void getHumanTaskInstance() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); final List activityInstances = getProcessAPI().getActivities(pi0.getId(), 0, 10); final long activityInstanceId = activityInstances.get(0).getId(); final HumanTaskInstance userTaskInstance = getProcessAPI().getHumanTaskInstance(activityInstanceId); assertEquals("step1", userTaskInstance.getName()); disableAndDeleteProcess(processDefinition); } private DesignProcessDefinition createProcessWithActorAndHumanTaskAndStringData() throws Exception { return new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME) .addDescription("Delivery all day and night long").addUserTask("step1", ACTOR_NAME) .addShortTextData("dataName", new ExpressionBuilder().createConstantStringExpression("beforeUpdate")) .getProcess(); } private ActivityInstance createProcessAndAssignUserTask(final User user, final ProcessDefinition processDefinition) throws Exception { final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); waitForUserTask(processInstance, "step1"); final List activityInstances = new ArrayList<>( getProcessAPI().getActivities(processInstance.getId(), 0, 20)); final ActivityInstance activityInstance = activityInstances.get(activityInstances.size() - 1); assertEquals("ready", activityInstance.getState()); getProcessAPI().assignUserTask(activityInstance.getId(), user.getId()); return activityInstance; } @Test public void setTaskPriority() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("step1", ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); HumanTaskInstance step1 = waitForUserTaskAndGetIt(processInstance, "step1"); assertEquals(TaskPriority.NORMAL, step1.getPriority()); final long step1Id = step1.getId(); getProcessAPI().setTaskPriority(step1Id, TaskPriority.HIGHEST); step1 = getProcessAPI().getHumanTaskInstance(step1Id); assertEquals(TaskPriority.HIGHEST, step1.getPriority()); getProcessAPI().setTaskPriority(step1Id, TaskPriority.LOWEST); step1 = getProcessAPI().getHumanTaskInstance(step1Id); assertEquals(TaskPriority.LOWEST, step1.getPriority()); disableAndDeleteProcess(processDefinition); } @Test public void setState() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("step1", ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); HumanTaskInstance step1 = waitForUserTaskAndGetIt(processInstance, "step1"); final long activityInstanceId = step1.getId(); getProcessAPI().setActivityStateById(activityInstanceId, INITIALIZING_STATE_ID); step1 = getProcessAPI().getHumanTaskInstance(activityInstanceId); assertEquals(ActivityStates.INITIALIZING_STATE, step1.getState()); // test set state by stateName getProcessAPI().setActivityStateByName(activityInstanceId, ActivityStates.EXECUTING_STATE); step1 = getProcessAPI().getHumanTaskInstance(activityInstanceId); assertEquals(ActivityStates.EXECUTING_STATE, step1.getState()); getProcessAPI().setActivityStateByName(activityInstanceId, ActivityStates.SKIPPED_STATE); // will skip task and finish process waitForProcessToFinish(processInstance.getId()); disableAndDeleteProcess(processDefinition); } @Test public void setStateShouldTerminateATaskWithBoundaryEvent() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask("step1", ACTOR_NAME).addBoundaryEvent("theBoundaryEvent") .addTimerEventTriggerDefinition(DURATION, new ExpressionBuilder().createConstantLongExpression(9000L)); processBuilder.addEndEvent("boundaryEnd"); processBuilder.addEndEvent("TheEnd"); processBuilder.addTransition("step1", "TheEnd"); processBuilder.addTransition("theBoundaryEvent", "boundaryEnd"); final DesignProcessDefinition designProcessDefinition = processBuilder.getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); HumanTaskInstance step1 = waitForUserTaskAndGetIt(processInstance, "step1"); List eventInstances = getProcessAPI().getEventInstances(processInstance.getId(), 0, 1000, NAME_DESC); assertThat(eventInstances.size()).isEqualTo(1); final long activityInstanceId = step1.getId(); getProcessAPI().setActivityStateById(activityInstanceId, INITIALIZING_STATE_ID); step1 = getProcessAPI().getHumanTaskInstance(activityInstanceId); assertEquals(ActivityStates.INITIALIZING_STATE, step1.getState()); getProcessAPI().setActivityStateByName(activityInstanceId, ActivityStates.SKIPPED_STATE); // skip task and finish process waitForProcessToFinish(processInstance.getId()); //verify that the BD has been cleaned eventInstances = getProcessAPI().getEventInstances(processInstance.getId(), 0, 1000, NAME_DESC); assertThat(eventInstances).isEmpty(); disableAndDeleteProcess(processDefinition); } @Test public void setStateByName_should_remove_time_of_linked_boundary_events() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask("step1", ACTOR_NAME).addBoundaryEvent("theBoundaryEvent") .addTimerEventTriggerDefinition(DURATION, new ExpressionBuilder().createConstantLongExpression(9000L)); processBuilder.addUserTask("step2", ACTOR_NAME); processBuilder.addTransition("step1", "step2"); processBuilder.addTransition("theBoundaryEvent", "step2"); final DesignProcessDefinition designProcessDefinition = processBuilder.getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); getProcessAPI().setActivityStateByName(waitForUserTaskAndGetIt(processInstance, "step1").getId(), ActivityStates.SKIPPED_STATE); waitForUserTask(processInstance, "step2"); Awaitility.await().until(() -> getProcessAPI() .searchTimerEventTriggerInstances(processInstance.getId(), new SearchOptionsBuilder(0, 100).done()) .getResult().isEmpty()); disableAndDeleteProcess(processDefinition); } @Test public void should_use_expression_to_compute_human_task_due_date() throws Exception { //given final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); builder.addActor(ACTOR_NAME); ExpressionBuilder oneHourExpressionBuilder = new ExpressionBuilder(); oneHourExpressionBuilder.createGroovyScriptExpression("dueDate expression", "3600000L", Long.class.getName()); ExpressionBuilder nullExpressionBuilder = new ExpressionBuilder(); nullExpressionBuilder.createGroovyScriptExpression("dueDate expression", "null", Long.class.getName()); ExpressionBuilder failExpressionBuilder = new ExpressionBuilder(); failExpressionBuilder.createGroovyScriptExpression("dueDate expression", "not a Long", Long.class.getName()); builder.addUserTask("userTask", ACTOR_NAME).addExpectedDuration(oneHourExpressionBuilder.done()); builder.addManualTask("manualTask", ACTOR_NAME).addExpectedDuration(oneHourExpressionBuilder.done()); builder.addUserTask("userTaskNullExpression", ACTOR_NAME).addExpectedDuration(nullExpressionBuilder.done()); builder.addManualTask("manualTaskNullExpression", ACTOR_NAME).addExpectedDuration(nullExpressionBuilder.done()); builder.addUserTask("failUserTask", ACTOR_NAME).addExpectedDuration(failExpressionBuilder.done()); builder.addTransition("userTask", "userTaskNullExpression"); builder.addTransition("userTaskNullExpression", "manualTask"); builder.addTransition("manualTask", "manualTaskNullExpression"); builder.addTransition("manualTaskNullExpression", "failUserTask"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); //when final HumanTaskInstance userTask = waitForUserTaskAssignAndExecuteIt(processInstance, "userTask", user, null); final HumanTaskInstance userTaskNullDueDate = waitForUserTaskAssignAndExecuteIt(processInstance, "userTaskNullExpression", user, null); final HumanTaskInstance manualTask = waitForUserTaskAndAssignIt(processInstance, "manualTask", user); getProcessAPI().executeUserTask(manualTask.getId(), null); final HumanTaskInstance manualTaskNullDueDate = waitForUserTaskAndAssignIt(processInstance, "manualTaskNullExpression", user); getProcessAPI().executeUserTask(manualTaskNullDueDate.getId(), null); final ActivityInstance taskToFail = waitForTaskToFail(processInstance); //then assertThat(userTask.getExpectedEndDate()).as("should expression set expected end date").isNotNull(); assertThat(userTaskNullDueDate.getExpectedEndDate()).as("should have no due date").isNull(); assertThat(manualTask.getExpectedEndDate()).as("should expression set expected end date").isNotNull(); assertThat(manualTaskNullDueDate.getExpectedEndDate()).as("should have no due date").isNull(); assertThat(taskToFail.getState()).isEqualTo(ActivityStates.FAILED_STATE); disableAndDeleteProcess(processDefinition); } @Test public void should_use_activity_expression_context_when_evaluating_default_value_expression() throws Exception { //given final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); builder.addActor(ACTOR_NAME); ExpressionBuilder activityInstanceIdExpressionBuiler = new ExpressionBuilder(); activityInstanceIdExpressionBuiler.createGroovyScriptExpression("activityInstanceId", "activityInstanceId", Long.class.getName(), new ExpressionBuilder().createEngineConstant(ExpressionConstants.ACTIVITY_INSTANCE_ID)); builder.addUserTask("userTask", ACTOR_NAME).addLongData("myId", activityInstanceIdExpressionBuiler.done()); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); //when final HumanTaskInstance userTask = waitForUserTaskAndAssignIt(processInstance, "userTask", user); //then assertThat(getProcessAPI().getActivityDataInstance("myId", userTask.getId()).getValue()) .as("should expression set activity instance id").isEqualTo(userTask.getId()); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/LoopIT.java ================================================ /** * 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.activity; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.Serializable; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedLoopActivityInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.connectors.VariableStorage; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.test.TestStates; import org.junit.After; import org.junit.Test; /** * @author Matthieu Chaffotte */ public class LoopIT extends TestWithUser { @After public void afterTest() { VariableStorage.clearAll(); } @Test public void executeAStandardLoopUserTaskWhichDoesNotLoop() throws Exception { final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(false); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeAStandardLoopUserTaskWhichDoesNotLoop", "1.0"); builder.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); builder.addUserTask("step1", ACTOR_NAME).addLoop(true, condition); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId()); waitForProcessToFinish(instance); final List archivedActivityInstances = getProcessAPI().getArchivedActivityInstances( instance.getId(), 0, 100, ActivityInstanceCriterion.NAME_ASC); assertEquals(2, archivedActivityInstances.size()); for (final ArchivedActivityInstance archivedActivityInstance : archivedActivityInstances) { assertTrue(ArchivedLoopActivityInstance.class.isInstance(archivedActivityInstance)); } disableAndDeleteProcess(processDefinition); } @Test public void executeAStandardLoopUserTask() throws Exception { final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeAStandardLoopUserTask", "1.0"); builder.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); builder.addUserTask("step1", ACTOR_NAME).addLoop(false, condition); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step1", user); waitForUserTask("step1"); disableAndDeleteProcess(processDefinition); } @Test public void evaluateExpressionsOnLoopUserTask() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("evaluateExpressionsOnLoopUserTask", "1.0"); builder.addActor(ACTOR_NAME).addDescription("For Golf players only"); final String activityName = "launch"; builder.addStartEvent("dummy"); builder.addUserTask(activityName, ACTOR_NAME).addLoop(false, new ExpressionBuilder().createConstantBooleanExpression(true)); builder.addTransition("dummy", activityName); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); try { final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long userTaskId = waitForUserTask(processInstance, activityName); final Map> expressions = new HashMap<>(); expressions.put(new ExpressionBuilder().createConstantBooleanExpression(true), new HashMap(0)); getProcessAPI().evaluateExpressionsOnActivityInstance(userTaskId, expressions); } finally { disableAndDeleteProcess(processDefinition); } } @Test public void executeAStandardLoopWithMaxIteration() throws Exception { final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true); final int loopMax = 3; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeAStandardLoopUserTask", "1.0"); builder.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); builder.addIntegerData("loopMax", new ExpressionBuilder().createConstantIntegerExpression(loopMax)); builder.addUserTask("step1", ACTOR_NAME).addLoop(false, condition, new ExpressionBuilder().createDataExpression("loopMax", Integer.class.getName())); builder.addUserTask("step2", ACTOR_NAME).addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); for (int i = 0; i < loopMax; i++) { final long step1Id = waitForUserTaskAndcheckPendingHumanTaskInstances("step1", processInstance); assignAndExecuteStep(step1Id, user); } waitForUserTaskAndcheckPendingHumanTaskInstances("step2", processInstance); disableAndDeleteProcess(processDefinition); } @Test public void executeAStandardLoopWithConditionUsingLoopCounter() throws Exception { final Expression condition = new ExpressionBuilder().createGroovyScriptExpression( "executeAStandardLoopWithConditionUsingLoopCounter", "loopCounter < 3", Boolean.class.getName(), Arrays.asList(new ExpressionBuilder().createEngineConstant(ExpressionConstants.LOOP_COUNTER))); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeAStandardLoopUserTask", "1.0"); builder.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); builder.addUserTask("step1", ACTOR_NAME).addLoop(false, condition); builder.addUserTask("step2", ACTOR_NAME).addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); for (int i = 0; i < 3; i++) { final long step1Id = waitForUserTaskAndcheckPendingHumanTaskInstances("step1", processInstance); assignAndExecuteStep(step1Id, user); } waitForUserTaskAndcheckPendingHumanTaskInstances("step2", processInstance); disableAndDeleteProcess(processDefinition); } @Test public void executeAStandardLoopWithConditionUsingDataUsingLoopCounter() throws Exception { final Expression condition = new ExpressionBuilder().createGroovyScriptExpression( "executeAStandardLoopWithConditionUsingLoopCounter", "pData + loopCounter < 6", Boolean.class.getName(), Arrays.asList(new ExpressionBuilder().createDataExpression("pData", Integer.class.getName()), new ExpressionBuilder().createEngineConstant(ExpressionConstants.LOOP_COUNTER))); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeAStandardLoopUserTask", "1.0"); builder.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); builder.addData("pData", Integer.class.getName(), null); UserTaskDefinitionBuilder step1 = builder.addUserTask("step1", ACTOR_NAME); step1.addLoop(false, condition); step1.addData("theData", Integer.class.getName(), new ExpressionBuilder().createEngineConstant(ExpressionConstants.LOOP_COUNTER)); step1.addOperation( new OperationBuilder().createSetDataOperation("pData", new ExpressionBuilder().createEngineConstant(ExpressionConstants.LOOP_COUNTER))); builder.addUserTask("step2", ACTOR_NAME).addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // No need to verify anything, if no exception, query exists getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.LOOP_ACTIVITY).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()) .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.LOOP_ACTIVITY) .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done()); for (int i = 0; i < 3; i++) { final long step1Id = waitForUserTaskAndcheckPendingHumanTaskInstances("step1", processInstance); assignAndExecuteStep(step1Id, user); } waitForUserTaskAndcheckPendingHumanTaskInstances("step2", processInstance); disableAndDeleteProcess(processDefinition); } private long waitForUserTaskAndcheckPendingHumanTaskInstances(final String userTaskName, final ProcessInstance processInstance) throws Exception { final long pendingTaskId = waitForUserTask(processInstance, userTaskName); final List pendingTasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, null); assertEquals(1, pendingTasks.size()); return pendingTaskId; } @Test public void executeAStandardLoopWithConditionUsingData() throws Exception { final Expression condition = new ExpressionBuilder().createGroovyScriptExpression( "executeAStandardLoopWithConditionUsingData1", "myData < 3", Boolean.class.getName(), Arrays.asList(new ExpressionBuilder().createDataExpression("myData", Integer.class.getName()))); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeAStandardLoopUserTaskWithData", "1.0"); builder.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); builder.addData("myData", Integer.class.getName(), new ExpressionBuilder().createConstantIntegerExpression(0)); builder.addUserTask("step1", ACTOR_NAME) .addLoop(false, condition) .addOperation( new LeftOperandBuilder().createNewInstance("myData").done(), OperatorType.ASSIGNMENT, "=", null, new ExpressionBuilder().createGroovyScriptExpression( "executeAStandardLoopWithConditionUsingData1", "myData + 1", Integer.class.getName(), Arrays.asList(new ExpressionBuilder() .createDataExpression("myData", Integer.class.getName())))); builder.addUserTask("step2", ACTOR_NAME).addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); for (int i = 0; i < 3; i++) { final long step1Id = waitForUserTaskAndcheckPendingHumanTaskInstances("step1", processInstance); assignAndExecuteStep(step1Id, user); } waitForUserTaskAndcheckPendingHumanTaskInstances("step2", processInstance); disableAndDeleteProcess(processDefinition); } @Test public void abortProcessWithActiveLoopActivity() throws Exception { // given final String loopName = "step1"; final String userTaskName = "step2"; final ProcessDefinition processDefinition = deployAndEnableProcessWithLoopAndUserTaskInPararallelAndTerminateEvent( loopName, userTaskName); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance.getId(), loopName); // when waitForUserTaskAndExecuteIt(processInstance, userTaskName, user); // then // executing the user task will terminate the process: the loop activity must be aborted waitForFlowNodeInState(processInstance, loopName, TestStates.ABORTED, true); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } private ProcessDefinition deployAndEnableProcessWithLoopAndUserTaskInPararallelAndTerminateEvent( final String loopName, final String parallelTaskName) throws Exception { final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("My proc", "1.0"); builder.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); builder.addStartEvent("start"); builder.addUserTask(loopName, ACTOR_NAME).addLoop(false, condition); builder.addUserTask(parallelTaskName, ACTOR_NAME); builder.addGateway("gateway", GatewayType.PARALLEL); builder.addEndEvent("terminate").addTerminateEventTrigger(); builder.addTransition("start", "gateway"); builder.addTransition("gateway", loopName); builder.addTransition("gateway", parallelTaskName); builder.addTransition(loopName, "terminate"); builder.addTransition(parallelTaskName, "terminate"); return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/ManualTasksIT.java ================================================ /** * 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.activity; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.List; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.ManualTaskInstance; import org.bonitasoft.engine.bpm.flownode.UserTaskInstance; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.connectors.VariableStorage; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.junit.After; import org.junit.Test; public class ManualTasksIT extends TestWithUser { @Override @After public void after() throws Exception { VariableStorage.clearAll(); super.after(); } @Test public void executeProcessWithManualTask() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder().createNewInstance( "executeConnectorOnStartOfAnAutomaticActivity", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addDescription("Delivery all day and night long"); designProcessDefinition.addAutomaticTask("step1"); designProcessDefinition.addManualTask("step2", ACTOR_NAME); designProcessDefinition.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndAssignIt(processInstance, "step2", user); final List toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(user.getId(), 0, 10, null); assertEquals(1, toDoTasks.size()); final HumanTaskInstance humanTaskInstance = toDoTasks.get(0); assertEquals(user.getId(), humanTaskInstance.getAssigneeId()); // No need to verify anything, if no exception, query exists getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.MANUAL_TASK).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()) .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.MANUAL_TASK) .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done()); getProcessAPI().executeFlowNode(humanTaskInstance.getId()); waitForProcessToFinish(processInstance.getId()); disableAndDeleteProcess(processDefinition); } @Test public void executeProcessWithManualTaskAndUserTask() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder().createNewInstance( "executeConnectorOnStartOfAnAutomaticActivity", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addDescription("Delivery all day and night long"); designProcessDefinition.addAutomaticTask("step1"); designProcessDefinition.addManualTask("step2", ACTOR_NAME); designProcessDefinition.addUserTask("step3", ACTOR_NAME); designProcessDefinition.addTransition("step1", "step2"); designProcessDefinition.addTransition("step1", "step3"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndAssignIt(processInstance, "step2", user); waitForUserTaskAndAssignIt(processInstance, "step3", user); final List toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(user.getId(), 0, 10, null); assertEquals(2, toDoTasks.size()); final HumanTaskInstance humanTaskInstance1 = toDoTasks.get(0); final HumanTaskInstance humanTaskInstance2 = toDoTasks.get(1); HumanTaskInstance step3; HumanTaskInstance step2; if (humanTaskInstance1.getName().equals("step3")) { step3 = humanTaskInstance1; step2 = humanTaskInstance2; } else { step3 = humanTaskInstance2; step2 = humanTaskInstance1; } assertEquals(user.getId(), humanTaskInstance1.getAssigneeId()); assertEquals(user.getId(), humanTaskInstance2.getAssigneeId()); assertTrue(step2 instanceof ManualTaskInstance); assertTrue(step3 instanceof UserTaskInstance); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/MultiInstanceIT.java ================================================ /** * 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.activity; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID; import static org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor.STATE_NAME; import static org.bonitasoft.engine.expression.ExpressionConstants.*; import static org.bonitasoft.engine.operation.OperatorType.ASSIGNMENT; import static org.bonitasoft.engine.test.TestStates.ABORTED; import static org.junit.Assert.*; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.actor.ActorCriterion; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.comment.Comment; import org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.ActivityStates; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.ArchivedAutomaticTaskInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.ArchivedMultiInstanceActivityInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.MultiInstanceActivityInstance; import org.bonitasoft.engine.bpm.flownode.impl.internal.FlowElementContainerDefinitionImpl; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.connectors.TestConnector; import org.bonitasoft.engine.connectors.VariableStorage; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.filter.user.TestFilterWithAutoAssign; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Baptiste Mesta * @author Celine Souchet */ public class MultiInstanceIT extends TestWithUser { private static final String JACK = "jack"; private static final String JOHN = "john"; private static final String JENNY = "jenny"; private User john; private User jack; private User jenny; @Override @Before public void before() throws Exception { super.before(); john = createUser(JOHN, "bpm"); jack = createUser(JACK, "bpm"); jenny = createUser(JENNY, "bpm"); logout(); loginOnDefaultTenantWith(JOHN, "bpm"); } @Override @After public void after() throws Exception { deleteUser(JOHN); deleteUser(JACK); deleteUser(JENNY); VariableStorage.clearAll(); super.after(); } @Test public void executeAMultiInstanceUserTaskWhichCreate0Task() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance( "executeAMultiInstanceUserTaskWhichCreate0Task", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addAutomaticTask("autostep").addUserTask("step1", ACTOR_NAME) .addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(0)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId()); waitForProcessToFinish(instance); final List archivedActivityInstances = getProcessAPI().getArchivedActivityInstances( instance.getId(), 0, 100, ActivityInstanceCriterion.NAME_ASC); assertEquals(4, archivedActivityInstances.size()); for (final ArchivedActivityInstance archivedActivityInstance : archivedActivityInstances) { assertTrue(ArchivedAutomaticTaskInstance.class.isInstance(archivedActivityInstance) && archivedActivityInstance.getName().contains("auto") || ArchivedMultiInstanceActivityInstance.class.isInstance(archivedActivityInstance)); } disableAndDeleteProcess(processDefinition); } @Test public void executeAMultiInstanceUserTask() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeAMultiInstanceUserTask", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addUserTask("step1", ACTOR_NAME).addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(2)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step1", john); waitForUserTask(processInstance, "step1"); disableAndDeleteProcess(processDefinition); } @Test public void searchMultiInstance() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeAMultiInstanceUserTask", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addUserTask("step1", ACTOR_NAME).addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(1)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); // No need to verify anything, if no exception, query exists getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()) .filter(ProcessSupervisorSearchDescriptor.USER_ID, john.getId()).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.MULTI_INSTANCE_ACTIVITY) .filter(ProcessSupervisorSearchDescriptor.USER_ID, john.getId()).done()); final SearchResult searchActivities = getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()) .filter(ActivityInstanceSearchDescriptor.STATE_NAME, "executing") .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.MULTI_INSTANCE_ACTIVITY) .done()); assertThat(searchActivities.getResult().get(0).getType()) .isEqualByComparingTo(FlowNodeType.MULTI_INSTANCE_ACTIVITY); final MultiInstanceActivityInstance activityInstance = (MultiInstanceActivityInstance) searchActivities .getResult().get(0); assertEquals(1, activityInstance.getNumberOfActiveInstances()); final SearchResult searchFlowNode = getProcessAPI().searchFlowNodeInstances( new SearchOptionsBuilder(0, 10) .filter(FlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, processInstance.getId()) .filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, "executing").done()); final MultiInstanceActivityInstance flowNode = (MultiInstanceActivityInstance) searchFlowNode.getResult() .get(0); assertEquals(1, flowNode.getNumberOfActiveInstances()); assignAndExecuteStep(step1Id, john); waitForProcessToFinish(processInstance); final SearchResult searchArchivedActivities = getProcessAPI() .searchArchivedActivities( new SearchOptionsBuilder(0, 10) .filter(ArchivedActivityInstanceSearchDescriptor.SOURCE_OBJECT_ID, flowNode.getId()) .done()); assertTrue(ArchivedMultiInstanceActivityInstance.class.isInstance(searchArchivedActivities.getResult().get(0))); assertEquals(flowNode.getId(), searchArchivedActivities.getResult().get(0).getSourceObjectId()); final SearchResult searchArchivedFlowNode = getProcessAPI() .searchArchivedFlowNodeInstances( new SearchOptionsBuilder(0, 10) .filter(ArchivedFlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, processInstance.getId()) .filter(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, "executing").done()); assertTrue(ArchivedMultiInstanceActivityInstance.class.isInstance(searchArchivedFlowNode.getResult().get(0))); assertEquals(flowNode.getId(), searchArchivedFlowNode.getResult().get(0).getSourceObjectId()); disableAndDeleteProcess(processDefinition); } @Test public void executeAMultiInstanceWithLoopDataInputAndOutput() throws Exception { final List outputList = executeAMultiInstanceWithLoopDataAs("[58,26,12]", "[1,2,3]"); assertEquals(3, outputList.size()); assertEquals(59, outputList.get(0)); assertEquals(27, outputList.get(1)); assertEquals(13, outputList.get(2)); } private List executeAMultiInstanceWithLoopDataAs(final String inputListScript, final String outputListScript) throws Exception { final String anotherActor = "anotherActor"; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeAMultiInstanceWithMaxIteration", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addActor(anotherActor); final String loopDataInputName = "loopDataInput_"; String loopDataOutputName = "loopDataOutput_"; builder.addData(loopDataInputName, List.class.getName(), new ExpressionBuilder().createGroovyScriptExpression("executeAMultiInstanceWithLoopDataInputAndOutput1", inputListScript, List.class.getName())); if (outputListScript != null) { builder.addData( loopDataOutputName, List.class.getName(), new ExpressionBuilder().createGroovyScriptExpression( "executeAMultiInstanceWithLoopDataInputAndOutput2", outputListScript, List.class.getName())); } else { loopDataOutputName = loopDataInputName; } final UserTaskDefinitionBuilder userTask = builder.addUserTask("step1", ACTOR_NAME); userTask.addData("dataInputItem_", Integer.class.getName(), new ExpressionBuilder().createConstantIntegerExpression(0)); userTask.addData("dataOutputItem_", Integer.class.getName(), new ExpressionBuilder().createConstantIntegerExpression(0)); userTask.addOperation( new LeftOperandBuilder().createNewInstance("dataOutputItem_").done(), ASSIGNMENT, "=", null, new ExpressionBuilder().createGroovyScriptExpression("executeAMultiInstanceWithLoopDataInputAndOutput3", "dataInputItem_ + 1", Integer.class.getName(), new ExpressionBuilder().createDataExpression("dataInputItem_", Integer.class.getName()))); userTask.addMultiInstance(true, loopDataInputName).addDataInputItemRef("dataInputItem_") .addDataOutputItemRef("dataOutputItem_") .addLoopDataOutputRef(loopDataOutputName); builder.addUserTask("lastTask", anotherActor); builder.addTransition("step1", "lastTask"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), Arrays.asList(ACTOR_NAME, anotherActor), Arrays.asList(john, jack)); final ProcessInstance process = getProcessAPI().startProcess(processDefinition.getId()); final DataInstance processDataInstance2 = getProcessAPI().getProcessDataInstance(loopDataInputName, process.getId()); final List value = (List) processDataInstance2.getValue(); final int loopMax = value.size(); checkPendingTaskSequentially(loopMax, process, false); waitForUserTask(process, "lastTask"); final DataInstance processDataInstance = getProcessAPI().getProcessDataInstance(loopDataOutputName, process.getId()); assertNotNull("unable to find the loop data output on the process", processDataInstance); final List list = (List) processDataInstance.getValue(); disableAndDeleteProcess(processDefinition); return list; } @Test public void executeAMultiInstanceWithMaxIteration() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeAMultiInstanceWithMaxIteration", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addIntegerData("a", new ExpressionBuilder().createConstantIntegerExpression(1)); builder.addIntegerData("b", new ExpressionBuilder().createConstantIntegerExpression(2)); final int loopMax = 3; builder.addUserTask("step1", ACTOR_NAME).addMultiInstance( true, new ExpressionBuilder().createGroovyScriptExpression("executeAMultiInstanceWithMaxIteration", "a + b", Integer.class.getName(), new ExpressionBuilder().createDataExpression("a", Integer.class.getName()), new ExpressionBuilder().createDataExpression("b", Integer.class.getName()))); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); checkPendingTaskSequentially(loopMax, processInstance, true); disableAndDeleteProcess(processDefinition); } @Test public void executeAMultiInstanceWithLoopDataOutputEmpty() throws Exception { final List list = executeAMultiInstanceWithLoopDataAs("[58,26,12]", "[]"); assertEquals(3, list.size()); assertEquals(59, list.get(0)); assertEquals(27, list.get(1)); assertEquals(13, list.get(2)); } @Test public void executeAMultiInstanceWithLoopDataOutputNull() throws Exception { final List list = executeAMultiInstanceWithLoopDataAs("[58,26,12]", "null"); assertEquals(3, list.size()); assertEquals(59, list.get(0)); assertEquals(27, list.get(1)); assertEquals(13, list.get(2)); } @Test public void executeAMultiInstanceWithLoopDataOutputTooShort() throws Exception { final List list = executeAMultiInstanceWithLoopDataAs("[58,26,12]", "[1,2]"); assertEquals(3, list.size()); assertEquals(59, list.get(0)); assertEquals(27, list.get(1)); assertEquals(13, list.get(2)); } @Test public void executeAMultiInstanceWithLoopDataOutputTooLong() throws Exception { final List list = executeAMultiInstanceWithLoopDataAs("[58,26,12]", "[1,2,3,4]"); assertEquals(4, list.size()); assertEquals(59, list.get(0)); assertEquals(27, list.get(1)); assertEquals(13, list.get(2)); assertEquals(4, list.get(3)); } @Test public void executeAMultiInstanceWithSameLoopDataAsInputAndOutput() throws Exception { final List list = executeAMultiInstanceWithLoopDataAs("[58,26,12]", null); assertEquals(3, list.size()); assertEquals(59, list.get(0)); assertEquals(27, list.get(1)); assertEquals(13, list.get(2)); } @Test public void executeAMultiInstanceParallelWithLoopCardinalityUsingGroovyAndData() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance( "executeAMultiInstanceParallelWithMaxIteration", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addIntegerData("a", new ExpressionBuilder().createConstantIntegerExpression(1)); builder.addIntegerData("b", new ExpressionBuilder().createConstantIntegerExpression(2)); final int loopMax = 3; builder.addUserTask("step1", ACTOR_NAME).addMultiInstance( false, new ExpressionBuilder().createGroovyScriptExpression( "executeAMultiInstanceParallelWithLoopCardinalityUsingGroovyAndData", "a + b", Integer.class.getName(), new ExpressionBuilder().createDataExpression("a", Integer.class.getName()), new ExpressionBuilder().createDataExpression("b", Integer.class.getName()))); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); checkPendingTaskInParallel(loopMax, loopMax, processInstance); disableAndDeleteProcess(processDefinition); } @Test public void executeAMultiInstanceParallelWithLoopCardinalityMoreThan20() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance( "executeAMultiInstanceParallelWithMaxIteration", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addIntegerData("a", new ExpressionBuilder().createConstantIntegerExpression(16)); builder.addIntegerData("b", new ExpressionBuilder().createConstantIntegerExpression(14)); final int loopMax = 30; builder.addUserTask("step1", ACTOR_NAME).addMultiInstance( false, new ExpressionBuilder().createGroovyScriptExpression( "executeAMultiInstanceParallelWithLoopCardinalityUsingGroovyAndData", "a + b", Integer.class.getName(), new ExpressionBuilder().createDataExpression("a", Integer.class.getName()), new ExpressionBuilder().createDataExpression("b", Integer.class.getName()))); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); checkPendingTaskInParallel(loopMax, loopMax, processInstance); disableAndDeleteProcess(processDefinition); } @Test public void executeAMultiInstanceParallelWithCompletionCondition() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance( "executeAMultiInstanceParallelWithCompletionCondition", PROCESS_VERSION); builder.addActor(ACTOR_NAME); final int loopMax = 3; builder.addUserTask("step1", ACTOR_NAME) .addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(3)) .addCompletionCondition( new ExpressionBuilder().createGroovyScriptExpression( "executeAMultiInstanceParallelWithCompletionCondition", NUMBER_OF_COMPLETED_INSTANCES.getEngineConstantName() + " >= 2 ", Boolean.class.getName(), new ExpressionBuilder().createEngineConstant(NUMBER_OF_COMPLETED_INSTANCES))); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); checkPendingTaskInParallel(loopMax, 2, processInstance); disableAndDeleteProcess(processDefinition); } @Test public void remainingInstancesAreAbortedAfterCompletionCondition() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance( "remainingInstancesAreAbortedAfterCompletionCondition", PROCESS_VERSION); builder.addActor(ACTOR_NAME); final int loopMax = 3; builder.addUserTask("step1", ACTOR_NAME) .addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(loopMax)) .addCompletionCondition( new ExpressionBuilder().createGroovyScriptExpression( "remainingInstancesAreAbortedAfterCompletionCondition", NUMBER_OF_COMPLETED_INSTANCES.getEngineConstantName() + " == 1 ", Boolean.class.getName(), new ExpressionBuilder().createEngineConstant(NUMBER_OF_COMPLETED_INSTANCES))); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); checkPendingTaskInParallel(loopMax, 1, processInstance); disableAndDeleteProcess(processDefinition); } /** * Test of completion condition (Sequential multi-instance - Number of completed instances). * * @throws Exception * @since 6.0 */ @Test public void executeAMultiInstanceSequentialWithCompletionCondition() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance( "executeAMultiInstanceSequentialWithCompletionCondition", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addUserTask("step1", ACTOR_NAME) .addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(20)) .addCompletionCondition( new ExpressionBuilder().createGroovyScriptExpression( "executeAMultiInstanceSequentialWithCompletionCondition", NUMBER_OF_COMPLETED_INSTANCES.getEngineConstantName() + " >= 15 ", Boolean.class.getName(), new ExpressionBuilder().createEngineConstant(NUMBER_OF_COMPLETED_INSTANCES))); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); checkPendingTaskSequentially(15, processInstance, true); disableAndDeleteProcess(processDefinition); } @Test public void abortAMultiInstanceSequential() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addStartEvent("Start").addEndEvent("End").addTerminateEventTrigger(); builder.addUserTask("Step1", ACTOR_NAME) .addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(20)) .addCompletionCondition(new ExpressionBuilder().createConstantBooleanExpression(false)); builder.addGateway("Gateway", GatewayType.PARALLEL).addUserTask("Step2", ACTOR_NAME); builder.addTransition("Start", "Gateway").addTransition("Gateway", "Step1").addTransition("Step1", "End") .addTransition("Gateway", "Step2") .addTransition("Step2", "End"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "Step1"); waitForUserTaskAndExecuteIt(processInstance, "Step2", john); waitForProcessToFinish(processInstance); final ArchivedActivityInstance archivedStep1 = getProcessAPI().getArchivedActivityInstance(step1Id); assertEquals(ActivityStates.ABORTED_STATE, archivedStep1.getState()); disableAndDeleteProcess(processDefinition); } /** * Test with completion condition equal to true (sequential multi-instance). * * @throws Exception * @since 6.0 */ @Test public void executeAMultiInstanceSequentialWithConpletionConditionTrue() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance( "executeAMultiInstanceSequentialWithCompletionConditionTrue", PROCESS_VERSION); builder.addActor(ACTOR_NAME).addDescription("Deliver all day and night long"); builder.addUserTask("step1", ACTOR_NAME) .addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(4)) .addCompletionCondition( new ExpressionBuilder().createGroovyScriptExpression( "executeAMultiInstanceSequentialWithCompletionConditionTrue", "true", Boolean.class.getName())); builder.addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME).addTransition("step1", "step2") .addTransition("step2", "step3"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); checkPendingTaskSequentially(3, processInstance, true); disableAndDeleteProcess(processDefinition); } /** * Test with completion condition equal to true (parallel multi-instance). * * @throws Exception * @since 6.0 */ @Test public void executeAMultiInstanceParallelWithConpletionConditionTrue() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance( "executeAMultiInstanceSequentialWithCompletionConditionTrue", PROCESS_VERSION); builder.addActor(ACTOR_NAME).addDescription("Deliver all day and night long"); builder.addUserTask("step1", ACTOR_NAME) .addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(4)) .addCompletionCondition( new ExpressionBuilder().createGroovyScriptExpression( "executeAMultiInstanceSequentialWithCompletionConditionTrue", "true", Boolean.class.getName())); builder.addAutomaticTask("step2").addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step1"); waitForUserTask(processInstance, "step1"); waitForUserTask(processInstance, "step1"); waitForUserTaskAndExecuteIt(processInstance, "step1", john); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } /** * Test of completion condition (Sequential multi-instance - Number of completed instances). * * @throws Exception * @since 6.0 */ @Test public void executeAMultiInstanceWithCompletionConditionNumberOfCompletedInstances() throws Exception { final String condition = " >= 2"; final int loopMax = 3; final int numberOfExecutedActivities = loopMax - 1; withMultiInstanceAttribute(condition, NUMBER_OF_COMPLETED_INSTANCES, numberOfExecutedActivities); } /** * Test of completion condition (Sequential multi-instance - Number of instances). * * @throws Exception * @since 6.0 */ @Test public void executeAMultiInstanceWithCompletionConditionNumberOfInstances() throws Exception { final String condition = " >= 2"; final int loopMax = 3; final int numberOfExecutedActivities = loopMax - 1; withMultiInstanceAttribute(condition, ExpressionConstants.NUMBER_OF_INSTANCES, numberOfExecutedActivities); } /** * Test of completion condition (Sequential multi-instance - Number of terminated instances). * * @throws Exception * @since 6.0 */ @Test public void executeAMultiInstanceWithCompletionConditionNumberOfTerminatedInstances() throws Exception { final String condition = " >= 2"; final int loopMax = 3; final int numberOfExecutedActivities = loopMax;// numberOfTerminated instance is never >= 2 here withMultiInstanceAttribute(condition, ExpressionConstants.NUMBER_OF_TERMINATED_INSTANCES, numberOfExecutedActivities); } /** * Test of completion condition (Sequential multi-instance - Number of actives instances). * * @throws Exception * @since 6.0 */ @Test public void executeAMultiInstanceWithCompletionConditionNumberOfActiveInstances() throws Exception { final String condition = " == 2"; final int loopMax = 3; final int numberOfExecutedActivities = loopMax;// will never be true when is in sequence withMultiInstanceAttribute(condition, ExpressionConstants.NUMBER_OF_ACTIVE_INSTANCES, numberOfExecutedActivities); } /** * Test of completion condition (Parallel multi-instance - Number of completed instances). * * @throws Exception * @since 6.0 */ @Test public void executeAMultiInstanceParallelWithCompletionConditionNumberOfCompletedInstances() throws Exception { final String condition = " >= 2 "; final int loopMax = 3; final int numberOfExecutedActivities = loopMax; final int numberOfTaskToComplete = 2; withParallelMultiInstanceAttribute(condition, NUMBER_OF_COMPLETED_INSTANCES, numberOfExecutedActivities, numberOfTaskToComplete); } /** * Test of completion condition (Parallel multi-instance - Number of instances). * * @throws Exception * @since 6.0 */ @Test public void executeAMultiInstanceParallelWithCompletionConditionNumberOfInstances() throws Exception { final String condition = " >= 2"; final int loopMax = 3; final int numberOfExecutedActivities = loopMax; final int numberOfTaskToComplete = 1; withParallelMultiInstanceAttribute(condition, ExpressionConstants.NUMBER_OF_INSTANCES, numberOfExecutedActivities, numberOfTaskToComplete); } /** * Test of completion condition (Parallel multi-instance - Number of terminated instances). * * @throws Exception * @since 6.0 */ @Test public void executeAMultiInstanceParallelWithCompletionConditionNumberOfTerminatedInstances() throws Exception { final String condition = " >= 2"; final int loopMax = 3; final int numberOfExecutedActivities = loopMax; final int numberOfTaskToComplete = 3; withParallelMultiInstanceAttribute(condition, ExpressionConstants.NUMBER_OF_TERMINATED_INSTANCES, numberOfExecutedActivities, numberOfTaskToComplete); } /** * Test of completion condition (Parallel multi-instance - Number of actives instances). * * @throws Exception * @since 6.0 */ @Test public void executeAMultiInstanceParallelWithCompletionConditionNumberOfActiveInstances() throws Exception { final String condition = " == 2"; final int loopMax = 3; final int numberOfExecutedActivities = loopMax; final int numberOfTaskToComplete = 1; withParallelMultiInstanceAttribute(condition, ExpressionConstants.NUMBER_OF_ACTIVE_INSTANCES, numberOfExecutedActivities, numberOfTaskToComplete); } /** * Test of task execution after a sequential multi-instance with user tasks. * * @throws Exception * @since 6.0 */ @Test public void executeTaskAfterMultiInstanceSequential() throws Exception { final int loopMax = 3; final int numberOfExecutedActivities = loopMax + 1; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeTaskAfterMultiInstanceSequential", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addUserTask("step1", ACTOR_NAME).addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(loopMax)); builder.addAutomaticTask("step2").addUserTask("step3", ACTOR_NAME).addTransition("step1", "step2") .addTransition("step2", "step3"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); checkPendingTaskSequentially(numberOfExecutedActivities, processInstance, true); disableAndDeleteProcess(processDefinition); } /** * Test of task execution after a sequential multi-instance with automatics tasks. * * @throws Exception * @since 6.0 */ @Test public void executeTaskAfterMultiInstanceSequentialAuto() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance( "executeTaskAfterMultiInstanceSequentialAuto", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addAutomaticTask("step1").addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(3)); builder.addAutomaticTask("step2").addUserTask("step3", ACTOR_NAME).addTransition("step1", "step2") .addTransition("step2", "step3"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step3", john); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } /** * Test of task execution after a parallel multi-instance with user tasks. * * @throws Exception * @since 6.0 */ @Test public void executeTaskAfterMultiInstanceParallel() throws Exception { final int loopMax = 3; final int numberOfTaskToComplete = 3; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeTaskAfterMultiInstanceSequential", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addUserTask("step1", ACTOR_NAME).addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(loopMax)); builder.addAutomaticTask("step2").addAutomaticTask("step3").addTransition("step1", "step2") .addTransition("step2", "step3"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); checkPendingTaskInParallel(loopMax, numberOfTaskToComplete, processInstance); disableAndDeleteProcess(processDefinition); } /** * Test of task execution after a parallel multi-instance with automatics tasks. * * @throws Exception * @since 6.0 */ @Test public void executeTaskAfterMultiInstanceParallelAuto() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance( "executeTaskAfterMultiInstanceSequentialAuto", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addAutomaticTask("step1").addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(3)); builder.addAutomaticTask("step2").addUserTask("step3", ACTOR_NAME).addTransition("step1", "step2") .addTransition("step2", "step3"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step3", john); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } /** * Test of parallel multi-instance with several users. * * @throws Exception * @since 6.0 */ @Test public void multiInstanceParallelWithSeveralUsers() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeMultiInstanceWithActors", PROCESS_VERSION); builder.addActor(ACTOR_NAME).addDescription("Survey"); builder.addUserTask("step1", ACTOR_NAME).addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(3)); builder.addAutomaticTask("step2").addTransition("step1", "step2"); final List listUsers = new ArrayList<>(); final List listActors = new ArrayList<>(); listUsers.add(john); listActors.add(ACTOR_NAME); listUsers.add(jack); listActors.add(ACTOR_NAME); listUsers.add(jenny); listActors.add(ACTOR_NAME); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), listActors, listUsers); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); checkNbPendingTaskOf(3, john); checkNbPendingTaskOf(3, jack); checkNbPendingTaskOf(3, jenny); // Execute task of multi-instance for John final List pendingTasks1 = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); final HumanTaskInstance pendingTask1 = pendingTasks1.get(0); assignAndExecuteStep(pendingTask1, john.getId()); // Execute task of multi-instance for Jack final List pendingTasks2 = getProcessAPI().getPendingHumanTaskInstances(jack.getId(), 0, 10, null); final HumanTaskInstance pendingTask2 = pendingTasks2.get(0); assignAndExecuteStep(pendingTask2, jack.getId()); // Execute task of multi-instance for Jenny final List pendingTasks3 = getProcessAPI().getPendingHumanTaskInstances(jenny.getId(), 0, 10, null); final HumanTaskInstance pendingTask3 = pendingTasks3.get(0); assignAndExecuteStep(pendingTask3, jenny.getId()); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } /** * Test of sequential multi-instance with several users. */ @Test public void multiInstanceSequentialWithSeveralUsers() throws Exception { final int loopMax = 3; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeMultiInstanceWithActors", PROCESS_VERSION); builder.addActor(ACTOR_NAME).addDescription("Survey"); builder.addIntegerData("numberOfAlreadyFinishedTaskInstances", new ExpressionBuilder().createConstantIntegerExpression(120)); builder.addIntegerData("totalNumberOfTaskInstances", new ExpressionBuilder().createConstantIntegerExpression(120)); builder.addIntegerData("numberOfActiveInstances", new ExpressionBuilder().createConstantIntegerExpression(120)); builder.addIntegerData("numberOfTerminatedInstances", new ExpressionBuilder().createConstantIntegerExpression(120)); UserTaskDefinitionBuilder step1 = builder.addUserTask("step1", ACTOR_NAME); step1.addOperation(new LeftOperandBuilder().createDataLeftOperand("numberOfAlreadyFinishedTaskInstances"), ASSIGNMENT, "=", new ExpressionBuilder().createGroovyScriptExpression("CompletedInstancesCountScript", "numberOfCompletedInstances", "java.lang.Integer", new ExpressionBuilder().createEngineConstant(NUMBER_OF_COMPLETED_INSTANCES))); step1.addOperation(new LeftOperandBuilder().createDataLeftOperand("totalNumberOfTaskInstances"), ASSIGNMENT, "=", new ExpressionBuilder().createGroovyScriptExpression("TotalInstancesCountScript", "numberOfInstances", "java.lang.Integer", new ExpressionBuilder().createEngineConstant(NUMBER_OF_INSTANCES))); step1.addOperation(new LeftOperandBuilder().createDataLeftOperand("numberOfActiveInstances"), ASSIGNMENT, "=", new ExpressionBuilder().createGroovyScriptExpression("numberOfActiveInstancesScript", "numberOfActiveInstances", "java.lang.Integer", new ExpressionBuilder().createEngineConstant(NUMBER_OF_ACTIVE_INSTANCES))); step1.addOperation(new LeftOperandBuilder().createDataLeftOperand("numberOfTerminatedInstances"), ASSIGNMENT, "=", new ExpressionBuilder().createGroovyScriptExpression("numberOfTerminatedInstancesScript", "numberOfTerminatedInstances", "java.lang.Integer", new ExpressionBuilder().createEngineConstant(NUMBER_OF_TERMINATED_INSTANCES))); step1.addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(loopMax)); builder.addAutomaticTask("step2").addTransition("step1", "step2"); final List listUsers = new ArrayList<>(); final List listActors = new ArrayList<>(); listUsers.add(john); listActors.add(ACTOR_NAME); listUsers.add(jack); listActors.add(ACTOR_NAME); listUsers.add(jenny); listActors.add(ACTOR_NAME); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), listActors, listUsers); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // Execute task of multi-instance for John final List pendingTasks1 = checkNbPendingTaskOf(1, john).getPendingHumanTaskInstances(); final HumanTaskInstance pendingTask1 = pendingTasks1.get(0); assignAndExecuteStep(pendingTask1, john.getId()); // Execute task of multi-instance for Jack final List pendingTasks2 = checkNbPendingTaskOf(1, jack).getPendingHumanTaskInstances(); final HumanTaskInstance pendingTask2 = pendingTasks2.get(0); assignAndExecuteStep(pendingTask2, jack.getId()); final SearchOptionsBuilder sob = new SearchOptionsBuilder(0, 0); sob.filter(ArchivedHumanTaskInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, processInstance.getId()); sob.filter(ArchivedHumanTaskInstanceSearchDescriptor.NAME, pendingTask2.getName()); sob.filter(ArchivedHumanTaskInstanceSearchDescriptor.STATE_NAME, "completed"); final SearchOptions searchOptions = sob.done(); await().until(() -> getProcessAPI().searchArchivedHumanTasks(searchOptions).getCount() == 2); int numberOfFinishedTaskInstances = (int) getProcessAPI() .getProcessDataInstance("numberOfAlreadyFinishedTaskInstances", processInstance.getId()) .getValue(); assertThat(numberOfFinishedTaskInstances).isEqualTo(1); // Execute task of multi-instance for Jenny checkNbPendingTaskOf(1, jenny); final List pendingTasks3 = getProcessAPI().getPendingHumanTaskInstances(jenny.getId(), 0, 10, null); final HumanTaskInstance pendingTask3 = pendingTasks3.get(0); assignAndExecuteStep(pendingTask3, jenny.getId()); waitForProcessToFinish(processInstance); numberOfFinishedTaskInstances = (int) getProcessAPI() .getArchivedProcessDataInstance("numberOfAlreadyFinishedTaskInstances", processInstance.getId()) .getValue(); int totalNumberOfTaskInstances = (int) getProcessAPI() .getArchivedProcessDataInstance("totalNumberOfTaskInstances", processInstance.getId()) .getValue(); int numberOfActiveInstances = (int) getProcessAPI() .getArchivedProcessDataInstance("numberOfActiveInstances", processInstance.getId()) .getValue(); int numberOfTerminatedInstances = (int) getProcessAPI() .getArchivedProcessDataInstance("numberOfTerminatedInstances", processInstance.getId()) .getValue(); assertThat(numberOfFinishedTaskInstances).isEqualTo(2); assertThat(totalNumberOfTaskInstances).isEqualTo(3); assertThat(numberOfActiveInstances).isEqualTo(1); assertThat(numberOfTerminatedInstances).isEqualTo(0); disableAndDeleteProcess(processDefinition); } /** * Test of sequential multi-instance with sub-process. * * @throws Exception * @since 6.0 */ @Test public void multiInstanceSequentialWithSubProcess() throws Exception { final int loopMax = 3; // Sub-process definition final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("SubProcessInAMultiInstance", PROCESS_VERSION); builder.addActor(ACTOR_NAME).addAutomaticTask("step1").addAutomaticTask("step2").addTransition("step1", "step2"); final ProcessDefinition subProcess = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression(subProcess.getName()); final Expression targetProcessVersionExpr = new ExpressionBuilder() .createConstantStringExpression(subProcess.getVersion()); final ProcessDefinitionBuilder builderProc = new ProcessDefinitionBuilder() .createNewInstance("executeMultiInstanceSequentialWithSubProcess", "1.1"); builderProc.addActor(ACTOR_NAME).addStartEvent("start") .addCallActivity("callActivity", targetProcessNameExpr, targetProcessVersionExpr) .addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(loopMax)); builderProc.addAutomaticTask("step3").addEndEvent("end").addTransition("start", "callActivity") .addTransition("callActivity", "step3") .addTransition("step3", "end"); final ProcessDefinition mainProcess = deployAndEnableProcessWithActor(builderProc.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(mainProcess.getId()); waitForProcessToFinish(processInstance); disableAndDeleteProcess(mainProcess); disableAndDeleteProcess(subProcess); } private void withMultiInstanceAttribute(final String condition, final ExpressionConstants expressionConstant, final int numberOfExecutedActivities) throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeAStandardLoopUserTask" + condition, PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addIntegerData("a", new ExpressionBuilder().createConstantIntegerExpression(1)); builder.addIntegerData("b", new ExpressionBuilder().createConstantIntegerExpression(2)); builder.addUserTask("step1", ACTOR_NAME) .addMultiInstance( true, new ExpressionBuilder().createGroovyScriptExpression("testWithMultiInstanceAttribute1", "a + b", Integer.class.getName(), new ExpressionBuilder().createDataExpression("a", Integer.class.getName()), new ExpressionBuilder().createDataExpression("b", Integer.class.getName()))) .addCompletionCondition( new ExpressionBuilder().createGroovyScriptExpression("testWithMultiInstanceAttribute2", expressionConstant.getEngineConstantName() + condition, Boolean.class.getName(), new ExpressionBuilder().createEngineConstant(expressionConstant))); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); checkPendingTaskSequentially(numberOfExecutedActivities, processInstance, true); disableAndDeleteProcess(processDefinition); } private void withParallelMultiInstanceAttribute(final String condition, final ExpressionConstants expressionConstant, final int numberOfExecutedActivities, final int numberOfTaskToComplete) throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeParallelUserTask" + condition, PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addIntegerData("a", new ExpressionBuilder().createConstantIntegerExpression(1)); builder.addIntegerData("b", new ExpressionBuilder().createConstantIntegerExpression(2)); builder.addUserTask("step1", ACTOR_NAME) .addMultiInstance( false, new ExpressionBuilder().createGroovyScriptExpression("testWithMultiInstanceAttribute1", "a + b", Integer.class.getName(), new ExpressionBuilder().createDataExpression("a", Integer.class.getName()), new ExpressionBuilder().createDataExpression("b", Integer.class.getName()))) .addCompletionCondition( new ExpressionBuilder().createGroovyScriptExpression("testWithMultiInstanceAttribute2", expressionConstant.getEngineConstantName() + condition, Boolean.class.getName(), new ExpressionBuilder().createEngineConstant(expressionConstant))); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); checkPendingTaskInParallel(numberOfExecutedActivities, numberOfTaskToComplete, processInstance); disableAndDeleteProcess(processDefinition); } private void checkPendingTaskSequentially(final int loopMax, final ProcessInstance processInstance, final boolean mustBeFinished) throws Exception { for (int i = 0; i < loopMax; i++) { waitForPendingTasks(john.getId(), 1); final List pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); final HumanTaskInstance pendingTask = pendingTasks.get(0); assignAndExecuteStep(pendingTask, john.getId()); } if (mustBeFinished) { waitForProcessToFinish(processInstance); } } private void checkPendingTaskInParallel(final int numberOfTask, final int numberOfTaskToCompleteMI, final ProcessInstance processInstance) throws Exception { final List pendingTasks = waitForPendingTasks(john.getId(), numberOfTask); for (int i = 0; i < numberOfTaskToCompleteMI; i++) { assignAndExecuteStep(pendingTasks.get(i), john.getId()); } waitForProcessToFinish(processInstance); final int nbAbortedActivities = numberOfTask - numberOfTaskToCompleteMI; checkNbOfArchivedActivities(processInstance, nbAbortedActivities); } @Test public void multiInstanceVoteUseCase() throws Exception { // Build process definition final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeMultiInstanceWithSeveralActors", PROCESS_VERSION); builder.addActor(ACTOR_NAME).addDescription("Survey"); final String loopDataInputName = "listOfUserId"; final String exprListUserIds = "[" + john.getId() + "l," + jack.getId() + "l," + jenny.getId() + "l]"; builder.addData(loopDataInputName, List.class.getName(), new ExpressionBuilder().createGroovyScriptExpression("createListUserId", exprListUserIds, List.class.getName())); final UserTaskDefinitionBuilder userTaskBuilder = new UserTaskDefinitionBuilder(builder, (FlowElementContainerDefinitionImpl) builder.getProcess() .getProcessContainer(), "step1", ACTOR_NAME); userTaskBuilder .addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilterWithAutoAssign", PROCESS_VERSION) .addInput("userId", new ExpressionBuilder().createDataExpression("userIdValue", Long.class.getName())); userTaskBuilder.addData("userIdValue", Long.class.getName(), null); userTaskBuilder.addMultiInstance(false, loopDataInputName).addDataInputItemRef("userIdValue"); userTaskBuilder.addDisplayName(new ExpressionBuilder().createConstantStringExpression("displayName")); builder.addAutomaticTask("step2").addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithTestFilter(ACTOR_NAME, builder); // Start process, and wait all multiinstancied user tasks final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step1"); waitForUserTask(processInstance, "step1"); waitForUserTask(processInstance, "step1"); // Get comments and check it final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()); searchOptionsBuilder.sort(SearchCommentsDescriptor.CONTENT, Order.ASC); final List comments = getProcessAPI().searchComments(searchOptionsBuilder.done()).getResult(); assertEquals("The task \"displayName\" is now assigned to " + jack.getUserName(), comments.get(0).getContent()); assertEquals("The task \"displayName\" is now assigned to " + jenny.getUserName(), comments.get(1).getContent()); assertEquals("The task \"displayName\" is now assigned to " + john.getUserName(), comments.get(2).getContent()); disableAndDeleteProcess(processDefinition); } private ProcessDefinition deployProcessWithTestFilter(final String actorName, final ProcessDefinitionBuilder designProcessDefinition) throws Exception { final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition( designProcessDefinition.done()); final List impl = generateFilterImplementations("TestFilterWithAutoAssign"); for (final BarResource barResource : impl) { businessArchiveBuilder.addUserFilters(barResource); } final List generateFilterDependencies = new ArrayList<>(1); final byte[] data = IOUtil.generateJar(TestFilterWithAutoAssign.class); generateFilterDependencies.add(new BarResource("TestFilterWithAutoAssign.jar", data)); for (final BarResource barResource : generateFilterDependencies) { businessArchiveBuilder.addClasspathResource(barResource); } final ProcessDefinition processDefinition = deployProcess(businessArchiveBuilder.done()); getProcessAPI().addUserToActor(actorName, processDefinition, john.getId()); getProcessAPI().addUserToActor(actorName, processDefinition, jack.getId()); getProcessAPI().addUserToActor(actorName, processDefinition, jenny.getId()); getProcessAPI().enableProcess(processDefinition.getId()); return processDefinition; } private List generateFilterImplementations(final String filterName) throws IOException { final List resources = new ArrayList<>(1); final InputStream inputStream = TestConnector.class.getClassLoader() .getResourceAsStream("org/bonitasoft/engine/filter/user/" + filterName + ".impl"); final byte[] data = IOUtil.getAllContentFrom(inputStream); inputStream.close(); resources.add(new BarResource(filterName + ".impl", data)); return resources; } @Test public void multiInstanceWithANullList_should_put_the_task_in_failed_state() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("multiInstanceWithAnEmptyList", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addData("list", List.class.getName(), null); final UserTaskDefinitionBuilder taskDefinitionBuilder = builder.addUserTask("step1", ACTOR_NAME); taskDefinitionBuilder.addData("listValue", String.class.getName(), null); taskDefinitionBuilder.addMultiInstance(true, "list").addDataInputItemRef("listValue"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForFlowNodeInFailedState(processInstance); disableAndDeleteProcess(processDefinition); } @Test public void should_not_create_instance_when_multi_instantiated_on_empty_list() throws Exception { //given ProcessDefinitionBuilder p1 = new ProcessDefinitionBuilder().createNewInstance("p1", "1"); p1.addData("loop", List.class.getName(), new ExpressionBuilder().createGroovyScriptExpression("list", "[]", List.class.getName())); p1.addUserTask("step1", "john").addMultiInstance(true, "loop"); p1.addUserTask("step3", "john").addMultiInstance(false, "loop"); p1.addActor("john"); p1.addUserTask("step2", "john"); p1.addUserTask("step4", "john"); p1.addTransition("step1", "step2"); p1.addTransition("step3", "step4"); ProcessDefinition deploy = getProcessAPI().deploy(p1.done()); List actors = getProcessAPI().getActors(deploy.getId(), 0, 1, ActorCriterion.NAME_ASC); getProcessAPI().addUserToActor(actors.get(0).getId(), getIdentityAPI().getUserByUserName("john").getId()); getProcessAPI().enableProcess(deploy.getId()); //when ProcessInstance processInstance = getProcessAPI().startProcess(deploy.getId()); //no task step1 //then waitForUserTask(processInstance.getId(), "step2"); waitForUserTask(processInstance.getId(), "step4"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, processInstance.getId()); builder.leftParenthesis().filter(ArchivedHumanTaskInstanceSearchDescriptor.NAME, "step1").or() .filter(ArchivedHumanTaskInstanceSearchDescriptor.NAME, "step3").rightParenthesis(); builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.STATE_NAME, ActivityStates.COMPLETED_STATE); SearchResult archivedHumanTaskInstanceSearchResult = getProcessAPI() .searchArchivedHumanTasks(builder.done()); assertThat(archivedHumanTaskInstanceSearchResult.getResult()).isEmpty(); //clean up disableAndDeleteProcess(deploy); } @Test public void multiInstance_with_a_define_instance_number_should_be_able_to_save_value_into_a_data_output_list() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("multiInstanceProcess", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addData("list", List.class.getName(), new ExpressionBuilder().createGroovyScriptExpression("EmptyList", "[]", List.class.getName())); final UserTaskDefinitionBuilder taskDefinitionBuilder = builder.addUserTask("step1", ACTOR_NAME); taskDefinitionBuilder.addData("listValue", String.class.getName(), null); taskDefinitionBuilder.addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(1)) .addDataOutputItemRef("listValue") .addLoopDataOutputRef("list"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); long step1 = waitForUserTask(processInstance.getId(), "step1"); getProcessAPI().assignUserTask(step1, john.getId()); getProcessAPI().executeUserTask(john.getId(), step1, null); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Test public void should_abort_children_of_multi_instance_when_skipping_the_multi_instance() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("skippedMultiInstance", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addUserTask("step1", ACTOR_NAME).addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(2)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step1"); HumanTaskInstance step1 = waitForUserTaskAndGetIt(processInstance, "step1"); FlowNodeInstance multiInstance = getProcessAPI().getFlowNodeInstance(step1.getParentContainerId()); assertThat(multiInstance.getType()).isEqualTo(FlowNodeType.MULTI_INSTANCE_ACTIVITY); getProcessAPI().setActivityStateByName(multiInstance.getId(), ActivityStates.SKIPPED_STATE); waitForProcessToFinish(processInstance); SearchResult archivedActivityInstance = getProcessAPI() .searchArchivedActivities(new SearchOptionsBuilder(0, 10) .filter(ROOT_PROCESS_INSTANCE_ID, processInstance.getId()) .filter(STATE_NAME, ABORTED.getStateName()).done()); assertThat(archivedActivityInstance.getResult()).hasSize(2); disableAndDeleteProcess(processDefinition); } @Test public void should_cancel_multi_instance_with_call_activity_and_boundary() throws Exception { ProcessDefinition subProcessDefinition = deployAndEnableProcessWithActor(new ProcessDefinitionBuilder() .createNewInstance("subProcess", "1.0") .addActor("actor") .addAutomaticTask("task1") .addUserTask("task2", "actor") .addTransition("task1", "task2").getProcess(), "actor", user); ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("multi+call+boundary", "1.0"); CallActivityBuilder callActivityBuilder = processDefinitionBuilder .addCallActivity("call", new ExpressionBuilder().createConstantStringExpression("subProcess"), new ExpressionBuilder().createConstantStringExpression("1.0")); callActivityBuilder.addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(10)); callActivityBuilder.addBoundaryEvent("boundary").addSignalEventTrigger("signal"); processDefinitionBuilder.addAutomaticTask("auto1"); processDefinitionBuilder.addTransition("boundary", "auto1"); ProcessDefinition processDefinition = deployAndEnableProcess(processDefinitionBuilder.getProcess()); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt("task2", user); waitForUserTask("task2"); getProcessAPI().cancelProcessInstance(processInstance.getId()); waitForProcessToBeInState(processInstance, ProcessInstanceState.CANCELLED); await().until(() -> getProcessAPI().searchProcessInstances(new SearchOptionsBuilder(0, 100).done()) .getResult().isEmpty()); deleteProcessInstanceAndArchived(subProcessDefinition, processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/PendingTasksIT.java ================================================ /** * 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.activity; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.List; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.actor.ActorCriterion; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.TaskPriority; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.connectors.VariableStorage; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserMembership; import org.bonitasoft.engine.identity.UserNotFoundException; import org.bonitasoft.engine.identity.UserSearchDescriptor; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.util.CollectionUtils; public class PendingTasksIT extends TestWithTechnicalUser { private static final String JACK = "jack"; private User john; private User jack; @Override @Before public void before() throws Exception { super.before(); john = createUser(USERNAME, PASSWORD); jack = createUser(JACK, PASSWORD); } @Override @After public void after() throws Exception { deleteUser(USERNAME); deleteUser(JACK); VariableStorage.clearAll(); super.after(); } @Test public void searchMyAvailableHumanTasks() throws Exception { final User user = createUser("Barnabooth", "Strongwood"); final User user2 = createUser("Unknown", "Stranger"); final Group group = createGroup("un_used"); // Process def with 2 instances: final String otherActor = "NotForMe"; final ProcessDefinitionBuilder processBuilder1 = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_branches", "1.0"); processBuilder1.addActor(ACTOR_NAME).addActor(otherActor); processBuilder1.addUserTask("step1", ACTOR_NAME).addUserTask("step2", ACTOR_NAME) .addUserTask("step3", otherActor).addUserTask("step4", otherActor); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder1.done()) .done()); final List actors = getProcessAPI().getActors(processDefinition.getId(), 0, 2, ActorCriterion.NAME_ASC); getProcessAPI().addUserToActor(actors.get(0).getId(), user.getId()); getProcessAPI().addGroupToActor(actors.get(1).getId(), group.getId()); getProcessAPI().enableProcess(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId()); // Wait for tasks in READY state: waitForUserTask(processInstance, "step1"); final long step2Id = waitForUserTask(processInstance, "step2"); final long step3Id = waitForUserTask(processInstance, "step3"); waitForUserTask(processInstance, "step4"); waitForUserTask(processInstance2, "step1"); waitForUserTask(processInstance2, "step2"); waitForUserTask(processInstance2, "step3"); waitForUserTask(processInstance2, "step4"); // 4 tasks should already be pending for me: final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, Order.DESC); SearchResult humanTasksSearch = getProcessAPI().searchMyAvailableHumanTasks(user.getId(), searchOptionsBuilder.done()); assertEquals(4, humanTasksSearch.getCount()); List searchResult = humanTasksSearch.getResult(); assertThat(searchResult).extracting(HumanTaskInstance::getRootContainerId) .isSortedAccordingTo(Comparator.reverseOrder()); // Force assigning 'task3' (DESC name sort) to me (event though I am not an actor for it): getProcessAPI().assignUserTask(step3Id, user.getId()); // 5 tasks should now be available for me: humanTasksSearch = getProcessAPI().searchMyAvailableHumanTasks(user.getId(), searchOptionsBuilder.done()); assertEquals(5, humanTasksSearch.getCount()); // Force assigning 'task2' (DESC name sort) to someone else than me (event though he is not an actor for it): getProcessAPI().assignUserTask(step2Id, user2.getId()); // 4 tasks should now be available for me: humanTasksSearch = getProcessAPI().searchMyAvailableHumanTasks(user.getId(), searchOptionsBuilder.done()); assertEquals(4, humanTasksSearch.getCount()); // 5 tasks should be available for me when calling searchPendingOrAssignedToUserOrTakenTasks // because I can see tasks assigned to others humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(user.getId(), searchOptionsBuilder.done()); assertEquals(5, humanTasksSearch.getCount()); disableAndDeleteProcess(processDefinition); deleteUsers(user, user2); deleteGroups(group); } @Test public void searchMyAvailableHumanTasksWithActorFilters() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); final User user = createUser("Barnabooth", "Strongwood"); //Build 2 processes with actor filters final ProcessDefinitionBuilder jackDesignProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("processWithUserFilter", "1.0"); jackDesignProcessDefinition.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); jackDesignProcessDefinition.addAutomaticTask("step1"); final UserTaskDefinitionBuilder addUserTask = jackDesignProcessDefinition.addUserTask("step2", ACTOR_NAME); addUserTask.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilter", "1.0").addInput("userId", // filter selects "jack" as candidate: new ExpressionBuilder().createConstantLongExpression(jack.getId())); jackDesignProcessDefinition.addTransition("step1", "step2"); final ProcessDefinitionBuilder userDesignProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("processWithUserFilter", "2.0"); userDesignProcessDefinition.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); userDesignProcessDefinition.addAutomaticTask("step1"); userDesignProcessDefinition.addUserTask("step2", ACTOR_NAME) .addUserFilter("test2", "org.bonitasoft.engine.filter.user.testFilter", "1.0").addInput("userId", // filter selects "user" as candidate: new ExpressionBuilder().createConstantLongExpression(user.getId())); userDesignProcessDefinition.addTransition("step1", "step2"); // Only john is member of the actor, for both processes: final ProcessDefinition jackProcessDefinition = deployProcessWithTestFilter(jackDesignProcessDefinition, ACTOR_NAME, john, "TestFilter"); final ProcessDefinition userProcessDefinition = deployProcessWithTestFilter(userDesignProcessDefinition, ACTOR_NAME, john, "TestFilter"); // Start first process ProcessInstance jackProcessInstance = getProcessAPI().startProcess(jackProcessDefinition.getId()); long stepIdProc1 = waitForUserTask(jackProcessInstance, "step2"); // actor filter allows jack to see it SearchResult humanTasksSearch = getProcessAPI() .searchPendingOrAssignedToUserOrAssignedToOthersTasks(jack.getId(), searchOptionsBuilder.done()); assertEquals(1, humanTasksSearch.getCount()); // but not john humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(john.getId(), searchOptionsBuilder.done()); assertEquals(0, humanTasksSearch.getCount()); //john takes the task. This overrides the actor filter getProcessAPI().assignUserTask(stepIdProc1, john.getId()); // jack still sees the task humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(jack.getId(), searchOptionsBuilder.done()); assertEquals(1, humanTasksSearch.getCount()); // john sees the task because he is now assigned to it humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(john.getId(), searchOptionsBuilder.done()); assertEquals(1, humanTasksSearch.getCount()); // assign jack getProcessAPI().assignUserTask(stepIdProc1, jack.getId()); // jack sees the task humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(jack.getId(), searchOptionsBuilder.done()); assertEquals(1, humanTasksSearch.getCount()); // john does not see the task anymore humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(john.getId(), searchOptionsBuilder.done()); assertEquals(0, humanTasksSearch.getCount()); //start proc 2 ProcessInstance userProcessInstance = getProcessAPI().startProcess(userProcessDefinition.getId()); waitForUserTask(userProcessInstance, "step2"); // Proc2 // Non-actor user sees the task, because actor filter allows him specifically humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(user.getId(), searchOptionsBuilder.done()); assertEquals(1, humanTasksSearch.getCount()); // actor member does not see it, because actor filter does not allow him to humanTasksSearch = getProcessAPI().searchPendingOrAssignedToUserOrAssignedToOthersTasks(john.getId(), searchOptionsBuilder.done()); assertEquals(0, humanTasksSearch.getCount()); disableAndDeleteProcess(jackProcessDefinition); disableAndDeleteProcess(userProcessDefinition); deleteUser(user); } @Test public void getPendingHumanTaskInstancesInTwoProcesses() throws Exception { // 1. create a user 'test' User test; try { test = getIdentityAPI().getUserByUserName(JACK); } catch (final UserNotFoundException e) { test = getIdentityAPI().createUser(JACK, PASSWORD); } final long userId = test.getId(); // 2. install two processes final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addActor("myActor"); processBuilder.addUserTask("Request", "myActor"); processBuilder.addUserTask("Approval", "myActor"); processBuilder.addTransition("Request", "Approval"); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive(); final BusinessArchive businessArchive = businessArchiveBuilder.setProcessDefinition(designProcessDefinition) .done(); final ProcessDefinitionBuilder processBuilder2 = new ProcessDefinitionBuilder() .createNewInstance("SecontProcess", "1.0"); processBuilder2.addActor("myActor").addUserTask("Request", "myActor").addUserTask("Approval", "myActor") .addTransition("Request", "Approval"); final DesignProcessDefinition designProcessDefinition2 = processBuilder2.done(); final BusinessArchive businessArchive2 = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition2).done(); final ProcessDefinition processDefinition1 = deployProcess(businessArchive); final ProcessDefinition processDefinition2 = deployProcess(businessArchive2); // 3. assign user 'test' to actor of both processes ActorInstance processActor = getProcessAPI() .getActors(processDefinition1.getId(), 0, 1, ActorCriterion.NAME_ASC).get(0); getProcessAPI().addUserToActor(processActor.getId(), test.getId()); processActor = getProcessAPI().getActors(processDefinition2.getId(), 0, 1, ActorCriterion.NAME_ASC).get(0); getProcessAPI().addUserToActor(processActor.getId(), test.getId()); // 4. enable both processes getProcessAPI().enableProcess(processDefinition1.getId()); getProcessAPI().enableProcess(processDefinition2.getId()); // 5. start both processes final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition1.getId()); waitForUserTask(processInstance1, "Request"); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId()); waitForUserTask(processInstance2, "Request"); // 6.check Pending tasks list. The below exception appears. final List userTaskInstances = getProcessAPI().getPendingHumanTaskInstances(userId, 0, 10, ActivityInstanceCriterion.NAME_ASC); assertNotNull(userTaskInstances); assertEquals(2, userTaskInstances.size()); // Clean up deleteUser(test); disableAndDeleteProcess(processDefinition1, processDefinition2); } @Test public void getPendingHumanTaskInstancePriorityAndExpectedEndDate() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); final TaskPriority priority = TaskPriority.HIGHEST; final int oneDay = 24 * 60 * 60 * 1000; processBuilder.addActor(ACTOR_NAME).addDescription("Delivery all day and night long") .addUserTask("deliver", ACTOR_NAME).addPriority(priority.name()) .addExpectedDuration(oneDay); final DesignProcessDefinition processDesignDefinition = processBuilder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDesignDefinition).done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, john); final Date before = new Date(); Thread.sleep(20); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "deliver"); final List activityInstances = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT); Thread.sleep(20); final Date after = new Date(); assertEquals(1, activityInstances.size()); final HumanTaskInstance humanTaskInstance = activityInstances.get(0); assertEquals(priority, humanTaskInstance.getPriority()); final long time = humanTaskInstance.getExpectedEndDate().getTime(); assertTrue(before.getTime() + oneDay < time && time < after.getTime() + oneDay); disableAndDeleteProcess(processDefinition); } @Test public void taskAlreadyClaimedByMe() throws Exception { final User user = createUser("login", "password"); final ProcessDefinition processDefinition = deployProcessWithUserTask(user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); final long taskId = waitForUserTask(startProcess, "Request"); getProcessAPI().assignUserTask(taskId, user.getId()); getProcessAPI().assignUserTask(taskId, user.getId()); assertEquals(1, getProcessAPI() .getAssignedHumanTaskInstances(user.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT).size()); deleteUser(user); disableAndDeleteProcess(processDefinition); } @Test public void taskAlreadyClaimedByOther() throws Exception { final User user1 = createUser("login1", "password"); final User user2 = createUser("login2", "password"); final ProcessDefinition processDefinition = deployProcessWithUserTask(user1); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); final long taskId = waitForUserTask(startProcess, "Request"); getProcessAPI().assignUserTask(taskId, user1.getId()); getProcessAPI().assignUserTask(taskId, user2.getId()); logout(); loginOnDefaultTenantWith("login1", "password"); assertEquals(1, getProcessAPI() .getAssignedHumanTaskInstances(user2.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT).size()); assertEquals(0, getProcessAPI() .getAssignedHumanTaskInstances(user1.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT).size()); deleteUser(user1); deleteUser(user2); disableAndDeleteProcess(processDefinition); } @Test public void searchTasksWithSpecialCharsInFreeText() throws Exception { final User user = createUser("login", "password"); final String taskName = "étape"; final String processName = "никола"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(processName, "1"); processBuilder.addActor("myActor"); processBuilder.addUserTask(taskName, "myActor"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), "myActor", user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, taskName); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.searchTerm(taskName); final SearchResult taskRes = getProcessAPI().searchHumanTaskInstances(builder.done()); assertEquals(1, taskRes.getCount()); assertEquals(taskName, taskRes.getResult().get(0).getName()); builder.searchTerm(processName); final SearchResult procDefRes = getProcessAPI() .searchProcessDeploymentInfos(builder.done()); assertEquals(1, procDefRes.getCount()); assertEquals(processName, procDefRes.getResult().get(0).getName()); deleteUser(user); disableAndDeleteProcess(processDefinition); } private ProcessDefinition deployProcessWithUserTask(final User user1) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addActor("myActor"); processBuilder.addUserTask("Request", "myActor"); return deployAndEnableProcessWithActor(processBuilder.done(), "myActor", user1); } @Test public void taskAlreadyReleased() throws Exception { final User user = createUser("login1", "password"); final ProcessDefinition processDefinition = deployProcessWithUserTask(user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); final long taskId = waitForUserTask(startProcess, "Request"); getProcessAPI().assignUserTask(taskId, user.getId()); getProcessAPI().releaseUserTask(taskId); getProcessAPI().releaseUserTask(taskId); deleteUser(user); disableAndDeleteProcess(processDefinition); } @Test public void actorMappedToGroup() throws Exception { final Group mainGroup = createGroup("main"); final Role member = createRole("member"); final UserMembership m1 = getIdentityAPI().addUserMembership(john.getId(), mainGroup.getId(), member.getId()); final UserMembership m2 = getIdentityAPI().addUserMembership(jack.getId(), mainGroup.getId(), member.getId()); final ProcessDefinition processDefinition = deployProcessMappedToGroup(mainGroup); getProcessAPI().startProcess(processDefinition.getId()); waitForPendingTasks(john.getId(), 1); waitForPendingTasks(jack.getId(), 1); disableAndDeleteProcess(processDefinition); deleteUserMembership(m1.getId()); deleteUserMembership(m2.getId()); deleteGroups(mainGroup); deleteRoles(member); } @Test public void actorMappedToDifferrentGroup() throws Exception { final Group mainGroup = createGroup("main"); final Group secondGroup = createGroup("second"); final Role member = createRole("member"); final UserMembership m1 = getIdentityAPI().addUserMembership(john.getId(), mainGroup.getId(), member.getId()); final UserMembership m2 = getIdentityAPI().addUserMembership(jack.getId(), secondGroup.getId(), member.getId()); final ProcessDefinition processDefinition = deployProcessMappedToGroup(mainGroup); getProcessAPI().startProcess(processDefinition.getId()); waitForPendingTasks(john.getId(), 1); final List pendingHumanTaskInstances = getProcessAPI() .getPendingHumanTaskInstances(jack.getId(), 0, 10, null); assertTrue(pendingHumanTaskInstances.isEmpty()); disableAndDeleteProcess(processDefinition); deleteUserMembership(m1.getId()); deleteUserMembership(m2.getId()); deleteGroups(secondGroup, mainGroup); deleteRoles(member); } @Test public void actorMappedToGrandChildGroup() throws Exception { final Group mainGroup = createGroup("main"); final Group childGroup = createGroup("child", "/main"); final Group grandChildGroup = createGroup("gChild", "/main/child"); assertEquals(childGroup.getPath(), grandChildGroup.getParentPath()); assertEquals(mainGroup.getPath(), childGroup.getParentPath()); final Role member = createRole("member"); final UserMembership m1 = getIdentityAPI().addUserMembership(john.getId(), mainGroup.getId(), member.getId()); final UserMembership m2 = getIdentityAPI().addUserMembership(jack.getId(), grandChildGroup.getId(), member.getId()); final ProcessDefinition processDefinition = deployProcessMappedToGroup(mainGroup); try { getProcessAPI().startProcess(processDefinition.getId()); waitForPendingTasks(john.getId(), 1); waitForPendingTasks(jack.getId(), 1); } finally { disableAndDeleteProcess(processDefinition); deleteUserMembership(m1.getId()); deleteUserMembership(m2.getId()); deleteGroups(grandChildGroup, childGroup, mainGroup); deleteRoles(member); } } @Test public void actorMappedToChildGroup() throws Exception { final Group mainGroup = createGroup("main"); final Group secondGroup = createGroup("second", "/main"); final Role member = createRole("member"); final UserMembership m1 = getIdentityAPI().addUserMembership(john.getId(), mainGroup.getId(), member.getId()); final UserMembership m2 = getIdentityAPI().addUserMembership(jack.getId(), secondGroup.getId(), member.getId()); final ProcessDefinition processDefinition = deployProcessMappedToGroup(mainGroup); try { getProcessAPI().startProcess(processDefinition.getId()); waitForPendingTasks(john.getId(), 1); waitForPendingTasks(jack.getId(), 1); } finally { disableAndDeleteProcess(processDefinition); deleteUserMembership(m1.getId()); deleteUserMembership(m2.getId()); deleteGroups(secondGroup, mainGroup); deleteRoles(member); } } private ProcessDefinition deployProcessMappedToGroup(final Group mainGroup) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addActor("myActor"); processBuilder.addUserTask("Request", "myActor"); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive(); final BusinessArchive businessArchive = businessArchiveBuilder.setProcessDefinition(designProcessDefinition) .done(); final ProcessDefinition processDefinition = deployProcess(businessArchive); // map actor to group final ActorInstance processActor = getProcessAPI() .getActors(processDefinition.getId(), 0, 1, ActorCriterion.NAME_ASC).get(0); getProcessAPI().addGroupToActor(processActor.getId(), mainGroup.getId()); getProcessAPI().enableProcess(processDefinition.getId()); return processDefinition; } @Test public void searchPossibleUsersOfTaskUserActor() throws Exception { loginOnDefaultTenantWith(USERNAME, PASSWORD); final Group group = createGroup("group"); final Role role = createRole("role"); final User jaakko = createUser("jaakko", PASSWORD); createUserMembership(jaakko.getUserName(), role.getName(), group.getPath()); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("assign", "5.0"); designProcessDefinition.addActor("acme"); designProcessDefinition.addUserTask("step1", "acme"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), "acme", jaakko); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC); final List possibleUsers = getProcessAPI() .searchUsersWhoCanExecutePendingHumanTask(step1Id, builder.done()).getResult(); assertEquals(1, possibleUsers.size()); assertEquals(jaakko, possibleUsers.get(0)); // cleanup: deleteGroups(group); deleteRoles(role); deleteUser(jaakko); disableAndDeleteProcess(processDefinition); } @Test public void searchPossibleUsersOfTaskUserActorWithoutMembership() throws Exception { loginOnDefaultTenantWith(USERNAME, PASSWORD); final Group group = createGroup("group"); final Role role = createRole("role"); final User jaakko = createUser("jaakko", PASSWORD); // createUserMembership(jack.getUserName(), role.getName(), group.getName()); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("assign", "5.0"); designProcessDefinition.addActor("acme"); designProcessDefinition.addUserTask("step1", "acme"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), "acme", jaakko); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC); final SearchResult searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id, builder.done()); assertEquals(1, searchResult.getCount()); final List possibleUsers = searchResult.getResult(); assertEquals(1, possibleUsers.size()); assertEquals(jaakko, possibleUsers.get(0)); // cleanup: deleteGroups(group); deleteRoles(role); deleteUser(jaakko); disableAndDeleteProcess(processDefinition); } @Test public void searchPossibleUsersOfTaskRoleActor() throws Exception { loginOnDefaultTenantWith(USERNAME, PASSWORD); final Group group = createGroup("group"); final Role role = createRole("role"); final User jaakko = createUser("jaakko", PASSWORD); createUserMembership(jaakko.getUserName(), role.getName(), group.getPath()); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("assign", "5.0"); designProcessDefinition.addActor("acme"); designProcessDefinition.addUserTask("step1", "acme"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), "acme", role); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC); final SearchResult searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id, builder.done()); assertEquals(1, searchResult.getCount()); final List possibleUsers = searchResult.getResult(); assertEquals(1, possibleUsers.size()); assertEquals(jaakko, possibleUsers.get(0)); // cleanup: deleteGroups(group); deleteRoles(role); deleteUser(jaakko); disableAndDeleteProcess(processDefinition); } @Test public void searchPossibleUsersOfTaskGroupActor() throws Exception { loginOnDefaultTenantWith(USERNAME, PASSWORD); final Group group = createGroup("group"); final Role role = createRole("role"); final User jaakko = createUser("jaakko", PASSWORD); createUserMembership(jaakko.getUserName(), role.getName(), group.getPath()); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("assign", "5.0"); designProcessDefinition.addActor("acme"); designProcessDefinition.addUserTask("step1", "acme"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), "acme", group); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC); final SearchResult searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id, builder.done()); assertEquals(1, searchResult.getCount()); final List possibleUsers = searchResult.getResult(); assertEquals(1, possibleUsers.size()); assertEquals(jaakko, possibleUsers.get(0)); // cleanup: deleteGroups(group); deleteRoles(role); deleteUser(jaakko); disableAndDeleteProcess(processDefinition); } @Test public void searchPossibleUsersOfTaskShouldReturnAllUsersInThePaginationRange() throws Exception { loginOnDefaultTenantWith(USERNAME, PASSWORD); final Group group = createGroup("group"); final Role role = createRole("role"); final int USER_LIST_SIZE = 21; final List users = new ArrayList<>(USER_LIST_SIZE); for (int i = 0; i < USER_LIST_SIZE; i++) { final User newUser = createUser("user_" + i, "pwd"); users.add(newUser); createUserMembership(newUser.getUserName(), role.getName(), group.getName()); } final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("getPossible_pagination", "1.1"); final String activityName = "step1"; designProcessDefinition.addActor(ACTOR_NAME); designProcessDefinition.addUserTask(activityName, ACTOR_NAME); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), ACTOR_NAME, users); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, activityName); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 30); builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC); final SearchResult searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id, builder.done()); assertEquals(21, searchResult.getCount()); final List possibleUsers = searchResult.getResult(); // make sure the list is not limited to 20: assertEquals(21, possibleUsers.size()); // cleanup: deleteGroups(group); deleteRoles(role); deleteUsers(users); disableAndDeleteProcess(processDefinition); } @Test public void searchPossibleUsersOfTaskSubGroupActor() throws Exception { loginOnDefaultTenantWith(USERNAME, PASSWORD); final Group group = createGroup("group"); final Group group2 = createGroup("gr", group.getPath()); final Role role = createRole("role"); final User jaakko = createUser("jaakko", PASSWORD); createUserMembership(jack.getUserName(), role.getName(), group.getPath()); createUserMembership(jaakko.getUserName(), role.getName(), group2.getPath()); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("assign", "5.0"); designProcessDefinition.addActor("acme"); designProcessDefinition.addUserTask("step1", "acme"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), "acme", group); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(UserSearchDescriptor.USER_NAME, Order.ASC); SearchResult searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id, builder.done()); assertEquals(2, searchResult.getCount()); List possibleUsers = searchResult.getResult(); assertEquals(2, possibleUsers.size()); assertEquals(jaakko, possibleUsers.get(0)); assertEquals(jack, possibleUsers.get(1)); builder = new SearchOptionsBuilder(1, 10); builder.sort(UserSearchDescriptor.USER_NAME, Order.ASC); searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id, builder.done()); assertEquals(2, searchResult.getCount()); possibleUsers = searchResult.getResult(); assertEquals(1, possibleUsers.size()); assertEquals(jack, possibleUsers.get(0)); // cleanup: deleteGroups(group); deleteRoles(role); deleteUser(jaakko); disableAndDeleteProcess(processDefinition); } @Test public void searchPossibleUsersOfFilteredTask() throws Exception { loginOnDefaultTenantWith(USERNAME, PASSWORD); final Group group = createGroup("group"); final Group group2 = createGroup("gr", group.getPath()); final Role role = createRole("role"); final User jaakko = createUser("jaakko", PASSWORD); createUserMembership(jaakko.getUserName(), role.getName(), group2.getPath()); createUserMembership(jack.getUserName(), role.getName(), group2.getPath()); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("assign", "5.2"); designProcessDefinition.addActor("acme"); final UserTaskDefinitionBuilder taskDefinitionBuilder = designProcessDefinition.addUserTask("step1", "acme"); taskDefinitionBuilder.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilter", "1.0").addInput( "userId", new ExpressionBuilder().createConstantLongExpression(jaakko.getId())); final UserTaskDefinitionBuilder definitionBuilder = designProcessDefinition.addUserTask("step2", "acme"); definitionBuilder.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilter", "1.0").addInput( "userId", new ExpressionBuilder().createConstantLongExpression(jack.getId())); final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, "acme", jaakko, "TestFilter"); getProcessAPI().addUserToActor("acme", processDefinition, jack.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); waitForUserTask(processInstance, "step2"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 2); builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC); final SearchResult searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id, builder.done()); assertEquals(1, searchResult.getCount()); final List possibleUsers = searchResult.getResult(); assertEquals(1, possibleUsers.size()); assertEquals(jaakko, possibleUsers.get(0)); // cleanup: deleteGroups(group); deleteRoles(role); deleteUser(jaakko); disableAndDeleteProcess(processDefinition); } @Test public void searchPossibleUsersShouldReturnThoseStartingWithSearchedNamed() throws Exception { loginOnDefaultTenantWith(USERNAME, PASSWORD); final Group group = createGroup("group"); final Group group2 = createGroup("gr", group.getPath()); final Role role = createRole("role"); final User jaakko = createUser("jaakko", PASSWORD); createUserMembership(jack.getUserName(), role.getName(), group.getPath()); createUserMembership(jaakko.getUserName(), role.getName(), group2.getPath()); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("assign", "5.0"); designProcessDefinition.addActor("acme"); designProcessDefinition.addUserTask("step1", "acme"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), "acme", group); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(UserSearchDescriptor.USER_NAME, Order.ASC); builder.searchTerm("jac"); SearchResult searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id, builder.done()); assertEquals(1, searchResult.getCount()); List possibleUsers = searchResult.getResult(); assertEquals(1, possibleUsers.size()); assertEquals(jack, possibleUsers.get(0)); builder = new SearchOptionsBuilder(0, 1); builder.sort(UserSearchDescriptor.USER_NAME, Order.ASC); builder.searchTerm("jaa"); searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id, builder.done()); assertEquals(1, searchResult.getCount()); possibleUsers = searchResult.getResult(); assertEquals(1, possibleUsers.size()); assertEquals(jaakko, possibleUsers.get(0)); // cleanup: deleteGroups(group); deleteRoles(role); deleteUser(jaakko); disableAndDeleteProcess(processDefinition); } @Test public void getPossibleUsersOfUnknownTask() throws Exception { loginOnDefaultTenantWith(USERNAME, PASSWORD); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC); final SearchResult searchResult = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(-156L, builder.done()); assertEquals(0, searchResult.getCount()); final List possibleUsers = searchResult.getResult(); assertTrue(CollectionUtils.isEmpty(possibleUsers)); } @Test public void test() throws Exception { final Group mainGroup = createGroup("main"); final Role member = createRole("member"); final UserMembership m1 = getIdentityAPI().addUserMembership(john.getId(), mainGroup.getId(), member.getId()); final ProcessDefinition processDefinition = deployProcessMappedToGroup(mainGroup); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance.getId(), "Request"); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(startProcess, "Request", john); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("secondProcess", "1.0"); processBuilder.addActor("myActor").addActor("myActor2"); processBuilder.addUserTask("Request1", "myActor2"); processBuilder.addUserTask("Request2", "myActor"); processBuilder.addTransition("Request2", "Request1"); final ProcessDefinition definition = deployAndEnableProcessWithActor(processBuilder.done(), Arrays.asList("myActor", "myActor2"), Arrays.asList(john, jack)); final ProcessInstance instance = getProcessAPI().startProcess(definition.getId()); waitForUserTaskAndAssignIt(instance, "Request2", john); List pendingHumanTaskInstances = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertThat(pendingHumanTaskInstances).hasSize(1); getProcessAPI().disableProcess(processDefinition.getId()); pendingHumanTaskInstances = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertThat(pendingHumanTaskInstances).hasSize(1); disableAndDeleteProcess(definition); deleteProcess(processDefinition); deleteUserMembership(m1.getId()); deleteGroups(mainGroup); deleteRoles(member); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/ReceiveTasksIT.java ================================================ /** * 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.activity; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.Serializable; import java.util.*; import java.util.Map.Entry; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.actor.ActorCriterion; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.flownode.*; import org.bonitasoft.engine.bpm.process.*; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ReceiveTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ThrowMessageEventTriggerBuilder; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.operation.*; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.TestStates; import org.junit.Test; /** * @author Julien Molinaro */ public class ReceiveTasksIT extends TestWithUser { private static final String SEARCH_WAITING_EVENTS_COMMAND = "searchWaitingEventsCommand"; private static final String SEARCH_OPTIONS_KEY = "searchOptions"; /* * 1 receiveProcess, no message sent * dynamic -> deployAndEnable(receiveProcess), startProcess(receiveProcess) * checks : receiveProcess wait on receive task and don't and halt on the user task. */ @SuppressWarnings("unchecked") @Test public void noMessageSentSoReceiveProcessIsWaiting() throws Exception { final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithReceivedTask("receiveMessageProcess", "waitForMessage", "userTask1", "delivery", user, "m1", null, null, null); final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); waitForFlowNodeInState(receiveMessageProcessInstance, "waitForMessage", TestStates.WAITING, true); // we check after that the waiting event is still here final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(WaitingEventSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, receiveMessageProcessInstance.getId()); final Map parameters = new HashMap<>(1); parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done()); final SearchResult searchResult = (SearchResult) getCommandAPI() .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters); assertEquals(1, searchResult.getCount()); disableAndDeleteProcess(receiveMessageProcess); } /* * 1 receiveProcess, 1 sendProcess, Message goes from EndEvent to ReceiveTask * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(receiveProcess), * startProcess(sendProcess) * checks : receiveProcess wait on receive task, sendProcess is finished, receiveProcess continue and halt on the * user task, receive task is archived */ @SuppressWarnings("unchecked") @Test public void receiveMessageSentAfterReceiveProcessIsWaiting() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent("sendMessageProcess", "m2", "receiveMessageProcess", "waitForMessage", null, null, null, null); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithReceivedTask("receiveMessageProcess", "waitForMessage", "userTask1", "delivery", user, "m2", null, null, null); final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); waitForFlowNodeInState(receiveMessageProcessInstance, "waitForMessage", TestStates.WAITING, true); SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(WaitingEventSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, receiveMessageProcessInstance.getId()); final Map parameters = new HashMap<>(1); parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done()); SearchResult searchResult = (SearchResult) getCommandAPI() .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters); assertEquals(1, searchResult.getCount()); final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId()); waitForProcessToFinish(sendMessageProcessInstance); waitForUserTask(receiveMessageProcessInstance, "userTask1"); searchResult = (SearchResult) getCommandAPI().execute(SEARCH_WAITING_EVENTS_COMMAND, parameters); assertEquals(0, searchResult.getCount()); searchOptionsBuilder = new SearchOptionsBuilder(0, 20); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, receiveMessageProcessInstance.getId()); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.RECEIVE_TASK); final SearchResult archivedActivityInstancesSearch = getProcessAPI() .searchArchivedActivities(searchOptionsBuilder.done()); assertEquals(1, archivedActivityInstancesSearch.getCount()); assertTrue(archivedActivityInstancesSearch.getResult().get(0) instanceof ArchivedReceiveTaskInstance); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } /* * Verify receiveProcess receive message targeting it, even if sent before its existence. * 1 receiveProcess, 1 sendProcess, Message goes from EndEvent to ReceiveTask * dynamic -> deployAndEnable(sendProcess), startProcess(sendProcess), deployAndEnable(receiveProcess), * startProcess(receiveProcess) * checks : sendProcess is finished, receiveProcess goes through receive task (found message sent by sendProcess) * and reaches user task. */ @Test public void receiveMessageSentBeforeReceiveProcessIsEnabled() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent("sendMessageProcess", "m3", "receiveMessageProcess", "waitForMessage", null, null, null, null); final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId()); waitForProcessToFinish(sendMessageProcessInstance); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithReceivedTask("receiveMessageProcess", "waitForMessage", "userTask1", "delivery", user, "m3", null, null, null); final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); // No need to verify anything, if no exception, query exists getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, receiveMessageProcessInstance.getId()) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.RECEIVE_TASK).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, receiveMessageProcessInstance.getId()) .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.RECEIVE_TASK) .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done()); waitForUserTask(receiveMessageProcessInstance, "userTask1"); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } /* * 1 receiveProcess, 2 sendProcess, 2 Messages go from EndEvent to ReceiveTask * dynamic -> deployAndEnable(sendProcesses), startProcess(sendProcesses), deployAndEnable(receiveProcess), * startProcess(receiveProcess) * checks : sendProcesses are finished, receiveProcess goes through receive task (found one message sent by * sendProcess) and reaches user task. */ @Test public void receiveMessageSentTwice() throws Exception { ProcessDefinition sendMessageProcess1 = null; ProcessDefinition sendMessageProcess2 = null; ProcessDefinition receiveMessageProcess = null; try { sendMessageProcess1 = deployAndEnableProcessWithEndMessageEvent("sendMessageProcess1", "m4", "receiveMessageProcess", "waitForMessage", null, null, null, null); sendMessageProcess2 = deployAndEnableProcessWithEndMessageEvent("sendMessageProcess2", "m4", "receiveMessageProcess", "waitForMessage", null, null, null, null); final ProcessInstance sendMessageProcessInstance1 = getProcessAPI() .startProcess(sendMessageProcess1.getId()); final ProcessInstance sendMessageProcessInstance2 = getProcessAPI() .startProcess(sendMessageProcess2.getId()); waitForProcessToFinish(sendMessageProcessInstance1); waitForProcessToFinish(sendMessageProcessInstance2); receiveMessageProcess = deployAndEnableProcessWithReceivedTask("receiveMessageProcess", "waitForMessage", "userTask1", "delivery", user, "m4", null, null, null); final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); waitForUserTask(receiveMessageProcessInstance, "userTask1"); final ProcessInstance receiveMessageProcessInstance2 = getProcessAPI() .startProcess(receiveMessageProcess.getId()); waitForUserTask(receiveMessageProcessInstance2, "userTask1"); } finally { disableAndDeleteProcess(sendMessageProcess1); disableAndDeleteProcess(sendMessageProcess2); disableAndDeleteProcess(receiveMessageProcess); } } /* * 1 receiveProcess, 1 sendProcess, Message contains data goes from EndEvent to ReceiveTask * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(receiveProcess), * startProcess(sendProcess) * checks : receiveProcess wait on receive task, sendProcess is finished, receiveProcess goes through receive task * (found message sent by * sendProcess) and reaches user task, data is transmitted to * the receiveProcess. */ @Test public void receiveMessageWithData() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent("sendMessageProcess", "m5", "receiveMessageProcess", "waitForMessage", null, Collections.singletonMap("lastName", String.class.getName()), Collections.singletonMap("lName", String.class.getName()), Collections.singletonMap("lName", "lastName")); final List receiveMessageOperations = Collections .singletonList(buildAssignOperation("name", "lName", String.class.getName(), ExpressionType.TYPE_VARIABLE)); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithReceivedTask("receiveMessageProcess", "waitForMessage", "userTask1", "delivery", user, "m5", null, Collections.singletonMap("name", String.class.getName()), receiveMessageOperations); final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); waitForFlowNodeInState(receiveMessageProcessInstance, "waitForMessage", TestStates.WAITING, true); final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId(), Arrays.asList( buildAssignOperation("lastName", "Doe", String.class.getName(), ExpressionType.TYPE_CONSTANT)), null); waitForProcessToFinish(sendMessageProcessInstance); final HumanTaskInstance step1 = waitForUserTaskAndGetIt(receiveMessageProcessInstance, "userTask1"); final DataInstance dataInstance = getProcessAPI().getProcessDataInstance("name", step1.getRootContainerId()); assertEquals("Doe", dataInstance.getValue()); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } /* * dynamic -> deployAndEnable(receiveProcess), startProcess(receiveProcess), cancelProcessInstance(receiveProcess) * checks : receiveProcess wait on receive task, 1 waiting event, receiveProcess is cancelled, receiveProcess is * archived, no more waiting event */ @SuppressWarnings("unchecked") @Test public void cancelInstanceShouldDeleteWaitingEvents() throws Exception { final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithReceivedTask("receiveMessageProcess", "waitForMessage", "userTask1", "delivery", user, "m1", null, null, null); final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); waitForFlowNodeInState(receiveMessageProcessInstance, "waitForMessage", TestStates.WAITING, true); SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(WaitingEventSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, receiveMessageProcessInstance.getId()); final Map parameters = new HashMap<>(1); parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done()); SearchResult searchResult = (SearchResult) getCommandAPI() .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters); assertEquals(1, searchResult.getCount()); getProcessAPI().cancelProcessInstance(receiveMessageProcessInstance.getId()); waitForProcessToBeInState(receiveMessageProcessInstance, ProcessInstanceState.CANCELLED); searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, receiveMessageProcessInstance.getId()); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.RECEIVE_TASK); final SearchResult archivedActivityInstancesSearch = getProcessAPI() .searchArchivedActivities(searchOptionsBuilder.done()); assertEquals(1, archivedActivityInstancesSearch.getCount()); assertTrue(archivedActivityInstancesSearch.getResult().get(0) instanceof ArchivedReceiveTaskInstance); assertEquals(TestStates.CANCELLED.getStateName(), archivedActivityInstancesSearch.getResult().get(0).getState()); searchResult = (SearchResult) getCommandAPI().execute(SEARCH_WAITING_EVENTS_COMMAND, parameters); assertEquals(0, searchResult.getCount()); disableAndDeleteProcess(receiveMessageProcess); } private ProcessDefinition deployAndEnableProcessWithEndMessageEvent(final String processName, final String messageName, final String targetProcess, final String targetFlowNode, final List> correlations, final Map processData, final Map messageData, final Map dataInputMapping) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder(); processBuilder.createNewInstance(processName, "1.0"); addProcessData(processData, processBuilder); processBuilder.addStartEvent("startEvent"); processBuilder.addAutomaticTask("auto1"); // create expression for target process/flowNode final Expression targetProcessExpression = new ExpressionBuilder() .createConstantStringExpression(targetProcess); final Expression targetFlowNodeExpression = new ExpressionBuilder() .createConstantStringExpression(targetFlowNode); final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder = processBuilder.addEndEvent("endEvent") .addMessageEventTrigger(messageName, targetProcessExpression, targetFlowNodeExpression); addCorrelations(correlations, throwMessageEventTriggerBuilder); addMessageData(messageData, dataInputMapping, throwMessageEventTriggerBuilder); processBuilder.addTransition("startEvent", "auto1"); processBuilder.addTransition("auto1", "endEvent"); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final ProcessDefinition sendMessageProcess = deployAndEnableProcess(designProcessDefinition); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(sendMessageProcess.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); return sendMessageProcess; } private ProcessDefinition deployAndEnableProcessWithReceivedTask(final String processName, final String receiveTaskName, final String userTaskName, final String actorName, final User user, final String messageName, final List> correlations, final Map processData, final List operations) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder(); processBuilder.createNewInstance(processName, "1.0"); addProcessData(processData, processBuilder); processBuilder.addStartEvent("startEvent"); final ReceiveTaskDefinitionBuilder receiveTaskBuilder = processBuilder.addReceiveTask(receiveTaskName, messageName); if (correlations != null) { for (final Entry entry : correlations) { receiveTaskBuilder.addCorrelation(entry.getKey(), entry.getValue()); } } if (operations != null) { for (final Operation operation : operations) { receiveTaskBuilder.addMessageOperation(operation); } } processBuilder.addActor(actorName); processBuilder.addUserTask(userTaskName, actorName); processBuilder.addEndEvent("endEvent"); processBuilder.addTransition("startEvent", receiveTaskName); processBuilder.addTransition(receiveTaskName, userTaskName); processBuilder.addTransition(userTaskName, "endEvent"); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder(); archiveBuilder.createNewBusinessArchive().setProcessDefinition(designProcessDefinition); final BusinessArchive receiveMessageArchive = archiveBuilder.done(); final ProcessDefinition receiveMessageProcess = deployProcess(receiveMessageArchive); final List actors = getProcessAPI().getActors(receiveMessageProcess.getId(), 0, 1, ActorCriterion.NAME_ASC); getProcessAPI().addUserToActor(actors.get(0).getId(), user.getId()); getProcessAPI().enableProcess(receiveMessageProcess.getId()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(receiveMessageProcess.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); return receiveMessageProcess; } private void addMessageData(final Map messageData, final Map dataInputMapping, final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder) throws InvalidExpressionException { if (messageData != null) { for (final Entry entry : messageData.entrySet()) { final Expression displayName = new ExpressionBuilder().createConstantStringExpression(entry.getKey()); Expression defaultValue = null; if (dataInputMapping.containsKey(entry.getKey())) { defaultValue = new ExpressionBuilder().createDataExpression(dataInputMapping.get(entry.getKey()), entry.getValue()); } throwMessageEventTriggerBuilder.addMessageContentExpression(displayName, defaultValue); } } } private void addProcessData(final Map data, final ProcessDefinitionBuilder processBuilder) { if (data != null) { for (final Entry entry : data.entrySet()) { processBuilder.addData(entry.getKey(), entry.getValue(), null); } } } private void addCorrelations(final List> correlations, final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder) { if (correlations != null) { for (final Entry entry : correlations) { throwMessageEventTriggerBuilder.addCorrelation(entry.getKey(), entry.getValue()); } } } private Operation buildAssignOperation(final String dataInstanceName, final String newConstantValue, final String className, final ExpressionType expressionType) throws InvalidExpressionException { final LeftOperand leftOperand = new LeftOperandBuilder().createNewInstance().setName(dataInstanceName).done(); final Expression expression = new ExpressionBuilder().createNewInstance(dataInstanceName) .setContent(newConstantValue) .setExpressionType(expressionType.name()).setReturnType(className).done(); final Operation operation; operation = new OperationBuilder().createNewInstance().setOperator("=").setLeftOperand(leftOperand) .setType(OperatorType.ASSIGNMENT) .setRightOperand(expression).done(); return operation; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/SendTaskIT.java ================================================ /** * 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.activity; import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedSendTaskInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.SendTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.event.AbstractEventIT; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.operation.LeftOperand; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.junit.Test; /** * @author Baptiste Mesta */ public class SendTaskIT extends AbstractEventIT { public static final String DATA_INPUT_ITEM_REF_NAME = "lastName"; private ProcessDefinition deployAndEnableProcessWithSendTask(final String processName, final String messageName, final String targetProcess, final String targetFlowNode, final Map processData, final Map messageData, final Map dataInputMapping) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder(); processBuilder.createNewInstance(processName, "1.0"); addProcessData(processData, processBuilder); processBuilder.addStartEvent("startEvent"); // create expression for target process/flowNode final Expression targetProcessExpression = new ExpressionBuilder() .createConstantStringExpression(targetProcess); final SendTaskDefinitionBuilder sendTaskDefinitionBuilder; sendTaskDefinitionBuilder = processBuilder.addSendTask("sendMessage", messageName, targetProcessExpression); if (targetFlowNode != null) { sendTaskDefinitionBuilder .setTargetFlowNode(new ExpressionBuilder().createConstantStringExpression(targetFlowNode)); } processBuilder.addEndEvent("endEvent"); addMessageData(messageData, dataInputMapping, sendTaskDefinitionBuilder); processBuilder.addTransition("startEvent", "sendMessage"); processBuilder.addTransition("sendMessage", "endEvent"); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final ProcessDefinition sendMessageProcess = deployAndEnableProcess(designProcessDefinition); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(sendMessageProcess.getId()); assertThat(processDeploymentInfo.getActivationState()).isEqualTo(ActivationState.ENABLED); return sendMessageProcess; } private void addMessageData(final Map messageData, final Map dataInputMapping, final SendTaskDefinitionBuilder sendTaskDefinitionBuilder) throws InvalidExpressionException { if (messageData != null) { for (final Entry entry : messageData.entrySet()) { final Expression displayName = new ExpressionBuilder().createConstantStringExpression(entry.getKey()); Expression defaultValue = null; if (dataInputMapping.containsKey(entry.getKey())) { defaultValue = new ExpressionBuilder().createDataExpression(dataInputMapping.get(entry.getKey()), entry.getValue()); } sendTaskDefinitionBuilder.addMessageContentExpression(displayName, defaultValue); } } } private ProcessDefinition deployAndEnableProcessWithMultiInstantiatedSendTask(final String processName, final String messageName, final String targetProcess, final String targetFlowNode, String inputListScript, final Map messageData, final Map dataInputMapping) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder(); processBuilder.createNewInstance(processName, "1.0"); processBuilder.addStartEvent("startEvent"); String loopDataInput = "loopDataInput"; processBuilder .addData( loopDataInput, List.class.getName(), new ExpressionBuilder().createGroovyScriptExpression( "executeAMultiInstanceWithLoopDataInputAndOutput1", inputListScript, List.class.getName())); // create expression for target process/flowNode final Expression targetProcessExpression = new ExpressionBuilder() .createConstantStringExpression(targetProcess); final SendTaskDefinitionBuilder sendTaskDefinitionBuilder; sendTaskDefinitionBuilder = processBuilder.addSendTask("sendMessage", messageName, targetProcessExpression); if (targetFlowNode != null) { sendTaskDefinitionBuilder .setTargetFlowNode(new ExpressionBuilder().createConstantStringExpression(targetFlowNode)); } sendTaskDefinitionBuilder.addShortTextData(DATA_INPUT_ITEM_REF_NAME, null); sendTaskDefinitionBuilder.addMultiInstance(false, loopDataInput).addDataInputItemRef(DATA_INPUT_ITEM_REF_NAME); processBuilder.addEndEvent("endEvent"); addMessageData(messageData, dataInputMapping, sendTaskDefinitionBuilder); processBuilder.addTransition("startEvent", "sendMessage"); processBuilder.addTransition("sendMessage", "endEvent"); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final ProcessDefinition sendMessageProcess = deployAndEnableProcess(designProcessDefinition); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(sendMessageProcess.getId()); assertThat(processDeploymentInfo.getActivationState()).isEqualTo(ActivationState.ENABLED); return sendMessageProcess; } private Operation buildAssignOperation(final String dataInstanceName, final String newConstantValue, final String className, final ExpressionType expressionType) throws InvalidExpressionException { final LeftOperand leftOperand = new LeftOperandBuilder().createNewInstance().setName(dataInstanceName).done(); final Expression expression = new ExpressionBuilder().createNewInstance(dataInstanceName) .setContent(newConstantValue) .setExpressionType(expressionType).setReturnType(className).done(); final Operation operation; operation = new OperationBuilder().createNewInstance().setOperator("=").setLeftOperand(leftOperand) .setType(OperatorType.ASSIGNMENT) .setRightOperand(expression).done(); return operation; } /* * 1 receiveProcess, 1 sendProcess, Message contains data goes from SendTask to IntermediateEvent * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess) * checks : receiveProcess start and stop on catchEvent, sendProcess is finished, , receiveProcess continues and * reaches user task , data is transmitted to * the receiveProcess. */ @Test public void dataTransferFromSendTaskToMessageIntermediateCatchEventWithTargetFlowNode() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithSendTask("sendMessageProcess", "m14", "receiveMessageProcess", "waitForMessage", Collections.singletonMap("lastName", String.class.getName()), Collections.singletonMap("lName", String.class.getName()), Collections.singletonMap("lName", "lastName")); final List catchMessageOperations = Collections .singletonList(buildAssignOperation("name", "lName", String.class.getName(), ExpressionType.TYPE_VARIABLE)); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent( "receiveMessageProcess", "m14", null, Collections.singletonMap("name", String.class.getName()), catchMessageOperations); // start a instance of a receive message process final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); // wait the event node instance waitForEventInWaitingState(receiveMessageProcessInstance, CATCH_EVENT_NAME); DataInstance dataInstance = getProcessAPI().getProcessDataInstance("name", receiveMessageProcessInstance.getId()); assertThat(dataInstance.getValue()).isNull(); final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId(), Arrays.asList( buildAssignOperation("lastName", "Doe", String.class.getName(), ExpressionType.TYPE_CONSTANT)), null); // No need to verify anything, if no exception, query exists getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, sendMessageProcessInstance.getId()) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.SEND_TASK).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, sendMessageProcessInstance.getId()) .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.SEND_TASK) .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done()); waitForProcessToFinish(sendMessageProcessInstance); final List archivedActivityInstances = getProcessAPI().getArchivedActivityInstances( sendMessageProcessInstance.getId(), 0, 10, ActivityInstanceCriterion.LAST_UPDATE_DESC); assertThat(archivedActivityInstances.get(0)).isInstanceOf(ArchivedSendTaskInstance.class); waitForUserTask(receiveMessageProcessInstance, CATCH_MESSAGE_STEP1_NAME); dataInstance = getProcessAPI().getProcessDataInstance("name", receiveMessageProcessInstance.getId()); assertThat(dataInstance.getValue()).isEqualTo("Doe"); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } /* * 1 receiveProcess, 1 sendProcess, Message contains data goes from SendTask to IntermediateEvent * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess) * checks : receiveProcess start and stop on catchEvent, sendProcess is finished, , receiveProcess continues and * reaches user task , data is transmitted to * the receiveProcess. */ @Test public void dataTransferFromSendTaskToMessageIntermediateCatchEvent() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithSendTask("sendMessageProcess", "m14", "receiveMessageProcess", null, Collections.singletonMap("lastName", String.class.getName()), Collections.singletonMap("lName", String.class.getName()), Collections.singletonMap("lName", "lastName")); final List catchMessageOperations = Collections .singletonList(buildAssignOperation("name", "lName", String.class.getName(), ExpressionType.TYPE_VARIABLE)); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent( "receiveMessageProcess", "m14", null, Collections.singletonMap("name", String.class.getName()), catchMessageOperations); // start a instance of a receive message process final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); // wait the event node instance waitForEventInWaitingState(receiveMessageProcessInstance, CATCH_EVENT_NAME); DataInstance dataInstance = getProcessAPI().getProcessDataInstance("name", receiveMessageProcessInstance.getId()); assertThat(dataInstance.getValue()).isNull(); final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId(), Arrays.asList( buildAssignOperation("lastName", "Doe", String.class.getName(), ExpressionType.TYPE_CONSTANT)), null); waitForProcessToFinish(sendMessageProcessInstance); waitForUserTask(receiveMessageProcessInstance, CATCH_MESSAGE_STEP1_NAME); dataInstance = getProcessAPI().getProcessDataInstance("name", receiveMessageProcessInstance.getId()); assertThat(dataInstance.getValue()).isEqualTo("Doe"); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } /* * 1 receiveProcess, 1 sendProcess, Message contains data goes from multi instantiated SendTask to StartEvent * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess) * checks : receiveProcess start and reaches user task , data is transmitted to the receiveProcess. */ @Test public void can_transfer_taskData_from_multi_instance_sendTask_to_messageStartEvent() throws Exception { //given final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithMultiInstantiatedSendTask( "sendMessageProcess", "m15", "receiveMessageProcess", START_EVENT_NAME, "[\"Doe\", \"Smith\"]", Collections.singletonMap("lName", String.class.getName()), Collections.singletonMap("lName", DATA_INPUT_ITEM_REF_NAME)); final List catchMessageOperations = Collections .singletonList(buildAssignOperation("name", "lName", String.class.getName(), ExpressionType.TYPE_VARIABLE)); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent( "receiveMessageProcess", "m15", Collections.singletonMap("name", String.class.getName()), catchMessageOperations); //when final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId()); //then waitForProcessToFinish(sendMessageProcessInstance); //two instances should be created //step of first instance HumanTaskInstance step1I1 = waitForUserTaskAndGetIt(START_WITH_MESSAGE_STEP1_NAME); assertThat(step1I1.getProcessDefinitionId()).isEqualTo(receiveMessageProcess.getId()); //step of second instance HumanTaskInstance step1I2 = waitForUserTaskAndGetIt(START_WITH_MESSAGE_STEP1_NAME); assertThat(step1I2.getProcessDefinitionId()).isEqualTo(receiveMessageProcess.getId()); assertThat(step1I1.getParentProcessInstanceId()).isNotEqualTo(step1I2.getParentProcessInstanceId()); //data of first instance DataInstance dataInstanceI1 = getProcessAPI().getProcessDataInstance("name", step1I1.getParentProcessInstanceId()); //data of second instance DataInstance dataInstanceI2 = getProcessAPI().getProcessDataInstance("name", step1I2.getParentProcessInstanceId()); assertThat(Arrays.asList(dataInstanceI1.getValue(), dataInstanceI2.getValue())).contains("Doe", "Smith"); //clean up disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/UserTaskAssignationIT.java ================================================ /** * 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.activity; import static org.junit.Assert.*; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import net.jodah.concurrentunit.Waiter; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.FlowNodeExecutionException; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.connectors.VariableStorage; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.test.check.CheckNbAssignedTaskOf; import org.bonitasoft.engine.test.check.CheckNbPendingTaskOf; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class UserTaskAssignationIT extends TestWithTechnicalUser { @Rule public ExpectedException thrown = ExpectedException.none(); private static final String JOHN = "john"; private static final String JACK = "jack"; private User john; private User jack; private ProcessDefinition processDefinition; private ProcessInstance processInstance; private HumanTaskInstance step2; @Override @Before public void before() throws Exception { super.before(); john = createUser(JOHN, "bpm"); jack = createUser(JACK, "bpm"); loginOnDefaultTenantWith(JOHN, "bpm"); processDefinition = deployAndEnableSimpleProcess(); processInstance = getProcessAPI().startProcess(processDefinition.getId()); step2 = waitForUserTaskAndGetIt(processInstance, "step2"); } @Override @After public void after() throws Exception { disableAndDeleteProcess(processDefinition); deleteUser(JOHN); deleteUser(JACK); VariableStorage.clearAll(); super.after(); } @Test public void getAssignedHumanTasksWithStartedState() throws Exception { assignAndExecuteStep(step2, john); final List toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null); // Task is in STARTED state so should not be retrieved: assertEquals(0, toDoTasks.size()); waitForProcessToFinish(processInstance); } @Test(expected = FlowNodeExecutionException.class) public void cannotExecuteAnUnassignedTask() throws Exception { // execute activity without assign it before, an exception is expected getProcessAPI().executeFlowNode(step2.getId()); } @Test public void assignUserTask() throws Exception { getProcessAPI().assignUserTask(step2.getId(), john.getId()); final List toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, toDoTasks.size()); assertEquals(john.getId(), toDoTasks.get(0).getAssigneeId()); } @Test public void canAssignTask2Times() throws Exception { getProcessAPI().assignUserTask(step2.getId(), john.getId()); // No exception expected getProcessAPI().assignUserTask(step2.getId(), john.getId()); } @Test public void assignUserTaskSeveralTimes() throws Exception { // after assign getProcessAPI().assignUserTask(step2.getId(), john.getId()); List toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, toDoTasks.size()); assertEquals(john.getId(), toDoTasks.get(0).getAssigneeId()); List pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, pendingTasks.size()); // after release getProcessAPI().releaseUserTask(toDoTasks.get(0).getId()); toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, toDoTasks.size()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, pendingTasks.size()); assertEquals(0, pendingTasks.get(0).getAssigneeId()); // re assign getProcessAPI().assignUserTask(step2.getId(), jack.getId()); toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(1, toDoTasks.size()); assertEquals(jack.getId(), toDoTasks.get(0).getAssigneeId()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, pendingTasks.size()); getProcessAPI().releaseUserTask(toDoTasks.get(0).getId()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, pendingTasks.size()); assertEquals(0, pendingTasks.get(0).getAssigneeId()); // re assign getProcessAPI().assignUserTask(step2.getId(), john.getId()); toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, toDoTasks.size()); assertEquals(john.getId(), toDoTasks.get(0).getAssigneeId()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, pendingTasks.size()); } @Test public void releaseUserTask() throws Exception { // after assign getProcessAPI().assignUserTask(step2.getId(), john.getId()); List toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, toDoTasks.size()); assertEquals(john.getId(), toDoTasks.get(0).getAssigneeId()); List pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, pendingTasks.size()); // after release getProcessAPI().releaseUserTask(toDoTasks.get(0).getId()); toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, toDoTasks.size()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, pendingTasks.size()); } @Test public void assignUserTaskSeveralTimesByChangingLogin() throws Exception { // login as jack logout(); loginOnDefaultTenantWith(JACK, "bpm"); // assign getProcessAPI().assignUserTask(step2.getId(), jack.getId()); List toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(1, toDoTasks.size()); assertEquals(jack.getId(), toDoTasks.get(0).getAssigneeId()); List pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, pendingTasks.size()); // release getProcessAPI().releaseUserTask(toDoTasks.get(0).getId()); toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(0, toDoTasks.size()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, pendingTasks.size()); // re assign getProcessAPI().assignUserTask(pendingTasks.get(0).getId(), jack.getId()); toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(1, toDoTasks.size()); assertEquals(jack.getId(), toDoTasks.get(0).getAssigneeId()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, pendingTasks.size()); } @Test public void assignedDateUpdate() throws Exception { final Long taskId = step2.getId(); // First assign getProcessAPI().assignUserTask(taskId, john.getId()); assertTrue("Fail to claim task", new CheckNbAssignedTaskOf(getProcessAPI(), 30, 2000, false, 1, john).waitUntil()); final Date firstClaimedDate = getProcessAPI().getHumanTaskInstance(taskId).getClaimedDate(); assertNotNull("Claimed date not set during first assignment", firstClaimedDate); // Release getProcessAPI().releaseUserTask(taskId); assertTrue("Fail to release task", new CheckNbPendingTaskOf(getProcessAPI(), 30, 2000, false, 1, john).waitUntil()); assertNull("Claimed date not unset during release", getProcessAPI().getHumanTaskInstance(taskId).getClaimedDate()); // Second assign getProcessAPI().assignUserTask(taskId, john.getId()); assertTrue("Fail to claim task for the second time", new CheckNbAssignedTaskOf(getProcessAPI(), 30, 2000, false, 1, john).waitUntil()); final HumanTaskInstance task = getProcessAPI().getHumanTaskInstance(taskId); assertNotNull("Claimed date not set during first assignment", task.getClaimedDate()); assertNotEquals("Claimed date not updated", firstClaimedDate, task.getClaimedDate()); } @Test @Ignore("lastUpdateDate should be removed (not used)") public void lastUpdateDateUpdate() throws Exception { final Long taskId = step2.getId(); Date previousUpdateDate = step2.getLastUpdateDate(); // First assign getProcessAPI().assignUserTask(taskId, john.getId()); if (!new CheckNbAssignedTaskOf(getProcessAPI(), 50, 5000, false, 1, john).waitUntil()) { fail("Fail to claim task"); } HumanTaskInstance task = getProcessAPI().getHumanTaskInstance(taskId); assertNotNull("Last update date not set during first assignment", task.getLastUpdateDate()); assertFalse("Last update date not updated during first assignment", task.getLastUpdateDate().equals(previousUpdateDate)); previousUpdateDate = task.getLastUpdateDate(); // Release getProcessAPI().releaseUserTask(taskId); task = waitForUserTaskAndGetIt(processInstance, "step2"); assertFalse("Last update date not updated during release", previousUpdateDate.equals(task.getLastUpdateDate())); previousUpdateDate = task.getLastUpdateDate(); // Second assign getProcessAPI().assignUserTask(taskId, john.getId()); if (!new CheckNbAssignedTaskOf(getProcessAPI(), 50, 5000, false, 1, john).waitUntil()) { fail("Fail to claim task for the second time"); } task = getProcessAPI().getHumanTaskInstance(taskId); assertFalse("Last update date not updated during second assignment", previousUpdateDate.equals(task.getLastUpdateDate())); } private ProcessDefinition deployAndEnableSimpleProcess() throws BonitaException, InvalidProcessDefinitionException { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(false, true)); return deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, john); } @Test public void assignUserTaskIfNotAssigned() throws Exception { getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), john.getId()); final List toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, toDoTasks.size()); assertEquals(john.getId(), toDoTasks.get(0).getAssigneeId()); } @Test public void shouldAssignAndUnassign() throws Exception { getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), john.getId()); getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), 0L); } @Test public void shouldAssignAndAssignAgainToSameUser() throws Exception { getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), john.getId()); getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), john.getId()); } @Test public void canNotAssignTask2TimestoDifferentUserIfAssigned() throws Exception { getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), john.getId()); thrown.expect(UpdateException.class); getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), jack.getId()); } @Test public void shouldAssignTaskSeveralTimesToDifferentUsers() throws Exception { getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), john.getId()); List toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, toDoTasks.size()); assertEquals(john.getId(), toDoTasks.get(0).getAssigneeId()); List pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, pendingTasks.size()); getProcessAPI().releaseUserTask(toDoTasks.get(0).getId()); toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, toDoTasks.size()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, pendingTasks.size()); assertEquals(0, pendingTasks.get(0).getAssigneeId()); getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), jack.getId()); toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(1, toDoTasks.size()); assertEquals(jack.getId(), toDoTasks.get(0).getAssigneeId()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, pendingTasks.size()); getProcessAPI().releaseUserTask(toDoTasks.get(0).getId()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, pendingTasks.size()); assertEquals(0, pendingTasks.get(0).getAssigneeId()); getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), john.getId()); toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, toDoTasks.size()); assertEquals(john.getId(), toDoTasks.get(0).getAssigneeId()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, pendingTasks.size()); } @Test public void shouldAssignUserTaskSeveralTimesByChangingLogin() throws Exception { logout(); loginOnDefaultTenantWith(JACK, "bpm"); getProcessAPI().assignUserTaskIfNotAssigned(step2.getId(), jack.getId()); List toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(1, toDoTasks.size()); assertEquals(jack.getId(), toDoTasks.get(0).getAssigneeId()); List pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, pendingTasks.size()); getProcessAPI().releaseUserTask(toDoTasks.get(0).getId()); toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(0, toDoTasks.size()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, pendingTasks.size()); getProcessAPI().assignUserTaskIfNotAssigned(pendingTasks.get(0).getId(), jack.getId()); toDoTasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(1, toDoTasks.size()); assertEquals(jack.getId(), toDoTasks.get(0).getAssigneeId()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, pendingTasks.size()); } @Test public void assignedDateUpdateIfNotAssigned() throws Exception { final Long taskId = step2.getId(); getProcessAPI().assignUserTaskIfNotAssigned(taskId, john.getId()); assertTrue("Fail to claim task", new CheckNbAssignedTaskOf(getProcessAPI(), 30, 2000, false, 1, john).waitUntil()); final Date firstClaimedDate = getProcessAPI().getHumanTaskInstance(taskId).getClaimedDate(); assertNotNull("Claimed date not set during first assignment", firstClaimedDate); getProcessAPI().releaseUserTask(taskId); assertTrue("Fail to release task", new CheckNbPendingTaskOf(getProcessAPI(), 30, 2000, false, 1, john).waitUntil()); assertNull("Claimed date not unset during release", getProcessAPI().getHumanTaskInstance(taskId).getClaimedDate()); getProcessAPI().assignUserTaskIfNotAssigned(taskId, john.getId()); assertTrue("Fail to claim task for the second time", new CheckNbAssignedTaskOf(getProcessAPI(), 30, 2000, false, 1, john).waitUntil()); final HumanTaskInstance task = getProcessAPI().getHumanTaskInstance(taskId); assertNotNull("Claimed date not set during first assignment", task.getClaimedDate()); assertNotEquals("Claimed date not updated", firstClaimedDate, task.getClaimedDate()); } @Test public void assignUserTaskSeveralThreads() throws Throwable { final Long taskId = step2.getId(); loginOnDefaultTenantWith(JACK, "bpm"); ProcessAPI pAPI = getProcessAPI(); Waiter waiter = new Waiter(); Boolean[] exceptionThreads = new Boolean[] { false, false }; Thread thread1 = new Thread() { public void run() { try { pAPI.assignUserTaskIfNotAssigned(taskId, john.getId()); } catch (Exception e) { exceptionThreads[0] = true; } finally { waiter.resume(); } } }; Thread thread2 = new Thread() { public void run() { try { pAPI.assignUserTaskIfNotAssigned(taskId, jack.getId()); } catch (Exception e) { exceptionThreads[0] = true; } finally { waiter.resume(); } } }; thread1.start(); thread2.start(); waiter.await(1, TimeUnit.SECONDS, 2); //Assert only one failed; assertNotEquals("Both assigments failed", exceptionThreads[0] || exceptionThreads[1], false); assertNotEquals("Both assigments took place", exceptionThreads[0] && exceptionThreads[1], true); } @Test public void assignUserTaskSeveralThreadsWithoutCheck() throws Throwable { final Long taskId = step2.getId(); loginOnDefaultTenantWith(JACK, "bpm"); ProcessAPI pAPI = getProcessAPI(); Waiter waiter = new Waiter(); Boolean[] exceptionThreads = new Boolean[] { false, false }; Thread thread1 = new Thread() { public void run() { try { pAPI.assignUserTask(taskId, john.getId()); } catch (Exception e) { exceptionThreads[0] = true; } finally { waiter.resume(); } } }; Thread thread2 = new Thread() { public void run() { try { pAPI.assignUserTask(taskId, jack.getId()); } catch (Exception e) { exceptionThreads[0] = true; } finally { waiter.resume(); } } }; thread1.start(); thread2.start(); waiter.await(1, TimeUnit.SECONDS, 2); //Assert only one failed; assertEquals("Both assigments took place", exceptionThreads[0] || exceptionThreads[1], false); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/activity/work/RetryWorkIT.java ================================================ /** * Copyright (C) 2020 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.activity.work; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.junit.Before; import org.junit.Test; /** * @author Baptiste Mesta. */ public class RetryWorkIT extends CommonAPIIT { @Before public void before() throws Exception { loginWithTechnicalUser(); } @Test public void should_retry_exception_in_groovy_scripts() throws Exception { String script = "public class AScriptThatFailsOneTime{\n" + " static alreadyExecutedOnce = false\n" + "\n" + " String exec(){\n" + " if(alreadyExecutedOnce){\n" + " return \"ok\"\n" + " }\n" + " alreadyExecutedOnce = true\n" + " throw new org.bonitasoft.engine.commons.exceptions.SRetryableException('something')\n" + " }\n" + "}\n" + "new AScriptThatFailsOneTime().exec()"; DesignProcessDefinition process = new ProcessDefinitionBuilder().createNewInstance("processToRetry", "1.0") .addAutomaticTask("theTask").addDisplayName(new ExpressionBuilder() .createGroovyScriptExpression("failonetime", script, String.class.getName())) .getProcess(); ProcessDefinition processDefinition = getProcessAPI().deployAndEnableProcess(process); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); //the task should fail one but be retried waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/application/ApplicationIT.java ================================================ /** * 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.application; import static org.assertj.core.api.Assertions.assertThat; import java.util.List; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.api.permission.APICallContext; import org.bonitasoft.engine.business.application.*; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.Test; /** * @author Emmanuel Duchastenier */ public class ApplicationIT extends TestWithTechnicalUser { @Test public void searchApplications_can_filter_by_user_id() throws Exception { //given final User user1 = createUser("walter.bates", "bpm"); final User user2 = createUser("helen.kelly", "bpm"); final User user3 = createUser("daniela.angelo", "bpm"); final User user4 = createUser("jan.fisher", "bpm"); final List profiles = getProfileAPI().searchProfiles(new SearchOptionsBuilder(0, 10).done()) .getResult(); getProfileAPI().createProfileMember(profiles.get(0).getId(), user1.getId(), null, null); getProfileAPI().createProfileMember(profiles.get(1).getId(), user2.getId(), null, null); getProfileAPI().createProfileMember(profiles.get(0).getId(), user3.getId(), null, null); getProfileAPI().createProfileMember(profiles.get(1).getId(), user3.getId(), null, null); final ApplicationCreator hrCreator = new ApplicationCreator("HR-dashboard", "HR dashboard", "1.0") .setProfileId(profiles.get(0).getId()); final ApplicationCreator engineeringCreator = new ApplicationCreator("Engineering-dashboard", "Engineering dashboard", "1.0").setProfileId(profiles.get(0).getId()); final ApplicationCreator marketingCreator = new ApplicationCreator("Marketing-dashboard", "Marketing dashboard", "1.0").setProfileId(profiles.get(1).getId()); final Application hr = getApplicationAPI().createApplication(hrCreator); final Application engineering = getApplicationAPI().createApplication(engineeringCreator); final Application marketing = getApplicationAPI().createApplication(marketingCreator); //when final SearchOptionsBuilder builderUser1 = new SearchOptionsBuilder(0, 10); builderUser1.filter(ApplicationSearchDescriptor.USER_ID, user1.getId()); builderUser1.sort(ApplicationSearchDescriptor.DISPLAY_NAME, Order.ASC); final SearchResult applicationsUser1 = getApplicationAPI() .searchIApplications(builderUser1.done()); assertThat(applicationsUser1).isNotNull(); assertThat(applicationsUser1.getResult()).contains(engineering, hr).doesNotContain(marketing); final SearchOptionsBuilder builderUser2 = new SearchOptionsBuilder(0, 10); builderUser2.filter(ApplicationSearchDescriptor.USER_ID, user2.getId()); builderUser2.sort(ApplicationSearchDescriptor.DISPLAY_NAME, Order.ASC); final SearchResult applicationsUser2 = getApplicationAPI() .searchIApplications(builderUser2.done()); assertThat(applicationsUser2).isNotNull(); assertThat(applicationsUser2.getResult()).contains(marketing).doesNotContain(engineering, hr); final SearchOptionsBuilder builderUser3 = new SearchOptionsBuilder(0, 10); builderUser3.filter(ApplicationSearchDescriptor.USER_ID, user3.getId()); builderUser3.sort(ApplicationSearchDescriptor.DISPLAY_NAME, Order.ASC); final SearchResult applicationsUser3 = getApplicationAPI() .searchIApplications(builderUser3.done()); assertThat(applicationsUser3).isNotNull(); assertThat(applicationsUser3.getResult()).contains(engineering, hr, marketing); final SearchOptionsBuilder builderUser4 = new SearchOptionsBuilder(0, 10); builderUser4.filter(ApplicationSearchDescriptor.USER_ID, user4.getId()); builderUser4.sort(ApplicationSearchDescriptor.DISPLAY_NAME, Order.ASC); final SearchResult applicationsUser4 = getApplicationAPI() .searchIApplications(builderUser4.done()); assertThat(applicationsUser4.getResult()).isEmpty(); // Let's check SAM (=tenant admin), has access to applications mapped to "_BONITA_INTERNAL_PROFILE_SUPER_ADMIN": final List importStatus = getLivingApplicationAPI().importApplications( IOUtils.toByteArray(ApplicationIT.class.getResourceAsStream("superAdminApp.xml")), ApplicationImportPolicy.FAIL_ON_DUPLICATES); assertThat(importStatus).isNotEmpty().allMatch(status -> status.getStatus().equals(ImportStatus.Status.ADDED)); final SearchOptionsBuilder soSystemAdmin = new SearchOptionsBuilder(0, 10); soSystemAdmin.filter(ApplicationSearchDescriptor.USER_ID, "-1"); // -1 is userId for SAM (= tenant admin) final SearchResult samApplications = getApplicationAPI() .searchIApplications(soSystemAdmin.done()); final List applications = samApplications.getResult(); assertThat(applications).hasSize(1); assertThat(applications.get(0).getToken()).isEqualTo("superAdminAppBonita"); getApplicationAPI().deleteApplication(applications.get(0).getId()); } @Test public void searchApplications_can_filter_by_user_id_and_process_display_name() throws Exception { //given final User user1 = createUser("walter.bates", "bpm"); final List profiles = getProfileAPI().searchProfiles(new SearchOptionsBuilder(0, 10).done()) .getResult(); getProfileAPI().createProfileMember(profiles.get(0).getId(), user1.getId(), null, null); final ApplicationCreator hrCreator = new ApplicationCreator("HR-dashboard", "HR dashboard", "1.0") .setProfileId(profiles.get(0).getId()); final ApplicationCreator engineeringCreator = new ApplicationCreator("Engineering-dashboard", "Engineering dashboard", "1.0").setProfileId(profiles.get(0).getId()); final ApplicationCreator marketingCreator = new ApplicationCreator("Marketing-dashboard", "Marketing dashboard", "1.0").setProfileId(profiles.get(1).getId()); final Application hr = getApplicationAPI().createApplication(hrCreator); final Application engineering = getApplicationAPI().createApplication(engineeringCreator); final Application marketing = getApplicationAPI().createApplication(marketingCreator); //when final SearchOptionsBuilder builderUser1 = new SearchOptionsBuilder(0, 10); builderUser1.filter(ApplicationSearchDescriptor.USER_ID, user1.getId()); builderUser1.filter(ApplicationSearchDescriptor.DISPLAY_NAME, "Engineering dashboard"); final SearchResult applicationsUser1 = getApplicationAPI() .searchIApplications(builderUser1.done()); assertThat(applicationsUser1).isNotNull(); assertThat(applicationsUser1.getCount()).isEqualTo(1); assertThat(applicationsUser1.getResult()).containsExactly(engineering); } @Test public void should_access_identity_api_using_default_application_permissions() throws Exception { // Given var testPage = getPageAPI().createPage("page-to-test-permissions.zip", IOUtils.toByteArray(ApplicationIT.class.getResourceAsStream("/page-to-test-permissions.zip"))); var testApp = getApplicationAPI().importApplications( IOUtils.toByteArray(ApplicationIT.class.getResourceAsStream("/application-to-test-permissions.xml")), ApplicationImportPolicy.FAIL_ON_DUPLICATES); // Uses page-to-test-permissions.zip // Uses application-to-test-permissions.xml User user = createUser("baptiste", "bpm"); loginOnDefaultTenantWith("baptiste", "bpm"); assertThat(getPermissionAPI().isAuthorized(new APICallContext("GET", "identity", "user", null))).isFalse(); getProfileAPI().createProfileMember( getProfileAPI().searchProfiles(new SearchOptionsBuilder(0, 1).searchTerm("User").done()).getResult() .get(0).getId(), user.getId(), -1L, -1L); loginOnDefaultTenantWith("baptiste", "bpm"); long userId = user.getId(); assertThat( getPermissionAPI().isAuthorized(new APICallContext("GET", "identity", "user", String.valueOf(userId)))) .isTrue(); assertThat(getPermissionAPI() .isAuthorized(new APICallContext("GET", "identity", "user", String.valueOf(userId + 1)))).isFalse(); assertThat(getPermissionAPI().isAuthorized(new APICallContext("GET", "identity", "user", null))).isFalse(); getProfileAPI().createProfileMember( getProfileAPI().searchProfiles(new SearchOptionsBuilder(0, 1).searchTerm("Admin").done()).getResult() .get(0).getId(), user.getId(), -1L, -1L); loginOnDefaultTenantWith("baptiste", "bpm"); assertThat(getPermissionAPI().isAuthorized(new APICallContext("GET", "identity", "user", null))).isTrue(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/application/LivingApplicationIT.java ================================================ /** * 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.business.application; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.fail; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class LivingApplicationIT extends TestWithLivingApplication { @Test public void createApplication_returns_application_based_on_ApplicationCreator_information() throws Exception { //given final Profile profile = getProfileUser(); Page defaultLayout = getPageAPI().getPageByName(DEFAULT_LAYOUT_NAME); Page defaultTheme = getPageAPI().getPageByName(DEFAULT_THEME_NAME); final ApplicationCreator creator = new ApplicationCreator("My-Application", "My application display name", "1.0"); creator.setDescription("This is my application"); creator.setIconPath("/icon.jpg"); creator.setProfileId(profile.getId()); //when final Application application = getLivingApplicationAPI().createApplication(creator); //then assertThat(application).isNotNull(); assertThat(application.getToken()).isEqualTo("My-Application"); assertThat(application.getDisplayName()).isEqualTo("My application display name"); assertThat(application.getVersion()).isEqualTo("1.0"); assertThat(application.getId()).isGreaterThan(0); assertThat(application.getDescription()).isEqualTo("This is my application"); assertThat(application.getIconPath()).isEqualTo("/icon.jpg"); assertThat(application.getCreatedBy()).isEqualTo(getUser().getId()); assertThat(application.getUpdatedBy()).isEqualTo(getUser().getId()); assertThat(application.getHomePageId()).isNull(); assertThat(application.getProfileId()).isEqualTo(profile.getId()); assertThat(application.getLayoutId()).isEqualTo(defaultLayout.getId()); assertThat(application.getThemeId()).isEqualTo(defaultTheme.getId()); getLivingApplicationAPI().deleteApplication(application.getId()); } @Test public void createApplicationLink_returns_application_based_on_ApplicationCreator_information() throws Exception { //given final Profile profile = getProfileUser(); final ApplicationLinkCreator creator = new ApplicationLinkCreator("My-Application", "My application display name", "1.0"); creator.setDescription("This is my application"); creator.setIconPath("/icon.jpg"); creator.setProfileId(profile.getId()); //when final ApplicationLink application = getLivingApplicationAPI().createApplicationLink(creator); //then assertThat(application).isNotNull(); assertThat(application.getToken()).isEqualTo("My-Application"); assertThat(application.getDisplayName()).isEqualTo("My application display name"); assertThat(application.getVersion()).isEqualTo("1.0"); assertThat(application.getId()).isGreaterThan(0); assertThat(application.getDescription()).isEqualTo("This is my application"); assertThat(application.getIconPath()).isEqualTo("/icon.jpg"); assertThat(application.getCreatedBy()).isEqualTo(getUser().getId()); assertThat(application.getUpdatedBy()).isEqualTo(getUser().getId()); assertThat(application.getProfileId()).isEqualTo(profile.getId()); getLivingApplicationAPI().deleteApplication(application.getId()); } @Test public void createApplication_without_profile_should_have_null_profileId() throws Exception { //given final ApplicationCreator creator = new ApplicationCreator("My-Application", "My application display name", "1.0"); //when final Application application = getLivingApplicationAPI().createApplication(creator); //then assertThat(application).isNotNull(); assertThat(application.getProfileId()).isNull(); } @Test public void updateApplication_should_return_application_up_to_date() throws Exception { //given final Profile profile = getProfileUser(); final ApplicationCreator creator = new ApplicationCreator("My-Application", "My application display name", "1.0"); final Application application = getLivingApplicationAPI().createApplication(creator); final ApplicationUpdater updater = new ApplicationUpdater(); updater.setToken("My-updated-app"); updater.setDisplayName("Updated display name"); updater.setVersion("1.1"); updater.setDescription("Up description"); updater.setIconPath("/newIcon.jpg"); updater.setProfileId(profile.getId()); updater.setState(ApplicationState.ACTIVATED.name()); //when final Application updatedApplication = getLivingApplicationAPI().updateApplication(application.getId(), updater); //then assertThat(updatedApplication).isNotNull(); assertThat(updatedApplication.getToken()).isEqualTo("My-updated-app"); assertThat(updatedApplication.getDisplayName()).isEqualTo("Updated display name"); assertThat(updatedApplication.getVersion()).isEqualTo("1.1"); assertThat(updatedApplication.getDescription()).isEqualTo("Up description"); assertThat(updatedApplication.getIconPath()).isEqualTo("/newIcon.jpg"); assertThat(updatedApplication.getProfileId()).isEqualTo(profile.getId()); assertThat(updatedApplication.getState()).isEqualTo(ApplicationState.ACTIVATED.name()); assertThat(updatedApplication).isEqualTo(getLivingApplicationAPI().getApplication(application.getId())); getLivingApplicationAPI().deleteApplication(application.getId()); } @Test public void updateApplicationLink_should_return_application_up_to_date() throws Exception { //given final Profile profile = getProfileUser(); final ApplicationLinkCreator creator = new ApplicationLinkCreator("My-Application", "My application display name", "1.0"); final ApplicationLink application = getLivingApplicationAPI().createApplicationLink(creator); final ApplicationLinkUpdater updater = new ApplicationLinkUpdater(); updater.setToken("My-updated-app"); updater.setDisplayName("Updated display name"); updater.setVersion("1.1"); updater.setDescription("Up description"); updater.setIconPath("/newIcon.jpg"); updater.setProfileId(profile.getId()); updater.setState(ApplicationState.ACTIVATED.name()); //when final ApplicationLink updatedApplication = getLivingApplicationAPI().updateApplicationLink(application.getId(), updater); //then assertThat(updatedApplication).isNotNull(); assertThat(updatedApplication.getToken()).isEqualTo("My-updated-app"); assertThat(updatedApplication.getDisplayName()).isEqualTo("Updated display name"); assertThat(updatedApplication.getVersion()).isEqualTo("1.1"); assertThat(updatedApplication.getDescription()).isEqualTo("Up description"); assertThat(updatedApplication.getIconPath()).isEqualTo("/newIcon.jpg"); assertThat(updatedApplication.getProfileId()).isEqualTo(profile.getId()); assertThat(updatedApplication.getState()).isEqualTo(ApplicationState.ACTIVATED.name()); assertThat(updatedApplication).isEqualTo(getLivingApplicationAPI().getIApplication(application.getId())); getLivingApplicationAPI().deleteApplication(application.getId()); } @Test public void getApplication_returns_application_with_the_given_id() throws Exception { //given final ApplicationCreator creator = new ApplicationCreator("My-Application", "My application display name", "1.0"); final Application createdApp = getLivingApplicationAPI().createApplication(creator); assertThat(createdApp).isNotNull(); //when final IApplication retrievedApp = getLivingApplicationAPI().getIApplication(createdApp.getId()); //then assertThat(retrievedApp).isEqualTo(createdApp); } @Test public void getApplicationByToken_returns_application_with_the_given_token() throws Exception { //given final ApplicationCreator creator = new ApplicationCreator("My-Application", "My application display name", "1.0"); final Application createdApp = getLivingApplicationAPI().createApplication(creator); assertThat(createdApp).isNotNull(); //when final IApplication retrievedApp = getLivingApplicationAPI().getIApplicationByToken(createdApp.getToken()); //then assertThat(retrievedApp).isEqualTo(createdApp); } @Test public void deleteApplication_should_delete_application_with_the_given_id() throws Exception { //given final ApplicationCreator creator = new ApplicationCreator("My-Application", "My application display name", "1.0"); final Application createdApp = getLivingApplicationAPI().createApplication(creator); assertThat(createdApp).isNotNull(); //when getLivingApplicationAPI().deleteApplication(createdApp.getId()); //then try { getLivingApplicationAPI().getApplication(createdApp.getId()); fail("Not found exception"); } catch (final NotFoundException e) { //ok } } @Test public void searchApplications_without_filter_return_all_elements_based_on_pagination() throws Exception { //given final ApplicationCreator hrCreator = new ApplicationCreator("AAA_HR-dashboard", "HR dash board", "1.0"); final ApplicationCreator engineeringCreator = new ApplicationCreator("AAA_Engineering-dashboard", "Engineering dashboard", "1.0"); final ApplicationCreator marketingCreator = new ApplicationCreator("AAA_Marketing-dashboard", "Marketing dashboard", "1.0"); final Application hr = getLivingApplicationAPI().createApplication(hrCreator); final Application engineering = getLivingApplicationAPI().createApplication(engineeringCreator); final Application marketing = getLivingApplicationAPI().createApplication(marketingCreator); //when final SearchResult firstPage = getLivingApplicationAPI() .searchIApplications(buildSearchOptions("AAA", 0, 2)); //then assertThat(firstPage).isNotNull(); assertThat(firstPage.getCount()).isEqualTo(3); assertThat(firstPage.getResult()).containsExactly(engineering, hr); //when final SearchResult secondPage = getLivingApplicationAPI() .searchIApplications(buildSearchOptions("AAA", 2, 2)); //then assertThat(secondPage).isNotNull(); assertThat(secondPage.getCount()).isEqualTo(3); assertThat(secondPage.getResult()).containsExactly(marketing); } @Test public void searchApplications_can_filter_on_name() throws Exception { //given final ApplicationCreator hrCreator = new ApplicationCreator("HR-dashboard", "HR dash board", "1.0"); final ApplicationCreator engineeringCreator = new ApplicationCreator("Engineering-dashboard", "Engineering dashboard", "1.0"); final ApplicationCreator marketingCreator = new ApplicationCreator("Marketing-dashboard", "Marketing dashboard", "1.0"); getLivingApplicationAPI().createApplication(hrCreator); final Application engineering = getLivingApplicationAPI().createApplication(engineeringCreator); getLivingApplicationAPI().createApplication(marketingCreator); //when final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10); builder.filter(ApplicationSearchDescriptor.TOKEN, "Engineering-dashboard"); final SearchResult applications = getLivingApplicationAPI().searchIApplications(builder.done()); assertThat(applications).isNotNull(); assertThat(applications.getCount()).isEqualTo(1); assertThat(applications.getResult()).containsExactly(engineering); } @Test public void searchApplications_can_filter_on_display_name() throws Exception { //given final ApplicationCreator hrCreator = new ApplicationCreator("HR-dashboard", "HR dashboard", "1.0"); final ApplicationCreator engineeringCreator = new ApplicationCreator("Engineering-dashboard", "Engineering dashboard", "1.0"); final ApplicationCreator marketingCreator = new ApplicationCreator("Marketing-dashboard", "Marketing dashboard", "1.0"); final Application hr = getLivingApplicationAPI().createApplication(hrCreator); getLivingApplicationAPI().createApplication(engineeringCreator); getLivingApplicationAPI().createApplication(marketingCreator); //when final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10); builder.filter(ApplicationSearchDescriptor.DISPLAY_NAME, "HR dashboard"); final SearchResult applications = getLivingApplicationAPI().searchIApplications(builder.done()); assertThat(applications).isNotNull(); assertThat(applications.getCount()).isEqualTo(1); assertThat(applications.getResult()).containsExactly(hr); } @Test public void searchApplications_can_filter_on_version() throws Exception { //given final ApplicationCreator hrCreator = new ApplicationCreator("HR-dashboard", "HR dash board", "2.0"); final ApplicationCreator engineeringCreator = new ApplicationCreator("Engineering-dashboard", "Engineering dashboard", "1.0"); final ApplicationCreator marketingCreator = new ApplicationCreator("Marketing-dashboard", "Marketing dashboard", "2.0"); final Application hr = getLivingApplicationAPI().createApplication(hrCreator); getLivingApplicationAPI().createApplication(engineeringCreator); final Application marketing = getLivingApplicationAPI().createApplication(marketingCreator); //when final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10); builder.filter(ApplicationSearchDescriptor.VERSION, "2.0"); final SearchResult applications = getLivingApplicationAPI().searchIApplications(builder.done()); assertThat(applications).isNotNull(); assertThat(applications.getCount()).isEqualTo(2); assertThat(applications.getResult()).containsExactly(hr, marketing); } @Test public void searchApplications_can_filter_on_profileId() throws Exception { //given final Profile profile = getProfileUser(); final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10); long initialCount = getLivingApplicationAPI().searchIApplications(builder.done()).getCount(); builder.filter(ApplicationSearchDescriptor.PROFILE_ID, profile.getId()); final ApplicationCreator hrCreator = new ApplicationCreator("HR-dashboard", "HR dash board", "1.0"); final ApplicationCreator engineeringCreator = new ApplicationCreator("Engineering-dashboard", "Engineering dashboard", "1.0"); engineeringCreator.setProfileId(profile.getId()); final ApplicationCreator marketingCreator = new ApplicationCreator("Marketing-dashboard", "Marketing dashboard", "1.0"); final Application hr = getLivingApplicationAPI().createApplication(hrCreator); final Application engineering = getLivingApplicationAPI().createApplication(engineeringCreator); final Application marketing = getLivingApplicationAPI().createApplication(marketingCreator); //when final SearchResult applications = getLivingApplicationAPI().searchIApplications(builder.done()); assertThat(applications).isNotNull(); assertThat(applications.getCount()).isEqualTo(initialCount + 1); assertThat(applications.getResult()).contains(engineering); getLivingApplicationAPI().deleteApplication(hr.getId()); getLivingApplicationAPI().deleteApplication(engineering.getId()); getLivingApplicationAPI().deleteApplication(marketing.getId()); } @Test public void searchApplications_can_use_search_term() throws Exception { //given final ApplicationCreator hrCreator = new ApplicationCreator("HR-dashboard", "My HR dashboard", "2.0"); final ApplicationCreator engineeringCreator = new ApplicationCreator("Engineering-dashboard", "Engineering dashboard", "1.0"); final ApplicationCreator marketingCreator = new ApplicationCreator("My", "Marketing", "2.0"); final Application hr = getLivingApplicationAPI().createApplication(hrCreator); getLivingApplicationAPI().createApplication(engineeringCreator); final Application marketing = getLivingApplicationAPI().createApplication(marketingCreator); //when final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10); builder.searchTerm("My"); final SearchResult applications = getLivingApplicationAPI().searchIApplications(builder.done()); assertThat(applications).isNotNull(); assertThat(applications.getCount()).isEqualTo(2); assertThat(applications.getResult()).containsExactly(hr, marketing); } @Test public void should_create_update_replace_icon_of_application() throws Exception { //Create application with icon ApplicationCreator creator = new ApplicationCreator("appWithIcon", "An application having an icon", "1.0"); creator.setIcon("icon.png", "PNG\n\t some png content".getBytes()); Application applicationCreated = getLivingApplicationAPI().createApplication(creator); assertThat(applicationCreated.hasIcon()).isTrue(); assertThat(getLivingApplicationAPI().getIconOfApplication(applicationCreated.getId())).satisfies(icon -> { assertThat(new String(icon.getContent())).isEqualTo("PNG\n\t some png content"); assertThat(icon.getMimeType()).isEqualTo("image/png"); }); //replace icon of application Thread.sleep(10); Application applicationUpdated = getLivingApplicationAPI().updateApplication(applicationCreated.getId(), new ApplicationUpdater().setIcon("toto.jpg", "jpeg content".getBytes())); assertThat(applicationUpdated.hasIcon()).isTrue(); assertThat(applicationUpdated.getLastUpdateDate()).isAfter(applicationCreated.getLastUpdateDate()); assertThat(getLivingApplicationAPI().getIconOfApplication(applicationUpdated.getId())).satisfies(icon -> { assertThat(new String(icon.getContent())).isEqualTo("jpeg content"); assertThat(icon.getMimeType()).isEqualTo("image/jpeg"); }); // remove icon Thread.sleep(10); Application applicationWithIconRemoved = getLivingApplicationAPI().updateApplication(applicationCreated.getId(), new ApplicationUpdater().setIcon(null, null)); assertThat(applicationWithIconRemoved.hasIcon()).isFalse(); assertThat(applicationWithIconRemoved.getLastUpdateDate()).isAfter(applicationUpdated.getLastUpdateDate()); assertThat(getLivingApplicationAPI().getIconOfApplication(applicationWithIconRemoved.getId())).isNull(); } @Test public void should_throw_not_found_when_getting_icon_of_unknown_application() { assertThatThrownBy(() -> getLivingApplicationAPI().getIconOfApplication(543256432L)) .isInstanceOf(ApplicationNotFoundException.class); } private SearchOptions buildSearchOptions(String prefix, final int startIndex, final int maxResults) { final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(startIndex, maxResults); builder.searchTerm(prefix); return builder.done(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/application/LivingApplicationImportExportIT.java ================================================ /** * 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.business.application; import static org.assertj.core.api.Assertions.assertThat; import java.util.List; import org.apache.commons.io.IOUtils; import org.assertj.core.util.xml.XmlStringPrettyFormatter; import org.bonitasoft.engine.api.ImportError; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class LivingApplicationImportExportIT extends TestWithLivingApplication { private SearchOptions buildSearchOptions(final int startIndex, final int maxResults) { return getAppSearchBuilderOrderById(startIndex, maxResults).done(); } @Test public void exportApplications_should_return_the_byte_content_of_xml_file_containing_selected_applications() throws Exception { //given Profile userProfile = getProfileUser(); final byte[] applicationsByteArray = IOUtils .toByteArray(LivingApplicationIT.class.getResourceAsStream("applications.xml")); final String xmlPrettyFormatExpected = XmlStringPrettyFormatter .xmlPrettyFormat(new String(applicationsByteArray)); final ApplicationCreator hrCreator = new ApplicationCreator("HR-dashboard", "My HR dashboard", "2.0"); hrCreator.setDescription("This is the HR dashboard."); hrCreator.setIconPath("/icon.jpg"); hrCreator.setProfileId(userProfile.getId()); final ApplicationCreator engineeringCreator = new ApplicationCreator("Engineering-dashboard", "Engineering dashboard", "1.0"); final ApplicationCreator marketingCreator = new ApplicationCreator("My", "Marketing", "2.0"); final Application hr = getLivingApplicationAPI().createApplication(hrCreator); // Associate a new page to application hr (real page name is defined in zip/page.properties): final Page myPage = getPageAPI().createPage("not_used", IOUtils.toByteArray(LivingApplicationIT.class.getResourceAsStream("dummy-bizapp-page.zip"))); final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(hr.getId(), myPage.getId(), "my-new-custom-page"); // Add menus: final ApplicationMenu menu = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(hr.getId(), "HR follow-up")); final ApplicationMenuCreator subMenuCreator = new ApplicationMenuCreator(hr.getId(), "Daily HR follow-up", appPage.getId()); subMenuCreator.setParentId(menu.getId()); getLivingApplicationAPI().createApplicationMenu(subMenuCreator); getLivingApplicationAPI().createApplicationMenu(new ApplicationMenuCreator(hr.getId(), "Empty menu")); // Add home page: getLivingApplicationAPI().setApplicationHomePage(hr.getId(), appPage.getId()); getLivingApplicationAPI().createApplication(engineeringCreator); final Application marketing = getLivingApplicationAPI().createApplication(marketingCreator); //when final byte[] exportedBytes = getLivingApplicationAPI().exportApplications(hr.getId(), marketing.getId()); final String xmlPrettyFormatExported = XmlStringPrettyFormatter.xmlPrettyFormat(new String(exportedBytes)); //then assertThatXmlHaveNoDifferences(xmlPrettyFormatExpected, xmlPrettyFormatExported); getLivingApplicationAPI().deleteApplication(hr.getId()); getPageAPI().deletePage(myPage.getId()); } @Test public void importApplications_should_create_all_applications_contained_by_xml_file_and_return_status_ok() throws Exception { //given final Profile profile = getProfileUser(); long initialCount = getLivingApplicationAPI() .searchIApplications(buildSearchOptions(0, 10)).getCount(); // create page necessary to import application hr (real page name is defined in zip/page.properties): final Page myPage = getPageAPI().createPage("not_used", IOUtils.toByteArray(LivingApplicationIT.class.getResourceAsStream("dummy-bizapp-page.zip"))); final Page defaultLayout = getPageAPI().getPageByName(DEFAULT_LAYOUT_NAME); final Page defaultTheme = getPageAPI().getPageByName(DEFAULT_THEME_NAME); final byte[] applicationsByteArray = IOUtils .toByteArray(LivingApplicationIT.class.getResourceAsStream("applications.xml")); //when final List importStatus = getLivingApplicationAPI().importApplications(applicationsByteArray, ApplicationImportPolicy.FAIL_ON_DUPLICATES); //then assertThat(importStatus).hasSize(2); assertIsAddOkStatus(importStatus.get(0), "HR-dashboard"); assertIsAddOkStatus(importStatus.get(1), "My"); // check applications were created final SearchResult searchResult = getLivingApplicationAPI() .searchIApplications(buildSearchOptions(0, 10)); assertThat(searchResult.getCount()).isEqualTo(initialCount + 2); Application hrApp = (Application) searchResult.getResult().stream().filter(a -> a.getToken().contains("HR")) .findFirst().orElseThrow(); assertIsHRApplication(profile, defaultLayout, defaultTheme, hrApp); assertIsMarketingApplication( (Application) searchResult.getResult().stream().filter(a -> a.getToken().contains("My")) .findFirst().orElseThrow()); //check pages were created SearchOptionsBuilder builder = getAppSearchBuilderOrderById(0, 10); builder.filter(ApplicationPageSearchDescriptor.APPLICATION_ID, hrApp.getId()); SearchResult pageSearchResult = getLivingApplicationAPI() .searchApplicationPages(builder.done()); assertThat(pageSearchResult.getCount()).isEqualTo(1); ApplicationPage myNewCustomPage = pageSearchResult.getResult().get(0); assertIsMyNewCustomPage(myPage, hrApp, myNewCustomPage); //check home page assertThat(hrApp.getHomePageId()).isEqualTo(myNewCustomPage.getId()); //check menu is created builder = getAppSearchBuilderOrderById(0, 10); builder.filter(ApplicationMenuSearchDescriptor.APPLICATION_ID, hrApp.getId()); SearchResult menuSearchResult = getLivingApplicationAPI() .searchApplicationMenus(builder.done()); assertThat(menuSearchResult.getCount()).isEqualTo(3); ApplicationMenu hrFollowUpMenu = menuSearchResult.getResult().get(0); assertIsHrFollowUpMenu(hrFollowUpMenu); assertIsDailyHrFollowUpMenu(menuSearchResult.getResult().get(1), hrFollowUpMenu, myNewCustomPage); assertIsEmptyMenu(menuSearchResult.getResult().get(2)); getLivingApplicationAPI().deleteApplication(hrApp.getId()); getPageAPI().deletePage(myPage.getId()); } @Test public void importApplications_should_create_applications_contained_by_xml_file_and_return_error_if_there_is_unavailable_info() throws Exception { long initialCount = getLivingApplicationAPI() .searchIApplications(buildSearchOptions(0, 10)).getCount(); //given final byte[] applicationsByteArray = IOUtils.toByteArray(LivingApplicationIT.class .getResourceAsStream("/org/bonitasoft/engine/business/application/applicationWithUnavailableInfo.xml")); // create page necessary to import application hr (real page name is defined in zip/page.properties): final Page myPage = getPageAPI().createPage("not_used", IOUtils.toByteArray(LivingApplicationIT.class.getResourceAsStream("dummy-bizapp-page.zip"))); //when final List importStatus = getLivingApplicationAPI().importApplications(applicationsByteArray, ApplicationImportPolicy.FAIL_ON_DUPLICATES); //then assertThat(importStatus).hasSize(1); assertThat(importStatus.get(0).getName()).isEqualTo("HR-dashboard"); assertThat(importStatus.get(0).getStatus()).isEqualTo(ImportStatus.Status.ADDED); ImportError profileError = new ImportError("ThisProfileDoesNotExist", ImportError.Type.PROFILE); ImportError customPageError = new ImportError("custompage_notexists", ImportError.Type.PAGE); ImportError appPageError1 = new ImportError("will-not-be-imported", ImportError.Type.APPLICATION_PAGE); ImportError appPageError2 = new ImportError("never-existed", ImportError.Type.APPLICATION_PAGE); assertThat(importStatus.get(0).getErrors()).containsExactly(profileError, customPageError, appPageError1, appPageError2); // check applications were created final SearchResult searchResult = getLivingApplicationAPI() .searchIApplications(buildSearchOptions(0, 10)); assertThat(searchResult.getCount()).isEqualTo(initialCount + 1); final Application app1 = (Application) searchResult.getResult().stream() .filter(a -> a.getToken().contains("HR")) .findFirst().orElseThrow(); assertThat(app1.getToken()).isEqualTo("HR-dashboard"); assertThat(app1.getVersion()).isEqualTo("2.0"); assertThat(app1.getDisplayName()).isEqualTo("My HR dashboard"); assertThat(app1.getDescription()).isEqualTo("This is the HR dashboard."); assertThat(app1.getIconPath()).isEqualTo("/icon.jpg"); assertThat(app1.getState()).isEqualTo("ACTIVATED"); assertThat(app1.getProfileId()).isNull(); //check only one application page was created SearchOptionsBuilder builder = getAppSearchBuilderOrderById(0, 10); builder.filter(ApplicationPageSearchDescriptor.APPLICATION_ID, app1.getId()); SearchResult pageSearchResult = getLivingApplicationAPI() .searchApplicationPages(builder.done()); assertThat(pageSearchResult.getCount()).isEqualTo(1); assertThat(pageSearchResult.getResult().get(0).getToken()).isEqualTo("my-new-custom-page"); builder = getAppSearchBuilderOrderById(0, 10); builder.filter(ApplicationMenuSearchDescriptor.APPLICATION_ID, app1.getId()); //check three menus were created SearchResult menuSearchResult = getLivingApplicationAPI() .searchApplicationMenus(builder.done()); assertThat(menuSearchResult.getCount()).isEqualTo(3); assertThat(menuSearchResult.getResult().get(0).getDisplayName()).isEqualTo("HR follow-up"); assertThat(menuSearchResult.getResult().get(0).getIndex()).isEqualTo(1); assertThat(menuSearchResult.getResult().get(1).getDisplayName()).isEqualTo("Daily HR follow-up"); assertThat(menuSearchResult.getResult().get(1).getIndex()).isEqualTo(1); assertThat(menuSearchResult.getResult().get(2).getDisplayName()).isEqualTo("Empty menu"); assertThat(menuSearchResult.getResult().get(2).getIndex()).isEqualTo(2); getLivingApplicationAPI().deleteApplication(app1.getId()); getPageAPI().deletePage(myPage.getId()); } @Test public void export_after_import_should_return_the_same_xml_file() throws Exception { //given long initialCount = getLivingApplicationAPI() .searchIApplications(buildSearchOptions(0, 10)).getCount(); // create page necessary to import application hr (real page name is defined in zip/page.properties): final Page myPage = getPageAPI().createPage("not_used", IOUtils.toByteArray(LivingApplicationIT.class.getResourceAsStream("dummy-bizapp-page.zip"))); final byte[] importedByteArray = IOUtils.toByteArray(LivingApplicationIT.class .getResourceAsStream("applications.xml")); getLivingApplicationAPI().importApplications(importedByteArray, ApplicationImportPolicy.FAIL_ON_DUPLICATES); final SearchResult searchResult = getLivingApplicationAPI() .searchIApplications(buildSearchOptions(0, 10)); assertThat(searchResult.getCount()).isEqualTo(initialCount + 2); //when Application hrApplication = (Application) searchResult.getResult().stream() .filter(a -> a.getToken().contains("HR")) .findFirst().orElseThrow(); byte[] exportedByteArray = getLivingApplicationAPI().exportApplications(hrApplication.getId(), searchResult.getResult().stream().filter(a -> a.getToken().contains("My")).findFirst().orElseThrow() .getId()); //then final String xmlPrettyFormatExpected = XmlStringPrettyFormatter.xmlPrettyFormat(new String(importedByteArray)); final String xmlPrettyFormatActual = XmlStringPrettyFormatter.xmlPrettyFormat(new String(exportedByteArray)); assertThatXmlHaveNoDifferences(xmlPrettyFormatExpected, xmlPrettyFormatActual); getLivingApplicationAPI().deleteApplication(hrApplication.getId()); getPageAPI().deletePage(myPage.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/application/LivingApplicationMenuIT.java ================================================ /** * 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.business.application; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class LivingApplicationMenuIT extends TestWithCustomPage { private Application application; private ApplicationPage appPage; @Override @Before public void setUp() throws Exception { super.setUp(); application = getLivingApplicationAPI().createApplication(new ApplicationCreator("app", "My app", "1.0")); appPage = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "myPage"); } @Override @After public void tearDown() throws Exception { getLivingApplicationAPI().deleteApplication(application.getId()); application = null; appPage = null; super.tearDown(); } @Test public void createApplicationMenu_ApplicationPage_should_return_applicationMenu_based_on_creator_and_should_manage_indexes() throws Exception { //given final ApplicationMenuCreator creator = new ApplicationMenuCreator(application.getId(), "Main"); //when final ApplicationMenu createdAppMenu = getLivingApplicationAPI().createApplicationMenu(creator); //then assertThat(createdAppMenu).isNotNull(); assertThat(createdAppMenu.getDisplayName()).isEqualTo("Main"); assertThat(createdAppMenu.getApplicationId()).isEqualTo(application.getId()); assertThat(createdAppMenu.getApplicationPageId()).isNull(); assertThat(createdAppMenu.getIndex()).isEqualTo(1); assertThat(createdAppMenu.getParentId()).isNull(); assertThat(createdAppMenu.getId()).isPositive(); //when //create a second menu ApplicationMenu index2Menu = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "second menu")); //then // should have a index incremented assertThat(index2Menu.getIndex()).isEqualTo(createdAppMenu.getIndex() + 1); } @Test public void createApplicationMenu_with_applicationPage_should_return_ApplicationMenu_with_applicationPageId() throws Exception { //given final ApplicationMenuCreator creator = new ApplicationMenuCreator(application.getId(), "Main", appPage.getId()); //when final ApplicationMenu createdAppMenu = getLivingApplicationAPI().createApplicationMenu(creator); //then assertThat(createdAppMenu).isNotNull(); assertThat(createdAppMenu.getApplicationPageId()).isEqualTo(appPage.getId()); } @Test public void createApplicationMenu_with_parent_menu_should_return_ApplicationMenu_with_parentId_and_should_manage_indexes() throws Exception { //given final ApplicationMenuCreator mainCreator = new ApplicationMenuCreator(application.getId(), "Main"); final ApplicationMenu mainMenu = getLivingApplicationAPI().createApplicationMenu(mainCreator); final ApplicationMenuCreator childCreator = new ApplicationMenuCreator(application.getId(), "Child", appPage.getId()); childCreator.setParentId(mainMenu.getId()); //when final ApplicationMenu createdAppMenu = getLivingApplicationAPI().createApplicationMenu(childCreator); //then assertThat(createdAppMenu).isNotNull(); assertThat(createdAppMenu.getParentId()).isEqualTo(mainMenu.getId()); assertThat(createdAppMenu.getIndex()).isEqualTo(1); //when //create a child menu second menu ApplicationMenuCreator secondChildCreator = new ApplicationMenuCreator(application.getId(), "second child", appPage.getId()); secondChildCreator.setParentId(mainMenu.getId()); ApplicationMenu secondChild = getLivingApplicationAPI().createApplicationMenu(secondChildCreator); //then //should have incremented index assertThat(secondChild.getIndex()).isEqualTo(2); } @Test public void updateApplicationMenu_should_update_application_menu_based_on_updater() throws Exception { //given final ApplicationMenuCreator parentCreator = new ApplicationMenuCreator(application.getId(), "Main"); final ApplicationMenuCreator childCreator = new ApplicationMenuCreator(application.getId(), "Child"); final ApplicationMenu parentAppMenu = getLivingApplicationAPI().createApplicationMenu(parentCreator); final ApplicationMenu childCreatedAppMenu = getLivingApplicationAPI().createApplicationMenu(childCreator); ApplicationMenuUpdater updater = new ApplicationMenuUpdater(); updater.setApplicationPageId(appPage.getId()); updater.setParentId(parentAppMenu.getId()); updater.setDisplayName("Updated child"); //when ApplicationMenu updatedChildMenu = getLivingApplicationAPI().updateApplicationMenu(childCreatedAppMenu.getId(), updater); //then assertThat(updatedChildMenu).isNotNull(); //updated: assertThat(updatedChildMenu.getDisplayName()).isEqualTo("Updated child"); assertThat(updatedChildMenu.getApplicationPageId()).isEqualTo(appPage.getId()); assertThat(updatedChildMenu.getParentId()).isEqualTo(parentAppMenu.getId()); assertThat(updatedChildMenu.getIndex()).isEqualTo(1); //because parent changed //not changed: assertThat(updatedChildMenu.getApplicationId()).isEqualTo(application.getId()); //given //check it's possible to clean parent and application page updater = new ApplicationMenuUpdater(); updater.setApplicationPageId(null); updater.setParentId(null); //when updatedChildMenu = getLivingApplicationAPI().updateApplicationMenu(childCreatedAppMenu.getId(), updater); //then assertThat(updatedChildMenu).isNotNull(); assertThat(updatedChildMenu).isEqualTo(getLivingApplicationAPI().getApplicationMenu(updatedChildMenu.getId())); // updated: assertThat(updatedChildMenu.getApplicationPageId()).isNull(); assertThat(updatedChildMenu.getParentId()).isNull(); assertThat(updatedChildMenu.getIndex()).isEqualTo(2); //because parent changed //not changed: assertThat(updatedChildMenu.getDisplayName()).isEqualTo("Updated child"); assertThat(updatedChildMenu.getApplicationId()).isEqualTo(application.getId()); } @Test public void updateApplicationMenu_index_should_organize_indexes_for_elements_having_the_same_parent() throws Exception { //given final ApplicationMenu parentAppMenu1 = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "Main1")); final ApplicationMenu parentAppMenu2 = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "Main2")); final ApplicationMenuCreator childCreator1 = new ApplicationMenuCreator(application.getId(), "Child"); childCreator1.setParentId(parentAppMenu1.getId()); final ApplicationMenuCreator childCreator2 = new ApplicationMenuCreator(application.getId(), "Child"); childCreator2.setParentId(parentAppMenu1.getId()); final ApplicationMenuCreator childCreator3 = new ApplicationMenuCreator(application.getId(), "Child"); childCreator3.setParentId(parentAppMenu1.getId()); final ApplicationMenu childCreatedAppMenu1 = getLivingApplicationAPI().createApplicationMenu(childCreator1); final ApplicationMenu childCreatedAppMenu2 = getLivingApplicationAPI().createApplicationMenu(childCreator2); final ApplicationMenu childCreatedAppMenu3 = getLivingApplicationAPI().createApplicationMenu(childCreator3); //when move up ApplicationMenu updatedChildMenu = getLivingApplicationAPI().updateApplicationMenu(childCreatedAppMenu3.getId(), new ApplicationMenuUpdater().setIndex(1)); //then assertThat(updatedChildMenu.getIndex()).isEqualTo(1); assertThat(getLivingApplicationAPI().getApplicationMenu(childCreatedAppMenu1.getId()).getIndex()).isEqualTo(2); assertThat(getLivingApplicationAPI().getApplicationMenu(childCreatedAppMenu2.getId()).getIndex()).isEqualTo(3); //when move down updatedChildMenu = getLivingApplicationAPI().updateApplicationMenu(childCreatedAppMenu3.getId(), new ApplicationMenuUpdater().setIndex(2)); //then assertThat(updatedChildMenu.getIndex()).isEqualTo(2); assertThat(getLivingApplicationAPI().getApplicationMenu(childCreatedAppMenu1.getId()).getIndex()).isEqualTo(1); assertThat(getLivingApplicationAPI().getApplicationMenu(childCreatedAppMenu2.getId()).getIndex()).isEqualTo(3); //when change parent updatedChildMenu = getLivingApplicationAPI().updateApplicationMenu(childCreatedAppMenu3.getId(), new ApplicationMenuUpdater().setParentId(parentAppMenu2.getId())); //then assertThat(updatedChildMenu.getIndex()).isEqualTo(1); assertThat(getLivingApplicationAPI().getApplicationMenu(childCreatedAppMenu1.getId()).getIndex()).isEqualTo(1); assertThat(getLivingApplicationAPI().getApplicationMenu(childCreatedAppMenu2.getId()).getIndex()).isEqualTo(2); } @Test public void getApplicationMenu_should_return_the_applicationMenu_identified_by_the_given_id() throws Exception { //given final ApplicationMenuCreator creator = new ApplicationMenuCreator(application.getId(), "Main", appPage.getId()); final ApplicationMenu createdAppMenu = getLivingApplicationAPI().createApplicationMenu(creator); //when final ApplicationMenu retrievedMenu = getLivingApplicationAPI().getApplicationMenu(createdAppMenu.getId()); //then assertThat(retrievedMenu).isNotNull(); assertThat(retrievedMenu).isEqualTo(createdAppMenu); } @Test public void deleteApplicationMenu_should_remove_the_applicationMenu_identified_by_the_given_id() throws Exception { //given final ApplicationMenuCreator creator = new ApplicationMenuCreator(application.getId(), "Main", appPage.getId()); final ApplicationMenu createdAppMenu = getLivingApplicationAPI().createApplicationMenu(creator); //when getLivingApplicationAPI().deleteApplicationMenu(createdAppMenu.getId()); //then verifyNotExists(createdAppMenu); } @Test public void deleteApplicationMenu_should_remove_sub_menus() throws Exception { //given Application application = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app2", "My secpond app", "1.0")); ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "myPage"); ApplicationMenu mainMenu = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "Main")); ApplicationMenu mainMenu2 = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "Main2")); ApplicationMenu subMenu = getLivingApplicationAPI().createApplicationMenu( new ApplicationMenuCreator(application.getId(), "Main", appPage.getId()).setParentId(mainMenu.getId())); //when getLivingApplicationAPI().deleteApplicationMenu(mainMenu.getId()); //then verifyNotExists(mainMenu); verifyNotExists(subMenu); verifyExists(mainMenu2); verifyExists(appPage); } @Test public void deleteApplication_also_deletes_application_pages_and_applicationMenu() throws Exception { //given final Application application = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app2", "My secpond app", "1.0")); final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "myPage"); final ApplicationMenu mainMenu = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "Main")); ApplicationMenuCreator subMenuCreator = new ApplicationMenuCreator(application.getId(), "Main", appPage.getId()); subMenuCreator.setParentId(mainMenu.getId()); final ApplicationMenu subMenu = getLivingApplicationAPI().createApplicationMenu(subMenuCreator); //when getLivingApplicationAPI().deleteApplication(application.getId()); //then verifyNotExists(mainMenu); verifyNotExists(subMenu); verifyNotExists(appPage); } @Test public void deleteApplicationPage_also_deletes_related_applicationMenu() throws Exception { //given final ApplicationPage pageToDelete = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "pageToDelete"); final ApplicationPage pageToKeep = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "pageToKeep"); final ApplicationMenu menuToDelete = getLivingApplicationAPI().createApplicationMenu( new ApplicationMenuCreator(application.getId(), "Main", pageToDelete.getId())); final ApplicationMenu menuToKeep = getLivingApplicationAPI().createApplicationMenu( new ApplicationMenuCreator(application.getId(), "Main", pageToKeep.getId())); final ApplicationMenu containerMenu = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "Main")); //when getLivingApplicationAPI().deleteApplicationPage(pageToDelete.getId()); //then // container menu and menu related to another page are keep verifyExists(containerMenu); verifyExists(menuToKeep); // menu related application is deleted verifyNotExists(menuToDelete); } private void verifyExists(ApplicationMenu applicationMenu) throws ApplicationMenuNotFoundException { ApplicationMenu retrievedMenu = getLivingApplicationAPI().getApplicationMenu(applicationMenu.getId()); assertThat(retrievedMenu).isNotNull(); } private void verifyExists(ApplicationPage applicationPage) throws ApplicationPageNotFoundException { ApplicationPage retrievedAppPage = getLivingApplicationAPI().getApplicationPage(applicationPage.getId()); assertThat(retrievedAppPage).isNotNull(); } private void verifyNotExists(ApplicationMenu applicationMenu) { try { getLivingApplicationAPI().getApplicationMenu(applicationMenu.getId()); //throws exception fail("exception expected"); } catch (ApplicationMenuNotFoundException e) { //OK } } private void verifyNotExists(ApplicationPage applicationPage) { try { getLivingApplicationAPI().getApplicationPage(applicationPage.getId()); //throws exception fail("exception expected"); } catch (ApplicationPageNotFoundException e) { //OK } } @Test public void searchApplicationMenus_without_filters_without_search_term_should_return_all_applicationMenues_pagged() throws Exception { //given final ApplicationMenu menu1 = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "first", appPage.getId())); final ApplicationMenu menu2 = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "second", appPage.getId())); final ApplicationMenu menu3 = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "third", appPage.getId())); //when final SearchResult firstPage = getLivingApplicationAPI() .searchApplicationMenus(buildSearchOptions(0, 2).filter("applicationId", application.getId()).done()); final SearchResult secondPage = getLivingApplicationAPI() .searchApplicationMenus(buildSearchOptions(2, 2).filter("applicationId", application.getId()).done()); //then assertThat(firstPage).isNotNull(); assertThat(firstPage.getCount()).isEqualTo(3); assertThat(firstPage.getResult()).containsExactly(menu1, menu2); assertThat(secondPage).isNotNull(); assertThat(secondPage.getCount()).isEqualTo(3); assertThat(secondPage.getResult()).containsExactly(menu3); } @Test public void searchApplicationMenus_can_filter_on_displayname() throws Exception { //given getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "first", appPage.getId())); final ApplicationMenu menu2 = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "second", appPage.getId())); getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "third", appPage.getId())); //when final SearchOptionsBuilder builder = getApplicationMenuSearchBuilder(0, 10); builder.filter(ApplicationMenuSearchDescriptor.DISPLAY_NAME, "second"); final SearchResult searchResult = getLivingApplicationAPI() .searchApplicationMenus(builder.done()); //then assertThat(searchResult).isNotNull(); assertThat(searchResult.getCount()).isEqualTo(1); assertThat(searchResult.getResult()).containsExactly(menu2); } @Test public void searchApplicationMenus_can_filter_on_index() throws Exception { //given getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "first", appPage.getId())); getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "second", appPage.getId())); final ApplicationMenu menu3 = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "third", appPage.getId())); //when final SearchOptionsBuilder builder = getApplicationMenuSearchBuilder(0, 10); builder.filter(ApplicationMenuSearchDescriptor.INDEX, menu3.getIndex()); final SearchResult searchResult = getLivingApplicationAPI() .searchApplicationMenus(builder.done()); //then assertThat(searchResult).isNotNull(); assertThat(searchResult.getCount()).isEqualTo(1); assertThat(searchResult.getResult()).containsExactly(menu3); } @Test public void searchApplicationMenus_can_filter_on_applicationPageId() throws Exception { //given final ApplicationPage appPage2 = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "mySecondPage"); final ApplicationMenu menu1 = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "first", appPage.getId())); getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "second", appPage2.getId())); final ApplicationMenu menu3 = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "third", appPage.getId())); //when final SearchOptionsBuilder builder = getApplicationMenuSearchBuilder(0, 10); builder.filter(ApplicationMenuSearchDescriptor.APPLICATION_PAGE_ID, appPage.getId()); final SearchResult searchResult = getLivingApplicationAPI() .searchApplicationMenus(builder.done()); //then assertThat(searchResult).isNotNull(); assertThat(searchResult.getCount()).isEqualTo(2); assertThat(searchResult.getResult()).containsExactly(menu1, menu3); } @Test public void searchApplicationMenus_can_filter_on_applicationId() throws Exception { //given final Application application2 = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app2", "My second app", "1.0")); final ApplicationPage appPage2 = getLivingApplicationAPI().createApplicationPage(application2.getId(), getPage().getId(), "mySecondPage"); final ApplicationMenu menu1 = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application2.getId(), "first", appPage2.getId())); getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "second", appPage.getId())); final ApplicationMenu menu3 = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application2.getId(), "third", appPage2.getId())); //when final SearchOptionsBuilder builder = getApplicationMenuSearchBuilder(0, 10); builder.filter(ApplicationMenuSearchDescriptor.APPLICATION_ID, application2.getId()); final SearchResult searchResult = getLivingApplicationAPI() .searchApplicationMenus(builder.done()); //then assertThat(searchResult).isNotNull(); assertThat(searchResult.getCount()).isEqualTo(2); assertThat(searchResult.getResult()).containsExactly(menu1, menu3); } @Test public void searchApplicationMenus_can_filter_on_parentId() throws Exception { //given final ApplicationMenu parentMenu = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "parent")); final ApplicationMenuCreator creator = new ApplicationMenuCreator(application.getId(), "child", appPage.getId()); creator.setParentId(parentMenu.getId()); final ApplicationMenu childMenu = getLivingApplicationAPI().createApplicationMenu(creator); getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "third", appPage.getId())); //when final SearchOptionsBuilder builder = getApplicationMenuSearchBuilder(0, 10); builder.filter(ApplicationMenuSearchDescriptor.PARENT_ID, parentMenu.getId()); final SearchResult searchResult = getLivingApplicationAPI() .searchApplicationMenus(builder.done()); //then assertThat(searchResult).isNotNull(); assertThat(searchResult.getCount()).isEqualTo(1); assertThat(searchResult.getResult()).containsExactly(childMenu); } @Test public void searchApplicationMenus_can_use_searchTerm() throws Exception { //given getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "first", appPage.getId())); final ApplicationMenu menu2 = getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "second", appPage.getId())); getLivingApplicationAPI() .createApplicationMenu(new ApplicationMenuCreator(application.getId(), "third", appPage.getId())); //when final SearchOptionsBuilder builder = getApplicationMenuSearchBuilder(0, 10); builder.searchTerm("second"); final SearchResult searchResult = getLivingApplicationAPI() .searchApplicationMenus(builder.done()); //then assertThat(searchResult).isNotNull(); assertThat(searchResult.getCount()).isEqualTo(1); assertThat(searchResult.getResult()).containsExactly(menu2); } private SearchOptionsBuilder buildSearchOptions(final int startIndex, final int maxResults) { final SearchOptionsBuilder builder = getApplicationMenuSearchBuilder(startIndex, maxResults); return builder; } private SearchOptionsBuilder getApplicationMenuSearchBuilder(final int startIndex, final int maxResults) { final SearchOptionsBuilder builder = new SearchOptionsBuilder(startIndex, maxResults); builder.sort(ApplicationMenuSearchDescriptor.INDEX, Order.ASC); return builder; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/application/LivingApplicationPageIT.java ================================================ /** * 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.business.application; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; import java.util.List; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.Test; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta */ public class LivingApplicationPageIT extends TestWithCustomPage { @Test public void createApplicationPage_returns_applicationPage_based_on_the_given_parameters() throws Exception { //given final Application application = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app", "My app", "1.0")); //when final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "firstPage"); //then assertThat(appPage.getId()).isGreaterThan(0); assertThat(appPage.getApplicationId()).isEqualTo(application.getId()); assertThat(appPage.getPageId()).isEqualTo(getPage().getId()); assertThat(appPage.getToken()).isEqualTo("firstPage"); getLivingApplicationAPI().deleteApplication(application.getId()); } @Test public void setApplicationHomePage_should_update_the_application_homePage() throws Exception { //given final Application application = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app", "My app", "1.0")); final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "firstPage"); //when getLivingApplicationAPI().setApplicationHomePage(application.getId(), appPage.getId()); //then final Application upToDateApp = (Application) getLivingApplicationAPI().getApplication(application.getId()); assertThat(upToDateApp.getHomePageId()).isEqualTo(appPage.getId()); getLivingApplicationAPI().deleteApplication(application.getId()); } @Test public void updateApplication_should_update_home_page() throws Exception { //given final ApplicationCreator creator = new ApplicationCreator("My-Application", "My application display name", "1.0"); final Application application = getLivingApplicationAPI().createApplication(creator); final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "firstPage"); final ApplicationUpdater updater = new ApplicationUpdater(); updater.setHomePageId(appPage.getId()); //when final Application updatedApplication = getLivingApplicationAPI().updateApplication(application.getId(), updater); //then assertThat(updatedApplication).isNotNull(); assertThat(updatedApplication.getHomePageId()).isEqualTo(appPage.getId()); getLivingApplicationAPI().deleteApplication(application.getId()); } @Test public void getApplicationPage_byNameAndAppName_returns_the_applicationPage_corresponding_to_the_given_parameters() throws Exception { //given final Application application = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app", "My app", "1.0")); final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "firstPage"); //when final ApplicationPage retrievedAppPage = getLivingApplicationAPI().getApplicationPage(application.getToken(), appPage.getToken()); //then assertThat(retrievedAppPage).isEqualTo(appPage); getLivingApplicationAPI().deleteApplication(application.getId()); } @Test public void getApplicationPage_byId_returns_the_applicationPage_corresponding_to_the_given_Id() throws Exception { //given final Application application = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app", "My app", "1.0")); final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "firstPage"); //when final ApplicationPage retrievedAppPage = getLivingApplicationAPI().getApplicationPage(appPage.getId()); //then assertThat(retrievedAppPage).isEqualTo(appPage); getLivingApplicationAPI().deleteApplication(application.getId()); } @Test public void deleteApplication_should_also_delete_related_applicationPage() throws Exception { //given final Application application = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app", "My app", "1.0")); final ApplicationPage homePage = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "firstPage"); getLivingApplicationAPI().setApplicationHomePage(application.getId(), homePage.getId()); final ApplicationPage aAppPage = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "secondPage"); //when getLivingApplicationAPI().deleteApplication(application.getId()); //then verifyNotExists(homePage); verifyNotExists(aAppPage); } private void verifyNotExists(ApplicationPage applicationPage) { try { getLivingApplicationAPI().getApplicationPage(applicationPage.getId()); //throws exception Assertions.fail("exception expected"); } catch (ApplicationPageNotFoundException e) { //OK } } @Test public void deleteApplicationPage_should_delete_applicationPage_with_the_given_id() throws Exception { //given final Application application = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app", "My app", "1.0")); final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "firstPage"); //when getLivingApplicationAPI().deleteApplicationPage(appPage.getId()); //then try { getLivingApplicationAPI().getApplicationPage(appPage.getId()); fail("Not found expected"); } catch (final ApplicationPageNotFoundException e) { //OK } } @Test public void getApplicationHomePage_should_return_application_homePage() throws Exception { //given final Application application = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app", "My app", "1.0")); final ApplicationPage appPage = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "firstPage"); getLivingApplicationAPI().setApplicationHomePage(application.getId(), appPage.getId()); //when final ApplicationPage homePage = getLivingApplicationAPI().getApplicationHomePage(application.getId()); //then assertThat(homePage).isEqualTo(appPage); getLivingApplicationAPI().deleteApplication(application.getId()); } @Test public void searchApplicationPages_without_filters_and_search_term_should_return_all_applicationPages_pagged() throws Exception { //given final Application application = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app", "My app", "1.0")); final ApplicationPage appPage1 = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "AAA_1"); final ApplicationPage appPage2 = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "AAA_2"); final ApplicationPage appPage3 = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "AAA_3"); //when final SearchResult searchResultPage1 = getLivingApplicationAPI() .searchApplicationPages(buildSearchOptions("AAA", 0, 2)); final SearchResult searchResultPage2 = getLivingApplicationAPI() .searchApplicationPages(buildSearchOptions("AAA", 2, 2)); //then assertThat(searchResultPage1).isNotNull(); assertThat(searchResultPage1.getCount()).isEqualTo(3); assertThat(searchResultPage1.getResult()).containsExactly(appPage1, appPage2); assertThat(searchResultPage2).isNotNull(); assertThat(searchResultPage2.getCount()).isEqualTo(3); assertThat(searchResultPage2.getResult()).containsExactly(appPage3); getLivingApplicationAPI().deleteApplication(application.getId()); } @Test public void searchApplicationPages_can_filter_on_name() throws Exception { //given final Application application = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app", "My app", "1.0")); getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "firstPage"); final ApplicationPage appPage2 = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "secondPage"); getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "thirdPage"); //when final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10); builder.filter(ApplicationPageSearchDescriptor.TOKEN, "secondPage"); final SearchResult searchResult = getLivingApplicationAPI() .searchApplicationPages(builder.done()); //then assertThat(searchResult).isNotNull(); assertThat(searchResult.getCount()).isEqualTo(1); assertThat(searchResult.getResult()).containsExactly(appPage2); getLivingApplicationAPI().deleteApplication(application.getId()); } @Test public void searchApplicationPages_can_filter_on_applicationId() throws Exception { //given final Application application1 = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app1", "My app 1", "1.0")); final Application application2 = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app2", "My app 2", "1.0")); final ApplicationPage appPage1 = getLivingApplicationAPI().createApplicationPage(application1.getId(), getPage().getId(), "firstPage"); getLivingApplicationAPI().createApplicationPage(application2.getId(), getPage().getId(), "secondPage"); final ApplicationPage appPage3 = getLivingApplicationAPI().createApplicationPage(application1.getId(), getPage().getId(), "thirdPage"); //when final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10); builder.filter(ApplicationPageSearchDescriptor.APPLICATION_ID, application1.getId()); final SearchResult searchResult = getLivingApplicationAPI() .searchApplicationPages(builder.done()); //then assertThat(searchResult).isNotNull(); assertThat(searchResult.getCount()).isEqualTo(2); assertThat(searchResult.getResult()).containsExactly(appPage1, appPage3); getLivingApplicationAPI().deleteApplication(application1.getId()); } @Test public void searchApplicationPages_can_filter_on_pageId() throws Exception { //given final Page page2 = createPage("custompage_MyPage2"); final Application application = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app", "My app", "1.0")); getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "firstPage"); final ApplicationPage appPage2 = getLivingApplicationAPI().createApplicationPage(application.getId(), page2.getId(), "secondPage"); final ApplicationPage appPage3 = getLivingApplicationAPI().createApplicationPage(application.getId(), page2.getId(), "thirdPage"); //when final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10); builder.filter(ApplicationPageSearchDescriptor.PAGE_ID, page2.getId()); final SearchResult searchResult = getLivingApplicationAPI() .searchApplicationPages(builder.done()); //then assertThat(searchResult).isNotNull(); assertThat(searchResult.getCount()).isEqualTo(2); assertThat(searchResult.getResult()).containsExactly(appPage2, appPage3); //clean getLivingApplicationAPI().deleteApplication(application.getId()); getPageAPI().deletePage(page2.getId()); } @Test public void searchApplicationPages_can_filter_on_id() throws Exception { //given final Application application = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app", "My app", "1.0")); getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "firstPage"); final ApplicationPage appPage2 = getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "secondPage"); getLivingApplicationAPI().createApplicationPage(application.getId(), getPage().getId(), "thirdPage"); //when final SearchOptionsBuilder builder = getAppSearchBuilderOrderByToken(0, 10); builder.filter(ApplicationPageSearchDescriptor.ID, appPage2.getId()); final SearchResult searchResult = getLivingApplicationAPI() .searchApplicationPages(builder.done()); //then assertThat(searchResult).isNotNull(); assertThat(searchResult.getCount()).isEqualTo(1); assertThat(searchResult.getResult()).containsExactly(appPage2); getLivingApplicationAPI().deleteApplication(application.getId()); } private SearchOptions buildSearchOptions(String prefix, final int startIndex, final int maxResults) { return getAppSearchBuilderOrderByToken(startIndex, maxResults) .searchTerm(prefix).done(); } @Test public void getAllAccessiblePageForAProfile() throws Exception { //given //profile1 Profile profile1 = getProfileUser(); //app1 final Application app1 = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app1", "My app1", "1.0").setProfileId(profile1.getId())); final Page page1 = createPage("custompage_page1"); getLivingApplicationAPI().createApplicationPage(app1.getId(), page1.getId(), "appPage1"); final Page page2 = createPage("custompage_page2"); getLivingApplicationAPI().createApplicationPage(app1.getId(), page2.getId(), "appPage2"); //app2 final Application app2 = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app2", "My app2", "1.0").setProfileId(profile1.getId())); final Page page3 = createPage("custompage_page3"); getLivingApplicationAPI().createApplicationPage(app2.getId(), page3.getId(), "appPage1"); //profile2 Profile profile2 = getProfileAdmin(); //app3 final Application app3 = getLivingApplicationAPI() .createApplication(new ApplicationCreator("app3", "My app3", "1.0").setProfileId(profile2.getId())); final Page page4 = createPage("custompage_page4"); getLivingApplicationAPI().createApplicationPage(app3.getId(), page4.getId(), "appPage1"); //when List allPagesForProfile1 = getLivingApplicationAPI().getAllPagesForProfile(profile1.getId()); List allPagesForProfile2 = getLivingApplicationAPI().getAllPagesForProfile(profile2.getId()); //then assertThat(allPagesForProfile1).containsExactlyInAnyOrder("custompage_themeBonita", "custompage_layoutBonita", "custompage_page1", "custompage_page2", "custompage_page3"); assertThat(getLivingApplicationAPI().getAllPagesForProfile(profile1.getName())).containsExactlyInAnyOrder( "custompage_themeBonita", "custompage_layoutBonita", "custompage_page1", "custompage_page2", "custompage_page3"); assertThat(allPagesForProfile2).containsExactlyInAnyOrder("custompage_themeBonita", "custompage_layoutBonita", "custompage_page4"); assertThat(getLivingApplicationAPI().getAllPagesForProfile(profile2.getName())) .containsExactlyInAnyOrder("custompage_themeBonita", "custompage_layoutBonita", "custompage_page4"); //clean getLivingApplicationAPI().deleteApplication(app1.getId()); getLivingApplicationAPI().deleteApplication(app2.getId()); getLivingApplicationAPI().deleteApplication(app3.getId()); getPageAPI().deletePage(page1.getId()); getPageAPI().deletePage(page2.getId()); getPageAPI().deletePage(page3.getId()); getPageAPI().deletePage(page4.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/data/BDMPostgreSQLTextFieldIT.java ================================================ /** * 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.business.data; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assume.assumeTrue; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import javax.naming.Context; import javax.sql.DataSource; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.api.TenantAdministrationAPI; import org.bonitasoft.engine.bdm.BusinessObjectModelConverter; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * Integration test to verify that TEXT fields in BDM are correctly mapped to PostgreSQL TEXT columns * instead of OID columns (which was the default behavior in Hibernate 5.6.2+). * This test validates the fix for the PostgreSQL TEXT field handling in EntityCodeGenerator * which uses @Type(type="org.bonitasoft.engine.persistence.PostgresMaterializedClobType) annotation * to force TEXT column type. */ public class BDMPostgreSQLTextFieldIT extends CommonAPIIT { private static final String BDM_PACKAGE_PREFIX = "com.company.model"; private static final String DOCUMENT_BO = "Document"; private TenantAdministrationAPI tenantAdministrationAPI; @Before public void setUp() throws Exception { loginWithTechnicalUser(); tenantAdministrationAPI = getTenantAdministrationAPI(); // Only run this test on PostgreSQL String dbVendor = System.getProperty("sysprop.bonita.bdm.db.vendor", "h2"); assumeTrue("This test only runs on PostgreSQL", dbVendor.toLowerCase().contains("postgres")); } @After public void cleanup() throws Exception { cleanAndUninstallBusinessDataModel(); logout(); } @Test public void should_create_TEXT_column_for_TEXT_fields_in_postgresql() throws Exception { // given: a BDM with a TEXT field final BusinessObject documentBO = new BusinessObject(); documentBO.setQualifiedName(BDM_PACKAGE_PREFIX + "." + DOCUMENT_BO); final SimpleField contentField = new SimpleField(); contentField.setName("content"); contentField.setType(FieldType.TEXT); contentField.setNullable(true); documentBO.addField(contentField); final SimpleField descriptionField = new SimpleField(); descriptionField.setName("description"); descriptionField.setType(FieldType.TEXT); descriptionField.setNullable(false); documentBO.addField(descriptionField); final BusinessObjectModel businessObjectModel = new BusinessObjectModel(); businessObjectModel.addBusinessObject(documentBO); // when: deploy the BDM final byte[] zip = new BusinessObjectModelConverter().zip(businessObjectModel); pauseTenantIfNeeded(); tenantAdministrationAPI.cleanAndUninstallBusinessDataModel(); final String businessDataModelVersion = tenantAdministrationAPI.updateBusinessDataModel(zip); resumeTenant(); assertThat(businessDataModelVersion).as("should have deployed BDM").isNotNull(); // then: verify column types in PostgreSQL database verifyColumnType("document", "content", "text"); verifyColumnType("document", "description", "text"); } private void verifyColumnType(String tableName, String columnName, String expectedType) throws Exception { // Get BDM datasource from JNDI Context initialContext = new javax.naming.InitialContext(); DataSource dataSource = (DataSource) initialContext.lookup("java:comp/env/NotManagedBizDataDS"); try (Connection connection = dataSource.getConnection()) { DatabaseMetaData metaData = connection.getMetaData(); // Query column information from PostgreSQL try (ResultSet rs = metaData.getColumns(null, null, tableName, columnName)) { assertThat(rs.next()) .as("Column " + columnName + " should exist in table " + tableName) .isTrue(); String typeName = rs.getString("TYPE_NAME"); assertThat(typeName.toLowerCase()) .as("Column " + columnName + " should be of type TEXT, not OID") .isEqualTo(expectedType); } } } private void cleanAndUninstallBusinessDataModel() throws Exception { pauseTenantIfNeeded(); final String version = tenantAdministrationAPI.getBusinessDataModelVersion(); if (version != null) { tenantAdministrationAPI.cleanAndUninstallBusinessDataModel(); } resumeTenant(); assertThat(tenantAdministrationAPI.getBusinessDataModelVersion()).as("should remove BDM").isNull(); } private void pauseTenantIfNeeded() throws Exception { if (!tenantAdministrationAPI.isPaused()) { tenantAdministrationAPI.pause(); } } private void resumeTenant() throws Exception { if (tenantAdministrationAPI.isPaused()) { tenantAdministrationAPI.resume(); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/data/BDMUpdateIT.java ================================================ /** * 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.business.data; import static org.assertj.core.api.Assertions.assertThat; import java.time.OffsetDateTime; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.api.TenantAdministrationAPI; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.RelationField; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.tenant.TenantResource; import org.junit.Before; import org.junit.Test; /** * Those tests fail on MySQL and SQLServer because after installing the second BDM version, Hibernate is not aware of * previous FK_ that stay in database, so it does not drop them, and then the 'drop table' fails on test cleanup. * Those cases should be handled when we support more "BDM update" scenarios, leveraging Hibernate poor level of * support on HBM2DDL. */ public class BDMUpdateIT extends CommonAPIIT { public static final String DOT = "."; public static final String PARENT_BO = "ParentBO"; public static final String CHILD_BO = "ChildBO"; public static final String OTHER_CHILD_BO = "OtherChildBO"; private static final String BDM_PACKAGE_PREFIX = "com.company.model"; private TenantAdministrationAPI tenantAdministrationAPI; @Before public void setUp() throws Exception { loginWithTechnicalUser(); tenantAdministrationAPI = getTenantAdministrationAPI(); } // No @After needed: CommonAPIIT.clean() handles BDM and logout @Test public void should_change_single_aggregation_relation() throws Exception { checkSingleRelation(RelationField.Type.AGGREGATION); } @Test public void should_change_single_composition_relation() throws Exception { checkSingleRelation(RelationField.Type.COMPOSITION); } @Test public void should_change_multiple_aggregation_relation() throws Exception { checkMultipleRelation(RelationField.Type.AGGREGATION); } @Test public void should_change_multiple_composition_relation() throws Exception { checkMultipleRelation(RelationField.Type.COMPOSITION); } protected void checkSingleRelation(RelationField.Type relationType) throws Exception { // given final BusinessObject businessObject = getBusinessObject(PARENT_BO); businessObject.addField(getSingleRelationField(getBusinessObject(CHILD_BO), relationType)); final String version = installBusinessDataModel(getBusinessObjectModel(businessObject)); ensureBDMIsInstalled(version); // when uninstallBusinessDataModel(); final BusinessObject newBusinessObject = getBusinessObject(PARENT_BO); newBusinessObject.addField(getSingleRelationField(getBusinessObject(OTHER_CHILD_BO), relationType)); final String newVersion = installBusinessDataModel(getBusinessObjectModel(newBusinessObject)); // then assertThat(newVersion).isNotNull().isNotEqualTo(version); } protected void checkMultipleRelation(RelationField.Type relationType) throws Exception { // given final BusinessObject businessObject = getBusinessObject(PARENT_BO); businessObject.addField(getMultipleRelationField(getBusinessObject(CHILD_BO), relationType)); final String version = installBusinessDataModel(getBusinessObjectModel(businessObject)); ensureBDMIsInstalled(version); // when uninstallBusinessDataModel(); final BusinessObject newBusinessObject = getBusinessObject(PARENT_BO); newBusinessObject.addField(getMultipleRelationField(getBusinessObject(OTHER_CHILD_BO), relationType)); final String newVersion = installBusinessDataModel(getBusinessObjectModel(newBusinessObject)); // then assertThat(newVersion).isNotNull().isNotEqualTo(version); } protected void resumeTenant() throws Exception { if (tenantAdministrationAPI.isPaused()) { tenantAdministrationAPI.resume(); } } protected BusinessObjectModel getBusinessObjectModel(BusinessObject parentBusinessObject) { final BusinessObjectModel businessObjectModel = new BusinessObjectModel(); businessObjectModel.addBusinessObject(parentBusinessObject); addChildBusinessObjects(businessObjectModel); return businessObjectModel; } protected void addChildBusinessObjects(BusinessObjectModel businessObjectModel1) { businessObjectModel1.addBusinessObject(getBusinessObject(BDMUpdateIT.CHILD_BO)); businessObjectModel1.addBusinessObject(getBusinessObject(BDMUpdateIT.OTHER_CHILD_BO)); } private BusinessObject getBusinessObject(String boName) { final BusinessObject businessObject = new BusinessObject(); businessObject.setQualifiedName(BDM_PACKAGE_PREFIX + DOT + boName); businessObject.addField(getSimpleField()); return businessObject; } private RelationField getSingleRelationField(BusinessObject businessObject, RelationField.Type relationType) { return getRelationField(businessObject, relationType, "single", Boolean.FALSE); } private RelationField getMultipleRelationField(BusinessObject businessObject, RelationField.Type relationType) { return getRelationField(businessObject, relationType, "multiple", Boolean.TRUE); } private RelationField getRelationField(BusinessObject businessObject, RelationField.Type relationType, String name, Boolean isCollection) { final RelationField relationField = new RelationField(); relationField.setType(relationType); relationField.setFetchType(RelationField.FetchType.LAZY); relationField.setName(name); relationField.setCollection(isCollection); relationField.setNullable(Boolean.TRUE); relationField.setReference(businessObject); return relationField; } private SimpleField getSimpleField() { SimpleField simpleField = new SimpleField(); simpleField.setName("name"); simpleField.setType(FieldType.STRING); return simpleField; } private void uninstallBusinessDataModel() throws Exception { loginWithTechnicalUser(); pauseTenantIfNeeded(); final String version = tenantAdministrationAPI.getBusinessDataModelVersion(); if (version != null) { tenantAdministrationAPI.uninstallBusinessDataModel(); } resumeTenant(); assertThat(tenantAdministrationAPI.getBusinessDataModelVersion()).as("should uninstall BusinessDataModel") .isNull(); } private void pauseTenantIfNeeded() throws UpdateException { if (!tenantAdministrationAPI.isPaused()) { tenantAdministrationAPI.pause(); } } private void ensureBDMIsInstalled(String bdmVersion) { assertThat(bdmVersion).as("should have deployed BDM").isNotNull(); TenantResource tenantResource = tenantAdministrationAPI.getBusinessDataModelResource(); assertThat(tenantResource.getLastUpdateDate()).isAfter(OffsetDateTime.now().minusMinutes(1)); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/data/BDRepositoryIT.java ================================================ /** * 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.business.data; import static java.util.Collections.singletonList; import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; import static org.apache.commons.lang3.StringUtils.substringAfter; import static org.apache.commons.lang3.StringUtils.substringBefore; import static org.assertj.core.api.Assertions.*; import static org.bonitasoft.engine.test.BDMTestUtil.*; import static org.bonitasoft.engine.test.BuildTestUtil.generateConnectorImplementation; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.IntStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import net.javacrumbs.jsonunit.core.Option; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.api.APIClient; import org.bonitasoft.engine.bdm.BusinessObjectDAOFactory; import org.bonitasoft.engine.bdm.BusinessObjectModelConverter; import org.bonitasoft.engine.bdm.dao.BusinessObjectDAO; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.RelationField; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryMetadata; import org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryResult; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.contract.Type; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.document.DocumentNotFoundException; import org.bonitasoft.engine.bpm.document.DocumentValue; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.TimerType; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessEnablementException; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder; import org.bonitasoft.engine.bpm.process.impl.CatchMessageEventTriggerDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.IntermediateThrowEventDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.StartEventDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ThrowMessageEventTriggerBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.command.CommandExecutionException; import org.bonitasoft.engine.command.CommandNotFoundException; import org.bonitasoft.engine.command.CommandParameterizationException; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.exception.UnavailableLockException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.expression.ExpressionEvaluationException; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.expression.impl.ExpressionImpl; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.test.APITestUtil; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BDRepositoryIT extends CommonAPIIT { private static final Logger log = LoggerFactory.getLogger(BDRepositoryIT.class); private static final String BUSINESS_DATA_CLASS_NAME_ID_FIELD = "/businessdata/{className}/{id}/{field}"; private static final String ENTITY_CLASS_NAME = "entityClassName"; private static String bdmDeployedVersion = "0"; private static int iterator = 1; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Rule public ExpectedException expectedException = ExpectedException.none(); private User testUser; private File clientFolder; private BusinessObjectModel model; @Before public void setUp() throws Exception { clientFolder = temporaryFolder.newFolder(); loginWithTechnicalUser(); testUser = createUser("testUser", "bpm"); assertThat(getTenantAdministrationAPI().isPaused()).as("should not have tenant is paused mode").isFalse(); model = buildBOM(); installAndVerifyBusinessDataModel(model); assertThat(getTenantAdministrationAPI().isPaused()) .as("should have resume tenant after installing Business Object Model").isFalse(); } @After public void tearDown() throws Exception { try { FileUtils.deleteDirectory(clientFolder); } catch (final Exception e) { clientFolder.deleteOnExit(); } if (!getTenantAdministrationAPI().isPaused()) { getTenantAdministrationAPI().pause(); } getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); getTenantAdministrationAPI().resume(); deleteUser(testUser); logout(); } @Test public void deploying_bdm_with_invalid_query_should_throw_a_BDM_deployment_exception() throws Exception { getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); assertThatThrownBy( () -> getTenantAdministrationAPI().updateBusinessDataModel(getZip(buildBOMWithInvalidQuery()))) .isInstanceOf(BusinessDataRepositoryDeploymentException.class); } @Test public void deploying_a_bdm_with_unique_constraint_on_multiple_field_should_fail_and_allow_to_deploy_a_clean_bdm_afterwards() throws Exception { // create bom - invalid final SimpleField name = new SimpleField(); name.setName("name"); name.setCollection(true); name.setType(FieldType.STRING); final BusinessObject countryBO = new BusinessObject(); countryBO.setQualifiedName(COUNTRY_QUALIFIED_NAME); countryBO.addField(name); countryBO.addUniqueConstraint("uk_name", "name"); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(countryBO); //test sequence - install should fail (Invalid BDM) getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); // the exception should be InvalidBusinessDataModelException assertThatThrownBy(() -> getTenantAdministrationAPI().updateBusinessDataModel(getZip(bom))) .isInstanceOf(BusinessDataRepositoryDeploymentException.class) .hasMessageContaining("Unable to create unique key constraint"); assertThat(getTenantAdministrationAPI().getBusinessDataModelVersion()).isNull(); // remove unique constraint & try to re-install - should work countryBO.setUniqueConstraints(Collections.emptyList()); getTenantAdministrationAPI().updateBusinessDataModel(getZip(bom)); assertThat(getTenantAdministrationAPI().getBusinessDataModelVersion()).isNotNull(); getTenantAdministrationAPI().resume(); } @Test public void deploying_bdm_after_process_should_put_process_in_resolved_state() throws Exception { final String qualifiedName = "com.company.test.Bo"; final BusinessObjectModel bom = buildSimpleBom(qualifiedName); final ProcessDefinition processDefinition = deploySimpleProcessWithBusinessData(qualifiedName); ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertThat(processDeploymentInfo.getConfigurationState()).isEqualTo(ConfigurationState.UNRESOLVED); installAndVerifyBusinessDataModel(bom); processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()); assertThat(processDeploymentInfo.getConfigurationState()).isEqualTo(ConfigurationState.RESOLVED); deleteProcess(processDefinition); } @Test public void should_keep_old_bdm_in_case_of_update_bdm_error() throws Exception { final AddressRef ref1 = new AddressRef("newYorkAddr", "33, corner street", "NY"); final AddressRef ref2 = new AddressRef("romeAddr", "2, plaza del popolo", "Roma"); addEmployee("Marcel", "Pagnol", ref1, ref2); final BusinessObjectModelConverter businessObjectConverter = new BusinessObjectModelConverter(); final String processContractInputName = "name_input"; final Expression countryExpression = new ExpressionBuilder().createGroovyScriptExpression( "createNewCountry", "import " + COUNTRY_QUALIFIED_NAME + "; Country c = new Country(); c.name = " + processContractInputName + "; return c;", COUNTRY_QUALIFIED_NAME, new ExpressionBuilder().createContractInputExpression(processContractInputName, String.class.getName())); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "shouldKeepOldBdmInCaseOfUpdateBdmError", "6.3-beta"); final String businessDataName = "countryName"; final String newCountryName = "France"; processDefinitionBuilder.addBusinessData(businessDataName, COUNTRY_QUALIFIED_NAME, countryExpression); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("start"); processDefinitionBuilder.addUserTask("step0", ACTOR_NAME); processDefinitionBuilder.addEndEvent("end"); processDefinitionBuilder.addTransition("start", "step0"); processDefinitionBuilder.addTransition("step0", "end"); processDefinitionBuilder.addContract().addInput(processContractInputName, Type.TEXT, null); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(definition.getId(), Collections.singletonMap(processContractInputName, newCountryName)); waitForUserTask("step0"); Map> expressions = new HashMap<>(1); getTenantAdministrationAPI().pause(); final String modelVersionInDatabase = getTenantAdministrationAPI().getBusinessDataModelVersion(); final BusinessObject countryBO = model.getBusinessObjects().stream() .filter(bo -> bo.getQualifiedName().equals(COUNTRY_QUALIFIED_NAME)).findFirst().get(); final SimpleField population = new SimpleField(); population.setName("population"); population.setType(FieldType.STRING); countryBO.addField(population); final BusinessObject employeeBO = model.getBusinessObjects().stream() .filter(bo -> bo.getQualifiedName().equals(EMPLOYEE_QUALIFIED_NAME)).findFirst().get(); ((RelationField) employeeBO.getField("addresses")).setType(RelationField.Type.COMPOSITION); try { getTenantAdministrationAPI().updateBusinessDataModel(businessObjectConverter.zip(model)); fail("should not be able to update the bdm"); } catch (BusinessDataRepositoryDeploymentException ignored) { log.error("ignored ", ignored); } assertThat(modelVersionInDatabase).isEqualTo(getTenantAdministrationAPI().getBusinessDataModelVersion()); getTenantAdministrationAPI().resume(); final String expressionPopulation = "bizDataExprName"; expressions.put(new ExpressionBuilder().createGroovyScriptExpression(expressionPopulation, businessDataName + ".population", String.class.getName(), new ExpressionBuilder().createBusinessDataExpression(businessDataName, COUNTRY_QUALIFIED_NAME)), null); try { getProcessAPI() .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions); fail("population field should not exist"); } catch (ExpressionEvaluationException ignored) { log.error("ignored population field", ignored); } disableAndDeleteProcess(definition.getId()); } @Test public void should_not_fail_when_resuming_tenant_during_install_of_bdm() throws Exception { getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); Future installOfTheBDM = Executors.newSingleThreadExecutor().submit(() -> { try { return getTenantAdministrationAPI().updateBusinessDataModel(getZip(businessObjectModel(bom -> { bom.addBusinessObject(businessObject("com.company.ExampleBusinessObject", (businessObject) -> { businessObject.addField(stringField("aField")); businessObject.addQuery("findExampleByAField", "SELECT e FROM ExampleBusinessObject e", "com.company.ExampleBusinessObject"); })); }))); } catch (Exception e) { throw new RuntimeException(e); } }); log.info("Sleeping 80 ms to let installBusinessDataModel() begin before trying to resume tenant"); Thread.sleep(80); getTenantAdministrationAPI().resume(); installOfTheBDM.get(10, TimeUnit.SECONDS); assertThatNoException().isThrownBy(() -> getBusinessDataByQuery(Collections.emptyMap(), 0, 10, "findExampleByAField", "com.company.ExampleBusinessObject")); } private void installAndVerifyBusinessDataModel(final BusinessObjectModel bom) throws Exception { var businessDataModelVersion = installBusinessDataModel(bom); assertThat(businessDataModelVersion).as("should have deployed BDM").isNotNull(); verifyBdmIsWellDeployed(); } private void verifyBdmIsWellDeployed() throws Exception { final String businessDataModelVersion = getTenantAdministrationAPI().getBusinessDataModelVersion(); APITestUtil.LOGGER.warn("previous businessDataModelVersion:" + bdmDeployedVersion); APITestUtil.LOGGER.warn("new businessDataModelVersion :" + businessDataModelVersion); assertThat(businessDataModelVersion).as("should have deployed a new version of BDM") .isNotEqualTo(bdmDeployedVersion); bdmDeployedVersion = businessDataModelVersion; } private ProcessDefinition deploySimpleProcessWithBusinessData(final String aQualifiedName) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("test", "1.2-alpha"); processDefinitionBuilder.addActor(ACTOR_NAME); final String bizDataName = "myBizData"; processDefinitionBuilder.addBusinessData(bizDataName, aQualifiedName, null); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDefinitionBuilder.done()).done()); getProcessAPI().addUserToActor(ACTOR_NAME, processDefinition, testUser.getId()); return processDefinition; } @Test public void shouldBeAbleToUpdateBusinessDataUsingBizDataJavaSetterOperation() throws Exception { final String processContractInputName = "lastName_input"; final String initialLastNameValue = "Trebi"; final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression( "createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee e = new Employee(); e.firstName = 'Jules'; e.lastName = " + processContractInputName + "; return e;", EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createContractInputExpression(processContractInputName, String.class.getName())); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "shouldBeAbleToUpdateBusinessDataUsingJavaSetterOperation", "6.3-beta"); final String businessDataName = "newBornBaby"; final String newEmployeeFirstName = "Manon"; final String newEmployeeLastName = "Péuigrec"; processDefinitionBuilder.addBusinessData(businessDataName, EMPLOYEE_QUALIFIED_NAME, employeeExpression); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step0", ACTOR_NAME); processDefinitionBuilder .addAutomaticTask("step1") .addOperation( new OperationBuilder().createBusinessDataSetAttributeOperation(businessDataName, "setFirstName", String.class.getName(), new ExpressionBuilder().createConstantStringExpression(newEmployeeFirstName))) .addOperation( new OperationBuilder().createBusinessDataSetAttributeOperation(businessDataName, "setLastName", String.class.getName(), new ExpressionBuilder().createConstantStringExpression(newEmployeeLastName))); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addTransition("step0", "step1"); processDefinitionBuilder.addTransition("step1", "step2"); processDefinitionBuilder.addContract().addInput(processContractInputName, Type.TEXT, null); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(definition.getId(), Collections.singletonMap(processContractInputName, initialLastNameValue)); final long step0 = waitForUserTask(processInstance, "step0"); // Check that initial BizData value used process contract input: Map> expressions = new HashMap<>(1); final String expressionName = "bizDataExprName"; expressions.put(new ExpressionBuilder().createGroovyScriptExpression(expressionName, businessDataName + ".lastName", String.class.getName(), new ExpressionBuilder().createBusinessDataExpression(businessDataName, EMPLOYEE_QUALIFIED_NAME)), null); final String returnedInitialLastName = (String) getProcessAPI() .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions).get( expressionName); assertThat(returnedInitialLastName).isEqualTo(initialLastNameValue); assignAndExecuteStep(step0, testUser); final long step2 = waitForUserTask(processInstance, "step2"); // Let's check the updated firstName + lastName values by calling an expression: expressions = new HashMap<>(2); final String expressionFirstName = "retrieve_FirstName"; expressions.put(new ExpressionBuilder().createGroovyScriptExpression(expressionFirstName, businessDataName + ".firstName", String.class.getName(), new ExpressionBuilder().createBusinessDataExpression(businessDataName, EMPLOYEE_QUALIFIED_NAME)), null); final String expressionLastName = "retrieve_new_lastName"; expressions.put(new ExpressionBuilder().createGroovyScriptExpression(expressionLastName, businessDataName + ".lastName", String.class.getName(), new ExpressionBuilder().createBusinessDataExpression(businessDataName, EMPLOYEE_QUALIFIED_NAME)), null); final Map evaluatedExpressions = getProcessAPI() .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions); final String returnedFirstName = (String) evaluatedExpressions.get(expressionFirstName); final String returnedLastName = (String) evaluatedExpressions.get(expressionLastName); assertThat(returnedFirstName).isEqualTo(newEmployeeFirstName); assertThat(returnedLastName).isEqualTo(newEmployeeLastName); assertCount(processInstance.getId()); assignAndExecuteStep(step2, testUser); disableAndDeleteProcess(definition.getId()); } @Test public void deployABDRAndCreateADefaultBusinessDataAndReuseReference() throws Exception { final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee e = new Employee(); e.firstName = 'Jane'; e.lastName = 'Doe'; return e;", EMPLOYEE_QUALIFIED_NAME); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("test", "1.2-alpha"); processDefinitionBuilder.addBusinessData("myEmployee", EMPLOYEE_QUALIFIED_NAME, employeeExpression); final String secondBizData = "people"; processDefinitionBuilder.addBusinessData(secondBizData, EMPLOYEE_QUALIFIED_NAME, null); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME).addOperation( new OperationBuilder().attachBusinessDataSetAttributeOperation(secondBizData, new ExpressionBuilder().createQueryBusinessDataExpression( "oneEmployee", "Employee." + GET_EMPLOYEE_BY_LAST_NAME_QUERY_NAME, EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createConstantStringExpression("lastName", "Doe")))); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addTransition("step1", "step2"); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); final String employeeToString = getEmployeeToString("myEmployee", processInstance.getId()); assertThat(employeeToString).isEqualTo("Employee [firstName=Jane, lastName=Doe]"); assignAndExecuteStep(step1Id, testUser); waitForUserTask(processInstance, "step2"); final String people = getEmployeeToString(secondBizData, processInstance.getId()); assertThat(people).isEqualTo("Employee [firstName=Jane, lastName=Doe]"); disableAndDeleteProcess(definition.getId()); } @Test public void deployABDRAndCreateABOAndUpdateThroughAGroovyScript() throws Exception { final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee e = new Employee(); e.firstName = 'John'; e.lastName = 'Doe'; return e;", EMPLOYEE_QUALIFIED_NAME); final Expression getEmployeeExpression = new ExpressionBuilder().createBusinessDataExpression("myEmployee", EMPLOYEE_QUALIFIED_NAME); // try to modify the business data final Expression scriptExpression = new ExpressionBuilder().createGroovyScriptExpression("updateBizData", "myEmployee.lastName = 'BPM'; return 'BPM'", String.class.getName(), getEmployeeExpression); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("test", "1.2-alpha"); processDefinitionBuilder.addBusinessData("myEmployee", EMPLOYEE_QUALIFIED_NAME, employeeExpression); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME).addDisplayDescription(scriptExpression); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance instance = getProcessAPI().startProcess(definition.getId()); waitForUserTask(instance, "step1"); Thread.sleep(100); // this is a small wait in order to avoid issue on sql server when transaction has more than one XA resource final String employeeToString = getEmployeeToString("myEmployee", instance.getId()); assertThat(employeeToString).isEqualTo("Employee [firstName=John, lastName=BPM]"); disableAndDeleteProcess(definition.getId()); } @Test public void updatingABOThroughAGroovyScriptShouldUpdateTheBO() throws Exception { String businessDataName = "myEmployee"; final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("BS-18402", "7.7.2"); processDefinitionBuilder.addBusinessData(businessDataName, EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createGroovyScriptExpression("createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee e = new Employee(); e.firstName = 'John'; e.lastName = 'Doe'; return e;", EMPLOYEE_QUALIFIED_NAME)); processDefinitionBuilder.addBusinessData("myAddress", ADDRESS_QUALIFIED_NAME, new ExpressionBuilder().createGroovyScriptExpression("CreateNewAddress", "import " + ADDRESS_QUALIFIED_NAME + "; Address a = new Address(); a.street='32, rue Gustave Eiffel'; a.city='Grenoble'; return a;", ADDRESS_QUALIFIED_NAME)); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addAutomaticTask("step1") .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(businessDataName), OperatorType.ASSIGNMENT, null, null, new ExpressionBuilder().createGroovyScriptExpression("setEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "; myEmployee.setAddress(myAddress); return myEmployee;", EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createBusinessDataExpression("myAddress", ADDRESS_QUALIFIED_NAME), new ExpressionBuilder().createBusinessDataExpression(businessDataName, EMPLOYEE_QUALIFIED_NAME))); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addStartEvent("Start"); processDefinitionBuilder.addEndEvent("End"); processDefinitionBuilder.addTransition("Start", "step1"); processDefinitionBuilder.addTransition("step1", "step2"); processDefinitionBuilder.addTransition("step2", "End"); processDefinitionBuilder.addContextEntry("myEmployee_context_key", new ExpressionBuilder().createGroovyScriptExpression("retrieveEmployeeAddressBasicInfo", "\"Employee [ address street = \" + " + businessDataName + ".address.street + \" ]\";", String.class.getName(), new ExpressionBuilder().createBusinessDataExpression(businessDataName, EMPLOYEE_QUALIFIED_NAME))); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance instance = getProcessAPI().startProcess(definition.getId()); waitForUserTask(instance.getId(), "step2"); final Serializable employeeFromContext = getProcessAPI().getProcessInstanceExecutionContext(instance.getId()) .get("myEmployee_context_key"); assertThat(employeeFromContext).isEqualTo("Employee [ address street = 32, rue Gustave Eiffel ]"); disableAndDeleteProcess(definition.getId()); } @Test(expected = ProcessEnablementException.class) public void deployProcessWithWrongBusinessDataTypeShouldNotBeDeployable() throws Exception { final User user = createUser("login1", "password"); ProcessDefinition processDefinition = null; try { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("firstProcess", "1.0"); processBuilder.addActor("myActor"); processBuilder.addBusinessData("myBizData", Long.class.getName(), new ExpressionBuilder().createConstantLongExpression(12L)); processBuilder.addUserTask("Request", "myActor"); processDefinition = getProcessAPI().deploy(processBuilder.done()); addUserToFirstActorOfProcess(user.getId(), processDefinition); getProcessAPI().enableProcess(processDefinition.getId()); // Should not fail here, if the Server process model is valid: } finally { deleteProcess(processDefinition); deleteUser(user); } } @Test public void deployABDRAndExecuteAGroovyScriptWhichContainsAPOJOFromTheBDR() throws Exception { final Expression stringExpression = new ExpressionBuilder() .createGroovyScriptExpression( "alive", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee e = new Employee(); e.firstName = 'John'; e.lastName = 'Doe'; return \"Employee [firstName=\" + e.firstName + \", lastName=\" + e.lastName + \"]\"", String.class.getName()); final Map> expressions = new HashMap<>(); expressions.put(stringExpression, new HashMap()); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("test", "1.2-alpha"); processDefinitionBuilder.addAutomaticTask("stepO"); final ProcessDefinition processDefinition = getProcessAPI().deploy(processDefinitionBuilder.done()); getProcessAPI().enableProcess(processDefinition.getId()); final Map result = getProcessAPI() .evaluateExpressionsOnProcessDefinition(processDefinition.getId(), expressions); assertThat(result).hasSize(1); final Set> entrySet = result.entrySet(); final Entry entry = entrySet.iterator().next(); assertThat(entry.getValue()).isEqualTo("Employee [firstName=John, lastName=Doe]"); disableAndDeleteProcess(processDefinition.getId()); } @Test(expected = BonitaRuntimeException.class) public void createAnEmployeeWithARequiredFieldAtNullThrowsAnException() throws Exception { final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee e = new Employee(); e.firstName = 'John'; return e;", EMPLOYEE_QUALIFIED_NAME); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("test", "1.2-alpha"); processDefinitionBuilder.addBusinessData("myEmployee", EMPLOYEE_QUALIFIED_NAME, employeeExpression); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); try { getProcessAPI().startProcess(definition.getId()); } finally { disableAndDeleteProcess(definition.getId()); } } @Test(expected = BonitaRuntimeException.class) public void createAnEmployeeWithATooSmallFieldAtNullThrowsAnException() throws Exception { final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee e = new Employee(); e.firstName = 'John124578/'; e.lastName = 'Doe'; return e;", EMPLOYEE_QUALIFIED_NAME); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("test", "1.2-alpha"); processDefinitionBuilder.addBusinessData("myEmployee", EMPLOYEE_QUALIFIED_NAME, employeeExpression); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); try { getProcessAPI().startProcess(definition.getId()); } finally { disableAndDeleteProcess(definition.getId()); } } @Test public void updateBusinessDataShouldWorkOutsideATransaction() throws Exception { final String taskName = "step"; final ProcessDefinition definition = buildProcessThatUpdateBizDataInsideConnector(taskName); final ProcessInstance instance = getProcessAPI().startProcess(definition.getId()); waitForUserTask(instance, taskName); final String employeeToString = getEmployeeToString("myEmployee", instance.getId()); assertThat(employeeToString).isEqualTo("Employee [firstName=John, lastName=Hakkinen]"); assertCount(instance.getId()); disableAndDeleteProcess(definition); } @Test public void should_deploy_generate_client_bdm_zip() throws Exception { ArrayList entries = listEntries(getTenantAdministrationAPI().getClientBDMZip()); assertThat(entries).containsOnly("README.md", "example-pom.xml", "bdm-dao.jar", "bdm-model.jar", "bom.zip"); } private ArrayList listEntries(byte[] clientBDMZip) throws IOException { ArrayList entries = new ArrayList<>(); ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(clientBDMZip)); ZipEntry nextEntry; while ((nextEntry = zipInputStream.getNextEntry()) != null) { entries.add(nextEntry.getName()); } zipInputStream.close(); return entries; } @Test public void should_undeploy_delete_generate_client_bdm_zip() throws Exception { loginWithTechnicalUser(); getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); getTenantAdministrationAPI().resume(); expectedException.expect(BusinessDataRepositoryException.class); getTenantAdministrationAPI().getClientBDMZip(); } @Test public void shouldBeAbleToRunDAOCallThroughGroovy() throws Exception { final String firstName = "FlofFlof"; final String lastName = "Boudin"; final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "; import " + ADDRESS_QUALIFIED_NAME + "; Employee e = new Employee(); e.firstName = '" + firstName + "'; e.lastName = '" + lastName + "'; e.addToAddresses(myAddress); return e;", EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createBusinessDataExpression("myAddress", ADDRESS_QUALIFIED_NAME)); final Expression addressExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewAddress", "import " + ADDRESS_QUALIFIED_NAME + "; Address a = new Address(); a.street='32, rue Gustave Eiffel'; a.city='Grenoble'; return a;", ADDRESS_QUALIFIED_NAME); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "shouldBeAbleToRunDAOCallThroughGroovy", "6.3.1"); final String employeeDAOName = "employeeDAO"; final String bizDataName = "myEmployee"; processDefinitionBuilder.addBusinessData(bizDataName, EMPLOYEE_QUALIFIED_NAME, null); processDefinitionBuilder.addBusinessData("myAddress", ADDRESS_QUALIFIED_NAME, null); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addAutomaticTask("step1") .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand("myAddress"), OperatorType.ASSIGNMENT, null, null, addressExpression) .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(bizDataName), OperatorType.ASSIGNMENT, null, null, employeeExpression); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addTransition("step1", "step2"); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); waitForUserTask(processInstance, "step2"); // Let's check we can retrieve firstName using DAO call: final long processInstanceId = processInstance.getId(); final Map> expressions = new HashMap<>(1); final String getLastNameWithDAOExpression = "retrieveEmployeeByFirstName"; expressions.put( new ExpressionBuilder().createGroovyScriptExpression(getLastNameWithDAOExpression, "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee e = " + employeeDAOName + ".findByFirstName('" + firstName + "', 0, 10).get(0); e.getAddresses().get(0).city", String.class.getName(), new ExpressionBuilder().buildBusinessObjectDAOExpression(employeeDAOName, EMPLOYEE_QUALIFIED_NAME + "DAO")), null); expressions.put(new ExpressionBuilder().createQueryBusinessDataExpression(COUNT_ADDRESS, "Address." + COUNT_ADDRESS, Long.class.getName()), null); expressions.put(new ExpressionBuilder().createQueryBusinessDataExpression(COUNT_EMPLOYEE, "Employee." + COUNT_EMPLOYEE, Long.class.getName()), null); Map evaluatedExpressions = getProcessAPI() .evaluateExpressionsOnProcessInstance(processInstanceId, expressions); String returnedLastName = (String) evaluatedExpressions.get(getLastNameWithDAOExpression); assertThat(returnedLastName).isEqualTo("Grenoble"); final Serializable nbOfAddress = evaluatedExpressions.get(COUNT_ADDRESS); final Serializable nbOfEmployee = evaluatedExpressions.get(COUNT_EMPLOYEE); assertThat(nbOfAddress).isEqualTo(1L); assertThat(nbOfEmployee).isEqualTo(1L); logout(); loginWithTechnicalUser(); getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().resume(); logout(); loginOnDefaultTenantWith("testUser", "bpm"); evaluatedExpressions = getProcessAPI().evaluateExpressionsOnProcessInstance(processInstanceId, expressions); returnedLastName = (String) evaluatedExpressions.get(getLastNameWithDAOExpression); assertThat(returnedLastName).isEqualTo("Grenoble"); logout(); loginWithTechnicalUser(); assertCount(processInstanceId); disableAndDeleteProcess(definition.getId()); } /** * {@link BDRepositoryIT#should_use_apiClient_to_instantiate_dao_on_client_side} * * @throws Exception */ @Test @Deprecated public void should_use_factory_to_instantiate_dao_on_client_side() throws Exception { final AddressRef ref1 = new AddressRef("newYorkAddr", "33, corner street", "NY"); final AddressRef ref2 = new AddressRef("romeAddr", "2, plaza del popolo", "Roma"); addEmployee("Marcel", "Pagnol", ref1, ref2); final APISession apiSession = getSession(); final byte[] clientBDMZip = getTenantAdministrationAPI().getClientBDMZip(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); final ClassLoader classLoaderWithBDM = new ClassloaderRefresher().loadClientModelInClassloader(clientBDMZip, contextClassLoader, EMPLOYEE_QUALIFIED_NAME, clientFolder); try { Thread.currentThread().setContextClassLoader(classLoaderWithBDM); @SuppressWarnings("unchecked") final Class daoInterface = (Class) Class.forName( EMPLOYEE_QUALIFIED_NAME + "DAO", true, classLoaderWithBDM); final BusinessObjectDAOFactory businessObjectDAOFactory = new BusinessObjectDAOFactory(); final BusinessObjectDAO daoImpl = businessObjectDAOFactory.createDAO(apiSession, daoInterface); assertThat(daoImpl.getClass().getName()).isEqualTo(EMPLOYEE_QUALIFIED_NAME + "DAOImpl"); Method daoMethod = daoImpl.getClass().getMethod("findByLastName", String.class, int.class, int.class); assertThat(daoMethod).isNotNull(); assertThat(daoMethod.getReturnType().getName()).isEqualTo(List.class.getName()); List result = (List) daoMethod.invoke(daoImpl, "Pagnol", 0, 10); assertThat(result).isNotEmpty().hasSize(1); result = (List) daoMethod.invoke(daoImpl, "Hanin", 0, 10); assertThat(result).isEmpty(); daoMethod = daoImpl.getClass().getMethod("findByFirstNameAndLastName", String.class, String.class); assertThat(daoMethod).isNotNull(); assertThat(daoMethod.getReturnType().getName()).isEqualTo(EMPLOYEE_QUALIFIED_NAME); final Object employee = daoMethod.invoke(daoImpl, "Marcel", "Pagnol"); assertThat(employee).isNotNull(); final List lazyAddresses = (List) employee.getClass().getMethod("getAddresses", new Class[0]) .invoke(employee); assertThat(lazyAddresses).hasSize(2); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } @Test public void should_use_apiClient_to_instantiate_dao_on_client_side() throws Exception { final AddressRef ref1 = new AddressRef("newYorkAddr", "33, corner street", "NY"); final AddressRef ref2 = new AddressRef("romeAddr", "2, plaza del popolo", "Roma"); addEmployee("Marcel", "Pagnol", ref1, ref2); final APISession apiSession = getSession(); final byte[] clientBDMZip = getTenantAdministrationAPI().getClientBDMZip(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); final ClassLoader classLoaderWithBDM = new ClassloaderRefresher().loadClientModelInClassloader(clientBDMZip, contextClassLoader, EMPLOYEE_QUALIFIED_NAME, clientFolder); try { Thread.currentThread().setContextClassLoader(classLoaderWithBDM); @SuppressWarnings("unchecked") final Class daoInterface = (Class) Class.forName( EMPLOYEE_QUALIFIED_NAME + "DAO", true, classLoaderWithBDM); final APIClient client = new APIClient(apiSession); final BusinessObjectDAO daoImpl = client.getDAO(daoInterface); assertThat(daoImpl.getClass().getName()).isEqualTo(EMPLOYEE_QUALIFIED_NAME + "DAOImpl"); Method daoMethod = daoImpl.getClass().getMethod("countForFindByLastName", String.class); assertThat(daoMethod).isNotNull(); assertThat(daoMethod.getReturnType().getName()).isEqualTo(Long.class.getName()); final Long count = (Long) daoMethod.invoke(daoImpl, "Pagnol"); assertThat(count).isEqualTo(1); daoMethod = daoImpl.getClass().getMethod("findByLastName", String.class, int.class, int.class); assertThat(daoMethod).isNotNull(); assertThat(daoMethod.getReturnType().getName()).isEqualTo(List.class.getName()); List result = (List) daoMethod.invoke(daoImpl, "Pagnol", 0, 10); assertThat(result).isNotEmpty().hasSize(1); result = (List) daoMethod.invoke(daoImpl, "Hanin", 0, 10); assertThat(result).isEmpty(); daoMethod = daoImpl.getClass().getMethod("findByFirstNameAndLastName", String.class, String.class); assertThat(daoMethod).isNotNull(); assertThat(daoMethod.getReturnType().getName()).isEqualTo(EMPLOYEE_QUALIFIED_NAME); final Object employee = daoMethod.invoke(daoImpl, "Marcel", "Pagnol"); assertThat(employee).isNotNull(); final List lazyAddresses = (List) employee.getClass().getMethod("getAddresses", new Class[0]) .invoke(employee); assertThat(lazyAddresses).hasSize(2); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } @Test public void should_retrieve_bdm_object_with_lazy_and_non_lazy_composition_objects_using_dao() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("test", "1.2-alpha"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addBusinessData("myEmployee", EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createGroovyScriptExpression("createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "\n" + "import " + DOG_QUALIFIED_NAME + "\n" + "import " + CAT_QUALIFIED_NAME + "\n" + "Employee e = new Employee()\n" + "e.firstName ='john'\n" + "e.lastName ='doe'\n" + "def d = new Dog()\n" + "d.name = 'kiki'\n" + "d.age = 2\n" + "e.setDog(d)\n" + "def c = new Cat()\n" + "c.name = 'fifi'\n" + "c.age = 5\n" + "e.setCat(c)\n" + "return e", EMPLOYEE_QUALIFIED_NAME)); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance instance = getProcessAPI().startProcess(definition.getId()); waitForUserTask(instance, "step1"); disableAndDeleteProcess(definition.getId()); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); final ClassLoader classLoaderWithBDM = new ClassloaderRefresher().loadClientModelInClassloader( getTenantAdministrationAPI().getClientBDMZip(), contextClassLoader, EMPLOYEE_QUALIFIED_NAME, clientFolder); try { Thread.currentThread().setContextClassLoader(classLoaderWithBDM); final BusinessObjectDAO daoImpl = new APIClient(getSession()) .getDAO((Class) Class.forName(EMPLOYEE_QUALIFIED_NAME + "DAO", true, classLoaderWithBDM)); List employees = (List) daoImpl.getClass().getMethod("find", int.class, int.class).invoke(daoImpl, 0, 100); assertThat(invokeMethod(invokeMethod(employees.get(0), "getCat"), "getName")).isEqualTo("fifi"); assertThat(invokeMethod(invokeMethod(employees.get(0), "getDog"), "getName")).isEqualTo("kiki"); Object employee = daoImpl.getClass().getMethod("findByFirstNameAndLastName", String.class, String.class) .invoke(daoImpl, "john", "doe"); assertThat(invokeMethod(invokeMethod(employee, "getCat"), "getName")).isEqualTo("fifi"); assertThat(invokeMethod(invokeMethod(employee, "getDog"), "getName")).isEqualTo("kiki"); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } private Object invokeMethod(Object object, String method) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { return object.getClass().getMethod(method, new Class[0]).invoke(object); } private void addEmployee(final String firstName, final String lastName, final AddressRef... addresses) throws Exception { final List dependencies = new ArrayList<>(); for (final AddressRef ref : addresses) { dependencies.add(ref.getExpression()); } final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewEmployee", createNewEmployeeScriptContent(firstName, lastName, addresses), EMPLOYEE_QUALIFIED_NAME, dependencies); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("test", "1.2-alpha"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addBusinessData("myEmployee", EMPLOYEE_QUALIFIED_NAME, null); final UserTaskDefinitionBuilder task = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); for (final AddressRef ref : addresses) { processDefinitionBuilder.addBusinessData(ref.getVarName(), ADDRESS_QUALIFIED_NAME, null); task.addOperation(ref.getCreationOperation()); } task.addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand("myEmployee"), OperatorType.ASSIGNMENT, null, null, employeeExpression); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance instance = getProcessAPI().startProcess(definition.getId()); waitForUserTaskAndExecuteIt(instance, "step1", testUser); waitForProcessToFinish(instance); disableAndDeleteProcess(definition.getId()); } private String createNewEmployeeScriptContent(final String firstName, final String lastName, final AddressRef... addresses) { final StringBuilder sb = new StringBuilder(); sb.append("import "); sb.append(EMPLOYEE_QUALIFIED_NAME); sb.append("\n"); sb.append("import "); sb.append(ADDRESS_QUALIFIED_NAME); sb.append("\n"); sb.append("Employee e = new Employee();"); sb.append("\n"); sb.append("e.firstName ="); sb.append("'" + firstName + "'"); sb.append("\n"); sb.append("e.lastName ="); sb.append("'" + lastName + "'"); sb.append("\n"); if (addresses != null) { for (AddressRef address : addresses) { sb.append("e.addToAddresses(" + address.getVarName() + ")"); sb.append("\n"); } } sb.append("return e;"); return sb.toString(); } private ProcessDefinition buildProcessThatUpdateBizDataInsideConnector(final String taskName) throws Exception { final Expression getEmployeeExpression = new ExpressionBuilder().createBusinessDataExpression("myEmployee", EMPLOYEE_QUALIFIED_NAME); final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression( "createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee e = new Employee(); e.firstName = 'John'; e.lastName = 'Doe'; e.addToPhoneNumbers('78945612'); return e;", EMPLOYEE_QUALIFIED_NAME); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("BizDataAndConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addBusinessData("myEmployee", EMPLOYEE_QUALIFIED_NAME, employeeExpression); processDefinitionBuilder .addUserTask(taskName, ACTOR_NAME) .addConnector("updateBusinessData", "org.bonitasoft.connector.BusinessDataUpdateConnector", "1.0", ConnectorEvent.ON_ENTER) .addInput("bizData", getEmployeeExpression) .addOutput( new OperationBuilder().createBusinessDataSetAttributeOperation("myEmployee", "setLastName", String.class.getName(), new ExpressionBuilder().createGroovyScriptExpression( "retrieve modified lastname from connector", "output1.getLastName()", String.class.getName(), new ExpressionBuilder() .createBusinessDataExpression("output1", EMPLOYEE_QUALIFIED_NAME)))); final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition( processDefinitionBuilder.done()); BarResource barResource = getBarResource( "/org/bonitasoft/engine/business/data/BusinessDataUpdateConnector.impl", "BusinessDataUpdateConnector.impl", BDRepositoryIT.class); businessArchiveBuilder.addConnectorImplementation(barResource); barResource = BuildTestUtil.generateJarAndBuildBarResource(BusinessDataUpdateConnector.class, "BusinessDataUpdateConnector.jar"); businessArchiveBuilder.addClasspathResource(barResource); return deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, testUser); } private String getEmployeeToString(final String businessDataName, final long processInstanceId) throws InvalidExpressionException { final Map> expressions = new HashMap<>(5); final String expressionEmployee = "retrieve_Employee"; expressions.put( new ExpressionBuilder().createGroovyScriptExpression(expressionEmployee, "\"Employee [firstName=\" + " + businessDataName + ".firstName + \", lastName=\" + " + businessDataName + ".lastName + \"]\";", String.class.getName(), new ExpressionBuilder().createBusinessDataExpression(businessDataName, EMPLOYEE_QUALIFIED_NAME)), null); try { final Map evaluatedExpressions = getProcessAPI() .evaluateExpressionsOnProcessInstance(processInstanceId, expressions); return (String) evaluatedExpressions.get(expressionEmployee); } catch (final ExpressionEvaluationException eee) { System.err.println(eee.getMessage()); return null; } } @Test public void shouldBeAbleToCreate2BusinessDataUsingIntermixedBizDataJavaSetterOperations() throws Exception { final Expression countryQueryNameParameter = new ExpressionBuilder().createExpression("name", "France", String.class.getName(), ExpressionType.TYPE_CONSTANT); final Expression countryQueryExpression = new ExpressionBuilder().createQueryBusinessDataExpression("country", "Country.findByName", COUNTRY_QUALIFIED_NAME, countryQueryNameParameter); final Expression createNewAddressExpression = new ExpressionBuilder().createGroovyScriptExpression( "createNewAddress", "import " + ADDRESS_QUALIFIED_NAME + "; Address a = new Address(street:'32, rue Gustave Eiffel', city:'Grenoble'); a;", ADDRESS_QUALIFIED_NAME); final Expression createNewCountryExpression = new ExpressionBuilder().createGroovyScriptExpression( "createNewCountry", "import " + COUNTRY_QUALIFIED_NAME + "; Country c = new Country(name:'France'); c;", COUNTRY_QUALIFIED_NAME); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "shouldBeAbleToUpdateBusinessDataUsingJavaSetterOperation", PROCESS_VERSION); final String businessDataName = "newBornBaby"; final String businessDataName2 = "data2"; processDefinitionBuilder.addBusinessData(businessDataName, EMPLOYEE_QUALIFIED_NAME, null); processDefinitionBuilder.addBusinessData(businessDataName2, EMPLOYEE_QUALIFIED_NAME, null); processDefinitionBuilder.addBusinessData("address", ADDRESS_QUALIFIED_NAME, createNewAddressExpression); processDefinitionBuilder.addBusinessData("country", COUNTRY_QUALIFIED_NAME, createNewCountryExpression); final String retrievedCountryData = "retrievedCountry"; processDefinitionBuilder.addBusinessData(retrievedCountryData, COUNTRY_QUALIFIED_NAME, null); processDefinitionBuilder.addBusinessData("noneAddress", ADDRESS_QUALIFIED_NAME, null); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder .addAutomaticTask("step1") .addOperation( new OperationBuilder().createBusinessDataSetAttributeOperation(businessDataName, "setFirstName", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("Manon"))) .addOperation( new OperationBuilder().createBusinessDataSetAttributeOperation(businessDataName2, "setFirstName", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("Plop"))) .addOperation( new OperationBuilder().createBusinessDataSetAttributeOperation(businessDataName, "setLastName", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("Péuigrec"))) .addOperation( new OperationBuilder().createBusinessDataSetAttributeOperation(businessDataName2, "setLastName", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("Plip"))) .addOperation( new OperationBuilder().createBusinessDataSetAttributeOperation("address", "setCountry", COUNTRY_QUALIFIED_NAME, countryQueryExpression)) .addOperation( new OperationBuilder().createBusinessDataSetAttributeOperation(businessDataName2, "setAddress", ADDRESS_QUALIFIED_NAME, new ExpressionBuilder().createBusinessDataExpression("noneAddress", ADDRESS_QUALIFIED_NAME))) .addAutomaticTask("step2") .addOperation( new OperationBuilder().attachBusinessDataSetAttributeOperation(retrievedCountryData, countryQueryExpression)) .addOperation( new OperationBuilder().createBusinessDataSetAttributeOperation(retrievedCountryData, "setName", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("FRANCE"))); processDefinitionBuilder.addUserTask("step3", ACTOR_NAME); processDefinitionBuilder.addTransition("step1", "step2"); processDefinitionBuilder.addTransition("step2", "step3"); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); waitForUserTask(processInstance, "step3"); final Expression getEmployeeAddressExpression = new ExpressionBuilder().createGroovyScriptExpression( "getEmployeeAddress", "if (" + businessDataName2 + ".address == null) return \"null\"\n" + businessDataName2 + ".address.city", String.class.getName(), new ExpressionBuilder().createBusinessDataExpression(businessDataName2, EMPLOYEE_QUALIFIED_NAME)); final Expression getCountryExpression = new ExpressionBuilder().createGroovyScriptExpression("getCountry", "if (" + retrievedCountryData + " == null) return \"null\"\n" + retrievedCountryData + ".name", String.class.getName(), new ExpressionBuilder().createBusinessDataExpression(retrievedCountryData, COUNTRY_QUALIFIED_NAME)); final Map> expressions = new HashMap<>(2); expressions.put(getEmployeeAddressExpression, null); expressions.put(getCountryExpression, null); final Map result = getProcessAPI().evaluateExpressionsOnProcessInstance( processInstance.getId(), expressions); assertThat(result.get(getEmployeeAddressExpression.getName())).isEqualTo("null"); assertThat(result.get(getCountryExpression.getName())).isEqualTo("FRANCE"); disableAndDeleteProcess(definition.getId()); } @Test public void shouldBeAbleToDeleteABusinessDataUsingOperation() throws Exception { final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee e = new Employee(); e.firstName = 'John'; e.lastName = 'Doe'; return e;", EMPLOYEE_QUALIFIED_NAME); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "shouldBeAbleToUpdateBusinessDataUsingJavaSetterOperation", "6.3-beta"); final String businessDataName = "employee"; processDefinitionBuilder.addBusinessData(businessDataName, EMPLOYEE_QUALIFIED_NAME, employeeExpression); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME) .addOperation(new OperationBuilder().deleteBusinessDataOperation(businessDataName)); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addTransition("step1", "step2"); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); final Map> expressions = new HashMap<>(2); expressions.put( new ExpressionBuilder().createQueryBusinessDataExpression("countEmployee", "Employee.countEmployee", Long.class.getName()), Collections.emptyMap()); final long processInstanceId = processInstance.getId(); Map result = getProcessAPI().evaluateExpressionsOnProcessInstance(processInstanceId, expressions); assertThat(result.get("countEmployee")).isEqualTo(1L); assignAndExecuteStep(step1Id, testUser); waitForUserTask(processInstance, "step2"); result = getProcessAPI().evaluateExpressionsOnProcessInstance(processInstanceId, expressions); assertThat(result.get("countEmployee")).isEqualTo(0L); disableAndDeleteProcess(definition.getId()); } public void assertCount(final long processInstanceId) throws Exception { final Map> expressions = new HashMap<>(2); expressions.put( new ExpressionBuilder().createQueryBusinessDataExpression("countEmployee", "Employee.countEmployee", Long.class.getName()), Collections.emptyMap()); final Map result = getProcessAPI().evaluateExpressionsOnProcessInstance(processInstanceId, expressions); assertThat(result.get("countEmployee")).isEqualTo(1L); } @Test public void deployABDRAndCreateAndUpdateAMultipleBusinessData() throws Exception { final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression( "createNewEmployees", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee john = new Employee(); john.firstName = 'John'; john.lastName = '저는 7년 동안 한국에서 살았어요';" + " Employee jane = new Employee(); jane.firstName = 'Jane'; jane.lastName = 'Doe'; return [jane, john];", List.class.getName()); final Expression jackExpression = new ExpressionBuilder().createGroovyScriptExpression("createJack", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee jack = new Employee(); jack.firstName = 'Jack'; jack.lastName = 'Doe'; return jack;", EMPLOYEE_QUALIFIED_NAME); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("test", "1.2-alpha"); processDefinitionBuilder.addBusinessData("myEmployees", EMPLOYEE_QUALIFIED_NAME, employeeExpression) .setMultiple(true); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME) .addOperation(new OperationBuilder().createBusinessDataSetAttributeOperation("myEmployees", "add", Object.class.getName(), jackExpression)); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addTransition("step1", "step2"); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance instance = getProcessAPI().startProcess(definition.getId()); final long step1Id = waitForUserTask(instance, "step1"); String employeeToString = getEmployeesToString("myEmployees", instance.getId()); assertThat(employeeToString).isEqualTo("Employee [firstName=[Jane, John], lastName=[Doe, 저는 7년 동안 한국에서 살았어요]]"); assignAndExecuteStep(step1Id, testUser); waitForUserTask(instance, "step2"); employeeToString = getEmployeesToString("myEmployees", instance.getId()); assertThat(employeeToString) .isEqualTo("Employee [firstName=[Jane, John, Jack], lastName=[Doe, 저는 7년 동안 한국에서 살았어요, Doe]]"); disableAndDeleteProcess(definition.getId()); } @Test public void deployBDRAndCreateAndUpdateAInitiallyEmptyMultipleBusinessData() throws Exception { final Expression employeeExpression1 = new ExpressionBuilder().createGroovyScriptExpression( "createNewEmployees", "import " + EMPLOYEE_QUALIFIED_NAME + "; return new ArrayList<>();", List.class.getName()); final Expression myEmployeesDependency = new ExpressionBuilder().createBusinessDataExpression("myEmployees", List.class.getName()); final Expression jackExpression = new ExpressionBuilder().createGroovyScriptExpression("createJack", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee jack = new Employee(); jack.firstName = 'Jack'; jack.lastName = 'Doe'; myEmployees.add(jack) ; return myEmployees;", List.class.getName(), myEmployeesDependency); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("test", "1.2-alpha"); processDefinitionBuilder.addBusinessData("myEmployees", EMPLOYEE_QUALIFIED_NAME, employeeExpression1) .setMultiple(true); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME) .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand("myEmployees"), OperatorType.ASSIGNMENT, null, null, jackExpression); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addTransition("step1", "step2"); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance instance = getProcessAPI().startProcess(definition.getId()); final long step1Id = waitForUserTask(instance, "step1"); String employeeToString = getEmployeesToString("myEmployees", instance.getId()); assertThat(employeeToString).isEqualTo("Employee [firstName=[], lastName=[]]"); assignAndExecuteStep(step1Id, testUser); waitForUserTask(instance, "step2"); employeeToString = getEmployeesToString("myEmployees", instance.getId()); assertThat(employeeToString) .isEqualTo("Employee [firstName=[Jack], lastName=[Doe]]"); disableAndDeleteProcess(definition.getId()); } private String getEmployeesToString(final String businessDataName, final long processInstanceId) throws InvalidExpressionException { final Map> expressions = new HashMap<>(5); final String expressionEmployee = "retrieve_Employee"; expressions.put( new ExpressionBuilder().createGroovyScriptExpression(expressionEmployee, "\"Employee [firstName=\" + " + businessDataName + ".firstName + \", lastName=\" + " + businessDataName + ".lastName + \"]\";", String.class.getName(), new ExpressionBuilder().createBusinessDataExpression(businessDataName, List.class.getName())), null); try { final Map evaluatedExpressions = getProcessAPI() .evaluateExpressionsOnProcessInstance(processInstanceId, expressions); return (String) evaluatedExpressions.get(expressionEmployee); } catch (final ExpressionEvaluationException eee) { System.err.println(eee.getMessage()); return null; } } @Test public void useMultipleBusinessDataInAUserTaskWithMultiInstance() throws Exception { final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression( "createNewEmployees", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee john = new Employee(); john.firstName = 'John'; john.lastName = 'Doe';" + " Employee jane = new Employee(); jane.firstName = 'Jane'; jane.lastName = 'Doe'; [jane, john]", List.class.getName()); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("MBIMI", "1.2-beta"); builder.addBusinessData("myEmployees", EMPLOYEE_QUALIFIED_NAME, employeeExpression).setMultiple(true); builder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder userTaskBuilder = builder.addUserTask("step1", ACTOR_NAME); userTaskBuilder.addBusinessData("employee", EMPLOYEE_QUALIFIED_NAME); userTaskBuilder.addMultiInstance(false, "myEmployees").addDataInputItemRef("employee"); userTaskBuilder.addOperation(new OperationBuilder().createBusinessDataSetAttributeOperation("employee", "setLastName", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("Smith"))); builder.addUserTask("step2", ACTOR_NAME); builder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, testUser); final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(instance, "step1", testUser); waitForUserTaskAndExecuteIt(instance, "step1", testUser); waitForUserTask(instance, "step2"); final String employeeToString = getEmployeesToString("myEmployees", instance.getId()); assertThat(firstNames(employeeToString)).containsOnlyOnce("Jane", "John"); assertThat(lastNames(employeeToString)).containsExactly("Smith", "Smith"); disableAndDeleteProcess(processDefinition); } private String[] firstNames(final String employeeToString) { String firstNames = substringAfter(employeeToString, "firstName=["); firstNames = substringBefore(firstNames, "], lastName="); return StringUtils.split(firstNames, ", "); } private String[] lastNames(final String employeeToString) { String lastNames = substringAfter(employeeToString, "lastName=["); lastNames = substringBefore(lastNames, "]]"); return StringUtils.split(lastNames, ", "); } @Test public void useMultipleBusinessDataInACallActivityWithSequentialMultiInstance() throws Exception { ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("UpdateEmployee", "1.2-beta"); builder.addActor(ACTOR_NAME); builder.addBusinessData("employee", EMPLOYEE_QUALIFIED_NAME, null); final OperationBuilder operationBuilder = new OperationBuilder(); builder.addUserTask("step1", ACTOR_NAME) .addOperation(operationBuilder.createBusinessDataSetAttributeOperation("employee", "setLastName", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("Smith"))); final ProcessDefinition subProcessDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, testUser); final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression( "createNewEmployees", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee john = new Employee(); john.firstName = 'John'; john.lastName = 'Doe';" + " Employee jane = new Employee(); jane.firstName = 'Jane'; jane.lastName = 'Doe'; return [jane, john];", List.class.getName()); builder = new ProcessDefinitionBuilder().createNewInstance("MBIMI", "1.2-beta"); builder.addBusinessData("myEmployees", EMPLOYEE_QUALIFIED_NAME, employeeExpression).setMultiple(true); builder.addActor(ACTOR_NAME); final CallActivityBuilder callActivity = builder.addCallActivity("step1", new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getName()), new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getVersion())); callActivity.addBusinessData("miEmployee", EMPLOYEE_QUALIFIED_NAME); callActivity.addDataInputOperation( operationBuilder.createNewInstance() .attachBusinessDataSetAttributeOperation("employee", new ExpressionBuilder().createBusinessDataExpression("miEmployee", EMPLOYEE_QUALIFIED_NAME))) .addMultiInstance(true, "myEmployees").addDataInputItemRef("miEmployee"); builder.addUserTask("step2", ACTOR_NAME); builder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, testUser); final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(instance, "step1", testUser); waitForUserTaskAndExecuteIt(instance, "step1", testUser); waitForUserTask(instance, "step2"); final String employeeToString = getEmployeesToString("myEmployees", instance.getId()); assertThat(employeeToString).contains("Jane", "John", "Smith").doesNotContain("Doe"); disableAndDeleteProcess(processDefinition); disableAndDeleteProcess(subProcessDefinition); } @Test public void useMultipleBusinessDataInACallActivityWithInDataMultiInstance() throws Exception { ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("UpdateEmployee", "1.2-beta"); builder.addActor(ACTOR_NAME); builder.addBusinessData("employee", EMPLOYEE_QUALIFIED_NAME, null); builder.addUserTask("step1", ACTOR_NAME) .addOperation(new OperationBuilder().createBusinessDataSetAttributeOperation("employee", "setLastName", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("Smith"))); final ProcessDefinition subProcessDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, testUser); final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression( "createNewEmployees", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee john = new Employee(); john.firstName = 'John'; john.lastName = 'Doe';" + " Employee jane = new Employee(); jane.firstName = 'Jane'; jane.lastName = 'Doe'; [jane, john];", List.class.getName()); builder = new ProcessDefinitionBuilder().createNewInstance("MBIMI", "1.2-beta"); builder.addBusinessData("myEmployees", EMPLOYEE_QUALIFIED_NAME, employeeExpression).setMultiple(true); builder.addBusinessData("myNewEmployees", EMPLOYEE_QUALIFIED_NAME, null).setMultiple(true); builder.addActor(ACTOR_NAME); final CallActivityBuilder callActivityBuilder = builder.addCallActivity("step1", new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getName()), new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getVersion())); callActivityBuilder.addBusinessData("miEmployee", EMPLOYEE_QUALIFIED_NAME); callActivityBuilder.addBusinessData("newEmployee", EMPLOYEE_QUALIFIED_NAME); callActivityBuilder.addDataInputOperation( new OperationBuilder().attachBusinessDataSetAttributeOperation("employee", new ExpressionBuilder().createBusinessDataExpression("miEmployee", EMPLOYEE_QUALIFIED_NAME))); callActivityBuilder.addDataOutputOperation( new OperationBuilder().attachBusinessDataSetAttributeOperation("newEmployee", new ExpressionBuilder().createBusinessDataExpression("employee", EMPLOYEE_QUALIFIED_NAME))); callActivityBuilder.addMultiInstance(false, "myEmployees").addDataInputItemRef("miEmployee") .addDataOutputItemRef("newEmployee").addLoopDataOutputRef("myNewEmployees"); builder.addUserTask("step2", ACTOR_NAME); builder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, testUser); final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(instance, "step1", testUser); waitForUserTaskAndExecuteIt(instance, "step1", testUser); waitForUserTask(instance, "step2"); final String employeeToString = getEmployeesToString("myNewEmployees", instance.getId()); assertThat(employeeToString).contains("Jane", "John", "Smith").doesNotContain("Doe"); disableAndDeleteProcess(processDefinition); disableAndDeleteProcess(subProcessDefinition); } @Test public void useMultipleBusinessDataInACallActivityWithOutDataMultiInstance() throws Exception { final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression( "createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee john = new Employee(); john.firstName = 'John' + activityInstanceId; john.lastName = 'Doe'; john;", EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createEngineConstant(ExpressionConstants.ACTIVITY_INSTANCE_ID)); ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("createEmployee", "1.2-beta"); builder.addActor(ACTOR_NAME); builder.addBusinessData("employee", EMPLOYEE_QUALIFIED_NAME, null); builder.addUserTask("step1", ACTOR_NAME) .addOperation(new OperationBuilder().attachBusinessDataSetAttributeOperation("employee", employeeExpression)); final ProcessDefinition subProcessDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, testUser); builder = new ProcessDefinitionBuilder().createNewInstance("MBIMI", "1.2-beta"); builder.addBusinessData("myEmployees", EMPLOYEE_QUALIFIED_NAME, null).setMultiple(true); builder.addActor(ACTOR_NAME); final CallActivityBuilder callActivityBuilder = builder.addCallActivity("step1", new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getName()), new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getVersion())); callActivityBuilder.addBusinessData("newEmployee", EMPLOYEE_QUALIFIED_NAME); callActivityBuilder.addDataOutputOperation( new OperationBuilder().attachBusinessDataSetAttributeOperation("newEmployee", new ExpressionBuilder().createBusinessDataExpression("employee", EMPLOYEE_QUALIFIED_NAME))); callActivityBuilder.addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(2)) .addDataOutputItemRef("newEmployee") .addLoopDataOutputRef("myEmployees"); builder.addUserTask("step2", ACTOR_NAME); builder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, testUser); final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(instance, "step1", testUser); waitForUserTaskAndExecuteIt(instance, "step1", testUser); waitForUserTask(instance, "step2"); final String employeeToString = getEmployeesToString("myEmployees", instance.getId()); assertThat(employeeToString).contains("John", "Doe"); disableAndDeleteProcess(processDefinition); disableAndDeleteProcess(subProcessDefinition); } //BS-13803 @Test public void initializeBusinessDataInCalledProcessWithContractInput() throws Exception { final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression( "createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee john = new Employee(); john.firstName = theInput; john.lastName = 'Doe'; john;", EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createContractInputExpression("theInput", String.class.getName())); ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("createEmployeeInCallActivity", "1.2-beta"); builder.addActor(ACTOR_NAME); builder.addContract().addInput("theInput", Type.TEXT, "the input"); builder.addBusinessData("employee", EMPLOYEE_QUALIFIED_NAME, employeeExpression); builder.addUserTask("step1", ACTOR_NAME); final ProcessDefinition subProcessDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, testUser); builder = new ProcessDefinitionBuilder().createNewInstance("createEmployeeInCallActivityMaster", "1.2-beta"); builder.addActor(ACTOR_NAME); builder.addCallActivity("call", new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getName()), new ExpressionBuilder().createConstantStringExpression(subProcessDefinition.getVersion())) .addProcessStartContractInput("theInput", new ExpressionBuilder().createConstantStringExpression("theValue")); builder.addUserTask("step2", ACTOR_NAME); builder.addTransition("call", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, testUser); final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask("step1"); final Expression employee = new ExpressionBuilder().createGroovyScriptExpression("script", "employee.firstName", String.class.getName(), new ExpressionBuilder().createBusinessDataExpression("employee", EMPLOYEE_QUALIFIED_NAME)); final Serializable employeeResult = getProcessAPI().evaluateExpressionsOnActivityInstance(step1Id, Collections.singletonMap(employee, null)).get("script"); assertThat(employeeResult).isEqualTo("theValue"); getProcessAPI().assignUserTask(step1Id, testUser.getId()); getProcessAPI().executeFlowNode(step1Id); waitForUserTask(instance, "step2"); disableAndDeleteProcess(processDefinition); disableAndDeleteProcess(subProcessDefinition); } @Test public void should_return_the_list_of_entities_from_the_multiple_instance() throws Exception { final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression( "createNewEmployees", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee john = new Employee(); john.firstName = 'John'; john.lastName = 'Doe';" + " Employee jane = new Employee(); jane.firstName = 'Jane'; jane.lastName = 'Doe';" + " Employee rambo = new Employee(); rambo.firstName = 'John'; rambo.lastName = 'Rambo'; [jane, john, rambo]", List.class.getName()); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("MBIMI", "1.2-beta"); builder.addBusinessData("myEmployees", EMPLOYEE_QUALIFIED_NAME, employeeExpression).setMultiple(true); builder.addData("names", List.class.getName(), null); builder.addBusinessData("firstNames", EMPLOYEE_QUALIFIED_NAME, null).setMultiple(true); builder.addContextEntry("firstNames_ref", new ExpressionBuilder().createBusinessDataReferenceExpression("firstNames")); builder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder userTaskBuilder = builder.addUserTask("step1", ACTOR_NAME); userTaskBuilder.addBusinessData("employee", EMPLOYEE_QUALIFIED_NAME); userTaskBuilder.addShortTextData("name", null); userTaskBuilder.addMultiInstance(false, "myEmployees").addDataInputItemRef("employee") .addDataOutputItemRef("name").addLoopDataOutputRef("names"); userTaskBuilder.addOperation(new OperationBuilder().createSetDataOperation("name", new ExpressionBuilder().createConstantStringExpression("Doe"))); UserTaskDefinitionBuilder step2Builder = builder.addUserTask("step2", ACTOR_NAME); step2Builder.addOperation(new OperationBuilder().attachBusinessDataSetAttributeOperation("firstNames", new ExpressionBuilder() .createQueryBusinessDataExpression("findFirstNames", "Employee." + FIND_EMPLOYEE_WITH_FIRSTNAMES, List.class.getName(), new ExpressionBuilder().createGroovyScriptExpression("firstNames", "['John'] as String[]", String[].class.getName()), new ExpressionBuilder().createExpression("startIndex", "0", Integer.class.getName(), ExpressionType.TYPE_CONSTANT), new ExpressionBuilder().createExpression("maxResults", "10", Integer.class.getName(), ExpressionType.TYPE_CONSTANT)))); builder.addUserTask("step3", ACTOR_NAME); builder.addTransition("step1", "step2"); builder.addTransition("step2", "step3"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, testUser); final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(instance, "step1", testUser); waitForUserTaskAndExecuteIt(instance, "step1", testUser); waitForUserTaskAndExecuteIt(instance, "step1", testUser); waitForUserTaskAndExecuteIt(instance, "step2", testUser); waitForUserTask(instance, "step3"); final DataInstance namesDataInstance = getProcessAPI().getProcessDataInstance("names", instance.getId()); assertThat(namesDataInstance.getValue().toString()).isEqualTo("[Doe, Doe, Doe]"); final Serializable firstNamesDataInstance = getProcessAPI().getProcessInstanceExecutionContext(instance.getId()) .get("firstNames_ref"); //Only employee with firstname == john assertThat(((MultipleBusinessDataReference) firstNamesDataInstance).getStorageIds()).hasSize(2); final Map employee = getProcessAPI().evaluateExpressionsOnProcessInstance( instance.getId(), Collections.singletonMap(new ExpressionBuilder().createBusinessDataReferenceExpression("myEmployees"), Collections.emptyMap())); assertThat(employee).hasSize(1); assertThat(employee.get("myEmployees")).isInstanceOf(MultipleBusinessDataReference.class); final MultipleBusinessDataReference myEmployees = (MultipleBusinessDataReference) employee.get("myEmployees"); assertThat(myEmployees.getName()).isEqualTo("myEmployees"); assertThat(myEmployees.getType()).isEqualTo(EMPLOYEE_QUALIFIED_NAME); assertThat(myEmployees.getStorageIds()).hasSize(3); disableAndDeleteProcess(processDefinition); } @Test public void getProcessBusinessDataReferencesShoulReturnTheListOfReferences() throws Exception { final String taskName = "step"; final ProcessDefinition definition = buildProcessThatUpdateBizDataInsideConnector(taskName); final ProcessInstance instance = getProcessAPI().startProcess(definition.getId()); waitForUserTask(instance, taskName); final List references = getBusinessDataAPI() .getProcessBusinessDataReferences(instance.getId(), 0, 10); assertThat(references).hasSize(1); assertThat(((SimpleBusinessDataReference) references.get(0)).getStorageId()).isNotNull(); disableAndDeleteProcess(definition); } @Test public void getBusinessDataCommand_should_return_json_entities() throws Exception { final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression( "createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "; import " + ADDRESS_QUALIFIED_NAME + "; Employee e = new Employee(); e.firstName = 'Alphonse';" + " e.hireDate=new Date(1422742559000L); " + " e.lastName = 'Dupond'; e.addToPhoneNumbers('123456789'); e.setAddress(myAddress);e.addToAddresses(myAddress); return e;", EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createBusinessDataExpression("myAddress", ADDRESS_QUALIFIED_NAME)); final Expression addressExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewAddress", "import " + ADDRESS_QUALIFIED_NAME + "; import " + COUNTRY_QUALIFIED_NAME + "; " + "Address a = new Address(); a.street='32, rue Gustave Eiffel'; a.city='Grenoble'; a.country = myCountry ; a;", ADDRESS_QUALIFIED_NAME); final Expression countryExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewCountry", "import " + COUNTRY_QUALIFIED_NAME + "; " + "Country c = new Country(); c.name='France'; " + " c;", COUNTRY_QUALIFIED_NAME); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "rest", "1.0"); final String bizDataName = "myEmployee"; processDefinitionBuilder.addBusinessData("myCountry", COUNTRY_QUALIFIED_NAME, null); processDefinitionBuilder.addBusinessData("myAddress", ADDRESS_QUALIFIED_NAME, null); processDefinitionBuilder.addBusinessData(bizDataName, EMPLOYEE_QUALIFIED_NAME, null); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addAutomaticTask("step1") .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand("myCountry"), OperatorType.ASSIGNMENT, null, null, countryExpression) .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand("myAddress"), OperatorType.ASSIGNMENT, null, null, addressExpression) .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(bizDataName), OperatorType.ASSIGNMENT, null, null, employeeExpression); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step2"); final SimpleBusinessDataReference businessDataReference = (SimpleBusinessDataReference) getBusinessDataAPI() .getProcessBusinessDataReference( bizDataName, processInstance.getId()); verifyCommandGetBusinessDataById(businessDataReference); verifyCommandGetBusinessDataByIds(businessDataReference); verifyCommandGetQuery_findByFirstNameAndLastNameNewOrder(); verifyCommandGetQuery_getEmployeeByPhoneNumber(); verifyCommandGetQuery_findByFirstNameFetchAddresses(); verifyCommandGetQuery_countEmployee(); verifyCommandGetQuery_findByHireDate(); disableAndDeleteProcess(processDefinition.getId()); } private void verifyCommandGetBusinessDataByIds(final SimpleBusinessDataReference businessDataReference) throws Exception { final List ids = new ArrayList<>(); ids.add(businessDataReference.getStorageId()); final Map parameters = new HashMap<>(); parameters.put("businessDataIds", (Serializable) ids); parameters.put("entityClassName", EMPLOYEE_QUALIFIED_NAME); parameters.put("businessDataURIPattern", "/businessdata/{className}/{id}/{field}"); // when final String lazyAddressResultWithChildName = (String) getCommandAPI().execute("getBusinessDataByIds", parameters); // then assertThatJson(lazyAddressResultWithChildName).as("should get address with lazy link to country") .node("[0].addresses[0].links[0]") .isObject() .containsEntry("rel", "country") .containsKey("href"); } private void verifyCommandGetBusinessDataById(final SimpleBusinessDataReference businessDataReference) throws Exception { final Map parameters = new HashMap<>(); parameters.put("businessDataId", businessDataReference.getStorageId()); parameters.put("entityClassName", EMPLOYEE_QUALIFIED_NAME); parameters.put("businessDataChildName", "address"); parameters.put("businessDataURIPattern", "/businessdata/{className}/{id}/{field}"); // when final String lazyAddressResultWithChildName = (String) getCommandAPI().execute("getBusinessDataById", parameters); // then assertThatJson(lazyAddressResultWithChildName).as("should get address with lazy link to country") .when(Option.IGNORING_VALUES) .isEqualTo(getJsonContent("getBusinessDataByIdAddress.json")); // when parameters.remove("businessDataChildName"); final String employeeResultWithAddress = (String) getCommandAPI().execute("getBusinessDataById", parameters); // then assertThatJson(employeeResultWithAddress).as("should get employee with lazy link to country in addresses") .node("addresses[0].links[0]") .isObject() .containsEntry("rel", "country") .containsKey("href"); } private void verifyCommandGetQuery_findByFirstNameAndLastNameNewOrder() throws Exception { final Map parameters = new HashMap<>(); final Map queryParameters = new HashMap<>(); queryParameters.put("firstName", "Alphonse"); queryParameters.put("lastName", "Dupond"); parameters.put("queryName", FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER); parameters.put(ENTITY_CLASS_NAME, EMPLOYEE_QUALIFIED_NAME); parameters.put("startIndex", 0); parameters.put("maxResults", 10); parameters.put("businessDataURIPattern", "/businessdata/{className}/{id}/{field}"); parameters.put("queryParameters", (Serializable) queryParameters); // when ((BusinessDataQueryResult) getCommandAPI().execute("getBusinessDataByQueryCommand", parameters)) .getJsonResults(); getCommandAPI().addDependency("temporaryDeps" + iterator, new byte[] { 0, 1 }); iterator++; final Serializable jsonResult = ((BusinessDataQueryResult) getCommandAPI() .execute("getBusinessDataByQueryCommand", parameters)).getJsonResults(); // then assertThatJson(jsonResult).as("should get employee") .when(Option.IGNORING_VALUES) .whenIgnoringPaths("[0].hireDate") .isEqualTo(getJsonContent("findByFirstNameAndLastNameNewOrder.json")); } private void verifyCommandGetQuery_getEmployeeByPhoneNumber() throws Exception { final Map parameters = new HashMap<>(); final Map queryParameters = new HashMap<>(); queryParameters.put("phoneNumber", "123456789"); parameters.put("queryName", GET_EMPLOYEE_BY_PHONE_NUMBER_QUERY_NAME); parameters.put(ENTITY_CLASS_NAME, EMPLOYEE_QUALIFIED_NAME); parameters.put("startIndex", 0); parameters.put("maxResults", 10); parameters.put("businessDataURIPattern", BUSINESS_DATA_CLASS_NAME_ID_FIELD); parameters.put("queryParameters", (Serializable) queryParameters); // when final BusinessDataQueryResult businessDataQueryResult = (BusinessDataQueryResult) getCommandAPI() .execute("getBusinessDataByQueryCommand", parameters); final String jsonResult = (String) businessDataQueryResult.getJsonResults(); // then assertThatJson(jsonResult).as("should get employee") .when(Option.IGNORING_VALUES) .whenIgnoringPaths("[0].hireDate", "[0].booleanField") .isEqualTo(getJsonContent("getEmployeeByPhoneNumber.json")); } private void verifyCommandGetQuery_findByFirstNameFetchAddresses() throws Exception { final BusinessDataQueryResult businessDataQueryResult = getBusinessDataByQuery( Collections.singletonMap("firstName", "Alphonse"), 0, 10, FIND_BY_FIRST_NAME_FETCH_ADDRESSES, EMPLOYEE_QUALIFIED_NAME); // then assertThat(businessDataQueryResult.getBusinessDataQueryMetadata()) .as("should have no metadata when custom countFor is not here").isNull(); assertThatJson(businessDataQueryResult.getJsonResults()).as("should get employee") .when(Option.IGNORING_VALUES) .whenIgnoringPaths("[0].hireDate", "[0].booleanField") .isEqualTo(getJsonContent("findByFirstNameFetchAddresses.json")); } private BusinessDataQueryResult getBusinessDataByQuery(Map queryParameters, int startIndex, int maxResult, String queryName, String entityClassName) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException { Map parameters = new HashMap<>(); parameters.put("queryName", queryName); parameters.put(ENTITY_CLASS_NAME, entityClassName); parameters.put("startIndex", startIndex); parameters.put("maxResults", maxResult); parameters.put("businessDataURIPattern", BUSINESS_DATA_CLASS_NAME_ID_FIELD); parameters.put("queryParameters", (Serializable) queryParameters); // when return (BusinessDataQueryResult) getCommandAPI() .execute("getBusinessDataByQueryCommand", parameters); } private void verifyCommandGetQuery_countEmployee() throws Exception { final Map parameters = new HashMap<>(); parameters.put("queryName", COUNT_EMPLOYEE); parameters.put(ENTITY_CLASS_NAME, EMPLOYEE_QUALIFIED_NAME); parameters.put("startIndex", 0); parameters.put("maxResults", 10); parameters.put("businessDataURIPattern", BUSINESS_DATA_CLASS_NAME_ID_FIELD); // when final BusinessDataQueryResult businessDataQueryResult = (BusinessDataQueryResult) getCommandAPI() .execute("getBusinessDataByQueryCommand", parameters); // then - Standard shape returns { "value": n } assertThatJson(businessDataQueryResult.getJsonResults()).as("should get employee count") .isEqualTo("{ \"value\": 1 }"); } private void verifyCommandGetQuery_findByHireDate() throws Exception { final Map queryParameters = new HashMap<>(); queryParameters.put("date1", "1930-01-15"); queryParameters.put("date2", "2050-12-31"); final BusinessDataQueryResult businessDataQueryResult = getBusinessDataByQuery(queryParameters, 0, 10, FIND_BY_HIRE_DATE_RANGE, EMPLOYEE_QUALIFIED_NAME); // then assertThatJson(businessDataQueryResult.getJsonResults()).as("should get employee") .isArray() .isNotEmpty(); final BusinessDataQueryMetadata businessDataQueryMetadata = businessDataQueryResult .getBusinessDataQueryMetadata(); assertThat(businessDataQueryMetadata).as("should have metadata").isNotNull(); assertThat(businessDataQueryMetadata.getCount()).isEqualTo(1L); assertThat(businessDataQueryMetadata.getStartIndex()).isZero(); assertThat(businessDataQueryMetadata.getMaxResults()).isEqualTo(10); } @Override public BarResource getResource(final String path, final String name) throws IOException { return super.getResource(path, name); } private String getJsonContent(final String jsonFileName) throws IOException { final String json; json = new String(IOUtils.toByteArray(this.getClass().getResourceAsStream(jsonFileName))); return json; } @Test public void deployABDRAndCreateInOperationAMultipleBusinessData() throws Exception { final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression( "createNewEmployees", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee john = new Employee(); john.firstName = 'John'; john.lastName = 'Doe';" + " Employee jane = new Employee(); jane.firstName = 'Jane'; jane.lastName = 'Doe'; return [jane, john];", List.class.getName()); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("test", "1.2-alpha"); processDefinitionBuilder.addBusinessData("myEmployees", EMPLOYEE_QUALIFIED_NAME, null).setMultiple(true); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME).addOperation( new OperationBuilder().createBusinessDataSetAttributeOperation("myEmployees", "addAll", "java.util.Collection", employeeExpression)); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addTransition("step1", "step2"); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance instance = getProcessAPI().startProcess(definition.getId()); final long step1Id = waitForUserTask(instance, "step1"); String employeeToString = getEmployeesToString("myEmployees", instance.getId()); assertThat(firstNames(employeeToString)).isEmpty(); assertThat(lastNames(employeeToString)).isEmpty(); assignAndExecuteStep(step1Id, testUser); waitForUserTask(instance, "step2"); employeeToString = getEmployeesToString("myEmployees", instance.getId()); assertThat(firstNames(employeeToString)).containsOnlyOnce("Jane", "John"); assertThat(lastNames(employeeToString)).containsExactly("Doe", "Doe"); disableAndDeleteProcess(definition.getId()); } @Test public void should_subprocess_get_bdm_from_parent_process_instance_when_using_task_loop() throws Exception { final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression( "createNewEmployees", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee john = new Employee(); john.firstName = 'John'; john.lastName = 'Doe';" + " Employee jane = new Employee(); jane.firstName = 'Jane'; jane.lastName = 'Doe'; return [jane, john];", List.class.getName()); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("test", "1.2-alpha"); processDefinitionBuilder.addBusinessData("myEmployees", EMPLOYEE_QUALIFIED_NAME, employeeExpression) .setMultiple(true); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); //setup expressions processDefinitionBuilder.addContextEntry("retrieve_Employee", new ExpressionBuilder().createGroovyScriptExpression("retrieve_Employee", "\"Employee [firstName=\" + myEmployees" + ".firstName + \", lastName=\" + myEmployees.lastName + \"]\";", String.class.getName(), new ExpressionBuilder().createBusinessDataExpression("myEmployees", List.class.getName()))); //setup subprocess with loop SubProcessDefinitionBuilder subProcessDefinitionBuilder = processDefinitionBuilder .addSubProcess("subProcess", true).getSubProcessBuilder(); subProcessDefinitionBuilder.addStartEvent("subStart") .addTimerEventTriggerDefinition(TimerType.DURATION, new ExpressionBuilder().createConstantLongExpression(100L)); subProcessDefinitionBuilder.addManualTask("subStep1", ACTOR_NAME) .addBusinessData("employee", EMPLOYEE_QUALIFIED_NAME) .addOperation(new OperationBuilder().createBusinessDataSetAttributeOperation("employee", "setLastName", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("Smith"))) .addMultiInstance(false, "myEmployees").addDataInputItemRef("employee"); subProcessDefinitionBuilder.addEndEvent("subEnd"); subProcessDefinitionBuilder.addTransition("subStart", "subStep1"); subProcessDefinitionBuilder.addTransition("subStep1", "subEnd"); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance instance = getProcessAPI().startProcess(definition.getId()); // wait for the subprocess user task - first time long activityInstanceId1 = waitForUserTask("subStep1"); // execute the task getProcessAPI().assignUserTask(activityInstanceId1, testUser.getId()); getProcessAPI().executeFlowNode(activityInstanceId1); // wait for the subprocess user task - second time long activityInstanceId2 = waitForUserTask("subStep1"); ActivityInstance activityInstance2 = getProcessAPI().getActivityInstance(activityInstanceId2); // execute the task getProcessAPI().assignUserTask(activityInstanceId2, testUser.getId()); getProcessAPI().executeFlowNode(activityInstanceId2); // wait for the subprocess to end waitForProcessToFinish(activityInstance2.getParentProcessInstanceId()); waitForProcessToBeInState(instance.getId(), ProcessInstanceState.ABORTED); // get ArchivedProcessInstanceExecutionContext long archivedProcessInstanceId = getProcessAPI().getArchivedProcessInstances(instance.getId(), 0, 10).get(0) .getId(); String employeeToString = (String) getProcessAPI() .getArchivedProcessInstanceExecutionContext(archivedProcessInstanceId).get("retrieve_Employee"); assertThat(lastNames(employeeToString)).containsExactly("Smith", "Smith"); disableAndDeleteProcess(definition.getId()); } @Test public void should_get_the_lazy_list_in_a_multiple_business_data() throws Exception { final Expression initProducts = new ExpressionBuilder().createGroovyScriptExpression("initProducts", "import " + PRODUCT_QUALIFIED_NAME + ";" + " Product p1 = new Product(); p1.name = 'Rock'; " + " Product p2 = new Product(); p2.name = 'Paper'; " + " return [p1, p2];", List.class.getName()); final Expression productDependency = new ExpressionBuilder().createBusinessDataExpression("products", List.class.getName()); final Expression initCatalogs = new ExpressionBuilder().createGroovyScriptExpression( "initCatalogs", "import " + PRODUCT_CATALOG_QUALIFIED_NAME + ";" + " ProductCatalog pc = new ProductCatalog(); pc.name = 'MyFirstCatalog'; pc.setProducts(products);" + " return [pc];", List.class.getName(), productDependency); final Expression catalogDependency = new ExpressionBuilder().createBusinessDataExpression("productCatalogs", List.class.getName()); final Expression nbOfProducts = new ExpressionBuilder().createGroovyScriptExpression("nbOfProducts", "import " + PRODUCT_CATALOG_QUALIFIED_NAME + ";" + " productCatalogs.get(0).getProducts().size()", Integer.class.getName(), catalogDependency); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("def", "6.3-beta"); builder.addActor(ACTOR_NAME); builder.addBusinessData("products", PRODUCT_QUALIFIED_NAME, initProducts).setMultiple(true); builder.addBusinessData("productCatalogs", PRODUCT_CATALOG_QUALIFIED_NAME, null).setMultiple(true); builder.addIntegerData("count", null); builder.addAutomaticTask("initCatalogs") .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand("productCatalogs"), OperatorType.ASSIGNMENT, null, null, initCatalogs); builder.addUserTask("next", ACTOR_NAME) .addOperation(new OperationBuilder().createSetDataOperation("count", nbOfProducts)); builder.addTransition("initCatalogs", "next"); final ProcessDefinition definition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, testUser); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); waitForUserTaskAndExecuteIt(processInstance, "next", testUser); disableAndDeleteProcess(definition.getId()); } @Test public void should_update_composition_entities() throws Exception { String initCatalog = "import " + PRODUCT_CATALOG_QUALIFIED_NAME + "\n" + "import " + "com.company.model.Edition" + "\n" + "Edition edition = new Edition() \n" + "edition.releaseYear = '2015' \n" + "ProductCatalog pc = new ProductCatalog() \n" + "pc.name = 'MyFirstCatalog' \n" + "pc.setEditions([edition]) \n" + "pc\n"; final Expression initCatalogExpression = new ExpressionBuilder().createGroovyScriptExpression("initCatalog", initCatalog, PRODUCT_CATALOG_QUALIFIED_NAME); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("compo", "8.2"); builder.addActor(ACTOR_NAME); builder.addBusinessData("productCatalog", PRODUCT_CATALOG_QUALIFIED_NAME, initCatalogExpression); builder.addUserTask("updateCatalog", ACTOR_NAME) .addOperation( new OperationBuilder().createBusinessDataSetAttributeOperation("productCatalog", "setName", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("myUdaptedCatalog"))); builder.addUserTask("unreferenceCatalog", ACTOR_NAME) .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand("productCatalog"), OperatorType.ASSIGNMENT, null, null, new ExpressionBuilder().createGroovyScriptExpression("nullExpression", "null", PRODUCT_CATALOG_QUALIFIED_NAME)); builder.addUserTask("result", ACTOR_NAME); builder.addTransition("updateCatalog", "unreferenceCatalog"); builder.addTransition("unreferenceCatalog", "result"); final ProcessDefinition definition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, testUser); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); waitForUserTaskAndExecuteIt("updateCatalog", testUser); waitForUserTaskAndExecuteIt("unreferenceCatalog", testUser); waitForUserTask(processInstance, "result"); final SimpleBusinessDataReference businessDataReference = (SimpleBusinessDataReference) getBusinessDataAPI() .getProcessBusinessDataReference( "productCatalog", processInstance.getId()); assertThat(businessDataReference.getStorageId()).isNull(); assertThat(businessDataReference.getStorageIdAsString()).isNull(); disableAndDeleteProcess(definition.getId()); } @Test public void should_associate_the_right_address() throws Exception { final Expression addressExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewAddress", "import " + ADDRESS_QUALIFIED_NAME + "; new Address(street:'32, rue Gustave Eiffel', city:'Grenoble')", ADDRESS_QUALIFIED_NAME); final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "; new Employee(firstName:'John', lastName:'Doe', address:myAddress)", EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createBusinessDataExpression("myAddress", ADDRESS_QUALIFIED_NAME)); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "theProcess", "6.3.1"); final String bizDataName = "myEmployee"; processDefinitionBuilder.addBusinessData(bizDataName, EMPLOYEE_QUALIFIED_NAME, null); processDefinitionBuilder.addBusinessData("myAddress", ADDRESS_QUALIFIED_NAME, addressExpression); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addAutomaticTask("step1") .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(bizDataName), OperatorType.ASSIGNMENT, null, null, employeeExpression); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addTransition("step1", "step2"); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); waitForUserTask(processInstance, "step2"); final Long numberOfAddresses = getNumberOfAddresses(processInstance.getId()); assertThat(numberOfAddresses).isEqualTo(1L); final String address = getAddressAsAString("myAddress", processInstance.getId()); assertThat(address).isEqualTo("Address [street=32, rue Gustave Eiffel, city=Grenoble]"); final SimpleBusinessDataReference businessDataReference = (SimpleBusinessDataReference) getBusinessDataAPI() .getProcessBusinessDataReference( bizDataName, processInstance.getId()); final Expression idExpression = new ExpressionBuilder().createExpression("id", String.valueOf(businessDataReference.getStorageId()), Long.class.getName(), ExpressionType.TYPE_CONSTANT); final Expression getEmployeeExpression = new ExpressionBuilder().createQueryBusinessDataExpression( "getEmployee", "Employee.findByPersistId", EMPLOYEE_QUALIFIED_NAME, idExpression); final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance("init", "3.2"); processDefBuilder.addBusinessData(bizDataName, EMPLOYEE_QUALIFIED_NAME, getEmployeeExpression); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addUserTask("step2", ACTOR_NAME); final ProcessDefinition definition2 = deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance processInstance2 = getProcessAPI().startProcess(definition2.getId()); waitForUserTask(processInstance2, "step2"); disableAndDeleteProcess(definition.getId()); disableAndDeleteProcess(definition2.getId()); } @Test public void should_associate_the_right_addresses() throws Exception { final Expression addressExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewAddress", "import " + ADDRESS_QUALIFIED_NAME + "; new Address(street:'32, rue Gustave Eiffel', city:'Grenoble')", ADDRESS_QUALIFIED_NAME); final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + ";import java.time.LocalDate;LocalDate localDate = LocalDate.of(1984,10,24); Employee e = new Employee(firstName:'John', lastName:'Doe', addresses:[myAddress]);e.birthDate = localDate; return e; ", EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createBusinessDataExpression("myAddress", ADDRESS_QUALIFIED_NAME)); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "theProcess", "6.3.1"); final String bizDataName = "myEmployee"; processDefinitionBuilder.addBusinessData(bizDataName, EMPLOYEE_QUALIFIED_NAME, null); processDefinitionBuilder.addBusinessData("myAddress", ADDRESS_QUALIFIED_NAME, addressExpression); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addAutomaticTask("step1") .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(bizDataName), OperatorType.ASSIGNMENT, null, null, employeeExpression); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME) .addOperation( new OperationBuilder().createBusinessDataSetAttributeOperation(bizDataName, "addToAddresses", ADDRESS_QUALIFIED_NAME, new ExpressionBuilder().createBusinessDataExpression("myAddress", ADDRESS_QUALIFIED_NAME))) .addOperation( new OperationBuilder().createBusinessDataSetAttributeOperation(bizDataName, "setLastName", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("Smith"))); processDefinitionBuilder.addUserTask("step3", ACTOR_NAME); processDefinitionBuilder.addTransition("step1", "step2"); processDefinitionBuilder.addTransition("step2", "step3"); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); final long userTaskId = waitForUserTask(processInstance, "step2"); Long numberOfAddresses = getNumberOfAddresses(processInstance.getId()); assertThat(numberOfAddresses).isEqualTo(1L); String address = getAddressAsAString("myAddress", processInstance.getId()); assertThat(address).isEqualTo("Address [street=32, rue Gustave Eiffel, city=Grenoble]"); assignAndExecuteStep(userTaskId, testUser); waitForUserTask(processInstance, "step3"); numberOfAddresses = getNumberOfAddresses(processInstance.getId()); assertThat(numberOfAddresses).isEqualTo(1L); address = getAddressAsAString("myAddress", processInstance.getId()); assertThat(address).isEqualTo("Address [street=32, rue Gustave Eiffel, city=Grenoble]"); final String employee = getEmployeeAsAString(bizDataName, processInstance.getId()); assertThat(employee).isEqualTo( "Employee [firstName=John, lastName=Smith, address=null, addresses.count=2, birthDate = 1984-10-24 ]"); disableAndDeleteProcess(definition.getId()); } @Test public void should_install_bdm_reevaluate_process_resolutions() throws Exception { getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); getTenantAdministrationAPI().resume(); ProcessDefinition deploy = getProcessAPI() .deploy(new ProcessDefinitionBuilder().createNewInstance("catClinicProcess", "1.0") .addBusinessData("catPatient", "com.acme.Cat", null).getProcess()); // this should have resolution problem: no BDM deployed assertThat(getProcessAPI().getProcessResolutionProblems(deploy.getId())).hasSize(1).anySatisfy(p -> { assertThat(p.getLevel()).isEqualTo(Problem.Level.ERROR); assertThat(p.getResource()).isEqualTo("business data"); assertThat(p.getResourceId()).isEqualTo("catPatient");// the catPatient business data is not handled by the bdm (no bdm deployed) }); //fix the BDM installAndVerifyBusinessDataModel(businessObjectModel( bom -> bom.addBusinessObject(businessObject("com.acme.Cat", bo -> { bo.addField(stringField("name")); bo.addField(stringField("color")); bo.addField(stringField("furType")); bo.addQuery("findCatByColor", "SELECT c from com.acme.Cat c WHERE c.color = :color", "com.acme.Cat"); })))); // process should be ok assertThat(getProcessAPI().getProcessResolutionProblems(deploy.getId())).isEmpty(); } /** * In case the users call the install BDM multiple times, the API must handle correctly consecutive calls. */ @Test public void should_handle_consecutive_calls() throws Exception { getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); getTenantAdministrationAPI().resume(); final byte[] zip = getZip(businessObjectModel(bom -> { IntStream.range(1, 8).forEach(i -> { bom.addBusinessObject(businessObject("com.acme.Dwarf" + i, bo -> { bo.addField(stringField("name")); bo.addField(stringField("personality")); bo.addField(stringField("color")); bo.addQuery("findDwarf" + i + "ByColor", "SELECT d from com.acme.Dwarf" + i + " d WHERE d.color = :color", "com.acme.Dwarf" + i); })); }); })); // pause only once getTenantAdministrationAPI().pause(); //install the BDM twice with concurrent calls AtomicReference firstException = new AtomicReference<>(); Thread first = new Thread(() -> { try { final String businessDataModelVersion = getTenantAdministrationAPI().updateBusinessDataModel(zip); assertThat(businessDataModelVersion).as("should have deployed BDM").isNotNull(); } catch (Exception e) { firstException.set(e); } }); AtomicReference secondException = new AtomicReference<>(null); Thread second = new Thread(() -> { try { Thread.sleep(200); final String businessDataModelVersion = getTenantAdministrationAPI().updateBusinessDataModel(zip); assertThat(businessDataModelVersion).as("should have deployed BDM").isNotNull(); } catch (Exception e) { secondException.set(e); } }); first.start(); second.start(); first.join(); second.join(); // resume and check deployment getTenantAdministrationAPI().resume(); verifyBdmIsWellDeployed(); Exception e1 = firstException.get(); if (e1 != null) { throw e1; } Exception e2 = secondException.get(); // 2nd request should throw an update exception and operation should abort before starting a transaction assertThat(e2).isNotNull(); assertThat(e2).satisfiesAnyOf( eParam -> assertThat(eParam).isInstanceOf(UnavailableLockException.class), eParam -> assertThat(eParam).getCause().isInstanceOf(UnavailableLockException.class)); } @Test public void should_initialize_a_bo_with_empty_query_result() throws Exception { //given final Expression queryBusinessDataExpression = new ExpressionBuilder().createQueryBusinessDataExpression( "findQuery", "Employee." + GET_EMPLOYEE_BY_LAST_NAME_QUERY_NAME, EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createConstantStringExpression("lastName", "notExists")); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "emptyQueryResult", "1.0"); final String bizDataName = "myEmployee"; processDefinitionBuilder.addBusinessData(bizDataName, EMPLOYEE_QUALIFIED_NAME, queryBusinessDataExpression); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); //when final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); waitForUserTask(processInstance, "step1"); //then final Map> expressions = new HashMap<>(); final String expressionEmployee = "retrieve_Employee"; expressions.put( new ExpressionBuilder().createGroovyScriptExpression(expressionEmployee, " if (" + bizDataName + "==null) { return Boolean.TRUE } else {return Boolean.FALSE} ", Boolean.class.getName(), new ExpressionBuilder().createBusinessDataExpression(bizDataName, EMPLOYEE_QUALIFIED_NAME)), null); final Map evaluatedExpressions = getProcessAPI() .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions); assertThat(evaluatedExpressions).as("should not have a reference to business data") .contains(entry(expressionEmployee, Boolean.TRUE)); disableAndDeleteProcess(definition.getId()); } @Test public void should_initialize_a_multiple_bo_with_empty_query_result() throws Exception { //given final ExpressionImpl dependencyStartIndex = new ExpressionImpl(); dependencyStartIndex.setExpressionType(ExpressionType.TYPE_CONSTANT.name()); dependencyStartIndex.setName("startIndex"); dependencyStartIndex.setReturnType(Integer.class.getName()); dependencyStartIndex.setContent("0"); final ExpressionImpl dependencyMaxResults = new ExpressionImpl(); dependencyMaxResults.setExpressionType(ExpressionType.TYPE_CONSTANT.name()); dependencyMaxResults.setName("maxResults"); dependencyMaxResults.setReturnType(Integer.class.getName()); dependencyMaxResults.setContent("10"); final Expression queryBusinessDataExpression = new ExpressionBuilder().createQueryBusinessDataExpression( "findQuery", "Employee.find", List.class.getName(), dependencyStartIndex, dependencyMaxResults); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "multipleEmptyQueryResult", "1.0"); final String bizDataName = "myEmployees"; processDefinitionBuilder.addBusinessData(bizDataName, EMPLOYEE_QUALIFIED_NAME, queryBusinessDataExpression) .setMultiple(true); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, testUser); //when final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); waitForUserTask(processInstance, "step1"); //then final Map> expressions = new HashMap<>(); final String expressionEmployee = "retrieve_Employee"; expressions.put( new ExpressionBuilder().createGroovyScriptExpression(expressionEmployee, bizDataName + ".toString()", String.class.getName(), new ExpressionBuilder().createBusinessDataExpression(bizDataName, List.class.getName())), null); final Map evaluatedExpressions = getProcessAPI() .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions); assertThat(evaluatedExpressions).as("should not have a reference to business data") .contains(entry(expressionEmployee, "[]")); disableAndDeleteProcess(definition.getId()); } public Long getNumberOfAddresses(final long processInstanceId) throws Exception { final Map> expressions = new HashMap<>(2); expressions.put( new ExpressionBuilder().createQueryBusinessDataExpression("countAddresses", "Address.countAddress", Long.class.getName()), Collections.emptyMap()); final Map result = getProcessAPI().evaluateExpressionsOnProcessInstance(processInstanceId, expressions); return (Long) result.get("countAddresses"); } public String getAddressAsAString(final String addressName, final long processInstanceId) throws Exception { final Map> expressions = new HashMap<>(2); expressions.put( new ExpressionBuilder().createGroovyScriptExpression("getAddress", "\"Address [street=\" + " + addressName + ".street + \", city=\" + " + addressName + ".city + \"]\";", String.class.getName(), new ExpressionBuilder().createBusinessDataExpression(addressName, ADDRESS_QUALIFIED_NAME)), null); final Map result = getProcessAPI().evaluateExpressionsOnProcessInstance(processInstanceId, expressions); return (String) result.get("getAddress"); } private String getEmployeeAsAString(final String businessDataName, final long processInstanceId) throws InvalidExpressionException { final Map> expressions = new HashMap<>(5); final String expressionEmployee = "retrieve_Employee"; expressions.put( new ExpressionBuilder().createGroovyScriptExpression(expressionEmployee, "\"Employee [firstName=\" + " + businessDataName + ".firstName + \", lastName=\" + " + businessDataName + ".lastName + \", address=\" + " + businessDataName + ".address + \", addresses.count=\" + " + businessDataName + ".addresses.size() + \", birthDate = \"+" + businessDataName + ".birthDate + \" ]\";", String.class.getName(), new ExpressionBuilder().createBusinessDataExpression(businessDataName, EMPLOYEE_QUALIFIED_NAME)), null); try { final Map evaluatedExpressions = getProcessAPI() .evaluateExpressionsOnProcessInstance(processInstanceId, expressions); return (String) evaluatedExpressions.get(expressionEmployee); } catch (final ExpressionEvaluationException eee) { System.err.println(eee.getMessage()); return null; } } @Test public void evaluate_context_on_process_and_task() throws Exception { final ProcessDefinitionBuilder p1Builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithContext", "1.0"); final Expression bizDataValue = new ExpressionBuilder() .createGroovyScriptExpression("createNewEmployee", "import " + EMPLOYEE_QUALIFIED_NAME + "; Employee e = new Employee(); e.firstName = 'Jane'; e.lastName = 'Doe'; return e;", EMPLOYEE_QUALIFIED_NAME); p1Builder.addBusinessData("bizData", EMPLOYEE_QUALIFIED_NAME, bizDataValue); p1Builder.addDocumentDefinition("myDoc").addFile("myDoc.txt").addContentFileName("myDoc.txt"); final Expression bizData = new ExpressionBuilder().createBusinessDataExpression("bizData", EMPLOYEE_QUALIFIED_NAME); p1Builder.addContextEntry("process_key1", new ExpressionBuilder().createGroovyScriptExpression("retrieve_firstname", "bizData.firstName", String.class.getName(), bizData)); final UserTaskDefinitionBuilder task1 = p1Builder.addUserTask("step1", "actor"); task1.addShortTextData("task1Data", new ExpressionBuilder().createConstantStringExpression("task1DataValue")); task1.addContextEntry("task_key1", new ExpressionBuilder().createDataExpression("task1Data", String.class.getName())); task1.addContextEntry("task_key2", new ExpressionBuilder().createConstantStringExpression("constantValue")); task1.addContextEntry("processBizDataFromTask1", new ExpressionBuilder().createGroovyScriptExpression("retrieve_firstname", "bizData.lastName", String.class.getName(), bizData)); task1.addContextEntry("doc_key", new ExpressionBuilder().createGroovyScriptExpression("doc.name", "myDoc.fileName", String.class.getName(), new ExpressionBuilder().createDocumentReferenceExpression("myDoc"))); UserTaskDefinitionBuilder task2 = p1Builder.addUserTask("step2", "actor"); task2.addShortTextData("task2Data", new ExpressionBuilder().createConstantStringExpression("task2DataValue")); p1Builder.addActor("actor"); final String myDocumentContent = "Some document content"; final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(p1Builder.getProcess()) .addDocumentResource(new BarResource("myDoc.txt", myDocumentContent.getBytes())).done(); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, "actor", testUser); ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId()); long step1 = waitForUserTask(processInstance1.getId(), "step1"); long step2 = waitForUserTask(processInstance1.getId(), "step2"); assertThat(getProcessAPI().getProcessInstanceExecutionContext(processInstance1.getId())) .containsOnly(entry("process_key1", "Jane")); assertThat(getProcessAPI().getUserTaskExecutionContext(step1)).containsOnly( entry("task_key1", "task1DataValue"), entry("task_key2", "constantValue"), entry("processBizDataFromTask1", "Doe"), entry("doc_key", "myDoc.txt")); assertThat(getProcessAPI().getUserTaskExecutionContext(step2)).isEmpty(); assignAndExecuteStep(step1, testUser.getId()); assignAndExecuteStep(step2, testUser.getId()); waitForProcessToFinish(processInstance1); Thread.sleep(10); final ArchivedProcessInstance finalArchivedProcessInstance = getProcessAPI() .getFinalArchivedProcessInstance(processInstance1.getId()); final ArchivedActivityInstance archivedStep1 = getProcessAPI().getArchivedActivityInstance(step1); final ArchivedActivityInstance archivedStep2 = getProcessAPI().getArchivedActivityInstance(step2); assertThat(getProcessAPI().getArchivedProcessInstanceExecutionContext(finalArchivedProcessInstance.getId())) .containsOnly(entry("process_key1", "Jane")); assertThat(getProcessAPI().getArchivedUserTaskExecutionContext(archivedStep1.getId())).containsOnly( entry("task_key1", "task1DataValue"), entry("task_key2", "constantValue"), entry("processBizDataFromTask1", "Doe"), entry("doc_key", "myDoc.txt")); assertThat(getProcessAPI().getArchivedUserTaskExecutionContext(archivedStep2.getId())).isEmpty(); disableAndDeleteProcess(processDefinition); } @Test public void should_event_sub_process_only_start_element_in_the_event_sub_process() throws Exception { /* * We test here that an event sub process instantiation do nothing on the parent process * see bug BS-15123 and BS-15275 */ //given ProcessDefinitionBuilder parentProcessBuilder = new ProcessDefinitionBuilder() .createNewInstance("ParentProcessWithSignalEventSubProcess", "1.0"); parentProcessBuilder.addActor(ACTOR_NAME); parentProcessBuilder.addAutomaticTask("updateTask").addOperation(new OperationBuilder().createSetDocument( "myDoc", new ExpressionBuilder().createGroovyScriptExpression("updateDocContent", "import org.bonitasoft.engine.bpm.document.DocumentValue;return new DocumentValue('updatedContents'.getBytes(),'plain/text','myDoc.txt');", DocumentValue.class.getName()))); parentProcessBuilder.addUserTask("userTask", ACTOR_NAME); parentProcessBuilder.addTransition("updateTask", "userTask"); parentProcessBuilder.addContract().addInput("employeeName", Type.TEXT, "the name of the business data"); parentProcessBuilder.addBusinessData("myBusinessData", EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createGroovyScriptExpression("initBD", "import " + EMPLOYEE_QUALIFIED_NAME + "\n" + "Employee e = new Employee(); e.firstName = 'Jules'; e.lastName = employeeName; return e;", EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createContractInputExpression("employeeName", String.class.getName()))); parentProcessBuilder.addShortTextData("textData", new ExpressionBuilder().createConstantStringExpression("parentVar")); parentProcessBuilder.addIntegerData("intData", new ExpressionBuilder().createConstantIntegerExpression(1)); parentProcessBuilder.addDocumentDefinition("myDoc") .addInitialValue(new ExpressionBuilder().createGroovyScriptExpression("updateDocContent", "import org.bonitasoft.engine.bpm.document.DocumentValue;return new DocumentValue('initialContent'.getBytes(),'plain/text','myDoc.txt');", DocumentValue.class.getName())); parentProcessBuilder.addDocumentListDefinition("MyList") .addInitialValue(new ExpressionBuilder().createGroovyScriptExpression("updateDocContent", "import org.bonitasoft.engine.bpm.document.DocumentValue;return [new DocumentValue('initialContent'.getBytes(),'plain/text','myDoc1.txt'), new DocumentValue('initialContent'.getBytes(),'plain/text','myDoc2.txt')];", List.class.getName())); //construct sub process SubProcessDefinitionBuilder subProcessBuilder = parentProcessBuilder .addSubProcess("interruptWithSignalProcess", true).getSubProcessBuilder(); StartEventDefinitionBuilder startEventDefinitionBuilder = subProcessBuilder.addStartEvent("signalStart"); startEventDefinitionBuilder.addSignalEventTrigger("theSignal"); subProcessBuilder.addUserTask("userTaskInSubProcess", ACTOR_NAME); subProcessBuilder.addEndEvent("endSubProcess"); subProcessBuilder.addTransition("signalStart", "userTaskInSubProcess"); subProcessBuilder.addTransition("userTaskInSubProcess", "endSubProcess"); subProcessBuilder.addShortTextData("textDataInSub", new ExpressionBuilder().createConstantStringExpression("childVar")); subProcessBuilder.addDoubleData("value", new ExpressionBuilder().createConstantDoubleExpression(10.0)); DesignProcessDefinition processDefinition1 = parentProcessBuilder.done(); BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDefinition1); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, testUser); ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(processDefinition.getId(), Collections.singletonMap("employeeName", "Doe")); //when waitForUserTask("userTask"); assertThat(new String(getProcessAPI().getDocumentContent( getProcessAPI().getLastDocument(processInstance.getId(), "myDoc").getContentStorageId()))) .isEqualTo("updatedContents"); getProcessAPI().sendSignal("theSignal"); //then ActivityInstance eventSubProcessActivity = getProcessAPI() .getActivityInstance(waitForUserTask("userTaskInSubProcess")); //instantiation of the event sub process work and did not reinitialized elements assertThat(new String(getProcessAPI().getDocumentContent( getProcessAPI().getLastDocument(processInstance.getId(), "myDoc").getContentStorageId()))) .isEqualTo("updatedContents"); assertThat(getProcessAPI().getDocumentList(processInstance.getId(), "MyList", 0, 100)).hasSize(2); try { getProcessAPI().getLastDocument(eventSubProcessActivity.getParentProcessInstanceId(), "myDoc"); fail("should not be found"); } catch (DocumentNotFoundException ignored) { } assertThat( getProcessAPI().getDocumentList(eventSubProcessActivity.getParentProcessInstanceId(), "MyList", 0, 100)) .isEmpty(); disableAndDeleteProcess(processDefinition); } @Test public void should_be_able_to_update_business_object_in_event_sub_process() throws Exception { //given ProcessDefinitionBuilder parentProcessBuilder = new ProcessDefinitionBuilder() .createNewInstance("UpdateBusinessDataInEventSubProcess", "1.0"); parentProcessBuilder.addActor(ACTOR_NAME); parentProcessBuilder.addUserTask("userTask", ACTOR_NAME); parentProcessBuilder.addContextEntry("ref_myBusinessData", new ExpressionBuilder().createBusinessDataReferenceExpression("myBusinessData")); parentProcessBuilder.addContract().addInput("employeeName", Type.TEXT, "the name of the business data"); parentProcessBuilder.addBusinessData("myBusinessData", EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createGroovyScriptExpression("initBD", "import " + EMPLOYEE_QUALIFIED_NAME + "\n" + "Employee e = new Employee(); e.firstName = 'Jules'; e.lastName = employeeName; return e;", EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createContractInputExpression("employeeName", String.class.getName()))); //construct sub process SubProcessDefinitionBuilder subProcessBuilder = parentProcessBuilder.addSubProcess("updateBusinessData", true) .getSubProcessBuilder(); StartEventDefinitionBuilder startEventDefinitionBuilder = subProcessBuilder.addStartEvent("signalStart"); startEventDefinitionBuilder.addSignalEventTrigger("theSignal"); subProcessBuilder.addAutomaticTask("updateBD") .addOperation(new OperationBuilder().createBusinessDataSetAttributeOperation("myBusinessData", "setLastName", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("newName"))); subProcessBuilder.addUserTask("userTaskInSubProcess", ACTOR_NAME); subProcessBuilder.addEndEvent("endSubProcess"); subProcessBuilder.addTransition("signalStart", "updateBD"); subProcessBuilder.addTransition("updateBD", "userTaskInSubProcess"); subProcessBuilder.addTransition("userTaskInSubProcess", "endSubProcess"); DesignProcessDefinition processDefinition1 = parentProcessBuilder.done(); BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDefinition1); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, testUser); ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(processDefinition.getId(), Collections.singletonMap("employeeName", "Doe")); waitForUserTask("userTask"); assertThatJson( getBusinessDataAsJson((SimpleBusinessDataReference) getProcessAPI().getProcessInstanceExecutionContext( processInstance.getId()).get("ref_myBusinessData"))) .node("lastName").isEqualTo("\"Doe\""); //when getProcessAPI().sendSignal("theSignal"); waitForUserTask("userTaskInSubProcess"); //then assertThatJson( getBusinessDataAsJson((SimpleBusinessDataReference) getProcessAPI().getProcessInstanceExecutionContext( processInstance.getId()).get("ref_myBusinessData"))) .node("lastName").isEqualTo("\"newName\""); disableAndDeleteProcess(processDefinition); } private String getBusinessDataAsJson(SimpleBusinessDataReference myBusinessData) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException { final Map parameters = new HashMap<>(); parameters.put("businessDataId", myBusinessData.getStorageId()); parameters.put("entityClassName", EMPLOYEE_QUALIFIED_NAME); parameters.put("businessDataURIPattern", "/businessdata/{className}/{id}/{field}"); return (String) getCommandAPI().execute("getBusinessDataById", parameters); } @Test public void shouldRetrieveBDMObjectsInLeftOperandsInCatchMessages() throws Exception { ProcessDefinitionBuilder throwProcessBuilder = new ProcessDefinitionBuilder().createNewInstance("MSG", "1.0"); throwProcessBuilder.addStartEvent("startEvent"); throwProcessBuilder.addActor(ACTOR_NAME); IntermediateThrowEventDefinitionBuilder intermediateThrowEvent = throwProcessBuilder .addIntermediateThrowEvent("sendMessage"); Expression targetProcessExpression = new ExpressionBuilder().createConstantStringExpression("BDM"); Expression targetFlowNodeExpression = new ExpressionBuilder().createConstantStringExpression("message1"); ThrowMessageEventTriggerBuilder messageEventTriggerBuilder = intermediateThrowEvent.addMessageEventTrigger( "msg_name", targetProcessExpression, targetFlowNodeExpression); messageEventTriggerBuilder.addMessageContentExpression( new ExpressionBuilder().createConstantStringExpression("msg_name"), new ExpressionBuilder().createConstantStringExpression("fabrice")); throwProcessBuilder.addTransition("startEvent", "sendMessage"); DesignProcessDefinition designThrowProcessDefinition = throwProcessBuilder.done(); ProcessDefinitionBuilder catchProcessBuilder = new ProcessDefinitionBuilder().createNewInstance("BDM", "1.0"); catchProcessBuilder.addStartEvent("startEvent"); catchProcessBuilder.addActor(ACTOR_NAME); catchProcessBuilder.addBusinessData("myBusinessData", EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createGroovyScriptExpression("initBD", "import " + EMPLOYEE_QUALIFIED_NAME + "\n" + "Employee e = new Employee(); e.firstName = 'Jules'; e.lastName = 'employeeName'; return e;", EMPLOYEE_QUALIFIED_NAME)); CatchMessageEventTriggerDefinitionBuilder catchMessageEventTriggerDefinitionBuilder = catchProcessBuilder .addIntermediateCatchEvent("message1") .addMessageEventTrigger("msg_name"); catchMessageEventTriggerDefinitionBuilder .addOperation(new OperationBuilder().createBusinessDataSetAttributeOperation("myBusinessData", "setFirstName", String.class.getName(), new ExpressionBuilder().createDataExpression("msg_name", String.class.getName()))); catchProcessBuilder.addTransition("startEvent", "message1"); DesignProcessDefinition designCatchProcessDefinition = catchProcessBuilder.done(); BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designCatchProcessDefinition); ProcessDefinition catchProcessDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, testUser); ProcessInstance catchProcessInstance = getProcessAPI().startProcessWithInputs(catchProcessDefinition.getId(), Collections.emptyMap()); businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designThrowProcessDefinition); ProcessDefinition throwProcessDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, testUser); waitForEventInWaitingState(catchProcessInstance, "message1"); ProcessInstance throwProcessInstance = getProcessAPI().startProcess(throwProcessDefinition.getId()); // Message should have been received and process should have finished: waitForProcessToFinish(throwProcessInstance); waitForProcessToFinish(catchProcessInstance); disableAndDeleteProcess(catchProcessDefinition); disableAndDeleteProcess(throwProcessDefinition); } @Test public void should_connector_using_bdm_still_work_after_bdm_update() throws Exception { installAndVerifyBusinessDataModel(bomMyObjectWith1Field()); //connector that call setter on the bdm object byte[] setNameConnectorJar = IOUtil.generateJar(singletonList(retrieveClientBDMModelJar()), "com.acme.SetNameOfBDM", "package com.acme;", "public class SetNameOfBDM extends org.bonitasoft.engine.connector.AbstractConnector {", " public void validateInputParameters() {}", " protected void executeBusinessLogic() {", " System.out.println(\"setting the name of a bdm object in a connector:\"+getInputParameter(\"objectToUpdate\"));", " ((com.acme.MyObject)getInputParameter(\"objectToUpdate\")).setName(\"someName\");", " System.out.println(\"Done!\"+getInputParameter(\"objectToUpdate\"));", " }", "}"); //process that call the connector on the bdm object ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("ProcessThatUpdateBDM", "1.0"); processBuilder.addBusinessData("myObject", "com.acme.MyObject", new ExpressionBuilder().createGroovyScriptExpression("init", "new com.acme.MyObject()", "com.acme.MyObject")); processBuilder.addAutomaticTask("task1") .addConnector("setNameOfBDM", "setNameOfBDM", "1.0", ConnectorEvent.ON_ENTER) .addInput("objectToUpdate", new ExpressionBuilder().createBusinessDataExpression("myObject", "com.acme.MyObject")); BusinessArchive bar = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processBuilder.done()) .addClasspathResource(new BarResource("setNameConnectorJar.jar", setNameConnectorJar)) .addConnectorImplementation(generateConnectorImplementation("setNameOfBDM", "1.0", "com.acme.SetNameOfBDM", "setNameConnectorJar.jar")) .done(); ProcessDefinition processDefinition = getProcessAPI().deployAndEnableProcess(bar); // log. //start and wait for finish, it should work log.info("start process 1"); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForProcessToFinish(processInstance); log.info("install new BDM"); //deploy a new version of the bdm that is compatible installAndVerifyBusinessDataModel(bomMyObjectWith2Fields()); //connector should still work log.info("start process 2"); ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId()); waitForProcessToFinish(processInstance2); } private Path retrieveClientBDMModelJar() throws BusinessDataRepositoryException, IOException { byte[] clientBDMZip = getTenantAdministrationAPI().getClientBDMZip(); byte[] bdmModelJar = org.bonitasoft.engine.io.IOUtils.unzip(clientBDMZip).get("bdm-model.jar"); Path tmpBdmJar = Files.createTempFile("tmpbdmJar", ".jar"); Files.write(tmpBdmJar, bdmModelJar); return tmpBdmJar; } private BusinessObjectModel bomMyObjectWith2Fields() { BusinessObjectModel bom2 = new BusinessObjectModel(); BusinessObject businessObject2 = new BusinessObject(); SimpleField name = new SimpleField(); name.setName("name"); name.setType(FieldType.STRING); SimpleField age = new SimpleField(); age.setName("age"); age.setType(FieldType.INTEGER); businessObject2.addField(name); businessObject2.addField(age); businessObject2.setQualifiedName("com.acme.MyObject"); bom2.addBusinessObject(businessObject2); return bom2; } private BusinessObjectModel bomMyObjectWith1Field() { BusinessObjectModel bom1 = new BusinessObjectModel(); BusinessObject businessObject1 = new BusinessObject(); SimpleField name = new SimpleField(); name.setName("name"); name.setType(FieldType.STRING); businessObject1.addField(name); businessObject1.setQualifiedName("com.acme.MyObject"); bom1.addBusinessObject(businessObject1); return bom1; } class AddressRef { private final String varName; private final String street; private final String city; AddressRef(final String varName, final String street, final String city) { this.varName = varName; this.street = street; this.city = city; } public Expression getExpression() throws InvalidExpressionException { return new ExpressionBuilder().createBusinessDataExpression(getVarName(), ADDRESS_QUALIFIED_NAME); } public String getVarName() { return varName; } public String getStreet() { return street; } public String getCity() { return city; } public Operation getCreationOperation() throws InvalidExpressionException { String sb = "import " + ADDRESS_QUALIFIED_NAME + "\n" + "Address a = new Address();\n" + "a.street ='" + street + "'\n" + "a.city ='" + city + "'\n" + "return a;"; final Expression addressExpression = new ExpressionBuilder().createGroovyScriptExpression( "createAddress" + varName, sb, ADDRESS_QUALIFIED_NAME); return new OperationBuilder().createNewInstance() .setLeftOperand(new LeftOperandBuilder().createBusinessDataLeftOperand(varName)) .setType(OperatorType.ASSIGNMENT) .setRightOperand(addressExpression).done(); } } private BusinessObjectModel buildBOMWithInvalidQuery() { return businessObjectModel( bom -> bom.addBusinessObject(businessObject("com.acme.Cat", bo -> { bo.addField(stringField("name")); bo.addField(stringField("color")); bo.addField(stringField("furType")); bo.addQuery("findCatByColor", "SELECT c from com.acme.Cat \" c WHERE c.color = :color", "com.acme.Cat"); }))); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/client/BonitaClientXMLTest.java ================================================ /** * 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.client; import static org.junit.Assert.assertEquals; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.api.ApiAccessType; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.home.BonitaHome; import org.bonitasoft.engine.util.APITypeManager; import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.rules.TestRule; /** * @author Elias Ricken de Medeiros */ public class BonitaClientXMLTest { private static final String BONITA_HOME_CLIENT_INVALID_API_TYPE = "build/bonita_home_client_invalidAPIType"; private static final String BONITA_HOME_CLIENT_HTTP = "build/bonita_home_client_HTTP"; @Rule public final TestRule restoreSystemProperties = new RestoreSystemProperties(); @After public void tearDown() { TenantAPIAccessor.refresh(); } @Test public void testGetAPIType() throws Exception { ApiAccessType apiType = APITypeManager.getAPIType(); assertEquals(ApiAccessType.LOCAL, apiType); } @Test public void testGetAPITypeParameters() throws Exception { Map parameters = APITypeManager.getAPITypeParameters(); final Map expectedParameters = new HashMap<>(); assertEquals(expectedParameters, parameters); TenantAPIAccessor.refresh(); System.setProperty(BonitaHome.BONITA_HOME, BONITA_HOME_CLIENT_HTTP); parameters = APITypeManager.getAPITypeParameters(); expectedParameters.put("org.bonitasoft.engine.api-type.server.url", "127.255.0.123"); expectedParameters.put("org.bonitasoft.engine.api-type.application.name", "MyBonitaInstallation"); assertEquals(expectedParameters, parameters); } @Test(expected = UnknownAPITypeException.class) public void testCannotUseAnInvalidAPITypePlatForm() throws Exception { System.setProperty(BonitaHome.BONITA_HOME, BONITA_HOME_CLIENT_INVALID_API_TYPE); PlatformAPIAccessor.getPlatformLoginAPI(); } @Test(expected = UnknownAPITypeException.class) public void testCannotUseAnInvalidAPITypeTenants() throws Exception { System.setProperty(BonitaHome.BONITA_HOME, BONITA_HOME_CLIENT_INVALID_API_TYPE); TenantAPIAccessor.getLoginAPI(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/AdvancedStartProcessCommandIT.java ================================================ /** * 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.command; import static org.bonitasoft.engine.command.helper.designer.Transition.fails; import static org.bonitasoft.engine.command.helper.designer.Transition.meet; import java.io.Serializable; 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 org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.document.DocumentValue; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.command.helper.ProcessDeployer; import org.bonitasoft.engine.command.helper.designer.Gateway; import org.bonitasoft.engine.command.helper.designer.SimpleProcessDesigner; import org.bonitasoft.engine.command.helper.designer.UserTask; import org.bonitasoft.engine.command.helper.expectation.TestUtils; import org.bonitasoft.engine.connectors.TestConnectorWithOutput; import org.bonitasoft.engine.connectors.VariableStorage; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.junit.After; import org.junit.Test; /** * Created by Vincent Elcrin * Date: 11/12/13 * Time: 09:45 */ public class AdvancedStartProcessCommandIT extends TestWithUser { private static final String CONNECTOR_OUTPUT_NAME = "output1"; private static final String CONNECTOR_INPUT_NAME = "input1"; private static final String CONNECTOR_WITH_OUTPUT_ID = "org.bonitasoft.connector.testConnectorWithOutput"; private final SimpleProcessDesigner designer = new SimpleProcessDesigner(getProcessDefinitionBuilder()); private final TestUtils wrapper = new TestUtils(this); private final ProcessDeployer processDeployer = getProcessDeployer(); @After public void afterTest() throws BonitaException { processDeployer.clean(); VariableStorage.clearAll(); } @Test public void should_start_a_sequential_process() throws Exception { final ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1")) .then(new UserTask("step 2")) .then(new UserTask("step 3")) .end()); final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), "step 2"); process.expect("step 2").toBeReady(); process.expect("start", "step 1").toNotHaveArchives(); } @Test public void should_start_a_sequential_process_with_variables() throws Exception { final ProcessDefinitionBuilder builder = getProcessDefinitionBuilder(); builder.addShortTextData("variable", new ExpressionBuilder().createConstantStringExpression("default")); final SimpleProcessDesigner designer = new SimpleProcessDesigner(builder); final ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1")) .then(new UserTask("step 2")) .then(new UserTask("step 3")) .end()); final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), "step 2", Arrays.asList(createSetDataOperation("variable", "Done!")), Collections. emptyMap()); process.expectVariable("variable").toBe("Done!"); } @Test public void should_be_able_to_start_a_sequential_process_with_a_document() throws Exception { final ProcessDefinitionBuilder builder = getProcessDefinitionBuilder(); builder.addDocumentDefinition("document"); final ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1")) .then(new UserTask("step 2")) .then(new UserTask("step 3")) .end()); final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), "step 2", Collections.singletonList(new OperationBuilder().createSetDocument("document", new ExpressionBuilder().createInputExpression("value", DocumentValue.class.getName()))), Collections. singletonMap( "value", new DocumentValue("content".getBytes(), "plain/text", "document"))); process.expectDocument("document").toBe("content"); } @Test public void should_be_able_to_start_a_process_with_a_parallel_split() throws Exception { final ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1")) .then(new Gateway("split", GatewayType.PARALLEL)) .then(new UserTask("step 2"), new UserTask("step 3")) .end()); final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), "step 1"); process.execute(user, "step 1", "step 2", "step 3"); process.isExpected().toFinish(); process.expect("start").toNotHaveArchives(); } @Test public void should_be_able_to_start_a_process_with_an_exclusive_merge() throws Exception { final ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1"), new UserTask("step 2")) .then(new Gateway("exclusive", GatewayType.EXCLUSIVE)) .then(new UserTask("step 3")) .end()); final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), "step 2"); process.execute(user, "step 2", "step 3"); process.isExpected().toFinish(); process.expect("start").toNotHaveArchives(); process.expect("step 3").toBeExecuted(1); } @Test public void should_be_able_to_start_a_process_with_an_exclusive_split() throws Exception { final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true); final ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1")) .then(new Gateway("exclusive", GatewayType.EXCLUSIVE)) .then( new UserTask("step 2").when("exclusive", fails()), new UserTask("step 3").when("exclusive", meet(condition))) .end()); final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), "step 1"); process.execute(user, "step 1", "step 3"); process.isExpected().toFinish(); process.expect("start").toNotHaveArchives(); process.expect("step 3").toBeExecuted(1); } @Test public void should_be_able_to_start_a_process_before_an_inclusive() throws Exception { final ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1")) .then(new Gateway("inclusive 1", GatewayType.INCLUSIVE)) .then(new UserTask("step 2"), new UserTask("step 3")) .then(new Gateway("inclusive 2", GatewayType.INCLUSIVE)) .then(new UserTask("step 4")) .end()); final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), "step 1"); process.execute(user, "step 1", "step 2", "step 3", "step 4"); process.isExpected().toFinish(); process.expect("start").toNotHaveArchives(); } private Operation createSetDataOperation(final String name, final String value) throws InvalidExpressionException { return new OperationBuilder().createSetDataOperation(name, new ExpressionBuilder().createConstantStringExpression(value)); } private ProcessDefinitionBuilder getProcessDefinitionBuilder() { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); builder.createNewInstance("Designed by designer", "1.0") .addActor("actor") .addDescription("Coding all-night-long"); return builder; } private TestUtils.Process startProcess(final long startedBy, final long processDefinitionId, final String activityName) throws Exception { return startProcess(startedBy, processDefinitionId, activityName, null, null); } private TestUtils.Process startProcess(final long startedBy, final long processDefinitionId, final String activityName, final List operations, final Map context) throws Exception { final Map parameters = new HashMap<>(); parameters.put("started_by", startedBy); parameters.put("process_definition_id", processDefinitionId); parameters.put("activity_name", activityName); if (operations != null) { parameters.put("operations", new ArrayList<>(operations)); } if (context != null) { parameters.put("context", new HashMap<>(context)); } return wrapper.wrap((ProcessInstance) getCommandAPI().execute("advancedStartProcessCommand", parameters)); } private ProcessDeployer getProcessDeployer() { return new ProcessDeployer() { @Override public ProcessDefinition deploy(final DesignProcessDefinition design) throws BonitaException { return deployAndEnableProcessWithActor(design, "actor", user); } @Override public void clean(final ProcessDefinition processDefinition) throws BonitaException { disableAndDeleteProcess(processDefinition); } }; } @Test public void advancedStartProcessCommandWithConnectorOnEnterOnProcess() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", null); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME).addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addConnector("myConnector", CONNECTOR_WITH_OUTPUT_ID, "1.0", ConnectorEvent.ON_ENTER) .addInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createConstantStringExpression("a")) .addOutput(new LeftOperandBuilder().createNewInstance().setName("outputOfConnector").done(), OperatorType.ASSIGNMENT, "=", "", new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME, String.class.getName())); processDefinitionBuilder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActorAndConnector( processDefinitionBuilder, ACTOR_NAME, user, "TestConnectorWithOutput.impl", TestConnectorWithOutput.class, "TestConnectorWithOutput.jar"); // Start the process with the command on the step2 final Map parametersCommand = new HashMap<>(); parametersCommand.put("started_by", user.getId()); parametersCommand.put("process_definition_id", processDefinition.getId()); parametersCommand.put("activity_name", "step2"); // command API execution getCommandAPI().execute("advancedStartProcessCommand", parametersCommand); waitForUserTask("step2"); // Clean disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/CommandIT.java ================================================ /** * 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.command; import static org.bonitasoft.engine.commons.io.IOUtil.generateJar; import static org.junit.Assert.*; import java.io.IOException; import java.io.Serializable; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.api.CommandAPI; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.Test; public class CommandIT extends TestWithTechnicalUser { @Test(expected = AlreadyExistsException.class) public void commandAlreadyExistsException() throws Exception { getCommandAPI().addDependency("commands", "jar".getBytes()); getCommandAPI().register("command1", "command description", "implementation"); try { getCommandAPI().register("command1", "command description", "implementation"); } finally { getCommandAPI().unregister("command1"); getCommandAPI().removeDependency("commands"); } } @Test(expected = CommandNotFoundException.class) public void commandNotFoundException() throws BonitaException { getCommandAPI().getCommand("b"); } @Test(expected = CommandNotFoundException.class) public void executeUnknownCommand() throws BonitaException { final Map parameters = new HashMap<>(); parameters.put("n1", "v1"); getCommandAPI().execute("com", parameters); } @Test public void executeCommandWithParameters() throws BonitaException, IOException { final byte[] byteArray = generateJar(IntegerCommand.class); getCommandAPI().addDependency("commands", byteArray); getCommandAPI().register("intReturn", "Retrieving the integer value", "org.bonitasoft.engine.command.IntegerCommand"); final Map parameters = new HashMap<>(); parameters.put("int", 83); final Integer actual = (Integer) getCommandAPI().execute("intReturn", parameters); assertEquals(Integer.valueOf(83), actual); getCommandAPI().unregister("intReturn"); getCommandAPI().removeDependency("commands"); } @Test(expected = CommandParameterizationException.class) public void commandThrowsCommandParameterizationException() throws BonitaException, IOException { final byte[] byteArray = generateJar(ParameterizationExceptionCommand.class); getCommandAPI().addDependency("commands", byteArray); getCommandAPI().register("except", "Throws ParameterizationException", "org.bonitasoft.engine.command.ParameterizationExceptionCommand"); final Map parameters = new HashMap<>(); parameters.put("key", 83); try { getCommandAPI().execute("except", parameters); } finally { getCommandAPI().unregister("except"); getCommandAPI().removeDependency("commands"); } } @Test(expected = CommandExecutionException.class) public void commandThrowsCommandExecutionException() throws BonitaException, IOException { final byte[] byteArray = generateJar(ExecutionExceptionCommand.class); getCommandAPI().addDependency("commands", byteArray); getCommandAPI().register("except", "Throws ExecutionExceptionCommand", "org.bonitasoft.engine.command.ExecutionExceptionCommand"); final Map parameters = new HashMap<>(); parameters.put("key", 83); try { getCommandAPI().execute("except", parameters); } finally { getCommandAPI().unregister("except"); getCommandAPI().removeDependency("commands"); } } @Test public void createCommand() throws BonitaException { try { getCommandAPI().addDependency("commands", "jar".getBytes()); final CommandDescriptor command = getCommandAPI().register("command1", "command description", "implementation"); assertNotNull(command); assertEquals("command1", command.getName()); assertEquals("command description", command.getDescription()); } finally { getCommandAPI().unregister("command1"); getCommandAPI().removeDependency("commands"); } } @Test public void deleteCommand() throws BonitaException { // delete command by name getCommandAPI().addDependency("commands", "jar".getBytes()); CommandDescriptor command = getCommandAPI().register("command1", "command description", "implementation"); assertNotNull(command); getCommandAPI().unregister("command1"); try { getCommandAPI().getCommand("command1"); fail("Command does not exist anymore"); } catch (final CommandNotFoundException e) { getCommandAPI().removeDependency("commands"); } // delete command by id getCommandAPI().addDependency("commands", "jar".getBytes()); command = getCommandAPI().register("command1", "command description", "implementation"); assertNotNull(command); getCommandAPI().unregister(command.getId()); try { getCommandAPI().getCommand("command1"); fail("Command does not exist anymore"); } catch (final CommandNotFoundException e) { getCommandAPI().removeDependency("commands"); } } @Test public void getCommandByName() throws BonitaException { final String commandName = "command1"; try { getCommandAPI().addDependency("commands", "jar".getBytes()); getCommandAPI().register(commandName, "command description", "implementation"); final CommandDescriptor command = getCommandAPI().getCommand(commandName); assertEquals("command1", command.getName()); } finally { getCommandAPI().unregister(commandName); getCommandAPI().removeDependency("commands"); } } @Test public void updateCommand() throws BonitaException { getCommandAPI().addDependency("commands", "jar".getBytes()); final CommandDescriptor oldCommand = getCommandAPI().register("command", "old description", "implementation"); final long commandId = oldCommand.getId(); assertEquals("command", oldCommand.getName()); assertEquals("old description", oldCommand.getDescription()); // test update name specified command final CommandUpdater commandUpdateDescriptor = new CommandUpdater(); commandUpdateDescriptor.setDescription("new description"); getCommandAPI().update("command", commandUpdateDescriptor); CommandDescriptor newCommand = getCommandAPI().getCommand("command"); assertEquals(commandId, newCommand.getId()); assertEquals("command", newCommand.getName()); assertEquals("new description", newCommand.getDescription()); // test update id specified command commandUpdateDescriptor.setName("updatedCommandName for the id specified command"); commandUpdateDescriptor.setDescription("updatedDescription for the id specified command"); getCommandAPI().update(commandId, commandUpdateDescriptor); newCommand = getCommandAPI().get(commandId); assertEquals("updatedCommandName for the id specified command", newCommand.getName()); assertEquals("updatedDescription for the id specified command", newCommand.getDescription()); getCommandAPI().unregister(commandId); getCommandAPI().removeDependency("commands"); } @Test public void getCommandsWithCommandCriterion() throws BonitaException { try { getCommandAPI().addDependency("commands", "jar".getBytes()); getCommandAPI().register("aaaCommand2", "command description", "implementation"); getCommandAPI().register("aaaCommand3", "command description", "implementation"); getCommandAPI().register("aaaCommand1", "command description", "implementation"); final List commandsPage1 = getCommandAPI().getAllCommands(0, 2, CommandCriterion.NAME_ASC); assertEquals(2, commandsPage1.size()); assertEquals("aaaCommand1", commandsPage1.get(0).getName()); assertEquals("aaaCommand2", commandsPage1.get(1).getName()); final List commandsPage2 = getCommandAPI().getAllCommands(2, 1, CommandCriterion.NAME_ASC); assertEquals(1, commandsPage2.size()); assertEquals("aaaCommand3", commandsPage2.get(0).getName()); } finally { getCommandAPI().unregister("aaaCommand2"); getCommandAPI().unregister("aaaCommand3"); getCommandAPI().unregister("aaaCommand1"); getCommandAPI().removeDependency("commands"); } } @Test public void testGetCommands() throws BonitaException { // Create and register Commands as System is false getCommandAPI().addDependency("commands", "jar".getBytes()); getCommandAPI().register("command1", "GetCommands description", "implementation"); getCommandAPI().register("command2", "GetCommands description", "implementation"); getCommandAPI().register("command3", "GetCommands description", "implementation"); // Search and test the result final List commands = getCommandAPI().getUserCommands(0, 3, CommandCriterion.NAME_ASC); assertEquals(3, commands.size()); assertEquals(false, commands.get(0).isSystemCommand()); assertEquals(false, commands.get(1).isSystemCommand()); assertEquals(false, commands.get(2).isSystemCommand()); // Clean Commands getCommandAPI().unregister("command1"); getCommandAPI().unregister("command2"); getCommandAPI().unregister("command3"); getCommandAPI().removeDependency("commands"); } @Test public void getCommandById() throws BonitaException { final String commandName = "command1"; try { getCommandAPI().addDependency("commands", "jar".getBytes()); final CommandDescriptor registeredCommand = getCommandAPI().register(commandName, "command description", "implementation"); final CommandDescriptor command = getCommandAPI().get(registeredCommand.getId()); assertEquals("command1", command.getName()); } finally { getCommandAPI().unregister(commandName); getCommandAPI().removeDependency("commands"); } } @Test public void searchCommands() throws BonitaException { final CommandAPI commandAPI = getCommandAPI(); commandAPI.addDependency("commands", "jar".getBytes()); final CommandDescriptor command1 = commandAPI.register("testCommand1", "SearchCommands description1", "implementation"); final CommandDescriptor command2 = commandAPI.register("testCommand2", "GetCommands description2", "implementation"); final CommandDescriptor command3 = commandAPI.register("testCommand3", "GetCommands description3", "implementation"); try { // search paging with order ASC SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 2); builder.searchTerm("testCommand"); builder.sort(CommandSearchDescriptor.NAME, Order.ASC); SearchResult searchCommands = commandAPI.searchCommands(builder.done()); assertNotNull(searchCommands); assertEquals(3, searchCommands.getCount()); List commands = searchCommands.getResult(); assertEquals(2, commands.size()); assertEquals(command1, commands.get(0)); assertEquals(command2, commands.get(1)); builder = new SearchOptionsBuilder(2, 2); builder.searchTerm("testCommand"); builder.sort(CommandSearchDescriptor.NAME, Order.ASC); searchCommands = commandAPI.searchCommands(builder.done()); assertNotNull(searchCommands); assertEquals(3, searchCommands.getCount()); commands = searchCommands.getResult(); assertEquals(1, commands.size()); assertEquals(command3, commands.get(0)); builder = new SearchOptionsBuilder(3, 2); builder.searchTerm("testCommand"); builder.sort(CommandSearchDescriptor.NAME, Order.ASC); searchCommands = commandAPI.searchCommands(builder.done()); assertEquals(0, searchCommands.getResult().size()); // test Desc builder = new SearchOptionsBuilder(0, 2); builder.searchTerm("testCommand"); builder.sort(CommandSearchDescriptor.NAME, Order.DESC); searchCommands = commandAPI.searchCommands(builder.done()); assertNotNull(searchCommands); assertEquals(3, searchCommands.getCount()); commands = searchCommands.getResult(); assertEquals(2, commands.size()); assertEquals(command3, commands.get(0)); assertEquals(command2, commands.get(1)); // test search with filter builder = new SearchOptionsBuilder(0, 10); builder.filter(CommandSearchDescriptor.NAME, "testCommand1"); searchCommands = commandAPI.searchCommands(builder.done()); assertNotNull(searchCommands); assertEquals(1, searchCommands.getCount()); commands = searchCommands.getResult(); assertEquals(1, commands.size()); assertEquals(command1, commands.get(0)); // test search with term builder = new SearchOptionsBuilder(0, 10); builder.searchTerm("testCommand"); builder.sort(CommandSearchDescriptor.NAME, Order.ASC); searchCommands = commandAPI.searchCommands(builder.done()); assertNotNull(searchCommands); assertEquals(3, searchCommands.getCount()); commands = searchCommands.getResult(); assertEquals(3, commands.size()); assertEquals(command1, commands.get(0)); assertEquals(command2, commands.get(1)); assertEquals(command3, commands.get(2)); } finally { // Clean Commands commandAPI.unregister("testCommand1"); commandAPI.unregister("testCommand2"); commandAPI.unregister("testCommand3"); commandAPI.removeDependency("commands"); } } @Test public void searchCommandsWithApostrophe() throws BonitaException { getCommandAPI().addDependency("commands", "jar".getBytes()); final CommandDescriptor command1 = getCommandAPI().register("'command'1", "SearchCommands description1", "implementation"); final CommandDescriptor command2 = getCommandAPI().register("command2", "'command'1", "implementation"); final CommandDescriptor command3 = getCommandAPI().register("command3", "'SearchCommands description1", "command'tation"); // test search with filter final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(CommandSearchDescriptor.NAME, Order.ASC); builder.searchTerm("'"); final CommandAPI commandAPI = getCommandAPI(); final SearchResult searchCommands = commandAPI.searchCommands(builder.done()); assertNotNull(searchCommands); assertEquals(3, searchCommands.getCount()); final List commands = searchCommands.getResult(); assertEquals(3, commands.size()); assertEquals(command1, commands.get(0)); assertEquals(command2, commands.get(1)); assertEquals(command3, commands.get(2)); // Clean Commands getCommandAPI().unregister("'command'1"); getCommandAPI().unregister("command2"); getCommandAPI().unregister("command3"); getCommandAPI().removeDependency("commands"); } @Test public void executeCommandById() throws BonitaException, IOException { final byte[] byteArray = generateJar(IntegerCommand.class); getCommandAPI().addDependency("commands", byteArray); final CommandDescriptor command = getCommandAPI() .register("intReturn", "Retrieving the integer value", "org.bonitasoft.engine.command.IntegerCommand"); final CommandDescriptor commandById = getCommandAPI().get(command.getId()); assertEquals(commandById.getId(), command.getId()); final Map parameters = new HashMap<>(); parameters.put("int", 83); final Integer actual = (Integer) getCommandAPI().execute(commandById.getId(), parameters); assertEquals(Integer.valueOf(83), actual); getCommandAPI().unregister("intReturn"); getCommandAPI().removeDependency("commands"); } @Test(expected = BonitaRuntimeException.class) public void executeCommandThrowsANPE() throws BonitaException, IOException { final byte[] byteArray = generateJar(NPECommand.class); getCommandAPI().addDependency("commands", byteArray); getCommandAPI().register("NPEReturns", "Throws a NPE", "org.bonitasoft.engine.command.NPECommand"); try { getCommandAPI().execute("NPEReturns", null); } finally { getCommandAPI().unregister("NPEReturns"); getCommandAPI().removeDependency("commands"); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/ExecuteBDMQueryCommandIT.java ================================================ /** * 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.command; import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO; import static org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder.aBOM; import static org.bonitasoft.engine.bdm.builder.FieldBuilder.aRelationField; import static org.bonitasoft.engine.bdm.builder.FieldBuilder.aSimpleField; import static org.bonitasoft.engine.bdm.builder.QueryBuilder.aQuery; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.time.LocalDate; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.bdm.BusinessObjectModelConverter; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.RelationField.Type; import org.bonitasoft.engine.bdm.serialization.BusinessDataObjectMapper; import org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryResult; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.business.data.ClassloaderRefresher; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.io.IOUtils; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; /** * @author Romain Bioteau */ public class ExecuteBDMQueryCommandIT extends CommonAPIIT { private static final String EXECUTE_BDM_QUERY_COMMAND = "executeBDMQuery"; public static final String GET_BUSINESS_DATA_BY_QUERY_COMMAND = "getBusinessDataByQueryCommand"; private static final String EMPLOYEE_QUALIF_CLASSNAME = "org.bonita.pojo.BonitaEmployee"; private static final String ADDRESS_QUALIF_CLASSNAME = "org.bonita.pojo.BonitaAddress"; private static final String RETURNS_LIST = "returnsList"; private static final String QUERY_PARAMETERS = "queryParameters"; public static final String ENTITY_CLASS_NAME = "entityClassName"; private static final String RETURN_TYPE = "returnType"; private static final String START_INDEX = "startIndex"; private static final String MAX_RESULTS = "maxResults"; private static final String QUERY_NAME = "queryName"; protected User businessUser; private ClassLoader contextClassLoader; private static File clientFolder; private LocalDate birthdate; private BusinessObjectModel buildCustomBOM() { final BusinessObject addressBO = aBO(ADDRESS_QUALIF_CLASSNAME) .withField(aSimpleField().withName("street").ofType(FieldType.STRING).build()).build(); final BusinessObject employee = aBO(EMPLOYEE_QUALIF_CLASSNAME) .withDescription("Describe final a simple employee") .withField(aSimpleField().withName("firstName").ofType(FieldType.STRING).withLength(10).build()) .withField(aSimpleField().withName("lastName").ofType(FieldType.STRING).notNullable().build()) .withField(aSimpleField().withName("birthdate").ofType(FieldType.LOCALDATE).nullable().build()) .withField(aRelationField().withName("addresses").ofType(Type.COMPOSITION).referencing(addressBO) .multiple().lazy().build()) .withQuery( aQuery().withName("getNoEmployees") .withContent("SELECT e FROM BonitaEmployee e WHERE e.firstName = 'INEXISTANT'") .withReturnType(List.class.getName()).build()) .withQuery( aQuery().withName("getEmployeeByFirstNameAndLastName") .withContent( "SELECT e FROM BonitaEmployee e WHERE e.firstName=:firstName AND e.lastName=:lastName") .withReturnType(EMPLOYEE_QUALIF_CLASSNAME) .withQueryParameter("firstName", String.class.getName()) .withQueryParameter("lastName", String.class.getName()).build()) .withQuery( aQuery().withName("customQuery") .withContent("SELECT e FROM BonitaEmployee e") .withReturnType(List.class.getName()) .build()) .withQuery( aQuery().withName("countForCustomQuery") .withContent("SELECT COUNT(e) FROM BonitaEmployee e") .withReturnType(Long.class.getName()) .build()) .build(); return aBOM().withBOs(addressBO, employee).build(); } @BeforeClass public static void initTestClass() throws IOException { clientFolder = IOUtils.createTempDirectory("ExecuteBDMQueryCommandIT_client"); clientFolder.mkdirs(); } @AfterClass public static void cleanTestClass() { try { FileUtils.deleteDirectory(clientFolder); } catch (final Exception e) { clientFolder.deleteOnExit(); } } @Before public void beforeTest() throws Exception { loginWithTechnicalUser(); businessUser = createUser(USERNAME, PASSWORD); logout(); loginWithTechnicalUser(); final BusinessObjectModelConverter converter = new BusinessObjectModelConverter(); final byte[] zip = converter.zip(buildCustomBOM()); assertThat(getTenantAdministrationAPI().isPaused()).as("Tenant is paused?").isFalse(); getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); getTenantAdministrationAPI().updateBusinessDataModel(zip); getTenantAdministrationAPI().resume(); logout(); loginOnDefaultTenantWith(USERNAME, PASSWORD); loadClientJars(); birthdate = LocalDate.of(1982, 3, 17); addEmployee("Romain", "Bioteau", birthdate, "54, Grand Rue", "38 , Gabrile Péri"); addEmployee("Jules", "Bioteau", null, "78 , Colonel Bougault"); addEmployee("Matthieu", "Chaffotte", null); } private void loadClientJars() throws Exception { contextClassLoader = Thread.currentThread().getContextClassLoader(); final byte[] clientBDMZip = getTenantAdministrationAPI().getClientBDMZip(); final ClassLoader classLoaderWithBDM = new ClassloaderRefresher().loadClientModelInClassloader(clientBDMZip, contextClassLoader, EMPLOYEE_QUALIF_CLASSNAME, clientFolder); Thread.currentThread().setContextClassLoader(classLoaderWithBDM); } @After public void cleanClassLoader_and_uninstall_bdm() throws BonitaException { // reset previous classloader: if (contextClassLoader != null) { Thread.currentThread().setContextClassLoader(contextClassLoader); } logout(); loginWithTechnicalUser(); if (!getTenantAdministrationAPI().isPaused()) { getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); getTenantAdministrationAPI().resume(); } deleteUser(businessUser); logout(); } @Test public void should_execute_returns_empty_list() throws Exception { final Map parameters = new HashMap<>(); parameters.put(QUERY_NAME, "BonitaEmployee.getNoEmployees"); parameters.put(RETURNS_LIST, true); parameters.put(RETURN_TYPE, EMPLOYEE_QUALIF_CLASSNAME); parameters.put(START_INDEX, 0); parameters.put(MAX_RESULTS, 10); final byte[] result = (byte[]) getCommandAPI().execute(EXECUTE_BDM_QUERY_COMMAND, parameters); assertThat(deserializeListResult(result)).isNotNull().isInstanceOf(List.class).isEmpty(); } @Test public void should_execute_returns_employee_list() throws Exception { final Map parameters = new HashMap<>(); parameters.put(QUERY_NAME, "BonitaEmployee.find"); parameters.put(RETURNS_LIST, true); parameters.put(RETURN_TYPE, EMPLOYEE_QUALIF_CLASSNAME); parameters.put(START_INDEX, 0); parameters.put(MAX_RESULTS, 10); final byte[] result = (byte[]) getCommandAPI().execute(EXECUTE_BDM_QUERY_COMMAND, parameters); assertThat(deserializeListResult(result)).isNotNull().isInstanceOf(List.class).hasSize(3); } @Test public void getListFromQueryShouldLimitToMaxResults() throws Exception { final Map parameters = new HashMap<>(); parameters.put(QUERY_NAME, "BonitaEmployee.find"); parameters.put(RETURNS_LIST, true); parameters.put(RETURN_TYPE, EMPLOYEE_QUALIF_CLASSNAME); parameters.put(START_INDEX, 0); parameters.put(MAX_RESULTS, 2); final byte[] result = (byte[]) getCommandAPI().execute(EXECUTE_BDM_QUERY_COMMAND, parameters); assertThat(deserializeListResult(result)).isNotNull().isInstanceOf(List.class).hasSize(2); } @Test public void should_have_a_count_query() throws Exception { final Map parameters = new HashMap<>(); parameters.put(QUERY_NAME, "BonitaEmployee.countForFind"); parameters.put(RETURNS_LIST, false); parameters.put(RETURN_TYPE, Long.class.getName()); parameters.put(START_INDEX, 0); parameters.put(MAX_RESULTS, 1); final byte[] result = (byte[]) getCommandAPI().execute(EXECUTE_BDM_QUERY_COMMAND, parameters); final BusinessDataObjectMapper mapper = new BusinessDataObjectMapper(); final Long count = mapper.readValue(result, Long.class); assertThat(count).isEqualTo(3L); } @Test public void should_have_query_metadata_on_auto_generated_queries() throws Exception { //given final BusinessDataQueryResult businessDataQueryResult = executeQuery("find", 2, 1); // Standard shape returns single object for single result from List query assertThatJson(businessDataQueryResult.getJsonResults()).as("should get json results") .isEqualTo(getJsonContent("Employee.find.2.1.json")); assertThat(businessDataQueryResult.getBusinessDataQueryMetadata().getCount()).isEqualTo(3L); assertThat(businessDataQueryResult.getBusinessDataQueryMetadata().getStartIndex()).isEqualTo(2); assertThat(businessDataQueryResult.getBusinessDataQueryMetadata().getMaxResults()).isEqualTo(1); } @Test public void should_return_count_result_when_executing_count_query() throws Exception { //given final BusinessDataQueryResult businessDataQueryResult = executeQuery("countForCustomQuery", 0, 1); // Standard shape returns object with value property assertThatJson(businessDataQueryResult.getJsonResults()).as("should get json results") .isEqualTo("{\"value\":3}"); } @Test public void should_have_query_metadata_on_custom_queries_with_related_count() throws Exception { //given int startIndex = 2; int maxResults = 1; final BusinessDataQueryResult businessDataQueryResult = executeQuery("customQuery", startIndex, maxResults); // Standard shape returns array for List-returning query assertThatJson(businessDataQueryResult.getJsonResults()).as("should get json results") .isEqualTo(getJsonContent("Employee.find.2.1.json")); assertThat(businessDataQueryResult.getBusinessDataQueryMetadata()).isNotNull(); assertThat(businessDataQueryResult.getBusinessDataQueryMetadata().getCount()).isEqualTo(3L); assertThat(businessDataQueryResult.getBusinessDataQueryMetadata().getStartIndex()).isEqualTo(startIndex); assertThat(businessDataQueryResult.getBusinessDataQueryMetadata().getMaxResults()).isEqualTo(maxResults); } private BusinessDataQueryResult executeQuery(String queryName, int startIndex, int maxResults) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException { final Map parameters = new HashMap<>(); HashMap queryParameters = new HashMap<>(); parameters.put(QUERY_NAME, queryName); parameters.put(ENTITY_CLASS_NAME, EMPLOYEE_QUALIF_CLASSNAME); parameters.put(QUERY_PARAMETERS, queryParameters); parameters.put(START_INDEX, startIndex); parameters.put(MAX_RESULTS, maxResults); parameters.put("businessDataURIPattern", "/businessdata/{className}/{id}/{field}"); //when return (BusinessDataQueryResult) getCommandAPI().execute(GET_BUSINESS_DATA_BY_QUERY_COMMAND, parameters); } @Test public void should_have_results_but_no_query_metadata_when_count_is_not_available() throws Exception { //given final BusinessDataQueryResult businessDataQueryResult = executeQuery("getNoEmployees", 2, 1); //then // Returns empty array for List queries with no results assertThatJson(businessDataQueryResult.getJsonResults()).as("should get json results").isEqualTo("[]"); assertThat(businessDataQueryResult.getBusinessDataQueryMetadata()).isNull(); } @Test public void should_execute_returns_a_single_employee() throws Exception { final Map parameters = new HashMap<>(); parameters.put(QUERY_NAME, "BonitaEmployee.getEmployeeByFirstNameAndLastName"); parameters.put(RETURN_TYPE, EMPLOYEE_QUALIF_CLASSNAME); final Map queryParameters = new HashMap<>(); queryParameters.put("firstName", "Romain"); queryParameters.put("lastName", "Bioteau"); parameters.put(QUERY_PARAMETERS, (Serializable) queryParameters); final byte[] result = (byte[]) getCommandAPI().execute(EXECUTE_BDM_QUERY_COMMAND, parameters); final Serializable employee = deserializeSimpleResult(result); assertThat(employee).isNotNull().isInstanceOf(Entity.class); assertThat(employee.getClass().getName()).isEqualTo(EMPLOYEE_QUALIF_CLASSNAME); assertThat(employee.getClass().getMethod("getFirstName", new Class[0]).invoke(employee)).isEqualTo("Romain"); assertThat(employee.getClass().getMethod("getLastName", new Class[0]).invoke(employee)).isEqualTo("Bioteau"); assertThat(employee.getClass().getMethod("getBirthdate", new Class[0]).invoke(employee)).isEqualTo(birthdate); final Object invoke = employee.getClass().getMethod("getAddresses", new Class[0]).invoke(employee); assertThat(invoke).isInstanceOf(List.class); assertThat((List) invoke).isEmpty(); } @Test(expected = CommandExecutionException.class) public void should_execute_throw_a_CommandExecutionException_if_result_is_not_single() throws Exception { final Map parameters = new HashMap<>(); parameters.put(QUERY_NAME, "BonitaEmployee.findByLastName"); parameters.put(RETURN_TYPE, EMPLOYEE_QUALIF_CLASSNAME); final Map queryParameters = new HashMap<>(); queryParameters.put("lastName", "Bioteau"); parameters.put(QUERY_PARAMETERS, (Serializable) queryParameters); getCommandAPI().execute(EXECUTE_BDM_QUERY_COMMAND, parameters); } @Test(expected = BonitaRuntimeException.class) public void should_execute_throw_BonitaRuntimeException_if_query_not_exists() throws Exception { final Map parameters = new HashMap<>(); parameters.put(QUERY_NAME, "unknownQuery"); parameters.put(RETURN_TYPE, EMPLOYEE_QUALIF_CLASSNAME); getCommandAPI().execute(EXECUTE_BDM_QUERY_COMMAND, parameters); } public void addEmployee(final String firstName, final String lastName, final LocalDate birthdate, final String... addresses) throws Exception { final Expression employeeExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewEmployee", createNewEmployeeScriptContent(firstName, lastName, birthdate, addresses), EMPLOYEE_QUALIF_CLASSNAME); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("test", "1.2-alpha"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addBusinessData("myEmployee", EMPLOYEE_QUALIF_CLASSNAME, null); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME).addOperation( new LeftOperandBuilder().createBusinessDataLeftOperand("myEmployee"), OperatorType.ASSIGNMENT, null, null, employeeExpression); final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.done(); final ProcessDefinition definition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, businessUser); final ProcessInstance instance = getProcessAPI().startProcess(definition.getId()); waitForUserTaskAndExecuteIt(instance, "step1", businessUser); checkProcessInstanceIsArchived(instance); disableAndDeleteProcess(definition.getId()); } private String createNewEmployeeScriptContent(final String firstName, final String lastName, LocalDate birthdate, final String... addresses) { final StringBuilder sb = new StringBuilder(); sb.append("import "); sb.append(EMPLOYEE_QUALIF_CLASSNAME); sb.append("\n"); sb.append("import "); sb.append(ADDRESS_QUALIF_CLASSNAME); sb.append("\n"); sb.append("BonitaEmployee e = new BonitaEmployee();"); sb.append("\n"); sb.append("e.firstName ="); sb.append("'" + firstName + "'"); sb.append("\n"); sb.append("e.lastName ="); sb.append("'" + lastName + "'"); sb.append("\n"); sb.append("e.birthdate = "); if (birthdate != null) { sb.append("java.time.LocalDate.parse(\"" + birthdate.toString() + "\")"); } else { sb.append("null"); } sb.append("\n"); if (addresses != null) { for (int i = 0; i < addresses.length; i++) { final String addressVar = "a" + String.valueOf(i); sb.append("BonitaAddress " + addressVar + " = new BonitaAddress();"); sb.append("\n"); sb.append(addressVar + ".street ="); sb.append("'" + addresses[i] + "'"); sb.append("\n"); sb.append("e.addToAddresses(" + addressVar + ")"); sb.append("\n"); } } sb.append("return e;"); return sb.toString(); } private Serializable deserializeSimpleResult(final byte[] result) throws Exception { return new BusinessDataObjectMapper().readValue(result, (Class) Thread.currentThread().getContextClassLoader() .loadClass(EMPLOYEE_QUALIF_CLASSNAME)); } private List deserializeListResult(final byte[] result) throws Exception { return new BusinessDataObjectMapper().readListValue(result, (Class) Thread.currentThread().getContextClassLoader() .loadClass(EMPLOYEE_QUALIF_CLASSNAME)); } private String getJsonContent(final String jsonFileName) throws IOException { return new String(org.apache.commons.io.IOUtils.toByteArray(this.getClass().getResourceAsStream(jsonFileName))); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/ExecutionExceptionCommand.java ================================================ /** * Copyright (C) 2011 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.command; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Matthieu Chaffotte */ public class ExecutionExceptionCommand extends RuntimeCommand { @Override public Serializable execute(final Map parameters, final ServiceAccessor serviceAccessor) throws SCommandParameterizationException, SCommandExecutionException { throw new SCommandExecutionException("fail"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/IntegerCommand.java ================================================ /** * Copyright (C) 2012 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.command; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Matthieu Chaffotte */ public class IntegerCommand extends RuntimeCommand { @Override public Serializable execute(final Map parameters, final ServiceAccessor serviceAccessor) throws SCommandParameterizationException, SCommandExecutionException { return parameters.get("int"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/MultipleStartPointsProcessCommandIT.java ================================================ /** * 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.command; import static org.bonitasoft.engine.command.helper.designer.Transition.fails; import static org.bonitasoft.engine.command.helper.designer.Transition.meet; import java.io.Serializable; 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 org.assertj.core.api.Assertions; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.contract.Type; import org.bonitasoft.engine.bpm.document.DocumentValue; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.command.helper.ProcessDeployer; import org.bonitasoft.engine.command.helper.designer.BoundaryEvent; import org.bonitasoft.engine.command.helper.designer.Branch; import org.bonitasoft.engine.command.helper.designer.Gateway; import org.bonitasoft.engine.command.helper.designer.Signal; import org.bonitasoft.engine.command.helper.designer.SimpleProcessDesigner; import org.bonitasoft.engine.command.helper.designer.UserTask; import org.bonitasoft.engine.command.helper.expectation.TestUtils; import org.bonitasoft.engine.connectors.TestConnectorWithOutput; import org.bonitasoft.engine.connectors.VariableStorage; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.junit.After; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class MultipleStartPointsProcessCommandIT extends TestWithUser { private static final String CONNECTOR_OUTPUT_NAME = "output1"; private static final String CONNECTOR_INPUT_NAME = "input1"; private static final String CONNECTOR_WITH_OUTPUT_ID = "org.bonitasoft.connector.testConnectorWithOutput"; private final SimpleProcessDesigner designer = new SimpleProcessDesigner(getProcessDefinitionBuilder()); private final TestUtils wrapper = new TestUtils(this); private final ProcessDeployer processDeployer = getProcessDeployer(); @After public void afterTest() throws BonitaException { processDeployer.clean(); VariableStorage.clearAll(); } @Test public void should_start_a_sequential_process() throws Exception { final ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1")) .then(new UserTask("step 2")) .then(new UserTask("step 3")) .end()); final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), Collections.singletonList("step 2")); process.expect("step 2").toBeReady(); process.expect("start", "step 1").toNotHaveArchives(); } @Test public void should_start_a_sequential_process_with_variables() throws Exception { final ProcessDefinitionBuilder builder = getProcessDefinitionBuilder(); builder.addShortTextData("variable", new ExpressionBuilder().createConstantStringExpression("default")); final SimpleProcessDesigner designer = new SimpleProcessDesigner(builder); final ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1")) .then(new UserTask("step 2")) .then(new UserTask("step 3")) .end()); final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), Collections.singletonList("step 2"), Collections.singletonList(createSetDataOperation("variable", "Done!")), Collections. emptyMap()); process.expectVariable("variable").toBe("Done!"); } @Test public void should_be_able_to_start_a_sequential_process_with_a_document() throws Exception { final ProcessDefinitionBuilder builder = getProcessDefinitionBuilder(); builder.addDocumentDefinition("document"); final ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1")) .then(new UserTask("step 2")) .then(new UserTask("step 3")) .end()); final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), Collections.singletonList("step 2"), Collections.singletonList(new OperationBuilder().createSetDocument("document", new ExpressionBuilder().createInputExpression("value", DocumentValue.class.getName()))), Collections. singletonMap( "value", new DocumentValue("content".getBytes(), "plain/text", "document"))); process.expectDocument("document").toBe("content"); } @Test public void should_be_able_to_start_a_process_before_a_parallel_merge() throws Exception { ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new Gateway("split", GatewayType.PARALLEL)) .then(new UserTask("step 1"), new UserTask("step 2")) .then(new Gateway("merge", GatewayType.PARALLEL)) .then(new UserTask("step 3")) .end()); TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), Arrays.asList("step 1", "step 2")); process.execute(user, "step 1", "step 2"); process.execute(user, "step 3"); process.isExpected().toFinish(); process.expect("start", "split").toNotHaveArchives(); process.expect("merge").toBeExecuted(1); } @Test public void should_be_able_to_start_a_process_before_consecutive_parallel_merges() throws Exception { ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then( new UserTask("step 1"), new Branch().start(new Gateway("parallel 1", GatewayType.PARALLEL)) .then(new UserTask("step 2"), new UserTask("step 3")) .then(new Gateway("parallel 2", GatewayType.PARALLEL))) .then(new Gateway("parallel 3", GatewayType.PARALLEL)) .then(new UserTask("step 4")) .end()); TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), Arrays.asList("step 1", "step 2", "step 3")); process.execute(user, "step 1", "step 2", "step 3", "step 4"); process.isExpected().toFinish(); process.expect("step 1", "step 2", "step 3", "step 4").toBeExecuted(1); process.expect("start", "parallel 1").toNotHaveArchives(); } @Test public void should_be_able_to_start_a_process_before_a_parallel_split() throws Exception { final ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1")) .then(new Gateway("split", GatewayType.PARALLEL)) .then(new UserTask("step 2"), new UserTask("step 3")) .end()); final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), Collections.singletonList("step 1")); process.execute(user, "step 1", "step 2", "step 3"); process.isExpected().toFinish(); process.expect("start").toNotHaveArchives(); } @Test public void should_be_able_to_start_a_process_before_an_exclusive_merge_with_only_one_active_branch() throws Exception { final ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1"), new UserTask("step 2")) .then(new Gateway("exclusive", GatewayType.EXCLUSIVE)) .then(new UserTask("step 3")) .end()); final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), Collections.singletonList("step 2")); process.execute(user, "step 2", "step 3"); process.isExpected().toFinish(); process.expect("start").toNotHaveArchives(); process.expect("step 3").toBeExecuted(1); } @Test public void should_be_able_to_start_a_process_before_an_exclusive_merge_with_execute_all_incoming_branches() throws Exception { ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1"), new UserTask("step 2")) .then(new Gateway("exclusive", GatewayType.EXCLUSIVE)) .then(new UserTask("step 3")) .end()); TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), Arrays.asList("step 1", "step 2")); process.execute(user, "step 1", "step 2", "step 3", "step 3"); process.isExpected().toFinish(); process.expect("start").toNotHaveArchives(); process.expect("step 3").toBeExecuted(2); } @Test public void should_be_able_to_start_a_process_before_an_exclusive_merge_with_conditions() throws Exception { Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true); ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1"), new UserTask("step 2")) .then(new Gateway("exclusive", GatewayType.EXCLUSIVE) .when("step 1", fails().toMeet(condition).goingTo(new UserTask("step 3"))) .when("step 2", meet(condition).otherwise(new UserTask("step 4")))) .then(new UserTask("step 5")) .end()); TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), Arrays.asList("step 1", "step 2")); process.execute(user, "step 1", "step 2", "step 3", "step 5"); process.isExpected().toFinish(); process.expect("start").toNotHaveArchives(); process.expect("step 3", "step 5").toBeExecuted(1); } @Test public void should_be_able_to_start_a_process_before_an_exclusive_split() throws Exception { final Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true); final ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1")) .then(new Gateway("exclusive", GatewayType.EXCLUSIVE)) .then( new UserTask("step 2").when("exclusive", fails()), new UserTask("step 3").when("exclusive", meet(condition))) .end()); final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), Collections.singletonList("step 1")); process.execute(user, "step 1", "step 3"); process.isExpected().toFinish(); process.expect("start").toNotHaveArchives(); process.expect("step 3").toBeExecuted(1); } @Test public void should_be_able_to_start_a_process_before_an_inclusive() throws Exception { final ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1")) .then(new Gateway("inclusive 1", GatewayType.INCLUSIVE)) .then(new UserTask("step 2"), new UserTask("step 3")) .then(new Gateway("inclusive 2", GatewayType.INCLUSIVE)) .then(new UserTask("step 4")) .end()); final TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), Collections.singletonList("step 1")); process.execute(user, "step 1", "step 2", "step 3", "step 4"); process.isExpected().toFinish(); process.expect("start").toNotHaveArchives(); } @Test public void should_be_able_to_start_before_an_inclusive_merge_with_all_incoming_branches() throws Exception { ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1")) .then(new Gateway("inclusive 1", GatewayType.INCLUSIVE)) .then(new UserTask("step 2"), new UserTask("step 3")) .then(new Gateway("inclusive 2", GatewayType.INCLUSIVE)) .then(new UserTask("step 4")) .end()); TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), Arrays.asList("step 2", "step 3")); process.execute(user, "step 2", "step 3", "step 4"); process.isExpected().toFinish(); process.expect("step 1").toNotHaveArchives(); process.expect("step 4").toBeExecuted(1); } @Test public void should_be_able_to_start_before_an_inclusive_with_sub_set_of_incoming_branches() throws Exception { ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1")) .then(new Gateway("inclusive 1", GatewayType.INCLUSIVE)) .then(new UserTask("step 2"), new UserTask("step 3")) .then(new Gateway("inclusive 2", GatewayType.INCLUSIVE)) .then(new UserTask("step 4")) .end()); TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), Collections.singletonList("step 2")); process.execute(user, "step 2", "step 4"); process.isExpected().toFinish(); process.expect("step 1").toNotHaveArchives(); process.expect("step 4").toBeExecuted(1); } @Test public void should_be_able_to_start_an_inclusive_with_boundary() throws Exception { ProcessDefinition processDefinition = processDeployer.deploy(designer .start() .then(new UserTask("step 1")) .then(new Gateway("inclusive 1", GatewayType.INCLUSIVE)) .then( new UserTask("step 2"), new UserTask("step 3").with( new BoundaryEvent("boundary", new UserTask("step 4")).triggeredBy(new Signal("foo")))) .then(new Gateway("inclusive 2", GatewayType.INCLUSIVE)) .then(new UserTask("step 5")) .end()); TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), Arrays.asList("step 2", "step 3")); process.expect("step 3").toBeReady(); process.execute(user, "step 2"); process.sendSignal("foo"); process.execute(user, "step 4", "step 5"); process.isExpected().toFinish(); process.expect("step 2", "step 4", "step 5").toBeExecuted(1); process.expect("step 3").toBeAborted(); } private Operation createSetDataOperation(final String name, final String value) throws InvalidExpressionException { return new OperationBuilder().createSetDataOperation(name, new ExpressionBuilder().createConstantStringExpression(value)); } private ProcessDefinitionBuilder getProcessDefinitionBuilder() { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); builder.createNewInstance("Designed by designer", "1.0") .addActor("actor") .addDescription("Coding all-night-long"); return builder; } private TestUtils.Process startProcess(final long startedBy, final long processDefinitionId, final List activityNames) throws Exception { return startProcess(startedBy, processDefinitionId, activityNames, null, null); } private TestUtils.Process startProcess(final long startedBy, final long processDefinitionId, final List activityNames, final List operations, final Map context) throws Exception { return startProcess(startedBy, processDefinitionId, activityNames, operations, context, null); } private TestUtils.Process startProcess(final long startedBy, final long processDefinitionId, final List activityNames, final List operations, final Map context, Map processContractInputs) throws Exception { final Map parameters = new HashMap<>(); parameters.put("started_by", startedBy); parameters.put("process_definition_id", processDefinitionId); parameters.put("activity_names", new ArrayList<>(activityNames)); if (operations != null) { parameters.put("operations", new ArrayList<>(operations)); } if (context != null) { parameters.put("context", new HashMap<>(context)); } if (processContractInputs != null) { parameters.put("process_contract_inputs", new HashMap<>(processContractInputs)); } return wrapper.wrap((ProcessInstance) getCommandAPI().execute("multipleStartPointsProcessCommand", parameters)); } private ProcessDeployer getProcessDeployer() { return new ProcessDeployer() { @Override public ProcessDefinition deploy(final DesignProcessDefinition design) throws BonitaException { return deployAndEnableProcessWithActor(design, "actor", user); } @Override public void clean(final ProcessDefinition processDefinition) throws BonitaException { disableAndDeleteProcess(processDefinition); } }; } @Test public void advancedStartProcessCommandWithConnectorOnEnterOnProcess() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", null); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME).addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addConnector("myConnector", CONNECTOR_WITH_OUTPUT_ID, "1.0", ConnectorEvent.ON_ENTER) .addInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createConstantStringExpression("a")) .addOutput(new LeftOperandBuilder().createNewInstance().setName("outputOfConnector").done(), OperatorType.ASSIGNMENT, "=", "", new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME, String.class.getName())); processDefinitionBuilder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActorAndConnector( processDefinitionBuilder, ACTOR_NAME, user, "TestConnectorWithOutput.impl", TestConnectorWithOutput.class, "TestConnectorWithOutput.jar"); // Start the process with the command on the step2 final Map parametersCommand = new HashMap<>(); parametersCommand.put("started_by", user.getId()); parametersCommand.put("process_definition_id", processDefinition.getId()); parametersCommand.put("activity_names", new ArrayList<>(Collections.singletonList("step2"))); // command API execution getCommandAPI().execute("multipleStartPointsProcessCommand", parametersCommand); waitForUserTask("step2"); // Clean disableAndDeleteProcess(processDefinition); } @Test public void should_start_process_with_process_contract_input() throws Exception { ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("process1", "1.0"); builder.addContract().addInput("input1", Type.TEXT, "text input"); builder.addContract().addInput("input2", Type.INTEGER, "int input"); builder.addUserTask("user1", "actor").addDisplayName( new ExpressionBuilder().createDataExpression("calculatedTaskName", String.class.getName())); builder.addUserTask("user2", "actor"); builder.addActor("actor", true); builder.addLongTextData("calculatedTaskName", null); ProcessDefinition processDefinition = processDeployer.deploy(builder.done()); Map processContractInputs = new HashMap<>(); processContractInputs.put("input1", "the Input1 value"); processContractInputs.put("input2", 145325); List operations = Collections .singletonList(new OperationBuilder().createSetDataOperation("calculatedTaskName", new ExpressionBuilder().createContractInputExpression("input1", String.class.getName()))); TestUtils.Process process = startProcess(user.getId(), processDefinition.getId(), Collections.singletonList("user1"), operations, null, processContractInputs); long user1 = waitForUserTask("user1"); String taskDisplayName = getProcessAPI().getFlowNodeInstance(user1).getDisplayName(); Assertions.assertThat(taskDisplayName).isEqualTo("the Input1 value"); getProcessAPI().assignUserTask(user1, user.getId()); getProcessAPI().executeUserTask(user1, Collections. emptyMap()); process.isExpected().toFinish(); process.expect("user1").toBeExecuted(1); process.expect("user2").toNotHaveArchives(); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/NPECommand.java ================================================ /** * 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.command; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.service.ServiceAccessor; public class NPECommand extends RuntimeCommand { public Serializable execute(Map parameters, ServiceAccessor serviceAccessor) throws SCommandParameterizationException, SCommandExecutionException { throw new NullPointerException(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/ParameterizationExceptionCommand.java ================================================ /** * Copyright (C) 2012 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.command; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Matthieu Chaffotte */ public class ParameterizationExceptionCommand extends RuntimeCommand { @Override public Serializable execute(final Map parameters, final ServiceAccessor serviceAccessor) throws SCommandParameterizationException, SCommandExecutionException { throw new SCommandParameterizationException("parameters are null"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/helper/designer/BranchTest.java ================================================ /** * 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.command.helper.designer; import static org.mockito.Mockito.verify; import java.util.Arrays; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Vincent Elcrin */ @RunWith(MockitoJUnitRunner.class) public class BranchTest { @Mock ProcessDefinitionBuilder builder; @Test public void bind_should_link_first_fragment_of_the_branch() { Branch branch = new Branch().start(new UserTask("2")).then(new UserTask("3")); branch.bind(Arrays. asList(new UserTask("1")), builder); verify(builder).addTransition("1", "2"); } @Test public void bind_should_link_last_fragment_of_the_branch() { Branch branch = new Branch().start(new UserTask("1")).then(new UserTask("2")); UserTask task = new UserTask("3"); task.bind(Arrays. asList(branch), builder); verify(builder).addTransition("2", "3"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/helper/designer/DesignerTestUtils.java ================================================ /** * 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.command.helper.designer; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.bpm.flownode.ActivityDefinition; import org.bonitasoft.engine.bpm.flownode.GatewayDefinition; import org.bonitasoft.engine.bpm.flownode.TransitionDefinition; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; /** * @author Vincent Elcrin */ public class DesignerTestUtils { interface Stringifier { String stringify(O object); } public static String stringify(Collection items, Stringifier stringifier) { List strings = new ArrayList<>(items.size()); for (O item : items) { strings.add(stringifier.stringify(item)); } Collections.sort(strings); return strings.toString(); } public static String getGateways(final DesignProcessDefinition design) { return stringify(design.getFlowElementContainer().getGatewaysList(), new Stringifier() { @Override public String stringify(GatewayDefinition gateway) { String text = gateway.getName(); if (gateway.getDefaultTransition() != null) { text += " (" + getTransitionName(gateway.getDefaultTransition(), design) + ")"; } return text; } }); } public static String getActivities(final DesignProcessDefinition design) { return stringify(design.getFlowElementContainer().getActivities(), new Stringifier() { @Override public String stringify(ActivityDefinition activity) { String text = activity.getName(); if (activity.getDefaultTransition() != null) { text += " (" + getTransitionName(activity.getDefaultTransition(), design) + ")"; } return text; } }); } public static String getTransitions(final DesignProcessDefinition design) { return stringify(design.getFlowElementContainer().getTransitions(), new Stringifier() { @Override public String stringify(TransitionDefinition transition) { String text = getTransitionName(transition, design); if (transition.getCondition() != null) { text += " (" + transition.getCondition().getName() + ")"; } return text; } }); } private static String getTransitionName(TransitionDefinition transition, DesignProcessDefinition design) { String text = design.getFlowElementContainer().getFlowNode(transition.getSource()).getName(); text += "_->_"; text += design.getFlowElementContainer().getFlowNode(transition.getTarget()).getName(); return text; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/command/helper/designer/SimpleProcessDesignerTest.java ================================================ /** * 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.command.helper.designer; import static org.bonitasoft.engine.command.helper.designer.Transition.fails; import static org.bonitasoft.engine.command.helper.designer.Transition.meet; import static org.junit.Assert.assertEquals; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.junit.Test; /** * Created by Vincent Elcrin * Date: 16/12/13 * Time: 18:44 */ public class SimpleProcessDesignerTest { ProcessDefinitionBuilder builder = createProcessDefinitionBuilder(); @Test public void should_be_able_to_design_a_linear_process() throws Exception { DesignProcessDefinition design = new SimpleProcessDesigner(builder) .startWith(new StartEvent("A")) .then(new UserTask("B")) .then(new UserTask("C")) .then(new EndEvent("D")) .done(); assertEquals("[B, C]", DesignerTestUtils.getActivities(design)); assertEquals("[A_->_B, B_->_C, C_->_D]", DesignerTestUtils.getTransitions(design)); } @Test public void should_be_able_to_design_a_process_with_a_gate() throws Exception { DesignProcessDefinition design = new SimpleProcessDesigner(builder) .startWith(new StartEvent("A")) .then(new UserTask("B")) .then(new Gateway("C", GatewayType.EXCLUSIVE)) .then(new UserTask("D")) .then(new EndEvent("E")) .done(); assertEquals("[B, D]", DesignerTestUtils.getActivities(design)); assertEquals("[C]", DesignerTestUtils.getGateways(design)); assertEquals("[A_->_B, B_->_C, C_->_D, D_->_E]", DesignerTestUtils.getTransitions(design)); } @Test public void should_be_able_to_design_a_process_with_parallel_activities() throws Exception { DesignProcessDefinition design = new SimpleProcessDesigner(builder) .startWith(new StartEvent("A")) .then(new UserTask("B1"), new UserTask("B2")) .then(new EndEvent("C")) .done(); assertEquals("[B1, B2]", DesignerTestUtils.getActivities(design)); assertEquals("[A_->_B1, A_->_B2, B1_->_C, B2_->_C]", DesignerTestUtils.getTransitions(design)); } @Test public void should_be_able_to_design_a_process_multiple_incoming_conditions() throws Exception { Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true); DesignProcessDefinition design = new SimpleProcessDesigner(builder) .startWith(new StartEvent("A")) .then(new UserTask("B")) .then(new UserTask("C1"), new UserTask("C2")) .then(new Gateway("D", GatewayType.EXCLUSIVE) .when("C1", fails()) .when("C2", meet(condition))) .then(new EndEvent("F")) .done(); assertEquals("[B, C1 (C1_->_D), C2]", DesignerTestUtils.getActivities(design)); assertEquals("[D]", DesignerTestUtils.getGateways(design)); assertEquals("[A_->_B, B_->_C1, B_->_C2, C1_->_D, C2_->_D (true), D_->_F]", DesignerTestUtils.getTransitions(design)); } @Test public void should_be_able_to_design_a_process_with_multiple_outgoing_conditions() throws Exception { Expression condition = new ExpressionBuilder().createConstantBooleanExpression(true); DesignProcessDefinition design = new SimpleProcessDesigner(builder) .startWith(new StartEvent("A")) .then(new UserTask("B")) .then(new Gateway("C", GatewayType.EXCLUSIVE)) .then( new UserTask("D1").when("C", fails()), new UserTask("D2").when("C", meet(condition))) .then(new EndEvent("E")) .done(); assertEquals("[B, D1, D2]", DesignerTestUtils.getActivities(design)); assertEquals("[C (C_->_D1)]", DesignerTestUtils.getGateways(design)); assertEquals("[A_->_B, B_->_C, C_->_D1, C_->_D2 (true), D1_->_E, D2_->_E]", DesignerTestUtils.getTransitions(design)); } @Test public void should_be_able_to_design_a_process_with_complex_branch() throws Exception { DesignProcessDefinition design = new SimpleProcessDesigner(builder) .startWith(new StartEvent("A")) .then(new UserTask("B")) .then(new Gateway("C", GatewayType.PARALLEL)) .then( new Branch().start(new UserTask("D1")).then(new UserTask("D2")), new UserTask("E")) .then(new Gateway("F", GatewayType.PARALLEL)) .then(new EndEvent("G")) .done(); assertEquals("[B, D1, D2, E]", DesignerTestUtils.getActivities(design)); assertEquals("[C, F]", DesignerTestUtils.getGateways(design)); assertEquals("[A_->_B, B_->_C, C_->_D1, C_->_E, D1_->_D2, D2_->_F, E_->_F, F_->_G]", DesignerTestUtils.getTransitions(design)); } @Test public void should_be_able_to_design_a_process_with_complex_() throws Exception { DesignProcessDefinition design = new SimpleProcessDesigner(builder) .startWith(new StartEvent("A")) .then( new UserTask("B"), new Branch() .start(new Gateway("C", GatewayType.PARALLEL)) .then(new UserTask("D"), new UserTask("E")) .then(new Gateway("F", GatewayType.PARALLEL))) .then(new Gateway("G", GatewayType.PARALLEL)) .then(new UserTask("H")) .then(new EndEvent("I")) .done(); assertEquals("[A_->_B, A_->_C, B_->_G, C_->_D, C_->_E, D_->_F, E_->_F, F_->_G, G_->_H, H_->_I]", DesignerTestUtils.getTransitions(design)); } private ProcessDefinitionBuilder createProcessDefinitionBuilder() { ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); builder.createNewInstance("Designed by designer", "1.0"); return builder; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/connectors/RemoteConnectorExecutionIT.java ================================================ /** * 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.connectors; import static org.assertj.core.api.Assertions.tuple; import static org.bonitasoft.engine.test.BuildTestUtil.generateConnectorImplementation; import static org.junit.Assert.*; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.connector.ArchiveConnectorInstancesSearchDescriptor; import org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance; import org.bonitasoft.engine.bpm.connector.ConnectorCriterion; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor; import org.bonitasoft.engine.bpm.connector.ConnectorInstance; import org.bonitasoft.engine.bpm.connector.ConnectorInstancesSearchDescriptor; import org.bonitasoft.engine.bpm.connector.ConnectorState; import org.bonitasoft.engine.bpm.connector.FailAction; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.document.DocumentsSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ActivityDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.BoundaryEventDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder; import org.bonitasoft.engine.bpm.process.impl.ConnectorDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ReceiveTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.SendTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.operation.LeftOperand; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.test.TestStates; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @SuppressWarnings("javadoc") public class RemoteConnectorExecutionIT extends ConnectorExecutionIT { @Rule public ExpectedException expectedException = ExpectedException.none(); private static final String CONNECTOR_OUTPUT_NAME = "output1"; private static final String CONNECTOR_INPUT_NAME = "input1"; private static final String CONNECTOR_WITH_OUTPUT_ID = "org.bonitasoft.connector.testConnectorWithOutput"; private static final String FLOWNODE = "flowNode"; private static final String PROCESS = "process"; public static final String TEST_CONNECTOR_THAT_THROW_EXCEPTION_ID = "testConnectorThatThrowException"; @Test public void executeConnectorWithJNDILookupAndAPICall() throws Exception { final Expression localDataExpression = new ExpressionBuilder().createConstantLongExpression(0L); final Expression processNameExpression = new ExpressionBuilder().createConstantStringExpression(PROCESS_NAME); final Expression processVersionExpression = new ExpressionBuilder().createConstantStringExpression("1.0"); final Expression outputExpression = new ExpressionBuilder().createInputExpression("processId", Long.class.getName()); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); designProcessDefinition.addLongData("processId", localDataExpression); designProcessDefinition.addAutomaticTask("start"); designProcessDefinition.addAutomaticTask("step1") .addConnector("myConnector", "org.bonitasoft.engine.connectors.TestConnectorWithAPICall", "1.0", ConnectorEvent.ON_FINISH) .addInput("processName", processNameExpression).addInput("processVersion", processVersionExpression) .addOutput(new OperationBuilder().createSetDataOperation("processId", outputExpression)); designProcessDefinition.addAutomaticTask("end"); designProcessDefinition.addTransition("start", "step1"); designProcessDefinition.addTransition("step1", "end"); final ProcessDefinition processDefinition = deployAndEnableProcessWithTestConnectorWithAPICall( designProcessDefinition); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForProcessToBeInState(processInstance, ProcessInstanceState.COMPLETED); disableAndDeleteProcess(processDefinition); } @Test public void executeConnectorOnFinishOfAnAutomaticActivityWithDataAsOutput() throws Exception { final String valueOfInput1 = "valueOfInput1"; final String defaultValue = "default"; final String dataName = "myData1"; final Expression dataDefaultValue = new ExpressionBuilder().createConstantStringExpression(defaultValue); final Expression input1Expression = new ExpressionBuilder().createConstantStringExpression(valueOfInput1); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "executeConnectorOnFinishOfAnAutomaticActivityWithDataAsOutput", "1.0"); processDefinitionBuilder.addShortTextData(dataName, dataDefaultValue); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step0", ACTOR_NAME); processDefinitionBuilder .addAutomaticTask("step1") .addConnector("myConnector", CONNECTOR_WITH_OUTPUT_ID, "1.0", ConnectorEvent.ON_FINISH) .addInput(CONNECTOR_INPUT_NAME, input1Expression) .addOutput(new LeftOperandBuilder().createNewInstance().setName(dataName).done(), OperatorType.ASSIGNMENT, "=", "", new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME, String.class.getName())); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addTransition("step0", "step1"); processDefinitionBuilder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithOutput( processDefinitionBuilder, ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); assertEquals(defaultValue, getProcessAPI().getProcessDataInstance(dataName, startProcess.getId()).getValue()); waitForUserTaskAndExecuteIt(startProcess, "step0", user); waitForUserTask(startProcess, "step2"); assertEquals(valueOfInput1, getProcessAPI().getProcessDataInstance(dataName, startProcess.getId()).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void executeConnectorOnFinishOfMultiInstancedActivity() throws Exception { // deploy the process final String globalDataName = "globalData"; final String userTaskName = "step2"; final String multiTaskName = "multi"; final ProcessDefinition processDefinition = deployProcessWithConnectorOnMutiInstance(globalDataName, userTaskName, multiTaskName, true, 2, ConnectorEvent.ON_FINISH); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // wait for the first Multi-instance and execute it: the connector must be executed waitForUserTaskAndExecuteIt(processInstance, multiTaskName, user); // wait for the second Multi-instance final long multiInstanceId = waitForUserTask(processInstance, multiTaskName); // check the data value DataInstance globalData = getProcessAPI().getProcessDataInstance(globalDataName, processInstance.getId()); assertEquals(1L, globalData.getValue()); // execute the second Multi-instance: the connector must be executed again assignAndExecuteStep(multiInstanceId, userId); // wait for user task that follows the multi task waitForUserTask(processInstance, userTaskName); // check the data value globalData = getProcessAPI().getProcessDataInstance(globalDataName, processInstance.getId()); assertEquals(2L, globalData.getValue()); // delete process disableAndDeleteProcess(processDefinition); } private ProcessDefinition deployProcessWithConnectorOnMutiInstance(final String globalDataName, final String userTaskName, final String multiTaskName, final boolean isSequential, final int nbOfInstances, final ConnectorEvent activationEvent) throws Exception { final String localDataName = "localData"; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("connectorOnMultiInstance", "1.0"); builder.addActor(ACTOR_NAME); builder.addLongData(globalDataName, new ExpressionBuilder().createConstantLongExpression(0L)); builder.addStartEvent("start"); final UserTaskDefinitionBuilder taskBuilder = builder.addUserTask(multiTaskName, ACTOR_NAME); taskBuilder.addMultiInstance(isSequential, new ExpressionBuilder().createConstantIntegerExpression(nbOfInstances)); taskBuilder.addLongData(localDataName, new ExpressionBuilder().createConstantLongExpression(1L)); taskBuilder .addConnector("connector", "connectorInJar", "1.0.0", activationEvent) .addInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createDataExpression(localDataName, Long.class.getName())) .addOutput( new LeftOperandBuilder().createDataLeftOperand(globalDataName), OperatorType.ASSIGNMENT, "=", "", new ExpressionBuilder().createGroovyScriptExpression("Script", globalDataName + " + 1", Long.class.getName(), new ExpressionBuilder().createDataExpression(globalDataName, Long.class.getName()))); // final UserTaskDefinitionBuilder taskBuilder = builder.addUserTask(multiTaskName, ACTOR_NAME); // taskBuilder.addMultiInstance(isSequential, new ExpressionBuilder().createConstantIntegerExpression(nbOfInstances)); // taskBuilder.addLongData(localDataName, new ExpressionBuilder().createConstantLongExpression(1L)); // taskBuilder // .addConnector("connector", CONNECTOR_WITH_OUTPUT_ID, "1.0", activationEvent) // .addSimpleInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createDataExpression(localDataName, Long.class.getName())) // .addOutput( // new LeftOperandBuilder().createDataLeftOperand(globalDataName), // OperatorType.ASSIGNMENT, "=", "", // new ExpressionBuilder().createGroovyScriptExpression("Script", globalDataName + " + 1", Long.class.getName(), // new ExpressionBuilder().createDataExpression(globalDataName, Long.class.getName()))); builder.addUserTask(userTaskName, ACTOR_NAME); builder.addEndEvent("end"); builder.addTransition("start", multiTaskName); builder.addTransition(multiTaskName, userTaskName); builder.addTransition(userTaskName, "end"); final List resources = new ArrayList<>(); addResource(resources, "/org/bonitasoft/engine/connectors/TestConnectorInJar.impl", "TestConnectorInJar.impl"); addResource(resources, "/org/bonitasoft/engine/connectors/connector-in-jar.jar.bak", "connector-in-jar.jar"); final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchiveBuilder.addConnectorImplementation(resources.get(0)); businessArchiveBuilder.addClasspathResource(resources.get(1)); businessArchiveBuilder.setProcessDefinition(builder.done()); return deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, user); } @Test public void executeConnectorMultipleConnectorsOnOneActivity() throws Exception { final String defaultValue = "a"; final String dataName = "myData1"; final Expression dataDefaultValue = new ExpressionBuilder().createConstantStringExpression(defaultValue); final Expression input1Expression = new ExpressionBuilder().createConstantStringExpression("a"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "executeConnectorOnFinishOfAnAutomaticActivityWithDataAsOutput", "1.0"); processDefinitionBuilder.addShortTextData(dataName, dataDefaultValue); processDefinitionBuilder.addActor(ACTOR_NAME); final String inputName = CONNECTOR_INPUT_NAME; processDefinitionBuilder.addUserTask("step0", ACTOR_NAME); final AutomaticTaskDefinitionBuilder addAutomaticTask = processDefinitionBuilder.addAutomaticTask("step1"); final int nbOfConnectors = 25; for (int i = 0; i < nbOfConnectors; i++) { addAutomaticTask .addConnector("myConnector" + i, CONNECTOR_WITH_OUTPUT_ID, "1.0", ConnectorEvent.ON_FINISH) .addInput(inputName, input1Expression) .addOutput( new LeftOperandBuilder().createNewInstance().setName(dataName).done(), OperatorType.ASSIGNMENT, "=", "", new ExpressionBuilder().createGroovyScriptExpression("concat", "myData1+output1", String.class.getName(), new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME, String.class.getName()), new ExpressionBuilder().createDataExpression("myData1", String.class.getName()))); } processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addTransition("step0", "step1"); processDefinitionBuilder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithOutput( processDefinitionBuilder, ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); assertEquals(defaultValue, getProcessAPI().getProcessDataInstance(dataName, startProcess.getId()).getValue()); waitForUserTaskAndExecuteIt(startProcess, "step0", user); waitForUserTask(startProcess, "step2"); final String value = (String) getProcessAPI().getProcessDataInstance(dataName, startProcess.getId()).getValue(); assertEquals(nbOfConnectors + 1, value.length()); for (int i = 0; i < value.length(); i++) { assertEquals('a', value.charAt(i)); } disableAndDeleteProcess(processDefinition); } @Test public void redeployProcessWithNoConnectorImplem() throws Exception { final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnActivityInstance", "1.0"); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addUserTask("step0", ACTOR_NAME); final ProcessDefinition processDefinition1 = deployProcessWithExternalTestConnector(processDefBuilder, ACTOR_NAME, user); List connectorImplems = getProcessAPI() .getConnectorImplementations(processDefinition1.getId(), 0, 100, null); assertEquals(1, getProcessAPI().getNumberOfConnectorImplementations(processDefinition1.getId())); assertEquals(1, connectorImplems.size()); disableAndDeleteProcess(processDefinition1); // redeploy same process: final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, user); connectorImplems = getProcessAPI().getConnectorImplementations(processDefinition2.getId(), 0, 100, null); assertEquals(0, connectorImplems.size()); disableAndDeleteProcess(processDefinition2); } @Test public void executeConnectorOnProcessDefinition() throws Exception { final String valueOfInput1 = "Lily"; final String valueOfInput2 = "Lucy"; final String mainExpContent = "'welcome '+valueOfInput1+' and '+valueOfInput2"; final String inputName1 = "valueOfInput1"; final String inputName2 = "valueOfInput2"; final String mainInputName1 = "param1"; final String resContent = "welcome Lily and Lucy"; // Input expression final Expression input1Expression = new ExpressionBuilder().createInputExpression(inputName1, String.class.getName()); final Expression input2Expression = new ExpressionBuilder().createInputExpression(inputName2, String.class.getName()); // Main Expression final Expression mainExp = new ExpressionBuilder().createExpression(mainInputName1, mainExpContent, ExpressionType.TYPE_READ_ONLY_SCRIPT.toString(), String.class.getName(), "GROOVY", Arrays.asList(input1Expression, input2Expression)); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnActivityInstance", "1.0"); final String actorName = "Isabelle Adjani"; designProcessDefinition.addActor(actorName); final ProcessDefinition processDefinition = deployProcessWithExternalTestConnector(designProcessDefinition, actorName, user); final Map connectorInputParameters = getConnectorInputParameters(mainInputName1, mainExp); final Map> inputValues = getInputValues(mainInputName1, Arrays.asList(inputName1, inputName2), Arrays.asList(valueOfInput1, valueOfInput2)); final Map res = getProcessAPI().executeConnectorOnProcessDefinition( ConnectorExecutionIT.DEFAULT_EXTERNAL_CONNECTOR_ID, ConnectorExecutionIT.DEFAULT_EXTERNAL_CONNECTOR_VERSION, connectorInputParameters, inputValues, processDefinition.getId()); assertEquals(resContent, res.get(mainInputName1)); assertTrue((Boolean) res.get("hasBeenValidated")); disableAndDeleteProcess(processDefinition); } private Map getConnectorInputParameters(final String mainName, final Expression mainExp) { final Map connectorInputParameters = new HashMap<>(); connectorInputParameters.put(mainName, mainExp); return connectorInputParameters; } private Map> getInputValues(final String mainName, final List names, final List vars) { final Map> inputValues = new HashMap<>(); final Map values = new HashMap<>(); if (names != null && !names.isEmpty() && vars != null && !vars.isEmpty() && names.size() == vars.size()) { for (int i = 0; i < names.size(); i++) { values.put(names.get(i), vars.get(i)); } } inputValues.put(mainName, values); return inputValues; } @Test public void executeConnectorInJar() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("testConnectorWithExecutionTooLong", "1.0"); designProcessDefinition.addActor(ACTOR_NAME); designProcessDefinition.addAutomaticTask("step1").addConnector("myConnector1", "connectorInJar", "1.0.0", ConnectorEvent.ON_ENTER); final List resources = new ArrayList<>(); addResource(resources, "/org/bonitasoft/engine/connectors/TestConnectorInJar.impl", "TestConnectorInJar.impl"); addResource(resources, "/org/bonitasoft/engine/connectors/connector-in-jar.jar.bak", "connector-in-jar.jar"); final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchiveBuilder.addConnectorImplementation(resources.get(0)); businessArchiveBuilder.addClasspathResource(resources.get(1)); businessArchiveBuilder.setProcessDefinition(designProcessDefinition.done()); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, user); final ProcessInstance process = getProcessAPI().startProcess(processDefinition.getId()); waitForProcessToFinish(process); disableAndDeleteProcess(processDefinition); } @Test public void getNumberOfConnectorImplementationsWhenProcessDoesNotExists() { assertEquals(0, getProcessAPI().getNumberOfConnectorImplementations(123L)); } @Test public void getConnectorImplementations() throws Exception { // connector information final String connectorId = "org.bonitasoft.connector.testConnector"; final Expression input1Expression = new ExpressionBuilder().createConstantStringExpression("valueOfInput"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addAutomaticTask("step1") .addConnector("myConnector", connectorId, "1.0", ConnectorEvent.ON_ENTER) .addInput(CONNECTOR_INPUT_NAME, input1Expression); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addTransition("step1", "step2"); final List connectorImplementations = Arrays.asList( BuildTestUtil.getContentAndBuildBarResource("TestConnector.impl", TestConnector.class), BuildTestUtil.getContentAndBuildBarResource("TestConnectorWithOutput.impl", TestConnectorWithOutput.class), BuildTestUtil.getContentAndBuildBarResource("TestConnector3.impl", TestConnector3.class)); final List generateConnectorDependencies = Arrays.asList( BuildTestUtil.generateJarAndBuildBarResource(TestConnector.class, "TestConnector.jar"), BuildTestUtil.generateJarAndBuildBarResource(VariableStorage.class, "VariableStorage.jar")); final ProcessDefinition processDefinition = deployAndEnableProcessWithActorAndConnectorAndParameter( processDefinitionBuilder, ACTOR_NAME, user, connectorImplementations, generateConnectorDependencies, null); final long processDefinitionId = processDefinition.getId(); assertEquals(3, getProcessAPI().getNumberOfConnectorImplementations(processDefinitionId)); // test ASC List connectorImplementationDescriptors = getProcessAPI() .getConnectorImplementations(processDefinitionId, 0, 3, ConnectorCriterion.DEFINITION_ID_ASC); assertNotNull(connectorImplementationDescriptors); assertEquals(3, connectorImplementationDescriptors.size()); assertEquals(connectorId, connectorImplementationDescriptors.get(0).getDefinitionId()); assertEquals(CONNECTOR_WITH_OUTPUT_ID, connectorImplementationDescriptors.get(2).getDefinitionId()); connectorImplementationDescriptors = getProcessAPI().getConnectorImplementations(processDefinitionId, 0, 1, ConnectorCriterion.DEFINITION_ID_ASC); assertEquals(1, connectorImplementationDescriptors.size()); assertEquals(connectorId, connectorImplementationDescriptors.get(0).getDefinitionId()); connectorImplementationDescriptors = getProcessAPI().getConnectorImplementations(processDefinitionId, 2, 1, ConnectorCriterion.DEFINITION_ID_ASC); assertEquals(1, connectorImplementationDescriptors.size()); assertEquals(CONNECTOR_WITH_OUTPUT_ID, connectorImplementationDescriptors.get(0).getDefinitionId()); // test DESC connectorImplementationDescriptors = getProcessAPI().getConnectorImplementations(processDefinitionId, 2, 1, ConnectorCriterion.DEFINITION_ID_DESC); assertNotNull(connectorImplementationDescriptors); assertEquals(1, connectorImplementationDescriptors.size()); assertEquals(connectorId, connectorImplementationDescriptors.get(0).getDefinitionId()); disableAndDeleteProcess(processDefinition); } @Test public void longConnectorOutputStoredInProcessVariableShouldThrowANiceException() throws Exception { // connector information final Expression outputOfConnectorExpression = new ExpressionBuilder() .createConstantStringExpression("outputExpression"); final Expression groovyExpression = new ExpressionBuilder().createGroovyScriptExpression("generateLongOutput", "'a'*1000", String.class.getName()); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", outputOfConnectorExpression); processDefinitionBuilder .addAutomaticTask("step1") .addConnector("theConnector", CONNECTOR_WITH_OUTPUT_ID, "1.0", ConnectorEvent.ON_ENTER) .addInput(CONNECTOR_INPUT_NAME, groovyExpression) .addOutput(new LeftOperandBuilder().createNewInstance().setName("outputOfConnector").done(), OperatorType.ASSIGNMENT, "=", "", new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME, String.class.getName())); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithOutput( processDefinitionBuilder, ACTOR_NAME, user); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForTaskToFail(startProcess); disableAndDeleteProcess(processDefinition); } @Test public void executeConnectorThatThrowExceptionFailUserTask() throws Exception { final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter("normal", FailAction.FAIL, false); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForTaskToFail(startProcess); disableAndDeleteProcess(processDefinition); } @Test public void executeConnectorThatThrowExceptionFailAutomaticTaskOnEnter() throws Exception { final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnAutomaticTaskOnEnter( "normal", FailAction.FAIL, false); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForTaskToFail(startProcess); disableAndDeleteProcess(processDefinition); } @Test public void executeConnectorThatThrowExceptionFailAutomaticTaskOnFinish() throws Exception { final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnAutomaticTaskOnFinish( "normal", FailAction.FAIL, false); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForTaskToFail(startProcess); disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionFailPolicyOnTaskInput() throws Exception { final Expression outputOfConnectorExpression = new ExpressionBuilder() .createConstantStringExpression("outputExpression"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", outputOfConnectorExpression); final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER); addConnector.addInput("kind", new ExpressionBuilder().createGroovyScriptExpression("fails", "8/0", String.class.getName())); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException( processDefinitionBuilder, ACTOR_NAME, user); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); final ActivityInstance activityInstance = waitForTaskToFail(startProcess); final SearchOptions searchOptions = getFirst100ConnectorInstanceSearchOptions(activityInstance.getId(), FLOWNODE).done(); final SearchResult connectorInstances = getProcessAPI() .searchConnectorInstances(searchOptions); assertEquals(1, connectorInstances.getCount()); disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionIgnorePolicyOnTaskInput() throws Exception { final Expression outputOfConnectorExpression = new ExpressionBuilder() .createConstantStringExpression("outputExpression"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", outputOfConnectorExpression); final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER); addConnector.addInput("kind", new ExpressionBuilder().createGroovyScriptExpression("fails", "8/0", String.class.getName())); addConnector.ignoreError(); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException( processDefinitionBuilder, ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(startProcess, "step1"); final SearchOptions searchOptions = getFirst100ConnectorInstanceSearchOptions(step1Id, FLOWNODE).done(); final SearchResult connectorInstances = getProcessAPI() .searchConnectorInstances(searchOptions); assertEquals(1, connectorInstances.getCount()); disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionFailPolicyOnProcessInput() throws Exception { final Expression outputOfConnectorExpression = new ExpressionBuilder() .createConstantStringExpression("outputExpression"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", outputOfConnectorExpression); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); final ConnectorDefinitionBuilder addConnector = processDefinitionBuilder.addConnector( "testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER); addConnector.addInput("kind", new ExpressionBuilder().createGroovyScriptExpression("fails", "8/0", String.class.getName())); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException( processDefinitionBuilder, ACTOR_NAME, user); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForProcessToBeInState(startProcess, ProcessInstanceState.ERROR); final SearchOptions searchOptions = getFirst100ConnectorInstanceSearchOptions(startProcess.getId(), PROCESS) .done(); final SearchResult connectorInstances = getProcessAPI() .searchConnectorInstances(searchOptions); assertEquals(1, connectorInstances.getCount()); disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionErrorEventPolicyBoundaryOnTaskInput() throws Exception { final Expression outputOfConnectorExpression = new ExpressionBuilder() .createConstantStringExpression("outputExpression"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", outputOfConnectorExpression); final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask("step1inputfail", ACTOR_NAME); final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER); addConnector.addInput("kind", new ExpressionBuilder().createGroovyScriptExpression("fails", "8/0", String.class.getName())); addConnector.throwErrorEventWhenFailed("error"); final BoundaryEventDefinitionBuilder boundaryEvent = userTaskBuilder.addBoundaryEvent("errorBoundary", true); boundaryEvent.addErrorEventTrigger("error"); processDefinitionBuilder.addUserTask("errorTask", ACTOR_NAME); processDefinitionBuilder.addTransition("errorBoundary", "errorTask"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException( processDefinitionBuilder, ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); // the connector must trigger this exception step waitForUserTaskAndExecuteIt(startProcess, "errorTask", user); waitForProcessToFinish(startProcess); // clean up disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionErrorEventPolicyBoundaryOnAutomaticTask() throws Exception { final Expression outputOfConnectorExpression = new ExpressionBuilder() .createConstantStringExpression("outputExpression"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", outputOfConnectorExpression); final AutomaticTaskDefinitionBuilder taskBuilder = processDefinitionBuilder.addAutomaticTask("step1inputfail"); final ConnectorDefinitionBuilder addConnector = taskBuilder.addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER); addConnector.addInput("kind", new ExpressionBuilder().createGroovyScriptExpression("fails", "8/0", String.class.getName())); addConnector.throwErrorEventWhenFailed("error"); final BoundaryEventDefinitionBuilder boundaryEvent = taskBuilder.addBoundaryEvent("errorBoundary"); boundaryEvent.addErrorEventTrigger("error"); processDefinitionBuilder.addUserTask("errorTask", ACTOR_NAME); processDefinitionBuilder.addTransition("errorBoundary", "errorTask"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException( processDefinitionBuilder, ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); // the connector must trigger this exception step waitForUserTaskAndExecuteIt(startProcess, "errorTask", user); waitForProcessToFinish(startProcess); // clean up disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionErrorEventPolicyBoundaryOnReceiveTask() throws Exception { final Expression outputOfConnectorExpression = new ExpressionBuilder() .createConstantStringExpression("outputExpression"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", outputOfConnectorExpression); final ReceiveTaskDefinitionBuilder taskBuilder = processDefinitionBuilder.addReceiveTask("step1inputfail", "m1"); final ConnectorDefinitionBuilder addConnector = taskBuilder.addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER); addConnector.addInput("kind", new ExpressionBuilder().createGroovyScriptExpression("fails", "8/0", String.class.getName())); addConnector.throwErrorEventWhenFailed("error"); final BoundaryEventDefinitionBuilder boundaryEvent = taskBuilder.addBoundaryEvent("errorBoundary"); boundaryEvent.addErrorEventTrigger("error"); processDefinitionBuilder.addUserTask("errorTask", ACTOR_NAME); processDefinitionBuilder.addTransition("errorBoundary", "errorTask"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException( processDefinitionBuilder, ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); // the connector must trigger this exception step waitForUserTaskAndExecuteIt(startProcess, "errorTask", user); waitForProcessToFinish(startProcess); // clean up disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionErrorEventPolicyBoundaryOnSendTask() throws Exception { final Expression outputOfConnectorExpression = new ExpressionBuilder() .createConstantStringExpression("outputExpression"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", outputOfConnectorExpression); final SendTaskDefinitionBuilder taskBuilder = processDefinitionBuilder.addSendTask("step1inputfail", "m1", new ExpressionBuilder().createConstantStringExpression("p2")); final ConnectorDefinitionBuilder addConnector = taskBuilder.addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER); addConnector.addInput("kind", new ExpressionBuilder().createGroovyScriptExpression("fails", "8/0", String.class.getName())); addConnector.throwErrorEventWhenFailed("error"); final BoundaryEventDefinitionBuilder boundaryEvent = taskBuilder.addBoundaryEvent("errorBoundary"); boundaryEvent.addErrorEventTrigger("error"); processDefinitionBuilder.addUserTask("errorTask", ACTOR_NAME); processDefinitionBuilder.addTransition("errorBoundary", "errorTask"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException( processDefinitionBuilder, ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); // the connector must trigger this exception step waitForUserTaskAndExecuteIt(startProcess, "errorTask", user); waitForProcessToFinish(startProcess); // clean up disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionErrorEventPolicyBoundaryOnTaskOutput() throws Exception { final Expression outputOfConnectorExpression = new ExpressionBuilder() .createConstantStringExpression("outputExpression"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", outputOfConnectorExpression); final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask("step1inputfail", ACTOR_NAME); final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector("myConnector", CONNECTOR_WITH_OUTPUT_ID, "1.0", ConnectorEvent.ON_ENTER); addConnector.addInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createConstantStringExpression("a")); addConnector.addOutput(new LeftOperandBuilder().createNewInstance().setName("outputOfConnector").done(), OperatorType.ASSIGNMENT, "=", "", new ExpressionBuilder().createGroovyScriptExpression("concat", "8/0", String.class.getName())); addConnector.throwErrorEventWhenFailed("error"); final BoundaryEventDefinitionBuilder boundaryEvent = userTaskBuilder.addBoundaryEvent("errorBoundary", true); boundaryEvent.addErrorEventTrigger("error"); processDefinitionBuilder.addUserTask("errorTask", ACTOR_NAME); processDefinitionBuilder.addTransition("errorBoundary", "errorTask"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithOutput( processDefinitionBuilder, ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); // the connector must trigger this exception step waitForUserTaskAndExecuteIt(startProcess, "errorTask", user); waitForProcessToFinish(startProcess); // clean up disableAndDeleteProcess(processDefinition); } @Test public void ignoreErrorConnectorOnBoundaryWhenInputFail() throws Exception { final Expression outputOfConnectorExpression = new ExpressionBuilder() .createConstantStringExpression("outputExpression"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", outputOfConnectorExpression); final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER); addConnector.addInput("kind", new ExpressionBuilder().createGroovyScriptExpression("fails", "8/0", String.class.getName())); addConnector.ignoreError(); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException( processDefinitionBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // the connector must trigger this exception step waitForUserTaskAndExecuteIt(processInstance, "step1", user); waitForProcessToFinish(processInstance); // clean up disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionIgnorePolicyOnTaskOutput() throws Exception { final Expression outputOfConnectorExpression = new ExpressionBuilder() .createConstantStringExpression("outputExpression"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", outputOfConnectorExpression); final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector("myConnector", CONNECTOR_WITH_OUTPUT_ID, "1.0", ConnectorEvent.ON_ENTER); addConnector.addInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createConstantStringExpression("a")); addConnector.addOutput(new LeftOperandBuilder().createNewInstance().setName("outputOfConnector").done(), OperatorType.ASSIGNMENT, "=", "", new ExpressionBuilder().createGroovyScriptExpression("concat", "8/0", String.class.getName())); addConnector.ignoreError(); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithOutput( processDefinitionBuilder, ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); // the connector must trigger this exception step waitForUserTaskAndExecuteIt(startProcess, "step1", user); waitForProcessToFinish(startProcess); // clean up disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionFailPolicyOnTaskOutput() throws Exception { final Expression dataDefaultValue = new ExpressionBuilder().createConstantStringExpression("NaN"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("result", dataDefaultValue); final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector("myConnector", CONNECTOR_WITH_OUTPUT_ID, "1.0", ConnectorEvent.ON_ENTER); addConnector.addInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createConstantStringExpression("a")); addConnector.addOutput(new LeftOperandBuilder().createNewInstance().setName("outputOfConnector").done(), OperatorType.ASSIGNMENT, "=", "", new ExpressionBuilder().createGroovyScriptExpression("concat", "8/0", String.class.getName())); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithOutput( processDefinitionBuilder, ACTOR_NAME, user); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); final ActivityInstance activityInstance = waitForTaskToFail(startProcess); final SearchOptions searchOptions = getFirst100ConnectorInstanceSearchOptions(activityInstance.getId(), FLOWNODE).done(); final SearchResult connectorInstances = getProcessAPI() .searchConnectorInstances(searchOptions); assertEquals(1, connectorInstances.getCount()); disableAndDeleteProcess(processDefinition); } private SearchOptionsBuilder getFirst100ConnectorInstanceSearchOptions(final long containerId, final String containerType) { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 100); searchOptionsBuilder.filter(ConnectorInstancesSearchDescriptor.CONTAINER_ID, containerId); searchOptionsBuilder.filter(ConnectorInstancesSearchDescriptor.CONTAINER_TYPE, containerType); searchOptionsBuilder.filter(ConnectorInstancesSearchDescriptor.STATE, ConnectorState.FAILED.name()); searchOptionsBuilder.sort(ConnectorInstancesSearchDescriptor.NAME, Order.ASC); return searchOptionsBuilder; } @Test public void executeConnectorThatThrowRuntimeExceptionFailUserTaskOnEnter() throws Exception { final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter("runtime", FailAction.FAIL, false); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForTaskToFail(startProcess); disableAndDeleteProcess(processDefinition); } @Test public void executeConnectorThatThrowRuntimeExceptionInConnectFailUserTaskOnEnter() throws Exception { final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter("connect", FailAction.FAIL, false); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForTaskToFail(startProcess); disableAndDeleteProcess(processDefinition); } @Test public void executeConnectorThatThrowRuntimeExceptionInDisconnectFailUserTaskOnEnter() throws Exception { final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter("disconnect", FailAction.FAIL, false); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForTaskToFail(startProcess); disableAndDeleteProcess(processDefinition); } @Test public void executeConnectorThatThrowRuntimeExceptionFailAutomaticTaskOnEnter() throws Exception { final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnAutomaticTaskOnEnter( "runtime", FailAction.FAIL, false); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForTaskToFail(startProcess); disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowRuntimeExceptionFailTask() throws Exception { final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnAutomaticTaskOnFinish( "runtime", FailAction.FAIL, false); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForTaskToFail(startProcess); disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowRuntimeExceptionFailProcess() throws Exception { final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter("runtime", FailAction.FAIL, true); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForProcessToBeInState(startProcess, ProcessInstanceState.ERROR); disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionFailProcess() throws Exception { final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter("normal", FailAction.FAIL, true); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForProcessToBeInState(startProcess, ProcessInstanceState.ERROR); disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionIgnorePolicyOnTask() throws Exception { final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter("normal", FailAction.IGNORE, false); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForUserTask(startProcess, "step1"); disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionIgnorePolicyOnProcess() throws Exception { final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter("normal", FailAction.IGNORE, true); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForUserTask(startProcess, "step1"); disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionErrorEventPolicyWithEventSubProcessOnTask() throws Exception { final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter("normal", FailAction.ERROR_EVENT, false); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForUserTask(startProcess, "errorTask"); disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionErrorEventPolicyWithEventSubProcessOnProcess() throws Exception { final ProcessDefinition processDefinition = getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter("normal", FailAction.ERROR_EVENT, true); final long processDefinitionId = processDefinition.getId(); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinitionId); waitForUserTask(startProcess, "errorTask"); disableAndDeleteProcess(processDefinition); } private ProcessDefinition getProcessWithConnectorThatThrowError(final String exceptionType, final FailAction failAction, final boolean onProcess, final boolean withUserTask, final boolean onEnter) throws Exception { final Expression outputOfConnectorExpression = new ExpressionBuilder() .createConstantStringExpression("outputExpression"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", outputOfConnectorExpression); // Add a task final ActivityDefinitionBuilder activityDefinitionBuilder; if (withUserTask) { activityDefinitionBuilder = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); } else { activityDefinitionBuilder = processDefinitionBuilder.addAutomaticTask("step1"); } // Connector to add on enter or on finish final ConnectorEvent connectorEvent; if (onEnter) { connectorEvent = ConnectorEvent.ON_ENTER; } else { connectorEvent = ConnectorEvent.ON_FINISH; } // Add connector on process or task final ConnectorDefinitionBuilder addConnector; if (onProcess) { addConnector = processDefinitionBuilder.addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", connectorEvent); } else { addConnector = activityDefinitionBuilder.addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", connectorEvent); } addConnector.addInput("kind", new ExpressionBuilder().createConstantStringExpression(exceptionType)); switch (failAction) { case ERROR_EVENT: addConnector.throwErrorEventWhenFailed("error"); final SubProcessDefinitionBuilder subProcessBuilder = processDefinitionBuilder .addSubProcess("errorSub", true).getSubProcessBuilder(); subProcessBuilder.addStartEvent("errorstart").addErrorEventTrigger("error"); subProcessBuilder.addUserTask("errorTask", ACTOR_NAME); subProcessBuilder.addTransition("errorstart", "errorTask"); break; case IGNORE: addConnector.ignoreError(); break; case FAIL: default: break; } return deployProcessWithActorAndTestConnectorThatThrowException(processDefinitionBuilder, ACTOR_NAME, user); } private ProcessDefinition getProcessWithConnectorThatThrowErrorOnUserTaskOnEnter(final String exceptionType, final FailAction failAction, final boolean onProcess) throws Exception { return getProcessWithConnectorThatThrowError(exceptionType, failAction, onProcess, true, true); } private ProcessDefinition getProcessWithConnectorThatThrowErrorOnAutomaticTaskOnEnter(final String exceptionType, final FailAction failAction, final boolean onProcess) throws Exception { return getProcessWithConnectorThatThrowError(exceptionType, failAction, onProcess, false, true); } private ProcessDefinition getProcessWithConnectorThatThrowErrorOnAutomaticTaskOnFinish(final String exceptionType, final FailAction failAction, final boolean onProcess) throws Exception { return getProcessWithConnectorThatThrowError(exceptionType, failAction, onProcess, false, false); } @Test public void connectorThatThrowExceptionErrorEventPolicyBoundaryOnTaskShouldAbortCurrentTask() throws Exception { final Expression outputOfConnectorExpression = new ExpressionBuilder() .createConstantStringExpression("outputExpression"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", outputOfConnectorExpression); final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); final ConnectorDefinitionBuilder addConnector; addConnector = userTaskBuilder.addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER); addConnector.addInput("kind", new ExpressionBuilder().createConstantStringExpression("normal")); addConnector.throwErrorEventWhenFailed("error"); final BoundaryEventDefinitionBuilder boundaryEvent = userTaskBuilder.addBoundaryEvent("errorBoundary", true); boundaryEvent.addErrorEventTrigger("error"); processDefinitionBuilder.addUserTask("errorTask", ACTOR_NAME); processDefinitionBuilder.addTransition("errorBoundary", "errorTask"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException( processDefinitionBuilder, ACTOR_NAME, user); try { final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); // the connector must trigger this exception step waitForUserTask(startProcess, "errorTask"); waitForFlowNodeInState(startProcess, "step1", TestStates.ABORTED, false); } finally { // clean up disableAndDeleteProcess(processDefinition); } } @Test public void connectorThatThrowExceptionErrorEventPolicyNotCatchOnTask() throws Exception { final Expression outputOfConnectorExpression = new ExpressionBuilder() .createConstantStringExpression("outputExpression"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", outputOfConnectorExpression); final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); final ConnectorDefinitionBuilder addConnector; addConnector = userTaskBuilder.addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER); addConnector.addInput("kind", new ExpressionBuilder().createConstantStringExpression("normal")); addConnector.throwErrorEventWhenFailed("error"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorThatThrowException( processDefinitionBuilder, ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); // the connector must trigger this exception step waitForTaskToFail(startProcess); // clean up disableAndDeleteProcess(processDefinition); } @Test public void connectorThatThrowExceptionErrorEventPolicyCallActivityOnTask() throws Exception { // create the process with connector throwing error final Expression outputOfConnectorExpression = new ExpressionBuilder() .createConstantStringExpression("outputExpression"); ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("outputOfConnector", outputOfConnectorExpression).addUserTask("step1", ACTOR_NAME); final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); final ConnectorDefinitionBuilder addConnector = userTaskBuilder.addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER); addConnector.addInput("kind", new ExpressionBuilder().createConstantStringExpression("normal")); addConnector.throwErrorEventWhenFailed("error"); processDefinitionBuilder.addTransition("step1", "step2"); final ProcessDefinition calledProcess = deployProcessWithActorAndTestConnectorThatThrowException( processDefinitionBuilder, ACTOR_NAME, user); // create parent process with call activity and boundary processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance("parentProcess", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); final CallActivityBuilder callActivityBuilder = processDefinitionBuilder.addCallActivity("processWithConnector", new ExpressionBuilder().createConstantStringExpression("processWithConnector"), new ExpressionBuilder().createConstantStringExpression("1.0")); final BoundaryEventDefinitionBuilder boundaryEvent = callActivityBuilder.addBoundaryEvent("errorBoundary", true); boundaryEvent.addErrorEventTrigger("error"); processDefinitionBuilder.addUserTask("errorTask", ACTOR_NAME); processDefinitionBuilder.addTransition("errorBoundary", "errorTask"); final ProcessDefinition callingProcess = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); // start parent final ProcessInstance processInstance = getProcessAPI().startProcess(callingProcess.getId()); final HumanTaskInstance step1 = waitForUserTaskAndExecuteAndGetIt("step1", user); // the connector must trigger this exception step of the calling process waitForUserTaskAndExecuteIt(processInstance, "errorTask", user); waitForProcessToFinish(processInstance); waitForProcessToBeInState(step1.getParentProcessInstanceId(), ProcessInstanceState.ABORTED); // clean up disableAndDeleteProcess(callingProcess, calledProcess); } @Test public void should_order_connectors_by_execution_order_when_no_filter_are_set() throws Exception { AutomaticTaskDefinitionBuilder automaticTask = new ProcessDefinitionBuilder() .createNewInstance("processWithConnectors", " 1.0") .addAutomaticTask("step"); automaticTask.addConnector("enter1", "connectorDef1", "1.0", ConnectorEvent.ON_ENTER); automaticTask.addConnector("enter2", "failingConnector", "1.0", ConnectorEvent.ON_ENTER); automaticTask.addConnector("enter3", "connectorDef1", "1.0", ConnectorEvent.ON_ENTER); automaticTask.addConnector("finish1", "connectorDef1", "1.0", ConnectorEvent.ON_FINISH); automaticTask.addConnector("finish2", "connectorDef1", "1.0", ConnectorEvent.ON_FINISH); BusinessArchive bar = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(automaticTask .getProcess()) .addConnectorImplementation( generateConnectorImplementation("connectorDef1", "1.0", DoNothingConnector.class)) .addConnectorImplementation( generateConnectorImplementation("failingConnector", "1.0", FailingConnector.class)) .done(); ProcessDefinition processDefinition = getProcessAPI().deploy(bar); getProcessAPI().enableProcess(processDefinition.getId()); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); FlowNodeInstance failedTask = waitForFlowNodeInFailedState(processInstance, "step"); SearchResult connectorInstances = getProcessAPI().searchConnectorInstances( new SearchOptionsBuilder(0, 100) .filter(ConnectorInstancesSearchDescriptor.CONTAINER_ID, failedTask.getId()) .done()); Assertions.assertThat(connectorInstances.getResult()).extracting("name", "state") .containsExactly( tuple("enter1", ConnectorState.DONE), tuple("enter2", ConnectorState.FAILED), tuple("enter3", ConnectorState.TO_BE_EXECUTED), tuple("finish1", ConnectorState.TO_BE_EXECUTED), tuple("finish2", ConnectorState.TO_BE_EXECUTED)); disableAndDeleteProcess(processDefinition); } @Test public void searchArchivedConnectorInstance() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnActivityInstance", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder .addConnector("myConnectorOnProcess", CONNECTOR_WITH_OUTPUT_ID, "1.0", ConnectorEvent.ON_ENTER) .addInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createConstantStringExpression("value1")); processDefinitionBuilder.addAutomaticTask("step1") .addConnector("myConnectorOnStep", CONNECTOR_WITH_OUTPUT_ID, "1.0", ConnectorEvent.ON_ENTER) .addInput(CONNECTOR_INPUT_NAME, new ExpressionBuilder().createConstantStringExpression("value1")); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithOutput( processDefinitionBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step2Id = waitForUserTask(processInstance, "step2"); // search with filter on name SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 100); searchOptionsBuilder.filter(ArchiveConnectorInstancesSearchDescriptor.NAME, "myConnectorOnStep"); searchOptionsBuilder.sort(ArchiveConnectorInstancesSearchDescriptor.NAME, Order.ASC); SearchOptions searchOptions = searchOptionsBuilder.done(); SearchResult connectorInstances = getProcessAPI() .searchArchivedConnectorInstances(searchOptions); Assertions.assertThat(connectorInstances.getResult()).extracting("name").containsExactly("myConnectorOnStep"); // finish process assignAndExecuteStep(step2Id, userId); waitForProcessToFinish(processInstance); // search for archived connector instances searchOptionsBuilder = new SearchOptionsBuilder(0, 100); searchOptionsBuilder.filter(ArchiveConnectorInstancesSearchDescriptor.CONTAINER_ID, processInstance.getId()); searchOptionsBuilder.filter(ArchiveConnectorInstancesSearchDescriptor.CONTAINER_TYPE, PROCESS); searchOptionsBuilder.sort(ArchiveConnectorInstancesSearchDescriptor.NAME, Order.ASC); searchOptions = searchOptionsBuilder.done(); connectorInstances = getProcessAPI().searchArchivedConnectorInstances(searchOptions); Assertions.assertThat(connectorInstances.getResult()).extracting("name") .containsExactly("myConnectorOnProcess"); // search for archived connector instances using SOURCE_OBJECT_ID searchOptionsBuilder = new SearchOptionsBuilder(0, 100); searchOptionsBuilder.filter(ArchiveConnectorInstancesSearchDescriptor.SOURCE_OBJECT_ID, connectorInstances.getResult().get(0).getSourceObjectId()); searchOptions = searchOptionsBuilder.done(); Assertions.assertThat(getProcessAPI().searchArchivedConnectorInstances(searchOptions).getResult()) .extracting("name") .containsOnly("myConnectorOnProcess"); // now also connector of process is archived searchOptionsBuilder = new SearchOptionsBuilder(0, 100); searchOptionsBuilder.sort(ArchiveConnectorInstancesSearchDescriptor.NAME, Order.ASC); searchOptions = searchOptionsBuilder.done(); connectorInstances = getProcessAPI().searchArchivedConnectorInstances(searchOptions); Assertions.assertThat(connectorInstances.getResult()).extracting("name").containsExactly("myConnectorOnProcess", "myConnectorOnStep"); disableAndDeleteProcess(processDefinition); // check all archived connectors are deleted searchOptionsBuilder = new SearchOptionsBuilder(0, 100); searchOptions = searchOptionsBuilder.done(); connectorInstances = getProcessAPI().searchArchivedConnectorInstances(searchOptions); assertTrue("there should be no archived connector anymore", connectorInstances.getResult().isEmpty()); } @Test public void connectorWithExternalLibraryInInputAndOutput() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder(); final ProcessDefinitionBuilder pBuilder = processDefinitionBuilder.createNewInstance("emptyProcess", String.valueOf(System.currentTimeMillis())); final UserTaskDefinitionBuilder addUserTask = pBuilder.addActor(ACTOR_NAME).addUserTask("step1", ACTOR_NAME); addUserTask.addData("dataActivity", java.lang.Object.class.getName(), new ExpressionBuilder().createGroovyScriptExpression("myScript", "new org.bonitasoft.dfgdfg.Restaurant()", java.lang.Object.class.getName())); addUserTask .addConnector("myConnector1", "connectorInJar", "1.0.0", ConnectorEvent.ON_ENTER) .addInput("input", new ExpressionBuilder().createGroovyScriptExpression("myScript", "new org.bonitasoft.dfgdfg.Restaurant()", Object.class.getName())) .addOutput( new OperationBuilder().createSetDataOperation("dataActivity", new ExpressionBuilder().createGroovyScriptExpression("myScript", "new org.bonitasoft.dfgdfg.Restaurant()", "org.bonitasoft.dfgdfg.Restaurant"))); final DesignProcessDefinition done = pBuilder.done(); final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(done); builder.addConnectorImplementation( getResource("/org/bonitasoft/engine/connectors/TestConnectorInJar.impl", "TestConnectorInJar.impl")); builder.addClasspathResource( getResource("/org/bonitasoft/engine/connectors/connector-in-jar.jar.bak", "connector-in-jar.jar")); builder.addClasspathResource(getResource("/org.bonitasoft.dfgdfg.bak", "org.bonitasoft.dfgdfg.jar")); final BusinessArchive businessArchive = builder.done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step1"); disableAndDeleteProcess(processDefinition); } @Test public void executeConnectorOnProcessDefinitionWithOperations() throws Exception { final Expression input1Expression = new ExpressionBuilder().createInputExpression("valueOfInput1", String.class.getName()); final Expression input2Expression = new ExpressionBuilder().createInputExpression("valueOfInput2", String.class.getName()); final Expression mainExp = new ExpressionBuilder().createExpression("param1", "'welcome '+valueOfInput1+' and '+valueOfInput2", ExpressionType.TYPE_READ_ONLY_SCRIPT.toString(), String.class.getName(), "GROOVY", Arrays.asList(input1Expression, input2Expression)); // process with data "Mett" final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnActivityInstance", "1.0"); designProcessDefinition.addActor(ACTOR_NAME); designProcessDefinition.addUserTask("step1", ACTOR_NAME); final ProcessDefinition processDefinition = deployProcessWithExternalTestConnector(designProcessDefinition, ACTOR_NAME, user); // execute connector with operations: // connector return param1="welcome Lily and Lucy and Mett" // operations: put "Jack" in data valueOfInput3, param1 in "externalData" and "John" in "externalDataConst" // Create Operation map: final List operations = new ArrayList<>(2); // set valueOfInput3 final Map contexts = new HashMap<>(); operations.add(new OperationBuilder().createNewInstance() .setLeftOperand("externalData", LeftOperand.TYPE_EXTERNAL_DATA) .setRightOperand(new ExpressionBuilder().createInputExpression("param1", String.class.getName())) .setType(OperatorType.ASSIGNMENT).done()); operations.add(new OperationBuilder().createNewInstance() .setLeftOperand("externalDataConst", LeftOperand.TYPE_EXTERNAL_DATA) .setRightOperand(new ExpressionBuilder().createConstantStringExpression("John")) .setType(OperatorType.ASSIGNMENT).done()); final Map connectorInputParameters = getConnectorInputParameters("param1", mainExp); final Map> inputValues = getInputValues("param1", Arrays.asList("valueOfInput1", "valueOfInput2"), Arrays.asList("Lily", "Lucy")); final Map res = getProcessAPI().executeConnectorOnProcessDefinition( ConnectorExecutionIT.DEFAULT_EXTERNAL_CONNECTOR_ID, ConnectorExecutionIT.DEFAULT_EXTERNAL_CONNECTOR_VERSION, connectorInputParameters, inputValues, operations, contexts, processDefinition.getId()); assertEquals("welcome Lily and Lucy", res.get("externalData")); assertEquals("John", res.get("externalDataConst")); disableAndDeleteProcess(processDefinition); } @Test // The connector output is list to which an element is added when the method connect or disconnect is called. // when the output operations are executed only the method is connect is supposed to be called, that is, the list must contain only one element. public void executeConnectorWithConnectedResouce() throws Exception { final String dataName = "isConnected"; final String userTaskName = "step1"; final ProcessDefinition processDefinition = deployProcessWithConnectorWithConnectedResources(dataName, userTaskName); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, userTaskName); final DataInstance dataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstance.getId()); assertEquals(1, dataInstance.getValue()); disableAndDeleteProcess(processDefinition); } private ProcessDefinition deployProcessWithConnectorWithConnectedResources(final String dataName, final String userTaskName) throws InvalidExpressionException, BonitaException, IOException { // expression to get the connector output final Expression connectorOutPutExpr = new ExpressionBuilder().createInputExpression("output", List.class.getName()); // expression to get the list size final Expression rightSideOperation = new ExpressionBuilder().createJavaMethodCallExpression("getOutPut", "size", Integer.class.getName(), connectorOutPutExpr); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("proc", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addIntegerData(dataName, null);// data to store the list size processDefinitionBuilder.addUserTask(userTaskName, ACTOR_NAME) .addConnector("myConnector", "org.bonitasoft.connector.testConnectorWithConnectedResource", "1.0", ConnectorEvent.ON_ENTER) .addOutput(new OperationBuilder().createSetDataOperation(dataName, rightSideOperation)); return deployProcessWithActorAndTestConnectorWithConnectedResource(processDefinitionBuilder, ACTOR_NAME, user); } @Test public void executeConnectorOnEnterAfterUserFilter() throws Exception { final String dataName = "taskAssignee"; final User jack = createUser("jack", "bpm"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithConnector", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addLongData(dataName, null); // expression to get the connector output final Expression connectorOutPutExpr = new ExpressionBuilder().createInputExpression( ExpressionConstants.TASK_ASSIGNEE_ID.getEngineConstantName(), Long.class.getName()); final UserTaskDefinitionBuilder addUserTask = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); addUserTask.addConnector("myConnector", "org.bonitasoft.connector.testConnectorEngineExecutionContext", "1.0", ConnectorEvent.ON_ENTER).addOutput( new OperationBuilder().createSetDataOperation(dataName, connectorOutPutExpr)); addUserTask.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilterWithAutoAssign", "1.0").addInput( "userId", new ExpressionBuilder().createConstantLongExpression(userId)); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addTransition("step1", "step2"); final List userIds = new ArrayList<>(); userIds.add(user); userIds.add(jack); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorEngineExecutionContextAndFilterWithAutoAssign( processDefinitionBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step1", jack); waitForUserTask(processInstance, "step2"); final DataInstance processDataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstance.getId()); assertEquals(Long.valueOf(userId), processDataInstance.getValue()); disableAndDeleteProcess(processDefinition); deleteUser(jack); } @Test public void getEngineExecutionContext() throws Exception { final String dataName = "taskAssignee"; final String step1 = "step1"; final String step2 = "step2"; final ProcessDefinition processDefinition = deployProcWithConnectorEngineExecContext(dataName, step1, step2); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, step1, user); waitForUserTask(processInstance, step2); final DataInstance dataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstance.getId()); assertEquals(userId, dataInstance.getValue()); disableAndDeleteProcess(processDefinition); } private ProcessDefinition deployProcWithConnectorEngineExecContext(final String dataName, final String step1, final String step2) throws InvalidExpressionException, BonitaException, IOException { // expression to get the connector output final Expression connectorOutPutExpr = new ExpressionBuilder().createInputExpression( ExpressionConstants.TASK_ASSIGNEE_ID.getEngineConstantName(), Long.class.getName()); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("proc", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addLongData(dataName, null);// data to store the connector output processDefinitionBuilder.addUserTask(step1, ACTOR_NAME) .addConnector("myConnector", "org.bonitasoft.connector.testConnectorEngineExecutionContext", "1.0", ConnectorEvent.ON_FINISH) .addOutput(new OperationBuilder().createSetDataOperation(dataName, connectorOutPutExpr)); processDefinitionBuilder.addUserTask(step2, ACTOR_NAME); processDefinitionBuilder.addTransition(step1, step2); return deployProcessWithActorAndTestConnectorEngineExecutionContext(processDefinitionBuilder, ACTOR_NAME, user); } @Test public void executeConnectorWithInputExpressionUsingAPI() throws Exception { final String definitionId = "connectorThatUseAPI"; final String definitionVersion = "1.0"; final byte[] connectorImplementationFile = BuildTestUtil.buildConnectorImplementationFile(definitionId, definitionVersion, "impl1", "1.0", TestConnectorWithOutput.class.getName()); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithConnector", "1.12"); builder.addActor(ACTOR_NAME); builder.addLongData("numberOfUser", new ExpressionBuilder().createConstantLongExpression(-1)); final ConnectorDefinitionBuilder connector = builder.addAutomaticTask("step1").addConnector("theConnector", definitionId, definitionVersion, ConnectorEvent.ON_ENTER); String script = "org.bonitasoft.engine.identity.User createUser = apiAccessor.getIdentityAPI().createUser(new org.bonitasoft.engine.identity.UserCreator(\"myUser\", \"password\"));\n"; script += "apiAccessor.getProcessAPI().attachDocument(processInstanceId, \"a\", \"a\", \"application/pdf\",\"test\".getBytes());"; script += "return apiAccessor.getIdentityAPI().getNumberOfUsers();"; connector.addInput( CONNECTOR_INPUT_NAME, new ExpressionBuilder().createGroovyScriptExpression("script", script, Long.class.getName(), new ExpressionBuilder().createEngineConstant(ExpressionConstants.API_ACCESSOR), new ExpressionBuilder().createEngineConstant(ExpressionConstants.PROCESS_INSTANCE_ID))); connector.addOutput(new OperationBuilder().createSetDataOperation("numberOfUser", new ExpressionBuilder().createInputExpression(CONNECTOR_OUTPUT_NAME, Long.class.getName()))); builder.addUserTask("step2", ACTOR_NAME); builder.addTransition("step1", "step2"); final BusinessArchiveBuilder barBuilder = new BusinessArchiveBuilder().createNewBusinessArchive(); barBuilder.addConnectorImplementation(new BarResource("connector.impl", connectorImplementationFile)); barBuilder.addClasspathResource( new BarResource("connector.jar", IOUtil.generateJar(TestConnectorWithOutput.class))); barBuilder.setProcessDefinition(builder.done()); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(barBuilder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step2"); final DataInstance numberOfUser = getProcessAPI().getProcessDataInstance("numberOfUser", processInstance.getId()); assertEquals(2L, numberOfUser.getValue()); final SearchResult documents = getProcessAPI().searchDocuments( new SearchOptionsBuilder(0, 10) .filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()).done()); assertEquals(1, documents.getCount()); disableAndDeleteProcess(processDefinition); getIdentityAPI().deleteUser("myUser"); } @Test public void getConnectorWithFailureInformationOnConnectorExecution() throws Exception { //given final ProcessDefinition processDefinition = deployAndEnableProcessWithFailingConnector(); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final ActivityInstance failedTask = waitForTaskToFail(processInstance); final SearchResult searchResult = getProcessAPI().searchConnectorInstances( getFirst100ConnectorInstanceSearchOptions(failedTask.getId(), FLOWNODE).done()); Assertions.assertThat(searchResult.getCount()).isEqualTo(1); Assertions.assertThat(searchResult.getResult().get(0).getState()).isEqualTo(ConnectorState.FAILED); //when final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(ArchivedFlowNodeInstanceSearchDescriptor.ORIGINAL_FLOW_NODE_ID, Order.ASC); builder.filter(ArchivedFlowNodeInstanceSearchDescriptor.ORIGINAL_FLOW_NODE_ID, failedTask.getId()); builder.filter(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, "executing"); final SearchResult archivedFlowNodeInstanceSearchResult = getProcessAPI() .searchArchivedFlowNodeInstances(builder.done()); //then Assertions.assertThat(archivedFlowNodeInstanceSearchResult.getCount()).isEqualTo(1); disableAndDeleteProcess(processDefinition); } private ProcessDefinition deployAndEnableProcessWithFailingConnector() throws Exception { final Expression normal = new ExpressionBuilder() .createConstantStringExpression(TestConnectorThatThrowException.NORMAL); final Expression defaultValueExpression = new ExpressionBuilder().createConstantStringExpression("initial"); final String dataName = "myVar"; final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); designProcessDefinition.addActor(ACTOR_NAME); designProcessDefinition.addShortTextData(dataName, defaultValueExpression); designProcessDefinition .addAutomaticTask("step1") .addConnector("myConnector", TEST_CONNECTOR_THAT_THROW_EXCEPTION_ID, "1.0", ConnectorEvent.ON_ENTER) .addInput("kind", normal) .addOutput(new LeftOperandBuilder().createNewInstance().setName(dataName).done(), OperatorType.ASSIGNMENT, "=", "", new ExpressionBuilder().createInputExpression("output1", String.class.getName())); final ProcessDefinition processDefinition = deployAndEnableProcessWithActorAndTestConnectorThatThrowException( designProcessDefinition, ACTOR_NAME, user); return processDefinition; } public ProcessDefinition deployAndEnableProcessWithActorAndTestConnectorThatThrowException( final ProcessDefinitionBuilder processDefinitionBuilder, final String actor, final User user) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actor, user, null, "TestConnectorThatThrowException.impl", TestConnectorThatThrowException.class, "TestConnectorThatThrowException.jar"); } @Test public void should_be_able_to_execute_process_connector_without_flownode() throws Exception { ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithoutFlownodes", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_ENTER) .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression("valueOfInput1")); ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForProcessToFinish(processInstance); } @Test public void should_put_connector_in_failed_even_when_retryable_exception_happens_while_evaluating_output_operations() throws Exception { ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithoutFlownodes", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder.addData("output", "java.lang.String", null); processBuilder .addAutomaticTask("task") .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_ENTER) .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression("valueOfInput1")) .addOutput(new OperationBuilder().createSetDataOperation("output", new ExpressionBuilder().createGroovyScriptExpression("throw retryable", "throw new org.bonitasoft.engine.commons.exceptions.SRetryableException('a retryable exception')", "java.lang.String"))); ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); ActivityInstance failedTask = waitForTaskToFail(processInstance); Assertions.assertThat(getProcessAPI() .searchConnectorInstances( getFirst100ConnectorInstanceSearchOptions(failedTask.getId(), FLOWNODE).done()) .getResult().get(0)).satisfies(connector -> { Assertions.assertThat(connector.getState()).isEqualTo(ConnectorState.FAILED); }); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/AbstractWaitingEventIT.java ================================================ /** * 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.event; import static org.junit.Assert.assertEquals; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.bpm.flownode.WaitingEvent; import org.bonitasoft.engine.bpm.flownode.WaitingEventSearchDescriptor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; /** * @author Elias Ricken de Medeiros */ public abstract class AbstractWaitingEventIT extends AbstractEventIT { private static final String SEARCH_WAITING_EVENTS_COMMAND = "searchWaitingEventsCommand"; private static final String SEARCH_OPTIONS_KEY = "searchOptions"; protected void checkNumberOfWaitingEvents(final String errorMessage, final String flowNodeName, final long expectedNbOfWaitingEvents) throws BonitaException { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(WaitingEventSearchDescriptor.FLOW_NODE_NAME, flowNodeName); final Map parameters = new HashMap<>(1); parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done()); @SuppressWarnings("unchecked") final SearchResult searchResult = (SearchResult) getCommandAPI() .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters); assertEquals(errorMessage, expectedNbOfWaitingEvents, searchResult.getCount()); } protected void checkNumberOfWaitingEvents(final String flowNodeName, final int expectedNbOfWaitingEvents) throws BonitaException { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(WaitingEventSearchDescriptor.FLOW_NODE_NAME, flowNodeName); final Map parameters = new HashMap<>(1); parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done()); @SuppressWarnings("unchecked") final SearchResult searchResult = (SearchResult) getCommandAPI() .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters); Assertions.assertThat(searchResult.getResult()).hasSize(expectedNbOfWaitingEvents); } protected void checkNumberOfWaitingEventsInProcess(final String processName, final long expectedNbOfWaitingEvents) throws BonitaException { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(WaitingEventSearchDescriptor.PROCESS_NAME, processName); final Map parameters = new HashMap<>(1); parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done()); @SuppressWarnings("unchecked") final SearchResult searchResult = (SearchResult) getCommandAPI() .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters); assertEquals(expectedNbOfWaitingEvents, searchResult.getCount()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/EndEventIT.java ================================================ /** * 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.event; import static org.junit.Assert.assertEquals; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.test.TestStates; import org.junit.Test; public class EndEventIT extends AbstractEventIT { @Test public void executeStartAndEndEvents() throws Exception { final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process", "1.0") .addStartEvent("startEvent").addAutomaticTask("step1").addEndEvent("endEvent") .addTransition("startEvent", "step1") .addTransition("step1", "endEvent").getProcess(); final ProcessDefinition definition = deployAndEnableProcess(designProcessDefinition); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(definition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); waitForProcessToFinish(processInstance); disableAndDeleteProcess(definition); } @Test(expected = InvalidProcessDefinitionException.class) public void startEventCannotHaveIncomingTransition() throws Exception { new ProcessDefinitionBuilder().createNewInstance("My_Process", "1.0").addStartEvent("startEvent") .addAutomaticTask("step1") .addTransition("step1", "startEvent").getProcess(); } @Test(expected = InvalidProcessDefinitionException.class) public void endEventCannotHaveOutgoingTransition() throws Exception { new ProcessDefinitionBuilder().createNewInstance("My_Process", "1.0").addAutomaticTask("step1") .addEndEvent("endEvent") .addTransition("endEvent", "step1").getProcess(); } @Test public void terminateEndEventAlone() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("Proc", "1.0"); builder.addEndEvent("stop").addTerminateEventTrigger(); final ProcessDefinition process = deployAndEnableProcess(builder.done()); final ProcessInstance startProcess = getProcessAPI().startProcess(process.getId()); waitForProcessToFinish(startProcess); disableAndDeleteProcess(process); } @Test public void executeStartAndEndEventWithTask() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("executeStartAndEndEventWithTask", "1.0"); builder.addStartEvent("start").addAutomaticTask("step1").addEndEvent("stop").addTerminateEventTrigger() .addTransition("start", "step1") .addTransition("step1", "stop"); final ProcessDefinition process = deployAndEnableProcess(builder.done()); final ProcessInstance startProcess = getProcessAPI().startProcess(process.getId()); waitForProcessToFinish(startProcess); disableAndDeleteProcess(process); } @Test public void terminateEndEventWithTasks() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("Proc", "1.0"); builder.addActor(ACTOR_NAME); builder.addUserTask("step1", ACTOR_NAME); builder.addEndEvent("stop").addTerminateEventTrigger(); builder.addTransition("step1", "stop"); final ProcessDefinition process = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(process.getId()); waitForUserTaskAndExecuteIt(startProcess, "step1", user); waitForProcessToFinish(startProcess); disableAndDeleteProcess(process); } @Test public void terminateEndEventWithNotFinishedBranch() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("Proc", "1.0"); builder.addActor(ACTOR_NAME); builder.addUserTask("step1", ACTOR_NAME); builder.addUserTask("step2", ACTOR_NAME); builder.addEndEvent("stop").addTerminateEventTrigger(); builder.addTransition("step1", "stop"); builder.addTransition("step2", "stop"); final ProcessDefinition process = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); waitForUserTask(processInstance.getId(), "step2"); waitForUserTaskAndExecuteIt(processInstance, "step1", user); // should finish even if we don't execute step2 waitForFlowNodeInState(processInstance, "step2", TestStates.ABORTED, true); waitForProcessToFinish(processInstance); disableAndDeleteProcess(process); } @Test public void terminateEndEvendWithNotFinishedBranch2() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("Proc", "1.0"); builder.addActor(ACTOR_NAME); builder.addUserTask("step1", ACTOR_NAME); builder.addUserTask("step2", ACTOR_NAME); builder.addUserTask("step3", ACTOR_NAME); builder.addEndEvent("stop").addTerminateEventTrigger(); builder.addTransition("step1", "stop"); builder.addTransition("step2", "step3"); builder.addTransition("step3", "stop"); final ProcessDefinition process = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); waitForUserTask(processInstance.getId(), "step2"); waitForUserTaskAndExecuteIt(processInstance, "step1", user); // should finish even if we don't execute step2 waitForFlowNodeInState(processInstance, "step2", TestStates.ABORTED, true); waitForProcessToFinish(processInstance); disableAndDeleteProcess(process); } // @Ignore("Currently ignored because it cause timeout lock on data base: need to refactor transactions and so on") @Test public void terminateEndEvendWithNotFinishedMultipleBranch() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("Proc", "1.0"); builder.addActor(ACTOR_NAME); builder.addUserTask("step1", ACTOR_NAME); builder.addEndEvent("stop").addTerminateEventTrigger(); builder.addTransition("step1", "stop"); for (int i = 2; i < 6; i++) { builder.addUserTask("step" + i, ACTOR_NAME); builder.addTransition("step" + i, "stop"); } final ProcessDefinition process = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(process.getId()); waitForUserTask(startProcess, "step2"); waitForUserTask(startProcess, "step3"); waitForUserTask(startProcess, "step4"); waitForUserTask(startProcess, "step5"); waitForUserTaskAndExecuteIt(startProcess, "step1", user); // should finish even if we don't execute step2 waitForProcessToFinish(startProcess); disableAndDeleteProcess(process); } @Test public void terminateEventWithMultiInstanceParallel() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("terminateEventWithMultiInstance", "1.0"); builder.addAutomaticTask("step1").addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(3)); builder.addEndEvent("stop").addTerminateEventTrigger().addTransition("step1", "stop"); final ProcessDefinition process = deployAndEnableProcess(builder.done()); final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); waitForProcessToFinish(processInstance); disableAndDeleteProcess(process); } @Test public void terminateEventWithMultiInstanceSequential() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("terminateEventWithMultiInstance", "1.0"); builder.addActor(ACTOR_NAME).addUserTask("step1", ACTOR_NAME).addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(3)); builder.addEndEvent("stop").addTerminateEventTrigger().addTransition("step1", "stop"); final ProcessDefinition process = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); for (int i = 0; i < 3; i++) { waitForUserTaskAndExecuteIt(processInstance, "step1", user); } waitForProcessToFinish(processInstance); disableAndDeleteProcess(process); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/ErrorBoundaryEventIT.java ================================================ /** * 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.event; import java.util.List; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.test.TestStates; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class ErrorBoundaryEventIT extends AbstractEventIT { @Test public void errorBoundaryEventTriggeredNamedError() throws Exception { executionWitherrorEventTriggered("error1"); } @Test public void errorBoundaryEventTriggeredCatchAllError() throws Exception { executionWitherrorEventTriggered(null); } protected void executionWitherrorEventTriggered(final String catchErrorCode) throws Exception { final ProcessDefinition calledProcDef = deployAndEnableProcessWithEndThrowErrorEvent("calledProcess", "error1"); final ProcessDefinition callerProcDef = deployAndEnableProcessWithBoundaryErrorEventOnCallActivity( "pErrorBoundary", "calledProcess", "callStep", catchErrorCode, "delivery"); final ProcessInstance processInstance = getProcessAPI().startProcess(callerProcDef.getId()); final FlowNodeInstance callActivity = waitForFlowNodeInExecutingState(processInstance, "callStep", false); final ActivityInstance calledStep1 = waitForUserTaskAndGetIt(processInstance, "calledStep1"); final long calledStep2Id = waitForUserTask(processInstance, "calledStep2"); final ProcessInstance calledProcessInstance = getProcessAPI() .getProcessInstance(calledStep1.getParentProcessInstanceId()); assignAndExecuteStep(calledStep1, user); waitForProcessToFinish(calledProcessInstance); try { waitForArchivedActivity(calledStep2Id, TestStates.ABORTED); } catch (final Exception e) { final List archivedActivityInstances = getProcessAPI() .getArchivedActivityInstances(processInstance.getId(), 0, 100, ActivityInstanceCriterion.DEFAULT); System.out.println("After completion of the called process"); for (final ArchivedActivityInstance archivedActivityInstance : archivedActivityInstances) { System.out.println("name=" + archivedActivityInstance.getName() + ", state=" + archivedActivityInstance.getState() + ", archivedDate=" + archivedActivityInstance.getArchiveDate().getTime()); } throw new Exception(archivedActivityInstances.toString(), e); } waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user); waitForProcessToFinish(processInstance); waitForArchivedActivity(callActivity.getId(), TestStates.ABORTED); checkWasntExecuted(processInstance, "step2"); disableAndDeleteProcess(calledProcDef, callerProcDef); } @Test public void errorBoundaryEventNotTriggered() throws Exception { final ProcessDefinition calledProcDef = deployAndEnableProcessWithEndThrowErrorEvent("calledProcess", "error1"); final ProcessDefinition callerProcDef = deployAndEnableProcessWithBoundaryErrorEventOnCallActivity( "pErrorBoundary", "calledProcess", "callStep", "error1", "delivery"); try { final ProcessInstance processInstance = getProcessAPI().startProcess(callerProcDef.getId()); final ActivityInstance calledStep1 = waitForUserTaskAndGetIt(processInstance, "calledStep1"); final ProcessInstance calledProcessInstance = getProcessAPI() .getProcessInstance(calledStep1.getParentProcessInstanceId()); waitForUserTaskAndExecuteIt(processInstance, "calledStep2", user); waitForFlowNodeInState(processInstance, "calledStep1", TestStates.ABORTED, true); waitForProcessToFinish(calledProcessInstance); waitForUserTaskAndExecuteIt(processInstance, "step2", user); waitForProcessToFinish(processInstance); checkWasntExecuted(processInstance, EXCEPTION_STEP); } finally { disableAndDeleteProcess(calledProcDef, callerProcDef); } } @Test public void uncaughtThrowErrorEvent() throws Exception { final ProcessDefinition calledProcDef = deployAndEnableProcessWithEndThrowErrorEvent("calledProcess", "error1"); // catch a different error final ProcessDefinition callerProcDef = deployAndEnableProcessWithBoundaryErrorEventOnCallActivity( "pErrorBoundary", "calledProcess", "callStep", "error2", "delivery"); final ProcessInstance processInstance = getProcessAPI().startProcess(callerProcDef.getId()); waitForFlowNodeInExecutingState(processInstance, "callStep", false); final long calledStep2Id = waitForUserTask(processInstance, "calledStep2"); waitForUserTaskAndExecuteIt(processInstance, "calledStep1", user); waitForArchivedActivity(calledStep2Id, TestStates.ABORTED); // if there are no catch error able to handle the thrown error, the throw error event has the same behavior as a terminate event. waitForUserTaskAndExecuteIt(processInstance, "step2", user); waitForProcessToFinish(processInstance); checkWasntExecuted(processInstance, EXCEPTION_STEP); disableAndDeleteProcess(calledProcDef, callerProcDef); } @Test public void errorEventCaughtAtParentLevel2() throws Exception { final ProcessDefinition procDefLevel0 = deployAndEnableProcessWithEndThrowErrorEvent("procDefLevel0", "error1"); final ProcessDefinition procDefLevel1 = deployAndEnableProcessWithBoundaryErrorEventOnCallActivity( "procDefLevel1", "procDefLevel0", "callStepL1", "error2", "delivery"); final ProcessDefinition procDefLevel2 = deployAndEnableProcessWithBoundaryErrorEventOnCallActivity( "procDefLevel2", "procDefLevel1", "callStepL2", "error1", "delivery"); final ProcessInstance processInstance = getProcessAPI().startProcess(procDefLevel2.getId()); final FlowNodeInstance callActivityL2 = waitForFlowNodeInExecutingState(processInstance, "callStepL2", false); final FlowNodeInstance callActivityL1 = waitForFlowNodeInExecutingState(processInstance, "callStepL1", true); final long calledStep2Id = waitForUserTask(processInstance, "calledStep2"); final ActivityInstance calledStep1 = waitForUserTaskAndGetIt(processInstance, "calledStep1"); final ProcessInstance calledProcessInstanceL0 = getProcessAPI() .getProcessInstance(calledStep1.getParentProcessInstanceId()); final ProcessInstance calledProcessInstanceL1 = getProcessAPI() .getProcessInstance(callActivityL1.getParentProcessInstanceId()); assignAndExecuteStep(calledStep1, user); waitForArchivedActivity(calledStep2Id, TestStates.ABORTED); final FlowNodeInstance executionStep = waitForFlowNodeInReadyState(processInstance, EXCEPTION_STEP, false); waitForProcessToFinish(calledProcessInstanceL0); waitForProcessToBeInState(calledProcessInstanceL1, ProcessInstanceState.ABORTED); assignAndExecuteStep(executionStep.getId(), user.getId()); waitForProcessToFinish(processInstance); waitForArchivedActivity(callActivityL1.getId(), TestStates.ABORTED); waitForArchivedActivity(callActivityL2.getId(), TestStates.ABORTED); checkWasntExecuted(processInstance, "step2"); disableAndDeleteProcess(procDefLevel0, procDefLevel1, procDefLevel2); } @Test public void errorEventTwoCatchErrorMatching() throws Exception { final ProcessDefinition procDefLevel0 = deployAndEnableProcessWithEndThrowErrorEvent("procDefLevel0", "error1"); final ProcessDefinition procDefLevel1 = deployAndEnableProcessWithBoundaryErrorEventOnCallActivity( "procDefLevel1", "procDefLevel0", "callStepL1", "error1", "delivery"); final ProcessDefinition procDefLevel2 = deployAndEnableProcessWithBoundaryErrorEventOnCallActivity( "procDefLevel2", "procDefLevel1", "callStepL2", "error1", "delivery"); final ProcessInstance processInstance = getProcessAPI().startProcess(procDefLevel2.getId()); final FlowNodeInstance callActivityL2 = waitForFlowNodeInExecutingState(processInstance, "callStepL2", false); final FlowNodeInstance callActivityL1 = waitForFlowNodeInExecutingState(processInstance, "callStepL1", true); final ActivityInstance calledStep1 = waitForUserTaskAndGetIt(processInstance, "calledStep1"); final long calledStep2Id = waitForUserTask(processInstance, "calledStep2"); final ProcessInstance calledProcessInstanceL0 = getProcessAPI() .getProcessInstance(calledStep1.getParentProcessInstanceId()); final ProcessInstance calledProcessInstanceL1 = getProcessAPI() .getProcessInstance(callActivityL1.getParentProcessInstanceId()); assignAndExecuteStep(calledStep1, user.getId()); waitForArchivedActivity(calledStep2Id, TestStates.ABORTED); final FlowNodeInstance executionStep = waitForFlowNodeInReadyState(calledProcessInstanceL1, EXCEPTION_STEP, false); waitForProcessToFinish(calledProcessInstanceL0); assignAndExecuteStep(executionStep.getId(), user.getId()); waitForProcessToFinish(calledProcessInstanceL1); waitForUserTaskAndExecuteIt(processInstance, "step2", user); waitForProcessToFinish(processInstance); waitForArchivedActivity(callActivityL1.getId(), TestStates.ABORTED); waitForArchivedActivity(callActivityL2.getId(), TestStates.NORMAL_FINAL); checkWasntExecuted(calledProcessInstanceL1, "step2"); disableAndDeleteProcess(procDefLevel0, procDefLevel1, procDefLevel2); } @Test public void errorCodeThrownBySubProcessShouldBeCatchByMainProcess() throws Exception { final ProcessDefinition subProcess = deployAndEnableSubProcessWhichThrowsAnErrorEvent("SubProcess", "Mistake"); final ProcessDefinition midProcess = deployAndEnableMidProcessWhichContainsACallActivity("MidProcess", "SubProcess"); final ProcessDefinition mainProcess = deployAndEnableProcessWithBoundaryErrorEventOnMICallActivity("Process", "MidProcess", "Mistake", "acme"); final ProcessInstance instance = getProcessAPI().startProcess(mainProcess.getId()); waitForFlowNodeInReadyState(instance, EXCEPTION_STEP, true); waitForFlowNodeInState(instance, "step1", TestStates.ABORTED, false); disableAndDeleteProcess(mainProcess, midProcess, subProcess); } @Test public void processWithMIUserTaskWithErrorEvent_should_take_the_error_flow() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = BuildTestUtil .buildProcessDefinitionWithMultiInstanceUserTaskAndFailedConnector(PROCESS_NAME, "step1"); final ProcessDefinition processDefinition = deployAndEnableProcessWithTestConnectorThatThrowException( processDefinitionBuilder); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step1", user); waitForUserTaskAndExecuteIt(processInstance, "errorFlow", user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/ErrorEventSubProcessIT.java ================================================ /** * 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.event; import static org.junit.Assert.assertEquals; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.data.DataNotFoundException; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionEvaluationException; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.test.TestStates; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros */ public class ErrorEventSubProcessIT extends AbstractWaitingEventIT { private List processDefinitions; @Override @Before public void before() throws Exception { super.before(); processDefinitions = new ArrayList<>(); } @Override @After public void after() throws Exception { disableAndDeleteProcess(processDefinitions); super.after(); } @Test public void errorEventSubProcessTriggeredNamedError() throws Exception { executeProcessTriggeringEventSubProcess("e1", "e1"); } @Test public void errorEventSubProcessTriggeredCatchAllErrors() throws Exception { executeProcessTriggeringEventSubProcess(null, "e1"); } private void executeProcessTriggeringEventSubProcess(final String catchErrorCode, final String throwErrorCode) throws Exception { final String subProcStartEventName = "errorStart"; processDefinitions.add( deployAndEnableProcessWithErrorEventSubProcess(catchErrorCode, throwErrorCode, subProcStartEventName)); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitions.get(0).getId()); waitForUserTask(processInstance.getId(), "step1"); final long step2Id = waitForUserTask(processInstance, "step2"); List activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10); assertEquals(2, activities.size()); checkNumberOfWaitingEvents(subProcStartEventName, 1); // throw error assignAndExecuteStep(step2Id, user); waitForArchivedActivity(step2Id, TestStates.NORMAL_FINAL); waitForFlowNodeInExecutingState(processInstance, BuildTestUtil.EVENT_SUB_PROCESS_NAME, false); final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, "subStep"); final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId()); activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10); assertEquals(2, activities.size()); // the parent process instance is supposed to be aborted, so no more waiting events are expected checkNumberOfWaitingEvents(subProcStartEventName, 0); assignAndExecuteStep(subStep, user); waitForProcessToFinish(subProcInst); waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED); // check that the transition wasn't taken checkWasntExecuted(processInstance, "end"); } @Test public void errorEventSubProcessNotTriggered() throws Exception { final String subProcStartEventName = "errorStart"; processDefinitions .add(deployAndEnableProcessWithErrorEventSubProcess("error 1", "error 1", subProcStartEventName)); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitions.get(0).getId()); final long step1Id = waitForUserTask(processInstance.getId(), "step1"); final long step2Id = waitForUserTask(processInstance.getId(), "step2"); getProcessAPI().getProcessDataInstance("throwException", processInstance.getId()); getProcessAPI().updateProcessDataInstance("throwException", processInstance.getId(), false); final List activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10); assertEquals(2, activities.size()); checkNumberOfWaitingEvents(subProcStartEventName, 1); assignAndExecuteStep(step1Id, user); assignAndExecuteStep(step2Id, user); waitForArchivedActivity(step1Id, TestStates.NORMAL_FINAL); waitForProcessToFinish(processInstance); // the parent process instance has completed, so no more waiting events are expected checkNumberOfWaitingEvents(subProcStartEventName, 0); } @Test public void createSeveralInstances() throws Exception { processDefinitions.add(deployAndEnableProcessWithErrorEventSubProcess("e2", "e2", "errorStart")); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinitions.get(0).getId()); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinitions.get(0).getId()); // throw error waitForUserTask(processInstance1.getId(), "step1"); waitForUserTaskAndExecuteIt(processInstance1, "step2", user); waitForUserTask(processInstance2.getId(), "step1"); waitForUserTaskAndExecuteIt(processInstance2, "step2", user); waitForUserTask(processInstance1.getId(), "subStep"); waitForUserTask(processInstance2.getId(), "subStep"); } @Test public void subProcessCanAccessParentData() throws Exception { processDefinitions.add(deployAndEnableProcessWithErrorEventSubProcessAndData("error1", "error1", "errorStart")); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitions.get(0).getId()); waitForUserTask(processInstance.getId(), "step1"); waitForUserTaskAndExecuteIt(processInstance, "step2", user); final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, "subStep"); final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId()); checkRetrieveDataInstances(processInstance, subStep, subProcInst); checkEvaluateExpression(subStep, "count", Integer.class, 1); assignAndExecuteStep(subStep, user); waitForProcessToFinish(subProcInst); waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED); } @Test public void subProcessCanAccessParentDataEvenIfItDoesntHaveLocalData() throws Exception { final String rootUserTaskName = "step1"; final String subProcUserTaskName = "subStep"; final String dataName = "content"; final String dataValue = "default"; processDefinitions.add(deployAndEnableProcessWithErrorEventSubProcessAndDataOnlyInRoot("error1", "errorStart", rootUserTaskName, subProcUserTaskName, dataName, dataValue)); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitions.get(0).getId()); waitForUserTaskAndExecuteIt(processInstance, rootUserTaskName, user); final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, subProcUserTaskName); final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId()); checkProcessDataInstance(dataName, subProcInst.getId(), dataValue); checkProcessDataInstance(dataName, processInstance.getId(), dataValue); checkEvaluateExpression(subStep, dataName, String.class, dataValue); assignAndExecuteStep(subStep, user); waitForProcessToFinish(subProcInst); waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED); } @Test public void eventSubProcessWithDataAndRootProcessWithNoData() throws Exception { final String rootUserTaskName = "step1"; final String subProcUserTaskName = "subStep"; final String dataName = "content"; final String dataValue = "default"; processDefinitions.add(deployAndEnableProcessWithErrorEventSubProcessAndDataOnlyInSubProc("error1", "errorStart", rootUserTaskName, subProcUserTaskName, dataName, dataValue)); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitions.get(0).getId()); waitForUserTaskAndExecuteIt(processInstance, rootUserTaskName, user); final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, subProcUserTaskName); final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId()); checkProcessDataInstance(dataName, subProcInst.getId(), dataValue); checkEvaluateExpression(subStep, dataName, String.class, dataValue); assignAndExecuteStep(subStep, user); waitForProcessToFinish(subProcInst); waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED); } private void checkEvaluateExpression(final ActivityInstance subStep, final String dataName, final Class expressionType, final Serializable expectedValue) throws InvalidExpressionException, ExpressionEvaluationException { final Map> expressions = new HashMap<>(1); final Expression contVarExpr = new ExpressionBuilder().createDataExpression(dataName, expressionType.getName()); expressions.put(contVarExpr, null); final Map expressionResults = getProcessAPI() .evaluateExpressionsOnActivityInstance(subStep.getId(), expressions); assertEquals(expectedValue, expressionResults.get(contVarExpr.getName())); } private void checkRetrieveDataInstances(final ProcessInstance processInstance, final ActivityInstance subStep, final ProcessInstance subProcInst) throws DataNotFoundException { checkProcessDataInstance("count", subProcInst.getId(), 1); checkProcessDataInstance("content", subProcInst.getId(), "childVar"); checkProcessDataInstance("value", subProcInst.getId(), 10.0); checkProcessDataInstance("content", processInstance.getId(), "parentVar"); checkActivityDataInstance("content", subStep.getId(), "childActivityVar"); } private void checkProcessDataInstance(final String dataName, final long processInstanceId, final Serializable expectedValue) throws DataNotFoundException { final DataInstance processDataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstanceId); assertEquals(expectedValue, processDataInstance.getValue()); } private void checkActivityDataInstance(final String dataName, final long activityInstanceId, final Serializable expectedValue) throws DataNotFoundException { final DataInstance activityDataInstance = getProcessAPI().getActivityDataInstance(dataName, activityInstanceId); assertEquals(expectedValue, activityDataInstance.getValue()); } @Test public void errorEventSubProcInsideTargetCallActivity() throws Exception { processDefinitions.add(deployAndEnableProcessWithErrorEventSubProcess("e1", "e1", "errorStart")); processDefinitions.add(deployAndEnableProcessWithCallActivity(processDefinitions.get(0).getName(), processDefinitions.get(0).getVersion())); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitions.get(1).getId()); final ActivityInstance step1 = waitForUserTaskAndGetIt(processInstance, "step1"); waitForUserTaskAndExecuteIt(processInstance, "step2", user); final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, "subStep"); final ProcessInstance calledProcInst = getProcessAPI().getProcessInstance(step1.getParentProcessInstanceId()); final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId()); waitForArchivedActivity(step1.getId(), TestStates.ABORTED); assignAndExecuteStep(subStep, user); waitForProcessToFinish(subProcInst); waitForProcessToBeInState(calledProcInst, ProcessInstanceState.ABORTED); waitForUserTaskAndExecuteIt(processInstance, "step2", user); waitForProcessToFinish(processInstance); } @Test public void processWithErrorEventSubProcAndCallActivity_must_be_finished_when_subProcess_is_finished() throws Exception { // Create the target process processDefinitions.add(deployAndEnableProcessWithTestConnectorThatThrowException(BuildTestUtil .buildProcessDefinitionWithUserTaskAndFailedConnector(PROCESS_NAME))); // Create the caller process final Expression targetProcessExpr = new ExpressionBuilder() .createConstantStringExpression(BuildTestUtil.PROCESS_NAME); final Expression targetVersionExpr = new ExpressionBuilder() .createConstantStringExpression(BuildTestUtil.PROCESS_VERSION); final ProcessDefinitionBuilder builder = BuildTestUtil.buildProcessDefinitionWithCallActivity( "ProcessWithEventSubProcessAndCallActivity", targetProcessExpr, targetVersionExpr); BuildTestUtil.buildErrorEventSubProcessWithUserTask("SubStep", builder); processDefinitions.add(deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user)); // Start the caller process final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitions.get(1).getId()); final HumanTaskInstance stepBeforeFailedConnector = waitForUserTaskAndExecuteAndGetIt( "StepBeforeFailedConnector", user); final ActivityInstance subStep = waitForUserTaskAndExecuteAndGetIt("SubStep", user); waitForProcessToFinish(subStep.getParentProcessInstanceId()); waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED); waitForProcessToBeInState(stepBeforeFailedConnector.getParentProcessInstanceId(), ProcessInstanceState.ABORTED); } @Test public void should_catch_error_in_event_subprocess_when_process_is_disabled_and_then_cancel_it() throws Exception { //given: a process with error event subprocess catching an error ProcessDefinition parent = deployAndEnableProcessWithActor(new ProcessDefinitionBuilder() .createNewInstance("Parent process with error event subprocess", "1.0") .addActor("actor", true) .addStartEvent("start") .addCallActivity("call", new ExpressionBuilder().createConstantStringExpression("sendError"), new ExpressionBuilder().createConstantStringExpression("1.0")) .addTransition("start", "call") .addSubProcess("compensateEventSubProcess", true).getSubProcessBuilder() .addStartEvent("error").addErrorEventTrigger("theError") .addUserTask("eventSubProcessTask", "actor") .addTransition("error", "eventSubProcessTask").getProcess(), "actor", user); // a called process sending an error ProcessDefinition child = deployAndEnableProcessWithActor( new ProcessDefinitionBuilder().createNewInstance("sendError", "1.0") .addActor("actor", true) .addStartEvent("start") .addUserTask("task", "actor") .addEndEvent("sendError").addErrorEventTrigger("theError") .addTransition("start", "task") .addTransition("task", "sendError").getProcess(), "actor", user); processDefinitions.add(parent); processDefinitions.add(child); ProcessInstance processInstance = getProcessAPI().startProcess(parent.getId()); // we wait for the task in the called process long task = waitForUserTask("task"); //when: we disable the parent process and execute the sub process that sends the error getProcessAPI().disableProcess(parent.getId()); getProcessAPI().assignAndExecuteUserTask(user.getId(), task, Collections.emptyMap()); //then: the error event sub process should be triggered waitForUserTask("eventSubProcessTask"); // then: we should be able to cancel that instance getProcessAPI().cancelProcessInstance(processInstance.getId()); waitForProcessToBeInState(processInstance, ProcessInstanceState.CANCELLED); // to allow proper clean-up: getProcessAPI().enableProcess(parent.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/EventTriggerIT.java ================================================ /** * 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.event; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.time.Duration; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Date; import java.util.List; import org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstance; import org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.TestStates; import org.junit.Test; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ public class EventTriggerIT extends AbstractEventIT { @Test public void searchTimerEventTriggerInstances() throws Exception { final ProcessDefinition process1 = deployAndEnableSimpleProcess("Toto", "moi"); final ProcessDefinition process2 = deployAndEnableProcessWithBoundaryTimerEventOnCallActivity(7200000L, false, "Toto"); final ProcessDefinition process3 = deployAndEnableProcessWithMessageEventSubProcess(); final ProcessDefinition process4 = deployAndEnableProcessWithBoundarySignalEvent("signal"); final ProcessInstance processInstance2 = getProcessAPI().startProcess(process2.getId()); final ProcessInstance processInstance3 = getProcessAPI().startProcess(process3.getId()); final ProcessInstance processInstance4 = getProcessAPI().startProcess(process4.getId()); waitForFlowNodeInState(processInstance2, "timer", TestStates.WAITING, true); waitForUserTask(processInstance3, PARENT_PROCESS_USER_TASK_NAME); waitForUserTask(processInstance4, "step1"); // Return only timer event trigger SearchOptions options = new SearchOptionsBuilder(0, 10).done(); SearchResult searchTimerEventTriggerInstances = getProcessAPI() .searchTimerEventTriggerInstances(processInstance3.getId(), options); assertEquals(0, searchTimerEventTriggerInstances.getCount()); assertTrue(searchTimerEventTriggerInstances.getResult().isEmpty()); searchTimerEventTriggerInstances = getProcessAPI().searchTimerEventTriggerInstances(processInstance4.getId(), options); assertEquals(0, searchTimerEventTriggerInstances.getCount()); assertTrue(searchTimerEventTriggerInstances.getResult().isEmpty()); searchTimerEventTriggerInstances = getProcessAPI().searchTimerEventTriggerInstances(processInstance2.getId(), options); assertEquals(2, searchTimerEventTriggerInstances.getCount()); options = new SearchOptionsBuilder(0, 10) .filter(TimerEventTriggerInstanceSearchDescriptor.EVENT_INSTANCE_NAME, "timer").done(); searchTimerEventTriggerInstances = getProcessAPI().searchTimerEventTriggerInstances(processInstance2.getId(), options); assertEquals(1, searchTimerEventTriggerInstances.getCount()); final List result = searchTimerEventTriggerInstances.getResult(); assertEquals(1, result.size()); assertEquals("timer", result.get(0).getEventInstanceName()); disableAndDeleteProcess(process2, process1, process3, process4); } @Test public void updateTimerEventTriggerInstance() throws Exception { final ProcessDefinition process1 = deployAndEnableSimpleProcess("Toto2", "moi"); final ProcessDefinition process2 = deployAndEnableProcessWithBoundaryTimerEventOnCallActivity(7200000L, false, "Toto2"); final ProcessInstance processInstance2 = getProcessAPI().startProcess(process2.getId()); try { waitForFlowNodeInState(processInstance2, "timer", TestStates.WAITING, true); final SearchOptions options = new SearchOptionsBuilder(0, 10).done(); final List result = getProcessAPI() .searchTimerEventTriggerInstances(processInstance2.getId(), options).getResult(); assertThat(result).hasSize(2); final Date date = new Date(); final Date newDate = getProcessAPI().updateExecutionDateOfTimerEventTriggerInstance(result.get(0).getId(), date); assertTrue(newDate.equals(date) || newDate.after(date)); waitForUserTask(processInstance2, EXCEPTION_STEP); // When using optimisation, we notify quartz of a change in triggers and help it trigger any change in triggers quickly. // Without optimisations, it takes around 30 seconds assertThat(Duration.between(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()), LocalDateTime.now(ZoneId.systemDefault()))) .isLessThanOrEqualTo(Duration.ofSeconds(10)); assertThat(getProcessAPI().searchTimerEventTriggerInstances(processInstance2.getId(), options).getResult()) .hasSize(1); } finally { disableAndDeleteProcess(process2, process1); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/InterruptingTimerBoundaryEventIT.java ================================================ /** * 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.event; import static org.junit.Assert.assertEquals; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.TimerType; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.test.TestStates; import org.junit.Test; public class InterruptingTimerBoundaryEventIT extends AbstractEventIT { @Test public void timerBoundaryEventTriggered() throws Exception { final int timerDuration = 1000; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEvent(timerDuration, true, "step1", "exceptionStep", "step2"); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance.getId(), "step1"); // wait timer trigger waitForUserTaskAndExecuteIt(processInstance, "exceptionStep", user); waitForFlowNodeInState(processInstance, "step1", TestStates.ABORTED, true); waitForProcessToFinish(processInstance); checkFlowNodeWasntExecuted(processInstance.getId(), "step2"); disableAndDeleteProcess(processDefinition); } @Test public void timerBoundaryEventWithScriptThatFail() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("pTimerBoundary", "2.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("start"); final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); userTaskDefinitionBuilder.addBoundaryEvent("timer", true).addTimerEventTriggerDefinition(TimerType.DURATION, new ExpressionBuilder().createGroovyScriptExpression("script", "throw new java.lang.RuntimeException()", Long.class.getName())); processDefinitionBuilder.addAutomaticTask("timerStep"); processDefinitionBuilder.addTransition("timer", "timerStep"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForFlowNodeInFailedState(processInstance, "step1"); disableAndDeleteProcess(processDefinition); } @Test public void timerBoundaryEventTriggeredOnCallActivity() throws Exception { final int timerDuration = 2000; final String simpleProcessName = "targetProcess"; final String simpleTaskName = "stepCA"; // deploy a simple process p1 final ProcessDefinition targetProcessDefinition = deployAndEnableSimpleProcess(simpleProcessName, simpleTaskName); // deploy a process, p2, with a call activity calling p1. The call activity has an interrupting timer boundary event final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEventOnCallActivity( timerDuration, true, simpleProcessName); // start the root process and wait for boundary event trigger final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long stepCAId = waitForUserTask(processInstance, simpleTaskName); // wait timer trigger // check that the exception flow was taken waitForUserTaskAndExecuteIt(processInstance, "exceptionStep", user); waitForProcessToFinish(processInstance); final ArchivedActivityInstance archActivityInst = getProcessAPI().getArchivedActivityInstance(stepCAId); assertEquals(TestStates.ABORTED.getStateName(), archActivityInst.getState()); checkFlowNodeWasntExecuted(processInstance.getId(), PARENT_PROCESS_USER_TASK_NAME); disableAndDeleteProcess(processDefinition); disableAndDeleteProcess(targetProcessDefinition); } @Test public void timerBoundaryEventTriggeredOnSequentialMultiInstance() throws Exception { // deploy a process with a interrupting timer boundary event attached to a sequential multi-instance final int timerDuration = 1000; final String multiTaskName = "step1"; final ProcessDefinition processDefinition = deployAndEnableProcessMultiInstanceWithBoundaryEvent(timerDuration, true, multiTaskName, 4, true, "step2", "exceptionStep"); // start the process and wait the timer to trigger final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance.getId(), multiTaskName); // wait timer trigger // check that the exception flow was taken waitForUserTaskAndExecuteIt(processInstance, "exceptionStep", user); waitForFlowNodeInState(processInstance, multiTaskName, TestStates.ABORTED, true); waitForProcessToFinish(processInstance); checkFlowNodeWasntExecuted(processInstance.getId(), "step2"); disableAndDeleteProcess(processDefinition.getId()); } @Test public void timerBoundaryEventTriggeredOnParallelMultiInstance() throws Exception { final int timerDuration = 1000; final int loopCardinality = 4; final boolean isSequential = false; // deploy a process with a interrupting timer boundary event attached to a parallel multi-instance final ProcessDefinition processDefinition = deployAndEnableProcessMultiInstanceWithBoundaryEvent(timerDuration, true, "step1", loopCardinality, isSequential, "step2", "exceptionStep"); // start the process and wait for process to be triggered final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); // wait timer trigger waitForUserTaskAndExecuteIt(processInstance, "exceptionStep", user); waitForProcessToFinish(processInstance); final ArchivedActivityInstance archActivityInst = getProcessAPI().getArchivedActivityInstance(step1Id); assertEquals(TestStates.ABORTED.getStateName(), archActivityInst.getState()); checkFlowNodeWasntExecuted(processInstance.getId(), "step2"); disableAndDeleteProcess(processDefinition.getId()); } @Test public void timerBoundaryEventTriggeredOnLoopActivity() throws Exception { final long timerDuration = 1000; final int loopMax = 2; final String loopActivityName = "step1"; final String normalFlowStepName = "step2"; final String exceptionFlowStepName = "exceptionStep"; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEventOnLoopActivity( timerDuration, true, loopMax, loopActivityName, normalFlowStepName, exceptionFlowStepName); // start the process and wait timer to trigger final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance.getId(), loopActivityName); Thread.sleep(timerDuration); // wait timer trigger // verify that the exception flow was taken waitForUserTaskAndExecuteIt(processInstance, exceptionFlowStepName, user); // verify that the normal flow was aborted waitForFlowNodeInState(processInstance, loopActivityName, TestStates.ABORTED, true); waitForProcessToFinish(processInstance); checkFlowNodeWasntExecuted(processInstance.getId(), normalFlowStepName); disableAndDeleteProcess(processDefinition.getId()); } @Test public void timerBoundaryEventTriggeredAndLongData() throws Exception { final int timerDuration = 1000; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEventOnHumanTask( timerDuration, true); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // Wait and execute the step1 with a timer boundary event waitForUserTaskAndAssignIt(processInstance, "step1", user); // wait timer trigger waitForUserTaskAndExecuteIt(processInstance, "exceptionStep", user); waitForFlowNodeInState(processInstance, "step1", TestStates.ABORTED, true); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/MessageBoundaryEventIT.java ================================================ /** * 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.event; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.bonitasoft.engine.util.AssertionsUtils.assertNoErrorAfterXAttemps; import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertEquals; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.test.TestStates; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MessageBoundaryEventIT extends AbstractEventIT { private static final Logger logger = LoggerFactory.getLogger(MessageBoundaryEventIT.class); @Test public void messageBoundaryEventTriggered() throws Exception { final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEvent("MyMessage"); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance.getId(), "step1"); // Check that boundary event has been created: long otherBoundaries = getProcessAPI() .searchFlowNodeInstances(new SearchOptionsBuilder(0, 10) .filter(FlowNodeInstanceSearchDescriptor.NAME, "otherBoundaryNotTriggered").done()) .getCount(); assertThat(otherBoundaries).isEqualTo(1); getProcessAPI().sendMessage("MyMessage", new ExpressionBuilder().createConstantStringExpression("pMessageBoundary"), new ExpressionBuilder().createConstantStringExpression(BOUNDARY_NAME), null); waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user); waitForProcessToFinish(processInstance); waitForArchivedActivity(step1Id, TestStates.ABORTED); checkWasntExecuted(processInstance, "step2"); // Boundary events are not archived, so there is no way to check if the second boundary has been properly aborted (otherBoundaryNotTriggered). // For that matter, only check that boundary is deleted: otherBoundaries = getProcessAPI() .searchFlowNodeInstances(new SearchOptionsBuilder(0, 10) .filter(FlowNodeInstanceSearchDescriptor.NAME, "otherBoundaryNotTriggered").done()) .getCount(); assertThat(otherBoundaries).isEqualTo(0); disableAndDeleteProcess(processDefinition); } @Test public void messageBoundaryEventNotTriggered() throws Exception { final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEvent("MyMessage1"); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step1", user); final long step2Id = waitForUserTask(processInstance.getId(), "step2"); getProcessAPI().sendMessage("MyMessage1", new ExpressionBuilder().createConstantStringExpression("pMessageBoundary"), new ExpressionBuilder().createConstantStringExpression("step1"), null); assignAndExecuteStep(step2Id, user); checkWasntExecuted(processInstance, "exceptionStep"); disableAndDeleteProcess(processDefinition); } @Test public void messageBoundaryEventOnCallActivityTriggered() throws Exception { final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnCallActivity(); final ProcessDefinition calledProcessDefinition = deployAndEnableSimpleProcess("calledProcess", "calledTask"); try { final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final ActivityInstance calledStep = waitForUserTaskAndGetIt(processInstance.getId(), "calledTask"); final ProcessInstance calledProcessInstance = getProcessAPI() .getProcessInstance(calledStep.getParentProcessInstanceId()); getProcessAPI().sendMessage("MyMessage", new ExpressionBuilder().createConstantStringExpression("pMessageBoundary"), new ExpressionBuilder().createConstantStringExpression(BOUNDARY_NAME), null); waitForUserTaskAndExecuteIt(processInstance, "exceptionStep", user); waitForProcessToBeInState(calledProcessInstance, ProcessInstanceState.ABORTED); waitForProcessToFinish(processInstance); waitForArchivedActivity(calledStep.getId(), TestStates.ABORTED); checkWasntExecuted(processInstance, "step2"); } finally { disableAndDeleteProcess(processDefinition, calledProcessDefinition); } } @Test public void messageBoundaryEventOnCallActivityNotTriggered() throws Exception { final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnCallActivity(); final ProcessDefinition calledProcessDefinition = deployAndEnableSimpleProcess("calledProcess", "calledTask"); try { final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final ActivityInstance calledStep = waitForUserTaskAndGetIt(processInstance.getId(), "calledTask"); final ProcessInstance calledProcessInstance = getProcessAPI() .getProcessInstance(calledStep.getParentProcessInstanceId()); assignAndExecuteStep(calledStep, user.getId()); final long step2Id = waitForUserTask(processInstance.getId(), "step2"); getProcessAPI().sendMessage("MyMessage", new ExpressionBuilder().createConstantStringExpression("pMessageBoundary"), new ExpressionBuilder().createConstantStringExpression(BOUNDARY_NAME), null); waitForProcessToFinish(calledProcessInstance); assignAndExecuteStep(step2Id, user); waitForProcessToFinish(processInstance); checkWasntExecuted(processInstance, "exceptionStep"); } finally { disableAndDeleteProcess(processDefinition, calledProcessDefinition); } } @Test public void messageBoundaryEventTriggeredOnSequentialMultiInstance() throws Exception { final int loopCardinality = 4; final boolean isSequential = true; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnMultiInstance( loopCardinality, isSequential); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance.getId(), "step1"); getProcessAPI().sendMessage("MyMessage", new ExpressionBuilder() .createConstantStringExpression("processWithBoundaryMessageEventAndMultiInstance"), new ExpressionBuilder().createConstantStringExpression(BOUNDARY_NAME), null); waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user); waitForProcessToFinish(processInstance); waitForArchivedActivity(step1Id, TestStates.ABORTED); checkWasntExecuted(processInstance, "step2"); disableAndDeleteProcess(processDefinition); } @Test public void messageBoundaryEventNotTriggeredOnSequentialMultiInstance() throws Exception { final int loopCardinality = 3; final boolean isSequential = true; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnMultiInstance( loopCardinality, isSequential); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); for (int i = 0; i < loopCardinality; i++) { waitForUserTaskAndExecuteIt(processInstance, "step1", user); } final long step2Id = waitForUserTask(processInstance.getId(), "step2"); getProcessAPI().sendMessage("MyMessage1", new ExpressionBuilder().createConstantStringExpression("pMessageBoundary"), new ExpressionBuilder().createConstantStringExpression("step1"), null); assignAndExecuteStep(step2Id, user); waitForProcessToFinish(processInstance); checkWasntExecuted(processInstance, "exceptionStep"); disableAndDeleteProcess(processDefinition); } @Test public void messageBoundaryEventTriggeredOnParallelMultiInstance() throws Exception { final int loopCardinality = 4; final boolean isSequential = false; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnMultiInstance( loopCardinality, isSequential); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final List pendingTasks = waitForPendingTasks(user, loopCardinality); for (final HumanTaskInstance humanTaskInstance : pendingTasks) { assertEquals("step1", humanTaskInstance.getName()); } getProcessAPI().sendMessage("MyMessage", new ExpressionBuilder() .createConstantStringExpression("processWithBoundaryMessageEventAndMultiInstance"), new ExpressionBuilder().createConstantStringExpression(BOUNDARY_NAME), null); waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user); waitForProcessToFinish(processInstance); for (final HumanTaskInstance humanTaskInstance : pendingTasks) { waitForArchivedActivity(humanTaskInstance.getId(), TestStates.ABORTED); } checkWasntExecuted(processInstance, "step2"); disableAndDeleteProcess(processDefinition); } @Test public void messageBoundaryEventNotTriggeredOnParallelMultiInstance() throws Exception { final int loopCardinality = 3; final boolean isSequential = false; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnMultiInstance( loopCardinality, isSequential); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); for (int i = 0; i < loopCardinality; i++) { waitForUserTaskAndExecuteIt(processInstance, "step1", user); } final long step2Id = waitForUserTask(processInstance.getId(), "step2"); getProcessAPI().sendMessage("MyMessage1", new ExpressionBuilder().createConstantStringExpression("processWithMultiInstanceAndBoundaryEvent"), new ExpressionBuilder().createConstantStringExpression("step1"), null); assignAndExecuteStep(step2Id, user); checkWasntExecuted(processInstance, "exceptionStep"); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Test public void messageBoundaryEventTriggeredOnLoopActivity() throws Exception { final int loopMax = 3; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnLoopActivity( loopMax); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance.getId(), "step1"); getProcessAPI().sendMessage("MyMessage", new ExpressionBuilder().createConstantStringExpression("processWithLoopActivityAndBoundaryEvent"), new ExpressionBuilder().createConstantStringExpression(BOUNDARY_NAME), null); waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user); waitForProcessToFinish(processInstance); waitForArchivedActivity(step1Id, TestStates.ABORTED); checkWasntExecuted(processInstance, "step2"); disableAndDeleteProcess(processDefinition); } @Test public void messageBoundaryEventNotTriggeredOnLoopActivity() throws Exception { final int loopMax = 2; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEventOnLoopActivity( loopMax); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); for (int i = 0; i < loopMax; i++) { waitForUserTaskAndExecuteIt(processInstance, "step1", user); } final long step2Id = waitForUserTask(processInstance.getId(), "step2"); getProcessAPI().sendMessage("MyMessage1", new ExpressionBuilder().createConstantStringExpression("processWithLoopActivityAndBoundaryEvent"), new ExpressionBuilder().createConstantStringExpression("step1"), null); assignAndExecuteStep(step2Id, user); waitForProcessToFinish(processInstance); checkWasntExecuted(processInstance, "exceptionStep"); disableAndDeleteProcess(processDefinition); } @Test public void process_with_call_activity_aborted_by_boundary_event_should_complete_along_with_its_target_process() throws Exception { // given: final DesignProcessDefinition subProcess = new ProcessDefinitionBuilder() .createNewInstance("SubProcessWith2AutomaticTasks", "2.7") .addAutomaticTask("sub1").addAutomaticTask("sub2") .addTransition("sub1", "sub2") .getProcess(); final DesignProcessDefinition parentProcess = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithCallActivityAborted", "7.3") .addCallActivity("call", new ExpressionBuilder().createConstantStringExpression(subProcess.getName()), new ExpressionBuilder().createConstantStringExpression(subProcess.getVersion())) .addBoundaryEvent("boundary", true).addMessageEventTrigger("abortCallActivity") .addEndEvent("end") .addTransition("boundary", "end") .getProcess(); getProcessAPI().deployAndEnableProcess(subProcess); ProcessDefinition parentProcessDefinition = getProcessAPI().deployAndEnableProcess(parentProcess); assertNoErrorAfterXAttemps(5, () -> { getProcessAPI().startProcess(parentProcessDefinition.getId()); getProcessAPI().sendMessage("abortCallActivity", new ExpressionBuilder().createConstantStringExpression("ProcessWithCallActivityAborted"), new ExpressionBuilder().createConstantStringExpression("boundary"), Collections.emptyMap()); await().until( () -> getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.LAST_UPDATE_ASC), hasSize(0)); }, e -> logAllProcesses()); } @Test public void process_with_call_activity_aborted_by_terminate_end_event_should_complete_along_with_its_target_process() throws Exception { // given: final DesignProcessDefinition subProcess = new ProcessDefinitionBuilder() .createNewInstance("SubProcessWith2AutomaticTasks", "2.7") .addAutomaticTask("sub1").addAutomaticTask("sub2") .addTransition("sub1", "sub2") .getProcess(); final DesignProcessDefinition parentProcess = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithCallActivityAborted", "7.3") .addStartEvent("start").addCallActivity("call", new ExpressionBuilder().createConstantStringExpression(subProcess.getName()), new ExpressionBuilder().createConstantStringExpression(subProcess.getVersion())) // will loop forever until the terminate and event aborts it: .addLoop(false, new ExpressionBuilder().createConstantBooleanExpression(true)).addAutomaticTask("auto1") .addAutomaticTask("auto2").addEndEvent("end").addTerminateEventTrigger() .addTransition("start", "call") .addTransition("start", "auto1") .addTransition("auto1", "auto2") .addTransition("auto2", "end").getProcess(); getProcessAPI().deployAndEnableProcess(subProcess); ProcessDefinition parentProcessDefinition = getProcessAPI().deployAndEnableProcess(parentProcess); assertNoErrorAfterXAttemps(5, () -> { getProcessAPI().startProcess(parentProcessDefinition.getId()); await().until( () -> getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.LAST_UPDATE_ASC), hasSize(0)); }, e -> logAllProcesses()); } protected void logAllProcesses() throws SearchException { logger.error("Found processes instances"); for (ProcessInstance p : getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.LAST_UPDATE_ASC)) { logger.error(" * " + p); } logger.error("Found flow nodes"); for (FlowNodeInstance p : getProcessAPI() .searchFlowNodeInstances(new SearchOptionsBuilder(0, 100).done()).getResult()) { logger.error(" * " + p); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/MessageEventIT.java ================================================ /** * 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.event; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.*; import static org.junit.Assume.assumeNotNull; 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.Map.Entry; import java.util.concurrent.TimeUnit; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ActivityStates; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.SendEventException; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.CatchMessageEventTriggerDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ThrowMessageEventTriggerBuilder; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.operation.LeftOperand; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.test.StartProcessUntilStep; import org.bonitasoft.engine.test.TestStates; import org.bonitasoft.engine.test.wait.WaitForEvent; import org.bonitasoft.engine.test.wait.WaitForStep; import org.junit.Test; @SuppressWarnings("javadoc") public class MessageEventIT extends AbstractEventIT { /* * 1 receiveProcess, 1 sendProcess, Message goes from EndEvent to StartEvent * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess) * check receiveProcess has started and halt on the user task. */ @Test public void messageStartEventMessageSentAfterEnable() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent( START_WITH_MESSAGE_PROCESS_NAME, "startEvent"); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent( Collections. emptyMap(), Collections. emptyList()); final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId()); waitForProcessToFinish(sendMessageProcessInstance); waitForUserTask(START_WITH_MESSAGE_STEP1_NAME); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } /* * 1 receiveProcess, 1 sendProcess, Message goes from EndEvent to StartEvent * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess) * check receiveProcess has started and halt on the user task. */ @Test public void messageStartEventMessageSentAfterEnableWithNoTargetFlowNode() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent( START_WITH_MESSAGE_PROCESS_NAME, null); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent( Collections. emptyMap(), Collections. emptyList()); final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId()); waitForProcessToFinish(sendMessageProcessInstance); waitForUserTask(START_WITH_MESSAGE_STEP1_NAME); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } /* * Differs only by the dynamic with messageStartEventMessageSentAfterEnable (Before instead of After) * dynamic -> deployAndEnable(sendProcess), startProcess(sendProcess), deployAndEnable(receiveProcess) */ @Test public void messageStartEventMessageSentBeforeEnable() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent( START_WITH_MESSAGE_PROCESS_NAME, "startEvent"); // the message will be send before the target process is deployed final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId()); waitForProcessToFinish(sendMessageProcessInstance); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent( Collections. emptyMap(), Collections. emptyList()); waitForUserTask(START_WITH_MESSAGE_STEP1_NAME); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } /* * 1 receiveProcess, 1 sendProcess, Message goes from EndEvent to IntermediateEvent * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(receiveProcess), * startProcess(sendProcess) * checks : receiveProcess stop on catchEvent, sendProcess is finished, receiveProcess continue and reaches user * task. */ @Test public void messageIntermediateCatchEventMessageSentAfterCatch() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent( CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null, null, null); final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); waitForEventInWaitingState(receiveMessageProcessInstance, CATCH_EVENT_NAME); final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId()); waitForProcessToFinish(sendMessageProcessInstance); waitForUserTask(CATCH_MESSAGE_STEP1_NAME); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } @Test public void messageIntermediateCatchEventMessageSentBeforeCatch() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent( CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null, null, null); final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId()); waitForProcessToFinish(sendMessageProcessInstance); final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); Thread.sleep(100);// small sleep but don't wait for the event to be waiting, it might happen that that event is already matched at this point waitForUserTask(receiveMessageProcessInstance, CATCH_MESSAGE_STEP1_NAME); disableAndDeleteProcess(receiveMessageProcess); disableAndDeleteProcess(sendMessageProcess); } /* * Verify that correlation to determine the targeted process work well (Both receiveProcess are instance of the same * processDefinition) * 2 receiveProcess, 2 sendProcess, sendProcess1[1, Doe] -> receiveProcess1[1], sendProcess2[2,Doe Doe] -> * receiveProcess2[2] * checks : receiveProcesses stop on catchEvent, sendProcess1 is finished. */ @Test public void messageIntermediateCatchEventWithCorrelations() throws Exception { final Map data = new HashMap<>(); data.put("docNumber", Integer.class.getName()); data.put("lastName", String.class.getName()); final ArrayList> correlations = new ArrayList<>(1); final Expression docCorrelationKey = new ExpressionBuilder().createConstantStringExpression("docKey"); final Expression docCorrelationValue = new ExpressionBuilder().createDataExpression("docNumber", Integer.class.getName()); correlations.add(Map.entry(docCorrelationKey, docCorrelationValue)); final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent( CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, correlations, data, null, null); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEventAnd1Correlation(); // start two instances of a receive message process final ProcessInstance receiveMessageProcessInstance1 = getProcessAPI().startProcess( receiveMessageProcess.getId(), Arrays.asList( buildAssignOperation("docRef", "1", Integer.class.getName(), ExpressionType.TYPE_CONSTANT)), null); final ProcessInstance receiveMessageProcessInstance2 = getProcessAPI().startProcess( receiveMessageProcess.getId(), Arrays.asList( buildAssignOperation("docRef", "2", Integer.class.getName(), ExpressionType.TYPE_CONSTANT)), null); // wait the event node instance waitForEvent(receiveMessageProcessInstance1, CATCH_EVENT_NAME, TestStates.WAITING); waitForEvent(receiveMessageProcessInstance2, CATCH_EVENT_NAME, TestStates.WAITING); // instantiate a process containing correlations matching with receiveMessageProcessInstance1 final ProcessInstance sendMessageProcessInstance1 = getProcessAPI().startProcess( sendMessageProcess.getId(), Arrays.asList( buildAssignOperation("docNumber", "1", Integer.class.getName(), ExpressionType.TYPE_CONSTANT), buildAssignOperation("lastName", "Doe", String.class.getName(), ExpressionType.TYPE_CONSTANT)), null); waitForProcessToFinish(sendMessageProcessInstance1); assertNotNull(waitForUserTask(receiveMessageProcessInstance1.getId(), CATCH_MESSAGE_STEP1_NAME)); waitForEventInWaitingState(receiveMessageProcessInstance2, CATCH_EVENT_NAME); // instantiate a process containing correlations matching with receiveMessageProcessInstance2 final ProcessInstance sendMessageProcessInstance2 = getProcessAPI().startProcess( sendMessageProcess.getId(), Arrays.asList( buildAssignOperation("docNumber", "2", Integer.class.getName(), ExpressionType.TYPE_CONSTANT), buildAssignOperation("lastName", "Doe Doe", String.class.getName(), ExpressionType.TYPE_CONSTANT)), null); waitForProcessToFinish(sendMessageProcessInstance2); assertNotNull(waitForUserTask(receiveMessageProcessInstance2.getId(), CATCH_MESSAGE_STEP1_NAME)); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } /* * Verify that if a sendProcess has for targets 2 instances of the same ProcessDefinition and no correlation key is * defined * (equivalent to matching keys), exactly one of the receiveProcess catches the message. * 2 receiveProcess, 1 sendProcess, sendProcess1 -> receiveProcess1, sendProcess2 -> receiveProcess2[2] * checks : receiveProcesses stop on catchEvent, sendProcess1 is finished. */ @Test public void messageIntermediateCatchEventWithoutCorrelations() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent( CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null, null, null); // start two instances of a receive message process final ProcessInstance receiveMessageProcessInstance1 = getProcessAPI() .startProcess(receiveMessageProcess.getId()); final ProcessInstance receiveMessageProcessInstance2 = getProcessAPI() .startProcess(receiveMessageProcess.getId()); // wait the event node instance waitForEvent(receiveMessageProcessInstance1, CATCH_EVENT_NAME, TestStates.WAITING); waitForEvent(receiveMessageProcessInstance2, CATCH_EVENT_NAME, TestStates.WAITING); // instantiate a process containing whom the targetProcess is of the ProcessDefinition receiveMessageProcess final ProcessInstance sendMessageProcessInstance1 = getProcessAPI().startProcess(sendMessageProcess.getId()); waitForProcessToFinish(sendMessageProcessInstance1); final HumanTaskInstance waitForUserTask = waitForUserTaskAndGetIt(CATCH_MESSAGE_STEP1_NAME); final long processInstance = waitForUserTask.getRootContainerId(); assertTrue(processInstance == receiveMessageProcessInstance1.getId() || processInstance == receiveMessageProcessInstance2.getId()); assertEquals(1, getProcessAPI() .getPendingHumanTaskInstances(user.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT).size()); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } /* * Verify that even if matching correlations keys are not in the same order in the receiveProcess and sendProcess, * the message is transmitted. * 1 receiveProcess [value1,value2], 1 sendProcesss[value1,value2], message goes from endEvent to intermediateEvent * checks : receiveProcess stop on catchEvent , sendProcess is finished, receiveProcess continues and reaches user * task. */ @Test public void correlationKeyInWrongOrderShouldWork() throws Exception { final ArrayList> correlations = new ArrayList<>(2); correlations .add(Map.entry(new ExpressionBuilder().createConstantStringExpression("aKey"), new ExpressionBuilder() .createConstantStringExpression("value1"))); correlations.add( Map.entry(new ExpressionBuilder().createConstantStringExpression("bKey"), new ExpressionBuilder() .createConstantStringExpression("value2"))); final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent( CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, correlations, null, null, null); final ArrayList> correlationsReceive = new ArrayList<>(2); correlationsReceive.add( Map.entry(new ExpressionBuilder().createConstantStringExpression("bKey"), new ExpressionBuilder() .createConstantStringExpression("value2"))); correlationsReceive.add( Map.entry(new ExpressionBuilder().createConstantStringExpression("aKey"), new ExpressionBuilder() .createConstantStringExpression("value1"))); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent( correlationsReceive, null, null); // start two instances of a receive message process final ProcessInstance receiveMessageProcessInstance1 = getProcessAPI() .startProcess(receiveMessageProcess.getId()); // wait the event node instance waitForEvent(receiveMessageProcessInstance1, CATCH_EVENT_NAME, TestStates.WAITING); // instantiate a process containing correlations matching with receiveMessageProcessInstance1 final ProcessInstance sendMessageProcessInstance1 = getProcessAPI().startProcess(sendMessageProcess.getId()); waitForProcessToFinish(sendMessageProcessInstance1); waitForUserTask(receiveMessageProcessInstance1, CATCH_MESSAGE_STEP1_NAME); // waitForStep(100, 5000, "userTask1", receiveMessageProcessInstance1); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } /* * Verify that a sendProcess must have at least all correlation keys of the receiveProcess for the message to be * transmitted. * 1 receiveProcess, 2 sendProcess, receiveProcess1, sendProcess1, sendProcess2 * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess) * checks : sendProcess is finished, , receiveProcess start and stop on user task, data is transmitted to the * receiveProcess */ @Test public void multipleCorrelationsKeys() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEventAndCorrelation(); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEventAnd2Correlations(); // start a instance of a receive message process final ProcessInstance receiveMessageProcessInstance1 = getProcessAPI().startProcess( receiveMessageProcess.getId(), Arrays.asList( buildAssignOperation("docRef", "1", Integer.class.getName(), ExpressionType.TYPE_CONSTANT), buildAssignOperation("name", "Doe Doe", String.class.getName(), ExpressionType.TYPE_CONSTANT)), null); // wait the event node instance waitForEvent(receiveMessageProcessInstance1, CATCH_EVENT_NAME, TestStates.WAITING); checkUserHasNoPendingTasks(); // instantiate a process having one one correlation key matching, the process must not go further final ProcessInstance sendMessageProcessInstance1 = getProcessAPI().startProcess( sendMessageProcess.getId(), Arrays.asList( buildAssignOperation("docNumber", "1", Integer.class.getName(), ExpressionType.TYPE_CONSTANT), buildAssignOperation("lastName", "Doe 2", String.class.getName(), ExpressionType.TYPE_CONSTANT)), null); waitForProcessToFinish(sendMessageProcessInstance1); // 1 sec because it's an assert false and we forced matching of event assertFalse(new WaitForStep(DEFAULT_REPEAT_EACH, 500, CATCH_MESSAGE_STEP1_NAME, receiveMessageProcessInstance1.getId(), getProcessAPI()).waitUntil()); // instantiate a process having both two correlation keys matching, the process must go further final ProcessInstance sendMessageProcessInstance2 = getProcessAPI().startProcess( sendMessageProcess.getId(), Arrays.asList( buildAssignOperation("docNumber", "1", Integer.class.getName(), ExpressionType.TYPE_CONSTANT), buildAssignOperation("lastName", "Doe Doe", String.class.getName(), ExpressionType.TYPE_CONSTANT)), null); waitForProcessToFinish(sendMessageProcessInstance2); waitForUserTask(receiveMessageProcessInstance1, CATCH_MESSAGE_STEP1_NAME); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } private Operation buildAssignOperation(final String dataInstanceName, final String newConstantValue, final String className, final ExpressionType expressionType) throws InvalidExpressionException { final LeftOperand leftOperand = new LeftOperandBuilder().createNewInstance().setName(dataInstanceName).done(); final Expression expression = new ExpressionBuilder().createNewInstance(dataInstanceName) .setContent(newConstantValue) .setExpressionType(expressionType.name()).setReturnType(className).done(); final Operation operation; operation = new OperationBuilder().createNewInstance().setOperator("=").setLeftOperand(leftOperand) .setType(OperatorType.ASSIGNMENT) .setRightOperand(expression).done(); return operation; } /* * 1 receiveProcess, 1 sendProcess, Message goes from IntermediateEvent to StartEvent * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess) * checks : sendProcess is finished, receiveProcess start and stop on user task. */ @Test public void messageIntermediateThrowEventMessageSentAfterEnable() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithIntermediateThrowMessageEvent( START_WITH_MESSAGE_PROCESS_NAME, "startEvent"); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent( Collections. emptyMap(), Collections. emptyList()); final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId()); waitForProcessToFinish(sendMessageProcessInstance); waitForUserTask(START_WITH_MESSAGE_STEP1_NAME); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } @Test public void messageIntermediateThrow2EventMessages() throws Exception { final List messages = new ArrayList<>(); messages.add("catchMessage"); messages.add("startMessage"); final List targetProcesses = new ArrayList<>(); targetProcesses.add(CATCH_MESSAGE_PROCESS_NAME); targetProcesses.add(START_WITH_MESSAGE_PROCESS_NAME); final List targetFlowNodes = new ArrayList<>(); targetFlowNodes.add(CATCH_EVENT_NAME); targetFlowNodes.add("startEvent"); final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithIntermediateThrowMessageEvent(messages, targetProcesses, targetFlowNodes); final ProcessDefinition startWithMessageProcess = deployAndEnableProcessWithStartMessageEvent( START_WITH_MESSAGE_PROCESS_NAME, "startMessage", null, null); final ProcessDefinition catchMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent( CATCH_MESSAGE_PROCESS_NAME, "catchMessage", null, null, null); final ProcessInstance catchMessageProcessInstance = getProcessAPI().startProcess(catchMessageProcess.getId()); waitForEventInWaitingState(catchMessageProcessInstance, CATCH_EVENT_NAME); final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId()); waitForProcessToFinish(sendMessageProcessInstance); checkNbPendingTaskOf(2, user); final List taskInstances = getProcessAPI() .getPendingHumanTaskInstances(user.getId(), 0, 10, ActivityInstanceCriterion.NAME_ASC); assertEquals(2, taskInstances.size()); assertEquals(CATCH_MESSAGE_STEP1_NAME, taskInstances.get(0).getName()); assertEquals(START_WITH_MESSAGE_STEP1_NAME, taskInstances.get(1).getName()); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(startWithMessageProcess); disableAndDeleteProcess(catchMessageProcess); } /* * 1 receiveProcess, 1 sendProcess, Message contains datas goes from EndEvent to StartEvent * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess) * checks : sendProcess is finished, , receiveProcess start and stop on user task, data is transmitted to the * receiveProcess */ @Test public void dataTransferFromMessageEndEventToStartMessageEvent() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent( START_WITH_MESSAGE_PROCESS_NAME, "startEvent", null, Collections.singletonMap("lastName", String.class.getName()), Collections.singletonMap("lName", String.class.getName()), Collections.singletonMap("lName", "lastName")); final List catchMessageOperations = Collections .singletonList(buildAssignOperation("name", "lName", String.class.getName(), ExpressionType.TYPE_VARIABLE)); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent( Collections.singletonMap("name", String.class.getName()), catchMessageOperations); final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId(), Arrays.asList( buildAssignOperation("lastName", "Doe", String.class.getName(), ExpressionType.TYPE_CONSTANT)), null); waitForProcessToFinish(sendMessageProcessInstance); // at the first test some time the cron job time some time before executing final HumanTaskInstance userTask = waitForUserTaskAndGetIt(START_WITH_MESSAGE_STEP1_NAME); final DataInstance dataInstance = getProcessAPI().getProcessDataInstance("name", userTask.getRootContainerId()); assertEquals("Doe", dataInstance.getValue()); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } /* * 1 receiveProcess, 1 sendProcess, Message contains datas goes from EndEvent to IntermediateEvent * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess) * checks : receiveProcess start and stop on catchEvent, sendProcess is finished, , receiveProcess continues and * reaches user task , data is transmitted to * the receiveProcess. */ @Test public void dataTransferFromMessageEndEventToMessageIntermediateCatchEvent() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithEndMessageEvent( CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, null, Collections.singletonMap("lastName", String.class.getName()), Collections.singletonMap("lName", String.class.getName()), Collections.singletonMap("lName", "lastName")); final List catchMessageOperations = Collections .singletonList(buildAssignOperation("name", "lName", String.class.getName(), ExpressionType.TYPE_VARIABLE)); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null, Collections.singletonMap("name", String.class.getName()), catchMessageOperations); // start a instance of a receive message process final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); // wait the event node instance waitForEventInWaitingState(receiveMessageProcessInstance, CATCH_EVENT_NAME); DataInstance dataInstance = getProcessAPI().getProcessDataInstance("name", receiveMessageProcessInstance.getId()); assertNull("Data is not null", dataInstance.getValue()); final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId(), Arrays.asList( buildAssignOperation("lastName", "Doe", String.class.getName(), ExpressionType.TYPE_CONSTANT)), null); waitForProcessToFinish(sendMessageProcessInstance); waitForUserTask(receiveMessageProcessInstance, CATCH_MESSAGE_STEP1_NAME); dataInstance = getProcessAPI().getProcessDataInstance("name", receiveMessageProcessInstance.getId()); assertEquals("Doe", dataInstance.getValue()); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } /* * Verify receiveProcess receive message targeting it, even if sent before its existence. * 1 receiveProcess, 1 sendProcess, Message goes from IntermediateEvent to IntermediateEvent * dynamic -> deployAndEnable(sendProcess), startProcess(sendProcess), deployAndEnable(receiveProcess), * startProcess(receiveProcess) * checks : sendProcess is finished, receiveProcess reaches catchEvent and continue (found message sent by * sendProcess) and reaches user task. */ @Test public void messageSentProcessFinishBeforeReceiveProcessIsEnabled() throws Exception { final ProcessDefinition sendMessageProcess = deployAndEnableProcessWithIntermediateThrowMessageEvent( CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME); final ProcessInstance sendMessageProcessInstance = getProcessAPI().startProcess(sendMessageProcess.getId()); waitForProcessToFinish(sendMessageProcessInstance); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null, null, null); final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); Thread.sleep(100);// small sleep but don't wait for the event to be waiting, it might happen that that event is already matched at this point waitForUserTask(receiveMessageProcessInstance, CATCH_MESSAGE_STEP1_NAME); disableAndDeleteProcess(sendMessageProcess); disableAndDeleteProcess(receiveMessageProcess); } /* * Test where the message goes from a throwEvent to a catchEvent belonging to the same process hence the same pool * (forbidden by BPMN 2.0) * But the studio forbid this case, so this should never happen in the Engine. */ @Test public void messageEventIntraProcess() throws Exception { final ProcessDefinition sendAndReceiveMessageProcess = deployAndEnableProcessWithIntraMessageEvent( "sendAndReceiveMessageProcess", CATCH_EVENT_NAME); final ProcessInstance sendAndReceiveMessageProcessInstance = getProcessAPI() .startProcess(sendAndReceiveMessageProcess.getId()); final long step2Id = waitForUserTask(sendAndReceiveMessageProcessInstance, "userTask2"); waitForEventInWaitingState(sendAndReceiveMessageProcessInstance, CATCH_EVENT_NAME); assignAndExecuteStep(step2Id, user); waitForUserTask(sendAndReceiveMessageProcessInstance, "userTask3"); disableAndDeleteProcess(sendAndReceiveMessageProcess); } /* * 1 receiveProcess, 2 sendProcess, 2 Messages go from EndEvent to IntermediateEvent * dynamic -> deployAndEnable(sendProcess), deployAndEnable(receiveProcess), startProcess(sendProcess), * startProcess(receiveProcess) * checks : sendProcess is finished, receiveProcess reaches catchEvent and continue (found message sent by * sendProcess) and reaches user task. */ @Test public void messageIntermediateCatchEventMessageMultiSend() throws Exception { final ProcessDefinition sendMessageProcess1 = deployAndEnableProcessWithEndMessageEvent("sendMessageProcess1", MESSAGE_NAME, CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, null, null, null, null); final ProcessDefinition sendMessageProcess2 = deployAndEnableProcessWithEndMessageEvent("sendMessageProcess2", MESSAGE_NAME, CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, null, null, null, null); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null, null, null); final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); waitForEventInWaitingState(receiveMessageProcessInstance, CATCH_EVENT_NAME); final ProcessInstance sendMessageProcessInstance1 = getProcessAPI().startProcess(sendMessageProcess1.getId()); waitForProcessToFinish(sendMessageProcessInstance1); waitForUserTask(CATCH_MESSAGE_STEP1_NAME); disableAndDeleteProcess(sendMessageProcess1); disableAndDeleteProcess(sendMessageProcess2); disableAndDeleteProcess(receiveMessageProcess); } @Test public void deleteProcessInstanceShouldDeleteWaitingEvents() throws Exception { final ProcessDefinition processDefinition = deployAndEnableProcessWithIntermediateCatchMessageEvent(null, null, null); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); assumeNotNull(processInstance); waitForEventInWaitingState(processInstance, CATCH_EVENT_NAME); final long processInstanceId = processInstance.getId(); getProcessAPI().deleteProcessInstance(processInstanceId); assertThat(new WaitForEvent(50, 1000, CATCH_EVENT_NAME, processInstanceId, getProcessAPI()).waitUntil(), is(false)); disableAndDeleteProcess(processDefinition); } @Test public void sendMessageViaAPIToStartMessageEvent() throws Exception { final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent( Collections. emptyMap(), Collections. emptyList()); // send message sendMessage(MESSAGE_NAME, START_WITH_MESSAGE_PROCESS_NAME, "startEvent", null); waitForUserTask(START_WITH_MESSAGE_STEP1_NAME); disableAndDeleteProcess(receiveMessageProcess); } protected void sendMessage(final String messageName, final String targetProcessName, final String targetFlowNodeName, final Map messageContent) throws Exception { final Expression targetProcessExpression = new ExpressionBuilder() .createConstantStringExpression(targetProcessName); final Expression targetFlowNodeExpression = new ExpressionBuilder() .createConstantStringExpression(targetFlowNodeName); getProcessAPI().sendMessage(messageName, targetProcessExpression, targetFlowNodeExpression, messageContent); } private void sendMessage(final String messageName, final String targetProcessName, final String targetFlowNodeName, final Map messageContent, final Map correlations) throws Exception { final Expression targetProcessExpression = new ExpressionBuilder() .createConstantStringExpression(targetProcessName); final Expression targetFlowNodeExpression = new ExpressionBuilder() .createConstantStringExpression(targetFlowNodeName); getProcessAPI().sendMessage(messageName, targetProcessExpression, targetFlowNodeExpression, messageContent, correlations); } @Test public void sendMessageViaAPIToIntermediateMessageEvent() throws Exception { final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null, null, null); final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); waitForEventInWaitingState(receiveMessageProcessInstance, CATCH_EVENT_NAME); checkUserHasNoPendingTasks(); // send message sendMessage(MESSAGE_NAME, CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, null); waitForUserTask(CATCH_MESSAGE_STEP1_NAME); disableAndDeleteProcess(receiveMessageProcess); } @Test public void sendMessageWithDataViaAPIToStartMessageEvent() throws Exception { final Expression lastNameDisplay = new ExpressionBuilder().createConstantStringExpression("lName"); final Expression lastNameValue = new ExpressionBuilder().createConstantStringExpression("Doe"); final List catchMessageOperations = Collections .singletonList(buildAssignOperation("name", "lName", String.class.getName(), ExpressionType.TYPE_VARIABLE)); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent( Collections.singletonMap("name", String.class.getName()), catchMessageOperations); checkUserHasNoPendingTasks(); sendMessage(MESSAGE_NAME, START_WITH_MESSAGE_PROCESS_NAME, "startEvent", Collections.singletonMap(lastNameDisplay, lastNameValue)); // at the first test some time the cron job time some time before executing final HumanTaskInstance taskInstance = waitForUserTaskAndGetIt(START_WITH_MESSAGE_STEP1_NAME); final DataInstance dataInstance = getProcessAPI().getProcessDataInstance("name", taskInstance.getRootContainerId()); assertEquals("Doe", dataInstance.getValue()); // Clean up disableAndDeleteProcess(receiveMessageProcess); } @Test public void sendMessageTwiceTriggersTwoStartMessageEvents() throws Exception { final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithStartMessageEvent( Collections. emptyMap(), Collections. emptyList()); sendMessage(MESSAGE_NAME, START_WITH_MESSAGE_PROCESS_NAME, "startEvent", Collections. emptyMap()); final ActivityInstance taskFirstProcInst = waitForUserTaskAndGetIt(START_WITH_MESSAGE_STEP1_NAME); sendMessage(MESSAGE_NAME, START_WITH_MESSAGE_PROCESS_NAME, "startEvent", Collections. emptyMap()); final ActivityInstance taskSecondProcInst = waitForUserTaskAndGetIt(START_WITH_MESSAGE_STEP1_NAME); assertNotEquals(taskFirstProcInst.getId(), taskSecondProcInst.getId()); assertNotEquals(taskFirstProcInst.getParentProcessInstanceId(), taskSecondProcInst.getParentProcessInstanceId()); disableAndDeleteProcess(receiveMessageProcess); } private void checkUserHasNoPendingTasks() { final List taskInstances = getProcessAPI() .getPendingHumanTaskInstances(user.getId(), 0, 10, ActivityInstanceCriterion.NAME_ASC); assertEquals(0, taskInstances.size()); } @Test public void sendMessageWithCorrelationViaAPIToIntermediateMessageEvent() throws Exception { final Expression docCorrelationKey = new ExpressionBuilder().createConstantStringExpression("docKey"); final Expression docCorrelationValue = new ExpressionBuilder().createConstantIntegerExpression(1); final Expression nameCorrelationKey = new ExpressionBuilder().createConstantStringExpression("nameKey"); final Expression nameCorrelationValue1 = new ExpressionBuilder().createConstantStringExpression("Doe 2"); final Expression nameCorrelationValue2 = new ExpressionBuilder().createConstantStringExpression("Doe Doe"); final Map correlations1 = new HashMap<>(2); correlations1.put(docCorrelationKey, docCorrelationValue); correlations1.put(nameCorrelationKey, nameCorrelationValue1); // don't match final Map correlations2 = new HashMap<>(2); correlations2.put(docCorrelationKey, docCorrelationValue); correlations2.put(nameCorrelationKey, nameCorrelationValue2); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEventAnd2Correlations(); // start a instance of a receive message process final ProcessInstance receiveMessageProcessInstance1 = getProcessAPI().startProcess( receiveMessageProcess.getId(), Arrays.asList( buildAssignOperation("docRef", "1", Integer.class.getName(), ExpressionType.TYPE_CONSTANT), buildAssignOperation("name", "Doe Doe", String.class.getName(), ExpressionType.TYPE_CONSTANT)), null); // wait the event node instance waitForEvent(receiveMessageProcessInstance1, CATCH_EVENT_NAME, TestStates.WAITING); // send a message having only one correlation key matching, the process must not go further sendMessage(MESSAGE_NAME, CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, Collections. emptyMap(), correlations1); assertFalse(new WaitForStep(50, 1000, "userTask1", receiveMessageProcessInstance1.getId(), getProcessAPI()) .waitUntil()); // send a message having both two correlations keys matching, the process must go further sendMessage(MESSAGE_NAME, CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, Collections. emptyMap(), correlations2); waitForUserTask(receiveMessageProcessInstance1, CATCH_MESSAGE_STEP1_NAME); disableAndDeleteProcess(receiveMessageProcess); } @Test public void sendMessageWithDataViaAPIToIntermediateCatchMessageEvent() throws Exception { final List catchMessageOperations = Collections .singletonList(buildAssignOperation("name", "lName", String.class.getName(), ExpressionType.TYPE_VARIABLE)); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithIntermediateCatchMessageEvent(null, Collections.singletonMap("name", String.class.getName()), catchMessageOperations); // start a instance of a receive message process final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); // wait the event node instance waitForEventInWaitingState(receiveMessageProcessInstance, CATCH_EVENT_NAME); DataInstance dataInstance = getProcessAPI().getProcessDataInstance("name", receiveMessageProcessInstance.getId()); assertNull("Data is not null", dataInstance.getValue()); final Expression lastNameDisplay = new ExpressionBuilder().createConstantStringExpression("lName"); final Expression lastNameValue = new ExpressionBuilder().createConstantStringExpression("Doe"); sendMessage(MESSAGE_NAME, CATCH_MESSAGE_PROCESS_NAME, CATCH_EVENT_NAME, Collections.singletonMap(lastNameDisplay, lastNameValue)); waitForUserTask(receiveMessageProcessInstance, CATCH_MESSAGE_STEP1_NAME); dataInstance = getProcessAPI().getProcessDataInstance("name", receiveMessageProcessInstance.getId()); assertEquals("Doe", dataInstance.getValue()); disableAndDeleteProcess(receiveMessageProcess); } @Test public void should_be_able_to_send_a_good_message_even_after_sending_a_bad_one() throws Exception { DesignProcessDefinition process = new ProcessDefinitionBuilder() .createNewInstance(CATCH_MESSAGE_PROCESS_NAME, PROCESS_VERSION) .addActor(ACTOR_NAME) .addShortTextData("processData", null) .addIntermediateCatchEvent("waitingMessage") .addMessageEventTrigger("aMessage") .addOperation(new OperationBuilder().createSetDataOperation("processData", new ExpressionBuilder().createDataExpression("messageData", String.class.getName()))) .addUserTask("step1", ACTOR_NAME) .addTransition("waitingMessage", "step1").getProcess(); final ProcessDefinition receiveMessageProcess = deployAndEnableProcessWithActor(process, ACTOR_NAME, user); final ProcessInstance receiveMessageProcessInstance = getProcessAPI() .startProcess(receiveMessageProcess.getId()); // send a message that match the waiting message but with missing data sendMessage("aMessage", CATCH_MESSAGE_PROCESS_NAME, "waitingMessage", emptyMap()); Thread.sleep(200); // the message should not be handled (no way to check that using the API) Assertions.assertThat(getProcessAPI().getOpenActivityInstances(receiveMessageProcessInstance.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT)).isEmpty(); // send the same message but with the missing data sendMessage("aMessage", CATCH_MESSAGE_PROCESS_NAME, "waitingMessage", singletonMap(string("messageData"), string("Doe"))); // the waiting message is now correctly triggered waitForUserTask(receiveMessageProcessInstance, CATCH_MESSAGE_STEP1_NAME); // we verify that it was really the second message that matched DataInstance dataInstance = getProcessAPI().getProcessDataInstance("processData", receiveMessageProcessInstance.getId()); Assertions.assertThat(dataInstance.getValue()).isEqualTo("Doe"); disableAndDeleteProcess(receiveMessageProcess); } private Expression string(String messageData) throws InvalidExpressionException { return new ExpressionBuilder().createConstantStringExpression(messageData); } @Test(expected = SendEventException.class) public void sendMessageWithTooManyCorrelations() throws Exception { final Map correlations = new HashMap<>(6); correlations.put(new ExpressionBuilder().createConstantStringExpression("key1"), new ExpressionBuilder().createConstantIntegerExpression(1)); correlations.put(new ExpressionBuilder().createConstantStringExpression("key2"), new ExpressionBuilder().createConstantStringExpression("label")); correlations.put(new ExpressionBuilder().createConstantStringExpression("key3"), new ExpressionBuilder().createConstantStringExpression("2")); correlations.put(new ExpressionBuilder().createConstantStringExpression("key4"), new ExpressionBuilder().createConstantStringExpression("Doe 2")); correlations.put(new ExpressionBuilder().createConstantStringExpression("key5"), new ExpressionBuilder().createConstantStringExpression("Doe 2")); correlations.put(new ExpressionBuilder().createConstantStringExpression("key6"), new ExpressionBuilder().createConstantStringExpression("Doe 2")); final Expression targetProcessExpression = new ExpressionBuilder().createConstantStringExpression("p1"); final Expression targetFlowNodeExpression = new ExpressionBuilder().createConstantStringExpression("step1"); getProcessAPI().sendMessage(MESSAGE_NAME, targetProcessExpression, targetFlowNodeExpression, null, correlations); } @Test public void sendMessageToTerminateProcessWithLoop() throws Exception { ProcessDefinition processToKillDefinition = null; ProcessDefinition killerProcessDefinition = null; try { // Process to kill final Expression falseExpr = new ExpressionBuilder().createConstantBooleanExpression(false); final Expression dataExpr = new ExpressionBuilder().createDataExpression("endtask", Boolean.class.getName()); final Expression condition = new ExpressionBuilder().createGroovyScriptExpression("check", "!endtask", Boolean.class.getName(), dataExpr); final Expression loopMax = new ExpressionBuilder().createConstantIntegerExpression(10); final ProcessDefinitionBuilder processToKillDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("ProcessToKill", PROCESS_VERSION); processToKillDefinitionBuilder.addActor(ACTOR_NAME).addStartEvent("ToKillStart").addEndEvent("ToKillEnd") .addTerminateEventTrigger(); processToKillDefinitionBuilder.addData("endtask", Boolean.class.getName(), falseExpr); processToKillDefinitionBuilder.addUserTask("Step1", ACTOR_NAME).addLoop(false, condition, loopMax); processToKillDefinitionBuilder.addUserTask("Step2", ACTOR_NAME); processToKillDefinitionBuilder.addGateway("Gateway", GatewayType.PARALLEL); // Catch Message Event final CatchMessageEventTriggerDefinitionBuilder catchMessageEventTriggerDefinitionBuilder = processToKillDefinitionBuilder .addIntermediateCatchEvent(CATCH_EVENT_NAME).addMessageEventTrigger("msgKiller"); final ArrayList> correlations = new ArrayList<>(1); final Expression correlationKey = new ExpressionBuilder().createConstantStringExpression("key"); final Expression correlationValue = new ExpressionBuilder().createGroovyScriptExpression("getId", "processInstanceId", Long.class.getName(), new ExpressionBuilder().createEngineConstant(ExpressionConstants.PROCESS_INSTANCE_ID)); correlations.add(Map.entry(correlationKey, correlationValue)); addCorrelations(catchMessageEventTriggerDefinitionBuilder, correlations); // Transitions processToKillDefinitionBuilder.addTransition("ToKillStart", "Gateway"); processToKillDefinitionBuilder.addTransition("Gateway", "Step1"); processToKillDefinitionBuilder.addTransition("Step1", "Step2"); processToKillDefinitionBuilder.addTransition("Step2", "ToKillEnd"); processToKillDefinitionBuilder.addTransition("Gateway", CATCH_EVENT_NAME); processToKillDefinitionBuilder.addTransition(CATCH_EVENT_NAME, "ToKillEnd"); processToKillDefinition = deployAndEnableProcessWithActor(processToKillDefinitionBuilder.done(), ACTOR_NAME, user); final StartProcessUntilStep toKillStartProcessAndWaitForTask = startProcessAndWaitForTask( processToKillDefinition.getId(), "Step1"); final ProcessInstance processToKillInstance = toKillStartProcessAndWaitForTask.getProcessInstance(); // Killer process final ProcessDefinitionBuilder killerProcessDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("KillerProcess", PROCESS_VERSION); killerProcessDefinitionBuilder.addActor(ACTOR_NAME).addStartEvent("KillerStart"); killerProcessDefinitionBuilder.addUserTask("Step3", ACTOR_NAME); // Throw Message Event final Expression targetProcess = new ExpressionBuilder().createConstantStringExpression("ProcessToKill"); final Expression targetFlowNode = new ExpressionBuilder().createConstantStringExpression(CATCH_EVENT_NAME); final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder = killerProcessDefinitionBuilder .addEndEvent("KillerEnd") .addMessageEventTrigger("msgKiller", targetProcess, targetFlowNode); final ArrayList> endCorrelations = new ArrayList<>(1); final Expression endCorrelationKey = new ExpressionBuilder().createConstantStringExpression("key"); final Expression endCorrelationValue = new ExpressionBuilder() .createConstantLongExpression(processToKillInstance.getId()); endCorrelations.add(Map.entry(endCorrelationKey, endCorrelationValue)); addCorrelations(throwMessageEventTriggerBuilder, endCorrelations); // Transitions killerProcessDefinitionBuilder.addTransition("KillerStart", "Step3"); killerProcessDefinitionBuilder.addTransition("Step3", "KillerEnd"); killerProcessDefinition = deployAndEnableProcessWithActor(killerProcessDefinitionBuilder.done(), ACTOR_NAME, user); final StartProcessUntilStep killerStartProcessAndWaitForTask = startProcessAndWaitForTask( killerProcessDefinition.getId(), "Step3"); assignAndExecuteStep(killerStartProcessAndWaitForTask.getActivityInstance(), user); waitForProcessToFinish(killerStartProcessAndWaitForTask.getProcessInstance()); // Check that process to kill is terminated waitForProcessToFinish(processToKillInstance); final ArchivedActivityInstance step1 = getProcessAPI() .getArchivedActivityInstance(toKillStartProcessAndWaitForTask.getActivityInstance().getId()); assertEquals(ActivityStates.ABORTED_STATE, step1.getState()); } finally { // Clean up disableAndDeleteProcess(processToKillDefinition, killerProcessDefinition); } } @Test public void can_use_a_variable_to_define_target_process() throws Exception { //given ProcessDefinition receiveMsgProcess = deployAndEnableProcessWithStartMessageEvent("receiveMsgProcess", "go"); ProcessDefinition sendMessageProcess = deployAndEnableProcessSendingMessageUsingVariableAsTarget( "receiveMsgProcess", "startEvent", "go"); //when ProcessInstance processInstance = getProcessAPI().startProcess(sendMessageProcess.getId()); //then waitForProcessToFinish(processInstance); long taskId = waitForUserTask(START_WITH_MESSAGE_STEP1_NAME); HumanTaskInstance taskInstance = getProcessAPI().getHumanTaskInstance(taskId); Assertions.assertThat(taskInstance.getProcessDefinitionId()).isEqualTo(receiveMsgProcess.getId()); //clean up disableAndDeleteProcess(receiveMsgProcess, sendMessageProcess); } @Test public void can_delete_message_by_creationDate() throws Exception { //clean any existing messages getProcessAPI().deleteMessageByCreationDate(System.currentTimeMillis(), null); final Expression targetProcessExpression = new ExpressionBuilder().createConstantStringExpression("p1"); final Expression targetFlowNodeExpression = new ExpressionBuilder().createConstantStringExpression("step1"); getProcessAPI().sendMessage("go", targetProcessExpression, targetFlowNodeExpression, null, null); long untilDate = System.currentTimeMillis(); TimeUnit.MILLISECONDS.sleep(5); getProcessAPI().sendMessage("go2", targetProcessExpression, targetFlowNodeExpression, null, null); getProcessAPI().sendMessage("go3", targetProcessExpression, targetFlowNodeExpression, null, null); int nbMessageDeleted = getProcessAPI().deleteMessageByCreationDate(untilDate, null); Assertions.assertThat(nbMessageDeleted).isEqualTo(1); SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 2); searchOptionsBuilder.filter("messageName", "go2"); nbMessageDeleted = getProcessAPI().deleteMessageByCreationDate(System.currentTimeMillis(), searchOptionsBuilder.done()); Assertions.assertThat(nbMessageDeleted).isEqualTo(1); //clean up nbMessageDeleted = getProcessAPI().deleteMessageByCreationDate(System.currentTimeMillis(), null); Assertions.assertThat(nbMessageDeleted).isEqualTo(1); nbMessageDeleted = getProcessAPI().deleteMessageByCreationDate(System.currentTimeMillis(), null); Assertions.assertThat(nbMessageDeleted).isEqualTo(0); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/MessageEventSubProcessIT.java ================================================ /** * 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.event; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import java.io.Serializable; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.data.DataNotFoundException; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ThrowMessageEventTriggerBuilder; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.test.TestStates; import org.junit.Test; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class MessageEventSubProcessIT extends AbstractWaitingEventIT { @Test public void messageEventSubProcessTransmitData() throws Exception { // create a process with a user step and an event subprocess that start with a start message event having an operation updating the data final ProcessDefinitionBuilder receiveProcessBuilder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithEventSubProcess", "1.0"); receiveProcessBuilder.addActor(ACTOR_NAME); receiveProcessBuilder.addShortTextData("aData", new ExpressionBuilder().createConstantStringExpression("defaultValue")); receiveProcessBuilder.addUserTask("waitHere", ACTOR_NAME); final SubProcessDefinitionBuilder subProcessBuilder = receiveProcessBuilder .addSubProcess("startWithMessage", true).getSubProcessBuilder(); subProcessBuilder.addUserTask("stepInSubProcess", ACTOR_NAME); subProcessBuilder .addStartEvent("start") .addMessageEventTrigger("msg") .addOperation( new OperationBuilder().createSetDataOperation("aData", new ExpressionBuilder().createDataExpression("msgData", String.class.getName()))); subProcessBuilder.addTransition("start", "stepInSubProcess"); final ProcessDefinition receiveProcess = deployAndEnableProcessWithActor(receiveProcessBuilder.done(), ACTOR_NAME, user); // create an other process that send a message final ProcessDefinitionBuilder sendProcessBuilder = new ProcessDefinitionBuilder() .createNewInstance("SendMsgProcess", "1.0"); final ThrowMessageEventTriggerBuilder addMessageEventTrigger = sendProcessBuilder .addIntermediateThrowEvent("send").addMessageEventTrigger("msg", new ExpressionBuilder().createConstantStringExpression("ProcessWithEventSubProcess")); addMessageEventTrigger.addMessageContentExpression( new ExpressionBuilder().createConstantStringExpression("msgData"), new ExpressionBuilder().createGroovyScriptExpression("msgVariable", "\"message variable OK\"", String.class.getName())); final ProcessDefinition sendProcess = deployAndEnableProcess(sendProcessBuilder.done()); ProcessInstance receiveProcessInstance = getProcessAPI().startProcess(receiveProcess.getId()); waitForUserTask("waitHere"); ProcessInstance processInstance = getProcessAPI().startProcess(sendProcess.getId()); final long stepInSubProcessId = waitForUserTask("stepInSubProcess"); // No need to verify anything, if no exception, query exists getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.SUB_PROCESS).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()) .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.SUB_PROCESS) .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, receiveProcessInstance.getId()) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.SUB_PROCESS).done()); // data should be transmit from the message assertEquals("message variable OK", getProcessAPI().getActivityDataInstance("aData", stepInSubProcessId).getValue()); disableAndDeleteProcess(sendProcess, receiveProcess); } @Test public void messageEventSubProcessTriggered() throws Exception { final ProcessDefinition process = deployAndEnableProcessWithMessageEventSubProcess(); final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); final long step1Id = waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME); final List activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10); assertEquals(1, activities.size()); checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 1); // send message to start event sub process getProcessAPI().sendMessage(MESSAGE_NAME, new ExpressionBuilder().createConstantStringExpression(process.getName()), new ExpressionBuilder().createConstantStringExpression(SUB_PROCESS_START_NAME), null); final FlowNodeInstance eventSubProcessActivity = waitForFlowNodeInExecutingState(processInstance, "eventSubProcess", false); final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, SUB_PROCESS_USER_TASK_NAME); final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId()); checkNumberOfWaitingEvents( "The parent process instance is supposed to be aborted, so no more waiting events are expected.", SUB_PROCESS_START_NAME, 0); waitForArchivedActivity(step1Id, TestStates.ABORTED); assignAndExecuteStep(subStep, user); waitForArchivedActivity(eventSubProcessActivity.getId(), TestStates.NORMAL_FINAL); waitForProcessToFinish(subProcInst); waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED); // check that the transition wasn't taken checkWasntExecuted(processInstance, "end"); disableAndDeleteProcess(process.getId()); } @Test public void messageEventSubProcessTriggeredWithIntermediateThrowEvent() throws Exception { final String receiverProcessName = "ReceiverEndMessageEvent"; final String startName = "start"; final String endName = "end"; // Create and deploy Sender process final Expression targetReceiverProcessExpression = new ExpressionBuilder() .createConstantStringExpression(receiverProcessName); final ProcessDefinitionBuilder senderBuilder = new ProcessDefinitionBuilder() .createNewInstance("SenderEndMessageEvent", "1.0"); senderBuilder.addActor(ACTOR_NAME); senderBuilder.addStartEvent(startName); senderBuilder.addIntermediateThrowEvent(THROW_MESSAGE_TASK_NAME).addMessageEventTrigger(MESSAGE_NAME, targetReceiverProcessExpression); senderBuilder.addUserTask(PARENT_PROCESS_USER_TASK_NAME, ACTOR_NAME); senderBuilder.addEndEvent(endName).addMessageEventTrigger(MESSAGE_NAME + 1, targetReceiverProcessExpression); senderBuilder.addTransition(startName, THROW_MESSAGE_TASK_NAME); senderBuilder.addTransition(THROW_MESSAGE_TASK_NAME, PARENT_PROCESS_USER_TASK_NAME); senderBuilder.addTransition(PARENT_PROCESS_USER_TASK_NAME, endName); final ProcessDefinition senderProcessDefinition = deployAndEnableProcessWithActor(senderBuilder.done(), ACTOR_NAME, user); // Create and deploy Receiver process with SubProcess final ProcessDefinitionBuilder receiverBuilder = new ProcessDefinitionBuilder() .createNewInstance(receiverProcessName, "1.0"); receiverBuilder.addActor(ACTOR_NAME); receiverBuilder.addStartEvent(startName).addMessageEventTrigger(MESSAGE_NAME); receiverBuilder.addUserTask(receiverProcessName + PARENT_PROCESS_USER_TASK_NAME, ACTOR_NAME); receiverBuilder.addTransition(startName, receiverProcessName + PARENT_PROCESS_USER_TASK_NAME); final SubProcessDefinitionBuilder subProcessBuilder = receiverBuilder.addSubProcess("EventSubProcess", true) .getSubProcessBuilder(); subProcessBuilder.addStartEvent(SUB_PROCESS_START_NAME).addMessageEventTrigger(MESSAGE_NAME + 1); subProcessBuilder.addUserTask(SUB_PROCESS_USER_TASK_NAME, ACTOR_NAME); subProcessBuilder.addTransition(SUB_PROCESS_START_NAME, SUB_PROCESS_USER_TASK_NAME); final ProcessDefinition receiverProcessDefinition = deployAndEnableProcessWithActor(receiverBuilder.done(), ACTOR_NAME, user); // Start and execute the Sender process final ProcessInstance senderProcessInstance = getProcessAPI().startProcess(senderProcessDefinition.getId()); final long step1Id = waitForUserTask(senderProcessInstance, PARENT_PROCESS_USER_TASK_NAME); waitForPendingTasks(user.getId(), 2); checkNumberOfWaitingEventsInProcess(receiverProcessName, 2); assignAndExecuteStep(step1Id, user); waitForProcessToFinish(senderProcessInstance); checkNumberOfWaitingEvents( "The parent process instance is supposed to be finished, so no more waiting events are expected.", startName, 1); // Execute the Receiver process final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(ProcessInstanceSearchDescriptor.NAME, receiverProcessName); final ProcessInstance receiverProcessInstance = getProcessAPI() .searchOpenProcessInstances(searchOptionsBuilder.done()).getResult().get(0); waitForUserTask(receiverProcessInstance.getId(), SUB_PROCESS_USER_TASK_NAME); var pendingHumanTaskInstances = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT); try { assertThat(pendingHumanTaskInstances).hasSize(1); } catch (AssertionError e) { System.err.println("Too many humanTaskInstances found: " + pendingHumanTaskInstances.size()); for (HumanTaskInstance humanTaskInstance : pendingHumanTaskInstances) { System.err.println("humanTaskInstance = " + humanTaskInstance); } Thread.sleep(10_000); pendingHumanTaskInstances = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT); if (pendingHumanTaskInstances.size() == 1) { System.err.println("Waiting longer solved the problem!"); } throw e; } finally { // Clean-up disableAndDeleteProcess(senderProcessDefinition.getId()); disableAndDeleteProcess(receiverProcessDefinition.getId()); } } @Test public void messageEventSubProcessNotTriggered() throws Exception { final ProcessDefinition process = deployAndEnableProcessWithMessageEventSubProcess(); final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); final long step1Id = waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME); final List activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10); assertEquals(1, activities.size()); checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 1); assignAndExecuteStep(step1Id, user); waitForArchivedActivity(step1Id, TestStates.NORMAL_FINAL); waitForProcessToFinish(processInstance); // the parent process instance has completed, so no more waiting events are expected checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 0); disableAndDeleteProcess(process); } @Test public void messageEventSubProcessTriggeredWithCorrelation() throws Exception { final Expression correlationKey = new ExpressionBuilder().createConstantStringExpression("productName"); final Expression catchCorrelationValue = new ExpressionBuilder().createDataExpression(SHORT_DATA_NAME, String.class.getName()); final ProcessDefinition process = deployAndEnableProcessWithMessageEventSubProcessAndData( Collections.singletonList(Map.entry( correlationKey, catchCorrelationValue))); final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME); // send message to start event sub process final Expression throwCorrelationValue = new ExpressionBuilder().createConstantStringExpression("parentVar");// the default data value getProcessAPI().sendMessage(MESSAGE_NAME, new ExpressionBuilder().createConstantStringExpression(process.getName()), new ExpressionBuilder().createConstantStringExpression(SUB_PROCESS_START_NAME), null, Collections.singletonMap(correlationKey, throwCorrelationValue)); waitForUserTask(processInstance.getId(), SUB_PROCESS_USER_TASK_NAME); disableAndDeleteProcess(process.getId()); } @Test public void messageEventSubProcessTriggeredWithCorrelationAndIntermediateThrowEvent() throws Exception { // Correlation final Expression correlationKey = new ExpressionBuilder().createConstantStringExpression("productName"); final Expression correlationValue = new ExpressionBuilder().createDataExpression(SHORT_DATA_NAME, String.class.getName()); // Sender final Expression targetReceiverProcessExpression = new ExpressionBuilder() .createConstantStringExpression("ProcessWithEventSubProcess"); final ProcessDefinitionBuilder senderBuilder = new ProcessDefinitionBuilder() .createNewInstance("SenderEndMessageEvent", "1.0"); senderBuilder.addActor(ACTOR_NAME).addShortTextData(SHORT_DATA_NAME, new ExpressionBuilder().createConstantStringExpression("parentVar")); senderBuilder.addIntermediateThrowEvent(THROW_MESSAGE_TASK_NAME) .addMessageEventTrigger(MESSAGE_NAME, targetReceiverProcessExpression) .addCorrelation(correlationKey, correlationValue); final ProcessDefinition senderProcessDefinition = deployAndEnableProcessWithActor(senderBuilder.done(), ACTOR_NAME, user); // Receiver final ProcessDefinition receiverProcessDefinition = deployAndEnableProcessWithMessageEventSubProcessAndData( Collections .singletonList(Map.entry(correlationKey, correlationValue))); final ProcessInstance receiverProcessInstance = getProcessAPI().startProcess(receiverProcessDefinition.getId()); waitForUserTask(receiverProcessInstance, PARENT_PROCESS_USER_TASK_NAME); // send message to start event sub process getProcessAPI().startProcess(senderProcessDefinition.getId()); waitForUserTask(receiverProcessInstance.getId(), SUB_PROCESS_USER_TASK_NAME); disableAndDeleteProcess(senderProcessDefinition, receiverProcessDefinition); } @Test public void createSeveralInstances() throws Exception { final ProcessDefinition process = deployAndEnableProcessWithMessageEventSubProcess(); final ProcessInstance processInstance1 = getProcessAPI().startProcess(process.getId()); final ProcessInstance processInstance2 = getProcessAPI().startProcess(process.getId()); // start the first event sub-process getProcessAPI().sendMessage(MESSAGE_NAME, new ExpressionBuilder().createConstantStringExpression(process.getName()), new ExpressionBuilder().createConstantStringExpression(SUB_PROCESS_START_NAME), null); // start the second event sub-process getProcessAPI().sendMessage(MESSAGE_NAME, new ExpressionBuilder().createConstantStringExpression(process.getName()), new ExpressionBuilder().createConstantStringExpression(SUB_PROCESS_START_NAME), null); waitForUserTask(processInstance1, SUB_PROCESS_USER_TASK_NAME); waitForUserTask(processInstance2, SUB_PROCESS_USER_TASK_NAME); disableAndDeleteProcess(process.getId()); } @Test public void subProcessCanAccessParentData() throws Exception { final ProcessDefinition process = deployAndEnableProcessWithMessageEventSubProcessAndData(null); final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); waitForUserTask(processInstance.getId(), PARENT_PROCESS_USER_TASK_NAME); getProcessAPI().sendMessage(MESSAGE_NAME, new ExpressionBuilder().createConstantStringExpression(process.getName()), new ExpressionBuilder().createConstantStringExpression(SUB_PROCESS_START_NAME), null); final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance.getId(), SUB_PROCESS_USER_TASK_NAME); final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId()); checkProcessDataInstance(INT_DATA_NAME, subProcInst.getId(), 1); checkProcessDataInstance(SHORT_DATA_NAME, subProcInst.getId(), "childVar"); checkProcessDataInstance("value", subProcInst.getId(), 10.0); checkProcessDataInstance(SHORT_DATA_NAME, processInstance.getId(), "parentVar"); checkActivityDataInstance(SHORT_DATA_NAME, subStep.getId(), "childActivityVar"); assignAndExecuteStep(subStep, user); waitForProcessToFinish(subProcInst); waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED); disableAndDeleteProcess(process.getId()); } private void checkProcessDataInstance(final String dataName, final long processInstanceId, final Serializable expectedValue) throws DataNotFoundException { final DataInstance processDataInstance; processDataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstanceId); assertEquals(expectedValue, processDataInstance.getValue()); } private void checkActivityDataInstance(final String dataName, final long activityInstanceId, final Serializable expectedValue) throws DataNotFoundException { final DataInstance activityDataInstance; activityDataInstance = getProcessAPI().getActivityDataInstance(dataName, activityInstanceId); assertEquals(expectedValue, activityDataInstance.getValue()); } @Test public void messageEventSubProcInsideTargetCallActivity() throws Exception { final ProcessDefinition targetProcess = deployAndEnableProcessWithMessageEventSubProcess(); final ProcessDefinition callerProcess = deployAndEnableProcessWithCallActivity(targetProcess.getName(), targetProcess.getVersion()); final ProcessInstance processInstance = getProcessAPI().startProcess(callerProcess.getId()); try { final ActivityInstance step1 = waitForUserTaskAndGetIt(processInstance, PARENT_PROCESS_USER_TASK_NAME); getProcessAPI().sendMessage(MESSAGE_NAME, new ExpressionBuilder().createConstantStringExpression(targetProcess.getName()), new ExpressionBuilder().createConstantStringExpression(SUB_PROCESS_START_NAME), null); final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, SUB_PROCESS_USER_TASK_NAME); final ProcessInstance calledProcInst = getProcessAPI() .getProcessInstance(step1.getParentProcessInstanceId()); final ProcessInstance subProcInst = getProcessAPI() .getProcessInstance(subStep.getParentProcessInstanceId()); waitForArchivedActivity(step1.getId(), TestStates.ABORTED); assignAndExecuteStep(subStep, user.getId()); waitForProcessToFinish(subProcInst); waitForProcessToBeInState(calledProcInst, ProcessInstanceState.ABORTED); waitForUserTaskAndExecuteIt(processInstance, "step2", user); waitForProcessToFinish(processInstance); } finally { disableAndDeleteProcess(callerProcess); disableAndDeleteProcess(targetProcess); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/NonInterruptingTimerBoundaryEventIT.java ================================================ /** * 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.event; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class NonInterruptingTimerBoundaryEventIT extends AbstractEventIT { @Test public void nonInterruptTimerBoundaryEventTriggered() throws Exception { // deploy process with non-interrupting boundary event final long timerDuration = 1000; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEvent(timerDuration, false, "step1", "exceptionStep", "step2"); // start the process and wait for timer to trigger final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance.getId(), "step1"); Thread.sleep(timerDuration); // wait timer trigger // check that the exception flow was taken final long exceptionFlowStepId = waitForUserTask(processInstance, "exceptionStep"); // execute the task containing the boundary and verify that the normal flow continues assignAndExecuteStep(step1Id, user); final long normalFlowStepId = waitForUserTask(processInstance, "step2"); // execute exception flow step and normal flow step and verify that the process has finished assignAndExecuteStep(exceptionFlowStepId, user); assignAndExecuteStep(normalFlowStepId, user); waitForProcessToFinish(processInstance); // clean up disableAndDeleteProcess(processDefinition); } @Test public void nonInterruptTimerBoundaryEventTriggeredOnCallActivity() throws Exception { final long timerDuration = 2000; final String simpleProcessName = "targetProcess"; final String simpleTaskName = "stepCA"; // deploy a simple process p1 final ProcessDefinition targetProcessDefinition = deployAndEnableSimpleProcess(simpleProcessName, simpleTaskName); // deploy a process, p2, with a call activity calling p1. The call activity has a non-interrupting timer boundary event final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEventOnCallActivity( timerDuration, false, simpleProcessName); // start the root process and wait for boundary event triggering final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long stepCAId = waitForUserTask(processInstance.getId(), simpleTaskName); Thread.sleep(timerDuration); // wait timer trigger // check that the exception flow was taken final long exceptionFlowStepId = waitForUserTask(processInstance.getId(), EXCEPTION_STEP); // execute the user task of p1 and check that the normal flow also was taken assignAndExecuteStep(stepCAId, user.getId()); final long normalFlowStepId = waitForUserTask(processInstance.getId(), PARENT_PROCESS_USER_TASK_NAME); // execute exception flow and normal flow and verify that the process completes assignAndExecuteStep(exceptionFlowStepId, user); assignAndExecuteStep(normalFlowStepId, user); waitForProcessToFinish(processInstance); // clean up disableAndDeleteProcess(processDefinition); disableAndDeleteProcess(targetProcessDefinition); } @Test public void nonInterruptTimerBoundaryEventTriggeredOnSequentialMultiInstance() throws Exception { // deploy a process with a non-interrupting timer boundary event attached to a sequential multi-instance final long timerDuration = 1000; final String multiTaskName = "step1"; final String exceptionFlowTaskName = "exceptionStep"; final int loopCardinality = 2; final String normalFlowTaskName = "step2"; final ProcessDefinition processDefinition = deployAndEnableProcessMultiInstanceWithBoundaryEvent(timerDuration, false, multiTaskName, loopCardinality, true, normalFlowTaskName, exceptionFlowTaskName); // start the process and wait the timer to trigger final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long multiInstanceId = waitForUserTask(processInstance, multiTaskName); Thread.sleep(timerDuration); // wait timer trigger // check that the exception flow was taken final long exceptionFlowStepId = waitForUserTask(processInstance, exceptionFlowTaskName); // execute multi-instances and verify that normal flow continues assignAndExecuteStep(multiInstanceId, user); waitForUserTasksAndExecuteIt(multiTaskName, processInstance, loopCardinality - 1); final long normalFlowStepId = waitForUserTask(processInstance, normalFlowTaskName); // execute exception flow and normal flow and verify that the process completes assignAndExecuteStep(exceptionFlowStepId, user); assignAndExecuteStep(normalFlowStepId, user); waitForProcessToFinish(processInstance); // clean up disableAndDeleteProcess(processDefinition.getId()); } @Test public void nonInterruptTimerBoundaryEventTriggeredOnParallelMultiInstance() throws Exception { final long timerDuration = 1000; final int loopCardinality = 2; // deploy a process with a interrupting timer boundary event attached to a parallel multi-instance final String multiTaskName = "step1"; final String normalTaskName = "step2"; final ProcessDefinition processDefinition = deployAndEnableProcessMultiInstanceWithBoundaryEvent(timerDuration, false, multiTaskName, loopCardinality, false, normalTaskName, "exceptionStep"); // start the process and wait for process to be triggered final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, multiTaskName); // verify that the exception flow was taken final long exceptionFlowStepId = waitForUserTask(processInstance, "exceptionStep"); // execute multi-instance and verify that normal flow continues executeRemainingParallelMultiInstances(multiTaskName, processInstance, loopCardinality); final long normalTaskId = waitForUserTask(processInstance, normalTaskName); // execute exception flow and normal flow and verify the process completes assignAndExecuteStep(exceptionFlowStepId, user); assignAndExecuteStep(normalTaskId, user); waitForProcessToFinish(processInstance); // clean up disableAndDeleteProcess(processDefinition.getId()); } @Test public void nonInterruptTimerBoundaryEventTriggeredOnLoopActivity() throws Exception { final long timerDuration = 1000; final int loopMax = 2; final String loopActivityName = "step1"; final String normalFlowStepName = "step2"; final String exceptionFlowStepName = "exceptionStep"; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEventOnLoopActivity( timerDuration, false, loopMax, loopActivityName, normalFlowStepName, exceptionFlowStepName); // start the process and wait timer to trigger final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long loopId = waitForUserTask(processInstance, loopActivityName); Thread.sleep(timerDuration); // wait timer trigger // verify that the exception flow was taken final long exceptionStepId = waitForUserTask(processInstance, exceptionFlowStepName); // execute all loop activities and verify that the nomal flow continues assignAndExecuteStep(loopId, user); waitForUserTasksAndExecuteIt(loopActivityName, processInstance, loopMax - 1); final long normalFlowStepId = waitForUserTask(processInstance, normalFlowStepName); // execute the exception flow and the normal flow and verify that the process completes assignAndExecuteStep(exceptionStepId, user); assignAndExecuteStep(normalFlowStepId, user); waitForProcessToFinish(processInstance); // clean up disableAndDeleteProcess(processDefinition.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/SignalBoundaryEventIT.java ================================================ /** * 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.event; import static org.junit.Assert.assertFalse; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.test.TestStates; import org.bonitasoft.engine.test.wait.WaitForStep; import org.junit.Test; public class SignalBoundaryEventIT extends AbstractEventIT { @Test public void signalBoundaryEventTriggered() throws Exception { final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEvent("MySignal"); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); getProcessAPI().sendSignal("MySignal"); waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user); waitForProcessToFinish(processInstance); waitForArchivedActivity(step1Id, TestStates.ABORTED); checkWasntExecuted(processInstance, "step2"); disableAndDeleteProcess(processDefinition); } @Test public void signalBoundaryEventNotTriggered() throws Exception { final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryMessageEvent("MySignal1"); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step1", user); final long step2 = waitForUserTask(processInstance, "step2"); getProcessAPI().sendSignal("MySignal1"); final WaitForStep waitForExceptionStep = new WaitForStep(50, 1000, EXCEPTION_STEP, processInstance.getId(), TestStates.READY, getProcessAPI()); assertFalse(waitForExceptionStep.waitUntil()); assignAndExecuteStep(step2, user); waitForProcessToFinish(processInstance); checkWasntExecuted(processInstance, EXCEPTION_STEP); disableAndDeleteProcess(processDefinition); } @Test public void signalBoundaryEventOnCallActivityTriggered() throws Exception { final String signalName = "MySignal"; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnCallActivity( signalName); final ProcessDefinition calledProcessDefinition = deployAndEnableSimpleProcess("calledProcess", "calledStep"); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final ActivityInstance calledStep = waitForUserTaskAndGetIt(processInstance, "calledStep"); final ProcessInstance calledProcessInstance = getProcessAPI() .getProcessInstance(calledStep.getParentProcessInstanceId()); getProcessAPI().sendSignal("MySignal"); waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user); waitForProcessToBeInState(calledProcessInstance, ProcessInstanceState.ABORTED); waitForProcessToFinish(processInstance); waitForArchivedActivity(calledStep.getId(), TestStates.ABORTED); checkWasntExecuted(processInstance, "step2"); disableAndDeleteProcess(processDefinition); disableAndDeleteProcess(calledProcessDefinition); } @Test public void signalBoundaryEventOnCallActivityNotTriggered() throws Exception { final String signalName = "MySignal"; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnCallActivity( signalName); final ProcessDefinition calledProcessDefinition = deployAndEnableSimpleProcess("calledProcess", "calledStep"); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final ActivityInstance calledStep = waitForUserTaskAndGetIt(processInstance, "calledStep"); final ProcessInstance calledProcessInstance = getProcessAPI() .getProcessInstance(calledStep.getParentProcessInstanceId()); assignAndExecuteStep(calledStep, user); final long step2Id = waitForUserTask(processInstance, "step2"); waitForProcessToFinish(calledProcessInstance); getProcessAPI().sendSignal("MySignal"); final WaitForStep waitForExceptionStep = new WaitForStep(50, 1000, EXCEPTION_STEP, processInstance.getId(), TestStates.READY, getProcessAPI()); assertFalse(waitForExceptionStep.waitUntil()); assignAndExecuteStep(step2Id, user); waitForProcessToFinish(processInstance); checkWasntExecuted(processInstance, EXCEPTION_STEP); disableAndDeleteProcess(processDefinition); disableAndDeleteProcess(calledProcessDefinition); } @Test public void signalBoundaryEventTriggeredOnSequentialMultiInstance() throws Exception { final int loopCardinality = 4; final boolean isSequential = true; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnMultiInstance( loopCardinality, isSequential); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); getProcessAPI().sendSignal("MySignal"); waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user); waitForProcessToFinish(processInstance); waitForArchivedActivity(step1Id, TestStates.ABORTED); checkWasntExecuted(processInstance, "step2"); disableAndDeleteProcess(processDefinition); } @Test public void signalBoundaryEventNotTriggeredOnSequentialMultiInstance() throws Exception { final int loopCardinality = 3; final boolean isSequential = true; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnMultiInstance( loopCardinality, isSequential); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); for (int i = 0; i < loopCardinality; i++) { waitForUserTaskAndExecuteIt(processInstance, "step1", user); } final long step2Id = waitForUserTask(processInstance, "step2"); getProcessAPI().sendSignal("MySignal1"); final WaitForStep waitForExceptionStep = new WaitForStep(50, 1000, EXCEPTION_STEP, processInstance.getId(), TestStates.READY, getProcessAPI()); assertFalse(waitForExceptionStep.waitUntil()); assignAndExecuteStep(step2Id, user); waitForProcessToFinish(processInstance); checkWasntExecuted(processInstance, EXCEPTION_STEP); disableAndDeleteProcess(processDefinition); } @Test public void signalBoundaryEventTriggeredOnParallelMultiInstance() throws Exception { final int loopCardinality = 4; final boolean isSequential = false; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnMultiInstance( loopCardinality, isSequential); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); getProcessAPI().sendSignal("MySignal"); waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user); waitForProcessToFinish(processInstance); waitForArchivedActivity(step1Id, TestStates.ABORTED); checkWasntExecuted(processInstance, "step2"); disableAndDeleteProcess(processDefinition); } @Test public void signalBoundaryEventNotTriggeredOnParallelMultiInstance() throws Exception { final int loopCardinality = 3; final boolean isSequential = false; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnMultiInstance( loopCardinality, isSequential); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); for (int i = 0; i < loopCardinality; i++) { waitForUserTaskAndExecuteIt(processInstance, "step1", user); } final long step2 = waitForUserTask(processInstance, "step2"); getProcessAPI().sendSignal("MySignal1"); final WaitForStep waitForExceptionStep = new WaitForStep(50, 1000, EXCEPTION_STEP, processInstance.getId(), TestStates.READY, getProcessAPI()); assertFalse(waitForExceptionStep.waitUntil()); assignAndExecuteStep(step2, user); waitForProcessToFinish(processInstance); checkWasntExecuted(processInstance, EXCEPTION_STEP); disableAndDeleteProcess(processDefinition); } @Test public void signalBoundaryEventTriggeredOnLoopActivity() throws Exception { final int loopMax = 3; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnLoopActivity( loopMax); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); getProcessAPI().sendSignal("MySignal"); waitForUserTaskAndExecuteIt(processInstance, EXCEPTION_STEP, user); waitForProcessToFinish(processInstance); waitForArchivedActivity(step1Id, TestStates.ABORTED); checkWasntExecuted(processInstance, "step2"); disableAndDeleteProcess(processDefinition); } @Test public void signalBoundaryEventNotTriggeredOnLoopActivity() throws Exception { final int loopMax = 2; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundarySignalEventOnLoopActivity( loopMax); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); for (int i = 0; i < loopMax; i++) { waitForUserTaskAndExecuteIt(processInstance, "step1", user); } final long step2Id = waitForUserTask(processInstance, "step2"); getProcessAPI().sendSignal("MySignal1"); final WaitForStep waitForExceptionStep = new WaitForStep(50, 1000, EXCEPTION_STEP, processInstance.getId(), TestStates.READY, getProcessAPI()); assertFalse(waitForExceptionStep.waitUntil()); assignAndExecuteStep(step2Id, user); waitForProcessToFinish(processInstance); checkWasntExecuted(processInstance, EXCEPTION_STEP); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/SignalEventIT.java ================================================ /** * 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.event; import static org.junit.Assert.assertEquals; import java.util.List; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.test.TestStates; import org.junit.Test; public class SignalEventIT extends AbstractEventIT { @Test public void sendSignal() throws Exception { final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder(); ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); builder.createNewInstance("SayGO", "1.0").addActor(ACTOR_NAME).addStartEvent("Start") .addUserTask("step1", ACTOR_NAME).addEndEvent("End") .addSignalEventTrigger("GO").addTransition("Start", "step1").addTransition("step1", "End"); archiveBuilder.createNewBusinessArchive().setProcessDefinition(builder.done()); final BusinessArchive endSignalArchive = archiveBuilder.done(); builder = new ProcessDefinitionBuilder(); builder.createNewInstance("GetGO", "1.0").addActor(ACTOR_NAME).addStartEvent("StartOnSignal") .addSignalEventTrigger("GO") .addUserTask("Task1", ACTOR_NAME).addTransition("StartOnSignal", "Task1"); archiveBuilder.createNewBusinessArchive().setProcessDefinition(builder.done()); final BusinessArchive startSignalArchive = archiveBuilder.done(); final ProcessDefinition processDefinitionWithStartSignal = deployAndEnableProcessWithActor(startSignalArchive, ACTOR_NAME, user); final ProcessDefinition processDefinitionWithEndSignal = deployAndEnableProcessWithActor(endSignalArchive, ACTOR_NAME, user); logout(); loginOnDefaultTenantWith(USERNAME, PASSWORD); // Check that the process with trigger signal on start is not started, before send signal final ProcessInstance processInstanceWithEndSignal = getProcessAPI() .startProcess(processDefinitionWithEndSignal.getId()); waitForUserTask(processInstanceWithEndSignal, "step1"); checkNbOfProcessInstances(1); List taskInstances = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, ActivityInstanceCriterion.NAME_ASC); assertEquals(1, taskInstances.size()); assertEquals("step1", taskInstances.get(0).getName()); // Send signal assignAndExecuteStep(taskInstances.get(0), user); waitForProcessToFinish(processInstanceWithEndSignal.getId()); // Check that the process with trigger signal on start is started, after send signal waitForUserTask("Task1"); taskInstances = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, ActivityInstanceCriterion.NAME_ASC); assertEquals(1, taskInstances.size()); assertEquals("Task1", taskInstances.get(0).getName()); disableAndDeleteProcess(processDefinitionWithStartSignal, processDefinitionWithEndSignal); } @Test public void sendIntermediateCatchSignal() throws Exception { final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder(); ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); builder.createNewInstance("SayGO", "1.0").addStartEvent("Start").addEndEvent("End").addSignalEventTrigger("GO") .addTransition("Start", "End"); archiveBuilder.createNewBusinessArchive().setProcessDefinition(builder.done()); final BusinessArchive endSignalArchive = archiveBuilder.done(); builder = new ProcessDefinitionBuilder(); builder.createNewInstance("GetGO", "1.0").addActor(ACTOR_NAME).addStartEvent("Start") .addIntermediateCatchEvent("OnSignal").addSignalEventTrigger("GO") .addUserTask("Task1", ACTOR_NAME).addTransition("Start", "OnSignal").addTransition("OnSignal", "Task1"); archiveBuilder.createNewBusinessArchive().setProcessDefinition(builder.done()); final BusinessArchive startSignalArchive = archiveBuilder.done(); final ProcessDefinition startSignal = deployAndEnableProcessWithActor(startSignalArchive, ACTOR_NAME, user); final ProcessDefinition endSignal = deployAndEnableProcess(endSignalArchive); logout(); loginOnDefaultTenantWith(USERNAME, PASSWORD); final ProcessInstance instance = getProcessAPI().startProcess(startSignal.getId()); waitForEvent(instance, "OnSignal", TestStates.WAITING); getProcessAPI().startProcess(endSignal.getId()); waitForUserTask("Task1"); disableAndDeleteProcess(startSignal, endSignal); } @Test public void sendIntermadiateThrowSignal() throws Exception { final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder(); ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); final String intermediate = "Intermediate"; builder.createNewInstance("SayGO", "1.0").addStartEvent("Start").addIntermediateThrowEvent(intermediate) .addSignalEventTrigger("GO").addEndEvent("End") .addTransition("Start", intermediate).addTransition(intermediate, "End"); archiveBuilder.createNewBusinessArchive().setProcessDefinition(builder.done()); final BusinessArchive endSignalArchive = archiveBuilder.done(); builder = new ProcessDefinitionBuilder(); builder.createNewInstance("GetGO", "1.0").addActor(ACTOR_NAME).addStartEvent("StartOnSignal") .addSignalEventTrigger("GO") .addUserTask("Task1", ACTOR_NAME).addTransition("StartOnSignal", "Task1"); archiveBuilder.createNewBusinessArchive().setProcessDefinition(builder.done()); final BusinessArchive startSignalArchive = archiveBuilder.done(); final ProcessDefinition startSignal = deployAndEnableProcessWithActor(startSignalArchive, ACTOR_NAME, user); final ProcessDefinition endSignal = deployAndEnableProcess(endSignalArchive); logout(); loginOnDefaultTenantWith(USERNAME, PASSWORD); getProcessAPI().startProcess(endSignal.getId()); waitForUserTask("Task1"); disableAndDeleteProcess(startSignal, endSignal); } @Test public void sendSignalViaAPIToStartSignalEvent() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); builder.createNewInstance("GetGO", "1.0").addActor(ACTOR_NAME).addStartEvent("StartOnSignal") .addSignalEventTrigger("GO") .addUserTask("Task1", ACTOR_NAME).addTransition("StartOnSignal", "Task1"); final DesignProcessDefinition startSignalDef = builder.done(); final ProcessDefinition startSignal = deployAndEnableProcessWithActor(startSignalDef, ACTOR_NAME, user); logout(); loginOnDefaultTenantWith(USERNAME, PASSWORD); getProcessAPI().sendSignal("GO"); waitForUserTask("Task1"); disableAndDeleteProcess(startSignal); } @Test public void sendSignalViaAPIToIntermediateSignalEvent() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); builder.createNewInstance("GetGO", "1.0").addActor(ACTOR_NAME).addStartEvent("Start") .addIntermediateCatchEvent("OnSignal").addSignalEventTrigger("GO") .addUserTask("Task1", ACTOR_NAME).addTransition("Start", "OnSignal").addTransition("OnSignal", "Task1"); final ProcessDefinition intermediateSignal = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); logout(); loginOnDefaultTenantWith(USERNAME, PASSWORD); final ProcessInstance instance = getProcessAPI().startProcess(intermediateSignal.getId()); waitForEvent(instance, "OnSignal", TestStates.WAITING); getProcessAPI().sendSignal("GO"); waitForUserTask("Task1"); disableAndDeleteProcess(intermediateSignal); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/SignalEventSubProcessIT.java ================================================ /** * 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.event; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import java.io.Serializable; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.data.DataNotFoundException; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.test.TestStates; import org.junit.Test; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SignalEventSubProcessIT extends AbstractWaitingEventIT { @Test public void evaluateExpressionsOnLoopUserTaskInSupProcess() throws Exception { final ProcessDefinition process = deployAndEnableProcessWithSignalEventSubProcess(false, true); final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME); checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 1); // send signal to start event sub process getProcessAPI().sendSignal(SIGNAL_NAME); waitForFlowNodeInExecutingState(processInstance, SUB_PROCESS_NAME, false); final long subStepId = waitForUserTask(processInstance, SUB_PROCESS_USER_TASK_NAME); final Map> expressions = new HashMap<>(); expressions.put(new ExpressionBuilder().createDataExpression(SHORT_DATA_NAME, String.class.getName()), new HashMap(0)); final Map expressionResults = getProcessAPI() .evaluateExpressionsOnActivityInstance(subStepId, expressions); assertEquals("childActivityVar", expressionResults.get(SHORT_DATA_NAME)); disableAndDeleteProcess(process); } @Test public void signalEventSubProcessTriggered() throws Exception { // given final ProcessDefinition process = deployAndEnableProcessWithSignalEventSubProcess(false, false); // when final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); final long step1Id = waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME); // then List activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10); assertThat(activities).as("should have 1 activity").hasSize(1); checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 1); // when getProcessAPI().sendSignal(SIGNAL_NAME); waitForArchivedActivity(step1Id, TestStates.ABORTED); final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, SUB_PROCESS_USER_TASK_NAME); // then checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 0); activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10); assertThat(activities).as("should have 2 activities: sub-process flow node and user task").hasSize(2); assertEquals(SUB_PROCESS_NAME, activities.get(0).getName()); assertEquals(SUB_PROCESS_USER_TASK_NAME, activities.get(1).getName()); // when final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId()); assignAndExecuteStep(subStep, user); waitForProcessToFinish(subProcInst); waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED); // then // check that the transition wasn't taken checkWasntExecuted(processInstance, PARENT_END); disableAndDeleteProcess(process); } @Test public void signalEventSubProcessTriggeredWithIntermediateThrowEvent() throws Exception { final ProcessDefinition process = deployAndEnableProcessWithSignalEventSubProcess(true, false); final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); final long step1Id = waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME); checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 1); assignAndExecuteStep(step1Id, user); waitForUserTask(processInstance, SUB_PROCESS_USER_TASK_NAME); disableAndDeleteProcess(process); } @Test public void signalEventSubProcessNotTriggered() throws Exception { final ProcessDefinition process = deployAndEnableProcessWithSignalEventSubProcess(false, false); final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); final long step1Id = waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME); final List activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10); assertEquals(1, activities.size()); checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 1); assignAndExecuteStep(step1Id, user); waitForArchivedActivity(step1Id, TestStates.NORMAL_FINAL); waitForProcessToFinish(processInstance); // the parent process instance has completed, so no more waiting events are expected checkNumberOfWaitingEvents(SUB_PROCESS_START_NAME, 0); disableAndDeleteProcess(process); } @Test public void createSeveralInstances() throws Exception { final ProcessDefinition process = deployAndEnableProcessWithSignalEventSubProcess(false, false); final ProcessInstance processInstance1 = getProcessAPI().startProcess(process.getId()); final ProcessInstance processInstance2 = getProcessAPI().startProcess(process.getId()); waitForUserTask(processInstance1, PARENT_PROCESS_USER_TASK_NAME); waitForUserTask(processInstance2, PARENT_PROCESS_USER_TASK_NAME); // send signal to start event sub processes: one signal must start the event sub-processes in the two process instances getProcessAPI().sendSignal(SIGNAL_NAME); waitForUserTask(processInstance1, SUB_PROCESS_USER_TASK_NAME); waitForUserTask(processInstance2, SUB_PROCESS_USER_TASK_NAME); Thread.sleep(50); disableAndDeleteProcess(process); } @Test public void subProcessCanAccessParentData() throws Exception { final ProcessDefinition process = deployAndEnableProcessWithSignalEventSubProcess(false, true); final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); waitForUserTask(processInstance, PARENT_PROCESS_USER_TASK_NAME); getProcessAPI().sendSignal(SIGNAL_NAME); final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, SUB_PROCESS_USER_TASK_NAME); final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId()); checkProcessDataInstance("count", subProcInst.getId(), 1); checkProcessDataInstance(SHORT_DATA_NAME, subProcInst.getId(), "childVar"); checkProcessDataInstance("value", subProcInst.getId(), 10.0); checkProcessDataInstance(SHORT_DATA_NAME, processInstance.getId(), "parentVar"); checkActivityDataInstance(SHORT_DATA_NAME, subStep.getId(), "childActivityVar"); assignAndExecuteStep(subStep, user); waitForProcessToFinish(subProcInst); waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED); disableAndDeleteProcess(process); } private void checkProcessDataInstance(final String dataName, final long processInstanceId, final Serializable expectedValue) throws DataNotFoundException { final DataInstance processDataInstance; processDataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstanceId); assertEquals(expectedValue, processDataInstance.getValue()); } private void checkActivityDataInstance(final String dataName, final long activityInstanceId, final Serializable expectedValue) throws DataNotFoundException { final DataInstance activityDataInstance; activityDataInstance = getProcessAPI().getActivityDataInstance(dataName, activityInstanceId); assertEquals(expectedValue, activityDataInstance.getValue()); } @Test public void signalEventSubProcInsideTargetCallActivity() throws Exception { final ProcessDefinition targetProcess = deployAndEnableProcessWithSignalEventSubProcess(false, false); final ProcessDefinition callerProcess = deployAndEnableProcessWithCallActivity(targetProcess.getName(), targetProcess.getVersion()); final ProcessInstance processInstance = getProcessAPI().startProcess(callerProcess.getId()); final ActivityInstance step1 = waitForUserTaskAndGetIt(processInstance, PARENT_PROCESS_USER_TASK_NAME); getProcessAPI().sendSignal(SIGNAL_NAME); final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, SUB_PROCESS_USER_TASK_NAME); final ProcessInstance calledProcInst = getProcessAPI().getProcessInstance(step1.getParentProcessInstanceId()); final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId()); waitForArchivedActivity(step1.getId(), TestStates.ABORTED); assignAndExecuteStep(subStep, user); waitForProcessToFinish(subProcInst); waitForProcessToBeInState(calledProcInst, ProcessInstanceState.ABORTED); waitForUserTaskAndExecuteIt(processInstance, "step2", user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(callerProcess.getId()); disableAndDeleteProcess(targetProcess.getId()); } @Test public void should_process_with_gateway_be_canceled_by_event_subprocess() throws Exception { //given ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("ProcessInterrupted", "1.0"); processDefinitionBuilder.addActor("john", true); processDefinitionBuilder.addStartEvent("start"); processDefinitionBuilder.addAutomaticTask("auto1"); processDefinitionBuilder.addGateway("p1", GatewayType.PARALLEL); processDefinitionBuilder.addUserTask("user1", "john"); processDefinitionBuilder.addAutomaticTask("auto2"); processDefinitionBuilder.addGateway("p2", GatewayType.PARALLEL); processDefinitionBuilder.addEndEvent("end").addTerminateEventTrigger(); processDefinitionBuilder.addTransition("start", "auto1"); processDefinitionBuilder.addTransition("auto1", "p1"); processDefinitionBuilder.addTransition("p1", "auto2"); processDefinitionBuilder.addTransition("p1", "user1"); processDefinitionBuilder.addTransition("auto2", "p2"); processDefinitionBuilder.addTransition("user1", "p2"); processDefinitionBuilder.addTransition("p2", "end"); SubProcessDefinitionBuilder eventSub = processDefinitionBuilder.addSubProcess("eventSub", true) .getSubProcessBuilder(); eventSub.addStartEvent("signalStart").addSignalEventTrigger("bip_bip"); eventSub.addAutomaticTask("subAuto1"); eventSub.addEndEvent("subEnd").addTerminateEventTrigger(); eventSub.addTransition("signalStart", "subAuto1").addTransition("subAuto1", "subEnd"); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), "john", user); //when ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance.getId(), "user1"); getProcessAPI().sendSignal("bip_bip"); //then waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/StartEventIT.java ================================================ /** * 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.event; import static org.junit.Assert.*; import java.util.List; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.TimerType; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.junit.Test; public class StartEventIT extends TestWithUser { @Test public void executeSeveralStartEventsInSameProcessDefinition() throws Exception { final int timerValue = 10000; final Expression timerExpression = new ExpressionBuilder().createConstantLongExpression(timerValue); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("startEvent").addUserTask("step1", ACTOR_NAME) .addTransition("startEvent", "step1"); processDefinitionBuilder.addStartEvent("startEventWithSignal").addSignalEventTrigger("signalName") .addUserTask("step1WithSignal", ACTOR_NAME) .addTransition("startEventWithSignal", "step1WithSignal"); processDefinitionBuilder.addStartEvent("startEventWithTimer") .addTimerEventTriggerDefinition(TimerType.DURATION, timerExpression) .addUserTask("step1WithTimer", ACTOR_NAME).addTransition("startEventWithTimer", "step1WithTimer"); processDefinitionBuilder.addStartEvent("startEventWithMessage").addMessageEventTrigger("message") .addUserTask("step1WithMessage", ACTOR_NAME) .addTransition("startEventWithMessage", "step1WithMessage"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor( processDefinitionBuilder.getProcess(), ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); final HumanTaskInstance step1 = waitForUserTaskAndGetIt("step1"); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10) .sort(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, Order.ASC); List humanTaskInstances = getProcessAPI() .searchPendingTasksForUser(user.getId(), searchOptionsBuilder.done()).getResult(); assertNotNull( "searchPendingTasksForUser give a null result for userId:" + user.getId() + " search options:" + searchOptionsBuilder.done(), humanTaskInstances); // timerValue is slower than startProcess time // then we have 2 tasks assertTrue(humanTaskInstances.size() > 0); for (final HumanTaskInstance humanTaskInstance : humanTaskInstances) { if (humanTaskInstance.getRootContainerId() == step1.getRootContainerId()) { // even if step1WithTimer is fired // the start event should be the first one assertEquals("step1", humanTaskInstance.getName()); } } // Verify that the start without trigger, and the start with a timer are started // wait for process instance creation waitForUserTask("step1WithTimer"); humanTaskInstances = getProcessAPI().searchPendingTasksForUser(user.getId(), searchOptionsBuilder.done()) .getResult(); assertEquals(2, humanTaskInstances.size()); assertEquals("step1", humanTaskInstances.get(0).getName()); assertEquals("step1WithTimer", humanTaskInstances.get(1).getName()); final ProcessInstance processInstanceWithTimer = getProcessAPI() .getProcessInstance(humanTaskInstances.get(1).getRootContainerId()); assertEquals(0, processInstanceWithTimer.getStartedBy()); assertNotEquals(startProcess.getId(), processInstanceWithTimer.getId()); // Verify that the start without trigger, the start with a timer, and the start with signal are started getProcessAPI().sendSignal("signalName"); waitForUserTask("step1WithSignal"); humanTaskInstances = getProcessAPI().searchPendingTasksForUser(user.getId(), searchOptionsBuilder.done()) .getResult(); assertEquals(3, humanTaskInstances.size()); assertEquals("step1", humanTaskInstances.get(0).getName()); assertEquals("step1WithTimer", humanTaskInstances.get(1).getName()); assertEquals("step1WithSignal", humanTaskInstances.get(2).getName()); final ProcessInstance processInstanceWithSignal = getProcessAPI() .getProcessInstance(humanTaskInstances.get(2).getRootContainerId()); assertEquals(0, processInstanceWithSignal.getStartedBy()); assertNotEquals(processInstanceWithTimer.getId(), processInstanceWithSignal.getId()); // Verify all start are started getProcessAPI().sendMessage("message", new ExpressionBuilder().createConstantStringExpression(PROCESS_NAME), new ExpressionBuilder().createConstantStringExpression("startEventWithMessage"), null); waitForUserTask("step1WithMessage"); humanTaskInstances = getProcessAPI().searchPendingTasksForUser(user.getId(), searchOptionsBuilder.done()) .getResult(); assertEquals(4, humanTaskInstances.size()); assertEquals("step1", humanTaskInstances.get(0).getName()); assertEquals("step1WithTimer", humanTaskInstances.get(1).getName()); assertEquals("step1WithSignal", humanTaskInstances.get(2).getName()); assertEquals("step1WithMessage", humanTaskInstances.get(3).getName()); final ProcessInstance processInstanceWithMessage = getProcessAPI() .getProcessInstance(humanTaskInstances.get(3).getRootContainerId()); assertEquals(0, processInstanceWithMessage.getStartedBy()); assertNotEquals(processInstanceWithSignal.getId(), processInstanceWithMessage.getId()); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/TimerBoundaryEventIT.java ================================================ /** * 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.event; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.test.TestStates; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class TimerBoundaryEventIT extends AbstractEventIT { @Test public void nonInterruptingNotTrigerAfterInterruptingTriggering() throws Exception { // deploy process final String taskWithBoundaryName = "taskWithBoundary"; final int nonInterruptingTimer = 30 * 1000; // the timer will be aborted, so no problem if it's a hight value final String interruptExceptionTaskName = "afterInterrupt"; final String nonInterruptExceptionTaskName = "afterNonInterrupt"; final String interruptTimerName = "interruptTimer"; final String nonInterruptTimerName = "nonInterruptTimer"; final ProcessDefinition processDefinition = deployAndEnableProcessWithInterruptingAndNonInterruptingTimer(100, nonInterruptingTimer, taskWithBoundaryName, interruptExceptionTaskName, nonInterruptExceptionTaskName, interruptTimerName, nonInterruptTimerName); // start process and wait for task in aborted state final ProcessInstance processInstance = getProcessAPI().startProcess(user.getId(), processDefinition.getId()); waitForFlowNodeInState(processInstance, taskWithBoundaryName, TestStates.ABORTED, false); // verify that non-interrupting timer was aborted waitForFlowNodeInState(processInstance, nonInterruptTimerName, TestStates.ABORTED, false); // verify that exception flow was taken waitForUserTask(interruptExceptionTaskName); getProcessAPI().deleteProcessInstance(processInstance.getId()); disableAndDeleteProcess(processDefinition); } @Test public void interruptingTrigerAfterNonInterruptingTriggering() throws Exception { // deploy process final String taskWithBoundaryName = "taskWithBoundary"; final String interruptExceptionTaskName = "afterInterrupt"; final String nonInterruptExceptionTaskName = "afterNonInterrupt"; final String interruptTimerName = "interruptTimer"; final String nonInterruptTimerName = "nonInterruptTimer"; final ProcessDefinition processDefinition = deployAndEnableProcessWithInterruptingAndNonInterruptingTimer(1000, 100, taskWithBoundaryName, interruptExceptionTaskName, nonInterruptExceptionTaskName, interruptTimerName, nonInterruptTimerName); // start process and wait for task in ready state final ProcessInstance processInstance = getProcessAPI().startProcess(user.getId(), processDefinition.getId()); waitForUserTask(taskWithBoundaryName); // verify that exception flow was taken for non-interrupting event waitForUserTask(nonInterruptExceptionTaskName); // wait for aborted state waitForFlowNodeInState(processInstance, taskWithBoundaryName, TestStates.ABORTED, false); // verify that interrupting event was triggered waitForUserTask(interruptExceptionTaskName); getProcessAPI().deleteProcessInstance(processInstance.getId()); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/TimerEventIT.java ================================================ /** * 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.event; import static org.junit.Assert.*; import java.util.Date; import java.util.Iterator; import java.util.List; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.flownode.EventCriterion; import org.bonitasoft.engine.bpm.flownode.EventInstance; import org.bonitasoft.engine.bpm.flownode.IntermediateCatchEventInstance; import org.bonitasoft.engine.bpm.flownode.TimerType; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.test.TestStates; import org.junit.Test; public class TimerEventIT extends TestWithUser { @Test public void timerIntermediateCatchEventDuration() throws Exception { final String step1Name = "step1"; final String step2Name = "step2"; final Expression timerExpression = new ExpressionBuilder().createConstantLongExpression(1000); // the timer intermediate catch event will wait one // second final ProcessDefinition definition = deployProcessWithTimerIntermediateCatchEventAndUserTask(TimerType.DURATION, timerExpression, step1Name, step2Name); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); waitForUserTaskAndExecuteIt(processInstance, step1Name, user); waitForEventInWaitingState(processInstance, "intermediateCatchEvent"); final long processInstanceId = processInstance.getId(); EventInstance eventInstance = getEventInstance(processInstanceId, "intermediateCatchEvent"); checkIntermediateCatchEventInstance(eventInstance, "intermediateCatchEvent", TestStates.WAITING); // wait trigger activation int cnt = 0; // BS-9586 : for mysql, we wait longer while (cnt < 10 && eventInstance != null) { Thread.sleep(1000); eventInstance = getEventInstance(processInstanceId, "intermediateCatchEvent"); cnt++; } assertNull(eventInstance);// finished waitForUserTask(processInstance, step2Name); disableAndDeleteProcess(definition); } @Test public void timerIntermediateCatchEventDate() throws Exception { final String step1Name = "step1"; final String step2Name = "step2"; final long expectedDate = System.currentTimeMillis() + 5000; final Expression timerExpression = new ExpressionBuilder() .createGroovyScriptExpression("testTimerIntermediateCatchEventDate", "return new Date(" + expectedDate + "l)", Date.class.getName()); // the timer intermediate catch // event will wait one second final ProcessDefinition definition = deployProcessWithTimerIntermediateCatchEventAndUserTask(TimerType.DATE, timerExpression, step1Name, step2Name); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); waitForUserTaskAndExecuteIt(processInstance, step1Name, user); waitForFlowNodeInState(processInstance, "intermediateCatchEvent", TestStates.WAITING, true); final EventInstance eventInstance = getEventInstance(processInstance.getId(), "intermediateCatchEvent"); checkIntermediateCatchEventInstance(eventInstance, "intermediateCatchEvent", TestStates.WAITING); // wait trigger activation waitForUserTask(processInstance, step2Name); final long now = System.currentTimeMillis(); assertTrue("Event has triggered too early !" + (now - expectedDate), expectedDate <= now); disableAndDeleteProcess(definition); } @Test public void timerStartEventDate() throws Exception { final String stepName = "step1"; final long expectedDate = System.currentTimeMillis() + 1000; final Expression timerExpression = new ExpressionBuilder() .createGroovyScriptExpression("testTimerStartEventDate", "return new Date(" + expectedDate + "l);", Date.class.getName()); // the new instance must be // created in one second final ProcessDefinition definition = deployProcessWithTimerStartEventAndUserTask(TimerType.DATE, timerExpression, stepName); final List processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.CREATION_DATE_DESC); assertTrue(processInstances.isEmpty()); // wait for process instance creation waitForUserTask(stepName); disableAndDeleteProcess(definition); } @Test public void timerStartEventDuration() throws Exception { final String stepName = "step1"; final Expression timerExpression = new ExpressionBuilder().createConstantLongExpression(1500); // the new instance must be created in one second final ProcessDefinition definition = deployProcessWithTimerStartEventAndUserTask(TimerType.DURATION, timerExpression, stepName); waitForInitializingProcess(); waitForUserTask(stepName); disableAndDeleteProcess(definition); } private EventInstance getEventInstance(final long processInstanceId, final String eventName) throws RetrieveException { final List eventInstances = getProcessAPI().getEventInstances(processInstanceId, 0, 10, EventCriterion.NAME_ASC); EventInstance searchedEventInstance = null; final Iterator iterator = eventInstances.iterator(); while (iterator.hasNext() && searchedEventInstance == null) { final EventInstance eventInstance = iterator.next(); if (eventInstance.getName().equals(eventName)) { searchedEventInstance = eventInstance; } } return searchedEventInstance; } private void checkIntermediateCatchEventInstance(final EventInstance eventInstance, final String eventName, final TestStates state) { assertTrue(eventInstance instanceof IntermediateCatchEventInstance); checkEventInstance(eventInstance, eventName, state); } private void checkEventInstance(final EventInstance eventInstance, final String eventName, final TestStates state) { assertEquals(eventName, eventInstance.getName()); assertEquals(state.getStateName(), eventInstance.getState()); // if(TestStates.getNormalFinalState(eventInstance).equals(state)) { // assertTrue(eventInstance.getEndDate() > 0); // } } private ProcessDefinition deployProcessWithTimerIntermediateCatchEventAndUserTask(final TimerType timerType, final Expression timerValue, final String step1Name, final String step2Name) throws Exception { final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My Process with start event", "1.0") .addActor(ACTOR_NAME).addDescription("Delivery all day and night long").addStartEvent("startEvent") .addUserTask(step1Name, ACTOR_NAME) .addIntermediateCatchEvent("intermediateCatchEvent") .addTimerEventTriggerDefinition(timerType, timerValue).addUserTask(step2Name, ACTOR_NAME) .addEndEvent("endEvent").addTransition("startEvent", step1Name) .addTransition(step1Name, "intermediateCatchEvent") .addTransition("intermediateCatchEvent", step2Name).addTransition(step2Name, "endEvent").getProcess(); final ProcessDefinition definition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(definition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); return definition; } private ProcessDefinition deployProcessWithTimerStartEventAndUserTask(final TimerType timerType, final Expression timerValue, final String stepName) throws Exception { final String delivery = "Delivery men"; final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My Process with start event", "1.0") .addActor(delivery).addDescription("Delivery all day and night long").addStartEvent("startEvent") .addTimerEventTriggerDefinition(timerType, timerValue).addUserTask(stepName, delivery) .addEndEvent("endEvent") .addTransition("startEvent", stepName).addTransition(stepName, "endEvent").getProcess(); final ProcessDefinition definition = deployAndEnableProcessWithActor(designProcessDefinition, delivery, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(definition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); return definition; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/event/TimerEventSubProcessIT.java ================================================ /** * 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.event; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.Date; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.data.DataNotFoundException; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.TimerType; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.StartEventDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.test.TestStates; import org.junit.Test; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros */ public class TimerEventSubProcessIT extends AbstractEventIT { @Test public void timerEventSubProcessTriggered() throws Exception { // given final int timerDuration = 2000; final ProcessDefinition process = deployAndEnableProcessWithTimerEventSubProcess(timerDuration); final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); // when final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, "subStep"); final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId()); final Date processStartDate = processInstance.getStartDate(); assertThat(subProcInst.getStartDate()).as( String.format("process started at %s should trigger subprocess at %s (+ %d ms) ", formatedDate(processStartDate), formatedDate(subProcInst.getStartDate()), timerDuration)) .isAfter(processStartDate); // cleanup assignAndExecuteStep(subStep, user); waitForProcessToFinish(subProcInst); disableAndDeleteProcess(process.getId()); } private String formatedDate(final Date date) { final SimpleDateFormat dt = new SimpleDateFormat(DATE_FORMAT_WITH_MS); return dt.format(date); } @Test public void timerEventSubProcessNotTriggered() throws Exception { final int timerDuration = 6000; final ProcessDefinition process = deployAndEnableProcessWithTimerEventSubProcess(timerDuration); final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); waitForUserTaskAndExecuteIt(processInstance, "step1", user); waitForProcessToFinish(processInstance); Thread.sleep(timerDuration); disableAndDeleteProcess(process.getId()); } // added after problems with job name @Test public void testCreateSeveralInstances() throws Exception { final int timerDuration = 500; final ProcessDefinition process = deployAndEnableProcessWithTimerEventSubProcess(timerDuration); final ProcessInstance processInstance1 = getProcessAPI().startProcess(process.getId()); final ProcessInstance processInstance2 = getProcessAPI().startProcess(process.getId()); waitForUserTask(processInstance1, "subStep"); waitForUserTask(processInstance2, "subStep"); disableAndDeleteProcess(process.getId()); } @Test public void subProcessCanAccessParentData() throws Exception { final int timerDuration = 2000; final ProcessDefinition process = deployAndEnableProcessWithTimerEventSubProcessAndData(timerDuration); final ProcessInstance processInstance = getProcessAPI().startProcess(process.getId()); final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, "subStep"); final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId()); checkProcessDataInstance("count", subProcInst.getId(), 1); checkProcessDataInstance("content", subProcInst.getId(), "childVar"); checkProcessDataInstance("value", subProcInst.getId(), 10.0); checkProcessDataInstance("content", processInstance.getId(), "parentVar"); checkActivityDataInstance("content", subStep.getId(), "childActivityVar"); assignAndExecuteStep(subStep, user.getId()); waitForProcessToFinish(subProcInst); waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED); disableAndDeleteProcess(process.getId()); } private void checkProcessDataInstance(final String dataName, final long processInstanceId, final Serializable expectedValue) throws DataNotFoundException { final DataInstance processDataInstance; processDataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstanceId); assertEquals(expectedValue, processDataInstance.getValue()); } private void checkActivityDataInstance(final String dataName, final long activityInstanceId, final Serializable expectedValue) throws DataNotFoundException { final DataInstance activityDataInstance; activityDataInstance = getProcessAPI().getActivityDataInstance(dataName, activityInstanceId); assertEquals(expectedValue, activityDataInstance.getValue()); } @Test public void timerEventSubProcInsideTargetCallActivity() throws Exception { final ProcessDefinition targetProcess = deployAndEnableProcessWithTimerEventSubProcess(2000); final ProcessDefinition callerProcess = deployAndEnableProcessWithCallActivity(targetProcess.getName(), targetProcess.getVersion()); final ProcessInstance processInstance = getProcessAPI().startProcess(callerProcess.getId()); final ActivityInstance step1 = waitForUserTaskAndGetIt(processInstance, "step1"); final ActivityInstance subStep = waitForUserTaskAndGetIt(processInstance, "subStep"); final ProcessInstance calledProcInst = getProcessAPI().getProcessInstance(step1.getParentProcessInstanceId()); final ProcessInstance subProcInst = getProcessAPI().getProcessInstance(subStep.getParentProcessInstanceId()); waitForFlowNodeInState(processInstance, "step1", TestStates.ABORTED, true); assignAndExecuteStep(subStep, user); waitForProcessToFinish(subProcInst); waitForProcessToBeInState(calledProcInst, ProcessInstanceState.ABORTED); waitForUserTaskAndExecuteIt(processInstance, "step2", user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(callerProcess.getId()); disableAndDeleteProcess(targetProcess.getId()); } @Test public void should_be_able_to_init_timer_from_data_in_parent() throws Exception { //given ProcessDefinitionBuilder parentProcessBuilder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithESPTimerBasedOnData", "1.0"); parentProcessBuilder.addActor(ACTOR_NAME); parentProcessBuilder.addUserTask("userTask", ACTOR_NAME); parentProcessBuilder.addLongData("timeToWait", new ExpressionBuilder().createConstantLongExpression(1000)); //construct sub process SubProcessDefinitionBuilder subProcessBuilder = parentProcessBuilder .addSubProcess("subProcessToStartWithTimer", true).getSubProcessBuilder(); StartEventDefinitionBuilder startEventDefinitionBuilder = subProcessBuilder.addStartEvent("timerStart"); startEventDefinitionBuilder.addTimerEventTriggerDefinition(TimerType.DURATION, new ExpressionBuilder().createDataExpression("timeToWait", Long.class.getName())); subProcessBuilder.addUserTask("userTaskInSubProcess", ACTOR_NAME); subProcessBuilder.addEndEvent("endSubProcess"); subProcessBuilder.addTransition("timerStart", "userTaskInSubProcess"); subProcessBuilder.addTransition("userTaskInSubProcess", "endSubProcess"); DesignProcessDefinition processDefinition1 = parentProcessBuilder.done(); BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDefinition1); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, user); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); //when waitForUserTask("userTaskInSubProcess"); //then disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/filter/user/UserFilterIT.java ================================================ /** * 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.filter.user; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.List; import java.util.Map; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.comment.Comment; import org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.connectors.VariableStorage; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.check.CheckNbAssignedTaskOf; import org.bonitasoft.engine.test.check.CheckNbPendingTaskOf; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Baptiste Mesta * @author Yanyan Liu */ public class UserFilterIT extends TestWithTechnicalUser { private static final String JOHN = "john"; private static final String JACK = "jack"; private static final String JAMES = "james"; private User john; private User jack; private User james; @Override @Before public void before() throws Exception { super.before(); john = createUser(JOHN, "bpm"); jack = createUser(JACK, "bpm"); james = createUser(JAMES, "bpm"); logout(); loginOnDefaultTenantWith(JOHN, "bpm"); } @Override @After public void after() throws Exception { deleteUser(JOHN); deleteUser(JACK); deleteUser(JAMES); VariableStorage.clearAll(); super.after(); } @Test public void filterTask() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("processWithUserFilter", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); designProcessDefinition.addAutomaticTask("step1"); final UserTaskDefinitionBuilder addUserTask = designProcessDefinition.addUserTask("step2", ACTOR_NAME); addUserTask.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilter", "1.0").addInput("userId", new ExpressionBuilder().createConstantLongExpression(jack.getId())); designProcessDefinition.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, ACTOR_NAME, john, "TestFilter"); getProcessAPI().startProcess(processDefinition.getId()); assertTrue(new CheckNbPendingTaskOf(getProcessAPI(), 50, 5000, false, 1, jack).waitUntil()); List pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, pendingTasks.size()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(1, pendingTasks.size()); disableAndDeleteProcess(processDefinition); } /* * Task must be in failed state even for any exception thrown is the user filter */ @Test public void filterTaskWithUserFilterNotFound() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("processWithUserFilter", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); designProcessDefinition.addAutomaticTask("step1"); final UserTaskDefinitionBuilder addUserTask = designProcessDefinition.addUserTask("step2", ACTOR_NAME); addUserTask.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilterWithClassNotFound", "1.0") .addInput("userId", new ExpressionBuilder().createConstantLongExpression(jack.getId())); designProcessDefinition.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, ACTOR_NAME, john, "TestFilterWithClassNotFound"); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForTaskToFail(processInstance); disableAndDeleteProcess(processDefinition.getId()); } /* * Task must be in failed state even for any exception thrown is the user filter */ @Test public void filterTaskWithUserFilterThatThrowException() throws Exception { final ProcessDefinitionBuilder processWithRuntime = new ProcessDefinitionBuilder() .createNewInstance("processWithUserFilterRuntimeException", "1.0"); processWithRuntime.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); processWithRuntime.addAutomaticTask("step1"); final UserTaskDefinitionBuilder addUserTask = processWithRuntime.addUserTask("step2", ACTOR_NAME); addUserTask.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilterThatThrowException", "1.0") .addInput("exception", new ExpressionBuilder().createConstantStringExpression("runtime")); processWithRuntime.addTransition("step1", "step2"); final ProcessDefinition processDefinitionWithRuntime = deployProcessWithTestFilter(processWithRuntime, ACTOR_NAME, john, "TestFilterThatThrowException"); final ProcessDefinitionBuilder processWithException = new ProcessDefinitionBuilder() .createNewInstance("processWithUserFilterNormalException", "1.0"); processWithException.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); processWithException.addAutomaticTask("step1"); final UserTaskDefinitionBuilder addUserTask2 = processWithException.addUserTask("step2", ACTOR_NAME); addUserTask2.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilterThatThrowException", "1.0") .addInput("exception", new ExpressionBuilder().createConstantStringExpression("normal")); processWithException.addTransition("step1", "step2"); final ProcessDefinition processDefinitionWithException = deployProcessWithTestFilter(processWithException, ACTOR_NAME, john, "TestFilterThatThrowException"); final ProcessDefinitionBuilder processWithNoClassDef = new ProcessDefinitionBuilder() .createNewInstance("processWithUserFilterNoClassDef", "1.0"); processWithNoClassDef.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); processWithNoClassDef.addAutomaticTask("step1"); final UserTaskDefinitionBuilder addUserTask3 = processWithNoClassDef.addUserTask("step2", ACTOR_NAME); addUserTask3.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilterThatThrowNoClassDef", "1.0") .addInput("exception", new ExpressionBuilder().createConstantStringExpression("normal")); processWithNoClassDef.addTransition("step1", "step2"); final ProcessDefinition processDefinitionNoClassDef = deployProcessWithTestFilter(processWithNoClassDef, ACTOR_NAME, john, "TestFilterThatThrowNoClassDef"); final ProcessInstance processInstanceException = getProcessAPI() .startProcess(processDefinitionWithException.getId()); final ProcessInstance processInstanceRuntime = getProcessAPI() .startProcess(processDefinitionWithRuntime.getId()); final ProcessInstance processInstanceNoClassDef = getProcessAPI() .startProcess(processDefinitionNoClassDef.getId()); waitForTaskToFail(processInstanceRuntime); waitForTaskToFail(processInstanceException); waitForTaskToFail(processInstanceNoClassDef); disableAndDeleteProcess(processDefinitionWithRuntime); disableAndDeleteProcess(processDefinitionWithException); disableAndDeleteProcess(processDefinitionNoClassDef); } @Test public void filterTaskWithAutoAssign() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("processWithUserFilterWithAutoAssign", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); designProcessDefinition.addAutomaticTask("step1"); final UserTaskDefinitionBuilder addUserTask = designProcessDefinition.addUserTask("step2", ACTOR_NAME); addUserTask.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilterWithAutoAssign", "1.0").addInput( "userId", new ExpressionBuilder().createConstantLongExpression(jack.getId())); addUserTask .addDisplayName(new ExpressionBuilder().createConstantStringExpression("A task to test user filter")); designProcessDefinition.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, ACTOR_NAME, john, "TestFilterWithAutoAssign"); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); assertTrue(new CheckNbAssignedTaskOf(getProcessAPI(), 50, 5000, false, 1, jack).waitUntil()); List tasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, tasks.size()); tasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(1, tasks.size()); final SearchResult commentSearchResult = getProcessAPI().searchComments( new SearchOptionsBuilder(0, 10) .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()).done()); assertThat(commentSearchResult.getResult()).hasSize(1); assertThat(commentSearchResult.getResult().get(0).getContent()) .isEqualTo("The task \"A task to test user filter\" is now assigned to jack"); disableAndDeleteProcess(processDefinition); } @Test public void filterTaskWithNullInput() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("processWithUserFilterUsingActorName", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); designProcessDefinition.addAutomaticTask("step1"); final UserTaskDefinitionBuilder addUserTask = designProcessDefinition.addUserTask("step2", ACTOR_NAME); addUserTask.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilterUsingActorName", "1.0") .addInput("userIds", null); designProcessDefinition.addTransition("step1", "step2"); assertThatExceptionOfType(BonitaException.class) .isThrownBy(() -> deployProcessWithTestFilter(designProcessDefinition, ACTOR_NAME, john, "TestFilterUsingActorName")) .withRootCauseInstanceOf(InvalidProcessDefinitionException.class); } @Test public void filterTaskUsingFilterName() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("processWithUserFilterUsingActorName", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addDescription("ACTOR_NAME all day and night long"); designProcessDefinition.addAutomaticTask("step1"); final UserTaskDefinitionBuilder addUserTask = designProcessDefinition.addUserTask("step2", ACTOR_NAME); addUserTask.addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilterUsingActorName", "1.0").addInput( "userIds", new ExpressionBuilder().createGroovyScriptExpression("myScript", "['" + ACTOR_NAME + "':" + james.getId() + "l,'notACTOR_NAME':" + jack.getId() + "l]", Map.class.getName())); designProcessDefinition.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, ACTOR_NAME, john, "TestFilterUsingActorName"); getProcessAPI().startProcess(processDefinition.getId()); assertTrue(new CheckNbPendingTaskOf(getProcessAPI(), 50, 6000, false, 1, james).waitUntil()); List pendingTasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(0, pendingTasks.size()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(0, pendingTasks.size()); pendingTasks = getProcessAPI().getPendingHumanTaskInstances(james.getId(), 0, 10, null); assertEquals(1, pendingTasks.size()); disableAndDeleteProcess(processDefinition); } @Test public void updateUserFilterAfterAUserDeletion() throws Exception { final Group group = createGroup("group1"); final Role role = createRole("role1"); createUserMembership(jack.getUserName(), "role1", "group1"); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithUserFilterWithAutoAssign", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder.addAutomaticTask("step1"); final UserTaskDefinitionBuilder addUserTask = processBuilder.addUserTask("step2", ACTOR_NAME); addUserTask.addUserFilter("test", "org.bonitasoft.engine.filter.user.GroupUserFilter", "1.0").addInput( "groupId", new ExpressionBuilder().createConstantLongExpression(group.getId())); processBuilder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithTestFilter(processBuilder, ACTOR_NAME, john, "GroupUserFilter"); getProcessAPI().startProcess(processDefinition.getId()); assertTrue(new CheckNbAssignedTaskOf(getProcessAPI(), 50, 5000, false, 1, jack).waitUntil()); List tasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(1, tasks.size()); getIdentityAPI().deleteUser(jack.getId()); createUserMembership(john.getUserName(), "role1", "group1"); tasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(1, tasks.size()); getProcessAPI().updateActorsOfUserTask(tasks.get(0).getId()); tasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(0, tasks.size()); tasks = getProcessAPI().getAssignedHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, tasks.size()); deleteGroups(group); deleteRoles(role); disableAndDeleteProcess(processDefinition); } @Test(expected = UpdateException.class) public void unableToUpdateActorsOnAGateway() throws Exception { final Expression scriptExpression = new ExpressionBuilder().createGroovyScriptExpression("mycondition", "fzdfsdfsdfsdfsdf", Boolean.class.getName()); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_exclusive_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addGateway("gateway1", GatewayType.EXCLUSIVE) .addTransition("step1", "gateway1").addTransition("gateway1", "step2", scriptExpression) .addDefaultTransition("gateway1", "step3").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, john); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final FlowNodeInstance failFlowNodeInstance = waitForFlowNodeInFailedState(processInstance); assertEquals("gateway1", failFlowNodeInstance.getName()); try { getProcessAPI().updateActorsOfUserTask(failFlowNodeInstance.getId()); } finally { disableAndDeleteProcess(processDefinition); } } @Test public void doNotUpateAHumanTaskIfNoUserFilterIsDefined() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("doNotUpateAHumanTaskIfNoUserFilterIsDefined", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder.addAutomaticTask("step1"); processBuilder.addUserTask("step2", ACTOR_NAME); processBuilder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, john); getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask("step2"); List tasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT); assertEquals(1, tasks.size()); final HumanTaskInstance taskBefore = tasks.get(0); getProcessAPI().updateActorsOfUserTask(tasks.get(0).getId()); tasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT); assertEquals(1, tasks.size()); assertEquals(taskBefore, tasks.get(0)); disableAndDeleteProcess(processDefinition); } @Test public void updateUserFilterAfterAUserAdd() throws Exception { final Group group = createGroup("group1"); final Role role = createRole("role1"); createUserMembership(jack.getUserName(), "role1", "group1"); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("updateUserFilterAfterAUserAdd", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder.addAutomaticTask("step1"); final UserTaskDefinitionBuilder addUserTask = processBuilder.addUserTask("step2", ACTOR_NAME); addUserTask.addUserFilter("test", "org.bonitasoft.engine.filter.user.GroupUserFilter", "1.0").addInput( "groupId", new ExpressionBuilder().createConstantLongExpression(group.getId())); processBuilder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithTestFilter(processBuilder, ACTOR_NAME, john, "GroupUserFilter"); getProcessAPI().startProcess(processDefinition.getId()); assertTrue(new CheckNbAssignedTaskOf(getProcessAPI(), 50, 5000, false, 1, jack).waitUntil()); List tasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(1, tasks.size()); createUserMembership(john.getUserName(), "role1", "group1"); tasks = getProcessAPI().getAssignedHumanTaskInstances(jack.getId(), 0, 10, null); assertEquals(1, tasks.size()); getProcessAPI().updateActorsOfUserTask(tasks.get(0).getId()); tasks = getProcessAPI().getPendingHumanTaskInstances(jack.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT); assertEquals(1, tasks.size()); assertEquals(0, tasks.get(0).getAssigneeId()); tasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT); assertEquals(1, tasks.size()); assertEquals(0, tasks.get(0).getAssigneeId()); deleteGroups(group); deleteRoles(role); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/form/FormMappingIT.java ================================================ /** * 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.form; import static org.assertj.core.api.Assertions.assertThat; import java.io.Serializable; import java.util.Collections; import java.util.Map; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.form.FormMappingModelBuilder; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.V6FormDeployException; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.page.PageURL; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; /** * @author Baptiste Mesta */ public class FormMappingIT extends TestWithUser { private final Map context = Collections.emptyMap(); @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void deployProcessesWithFormMappings() throws Exception { ProcessDefinitionBuilder p1Builder = new ProcessDefinitionBuilder().createNewInstance("P1", "1.0"); p1Builder.addUserTask("step1", "actor").addUserTask("step2", "actor"); p1Builder.addActor("actor"); BusinessArchiveBuilder bar1 = new BusinessArchiveBuilder() .createNewBusinessArchive() .setProcessDefinition(p1Builder.done()) .setFormMappings( FormMappingModelBuilder.buildFormMappingModel() .addProcessStartForm("processStartForm", FormMappingTarget.URL) .addTaskForm("task1Form", FormMappingTarget.INTERNAL, "step1") .addProcessOverviewForm("process1OverviewForm", FormMappingTarget.INTERNAL).build()); ProcessDefinitionBuilder p2Builder = new ProcessDefinitionBuilder().createNewInstance("P2", "1.0"); p2Builder.addUserTask("step1", "actor").addUserTask("step2", "actor"); p2Builder.addActor("actor"); BusinessArchiveBuilder bar2 = new BusinessArchiveBuilder() .createNewBusinessArchive() .setProcessDefinition(p2Builder.done()) .setFormMappings( FormMappingModelBuilder.buildFormMappingModel() .addProcessStartForm("processStartForm", FormMappingTarget.URL) .addTaskForm("task2Form", FormMappingTarget.URL, "step1") .addProcessOverviewForm(null, FormMappingTarget.LEGACY).build()); ProcessDefinition p1 = getProcessAPI().deploy(bar1.done()); ProcessAPI processConfigurationAPI = getProcessAPI(); //get FormMapping processStartForm1 = processConfigurationAPI.searchFormMappings(new SearchOptionsBuilder(0, 10) .filter(FormMappingSearchDescriptor.TYPE, FormMappingType.PROCESS_START) .filter(FormMappingSearchDescriptor.PROCESS_DEFINITION_ID, p1.getId()) .done()).getResult().get(0); FormMapping processOverviewForm1 = processConfigurationAPI .searchFormMappings( new SearchOptionsBuilder(0, 10) .filter(FormMappingSearchDescriptor.TYPE, FormMappingType.PROCESS_OVERVIEW) .filter(FormMappingSearchDescriptor.PROCESS_DEFINITION_ID, p1.getId()) .done()) .getResult().get(0); FormMapping step1Form1 = processConfigurationAPI.searchFormMappings(new SearchOptionsBuilder(0, 10) .filter(FormMappingSearchDescriptor.TYPE, FormMappingType.TASK) .filter(FormMappingSearchDescriptor.PROCESS_DEFINITION_ID, p1.getId()) .filter(FormMappingSearchDescriptor.TASK, "step1").done()).getResult().get(0); FormMapping step2Form1 = processConfigurationAPI.searchFormMappings(new SearchOptionsBuilder(0, 10) .filter(FormMappingSearchDescriptor.TYPE, FormMappingType.TASK) .filter(FormMappingSearchDescriptor.PROCESS_DEFINITION_ID, p1.getId()) .filter(FormMappingSearchDescriptor.TASK, "step2").done()).getResult().get(0); assertThat(processStartForm1.getProcessDefinitionId()).isEqualTo(p1.getId()); assertThat(processStartForm1.getPageId()).isNull(); assertThat(processStartForm1.getURL()).isEqualTo("processStartForm"); assertThat(processStartForm1.getTarget()).isEqualTo(FormMappingTarget.URL); assertThat(processOverviewForm1.getProcessDefinitionId()).isEqualTo(p1.getId()); assertThat(processOverviewForm1.getPageId()).isNull(); assertThat(processOverviewForm1.getURL()).isNull(); assertThat(processOverviewForm1.getTarget()).isEqualTo(FormMappingTarget.INTERNAL); // referenced page does not exists assertThat(step1Form1.getProcessDefinitionId()).isEqualTo(p1.getId()); assertThat(step1Form1.getPageId()).isNull(); assertThat(step2Form1.getProcessDefinitionId()).isEqualTo(p1.getId()); assertThat(step2Form1.getURL()).isNull(); assertThat(step2Form1.getTarget()).isEqualTo(FormMappingTarget.NONE); //search SearchResult formMappingSearchResult = processConfigurationAPI .searchFormMappings(new SearchOptionsBuilder(0, 100).sort( FormMappingSearchDescriptor.ID, Order.DESC).done()); assertThat(formMappingSearchResult.getCount()).isEqualTo(4); assertThat(formMappingSearchResult.getResult()).extracting("processDefinitionId").containsExactly( p1.getId(), p1.getId(), p1.getId(), p1.getId()); assertThat(formMappingSearchResult.getCount()).isEqualTo(4); formMappingSearchResult = processConfigurationAPI .searchFormMappings(new SearchOptionsBuilder(0, 100).sort(FormMappingSearchDescriptor.ID, Order.DESC) .filter(FormMappingSearchDescriptor.TASK, "step1").done()); assertThat(formMappingSearchResult.getCount()).isEqualTo(1); formMappingSearchResult = processConfigurationAPI .searchFormMappings(new SearchOptionsBuilder(0, 100).sort(FormMappingSearchDescriptor.ID, Order.DESC) .filter(FormMappingSearchDescriptor.TYPE, FormMappingType.PROCESS_START).done()); assertThat(formMappingSearchResult.getCount()).isEqualTo(1); // to be allowed to access URL: getProcessAPI().createProcessSupervisorForUser(p1.getId(), user.getId()); //resolve urls: PageURL p1Instanciation = getPageAPI().resolvePageOrURL("process/P1/1.0", context, true); PageURL p1Overview = getPageAPI().resolvePageOrURL("processInstance/P1/1.0", context, true); PageURL p1step1Instanciation = getPageAPI().resolvePageOrURL("taskInstance/P1/1.0/step1", context, true); assertThat(p1Instanciation.getUrl()).isEqualTo("processStartForm"); assertThat(p1Overview.getPageId()).isNull(); assertThat(p1step1Instanciation.getUrl()).isEqualTo(null); getProcessAPI().deleteProcessDefinition(p1.getId()); assertThat( processConfigurationAPI .searchFormMappings(new SearchOptionsBuilder(0, 100) .sort(FormMappingSearchDescriptor.ID, Order.DESC).done()) .getResult()) .isEmpty(); } @Test public void deployProcessesWithV6FormMappingsFails() throws Exception { // given: ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("P2", "1.0"); processBuilder.addUserTask("step1", "actor").addUserTask("step2", "actor"); BusinessArchiveBuilder bar = new BusinessArchiveBuilder() .createNewBusinessArchive() .setProcessDefinition(processBuilder.done()) .setFormMappings( FormMappingModelBuilder.buildFormMappingModel() .addProcessStartForm("processStartForm", FormMappingTarget.URL) .addTaskForm("task2Form", FormMappingTarget.URL, "step1") .addProcessOverviewForm(null, FormMappingTarget.LEGACY).build()); // then: expectedException.expect(V6FormDeployException.class); expectedException.expectMessage("The process contains v6 forms"); // when: getProcessAPI().deploy(bar.done()); } @Test public void resolvePageOrURLThrowsNotFoundExceptionForUndefinedFormMapping() throws Exception { ProcessDefinitionBuilder p1Builder = new ProcessDefinitionBuilder().createNewInstance("CustomerSupport", "1.0"); p1Builder.addActor("actor").addUserTask("step", "actor"); BusinessArchiveBuilder bar = new BusinessArchiveBuilder() .createNewBusinessArchive().setProcessDefinition(p1Builder.done()) .setFormMappings(FormMappingModelBuilder.buildFormMappingModel() .addTaskForm(null, FormMappingTarget.UNDEFINED, "step").build()); ProcessDefinition processDefinition = deployProcess(bar.done()); expectedException.expect(NotFoundException.class); // try to resolve url: try { getPageAPI().resolvePageOrURL("taskInstance/CustomerSupport/1.0/step", Collections.emptyMap(), true); } finally { deleteProcess(processDefinition); } } @Test public void resolvePageOrURL_should_return_null_mapping_for_NONE() throws Exception { ProcessDefinitionBuilder pBuilder = new ProcessDefinitionBuilder().createNewInstance("CustomerSupport", "1.0"); pBuilder.addActor("actor").addUserTask("step", "actor"); BusinessArchiveBuilder bar = new BusinessArchiveBuilder() .createNewBusinessArchive().setProcessDefinition(pBuilder.done()) .setFormMappings(FormMappingModelBuilder.buildFormMappingModel() .addTaskForm(null, FormMappingTarget.NONE, "step").build()); ProcessDefinition processDefinition = deployProcess(bar.done()); // to be allowed to access URL: getProcessAPI().createProcessSupervisorForUser(processDefinition.getId(), user.getId()); // try to resolve url: PageURL pageURL = getPageAPI().resolvePageOrURL("taskInstance/CustomerSupport/1.0/step", context, true); assertThat(pageURL.getPageId()).isNull(); assertThat(pageURL.getUrl()).isNull(); deleteProcess(processDefinition); } @Test public void deployProcessWithInternalPagesIncludedShouldBeResolved() throws Exception { Page custompage_globalpage = getPageAPI().createPage("globalPage.zip", createTestPageContent("custompage_globalpage", "Global page", "a global page")); ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("CustomerSupport", "1.12"); final String custompage_startProcessForm = "custompage_startProcessForm"; BusinessArchiveBuilder bar = new BusinessArchiveBuilder() .createNewBusinessArchive() .setProcessDefinition(processBuilder.done()) .setFormMappings( FormMappingModelBuilder.buildFormMappingModel() .addProcessStartForm("custompage_startProcessForm", FormMappingTarget.INTERNAL) .addProcessOverviewForm("custompage_globalpage", FormMappingTarget.INTERNAL).build()) .addExternalResource( new BarResource("customPages/custompage_startProcessForm.zip", createTestPageContent(custompage_startProcessForm, "kikoo", "LOL"))); final ProcessDefinition processDefinition = deployProcess(bar.done()); final Page page = getPageAPI().getPageByNameAndProcessDefinitionId(custompage_startProcessForm, processDefinition.getId()); assertThat(page.getId()).isNotNull(); assertThat(getProcessAPI().getProcessResolutionProblems(processDefinition.getId())).isEmpty(); getProcessAPI().enableProcess(processDefinition.getId()); // Should not throw Exception final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertThat(processDeploymentInfo.getConfigurationState()).isEqualTo(ConfigurationState.RESOLVED); // to be allowed to access URL: getProcessAPI().createProcessSupervisorForUser(processDefinition.getId(), user.getId()); final PageURL pageURLStart = getPageAPI().resolvePageOrURL("process/CustomerSupport/1.12", context, true); final PageURL pageURLOverview = getPageAPI().resolvePageOrURL("processInstance/CustomerSupport/1.12", context, true); assertThat(pageURLStart.getPageId()).isNotNull(); assertThat(page.getId()).isEqualTo(pageURLStart.getPageId()); assertThat(pageURLOverview.getPageId()).isNotNull(); assertThat(custompage_globalpage.getId()).isEqualTo(pageURLOverview.getPageId()); getPageAPI().deletePage(page.getId()); assertThat(getProcessAPI().getProcessResolutionProblems(processDefinition.getId())).hasSize(1); getPageAPI().deletePage(custompage_globalpage.getId()); disableAndDeleteProcess(processDefinition); } @Test public void processWithEventSubProcess() throws Exception { ProcessDefinitionBuilder p1Builder = new ProcessDefinitionBuilder().createNewInstance("P1", "1.0"); p1Builder.addUserTask("step1", "actor").addUserTask("step2", "actor"); p1Builder.addActor("actor"); final SubProcessDefinitionBuilder eventSubProc = p1Builder.addSubProcess("eventSubProc", true) .getSubProcessBuilder(); eventSubProc.addUserTask("subTask", "actor"); eventSubProc.addStartEvent("start").addSignalEventTrigger("theSignal"); eventSubProc.addTransition("start", "subTask"); BusinessArchiveBuilder bar1 = new BusinessArchiveBuilder() .createNewBusinessArchive() .setProcessDefinition(p1Builder.done()) .setFormMappings( FormMappingModelBuilder.buildFormMappingModel() .addProcessStartForm("processStartForm", FormMappingTarget.URL) .addTaskForm("task1Form", FormMappingTarget.INTERNAL, "step1") .addTaskForm("urlForThesubTask", FormMappingTarget.URL, "subTask") .addProcessOverviewForm("process1OverviewForm", FormMappingTarget.INTERNAL).build()); ProcessDefinition p1 = getProcessAPI().deploy(bar1.done()); SearchResult formMappingSearchResult = getProcessAPI() .searchFormMappings(new SearchOptionsBuilder(0, 100).sort( FormMappingSearchDescriptor.ID, Order.DESC).filter(FormMappingSearchDescriptor.TASK, "subTask") .done()); assertThat(formMappingSearchResult.getCount()).isEqualTo(1); assertThat(formMappingSearchResult.getResult().get(0).getURL()).isEqualTo("urlForThesubTask"); deleteProcess(p1); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/identity/CustomUserInfoIT.java ================================================ /** * 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.identity; import static org.assertj.core.api.Assertions.assertThat; import java.util.List; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.After; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class CustomUserInfoIT extends TestWithUser { private static String DEFAULT_NAME = "Skills"; @Override @After public void after() throws Exception { final int pageSize = 20; List definitions; do { definitions = getIdentityAPI().getCustomUserInfoDefinitions(0, pageSize); deleteUserInfo(definitions); } while (definitions.size() == pageSize); super.after(); } @Test public void createCustomUserInfoDefinition_should_return_the_new_created_object() throws Exception { // given final CustomUserInfoDefinitionCreator creator = new CustomUserInfoDefinitionCreator(DEFAULT_NAME, "The user skills."); // when final CustomUserInfoDefinition info = getIdentityAPI().createCustomUserInfoDefinition(creator); // then assertThat(info.getName()).isEqualTo(DEFAULT_NAME); assertThat(info.getDescription()).isEqualTo("The user skills."); } @Test public void getCustomUserInfoDefinitions_return_objects_according_to_pagination_size_and_ordered_by_name_asc() throws Exception { // given final CustomUserInfoDefinition skills = getIdentityAPI() .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator(DEFAULT_NAME)); final CustomUserInfoDefinition a = getIdentityAPI() .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator("A")); final CustomUserInfoDefinition b = getIdentityAPI() .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator("B")); final int pagegSize = 2; // when List definitions = getIdentityAPI().getCustomUserInfoDefinitions(0, pagegSize); // then (first page) assertThat(definitions).containsExactly(a, b); // when definitions = getIdentityAPI().getCustomUserInfoDefinitions(2, pagegSize); // then (second page) assertThat(definitions).containsExactly(skills); } @Test public void getCustomUserInfo_should_return_all_info_even_when_value_is_null() throws Exception { // given final CustomUserInfoDefinition job = getIdentityAPI() .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator("job")); final CustomUserInfoDefinition skill = getIdentityAPI() .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator("skill")); getIdentityAPI().setCustomUserInfoValue(skill.getId(), user.getId(), "java"); // when final List infoPage1 = getIdentityAPI().getCustomUserInfo(user.getId(), 0, 1); final List infoPage2 = getIdentityAPI().getCustomUserInfo(user.getId(), 1, 1); // then assertThat(infoPage1.get(0).getDefinition()).isEqualTo(job); assertThat(infoPage1.get(0).getValue()).isEqualTo(null); assertThat(infoPage2.get(0).getDefinition()).isEqualTo(skill); assertThat(infoPage2.get(0).getValue()).isEqualTo("java"); } @Test public void setCustomUserInfoValue_should_delete_CustomUserInfoValue_when_set_to_null() throws Exception { // given final CustomUserInfoDefinition job = getIdentityAPI() .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator("job")); getIdentityAPI().setCustomUserInfoValue(job.getId(), user.getId(), "code slayer"); final CustomUserInfoDefinition skill = getIdentityAPI() .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator("skill")); getIdentityAPI().setCustomUserInfoValue(skill.getId(), user.getId(), "java"); // when getIdentityAPI().setCustomUserInfoValue(skill.getId(), user.getId(), null); // then final List values = getIdentityAPI() .searchCustomUserInfoValues(new SearchOptionsBuilder(0, 10).done()).getResult(); assertThat(values.get(0).getDefinitionId()).isEqualTo(job.getId()); assertThat(values.get(0).getUserId()).isEqualTo(user.getId()); assertThat(values.get(0).getValue()).isEqualTo("code slayer"); } @Test public void setCustomUserInfoValue_should_update_CustomUserInfoValue_when_one_already_exist() throws Exception { // given final CustomUserInfoDefinition job = getIdentityAPI() .createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator("job")); getIdentityAPI().setCustomUserInfoValue(job.getId(), user.getId(), "code slayer"); // when getIdentityAPI().setCustomUserInfoValue(job.getId(), user.getId(), "or not"); // then final List values = getIdentityAPI() .searchCustomUserInfoValues(new SearchOptionsBuilder(0, 10).done()).getResult(); assertThat(values.get(0).getDefinitionId()).isEqualTo(job.getId()); assertThat(values.get(0).getUserId()).isEqualTo(user.getId()); assertThat(values.get(0).getValue()).isEqualTo("or not"); } private void deleteUserInfo(final List definitions) throws DeletionException { for (final CustomUserInfoDefinition definition : definitions) { getIdentityAPI().deleteCustomUserInfoDefinition(definition.getId()); } } @Test public void deleteCustomUserInfoDefinition_should_delete_definition_and_values_from_database() throws Exception { // given final CustomUserInfoDefinition info = createDefinition(DEFAULT_NAME); getIdentityAPI().setCustomUserInfoValue(info.getId(), user.getId(), "Java"); assertThat(getIdentityAPI().getCustomUserInfoDefinitions(0, 10)).containsExactly(info); final SearchOptions searchOptions = new SearchOptionsBuilder(0, 1).done(); assertThat(getIdentityAPI().searchCustomUserInfoValues(searchOptions).getCount()).isEqualTo(1); // when getIdentityAPI().deleteCustomUserInfoDefinition(info.getId()); final List definitions = getIdentityAPI().getCustomUserInfoDefinitions(0, 10); final SearchResult values = getIdentityAPI().searchCustomUserInfoValues(searchOptions); // then assertThat(definitions).isEmpty(); assertThat(values.getCount()).isEqualTo(0); } @Test public void deleteUser_should_delete_related_custom_user_info_value_from_database() throws Exception { // given final CustomUserInfoDefinition info = createDefinition(DEFAULT_NAME); final User user = createUser("user.with.custom.user.info", "bpm"); getIdentityAPI().setCustomUserInfoValue(info.getId(), user.getId(), "Java"); assertThat(getIdentityAPI().getCustomUserInfoDefinitions(0, 10)).containsExactly(info); final SearchOptions searchOptions = new SearchOptionsBuilder(0, 1).done(); assertThat(getIdentityAPI().searchCustomUserInfoValues(searchOptions).getCount()).isEqualTo(1); // when getIdentityAPI().deleteUser(user.getId()); final List definitions = getIdentityAPI().getCustomUserInfoDefinitions(0, 10); final SearchResult values = getIdentityAPI().searchCustomUserInfoValues(searchOptions); // then assertThat(definitions.size()).isEqualTo(1); assertThat(values.getCount()).isEqualTo(0); } private CustomUserInfoDefinition createDefinition(final String name) throws CreationException { final CustomUserInfoDefinitionCreator creator = new CustomUserInfoDefinitionCreator(name); return getIdentityAPI().createCustomUserInfoDefinition(creator); } @Test public void getUserIdsWithCustomUserInfo_should_return_only_users_with_the_given_user_info() throws Exception { //given final User user2 = createUser("jack", "bpm"); final CustomUserInfoDefinition info = createDefinition(DEFAULT_NAME); getIdentityAPI().setCustomUserInfoValue(info.getId(), user.getId(), "Java"); getIdentityAPI().setCustomUserInfoValue(info.getId(), user2.getId(), "C++"); //when final List userIdsExactMatch = getIdentityAPI().getUserIdsWithCustomUserInfo(DEFAULT_NAME, "C++", false, 0, 10); final List userIdsExactMatchNoResults = getIdentityAPI().getUserIdsWithCustomUserInfo(DEFAULT_NAME, "av", false, 0, 10); final List userIdsPartialMatch = getIdentityAPI().getUserIdsWithCustomUserInfo(DEFAULT_NAME, "av", true, 0, 10); //then assertThat(userIdsExactMatch).containsExactly(user2.getId()); assertThat(userIdsExactMatchNoResults).isEmpty(); assertThat(userIdsPartialMatch).containsExactly(user.getId()); //clean deleteUser(user2); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/identity/GroupIT.java ================================================ /** * 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.identity; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.api.ApiAccessType; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.InvalidGroupNameException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.identity.impl.IconImpl; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.util.APITypeManager; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class GroupIT extends TestWithTechnicalUser { private Group defaultGroup; @Rule public ExpectedException expectedException = ExpectedException.none(); @Override @Before public void before() throws Exception { super.before(); final GroupCreator groupCreator = new GroupCreator("test"); groupCreator.setDescription("description").setDisplayName("label"); defaultGroup = getIdentityAPI().createGroup(groupCreator); } @Override @After public void after() throws Exception { getIdentityAPI().deleteGroup(defaultGroup.getId()); defaultGroup = null; super.after(); } @Test public void getGroup() throws BonitaException { final Group group = getIdentityAPI().getGroup(defaultGroup.getId()); assertNotNull(group); assertEquals("test", group.getName()); assertEquals("label", group.getDisplayName()); assertEquals("description", group.getDescription()); } @Test(expected = GroupNotFoundException.class) public void getGroupByGroupNotFound() throws BonitaException { getIdentityAPI().getGroup(0); } @Test public void getNumberOfGroups() throws BonitaException { assertEquals(1, getIdentityAPI().getNumberOfGroups()); final Group newGroup = getIdentityAPI().createGroup("NewGroup", null); assertEquals(2, getIdentityAPI().getNumberOfGroups()); getIdentityAPI().deleteGroup(newGroup.getId()); } @Test(expected = AlreadyExistsException.class) public void createGroupBygroupWithGroupAlreadyExistException() throws BonitaException { Group group = getIdentityAPI().createGroup("NewGroup", null); try { group = getIdentityAPI().createGroup("NewGroup", null); } finally { getIdentityAPI().deleteGroup(group.getId()); } } @Test public void getGroupByGroupName() throws BonitaException { final String groupName = "group111"; final Group groupM = getIdentityAPI().createGroup(groupName, null); final Group group = getIdentityAPI().getGroupByPath(groupName); assertNotNull(group); assertEquals(groupName, group.getName()); assertEquals(groupM.getId(), group.getId()); getIdentityAPI().deleteGroup(group.getId()); } @Test public void getGroups() throws BonitaException { final Group groupA = createGroup("testA", "labelA", "descrtptionA"); final Group groupB = createGroup("testB", "labelB", "descrtptionB"); final List listGroups = getIdentityAPI().getGroups(0, 5000, GroupCriterion.NAME_ASC); assertNotNull(listGroups); assertEquals(3, listGroups.size()); assertEquals("testA", listGroups.get(1).getName()); assertEquals("labelA", listGroups.get(1).getDisplayName()); assertEquals("descrtptionA", listGroups.get(1).getDescription()); assertEquals("testB", listGroups.get(2).getName()); assertEquals("labelB", listGroups.get(2).getDisplayName()); assertEquals("descrtptionB", listGroups.get(2).getDescription()); getIdentityAPI().deleteGroup(groupA.getId()); getIdentityAPI().deleteGroup(groupB.getId()); } @Test public void getGroupsByIDs() throws BonitaException { final String group1 = "Group1"; final Group groupCreated1 = getIdentityAPI().createGroup(group1, null); final String group2 = "Group2"; final Group groupCreated2 = getIdentityAPI().createGroup(group2, null); final List groupIds = new ArrayList<>(); groupIds.add(groupCreated1.getId()); groupIds.add(groupCreated2.getId()); final Map groups = getIdentityAPI().getGroups(groupIds); assertNotNull(groups); assertEquals(2, groups.size()); assertEquals(group1, groups.get(groupCreated1.getId()).getName()); assertEquals(group2, groups.get(groupCreated2.getId()).getName()); getIdentityAPI().deleteGroup(groups.get(groupCreated1.getId()).getId()); getIdentityAPI().deleteGroup(groups.get(groupCreated2.getId()).getId()); } public void getGroupsByIDsWithoutGroupNotFoundException() throws BonitaException { final String group1 = "Group1"; final Group groupCreated1 = getIdentityAPI().createGroup(group1, null); final String group2 = "Group2"; final Group groupCreated2 = getIdentityAPI().createGroup(group2, null); final List groupIds = new ArrayList<>(); groupIds.add(groupCreated1.getId()); groupIds.add(groupCreated2.getId() + 100); final Map groups = getIdentityAPI().getGroups(groupIds); assertNotNull(groups); assertEquals(1, groups.size()); assertEquals(group1, groups.get(0).getName()); getIdentityAPI().deleteGroup(groupCreated1.getId()); getIdentityAPI().deleteGroup(groupCreated2.getId()); } @Test(expected = AlreadyExistsException.class) public void createGroupExistException() throws BonitaException { getIdentityAPI().createGroup("test", null); } @Test(expected = AlreadyExistsException.class) public void createSubGroupExistException() throws BonitaException { final Group group = getIdentityAPI().createGroup("r&d", "bonita"); try { getIdentityAPI().createGroup("r&d", "bonita"); } finally { deleteGroups(group); } } @Test public void deleteGroup() throws BonitaException { final long numberOfGroups = getIdentityAPI().getNumberOfGroups(); final Group group = getIdentityAPI().createGroup("groupName", null); assertEquals(numberOfGroups + 1, getIdentityAPI().getNumberOfGroups()); getIdentityAPI().deleteGroup(group.getId()); assertEquals(numberOfGroups, getIdentityAPI().getNumberOfGroups()); } @Test public void deleteGroupDeleteChildGroups() throws BonitaException { final long numberOfGroups = getIdentityAPI().getNumberOfGroups(); final Group parentGroup = getIdentityAPI().createGroup("parentGroup", null); final Group notParentGroup = getIdentityAPI().createGroup("notParentGroup", null); final Group subGroup = getIdentityAPI().createGroup("subGroup", parentGroup.getPath()); assertEquals(numberOfGroups + 3, getIdentityAPI().getNumberOfGroups()); getIdentityAPI().deleteGroup(parentGroup.getId()); try { getIdentityAPI().getGroup(subGroup.getId()); fail("child group should not exists anymore"); } catch (final GroupNotFoundException e) { // ok } getIdentityAPI().getGroup(notParentGroup.getId()); assertEquals(numberOfGroups + 1, getIdentityAPI().getNumberOfGroups()); getIdentityAPI().deleteGroup(notParentGroup.getId()); } @Test public void deleteGroupDeleteChildGroupsRecursivly() throws BonitaException { final long numberOfGroups = getIdentityAPI().getNumberOfGroups(); final Group parentGroup = getIdentityAPI().createGroup("parentGroup", null); for (int i = 0; i < 25; i++) { final Group sub = getIdentityAPI().createGroup("subGroup" + i, parentGroup.getPath()); for (int j = 0; j < 25; j++) { getIdentityAPI().createGroup("subSubGroup" + j, sub.getPath()); } } assertEquals(numberOfGroups + 1 + 25 + 25 * 25, getIdentityAPI().getNumberOfGroups()); getIdentityAPI().deleteGroup(parentGroup.getId()); assertEquals(numberOfGroups, getIdentityAPI().getNumberOfGroups()); } @Test public void deleteGroupsChildrenAndParent() throws BonitaException { final long numberOfGroups = getIdentityAPI().getNumberOfGroups(); final Group parentGroup = getIdentityAPI().createGroup("parentGroup", null); final Group sub0 = getIdentityAPI().createGroup("subGroup0", parentGroup.getPath()); final Group sub01 = getIdentityAPI().createGroup("subSubGroup0", sub0.getPath()); final Group sub1 = getIdentityAPI().createGroup("subGroup1", parentGroup.getPath()); final Group sub11 = getIdentityAPI().createGroup("subSubGroup1", sub1.getPath()); assertEquals(numberOfGroups + 5, getIdentityAPI().getNumberOfGroups()); getIdentityAPI().deleteGroups(Arrays.asList(sub01.getId(), parentGroup.getId(), sub11.getId())); assertEquals(numberOfGroups, getIdentityAPI().getNumberOfGroups()); } @Test public void deleteGroupNotFoundException() throws Exception { expectedException.expect(DeletionException.class); //Exception causes are not serialized if API type is HTTP if (ApiAccessType.LOCAL.equals(APITypeManager.getAPIType())) { expectedException.expectCause(is(instanceOf(GroupNotFoundException.class))); } getIdentityAPI().deleteGroup(0); } @Test public void deleteGroups() throws BonitaException { assertNotNull(getIdentityAPI().getNumberOfGroups()); assertEquals(1, getIdentityAPI().getNumberOfGroups()); final List groupIdList = new ArrayList<>(); final Group group1 = getIdentityAPI().createGroup("testName1", null); groupIdList.add(group1.getId()); assertEquals(2, getIdentityAPI().getNumberOfGroups()); final Group group2 = getIdentityAPI().createGroup("testName2", null); groupIdList.add(group2.getId()); assertEquals(3, getIdentityAPI().getNumberOfGroups()); assertEquals(2, groupIdList.size()); getIdentityAPI().deleteGroups(groupIdList); assertEquals(1, getIdentityAPI().getNumberOfGroups()); } @Test(expected = DeletionException.class) public void deleteGroupsWithNotExistId() throws BonitaException { assertNotNull(getIdentityAPI().getNumberOfGroups()); assertEquals(1, getIdentityAPI().getNumberOfGroups()); final List groupIdList = new ArrayList<>(); final Group group1 = getIdentityAPI().createGroup("testName1", null); groupIdList.add(group1.getId()); assertEquals(2, getIdentityAPI().getNumberOfGroups()); groupIdList.add((long) 0); assertEquals(2, groupIdList.size()); getIdentityAPI().deleteGroup(group1.getId()); getIdentityAPI().deleteGroups(groupIdList); } @Test public void updateGroup() throws BonitaException { final Group group1 = getIdentityAPI().getGroup(defaultGroup.getId()); assertEquals("test", group1.getName()); final GroupUpdater updateDescriptor = new GroupUpdater(); updateDescriptor.updateName("newtest"); updateDescriptor.updateDisplayName("newlabel"); updateDescriptor.updateDescription("newdescription"); getIdentityAPI().updateGroup(group1.getId(), updateDescriptor); final Group group2 = getIdentityAPI().getGroup(group1.getId()); assertNotNull(group2); assertEquals("newtest", group2.getName()); assertEquals("newlabel", group2.getDisplayName()); assertEquals("newdescription", group2.getDescription()); } @Test public void updateParentGroupPath() throws BonitaException { final Group newRootGroup = createGroup("BonitaSoft", "BonitaSoft", "BonitaSoft company"); final String groupL2Name = "France"; Group groupL2 = getIdentityAPI().createGroup(groupL2Name, defaultGroup.getPath()); assertEquals(defaultGroup.getPath(), groupL2.getParentPath()); final String groupL3Name = "Grenoble"; Group groupL3 = getIdentityAPI().createGroup(groupL3Name, groupL2.getPath()); assertEquals(groupL2.getPath(), groupL3.getParentPath()); final GroupUpdater updateDescriptor = new GroupUpdater(); updateDescriptor.updateParentPath(newRootGroup.getPath()); // update parent path getIdentityAPI().updateGroup(groupL2.getId(), updateDescriptor); groupL2 = getIdentityAPI().getGroup(groupL2.getId()); assertEquals(newRootGroup.getPath(), groupL2.getParentPath()); // assert children are also updated groupL3 = getIdentityAPI().getGroup(groupL3.getId()); assertEquals(groupL2.getPath(), groupL3.getParentPath()); getIdentityAPI().deleteGroup(groupL3.getId()); getIdentityAPI().deleteGroup(groupL2.getId()); getIdentityAPI().deleteGroup(newRootGroup.getId()); } @Test public void when_update_group_with_empty_parent_path_it_is_set_to_null() throws BonitaException { final String parentGroupPath = "/parentPath"; final Group group = createGroup("BonitaSoft", parentGroupPath); Group result = getIdentityAPI().getGroup(group.getId()); assertEquals("The parent path must be equals to " + parentGroupPath + ".", parentGroupPath, result.getParentPath()); // update parent path final GroupUpdater updateDescriptor = new GroupUpdater(); updateDescriptor.updateParentPath(""); getIdentityAPI().updateGroup(group.getId(), updateDescriptor); result = getIdentityAPI().getGroup(group.getId()); assertNull("The parent path must be null.", result.getParentPath()); getIdentityAPI().deleteGroup(group.getId()); } @Test public void when_create_group_with_empty_parent_path_it_is_set_to_null() throws BonitaException { final Group group = createGroup("BonitaSoft", ""); final Group result = getIdentityAPI().getGroup(group.getId()); assertNull("The parent path must be null.", result.getParentPath()); getIdentityAPI().deleteGroup(group.getId()); } @Test public void updateGroupNameAlsoUpdateChildren() throws BonitaException { final Group newRootGroup = createGroup("BonitaSoft", "BonitaSoft", "BonitaSoft company"); final String groupL2Name = "France"; Group groupL2 = getIdentityAPI().createGroup(groupL2Name, defaultGroup.getPath()); assertEquals(defaultGroup.getPath(), groupL2.getParentPath()); final String groupL3Name = "Grenoble"; Group groupL3 = getIdentityAPI().createGroup(groupL3Name, groupL2.getPath()); assertEquals(groupL2.getPath(), groupL3.getParentPath()); final GroupUpdater updateDescriptor = new GroupUpdater(); updateDescriptor.updateName("Germany"); // update parent path getIdentityAPI().updateGroup(groupL2.getId(), updateDescriptor); groupL2 = getIdentityAPI().getGroup(groupL2.getId()); assertEquals("Germany", groupL2.getName()); // assert children are also updated groupL3 = getIdentityAPI().getGroup(groupL3.getId()); assertEquals("/" + defaultGroup.getName() + "/Germany", groupL3.getParentPath()); getIdentityAPI().deleteGroup(groupL3.getId()); getIdentityAPI().deleteGroup(groupL2.getId()); getIdentityAPI().deleteGroup(newRootGroup.getId()); } @Test(expected = AlreadyExistsException.class) public void should_throw_AlreadyExistException_when_update_group_with_group_name_already_exist() throws BonitaException { Group groupToUpdate = getIdentityAPI().createGroup("England", defaultGroup.getPath()); final GroupUpdater groupUpdater = new GroupUpdater(); groupUpdater.updateName("test"); getIdentityAPI().updateGroup(groupToUpdate.getId(), groupUpdater); getIdentityAPI().deleteGroup(groupToUpdate.getId()); } @Test public void should_updated_group_when_name_already_exist_but_path_dont_exist() throws BonitaException { final Group newRootGroup = createGroup("Country", "Country", "Country company"); final String groupFranceName = "France"; Group groupFrance = getIdentityAPI().createGroup(groupFranceName, newRootGroup.getPath()); final String groupEnglandName = "England"; Group groupEngland = getIdentityAPI().createGroup(groupEnglandName, newRootGroup.getPath()); final GroupUpdater groupUpdater = new GroupUpdater(); groupUpdater.updateName("France"); groupUpdater.updateParentPath(null); getIdentityAPI().updateGroup(groupEngland.getId(), groupUpdater); // Asset Group group = getIdentityAPI().getGroup(groupEngland.getId()); assertEquals("France", group.getName()); assertEquals("/France", group.getPath()); // Clean getIdentityAPI().deleteGroup(groupFrance.getId()); getIdentityAPI().deleteGroup(groupEngland.getId()); getIdentityAPI().deleteGroup(newRootGroup.getId()); } @Test public void updateGroupNameAndParenthAlsoUpdateAllChildrenInfos() throws BonitaException { // arrange Group parentGroup = getIdentityAPI().createGroup("France", defaultGroup.getPath()); Group childGroup = getIdentityAPI().createGroup("Grenoble", parentGroup.getPath()); // act final GroupUpdater group2Updater = new GroupUpdater(); group2Updater.updateParentPath("/WorldCompany"); group2Updater.updateName("Germany"); getIdentityAPI().updateGroup(parentGroup.getId(), group2Updater); // assert parentGroup = getIdentityAPI().getGroup(parentGroup.getId()); childGroup = getIdentityAPI().getGroup(childGroup.getId()); assertEquals("/WorldCompany/Germany", childGroup.getParentPath()); assertEquals("/WorldCompany/Germany/Grenoble", childGroup.getPath()); // clean-up: getIdentityAPI().deleteGroup(childGroup.getId()); getIdentityAPI().deleteGroup(parentGroup.getId()); } @Test(expected = GroupNotFoundException.class) public void updateGroupsNotFoundException() throws BonitaException { final GroupUpdater updateDescriptor = new GroupUpdater(); updateDescriptor.updateName("newtest"); updateDescriptor.updateDisplayName("newlabel"); updateDescriptor.updateDescription("newdescription"); getIdentityAPI().updateGroup(0, updateDescriptor); } @Test public void getUsersInGroup() throws BonitaException { final User aUserInRoleA = getIdentityAPI().createUser("testnameA", "bpm"); final User bUserInRoleA = getIdentityAPI().createUser("testnameB", "bpm"); final User cUserInRoleB = getIdentityAPI().createUser("testnameC", "bpm"); final User dUser = getIdentityAPI().createUser("testnameD", "bpm"); final Group group = createGroup("group", "testLabel", "description"); final List userIds = new ArrayList<>(); userIds.add(aUserInRoleA.getId()); userIds.add(bUserInRoleA.getId()); final RoleCreator roleCreatorA = new RoleCreator("RoleA"); roleCreatorA.setDisplayName("LabelA").setDescription("DescriptionA"); final Role testRoleA = getIdentityAPI().createRole(roleCreatorA); getIdentityAPI().addUserMemberships(userIds, defaultGroup.getId(), testRoleA.getId()); final List testIds = new ArrayList<>(); testIds.add(cUserInRoleB.getId()); final RoleCreator roleCreatorB = new RoleCreator("RoleB"); roleCreatorB.setDisplayName("LabelB").setDescription("DescriptionB"); final Role testRoleB = getIdentityAPI().createRole(roleCreatorB); getIdentityAPI().addUserMemberships(testIds, group.getId(), testRoleB.getId()); final List users = getIdentityAPI().getUsersInGroup(defaultGroup.getId(), 0, 5000, UserCriterion.USER_NAME_ASC); assertNotNull(users); assertEquals(2, users.size()); assertEquals("testnameA", users.get(0).getUserName()); assertEquals("testnameB", users.get(1).getUserName()); getIdentityAPI().deleteUserMemberships(userIds, defaultGroup.getId(), testRoleA.getId()); getIdentityAPI().deleteUserMemberships(testIds, group.getId(), testRoleB.getId()); getIdentityAPI().deleteUser(aUserInRoleA.getId()); getIdentityAPI().deleteUser(bUserInRoleA.getId()); getIdentityAPI().deleteUser(cUserInRoleB.getId()); getIdentityAPI().deleteUser(dUser.getId()); getIdentityAPI().deleteRole(testRoleA.getId()); getIdentityAPI().deleteRole(testRoleB.getId()); getIdentityAPI().deleteGroup(group.getId()); } @Test public void getActiveUsersInGroup() throws BonitaException { final User aUserInRoleA = getIdentityAPI().createUser(new UserCreator("testnameA", "bpm").setEnabled(true)); final User bUserInRoleA = getIdentityAPI().createUser(new UserCreator("testnameB", "bpm").setEnabled(false)); final User cUserInRoleB = getIdentityAPI().createUser(new UserCreator("testnameC", "bpm").setEnabled(true)); final User dUser = getIdentityAPI().createUser(new UserCreator("testnameD", "bpm").setEnabled(true)); final Group group = createGroup("group", "testLabel", "description"); final List userIds = new ArrayList<>(); userIds.add(aUserInRoleA.getId()); userIds.add(bUserInRoleA.getId()); final RoleCreator roleCreatorA = new RoleCreator("RoleA"); roleCreatorA.setDisplayName("LabelA").setDescription("DescriptionA"); final Role testRoleA = getIdentityAPI().createRole(roleCreatorA); getIdentityAPI().addUserMemberships(userIds, defaultGroup.getId(), testRoleA.getId()); final List testIds = new ArrayList<>(); testIds.add(cUserInRoleB.getId()); final RoleCreator roleCreatorB = new RoleCreator("RoleB"); roleCreatorB.setDisplayName("LabelB").setDescription("DescriptionB"); final Role testRoleB = getIdentityAPI().createRole(roleCreatorB); getIdentityAPI().addUserMemberships(testIds, group.getId(), testRoleB.getId()); final List users = getIdentityAPI().getActiveUsersInGroup(defaultGroup.getId(), 0, 5000, UserCriterion.USER_NAME_ASC); assertNotNull(users); assertEquals(1, users.size()); assertEquals("testnameA", users.get(0).getUserName()); getIdentityAPI().deleteUserMemberships(userIds, defaultGroup.getId(), testRoleA.getId()); getIdentityAPI().deleteUserMemberships(testIds, group.getId(), testRoleB.getId()); getIdentityAPI().deleteUser(aUserInRoleA.getId()); getIdentityAPI().deleteUser(bUserInRoleA.getId()); getIdentityAPI().deleteUser(cUserInRoleB.getId()); getIdentityAPI().deleteUser(dUser.getId()); getIdentityAPI().deleteRole(testRoleA.getId()); getIdentityAPI().deleteRole(testRoleB.getId()); getIdentityAPI().deleteGroup(group.getId()); } @Test public void getInactiveUsersInGroup() throws BonitaException { final User aUserInRoleA = getIdentityAPI().createUser(new UserCreator("testnameA", "bpm").setEnabled(true)); final User bUserInRoleA = getIdentityAPI().createUser(new UserCreator("testnameB", "bpm").setEnabled(false)); final User cUserInRoleA = getIdentityAPI().createUser(new UserCreator("testnameE", "bpm").setEnabled(false)); final User cUserInRoleB = getIdentityAPI().createUser(new UserCreator("testnameC", "bpm").setEnabled(true)); final User dUser = getIdentityAPI().createUser(new UserCreator("testnameD", "bpm").setEnabled(true)); final Group group = createGroup("group", "testLabel", "description"); final List userIds = new ArrayList<>(); userIds.add(aUserInRoleA.getId()); userIds.add(bUserInRoleA.getId()); userIds.add(cUserInRoleA.getId()); final RoleCreator roleCreatorA = new RoleCreator("RoleA"); roleCreatorA.setDisplayName("LabelA").setDescription("DescriptionA"); final Role testRoleA = getIdentityAPI().createRole(roleCreatorA); getIdentityAPI().addUserMemberships(userIds, defaultGroup.getId(), testRoleA.getId()); final List testIds = new ArrayList<>(); testIds.add(cUserInRoleB.getId()); final RoleCreator roleCreatorB = new RoleCreator("RoleB"); roleCreatorB.setDisplayName("LabelB").setDescription("DescriptionB"); final Role testRoleB = getIdentityAPI().createRole(roleCreatorB); getIdentityAPI().addUserMemberships(testIds, group.getId(), testRoleB.getId()); final List users = getIdentityAPI().getInactiveUsersInGroup(defaultGroup.getId(), 0, 5000, UserCriterion.USER_NAME_ASC); assertNotNull(users); assertEquals(2, users.size()); assertEquals("testnameB", users.get(0).getUserName()); assertEquals("testnameE", users.get(1).getUserName()); getIdentityAPI().deleteUserMemberships(userIds, defaultGroup.getId(), testRoleA.getId()); getIdentityAPI().deleteUserMemberships(testIds, group.getId(), testRoleB.getId()); getIdentityAPI().deleteUser(aUserInRoleA.getId()); getIdentityAPI().deleteUser(bUserInRoleA.getId()); getIdentityAPI().deleteUser(cUserInRoleB.getId()); getIdentityAPI().deleteUser(dUser.getId()); getIdentityAPI().deleteRole(testRoleA.getId()); getIdentityAPI().deleteRole(testRoleB.getId()); getIdentityAPI().deleteGroup(group.getId()); } @Test public void getNumberOfUsersInGroup() throws BonitaException { final User aUser = getIdentityAPI().createUser("testnameA", "bpm"); final User bUser = getIdentityAPI().createUser("testnameB", "bpm"); final List userIds = new ArrayList<>(); userIds.add(aUser.getId()); userIds.add(bUser.getId()); final Role testRole = getIdentityAPI().createRole("testRole"); getIdentityAPI().addUserMemberships(userIds, defaultGroup.getId(), testRole.getId()); final List users = getIdentityAPI().getUsersInGroup(defaultGroup.getId(), 0, 5000, UserCriterion.USER_NAME_ASC); final long count = getIdentityAPI().getNumberOfUsersInGroup(defaultGroup.getId()); assertNotNull(users); assertNotNull(count); assertEquals(count, users.size()); assertEquals("testnameA", users.get(0).getUserName()); assertEquals("testnameB", users.get(1).getUserName()); getIdentityAPI().deleteUserMemberships(userIds, defaultGroup.getId(), testRole.getId()); getIdentityAPI().deleteUser(aUser.getId()); getIdentityAPI().deleteUser(bUser.getId()); getIdentityAPI().deleteRole(testRole.getId()); } @Test public void getPaginatedGroupsWithGroupCriterion() throws BonitaException { final Group groupA = createGroup("testA", "labelA", "descrtptionA"); final Group groupB = createGroup("testB", "labelB", "descrtptionB"); final Group groupC = createGroup("testc", "labelC", "descrtptionC"); final Group groupD = createGroup("testd", "labelD", "descrtptionD"); final List groupNameASCPage1 = getIdentityAPI().getGroups(0, 3, GroupCriterion.NAME_ASC); assertEquals(3, groupNameASCPage1.size()); assertEquals("testA", groupNameASCPage1.get(1).getName()); assertEquals("testB", groupNameASCPage1.get(2).getName()); final List groupNameASCPage2 = getIdentityAPI().getGroups(3, 3, GroupCriterion.NAME_ASC); assertEquals(2, groupNameASCPage2.size()); assertEquals("testc", groupNameASCPage2.get(0).getName()); assertEquals("testd", groupNameASCPage2.get(1).getName()); final List groupNameDESC = getIdentityAPI().getGroups(0, 3, GroupCriterion.NAME_DESC); assertEquals(3, groupNameDESC.size()); assertEquals("testd", groupNameDESC.get(0).getName()); assertEquals("testc", groupNameDESC.get(1).getName()); final List groupLabelASC = getIdentityAPI().getGroups(0, 3, GroupCriterion.LABEL_ASC); assertEquals(3, groupLabelASC.size()); assertEquals("labelA", groupLabelASC.get(1).getDisplayName()); assertEquals("labelB", groupLabelASC.get(2).getDisplayName()); final List groupLabelDESC = getIdentityAPI().getGroups(0, 3, GroupCriterion.LABEL_DESC); assertEquals(3, groupLabelDESC.size()); assertEquals("labelD", groupLabelDESC.get(0).getDisplayName()); assertEquals("labelC", groupLabelDESC.get(1).getDisplayName()); getIdentityAPI().deleteGroup(groupA.getId()); getIdentityAPI().deleteGroup(groupB.getId()); getIdentityAPI().deleteGroup(groupC.getId()); getIdentityAPI().deleteGroup(groupD.getId()); } @Test public void searchGroupUsingFilter() throws BonitaException { final Group groupA = createGroup("testA", "labelA", "desc"); final Group groupB = createGroup("testB", "labelB", "Bbb"); final Group groupC = createGroup("c", "labelC", "descrtptionC"); final Group groupD = createGroup("d", "labelD", "descrtptionD"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(GroupSearchDescriptor.ID, groupC.getId()); final SearchResult searchGroups = getIdentityAPI().searchGroups(builder.done()); assertNotNull(searchGroups); assertEquals(1, searchGroups.getCount()); final List groups = searchGroups.getResult(); assertEquals(groupC, groups.get(0)); getIdentityAPI().deleteGroup(groupA.getId()); getIdentityAPI().deleteGroup(groupB.getId()); getIdentityAPI().deleteGroup(groupC.getId()); getIdentityAPI().deleteGroup(groupD.getId()); } @Test public void searchGroupWithApostrophe() throws BonitaException { final Group groupA = createGroup("test'A", "labelA", "desc"); final Group groupB = createGroup("testB", "test'B", "Bbb"); final Group groupC = createGroup("testc", "labelC", "test'C"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(GroupSearchDescriptor.NAME, Order.ASC); builder.searchTerm("test'"); final SearchResult searchGroups = getIdentityAPI().searchGroups(builder.done()); assertNotNull(searchGroups); assertEquals(3, searchGroups.getCount()); final List groups = searchGroups.getResult(); assertEquals(groupA, groups.get(0)); assertEquals(groupB, groups.get(1)); assertEquals(groupC, groups.get(2)); getIdentityAPI().deleteGroup(groupA.getId()); getIdentityAPI().deleteGroup(groupB.getId()); getIdentityAPI().deleteGroup(groupC.getId()); } @Test public void checkCreatedByForGroup() throws BonitaException { final Group group = createGroup("group1", "myGroup", "descrtption"); assertNotNull(group); assertNotNull(group.getCreatedBy()); assertEquals(getSession().getUserId(), group.getCreatedBy()); getIdentityAPI().deleteGroup(group.getId()); } @Test public void should_be_able_to_create_big_groups_hierarchy() throws BonitaException { // this should work // acme -> Site -> Service -> Departement -> Back Office & Logistique createGroup("acme"); createGroup("Site", "/acme"); createGroup("Service", "/acme/Site"); createGroup("Departement", "/acme/Site/Service"); createGroup("Back Office & Logistique", "/acme/Site/Service/Departement"); createGroup("Administration Titres", "/acme/Site/Service/Departement/Back Office & Logistique"); assertEquals("Administration Titres", getIdentityAPI() .getGroupByPath("/acme/Site/Service/Departement/Back Office & Logistique/Administration Titres") .getName()); deleteGroups(getIdentityAPI().getGroupByPath("/acme")); } @Test public void should_createGroup_with_icon_create_the_icon() throws Exception { //given Group mainGroup = getIdentityAPI() .createGroup(new GroupCreator("mainGroup").setIcon("main.png", new byte[] { 1, 2, 3 })); //when Icon icon = getIdentityAPI().getIcon(mainGroup.getIconId()); //then assertThat(icon).isEqualTo(new IconImpl(icon.getId(), "image/png", new byte[] { 1, 2, 3 })); //clean up deleteGroups(getIdentityAPI().getGroupByPath("/mainGroup")); } @Test public void should_updateGroup_with_new_icon_create_a_new_icon() throws Exception { //given Group mainGroup = getIdentityAPI() .createGroup(new GroupCreator("mainGroup").setIcon("main.png", new byte[] { 1, 2, 3 })); //when Group group = getIdentityAPI().updateGroup(mainGroup.getId(), new GroupUpdater().updateIcon("newIcon.jpg", new byte[] { 3, 4, 5 })); //then Icon icon = getIdentityAPI().getIcon(group.getIconId()); assertThat(icon.getId()).isNotEqualTo(mainGroup.getIconId()); assertThat(icon.getMimeType()).isEqualTo("image/jpeg"); assertThat(icon.getContent()).isEqualTo(new byte[] { 3, 4, 5 }); //clean up deleteGroups(getIdentityAPI().getGroupByPath("/mainGroup")); } @Test public void should_deleteGroup_with_icon_delete_the_icon() throws Exception { //given Group mainGroup = getIdentityAPI() .createGroup(new GroupCreator("mainGroup").setIcon("main.png", new byte[] { 1, 2, 3 })); //when getIdentityAPI().deleteGroup(mainGroup.getId()); //then expectedException.expect(NotFoundException.class); getIdentityAPI().getIcon(mainGroup.getIconId()); } @Test public void should_update_childrens_group_ParentPath_with_correct_value() throws BonitaException { //given Group acme = getIdentityAPI().createGroup("Acme", null); Group site = getIdentityAPI().createGroup("Site", acme.getPath()); //when final GroupUpdater group2Updater = new GroupUpdater(); group2Updater.updateDescription("laalalala"); group2Updater.updateName("Acme2"); getIdentityAPI().updateGroup(acme.getId(), group2Updater); //then assertThat(getIdentityAPI().getGroup(acme.getId()).getDescription()).contains("laalalala"); assertThat(getIdentityAPI().getGroup(site.getId()).getParentPath()).isEqualTo("/Acme2"); deleteGroups(getIdentityAPI().getGroup(acme.getId())); } @Test(expected = InvalidGroupNameException.class) public void should_not_create_group_when_given_invalid_name() throws CreationException { //when Group acme = getIdentityAPI().createGroup("/Acme", null); } @Test(expected = InvalidGroupNameException.class) public void should_not_create_group_when_given_invalid_name_in_creator() throws CreationException { //when Group acme = getIdentityAPI().createGroup(new GroupCreator("Bon/ta")); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/identity/MembershipIT.java ================================================ /** * 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.identity; import static org.junit.Assert.*; import java.util.Date; import java.util.List; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.CreationException; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Bole Zhang * @author Baptiste Mesta */ public class MembershipIT extends TestWithTechnicalUser { private static User user1; private static User user2; private static User user3; private static User user4; private static Role role1; private static Role role2; private static Role role3; private static Role role4; private static Group group1; private static Group group2; private static Group group3; private static Group group4; @Override @Before public void before() throws Exception { super.before(); createTestedUserMemberships(); } @Override @After public void after() throws Exception { deleteTestedUserMemberships(); super.after(); } @Test public void getUserMembershipsWithPageOutOfRangeException() throws BonitaException { final User u = getIdentityAPI().createUser("u", "engine"); final List userMemberships = getIdentityAPI().getUserMemberships(u.getId(), 0, 3, UserMembershipCriterion.ROLE_NAME_ASC); assertTrue(userMemberships.isEmpty()); getIdentityAPI().deleteUser(u.getId()); } @Test(expected = CreationException.class) public void createUserMembershipWithoutUser() throws BonitaException { final RoleCreator roleCreator = new RoleCreator("roleM"); final Role roleM = getIdentityAPI().createRole(roleCreator); final Group groupM = getIdentityAPI().createGroup("groupM", null); try { getIdentityAPI().addUserMembership(-2, groupM.getId(), roleM.getId()); } finally { getIdentityAPI().deleteRole(roleM.getId()); getIdentityAPI().deleteGroup(groupM.getId()); } } @Test(expected = CreationException.class) public void createUserMembershipWithoutRole() throws BonitaException { final User userM = getIdentityAPI().createUser("userM", "engine"); final Group groupM = getIdentityAPI().createGroup("groupM", null); try { getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), -4); } finally { getIdentityAPI().deleteUser(userM.getId()); getIdentityAPI().deleteGroup(groupM.getId()); } } @Test(expected = CreationException.class) public void createUserMembershipWithoutGroup() throws BonitaException { final User userM = getIdentityAPI().createUser("userM", "engine"); final RoleCreator roleCreator = new RoleCreator("roleM"); final Role roleM = getIdentityAPI().createRole(roleCreator); try { getIdentityAPI().addUserMembership(userM.getId(), -83, roleM.getId()); } finally { getIdentityAPI().deleteUser(userM.getId()); getIdentityAPI().deleteRole(roleM.getId()); } } @Test public void addUserMembership() throws BonitaException { final User userM = getIdentityAPI().createUser("userM", "engine"); final Role roleM = getIdentityAPI().createRole("roleM"); final Group groupM = getIdentityAPI().createGroup("groupM", null); final UserMembership userMembership = getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), roleM.getId()); try { assertNotNull(userMembership); assertEquals(getSession().getUserId(), userMembership.getAssignedBy()); assertEquals(userM.getId(), userMembership.getUserId()); assertEquals(roleM.getId(), userMembership.getRoleId()); assertEquals(groupM.getId(), userMembership.getGroupId()); // check assignedBy for membership assertEquals(getSession().getUserId(), userMembership.getAssignedBy()); } finally { getIdentityAPI().deleteUserMembership(userM.getId(), groupM.getId(), roleM.getId()); getIdentityAPI().deleteUser(userM.getId()); getIdentityAPI().deleteRole(roleM.getId()); getIdentityAPI().deleteGroup(groupM.getId()); } } @Test(expected = AlreadyExistsException.class) public void addTwiceSameUserMembership() throws BonitaException { final User userM = getIdentityAPI().createUser("userM", "engine"); final Role roleM = getIdentityAPI().createRole("roleM"); final Group groupM = getIdentityAPI().createGroup("groupM", null); // Add first time the userMembership getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), roleM.getId()); try { // Add twice the same userMembership getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), roleM.getId()); } finally { getIdentityAPI().deleteUserMembership(userM.getId(), groupM.getId(), roleM.getId()); getIdentityAPI().deleteUser(userM.getId()); getIdentityAPI().deleteRole(roleM.getId()); getIdentityAPI().deleteGroup(groupM.getId()); } } @Test public void getRoleIdAndGroupIdFromMembership() throws BonitaException { final User userM = getIdentityAPI().createUser("userM", "engine"); final Role roleM = getIdentityAPI().createRole("roleM"); final Group groupM = getIdentityAPI().createGroup("groupM", null); final UserMembership userMembership = getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), roleM.getId()); final Group group = getIdentityAPI().getGroup(userMembership.getGroupId()); final Role role = getIdentityAPI().getRole(userMembership.getRoleId()); try { assertEquals("groupM", group.getName()); assertEquals("roleM", role.getName()); } finally { getIdentityAPI().deleteUserMembership(userM.getId(), groupM.getId(), roleM.getId()); getIdentityAPI().deleteUser(userM.getId()); getIdentityAPI().deleteRole(roleM.getId()); getIdentityAPI().deleteGroup(groupM.getId()); } } @Test public void getMembershipByMembershipId() throws BonitaException { final User userM = getIdentityAPI().createUser("userM", "engine"); final Role roleM = getIdentityAPI().createRole("roleM"); final Group groupM = getIdentityAPI().createGroup("groupM", null); final UserMembership userMembership = getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), roleM.getId()); try { final UserMembership membership2 = getIdentityAPI().getUserMembership(userMembership.getId()); assertNotNull(membership2); assertEquals(userM.getId(), membership2.getUserId()); assertEquals(roleM.getId(), membership2.getRoleId()); assertEquals(groupM.getId(), membership2.getGroupId()); assertEquals(userM.getUserName(), membership2.getUsername()); assertEquals(roleM.getName(), membership2.getRoleName()); assertEquals(groupM.getName(), membership2.getGroupName()); } finally { getIdentityAPI().deleteUserMembership(userM.getId(), groupM.getId(), roleM.getId()); getIdentityAPI().deleteUser(userM.getId()); getIdentityAPI().deleteRole(roleM.getId()); getIdentityAPI().deleteGroup(groupM.getId()); } } @Test public void getAssignedByOnUserMembership() throws BonitaException, InterruptedException { final User userM = createUser("aTest", "engine"); final User plop = createUser("plop", "bpm"); final Role roleM = getIdentityAPI().createRole("roleM"); final Group groupM = getIdentityAPI().createGroup("groupM", null); logout(); loginOnDefaultTenantWith(plop.getUserName(), "bpm"); final Date beforeDate = new Date(); Thread.sleep(10); final UserMembership userMembership = getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), roleM.getId()); Thread.sleep(10); final Date afterDate = new Date(); try { final UserMembership membership2 = getIdentityAPI().getUserMembership(userMembership.getId()); assertNotNull(membership2); assertEquals(plop.getId(), membership2.getAssignedBy()); assertTrue( "exprected date between <" + beforeDate + "> and <" + afterDate + "> but was <" + membership2.getAssignedDate() + ">", membership2 .getAssignedDate().compareTo(beforeDate) > 0 && membership2.getAssignedDate().compareTo(afterDate) < 0); assertEquals(userM.getId(), membership2.getUserId()); assertEquals(roleM.getId(), membership2.getRoleId()); assertEquals(groupM.getId(), membership2.getGroupId()); } finally { getIdentityAPI().deleteUserMembership(userM.getId(), groupM.getId(), roleM.getId()); getIdentityAPI().deleteUser(userM.getId()); getIdentityAPI().deleteUser(plop.getId()); getIdentityAPI().deleteRole(roleM.getId()); getIdentityAPI().deleteGroup(groupM.getId()); } } @Test public void getNumberOfUserMembershipsByUserId() { assertEquals(4, getIdentityAPI().getNumberOfUserMemberships(user1.getId())); assertEquals(1, getIdentityAPI().getNumberOfUserMemberships(user4.getId())); } @Test public void updateUserMemberships() throws BonitaException { final User userM = getIdentityAPI().createUser("userM", "engine"); final Role roleM = getIdentityAPI().createRole("roleM"); final Group groupM = getIdentityAPI().createGroup("groupM", null); final UserMembership userMembership = getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), roleM.getId()); assertEquals(groupM.getId(), userMembership.getGroupId()); assertEquals(roleM.getId(), userMembership.getRoleId()); final UserMembership newMembership = getIdentityAPI().updateUserMembership(userMembership.getId(), group4.getId(), role3.getId()); assertEquals(group4.getId(), newMembership.getGroupId()); assertEquals(role3.getId(), newMembership.getRoleId()); getIdentityAPI().deleteUserMembership(newMembership.getId()); getIdentityAPI().deleteRole(roleM.getId()); getIdentityAPI().deleteGroup(groupM.getId()); getIdentityAPI().deleteUser(userM.getId()); } @Test public void getUserMembershipsByRole() { List userMemberships = getIdentityAPI().getUserMembershipsByRole(role1.getId(), 0, 500); assertEquals(2, userMemberships.size()); userMemberships = getIdentityAPI().getUserMembershipsByRole(role1.getId(), 0, 1); assertEquals(1, userMemberships.size()); userMemberships = getIdentityAPI().getUserMembershipsByRole(role1.getId(), 1, 1); assertEquals(1, userMemberships.size()); userMemberships = getIdentityAPI().getUserMembershipsByRole(role1.getId(), 20, 500); assertEquals(0, userMemberships.size()); } @Test public void getUserMembershipsByGroup() { List userMemberships = getIdentityAPI().getUserMembershipsByGroup(group1.getId(), 0, 500); assertEquals(1, userMemberships.size()); userMemberships = getIdentityAPI().getUserMembershipsByGroup(group3.getId(), 0, 500); assertEquals(3, userMemberships.size()); userMemberships = getIdentityAPI().getUserMembershipsByGroup(group3.getId(), 0, 2); assertEquals(2, userMemberships.size()); userMemberships = getIdentityAPI().getUserMembershipsByGroup(group3.getId(), 1, 2); assertEquals(2, userMemberships.size()); userMemberships = getIdentityAPI().getUserMembershipsByGroup(group3.getId(), 20, 2); assertEquals(0, userMemberships.size()); } @Test public void getUserMembershipsOrderByRoleNameAsc() { final List userMemberships = getIdentityAPI().getUserMemberships(user1.getId(), 0, 3, UserMembershipCriterion.ROLE_NAME_ASC); assertEquals(3, userMemberships.size()); assertEquals(role1.getName(), userMemberships.get(0).getRoleName()); assertEquals(role2.getName(), userMemberships.get(1).getRoleName()); assertEquals(role3.getName(), userMemberships.get(2).getRoleName()); } @Test public void getUserMembershipsOrderByRoleNameDesc() { final List userMemberships = getIdentityAPI().getUserMemberships(user1.getId(), 0, 3, UserMembershipCriterion.ROLE_NAME_DESC); assertEquals(3, userMemberships.size()); assertEquals(role4.getName(), userMemberships.get(0).getRoleName()); assertEquals(role3.getName(), userMemberships.get(1).getRoleName()); assertEquals(role2.getName(), userMemberships.get(2).getRoleName()); } @Test public void getUserMembershipsByGroupNameAsc() { final List userMemberships = getIdentityAPI().getUserMemberships(user1.getId(), 0, 3, UserMembershipCriterion.GROUP_NAME_ASC); assertEquals(3, userMemberships.size()); assertEquals(group1.getName(), userMemberships.get(0).getGroupName()); assertEquals(group2.getName(), userMemberships.get(1).getGroupName()); assertEquals(group3.getName(), userMemberships.get(2).getGroupName()); } @Test public void getUserMembershipsByGroupNameDesc() { final List userMemberships = getIdentityAPI().getUserMemberships(user1.getId(), 0, 3, UserMembershipCriterion.GROUP_NAME_DESC); assertEquals(3, userMemberships.size()); assertEquals(group4.getName(), userMemberships.get(0).getGroupName()); assertEquals(group3.getName(), userMemberships.get(1).getGroupName()); assertEquals(group2.getName(), userMemberships.get(2).getGroupName()); } @Test public void getAssignedBy() { final List userMemberships = getIdentityAPI().getUserMemberships(user1.getId(), 0, 3, UserMembershipCriterion.ASSIGNED_DATE_DESC); assertEquals(user1.getId(), userMemberships.get(0).getAssignedBy()); } @Test public void getUserMembershipsByAssignedDateAsc() { final List userMemberships = getIdentityAPI().getUserMemberships(user1.getId(), 0, 3, UserMembershipCriterion.ASSIGNED_DATE_ASC); assertEquals(3, userMemberships.size()); assertEquals(role1.getName(), userMemberships.get(0).getRoleName()); assertEquals(role2.getName(), userMemberships.get(1).getRoleName()); assertEquals(role3.getName(), userMemberships.get(2).getRoleName()); } @Test public void getUserMembershipsByAssignedDateDesc() { final List userMemberships = getIdentityAPI().getUserMemberships(user1.getId(), 0, 3, UserMembershipCriterion.ASSIGNED_DATE_DESC); assertEquals(3, userMemberships.size()); assertEquals(role4.getName(), userMemberships.get(0).getRoleName()); assertEquals(role3.getName(), userMemberships.get(1).getRoleName()); assertEquals(role2.getName(), userMemberships.get(2).getRoleName()); } @Test public void deleteUserMemberships() throws BonitaException { final User userM = getIdentityAPI().createUser("userM", "engine"); final Role roleM = getIdentityAPI().createRole("roleM"); final Group groupM = getIdentityAPI().createGroup("groupM", null); getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), roleM.getId()); assertEquals(1, getIdentityAPI().getNumberOfUserMemberships(userM.getId())); getIdentityAPI().deleteUserMembership(userM.getId(), groupM.getId(), roleM.getId()); assertEquals(0, getIdentityAPI().getNumberOfUserMemberships(userM.getId())); getIdentityAPI().deleteUser(userM.getId()); getIdentityAPI().deleteRole(roleM.getId()); getIdentityAPI().deleteGroup(groupM.getId()); } @Test public void deleteUserMembership() throws BonitaException { final User userT = getIdentityAPI().createUser("userT", "engine"); final Role roleT = getIdentityAPI().createRole("roleT"); final Group groupT = getIdentityAPI().createGroup("groupT", null); final UserMembership membership1 = getIdentityAPI().addUserMembership(userT.getId(), groupT.getId(), roleT.getId()); assertEquals(1, getIdentityAPI().getNumberOfUserMemberships(userT.getId())); final UserMembership membership2 = getIdentityAPI().getUserMembership(membership1.getId()); getIdentityAPI().deleteUserMembership(membership2.getId()); assertEquals(0, getIdentityAPI().getNumberOfUserMemberships(userT.getId())); getIdentityAPI().deleteUser(userT.getId()); getIdentityAPI().deleteRole(roleT.getId()); getIdentityAPI().deleteGroup(groupT.getId()); } @Test public void deleteUserMembershipsByDeleteRole() throws BonitaException { final User userM = getIdentityAPI().createUser("userR", "engine"); final Role roleM = getIdentityAPI().createRole("roleR"); final Group groupM1 = getIdentityAPI().createGroup("groupR1", null); final Group groupM2 = getIdentityAPI().createGroup("groupR2", null); getIdentityAPI().addUserMembership(userM.getId(), groupM1.getId(), roleM.getId()); getIdentityAPI().addUserMembership(userM.getId(), groupM2.getId(), roleM.getId()); assertEquals(2, getIdentityAPI().getNumberOfUserMemberships(userM.getId())); getIdentityAPI().deleteRole(roleM.getId()); assertEquals(0, getIdentityAPI().getNumberOfUserMemberships(userM.getId())); getIdentityAPI().deleteUser(userM.getId()); getIdentityAPI().deleteGroup(groupM1.getId()); getIdentityAPI().deleteGroup(groupM2.getId()); } @Test public void deleteUserMembershipsByDeleteGroup() throws BonitaException { final User userM = getIdentityAPI().createUser("userR", "engine"); final Role roleM1 = getIdentityAPI().createRole("roleR1"); final Role roleM2 = getIdentityAPI().createRole("roleR2"); final Group groupM = getIdentityAPI().createGroup("groupR", null); getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), roleM1.getId()); getIdentityAPI().addUserMembership(userM.getId(), groupM.getId(), roleM2.getId()); assertEquals(2, getIdentityAPI().getNumberOfUserMemberships(userM.getId())); getIdentityAPI().deleteGroup(groupM.getId()); assertEquals(0, getIdentityAPI().getNumberOfUserMemberships(userM.getId())); getIdentityAPI().deleteUser(userM.getId()); getIdentityAPI().deleteRole(roleM1.getId()); getIdentityAPI().deleteRole(roleM2.getId()); } private void createTestedUserMemberships() throws BonitaException, InterruptedException { user1 = createUser("userM1", "engine1"); logout(); loginOnDefaultTenantWith(user1.getUserName(), "engine1"); user2 = createUser("userM2", "engine2"); user3 = createUser("userM3", "engine3"); user4 = createUser("userM4", "engine4"); final RoleCreator roleCreator1 = new RoleCreator("roleM1"); roleCreator1.setDisplayName("roleLabel").setDescription("create role for userMembership"); role1 = getIdentityAPI().createRole(roleCreator1); final RoleCreator roleCreator2 = new RoleCreator("roleM2"); roleCreator2.setDisplayName("roleLabel").setDescription("create role for userMembership"); role2 = getIdentityAPI().createRole(roleCreator2); final RoleCreator roleCreator3 = new RoleCreator("roleM3"); roleCreator3.setDisplayName("roleLabel").setDescription("create role for userMembership"); role3 = getIdentityAPI().createRole(roleCreator3); final RoleCreator roleCreator4 = new RoleCreator("roleM4"); roleCreator4.setDisplayName("roleLabel").setDescription("create role for userMembership"); role4 = getIdentityAPI().createRole(roleCreator4); group1 = createGroup("groupM1", "grouplabel", "create group for userMembership"); group2 = createGroup("groupM2", "grouplabel", "create group for userMembership"); group3 = createGroup("groupM3", "grouplabel", "create group for userMembership"); group4 = createGroup("groupM4", "grouplabel", "create group for userMembership"); getIdentityAPI().addUserMembership(user1.getId(), group1.getId(), role1.getId()); Thread.sleep(10); getIdentityAPI().addUserMembership(user1.getId(), group2.getId(), role2.getId()); Thread.sleep(10); getIdentityAPI().addUserMembership(user1.getId(), group3.getId(), role3.getId()); Thread.sleep(10); getIdentityAPI().addUserMembership(user1.getId(), group4.getId(), role4.getId()); Thread.sleep(10); getIdentityAPI().addUserMembership(user2.getId(), group3.getId(), role4.getId()); getIdentityAPI().addUserMembership(user3.getId(), group4.getId(), role2.getId()); getIdentityAPI().addUserMembership(user4.getId(), group3.getId(), role1.getId()); } private void deleteTestedUserMemberships() throws BonitaException { getIdentityAPI().deleteUserMembership(user1.getId(), group1.getId(), role1.getId()); getIdentityAPI().deleteUserMembership(user1.getId(), group2.getId(), role2.getId()); getIdentityAPI().deleteUserMembership(user1.getId(), group3.getId(), role3.getId()); getIdentityAPI().deleteUserMembership(user1.getId(), group4.getId(), role4.getId()); getIdentityAPI().deleteUserMembership(user2.getId(), group3.getId(), role4.getId()); getIdentityAPI().deleteUserMembership(user3.getId(), group4.getId(), role2.getId()); getIdentityAPI().deleteUserMembership(user4.getId(), group3.getId(), role1.getId()); getIdentityAPI().deleteUser(user1.getId()); getIdentityAPI().deleteUser(user2.getId()); getIdentityAPI().deleteUser(user3.getId()); getIdentityAPI().deleteUser(user4.getId()); getIdentityAPI().deleteRole(role1.getId()); getIdentityAPI().deleteRole(role2.getId()); getIdentityAPI().deleteRole(role3.getId()); getIdentityAPI().deleteRole(role4.getId()); getIdentityAPI().deleteGroup(group1.getId()); getIdentityAPI().deleteGroup(group2.getId()); getIdentityAPI().deleteGroup(group3.getId()); getIdentityAPI().deleteGroup(group4.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/identity/OrganizationIT.java ================================================ /** * 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.identity; import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; import static org.junit.Assert.*; import java.io.*; import java.util.*; import java.util.Map.Entry; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.identity.GroupCreator.GroupField; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.StartProcessUntilStep; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.xml.sax.InputSource; public class OrganizationIT extends TestWithTechnicalUser { private static final String SKILLS_VALUE = "Java"; private static final String SKILLS_UPDATED_VALUE = "Java, Groovy"; private static final String LOCATION_VALUE = "Engineering"; private static final String QA = "QA"; private static final String ENGINE = "Engine"; private static final String WEB_TEAM_MANAGER = "Web Team Manager"; private static final String JOHNNYFOOTBALL = "johnnyfootball"; private static final String DEVELOPER = "Developer"; private static final String WEB_TEAM = "web team"; private static final String BONITA_MANAGER = "Bonita Manager"; private static final String MANAGER = "Manager"; private static final String WEB_GROUP_NAME = "Web"; private static final String LIUYANYAN_USERNAME = "liuyanyan"; private static final String ANTHONY_USERNAME = "anthony.birembault"; private static final String SKILLS_DESCRIPTION = "The user skills"; private static final String SKILLS_NAME = "Skills"; private static final String LOCATION_NAME = "Office location"; private static Date defaultUserLastUpdateDate = null; private static Date defaultGroupLastUpdateDate = null; private static Date defaultRoleLastUpdateDate = null; @Rule public final SystemOutRule systemOutRule = new SystemOutRule(); @Test public void importOrganization() throws Exception { // when importOrganization("simpleOrganization.xml"); // then final Map userInfoDefinitonsMap = checkDefaultCustomUserInfoDefinitions(); checkDefaultUsers(); checkDefaultCustomUserInfoValues(userInfoDefinitonsMap); checkDefaultGroups(); checkDefaultRoles(); checkDefaultMembership(); // clean-up getIdentityAPI().deleteOrganization(); } private void checkDefaultCustomUserInfoValues(final Map userInfoDefinitonsMap) throws Exception { checkDefaultCustomUserInfoValueForFirstUser(userInfoDefinitonsMap); checkDefaultCustomUserInfoValueForSecondUser(userInfoDefinitonsMap); } private void checkCustomUserInfoValuesAfterUpdate(final Map userInfoDefinitonsMap) throws Exception { // the first user is not present in the second file to import, so his information keep the same checkDefaultCustomUserInfoValueForFirstUser(userInfoDefinitonsMap); checkCustomUserInfoValueForSecondUserAfterUpdate(userInfoDefinitonsMap); } private void checkDefaultCustomUserInfoValueForSecondUser( final Map userInfoDefinitonsMap) throws UserNotFoundException { final User user = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME); final SearchOptions searchOptions = getCustomUserInfoValueSearchOptions(user); final SearchResult searchResult = getIdentityAPI() .searchCustomUserInfoValues(searchOptions); assertThat(searchResult.getCount()).isEqualTo(1); checkCustomUserInfo(searchResult.getResult().get(0), SKILLS_NAME, SKILLS_VALUE, userInfoDefinitonsMap); } private void checkCustomUserInfoValueForSecondUserAfterUpdate( final Map userInfoDefinitonsMap) throws UserNotFoundException { final User user = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME); final SearchOptions searchOptions = getCustomUserInfoValueSearchOptions(user); final SearchResult searchResult = getIdentityAPI() .searchCustomUserInfoValues(searchOptions); assertThat(searchResult.getCount()).isEqualTo(2); checkCustomUserInfo(searchResult.getResult().get(0), LOCATION_NAME, LOCATION_VALUE, userInfoDefinitonsMap); checkCustomUserInfo(searchResult.getResult().get(1), SKILLS_NAME, SKILLS_UPDATED_VALUE, userInfoDefinitonsMap); } private void checkDefaultCustomUserInfoValueForFirstUser( final Map userInfoDefinitonsMap) throws UserNotFoundException { final User user = getIdentityAPI().getUserByUserName(ANTHONY_USERNAME); final SearchOptions searchOptions = getCustomUserInfoValueSearchOptions(user); final SearchResult searchResult = getIdentityAPI() .searchCustomUserInfoValues(searchOptions); assertThat(searchResult.getCount()).isEqualTo(2); checkCustomUserInfo(searchResult.getResult().get(0), LOCATION_NAME, LOCATION_VALUE, userInfoDefinitonsMap); checkCustomUserInfo(searchResult.getResult().get(1), SKILLS_NAME, SKILLS_VALUE, userInfoDefinitonsMap); } private SearchOptions getCustomUserInfoValueSearchOptions(final User user) { final SearchOptionsBuilder optionsBuilder = new SearchOptionsBuilder(0, 10); optionsBuilder.filter(CustomUserInfoValueSearchDescriptor.USER_ID, user.getId()); optionsBuilder.sort(CustomUserInfoValueSearchDescriptor.DEFINITION_ID, Order.ASC); return optionsBuilder.done(); } private void checkCustomUserInfo(final CustomUserInfoValue customUserInfoValue, final String expectedName, final String expectedValue, final Map userInfoDefinitonsMap) { assertThat(customUserInfoValue.getDefinitionId()).isEqualTo(userInfoDefinitonsMap.get(expectedName).getId()); assertThat(customUserInfoValue.getValue()).isEqualTo(expectedValue); } private Map checkDefaultCustomUserInfoDefinitions() { final List customUserInfoDefinitions = getIdentityAPI() .getCustomUserInfoDefinitions(0, 10); assertThat(customUserInfoDefinitions.size()).isEqualTo(2); final CustomUserInfoDefinition firstDefinition = customUserInfoDefinitions.get(0); final CustomUserInfoDefinition secondDefinition = customUserInfoDefinitions.get(1); checkCustomUserInfoDefinition(LOCATION_NAME, null, firstDefinition); checkCustomUserInfoDefinition(SKILLS_NAME, SKILLS_DESCRIPTION, secondDefinition); final Map userInfoDefMap = new HashMap<>(2); userInfoDefMap.put(firstDefinition.getName(), firstDefinition); userInfoDefMap.put(secondDefinition.getName(), secondDefinition); return userInfoDefMap; } private Map checkCustomUserInfoDefinitionsAfterUpdate() { final List customUserInfoDefinitions = getIdentityAPI() .getCustomUserInfoDefinitions(0, 10); assertThat(customUserInfoDefinitions.size()).isEqualTo(2); final CustomUserInfoDefinition firstDefinition = customUserInfoDefinitions.get(0); final CustomUserInfoDefinition secondDefinition = customUserInfoDefinitions.get(1); checkCustomUserInfoDefinition(LOCATION_NAME, "The office location", firstDefinition); checkCustomUserInfoDefinition(SKILLS_NAME, "The user skills were updated", secondDefinition); final Map userInfoDefMap = new HashMap<>(2); userInfoDefMap.put(firstDefinition.getName(), firstDefinition); userInfoDefMap.put(secondDefinition.getName(), secondDefinition); return userInfoDefMap; } private void checkCustomUserInfoDefinition(final String expectedName, final String expectedDescription, final CustomUserInfoDefinition customUserInfoDefinition) { assertThat(customUserInfoDefinition.getName()).isEqualTo(expectedName); assertThat(customUserInfoDefinition.getDescription()).isEqualTo(expectedDescription); } @Test public void reImportUserMembershipDeleted() throws Exception { final String userName = ANTHONY_USERNAME; importOrganization("simpleOrganization.xml"); final User persistedUser = getIdentityAPI().getUserByUserName(userName); final List userMemberships = getIdentityAPI().getUserMemberships(persistedUser.getId(), 0, 10, UserMembershipCriterion.ASSIGNED_DATE_ASC); assertEquals(1, userMemberships.size()); getIdentityAPI().deleteUserMembership(userMemberships.get(0).getId()); // Re-import organization importOrganization("simpleOrganization.xml"); final User persistedUser2 = getIdentityAPI().getUserByUserName(userName); final List userMemberships2 = getIdentityAPI().getUserMemberships(persistedUser2.getId(), 0, 10, UserMembershipCriterion.ASSIGNED_DATE_ASC); assertEquals(1, userMemberships2.size()); // clean-up getIdentityAPI().deleteOrganization(); } private void importOrganization(final String fileName) throws IOException, OrganizationImportException { try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream(fileName)) { getIdentityAPI().importOrganization(IOUtils.toString(xmlStream, UTF_8)); } } @Test public void importOrganizationWithEnabledAndDisabledUsers() throws Exception { // create XML file importOrganization("simpleOrganization.xml"); final User persistedUser = getIdentityAPI().getUserByUserName(ANTHONY_USERNAME); assertEquals(WEB_TEAM_MANAGER, persistedUser.getJobTitle()); assertFalse(persistedUser.isEnabled()); // check createdBy for user assertEquals(getSession().getUserId(), persistedUser.getCreatedBy()); final User persistedUser1 = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME); assertNotNull(persistedUser1); assertTrue(persistedUser1.isEnabled()); // clean-up getIdentityAPI().deleteUser(persistedUser.getId()); getIdentityAPI().deleteUser(persistedUser1.getId()); final Role persistedRole = getIdentityAPI().getRoleByName(MANAGER); assertNotNull(persistedRole); getIdentityAPI().deleteRole(persistedRole.getId()); final Role persistedRole1 = getIdentityAPI().getRoleByName(DEVELOPER); assertNotNull(persistedRole1); getIdentityAPI().deleteRole(persistedRole1.getId()); final Group persistedGroup = getIdentityAPI().getGroupByPath(WEB_GROUP_NAME); assertNotNull(persistedGroup); getIdentityAPI().deleteGroup(persistedGroup.getId()); final Group persistedGroup1 = getIdentityAPI().getGroupByPath(ENGINE); assertNotNull(persistedGroup1); getIdentityAPI().deleteGroup(persistedGroup1.getId()); } @Test public void importComplexOrganization() throws Exception { // create XML file try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream("complexOrganization.xml")) { final byte[] organisationContent = IOUtils.toByteArray(xmlStream); getIdentityAPI().importOrganization(new String(organisationContent)); } final User jack = getIdentityAPI().getUserByUserName("jack"); final User john = getIdentityAPI().getUserByUserName("john"); assertEquals("John", john.getFirstName()); assertEquals("Doe", john.getLastName()); assertEquals("M", john.getTitle()); assertEquals(jack.getId(), john.getManagerUserId()); assertEquals(WEB_TEAM_MANAGER, john.getJobTitle()); final ContactData johnPersoContactData = getIdentityAPI().getUserContactData(john.getId(), true); assertEquals("emailValue", johnPersoContactData.getEmail()); assertEquals("phoneNumberValue", johnPersoContactData.getPhoneNumber()); assertEquals("mobileNumberValue", johnPersoContactData.getMobileNumber()); assertEquals("faxNumberValue", johnPersoContactData.getFaxNumber()); assertEquals("buildingValue", johnPersoContactData.getBuilding()); assertEquals("roomValue", johnPersoContactData.getRoom()); assertEquals("addressValue", johnPersoContactData.getAddress()); assertEquals("zipCodeValue", johnPersoContactData.getZipCode()); assertEquals("cityValue", johnPersoContactData.getCity()); assertEquals("stateValue", johnPersoContactData.getState()); assertEquals("countryValue", johnPersoContactData.getCountry()); assertEquals("websiteValue", johnPersoContactData.getWebsite()); final ContactData johnProfessionalContactData = getIdentityAPI().getUserContactData(john.getId(), false); assertEquals("emailProfessionalValue", johnProfessionalContactData.getEmail()); assertEquals("phoneNumberProfessionalValue", johnProfessionalContactData.getPhoneNumber()); assertEquals("mobileNumberProfessionalValue", johnProfessionalContactData.getMobileNumber()); assertEquals("faxNumberProfessionalValue", johnProfessionalContactData.getFaxNumber()); assertEquals("buildingProfessionalValue", johnProfessionalContactData.getBuilding()); assertEquals("roomProfessionalValue", johnProfessionalContactData.getRoom()); assertEquals("addressProfessionalValue", johnProfessionalContactData.getAddress()); assertEquals("zipCodeProfessionalValue", johnProfessionalContactData.getZipCode()); assertEquals("cityProfessionalValue", johnProfessionalContactData.getCity()); assertEquals("stateProfessionalValue", johnProfessionalContactData.getState()); assertEquals("countryProfessionalValue", johnProfessionalContactData.getCountry()); assertEquals("websiteProfessionalValue", johnProfessionalContactData.getWebsite()); getIdentityAPI().getUserByUserName("james"); final Group bonitaRD = getIdentityAPI().getGroupByPath("/BonitaSoft/RD"); final Group rd = getIdentityAPI().getGroupByPath("/RD"); getIdentityAPI().getGroupByPath("/BonitaSoft/RD"); List userMemberships = getIdentityAPI().getUserMemberships(john.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC); assertEquals(1, userMemberships.size()); UserMembership userMembership = userMemberships.get(0); assertEquals(bonitaRD.getId(), userMembership.getGroupId()); assertEquals(jack.getId(), userMembership.getAssignedBy()); assertEquals(new Date(1331142448365L), userMembership.getAssignedDate()); userMemberships = getIdentityAPI().getUserMemberships(jack.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC); assertEquals(1, userMemberships.size()); userMembership = userMemberships.get(0); assertEquals(rd.getId(), userMembership.getGroupId()); // clean-up getIdentityAPI().deleteOrganization(); } @Test public void importOrganizationWithWarnings_return_no_warnings_on_good_XML() throws IOException, OrganizationImportException, DeletionException { //given List warnings; try (final InputStream xmlStream = OrganizationIT.class.getResourceAsStream("complexOrganization.xml")) { final byte[] organisationContent = IOUtils.toByteArray(xmlStream); //when warnings = getIdentityAPI().importOrganizationWithWarnings(new String(organisationContent), ImportPolicy.IGNORE_DUPLICATES); } //then assertThat(warnings).isEmpty(); // clean-up getIdentityAPI().deleteOrganization(); } @Test(expected = GroupNotFoundException.class) public void importOrganizationWithWarnings_return_warnings_on_faulty_group_names() throws DeletionException, OrganizationImportException, IOException, GroupNotFoundException { //given List warnings; try (final InputStream xmlStream = OrganizationIT.class .getResourceAsStream("complexOrganizationWithBadGroup.xml")) { final byte[] organisationContent = IOUtils.toByteArray(xmlStream); //when warnings = getIdentityAPI().importOrganizationWithWarnings(new String(organisationContent), ImportPolicy.IGNORE_DUPLICATES); } //then try { assertThat(warnings).hasSize(1); assertThat(warnings).contains( "The group name RD/Studio contains the character '/' which is not supported. The group has not been imported"); getIdentityAPI().getGroupByPath("/RD/Studio"); } finally { // clean-up getIdentityAPI().deleteOrganization(); } } @Test public void importOrganizationWithWarnings_imports_the_correct_groups_if_the_incorrect_one_is_present() throws DeletionException, OrganizationImportException, IOException, GroupNotFoundException { //given try (final InputStream xmlStream = OrganizationIT.class .getResourceAsStream("complexOrganizationWithBadGroup.xml")) { final byte[] organisationContent = IOUtils.toByteArray(xmlStream); //when getIdentityAPI().importOrganizationWithWarnings(new String(organisationContent), ImportPolicy.IGNORE_DUPLICATES); } //then //Should not throw any exception getIdentityAPI().getGroupByPath("/BonitaSoft"); getIdentityAPI().getGroupByPath("/BonitaSoft/RD"); getIdentityAPI().getGroupByPath("/BonitaSoft/Support"); assertThat(getIdentityAPI().getNumberOfGroups()).isEqualTo(3); // clean-up getIdentityAPI().deleteOrganization(); } @Test public void importACMEOrganizationTwiceWithDefaultProfile() throws Exception { try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream("ACME.xml")) { final byte[] organisationContent = IOUtils.toByteArray(xmlStream); getIdentityAPI().importOrganization(new String(organisationContent)); getIdentityAPI().importOrganization(new String(organisationContent)); } // clean-up getIdentityAPI().deleteOrganization(); } @Test public void should_not_update_existing_user_when_importing_organization_in_IGNORE_DUPLICATES() throws Exception { User walter, oldManager, newManager; // first upload of ACME organization try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream("ACME.xml")) { getIdentityAPI().importOrganizationWithWarnings(new String(IOUtils.toByteArray(xmlStream)), ImportPolicy.IGNORE_DUPLICATES); } //change walter.bates manager walter = getIdentityAPI().getUserByUserName("walter.bates"); //set new new manager oldManager = getIdentityAPI().getUser(walter.getManagerUserId()); newManager = getIdentityAPI().getUserByUserName("william.jobs"); //set new manager assertThat(oldManager.getId()).isNotEqualTo(newManager.getId()); getIdentityAPI().updateUser(walter.getId(), new UserUpdater() .setLastName("Bates-Jobs") .setManagerId(newManager.getId())); //import again ACME orga try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream("ACME.xml")) { getIdentityAPI().importOrganizationWithWarnings(new String(IOUtils.toByteArray(xmlStream)), ImportPolicy.IGNORE_DUPLICATES); } //check if user still have the newManager as Manager assertThat(getIdentityAPI().getUserByUserName("walter.bates")).satisfies(u -> { assertThat(u.getManagerUserId()).isEqualTo(newManager.getId()); assertThat(u.getLastName()).isEqualTo("Bates-Jobs"); }); // clean-up getIdentityAPI().deleteOrganization(); } @Test public void importACMEOrganizationTwiceButRemoveGroupsAndRole() throws Exception { // create XML file final byte[] organisationContent; try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream("ACME.xml")) { organisationContent = IOUtils.toByteArray(xmlStream); } getIdentityAPI().importOrganization(new String(organisationContent)); final long numberOfGroups = getIdentityAPI().getNumberOfGroups(); final long numberOfRoles = getIdentityAPI().getNumberOfRoles(); // remove some groups and roles final List groups = getIdentityAPI().getGroups(1, 3, GroupCriterion.NAME_ASC); final List groupIds = new ArrayList<>(groups.size()); for (final Group group : groups) { groupIds.add(group.getId()); } getIdentityAPI().deleteGroups(groupIds); final List roles = getIdentityAPI().getRoles(0, 2, RoleCriterion.NAME_ASC); final List roleIds = new ArrayList<>(roles.size()); for (final Role role : roles) { roleIds.add(role.getId()); } getIdentityAPI().deleteRoles(roleIds); getIdentityAPI().importOrganization(new String(organisationContent)); assertEquals(numberOfGroups, getIdentityAPI().getNumberOfGroups()); assertEquals(numberOfRoles, getIdentityAPI().getNumberOfRoles()); // clean-up getIdentityAPI().deleteOrganization(); } @Test(expected = InvalidOrganizationFileFormatException.class) public void importOrganizationWithOrganizationImportException() throws Exception { final String xmlOrganization = ""; final User createUser = getIdentityAPI().createUser("john", "bpm"); try { getIdentityAPI().importOrganization(xmlOrganization); } catch (final OrganizationImportException e) { // check john was not deleted: assertNotNull("import organization with a bad file made the organization to be deleted!", getIdentityAPI().getUserByUserName("john")); deleteUser(createUser); throw e; } } @Test(expected = OrganizationImportException.class) public void importOrganizationFailRollBackToOldOrganization() throws Exception { try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream("organizationFailOnDuplicates.xml")) { byte[] organisationContent = IOUtils.toByteArray(xmlStream); final String xmlOrganization = new String(organisationContent); getIdentityAPI().importOrganization(xmlOrganization); // check john is replaced before james is imported (in the xml) assertTrue(xmlOrganization, xmlOrganization.indexOf("james") < xmlOrganization.indexOf("john")); // clear organization getIdentityAPI().deleteOrganization(); // ensure james does not exist anymore try { getIdentityAPI().getUserByUserName("james"); fail("james should not be found."); } catch (final UserNotFoundException unfe) { // ok } // we have only "john" as user final User createUser = getIdentityAPI().createUser("john", "bpm", "John", null); try { organisationContent = IOUtils.toByteArray(xmlStream); getIdentityAPI().importOrganizationWithWarnings(new String(organisationContent), ImportPolicy.FAIL_ON_DUPLICATES); } catch (final OrganizationImportException e) { // check john was not deleted and have old username try { getIdentityAPI().getUserByUserName("james"); fail("old organisation should be restored"); } catch (final UserNotFoundException unfe) { deleteUser(createUser); throw e; } } } } @Test public void importOrganizationWithCycle() throws Exception { try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream("organizationWithCycle.xml")) { final byte[] bs = IOUtils.toByteArray(xmlStream); final String organizationContent = new String(bs); getIdentityAPI().importOrganization(organizationContent); // clean-up getIdentityAPI().deleteOrganization(); getIdentityAPI().importOrganization(organizationContent); } final List users = getIdentityAPI().getUsers(0, 10, UserCriterion.USER_NAME_ASC); assertThat(users).extracting("userName", "managerUserId").containsExactly(tuple("user1", users.get(1).getId()), tuple("user2", users.get(2).getId()), tuple("user3", users.get(0).getId())); assertEquals(3, users.size()); getIdentityAPI().deleteOrganization(); } @Test public void deleteOrganization() throws Exception { // Create records for user role, group and membership final User persistedUser1 = getIdentityAPI().createUser(LIUYANYAN_USERNAME, "bpm"); final User persistedUser2 = getIdentityAPI().createUser(ANTHONY_USERNAME, "bpm"); final RoleCreator rc1 = new RoleCreator(DEVELOPER); rc1.setDisplayName("roleDisplayName"); final Role persistedRole1 = getIdentityAPI().createRole(rc1); final RoleCreator rc2 = new RoleCreator(MANAGER); rc2.setDisplayName("roleDisplayName"); final Role persistedRole2 = getIdentityAPI().createRole(rc2); final GroupCreator groupCreator1 = new GroupCreator(ENGINE); groupCreator1.setDisplayName("engine team"); final Group persistedGroup1 = getIdentityAPI().createGroup(groupCreator1); final GroupCreator groupCreator2 = new GroupCreator(WEB_GROUP_NAME); groupCreator2.setDisplayName(WEB_TEAM); final Group persistedGroup2 = getIdentityAPI().createGroup(groupCreator2); getIdentityAPI().addUserMembership(persistedUser1.getId(), persistedGroup1.getId(), persistedRole1.getId()); getIdentityAPI().addUserMembership(persistedUser2.getId(), persistedGroup2.getId(), persistedRole2.getId()); getIdentityAPI().createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator("ToDelete")); assertEquals(1, getIdentityAPI().getNumberOfUserMemberships(persistedUser1.getId())); assertEquals(2, getIdentityAPI().getNumberOfGroups()); assertEquals(2, getIdentityAPI().getNumberOfUsers()); assertEquals(2, getIdentityAPI().getNumberOfRoles()); // Create process that is mapped to user of organization final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); builder.createNewInstance("deleteAllHuman", "1.1").addActor(ACTOR_NAME, true).addUserTask("human", ACTOR_NAME); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, persistedUser1); final StartProcessUntilStep startProcessUntilStep = startProcessAndWaitForTask(processDefinition.getId(), "human"); assignAndExecuteStep(startProcessUntilStep.getActivityInstance(), persistedUser1.getId()); waitForProcessToFinish(startProcessUntilStep.getProcessInstance()); // delete organization and do check getIdentityAPI().deleteOrganization(); assertEquals(0, getIdentityAPI().getNumberOfGroups()); assertEquals(0, getIdentityAPI().getNumberOfUsers()); assertEquals(0, getIdentityAPI().getNumberOfRoles()); assertEquals(0, getIdentityAPI().getNumberOfUserMemberships(persistedUser1.getId())); assertThat(getIdentityAPI().getCustomUserInfoDefinitions(0, 10)).isEmpty(); // reload the process deploy info: final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState()); // Clean up disableAndDeleteProcess(processDefinition); } @Test(expected = DeletionException.class) public void cantDeleteOrganizationWhenProcessInstanceIsActive() throws Exception { // Create records for user role, group and membership final User persistedUser1 = getIdentityAPI().createUser(LIUYANYAN_USERNAME, "bpm"); final User persistedUser2 = getIdentityAPI().createUser(ANTHONY_USERNAME, "bpm"); final RoleCreator rc1 = new RoleCreator(DEVELOPER); rc1.setDisplayName("roleDisplayName"); final Role persistedRole1 = getIdentityAPI().createRole(rc1); final RoleCreator rc2 = new RoleCreator(MANAGER); rc2.setDisplayName("roleDisplayName"); final Role persistedRole2 = getIdentityAPI().createRole(rc2); final GroupCreator groupCreator1 = new GroupCreator(ENGINE); groupCreator1.setDisplayName("engine team"); final Group persistedGroup1 = getIdentityAPI().createGroup(groupCreator1); final GroupCreator groupCreator2 = new GroupCreator(WEB_GROUP_NAME); groupCreator2.setDisplayName(WEB_TEAM); final Group persistedGroup2 = getIdentityAPI().createGroup(groupCreator2); getIdentityAPI().addUserMembership(persistedUser1.getId(), persistedGroup1.getId(), persistedRole1.getId()); getIdentityAPI().addUserMembership(persistedUser2.getId(), persistedGroup2.getId(), persistedRole2.getId()); assertEquals(1, getIdentityAPI().getNumberOfUserMemberships(persistedUser1.getId())); assertEquals(2, getIdentityAPI().getNumberOfGroups()); assertEquals(2, getIdentityAPI().getNumberOfUsers()); assertEquals(2, getIdentityAPI().getNumberOfRoles()); // Create process that is mapped to user of organization final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); builder.createNewInstance("deleteAllHuman", "1.1").addActor(ACTOR_NAME, true).addUserTask("human", ACTOR_NAME); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, persistedUser1); final StartProcessUntilStep startProcessUntilStep = startProcessAndWaitForTask(processDefinition.getId(), "human"); // delete organization and do check try { getIdentityAPI().deleteOrganization(); } finally { // Clean up assignAndExecuteStep(startProcessUntilStep.getActivityInstance(), persistedUser1.getId()); waitForProcessToFinish(startProcessUntilStep.getProcessInstance()); getIdentityAPI().deleteOrganization(); disableAndDeleteProcess(processDefinition); } } @Test(expected = OrganizationImportException.class) public void importOrganizationFailOnDuplicates() throws Exception { // create XML file final ImportPolicy policy = ImportPolicy.FAIL_ON_DUPLICATES; final String userName = ANTHONY_USERNAME; final String roleName = MANAGER; final String groupName = WEB_GROUP_NAME; final String userName1 = LIUYANYAN_USERNAME; final String roleName1 = DEVELOPER; final String roleDisplayName1 = "Bonita developer"; final String groupName1 = ENGINE; final String groupDisplayName1 = "engine team"; importAndCheckFirstSimpleOrganization(); final User userToDisable = getIdentityAPI().getUserByUserName(userName); final UserUpdater updateDescriptor = new UserUpdater(); updateDescriptor.setEnabled(false); getIdentityAPI().updateUser(userToDisable.getId(), updateDescriptor); assertEquals(2, getIdentityAPI().getNumberOfUsers()); try { importOrganizationWithPolicy("simpleOrganizationDuplicates2.xml", policy); } catch (final OrganizationImportException e) { assertEquals(2, getIdentityAPI().getNumberOfUsers()); assertEquals(2, getIdentityAPI().getNumberOfGroups()); assertEquals(2, getIdentityAPI().getNumberOfRoles()); final User persistedUser = getIdentityAPI().getUserByUserName(userName); assertNotNull(persistedUser); assertEquals(WEB_TEAM_MANAGER, persistedUser.getJobTitle()); assertFalse(persistedUser.isEnabled()); final Role persistedRole = getIdentityAPI().getRoleByName(roleName); assertNotNull(persistedRole); assertEquals(BONITA_MANAGER, persistedRole.getDisplayName()); final Group persistedGroup = getIdentityAPI().getGroupByPath(groupName); assertNotNull(persistedGroup); assertEquals(WEB_TEAM, persistedGroup.getDisplayName()); final UserMembership persistedMembership = getIdentityAPI() .getUserMemberships(persistedUser.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC).get(0); assertEquals(groupName, persistedMembership.getGroupName()); assertEquals(roleName, persistedMembership.getRoleName()); assertEquals(userName, persistedMembership.getUsername()); // check assignedBy for membership assertEquals(getSession().getUserId(), persistedMembership.getAssignedBy()); final User persistedUser1 = getIdentityAPI().getUserByUserName(userName1); assertNotNull(persistedUser1); assertTrue(persistedUser1.isEnabled()); final Role persistedRole1 = getIdentityAPI().getRoleByName(roleName1); assertNotNull(persistedRole1); assertEquals(roleDisplayName1, persistedRole1.getDisplayName()); final Group persistedGroup1 = getIdentityAPI().getGroupByPath(groupName1); assertNotNull(persistedGroup1); assertEquals(groupDisplayName1, persistedGroup1.getDisplayName()); final UserMembership persistedMembership1 = getIdentityAPI() .getUserMemberships(persistedUser1.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC) .get(0); assertEquals(groupName1, persistedMembership1.getGroupName()); assertEquals(roleName1, persistedMembership1.getRoleName()); assertEquals(userName1, persistedMembership1.getUsername()); // check assignedBy for membership assertEquals(getSession().getUserId(), persistedMembership1.getAssignedBy()); // clean-up getIdentityAPI().deleteUser(persistedUser.getId()); getIdentityAPI().deleteRole(persistedRole.getId()); getIdentityAPI().deleteGroup(persistedGroup.getId()); getIdentityAPI().deleteUser(persistedUser1.getId()); getIdentityAPI().deleteRole(persistedRole1.getId()); getIdentityAPI().deleteGroup(persistedGroup1.getId()); throw e; } fail("This statement shouldn't be reached."); } @Test public void importOrganizationFailOnDuplicatesNoDuplicates() throws Exception { // create XML file final ImportPolicy policy = ImportPolicy.FAIL_ON_DUPLICATES; final String userName = ANTHONY_USERNAME; final String roleName = MANAGER; final String groupName = WEB_GROUP_NAME; final String userName1 = LIUYANYAN_USERNAME; final String roleName1 = DEVELOPER; final String roleDisplayName1 = "Bonita developer"; final String groupName1 = ENGINE; final String groupDisplayName1 = "engine team"; final String userName2 = JOHNNYFOOTBALL; final String roleName2 = "Tester"; final String groupName2 = QA; importAndCheckFirstSimpleOrganization(); importOrganizationWithPolicy("simpleOrganizationNoDuplicates.xml", policy); assertEquals(3, getIdentityAPI().getNumberOfUsers()); assertEquals(3, getIdentityAPI().getNumberOfGroups()); assertEquals(3, getIdentityAPI().getNumberOfRoles()); final User persistedUser = getIdentityAPI().getUserByUserName(userName); assertEquals(WEB_TEAM_MANAGER, persistedUser.getJobTitle()); // check createdBy for user assertEquals(getSession().getUserId(), persistedUser.getCreatedBy()); final Group persistedGroup = getIdentityAPI().getGroupByPath(groupName); assertEquals(WEB_TEAM, persistedGroup.getDisplayName()); // check createdBy for group assertEquals(getSession().getUserId(), persistedGroup.getCreatedBy()); final Role persistedRole = getIdentityAPI().getRoleByName(roleName); assertEquals(BONITA_MANAGER, persistedRole.getDisplayName()); // check createdBy for role assertEquals(getSession().getUserId(), persistedRole.getCreatedBy()); final UserMembership persistedMembership = getIdentityAPI() .getUserMemberships(persistedUser.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC) .get(0); assertEquals(groupName, persistedMembership.getGroupName()); assertEquals(roleName, persistedMembership.getRoleName()); assertEquals(userName, persistedMembership.getUsername()); // check assignedBy for membership assertEquals(getSession().getUserId(), persistedMembership.getAssignedBy()); final User persistedUser1 = getIdentityAPI().getUserByUserName(userName1); assertNotNull(persistedUser1); final Role persistedRole1 = getIdentityAPI().getRoleByName(roleName1); assertNotNull(persistedRole1); assertEquals(roleDisplayName1, persistedRole1.getDisplayName()); final Group persistedGroup1 = getIdentityAPI().getGroupByPath(groupName1); assertNotNull(persistedGroup1); assertEquals(groupDisplayName1, persistedGroup1.getDisplayName()); final UserMembership persistedMembership1 = getIdentityAPI() .getUserMemberships(persistedUser1.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC) .get(0); assertEquals(groupName1, persistedMembership1.getGroupName()); assertEquals(roleName1, persistedMembership1.getRoleName()); assertEquals(userName1, persistedMembership1.getUsername()); // check assignedBy for membership assertEquals(getSession().getUserId(), persistedMembership1.getAssignedBy()); final User persistedUser2 = getIdentityAPI().getUserByUserName(userName2); assertNotNull(persistedUser2); final Role persistedRole2 = getIdentityAPI().getRoleByName(roleName2); assertNotNull(persistedRole2); final Group persistedGroup2 = getIdentityAPI().getGroupByPath(groupName2); assertNotNull(persistedGroup2); final UserMembership persistedMembership2 = getIdentityAPI() .getUserMemberships(persistedUser2.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC) .get(0); assertEquals(groupName2, persistedMembership2.getGroupName()); assertEquals(roleName2, persistedMembership2.getRoleName()); assertEquals(userName2, persistedMembership2.getUsername()); // check assignedBy for membership assertEquals(getSession().getUserId(), persistedMembership2.getAssignedBy()); // clean-up getIdentityAPI().deleteOrganization(); } @Test public void importOrganizationMergeDuplicates() throws Exception { // given final ImportPolicy policy = ImportPolicy.MERGE_DUPLICATES; importAndCheckFirstSimpleOrganization(); // when importOrganizationWithPolicy("simpleOrganizationDuplicates2.xml", policy); // then final Map infoDefinitonsAfterUpdate = checkCustomUserInfoDefinitionsAfterUpdate(); checkUsersAfterUpdate(); checkCustomUserInfoValuesAfterUpdate(infoDefinitonsAfterUpdate); checkGroupsAfterUpdate(); checkRolesAfterUpdate(); checkMembershipAfterUpdate(); // clean-up getIdentityAPI().deleteOrganization(); } private void checkMembershipAfterUpdate() throws UserNotFoundException { final User persistedUser = getIdentityAPI().getUserByUserName(ANTHONY_USERNAME); final UserMembership persistedMembership = getIdentityAPI() .getUserMemberships(persistedUser.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC) .get(0); assertEquals(WEB_GROUP_NAME, persistedMembership.getGroupName()); assertEquals(MANAGER, persistedMembership.getRoleName()); assertEquals(ANTHONY_USERNAME, persistedMembership.getUsername()); // check assignedBy for membership assertEquals(getSession().getUserId(), persistedMembership.getAssignedBy()); final User persistedUser1 = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME); final UserMembership persistedMembership1 = getIdentityAPI() .getUserMemberships(persistedUser1.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC) .get(0); assertEquals(ENGINE, persistedMembership1.getGroupName()); assertEquals(DEVELOPER, persistedMembership1.getRoleName()); assertEquals(LIUYANYAN_USERNAME, persistedMembership1.getUsername()); // check assignedBy for membership assertEquals(getSession().getUserId(), persistedMembership1.getAssignedBy()); final User persistedUser2 = getIdentityAPI().getUserByUserName(JOHNNYFOOTBALL); final UserMembership persistedMembership2 = getIdentityAPI() .getUserMemberships(persistedUser2.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC) .get(0); assertEquals(QA, persistedMembership2.getGroupName()); assertEquals("Tester", persistedMembership2.getRoleName()); assertEquals(JOHNNYFOOTBALL, persistedMembership2.getUsername()); // check assignedBy for membership assertEquals(getSession().getUserId(), persistedMembership2.getAssignedBy()); } private void checkRolesAfterUpdate() throws RoleNotFoundException { final Role persistedRole = getIdentityAPI().getRoleByName(MANAGER); assertEquals(BONITA_MANAGER, persistedRole.getDisplayName()); // check createdBy for role assertEquals(getSession().getUserId(), persistedRole.getCreatedBy()); final Role persistedRole1 = getIdentityAPI().getRoleByName(DEVELOPER); assertNotNull(persistedRole1); assertEquals("Bonitasoft developer", persistedRole1.getDisplayName()); assertTrue(persistedRole1.getLastUpdate().after(defaultRoleLastUpdateDate)); final Role persistedRole2 = getIdentityAPI().getRoleByName("Tester"); assertNotNull(persistedRole2); } private void checkGroupsAfterUpdate() throws GroupNotFoundException { final Group persistedGroup = getIdentityAPI().getGroupByPath(WEB_GROUP_NAME); assertEquals(WEB_TEAM, persistedGroup.getDisplayName()); assertEquals(getSession().getUserId(), persistedGroup.getCreatedBy()); final Group persistedGroup1 = getIdentityAPI().getGroupByPath(ENGINE); assertNotNull(persistedGroup1); assertEquals("RD engine team", persistedGroup1.getDisplayName()); assertTrue(persistedGroup1.getLastUpdate().after(defaultGroupLastUpdateDate)); final Group persistedGroup2 = getIdentityAPI().getGroupByPath(QA); assertNotNull(persistedGroup2); } private void checkUsersAfterUpdate() throws UserNotFoundException { assertEquals(3, getIdentityAPI().getNumberOfGroups()); assertEquals(3, getIdentityAPI().getNumberOfRoles()); assertEquals(3, getIdentityAPI().getNumberOfUsers()); final User persistedUser = getIdentityAPI().getUserByUserName(ANTHONY_USERNAME); assertEquals(WEB_TEAM_MANAGER, persistedUser.getJobTitle()); assertEquals(getSession().getUserId(), persistedUser.getCreatedBy()); final User persistedUser1 = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME); assertNotNull(persistedUser1); assertTrue(persistedUser1.getLastUpdate().after(defaultUserLastUpdateDate)); final User persistedUser2 = getIdentityAPI().getUserByUserName(JOHNNYFOOTBALL); assertNotNull(persistedUser2); } @Test public void importOrganizationMergeDuplicatesWithEnabledAndDisabledUsers() throws Exception { // create XML file importAndCheckFirstSimpleOrganization(); importOrganizationWithPolicy("simpleOrganizationDuplicates2.xml", ImportPolicy.MERGE_DUPLICATES); assertEquals(3, getIdentityAPI().getNumberOfUsers()); assertEquals(3, getIdentityAPI().getNumberOfGroups()); assertEquals(3, getIdentityAPI().getNumberOfRoles()); final User persistedUser = getIdentityAPI().getUserByUserName(JOHNNYFOOTBALL); assertNotNull(persistedUser); assertFalse(persistedUser.isEnabled()); final User persistedUser1 = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME); assertNotNull(persistedUser1); assertTrue(persistedUser1.isEnabled()); // clean-up getIdentityAPI().deleteUser(persistedUser.getId()); getIdentityAPI().deleteUser(persistedUser1.getId()); getIdentityAPI().deleteRole(getIdentityAPI().getRoleByName("Tester").getId()); getIdentityAPI().deleteGroup(getIdentityAPI().getGroupByPath(QA).getId()); getIdentityAPI().deleteRole(getIdentityAPI().getRoleByName(DEVELOPER).getId()); getIdentityAPI().deleteGroup(getIdentityAPI().getGroupByPath(ENGINE).getId()); getIdentityAPI().deleteUser(getIdentityAPI().getUserByUserName(ANTHONY_USERNAME).getId()); getIdentityAPI().deleteRole(getIdentityAPI().getRoleByName(MANAGER).getId()); getIdentityAPI().deleteGroup(getIdentityAPI().getGroupByPath(WEB_GROUP_NAME).getId()); } @Test public void importOrganizationIgnoreDuplicates() throws Exception { // create XML file final ImportPolicy policy = ImportPolicy.IGNORE_DUPLICATES; final String userName = ANTHONY_USERNAME; final String roleName = MANAGER; final String groupName = WEB_GROUP_NAME; final String userName1 = LIUYANYAN_USERNAME; final String roleName1 = DEVELOPER; final String roleDisplayName1 = "Bonita developer"; final String groupName1 = ENGINE; final String groupDisplayName1 = "engine team"; final String userName2 = JOHNNYFOOTBALL; final String roleName2 = "Tester"; final String groupName2 = QA; importAndCheckFirstSimpleOrganization(); importOrganizationWithPolicy("simpleOrganizationDuplicates2.xml", policy); assertEquals(3, getIdentityAPI().getNumberOfUsers()); assertEquals(3, getIdentityAPI().getNumberOfGroups()); assertEquals(3, getIdentityAPI().getNumberOfRoles()); final User persistedUser = getIdentityAPI().getUserByUserName(userName); assertEquals(WEB_TEAM_MANAGER, persistedUser.getJobTitle()); // check createdBy for user assertEquals(getSession().getUserId(), persistedUser.getCreatedBy()); final Group persistedGroup = getIdentityAPI().getGroupByPath(groupName); assertEquals(WEB_TEAM, persistedGroup.getDisplayName()); // check createdBy for group assertEquals(getSession().getUserId(), persistedGroup.getCreatedBy()); final Role persistedRole = getIdentityAPI().getRoleByName(roleName); assertEquals(BONITA_MANAGER, persistedRole.getDisplayName()); // check createdBy for role assertEquals(getSession().getUserId(), persistedRole.getCreatedBy()); final UserMembership persistedMembership = getIdentityAPI() .getUserMemberships(persistedUser.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC) .get(0); assertEquals(groupName, persistedMembership.getGroupName()); assertEquals(roleName, persistedMembership.getRoleName()); assertEquals(userName, persistedMembership.getUsername()); // check assignedBy for membership assertEquals(getSession().getUserId(), persistedMembership.getAssignedBy()); final User persistedUser1 = getIdentityAPI().getUserByUserName(userName1); assertNotNull(persistedUser1); final Role persistedRole1 = getIdentityAPI().getRoleByName(roleName1); assertNotNull(persistedRole1); assertEquals(roleDisplayName1, persistedRole1.getDisplayName()); final Group persistedGroup1 = getIdentityAPI().getGroupByPath(groupName1); assertNotNull(persistedGroup1); assertEquals(groupDisplayName1, persistedGroup1.getDisplayName()); final UserMembership persistedMembership1 = getIdentityAPI() .getUserMemberships(persistedUser1.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC) .get(0); assertEquals(groupName1, persistedMembership1.getGroupName()); assertEquals(roleName1, persistedMembership1.getRoleName()); assertEquals(userName1, persistedMembership1.getUsername()); // check assignedBy for membership assertEquals(getSession().getUserId(), persistedMembership1.getAssignedBy()); final User persistedUser2 = getIdentityAPI().getUserByUserName(userName2); assertNotNull(persistedUser2); final Role persistedRole2 = getIdentityAPI().getRoleByName(roleName2); assertNotNull(persistedRole2); final Group persistedGroup2 = getIdentityAPI().getGroupByPath(groupName2); assertNotNull(persistedGroup2); final UserMembership persistedMembership2 = getIdentityAPI() .getUserMemberships(persistedUser2.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC) .get(0); assertEquals(groupName2, persistedMembership2.getGroupName()); assertEquals(roleName2, persistedMembership2.getRoleName()); assertEquals(userName2, persistedMembership2.getUsername()); // check assignedBy for membership assertEquals(getSession().getUserId(), persistedMembership2.getAssignedBy()); // clean-up getIdentityAPI().deleteUser(persistedUser.getId()); getIdentityAPI().deleteRole(persistedRole.getId()); getIdentityAPI().deleteGroup(persistedGroup.getId()); getIdentityAPI().deleteUser(persistedUser1.getId()); getIdentityAPI().deleteRole(persistedRole1.getId()); getIdentityAPI().deleteGroup(persistedGroup1.getId()); getIdentityAPI().deleteUser(persistedUser2.getId()); getIdentityAPI().deleteRole(persistedRole2.getId()); getIdentityAPI().deleteGroup(persistedGroup2.getId()); } @Test public void importOrganizationIgnoreDuplicatesWithEnabledAndDisabledUsers() throws Exception { // create XML file importAndCheckFirstSimpleOrganization(); importOrganizationWithPolicy("simpleOrganizationDuplicates2.xml", ImportPolicy.IGNORE_DUPLICATES); assertEquals(3, getIdentityAPI().getNumberOfUsers()); assertEquals(3, getIdentityAPI().getNumberOfGroups()); assertEquals(3, getIdentityAPI().getNumberOfRoles()); final User persistedUser = getIdentityAPI().getUserByUserName(JOHNNYFOOTBALL); assertNotNull(persistedUser); assertFalse(persistedUser.isEnabled()); final User persistedUser1 = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME); assertNotNull(persistedUser1); assertTrue(persistedUser1.isEnabled()); // clean-up getIdentityAPI().deleteUser(persistedUser.getId()); getIdentityAPI().deleteUser(persistedUser1.getId()); getIdentityAPI().deleteRole(getIdentityAPI().getRoleByName("Tester").getId()); getIdentityAPI().deleteGroup(getIdentityAPI().getGroupByPath(QA).getId()); getIdentityAPI().deleteRole(getIdentityAPI().getRoleByName(DEVELOPER).getId()); getIdentityAPI().deleteGroup(getIdentityAPI().getGroupByPath(ENGINE).getId()); getIdentityAPI().deleteUser(getIdentityAPI().getUserByUserName(ANTHONY_USERNAME).getId()); getIdentityAPI().deleteRole(getIdentityAPI().getRoleByName(MANAGER).getId()); getIdentityAPI().deleteGroup(getIdentityAPI().getGroupByPath(WEB_GROUP_NAME).getId()); } private void importOrganizationWithPolicy(final String xmlFile, final ImportPolicy policy) throws Exception { try (InputStream xmlStream = OrganizationIT.class.getResourceAsStream(xmlFile)) { final String organizationContent = IOUtils.toString(xmlStream, UTF_8); getIdentityAPI().importOrganizationWithWarnings(organizationContent, policy); } } private void importAndCheckFirstSimpleOrganization() throws Exception { // when importOrganization("simpleOrganizationDuplicates1.xml"); // then final Map userInfoDefinitions = checkDefaultCustomUserInfoDefinitions(); checkDefaultUsers(); checkDefaultCustomUserInfoValues(userInfoDefinitions); checkDefaultGroups(); checkDefaultRoles(); checkDefaultMembership(); } private void checkDefaultMembership() throws UserNotFoundException { final User persistedUser = getIdentityAPI().getUserByUserName(ANTHONY_USERNAME); final UserMembership persistedMembership = getIdentityAPI() .getUserMemberships(persistedUser.getId(), 0, 10, UserMembershipCriterion.GROUP_NAME_ASC) .get(0); assertEquals(WEB_GROUP_NAME, persistedMembership.getGroupName()); assertEquals(MANAGER, persistedMembership.getRoleName()); assertEquals(ANTHONY_USERNAME, persistedMembership.getUsername()); assertEquals(getSession().getUserId(), persistedMembership.getAssignedBy()); } private void checkDefaultRoles() throws RoleNotFoundException { final Role persistedRole = getIdentityAPI().getRoleByName(MANAGER); assertEquals(BONITA_MANAGER, persistedRole.getDisplayName()); assertEquals(getSession().getUserId(), persistedRole.getCreatedBy()); final Role persistedRole1 = getIdentityAPI().getRoleByName(DEVELOPER); assertNotNull(persistedRole1); if (defaultRoleLastUpdateDate == null) { defaultRoleLastUpdateDate = persistedRole1.getLastUpdate(); } } private void checkDefaultGroups() throws GroupNotFoundException { final Group persistedGroup = getIdentityAPI().getGroupByPath(WEB_GROUP_NAME); assertEquals(WEB_TEAM, persistedGroup.getDisplayName()); assertEquals(getSession().getUserId(), persistedGroup.getCreatedBy()); final Group persistedGroup1 = getIdentityAPI().getGroupByPath(ENGINE); assertNotNull(persistedGroup1); if (defaultGroupLastUpdateDate == null) { defaultGroupLastUpdateDate = persistedGroup1.getLastUpdate(); } } private void checkDefaultUsers() throws UserNotFoundException { final User persistedUser = getIdentityAPI().getUserByUserName(ANTHONY_USERNAME); assertEquals(WEB_TEAM_MANAGER, persistedUser.getJobTitle()); assertEquals(getSession().getUserId(), persistedUser.getCreatedBy()); final User persistedUser1 = getIdentityAPI().getUserByUserName(LIUYANYAN_USERNAME); assertNotNull(persistedUser1); if (defaultUserLastUpdateDate == null) { defaultUserLastUpdateDate = persistedUser1.getLastUpdate(); } } @Test public void exportOrganization() throws Exception { // create records for user role, group and membership // users final User persistedUser1 = getIdentityAPI().createUser(LIUYANYAN_USERNAME, "bpm"); final UserCreator creator = new UserCreator(ANTHONY_USERNAME, "bpm"); creator.setJobTitle(WEB_TEAM_MANAGER); final User persistedUser2 = getIdentityAPI().createUser(creator); // roles final RoleCreator rc1 = new RoleCreator(DEVELOPER); rc1.setDisplayName("Bonita developer"); rc1.setIcon("myIcon.jpg", new byte[] { 1, 2, 3 }); final Role persistedRole1 = getIdentityAPI().createRole(rc1); final RoleCreator rc2 = new RoleCreator(MANAGER); rc2.setDisplayName(BONITA_MANAGER); final Role persistedRole2 = getIdentityAPI().createRole(rc2); // groups final GroupCreator groupCreator1 = new GroupCreator(ENGINE); groupCreator1.setDisplayName("engine team"); final Group persistedGroup1 = getIdentityAPI().createGroup(groupCreator1); final GroupCreator groupCreator2 = new GroupCreator(WEB_GROUP_NAME); groupCreator2.setDisplayName(WEB_TEAM); final Group persistedGroup2 = getIdentityAPI().createGroup(groupCreator2); // membership final UserMembership membership1 = getIdentityAPI().addUserMembership(persistedUser1.getId(), persistedGroup1.getId(), persistedRole1.getId()); final UserMembership membership2 = getIdentityAPI().addUserMembership(persistedUser2.getId(), persistedGroup2.getId(), persistedRole2.getId()); // custom user info definition final CustomUserInfoDefinition skills = getIdentityAPI().createCustomUserInfoDefinition( new CustomUserInfoDefinitionCreator(SKILLS_NAME, SKILLS_DESCRIPTION)); getIdentityAPI().createCustomUserInfoDefinition(new CustomUserInfoDefinitionCreator(LOCATION_NAME)); // custom user info value getIdentityAPI().setCustomUserInfoValue(skills.getId(), persistedUser1.getId(), SKILLS_VALUE); // export and check final String organizationContent = getIdentityAPI().exportOrganization(); assertThat(organizationContent).contains(DEVELOPER); assertThat(organizationContent).contains("Bonita developer"); assertThat(organizationContent).contains(ENGINE); //Icon name and Icon path are not exported (deprecated) assertThat(organizationContent).doesNotContain(""); assertThat(organizationContent).doesNotContain(""); assertThat(organizationContent).contains("engine team"); assertThat(organizationContent) .contains(getIdentityAPI().getUserMembership(membership1.getId()).getGroupName()); assertThat(organizationContent) .contains(getIdentityAPI().getUserMembership(membership2.getId()).getGroupName()); assertThat(organizationContent).contains(SKILLS_NAME); assertThat(organizationContent).contains(SKILLS_DESCRIPTION); assertThat(organizationContent).contains(LOCATION_NAME); assertThat(organizationContent).contains(SKILLS_VALUE); // clean-up getIdentityAPI().deleteOrganization(); } @Test public void exportOrganizationWithSpecialCharacters() throws Exception { // create records for user role, group and membership final User user = getIdentityAPI().createUser("Céline*^$", "ploµ¨µ%§"); final User user2 = getIdentityAPI().createUser("ééééééééééééééééééé", "éééééééééééééééé"); final RoleCreator roleCreator = new RoleCreator("Développeur"); roleCreator.setDisplayName("'(-è"); roleCreator.setDescription("è-__ç_"); roleCreator.setIconName("(-è_"); roleCreator.setIconPath("^*_ç"); final Role role = getIdentityAPI().createRole(roleCreator); final GroupCreator groupCreator = new GroupCreator("µ£¨µ"); groupCreator.setDisplayName(".?/5434%¨%¨%"); groupCreator.setDescription("è-__ç_2"); groupCreator.setIconName("(-è_2"); groupCreator.setIconPath("^*_ç2"); groupCreator.setParentPath("$*ù$^ù"); final Group group = getIdentityAPI().createGroup(groupCreator); final UserMembership membership = getIdentityAPI().addUserMembership(user.getId(), group.getId(), role.getId()); // export and check final String organizationContent = getIdentityAPI().exportOrganization(); // Role for (final Entry entry : roleCreator.getFields().entrySet()) { assertThat(organizationContent).contains((String) entry.getValue()); } // Group for (final Entry entry : groupCreator.getFields().entrySet()) { assertThat(organizationContent).contains((String) entry.getValue()); } // User assertThat(organizationContent).contains("Céline*^$"); assertThat(organizationContent).contains("ééééééééééééééééééé"); // UserMembership assertThat(organizationContent).contains(getIdentityAPI().getUserMembership(membership.getId()).getGroupName()); // Verify all tags assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(" "); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); // clean-up deleteUsers(user, user2); getIdentityAPI().deleteRole(role.getId()); getIdentityAPI().deleteGroup(group.getId()); } @Test public void importAndExportOrganizationWithSpecialCharacters() throws Exception { importOrganization("OrganizationWithSpecialCharacters.xml"); // export and check final String organizationContent = getIdentityAPI().exportOrganization(); // Role assertThat(organizationContent).contains("ééé"); // Group assertThat(organizationContent).contains("ééééééééé"); // User assertThat(organizationContent).contains("éé"); // Verify all tags assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(" "); assertThat(organizationContent).contains(""); assertThat(organizationContent).contains(""); // clean-up getIdentityAPI().deleteOrganization(); } @Test public void exportAndImportOrganization() throws Exception { // create records for user role, group and membership final String password = "bpm"; final User persistedUser1 = getIdentityAPI().createUser(LIUYANYAN_USERNAME, password); final UserCreator creator = new UserCreator(ANTHONY_USERNAME, password); creator.setJobTitle(WEB_TEAM_MANAGER); final User persistedUser2 = getIdentityAPI().createUser(creator); final RoleCreator rc1 = new RoleCreator(DEVELOPER); rc1.setDisplayName("Bonita developer"); final Role persistedRole1 = getIdentityAPI().createRole(rc1); final RoleCreator rc2 = new RoleCreator(MANAGER); rc2.setDisplayName(BONITA_MANAGER); final Role persistedRole2 = getIdentityAPI().createRole(rc2); final GroupCreator groupCreator1 = new GroupCreator(ENGINE); groupCreator1.setDisplayName("engine team"); final Group persistedGroup1 = getIdentityAPI().createGroup(groupCreator1); final GroupCreator groupCreator2 = new GroupCreator(WEB_GROUP_NAME); groupCreator2.setDisplayName(WEB_TEAM); final Group persistedGroup2 = getIdentityAPI().createGroup(groupCreator2); final UserMembership membership1 = getIdentityAPI().addUserMembership(persistedUser1.getId(), persistedGroup1.getId(), persistedRole1.getId()); final UserMembership membership2 = getIdentityAPI().addUserMembership(persistedUser2.getId(), persistedGroup2.getId(), persistedRole2.getId()); // export and check final String organizationContent = getIdentityAPI().exportOrganization(); assertThat(organizationContent).contains(DEVELOPER); assertThat(organizationContent).contains("Bonita developer"); assertThat(organizationContent).contains(ENGINE); assertThat(organizationContent).contains("engine team"); assertThat(organizationContent) .contains(getIdentityAPI().getUserMembership(membership1.getId()).getGroupName()); assertThat(organizationContent) .contains(getIdentityAPI().getUserMembership(membership2.getId()).getGroupName()); // clean-up getIdentityAPI().deleteUser(persistedUser1.getId()); getIdentityAPI().deleteUser(persistedUser2.getId()); getIdentityAPI().deleteRole(persistedRole1.getId()); getIdentityAPI().deleteRole(persistedRole2.getId()); getIdentityAPI().deleteGroup(persistedGroup1.getId()); getIdentityAPI().deleteGroup(persistedGroup2.getId()); getIdentityAPI().importOrganization(organizationContent); final List users = getIdentityAPI().getUsers(0, 10, UserCriterion.FIRST_NAME_ASC); for (final User user : users) { getIdentityAPI().deleteUser(user.getId()); } assertEquals(2, users.size()); final List roles = getIdentityAPI().getRoles(0, 10, RoleCriterion.NAME_ASC); for (final Role role : roles) { getIdentityAPI().deleteRole(role.getId()); } final List groups = getIdentityAPI().getGroups(0, 10, GroupCriterion.NAME_ASC); for (final Group group : groups) { getIdentityAPI().deleteGroup(group.getId()); } } @Test public void exportOrganizationWithDisabledUsers() throws Exception { // create records for user final User persistedUser = getIdentityAPI().createUser(LIUYANYAN_USERNAME, "bpm"); final UserUpdater updater = new UserUpdater(); updater.setEnabled(false); getIdentityAPI().updateUser(persistedUser.getId(), updater); // export and check final String organizationContent = getIdentityAPI().exportOrganization(); assertThat(organizationContent).contains("false"); // clean-up getIdentityAPI().deleteUser(persistedUser.getId()); } @Test public void exportOrganizationWithEnabledUsers() throws Exception { // create records for user final UserCreator creator = new UserCreator(ANTHONY_USERNAME, "bpm"); creator.setEnabled(true); final User persistedUser = getIdentityAPI().createUser(creator); // export and check final String organizationContent = getIdentityAPI().exportOrganization(); assertThat(organizationContent).contains("true"); // clean-up getIdentityAPI().deleteUser(persistedUser.getId()); } @Test public void should_import_manager_of_user() throws Exception { //given User john = getIdentityAPI().createUser("john", "bpm"); //manager is created after the user to ensure it does not depend of user import order User johnManager = getIdentityAPI().createUser("johnManager", "bpm"); getIdentityAPI().updateUser(john.getId(), new UserUpdater().setManagerId(johnManager.getId())); String organization = getIdentityAPI().exportOrganization(); getIdentityAPI().deleteOrganization(); //when getIdentityAPI().importOrganization(organization); //then assertThat(getIdentityAPI().getUserByUserName("john").getManagerUserId()) .as("manager id of john").isEqualTo(getIdentityAPI().getUserByUserName("johnManager").getId()); //clean getIdentityAPI().deleteOrganization(); } @Test public void should_import_update_manager_of_user() throws Exception { //given User john = getIdentityAPI().createUser("john", "bpm"); //manager is created after the user to ensure it does not depend of user import order User johnManager = getIdentityAPI().createUser("johnManager", "bpm"); getIdentityAPI().updateUser(john.getId(), new UserUpdater().setManagerId(johnManager.getId())); User newJohnManager = getIdentityAPI().createUser("newjohnManager", "bpm"); String organization = getIdentityAPI().exportOrganization(); getIdentityAPI().updateUser(john.getId(), new UserUpdater().setManagerId(newJohnManager.getId())); getIdentityAPI().deleteUser(johnManager.getId()); //when getIdentityAPI().importOrganization(organization); //then assertThat(getIdentityAPI().getUserByUserName("john").getManagerUserId()) .as("manager id of john").isEqualTo(getIdentityAPI().getUserByUserName("johnManager").getId()); //clean getIdentityAPI().deleteOrganization(); } @Test public void should_import_user_even_if_manager_is_unkown() throws Exception { //given User john = getIdentityAPI().createUser("john", "bpm"); //manager is created after the user to ensure it does not depend of user import order User johnManager = getIdentityAPI().createUser("johnManager", "bpm"); getIdentityAPI().updateUser(john.getId(), new UserUpdater().setManagerId(johnManager.getId())); String organization = getIdentityAPI().exportOrganization(); getIdentityAPI().deleteOrganization(); //when systemOutRule.enableLog(); getIdentityAPI().importOrganization(removeJohnManagerNode(organization)); //then assertThat(getIdentityAPI().getUserByUserName("john").getManagerUserId()) .as("manager id of john").isEqualTo(0); assertThat(systemOutRule.getLog()) .contains( "The user john has a manager with username johnManager, but this one does not exist. Please set it manually."); //clean getIdentityAPI().deleteOrganization(); } private String removeJohnManagerNode(String organization) throws Exception { DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); Document doc = docBuilder.parse(new InputSource(new StringReader(organization))); Node organizationNode = doc.getFirstChild(); Node users = organizationNode.getChildNodes().item(3); if (users.getChildNodes().item(3).getAttributes().item(0).getNodeValue().equals("johnManager")) { users.removeChild(users.getChildNodes().item(3)); } else { users.removeChild(users.getChildNodes().item(1)); } TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); DOMSource source = new DOMSource(doc); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); transformer.transform(source, result); return writer.toString(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/identity/RoleIT.java ================================================ /** * 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.identity; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.impl.IconImpl; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.Test; public class RoleIT extends TestWithTechnicalUser { @Test public void createRoleUsingTheRoleBuilder() throws BonitaException { final String manager = "manager"; final Role role = getIdentityAPI().createRole(manager); assertNotNull(role); assertEquals(manager, role.getName()); getIdentityAPI().deleteRole(role.getId()); } @Test(expected = AlreadyExistsException.class) public void cannotCreateARoleWhichAlreadyExists() throws BonitaException { final String manager = "manager"; final Role role = getIdentityAPI().createRole(manager); final long roleId = role.getId(); try { getIdentityAPI().createRole(manager); } finally { getIdentityAPI().deleteRole(roleId); } } @Test(expected = CreationException.class) public void cannotCreateANullRole() throws BonitaException { getIdentityAPI().createRole((RoleCreator) null); } @Test(expected = CreationException.class) public void createARoleFail() throws BonitaException { getIdentityAPI().createRole((String) null); } @Test public void getRoleByName() throws BonitaException { final String manager = "manager"; final Role createdRole = getIdentityAPI().createRole(manager); final Role searchedRole = getIdentityAPI().getRoleByName(manager); assertEquals(manager, searchedRole.getName()); assertEquals(createdRole.getId(), searchedRole.getId()); getIdentityAPI().deleteRole(searchedRole.getId()); } @Test(expected = RoleNotFoundException.class) public void cannotGetARoleByAnUnexistingName() throws BonitaException { getIdentityAPI().getRoleByName("manager"); } @Test public void getAnEmptyListWhenNoRolesAreDefined() { final List roles = getIdentityAPI().getRoles(0, 10, RoleCriterion.NAME_ASC); assertEquals(0, roles.size()); } @Test public void getARole() throws Exception { final String manager = "manager"; final Role role = getIdentityAPI().createRole(manager); final List roles = getIdentityAPI().getRoles(0, 10, RoleCriterion.NAME_ASC); assertEquals(1, roles.size()); getIdentityAPI().deleteRole(role.getId()); } @Test public void getRole() throws BonitaException { final String manager = "manager"; final Role createdRole = getIdentityAPI().createRole(manager); final Role role = getIdentityAPI().getRole(createdRole.getId()); assertNotNull(role); assertEquals(manager, role.getName()); getIdentityAPI().deleteRole(role.getId()); } @Test public void roleNameAndDisplayNameShouldAccept255Chars() throws BonitaException { final String stringIndex_255_chars = "ईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईईऑऑऑऑऑ"; final Role role = getIdentityAPI() .createRole(new RoleCreator(stringIndex_255_chars).setDisplayName(stringIndex_255_chars)); // Should be no exception: getIdentityAPI().deleteRole(role.getId()); } @Test(expected = Exception.class) public void roleNameShouldNotAccept256Chars() throws BonitaException { final String stringIndex_256_chars = "ッッッシシジジッシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシシ"; getIdentityAPI().createRole(stringIndex_256_chars); } @Test(expected = Exception.class) public void roleDisplayNameShouldNotAccept256Chars() throws BonitaException { final String stringIndex_256_chars = "ッッッツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツツヅ"; getIdentityAPI().createRole(new RoleCreator("someName").setDisplayName(stringIndex_256_chars)); } @Test public void getRolesByIDs() throws BonitaException { final String manager = "manager"; final Role roleCreated1 = getIdentityAPI().createRole(manager); final String teamManager = "teamManager"; final Role roleCreated2 = getIdentityAPI().createRole(teamManager); final List roleIds = new ArrayList<>(); roleIds.add(roleCreated1.getId()); roleIds.add(roleCreated2.getId()); final Map roles = getIdentityAPI().getRoles(roleIds); assertNotNull(roles); assertEquals(2, roles.size()); assertEquals(manager, roles.get(roleCreated1.getId()).getName()); assertEquals(teamManager, roles.get(roleCreated2.getId()).getName()); getIdentityAPI().deleteRole(roles.get(roleCreated1.getId()).getId()); getIdentityAPI().deleteRole(roles.get(roleCreated2.getId()).getId()); } public void getRolesByIDsWithoutRoleNotFoundException() throws BonitaException { final String manager = "manager"; final Role roleCreated1 = getIdentityAPI().createRole(manager); final String teamManager = "teamManager"; final Role roleCreated2 = getIdentityAPI().createRole(teamManager); final List roleIds = new ArrayList<>(); roleIds.add(roleCreated1.getId()); roleIds.add(roleCreated2.getId() + 100); final Map roles = getIdentityAPI().getRoles(roleIds); assertNotNull(roles); assertEquals(1, roles.size()); assertEquals(teamManager, roles.get(roleCreated1.getId()).getName()); getIdentityAPI().deleteRole(roleCreated1.getId()); getIdentityAPI().deleteRole(roleCreated2.getId()); } @Test(expected = RoleNotFoundException.class) public void cannotGetAnUnexistingRole() throws BonitaException { getIdentityAPI().getRole(45); } @Test public void getNumberOfRoles() throws BonitaException { final String manager = "manager"; final String developer = "developer"; final Role role1 = getIdentityAPI().createRole(manager); final Role role2 = getIdentityAPI().createRole(developer); assertEquals(manager, role1.getName()); assertEquals(developer, role2.getName()); final long numberOfRoles = getIdentityAPI().getNumberOfRoles(); assertEquals(2, numberOfRoles); getIdentityAPI().deleteRole(role1.getId()); getIdentityAPI().deleteRole(role2.getId()); } @Test public void noRolesWhenThePageIndexIsOutOfRange() { final List roles = getIdentityAPI().getRoles(5, 10, RoleCriterion.NAME_ASC); assertTrue(roles.isEmpty()); } @Test public void deleteARole() throws BonitaException { final String manager = "manager"; final Role role = getIdentityAPI().createRole(manager); getIdentityAPI().deleteRole(role.getId()); } @Test public void deleteRoles() throws BonitaException { final String manager = "manager"; final String developer = "developer"; final Role role1 = getIdentityAPI().createRole(manager); final Role role2 = getIdentityAPI().createRole(developer); final List roleIds = new ArrayList<>(); roleIds.add(role2.getId()); roleIds.add(role1.getId()); getIdentityAPI().deleteRoles(roleIds); final long numberOfRoles = getIdentityAPI().getNumberOfRoles(); assertEquals(0, numberOfRoles); } @Test public void cannotDeleteARoleTwice() throws BonitaException { final String developer = "developer"; final Role role = getIdentityAPI().createRole(developer); getIdentityAPI().deleteRole(role.getId()); getIdentityAPI().deleteRole(role.getId()); } @Test public void canDeleteNoRole() throws BonitaException { final List roleIds = new ArrayList<>(); getIdentityAPI().deleteRoles(roleIds); } @Test(expected = DeletionException.class) public void cannotDeleteNullRoles() throws BonitaException { getIdentityAPI().deleteRoles(null); } @Test public void updateARole() throws Exception { final String developer = "developer"; final Role role = getIdentityAPI().createRole(developer); assertEquals(developer, role.getName()); final String manager = "manager"; final RoleUpdater updateDescriptor = new RoleUpdater(); updateDescriptor.setName(manager); final Role updatedRole = getIdentityAPI().updateRole(role.getId(), updateDescriptor); assertNotNull(updatedRole); assertEquals(manager, updatedRole.getName()); getIdentityAPI().deleteRole(updatedRole.getId()); } @Test(expected = UpdateException.class) public void canontUpdateARoleWithANullUpdateDescriptor() throws Exception { final String developer = "developer"; final Role role = getIdentityAPI().createRole(developer); try { getIdentityAPI().updateRole(role.getId(), null); } finally { getIdentityAPI().deleteRole(role.getId()); } } @Test(expected = RoleNotFoundException.class) public void cannotUpdateARoleWithAnUnexistingRoleIdentifier() throws Exception { final String manager = "manager"; final RoleUpdater updateDescriptor = new RoleUpdater(); updateDescriptor.setName(manager); getIdentityAPI().updateRole(83, updateDescriptor); } @Test public void getUsersOfARole() throws BonitaException { final String developer = "developer"; final Role role = getIdentityAPI().createRole(developer); final User user1 = getIdentityAPI().createUser("user1", "bpm"); final User user2 = getIdentityAPI().createUser("user2", "bpm"); final User user3 = getIdentityAPI().createUser("user3", "bpm"); final List userIds = new ArrayList<>(); userIds.add(user1.getId()); userIds.add(user2.getId()); final Group group = getIdentityAPI().createGroup("R&D", null); getIdentityAPI().addUserMemberships(userIds, group.getId(), role.getId()); final List users = getIdentityAPI().getUsersInRole(role.getId(), 0, 10, UserCriterion.USER_NAME_ASC); assertNotNull(users); assertEquals(2, users.size()); assertEquals(user1, users.get(0)); assertEquals(user2, users.get(1)); getIdentityAPI().deleteUserMemberships(userIds, group.getId(), role.getId()); getIdentityAPI().deleteRole(role.getId()); getIdentityAPI().deleteGroup(group.getId()); getIdentityAPI().deleteUsers(userIds); getIdentityAPI().deleteUser(user3.getId()); } @Test public void getActiveUsersOfARole_should_return_all_active_users_of_the_role() throws BonitaException { final String developer = "developer"; final Role role = getIdentityAPI().createRole(developer); final User user1 = getIdentityAPI().createUser(new UserCreator("user1", "bpm").setEnabled(true)); final User user2 = getIdentityAPI().createUser(new UserCreator("user2", "bpm").setEnabled(true)); final User user3 = getIdentityAPI().createUser(new UserCreator("user3", "bpm").setEnabled(false)); final User user4 = getIdentityAPI().createUser(new UserCreator("user4", "bpm").setEnabled(true)); final List userIds = new ArrayList<>(); userIds.add(user1.getId()); userIds.add(user2.getId()); userIds.add(user3.getId()); final Group group = getIdentityAPI().createGroup("R&D", null); getIdentityAPI().addUserMemberships(userIds, group.getId(), role.getId()); final List users = getIdentityAPI().getActiveUsersInRole(role.getId(), 0, 10, UserCriterion.USER_NAME_ASC); assertNotNull(users); assertEquals(2, users.size()); assertEquals(user1, users.get(0)); assertEquals(user2, users.get(1)); getIdentityAPI().deleteUserMemberships(userIds, group.getId(), role.getId()); getIdentityAPI().deleteRole(role.getId()); getIdentityAPI().deleteGroup(group.getId()); getIdentityAPI().deleteUsers(userIds); getIdentityAPI().deleteUser(user3.getId()); getIdentityAPI().deleteUser(user4.getId()); } @Test public void getInactiveUsersOfARole_should_return_all_inactive_users_of_the_role() throws BonitaException { final String developer = "developer"; final Role role = getIdentityAPI().createRole(developer); final User user1 = getIdentityAPI().createUser(new UserCreator("user1", "bpm").setEnabled(true)); final User user2 = getIdentityAPI().createUser(new UserCreator("user2", "bpm").setEnabled(true)); final User user3 = getIdentityAPI().createUser(new UserCreator("user3", "bpm").setEnabled(false)); final User user4 = getIdentityAPI().createUser(new UserCreator("user4", "bpm").setEnabled(false)); final List userIds = new ArrayList<>(); userIds.add(user1.getId()); userIds.add(user2.getId()); userIds.add(user3.getId()); final Group group = getIdentityAPI().createGroup("R&D", null); getIdentityAPI().addUserMemberships(userIds, group.getId(), role.getId()); final List users = getIdentityAPI().getInactiveUsersInRole(role.getId(), 0, 10, UserCriterion.USER_NAME_ASC); assertNotNull(users); assertEquals(1, users.size()); assertEquals(user3, users.get(0)); getIdentityAPI().deleteUserMemberships(userIds, group.getId(), role.getId()); getIdentityAPI().deleteRole(role.getId()); getIdentityAPI().deleteGroup(group.getId()); getIdentityAPI().deleteUsers(userIds); getIdentityAPI().deleteUser(user3.getId()); getIdentityAPI().deleteUser(user4.getId()); } @Test public void getNumberOfUsersInRole() throws BonitaException { final String developer = "developer"; final Role role = getIdentityAPI().createRole(developer); final User user1 = getIdentityAPI().createUser("user1", "bpm"); final User user2 = getIdentityAPI().createUser("user2", "bpm"); final List userIds = new ArrayList<>(); userIds.add(user1.getId()); userIds.add(user2.getId()); final Group group = getIdentityAPI().createGroup("R&D", null); getIdentityAPI().addUserMemberships(userIds, group.getId(), role.getId()); final long count = getIdentityAPI().getNumberOfUsersInRole(role.getId()); assertEquals(2, count); getIdentityAPI().deleteUserMemberships(userIds, group.getId(), role.getId()); getIdentityAPI().deleteRole(role.getId()); getIdentityAPI().deleteGroup(group.getId()); getIdentityAPI().deleteUser(user1.getId()); getIdentityAPI().deleteUser(user2.getId()); } @Test public void getPaginatedRolesWithRoleCriterion() throws BonitaException { final String developer = "developer"; final String manager = "manager"; final String cto = "chief technology officer"; final RoleCreator roleCreator1 = new RoleCreator(developer); roleCreator1.setDisplayName(developer); final Role aRole = getIdentityAPI().createRole(roleCreator1); final RoleCreator roleCreator2 = new RoleCreator(manager); roleCreator2.setDisplayName(manager); final Role bRole = getIdentityAPI().createRole(roleCreator2); final RoleCreator roleCreator3 = new RoleCreator(cto); roleCreator3.setDisplayName(cto); final Role cRole = getIdentityAPI().createRole(roleCreator3); final List rolesNameAsc = getIdentityAPI().getRoles(0, 3, RoleCriterion.NAME_ASC); assertEquals(3, rolesNameAsc.size()); assertEquals(cto, rolesNameAsc.get(0).getName()); assertEquals(developer, rolesNameAsc.get(1).getName()); assertEquals(manager, rolesNameAsc.get(2).getName()); final List rolesNameDesc = getIdentityAPI().getRoles(0, 3, RoleCriterion.NAME_DESC); assertEquals(3, rolesNameDesc.size()); assertEquals(manager, rolesNameDesc.get(0).getName()); assertEquals(developer, rolesNameDesc.get(1).getName()); assertEquals(cto, rolesNameDesc.get(2).getName()); final List rolesLabelAsc = getIdentityAPI().getRoles(0, 3, RoleCriterion.DISPLAY_NAME_ASC); assertEquals(3, rolesLabelAsc.size()); assertEquals(cto, rolesLabelAsc.get(0).getDisplayName()); assertEquals(developer, rolesLabelAsc.get(1).getDisplayName()); assertEquals(manager, rolesLabelAsc.get(2).getDisplayName()); final List rolesLabelDesc = getIdentityAPI().getRoles(0, 3, RoleCriterion.DISPLAY_NAME_DESC); assertEquals(3, rolesLabelDesc.size()); assertEquals(manager, rolesLabelDesc.get(0).getDisplayName()); assertEquals(developer, rolesLabelDesc.get(1).getDisplayName()); assertEquals(cto, rolesLabelDesc.get(2).getDisplayName()); getIdentityAPI().deleteRole(aRole.getId()); getIdentityAPI().deleteRole(bRole.getId()); getIdentityAPI().deleteRole(cRole.getId()); } @Test public void getRolesOnMultiPages() throws BonitaException { final String roleName1 = "role1"; final String roleName2 = "role2"; final String roleName3 = "role3"; final String roleName4 = "role4"; final String roleName5 = "role5"; final Role role1 = getIdentityAPI().createRole(roleName1); final Role role2 = getIdentityAPI().createRole(roleName2); final Role role3 = getIdentityAPI().createRole(roleName3); final Role role4 = getIdentityAPI().createRole(roleName4); final Role role5 = getIdentityAPI().createRole(roleName5); List rolesNameAsc = getIdentityAPI().getRoles(0, 2, RoleCriterion.NAME_ASC); assertEquals(2, rolesNameAsc.size()); assertEquals(roleName1, rolesNameAsc.get(0).getName()); assertEquals(roleName2, rolesNameAsc.get(1).getName()); rolesNameAsc = getIdentityAPI().getRoles(2, 2, RoleCriterion.NAME_ASC); assertEquals(2, rolesNameAsc.size()); assertEquals(roleName3, rolesNameAsc.get(0).getName()); assertEquals(roleName4, rolesNameAsc.get(1).getName()); rolesNameAsc = getIdentityAPI().getRoles(4, 2, RoleCriterion.NAME_ASC); assertEquals(1, rolesNameAsc.size()); assertEquals(roleName5, rolesNameAsc.get(0).getName()); final List roles = getIdentityAPI().getRoles(5, 2, RoleCriterion.NAME_ASC); assertTrue(roles.isEmpty()); getIdentityAPI().deleteRole(role1.getId()); getIdentityAPI().deleteRole(role2.getId()); getIdentityAPI().deleteRole(role3.getId()); getIdentityAPI().deleteRole(role4.getId()); getIdentityAPI().deleteRole(role5.getId()); } @Test(expected = AlreadyExistsException.class) public void cannotCreateTwoRoleWithTheSameName() throws BonitaException { final String role = "role"; final Role role1 = getIdentityAPI().createRole(role); try { getIdentityAPI().createRole(role); } finally { getIdentityAPI().deleteRole(role1.getId()); } } @Test public void searchRoleUsingFilter() throws BonitaException { final RoleCreator roleCreator1 = new RoleCreator("manager"); roleCreator1.setDisplayName("Man"); final Role mananger = getIdentityAPI().createRole(roleCreator1); final RoleCreator roleCreator2 = new RoleCreator("developer"); roleCreator2.setDisplayName("Dev"); final Role dev = getIdentityAPI().createRole(roleCreator2); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(RoleSearchDescriptor.DISPLAY_NAME, "Dev"); final SearchResult searchRoles = getIdentityAPI().searchRoles(builder.done()); assertNotNull(searchRoles); assertEquals(1, searchRoles.getCount()); final List roles = searchRoles.getResult(); assertEquals(dev, roles.get(0)); getIdentityAPI().deleteRole(mananger.getId()); getIdentityAPI().deleteRole(dev.getId()); } @Test public void searchRoleWithApostrophe() throws BonitaException { final RoleCreator roleCreator1 = new RoleCreator("mana'ger"); roleCreator1.setDisplayName("A"); final Role mananger = getIdentityAPI().createRole(roleCreator1); final RoleCreator roleCreator2 = new RoleCreator("developer"); roleCreator2.setDisplayName("mana'B"); final Role dev = getIdentityAPI().createRole(roleCreator2); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(RoleSearchDescriptor.DISPLAY_NAME, Order.ASC); builder.searchTerm("mana'"); final SearchResult searchRoles = getIdentityAPI().searchRoles(builder.done()); assertNotNull(searchRoles); assertEquals(2, searchRoles.getCount()); final List roles = searchRoles.getResult(); assertEquals(mananger, roles.get(0)); assertEquals(dev, roles.get(1)); getIdentityAPI().deleteRole(mananger.getId()); getIdentityAPI().deleteRole(dev.getId()); } @Test public void getRolesFromIds() throws BonitaException { final RoleCreator roleCreator1 = new RoleCreator("managerA"); roleCreator1.setDisplayName("managerA"); final Role RoleA = getIdentityAPI().createRole(roleCreator1); final RoleCreator roleCreator2 = new RoleCreator("managerB"); roleCreator2.setDisplayName("managerB"); final Role RoleB = getIdentityAPI().createRole(roleCreator2); final RoleCreator roleCreator3 = new RoleCreator("managerC"); roleCreator3.setDisplayName("managerC"); final Role RoleC = getIdentityAPI().createRole(roleCreator3); final RoleCreator roleCreator4 = new RoleCreator("managerD"); roleCreator4.setDisplayName("managerD"); final Role RoleD = getIdentityAPI().createRole(roleCreator4); final long roleAId = RoleA.getId(); final long roleBId = RoleB.getId(); final long roleCId = RoleC.getId(); final long roleDId = RoleD.getId(); final List roleIds = new ArrayList<>(); roleIds.add(roleAId); roleIds.add(roleBId); roleIds.add(roleCId); roleIds.add(roleDId); final Map roles = getIdentityAPI().getRoles(roleIds); assertNotNull(roles); assertEquals(4, roles.size()); assertEquals(RoleA, roles.get(roleAId)); assertEquals(RoleB, roles.get(roleBId)); assertEquals(RoleC, roles.get(roleCId)); assertEquals(RoleD, roles.get(roleDId)); getIdentityAPI().deleteRoles(roleIds); } @Test public void checkCreatedByForRole() throws BonitaException { final String manager = "manager"; final Role createdRole = getIdentityAPI().createRole(manager); // check createdBy for role assertNotNull(createdRole.getCreatedBy()); assertEquals(getSession().getUserId(), createdRole.getCreatedBy()); getIdentityAPI().deleteRole(createdRole.getId()); } @Test public void should_createRole_with_icon_creates_the_icon() throws Exception { //given Role aRole = getIdentityAPI().createRole(new RoleCreator("aRole").setIcon("main.png", new byte[] { 1, 2, 3 })); //when Icon icon = getIdentityAPI().getIcon(aRole.getIconId()); //then assertThat(icon).isEqualTo(new IconImpl(icon.getId(), "image/png", new byte[] { 1, 2, 3 })); //clean up deleteRoles(aRole); } @Test public void should_updateRole_with_new_icon_create_a_new_icon() throws Exception { //given Role aRole = getIdentityAPI().createRole(new RoleCreator("aRole").setIcon("main.png", new byte[] { 1, 2, 3 })); //when Role role = getIdentityAPI().updateRole(aRole.getId(), new RoleUpdater().setIcon("newIcon.jpg", new byte[] { 3, 4, 5 })); //then Icon icon = getIdentityAPI().getIcon(role.getIconId()); assertThat(icon.getId()).isNotEqualTo(aRole.getIconId()); assertThat(icon.getMimeType()).isEqualTo("image/jpeg"); assertThat(icon.getContent()).isEqualTo(new byte[] { 3, 4, 5 }); //clean up deleteRoles(aRole); } @Test(expected = NotFoundException.class) public void should_deleteRole_with_icon_delete_the_icon() throws Exception { //given Role aRole = getIdentityAPI().createRole(new RoleCreator("aRole").setIcon("main.png", new byte[] { 1, 2, 3 })); //when getIdentityAPI().deleteRole(aRole.getId()); //then getIdentityAPI().getIcon(aRole.getIconId()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/identity/UserIT.java ================================================ /** * 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.identity; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.api.PlatformAPI; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.impl.IconImpl; import org.bonitasoft.engine.platform.LoginException; import org.bonitasoft.engine.platform.NodeNotStartedException; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.PlatformSession; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class UserIT extends TestWithTechnicalUser { @Rule public ExpectedException expectedException = ExpectedException.none(); /** * This test is here for arbitrary reason: it has to be tested on ANY API call. */ @Test(expected = NodeNotStartedException.class) public void unableToCallPlatformMethodOnStoppedNode() throws Exception { logout(); PlatformSession session = loginOnPlatform(); PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(session); platformAPI.stopNode(); logoutOnPlatform(session); try { loginWithTechnicalUser(); } finally { session = loginOnPlatform(); platformAPI = PlatformAPIAccessor.getPlatformAPI(session); platformAPI.startNode(); logoutOnPlatform(session); loginWithTechnicalUser(); } } @Test(expected = CreationException.class) public void cannotCreateAUserWithAnEmptyPassword() throws BonitaException { final String userName = "matti"; final String password = ""; createUser(userName, password); } @Test(expected = CreationException.class) public void cannotCreateAUserWithAnEmptyUserName() throws BonitaException { final String userName = ""; final String password = "revontuli"; createUser(userName, password); } @Test(expected = CreationException.class) public void cannotCreateAUserWithANullPassword() throws BonitaException { final String userName = "matti"; createUser(userName, null); } @Test(expected = CreationException.class) public void cannotCreateAUserWithANullUserName() throws BonitaException { final String password = "revontuli"; createUser(null, password); } @Test public void createUserByUsernameAndPassword() throws BonitaException { getIdentityAPI().getNumberOfUsers(); final User userCreated = getIdentityAPI().createUser("bonitasoft", "123456"); assertNotNull(userCreated); assertEquals("bonitasoft", userCreated.getUserName()); final User user = getIdentityAPI().getUserByUserName("bonitasoft"); assertNotNull(user); getIdentityAPI().deleteUser("bonitasoft"); } @Test public void createEnabledUserByUsernameAndPassword() throws BonitaException { final User userCreated = getIdentityAPI().createUser("bonitasoft", "123456"); assertNotNull(userCreated); final User user = getIdentityAPI().getUserByUserName("bonitasoft"); assertNotNull(user); assertTrue(user.isEnabled()); getIdentityAPI().deleteUser("bonitasoft"); } @Test(expected = AlreadyExistsException.class) public void createUserByUsernameAndPasswordException() throws BonitaException { final User userCreated = getIdentityAPI().createUser("bonitasoft", "123456"); assertNotNull(userCreated); assertEquals("bonitasoft", userCreated.getUserName()); final User user = getIdentityAPI().getUserByUserName("bonitasoft"); assertNotNull(user); assertEquals("bonitasoft", userCreated.getUserName()); try { getIdentityAPI().createUser("bonitasoft", "123456"); } finally { getIdentityAPI().deleteUser("bonitasoft"); } } public void getFirstPageWithNoResult() { getIdentityAPI().getUsers(0, 10, UserCriterion.USER_NAME_ASC); } @Test(expected = CreationException.class) public void createUserFailed() throws BonitaException { getIdentityAPI().createUser(null, null); } @Test(expected = CreationException.class) public void createUserUsingNullUser() throws BonitaException { getIdentityAPI().createUser(null); } @Test public void createUserByAUser() throws BonitaException { final String userName = "spring"; final UserCreator creator = new UserCreator(userName, "bpm"); creator.setTitle("wwwwwwwwwwwwwwwwwwwwwwwwwwww"); final User user = getIdentityAPI().createUser(creator); assertNotNull(user); assertEquals(userName, user.getUserName()); getIdentityAPI().deleteUser(userName); } @Test public void createEnabledUserByAUser() throws BonitaException { final User user = getIdentityAPI().createUser("bonitasoft", "bpm"); assertNotNull(user); assertTrue(user.isEnabled()); getIdentityAPI().deleteUser("bonitasoft"); } @Test public void createDisabledUserByAUser() throws BonitaException { final UserCreator creator = new UserCreator("bonitasoft", "bpm"); creator.setEnabled(false); final User user = getIdentityAPI().createUser(creator); assertNotNull(user); assertFalse(user.isEnabled()); getIdentityAPI().deleteUser("bonitasoft"); } @Test(expected = CreationException.class) public void cannotCreateAUserWithANullUserNameUsingBuilder() throws BonitaException { getIdentityAPI().createUser(null, "revontuli"); } @Test(expected = CreationException.class) public void cannotCreateAUserWithANullPasswordUsingBuilder() throws BonitaException { getIdentityAPI().createUser("matti", null); } @Test(expected = CreationException.class) public void cannotCreateAUserWithAnEmptyUserNameUsingBuilder() throws BonitaException { getIdentityAPI().createUser("", "revontuli"); } @Test(expected = CreationException.class) public void cannotCreateAUserWithAnEmptyPasswordUsingBuilder() throws BonitaException { getIdentityAPI().createUser("matti", ""); } @Test(expected = UserNotFoundException.class) public void getUserByUsernameWithException() throws BonitaException { getIdentityAPI().getUserByUserName("Bonita"); } @Test public void getUserByUsername() throws BonitaException { getIdentityAPI().createUser("bonita", "password"); final User user = getIdentityAPI().getUserByUserName("bonita"); assertEquals("bonita", user.getUserName()); getIdentityAPI().deleteUser("bonita"); } @Test public void getNumberOfUsers() throws BonitaException { getIdentityAPI().createUser("jane", "bpm"); getIdentityAPI().createUser("paul", "bpm"); final long usersCount = getIdentityAPI().getNumberOfUsers(); assertEquals(2, usersCount); getIdentityAPI().deleteUser("jane"); getIdentityAPI().deleteUser("paul"); } @Test public void getUserById() throws BonitaException { final User userCreated = getIdentityAPI().createUser("zhang", "engine"); final User user = getIdentityAPI().getUser(userCreated.getId()); assertNotNull(user); assertEquals("zhang", user.getUserName()); getIdentityAPI().deleteUser("zhang"); } @Test(expected = UserNotFoundException.class) public void cannotGetTechUser() throws BonitaException { getIdentityAPI().getUser(-1); } @Test public void cannotGetTechUserInList() { final Map users = getIdentityAPI().getUsers(asList(-1L)); assertNull(users.get(-1)); } @Test(expected = UserNotFoundException.class) public void getUserByIDWithUserNotFoundException() throws BonitaException { final User userCreated = getIdentityAPI().createUser("zhang", "engine"); try { getIdentityAPI().getUser(userCreated.getId() + 100); } finally { getIdentityAPI().deleteUser("zhang"); } } @Test public void getUsersByIDs() throws BonitaException { final User userCreated1 = getIdentityAPI().createUser("zhang", "engine"); final User userCreated2 = getIdentityAPI().createUser("jmege", "engine"); final List userIds = new ArrayList<>(); userIds.add(userCreated1.getId()); userIds.add(userCreated2.getId()); final Map users = getIdentityAPI().getUsers(userIds); assertNotNull(users); assertEquals(2, users.size()); assertEquals("zhang", users.get(userCreated1.getId()).getUserName()); assertEquals("jmege", users.get(userCreated2.getId()).getUserName()); deleteUsers(userCreated1, userCreated2); } public void getUsersByIDsWithoutUserNotFoundException() throws BonitaException { final User userCreated1 = getIdentityAPI().createUser("zhang", "engine"); final User userCreated2 = getIdentityAPI().createUser("jmege", "engine"); final List userIds = new ArrayList<>(); userIds.add(userCreated1.getId()); userIds.add(userCreated2.getId() + 100); final Map users = getIdentityAPI().getUsers(userIds); assertNotNull(users); assertEquals(1, users.size()); assertEquals("zhang", users.get(userCreated1.getId()).getUserName()); deleteUsers(userCreated1, userCreated2); } @Test public void deleteUserByUserName() throws BonitaException { getIdentityAPI().createUser("testDelete", "engine"); getIdentityAPI().deleteUser("testDelete"); assertEquals(0, getIdentityAPI().getNumberOfUsers()); } @Test public void deleteNonExistingUserByUserName() throws BonitaException { final String userName = "testDelete"; getIdentityAPI().createUser(userName, "engine"); getIdentityAPI().deleteUser(userName); getIdentityAPI().deleteUser(userName); } @Test(expected = DeletionException.class) public void deleteUserByUserNameWithUserDeletionException() throws BonitaException { getIdentityAPI().deleteUser(null); } @Test public void deleteUser() throws BonitaException { final User user = getIdentityAPI().createUser("testDelete", "engine"); getIdentityAPI().deleteUser(user.getId()); assertEquals(0, getIdentityAPI().getNumberOfUsers()); } @Test public void testUserContactInfos() throws BonitaException { final UserCreator creator = new UserCreator("john", "bpm"); creator.setFirstName("John").setLastName("Doe"); final ContactDataCreator persoCreator = new ContactDataCreator(); // BS-7711 persoCreator.setEmail("anemailwithmorethanfifteencharacter@extremellylongdomainname.com"); final ContactDataCreator proCreator = new ContactDataCreator(); proCreator.setEmail("john.doe@bonitasoft.com"); creator.setPersonalContactData(persoCreator); creator.setProfessionalContactData(proCreator); final User john = getIdentityAPI().createUser(creator); final ContactData persoData = getIdentityAPI().getUserContactData(john.getId(), true); assertEquals("anemailwithmorethanfifteencharacter@extremellylongdomainname.com", persoData.getEmail()); final ContactData prooData = getIdentityAPI().getUserContactData(john.getId(), false); assertEquals("john.doe@bonitasoft.com", prooData.getEmail()); getIdentityAPI().deleteUser(john.getId()); assertEquals(0, getIdentityAPI().getNumberOfUsers()); assertNull(getIdentityAPI().getUserContactData(john.getId(), true)); } @Test public void deleteNonExistingUser() throws BonitaException { final User user = getIdentityAPI().createUser("testDelete", "engine"); getIdentityAPI().deleteUser(user.getId()); getIdentityAPI().deleteUser(user.getId()); } @Test public void deleteUsers() throws BonitaException { final List userIds = new ArrayList<>(); userIds.add(getIdentityAPI().createUser("user1", "engine").getId()); userIds.add(getIdentityAPI().createUser("user2", "engine").getId()); userIds.add(getIdentityAPI().createUser("user3", "engine").getId()); userIds.add(getIdentityAPI().createUser("user4", "engine").getId()); userIds.add(getIdentityAPI().createUser("user5", "engine").getId()); getIdentityAPI().deleteUsers(userIds); assertEquals(0, getIdentityAPI().getNumberOfUsers()); } @Test public void deleteNonExistingUsers() throws BonitaException { getIdentityAPI().deleteUsers(null); } @Test public void deleteUsersDeleteAllExistingOnesAndIgnoresOthers() throws BonitaException { final List userIds = new ArrayList<>(); userIds.add(getIdentityAPI().createUser("user1", "engine").getId()); userIds.add(getIdentityAPI().createUser("user2", "engine").getId()); userIds.add(152458L); getIdentityAPI().deleteUsers(userIds); assertEquals(0, getIdentityAPI().getNumberOfUsers()); } @Test public void updateUser() throws BonitaException { final String james = "james"; final User user = getIdentityAPI().createUser(james, "mbp"); final UserUpdater updateDescriptor = buildUserUpdaterWithProAndPersoContact(); final User updatedUser = getIdentityAPI().updateUser(user.getId(), updateDescriptor); checkUser(updatedUser); final ContactData persoData = getIdentityAPI().getUserContactData(updatedUser.getId(), true); checkPersoUserContactData(persoData); final ContactData proData = getIdentityAPI().getUserContactData(updatedUser.getId(), false); checkProUserContactData(proData); getIdentityAPI().deleteUser(updatedUser.getId()); } private UserUpdater buildUserUpdater() { final UserUpdater updateDescriptor = new UserUpdater(); updateDescriptor.setFirstName("Changed first name"); updateDescriptor.setIconName("new icon name"); updateDescriptor.setIconPath("new_icon_path"); updateDescriptor.setJobTitle("New job title"); updateDescriptor.setLastName("Modified Last name"); updateDescriptor.setManagerId(12354L); updateDescriptor.setPassword("Ch4n63D_P455W0RD"); updateDescriptor.setUserName("new_user_name"); updateDescriptor.setTitle("titre"); return updateDescriptor; } private void checkUser(final User updatedUser) { assertNotNull(updatedUser); assertEquals("new_user_name", updatedUser.getUserName()); assertEquals("Changed first name", updatedUser.getFirstName()); assertEquals("New job title", updatedUser.getJobTitle()); assertEquals("Modified Last name", updatedUser.getLastName()); assertEquals(12354L, updatedUser.getManagerUserId()); assertEquals("titre", updatedUser.getTitle()); } private UserUpdater buildUserUpdaterWithProContact() { final ContactDataUpdater proDataUpDescr = buildProContactDataUpdater(); final UserUpdater updateDescriptor = buildUserUpdater(); updateDescriptor.setProfessionalContactData(proDataUpDescr); return updateDescriptor; } private UserUpdater buildUserUpdaterWithProAndPersoContact() { final ContactDataUpdater proDataUpDescr = buildProContactDataUpdater(); final ContactDataUpdater persoDataUpDescr = buildPersoContactDataUpdater(); final UserUpdater updateDescriptor = buildUserUpdater(); updateDescriptor.setProfessionalContactData(proDataUpDescr); updateDescriptor.setPersonalContactData(persoDataUpDescr); return updateDescriptor; } private ContactDataUpdater buildProContactDataUpdater() { final ContactDataUpdater proDataUpDescr = new ContactDataUpdater(); proDataUpDescr.setAddress("34 Gustave Eiffel"); proDataUpDescr.setBuilding("BigBlock"); proDataUpDescr.setCity("L.A."); proDataUpDescr.setCountry("Spain"); proDataUpDescr.setEmail("noreply@bonitasoft.com"); proDataUpDescr.setFaxNumber("01-356743254"); proDataUpDescr.setMobileNumber("06-02-1111111"); proDataUpDescr.setPhoneNumber("04-76-111111"); proDataUpDescr.setRoom("A304"); proDataUpDescr.setState("Isere"); proDataUpDescr.setWebsite("http://www.ofelia.com"); proDataUpDescr.setZipCode("38000"); return proDataUpDescr; } private void checkProUserContactData(final ContactData proData) { assertEquals("34 Gustave Eiffel", proData.getAddress()); assertEquals("BigBlock", proData.getBuilding()); assertEquals("L.A.", proData.getCity()); assertEquals("Spain", proData.getCountry()); assertEquals("noreply@bonitasoft.com", proData.getEmail()); assertEquals("01-356743254", proData.getFaxNumber()); assertEquals("06-02-1111111", proData.getMobileNumber()); assertEquals("04-76-111111", proData.getPhoneNumber()); assertEquals("A304", proData.getRoom()); assertEquals("Isere", proData.getState()); assertEquals("http://www.ofelia.com", proData.getWebsite()); assertEquals("38000", proData.getZipCode()); } private ContactDataUpdater buildPersoContactDataUpdater() { final ContactDataUpdater persoDataUpDescr = new ContactDataUpdater(); persoDataUpDescr.setAddress("3 rue des lilas"); persoDataUpDescr.setBuilding("SkyScrapper"); persoDataUpDescr.setCity("Lyon"); persoDataUpDescr.setCountry("Lichtenstein"); persoDataUpDescr.setEmail("noreply@yahoo.es"); persoDataUpDescr.setFaxNumber("01-020013021452"); persoDataUpDescr.setMobileNumber("06-02-000000"); persoDataUpDescr.setPhoneNumber("04-76-000000"); persoDataUpDescr.setRoom("Home"); persoDataUpDescr.setState("Rhône"); persoDataUpDescr.setWebsite("http://perso.bonitasoft.com"); persoDataUpDescr.setZipCode("69000"); return persoDataUpDescr; } private void checkPersoUserContactData(final ContactData persoData) { assertEquals("3 rue des lilas", persoData.getAddress()); assertEquals("SkyScrapper", persoData.getBuilding()); assertEquals("Lyon", persoData.getCity()); assertEquals("Lichtenstein", persoData.getCountry()); assertEquals("noreply@yahoo.es", persoData.getEmail()); assertEquals("01-020013021452", persoData.getFaxNumber()); assertEquals("06-02-000000", persoData.getMobileNumber()); assertEquals("04-76-000000", persoData.getPhoneNumber()); assertEquals("Home", persoData.getRoom()); assertEquals("Rhône", persoData.getState()); assertEquals("http://perso.bonitasoft.com", persoData.getWebsite()); assertEquals("69000", persoData.getZipCode()); } @Test public void updateUserToBeEnabled() throws BonitaException { // Create user, and updateDescriptor final User user = getIdentityAPI().createUser("bonitasoft", "123456"); assertNotNull(user); assertTrue(user.isEnabled()); final UserUpdater updateDescriptor = new UserUpdater(); updateDescriptor.setEnabled(true); // Update user final User updatedUser = getIdentityAPI().updateUser(user.getId(), updateDescriptor); assertNotNull(updatedUser); assertTrue(updatedUser.isEnabled()); // Clean getIdentityAPI().deleteUser("bonitasoft"); } @Test public void updateUserToBeDisabled() throws BonitaException { // Create user, and updateDescriptor final User user = getIdentityAPI().createUser("bonitasoft", "123456"); assertNotNull(user); assertTrue(user.isEnabled()); final UserUpdater updateDescriptor = new UserUpdater(); updateDescriptor.setEnabled(false); // Update user final User updatedUser = getIdentityAPI().updateUser(user.getId(), updateDescriptor); assertFalse(updatedUser.isEnabled()); // Clean getIdentityAPI().deleteUser("bonitasoft"); } @Test public void updateUserManager() throws BonitaException { final User matti = getIdentityAPI().createUser("matti", "bpm"); final User james = getIdentityAPI().createUser("james", "bpm"); assertEquals(0, james.getManagerUserId()); final UserUpdater updateDescriptor = new UserUpdater(); updateDescriptor.setManagerId(matti.getId()); final User updatedUser = getIdentityAPI().updateUser(james.getId(), updateDescriptor); assertNotNull(updatedUser); assertEquals(matti.getId(), updatedUser.getManagerUserId()); deleteUsers(james, matti); } @Test(expected = UserNotFoundException.class) public void updateUserWithUserNotFoundException() throws BonitaException { final UserUpdater updateDescriptor = new UserUpdater(); updateDescriptor.setUserName("john"); updateDescriptor.setPassword("bpm"); final int userId = 100; getIdentityAPI().updateUser(userId, updateDescriptor); } @Test(expected = UpdateException.class) public void updateUserWithUserUpdateException() throws BonitaException { final User oldUser = getIdentityAPI().createUser("old", "oldPassword"); try { getIdentityAPI().updateUser(oldUser.getId(), null); } finally { getIdentityAPI().deleteUser(oldUser.getId()); } } @Test public void updateUserWithOnlyDataChanging() throws BonitaException { final User user = getIdentityAPI().createUser("james", "mbp"); final ContactDataUpdater persoDataUpDescr = buildPersoContactDataUpdater(); final ContactDataUpdater proDataUpDescr = buildProContactDataUpdater(); final UserUpdater updater = new UserUpdater(); updater.setPersonalContactData(persoDataUpDescr); updater.setProfessionalContactData(proDataUpDescr); final User updatedUser = getIdentityAPI().updateUser(user.getId(), updater); assertNotNull(updatedUser); final ContactData persoData = getIdentityAPI().getUserContactData(updatedUser.getId(), true); checkPersoUserContactData(persoData); final ContactData proData = getIdentityAPI().getUserContactData(updatedUser.getId(), false); checkProUserContactData(proData); getIdentityAPI().deleteUser(updatedUser.getId()); } @Test public void getPaginatedUsersWithUserCriterion() throws BonitaException { final User user1 = getIdentityAPI().createUser("auser1o", "bpm", "a", "a"); final User user2 = getIdentityAPI().createUser("cuser2o", "bpm", "c", "c"); final User user3 = getIdentityAPI().createUser("buser3o", "bpm", "b", "b"); final User user4 = getIdentityAPI().createUser("euser4o", "bpm", "e", "e"); final User user5 = getIdentityAPI().createUser("duser5o", "bpm", "d", "d"); List usersASC = getIdentityAPI().getUsers(0, 3, UserCriterion.USER_NAME_ASC); assertEquals(3, usersASC.size()); assertEquals("auser1o", usersASC.get(0).getUserName()); assertEquals("buser3o", usersASC.get(1).getUserName()); assertEquals("cuser2o", usersASC.get(2).getUserName()); usersASC = getIdentityAPI().getUsers(3, 3, UserCriterion.USER_NAME_ASC); assertEquals(2, usersASC.size()); assertEquals("duser5o", usersASC.get(0).getUserName()); assertEquals("euser4o", usersASC.get(1).getUserName()); final List users = getIdentityAPI().getUsers(6, 3, UserCriterion.USER_NAME_ASC); assertTrue(users.isEmpty()); final List usersDESC = getIdentityAPI().getUsers(0, 3, UserCriterion.USER_NAME_DESC); assertEquals(3, usersDESC.size()); assertEquals("euser4o", usersDESC.get(0).getUserName()); assertEquals("duser5o", usersDESC.get(1).getUserName()); assertEquals("cuser2o", usersDESC.get(2).getUserName()); final List usersFirstNameASC = getIdentityAPI().getUsers(0, 3, UserCriterion.FIRST_NAME_ASC); assertEquals(3, usersFirstNameASC.size()); assertEquals("a", usersFirstNameASC.get(0).getFirstName()); assertEquals("b", usersFirstNameASC.get(1).getFirstName()); assertEquals("c", usersFirstNameASC.get(2).getFirstName()); final List usersFirstNameDESC = getIdentityAPI().getUsers(0, 3, UserCriterion.FIRST_NAME_DESC); assertEquals(3, usersFirstNameDESC.size()); assertEquals("e", usersFirstNameDESC.get(0).getFirstName()); assertEquals("d", usersFirstNameDESC.get(1).getFirstName()); assertEquals("c", usersFirstNameDESC.get(2).getFirstName()); final List usersLastNameASC = getIdentityAPI().getUsers(0, 3, UserCriterion.LAST_NAME_ASC); assertEquals(3, usersLastNameASC.size()); assertEquals("a", usersLastNameASC.get(0).getLastName()); assertEquals("b", usersLastNameASC.get(1).getLastName()); assertEquals("c", usersLastNameASC.get(2).getLastName()); final List usersLastNameDESC = getIdentityAPI().getUsers(0, 3, UserCriterion.LAST_NAME_DESC); assertEquals(3, usersLastNameDESC.size()); assertEquals("e", usersLastNameDESC.get(0).getLastName()); assertEquals("d", usersLastNameDESC.get(1).getLastName()); assertEquals("c", usersLastNameDESC.get(2).getLastName()); deleteUsers(user1, user2, user3, user4, user5); } @Test(expected = AlreadyExistsException.class) public void cannotCreateTwoUserWithTheSameUserName() throws BonitaException { final String username = "install"; final User user1 = getIdentityAPI().createUser(username, "bpm"); try { getIdentityAPI().createUser(username, "bos"); } finally { getIdentityAPI().deleteUser(user1.getId()); } } @Test(expected = SearchException.class) public void searchUserWithWrongSortKey() throws BonitaException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(UserSearchDescriptor.FIRST_NAME, "Jean-Gustave"); builder.sort("WRONG_SORT_KEY", Order.ASC); getIdentityAPI().searchUsers(builder.done()); } @Test public void searchUser() throws BonitaException { final List users = new ArrayList<>(); users.add(getIdentityAPI().createUser("jgrGF[|00", "bpm", "John", "Taylor")); users.add(getIdentityAPI().createUser("45èDG'fgb", "bpm", "Jack", "Jack")); users.add(getIdentityAPI().createUser("à\"(èg", "bpm", "John", "Smith")); users.add(getIdentityAPI().createUser("^^jhg", "bpm", "Paul", "Taylor")); users.add(getIdentityAPI().createUser("مرحبا!", "bpm", "Jack", "Jack")); users.add(getIdentityAPI().createUser("user02", "bpm", "Pierre", "Smith")); users.add(getIdentityAPI().createUser("User00", "bpm", "Marie", "Taylor")); // Filter and order test SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(UserSearchDescriptor.FIRST_NAME, "John"); builder.sort(UserSearchDescriptor.LAST_NAME, Order.ASC); final SearchResult searchUsers = getIdentityAPI().searchUsers(builder.done()); assertNotNull(searchUsers); assertEquals(2, searchUsers.getCount()); List usersResult = searchUsers.getResult(); assertEquals(users.get(2), usersResult.get(0)); assertEquals(users.get(0), usersResult.get(1)); // Pagination test builder = new SearchOptionsBuilder(0, 10); builder.sort(UserSearchDescriptor.LAST_NAME, Order.ASC); final List allUsers = getIdentityAPI().searchUsers(builder.done()).getResult(); assertEquals(7, allUsers.size()); builder = new SearchOptionsBuilder(0, 5); builder.sort(UserSearchDescriptor.LAST_NAME, Order.ASC); usersResult = getIdentityAPI().searchUsers(builder.done()).getResult(); assertEquals(5, usersResult.size()); for (int i = 0; i < 5; i++) { assertEquals(allUsers.get(i), usersResult.get(i)); } builder = new SearchOptionsBuilder(5, 10); builder.sort(UserSearchDescriptor.LAST_NAME, Order.ASC); usersResult = getIdentityAPI().searchUsers(builder.done()).getResult(); assertEquals(2, usersResult.size()); for (int i = 0; i < 2; i++) { assertEquals(allUsers.get(i + 5), usersResult.get(i)); } deleteUsers(users); } @Test public void searchUserSortedById() throws BonitaException { final List users = new ArrayList<>(); users.add(getIdentityAPI().createUser("jgrGF[|00", "bpm", "John", "Taylor")); users.add(getIdentityAPI().createUser("user02", "bpm", "Pierre", "Smith")); users.add(getIdentityAPI().createUser("User00", "bpm", "Marie", "Taylor")); // Search ordered by id SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(UserSearchDescriptor.ID, Order.ASC); List usersResult = getIdentityAPI().searchUsers(builder.done()).getResult(); assertEquals(3, usersResult.size()); assertThat(usersResult.get(0).getId()).isLessThan(usersResult.get(1).getId()); assertThat(usersResult.get(1).getId()).isLessThan(usersResult.get(2).getId()); deleteUsers(users); } /* * Relates to https://bonitasoft.atlassian.net/browse/RUNTIME-590 * All search should be insensitive */ @Test public void should_search_user_case_insensitively() throws BonitaException { List users = asList( getIdentityAPI().createUser("Jean_Michel", "bpm", "Jean Michel", "Jarre"), getIdentityAPI().createUser("michel.mimi", "bpm", "Michel", "Mimi")); assertThat(getIdentityAPI().searchUsers( new SearchOptionsBuilder(0, 10).searchTerm("jean").sort(UserSearchDescriptor.ID, Order.ASC).done()) .getResult()) .hasSize(1).allMatch(user -> user.getUserName().equals("Jean_Michel")); assertThat(getIdentityAPI().searchUsers( new SearchOptionsBuilder(0, 10).searchTerm("Jean").sort(UserSearchDescriptor.ID, Order.ASC).done()) .getResult()) .hasSize(1).allMatch(user -> user.getUserName().equals("Jean_Michel")); deleteUsers(users); } @Test public void searchEnabledDisabledUsers() throws BonitaException { // Create users final User john = getIdentityAPI().createUser("john002", "bpm", "John", "Taylor"); final User jack = getIdentityAPI().createUser("jack001", "bpm", "Jack", "Doe"); // Disabled jack final UserUpdater updateDescriptor = new UserUpdater(); updateDescriptor.setEnabled(false); final User updatedJack = getIdentityAPI().updateUser(jack.getId(), updateDescriptor); // Search enabled users SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(UserSearchDescriptor.ENABLED, true); SearchResult searchUsers = getIdentityAPI().searchUsers(builder.done()); assertNotNull(searchUsers); assertEquals(1, searchUsers.getCount()); List users = searchUsers.getResult(); assertEquals(john, users.get(0)); // Search disabled users builder = new SearchOptionsBuilder(0, 10); builder.filter(UserSearchDescriptor.ENABLED, false); searchUsers = getIdentityAPI().searchUsers(builder.done()); assertNotNull(searchUsers); assertEquals(1, searchUsers.getCount()); users = searchUsers.getResult(); assertEquals(updatedJack, users.get(0)); // Clean up deleteUsers(john, jack); } @Test public void searchUserUsingTerm() throws BonitaException { final User john2 = getIdentityAPI().createUser("john002", "bpm", "John", "Taylor"); final User jack = getIdentityAPI().createUser("jack001", "bpm", "Jack", "Doe"); final User john1 = getIdentityAPI().createUser("john001", "bpm", "John", "Smith"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.searchTerm("Jo"); builder.sort(UserSearchDescriptor.LAST_NAME, Order.ASC); final SearchResult searchUsers = getIdentityAPI().searchUsers(builder.done()); assertNotNull(searchUsers); assertEquals(2, searchUsers.getCount()); final List users = searchUsers.getResult(); assertEquals(john1, users.get(0)); assertEquals(john2, users.get(1)); deleteUsers(john1, john2, jack); } @Test public void searchUserWithApostrophe() throws BonitaException { final User user1 = getIdentityAPI().createUser("'john'002", "bpm", "John", "A"); final User user3 = getIdentityAPI().createUser("c", "bpm", "'john'n", "C"); final User user4 = getIdentityAPI().createUser("d", "bpm", "'d", "John'"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.searchTerm("'"); builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC); final SearchResult searchUsers = getIdentityAPI().searchUsers(builder.done()); assertNotNull(searchUsers); assertEquals(3, searchUsers.getCount()); final List users = searchUsers.getResult(); assertEquals(user4, users.get(0)); assertEquals(user3, users.get(1)); assertEquals(user1, users.get(2)); deleteUsers(user1, user3, user4); } @Test public void searchTermWithSpecialChars() throws BonitaException { List users = asList( getIdentityAPI().createUser("Sébastien", "ENCRYPTED", "Sébation", "Martin"), getIdentityAPI().createUser("房子", "ENCRYPTED", "\uD83D\uDC7A", "龜")); assertThat(getIdentityAPI().searchUsers(new SearchOptionsBuilder(0, 10).searchTerm("Séba").done()).getResult()) .hasSize(1) .allMatch(user -> user.getUserName().equals("Sébastien")); assertThat(getIdentityAPI().searchUsers(new SearchOptionsBuilder(0, 10).searchTerm("龜").done()).getResult()) .hasSize(1) .allMatch(user -> user.getUserName().equals("房子")); getIdentityAPI().deleteUsers(users.stream().map(User::getId).collect(Collectors.toList())); } @Test public void searchUsersInGroup() throws BonitaException { final User john1 = createUser("john001", "bpm", "John", "Smith"); final User jack = createUser("jack001", "bpm", "Jack", "Doe"); final User john2 = createUser("john002", "bpm", "John", "Taylor"); final Group group = createGroup("group1"); final Role role = createRole("manager"); getIdentityAPI().addUserMemberships(asList(john1.getId(), jack.getId()), group.getId(), role.getId()); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(UserSearchDescriptor.GROUP_ID, (Long) (group.getId())); builder.sort(UserSearchDescriptor.USER_NAME, Order.DESC); final SearchResult searchUsers = getIdentityAPI().searchUsers(builder.done()); assertNotNull(searchUsers); assertEquals(2, searchUsers.getCount()); final List users = searchUsers.getResult(); assertEquals(john1, users.get(0)); assertEquals(jack, users.get(1)); getIdentityAPI().deleteGroup(group.getId()); getIdentityAPI().deleteRole(role.getId()); deleteUsers(john1, john2, jack); } @Test public void searchUsersInRole() throws BonitaException { final User john1 = createUser("john001", "bpm", "John", "Smith"); final User jack = createUser("jack001", "bpm", "Jack", "Doe"); final User john2 = createUser("john002", "bpm", "John", "Taylor"); final Group group = createGroup("group1"); final Role role = createRole("manager"); getIdentityAPI().addUserMemberships(asList(john1.getId(), jack.getId()), group.getId(), role.getId()); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(UserSearchDescriptor.ROLE_ID, (Long) (role.getId())); builder.sort(UserSearchDescriptor.USER_NAME, Order.ASC); final SearchResult searchUsers = getIdentityAPI().searchUsers(builder.done()); assertNotNull(searchUsers); assertEquals(2, searchUsers.getCount()); final List users = searchUsers.getResult(); assertEquals(jack, users.get(0)); assertEquals(john1, users.get(1)); getIdentityAPI().deleteGroup(group.getId()); getIdentityAPI().deleteRole(role.getId()); deleteUsers(john1, john2, jack); } @Test public void searchUsersInRoleAndGroup() throws BonitaException { final User john1 = createUser("john001", "bpm", "John", "Smith"); final User jack = createUser("jack001", "bpm", "Jack", "Doe"); final User john2 = createUser("john002", "bpm", "John", "Taylor"); final Group group1 = createGroup("group1"); final Group group2 = createGroup("group2"); final Role role1 = createRole("manager"); final Role role2 = createRole("delivery"); getIdentityAPI().addUserMemberships(asList(john1.getId(), jack.getId()), group1.getId(), role1.getId()); getIdentityAPI().addUserMemberships(asList(john2.getId()), group2.getId(), role1.getId()); getIdentityAPI().addUserMemberships(asList(john2.getId(), jack.getId()), group1.getId(), role2.getId()); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(UserSearchDescriptor.GROUP_ID, group2.getId()); builder.filter(UserSearchDescriptor.ROLE_ID, role1.getId()); builder.sort(UserSearchDescriptor.USER_NAME, Order.ASC); final SearchResult searchUsers = getIdentityAPI().searchUsers(builder.done()); assertNotNull(searchUsers); assertEquals(1, searchUsers.getCount()); final List users = searchUsers.getResult(); assertEquals(john2, users.get(0)); deleteGroups(group1, group2); deleteRoles(role1, role2); deleteUsers(john1, john2, jack); } @Test public void searchTeamMembers() throws BonitaException { final User manager = getIdentityAPI().createUser("john002", "bpm", "John", "Taylor"); final UserCreator creator = new UserCreator("jack001", "bpm"); creator.setFirstName("Jack").setLastName("Doe").setManagerUserId(manager.getId()); final User jack = getIdentityAPI().createUser(creator); final User john = getIdentityAPI().createUser("john001", "bpm", "John", "Smith"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(UserSearchDescriptor.MANAGER_USER_ID, (Long) manager.getId()); final SearchResult searchUsers = getIdentityAPI().searchUsers(builder.done()); assertNotNull(searchUsers); assertEquals(1, searchUsers.getCount()); final List users = searchUsers.getResult(); assertEquals(jack, users.get(0)); deleteUsers(john, manager, jack); } @Test public void searchUsersNotInTeam() throws BonitaException { // Manager final User manager = getIdentityAPI().createUser("john002", "bpm", "John", "Taylor"); // User with manager final UserCreator creator = new UserCreator("jack001", "bpm"); creator.setFirstName("Jack").setLastName("Doe").setManagerUserId(manager.getId()); final User jack = getIdentityAPI().createUser(creator); // User without manager final User john = getIdentityAPI().createUser("john003", "bpm", "John", "Smith"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.differentFrom(UserSearchDescriptor.MANAGER_USER_ID, manager.getId()) .differentFrom(UserSearchDescriptor.USER_NAME, manager.getUserName()); final SearchResult searchUsers = getIdentityAPI().searchUsers(builder.done()); assertNotNull(searchUsers); assertEquals(1, searchUsers.getCount()); final List users = searchUsers.getResult(); assertEquals(john, users.get(0)); deleteUsers(john, manager, jack); } @Test public void getUsersFromIdsShouldReturnUsersInTheRightOrder() throws BonitaException { final List expectedUsers = new ArrayList<>(); expectedUsers.add(getIdentityAPI().createUser("zhao", "engine")); expectedUsers.add(getIdentityAPI().createUser("qian", "engine")); expectedUsers.add(getIdentityAPI().createUser("sun", "engine")); expectedUsers.add(getIdentityAPI().createUser("li", "engine")); expectedUsers.add(getIdentityAPI().createUser("zhou", "engine")); final List userIds = new ArrayList<>(5); userIds.add(expectedUsers.get(4).getId()); userIds.add(expectedUsers.get(0).getId()); userIds.add(expectedUsers.get(2).getId()); userIds.add(expectedUsers.get(1).getId()); userIds.add(expectedUsers.get(3).getId()); final Map usersResult = getIdentityAPI().getUsers(userIds); assertNotNull(usersResult); assertEquals(5, usersResult.size()); assertEquals(expectedUsers.get(0), usersResult.get(expectedUsers.get(0).getId())); assertEquals(expectedUsers.get(1), usersResult.get(expectedUsers.get(1).getId())); assertEquals(expectedUsers.get(2), usersResult.get(expectedUsers.get(2).getId())); assertEquals(expectedUsers.get(3), usersResult.get(expectedUsers.get(3).getId())); assertEquals(expectedUsers.get(4), usersResult.get(expectedUsers.get(4).getId())); deleteUsers(expectedUsers); } @Test public void checkCreatedByForCreatedUser() throws BonitaException { final User userCreated = getIdentityAPI().createUser("bonitasoft", "123456"); assertNotNull(userCreated); assertEquals("bonitasoft", userCreated.getUserName()); final User user = getIdentityAPI().getUserByUserName("bonitasoft"); assertNotNull(user); assertNotNull(user.getCreatedBy()); assertEquals(getSession().getUserId(), user.getCreatedBy()); getIdentityAPI().deleteUser("bonitasoft"); } @Test public void getUserIds() throws BonitaException { final User matti = getIdentityAPI().createUser("matti", "bpm"); final User jani = getIdentityAPI().createUser("jani", "bpm"); final List userNames = new ArrayList<>(3); userNames.add("jani"); userNames.add("liisa"); userNames.add("matti"); final Map userIds = getIdentityAPI().getUsersByUsernames(userNames); assertEquals(2, userIds.size()); assertEquals(jani, userIds.get("jani")); assertEquals(matti, userIds.get("matti")); deleteUsers(matti, jani); } @Test public void getUserWithProContactData() throws BonitaException { final String james = "james"; final User user = getIdentityAPI().createUser(james, "mbp"); final UserUpdater updateDescriptor = buildUserUpdaterWithProContact(); final User updatedUser = getIdentityAPI().updateUser(user.getId(), updateDescriptor); final ContactData userContactData = getIdentityAPI().getUserContactData(updatedUser.getId(), false); final UserWithContactData proUser = getIdentityAPI().getUserWithProfessionalDetails(updatedUser.getId()); assertEquals(userContactData, proUser.getContactData()); assertEquals(updatedUser, proUser.getUser()); getIdentityAPI().deleteUser(updatedUser.getId()); } @Test public void getUserWithoutProContactData() throws BonitaException { final String james = "james"; final User user = getIdentityAPI().createUser(james, "mbp"); final UserUpdater updateDescriptor = buildUserUpdater(); final User updatedUser = getIdentityAPI().updateUser(user.getId(), updateDescriptor); final UserWithContactData proUser = getIdentityAPI().getUserWithProfessionalDetails(updatedUser.getId()); assertNull(proUser.getContactData()); assertEquals(updatedUser, proUser.getUser()); getIdentityAPI().deleteUser(updatedUser.getId()); } @Test(expected = UserNotFoundException.class) public void throwExceptionWhenGettingUnknownUserWithProContactData() throws BonitaException { getIdentityAPI().getUserWithProfessionalDetails(-45L); } @Test(expected = UserNotFoundException.class) public void throwExceptionWhenGettingTechnicalUserWithProContactData() throws BonitaException { getIdentityAPI().getUserWithProfessionalDetails(-1L); } @Test public void can_create_user_with_255_char_in_fields() throws Exception { final UserCreator creator = new UserCreator(completeWithZeros("user"), "bpm"); creator.setJobTitle(completeWithZeros("Engineer")); creator.setFirstName(completeWithZeros("First")); creator.setLastName(completeWithZeros("Last")); final ContactDataCreator contactDataCreator = new ContactDataCreator(); contactDataCreator.setAddress(completeWithZeros("32 Rue Gustave Eiffel")); creator.setProfessionalContactData(contactDataCreator); //when final User user = getIdentityAPI().createUser(creator); //then assertThat(user).isNotNull(); assertThat(user.getUserName()).hasSize(255); assertThat(user.getFirstName()).hasSize(255); assertThat(user.getLastName()).hasSize(255); assertThat(user.getJobTitle()).hasSize(255); //when final UserWithContactData userWithContactData = getIdentityAPI().getUserWithProfessionalDetails(user.getId()); //then assertThat(userWithContactData).isNotNull(); assertThat(userWithContactData.getContactData().getAddress()).hasSize(255); //clean getIdentityAPI().deleteUser(user.getId()); } private String completeWithZeros(final String prefix) { return prefix + "0".repeat(Math.max(0, 255 - prefix.length())); } @Test public void login_should_work_with_specific_characters() throws Exception { final User jyri = createUser("Jyri", "1234"); final UserUpdater updater = new UserUpdater(); updater.setPassword("ñ1234"); getIdentityAPI().updateUser(jyri.getId(), updater); loginOnDefaultTenantWith("Jyri", "ñ1234"); assertThat(getApiClient().getSession()).isNotNull(); getIdentityAPI().deleteUser(jyri.getId()); } @Test public void theTest() throws Exception { loginWithTechnicalUser(); User john = createUser("john", "bpm"); User jack = createUser("jack", "bpm"); Date connection1 = getIdentityAPI().getUserByUserName("john").getLastConnection(); Thread.sleep(20); logout(); loginOnDefaultTenantWith("john", "bpm"); Date connection2 = getIdentityAPI().getUserByUserName("john").getLastConnection(); getIdentityAPI().getUserByUserName("john").getLastConnection(); Thread.sleep(20); logout(); loginOnDefaultTenantWith("john", "bpm"); Date connection3 = getIdentityAPI().getUserByUserName("john").getLastConnection(); Thread.sleep(20); logout(); loginOnDefaultTenantWith("jack", "bpm"); Date connection4 = getIdentityAPI().getUserByUserName("john").getLastConnection(); logout(); loginWithTechnicalUser(); deleteUsers(john, jack); assertThat(connection1).isNull(); assertThat(connection2).isBefore(connection3); assertThat(connection3).isEqualTo(connection4); } @Test public void should_create_user_with_icon_create_the_icon() throws Exception { //given User user = getIdentityAPI().createUser( new UserCreator("userWithIcon", "thePassword").setIcon("myAvatar.jpg", "avatarContent".getBytes())); //when Icon icon = getIdentityAPI().getIcon(user.getIconId()); //then assertThat(icon).isEqualTo(new IconImpl(icon.getId(), "image/jpeg", "avatarContent".getBytes())); //cleanup getIdentityAPI().deleteUser("userWithIcon"); } @Test public void should_delete_user_delete_the_icon() throws Exception { //given User user = getIdentityAPI().createUser( new UserCreator("userWithIcon", "thePassword").setIcon("myAvatar.jpg", "avatarContent".getBytes())); //when getIdentityAPI().deleteUser(user.getId()); //then expectedException.expect(NotFoundException.class); expectedException.expectMessage("unable to find icon with id " + user.getIconId()); try { getIdentityAPI().getIcon(user.getIconId()); } finally { getIdentityAPI().deleteUser("userWithIcon"); } } @Test public void should_update_user_create_a_new_icon() throws Exception { //given User user = getIdentityAPI().createUser(new UserCreator("userWithIcon", "thePassword")); //when User updatedUser = getIdentityAPI().updateUser(user.getId(), new UserUpdater().setIcon("myFile.png", "content".getBytes())); //then Icon icon = getIdentityAPI().getIcon(updatedUser.getIconId()); assertThat(icon).isEqualTo(new IconImpl(icon.getId(), "image/png", "content".getBytes())); //cleanup getIdentityAPI().deleteUser("userWithIcon"); } @Test public void should_update_user_with_new_icon_create_a_new_icon() throws Exception { //given User user = getIdentityAPI().createUser( new UserCreator("userWithIcon", "thePassword").setIcon("myAvatar.png", "avatarContent".getBytes())); //when User updatedUser = getIdentityAPI().updateUser(user.getId(), new UserUpdater().setIcon("myFile.jpg", "content".getBytes())); //then Icon newIcon = getIdentityAPI().getIcon(updatedUser.getIconId()); assertThat(newIcon.getId()).isNotEqualTo(user.getIconId()); assertThat(newIcon.getMimeType()).isEqualTo("image/jpeg"); assertThat(newIcon.getContent()).isEqualTo("content".getBytes()); //cleanup getIdentityAPI().deleteUser("userWithIcon"); } @Test public void should_update_user_delete_the_icon() throws Exception { //given User user = getIdentityAPI().createUser( new UserCreator("userWithIcon", "thePassword").setIcon("myAvatar.jpg", "avatarContent".getBytes())); Icon icon = getIdentityAPI().getIcon(user.getIconId()); //when User updateUser = getIdentityAPI().updateUser(user.getId(), new UserUpdater().setIcon(null, null)); //then try { getIdentityAPI().getIcon(icon.getId()); fail(); } catch (NotFoundException ignored) { } assertThat(updateUser.getIconId()).isNull(); //cleanup getIdentityAPI().deleteUser("userWithIcon"); } @Test(expected = LoginException.class) public void loginFailsUsingWrongUser() throws BonitaException { final String userName = "hannu"; final String password = "install"; TenantAPIAccessor.getLoginAPI().login(userName, password); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/login/PlatformLoginAPIIT.java ================================================ /** * 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.login; import java.io.IOException; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.api.PlatformCommandAPI; import org.bonitasoft.engine.api.PlatformLoginAPI; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.engine.session.SessionNotFoundException; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class PlatformLoginAPIIT extends CommonAPIIT { private static final String COMMAND_NAME = "deletePlatformSessionCommand"; private static PlatformLoginAPI platformLoginAPI; @Before public void before() throws BonitaException, IOException { platformLoginAPI = PlatformAPIAccessor.getPlatformLoginAPI(); } @Test(expected = SessionNotFoundException.class) public void testSessionNotFoundExceptionIsThrownAfterSessionDeletion() throws Exception { // login to create a session final PlatformSession sessionToDelete = platformLoginAPI.login("platformAdmin", "platform"); // delete the session created by the login deleteSession(sessionToDelete.getId()); // will throw SessionNotFoundException platformLoginAPI.logout(sessionToDelete); } private void deleteSession(final long sessionId) throws Exception { // create a new session to deploy and execute commands final PlatformSession session = platformLoginAPI.login("platformAdmin", "platform"); final PlatformCommandAPI platformCommandAPI = PlatformAPIAccessor.getPlatformCommandAPI(session); // register and execute a command to delete a session platformCommandAPI.register(COMMAND_NAME, "Deletes a platform session based on its sessionId", "org.bonitasoft.engine.command.DeletePlatformSessionCommand"); final Map parameters = new HashMap<>(); parameters.put("sessionId", sessionId); platformCommandAPI.execute(COMMAND_NAME, parameters); platformCommandAPI.unregister(COMMAND_NAME); // logout platformLoginAPI.logout(session); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/mdc/APICallLogIT.java ================================================ /** * 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.mdc; import java.util.Map; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserCreator; import org.bonitasoft.engine.identity.UserCreator.UserField; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; public class APICallLogIT extends CommonAPIIT { private User bill; @Before public void beforeTest() throws BonitaException { loginWithTechnicalUser(); bill = createUser(USERNAME, "bpm"); } @Rule public SystemOutRule systemOutRule = new SystemOutRule().enableLog(); @Test public void apiCallAndInspectLogs() throws Exception { // given loginWithTechnicalUser(); logout(); // when loginOnDefaultTenantWith(USERNAME, "bpm"); systemOutRule.clearLog(); try { var creator = new UserCreator("johny", "bpm"); // using the deprecated method should log a warning creator.getFields().put(UserField.ICON_NAME, "test.png"); getIdentityAPI().createUser(creator); // then and error is logged with user id in context LogITUtil.checkLogEntryContains(systemOutRule.getLog(), Map.of(MDCConstants.USER_ID, Long.toString(bill.getId()))); } finally { getIdentityAPI().deleteUser("johny"); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/mdc/ConnectorExecutionLogIT.java ================================================ /** * 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.mdc; import static org.bonitasoft.engine.test.BuildTestUtil.generateConnectorImplementation; import java.util.Map; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.connectors.DoNothingConnector; import org.bonitasoft.engine.connectors.FailingConnector; import org.bonitasoft.engine.execution.ProcessExecutorImpl; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; import org.slf4j.LoggerFactory; public class ConnectorExecutionLogIT extends CommonAPIIT { @Rule public SystemOutRule systemOutRule = new SystemOutRule().enableLog(); @After public void afterTest() throws Exception { logout(); } @Before public void beforeTest() throws Exception { loginWithTechnicalUser(); } @Test public void executeConnectorAndInspectLogs() throws Exception { AutomaticTaskDefinitionBuilder automaticTask = new ProcessDefinitionBuilder() .createNewInstance("processWithConnectors", " 1.0") .addAutomaticTask("step"); automaticTask.addConnector("enter1", "connectorDef1", "1.0", ConnectorEvent.ON_ENTER); BusinessArchive bar = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(automaticTask .getProcess()) .addConnectorImplementation( generateConnectorImplementation("connectorDef1", "1.0", DoNothingConnector.class)) .done(); ProcessDefinition processDefinition = getProcessAPI().deploy(bar); getProcessAPI().enableProcess(processDefinition.getId()); final Logger exeLogger = (Logger) LoggerFactory.getLogger(ProcessExecutorImpl.class); Level oldLogLevel = exeLogger.getLevel(); try { exeLogger.setLevel(Level.DEBUG); // when systemOutRule.clearLog(); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForProcessToFinish(processInstance); // then we must have context for exception, even if it was logged outside of the initial throwing scope checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()), MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDefinition.getId()), MDCConstants.ROOT_PROCESS_INSTANCE_ID, Long.toString(processInstance.getRootProcessInstanceId()))); } finally { disableAndDeleteProcess(processDefinition); // restore old log level exeLogger.setLevel(oldLogLevel); } } @Test public void executeFailedConnectorAndInspectLogs() throws Exception { AutomaticTaskDefinitionBuilder automaticTask = new ProcessDefinitionBuilder() .createNewInstance("processWithFailedConnectors", " 1.0") .addAutomaticTask("step"); automaticTask.addConnector("enter1", "connectorDef1", "1.0", ConnectorEvent.ON_ENTER); automaticTask.addConnector("enter2", "failingConnector", "1.0", ConnectorEvent.ON_ENTER); BusinessArchive bar = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(automaticTask .getProcess()) .addConnectorImplementation( generateConnectorImplementation("connectorDef1", "1.0", DoNothingConnector.class)) .addConnectorImplementation( generateConnectorImplementation("failingConnector", "1.0", FailingConnector.class)) .done(); ProcessDefinition processDefinition = getProcessAPI().deploy(bar); getProcessAPI().enableProcess(processDefinition.getId()); try { // when systemOutRule.clearLog(); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); FlowNodeInstance failedTask = waitForFlowNodeInFailedState(processInstance, "step"); // then we must have context for exception, even if it was logged outside of the initial throwing scope checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()), MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDefinition.getId()), MDCConstants.ROOT_PROCESS_INSTANCE_ID, Long.toString(processInstance.getRootProcessInstanceId()), MDCConstants.FLOW_NODE_INSTANCE_ID, Long.toString(failedTask.getId()))); } finally { disableAndDeleteProcess(processDefinition); } } /** * Check there is a line in the log which contain context variables * * @param context context variables to check */ private void checkLogEntryContains(Map context) { var log = systemOutRule.getLogWithNormalizedLineSeparator(); LogITUtil.checkLogEntryContains(log, context); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/mdc/LogITUtil.java ================================================ /** * 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.mdc; import static org.assertj.core.api.Assertions.assertThat; import java.text.MessageFormat; import java.util.Collection; import java.util.Collections; import java.util.Map; public class LogITUtil { /** * Check there is a line in the log which contain context variables * * @param log the log text * @param context context variables to check */ public static void checkLogEntryContains(String log, Map context) { checkLogEntryContains(log, context, Collections.emptyList()); } /** * Check there is a line in the log which contain context variables * * @param log the log text * @param context context variables to check * @param forbiddenKeys keys that must not appear in the context */ public static void checkLogEntryContains(String log, Map context, Collection forbiddenKeys) { var lines = log.split("\n\\|"); assertThat(lines).anyMatch(l -> { var entries = context.entrySet().stream(); return entries.allMatch(e -> { // we use the '| %X' in the pattern, so that every variable is preceded by a blank space var valueLog = MessageFormat.format(" {0}={1}", e.getKey(), e.getValue()); return l.contains(valueLog); }) && forbiddenKeys.stream().noneMatch(k -> { var keyLog = MessageFormat.format(" {0}=", k); return l.contains(keyLog); }); }); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/mdc/ProcessExecutionLogIT.java ================================================ /** * 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.mdc; import static org.assertj.core.api.Assertions.assertThat; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.regex.Pattern; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.api.impl.ProcessAPIImpl; import org.bonitasoft.engine.api.impl.ProcessManagementAPIImplDelegate; import org.bonitasoft.engine.api.impl.ProcessStarter; import org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager; import org.bonitasoft.engine.api.impl.transaction.process.DisableProcess; import org.bonitasoft.engine.api.impl.transaction.process.EnableProcess; import org.bonitasoft.engine.bar.BusinessArchiveServiceImpl; import org.bonitasoft.engine.bpm.process.impl.internal.DesignProcessDefinitionImpl; import org.bonitasoft.engine.classloader.ClassLoaderServiceImpl; import org.bonitasoft.engine.execution.ProcessExecutorImpl; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.test.TestStates; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; import org.slf4j.LoggerFactory; public class ProcessExecutionLogIT extends CommonAPIIT { @Rule public SystemOutRule systemOutRule = new SystemOutRule().enableLog(); @After public void afterTest() throws Exception { logout(); } @Before public void beforeTest() throws Exception { loginWithTechnicalUser(); } @Test public void inspectTransactionIdInLogs() throws Exception { var txIdPattern = Pattern.compile(" " + MDCConstants.TRANSACTION_ID + "=([0-9a-f:]+)"); Function> getTxId = l -> { var matcher = txIdPattern.matcher(l); return matcher.find() ? Optional.of(matcher.group(1)) : Optional.empty(); }; BiConsumer> checkLogHasTxIds = (log, classPatternsWithSameTx) -> { var classesToFind = new HashMap(classPatternsWithSameTx); var txIdFoundByClass = new HashMap(classPatternsWithSameTx.size()); var lines = log.split("\n\\|"); for (var l : lines) { var classFound = classesToFind.keySet().stream().filter(c -> { var linePattern = String.format("(?s).*(%s)\\|.*", c); return l.matches(linePattern); }).findAny(); classFound.ifPresent(c -> { // should have a tx id var txId = getTxId.apply(l); assertThat(txId).as("Log line %s should contain %s", l, MDCConstants.TRANSACTION_ID).isPresent(); var toFind = classesToFind.get(c); assertThat(toFind).as("Log line %s was found, but we expected only %d lines for class pattern %s", l, classPatternsWithSameTx.get(c), c) .isGreaterThan(0); classesToFind.put(c, toFind - 1); var previousTxId = txIdFoundByClass.put(c, txId.get()); if (previousTxId != null) { assertThat(previousTxId) .as("Transaction id should be consistent for class pattern %s. It has changed in line %s", c, l) .isEqualTo(txId.get()); } }); // there may be other log lines with or without transaction id... } assertThat(classesToFind).allSatisfy((c, i) -> assertThat(i) .as("We expected %2$d log lines for class pattern %1$s but found only %3$d.", c, classPatternsWithSameTx.get(c), classPatternsWithSameTx.get(c) - i) .isEqualTo(0)); }; var designProcessDef = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps(List.of("H1"), List.of(true)); var user = createUser(USERNAME, PASSWORD); // when systemOutRule.clearLog(); var processDef = deployAndEnableProcessWithActor(designProcessDef, BuildTestUtil.ACTOR_NAME, user); // then, check logs have transaction id for the given classes var businessArchivePattern = BusinessArchiveArtifactsManager.class.getSimpleName() + "|" + BusinessArchiveServiceImpl.class.getSimpleName(); checkLogHasTxIds.accept(systemOutRule.getLogWithNormalizedLineSeparator(), Map.of(businessArchivePattern, 2, ClassLoaderServiceImpl.class.getSimpleName(), 2, EnableProcess.class.getSimpleName(), 1)); long userId = user.getId(); try { // when systemOutRule.clearLog(); getProcessAPI().startProcess(userId, processDef.getId()); // then consecutive logs contain the same transaction id or a unique new one checkLogHasTxIds.accept(systemOutRule.getLogWithNormalizedLineSeparator(), Map.of(ProcessStarter.class.getSimpleName(), 1)); } finally { // when systemOutRule.clearLog(); disableAndDeleteProcess(processDef); // then there should be a transaction id once again checkLogHasTxIds.accept(systemOutRule.getLogWithNormalizedLineSeparator(), Map.of(DisableProcess.class.getSimpleName(), 1, ProcessManagementAPIImplDelegate.class.getSimpleName(), 1)); } } @Test public void failProcessWithExceptionAndInspectLogs() throws Exception { var designProcessDef = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps(List.of("S1", "H1"), List.of(false, true)); ((DesignProcessDefinitionImpl) designProcessDef).setStringIndex(1, "i1", new ExpressionBuilder().createConstantStringExpression("a")); for (var act : designProcessDef.getFlowElementContainer().getActivities()) { switch (act.getName()) { case "S1": // make S1 fail explicitly var script = "throw new Exception(\"Expected fail=OK\")"; Expression lastingExpr = new ExpressionBuilder().createGroovyScriptExpression("script", script, String.class.getName()); Operation operation = new OperationBuilder().createSetStringIndexOperation(1, lastingExpr); act.getOperations().add(operation); break; default: break; } } var user = createUser(USERNAME, PASSWORD); var processDef = deployAndEnableProcessWithActor(designProcessDef, BuildTestUtil.ACTOR_NAME, user); long userId = user.getId(); try { // when systemOutRule.clearLog(); var processInstance = getProcessAPI().startProcess(userId, processDef.getId()); var s1 = waitForFlowNodeInFailedState(processInstance, "S1"); // then we must have context for exception, even if it was logged outside of the initial throwing scope checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()), MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDef.getId()), MDCConstants.FLOW_NODE_INSTANCE_ID, Long.toString(s1.getId()), "Expected fail", "OK")); } finally { disableAndDeleteProcess(processDef); } } @Test public void executeProcessAndInspectLogs() throws Exception { var designProcessDef = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps(List.of("H1", "S2", "H3"), List.of(true, false, true)); ((DesignProcessDefinitionImpl) designProcessDef).setStringIndex(1, "i1", new ExpressionBuilder().createConstantStringExpression("a")); for (var act : designProcessDef.getFlowElementContainer().getActivities()) { switch (act.getName()) { case "S2": // make S2 last a bit var script = """ Thread.sleep(500) return 'b' """; Expression lastingExpr = new ExpressionBuilder().createGroovyScriptExpression("script", script, String.class.getName()); Operation operation = new OperationBuilder().createSetStringIndexOperation(1, lastingExpr); act.getOperations().add(operation); break; default: break; } } var user = createUser(USERNAME, PASSWORD); var processDef = deployAndEnableProcessWithActor(designProcessDef, BuildTestUtil.ACTOR_NAME, user); long userId = user.getId(); long substituteId = getSession().getUserId(); // set debug level to get execution logs on service tasks and process end final Logger exeLogger = (Logger) LoggerFactory.getLogger(ProcessExecutorImpl.class); Level oldExeLogLevel = exeLogger.getLevel(); final Logger apiLogger = (Logger) LoggerFactory.getLogger(ProcessAPIImpl.class); Level oldApiLogLevel = apiLogger.getLevel(); try { exeLogger.setLevel(Level.DEBUG); apiLogger.setLevel(Level.INFO); // when systemOutRule.clearLog(); var processInstance = getProcessAPI().startProcess(userId, processDef.getId()); // then checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()), MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDef.getId()), MDCConstants.USER_ID, Long.toString(userId), MDCConstants.SUBSTITUTE_USER_ID, Long.toString(substituteId))); // when var h1 = waitForUserTaskAndGetIt(processInstance, "H1"); systemOutRule.clearLog(); getProcessAPI().assignAndExecuteUserTask(userId, h1.getId(), Collections.emptyMap()); // then checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()), MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDef.getId()), MDCConstants.FLOW_NODE_INSTANCE_ID, Long.toString(h1.getId()), MDCConstants.USER_ID, Long.toString(userId), MDCConstants.SUBSTITUTE_USER_ID, Long.toString(substituteId))); // when systemOutRule.clearLog(); // give S2 the required time to process var h3 = waitForUserTaskAndGetIt(processInstance, "H3"); var s2Id = waitForFlowNodeInState(processInstance, "S2", TestStates.NORMAL_FINAL, false); // then checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()), MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDef.getId()), MDCConstants.FLOW_NODE_INSTANCE_ID, Long.toString(s2Id))); //when systemOutRule.clearLog(); getProcessAPI().assignAndExecuteUserTask(userId, h3.getId(), Collections.emptyMap()); // then checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()), MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDef.getId()), MDCConstants.FLOW_NODE_INSTANCE_ID, Long.toString(h3.getId()), MDCConstants.USER_ID, Long.toString(userId), MDCConstants.SUBSTITUTE_USER_ID, Long.toString(substituteId))); // when waitForProcessToFinish(processInstance); // then checkLogEntryContains(Map.of(MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstance.getId()), MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDef.getId())), // distinguish process end from human task... Collections.singletonList(MDCConstants.FLOW_NODE_INSTANCE_ID)); } finally { disableAndDeleteProcess(processDef); // restore old log level exeLogger.setLevel(oldExeLogLevel); apiLogger.setLevel(oldApiLogLevel); } } /** * Check there is a line in the log which contain context variables * * @param context context variables to check */ private void checkLogEntryContains(Map context) { var log = systemOutRule.getLogWithNormalizedLineSeparator(); LogITUtil.checkLogEntryContains(log, context); } /** * Check there is a line in the log which contain context variables * * @param context context variables to check * @param forbiddenKeys keys that must not appear in the context */ private void checkLogEntryContains(Map context, Collection forbiddenKeys) { var log = systemOutRule.getLogWithNormalizedLineSeparator(); LogITUtil.checkLogEntryContains(log, context, forbiddenKeys); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/operation/OperationIT.java ================================================ /** * 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.operation; import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import org.assertj.core.api.SoftAssertions; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.contract.FileInputValue; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.document.DocumentValue; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.xml.DocumentManager; import org.custommonkey.xmlunit.XMLUnit; import org.junit.Test; import org.w3c.dom.Document; import org.w3c.dom.Node; /** * @author Baptiste Mesta */ public class OperationIT extends TestWithUser { @Test public void executeStringOperationOnData() throws Exception { createAndExecuteProcessWithOperations("executeStringOperationOnData", singletonList("myData1"), singletonList(String.class.getName()), singletonList(new LeftOperandBuilder().createNewInstance().setName("myData1").done()), singletonList(OperatorType.ASSIGNMENT), singletonList("="), singletonList(new ExpressionBuilder().createConstantStringExpression("val1")), singletonList(new ExpressionBuilder().createConstantStringExpression("val2")), singletonList("val1"), singletonList("val2")); } @Test public void executeBooleanOperationOnData() throws Exception { createAndExecuteProcessWithOperations("executeBooleanOperationOnData", singletonList("myData1"), singletonList(Boolean.class.getName()), singletonList(new LeftOperandBuilder().createNewInstance().setName("myData1").done()), singletonList(OperatorType.ASSIGNMENT), singletonList("="), singletonList(new ExpressionBuilder().createConstantBooleanExpression(true)), singletonList(new ExpressionBuilder().createConstantBooleanExpression(false)), singletonList(true), singletonList(false)); } @Test public void executeStringIndexOperation() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("procWithStringIndexes", "1.0"); final String actorName = "doctor"; designProcessDefinition.addActor(actorName).addDescription("The doc'"); designProcessDefinition.addUserTask("step1", actorName); designProcessDefinition.addUserTask("step3", actorName); final AutomaticTaskDefinitionBuilder addAutomaticTask = designProcessDefinition.addAutomaticTask("step2"); addAutomaticTask.addOperation(new OperationBuilder().createSetStringIndexOperation(1, new ExpressionBuilder().createConstantStringExpression("newValue1"))); addAutomaticTask.addOperation(new OperationBuilder().createSetStringIndexOperation(2, new ExpressionBuilder().createConstantStringExpression("newValue2"))); addAutomaticTask.addOperation(new OperationBuilder().createSetStringIndexOperation(3, new ExpressionBuilder().createConstantStringExpression("newValue3"))); addAutomaticTask.addOperation(new OperationBuilder().createSetStringIndexOperation(4, new ExpressionBuilder().createConstantStringExpression("newValue4"))); addAutomaticTask.addOperation(new OperationBuilder().createSetStringIndexOperation(5, new ExpressionBuilder().createConstantStringExpression("newValue5"))); designProcessDefinition.addTransition("step1", "step2"); designProcessDefinition.addTransition("step2", "step3"); designProcessDefinition.setStringIndex(1, "label1", new ExpressionBuilder().createConstantStringExpression("value1")); designProcessDefinition.setStringIndex(2, "label2", new ExpressionBuilder().createConstantStringExpression("value2")); designProcessDefinition.setStringIndex(3, "label3", new ExpressionBuilder().createConstantStringExpression("value3")); designProcessDefinition.setStringIndex(4, "label4", new ExpressionBuilder().createConstantStringExpression("value4")); designProcessDefinition.setStringIndex(5, "label5", new ExpressionBuilder().createConstantStringExpression("value5")); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), actorName, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(startProcess, "step1"); ProcessInstance processInstance = getProcessAPI().getProcessInstance(startProcess.getId()); assertEquals("value1", processInstance.getStringIndex1()); assertEquals("value2", processInstance.getStringIndex2()); assertEquals("value3", processInstance.getStringIndex3()); assertEquals("value4", processInstance.getStringIndex4()); assertEquals("value5", processInstance.getStringIndex5()); assignAndExecuteStep(step1Id, user); waitForUserTask(startProcess, "step3"); processInstance = getProcessAPI().getProcessInstance(startProcess.getId()); assertEquals("newValue1", processInstance.getStringIndex1()); assertEquals("newValue2", processInstance.getStringIndex2()); assertEquals("newValue3", processInstance.getStringIndex3()); assertEquals("newValue4", processInstance.getStringIndex4()); assertEquals("newValue5", processInstance.getStringIndex5()); disableAndDeleteProcess(processDefinition); } @Test public void executeStringIndexOperationUsingData() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("procWithStringIndexes", "1.0"); final String actorName = "doctor"; designProcessDefinition.addData("baseData", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("baseValue")); designProcessDefinition.addActor(actorName).addDescription("The doc'"); designProcessDefinition.addUserTask("step1", actorName); designProcessDefinition.addUserTask("step3", actorName); final AutomaticTaskDefinitionBuilder addAutomaticTask = designProcessDefinition.addAutomaticTask("step2"); addAutomaticTask.addOperation(new LeftOperandBuilder().createDataLeftOperand("baseData"), OperatorType.ASSIGNMENT, null, null, new ExpressionBuilder().createConstantStringExpression("changedData")); addAutomaticTask.addOperation(new OperationBuilder().createSetStringIndexOperation(1, new ExpressionBuilder().createDataExpression("baseData", String.class.getName()))); designProcessDefinition.addTransition("step1", "step2"); designProcessDefinition.addTransition("step2", "step3"); designProcessDefinition.setStringIndex(1, "label1", new ExpressionBuilder().createDataExpression("baseData", String.class.getName())); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), actorName, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(startProcess, "step1"); ProcessInstance processInstance = getProcessAPI().getProcessInstance(startProcess.getId()); assertEquals("baseValue", processInstance.getStringIndex1()); assignAndExecuteStep(step1Id, user); waitForUserTask(startProcess, "step3"); processInstance = getProcessAPI().getProcessInstance(startProcess.getId()); assertEquals("changedData", processInstance.getStringIndex1()); disableAndDeleteProcess(processDefinition); } @Test(expected = InvalidProcessDefinitionException.class) public void executeOperationWithNoExpression() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("procWithStringIndexes", "1.0"); final String actorName = "doctor"; designProcessDefinition.addActor(actorName).addDescription("The doc'"); designProcessDefinition.addUserTask("step1", actorName); designProcessDefinition.addUserTask("step3", actorName); final AutomaticTaskDefinitionBuilder addAutomaticTask = designProcessDefinition.addAutomaticTask("step2"); addAutomaticTask.addOperation(new OperationBuilder().createSetStringIndexOperation(1, null)); designProcessDefinition.addTransition("step1", "step2"); designProcessDefinition.addTransition("step2", "step3"); designProcessDefinition.done(); } @Test public void executeIntegerOperationOnData() throws Exception { createAndExecuteProcessWithOperations("executeIntegerOperationOnData", singletonList("myData12"), singletonList(Integer.class.getName()), singletonList(new LeftOperandBuilder().createNewInstance().setName("myData12").done()), singletonList(OperatorType.ASSIGNMENT), singletonList("="), singletonList(new ExpressionBuilder().createConstantIntegerExpression(1)), singletonList(new ExpressionBuilder().createConstantIntegerExpression(2)), singletonList(1), singletonList(2)); } @Test public void executeLongOperationOnData() throws Exception { createAndExecuteProcessWithOperations("executeLongOperationOnData", singletonList("myData12"), singletonList(Long.class.getName()), singletonList(new LeftOperandBuilder().createNewInstance().setName("myData12").done()), singletonList(OperatorType.ASSIGNMENT), singletonList("="), singletonList(new ExpressionBuilder().createConstantLongExpression(1234567891234567891L)), singletonList(new ExpressionBuilder().createConstantLongExpression(1234567891234567892L)), singletonList(1234567891234567891L), singletonList(1234567891234567892L)); } @Test public void executeMultipleOperations() throws Exception { createAndExecuteProcessWithOperations("executeMultipleOperations", Arrays.asList("myData1", "myData2", "myData3"), Arrays.asList( String.class.getName(), Boolean.class.getName(), Long.class.getName()), Arrays.asList( new LeftOperandBuilder().createNewInstance().setName("myData1").done(), new LeftOperandBuilder().createNewInstance().setName("myData2").done(), new LeftOperandBuilder().createNewInstance().setName("myData3").done()), Arrays.asList(OperatorType.ASSIGNMENT, OperatorType.ASSIGNMENT, OperatorType.ASSIGNMENT), Arrays.asList("=", "=", "="), Arrays.asList(new ExpressionBuilder().createConstantStringExpression("test1"), new ExpressionBuilder().createConstantBooleanExpression(false), new ExpressionBuilder().createConstantLongExpression(1234567891234567891L)), Arrays.asList(new ExpressionBuilder().createConstantStringExpression("test2"), new ExpressionBuilder().createConstantBooleanExpression(true), new ExpressionBuilder().createConstantLongExpression(1234567891234567892L)), Arrays.asList("test1", false, 1234567891234567891L), Arrays.asList("test2", true, 1234567891234567892L)); } @Test public void executeMultipleOperationsWithSameData() throws Exception { createAndExecuteProcessWithOperations("executeMultipleOperationsWithSameData", Arrays.asList("myData1", "myData2", "myData3", "myData1"), Arrays.asList(String.class.getName(), Boolean.class.getName(), Long.class.getName(), String.class.getName()), Arrays.asList( new LeftOperandBuilder().createNewInstance().setName("myData1").done(), new LeftOperandBuilder().createNewInstance().setName("myData2") .done(), new LeftOperandBuilder().createNewInstance().setName("myData3").done(), new LeftOperandBuilder().createNewInstance() .setName("myData1").done()), Arrays.asList(OperatorType.ASSIGNMENT, OperatorType.ASSIGNMENT, OperatorType.ASSIGNMENT, OperatorType.ASSIGNMENT), Arrays.asList("=", "=", "=", "="), Arrays.asList( new ExpressionBuilder().createConstantStringExpression("test1"), new ExpressionBuilder().createConstantBooleanExpression(false), new ExpressionBuilder().createConstantLongExpression(1234567891234567891L), new ExpressionBuilder().createConstantStringExpression("test1")), Arrays.asList( new ExpressionBuilder().createConstantStringExpression("test2"), new ExpressionBuilder().createConstantBooleanExpression(true), new ExpressionBuilder().createConstantLongExpression(1234567891234567892L), new ExpressionBuilder().createConstantStringExpression("test3")), Arrays.asList("test1", false, 1234567891234567891L, "test1"), Arrays.asList("test3", true, 1234567891234567892L, "test3")); } @Test public void should_set_documents_with_operations() throws Exception { final String actorName = "Document supplier"; final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("should_set_documents_with_operations", "1.0"); processDefinitionBuilder.addActor(actorName).addDescription("Process documents"); processDefinitionBuilder.addDocumentDefinition("doc1"); processDefinitionBuilder.addDocumentDefinition("doc2").addDescription("Will receive the content of doc1"); processDefinitionBuilder.addDocumentListDefinition("docList1"); processDefinitionBuilder.addDocumentListDefinition("docList2") .addDescription("Will receive the content of docList1"); processDefinitionBuilder.addUserTask("step0", actorName); processDefinitionBuilder .addAutomaticTask("step1") .addOperation(new OperationBuilder().createSetDocumentList("docList1", new ExpressionBuilder().createGroovyScriptExpression("setDocumentList1()", "[new org.bonitasoft.engine.bpm.contract.FileInputValue('doc1.txt', 'content of doc1'.getBytes('UTF-8'))" + ",new org.bonitasoft.engine.bpm.contract.FileInputValue('doc2.txt', 'content of doc2'.getBytes('UTF-8'))]", List.class.getName()))) .addOperation(new OperationBuilder().createSetDocumentList("docList2", new ExpressionBuilder().createGroovyScriptExpression("setDocList1ValueToDocList2()", "return docList1", List.class.getName(), // mimic what we have in BS-18741, force usage of document list reference as this is done // in the Studio (automatic dependencies resolution) new ExpressionBuilder().createDocumentListExpression("docList1")))) .addOperation(new OperationBuilder().createSetDocument("doc1", new ExpressionBuilder().createGroovyScriptExpression("setDoc1()", "new org.bonitasoft.engine.bpm.contract.FileInputValue('doc1.txt', 'content of doc1'.getBytes('UTF-8'))", FileInputValue.class.getName()))) .addOperation(new OperationBuilder().createSetDocument("doc2", new ExpressionBuilder().createGroovyScriptExpression("setDoc1ValueToDoc2()", "doc1", DocumentValue.class.getName() // mimic what we have in BS-18741, force usage of document reference as this is done // in the Studio (automatic dependencies resolution) , new ExpressionBuilder().createDocumentReferenceExpression("doc1")))); processDefinitionBuilder.addUserTask("step2", actorName); processDefinitionBuilder.addTransition("step0", "step1"); processDefinitionBuilder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), actorName, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step0", user); waitForUserTask(processInstance, "step2"); SoftAssertions softly = new SoftAssertions(); final List docList1AfterExecution = getProcessAPI() .getDocumentList(processInstance.getId(), "docList1", 0, 100); softly.assertThat(docList1AfterExecution).as("docList1 after execution") .hasSize(2) .extracting(org.bonitasoft.engine.bpm.document.Document::getContentFileName) .containsOnly("doc1.txt", "doc2.txt"); final List docList2AfterExecution = getProcessAPI() .getDocumentList(processInstance.getId(), "docList2", 0, 100); softly.assertThat(docList2AfterExecution).as("docList2 after execution") .hasSize(2) .extracting(org.bonitasoft.engine.bpm.document.Document::getContentFileName) .containsOnly("doc1.txt", "doc2.txt"); final org.bonitasoft.engine.bpm.document.Document doc1AfterExecution = getProcessAPI() .getLastDocument(processInstance.getId(), "doc1"); softly.assertThat(doc1AfterExecution.hasContent()).isTrue(); softly.assertThat(doc1AfterExecution.getContentFileName()).isEqualTo("doc1.txt"); final org.bonitasoft.engine.bpm.document.Document doc2AfterExecution = getProcessAPI() .getLastDocument(processInstance.getId(), "doc2"); softly.assertThat(doc2AfterExecution.hasContent()).isTrue(); softly.assertThat(doc2AfterExecution.getContentFileName()).isEqualTo("doc1.txt"); disableAndDeleteProcess(processDefinition); softly.assertAll(); } protected void createAndExecuteProcessWithOperations(final String procName, final List dataName, final List className, final List leftOperand, final List assignment, final List operator, final List dataDefaultValue, final List expression, final List valueBefore, final List valueAfter) throws Exception { final String delivery = "Delivery men"; final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance(procName, "1.0"); final HashSet dataSet = new HashSet<>(dataName.size()); for (int i = 0; i < dataName.size(); i++) { final String name = dataName.get(i); if (!dataSet.contains(name)) { designProcessDefinition.addData(name, className.get(i), dataDefaultValue.get(i)); dataSet.add(name); } } designProcessDefinition.addActor(delivery).addDescription("Delivery all day and night long"); designProcessDefinition.addUserTask("step0", delivery); final AutomaticTaskDefinitionBuilder addAutomaticTask = designProcessDefinition.addAutomaticTask("step1"); for (int i = 0; i < leftOperand.size(); i++) { // No Java operation, so empty string passed: addAutomaticTask.addOperation(leftOperand.get(i), assignment.get(i), operator.get(i), "", expression.get(i)); } designProcessDefinition.addUserTask("step2", delivery); designProcessDefinition.addTransition("step0", "step1"); designProcessDefinition.addTransition("step1", "step2"); designProcessDefinition.setStringIndex(1, "label1", new ExpressionBuilder().createConstantStringExpression("value1")); designProcessDefinition.setStringIndex(2, "label2", new ExpressionBuilder().createConstantStringExpression("value2")); designProcessDefinition.setStringIndex(3, "label3", new ExpressionBuilder().createConstantStringExpression("value3")); designProcessDefinition.setStringIndex(4, "label4", new ExpressionBuilder().createConstantStringExpression("value4")); designProcessDefinition.setStringIndex(5, "label5", new ExpressionBuilder().createConstantStringExpression("value5")); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), delivery, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); final HashMap inverDataOrder = new HashMap<>(dataName.size()); for (int i = dataName.size() - 1; i >= 0; i--) { final String name = dataName.get(i); if (!inverDataOrder.containsKey(name)) { inverDataOrder.put(name, i); } } for (final Entry dataIndex : inverDataOrder.entrySet()) { assertEquals("before execution of operation " + dataIndex.getKey(), valueBefore.get(dataIndex.getValue()), getProcessAPI().getProcessDataInstance(dataIndex.getKey(), startProcess.getId()).getValue()); } waitForUserTaskAndExecuteIt(startProcess, "step0", user); waitForUserTask(startProcess, "step2"); for (final Entry dataIndex : inverDataOrder.entrySet()) { assertEquals("after execution of operation " + dataIndex.getKey(), valueAfter.get(dataIndex.getValue()), getProcessAPI().getProcessDataInstance(dataIndex.getKey(), startProcess.getId()).getValue()); } disableAndDeleteProcess(processDefinition); } @Test public void updateAnAttributeOfAnXMLNodeOfADataUsingXPath() throws Exception { final String defaultValue = ""; final String updatedValue = ""; final String xPathExpression = "/root/@name"; final String updatedContent = "after"; executeProcessAndUpdateData(defaultValue, xPathExpression, updatedContent, updatedValue); } @Test public void setTheContentOfAnXMLNodeOfADataUsingXPath() throws Exception { final String defaultValue = ""; final String updatedValue = "after"; final String xPathExpression = "/root/text()"; final String updatedContent = "after"; executeProcessAndUpdateData(defaultValue, xPathExpression, updatedContent, updatedValue); } @Test public void updateTheContentOfAnXMLNodeOfADataUsingXPath() throws Exception { final String defaultValue = "before"; final String updatedValue = "after"; final String xPathExpression = "/root/text()"; final String updatedContent = "after"; executeProcessAndUpdateData(defaultValue, xPathExpression, updatedContent, updatedValue); } @Test public void updateTheContentOfASubNodeOfAnXMLNodeOfADataUsingXPath() throws Exception { final String defaultValue = "before"; final String updatedValue = "after"; final String xPathExpression = "/node/subnode[1]/text()"; final String updatedContent = "after"; executeProcessAndUpdateData(defaultValue, xPathExpression, updatedContent, updatedValue); } @Test public void removeAndCreateANewNodeOfAnXMLNodeOfADataUsingXPath() throws Exception { final String defaultValue = ""; final String updatedValue = ""; final String xPathExpression = "/node/before"; final String updatedContent = "after"; final ExpressionBuilder expressionBuilder = new ExpressionBuilder(); final Expression rightOperand = expressionBuilder.createGroovyScriptExpression( "removeAndCreateANewNodeOfAnXMLNodeOfADataUsingXPath", "import javax.xml.parsers.DocumentBuilder;" + "import javax.xml.parsers.DocumentBuilderFactory;import org.w3c.dom.Node;" + "DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); return builder.newDocument().createElement(\"" + updatedContent + "\");", Node.class.getName()); executeProcessAndUpdateData(defaultValue, xPathExpression, rightOperand, updatedValue); } @Test public void updateTheContentOfAnXMLNodeOfADataWithIntegerValueUsingXPath() throws Exception { updateAttributeAndContentWithNumericValueUsingXPath( new ExpressionBuilder().createConstantIntegerExpression(123), "123"); } private void updateAttributeAndContentWithNumericValueUsingXPath(final Expression rightOperand, final String result) throws Exception { executeProcessAndUpdateData(Arrays.asList("before", ""), Arrays.asList("/root/text()", "/root/@name"), Arrays.asList(rightOperand, rightOperand), Arrays.asList("" + result + "", "")); } @Test public void updateTheContentOfAnXMLNodeOfADataWithBooleanValueUsingXPath() throws Exception { updateAttributeAndContentWithNumericValueUsingXPath( new ExpressionBuilder().createConstantBooleanExpression(true), "true"); } @Test public void updateTheContentOfAnXMLNodeOfADataWithLongValueUsingXPath() throws Exception { updateAttributeAndContentWithNumericValueUsingXPath(new ExpressionBuilder().createConstantLongExpression(123), "123"); } @Test public void updateTheContentOfAnXMLNodeOfADataWithDoubleValueUsingXPath() throws Exception { updateAttributeAndContentWithNumericValueUsingXPath( new ExpressionBuilder().createConstantDoubleExpression(123.123), "123.123"); } private void executeProcessAndUpdateData(final String defaultValue, final String xPathExpression, final String updatedContent, final String updatedValue) throws Exception { final ExpressionBuilder expressionBuilder = new ExpressionBuilder(); final Expression rightOperand = expressionBuilder.createConstantStringExpression(updatedContent); executeProcessAndUpdateData(defaultValue, xPathExpression, rightOperand, updatedValue); } private void executeProcessAndUpdateData(final List defaultValues, final List xPathExpressions, final List rightOperands, final List updatedValues) throws Exception { final ExpressionBuilder expressionBuilder = new ExpressionBuilder(); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("XPathEvaluation", "1.0"); final String variableBaseName = "var"; int i = 0; designProcessDefinition.addActor("Workers"); final UserTaskDefinitionBuilder userTask = designProcessDefinition.addAutomaticTask("start") .addUserTask("step1", "Workers"); final Iterator xPathExpression = xPathExpressions.iterator(); final Iterator rightOperand = rightOperands.iterator(); for (final String defaultValue : defaultValues) { final String variableName = variableBaseName + i; i++; final Expression defaultExpression = expressionBuilder.createConstantStringExpression(defaultValue); designProcessDefinition.addXMLData(variableName, defaultExpression).setNamespace("http://www.w3c.org"); userTask.addOperation(new OperationBuilder().createXPathOperation(variableName, xPathExpression.next(), rightOperand.next())); } designProcessDefinition.addUserTask("step2", "Workers").addTransition("start", "step1").addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), "Workers", user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(startProcess, "step1"); i = 0; for (final String defaultValue : defaultValues) { final String variableName = variableBaseName + i; i++; assertEquals(defaultValue, getProcessAPI().getProcessDataInstance(variableName, startProcess.getId()).getValue()); } assignAndExecuteStep(step1Id, user); waitForUserTask(startProcess, "step2"); i = 0; for (final String updatedValue : updatedValues) { final String variableName = variableBaseName + i; i++; final Document expected = DocumentManager.generateDocument(updatedValue); final Document actual = DocumentManager.generateDocument( (String) getProcessAPI().getProcessDataInstance(variableName, startProcess.getId()) .getValue()); XMLUnit.setIgnoreWhitespace(true); assertTrue(XMLUnit.compareXML(expected, actual).identical()); } disableAndDeleteProcess(processDefinition); } private void executeProcessAndUpdateData(final String defaultValue, final String xPathExpression, final Expression rightOperand, final String updatedValue) throws Exception { executeProcessAndUpdateData(singletonList(defaultValue), singletonList(xPathExpression), singletonList(rightOperand), singletonList(updatedValue)); } @Test public void initializeAVariableUsingAPIAccessor() throws Exception { final ExpressionBuilder expressionBuilder = new ExpressionBuilder(); final Expression apiAccessor = expressionBuilder.createAPIAccessorExpression(); final List dependencies = new ArrayList<>(1); dependencies.add(apiAccessor); final Expression defaultExpression = expressionBuilder.createGroovyScriptExpression( "initializeAVariableUsingAPIAccessor", "apiAccessor.getIdentityAPI().getNumberOfUsers()", Long.class.getName(), dependencies); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("APIAccessorInGroovyScript", "1.0"); designProcessDefinition.addLongData("users", defaultExpression); designProcessDefinition.addActor("Workers").addUserTask("step1", "Workers"); designProcessDefinition.addAutomaticTask("start").addTransition("start", "step1"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), "Workers", user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(startProcess, "step1"); final long numberOfUsers = getIdentityAPI().getNumberOfUsers(); assertEquals(numberOfUsers, getProcessAPI().getProcessDataInstance("users", startProcess.getId()).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void updateAVariableUsingLoggedUser() throws Exception { final LeftOperandBuilder leftOperandBuilder = new LeftOperandBuilder(); final LeftOperand leftOperand = leftOperandBuilder.createDataLeftOperand("userId"); final ExpressionBuilder expressionBuilder = new ExpressionBuilder(); final Expression loggedUser = expressionBuilder.createEngineConstant(ExpressionConstants.LOGGED_USER_ID); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("APIAccessorInGroovyScript", "1.0"); designProcessDefinition.addLongData("userId", new ExpressionBuilder().createConstantLongExpression(123L)); designProcessDefinition.addActor("Workers").addUserTask("step1", "Workers").addOperation(leftOperand, OperatorType.ASSIGNMENT, "=", null, loggedUser); designProcessDefinition.addAutomaticTask("start").addUserTask("step2", "Workers") .addTransition("start", "step1").addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), "Workers", user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(startProcess, "step1", user); waitForUserTask(startProcess, "step2"); assertEquals(user.getId(), getProcessAPI().getProcessDataInstance("userId", startProcess.getId()).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void updateAVariableUsingAPIAccessor() throws Exception { final LeftOperandBuilder leftOperandBuilder = new LeftOperandBuilder(); final LeftOperand leftOperand = leftOperandBuilder.createNewInstance("users").done(); final ExpressionBuilder expressionBuilder = new ExpressionBuilder(); final Expression apiAccessor = expressionBuilder.createAPIAccessorExpression(); final List dependencies = new ArrayList<>(1); dependencies.add(apiAccessor); final Expression defaultExpression = expressionBuilder.createGroovyScriptExpression( "updateAVariableUsingAPIAccessor", "apiAccessor.getIdentityAPI().getNumberOfUsers()", Long.class.getName(), dependencies); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("APIAccessorInGroovyScript", "1.0"); designProcessDefinition.addLongData("users", null); designProcessDefinition.addActor("Workers").addUserTask("step1", "Workers") .addOperation(leftOperand, OperatorType.ASSIGNMENT, "=", null, defaultExpression); designProcessDefinition.addAutomaticTask("start").addUserTask("step2", "Workers") .addTransition("start", "step1").addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), "Workers", user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(startProcess, "step1", user); waitForUserTask(startProcess, "step2"); final long numberOfUsers = getIdentityAPI().getNumberOfUsers(); assertEquals(numberOfUsers, getProcessAPI().getProcessDataInstance("users", startProcess.getId()).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void takeTransitionUsingAPIAccessor() throws Exception { final ExpressionBuilder expressionBuilder = new ExpressionBuilder(); final Expression apiAccessor = expressionBuilder.createAPIAccessorExpression(); final List dependencies = new ArrayList<>(1); dependencies.add(apiAccessor); final Expression defaultExpression = expressionBuilder.createGroovyScriptExpression( "takeTransitionUsingAPIAccessor", "apiAccessor.getIdentityAPI().getNumberOfUsers() < 100", Boolean.class.getName(), dependencies); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("APIAccessorInGroovyScript", "1.0"); designProcessDefinition.addActor("Workers").addUserTask("step1", "Workers"); designProcessDefinition.addAutomaticTask("start").addUserTask("step2", "Workers") .addTransition("start", "step1", defaultExpression) .addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), "Workers", user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(startProcess, "step1"); disableAndDeleteProcess(processDefinition); } @Test public void javaMethodOperationWithPrimitiveParameters() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "javaMethodOperationWithPrimitiveParameters", String.valueOf(System.currentTimeMillis())); final AutomaticTaskDefinitionBuilder task1Def = processDefinitionBuilder .addActor(ACTOR_NAME) .addData( "myDatum", "java.lang.StringBuilder", new ExpressionBuilder().createGroovyScriptExpression("myInitGroovyScript", "new java.lang.StringBuilder(\"_\")", "java.lang.StringBuilder")) .addAutomaticTask("step1"); task1Def.addOperation(new OperationBuilder().createJavaMethodOperation("myDatum", "append", "int", new ExpressionBuilder().createConstantIntegerExpression(55))); task1Def.addUserTask("step2", ACTOR_NAME).addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step2Id = waitForUserTask(processInstance, "step2"); final DataInstance activityDataInstance = getProcessAPI().getActivityDataInstance("myDatum", step2Id); assertEquals(StringBuilder.class, activityDataInstance.getValue().getClass()); assertEquals("_55", activityDataInstance.getValue().toString()); disableAndDeleteProcess(processDefinition); } @Test public void executeJavaOperationWithCustomType() throws Exception { final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive(); builder.addClasspathResource(getResource("/custom-0.1.jar.bak", "custom-0.1.jar")); builder.addClasspathResource(getResource("/org.bonitasoft.dfgdfg.bak", "org.bonitasoft.dfgdfg.jar")); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithCustomData", "1.0"); designProcessDefinition.addData("adress", "org.bonitasoft.custom.Address", new ExpressionBuilder().createGroovyScriptExpression("create adress", "new org.bonitasoft.custom.Address(\"name1\",\"Rue ampère\",\"38000\",\"Grenoble\",\"France\")", "org.bonitasoft.custom.Address")); designProcessDefinition.addData("contact", "org.bonitasoft.dfgdfg.Contact", new ExpressionBuilder().createGroovyScriptExpression("create contact", "new org.bonitasoft.dfgdfg.Contact()", "org.bonitasoft.dfgdfg.Contact")); designProcessDefinition .addActor("Workers") .addUserTask("step1", "Workers") .addOperation( new OperationBuilder().createJavaMethodOperation("adress", "setName", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("myAddress"))) .addOperation( new OperationBuilder().createJavaMethodOperation("contact", "setSite", Long.class.getName(), new ExpressionBuilder().createConstantLongExpression(12))); designProcessDefinition.addAutomaticTask("start").addUserTask("step2", "Workers") .addTransition("start", "step1").addTransition("step1", "step2"); builder.setProcessDefinition(designProcessDefinition.done()); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), "Workers", user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(startProcess, "step1", user); waitForUserTask(startProcess, "step2"); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/page/PageAPIIT.java ================================================ /** * Copyright (C) 2015 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.page; import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.bonitasoft.engine.io.FileAndContentUtils.file; import static org.bonitasoft.engine.io.FileAndContentUtils.zip; import static org.bonitasoft.engine.page.PageAssert.assertThat; import static org.junit.Assert.assertThrows; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.StringReader; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.apache.commons.io.IOUtils; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.api.permission.APICallContext; import org.bonitasoft.engine.application.ApplicationIT; import org.bonitasoft.engine.business.application.ApplicationImportPolicy; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.InvalidPageTokenException; import org.bonitasoft.engine.exception.InvalidPageZipMissingIndexException; import org.bonitasoft.engine.exception.UpdatingWithInvalidPageZipContentException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.profile.ProfileSearchDescriptor; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.CommonTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; public class PageAPIIT extends CommonAPIIT { public static final long PROCESS_DEFINITION_ID = 5846L; private static final String DISPLAY_NAME = "My P\u00e4ge"; private static final String CONTENT_NAME = "content.zip"; private static final String PAGE_DESCRIPTION = "page description"; private static final String PAGE_NAME2 = "custompage_page2"; private static final String PAGE_NAME1 = "custompage_page1"; @Before public void before() throws Exception { loginWithTechnicalUser(); final SearchResult searchPages = getPageAPI() .searchPages(new SearchOptionsBuilder(0, Integer.MAX_VALUE).done()); for (final Page page : searchPages.getResult()) { if (!page.isProvided()) { getPageAPI().deletePage(page.getId()); } } } @After public void after() throws Exception { logout(); } @Test public void should_getPage_return_the_page() throws Exception { // given final String name = generateUniquePageName(0); final byte[] pageContent = createTestPageContent(name, DISPLAY_NAME, PAGE_DESCRIPTION); final Page page = getPageAPI().createPage( new PageCreator(name, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME) .setContentType(ContentType.FORM) .setProcessDefinitionId(PROCESS_DEFINITION_ID), pageContent); // when final Page returnedPage = getPageAPI().getPage(page.getId()); // then Assertions.assertThat(returnedPage).usingRecursiveComparison().isEqualTo(page); PageAssert.assertThat(returnedPage) .hasProcessDefinitionId(PROCESS_DEFINITION_ID) .hasContentType(ContentType.FORM); } @Test public void updatePage_should_set_provided_field_to_false_if_provided_pages_are_modified() throws Exception { //given: try (var is = PageAPIIT.class.getResourceAsStream("/provided_page_ready_to_update.zip")) { getPageAPI().createPage("provided_page_ready_to_update.zip", is.readAllBytes()); } // when final PageUpdater pageUpdater = new PageUpdater(); final String newDisplayName = "new display name"; pageUpdater.setDisplayName(newDisplayName); final Page providedPageToUpdate = getPageAPI() .searchPages(new SearchOptionsBuilder(0, 1) .filter(PageSearchDescriptor.NAME, "custompage_providedpagetoupdate").done()) .getResult().get(0); final Page returnedPage = getPageAPI().updatePage(providedPageToUpdate.getId(), pageUpdater); //then assertThat(returnedPage.isProvided()).isFalse(); } @Test public void updatePage_should_return_the_modified_page() throws Exception { // given final User john = createUser("john", "bpm"); final User jack = createUser("jack", "bpm"); logout(); loginOnDefaultTenantWith("john", "bpm"); final String pageName = generateUniquePageName(0); final byte[] pageContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION); final Page pageBeforeUpdate = getPageAPI().createPage( new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), pageContent); Thread.sleep(10); assertThat(pageBeforeUpdate.getInstalledBy()).isEqualTo(john.getId()); assertThat(pageBeforeUpdate.getLastUpdatedBy()).isEqualTo(john.getId()); logout(); loginOnDefaultTenantWith("jack", "bpm"); // when final PageUpdater pageUpdater = new PageUpdater(); final String newDescription = "new description"; final String newDisplayName = "new display name"; final String newContentName = "new_content.zip"; pageUpdater.setDescription(newDescription); pageUpdater.setDisplayName(newDisplayName); pageUpdater.setName("newName"); pageUpdater.setContentName(newContentName); pageUpdater.setContentType(ContentType.FORM); pageUpdater.setProcessDefinitionId(5L); final Page returnedPage = getPageAPI().updatePage(pageBeforeUpdate.getId(), pageUpdater); // then assertThat(returnedPage) .hasInstalledBy(john.getId()) .hasInstalledBy(pageBeforeUpdate.getInstalledBy()) .hasLastUpdatedBy(jack.getId()) .hasName(pageBeforeUpdate.getName()) .hasInstallationDate(pageBeforeUpdate.getInstallationDate()) .hasDisplayName(newDisplayName) .hasContentName(newContentName) .hasDescription(newDescription) .hasContentType(ContentType.FORM) .hasProcessDefinitionId(5L); assertThat(returnedPage.getLastModificationDate()).as("last modification time should be updated") .isAfter(pageBeforeUpdate.getLastModificationDate()); logout(); loginWithTechnicalUser(); deleteUser(john); deleteUser(jack); } @Test(expected = AlreadyExistsException.class) public void updateForm_with_existing_process_definitionId_should_fail() throws Exception { final PageUpdater pageUpdater = new PageUpdater(); // given getPageAPI().createPage( new PageCreator(PAGE_NAME1, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME) .setProcessDefinitionId(PROCESS_DEFINITION_ID), CommonTestUtil.createTestPageContent(PAGE_NAME1, DISPLAY_NAME, PAGE_DESCRIPTION)); final Page page2 = getPageAPI().createPage( new PageCreator(PAGE_NAME1, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), CommonTestUtil.createTestPageContent(PAGE_NAME1, DISPLAY_NAME, PAGE_DESCRIPTION)); // when pageUpdater.setProcessDefinitionId(PROCESS_DEFINITION_ID); getPageAPI().updatePage(page2.getId(), pageUpdater); // then // exception } @Test(expected = UpdatingWithInvalidPageZipContentException.class) public void updatePageContent_with_bad_content_should_fail() throws Exception { // given final Page createPage = getPageAPI().createPage( new PageCreator(PAGE_NAME1, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), CommonTestUtil.createTestPageContent(PAGE_NAME1, DISPLAY_NAME, PAGE_DESCRIPTION)); // when getPageAPI().updatePageContent(createPage.getId(), IOUtil.zip(Collections.singletonMap("README.md", "empty file".getBytes()))); // then // exception } @Test public void updatePageContent_should_update_page_from_properties_except_name() throws Exception { // given final Page pageBefore = getPageAPI().createPage( new PageCreator(PAGE_NAME1, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), CommonTestUtil.createTestPageContent(PAGE_NAME1, DISPLAY_NAME, PAGE_DESCRIPTION)); // when final String newDescription = "new description"; final String newDisplayName = "new display name"; final byte[] updatedPageContent = CommonTestUtil.createTestPageContent(PAGE_NAME2, newDisplayName, newDescription); getPageAPI().updatePageContent(pageBefore.getId(), updatedPageContent); // then final Page pageAfter = getPageAPI().getPage(pageBefore.getId()); assertThat(pageAfter.getName()).as("should not update page name").isEqualTo(PAGE_NAME1); assertThat(pageAfter.getDisplayName()).as("should update page display name").isEqualTo(newDisplayName); assertThat(pageAfter.getDescription()).as("should update page name").isEqualTo(newDescription); } @Test public void should_update_content_return_the_modified_content() throws Exception { // given final Date createTimeMillis = new Date(System.currentTimeMillis()); final String pageName = generateUniquePageName(0); final byte[] oldContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION); final Page page = getPageAPI().createPage( new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), oldContent); final long pageId = page.getId(); // when // wait to see modified last update time Thread.sleep(1000); final Date updateTimeMillis = new Date(System.currentTimeMillis()); assertThat(updateTimeMillis).as("should wait 1 second").isAfter(createTimeMillis); final byte[] newContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION); getPageAPI().updatePageContent(pageId, newContent); final byte[] returnedPageContent = getPageAPI().getPageContent(pageId); final Page returnedPage = getPageAPI().getPage(pageId); // then checkPageContentContainsProperties(returnedPageContent, DISPLAY_NAME, PAGE_DESCRIPTION); assertThat(returnedPage.getLastModificationDate()).as("last modification date should be modified ").isAfter( page.getLastModificationDate()); } @Test public void should_create_page_use_content_type_in_properties() throws Exception { // given final String pageName1 = generateUniquePageName(0); final byte[] pageContent1 = CommonTestUtil.createTestPageContent(pageName1, DISPLAY_NAME, "with content " + PAGE_DESCRIPTION, "contentType=WillBeIgnored", "apiExtensions=myGetResource", "myGetResource.method=POST", "myGetResource.pathTemplate=helloWorld", "myGetResource.classFileName=Index.groovy", "myGetResource.permissions=application_visualization"); final String pageName2 = generateUniquePageName(1); final byte[] pageContent2 = CommonTestUtil.createTestPageContent(pageName2, DISPLAY_NAME, "with page creator " + PAGE_DESCRIPTION, "contentType=" + ContentType.API_EXTENSION, "apiExtensions=myGetResource", "myGetResource.method=GET", "myGetResource.pathTemplate=helloWorld", "myGetResource.classFileName=Index.groovy", "myGetResource.permissions=application_visualization"); // when final Page pageWithCreator = getPageAPI().createPage( new PageCreator(pageName1, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME) .setContentType(ContentType.API_EXTENSION), pageContent1); final Page pageWithContent = getPageAPI().createPage(pageName2, pageContent2); // then assertThat(pageWithContent).hasContentType(ContentType.API_EXTENSION); assertThat(pageWithCreator).hasContentType(ContentType.API_EXTENSION); } @Test public void should_create_a_page_and_delete_it_and_recreate_it_with_same_values() throws Exception { // given final String pageName = generateUniquePageName(1); final byte[] pageContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, "with page creator " + PAGE_DESCRIPTION, "contentType=" + ContentType.API_EXTENSION, "apiExtensions=myGetResource, myPostResource", "myGetResource.method=GET", "myGetResource.pathTemplate=helloWorld", "myGetResource.classFileName=Index.groovy", "myGetResource.permissions=newPermission", "myPostResource.method=POST", "myPostResource.pathTemplate=helloWorld", "myPostResource.classFileName=Index.groovy", "myPostResource.permissions=newPermission"); // when Page pageWithContent = getPageAPI().createPage(pageName, pageContent); getPageAPI().deletePage(pageWithContent.getId()); pageWithContent = getPageAPI().createPage(pageName, pageContent); // then assertThat(pageWithContent).hasContentType(ContentType.API_EXTENSION); } @Test public void should_getPage_by_name_return_the_page() throws Exception { // given final String pageName = generateUniquePageName(0); final byte[] pageContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION); final Page page = getPageAPI().createPage( new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), pageContent); // when final Page returnedPage = getPageAPI().getPageByName(page.getName()); // then Assertions.assertThat(returnedPage).usingRecursiveComparison().isEqualTo(page); } @Test(expected = AlreadyExistsException.class) public void should_createPage_with_same_name_throw_already_exists() throws Exception { // , "content.zip"given final String pageName = generateUniquePageName(0); final byte[] pageContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION); getPageAPI().createPage( new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), pageContent); // when getPageAPI().createPage( new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), pageContent); // then: expected exception } @Test(expected = InvalidPageTokenException.class) public void should_createPage_with_invalid_name_InvalidPageTokenException() throws Exception { // , "content.zip"given final String pageName = "plop"; final byte[] pageContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION); getPageAPI().createPage( new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), pageContent); // when getPageAPI().createPage( new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), pageContent); // then: expected exception } @Test(expected = InvalidPageTokenException.class) public void should_createPage_with_no_name_InvalidPageTokenException() throws Exception { // , "content.zip"given final String pageName = ""; final byte[] pageContent = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION); getPageAPI().createPage( new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), pageContent); // when getPageAPI().createPage( new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), pageContent); // then: expected exception } @Test public void should_createPage_with_invalid_content_InvalidPageZipContentException() throws Exception { // given final String pageName = generateUniquePageName(0); final byte[] pageContent = zip(file("README.md", "empty file".getBytes()), file("page.properties", String.format("name=%s\ncontentType=page", pageName).getBytes())); // when assertThrows(InvalidPageZipMissingIndexException.class, () -> getPageAPI().createPage( new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), pageContent)); // then: expected exception } @Test public void should_getPageContent_return_the_content() throws Exception { // given final String pageName = generateUniquePageName(0); final String pageDescription = "a verry long page description, maybe the longest description you will ever see, check that:" + " Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut" + " labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris" + " nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit " + "esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in " + "culpa qui officia deserunt mollit anim id est laborum."; final byte[] bytes = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, pageDescription); final Page page = getPageAPI().createPage( new PageCreator(pageName, CONTENT_NAME).setDescription(pageDescription).setDisplayName(DISPLAY_NAME), bytes); // when final byte[] pageContent = getPageAPI().getPageContent(page.getId()); // then checkPageContentContainsProperties(pageContent, DISPLAY_NAME, pageDescription); } @Test public void should_throw_an_exception_if_the_page_content_does_not_exist() { // when final PageNotFoundException exception = assertThrows(PageNotFoundException.class, () -> getPageAPI().getPageContent(995464654654L)); // then assertThat(exception.getMessage()).contains("Page with id 995464654654 not found"); } private void checkPageContentContainsProperties(final byte[] content, final String displayName, final String description) throws Exception { try { Map contentAsMap = unzip(content); assertThat(contentAsMap.keySet()).as("should contains page.properties").contains("page.properties"); final String string = contentAsMap.get("page.properties"); final Properties props = new Properties(); props.load(new StringReader(string)); assertThat(props.getProperty("description")).as("should have same description").isEqualTo(description); assertThat(props.getProperty("displayName")).as("should have same displayName").isEqualTo(displayName); } catch (final IOException e) { fail("unzip error", e); } } private Map unzip(final byte[] zipFile) throws Exception { final ByteArrayInputStream bais = new ByteArrayInputStream(zipFile); ZipEntry zipEntry; final Map zipMap = new HashMap<>(); try (ZipInputStream zipInputstream = new ZipInputStream(bais)) { while ((zipEntry = zipInputstream.getNextEntry()) != null) { final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); int bytesRead; final byte[] buffer = new byte[4096]; while ((bytesRead = zipInputstream.read(buffer)) > -1) { byteArrayOutputStream.write(buffer, 0, bytesRead); } zipMap.put(zipEntry.getName(), byteArrayOutputStream.toString(UTF_8)); } } return zipMap; } @Test(expected = PageNotFoundException.class) public void deletePage_should_delete_the_page() throws Exception { // given final String pageName = generateUniquePageName(0); final byte[] bytes = CommonTestUtil.createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION); final Page page = getPageAPI().createPage( new PageCreator(pageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), bytes); // when getPageAPI().deletePage(page.getId()); // then getPageAPI().getPage(page.getId()); } @Test(expected = AlreadyExistsException.class) public void should_duplicates_with_same_name_and_process_definitionId_throw_exception() throws Exception { // given final String pageName = generateUniquePageName(0); final byte[] bytes = createTestPageContent(pageName, DISPLAY_NAME, PAGE_DESCRIPTION); getPageAPI().createPage( new PageCreator(pageName, CONTENT_NAME, ContentType.FORM, PROCESS_DEFINITION_ID) .setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), bytes); // when then exception getPageAPI().createPage( new PageCreator(pageName, CONTENT_NAME, ContentType.FORM, PROCESS_DEFINITION_ID) .setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), bytes); } @Test public void should_search_with_search_term() throws Exception { final String description = "description"; final String matchingValue = "Cool"; final String matchingDisplayName = matchingValue + " page!"; // given final int noneMatchingCount = 8; for (int i = 0; i < noneMatchingCount; i++) { final String generateUniquePageName = generateUniquePageName(i) + i; getPageAPI().createPage( new PageCreator(generateUniquePageName, CONTENT_NAME).setDescription(description) .setDisplayName(DISPLAY_NAME), CommonTestUtil.createTestPageContent(generateUniquePageName, DISPLAY_NAME, PAGE_DESCRIPTION)); } final String generateUniquePageName = generateUniquePageName(9); final Page pageWithMatchingSearchTerm = getPageAPI().createPage( new PageCreator(generateUniquePageName, CONTENT_NAME).setDescription(description) .setDisplayName(matchingDisplayName), CommonTestUtil.createTestPageContent(generateUniquePageName, DISPLAY_NAME, PAGE_DESCRIPTION)); // when final SearchResult searchPages = getPageAPI() .searchPages(new SearchOptionsBuilder(0, 5).searchTerm(matchingValue).done()); // then final List results = searchPages.getResult(); assertThat(results.size()).as("should have only one matching page").isEqualTo(1); Assertions.assertThat(results.get(0)).as("should get the page with matching search term") .usingRecursiveComparison().isEqualTo(pageWithMatchingSearchTerm); } private String generateUniquePageName(final int i) { return ("custompage_unique" + i) + System.currentTimeMillis(); } @Test public void should_8_pages_search_5_first_results_give_5_first_results() throws Exception { // given final int expectedResultSize = 5; for (int i = 0; i < expectedResultSize + 3; i++) { final String generateUniquePageName = generateUniquePageName(i) + 1; final byte[] pageContent = CommonTestUtil.createTestPageContent(generateUniquePageName, DISPLAY_NAME, PAGE_DESCRIPTION); getPageAPI().createPage( new PageCreator(generateUniquePageName, CONTENT_NAME).setDescription(PAGE_DESCRIPTION) .setDisplayName(DISPLAY_NAME), pageContent); } // when final SearchResult searchPages = getPageAPI().searchPages(new SearchOptionsBuilder(0, 5).done()); // then final List results = searchPages.getResult(); String stringBuilder = "should have only " + expectedResultSize + " results"; assertThat(results.size()).as(stringBuilder).isEqualTo(expectedResultSize); } @Test public void should_search_by_display_name() throws Exception { // given final String description = PAGE_DESCRIPTION; final String matchingDisplayName = DISPLAY_NAME; final String noneMatchingDisplayName = "aaa"; // given final int expectedMatchingResults = 3; for (int i = 0; i < expectedMatchingResults; i++) { final String generateUniquePageName = generateUniquePageName(i); final byte[] pageContent = CommonTestUtil.createTestPageContent(generateUniquePageName, matchingDisplayName, description); getPageAPI().createPage( new PageCreator(generateUniquePageName, CONTENT_NAME).setDescription(description) .setDisplayName(matchingDisplayName), pageContent); } final String anOtherName = generateUniquePageName(4); getPageAPI().createPage( new PageCreator(anOtherName, CONTENT_NAME).setDescription("an awesome page!!!!!!!") .setDisplayName(noneMatchingDisplayName), CommonTestUtil.createTestPageContent(anOtherName, noneMatchingDisplayName, "an awesome page!!!!!!!")); // when final SearchResult searchPages = getPageAPI().searchPages( new SearchOptionsBuilder(0, expectedMatchingResults + 2) .filter(PageSearchDescriptor.DISPLAY_NAME, matchingDisplayName).done()); // then final List results = searchPages.getResult(); assertThat(results.size()).as("should have " + expectedMatchingResults + " results").isEqualTo(expectedMatchingResults); } @Test public void should_search_by_content_type() throws Exception { // given final String matchingDisplayName = DISPLAY_NAME; final String noneMatchingDisplayName = "aaa"; // given final int expectedMatchingResults = 3; for (int i = 0; i < expectedMatchingResults; i++) { final String generateUniquePageName = generateUniquePageName(i); final byte[] pageContent = createTestPageContent(generateUniquePageName, matchingDisplayName, PAGE_DESCRIPTION); getPageAPI().createPage( new PageCreator(generateUniquePageName, CONTENT_NAME, ContentType.FORM, PROCESS_DEFINITION_ID + i) .setDescription( "should be excluded from results") .setDisplayName(matchingDisplayName), pageContent); getPageAPI().createPage( new PageCreator(generateUniquePageName, CONTENT_NAME).setDescription("should be in search results") .setDisplayName(matchingDisplayName), pageContent); } final String anOtherName = generateUniquePageName(4); getPageAPI().createPage( new PageCreator(anOtherName, CONTENT_NAME).setDescription("should be excluded from results") .setDisplayName(noneMatchingDisplayName), createTestPageContent(anOtherName, noneMatchingDisplayName, "an awesome page!!!!!!!")); // when final SearchResult searchPages = getPageAPI().searchPages( new SearchOptionsBuilder(0, expectedMatchingResults + 2) .filter(PageSearchDescriptor.DISPLAY_NAME, matchingDisplayName) .filter(PageSearchDescriptor.CONTENT_TYPE, ContentType.PAGE).done()); // then final List results = searchPages.getResult(); assertThat(results.size()).as("should have " + expectedMatchingResults + " results").isEqualTo(expectedMatchingResults); } @Test public void should_search_work_on_desc_order() throws Exception { final String displayName = DISPLAY_NAME; final String description = PAGE_DESCRIPTION; final String firstPageNameInDescOrder = "custompage_zPageName"; // given final int numberOfNonsMatchingPage = 5; for (int i = 0; i < numberOfNonsMatchingPage; i++) { final String generateUniquePageName = generateUniquePageName(i) + i; final byte[] pageContent = CommonTestUtil.createTestPageContent(generateUniquePageName, displayName, description); getPageAPI().createPage( new PageCreator(generateUniquePageName, CONTENT_NAME).setDescription(description) .setDisplayName(displayName), pageContent); } final Page expectedMatchingPage = getPageAPI().createPage( new PageCreator(firstPageNameInDescOrder, CONTENT_NAME).setDescription(description) .setDisplayName(displayName), CommonTestUtil.createTestPageContent(firstPageNameInDescOrder, displayName, description)); // when final SearchResult searchPages = getPageAPI().searchPages( new SearchOptionsBuilder(0, 1).sort(PageSearchDescriptor.NAME, Order.DESC).done()); // then final List results = searchPages.getResult(); Assertions.assertThat(results.get(0)).usingRecursiveComparison().isEqualTo(expectedMatchingPage); } @Test public void updatePageContent_and_deletePage_should_update_permissions() throws Exception { // setup final User jack = createUser("jack", "bpm"); Profile profile = getProfileAPI() .searchProfiles(new SearchOptionsBuilder(0, 1).filter(ProfileSearchDescriptor.NAME, "User").done()) .getResult().get(0); getProfileAPI().createProfileMember(profile.getId(), jack.getId(), null, null); getLivingApplicationAPI().importApplications( IOUtils.toByteArray(ApplicationIT.class.getResourceAsStream("testApp.xml")), ApplicationImportPolicy.FAIL_ON_DUPLICATES); // given final String apiExtensionName = generateUniquePageName(0); final byte[] apiExtensionContent1 = CommonTestUtil.createTestPageContent(apiExtensionName, DISPLAY_NAME, "with content " + PAGE_DESCRIPTION, "contentType=" + ContentType.API_EXTENSION, "apiExtensions=myGetResource, myPostResource", "myGetResource.method=GET", "myGetResource.pathTemplate=helloWorld", "myGetResource.classFileName=Index.groovy", "myGetResource.permissions=application_visualization", "myPostResource.method=POST", "myPostResource.pathTemplate=helloWorld", "myPostResource.classFileName=Index.groovy", "myPostResource.permissions = application_visualization"); // when final Page apiExtension = getPageAPI().createPage(apiExtensionName, apiExtensionContent1); // need to log back in to actualize permissions: logout(); loginOnDefaultTenantWith("jack", "bpm"); // Check that we are not authorized before update of the page properties content: APICallContext apiCallContext = new APICallContext("GET", "extension", "helloWorld_v2", null); assertThat(getPermissionAPI().isAuthorized(apiCallContext)).isFalse(); final byte[] apiExtensionContent2 = CommonTestUtil.createTestPageContent(apiExtensionName, DISPLAY_NAME, "with content " + PAGE_DESCRIPTION, "contentType=" + ContentType.API_EXTENSION, "apiExtensions=myGetResource, myPutResource", "myGetResource.method=GET", "myGetResource.pathTemplate=helloWorld_v2", "myGetResource.classFileName=Index.groovy", "myGetResource.permissions=application_visualization", "myPutResource.method=PUT", "myPutResource.pathTemplate=helloWorld", "myPutResource.classFileName=Index.groovy", "myPutResource.permissions = application_visualization"); getPageAPI().updatePageContent(apiExtension.getId(), apiExtensionContent2); // Application testApp.xml references layout custompage_layoutBonita that declares resources bound to // the 'application_visualization' permission. So this new apiExtension, accessible through 'GET|extension/helloWorld_v2', is accessible // to anyone having 'application_visualization' permission. // Check that permission has been update to file through user permission check: assertThat(getPermissionAPI().isAuthorized(apiCallContext)).isTrue(); // Check that previous version has indeed been removed: assertThat(getPermissionAPI().isAuthorized(new APICallContext("GET", "extension", "helloWorld", null))) .isFalse(); getPageAPI().deletePage(apiExtension.getId()); // Check that permissions have been removed with the page deletion: assertThat(getPermissionAPI().isAuthorized(apiCallContext)).isFalse(); //cleanup logout(); loginWithTechnicalUser(); deleteUser(jack); } @Test public void should_search_work_with_processDefinitionId_set_to_null() throws Exception { // given String name = generateUniquePageName(345); getPageAPI().createPage( new PageCreator(name, CONTENT_NAME).setDescription(PAGE_DESCRIPTION).setDisplayName(DISPLAY_NAME), CommonTestUtil.createTestPageContent(name, DISPLAY_NAME, PAGE_DESCRIPTION)); // when final SearchResult searchPages = getPageAPI().searchPages( new SearchOptionsBuilder(0, 1).filter(PageSearchDescriptor.NAME, name) .filter(PageSearchDescriptor.PROCESS_DEFINITION_ID, null).done()); // then final List results = searchPages.getResult(); assertThat(results.get(0).getName()).isEqualTo(name); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/platform/PlatformIT.java ================================================ /** * 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.platform; import static org.awaitility.Awaitility.await; import static org.junit.Assert.*; import java.time.Duration; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.PrintTestsStatusRule; import org.bonitasoft.engine.api.APIClient; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.LoginAPI; import org.bonitasoft.engine.api.PlatformAPI; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.flownode.TimerType; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.engine.test.PlatformTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PlatformIT extends CommonAPIIT { private static final Logger LOGGER = LoggerFactory.getLogger(PlatformIT.class); private static PlatformAPI platformAPI; private static PlatformSession session; private static final PlatformTestUtil platformTestUtil = new PlatformTestUtil(); @After public void after() throws BonitaException { if (!platformAPI.isNodeStarted()) { platformAPI.startNode(); } platformTestUtil.logoutOnPlatform(session); try { platformTestUtil.deployCommandsOnDefaultTenant(); } catch (AlreadyExistsException ignored) { } } @Before public void before() throws BonitaException { session = platformTestUtil.loginOnPlatform(); platformAPI = PlatformAPIAccessor.getPlatformAPI(session); if (!platformAPI.isNodeStarted()) { platformAPI.startNode(); } } @Rule public TestRule testWatcher = new PrintTestsStatusRule(LOGGER) { @Override public void clean() { } }; @Test public void isPlatformCreated() throws BonitaException { assertTrue(platformAPI.isPlatformCreated()); } @Test public void getPlatformState() throws Exception { // test started state PlatformState state = platformAPI.getPlatformState(); assertEquals(PlatformState.STARTED, state); // test stopped state platformAPI.stopNode(); state = platformAPI.getPlatformState(); assertEquals(PlatformState.STOPPED, state); // test exception:PlatformNotFoundException } @Test public void callStopNodeTwice() throws Exception { platformAPI.stopNode(); platformAPI.stopNode(); } @Test public void stopNodeAndStartNode() throws Exception { final LoginAPI loginAPI = TenantAPIAccessor.getLoginAPI(); final APISession tenantSession = loginAPI.login("install", "install"); final IdentityAPI identityAPI = TenantAPIAccessor.getIdentityAPI(tenantSession); identityAPI.getNumberOfUsers(); restartPlatform(); try { identityAPI.getNumberOfUsers(); fail("session should not work"); } catch (final InvalidSessionException e) { // ok } } private static void restartPlatform() throws StopNodeException, StartNodeException { platformAPI.stopNode(); platformAPI.startNode(); } @Test public void should_have_processes_with_duration_timer_still_work_after_restart() throws Exception { APIClient apiClient = new APIClient(); apiClient.login("install", "install"); ProcessDefinition wait2Sec = apiClient.getProcessAPI() .deployAndEnableProcess(new ProcessDefinitionBuilder() .createNewInstance("a process with 2 sec intermediate timer", "1.0") .addIntermediateCatchEvent("wait2Sec").addTimerEventTriggerDefinition(TimerType.DURATION, new ExpressionBuilder().createConstantLongExpression(2000)) .getProcess()); for (int i = 0; i < 20; i++) { apiClient.getProcessAPI().startProcess(wait2Sec.getId()); } await().until(() -> apiClient.getProcessAPI().getNumberOfProcessInstances(), nb -> nb > 0L); Assertions.assertThat(apiClient.getProcessAPI().getNumberOfProcessInstances()).isGreaterThan(0); restartPlatform(); apiClient.login("install", "install"); await().atMost(Duration.ofMinutes(4)).until(() -> apiClient.getProcessAPI().getNumberOfProcessInstances(), nb -> nb == 0L); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/platform/PlatformLoginIT.java ================================================ /** * 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.platform; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.SessionNotFoundException; import org.bonitasoft.engine.session.impl.PlatformSessionImpl; import org.junit.Test; public class PlatformLoginIT extends CommonAPIIT { @Test(expected = InvalidPlatformCredentialsException.class) public void login_with_bad_credentials_should_throw_InvalidPlatformCredentialsException() throws BonitaException { PlatformAPIAccessor.getPlatformLoginAPI().login("bad", "bad"); } @Test(expected = SessionNotFoundException.class) public void logout_with_bad_session_should_throw_SessionNotFound() throws BonitaException { PlatformAPIAccessor.getPlatformLoginAPI().logout(new PlatformSessionImpl(123L, null, -1L, null, -1L)); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/platform/command/PlatformCommandIT.java ================================================ /** * 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.platform.command; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import java.io.IOException; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.api.PlatformCommandAPI; import org.bonitasoft.engine.command.CommandDescriptor; import org.bonitasoft.engine.command.CommandNotFoundException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.PlatformSession; import org.junit.After; import org.junit.Before; import org.junit.Test; public class PlatformCommandIT extends CommonAPIIT { private static PlatformCommandAPI platformCommandAPI; private static PlatformSession session; @Before public void before() throws BonitaException, IOException { session = loginOnPlatform(); platformCommandAPI = PlatformAPIAccessor.getPlatformCommandAPI(session); } @After public void after() throws BonitaException { logoutOnPlatform(session); } @Test public void createPlatformCommand() throws BonitaException { try { platformCommandAPI.addDependency("commands", "jar".getBytes()); final CommandDescriptor command = platformCommandAPI.register("testPlatformCommand", "command description", "implementation"); assertNotNull(command); assertEquals("testPlatformCommand", command.getName()); assertEquals("command description", command.getDescription()); } finally { platformCommandAPI.unregister("testPlatformCommand"); platformCommandAPI.removeDependency("commands"); } } @Test public void deletePlatformCommand() throws BonitaException { platformCommandAPI.addDependency("commands", "jar".getBytes()); platformCommandAPI.register("platformCommand1", "command description", "implementation"); final CommandDescriptor command = platformCommandAPI.getCommand("platformCommand1"); assertNotNull(command); platformCommandAPI.unregister("platformCommand1"); platformCommandAPI.removeDependency("commands"); try { platformCommandAPI.getCommand("platformCommand1"); fail("command should be deleted"); } catch (final CommandNotFoundException ignored) { } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/EvaluateExpressionIT.java ================================================ /** * 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.process; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ActivityDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.TransitionDefinitionBuilder; import org.bonitasoft.engine.connector.EngineExecutionContext; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.expression.ExpressionEvaluationException; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.identity.User; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Zhao Na * @author Baptiste Mesta * @author Celine Souchet */ public class EvaluateExpressionIT extends TestWithUser { private static final String STEP2_NAME = "Approval"; private static final String STEP1_NAME = "Request"; public static final String EXPECTED_EVALUATED_DATE = "2013-07-18 14:49:26 GMT+02:00plop"; private ProcessDefinition processDefinition = null; private ProcessInstance processInstance = null; private Map> expressions; @Override @Before public void before() throws Exception { super.before(); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addData("stringData", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("Word")); processDefinitionBuilder.addDateData("dateData", new ExpressionBuilder().createConstantDateExpression("2013-07-18T14:49:26.86+02:00")); processDefinitionBuilder.addData("doubleData", Double.class.getName(), new ExpressionBuilder().createConstantDoubleExpression(2D)); processDefinitionBuilder.addData("longData", Long.class.getName(), new ExpressionBuilder().createConstantLongExpression(1L)); processDefinitionBuilder.addData("booleanData", Boolean.class.getName(), new ExpressionBuilder().createConstantBooleanExpression(true)); processDefinitionBuilder.addData("floatData", Float.class.getName(), new ExpressionBuilder().createConstantFloatExpression(100F)); processDefinitionBuilder.addData("integerData", Integer.class.getName(), new ExpressionBuilder().createConstantIntegerExpression(4)); processDefinitionBuilder.addData("processData", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("processData")); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask(STEP1_NAME, ACTOR_NAME); processDefinitionBuilder.addUserTask(STEP2_NAME, ACTOR_NAME); processDefinitionBuilder.addTransition(STEP1_NAME, STEP2_NAME); processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); processInstance = getProcessAPI().startProcess(processDefinition.getId()); final List stringDependencies = new ArrayList<>(); stringDependencies.add(new ExpressionBuilder().createDataExpression("stringData", String.class.getName())); stringDependencies.add(new ExpressionBuilder().createInputExpression("field_string", String.class.getName())); final Expression stringExpression = new ExpressionBuilder().createGroovyScriptExpression("StringScript", "stringData + \"-\" + field_string", String.class.getName(), stringDependencies); final Map fieldValues = new HashMap<>(); fieldValues.put("field_string", "Excel"); final List dateDependencies = Collections .singletonList(new ExpressionBuilder().createDataExpression("dateData", Date.class.getName())); final Expression dateExpression = new ExpressionBuilder().createGroovyScriptExpression("DateScript", "import java.text.SimpleDateFormat;import java.util.TimeZone;" + "SimpleDateFormat dateFormat = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss zzz\"); dateFormat.setTimeZone(TimeZone.getTimeZone(\"GMT+2\"));" + "dateFormat.format(dateData) + \"plop\"", String.class.getName(), dateDependencies); final List longDependencies = Collections .singletonList(new ExpressionBuilder().createDataExpression("longData", Long.class.getName())); final Expression longExpression = new ExpressionBuilder().createGroovyScriptExpression("LongScript", "String.valueOf(longData)+ \"plop\"", String.class.getName(), longDependencies); final List doubleDependencies = Collections .singletonList(new ExpressionBuilder().createDataExpression("doubleData", Double.class.getName())); final Expression doubleExpression = new ExpressionBuilder().createGroovyScriptExpression("DoubleScript", "String.valueOf(doubleData) + \"plop\"", String.class.getName(), doubleDependencies); final List booleanDependencies = Collections .singletonList(new ExpressionBuilder().createDataExpression("booleanData", Boolean.class.getName())); final Expression booleanExpression = new ExpressionBuilder().createGroovyScriptExpression("BooleanScript", "booleanData && false", Boolean.class.getName(), booleanDependencies); final List floatDependencies = Collections .singletonList(new ExpressionBuilder().createDataExpression("floatData", Float.class.getName())); final Expression floatExpression = new ExpressionBuilder().createGroovyScriptExpression("FloatScript", "String.valueOf(floatData) + \"plop\"", String.class.getName(), floatDependencies); final List integerDependencies = Collections .singletonList(new ExpressionBuilder().createDataExpression("integerData", Integer.class.getName())); final Expression integerExpression = new ExpressionBuilder().createGroovyScriptExpression("IntegerScript", "String.valueOf(integerData) + \"plop\"", String.class.getName(), integerDependencies); final Expression constantStringExpression = new ExpressionBuilder().createNewInstance("Word").setContent("") .setExpressionType(ExpressionType.TYPE_CONSTANT).setReturnType(String.class.getName()).done(); expressions = new HashMap<>(); expressions.put(stringExpression, fieldValues); expressions.put(dateExpression, new HashMap()); expressions.put(longExpression, new HashMap()); expressions.put(doubleExpression, new HashMap()); expressions.put(booleanExpression, new HashMap()); expressions.put(floatExpression, new HashMap()); expressions.put(integerExpression, new HashMap()); expressions.put(constantStringExpression, new HashMap()); expressions.put(new ExpressionBuilder().createDataExpression("processData", String.class.getName()), new HashMap()); } @Override @After public void after() throws Exception { disableAndDeleteProcess(processDefinition); super.after(); } private static ProcessDefinitionBuilder createProcessDefinitionBuilderWithHumanAndAutomaticSteps( final String processName, final String processVersion, final List stepNames, final List isHuman) { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(processName, processVersion); processBuilder.addActor("Actor1"); ActivityDefinitionBuilder activityDefinitionBuilder = null; for (int i = 0; i < stepNames.size(); i++) { final String stepName = stepNames.get(i); if (isHuman.get(i)) { if (activityDefinitionBuilder != null) { activityDefinitionBuilder = activityDefinitionBuilder.addUserTask(stepName, "Actor1"); } else { activityDefinitionBuilder = processBuilder.addUserTask(stepName, "Actor1"); } } else { if (activityDefinitionBuilder != null) { activityDefinitionBuilder = activityDefinitionBuilder.addAutomaticTask(stepName); } else { activityDefinitionBuilder = processBuilder.addAutomaticTask(stepName); } } } TransitionDefinitionBuilder transitionDefinitionBuilder = null; for (int i = 0; i < stepNames.size() - 1; i++) { if (transitionDefinitionBuilder != null) { transitionDefinitionBuilder = transitionDefinitionBuilder.addTransition(stepNames.get(i), stepNames.get(i + 1)); } else { transitionDefinitionBuilder = activityDefinitionBuilder.addTransition(stepNames.get(i), stepNames.get(i + 1)); } } return processBuilder; } private void cleanup(final long processDefinitionId) throws BonitaException { disableAndDeleteProcess(processDefinitionId); } private ProcessDefinition createAndDeployProcessDefinitionAndInstance(final String dataName, final int value, final boolean isHuman, final User user) throws Exception { // create data expression final Expression dataDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(value); // create a processDefinition with data and parameter... final DesignProcessDefinition processDef = createProcessDefinitionBuilderWithHumanAndAutomaticSteps( "My_Process", "1.0", Collections.singletonList("step1"), Collections.singletonList(isHuman)) .addIntegerData(dataName, dataDefaultExp) .addDescription("Delivery all day and night long").getProcess(); return deployAndEnableProcessWithActor(processDef, "Actor1", user); } @Test public void evaluateExpressionsAtProcessInstantiation() throws Exception { waitForPendingTasks(getSession().getUserId(), 1); final Map result = getProcessAPI() .evaluateExpressionsAtProcessInstanciation(processInstance.getId(), expressions); assertEquals("Word-Excel", result.get("StringScript")); assertEquals(EXPECTED_EVALUATED_DATE, result.get("DateScript")); assertEquals("1plop", result.get("LongScript")); assertEquals("2.0plop", result.get("DoubleScript")); assertEquals(false, result.get("BooleanScript")); assertEquals("100.0plop", result.get("FloatScript")); assertEquals("4plop", result.get("IntegerScript")); } @Test(expected = ExpressionEvaluationException.class) public void evaluateExpressionsAtProcessInstantiationWithWrongProcessInstanceId() throws Exception { getProcessAPI().evaluateExpressionsAtProcessInstanciation(36, expressions); } @Test public void evaluateExpressionsOnCompletedActivityInstance() throws Exception { final long step1Id = waitForUserTaskAndExecuteIt(processInstance, STEP1_NAME, user); final Map result = getProcessAPI().evaluateExpressionsOnCompletedActivityInstance(step1Id, expressions); assertEquals("Word-Excel", result.get("StringScript")); assertEquals(EXPECTED_EVALUATED_DATE, result.get("DateScript")); assertEquals("1plop", result.get("LongScript")); assertEquals("2.0plop", result.get("DoubleScript")); assertEquals(false, result.get("BooleanScript")); assertEquals("100.0plop", result.get("FloatScript")); assertEquals("4plop", result.get("IntegerScript")); assertEquals("processData", result.get("processData")); } @Test public void evaluateExpressionsOnCompletedActivityInstanceWithArchivedProcess() throws Exception { final long step1Id = waitForUserTaskAndExecuteIt(processInstance, STEP1_NAME, user); waitForUserTaskAndExecuteIt(processInstance, STEP2_NAME, user); waitForProcessToFinish(processInstance); final Map result = getProcessAPI().evaluateExpressionsOnCompletedActivityInstance(step1Id, expressions); assertEquals("Word-Excel", result.get("StringScript")); assertEquals(EXPECTED_EVALUATED_DATE, result.get("DateScript")); assertEquals("1plop", result.get("LongScript")); assertEquals("2.0plop", result.get("DoubleScript")); assertEquals(false, result.get("BooleanScript")); assertEquals("100.0plop", result.get("FloatScript")); assertEquals("4plop", result.get("IntegerScript")); assertEquals("processData", result.get("processData")); } @Test public void evaluateExpressionsOnCompletedActivityInstanceInSubProcess() throws Exception { ProcessDefinitionBuilder caller = new ProcessDefinitionBuilder().createNewInstance("Caller", "1"); caller.addCallActivity("callActivity", new ExpressionBuilder().createConstantStringExpression(PROCESS_NAME), new ExpressionBuilder().createConstantStringExpression(PROCESS_VERSION)); ProcessDefinition callerProcess = deployAndEnableProcess(caller.done()); ProcessInstance callerInstance = getProcessAPI().startProcess(callerProcess.getId()); final long step1Id = waitForUserTaskAndExecuteIt(callerInstance, STEP1_NAME, user); waitForUserTaskAndExecuteIt(callerInstance, STEP2_NAME, user); waitForProcessToFinish(callerInstance); final Map result = getProcessAPI().evaluateExpressionsOnCompletedActivityInstance(step1Id, expressions); assertEquals("Word-Excel", result.get("StringScript")); assertEquals(EXPECTED_EVALUATED_DATE, result.get("DateScript")); assertEquals("1plop", result.get("LongScript")); assertEquals("2.0plop", result.get("DoubleScript")); assertEquals(false, result.get("BooleanScript")); assertEquals("100.0plop", result.get("FloatScript")); assertEquals("4plop", result.get("IntegerScript")); assertEquals("processData", result.get("processData")); disableAndDeleteProcess(callerProcess); } @Test(expected = ExpressionEvaluationException.class) public void evaluateExpressionsOnCompletedActivityInstanceWithWrongActivityInstanceId() throws Exception { getProcessAPI().evaluateExpressionsOnCompletedActivityInstance(36, expressions); } @Test public void evaluateExpressionsOnCompletedProcessInstance() throws Exception { waitForUserTaskAndExecuteIt(processInstance, STEP1_NAME, user); waitForUserTaskAndExecuteIt(processInstance, STEP2_NAME, user); waitForProcessToFinish(processInstance); final Map result = getProcessAPI() .evaluateExpressionOnCompletedProcessInstance(processInstance.getId(), expressions); assertEquals("Word-Excel", result.get("StringScript")); assertEquals(EXPECTED_EVALUATED_DATE, result.get("DateScript")); assertEquals("1plop", result.get("LongScript")); assertEquals("2.0plop", result.get("DoubleScript")); assertEquals(false, result.get("BooleanScript")); assertEquals("100.0plop", result.get("FloatScript")); assertEquals("4plop", result.get("IntegerScript")); assertEquals("processData", result.get("processData")); } @Test public void evaluateExpressionsOnCompletedProcessInstanceAfterVariableUpdate() throws Exception { waitForUserTaskAndExecuteIt(processInstance, STEP1_NAME, user); getProcessAPI().updateProcessDataInstance("stringData", processInstance.getId(), "Plop"); waitForUserTaskAndExecuteIt(processInstance, STEP2_NAME, user); waitForProcessToFinish(processInstance); final Map result = getProcessAPI() .evaluateExpressionOnCompletedProcessInstance(processInstance.getId(), expressions); assertEquals( "if Word-Excel is returned, it means the values of the variable used are the latest ones whereas it should be the ones of when the activity was submited", "Plop-Excel", result.get("StringScript")); assertEquals(EXPECTED_EVALUATED_DATE, result.get("DateScript")); assertEquals("1plop", result.get("LongScript")); assertEquals("2.0plop", result.get("DoubleScript")); assertEquals(false, result.get("BooleanScript")); assertEquals("100.0plop", result.get("FloatScript")); assertEquals("4plop", result.get("IntegerScript")); assertEquals("processData", result.get("processData")); } @Test public void evaluateExpressionsOnActivityInstance() throws Exception { final long step1Id = waitForUserTask(processInstance, STEP1_NAME); final Map result = getProcessAPI().evaluateExpressionsOnActivityInstance(step1Id, expressions); assertEquals("Word-Excel", result.get("StringScript")); assertEquals(EXPECTED_EVALUATED_DATE, result.get("DateScript")); assertEquals("1plop", result.get("LongScript")); assertEquals("2.0plop", result.get("DoubleScript")); assertEquals(false, result.get("BooleanScript")); assertEquals("100.0plop", result.get("FloatScript")); assertEquals("4plop", result.get("IntegerScript")); assertEquals("", result.get("Word")); assertEquals("processData", result.get("processData")); } @Test(expected = ExpressionEvaluationException.class) public void evaluateExpressionsOnActivityInstanceWithWrongActivityInstanceId() throws Exception { getProcessAPI().evaluateExpressionsOnActivityInstance(36, expressions); } @Test public void evaluateAssigneeId() throws Exception { final ActivityInstance userTaskInstance = waitForUserTaskAndAssignIt(processInstance, STEP1_NAME, user); final Expression taskAssigneeExpr = new ExpressionBuilder() .createEngineConstant(ExpressionConstants.TASK_ASSIGNEE_ID); final Expression engineExecContextExpr = new ExpressionBuilder() .createEngineConstant(ExpressionConstants.ENGINE_EXECUTION_CONTEXT); final Map> engineExpresssions = new HashMap<>(); engineExpresssions.put(taskAssigneeExpr, Collections. emptyMap()); engineExpresssions.put(engineExecContextExpr, Collections. emptyMap()); final Map result = getProcessAPI() .evaluateExpressionsOnActivityInstance(userTaskInstance.getId(), engineExpresssions); assertEquals(user.getId(), result.get(taskAssigneeExpr.getContent())); assertEquals(user.getId(), ((EngineExecutionContext) result.get(engineExecContextExpr.getContent())).getTaskAssigneeId()); } @Test public void evaluateExpressionsOnProcessInstanceWithInitialValues() throws Exception { waitForUserTaskAndExecuteIt(processInstance, STEP1_NAME, user); getProcessAPI().updateProcessDataInstance("stringData", processInstance.getId(), "Excel"); final Map result = getProcessAPI() .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions); assertFalse("Result should not be empty", result.isEmpty()); assertEquals("Excel-Excel", result.get("StringScript")); assertEquals(EXPECTED_EVALUATED_DATE, result.get("DateScript")); assertEquals("1plop", result.get("LongScript")); assertEquals("2.0plop", result.get("DoubleScript")); assertEquals(false, result.get("BooleanScript")); assertEquals("100.0plop", result.get("FloatScript")); assertEquals("4plop", result.get("IntegerScript")); assertEquals("processData", result.get("processData")); } @Test public void evaluateExpressionsOnProcessInstanceWithCurrentValues() throws Exception { waitForUserTaskAndExecuteIt(processInstance, STEP1_NAME, user); getProcessAPI().updateProcessDataInstance("stringData", processInstance.getId(), "Excel"); final Map result = getProcessAPI() .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions); assertEquals("Excel-Excel", result.get("StringScript")); assertEquals(EXPECTED_EVALUATED_DATE, result.get("DateScript")); assertEquals("1plop", result.get("LongScript")); assertEquals("2.0plop", result.get("DoubleScript")); assertEquals(false, result.get("BooleanScript")); assertEquals("100.0plop", result.get("FloatScript")); assertEquals("4plop", result.get("IntegerScript")); assertEquals("processData", result.get("processData")); } @Test(expected = ExpressionEvaluationException.class) public void evaluateExpressionsOnProcessInstanceWithWrongProcessInstanceId() throws Exception { getProcessAPI().evaluateExpressionsOnProcessInstance(36, expressions); } @Test(expected = ExpressionEvaluationException.class) public void evaluateExpressionsOnProcessInstanceWithNoExpressions() throws Exception { getProcessAPI().evaluateExpressionsOnProcessInstance(1, null); } @Test(expected = ExpressionEvaluationException.class) public void evaluateExpressionsOnProcessDefinition() throws Exception { final Map result = getProcessAPI() .evaluateExpressionsOnProcessDefinition(processDefinition.getId(), expressions); assertFalse("Result should not be empty", result.isEmpty()); assertEquals("Word", result.get("StringScript")); assertEquals(EXPECTED_EVALUATED_DATE, result.get("DateScript")); assertEquals("1plop", result.get("LongScript")); assertEquals("2.0plop", result.get("DoubleScript")); assertEquals(false, result.get("BooleanScript")); assertEquals("100.0plop", result.get("FloatScript")); assertEquals("4plop", result.get("IntegerScript")); assertEquals("processData", result.get("processData")); } @Test(expected = ExpressionEvaluationException.class) public void evaluateExpressionsOnProcessDefinitionWithWrongProcessDefinitionId() throws Exception { getProcessAPI().evaluateExpressionsOnProcessDefinition(36, expressions); } @Test public void evaluatePatternExpression() throws Exception { final String dataName = "birthYear"; // get processInstance final ProcessDefinition processDefinition = createAndDeployProcessDefinitionAndInstance(dataName, 1977, true, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final Expression expData = new ExpressionBuilder().createDataExpression(dataName, Integer.class.getName()); final Expression expConstantExpression = new ExpressionBuilder().createConstantStringExpression("year"); final String messagePattern = "My birth ${year} is ${birthYear}"; final Expression expPattern = new ExpressionBuilder().createPatternExpression("TestEvaluatePatternExpression", messagePattern, expData, expConstantExpression); final Map> expressions = new HashMap<>(); expressions.put(expPattern, null); final Map result = getProcessAPI() .evaluateExpressionsOnProcessInstance(processInstance.getId(), expressions); assertEquals("My birth year is 1977", result.get(messagePattern)); cleanup(processDefinition.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/FlowPatternsIT.java ================================================ /** * 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.process; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.junit.Test; /** * Test common use case of flow pattern in bpmn * * @author Baptiste Mesta */ public class FlowPatternsIT extends TestWithUser { @Test public void process_with_inclusive() throws Exception { final ProcessDefinition processDefinition = deployProcessWithInclusiveGateway(); //all conditions are true ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); long step0 = waitForUserTask("Step0"); getProcessAPI().updateProcessDataInstance("cond1", processInstance.getId(), true); getProcessAPI().updateProcessDataInstance("cond2", processInstance.getId(), true); getProcessAPI().updateProcessDataInstance("cond3", processInstance.getId(), true); getProcessAPI().updateProcessDataInstance("cond4", processInstance.getId(), true); assignAndExecuteStep(step0, user); waitForUserTaskAndExecuteIt(processInstance, "Step1", user); waitForUserTaskAndExecuteIt(processInstance, "Step2", user); waitForUserTaskAndExecuteIt(processInstance, "Step3", user); waitForUserTaskAndExecuteIt(processInstance, "Step4", user); waitForUserTaskAndExecuteIt(processInstance, "Step6", user); waitForProcessToFinish(processInstance.getId()); //all conditions are false processInstance = getProcessAPI().startProcess(processDefinition.getId()); step0 = waitForUserTask("Step0"); getProcessAPI().updateProcessDataInstance("cond1", processInstance.getId(), false); getProcessAPI().updateProcessDataInstance("cond2", processInstance.getId(), false); getProcessAPI().updateProcessDataInstance("cond3", processInstance.getId(), false); getProcessAPI().updateProcessDataInstance("cond4", processInstance.getId(), false); assignAndExecuteStep(step0, user); waitForUserTaskAndExecuteIt(processInstance, "Step5", user); waitForUserTaskAndExecuteIt(processInstance, "Step6", user); waitForProcessToFinish(processInstance.getId()); //some conditions are false processInstance = getProcessAPI().startProcess(processDefinition.getId()); step0 = waitForUserTask("Step0"); getProcessAPI().updateProcessDataInstance("cond1", processInstance.getId(), false); getProcessAPI().updateProcessDataInstance("cond2", processInstance.getId(), true); getProcessAPI().updateProcessDataInstance("cond3", processInstance.getId(), false); getProcessAPI().updateProcessDataInstance("cond4", processInstance.getId(), true); assignAndExecuteStep(step0, user); waitForUserTaskAndExecuteIt(processInstance, "Step2", user); waitForUserTaskAndExecuteIt(processInstance, "Step4", user); waitForUserTaskAndExecuteIt(processInstance, "Step6", user); waitForProcessToFinish(processInstance.getId()); disableAndDeleteProcess(processDefinition); } ProcessDefinition deployProcessWithInclusiveGateway() throws Exception { ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("InclusiveProcess", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addBooleanData("cond1", new ExpressionBuilder().createConstantBooleanExpression(true)); builder.addBooleanData("cond2", new ExpressionBuilder().createConstantBooleanExpression(true)); builder.addBooleanData("cond3", new ExpressionBuilder().createConstantBooleanExpression(true)); builder.addBooleanData("cond4", new ExpressionBuilder().createConstantBooleanExpression(true)); builder.addStartEvent("Start"); builder.addUserTask("Step0", ACTOR_NAME); builder.addUserTask("Step1", ACTOR_NAME); builder.addUserTask("Step2", ACTOR_NAME); builder.addUserTask("Step3", ACTOR_NAME); builder.addUserTask("Step4", ACTOR_NAME); builder.addUserTask("Step5", ACTOR_NAME); builder.addGateway("Gateway1", GatewayType.INCLUSIVE); builder.addGateway("Gateway2", GatewayType.INCLUSIVE); builder.addUserTask("Step6", ACTOR_NAME); builder.addEndEvent("End"); builder.addTransition("Start", "Step0"); builder.addTransition("Step0", "Gateway1"); builder.addTransition("Gateway1", "Step1", new ExpressionBuilder().createDataExpression("cond1", Boolean.class.getName())); builder.addTransition("Gateway1", "Step2", new ExpressionBuilder().createDataExpression("cond2", Boolean.class.getName())); builder.addTransition("Gateway1", "Step3", new ExpressionBuilder().createDataExpression("cond3", Boolean.class.getName())); builder.addTransition("Gateway1", "Step4", new ExpressionBuilder().createDataExpression("cond4", Boolean.class.getName())); builder.addDefaultTransition("Gateway1", "Step5"); builder.addTransition("Step1", "Gateway2"); builder.addTransition("Step2", "Gateway2"); builder.addTransition("Step3", "Gateway2"); builder.addTransition("Step4", "Gateway2"); builder.addTransition("Step5", "Gateway2"); builder.addTransition("Gateway2", "Step6"); builder.addTransition("Step6", "End"); final DesignProcessDefinition designProcessDefinition = builder.getProcess(); return deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); } @Test public void process_that_merge_different_branches() throws Exception { /* * step 1 and 2 are merged into step 4 by gateway 2 and step 3 and step4 are merged by gateway3 */ ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("MultipleMergeProcess", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addStartEvent("Start"); builder.addUserTask("Step1", ACTOR_NAME); builder.addUserTask("Step2", ACTOR_NAME); builder.addUserTask("Step3", ACTOR_NAME); builder.addUserTask("Step4", ACTOR_NAME); builder.addUserTask("Step5", ACTOR_NAME); builder.addGateway("Gateway1", GatewayType.PARALLEL); builder.addGateway("Gateway2", GatewayType.PARALLEL); builder.addGateway("Gateway3", GatewayType.INCLUSIVE); builder.addEndEvent("End"); builder.addTransition("Start", "Gateway1"); builder.addTransition("Gateway1", "Step1"); builder.addTransition("Gateway1", "Step2"); builder.addTransition("Gateway1", "Step3"); builder.addTransition("Step1", "Gateway2"); builder.addTransition("Step2", "Gateway2"); builder.addTransition("Gateway2", "Step4"); builder.addTransition("Step3", "Gateway3"); builder.addTransition("Step4", "Gateway3"); builder.addTransition("Gateway3", "Step5"); builder.addTransition("Step5", "End"); final DesignProcessDefinition designProcessDefinition = builder.getProcess(); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt("Step1", user); waitForUserTaskAndExecuteIt("Step2", user); waitForUserTaskAndExecuteIt("Step4", user); waitForUserTaskAndExecuteIt("Step3", user); waitForUserTaskAndExecuteIt("Step5", user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Test public void process_with_branch_out() throws Exception { ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("MultipleMergeProcess", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addStartEvent("Start"); builder.addUserTask("Step1", ACTOR_NAME); builder.addUserTask("Step2", ACTOR_NAME); builder.addUserTask("Step3", ACTOR_NAME); builder.addUserTask("Step4", ACTOR_NAME); builder.addGateway("Gateway1", GatewayType.PARALLEL); builder.addGateway("Gateway2", GatewayType.PARALLEL); builder.addEndEvent("End1"); builder.addEndEvent("End2"); builder.addTransition("Start", "Gateway1"); builder.addTransition("Gateway1", "Step1"); builder.addTransition("Gateway1", "Step2"); builder.addTransition("Step1", "Gateway2"); builder.addTransition("Step2", "Gateway2"); builder.addTransition("Step2", "Step3"); builder.addTransition("Gateway2", "Step4"); builder.addTransition("Step4", "End1"); builder.addTransition("Step3", "End2"); final DesignProcessDefinition designProcessDefinition = builder.getProcess(); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt("Step2", user); waitForUserTaskAndExecuteIt("Step1", user); waitForUserTaskAndExecuteIt("Step4", user); waitForUserTaskAndExecuteIt("Step3", user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Test public void inclusive_merge_with_no_start() throws Exception { ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("MultipleMergeProcess", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addUserTask("Step1", ACTOR_NAME); builder.addUserTask("Step2", ACTOR_NAME); builder.addUserTask("Step3", ACTOR_NAME); builder.addGateway("Gateway1", GatewayType.INCLUSIVE); builder.addTransition("Step1", "Gateway1"); builder.addTransition("Step2", "Gateway1"); builder.addTransition("Gateway1", "Step3"); final DesignProcessDefinition designProcessDefinition = builder.getProcess(); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt("Step1", user); waitForUserTaskAndExecuteIt("Step2", user); waitForUserTaskAndExecuteIt("Step3", user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/GatewayExecutionIT.java ================================================ /** * 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.process; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.api.PlatformAPI; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.GatewayInstance; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.TimerType; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.ComparisonOperator; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.engine.test.TestStates; import org.bonitasoft.engine.test.WaitUntil; import org.bonitasoft.engine.test.check.CheckNbPendingTaskOf; import org.junit.Test; public class GatewayExecutionIT extends TestWithUser { private Expression trueExpression; private Expression falseExpression; @Test public void archiveGatewayInstance() throws Exception { createTrueAndFalseExpression(); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("My_Process", PROCESS_VERSION); builder.addActor(ACTOR_NAME).addDescription("description"); builder.addAutomaticTask("step1"); builder.addUserTask("step2", ACTOR_NAME); builder.addUserTask("step3", ACTOR_NAME); builder.addUserTask("step4", ACTOR_NAME); builder.addGateway("d'stàp", GatewayType.INCLUSIVE) .addDisplayDescriptionAfterCompletion( new ExpressionBuilder().createConstantStringExpression("description after completion")) .addDisplayName(new ExpressionBuilder().createConstantStringExpression("display name")); builder.addTransition("step1", "d'stàp"); builder.addTransition("d'stàp", "step2", falseExpression); builder.addTransition("d'stàp", "step3", falseExpression) .addDefaultTransition("d'stàp", "step4"); final DesignProcessDefinition designProcessDefinition = builder.getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // create gateway instance and transition instance and archive them final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); waitForUserTask(processInstance, "step4"); // test gateway instance, gateway instance has been deleted after archive final SearchOptionsBuilder builder0 = new SearchOptionsBuilder(0, 10); builder0.filter(FlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, processInstance.getId()); builder0.filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, "completed"); builder0.filter(FlowNodeInstanceSearchDescriptor.NAME, "d'stàp"); final SearchResult searchResult0 = getProcessAPI().searchFlowNodeInstances(builder0.done()); assertEquals(0, searchResult0.getCount()); // search archive gateway instances: SearchOptionsBuilder builder1 = new SearchOptionsBuilder(0, 10); builder1.filter(ArchivedFlowNodeInstanceSearchDescriptor.FLOW_NODE_TYPE, "gate"); builder1.filter(ArchivedFlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, processInstance.getId()); final SearchResult searchResult1 = getProcessAPI() .searchArchivedFlowNodeInstances(builder1.done()); // we expect all normal gateway states to be archived: assertEquals(getProcessAPI().getSupportedStates(FlowNodeType.GATEWAY).size(), searchResult1.getCount()); // check display name/description builder1 = new SearchOptionsBuilder(0, 10); builder1.filter(ArchivedFlowNodeInstanceSearchDescriptor.FLOW_NODE_TYPE, "gate"); builder1.filter(ArchivedFlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, processInstance.getId()); builder1.filter(ArchivedFlowNodeInstanceSearchDescriptor.NAME, "d'stàp"); builder1.filter(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, "completed"); final ArchivedFlowNodeInstance gatewayOne = getProcessAPI().searchArchivedFlowNodeInstances(builder1.done()) .getResult().get(0); // we expect all normal gateway states to be archived: assertThat(gatewayOne.getDisplayName()).isEqualTo("display name"); assertThat(gatewayOne.getDisplayDescription()).isEqualTo("description after completion"); // clean disableAndDeleteProcess(processDefinition); } /** * Parallel gateway with * 1 automatic step in input * 2 user tasks in output * > 2 tasks for john expected */ @Test public void processWithParallelGatewaySplit() throws Exception { // test initialization (will be extracted in other methods final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addGateway("gateway1", GatewayType.PARALLEL) .addTransition("step1", "gateway1").addTransition("gateway1", "step2") .addTransition("gateway1", "step3").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // test execution final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); // we should have 2 elements ready: final long step2Id = waitForUserTask(processInstance, "step2"); final long step3Id = waitForUserTask(processInstance, "step3"); assignAndExecuteStep(step2Id, user); assignAndExecuteStep(step3Id, user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } /** * Parallel gateway with * 2 automatic step in input * 1 user task in output * > 1 task for john expected */ @Test public void processWithParallelGatewayMerge() throws Exception { //given final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1").addAutomaticTask("step2").addAutomaticTask("step3") .addUserTask("step4", ACTOR_NAME) .addGateway("gateway1", GatewayType.PARALLEL).addGateway("gateway2", GatewayType.PARALLEL) .addTransition("step1", "gateway1") .addTransition("gateway1", "step2").addTransition("gateway1", "step3") .addTransition("step2", "gateway2").addTransition("step3", "gateway2") .addTransition("gateway2", "step4").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); // when final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); // then waitForUserTaskAndExecuteIt(processInstance, "step4", user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } /** * Parallel gateway with * 2 automatic step in input * 1 user task in output * > 1 task for john expected */ @Test public void processMultiMerge() throws Exception { final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1").addAutomaticTask("step2").addAutomaticTask("step3") .addUserTask("step4", ACTOR_NAME) .addGateway("gateway1", GatewayType.PARALLEL).addTransition("step1", "gateway1") .addTransition("gateway1", "step2") .addTransition("gateway1", "step3").addTransition("step2", "step4").addTransition("step3", "step4") .getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step4", "step4"); } /** * Parallel gateway with * used as a XOR Gateway * Exception expected */ @Test(expected = InvalidProcessDefinitionException.class) public void parallelWithConditionalTransitions() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1") .addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addGateway("gateway1", GatewayType.PARALLEL) .addTransition("step1", "gateway1").addTransition("gateway1", "step2", trueExpression) .addTransition("gateway1", "step3", falseExpression) .getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step2", "step3"); } /* * Exclusive gateway with * unconditionnal output transitions * Expected : step 2 */ @Test public void exclusiveWithUnconditionalTransitions() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_exclusive_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1") .addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addGateway("gateway1", GatewayType.EXCLUSIVE) .addTransition("step1", "gateway1").addTransition("gateway1", "step2") .addTransition("gateway1", "step3").getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step2"); } /* * Inclusive gateway with * unconditionnal output transitions * Expected : step2, step3 */ @Test public void inclusiveWithUnconditionalTransitions() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_inclusive_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1") .addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addGateway("gateway1", GatewayType.INCLUSIVE) .addTransition("step1", "gateway1").addTransition("gateway1", "step2") .addTransition("gateway1", "step3").getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step2", "step3"); } /** * Linear process * with auto task -> user task -> auto task -> user task * 1 task pending + 1 task pending for John */ @Test public void linearProcessWith2UserTasks() throws Exception { final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1") .addUserTask("step2", ACTOR_NAME).addAutomaticTask("step3").addUserTask("step4", ACTOR_NAME) .addTransition("step1", "step2") .addTransition("step2", "step3").addTransition("step3", "step4").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); // we should have 2 elements ready: final CheckNbPendingTaskOf checkNbPendingTaskOf = new CheckNbPendingTaskOf(getProcessAPI(), 300, 5000, true, 1, user); assertTrue("there was no pending task for john (expected step2)", checkNbPendingTaskOf.waitUntil()); final List pendingTasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, null); assertEquals(1, pendingTasks.size()); final HumanTaskInstance humanTaskInstance = pendingTasks.get(0); assertEquals("step2", humanTaskInstance.getName()); assignAndExecuteStep(humanTaskInstance, user.getId()); assertTrue("there was no pending task for john (expected step4)", checkNbPendingTaskOf.waitUntil()); final List pendingTasks2 = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, null); assertEquals(1, pendingTasks2.size()); final HumanTaskInstance humanTaskInstance2 = pendingTasks2.get(0); assertEquals("step4", humanTaskInstance2.getName()); assignAndExecuteStep(humanTaskInstance2, user.getId()); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } /** * Exclusive gateway with * 1 automatic step in input * 1 user task in output * > 1 task for john expected */ @Test public void processWithExclusiveGatewayWith1InputAnd1Output() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME) .addGateway("gateway1", GatewayType.EXCLUSIVE).addTransition("step1", "gateway1") .addTransition("gateway1", "step2", trueExpression).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // test execution final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); // we should have 1 elements ready: waitForUserTaskAndExecuteIt(processInstance, "step2", user); disableAndDeleteProcess(processDefinition); } @Test public void processWithExclusiveAndInclusive() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addStartEvent("start") .addUserTask("step1", ACTOR_NAME).addGateway("para1", GatewayType.PARALLEL).addAutomaticTask("step2") .addGateway("inclu1", GatewayType.INCLUSIVE).addAutomaticTask("step3").addAutomaticTask("step4") .addGateway("inclu2", GatewayType.INCLUSIVE) .addEndEvent("end").addTerminateEventTrigger().addGateway("para2", GatewayType.PARALLEL) .addTransition("start", "step1") .addTransition("step1", "para1").addTransition("para1", "step2").addTransition("para1", "inclu1") .addTransition("inclu1", "step3", new ExpressionBuilder().createConstantBooleanExpression(true)) .addTransition("inclu1", "step4", new ExpressionBuilder().createConstantBooleanExpression(false)) .addTransition("step4", "inclu2") .addTransition("step3", "inclu2").addTransition("inclu2", "para2").addTransition("step2", "para2") .addTransition("para2", "end").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); final ProcessInstance startProcess = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); waitForUserTaskAndExecuteIt(startProcess, "step1", user); waitForProcessToFinish(startProcess); disableAndDeleteProcess(processDefinition); } /** * exclusive gateway * 3 output: * step2 condition = true * step3 condition = true * step4 = default * expected step2 */ @Test public void exclusiveSplit1() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addUserTask("step4", ACTOR_NAME) .addGateway("gateway1", GatewayType.EXCLUSIVE).addTransition("step1", "gateway1") .addTransition("gateway1", "step2", trueExpression) .addTransition("gateway1", "step3", trueExpression).addDefaultTransition("gateway1", "step4") .getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step2"); } /** * exclusive gateway * 3 output: * step3 condition = true * step2 condition = true * step4 = default * expected step3 */ @Test public void exclusiveSplit1bis() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addUserTask("step4", ACTOR_NAME) .addGateway("gateway1", GatewayType.EXCLUSIVE).addTransition("step1", "gateway1") .addTransition("gateway1", "step3", trueExpression) .addTransition("gateway1", "step2", trueExpression).addDefaultTransition("gateway1", "step4") .getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step3"); } /** * exclusive gateway * 3 output: * step2 condition = false * step3 condition = true * step4 = default * expected step3 */ @Test public void exclusiveSplit2() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addUserTask("step4", ACTOR_NAME) .addGateway("gateway1", GatewayType.EXCLUSIVE).addTransition("step1", "gateway1") .addTransition("gateway1", "step2", falseExpression) .addTransition("gateway1", "step3", trueExpression).addDefaultTransition("gateway1", "step4") .getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step3"); } /** * exclusive gateway * 3 output: * step2 condition = false * step3 condition = false * step4 = default * expected step4 */ @Test public void exclusiveSplit3() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addUserTask("step4", ACTOR_NAME) .addGateway("gateway1", GatewayType.EXCLUSIVE).addTransition("step1", "gateway1") .addTransition("gateway1", "step2", falseExpression) .addTransition("gateway1", "step3", falseExpression).addDefaultTransition("gateway1", "step4") .getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step4"); } /** * exclusive gateway * 3 output: * step2 condition = false * step3 condition = false * expected exception: no default gateway is defined */ @Test public void exclusiveSplitWithNoDefaultTransition() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addGateway("gateway1", GatewayType.EXCLUSIVE) .addTransition("step1", "gateway1").addTransition("gateway1", "step2", falseExpression) .addTransition("gateway1", "step3", falseExpression) .getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, ""); } /** * exclusive gateway * 1 output: * step2 condition = false * gateway fail * fix and restart gateway * expected step2 ready */ @Test public void exclusiveSplitWithNoDefaultTransitionFailThenRestart() throws Exception { createTrueAndFalseExpression(); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "My_Process_with_parallel_gateway", PROCESS_VERSION); processDefinitionBuilder.addActor("actor").addAutomaticTask("step1").addUserTask("step2", "actor") .addGateway("gateway1", GatewayType.EXCLUSIVE) .addTransition("step1", "gateway1") .addTransition("gateway1", "step2", new ExpressionBuilder().createDataExpression("condition", Boolean.class.getName())) .getProcess(); processDefinitionBuilder.addData("condition", Boolean.class.getName(), falseExpression); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), "actor", user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // the gateway failed final FlowNodeInstance gateway = waitForFlowNodeInFailedState(processInstance); final SearchOptions searchOptions = new SearchOptionsBuilder(0, 1) .filter(FlowNodeInstanceSearchDescriptor.NAME, "gateway1").done(); final SearchResult searchFlowNodeInstances = getProcessAPI() .searchFlowNodeInstances(searchOptions); final FlowNodeInstance flowNodeInstance = searchFlowNodeInstances.getResult().get(0); assertTrue(flowNodeInstance instanceof GatewayInstance); // retry the gateway getProcessAPI().retryTask(gateway.getId()); waitForFlowNodeInFailedState(processInstance); // should still be in failed final SearchResult searchFlowNodeInstances2 = getProcessAPI() .searchFlowNodeInstances(searchOptions); assertEquals("failed", searchFlowNodeInstances2.getResult().get(0).getState()); // change value of condition to make it work getProcessAPI().updateProcessDataInstance("condition", processInstance.getId(), true); // retry the gateway getProcessAPI().retryTask(gateway.getId()); // we should have step2 ready waitForUserTask(processInstance, "step2"); disableAndDeleteProcess(processDefinition); } /** * inclusive gateway * 3 output: * step2 condition = true * step3 condition = true * step4 = default * expected step2 + step3 */ @Test public void inclusiveSplit1() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1") .addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME).addUserTask("step4", ACTOR_NAME) .addGateway("gateway1", GatewayType.INCLUSIVE).addTransition("step1", "gateway1") .addTransition("gateway1", "step2", trueExpression) .addTransition("gateway1", "step3", trueExpression).addDefaultTransition("gateway1", "step4") .getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step2", "step3"); } /** * inclusive gateway * 3 output: * step2 condition = false * step3 condition = true * step4 = default * expected step3 */ @Test public void inclusiveSplit2() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addUserTask("step4", ACTOR_NAME) .addGateway("gateway1", GatewayType.INCLUSIVE).addTransition("step1", "gateway1") .addTransition("gateway1", "step2", falseExpression) .addTransition("gateway1", "step3", trueExpression).addDefaultTransition("gateway1", "step4") .getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step3"); } /** * inclusive gateway * 3 output: * step2 condition = false * step3 condition = true * step4 = default * expected step3 */ @Test public void inclusiveSplit2WithDataAsCondition() throws Exception { createTrueAndFalseExpression(); final Expression trueData = new ExpressionBuilder().createDataExpression("trueData", Boolean.class.getName()); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "My_Process_with_parallel_gateway", PROCESS_VERSION); processDefinitionBuilder.addBooleanData("trueData", trueExpression); final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder .addActor(ACTOR_NAME) .addAutomaticTask("step1") .addUserTask("step2", ACTOR_NAME) .addUserTask("step3", ACTOR_NAME) .addUserTask("step4", ACTOR_NAME) .addGateway("gateway1", GatewayType.INCLUSIVE) .addTransition("step1", "gateway1") .addTransition( "gateway1", "step2", new ExpressionBuilder().createGroovyScriptExpression("inclusiveSplit2WithDataAsCondition", "!trueData", Boolean.class.getName(), Arrays.asList(trueData))) .addTransition("gateway1", "step3", trueData).addDefaultTransition("gateway1", "step4").getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step3"); } /** * exclusive gateway * 3 output: * step2 condition = false * step3 condition = false * step4 = default * expected step4 */ @Test public void inclusiveSplit3() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addUserTask("step4", ACTOR_NAME) .addGateway("gateway1", GatewayType.INCLUSIVE).addTransition("step1", "gateway1") .addTransition("gateway1", "step2", falseExpression) .addTransition("gateway1", "step3", falseExpression).addDefaultTransition("gateway1", "step4") .getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step4"); } /** * exclusive gateway * 3 output: * step2 condition = false * step3 condition = false * expected Exception: no default transition on gateway */ @Test public void inclusiveSplitWithNoDefaultTransition() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addGateway("gateway1", GatewayType.INCLUSIVE) .addTransition("step1", "gateway1").addTransition("gateway1", "step2", falseExpression) .addTransition("gateway1", "step3", falseExpression) .getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, ""); } /** * Inclusive then Inclusive Gateway * expected step5 */ @Test public void inclusiveSplitThenInclusiveMerge() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1") .addAutomaticTask("step2").addAutomaticTask("step3").addUserTask("step4", ACTOR_NAME) .addUserTask("step5", ACTOR_NAME) .addGateway("gateway1", GatewayType.INCLUSIVE).addGateway("gateway2", GatewayType.INCLUSIVE) .addTransition("step1", "gateway1") .addTransition("gateway1", "step2", trueExpression).addTransition("gateway1", "step3", trueExpression) .addDefaultTransition("gateway1", "step4").addTransition("step2", "gateway2") .addTransition("step3", "gateway2") .addTransition("step4", "gateway2").addTransition("gateway2", "step5").getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step5"); } /** * Parallel then Inclusive Gateway * step 1 -> parallel gateway1 -> ( step 2, step3 ) -> inclusive gateway2 -> step4 * Dynamic expected : step2 & step3 pending, exec step2, step3 pending , exec step3, step4 pending , exec step4, end * process. */ @Test public void parallelSplitThenInclusiveMerge() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addUserTask("step4", ACTOR_NAME) .addGateway("gateway1", GatewayType.PARALLEL).addGateway("gateway2", GatewayType.INCLUSIVE) .addTransition("step1", "gateway1") .addTransition("gateway1", "step2").addTransition("gateway1", "step3") .addTransition("step2", "gateway2").addTransition("step3", "gateway2") .addTransition("gateway2", "step4").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // test execution final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); // we should have 2 elements ready: final long step3Id = waitForUserTask(processInstance, "step3"); waitForUserTaskAndExecuteIt(processInstance, "step2", user); final CheckNbPendingTaskOf checkNbPendingTaskOf2 = new CheckNbPendingTaskOf(getProcessAPI(), 50, 2000, true, 2, user); assertFalse("there was no pending task for john (expected step3)", checkNbPendingTaskOf2.waitUntil()); assignAndExecuteStep(step3Id, user); waitForUserTaskAndExecuteIt(processInstance, "step4", user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } /** * Notify inclusive gateway that a branch died * inclusive1 -> step1+step2 -> inclusive2 -> step3 * complete step2 * step1 is interrupted by a boundary * exception path of boundary is finished * expected: step3 is active because the inclusive2 was notified */ @Test public void notifyInclusiveGatewayThatABranchDied() throws Exception { createTrueAndFalseExpression(); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("NotifiedBranchDeadProcess", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addStartEvent("start"); builder.addGateway("inclusive1", GatewayType.INCLUSIVE); builder.addUserTask("step1", ACTOR_NAME).addBoundaryEvent("signal", true).addSignalEventTrigger("bip"); builder.addUserTask("exceptionStep", ACTOR_NAME); builder.addUserTask("step2", ACTOR_NAME); builder.addGateway("inclusive2", GatewayType.INCLUSIVE); builder.addUserTask("step3", ACTOR_NAME); builder.addTransition("start", "inclusive1"); builder.addTransition("inclusive1", "step1"); builder.addTransition("inclusive1", "step2"); builder.addTransition("step1", "inclusive2"); builder.addTransition("signal", "exceptionStep"); builder.addTransition("step2", "inclusive2"); builder.addTransition("inclusive2", "step3"); final DesignProcessDefinition designProcessDefinition = builder.getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); // execute step2 waitForUserTask(processInstance, "step1"); waitForUserTaskAndExecuteIt(processInstance, "step2", user); // send signal to trigger boundary getProcessAPI().sendSignal("bip"); // wait and execute exceptionStep waitForUserTaskAndExecuteIt(processInstance, "exceptionStep", user); // step3 should be ready waitForUserTaskAndExecuteIt(processInstance, "step3", user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } /** * Notify inclusive gateway that a branch died * inclusive1 -> step1+step2 -> inclusive2 -> step3 * complete step2 * step1 is interrupted by a boundary * exception path of boundary is finished * expected: step3 is active because the inclusive2 was notified */ @Test public void inclusiveGatewayWithNonInterruptingBoundary() throws Exception { createTrueAndFalseExpression(); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("NotifiedBranchDeadProcess", PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addStartEvent("start"); builder.addGateway("inclusive1", GatewayType.INCLUSIVE); builder.addUserTask("step1", ACTOR_NAME).addBoundaryEvent("timer", false) .addTimerEventTriggerDefinition(TimerType.DURATION, new ExpressionBuilder().createConstantLongExpression(1)); builder.addUserTask("exceptionStep", ACTOR_NAME); builder.addUserTask("step2", ACTOR_NAME); builder.addGateway("inclusive2", GatewayType.INCLUSIVE); builder.addUserTask("step3", ACTOR_NAME); builder.addTransition("start", "inclusive1"); builder.addTransition("inclusive1", "step1"); builder.addTransition("inclusive1", "step2"); builder.addTransition("step1", "inclusive2"); builder.addTransition("timer", "exceptionStep"); builder.addTransition("step2", "inclusive2"); builder.addTransition("inclusive2", "step3"); final DesignProcessDefinition designProcessDefinition = builder.getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); // execute step2 final long step1Id = waitForUserTask(processInstance, "step1"); waitForUserTaskAndExecuteIt(processInstance, "step2", user); waitForUserTask(processInstance, "exceptionStep"); // step1 should still be here assignAndExecuteStep(step1Id, user); // step3 should be ready event if exceptionStep is not waitForUserTask(processInstance, "step3"); disableAndDeleteProcess(processDefinition); } /** * Inclusive then Exclusive Gateway * Expected : step5 */ @Test public void inclusiveSplitThenExclusiveMerge() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1") .addAutomaticTask("step2").addAutomaticTask("step3").addAutomaticTask("step4") .addUserTask("step5", ACTOR_NAME) .addGateway("gateway1", GatewayType.INCLUSIVE).addGateway("gateway2", GatewayType.EXCLUSIVE) .addTransition("step1", "gateway1") .addTransition("gateway1", "step2", trueExpression).addTransition("gateway1", "step3", trueExpression) .addDefaultTransition("gateway1", "step4").addTransition("step2", "gateway2") .addTransition("step3", "gateway2") .addTransition("step4", "gateway2").addTransition("gateway2", "step5").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); // test execution final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // we should have 2 elements ready: waitForUserTaskAndExecuteIt(processInstance, "step5", user); waitForUserTaskAndExecuteIt(processInstance, "step5", user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } /** * Exclusive with default path then Inclusive Gateway * Expected : step5 * BS-13596 */ @Test public void exclusiveThenInclusive() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_exclusive_inclusive_gateways", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1") .addAutomaticTask("step2").addAutomaticTask("step3").addAutomaticTask("step4") .addUserTask("step5", ACTOR_NAME).addAutomaticTask("step6") .addGateway("gateway1", GatewayType.EXCLUSIVE).addGateway("gateway2", GatewayType.INCLUSIVE) .addGateway("gateway3", GatewayType.INCLUSIVE) .addTransition("step1", "gateway1") .addTransition("gateway1", "step6", falseExpression) .addDefaultTransition("gateway1", "gateway2").addTransition("gateway2", "step2", trueExpression) .addTransition("gateway2", "step3", trueExpression) .addDefaultTransition("gateway2", "step4").addTransition("step2", "gateway3") .addTransition("step3", "gateway3") .addTransition("step4", "gateway3").addTransition("gateway3", "step5").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); // test execution final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // we should have 2 elements ready: waitForUserTaskAndExecuteIt(processInstance, "step5", user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } /** * Exclusive then Exclusive Gateway * expected : step4 */ @Test public void exclusiveSplitThenExclusiveMerge() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1") .addAutomaticTask("step2").addAutomaticTask("step3").addAutomaticTask("step4") .addUserTask("step5", ACTOR_NAME) .addGateway("gateway1", GatewayType.EXCLUSIVE).addGateway("gateway2", GatewayType.EXCLUSIVE) .addTransition("step1", "gateway1") .addTransition("gateway1", "step2", trueExpression).addTransition("gateway1", "step3", trueExpression) .addDefaultTransition("gateway1", "step4").addTransition("step2", "gateway2") .addTransition("step3", "gateway2") .addTransition("step4", "gateway2").addTransition("gateway2", "step5").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // test execution final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); // we should have 1 elements ready: waitForUserTaskAndExecuteIt(processInstance, "step5", user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } /** * Parallel then Exclusive Gateway * expected : step5 * Fails sometimes */ @Test public void parallelSplitThenExclusiveMerge() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1") .addAutomaticTask("step2").addAutomaticTask("step3").addAutomaticTask("step4") .addUserTask("step5", ACTOR_NAME) .addGateway("gateway1", GatewayType.PARALLEL).addGateway("gateway2", GatewayType.EXCLUSIVE) .addTransition("step1", "gateway1") .addTransition("gateway1", "step2").addTransition("gateway1", "step3") .addTransition("gateway1", "step4") .addTransition("step2", "gateway2") .addTransition("step3", "gateway2").addTransition("step4", "gateway2") .addTransition("gateway2", "step5").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // test execution final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); // we should have 3 elements ready: waitForUserTaskAndExecuteIt(processInstance, "step5", user); waitForUserTaskAndExecuteIt(processInstance, "step5", user); waitForUserTaskAndExecuteIt(processInstance, "step5", user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } /** * Inclusive then Parallel Gateway * Expected : Process is blocked on gateway2 (parallel gateway waiting for inactive transition) */ @Test public void inclusiveSplitThenParallelMerge() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1") .addAutomaticTask("step2").addAutomaticTask("step3").addUserTask("step4", ACTOR_NAME) .addUserTask("step5", ACTOR_NAME) .addGateway("gateway1", GatewayType.INCLUSIVE).addGateway("gateway2", GatewayType.PARALLEL) .addTransition("step1", "gateway1") .addTransition("gateway1", "step2", trueExpression).addTransition("gateway1", "step3", trueExpression) .addDefaultTransition("gateway1", "step4").addTransition("step2", "gateway2") .addTransition("step3", "gateway2") .addTransition("step4", "gateway2").addTransition("gateway2", "step5").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); waitForGateway(processInstance, "gateway2"); final List pendingHumanTaskInstances = getProcessAPI().getPendingHumanTaskInstances( user.getId(), 0, 10, ActivityInstanceCriterion.NAME_ASC); assertEquals(0, pendingHumanTaskInstances.size()); disableAndDeleteProcess(processDefinition); } /** * Inclusive then Parallel Gateway * Expected : Process is blocked on gateway2 (parallel gateway waiting for inactive transition) */ @Test public void exclusiveSplitThenParallelMerge() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Processy_exclusiveSplitThenParallelMerge", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1") .addAutomaticTask("step2").addUserTask("step3", ACTOR_NAME).addUserTask("step4", ACTOR_NAME) .addUserTask("step5", ACTOR_NAME) .addGateway("gateway1", GatewayType.EXCLUSIVE).addGateway("gateway2", GatewayType.PARALLEL) .addTransition("step1", "gateway1") .addTransition("gateway1", "step2", trueExpression).addTransition("gateway1", "step3", trueExpression) .addDefaultTransition("gateway1", "step4").addTransition("step2", "gateway2") .addTransition("step3", "gateway2") .addTransition("step4", "gateway2").addTransition("gateway2", "step5").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // test execution final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); waitForGateway(processInstance, "gateway2"); final List pendingHumanTaskInstances = getProcessAPI().getPendingHumanTaskInstances( user.getId(), 0, 10, ActivityInstanceCriterion.NAME_ASC); assertEquals(0, pendingHumanTaskInstances.size()); disableAndDeleteProcess(processDefinition); } @Test public void activitySplitsToTrueTransitionEvenIfADefaultTransitionIsSet() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("splitActivity", PROCESS_VERSION) .addActor(ACTOR_NAME).addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME) .addUserTask("step3", ACTOR_NAME) .addUserTask("step4", ACTOR_NAME).addTransition("step1", "step2", trueExpression) .addTransition("step1", "step3", falseExpression) .addDefaultTransition("step1", "step4").getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step2"); } @Test public void activitySplitsToDefaultTransitionWhenAllTransitionsAreConditionalAndFalse() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("splitActivity", PROCESS_VERSION) .addActor(ACTOR_NAME).addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME) .addUserTask("step3", ACTOR_NAME) .addUserTask("step4", ACTOR_NAME).addTransition("step1", "step2", falseExpression) .addTransition("step1", "step3", falseExpression) .addDefaultTransition("step1", "step4").getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step4"); } @Test public void activitySplitsToDefaultTransitionAndNormalTransition() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("splitActivity", PROCESS_VERSION) .addActor(ACTOR_NAME).addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME) .addUserTask("step3", ACTOR_NAME) .addUserTask("step4", ACTOR_NAME).addTransition("step1", "step2") .addTransition("step1", "step3", falseExpression) .addDefaultTransition("step1", "step4").getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step2", "step4"); } @Test public void activitySplitsToTrueConditionTransitionAndNormalTransition() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("splitActivity", PROCESS_VERSION) .addActor(ACTOR_NAME).addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME) .addUserTask("step3", ACTOR_NAME) .addUserTask("step4", ACTOR_NAME).addTransition("step1", "step2") .addTransition("step1", "step3", trueExpression) .addDefaultTransition("step1", "step4").getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step2", "step3"); } @Test public void activitySplitsTo2NormalTransitions() throws Exception { final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", PROCESS_VERSION).addActor(ACTOR_NAME) .addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addTransition("step1", "step2") .addTransition("step1", "step3").getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step2", "step3"); } @Test public void activitySplitsTo2TrueTransitions() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("splitActivity", PROCESS_VERSION) .addActor(ACTOR_NAME).addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME) .addUserTask("step3", ACTOR_NAME) .addUserTask("step4", ACTOR_NAME).addTransition("step1", "step2", trueExpression) .addTransition("step1", "step3", trueExpression) .addDefaultTransition("step1", "step4").getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step2", "step3"); } @Test public void activitySplitsWithNormalAndFalseConditionnal() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("splitActivity", PROCESS_VERSION) .addActor(ACTOR_NAME).addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME) .addUserTask("step3", ACTOR_NAME) .addTransition("step1", "step2").addTransition("step1", "step3", falseExpression).getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, ""); } @Test public void activitySplitsWith2FalseConditionnal() throws Exception { createTrueAndFalseExpression(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("splitActivity", PROCESS_VERSION) .addActor(ACTOR_NAME).addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME) .addUserTask("step3", ACTOR_NAME) .addTransition("step1", "step2", falseExpression).addTransition("step1", "step3", falseExpression) .getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, ""); } @Test public void inputAndOutputTransitionsOfInclusiveGatewayShouldBeEvaluatedToTrue() throws Exception { final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("testInputTransitionOfInclusiveShouldBeEvaluatedToTrue", PROCESS_VERSION) .addActor(ACTOR_NAME).addAutomaticTask("step1") .addGateway("gateway", GatewayType.INCLUSIVE).addUserTask("step2", ACTOR_NAME) .addTransition("step1", "gateway") .addTransition("gateway", "step2").getProcess(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, designProcessDefinition, "step2"); } private void assertJohnHasGotTheExpectedTaskPending(final String actorName, final DesignProcessDefinition designProcessDefinition, final String... expected) throws Exception { final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, actorName, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); // we should have 2 elements ready: if (expected.length == 1 && expected[0].isEmpty()) { final WaitUntil waitUntil = new WaitUntil(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT) { @Override protected boolean check() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(ActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId()); searchOptionsBuilder.filter(ActivityInstanceSearchDescriptor.STATE_NAME, "failed"); final SearchResult searchActivities = getProcessAPI() .searchFlowNodeInstances(searchOptionsBuilder.done()); return searchActivities.getCount() == 1; } }; assertTrue("Expected a task in fail state, there was none or more than one", waitUntil.waitUntil()); } else { final CheckNbPendingTaskOf checkNbPendingTaskOf = new CheckNbPendingTaskOf(getProcessAPI(), DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, true, expected.length, user); // assertTrue("there was no pending task for john", checkNbPendingTaskOf.waitUntil()); checkNbPendingTaskOf.waitUntil(); assertEquals(expected.length, checkNbPendingTaskOf.getPendingHumanTaskInstances().size()); final List pendingTasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, expected.length, ActivityInstanceCriterion.NAME_ASC); for (int i = 0; i < expected.length; i++) { assertEquals(expected[i], pendingTasks.get(i).getName());// TODO check order } for (final HumanTaskInstance humanTaskInstance : pendingTasks) { getProcessAPI().assignUserTask(humanTaskInstance.getId(), user.getId()); } for (final HumanTaskInstance humanTaskInstance : pendingTasks) { getProcessAPI().executeFlowNode(humanTaskInstance.getId()); } waitForProcessToFinish(processInstance); } disableAndDeleteProcess(processDefinition); } protected void createTrueAndFalseExpression() throws InvalidExpressionException { trueExpression = new ExpressionBuilder().createConstantBooleanExpression(true); falseExpression = new ExpressionBuilder().createConstantBooleanExpression(false); } @Test public void manyTransitionsToGateway() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); builder.createNewInstance("tooLong", "255"); builder.addActor(ACTOR_NAME); builder.addStartEvent("start"); builder.addGateway("sem", GatewayType.PARALLEL); for (int j = 0; j < 50; j++) { builder.addAutomaticTask("automatic-automatic-automatic-automatic" + j); builder.addTransition("start", "automatic-automatic-automatic-automatic" + j); builder.addTransition("automatic-automatic-automatic-automatic" + j, "sem"); } builder.addUserTask("step", ACTOR_NAME); builder.addEndEvent("end"); builder.addTransition("sem", "step"); builder.addTransition("step", "end"); builder.done(); assertJohnHasGotTheExpectedTaskPending(ACTOR_NAME, builder.getProcess(), "step"); } @Test public void tooManyTokens() throws Exception { ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder(); processDefinitionBuilder = processDefinitionBuilder.createNewInstance("tooManyTokens", PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("Start"); processDefinitionBuilder.addGateway("Gateway1", GatewayType.PARALLEL); processDefinitionBuilder.addUserTask("Step1", ACTOR_NAME); processDefinitionBuilder.addEndEvent("End1"); processDefinitionBuilder.addUserTask("Step3", ACTOR_NAME); processDefinitionBuilder.addGateway("Gateway2", GatewayType.PARALLEL); processDefinitionBuilder.addUserTask("Step2", ACTOR_NAME); processDefinitionBuilder.addEndEvent("Terminate").addTerminateEventTrigger(); processDefinitionBuilder.addTransition("Start", "Gateway1"); processDefinitionBuilder.addTransition("Gateway1", "Step1"); processDefinitionBuilder.addTransition("Gateway1", "Step3"); processDefinitionBuilder.addTransition("Step3", "Gateway2"); processDefinitionBuilder.addTransition("Step1", "Gateway2"); processDefinitionBuilder.addTransition("Step1", "End1"); processDefinitionBuilder.addTransition("Gateway2", "Step2"); processDefinitionBuilder.addTransition("Step2", "Terminate"); final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "Step3", user); waitForUserTaskAndExecuteIt(processInstance, "Step1", user); waitForUserTask("Step2"); // should also get the exception...not yet in the task disableAndDeleteProcess(processDefinition); } @Test public void processWithDiedBranchInclusive() throws Exception { ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder(); processDefinitionBuilder = processDefinitionBuilder.createNewInstance("processWithDiedBranch", PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("Start"); processDefinitionBuilder.addGateway("Gateway1", GatewayType.PARALLEL); processDefinitionBuilder.addUserTask("Step1", ACTOR_NAME); processDefinitionBuilder.addUserTask("Step2", ACTOR_NAME); processDefinitionBuilder.addUserTask("Step3", ACTOR_NAME); processDefinitionBuilder.addUserTask("Step4", ACTOR_NAME); processDefinitionBuilder.addGateway("Gateway2", GatewayType.INCLUSIVE); processDefinitionBuilder.addGateway("Exclu", GatewayType.EXCLUSIVE); processDefinitionBuilder.addTransition("Start", "Gateway1"); processDefinitionBuilder.addTransition("Gateway1", "Step1"); processDefinitionBuilder.addTransition("Gateway1", "Step2"); processDefinitionBuilder.addTransition("Step2", "Exclu"); processDefinitionBuilder.addTransition("Exclu", "Step3", trueExpression); processDefinitionBuilder.addTransition("Exclu", "Gateway2", falseExpression); processDefinitionBuilder.addTransition("Step1", "Gateway2"); processDefinitionBuilder.addTransition("Gateway2", "Step4"); final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); //branch 1 is executed waitForUserTaskAndExecuteIt(processInstance, "Step1", user); //execute branch 2 that split to step3 waitForUserTaskAndExecuteIt(processInstance, "Step2", user); //step3 should be ready (branch does not go to the inclusive anymore waitForUserTask("Step3"); //gateway 2 should have merged after the step killed the branch waitForUserTask("Step4"); disableAndDeleteProcess(processDefinition); } @Test public void processWithAlternativePathUsingExclusiveGatewayMergedByInclusive() throws Exception { /* * Diagram: one step5 is executed, step6 should be ready * ╭────❲Step2❳────────────────────╮ * ○─❲Step1❳─❬o❭ ❬o❭─❲Step6❳─○ * ╰────❲Step3❳──❬x❭──❲Step4❳────╯ * ╰────❲Step5❳──○ */ ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder(); processDefinitionBuilder = processDefinitionBuilder.createNewInstance("processWithDiedBranch", PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("Start"); processDefinitionBuilder.addUserTask("Step1", ACTOR_NAME); processDefinitionBuilder.addGateway("Gateway1", GatewayType.INCLUSIVE); processDefinitionBuilder.addUserTask("Step2", ACTOR_NAME); processDefinitionBuilder.addUserTask("Step3", ACTOR_NAME); processDefinitionBuilder.addGateway("Exclu", GatewayType.EXCLUSIVE); processDefinitionBuilder.addUserTask("Step4", ACTOR_NAME); processDefinitionBuilder.addUserTask("Step5", ACTOR_NAME); processDefinitionBuilder.addGateway("Gateway2", GatewayType.INCLUSIVE); processDefinitionBuilder.addUserTask("Step6", ACTOR_NAME); processDefinitionBuilder.addTransition("Start", "Gateway1"); processDefinitionBuilder.addTransition("Gateway1", "Step2"); processDefinitionBuilder.addTransition("Gateway1", "Step3"); processDefinitionBuilder.addTransition("Step3", "Exclu"); processDefinitionBuilder.addDefaultTransition("Exclu", "Step4"); processDefinitionBuilder.addTransition("Exclu", "Step5", trueExpression); processDefinitionBuilder.addTransition("Step4", "Gateway2"); processDefinitionBuilder.addTransition("Step2", "Gateway2"); processDefinitionBuilder.addTransition("Gateway2", "Step6"); final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "Step1", user); waitForUserTaskAndExecuteIt(processInstance, "Step2", user); waitForUserTaskAndExecuteIt(processInstance, "Step3", user); waitForUserTask("Step5"); waitForUserTask("Step6"); disableAndDeleteProcess(processDefinition); } @Test public void processWithLoopAndInclusive() throws Exception { ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder(); processDefinitionBuilder = processDefinitionBuilder.createNewInstance("processWithDiedBranch", PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("Start"); processDefinitionBuilder.addGateway("Gateway1", GatewayType.PARALLEL); processDefinitionBuilder.addUserTask("Step1", ACTOR_NAME); processDefinitionBuilder.addUserTask("Step2", ACTOR_NAME); processDefinitionBuilder.addGateway("Gateway2", GatewayType.INCLUSIVE); processDefinitionBuilder.addUserTask("Step3", ACTOR_NAME); processDefinitionBuilder.addTransition("Start", "Gateway1"); processDefinitionBuilder.addTransition("Gateway1", "Step1"); processDefinitionBuilder.addTransition("Step1", "Step1"); processDefinitionBuilder.addTransition("Gateway1", "Step2"); processDefinitionBuilder.addTransition("Step1", "Gateway2"); processDefinitionBuilder.addTransition("Step2", "Gateway2"); processDefinitionBuilder.addTransition("Gateway2", "Step3"); final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); //branch 1 is executed waitForUserTaskAndExecuteIt(processInstance, "Step1", user); waitForUserTaskAndExecuteIt(processInstance, "Step1", user); waitForUserTaskAndExecuteIt(processInstance, "Step1", user); waitForUserTaskAndExecuteIt(processInstance, "Step1", user); waitForUserTaskAndExecuteIt(processInstance, "Step2", user); waitForUserTask("Step3"); waitForUserTask("Step3"); waitForUserTask("Step3"); waitForUserTask("Step3"); waitForUserTaskAndExecuteIt(processInstance, "Step1", user); waitForUserTask("Step3"); disableAndDeleteProcess(processDefinition); } @Test public void restartNodeShouldNotRestartGatewaysWithNotFullfilledMergingCondition() throws Exception { final ProcessDefinitionBuilder processDesignBuilder = new ProcessDefinitionBuilder() .createNewInstance("process_with_join_gateway", PROCESS_VERSION); processDesignBuilder.addStartEvent("goForIt"); processDesignBuilder.addEndEvent("terminated"); final DesignProcessDefinition designProcessDefinition = processDesignBuilder.addActor(ACTOR_NAME) .addGateway("split", GatewayType.PARALLEL) .addAutomaticTask("autoTask").addUserTask("manualTask", ACTOR_NAME) .addGateway("join", GatewayType.PARALLEL).addTransition("goForIt", "split") .addTransition("split", "autoTask").addTransition("split", "manualTask") .addTransition("autoTask", "join").addTransition("manualTask", "join") .addTransition("join", "terminated").getProcess(); loginOnDefaultTenantWith(USERNAME, PASSWORD); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); FlowNodeInstance joinGateway = waitForFlowNodeInExecutingState(processInstance, "join", false); logout(); final PlatformSession loginPlatform = loginOnPlatform(); final PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(loginPlatform); platformAPI.stopNode(); platformAPI.startNode(); logoutOnPlatform(loginPlatform); loginOnDefaultTenantWith(USERNAME, PASSWORD); // To be sure asynchronous restart works have been executed: Thread.sleep(200); joinGateway = getProcessAPI().getFlowNodeInstance(joinGateway.getId()); assertEquals(TestStates.EXECUTING.getStateName(), joinGateway.getState()); disableAndDeleteProcess(processDefinition); } @Test public void can_evaluate_transitions_with_logical_complement_expression_using_null_variable() throws Exception { //given Expression booleanVarExpr = new ExpressionBuilder().createDataExpression("b", Boolean.class.getName()); Expression notB = new ExpressionBuilder().createLogicalComplementExpression("notB", booleanVarExpr); String start = "start"; String gateway = "gateway"; String step1 = "step1"; String step2 = "step2"; ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("proc", "1.0"); builder.addActor("delivery"); builder.addBooleanData("b", null); builder.addStartEvent(start); builder.addGateway(gateway, GatewayType.EXCLUSIVE); builder.addUserTask(step1, "delivery"); builder.addUserTask(step2, "delivery"); builder.addTransition(start, gateway); builder.addDefaultTransition(gateway, step1); builder.addTransition(gateway, step2, notB); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), "delivery", user); //when ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); //then waitForFlowNodeInReadyState(processInstance, "step1", true); disableAndDeleteProcess(processDefinition); } @Test public void can_evaluate_transitions_with_comparison_expression_using_null_variable() throws Exception { //given Expression integerVarExpr = new ExpressionBuilder().createDataExpression("count", Integer.class.getName()); Expression fiveExpr = new ExpressionBuilder().createConstantIntegerExpression(5); Expression greaterThan = new ExpressionBuilder().createComparisonExpression("greaterThan", integerVarExpr, ComparisonOperator.GREATER_THAN, fiveExpr); String start = "start"; String gateway = "gateway"; String step1 = "step1"; String step2 = "step2"; ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("proc", "1.0"); builder.addActor("delivery"); builder.addIntegerData("count", null); builder.addStartEvent(start); builder.addGateway(gateway, GatewayType.EXCLUSIVE); builder.addUserTask(step1, "delivery"); builder.addUserTask(step2, "delivery"); builder.addTransition(start, gateway); builder.addDefaultTransition(gateway, step1); builder.addTransition(gateway, step2, greaterThan); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), "delivery", user); //when ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); //then waitForFlowNodeInReadyState(processInstance, "step1", true); disableAndDeleteProcess(processDefinition); } @Test public void link_events_following_a_parallel_gateway_and_going_into_one_should_properly_activate_it() throws Exception { ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("proc", "1.0"); String task2 = "task2"; String gateway = "gateway"; String gateway1 = "gateway1"; String theEnd = "theEnd"; builder.addEndEvent(theEnd); builder.addActor("user"); builder.addGateway(gateway1, GatewayType.PARALLEL); builder.addUserTask(task2, "user"); builder.addGateway(gateway, GatewayType.PARALLEL); builder.addTransition(gateway, gateway1); builder.addTransition(gateway, gateway1); builder.addTransition(gateway, gateway1); builder.addTransition(gateway1, task2); builder.addTransition(task2, theEnd); DesignProcessDefinition designProcessDefinition = builder.done(); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, "user", user); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, task2, user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Test public void link_events_coming_from_the_same_node_and_going_into_an_inclusive_gateway_should_activate_it_only_once() throws Exception { ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("proc", "1.0"); String task1 = "task1"; String gateway = "gateway"; String gateway1 = "gateway1"; String theEnd = "theEnd"; String task2 = "task2"; String start = "start"; builder.addEndEvent(theEnd); builder.addStartEvent(start); builder.addActor("user"); builder.addGateway(gateway, GatewayType.INCLUSIVE); builder.addGateway(gateway1, GatewayType.PARALLEL); builder.addUserTask(task2, "user"); builder.addAutomaticTask(task1); builder.addTransition(task1, gateway1); builder.addTransition(gateway1, gateway); builder.addTransition(gateway1, gateway); builder.addTransition(gateway1, gateway); builder.addTransition(gateway, task2); builder.addTransition(task2, theEnd); builder.addTransition(start, task1); DesignProcessDefinition designProcessDefinition = builder.done(); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, "user", user); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, task2, user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/GetProcessDefinitionIT.java ================================================ /** * 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.process; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.connectors.TestConnector; import org.bonitasoft.engine.connectors.TestConnectorThatThrowException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.filter.user.TestFilter; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserMembership; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.Test; public class GetProcessDefinitionIT extends TestWithTechnicalUser { private List enabledProcessDefinitions; private ProcessDefinition processDefinition4; private List users = null; private List categories = null; private List groups = null; private List roles = null; private List userMemberships = null; @Test public void getProcessDefinitionsUnrelatedToCategoryUserCanStart() throws Exception { beforeSearchProcessDefinitionsUserCanStart(); // test differentFrom process with one category List result = getProcessAPI().getProcessDeploymentInfosUnrelatedToCategory( categories.get(2).getId(), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(7, result.size()); assertEquals(enabledProcessDefinitions.get(0).getId(), result.get(0).getProcessId()); assertEquals(enabledProcessDefinitions.get(1).getId(), result.get(1).getProcessId()); assertEquals(enabledProcessDefinitions.get(2).getId(), result.get(2).getProcessId()); assertEquals(enabledProcessDefinitions.get(3).getId(), result.get(3).getProcessId()); assertEquals(enabledProcessDefinitions.get(4).getId(), result.get(4).getProcessId()); assertEquals(enabledProcessDefinitions.get(5).getId(), result.get(5).getProcessId()); assertEquals(enabledProcessDefinitions.get(6).getId(), result.get(6).getProcessId()); // test differentFrom process with 2 categories, one of which is to extract result = getProcessAPI().getProcessDeploymentInfosUnrelatedToCategory(categories.get(0).getId(), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(7, result.size()); assertEquals(enabledProcessDefinitions.get(0).getId(), result.get(0).getProcessId()); assertEquals(enabledProcessDefinitions.get(1).getId(), result.get(1).getProcessId()); assertEquals(processDefinition4.getId(), result.get(2).getProcessId()); assertEquals(enabledProcessDefinitions.get(3).getId(), result.get(3).getProcessId()); assertEquals(enabledProcessDefinitions.get(4).getId(), result.get(4).getProcessId()); assertEquals(enabledProcessDefinitions.get(5).getId(), result.get(5).getProcessId()); assertEquals(enabledProcessDefinitions.get(6).getId(), result.get(6).getProcessId()); afterSearchProcessDefinitionsUserCanStart(); } private void beforeSearchProcessDefinitionsUserCanStart() throws Exception { // create users users = new ArrayList<>(2); final User chico = createUser("chicobento", "bpm"); final User cebolinha = createUser("cebolinha", "bpm"); final User cascao = createUser("cascao", "bpm"); final User magali = createUser("magali", "bpm"); final User monica = createUser("monica", "bpm"); final User dorinha = createUser("dorinha", "bpm"); users.add(chico); users.add(cebolinha); users.add(cascao); users.add(magali); users.add(monica); users.add(dorinha); // create groups groups = new ArrayList<>(2); final Group group1 = createGroup("group1"); groups.add(group1); final Group group2 = createGroup("group2"); groups.add(group2); // create roles roles = new ArrayList<>(2); final Role role1 = createRole("role1"); final Role role2 = createRole("role2"); roles.add(role1); roles.add(role2); // create user memberships userMemberships = new ArrayList<>(3); userMemberships.add(getIdentityAPI().addUserMembership(magali.getId(), group1.getId(), role1.getId())); userMemberships.add(getIdentityAPI().addUserMembership(monica.getId(), group1.getId(), role2.getId())); userMemberships.add(getIdentityAPI().addUserMembership(dorinha.getId(), group2.getId(), role1.getId())); // create processes createProcessesDefForSearchProcessUserCanStart(); categories = new ArrayList<>(3); final Category category1 = getProcessAPI().createCategory("category1", "the first known category"); final Category category2 = getProcessAPI().createCategory("category2", "the second known category"); final Category category3 = getProcessAPI().createCategory("category3", "the third known category"); categories.add(category1); categories.add(category2); categories.add(category3); getProcessAPI().addProcessDefinitionToCategory(category1.getId(), enabledProcessDefinitions.get(2).getId()); getProcessAPI().addProcessDefinitionToCategory(category2.getId(), enabledProcessDefinitions.get(2).getId()); getProcessAPI().addProcessDefinitionToCategory(category2.getId(), enabledProcessDefinitions.get(1).getId()); getProcessAPI().addProcessDefinitionToCategory(category3.getId(), processDefinition4.getId()); } private void createProcessesDefForSearchProcessUserCanStart() throws Exception { enabledProcessDefinitions = new ArrayList<>(4); final String actor1 = ACTOR_NAME; final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process1", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor1, true); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, actor1, users.get(0)); enabledProcessDefinitions.add(processDefinition1); // create process2 final String actor2 = "Actor2"; final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process2", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actor2, users.get(1)); enabledProcessDefinitions.add(processDefinition2); final DesignProcessDefinition designProcessDefinition3 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process3", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, actor2, users.get(1)); enabledProcessDefinitions.add(processDefinition3); // process not enabled final DesignProcessDefinition designProcessDefinition4 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process4", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); processDefinition4 = getProcessAPI().deploy(designProcessDefinition4); getProcessAPI().addUserToActor(actor2, processDefinition4, users.get(1).getId()); // process without actor initiator final DesignProcessDefinition designProcessDefinition5 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process5", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, false); final ProcessDefinition processDefinition5 = deployAndEnableProcessWithActor(designProcessDefinition5, actor2, users.get(2)); enabledProcessDefinitions.add(processDefinition5); // actor initiator is a group final DesignProcessDefinition designProcessDefinition6 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process6", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition6 = deployAndEnableProcessWithActor(designProcessDefinition6, actor2, groups.get(0)); enabledProcessDefinitions.add(processDefinition6); // actor initiator is a role final DesignProcessDefinition designProcessDefinition7 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process7", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition7 = deployAndEnableProcessWithActor(designProcessDefinition7, actor2, roles.get(0)); enabledProcessDefinitions.add(processDefinition7); // actor initiator is a membership final DesignProcessDefinition designProcessDefinition8 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process8", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition8 = deployAndEnableProcessWithActor(designProcessDefinition8, actor2, roles.get(0), groups.get(0)); enabledProcessDefinitions.add(processDefinition8); } private void afterSearchProcessDefinitionsUserCanStart() throws BonitaException { disableAndDeleteProcess(enabledProcessDefinitions); deleteProcess(processDefinition4); deleteUserMemberships(userMemberships); deleteUsers(users); deleteCategories(categories); deleteGroups(groups); deleteRoles(roles); } @Test public void getProcessesWithActorOnlyForUser() throws Exception { final String actorName1 = "ITAccountCreator"; final String actorName2 = "ITAccountValidator"; final User user1 = createUser("any", "contrasena"); final User user2 = createUser("bob", "smith"); final DesignProcessDefinition design1 = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps( "MyProcess1", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actorName1, false); final ProcessDefinition pDef1 = deployAndEnableProcessWithActor(design1, actorName1, user1); final DesignProcessDefinition design2 = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps( "MyProcess2", "1.1", Arrays.asList("mi_etapa"), Arrays.asList(true), actorName2, false); final ProcessDefinition pDef2 = deployAndEnableProcessWithActor(design2, actorName2, user1); final int startIndex = 0; final int maxResults = 10; List processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUser( user1.getId(), startIndex, maxResults, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(2, processes.size()); assertEquals(processes.get(0).getProcessId(), pDef1.getId()); assertEquals(processes.get(1).getProcessId(), pDef2.getId()); // assertThat("Process definition Ids are not the expected ones", processes, idAre(pDef1.getId(), pDef2.getId())); final ActorInstance actorInstance = getActor(actorName2, pDef2); if (actorInstance != null) { // So that we have more than one user member of actorName2 in pDef2: getProcessAPI().addUserToActor(actorInstance.getId(), user2.getId()); } processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUser(user1.getId(), startIndex, maxResults, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, processes.size()); assertEquals(processes.get(0).getProcessId(), pDef1.getId()); disableAndDeleteProcess(pDef1); disableAndDeleteProcess(pDef2); deleteUser(user1); deleteUser(user2); } @Test public void getProcessesWithActorOnlyForUsers() throws Exception { final String actorName1 = "ITAccountCreator"; final String actorName2 = "ITAccountValidator"; final User user1 = createUser("any", "contrasena"); final User user2 = createUser("bob", "smith"); final User user3 = createUser("mark", "sampaio"); final DesignProcessDefinition design1 = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps( "MyProcess1", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actorName1, false); final ProcessDefinition pDef1 = deployAndEnableProcessWithActor(design1, actorName1, user1); final DesignProcessDefinition design2 = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps( "MyProcess2", "1.1", Arrays.asList("mi_etapa"), Arrays.asList(true), actorName2, false); final ProcessDefinition pDef2 = deployAndEnableProcessWithActor(design2, actorName2, user2); List processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers( Arrays.asList(user1.getId(), user2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(2, processes.size()); assertEquals(processes.get(0).getProcessId(), pDef1.getId()); assertEquals(processes.get(1).getProcessId(), pDef2.getId()); // assertThat("Process definition Ids are not the expected ones", processes, idAre(pDef1.getId(), pDef2.getId())); final ActorInstance actorInstance = getActor(actorName2, pDef2); if (actorInstance != null) { // So that we have more than one user member of actorName2 in pDef2: getProcessAPI().addUserToActor(actorInstance.getId(), user3.getId()); } final ActorInstance actorInstance2 = getActor(actorName1, pDef1); if (actorInstance2 != null) { // So that we have more than one user member of actorName1 in pDef1: getProcessAPI().addUserToActor(actorInstance2.getId(), user3.getId()); } processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers( Arrays.asList(user1.getId(), user2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(0, processes.size()); processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers( Arrays.asList(user1.getId(), user3.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, processes.size()); assertEquals(pDef1.getId(), processes.get(0).getProcessId()); processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers( Arrays.asList(user2.getId(), user3.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, processes.size()); assertEquals(pDef2.getId(), processes.get(0).getProcessId()); processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUser(user3.getId(), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(0, processes.size()); disableAndDeleteProcess(pDef1); disableAndDeleteProcess(pDef2); deleteUser(user1); deleteUser(user2); deleteUser(user3); } @Test public void getProcessesWithActorOnlyRole() throws Exception { final String actorName = "actor"; // create user final User manu = createUser(USERNAME, PASSWORD); loginOnDefaultTenantWith(USERNAME, PASSWORD); final Role roleToDelete = createRole("roleToDelete"); final Role roleToKeep = createRole("roleToKeep"); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("MyProcess1", "1.0").addActor(actorName) .addUserTask("userTask", actorName).getProcess(); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition, actorName, roleToDelete); final DesignProcessDefinition designProcessDefinition2 = new ProcessDefinitionBuilder() .createNewInstance("MyProcess2", "1.0").addActor(actorName) .addUserTask("userTask", actorName).getProcess(); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actorName, roleToDelete, roleToKeep); List procDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRole( roleToDelete.getId(), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, procDepInfos.size()); final ProcessDeploymentInfo firstPDInfos = procDepInfos.get(0); assertEquals(processDefinition1.getName(), firstPDInfos.getName()); assertEquals(processDefinition1.getId(), firstPDInfos.getProcessId()); final ActorInstance actorInstance = getActor(actorName, processDefinition1); if (actorInstance != null) { // So that we have more than one user member of actorName in processDefinition1: getProcessAPI().addRoleToActor(actorInstance.getId(), roleToKeep.getId()); } procDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRole(roleToDelete.getId(), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(0, procDepInfos.size()); deleteRoles(roleToKeep); deleteRoles(roleToDelete); deleteUser(manu); disableAndDeleteProcess(processDefinition1); disableAndDeleteProcess(processDefinition2); } @Test public void getProcessesWithActorOnlyRoles() throws Exception { final String actorName = "actor"; // create user final User manu = createUser(USERNAME, PASSWORD); loginOnDefaultTenantWith(USERNAME, PASSWORD); final Role roleToDelete1 = createRole("roleToDelete1"); final Role roleToKeep1 = createRole("roleToKeep1"); final Role roleToDelete2 = createRole("roleToDelete2"); final Role roleToKeep2 = createRole("roleToKeep2"); final DesignProcessDefinition designProcessDefinition11 = new ProcessDefinitionBuilder() .createNewInstance("MyProcess11", "1.0").addActor(actorName) .addUserTask("userTask", actorName).getProcess(); final ProcessDefinition processDefinition11 = deployAndEnableProcessWithActor(designProcessDefinition11, actorName, roleToDelete1); final DesignProcessDefinition designProcessDefinition12 = new ProcessDefinitionBuilder() .createNewInstance("MyProcess12", "1.0").addActor(actorName) .addUserTask("userTask", actorName).getProcess(); final ProcessDefinition processDefinition12 = deployAndEnableProcessWithActor(designProcessDefinition12, actorName, roleToDelete1, roleToKeep1); final DesignProcessDefinition designProcessDefinition21 = new ProcessDefinitionBuilder() .createNewInstance("MyProcess21", "1.0").addActor(actorName) .addUserTask("userTask", actorName).getProcess(); final ProcessDefinition processDefinition21 = deployAndEnableProcessWithActor(designProcessDefinition21, actorName, roleToDelete2); final DesignProcessDefinition designProcessDefinition22 = new ProcessDefinitionBuilder() .createNewInstance("MyProcess22", "1.0").addActor(actorName) .addUserTask("userTask", actorName).getProcess(); final ProcessDefinition processDefinition22 = deployAndEnableProcessWithActor(designProcessDefinition22, actorName, roleToDelete2, roleToKeep2); List pDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles( Arrays.asList(roleToDelete1.getId(), roleToDelete2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(2, pDepInfos.size()); ProcessDeploymentInfo pDepInfo1 = pDepInfos.get(0); final ProcessDeploymentInfo pDepInfo2 = pDepInfos.get(1); assertEquals(processDefinition11.getId(), pDepInfo1.getProcessId()); assertEquals(processDefinition11.getName(), pDepInfo1.getName()); assertEquals(processDefinition21.getId(), pDepInfo2.getProcessId()); assertEquals(processDefinition21.getName(), pDepInfo2.getName()); final ActorInstance actorInstance = getActor(actorName, processDefinition11); if (actorInstance != null) { // So that we have more than one user member of actorName in processDefinition11: getProcessAPI().addRoleToActor(actorInstance.getId(), roleToKeep1.getId()); } pDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles( Arrays.asList(roleToDelete1.getId(), roleToDelete2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, pDepInfos.size()); pDepInfo1 = pDepInfos.get(0); assertEquals(processDefinition21.getId(), pDepInfo1.getProcessId()); assertEquals(processDefinition21.getName(), pDepInfo1.getName()); deleteRoles(roleToKeep1, roleToDelete1, roleToKeep2, roleToDelete2); deleteUser(manu); disableAndDeleteProcess(processDefinition11, processDefinition12, processDefinition21, processDefinition22); } @Test public void getProcessesWithActorOnlyGroup() throws Exception { final String actorName = "actor"; // create user final User manu = createUser(USERNAME, PASSWORD); loginOnDefaultTenantWith(USERNAME, PASSWORD); final Group groupToDelete = createGroup("groupToDelete"); final Group groupSonToDelete = createGroup("sonToDelete", "/groupToDelete"); final Group groupToKeep = createGroup("groupToDeleteNot"); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("MyProcess1", "1.0").addActor(actorName) .addUserTask("userTask", actorName).getProcess(); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition, actorName, groupToDelete); final DesignProcessDefinition designProcessDefinition2 = new ProcessDefinitionBuilder() .createNewInstance("MyProcess2", "1.0").addActor(actorName) .addUserTask("userTask", actorName).getProcess(); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actorName, groupToDelete, groupToKeep); final DesignProcessDefinition designProcessDefinition3 = new ProcessDefinitionBuilder() .createNewInstance("MyProcess3", "1.0").addActor(actorName) .addUserTask("userTask", actorName).getProcess(); final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, actorName, groupSonToDelete); List procDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroup( groupToDelete.getId(), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(2, procDepInfos.size()); ProcessDeploymentInfo firstPDInfos = procDepInfos.get(0); final ProcessDeploymentInfo secondPDInfos = procDepInfos.get(1); assertEquals(processDefinition1.getName(), firstPDInfos.getName()); assertEquals(processDefinition1.getId(), firstPDInfos.getProcessId()); assertEquals(processDefinition3.getName(), secondPDInfos.getName()); assertEquals(processDefinition3.getId(), secondPDInfos.getProcessId()); final ActorInstance actorInstance = getActor(actorName, processDefinition3); if (actorInstance != null) { // So that we have more than one user member of actorName in processDefinition3: getProcessAPI().addGroupToActor(actorInstance.getId(), groupToKeep.getId()); } procDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroup(groupToDelete.getId(), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, procDepInfos.size()); firstPDInfos = procDepInfos.get(0); assertEquals(processDefinition1.getName(), firstPDInfos.getName()); assertEquals(processDefinition1.getId(), firstPDInfos.getProcessId()); deleteGroups(groupToKeep); deleteGroups(groupToDelete); deleteUser(manu); disableAndDeleteProcess(processDefinition1); disableAndDeleteProcess(processDefinition2); disableAndDeleteProcess(processDefinition3); } @Test public void getProcessesWithActorOnlyForGroups() throws Exception { final String actorName = "actor"; // create user final User manu = createUser(USERNAME, PASSWORD); loginOnDefaultTenantWith(USERNAME, PASSWORD); final Group groupToDelete = createGroup("groupToDelete"); final Group groupSonToDelete = createGroup("sonToDelete", "/groupToDeleteNot"); final Group groupToKeep = createGroup("groupToDeleteNot"); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("MyProcess1", "1.0").addActor(actorName) .addUserTask("userTask", actorName).getProcess(); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition, actorName, groupToDelete); final DesignProcessDefinition designProcessDefinition2 = new ProcessDefinitionBuilder() .createNewInstance("MyProcess2", "1.0").addActor(actorName) .addUserTask("userTask", actorName).getProcess(); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actorName, groupToDelete, groupToKeep); final DesignProcessDefinition designProcessDefinition3 = new ProcessDefinitionBuilder() .createNewInstance("MyProcess3", "1.0").addActor(actorName) .addUserTask("userTask", actorName).getProcess(); final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, actorName, groupSonToDelete); List procDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups( Arrays.asList(groupSonToDelete.getId(), groupToDelete.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(2, procDepInfos.size()); final ProcessDeploymentInfo firstPDInfos = procDepInfos.get(0); final ProcessDeploymentInfo secondPDInfos = procDepInfos.get(1); assertEquals(processDefinition1.getName(), firstPDInfos.getName()); assertEquals(processDefinition1.getId(), firstPDInfos.getProcessId()); assertEquals(processDefinition3.getName(), secondPDInfos.getName()); assertEquals(processDefinition3.getId(), secondPDInfos.getProcessId()); final ActorInstance actorInstance = getActor(actorName, processDefinition1); if (actorInstance != null) { // So that we have more than one user member of actorName in processDefinition1: getProcessAPI().addGroupToActor(actorInstance.getId(), groupSonToDelete.getId()); } procDepInfos = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups( Arrays.asList(groupToKeep.getId(), groupToDelete.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(3, procDepInfos.size()); deleteGroups(groupToKeep, groupToDelete); deleteUser(manu); disableAndDeleteProcess(processDefinition1, processDefinition2, processDefinition3); } @Test public void getProcessesWithActorWithParticularCases() throws Exception { final String actorName = "actor"; final Role roleToDelete1 = createRole("roleToDelete1"); final Role roleToDelete2 = createRole("roleToDelete2"); final Group groupToDelete1 = createGroup("groupToDelete1"); final Group groupToDelete2 = createGroup("groupToDelete2"); final User userToDelete1 = createUser("userToDelete1", "pwd"); final User userToDelete2 = createUser("userToDelete2", "pwd"); final DesignProcessDefinition designProcessDefinition1 = new ProcessDefinitionBuilder() .createNewInstance("MyProcess1", "1.0").addActor(actorName) .addUserTask("userTask", actorName).getProcess(); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, actorName, roleToDelete1); final DesignProcessDefinition designProcessDefinition2 = new ProcessDefinitionBuilder() .createNewInstance("MyProcess2", "1.0").addActor(actorName) .addUserTask("userTask", actorName).getProcess(); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actorName, groupToDelete1); final DesignProcessDefinition designProcessDefinition3 = new ProcessDefinitionBuilder() .createNewInstance("MyProcess3", "1.0").addActor(actorName) .addUserTask("userTask", actorName).getProcess(); final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, actorName, userToDelete1); List pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles( Arrays.asList(roleToDelete1.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, pDepInfos1.size()); assertEquals(processDefinition1.getName(), pDepInfos1.get(0).getName()); assertEquals(processDefinition1.getId(), pDepInfos1.get(0).getProcessId()); pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles( Arrays.asList(roleToDelete2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(0, pDepInfos1.size()); pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles( Arrays.asList(roleToDelete1.getId(), roleToDelete2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, pDepInfos1.size()); List pDepInfos2 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups( Arrays.asList(groupToDelete1.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, pDepInfos2.size()); assertEquals(processDefinition2.getName(), pDepInfos2.get(0).getName()); assertEquals(processDefinition2.getId(), pDepInfos2.get(0).getProcessId()); pDepInfos2 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups( Arrays.asList(groupToDelete2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(0, pDepInfos2.size()); pDepInfos2 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups( Arrays.asList(groupToDelete1.getId(), groupToDelete2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, pDepInfos2.size()); List pDepInfos3 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers( Arrays.asList(userToDelete1.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, pDepInfos3.size()); assertEquals(processDefinition3.getName(), pDepInfos3.get(0).getName()); assertEquals(processDefinition3.getId(), pDepInfos3.get(0).getProcessId()); pDepInfos3 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers( Arrays.asList(userToDelete2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(0, pDepInfos3.size()); pDepInfos3 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers( Arrays.asList(userToDelete1.getId(), userToDelete2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, pDepInfos3.size()); ActorInstance actorInstance = getActor(actorName, processDefinition1); if (actorInstance != null) { // So that we have more than one user member of actorName in processDefinition1: getProcessAPI().addRoleToActor(actorInstance.getId(), roleToDelete2.getId()); } actorInstance = getActor(actorName, processDefinition2); if (actorInstance != null) { // So that we have more than one user member of actorName in processDefinition2: getProcessAPI().addGroupToActor(actorInstance.getId(), groupToDelete2.getId()); } actorInstance = getActor(actorName, processDefinition3); if (actorInstance != null) { // So that we have more than one user member of actorName in processDefinition3: getProcessAPI().addUserToActor(actorInstance.getId(), userToDelete2.getId()); } pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles( Arrays.asList(roleToDelete1.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(0, pDepInfos1.size()); pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles( Arrays.asList(roleToDelete2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(0, pDepInfos1.size()); pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForRoles( Arrays.asList(roleToDelete1.getId(), roleToDelete2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, pDepInfos1.size()); assertEquals(processDefinition1.getId(), pDepInfos1.get(0).getProcessId()); pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups( Arrays.asList(groupToDelete1.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(0, pDepInfos1.size()); pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups( Arrays.asList(groupToDelete2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(0, pDepInfos1.size()); pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForGroups( Arrays.asList(groupToDelete1.getId(), groupToDelete2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, pDepInfos1.size()); assertEquals(processDefinition2.getId(), pDepInfos1.get(0).getProcessId()); pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers( Arrays.asList(userToDelete1.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(0, pDepInfos1.size()); pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers( Arrays.asList(userToDelete2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(0, pDepInfos1.size()); pDepInfos1 = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUsers( Arrays.asList(userToDelete1.getId(), userToDelete2.getId()), 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, pDepInfos1.size()); assertEquals(processDefinition3.getId(), pDepInfos1.get(0).getProcessId()); deleteRoles(roleToDelete1, roleToDelete2); deleteGroups(groupToDelete1, groupToDelete2); deleteUsers(userToDelete1, userToDelete2); disableAndDeleteProcess(processDefinition1); disableAndDeleteProcess(processDefinition2); disableAndDeleteProcess(processDefinition3); } @Test public void getPaginatedProcessesWithActorOnlyForUser() throws Exception { final String actorName1 = "ITAccountCreator"; final String actorName2 = "ITAccountValidator"; final User user1 = createUser("any", "contrasena"); final User user2 = createUser("bob", "smith"); final DesignProcessDefinition design1 = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps( "MyProcess1", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actorName1, false); final ProcessDefinition pDef1 = deployAndEnableProcessWithActor(design1, actorName1, user1); final DesignProcessDefinition design2 = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps( "MyProcess2", "1.1", Arrays.asList("mi_etapa"), Arrays.asList(true), actorName2, false); final ProcessDefinition pDef2 = deployAndEnableProcessWithActor(design2, actorName2, user1); final int startIndex = 0; final int maxResults = 1; final List processes = getProcessAPI().getProcessDeploymentInfosWithActorOnlyForUser( user1.getId(), startIndex, maxResults, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, processes.size()); assertEquals(processes.get(0).getProcessId(), pDef1.getId()); disableAndDeleteProcess(pDef1); disableAndDeleteProcess(pDef2); deleteUser(user1); deleteUser(user2); } @Test public void getExistingDesignProcessDefinition() throws Exception { final User user = createUser("any", "contrasena"); final Expression targetProcessNameExpr = new ExpressionBuilder().createConstantStringExpression(PROCESS_NAME); final Expression targetProcessVersionExpr = new ExpressionBuilder() .createConstantStringExpression(PROCESS_VERSION); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME, true); processBuilder.addActor("actor2"); processBuilder.addAutomaticTask("AutomaticTask") .addCallActivity("CallActivity", targetProcessNameExpr, targetProcessVersionExpr) .addManualTask("ManualTask", ACTOR_NAME) .addBoundaryEvent("BoundaryEvent").addSignalEventTrigger("signalName"); processBuilder.addUserTask("UserTask", ACTOR_NAME) .addUserFilter("test", "org.bonitasoft.engine.filter.user.testFilter", "1.0") .addInput("userId", new ExpressionBuilder().createConstantLongExpression(3)); processBuilder.addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER); processBuilder.addDocumentDefinition("Doc").addUrl("plop"); processBuilder.addGateway("Gateway", GatewayType.PARALLEL); processBuilder.addBlobData("BlobData", null).addDescription("blolbDescription").addBooleanData("BooleanData", null); processBuilder.addDisplayName("plop").addDisplayDescription("plop2").addEndEvent("EndEvent"); processBuilder.addIntermediateCatchEvent("IntermediateCatchEvent") .addIntermediateThrowEvent("IntermediateThrowEvent"); processBuilder.addReceiveTask("ReceiveTask", "messageName"); processBuilder.addSendTask("SendTask", "messageName", targetProcessNameExpr); processBuilder.addTransition("BoundaryEvent", "ManualTask"); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final ProcessDefinition processDefinition = deployProcessWithTestFilter(designProcessDefinition, user); assertNotNull(processDefinition); final DesignProcessDefinition resultDesignProcessDefinition = getProcessAPI() .getDesignProcessDefinition(processDefinition.getId()); assertEquals(designProcessDefinition, resultDesignProcessDefinition); disableAndDeleteProcess(processDefinition); deleteUser(user); } @Test(expected = ProcessDefinitionNotFoundException.class) public void getNotExistingDesignProcessDefinition() throws Exception { getProcessAPI().getDesignProcessDefinition(16548654L); } private ProcessDefinition deployProcessWithTestFilter(final DesignProcessDefinition designProcessDefinition, final User user) throws Exception { final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition( designProcessDefinition); final List impl = generateFilterImplementations("TestFilter"); for (final BarResource barResource : impl) { businessArchiveBuilder.addUserFilters(barResource); } final List generateFilterDependencies = new ArrayList<>(1); final byte[] data = IOUtil.generateJar(TestFilter.class); generateFilterDependencies.add(new BarResource("TestFilter.jar", data)); for (final BarResource barResource : generateFilterDependencies) { businessArchiveBuilder.addClasspathResource(barResource); } businessArchiveBuilder.addConnectorImplementation( getResource("/org/bonitasoft/engine/connectors/TestConnectorThatThrowException.impl", "TestConnectorThatThrowException.impl")); businessArchiveBuilder .addClasspathResource(BuildTestUtil .generateJarAndBuildBarResource(TestConnectorThatThrowException.class, "TestConnectorThatThrowException.jar")); final ProcessDefinition processDefinition = deployProcess(businessArchiveBuilder.done()); getProcessAPI().addUserToActor(ACTOR_NAME, processDefinition, user.getId()); getProcessAPI().addUserToActor("actor2", processDefinition, user.getId()); getProcessAPI().enableProcess(processDefinition.getId()); return processDefinition; } private List generateFilterImplementations(final String filterName) throws IOException { final List resources = new ArrayList<>(1); final InputStream inputStream = TestConnector.class.getClassLoader() .getResourceAsStream("org/bonitasoft/engine/filter/user/" + filterName + ".impl"); final byte[] data = IOUtil.getAllContentFrom(inputStream); inputStream.close(); resources.add(new BarResource("TestFilter.impl", data)); return resources; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessCategoryIT.java ================================================ /** * 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.process; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.engine.bpm.category.CategoryCriterion; import org.bonitasoft.engine.bpm.category.CategoryNotFoundException; import org.bonitasoft.engine.bpm.category.CategoryUpdater; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeployException; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; public class ProcessCategoryIT extends TestWithTechnicalUser { protected static final String USERNAME = "dwight"; protected static final String PASSWORD = "Schrute"; private final String name = "category"; private final String description = "description"; List categories; List processDefinitions; @Override @Before public void before() throws Exception { super.before(); categories = new ArrayList<>(); processDefinitions = new ArrayList<>(); } @Override @After public void after() throws Exception { for (final Category category : categories) { getProcessAPI().deleteCategory(category.getId()); } for (final ProcessDefinition processDefinitionId : processDefinitions) { deleteProcess(processDefinitionId); } super.after(); } @Test public void createCategory() throws Exception { final Category category = getProcessAPI().createCategory(name, description); categories.add(category); assertNotNull(category); assertEquals(name, category.getName()); final long categoryId = category.getId(); final Category rCategory = getProcessAPI().getCategory(categoryId); assertNotNull(rCategory); assertEquals(name, rCategory.getName()); } @Test public void createCategoryWithCreatorAsAnID() throws Exception { final User user = createUser(USERNAME, PASSWORD); logout(); loginOnDefaultTenantWith(USERNAME, PASSWORD); final Category category = getProcessAPI().createCategory(name, description); categories.add(category); assertNotNull(category); assertEquals(name, category.getName()); assertEquals(user.getId(), category.getCreator()); deleteUser(user); } @Test(expected = AlreadyExistsException.class) public void createCategoryWithCategoryAlreadyExistException() throws Exception { final Category category = getProcessAPI().createCategory(name, description); categories.add(category); assertNotNull(category); assertEquals(name, category.getName()); getProcessAPI().createCategory(name, description); } @Test(expected = CreationException.class) public void createCategoryWithCategoryCreationException() throws Exception { getProcessAPI().createCategory(null, description); } @Test public void getNumberOfCategories() throws Exception { final Category category1 = getProcessAPI().createCategory(name + 1, description); categories.add(category1); final Category category2 = getProcessAPI().createCategory(name + 2, description); categories.add(category2); final long categoriesCount = getProcessAPI().getNumberOfCategories(); assertEquals(2, categoriesCount); } @Test public void getCategory() throws Exception { final Category category = getProcessAPI().createCategory(name, description); categories.add(category); assertNotNull(category); assertEquals(name, category.getName()); final long categoryId = category.getId(); final Category rCategory = getProcessAPI().getCategory(categoryId); assertNotNull(rCategory); assertEquals(categoryId, rCategory.getId()); assertEquals(name, rCategory.getName()); assertEquals(description, rCategory.getDescription()); } @Test(expected = CategoryNotFoundException.class) public void getCategoryWithCategoryNotFoundException() throws Exception { final Category category = getProcessAPI().createCategory(name, description); categories.add(category); assertNotNull(category); assertEquals(name, category.getName()); getProcessAPI().getCategory(category.getId() + 1); } @Test public void getCategories() throws Exception { final Category category1 = getProcessAPI().createCategory("category1", description); categories.add(category1); final Category category2 = getProcessAPI().createCategory("category2", description); categories.add(category2); final Category category3 = getProcessAPI().createCategory("category3", description); categories.add(category3); final Category category4 = getProcessAPI().createCategory("category4", description); categories.add(category4); final Category category5 = getProcessAPI().createCategory("category5", description); categories.add(category5); List categoriesNameAsc = getProcessAPI().getCategories(0, 2, CategoryCriterion.NAME_ASC); assertEquals(2, categoriesNameAsc.size()); assertEquals(category1.getName(), categoriesNameAsc.get(0).getName()); assertEquals(category2.getName(), categoriesNameAsc.get(1).getName()); categoriesNameAsc = getProcessAPI().getCategories(2, 2, CategoryCriterion.NAME_ASC); assertEquals(2, categoriesNameAsc.size()); assertEquals(category3.getName(), categoriesNameAsc.get(0).getName()); assertEquals(category4.getName(), categoriesNameAsc.get(1).getName()); categoriesNameAsc = getProcessAPI().getCategories(4, 2, CategoryCriterion.NAME_ASC); assertEquals(1, categoriesNameAsc.size()); assertEquals(category5.getName(), categoriesNameAsc.get(0).getName()); categoriesNameAsc = getProcessAPI().getCategories(6, 2, CategoryCriterion.NAME_ASC); assertEquals(0, categoriesNameAsc.size()); } @Test public void updateCategory() throws Exception { final Category oldCategory = getProcessAPI().createCategory(name, description); categories.add(oldCategory); final String newName = "updatedName"; final String newDescription = "updatedDescription"; final CategoryUpdater updater = new CategoryUpdater(); updater.setName(newName).setDescription(newDescription); getProcessAPI().updateCategory(oldCategory.getId(), updater); final Category categoryUpdated = getProcessAPI().getCategory(oldCategory.getId()); assertNotNull(categoryUpdated); assertEquals(newName, categoryUpdated.getName()); assertEquals(newDescription, categoryUpdated.getDescription()); } @Test(expected = CategoryNotFoundException.class) public void updateCategoryWithCategoryNotFoundException() throws Exception { final Category category = getProcessAPI().createCategory(name, description); categories.add(category); final CategoryUpdater updater = new CategoryUpdater(); updater.setName("updatedName").setDescription("updatedDescription"); getProcessAPI().updateCategory(category.getId() + 1, updater); } @Test(expected = UpdateException.class) public void updateCategoryWithCategoryUpdateException() throws Exception { getProcessAPI().updateCategory(0, null); } @Test(expected = CategoryNotFoundException.class) public void deleteCategory() throws Exception { final Category category = getProcessAPI().createCategory(name, description); getProcessAPI().deleteCategory(category.getId()); getProcessAPI().getCategory(category.getId()); } @Test(expected = DeletionException.class) public void deleteCategoryWithCategoryNotFoundException() throws Exception { getProcessAPI().deleteCategory(Long.MAX_VALUE); } @Test(expected = DeletionException.class) public void deleteCategoryWithCategoryDeletionException() throws Exception { getProcessAPI().deleteCategory(0); } @Test public void processDefinitionWithCategoryPagination() throws Exception { final Category category = getProcessAPI().createCategory(name, description); categories.add(category); // test final List processDeploymentInfos = getProcessAPI().getProcessDeploymentInfosOfCategory( category.getId(), 0, 10, ProcessDeploymentInfoCriterion.ACTIVATION_STATE_ASC); assertEquals(0, processDeploymentInfos.size()); } @Test public void removeSeveralCategoriesToProcessDefinition() throws Exception { // generate category id; final Category category1 = getProcessAPI().createCategory("Human resources", "Category for personnel Management"); categories.add(category1); final Category category2 = getProcessAPI().createCategory("Travel Service", "Category for all related travel matters"); categories.add(category2); final Category category3 = getProcessAPI().createCategory("Cleaning Service", "Category for all related travel matters"); categories.add(category3); // generate process definition id final ProcessDefinition processDefinition = generateProcessDefinition(1, "processName", "version").get(0); processDefinitions.add(processDefinition); final long processDefinitionId = processDefinition.getId(); // test number of current categories form this process: getProcessAPI().addCategoriesToProcess(processDefinitionId, Arrays.asList(new Long[] { category1.getId(), category2.getId(), category3.getId() })); List categories = getProcessAPI().getCategoriesOfProcessDefinition(processDefinitionId, 0, 10, CategoryCriterion.NAME_DESC); assertEquals(3, categories.size()); getProcessAPI().removeCategoriesFromProcess(processDefinitionId, Arrays.asList(new Long[] { category1.getId(), category3.getId() })); // test number of current categories form this process: categories = getProcessAPI().getCategoriesOfProcessDefinition(processDefinitionId, 0, 10, CategoryCriterion.NAME_DESC); assertEquals(1, categories.size()); // check if the remaining one is the good one: assertEquals(category2.getId(), categories.get(0).getId()); } @Test public void addSeveralCategoriesToProcessDefinition() throws Exception { final Category category1 = getProcessAPI().createCategory("Human resources", "Category for personnel Management"); categories.add(category1); final Category category2 = getProcessAPI().createCategory("Travel Service", "Category for all related travel matters"); categories.add(category2); final ProcessDefinition processDefinition = generateProcessDefinition(1, "processName", "version").get(0); processDefinitions.add(processDefinition); // test getProcessAPI().addCategoriesToProcess(processDefinition.getId(), Arrays.asList(new Long[] { category1.getId(), category2.getId() })); final List categories = getProcessAPI().getCategoriesOfProcessDefinition(processDefinition.getId(), 0, 10, CategoryCriterion.NAME_DESC); assertEquals(2, categories.size()); assertEquals(category1.getId(), categories.get(1).getId()); assertEquals(category2.getId(), categories.get(0).getId()); } @Test public void addProcessDefinitionToCategory() throws Exception { // generate category id; final Category category = getProcessAPI().createCategory(name, description); categories.add(category); final long categoryId = category.getId(); // generate process definition id final ProcessDefinition processDefinition = generateProcessDefinition(1, "processName", "version").get(0); processDefinitions.add(processDefinition); // test getProcessAPI().addProcessDefinitionToCategory(categoryId, processDefinition.getId()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfosOfCategory(categoryId, 0, 10, ProcessDeploymentInfoCriterion.ACTIVATION_STATE_ASC) .get(0); assertEquals(processDefinition.getName(), processDeploymentInfo.getName()); assertEquals(processDefinition.getVersion(), processDeploymentInfo.getVersion()); } @Test(expected = CreationException.class) public void addProcessDefinitionToUnknownCategory() throws Exception { // generate process definition id final ProcessDefinition processDefinition = generateProcessDefinition(1, "processName", "version").get(0); processDefinitions.add(processDefinition); // test getProcessAPI().addProcessDefinitionToCategory(0, processDefinition.getId()); } @Test(expected = CreationException.class) public void addUnknowProcessDefinitionToCategory() throws Exception { // generate category id; final Category category = getProcessAPI().createCategory(name, description); categories.add(category); // test getProcessAPI().addProcessDefinitionToCategory(category.getId(), 0); } @Test public void addProcessDefinitionosToCategory() throws Exception { // generate category id; final Category category = getProcessAPI().createCategory(name, description); categories.add(category); final long categoryId = category.getId(); // generate process definition id processDefinitions = generateProcessDefinition(3, "process", "version"); final List processDefinitionIds = new ArrayList<>(); for (final ProcessDefinition processDefinition : processDefinitions) { processDefinitionIds.add(processDefinition.getId()); } // test getProcessAPI().addProcessDefinitionsToCategory(categoryId, processDefinitionIds); final List processDeploymentInfoList = getProcessAPI() .getProcessDeploymentInfosOfCategory(categoryId, 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertNotNull(processDeploymentInfoList); assertEquals(3, processDeploymentInfoList.size()); } @Test public void getNumberOfCategoriesByProcessDefinition() throws Exception { // generate categories categories = generateCategory(3, "categoryName", "test get number of categories by process definition"); // generate process definition final ProcessDefinition processDefinition = generateProcessDefinition(1, "processName", "version").get(0); processDefinitions.add(processDefinition); final long processDefinitionId = processDefinition.getId(); // test for (final Category category : categories) { getProcessAPI().addProcessDefinitionToCategory(category.getId(), processDefinitionId); } final long categoryNumber = getProcessAPI().getNumberOfCategories(processDefinitionId); assertEquals(3, categoryNumber); } @Test public void getNumberOfCategoriesOfInexistentProcess() { final long numberOfCategories = getProcessAPI().getNumberOfCategories(Long.MAX_VALUE); assertEquals(0, numberOfCategories); } @Test public void getNumberOfProcessesInCategory() throws Exception { // generate categories final Category category = generateCategory(1, "categoryName", "test get number of processes in category") .get(0); categories.add(category); // generate process definition processDefinitions = generateProcessDefinition(3, "processName", "version"); // test for (final ProcessDefinition processDefinition : processDefinitions) { getProcessAPI().addProcessDefinitionToCategory(category.getId(), processDefinition.getId()); } final long processNumber = getProcessAPI().getNumberOfProcessDefinitionsOfCategory(category.getId()); assertEquals(3, processNumber); } @Test public void getNumberOfProcessesInCategoryWithInexistentCategory() { final long processesInCategory = getProcessAPI().getNumberOfProcessDefinitionsOfCategory(Long.MAX_VALUE); assertEquals(0, processesInCategory); } @Test public void getProcessDeploymentInfosOfCategory() throws Exception { // generate category final Category category = getProcessAPI().createCategory(name, description); categories.add(category); final long categoryId = category.getId(); // generate process definitions processDefinitions = generateProcessDefinition(3, "process", "version"); final List processDefinitionIds = new ArrayList<>(); for (final ProcessDefinition processDefinition : processDefinitions) { processDefinitionIds.add(processDefinition.getId()); } // add process definitions to category getProcessAPI().addProcessDefinitionsToCategory(categoryId, processDefinitionIds); // test final List processDeploymentInfoList_NameASC = getProcessAPI() .getProcessDeploymentInfosOfCategory(categoryId, 0, 3, ProcessDeploymentInfoCriterion.NAME_ASC); assertNotNull(processDeploymentInfoList_NameASC); assertEquals(3, processDeploymentInfoList_NameASC.size()); assertEquals("process1", processDeploymentInfoList_NameASC.get(0).getName()); assertEquals("process2", processDeploymentInfoList_NameASC.get(1).getName()); assertEquals("process3", processDeploymentInfoList_NameASC.get(2).getName()); final List processDeploymentInfoList_NameDESC = getProcessAPI() .getProcessDeploymentInfosOfCategory(categoryId, 0, 3, ProcessDeploymentInfoCriterion.NAME_DESC); assertNotNull(processDeploymentInfoList_NameDESC); assertEquals(3, processDeploymentInfoList_NameDESC.size()); assertEquals("process3", processDeploymentInfoList_NameDESC.get(0).getName()); assertEquals("process2", processDeploymentInfoList_NameDESC.get(1).getName()); assertEquals("process1", processDeploymentInfoList_NameDESC.get(2).getName()); final List outOfRangeList = getProcessAPI().getProcessDeploymentInfosOfCategory( categoryId, 3, 3, ProcessDeploymentInfoCriterion.NAME_DESC); assertEquals(0, outOfRangeList.size()); } @Test public void getCategoriesOfProcessDefinition() throws Exception { // generate categories categories = generateCategory(3, "category", "test get categories of process definition "); // generate process definition final ProcessDefinition processDefinition = generateProcessDefinition(1, "process", "version").get(0); processDefinitions.add(processDefinition); final long processDefinitionId = processDefinition.getId(); // add for (final Category category : categories) { getProcessAPI().addProcessDefinitionToCategory(category.getId(), processDefinitionId); } // test final List categoryList_ASC = getProcessAPI().getCategoriesOfProcessDefinition(processDefinitionId, 0, 10, CategoryCriterion.NAME_ASC); assertNotNull(categoryList_ASC); assertEquals(3, categoryList_ASC.size()); assertEquals("category1", categoryList_ASC.get(0).getName()); assertEquals("category2", categoryList_ASC.get(1).getName()); assertEquals("category3", categoryList_ASC.get(2).getName()); final List categoryList_DESC = getProcessAPI().getCategoriesOfProcessDefinition(processDefinitionId, 0, 10, CategoryCriterion.NAME_DESC); assertNotNull(categoryList_DESC); assertEquals(3, categoryList_DESC.size()); assertEquals("category3", categoryList_DESC.get(0).getName()); assertEquals("category2", categoryList_DESC.get(1).getName()); assertEquals("category1", categoryList_DESC.get(2).getName()); final List outOfRangeCategories = getProcessAPI() .getCategoriesOfProcessDefinition(processDefinitionId, 3, 10, CategoryCriterion.NAME_ASC); assertEquals(0, outOfRangeCategories.size()); } @Test public void getCategoriesUnrelatedToProcessDefinition() throws Exception { // generate categories categories = generateCategory(3, "category", "test get categories of process definition "); // generate process definition processDefinitions = generateProcessDefinition(3, "process", "version"); final long processDefinitionId = processDefinitions.get(0).getId(); // add getProcessAPI().addProcessDefinitionToCategory(categories.get(1).getId(), processDefinitions.get(1).getId()); getProcessAPI().addProcessDefinitionToCategory(categories.get(1).getId(), processDefinitions.get(2).getId()); getProcessAPI().addProcessDefinitionToCategory(categories.get(2).getId(), processDefinitions.get(2).getId()); // Test : No category related final List categoryList_ASC = getProcessAPI().getCategoriesUnrelatedToProcessDefinition( processDefinitionId, 0, 10, CategoryCriterion.NAME_ASC); assertNotNull(categoryList_ASC); assertEquals(3, categoryList_ASC.size()); assertEquals("category1", categoryList_ASC.get(0).getName()); assertEquals("category2", categoryList_ASC.get(1).getName()); assertEquals("category3", categoryList_ASC.get(2).getName()); // Test : 2 related categories final List categoryList_DESC = getProcessAPI().getCategoriesUnrelatedToProcessDefinition( processDefinitions.get(2).getId(), 0, 10, CategoryCriterion.NAME_DESC); assertNotNull(categoryList_DESC); assertEquals(1, categoryList_DESC.size()); assertEquals("category1", categoryList_DESC.get(0).getName()); } @Test public void searchCategoriesNotOfProcessDefinition() throws Exception { // generate categories categories = generateCategory(3, "category", "test get categories of process definition "); // generate process definition final ProcessDefinition processDefinition = generateProcessDefinition(1, "process", "version").get(0); processDefinitions.add(processDefinition); final long processDefinitionId = processDefinition.getId(); // add for (final Category category : categories) { getProcessAPI().addProcessDefinitionToCategory(category.getId(), processDefinitionId); } // test final List categoryList_ASC = getProcessAPI().getCategoriesOfProcessDefinition(processDefinitionId, 0, 10, CategoryCriterion.NAME_ASC); assertNotNull(categoryList_ASC); assertEquals(3, categoryList_ASC.size()); assertEquals("category1", categoryList_ASC.get(0).getName()); assertEquals("category2", categoryList_ASC.get(1).getName()); assertEquals("category3", categoryList_ASC.get(2).getName()); final List categoryList_DESC = getProcessAPI().getCategoriesOfProcessDefinition(processDefinitionId, 0, 10, CategoryCriterion.NAME_DESC); assertNotNull(categoryList_DESC); assertEquals(3, categoryList_DESC.size()); assertEquals("category3", categoryList_DESC.get(0).getName()); assertEquals("category2", categoryList_DESC.get(1).getName()); assertEquals("category1", categoryList_DESC.get(2).getName()); final List outOfRangeCategories = getProcessAPI() .getCategoriesOfProcessDefinition(processDefinitionId, 3, 10, CategoryCriterion.NAME_ASC); assertEquals(0, outOfRangeCategories.size()); } @Test public void removeProcessDefinitionsOfCategory() throws Exception { // generate category final Category category = getProcessAPI().createCategory(name, description); categories.add(category); // generate process definitions processDefinitions = generateProcessDefinition(3, "process", "version"); final List processDefinitionIds = new ArrayList<>(); for (final ProcessDefinition processDefinition : processDefinitions) { processDefinitionIds.add(processDefinition.getId()); } // add process definitions to category getProcessAPI().addProcessDefinitionsToCategory(category.getId(), processDefinitionIds); // test getProcessAPI().removeProcessDefinitionsFromCategory(category.getId(), 0, 2); final List processDeploymentInfos = getProcessAPI().getProcessDeploymentInfosOfCategory( category.getId(), 0, 10, ProcessDeploymentInfoCriterion.ACTIVATION_STATE_ASC); assertNotNull(processDeploymentInfos); assertEquals(1, processDeploymentInfos.size()); } @Test public void removeProcessDefinitionsOfUnknownCategoryDoesntThrowException() throws Exception { // generate process definitions processDefinitions = generateProcessDefinition(3, "process", "version"); // test getProcessAPI().removeProcessDefinitionsFromCategory(Long.MAX_VALUE, 0, 0); } @Test public void removeProcessDefinitionFromCategory() throws Exception { // generate categories categories = generateCategory(3, "category", "test remove one specified process definition from all categories "); // generate process definition final ProcessDefinition processDefinition = generateProcessDefinition(1, "process", "version").get(0); processDefinitions.add(processDefinition); final long processDefinitionId = processDefinition.getId(); // add for (final Category category : categories) { getProcessAPI().addProcessDefinitionToCategory(category.getId(), processDefinitionId); } long count = getProcessAPI().getNumberOfCategories(processDefinitionId); assertEquals(3, count); // test getProcessAPI().removeCategoriesFromProcessDefinition(processDefinitionId, 0, 2); count = getProcessAPI().getNumberOfCategories(processDefinitionId); assertEquals(1, count); } @Test public void removeUnknowProcessDefinitionFromCategoryDontThrowsException() throws Exception { getProcessAPI().removeCategoriesFromProcessDefinition(0, 0, 1); } @Test public void getNumberOfUnCategoriedProcessesDefinitions() throws Exception { long processDefinitionCount = getProcessAPI().getNumberOfUncategorizedProcessDefinitions(); assertEquals(0, processDefinitionCount); final ProcessDefinition processDefinition = generateProcessDefinition(1, "processName", "version").get(0); processDefinitions.add(processDefinition); final long processDefinitionId = processDefinition.getId(); processDefinitionCount = getProcessAPI().getNumberOfUncategorizedProcessDefinitions(); assertEquals(1, processDefinitionCount); } @Test public void getUnCategoriedProcesses() throws Exception { processDefinitions = generateProcessDefinition(3, "process", "version"); final List processDeploymentInfoList_NameASC = getProcessAPI() .getUncategorizedProcessDeploymentInfos(0, 3, ProcessDeploymentInfoCriterion.NAME_ASC); assertNotNull(processDeploymentInfoList_NameASC); assertEquals(3, processDeploymentInfoList_NameASC.size()); assertEquals("process1", processDeploymentInfoList_NameASC.get(0).getName()); assertEquals("process2", processDeploymentInfoList_NameASC.get(1).getName()); assertEquals("process3", processDeploymentInfoList_NameASC.get(2).getName()); final List processDeploymentInfoList_NameDESC = getProcessAPI() .getUncategorizedProcessDeploymentInfos(0, 2, ProcessDeploymentInfoCriterion.NAME_DESC); assertNotNull(processDeploymentInfoList_NameDESC); assertEquals(2, processDeploymentInfoList_NameDESC.size()); assertEquals("process3", processDeploymentInfoList_NameDESC.get(0).getName()); assertEquals("process2", processDeploymentInfoList_NameDESC.get(1).getName()); final List outOfRangeProcesses = getProcessAPI().getUncategorizedProcessDeploymentInfos( 3, 3, ProcessDeploymentInfoCriterion.NAME_DESC); assertEquals(0, outOfRangeProcesses.size()); } private List generateCategory(final int count, final String categoryName, final String description) throws AlreadyExistsException, CreationException { final List categoryList = new ArrayList<>(); for (int i = 1; i <= count; i++) { categoryList.add(getProcessAPI().createCategory(categoryName + i, description + i)); } return categoryList; } private List generateProcessDefinition(final int count, final String processName, final String version) throws InvalidBusinessArchiveFormatException, ProcessDeployException, InvalidProcessDefinitionException, AlreadyExistsException { final List processDefinitionList = new ArrayList<>(); for (int i = 1; i <= count; i++) { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(processName + i, version + i, Arrays.asList("step1"), Arrays.asList(true)); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition) .done(); processDefinitionList.add(getProcessAPI().deploy(businessArchive)); } return processDefinitionList; } @Test(expected = AlreadyExistsException.class) public void cannotAddTheSameCategoryToAProcess() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder(); final DesignProcessDefinition definition = processBuilder.createNewInstance("category", "0.9") .addAutomaticTask("step1").getProcess(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(definition).done(); final ProcessDefinition processDefinition = getProcessAPI().deploy(businessArchive); processDefinitions.add(processDefinition); final Category category = getProcessAPI().createCategory("Human resources", "Category for personnel Management"); categories.add(category); getProcessAPI().addCategoriesToProcess(processDefinition.getId(), Arrays.asList(category.getId())); getProcessAPI().addCategoriesToProcess(processDefinition.getId(), Arrays.asList(category.getId())); fail("It is not allowed to add twice the same category to a process"); } @Test(expected = AlreadyExistsException.class) public void cannotAddTheSameCategoryToAProcessList() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder(); final DesignProcessDefinition definition = processBuilder.createNewInstance("category", "0.9") .addAutomaticTask("step1").getProcess(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(definition).done(); final ProcessDefinition processDefinition = getProcessAPI().deploy(businessArchive); processDefinitions.add(processDefinition); final Category category = getProcessAPI().createCategory("Human resources", "Category for personnel Management"); categories.add(category); getProcessAPI().addProcessDefinitionsToCategory(category.getId(), Arrays.asList(processDefinition.getId())); getProcessAPI().addProcessDefinitionsToCategory(category.getId(), Arrays.asList(processDefinition.getId())); fail("It is not allowed to add twice the same category to a process"); } @Test(expected = AlreadyExistsException.class) public void cannotAssociateTheSameCategoryTwiceWitAProcess() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder(); final DesignProcessDefinition definition = processBuilder.createNewInstance("category", "0.9") .addAutomaticTask("step1").getProcess(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(definition).done(); final ProcessDefinition processDefinition = getProcessAPI().deploy(businessArchive); processDefinitions.add(processDefinition); final Category category = getProcessAPI().createCategory("Human resources", "Category for personnel Management"); categories.add(category); getProcessAPI().addProcessDefinitionToCategory(category.getId(), processDefinition.getId()); getProcessAPI().addProcessDefinitionToCategory(category.getId(), processDefinition.getId()); fail("It is not allowed to add twice the same category to a process"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessDeletionIT.java ================================================ /** * 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.process; import static java.util.Collections.singletonList; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.engine.bpm.category.CategoryCriterion; import org.bonitasoft.engine.bpm.comment.ArchivedComment; import org.bonitasoft.engine.bpm.comment.Comment; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion; import org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.impl.DocumentDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Celine Souchet */ public class ProcessDeletionIT extends TestWithUser { private List processDefinitions; @Override @Before public void before() throws Exception { super.before(); processDefinitions = new ArrayList<>(); } @Override @After public void after() throws Exception { disableAndDeleteProcess(processDefinitions); super.after(); } private ProcessDefinition deployProcessWithSeveralOutGoingTransitions() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("process To Delete", "2.5"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); for (int i = 0; i < 10; i++) { final String activityName = "step2" + i; processDefinitionBuilder.addUserTask(activityName, ACTOR_NAME); processDefinitionBuilder.addTransition("step1", activityName); } return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); } @Test public void deleteProcessInstanceStopsCreatingNewActivities() throws Exception { final ProcessDefinition processDefinition = deployProcessWithSeveralOutGoingTransitions(); processDefinitions.add(processDefinition); // To clean in the end final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step1", user); getProcessAPI().deleteProcessInstance(processInstance.getId()); Thread.sleep(1500); final List taskInstances = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 100, ActivityInstanceCriterion.DEFAULT); assertEquals(0, taskInstances.size()); } @Test public void deleteProcessInstanceAlsoDeleteArchivedElements() throws Exception { final ProcessDefinition processDefinition = deployProcessWithSeveralOutGoingTransitions(); processDefinitions.add(processDefinition); // To clean in the end final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step1", user); for (int i = 0; i < 10; i++) { waitForUserTask(processInstance, "step2" + i); } getProcessAPI().deleteProcessInstance(processInstance.getId()); final List taskInstances = getProcessAPI().getArchivedActivityInstances( processInstance.getId(), 0, 100, ActivityInstanceCriterion.DEFAULT); assertEquals(0, taskInstances.size()); } @Test public void deleteArchivedProcessInstanceAlsoDeleteArchivedElements() throws Exception { final ProcessDefinition processDefinition = deployProcessWithSeveralOutGoingTransitions(); processDefinitions.add(processDefinition); // To clean in the end final ProcessInstance processInstance = startAndFinishProcess(processDefinition); getProcessAPI().deleteArchivedProcessInstances(processDefinition.getId(), 0, 50); final List taskInstances = getProcessAPI().getArchivedActivityInstances( processInstance.getId(), 0, 100, ActivityInstanceCriterion.DEFAULT); assertEquals(0, taskInstances.size()); } @Test public void deleteArchivedProcessInstancesInAllStatesByIdsAlsoDeleteArchivedElements() throws Exception { final ProcessDefinition processDefinition = deployProcessWithSeveralOutGoingTransitions(); processDefinitions.add(processDefinition); // To clean in the end final ProcessInstance processInstance1 = startAndFinishProcess(processDefinition); startAndFinishProcess(processDefinition); startAndFinishProcess(processDefinition); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ProcessInstanceSearchDescriptor.NAME, Order.ASC); final List archivedProcessInstances = getProcessAPI() .searchArchivedProcessInstances(searchOptionsBuilder.done()) .getResult(); getProcessAPI().deleteArchivedProcessInstancesInAllStates( Arrays.asList(archivedProcessInstances.get(0).getSourceObjectId(), archivedProcessInstances.get(2).getSourceObjectId())); final List taskInstances = getProcessAPI().getArchivedActivityInstances( processInstance1.getId(), 0, 100, ActivityInstanceCriterion.DEFAULT); assertEquals(0, taskInstances.size()); final long numberOfArchivedProcessInstancesAfterDelete = getProcessAPI() .searchArchivedProcessInstancesInAllStates(searchOptionsBuilder.done()) .getCount(); assertEquals(3, numberOfArchivedProcessInstancesAfterDelete); } @Test public void deleteArchivedProcessInstancesInAllStatesByIdAlsoDeleteArchivedElements() throws Exception { final ProcessDefinition processDefinition = deployProcessWithSeveralOutGoingTransitions(); processDefinitions.add(processDefinition); // To clean in the end final ProcessInstance processInstance = startAndFinishProcess(processDefinition); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ProcessInstanceSearchDescriptor.NAME, Order.ASC); final List archivedProcessInstances = getProcessAPI() .searchArchivedProcessInstancesInAllStates(searchOptionsBuilder.done()) .getResult(); getProcessAPI().deleteArchivedProcessInstancesInAllStates(archivedProcessInstances.get(0).getSourceObjectId()); final List taskInstances = getProcessAPI().getArchivedActivityInstances( processInstance.getId(), 0, 100, ActivityInstanceCriterion.DEFAULT); assertEquals(0, taskInstances.size()); final long numberOfArchivedProcessInstancesAfterDelete = getProcessAPI() .searchArchivedProcessInstancesInAllStates(searchOptionsBuilder.done()) .getCount(); assertEquals(archivedProcessInstances.size() - 3, numberOfArchivedProcessInstancesAfterDelete); } private ProcessInstance startAndFinishProcess(final ProcessDefinition processDefinition) throws Exception { final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step1", user); for (int i = 0; i < 10; i++) { waitForUserTaskAndExecuteIt(processInstance, "step2" + i, user); } waitForProcessToFinish(processInstance); return processInstance; } @Test public void deleteProcessDefinitionStopsCreatingNewActivities() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("process To Delete", "2.5"); final String actorName = "delivery"; processDefinitionBuilder.addActor(actorName); processDefinitionBuilder.addUserTask("step1", actorName); for (int i = 0; i < 30; i++) { final String activityName = "step2" + i; processDefinitionBuilder.addUserTask(activityName, actorName); processDefinitionBuilder.addTransition("step1", activityName); } final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), actorName, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step1", user); disableAndDeleteProcess(processDefinition.getId()); // will fail in CommonAPITest.succeeded if activities are created after delete Thread.sleep(1500); } @Test public void deleteProcessInstanceAlsoDeleteChildrenProcesses() throws Exception { // deploy a simple process P1 final String simpleStepName = "simpleStep"; final ProcessDefinition simpleProcess = deployAndEnableSimpleProcess("simpleProcess", simpleStepName); processDefinitions.add(simpleProcess); // To clean in the end // deploy a process P2 containing a call activity calling P1 final String intermediateStepName = "intermediateStep1"; final String intermediateCallActivityName = "intermediateCall"; final ProcessDefinition intermediateProcess = deployAndEnableProcessWithCallActivity("intermediateProcess", simpleProcess.getName(), intermediateStepName, intermediateCallActivityName); processDefinitions.add(intermediateProcess); // To clean in the end // deploy a process P3 containing a call activity calling P2 final String rootStepName = "rootStep1"; final String rootCallActivityName = "rootCall"; final ProcessDefinition rootProcess = deployAndEnableProcessWithCallActivity("rootProcess", intermediateProcess.getName(), rootStepName, rootCallActivityName); processDefinitions.add(rootProcess); // To clean in the end // start P3, the call activities will start instances of P2 a and P1 final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcess.getId()); waitForUserTask(rootProcessInstance, simpleStepName); // check that the instances of p1, p2 and p3 were created List processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_ASC); assertEquals(3, processInstances.size()); // check that archived flow nodes List taskInstances = getProcessAPI().getArchivedActivityInstances( rootProcessInstance.getId(), 0, 100, ActivityInstanceCriterion.DEFAULT); assertTrue(taskInstances.size() > 0); // delete the root process instance getProcessAPI().deleteProcessInstance(rootProcessInstance.getId()); // check that the instances of p1 and p2 were deleted processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_ASC); assertEquals(0, processInstances.size()); // check that archived flow nodes were not deleted. taskInstances = getProcessAPI().getArchivedActivityInstances(rootProcessInstance.getId(), 0, 100, ActivityInstanceCriterion.DEFAULT); assertEquals(0, taskInstances.size()); } private ProcessDefinition deployAndEnableSimpleProcess(final String processName, final String userTaskName) throws Exception { final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(processName, "1.0"); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addStartEvent("tStart"); processDefBuilder.addUserTask(userTaskName, ACTOR_NAME); processDefBuilder.addEndEvent("tEnd"); processDefBuilder.addTransition("tStart", userTaskName); processDefBuilder.addTransition(userTaskName, "tEnd"); return deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, user); } private ProcessDefinition deployAndEnableProcessWithCallActivity(final String processName, final String targetProcessName, final String userTaskName, final String callActivityName) throws Exception { final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression(targetProcessName); final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(processName, "1.0"); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addStartEvent("start"); processDefBuilder.addCallActivity(callActivityName, targetProcessNameExpr, null); processDefBuilder.addUserTask(userTaskName, ACTOR_NAME); processDefBuilder.addEndEvent("end"); processDefBuilder.addTransition("start", callActivityName); processDefBuilder.addTransition(callActivityName, userTaskName); processDefBuilder.addTransition(userTaskName, "end"); return deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, user); } @Test public void deleteProcessInstanceAlsoDeleteArchivedChildrenProcesses() throws Exception { // deploy a simple process P1 final String simpleStepName = "simpleStep"; final ProcessDefinition simpleProcess = deployAndEnableSimpleProcess("simpleProcess", simpleStepName); processDefinitions.add(simpleProcess); // To clean in the end // deploy a process P2 containing a call activity calling P1 final String rootStepName = "rootStep1"; final String rootCallActivityName = "rootCall"; final ProcessDefinition rootProcess = deployAndEnableProcessWithCallActivity("rootProcess", simpleProcess.getName(), rootStepName, rootCallActivityName); processDefinitions.add(rootProcess); // To clean in the end // start P2, the call activities will start an instance of P1 final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcess.getId()); final ActivityInstance simpleTask = waitForUserTaskAndGetIt(rootProcessInstance, simpleStepName); final ProcessInstance simpleProcessInstance = getProcessAPI() .getProcessInstance(simpleTask.getParentProcessInstanceId()); assignAndExecuteStep(simpleTask, user.getId()); waitForProcessToFinish(simpleProcessInstance); waitForUserTask(rootProcessInstance, rootStepName); // check that only one instance (p2) is in the journal: p1 is supposed to be archived List processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_ASC); assertEquals(1, processInstances.size()); // check that there are archived instances of p1 List archivedProcessInstanceList = getProcessAPI() .getArchivedProcessInstances(simpleProcessInstance.getId(), 0, 10); assertTrue(archivedProcessInstanceList.size() > 0); // delete the root process instance getProcessAPI().deleteProcessInstance(rootProcessInstance.getId()); // check that the instance of p2 was deleted processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_ASC); assertEquals(0, processInstances.size()); // check that the archived instances of p1 were not deleted archivedProcessInstanceList = getProcessAPI().getArchivedProcessInstances(simpleProcessInstance.getId(), 0, 10); assertEquals(0, archivedProcessInstanceList.size()); // check that archived flow node were not deleted. final List taskInstances = getProcessAPI().getArchivedActivityInstances( rootProcessInstance.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT); assertEquals(0, taskInstances.size()); } @Test public void deleteArchivedProcessInstanceAndChildrenProcesses() throws Exception { // deploy a simple process P1 final String simpleStepName = "simpleStep"; final ProcessDefinition simpleProcess = deployAndEnableSimpleProcess("simpleProcess", simpleStepName); processDefinitions.add(simpleProcess); // To clean in the end // deploy a process P2 containing a call activity calling P1 final String rootStepName = "rootStep1"; final String rootCallActivityName = "rootCall"; final ProcessDefinition rootProcess = deployAndEnableProcessWithCallActivity("rootProcess", simpleProcess.getName(), rootStepName, rootCallActivityName); processDefinitions.add(rootProcess); // To clean in the end // start P2, the call activities will start an instance of P1 final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcess.getId()); final ActivityInstance simpleTask = waitForUserTaskAndAssignIt(rootProcessInstance, simpleStepName, user); final ProcessInstance simpleProcessInstance = getProcessAPI() .getProcessInstance(simpleTask.getParentProcessInstanceId()); getProcessAPI().executeFlowNode(simpleTask.getId()); waitForUserTask(rootProcessInstance, rootStepName); waitForProcessToFinish(simpleProcessInstance); // check that only one instance (p2) is in the journal: p1 is supposed to be archived List processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_ASC); assertEquals(1, processInstances.size()); // check that there are archived instances of p1 List archivedProcessInstanceList = getProcessAPI() .getArchivedProcessInstances(simpleProcessInstance.getId(), 0, 10); assertTrue(archivedProcessInstanceList.size() > 0); // delete archived root process instances getProcessAPI().deleteArchivedProcessInstances(rootProcess.getId(), 0, 30); // check that the instance of p2 was not deleted processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_ASC); assertEquals(1, processInstances.size()); // check that the archived instances of p1 were deleted archivedProcessInstanceList = getProcessAPI().getArchivedProcessInstances(simpleProcessInstance.getId(), 0, 1000); assertEquals(0, archivedProcessInstanceList.size()); // check that archived flow node were deleted. final List taskInstances = getProcessAPI().getArchivedActivityInstances( rootProcessInstance.getId(), 0, 10, ActivityInstanceCriterion.DEFAULT); assertEquals(0, taskInstances.size()); } @Test public void deleteProcessInstanceAlsoDeleteEventSubProcesses() throws Exception { final String parentTaskName = "step1"; final String childTaskName = "subStep"; final String signalName = "go"; // deploy and create a instance of a process containing an event sub-process final ProcessDefinition processDefinition = deployAndEnableProcessWithSignalEventSubProcess(parentTaskName, childTaskName, signalName); processDefinitions.add(processDefinition); // To clean in the end final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(processDefinition.getId()); // wait for the first step in the parent process before sending signal the launch the event sub-process waitForUserTask(rootProcessInstance, parentTaskName); getProcessAPI().sendSignal(signalName); // wait for first step in the event sub-process waitForUserTask(rootProcessInstance, childTaskName); // check the number of process instances: 2 expected the root process instance and the event subprocess List processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.DEFAULT); assertEquals(2, processInstances.size()); // delete the root process instance: the event subprocess must be deleted at same time getProcessAPI().deleteProcessInstance(rootProcessInstance.getId()); // check the number of proces instances processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.DEFAULT); assertEquals(0, processInstances.size()); } private ProcessDefinition deployAndEnableProcessWithSignalEventSubProcess(final String parentTaskName, final String childTaskName, final String signalName) throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithEventSubProcess", "1.0"); builder.addActor("mainActor"); builder.addStartEvent("start"); builder.addUserTask(parentTaskName, "mainActor"); builder.addEndEvent("end"); builder.addTransition("start", parentTaskName); builder.addTransition(parentTaskName, "end"); final SubProcessDefinitionBuilder subProcessBuilder = builder.addSubProcess("eventSubProcess", true) .getSubProcessBuilder(); subProcessBuilder.addStartEvent("startSub").addSignalEventTrigger(signalName); subProcessBuilder.addUserTask(childTaskName, "mainActor"); subProcessBuilder.addEndEvent("endSubProcess"); subProcessBuilder.addTransition("startSub", childTaskName); subProcessBuilder.addTransition(childTaskName, "endSubProcess"); final DesignProcessDefinition processDefinition = builder.done(); return deployAndEnableProcessWithActor(processDefinition, "mainActor", user); } @Test(expected = DeletionException.class) public void deleteJustProcessDefinition() throws Exception { // deploy a simple process final String userTaskName = "step1"; final ProcessDefinition processDefinition = deployAndEnableSimpleProcess("myProcess", userTaskName); processDefinitions.add(processDefinition); // To clean in the end // start a process and execute it until end final ProcessInstance processInstanceToArchive = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstanceToArchive, userTaskName, user); waitForProcessToFinish(processInstanceToArchive); // start a process non completed process final ProcessInstance activeProcessInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(activeProcessInstance, userTaskName); // check number of process instances and archived process instances List processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.DEFAULT); final List archivedProcessInstances = getProcessAPI().getArchivedProcessInstances(0, 10, ProcessInstanceCriterion.DEFAULT); assertEquals(1, processInstances.size()); assertEquals(1, archivedProcessInstances.size()); // delete definition try { getProcessAPI().disableAndDeleteProcessDefinition(processDefinition.getId()); } finally { // check number of process instances and archived process instances processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.DEFAULT); assertFalse(processInstances.isEmpty()); List archivedProcessInstanceList = getProcessAPI() .getArchivedProcessInstances(processInstanceToArchive.getId(), 0, 10); assertFalse(archivedProcessInstanceList.isEmpty()); archivedProcessInstanceList = getProcessAPI().getArchivedProcessInstances(activeProcessInstance.getId(), 0, 10); assertFalse(archivedProcessInstanceList.isEmpty()); } } @Test public void deleteProcessDefinition() throws Exception { // deploy a simple process final String userTaskName = "step1"; final ProcessDefinition processDefinition = deployAndEnableSimpleProcess("myProcess", userTaskName); // delete definition getProcessAPI().disableAndDeleteProcessDefinition(processDefinition.getId()); final SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 5); optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.DEPLOYMENT_DATE, Order.DESC); final SearchResult searchResult = getProcessAPI() .searchProcessDeploymentInfos(optsBuilder.done()); assertEquals(0, searchResult.getCount()); assertTrue(searchResult.getResult().isEmpty()); } @Test public void should_delete_archived_version_of_process_instance() throws Exception { // deploy a simple process final String userTaskName = "step1"; final ProcessDefinition processDefinition = deployAndEnableSimpleProcess("myProcess", userTaskName); processDefinitions.add(processDefinition); // To clean in the end // start a process non completed process final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, userTaskName); // delete the process instance getProcessAPI().deleteProcessInstance(processInstance.getId()); // check that all archived process instance related to this process were deleted final List archivedProcessInstanceList = getProcessAPI() .getArchivedProcessInstances(processInstance.getId(), 0, 10); assertEquals(0, archivedProcessInstanceList.size()); } @Test public void deleteArchivedProcessInstances() throws Exception { // deploy a simple process final String userTaskName = "step1"; final ProcessDefinition processDefinition = deployAndEnableSimpleProcess("myProcess", userTaskName); processDefinitions.add(processDefinition); // To clean in the end // start a process non completed process final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, userTaskName); // delete archived process instances getProcessAPI().deleteArchivedProcessInstances(processDefinition.getId(), 0, 1000); // check that all archived process instance related to this process were deleted final List archivedProcessInstanceList = getProcessAPI() .getArchivedProcessInstances(processInstance.getId(), 0, 10); assertEquals(0, archivedProcessInstanceList.size()); } @Test public void deleteProcessInstanceAlsoDeleteDocuments() throws Exception { // deploy and instantiate a process containing data and documents final String userTaskName = "step1"; final String url = "http://intranet.bonitasoft.com/private/docStorage/anyValue"; final ProcessDefinition processDefinition = deployAndEnableProcessWithDocument("myProcess", userTaskName, "Doc", url); processDefinitions.add(processDefinition); // To clean in the end final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, userTaskName); // check the number of data and documents SearchResult documentsSearchResult = getProcessAPI() .searchDocuments(new SearchOptionsBuilder(0, 10).done()); assertEquals(1, documentsSearchResult.getCount()); // delete process instance getProcessAPI().deleteProcessInstance(processInstance.getId()); // check the number of data and documents documentsSearchResult = getProcessAPI().searchDocuments(new SearchOptionsBuilder(0, 10).done()); assertEquals(0, documentsSearchResult.getCount()); } private ProcessDefinition deployAndEnableProcessWithDocument(final String processName, final String userTaskName, final String docName, final String url) throws Exception { final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(processName, "1.0"); processDefBuilder.addActor(ACTOR_NAME); final DocumentDefinitionBuilder documentDefinitionBuilder = processDefBuilder.addDocumentDefinition(docName); documentDefinitionBuilder.addUrl(url); processDefBuilder.addStartEvent("tStart"); processDefBuilder.addUserTask(userTaskName, ACTOR_NAME); processDefBuilder.addEndEvent("tEnd"); processDefBuilder.addTransition("tStart", userTaskName); processDefBuilder.addTransition(userTaskName, "tEnd"); return deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, user); } @Test public void deleteProcessInstanceAlsoDeleteComments() throws Exception { // deploy and start a simple process final String userTaskName = "step1"; final ProcessDefinition processDefinition = deployAndEnableSimpleProcess("myProcess", userTaskName); processDefinitions.add(processDefinition); // To clean in the end final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, userTaskName); // add a comment getProcessAPI().addProcessComment(processInstance.getId(), "just do it."); SearchResult searchResult = getProcessAPI().searchComments(new SearchOptionsBuilder(0, 10).done()); assertTrue(searchResult.getCount() > 0); // delete process instance getProcessAPI().deleteProcessInstance(processInstance.getId()); // check all comments were deleted searchResult = getProcessAPI().searchComments(new SearchOptionsBuilder(0, 10).done()); assertEquals(0, searchResult.getCount()); } @Test public void deleteProcessInstanceAndComments() throws Exception { // deploy and start a simple process final String userTaskName = "etapa1"; final ProcessDefinition processDefinition = deployAndEnableSimpleProcess("ArchivedCommentsDeletion", userTaskName); processDefinitions.add(processDefinition); // To clean in the end final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long userTaskId = waitForUserTask(processInstance, userTaskName); // add a comment getProcessAPI().addProcessComment(processInstance.getId(), "just do it."); assignAndExecuteStep(userTaskId, user); waitForProcessToFinish(processInstance); SearchResult searchResult = getProcessAPI() .searchArchivedComments(new SearchOptionsBuilder(0, 10).done()); assertTrue(searchResult.getCount() > 0); getProcessAPI().deleteProcessInstances(processDefinition.getId(), 0, 10); // check all archived comments were deleted along with process instance: searchResult = getProcessAPI().searchArchivedComments(new SearchOptionsBuilder(0, 10).done()); assertEquals(2, searchResult.getCount()); // "just do it." && technical comment } @Test public void deleteArchivedProcessInstanceAndComments() throws Exception { // deploy and start a simple process final String userTaskName = "etapa1"; final ProcessDefinition processDefinition = deployAndEnableSimpleProcess("ArchivedCommentsDeletion", userTaskName); processDefinitions.add(processDefinition); // To clean in the end final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long userTaskId = waitForUserTask(processInstance, userTaskName); // add a comment getProcessAPI().addProcessComment(processInstance.getId(), "just do it2."); assignAndExecuteStep(userTaskId, user); waitForProcessToFinish(processInstance); SearchResult searchResult = getProcessAPI() .searchArchivedComments(new SearchOptionsBuilder(0, 10).done()); assertTrue(searchResult.getCount() > 0); getProcessAPI().deleteArchivedProcessInstances(processDefinition.getId(), 0, 10); // check all archived comments were deleted along with process instance: searchResult = getProcessAPI().searchArchivedComments(new SearchOptionsBuilder(0, 10).done()); assertEquals(0, searchResult.getCount()); } @Test public void deleteShouldNotKeepResources() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process", "1.0", Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); Category category = getProcessAPI().createCategory("processCategory", "category assigned to process"); getProcessAPI().addCategoriesToProcess(processDefinition.getId(), singletonList(category.getId())); assertEquals(1, getProcessAPI() .getCategoriesOfProcessDefinition(processDefinition.getId(), 0, 5, CategoryCriterion.NAME_ASC).size()); getProcessAPI().createProcessSupervisorForUser(processDefinition.getId(), user.getId()); getProcessAPI().createProcessSupervisorForGroup(processDefinition.getId(), createGroup("supervisingGroup").getId()); getProcessAPI().createProcessSupervisorForRole(processDefinition.getId(), createRole("supervisingRole").getId()); SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 5); builder.filter(ProcessSupervisorSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId()); assertEquals(3, getProcessAPI().searchProcessSupervisors(builder.done()).getCount()); disableAndDeleteProcess(processDefinition); assertEquals(0, getProcessAPI() .getCategoriesOfProcessDefinition(processDefinition.getId(), 0, 5, CategoryCriterion.NAME_ASC).size()); assertEquals(0, getProcessAPI().searchProcessSupervisors(builder.done()).getCount()); assertNotNull(getProcessAPI().getCategory(category.getId())); } @Test public void deleteShouldNotModifyAnotherProcess() throws Exception { final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process1", "1.0", Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, ACTOR_NAME, user); final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process2", "1.0", Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, ACTOR_NAME, user); Category category1 = getProcessAPI().createCategory("processCategory1", "category assigned to both processes"); getProcessAPI().addCategoriesToProcess(processDefinition1.getId(), singletonList(category1.getId())); getProcessAPI().addCategoriesToProcess(processDefinition2.getId(), singletonList(category1.getId())); Category category2 = getProcessAPI().createCategory("processCategory2", "category assigned only to second process"); getProcessAPI().addCategoriesToProcess(processDefinition2.getId(), singletonList(category2.getId())); assertEquals(1, getProcessAPI() .getCategoriesOfProcessDefinition(processDefinition1.getId(), 0, 5, CategoryCriterion.NAME_ASC).size()); assertEquals(2, getProcessAPI() .getCategoriesOfProcessDefinition(processDefinition2.getId(), 0, 5, CategoryCriterion.NAME_ASC).size()); getProcessAPI().createProcessSupervisorForUser(processDefinition1.getId(), user.getId()); getProcessAPI().createProcessSupervisorForUser(processDefinition2.getId(), user.getId()); SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 5); builder.filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()); assertEquals(2, getProcessAPI().searchProcessSupervisors(builder.done()).getCount()); disableAndDeleteProcess(processDefinition1); SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 5); builder2.filter(ProcessSupervisorSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition1.getId()); assertEquals(0, getProcessAPI() .getCategoriesOfProcessDefinition(processDefinition1.getId(), 0, 5, CategoryCriterion.NAME_ASC).size()); assertEquals(0, getProcessAPI().searchProcessSupervisors(builder2.done()).getCount()); SearchOptionsBuilder builder3 = new SearchOptionsBuilder(0, 5); builder3.filter(ProcessSupervisorSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition2.getId()); assertEquals(2, getProcessAPI().getCategoriesOfProcessDefinition(processDefinition2.getId(), 0, 5, CategoryCriterion.NAME_ASC).size()); assertEquals(1, getProcessAPI().searchProcessSupervisors(builder3.done()).getCount()); assertNotNull(getProcessAPI().getCategory(category1.getId())); assertNotNull(getProcessAPI().getCategory(category2.getId())); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessDeploymentIT.java ================================================ /** * 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.process; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import java.io.ByteArrayInputStream; import java.io.File; import java.util.Arrays; import java.util.Collections; import java.util.Random; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory; import org.bonitasoft.engine.bpm.bar.actorMapping.Actor; import org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping; import org.bonitasoft.engine.bpm.bar.form.model.FormMappingDefinition; import org.bonitasoft.engine.bpm.bar.form.model.FormMappingModel; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.process.*; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.connectors.TestConnectorWithOutput; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.form.FormMappingTarget; import org.bonitasoft.engine.form.FormMappingType; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.test.APITestUtil; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Frédéric Bouquet * @author Céline Souchet */ public class ProcessDeploymentIT extends TestWithUser { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void deployProcessInDisabledState() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition) .done()); addUserToFirstActorOfProcess(user.getId(), processDefinition); ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState()); getProcessAPI().enableProcess(processDefinition.getId()); processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // Clean up disableAndDeleteProcess(processDefinition); } @Test public void deployProcessFromFile() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition).done(); File folder = temporaryFolder.newFolder(); File tempFile = new File(folder, "tempFile"); BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, tempFile); // read from the file final BusinessArchive readBusinessArchive = BusinessArchiveFactory.readBusinessArchive(tempFile); final ProcessDefinition processDefinition = deployProcess(readBusinessArchive); addUserToFirstActorOfProcess(user.getId(), processDefinition); ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState()); getProcessAPI().enableProcess(processDefinition.getId()); processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // Clean up disableAndDeleteProcess(processDefinition); } @Test public void deployProcessWithUTF8Characteres() throws Exception { // Create process final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1_1"), Arrays.asList(false)); final ProcessDefinition processDefinition1 = deployAndEnableProcess(designProcessDefinition1); assertEquals(APITestUtil.PROCESS_NAME, processDefinition1.getName()); // delete data for test disableAndDeleteProcess(processDefinition1); } @Test public void deployBigProcess() throws Exception { // Create process final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1_1"), Arrays.asList(false)); final byte[] bigContent = new byte[1024 * 1024 * 5]; new Random().nextBytes(bigContent); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition1) .addClasspathResource(new BarResource("bigRessource", bigContent)).done(); final ProcessDefinition processDefinition = deployProcess(businessArchive); getProcessAPI().enableProcess(processDefinition.getId()); disableAndDeleteProcess(processDefinition); } @Test(expected = AlreadyExistsException.class) public void deployProcess2Times() throws Exception { // First process def with 2 instances: final DesignProcessDefinition designProcessDef1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("initTask1"), Arrays.asList(true)); final ProcessDefinition processDef1 = deployAndEnableProcessWithActor(designProcessDef1, APITestUtil.ACTOR_NAME, user); try { deployAndEnableProcessWithActor(designProcessDef1, APITestUtil.ACTOR_NAME, user); } finally { disableAndDeleteProcess(processDef1); } } @Test public void exportBusinessArchiveWithAllArtifacts() throws Exception { final User john = createUser("john", "bpm"); final User jack = createUser("jack", "bpm"); //create the process final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive(); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcess", "1.0"); processDefinitionBuilder.addConnector("connectorName", "theConnectorId", "theConnectorVersion", ConnectorEvent.ON_ENTER); processDefinitionBuilder.addActor("actor"); processDefinitionBuilder.addUserTask("step1", "actor").addUserFilter("userFilterName", "theUserFilterId", "theUserFilterVersion"); processDefinitionBuilder.addParameter("param1", String.class.getName()); processDefinitionBuilder.addDocumentDefinition("myDoc").addContentFileName("myPdfModifiedName.pdf") .addDescription("a cool pdf document") .addMimeType("application/pdf") .addFile("myPdf.pdf").addDescription("my description"); businessArchiveBuilder.setProcessDefinition(processDefinitionBuilder.done()); //create the business archive businessArchiveBuilder.setParameters(Collections.singletonMap("param1", "theValue")); final FormMappingModel formMappingModel = new FormMappingModel(); formMappingModel.addFormMapping( new FormMappingDefinition("theUrl", FormMappingType.TASK, FormMappingTarget.URL, "step1")); businessArchiveBuilder.setFormMappings(formMappingModel); final Actor actor = new Actor("actor"); actor.addUser("john"); final ActorMapping actorMapping = new ActorMapping(); actorMapping.addActor(actor); businessArchiveBuilder.setActorMapping(actorMapping); final byte[] connectorImplementationFile = BuildTestUtil.buildConnectorImplementationFile("theConnectorId", "theConnectorVersion", "impl1", "1.0", TestConnectorWithOutput.class.getName()); businessArchiveBuilder .addConnectorImplementation(new BarResource("theConnctor.impl", connectorImplementationFile)); final byte[] userFilterImpl = BuildTestUtil.buildConnectorImplementationFile("theConnectorId", "theConnectorVersion", "impl1", "1.0", TestConnectorWithOutput.class.getName()); businessArchiveBuilder.addUserFilters(new BarResource("theUserFilter.impl", userFilterImpl)); final byte[] pdfContent = new byte[] { 5, 0, 1, 4, 6, 5, 2, 3, 1, 5, 6, 8, 4, 6, 6, 3, 2, 4, 5 }; businessArchiveBuilder.addDocumentResource(new BarResource("myPdf.pdf", pdfContent)); businessArchiveBuilder .addClasspathResource(BuildTestUtil.generateJarAndBuildBarResource(ProcessAPI.class, "myJar,jar")); businessArchiveBuilder.addExternalResource(new BarResource("index.html", "".getBytes())); businessArchiveBuilder.addExternalResource(new BarResource("content/other.html", "1".getBytes())); //deploy final BusinessArchive businessArchive = businessArchiveBuilder.done(); final ProcessDefinition processDefinition = getProcessAPI().deploy(businessArchive); assertThat(getProcessAPI().getProcessResolutionProblems(processDefinition.getId())).isEmpty(); getProcessAPI().enableProcess(processDefinition.getId()); //modify //export final byte[] bytes = getProcessAPI().exportBarProcessContentUnderHome(processDefinition.getId()); final BusinessArchive exportedBAR = BusinessArchiveFactory.readBusinessArchive(new ByteArrayInputStream(bytes)); //check assertThat(exportedBAR.getResources().keySet()).containsAll(businessArchive.getResources().keySet()); assertThat(exportedBAR.getFormMappingModel().getFormMappings()) .containsAll(businessArchive.getFormMappingModel().getFormMappings()); assertThat(exportedBAR.getParameters()).isEqualTo(businessArchive.getParameters()); assertThat(exportedBAR.getProcessDefinition()).isEqualTo(businessArchive.getProcessDefinition()); deleteUsers(john, jack); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessDescriptionIT.java ================================================ /** * 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.process; import static org.junit.Assert.assertEquals; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.junit.Test; public class ProcessDescriptionIT extends TestWithUser { @Test public void allInstanceDescriptions() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("descProcess", "1.0"); processBuilder.addDescription("processDescription"); processBuilder.addActor(ACTOR_NAME).addDescription("actorDescription"); processBuilder.addBooleanData("booleanProcessData", null).addDescription("descBooleanProcessData"); processBuilder.addStartEvent("start"); processBuilder.addGateway("gateway", GatewayType.PARALLEL).addDescription("descGateway"); processBuilder.addUserTask("userTask", ACTOR_NAME).addDescription("descUserTask") .addBooleanData("booleanUserTaskData", null) .addDescription("descBooleanUserTaskData"); processBuilder.addTransition("start", "gateway"); processBuilder.addTransition("gateway", "userTask"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); assertEquals("processDescription", processInstance.getDescription()); final DataInstance processDataInstance = getProcessAPI().getProcessDataInstance("booleanProcessData", processInstance.getId()); assertEquals("descBooleanProcessData", processDataInstance.getDescription()); final long userTaskId = waitForUserTask(processInstance, "userTask"); final DataInstance activityDataInstance = getProcessAPI().getActivityDataInstance("booleanUserTaskData", userTaskId); assertEquals("descBooleanUserTaskData", activityDataInstance.getDescription()); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessExecutionIT.java ================================================ /** * 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.process; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.test.BuildTestUtil.generateConnectorImplementation; import static org.bonitasoft.engine.test.BuildTestUtil.generateJarAndBuildBarResource; import static org.junit.Assert.*; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; import org.apache.commons.lang3.time.DateUtils; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.comment.Comment; import org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.UserTaskInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.APITestUtil; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.test.TestStates; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ProcessExecutionIT extends TestWithUser { @Rule public ExpectedException expectedException = ExpectedException.none(); private static final Logger LOGGER = LoggerFactory.getLogger(ProcessExecutionIT.class); /** * there was an issue on deploy when the classloader needed to be refreshed * (because of Schemafactory was loading parser not in transaction) * * @throws Exception */ @Test public void ensureADeployWorksAfterAChangeInDependencies() throws Exception { final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process123", "1.0", Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, ACTOR_NAME, user); getCommandAPI().addDependency("kikoo", new byte[] { 0, 2, 3 }); final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process", "1.0", Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); disableAndDeleteProcess(processDefinition); disableAndDeleteProcess(processDefinition1); getCommandAPI().removeDependency("kikoo"); } @Test public void startProcessWithCurrentUser() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process", "1.0", Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // Check that the current getSession() user name is the one used to start the process: LOGGER.debug("current getSession() user name used to start the process: " + processInstance.getStartedBy()); assertEquals(getSession().getUserId(), processInstance.getStartedBy()); // Clean up waitForUserTask(processInstance, "step1"); disableAndDeleteProcess(processDefinition); } @Test public void startProcessFor() throws Exception { final User jack = createUser("jack", PASSWORD); final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process", "1.0", Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(jack.getId(), processDefinition.getId()); try { waitForUserTask(processInstance, "step1"); // Check that the given user name is the one used to start the process: assertEquals(jack.getId(), processInstance.getStartedBy()); assertEquals(user.getId(), processInstance.getStartedBySubstitute()); // Check system comment final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100) .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()) .done(); final List comments = getProcessAPI().searchComments(searchOptions).getResult(); boolean haveCommentForDelegate = false; for (final Comment comment : comments) { haveCommentForDelegate = haveCommentForDelegate || comment.getContent().contains( "The user " + USERNAME + " acting as delegate of the user jack has started the case."); } assertTrue(haveCommentForDelegate); } finally { // Clean up disableAndDeleteProcess(processDefinition); deleteUsers(jack); } } @Test public void createAndExecuteProcessWithAutomaticSteps() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process", "1.1", Arrays.asList("step1", "step2"), Arrays.asList(false, false)); final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition); final ProcessInstance processInstance = getProcessAPI().startProcess(user.getId(), processDefinition.getId()); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Test public void deleteUnknownProcess() throws Exception { expectedException.expect(DeletionException.class); getProcessAPI().deleteProcessDefinition(123456789); } @Test public void executeProcessWithNoActivities() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process", "1.3", Collections. emptyList(), Collections. emptyList()); final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final List activities = getProcessAPI().getActivities(processDefinition.getId(), 0, 200); assertEquals(0, activities.size()); assertTrue("Process instance should be completed", containsState(getProcessAPI().getArchivedProcessInstances(processInstance.getId(), 0, 10), TestStates.NORMAL_FINAL));// FIXME disableAndDeleteProcess(processDefinition); } @Test public void checkStartAndEndDate() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_ProcessToCheckDate", "1.0", Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); long before = new Date().getTime(); Thread.sleep(10); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); Thread.sleep(10); long after = new Date().getTime(); final long startDate = processInstance.getStartDate().getTime(); assertTrue("The process instance must start between " + before + " and " + after + ", but was " + startDate, after >= startDate && startDate >= before); assertEquals(getSession().getUserId(), processInstance.getStartedBy()); final Long step1Id = waitForUserTask("step1"); before = new Date().getTime(); assignAndExecuteStep(step1Id, user); waitForProcessToFinish(processInstance); after = new Date().getTime(); final long endDate = getProcessAPI().getFinalArchivedProcessInstance(processInstance.getId()).getEndDate() .getTime(); assertTrue("The process instance must finish between " + before + " and " + after + ", but was " + endDate, after >= endDate && endDate >= before); disableAndDeleteProcess(processDefinition); } @Test public void checkLastUpdateDateOfAnArchivedProcess() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_ProcessToCheckDate", "1.0", Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); long before = new Date().getTime(); Thread.sleep(10); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); Thread.sleep(10); long after = new Date().getTime(); final long processStartDate = processInstance.getStartDate().getTime(); assertTrue("The process instance " + processInstance.getName() + " must start between <" + before + "> and <" + after + ">, but was <" + processStartDate + ">", after >= processStartDate && processStartDate >= before); assertEquals(getSession().getUserId(), processInstance.getStartedBy()); final Long step1Id = waitForUserTask("step1"); before = new Date().getTime(); assignAndExecuteStep(step1Id, user); waitForProcessToFinish(processInstance); after = new Date().getTime(); final long lastUpdate = getProcessAPI().getFinalArchivedProcessInstance(processInstance.getId()).getLastUpdate() .getTime(); assertTrue("The process instance " + processInstance.getName() + " must update in last between <" + before + "> and <" + after + ">, but was <" + lastUpdate + ">", after >= lastUpdate && lastUpdate >= before); disableAndDeleteProcess(processDefinition); } @Test public void checkProcessIsArchived() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addStartEvent("start"); builder.addUserTask("step1", ACTOR_NAME); builder.addEndEvent("end"); builder.addTransition("start", "step1").addTransition("step1", "end"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final Long step1Id = waitForUserTask(processInstance, "step1"); final List archs = getProcessAPI().getArchivedProcessInstances(processInstance.getId(), 0, 100); assertEquals(1, archs.size()); assertEquals(TestStates.INITIALIZING.getStateName(), archs.get(0).getState()); assignAndExecuteStep(step1Id, user); waitForProcessToFinish(processInstance); // Verify if the process instance is archived final ArchivedProcessInstance archivedProcessInstance = getProcessAPI() .getFinalArchivedProcessInstance(processInstance.getId()); assertNotNull(archivedProcessInstance); try { getProcessAPI().getProcessInstance(processInstance.getId()); fail("A ProcessInstanceNotFoundException should have been raised"); } catch (final ProcessInstanceNotFoundException e) { // ok } // Verify if the flow node instances are archived final SearchOptionsBuilder optionsBuilder = new SearchOptionsBuilder(0, 20); optionsBuilder.sort(ArchivedFlowNodeInstanceSearchDescriptor.NAME, Order.ASC); optionsBuilder.filter(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, "completed"); final List archivedFlowNodeInstances = getProcessAPI() .searchArchivedFlowNodeInstances(optionsBuilder.done()).getResult(); // To uncomment if need to fix BS-11970 // assertEquals(3, archivedFlowNodeInstances.size()); // assertEquals("end", archivedFlowNodeInstances.get(0).getName()); // assertEquals("start", archivedFlowNodeInstances.get(1).getName()); // assertEquals("step1", archivedFlowNodeInstances.get(2).getName()); // To remove if need to fix BS-11970 assertEquals(1, archivedFlowNodeInstances.size()); assertEquals("step1", archivedFlowNodeInstances.get(0).getName()); disableAndDeleteProcess(processDefinition); } @Test public void getArchivedProcessInstanceById() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("ProcessToArchive", "1.0", Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, APITestUtil.ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step1"); final List archs = getProcessAPI().getArchivedProcessInstances(processInstance.getId(), 0, 100); assertEquals(1, archs.size()); final ArchivedProcessInstance archivedProcessInstance = archs.get(0); assertEquals(archivedProcessInstance, getProcessAPI().getArchivedProcessInstance(archivedProcessInstance.getId())); disableAndDeleteProcess(processDefinition); } @Test(expected = ArchivedProcessInstanceNotFoundException.class) public void getArchivedProcessInstanceByIdNotFound() throws Exception { getProcessAPI().getArchivedProcessInstance(123456789L); } @Test public void checkArchiveDate() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_ProcessToCheckDate", "1.0", Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); final long before = new Date().getTime(); assignAndExecuteStep(step1Id, user.getId()); waitForProcessToFinish(processInstance); final long after = new Date().getTime(); final ArchivedProcessInstance archivedProcessInstance = getProcessAPI() .getFinalArchivedProcessInstance(processInstance.getId()); assertNotNull(archivedProcessInstance); long archiveDate = archivedProcessInstance.getArchiveDate().getTime(); assertTrue("The process must be archived between " + before + " and " + after + ", but was " + archiveDate, after >= archiveDate && archiveDate >= before); final ArchivedActivityInstance archivedActivityInstance = getProcessAPI().getArchivedActivityInstance(step1Id); assertNotNull(archivedActivityInstance); assertEquals(step1Id, archivedActivityInstance.getSourceObjectId()); archiveDate = archivedActivityInstance.getArchiveDate().getTime(); assertTrue("The step1 must be archived between " + before + " and " + after + ", but was " + archiveDate, after >= archiveDate && archiveDate >= before); disableAndDeleteProcess(processDefinition); } @Test public void checkArchiveStartedBy() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_ProcessToCheckDate", "1.0", Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); assertEquals(user.getId(), processInstance.getStartedBy()); waitForUserTaskAndExecuteIt(processInstance, "step1", user); waitForProcessToFinish(processInstance); final ArchivedProcessInstance archivedProcessInstance = getProcessAPI() .getFinalArchivedProcessInstance(processInstance.getId()); assertNotNull(archivedProcessInstance); assertEquals(user.getId(), archivedProcessInstance.getStartedBy()); disableAndDeleteProcess(processDefinition); } @Test public void activityDisplayDescriptionUndefined() throws Exception { // create process definition; final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION) .addActor(ACTOR_NAME).addAutomaticTask("auto1").addUserTask("task1", ACTOR_NAME).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi = getProcessAPI().startProcess(processDefinition.getId()); final ArchivedActivityInstance auto1 = waitForActivityInCompletedState(pi, "auto1", true); assertEquals(null, auto1.getDisplayDescription()); final ActivityInstance activityInstance = waitForTaskInState(pi, "task1", TestStates.READY); assertEquals(null, activityInstance.getDisplayDescription()); disableAndDeleteProcess(processDefinition); } @Test public void should_update_and_sort_due_date_with_null_values() throws Exception { //given final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("processWithNullDueDate", "7.4"); builder.addActor(ACTOR_NAME); builder.addUserTask("step1", ACTOR_NAME).addExpectedDuration(3600L); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition.getId()); final UserTaskInstance todayTask = (UserTaskInstance) waitForUserTaskAndAssignIt(processInstance1, "step1", user); final UserTaskInstance nextWeekTask = (UserTaskInstance) waitForUserTaskAndAssignIt(processInstance2, "step1", user); final UserTaskInstance nullDueDate = (UserTaskInstance) waitForUserTaskAndAssignIt(processInstance3, "step1", user); //when getProcessAPI().updateDueDateOfTask(nullDueDate.getId(), null); final Date nextWeekDueDate = DateUtils.addDays(nextWeekTask.getExpectedEndDate(), 7); getProcessAPI().updateDueDateOfTask(nextWeekTask.getId(), nextWeekDueDate); //new business logic for sort to be used in portal final SearchResult ascNullsLast = getHumanTaskInstanceSearchResult(Order.ASC_NULLS_LAST); final SearchResult descNullsFirst = getHumanTaskInstanceSearchResult(Order.DESC_NULLS_FIRST); //not required by business logic but tested to verify orderBy sql keyword are valid SQL final SearchResult ascNullsFirst = getHumanTaskInstanceSearchResult(Order.ASC_NULLS_FIRST); final SearchResult descNullsLast = getHumanTaskInstanceSearchResult(Order.DESC_NULLS_LAST); //then assertThat(ascNullsLast.getResult()).extracting("id") .as("should have null as last value") .containsExactly(todayTask.getId(), nextWeekTask.getId(), nullDueDate.getId()); assertThat(ascNullsFirst.getResult()).extracting("id") .as("should have null as first value") .containsExactly(nullDueDate.getId(), todayTask.getId(), nextWeekTask.getId()); assertThat(descNullsFirst.getResult()).extracting("id") .as("should have null as first value") .containsExactly(nullDueDate.getId(), nextWeekTask.getId(), todayTask.getId()); assertThat(descNullsLast.getResult()).extracting("id") .as("should have null as first value") .containsExactly(nextWeekTask.getId(), todayTask.getId(), nullDueDate.getId()); assertThat(getProcessAPI().getHumanTaskInstance(nullDueDate.getId()).getExpectedEndDate()) .as("should have updated expected date to null").isNull(); assertThat(getProcessAPI().getHumanTaskInstance(nextWeekTask.getId()).getExpectedEndDate()) .as("should have updated expected date to next week") .isEqualTo(nextWeekDueDate); disableAndDeleteProcess(processDefinition); } public SearchResult getHumanTaskInstanceSearchResult(Order order) throws SearchException { return getProcessAPI() .searchHumanTaskInstances(new SearchOptionsBuilder(0, 100) .sort(HumanTaskInstanceSearchDescriptor.DUE_DATE, order).done()); } @Test(expected = UpdateException.class) public void updateDueDateOfUnknownTask() throws Exception { getProcessAPI().updateDueDateOfTask(123456789L, new Date()); } @Test public void should_be_able_to_set_due_date_with_connector() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("processWithDueDate", "1.0"); builder.addActor(ACTOR_NAME); builder.addUserTask("step1", ACTOR_NAME) .addConnector("setDueDate", "dueDateConnector", "1.0", ConnectorEvent.ON_ENTER); BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(builder.done()) .addConnectorImplementation( generateConnectorImplementation("dueDateConnector", "1.0", SetDueDateConnector.class)) .addClasspathResource(generateJarAndBuildBarResource(SetDueDateConnector.class, "dueDate.jar")).done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId()); final HumanTaskInstance step1 = waitForUserTaskAndGetIt(processInstance1, "step1"); assertThat(step1.getExpectedEndDate()).isEqualTo(SetDueDateConnector.dueDate); disableAndDeleteProcess(processDefinition); } @Test public void executeTaskFor() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final User jack = createUser("jack", PASSWORD); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, APITestUtil.ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); try { // execute step 1 using john final ActivityInstance step1 = waitForUserTaskAndGetIt(processInstance, "step1"); assertEquals(0, step1.getExecutedBy()); getProcessAPI().assignUserTask(step1.getId(), jack.getId()); getProcessAPI().executeFlowNode(jack.getId(), step1.getId()); waitForUserTask(processInstance, "step2"); // check that the step1 was executed by john final ArchivedActivityInstance step1Archived = getProcessAPI().getArchivedActivityInstance(step1.getId()); assertEquals(jack.getId(), step1Archived.getExecutedBy()); assertEquals(user.getId(), step1Archived.getExecutedBySubstitute()); // Check system comment final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100) .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()) .done(); final List comments = getProcessAPI().searchComments(searchOptions).getResult(); boolean haveCommentForDelegate = false; for (final Comment comment : comments) { haveCommentForDelegate = haveCommentForDelegate || comment.getContent().contains("The user " + USERNAME + " acting as delegate of the user jack has done the task \"step1\"."); } assertTrue(haveCommentForDelegate); } finally { // clean disableAndDeleteProcess(processDefinition); deleteUsers(jack); } } @Test public void systemCommentsShouldBeAutoAddedAtTaskAssignment() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("ProcessToArchive", "1.0"); builder.addActor(ACTOR_NAME); builder.addUserTask("step1", ACTOR_NAME) .addDisplayName(new ExpressionBuilder().createConstantStringExpression("Step1 display name")); builder.addUserTask("step2", ACTOR_NAME); builder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), APITestUtil.ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step1", user); waitForUserTask(processInstance, "step2"); final SearchResult searchResult = getProcessAPI() .searchComments(new SearchOptionsBuilder(0, 5).done()); final List commentList = searchResult.getResult(); assertEquals(1, commentList.size()); assertEquals("The task \"Step1 display name\" is now assigned to " + USERNAME, commentList.get(0).getContent()); assertEquals(1, getProcessAPI().countComments(new SearchOptionsBuilder(0, 5).done())); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessManagementIT.java ================================================ /** * 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.process; import static java.util.concurrent.TimeUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.junit.Assert.*; import java.io.Serializable; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.connector.ArchiveConnectorInstancesSearchDescriptor; import org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.connector.ConnectorState; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ActivityStates; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.TaskPriority; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessActivationException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoUpdater; import org.bonitasoft.engine.bpm.process.ProcessEnablementException; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion; import org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.StartEventDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.connectors.TestConnectorThatThrowException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.ExecutionException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.APITestUtil; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.test.StartProcessUntilStep; import org.bonitasoft.engine.test.TestStates; import org.junit.Test; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Frédéric Bouquet * @author Céline Souchet */ public class ProcessManagementIT extends TestWithUser { @Test public void getProcessDeployInfo() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final Date before = new Date(); Thread.sleep(10); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition) .done()); addUserToFirstActorOfProcess(user.getId(), processDefinition); Thread.sleep(10); final Date after = new Date(); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); deleteProcess(processDefinition); assertNotNull(processDeploymentInfo); assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState()); assertEquals(APITestUtil.PROCESS_NAME, processDeploymentInfo.getName()); assertEquals(APITestUtil.PROCESS_VERSION, processDeploymentInfo.getVersion()); assertEquals(getSession().getUserId(), processDeploymentInfo.getDeployedBy()); final Date deployDate = processDeploymentInfo.getDeploymentDate(); assertTrue( "deploy date was too soon (expected > " + before.getTime() + " but was " + deployDate.getTime() + ")", deployDate.after(before)); assertTrue("deploy date was too late (expected < " + after.getTime() + " but was " + deployDate.getTime() + ")", deployDate.before(after)); } @Test(expected = ProcessActivationException.class) public void runDisabledProcess() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition) .done()); addUserToFirstActorOfProcess(user.getId(), processDefinition); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState()); try { getProcessAPI().startProcess(user.getId(), processDefinition.getId()); } finally { deleteProcess(processDefinition); } } @Test public void disableProcess() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); getProcessAPI().disableProcess(processDefinition.getId()); processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState()); deleteProcess(processDefinition); } @Test(expected = ProcessActivationException.class) public void disableDisabledProcess() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition) .done()); try { getProcessAPI().disableProcess(processDefinition.getId()); getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()); } finally { deleteProcess(processDefinition); } } @Test public void enableEnabledProcess() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); try { getProcessAPI().enableProcess(processDefinition.getId()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState()); fail("expected a ProcessEnablementException"); } catch (final ProcessEnablementException e) { // ok } finally { getProcessAPI().disableProcess(processDefinition.getId()); deleteProcess(processDefinition); } } @Test(expected = DeletionException.class) public void deleteEnabledProcess() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); try { deleteProcess(processDefinition); } finally { getProcessAPI().disableProcess(processDefinition.getId()); deleteProcess(processDefinition); } } @Test public void deleteProcess() throws Exception { final long numberOfProcesses = getProcessAPI().getNumberOfProcessDeploymentInfos(); final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition) .done()); assertNotNull(getProcessAPI().getProcessDefinition(processDefinition.getId())); assertNotNull(getProcessAPI().getProcessDeploymentInfo(processDefinition.getId())); assertEquals(numberOfProcesses + 1, getProcessAPI().getNumberOfProcessDeploymentInfos()); deleteProcess(processDefinition); assertEquals(numberOfProcesses, getProcessAPI().getNumberOfProcessDeploymentInfos()); try { getProcessAPI().getProcessDefinition(processDefinition.getId()); fail("Should throw ProcessDefinitionNotFoundException"); } catch (final ProcessDefinitionNotFoundException e) { // ok } try { getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()); fail("Should throw ProcessDefinitionNotFoundException"); } catch (final ProcessDefinitionNotFoundException e) { // ok } } @Test public void getProcessesList() throws Exception { assertEquals(0, getProcessAPI().getNumberOfProcessDeploymentInfos()); final List ids = createProcessDefinitionWithTwoHumanStepsAndDeployBusinessArchive(89); assertEquals(89, getProcessAPI().getNumberOfProcessDeploymentInfos()); final List processes = getProcessAPI().getProcessDeploymentInfos(0, 10, ProcessDeploymentInfoCriterion.NAME_DESC); assertEquals(10, processes.size()); assertEquals("ProcessName88", processes.get(0).getName()); assertEquals("ProcessName79", processes.get(9).getName()); final List processes1 = getProcessAPI().getProcessDeploymentInfos(0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(10, processes1.size()); assertEquals("ProcessName00", processes1.get(0).getName()); assertEquals("ProcessName09", processes1.get(9).getName()); final List processes2 = getProcessAPI().getProcessDeploymentInfos(20, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(10, processes2.size()); assertEquals("ProcessName20", processes2.get(0).getName()); assertEquals("ProcessName29", processes2.get(9).getName()); getProcessAPI().deleteProcessDefinitions(ids); assertEquals(0, getProcessAPI().getNumberOfProcessDeploymentInfos()); } @Test(expected = InvalidProcessDefinitionException.class) public void createProcessWithNoName() throws Exception { final List emptyList = Collections.emptyList(); final List emptyList2 = Collections.emptyList(); final DesignProcessDefinition processDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(null, null, emptyList, emptyList2); deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processDefinition).done()); } @Test public void getArchivedActivityInstances() throws Exception { final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME + "1", PROCESS_VERSION, Arrays.asList("step1", "step2"), Arrays.asList(false, false)); final ProcessDefinition processDefinition1 = deployAndEnableProcess(designProcessDefinition1); final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME + "2", PROCESS_VERSION, Arrays.asList("task1", "task2", "task3"), Arrays.asList(false, false, false)); final ProcessDefinition processDefinition2 = deployAndEnableProcess(designProcessDefinition2); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition1.getId()); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId()); // one archive for each change in the activity state. For automatic tasks we have initializingAndExecuting, completed await().atMost(20, SECONDS).until(() -> getProcessAPI().getArchivedActivityInstances(processInstance1.getId(), 0, 100, ActivityInstanceCriterion.DEFAULT), hasSize(2 * 2)); await().atMost(20, SECONDS).until(() -> getProcessAPI().getArchivedActivityInstances(processInstance2.getId(), 0, 100, ActivityInstanceCriterion.DEFAULT), hasSize(3 * 2)); waitForProcessToFinish(processInstance1); waitForProcessToFinish(processInstance2); disableAndDeleteProcess(processDefinition1, processDefinition2); } @Test public void getArchivedActivityInstancesOrderByName() throws Exception { getArchivedActivityInstancesOrderByPagingCriterion(ActivityInstanceCriterion.NAME_ASC, 0, 1, 2, ActivityInstanceCriterion.NAME_DESC, 2, 1, 0); } private void getArchivedActivityInstancesOrderByPagingCriterion(final ActivityInstanceCriterion criterionAsc, final int asc1, final int asc2, final int asc3, final ActivityInstanceCriterion criterionDsc, final int desc1, final int desc2, final int desc3) throws Exception { final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME + criterionAsc, PROCESS_VERSION, Arrays.asList("task1", "task2", "task3"), Arrays.asList(false, false, false)); final ProcessDefinition processDefinition2 = deployAndEnableProcess(designProcessDefinition2); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId()); final int nbActivities = 3; // task1, task2, task3 final int nbOfStates = 2; // executing, completed checkNbOfArchivedActivityInstances(processInstance2, 3 * nbOfStates); List archivedActivityInstances = getProcessAPI() .getArchivedActivityInstances(processInstance2.getId(), 0, 100, criterionAsc); for (int i = 0; i < nbOfStates; i++) { if (criterionAsc.equals(ActivityInstanceCriterion.REACHED_STATE_DATE_ASC) || criterionAsc.equals(ActivityInstanceCriterion.LAST_UPDATE_ASC)) { assertEquals("task1", archivedActivityInstances.get(asc1 + i * nbActivities).getName()); assertEquals("task2", archivedActivityInstances.get(asc2 + i * nbActivities).getName()); assertEquals("task3", archivedActivityInstances.get(asc3 + i * nbActivities).getName()); } if (criterionAsc.equals(ActivityInstanceCriterion.NAME_ASC)) { assertEquals("task1", archivedActivityInstances.get(asc1 * nbOfStates + i).getName()); assertEquals("task2", archivedActivityInstances.get(asc2 * nbOfStates + i).getName()); assertEquals("task3", archivedActivityInstances.get(asc3 * nbOfStates + i).getName()); } } archivedActivityInstances = getProcessAPI().getArchivedActivityInstances(processInstance2.getId(), 0, 100, criterionDsc); for (int i = 0; i < nbOfStates; i++) { if (criterionDsc.equals(ActivityInstanceCriterion.REACHED_STATE_DATE_DESC) || criterionDsc.equals(ActivityInstanceCriterion.LAST_UPDATE_DESC)) { assertEquals("task1", archivedActivityInstances.get(desc1 + i * nbActivities).getName()); assertEquals("task2", archivedActivityInstances.get(desc2 + i * nbActivities).getName()); assertEquals("task3", archivedActivityInstances.get(desc3 + i * nbActivities).getName()); } if (criterionDsc.equals(ActivityInstanceCriterion.NAME_DESC)) { assertEquals("task1", archivedActivityInstances.get(desc1 * nbOfStates + i).getName()); assertEquals("task2", archivedActivityInstances.get(desc2 * nbOfStates + i).getName()); assertEquals("task3", archivedActivityInstances.get(desc3 * nbOfStates + i).getName()); } } waitForProcessToFinish(processInstance2); disableAndDeleteProcess(processDefinition2); } @Test public void getArchivedActivityInstancesOfAnUnknownProcess() { final List archivedActivityInstances = getProcessAPI().getArchivedActivityInstances( 456213846564L, 0, 100, ActivityInstanceCriterion.REACHED_STATE_DATE_ASC); assertEquals(0, archivedActivityInstances.size()); } /** * checks that * {@link org.bonitasoft.engine.api.ProcessRuntimeAPI#getOpenActivityInstances(long, int, int, ActivityInstanceCriterion)} * returns the good list * of open activities * only. * An open activity is an activity with state NON-FINAL and STABLE. */ @Test public void checkActivityInstancesWithOpenState() throws Exception { final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition(); final BusinessArchive businessArchive1 = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition1).done(); final ProcessDefinition processDefinition1 = deployProcess(businessArchive1); addUserToFirstActorOfProcess(1, processDefinition1); getProcessAPI().enableProcess(processDefinition1.getId()); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition1.getId()); // 1 instance of process def 2: final ProcessDefinitionBuilder processBuilder2 = new ProcessDefinitionBuilder().createNewInstance( "checkActivityInstancesWithOpenState", PROCESS_VERSION); processBuilder2.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition2 = processBuilder2.addAutomaticTask("step1") .addUserTask("step2", ACTOR_NAME) .addUserTask("step3", ACTOR_NAME).addTransition("step1", "step2").addTransition("step1", "step3") .getProcess(); final BusinessArchive businessArchive2 = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition2).done(); final ProcessDefinition processDefinition2 = deployProcess(businessArchive2); addUserToFirstActorOfProcess(1, processDefinition2); getProcessAPI().enableProcess(processDefinition2.getId()); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId()); // Check the size returned, and the state of each one: waitForUserTask(processInstance1, "step2"); waitForUserTask(processInstance1, "step3"); waitForUserTask(processInstance1, "step4"); List openedActivityInstances = getProcessAPI().getOpenActivityInstances( processInstance1.getId(), 0, 3000, ActivityInstanceCriterion.DEFAULT); for (final ActivityInstance activityInstance : openedActivityInstances) { // Check that all TestStates are Open (Ready): assertEquals(activityInstance.getState(), TestStates.READY.getStateName()); } waitForUserTask(processInstance2, "step2"); waitForUserTask(processInstance2, "step3"); openedActivityInstances = getProcessAPI().getOpenActivityInstances(processInstance1.getId(), 0, 200, ActivityInstanceCriterion.DEFAULT); for (final ActivityInstance activityInstance : openedActivityInstances) { // Check that all TestStates are Open (Ready): assertEquals(activityInstance.getState(), TestStates.READY.getStateName()); } disableAndDeleteProcess(processDefinition1, processDefinition2); } @Test public void openActivityInstancesOrder() throws Exception { checkOpenActivityInstanceOrder(ActivityInstanceCriterion.NAME_ASC, ActivityInstanceCriterion.NAME_DESC); // checkOpenActivityInstanceOrder(ActivityInstanceCriterion.PRIORITY_ASC, ActivityInstanceCriterion.PRIORITY_DESC); } @Test public void openActivityInstancesOrderByLastUpdate() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition).done(); final ProcessDefinition processDefinition = deployProcess(businessArchive); addUserToFirstActorOfProcess(1, processDefinition); getProcessAPI().enableProcess(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // We check the size first, to be sure to wait long enough before retrieving the list: waitForUserTask(processInstance, "step2"); waitForUserTask(processInstance, "step3"); waitForUserTask(processInstance, "step4"); List openedActivityInstances; openedActivityInstances = getProcessAPI().getOpenActivityInstances(processInstance.getId(), 0, 200, ActivityInstanceCriterion.LAST_UPDATE_ASC); assertTrue(openedActivityInstances.get(0).getLastUpdateDate() .compareTo(openedActivityInstances.get(1).getLastUpdateDate()) <= 0); assertTrue(openedActivityInstances.get(1).getLastUpdateDate() .compareTo(openedActivityInstances.get(2).getLastUpdateDate()) <= 0); openedActivityInstances = getProcessAPI().getOpenActivityInstances(processInstance.getId(), 0, 200, ActivityInstanceCriterion.LAST_UPDATE_DESC); assertTrue(openedActivityInstances.get(0).getLastUpdateDate() .compareTo(openedActivityInstances.get(1).getLastUpdateDate()) >= 0); assertTrue(openedActivityInstances.get(1).getLastUpdateDate() .compareTo(openedActivityInstances.get(2).getLastUpdateDate()) >= 0); disableAndDeleteProcess(processDefinition); } private void checkOpenActivityInstanceOrder(final ActivityInstanceCriterion ascendingCriterion, final ActivityInstanceCriterion descendingCriterion) throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition).done(); final ProcessDefinition processDefinition = deployProcess(businessArchive); addUserToFirstActorOfProcess(1, processDefinition); getProcessAPI().enableProcess(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final String step2Name = "step2"; waitForUserTask(processInstance, step2Name); final String step3Name = "step3"; waitForUserTask(processInstance, step3Name); final String step4Name = "step4"; waitForUserTask(processInstance, step4Name); List openedActivityInstances; openedActivityInstances = getProcessAPI().getOpenActivityInstances(processInstance.getId(), 0, 200, ascendingCriterion); assertEquals(step2Name, openedActivityInstances.get(0).getName()); assertEquals(step3Name, openedActivityInstances.get(1).getName()); assertEquals(step4Name, openedActivityInstances.get(2).getName()); openedActivityInstances = getProcessAPI().getOpenActivityInstances(processInstance.getId(), 0, 200, descendingCriterion); assertEquals(step2Name, openedActivityInstances.get(2).getName()); assertEquals(step3Name, openedActivityInstances.get(1).getName()); assertEquals(step4Name, openedActivityInstances.get(0).getName()); disableAndDeleteProcess(processDefinition); } @Test public void getNumberOfOpenedActivityInstances() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition).done(); final ProcessDefinition processDefinition = deployProcess(businessArchive); addUserToFirstActorOfProcess(1, processDefinition); getProcessAPI().enableProcess(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step2"); waitForUserTask(processInstance, "step3"); waitForUserTask(processInstance, "step4"); assertEquals(3, getProcessAPI().getNumberOfOpenedActivityInstances(processInstance.getId())); disableAndDeleteProcess(processDefinition); } @Test public void getProcessDefinitionIdFromProcessInstanceId() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); final long processDefinitionId = getProcessAPI().getProcessDefinitionIdFromProcessInstanceId(pi0.getId()); assertEquals(processDefinition.getId(), processDefinitionId); // Clean up waitForUserTask(pi0, "step1"); disableAndDeleteProcess(processDefinition); assertEquals(0, getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.DEFAULT).size()); } @Test public void getProcessDefinitionIdFromActivityInstanceId() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); final List activityInstances = getProcessAPI().getActivities(pi0.getId(), 0, 10); for (final ActivityInstance activityInstance : activityInstances) { final long processDefinitionId = getProcessAPI() .getProcessDefinitionIdFromActivityInstanceId(activityInstance.getId()); assertEquals(processDefinition.getId(), processDefinitionId); } disableAndDeleteProcess(processDefinition); } @Test public void getProcessResources() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition( processDefinitionBuilder.done()); // Add a resource to the biz archive: businessArchiveBuilder.addExternalResource(new BarResource("dummy.txt", "DUMMY".getBytes())); businessArchiveBuilder .addExternalResource(new BarResource("folder/document.txt", "SOME DOCUMENT TEXT".getBytes())); businessArchiveBuilder.addExternalResource(new BarResource("folder/image.jpg", "UNUSED".getBytes())); businessArchiveBuilder.addExternalResource(new BarResource("someFolder/bin.sh", "some script".getBytes())); businessArchiveBuilder.addExternalResource(new BarResource("ajar.jar", "a jar in resources".getBytes())); businessArchiveBuilder .addClasspathResource(new BarResource("xsds/general-xsd-jar.jar", "a jar in a subfolder".getBytes())); businessArchiveBuilder .addClasspathResource(new BarResource("acme-lib-001.jar", "a jar at the root".getBytes())); // deploy the process to unzip the .bar in BONITA_HOME: final ProcessDefinition processDefinition = deployProcess(businessArchiveBuilder.done()); //check resources assertThat(getProcessAPI().getProcessResources(processDefinition.getId(), ".*/.*\\.txt")) .hasSize(2) .containsEntry("resources/dummy.txt", "DUMMY".getBytes()) .containsEntry("resources/folder/document.txt", "SOME DOCUMENT TEXT".getBytes()); //check jars assertThat(getProcessAPI().getProcessResources(processDefinition.getId(), "^classpath/.*\\.jar$")) .hasSize(2) .containsEntry("classpath/xsds/general-xsd-jar.jar", "a jar in a subfolder".getBytes()) .containsEntry("classpath/acme-lib-001.jar", "a jar at the root".getBytes()); //check both assertThat(getProcessAPI().getProcessResources(processDefinition.getId(), "^.*/.*\\.jar$")) .hasSize(3) .containsEntry("classpath/xsds/general-xsd-jar.jar", "a jar in a subfolder".getBytes()) .containsEntry("classpath/acme-lib-001.jar", "a jar at the root".getBytes()) .containsEntry("resources/ajar.jar", "a jar in resources".getBytes()); deleteProcess(processDefinition); } @Test public void getLatestProcessDefinitionId() throws Exception { // create two process definitions final String processName = "getLatestProcessDefinitionId"; final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(processName, "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition1) .done(), ACTOR_NAME, user); final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(processName, "2.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition2) .done(), ACTOR_NAME, user); // test and do assert final long latestProcessDefinitionId = getProcessAPI().getLatestProcessDefinitionId(processName); assertEquals(processDefinition2.getId(), latestProcessDefinitionId); disableAndDeleteProcess(processDefinition1, processDefinition2); } @Test(expected = ProcessDefinitionNotFoundException.class) public void getLatestProcessDefinitionIdWithProcessInstanceNotFoundException() throws Exception { final String PROCESS_NAME = String.valueOf(System.currentTimeMillis()); getProcessAPI().getLatestProcessDefinitionId(PROCESS_NAME); } @Test public void getSupportedStates() { Set TestStatesName = getProcessAPI().getSupportedStates(FlowNodeType.USER_TASK); assertEquals(5, TestStatesName.size()); TestStatesName = getProcessAPI().getSupportedStates(FlowNodeType.AUTOMATIC_TASK); assertEquals(3, TestStatesName.size()); TestStatesName = getProcessAPI().getSupportedStates(FlowNodeType.START_EVENT); assertEquals(2, TestStatesName.size()); TestStatesName = getProcessAPI().getSupportedStates(FlowNodeType.END_EVENT); assertEquals(2, TestStatesName.size()); TestStatesName = getProcessAPI().getSupportedStates(FlowNodeType.GATEWAY); assertEquals(2, TestStatesName.size()); TestStatesName = getProcessAPI().getSupportedStates(FlowNodeType.INTERMEDIATE_CATCH_EVENT); assertEquals(4, TestStatesName.size()); TestStatesName = getProcessAPI().getSupportedStates(FlowNodeType.CALL_ACTIVITY); assertEquals(5, TestStatesName.size()); } @Test public void getActivityInstanceState() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); final List activityInstances = getProcessAPI().getActivities(pi0.getId(), 0, 10); for (final ActivityInstance activityInstance : activityInstances) { final String stateName = getProcessAPI().getActivityInstanceState(activityInstance.getId()); assertTrue("initializing".equals(stateName) || "ready".equals(stateName)); } disableAndDeleteProcess(processDefinition); } @Test public void getActivityInstancePaginated() throws Exception { final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); definitionBuilder.addStartEvent("start"); definitionBuilder.addActor(ACTOR_NAME); definitionBuilder.addUserTask("initTask1", ACTOR_NAME); definitionBuilder.addUserTask("initTask2", ACTOR_NAME); definitionBuilder.addUserTask("initTask3", ACTOR_NAME); definitionBuilder.addEndEvent("end"); definitionBuilder.addTransition("start", "initTask1"); definitionBuilder.addTransition("start", "initTask2"); definitionBuilder.addTransition("start", "initTask3"); final DesignProcessDefinition designProcessDefinition = definitionBuilder.done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "initTask1"); waitForUserTask(processInstance, "initTask2"); waitForUserTask(processInstance, "initTask3"); List activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 2); assertEquals(2, activityInstances.size()); activityInstances = getProcessAPI().getActivities(processInstance.getId(), 2, 2); assertEquals(1, activityInstances.size()); disableAndDeleteProcess(processDefinition); } @Test public void getProcessDefinitionIdByNameAndVersion() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition) .done()); // do test and assert final long processDefId = getProcessAPI().getProcessDefinitionId(APITestUtil.PROCESS_NAME, APITestUtil.PROCESS_VERSION); assertEquals(processDefId, processDefinition.getId()); deleteProcess(processDefinition); } @Test(expected = ProcessDefinitionNotFoundException.class) public void getProcessDefinitionIdByNameAndVersionWithExcepton() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition) .done()); // do test and assert try { getProcessAPI().getProcessDefinitionId(APITestUtil.PROCESS_NAME + "_wrongName", APITestUtil.PROCESS_VERSION); } finally { deleteProcess(processDefinition); } } @Test public void severalActivityUpdates() throws Exception { final UserTaskDefinitionBuilder userTask = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME) .addDescription("Delivery all day and night long").addUserTask("step1", ACTOR_NAME); final String dataName1 = "dataName"; final String dataName2 = "myHero"; userTask.addShortTextData(dataName1, new ExpressionBuilder().createConstantStringExpression("beforeUpdate")); userTask.addShortTextData(dataName2, new ExpressionBuilder().createConstantStringExpression("Actarus")); final DesignProcessDefinition processDef = userTask.getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); final StartProcessUntilStep startProcessAndWaitForTask = startProcessAndWaitForTask( processDeploymentInfo.getProcessId(), "step1"); final long activityInstanceId = startProcessAndWaitForTask.getActivityInstance().getId(); DataInstance dataInstance = getProcessAPI().getActivityDataInstance(dataName1, activityInstanceId); final String newConstantValue1 = "afterUpdate"; final Operation stringOperation = BuildTestUtil.buildStringOperation(dataInstance.getName(), newConstantValue1, false); final String newConstantValue2 = "GOLDORAK"; dataInstance = getProcessAPI().getActivityDataInstance(dataName2, activityInstanceId); final Operation stringOperation2 = BuildTestUtil.buildStringOperation(dataInstance.getName(), newConstantValue2, false); final List operations = new ArrayList<>(); operations.add(stringOperation); operations.add(stringOperation2); getProcessAPI().updateActivityInstanceVariables(operations, activityInstanceId, null); DataInstance dataI = getProcessAPI().getActivityDataInstance(dataName1, activityInstanceId); assertEquals(newConstantValue1, dataI.getValue()); dataI = getProcessAPI().getActivityDataInstance(dataName2, activityInstanceId); assertEquals(newConstantValue2, dataI.getValue()); disableAndDeleteProcess(processDefinition); } @Test public void updateActivityInstanceVariables() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); final UserTaskDefinitionBuilder addUserTask = processDefinitionBuilder .addActor(ACTOR_NAME) .addDescription("Delivery all day and night long") .addUserTask("step1", ACTOR_NAME); processDefinitionBuilder.addShortTextData("a", new ExpressionBuilder().createConstantStringExpression("aprocess")); processDefinitionBuilder.addShortTextData("b", new ExpressionBuilder().createConstantStringExpression("bprocess")); processDefinitionBuilder.addShortTextData("c", new ExpressionBuilder().createConstantStringExpression("cprocess")); processDefinitionBuilder.addShortTextData("d", new ExpressionBuilder().createConstantStringExpression("dprocess")); processDefinitionBuilder.addShortTextData("e", new ExpressionBuilder().createConstantStringExpression("eprocess")); addUserTask.addShortTextData("a", new ExpressionBuilder().createConstantStringExpression("aacti")); addUserTask.addShortTextData("b", new ExpressionBuilder().createConstantStringExpression("bacti")) .isTransient(); addUserTask.addShortTextData("f", new ExpressionBuilder().createConstantStringExpression("facti")); addUserTask.addShortTextData("g", new ExpressionBuilder().createConstantStringExpression("gacti")) .isTransient(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor( processDefinitionBuilder.getProcess(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); List dataInstances = getProcessAPI().getActivityDataInstances(step1Id, 0, 10); assertThat(dataInstances).hasSize(6); final ArrayList names = new ArrayList<>(6); ArrayList values = new ArrayList<>(6); for (final DataInstance dataInstance2 : dataInstances) { names.add(dataInstance2.getName()); values.add((String) dataInstance2.getValue()); } assertThat(names).contains("a", "b", "c", "d", "e", "f"); assertThat(values).contains("aacti", "bprocess", "cprocess", "dprocess", "eprocess", "facti"); final List operations = new ArrayList<>(); for (final DataInstance dataInstance2 : dataInstances) { final Operation stringOperation = BuildTestUtil.buildStringOperation(dataInstance2.getName(), dataInstance2.getValue() + "+up", false); operations.add(stringOperation); } getProcessAPI().updateActivityInstanceVariables(operations, step1Id, null); dataInstances = getProcessAPI().getActivityDataInstances(step1Id, 0, 10); assertThat(dataInstances).hasSize(6); values = new ArrayList<>(6); for (final DataInstance dataInstance2 : dataInstances) { values.add((String) dataInstance2.getValue()); } assertThat(values).contains("aacti+up", "bprocess+up", "cprocess+up", "dprocess+up", "eprocess+up", "facti+up"); disableAndDeleteProcess(processDefinition); } @Test public void updateActivityInstanceVariable() throws Exception { final DesignProcessDefinition processDef = createProcessWithActorAndHumanTaskAndStringData(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); waitForUserTask(processInstance, "step1"); final List activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 10); final long activityInstanceId = activityInstances.get(0).getId(); final String updatedValue = "afterUpdate"; final Map variables = new HashMap<>(2); variables.put("dataName", updatedValue); getProcessAPI().updateActivityInstanceVariables(activityInstanceId, variables); final DataInstance dataInstance = getProcessAPI().getActivityDataInstance("dataName", activityInstanceId); assertEquals(updatedValue, dataInstance.getValue()); disableAndDeleteProcess(processDefinition); } @Test(expected = UpdateException.class) public void cannotUpdateAnActivityInstanceVariable() throws Exception { final DesignProcessDefinition processDef = createProcessWithActorAndHumanTaskAndStringData(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); waitForUserTask(processInstance, "step1"); final List activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 10); final long activityInstanceId = activityInstances.get(0).getId(); final String updatedValue = "afterUpdate"; final Map variables = new HashMap<>(2); variables.put("dataName1", updatedValue); try { getProcessAPI().updateActivityInstanceVariables(activityInstanceId, variables); } finally { disableAndDeleteProcess(processDefinition); } } @Test public void canExecuteTask() throws Exception { final long userId = user.getId(); final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long activityInstanceId = waitForUserTask(processInstance, "step1"); assertFalse("The user " + USERNAME + " shouldn't be able to execute the task step1.", getProcessAPI().canExecuteTask(activityInstanceId, userId)); getProcessAPI().assignUserTask(activityInstanceId, userId); Thread.sleep(100); assertTrue("The user " + USERNAME + " should be able to execute the task step1.", getProcessAPI().canExecuteTask(activityInstanceId, userId)); disableAndDeleteProcess(processDefinition); } @Test public void getOneAssignedUserTaskInstanceOfProcessInstance() throws Exception { final DesignProcessDefinition processDef = createProcessWithActorAndHumanTaskAndStringData(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); final ActivityInstance activityInstance = createProcessAndAssignUserTask(user, processDefinition); final long userTaskId = getProcessAPI() .getOneAssignedUserTaskInstanceOfProcessInstance(activityInstance.getParentContainerId(), user.getId()); assertEquals(activityInstance.getId(), userTaskId); disableAndDeleteProcess(processDefinition); } @Test public void getOneAssignedUserTaskInstanceOfProcessDefinition() throws Exception { final DesignProcessDefinition processDef = createProcessWithActorAndHumanTaskAndStringData(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); final ActivityInstance activityInstance = createProcessAndAssignUserTask(user, processDefinition); final long userTaskId = getProcessAPI() .getOneAssignedUserTaskInstanceOfProcessDefinition(processDefinition.getId(), user.getId()); assertEquals(activityInstance.getId(), userTaskId); disableAndDeleteProcess(processDefinition); } private ActivityInstance createProcessAndAssignUserTask(final User user, final ProcessDefinition processDefinition) throws Exception { final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); waitForUserTask(processInstance, "step1"); final List activityInstances = new ArrayList<>( getProcessAPI().getActivities(processInstance.getId(), 0, 20)); final ActivityInstance activityInstance = activityInstances.get(activityInstances.size() - 1); assertEquals("ready", activityInstance.getState()); getProcessAPI().assignUserTask(activityInstance.getId(), user.getId()); return activityInstance; } @Test public void updateProcessDeploymentInfo() throws Exception { // create process definition; final String processDescription = "myProcessDisplayName"; final String processDisplayName = "myProcessDescription"; final String processDisplayDescription = "myProcessDisplayDescription"; final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION) .addDisplayName(processDisplayName).addDisplayDescription(processDisplayDescription) .addDescription(processDescription) .addUserTask("task1", "actor").getProcess(); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition) .done()); final long processId = processDefinition.getId(); // before update, display name should be the same as process name final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processId); assertEquals(PROCESS_NAME, processDeploymentInfo.getName()); assertEquals(processDisplayName, processDeploymentInfo.getDisplayName()); assertEquals(processDisplayDescription, processDeploymentInfo.getDisplayDescription()); assertEquals(processDescription, processDeploymentInfo.getDescription()); assertEquals(null, processDeploymentInfo.getIconPath()); // update and do assert final ProcessDeploymentInfoUpdater processDeploymentInfoUpdateDescriptor = new ProcessDeploymentInfoUpdater(); final String updatedDisplayName = "updatedDisplayName"; final String updatedDisplayDescription = "updatedDisplayDescription"; final String iconPath = "iconPathOne"; processDeploymentInfoUpdateDescriptor.setDisplayName(updatedDisplayName); processDeploymentInfoUpdateDescriptor.setDisplayDescription(updatedDisplayDescription); processDeploymentInfoUpdateDescriptor.setIconPath(iconPath); getProcessAPI().updateProcessDeploymentInfo(processId, processDeploymentInfoUpdateDescriptor); final ProcessDeploymentInfo updatedProcessDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processId); assertEquals(updatedDisplayName, updatedProcessDeploymentInfo.getDisplayName()); assertEquals(updatedDisplayDescription, updatedProcessDeploymentInfo.getDisplayDescription()); assertEquals(iconPath, updatedProcessDeploymentInfo.getIconPath()); deleteProcess(processDefinition); } @Test public void updateProcessDisplayDescriptionToNull() throws Exception { // create process definition; final String processDescription = "myProcessDisplayName"; final String processDisplayName = "myProcessDescription"; final String processDisplayDescription = "myProcessDisplayDescription"; final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION) .addDisplayName(processDisplayName).addDisplayDescription(processDisplayDescription) .addDescription(processDescription) .addUserTask("task1", "actor").getProcess(); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition) .done()); final long processId = processDefinition.getId(); // before update, display name should be the same as process name final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processId); assertEquals(PROCESS_NAME, processDeploymentInfo.getName()); assertEquals(processDisplayName, processDeploymentInfo.getDisplayName()); assertEquals(processDisplayDescription, processDeploymentInfo.getDisplayDescription()); assertEquals(processDescription, processDeploymentInfo.getDescription()); // update and do assert final ProcessDeploymentInfoUpdater processDeploymentInfoUpdateDescriptor = new ProcessDeploymentInfoUpdater(); final String updatedDisplayDescription = null; processDeploymentInfoUpdateDescriptor.setDisplayDescription(updatedDisplayDescription); getProcessAPI().updateProcessDeploymentInfo(processId, processDeploymentInfoUpdateDescriptor); final ProcessDeploymentInfo updatedProcessDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(processId); assertEquals(updatedDisplayDescription, updatedProcessDeploymentInfo.getDisplayDescription()); deleteProcess(processDefinition); } @Test(expected = ProcessDefinitionNotFoundException.class) public void updateProcessDeploymentInfoWithProcessDefinitionNotFoundException() throws Exception { // create process definition; final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition) .done()); final long processId = processDefinition.getId(); // update with wrong processId final ProcessDeploymentInfoUpdater processDeploymentInfoUpdateDescriptor = new ProcessDeploymentInfoUpdater(); final String updatedDisplayName = "updatedDisplayName"; processDeploymentInfoUpdateDescriptor.setDisplayName(updatedDisplayName); try { getProcessAPI().updateProcessDeploymentInfo(processId + 1, processDeploymentInfoUpdateDescriptor); } finally { deleteProcess(processDefinition); } } @Test(expected = UpdateException.class) public void updateProcessDeploymentInfoWithException() throws Exception { // create process definition; final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition) .done()); final long processId = processDefinition.getId(); // update and do assert final ProcessDeploymentInfoUpdater processDeploymentInfoUpdateDescriptor = new ProcessDeploymentInfoUpdater(); try { getProcessAPI().updateProcessDeploymentInfo(processId, processDeploymentInfoUpdateDescriptor); } finally { deleteProcess(processDefinition); } } @Test public void checkDataNameInProcess() throws Exception { // create process definition with integer data; final String dataName1 = "$nAéç_mèE"; final String dataName2 = "refhbh bgrtg"; final String dataName3 = "refhbh-bgrtg"; final String dataName4 = "refhbh+bgrtg"; final String dataName5 = "refhbh?bgrtg"; try { new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION) .addIntegerData(dataName1, new ExpressionBuilder().createConstantIntegerExpression(1)) .addIntegerData(dataName2, new ExpressionBuilder().createConstantIntegerExpression(2)) .addIntegerData(dataName3, new ExpressionBuilder().createConstantIntegerExpression(3)) .addIntegerData(dataName4, new ExpressionBuilder().createConstantIntegerExpression(4)) .addIntegerData(dataName5, new ExpressionBuilder().createConstantIntegerExpression(5)).getProcess(); fail("This test should not reach this statement"); } catch (final InvalidProcessDefinitionException ipde) { // System.out.println(ipde.getMessage()); assertTrue(!ipde.getMessage().contains(dataName1)); assertTrue(ipde.getMessage().contains(dataName2)); assertTrue(ipde.getMessage().contains(dataName3)); assertTrue(ipde.getMessage().contains(dataName4)); assertTrue(ipde.getMessage().contains(dataName5)); } } @Test public void checkProcessInstanceDataValue() throws Exception { // create process definition with integer data; final String dataName = "var1"; final DesignProcessDefinition processDef = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME) .addIntegerData(dataName, new ExpressionBuilder().createConstantIntegerExpression(1)) .addUserTask("step1", ACTOR_NAME) .addAutomaticTask("step2").addTransition("step1", "step2").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); // create Operation keyed map final Operation integerOperation = BuildTestUtil.buildIntegerOperation(dataName, 2); final Map context = new HashMap<>(); context.put("page", "1"); final long processDefinitionId = processDefinition.getId(); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitionId, Arrays.asList(integerOperation), context); final DataInstance dataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstance.getId()); assertEquals(2, dataInstance.getValue()); disableAndDeleteProcess(processDefinition); } @Test public void getActivityReachedStateDate() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step one", "step two"), Arrays.asList(true, true)); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition1.getId()); HumanTaskInstance humanTaskInstance = waitForUserTaskAndGetIt(startProcess, "step one"); final Date reachedDate = humanTaskInstance.getReachedStateDate(); assertNotNull(reachedDate); humanTaskInstance = getProcessAPI().getHumanTaskInstance(humanTaskInstance.getId()); final long readyDate = humanTaskInstance.getReachedStateDate().getTime(); final long processStartDate = startProcess.getStartDate().getTime(); assertTrue( "The process start at " + processStartDate + ", and the user task " + humanTaskInstance.getName() + " reached state at " + readyDate, processStartDate <= readyDate); getProcessAPI().assignUserTask(humanTaskInstance.getId(), user.getId()); getProcessAPI().executeFlowNode(humanTaskInstance.getId()); long userTask = waitForUserTask("step two"); assignAndExecuteStep(userTask, user); waitForProcessToFinish(startProcess); // look in archive assertEquals(reachedDate, getProcessAPI().getActivityReachedStateDate(humanTaskInstance.getId(), TestStates.READY.getStateName())); disableAndDeleteProcess(processDefinition1); } @Test public void checkOrderPriorityEnum() { assertEquals(0, TaskPriority.LOWEST.ordinal()); assertEquals(1, TaskPriority.UNDER_NORMAL.ordinal()); assertEquals(2, TaskPriority.NORMAL.ordinal()); assertEquals(3, TaskPriority.ABOVE_NORMAL.ordinal()); assertEquals(4, TaskPriority.HIGHEST.ordinal()); } @Test public void activityWithDisplayNameAndDescription() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final String displayName = "display name"; final String displayDescriptionAfterCompletion = "display description after completion"; final String displayDescription = "display description"; final Expression dispDescAfterCompletionExpression = new ExpressionBuilder().createGroovyScriptExpression( "dynGroovyScriptWithLongDep", "return '" + displayDescriptionAfterCompletion + "' + rootProcessInstanceId", String.class.getName(), new ExpressionBuilder().createEngineConstant(ExpressionConstants.ROOT_PROCESS_INSTANCE_ID)); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("step1", ACTOR_NAME) .addDisplayName(new ExpressionBuilder().createConstantStringExpression(displayName)) .addDisplayDescriptionAfterCompletion(dispDescAfterCompletionExpression) .addDisplayDescription(new ExpressionBuilder().createConstantStringExpression(displayDescription)) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); final HumanTaskInstance userTaskInstance = waitForUserTaskAndGetIt(pi0, "step1"); assertEquals(displayName, userTaskInstance.getDisplayName()); assertEquals(displayDescription, userTaskInstance.getDisplayDescription()); assignAndExecuteStep(userTaskInstance, user.getId()); waitForCompletedArchivedStep("step1", processDefinition.getId(), displayName, displayDescriptionAfterCompletion + pi0.getId()); // Clean up disableAndDeleteProcess(processDefinition); } @Test public void activityWithDisplayNameAndDescriptionAndNoAfterCompletion() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final String displayName = "display name"; final String displayDescription = "display description"; final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("step1", ACTOR_NAME) .addDisplayName(new ExpressionBuilder().createConstantStringExpression(displayName)) .addDisplayDescription(new ExpressionBuilder().createConstantStringExpression(displayDescription)) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(pi0, "step1"); final HumanTaskInstance userTaskInstance = getProcessAPI().getHumanTaskInstance(step1Id); assertEquals(displayName, userTaskInstance.getDisplayName()); assertEquals(displayDescription, userTaskInstance.getDisplayDescription()); assignAndExecuteStep(userTaskInstance, user.getId()); waitForCompletedArchivedStep("step1", processDefinition.getId(), displayName, displayDescription); // Clean up disableAndDeleteProcess(processDefinition); } @Test public void activityWithNoDisplayNameAndDescription() throws Exception { final String stepName = "staticName"; final String stepDescription = "staticDescription"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(stepName, ACTOR_NAME) .addDescription(stepDescription).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); final HumanTaskInstance userTaskInstance = waitForUserTaskAndGetIt(pi0, stepName); assertEquals(stepName, userTaskInstance.getDisplayName()); assertEquals(stepDescription, userTaskInstance.getDisplayDescription()); assertEquals(stepDescription, userTaskInstance.getDescription()); assignAndExecuteStep(userTaskInstance, user.getId()); waitForCompletedArchivedStep(stepName, processDefinition.getId(), stepName, stepDescription); // Clean up disableAndDeleteProcess(processDefinition); } @Test public void getNumberOfOpenTasksForUsers() throws Exception { final String username1 = "jack"; final String username2 = "john"; final String username3 = "lucy"; final User jack = createUser(username1, PASSWORD); final User john = createUser(username2, PASSWORD); final User lucy = createUser(username3, PASSWORD); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addDescription("Coding all scrum-sprint-long") .addUserTask("userTask1", ACTOR_NAME) .addUserTask("userTask2", ACTOR_NAME) .addUserTask("userTask3", ACTOR_NAME); final DesignProcessDefinition processDefinition = processBuilder.done(); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinition, ACTOR_NAME, jack); final ProcessInstance startedProcess = getProcessAPI().startProcess(definition.getId()); final long step1Id = waitForUserTask(startedProcess, "userTask1"); final long step2Id = waitForUserTask(startedProcess, "userTask2"); waitForUserTask(startedProcess, "userTask3"); // add lucy to actor getProcessAPI().addUserToActor(ACTOR_NAME, definition, lucy.getId()); // assign first user task to jack, second one to john, leaving the third pending getProcessAPI().assignUserTask(step1Id, jack.getId()); getProcessAPI().assignUserTask(step2Id, john.getId()); // check final List userIds = new ArrayList<>(); userIds.add(jack.getId()); userIds.add(john.getId()); userIds.add(lucy.getId()); final Map myAssignedTasksNb = getProcessAPI().getNumberOfOpenTasks(userIds); assertNotNull(myAssignedTasksNb); assertEquals(3, myAssignedTasksNb.size()); assertEquals(2L, (long) myAssignedTasksNb.get(jack.getId())); // jack has one assigned task and one pending task assertEquals(1L, (long) myAssignedTasksNb.get(john.getId())); // john has one assigned task assertEquals(1L, (long) myAssignedTasksNb.get(lucy.getId())); // lucy has one pending task disableAndDeleteProcess(definition); deleteUsers(jack, john, lucy); } @Test public void getNumberOfOverdueTasksForUsers() throws Exception { final String username1 = "jack"; final String username2 = "john"; final String username3 = "lucy"; final User jack = createUser(username1, PASSWORD); final User john = createUser(username2, PASSWORD); final User lucy = createUser(username3, PASSWORD); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addDescription("Coding all scrum-sprint-long") .addUserTask("userTask1", ACTOR_NAME).addExpectedDuration(0L) .addUserTask("userTask2", ACTOR_NAME).addExpectedDuration(0L) .addUserTask("userTask3", ACTOR_NAME).addExpectedDuration(0L); final DesignProcessDefinition processDefinition = processBuilder.done(); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinition, ACTOR_NAME, jack); final ProcessInstance startedProcess = getProcessAPI().startProcess(definition.getId()); final long step1Id = waitForUserTask(startedProcess, "userTask1"); final long step2Id = waitForUserTask(startedProcess, "userTask2"); waitForUserTask(startedProcess, "userTask3"); // add lucy to actor getProcessAPI().addUserToActor(ACTOR_NAME, definition, lucy.getId()); // assign first user task to jack, second one to john, leaving the third pending getProcessAPI().assignUserTask(step1Id, jack.getId()); getProcessAPI().assignUserTask(step2Id, john.getId()); // check final List userIds = new ArrayList<>(); userIds.add(jack.getId()); userIds.add(john.getId()); userIds.add(lucy.getId()); final Map myAssignedTasksNb = getProcessAPI().getNumberOfOverdueOpenTasks(userIds); assertNotNull(myAssignedTasksNb); assertEquals(3, myAssignedTasksNb.size()); assertEquals(2L, (long) myAssignedTasksNb.get(jack.getId())); // jack has one assigned overdue task and one pending overdue task assertEquals(1L, (long) myAssignedTasksNb.get(john.getId())); // john has one assigned overdue task assertEquals(1L, (long) myAssignedTasksNb.get(lucy.getId())); // lucy has one pending overdue task disableAndDeleteProcess(definition); deleteUsers(jack, john, lucy); } @Test public void getProcessDefinitionsDeployInfo() throws Exception { // create process1 final DesignProcessDefinition designProcessDefinition1 = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, "1.1") .addDescription("My process 1").addDisplayName("Process 1") .addDisplayDescription("The process definition that is cool").done(); final ProcessDefinition processDefinition1 = deployAndEnableProcess(designProcessDefinition1); assertNotNull(processDefinition1); assertEquals(PROCESS_NAME, processDefinition1.getName()); assertEquals("1.1", processDefinition1.getVersion()); assertEquals("My process 1", processDefinition1.getDescription()); // put all processDefinitionId to a list as parameter final long processDefinitionIdA = processDefinition1.getId(); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinitionIdA); assertNotNull(processDeploymentInfo); assertEquals(PROCESS_NAME, processDeploymentInfo.getName()); assertEquals("1.1", processDeploymentInfo.getVersion()); assertEquals("My process 1", processDeploymentInfo.getDescription()); assertEquals("The process definition that is cool", processDeploymentInfo.getDisplayDescription()); assertEquals("Process 1", processDeploymentInfo.getDisplayName()); disableAndDeleteProcess(processDefinition1); } @Test public void getProcessDefinitionsFromIds() throws Exception { // create process1 final String PROCESS_NAME1 = "processDefinition1"; final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME1, "1.1", Arrays.asList("step1_1", "step1_2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, ACTOR_NAME, user); // create process2 final String PROCESS_NAME2 = "processDefinition2"; final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME2, "1.2", Arrays.asList("step2_1", "step2_2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, ACTOR_NAME, user); // create process3 final String PROCESS_NAME3 = "processDefinition3"; final DesignProcessDefinition designProcessDefinition3 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME3, "1.3", Arrays.asList("step2_1", "step2_2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, ACTOR_NAME, user); // create process4 final String PROCESS_NAME4 = "processDefinition4"; final DesignProcessDefinition designProcessDefinition4 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME4, "1.4", Arrays.asList("step2_1", "step2_2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition4 = deployAndEnableProcessWithActor(designProcessDefinition4, ACTOR_NAME, user); // put all processDefinitionId to a list as parameter final long processDefinitionIdA = processDefinition1.getId(); final long processDefinitionIdB = processDefinition2.getId(); final long processDefinitionIdC = processDefinition3.getId(); final long processDefinitionIdD = processDefinition4.getId(); final List processDefinitionIds = new ArrayList<>(); processDefinitionIds.add(processDefinitionIdA); processDefinitionIds.add(processDefinitionIdB); processDefinitionIds.add(processDefinitionIdC); processDefinitionIds.add(processDefinitionIdD); // do search and assert final Map processDeploymentInfos = getProcessAPI() .getProcessDeploymentInfosFromIds(processDefinitionIds); assertNotNull(processDeploymentInfos); assertEquals(4, processDeploymentInfos.size()); assertEquals(processDefinition1.getId(), processDeploymentInfos.get(processDefinitionIdA).getProcessId()); assertEquals(processDefinition2.getId(), processDeploymentInfos.get(processDefinitionIdB).getProcessId()); assertEquals(processDefinition3.getId(), processDeploymentInfos.get(processDefinitionIdC).getProcessId()); assertEquals(processDefinition4.getId(), processDeploymentInfos.get(processDefinitionIdD).getProcessId()); // delete data for test disableAndDeleteProcess(processDefinition1); disableAndDeleteProcess(processDefinition2); disableAndDeleteProcess(processDefinition3); disableAndDeleteProcess(processDefinition4); } @Test public void getProcessDefinitionsFromProcessInstanceIds() throws Exception { // create process1 final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addDescription("description"); processBuilder.addDisplayDescription("displayDescription"); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition1 = processBuilder.addUserTask("step1", ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, ACTOR_NAME, user); final ProcessInstance pi1 = getProcessAPI().startProcess(processDefinition1.getId()); // create process2 final String processName2 = "processDefinition2"; final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(processName2, "1.2", Arrays.asList("step2_1", "step2_2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, ACTOR_NAME, user); final ProcessInstance pi2 = getProcessAPI().startProcess(processDefinition2.getId()); // create process3 final String processName3 = "processDefinition3"; final DesignProcessDefinition designProcessDefinition3 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(processName3, "1.3", Arrays.asList("step3_1", "step3_2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, ACTOR_NAME, user); final ProcessInstance pi3 = getProcessAPI().startProcess(processDefinition3.getId()); // create process4 final String processName4 = "processDefinition4"; final DesignProcessDefinition designProcessDefinition4 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(processName4, "1.4", Arrays.asList("step4_1", "step4_2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition4 = deployAndEnableProcessWithActor(designProcessDefinition4, ACTOR_NAME, user); final ProcessInstance pi4 = getProcessAPI().startProcess(processDefinition4.getId()); // put all processInstantsId to a list as parameter final long processInstantsIdA = pi1.getId(); final long processInstantsIdB = pi2.getId(); final long processInstantsIdC = pi3.getId(); final long processInstantsIdD = pi4.getId(); final List processInstantsIds = new ArrayList<>(); processInstantsIds.add(processInstantsIdA); processInstantsIds.add(processInstantsIdB); processInstantsIds.add(processInstantsIdC); processInstantsIds.add(processInstantsIdD); // do search and assert final Map processDeploymentInfos = getProcessAPI() .getProcessDeploymentInfosFromProcessInstanceIds(processInstantsIds); assertNotNull(processDeploymentInfos); assertEquals(4, processDeploymentInfos.size()); assertEquals(processDefinition1.getId(), processDeploymentInfos.get(processInstantsIdA).getProcessId()); assertEquals(processDefinition2.getId(), processDeploymentInfos.get(processInstantsIdB).getProcessId()); assertEquals(processDefinition3.getId(), processDeploymentInfos.get(processInstantsIdC).getProcessId()); assertEquals(processDefinition4.getId(), processDeploymentInfos.get(processInstantsIdD).getProcessId()); // delete data for test disableAndDeleteProcess(processDefinition1, processDefinition2, processDefinition3, processDefinition4); } @Test public void retryTask() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("step1", ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); final long activityInstanceId = waitForUserTaskAndAssignIt(pi0, "step1", user).getId(); getProcessAPI().setActivityStateById(activityInstanceId, 3); final HumanTaskInstance userTaskInstance = getProcessAPI().getHumanTaskInstance(activityInstanceId); assertEquals(ActivityStates.FAILED_STATE, userTaskInstance.getState()); getProcessAPI().retryTask(activityInstanceId); waitForProcessToFinish(pi0); disableAndDeleteProcess(processDefinition); } /** * Scenario in the same task: * 1 -> connector on_enter fails * 2 -> fix issue + retryTask * 3 -> operation fails * 4 -> fix issue + retryTask * 5 -> connector on_finish fails * 6 -> fix issue + retryTask * 7 -> transition fails * 8 -> fix issue + retryTask * 9 -> task is finally completed */ @Test public void retryTask_should_retry_failed_connectors_and_operations() throws Exception { //given String watchingOnEnterVar = "watchingOnEnterVar"; String watchingOnFinishVar = "watchingOnFinishVar"; String watchingOperationsVar = "watchingOperationsVar"; String watchingTransitionVar = "watchingTransitionVar"; String countVar = "count"; String firstStepName = "auto"; String secondStepName = "auto1"; final ProcessDefinition processDefinition = deployProcessWithConnectorsAndOperationsThrowingException( watchingOnEnterVar, watchingOnFinishVar, watchingOperationsVar, watchingTransitionVar, countVar, firstStepName, secondStepName); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); //will fail on connector on enter FlowNodeInstance flowNodeInstance = waitForFlowNodeInFailedState(processInstance, firstStepName); //when //set variable to a value different of 1: operation will execute with success getProcessAPI().updateProcessDataInstance(watchingOnEnterVar, processInstance.getId(), "none"); getProcessAPI().retryTask(flowNodeInstance.getId()); //then will fail on operation waitForFlowNodeInFailedState(processInstance, firstStepName); //when getProcessAPI().updateProcessDataInstance(watchingOperationsVar, processInstance.getId(), 1); getProcessAPI().retryTask(flowNodeInstance.getId()); //then will fail on connector on finish waitForFlowNodeInFailedState(processInstance, firstStepName); //when getProcessAPI().updateProcessDataInstance(watchingOnFinishVar, processInstance.getId(), "none"); getProcessAPI().retryTask(flowNodeInstance.getId()); //then will fail due to exception on transition waitForFlowNodeInFailedState(processInstance, firstStepName); //when getProcessAPI().updateProcessDataInstance(watchingTransitionVar, processInstance.getId(), 1); System.out.println("------------------- retrying after expression -------------"); getProcessAPI().retryTask(flowNodeInstance.getId()); //finally completed waitForActivityInCompletedState(processInstance, firstStepName, true); waitForActivityInCompletedState(processInstance, secondStepName, true); waitForProcessToFinish(processInstance); //check connectors List connectorInstances = getArchivedConnectorInstances(flowNodeInstance); assertThat(connectorInstances).extracting("state").containsExactly(ConnectorState.DONE, ConnectorState.DONE); assertThat(connectorInstances).extracting("name").containsExactly("throwsExceptionOnEnter", "throwsExceptionOnFinish"); //check data instance DataInstance count = getProcessAPI().getArchivedActivityDataInstance(countVar, flowNodeInstance.getId()); assertThat(count.getValue()).isEqualTo(1); //clean disableAndDeleteProcess(processDefinition); } private ProcessDefinition deployProcessWithConnectorsAndOperationsThrowingException(final String watchingOnEnterVar, final String watchingOnFinishVar, final String watchingOperationsVar, final String watchingTransitionVar, final String countVar, final String firstStepName, final String secondStepName) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); //global data processBuilder.addShortTextData(watchingOnEnterVar, new ExpressionBuilder().createConstantStringExpression(TestConnectorThatThrowException.NORMAL)); processBuilder.addShortTextData(watchingOnFinishVar, new ExpressionBuilder().createConstantStringExpression("normal")); processBuilder.addIntegerData(watchingOperationsVar, new ExpressionBuilder().createConstantIntegerExpression(0)); processBuilder.addIntegerData(countVar, new ExpressionBuilder().createConstantIntegerExpression(0)); processBuilder.addIntegerData(watchingTransitionVar, new ExpressionBuilder().createConstantIntegerExpression(0)); AutomaticTaskDefinitionBuilder taskBuilder = processBuilder.addAutomaticTask(firstStepName); //local data taskBuilder.addShortTextData("localData", new ExpressionBuilder().createConstantStringExpression("default")); addConnectors(watchingOnEnterVar, watchingOnFinishVar, taskBuilder); addOperation(watchingOperationsVar, countVar, taskBuilder); processBuilder.addAutomaticTask(secondStepName); addTransition(firstStepName, secondStepName, watchingTransitionVar, processBuilder); return deployAndEnableProcessWithConnector(processBuilder, "TestConnectorThatThrowException.impl", TestConnectorThatThrowException.class, "TestConnectorThatThrowException.jar"); } private void addTransition(final String firstStepName, final String secondStepName, final String watchingTransitionVar, final ProcessDefinitionBuilder processBuilder) throws InvalidExpressionException { Expression transitionCondition = new ExpressionBuilder().createGroovyScriptExpression("throwExceptionIfZero", "if (" + watchingTransitionVar + " == 0) {\nthrow new RuntimeException(\" was zero\");\n} \nreturn true;", Boolean.class.getName(), new ExpressionBuilder().createDataExpression(watchingTransitionVar, Integer.class.getName())); processBuilder.addTransition(firstStepName, secondStepName, transitionCondition); } private void addOperation(final String watchingOperationsVar, final String countVar, final AutomaticTaskDefinitionBuilder taskBuilder) throws InvalidExpressionException { List dependencies = Arrays.asList( new ExpressionBuilder().createDataExpression(watchingOperationsVar, Integer.class.getName()), new ExpressionBuilder().createDataExpression(countVar, Integer.class.getName())); Expression leftOperationExpr = new ExpressionBuilder().createGroovyScriptExpression("throwExceptionIfZero", "if (" + watchingOperationsVar + "== 0) {\nthrow new RuntimeException(\" was zero\");\n} \nreturn " + countVar + " + 1;", Integer.class.getName(), dependencies); taskBuilder.addOperation(new OperationBuilder().createSetDataOperation(countVar, leftOperationExpr)); } private void addConnectors(final String watchingOnEnterVar, final String watchingOnFinishVar, final AutomaticTaskDefinitionBuilder taskBuilder) throws InvalidExpressionException { //connector on enter taskBuilder .addConnector("throwsExceptionOnEnter", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER) .addInput("kind", new ExpressionBuilder().createDataExpression(watchingOnEnterVar, String.class.getName())); //connector on finish taskBuilder .addConnector("throwsExceptionOnFinish", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_FINISH) .addInput("kind", new ExpressionBuilder().createDataExpression(watchingOnFinishVar, String.class.getName())); } @Test public void can_retryTask_twice() throws Exception { //given Expression dataExpression = new ExpressionBuilder().createDataExpression("watchingVar", String.class.getName()); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addShortTextData("watchingVar", new ExpressionBuilder().createConstantStringExpression("normal")); processBuilder.addAutomaticTask("auto").addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER) .addInput("kind", dataExpression); final ProcessDefinition processDefinition = deployAndEnableProcessWithConnector(processBuilder, "TestConnectorThatThrowException.impl", TestConnectorThatThrowException.class, "TestConnectorThatThrowException.jar"); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); //first fail FlowNodeInstance flowNodeInstance = waitForFlowNodeInFailedState(processInstance, "auto"); //when getProcessAPI().retryTask(flowNodeInstance.getId()); //then fail again waitForFlowNodeInFailedState(processInstance, "auto"); //when //set variable to a value different of 1: operation will execute with success getProcessAPI().updateProcessDataInstance("watchingVar", processInstance.getId(), "none"); getProcessAPI().retryTask(flowNodeInstance.getId()); //then success waitForActivityInCompletedState(processInstance, "auto", true); waitForProcessToFinish(processInstance); List connectorInstances = getArchivedConnectorInstances(flowNodeInstance); assertThat(connectorInstances).hasSize(1); assertThat(connectorInstances.get(0).getState()).isEqualTo(ConnectorState.DONE); //clean disableAndDeleteProcess(processDefinition); } private List getArchivedConnectorInstances(final FlowNodeInstance flowNodeInstance) throws SearchException { SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 100); builder.filter(ArchiveConnectorInstancesSearchDescriptor.CONTAINER_ID, flowNodeInstance.getId()); builder.filter(ArchiveConnectorInstancesSearchDescriptor.CONTAINER_TYPE, "flowNode"); builder.sort(ArchiveConnectorInstancesSearchDescriptor.NAME, Order.ASC); SearchResult searchResult = getProcessAPI() .searchArchivedConnectorInstances(builder.done()); return searchResult.getResult(); } @Test(expected = NotFoundException.class) public void getArchiveCommentNotFound() throws Exception { getProcessAPI().getArchivedComment(123456789L); } @Test public void getProcessDeploymentInfosFromArchivedProcessInstanceIds() throws Exception { final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1_1"), Arrays.asList(false)); final ProcessDefinition processDefinition1 = deployAndEnableProcess(designProcessDefinition1); final ProcessInstance pi1 = getProcessAPI().startProcess(processDefinition1.getId()); checkProcessInstanceIsArchived(pi1); // get archived process instances. It will have only the state completed. final List aProcessInstances = getProcessAPI().getArchivedProcessInstances(0, 10, ProcessInstanceCriterion.ARCHIVE_DATE_DESC); assertEquals(1, aProcessInstances.size()); // final ArchivedProcessInstance archivedProcessInstance1 = aProcessInstances.get(0); // We check that the retrieved process are the good one : assertEquals(pi1.getId(), archivedProcessInstance1.getSourceObjectId()); // put processInstantsId to a list as parameter final List archivedProcessInstantsIds = new ArrayList<>(); archivedProcessInstantsIds.add(archivedProcessInstance1.getId()); // do search and assert final Map processDeploymentInfos = getProcessAPI() .getProcessDeploymentInfosFromArchivedProcessInstanceIds( archivedProcessInstantsIds); assertNotNull(processDeploymentInfos); assertEquals(1, processDeploymentInfos.size()); assertEquals(processDefinition1.getId(), processDeploymentInfos.get(archivedProcessInstance1.getId()).getProcessId()); // delete data for test disableAndDeleteProcess(processDefinition1); } @Test public void cantResolveDataInExpressionInDataDefaultValue() throws Exception { final Expression aExpression = new ExpressionBuilder().createDataExpression("name", String.class.getName()); final Expression aScript = new ExpressionBuilder().createGroovyScriptExpression("script", "return name", String.class.getName(), aExpression); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("cantResolveDataInExpressionInDataDefaultValue", "1"); processBuilder.addActor(ACTOR_NAME).addDescription("Process to test archiving mechanism"); processBuilder.addShortTextData("name", new ExpressionBuilder().createConstantStringExpression("a value")); final AutomaticTaskDefinitionBuilder automaticTaskDefinitionBuilder = processBuilder .addAutomaticTask("activity"); automaticTaskDefinitionBuilder.addShortTextData("taskData", aExpression); automaticTaskDefinitionBuilder.addShortTextData("taskDataFromString", aScript); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(user.getId(), processDefinition.getId()); try { waitForProcessToFinish(processInstance); } catch (final Exception e) { fail("Process should finish"); } disableAndDeleteProcess(processDefinition); } @Test public void errorMessageWhileStartingProcessForClassCastToInteger() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("cantResolveDataInExpressionInDataDefaultValue", "1"); processBuilder.addActor(ACTOR_NAME).addDescription("Process to test archiving mechanism"); processBuilder.addIntegerData("aData", null); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); final Operation stringOperation = BuildTestUtil.buildStringOperation("aData", "15", false); final Map context = new HashMap<>(); context.put("page", "1"); try { getProcessAPI().startProcess(processDefinition.getId(), Arrays.asList(stringOperation), context); } catch (final ExecutionException e) { assertThat(e.getMessage()).contains("aData"); assertThat(e.getMessage()).contains("incompatible type"); } disableAndDeleteProcess(processDefinition); } @Test public void errorMessageWhileStartingProcessForClassCastToLong() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("cantResolveDataInExpressionInDataDefaultValue", "1"); processBuilder.addActor(ACTOR_NAME).addDescription("Process to test archiving mechanism"); processBuilder.addLongData("aData", null); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); final Operation stringOperation = BuildTestUtil.buildStringOperation("aData", "15", false); final Map context = new HashMap<>(); context.put("page", "1"); try { getProcessAPI().startProcess(processDefinition.getId(), Arrays.asList(stringOperation), context); } catch (final ExecutionException e) { assertThat(e.getMessage()).contains("aData"); assertThat(e.getMessage()).contains("incompatible type"); } disableAndDeleteProcess(processDefinition); } @Test public void errorMessageWhileStartingProcessForClassCastToDouble() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("cantResolveDataInExpressionInDataDefaultValue", "1"); processBuilder.addActor(ACTOR_NAME).addDescription("Process to test archiving mechanism"); processBuilder.addDoubleData("aData", null); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); final Operation stringOperation = BuildTestUtil.buildStringOperation("aData", "15", false); final ArrayList operations = new ArrayList<>(1); operations.add(stringOperation); final Map context = new HashMap<>(); context.put("page", "1"); try { getProcessAPI().startProcess(processDefinition.getId(), operations, context); } catch (final ExecutionException e) { assertThat(e.getMessage()).contains("aData"); assertThat(e.getMessage()).contains("incompatible type"); } disableAndDeleteProcess(processDefinition); } private List createProcessDefinitionWithTwoHumanStepsAndDeployBusinessArchive(final int nbProcess) throws Exception { final List ids = new ArrayList<>(); for (int i = 0; i < nbProcess; i++) { String processName = PROCESS_NAME; if (i >= 0 && i < 10) { processName += "0"; } final DesignProcessDefinition processDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(processName + i, PROCESS_VERSION + i, Arrays.asList("step1", "step2"), Arrays.asList(true, true)); ids.add(deployProcess(new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDefinition).done()).getId()); } return ids; } private DesignProcessDefinition createProcessWithActorAndHumanTaskAndStringData() throws Exception { return new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME) .addDescription("Delivery all day and night long").addUserTask("step1", ACTOR_NAME) .addShortTextData("dataName", new ExpressionBuilder().createConstantStringExpression("beforeUpdate")) .getProcess(); } @Test public void startProcessUsingInitialVariableValues() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("cantResolveDataInExpressionInDataDefaultValue", "1"); processBuilder.addActor(ACTOR_NAME).addDescription("Process to test archiving mechanism"); processBuilder.addDoubleData("D", new ExpressionBuilder().createConstantDoubleExpression(3.14)); processBuilder.addData("bigD", BigDecimal.class.getName(), null); processBuilder.addUserTask("step1", ACTOR_NAME); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); final Map variables = new HashMap<>(); variables.put("bigD", new BigDecimal("3.141592653589793")); final ProcessInstance instance = getProcessAPI().startProcess(processDefinition.getId(), variables); DataInstance dataInstance = getProcessAPI().getProcessDataInstance("bigD", instance.getId()); assertEquals(new BigDecimal("3.141592653589793"), dataInstance.getValue()); dataInstance = getProcessAPI().getProcessDataInstance("D", instance.getId()); assertEquals(Double.valueOf(3.14), dataInstance.getValue()); disableAndDeleteProcess(processDefinition); } @Test public void purgeClassLoader_should_clean_the_classloader_of_the_process_definition_when_it_is_disabled_without_a_running_instance() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("cantResolveDataInExpressionInDataDefaultValue", "1"); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask("step1", ACTOR_NAME); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); getProcessAPI().disableProcess(processDefinition.getId()); getProcessAPI().purgeClassLoader(processDefinition.getId()); deleteProcess(processDefinition); } @Test public void getProcessInstancesWithLabelOnStringIndex() throws Exception { ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("1" + PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask("step1", ACTOR_NAME); processBuilder.setStringIndex(1, "Label1", null); processBuilder.setStringIndex(2, "Label2", null); processBuilder.setStringIndex(3, "Label3", null); processBuilder.setStringIndex(4, "Label4", null); processBuilder.setStringIndex(5, "Label5", null); final ProcessDefinition process1 = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); processBuilder = new ProcessDefinitionBuilder().createNewInstance("2" + PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask("step1", ACTOR_NAME); processBuilder.setStringIndex(1, "LabelBis1", null); processBuilder.setStringIndex(2, "LabelBis2", null); processBuilder.setStringIndex(3, "LabelBis3", null); processBuilder.setStringIndex(4, "LabelBis4", null); processBuilder.setStringIndex(5, "LabelBis5", null); final ProcessDefinition process2 = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); startProcessAndWaitForTask(process1.getId(), "step1"); startProcessAndWaitForTask(process2.getId(), "step1"); final List processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_ASC); assertEquals(2, processInstances.size()); ProcessInstance processInstance = processInstances.get(0); assertEquals("1" + PROCESS_NAME, processInstance.getName()); assertEquals("Label1", processInstance.getStringIndexLabel(1)); assertEquals("Label2", processInstance.getStringIndexLabel(2)); assertEquals("Label3", processInstance.getStringIndexLabel(3)); assertEquals("Label4", processInstance.getStringIndexLabel(4)); assertEquals("Label5", processInstance.getStringIndexLabel(5)); processInstance = processInstances.get(1); assertEquals("2" + PROCESS_NAME, processInstance.getName()); assertEquals("LabelBis1", processInstance.getStringIndexLabel(1)); assertEquals("LabelBis2", processInstance.getStringIndexLabel(2)); assertEquals("LabelBis3", processInstance.getStringIndexLabel(3)); assertEquals("LabelBis4", processInstance.getStringIndexLabel(4)); assertEquals("LabelBis5", processInstance.getStringIndexLabel(5)); disableAndDeleteProcess(process1); disableAndDeleteProcess(process2); } @Test public void should_event_sub_process_do_not_have_search_index_of_parent() throws Exception { //given /* * We test here that an event sub process instantiation do nothing on the parent process * see bug BS-15123 and BS-15275 */ //given ProcessDefinitionBuilder parentProcessBuilder = new ProcessDefinitionBuilder() .createNewInstance("ParentProcessWithSignalEventSubProcess", "1.0"); parentProcessBuilder.addActor(ACTOR_NAME); parentProcessBuilder.addUserTask("userTask", ACTOR_NAME); parentProcessBuilder.setStringIndex(1, "index1", new ExpressionBuilder().createConstantStringExpression("index1Value")); parentProcessBuilder.addShortTextData("textData", new ExpressionBuilder().createConstantStringExpression("parentVar")); //construct sub process SubProcessDefinitionBuilder subProcessBuilder = parentProcessBuilder .addSubProcess("interruptWithSignalProcess", true).getSubProcessBuilder(); StartEventDefinitionBuilder startEventDefinitionBuilder = subProcessBuilder.addStartEvent("signalStart"); startEventDefinitionBuilder.addSignalEventTrigger("theSignal"); subProcessBuilder.addUserTask("userTaskInSubProcess", ACTOR_NAME); subProcessBuilder.addEndEvent("endSubProcess"); subProcessBuilder.addTransition("signalStart", "userTaskInSubProcess"); subProcessBuilder.addTransition("userTaskInSubProcess", "endSubProcess"); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(parentProcessBuilder.done(), ACTOR_NAME, user); getProcessAPI().startProcess(processDefinition.getId()); //when waitForUserTask("userTask"); getProcessAPI().sendSignal("theSignal"); //then ActivityInstance eventSubProcessActivity = getProcessAPI() .getActivityInstance(waitForUserTask("userTaskInSubProcess")); Assertions .assertThat(getProcessAPI().getProcessInstance(eventSubProcessActivity.getRootContainerId()) .getStringIndex1()) .isEqualTo("index1Value"); Assertions .assertThat(getProcessAPI().getProcessInstance(eventSubProcessActivity.getParentProcessInstanceId()) .getStringIndex1()) .isNull(); disableAndDeleteProcess(processDefinition); } @Test public void getProcessInstancesWithStringIndex() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("1" + PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask("step1", ACTOR_NAME); final ExpressionBuilder expressionBuilder = new ExpressionBuilder(); processBuilder.setStringIndex(1, "Label1", expressionBuilder.createConstantStringExpression("Value1")); processBuilder.setStringIndex(2, "Label2", expressionBuilder.createGroovyScriptExpression("script", "return \"a\"+\"b\";", String.class.getName())); processBuilder.setStringIndex(3, "Label3", null); final ProcessDefinition process1 = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = startProcessAndWaitForTask(process1.getId(), "step1") .getProcessInstance(); assertEquals("Label1", processInstance.getStringIndexLabel(1)); assertEquals("Label2", processInstance.getStringIndexLabel(2)); assertEquals("Label3", processInstance.getStringIndexLabel(3)); assertNull(processInstance.getStringIndexLabel(4)); assertNull(processInstance.getStringIndexLabel(5)); assertEquals("Value1", processInstance.getStringIndex1()); assertEquals("ab", processInstance.getStringIndex2()); assertNull(processInstance.getStringIndex3()); assertNull(processInstance.getStringIndex4()); assertNull(processInstance.getStringIndex5()); disableAndDeleteProcess(process1); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessParameterIT.java ================================================ /** * 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.process; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.*; import java.io.Serializable; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.form.FormMappingDefinitionBuilder; import org.bonitasoft.engine.bpm.form.FormMappingModelBuilder; import org.bonitasoft.engine.bpm.parameter.ParameterCriterion; import org.bonitasoft.engine.bpm.parameter.ParameterInstance; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.form.FormMappingTarget; import org.bonitasoft.engine.form.FormMappingType; import org.bonitasoft.engine.identity.User; import org.junit.After; import org.junit.Before; import org.junit.Test; public class ProcessParameterIT extends CommonAPIIT { @After public void afterTest() throws BonitaException { logout(); } @Before public void beforeTest() throws BonitaException { loginWithTechnicalUser(); } @Test public void getNoParametersWhenAddingNoParameters() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addUserTask("userTask1", null); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final int numberOfParamters = getProcessAPI().getNumberOfParameterInstances(definition.getId()); assertEquals(0, numberOfParamters); deleteProcess(definition); } @Test public void getNumberOfParameters() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addParameter("key1", String.class.getCanonicalName()) .addParameter("key2", String.class.getCanonicalName()) .addParameter("key3", String.class.getCanonicalName()) .addParameter("key4", String.class.getCanonicalName()).addUserTask("userTask1", null); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final int numberOfParamters = getProcessAPI().getNumberOfParameterInstances(definition.getId()); assertEquals(4, numberOfParamters); deleteProcess(definition); } @Test(expected = RetrieveException.class) public void getNumberOfParametersThrowsAnExceptionBecauseTheProcessDoesNotExist() { getProcessAPI().getNumberOfParameterInstances(45); } @Test public void getNoParameters() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addUserTask("userTask1", null); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final List parameters = getProcessAPI().getParameterInstances(definition.getId(), 0, 20, ParameterCriterion.NAME_DESC); assertEquals(0, parameters.size()); deleteProcess(definition); } @Test public void getParameters() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addParameter("key1", String.class.getCanonicalName()) .addParameter("key2", String.class.getCanonicalName()) .addUserTask("userTask1", null); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final Map params = new HashMap<>(); params.put("key1", "engine"); params.put("key2", "bos"); businessArchive.setParameters(params); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final List parameters = getProcessAPI().getParameterInstances(definition.getId(), 0, 20, ParameterCriterion.NAME_ASC); assertEquals(2, parameters.size()); final ParameterInstance firstParameter = parameters.get(0); assertEquals("key1", firstParameter.getName()); assertEquals("engine", firstParameter.getValue()); final ParameterInstance secondParameter = parameters.get(1); assertEquals("key2", secondParameter.getName()); assertEquals("bos", secondParameter.getValue()); deleteProcess(definition); } @Test public void getParameter() throws Exception { final String parameterValue = "a very important piece of information"; final String parameterName = "myParam1"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("getParameter", "1.0"); processBuilder.addParameter("myParam1", String.class.getCanonicalName()) .addDescription("Parameter description"); processBuilder.addParameter("myParam2", String.class.getCanonicalName()).addUserTask("userTask1", null); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final Map params = new HashMap<>(); params.put(parameterName, parameterValue); byte[] theParam2Value = new byte[1150]; Arrays.fill(theParam2Value, (byte) 65); params.put("myParam2", new String(theParam2Value)); businessArchive.setParameters(params); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final ParameterInstance parameter = getProcessAPI().getParameterInstance(definition.getId(), parameterName); assertEquals(parameterName, parameter.getName()); assertEquals(parameterValue, parameter.getValue()); assertEquals("Parameter description", parameter.getDescription()); deleteProcess(definition); } @Test public void setProcessDataDefaultValueWithParameterValue() throws Exception { final User user = createUser("jules", "his_password"); final String parameterName = "anotherParam"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("setDataDefaultValueWithParameter", "9.23"); processBuilder.addActor(ACTOR_NAME); final String aTask = "userTask1"; final String dataName = "aData"; processBuilder .addParameter(parameterName, String.class.getCanonicalName()) .addData(dataName, String.class.getName(), new ExpressionBuilder().createParameterExpression("takes value of default parameter value", parameterName, String.class.getName())) .addUserTask(aTask, ACTOR_NAME); final DesignProcessDefinition design = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(design); final Map params = new HashMap<>(1); final String paramValue = "4 is the answer"; params.put(parameterName, paramValue); businessArchive.setParameters(params); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, aTask); final DataInstance dataInstance = getProcessAPI().getProcessDataInstance(dataName, processInstance.getId()); assertEquals(paramValue, dataInstance.getValue()); disableAndDeleteProcess(processDefinition); deleteUser(user); } @Test public void deployWithNullParamAndFormMappings() throws Exception { final String parameterName = "myParam1"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("getParameter", "1.0"); processBuilder.addParameter("myParam1", String.class.getCanonicalName()) .addParameter("myParam2", String.class.getCanonicalName()) .addUserTask("userTask1", null); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final Map params = new HashMap<>(); params.put(parameterName, null); businessArchive.setParameters(params); businessArchive.setFormMappings(FormMappingModelBuilder.buildFormMappingModel().withFormMapping( FormMappingDefinitionBuilder .buildFormMapping("somePage", FormMappingType.TASK, FormMappingTarget.INTERNAL) .withTaskname("someTask").build()) .build()); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); deleteProcess(definition); } @Test(expected = RetrieveException.class) public void getParameterOfAnUnknownProcess() throws BonitaException { getProcessAPI().getParameterInstance(123456789l, "unknown"); } @Test(expected = NotFoundException.class) public void getUnknownParameter() throws Exception { final String parameterValue = "a very important piece of information"; final String parameterName = "myParam1"; final String wrongParameterName = "wrongParameterName"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("getUnknownParameter", "1.0"); processBuilder.addParameter("myParam1", String.class.getCanonicalName()) .addParameter("myParam2", String.class.getCanonicalName()) .addUserTask("userTask1", null); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final Map params = new HashMap<>(); params.put(parameterName, parameterValue); params.put("myParam2", "an unused parameter"); businessArchive.setParameters(params); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); try { getProcessAPI().getParameterInstance(definition.getId(), wrongParameterName); } finally { deleteProcess(definition); } } @Test public void sortParametersByNameAsc() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addParameter("bee", String.class.getCanonicalName()) .addParameter("bear", String.class.getCanonicalName()) .addParameter("squirrel", String.class.getCanonicalName()) .addParameter("donkey", String.class.getCanonicalName()) .addUserTask("userTask1", null); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final Map params = new HashMap<>(); params.put("donkey", "engine"); params.put("bear", "bos"); params.put("squirrel", "bee"); params.put("bee", "busy"); businessArchive.setParameters(params); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final List parameters = getProcessAPI().getParameterInstances(definition.getId(), 0, 20, ParameterCriterion.NAME_ASC); assertEquals(4, parameters.size()); assertEquals("bear", parameters.get(0).getName()); assertEquals("bee", parameters.get(1).getName()); assertEquals("donkey", parameters.get(2).getName()); assertEquals("squirrel", parameters.get(3).getName()); deleteProcess(definition); } @Test public void sortParametersByNameDesc() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addParameter("bee", String.class.getCanonicalName()) .addParameter("bear", String.class.getCanonicalName()) .addParameter("squirrel", String.class.getCanonicalName()) .addParameter("donkey", String.class.getCanonicalName()) .addUserTask("userTask1", null); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final Map params = new HashMap<>(); params.put("donkey", "engine"); params.put("bear", "bos"); params.put("squirrel", "bee"); params.put("bee", "busy"); businessArchive.setParameters(params); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final List parameters = getProcessAPI().getParameterInstances(definition.getId(), 0, 20, ParameterCriterion.NAME_DESC); assertEquals(4, parameters.size()); assertEquals("squirrel", parameters.get(0).getName()); assertEquals("donkey", parameters.get(1).getName()); assertEquals("bee", parameters.get(2).getName()); assertEquals("bear", parameters.get(3).getName()); deleteProcess(definition); } @Test public void getPageOne() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addParameter("bee", String.class.getCanonicalName()) .addParameter("bear", String.class.getCanonicalName()) .addParameter("squirrel", String.class.getCanonicalName()) .addParameter("donkey", String.class.getCanonicalName()) .addUserTask("userTask1", null); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final Map params = new HashMap<>(); params.put("donkey", "engine"); params.put("bear", "bos"); params.put("squirrel", "bee"); params.put("bee", "busy"); businessArchive.setParameters(params); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final List parameters = getProcessAPI().getParameterInstances(definition.getId(), 0, 2, ParameterCriterion.NAME_DESC); assertEquals(2, parameters.size()); assertEquals("squirrel", parameters.get(0).getName()); assertEquals("donkey", parameters.get(1).getName()); deleteProcess(definition); } @Test public void getPageTwo() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addParameter("bee", String.class.getCanonicalName()) .addParameter("bear", String.class.getCanonicalName()) .addParameter("squirrel", String.class.getCanonicalName()) .addParameter("donkey", String.class.getCanonicalName()) .addUserTask("userTask1", null); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final Map params = new HashMap<>(); params.put("donkey", "engine"); params.put("bear", "bos"); params.put("squirrel", "bee"); params.put("bee", "busy"); businessArchive.setParameters(params); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final List parameters = getProcessAPI().getParameterInstances(definition.getId(), 2, 2, ParameterCriterion.NAME_DESC); assertEquals(2, parameters.size()); assertEquals("bee", parameters.get(0).getName()); assertEquals("bear", parameters.get(1).getName()); deleteProcess(definition); } @Test public void getPageTwoOutOfBound() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addParameter("bee", String.class.getCanonicalName()) .addParameter("bear", String.class.getCanonicalName()) .addParameter("squirrel", String.class.getCanonicalName()) .addParameter("donkey", String.class.getCanonicalName()) .addUserTask("userTask1", null); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final Map params = new HashMap<>(); params.put("donkey", "engine"); params.put("bear", "bos"); params.put("squirrel", "bee"); params.put("bee", "busy"); businessArchive.setParameters(params); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final List parameterInstances = getProcessAPI().getParameterInstances(definition.getId(), 8, 8, ParameterCriterion.NAME_ASC); assertEquals(0, parameterInstances.size()); deleteProcess(definition); } @Test public void emptyParameterIsAValidValue() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("emptyParameterIsAValidValue", "1.7"); processBuilder.addParameter("Astronaut", String.class.getCanonicalName()).addUserTask("userTask1", null); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final Map params = new HashMap<>(1); params.put("Astronaut", ""); businessArchive.setParameters(params); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(definition.getId()); assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState()); deleteProcess(definition); } @Test public void resolvedDependencies() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addParameter("bee", String.class.getCanonicalName()) .addParameter("bear", String.class.getCanonicalName()) .addParameter("squirrel", String.class.getCanonicalName()) .addParameter("donkey", String.class.getCanonicalName()) .addUserTask("userTask1", null); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final Map params = new HashMap<>(); params.put("donkey", "engine"); params.put("bear", "bos"); params.put("squirrel", "bee"); params.put("bee", "busy"); businessArchive.setParameters(params); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(definition.getId()); assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState()); deleteProcess(definition); } @Test public void showResolvedAndUnresolvedParameters() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addParameter("bee", String.class.getCanonicalName()).addDescription("description") .addParameter("bear", String.class.getCanonicalName()) .addUserTask("userTask1", null); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final Map params = new HashMap<>(); params.put("bee", "busy"); businessArchive.setParameters(params); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final List parameters = getProcessAPI().getParameterInstances(definition.getId(), 0, 20, ParameterCriterion.NAME_DESC); assertEquals(2, parameters.size()); final ParameterInstance parameter1 = parameters.get(0); assertEquals("bee", parameter1.getName()); assertEquals("busy", parameter1.getValue()); assertEquals("description", parameter1.getDescription()); assertEquals(String.class.getCanonicalName(), parameter1.getType()); final ParameterInstance parameter2 = parameters.get(1); assertEquals("bear", parameter2.getName()); assertNull(parameter2.getValue()); assertNull(parameter2.getDescription()); assertEquals(String.class.getCanonicalName(), parameter2.getType()); deleteProcess(definition); } @Test public void testParametersAreWellTyped() throws Exception { final String actor = "acting"; final User jack = createUserAndLogin("jack", "leaking_caldron"); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance( "testCantResolveDataInExpressionInDataDefaultValue", "1"); processBuilder.addActor(actor).addDescription("Process to test archiving mechanism"); processBuilder.addDoubleData("aData", null); processBuilder.addParameter("integerValue", String.class.getName()); processBuilder.addParameter("booleanValue", String.class.getName()); processBuilder.addParameter("doubleValue", String.class.getName()); processBuilder.addUserTask("humanTask", actor); final Map parameters = new HashMap<>(); parameters.put("integerValue", "15"); parameters.put("booleanValue", "true"); parameters.put("doubleValue", "1.1"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActorAndParameters(processBuilder.done(), actor, jack, parameters); final Expression integerParameter = new ExpressionBuilder().createParameterExpression("integerExpression", "integerValue", Integer.class.getName()); final Expression isIntegerValueInteger = new ExpressionBuilder().createGroovyScriptExpression( "testIntegerValueToBeInteger", "integerValue instanceof Integer", Boolean.class.getName(), integerParameter); final Expression booleanParameter = new ExpressionBuilder().createParameterExpression("booleanParameter", "booleanValue", Boolean.class.getName()); final Expression isBooleanValueBoolean = new ExpressionBuilder().createGroovyScriptExpression( "testBooleanValueToBeBoolean", "booleanValue instanceof Boolean", Boolean.class.getName(), booleanParameter); final Expression doubleParameter = new ExpressionBuilder().createParameterExpression("doubleExpression", "doubleValue", Double.class.getName()); final Expression isDoubleValueDouble = new ExpressionBuilder().createGroovyScriptExpression( "testDoubleValueToBeDouble", "doubleValue instanceof Double", Boolean.class.getName(), doubleParameter); final Map inputValues = new HashMap<>(0); assertThat((Boolean) getProcessAPI().evaluateExpressionOnProcessDefinition(isIntegerValueInteger, inputValues, processDefinition.getId()), is(true)); assertThat((Boolean) getProcessAPI().evaluateExpressionOnProcessDefinition(isBooleanValueBoolean, inputValues, processDefinition.getId()), is(true)); assertThat((Boolean) getProcessAPI().evaluateExpressionOnProcessDefinition(isDoubleValueDouble, inputValues, processDefinition.getId()), is(true)); disableAndDeleteProcess(processDefinition); deleteUser(jack); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessResolutionIT.java ================================================ /** * 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.process; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.List; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.actor.ActorCriterion; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.actor.ActorMember; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.junit.Assert; import org.junit.Test; public class ProcessResolutionIT extends TestWithTechnicalUser { @Test(expected = ProcessDefinitionNotFoundException.class) public void processNotFoundWhenGettingResolutionProblems() throws BonitaException { getProcessAPI().getProcessResolutionProblems(-458); } @Test public void noActorMapping() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); builder.createNewInstance("resolve", "1.0").addActor("Leader").addUserTask("step1", "Leader"); final DesignProcessDefinition processDefinition = builder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDefinition).done(); final ProcessDefinition definition = deployProcess(businessArchive); final ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId()); Assert.assertEquals(ConfigurationState.UNRESOLVED, deploymentInfo.getConfigurationState()); final List problems = getProcessAPI().getProcessResolutionProblems(definition.getId()); Assert.assertEquals(1, problems.size()); final Problem problem = problems.get(0); Assert.assertEquals(Problem.Level.ERROR, problem.getLevel()); Assert.assertEquals("actor", problem.getResource()); Assert.assertNotNull(problem.getDescription()); deleteProcess(definition.getId()); } @Test public void resolveActorMapping() throws Exception, InterruptedException { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); builder.createNewInstance("resolve", "1.0").addActor("Leader", true).addUserTask("step1", "Leader"); final DesignProcessDefinition processDefinition = builder.done(); final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive(); archiveBuilder.addClasspathResource(new BarResource("aDependency", new byte[] { 1, 5, 2, 3, 6, 4, 6, 8 })); final BusinessArchive businessArchive = archiveBuilder.setProcessDefinition(processDefinition).done(); final ProcessDefinition definition = deployProcess(businessArchive); ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId()); long previousUpdate = deploymentInfo.getLastUpdateDate().getTime(); long lastUpdate = previousUpdate; Assert.assertEquals(ConfigurationState.UNRESOLVED, deploymentInfo.getConfigurationState()); // add actor mapping on user Thread.sleep(10); final ActorInstance initiator = getProcessAPI().getActorInitiator(definition.getId()); getProcessAPI().addUserToActor(initiator.getId(), getSession().getUserId()); // check state is resolved deploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId()); Assert.assertEquals(ConfigurationState.RESOLVED, deploymentInfo.getConfigurationState()); lastUpdate = deploymentInfo.getLastUpdateDate().getTime(); assertTrue("lastupdate date not changed", lastUpdate > previousUpdate); previousUpdate = lastUpdate; List problems = getProcessAPI().getProcessResolutionProblems(definition.getId()); Assert.assertEquals(0, problems.size()); Thread.sleep(10); // remove actor member final ActorInstance actorInstance = getProcessAPI() .getActors(definition.getId(), 0, 10, ActorCriterion.NAME_ASC).get(0); final ActorMember actorMember = getProcessAPI().getActorMembers(actorInstance.getId(), 0, 10).get(0); getProcessAPI().removeActorMember(actorMember.getId()); // check unresolved deploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId()); Assert.assertEquals(ConfigurationState.UNRESOLVED, deploymentInfo.getConfigurationState()); lastUpdate = deploymentInfo.getLastUpdateDate().getTime(); assertTrue("lastupdate date not changed", lastUpdate > previousUpdate); previousUpdate = lastUpdate; Thread.sleep(10); // add user again to actor and check resolved again getProcessAPI().addUserToActor(actorInstance.getId(), getSession().getUserId()); deploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId()); Assert.assertEquals(ConfigurationState.RESOLVED, deploymentInfo.getConfigurationState()); lastUpdate = deploymentInfo.getLastUpdateDate().getTime(); assertTrue("lastupdate date not changed", lastUpdate > previousUpdate); previousUpdate = lastUpdate; problems = getProcessAPI().getProcessResolutionProblems(definition.getId()); Assert.assertEquals(0, problems.size()); deleteProcess(definition.getId()); } @Test public void deploy2ProcessWithSameDependency() throws Exception { ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); builder.createNewInstance("process", "1.0").addAutomaticTask("step1"); final DesignProcessDefinition processDefinition1 = builder.done(); final BusinessArchiveBuilder archiveBuilder1 = new BusinessArchiveBuilder().createNewBusinessArchive(); final BarResource resource = new BarResource("aDependency", new byte[] { 1, 5, 2, 3, 6, 4, 6, 8 }); archiveBuilder1.addClasspathResource(resource); final BusinessArchive businessArchive1 = archiveBuilder1.setProcessDefinition(processDefinition1).done(); final ProcessDefinition definition1 = getProcessAPI().deploy(businessArchive1); builder = new ProcessDefinitionBuilder(); builder.createNewInstance("processbis", "1.0").addAutomaticTask("step1"); final DesignProcessDefinition processDefinition2 = builder.done(); final BusinessArchiveBuilder archiveBuilder2 = new BusinessArchiveBuilder().createNewBusinessArchive(); archiveBuilder2.addClasspathResource(resource); final BusinessArchive businessArchive2 = archiveBuilder2.setProcessDefinition(processDefinition2).done(); final ProcessDefinition definition2 = getProcessAPI().deploy(businessArchive2); deleteProcess(definition1.getId()); deleteProcess(definition2.getId()); } @Test public void removeLastUserFromActorUnresolvesProcess() throws Exception { final User piouPiou = getIdentityAPI().createUser("Piou-piou", "s3cR3t"); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); final String actor = "Dev Leader"; builder.createNewInstance("update proc resolution", "1.0").addActor(actor, true).addUserTask("test_session", actor); final DesignProcessDefinition processDefinition = builder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDefinition).done(); final ProcessDefinition definition = deployProcess(businessArchive); final ActorInstance initiator = getProcessAPI().getActorInitiator(definition.getId()); getProcessAPI().addUserToActor(initiator.getId(), piouPiou.getId()); ProcessDeploymentInfo processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId()); assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState()); final List actorMembers = getProcessAPI().getActorMembers(initiator.getId(), 0, 10); getProcessAPI().removeActorMember(actorMembers.get(0).getId()); // reload the process deploy info: processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId()); assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState()); deleteProcess(definition.getId()); deleteUser(piouPiou); } @Test public void deleteUserFromActorUnresolvesProcess() throws Exception { final User piouPiou = getIdentityAPI().createUser("Piou-piou", "s3cR3t"); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); final String actor = "Dev Leader"; builder.createNewInstance("update proc resolution", "1.1").addActor(actor, true).addUserTask("deleteUser", actor); final DesignProcessDefinition processDefinition = builder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDefinition).done(); final ProcessDefinition definition = deployProcess(businessArchive); final ActorInstance initiator = getProcessAPI().getActorInitiator(definition.getId()); getProcessAPI().addUserToActor(initiator.getId(), piouPiou.getId()); ProcessDeploymentInfo processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId()); assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState()); getIdentityAPI().deleteUser(piouPiou.getId()); // reload the process deploy info: processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId()); assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState()); deleteProcess(definition.getId()); } @Test public void deleteRoleFromActorUnresolvesProcess() throws Exception { final Role role = getIdentityAPI().createRole("Tester"); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); final String actor = "Dev Leader"; builder.createNewInstance("update proc resolution", "1.2").addActor(actor, true).addUserTask("deleteRole", actor); final DesignProcessDefinition processDefinition = builder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDefinition).done(); final ProcessDefinition definition = deployProcess(businessArchive); final ActorInstance initiator = getProcessAPI().getActorInitiator(definition.getId()); getProcessAPI().addRoleToActor(initiator.getId(), role.getId()); ProcessDeploymentInfo processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId()); assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState()); getIdentityAPI().deleteRole(role.getId()); // reload the process deploy info: processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId()); assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState()); deleteProcess(definition.getId()); } @Test public void deleteGroupFromActorUnresolvesProcess() throws Exception { final Group group = getIdentityAPI().createGroup("Tester", null); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); final String actor = "Dev Leader"; builder.createNewInstance("update proc resolution", "1.2").addActor(actor, true).addUserTask("deleteGroup", actor); final DesignProcessDefinition processDefinition = builder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDefinition).done(); final ProcessDefinition definition = deployProcess(businessArchive); final ActorInstance initiator = getProcessAPI().getActorInitiator(definition.getId()); getProcessAPI().addGroupToActor(initiator.getId(), group.getId()); ProcessDeploymentInfo processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId()); assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState()); getIdentityAPI().deleteGroup(group.getId()); // reload the process deploy info: processDeploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId()); assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState()); deleteProcess(definition.getId()); } @Test public void noConnectorImplementation() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); builder.createNewInstance("resolve", "1.0").addAutomaticTask("auto").addConnector("exec", "exec-1.0", "1.0", ConnectorEvent.ON_ENTER); final DesignProcessDefinition processDefinition = builder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDefinition).done(); final ProcessDefinition definition = deployProcess(businessArchive); final ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(definition.getId()); Assert.assertEquals(ConfigurationState.UNRESOLVED, deploymentInfo.getConfigurationState()); final List problems = getProcessAPI().getProcessResolutionProblems(definition.getId()); Assert.assertEquals(1, problems.size()); final Problem problem = problems.get(0); Assert.assertEquals(Problem.Level.ERROR, problem.getLevel()); Assert.assertEquals("connector", problem.getResource()); Assert.assertNotNull(problem.getDescription()); deleteProcess(definition.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/ProcessWithExpressionIT.java ================================================ /** * 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.process; import static org.junit.Assert.*; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.ComparisonOperator; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.expression.ExpressionEvaluationException; import org.bonitasoft.engine.expression.XPathReturnType; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.junit.Test; /** * @author Baptiste Mesta */ public class ProcessWithExpressionIT extends TestWithUser { @Test public void executeProcessWithListExpression() throws Exception { final Serializable result = executeProcessAndGetResultOfExpression( new ExpressionBuilder().createListExpression("list1", Collections.singletonList(new ExpressionBuilder().createConstantStringExpression("test"))), List.class.getName()); assertTrue(result instanceof List); assertEquals(1, ((List) result).size()); assertEquals("test", ((List) result).get(0)); } @Test public void executeProcessWithListOfListOfExpression() throws Exception { final Serializable result = executeProcessAndGetResultOfExpression( new ExpressionBuilder().createListOfListExpression("myMap", Collections.singletonList(Collections .singletonList(new ExpressionBuilder().createConstantStringExpression("test")))), "java.util.List"); assertTrue(result instanceof List); assertEquals(1, ((List) result).size()); assertEquals("test", ((List) ((List) result).get(0)).get(0)); } @Test public void executeProcessWithScriptUsingApi() throws Exception { final Serializable result = executeProcessAndGetResultOfExpression( new ExpressionBuilder().createGroovyScriptExpression("scriptAPI", "apiAccessor.getProcessAPI().getProcessDefinition(processDefinitionId) != null ?Boolean.TRUE:Boolean.false", Boolean.class.getName(), new ExpressionBuilder().createEngineConstant(ExpressionConstants.API_ACCESSOR), new ExpressionBuilder().createEngineConstant(ExpressionConstants.PROCESS_DEFINITION_ID)), Boolean.class.getName()); assertEquals(Boolean.TRUE, result); } private Serializable executeProcessAndGetResultOfExpression(final Expression expression, final String dataType) throws Exception { return executeProcessAndGetResultOfExpression(expression, dataType, null, null, null); } private Serializable executeProcessAndGetResultOfExpression(final Expression expression, final String dataType, final String extraDataName, final String extraDataType, final Expression extraDataValue) throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("processWithExpression", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addDescription("Delivery all day and night long"); designProcessDefinition.addData("data1", dataType, null); if (extraDataName != null) { designProcessDefinition.addData(extraDataName, extraDataType, extraDataValue); } designProcessDefinition.addAutomaticTask("step1").addOperation( new LeftOperandBuilder().createNewInstance("data1").done(), OperatorType.ASSIGNMENT, "=", null, expression); designProcessDefinition.addUserTask("step2", ACTOR_NAME); designProcessDefinition.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step2"); final DataInstance processDataInstance = getProcessAPI().getProcessDataInstance("data1", processInstance.getId()); final Serializable value = processDataInstance.getValue(); disableAndDeleteProcess(processDefinition); return value; } @Test public void evaluateGroovyScriptWithConnectorHavingDependencies() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder(); final ProcessDefinitionBuilder pBuilder = processDefinitionBuilder.createNewInstance("emptyProcess", String.valueOf(System.currentTimeMillis())); pBuilder.addActor(ACTOR_NAME) .addUserTask("step1", ACTOR_NAME) .addDisplayName( new ExpressionBuilder().createGroovyScriptExpression("myScript", "new org.bonitasoft.engine.test.TheClassOfMyLibrary().aPublicMethod()", String.class.getName())); final DesignProcessDefinition done = pBuilder.done(); final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(done); final InputStream stream = CommonAPIIT.class.getResourceAsStream("/mylibrary-jar.bak"); assertNotNull(stream); final byte[] byteArray = IOUtils.toByteArray(stream); stream.close(); builder.addClasspathResource(new BarResource("mylibrary.jar", byteArray)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); try { final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final ActivityInstance task = waitForUserTaskAndGetIt(processInstance, "step1"); assertEquals("stringFromPublicMethod", task.getDisplayName()); } finally { disableAndDeleteProcess(processDefinition); } } @Test public void evaluateGroovyScriptWithDifferentClassloader() throws Exception { final BarResource myLibrary1 = getMyLibrary(); final BarResource myLibrary2 = getMyLibrary2(); final String scriptLib1 = "new org.bonitasoft.engine.test.TheClassOfMyLibrary().aPublicMethod()"; final String scriptLib2 = "new org.bonitasoft.engine.test2.TheClassOfMyLibrary2().aPublicMethod2()"; final ProcessDefinition p11 = deployProcessWithScriptAndLibrary(myLibrary1, scriptLib1); final ProcessDefinition p22 = deployProcessWithScriptAndLibrary(myLibrary2, scriptLib2); final ProcessDefinition p12 = deployProcessWithScriptAndLibrary(myLibrary1, scriptLib2); try { final ProcessInstance pi11 = getProcessAPI().startProcess(p11.getId()); final ProcessInstance pi22 = getProcessAPI().startProcess(p22.getId()); final ProcessInstance pi12 = getProcessAPI().startProcess(p12.getId()); final ActivityInstance task1 = waitForUserTaskAndGetIt(pi11, "step1"); assertEquals("stringFromPublicMethod", task1.getDisplayName()); final ActivityInstance task2 = waitForUserTaskAndGetIt(pi22, "step1"); assertEquals("stringFromPublicMethod2", task2.getDisplayName()); final ActivityInstance task3 = waitForTaskToFail(pi12); assertEquals("step1", task3.getName()); final ProcessInstance pi11bis = getProcessAPI().startProcess(p11.getId()); final ActivityInstance task1bis = waitForUserTaskAndGetIt(pi11bis, "step1"); assertEquals("stringFromPublicMethod", task1bis.getDisplayName()); } finally { disableAndDeleteProcess(p11); disableAndDeleteProcess(p22); disableAndDeleteProcess(p12); } } private ProcessDefinition deployProcessWithScriptAndLibrary(final BarResource myLibrary, final String script) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder(); final ProcessDefinitionBuilder pBuilder = processDefinitionBuilder.createNewInstance("emptyProcess", String.valueOf(System.currentTimeMillis())); pBuilder.addActor(ACTOR_NAME).addUserTask("step1", ACTOR_NAME) .addDisplayName(new ExpressionBuilder().createGroovyScriptExpression("myScript", script, String.class.getName())); final DesignProcessDefinition done = pBuilder.done(); final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(done); builder.addClasspathResource(myLibrary); return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); } private BarResource getMyLibrary() throws IOException { final InputStream stream = CommonAPIIT.class.getResourceAsStream("/mylibrary-jar.bak"); final byte[] byteArray = IOUtils.toByteArray(stream); stream.close(); return new BarResource("mylibrary.jar", byteArray); } private BarResource getMyLibrary2() throws IOException { final InputStream stream = CommonAPIIT.class.getResourceAsStream("/mylibrary2-jar.bak"); final byte[] byteArray = IOUtils.toByteArray(stream); stream.close(); return new BarResource("mylibrary.jar", byteArray); } @Test public void evaluateConstantExpressionFromApi() throws Exception { final ProcessDefinition processDefinition = deployEmptyProcess(); Expression expression = new ExpressionBuilder().createConstantBooleanExpression(true); final Map inputValues = new HashMap<>(0); assertEquals(true, getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues, processDefinition.getId())); expression = new ExpressionBuilder().createConstantStringExpression("test"); assertEquals("test", getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues, processDefinition.getId())); expression = new ExpressionBuilder().createConstantLongExpression(123456L); assertEquals(123456L, getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues, processDefinition.getId())); disableAndDeleteProcess(processDefinition); } private ProcessDefinition deployEmptyProcess() throws Exception { final DesignProcessDefinition done = new ProcessDefinitionBuilder() .createNewInstance("emptyProcess", String.valueOf(System.currentTimeMillis())) .done(); return deployAndEnableProcess(done); } @Test public void evaluateInputExpressionFromApi() throws Exception { final ProcessDefinition processDefinition = deployEmptyProcess(); final Expression expression = new ExpressionBuilder().createInputExpression("test", Boolean.class.getName()); final Map inputValues = new HashMap<>(); inputValues.put("test", true); assertEquals(true, getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues, processDefinition.getId())); disableAndDeleteProcess(processDefinition); } @Test public void evaluateGroovyExpressionFromApi() throws Exception { final ProcessDefinition processDefinition = deployEmptyProcess(); final Expression expression = new ExpressionBuilder().createGroovyScriptExpression( "evaluateGroovyExpressionFromApi", "input1 + 12", Integer.class.getName(), new ExpressionBuilder().createInputExpression("input1", Integer.class.getName())); final Map inputValues = new HashMap<>(1); inputValues.put("input1", 8); assertEquals(20, getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues, processDefinition.getId())); disableAndDeleteProcess(processDefinition); } @Test public void evaluateGroovyWithDataProvidedExpressionFromApi() throws Exception { final ProcessDefinition processDefinition = deployEmptyProcess(); final Expression expression = new ExpressionBuilder().createGroovyScriptExpression( "evaluateGroovyWithDataProvidedExpressionFromApi", "input1 + data1 + 12", Integer.class.getName(), new ExpressionBuilder().createInputExpression("input1", Integer.class.getName()), new ExpressionBuilder().createDataExpression("data1", Integer.class.getName())); final Map inputValues = new HashMap<>(2); inputValues.put("input1", 6); inputValues.put("data1", 2); assertEquals(20, getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues, processDefinition.getId())); disableAndDeleteProcess(processDefinition); } @SuppressWarnings("unchecked") @Test public void evaluateEmbeddedListExpressions() throws Exception { final ProcessDefinition processDefinition = deployEmptyProcess(); final Expression constantExpr = new ExpressionBuilder().createConstantStringExpression("DUMMY"); final String data1Content = "data1_name"; final Expression data1Expr = new ExpressionBuilder().createDataExpression(data1Content, String.class.getName()); final Expression listExpression1 = new ExpressionBuilder().createListExpression("manuList1", Arrays.asList(constantExpr, data1Expr)); final Expression felixConstExp = new ExpressionBuilder().createConstantStringExpression("FELIX"); final Expression listExpression2 = new ExpressionBuilder().createListExpression("manuList2", Arrays.asList(listExpression1, felixConstExp)); final Map inputValues = new HashMap<>(1); inputValues.put(data1Content, "dataValue"); final List result = (List) getProcessAPI().evaluateExpressionOnProcessDefinition( listExpression2, inputValues, processDefinition.getId()); assertEquals(2, result.size()); assertEquals(2, ((List) result.get(0)).size()); assertEquals("DUMMY", ((List) result.get(0)).get(0)); assertEquals("dataValue", ((List) result.get(0)).get(1)); assertEquals("FELIX", result.get(1)); disableAndDeleteProcess(processDefinition); } @Test public void evaluateListExpression() throws Exception { final ProcessDefinition processDefinition = deployEmptyProcess(); final Expression groovyExpr = new ExpressionBuilder() .createGroovyScriptExpression("evaluateListExpression", "1+'_'+data1_name", String.class.getName()); final Expression constantExpr = new ExpressionBuilder().createConstantStringExpression("Newbee"); final String data1Content = "data1_name"; final Expression data1Expr = new ExpressionBuilder().createDataExpression(data1Content, String.class.getName()); final Expression listExpression = new ExpressionBuilder().createListExpression("ManuList1", Arrays.asList(groovyExpr, constantExpr, data1Expr)); final Map inputValues = new HashMap<>(1); inputValues.put(data1Content, "EXPRESSION"); @SuppressWarnings("unchecked") final List result = (List) getProcessAPI().evaluateExpressionOnProcessDefinition( listExpression, inputValues, processDefinition.getId()); assertEquals(3, result.size()); assertEquals("1_EXPRESSION", result.get(0)); assertEquals("Newbee", result.get(1)); assertEquals("EXPRESSION", result.get(2)); disableAndDeleteProcess(processDefinition); } @Test(expected = ExpressionEvaluationException.class) public void evaluateDataExpressionFromApiOnUnknownData() throws Exception { final ProcessDefinition processDefinition = deployEmptyProcess(); final Expression expression = new ExpressionBuilder().createDataExpression("data", Boolean.class.getName()); final Map inputValues = new HashMap<>(0); try { getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues, processDefinition.getId()); } finally { disableAndDeleteProcess(processDefinition); } } @Test public void evaluateGreaterThanComparationExpression() throws Exception { final Expression dependExpr1 = new ExpressionBuilder().createConstantDoubleExpression(5.1); final Expression dependExpr2 = new ExpressionBuilder().createConstantIntegerExpression(6); final Expression expression1 = new ExpressionBuilder().createComparisonExpression("comp1", dependExpr1, ComparisonOperator.GREATER_THAN, dependExpr2); Serializable result = executeProcessAndGetResultOfExpression(expression1, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertFalse((Boolean) result); final Expression expression2 = new ExpressionBuilder().createComparisonExpression("comp1", dependExpr2, ComparisonOperator.GREATER_THAN, dependExpr1); result = executeProcessAndGetResultOfExpression(expression2, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertTrue((Boolean) result); final Expression expression3 = new ExpressionBuilder().createComparisonExpression("comp2", dependExpr2, ComparisonOperator.GREATER_THAN, dependExpr2); result = executeProcessAndGetResultOfExpression(expression3, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertFalse((Boolean) result); } @Test public void evaluateLogicalComplementExpression() throws Exception { final Expression dependExpr1 = new ExpressionBuilder().createConstantBooleanExpression(true); final Expression dependExpr2 = new ExpressionBuilder().createGroovyScriptExpression("toReturnFalse", "4 == 5", Boolean.class.getName()); final Expression expression1 = new ExpressionBuilder().createLogicalComplementExpression("complement1", dependExpr1); Serializable result = executeProcessAndGetResultOfExpression(expression1, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertFalse((Boolean) result); final Expression expression2 = new ExpressionBuilder().createLogicalComplementExpression("complement1", dependExpr2); result = executeProcessAndGetResultOfExpression(expression2, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertTrue((Boolean) result); } @Test public void evaluateGreaterThanOrEqualsComparisonExpression() throws Exception { final Expression exprOperand1 = new ExpressionBuilder().createConstantIntegerExpression(7); final Expression exprOperand2 = new ExpressionBuilder().createConstantIntegerExpression(9); final Expression exprOperand3 = new ExpressionBuilder().createConstantIntegerExpression(7); final Expression exprOperand4 = new ExpressionBuilder().createConstantIntegerExpression(-8); final Expression exprResult1 = new ExpressionBuilder().createComparisonExpression("GreaterThanOrEquals1", exprOperand1, ComparisonOperator.GREATER_THAN_OR_EQUALS, exprOperand2); Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertFalse((Boolean) result); final Expression exprResult2 = new ExpressionBuilder().createComparisonExpression("GreaterThanOrEquals2", exprOperand1, ComparisonOperator.GREATER_THAN_OR_EQUALS, exprOperand3); result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertTrue((Boolean) result); final Expression exprResult3 = new ExpressionBuilder().createComparisonExpression("GreaterThanOrEquals3", exprOperand3, ComparisonOperator.GREATER_THAN_OR_EQUALS, exprOperand4); result = executeProcessAndGetResultOfExpression(exprResult3, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertTrue((Boolean) result); } @Test public void evaluateLessThanComparisonExpression() throws Exception { final Expression exprOperand1 = new ExpressionBuilder().createConstantIntegerExpression(7); final Expression exprOperand2 = new ExpressionBuilder().createConstantIntegerExpression(9); final Expression exprOperand3 = new ExpressionBuilder().createConstantIntegerExpression(7); final Expression exprOperand4 = new ExpressionBuilder().createConstantIntegerExpression(-8); final Expression exprResult1 = new ExpressionBuilder() .createComparisonExpression("LessThan1", exprOperand1, ComparisonOperator.LESS_THAN, exprOperand2); Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertTrue((Boolean) result); final Expression exprResult2 = new ExpressionBuilder() .createComparisonExpression("LessThan2", exprOperand1, ComparisonOperator.LESS_THAN, exprOperand3); result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertFalse((Boolean) result); final Expression exprResult3 = new ExpressionBuilder() .createComparisonExpression("LessThan3", exprOperand3, ComparisonOperator.LESS_THAN, exprOperand4); result = executeProcessAndGetResultOfExpression(exprResult3, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertFalse((Boolean) result); } @Test public void evaluateLessThanOrEqualsComparisonExpression() throws Exception { final Expression exprOperand1 = new ExpressionBuilder().createConstantIntegerExpression(7); final Expression exprOperand2 = new ExpressionBuilder().createConstantIntegerExpression(9); final Expression exprOperand3 = new ExpressionBuilder().createConstantIntegerExpression(7); final Expression exprOperand4 = new ExpressionBuilder().createConstantIntegerExpression(-8); final Expression exprResult1 = new ExpressionBuilder().createComparisonExpression("LessThanOrEquals1", exprOperand1, ComparisonOperator.LESS_THAN_OR_EQUALS, exprOperand2); Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertTrue((Boolean) result); final Expression exprResult2 = new ExpressionBuilder().createComparisonExpression("LessThanOrEquals2", exprOperand1, ComparisonOperator.LESS_THAN_OR_EQUALS, exprOperand3); result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertTrue((Boolean) result); final Expression exprResult3 = new ExpressionBuilder().createComparisonExpression("LessThanOrEquals3", exprOperand3, ComparisonOperator.LESS_THAN_OR_EQUALS, exprOperand4); result = executeProcessAndGetResultOfExpression(exprResult3, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertFalse((Boolean) result); } @Test public void evaluateComparisonExpressionWithBoolean() throws Exception { final Expression exprOperand1 = new ExpressionBuilder().createConstantBooleanExpression(false); final Expression exprOperand2 = new ExpressionBuilder().createConstantBooleanExpression(true); final Expression exprResult1 = new ExpressionBuilder().createComparisonExpression("Equals", exprOperand1, ComparisonOperator.EQUALS, exprOperand2); Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertFalse((Boolean) result); final Expression exprResult2 = new ExpressionBuilder().createComparisonExpression("GreaterThan", exprOperand2, ComparisonOperator.GREATER_THAN, exprOperand1); result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertTrue((Boolean) result); } @Test public void evaluateComparisonExpressionWithDouble() throws Exception { final Expression exprOperand1 = new ExpressionBuilder().createConstantDoubleExpression(1.0); final Expression exprOperand2 = new ExpressionBuilder().createConstantDoubleExpression(2.0); final Expression exprOperand3 = new ExpressionBuilder().createConstantDoubleExpression(1.0); final Expression exprResult1 = new ExpressionBuilder().createComparisonExpression("Equals", exprOperand1, ComparisonOperator.EQUALS, exprOperand3); Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertTrue((Boolean) result); final Expression exprResult2 = new ExpressionBuilder().createComparisonExpression("GreaterThan", exprOperand1, ComparisonOperator.GREATER_THAN, exprOperand2); result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertFalse((Boolean) result); } @Test public void evaluateComparisonExpressionWithLong() throws Exception { final Expression exprOperand1 = new ExpressionBuilder().createConstantLongExpression(145); final Expression exprOperand2 = new ExpressionBuilder().createConstantLongExpression(353); final Expression exprOperand3 = new ExpressionBuilder().createConstantLongExpression(145); final Expression exprResult1 = new ExpressionBuilder().createComparisonExpression("Equals", exprOperand1, ComparisonOperator.EQUALS, exprOperand3); Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertTrue((Boolean) result); final Expression exprResult2 = new ExpressionBuilder().createComparisonExpression("GreaterThan", exprOperand1, ComparisonOperator.GREATER_THAN, exprOperand2); result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertFalse((Boolean) result); } @Test public void evaluateComparisonExpressionWithLowerCaseString() throws Exception { final Expression exprOperand1 = new ExpressionBuilder().createConstantStringExpression("john"); final Expression exprOperand2 = new ExpressionBuilder().createConstantStringExpression("ashley"); final Expression exprOperand3 = new ExpressionBuilder().createConstantStringExpression("john"); final Expression exprResult1 = new ExpressionBuilder().createComparisonExpression("Equals", exprOperand1, ComparisonOperator.EQUALS, exprOperand3); Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertTrue((Boolean) result); final Expression exprResult2 = new ExpressionBuilder().createComparisonExpression("GreaterThan", exprOperand2, ComparisonOperator.GREATER_THAN, exprOperand1); result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertFalse((Boolean) result); } @Test public void evaluateComparisonExpressionWithLowerCaseAndUpperCaseString() throws Exception { final Expression exprOperand1 = new ExpressionBuilder().createConstantStringExpression("john"); final Expression exprOperand2 = new ExpressionBuilder().createConstantStringExpression("ashley"); final Expression exprOperand3 = new ExpressionBuilder().createConstantStringExpression("JOHN"); final Expression exprOperand4 = new ExpressionBuilder().createConstantStringExpression("ASHLEY"); final Expression exprResult1 = new ExpressionBuilder().createComparisonExpression("Equals", exprOperand1, ComparisonOperator.EQUALS, exprOperand3); Serializable result = executeProcessAndGetResultOfExpression(exprResult1, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertFalse((Boolean) result); final Expression exprResult2 = new ExpressionBuilder().createComparisonExpression("GreaterThan", exprOperand2, ComparisonOperator.GREATER_THAN, exprOperand4); result = executeProcessAndGetResultOfExpression(exprResult2, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertTrue((Boolean) result); final Expression exprResult3 = new ExpressionBuilder().createComparisonExpression("Equals", exprOperand3, ComparisonOperator.EQUALS, exprOperand1); result = executeProcessAndGetResultOfExpression(exprResult3, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertFalse((Boolean) result); final Expression exprResult4 = new ExpressionBuilder().createComparisonExpression("GreaterThan", exprOperand4, ComparisonOperator.GREATER_THAN, exprOperand1); result = executeProcessAndGetResultOfExpression(exprResult4, Boolean.class.getName()); assertTrue(result instanceof Boolean); assertFalse((Boolean) result); } @Test public void runProcessWithScriptThatThrowExceptionFailTheTask() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithGroovy", "1.0"); processBuilder.addAutomaticTask("activityThatFail").addData("data1", String.class.getName(), new ExpressionBuilder().createGroovyScriptExpression("script", "throw new Exception()", String.class.getName())); processBuilder.addUserTask("aTask", ACTOR_NAME); processBuilder.addActor(ACTOR_NAME); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "aTask"); waitForTaskToFail(processInstance); disableAndDeleteProcess(processDefinition); } @Test public void processWithXPathExpression() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("XPathExpression", "1.0"); processBuilder.addData("data", String.class.getName(), new ExpressionBuilder().createXPathExpression("xpath", "/root/element/@name", XPathReturnType.STRING, "")); processBuilder.addUserTask("aDummyTask", ACTOR_NAME); processBuilder.addActor(ACTOR_NAME); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "aDummyTask"); assertEquals("Alexander Corvinus", getProcessAPI().getProcessDataInstance("data", processInstance.getId()).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void executeProcessWithAutomaticTasksAndTransitionFailed() throws Exception { // Build condition final Expression condition = new ExpressionBuilder().createGroovyScriptExpression( "evaluateGroovyExpressionFromApi", "throw new Exception()", Boolean.class.getName()); // Build process final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder().createNewInstance( "executeConnectorOnStartOfAnAutomaticActivity", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addDescription("Delivery all day and night long"); designProcessDefinition.addAutomaticTask("step1"); designProcessDefinition.addAutomaticTask("step2"); designProcessDefinition.addAutomaticTask("default"); designProcessDefinition.addTransition("step1", "step2", condition); designProcessDefinition.addDefaultTransition("step1", "default"); // Start process final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), ACTOR_NAME, user); getProcessAPI().startProcess(processDefinition.getId()); // Test if step1 state is FAILED waitForFlowNodeInFailedState("step1"); disableAndDeleteProcess(processDefinition); } @Test public void evaluateJavaMethodCallExpression() throws Exception { final String extraDataName = "myValues"; final String extraDataType = List.class.getName(); final Expression extraDataValue = new ExpressionBuilder().createGroovyScriptExpression("defaultList", "return Arrays.asList(1, 2 , 3);", extraDataType); final Expression dep = new ExpressionBuilder().createDataExpression(extraDataName, extraDataType); final Expression expression = new ExpressionBuilder().createJavaMethodCallExpression("getter", "toString", String.class.getName(), dep); final Serializable result = executeProcessAndGetResultOfExpression(expression, String.class.getName(), extraDataName, extraDataType, extraDataValue); assertEquals("[1, 2, 3]", result); } @Test public void evaluateJavaMethodCallExpressionTwice() throws Exception { // the cache on ClassReflector made impossible to call the same expression on the same class if it was loaded by 2 different process final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive(); builder.addClasspathResource(getResource("/custom-0.1.jar.bak", "custom-0.1.jar")); final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithCustomData", "1.0"); designProcessDefinition.addData("adress", "org.bonitasoft.custom.Address", new ExpressionBuilder().createGroovyScriptExpression("create adress", "new org.bonitasoft.custom.Address(\"name1\",\"Rue ampère\",\"38000\",\"Grenoble\",\"France\")", "org.bonitasoft.custom.Address")); designProcessDefinition .addActor(ACTOR_NAME) .addUserTask("step1", ACTOR_NAME) .addDisplayName( new ExpressionBuilder().createJavaMethodCallExpression("getNameOfAdress", "getName", String.class.getName(), new ExpressionBuilder().createDataExpression("adress", "org.bonitasoft.custom.Address"))); designProcessDefinition.addAutomaticTask("start").addTransition("start", "step1"); builder.setProcessDefinition(designProcessDefinition.done()); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition1.getId()); final ActivityInstance userTask = waitForUserTaskAndGetIt(processInstance1, "step1"); assertEquals("name1", userTask.getDisplayName()); // do the same thing a second time final BusinessArchiveBuilder builder2 = new BusinessArchiveBuilder().createNewBusinessArchive(); builder2.addClasspathResource(getResource("/custom-0.1.jar.bak", "custom-0.1.jar")); final ProcessDefinitionBuilder designProcessDefinition2 = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithCustomData", "1.1"); designProcessDefinition2.addData("adress", "org.bonitasoft.custom.Address", new ExpressionBuilder().createGroovyScriptExpression("create adress", "new org.bonitasoft.custom.Address(\"name1\",\"Rue ampère\",\"38000\",\"Grenoble\",\"France\")", "org.bonitasoft.custom.Address")); designProcessDefinition2 .addActor(ACTOR_NAME) .addUserTask("step1", ACTOR_NAME) .addDisplayName( new ExpressionBuilder().createJavaMethodCallExpression("getNameOfAdress", "getName", String.class.getName(), new ExpressionBuilder().createDataExpression("adress", "org.bonitasoft.custom.Address"))); designProcessDefinition2.addAutomaticTask("start").addTransition("start", "step1"); builder2.setProcessDefinition(designProcessDefinition2.done()); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(builder2.done(), ACTOR_NAME, user); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId()); final ActivityInstance userTask2 = waitForUserTaskAndGetIt(processInstance2, "step1"); assertEquals("name1", userTask2.getDisplayName()); final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition1.getId()); final ActivityInstance userTask3 = waitForUserTaskAndGetIt(processInstance3, "step1"); assertEquals("name1", userTask3.getDisplayName()); disableAndDeleteProcess(processDefinition1); disableAndDeleteProcess(processDefinition2); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/StartProcessWithOperationsIT.java ================================================ /** * 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.process; import static org.assertj.core.api.Assertions.assertThat; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.operation.OperationBuilder; import org.junit.Test; /** * @author Baptiste Mesta */ public class StartProcessWithOperationsIT extends TestWithUser { @Test public void startProcessWithJavaOperations() throws Exception { ProcessDefinitionBuilder processWithOps = new ProcessDefinitionBuilder().createNewInstance("processWithOps", "1.0"); User john = createUser("john", "bpm"); processWithOps.addActor("actor"); processWithOps.addData("data1", ArrayList.class.getName(), new ExpressionBuilder().createGroovyScriptExpression( "createList", "new java.util.ArrayList()", ArrayList.class.getName())); processWithOps.addUserTask("step1", "actor"); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processWithOps.done(), "actor", john); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId(), Arrays.asList( new OperationBuilder().createJavaMethodOperation("data1", "add", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("listValue"))), new HashMap()); waitForUserTask("step1"); DataInstance data1 = getProcessAPI().getProcessDataInstance("data1", processInstance.getId()); assertThat(((List) data1.getValue()).get(0)).isEqualTo("listValue"); disableAndDeleteProcess(processDefinition); deleteUser(john); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/actor/ExportActorMappingIT.java ================================================ /** * 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.process.actor; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import java.io.InputStream; import java.util.List; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.actor.ActorCriterion; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.junit.Test; /** * @author Matthieu Chaffotte */ public class ExportActorMappingIT extends TestWithUser { @Test public void exportSimpleActorMapping() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("exportProcess", "1.0"); processBuilder.addActor(ACTOR_NAME).addDescription("Delivery all day and night long").addUserTask("userTask1", ACTOR_NAME); final ProcessDefinition definition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); final String xmlContent = getProcessAPI().exportActorMapping(definition.getId()); final InputStream xmlStream = ImportActorMappingIT.class.getResourceAsStream("simpleActorMapping.xml"); try { final byte[] actormapping = IOUtils.toByteArray(xmlStream); assertThatXmlHaveNoDifferences(new String(actormapping), xmlContent); } finally { xmlStream.close(); } disableAndDeleteProcess(definition); } @Test public void exportActorMappingWithDeletedGroup() throws Exception { final Group sales = createGroup("sales"); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("exportProcess", "1.0"); processBuilder.addActor(ACTOR_NAME).addDescription("Delivery all day and night long").addUserTask("userTask1", ACTOR_NAME); final ProcessDefinition definition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, sales); deleteGroups(sales); // should work final String xmlContent = getProcessAPI().exportActorMapping(definition.getId()); disableAndDeleteProcess(definition); assertFalse(xmlContent.contains("") || xmlContent.contains("")); } @Test public void exportActorMappingWithDeletedUser() throws Exception { final User john = createUser("john", "bpm"); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("exportProcess", "1.0"); processBuilder.addActor(ACTOR_NAME).addDescription("Delivery all day and night long").addUserTask("userTask1", ACTOR_NAME); final ProcessDefinition definition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, john); deleteUsers(john); // should work final String xmlContent = getProcessAPI().exportActorMapping(definition.getId()); disableAndDeleteProcess(definition); assertFalse(xmlContent.contains("") || xmlContent.contains("john")); } @Test public void exportActorMappingWithDeletedMembership() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("exportProcess", "1.0"); processBuilder.addActor(ACTOR_NAME).addDescription("Delivery all day and night long").addUserTask("userTask1", ACTOR_NAME); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); loginWithTechnicalUser(); final Group group = createGroup("group"); final Role role = createRole("role"); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final List actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC); assertEquals(1, actors.size()); final ActorInstance actor = actors.get(0); getProcessAPI().addRoleAndGroupToActor(actor.getId(), role.getId(), group.getId()); deleteRoles(role); // should work final String xmlContent = getProcessAPI().exportActorMapping(definition.getId()); deleteProcess(definition); assertFalse(xmlContent.contains("") || xmlContent.contains("")); deleteGroups(group); } @Test public void exportActorMappingWithDeletedSubGroup() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("exportProcess", "1.0"); processBuilder.addActor(ACTOR_NAME).addDescription("Delivery all day and night long").addUserTask("userTask1", ACTOR_NAME); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); loginWithTechnicalUser(); final Group sales = createGroup("sales"); final Group subSales = createGroup("sub", "/sales"); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final List actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC); assertEquals(1, actors.size()); final ActorInstance actor = actors.get(0); getProcessAPI().addGroupToActor(actor.getId(), subSales.getId()); deleteGroups(sales); // should work final String xmlContent = getProcessAPI().exportActorMapping(definition.getId()); deleteProcess(definition); assertFalse(xmlContent, xmlContent.contains("") || xmlContent.contains("")); } @Test public void exportActorMappingWithDeletedRole() throws Exception { final Role sales = createRole("sales"); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("exportProcess", "1.0"); processBuilder.addActor(ACTOR_NAME).addDescription("Delivery all day and night long").addUserTask("userTask1", ACTOR_NAME); final ProcessDefinition definition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); deleteRoles(sales); // should work final String xmlContent = getProcessAPI().exportActorMapping(definition.getId()); disableAndDeleteProcess(definition); assertFalse(xmlContent.contains("") || xmlContent.contains("")); } @Test public void exportcomplexActorMapping() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("exportProcess", "1.0"); processBuilder.addActor(ACTOR_NAME).addDescription("Delivery all day and night long").addUserTask("userTask1", ACTOR_NAME); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); loginWithTechnicalUser(); final User john = createUser("john", "bpm"); final Group rd = createGroup("RD"); final Role role = createRole("dev"); // final UserMembership createUserMembership = createUserMembership(johnName, "RD", "dev"); final ProcessDefinition definition = getProcessAPI().deploy(businessArchive.done()); final List actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC); assertEquals(1, actors.size()); final ActorInstance actor = actors.get(0); assertEquals(ACTOR_NAME, actor.getName()); getProcessAPI().addUserToActor(actor.getId(), john.getId()); getProcessAPI().addGroupToActor(actor.getId(), rd.getId()); getProcessAPI().addRoleToActor(actor.getId(), role.getId()); getProcessAPI().addRoleAndGroupToActor(actor.getId(), role.getId(), rd.getId()); final String xmlContent = getProcessAPI().exportActorMapping(definition.getId()); final InputStream xmlStream = ImportActorMappingIT.class.getResourceAsStream("complexActorMapping.xml"); try { final byte[] actormapping = IOUtils.toByteArray(xmlStream); assertThatXmlHaveNoDifferences(new String(actormapping), xmlContent); } finally { xmlStream.close(); } getIdentityAPI().deleteGroup(rd.getId()); getIdentityAPI().deleteRole(role.getId()); deleteUser(john); deleteProcess(definition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/actor/ImportActorMappingIT.java ================================================ /** * 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.process.actor; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.io.InputStream; import java.util.List; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.actor.ActorCriterion; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.actor.ActorMappingImportException; import org.bonitasoft.engine.bpm.actor.ActorMember; import org.bonitasoft.engine.bpm.bar.ActorMappingMarshaller; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.bpm.process.ProcessActivationException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.junit.After; import org.junit.Assert; import org.junit.Test; /** * @author Matthieu Chaffotte */ public class ImportActorMappingIT extends TestWithTechnicalUser { private ProcessDefinition processDefinition; @Override @After public void after() throws Exception { try { getProcessAPI().disableProcess(processDefinition.getId()); } catch (final ProcessActivationException e) { // Do nothing. Process already disabled } deleteProcess(processDefinition); getIdentityAPI().deleteOrganization(); super.after(); } @Test public void importSimpleActorMapping() throws Exception { final User john = createUser(USERNAME, PASSWORD); final BusinessArchiveBuilder businessArchive = createAndDeployProcessDefinitionWithImportedActorMapping( "simpleActorMapping.xml"); processDefinition = deployProcess(businessArchive.done()); getProcessAPI().enableProcess(processDefinition.getId()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "userTask1"); waitForPendingTasks(john.getId(), 1); final List tasks = getProcessAPI().getPendingHumanTaskInstances(john.getId(), 0, 10, null); assertEquals(1, tasks.size()); deleteUser(john); } @Test public void importComplexActorMapping() throws Exception { final BusinessArchiveBuilder businessArchive = createAndDeployProcessDefinitionWithImportedActorMapping( "complexActorMapping.xml"); final User john = createUser("john", "bpm"); final Group rd = createGroup("RD"); final Role role = createRole("dev"); processDefinition = deployProcess(businessArchive.done()); getProcessAPI().enableProcess(processDefinition.getId()); getAndCheckActors(john, rd, role, processDefinition); } @Test public void importActorMappingWithWrongXMLFileBeforeDeploy() throws Exception { final BusinessArchiveBuilder businessArchive = createAndDeployProcessDefinitionWithImportedActorMapping( "actorMappingWithException.xml"); checkProcessNotActivated(businessArchive); } @Test(expected = ActorMappingImportException.class) public void importActorMappingWithWrongXMLFileAfterDeploy() throws Exception { final User user = createUser("john", "bpm"); createProcessDefinitionAndCheckActorMappingImportException(user, "actorMappingWithException.xml"); } @Test public void importActorMappingWithUnknownUserBeforeDeploy() throws Exception { final BusinessArchiveBuilder businessArchive = createAndDeployProcessDefinitionWithImportedActorMapping( "simpleActorMapping.xml"); checkProcessNotActivated(businessArchive); } @Test(expected = ActorMappingImportException.class) public void importActorMappingWithUnknownUserAfterDeploy() throws Exception { final User user = createUser("paul", "bpm"); createProcessDefinitionAndCheckActorMappingImportException(user, "actorMappingWithException.xml"); } @Test public void importActorMappingWithUnknownGroupBeforeDeploy() throws Exception { final BusinessArchiveBuilder businessArchive = createAndDeployProcessDefinitionWithImportedActorMapping( "complexActorMapping.xml"); createUser("john", "bpm"); createGroup("RD1"); createRole("dev"); // even if missing group the process is resolved checkProcessActivated(businessArchive); } @Test(expected = ActorMappingImportException.class) public void importActorMappingWithUnknownGroupAfterDeploy() throws Exception { final User user = createUser("john", "bpm"); createGroup("RD1"); createRole("dev"); createProcessDefinitionAndCheckActorMappingImportException(user, "complexActorMapping.xml"); } @Test public void importActorMappingWithUnknownRoleBeforeDeploy() throws Exception { final BusinessArchiveBuilder businessArchive = createAndDeployProcessDefinitionWithImportedActorMapping( "complexActorMapping.xml"); createUser("john", "bpm"); createGroup("RD"); createRole("dev1"); // even if missing role the process is resolved checkProcessActivated(businessArchive); } @Test(expected = ActorMappingImportException.class) public void importActorMappingWithUnknownRoleAfterDeploy() throws Exception { final User user = createUser("john", "bpm"); createGroup("RD"); createRole("dev1"); createProcessDefinitionAndCheckActorMappingImportException(user, "complexActorMapping.xml"); } @Test public void importActorMappingWithUnknownMemberShipBeforeDeploy() throws Exception { createUser("john", "bpm"); createGroup("RD2"); createRole("dev"); final BusinessArchiveBuilder businessArchive = createAndDeployProcessDefinitionWithImportedActorMapping( "complexActorMappingWithUnkownGroup.xml"); // even if missing membership the process is resolved checkProcessActivated(businessArchive); } @Test(expected = ActorMappingImportException.class) public void importActorMappingWithUnknownMemberShipAfterDeploy() throws Exception { final User user = createUser("john", "bpm"); createGroup("RD2"); createRole("dev"); createProcessDefinitionAndCheckActorMappingImportException(user, "complexActorMappingWithUnkownGroup.xml"); } @Test public void importActorMapping() throws Exception { final User user = createUser("john", "bpm"); final Group rd = createGroup("RD"); final Role role = createRole("dev"); final ProcessDefinitionBuilder processBuilder = createProcessDefinitionBuilder(); processDefinition = deployAndEnableProcessWithActor(processBuilder.getProcess(), ACTOR_NAME, user); getProcessAPI().importActorMapping(processDefinition.getId(), xmlToByteArray("complexActorMapping2.xml")); getAndCheckActors(user, rd, role, processDefinition); } @Test public void importActorMappingComputesProcessResolution() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder(); builder.createNewInstance("resolve", "1.0").addActor("Leader", true).addUserTask("step1", "Leader"); final DesignProcessDefinition processDesignDefinition = builder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDesignDefinition).done(); processDefinition = deployProcess(businessArchive); ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()); Assert.assertEquals(ConfigurationState.UNRESOLVED, deploymentInfo.getConfigurationState()); // Let's resolve actor mapping by importing a valid file: final User user = createUser("john", "bpm"); getProcessAPI() .importActorMapping( processDefinition.getId(), "john" .getBytes()); deploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()); Assert.assertEquals(ConfigurationState.RESOLVED, deploymentInfo.getConfigurationState()); deleteUser(user); } /** * @param xmlFileName * @return ProcessDefinition * @throws Exception * @since 6.0 */ private BusinessArchiveBuilder createAndDeployProcessDefinitionWithImportedActorMapping(final String xmlFileName) throws Exception { final DesignProcessDefinition processDefinition = createProcessDefinitionBuilder().done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); ActorMapping actormapping = new ActorMappingMarshaller().deserializeFromXML(xmlToByteArray(xmlFileName)); if (actormapping != null) { businessArchive.setActorMapping(actormapping); } return businessArchive; } /** * @param xmlFileName * @return * @throws IOException * @since 6.0 */ private byte[] xmlToByteArray(final String xmlFileName) throws IOException { final InputStream xmlStream = ImportActorMappingIT.class.getResourceAsStream(xmlFileName); byte[] actormapping = null; try { actormapping = IOUtils.toByteArray(xmlStream); } finally { xmlStream.close(); } return actormapping; } /** * @return * @since 6.0 */ private ProcessDefinitionBuilder createProcessDefinitionBuilder() { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addActor(ACTOR_NAME).addDescription("Delivery all day and night long").addUserTask("userTask1", ACTOR_NAME); return processBuilder; } private void checkProcessNotActivated(final BusinessArchiveBuilder businessArchive) throws Exception { processDefinition = deployProcess(businessArchive.done()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState()); final List processResolutionProblems = getProcessAPI() .getProcessResolutionProblems(processDefinition.getId()); assertEquals(1, processResolutionProblems.size()); final Problem problem = processResolutionProblems.get(0); assertEquals("actor", problem.getResource()); } private void checkProcessActivated(final BusinessArchiveBuilder businessArchive) throws Exception { processDefinition = deployProcess(businessArchive.done()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ConfigurationState.RESOLVED, processDeploymentInfo.getConfigurationState()); } /** * @param user * @param xmlFileName * @throws Exception * @since 6.0 */ private void createProcessDefinitionAndCheckActorMappingImportException(final User user, final String xmlFileName) throws Exception { final ProcessDefinitionBuilder processBuilder = createProcessDefinitionBuilder(); processDefinition = deployAndEnableProcessWithActor(processBuilder.getProcess(), ACTOR_NAME, user); getProcessAPI().importActorMapping(processDefinition.getId(), xmlToByteArray(xmlFileName)); } /** * @param user * @param group * @param role * @param definition * @since 6.0 */ private void getAndCheckActors(final User user, final Group group, final Role role, final ProcessDefinition definition) { final List actors = getProcessAPI().getActors(definition.getId(), 0, 15, ActorCriterion.NAME_DESC); final ActorInstance actorInstance = actors.get(0); final List actorMembers = getProcessAPI().getActorMembers(actorInstance.getId(), 0, 15); assertEquals(4, actorMembers.size()); for (final ActorMember actorMember : actorMembers) { assertTrue(checkRole(actorMember, role.getId()) || checkGroup(actorMember, group.getId()) || checkUser(actorMember, user.getId()) || checkMembership(actorMember, role.getId(), group.getId())); } } private boolean checkMembership(final ActorMember actorMember, final long roleId, final long groupId) { return actorMember.getRoleId() == roleId && actorMember.getGroupId() == groupId; } private boolean checkUser(final ActorMember actorMember, final long userId) { return actorMember.getUserId() == userId; } private boolean checkGroup(final ActorMember actorMember, final long groupId) { return actorMember.getGroupId() == groupId && actorMember.getRoleId() <= 0; } private boolean checkRole(final ActorMember actorMember, final long roleId) { return actorMember.getRoleId() == roleId && actorMember.getGroupId() <= 0; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/actor/ProcessActorIT.java ================================================ /** * 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.process.actor; import static org.junit.Assert.*; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.actor.ActorCriterion; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.actor.ActorMember; import org.bonitasoft.engine.bpm.actor.ActorUpdater; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.connectors.VariableStorage; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Test; public class ProcessActorIT extends TestWithUser { @Override @After public void after() throws Exception { VariableStorage.clearAll(); super.after(); } @Test(expected = AlreadyExistsException.class) public void mapTwiceSameActorToAGroup() throws Exception { final ProcessDefinition definition = deployAndEnableProcessWithHumanTask(PROCESS_NAME, ACTOR_NAME, "deliver"); final ActorInstance actor = checkActors(ACTOR_NAME, definition); final Group ergo = createGroup("Ergonomists"); try { getProcessAPI().addGroupToActor(actor.getId(), ergo.getId()); getProcessAPI().addGroupToActor(actor.getId(), ergo.getId()); fail("This statement should never be reached"); } finally { deleteGroups(ergo); disableAndDeleteProcess(definition); } } @Test public void mapActorToAlreadyMappedParentGroupShouldBeHandledSilently() throws Exception { final ProcessDefinition definition = deployAndEnableProcessWithHumanTask(PROCESS_NAME, ACTOR_NAME, "deliver"); final ActorInstance actor = checkActors(ACTOR_NAME, definition); final Group all = createGroup("Everyone"); final Group ergo = createGroup("Ergonomists", all.getPath()); try { getProcessAPI().addGroupToActor(actor.getId(), ergo.getId()); getProcessAPI().addGroupToActor(actor.getId(), all.getId()); final List actorMembers = getProcessAPI().getActorMembers(actor.getId(), 0, 50); int nbGroupActorMembers = 0; for (final ActorMember actorMember : actorMembers) { if (actorMember.getGroupId() != -1 && actorMember.getRoleId() == -1) { nbGroupActorMembers++; } } assertEquals("All group / actor mapping should have been retrieved", 2, nbGroupActorMembers); } finally { deleteGroups(ergo); deleteGroups(all); disableAndDeleteProcess(definition); } } @Test public void mapActorToAGroupAndGroupPlusRoleIsValid() throws Exception { final ProcessDefinition definition = deployAndEnableProcessWithHumanTask(PROCESS_NAME, ACTOR_NAME, "deliver"); final ActorInstance actor = checkActors(ACTOR_NAME, definition); final Role role = createRole("Quality Manager"); final Group ergo = createGroup("Ergonomists"); try { final ActorMember addGroupToActor = getProcessAPI().addGroupToActor(actor.getId(), ergo.getId()); final ActorMember addRoleAndGroupToActor = getProcessAPI().addRoleAndGroupToActor(actor.getId(), role.getId(), ergo.getId()); final List actorMembers = getProcessAPI().getActorMembers(actor.getId(), 0, 50); assertTrue("All group / actor mapping should have been retrieved", actorMembers.containsAll(Arrays.asList(addGroupToActor, addRoleAndGroupToActor))); } finally { deleteGroups(ergo); deleteRoles(role); disableAndDeleteProcess(definition); } } @Test public void johnHasGotAPendingTask() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addUserTask("deliver", ACTOR_NAME); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final List actors = getProcessAPI().getActors(processDefinition.getId(), 0, 1, ActorCriterion.NAME_ASC); assertEquals(1, actors.size()); final ActorInstance actor = actors.get(0); assertEquals(ACTOR_NAME, actor.getName()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "deliver"); final List tasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, null); assertEquals(1, tasks.size()); // Clean up disableAndDeleteProcess(processDefinition); } @Test public void deployProcessWithActorMappingNotMatchingOrganization() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addUserTask("deliver", ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final BusinessArchiveBuilder barBuilder = new BusinessArchiveBuilder().createNewBusinessArchive(); barBuilder.setProcessDefinition(designProcessDefinition); final StringBuilder builder = new StringBuilder(); builder.append(""); builder.append( ""); builder.append("\t"); builder.append("\t\t"); builder.append("\t\t\t/unknown"); builder.append("\t\t"); builder.append("\t\t"); builder.append("\t\t\ttoto"); builder.append("\t\t"); builder.append("\t"); builder.append(""); barBuilder.setActorMapping(builder.toString().getBytes("UTF-8")); final BusinessArchive businessArchive = barBuilder.done(); final ProcessDefinition processDefinition = deployProcess(businessArchive); // process deployed but not activated // trying to add an actor on the process now final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState()); final List processResolutionProblems = getProcessAPI() .getProcessResolutionProblems(processDefinition.getId()); assertEquals(1, processResolutionProblems.size()); final Problem problem = processResolutionProblems.get(0); assertEquals("actor", problem.getResource()); final List actors = getProcessAPI().getActors(processDeploymentInfo.getProcessId(), 0, 50, ActorCriterion.NAME_ASC); getProcessAPI().addUserToActor(actors.get(0).getId(), user.getId()); getProcessAPI().enableProcess(processDeploymentInfo.getProcessId()); assertEquals(ActivationState.ENABLED, getProcessAPI().getProcessDeploymentInfo(processDeploymentInfo.getProcessId()).getActivationState()); // Clean up disableAndDeleteProcess(processDefinition); } @Test public void butJamesHasNoPendingTask() throws Exception { final User plop = createUser("plop", PASSWORD); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addUserTask("userTask1", ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(designProcessDefinition); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); getProcessAPI().startProcess(processDefinition.getId()); Thread.sleep(1000); final List tasks = getProcessAPI().getPendingHumanTaskInstances(plop.getId(), 0, 10, null); assertEquals(0, tasks.size()); // Clean up disableAndDeleteProcess(processDefinition); deleteUser(plop); } @Test public void eliasHasAssignedAndPendingUserTasks() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addUserTask("userTask1", ACTOR_NAME).addUserTask("userTask2", ACTOR_NAME) .addUserTask("userTask3", ACTOR_NAME); final DesignProcessDefinition processDefinition = processBuilder.done(); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); waitForUserTask(processInstance, "userTask1"); waitForUserTask(processInstance, "userTask2"); waitForUserTask(processInstance, "userTask3"); final List activities = getProcessAPI().getActivities(processInstance.getId(), 0, 10); // assign first 2 user tasks to user: getProcessAPI().assignUserTask(activities.get(0).getId(), user.getId()); getProcessAPI().assignUserTask(activities.get(1).getId(), user.getId()); final long myAssignedTasksNb = getProcessAPI().getNumberOfAssignedHumanTaskInstances(user.getId()); assertEquals(2L, myAssignedTasksNb); // there should remain 1 usertask not assigned: final long actorPendingTasksNb = getProcessAPI().getNumberOfPendingHumanTaskInstances(user.getId()); assertEquals(1L, actorPendingTasksNb); disableAndDeleteProcess(definition); } private ProcessDefinition preparationBeforeTest(final String actorName) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.setActorInitiator(actorName).addUserTask("userTask1", actorName); final DesignProcessDefinition processDefinition = processBuilder.done(); final BusinessArchiveBuilder businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchive.setProcessDefinition(processDefinition); final File file = File.createTempFile("Actor", ".bar"); try { file.delete(); BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive.done(), file); final BusinessArchive archive = BusinessArchiveFactory.readBusinessArchive(file); return deployAndEnableProcessWithActor(archive.getProcessDefinition(), actorName, user); } finally { file.delete(); } } @Test public void getActor() throws Exception { final ProcessDefinition definition = preparationBeforeTest(ACTOR_NAME); // Get the existing process def actor list: final List actors = getProcessAPI().getActors(definition.getId(), 0, 10, ActorCriterion.NAME_ASC); final ActorInstance actor = actors.get(0); assertEquals(ACTOR_NAME, actor.getName()); assertEquals(definition.getId(), actor.getProcessDefinitionId()); disableAndDeleteProcess(definition); } @Test public void getActors() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); final List actorNameList = BuildTestUtil.buildActorAndDescription(processBuilder, 5); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.getProcess(), actorNameList, Arrays.asList(user, user, user, user, user)); // test ASC List actors = getProcessAPI().getActors(processDefinition.getId(), 0, 2, ActorCriterion.NAME_ASC); assertNotNull(actors); assertEquals(2, actors.size()); assertEquals(actorNameList.get(0), actors.get(0).getName()); assertEquals(actorNameList.get(1), actors.get(1).getName()); actors = getProcessAPI().getActors(processDefinition.getId(), 2, 2, ActorCriterion.NAME_ASC); assertNotNull(actors); assertEquals(2, actors.size()); assertEquals(actorNameList.get(2), actors.get(0).getName()); assertEquals(actorNameList.get(3), actors.get(1).getName()); actors = getProcessAPI().getActors(processDefinition.getId(), 4, 2, ActorCriterion.NAME_ASC); assertNotNull(actors); assertEquals(1, actors.size()); assertEquals(actorNameList.get(4), actors.get(0).getName()); // test PageOutOfRangeException actors = getProcessAPI().getActors(processDefinition.getId(), 6, 2, ActorCriterion.NAME_ASC); assertEquals(0, actors.size()); // test DESC actors = getProcessAPI().getActors(processDefinition.getId(), 0, 2, ActorCriterion.NAME_DESC); assertNotNull(actors); assertEquals(2, actors.size()); assertEquals(actorNameList.get(4), actors.get(0).getName()); assertEquals(actorNameList.get(3), actors.get(1).getName()); disableAndDeleteProcess(processDefinition); } @Test public void getActorsByIds() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addDescription("actor description1"); processBuilder.addActor("actor2").addDescription("actor description2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.getProcess(), Arrays.asList(ACTOR_NAME, "actor2"), Arrays.asList(user, user)); final List actors = getProcessAPI().getActors(processDefinition.getId(), 0, 5, null); assertNotNull(actors); assertEquals(2, actors.size()); final List actorIds = new ArrayList<>(); for (final ActorInstance actorInstance : actors) { actorIds.add(actorInstance.getId()); } final Map actorInstanceRes = getProcessAPI().getActorsFromActorIds(actorIds); assertNotNull(actorInstanceRes); assertEquals(2, actorInstanceRes.size()); boolean isHas1 = false; boolean isHas2 = false; for (final Entry et : actorInstanceRes.entrySet()) { if (ACTOR_NAME.equals(et.getValue().getName())) { isHas1 = true; } else { if ("actor2".equals(et.getValue().getName())) { isHas2 = true; } } } assertTrue(isHas1 && isHas2); disableAndDeleteProcess(processDefinition); } @Test public void updateActor() throws Exception { final ProcessDefinition definition = preparationBeforeTest(ACTOR_NAME); // Get the existing process def actor list: final List actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC); final ActorInstance actor = actors.get(0); final long actorId = actor.getId(); final ActorInstance actorRes = getProcessAPI().getActor(actorId); assertEquals(actorId, actorRes.getId()); assertEquals(ACTOR_NAME, actorRes.getName()); assertEquals(definition.getId(), actorRes.getProcessDefinitionId()); final String changedDescription = "It's okay!"; final String changedName = "ACTOR_NAME women"; final ActorUpdater descriptor = new ActorUpdater(); descriptor.setDescription(changedDescription); descriptor.setDisplayName(changedName); final ActorInstance actorUpdated = getProcessAPI().updateActor(actorId, descriptor); assertEquals(actorId, actorUpdated.getId()); assertEquals(ACTOR_NAME, actorUpdated.getName()); assertEquals(changedDescription, actorUpdated.getDescription()); assertEquals(changedName, actorUpdated.getDisplayName()); assertEquals(definition.getId(), actorUpdated.getProcessDefinitionId()); disableAndDeleteProcess(definition); } @Test(expected = UpdateException.class) public void updateActorWithEmptyDescriptor() throws Exception { final ActorUpdater descriptor = new ActorUpdater(); getProcessAPI().updateActor(1L, descriptor); } @Test(expected = UpdateException.class) public void updateActorWithNoDescriptor() throws Exception { getProcessAPI().updateActor(1L, null); } @Test public void addActorMembers() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addUserTask("userTask1", ACTOR_NAME); final DesignProcessDefinition processDefinition = processBuilder.done(); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(definition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final List actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC); assertEquals(1, actors.size()); final ActorInstance actor = actors.get(0); assertEquals(ACTOR_NAME, actor.getName()); assertEquals(1L, getProcessAPI().getNumberOfActorMembers(actor.getId())); final List actorMembers = getProcessAPI().getActorMembers(actor.getId(), 0, 10); assertEquals(1, actorMembers.size()); final ActorMember actorMember = actorMembers.get(0); assertEquals(user.getId(), actorMember.getUserId()); assertEquals(-1, actorMember.getGroupId()); assertEquals(-1, actorMember.getRoleId()); getProcessAPI().removeActorMember(actorMember.getId()); disableAndDeleteProcess(definition); } @Test(expected = AlreadyExistsException.class) public void cantAddTwiceUserToActor() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addUserTask("userTask1", ACTOR_NAME); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder.done()) .done()); getProcessAPI().addUserToActor(ACTOR_NAME, processDefinition, user.getId()); getProcessAPI().enableProcess(processDefinition.getId()); assertEquals(ActivationState.ENABLED, getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()).getActivationState()); try { getProcessAPI().addUserToActor(ACTOR_NAME, processDefinition, user.getId()); } finally { disableAndDeleteProcess(processDefinition); } } @Test(expected = AlreadyExistsException.class) public void cantAddTwiceRoleToActor() throws Exception { final Role role = createRole("Quality Manager"); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addUserTask("userTask1", ACTOR_NAME); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder.done()) .done()); getProcessAPI().addRoleToActor(ACTOR_NAME, processDefinition, role.getId()); getProcessAPI().enableProcess(processDefinition.getId()); assertEquals(ActivationState.ENABLED, getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()).getActivationState()); try { getProcessAPI().addRoleToActor(ACTOR_NAME, processDefinition, role.getId()); } finally { deleteRoles(role); disableAndDeleteProcess(processDefinition); } } @Test(expected = AlreadyExistsException.class) public void cantAddTwiceGroupToActor() throws Exception { final Group group = createGroup("Ergonomists"); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addUserTask("userTask1", ACTOR_NAME); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder.done()) .done()); getProcessAPI().addGroupToActor(ACTOR_NAME, group.getId(), processDefinition); getProcessAPI().enableProcess(processDefinition.getId()); assertEquals(ActivationState.ENABLED, getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()).getActivationState()); try { getProcessAPI().addGroupToActor(ACTOR_NAME, group.getId(), processDefinition); } finally { deleteGroups(group); disableAndDeleteProcess(processDefinition); } } @Test(expected = AlreadyExistsException.class) public void cantAddTwiceMembershipToActor() throws Exception { final Role role = createRole("Quality Manager"); final Group group = createGroup("Ergonomists"); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addUserTask("userTask1", ACTOR_NAME); final ProcessDefinition processDefinition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder.done()) .done()); getProcessAPI().addRoleAndGroupToActor(ACTOR_NAME, processDefinition, role.getId(), group.getId()); getProcessAPI().enableProcess(processDefinition.getId()); assertEquals(ActivationState.ENABLED, getProcessAPI().getProcessDeploymentInfo(processDefinition.getId()).getActivationState()); try { getProcessAPI().addRoleAndGroupToActor(ACTOR_NAME, processDefinition, role.getId(), group.getId()); } finally { deleteGroups(group); deleteRoles(role); disableAndDeleteProcess(processDefinition); } } @Test public void getNumberOfUsersOfActor() throws Exception { final User user1 = createUser("user1", "bpm"); final User user2 = createUser("user2", "bpm"); final User user3 = createUser("user3", "bpm"); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addUserTask("userTask1", ACTOR_NAME); final ProcessDefinition definition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder.done()) .done()); getProcessAPI().addUserToActor(ACTOR_NAME, definition, user1.getId()); getProcessAPI().addUserToActor(ACTOR_NAME, definition, user2.getId()); getProcessAPI().enableProcess(definition.getId()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(definition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // Retrieve actor final List actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC); assertEquals(1, actors.size()); final ActorInstance actor = actors.get(0); assertEquals(ACTOR_NAME, actor.getName()); // Check number of users mapped to actor final long result = getProcessAPI().getNumberOfUsersOfActor(actor.getId()); assertEquals(2, result); // Retrieve actor members to clean database final List actorMembers = getProcessAPI().getActorMembers(actor.getId(), 0, 10); getProcessAPI().removeActorMember(actorMembers.get(0).getId()); getProcessAPI().removeActorMember(actorMembers.get(1).getId()); disableAndDeleteProcess(definition); deleteUser(user1); deleteUser(user2); deleteUser(user3); } @Test public void getNumberOfRolesOfActor() throws Exception { final Role role1 = createRole("role1"); final Role role2 = createRole("role2"); final Role role3 = createRole("role3"); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addUserTask("roleTask1", ACTOR_NAME); final ProcessDefinition definition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder.done()) .done()); addMappingOfActorsForRole(ACTOR_NAME, role1.getId(), definition); addMappingOfActorsForRole(ACTOR_NAME, role2.getId(), definition); getProcessAPI().enableProcess(definition.getId()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(definition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // Retrieve actor final List actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC); assertEquals(1, actors.size()); final ActorInstance actor = actors.get(0); assertEquals(ACTOR_NAME, actor.getName()); // Check number of roles mapped to actor final long result = getProcessAPI().getNumberOfRolesOfActor(actor.getId()); assertEquals(2, result); // Retrieve actor members to clean database final List actorMembers = getProcessAPI().getActorMembers(actor.getId(), 0, 10); getProcessAPI().removeActorMember(actorMembers.get(0).getId()); getProcessAPI().removeActorMember(actorMembers.get(1).getId()); disableAndDeleteProcess(definition); deleteRoles(role1, role2, role3); } @Test public void getNumberOfGroupsOfActor() throws Exception { final Group group1 = createGroup("group1"); final Group group2 = createGroup("group2"); final Group group3 = createGroup("group3"); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addUserTask("groupTask1", ACTOR_NAME); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, group1, group2); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // Retrieve actor final List actors = getProcessAPI().getActors(processDefinition.getId(), 0, 1, ActorCriterion.NAME_ASC); assertEquals(1, actors.size()); final ActorInstance actor = actors.get(0); assertEquals(ACTOR_NAME, actor.getName()); // Check number of groups mapped to actor final long result = getProcessAPI().getNumberOfGroupsOfActor(actor.getId()); assertEquals(2, result); // Retrieve actor members to clean database final List actorMembers = getProcessAPI().getActorMembers(actor.getId(), 0, 10); getProcessAPI().removeActorMember(actorMembers.get(0).getId()); getProcessAPI().removeActorMember(actorMembers.get(1).getId()); disableAndDeleteProcess(processDefinition); deleteGroups(group1, group2, group3); } @Test public void getNumberOfMembershipsOfActor() throws Exception { final User user1 = createUser("user1", "bpm"); final User user2 = createUser("user2", "bpm"); final Group group1 = createGroup("group1"); final Group group2 = createGroup("group2"); final Role role1 = createRole("role1"); final Role role2 = createRole("role2"); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addUserTask("userMembershipTask1", ACTOR_NAME); final ProcessDefinition definition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(processBuilder.done()) .done()); getProcessAPI().addUserToActor(ACTOR_NAME, definition, user1.getId()); addMappingOfActorsForRoleAndGroup(ACTOR_NAME, user1.getId(), group1.getId(), definition); addMappingOfActorsForRoleAndGroup(ACTOR_NAME, user1.getId(), group2.getId(), definition); getProcessAPI().enableProcess(definition.getId()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(definition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // Retrieve actor final List actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC); assertEquals(1, actors.size()); final ActorInstance actor = actors.get(0); assertEquals(ACTOR_NAME, actor.getName()); // Check number of userMemberships mapped to actor final long result = getProcessAPI().getNumberOfMembershipsOfActor(actor.getId()); assertEquals(2, result); // Retrieve actor members to clean database final List actorMembers = getProcessAPI().getActorMembers(actor.getId(), 0, 10); getProcessAPI().removeActorMember(actorMembers.get(0).getId()); getProcessAPI().removeActorMember(actorMembers.get(1).getId()); disableAndDeleteProcess(definition); deleteUsers(user1, user2); deleteRoles(role1, role2); deleteGroups(group1, group2); } @Test public void getStartableProcessesForActors() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); processBuilder.setActorInitiator(ACTOR_NAME).addUserTask("userTask1", ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ActorInstance actorInstance = getProcessAPI().getActorInitiator(processDefinition.getId()); final Set actorIds = new HashSet<>(); actorIds.add(actorInstance.getId()); final List processDeploymentInfos = getProcessAPI() .getStartableProcessDeploymentInfosForActors(actorIds, 0, 10, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, processDeploymentInfos.size()); assertEquals(processDefinition.getId(), processDeploymentInfos.get(0).getProcessId()); disableAndDeleteProcess(processDefinition); } @Test public void testIsAllowedToStartProcess() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); processBuilder.setActorInitiator(ACTOR_NAME).addUserTask("userTask1", ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ActorInstance actorInstance = getProcessAPI().getActorInitiator(processDefinition.getId()); final Set actorIds = new HashSet<>(); actorIds.add(actorInstance.getId()); final boolean isAllowedToStartProcess = getProcessAPI().isAllowedToStartProcess(processDefinition.getId(), actorIds); assertEquals(true, isAllowedToStartProcess); disableAndDeleteProcess(processDefinition); } private ProcessDefinition deployAndEnableProcessWithHumanTask(final String processName, final String actorName, final String userTaskName) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder(); processBuilder.createNewInstance(processName, PROCESS_VERSION); processBuilder.addActor(actorName); processBuilder.addStartEvent("startEvent"); processBuilder.addUserTask(userTaskName, actorName); processBuilder.addEndEvent("endEvent"); processBuilder.addTransition("startEvent", userTaskName); processBuilder.addTransition(userTaskName, "endEvent"); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final ProcessDefinition processDef = deployAndEnableProcessWithActor(designProcessDefinition, actorName, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDef.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); return processDef; } @Test public void userHasGotAPendingTaskFromGroup() throws Exception { final ProcessDefinition definition = deployAndEnableProcessWithHumanTask(PROCESS_NAME, ACTOR_NAME, "deliver"); final ActorInstance actor = checkActors(ACTOR_NAME, definition); final Group group = getIdentityAPI().createGroup("group1", null); final Role role = getIdentityAPI().createRole("role1"); getIdentityAPI().addUserMembership(user.getId(), group.getId(), role.getId()); getProcessAPI().addGroupToActor(actor.getId(), group.getId()); getProcessAPI().startProcess(definition.getId()); waitForUserTask("deliver"); final List tasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, null); assertEquals(1, tasks.size()); cleanUserGroupAndRole(group, role); disableAndDeleteProcess(definition); } @Test public void mapActorToASubGroup() throws Exception { final ProcessDefinition definition = deployAndEnableProcessWithHumanTask(PROCESS_NAME, ACTOR_NAME, "deliver"); final ActorInstance actor = checkActors(ACTOR_NAME, definition); final Group parent = createGroup("parent"); final Group sub = createGroup("sub", "/parent"); final Role role = getIdentityAPI().createRole("role1"); getIdentityAPI().addUserMembership(user.getId(), sub.getId(), role.getId()); getProcessAPI().addGroupToActor(actor.getId(), parent.getId()); getProcessAPI().startProcess(definition.getId()); waitForPendingTasks(user.getId(), 1);// should have 1 task because john is in the parent deleteGroups(parent); deleteRoles(role); disableAndDeleteProcess(definition); } @Test public void userHasGotAPendingTaskFromRole() throws Exception { final ProcessDefinition definition = deployAndEnableProcessWithHumanTask(PROCESS_NAME, ACTOR_NAME, "deliver"); final ActorInstance actor = checkActors(ACTOR_NAME, definition); final Group group = getIdentityAPI().createGroup("group1", null); final Role role = getIdentityAPI().createRole("role1"); getIdentityAPI().addUserMembership(user.getId(), group.getId(), role.getId()); getProcessAPI().addRoleToActor(actor.getId(), role.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); waitForUserTask(processInstance, "deliver"); final List tasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, null); assertEquals(1, tasks.size()); cleanUserGroupAndRole(group, role); disableAndDeleteProcess(definition); } @Test public void userHasGotAPendingTaskFromRoleAndGroup() throws Exception { final ProcessDefinition definition = deployAndEnableProcessWithHumanTask(PROCESS_NAME, ACTOR_NAME, "deliver"); final ActorInstance actor = checkActors(ACTOR_NAME, definition); final Group group = getIdentityAPI().createGroup("group1", null); final Role role = getIdentityAPI().createRole("role1"); getIdentityAPI().addUserMembership(user.getId(), group.getId(), role.getId()); getProcessAPI().addRoleAndGroupToActor(actor.getId(), role.getId(), group.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); waitForUserTask(processInstance, "deliver"); final List tasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, null); assertEquals(1, tasks.size()); cleanUserGroupAndRole(group, role); disableAndDeleteProcess(definition); } @Test public void userDontGetAPendingTaskFromRoleOrGroupIfRoleAndGroupNeeded() throws Exception { final String actorName = "ACTOR_NAME men"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder(); processBuilder.createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(actorName); processBuilder.addStartEvent("startEvent"); processBuilder.addUserTask("deliver", actorName); processBuilder.addEndEvent("endEvent"); processBuilder.addTransition("startEvent", "deliver"); processBuilder.addTransition("deliver", "endEvent"); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); final ProcessDefinition definition = deployProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition) .done()); final ActorInstance actor = checkActors(actorName, definition); final Group group1 = getIdentityAPI().createGroup("group1", null); final Group group2 = getIdentityAPI().createGroup("group2", null); final Role role1 = getIdentityAPI().createRole("role1"); final Role role2 = getIdentityAPI().createRole("role2"); getIdentityAPI().addUserMembership(user.getId(), group1.getId(), role1.getId()); getProcessAPI().addRoleAndGroupToActor(actor.getId(), role1.getId(), group2.getId()); getProcessAPI().addRoleAndGroupToActor(actor.getId(), role2.getId(), group1.getId()); getProcessAPI().enableProcess(definition.getId()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(definition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); waitForUserTask(processInstance, "deliver"); final List tasks = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, null); assertEquals(0, tasks.size()); cleanUserGroupAndRole(group1, role1); getIdentityAPI().deleteGroup(group2.getId()); getIdentityAPI().deleteRole(role2.getId()); disableAndDeleteProcess(definition); } @Test public void getNumberOfActors() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); final List actorNames = BuildTestUtil.buildActorAndDescription(processBuilder, 2); processBuilder.addActor("initiator", true); // Process def : one starting automatic activity that fires 3 human activities: final DesignProcessDefinition designProcessDefinition = processBuilder.addAutomaticTask("step1") .addUserTask("step2", actorNames.get(0)) .addUserTask("step3", actorNames.get(0)).addUserTask("step4", actorNames.get(0)) .addTransition("step1", "step2") .addTransition("step1", "step3").addTransition("step1", "step4").getProcess(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition).done(); final ProcessDefinition processDefinition = deployProcess(businessArchive); int nbOfActors = getProcessAPI().getNumberOfActors(processDefinition.getId()); assertEquals(3, nbOfActors); final List actors = getProcessAPI().getActors(processDefinition.getId(), 0, 5, ActorCriterion.NAME_ASC); assertEquals(3, actors.size()); for (final ActorInstance actor : actors) { getProcessAPI().addUserToActor(actor.getId(), user.getId()); } getProcessAPI().enableProcess(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); nbOfActors = getProcessAPI().getNumberOfActors(processDefinition.getId()); assertEquals(3, nbOfActors); waitForUserTask(processInstance, "step2"); waitForUserTask(processInstance, "step3"); waitForUserTask(processInstance, "step4"); nbOfActors = getProcessAPI().getNumberOfActors(processDefinition.getId()); assertEquals(3, nbOfActors); // clean all data for test disableAndDeleteProcess(processDefinition); } private ActorInstance checkActors(final String ACTOR_NAME, final ProcessDefinition definition) { final List actors = getProcessAPI().getActors(definition.getId(), 0, 1, ActorCriterion.NAME_ASC); assertEquals(1, actors.size()); final ActorInstance actor = actors.get(0); assertEquals(ACTOR_NAME, actor.getName()); return actor; } private void cleanUserGroupAndRole(final Group group, final Role role) throws DeletionException { getIdentityAPI().deleteGroup(group.getId()); getIdentityAPI().deleteRole(role.getId()); } @Test public void getPaginatedStartableProcessesForActors() throws Exception { final ProcessDefinition firstDefinition = getProcessDefinition(PROCESS_NAME); final ProcessDefinition secondDefinition = getProcessDefinition("secondProcess"); final ActorInstance firstActorInstance = getProcessAPI().getActorInitiator(firstDefinition.getId()); final ActorInstance secondActorInstance = getProcessAPI().getActorInitiator(secondDefinition.getId()); final Set actorIds = new HashSet<>(); actorIds.add(firstActorInstance.getId()); actorIds.add(secondActorInstance.getId()); final List processDeploymentInfos = getProcessAPI() .getStartableProcessDeploymentInfosForActors(actorIds, 0, 1, ProcessDeploymentInfoCriterion.NAME_ASC); assertEquals(1, processDeploymentInfos.size()); assertEquals(firstDefinition.getId(), processDeploymentInfos.get(0).getProcessId()); disableAndDeleteProcess(firstDefinition); disableAndDeleteProcess(secondDefinition); } private ProcessDefinition getProcessDefinition(final String processName) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(processName, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); processBuilder.setActorInitiator(ACTOR_NAME).addUserTask("userTask1", ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.done(); return deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/comment/CommentIT.java ================================================ /** * 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.process.comment; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.util.List; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.comment.ArchivedComment; import org.bonitasoft.engine.bpm.comment.ArchivedCommentsSearchDescriptor; import org.bonitasoft.engine.bpm.comment.Comment; import org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.Test; public class CommentIT extends TestWithUser { @Test public void should_be_able_to_add_search_and_archive_comments() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); String taskName = "userTask1"; final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(taskName, ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); // get comments before add new. final List originalComments = getProcessAPI().searchComments( new SearchOptionsBuilder(0, 100).filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, pi0.getId()) .done()) .getResult(); // add comments final String commentContent = "added comment 0"; final Comment comment = getProcessAPI().addProcessComment(pi0.getId(), commentContent); assertNotNull(comment); assertEquals(commentContent, comment.getContent()); getProcessAPI().addProcessComment(pi0.getId(), "added comment 1"); Comment comment2 = getProcessAPI().addProcessComment(pi0.getId(), "added comment 2"); // get comments again. final List commentsAfterAdd = getProcessAPI().searchComments( new SearchOptionsBuilder(0, 100) .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, pi0.getId()) .sort(SearchCommentsDescriptor.POSTDATE, Order.ASC) .done()) .getResult(); assertThat(commentsAfterAdd).as("should have 3 more comments").hasSize(originalComments.size() + 3); assertThat(commentsAfterAdd.get(1).getContent()).isEqualTo("added comment 1"); // Archive comments waitForUserTaskAndExecuteIt(pi0, taskName, user); waitForProcessToFinish(pi0); final SearchResult searchArchivedComments = getProcessAPI().searchArchivedComments( new SearchOptionsBuilder(0, 10) .filter(ArchivedCommentsSearchDescriptor.PROCESS_INSTANCE_ID, pi0.getId()).done()); assertThat(searchArchivedComments.getCount()) .as("At least 3 comments should have been retrieved (+ possible automatic comments)") .isGreaterThanOrEqualTo(3); // ensure we can retrieve archived comments using the SOURCE_OBJECT_ID field final SearchResult searchArchivedCommentsBySourceObjectId = getProcessAPI() .searchArchivedComments( new SearchOptionsBuilder(0, 30) .filter(ArchivedCommentsSearchDescriptor.SOURCE_OBJECT_ID, comment2.getId()).done()); assertThat(searchArchivedCommentsBySourceObjectId.getCount()).isEqualTo(1); assertThat(searchArchivedCommentsBySourceObjectId.getResult().get(0).getContent()).isEqualTo("added comment 2"); // ensure we can retrieve archived comments using the CONTENT field final SearchResult searchArchivedCommentsByContent = getProcessAPI().searchArchivedComments( new SearchOptionsBuilder(0, 30).filter(ArchivedCommentsSearchDescriptor.CONTENT, "added comment 2") .done()); assertThat(searchArchivedCommentsByContent.getCount()).isEqualTo(1); assertThat(searchArchivedCommentsByContent.getResult().get(0).getSourceObjectId()).isEqualTo(comment2.getId()); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/data/ActivityDataDefinitionIT.java ================================================ /** * 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.process.data; import static org.bonitasoft.engine.matchers.NameMatcher.nameIs; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import java.util.List; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.data.DataDefinition; import org.bonitasoft.engine.bpm.flownode.ActivityDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.junit.Test; public class ActivityDataDefinitionIT extends TestWithUser { @Test public void getDataDefinitionsHavingComplexeInitialValue() throws Exception { final Expression scriptExpression = new ExpressionBuilder().createGroovyScriptExpression("a+b", "a+b", String.class.getName(), new ExpressionBuilder().createDataExpression("a", String.class.getName()), new ExpressionBuilder().createDataExpression("b", String.class.getName())); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addShortTextData("a", new ExpressionBuilder().createConstantStringExpression("avalue")); processDefinitionBuilder.addShortTextData("b", new ExpressionBuilder().createConstantStringExpression("bvalue")); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME).addData("myData", String.class.getName(), scriptExpression); final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final List dataDefList = getProcessAPI().getActivityDataDefinitions(processDefinition.getId(), "step1", 0, 10); assertEquals(1, dataDefList.size()); assertEquals(2, dataDefList.get(0).getDefaultValueExpression().getDependencies().size()); disableAndDeleteProcess(processDefinition); } @Test public void getNumberOfActivityDataDefinitions() throws Exception { final String intDataName = "luckyNum"; final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10); final String strDataName = "luckyColor"; final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression("blue"); final String taskName = "autoTask1"; // process level final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); // activity level final AutomaticTaskDefinitionBuilder activityDefinitionBuilder = processDefinitionBuilder .addAutomaticTask(taskName); activityDefinitionBuilder.addIntegerData(intDataName, intDefaultExp); activityDefinitionBuilder.addShortTextData(strDataName, strDefaultExp); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); final int number = getProcessAPI().getNumberOfActivityDataDefinitions(processDefinition.getId(), taskName); assertEquals(2, number); disableAndDeleteProcess(processDefinition); } @Test(expected = ActivityDefinitionNotFoundException.class) public void getActivityDataDefinitionsWithException() throws Exception { final String intDataName = "luckyNum"; final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10); final String strDataName = "luckyColor"; final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression("blue"); final String taskName = "autoTask1"; // process level final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); // activity level final AutomaticTaskDefinitionBuilder activityDefinitionBuilder = processDefinitionBuilder .addAutomaticTask(taskName); activityDefinitionBuilder.addIntegerData(intDataName, intDefaultExp); activityDefinitionBuilder.addShortTextData(strDataName, strDefaultExp); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); try { getProcessAPI().getActivityDataDefinitions(processDefinition.getId(), taskName + "qwer", 0, 5); } finally { disableAndDeleteProcess(processDefinition); } } @Test public void getActivityDataDefinitions() throws Exception { final String intDataName = "luckyNum"; final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10); final String strDataName = "luckyColor"; final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression("blue"); final String taskName = "autoTask1"; // process level final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); // activity level final AutomaticTaskDefinitionBuilder activityDefinitionBuilder = processDefinitionBuilder .addAutomaticTask(taskName); activityDefinitionBuilder.addIntegerData(intDataName, intDefaultExp); activityDefinitionBuilder.addShortTextData(strDataName, strDefaultExp); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); final List dataDefList = getProcessAPI().getActivityDataDefinitions(processDefinition.getId(), taskName, 0, 5); assertEquals(2, dataDefList.size()); final DataDefinition dataDef1 = dataDefList.get(0); assertEquals(intDataName, dataDef1.getName()); assertEquals(Integer.class.getName(), dataDef1.getClassName()); // assertEquals(intDefaultExp,dataDef1.getDefaultValueExpression()); final DataDefinition dataDef2 = dataDefList.get(1); assertEquals(strDataName, dataDef2.getName()); assertEquals(String.class.getName(), dataDef2.getClassName()); // assertEquals(strDefaultExp,dataDef2.getDefaultValueExpression()); disableAndDeleteProcess(processDefinition); } @Test public void getActivityDataDefinitionsPaginated() throws Exception { final String intDataName = "luckyNum"; final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10); final String strDataName = "color"; final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression("blue"); final String taskName = "autoTask1"; // process level final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); // activity level final AutomaticTaskDefinitionBuilder activityDefinitionBuilder = processDefinitionBuilder .addAutomaticTask(taskName); activityDefinitionBuilder.addIntegerData(intDataName, intDefaultExp); for (int i = 1; i <= 10; i++) { activityDefinitionBuilder.addShortTextData(strDataName + i, strDefaultExp); } final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); List processDataDefinitions = getProcessAPI() .getActivityDataDefinitions(processDefinition.getId(), taskName, 0, 5); assertEquals(5, processDataDefinitions.size()); assertThat(processDataDefinitions.get(0), nameIs("luckyNum")); assertThat(processDataDefinitions.get(1), nameIs("color1")); assertThat(processDataDefinitions.get(2), nameIs("color2")); assertThat(processDataDefinitions.get(3), nameIs("color3")); assertThat(processDataDefinitions.get(4), nameIs("color4")); processDataDefinitions = getProcessAPI().getActivityDataDefinitions(processDefinition.getId(), taskName, 5, 5); assertEquals(5, processDataDefinitions.size()); assertThat(processDataDefinitions.get(0), nameIs("color5")); assertThat(processDataDefinitions.get(1), nameIs("color6")); assertThat(processDataDefinitions.get(2), nameIs("color7")); assertThat(processDataDefinitions.get(3), nameIs("color8")); assertThat(processDataDefinitions.get(4), nameIs("color9")); processDataDefinitions = getProcessAPI().getActivityDataDefinitions(processDefinition.getId(), taskName, 10, 5); assertEquals(1, processDataDefinitions.size()); assertThat(processDataDefinitions.get(0), nameIs("color10")); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/data/ActivityDataInstanceIT.java ================================================ /** * 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.process.data; import static org.junit.Assert.*; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.data.ArchivedDataInstance; import org.bonitasoft.engine.bpm.data.ArchivedDataNotFoundException; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.exception.ExceptionContext; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionType; import org.junit.Test; public class ActivityDataInstanceIT extends TestWithUser { @Test public void getDataFromActivity() throws Exception { final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance("My_Process", "1.0") .addActor(ACTOR_NAME) .addDescription("Delivery all day and night long").addUserTask("step1", ACTOR_NAME) .addIntegerData("var1", new ExpressionBuilder().createConstantIntegerExpression(1)).getProcess(); assertDataOnActivityInstances(processDef, 1); } @Test public void getDataFromActivityThatIsAlsoInParent() throws Exception { final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance("My_Process", "1.1") .addActor(ACTOR_NAME) .addDescription("Delivery all day and night long") .addIntegerData("var1", new ExpressionBuilder().createConstantIntegerExpression(1)) .addUserTask("step1", ACTOR_NAME) .addIntegerData("var1", new ExpressionBuilder().createConstantIntegerExpression(2)).getProcess(); assertDataOnActivityInstances(processDef, 2); } @Test public void getDataFromActivityThatIsOnlyInParent() throws Exception { final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance("My_Process", "1.1") .addActor(ACTOR_NAME) .addDescription("Delivery all day and night long") .addIntegerData("var1", new ExpressionBuilder().createConstantIntegerExpression(3)) .addUserTask("step1", ACTOR_NAME).getProcess(); assertDataOnActivityInstances(processDef, 3); } private void assertDataOnActivityInstances(final DesignProcessDefinition processDef, final int expectedNumber) throws Exception { final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, org.bonitasoft.engine.test.APITestUtil.ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // test execution final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final long step1Id = waitForUserTask(processInstance, "step1"); // verify there are data final List processDataInstances = getProcessAPI().getActivityDataInstances(step1Id, 0, 10); assertFalse(processDataInstances.isEmpty()); assertEquals(1, processDataInstances.size()); assertEquals("var1", processDataInstances.get(0).getName()); assertEquals(expectedNumber, processDataInstances.get(0).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void updateIntegerActivityDataInstance() throws Exception { updateActivityDataInstance(1, 2); } @Test public void updateBooleanActivityDataInstance() throws Exception { updateActivityDataInstance(false, true); } private void updateActivityDataInstance(final Serializable defaultDataValue, final Serializable updatedDataValue) throws Exception { final UserTaskDefinitionBuilder addUserTask = new ProcessDefinitionBuilder() .createNewInstance("My_Process", "1.0").addActor(ACTOR_NAME) .addDescription("Delivery all day and night long").addUserTask("step1", ACTOR_NAME); if (defaultDataValue instanceof Boolean) { addUserTask.addBooleanData("var1", new ExpressionBuilder().createConstantBooleanExpression((Boolean) defaultDataValue)); } else if (defaultDataValue instanceof Integer) { addUserTask.addIntegerData("var1", new ExpressionBuilder().createConstantIntegerExpression((Integer) defaultDataValue)); } else { throw new Exception("This test does not support data type different from (boolean, integer)"); } final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(addUserTask.getProcess(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long activityInstanceId = waitForUserTask(processInstance, "step1"); final DataInstance dataInstance = getActivityDataInstance(activityInstanceId); assertEquals("var1", dataInstance.getName()); assertEquals(defaultDataValue, dataInstance.getValue()); getProcessAPI().updateActivityDataInstance("var1", activityInstanceId, updatedDataValue); final DataInstance updatedData = getActivityDataInstance(activityInstanceId); assertEquals(updatedDataValue, updatedData.getValue()); disableAndDeleteProcess(processDefinition); } @Test public void updateActivityInstanceOperationsWithStringDataNotTransient() throws Exception { final DesignProcessDefinition designProcessDefinition = createProcessWithActorAndHumanTaskAndInitStringDataNotTransient(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final long activityInstanceId = waitForUserTask(processInstance, "step1"); // Update data instance final String updatedValue = "afterUpdate"; updateActivityInstanceVariablesWithOperations(updatedValue, activityInstanceId, "dataName", false); assertEquals(updatedValue, getProcessAPI().getActivityDataInstance("dataName", activityInstanceId).getValue()); // Clean disableAndDeleteProcess(processDefinition); } @Test public void updateActivityInstanceVariable() throws Exception { final DesignProcessDefinition processDef = createProcessWithActorAndHumanTaskAndInitStringDataNotTransient(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final long activityInstanceId = waitForUserTask(processInstance, "step1"); final String updatedValue = "afterUpdate"; final Map variables = new HashMap<>(2); variables.put("dataName", updatedValue); getProcessAPI().updateActivityInstanceVariables(activityInstanceId, variables); final DataInstance dataInstance = getProcessAPI().getActivityDataInstance("dataName", activityInstanceId); assertEquals(updatedValue, dataInstance.getValue()); disableAndDeleteProcess(processDefinition); } @Test(expected = UpdateException.class) public void cannotUpdateAnActivityInstanceVariable() throws Exception { final DesignProcessDefinition processDef = createProcessWithActorAndHumanTaskAndInitStringDataNotTransient(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final long activityInstanceId = waitForUserTask(processInstance, "step1"); final Map variables = new HashMap<>(2); variables.put("dataName1", "afterUpdate"); try { getProcessAPI().updateActivityInstanceVariables(activityInstanceId, variables); } finally { disableAndDeleteProcess(processDefinition); } } private DataInstance getActivityDataInstance(final long activityInstanceId) { final List activityDataInstances = getProcessAPI().getActivityDataInstances(activityInstanceId, 0, 10); assertEquals(1, activityDataInstances.size()); return activityDataInstances.get(0); } @Test(expected = RetrieveException.class) public void dataNotAvailableAfterArchiveFromActivity() throws Exception { final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance("My_Process", "1.0") .addActor(ACTOR_NAME) .addDescription("Delivery all day and night long").addUserTask("step1", ACTOR_NAME) .addIntegerData("var1", new ExpressionBuilder().createConstantIntegerExpression(1)).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // test execution final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final long step1Id = waitForUserTask(processInstance, "step1"); // verify the retrieved data List processDataInstances = getProcessAPI().getActivityDataInstances(step1Id, 0, 10); assertFalse(processDataInstances.isEmpty()); assertEquals(1, processDataInstances.size()); assertEquals("var1", processDataInstances.get(0).getName()); assertEquals(1, processDataInstances.get(0).getValue()); // Execute pending task final List activities = getProcessAPI().getActivities(processInstance.getId(), 0, 200); final ActivityInstance activityInstance = activities.iterator().next(); assignAndExecuteStep(activityInstance, user.getId()); waitForProcessToFinish(processInstance); // retrieve data after process has finished try { getProcessAPI().getActivityDataInstances(processInstance.getId(), 0, 10); } finally { disableAndDeleteProcess(processDefinition); } } @Test public void executeProcess3TimesWithNotInitializedData() throws Exception { final Expression dataExpr = new ExpressionBuilder().createDataExpression("booleanData", Boolean.class.getName()); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME).addBooleanData("booleanData", null); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME); processDefinitionBuilder.addTransition("step1", "step2", dataExpr).addDefaultTransition("step1", "step3"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); // Start first process, and wait the first step final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId()); final long step1_1Id = waitForUserTask(processInstance1, "step1"); // Set data to true, for the first instance getProcessAPI().updateActivityDataInstance("booleanData", step1_1Id, true); // Start second process, and wait the first step final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId()); final long step1_2Id = waitForUserTask(processInstance2, "step1"); // Set data to false, for the second instance getProcessAPI().updateActivityDataInstance("booleanData", step1_2Id, false); // Start third process, and wait the first step final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition.getId()); final long step1_3Id = waitForUserTask(processInstance3, "step1"); // Set data to true, for the third instance getProcessAPI().updateActivityDataInstance("booleanData", step1_3Id, true); // Execute all step1 assignAndExecuteStep(step1_1Id, user); waitForUserTask(processInstance1, "step2"); assignAndExecuteStep(step1_2Id, user); waitForUserTask(processInstance2, "step3"); assignAndExecuteStep(step1_3Id, user); waitForUserTask(processInstance3, "step2"); // Check that only these 3 steps are pending assertEquals(3, getProcessAPI().getNumberOfPendingHumanTaskInstances(user.getId())); // Clean-up disableAndDeleteProcess(processDefinition); } @Test public void runProcessWithInvalidInitialValue() throws Exception { final ProcessDefinitionBuilder createNewInstance = new ProcessDefinitionBuilder() .createNewInstance("processwithIntegerData", "1.0"); createNewInstance.addAutomaticTask("step1").addIntegerData("intdata", new ExpressionBuilder().createExpression("d", "d", Integer.class.getName(), ExpressionType.TYPE_CONSTANT)); final ProcessDefinition processDefinition = deployAndEnableProcess(createNewInstance.done()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final ActivityInstance failedTask = waitForTaskToFail(processInstance); assertEquals("step1", failedTask.getName()); disableAndDeleteProcess(processDefinition); } @Test public void processWithDataHavingSameNameInTwoContainer() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask("step1", ACTOR_NAME).addShortTextData("a", new ExpressionBuilder().createConstantStringExpression("step1")); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME).addShortTextData("a", new ExpressionBuilder().createConstantStringExpression("step2")); processDefinitionBuilder.addShortTextData("a", new ExpressionBuilder().createConstantStringExpression("process")); final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final HumanTaskInstance step1 = waitForUserTaskAndGetIt(processInstance, "step1"); assertEquals(step1.getName(), getProcessAPI().getActivityDataInstance("a", step1.getId()).getValue()); final HumanTaskInstance step2 = waitForUserTaskAndGetIt(processInstance, "step2"); assertEquals(step2.getName(), getProcessAPI().getActivityDataInstance("a", step2.getId()).getValue()); assertEquals("process", getProcessAPI().getProcessDataInstance("a", processInstance.getId()).getValue()); disableAndDeleteProcess(processDefinition); } private DesignProcessDefinition createProcessWithActorAndHumanTaskAndInitStringDataNotTransient() throws Exception { return createProcessWithActorAndHumanTaskAndStringData( new ExpressionBuilder().createConstantStringExpression("beforeUpdate")).done(); } private ProcessDefinitionBuilder createProcessWithActorAndHumanTaskAndStringData(final Expression defaultValue) { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME).addDescription("Delivery all day and night long"); final UserTaskDefinitionBuilder userTaskBuilder = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); userTaskBuilder.addShortTextData("dataName", defaultValue); return processDefinitionBuilder; } @Test public void canGetDataInstanceWhenThereAreTransientData() throws Exception { final String userTaskName = "task1"; final ProcessDefinition processDefinition = deployAndEnableProcWithPersistedAndTransientVariable(); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long userTaskId = waitForUserTask(processInstance, userTaskName); final Map> expressions = new HashMap<>(2); final Expression persistedVariableExpression = new ExpressionBuilder().createDataExpression("persistedVariable", String.class.getName()); final Expression transientVariableExpression = new ExpressionBuilder() .createTransientDataExpression("transientVariable", String.class.getName()); expressions.put(persistedVariableExpression, null); expressions.put(transientVariableExpression, null); final Map expressionResult = getProcessAPI() .evaluateExpressionsOnActivityInstance(userTaskId, expressions); assertEquals("default", expressionResult.get(persistedVariableExpression.getName())); assertEquals("default", expressionResult.get(transientVariableExpression.getName())); disableAndDeleteProcess(processDefinition.getId()); } @Test public void getArchivedActivityDataInstance() throws Exception { final String dataName = "title"; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("ProcessToArchive", "1.0"); builder.addActor(ACTOR_NAME); builder.addUserTask("step", ACTOR_NAME).addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression("1")); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long userTaskId = waitForUserTask(processInstance, "step"); Thread.sleep(10); getProcessAPI().updateActivityDataInstance(dataName, userTaskId, "2"); final ArchivedDataInstance archivedData = getProcessAPI().getArchivedActivityDataInstance(dataName, userTaskId); assertEquals("2", archivedData.getValue()); disableAndDeleteProcess(processDefinition); } @Test public void getArchivedActivityDataInstanceFromAnArchivedProcess() throws Exception { final String dataName = "title"; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("ProcessToArchive", "1.0"); builder.addActor(ACTOR_NAME); builder.addUserTask("step", ACTOR_NAME).addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression("1")); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long userTaskId = waitForUserTask(processInstance, "step"); Thread.sleep(10); // to be sure that the archive date is different from the initial state date getProcessAPI().updateActivityDataInstance(dataName, userTaskId, "2"); assignAndExecuteStep(userTaskId, user.getId()); waitForProcessToFinish(processInstance.getId()); final ArchivedDataInstance archivedData = getProcessAPI().getArchivedActivityDataInstance(dataName, userTaskId); assertEquals("2", archivedData.getValue()); disableAndDeleteProcess(processDefinition); } @Test public void getArchivedActivityDataInstances() throws Exception { final String dataName = "title"; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("ProcessToArchive", "1.0"); builder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression("0")); builder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder taskDefinitionBuilder = builder.addUserTask("step", ACTOR_NAME); taskDefinitionBuilder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression("1")); // overrides the process data with the same name taskDefinitionBuilder.addShortTextData("job", new ExpressionBuilder().createConstantStringExpression("job")); taskDefinitionBuilder.addShortTextData("desc", new ExpressionBuilder().createConstantStringExpression("desc")); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long userTaskId = waitForUserTask(processInstance, "step"); Thread.sleep(10); getProcessAPI().updateActivityDataInstance(dataName, userTaskId, "2"); assignAndExecuteStep(userTaskId, user); waitForProcessToFinish(processInstance.getId()); List archivedDataInstances = getProcessAPI().getArchivedActivityDataInstances(userTaskId, 0, 10); assertEquals(3, archivedDataInstances.size()); ArchivedDataInstance archivedDataInstance = getArchivedDataInstance(archivedDataInstances, dataName); assertEquals("2", archivedDataInstance.getValue()); archivedDataInstance = getArchivedDataInstance(archivedDataInstances, "job"); assertEquals("job", archivedDataInstance.getValue()); archivedDataInstance = getArchivedDataInstance(archivedDataInstances, "desc"); assertEquals("desc", archivedDataInstance.getValue()); archivedDataInstances = getProcessAPI().getArchivedActivityDataInstances(userTaskId, 0, 1); assertEquals(1, archivedDataInstances.size()); archivedDataInstance = getArchivedDataInstance(archivedDataInstances, dataName); assertEquals("2", archivedDataInstance.getValue()); archivedDataInstances = getProcessAPI().getArchivedActivityDataInstances(userTaskId, 1, 10); assertEquals(2, archivedDataInstances.size()); archivedDataInstance = getArchivedDataInstance(archivedDataInstances, "job"); assertEquals("job", archivedDataInstance.getValue()); archivedDataInstance = getArchivedDataInstance(archivedDataInstances, "desc"); assertEquals("desc", archivedDataInstance.getValue()); disableAndDeleteProcess(processDefinition); } @Test public void getUnknownArchivedActivityDataInstance() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("ProcessToArchive", "1.0"); builder.addActor(ACTOR_NAME); builder.addUserTask("step", ACTOR_NAME); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long userTaskId = waitForUserTask(processInstance, "step"); try { getProcessAPI().getArchivedProcessDataInstance("o", userTaskId); fail("The data named 'o' does not exists"); } catch (final ArchivedDataNotFoundException ignored) { // Do nothing } finally { disableAndDeleteProcess(processDefinition); } } private ProcessDefinition deployAndEnableProcWithPersistedAndTransientVariable() throws Exception { final String startName = "start"; final String endName = "end"; final Expression defaultValue = new ExpressionBuilder().createConstantStringExpression("default"); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("proc", "1.0"); builder.addActor(ACTOR_NAME); builder.addStartEvent(startName); final UserTaskDefinitionBuilder taskBuilder = builder.addUserTask("task1", ACTOR_NAME); taskBuilder.addShortTextData("persistedVariable", defaultValue); taskBuilder.addShortTextData("transientVariable", defaultValue).isTransient(); builder.addEndEvent(endName); builder.addTransition(startName, "task1"); builder.addTransition("task1", endName); return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); } @Test public void cantUpdateActivityDataInstanceWithWrongValue() throws Exception { final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance("My_Process", "1.0") .addActor(ACTOR_NAME) .addDescription("Delivery all day and night long") .addUserTask("step1", ACTOR_NAME).addData("data", List.class.getName(), null).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); // test execution final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); // verify the retrieved data try { getProcessAPI().updateActivityDataInstance("data", step1Id, "wrong value"); fail(); } catch (final UpdateException e) { assertEquals("USERNAME=" + USERNAME + " | DATA_NAME=data | DATA_CLASS_NAME=java.util.List | The type of new value [" + String.class.getName() + "] is not compatible with the type of the data.", e.getMessage()); final Map exceptionContext = e.getContext(); assertEquals(List.class.getName(), exceptionContext.get(ExceptionContext.DATA_CLASS_NAME)); assertEquals("data", exceptionContext.get(ExceptionContext.DATA_NAME)); } // retrieve data after the update final List activityDataInstances = getProcessAPI().getActivityDataInstances(step1Id, 0, 10); assertEquals(1, activityDataInstances.size()); assertNull(activityDataInstances.get(0).getValue()); // Evaluate the data final List dependencies = Collections .singletonList(new ExpressionBuilder().createDataExpression("data", List.class.getName())); final Expression longExpression = new ExpressionBuilder().createGroovyScriptExpression("Script", "data = new ArrayList(); data.add(\"plop\"); return data;", List.class.getName(), dependencies); final Map> expressions = Collections.singletonMap(longExpression, Collections.emptyMap()); getProcessAPI().evaluateExpressionsOnActivityInstance(step1Id, expressions); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/data/ProcessDataDefinitionIT.java ================================================ /** * 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.process.data; import static org.bonitasoft.engine.matchers.NameMatcher.nameIs; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import java.util.List; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.data.DataDefinition; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.junit.Test; public class ProcessDataDefinitionIT extends TestWithUser { @Test public void getProcessDataDefinitions() throws Exception { final String intDataName = "luckyNum"; final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10); final String strDataName = "luckyColor"; final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression("blue"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addIntegerData(intDataName, intDefaultExp); processDefinitionBuilder.addShortTextData(strDataName, strDefaultExp); final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final List dataDefList = getProcessAPI().getProcessDataDefinitions(processDefinition.getId(), 0, 5); assertEquals(2, dataDefList.size()); final DataDefinition dataDef1 = dataDefList.get(0); assertEquals(intDataName, dataDef1.getName()); assertEquals(Integer.class.getName(), dataDef1.getClassName()); // assertEquals(intDefaultExp,dataDef1.getDefaultValueExpression()); final DataDefinition dataDef2 = dataDefList.get(1); assertEquals(strDataName, dataDef2.getName()); assertEquals(String.class.getName(), dataDef2.getClassName()); // assertEquals(strDefaultExp,dataDef2.getDefaultValueExpression()); disableAndDeleteProcess(processDefinition); } @Test public void getProcessDataDefinitionsPaginated() throws Exception { final String intDataName = "luckyNum"; final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10); final String strDataName = "color"; final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression("blue"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addIntegerData(intDataName, intDefaultExp); for (int i = 1; i <= 10; i++) { processDefinitionBuilder.addShortTextData(strDataName + i, strDefaultExp); } final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); List processDataDefinitions = getProcessAPI() .getProcessDataDefinitions(processDefinition.getId(), 0, 5); assertEquals(5, processDataDefinitions.size()); assertThat(processDataDefinitions.get(0), nameIs("luckyNum")); assertThat(processDataDefinitions.get(1), nameIs("color1")); assertThat(processDataDefinitions.get(2), nameIs("color2")); assertThat(processDataDefinitions.get(3), nameIs("color3")); assertThat(processDataDefinitions.get(4), nameIs("color4")); processDataDefinitions = getProcessAPI().getProcessDataDefinitions(processDefinition.getId(), 5, 5); assertEquals(5, processDataDefinitions.size()); assertThat(processDataDefinitions.get(0), nameIs("color5")); assertThat(processDataDefinitions.get(1), nameIs("color6")); assertThat(processDataDefinitions.get(2), nameIs("color7")); assertThat(processDataDefinitions.get(3), nameIs("color8")); assertThat(processDataDefinitions.get(4), nameIs("color9")); processDataDefinitions = getProcessAPI().getProcessDataDefinitions(processDefinition.getId(), 10, 5); assertEquals(1, processDataDefinitions.size()); assertThat(processDataDefinitions.get(0), nameIs("color10")); disableAndDeleteProcess(processDefinition); } @Test public void getNumberOfProcessDataDefinitions() throws Exception { final String intDataName = "luckyNum"; final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10); final String strDataName = "luckyColor"; final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression("blue"); final DesignProcessDefinition designProcessDefinition; // process level final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addIntegerData(intDataName, intDefaultExp); processDefinitionBuilder.addShortTextData(strDataName, strDefaultExp); designProcessDefinition = processDefinitionBuilder.done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final int i = getProcessAPI().getNumberOfProcessDataDefinitions(processDefinition.getId()); assertEquals(2, i); disableAndDeleteProcess(processDefinition); } @Test(expected = ProcessDefinitionNotFoundException.class) public void getProcessDataDefinitionsWithException() throws Exception { final String intDataName = "luckyNum"; final Expression intDefaultExp = new ExpressionBuilder().createConstantIntegerExpression(10); final String strDataName = "luckyColor"; final Expression strDefaultExp = new ExpressionBuilder().createConstantStringExpression("blue"); final DesignProcessDefinition designProcessDefinition; // process level final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addIntegerData(intDataName, intDefaultExp); processDefinitionBuilder.addShortTextData(strDataName, strDefaultExp); designProcessDefinition = processDefinitionBuilder.done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); try { getProcessAPI().getProcessDataDefinitions(processDefinition.getId() + 1, 0, 5); } finally { disableAndDeleteProcess(processDefinition); } } @Test public void executeProcessWithNotInitializedData() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); processDefinitionBuilder.addAutomaticTask("step1"); processDefinitionBuilder.addIntegerData("intdata", null); processDefinitionBuilder.addShortTextData("stringData", null); processDefinitionBuilder.addDateData("dateData", null); processDefinitionBuilder.addBlobData("blobData", null); processDefinitionBuilder.addBooleanData("booleanData", null); processDefinitionBuilder.addLongData("longData", null); processDefinitionBuilder.addDoubleData("doubleData", null); processDefinitionBuilder.addFloatData("floatData", null); processDefinitionBuilder.addXMLData("xmlData", null); processDefinitionBuilder.addData("javaData", "java.util.List", null); final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.done(); final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition); final List dataDefList = getProcessAPI().getProcessDataDefinitions(processDefinition.getId(), 0, 15); assertEquals(10, dataDefList.size()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/data/ProcessDataInstanceIT.java ================================================ /** * 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.process.data; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; import static org.junit.Assert.*; import java.io.Serializable; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.api.ApiAccessType; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.data.ArchivedDataInstance; import org.bonitasoft.engine.bpm.data.ArchivedDataNotFoundException; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.ExceptionContext; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.util.APITypeManager; import org.junit.Test; public class ProcessDataInstanceIT extends TestWithUser { @Test public void getIntegerDataInstanceFromProcess() throws Exception { final String className = Integer.class.getName(); final ProcessDefinition processDefinition = operateProcess(user, new ExpressionBuilder().createConstantIntegerExpression(1), className); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final List processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10); assertFalse(processDataInstances.isEmpty()); assertEquals(1, processDataInstances.size()); assertEquals("var1", processDataInstances.get(0).getName()); assertEquals(1, processDataInstances.get(0).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void getDataInstance_should_return_empty_list_when_start_index_is_greater_than_number_of_elements_in_Db() throws Exception { final String className = String.class.getName(); final ProcessDefinition processDefinition = operateProcess(user, new ExpressionBuilder().createConstantStringExpression("aaa"), className); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final List processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 2, 100); assertThat(processDataInstances).isEmpty(); disableAndDeleteProcess(processDefinition); } @Test public void getShortTextDataInstanceFromProcess() throws Exception { final String className = String.class.getName(); final ProcessDefinition processDefinition = operateProcess(user, new ExpressionBuilder().createConstantStringExpression("aaa"), className); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final List processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10); assertFalse(processDataInstances.isEmpty()); assertEquals(1, processDataInstances.size()); assertEquals("var1", processDataInstances.get(0).getName()); assertEquals("aaa", processDataInstances.get(0).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void processWithShortAndLongTextData() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("processWithLongAndShortText", "1.0"); builder.addUserTask("step1", ACTOR_NAME); builder.addActor(ACTOR_NAME); final String shortTextValue = "shortTextValue"; builder.addShortTextData("shortTextData", new ExpressionBuilder().createConstantStringExpression(shortTextValue)); final String longTextValue = "longTextValue"; final StringBuilder longBuilder = new StringBuilder(longTextValue); longBuilder.append(longTextValue.repeat(10)); builder.addLongTextData("longTextData", new ExpressionBuilder().createConstantStringExpression(longBuilder.toString())); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); assertEquals(shortTextValue, getProcessAPI().getProcessDataInstance("shortTextData", processInstance.getId()).getValue()); assertEquals(longBuilder.toString(), getProcessAPI().getProcessDataInstance("longTextData", processInstance.getId()).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void getDoubleDataInstanceFromProcess() throws Exception { final String className = Double.class.getName(); final ProcessDefinition processDefinition = operateProcess(user, new ExpressionBuilder().createConstantDoubleExpression(1.5), className); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final List processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10); assertFalse(processDataInstances.isEmpty()); assertEquals(1, processDataInstances.size()); assertEquals("var1", processDataInstances.get(0).getName()); assertEquals(1.5, processDataInstances.get(0).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void getFloatDataInstanceFromProcess() throws Exception { final String className = Float.class.getName(); final ProcessDefinition processDefinition = operateProcess(user, new ExpressionBuilder().createConstantFloatExpression(1.5f), className); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final List processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10); assertFalse(processDataInstances.isEmpty()); assertEquals(1, processDataInstances.size()); assertEquals("var1", processDataInstances.get(0).getName()); assertEquals(1.5f, processDataInstances.get(0).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void getBooleanDataInstanceFromProcess() throws Exception { final String className = Boolean.class.getName(); final ProcessDefinition processDefinition = operateProcess(user, new ExpressionBuilder().createConstantBooleanExpression(true), className); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final List processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10); assertFalse(processDataInstances.isEmpty()); assertEquals(1, processDataInstances.size()); assertEquals("var1", processDataInstances.get(0).getName()); assertEquals(true, processDataInstances.get(0).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void getDateDataInstanceFromProcess() throws Exception { final ProcessDefinition processDefinition = operateProcess(user, new ExpressionBuilder().createConstantDateExpression("2013-07-18T14:49:26.86+02:00"), Date.class.getName()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final List processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10); assertFalse(processDataInstances.isEmpty()); assertEquals(1, processDataInstances.size()); assertEquals("var1", processDataInstances.get(0).getName()); assertNotNull(processDataInstances.get(0).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void getLongDataInstanceFromProcess() throws Exception { final String className = Long.class.getName(); final ProcessDefinition processDefinition = operateProcess(user, new ExpressionBuilder().createConstantLongExpression(1), className); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final List processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10); assertFalse(processDataInstances.isEmpty()); assertEquals(1, processDataInstances.size()); assertEquals("var1", processDataInstances.get(0).getName()); assertEquals(1L, processDataInstances.get(0).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void updateProcessDataInstance() throws Exception { final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance("My_Process", "1.0") .addActor(ACTOR_NAME) .addDescription("Delivery all day and night long") .addIntegerData("var1", new ExpressionBuilder().createConstantIntegerExpression(1)) .addUserTask("step1", ACTOR_NAME).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); // test execution final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // verify the retrieved data List processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10); assertFalse(processDataInstances.isEmpty()); assertEquals(1, processDataInstances.size()); assertEquals("var1", processDataInstances.get(0).getName()); assertEquals(1, processDataInstances.get(0).getValue()); getProcessAPI().updateProcessDataInstance("var1", processInstance.getId(), 2); // retrieve data after the update processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10); assertFalse(processDataInstances.isEmpty()); assertEquals(1, processDataInstances.size()); assertEquals(2, processDataInstances.get(0).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void cantUpdateProcessDataInstanceWithWrongValue() throws Exception { final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance("My_Process", "1.0") .addActor(ACTOR_NAME) .addDescription("Delivery all day and night long") .addData("data", List.class.getName(), null) .addUserTask("step1", ACTOR_NAME).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); // test execution final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); // verify the retrieved data try { getProcessAPI().updateProcessDataInstance("data", processInstance.getId(), "wrong value"); fail(); } catch (final UpdateException e) { assertEquals("USERNAME=" + USERNAME + " | DATA_NAME=data | DATA_CLASS_NAME=java.util.List | The type of new value [" + String.class.getName() + "] is not compatible with the type of the data.", e.getMessage()); final Map exceptionContext = e.getContext(); assertEquals(List.class.getName(), exceptionContext.get(ExceptionContext.DATA_CLASS_NAME)); assertEquals("data", exceptionContext.get(ExceptionContext.DATA_NAME)); } // retrieve data after the update final List processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10); assertEquals(1, processDataInstances.size()); assertNull(processDataInstances.get(0).getValue()); // Evaluate the data final List dependencies = Collections .singletonList(new ExpressionBuilder().createDataExpression("data", List.class.getName())); final Expression longExpression = new ExpressionBuilder().createGroovyScriptExpression("Script", "data = new ArrayList(); data.add(\"plop\"); return data;", List.class.getName(), dependencies); final Map> expressions = Collections.singletonMap(longExpression, Collections.emptyMap()); getProcessAPI().evaluateExpressionsOnActivityInstance(step1Id, expressions); disableAndDeleteProcess(processDefinition); } @Test public void updateProcessDataInstanceTwice() throws Exception { final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance("My_Process", "1.0") .addActor(ACTOR_NAME) .addDescription("Delivery all day and night long") .addIntegerData("var1", new ExpressionBuilder().createConstantIntegerExpression(1)) .addUserTask("step1", ACTOR_NAME).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // test execution final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); // verify the retrieved data List processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10); assertFalse(processDataInstances.isEmpty()); assertEquals(1, processDataInstances.size()); assertEquals("var1", processDataInstances.get(0).getName()); assertEquals(1, processDataInstances.get(0).getValue()); getProcessAPI().updateProcessDataInstance("var1", processInstance.getId(), 2); // retrieve data after the update processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10); assertFalse(processDataInstances.isEmpty()); assertEquals(1, processDataInstances.size()); assertEquals(2, processDataInstances.get(0).getValue()); getProcessAPI().updateProcessDataInstance("var1", processInstance.getId(), 3); // retrieve data after the update processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10); assertFalse(processDataInstances.isEmpty()); assertEquals(1, processDataInstances.size()); assertEquals(3, processDataInstances.get(0).getValue()); disableAndDeleteProcess(processDefinition); } @Test(expected = RetrieveException.class) public void dataNotAvailableAfterArchiveFromProcess() throws Exception { final DesignProcessDefinition processDef = new ProcessDefinitionBuilder().createNewInstance("My_Process", "1.0") .addActor(ACTOR_NAME) .addDescription("Delivery all day and night long") .addIntegerData("var1", new ExpressionBuilder().createConstantIntegerExpression(1)) .addUserTask("step1", ACTOR_NAME).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // test execution final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); // verify the retrieved data List processDataInstances = getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10); assertFalse(processDataInstances.isEmpty()); assertEquals(1, processDataInstances.size()); assertEquals("var1", processDataInstances.get(0).getName()); assertEquals(1, processDataInstances.get(0).getValue()); // Execute pending task waitForUserTaskAndExecuteIt(processInstance, "step1", user); waitForProcessToFinish(processInstance); // retrieve data after process has finished try { getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10); } finally { disableAndDeleteProcess(processDefinition); } } private ProcessDefinition operateProcess(final User user, final Expression expression, final String className) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("My_Process", "1.0"); if (className.equals(Integer.class.getName())) { processDefinitionBuilder.addIntegerData("var1", expression); } else if (className.equals(Long.class.getName())) { processDefinitionBuilder.addLongData("var1", expression); } else if (className.equals(Double.class.getName())) { processDefinitionBuilder.addDoubleData("var1", expression); } else if (className.equals(Float.class.getName())) { processDefinitionBuilder.addFloatData("var1", expression); } else if (className.equals(Boolean.class.getName())) { processDefinitionBuilder.addBooleanData("var1", expression); } else if (className.equals(Date.class.getName())) { processDefinitionBuilder.addDateData("var1", expression); } else if (className.equals(String.class.getName())) { processDefinitionBuilder.addShortTextData("var1", expression); } final DesignProcessDefinition processDef = processDefinitionBuilder.addActor(ACTOR_NAME) .addDescription("Delivery all day and night long") .addUserTask("step1", ACTOR_NAME).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); return processDefinition; } @Test public void getArchivedProcessDataInstance() throws Exception { final String dataName = "title"; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("ProcessToArchive", "1.0"); builder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression("1")); builder.addActor("actor"); builder.addUserTask("step", "actor"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), "actor", user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); Thread.sleep(10); getProcessAPI().updateProcessDataInstance(dataName, processInstance.getId(), "2"); final ArchivedDataInstance archivedData = getProcessAPI().getArchivedProcessDataInstance(dataName, processInstance.getId()); assertEquals("2", archivedData.getValue()); disableAndDeleteProcess(processDefinition); } @Test public void getArchivedProcessDataInstanceFromAnArchivedProcess() throws Exception { final String dataName = "title"; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("ProcessToArchive", "1.0"); builder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression("1")); builder.addAutomaticTask("system"); final ProcessDefinition processDefinition = deployAndEnableProcess(builder.getProcess()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForProcessToFinish(processInstance); final ArchivedDataInstance archivedData = getProcessAPI().getArchivedProcessDataInstance(dataName, processInstance.getId()); assertEquals("1", archivedData.getValue()); disableAndDeleteProcess(processDefinition); } @Test public void doNotArchiveTransientProcessDataInstance() throws Exception { final String dataName = "test"; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("ProcessToArchive", "1.0"); builder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression("1")).isTransient(); builder.addActor("actor"); builder.addUserTask("step", "actor"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), "actor", user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); getProcessAPI().updateProcessDataInstance(dataName, processInstance.getId(), "2"); waitForUserTaskAndExecuteIt(processInstance, "step", user); waitForProcessToFinish(processInstance); try { getProcessAPI().getArchivedProcessDataInstance(dataName, processInstance.getId()); } catch (final ArchivedDataNotFoundException e) { disableAndDeleteProcess(processDefinition); } } @Test public void getUnknownArchivedProcessDataInstance() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("ProcessToArchive", "1.0"); builder.addAutomaticTask("system"); final ProcessDefinition processDefinition = deployAndEnableProcess(builder.getProcess()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForProcessToFinish(processInstance); try { getProcessAPI().getArchivedProcessDataInstance("o", processInstance.getId()); fail("The data named 'o' does not exists"); } catch (final ArchivedDataNotFoundException ignored) { // Do nothing } finally { disableAndDeleteProcess(processDefinition); } } @Test public void getArchivedProcessDataInstances() throws Exception { final String dataName = "title"; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("ProcessToArchive", "1.0"); builder.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression("1")); builder.addShortTextData("job", new ExpressionBuilder().createConstantStringExpression("job")); builder.addShortTextData("desc", new ExpressionBuilder().createConstantStringExpression("desc")); builder.addActor("actor"); builder.addUserTask("step", "actor"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), "actor", user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); Thread.sleep(10); getProcessAPI().updateProcessDataInstance(dataName, processInstance.getId(), "2"); List archivedDataInstances = getProcessAPI() .getArchivedProcessDataInstances(processInstance.getId(), 0, 10); assertEquals(3, archivedDataInstances.size()); ArchivedDataInstance archivedDataInstance = getArchivedDataInstance(archivedDataInstances, dataName); assertEquals("2", archivedDataInstance.getValue()); archivedDataInstance = getArchivedDataInstance(archivedDataInstances, "job"); assertEquals("job", archivedDataInstance.getValue()); archivedDataInstance = getArchivedDataInstance(archivedDataInstances, "desc"); assertEquals("desc", archivedDataInstance.getValue()); archivedDataInstances = getProcessAPI().getArchivedProcessDataInstances(processInstance.getId(), 0, 1); assertEquals(1, archivedDataInstances.size()); archivedDataInstance = getArchivedDataInstance(archivedDataInstances, dataName); assertEquals("2", archivedDataInstance.getValue()); archivedDataInstances = getProcessAPI().getArchivedProcessDataInstances(processInstance.getId(), 1, 10); assertEquals(2, archivedDataInstances.size()); archivedDataInstance = getArchivedDataInstance(archivedDataInstances, "job"); assertEquals("job", archivedDataInstance.getValue()); archivedDataInstance = getArchivedDataInstance(archivedDataInstances, "desc"); assertEquals("desc", archivedDataInstance.getValue()); waitForUserTaskAndExecuteIt(processInstance, "step", user); waitForProcessToFinish(processInstance); archivedDataInstances = getProcessAPI().getArchivedProcessDataInstances(processInstance.getId(), 0, 10); assertEquals(3, archivedDataInstances.size()); archivedDataInstance = getArchivedDataInstance(archivedDataInstances, dataName); assertEquals("2", archivedDataInstance.getValue()); archivedDataInstance = getArchivedDataInstance(archivedDataInstances, "job"); assertEquals("job", archivedDataInstance.getValue()); archivedDataInstance = getArchivedDataInstance(archivedDataInstances, "desc"); assertEquals("desc", archivedDataInstance.getValue()); disableAndDeleteProcess(processDefinition); } @Test public void getEmptyArchivedProcessDataInstances() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("ProcessToArchive", "1.0"); builder.addActor("actor"); builder.addUserTask("step", "actor"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), "actor", user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final List archivedDataInstances = getProcessAPI() .getArchivedProcessDataInstances(processInstance.getId(), 0, 10); assertEquals(0, archivedDataInstances.size()); disableAndDeleteProcess(processDefinition); } @Test public void getCustomTypeDataInstance() throws Exception { if (APITypeManager.getAPIType() != ApiAccessType.LOCAL) { //FIXME this test does not work in remote engine (class does not exists in client) // Migrate to Junit5 and add an annotation to disable this test return; } final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive(); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithPlopp", "1.0"); processDefinitionBuilder.addActor("user").addUserTask("step1", "user").addData("myStepData", "org.bonitasoft.plop.Plopp", new ExpressionBuilder().createGroovyScriptExpression("theScript", "new org.bonitasoft.plop.Plopp()", "org.bonitasoft.plop.Plopp")); processDefinitionBuilder.addData("myData", "org.bonitasoft.plop.Plopp", new ExpressionBuilder().createGroovyScriptExpression("theScript", "new org.bonitasoft.plop.Plopp()", "org.bonitasoft.plop.Plopp")); final User user = getIdentityAPI().createUser("custDataUser", "bpm"); businessArchiveBuilder.setProcessDefinition(processDefinitionBuilder.done()); businessArchiveBuilder.addClasspathResource( new BarResource("org.bonitasoft.plop.jar", IOUtil.getAllContentFrom(this.getClass().getResourceAsStream("/org.bonitasoft.plop.bak")))); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), "user", user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1 = waitForUserTask("step1"); assertThat(getProcessAPI().getProcessDataInstances(processInstance.getId(), 0, 10)) .extracting("name", "className") .containsOnly(tuple("myData", "org.bonitasoft.plop.Plopp")); assertThat(getProcessAPI().getProcessDataInstance("myData", processInstance.getId())).isNotNull(); assertThat(getProcessAPI().getActivityDataInstances(step1, 0, 10)).extracting("name", "className") .contains(tuple("myStepData", "org.bonitasoft.plop.Plopp")); assertThat(getProcessAPI().getActivityDataInstance("myStepData", step1)).isNotNull(); assertThat(getProcessAPI().getArchivedProcessDataInstances(processInstance.getId(), 0, 10)) .extracting("name", "className") .containsOnly(tuple("myData", "org.bonitasoft.plop.Plopp")); assertThat(getProcessAPI().getArchivedProcessDataInstance("myData", processInstance.getId())).isNotNull(); assertThat(getProcessAPI().getArchivedActivityDataInstances(step1, 0, 10)).extracting("name", "className") .contains(tuple("myStepData", "org.bonitasoft.plop.Plopp")); assertThat(getProcessAPI().getArchivedActivityDataInstance("myStepData", step1)).isNotNull(); disableAndDeleteProcess(processDefinition); deleteUser(user); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/document/DocumentIT.java ================================================ /** * 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.process.document; import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; import static org.junit.Assert.*; import java.io.Serializable; 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 org.assertj.core.api.Assertions; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException; import org.bonitasoft.engine.bpm.contract.FileInputValue; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.document.ArchivedDocument; import org.bonitasoft.engine.bpm.document.ArchivedDocumentNotFoundException; import org.bonitasoft.engine.bpm.document.ArchivedDocumentsSearchDescriptor; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.document.DocumentAttachmentException; import org.bonitasoft.engine.bpm.document.DocumentCriterion; import org.bonitasoft.engine.bpm.document.DocumentException; import org.bonitasoft.engine.bpm.document.DocumentNotFoundException; import org.bonitasoft.engine.bpm.document.DocumentValue; import org.bonitasoft.engine.bpm.document.DocumentsSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException; import org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder; import org.bonitasoft.engine.bpm.process.impl.DocumentBuilder; import org.bonitasoft.engine.bpm.process.impl.DocumentDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.DocumentListDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.APITestUtil; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.test.TestStates; import org.junit.Test; /** * @author Nicolas Chabanoles * @author Baptiste Mesta * @author Celine Souchet */ public class DocumentIT extends TestWithUser { private int processVersion = 0; @Test public void attachADocumentToProcessInstanceTest() throws Exception { final ProcessInstance pi = deployAndEnableWithActorAndStartIt(user); Document attachment; try { final Document doc = buildReferenceToExternalDocument(); attachment = getProcessAPI().attachDocument(pi.getId(), doc.getName(), doc.getContentFileName(), doc.getContentMimeType(), doc.getUrl()); final Document attachedDoc = getProcessAPI().getDocument(attachment.getId()); assertIsSameDocument(attachment, attachedDoc); assertFalse(attachedDoc.hasContent()); } finally { disableAndDeleteProcess(pi.getProcessDefinitionId()); } } @Test public void removeADocument() throws Exception { final ProcessInstance pi = deployAndEnableWithActorAndStartIt(user); try { //given final Document attachment = getProcessAPI().attachDocument(pi.getId(), "Name", "FileName", "MimeType", new byte[] { 1, 2, 3 }); //when final Document document = getProcessAPI().removeDocument(attachment.getId()); //then assertEquals("removeDocument should return the removed document", attachment, document); try { getProcessAPI().getDocument(attachment.getId()); fail("should not be able to find document after deletion"); } catch (final DocumentNotFoundException e) { //ok } final SearchResult archivedDocumentSearchResult = getProcessAPI().searchArchivedDocuments( new SearchOptionsBuilder(0, 100) .filter(ArchivedDocumentsSearchDescriptor.SOURCEOBJECT_ID, document.getId()).done()); assertThat(archivedDocumentSearchResult.getResult()).hasSize(1); assertThat(archivedDocumentSearchResult.getResult().get(0).getContentStorageId()) .isEqualTo(document.getContentStorageId()); assertThat(getProcessAPI().getDocumentContent(document.getContentStorageId())) .isEqualTo(new byte[] { 1, 2, 3 }); } finally { disableAndDeleteProcess(pi.getProcessDefinitionId()); } } @Test public void attachADocumentAndItsContentToProcessInstanceTest() throws Exception { final ProcessInstance pi = deployAndEnableWithActorAndStartIt(user); Document attachment; try { final String documentName = "newDocument"; final Document doc = BuildTestUtil.buildDocument(documentName); final byte[] documentContent = BuildTestUtil.generateContent(doc); attachment = getProcessAPI().attachDocument(pi.getId(), doc.getName(), doc.getContentFileName(), doc.getContentMimeType(), documentContent); final Document attachedDoc = getProcessAPI().getDocument(attachment.getId()); assertIsSameDocument(attachment, attachedDoc); final byte[] attachedContent = getProcessAPI().getDocumentContent(attachedDoc.getContentStorageId()); assertTrue(Arrays.equals(documentContent, attachedContent)); assertTrue(attachedDoc.hasContent()); } finally { disableAndDeleteProcess(pi.getProcessDefinitionId()); } } private void assertIsSameDocument(final Document attachment, final Document attachedDoc) { assertEquals("IDs are not the same!", attachment.getId(), attachedDoc.getId()); assertEquals("Process instances IDs are not the same!", attachment.getProcessInstanceId(), attachedDoc.getProcessInstanceId()); assertEquals("Names are not the same!", attachment.getName(), attachedDoc.getName()); assertEquals("Authors are not the same!", attachment.getAuthor(), attachedDoc.getAuthor()); assertEquals("Creation dates are not the same!", attachment.getCreationDate().getTime(), attachedDoc.getCreationDate().getTime()); assertEquals("Has content flags are not the same!", attachment.hasContent(), attachedDoc.hasContent()); assertEquals("File names are not the same!", attachment.getContentFileName(), attachedDoc.getContentFileName()); assertEquals("Mime types are not the same!", attachment.getContentMimeType(), attachedDoc.getContentMimeType()); assertEquals("Content storage IDs are not the same!", attachment.getContentStorageId(), attachedDoc.getContentStorageId()); assertEquals("URL are not the same!", attachment.getUrl(), attachedDoc.getUrl()); assertEquals("Descriptions are not the same!", attachment.getDescription(), attachedDoc.getDescription()); } private void assertIsSameDocument(final Document attachedDoc, final long processInstanceId, final String name, final long author, final boolean hasContent, final String fileName, final String mimeType, final String url, final String description) { assertEquals("Process instances IDs are not the same!", processInstanceId, attachedDoc.getProcessInstanceId()); assertEquals("Names are not the same!", name, attachedDoc.getName()); assertEquals("Authors are not the same!", author, attachedDoc.getAuthor()); assertEquals("Has content flags are not the same!", hasContent, attachedDoc.hasContent()); assertEquals("File names are not the same!", fileName, attachedDoc.getContentFileName()); assertEquals("Mime types are not the same!", mimeType, attachedDoc.getContentMimeType()); assertEquals("URL are not the same!", url, attachedDoc.getUrl()); assertEquals("Descriptions are not the same!", description, attachedDoc.getDescription()); } @Test public void createProcessWithUrlDocument() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("createProcessWithUrlDocument", "1.0"); designProcessDefinition.addActor(ACTOR_NAME); designProcessDefinition.addUserTask("step1", ACTOR_NAME); final String docName = "myRtfDocument"; final String url = "http://intranet.bonitasoft.com/private/docStorage/anyValue"; final DocumentDefinitionBuilder documentDefinition = designProcessDefinition.addDocumentDefinition(docName); documentDefinition.addUrl(url); final String description = "My document with an url and a description that is inside the process"; documentDefinition.addDescription(description); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition.getProcess()).done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); try { final SearchOptionsBuilder sob = new SearchOptionsBuilder(0, 10) .filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()); final SearchResult docs = getProcessAPI().searchDocuments(sob.done()); final Document actualDoc = docs.getResult().get(0); assertEquals(docName, actualDoc.getName()); assertEquals(url, actualDoc.getUrl()); assertEquals(description, actualDoc.getDescription()); assertTrue("Document Content filename should NOT be valuated", null == actualDoc.getContentFileName()); } finally { disableAndDeleteProcess(processDefinition); } } @Test public void attachAnExternalDocumentReferenceToProcessInstanceTest() throws Exception { final ProcessInstance pi = deployAndEnableWithActorAndStartIt(user); Document attachment; try { final Document doc = buildReferenceToExternalDocument(); attachment = getProcessAPI().attachDocument(pi.getId(), doc.getName(), doc.getContentFileName(), doc.getContentMimeType(), doc.getUrl()); final Document attachedDoc = getProcessAPI().getDocument(attachment.getId()); assertIsSameDocument(attachment, attachedDoc); } finally { disableAndDeleteProcess(pi.getProcessDefinitionId()); } } private Document buildReferenceToExternalDocument() { final String now = String.valueOf(System.currentTimeMillis()); final String documentName = now + "-series"; final DocumentBuilder builder = new DocumentBuilder().createNewInstance(documentName, true); builder.setDescription("a document that points to an url"); builder.setURL("http://tinyurl.com/7n77prz"); return builder.done(); } private Document buildReferenceToExternalDocument(final String documentName) { final DocumentBuilder builder = new DocumentBuilder().createNewInstance(documentName, true); builder.setDescription("a document that points to an url"); builder.setURL("http://tinyurl.com/7n77prz"); return builder.done(); } @Test public void attachAnExternalDocumentReferenceToProcessInstanceAsNewVersionTest() throws Exception { final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); buildAndAttachDocument(processInstance); Document attachment; try { final Document initialDocument = getAttachmentWithoutItsContent(processInstance); assertThat(initialDocument.getVersion()).isEqualTo("1"); final Document doc = buildReferenceToExternalDocument(); attachment = getProcessAPI().attachNewDocumentVersion(processInstance.getId(), initialDocument.getName(), doc.getContentFileName(), doc.getContentMimeType(), doc.getUrl()); final Document attachedDoc = getProcessAPI().getDocument(attachment.getId()); assertIsSameDocument(attachment, attachedDoc); assertThat(attachedDoc.getVersion()).isEqualTo("2"); } finally { disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } } @Test public void attachADocumentAndItsContentToProcessInstanceAsNewVersionTest() throws Exception { final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); buildAndAttachDocument(processInstance); try { final Document initialDocument = getAttachmentWithoutItsContent(processInstance); assertThat(initialDocument.getVersion()).isEqualTo("1"); final Document doc = BuildTestUtil.buildDocument(initialDocument.getName()); final Document attachment = getProcessAPI().attachNewDocumentVersion(processInstance.getId(), initialDocument.getName(), doc.getContentFileName(), doc.getContentMimeType(), initialDocument.getName().getBytes()); final Document attachedDoc = getProcessAPI().getDocument(attachment.getId()); assertIsSameDocument(attachedDoc, attachment.getProcessInstanceId(), attachment.getName(), attachment.getAuthor(), attachment.hasContent(), attachment.getContentFileName(), attachment.getContentMimeType(), attachment.getUrl(), attachment.getDescription()); assertThat(attachedDoc.getVersion()).isEqualTo("2"); } finally { disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } } @Test public void getDocumentContentTest() throws Exception { final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); final String documentName = String.valueOf(System.currentTimeMillis()); final byte[] content = documentName.getBytes(); final Document document = getProcessAPI().attachDocument(processInstance.getId(), documentName, "myPdf.pdf", "text/plain", content); try { final byte[] docContent = getProcessAPI().getDocumentContent(document.getContentStorageId()); assertThat(docContent).isEqualTo(content); assertThat(document.getUrl()).isEqualTo( "documentDownload?fileName=myPdf.pdf&contentStorageId=" + document.getContentStorageId()); } finally { disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } } @Test public void getLastDocumentTest() throws Exception { final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); buildAndAttachDocument(processInstance); Document attachment; Document lastVersion; try { final String documentName = getAttachmentDocumentName(processInstance); attachment = getAttachmentWithoutItsContent(processInstance); lastVersion = getProcessAPI().getLastDocument(processInstance.getId(), documentName); assertIsSameDocument(attachment, lastVersion); final Document doc = buildReferenceToExternalDocument(documentName); final Document newVersion = getProcessAPI().attachNewDocumentVersion(processInstance.getId(), doc.getName(), doc.getContentFileName(), doc.getContentMimeType(), doc.getUrl()); lastVersion = getProcessAPI().getLastDocument(processInstance.getId(), documentName); assertIsSameDocument(newVersion, lastVersion); } finally { disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } } @Test public void getDocumentOnProcessWithDocumentInDefinitionUsingBarResource() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("MyProcessWithDocumentsInBar", "1.0"); builder.addUserTask("step1", ACTOR_NAME); builder.addActor(ACTOR_NAME); builder.addDocumentDefinition("myDoc").addContentFileName("myPdfModifiedName.pdf") .addDescription("a cool pdf document").addMimeType("application/pdf") .addFile("myPdf.pdf").addDescription("my description"); final byte[] pdfContent = new byte[] { 5, 0, 1, 4, 6, 5, 2, 3, 1, 5, 6, 8, 4, 6, 6, 3, 2, 4, 5 }; final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(builder.getProcess()) .addDocumentResource(new BarResource("myPdf.pdf", pdfContent)).done(); final ProcessInstance processInstance = deployAndEnableProcessWithActorAndStartIt(businessArchive, user); try { final Document attachment = getAttachmentWithoutItsContent(processInstance); assertIsSameDocument(attachment, processInstance.getId(), "myDoc", user.getId(), true, "myPdfModifiedName.pdf", "application/pdf", attachment.getUrl(), "my description"); final byte[] docContent = getProcessAPI().getDocumentContent(attachment.getContentStorageId()); assertTrue(Arrays.equals(pdfContent, docContent)); } finally { waitForUserTask(processInstance, "step1"); disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } } @Test public void getDocumentUsingExpression() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("MyProcessWithDocumentsInBar", "1.0"); final AutomaticTaskDefinitionBuilder automaticTask = builder.addAutomaticTask("setDataTask"); automaticTask.addOperation(new LeftOperandBuilder().createNewInstance("myDocRef").done(), OperatorType.ASSIGNMENT, "=", null, new ExpressionBuilder().createDocumentReferenceExpression("myDoc")); automaticTask.addOperation( new LeftOperandBuilder().createNewInstance("docFileName").done(), OperatorType.ASSIGNMENT, "=", null, new ExpressionBuilder().createGroovyScriptExpression("myScript", "myDoc.getFileName()", String.class.getName(), new ExpressionBuilder().createDocumentReferenceExpression("myDoc"))); builder.addUserTask("step1", ACTOR_NAME); builder.addActor(ACTOR_NAME); builder.addData("myDocRef", Document.class.getName(), null); builder.addData("docFileName", String.class.getName(), null); builder.addDocumentDefinition("myDoc").addContentFileName("myPdfModifiedName.pdf") .addDescription("a cool pdf document").addMimeType("application/pdf") .addFile("myPdf.pdf"); builder.addTransition("setDataTask", "step1"); final byte[] pdfContent = new byte[] { 5, 0, 1, 4, 6, 5, 2, 3, 1, 5, 6, 8, 4, 6, 6, 3, 2, 4, 5 }; final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(builder.getProcess()) .addDocumentResource(new BarResource("myPdf.pdf", pdfContent)).done(); final ProcessInstance pi = deployAndEnableProcessWithActorAndStartIt(businessArchive, user); try { final long step1Id = waitForUserTask(pi, "step1"); final DataInstance activityDataInstance = getProcessAPI().getActivityDataInstance("myDocRef", step1Id); final Document docRef = (Document) activityDataInstance.getValue(); final Document attachment = getAttachmentWithoutItsContent(pi); assertEquals(attachment, docRef); assertEquals("myPdfModifiedName.pdf", getProcessAPI().getActivityDataInstance("docFileName", step1Id).getValue()); } finally { disableAndDeleteProcess(pi.getProcessDefinitionId()); } } @Test public void evaluateExpressionOnCompletedProcessInstance_should_be_able_to_retrieve_document_for_an_archived_process_instance() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("MyProcessWithDocumentsInBar", "1.0"); builder.addStartEvent("start"); builder.addAutomaticTask("auto"); builder.addEndEvent("end"); builder.addDocumentDefinition("document").addContentFileName("document.content").addFile("document.content"); final BusinessArchiveBuilder archive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(builder.getProcess()); archive.addDocumentResource(new BarResource("document.content", "content".getBytes())); final ProcessInstance processInstance = getProcessAPI() .startProcess(deployAndEnableProcess(archive.done()).getId()); waitForProcessToFinish(processInstance.getId()); final Map> expressions = Collections.singletonMap( new ExpressionBuilder().createDocumentReferenceExpression("document"), emptyMap()); final Map result = getProcessAPI() .evaluateExpressionOnCompletedProcessInstance(processInstance.getId(), expressions); assertEquals("document.content", ((Document) result.get("document")).getContentFileName()); disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } @Test public void getDocumentOnProcessWithDocumentInDefinitionUsingUrl() throws Exception { final String url = "http://plop.org/file.pdf"; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("MyProcessWithExternalDocuments", "1.0"); builder.addUserTask("step1", ACTOR_NAME); builder.addActor(ACTOR_NAME); builder.addDocumentDefinition("myDoc").addContentFileName("file.pdf").addDescription("a cool pdf document") .addMimeType("application/pdf").addUrl(url); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(builder.getProcess()).done(); final ProcessInstance processInstance = deployAndEnableProcessWithActorAndStartIt(businessArchive, user); try { final Document attachment = getAttachmentWithoutItsContent(processInstance); assertIsSameDocument(attachment, processInstance.getId(), "myDoc", user.getId(), false, "file.pdf", "application/pdf", url, "a cool pdf document"); } finally { // Clean up waitForUserTask(processInstance, "step1"); disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } } @Test public void getDocumentAtProcessInstantiation() throws Exception { final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); buildAndAttachDocument(processInstance); try { Thread.sleep(2000); final Document beforeUpdate = getAttachmentWithoutItsContent(processInstance); final Document doc = BuildTestUtil.buildDocument(beforeUpdate.getName()); getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(), doc.getContentFileName(), doc.getContentMimeType(), "contentOfTheDoc".getBytes()); final Document afterUpdate = getAttachmentWithoutItsContent(processInstance); assertNotSame(beforeUpdate, afterUpdate); final Document documentAtProcessInstantiation = getProcessAPI() .getDocumentAtProcessInstantiation(processInstance.getId(), afterUpdate.getName()); assertEquals(beforeUpdate, documentAtProcessInstantiation); } finally { disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } } @Test public void getDocumentAtActivityInstanceCompletion() throws Exception { final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); buildAndAttachDocument(processInstance); try { final Document beforeUpdate = getAttachmentWithoutItsContent(processInstance); final Document doc = BuildTestUtil.buildDocument(beforeUpdate.getName()); getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(), doc.getContentFileName(), doc.getContentMimeType(), "contentOfTheDoc".getBytes()); final Document afterUpdate = getAttachmentWithoutItsContent(processInstance); assertNotSame(beforeUpdate, afterUpdate); final long step1Id = waitForUserTaskAndExecuteIt(processInstance, "step1", user); waitForArchivedActivity(step1Id, TestStates.NORMAL_FINAL); final Document doc2 = BuildTestUtil.buildDocument(beforeUpdate.getName()); getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(), doc2.getContentFileName(), doc2.getContentMimeType(), "contentOfTheDoc".getBytes()); final Document documentAtActivityInstantiation = getProcessAPI() .getDocumentAtActivityInstanceCompletion(step1Id, beforeUpdate.getName()); assertEquals(afterUpdate, documentAtActivityInstantiation); } finally { disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } } @Test public void getNumberOfDocument() throws Exception { final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); buildAndAttachDocument(processInstance); try { final long initialNbOfDocument = getProcessAPI().getNumberOfDocuments(processInstance.getId()); final String documentName = "anotherDocumentReference"; final Document doc = buildReferenceToExternalDocument(); getProcessAPI().attachDocument(processInstance.getId(), documentName, doc.getContentFileName(), doc.getContentMimeType(), doc.getUrl()); final long currentNbOfDocument = getProcessAPI().getNumberOfDocuments(processInstance.getId()); assertEquals("Invalid number of attachments!", initialNbOfDocument + 1, currentNbOfDocument); } finally { disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } } @Test public void getNumberOfDocumentAfterAddingDocumentValue() throws Exception { final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); buildAndAttachDocument(processInstance); try { final long initialNbOfDocument = getProcessAPI().getNumberOfDocuments(processInstance.getId()); final String documentName = "anotherDocumentValue"; final Document doc = BuildTestUtil.buildDocument(documentName); getProcessAPI().attachDocument(processInstance.getId(), documentName, doc.getContentFileName(), doc.getContentMimeType(), documentName.getBytes()); final long currentNbOfDocument = getProcessAPI().getNumberOfDocuments(processInstance.getId()); assertEquals("Invalid number of attachments!", initialNbOfDocument + 1, currentNbOfDocument); } finally { disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } } @Test public void searchDocuments() throws Exception { // add a new document, search it. SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("ProcessDoc", "12000"); builder.addActor("actor").addUserTask("step1", "actor"); builder.addDocumentDefinition("Doc1").addDescription("This is a description").addContentFileName("doc.jpg") .addMimeType("image").addFile("doc.jpg"); builder.addDocumentDefinition("Doc2").addDescription("This is a description2").addContentFileName("doc2.jpg") .addMimeType("image").addFile("doc2.jpg"); final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(builder.done()) .addDocumentResource(new BarResource("doc.jpg", "Hello World".getBytes())) .addDocumentResource(new BarResource("doc2.jpg", "Hello World2".getBytes())); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), "actor", user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1 = waitForUserTask("step1"); searchOptionsBuilder.filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()); searchOptionsBuilder.sort(DocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC); SearchResult documentSearch = getProcessAPI().searchDocuments(searchOptionsBuilder.done()); assertThat(documentSearch.getCount()).isEqualTo(2); assertThat(documentSearch.getResult()).extracting("contentFileName", "processInstanceId", "author") .containsExactly( tuple("doc.jpg", processInstance.getId(), user.getId()), tuple("doc2.jpg", processInstance.getId(), user.getId())); final Document document = documentSearch.getResult().get(0); assertThat(getProcessAPI() .searchDocuments(new SearchOptionsBuilder(0, 45).searchTerm("Doc") .sort(DocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC).done()) .getResult() .get(0).getId()).isEqualTo(document.getId()); assertThat(getProcessAPI() .searchDocuments(new SearchOptionsBuilder(0, 45).searchTerm("This is") .sort(DocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC).done()) .getResult().get(0).getId()).isEqualTo(document.getId()); // search document by content storage id String contentStorageId = document.getContentStorageId(); final SearchResult docSearchedByContentStorageId = getProcessAPI().searchDocuments( new SearchOptionsBuilder(0, 100).filter(DocumentsSearchDescriptor.CONTENT_STORAGE_ID, contentStorageId) .done()); assertThat(docSearchedByContentStorageId.getCount()).isEqualTo(1); assertThat(docSearchedByContentStorageId.getResult().get(0).getContentStorageId()).isEqualTo(contentStorageId); // Finalize the process getProcessAPI().assignUserTask(step1, user.getId()); getProcessAPI().executeUserTask(user.getId(), step1, emptyMap()); waitForProcessToFinish(processInstance); //search archive document order by archive date searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()); searchOptionsBuilder.sort(ArchivedDocumentsSearchDescriptor.ARCHIVE_DATE, Order.DESC); SearchResult archiveDocumentSearch = getProcessAPI() .searchArchivedDocuments(searchOptionsBuilder.done()); assertThat(archiveDocumentSearch.getCount()).isEqualTo(2); assertThat(archiveDocumentSearch.getResult()).extracting("contentFileName", "processInstanceId", "author") .containsOnly( tuple("doc.jpg", processInstance.getId(), user.getId()), tuple("doc2.jpg", processInstance.getId(), user.getId())); // search archived document by content storage id final SearchResult archivedDocSearchedByContentStorageId = getProcessAPI() .searchArchivedDocuments( new SearchOptionsBuilder(0, 100) .filter(ArchivedDocumentsSearchDescriptor.CONTENT_STORAGE_ID, contentStorageId) .done()); assertThat(archivedDocSearchedByContentStorageId.getCount()).isEqualTo(1); assertThat(archivedDocSearchedByContentStorageId.getResult().get(0).getContentStorageId()) .isEqualTo(contentStorageId); disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } @Test public void searchDocumentsWithApostrophe() throws Exception { searchDocumentsWithApostrophe("'documentName", "fileName"); searchDocumentsWithApostrophe("documentName", "'fileName"); } private void searchDocumentsWithApostrophe(final String documentName, final String fileName) throws Exception { final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); buildAndAttachDocument(processInstance, documentName, fileName); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(DocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC); searchOptionsBuilder.searchTerm("'"); final SearchResult documentSearch = getProcessAPI().searchDocuments(searchOptionsBuilder.done()); assertEquals(1, documentSearch.getCount()); assertEquals(processInstance.getId(), documentSearch.getResult().get(0).getProcessInstanceId()); assertEquals(user.getId(), documentSearch.getResult().get(0).getAuthor()); disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } @Test public void documentsAreDeletedWhenProcessIsDeleted() throws Exception { final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); buildAndAttachDocument(processInstance, "test", "test.txt"); disableAndDeleteProcess(processInstance.getProcessDefinitionId()); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(DocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC); final SearchResult documentSearch = getProcessAPI().searchDocuments(searchOptionsBuilder.done()); assertEquals(0, documentSearch.getCount()); } @Test public void searchArchivedDocuments() throws Exception { // first time search, no document in archive table. final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); getProcessAPI().attachDocument(processInstance.getId(), "Doc 1", "doc1.jpg", "image", "Hello World".getBytes()); SearchOptionsBuilder searchOptionsBuilder; searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()); SearchResult documentSearch = getProcessAPI() .searchArchivedDocuments(searchOptionsBuilder.done()); assertEquals(0, documentSearch.getCount()); try { final Document beforeUpdate = getAttachmentWithoutItsContent(processInstance); getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(), "doc2.jpg", "image", "contentOfTheDoc".getBytes()); final Document afterUpdate = getAttachmentWithoutItsContent(processInstance); getProcessAPI().getDocumentAtProcessInstantiation(processInstance.getId(), afterUpdate.getName()); // search again. exist 1 document in archive table. searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()); searchOptionsBuilder.sort(ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC); documentSearch = getProcessAPI().searchArchivedDocuments(searchOptionsBuilder.done()); assertEquals(1, documentSearch.getCount()); ArchivedDocument archivedDocument = documentSearch.getResult().get(0); assertEquals(processInstance.getId(), archivedDocument.getProcessInstanceId()); assertEquals(user.getId(), archivedDocument.getAuthor()); // search with term: searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.searchTerm(afterUpdate.getName()); documentSearch = getProcessAPI().searchArchivedDocuments(searchOptionsBuilder.done()); archivedDocument = documentSearch.getResult().get(0); assertEquals(1, documentSearch.getCount()); assertEquals(afterUpdate.getName(), archivedDocument.getName()); assertThat( getProcessAPI().searchArchivedDocuments(new SearchOptionsBuilder(0, 45).searchTerm("doc1").done()) .getResult().get(0) .getContentFileName()) .isEqualTo("doc1.jpg"); } finally { disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } } @Test public void searchArchivedDocumentsWithApostropheInTheDocumentName() throws Exception { final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); buildAndAttachDocument(processInstance, "a'", "a"); SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); SearchResult documentSearch = getProcessAPI() .searchArchivedDocuments(searchOptionsBuilder.done()); assertEquals(0, documentSearch.getCount()); try { final Document beforeUpdate = getAttachmentWithoutItsContent(processInstance); final Document doc = BuildTestUtil.buildDocument(beforeUpdate.getName()); getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(), doc.getContentFileName(), doc.getContentMimeType(), "contentOfTheDoc".getBytes()); final Document afterUpdate = getAttachmentWithoutItsContent(processInstance); getProcessAPI().getDocumentAtProcessInstantiation(processInstance.getId(), afterUpdate.getName()); // search with term: searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.searchTerm("a'"); documentSearch = getProcessAPI().searchArchivedDocuments(searchOptionsBuilder.done()); assertEquals(1, documentSearch.getCount()); assertEquals(afterUpdate.getName(), documentSearch.getResult().get(0).getName()); } finally { disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } } @Test public void searchArchivedDocumentsWithApostropheInTheFileName() throws Exception { final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); buildAndAttachDocument(processInstance, "b", "b'"); SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); SearchResult documentSearch = getProcessAPI() .searchArchivedDocuments(searchOptionsBuilder.done()); assertEquals(0, documentSearch.getCount()); try { final Document beforeUpdate = getAttachmentWithoutItsContent(processInstance); final Document doc = BuildTestUtil.buildDocument(beforeUpdate.getName()); getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(), doc.getContentFileName(), doc.getContentMimeType(), "contentOfTheDoc".getBytes()); final Document afterUpdate = getAttachmentWithoutItsContent(processInstance); getProcessAPI().getDocumentAtProcessInstantiation(processInstance.getId(), afterUpdate.getName()); // search with term: searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.searchTerm("b'"); documentSearch = getProcessAPI().searchArchivedDocuments(searchOptionsBuilder.done()); assertEquals(1, documentSearch.getCount()); assertEquals(afterUpdate.getName(), documentSearch.getResult().get(0).getName()); } finally { disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } } @Test public void getArchivedVersionOfDocuments() throws Exception { // add new document final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); buildAndAttachDocument(processInstance); // search archive document. result is 0. final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()); final SearchResult documentSearch = getProcessAPI() .searchArchivedDocuments(searchOptionsBuilder.done()); assertEquals(0, documentSearch.getCount()); // archive document try { final Document beforeUpdate = getAttachmentWithoutItsContent(processInstance); final Document doc = BuildTestUtil.buildDocument(beforeUpdate.getName()); getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(), "newContent1.file", doc.getContentMimeType(), "contentOfTheDoc1".getBytes()); getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(), "newContent2.file", doc.getContentMimeType(), "contentOfTheDoc2".getBytes()); getAttachmentWithoutItsContent(processInstance); // get archived document final ArchivedDocument archivedDocument = getProcessAPI() .getArchivedVersionOfProcessDocument(beforeUpdate.getId()); assertNotNull(archivedDocument.getArchiveDate()); assertEquals(beforeUpdate.getId(), archivedDocument.getSourceObjectId()); assertEquals("newContent1.file", archivedDocument.getContentFileName()); assertEquals(processInstance.getId(), archivedDocument.getProcessInstanceId()); } finally { disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } } @Test(expected = ArchivedDocumentNotFoundException.class) public void getArchivedDocumentNotFound() throws BonitaException { getProcessAPI().getArchivedProcessDocument(123456789L); } @Test public void getArchivedDocument() throws Exception { // add new document final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); buildAndAttachDocument(processInstance); // search archive document. result is 0. final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()); final SearchResult documentSearch = getProcessAPI() .searchArchivedDocuments(searchOptionsBuilder.done()); assertEquals(0, documentSearch.getCount()); // archive document try { final Document beforeUpdate = getAttachmentWithoutItsContent(processInstance); final Document doc = BuildTestUtil.buildDocument(beforeUpdate.getName()); getProcessAPI().attachNewDocumentVersion(processInstance.getId(), beforeUpdate.getName(), doc.getContentFileName(), doc.getContentMimeType(), "contentOfTheDoc".getBytes()); getAttachmentWithoutItsContent(processInstance); final ArchivedDocument archivedDocument = getProcessAPI() .getArchivedVersionOfProcessDocument(beforeUpdate.getId()); assertEquals(archivedDocument, getProcessAPI().getArchivedProcessDocument(archivedDocument.getId())); } finally { disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } } @Test public void countAttachmentWithSomeAttachments() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); final long initialNbOfDocument = getProcessAPI().countAttachments(searchOptionsBuilder.done()); final ProcessInstance processInstance = deployAndEnableWithActorAndStartIt(user); buildAndAttachDocument(processInstance); final long numberOfAttachments = getProcessAPI().countAttachments(searchOptionsBuilder.done()); assertEquals(1 + initialNbOfDocument, numberOfAttachments); disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } @Test public void updateExistingDocumentWithOperation() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("procWithStringIndexes", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addDescription("The doc'"); designProcessDefinition.addUserTask("step1", ACTOR_NAME); final Expression groovyThatCreateDocumentContent = new ExpressionBuilder().createGroovyScriptExpression( "script", "return new org.bonitasoft.engine.bpm.document.DocumentValue(\"updated Content\".getBytes(), \"plain/text\", \"updatedContent.txt\");", DocumentValue.class.getName()); designProcessDefinition.addAutomaticTask("step2") .addOperation(new OperationBuilder().createSetDocument("textFile", groovyThatCreateDocumentContent)); designProcessDefinition.addUserTask("step3", ACTOR_NAME); designProcessDefinition.addTransition("step1", "step2"); designProcessDefinition.addTransition("step2", "step3"); designProcessDefinition.addDocumentDefinition("textFile").addContentFileName("myUnmodifiedTextFile.pdf") .addDescription("a cool text document") .addMimeType("plain/text").addFile("myUnmodifiedTextFile.txt"); final byte[] textContent = "Unmodified content".getBytes(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition.getProcess()) .addDocumentResource(new BarResource("myUnmodifiedTextFile.txt", textContent)) .done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // before update final long step1Id = waitForUserTask(processInstance, "step1"); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()); final SearchOptions searchOptions = searchOptionsBuilder.done(); SearchResult searchDocuments = getProcessAPI().searchDocuments(searchOptions); assertEquals(1, searchDocuments.getCount()); final Document initialDocument = searchDocuments.getResult().iterator().next(); final byte[] documentContent = getProcessAPI().getDocumentContent(initialDocument.getContentStorageId()); assertEquals("Unmodified content", new String(documentContent)); // update assignAndExecuteStep(step1Id, user.getId()); waitForUserTask(processInstance, "step3"); // after update searchDocuments = getProcessAPI().searchDocuments(searchOptions); assertEquals(1, searchDocuments.getCount()); final Document newDocument = searchDocuments.getResult().iterator().next(); assertEquals("textFile", newDocument.getName()); assertEquals("updatedContent.txt", newDocument.getContentFileName()); assertEquals("plain/text", newDocument.getContentMimeType()); final byte[] newDocumentContent = getProcessAPI().getDocumentContent(newDocument.getContentStorageId()); assertEquals("updated Content", new String(newDocumentContent)); disableAndDeleteProcess(processDefinition); } @Test public void updateExistingDocumentWithNullOperation() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("procWithStringIndexes", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addDescription("The doc'"); designProcessDefinition.addUserTask("step1", ACTOR_NAME); final Expression groovyThatReturnNull = new ExpressionBuilder().createGroovyScriptExpression("script", "return null;", DocumentValue.class.getName()); designProcessDefinition.addAutomaticTask("step2") .addOperation(new OperationBuilder().createSetDocument("textFile", groovyThatReturnNull)); designProcessDefinition.addUserTask("step3", ACTOR_NAME); designProcessDefinition.addTransition("step1", "step2"); designProcessDefinition.addTransition("step2", "step3"); designProcessDefinition.addDocumentDefinition("textFile").addContentFileName("myUnmodifiedTextFile.pdf") .addDescription("a cool text document") .addMimeType("plain/text").addFile("myUnmodifiedTextFile.txt"); final byte[] textContent = "Unmodified content".getBytes(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition.getProcess()) .addDocumentResource(new BarResource("myUnmodifiedTextFile.txt", textContent)) .done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // before update final long step1Id = waitForUserTask(processInstance, "step1"); final Document initialDocument = getProcessAPI().getLastDocument(processInstance.getId(), "textFile"); final byte[] documentContent = getProcessAPI().getDocumentContent(initialDocument.getContentStorageId()); assertEquals("Unmodified content", new String(documentContent)); // update assignAndExecuteStep(step1Id, user.getId()); waitForUserTask(processInstance, "step3"); // after update assertEquals("textFile", getProcessAPI().getArchivedVersionOfProcessDocument(initialDocument.getId()).getName()); try { getProcessAPI().getLastDocument(processInstance.getId(), "textFile"); fail(); } catch (final DocumentNotFoundException e) { // ok } finally { disableAndDeleteProcess(processDefinition); } } @Test public void updateExistingDocumentUrlWithOperation() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("procWithStringIndexes", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addDescription("The doc'"); designProcessDefinition.addUserTask("step1", ACTOR_NAME); designProcessDefinition.addAutomaticTask("step2").addOperation( new OperationBuilder().createSetDocument("textFile", getDocumentValueExpressionWithUrl("http://www.example.com/new_url.txt"))); designProcessDefinition.addUserTask("step3", ACTOR_NAME); designProcessDefinition.addTransition("step1", "step2"); designProcessDefinition.addTransition("step2", "step3"); designProcessDefinition.addDocumentDefinition("textFile").addContentFileName("myUnmodifiedTextFile.pdf") .addDescription("a cool text document") .addMimeType("plain/text").addUrl("http://www.example.com/original_url.txt"); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition.getProcess()).done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // before update final long step1Id = waitForUserTask(processInstance, "step1"); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()); final SearchOptions searchOptions = searchOptionsBuilder.done(); SearchResult searchDocuments = getProcessAPI().searchDocuments(searchOptions); assertEquals(1, searchDocuments.getCount()); final Document initialDocument = searchDocuments.getResult().iterator().next(); assertEquals("http://www.example.com/original_url.txt", initialDocument.getUrl()); // update assignAndExecuteStep(step1Id, user.getId()); waitForUserTask(processInstance, "step3"); // after update searchDocuments = getProcessAPI().searchDocuments(searchOptions); assertEquals(1, searchDocuments.getCount()); final Document newDocument = searchDocuments.getResult().iterator().next(); assertEquals("http://www.example.com/new_url.txt", newDocument.getUrl()); disableAndDeleteProcess(processDefinition); } @Test public void createDocumentWithOperationAndInitialValue() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("procWithStringIndexes", "1.0"); designProcessDefinition.addData("documentValue", DocumentValue.class.getName(), null); designProcessDefinition.addActor(ACTOR_NAME).addDescription("The doc'"); designProcessDefinition.addAutomaticTask("step0").addOperation( new OperationBuilder().createSetDataOperation("documentValue", new ExpressionBuilder().createDocumentReferenceExpression("textFile"))); designProcessDefinition.addUserTask("step1", ACTOR_NAME); final Expression groovyThatCreateDocumentContent = new ExpressionBuilder().createGroovyScriptExpression( "script", "return new org.bonitasoft.engine.bpm.document.DocumentValue(\"updated Content\".getBytes(), \"plain/text\", \"updatedContent.txt\");", DocumentValue.class.getName()); designProcessDefinition.addAutomaticTask("step2") .addOperation(new OperationBuilder().createSetDocument("textFile", groovyThatCreateDocumentContent)); designProcessDefinition.addUserTask("step3", ACTOR_NAME); designProcessDefinition.addTransition("step0", "step1"); designProcessDefinition.addTransition("step1", "step2"); designProcessDefinition.addTransition("step2", "step3"); designProcessDefinition.addDocumentDefinition("docInitWithDocValue").addInitialValue( new ExpressionBuilder().createGroovyScriptExpression("docValue", "new org.bonitasoft.engine.bpm.document.DocumentValue(\"hello3\".getBytes(),\"plain/text\",\"file1.txt\")", DocumentValue.class.getName())); designProcessDefinition.addDocumentDefinition("docInitWithFileInput").addInitialValue( new ExpressionBuilder().createGroovyScriptExpression("docValue2", "new org.bonitasoft.engine.bpm.contract.FileInputValue(\"file2.txt\", \"hello4\".getBytes())", FileInputValue.class.getName())); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // before update final long step1Id = waitForUserTask(processInstance, "step1"); // document value expression should return null when document don't exists assertNull(getProcessAPI().getProcessDataInstance("documentValue", processInstance.getId()).getValue()); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()); searchOptionsBuilder.sort(DocumentsSearchDescriptor.DOCUMENT_CREATIONDATE, Order.ASC); final SearchOptions searchOptions = searchOptionsBuilder.done(); SearchResult searchDocuments = getProcessAPI().searchDocuments(searchOptions); assertEquals(2, searchDocuments.getCount()); assertThat(searchDocuments.getResult()).extracting("name", "contentFileName").containsExactly( tuple("docInitWithDocValue", "file1.txt"), tuple("docInitWithFileInput", "file2.txt")); // update assignAndExecuteStep(step1Id, user); waitForUserTask(processInstance, "step3"); // after update searchDocuments = getProcessAPI().searchDocuments(searchOptions); assertEquals(3, searchDocuments.getCount()); final Document newDocument = searchDocuments.getResult().get(2); assertEquals("textFile", newDocument.getName()); assertEquals("updatedContent.txt", newDocument.getContentFileName()); assertEquals("plain/text", newDocument.getContentMimeType()); final byte[] newDocumentContent = getProcessAPI().getDocumentContent(newDocument.getContentStorageId()); assertEquals("updated Content", new String(newDocumentContent)); disableAndDeleteProcess(processDefinition); } @Test public void startProcessAndSetDocumentValueWithOperations() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("LivingDay", "1.0"); final String docRefName = "invoiceReference"; designProcessDefinition.addData(docRefName, DocumentValue.class.getName(), null); final String docName = "invoiceLetter"; designProcessDefinition.addData(docName, DocumentValue.class.getName(), null); designProcessDefinition.addActor(ACTOR_NAME); designProcessDefinition.addUserTask("step1", ACTOR_NAME); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), ACTOR_NAME, user); final String docUrl = "http://internal.intranet.org/resources/myDoc.pdf"; final Operation docRefOperation = new OperationBuilder().createSetDocument(docRefName, new ExpressionBuilder().createInputExpression("documentReference", DocumentValue.class.getName())); final Operation docContentOperation = new OperationBuilder().createSetDocument(docName, new ExpressionBuilder().createInputExpression("documentValue", DocumentValue.class.getName())); final Map expressionContext = new HashMap<>(2); final String documentFileName = "updatedContent.txt"; expressionContext.put("documentValue", new DocumentValue("Binary content of the document".getBytes(), "plain/text", documentFileName)); expressionContext.put("documentReference", new DocumentValue(docUrl)); final ProcessInstance myCase = getProcessAPI() .startProcess(processDefinition.getId(), asList(docContentOperation, docRefOperation), expressionContext); waitForUserTask(myCase, "step1"); SearchResult searchDocuments = getProcessAPI().searchDocuments( new SearchOptionsBuilder(0, 5).filter(DocumentsSearchDescriptor.DOCUMENT_NAME, docRefName).done()); assertEquals(docUrl, searchDocuments.getResult().get(0).getUrl()); searchDocuments = getProcessAPI().searchDocuments( new SearchOptionsBuilder(0, 5).filter(DocumentsSearchDescriptor.DOCUMENT_NAME, docName).done()); assertEquals(documentFileName, searchDocuments.getResult().get(0).getContentFileName()); disableAndDeleteProcess(processDefinition); } @Test public void createDocumentWithOperationUsingURL() throws Exception { // deploy and instantiate process final String url = "http://www.example.com/new_url.txt"; final ProcessDefinition processDefinition = deployProcessWithURLDocumentCreateOperation("textFile", url); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // before update final long step1Id = waitForUserTask(processInstance, "step1"); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()); final SearchOptions searchOptions = searchOptionsBuilder.done(); SearchResult searchDocuments = getProcessAPI().searchDocuments(searchOptions); assertEquals(0, searchDocuments.getCount()); // update assignAndExecuteStep(step1Id, user); waitForUserTask(processInstance, "step3"); // after update searchDocuments = getProcessAPI().searchDocuments(searchOptions); assertEquals(1, searchDocuments.getCount()); final Document newDocument = searchDocuments.getResult().iterator().next(); assertEquals("textFile", newDocument.getName()); assertEquals(url, newDocument.getUrl()); // cleaup disableAndDeleteProcess(processDefinition); } private ProcessDefinition deployProcessWithURLDocumentCreateOperation(final String documentName, final String url) throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("simpleProcess", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addDescription("The doctor"); designProcessDefinition.addUserTask("step1", ACTOR_NAME); designProcessDefinition.addAutomaticTask("step2").addOperation( new OperationBuilder().createSetDocument(documentName, getDocumentValueExpressionWithUrl(url))); designProcessDefinition.addUserTask("step3", ACTOR_NAME); designProcessDefinition.addTransition("step1", "step2"); designProcessDefinition.addTransition("step2", "step3"); return deployAndEnableProcessWithActor(designProcessDefinition.done(), ACTOR_NAME, user); } @Test public void getLastVersionOfDocumentsOfAProcess() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("procWithStringIndexes", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addDescription("The doc'"); UserTaskDefinitionBuilder userTaskBuilder = designProcessDefinition.addUserTask("step1", ACTOR_NAME); userTaskBuilder.addOperation(new OperationBuilder().createSetDocument("textFile2", getDocumentValueExpressionWithUrl("http://www.example.com/new_url.txt"))); userTaskBuilder = designProcessDefinition.addUserTask("step2", ACTOR_NAME); userTaskBuilder.addOperation(new OperationBuilder().createSetDocument("textFile4", getDocumentValueExpressionWithUrl("http://www.example.com/new_url.txt"))); designProcessDefinition.addDocumentDefinition("textFile2").addContentFileName("myFile3.pdf") .addDescription("a cool text document") .addMimeType("application/atom+xml").addUrl("http://www.example.com/original_url5.txt"); designProcessDefinition.addDocumentDefinition("textFile1").addContentFileName("myFile1.pdf") .addDescription("a cool text document") .addMimeType("plain/csv").addUrl("http://www.example.com/original_url3.txt"); designProcessDefinition.addDocumentDefinition("textFile3").addContentFileName("myFile4.pdf") .addDescription("a cool text document") .addMimeType("plain/text").addUrl("http://www.example.com/original_url2.txt"); designProcessDefinition.addDocumentDefinition("textFile4").addContentFileName("myFile2.pdf") .addDescription("a cool text document") .addMimeType("application/pdf").addUrl("http://www.example.com/original_url4.txt"); designProcessDefinition.addDocumentDefinition("textFile5").addContentFileName("myFile5.pdf") .addDescription("a cool text document") .addMimeType("plain/xml").addUrl("http://www.example.com/original_url1.txt"); designProcessDefinition.addUserTask("step3", ACTOR_NAME); designProcessDefinition.addTransition("step1", "step2"); designProcessDefinition.addTransition("step2", "step3"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); check(processInstance, 1, 2, 3, 4, 5, DocumentCriterion.NAME_ASC); check(processInstance, 5, 4, 3, 2, 1, DocumentCriterion.NAME_DESC); check(processInstance, 1, 4, 2, 3, 5, DocumentCriterion.FILENAME_ASC); check(processInstance, 5, 3, 2, 4, 1, DocumentCriterion.FILENAME_DESC); check(processInstance, 2, 4, 1, 3, 5, DocumentCriterion.MIMETYPE_ASC); check(processInstance, 5, 3, 1, 4, 2, DocumentCriterion.MIMETYPE_DESC); check(processInstance, 5, 3, 1, 4, 2, DocumentCriterion.URL_ASC); check(processInstance, 2, 4, 1, 3, 5, DocumentCriterion.URL_DESC); final User john = createUser("john", "bpm"); logout(); loginOnDefaultTenantWith("john", "bpm"); assignAndExecuteStep(step1Id, john.getId()); final long step2Id = waitForUserTask(processInstance, "step2"); // user id of john > matti check(processInstance, 1, 3, 4, 5, 2, DocumentCriterion.AUTHOR_ASC); check(processInstance, 2, 1, 3, 4, 5, DocumentCriterion.AUTHOR_DESC); // Assign and execute step2 assignAndExecuteStep(step2Id, john.getId()); waitForUserTask(processInstance, "step3"); // special check because date can be too close depending on systems final List dateAsc = getProcessAPI().getLastVersionOfDocuments(processInstance.getId(), 0, 10, DocumentCriterion.CREATION_DATE_ASC); assertEquals("textFile2", dateAsc.get(3).getName()); assertEquals("textFile4", dateAsc.get(4).getName()); final List dateDesc = getProcessAPI().getLastVersionOfDocuments(processInstance.getId(), 0, 10, DocumentCriterion.CREATION_DATE_DESC); assertEquals("textFile2", dateDesc.get(1).getName()); assertEquals("textFile4", dateDesc.get(0).getName()); disableAndDeleteProcess(processDefinition); deleteUser(john); } @Test(expected = InvalidProcessDefinitionException.class) public void startProcessWithLongSizeDocumentName() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithDocumentWithLongName", "1.0"); processBuilder.addActor(ACTOR_NAME).addUserTask("step1", ACTOR_NAME); // Build fileName with 256 characters final StringBuilder builder = new StringBuilder(); for (int i = 0; i < 5; i++) { builder.append("aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee"); // + 50 characters } builder.append("12.pdf"); final String fileName = builder.toString(); assertEquals(256, fileName.length()); // Build document processBuilder.addDocumentDefinition("doc").addFile("myPdf.pdf").addContentFileName(fileName) .addMimeType("application/octet-stream"); processBuilder.getProcess(); } @Test public void startProcessWithMaxSizeDocumentName() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithDocumentWithLongName", "1.0"); processBuilder.addActor(ACTOR_NAME).addUserTask("step1", ACTOR_NAME); // Build fileName with 255 characters final StringBuilder builder = new StringBuilder(); for (int i = 0; i < 5; i++) { builder.append("aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee"); // + 50 characters } builder.append("1.pdf"); final String fileName = builder.toString(); assertEquals(255, fileName.length()); // Build document final byte[] pdfContent = "Some document content".getBytes(); processBuilder.addDocumentDefinition("doc").addFile("myPdf.pdf").addContentFileName(fileName) .addMimeType("application/octet-stream"); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processBuilder.getProcess()) .addDocumentResource(new BarResource("myPdf.pdf", pdfContent)).done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user); getProcessAPI().startProcess(processDefinition.getId()); disableAndDeleteProcess(processDefinition.getId()); } @Test(expected = InvalidProcessDefinitionException.class) public void startProcessWithLongSizeDocumentURL() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithDocumentWithLongName", "1.0"); processBuilder.addActor(ACTOR_NAME).addUserTask("step1", ACTOR_NAME); // Build URL with 256 characters final StringBuilder builder = new StringBuilder("http://intranet.bonitasoft.com/private/docStorage/"); for (int i = 0; i < 975; i++) { builder.append("a"); // + 50 characters } final String url = builder.toString(); assertEquals(1025, url.length()); // Build document final String docName = "myRtfDocument"; final DocumentDefinitionBuilder documentDefinition = processBuilder.addDocumentDefinition(docName); documentDefinition.addUrl(url); processBuilder.getProcess(); } @Test public void startProcessWithMaxSizeDocumentURL() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithDocumentWithLongName", "1.0"); processBuilder.addActor(ACTOR_NAME).addUserTask("step1", ACTOR_NAME); // Build URL with 255 characters final StringBuilder builder = new StringBuilder("http://intranet.bonitasoft.com/private/docStorage/"); for (int i = 0; i < 974; i++) { builder.append("a"); // + 50 characters } final String url = builder.toString(); assertEquals(1024, url.length()); // Build document final String docName = "myRtfDocument"; final DocumentDefinitionBuilder documentDefinition = processBuilder.addDocumentDefinition(docName); documentDefinition.addUrl(url); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processBuilder.getProcess()) .done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user); getProcessAPI().startProcess(processDefinition.getId()); disableAndDeleteProcess(processDefinition.getId()); } @Test public void startProcessWithDocumentDefinitionWithNoInitialValue() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithDocumentWithLongName", "1.0"); processBuilder.addActor(ACTOR_NAME).addUserTask("step1", ACTOR_NAME); processBuilder.addDocumentDefinition("plop"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); getProcessAPI().startProcess(processDefinition.getId()); //process should be started even if no initial value on document waitForUserTask("step1"); disableAndDeleteProcess(processDefinition.getId()); } private Expression getDocumentValueExpressionWithUrl(final String url) throws InvalidExpressionException { return new ExpressionBuilder().createGroovyScriptExpression("script", "return new org.bonitasoft.engine.bpm.document.DocumentValue(\"" + url + "\");", DocumentValue.class.getName()); } private void check(final ProcessInstance processInstance, final int one, final int two, final int three, final int four, final int five, final DocumentCriterion documentCriterion) throws ProcessInstanceNotFoundException, DocumentException { final List lastVersionOfDocuments = getProcessAPI().getLastVersionOfDocuments(processInstance.getId(), 0, 10, documentCriterion); Assertions.assertThat(lastVersionOfDocuments).as("the order was not respected for " + documentCriterion) .extracting("name").containsExactly("textFile" + one, "textFile" + two, "textFile" + three, "textFile" + four, "textFile" + five); } @Test public void getMIDocumentOnProcess() throws Exception { final ExpressionBuilder expressionBuilder = new ExpressionBuilder(); final ProcessDefinitionBuilder miBuilder = new ProcessDefinitionBuilder().createNewInstance("MI", "0.8"); miBuilder.addData("urls", List.class.getName(), expressionBuilder.createGroovyScriptExpression("urls", "[\"http://someurl\", \"http://someurl1\", \"http://someurl2\"]", List.class.getName())); final CallActivityBuilder callActivityBuilder = miBuilder.addCallActivity("mi", expressionBuilder.createConstantStringExpression("DocSubProcess"), expressionBuilder.createConstantStringExpression("0.4")); callActivityBuilder.addShortTextData("url", null); callActivityBuilder .addDataInputOperation( new OperationBuilder().createSetDataOperation("url", expressionBuilder.createDataExpression("url", String.class.getName()))) .addMultiInstance(false, "urls") .addDataInputItemRef("url") .addCompletionCondition( expressionBuilder.createGroovyScriptExpression("urls", "numberOfCompletedInstances == urls.size();", Boolean.class.getName(), expressionBuilder.createDataExpression("urls", List.class.getName()))); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("DocSubProcess", "0.4"); builder.addDocumentDefinition("caseDocument").addUrl("toto"); builder.addShortTextData("url", null); builder.addActor(ACTOR_NAME); builder.addUserTask("step1", ACTOR_NAME).addOperation( new OperationBuilder().createSetDocument("caseDocument", expressionBuilder.createGroovyScriptExpression( "addDocVersion", "import org.bonitasoft.engine.bpm.document.DocumentValue;return new DocumentValue(url);", DocumentValue.class.getName(), expressionBuilder.createDataExpression("url", String.class.getName())))); builder.addUserTask("step2", ACTOR_NAME).addTransition("step1", "step2"); final ProcessDefinition docDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final ProcessDefinition miDefinition = deployAndEnableProcess(miBuilder.done()); getProcessAPI().startProcess(miDefinition.getId()); waitForUserTaskAndExecuteIt("step1", user); waitForUserTaskAndExecuteIt("step1", user); waitForUserTaskAndExecuteIt("step1", user); waitForUserTask("step2"); waitForUserTask("step2"); waitForUserTask("step2"); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(DocumentsSearchDescriptor.DOCUMENT_URL, Order.ASC); final SearchResult searchDocuments = getProcessAPI().searchDocuments(searchOptionsBuilder.done()); assertEquals(3, searchDocuments.getCount()); final List documents = searchDocuments.getResult(); final List urls = new ArrayList<>(); urls.add(documents.get(0).getUrl()); urls.add(documents.get(1).getUrl()); urls.add(documents.get(2).getUrl()); assertEquals(asList("http://someurl", "http://someurl1", "http://someurl2"), urls); disableAndDeleteProcess(miDefinition); disableAndDeleteProcess(docDefinition); } @Test public void getDocumentOnACallActivityOfAProcess() throws Exception { final ExpressionBuilder expressionBuilder = new ExpressionBuilder(); final ProcessDefinitionBuilder spBuilder = new ProcessDefinitionBuilder().createNewInstance("SubProcess", "0.8"); spBuilder.addActor(ACTOR_NAME); spBuilder.addUserTask("step1", ACTOR_NAME); spBuilder.addDocumentDefinition("document1").addMimeType("application/octet-stream").addContentFileName("file") .addFile("file"); final byte[] pdfContent = new byte[] { 5, 0, 1, 4, 6, 5, 2, 3, 1, 5, 6, 8, 4, 6, 6, 3, 2, 4, 5 }; final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(spBuilder.getProcess()) .addDocumentResource(new BarResource("file", pdfContent)).done(); final ProcessDefinition docDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("CAProcess", "0.4"); builder.addActor(ACTOR_NAME); builder.addCallActivity("ca", expressionBuilder.createConstantStringExpression("SubProcess"), expressionBuilder.createConstantStringExpression("0.8")); final ProcessDefinition caDefinition = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); getProcessAPI().startProcess(caDefinition.getId()); final long step1Id = waitForUserTask("step1"); final Expression expression = expressionBuilder.createDocumentReferenceExpression("document1"); final Map> expressions = new HashMap<>(5); expressions.put(expression, new HashMap()); final Map docsMap = getProcessAPI().evaluateExpressionsOnActivityInstance(step1Id, expressions); assertNotNull(docsMap); final String fileName = ((Document) docsMap.get(expression.getName())).getContentFileName(); assertEquals("file", fileName); disableAndDeleteProcess(caDefinition); disableAndDeleteProcess(docDefinition); } public ProcessInstance deployAndEnableWithActorAndStartIt(final User user) throws Exception { return deployAndEnableProcessWithActorAndStartIt(getNormalBar(), user); } public BusinessArchive getNormalBar() throws InvalidProcessDefinitionException, InvalidBusinessArchiveFormatException { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process", String.valueOf(processVersion++), asList("step1", "step2"), asList(true, true), ACTOR_NAME, false); return new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition) .done(); } @Test public void processWithDocumentList() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("processWithListOfDoc", "1.0"); builder.addActor("john"); builder.addLongData("doc1Id", null); builder.addLongData("doc2Id", null); builder.addUserTask("step1", "john"); final Expression scriptExpression1 = new ExpressionBuilder() .createGroovyScriptExpression( "updateDocs", "[new org.bonitasoft.engine.bpm.document.DocumentValue(doc2Id), " + "new org.bonitasoft.engine.bpm.document.DocumentValue(\"newFile\".getBytes(),\"plain/text\",\"file.txt\")," + "new org.bonitasoft.engine.bpm.document.DocumentValue(doc1Id,\"updatedDocFromUrl\".getBytes(),\"plain/text\",\"file.txt\")]", List.class.getName(), new ExpressionBuilder().createDataExpression("doc1Id", Long.class.getName()), new ExpressionBuilder().createDataExpression("doc2Id", Long.class.getName())); final Expression scriptExpression2 = new ExpressionBuilder() .createGroovyScriptExpression( "updateDocs", "[new org.bonitasoft.engine.bpm.document.DocumentValue(\"updatedDoc\".getBytes(),\"plain/text\",\"file.txt\")]", List.class.getName(), new ExpressionBuilder().createDataExpression("doc2Id", Long.class.getName())); final UserTaskDefinitionBuilder userTaskDefinitionBuilder = builder.addUserTask("updateStep", "john"); userTaskDefinitionBuilder .addOperation(new OperationBuilder().createSetDocumentList("invoicesCopy", new ExpressionBuilder().createDocumentListExpression("invoices"))); userTaskDefinitionBuilder .addOperation(new OperationBuilder().createSetDocumentList("invoices", scriptExpression1)); userTaskDefinitionBuilder .addOperation(new OperationBuilder().createSetDocumentList("emptyList", scriptExpression2)); // userTaskDefinitionBuilder.addOperation(new OperationBuilder().createSetDocumentList("unknown", scriptExpression2)); final UserTaskDefinitionBuilder verifyStepBuilder = builder.addUserTask("verifyStep", "john"); verifyStepBuilder .addDisplayDescription(new ExpressionBuilder().createGroovyScriptExpression("getInvoicesListSize", "String.valueOf(invoices.size())", String.class.getName(), new ExpressionBuilder().createDocumentListExpression("invoices"))); builder.addTransition("step1", "updateStep"); builder.addTransition("updateStep", "verifyStep"); final DocumentListDefinitionBuilder invoices = builder.addDocumentListDefinition("invoices"); invoices.addDescription("My invoices"); final String script = "[new org.bonitasoft.engine.bpm.document.DocumentValue(\"http://www.myurl.com/mydoc.txt\"), " + "new org.bonitasoft.engine.bpm.document.DocumentValue(\"hello1\".getBytes(),\"plain/text\",\"file.txt\")," + "new org.bonitasoft.engine.bpm.document.DocumentValue(\"hello2\".getBytes(),\"plain/text\",\"file.txt\")," + "null," + "new org.bonitasoft.engine.bpm.document.DocumentValue(\"hello3\".getBytes(),\"plain/text\",\"file.txt\")," + "new org.bonitasoft.engine.bpm.contract.FileInputValue(\"file.txt\", \"hello4\".getBytes())" + "]"; invoices.addInitialValue(new ExpressionBuilder().createGroovyScriptExpression("initialDocs", script, List.class.getName())); final DocumentListDefinitionBuilder invoicesCopy = builder.addDocumentListDefinition("invoicesCopy"); invoicesCopy.addDescription("My invoices copy"); builder.addDocumentListDefinition("emptyList"); final User john = createUser("john", "bpm"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), "john", john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); //we have a process with an initialized list and a non initialized list //check with api methods List invoices1 = getProcessAPI().getDocumentList(processInstance.getId(), "invoices", 0, 100); assertThat(invoices1).hasSize(5); final Document urlDocument = invoices1.get(0); assertThat(urlDocument.getUrl()).isEqualTo("http://www.myurl.com/mydoc.txt"); final Document fileDocument = invoices1.get(1); assertThat(getProcessAPI().getDocumentContent(fileDocument.getContentStorageId())) .isEqualTo("hello1".getBytes()); final Document fileFromFileInput = invoices1.get(4); assertThat(getProcessAPI().getDocumentContent(fileFromFileInput.getContentStorageId())) .isEqualTo("hello4".getBytes()); List invoicesCopyList = getProcessAPI().getDocumentList(processInstance.getId(), "invoicesCopy", 0, 100); assertThat(invoicesCopyList).as("initial value of the list that will copy invoices").isEmpty(); List emptyList = getProcessAPI().getDocumentList(processInstance.getId(), "emptyList", 0, 100); assertThat(emptyList).isEmpty(); try { getProcessAPI().getDocumentList(processInstance.getId(), "unknown", 0, 100); fail("should not find document list unknown"); } catch (final DocumentNotFoundException e) { //ok } getProcessAPI().updateProcessDataInstance("doc1Id", processInstance.getId(), urlDocument.getId()); getProcessAPI().updateProcessDataInstance("doc2Id", processInstance.getId(), fileDocument.getId()); //execute operation to update assignAndExecuteStep(step1Id, john.getId()); waitForUserTaskAndExecuteIt(processInstance, "updateStep", john); final HumanTaskInstance verifyStep = waitForUserTaskAndGetIt("verifyStep"); //check with api methods invoices1 = getProcessAPI().getDocumentList(processInstance.getId(), "invoices", 0, 100); assertThat(invoices1).hasSize(3); final Document movedFileDocument = invoices1.get(0); assertThat(movedFileDocument.getIndex()).isEqualTo(0);// was in index 2, now in index 1 assertThat(movedFileDocument.getId()).isEqualTo(fileDocument.getId()); Document emptyListDoc = invoices1.get(1); assertThat(emptyListDoc.hasContent()).isTrue(); assertThat(emptyListDoc.getContentFileName()).isEqualTo("file.txt"); assertThat(getProcessAPI().getDocumentContent(emptyListDoc.getContentStorageId())) .isEqualTo("newFile".getBytes()); Document updatedUrlFile = invoices1.get(2); assertThat(updatedUrlFile.getId()).isEqualTo(urlDocument.getId()); assertThat(updatedUrlFile.hasContent()).isTrue(); assertThat(updatedUrlFile.getContentFileName()).isEqualTo("file.txt"); assertThat(updatedUrlFile.getVersion()).isEqualTo("2"); assertThat(new String(getProcessAPI().getDocumentContent(updatedUrlFile.getContentStorageId()))) .isEqualTo("updatedDocFromUrl"); assertThat(getProcessAPI().getDocumentList(processInstance.getId(), "invoices", 1, 1).get(0)) .isEqualTo(emptyListDoc); emptyList = getProcessAPI().getDocumentList(processInstance.getId(), "emptyList", 0, 100); assertThat(emptyList).hasSize(1); emptyListDoc = emptyList.get(0); assertThat(emptyListDoc.hasContent()).isTrue(); assertThat(emptyListDoc.getContentFileName()).isEqualTo("file.txt"); assertThat(getProcessAPI().getDocumentContent(emptyListDoc.getContentStorageId())) .isEqualTo("updatedDoc".getBytes()); invoicesCopyList = getProcessAPI().getDocumentList(processInstance.getId(), "invoicesCopy", 0, 100); assertThat(invoicesCopyList).hasSize(5); final Document urlDocumentCopy = invoicesCopyList.get(0); assertThat(urlDocumentCopy.getUrl()).isEqualTo("http://www.myurl.com/mydoc.txt"); final Document fileDocumentCopy = invoicesCopyList.get(1); assertThat(getProcessAPI().getDocumentContent(fileDocumentCopy.getContentStorageId())) .isEqualTo("hello1".getBytes()); final Document fileFromFileInputCopy = invoicesCopyList.get(4); assertThat(getProcessAPI().getDocumentContent(fileFromFileInputCopy.getContentStorageId())) .isEqualTo("hello4".getBytes()); // List unknown = getProcessAPI().getDocumentList(processInstance.getId(), "unknown"); // assertThat(unknown).hasSize(1); //modify list with api method getProcessAPI().setDocumentList(processInstance.getId(), "invoices", Collections.singletonList(new DocumentValue(updatedUrlFile.getId()))); final List invoices2 = getProcessAPI().getDocumentList(processInstance.getId(), "invoices", 0, 100); assertThat(invoices2).hasSize(1); updatedUrlFile = invoices2.get(0); assertThat(updatedUrlFile.getId()).isEqualTo(urlDocument.getId()); assertThat(updatedUrlFile.hasContent()).isTrue(); assertThat(updatedUrlFile.getContentFileName()).isEqualTo("file.txt"); assertThat(updatedUrlFile.getVersion()).isEqualTo("2"); assertThat(new String(getProcessAPI().getDocumentContent(updatedUrlFile.getContentStorageId()))) .isEqualTo("updatedDocFromUrl"); //expression is executed on the display name of the verify step, the display name is the list size assertThat(verifyStep.getDisplayDescription()).isEqualTo("3"); //update empty list to have 3 version archived getProcessAPI().setDocumentList(processInstance.getId(), "emptyList", asList(new DocumentValue(emptyListDoc.getId(), "anUrl1"), new DocumentValue("anUrl2"))); getProcessAPI().setDocumentList(processInstance.getId(), "emptyList", asList(new DocumentValue("anUrl3"), new DocumentValue("anUrl4"))); final SearchResult searchAllVersions = getProcessAPI() .searchArchivedDocuments(new SearchOptionsBuilder(0, 100) .filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()) .sort(ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC) .sort(ArchivedDocumentsSearchDescriptor.DOCUMENT_VERSION, Order.ASC).done()); assertThat(searchAllVersions.getCount()).isEqualTo(9); final List result = searchAllVersions.getResult(); assertThat(result.get(0).getName()).isEqualTo("emptyList"); assertThat(result.get(0).getVersion()).isEqualTo("1"); assertThat(result.get(1).getName()).isEqualTo("emptyList"); assertThat(result.get(1).getVersion()).isEqualTo("1"); assertThat(result.get(2).getName()).isEqualTo("emptyList"); assertThat(result.get(2).getVersion()).isEqualTo("2"); assertThat(result.get(3).getName()).isEqualTo("invoices"); assertThat(result.get(3).getVersion()).isEqualTo("1"); disableAndDeleteProcess(processDefinition); deleteUser(john); } @Test public void deleteContentOfArchivedDocumentTest() throws Exception { //given final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("processWithDocumentToDelete", "1.0"); final User john = createUser("john", "bpm"); builder.addActor("actor"); builder.addUserTask("step1", "actor"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), "actor", john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final Document doc1v1 = getProcessAPI().attachDocument(processInstance.getId(), "doc1", "fileWithContent.txt", "plain/text", "TheContent1".getBytes()); getProcessAPI().attachNewDocumentVersion(processInstance.getId(), "doc1", "fileWithContent.txt", "plain/text", "theUrl"); final Document doc1v3 = getProcessAPI().attachNewDocumentVersion(processInstance.getId(), "doc1", "fileWithContent.txt", "plain/text", "TheContent2".getBytes()); final Document doc2v1 = getProcessAPI().attachDocument(processInstance.getId(), "doc2", "fileWithContent.txt", "plain/text", "TheContent".getBytes()); final Document doc2v2 = getProcessAPI().attachNewDocumentVersion(processInstance.getId(), "doc2", "fileWithContent.txt", "plain/text", "TheContent2".getBytes()); final Document doc2v3 = getProcessAPI().attachNewDocumentVersion(processInstance.getId(), "doc2", "fileWithContent.txt", "plain/text", "TheContent3".getBytes()); //when final SearchResult archivedDocumentSearchResult = getProcessAPI().searchArchivedDocuments( new SearchOptionsBuilder(0, 100) .filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()) .filter(ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME, "doc1") .sort(ArchivedDocumentsSearchDescriptor.DOCUMENT_VERSION, Order.ASC) .done()); final ArchivedDocument archDov1v1 = archivedDocumentSearchResult.getResult().get(0); assertThat(archDov1v1.getContentStorageId()).isEqualTo(doc1v1.getContentStorageId()); getProcessAPI().deleteContentOfArchivedDocument(archDov1v1.getId()); //then assertThat(getProcessAPI().getDocumentContent(doc1v1.getContentStorageId())).isNull(); assertThat(getProcessAPI().getDocumentContent(doc1v3.getContentStorageId())).isNotNull(); assertThat(getProcessAPI().getDocumentContent(doc2v1.getContentStorageId())).isNotNull(); assertThat(getProcessAPI().getDocumentContent(doc2v2.getContentStorageId())).isNotNull(); assertThat(getProcessAPI().getDocumentContent(doc2v3.getContentStorageId())).isNotNull(); disableAndDeleteProcess(processDefinition); deleteUser(john); } @Test public void add_and_update_a_single_document() throws Exception { //process with doc1 init and doc2 non init final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithDocToUpdate", "1.0"); builder.addDocumentDefinition("doc1").addUrl("the url"); builder.addDocumentDefinition("doc2"); builder.addActor("actor").addUserTask("step1", "actor"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), "actor", user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); //add doc2 getProcessAPI().addDocument(processInstance.getId(), "doc2", "my doc", new DocumentValue("the new url")); //add doc3 getProcessAPI().addDocument(processInstance.getId(), "doc3", "my doc", new DocumentValue("the new url")); //add doc1: fail try { getProcessAPI().addDocument(processInstance.getId(), "doc1", "my doc", new DocumentValue("the new url")); fail("should not be able to add a document on an existing document"); } catch (final AlreadyExistsException e) { //ok } //add doc4 with index: fail try { getProcessAPI().addDocument(processInstance.getId(), "doc1", "my doc", new DocumentValue("the new url").setIndex(12)); fail("should not be able to add a document with index when there is no list"); } catch (final DocumentAttachmentException e) { //ok } List result = getProcessAPI().searchDocuments( new SearchOptionsBuilder(0, 100) .filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()) .sort(DocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC).done()) .getResult(); assertThat(result).hasSize(3); assertThat(result.get(0).getName()).isEqualTo("doc1"); assertThat(result.get(1).getName()).isEqualTo("doc2"); assertThat(result.get(2).getName()).isEqualTo("doc3"); //update doc1 getProcessAPI().updateDocument(result.get(0).getId(), new DocumentValue("the new url updated")); //update doc2 getProcessAPI().updateDocument(result.get(1).getId(), new DocumentValue("the new url updated")); result = getProcessAPI().searchDocuments( new SearchOptionsBuilder(0, 100) .filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()) .sort(DocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC).done()) .getResult(); assertThat(result).hasSize(3); assertThat(result.get(0).getVersion()).isEqualTo("2"); assertThat(result.get(1).getVersion()).isEqualTo("2"); assertThat(result.get(2).getVersion()).isEqualTo("1"); disableAndDeleteProcess(processDefinition); } @SuppressWarnings("unchecked") @Test public void add_and_update_a_list_of_document() throws Exception { final ProcessInstance processInstance = deployProcessWithList(); // To ensure doc1_1 is not added in the same millis as the process instance start date: Thread.sleep(20); //add doc1_1 to list1 getProcessAPI().addDocument(processInstance.getId(), "list1", "doc list", new DocumentValue("doc1_1")); //add doc1_2 to list1 with bad index: fail try { getProcessAPI().addDocument(processInstance.getId(), "list1", "doc list", new DocumentValue("doc1_2").setIndex(12)); fail("should not be able to add a document on a list with bad index"); } catch (final DocumentAttachmentException e) { //ok } //add doc1_2 to list1 with good index getProcessAPI().addDocument(processInstance.getId(), "list1", "doc list", new DocumentValue("doc1_2").setIndex(0)); waitForUserTaskAndExecuteIt(processInstance, "step1", user); final long step2Id = waitForUserTask(processInstance, "step2"); //add doc1_3 to list1 at the end getProcessAPI().addDocument(processInstance.getId(), "list1", "doc list", new DocumentValue("doc1_3")); //add doc2 to list2 final Document document = getProcessAPI().addDocument(processInstance.getId(), "list2", "doc list", new DocumentValue("doc2")); //check added final List list1 = getProcessAPI().getDocumentList(processInstance.getId(), "list1", 0, 100); final List list2 = getProcessAPI().getDocumentList(processInstance.getId(), "list2", 0, 100); final SearchResult list1_search = getProcessAPI().searchDocuments( new SearchOptionsBuilder(0, 100) .filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()) .filter(DocumentsSearchDescriptor.DOCUMENT_NAME, "list1") .sort(DocumentsSearchDescriptor.LIST_INDEX, Order.DESC).done()); final ArrayList reversedList1 = new ArrayList<>(list1); Collections.reverse(reversedList1); assertThat(list1_search.getResult()).isEqualTo(reversedList1); assertThat(list1).hasSize(7); assertThat(list1.get(0).getUrl()).isEqualTo("doc1_2"); assertThat(list1.get(5).getUrl()).isEqualTo("doc1_1"); assertThat(list1.get(6).getUrl()).isEqualTo("doc1_3"); assertThat(list2).hasSize(1); assertThat(list2.get(0).getUrl()).isEqualTo("doc2"); final Document updated = getProcessAPI().updateDocument(document.getId(), new DocumentValue("The new value")); assertThat(updated.getIndex()).isEqualTo(document.getIndex()); assertThat(updated.getUrl()).isEqualTo("The new value"); final Map> expressions = new HashMap<>(); expressions.put(new ExpressionBuilder().createDocumentListExpression("list1"), emptyMap()); final List initialList1 = (List) getProcessAPI() .evaluateExpressionsAtProcessInstanciation(processInstance.getId(), expressions) .get("list1"); try { assertThat(initialList1).hasSize(4); } catch (Exception e) { for (Document doc : initialList1) { APITestUtil.LOGGER.debug("{}: {}", doc.getUrl(), doc.getCreationDate().getTime()); } throw e; } assertThat(initialList1.get(0).getUrl()).isEqualTo("http://www.myurl.com/mydoc.txt"); assertThat(new String(getProcessAPI().getDocumentContent(initialList1.get(1).getContentStorageId()))) .isEqualTo("hello1"); assertThat(new String(getProcessAPI().getDocumentContent(initialList1.get(2).getContentStorageId()))) .isEqualTo("hello2"); assertThat(new String(getProcessAPI().getDocumentContent(initialList1.get(3).getContentStorageId()))) .isEqualTo("hello3"); assignAndExecuteStep(step2Id, user.getId()); waitForProcessToFinish(processInstance.getId()); final List finalList1 = (List) getProcessAPI() .evaluateExpressionOnCompletedProcessInstance(processInstance.getId(), expressions) .get("list1"); assertThat(finalList1).hasSize(7); disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } @Test public void should_create_and_update_document_list_using_contract() throws Exception { User user = getIdentityAPI().createUser("james", "bpm"); ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithDocFromContract", "1.0"); builder.addContract().addFileInput("fileInputValues", "create my list of document", true); builder.addDocumentListDefinition("myDocumentList") .addInitialValue( new ExpressionBuilder().createContractInputExpression("fileInputValues", List.class.getName())); builder.addActor("actor"); UserTaskDefinitionBuilder userTaskBuilder = builder.addUserTask("updateList", "actor"); userTaskBuilder.addOperation(new OperationBuilder().createSetDocumentList("myDocumentList", new ExpressionBuilder().createContractInputExpression("fileInputValues", List.class.getName()))); userTaskBuilder.addContract().addFileInput("fileInputValues", "update my list of document", true); builder.addUserTask("checkListIsUpdated", "actor"); builder.addTransition("updateList", "checkListIsUpdated"); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), "actor", user); ArrayList createList = new ArrayList<>(asList( new FileInputValue("file1", "text/plain", "the content".getBytes()), new FileInputValue("file2", "text/plain", "the content".getBytes()), new FileInputValue("file3", "text/plain", "the content".getBytes()))); ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(processDefinition.getId(), singletonMap("fileInputValues", createList)); long updateListTask = waitForUserTask("updateList"); //verify list is created List myDocumentList = getProcessAPI().getDocumentList(processInstance.getId(), "myDocumentList", 0, 100); assertThat(myDocumentList).hasSize(3); assertThat(myDocumentList).extracting("fileName").containsExactly("file1", "file2", "file3"); getProcessAPI().assignUserTask(updateListTask, user.getId()); ArrayList updateList = new ArrayList<>(asList( new FileInputValue("file4", "text/plain", "the content".getBytes()), new FileInputValue("file3", "text/plain", null, String.valueOf(myDocumentList.get(2).getId())), new FileInputValue("file2", "text/plain", "updated content of file 2".getBytes(), String.valueOf(myDocumentList.get(1).getId())), new FileInputValue("updated file name", "updated content type", "updated content of file 1".getBytes(), String.valueOf(myDocumentList.get(0).getId())))); getProcessAPI().executeUserTask(user.getId(), updateListTask, singletonMap("fileInputValues", updateList)); waitForUserTask("checkListIsUpdated"); //verify list is update List myUpdatedDocumentList = getProcessAPI().getDocumentList(processInstance.getId(), "myDocumentList", 0, 100); assertThat(myUpdatedDocumentList).hasSize(4); //added document assertThat(myUpdatedDocumentList.get(0)).matches(d -> d.getContentFileName().equals("file4")); //moved document assertThat(myUpdatedDocumentList.get(1)).matches(d -> d.getContentFileName().equals("file3") && d.getContentStorageId().equals(myDocumentList.get(2).getContentStorageId()) && d.getId() == myDocumentList.get(2).getId()); //document with content updated assertThat(myUpdatedDocumentList.get(2)).matches(d -> d.getContentFileName().equals("file2") && !d.getContentStorageId().equals(myDocumentList.get(1).getContentStorageId()) && d.getId() == myDocumentList.get(1).getId()); //document with content updated and file name assertThat(myUpdatedDocumentList.get(3)).matches(d -> d.getContentFileName().equals("updated file name") && d.getContentMimeType().equals("updated content type") && !d.getContentStorageId().equals(myDocumentList.get(0).getContentStorageId()) && d.getId() == myDocumentList.get(0).getId()); disableAndDeleteProcess(processDefinition); } @Test public void should_create_and_update_document_using_contract() throws Exception { User user = getIdentityAPI().createUser("james", "bpm"); ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithDocFromContract", "1.0"); builder.addContract().addFileInput("fileInputValue", "create my document"); builder.addDocumentDefinition("myDocument") .addInitialValue(new ExpressionBuilder().createContractInputExpression("fileInputValue", FileInputValue.class.getName())); builder.addActor("actor"); UserTaskDefinitionBuilder userTaskBuilder = builder.addUserTask("update", "actor"); userTaskBuilder.addOperation(new OperationBuilder().createSetDocument("myDocument", new ExpressionBuilder() .createContractInputExpression("fileInputValue", FileInputValue.class.getName()))); userTaskBuilder.addContract().addFileInput("fileInputValue", "update my document"); builder.addUserTask("checkIsUpdated", "actor"); builder.addTransition("update", "checkIsUpdated"); ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.getProcess(), "actor", user); ProcessInstance processInstance = getProcessAPI().startProcessWithInputs(processDefinition.getId(), singletonMap("fileInputValue", new FileInputValue("file1", "text/plain", "the content".getBytes()))); long checkListIsCreated = waitForUserTask("update"); //verify list is created Document myDocument = getProcessAPI().getLastDocument(processInstance.getId(), "myDocument"); assertThat(myDocument.getContentFileName()).isEqualTo("file1"); getProcessAPI().assignUserTask(checkListIsCreated, user.getId()); getProcessAPI().executeUserTask(user.getId(), checkListIsCreated, singletonMap("fileInputValue", new FileInputValue("file1", "text/plain", "the content".getBytes(), String.valueOf(myDocument.getId())))); waitForUserTask("checkIsUpdated"); //verify list is update Document myUpdatedDocument = getProcessAPI().getLastDocument(processInstance.getId(), "myDocument"); //document with content updated assertThat(myUpdatedDocument.getId()).isEqualTo(myDocument.getId()); assertThat(myUpdatedDocument.getContentStorageId()).isNotEqualTo(myDocument.getContentStorageId()); disableAndDeleteProcess(processDefinition); } @Test public void removeDocumentFromList() throws Exception { final ProcessInstance processInstance = deployProcessWithList(); final List list1 = getProcessAPI().getDocumentList(processInstance.getId(), "list1", 0, 100); final Document document = getProcessAPI().removeDocument(list1.get(1).getId()); assertThat(document).isEqualTo(list1.get(1)); list1.remove(1); final List updatedList = getProcessAPI().getDocumentList(processInstance.getId(), "list1", 0, 100); assertThat(updatedList.get(0).getIndex()).isEqualTo(0); assertThat(updatedList.get(1).getIndex()).isEqualTo(1); assertThat(updatedList.get(2).getIndex()).isEqualTo(2); assertThat(updatedList.get(0).getId()).isEqualTo(list1.get(0).getId()); assertThat(updatedList.get(1).getId()).isEqualTo(list1.get(1).getId()); assertThat(updatedList.get(2).getId()).isEqualTo(list1.get(2).getId()); disableAndDeleteProcess(processInstance.getProcessDefinitionId()); } private ProcessInstance deployProcessWithList() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithDocToUpdate", "1.0"); //process with list1 with init value final String script = "[new org.bonitasoft.engine.bpm.document.DocumentValue(\"http://www.myurl.com/mydoc.txt\"), " + "new org.bonitasoft.engine.bpm.document.DocumentValue(\"hello1\".getBytes(),\"plain/text\",\"file1.txt\")," + "new org.bonitasoft.engine.bpm.document.DocumentValue(\"hello2\".getBytes(),\"plain/text\",\"file2.txt\")," + "new org.bonitasoft.engine.bpm.document.DocumentValue(\"hello3\".getBytes(),\"plain/text\",\"file3.txt\")" + "]"; builder.addDocumentListDefinition("list1") .addInitialValue(new ExpressionBuilder().createGroovyScriptExpression("initialDocs", script, List.class.getName())); //process with list2 without initial value builder.addDocumentListDefinition("list2"); builder.addActor("actor").addUserTask("step1", "actor").addUserTask("step2", "actor"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(builder.done(), "actor", user); return getProcessAPI().startProcess(processDefinition.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/instance/AbortProcessInstanceIT.java ================================================ /** * 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.process.instance; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertEquals; import java.util.List; import java.util.concurrent.Callable; import org.bonitasoft.engine.bpm.NamedElement; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.test.TestStates; import org.junit.Ignore; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AbortProcessInstanceIT extends AbstractProcessInstanceIT { private static final Logger logger = LoggerFactory.getLogger(AbortProcessInstanceIT.class); private ProcessDefinition deployProcessWithMultiInstanceCallActivity(final int loopCardinality, final String targetProcess, final String targetVersion) throws Exception { final Expression targetProcExpr = string(targetProcess); final Expression targetVersionExpr = string(targetVersion); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("RemainingInstancesAreAbortedAfterCompletionCondition", "1.0"); builder.addStartEvent("start"); builder.addCallActivity("callActivity", targetProcExpr, targetVersionExpr) .addMultiInstance(false, intExpr(loopCardinality)) .addCompletionCondition( new ExpressionBuilder() .createGroovyScriptExpression("deployProcessWithMultiInstanceCallActivity", ExpressionConstants.NUMBER_OF_COMPLETED_INSTANCES.getEngineConstantName() + " == 1 ", Boolean.class.getName(), new ExpressionBuilder().createEngineConstant( ExpressionConstants.NUMBER_OF_COMPLETED_INSTANCES))); builder.addEndEvent("end"); builder.addTransition("start", "callActivity"); builder.addTransition("callActivity", "end"); return deployAndEnableProcess(builder.done()); } @Test public void abortProcessWithHumanTasks() throws Exception { final String taskName1 = "userTask1"; final String taskName2 = "userTask2"; final String autoTaskName = "auto1"; final ProcessDefinition targetProcess = deployProcessWith2UserTasksAnd1AutoTask(taskName1, taskName2, autoTaskName); final int loopCardinality = 2; final ProcessDefinition parentProcess = deployProcessWithMultiInstanceCallActivity(loopCardinality, targetProcess.getName(), targetProcess.getVersion()); final ProcessInstance parentProcessInstance = getProcessAPI().startProcess(parentProcess.getId()); checkNbOfProcessInstances(loopCardinality + 1); // execute task1 of a target process instance final List pendingHumanTaskInstances = checkNbPendingTaskOf(true, 2 * loopCardinality, user) .getPendingHumanTaskInstances(); final HumanTaskInstance humanTaskInst1ToExecute = pendingHumanTaskInstances.get(0); assertEquals(taskName1, humanTaskInst1ToExecute.getName()); assignAndExecuteStep(humanTaskInst1ToExecute, user); final HumanTaskInstance humanTaskInst1ToAbort = pendingHumanTaskInstances.get(1); assertEquals(taskName1, humanTaskInst1ToAbort.getName()); final long toBeAbortedProcInstId = humanTaskInst1ToAbort.getParentProcessInstanceId(); final ProcessInstance procInstToAbort = getProcessAPI().getProcessInstance(toBeAbortedProcInstId); // execute task2 of same target process instance HumanTaskInstance humanTaskInst2ToExecute = pendingHumanTaskInstances.get(2); assertEquals(taskName2, humanTaskInst2ToExecute.getName()); HumanTaskInstance humanTaskInst2ToAbort = pendingHumanTaskInstances.get(3); assertEquals(taskName2, humanTaskInst2ToAbort.getName()); if (humanTaskInst1ToExecute.getParentProcessInstanceId() != humanTaskInst2ToExecute .getParentProcessInstanceId()) { // ensure tasks are in the same humanTaskInst2ToAbort = humanTaskInst2ToExecute; humanTaskInst2ToExecute = pendingHumanTaskInstances.get(3); } assignAndExecuteStep(humanTaskInst2ToExecute, user.getId()); // the target process instances that exceed the max loop must be in aborted state waitForProcessToBeInState(procInstToAbort, ProcessInstanceState.ABORTED); // task1 not executed must be in aborted state waitForFlowNodeInState(parentProcessInstance, humanTaskInst1ToAbort.getName(), TestStates.ABORTED, true); // task2 not executed must be in aborted state waitForFlowNodeInState(parentProcessInstance, humanTaskInst2ToAbort.getName(), TestStates.ABORTED, true); // check the automatic task in the aborted process instance was not created checkWasntExecuted(procInstToAbort, autoTaskName); // the parent process instance must finish in normal state waitForProcessToFinish(parentProcessInstance); disableAndDeleteProcess(parentProcess); disableAndDeleteProcess(targetProcess); } @Test public void should_abort_stable_and_non_stable_flow_nodes() throws Exception { executeAndVerifyCompleted(() -> new ProcessDefinitionBuilder().createNewInstance("processWithTerminate", "3.0") .addAutomaticTask("step1") .addAutomaticTask("step2") .addAutomaticTask("step3") .addAutomaticTask("step4") .addEndEvent("terminateEnd").addTerminateEventTrigger() .addAutomaticTask("step5") .addAutomaticTask("step6") .addAutomaticTask("step7") .addAutomaticTask("step8") .addAutomaticTask("step9") .addAutomaticTask("step10") .getProcess()); shouldNotHaveFailedTasks(); shouldAllCompleteWithAbortedOrCompleted(); assertThat(getAllCompletedArchivedFlowNodeInstances().stream().map(NamedElement::getName)) .containsOnly("step1", "step2", "step3", "step4", "step5", "step6", "step7", "step8", "step9", "step10"); } @Test public void should_abort_executing_loops() throws Exception { executeAndVerifyCompleted(() -> new ProcessDefinitionBuilder().createNewInstance("processWithTerminate", "1.0") .addAutomaticTask("step1").addLoop(false, bool(true)) .addAutomaticTask("step2").addLoop(false, bool(true)) .addAutomaticTask("step3").addLoop(false, bool(true)) .addAutomaticTask("step4").addLoop(false, bool(true)) .addAutomaticTask("step5").addLoop(false, bool(true)) .addAutomaticTask("step6").addLoop(false, bool(true)) .addAutomaticTask("step7").addLoop(false, bool(true)) .addAutomaticTask("step8").addLoop(false, bool(true)) .addAutomaticTask("step9").addLoop(false, bool(true)) .addAutomaticTask("step10").addLoop(false, bool(true)) .addAutomaticTask("step_before_abort").addEndEvent("terminateEnd").addTerminateEventTrigger() .addTransition("step_before_abort", "terminateEnd") .getProcess()); shouldNotHaveFailedTasks(); shouldAllCompleteWithAbortedOrCompleted(); assertThat(getAllCompletedArchivedFlowNodeInstances().stream().map(NamedElement::getName)) .contains("step1", "step2", "step3", "step4", "step5", "step6", "step7", "step8", "step9", "step10", "step_before_abort"); } private void shouldAllCompleteWithAbortedOrCompleted() throws SearchException { assertThat(getAllCompletedArchivedFlowNodeInstances()) .allSatisfy(a -> assertThat(a.getState()).isIn("aborted", "completed")); } @Test public void should_abort_executing_multiInstances() throws Exception { executeAndVerifyCompleted(() -> { ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("processWithTerminate", "1.0"); builder.addAutomaticTask("step1").addMultiInstance(false, intExpr(5)); builder.addAutomaticTask("step2").addMultiInstance(false, intExpr(5)); builder.addAutomaticTask("step3").addMultiInstance(false, intExpr(5)); builder.addAutomaticTask("step4").addMultiInstance(false, intExpr(5)); builder.addAutomaticTask("step5").addMultiInstance(false, intExpr(5)); builder.addAutomaticTask("step6").addMultiInstance(false, intExpr(5)); builder.addAutomaticTask("step7").addMultiInstance(false, intExpr(5)); builder.addAutomaticTask("step8").addMultiInstance(false, intExpr(5)); builder.addAutomaticTask("step9").addMultiInstance(false, intExpr(5)); builder.addAutomaticTask("step10").addMultiInstance(false, intExpr(5)); return builder .addAutomaticTask("step_before_abort").addEndEvent("terminateEnd").addTerminateEventTrigger() .addTransition("step_before_abort", "terminateEnd") .getProcess(); }); shouldNotHaveFailedTasks(); shouldAllCompleteWithAbortedOrCompleted(); assertThat(getAllCompletedArchivedFlowNodeInstances().stream().map(NamedElement::getName)) .contains("step1", "step2", "step3", "step4", "step5", "step6", "step7", "step8", "step9", "step10", "step_before_abort"); } @Test @Ignore("not working need the retry mechanism, the fix BR454 is needed ") public void should_abort_call_activities_from_calledProcess_with_event_subprocess() throws Exception { ProcessDefinition calledProcess = getProcessAPI() .deployAndEnableProcess(new ProcessDefinitionBuilder().createNewInstance("calledProcess", "4.0") .addAutomaticTask("sub1") .addAutomaticTask("sub2") .addAutomaticTask("sub3") .addAutomaticTask("sub4").getProcess()); ProcessDefinition endErrorProcess = getProcessAPI() .deployAndEnableProcess(new ProcessDefinitionBuilder().createNewInstance("endErrorProcess", "4.0") .addAutomaticTask("sub1") .addEndEvent("terminate").addErrorEventTrigger("theError") .addTransition("sub1", "terminate") .getProcess()); ProcessDefinition processDefinition = executeAndVerifyAborted(() -> { ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("processWithTerminate", "4.0"); builder.addCallActivity("step1", string("calledProcess"), string("4.0")); builder.addCallActivity("step2", string("calledProcess"), string("4.0")).addMultiInstance(false, intExpr(5)); builder.addCallActivity("step3", string("calledProcess"), string("4.0"));//.addLoop(false, bool(true)); builder.addCallActivity("step4", string("calledProcess"), string("4.0")); builder.addCallActivity("step5", string("calledProcess"), string("4.0")); builder.addCallActivity("step6", string("calledProcess"), string("4.0")); builder.addCallActivity("step7", string("calledProcess"), string("4.0")); builder.addCallActivity("step10", string("endErrorProcess"), string("4.0")); builder.addCallActivity("step8", string("calledProcess"), string("4.0")); builder.addCallActivity("step9", string("calledProcess"), string("4.0")); builder.addSubProcess("errorEventSub", true).getSubProcessBuilder() .addStartEvent("startError").addErrorEventTrigger("theError") .addAutomaticTask("stepToHandleError") .addTransition("startError", "stepToHandleError"); return builder .getProcess(); }); shouldNotHaveFailedTasks(); assertThat(getAllCompletedArchivedFlowNodeInstances().stream().map(NamedElement::getName)) .contains("stepToHandleError"); shouldAllCompleteWithAbortedOrCompleted(); disableAndDeleteProcess(calledProcess); disableAndDeleteProcess(endErrorProcess); disableAndDeleteProcess(processDefinition); } @Test @Ignore("not working need the retry mechanism, the fix BR454 is needed ") public void should_abort_elements_from_calledProcess_with_event_subprocess() throws Exception { ProcessDefinition endErrorProcess = getProcessAPI() .deployAndEnableProcess(new ProcessDefinitionBuilder().createNewInstance("endErrorProcess", "2.0") .addAutomaticTask("sub1") .addEndEvent("terminate").addErrorEventTrigger("theError") .addTransition("sub1", "terminate") .getProcess()); ProcessDefinition processDefinition = executeAndVerifyAborted(() -> { ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("processWithCalledProcess", "2.0"); builder.addAutomaticTask("step1"); builder.addAutomaticTask("step2").addMultiInstance(false, intExpr(5)); builder.addAutomaticTask("step3").addLoop(false, bool(true)); builder.addAutomaticTask("step4"); builder.addAutomaticTask("step5"); builder.addCallActivity("step10", string("endErrorProcess"), string("2.0")); builder.addAutomaticTask("step6"); builder.addAutomaticTask("step7"); builder.addAutomaticTask("step8"); builder.addAutomaticTask("step9"); builder.addSubProcess("errorEventSub", true).getSubProcessBuilder() .addStartEvent("startError").addErrorEventTrigger("theError") .addAutomaticTask("stepToHandleError") .addTransition("startError", "stepToHandleError"); return builder .getProcess(); }); shouldNotHaveFailedTasks(); assertThat(getAllCompletedArchivedFlowNodeInstances().stream().map(NamedElement::getName)) .contains("stepToHandleError"); shouldAllCompleteWithAbortedOrCompleted(); disableAndDeleteProcess(endErrorProcess); disableAndDeleteProcess(processDefinition); } @Test @Ignore("not working need the retry mechanism, the fix BR454 is needed ") public void should_abort_call_activities_from_parent_processes() throws Exception { ProcessDefinition calledProcess = getProcessAPI() .deployAndEnableProcess(new ProcessDefinitionBuilder().createNewInstance("calledProcess", "1.0") .addAutomaticTask("sub1") .addAutomaticTask("sub2") .addAutomaticTask("sub3") .addAutomaticTask("sub4").getProcess()); executeAndVerifyCompleted(() -> { ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("processWithTerminate", "1.0"); builder.addCallActivity("step1", string("calledProcess"), string("1.0")); builder.addCallActivity("step2", string("calledProcess"), string("1.0")).addMultiInstance(false, intExpr(5)); builder.addCallActivity("step3", string("calledProcess"), string("1.0")).addLoop(false, bool(true)); builder.addCallActivity("step4", string("calledProcess"), string("1.0")); builder.addCallActivity("step5", string("calledProcess"), string("1.0")); builder.addCallActivity("step6", string("calledProcess"), string("1.0")); builder.addCallActivity("step7", string("calledProcess"), string("1.0")); builder.addCallActivity("step8", string("calledProcess"), string("1.0")); builder.addCallActivity("step9", string("calledProcess"), string("1.0")); builder.addCallActivity("step10", string("calledProcess"), string("1.0")); return builder .addAutomaticTask("step_before_abort").addEndEvent("terminateEnd").addTerminateEventTrigger() .addTransition("step_before_abort", "terminateEnd") .getProcess(); }); shouldNotHaveFailedTasks(); assertThat(getAllCompletedArchivedFlowNodeInstances().stream().map(NamedElement::getName)) .contains("step1", "step2", "step3", "step4", "step5", "step6", "step7", "step8", "step9", "step10", "step_before_abort"); shouldAllCompleteWithAbortedOrCompleted(); disableAndDeleteProcess(calledProcess); } @Test public void should_abort_or_cancel_all_flow_nodes_including_boundary_events_when_process_is_aborted_or_cancelled() throws Exception { // given: ProcessDefinition processDefinition = deployAndEnableProcessWithActor(new ProcessDefinitionBuilder() .createNewInstance("process to be aborted with boundary", "2.a") .addActor("actor", true) .addStartEvent("start") .addUserTask("toBeAborted", "actor").addBoundaryEvent("boundary").addSignalEventTrigger("theSignal") .addUserTask("terminateTask", "actor") .addEndEvent("end").addTerminateEventTrigger() .addTransition("start", "toBeAborted") .addTransition("start", "terminateTask") .addTransition("boundary", "end") .addTransition("terminateTask", "end").getProcess(), "actor", user); // when: getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask("toBeAborted"); waitForUserTaskAndExecuteIt("terminateTask", user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "toBeAborted"); getProcessAPI().cancelProcessInstance(processInstance.getId()); // then: await().until( () -> getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.LAST_UPDATE_ASC), hasSize(0)); assertThat(getProcessAPI().searchFlowNodeInstances(new SearchOptionsBuilder(0, 100).done()).getResult()) .hasSize(0); await().until( () -> getProcessAPI() .searchArchivedFlowNodeInstances(new SearchOptionsBuilder(0, 100) .filter(ArchivedFlowNodeInstanceSearchDescriptor.NAME, "toBeAborted") .filter(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, "aborted") .done()) .getResult(), hasSize(1)); await().until( () -> getProcessAPI() .searchArchivedFlowNodeInstances(new SearchOptionsBuilder(0, 100) .filter(ArchivedFlowNodeInstanceSearchDescriptor.NAME, "toBeAborted") .filter(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, "cancelled") .done()) .getResult(), hasSize(1)); disableAndDeleteProcess(processDefinition); } private void executeAndVerifyCompleted(Callable callable) throws Exception { ProcessDefinition processDefinition = getProcessAPI().deployAndEnableProcess(callable.call()); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); try { waitForProcessToFinish(processInstance); } catch (Exception e) { logger.error("error while waiting for process to finish"); ArchivedProcessInstance finalArchivedProcessInstance = getProcessAPI() .getFinalArchivedProcessInstance(processInstance.getId()); logger.error("final archive: state={}, endDate={}", finalArchivedProcessInstance.getState(), finalArchivedProcessInstance.getEndDate()); getProcessAPI().searchProcessInstances(new SearchOptionsBuilder(0, 100).done()).getResult() .forEach(a -> logger.error("process found at the end: {}", a)); getProcessAPI().searchFlowNodeInstances(new SearchOptionsBuilder(0, 100).done()).getResult() .forEach(a -> logger.error("flow node found at the end: {}", a)); throw e; } } private ProcessDefinition executeAndVerifyAborted(Callable callable) throws Exception { ProcessDefinition processDefinition = getProcessAPI().deployAndEnableProcess(callable.call()); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); try { waitForProcessToBeInState(processInstance, ProcessInstanceState.ABORTED); } catch (Exception e) { logger.error("error while waiting for process to be aborted"); ArchivedProcessInstance finalArchivedProcessInstance = getProcessAPI() .getFinalArchivedProcessInstance(processInstance.getId()); logger.error("final archive: state={}, endDate={}", finalArchivedProcessInstance.getState(), finalArchivedProcessInstance.getEndDate()); getProcessAPI().searchProcessInstances(new SearchOptionsBuilder(0, 100).done()).getResult() .forEach(a -> logger.error("process found at the end: {}", a)); getProcessAPI().searchFlowNodeInstances(new SearchOptionsBuilder(0, 100).done()).getResult() .forEach(a -> logger.error("flow node found at the end: {}", a)); throw e; } return processDefinition; } private Expression bool(boolean b) throws InvalidExpressionException { return new ExpressionBuilder().createConstantBooleanExpression(b); } private Expression intExpr(int i) throws InvalidExpressionException { return new ExpressionBuilder().createConstantIntegerExpression(i); } private Expression string(String calledProcess) throws InvalidExpressionException { return new ExpressionBuilder().createConstantStringExpression(calledProcess); } private void shouldNotHaveFailedTasks() throws SearchException { assertThat(getProcessAPI().searchArchivedFlowNodeInstances(new SearchOptionsBuilder(0, 1000).done()).getResult() .stream().map(ArchivedFlowNodeInstance::getState)) .doesNotContain("failed"); } private List getAllCompletedArchivedFlowNodeInstances() throws SearchException { return getProcessAPI() .searchArchivedFlowNodeInstances(new SearchOptionsBuilder(0, 100) .filter(ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL, true).done()) .getResult(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/instance/AbstractProcessInstanceIT.java ================================================ /** * 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.process.instance; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; public abstract class AbstractProcessInstanceIT extends TestWithUser { protected ProcessDefinition deployProcessWith2UserTasksAnd1AutoTask(final String taskName1, final String taskName2, final String autoTaskName) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("My_Process", "1.0"); processDefinitionBuilder.addStartEvent("start"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addUserTask(taskName1, ACTOR_NAME); processDefinitionBuilder.addUserTask(taskName2, ACTOR_NAME); processDefinitionBuilder.addAutomaticTask(autoTaskName); processDefinitionBuilder.addEndEvent("end1"); processDefinitionBuilder.addEndEvent("end2"); processDefinitionBuilder.addTransition("start", taskName1); processDefinitionBuilder.addTransition(taskName1, autoTaskName); processDefinitionBuilder.addTransition(autoTaskName, "end1"); processDefinitionBuilder.addTransition("start", taskName2); processDefinitionBuilder.addTransition(taskName2, "end2"); return deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); } protected ProcessDefinition deployProcessWith2AutomaticTasks(final String taskName1, final String taskName2) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("My_Process", "1.0"); processDefinitionBuilder.addStartEvent("start"); processDefinitionBuilder.addAutomaticTask(taskName1); processDefinitionBuilder.addAutomaticTask(taskName2); processDefinitionBuilder.addEndEvent("end1"); processDefinitionBuilder.addEndEvent("end2"); processDefinitionBuilder.addTransition("start", taskName1); processDefinitionBuilder.addTransition(taskName1, "end1"); processDefinitionBuilder.addTransition("start", taskName2); processDefinitionBuilder.addTransition(taskName2, "end2"); return deployAndEnableProcess(processDefinitionBuilder.done()); } protected ProcessDefinition deployProcessWithCallActivity(final String taskName1, final String callActivityName, final String targetProcess, final String targetVersion, final String taskName2) throws Exception { final Expression targetProcessExpr = new ExpressionBuilder().createConstantStringExpression(targetProcess); final Expression targetVersionExpr = new ExpressionBuilder().createConstantStringExpression(targetVersion); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("Process with call activity", "1.0"); processDefinitionBuilder.addStartEvent("start"); processDefinitionBuilder.addAutomaticTask(taskName1); processDefinitionBuilder.addCallActivity(callActivityName, targetProcessExpr, targetVersionExpr); processDefinitionBuilder.addAutomaticTask(taskName2); processDefinitionBuilder.addEndEvent("end"); processDefinitionBuilder.addTransition("start", taskName1); processDefinitionBuilder.addTransition(taskName1, callActivityName); processDefinitionBuilder.addTransition(callActivityName, taskName2); processDefinitionBuilder.addTransition(taskName2, "end"); return deployAndEnableProcess(processDefinitionBuilder.done()); } protected ProcessDefinition deployProcessWithIntermediateThrowMessageEvent(final String eventName, final String messageName, final String targetProcessName, final String targetFlowNodeName) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("My_Process", "1.0"); processDefinitionBuilder.addStartEvent("start"); processDefinitionBuilder.addAutomaticTask("auto1"); // create expression for target process/flowNode final Expression targetProcessExpression = new ExpressionBuilder() .createConstantStringExpression(targetProcessName); final Expression targetFlowNodeExpression = new ExpressionBuilder() .createConstantStringExpression(targetFlowNodeName); processDefinitionBuilder.addIntermediateThrowEvent(eventName).addMessageEventTrigger(messageName, targetProcessExpression, targetFlowNodeExpression); processDefinitionBuilder.addAutomaticTask("auto2"); processDefinitionBuilder.addEndEvent("end"); processDefinitionBuilder.addTransition("start", "auto1"); processDefinitionBuilder.addTransition("auto1", eventName); processDefinitionBuilder.addTransition(eventName, "auto2"); processDefinitionBuilder.addTransition("auto2", "end"); return deployAndEnableProcess(processDefinitionBuilder.done()); } protected ProcessDefinition deployProcessWithIntermediateThrowSignalEvent(final String eventName, final String signalName) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("My_Process", "1.0"); processDefinitionBuilder.addStartEvent("start"); processDefinitionBuilder.addAutomaticTask("auto1"); processDefinitionBuilder.addIntermediateThrowEvent(eventName).addSignalEventTrigger(signalName); processDefinitionBuilder.addAutomaticTask("auto2"); processDefinitionBuilder.addEndEvent("end"); processDefinitionBuilder.addTransition("start", "auto1"); processDefinitionBuilder.addTransition("auto1", eventName); processDefinitionBuilder.addTransition(eventName, "auto2"); processDefinitionBuilder.addTransition("auto2", "end"); return deployAndEnableProcess(processDefinitionBuilder.done()); } protected ProcessDefinition deployProcessWithIntermediateCatchMessageEvent(final String eventName, final String messageName, final String previousStep, final String nextStep) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("My_Process", "1.0"); processDefinitionBuilder.addStartEvent("start"); processDefinitionBuilder.addAutomaticTask(previousStep); processDefinitionBuilder.addIntermediateCatchEvent(eventName).addMessageEventTrigger(messageName); processDefinitionBuilder.addAutomaticTask(nextStep); processDefinitionBuilder.addEndEvent("end"); processDefinitionBuilder.addTransition("start", previousStep); processDefinitionBuilder.addTransition(previousStep, eventName); processDefinitionBuilder.addTransition(eventName, nextStep); processDefinitionBuilder.addTransition(nextStep, "end"); return deployAndEnableProcess(processDefinitionBuilder.done()); } protected ProcessDefinition deployProcessWithIntermediateCatchSignalEvent(final String eventName, final String signalName, final String previousStep, final String nextStep) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("My_Process", "1.0"); processDefinitionBuilder.addStartEvent("start"); processDefinitionBuilder.addAutomaticTask("auto1"); processDefinitionBuilder.addIntermediateCatchEvent(eventName).addSignalEventTrigger(signalName); processDefinitionBuilder.addAutomaticTask(nextStep); processDefinitionBuilder.addEndEvent("end"); processDefinitionBuilder.addTransition("start", previousStep); processDefinitionBuilder.addTransition(previousStep, eventName); processDefinitionBuilder.addTransition(eventName, nextStep); processDefinitionBuilder.addTransition(nextStep, "end"); return deployAndEnableProcess(processDefinitionBuilder.done()); } protected ProcessDefinition deployProcessWithParallelGateways() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", "1.0"); processDefinitionBuilder.addAutomaticTask("step1"); processDefinitionBuilder.addAutomaticTask("step2"); processDefinitionBuilder.addAutomaticTask("step3"); processDefinitionBuilder.addAutomaticTask("step4"); processDefinitionBuilder.addGateway("gateway1", GatewayType.PARALLEL); processDefinitionBuilder.addGateway("gateway2", GatewayType.PARALLEL); processDefinitionBuilder.addTransition("step1", "gateway1"); processDefinitionBuilder.addTransition("gateway1", "step2"); processDefinitionBuilder.addTransition("gateway1", "step3"); processDefinitionBuilder.addTransition("step2", "gateway2"); processDefinitionBuilder.addTransition("step3", "gateway2"); processDefinitionBuilder.addTransition("gateway2", "step4"); return deployAndEnableProcess(processDefinitionBuilder.done()); } protected ProcessDefinition deployProcessWithExclusiveSplitGateway() throws Exception { final Expression trueExpression = new ExpressionBuilder().createConstantBooleanExpression(true); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_parallel_gateway", "1.0"); processDefinitionBuilder.addAutomaticTask("step1"); processDefinitionBuilder.addAutomaticTask("step2"); processDefinitionBuilder.addAutomaticTask("step3"); processDefinitionBuilder.addAutomaticTask("step4"); processDefinitionBuilder.addGateway("gateway1", GatewayType.EXCLUSIVE); processDefinitionBuilder.addTransition("step1", "gateway1"); processDefinitionBuilder.addTransition("gateway1", "step2", trueExpression); processDefinitionBuilder.addTransition("gateway1", "step3", trueExpression); processDefinitionBuilder.addDefaultTransition("gateway1", "step4"); return deployAndEnableProcess(processDefinitionBuilder.done()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/instance/CancelProcessInstanceIT.java ================================================ /** * 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.process.instance; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.Serializable; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.WaitingEvent; import org.bonitasoft.engine.bpm.flownode.WaitingEventSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion; import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.TestStates; import org.bonitasoft.engine.test.check.CheckNbOfActivities; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class CancelProcessInstanceIT extends AbstractProcessInstanceIT { private static final String SEARCH_WAITING_EVENTS_COMMAND = "searchWaitingEventsCommand"; private static final String SEARCH_OPTIONS_KEY = "searchOptions"; @Test public void cancelProcessInstanceWithHumanTasks() throws Exception { final String taskName1 = "userTask1"; final String taskName2 = "userTask2"; final ProcessDefinition processDefinition = deployProcessWith2UserTasksAnd1AutoTask(taskName1, taskName2, "auto1"); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, taskName1); final long step2Id = waitForUserTask(processInstance, taskName2); getProcessAPI().cancelProcessInstance(processInstance.getId()); waitForProcessToBeInState(processInstance, ProcessInstanceState.CANCELLED); final ArchivedActivityInstance archivedTask1 = getProcessAPI().getArchivedActivityInstance(step1Id); assertEquals(TestStates.CANCELLED.getStateName(), archivedTask1.getState()); final ArchivedActivityInstance archivedTask2 = getProcessAPI().getArchivedActivityInstance(step2Id); assertEquals(TestStates.CANCELLED.getStateName(), archivedTask2.getState()); disableAndDeleteProcess(processDefinition); } @Test public void cancelCallActivity() throws Exception { final String taskName1 = "userTask1"; final String taskName2 = "userTask2"; final String autoTaskName = "auto1"; final ProcessDefinition targetProcessDef = deployProcessWith2UserTasksAnd1AutoTask(taskName1, taskName2, autoTaskName); final ProcessDefinition callActivityProcDef = deployProcessWithCallActivity(taskName1, "callActivity", targetProcessDef.getName(), targetProcessDef.getVersion(), taskName2); final ProcessInstance parentProcessInstance = getProcessAPI().startProcess(callActivityProcDef.getId()); final FlowNodeInstance waitForFlowNode = waitForFlowNodeInExecutingState(parentProcessInstance, "callActivity", false); assertNotNull("Expected call activity in executing state", waitForFlowNode); checkNbOfProcessInstances(2, ProcessInstanceCriterion.NAME_DESC); final List processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_ASC); assertEquals(2, processInstances.size()); final ProcessInstance targetProcessInstance = processInstances.get(0); assertEquals(targetProcessDef.getId(), targetProcessInstance.getProcessDefinitionId()); final CheckNbOfActivities checkNbOfActivities = new CheckNbOfActivities(getProcessAPI(), 50, 5000, true, parentProcessInstance, 2, TestStates.READY); assertTrue(checkNbOfActivities.waitUntil()); getProcessAPI().cancelProcessInstance(parentProcessInstance.getId()); waitForProcessToBeInState(parentProcessInstance, ProcessInstanceState.CANCELLED); waitForProcessToBeInState(targetProcessInstance, ProcessInstanceState.CANCELLED); checkWasntExecuted(targetProcessInstance, autoTaskName); checkWasntExecuted(parentProcessInstance, taskName2); disableAndDeleteProcess(callActivityProcDef); disableAndDeleteProcess(targetProcessDef); } @SuppressWarnings("unchecked") @Test public void cancelProcessInstanceWithIntermediateCatchMessageEvent() throws Exception { final String catchMessageEvent = "receiveMessage"; final String previousStep = "auto1"; final String nextStep = "auto2"; final ProcessDefinition receiveProcess = deployProcessWithIntermediateCatchMessageEvent(catchMessageEvent, "m1", previousStep, nextStep); final ProcessInstance receiveProcessInstance = getProcessAPI().startProcess(receiveProcess.getId()); waitForFlowNodeInState(receiveProcessInstance, catchMessageEvent, TestStates.WAITING, true); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(WaitingEventSearchDescriptor.FLOW_NODE_NAME, catchMessageEvent); final Map parameters = new HashMap(1); parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done()); SearchResult searchResult = (SearchResult) getCommandAPI() .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters); assertEquals(1, searchResult.getCount()); getProcessAPI().cancelProcessInstance(receiveProcessInstance.getId()); waitForProcessToBeInState(receiveProcessInstance, ProcessInstanceState.CANCELLED); searchResult = (SearchResult) getCommandAPI().execute(SEARCH_WAITING_EVENTS_COMMAND, parameters); assertEquals(0, searchResult.getCount()); checkWasntExecuted(receiveProcessInstance, nextStep); disableAndDeleteProcess(receiveProcess); } @SuppressWarnings("unchecked") @Test public void cancelProcessInstanceWithIntermediateCatchSignalEvent() throws Exception { final String catchMessageEvent = "receiveSignal"; final String previousStep = "auto1"; final String nextStep = "auto2"; final ProcessDefinition receiveProcess = deployProcessWithIntermediateCatchSignalEvent(catchMessageEvent, "s1", previousStep, nextStep); final ProcessInstance receiveProcessInstance = getProcessAPI().startProcess(receiveProcess.getId()); waitForFlowNodeInState(receiveProcessInstance, catchMessageEvent, TestStates.WAITING, true); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(WaitingEventSearchDescriptor.FLOW_NODE_NAME, catchMessageEvent); final Map parameters = new HashMap(1); parameters.put(SEARCH_OPTIONS_KEY, searchOptionsBuilder.done()); SearchResult searchResult = (SearchResult) getCommandAPI() .execute(SEARCH_WAITING_EVENTS_COMMAND, parameters); assertEquals(1, searchResult.getCount()); getProcessAPI().cancelProcessInstance(receiveProcessInstance.getId()); waitForProcessToBeInState(receiveProcessInstance, ProcessInstanceState.CANCELLED); searchResult = (SearchResult) getCommandAPI().execute(SEARCH_WAITING_EVENTS_COMMAND, parameters); assertEquals(0, searchResult.getCount()); checkWasntExecuted(receiveProcessInstance, nextStep); disableAndDeleteProcess(receiveProcess); } @Test(expected = ProcessInstanceNotFoundException.class) public void cancelUnknownProcessInstance() throws Exception { getProcessAPI().cancelProcessInstance(45); } @Test public void should_cancel_process_instance_with_running_tasks() throws Exception { //given ProcessDefinition processDefinition = getProcessAPI() .deployAndEnableProcess( new ProcessDefinitionBuilder().createNewInstance("processWithATaskThatCallCancel", "1.0") .addStartEvent("start") .addCallActivity("call", new ExpressionBuilder().createConstantStringExpression("unknownProcess"), new ExpressionBuilder().createConstantStringExpression("1.0")) .addTransition("start", "call") .getProcess()); //when ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); ActivityInstance activityInstance = waitForTaskToFail(processInstance); assertThat(activityInstance.getName()).isEqualTo("call"); getProcessAPI().cancelProcessInstance(processInstance.getId()); waitForProcessToBeInState(processInstance, ProcessInstanceState.CANCELLED); //then try { getProcessAPI().getProcessInstance(processInstance.getId()); fail("process should not exist anymore"); } catch (ProcessInstanceNotFoundException ignored) { } //verify the call activity was archived in cancelled state: SearchResult archivedFlowNodeInstances = getProcessAPI() .searchArchivedFlowNodeInstances(new SearchOptionsBuilder(0, 10) .filter(ArchivedFlowNodeInstanceSearchDescriptor.NAME, "call") .filter(ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL, true).done()); assertThat(archivedFlowNodeInstances.getResult()).hasSize(1); assertThat(archivedFlowNodeInstances.getResult().get(0).getState()).isEqualTo("cancelled"); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/instance/InvolvedInProcessInstanceIT.java ================================================ /** * 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.process.instance; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import java.util.Collections; import org.bonitasoft.engine.bpm.actor.ActorCriterion; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.actor.ActorMember; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserCreator; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.Test; /** * @author Emmanuel Duchastenier */ public class InvolvedInProcessInstanceIT extends AbstractProcessInstanceIT { @Test public void isInvolvedInProcessInstanceAndTheirManager() throws Exception { //given //user and manager final User managerOfJim = createUser(new UserCreator("managerOfJim", "bpm")); // Jim starts the process instance: final User jim = createUser(new UserCreator("jim", "bpm").setManagerUserId(managerOfJim.getId())); final User managerOfJohn = createUser(new UserCreator("managerOfJohn", "bpm")); // john is mapped as direct user: final User john = createUser(new UserCreator("john", "bpm").setManagerUserId(managerOfJohn.getId())); final User managerOfJack = createUser(new UserCreator("managerOfJack", "bpm")); // Jack is mapped through his group: final User jack = createUser(new UserCreator("jack", "bpm").setManagerUserId(managerOfJack.getId())); final Group jackGroup = createGroup("jackGroup", null); final Role jackRole = createRole("jackRole"); getIdentityAPI().addUserMembership(jack.getId(), jackGroup.getId(), jackRole.getId()); final User managerOfJames = createUser(new UserCreator("managerOfJames", "bpm")); // James is mapped through his role: final User james = createUser(new UserCreator("james", "bpm").setManagerUserId(managerOfJames.getId())); final Group jamesGroup = createGroup("jamesGroup", null); final Role jamesRole = createRole("jamesRole"); getIdentityAPI().addUserMembership(james.getId(), jamesGroup.getId(), jamesRole.getId()); final User managerOfToto = createUser(new UserCreator("managerOfToto", "bpm")); // Toto is mapped through his group + role (=membership): final User toto = createUser(new UserCreator("toto", "bpm").setManagerUserId(managerOfToto.getId())); final Group totoGroup = createGroup("totoGroup", null); final Role totoRole = createRole("totoRole"); getIdentityAPI().addUserMembership(toto.getId(), totoGroup.getId(), totoRole.getId()); final ProcessDefinitionBuilder processBuilder1 = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder1.addActor(ACTOR_NAME); // 1 instance of process def: final DesignProcessDefinition designProcessDefinition = processBuilder1.addUserTask("step1", ACTOR_NAME) .addUserTask("step2", ACTOR_NAME) .addTransition("step1", "step2").getProcess(); final BusinessArchive businessArchive1 = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition).done(); final ProcessDefinition processDefinition = deployProcess(businessArchive1); // map user, group, role, and membership to that actor final ActorInstance actor = getProcessAPI().getActors(processDefinition.getId(), 0, 1, ActorCriterion.NAME_ASC) .get(0); final ActorMember jimActorMember = getProcessAPI().addUserToActor(actor.getId(), jim.getId()); final ActorMember johnActorMember = getProcessAPI().addUserToActor(actor.getId(), john.getId()); final ActorMember jackActorMember = getProcessAPI().addGroupToActor(actor.getId(), jackGroup.getId()); final ActorMember jamesActorMember = getProcessAPI().addRoleToActor(actor.getId(), jamesRole.getId()); getProcessAPI().addRoleAndGroupToActor(actor.getId(), totoRole.getId(), totoGroup.getId()); logoutThenloginAs(jim.getUserName(), "bpm"); getProcessAPI().enableProcess(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long processInstanceId = processInstance.getId(); getProcessAPI().removeActorMember(jimActorMember.getId()); // then await().alias("directly mapped user should be involved") .until(() -> getProcessAPI().isInvolvedInProcessInstance(john.getId(), processInstanceId)); assertThat(getProcessAPI().isInvolvedInProcessInstance(managerOfJohn.getId(), processInstanceId)).as( "manager of directly mapped user should not be involved").isFalse(); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(john.getId(), processInstanceId)) .as("directly mapped user should be involved") .isFalse(); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJohn.getId(), processInstanceId)) .as( "manager of directly mapped user should be involved") .isTrue(); assertThat(getProcessAPI().isInvolvedInProcessInstance(jack.getId(), processInstanceId)) .as("user mapped using group should be involved") .isTrue(); assertThat(getProcessAPI().isInvolvedInProcessInstance(managerOfJack.getId(), processInstanceId)).as( "manager of user mapped using group should not be involved").isFalse(); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(jack.getId(), processInstanceId)) .as("user mapped using group should be involved") .isFalse(); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJack.getId(), processInstanceId)) .as( "manager of user mapped using group should be involved") .isTrue(); assertThat(getProcessAPI().isInvolvedInProcessInstance(james.getId(), processInstanceId)) .as("user mapped using role should be involved") .isTrue(); assertThat(getProcessAPI().isInvolvedInProcessInstance(managerOfJames.getId(), processInstanceId)).as( "manager of user mapped using role should not be involved").isFalse(); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(james.getId(), processInstanceId)) .as("user mapped using role should be involved") .isFalse(); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJames.getId(), processInstanceId)) .as( "manager of user mapped using role should be involved") .isTrue(); assertThat(getProcessAPI().isInvolvedInProcessInstance(toto.getId(), processInstanceId)) .as("user mapped using membership should be involved") .isTrue(); assertThat(getProcessAPI().isInvolvedInProcessInstance(managerOfToto.getId(), processInstanceId)).as( "manager of user mapped using membership should not be involved").isFalse(); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(toto.getId(), processInstanceId)).as( "user mapped using membership should be involved").isFalse(); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfToto.getId(), processInstanceId)) .as( "manager of user mapped using membership should be involved") .isTrue(); assertThat(getProcessAPI().isInvolvedInProcessInstance(user.getId(), processInstanceId)) .as("not mapped user should not be involved").isFalse(); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(user.getId(), processInstanceId)) .as("not mapped user should not be involved").isFalse(); assertThat(getProcessAPI().isInvolvedInProcessInstance(jim.getId(), processInstanceId)) .as("the process instance initiator should be involved") .isTrue(); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(jim.getId(), processInstanceId)).as( "the process instance initiator should not be involved (as a manager)").isFalse(); assertThat(getProcessAPI().isInvolvedInProcessInstance(managerOfJim.getId(), processInstanceId)).as( "the manager of the process instance initiator should not be involved").isFalse(); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJim.getId(), processInstanceId)) .as( "the manager of process instance initiator should be involved (as a manager)") .isTrue(); getProcessAPI().removeActorMember(johnActorMember.getId()); logoutThenloginAs(john.getUserName(), "bpm"); final HumanTaskInstance step1Instance = waitForUserTaskAndAssignIt(processInstance, "step1", john); assertThat(getProcessAPI().isInvolvedInProcessInstance(john.getId(), processInstanceId)) .as("assigned user should be involved").isTrue(); assignAndExecuteStep(step1Instance, john); waitForActivityInCompletedState(processInstance, "step1", false); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJohn.getId(), processInstanceId)) .as( "the manager of the task executor should be involved") .isTrue(); getProcessAPI().removeActorMember(jamesActorMember.getId()); logoutThenloginAs(james.getUserName(), "bpm"); final HumanTaskInstance step2Instance = waitForUserTaskAndAssignIt(processInstance, "step2", james); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJames.getId(), processInstanceId)) .as("the manager of a user assigned to a task should be involved").isTrue(); getProcessAPI().removeActorMember(jackActorMember.getId()); logoutThenloginAs(jack.getUserName(), "bpm"); assignAndExecuteStep(step2Instance, jack); waitForProcessToFinish(processInstanceId); assertThat(getProcessAPI().isInvolvedInProcessInstance(jack.getId(), processInstanceId)).as( "a user executor of a task in an archived process instance should be involved").isTrue(); assertThat(getProcessAPI().isInvolvedInProcessInstance(managerOfJack.getId(), processInstanceId)).as( "a manager of a user executor of a task in an archived process instance should not be involved") .isFalse(); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(jack.getId(), processInstanceId)).as( "a user executor of a task in an archived process instance should not be involved (as a manager)") .isFalse(); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJack.getId(), processInstanceId)) .as( "a manager of a user executor of a task in an archived process instance should be involved (as a manager)") .isTrue(); assertThat(getProcessAPI().isInvolvedInProcessInstance(jim.getId(), processInstanceId)).as( "a user initiator of an archived process instance should be involved").isTrue(); assertThat(getProcessAPI().isInvolvedInProcessInstance(managerOfJim.getId(), processInstanceId)).as( "a manager of a user initiator of an archived process instance should not be involved").isFalse(); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(jim.getId(), processInstanceId)).as( "a user initiator of an archived process instance should not be involved (as a manager)").isFalse(); assertThat(getProcessAPI().isManagerOfUserInvolvedInProcessInstance(managerOfJim.getId(), processInstanceId)) .as( "a manager of a user initiator of an archived process instance should be involved (as a manager)") .isTrue(); //clean deleteUsers(john, jack, james, toto, managerOfJohn, managerOfJames, managerOfJack, managerOfToto, jim, managerOfJim); deleteGroups(jackGroup, jamesGroup, totoGroup); deleteRoles(jackRole, jamesRole, totoRole); disableAndDeleteProcess(processDefinition); } @Test public void isInvolvedInUserTask() throws Exception { //given // john is mapped as direct user: final User john = createUser(new UserCreator("john", "bpm")); final ProcessDefinitionBuilder processBuilder1 = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder1.addActor(ACTOR_NAME); // 1 instance of process def: final DesignProcessDefinition designProcessDefinition = processBuilder1.addUserTask("step1", ACTOR_NAME) .addUserTask("step2", ACTOR_NAME) .addTransition("step1", "step2").getProcess(); final BusinessArchive businessArchive1 = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition).done(); final ProcessDefinition processDefinition = deployProcess(businessArchive1); // map user, group, role, and membership to that actor final ActorInstance actor = getProcessAPI().getActors(processDefinition.getId(), 0, 1, ActorCriterion.NAME_ASC) .get(0); getProcessAPI().addUserToActor(actor.getId(), john.getId()); getProcessAPI().enableProcess(processDefinition.getId()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long processInstanceId = processInstance.getId(); long step1 = waitForUserTask(processInstanceId, "step1"); // then assertThat(getProcessAPI().isInvolvedInHumanTaskInstance(john.getId(), step1)) .as("directly mapped user should be involved").isTrue(); //clean deleteUsers(john); disableAndDeleteProcess(processDefinition); } @Test(expected = ProcessInstanceNotFoundException.class) public void isInvolvedInProcessInstanceWithProcessInstanceNotFoundException() throws Exception { getProcessAPI().isInvolvedInProcessInstance(user.getId(), 0); } @Test public void isInvolvedInProcessInstanceWithInvalidUser() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step2"); try { assertThat(getProcessAPI().isInvolvedInProcessInstance(999999999999L, processInstance.getId())).isFalse(); } finally { disableAndDeleteProcess(processDefinition); } } @Test public void should_assignee_of_call_activity_task_be_involved_in_root_process_instance() throws Exception { //given final User callActivityAssignee = createUser(new UserCreator("john", PASSWORD)); final ProcessDefinitionBuilder subProcessDefinitionBuilder = getProcessCallActivityDefinitionBuilder(); final ProcessDefinition subProcessDefinition = deployAndEnableProcessWithActor( subProcessDefinitionBuilder.done(), ACTOR_NAME, user); final ProcessDefinition rootProcessDefinition = deployAndEnableProcessWithActor( BuildTestUtil.buildProcessDefinitionWithCallActivity("processWithCallActivity", new ExpressionBuilder().createConstantStringExpression(BuildTestUtil.PROCESS_NAME), new ExpressionBuilder().createConstantStringExpression(BuildTestUtil.PROCESS_VERSION)).done(), ACTOR_NAME, user); //when final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcessDefinition.getId()); final long rootProcessInstanceId = rootProcessInstance.getId(); final HumanTaskInstance callActivityHumanTask = waitForUserTaskAndAssignIt("callActivityHumanTask", callActivityAssignee); //then assertThat(getProcessAPI().isInvolvedInProcessInstance(callActivityAssignee.getId(), rootProcessInstanceId)) .as("should user assigned to call activity id:" + callActivityHumanTask.getId() + " be involved in process with Id " + rootProcessInstanceId) .isTrue(); //clean up loginWithTechnicalUser(); disableAndDeleteProcess(rootProcessDefinition, subProcessDefinition); deleteUsers(callActivityAssignee); } @Test public void should_executor_of_call_activity_task_be_involved_in_root_process_instance() throws Exception { //given final User callActivityAssignee = createUser(new UserCreator("john", PASSWORD)); final User callActivityExecutor = createUser(new UserCreator("jack", PASSWORD)); final ProcessDefinitionBuilder subProcessDefinitionBuilder; subProcessDefinitionBuilder = getProcessCallActivityDefinitionBuilder(); final ProcessDefinition subProcessDefinition = deployAndEnableProcessWithActor( subProcessDefinitionBuilder.done(), ACTOR_NAME, user); final ProcessDefinition rootProcessDefinition = deployAndEnableProcessWithActor( BuildTestUtil.buildProcessDefinitionWithCallActivity("processWithCallActivity", new ExpressionBuilder().createConstantStringExpression(BuildTestUtil.PROCESS_NAME), new ExpressionBuilder().createConstantStringExpression(BuildTestUtil.PROCESS_VERSION)).done(), ACTOR_NAME, user); //when final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcessDefinition.getId()); final long rootProcessInstanceId = rootProcessInstance.getId(); final HumanTaskInstance callActivityHumanTask = waitForUserTaskAndAssignIt("callActivityHumanTask", callActivityAssignee); logoutThenloginAs(callActivityExecutor.getUserName(), PASSWORD); getProcessAPI().executeUserTask(callActivityExecutor.getId(), callActivityHumanTask.getId(), Collections.EMPTY_MAP); waitForProcessToFinish(rootProcessInstanceId); //then assertThat(getProcessAPI().isInvolvedInProcessInstance(callActivityAssignee.getId(), rootProcessInstanceId)) .as("should assigned user that didn't perform call activity id:" + callActivityHumanTask.getId() + " not be involved in process with Id " + rootProcessInstanceId) .isFalse(); assertThat(getProcessAPI().isInvolvedInProcessInstance(callActivityExecutor.getId(), rootProcessInstanceId)) .as("should user that perform call activity id:" + callActivityHumanTask.getId() + " be involved in process with Id " + rootProcessInstanceId) .isTrue(); //clean up loginWithTechnicalUser(); disableAndDeleteProcess(rootProcessDefinition, subProcessDefinition); deleteUsers(callActivityAssignee, callActivityExecutor); } private ProcessDefinitionBuilder getProcessCallActivityDefinitionBuilder() { ProcessDefinitionBuilder subProcessDefinitionBuilder; subProcessDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance(BuildTestUtil.PROCESS_NAME, BuildTestUtil.PROCESS_VERSION); subProcessDefinitionBuilder.addStartEvent("start"); subProcessDefinitionBuilder.addActor(ACTOR_NAME, true) .addManualTask("callActivityHumanTask", ACTOR_NAME); subProcessDefinitionBuilder.addEndEvent("end"); subProcessDefinitionBuilder.addTransition("start", "callActivityHumanTask") .addTransition("callActivityHumanTask", "end"); return subProcessDefinitionBuilder; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/process/instance/ProcessInstanceIT.java ================================================ /** * 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.process.instance; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import java.io.Serializable; import java.math.BigDecimal; 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 org.assertj.core.util.Lists; import org.bonitasoft.engine.api.ApiAccessType; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.comment.Comment; import org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion; import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserSearchDescriptor; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.APITestUtil; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.util.APITypeManager; import org.junit.Assert; import org.junit.Test; /** * @author Celine Souchet */ public class ProcessInstanceIT extends AbstractProcessInstanceIT { @Test public void checkProcessInstanceDescriptionNotNull() throws Exception { checkProcessInstanceDescription("My description"); } @Test public void checkProcessInstanceDescriptionNull() throws Exception { checkProcessInstanceDescription(null); } private void checkProcessInstanceDescription(final String description) throws Exception { // Create process definition with description; final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION) .addDescription(description).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition); // Start ProcessInstance final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); assertEquals(description, processInstance.getDescription()); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Test public void checkArchivedProcessInstanceDescriptionNotNull() throws Exception { checkArchivedProcessInstanceDescription("My description"); } @Test public void checkArchivedProcessInstanceDescriptionNull() throws Exception { checkArchivedProcessInstanceDescription(null); } private void checkArchivedProcessInstanceDescription(final String description) throws Exception { // Create process definition with description; final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION) .addDescription(description).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition); // Start ProcessInstance final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); assertEquals(description, processInstance.getDescription()); checkProcessInstanceIsArchived(processInstance); final List processInstances = getProcessAPI().getArchivedProcessInstances(0, 1, ProcessInstanceCriterion.CREATION_DATE_DESC); assertEquals(1, processInstances.size()); // We check that the retrieved processes are the good ones: final ArchivedProcessInstance archivedProcessInstance = processInstances.get(0); assertEquals(processInstance.getId(), archivedProcessInstance.getSourceObjectId()); assertEquals(description, archivedProcessInstance.getDescription()); disableAndDeleteProcess(processDefinition); } @Test public void deleteProcessInstance() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("step1", ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance process1 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance process2 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance process3 = getProcessAPI().startProcess(processDefinition.getId()); getProcessAPI().startProcess(processDefinition.getId()); getProcessAPI().startProcess(processDefinition.getId()); assertEquals(5, getProcessAPI().getNumberOfProcessInstances()); getProcessAPI().deleteProcessInstance(process1.getId()); getProcessAPI().deleteProcessInstance(process3.getId()); assertEquals(3, getProcessAPI().getNumberOfProcessInstances()); getProcessAPI().getProcessInstance(process2.getId()); try { getProcessAPI().getProcessInstance(process1.getId()); Assert.fail("this instance should be deleted"); } catch (final ProcessInstanceNotFoundException e) { // ok } disableAndDeleteProcess(processDefinition); } @Test public void deleteUnknownProcessInstance() throws Exception { Throwable exception = assertThrows(DeletionException.class, () -> getProcessAPI().deleteProcessInstance(123456789123L)); //Exception causes are not serialized if API type is HTTP if (ApiAccessType.LOCAL.equals(APITypeManager.getAPIType())) { assertTrue(exception.getCause() instanceof ProcessInstanceNotFoundException); } } @Test public void deleteProcessInstances() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("step1", ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance processInstance4 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance processInstance5 = getProcessAPI().startProcess(processDefinition.getId()); assertEquals(5, getProcessAPI().getNumberOfProcessInstances()); waitForUserTask(processInstance1, "step1"); waitForUserTask(processInstance2, "step1"); waitForUserTask(processInstance3, "step1"); waitForUserTask(processInstance4, "step1"); waitForUserTask(processInstance5, "step1"); getProcessAPI().deleteProcessInstances(processDefinition.getId(), 0, 4); assertEquals(1, getProcessAPI().getNumberOfProcessInstances()); // Clean up disableAndDeleteProcess(processDefinition); } @Test public void should_archivedProcessInstance_startDate_in_same_order() throws Exception { // given final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(false, false)); final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition); // when final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); Thread.sleep(20); final ProcessInstance pi1 = getProcessAPI().startProcess(processDefinition.getId()); Thread.sleep(20); final ProcessInstance pi2 = getProcessAPI().startProcess(processDefinition.getId()); waitForProcessToFinish(pi0.getId()); waitForProcessToFinish(pi1.getId()); waitForProcessToFinish(pi2.getId()); // then final List processInstances = getProcessAPI().getArchivedProcessInstances(0, 10, ProcessInstanceCriterion.CREATION_DATE_ASC); assertThat(processInstances).as("should find 3 archived processes").hasSize(3); final ArchivedProcessInstance returnedPI0 = processInstances.get(0); final ArchivedProcessInstance returnedPI1 = processInstances.get(1); final ArchivedProcessInstance returnedPI2 = processInstances.get(2); assertThat(pi0.getId()).as("archived process should have the same id") .isEqualTo(returnedPI0.getSourceObjectId()); assertThat(pi1.getId()).as("archived process should have the same id") .isEqualTo(returnedPI1.getSourceObjectId()); assertThat(pi2.getId()).as("archived process should have the same id") .isEqualTo(returnedPI2.getSourceObjectId()); assertThat(returnedPI0.getStartDate()).as("process 0 should start before or at same time than process 1") .isBeforeOrEqualsTo(returnedPI1.getStartDate()); assertThat(returnedPI1.getStartDate()).as("process 1 should start before or at same time than process 2") .isBeforeOrEqualsTo(returnedPI2.getStartDate()); disableAndDeleteProcess(processDefinition); assertThat(getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.DEFAULT)) .as("should delete all instances after process deletion").hasSize(0); } @Test public void getArchivedProcessInstanceOrderByLastUpdate() throws Exception { getArchivedProcessInstances(ProcessInstanceCriterion.LAST_UPDATE_ASC, 0, 1, 2, ProcessInstanceCriterion.LAST_UPDATE_DESC, 2, 1, 0); } private void getArchivedProcessInstances(final ProcessInstanceCriterion asc, final int asc1, final int asc2, final int asc3, final ProcessInstanceCriterion desc, final int desc1, final int desc2, final int desc3) throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(false, false)); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition).done(); final ProcessDefinition processDefinition = getProcessAPI().deploy(businessArchive); getProcessAPI().enableProcess(processDefinition.getId()); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); checkProcessInstanceIsArchived(pi0); final ProcessInstance pi1 = getProcessAPI().startProcess(processDefinition.getId()); checkProcessInstanceIsArchived(pi1); final ProcessInstance pi2 = getProcessAPI().startProcess(processDefinition.getId()); checkProcessInstanceIsArchived(pi2); // We asked for creation date descending order: List processInstances = getProcessAPI().getArchivedProcessInstances(0, 10, asc); assertEquals(3, processInstances.size()); assertEquals("completed", processInstances.get(asc1).getState()); assertEquals(pi0.getId(), processInstances.get(asc1).getSourceObjectId()); assertEquals("completed", processInstances.get(asc2).getState()); assertEquals(pi1.getId(), processInstances.get(asc2).getSourceObjectId()); assertEquals("completed", processInstances.get(asc3).getState()); assertEquals(pi2.getId(), processInstances.get(asc3).getSourceObjectId()); processInstances = getProcessAPI().getArchivedProcessInstances(0, 10, desc); assertEquals(3, processInstances.size()); assertEquals(pi0.getId(), processInstances.get(desc1).getSourceObjectId()); assertEquals(pi1.getId(), processInstances.get(desc2).getSourceObjectId()); assertEquals(pi2.getId(), processInstances.get(desc3).getSourceObjectId()); disableAndDeleteProcess(processDefinition); assertEquals(0, getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.DEFAULT).size()); } @Test public void getNumberOfArchiveProcessInstance() throws Exception { getNumberOfArchivedProcessInstances(); } private void getNumberOfArchivedProcessInstances() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1"), Arrays.asList(false)); final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition); long numberOfProcessInstancesBefore; numberOfProcessInstancesBefore = getProcessAPI().getNumberOfArchivedProcessInstances(); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition.getId()); checkProcessInstanceIsArchived(processInstance1); checkProcessInstanceIsArchived(processInstance2); checkProcessInstanceIsArchived(processInstance3); assertEquals(numberOfProcessInstancesBefore + 3, getProcessAPI().getNumberOfArchivedProcessInstances()); waitForProcessToFinish(processInstance1); waitForProcessToFinish(processInstance2); waitForProcessToFinish(processInstance3); disableAndDeleteProcess(processDefinition); } @Test public void getNoChildrenInstanceIdsFromProcessInstance() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); final List ids = getProcessAPI().getChildrenInstanceIdsOfProcessInstance(pi0.getId(), 0, 10, ProcessInstanceCriterion.DEFAULT); assertEquals(0, ids.size()); disableAndDeleteProcess(processDefinition); } @Test public void getSingleChildInstanceOfProcessInstance() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("SubProcessInAInstance", PROCESS_VERSION); builder.addActor(ACTOR_NAME).addAutomaticTask("step1").addAutomaticTask("step2") .addUserTask("userSubTask", ACTOR_NAME).addTransition("step1", "userSubTask") .addTransition("userSubTask", "step2"); final ProcessDefinition subProcess = deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression(subProcess.getName()); final Expression targetProcessVersionExpr = new ExpressionBuilder() .createConstantStringExpression(subProcess.getVersion()); final ProcessDefinitionBuilder builderProc = new ProcessDefinitionBuilder() .createNewInstance("executeInstanceSequentialWithSubProcess", "1.1"); builderProc.addActor(ACTOR_NAME).addStartEvent("start") .addCallActivity("callActivity", targetProcessNameExpr, targetProcessVersionExpr); builderProc.addAutomaticTask("step3").addEndEvent("end").addTransition("start", "callActivity") .addTransition("callActivity", "step3") .addUserTask("userTask", ACTOR_NAME).addTransition("step3", "userTask") .addTransition("userTask", "end"); final DesignProcessDefinition processDefinition = builderProc.done(); final ProcessDefinition mainProcess = deployAndEnableProcessWithActor(processDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(mainProcess.getId()); final HumanTaskInstance userTask = waitForUserTaskAndGetIt("userSubTask"); final List ids = getProcessAPI().getChildrenInstanceIdsOfProcessInstance(processInstance.getId(), 0, 10, ProcessInstanceCriterion.DEFAULT); assertThat(ids).isNotEmpty().hasSize(1).containsExactly(userTask.getParentProcessInstanceId()); disableAndDeleteProcess(mainProcess, subProcess); } @Test public void getChildrenInstanceIdsOfUnknownProcessInstance() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final List childrenInstanceIds = getProcessAPI().getChildrenInstanceIdsOfProcessInstance( processInstance.getId() + 1, 0, 10, ProcessInstanceCriterion.DEFAULT); assertTrue(childrenInstanceIds.isEmpty()); disableAndDeleteProcess(processDefinition); } @Test public void getProcessInstanceIdFromActivityInstanceId() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); final List activityInstances = getProcessAPI().getActivities(pi0.getId(), 0, 10); for (final ActivityInstance activityInstance : activityInstances) { final long processInstanceId = getProcessAPI() .getProcessInstanceIdFromActivityInstanceId(activityInstance.getId()); assertEquals(pi0.getId(), processInstanceId); } disableAndDeleteProcess(processDefinition); } @Test public void getNumberOfProcessInstances() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final long initialProcessInstanceNb = getProcessAPI().getNumberOfProcessInstances(); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition.getId()); final long finalProcessInstanceNb = getProcessAPI().getNumberOfProcessInstances(); assertEquals(initialProcessInstanceNb + 3, finalProcessInstanceNb); // Clean up waitForUserTask(processInstance1, "step1"); waitForUserTask(processInstance2, "step1"); waitForUserTask(processInstance3, "step1"); disableAndDeleteProcess(processDefinition); } @Test public void getProcessInstances() throws Exception { final List processDefinitions = createNbProcessDefinitionWithTwoHumanStepsAndDeployWithActor( 3, user); final List processInstances = startNbProcess(processDefinitions); getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.CREATION_DATE_ASC, processInstances); getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.CREATION_DATE_DESC, processInstances); getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.LAST_UPDATE_DESC, processInstances); getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.LAST_UPDATE_ASC, processInstances); getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.NAME_ASC, processInstances); getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.NAME_DESC, processInstances); getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.DEFAULT, processInstances); getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.STATE_DESC, processInstances); getProcessInstancesOrderByProcessInstanceCriterion(ProcessInstanceCriterion.STATE_ASC, processInstances); // Clean up disableAndDeleteProcess(processDefinitions); // We check that there are no resident process instances in DB: assertEquals(0, getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.DEFAULT).size()); } private void getProcessInstancesOrderByProcessInstanceCriterion( final ProcessInstanceCriterion processInstanceCriterion, final List processInstances) { // We asked for creation date descending order: final List resultProcessInstances = getProcessAPI().getProcessInstances(0, 10, processInstanceCriterion); assertEquals(3, resultProcessInstances.size()); final ProcessInstance returnedPI0 = resultProcessInstances.get(0); final ProcessInstance returnedPI1 = resultProcessInstances.get(1); final ProcessInstance returnedPI2 = resultProcessInstances.get(2); final ProcessInstance pi0 = processInstances.get(0); final ProcessInstance pi1 = processInstances.get(1); final ProcessInstance pi2 = processInstances.get(2); switch (processInstanceCriterion) { case STATE_ASC: // First state must be before second state : assertTrue(returnedPI0.getState().compareToIgnoreCase(returnedPI1.getState()) <= 0); // Second state must be before third state : assertTrue(returnedPI1.getState().compareToIgnoreCase(returnedPI2.getState()) <= 0); break; case STATE_DESC: // First state must be after second state : assertTrue(returnedPI0.getState().compareToIgnoreCase(returnedPI1.getState()) >= 0); // Second state must be after third state : assertTrue(returnedPI1.getState().compareToIgnoreCase(returnedPI2.getState()) >= 0); break; case LAST_UPDATE_ASC: // First last update must be before second last update : assertTrue(returnedPI0.getLastUpdate().before(returnedPI1.getLastUpdate())); // Second last update must be before third last update : assertTrue(returnedPI1.getLastUpdate().before(returnedPI2.getLastUpdate())); break; case LAST_UPDATE_DESC: // First last update must be after second last update : assertTrue(returnedPI0.getLastUpdate().after(returnedPI1.getLastUpdate())); // Second last update must be after third last update : assertTrue(returnedPI1.getLastUpdate().after(returnedPI2.getLastUpdate())); break; case NAME_ASC: // We check that the retrieved processes are the good ones: assertEquals(pi0.getId(), returnedPI0.getId()); assertEquals(pi1.getId(), returnedPI1.getId()); assertEquals(pi2.getId(), returnedPI2.getId()); // First name must be before second name : assertTrue(returnedPI0.getName().compareToIgnoreCase(returnedPI1.getName()) <= 0); // Second name must be before third name : assertTrue(returnedPI1.getName().compareToIgnoreCase(returnedPI2.getName()) <= 0); break; case NAME_DESC: // We check that the retrieved processes are the good ones: assertEquals(pi0.getId(), returnedPI2.getId()); assertEquals(pi1.getId(), returnedPI1.getId()); assertEquals(pi2.getId(), returnedPI0.getId()); // First name must be after second name : assertTrue(returnedPI0.getName().compareToIgnoreCase(returnedPI1.getName()) >= 0); // Second name must be after third name : assertTrue(returnedPI1.getName().compareToIgnoreCase(returnedPI2.getName()) >= 0); break; case CREATION_DATE_ASC: // We check that the retrieved processes are the good ones: assertEquals(pi0.getId(), returnedPI0.getId()); assertEquals(pi1.getId(), returnedPI1.getId()); assertEquals(pi2.getId(), returnedPI2.getId()); // First creation date must be before second creation date: assertTrue(returnedPI0.getStartDate().before(returnedPI1.getStartDate())); // Second creation date must be before third creation date: assertTrue(returnedPI1.getStartDate().before(returnedPI2.getStartDate())); break; case CREATION_DATE_DESC: case DEFAULT: default: // We check that the retrieved processes are the good ones: assertEquals(pi0.getId(), returnedPI2.getId()); assertEquals(pi1.getId(), returnedPI1.getId()); assertEquals(pi2.getId(), returnedPI0.getId()); // First creation date must be after second creation date: assertTrue(returnedPI0.getStartDate().after(returnedPI1.getStartDate())); // Second creation date must be after third creation date: assertTrue(returnedPI1.getStartDate().after(returnedPI2.getStartDate())); break; } } @Test public void setProcessInstanceState() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("step1", ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step1"); final long processInstanceId = processInstance.getId(); processInstance = getProcessAPI().getProcessInstance(processInstanceId); assertEquals("started", processInstance.getState()); getProcessAPI().setProcessInstanceState(processInstance, "initializing"); processInstance = getProcessAPI().getProcessInstance(processInstanceId); assertEquals("initializing", processInstance.getState()); getProcessAPI().setProcessInstanceState(processInstance, "started"); processInstance = getProcessAPI().getProcessInstance(processInstanceId); assertEquals("started", processInstance.getState()); disableAndDeleteProcess(processDefinition); } @Test public void startProcess2Times() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("initTask1"), Arrays.asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, APITestUtil.ACTOR_NAME, user); // Start process instance first time, and complete it final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "initTask1", user); waitForProcessToFinish(processInstance); // Start process instance second time final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance2, "initTask1", user); waitForProcessToFinish(processInstance2); disableAndDeleteProcess(processDefinition); } @Test public void startProcessUsingInitialVariableValuesFor() throws Exception { final String otherUserName = "other"; final User otherUser = createUser(otherUserName, "user"); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("cantResolveDataInExpressionInDataDefaultValue", "1"); processBuilder.addActor(ACTOR_NAME).addDescription("Process to test archiving mechanism"); processBuilder.addDoubleData("D", new ExpressionBuilder().createConstantDoubleExpression(3.14)); processBuilder.addData("bigD", BigDecimal.class.getName(), null); processBuilder.addUserTask("step1", ACTOR_NAME); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); try { final Map variables = new HashMap<>(); variables.put("bigD", new BigDecimal("3.141592653589793")); final ProcessInstance instance = getProcessAPI().startProcess(otherUser.getId(), processDefinition.getId(), variables); final ProcessInstance processInstance2 = getProcessAPI().getProcessInstance(instance.getId()); DataInstance dataInstance = getProcessAPI().getProcessDataInstance("bigD", instance.getId()); assertEquals(new BigDecimal("3.141592653589793"), dataInstance.getValue()); dataInstance = getProcessAPI().getProcessDataInstance("D", instance.getId()); assertEquals(Double.valueOf(3.14), dataInstance.getValue()); assertEquals(otherUser.getId(), processInstance2.getStartedBy()); assertEquals(user.getId(), processInstance2.getStartedBySubstitute()); // Check system comment final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100) .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance2.getId()) .done(); final List comments = getProcessAPI().searchComments(searchOptions).getResult(); boolean haveCommentForDelegate = false; for (final Comment comment : comments) { haveCommentForDelegate = haveCommentForDelegate || comment.getContent() .contains("The user " + USERNAME + " acting as delegate of the user " + otherUserName + " has started the case."); } assertTrue(haveCommentForDelegate); } finally { // Clean up disableAndDeleteProcess(processDefinition); deleteUsers(otherUser); } } @Test public void startProcessInstanceFor() throws Exception { final String otherUserName = "other"; final User otherUser = createUser(otherUserName, "user"); // create process definition with integer data; final String dataName = "var1"; final DesignProcessDefinition processDef = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME) .addIntegerData(dataName, new ExpressionBuilder().createConstantIntegerExpression(1)) .addUserTask("step1", ACTOR_NAME) .addAutomaticTask("step2").addTransition("step1", "step2").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, ACTOR_NAME, user); try { // create Operation keyed map final Operation integerOperation = BuildTestUtil.buildIntegerOperation(dataName, 2); final List operations = new ArrayList<>(); final Map contexts = new HashMap<>(); contexts.put("page", "1"); operations.add(integerOperation); final long processDefinitionId = processDefinition.getId(); final ProcessInstance processInstance = getProcessAPI().startProcess(otherUser.getId(), processDefinitionId, operations, contexts); final ProcessInstance processInstance2 = getProcessAPI().getProcessInstance(processInstance.getId()); assertEquals(otherUser.getId(), processInstance2.getStartedBy()); assertEquals(user.getId(), processInstance2.getStartedBySubstitute()); // Check system comment final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100) .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()) .done(); final List comments = getProcessAPI().searchComments(searchOptions).getResult(); boolean haveCommentForDelegate = false; for (final Comment comment : comments) { haveCommentForDelegate = haveCommentForDelegate || comment.getContent() .contains("The user " + USERNAME + " acting as delegate of the user " + otherUserName + " has started the case."); } assertTrue(haveCommentForDelegate); } finally { // Clean up disableAndDeleteProcess(processDefinition); deleteUser(otherUser); } } private List startNbProcess(final List processDefinitions) throws Exception { final List process = new ArrayList<>(); for (final ProcessDefinition processDefinition : processDefinitions) { process.add(getProcessAPI().startProcess(processDefinition.getId())); Thread.sleep(5);// avoid two instances with the same date } return process; } private List createNbProcessDefinitionWithTwoHumanStepsAndDeployWithActor(final int nbProcess, final User user) throws BonitaException { return createNbProcessDefinitionWithHumanAndAutomaticAndDeployWithActor(nbProcess, user, Arrays.asList("step1", "step2"), Arrays.asList(true, true)); } @Test public void getNumberOfUsersCanExecutePendingHumanTaskDeploymentInfo() throws Exception { final User otherUser = createUser("other", "user"); // create process definition with integer data; final String dataName = "var1"; final DesignProcessDefinition processDef = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME) .addIntegerData(dataName, new ExpressionBuilder().createConstantIntegerExpression(1)) .addUserTask("step1", ACTOR_NAME) .addAutomaticTask("step2").addTransition("step1", "step2").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, Lists.newArrayList(ACTOR_NAME, ACTOR_NAME), Lists.newArrayList(user, otherUser)); // create Operation keyed map final Operation integerOperation = BuildTestUtil.buildIntegerOperation(dataName, 2); final ProcessInstance processInstance = getProcessAPI().startProcess(otherUser.getId(), processDefinition.getId(), Collections.singletonList(integerOperation), Collections. singletonMap("page", "1")); final long step1Id = waitForUserTask(processInstance, "step1"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(UserSearchDescriptor.LAST_NAME, Order.DESC); final SearchResult results = getProcessAPI().searchUsersWhoCanExecutePendingHumanTask(step1Id, builder.done()); assertThat(results.getCount()).isSameAs(2L); assertThat(results.getResult()).isNotEmpty(); assertThat(results.getResult().get(0).getId()).isEqualTo(user.getId()); assertThat(results.getResult().get(1).getId()).isEqualTo(otherUser.getId()); // Clean up disableAndDeleteProcess(processDefinition); deleteUser(otherUser); } @Test public void runProcessInstanceWithDefaultFlownode_should_pass_all_human_task() throws Exception { final DesignProcessDefinition processDef = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME) .addUserTask("step1", ACTOR_NAME).addUserTask("step2", ACTOR_NAME) .addDefaultTransition("step1", "step2").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, Lists.newArrayList(ACTOR_NAME, ACTOR_NAME), Lists.newArrayList(user)); final ProcessInstance pi = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(pi, "step1", user); waitForUserTaskAndExecuteIt(pi, "step2", user); waitForProcessToFinish(pi); disableAndDeleteProcess(processDefinition); } @Test public void runProcessInstanceWithDefaultFlownode_and_another_evaluated_to_false_transition_should_passto_step2() throws Exception { final DesignProcessDefinition processDef = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME) .addUserTask("step1", ACTOR_NAME).addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addTransition("step1", "step3", new ExpressionBuilder().createConstantBooleanExpression(false)) .addDefaultTransition("step1", "step2") .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, Lists.newArrayList(ACTOR_NAME, ACTOR_NAME), Lists.newArrayList(user)); final ProcessInstance pi = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(pi, "step1", user); waitForUserTaskAndExecuteIt(pi, "step2", user); waitForProcessToFinish(pi); disableAndDeleteProcess(processDefinition); } @Test public void runProcessInstanceWithDefaultFlownode_and_another_evaluated_to_true_transition_should_passto_step3() throws Exception { final DesignProcessDefinition processDef = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME) .addUserTask("step1", ACTOR_NAME).addUserTask("step2", ACTOR_NAME).addUserTask("step3", ACTOR_NAME) .addTransition("step1", "step3", new ExpressionBuilder().createConstantBooleanExpression(true)) .addDefaultTransition("step1", "step2") .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, Lists.newArrayList(ACTOR_NAME, ACTOR_NAME), Lists.newArrayList(user)); final ProcessInstance pi = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(pi, "step1", user); waitForUserTaskAndExecuteIt(pi, "step3", user); waitForProcessToFinish(pi); disableAndDeleteProcess(processDefinition); } @Test public void runDeleteParentArchivedProcessInstanceAndElements_should_not_delete_process_instance_not_yet_archived() throws Exception { final DesignProcessDefinition processDef = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION).addActor(ACTOR_NAME) .addUserTask("step1", ACTOR_NAME).addUserTask("step2", ACTOR_NAME).addTransition("step1", "step2") .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDef, Lists.newArrayList(ACTOR_NAME, ACTOR_NAME), Lists.newArrayList(user)); final ProcessInstance pi = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(pi, "step1", user); final long nbDeleted = getProcessAPI().deleteArchivedProcessInstances(processDefinition.getId(), 0, 10); // there is one archived process instance deleted because the former process_instance state has been archived assertThat(nbDeleted).isEqualTo(1); waitForUserTaskAndExecuteIt(pi, "step2", user); waitForProcessToFinish(pi); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/profile/AbstractProfileIT.java ================================================ /** * 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.profile; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.junit.After; import org.junit.Before; /** * @author Baptiste Mesta */ public abstract class AbstractProfileIT extends TestWithTechnicalUser { protected Long adminProfileId; protected Long userProfileId; protected User user1; protected User user2; protected User user3; protected User user4; protected User user5; protected Group group1; protected Group group2; protected Group group3; protected Role role1; protected Role role2; protected Role role3; @Override @Before public void before() throws Exception { super.before(); user1 = createUser("userName1", "User1Pwd", "User1FirstName", "User1LastName"); user2 = createUser("userName2", "User2Pwd", "User2FirstName", "User2LastName"); user3 = createUser("userName3", "User3Pwd", "User3FirstName", "User3LastName"); user4 = createUser("userName4", "User4Pwd", "User4FirstName", "User4LastName"); user5 = createUser("userName5", "User5Pwd", "User5FirstName", "User5LastName"); group1 = createGroup("group1"); group2 = createGroup("group2"); group3 = createGroup("group3"); role1 = createRole("role1"); role2 = createRole("role2"); role3 = createRole("role3"); // search for the newly created profile IDs: adminProfileId = getProfileByName("Administrator").getId(); userProfileId = getProfileByName("User").getId(); getProfileAPI().createProfileMember(userProfileId, user1.getId(), null, null); getProfileAPI().createProfileMember(userProfileId, null, group1.getId(), null); getProfileAPI().createProfileMember(adminProfileId, user1.getId(), null, null); getProfileAPI().createProfileMember(adminProfileId, null, group1.getId(), role2.getId()); getProfileAPI().createProfileMember(adminProfileId, null, group2.getId(), role2.getId()); getProfileAPI().createProfileMember(adminProfileId, null, null, role1.getId()); getProfileAPI().createProfileMember(adminProfileId, null, null, role2.getId()); } private Profile getProfileByName(String name) throws SearchException { return getProfileAPI().searchProfiles(new SearchOptionsBuilder(0, 1).filter("name", name).done()).getResult() .get(0); } @Override @After public void after() throws Exception { deleteUsers(user1, user2, user3, user4, user5); deleteGroups(group1, group2, group3); deleteRoles(role1, role2, role3); super.after(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/profile/ProfileCommunityIT.java ================================================ /** * 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.profile; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.Test; /** * Specific tests for the community edition on the profile API */ public class ProfileCommunityIT extends AbstractProfileIT { @Test public void searchProfile() throws BonitaException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(ProfileSearchDescriptor.NAME, Order.DESC); final SearchResult searchedProfiles = getProfileAPI().searchProfiles(builder.done()); assertThat(searchedProfiles.getCount()).isEqualTo(2); assertThat(searchedProfiles.getResult().get(0).getName()).isEqualTo("User"); assertThat(searchedProfiles.getResult().get(1).getName()).isEqualTo("Administrator"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/profile/ProfileIT.java ================================================ /** * 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.profile; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import java.util.List; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.platform.LoginException; import org.bonitasoft.engine.platform.LogoutException; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.Test; /** * @author Julien Mege * @author Celine Souchet */ public class ProfileIT extends AbstractProfileIT { @Test public void should_have_profiles_in_session() throws Exception { getApiClient().logout(); List profilesOfUser1 = getProfilesFromSession(this.user1, "User1Pwd"); List profilesOfUser2 = getProfilesFromSession(this.user2, "User2Pwd"); List profilesOfUser3 = getProfilesFromSession(this.user3, "User3Pwd"); List profilesOfUser4 = getProfilesFromSession(this.user4, "User4Pwd"); List profilesOfUser5 = getProfilesFromSession(this.user5, "User5Pwd"); loginWithTechnicalUser(); //Theses profiles are the one mapped in the AbstractProfileIT's before assertThat(profilesOfUser1).containsExactlyInAnyOrder("Administrator", "User"); assertThat(profilesOfUser2).isEmpty(); assertThat(profilesOfUser3).isEmpty(); assertThat(profilesOfUser4).isEmpty(); assertThat(profilesOfUser5).isEmpty(); } private List getProfilesFromSession(User user, String password) throws LoginException, LogoutException { getApiClient().login(user.getUserName(), password); List profiles = getApiClient().getSession().getProfiles(); getApiClient().logout(); return profiles; } @Test public void searchProfileWithSearchTerm() throws BonitaException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(ProfileSearchDescriptor.NAME, Order.ASC); builder.searchTerm("Adm"); final SearchResult searchedProfiles = getProfileAPI().searchProfiles(builder.done()); assertEquals(1, searchedProfiles.getCount()); assertEquals("Administrator", searchedProfiles.getResult().get(0).getName()); } @Test(expected = SearchException.class) public void searchProfileWithWrongParameter() throws Exception { getProfileAPI().searchProfiles(null); } @Test(expected = ProfileNotFoundException.class) public void getProfileWithWrongParameter() throws Exception { getProfileAPI().getProfile(855); } @Test public void addUser_to_profile_updates_profile_metadata() throws Exception { final ProfileMemberCreator creator = new ProfileMemberCreator( getProfileAPI().getProfile(adminProfileId).getId()); creator.setUserId(user3.getId()); shouldProfileMemberOperation_update_profile_metadata(creator); } @Test public void addGroup_to_profile_updates_profile_metadata() throws Exception { final ProfileMemberCreator creator = new ProfileMemberCreator( getProfileAPI().getProfile(adminProfileId).getId()); creator.setGroupId(group3.getId()); shouldProfileMemberOperation_update_profile_metadata(creator); } @Test public void addRole_to_profile_updates_profile_metadata() throws Exception { final ProfileMemberCreator creator = new ProfileMemberCreator( getProfileAPI().getProfile(adminProfileId).getId()); creator.setRoleId(role3.getId()); shouldProfileMemberOperation_update_profile_metadata(creator); } @Test public void addRoleAndGroup_to_profile_updates_profile_metadata() throws Exception { final ProfileMemberCreator creator = new ProfileMemberCreator( getProfileAPI().getProfile(adminProfileId).getId()); creator.setGroupId(group3.getId()); creator.setRoleId(role3.getId()); shouldProfileMemberOperation_update_profile_metadata(creator); } private void shouldProfileMemberOperation_update_profile_metadata(final ProfileMemberCreator creator) throws Exception { // given final Profile profileBefore = getProfileAPI().getProfile(adminProfileId); // Make sure lastUpdateDate is changed Thread.sleep(5); // when logout(); loginOnDefaultTenantWith("userName3", "User3Pwd"); Thread.sleep(10); final ProfileMember createProfileMember = getProfileAPI().createProfileMember(creator); // then final Profile profileAfter = getProfileAPI().getProfile(adminProfileId); checkMetaData(profileBefore, profileAfter, user3); // Make sure lastUpdateDate is changed Thread.sleep(5); // when logout(); loginOnDefaultTenantWith("userName1", "User1Pwd"); getProfileAPI().deleteProfileMember(createProfileMember.getId()); // then final Profile profileAfterDelete = getProfileAPI().getProfile(adminProfileId); checkMetaData(profileAfter, profileAfterDelete, user1); } private void checkMetaData(final Profile profileBefore, final Profile profileAfter, final User user) { assertThat(profileAfter.getLastUpdateDate()).as("lastUpdateDate should be modified") .isAfter(profileBefore.getLastUpdateDate()); assertThat(profileAfter.getLastUpdatedBy()).as("lastUpdatedBy should be modified") .isNotEqualTo(profileBefore.getLastUpdatedBy()); assertThat(profileAfter.getLastUpdatedBy()).as("lastUpdatedBy should be modified").isEqualTo(user.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/profile/ProfileMemberIT.java ================================================ /** * 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.profile; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.Test; public class ProfileMemberIT extends AbstractProfileIT { @Test public void createAndDeleteGroupToProfile() throws BonitaException { checkCreateAndDeleProfileMember("group", null, group1.getId(), null); } @Test public void createAndDeleteRoleAndGroupToProfile() throws BonitaException { getIdentityAPI().addUserMembership(user1.getId(), group1.getId(), role1.getId()); getIdentityAPI().addUserMembership(user2.getId(), group1.getId(), role2.getId()); getIdentityAPI().addUserMembership(user3.getId(), group2.getId(), role1.getId()); // Create group Profile1 checkCreateAndDeleProfileMember("roleAndGroup", null, group1.getId(), role1.getId()); } @Test public void createAndDeleteRoleToProfile() throws BonitaException { checkCreateAndDeleProfileMember("role", null, null, role3.getId()); } @Test public void createandDeleteUserProfile() throws BonitaException { checkCreateAndDeleProfileMember("user", user2.getId(), null, null); } @Test(expected = CreationException.class) public void createProfileMemberWithWrongParameter() throws Exception { getProfileAPI().createProfileMember(856L, null, null, null); } @Test public void deleteOrganizationRemoveProfileMember() throws BonitaException { // create user and add profile final User user = getIdentityAPI().createUser("mixmaster.spike", "123456789"); getProfileAPI().createProfileMember(adminProfileId, user.getId(), null, null); // delete user getIdentityAPI().deleteUser(user.getId()); // check there is no user mixmaster.spike anymore in this profile final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC); builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, adminProfileId); final SearchResult searchedProfileMember = getProfileAPI().searchProfileMembers("user", builder.done()); assertEquals(1, searchedProfileMember.getResult().size()); } private void checkCreateAndDeleProfileMember(final String memberType, final Long userId, final Long groupId, final Long roleId) throws BonitaException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC); builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, adminProfileId); SearchResult searchedProfileMember = getProfileAPI().searchProfileMembers(memberType, builder.done()); final long numberOfProfileMembersBeforeCreation = searchedProfileMember.getCount(); final ProfileMember addProfileMemberResult = getProfileAPI().createProfileMember(adminProfileId, userId, groupId, roleId); searchedProfileMember = getProfileAPI().searchProfileMembers(memberType, builder.done()); assertEquals(numberOfProfileMembersBeforeCreation + 1, searchedProfileMember.getCount()); // delete UserProfile1 getProfileAPI().deleteProfileMember(addProfileMemberResult.getId()); searchedProfileMember = getProfileAPI().searchProfileMembers(memberType, builder.done()); assertEquals(numberOfProfileMembersBeforeCreation, searchedProfileMember.getCount()); } @Test(expected = DeletionException.class) public void deleteProfileMemberWithWrongParameter() throws Exception { getProfileAPI().deleteProfileMember(856L); } @Test(expected = CreationException.class) public void createTwiceSameProfileMemberWithUser() throws BonitaException { getProfileAPI().createProfileMember(adminProfileId, user1.getId(), null, null); getProfileAPI().createProfileMember(adminProfileId, user1.getId(), null, null); } @Test(expected = CreationException.class) public void createTwiceSameProfileMemberWithRole() throws BonitaException { getProfileAPI().createProfileMember(adminProfileId, null, null, role1.getId()); getProfileAPI().createProfileMember(adminProfileId, null, null, role1.getId()); } @Test(expected = CreationException.class) public void createTwiceSameProfileMemberWithGroup() throws BonitaException { getProfileAPI().createProfileMember(adminProfileId, null, group1.getId(), null); getProfileAPI().createProfileMember(adminProfileId, null, group1.getId(), null); } @Test(expected = CreationException.class) public void createTwiceSameProfileMemberWithMembership() throws BonitaException { getIdentityAPI().addUserMembership(user1.getId(), group1.getId(), role1.getId()); // Create UserProfile getProfileAPI().createProfileMember(adminProfileId, null, group1.getId(), role1.getId()); getProfileAPI().createProfileMember(adminProfileId, null, group1.getId(), role1.getId()); } @Test public void getProfileForUser() { // Get Profile For User final List profiles = getProfileAPI().getProfilesForUser(user1.getId(), 0, 10, ProfileCriterion.NAME_ASC); assertThat(profiles).hasSize(2).extracting("name").contains("Administrator", "User"); } @Test public void getProfileForUserReturnDisctinctProfiles() throws BonitaException { // user 1 is mapped to profile "Administrator" through direct "userName1" mapping + through "role1" mapping: getIdentityAPI().addUserMembership(user1.getId(), group2.getId(), role1.getId()); // Get Profile For User final List getUserProfiles = getProfileAPI().getProfilesForUser(user1.getId(), 0, 10, ProfileCriterion.NAME_ASC); assertEquals(2, getUserProfiles.size()); assertThat(getUserProfiles).extracting("name").containsExactlyInAnyOrder("Administrator", "User"); } @Test public void getProfileForUserFromGroup() throws BonitaException { getIdentityAPI().addUserMembership(user1.getId(), group1.getId(), role1.getId()); // Get Profile For User final List getUserProfiles = getProfileAPI().getProfilesForUser(user1.getId(), 0, 10, ProfileCriterion.NAME_ASC); assertEquals(2, getUserProfiles.size()); assertEquals("Administrator", getUserProfiles.get(0).getName()); assertEquals("User", getUserProfiles.get(1).getName()); } @Test public void getProfileForUserFromRole() throws BonitaException { getIdentityAPI().addUserMembership(user1.getId(), group1.getId(), role1.getId()); // Get Profile For User final List getUserProfiles = getProfileAPI().getProfilesForUser(user1.getId(), 0, 10, ProfileCriterion.NAME_ASC); assertEquals(2, getUserProfiles.size()); assertEquals("Administrator", getUserProfiles.get(0).getName()); assertEquals("User", getUserProfiles.get(1).getName()); } @Test public void getProfileForUserFromRoleAndGroup() throws BonitaException { getIdentityAPI().addUserMembership(user5.getId(), group1.getId(), role2.getId()); getIdentityAPI().addUserMembership(user2.getId(), group1.getId(), role3.getId()); // User2 have membership group1 + role3 and does not match a profile member of administrator profile: assertThat(getProfileAPI().getProfilesForUser(user2.getId(), 0, 10, ProfileCriterion.NAME_ASC)) .noneMatch(p -> p.getName().equals("Administrator")); // User5 have membership group1 + role2 and matches a profile member of administrator profile: assertThat(getProfileAPI().getProfilesForUser(user5.getId(), 0, 10, ProfileCriterion.NAME_ASC)) .anyMatch(p -> p.getName().equals("Administrator")); } @Test public void getProfilesForUserWithWrongParameter() { final List profilesForUser = getProfileAPI().getProfilesForUser(564162L, 0, 10, ProfileCriterion.NAME_ASC); assertEquals(0, profilesForUser.size()); } @Test public void searchUserProfileMembersForProfile() throws BonitaException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC); builder.filter(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, "User1FirstName"); builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, adminProfileId); builder.searchTerm("userName1"); final SearchResult searchedProfileMember = getProfileAPI().searchProfileMembers("user", builder.done()); assertEquals(1, searchedProfileMember.getResult().size()); assertEquals("User1FirstName", searchedProfileMember.getResult().get(0).getDisplayNamePart1()); assertEquals("User1LastName", searchedProfileMember.getResult().get(0).getDisplayNamePart2()); assertEquals("userName1", searchedProfileMember.getResult().get(0).getDisplayNamePart3()); } @Test public void searchGroupProfileMembersForProfile() throws BonitaException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC); builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, userProfileId); final SearchResult searchedProfileMember = getProfileAPI().searchProfileMembers("group", builder.done()); assertEquals(1, searchedProfileMember.getResult().size()); final ProfileMember profileMember = searchedProfileMember.getResult().get(0); assertEquals("group1", profileMember.getDisplayNamePart1()); final String displayNamePart3 = profileMember.getDisplayNamePart3(); // Can be null with Oracle assertTrue(displayNamePart3 == null || displayNamePart3.isEmpty()); } @Test public void searchRoleProfileMembersForProfile() throws BonitaException { SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC); builder.filter(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, "role2"); builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, adminProfileId); builder.searchTerm("role2"); SearchResult searchedProfileMember = getProfileAPI().searchProfileMembers("role", builder.done()); assertEquals(1, searchedProfileMember.getCount()); ProfileMember profileMember = searchedProfileMember.getResult().get(0); assertEquals("role2", profileMember.getDisplayNamePart1()); builder = new SearchOptionsBuilder(0, 10); builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC); builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, adminProfileId); searchedProfileMember = getProfileAPI().searchProfileMembers("role", builder.done()); profileMember = searchedProfileMember.getResult().get(0); assertEquals(2, searchedProfileMember.getCount()); assertEquals("role1", profileMember.getDisplayNamePart1()); final String displayNamePart2 = profileMember.getDisplayNamePart2(); // Can be null with Oracle assertTrue(displayNamePart2 == null || displayNamePart2.isEmpty()); final String displayNamePart3 = profileMember.getDisplayNamePart3(); assertTrue(displayNamePart3 == null || displayNamePart3.isEmpty()); assertEquals("role2", searchedProfileMember.getResult().get(1).getDisplayNamePart1()); } @Test public void searchRoleAndGroupProfileMembersForProfile() throws BonitaException { SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC); builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART2, Order.ASC); builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, adminProfileId); SearchResult searchedProfileMember = getProfileAPI().searchProfileMembers("roleAndGroup", builder.done()); assertEquals(2, searchedProfileMember.getCount()); assertEquals("role2", searchedProfileMember.getResult().get(0).getDisplayNamePart1()); assertEquals("group1", searchedProfileMember.getResult().get(0).getDisplayNamePart2()); assertEquals("role2", searchedProfileMember.getResult().get(1).getDisplayNamePart1()); assertEquals("group2", searchedProfileMember.getResult().get(1).getDisplayNamePart2()); builder = new SearchOptionsBuilder(0, 10); builder.sort(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, Order.ASC); builder.filter(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART2, "group2"); builder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, adminProfileId); searchedProfileMember = getProfileAPI().searchProfileMembers("roleAndGroup", builder.done()); assertEquals("role2", searchedProfileMember.getResult().get(0).getDisplayNamePart1()); assertEquals("group2", searchedProfileMember.getResult().get(0).getDisplayNamePart2()); } @Test public void getNumberOfProfileMembers() { final List profileIds = new ArrayList<>(); profileIds.add(adminProfileId); profileIds.add(userProfileId); final Map numberOfProfileMembers = getProfileAPI().getNumberOfProfileMembers(profileIds); assertNotNull(numberOfProfileMembers); assertEquals(2, numberOfProfileMembers.size()); assertEquals(Long.valueOf(5L), numberOfProfileMembers.get(adminProfileId)); assertEquals(Long.valueOf(2L), numberOfProfileMembers.get(userProfileId)); } @Test(expected = SearchException.class) public void searchProfileMembersForProfileWithWrongParameter() throws Exception { getProfileAPI().searchProfileMembers("plop", null); } @Test public void searchUserProfileMembersOfUser() throws BonitaException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(ProfileMemberSearchDescriptor.USER_ID, user1.getId()); final SearchResult searchedProfileMember = getProfileAPI().searchProfileMembers("user", builder.done()); assertEquals(2, searchedProfileMember.getResult().size()); assertEquals("User1FirstName", searchedProfileMember.getResult().get(0).getDisplayNamePart1()); assertEquals("User1LastName", searchedProfileMember.getResult().get(0).getDisplayNamePart2()); assertEquals("userName1", searchedProfileMember.getResult().get(0).getDisplayNamePart3()); } @Test public void searchUserProfileMembersOfGroup() throws BonitaException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(ProfileMemberSearchDescriptor.GROUP_ID, group1.getId()); final SearchResult searchedProfileMember = getProfileAPI().searchProfileMembers("group", builder.done()); assertEquals(1, searchedProfileMember.getResult().size()); assertEquals("group1", searchedProfileMember.getResult().get(0).getDisplayNamePart1()); } @Test public void searchUserProfileMembersOfRole() throws BonitaException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(ProfileMemberSearchDescriptor.ROLE_ID, role1.getId()); final SearchResult searchedProfileMember = getProfileAPI().searchProfileMembers("role", builder.done()); assertEquals(1, searchedProfileMember.getResult().size()); assertEquals("role1", searchedProfileMember.getResult().get(0).getDisplayNamePart1()); } @Test public void searchUserProfileMembersOfRoleAndGroup() throws BonitaException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(ProfileMemberSearchDescriptor.ROLE_ID, role2.getId()); builder.filter(ProfileMemberSearchDescriptor.GROUP_ID, group2.getId()); final SearchResult searchedProfileMember = getProfileAPI().searchProfileMembers("roleAndGroup", builder.done()); assertEquals(1, searchedProfileMember.getResult().size()); assertEquals("role2", searchedProfileMember.getResult().get(0).getDisplayNamePart1()); assertEquals("group2", searchedProfileMember.getResult().get(0).getDisplayNamePart2()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchActivityInstanceIT.java ================================================ /** * 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.search; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.test.TestStates.SKIPPED; import static org.junit.Assert.*; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.NamedElement; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.flownode.*; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.connectors.TestConnectorLongToExecute; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.test.TestStates; import org.bonitasoft.engine.test.check.CheckNbOfActivities; import org.junit.Test; /** * @author Baptiste Mesta * @author Celine Souchet */ public class SearchActivityInstanceIT extends TestWithUser { final long ONE_HOUR = 60L * 60L * 1000L; /** * Tests that the archiving mechanism works: *
    *
  • archived activities must be found in the archive
  • *
  • archived activities must be deleted from the journal
  • *
* * @throws Exception * @since 6.0 */ @Test public void activityArchivingMechanism() throws Exception { // create user and process final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addDescription("Process to test archiving mechanism") .addUserTask("userTask", ACTOR_NAME) .addUserTask("secondTask", ACTOR_NAME); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(user.getId(), processDefinition.getId()); // Wait for 2 activities in READY state: waitForUserTask(processInstance, "userTask"); waitForUserTask(processInstance, "secondTask"); // Check that no tasks are archived yet: SearchOptionsBuilder searchBuilder = new SearchOptionsBuilder(0, 12); searchBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, processInstance.getId()); SearchResult archActivitResult = getProcessAPI() .searchArchivedActivities(searchBuilder.done()); assertEquals(0, archActivitResult.getCount()); // Skip the 2 tasks one by one: final List activities = getProcessAPI().getActivities(processInstance.getId(), 0, 2); int nbTasksArchived = 0; for (final ActivityInstance activityInstance : activities) { skipTask(activityInstance.getId()); nbTasksArchived++; final int nbActivities = 2 - nbTasksArchived; // Check nb of remaining tasks in the journal: final CheckNbOfActivities checkNbActivities = new CheckNbOfActivities(getProcessAPI(), 200, 3000, true, processInstance, nbActivities); final boolean waitUntil = checkNbActivities.waitUntil(); assertTrue("Expected " + nbActivities + " open activities for process instance " + processInstance.getId() + " but got " + checkNbActivities.getResult().size(), waitUntil); // Check that both tasks are found in the archive: searchBuilder = new SearchOptionsBuilder(0, 12); searchBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, processInstance.getId()); archActivitResult = getProcessAPI().searchArchivedActivities(searchBuilder.done()); assertEquals(nbTasksArchived, archActivitResult.getCount()); } disableAndDeleteProcess(processDefinition); } @Test public void searchArchivedTasksManagedBy() throws Exception { // Create tasks, assign them, some to some users managed by "manager", some to other users with different manager. // execute / skip them so that the tasks (or only some) are archived. // Retrieve the tasks. check the count, the retrieved list, the order. final User jack = createUser("jack", "bpm"); // Jules is not subordinates of jack: final User jules = createUser("jules", "bpm"); final User john = createUser("john", "bpm", jack.getId()); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addDescription("Famous French actor"); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("userTask1", ACTOR_NAME) .addUserTask("userTask2", ACTOR_NAME) .addUserTask("userTask3", ACTOR_NAME).addUserTask("task4", ACTOR_NAME) .addUserTask("userTask5", ACTOR_NAME) .addUserTask("userTask6", ACTOR_NAME).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, jack); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); final long stepId1 = waitForUserTaskAndAssignIt(pi0, "userTask1", john).getId(); final long stepId2 = waitForUserTaskAndAssignIt(pi0, "userTask2", john).getId(); final long stepId3 = waitForUserTaskAndAssignIt(pi0, "userTask3", jack).getId(); final long stepId4 = waitForUserTaskAndAssignIt(pi0, "task4", john).getId(); final long stepId5 = waitForUserTaskAndAssignIt(pi0, "userTask5", jules).getId(); final long stepId6 = waitForUserTask(pi0, "userTask6"); // don't assign userTask6 to anyone. skipTask(stepId1); skipTask(stepId2); skipTask(stepId3); skipTask(stepId4); skipTask(stepId5); skipTask(stepId6); waitForProcessToFinish(pi0); SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); // filter only userTask1: builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.NAME, "userTask1"); builder.sort(ArchivedHumanTaskInstanceSearchDescriptor.NAME, Order.ASC); SearchResult aHumanTasksRes = getProcessAPI() .searchArchivedHumanTasksManagedBy(jack.getId(), builder.done()); assertEquals(1, aHumanTasksRes.getCount()); List tasks = aHumanTasksRes.getResult(); ArchivedHumanTaskInstance aArchivedHumanTaskInstance = tasks.get(0); assertEquals("userTask1", aArchivedHumanTaskInstance.getName()); builder = new SearchOptionsBuilder(0, 10); // filter all userTask*: builder.searchTerm("userTask"); builder.sort(ArchivedHumanTaskInstanceSearchDescriptor.NAME, Order.ASC); aHumanTasksRes = getProcessAPI().searchArchivedHumanTasksManagedBy(jack.getId(), builder.done()); assertEquals(2, aHumanTasksRes.getCount()); tasks = aHumanTasksRes.getResult(); aArchivedHumanTaskInstance = tasks.get(0); assertEquals("userTask1", aArchivedHumanTaskInstance.getName()); aArchivedHumanTaskInstance = tasks.get(1); assertEquals("userTask2", aArchivedHumanTaskInstance.getName()); builder = new SearchOptionsBuilder(0, 10); // filter all ask*: builder.searchTerm("user"); builder.sort(ArchivedHumanTaskInstanceSearchDescriptor.NAME, Order.ASC); aHumanTasksRes = getProcessAPI().searchArchivedHumanTasksManagedBy(jack.getId(), builder.done()); assertEquals(2, aHumanTasksRes.getCount()); tasks = aHumanTasksRes.getResult(); aArchivedHumanTaskInstance = tasks.get(0); assertEquals("userTask1", aArchivedHumanTaskInstance.getName()); aArchivedHumanTaskInstance = tasks.get(1); assertEquals("userTask2", aArchivedHumanTaskInstance.getName()); builder = new SearchOptionsBuilder(0, 10); // filter all task of this process definition: builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId()); builder.sort(ArchivedHumanTaskInstanceSearchDescriptor.NAME, Order.ASC); aHumanTasksRes = getProcessAPI().searchArchivedHumanTasksManagedBy(jack.getId(), builder.done()); assertEquals(3, aHumanTasksRes.getCount()); tasks = aHumanTasksRes.getResult(); aArchivedHumanTaskInstance = tasks.get(0); assertEquals("task4", aArchivedHumanTaskInstance.getName()); aArchivedHumanTaskInstance = tasks.get(1); assertEquals("userTask1", aArchivedHumanTaskInstance.getName()); aArchivedHumanTaskInstance = tasks.get(2); assertEquals("userTask2", aArchivedHumanTaskInstance.getName()); disableAndDeleteProcess(processDefinition); deleteUsers(john, jack, jules); } @Test public void searchAssignedTasksManagedBy() throws Exception { final User jack = createUser("jack", "bpm"); final User john = createUser("john", "bpm", jack.getId()); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("userTask1", ACTOR_NAME) .addUserTask("userTask2", ACTOR_NAME) .addUserTask("userTask3", ACTOR_NAME).addUserTask("task4", ACTOR_NAME).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndAssignIt(processInstance, "userTask1", john).getId(); waitForUserTaskAndAssignIt(processInstance, "userTask2", john).getId(); waitForUserTaskAndAssignIt(processInstance, "userTask3", jack).getId(); waitForUserTaskAndAssignIt(processInstance, "task4", john).getId(); SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(HumanTaskInstanceSearchDescriptor.NAME, "userTask1"); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); SearchResult searchAssignedTasksManagedBy = getProcessAPI() .searchAssignedTasksManagedBy(jack.getId(), builder.done()); assertEquals(1, searchAssignedTasksManagedBy.getCount()); List tasks = searchAssignedTasksManagedBy.getResult(); HumanTaskInstance humanTaskInstance = tasks.get(0); assertEquals("userTask1", humanTaskInstance.getName()); builder = new SearchOptionsBuilder(0, 10); builder.searchTerm("userTask"); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchAssignedTasksManagedBy = getProcessAPI().searchAssignedTasksManagedBy(jack.getId(), builder.done()); assertEquals(2, searchAssignedTasksManagedBy.getCount()); tasks = searchAssignedTasksManagedBy.getResult(); humanTaskInstance = tasks.get(0); assertEquals("userTask1", humanTaskInstance.getName()); humanTaskInstance = tasks.get(1); assertEquals("userTask2", humanTaskInstance.getName()); builder = new SearchOptionsBuilder(0, 10); builder.searchTerm("user"); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchAssignedTasksManagedBy = getProcessAPI().searchAssignedTasksManagedBy(jack.getId(), builder.done()); assertEquals(2, searchAssignedTasksManagedBy.getCount()); tasks = searchAssignedTasksManagedBy.getResult(); humanTaskInstance = tasks.get(0); assertEquals("userTask1", humanTaskInstance.getName()); humanTaskInstance = tasks.get(1); assertEquals("userTask2", humanTaskInstance.getName()); disableAndDeleteProcess(processDefinition); deleteUsers(john, jack); } @Test public void searchHumanTaskInstances() throws Exception { final Group group = createGroup("groupName"); final Role role = createRole("roleName"); // First process def with 2 instances: final DesignProcessDefinition designProcessDef1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(asList("initTask1"), asList(true)); final ProcessDefinition processDef1 = deployAndEnableProcessWithActor(designProcessDef1, ACTOR_NAME, user); final ProcessInstance pi1 = getProcessAPI().startProcess(processDef1.getId()); final long step1Id = waitForUserTask(pi1, "initTask1"); final ProcessInstance pi2 = getProcessAPI().startProcess(processDef1.getId()); waitForUserTask(pi2, "initTask1"); final DesignProcessDefinition designProcessDef2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME + 2, PROCESS_VERSION, asList("initTask2"), asList(true)); final ProcessDefinition processDef2 = deployAndEnableProcessWithActor(designProcessDef2, ACTOR_NAME, user); final ProcessInstance pi3 = getProcessAPI().startProcess(processDef2.getId()); waitForUserTask(pi3, "initTask2"); final ProcessInstance pi4 = getProcessAPI().startProcess(processDef2.getId()); waitForUserTask(pi4, "initTask2"); final ProcessInstance pi5 = getProcessAPI().startProcess(processDef2.getId()); waitForUserTask(pi5, "initTask2"); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); final SearchResult humanTasksSearch = getProcessAPI() .searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(5, humanTasksSearch.getCount()); searchHumanTaskInstancesFilteredByPriority(pi2); searchHumanTaskInstancesFilteredByAssigneeId(); searchHumanTaskInstancesFilteredByProcessInstance(pi2); searchHumanTaskInstancesFilteredByProcessDefinition(processDef2); searchHumanTaskInstancesFilteredByState(step1Id); searchHumanTaskInstancesFilteredByUser(user.getId(), processDef1.getId()); searchHumanTaskInstancesFilteredByGroup(group.getId(), processDef2.getId()); searchHumanTaskInstancesFilteredByRole(role.getId(), processDef1.getId()); searchHumanTaskInstancesFilteredByMembership(group.getId(), role.getId(), processDef2.getId()); searchHumanTaskInstancesByTerm(); disableAndDeleteProcess(processDef1, processDef2); deleteGroups(group); deleteRoles(role); } @Test public void searchAssignedAndPendingHumanTaskInstances() throws Exception { BusinessArchive bar1 = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance("p1", "1.0") .addActor("a") .addUserTask("p1task1", "a") .addUserTask("p1task2", "a") .addUserTask("p1task3", "a") .addConnector("c1", "cid", "1.0", ConnectorEvent.ON_FINISH).getProcess()) .addConnectorImplementation( BuildTestUtil.generateConnectorImplementation("cid", "1.0", SlowConnector.class)) .done(); BusinessArchive bar2 = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance("p2", "1.0") .addActor("a") .addUserTask("p2task1", "a") .addUserTask("p2task2", "a") .addUserTask("p2task3", "a") .addConnector("c1", "cid", "1.0", ConnectorEvent.ON_FINISH).getProcess()) .addConnectorImplementation( BuildTestUtil.generateConnectorImplementation("cid", "1.0", SlowConnector.class)) .done(); ProcessDefinition p1 = deployAndEnableProcessWithActor(bar1, "a", user); ProcessDefinition p2 = deployAndEnableProcessWithActor(bar2, "a", user); var instance1 = getProcessAPI().startProcess(p1.getId()); var instance2 = getProcessAPI().startProcess(p2.getId()); waitForUserTask("p1task1"); long p1task2 = waitForUserTask("p1task2"); long p1task3 = waitForUserTask("p1task3"); waitForUserTask("p2task1"); long p2task2 = waitForUserTask("p2task2"); long p2task3 = waitForUserTask("p2task3"); getProcessAPI().assignUserTask(p1task2, user.getId()); getProcessAPI().assignUserTask(p1task3, user.getId()); getProcessAPI().assignUserTask(p2task2, user.getId()); getProcessAPI().assignUserTask(p2task3, user.getId()); getProcessAPI().executeUserTask(p1task3, Collections.emptyMap()); getProcessAPI().executeUserTask(p2task3, Collections.emptyMap()); SearchResult searchResult = getProcessAPI().searchAssignedAndPendingHumanTasks( new SearchOptionsBuilder(0, 100) .sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC).done()); assertThat(searchResult).matches(haveTasks("p1task1", "p1task2", "p2task1", "p2task2"), toDescription(searchResult)); searchResult = getProcessAPI().searchAssignedAndPendingHumanTasks(new SearchOptionsBuilder(0, 100) .searchTerm("p1") .sort(HumanTaskInstanceSearchDescriptor.NAME, Order.DESC).done()); assertThat(searchResult).matches(haveTasks("p1task2", "p1task1"), toDescription(searchResult)); searchResult = getProcessAPI().searchAssignedAndPendingHumanTasks(new SearchOptionsBuilder(0, 100) .filter("name", "p1task3").done()); assertThat(searchResult).matches(haveTasks(), toDescription(searchResult)); try { waitForTaskInState(instance1, "p1task3", TestStates.NORMAL_FINAL); } catch (ActivityInstanceNotFoundException e) { // ignore it, instance is already completed } try { waitForTaskInState(instance2, "p2task3", TestStates.NORMAL_FINAL); } catch (ActivityInstanceNotFoundException e) { // ignore it, instance is already completed } disableAndDeleteProcess(p1, p2); } private String toDescription(SearchResult searchResult) { return "had tasks: " + searchResult.getResult().stream().map(NamedElement::getName).collect(Collectors.joining(", ")); } private Predicate> haveTasks(String... tasks) { return result -> result.getCount() == tasks.length && IntStream.range(0, tasks.length) .mapToObj(i -> tasks[i].equals(result.getResult().get(i).getName())) .allMatch(b -> b); } private void searchHumanTaskInstancesFilteredByUser(final long userId, final long processDefinitionId) throws Exception { final ProcessSupervisor supervisor = getProcessAPI().createProcessSupervisorForUser(processDefinitionId, userId); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.USER_ID, userId); final SearchResult humanTasksSearch = getProcessAPI() .searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(2, humanTasksSearch.getCount()); for (final HumanTaskInstance task : humanTasksSearch.getResult()) { assertEquals("initTask1", task.getName()); } getProcessAPI().deleteSupervisor(supervisor.getSupervisorId()); } private void searchHumanTaskInstancesFilteredByGroup(final long groupId, final long processDefinitionId) throws Exception { final ProcessSupervisor supervisor = getProcessAPI().createProcessSupervisorForGroup(processDefinitionId, groupId); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.GROUP_ID, groupId); final SearchResult humanTasksSearch = getProcessAPI() .searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(3, humanTasksSearch.getCount()); for (final HumanTaskInstance task : humanTasksSearch.getResult()) { assertEquals("initTask2", task.getName()); } getProcessAPI().deleteSupervisor(supervisor.getSupervisorId()); } private void searchHumanTaskInstancesFilteredByRole(final long roleId, final long processDefinitionId) throws Exception { final ProcessSupervisor supervisor = getProcessAPI().createProcessSupervisorForRole(processDefinitionId, roleId); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.ROLE_ID, roleId); final SearchResult humanTasksSearch = getProcessAPI() .searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(2, humanTasksSearch.getCount()); for (final HumanTaskInstance task : humanTasksSearch.getResult()) { assertEquals("initTask1", task.getName()); } getProcessAPI().deleteSupervisor(supervisor.getSupervisorId()); } private void searchHumanTaskInstancesFilteredByMembership(final long groupId, final long roleId, final long processDefinitionId) throws Exception { final ProcessSupervisor supervisor = getProcessAPI().createProcessSupervisorForMembership(processDefinitionId, groupId, roleId); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.ROLE_ID, roleId); final SearchResult humanTasksSearch = getProcessAPI() .searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(3, humanTasksSearch.getCount()); for (final HumanTaskInstance task : humanTasksSearch.getResult()) { assertEquals("initTask2", task.getName()); } final SearchOptionsBuilder searchOptionsBuilder2 = new SearchOptionsBuilder(0, 45); searchOptionsBuilder2.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder2.filter(HumanTaskInstanceSearchDescriptor.GROUP_ID, groupId); final SearchResult humanTasksSearch2 = getProcessAPI() .searchHumanTaskInstances(searchOptionsBuilder2.done()); assertEquals(humanTasksSearch, humanTasksSearch2); getProcessAPI().deleteSupervisor(supervisor.getSupervisorId()); } private void searchHumanTaskInstancesFilteredByState(final long step1Id) throws UpdateException, SearchException { getProcessAPI().assignUserTask(step1Id, user.getId()); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.STATE_NAME, ActivityStates.READY_STATE); final SearchResult humanTasksSearch = getProcessAPI() .searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(5, humanTasksSearch.getCount()); for (final HumanTaskInstance task : humanTasksSearch.getResult()) { assertEquals(ActivityStates.READY_STATE, task.getState()); } } private void searchHumanTaskInstancesByTerm() throws SearchException { SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.searchTerm("initTask2"); SearchResult humanTasksSearch = getProcessAPI() .searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(3, humanTasksSearch.getCount()); for (final HumanTaskInstance task : humanTasksSearch.getResult()) { assertEquals("initTask2", task.getName()); } searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.searchTerm("initTask"); humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(5, humanTasksSearch.getCount()); for (final HumanTaskInstance task : humanTasksSearch.getResult()) { assertTrue("keyword search sould return only tasks with name containing 'initTask'", task.getName().contains("initTask")); } } private void searchHumanTaskInstancesFilteredByProcessDefinition(final ProcessDefinition processDef2) throws SearchException { SearchOptionsBuilder searchOptionsBuilder; SearchResult humanTasksSearch; searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef2.getId()); humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(3, humanTasksSearch.getCount()); } private void searchHumanTaskInstancesFilteredByProcessInstance(final ProcessInstance pi2) throws SearchException { SearchOptionsBuilder searchOptionsBuilder; SearchResult humanTasksSearch; searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, pi2.getId()); humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(1, humanTasksSearch.getCount()); } private void searchHumanTaskInstancesFilteredByAssigneeId() throws SearchException { // There should be no assigned tasks to 'user': SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, user.getId()); SearchResult humanTasksSearch = getProcessAPI() .searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(0, humanTasksSearch.getCount()); // There should be 5 non-assigned tasks: searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, 0L); humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(5, humanTasksSearch.getCount()); } private void searchHumanTaskInstancesFilteredByPriority(final ProcessInstance pi2) throws UpdateException, SearchException { // There should be 1 task which priority is above_normal: final List activityInstances = getProcessAPI().getActivities(pi2.getId(), 0, 10); getProcessAPI().setTaskPriority(activityInstances.get(0).getId(), TaskPriority.ABOVE_NORMAL); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PRIORITY, TaskPriority.ABOVE_NORMAL); final SearchResult humanTasksSearch = getProcessAPI() .searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(1, humanTasksSearch.getCount()); assertEquals(TaskPriority.ABOVE_NORMAL, humanTasksSearch.getResult().get(0).getPriority()); } @Test public void searchHumanTaskInstancesOrderByPriorityAndDueDate() throws Exception { final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_NAME, PROCESS_VERSION); definitionBuilder.addStartEvent("start"); definitionBuilder.addActor(ACTOR_NAME); definitionBuilder.addUserTask("initTask1", ACTOR_NAME).addPriority(TaskPriority.HIGHEST.name()) .addExpectedDuration(dueDateInHours(10L)); definitionBuilder.addUserTask("initTask2", ACTOR_NAME).addPriority(TaskPriority.HIGHEST.name()) .addExpectedDuration(dueDateInHours(20L)); definitionBuilder.addUserTask("initTask3", ACTOR_NAME).addPriority(TaskPriority.LOWEST.name()) .addExpectedDuration(dueDateInHours(30L)); definitionBuilder.addUserTask("initTask4", ACTOR_NAME).addPriority(TaskPriority.HIGHEST.name()) .addExpectedDuration(dueDateInHours(5L)); definitionBuilder.addUserTask("initTask5", ACTOR_NAME).addPriority(TaskPriority.NORMAL.name()) .addExpectedDuration(dueDateInHours(15L)); definitionBuilder.addUserTask("initTask6", ACTOR_NAME).addPriority(TaskPriority.NORMAL.name()) .addExpectedDuration(dueDateInHours(10L)); definitionBuilder.addEndEvent("end"); definitionBuilder.addTransition("start", "initTask1"); definitionBuilder.addTransition("start", "initTask2"); definitionBuilder.addTransition("start", "initTask3"); definitionBuilder.addTransition("start", "initTask4"); definitionBuilder.addTransition("start", "initTask5"); definitionBuilder.addTransition("start", "initTask6"); final ProcessDefinition processDef = deployAndEnableProcessWithActor(definitionBuilder.done(), ACTOR_NAME, user); getProcessAPI().startProcess(processDef.getId()); waitForUserTask("initTask1"); waitForUserTask("initTask2"); waitForUserTask("initTask3"); waitForUserTask("initTask4"); waitForUserTask("initTask5"); waitForUserTask("initTask6"); // There should be 6 tasks SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); SearchResult humanTasksSearch = getProcessAPI() .searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(6, humanTasksSearch.getCount()); List humanTaskInstances; // There should be 6 tasks when order by ascending priority. The first task is "initTask3". searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId()); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.PRIORITY, Order.ASC); humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(6, humanTasksSearch.getCount()); humanTaskInstances = humanTasksSearch.getResult(); assertEquals("initTask3", humanTaskInstances.get(0).getName()); // There should be 6 tasks when order by descending priority. The last task is "initTask3". searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId()); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.PRIORITY, Order.DESC); humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(6, humanTasksSearch.getCount()); humanTaskInstances = humanTasksSearch.getResult(); assertEquals("initTask3", humanTaskInstances.get(5).getName()); // There should be 6 tasks when order by ascending due date in this order : "initTask4", ["initTask1" "initTask6"], "initTask5", "initTask2", // "initTask3" searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId()); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.DUE_DATE, Order.ASC); humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(6, humanTasksSearch.getCount()); humanTaskInstances = humanTasksSearch.getResult(); assertEquals("initTask4", humanTaskInstances.get(0).getName()); assertEquals("initTask5", humanTaskInstances.get(3).getName()); assertEquals("initTask2", humanTaskInstances.get(4).getName()); assertEquals("initTask3", humanTaskInstances.get(5).getName()); // There should be 6 tasks when order by descending due date in this order : "initTask3", "initTask2", "initTask5", ["initTask1" "initTask6"], // "initTask4" searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId()); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.DUE_DATE, Order.DESC); humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(6, humanTasksSearch.getCount()); humanTaskInstances = humanTasksSearch.getResult(); assertEquals("initTask3", humanTaskInstances.get(0).getName()); assertEquals("initTask2", humanTaskInstances.get(1).getName()); assertEquals("initTask5", humanTaskInstances.get(2).getName()); assertEquals("initTask4", humanTaskInstances.get(5).getName()); // There should be 6 tasks when order by due date and priority in this order : "initTask4", "initTask1", "initTask2", "initTask5", "initTask6", // "initTask3" searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId()); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.PRIORITY, Order.DESC); searchOptionsBuilder.sort(HumanTaskInstanceSearchDescriptor.DUE_DATE, Order.ASC); humanTasksSearch = getProcessAPI().searchHumanTaskInstances(searchOptionsBuilder.done()); assertEquals(6, humanTasksSearch.getCount()); humanTaskInstances = humanTasksSearch.getResult(); assertEquals("initTask4", humanTaskInstances.get(0).getName()); assertEquals("initTask1", humanTaskInstances.get(1).getName()); assertEquals("initTask2", humanTaskInstances.get(2).getName()); assertEquals("initTask6", humanTaskInstances.get(3).getName()); assertEquals("initTask5", humanTaskInstances.get(4).getName()); assertEquals("initTask3", humanTaskInstances.get(5).getName()); disableAndDeleteProcess(processDef); } private Expression dueDateInHours(long nbHours) throws InvalidExpressionException { Expression expression = new ExpressionBuilder().createConstantLongExpression(nbHours * ONE_HOUR); return expression; } /** * @throws Exception */ private void searchPendingTasks(final String taskName, final String actorName) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(actorName); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(taskName, actorName) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, actorName, user); // -------- start process and wait for tasks final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, taskName); // -------- test pending task search methods final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, 0); builder.filter(HumanTaskInstanceSearchDescriptor.STATE_NAME, ActivityStates.READY_STATE); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); builder.searchTerm("'"); final SearchResult searchHumanTaskInstances = getProcessAPI() .searchHumanTaskInstances(builder.done()); assertEquals(1, searchHumanTaskInstances.getCount()); final List tasks = searchHumanTaskInstances.getResult(); assertEquals(taskName, tasks.get(0).getName()); disableAndDeleteProcess(processDefinition); } @Test public void searchArchivedActivities() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("userTask1", ACTOR_NAME) .addUserTask("userTask2", ACTOR_NAME) .addAutomaticTask("automaticTask").addManualTask("manualTask", ACTOR_NAME).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi1 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance pi2 = getProcessAPI().startProcess(processDefinition.getId()); // No need to verify anything, if no exception, query exists getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, pi1.getId()) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.AUTOMATIC_TASK).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, pi1.getId()) .filter(ProcessSupervisorSearchDescriptor.USER_ID, user.getId()).done()); getProcessAPI().searchActivities( new SearchOptionsBuilder(0, 10) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.AUTOMATIC_TASK) .filter(ProcessSupervisorSearchDescriptor.USER_ID, pi1.getId()).done()); waitForUserTask(pi1, "userTask1"); waitForUserTaskAndAssignIt(pi1, "userTask2", user); waitForUserTask(pi1, "manualTask"); waitForUserTask(pi2, "userTask1"); waitForUserTask(pi2, "userTask2"); waitForUserTask(pi2, "manualTask"); // finish the tasks final List openedActivityInstances1 = getProcessAPI().getOpenActivityInstances(pi1.getId(), 0, 20, ActivityInstanceCriterion.DEFAULT); assertEquals(3, openedActivityInstances1.size()); for (final ActivityInstance activityInstance : openedActivityInstances1) { final long activityInstanceId = activityInstance.getId(); getProcessAPI().setActivityStateById(activityInstanceId, 12); } waitForProcessToFinish(pi1); final List openedActivityInstances2 = getProcessAPI().getOpenActivityInstances(pi2.getId(), 0, 20, ActivityInstanceCriterion.DEFAULT); assertEquals(3, openedActivityInstances2.size()); for (final ActivityInstance activityInstance : openedActivityInstances2) { final long activityInstanceId = activityInstance.getId(); getProcessAPI().setActivityStateById(activityInstanceId, 12); } waitForProcessToFinish(pi2); // each automatic task will have four-state archived instance SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 20); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId()); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.AUTOMATIC_TASK); SearchResult archivedActivityInstancesSearch = getProcessAPI() .searchArchivedActivities(searchOptionsBuilder.done()); assertEquals(2, archivedActivityInstancesSearch.getCount()); for (final ArchivedActivityInstance activity : archivedActivityInstancesSearch.getResult()) { assertTrue(activity instanceof ArchivedAutomaticTaskInstance); } // test activity type searchOptionsBuilder = new SearchOptionsBuilder(0, 20); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId()); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.USER_TASK); archivedActivityInstancesSearch = getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done()); assertEquals(4, archivedActivityInstancesSearch.getCount()); for (final ArchivedActivityInstance activity : archivedActivityInstancesSearch.getResult()) { assertTrue(activity instanceof ArchivedUserTaskInstance); } searchOptionsBuilder = new SearchOptionsBuilder(0, 20); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId()); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.MANUAL_TASK); archivedActivityInstancesSearch = getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done()); assertEquals(2, archivedActivityInstancesSearch.getCount()); for (final ArchivedActivityInstance activity : archivedActivityInstancesSearch.getResult()) { assertTrue(activity instanceof ArchivedManualTaskInstance); } searchOptionsBuilder = new SearchOptionsBuilder(0, 20); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId()); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.HUMAN_TASK); archivedActivityInstancesSearch = getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done()); assertEquals(6, archivedActivityInstancesSearch.getCount()); for (final ArchivedActivityInstance activity : archivedActivityInstancesSearch.getResult()) { assertTrue(activity instanceof ArchivedHumanTaskInstance); assertTrue(activity instanceof ArchivedManualTaskInstance || activity instanceof ArchivedUserTaskInstance); } searchOptionsBuilder = new SearchOptionsBuilder(0, 20); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, pi1.getId()); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.AUTOMATIC_TASK); archivedActivityInstancesSearch = getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done()); assertEquals(1, archivedActivityInstancesSearch.getCount()); for (final ArchivedActivityInstance activity : archivedActivityInstancesSearch.getResult()) { assertTrue(activity instanceof ArchivedAutomaticTaskInstance); } searchOptionsBuilder = new SearchOptionsBuilder(0, 20); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, pi1.getId()); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.HUMAN_TASK); archivedActivityInstancesSearch = getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done()); assertEquals(3, archivedActivityInstancesSearch.getCount()); for (final ArchivedActivityInstance activity : archivedActivityInstancesSearch.getResult()) { assertTrue(activity instanceof ArchivedHumanTaskInstance); assertTrue(activity instanceof ArchivedManualTaskInstance || activity instanceof ArchivedUserTaskInstance); } searchOptionsBuilder = new SearchOptionsBuilder(0, 20); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId()); searchOptionsBuilder.sort(ArchivedActivityInstanceSearchDescriptor.ARCHIVE_DATE, Order.DESC); archivedActivityInstancesSearch = getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done()); assertEquals(8, archivedActivityInstancesSearch.getCount()); for (final ArchivedActivityInstance activity : archivedActivityInstancesSearch.getResult()) { assertTrue(activity instanceof ArchivedManualTaskInstance || activity instanceof ArchivedUserTaskInstance || activity instanceof ArchivedAutomaticTaskInstance); } searchOptionsBuilder = new SearchOptionsBuilder(0, 20); searchOptionsBuilder.searchTerm("userTask"); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId()); searchOptionsBuilder.sort(ArchivedActivityInstanceSearchDescriptor.NAME, Order.DESC); final SearchResult taskInstanceSearchResult = getProcessAPI() .searchArchivedActivities(searchOptionsBuilder.done()); assertEquals(4, taskInstanceSearchResult.getCount()); final List archivedActivities = taskInstanceSearchResult.getResult(); final ArchivedActivityInstance aut1 = archivedActivities.get(0); final ArchivedActivityInstance aut2 = archivedActivities.get(1); final ArchivedActivityInstance aut3 = archivedActivities.get(2); final ArchivedActivityInstance aut4 = archivedActivities.get(3); assertEquals("userTask2", aut1.getName()); assertEquals("userTask2", aut2.getName()); assertEquals("userTask1", aut3.getName()); assertEquals("userTask1", aut4.getName()); disableAndDeleteProcess(processDefinition); } @Test public void searchArchivedTasks() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("userTask1", ACTOR_NAME) .addUserTask("userTask2", ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(pi0, "userTask1"); final long step2Id = waitForUserTaskAndAssignIt(pi0, "userTask2", user).getId(); getProcessAPI().setActivityStateByName(step1Id, SKIPPED.getStateName()); getProcessAPI().setActivityStateByName(step2Id, SKIPPED.getStateName()); waitForProcessToFinish(pi0); SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId()); builder.sort(ArchivedHumanTaskInstanceSearchDescriptor.NAME, Order.DESC); SearchResult taskInstanceSearchResult = getProcessAPI() .searchArchivedHumanTasks(builder.done()); assertEquals(2, taskInstanceSearchResult.getCount()); List archivedTasks = taskInstanceSearchResult.getResult(); final ArchivedHumanTaskInstance aut1 = archivedTasks.get(0); final ArchivedHumanTaskInstance aut2 = archivedTasks.get(1); assertEquals("userTask2", aut1.getName()); assertEquals("userTask1", aut2.getName()); final String archivedTaskActorName = getProcessAPI().getActor(aut1.getActorId()).getName(); assertEquals(ACTOR_NAME, archivedTaskActorName); // filter task assigned by user builder = new SearchOptionsBuilder(0, 10); builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, user.getId()); builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId()); builder.sort(ArchivedHumanTaskInstanceSearchDescriptor.NAME, Order.ASC); taskInstanceSearchResult = getProcessAPI().searchArchivedHumanTasks(builder.done()); assertEquals(1, taskInstanceSearchResult.getCount()); archivedTasks = taskInstanceSearchResult.getResult(); assertEquals("userTask2", archivedTasks.get(0).getName()); // search with sort on reached state date List descResult = getProcessAPI().searchArchivedHumanTasks( new SearchOptionsBuilder(0, 10) .sort(ArchivedHumanTaskInstanceSearchDescriptor.REACHED_STATE_DATE, Order.DESC).done()) .getResult(); assertThat(descResult.get(0).getReachedStateDate()).isAfterOrEqualsTo(descResult.get(1).getReachedStateDate()); List ascResult = getProcessAPI().searchArchivedHumanTasks( new SearchOptionsBuilder(0, 10) .sort(ArchivedHumanTaskInstanceSearchDescriptor.REACHED_STATE_DATE, Order.ASC).done()) .getResult(); assertThat(ascResult.get(0).getReachedStateDate()).isBeforeOrEqualsTo(ascResult.get(1).getReachedStateDate()); disableAndDeleteProcess(processDefinition); } @Test public void searchArchivedActivitiesInTerminalState() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("userTask1", ACTOR_NAME) .addUserTask("userTask2", ACTOR_NAME) .addUserTask("userTask3", ACTOR_NAME).addTransition("userTask2", "userTask3").getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndAssignIt(pi0, "userTask1", user); waitForUserTaskAndExecuteIt(pi0, "userTask2", user); waitForUserTask(pi0, "userTask3"); SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10) .filter(ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL, true); SearchResult searchFlowNodeInstances = getProcessAPI() .searchArchivedFlowNodeInstances(builder.done()); assertEquals(1, searchFlowNodeInstances.getResult().size()); assertEquals("userTask2", searchFlowNodeInstances.getResult().get(0).getName()); builder = new SearchOptionsBuilder(0, 10).filter(ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL, false); searchFlowNodeInstances = getProcessAPI().searchArchivedFlowNodeInstances(builder.done()); assertTrue(searchFlowNodeInstances.getResult().size() > 1); disableAndDeleteProcess(processDefinition); } @Test public void searchArchivedTasksWithApostrophe() throws Exception { final String taskName = "'Task"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(taskName, ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(pi0, taskName); final List activityInstances = getProcessAPI().getActivities(pi0.getId(), 0, 10); assertEquals(1, activityInstances.size()); for (final ActivityInstance activityInstance : activityInstances) { final long activityInstanceId = activityInstance.getId(); getProcessAPI().setActivityStateById(activityInstanceId, 12); } waitForProcessToFinish(pi0); final SearchResult taskInstanceSearchResult = getProcessAPI() .searchArchivedHumanTasks( new SearchOptionsBuilder(0, 10).searchTerm("'").done()); assertEquals(1, taskInstanceSearchResult.getCount()); final List archivedTasks = taskInstanceSearchResult.getResult(); final ArchivedHumanTaskInstance archivedHumanTaskInstance = archivedTasks.get(0); assertEquals(taskName, archivedHumanTaskInstance.getName()); final String archivedTaskActorName = getProcessAPI().getActor(archivedHumanTaskInstance.getActorId()).getName(); assertEquals(ACTOR_NAME, archivedTaskActorName); disableAndDeleteProcess(processDefinition); } /** * if you remove a process between the two calls, re-deploy it and re-instantiate it: it should get right results. * * @throws Exception */ @Test public void searchPendingUserTaskInstances() throws Exception { searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.NAME_ASC); searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.NAME_DESC); searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.REACHED_STATE_DATE_ASC); searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.REACHED_STATE_DATE_DESC); searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.LAST_UPDATE_ASC); searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.LAST_UPDATE_DESC); searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.PRIORITY_ASC); searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.PRIORITY_DESC); searchPendingUserTaskInstancesByActivityInstanceCriterion(ActivityInstanceCriterion.DEFAULT); } private void searchPendingUserTaskInstancesByActivityInstanceCriterion( final ActivityInstanceCriterion activityInstanceCriterion) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask("Request", ACTOR_NAME).addPriority(TaskPriority.LOWEST.name()); processBuilder.addUserTask("Request2", ACTOR_NAME); processBuilder.addUserTask("Approval", ACTOR_NAME); processBuilder.addUserTask("Approval2", ACTOR_NAME).addPriority(TaskPriority.HIGHEST.name()); processBuilder.addTransition("Request", "Approval"); processBuilder.addTransition("Request2", "Approval2"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "Request"); waitForUserTask(processInstance, "Request2"); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 20); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.NAME, "Request2"); final SearchResult humanTasksSearch = getProcessAPI().searchPendingTasksForUser(user.getId(), searchOptionsBuilder.done()); assertEquals(1, humanTasksSearch.getCount()); final HumanTaskInstance userTaskId = humanTasksSearch.getResult().get(0); assignAndExecuteStep(userTaskId, user.getId()); waitForUserTask(processInstance, "Approval2"); final List userTaskInstances = getProcessAPI().getPendingHumanTaskInstances(user.getId(), 0, 10, activityInstanceCriterion); assertNotNull(userTaskInstances); assertEquals(2, userTaskInstances.size()); switch (activityInstanceCriterion) { case NAME_ASC: assertEquals("Approval2", userTaskInstances.get(0).getName()); break; case NAME_DESC: assertEquals("Request", userTaskInstances.get(0).getName()); break; case REACHED_STATE_DATE_ASC: assertEquals("Request", userTaskInstances.get(0).getName()); break; case REACHED_STATE_DATE_DESC: assertEquals("Approval2", userTaskInstances.get(0).getName()); break; case LAST_UPDATE_ASC: assertEquals("Request", userTaskInstances.get(0).getName()); break; case LAST_UPDATE_DESC: assertEquals("Approval2", userTaskInstances.get(0).getName()); break; case PRIORITY_ASC: assertEquals("Request", userTaskInstances.get(0).getName()); break; case PRIORITY_DESC: case DEFAULT: default: assertEquals("Approval2", userTaskInstances.get(0).getName()); break; } // Clean up disableAndDeleteProcess(processDefinition); } @Test public void searchPendingTasks() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("userTask1", ACTOR_NAME) .addUserTask("userTask2", ACTOR_NAME) .addUserTask("userTask3", ACTOR_NAME).addUserTask("task4", ACTOR_NAME) .addUserTask("userTask5", ACTOR_NAME).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); // -------- start process and wait for tasks final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(pi0, "userTask1"); waitForUserTaskAndAssignIt(pi0, "userTask2", user); waitForUserTaskAndAssignIt(pi0, "userTask3", user); waitForUserTask(pi0, "task4"); waitForUserTask(pi0, "userTask5"); // -------- test pending task search methods SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, 0); builder.filter(HumanTaskInstanceSearchDescriptor.STATE_NAME, ActivityStates.READY_STATE); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); SearchResult searchHumanTaskInstances = getProcessAPI() .searchHumanTaskInstances(builder.done()); assertEquals(3, searchHumanTaskInstances.getCount()); List tasks = searchHumanTaskInstances.getResult(); HumanTaskInstance humanTaskInstance = tasks.get(0); assertEquals("task4", humanTaskInstance.getName()); humanTaskInstance = tasks.get(1); assertEquals("userTask1", humanTaskInstance.getName()); humanTaskInstance = tasks.get(2); assertEquals("userTask5", humanTaskInstance.getName()); // -------- test assign task search methods builder = new SearchOptionsBuilder(0, 10); builder.filter(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, user.getId()); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchHumanTaskInstances = getProcessAPI().searchHumanTaskInstances(builder.done()); assertEquals(2, searchHumanTaskInstances.getCount()); tasks = searchHumanTaskInstances.getResult(); humanTaskInstance = tasks.get(0); assertEquals("userTask2", humanTaskInstance.getName()); humanTaskInstance = tasks.get(1); assertEquals("userTask3", humanTaskInstance.getName()); disableAndDeleteProcess(processDefinition); } @Test public void searchAssignedAndPendingHumanTasks() throws Exception { final User john = createUser("John", PASSWORD); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("userTask1", ACTOR_NAME) .addUserTask("userTask2", ACTOR_NAME) .addUserTask("userTask3", ACTOR_NAME).addUserTask("task4", ACTOR_NAME) .addUserTask("userTask5", ACTOR_NAME).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, asList(user, john)); // -------- start process and wait for tasks final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(pi0, "userTask1"); waitForUserTaskAndAssignIt(pi0, "userTask2", user); waitForUserTaskAndAssignIt(pi0, "userTask3", user); waitForUserTaskAndAssignIt(pi0, "task4", john); waitForUserTask(pi0, "userTask5"); // -------- test assigned & pending task search methods SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); SearchResult searchHumanTaskInstances = getProcessAPI() .searchAssignedAndPendingHumanTasks(processDefinition.getId(), builder.done()); assertEquals(5, searchHumanTaskInstances.getCount()); List tasks = searchHumanTaskInstances.getResult(); assertEquals("task4", tasks.get(0).getName()); assertEquals("userTask1", tasks.get(1).getName()); assertEquals("userTask2", tasks.get(2).getName()); // -------- test assign task search methods builder = new SearchOptionsBuilder(0, 10); builder.filter(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, user.getId()); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchHumanTaskInstances = getProcessAPI().searchAssignedAndPendingHumanTasks(processDefinition.getId(), builder.done()); assertEquals(2, searchHumanTaskInstances.getCount()); tasks = searchHumanTaskInstances.getResult(); assertEquals("userTask2", tasks.get(0).getName()); assertEquals("userTask3", tasks.get(1).getName()); disableAndDeleteProcess(processDefinition); deleteUser(john); } @Test public void searchAssignedAndPendingHumanTasksFor() throws Exception { final User john = createUser("John", PASSWORD); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("userTask1", ACTOR_NAME) .addUserTask("userTask2", ACTOR_NAME) .addUserTask("userTask3", ACTOR_NAME).addUserTask("task4", ACTOR_NAME) .addUserTask("userTask5", ACTOR_NAME).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, asList(user, john)); // -------- start process and wait for tasks final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(pi0, "userTask1"); waitForUserTaskAndAssignIt(pi0, "userTask2", user); waitForUserTaskAndAssignIt(pi0, "userTask3", user); waitForUserTaskAndAssignIt(pi0, "task4", john); waitForUserTask(pi0, "userTask5"); // -------- test pending task search methods SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); SearchResult searchHumanTaskInstances = getProcessAPI() .searchAssignedAndPendingHumanTasksFor(processDefinition.getId(), john.getId(), builder.done()); assertEquals(3, searchHumanTaskInstances.getCount()); List tasks = searchHumanTaskInstances.getResult(); assertEquals("task4", tasks.get(0).getName()); assertEquals("userTask1", tasks.get(1).getName()); assertEquals("userTask5", tasks.get(2).getName()); // -------- test assign task search methods builder = new SearchOptionsBuilder(0, 10); builder.filter(HumanTaskInstanceSearchDescriptor.NAME, "userTask1"); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); searchHumanTaskInstances = getProcessAPI().searchAssignedAndPendingHumanTasksFor(processDefinition.getId(), john.getId(), builder.done()); assertEquals(1, searchHumanTaskInstances.getCount()); tasks = searchHumanTaskInstances.getResult(); assertEquals("userTask1", tasks.get(0).getName()); disableAndDeleteProcess(processDefinition); deleteUser(john); } @Test public void searchPendingHumanTasksAssignedToUser() throws Exception { final User john = createUser("John", PASSWORD); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); processBuilder .addUserTask("userTask1", ACTOR_NAME) .addUserTask("userTask2", ACTOR_NAME) .addUserTask("userTask3", ACTOR_NAME) .addUserTask("userTask4_assigned_to_John", ACTOR_NAME) .addUserTask("userTask5_assigned_to_John", ACTOR_NAME); processBuilder .addUserTask("userTask6_assigned_to_John_long_execution", ACTOR_NAME) .addConnector("Simulate long execution", "slow-connector", "1.0", ConnectorEvent.ON_FINISH); processBuilder.addUserTask("userTask7", ACTOR_NAME); final BusinessArchive businessArchive = new BusinessArchiveBuilder() .createNewBusinessArchive() .setProcessDefinition(processBuilder.done()) .addConnectorImplementation( new BarResource( "slow-connector.impl", BuildTestUtil.buildConnectorImplementationFile("slow-connector", "1.0", "slow-connector-impl", "1.0", SlowConnector.class.getName()))) .done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, asList(user, john)); // -------- start process and wait for tasks final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "userTask1"); waitForUserTaskAndAssignIt(processInstance, "userTask2", user); waitForUserTaskAndAssignIt(processInstance, "userTask3", user); waitForUserTaskAndAssignIt(processInstance, "userTask4_assigned_to_John", john); waitForUserTaskAndAssignIt(processInstance, "userTask5_assigned_to_John", john); HumanTaskInstance longExecutionHumanTask = waitForUserTaskAndAssignIt(processInstance, "userTask6_assigned_to_John_long_execution", john); waitForUserTask(processInstance, "userTask7"); getProcessAPI().executeUserTask(longExecutionHumanTask.getId(), null); // -------- test pending task search methods // Perform a search SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); SearchResult searchHumanTaskInstances = getProcessAPI() .searchPendingTasksAssignedToUser(john.getId(), builder.done()); assertThat(searchHumanTaskInstances.getResult()) .describedAs("Human tasks").hasSize(2) .extracting("name").describedAs("task names") .containsExactly("userTask4_assigned_to_John", "userTask5_assigned_to_John"); // Perform a count SearchResult searchHumanTaskInstancesCountOnly = getProcessAPI() .searchPendingTasksAssignedToUser( john.getId(), new SearchOptionsBuilder(0, 0).done()); assertThat(searchHumanTaskInstancesCountOnly.getCount()).describedAs("Total Human tasks count").isEqualTo(2); assertThat(searchHumanTaskInstancesCountOnly.getResult()).describedAs("Human tasks").isEmpty(); // -------- tear down try { waitForTaskInState(processInstance, "userTask6_assigned_to_John_long_execution", TestStates.NORMAL_FINAL); } catch (ActivityInstanceNotFoundException e) { // ignore it, activity is already finished } disableAndDeleteProcess(processDefinition); deleteUser(john); } @Test public void searchPendingTasksManagedBy() throws Exception { // Create tasks, some to some users managed by "manager", some to other users with different manager. final User jack = createUser("jack", "bpm"); // Jules is not subordinates of jack: final User jules = createUser("jules", "bpm"); final User john = createUser("john", "bpm", jack.getId()); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME).addDescription("Famous French actor"); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("userTask1", ACTOR_NAME) .addUserTask("userTask2", ACTOR_NAME) .addUserTask("userTask3", ACTOR_NAME).addUserTask("task4", ACTOR_NAME) .addUserTask("userTask5", ACTOR_NAME) .addUserTask("userTask6", ACTOR_NAME).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, john); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(pi0, "userTask1"); waitForUserTask(pi0, "userTask2"); waitForUserTask(pi0, "userTask3"); waitForUserTask(pi0, "task4"); waitForUserTask(pi0, "userTask5"); waitForUserTask(pi0, "userTask6"); // filter all *userTask*, managedBy jack: SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.searchTerm("usertask"); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); SearchResult aHumanTasksRes = getProcessAPI().searchPendingTasksManagedBy(jack.getId(), builder.done()); assertEquals(5, aHumanTasksRes.getCount()); List tasks = aHumanTasksRes.getResult(); HumanTaskInstance humanTaskInstance = tasks.get(0); assertEquals("userTask1", humanTaskInstance.getName()); humanTaskInstance = tasks.get(1); assertEquals("userTask2", humanTaskInstance.getName()); humanTaskInstance = tasks.get(2); assertEquals("userTask3", humanTaskInstance.getName()); humanTaskInstance = tasks.get(3); assertEquals("userTask5", humanTaskInstance.getName()); humanTaskInstance = tasks.get(4); assertEquals("userTask6", humanTaskInstance.getName()); // filter all *userTask*, managedBy jules: builder = new SearchOptionsBuilder(0, 10); builder.searchTerm("usertask"); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); aHumanTasksRes = getProcessAPI().searchPendingTasksManagedBy(jules.getId(), builder.done()); assertEquals(0, aHumanTasksRes.getCount()); assertTrue(aHumanTasksRes.getResult().isEmpty()); // filter task4, managedBy jack: builder = new SearchOptionsBuilder(0, 10); builder.searchTerm("task"); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.DESC); aHumanTasksRes = getProcessAPI().searchPendingTasksManagedBy(jack.getId(), builder.done()); assertEquals(6, aHumanTasksRes.getCount()); disableAndDeleteProcess(processDefinition); deleteUsers(john, jack, jules); } @Test public void searchPendingTasksWithMultipleWords() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("userTask", ACTOR_NAME) .addUserTask("step1", ACTOR_NAME) .addUserTask("etape1", ACTOR_NAME).addUserTask("tache", ACTOR_NAME).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); // -------- start process and wait for tasks final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "userTask"); waitForUserTask(processInstance, "step1"); waitForUserTask(processInstance, "etape1"); waitForUserTask(processInstance, "tache"); // -------- test pending task search methods final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, 0); builder.filter(HumanTaskInstanceSearchDescriptor.STATE_NAME, ActivityStates.READY_STATE); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); builder.searchTerm("eta userTask step"); final SearchResult searchHumanTaskInstances = getProcessAPI() .searchHumanTaskInstances(builder.done()); assertEquals(3, searchHumanTaskInstances.getCount()); final List tasks = searchHumanTaskInstances.getResult(); assertThat(tasks).extracting("name").containsExactly("etape1", "step1", "userTask"); disableAndDeleteProcess(processDefinition); } @Test public void searchActivityTaskInstancesAdvancedFilters() throws Exception { // define a process containing one userTask. final String taskName = "ActivityForUser"; final DesignProcessDefinition designProcessDef = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps( asList(taskName), asList(true)); final ProcessDefinition processDef = deployAndEnableProcessWithActor(designProcessDef, ACTOR_NAME, user); // start twice and get 2 processInstances for processDef final ProcessInstance pi1 = getProcessAPI().startProcess(processDef.getId()); waitForUserTask(pi1, taskName); Thread.sleep(5); final long afterCreationTask1 = System.currentTimeMillis(); final ProcessInstance pi2 = getProcessAPI().startProcess(processDef.getId()); waitForUserTask(pi2, taskName); final long afterCreationTask2 = System.currentTimeMillis(); Thread.sleep(5); final ProcessInstance pi3 = getProcessAPI().startProcess(processDef.getId()); waitForUserTask(pi3, taskName); SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(ActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId()); SearchResult activityInstancesSearch = getProcessAPI() .searchActivities(searchOptionsBuilder.done()); assertEquals(3, activityInstancesSearch.getCount()); // ********* LESS_THAN operator ********* searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(ActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId()); searchOptionsBuilder.lessThan(ActivityInstanceSearchDescriptor.LAST_MODIFICATION_DATE, afterCreationTask1); activityInstancesSearch = getProcessAPI().searchActivities(searchOptionsBuilder.done()); assertEquals(1, activityInstancesSearch.getCount()); assertEquals(pi1.getId(), activityInstancesSearch.getResult().get(0).getParentProcessInstanceId()); // ********* BETWEEN operator ********* searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(ActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId()); searchOptionsBuilder.between(ActivityInstanceSearchDescriptor.LAST_MODIFICATION_DATE, afterCreationTask1, afterCreationTask2); activityInstancesSearch = getProcessAPI().searchActivities(searchOptionsBuilder.done()); assertEquals(1, activityInstancesSearch.getCount()); final ActivityInstance activityInstance2 = activityInstancesSearch.getResult().get(0); assertEquals(pi2.getId(), activityInstance2.getParentProcessInstanceId()); // ********** all fields ********** searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, activityInstance2.getType()) .filter(ActivityInstanceSearchDescriptor.DISPLAY_NAME, activityInstance2.getDisplayName()) .filter(ActivityInstanceSearchDescriptor.NAME, activityInstance2.getName()) .filter(ActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, activityInstance2.getProcessDefinitionId()) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, activityInstance2.getRootContainerId()) .filter(ActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, activityInstance2.getParentProcessInstanceId()) .filter(ActivityInstanceSearchDescriptor.LAST_MODIFICATION_DATE, activityInstance2.getLastUpdateDate().getTime()); activityInstancesSearch = getProcessAPI().searchActivities(searchOptionsBuilder.done()); assertEquals(1, activityInstancesSearch.getCount()); final ActivityInstance activityInstance2Result = activityInstancesSearch.getResult().get(0); assertEquals(activityInstance2, activityInstance2Result); // ********* DIFFERENT FROM operator ********* SearchOptionsBuilder sob = new SearchOptionsBuilder(0, 10); sob.filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDef.getId()); sob.differentFrom(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, 0); final SearchOptions searchOpts = sob.done(); SearchResult humanTasksSR = getProcessAPI().searchHumanTaskInstances(searchOpts); assertEquals(0, humanTasksSR.getCount()); // assign one task getProcessAPI().assignUserTask(activityInstance2.getId(), user.getId()); // Should then be 1 task assigned (ASSIGNEE_ID different from 0): humanTasksSR = getProcessAPI().searchHumanTaskInstances(searchOpts); assertEquals(1, humanTasksSR.getCount()); // ********* OR operator ********* // PROCESS_INSTANCE_ID = pi1 OR ASSIGNEE_ID different from 0 (from pi2): sob = new SearchOptionsBuilder(0, 10); sob.filter(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, pi1.getId()); sob.or(); sob.differentFrom(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, 0); humanTasksSR = getProcessAPI().searchHumanTaskInstances(sob.done()); assertEquals(2, humanTasksSR.getCount()); disableAndDeleteProcess(processDef); } @Test public void searchActivityTaskInstancesWithApostrophe() throws Exception { // define a process containing one userTask. final String taskName = "Activity'ForUser"; final DesignProcessDefinition designProcessDef = BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps( asList(taskName), asList(true)); final ProcessDefinition processDef = deployAndEnableProcessWithActor(designProcessDef, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDef.getId()); waitForUserTask(processInstance, taskName); // Search apostrophe final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.searchTerm("Activity'"); final SearchResult activityInstancesSearch = getProcessAPI() .searchActivities(searchOptionsBuilder.done()); assertEquals(1, activityInstancesSearch.getCount()); assertEquals(processInstance.getId(), activityInstancesSearch.getResult().get(0).getParentProcessInstanceId()); disableAndDeleteProcess(processDef); } @Test public void searchPendingTasksWithLikeWildcardsCharacters() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("step#1a", ACTOR_NAME) .addUserTask("step#1_b", ACTOR_NAME) .addUserTask("step#1_c", ACTOR_NAME).addUserTask("%step#2", ACTOR_NAME) .addUserTask("mystep3", ACTOR_NAME).addUserTask("%step#4_a", ACTOR_NAME) .addUserTask("step:5", ACTOR_NAME).getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); // -------- start process and wait for tasks final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step#1a"); waitForUserTask(processInstance, "step#1_b"); waitForUserTask(processInstance, "step#1_c"); waitForUserTask(processInstance, "%step#2"); waitForUserTask(processInstance, "mystep3"); waitForUserTask(processInstance, "%step#4_a"); waitForUserTask(processInstance, "step:5"); // -------- test pending task search methods SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); builder.searchTerm("step#"); final SearchResult searchHumanTaskInstancesWithEscapeCharacter = getProcessAPI() .searchHumanTaskInstances(builder.done()); assertEquals(5, searchHumanTaskInstancesWithEscapeCharacter.getCount()); List tasks = searchHumanTaskInstancesWithEscapeCharacter.getResult(); assertThat(tasks).extracting("name").containsOnly("step#1_b", "step#1_c", "step#1a", "%step#2", "%step#4_a"); builder = new SearchOptionsBuilder(0, 10); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); builder.searchTerm("step#1_"); final SearchResult searchHumanTaskInstancesWithUnderscoreCharacter = getProcessAPI() .searchHumanTaskInstances(builder.done()); assertEquals(2, searchHumanTaskInstancesWithUnderscoreCharacter.getCount()); tasks = searchHumanTaskInstancesWithUnderscoreCharacter.getResult(); assertThat(tasks).extracting("name").containsOnly("step#1_b", "step#1_c"); builder = new SearchOptionsBuilder(0, 10); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); builder.searchTerm("%step#"); final SearchResult searchHumanTaskInstancesWithPercentageCharacter = getProcessAPI() .searchHumanTaskInstances(builder.done()); assertEquals(2, searchHumanTaskInstancesWithPercentageCharacter.getCount()); tasks = searchHumanTaskInstancesWithPercentageCharacter.getResult(); assertThat(tasks).extracting("name").containsOnly("%step#2", "%step#4_a"); builder = new SearchOptionsBuilder(0, 10); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); builder.searchTerm("step:5"); final SearchResult searchHumanTaskInstancesWithColonCharacter = getProcessAPI() .searchHumanTaskInstances(builder.done()); assertEquals(1, searchHumanTaskInstancesWithColonCharacter.getCount()); tasks = searchHumanTaskInstancesWithColonCharacter.getResult(); assertThat(tasks).extracting("name").containsExactly("step:5"); disableAndDeleteProcess(processDefinition); } @Test public void searchSendTask() throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); processBuilder .addSendTask("sendTask", "myMessage", new ExpressionBuilder().createConstantStringExpression("p1")) .addConnector("wait2000ms", "testConnectorLongToExecute", "1.0.0", ConnectorEvent.ON_FINISH) .addInput("timeout", new ExpressionBuilder().createConstantLongExpression(2000)); processBuilder.addAutomaticTask("autoTask"); processBuilder.addUserTask("userTask", ACTOR_NAME); processBuilder.addTransition("autoTask", "sendTask"); processBuilder.addTransition("sendTask", "userTask"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorLongToExecute(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // waitForFlowNodeInState(processInstance, "sendTask", TestStates.INITIALIZING, true); waitForFlowNodeInExecutingState(processInstance, "sendTask", true); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(ArchivedActivityInstanceSearchDescriptor.NAME, Order.ASC); final SearchResult searchActivities = getProcessAPI().searchActivities(builder.done()); assertEquals(1, searchActivities.getCount()); final List activities = searchActivities.getResult(); final ActivityInstance activity = activities.get(0); assertEquals("sendTask", activity.getName()); waitForUserTask(processInstance, "userTask"); disableAndDeleteProcess(processDefinition); } public ProcessDefinition deployProcessWithActorAndTestConnectorLongToExecute( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user, null, "TestConnectorLongToExecute.impl", TestConnectorLongToExecute.class, "TestConnectorLongToExecute.jar"); } @Test public void should_search_prevent_injection() throws Exception { final SearchResult result = getProcessAPI().searchFlowNodeInstances( new SearchOptionsBuilder(0, 10) .filter("name", "d');DELETE * FROM TENANTS;select * from flow_node where name=toto").done()); assertThat(result.getResult()).hasSize(0); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchCommentIT.java ================================================ /** * 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.search; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.comment.ArchivedComment; import org.bonitasoft.engine.bpm.comment.ArchivedCommentsSearchDescriptor; import org.bonitasoft.engine.bpm.comment.Comment; import org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.test.WaitUntil; import org.junit.Test; /** * @author Celine Souchet */ public class SearchCommentIT extends TestWithUser { @Test public void searchArchiveComment() throws Exception { // create a ProcessDefinition final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("userTask1", ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long userTask1Id = waitForUserTask(processInstance, "userTask1"); final String commentContent1 = "commentContent1"; getProcessAPI().addProcessComment(processInstance.getId(), commentContent1); assignAndExecuteStep(userTask1Id, user); waitForProcessToFinish(processInstance); final SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 10); builder2.filter(ArchivedCommentsSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()); final SearchResult archivedCommentsResult = getProcessAPI() .searchArchivedComments(builder2.done()); final List archivedComments = archivedCommentsResult.getResult(); final ArchivedComment archivedComment = archivedComments.get(0); assertEquals(archivedComment, getProcessAPI().getArchivedComment(archivedComment.getId())); // clean all data for test disableAndDeleteProcess(processDefinition); } @Test public void searchArchivedComments() throws Exception { searchArchivedComments("commentContent", new SearchOptionsBuilder(0, 10)); } @Test public void searchArchivedCommentsWithApostrophe() throws Exception { searchArchivedComments("comment'Content", new SearchOptionsBuilder(0, 10).searchTerm("comment'")); } private void searchArchivedComments(final String commentContent, final SearchOptionsBuilder builder) throws Exception { // create a ProcessDefinition final String activityName = "userTask1"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask(activityName, ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); // create a ProcessInstance final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, activityName); // add an comment to ProcessInstance getProcessAPI().addProcessComment(processInstance.getId(), commentContent); // test the comment is added to ProcessInstance final SearchOptionsBuilder builder0 = new SearchOptionsBuilder(0, 5); builder0.filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()); final SearchResult searchResult0 = getProcessAPI().searchComments(builder0.done()); final List commentList0 = searchResult0.getResult(); assertEquals(1, commentList0.size()); // get a ActivityInstance final List activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 10); // test before archive comment final SearchOptionsBuilder builder3 = new SearchOptionsBuilder(0, 10); builder3.filter(ArchivedCommentsSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()); SearchResult archivedCommentsResult = getProcessAPI().searchArchivedComments(builder3.done()); assertEquals(0, archivedCommentsResult.getCount()); // Archive a ProcessInstance for (final ActivityInstance activityInstance : activityInstances) { final long activityInstanceId = activityInstance.getId(); getProcessAPI().setActivityStateById(activityInstanceId, 12); } // make sure archiving of the process instance is finished final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 1, ArchivedProcessInstancesSearchDescriptor.ID, Order.ASC); // search and check result ASC assertTrue("Expected 1 ARCHIVED process instances not found", new WaitUntil(500, 10000) { @Override protected boolean check() throws Exception { return getProcessAPI().searchArchivedProcessInstances(searchOptions.done()).getCount() == 1; } }.waitUntil()); // test comment is archived builder.filter(ArchivedCommentsSearchDescriptor.PROCESS_INSTANCE_ID, processInstance.getId()); archivedCommentsResult = getProcessAPI().searchArchivedComments(builder.done()); assertEquals(1, archivedCommentsResult.getCount()); final List archivedComments = archivedCommentsResult.getResult(); final ArchivedComment archivedComment = archivedComments.get(0); assertEquals(commentContent, archivedComment.getContent()); // clean all data for test disableAndDeleteProcess(processDefinition); } @Test public void searchComments() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(Arrays.asList("step1", "step2"), Arrays.asList(true, true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance pi0 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance pi1 = getProcessAPI().startProcess(processDefinition.getId()); final String commentContent1 = "commentContent1"; final String commentContent2 = "commentContent2"; final String commentContent3 = "commentContent3"; final String commentContent4 = "content4"; final String commentContent5 = "content'5"; getProcessAPI().addProcessCommentOnBehalfOfUser(pi1.getId(), commentContent1, pi1.getStartedBy()); getProcessAPI().addProcessComment(pi1.getId(), commentContent2); getProcessAPI().addProcessComment(pi1.getId(), commentContent3); getProcessAPI().addProcessComment(pi0.getId(), commentContent4); getProcessAPI().addProcessComment(pi0.getId(), commentContent5); final SearchOptionsBuilder builder0 = new SearchOptionsBuilder(0, 5); builder0.filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, pi0.getId()); builder0.sort(SearchCommentsDescriptor.POSTDATE, Order.ASC); final SearchResult searchResult0 = getProcessAPI().searchComments(builder0.done()); final List commentList0 = searchResult0.getResult(); assertEquals(2, commentList0.size()); final SearchOptionsBuilder builder1 = new SearchOptionsBuilder(0, 5); builder1.filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, pi1.getId()); builder1.sort(SearchCommentsDescriptor.POSTDATE, Order.ASC); final SearchResult searchResult1 = getProcessAPI().searchComments(builder1.done()); final List commentList1 = searchResult1.getResult(); assertEquals(3, commentList1.size()); assertEquals(commentContent1, commentList1.get(0).getContent()); assertThat(commentList1.get(0).getUserId()).isEqualTo(pi1.getStartedBy()); assertEquals(commentContent2, commentList1.get(1).getContent()); assertEquals(commentContent3, commentList1.get(2).getContent()); final SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 5); builder2.sort(SearchCommentsDescriptor.POSTDATE, Order.ASC); builder2.searchTerm("comment"); final SearchResult searchResult2 = getProcessAPI().searchComments(builder2.done()); final List commentList2 = searchResult2.getResult(); assertEquals(3, commentList2.size()); // Search with a apostrophe final SearchOptionsBuilder builder4 = new SearchOptionsBuilder(0, 5); builder4.sort(SearchCommentsDescriptor.POSTDATE, Order.ASC); builder4.searchTerm("content'"); final SearchResult searchResult4 = getProcessAPI().searchComments(builder4.done()); final List commentList4 = searchResult4.getResult(); assertEquals(1, commentList4.size()); final SearchOptionsBuilder builder3 = new SearchOptionsBuilder(0, 5); builder3.filter(SearchCommentsDescriptor.USER_NAME, USERNAME); builder3.sort(SearchCommentsDescriptor.POSTDATE, Order.ASC); final SearchResult searchResult3 = getProcessAPI().searchComments(builder3.done()); final List commentList3 = searchResult3.getResult(); assertEquals(5, commentList3.size()); disableAndDeleteProcess(processDefinition); } @Test public void searchCommentsForPartArchivedCases() throws Exception { // create a ProcessDefinition final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("userTask1", ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); // create a ProcessInstance final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "userTask1"); waitForUserTask(processInstance2, "userTask1"); // add an comment to ProcessInstance final String commentContent1 = "commentContent1"; final String commentContent2 = "commentContent2"; getProcessAPI().addProcessComment(processInstance.getId(), commentContent1); getProcessAPI().addProcessComment(processInstance2.getId(), commentContent2); // get a ActivityInstance final List activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 10); // test before archive comment final SearchOptionsBuilder builder3 = new SearchOptionsBuilder(0, 10); SearchResult commentsResult = getProcessAPI().searchComments(builder3.done()); assertEquals(2, commentsResult.getCount()); // Archive a ProcessInstance for (final ActivityInstance activityInstance : activityInstances) { final long activityInstanceId = activityInstance.getId(); getProcessAPI().setActivityStateById(activityInstanceId, 12); } // make sure archiving of the process instance is finished final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 10, ArchivedProcessInstancesSearchDescriptor.ID, Order.ASC); // search and check result ASC assertTrue("Expected 1 ARCHIVED process instances not found", new WaitUntil(500, 10000) { @Override protected boolean check() throws Exception { return getProcessAPI().searchArchivedProcessInstances(searchOptions.done()).getCount() == 1; } }.waitUntil()); // test comment is archived final SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 10); commentsResult = getProcessAPI().searchComments(builder2.done()); assertEquals(1, commentsResult.getCount()); final List comments = commentsResult.getResult(); final Comment comment = comments.get(0); assertEquals(commentContent2, comment.getContent()); // clean all data for test disableAndDeleteProcess(processDefinition); } @Test public void searchCommentsInvolvingUser() throws Exception { final String jackUserName = "jack"; final String johnUserName = "john"; final User jack = createUser(jackUserName, PASSWORD); final User john = createUser(johnUserName, PASSWORD); final SearchOptionsBuilder builderInitJack = new SearchOptionsBuilder(0, 10); final SearchResult resultinitjack = getProcessAPI().searchCommentsInvolvingUser(jack.getId(), builderInitJack.done()); final long jackInitComments = resultinitjack.getCount(); final SearchOptionsBuilder builderInitJohn = new SearchOptionsBuilder(0, 10); final SearchResult resultInitJohn = getProcessAPI().searchCommentsInvolvingUser(john.getId(), builderInitJohn.done()); final long johnInitComments = resultInitJohn.getCount(); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("userTask1", ACTOR_NAME) .addUserTask("userTask2", ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, jack); final ProcessInstance instance1 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance instance2 = getProcessAPI().startProcess(john.getId(), processDefinition.getId()); waitForUserTaskAndAssignIt(instance1, "userTask1", jack); waitForUserTaskAndAssignIt(instance1, "userTask2", jack); logout(); loginOnDefaultTenantWith(jackUserName, PASSWORD); final String commentContent1 = "jack's comment Content1"; final String commentContent2 = "jack's comment Content3"; final String commentContent3 = "jack's comment Content3"; getProcessAPI().addProcessComment(instance1.getId(), commentContent1); getProcessAPI().addProcessComment(instance1.getId(), commentContent2); getProcessAPI().addProcessComment(instance1.getId(), commentContent3); logout(); loginOnDefaultTenantWith(johnUserName, PASSWORD); final String commentContent4 = "john's comment Content3"; getProcessAPI().addProcessComment(instance2.getId(), commentContent4); final SearchOptionsBuilder builder1 = new SearchOptionsBuilder(0, 10); final SearchResult result1 = getProcessAPI().searchCommentsInvolvingUser(jack.getId(), builder1.done()); assertEquals(jackInitComments + 3, result1.getCount()); final List commentList1 = result1.getResult(); assertNotNull(commentList1); assertEquals(jackInitComments + 3, commentList1.size()); final SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 10); final SearchResult result2 = getProcessAPI().searchCommentsInvolvingUser(john.getId(), builder2.done()); assertEquals(johnInitComments + 1, result2.getCount()); final List commentList2 = result2.getResult(); assertEquals(commentContent4, commentList2.get(0).getContent()); disableAndDeleteProcess(processDefinition); deleteUsers(jack, john); } @Test public void searchCommentsManagedBy() throws Exception { // Create two new users John Jack and Steven, and set John managed by Steven, login with John. final String johnUserName = "john"; final String jackUserName = "jack"; final String stevenUserName = "steven"; final User steven = createUser(stevenUserName, PASSWORD); final User jack = createUser(jackUserName, PASSWORD); final User john = createUser(johnUserName, PASSWORD, steven.getId()); final User jim = createUser("jim", PASSWORD, steven.getId()); logout(); loginOnDefaultTenantWith(johnUserName, PASSWORD); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("userTask1", ACTOR_NAME) .addUserTask("userTask2", ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, john); final ProcessInstance pi1 = getProcessAPI().startProcess(steven.getId(), processDefinition.getId()); waitForUserTaskAndAssignIt(pi1, "userTask1", john); waitForUserTaskAndAssignIt(pi1, "userTask2", john); getProcessAPI().addProcessComment(pi1.getId(), "John's comment Content1"); getProcessAPI().addProcessComment(pi1.getId(), "John's comment Content2"); getProcessAPI().addProcessComment(pi1.getId(), "John's comment Content3"); logout(); loginOnDefaultTenantWith(jackUserName, PASSWORD); final ProcessInstance pi2 = getProcessAPI().startProcess(jim.getId(), processDefinition.getId()); final String commentContent4 = "Jack's comment Content4"; getProcessAPI().addProcessComment(pi2.getId(), commentContent4); logout(); loginOnDefaultTenantWith(stevenUserName, PASSWORD); final ProcessInstance pi3 = getProcessAPI().startProcess(steven.getId(), processDefinition.getId()); waitForUserTaskAndAssignIt(pi3, "userTask1", john); final String commentContent5 = "Steven's comment Content5"; getProcessAPI().addProcessComment(pi3.getId(), commentContent5); final ProcessInstance pi4 = getProcessAPI().startProcess(steven.getId(), processDefinition.getId()); final String commentContent6 = "Steven's comment Content6"; getProcessAPI().addProcessComment(pi4.getId(), commentContent6); logout(); loginWithTechnicalUser(); final SearchOptionsBuilder builder3 = new SearchOptionsBuilder(0, 10); final SearchResult searchResult3 = getProcessAPI().searchCommentsManagedBy(jack.getId(), builder3.done()); final List commentList3 = searchResult3.getResult(); assertEquals(0, commentList3.size()); final SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 10); builder2.sort(SearchCommentsDescriptor.POSTED_BY_ID, Order.ASC); final SearchResult searchResult2 = getProcessAPI().searchCommentsManagedBy(steven.getId(), builder2.done()); final List commentList2 = searchResult2.getResult(); assertEquals(5, searchResult2.getCount()); // Order by POSTED_BY_ID uses creation order: assertEquals(Long.valueOf(steven.getId()), commentList2.get(0).getUserId()); assertEquals(Long.valueOf(jack.getId()), commentList2.get(1).getUserId()); assertEquals(Long.valueOf(john.getId()), commentList2.get(2).getUserId()); assertEquals(Long.valueOf(john.getId()), commentList2.get(3).getUserId()); assertEquals(Long.valueOf(john.getId()), commentList2.get(4).getUserId()); assertTrue(commentList2.get(0).getContent().contains("Content5")); disableAndDeleteProcess(processDefinition); deleteUsers(john, jack, jim, steven); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchProcessDefinitionIT.java ================================================ /** * 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.search; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Celine Souchet */ public class SearchProcessDefinitionIT extends TestWithUser { private List enabledProcessDefinitions; private List disabledProcessDefinitions; @Override @Before public void before() throws Exception { super.before(); enabledProcessDefinitions = new ArrayList<>(2); disabledProcessDefinitions = new ArrayList<>(2); } @Override @After public void after() throws Exception { disableAndDeleteProcess(enabledProcessDefinitions); deleteProcess(disabledProcessDefinitions); super.after(); } @Test public void searchRecentlyStartedByProcessDefinitions() throws Exception { final long userId = user.getId(); // create process1 final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps( Arrays.asList("step1", "step2"), Arrays.asList(true, true)); enabledProcessDefinitions.add(deployAndEnableProcessWithActor(designProcessDefinition1, ACTOR_NAME, user)); final ProcessInstance pi1 = getProcessAPI().startProcess(userId, enabledProcessDefinitions.get(0).getId()); waitForUserTask(pi1, "step1"); // create process2 final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process2", PROCESS_VERSION, Arrays.asList("step1", "step2"), Arrays.asList(true, true)); enabledProcessDefinitions.add(deployAndEnableProcessWithActor(designProcessDefinition2, ACTOR_NAME, user)); final ProcessInstance pi2 = getProcessAPI().startProcess(userId, enabledProcessDefinitions.get(1).getId()); waitForUserTask(pi2, "step1"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 5); builder.sort(ProcessDeploymentInfoSearchDescriptor.ID, Order.ASC); final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosStartedBy(userId, builder.done()); assertEquals(2, searchRes.getCount()); final List processDeploymentInfos = searchRes.getResult(); assertEquals("The first process definition must be " + designProcessDefinition1.getName(), enabledProcessDefinitions.get(0).getId(), processDeploymentInfos.get(0).getProcessId()); assertEquals("The second process definition must be " + designProcessDefinition2.getName(), enabledProcessDefinitions.get(1).getId(), processDeploymentInfos.get(1).getProcessId()); // test search in order final SearchOptionsBuilder builder1 = new SearchOptionsBuilder(0, 5); builder1.sort(ProcessDeploymentInfoSearchDescriptor.ID, Order.DESC); final SearchResult searchRes1 = getProcessAPI() .searchProcessDeploymentInfosStartedBy(userId, builder1.done()); assertEquals(2, searchRes1.getCount()); final List processDeploymentInfos1 = searchRes1.getResult(); assertNotNull(processDeploymentInfos1); assertEquals(2, processDeploymentInfos1.size()); assertEquals(enabledProcessDefinitions.get(1).getId(), processDeploymentInfos1.get(0).getProcessId()); assertEquals(enabledProcessDefinitions.get(0).getId(), processDeploymentInfos1.get(1).getProcessId()); // test term final SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 5); builder2.searchTerm("My_Process2"); // use name as term final SearchResult searchRes2 = getProcessAPI() .searchProcessDeploymentInfosStartedBy(userId, builder2.done()); assertEquals(1, searchRes2.getCount()); final List processDeploymentInfos2 = searchRes2.getResult(); assertNotNull(processDeploymentInfos2); assertEquals(1, processDeploymentInfos2.size()); assertEquals(enabledProcessDefinitions.get(1).getId(), processDeploymentInfos2.get(0).getProcessId()); // test filter final SearchOptionsBuilder builder3 = new SearchOptionsBuilder(0, 5); builder3.filter(ProcessDeploymentInfoSearchDescriptor.NAME, "My_Process2"); final SearchResult searchRes3 = getProcessAPI() .searchProcessDeploymentInfosStartedBy(userId, builder3.done()); assertEquals(1, searchRes3.getCount()); final List processDeploymentInfos3 = searchRes3.getResult(); assertNotNull(processDeploymentInfos3); assertEquals(1, processDeploymentInfos3.size()); assertEquals(enabledProcessDefinitions.get(1).getId(), processDeploymentInfos3.get(0).getProcessId()); } @Test public void searchProcessDefinitions() throws Exception { // create 2 process createNbProcessDefinitionWithTwoHumanStepsAndDeployWithActor(2, user); // Get all process definitions, reverse order: SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 5); optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.DEPLOYMENT_DATE, Order.DESC); final SearchResult searchRes0 = getProcessAPI() .searchProcessDeploymentInfos(optsBuilder.done()); assertEquals(2, searchRes0.getCount()); // reverse order: assertEquals(enabledProcessDefinitions.get(0).getId(), searchRes0.getResult().get(1).getProcessId()); assertEquals(enabledProcessDefinitions.get(1).getId(), searchRes0.getResult().get(0).getProcessId()); // partial term search optsBuilder = new SearchOptionsBuilder(0, 5); optsBuilder.searchTerm(PROCESS_NAME); // use process def as term final SearchResult searchRes2 = getProcessAPI() .searchProcessDeploymentInfos(optsBuilder.done()); assertEquals(2, searchRes2.getCount()); // partial term search optsBuilder = new SearchOptionsBuilder(0, 5); optsBuilder.searchTerm("1.01"); // use process def as term final SearchResult searchRes3 = getProcessAPI() .searchProcessDeploymentInfos(optsBuilder.done()); assertEquals(1, searchRes3.getCount()); } @Test public void searchProcessDefinitionsSorted() throws Exception { // create 5 process createNbProcessDefinitionWithTwoHumanStepsAndDeployWithActor(5, user); // Get all process definitions, reverse order: SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 10); optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.VERSION, Order.ASC); SearchResult searchRes0 = getProcessAPI() .searchProcessDeploymentInfos(optsBuilder.done()); assertEquals(5, searchRes0.getCount()); assertThat(searchRes0.getResult()).extracting("version").containsExactly("1.00", "1.01", "1.02", "1.03", "1.04"); optsBuilder = new SearchOptionsBuilder(0, 10); optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.VERSION, Order.DESC); searchRes0 = getProcessAPI().searchProcessDeploymentInfos(optsBuilder.done()); assertEquals(5, searchRes0.getCount()); assertThat(searchRes0.getResult()).extracting("version").containsExactly("1.04", "1.03", "1.02", "1.01", "1.00"); } @Test public void searchProcessDefinitionsFilter() throws Exception { // create 5 enabled process createNbProcessDefinitionWithTwoHumanStepsAndDeployWithActor(5, user); // create 1 disabled process final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("plop", PROCESS_VERSION, Arrays.asList("step1", "step2"), Arrays.asList(true, true)); disabledProcessDefinitions.add(getProcessAPI().deploy(designProcessDefinition)); // Filter on version SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 10); optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.VERSION, Order.ASC); optsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.VERSION, "1.03"); SearchResult searchResult = getProcessAPI() .searchProcessDeploymentInfos(optsBuilder.done()); assertEquals(1, searchResult.getCount()); assertThat(searchResult.getResult()).extracting("version").containsExactly("1.03"); // Filter on activation state optsBuilder = new SearchOptionsBuilder(0, 10); optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.VERSION, Order.ASC); optsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.ACTIVATION_STATE, ActivationState.ENABLED.name()); searchResult = getProcessAPI().searchProcessDeploymentInfos(optsBuilder.done()); assertEquals(5, searchResult.getCount()); assertFalse("Don't have to contain the process definition \"plop\" !!", searchResult.getResult().contains(enabledProcessDefinitions.get(4))); } @Test public void searchProcessDefinitionsWithApostropheOnProcessName() throws Exception { searchProcessDefinitions("process'Name", PROCESS_VERSION); } @Test public void searchProcessDefinitionsWithApostropheOnProcessVersion() throws Exception { searchProcessDefinitions(PROCESS_NAME, "process'VERSION"); } private void searchProcessDefinitions(final String processName, final String processVersion) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(processName, processVersion); processBuilder.addActor(ACTOR_NAME); enabledProcessDefinitions.add(deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, user)); // Get all process definitions, reverse order: final SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 10); optsBuilder.searchTerm("process'"); final SearchResult searchResult = getProcessAPI() .searchProcessDeploymentInfos(optsBuilder.done()); assertEquals(1, searchResult.getCount()); assertEquals(processName, searchResult.getResult().get(0).getName()); } @Test public void searchProcessDefinitionsSearchTermOnVersion() throws Exception { // create 5 process createNbProcessDefinitionWithTwoHumanStepsAndDeployWithActor(5, user); // Get all process definitions, reverse order: final SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 10); optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.VERSION, Order.ASC); optsBuilder.searchTerm("1.03"); final SearchResult searchRes0 = getProcessAPI() .searchProcessDeploymentInfos(optsBuilder.done()); assertEquals(1, searchRes0.getCount()); assertThat(searchRes0.getResult()).extracting("version").containsExactly("1.03"); } private void createNbProcessDefinitionWithTwoHumanStepsAndDeployWithActor(final int nbProcess, final User user) throws Exception { for (int i = 0; i < nbProcess; i++) { String processName = PROCESS_NAME; if (i >= 0 && i < 10) { processName += "0"; } final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(processName + i, PROCESS_VERSION + i, Arrays.asList("step1_" + i, "step2_" + i), Arrays.asList(true, true)); enabledProcessDefinitions.add(deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user)); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchProcessDeploymentInfosCanBeStartedByIT.java ================================================ /** * 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.search; import static org.junit.Assert.assertEquals; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserMembership; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Celine Souchet */ public class SearchProcessDeploymentInfosCanBeStartedByIT extends TestWithTechnicalUser { private List enabledProcessDefinitions; private List disabledProcessDefinitions; private List users = null; private List categories = null; private List groups = null; private List roles = null; private List userMemberships = null; @Override @Before public void before() throws Exception { super.before(); // create users users = new ArrayList(2); users.add(createUser("chicobento", "bpm")); users.add(createUser("cebolinha", "bpm")); users.add(createUser("cascao", "bpm")); users.add(createUser("magali", "bpm")); users.add(createUser("monica", "bpm")); users.add(createUser("dorinha", "bpm")); // create groups groups = new ArrayList(2); groups.add(createGroup("group1")); groups.add(createGroup("group2")); // create roles roles = new ArrayList(2); roles.add(createRole("role1")); roles.add(createRole("role2")); // create user memberships userMemberships = new ArrayList(3); userMemberships.add( getIdentityAPI().addUserMembership(users.get(3).getId(), groups.get(0).getId(), roles.get(0).getId())); userMemberships.add( getIdentityAPI().addUserMembership(users.get(4).getId(), groups.get(0).getId(), roles.get(1).getId())); userMemberships.add( getIdentityAPI().addUserMembership(users.get(5).getId(), groups.get(1).getId(), roles.get(0).getId())); // create processes enabledProcessDefinitions = new ArrayList(4); disabledProcessDefinitions = new ArrayList(1); createProcessesDefForSearchProcessUserCanStart(); categories = new ArrayList(3); categories.add(getProcessAPI().createCategory("category1", "the first known category")); categories.add(getProcessAPI().createCategory("category2", "the second known category")); categories.add(getProcessAPI().createCategory("category3", "the third known category")); getProcessAPI().addProcessDefinitionToCategory(categories.get(0).getId(), enabledProcessDefinitions.get(2).getId()); getProcessAPI().addProcessDefinitionToCategory(categories.get(1).getId(), enabledProcessDefinitions.get(2).getId()); getProcessAPI().addProcessDefinitionToCategory(categories.get(1).getId(), enabledProcessDefinitions.get(1).getId()); getProcessAPI().addProcessDefinitionToCategory(categories.get(2).getId(), enabledProcessDefinitions.get(3).getId()); } @Override @After public void after() throws Exception { disableAndDeleteProcess(enabledProcessDefinitions); deleteProcess(disabledProcessDefinitions); deleteCategories(categories); deleteUserMemberships(userMemberships); deleteUsers(users); deleteGroups(groups); deleteRoles(roles); super.after(); } @Test public void searchProcessDefinitionsUserCanStart() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosCanBeStartedBy(users.get(0).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(0).getName(), searchRes.getResult().get(0).getName()); searchRes = getProcessAPI().searchProcessDeploymentInfosCanBeStartedBy(users.get(1).getId(), searchOptionsBuilder.done()); assertEquals(2, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(1).getName(), searchRes.getResult().get(0).getName()); assertEquals(enabledProcessDefinitions.get(2).getName(), searchRes.getResult().get(1).getName()); // user associated to a process without actor initiator searchRes = getProcessAPI().searchProcessDeploymentInfosCanBeStartedBy(users.get(2).getId(), searchOptionsBuilder.done()); assertEquals(0, searchRes.getCount()); } @Test public void searchProcessDefinitionsUserCanStartFromGroup() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosCanBeStartedBy(users.get(4).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(4).getName(), searchRes.getResult().get(0).getName()); } @Test public void searchProcessDefinitionsUserCanStartFromRole() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosCanBeStartedBy(users.get(5).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(5).getName(), searchRes.getResult().get(0).getName()); } @Test public void searchProcessDefinitionsUserCanStartFromRoleAndGroup() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosCanBeStartedBy(users.get(3).getId(), searchOptionsBuilder.done()); assertEquals(3, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(4).getName(), searchRes.getResult().get(0).getName()); // from group assertEquals(enabledProcessDefinitions.get(5).getName(), searchRes.getResult().get(1).getName()); // from role assertEquals(enabledProcessDefinitions.get(6).getName(), searchRes.getResult().get(2).getName()); // from role and group } @Test public void searchProcessDefinitionsUserCanStartWithSearchTerm() throws Exception { // test term final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.searchTerm("My_Process2"); // use name as term final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosCanBeStartedBy(users.get(1).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(1).getId(), searchRes.getResult().get(0).getProcessId()); } @Test public void searchProcessDefinitionsUserCanStartWithFilter() throws Exception { // test filter on process name SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.NAME, "My_Process2"); SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosCanBeStartedBy(users.get(1).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(1).getId(), searchRes.getResult().get(0).getProcessId()); // test filter category searchOptionsBuilder = new SearchOptionsBuilder(0, 5).sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.CATEGORY_ID, categories.get(0).getId()); searchRes = getProcessAPI().searchProcessDeploymentInfosCanBeStartedBy(users.get(1).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(2).getId(), searchRes.getResult().get(0).getProcessId()); } private void createProcessesDefForSearchProcessUserCanStart() throws Exception { final String actor1 = ACTOR_NAME; final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process1", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor1, true); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, actor1, users.get(0)); enabledProcessDefinitions.add(processDefinition1); // create process2 final String actor2 = "Actor2"; final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process2", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actor2, users.get(1)); enabledProcessDefinitions.add(processDefinition2); final DesignProcessDefinition designProcessDefinition3 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process3", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, actor2, users.get(1)); enabledProcessDefinitions.add(processDefinition3); // process not enabled final DesignProcessDefinition designProcessDefinition4 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process4", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition4 = getProcessAPI().deploy( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition4) .done()); getProcessAPI().addUserToActor(actor2, processDefinition4, users.get(1).getId()); disabledProcessDefinitions.add(processDefinition4); // process without actor initiator final DesignProcessDefinition designProcessDefinition5 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process5", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, false); final ProcessDefinition processDefinition5 = deployAndEnableProcessWithActor(designProcessDefinition5, actor2, users.get(2)); enabledProcessDefinitions.add(processDefinition5); // actor initiator is a group final DesignProcessDefinition designProcessDefinition6 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process6", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition6 = deployAndEnableProcessWithActor(designProcessDefinition6, actor2, groups.get(0)); enabledProcessDefinitions.add(processDefinition6); // actor initiator is a role final DesignProcessDefinition designProcessDefinition7 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process7", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition7 = deployAndEnableProcessWithActor(designProcessDefinition7, actor2, roles.get(0)); enabledProcessDefinitions.add(processDefinition7); // actor initiator is a membership final DesignProcessDefinition designProcessDefinition8 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process8", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition8 = deployAndEnableProcessWithActor(designProcessDefinition8, actor2, roles.get(0), groups.get(0)); enabledProcessDefinitions.add(processDefinition8); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchProcessDeploymentInfosCanBeStartedByUsersManagedByIT.java ================================================ /** * 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.search; import static org.junit.Assert.assertEquals; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserMembership; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Celine Souchet */ public class SearchProcessDeploymentInfosCanBeStartedByUsersManagedByIT extends TestWithTechnicalUser { private List enabledProcessDefinitions; private List disabledProcessDefinitions; private List users = null; private List groups = null; private List roles = null; private List userMemberships = null; @Override @Before public void before() throws Exception { super.before(); // create users users = new ArrayList(10); users.add(createUser("magali", "bpm")); users.add(createUser("monica", "bpm")); users.add(createUser("manager", "bpm")); users.add(createUser("chicobento", users.get(0).getId())); users.add(createUser("chicobento2", users.get(2).getId())); users.add(createUser("cebolinha", users.get(0).getId())); users.add(createUser("cascao", users.get(1).getId())); users.add(createUser("dorinha", users.get(0).getId())); users.add(createUser("dorinha2", users.get(0).getId())); users.add(createUser("dorinha3", users.get(1).getId())); // create groups groups = new ArrayList(2); groups.add(createGroup("groups.get(0)")); groups.add(createGroup("groups.get(1)")); // create roles roles = new ArrayList(2); roles.add(createRole("roles.get(0)")); roles.add(createRole("roles.get(1)")); // create user memberships userMemberships = new ArrayList(4); userMemberships.add( getIdentityAPI().addUserMembership(users.get(7).getId(), groups.get(0).getId(), roles.get(0).getId())); userMemberships.add( getIdentityAPI().addUserMembership(users.get(8).getId(), groups.get(0).getId(), roles.get(1).getId())); userMemberships.add( getIdentityAPI().addUserMembership(users.get(9).getId(), groups.get(1).getId(), roles.get(0).getId())); userMemberships.add( getIdentityAPI().addUserMembership(users.get(4).getId(), groups.get(0).getId(), roles.get(0).getId())); // create processes enabledProcessDefinitions = new ArrayList(4); disabledProcessDefinitions = new ArrayList(1); createProcessesDefForSearchProcessUserCanStart(); } @Override @After public void after() throws Exception { disableAndDeleteProcess(enabledProcessDefinitions); deleteProcess(disabledProcessDefinitions); deleteUserMemberships(userMemberships); deleteUsers(users); deleteGroups(groups); deleteRoles(roles); super.after(); } @Test public void searchProcessDefinitionsUsersManagedByCanStart() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(users.get(8).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); List result = searchRes.getResult(); assertEquals(enabledProcessDefinitions.get(4).getName(), result.get(0).getName()); searchRes = getProcessAPI().searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(users.get(0).getId(), searchOptionsBuilder.done()); assertEquals(4, searchRes.getCount()); result = searchRes.getResult(); assertEquals(enabledProcessDefinitions.get(0).getName(), result.get(0).getName()); assertEquals(enabledProcessDefinitions.get(4).getName(), result.get(1).getName()); assertEquals(enabledProcessDefinitions.get(5).getName(), result.get(2).getName()); assertEquals(enabledProcessDefinitions.get(6).getName(), result.get(3).getName()); // user associated to a process without actor initiator searchRes = getProcessAPI().searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(users.get(6).getId(), searchOptionsBuilder.done()); assertEquals(0, searchRes.getCount()); } @Test public void searchProcessDefinitionsUsersManagedByCanStartFromRoleAndGroup() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(users.get(2).getId(), searchOptionsBuilder.done()); assertEquals(3, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(4).getName(), searchRes.getResult().get(0).getName()); // from group assertEquals(enabledProcessDefinitions.get(5).getName(), searchRes.getResult().get(1).getName()); // from role assertEquals(enabledProcessDefinitions.get(6).getName(), searchRes.getResult().get(2).getName()); // from role and group } @Test public void searchProcessDefinitionsUsersManagedByCanStartWithSearchTerm() throws Exception { // test term final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.searchTerm("My_Process7"); // use name as term final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(users.get(4).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(5).getId(), searchRes.getResult().get(0).getProcessId()); } @Test public void searchProcessDefinitionsUsersManagedByCanStartWithFilter() throws Exception { // test filter on process name final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.NAME, "My_Process7"); final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(users.get(4).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(5).getId(), searchRes.getResult().get(0).getProcessId()); } private void createProcessesDefForSearchProcessUserCanStart() throws Exception { final String actor1 = ACTOR_NAME; final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process1", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor1, true); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, actor1, users.get(3)); enabledProcessDefinitions.add(processDefinition1); // create process2 final String actor2 = "Actor2"; final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process2", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actor2, users.get(1)); enabledProcessDefinitions.add(processDefinition2); final DesignProcessDefinition designProcessDefinition3 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process3", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, actor2, users.get(1)); enabledProcessDefinitions.add(processDefinition3); // process not enabled final DesignProcessDefinition designProcessDefinition4 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process4", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition4 = getProcessAPI().deploy( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition4) .done()); getProcessAPI().addUserToActor(actor2, processDefinition4, users.get(1).getId()); disabledProcessDefinitions.add(processDefinition4); // process without actor initiator final DesignProcessDefinition designProcessDefinition5 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process5", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, false); final ProcessDefinition processDefinition5 = deployAndEnableProcessWithActor(designProcessDefinition5, actor2, users.get(2)); enabledProcessDefinitions.add(processDefinition5); // actor initiator is a group final DesignProcessDefinition designProcessDefinition6 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process6", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition6 = deployAndEnableProcessWithActor(designProcessDefinition6, actor2, groups.get(0)); enabledProcessDefinitions.add(processDefinition6); // actor initiator is a role final DesignProcessDefinition designProcessDefinition7 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process7", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition7 = deployAndEnableProcessWithActor(designProcessDefinition7, actor2, roles.get(0)); enabledProcessDefinitions.add(processDefinition7); // actor initiator is a membership final DesignProcessDefinition designProcessDefinition8 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process8", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition8 = deployAndEnableProcessWithActor(designProcessDefinition8, actor2, roles.get(0), groups.get(0)); enabledProcessDefinitions.add(processDefinition8); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksForIT.java ================================================ /** * 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.search; import static org.junit.Assert.assertEquals; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserMembership; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Celine Souchet */ public class SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksForIT extends TestWithTechnicalUser { private List enabledProcessDefinitions; private List users = null; private List groups = null; private List roles = null; private List userMemberships = null; @Override @After public void after() throws Exception { disableAndDeleteProcess(enabledProcessDefinitions); deleteUserMemberships(userMemberships); deleteUsers(users); deleteGroups(groups); deleteRoles(roles); super.after(); } @Override @Before public void before() throws Exception { super.before(); // create users users = new ArrayList<>(2); users.add(createUser("chicobento", "bpm")); users.add(createUser("cebolinha", "bpm")); users.add(createUser("cascao", "bpm")); // create groups groups = new ArrayList<>(2); groups.add(createGroup("group1")); groups.add(createGroup("group2")); // create roles roles = new ArrayList<>(2); roles.add(createRole("role1")); roles.add(createRole("role2")); // create user memberships userMemberships = new ArrayList<>(3); userMemberships.add( getIdentityAPI().addUserMembership(users.get(2).getId(), groups.get(0).getId(), roles.get(0).getId())); userMemberships.add( getIdentityAPI().addUserMembership(users.get(0).getId(), groups.get(0).getId(), roles.get(1).getId())); userMemberships.add( getIdentityAPI().addUserMembership(users.get(1).getId(), groups.get(1).getId(), roles.get(0).getId())); // create processes enabledProcessDefinitions = new ArrayList<>(4); createProcessesDefinitions(); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor( users.get(0).getId(), searchOptionsBuilder.done()); assertEquals(2, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(0).getName(), searchRes.getResult().get(0).getName()); searchRes = getProcessAPI().searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(users.get(1).getId(), searchOptionsBuilder.done()); assertEquals(3, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(1).getName(), searchRes.getResult().get(0).getName()); assertEquals(enabledProcessDefinitions.get(2).getName(), searchRes.getResult().get(1).getName()); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksForWithFilter() throws Exception { // test filter on process name final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.NAME, "My_Process2"); final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor( users.get(1).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(1).getId(), searchRes.getResult().get(0).getProcessId()); } private void createProcessesDefinitions() throws Exception { final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process1", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), ACTOR_NAME, true); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, ACTOR_NAME, users.get(0)); enabledProcessDefinitions.add(processDefinition1); startProcessAndWaitForTask(processDefinition1.getId(), "step1"); // create process2 final String actor2 = "Actor2"; final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process2", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actor2, users.get(1)); enabledProcessDefinitions.add(processDefinition2); startProcessAndWaitForTask(processDefinition2.getId(), "step1"); final DesignProcessDefinition designProcessDefinition3 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process3", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, actor2, users.get(1)); enabledProcessDefinitions.add(processDefinition3); startProcessAndWaitForTask(processDefinition3.getId(), "step1"); // actor initiator is a group final DesignProcessDefinition designProcessDefinition6 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process6", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition6 = deployAndEnableProcessWithActor(designProcessDefinition6, actor2, groups.get(0)); enabledProcessDefinitions.add(processDefinition6); startProcessAndWaitForTask(processDefinition6.getId(), "step1"); // actor initiator is a role final DesignProcessDefinition designProcessDefinition7 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process7", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition7 = deployAndEnableProcessWithActor(designProcessDefinition7, actor2, roles.get(0)); enabledProcessDefinitions.add(processDefinition7); startProcessAndWaitForTask(processDefinition7.getId(), "step1"); // actor initiator is a membership final DesignProcessDefinition designProcessDefinition8 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process8", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition8 = deployAndEnableProcessWithActor(designProcessDefinition8, actor2, roles.get(0), groups.get(0)); enabledProcessDefinitions.add(processDefinition8); startProcessAndWaitForTask(processDefinition8.getId(), "step1"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksIT.java ================================================ /** * 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.search; import static org.junit.Assert.assertEquals; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Celine Souchet */ public class SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksIT extends TestWithTechnicalUser { private List enabledProcessDefinitions; private List users = null; @Override @After public void after() throws Exception { disableAndDeleteProcess(enabledProcessDefinitions); deleteUsers(users); super.after(); } @Override @Before public void before() throws Exception { super.before(); // create users users = new ArrayList<>(2); users.add(createUser("chicobento", "bpm")); users.add(createUser("cebolinha", "bpm")); // create processes enabledProcessDefinitions = new ArrayList<>(4); createProcessesDefinitions(); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(searchOptionsBuilder.done()); assertEquals(2, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(0).getName(), searchRes.getResult().get(0).getName()); searchRes = getProcessAPI().searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy( users.get(1).getId(), searchOptionsBuilder.done()); assertEquals(0, searchRes.getCount()); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksWithFilter() throws Exception { // test filter on process name final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.NAME, "My_Process1"); final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks( searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(0).getId(), searchRes.getResult().get(0).getProcessId()); } private void createProcessesDefinitions() throws Exception { final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process1", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), ACTOR_NAME, true); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, ACTOR_NAME, users.get(0)); enabledProcessDefinitions.add(processDefinition1); startProcessAndWaitForTask(processDefinition1.getId(), "step1"); // create process2 final String actor2 = "Actor2"; final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process2", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actor2, users.get(1)); enabledProcessDefinitions.add(processDefinition2); startProcessAndWaitForTask(processDefinition2.getId(), "step1"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedByIT.java ================================================ /** * 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.search; import static org.junit.Assert.assertEquals; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Celine Souchet */ public class SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedByIT extends TestWithTechnicalUser { private List enabledProcessDefinitions; private List users = null; private ProcessSupervisor supervisor; @Override @After public void after() throws Exception { deleteSupervisor(supervisor); disableAndDeleteProcess(enabledProcessDefinitions); deleteUsers(users); super.after(); } @Override @Before public void before() throws Exception { super.before(); // create users users = new ArrayList<>(2); users.add(createUser("chicobento", "bpm")); users.add(createUser("cebolinha", "bpm")); // create processes enabledProcessDefinitions = new ArrayList<>(4); createProcessesDefinitions(); supervisor = getProcessAPI().createProcessSupervisorForUser(enabledProcessDefinitions.get(0).getId(), users.get(0).getId()); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy( users.get(0).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(0).getName(), searchRes.getResult().get(0).getName()); searchRes = getProcessAPI().searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy( users.get(1).getId(), searchOptionsBuilder.done()); assertEquals(0, searchRes.getCount()); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedByWithFilter() throws Exception { // test filter on process name final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.NAME, "My_Process1"); final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy( users.get(0).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(0).getId(), searchRes.getResult().get(0).getProcessId()); } private void createProcessesDefinitions() throws Exception { final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process1", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), ACTOR_NAME, true); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, ACTOR_NAME, users.get(0)); enabledProcessDefinitions.add(processDefinition1); startProcessAndWaitForTask(processDefinition1.getId(), "step1"); // create process2 final String actor2 = "Actor2"; final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process2", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actor2, users.get(1)); enabledProcessDefinitions.add(processDefinition2); startProcessAndWaitForTask(processDefinition2.getId(), "step1"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchProcessInstanceIT.java ================================================ /** * 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.search; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.test.BuildTestUtil.buildProcessDefinitionWithHumanAndAutomaticSteps; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.util.Collections; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.Index; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessInstanceUpdater; import org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder; import org.bonitasoft.engine.connectors.TestConnectorThatThrowException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserMembership; import org.bonitasoft.engine.identity.UserSearchDescriptor; import org.bonitasoft.engine.identity.UserUpdater; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.test.TestStates; import org.junit.Assert; import org.junit.Test; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class SearchProcessInstanceIT extends TestWithUser { @Test public void searchOpenProcessInstances() throws Exception { final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( "My_Process", "1.0", asList("step1", "step2"), asList(true, true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance instance1 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance instance2 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance instance3 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance instance4 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance instance5 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(instance1, "step1"); waitForUserTask(instance2, "step1"); waitForUserTask(instance3, "step1"); waitForUserTask(instance4, "step1"); waitForUserTask(instance5, "step1"); // search and check result ASC final SearchOptionsBuilder searchOptions1 = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 2, ProcessInstanceSearchDescriptor.ID, Order.ASC); SearchResult result = getProcessAPI().searchOpenProcessInstances(searchOptions1.done()); assertNotNull(result); assertEquals(5, result.getCount()); final List processInstanceList1 = result.getResult(); assertNotNull(processInstanceList1); assertEquals(2, processInstanceList1.size()); assertEquals(instance1.getId(), processInstanceList1.get(0).getId()); assertEquals(instance2.getId(), processInstanceList1.get(1).getId()); final SearchOptionsBuilder searchOptions2 = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 4, 2, ProcessInstanceSearchDescriptor.ID, Order.ASC); result = getProcessAPI().searchOpenProcessInstances(searchOptions2.done()); assertNotNull(result); assertEquals(5, result.getCount()); final List processInstanceList2 = result.getResult(); assertNotNull(processInstanceList2); assertEquals(1, processInstanceList2.size()); assertEquals(instance5.getId(), processInstanceList2.get(0).getId()); final SearchOptionsBuilder searchOptions3 = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 3, ProcessInstanceSearchDescriptor.ID, Order.DESC); // search and check result DESC result = getProcessAPI().searchOpenProcessInstances(searchOptions3.done()); assertNotNull(result); assertEquals(5, result.getCount()); final List processInstanceList3 = result.getResult(); assertNotNull(processInstanceList3); assertEquals(3, processInstanceList3.size()); assertEquals(instance5.getId(), processInstanceList3.get(0).getId()); assertEquals(instance4.getId(), processInstanceList3.get(1).getId()); assertEquals(instance3.getId(), processInstanceList3.get(2).getId()); final SearchOptionsBuilder searchOptions4 = BuildTestUtil.buildSearchOptions(processDefinition.getId() + 1, 0, 3, ProcessInstanceSearchDescriptor.ID, Order.DESC); result = getProcessAPI().searchOpenProcessInstances(searchOptions4.done()); assertNotNull(result); assertEquals(0, result.getCount()); disableAndDeleteProcess(processDefinition); } @Test public void searchFailedProcessInstances() throws Exception { // Build a process with a failed connector on enter final ProcessDefinition processDefinitionWithFailedConnector = deployAndEnableProcessWithConnector(BuildTestUtil .buildProcessDefinitionWithFailedConnector("Process with failed connector in enter"), "TestConnectorThatThrowException.impl", TestConnectorThatThrowException.class, "TestConnectorThatThrowException.jar"); // Build a process with a failed task final ProcessDefinition processDefinitionWithFailedTask = deployAndEnableProcessWithConnector(BuildTestUtil .buildProcessDefinitionWithAutomaticTaskAndFailedConnector("Process with failed task"), "TestConnectorThatThrowException.impl", TestConnectorThatThrowException.class, "TestConnectorThatThrowException.jar"); final ProcessInstance instance1 = getProcessAPI().startProcess(processDefinitionWithFailedConnector.getId()); waitForProcessToBeInState(instance1.getId(), ProcessInstanceState.ERROR); final ProcessInstance instance2 = getProcessAPI().startProcess(processDefinitionWithFailedTask.getId()); waitForFlowNodeInFailedState(instance2); final ProcessInstance instance3 = getProcessAPI().startProcess(processDefinitionWithFailedConnector.getId()); waitForProcessToBeInState(instance3.getId(), ProcessInstanceState.ERROR); // search and check result ASC final SearchOptionsBuilder searchOptions1 = BuildTestUtil.buildSearchOptions(0, 2, ProcessInstanceSearchDescriptor.START_DATE, Order.ASC); SearchResult result = getProcessAPI().searchFailedProcessInstances(searchOptions1.done()); assertNotNull(result); assertEquals(3, result.getCount()); final List processInstanceList1 = result.getResult(); assertNotNull(processInstanceList1); assertEquals(2, processInstanceList1.size()); assertEquals(instance1.getId(), processInstanceList1.get(0).getId()); assertEquals(instance2.getId(), processInstanceList1.get(1).getId()); // search and check result DESC final SearchOptionsBuilder searchOptions2 = BuildTestUtil.buildSearchOptions(0, 3, ProcessInstanceSearchDescriptor.START_DATE, Order.DESC); result = getProcessAPI().searchFailedProcessInstances(searchOptions2.done()); assertNotNull(result); assertEquals(3, result.getCount()); final List processInstanceList2 = result.getResult(); assertEquals(3, processInstanceList2.size()); assertEquals(instance3.getId(), processInstanceList2.get(0).getId()); assertEquals(instance2.getId(), processInstanceList2.get(1).getId()); assertEquals(instance1.getId(), processInstanceList2.get(2).getId()); // Search only process instance with state ERROR final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 3); builder.filter(ProcessInstanceSearchDescriptor.STATE_NAME, ProcessInstanceState.ERROR); builder.sort(ProcessInstanceSearchDescriptor.START_DATE, Order.ASC); result = getProcessAPI().searchFailedProcessInstances(builder.done()); assertNotNull(result); assertEquals(2, result.getCount()); final List processInstances = result.getResult(); assertNotNull(processInstances); assertEquals(2, processInstances.size()); assertEquals(instance1.getId(), processInstances.get(0).getId()); assertEquals(instance3.getId(), processInstances.get(1).getId()); disableAndDeleteProcess(processDefinitionWithFailedConnector, processDefinitionWithFailedTask); } /* * Start process not with jack * execute a task with jack * check archived process instances worked on are 0 * finish process * check there is one archived process instance worked on */ @Test public void searchArchivedProcessInstanceWorkedOnWithUserPerformedTask() throws Exception { // create process final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( asList("step1", "step2"), asList(true, true)); // assign pending task to jack final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step1", user); final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5, ProcessInstanceSearchDescriptor.ID, Order.ASC); // executed but not archived SearchResult result = getProcessAPI() .searchArchivedProcessInstancesInvolvingUser(user.getId(), searchOptions.done()); assertNotNull(result); assertEquals(0, result.getCount()); waitForUserTaskAndExecuteIt(processInstance, "step2", user); // process finished: no more in worked on waitForProcessToFinish(processInstance); result = getProcessAPI().searchArchivedProcessInstancesInvolvingUser(user.getId(), searchOptions.done()); assertNotNull(result); assertEquals(1, result.getCount()); final ArchivedProcessInstance processInstance2 = result.getResult().get(0); assertEquals(processInstance.getId(), processInstance2.getSourceObjectId()); disableAndDeleteProcess(processDefinition); } /* * Start process with jack * execute a task with john * check archived process instances worked on are 0 * finish process * check there is one archived process instance worked on */ @Test public void searchArchivedProcessInstanceWorkedOnWithUserStartedProcess() throws Exception { // create user final User jack = createUser("jack", PASSWORD); final User john = createUser("john", PASSWORD); logout(); loginOnDefaultTenantWith("john", PASSWORD); // create process final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( asList("step1"), asList(true)); // assign pending task to jack final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, jack); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask("step1"); final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5, ProcessInstanceSearchDescriptor.ID, Order.ASC); // the process is not started by jack but not finished: not in "workedOn" SearchResult result = getProcessAPI() .searchArchivedProcessInstancesInvolvingUser(john.getId(), searchOptions.done()); assertNotNull(result); assertEquals(0, result.getCount()); // assign assignAndExecuteStep(step1Id, jack); // process finished: in worked on waitForProcessToFinish(processInstance); result = getProcessAPI().searchArchivedProcessInstancesInvolvingUser(john.getId(), searchOptions.done()); assertNotNull(result); assertEquals(1, result.getCount()); disableAndDeleteProcess(processDefinition); deleteUsers(john, jack); } /* * Start process not with jack * assign task to jack * check worked on = 0 * execute task with jack * check worked on = 1 * finish process * check worked on = 0 */ @Test public void searchProcessInstanceWorkedOnWithUserPerformedTask() throws Exception { // create user final String username = "jack"; final User jack = createUser(username, PASSWORD); final User john = createUser("john", PASSWORD); // create process final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( asList("step1", "step2"), asList(true, true)); // assign pending task to jack final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, jack); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); logout(); loginOnDefaultTenantWith(username, PASSWORD); final long step1Id = waitForUserTask(processInstance, "step1"); final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5, ProcessInstanceSearchDescriptor.ID, Order.ASC); // the process is not started by jack and jack has not performed tasks: not in "workedOn" SearchResult result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(jack.getId(), searchOptions.done()); assertNotNull(result); assertEquals(0, result.getCount()); // assign getProcessAPI().assignUserTask(step1Id, jack.getId()); // after assigned: still not worked on result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(jack.getId(), searchOptions.done()); assertNotNull(result); assertEquals(0, result.getCount()); getProcessAPI().executeFlowNode(step1Id); final long step2Id = waitForUserTask(processInstance, "step2"); // one task was performed: the process is in "WorkedOn" result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(jack.getId(), searchOptions.done()); assertNotNull(result); assertEquals(1, result.getCount()); final ProcessInstance processInstance2 = result.getResult().get(0); assertEquals(processInstance.getId(), processInstance2.getId()); result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(john.getId(), searchOptions.done()); assertNotNull(result); assertEquals(0, result.getCount()); assignAndExecuteStep(step2Id, jack); // process finished: no more in worked on waitForProcessToFinish(processInstance); result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(jack.getId(), searchOptions.done()); assertNotNull(result); assertEquals(0, result.getCount()); disableAndDeleteProcess(processDefinition); deleteUsers(john, jack); } @Test public void searchProcessInstanceWorkedOnWithUserPerformedTaskOnMultipleInstances() throws Exception { // create user final String username = "jack"; final User jack = createUser(username, PASSWORD); final User john = createUser("john", PASSWORD); // create process final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( asList("step1", "step2"), asList(true, true)); // assign pending task to jack final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, jack); final ProcessInstance p1 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance p2 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance p3 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance p4 = getProcessAPI().startProcess(processDefinition.getId()); logout(); loginOnDefaultTenantWith(username, PASSWORD); waitForUserTaskAndExecuteIt(p1, "step1", jack); waitForUserTaskAndExecuteIt(p2, "step1", jack); waitForUserTaskAndExecuteIt(p3, "step1", jack); logout(); loginOnDefaultTenantWith("john", PASSWORD); waitForUserTaskAndExecuteIt(p4, "step1", jack); waitForUserTask(p1, "step2"); waitForUserTask(p2, "step2"); waitForUserTask(p3, "step2"); waitForUserTask(p4, "step2"); final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5, ProcessInstanceSearchDescriptor.ID, Order.ASC); SearchResult result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(jack.getId(), searchOptions.done()); assertNotNull(result); assertEquals(3, result.getCount()); assertEquals(3, result.getResult().size()); result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(john.getId(), searchOptions.done()); assertNotNull(result); assertEquals(1, result.getCount()); assertEquals(1, result.getResult().size()); disableAndDeleteProcess(processDefinition); deleteUsers(john, jack); } @Test public void searchProcessInstanceWorkedOnWithUserStartedItOnMultipleInstances() throws Exception { // create user final String username = "jack"; final User jack = createUser(username, PASSWORD); final User john = createUser("john", PASSWORD); // create process final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( asList("step1", "step2"), asList(true, true)); // assign pending task to jack final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, jack); logout(); loginOnDefaultTenantWith(username, PASSWORD); final ProcessInstance p1 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance p2 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance p3 = getProcessAPI().startProcess(processDefinition.getId()); logout(); loginOnDefaultTenantWith("john", PASSWORD); final ProcessInstance p4 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(p1, "step1"); waitForUserTask(p2, "step1"); waitForUserTask(p3, "step1"); waitForUserTask(p4, "step1"); final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5, ProcessInstanceSearchDescriptor.ID, Order.ASC); SearchResult result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(jack.getId(), searchOptions.done()); assertNotNull(result); assertEquals(3, result.getCount()); assertEquals(3, result.getResult().size()); result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(john.getId(), searchOptions.done()); assertNotNull(result); assertEquals(1, result.getCount()); assertEquals(1, result.getResult().size()); disableAndDeleteProcess(processDefinition); deleteUsers(john, jack); } /* * Start process with jack * assign/execute task with john * check worked on = 1 for jack * finish process * check worked on = 0 */ @Test public void searchProcessInstanceWorkedOnWithUserStartedProcess() throws Exception { // create user final User jack = createUser("jack", PASSWORD); // create process final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( asList("step1"), asList(true)); // assign pending task to jack final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, jack); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask("step1"); final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5, ProcessInstanceSearchDescriptor.ID, Order.ASC); // the process is started by jack and jack has not performed tasks: In "workedOn" SearchResult result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(user.getId(), searchOptions.done()); assertNotNull(result); assertEquals(1, result.getCount()); assignAndExecuteStep(step1Id, jack); // process finished: no more in worked on waitForProcessToFinish(processInstance); result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(user.getId(), searchOptions.done()); assertNotNull(result); assertEquals(0, result.getCount()); deleteUser(jack); disableAndDeleteProcess(processDefinition); } @Test public void searchOpenProcessInstancesInvolvingUsersManagedByWithUserPerformedTask() throws Exception { // create user final User paul = createUser("paul", "bpm"); final User jack = createUser("jack", paul.getId()); final User john = createUser("john", "bpm"); // create process final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( asList("step1", "step2"), asList(true, true)); // assign pending task to jack final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, jack); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); logout(); loginOnDefaultTenantWith("jack", "bpm"); final long step1Id = waitForUserTask("step1"); final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5, ProcessInstanceSearchDescriptor.ID, Order.ASC); // the process is not started by jack and jack has not performed tasks: not in "workedOn" SearchResult result = getProcessAPI() .searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done()); assertNotNull(result); assertEquals(0, result.getCount()); // assign getProcessAPI().assignUserTask(step1Id, jack.getId()); // after assigned: still not worked on result = getProcessAPI().searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done()); assertNotNull(result); assertEquals(0, result.getCount()); getProcessAPI().executeFlowNode(step1Id); final long step2Id = waitForUserTask("step2"); // one task was performed: the process is in "WorkedOn" result = getProcessAPI().searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done()); assertNotNull(result); assertEquals(1, result.getCount()); final ProcessInstance processInstance2 = result.getResult().get(0); assertEquals(processInstance.getId(), processInstance2.getId()); assignAndExecuteStep(step2Id, jack); // process finished: no more in worked on waitForProcessToFinish(processInstance); result = getProcessAPI().searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done()); assertNotNull(result); assertEquals(0, result.getCount()); disableAndDeleteProcess(processDefinition); deleteUsers(john, jack, paul); } @Test public void searchOpenProcessInstancesInvolvingUsersManagedByWithUserPerformedTaskOnMultipleInstances() throws Exception { // create user final User paul = createUser("paul", "bpm"); final User jack = createUser("jack", paul.getId()); final User john = createUser("john", paul.getId()); // create process final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( asList("step1", "step2"), asList(true, true)); // assign pending task to jack final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, jack); final ProcessInstance p1 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance p2 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance p3 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance p4 = getProcessAPI().startProcess(processDefinition.getId()); logout(); loginOnDefaultTenantWith("jack", "bpm"); waitForUserTaskAndExecuteIt(p1, "step1", jack); waitForUserTaskAndExecuteIt(p2, "step1", jack); waitForUserTaskAndExecuteIt(p3, "step1", jack); logout(); loginOnDefaultTenantWith("john", "bpm"); waitForUserTaskAndExecuteIt(p4, "step1", jack); waitForUserTask(p1, "step2"); waitForUserTask(p2, "step2"); waitForUserTask(p3, "step2"); waitForUserTask(p4, "step2"); final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5, ProcessInstanceSearchDescriptor.ID, Order.ASC); final SearchResult result = getProcessAPI() .searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done()); assertNotNull(result); assertEquals(4, result.getCount()); assertEquals(4, result.getResult().size()); disableAndDeleteProcess(processDefinition); deleteUsers(john, jack, paul); } @Test public void searchOpenProcessInstancesInvolvingUsersManagedByWithUserStartedItOnMultipleInstances() throws Exception { // create user final User paul = createUser("paul", "bpm"); final User jack = createUser("jack", paul.getId()); final User john = createUser("john", paul.getId()); final User pierre = createUser("pierre", "bpm"); // create process final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( asList("step1", "step2"), asList(true, true)); // assign pending task to jack final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, jack); logout(); loginOnDefaultTenantWith("jack", "bpm"); final ProcessInstance p1 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance p2 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance p3 = getProcessAPI().startProcess(processDefinition.getId()); logout(); loginOnDefaultTenantWith("john", "bpm"); final ProcessInstance p4 = getProcessAPI().startProcess(processDefinition.getId()); logout(); loginOnDefaultTenantWith("pierre", "bpm"); final ProcessInstance p5 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(p1, "step1"); waitForUserTask(p2, "step1"); waitForUserTask(p3, "step1"); waitForUserTask(p4, "step1"); waitForUserTask(p5, "step1"); final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5, ProcessInstanceSearchDescriptor.ID, Order.ASC); final SearchResult result = getProcessAPI() .searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done()); assertNotNull(result); assertEquals(4, result.getCount()); assertEquals(4, result.getResult().size()); disableAndDeleteProcess(processDefinition); deleteUsers(john, jack, paul, pierre); } @Test public void searchOpenProcessInstancesInvolvingUsersManagedByWithUserStartedProcess() throws Exception { // create user final User paul = createUser("paul", "bpm"); final User jack = createUser("jack", paul.getId()); final User john = createUser("john", paul.getId()); logout(); loginOnDefaultTenantWith("john", "bpm"); // create process final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( asList("step1"), asList(true)); // assign pending task to jack final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, jack); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask("step1"); final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 5, ProcessInstanceSearchDescriptor.ID, Order.ASC); // the process is started by jack and jack has not performed tasks: In "workedOn" SearchResult result = getProcessAPI() .searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done()); assertNotNull(result); assertEquals(1, result.getCount()); assignAndExecuteStep(step1Id, jack); // process finished: no more in worked on waitForProcessToFinish(processInstance); result = getProcessAPI().searchOpenProcessInstancesInvolvingUsersManagedBy(paul.getId(), searchOptions.done()); assertNotNull(result); assertEquals(0, result.getCount()); disableAndDeleteProcess(processDefinition); deleteUsers(john, jack, paul); } @Test public void searchOpenProcessInstancesStartedBy() throws Exception { // create process final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( "My_Process", "1.0", asList("step1", "step2"), asList(true, true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance instance1 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance instance2 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance instance3 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance instance4 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance instance5 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(instance1, "step1"); waitForUserTask(instance2, "step1"); waitForUserTask(instance3, "step1"); waitForUserTask(instance4, "step1"); waitForUserTask(instance5, "step1"); // test started by correct user final SearchOptionsBuilder searchOptions1 = BuildTestUtil.buildSearchOptions(processDefinition.getId(), 0, 10, ProcessInstanceSearchDescriptor.ID, Order.ASC); searchOptions1.filter(ProcessInstanceSearchDescriptor.STARTED_BY, user.getId()); SearchResult result = getProcessAPI().searchOpenProcessInstances(searchOptions1.done()); assertNotNull(result); assertEquals(5, result.getCount()); final List processInstanceList1 = result.getResult(); assertNotNull(processInstanceList1); assertEquals(5, processInstanceList1.size()); assertEquals(instance1.getId(), processInstanceList1.get(0).getId()); assertEquals(instance2.getId(), processInstanceList1.get(1).getId()); assertEquals(instance3.getId(), processInstanceList1.get(2).getId()); assertEquals(instance4.getId(), processInstanceList1.get(3).getId()); assertEquals(instance5.getId(), processInstanceList1.get(4).getId()); // test started by not existed user searchOptions1.filter(ProcessInstanceSearchDescriptor.STARTED_BY, user.getId() + 1500); result = getProcessAPI().searchOpenProcessInstances(searchOptions1.done()); assertNotNull(result); assertEquals(0, result.getCount()); disableAndDeleteProcess(processDefinition); } @Test public void searchArchivedProcessInstancesInvolvingUser() throws Exception { final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( asList("step1"), asList(false)); final ProcessDefinition processDefinition = deployAndEnableProcess(designProcessDefinition); final ProcessInstance instance1 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance instance2 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance instance3 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance instance4 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance instance5 = getProcessAPI().startProcess(processDefinition.getId()); waitForProcessToFinish(instance1); waitForProcessToFinish(instance2); waitForProcessToFinish(instance3); waitForProcessToFinish(instance4); waitForProcessToFinish(instance5); // test started by correct user final SearchOptionsBuilder opts = new SearchOptionsBuilder(0, 10); opts.filter(ArchivedProcessInstancesSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId()); opts.sort(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, Order.ASC); final SearchResult result = getProcessAPI() .searchArchivedProcessInstancesInvolvingUser(user.getId(), opts.done()); assertEquals(5, result.getCount()); final List processInstances = result.getResult(); assertNotNull(processInstances); assertEquals(5, processInstances.size()); assertEquals(instance1.getId(), processInstances.get(0).getSourceObjectId()); assertEquals(instance2.getId(), processInstances.get(1).getSourceObjectId()); assertEquals(instance3.getId(), processInstances.get(2).getSourceObjectId()); assertEquals(instance4.getId(), processInstances.get(3).getSourceObjectId()); assertEquals(instance5.getId(), processInstances.get(4).getSourceObjectId()); disableAndDeleteProcess(processDefinition); } @Test public void twoPoolsWithOneWithACallActivityCaseTest() throws Exception { final ProcessDefinition subProcessDefinition = buildAndDeploySubprocess(); final ProcessDefinition rootProcessDefinition = buildAndDeployProcessWithCallActivity(); final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcessDefinition.getId()); waitForUserTask(rootProcessInstance, "User task"); final SearchOptions opts = new SearchOptionsBuilder(0, 10).done(); final SearchResult processInstanceSearchResult = getProcessAPI() .searchOpenProcessInstances(opts); Assert.assertThat(processInstanceSearchResult.getCount(), is(1L)); disableAndDeleteProcess(rootProcessDefinition); disableAndDeleteProcess(subProcessDefinition); } private ProcessDefinition buildAndDeploySubprocess() throws InvalidProcessDefinitionException, BonitaException { final ProcessDefinitionBuilder subProcessDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("subprocess", "1.0"); subProcessDefinitionBuilder.addActor(ACTOR_NAME); subProcessDefinitionBuilder.addUserTask("User task", ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = subProcessDefinitionBuilder.done(); return deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); } private ProcessDefinition buildAndDeployProcessWithCallActivity() throws InvalidExpressionException, InvalidProcessDefinitionException, BonitaException { final ProcessDefinitionBuilder rootProcessDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("process1", "1.0"); rootProcessDefinitionBuilder.addActor(ACTOR_NAME); final Expression subProcessName = new ExpressionBuilder().createConstantStringExpression("subprocess"); final Expression subProcessVersion = new ExpressionBuilder().createConstantStringExpression("1.0"); rootProcessDefinitionBuilder.addCallActivity("call subprocess", subProcessName, subProcessVersion); final DesignProcessDefinition designProcessDefinition = rootProcessDefinitionBuilder.done(); return deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); } @Test public void searchProcessInstanceByRootProcessInstanceId() throws Exception { final ProcessDefinition subProcessDefinition = buildAndDeploySubprocess(); final ProcessDefinition rootProcessDefinition = buildAndDeployProcessWithCallActivity(); final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcessDefinition.getId()); waitForUserTask(rootProcessInstance, "User task"); SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(ProcessInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, rootProcessInstance.getId()); searchOptionsBuilder.differentFrom(ProcessInstanceSearchDescriptor.ID, rootProcessInstance.getId()); final SearchOptions opts = searchOptionsBuilder.done(); final SearchResult processInstanceSearchResult = getProcessAPI() .searchProcessInstances(opts); Assert.assertThat(processInstanceSearchResult.getCount(), is(1L)); Assert.assertThat(processInstanceSearchResult.getResult().get(0).getProcessDefinitionId(), is(subProcessDefinition.getId())); disableAndDeleteProcess(rootProcessDefinition); disableAndDeleteProcess(subProcessDefinition); } @Test public void searchArchivedProcessInstanceByRootProcessInstanceId() throws Exception { final ProcessDefinition subProcessDefinition = buildAndDeploySubprocess(); final ProcessDefinition rootProcessDefinition = buildAndDeployProcessWithCallActivity(); final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcessDefinition.getId()); waitForUserTaskAssignAndExecuteIt(rootProcessInstance, "User task", user, Collections.emptyMap()); waitForProcessToFinish(rootProcessInstance); SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, rootProcessInstance.getId()); searchOptionsBuilder.differentFrom(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, rootProcessInstance.getId()); searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.STATE_ID, ProcessInstanceState.COMPLETED.getId()); final SearchOptions opts = searchOptionsBuilder.done(); final SearchResult archivedProcessInstanceSearchResult = getProcessAPI() .searchArchivedProcessInstancesInAllStates(opts); Assert.assertThat(archivedProcessInstanceSearchResult.getCount(), is(1L)); Assert.assertThat(archivedProcessInstanceSearchResult.getResult().get(0).getProcessDefinitionId(), is(subProcessDefinition.getId())); disableAndDeleteProcess(rootProcessDefinition); disableAndDeleteProcess(subProcessDefinition); } @Test public void searchArchivedProcessInstancesWithApostrophe() throws Exception { // Create process final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("Na'me", PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("userTask1", ACTOR_NAME) .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask("userTask1"); final List activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 10); assertEquals(1, activityInstances.size()); for (final ActivityInstance activityInstance : activityInstances) { final long activityInstanceId = activityInstance.getId(); getProcessAPI().setActivityStateById(activityInstanceId, 12); } waitForProcessToFinish(processInstance); // Search Archived process final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10).searchTerm("Na'"); final SearchResult searchProcessInstanceResult = getProcessAPI() .searchArchivedProcessInstances(builder.done()); assertEquals(1, searchProcessInstanceResult.getCount()); final List archivedProcessInstances = searchProcessInstanceResult.getResult(); final ArchivedProcessInstance archivedProcessInstance = archivedProcessInstances.get(0); assertEquals(processInstance.getId(), archivedProcessInstance.getSourceObjectId()); assertEquals("Na'me", archivedProcessInstance.getName()); // Clean disableAndDeleteProcess(processDefinition); } @Test public void searchArchivedProcessInstancesRetrieveOnlyTerminalStates() throws Exception { // create a process instance in state completed and a process instance in the state canceled final ProcessDefinition simpleProcess = createArchivedProcessInstanceInStateCompletedAndCanceled(); // create process instance in state aborted final ProcessDefinition procWithEventSubProcess = createArchivedProcInstInAbortedState(); // search final SearchOptionsBuilder searchBuilder = new SearchOptionsBuilder(0, 10); searchBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STATE_ID, Order.ASC); final SearchResult searchResult = getProcessAPI() .searchArchivedProcessInstances(searchBuilder.done()); // check the result: 3 instances are expected, one in the state completed, one in the state canceled, and one in the state aborted assertEquals(3, searchResult.getCount()); final List processes = searchResult.getResult(); // canceled assertEquals(simpleProcess.getName(), processes.get(0).getName()); assertEquals(TestStates.CANCELLED.getStateName(), processes.get(0).getState()); // aborted assertEquals(procWithEventSubProcess.getName(), processes.get(1).getName()); assertEquals(TestStates.ABORTED.getStateName(), processes.get(1).getState()); // completed assertEquals(simpleProcess.getName(), processes.get(2).getName()); assertEquals(TestStates.NORMAL_FINAL.getStateName(), processes.get(2).getState()); // clean up disableAndDeleteProcess(simpleProcess.getId()); disableAndDeleteProcess(procWithEventSubProcess.getId()); } @Test public void searchUsersWhoCanStartProcess() throws Exception { // Create a disabled jack user final User jackIsDisabled = createUser("jack", PASSWORD); final UserUpdater userUpdater = new UserUpdater(); userUpdater.setEnabled(false); getIdentityAPI().updateUser(jackIsDisabled.getId(), userUpdater); // create a group and add a new user to it // this user will be add to the actor mapping directly and through this group // only one instance of this user should be returned by the search final User jackot = createUser("jackot", PASSWORD); Group employees = createGroup("Employees"); createRole("Member"); createUserMembership("jackot", "Member", "Employees"); // Create the process, deploy and enable it final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( PROCESS_NAME, PROCESS_VERSION, singletonList("step1"), singletonList(true), ACTOR_NAME, true); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, asList(employees), asList(user, jackot, jackIsDisabled)); // Search final SearchOptionsBuilder searchBuilder = new SearchOptionsBuilder(0, 10); searchBuilder.sort(UserSearchDescriptor.USER_NAME, Order.ASC); searchBuilder.searchTerm("jac"); final SearchResult searchResult = getProcessAPI() .searchUsersWhoCanStartProcessDefinition(processDefinition.getId(), searchBuilder.done()); assertThat(searchResult.getCount()).isEqualTo(1); final List users = searchResult.getResult(); assertThat(users).extracting("id").containsExactlyInAnyOrder(jackot.getId()); // clean up deleteUsers(jackIsDisabled, jackot); deleteGroups(employees); disableAndDeleteProcess(processDefinition.getId()); } @Test public void searchUsersWhoCanStartProcessInAGroup() throws Exception { final Group group = createGroup(GROUP_NAME); final Role role = createRole(ROLE_NAME); final UserMembership userMembership = getIdentityAPI().addUserMembership(user.getId(), group.getId(), role.getId()); final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( PROCESS_NAME, PROCESS_VERSION, asList("step1"), asList(true), ACTOR_NAME, true); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, group); // Search final SearchOptionsBuilder searchBuilder = new SearchOptionsBuilder(0, 10); searchBuilder.sort(UserSearchDescriptor.USER_NAME, Order.ASC); final SearchResult searchResult = getProcessAPI() .searchUsersWhoCanStartProcessDefinition(processDefinition.getId(), searchBuilder.done()); assertEquals(1, searchResult.getCount()); final List users = searchResult.getResult(); assertEquals(user.getId(), users.get(0).getId()); // clean up disableAndDeleteProcess(processDefinition.getId()); deleteUserMemberships(userMembership); deleteGroups(group); deleteRoles(role); } @Test public void searchUsersWhoCanStartProcessInARole() throws Exception { final Group group = createGroup(GROUP_NAME); final Role role = createRole(ROLE_NAME); final UserMembership userMembership = getIdentityAPI().addUserMembership(user.getId(), group.getId(), role.getId()); final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( PROCESS_NAME, PROCESS_VERSION, asList("step1"), asList(true), ACTOR_NAME, true); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, role); // Search final SearchOptionsBuilder searchBuilder = new SearchOptionsBuilder(0, 10); searchBuilder.sort(UserSearchDescriptor.USER_NAME, Order.ASC); final SearchResult searchResult = getProcessAPI() .searchUsersWhoCanStartProcessDefinition(processDefinition.getId(), searchBuilder.done()); assertEquals(1, searchResult.getCount()); final List users = searchResult.getResult(); assertEquals(user.getId(), users.get(0).getId()); // clean up disableAndDeleteProcess(processDefinition.getId()); deleteUserMemberships(userMembership); deleteGroups(group); deleteRoles(role); } @Test public void searchUsersWhoCanStartProcessInARoleAndAGroup() throws Exception { final Group group = createGroup(GROUP_NAME); final Role role = createRole(ROLE_NAME); final UserMembership userMembership = createUserMembership(USERNAME, ROLE_NAME, GROUP_NAME); final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( PROCESS_NAME, PROCESS_VERSION, asList("step1"), asList(true), ACTOR_NAME, true); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, role, group); // Search final SearchOptionsBuilder searchBuilder = new SearchOptionsBuilder(0, 10); searchBuilder.sort(UserSearchDescriptor.USER_NAME, Order.ASC); final SearchResult searchResult = getProcessAPI() .searchUsersWhoCanStartProcessDefinition(processDefinition.getId(), searchBuilder.done()); assertEquals(1, searchResult.getCount()); final List users = searchResult.getResult(); assertEquals(user.getId(), users.get(0).getId()); // clean up disableAndDeleteProcess(processDefinition.getId()); deleteUserMemberships(userMembership); deleteGroups(group); deleteRoles(role); } private ProcessDefinition createArchivedProcInstInAbortedState() throws Exception { final String userTaskName = "step1"; final String subProcTaskName = "subStep"; final String signalName = "go"; // deploy and start a process with event subprocess final ProcessDefinition procWithEventSubProcess = deployProcessWithEventSubProcess(userTaskName, subProcTaskName, signalName); final ProcessInstance procInstWithEventSubProc = getProcessAPI().startProcess(procWithEventSubProcess.getId()); // wait for first step of parent process and send a signal that will launch the event sub-process waitForUserTask(procInstWithEventSubProc, userTaskName); getProcessAPI().sendSignal(signalName); // execute user task and wait the parent process to finish (state aborted) waitForUserTaskAndExecuteIt(procInstWithEventSubProc, subProcTaskName, user); waitForProcessToBeInState(procInstWithEventSubProc, ProcessInstanceState.ABORTED); return procWithEventSubProcess; } private ProcessDefinition createArchivedProcessInstanceInStateCompletedAndCanceled() throws Exception { final String userTaskName = "step1"; // deploy and start simple process final ProcessDefinition simpleProcess = deployProcessWithHumanTask(userTaskName); final ProcessInstance processInstanceToComplete = getProcessAPI().startProcess(simpleProcess.getId()); // execute user task and wait process to finish: the process will be in the state completed waitForUserTaskAndExecuteIt(processInstanceToComplete, userTaskName, user); waitForProcessToFinish(processInstanceToComplete); // start another instance and cancel it: the process will be in the state canceled final ProcessInstance processInstanceToCancel = getProcessAPI().startProcess(simpleProcess.getId()); waitForUserTask(processInstanceToCancel, userTaskName); getProcessAPI().cancelProcessInstance(processInstanceToCancel.getId()); waitForProcessToBeInState(processInstanceToCancel, ProcessInstanceState.CANCELLED); return simpleProcess; } private ProcessDefinition deployProcessWithHumanTask(final String userTaskName) throws BonitaException, InvalidProcessDefinitionException { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("myProc", "1.0"); builder.addActor(ACTOR_NAME); builder.addUserTask(userTaskName, ACTOR_NAME); return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); } private ProcessDefinition deployProcessWithEventSubProcess(final String parentUserTaskName, final String suProcTaskName, final String signalName) throws BonitaException, InvalidProcessDefinitionException { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("eventProc", "1.0"); builder.addActor(ACTOR_NAME); builder.addUserTask(parentUserTaskName, ACTOR_NAME); final SubProcessDefinitionBuilder subProcessBuilder = builder.addSubProcess("sub", true).getSubProcessBuilder(); subProcessBuilder.addStartEvent("start").addSignalEventTrigger(signalName); subProcessBuilder.addUserTask(suProcTaskName, ACTOR_NAME); subProcessBuilder.addTransition("start", suProcTaskName); return deployAndEnableProcessWithActor(builder.done(), ACTOR_NAME, user); } @Test public void search_process_instances_in_all_states_retrieves_all_process_states() throws Exception { // deploy and start a process final ProcessDefinition simpleProcess = deployProcessWithHumanTask("step1"); final ProcessInstance procInst = getProcessAPI().startProcess(simpleProcess.getId()); // execute it until the end waitForUserTaskAndExecuteIt(procInst, "step1", user); waitForProcessToFinish(procInst); // search archived process instances: all states must be retrieved final SearchResult searchResult = searchArchivedProcessInstancesInAllStates( procInst.getId()); assertEquals(3, searchResult.getCount()); final List archivedProcesses = searchResult.getResult(); assertEquals(ProcessInstanceState.INITIALIZING.getId(), archivedProcesses.get(0).getStateId()); assertEquals(ProcessInstanceState.STARTED.getId(), archivedProcesses.get(1).getStateId()); assertEquals(ProcessInstanceState.COMPLETED.getId(), archivedProcesses.get(2).getStateId()); deleteProcessInstanceAndArchived(simpleProcess); disableAndDeleteProcess(simpleProcess); } @Test public void searchProcessInstancesWithAssigneeShouldReturnProcessWithAtLeastOneTaskAssignedToUser() throws Exception { final User otherUser = getIdentityAPI().createUser("newUser", "pepito"); final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( asList("step1"), asList(true)); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final long processDefId = processDefinition.getId(); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefId); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefId); final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefId); waitForUserTaskAndAssignIt(processInstance1, "step1", user); waitForUserTaskAndAssignIt(processInstance2, "step1", user); waitForUserTaskAndAssignIt(processInstance3, "step1", otherUser); final SearchOptions searchOptions = new SearchOptionsBuilder(0, 10) .filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefId) .filter(ProcessInstanceSearchDescriptor.ASSIGNEE_ID, user.getId()) .sort(ProcessInstanceSearchDescriptor.ID, Order.ASC).done(); final SearchResult processInstancesSearched = getProcessAPI() .searchProcessInstances(searchOptions); assertEquals(2, processInstancesSearched.getCount()); final List processInstances = processInstancesSearched.getResult(); assertEquals(processInstance1, processInstances.get(0)); assertEquals(processInstance2, processInstances.get(1)); disableAndDeleteProcess(processDefinition); deleteUser(otherUser); } private SearchResult searchArchivedProcessInstancesInAllStates( final long processInstanceId) throws SearchException { final SearchOptionsBuilder optionsBuilder = new SearchOptionsBuilder(0, 10); optionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, processInstanceId); optionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.CALLER_ID, -1L); optionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.ID, Order.ASC); return getProcessAPI().searchArchivedProcessInstancesInAllStates(optionsBuilder.done()); } @Test public void searchOpenProcessInstancesFromStringIndex1AndUpdateIt() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition).done(); final ProcessDefinition processDefinition = deployProcess(businessArchive); addUserToFirstActorOfProcess(1, processDefinition); getProcessAPI().enableProcess(processDefinition.getId()); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance1, "step2"); getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstanceUpdater updateDescriptor = new ProcessInstanceUpdater(); updateDescriptor.setStringIndex1("metsassa"); ProcessInstance processInstance = getProcessAPI().updateProcessInstance(processInstance1.getId(), updateDescriptor); assertEquals("metsassa", processInstance.getStringIndex1()); processInstance = getProcessAPI().updateProcessInstanceIndex(processInstance1.getId(), Index.FIRST, "metsassa1"); assertEquals("metsassa1", processInstance.getStringIndex1()); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(ProcessInstanceSearchDescriptor.STRING_INDEX_1, "metsassa1"); final SearchResult searchOpenProcessInstances = getProcessAPI() .searchOpenProcessInstances(builder.done()); assertEquals(1, searchOpenProcessInstances.getCount()); final List instances = searchOpenProcessInstances.getResult(); assertEquals(processInstance1, instances.get(0)); disableAndDeleteProcess(processDefinition); } @Test public void searchOpenProcessInstancesFromStringIndex2AndUpdateIt() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition).done(); final ProcessDefinition processDefinition = deployProcess(businessArchive); addUserToFirstActorOfProcess(1, processDefinition); getProcessAPI().enableProcess(processDefinition.getId()); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance1, "step2"); getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstanceUpdater updateDescriptor = new ProcessInstanceUpdater(); updateDescriptor.setStringIndex2("metsassa"); ProcessInstance processInstance = getProcessAPI().updateProcessInstance(processInstance1.getId(), updateDescriptor); assertEquals("metsassa", processInstance.getStringIndex2()); processInstance = getProcessAPI().updateProcessInstanceIndex(processInstance1.getId(), Index.SECOND, "metsassa2"); assertEquals("metsassa2", processInstance.getStringIndex2()); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(ProcessInstanceSearchDescriptor.STRING_INDEX_2, "metsassa2"); final SearchResult searchOpenProcessInstances = getProcessAPI() .searchOpenProcessInstances(builder.done()); assertEquals(1, searchOpenProcessInstances.getCount()); final List instances = searchOpenProcessInstances.getResult(); assertEquals(processInstance1, instances.get(0)); disableAndDeleteProcess(processDefinition); } @Test public void searchOpenProcessInstancesFromStringIndex3AndUpdateIt() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition).done(); final ProcessDefinition processDefinition = deployProcess(businessArchive); addUserToFirstActorOfProcess(1, processDefinition); getProcessAPI().enableProcess(processDefinition.getId()); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance1, "step2"); getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstanceUpdater updateDescriptor = new ProcessInstanceUpdater(); updateDescriptor.setStringIndex3("metsassa"); ProcessInstance processInstance = getProcessAPI().updateProcessInstance(processInstance1.getId(), updateDescriptor); assertEquals("metsassa", processInstance.getStringIndex3()); processInstance = getProcessAPI().updateProcessInstanceIndex(processInstance1.getId(), Index.THIRD, "metsassa3"); assertEquals("metsassa3", processInstance.getStringIndex3()); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(ProcessInstanceSearchDescriptor.STRING_INDEX_3, "metsassa3"); final SearchResult searchOpenProcessInstances = getProcessAPI() .searchOpenProcessInstances(builder.done()); assertEquals(1, searchOpenProcessInstances.getCount()); final List instances = searchOpenProcessInstances.getResult(); assertEquals(processInstance1, instances.get(0)); disableAndDeleteProcess(processDefinition); } @Test public void searchOpenProcessInstancesFromStringIndex4AndUpdateIt() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition).done(); final ProcessDefinition processDefinition = deployProcess(businessArchive); addUserToFirstActorOfProcess(1, processDefinition); getProcessAPI().enableProcess(processDefinition.getId()); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance1, "step2"); getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstanceUpdater updateDescriptor = new ProcessInstanceUpdater(); updateDescriptor.setStringIndex4("metsassa"); ProcessInstance processInstance = getProcessAPI().updateProcessInstance(processInstance1.getId(), updateDescriptor); assertEquals("metsassa", processInstance.getStringIndex4()); processInstance = getProcessAPI().updateProcessInstanceIndex(processInstance1.getId(), Index.FOURTH, "metsassa4"); assertEquals("metsassa4", processInstance.getStringIndex4()); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(ProcessInstanceSearchDescriptor.STRING_INDEX_4, "metsassa4"); final SearchResult searchOpenProcessInstances = getProcessAPI() .searchOpenProcessInstances(builder.done()); assertEquals(1, searchOpenProcessInstances.getCount()); final List instances = searchOpenProcessInstances.getResult(); assertEquals(processInstance1, instances.get(0)); disableAndDeleteProcess(processDefinition); } @Test public void searchOpenProcessInstancesFromStringIndex5AndUpdateIt() throws Exception { final DesignProcessDefinition designProcessDefinition = BuildTestUtil .buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition).done(); final ProcessDefinition processDefinition = deployProcess(businessArchive); addUserToFirstActorOfProcess(1, processDefinition); getProcessAPI().enableProcess(processDefinition.getId()); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance1, "step2"); getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstanceUpdater updateDescriptor = new ProcessInstanceUpdater(); updateDescriptor.setStringIndex5("metsassa"); ProcessInstance processInstance = getProcessAPI().updateProcessInstance(processInstance1.getId(), updateDescriptor); assertEquals("metsassa", processInstance.getStringIndex5()); processInstance = getProcessAPI().updateProcessInstanceIndex(processInstance1.getId(), Index.FIFTH, "metsassa5"); assertEquals("metsassa5", processInstance.getStringIndex5()); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(ProcessInstanceSearchDescriptor.STRING_INDEX_5, "metsassa5"); final SearchResult searchOpenProcessInstances = getProcessAPI() .searchOpenProcessInstances(builder.done()); assertEquals(1, searchOpenProcessInstances.getCount()); final List instances = searchOpenProcessInstances.getResult(); assertEquals(processInstance1, instances.get(0)); disableAndDeleteProcess(processDefinition); } @Test public void searchTermOnIndexOnProcessInstances() throws Exception { final User user1 = createUser("john1", "bpm"); final User user2 = createUser("john2", "bpm"); final User user3 = createUser("john3", "bpm"); final User user4 = createUser("john4", "bpm"); final DesignProcessDefinition designProcessDefinition1 = createProcessDefinition("3", true, "value1", "value2", "value3", "value4", "value5").done(); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, ACTOR_NAME, user1); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition1.getId()); waitForUserTask(processInstance1, "step1"); logout(); loginOnDefaultTenantWith("john1", "bpm"); final DesignProcessDefinition designProcessDefinition2 = createProcessDefinition("2", true, "value2", "value4", "value1", "value5", "value3").done(); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, ACTOR_NAME, user2); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId()); waitForUserTask(processInstance2, "step1"); logout(); loginOnDefaultTenantWith("john3", "bpm"); final DesignProcessDefinition designProcessDefinition3 = createProcessDefinition("5", true, "value4", "value3", "value5", "value2", "value1").done(); final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, ACTOR_NAME, user3); final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition3.getId()); waitForUserTask(processInstance3, "step1"); logout(); loginOnDefaultTenantWith("john2", "bpm"); final DesignProcessDefinition designProcessDefinition4 = createProcessDefinition("4", true, "value5", "value1", "value4", "value3", "value2").done(); final ProcessDefinition processDefinition4 = deployAndEnableProcessWithActor(designProcessDefinition4, ACTOR_NAME, user4); final ProcessInstance processInstance4 = getProcessAPI().startProcess(processDefinition4.getId()); waitForUserTask(processInstance4, "step1"); logout(); loginOnDefaultTenantWith("john4", "bpm"); final DesignProcessDefinition designProcessDefinition5 = createProcessDefinition("1", true, "value3", "value5", "value2", "value1", "value4").done(); final ProcessDefinition processDefinition5 = deployAndEnableProcessWithActor(designProcessDefinition5, ACTOR_NAME, user1); final ProcessInstance processInstance5 = getProcessAPI().startProcess(processDefinition5.getId()); waitForUserTask(processInstance5, "step1"); // Search term for STRING_INDEX final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor.ID, Order.ASC); searchOptionsBuilder.searchTerm("value1"); final List archivedProcessInstances = getProcessAPI() .searchOpenProcessInstances(searchOptionsBuilder.done()).getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getId()); assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getId()); assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getId()); assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getId()); assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getId()); disableAndDeleteProcess(processDefinition1, processDefinition2, processDefinition3, processDefinition4, processDefinition5); deleteUsers(user1, user2, user3, user4); } @Test public void searchArchivedProcessInstances() throws Exception { final User user1 = createUser("john1", "bpm"); final User user2 = createUser("john2", "bpm"); final User user3 = createUser("john3", "bpm"); final User user4 = createUser("john4", "bpm"); final DesignProcessDefinition designProcessDefinition1 = createProcessDefinition("3", false, "value1", "value2", "value3", "value4", "value5").done(); // final ProcessDefinition processDefinition1 = deployAndEnableWithActor(designProcessDefinition1, delivery, user1); final ProcessDefinition processDefinition1 = deployAndEnableProcess(designProcessDefinition1); final ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition1.getId()); waitForProcessToFinish(processInstance1); logout(); loginOnDefaultTenantWith("john1", "bpm"); final DesignProcessDefinition designProcessDefinition2 = createProcessDefinition("2", false, "value2", "value4", "value1", "value5", "value3").done(); final ProcessDefinition processDefinition2 = deployAndEnableProcess(designProcessDefinition2); final ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId()); waitForProcessToFinish(processInstance2); logout(); loginOnDefaultTenantWith("john3", "bpm"); final DesignProcessDefinition designProcessDefinition3 = createProcessDefinition("5", false, "value4", "value3", "value5", "value2", "value1").done(); final ProcessDefinition processDefinition3 = deployAndEnableProcess(designProcessDefinition3); final ProcessInstance processInstance3 = getProcessAPI().startProcess(processDefinition3.getId()); waitForProcessToFinish(processInstance3); logout(); loginOnDefaultTenantWith("john2", "bpm"); final DesignProcessDefinition designProcessDefinition4 = createProcessDefinition("4", false, "value5", "value1", "value4", "value3", "value2").done(); final ProcessDefinition processDefinition4 = deployAndEnableProcess(designProcessDefinition4); final ProcessInstance processInstance4 = getProcessAPI().startProcess(processDefinition4.getId()); waitForProcessToFinish(processInstance4); logout(); loginOnDefaultTenantWith("john4", "bpm"); final DesignProcessDefinition designProcessDefinition5 = createProcessDefinition("1", false, "value3", "value5", "value2", "value1", "value4").done(); final ProcessDefinition processDefinition5 = deployAndEnableProcess(designProcessDefinition5); final ProcessInstance processInstance5 = getProcessAPI().startProcess(processDefinition5.getId()); waitForProcessToFinish(processInstance5); // Order by ARCHIVE_DATE SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.ARCHIVE_DATE, Order.ASC); final SearchResult result = getProcessAPI() .searchArchivedProcessInstances(searchOptionsBuilder.done()); assertEquals(5, result.getCount()); List archivedProcessInstances = result.getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId()); assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId()); assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId()); assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId()); assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId()); // Order by END_DATE searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.END_DATE, Order.ASC); archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done()) .getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId()); assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId()); assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId()); assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId()); assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId()); // Order by ID searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.ID, Order.ASC); archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done()) .getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId()); assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId()); assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId()); assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId()); assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId()); // Order by LAST_UPDATE searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.LAST_UPDATE, Order.ASC); archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done()) .getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId()); assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId()); assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId()); assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId()); assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId()); // Order by NAME searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.NAME, Order.ASC); archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done()) .getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(processInstance5.getId(), archivedProcessInstances.get(0).getSourceObjectId()); assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId()); assertEquals(processInstance1.getId(), archivedProcessInstances.get(2).getSourceObjectId()); assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId()); assertEquals(processInstance3.getId(), archivedProcessInstances.get(4).getSourceObjectId()); // Order by SOURCE_OBJECT_ID searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, Order.ASC); archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done()) .getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId()); assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId()); assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId()); assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId()); assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId()); // Order by START_DATE searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.START_DATE, Order.ASC); archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done()) .getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId()); assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId()); assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId()); assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId()); assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId()); // Order by STARTED_BY searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STARTED_BY, Order.ASC); archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done()) .getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId()); assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId()); assertEquals(processInstance4.getId(), archivedProcessInstances.get(2).getSourceObjectId()); assertEquals(processInstance3.getId(), archivedProcessInstances.get(3).getSourceObjectId()); assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId()); // Order by STATE_ID searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STATE_ID, Order.ASC); archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done()) .getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(6, archivedProcessInstances.get(0).getStateId()); assertEquals(6, archivedProcessInstances.get(1).getStateId()); assertEquals(6, archivedProcessInstances.get(2).getStateId()); assertEquals(6, archivedProcessInstances.get(3).getStateId()); assertEquals(6, archivedProcessInstances.get(4).getStateId()); // Order by STRING_INDEX_1 searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_1, Order.ASC); archivedProcessInstances = getProcessAPI() .searchArchivedProcessInstances(searchOptionsBuilder.done()).getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId()); assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId()); assertEquals(processInstance5.getId(), archivedProcessInstances.get(2).getSourceObjectId()); assertEquals(processInstance3.getId(), archivedProcessInstances.get(3).getSourceObjectId()); assertEquals(processInstance4.getId(), archivedProcessInstances.get(4).getSourceObjectId()); // Order by STRING_INDEX_2 searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_2, Order.ASC); archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done()) .getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(processInstance4.getId(), archivedProcessInstances.get(0).getSourceObjectId()); assertEquals(processInstance1.getId(), archivedProcessInstances.get(1).getSourceObjectId()); assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId()); assertEquals(processInstance2.getId(), archivedProcessInstances.get(3).getSourceObjectId()); assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId()); // Order by STRING_INDEX_3 searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_3, Order.ASC); archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done()) .getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(processInstance2.getId(), archivedProcessInstances.get(0).getSourceObjectId()); assertEquals(processInstance5.getId(), archivedProcessInstances.get(1).getSourceObjectId()); assertEquals(processInstance1.getId(), archivedProcessInstances.get(2).getSourceObjectId()); assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId()); assertEquals(processInstance3.getId(), archivedProcessInstances.get(4).getSourceObjectId()); // Order by STRING_INDEX_4 searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_4, Order.ASC); archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done()) .getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(processInstance5.getId(), archivedProcessInstances.get(0).getSourceObjectId()); assertEquals(processInstance3.getId(), archivedProcessInstances.get(1).getSourceObjectId()); assertEquals(processInstance4.getId(), archivedProcessInstances.get(2).getSourceObjectId()); assertEquals(processInstance1.getId(), archivedProcessInstances.get(3).getSourceObjectId()); assertEquals(processInstance2.getId(), archivedProcessInstances.get(4).getSourceObjectId()); // Order by STRING_INDEX_5 searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_5, Order.ASC); archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done()) .getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(processInstance3.getId(), archivedProcessInstances.get(0).getSourceObjectId()); assertEquals(processInstance4.getId(), archivedProcessInstances.get(1).getSourceObjectId()); assertEquals(processInstance2.getId(), archivedProcessInstances.get(2).getSourceObjectId()); assertEquals(processInstance5.getId(), archivedProcessInstances.get(3).getSourceObjectId()); assertEquals(processInstance1.getId(), archivedProcessInstances.get(4).getSourceObjectId()); // Search term for STRING_INDEX searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.sort(org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor.ID, Order.ASC); searchOptionsBuilder.searchTerm("value1"); archivedProcessInstances = getProcessAPI().searchArchivedProcessInstances(searchOptionsBuilder.done()) .getResult(); assertNotNull(archivedProcessInstances); assertEquals(5, archivedProcessInstances.size()); assertEquals(processInstance1.getId(), archivedProcessInstances.get(0).getSourceObjectId()); assertEquals(processInstance2.getId(), archivedProcessInstances.get(1).getSourceObjectId()); assertEquals(processInstance3.getId(), archivedProcessInstances.get(2).getSourceObjectId()); assertEquals(processInstance4.getId(), archivedProcessInstances.get(3).getSourceObjectId()); assertEquals(processInstance5.getId(), archivedProcessInstances.get(4).getSourceObjectId()); disableAndDeleteProcess(processDefinition1, processDefinition2, processDefinition3, processDefinition4, processDefinition5); deleteUsers(user1, user2, user3, user4); } private ProcessDefinitionBuilder createProcessDefinition(final String processName, final boolean withUserTask, final String stringIndex1, final String stringIndex2, final String stringIndex3, final String stringIndex4, final String stringIndex5) throws InvalidExpressionException { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance(processName, "17.3"); designProcessDefinition.addDescription("Delivery all day and night long"); if (withUserTask) { designProcessDefinition.addActor(ACTOR_NAME); designProcessDefinition.addUserTask("step1", ACTOR_NAME); } else { designProcessDefinition.addAutomaticTask("step1"); } if (!StringUtils.isBlank(stringIndex1)) { designProcessDefinition.setStringIndex(1, "label1", new ExpressionBuilder().createConstantStringExpression(stringIndex1)); } if (!StringUtils.isBlank(stringIndex2)) { designProcessDefinition.setStringIndex(2, "label2", new ExpressionBuilder().createConstantStringExpression(stringIndex2)); } if (!StringUtils.isBlank(stringIndex3)) { designProcessDefinition.setStringIndex(3, "label3", new ExpressionBuilder().createConstantStringExpression(stringIndex3)); } if (!StringUtils.isBlank(stringIndex4)) { designProcessDefinition.setStringIndex(4, "label4", new ExpressionBuilder().createConstantStringExpression(stringIndex4)); } if (!StringUtils.isBlank(stringIndex5)) { designProcessDefinition.setStringIndex(5, "label5", new ExpressionBuilder().createConstantStringExpression(stringIndex5)); } return designProcessDefinition; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/search/SearchUncategorizedProcessDeploymentInfosCanBeStartedByIT.java ================================================ /** * 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.search; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.engine.bpm.category.CategoryCriterion; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserMembership; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Celine Souchet */ public class SearchUncategorizedProcessDeploymentInfosCanBeStartedByIT extends TestWithTechnicalUser { private List enabledProcessDefinitions; private List disabledProcessDefinitions; private List users = null; private List categories; private List groups = null; private List roles = null; private List userMemberships = null; @Override @Before public void before() throws Exception { super.before(); categories = new ArrayList(); users = new ArrayList(6); users.add(createUser("chicobento", "bpm")); users.add(createUser("cebolinha", "bpm")); users.add(createUser("cascao", "bpm")); users.add(createUser("magali", "bpm")); users.add(createUser("monica", "bpm")); users.add(createUser("dorinha", "bpm")); groups = new ArrayList(2); groups.add(createGroup("group1")); groups.add(createGroup("group2")); roles = new ArrayList(2); roles.add(createRole("role1")); roles.add(createRole("role2")); userMemberships = new ArrayList(3); userMemberships.add( getIdentityAPI().addUserMembership(users.get(3).getId(), groups.get(0).getId(), roles.get(0).getId())); userMemberships.add( getIdentityAPI().addUserMembership(users.get(4).getId(), groups.get(0).getId(), roles.get(1).getId())); userMemberships.add( getIdentityAPI().addUserMembership(users.get(5).getId(), groups.get(1).getId(), roles.get(0).getId())); enabledProcessDefinitions = new ArrayList(4); disabledProcessDefinitions = new ArrayList(1); createProcessesDefForSearchProcessUserCanStart(); } @Override @After public void after() throws Exception { disableAndDeleteProcess(enabledProcessDefinitions); deleteProcess(disabledProcessDefinitions); deleteCategories(categories); deleteUserMemberships(userMemberships); deleteUsers(users); deleteGroups(groups); deleteRoles(roles); super.after(); } @Test public void searchUncategorizedProcessDefinitions() throws Exception { loginOnDefaultTenantWith("chicobento", "bpm"); // add categories to processDefinition1 categories.add(getProcessAPI().createCategory("category1", "categoryDescription1")); categories.add(getProcessAPI().createCategory("category2", "categoryDescription2")); categories.add(getProcessAPI().createCategory("category3", "categoryDescription3")); final ArrayList categoryIds = new ArrayList(); categoryIds.add(categories.get(0).getId()); categoryIds.add(categories.get(1).getId()); categoryIds.add(categories.get(2).getId()); getProcessAPI().addCategoriesToProcess(enabledProcessDefinitions.get(0).getId(), categoryIds); categories = getProcessAPI().getCategoriesOfProcessDefinition(enabledProcessDefinitions.get(0).getId(), 0, 10, CategoryCriterion.NAME_ASC); assertTrue(!categories.isEmpty()); // Get all process definitions: SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 5); optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.DEPLOYMENT_DATE, Order.ASC); SearchResult searchRes0 = getProcessAPI() .searchProcessDeploymentInfos(optsBuilder.done()); assertEquals(8, searchRes0.getCount()); assertEquals(enabledProcessDefinitions.get(1).getId(), searchRes0.getResult().get(1).getProcessId()); assertEquals(enabledProcessDefinitions.get(0).getId(), searchRes0.getResult().get(0).getProcessId()); // Get all process definitions with no category associated: optsBuilder = new SearchOptionsBuilder(0, 7); optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.DEPLOYMENT_DATE, Order.ASC); searchRes0 = getProcessAPI().searchUncategorizedProcessDeploymentInfos(optsBuilder.done()); assertEquals(7, searchRes0.getCount()); assertEquals(enabledProcessDefinitions.get(1).getId(), searchRes0.getResult().get(0).getProcessId()); } @Test public void searchUncategorizedProcessDefinitionsUserCanStartFromGroup() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosCanBeStartedBy(users.get(4).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(4).getName(), searchRes.getResult().get(0).getName()); } @Test public void searchUncategorizedProcessDefinitionsUserCanStartFromRole() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosCanBeStartedBy(users.get(5).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(5).getName(), searchRes.getResult().get(0).getName()); } @Test public void searchUncategorizedProcessDefinitionsUserCanStartFromRoleAndGroup() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosCanBeStartedBy(users.get(3).getId(), searchOptionsBuilder.done()); assertEquals(3, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(4).getName(), searchRes.getResult().get(0).getName()); // from group assertEquals(enabledProcessDefinitions.get(5).getName(), searchRes.getResult().get(1).getName()); // from role assertEquals(enabledProcessDefinitions.get(6).getName(), searchRes.getResult().get(2).getName()); // from role and group } @Test public void searchUncategorizedProcessDefinitionsUserCanStartWithSearchTearm() throws Exception { // test term final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); searchOptionsBuilder.searchTerm("My_Process2"); // use name as term final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosCanBeStartedBy(users.get(1).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(1).getId(), searchRes.getResult().get(0).getProcessId()); } @Test public void searchUncategorizedProcessDefinitionsUserCanStart() throws Exception { // test uncategorized process definitions. final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 5) .sort(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); SearchResult searchRes = getProcessAPI() .searchUncategorizedProcessDeploymentInfosCanBeStartedBy(users.get(0).getId(), searchOptionsBuilder.done()); assertEquals(1, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(0).getName(), searchRes.getResult().get(0).getName()); searchRes = getProcessAPI().searchUncategorizedProcessDeploymentInfosCanBeStartedBy(users.get(1).getId(), searchOptionsBuilder.done()); assertEquals(2, searchRes.getCount()); assertEquals(enabledProcessDefinitions.get(1).getName(), searchRes.getResult().get(0).getName()); assertEquals(enabledProcessDefinitions.get(2).getName(), searchRes.getResult().get(1).getName()); // user associated to a process without actor initiator searchRes = getProcessAPI().searchUncategorizedProcessDeploymentInfosCanBeStartedBy(users.get(2).getId(), searchOptionsBuilder.done()); assertEquals(0, searchRes.getCount()); } private void createProcessesDefForSearchProcessUserCanStart() throws Exception { final String actor1 = ACTOR_NAME; final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process1", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor1, true); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition1, actor1, users.get(0)); enabledProcessDefinitions.add(processDefinition1); // create process2 final String actor2 = "Actor2"; final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process2", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition2 = deployAndEnableProcessWithActor(designProcessDefinition2, actor2, users.get(1)); enabledProcessDefinitions.add(processDefinition2); final DesignProcessDefinition designProcessDefinition3 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process3", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition3 = deployAndEnableProcessWithActor(designProcessDefinition3, actor2, users.get(1)); enabledProcessDefinitions.add(processDefinition3); // process not enabled final DesignProcessDefinition designProcessDefinition4 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process4", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition4 = getProcessAPI().deploy( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition(designProcessDefinition4) .done()); getProcessAPI().addUserToActor(actor2, processDefinition4, users.get(1).getId()); disabledProcessDefinitions.add(processDefinition4); // process without actor initiator final DesignProcessDefinition designProcessDefinition5 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process5", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, false); final ProcessDefinition processDefinition5 = deployAndEnableProcessWithActor(designProcessDefinition5, actor2, users.get(2)); enabledProcessDefinitions.add(processDefinition5); // actor initiator is a group final DesignProcessDefinition designProcessDefinition6 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process6", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition6 = deployAndEnableProcessWithActor(designProcessDefinition6, actor2, groups.get(0)); enabledProcessDefinitions.add(processDefinition6); // actor initiator is a role final DesignProcessDefinition designProcessDefinition7 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process7", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition7 = deployAndEnableProcessWithActor(designProcessDefinition7, actor2, roles.get(0)); enabledProcessDefinitions.add(processDefinition7); // actor initiator is a membership final DesignProcessDefinition designProcessDefinition8 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps("My_Process8", "1.0", Arrays.asList("step1", "step2"), Arrays.asList(true, true), actor2, true); final ProcessDefinition processDefinition8 = deployAndEnableProcessWithActor(designProcessDefinition8, actor2, roles.get(0), groups.get(0)); enabledProcessDefinitions.add(processDefinition8); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/supervisor/ProcessSupervisedIT.java ================================================ /** * 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.supervisor; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.engine.bpm.category.CategoryCriterion; import org.bonitasoft.engine.bpm.document.ArchivedDocument; import org.bonitasoft.engine.bpm.document.ArchivedDocumentsSearchDescriptor; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.document.DocumentsSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.TaskPriority; import org.bonitasoft.engine.bpm.flownode.UserTaskInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.RoleCreator; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserMembership; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; public class ProcessSupervisedIT extends TestWithTechnicalUser { private static final String SEARCH_S_COMMENT_SUPERVISED_BY = "searchSCommentSupervisedBy"; private static final String SUPERVISOR_ID_CMD_KEY = "supervisorId"; private User john; private User matti; private UserMembership membership; private Role role; private Group group; private ProcessDefinition definition; private List processDefinitions; private List processInstances; private ProcessSupervisor supervisorForUser; private ProcessSupervisor supervisorForRole; private ProcessSupervisor supervisorForGroup; private List supervisors; @Override @Before public void before() throws Exception { super.before(); john = createUser("john", "bpm"); matti = createUser("matti", "bpm"); logout(); loginOnDefaultTenantWith("matti", PASSWORD); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance("firstProcess", "1.0"); processBuilder.addActor(ACTOR_NAME).addUserTask("step1", ACTOR_NAME); processBuilder.addShortTextData("Application", null); definition = deployAndEnableProcessWithActor(processBuilder.done(), ACTOR_NAME, john); processDefinitions = new ArrayList<>(); processDefinitions.add(definition); // Create supervisors supervisorForUser = getProcessAPI().createProcessSupervisorForUser(definition.getId(), matti.getId()); assertEquals(definition.getId(), supervisorForUser.getProcessDefinitionId()); // add supervisor by role role = getIdentityAPI().createRole(new RoleCreator("developer")); supervisorForRole = getProcessAPI().createProcessSupervisorForRole(definition.getId(), role.getId()); // add supervisor group group = getIdentityAPI().createGroup("R&D", null); getIdentityAPI().addUserMembership(matti.getId(), group.getId(), role.getId()); supervisorForGroup = getProcessAPI().createProcessSupervisorForGroup(definition.getId(), group.getId()); // add supervisor membership membership = getIdentityAPI().addUserMembership(john.getId(), group.getId(), role.getId()); supervisors = new ArrayList<>(); supervisors.add(supervisorForGroup); supervisors.add(supervisorForRole); supervisors.add(supervisorForUser); // Three tasks processInstances = new ArrayList<>(); processInstances.add(getProcessAPI().startProcess(definition.getId())); processInstances.add(getProcessAPI().startProcess(definition.getId())); processInstances.add(getProcessAPI().startProcess(definition.getId())); waitForUserTaskAndAssignIt(processInstances.get(0), "step1", john); waitForUserTaskAndAssignIt(processInstances.get(1), "step1", john); waitForUserTask(processInstances.get(2), "step1"); } @Override @After public void after() throws Exception { deleteSupervisors(supervisors); disableAndDeleteProcess(processDefinitions); getIdentityAPI().deleteUserMembership(membership.getId()); getIdentityAPI().deleteUser(john.getId()); getIdentityAPI().deleteUser(matti.getId()); getIdentityAPI().deleteGroup(group.getId()); getIdentityAPI().deleteRole(role.getId()); super.after(); } @Test public void searchAssignedTasksSupervisedBy() throws Exception { SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.DESC); builder.filter("state", "ready"); builder.filter("name", "step1"); builder.filter("priority", TaskPriority.NORMAL); // Test with Enum value filter on Task Priority SearchResult searchResult = getProcessAPI() .searchAssignedTasksSupervisedBy(matti.getId(), builder.done()); assertEquals(2, searchResult.getResult().size()); UserTaskInstance taskInstance = (UserTaskInstance) searchResult.getResult().get(0); assertEquals("step1", taskInstance.getName()); assertEquals(john.getId(), taskInstance.getAssigneeId()); taskInstance = (UserTaskInstance) searchResult.getResult().get(1); assertEquals("step1", taskInstance.getName()); assertEquals(john.getId(), taskInstance.getAssigneeId()); // Test the same thing with numeric filter on Task Priority: builder = new SearchOptionsBuilder(0, 10); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.DESC); builder.filter("state", "ready"); builder.filter("name", "step1"); builder.filter("priority", TaskPriority.NORMAL.ordinal()); searchResult = getProcessAPI().searchAssignedTasksSupervisedBy(matti.getId(), builder.done()); assertEquals(2, searchResult.getResult().size()); taskInstance = (UserTaskInstance) searchResult.getResult().get(0); assertEquals("step1", taskInstance.getName()); assertEquals(john.getId(), taskInstance.getAssigneeId()); taskInstance = (UserTaskInstance) searchResult.getResult().get(1); assertEquals("step1", taskInstance.getName()); assertEquals(john.getId(), taskInstance.getAssigneeId()); // Test the same thing with String filter on Task Priority: builder = new SearchOptionsBuilder(0, 10); builder.sort(HumanTaskInstanceSearchDescriptor.NAME, Order.DESC); builder.filter("state", "ready"); builder.filter("name", "step1"); builder.filter("priority", TaskPriority.NORMAL.name()); searchResult = getProcessAPI().searchAssignedTasksSupervisedBy(matti.getId(), builder.done()); assertEquals(2, searchResult.getResult().size()); taskInstance = (UserTaskInstance) searchResult.getResult().get(0); assertEquals("step1", taskInstance.getName()); assertEquals(john.getId(), taskInstance.getAssigneeId()); taskInstance = (UserTaskInstance) searchResult.getResult().get(1); assertEquals("step1", taskInstance.getName()); assertEquals(john.getId(), taskInstance.getAssigneeId()); } @Test public void searchProcessDefinitionsSupervisedBy() throws Exception { // create processDefintions final List proDefIds = new ArrayList<>(); final int num = 5; for (int i = 0; i < num; i++) { final String actorName = "actorManu" + i; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("My_Process" + i, "1." + i); processBuilder.addActor(actorName).addDescription("actor description" + i); final DesignProcessDefinition designProcessDefinition = processBuilder.addUserTask("step1", actorName) .getProcess(); final ProcessDefinition processDefinition1 = deployAndEnableProcessWithActor(designProcessDefinition, actorName, john); processDefinitions.add(processDefinition1); proDefIds.add(processDefinition1.getId()); } // add same supervisors for (int i = num - 1; i > 0; i--) { supervisors.add(getProcessAPI().createProcessSupervisorForUser(proDefIds.get(i), john.getId())); } // create category hr final String categoryName = "HR"; final String categoryDescription = "This category for HR."; final Category category1 = getProcessAPI().createCategory(categoryName, categoryDescription); // create category sales final String categoryName2 = "sales"; final String categoryDescription2 = "This category for sales."; final Category category2 = getProcessAPI().createCategory(categoryName2, categoryDescription2); // three processDefinitions for HR getProcessAPI().addProcessDefinitionsToCategory(category1.getId(), new ArrayList<>(proDefIds.subList(0, 3))); // two processDefinitions for sales getProcessAPI().addProcessDefinitionsToCategory(category2.getId(), new ArrayList<>(proDefIds.subList(3, proDefIds.size()))); // test get all process Definitions without filter final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.sort(ProcessDeploymentInfoSearchDescriptor.ID, Order.DESC); final SearchResult searchRes = getProcessAPI() .searchProcessDeploymentInfosSupervisedBy(john.getId(), builder.done()); assertEquals(5, searchRes.getCount()); // test search in order final SearchOptionsBuilder builder1 = new SearchOptionsBuilder(0, 10); builder1.filter(ProcessDeploymentInfoSearchDescriptor.CATEGORY_ID, category1.getId()); builder1.sort(ProcessDeploymentInfoSearchDescriptor.ID, Order.DESC); final SearchResult searchRes1 = getProcessAPI() .searchProcessDeploymentInfosSupervisedBy(john.getId(), builder1.done()); assertEquals(2, searchRes1.getCount()); final List processDeploymentInfos1 = searchRes1.getResult(); assertNotNull(processDeploymentInfos1); assertEquals(2, processDeploymentInfos1.size()); assertEquals(proDefIds.get(2).longValue(), processDeploymentInfos1.get(0).getProcessId()); assertEquals(proDefIds.get(1).longValue(), processDeploymentInfos1.get(1).getProcessId()); // test term final SearchOptionsBuilder builder2 = new SearchOptionsBuilder(0, 10); builder2.filter(ProcessDeploymentInfoSearchDescriptor.CATEGORY_ID, category2.getId()); builder2.searchTerm("My_Process4"); // use name as term final SearchResult searchRes2 = getProcessAPI() .searchProcessDeploymentInfosSupervisedBy(john.getId(), builder2.done()); assertEquals(1, searchRes2.getCount()); final List processDeploymentInfos2 = searchRes2.getResult(); assertNotNull(processDeploymentInfos2); assertEquals(1, processDeploymentInfos2.size()); assertEquals(proDefIds.get(4).longValue(), processDeploymentInfos2.get(0).getProcessId()); getProcessAPI().deleteCategory(category1.getId()); getProcessAPI().deleteCategory(category2.getId()); } @Test @SuppressWarnings("unchecked") public void searchCommentsSupervisedBy() throws Exception { // prepare commentContent final ProcessInstance processInstance3 = processInstances.get(2); // add comment to processInstance getProcessAPI().addProcessComment(processInstance3.getId(), "commentContent1"); getProcessAPI().addProcessComment(processInstance3.getId(), "commentContent2"); getProcessAPI().addProcessComment(processInstance3.getId(), "commentContent3"); loginOnDefaultTenantWith("john", "bpm"); final ProcessDefinitionBuilder processBuilder2 = new ProcessDefinitionBuilder() .createNewInstance("secondProcess", "2.0"); processBuilder2.addDescription("definition2 description"); processBuilder2.addActor(ACTOR_NAME).addUserTask("temporize", ACTOR_NAME); final DesignProcessDefinition designprocessDefinition2 = processBuilder2.done(); final ProcessDefinition definition2 = deployAndEnableProcessWithActor(designprocessDefinition2, ACTOR_NAME, matti); processDefinitions.add(definition2); final ProcessInstance pi1 = getProcessAPI().startProcess(definition2.getId()); getProcessAPI().addProcessComment(pi1.getId(), "commentContent4"); getProcessAPI().addProcessComment(pi1.getId(), "commentContent5"); // create supervisor for definition2 final ProcessSupervisor supervisor = getProcessAPI().createProcessSupervisorForUser(definition2.getId(), john.getId()); supervisors.add(supervisor); assertEquals(definition2.getId(), supervisor.getProcessDefinitionId()); final Map parameters1 = new HashMap<>(); parameters1.put(SUPERVISOR_ID_CMD_KEY, matti.getId()); parameters1.put("SEARCH_OPTIONS_KEY", new SearchOptionsBuilder(0, 10).done()); final SearchResult searchResult1 = (SearchResult) getCommandAPI() .execute(SEARCH_S_COMMENT_SUPERVISED_BY, parameters1); assertEquals(3, searchResult1.getCount()); final Map parameters2 = new HashMap<>(); parameters2.put(SUPERVISOR_ID_CMD_KEY, john.getId()); parameters2.put("SEARCH_OPTIONS_KEY", new SearchOptionsBuilder(0, 10).done()); final SearchResult searchResult2 = (SearchResult) getCommandAPI() .execute(SEARCH_S_COMMENT_SUPERVISED_BY, parameters2); assertEquals(5, searchResult2.getCount()); } @Test public void searchDocumentsSupervisedBy() throws Exception { final ProcessInstance processInstance = processInstances.get(2); buildAndAttachDocument(processInstance); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()); final SearchResult documentSearch = getProcessAPI().searchDocumentsSupervisedBy(john.getId(), searchOptionsBuilder.done()); assertEquals(1, documentSearch.getCount()); assertEquals(processInstance.getId(), documentSearch.getResult().get(0).getProcessInstanceId()); } @Test public void searchArchivedDocumentsSupervisedBy() throws Exception { final ProcessInstance processInstance = processInstances.get(2); buildAndAttachDocument(processInstance); skipTasks(processInstance); waitForProcessToFinish(processInstance); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 45); searchOptionsBuilder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, processInstance.getId()); searchOptionsBuilder.sort(ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME, Order.ASC); final SearchResult documentSearch = getProcessAPI() .searchArchivedDocumentsSupervisedBy(matti.getId(), searchOptionsBuilder.done()); assertEquals(3, documentSearch.getCount()); assertEquals(processInstance.getId(), documentSearch.getResult().get(0).getProcessInstanceId()); } @Test public void searchPendingTasksSupervisedBy() throws Exception { final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(0, 10, HumanTaskInstanceSearchDescriptor.NAME, Order.ASC); final SearchResult result = getProcessAPI().searchPendingTasksSupervisedBy(matti.getId(), searchOptions.done()); assertNotNull(result); assertEquals(3, result.getCount()); final List humanTaskInstanceList = result.getResult(); assertNotNull(humanTaskInstanceList); assertEquals(3, humanTaskInstanceList.size()); assertEquals(getProcessAPI().getActivities(processInstances.get(0).getId(), 0, 10).get(0).getId(), humanTaskInstanceList.get(0).getId()); assertEquals(getProcessAPI().getActivities(processInstances.get(1).getId(), 0, 10).get(0).getId(), humanTaskInstanceList.get(1).getId()); assertEquals(getProcessAPI().getActivities(processInstances.get(2).getId(), 0, 10).get(0).getId(), humanTaskInstanceList.get(2).getId()); } @Test public void searchUncategorizedProcessDefinitionsSupervisedBy() throws Exception { // create process1 final String processName1 = "processDefinition1"; final DesignProcessDefinition designProcessDefinition1 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(processName1, "1.1", Arrays.asList("step1_1", "step1_2"), Arrays.asList(true, true)); processDefinitions.add(deployAndEnableProcessWithActor(designProcessDefinition1, ACTOR_NAME, matti)); // create process2 final String processName2 = "processDefinition2"; final DesignProcessDefinition designProcessDefinition2 = BuildTestUtil .buildProcessDefinitionWithHumanAndAutomaticSteps(processName2, "1.2", Arrays.asList("step2_1", "step2_2"), Arrays.asList(true, true)); processDefinitions.add(deployAndEnableProcessWithActor(designProcessDefinition2, ACTOR_NAME, matti)); // create supervisor supervisors .add(getProcessAPI().createProcessSupervisorForUser(processDefinitions.get(1).getId(), john.getId())); supervisors .add(getProcessAPI().createProcessSupervisorForUser(processDefinitions.get(2).getId(), matti.getId())); // add categories to processDefinition1 final ArrayList categoryIds = new ArrayList<>(); final Category c1 = getProcessAPI().createCategory("category1", "categoryDescription1"); final Category c2 = getProcessAPI().createCategory("category2", "categoryDescription2"); final Category c3 = getProcessAPI().createCategory("category3", "categoryDescription3"); categoryIds.add(c1.getId()); categoryIds.add(c2.getId()); categoryIds.add(c3.getId()); getProcessAPI().addCategoriesToProcess(processDefinitions.get(0).getId(), categoryIds); final List categories = getProcessAPI() .getCategoriesOfProcessDefinition(processDefinitions.get(0).getId(), 0, 10, CategoryCriterion.NAME_ASC); assertTrue(!categories.isEmpty()); // Get all process definitions: final SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, 5); optsBuilder.sort(ProcessDeploymentInfoSearchDescriptor.DEPLOYMENT_DATE, Order.DESC); SearchResult searchRes0 = getProcessAPI() .searchProcessDeploymentInfos(optsBuilder.done()); assertEquals(3, searchRes0.getCount()); // Get all process definitions with no category associated, supervised by user: searchRes0 = getProcessAPI().searchUncategorizedProcessDeploymentInfosSupervisedBy(matti.getId(), optsBuilder.done()); assertEquals(1, searchRes0.getCount()); assertEquals(processDefinitions.get(2).getId(), searchRes0.getResult().get(0).getProcessId()); assertEquals("processDefinition2", searchRes0.getResult().get(0).getName()); searchRes0 = getProcessAPI().searchUncategorizedProcessDeploymentInfosSupervisedBy(john.getId(), optsBuilder.done()); assertEquals(1, searchRes0.getCount()); assertEquals(processDefinitions.get(1).getId(), searchRes0.getResult().get(0).getProcessId()); assertEquals("processDefinition1", searchRes0.getResult().get(0).getName()); deleteCategories(categories); } @Test public void searchOpenProcessInstancesSupervisedBy() throws Exception { final ProcessInstance instance = processInstances.get(2); // prepare search options final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(0, 10, ProcessInstanceSearchDescriptor.ID, Order.ASC); final SearchResult result = getProcessAPI() .searchOpenProcessInstancesSupervisedBy(matti.getId(), searchOptions.done()); assertNotNull(result); assertEquals(3, result.getCount()); final List processInstanceList = result.getResult(); assertNotNull(processInstanceList); assertEquals(3, processInstanceList.size()); assertEquals(instance.getId(), processInstanceList.get(2).getId()); } @Test public void searchProcessInstancesInvolvingUserWithSupervisorStartedProcess() throws Exception { final long processDefinitionId = processDefinitions.get(0).getId(); // assign pending task to jack final ProcessInstance processInstance = getProcessAPI().startProcess(john.getId(), processDefinitionId); processInstances.add(processInstance); final long step1Id = waitForUserTask(processInstance, "step1"); logout(); loginOnDefaultTenantWith("john", PASSWORD); final SearchOptionsBuilder searchOptions = BuildTestUtil.buildSearchOptions(processDefinitionId, 0, 5, ProcessInstanceSearchDescriptor.ID, Order.ASC); SearchResult result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(john.getId(), searchOptions.done()); assertNotNull(result); assertEquals(1, result.getCount()); assertEquals(john.getId(), result.getResult().get(0).getStartedBy()); assertEquals(matti.getId(), result.getResult().get(0).getStartedBySubstitute()); getProcessAPI().assignUserTask(step1Id, john.getId()); getProcessAPI().executeFlowNode(matti.getId(), step1Id); waitForProcessToFinish(processInstance); result = getProcessAPI().searchOpenProcessInstancesInvolvingUser(john.getId(), searchOptions.done()); assertNotNull(result); assertEquals(0, result.getCount()); final SearchResult result2 = getProcessAPI() .searchArchivedProcessInstancesInvolvingUser(john.getId(), searchOptions.done()); assertNotNull(result2); assertEquals(1, result2.getCount()); assertEquals(john.getId(), result2.getResult().get(0).getStartedBy()); assertEquals(matti.getId(), result2.getResult().get(0).getStartedBySubstitute()); // No need to verify anything, if no exception, query exists getProcessAPI().searchProcessInstances( new SearchOptionsBuilder(0, 10) .filter(ProcessInstanceSearchDescriptor.ASSIGNEE_ID, john.getId()) .done()); getProcessAPI().searchProcessInstances( new SearchOptionsBuilder(0, 10) .filter(ProcessInstanceSearchDescriptor.PROCESS_SUPERVISOR_USER_ID, john.getId()) .done()); getProcessAPI().searchProcessInstances( new SearchOptionsBuilder(0, 10) .filter(ProcessInstanceSearchDescriptor.PROCESS_SUPERVISOR_USER_ID, john.getId()) .filter(ProcessInstanceSearchDescriptor.ASSIGNEE_ID, john.getId()) .done()); } @Test public void searchArchivedProcessInstancesSupervisedBy() throws Exception { final ProcessInstance processInstance = processInstances.get(2); final List activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 10); for (final ActivityInstance activityInstance : activityInstances) { final long activityInstanceId = activityInstance.getId(); skipTask(activityInstanceId); } waitForProcessToFinish(processInstance); // test supervisor final SearchResult sapi = getProcessAPI() .searchArchivedProcessInstancesSupervisedBy(matti.getId(), new SearchOptionsBuilder(0, 10).done()); assertThat(sapi.getCount()).as("Archived Process Instance count").isEqualTo(1); final List archivedProcessInstances = sapi.getResult(); assertThat(archivedProcessInstances).extracting(ArchivedProcessInstance::getSourceObjectId) .as("source object ids of archived process instances") .containsOnly(processInstance.getId()); // test supervisor with assignee getProcessAPI().searchArchivedProcessInstancesSupervisedBy(matti.getId(), new SearchOptionsBuilder(0, 10).filter(ProcessInstanceSearchDescriptor.ASSIGNEE_ID, john.getId()) .done()); // No need to verify anything, if no exception, query exists final long processDefinitionId = processInstance.getProcessDefinitionId(); getProcessAPI().searchArchivedProcessInstances( new SearchOptionsBuilder(0, 10) .filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinitionId) .filter(ProcessInstanceSearchDescriptor.ASSIGNEE_ID, john.getId()) .done()); getProcessAPI().searchArchivedProcessInstances( new SearchOptionsBuilder(0, 10) .filter(ProcessInstanceSearchDescriptor.PROCESS_SUPERVISOR_USER_ID, john.getId()) .done()); getProcessAPI().searchArchivedProcessInstances( new SearchOptionsBuilder(0, 10) .filter(ProcessInstanceSearchDescriptor.PROCESS_SUPERVISOR_USER_ID, john.getId()) .filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinitionId) .filter(ProcessInstanceSearchDescriptor.ASSIGNEE_ID, john.getId()) .done()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/supervisor/SupervisorIT.java ================================================ /** * 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.supervisor; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.*; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.engine.TestWithTechnicalUser; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.After; import org.junit.Before; import org.junit.Test; public class SupervisorIT extends TestWithTechnicalUser { private List users; private List groups; private List roles; private List processDefinitions; private List supervisors; @Override @Before public void before() throws Exception { super.before(); createUsers(); createGroups(); createRoles(); createProcessDefinitions(); supervisors = new ArrayList<>(); createUserSupervisors(); createGroupSupervisors(); createRoleSupervisors(); createMembershipSupervisors(); } @Override @After public void after() throws Exception { deleteSupervisors(supervisors); disableAndDeleteProcess(processDefinitions); deleteUsers(users); deleteRoles(roles); deleteGroups(groups); super.after(); } private void createProcessDefinitions() throws Exception { processDefinitions = new ArrayList<>(); processDefinitions.add(createProcessDefinition("myProcess1")); processDefinitions.add(createProcessDefinition("myProcess2")); } private ProcessDefinition createProcessDefinition(final String processName) throws Exception { // test process definition with no supervisor final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance(processName, "1.0").done(); return deployAndEnableProcess(designProcessDefinition); } private void createUsers() throws BonitaException { users = new ArrayList<>(); users.add(getIdentityAPI().createUser(USERNAME, PASSWORD)); users.add(createUser("user2", "bpm", "FirstName2", "LastName2")); users.add(createUser("user3", "bpm", "FirstName3", "LastName3")); users.add(createUser("user4", "bpm", "FirstName4", "LastName4")); users.add(createUser("user5", "bpm", "FirstName5", "LastName5")); } private void createGroups() throws BonitaException { groups = new ArrayList<>(); groups.add(getIdentityAPI().createGroup("Engine", null)); groups.add(createGroup("group2", "level2")); } private void createRoles() throws BonitaException { roles = new ArrayList<>(); roles.add(getIdentityAPI().createRole("Developer")); roles.add(createRole("role2")); } private void createUserSupervisors() throws BonitaException { final ProcessDefinition processDefinition1 = processDefinitions.get(0); final ProcessDefinition processDefinition2 = processDefinitions.get(1); supervisors .add(getProcessAPI().createProcessSupervisorForUser(processDefinition1.getId(), users.get(0).getId())); supervisors .add(getProcessAPI().createProcessSupervisorForUser(processDefinition1.getId(), users.get(1).getId())); supervisors .add(getProcessAPI().createProcessSupervisorForUser(processDefinition1.getId(), users.get(2).getId())); supervisors .add(getProcessAPI().createProcessSupervisorForUser(processDefinition2.getId(), users.get(3).getId())); supervisors .add(getProcessAPI().createProcessSupervisorForUser(processDefinition2.getId(), users.get(4).getId())); } private void createGroupSupervisors() throws BonitaException { supervisors.add(getProcessAPI().createProcessSupervisorForGroup(processDefinitions.get(0).getId(), groups.get(0).getId())); supervisors.add(getProcessAPI().createProcessSupervisorForGroup(processDefinitions.get(1).getId(), groups.get(1).getId())); } private void createRoleSupervisors() throws BonitaException { supervisors.add(getProcessAPI().createProcessSupervisorForRole(processDefinitions.get(0).getId(), roles.get(0).getId())); supervisors.add(getProcessAPI().createProcessSupervisorForRole(processDefinitions.get(1).getId(), roles.get(1).getId())); } private void createMembershipSupervisors() throws BonitaException { final ProcessDefinition processDefinition1 = processDefinitions.get(0); final Role role1 = roles.get(0); final Role role2 = roles.get(1); final Group group1 = groups.get(0); final Group group2 = groups.get(1); supervisors.add(getProcessAPI().createProcessSupervisorForMembership(processDefinition1.getId(), group1.getId(), role1.getId())); supervisors.add(getProcessAPI().createProcessSupervisorForMembership(processDefinition1.getId(), group2.getId(), role2.getId())); supervisors.add(getProcessAPI().createProcessSupervisorForMembership(processDefinitions.get(1).getId(), group2.getId(), role1.getId())); } @Test public void isUserProcessSupervisor() throws Exception { final long processDefinitionId = processDefinitions.get(0).getId(); final User user = createUser("user546", "bpm", "FirstName564", "LastName2"); final long userId = user.getId(); ProcessSupervisor createdSupervisor = null; try { // before create supervisor assertFalse(getProcessAPI().isUserProcessSupervisor(processDefinitionId, userId)); // create supervisor createdSupervisor = getProcessAPI().createProcessSupervisorForUser(processDefinitionId, userId); // after created supervisor assertTrue(getProcessAPI().isUserProcessSupervisor(processDefinitionId, userId)); } finally { // clean-up if (createdSupervisor != null) { deleteSupervisor(createdSupervisor.getSupervisorId()); } deleteUser(user); } } @Test public void getAndDeleteSupervisor() throws BonitaException { final long userId = users.get(0).getId(); // Count to assert final SearchOptionsBuilder builder = buildSearchOptions(null, 7, 3, ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC); final SearchResult result = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(12, result.getCount()); final ProcessSupervisor getSupervisorResult = result.getResult().get(0); assertEquals(supervisors.get(0).getSupervisorId(), getSupervisorResult.getSupervisorId()); assertEquals(userId, getSupervisorResult.getUserId()); assertEquals(supervisors.get(0).getProcessDefinitionId(), getSupervisorResult.getProcessDefinitionId()); } @Test(expected = AlreadyExistsException.class) public void cantCreateTwiceSameUserSupervisor() throws BonitaException { final long processDefinitionId = processDefinitions.get(0).getId(); final long userId = users.get(0).getId(); // Add Supervisor final ProcessSupervisor createdSupervisor = getProcessAPI().createProcessSupervisorForUser(processDefinitionId, userId); try { getProcessAPI().createProcessSupervisorForUser(processDefinitionId, userId); } finally { // clean-up deleteSupervisor(createdSupervisor.getSupervisorId()); } } @Test(expected = AlreadyExistsException.class) public void cantCreateTwiceSameGroupSupervisor() throws BonitaException { final long processDefinitionId = processDefinitions.get(0).getId(); final long groupId = groups.get(0).getId(); // Add Supervisor final ProcessSupervisor createdSupervisor = getProcessAPI().createProcessSupervisorForGroup(processDefinitionId, groupId); try { getProcessAPI().createProcessSupervisorForGroup(processDefinitionId, groupId); } finally { // clean-up deleteSupervisor(createdSupervisor.getSupervisorId()); } } @Test(expected = AlreadyExistsException.class) public void cantCreateTwiceSameRoleSupervisor() throws BonitaException { final long processDefinitionId = processDefinitions.get(0).getId(); final long roleId = roles.get(0).getId(); // Add Supervisor final ProcessSupervisor createdSupervisor = getProcessAPI().createProcessSupervisorForRole(processDefinitionId, roleId); try { getProcessAPI().createProcessSupervisorForRole(processDefinitionId, roleId); } finally { // clean-up deleteSupervisor(createdSupervisor.getSupervisorId()); } } @Test(expected = AlreadyExistsException.class) public void cantCreateTwiceSameMembershipSupervisor() throws BonitaException { final long processDefinitionId = processDefinitions.get(0).getId(); final Group group1 = groups.get(0); final Role role1 = roles.get(0); // Add Supervisor final ProcessSupervisor createdSupervisor = getProcessAPI().createProcessSupervisorForMembership( processDefinitionId, group1.getId(), role1.getId()); try { getProcessAPI().createProcessSupervisorForMembership(processDefinitionId, group1.getId(), role1.getId()); } finally { // clean-up deleteSupervisor(createdSupervisor.getSupervisorId()); } } @Test public void deleteSupervisors() throws BonitaException { final ProcessDefinition processDefinition1 = processDefinitions.get(0); final Role role1 = roles.get(0); final Role role2 = roles.get(1); final Group group2 = groups.get(1); final long userId = users.get(0).getId(); // Delete supervisor using ids // Unexisted Supervisor try { getProcessAPI().deleteSupervisor(processDefinition1.getId(), userId, role2.getId(), group2.getId()); fail("no exception was thrown when deleting an unknown supervisor"); } catch (final DeletionException e) { } final SearchOptionsBuilder builder = buildSearchOptions(null, 0, 10, ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC); SearchResult result = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(12, result.getCount()); try { getProcessAPI().deleteSupervisor(null, null, role1.getId(), null); fail("no exception was thrown when deleting an unknown supervisor"); } catch (final DeletionException e) { } result = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(12, result.getCount()); // Existed Supervisor getProcessAPI().deleteSupervisor(processDefinition1.getId(), null, role2.getId(), group2.getId()); supervisors.remove(10); result = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(11, result.getCount()); getProcessAPI().deleteSupervisor(processDefinition1.getId(), userId, null, null); supervisors.remove(0); result = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(10, result.getCount()); getProcessAPI().deleteSupervisor(processDefinition1.getId(), null, role1.getId(), null); supervisors.remove(6); result = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(9, result.getCount()); } @Test public void deleteSupervisor_should_throw_SupervisorNotFoundException_when_supervisor_does_not_exist() throws BonitaException { final ProcessDefinition myProcess1 = processDefinitions.get(0); final Role role2 = roles.get(1); final Group group2 = groups.get(1); // Removed Supervisor getProcessAPI().deleteSupervisor(myProcess1.getId(), null, role2.getId(), group2.getId()); this.supervisors.remove(10); // Already deleted Supervisor assertThatThrownBy( () -> getProcessAPI().deleteSupervisor(myProcess1.getId(), null, role2.getId(), group2.getId())) .isInstanceOf(DeletionException.class) // Only check the name and not the cause, because in HTTP mode, the stacks are merged and the cause removed: .hasMessageContaining("SupervisorNotFoundException"); } @Test public void addGroupToSupervisor() throws Exception { final long processDefinitionId = processDefinitions.get(0).getId(); final Group group = getIdentityAPI().createGroup("Engine789489", null); // Add Supervisor ProcessSupervisor createdSupervisor = null; try { createdSupervisor = getProcessAPI().createProcessSupervisorForGroup(processDefinitionId, group.getId()); assertEquals(processDefinitionId, createdSupervisor.getProcessDefinitionId()); // Search supervisor final SearchOptionsBuilder builder = buildSearchOptions(null, 12, 1, ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC); final SearchResult result = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(13, result.getCount()); final ProcessSupervisor getSupervisorResult = result.getResult().get(0); assertEquals(createdSupervisor.getSupervisorId(), getSupervisorResult.getSupervisorId()); assertEquals(group.getId(), getSupervisorResult.getGroupId()); assertEquals(createdSupervisor.getProcessDefinitionId(), getSupervisorResult.getProcessDefinitionId()); } finally { // clean-up if (createdSupervisor != null) { deleteSupervisor(createdSupervisor.getSupervisorId()); } deleteGroups(group); } } @Test public void addMembershipToSupervisor() throws Exception { final long processDefinitionId = processDefinitions.get(0).getId(); final Role role = getIdentityAPI().createRole("Developer5646"); final Group group1 = groups.get(0); // Add Supervisor ProcessSupervisor createdSupervisor = null; try { createdSupervisor = getProcessAPI() .createProcessSupervisorForMembership(processDefinitionId, group1.getId(), role.getId()); assertEquals(processDefinitionId, createdSupervisor.getProcessDefinitionId()); // Search supervisor final SearchOptionsBuilder builder = buildSearchOptions(null, 12, 12, ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC); final SearchResult result = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(13, result.getCount()); final ProcessSupervisor getSupervisorResult = result.getResult().get(0); assertEquals(createdSupervisor.getSupervisorId(), getSupervisorResult.getSupervisorId()); assertEquals(group1.getId(), getSupervisorResult.getGroupId()); assertEquals(role.getId(), getSupervisorResult.getRoleId()); assertEquals(createdSupervisor.getProcessDefinitionId(), getSupervisorResult.getProcessDefinitionId()); // Check is user supervisor assertTrue(getProcessAPI().isUserProcessSupervisor(processDefinitionId, users.get(0).getId())); } finally { // clean-up if (createdSupervisor != null) { deleteSupervisor(createdSupervisor.getSupervisorId()); } deleteRoles(role); } } @Test public void searchProcessSupervisorsForUser() throws Exception { final ProcessSupervisor supervisor4 = supervisors.get(3); final ProcessSupervisor supervisor5 = supervisors.get(4); // test ASC SearchOptionsBuilder builder = buildSearchOptions(null, 7, 3, ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC); final SearchResult result1 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(12, result1.getCount()); List supervisorsResult = result1.getResult(); assertNotNull(supervisorsResult); assertEquals(3, supervisorsResult.size()); assertEquals(supervisors.get(0).getUserId(), supervisorsResult.get(0).getUserId()); assertEquals(supervisors.get(1).getUserId(), supervisorsResult.get(1).getUserId()); assertEquals(supervisors.get(2).getUserId(), supervisorsResult.get(2).getUserId()); builder = buildSearchOptions(null, 10, 2, ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC); final SearchResult result2 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(12, result2.getCount()); supervisorsResult = result2.getResult(); assertNotNull(supervisorsResult); assertEquals(2, supervisorsResult.size()); assertEquals(supervisor4.getUserId(), supervisorsResult.get(0).getUserId()); assertEquals(supervisor5.getUserId(), supervisorsResult.get(1).getUserId()); // test DESC builder = buildSearchOptions(null, 0, 2, ProcessSupervisorSearchDescriptor.USER_ID, Order.DESC); final SearchResult result4 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(12, result4.getCount()); supervisorsResult = result4.getResult(); assertNotNull(supervisorsResult); assertEquals(2, supervisorsResult.size()); assertEquals(supervisor5.getUserId(), supervisorsResult.get(0).getUserId()); assertEquals(supervisor4.getUserId(), supervisorsResult.get(1).getUserId()); } @Test public void searchProcessSupervisorsForUserWithFilter() throws Exception { // filter on process Map filters = Collections.singletonMap( ProcessSupervisorSearchDescriptor.PROCESS_DEFINITION_ID, (Serializable) processDefinitions.get(1).getId()); SearchOptionsBuilder builder = buildSearchOptions(filters, 3, 5, ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC); final SearchResult result1 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(5, result1.getCount()); List supervisorsResult = result1.getResult(); assertEquals(supervisors.get(3).getUserId(), supervisorsResult.get(0).getUserId()); assertEquals(supervisors.get(4).getUserId(), supervisorsResult.get(1).getUserId()); // filter on firstname filters = Collections.singletonMap(ProcessSupervisorSearchDescriptor.USER_ID, (Serializable) users.get(0).getId()); builder = buildSearchOptions(filters, 0, 3, ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC); final SearchResult result2 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(1, result2.getCount()); supervisorsResult = result2.getResult(); assertEquals(supervisors.get(0).getUserId(), supervisorsResult.get(0).getUserId()); } @Test public void searchProcessSupervisorsForGroup() throws Exception { final SearchOptionsBuilder builder = buildSearchOptions(null, 8, 2, ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC); final SearchResult result1 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(12, result1.getCount()); final List supervisors = result1.getResult(); assertEquals(groups.get(0).getId(), supervisors.get(0).getGroupId()); assertEquals(groups.get(1).getId(), supervisors.get(1).getGroupId()); } @Test public void searchProcessSupervisorsForGroupWithFilter() throws Exception { final Group group1 = groups.get(0); final Map filters = Collections.singletonMap(ProcessSupervisorSearchDescriptor.GROUP_ID, (Serializable) group1.getId()); final SearchOptionsBuilder builder = buildSearchOptions(filters, 0, 5, ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC); final SearchResult result1 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(2, result1.getCount()); final List supervisors = result1.getResult(); assertEquals(group1.getId(), supervisors.get(0).getGroupId()); } @Test public void searchProcessSupervisorsForRole() throws Exception { final SearchOptionsBuilder builder = buildSearchOptions(null, 9, 11, ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC); final SearchResult result1 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(12, result1.getCount()); final List supervisors = result1.getResult(); assertEquals(roles.get(0).getId(), supervisors.get(0).getRoleId()); assertEquals(roles.get(1).getId(), supervisors.get(1).getRoleId()); } @Test public void searchProcessSupervisorsForRoleWithFilter() throws Exception { final Role role1 = roles.get(0); final Map filters = Collections.singletonMap(ProcessSupervisorSearchDescriptor.ROLE_ID, (Serializable) role1.getId()); final SearchOptionsBuilder builder = buildSearchOptions(filters, 0, 5, ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC); final SearchResult result1 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(3, result1.getCount()); final List supervisors = result1.getResult(); assertEquals(role1.getId(), supervisors.get(0).getRoleId()); } @Test public void searchProcessSupervisorsForRoleAndGroup() throws Exception { final Role role1 = roles.get(0); final Role role2 = roles.get(1); final Group group1 = groups.get(0); final Group group2 = groups.get(1); final SearchOptionsBuilder builder = buildSearchOptions(null, 8, 4, ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC); builder.sort(ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC); final SearchResult result1 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(12, result1.getCount()); final List supervisorsResult = result1.getResult(); assertEquals(role1.getId(), supervisorsResult.get(0).getRoleId()); assertEquals(group1.getId(), supervisorsResult.get(0).getGroupId()); assertEquals(role1.getId(), supervisorsResult.get(1).getRoleId()); assertEquals(group2.getId(), supervisorsResult.get(1).getGroupId()); assertEquals(role2.getId(), supervisorsResult.get(3).getRoleId()); assertEquals(group2.getId(), supervisorsResult.get(3).getGroupId()); } @Test public void searchProcessSupervisorsForRoleAndGroupWithFilter() throws Exception { final Map filters = Collections.singletonMap(ProcessSupervisorSearchDescriptor.ROLE_ID, (Serializable) roles.get(0).getId()); final SearchOptionsBuilder builder = buildSearchOptions(filters, 1, 5, ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC); builder.sort(ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC); final SearchResult result1 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(3, result1.getCount()); final List supervisorsResult = result1.getResult(); assertEquals(supervisors.get(9).getSupervisorId(), supervisorsResult.get(0).getSupervisorId()); assertEquals(supervisors.get(11).getSupervisorId(), supervisorsResult.get(1).getSupervisorId()); } @Test public void searchProcessSupervisorsForUserAndRole() throws Exception { final Role role1 = roles.get(0); final SearchOptionsBuilder builder = buildSearchOptions(null, 3, 6, ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC); builder.sort(ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC); final SearchResult result1 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(12, result1.getCount()); final List supervisors = result1.getResult(); assertEquals(role1.getId(), supervisors.get(0).getRoleId()); assertEquals(role1.getId(), supervisors.get(1).getRoleId()); assertEquals(roles.get(1).getId(), supervisors.get(2).getRoleId()); assertEquals(users.get(0).getId(), supervisors.get(4).getUserId()); assertEquals(users.get(1).getId(), supervisors.get(5).getUserId()); } @Test public void searchProcessSupervisorsForUserAndRoleWithFilter() throws Exception { final Role role1 = roles.get(0); final Map filters = Collections.singletonMap(ProcessSupervisorSearchDescriptor.ROLE_ID, (Serializable) role1.getId()); final SearchOptionsBuilder builder = buildSearchOptions(filters, 0, 5, ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC); builder.sort(ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC); final SearchResult result1 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(3, result1.getCount()); final List supervisors = result1.getResult(); assertEquals(role1.getId(), supervisors.get(0).getRoleId()); assertEquals(role1.getId(), supervisors.get(1).getRoleId()); } @Test public void searchProcessSupervisorsForUserAndGroup() throws Exception { final Group group2 = groups.get(1); final SearchOptionsBuilder builder = buildSearchOptions(null, 3, 8, ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC); builder.sort(ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC); final SearchResult result1 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(12, result1.getCount()); final List supervisors = result1.getResult(); assertEquals(groups.get(0).getId(), supervisors.get(0).getGroupId()); assertEquals(group2.getId(), supervisors.get(1).getGroupId()); assertEquals(group2.getId(), supervisors.get(2).getGroupId()); assertEquals(group2.getId(), supervisors.get(3).getGroupId()); assertEquals(users.get(0).getId(), supervisors.get(4).getUserId()); assertEquals(users.get(1).getId(), supervisors.get(5).getUserId()); } @Test public void searchProcessSupervisorsForUserAndGroupWithFilter() throws Exception { final Map filters = Collections.singletonMap(ProcessSupervisorSearchDescriptor.USER_ID, (Serializable) users.get(0).getId()); final SearchOptionsBuilder builder = buildSearchOptions(filters, 0, 5, ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC); builder.sort(ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC); final SearchResult result1 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(1, result1.getCount()); final List supervisors = result1.getResult(); assertEquals(supervisors.get(0).getSupervisorId(), supervisors.get(0).getSupervisorId()); } @Test public void searchProcessSupervisorsForUserAndGroupAndRole() throws Exception { final Role role1 = roles.get(0); final Role role2 = roles.get(1); final Group group1 = groups.get(0); final Group group2 = groups.get(1); final SearchOptionsBuilder builder = buildSearchOptions(null, 3, 6, ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC); builder.sort(ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC); builder.sort(ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC); final SearchResult result1 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(12, result1.getCount()); final List supervisors = result1.getResult(); assertEquals(group1.getId(), supervisors.get(0).getGroupId()); assertEquals(role1.getId(), supervisors.get(0).getRoleId()); assertEquals(group2.getId(), supervisors.get(2).getGroupId()); assertEquals(role1.getId(), supervisors.get(2).getRoleId()); assertEquals(group2.getId(), supervisors.get(3).getGroupId()); assertEquals(role2.getId(), supervisors.get(3).getRoleId()); assertEquals(users.get(0).getId(), supervisors.get(4).getUserId()); assertEquals(users.get(1).getId(), supervisors.get(5).getUserId()); } @Test public void searchProcessSupervisorsForUserAndGroupAndRoleWithFilter() throws Exception { final Map filters = Collections.singletonMap(ProcessSupervisorSearchDescriptor.USER_ID, (Serializable) users.get(0).getId()); final SearchOptionsBuilder builder = buildSearchOptions(filters, 0, 5, ProcessSupervisorSearchDescriptor.USER_ID, Order.ASC); builder.sort(ProcessSupervisorSearchDescriptor.GROUP_ID, Order.ASC); builder.sort(ProcessSupervisorSearchDescriptor.ROLE_ID, Order.ASC); final SearchResult result1 = getProcessAPI().searchProcessSupervisors(builder.done()); assertEquals(1, result1.getCount()); final List supervisors = result1.getResult(); assertEquals(supervisors.get(0).getSupervisorId(), supervisors.get(0).getSupervisorId()); } private SearchOptionsBuilder buildSearchOptions(final Map filters, final int pageIndex, final int numberOfResults, final String orderByField, final Order order) { final SearchOptionsBuilder builder = new SearchOptionsBuilder(pageIndex, numberOfResults); if (filters != null) { for (final Entry filter : filters.entrySet()) { builder.filter(filter.getKey(), filter.getValue()); } } builder.sort(orderByField, order); return builder; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/tenant/TenantMaintenanceIT.java ================================================ /** * 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.tenant; import static org.assertj.core.api.Assertions.fail; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.platform.LoginException; import org.junit.Test; /** * @author Laurent Leseigneur * @author Celine Souchet */ public class TenantMaintenanceIT extends TestWithUser { @Test public void should_be_able_to_start_process_after_pause_resume() throws Exception { //given final ProcessDefinition processDefinition = createProcessOnTenant(); //when logoutThenlogin(); pauseTenant(); //then assertCanLoginOnTenant(); //when loginWithTechnicalUser(); resumeTenant(); // then assertCanLoginOnTenantAndStartProcess(processDefinition); // cleanup loginWithTechnicalUser(); disableAndDeleteProcess(processDefinition.getId()); } private void assertCanLoginOnTenant() throws Exception { try { loginOnDefaultTenantWith(USERNAME, PASSWORD); logout(); } catch (LoginException e) { fail("Expected that user is able to log in, but is not"); } } private void assertCanLoginOnTenantAndStartProcess(final ProcessDefinition processDefinition) throws Exception { loginOnDefaultTenantWith(USERNAME, PASSWORD); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step1"); logout(); } private ProcessDefinition createProcessOnTenant() throws Exception { final String processName = new StringBuilder().append(PROCESS_NAME).toString(); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance(processName, PROCESS_VERSION) .addActor(ACTOR_NAME) .addStartEvent("start event") .addUserTask("step1", ACTOR_NAME) .addEndEvent("end event").getProcess(); return deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, getSession().getUserId()); } private void pauseTenant() throws BonitaException { getTenantAdministrationAPI().pause(); } private void resumeTenant() throws BonitaException { getTenantAdministrationAPI().resume(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/resources/application-to-test-permissions.xml ================================================ Bonita Editable Application bonita-application-directory.png Some page ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg| %X%n ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/resources/org/bonitasoft/engine/application/superAdminApp.xml ================================================ Bonita Super Administrator Application bonita-super-admin-application.png ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/resources/org/bonitasoft/engine/application/testApp.xml ================================================ Test App bonita-admin-application.png ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/resources/org/bonitasoft/engine/connectors/TestConnector.xml ================================================ 1.0 org.bonitasoft.connector.testConnector xxx.png other org/bonitasoft/engine/connectors/TestConnector.png input1 ================================================ FILE: bonita-integration-tests/bonita-integration-tests-client/src/test/resources/org/bonitasoft/engine/connectors/TestConnector_Implementation.xml ================================================ TestConnector.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/build.gradle ================================================ plugins { id("bonita-docker-database") id("bonita-tests") } dependencies { api libs.assertj api libs.mockitoCore api project(':bonita-integration-tests:bonita-integration-tests-client') implementation(project(":bpm:bonita-core:bonita-process-engine")) api project(':bpm:bonita-server') api libs.commonsIO testRuntimeOnly libs.logback testAnnotationProcessor libs.lombok testImplementation libs.lombok testImplementation libs.awaitility testImplementation libs.springTest testImplementation libs.systemLambda } tasks.register("testsJar", Jar) { archiveClassifier = 'tests' from(sourceSets.test.output) } group = 'org.bonitasoft.engine.test' publishing { publications { mavenJava(MavenPublication) { from project.components.java artifact testsJar } } } databaseIntegrationTest { include "**/*IT.class" } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/main/java/org/bonitasoft/engine/bpm/CommonBPMServicesTest.java ================================================ /** * 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.bpm; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.PrintTestsStatusRule; import org.bonitasoft.engine.actor.mapping.SActorCreationException; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.api.impl.IdentityAPIImpl; import org.bonitasoft.engine.api.impl.LoginAPIImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.FlowElementContainerDefinitionImpl; import org.bonitasoft.engine.bpm.process.impl.internal.DesignProcessDefinitionImpl; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.exception.SDeletingEnabledProcessException; 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.exception.SProcessDeletionException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceHierarchicalDeletionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; 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.SUserTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.SIdentityException; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.test.junit.BonitaEngineRule; import org.bonitasoft.engine.test.util.TestUtil; import org.bonitasoft.engine.transaction.STransactionCommitException; import org.bonitasoft.engine.transaction.STransactionCreationException; import org.bonitasoft.engine.transaction.STransactionException; import org.bonitasoft.engine.transaction.STransactionRollbackException; import org.bonitasoft.engine.transaction.TransactionService; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.rules.TestRule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class CommonBPMServicesTest { @Rule public BonitaEngineRule bonitaEngineRule = BonitaEngineRule.create(); private final static Logger LOGGER = LoggerFactory.getLogger(CommonBPMServicesTest.class); protected static SessionAccessor sessionAccessor; protected static ServiceAccessor serviceAccessor; @Rule public TestRule testWatcher = new PrintTestsStatusRule(LOGGER) { @Override public void clean() throws Exception { CommonBPMServicesTest.this.clean(); } }; private APISession apiSession = null; protected ServiceAccessorFactory getServiceAccessorFactory() { return ServiceAccessorFactory.getInstance(); } APISession getAPISession() { return this.apiSession; } protected SessionAccessor getSessionAccessor() { return sessionAccessor; } protected ServiceAccessor getServiceAccessor() { if (serviceAccessor == null) { try { serviceAccessor = getServiceAccessorFactory().createServiceAccessor(); } catch (Exception e) { throw new RuntimeException(e); } } return serviceAccessor; } protected TransactionService getTransactionService() { return getServiceAccessor().getTransactionService(); } @Before public void commonSetup() throws Exception { apiSession = new LoginAPIImpl().login(TestUtil.getDefaultUserName(), TestUtil.getDefaultPassword()); if (sessionAccessor == null) { sessionAccessor = getServiceAccessorFactory().createSessionAccessor(); } if (serviceAccessor == null) { serviceAccessor = getServiceAccessorFactory().createServiceAccessor(); } apiSession = new LoginAPIImpl().login(TestUtil.getDefaultUserName(), TestUtil.getDefaultPassword()); sessionAccessor.setSessionId(apiSession.getId()); } protected Group createGroup(final String groupName) throws CreationException { return createGroup(groupName, null); } protected Group createGroup(final String groupName, final String groupPath) throws CreationException { try { openTx(); final Group group = new IdentityAPIImpl().createGroup(groupName, groupPath); closeTx(); return group; } catch (final STransactionException e) { throw new CreationException(e); } } @After public void after() throws Exception { TestUtil.closeTransactionIfOpen(serviceAccessor.getTransactionService()); if (apiSession != null) { new LoginAPIImpl().logout(apiSession); } } private void clean() throws Exception { try { final APISession apiSession = new LoginAPIImpl().login(TestUtil.getDefaultUserName(), TestUtil.getDefaultPassword()); openTx(); cleanProcessesDefinitions(); cleanProcessInstance(); cleanMemberships(); cleaRoles(); cleanUsers(); cleanGroups(); getServiceAccessor().getLoginService().logout(apiSession.getId()); } finally { if (serviceAccessor.getTransactionService().isTransactionActive()) { closeTx(); } } } private void cleanProcessesDefinitions() throws SBonitaReadException, SProcessDefinitionNotFoundException, SProcessDeletionException, SDeletingEnabledProcessException { final QueryOptions queryOptions = new QueryOptions(0, 200, SProcessDefinitionDeployInfo.class, "name", OrderByType.ASC); final List processes = getServiceAccessor().getProcessDefinitionService() .getProcessDeploymentInfos(queryOptions); if (processes.size() > 0) { for (final SProcessDefinitionDeployInfo process : processes) { try { getServiceAccessor().getProcessDefinitionService() .disableProcessDeploymentInfo(process.getProcessId()); } catch (final Throwable ignored) { } getServiceAccessor().getProcessDefinitionService().delete(process.getProcessId()); } } } private void cleanProcessInstance() throws SBonitaReadException, SProcessInstanceNotFoundException, SProcessInstanceReadException, SFlowNodeReadException, SProcessInstanceModificationException, SProcessInstanceHierarchicalDeletionException { // let's clean up All Process Instances: List processInstances = getFirstProcessInstances(5000); while (processInstances.size() > 0) { for (final SProcessInstance sProcessInstance : processInstances) { getServiceAccessor().getProcessInstanceService().deleteProcessInstance(sProcessInstance.getId()); } // get the next 100: processInstances = getFirstProcessInstances(5000); } } private void cleanMemberships() throws SIdentityException { for (final SUserMembership sMembership : getServiceAccessor().getIdentityService().getUserMemberships(0, 5000)) { getServiceAccessor().getIdentityService().deleteUserMembership(sMembership); } } private void cleaRoles() throws SIdentityException { for (final SRole sRole : getServiceAccessor().getIdentityService().getRoles(0, 5000)) { getServiceAccessor().getIdentityService().deleteRole(sRole); } } private void cleanUsers() throws SIdentityException { for (final SUser sUser : getServiceAccessor().getIdentityService().getUsers(0, 5000)) { getServiceAccessor().getIdentityService().deleteUser(sUser); } } private void cleanGroups() throws SIdentityException { for (final SGroup sGroup : getServiceAccessor().getIdentityService().getGroups(0, 5000)) { getServiceAccessor().getIdentityService().deleteGroup(sGroup); } } public List getFirstProcessInstances(final int nb) throws SBonitaReadException { // we are already in a transaction context here: final OrderByOption orderByOption = new OrderByOption(SProcessInstance.class, SProcessInstance.LAST_UPDATE_KEY, OrderByType.DESC); final QueryOptions queryOptions = new QueryOptions(0, nb, Collections.singletonList(orderByOption), Collections.emptyList(), null); return getServiceAccessor().getProcessInstanceService().searchProcessInstances(queryOptions); } public List getFirstArchivedProcessInstances(final int nb) throws SBonitaReadException { // we are already in a transaction context here: final OrderByOption orderByOption = new OrderByOption(SAProcessInstance.class, SProcessInstance.LAST_UPDATE_KEY, OrderByType.DESC); final QueryOptions queryOptions = new QueryOptions(0, nb, Collections.singletonList(orderByOption), Collections.emptyList(), null); return getServiceAccessor().getProcessInstanceService().searchArchivedProcessInstances(queryOptions); } protected SProcessInstance createSProcessInstance() throws SBonitaException { final SProcessInstance processInstance = SProcessInstance.builder().name("process").processDefinitionId(1) .build(); openTx(); getServiceAccessor().getProcessInstanceService().createProcessInstance(processInstance); closeTx(); return processInstance; } protected void deleteSProcessInstance(final SProcessInstance processInstance) throws SBonitaException { openTx(); getServiceAccessor().getProcessInstanceService().deleteProcessInstance(processInstance.getId()); closeTx(); } protected SFlowNodeInstance getFlowNodeInstance(final long flowNodeInstanceId) throws SBonitaException { SFlowNodeInstance flowNodeInstance = null; openTx(); try { flowNodeInstance = getServiceAccessor().getActivityInstanceService() .getFlowNodeInstance(flowNodeInstanceId); } finally { closeTx(); } return flowNodeInstance; } protected SEventInstance createSStartEventInstance(final String eventName, final long flowNodeDefinitionId, final long rootProcessInstanceId, final long processDefinitionId, final long parentProcessInstanceId) throws STransactionCreationException, SEventInstanceCreationException, STransactionCommitException, STransactionRollbackException, SMessageInstanceCreationException { final SEventInstance eventInstance = BuilderFactory .get(SStartEventInstanceBuilderFactory.class) .createNewStartEventInstance(eventName, flowNodeDefinitionId, rootProcessInstanceId, rootProcessInstanceId, processDefinitionId, rootProcessInstanceId, parentProcessInstanceId) .done(); createSEventInstance(eventInstance); return eventInstance; } protected SEventInstance createSEndEventInstance(final String eventName, final long flowNodeDefinitionId, final long rootProcessInstanceId, final long processDefinitionId, final long parentProcessInstanceId) throws STransactionCreationException, SEventInstanceCreationException, STransactionCommitException, STransactionRollbackException, SMessageInstanceCreationException { final SEventInstance eventInstance = BuilderFactory .get(SEndEventInstanceBuilderFactory.class) .createNewEndEventInstance(eventName, flowNodeDefinitionId, rootProcessInstanceId, rootProcessInstanceId, processDefinitionId, rootProcessInstanceId, parentProcessInstanceId) .done(); createSEventInstance(eventInstance); return eventInstance; } protected SEventInstance createSIntermediateCatchEventInstance(final String eventName, final long flowNodeDefinitionId, final long rootProcessInstanceId, final long processDefinitionId, final long parentProcessInstanceId) throws STransactionCreationException, SEventInstanceCreationException, STransactionCommitException, STransactionRollbackException, SMessageInstanceCreationException { final SEventInstance eventInstance = BuilderFactory .get(SIntermediateCatchEventInstanceBuilderFactory.class) .createNewIntermediateCatchEventInstance(eventName, flowNodeDefinitionId, rootProcessInstanceId, parentProcessInstanceId, processDefinitionId, rootProcessInstanceId, parentProcessInstanceId) .done(); createSEventInstance(eventInstance); return eventInstance; } protected SEventInstance createSIntermediateThrowEventInstance(final String eventName, final long flowNodeDefinitionId, final long processInstanceId, final long processDefinitionId, final long parentProcessInstanceId) throws STransactionCreationException, SEventInstanceCreationException, STransactionCommitException, STransactionRollbackException, SMessageInstanceCreationException { final SEventInstance eventInstance = BuilderFactory .get(SIntermediateThrowEventInstanceBuilderFactory.class) .createNewIntermediateThrowEventInstance(eventName, flowNodeDefinitionId, processInstanceId, processInstanceId, processDefinitionId, processInstanceId, parentProcessInstanceId) .done(); createSEventInstance(eventInstance); return eventInstance; } protected SEventInstance createIntermediateThrowEventInstance(final String eventName, final long flowNodeDefinitionId, final long processInstanceId, final long processDefinitionId, final long parentProcessInstanceId) throws STransactionCreationException, SEventInstanceCreationException, STransactionCommitException, STransactionRollbackException, SMessageInstanceCreationException { final SEventInstance eventInstance = BuilderFactory .get(SIntermediateThrowEventInstanceBuilderFactory.class) .createNewIntermediateThrowEventInstance(eventName, flowNodeDefinitionId, processInstanceId, processInstanceId, processDefinitionId, processInstanceId, parentProcessInstanceId) .done(); createSEventInstance(eventInstance); return eventInstance; } protected void createSEventInstance(final SEventInstance eventInstance) throws STransactionCreationException, SEventInstanceCreationException, STransactionCommitException, STransactionRollbackException, SMessageInstanceCreationException { openTx(); getServiceAccessor().getEventInstanceService().createEventInstance(eventInstance); closeTx(); } protected void insertGatewayInstance(final SGatewayInstance gatewayInstance) throws SBonitaException { openTx(); getServiceAccessor().getGatewayInstanceService().createGatewayInstance(gatewayInstance); closeTx(); } protected SUserTaskInstance createSUserTaskInstance(final String name, final long flowNodeDefinitionId, final long parentId, final long processDefinitionId, final long rootProcessInst, final long actorId) throws SBonitaException { final SUserTaskInstance taskInstance = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class) .createNewUserTaskInstance(name, flowNodeDefinitionId, rootProcessInst, parentId, actorId, processDefinitionId, rootProcessInst, parentId) .done(); openTx(); getServiceAccessor().getActivityInstanceService().createActivityInstance(taskInstance); closeTx(); return taskInstance; } protected SActivityInstance createSAutomaticTaskInstance(final String name, final long flowNodeDefinitionId, final long parentId, final long processDefinitionId, final long rootProcessInst) throws SBonitaException { final SActivityInstance taskInstance = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class) .createNewAutomaticTaskInstance(name, flowNodeDefinitionId, rootProcessInst, parentId, processDefinitionId, rootProcessInst, parentId) .done(); openTx(); getServiceAccessor().getActivityInstanceService().createActivityInstance(taskInstance); closeTx(); return taskInstance; } protected SProcessDefinition buildSProcessDefinition(final String name, final String version) throws SProcessDefinitionException { final DesignProcessDefinitionImpl designProcessDefinition = new DesignProcessDefinitionImpl(name, version); designProcessDefinition.setProcessContainer(new FlowElementContainerDefinitionImpl()); return getServiceAccessor().getProcessDefinitionService().store(designProcessDefinition); } private SActor buildSActor(final String name, final long scopeId, final boolean initiator) throws SActorCreationException { final SActor sActor = SActor.builder().name(name).scopeId(scopeId).initiator(initiator).build(); return getServiceAccessor().getActorMappingService().addActor(sActor); } public SProcessDefinition createSProcessDefinitionWithSActor(final String name, final String version, final String actorName, final boolean actorIsInitiator, final List sUsersToAddToActor) throws SBonitaException { openTx(); final SProcessDefinition sProcessDefinition = buildSProcessDefinition(name, version); getServiceAccessor().getProcessDefinitionService().resolveProcess(sProcessDefinition.getId()); getServiceAccessor().getProcessDefinitionService().enableProcessDeploymentInfo(sProcessDefinition.getId()); final SActor sActor = buildSActor(actorName, sProcessDefinition.getId(), actorIsInitiator); for (final SUser sUser : sUsersToAddToActor) { getServiceAccessor().getActorMappingService().addUserToActor(sActor.getId(), sUser.getId()); } closeTx(); return sProcessDefinition; } public SProcessDefinition createSProcessDefinition(final String name, final String version) throws SBonitaException { openTx(); final SProcessDefinition sProcessDefinition = buildSProcessDefinition(name, version); getServiceAccessor().getProcessDefinitionService().resolveProcess(sProcessDefinition.getId()); getServiceAccessor().getProcessDefinitionService().enableProcessDeploymentInfo(sProcessDefinition.getId()); closeTx(); return sProcessDefinition; } public List createSProcessDefinitions(final int count, final String name, final String version) throws SBonitaException { final List processDefinitons = new ArrayList<>(); openTx(); for (int i = 1; i <= count; i++) { final SProcessDefinition sProcessDefinition = buildSProcessDefinition(name + i, version + i); getServiceAccessor().getProcessDefinitionService().resolveProcess(sProcessDefinition.getId()); getServiceAccessor().getProcessDefinitionService().enableProcessDeploymentInfo(sProcessDefinition.getId()); processDefinitons.add(sProcessDefinition); } closeTx(); return processDefinitons; } public void deleteSProcessDefinition(final SProcessDefinition sProcessDefinition) throws SBonitaException { deleteSProcessDefinition(sProcessDefinition.getId()); } public void deleteSProcessDefinition(final long sProcessDefinitionId) throws SBonitaException { openTx(); getServiceAccessor().getActorMappingService().deleteActors(sProcessDefinitionId); getServiceAccessor().getProcessDefinitionService().disableProcessDeploymentInfo(sProcessDefinitionId); getServiceAccessor().getProcessDefinitionService().delete(sProcessDefinitionId); closeTx(); } void openTx() throws STransactionCreationException { serviceAccessor.getTransactionService().begin(); } public void deleteSProcessDefinitions(final SProcessDefinition... sProcessDefinitions) throws SBonitaException { if (sProcessDefinitions != null) { deleteSProcessDefinitions(Arrays.asList(sProcessDefinitions)); } } public void deleteSProcessDefinitions(final List sProcessDefinitions) throws SBonitaException { openTx(); for (final SProcessDefinition sProcessDefinition : sProcessDefinitions) { getServiceAccessor().getActorMappingService().deleteActors(sProcessDefinition.getId()); getServiceAccessor().getProcessDefinitionService().disableProcessDeploymentInfo(sProcessDefinition.getId()); getServiceAccessor().getProcessDefinitionService().delete(sProcessDefinition.getId()); } closeTx(); } public List createEnabledSUsers(final int count, final String firstName, final String lastName, final String password) throws SBonitaException { openTx(); final List users = new ArrayList<>(); for (int i = 1; i <= count; i++) { users.add(getServiceAccessor().getIdentityService() .createUser(buildEnabledSUser(firstName + i, lastName + i, password + i, 0))); } closeTx(); return users; } public SUser createEnabledSUser(final String firstName, final String lastName, final String password) throws SBonitaException { return createEnabledSUser(firstName, lastName, password, 0); } public SUser createEnabledSUser(final String firstName, final String lastName, final String password, final long managerUserId) throws SBonitaException { openTx(); final SUser user = getServiceAccessor().getIdentityService() .createUser(buildEnabledSUser(firstName, lastName, password, managerUserId)); closeTx(); return user; } public SUser buildEnabledSUser(final String firstName, final String lastName, final String password, final long managerUserId) { final SUser.SUserBuilder userBuilder = SUser.builder(); userBuilder.createdBy(2); userBuilder.creationDate(6); userBuilder.enabled(true); userBuilder.firstName(firstName); userBuilder.jobTitle("jobTitle"); userBuilder.lastName(lastName); userBuilder.lastUpdate(4L); userBuilder.managerUserId(managerUserId); userBuilder.password(password); userBuilder.title("title"); userBuilder.userName(firstName); return userBuilder.build(); } public SUser createSUser(final String username, final String password) throws SBonitaException { openTx(); final SUser.SUserBuilder userBuilder = SUser.builder().userName(username).password(password); final SUser user = getServiceAccessor().getIdentityService().createUser(userBuilder.build()); closeTx(); return user; } public void deleteSUser(final SUser sUser) throws SBonitaException { openTx(); getServiceAccessor().getIdentityService().deleteUser(sUser); closeTx(); } public void deleteSUsers(final SUser... users) throws SBonitaException { if (users != null) { deleteSUsers(Arrays.asList(users)); } } public void deleteSUsers(final List users) throws SBonitaException { openTx(); for (final SUser sUser : users) { getServiceAccessor().getIdentityService().deleteUser(sUser); } closeTx(); } public void deleteSGroup(final SGroup sGroup) throws SBonitaException { openTx(); getServiceAccessor().getIdentityService().deleteGroup(sGroup); closeTx(); } public void deleteSGroups(final SGroup... groups) throws SBonitaException { if (groups != null) { openTx(); for (final SGroup sGroup : groups) { getServiceAccessor().getIdentityService().deleteGroup(sGroup); } closeTx(); } } public void deleteSRole(final SRole sRole) throws SBonitaException { openTx(); getServiceAccessor().getIdentityService().deleteRole(sRole); closeTx(); } public void deleteSRoles(final SRole... roles) throws SBonitaException { if (roles != null) { openTx(); for (final SRole sRole : roles) { getServiceAccessor().getIdentityService().deleteRole(sRole); } closeTx(); } } public SRole createSRole(final String roleName) throws SBonitaException { openTx(); final SRole role = SRole.builder().name(roleName).build(); getServiceAccessor().getIdentityService().createRole(role, null, null); closeTx(); return role; } public SUserMembership createSUserMembership(final SUser user, final SGroup group, final SRole role) throws SBonitaException { openTx(); final SUserMembership userMembership = SUserMembership.builder().userId(user.getId()).groupId(group.getId()) .roleId(role.getId()).build(); getServiceAccessor().getIdentityService().createUserMembership(userMembership); closeTx(); return userMembership; } protected List searchFlowNodeInstances(final QueryOptions searchOptions) throws SBonitaException { openTx(); final List flowNodes = getServiceAccessor().getActivityInstanceService() .searchFlowNodeInstances(SFlowNodeInstance.class, searchOptions); closeTx(); return flowNodes; } private void closeTx() throws STransactionCommitException, STransactionRollbackException { serviceAccessor.getTransactionService().complete(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/main/java/org/bonitasoft/engine/test/util/TestUtil.java ================================================ /** * 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.util; import java.util.concurrent.Callable; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; import org.bonitasoft.engine.transaction.STransactionCommitException; import org.bonitasoft.engine.transaction.STransactionException; import org.bonitasoft.engine.transaction.STransactionRollbackException; import org.bonitasoft.engine.transaction.TransactionService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Baptiste Mesta, Yanyan Liu */ public class TestUtil { private static final Logger LOGGER = LoggerFactory.getLogger(TestUtil.class); private static final String DEFAULT_USER_NAME = "install"; private static final String DEFAULT_PASSWORD = "install"; public static String getDefaultUserName() { return DEFAULT_USER_NAME; } public static String getDefaultPassword() { return DEFAULT_PASSWORD; } public static void startScheduler(final SchedulerService scheduler) throws Exception { if (!scheduler.isStarted()) { scheduler.start(); } } // Always call the startScheduler method after calling this one public static void stopScheduler(final SchedulerService scheduler, final TransactionService txService) throws Exception { if (scheduler.isStarted() && !scheduler.isStopped()) { try { txService.executeInTransaction((Callable) () -> { try { scheduler.deleteJobs(); } catch (SSchedulerException e) { if (!scheduler.getJobs().isEmpty()) { LOGGER.error("There are still some jobs not deleted!"); throw e; } } return null; }); } catch (final STransactionException txException) { throw new SSchedulerException(txException); } scheduler.stop(); } } public static void closeTransactionIfOpen(final TransactionService txService) { try { txService.complete(); } catch (STransactionCommitException | STransactionRollbackException e) { LOGGER.debug("Cannot complete the transaction. Probably already completed. Ignoring."); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/main/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/CallableWithException.java ================================================ /** * 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; /** * @author Baptiste Mesta */ public interface CallableWithException { T call() throws Exception; } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/DeleteEventTriggerInstanceIT.java ================================================ /** * 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; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.actorMapping.Actor; import org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping; import org.bonitasoft.engine.bpm.flownode.TimerType; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.junit.Before; import org.junit.Test; public class DeleteEventTriggerInstanceIT extends TestWithUser { @Before public void cleanTriggers() throws Exception { SQLUtils.execute("DELETE FROM event_trigger_instance"); } @Test public void should_delete_timer_event_trigger_on_interrupted_process() throws Exception { ProcessDefinition procWithTimers = getProcessAPI() .deployAndEnableProcess(new ProcessDefinitionBuilder().createNewInstance("ProcWithTimers", "1.0") .addStartEvent("start") .addIntermediateCatchEvent("intermediate") .addTimerEventTriggerDefinition(TimerType.DURATION, constant(100000)) .addTransition("start", "intermediate") .getProcess()); ProcessInstance processInstance = getProcessAPI().startProcess(procWithTimers.getId()); await().until(() -> !getProcessAPI() .searchFlowNodeInstances(new SearchOptionsBuilder(0, 1).filter("name", "intermediate").done()) .getResult().isEmpty()); getProcessAPI().deleteProcessInstance(processInstance.getId()); disableAndDeleteProcess(procWithTimers); assertThat(((Number) query("SELECT count(*) FROM event_trigger_instance").get(0)).intValue()).isEqualTo(0); } @Test public void should_delete_timer_event_of_aborted_boundary_event() throws Exception { ActorMapping actorMapping = new ActorMapping(); Actor actor = new Actor("actor"); actorMapping.addActor(actor); actor.addUser(user.getUserName()); ProcessDefinition procWithTimers = getProcessAPI().deployAndEnableProcess( new BusinessArchiveBuilder().createNewBusinessArchive().setProcessDefinition( new ProcessDefinitionBuilder().createNewInstance("ProcWithBoundaryTimers", "1.0") .addActor("actor") .addUserTask("myTask", "actor").addBoundaryEvent("timerBoundary", true) .addTimerEventTriggerDefinition(TimerType.DURATION, constant(100000)) .addUserTask("afterBoundary", "actor") .addUserTask("boundaryOut", "actor") .addTransition("timerBoundary", "boundaryOut") .getProcess()) .setActorMapping(actorMapping) .done()); ProcessInstance processInstance = getProcessAPI().startProcess(procWithTimers.getId()); long myTask = waitForUserTask("myTask"); getProcessAPI().assignAndExecuteUserTask(user.getId(), myTask, Collections.emptyMap()); long afterBoundary = waitForUserTask("afterBoundary"); getProcessAPI().assignAndExecuteUserTask(user.getId(), afterBoundary, Collections.emptyMap()); waitForProcessToFinish(processInstance.getId()); assertThat(((Number) query("SELECT count(*) FROM event_trigger_instance").get(0)).intValue()).isEqualTo(0); disableAndDeleteProcess(procWithTimers); assertThat(((Number) query("SELECT count(*) FROM event_trigger_instance").get(0)).intValue()).isEqualTo(0); } private List query(String query) throws Exception { int t = 0; //retry because database locking issues might happen while (true) { t++; try { return SQLUtils.query(query); } catch (Exception e) { if (t > 5) { throw e; } } } } private void deleteAllArchivedProcessInstances(ProcessDefinition processDefinition) throws Exception { getProcessAPI().deleteArchivedProcessInstances(processDefinition.getId(), 0, 1000); } private Expression constant(long longValue) throws InvalidExpressionException { return new ExpressionBuilder().createConstantLongExpression(longValue); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/MockQueriableLogSessionProviderImpl.java ================================================ /** * 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; import org.bonitasoft.engine.services.QueriableLogSessionProvider; /** * @author Elias Ricken de Medeiros */ public class MockQueriableLogSessionProviderImpl implements QueriableLogSessionProvider { @Override public String getUserId() { return "admin"; } @Override public String getClusterNode() { return "node1"; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/MockQueriableLoggerStrategy.java ================================================ /** * 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; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.services.QueriableLoggerStrategy; /** * @author Elias Ricken de Medeiros */ public class MockQueriableLoggerStrategy implements QueriableLoggerStrategy { private final List loggable; public MockQueriableLoggerStrategy() { this.loggable = new ArrayList(); this.loggable.add("execute_connector_:" + SQueriableLogSeverity.BUSINESS); this.loggable.add("variable_update_:" + SQueriableLogSeverity.BUSINESS); this.loggable.add("execute_connector_:" + SQueriableLogSeverity.INTERNAL); this.loggable.add("variable_update_:" + SQueriableLogSeverity.INTERNAL); } @Override public boolean isLoggable(final String actionType, final SQueriableLogSeverity severity) { return !this.loggable.contains(actionType + ":" + severity.toString()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/PageAPILocalIT.java ================================================ /** * 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; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.junit.After; import org.junit.Before; import org.junit.Test; public class PageAPILocalIT extends CommonAPIIT { @Before public void before() throws BonitaException { loginWithTechnicalUser(); } @After public void after() throws BonitaException { final SearchResult searchPages = getPageAPI().searchPages(new SearchOptionsBuilder(0, 1000).done()); for (final Page page : searchPages.getResult()) { if (!page.isProvided()) { getPageAPI().deletePage(page.getId()); } } logout(); } /* * when the tenant is created the provided page "bonita-home-page.zip" should be imported from classpath */ @Test public void should_provided_page_be_imported() throws BonitaException { // given // engine started // when final Page homePage = getPageAPI().getPageByName("custompage_home"); // then assertThat(homePage).isNotNull(); assertThat(homePage.isProvided()).isTrue(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/RunnableWithException.java ================================================ /** * 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; /** * @author Baptiste Mesta */ public interface RunnableWithException { void run() throws Exception; } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/SQLUtils.java ================================================ /** * 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; import java.lang.reflect.Field; import java.util.List; import org.bonitasoft.engine.persistence.HibernatePersistenceService; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.hibernate.SessionFactory; public class SQLUtils { private static SessionFactory sessionFactory; public static List query(String query) throws Exception { return ServiceAccessorSingleton.getInstance().getUserTransactionService() .executeInTransaction(() -> getSessionFactory().getCurrentSession().createSQLQuery(query).list()); } public static int execute(String query) throws Exception { return ServiceAccessorSingleton.getInstance().getUserTransactionService() .executeInTransaction( () -> getSessionFactory().getCurrentSession().createSQLQuery(query).executeUpdate()); } private static SessionFactory getSessionFactory() throws NoSuchFieldException, IllegalAccessException { if (sessionFactory == null) { sessionFactory = createSessionFactory(); } return sessionFactory; } private static SessionFactory createSessionFactory() throws NoSuchFieldException, IllegalAccessException { ReadPersistenceService persistenceService = ServiceAccessorSingleton.getInstance().getReadPersistenceService(); Field sessionFactoryField = HibernatePersistenceService.class.getDeclaredField("sessionFactory"); sessionFactoryField.setAccessible(true); return (SessionFactory) sessionFactoryField.get(persistenceService); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/application/installer/ApplicationInstallerIT.java ================================================ /** * Copyright (C) 2023 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.application.installer; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import java.io.File; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.api.impl.application.installer.ApplicationArchive; import org.bonitasoft.engine.api.impl.application.installer.ApplicationArchiveReader; import org.bonitasoft.engine.api.impl.application.installer.ApplicationInstaller; import org.bonitasoft.engine.api.impl.application.installer.detector.ArtifactTypeDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.BdmDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.CustomPageDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.IconDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.LayoutDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.LivingApplicationDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.OrganizationDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.PageAndFormDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.ProcessDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.ThemeDetector; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.business.application.ApplicationNotFoundException; import org.bonitasoft.engine.exception.ApplicationInstallationException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Emmanuel Duchastenier */ public class ApplicationInstallerIT extends CommonAPIIT { @Before public void before() throws Exception { loginWithTechnicalUser(); } @After public void after() throws Exception { if (!getTenantAdministrationAPI().isPaused()) { getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); getTenantAdministrationAPI().resume(); } logout(); } @Test public void custom_application_should_be_deployed_entirely() throws Exception { // ensure application did not exist initially: assertThatExceptionOfType(ApplicationNotFoundException.class) .isThrownBy(() -> getApplicationAPI().getIApplicationByToken("appsManagerBonita")); // given: ApplicationInstaller applicationInstaller = ServiceAccessorSingleton.getInstance() .lookup(ApplicationInstaller.class); final ApplicationArchiveReader applicationArchiveReader = new ApplicationArchiveReader( new ArtifactTypeDetector(new BdmDetector(), new LivingApplicationDetector(), new OrganizationDetector(), new CustomPageDetector(), new ProcessDetector(), new ThemeDetector(), new PageAndFormDetector(), new LayoutDetector(), new IconDetector())); // when: try (var applicationAsStream = ApplicationInstallerIT.class.getResourceAsStream("/customer-application.zip")) { var applicationArchive = applicationArchiveReader.read(applicationAsStream); applicationInstaller.install(applicationArchive); } // then: // Organization has been installed: final User captainBonita = getIdentityAPI().getUserByUserName("captainBonita"); assertThat(captainBonita).isNotNull(); assertThat(getIdentityAPI().getRoleByName("appsManager")).isNotNull(); assertThat(getIdentityAPI().getGroupByPath("/appsManagement")).isNotNull(); assertThat(getApplicationAPI().getIApplicationByToken("appsManagerBonita").getDisplayName()) .isEqualTo("Application manager"); final long processDefinitionId = getProcessAPI().getProcessDefinitionId("CallHealthCheck", "1.0"); final ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinitionId); assertThat(deploymentInfo.getConfigurationState()).isEqualTo(ConfigurationState.RESOLVED); assertThat(deploymentInfo.getActivationState()).isEqualTo(ActivationState.ENABLED); // Rest API Extension is there: assertThat(getPageAPI().getPageByName("custompage_processStarter")).isNotNull(); // Pages are there: assertThat(getPageAPI().getPageByName("custompage_HealthPage")).isNotNull(); // Layouts are there: assertThat(getPageAPI().getPageByName("custompage_pmLayout")).isNotNull(); } @Test public void custom_application_should_be_installed_with_configuration() throws Exception { // given: ApplicationInstaller applicationInstaller = ServiceAccessorSingleton.getInstance() .lookup(ApplicationInstaller.class); final ApplicationArchiveReader applicationArchiveReader = new ApplicationArchiveReader( new ArtifactTypeDetector(new BdmDetector(), new LivingApplicationDetector(), new OrganizationDetector(), new CustomPageDetector(), new ProcessDetector(), new ThemeDetector(), new PageAndFormDetector(), new LayoutDetector(), new IconDetector())); // when: try (var applicationAsStream = ApplicationInstallerIT.class .getResourceAsStream("/simple-app-1.0.0-SNAPSHOT-local.zip")) { var applicationArchive = applicationArchiveReader.read(applicationAsStream); applicationArchive.setConfigurationFile(new File(ApplicationInstallerIT.class .getResource("/simple-app-1.0.0-SNAPSHOT-local.bconf").getFile())); applicationInstaller.install(applicationArchive); } final long processDefinitionId = getProcessAPI().getProcessDefinitionId("Pool", "1.0"); final ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinitionId); assertThat(deploymentInfo.getConfigurationState()).isEqualTo(ConfigurationState.RESOLVED); assertThat(deploymentInfo.getActivationState()).isEqualTo(ActivationState.ENABLED); var paramInstance = getProcessAPI().getParameterInstance(processDefinitionId, "hello"); assertThat(paramInstance.getValue()).isEqualTo("world_post_install"); } @Test public void empty_custom_application_should_throw_an_exception() throws Exception { // given: ApplicationInstaller applicationInstaller = ServiceAccessorSingleton.getInstance() .lookup(ApplicationInstaller.class); final ApplicationArchiveReader applicationArchiveReader = new ApplicationArchiveReader( new ArtifactTypeDetector(new BdmDetector(), new LivingApplicationDetector(), new OrganizationDetector(), new CustomPageDetector(), new ProcessDetector(), new ThemeDetector(), new PageAndFormDetector(), new LayoutDetector(), new IconDetector())); try (var applicationAsStream = ApplicationInstallerIT.class .getResourceAsStream("/empty-customer-application.zip")) { final ApplicationArchive applicationArchive = applicationArchiveReader.read(applicationAsStream); // then: assertThatExceptionOfType(ApplicationInstallationException.class) .isThrownBy(() -> applicationInstaller.install(applicationArchive)) .withMessage("The Application Archive contains no valid artifact to install"); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/application/installer/ApplicationInstallerUpdateIT.java ================================================ /** * Copyright (C) 2023 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.application.installer; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import java.io.IOException; import java.io.InputStream; import java.time.Duration; import java.time.temporal.ChronoUnit; import org.awaitility.Awaitility; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.api.impl.application.installer.ApplicationArchive; import org.bonitasoft.engine.api.impl.application.installer.ApplicationArchiveReader; import org.bonitasoft.engine.api.impl.application.installer.ApplicationInstaller; import org.bonitasoft.engine.api.impl.application.installer.detector.ArtifactTypeDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.BdmDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.CustomPageDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.IconDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.LayoutDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.LivingApplicationDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.OrganizationDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.PageAndFormDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.ProcessDetector; import org.bonitasoft.engine.api.impl.application.installer.detector.ThemeDetector; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.business.application.ApplicationNotFoundException; import org.bonitasoft.engine.business.application.ApplicationPage; import org.bonitasoft.engine.business.application.IApplication; import org.bonitasoft.engine.exception.ApplicationInstallationException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.page.PageNotFoundException; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.engine.tenant.TenantResource; import org.junit.Before; import org.junit.Test; /** * @author Haroun El Alami * @author Danila Mazour */ public class ApplicationInstallerUpdateIT extends CommonAPIIT { private ApplicationInstaller applicationInstaller; private ApplicationArchiveReader applicationArchiveReader; @Before public void before() throws Exception { loginWithTechnicalUser(); applicationInstaller = ServiceAccessorSingleton.getInstance() .lookup(ApplicationInstaller.class); applicationArchiveReader = new ApplicationArchiveReader( new ArtifactTypeDetector(new BdmDetector(), new LivingApplicationDetector(), new OrganizationDetector(), new CustomPageDetector(), new ProcessDetector(), new ThemeDetector(), new PageAndFormDetector(), new LayoutDetector(), new IconDetector())); initFirstInstall(); } private void initFirstInstall() throws Exception { // ensure application did not exist initially: assertThatExceptionOfType(ApplicationNotFoundException.class) .isThrownBy(() -> getApplicationAPI().getIApplicationByToken("appsManagerBonita")); // given: try (final InputStream applicationAsStream = this.getClass().getResourceAsStream("/customer-application.zip")) { // when: applicationInstaller.install(applicationArchiveReader.read(applicationAsStream)); } // then: // Organization has been installed: final User captainBonita = getIdentityAPI().getUserByUserName("captainBonita"); assertThat(captainBonita).isNotNull(); assertThat(getIdentityAPI().getRoleByName("appsManager")).isNotNull(); assertThat(getIdentityAPI().getGroupByPath("/appsManagement")).isNotNull(); assertThat(getApplicationAPI().getIApplicationByToken("appsManagerBonita").getDisplayName()) .isEqualTo("Application manager"); final long processDefinitionId = getProcessAPI().getProcessDefinitionId("CallHealthCheck", "1.0"); final ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinitionId); assertThat(deploymentInfo.getConfigurationState()).isEqualTo(ConfigurationState.RESOLVED); assertThat(deploymentInfo.getActivationState()).isEqualTo(ActivationState.ENABLED); // Rest API Extension is there: assertThat(getPageAPI().getPageByName("custompage_processStarter")).isNotNull(); // Pages are there: assertThat(getPageAPI().getPageByName("custompage_HealthPage")).isNotNull(); // Layouts are there: assertThat(getPageAPI().getPageByName("custompage_pmLayout")).isNotNull(); } @Test public void empty_custom_application_should_throw_an_exception() throws Exception { // given: final InputStream applicationAsStream = this.getClass().getResourceAsStream("/empty-customer-application.zip"); final ApplicationArchive applicationArchive = applicationArchiveReader.read(applicationAsStream); // then: assertThatExceptionOfType(ApplicationInstallationException.class) .isThrownBy(() -> applicationInstaller.update(applicationArchive)) .withMessage("The Application Archive contains no valid artifact to install"); } @Test public void process_update_custom_application_with_same_installed_version() throws ApplicationNotFoundException, PageNotFoundException, ProcessDefinitionNotFoundException, IOException, ApplicationInstallationException { // given: //installed resources TenantResource bdm = getTenantAdministrationAPI().getBusinessDataModelResource(); IApplication application = getApplicationAPI().getIApplicationByToken("appsManagerBonita"); Page processStarterAPI = getPageAPI().getPageByName("custompage_processStarter"); Page healthPage = getPageAPI().getPageByName("custompage_HealthPage"); Page pmLayout = getPageAPI().getPageByName("custompage_pmLayout"); final long processDefinitionId = getProcessAPI().getProcessDefinitionId("CallHealthCheck", "1.0"); final ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinitionId); // when: try (var applicationAsStream = this.getClass().getResourceAsStream("/customer-application.zip")) { // Avoid test failure due to instant update. Awaitility.await().timeout(Duration.of(1, ChronoUnit.SECONDS)); applicationInstaller.update(applicationArchiveReader.read(applicationAsStream)); } // then: TenantResource updatedBdm = getTenantAdministrationAPI().getBusinessDataModelResource(); IApplication updatedApplication = getApplicationAPI().getIApplicationByToken("appsManagerBonita"); Page updatedProcessStarterAPI = getPageAPI().getPageByName("custompage_processStarter"); Page updatedHealthPage = getPageAPI().getPageByName("custompage_HealthPage"); Page updatedPmLayout = getPageAPI().getPageByName("custompage_pmLayout"); final ProcessDeploymentInfo deploymentInfoAfterUpdate = getProcessAPI() .getProcessDeploymentInfo(processDefinitionId); // check that bdm resource has NOT been updated (same content) assertThat(updatedBdm.getLastUpdateDate()).isEqualTo(bdm.getLastUpdateDate()); // check that resources has been updated assertThat(updatedApplication.getLastUpdateDate().after(application.getLastUpdateDate())).isTrue(); assertThat( updatedProcessStarterAPI.getLastModificationDate().after(processStarterAPI.getLastModificationDate())) .isTrue(); assertThat(updatedHealthPage.getLastModificationDate().after(healthPage.getLastModificationDate())).isTrue(); assertThat(updatedPmLayout.getLastModificationDate().after(pmLayout.getLastModificationDate())).isTrue(); // CallHealthCheck Process must not be updated assertThat(deploymentInfoAfterUpdate.getLastUpdateDate()).isEqualTo(deploymentInfo.getLastUpdateDate()); } @Test public void process_update_custom_application_with_new_version() throws ApplicationNotFoundException, PageNotFoundException, ProcessDefinitionNotFoundException, IOException, ApplicationInstallationException, SearchException { // given: //installed resources TenantResource bdm = getTenantAdministrationAPI().getBusinessDataModelResource(); IApplication application = getApplicationAPI().getIApplicationByToken("appsManagerBonita"); Page processStarterAPI = getPageAPI().getPageByName("custompage_processStarter"); Page healthPage = getPageAPI().getPageByName("custompage_HealthPage"); Page pmLayout = getPageAPI().getPageByName("custompage_pmLayout"); final long processDefinitionId = getProcessAPI().getProcessDefinitionId("CallHealthCheck", "1.0"); final ProcessDeploymentInfo deploymentInfo = getProcessAPI().getProcessDeploymentInfo(processDefinitionId); // when: final InputStream applicationAsStream = this.getClass().getResourceAsStream("/customer-application-v2.zip"); applicationInstaller.update(applicationArchiveReader.read(applicationAsStream)); // then: TenantResource updatedBdm = getTenantAdministrationAPI().getBusinessDataModelResource(); IApplication updatedApplication = getApplicationAPI().getIApplicationByToken("appsManagerBonita"); Page updatedProcessStarterAPI = getPageAPI().getPageByName("custompage_processStarter"); Page updatedHealthPage = getPageAPI().getPageByName("custompage_HealthPage"); Page updatedPmLayout = getPageAPI().getPageByName("custompage_pmLayout"); final ProcessDeploymentInfo deploymentInfoAfterUpdate = getProcessAPI() .getProcessDeploymentInfo(processDefinitionId); // check that resources has been updated assertThat(updatedBdm.getLastUpdateDate()).isAfter(bdm.getLastUpdateDate()); // check installed apps assertThat(updatedApplication.getLastUpdateDate().after(application.getLastUpdateDate())).isTrue(); // fetch application menus ApplicationPage healthzPage = getApplicationAPI().searchApplicationPages( new SearchOptionsBuilder(0, Integer.MAX_VALUE) .filter("applicationId", (Long) updatedApplication.getId()) .done()) .getResult().get(0); assertThat(healthzPage.getToken()).isEqualTo("healthz"); // new app installed assertThat(getPageAPI().getPageByName("custompage_reportsPage")).isNotNull(); // check updated custom pages assertThat( updatedProcessStarterAPI.getLastModificationDate().after(processStarterAPI.getLastModificationDate())) .isTrue(); assertThat(updatedProcessStarterAPI.getContentName()).isEqualTo("processStarter-1.1.zip"); assertThat(updatedHealthPage.getLastModificationDate().after(healthPage.getLastModificationDate())).isTrue(); assertThat(updatedHealthPage.getContentName()).isEqualTo("page_HealthPage.zip"); assertThat(updatedPmLayout.getLastModificationDate().after(pmLayout.getLastModificationDate())).isTrue(); assertThat(updatedPmLayout.getContentName()).isEqualTo("layout_pmLayout.zip"); // CallHealthCheck Process must not be updated assertThat(deploymentInfoAfterUpdate.getLastUpdateDate().after(deploymentInfo.getLastUpdateDate())).isTrue(); assertThat(deploymentInfoAfterUpdate.getActivationState()).isEqualTo(ActivationState.DISABLED); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/archive/ArchiveServiceIT.java ================================================ /** * 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.archive; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.data.instance.model.archive.SAShortTextDataInstance; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.junit.Before; import org.junit.Test; public class ArchiveServiceIT extends CommonBPMServicesTest { private static final long START_OF_2009 = 1230739500052L; private static final long BEFORE_2009 = 1130739200052L; private static final int ONE_DAY = 86400000; private ArchiveService archiveService; @Before public void before() { this.archiveService = getServiceAccessor().getArchiveService(); } @Test public void testRecordInsert() throws Exception { getTransactionService().begin(); final SAShortTextDataInstance dataInstance = insertDataWithYesterdayDate(); assertNotNull(dataInstance); getTransactionService().complete(); } @Test public void archiveInSlidingArchiveNotDone() throws Exception { getTransactionService().begin(); final SAShortTextDataInstance dataInstance = insertDataWithFirstJanuary2009Date(); getTransactionService().complete(); getTransactionService().begin(); final SAShortTextDataInstance dataInstanceFromArchive = selectDataByIdFromDefinitiveArchive(dataInstance); assertNotNull("should be in definitive archive", dataInstanceFromArchive); assertEquals(dataInstance.getName(), dataInstanceFromArchive.getName()); assertEquals(dataInstance.getValue(), dataInstanceFromArchive.getValue()); getTransactionService().complete(); } @Test public void insertWithNoDefinitiveArchiveForThatDate() throws Exception { getTransactionService().begin(); try { insertDataWithBefore2009Date(); } finally { getTransactionService().complete(); } } private SAShortTextDataInstance insertDataWithYesterdayDate() throws SRecorderException { return insertData(System.currentTimeMillis() - ONE_DAY); } private SAShortTextDataInstance insertDataWithFirstJanuary2009Date() throws SRecorderException { return insertData(START_OF_2009); } private SAShortTextDataInstance insertDataWithBefore2009Date() throws SRecorderException { return insertData(BEFORE_2009); } private SAShortTextDataInstance insertData(long before2009) throws SRecorderException { final SAShortTextDataInstance data = new SAShortTextDataInstance(); data.setName("archiveTestEmployee"); data.setValue("password"); archiveService.recordInsert(before2009, new ArchiveInsertRecord(data)); return data; } private SAShortTextDataInstance selectDataByIdFromDefinitiveArchive(final SAShortTextDataInstance dataInstance) throws SBonitaReadException { final SelectByIdDescriptor selectByIdDescriptor1 = new SelectByIdDescriptor<>( SAShortTextDataInstance.class, dataInstance.getId()); return archiveService.getDefinitiveArchiveReadPersistenceService().selectById(selectByIdDescriptor1); } @Test public void testRecordDelete() throws Exception { getTransactionService().begin(); final SAShortTextDataInstance dataInstance = insertDataWithYesterdayDate(); getTransactionService().complete(); getTransactionService().begin(); archiveService.recordDelete(new DeleteRecord(dataInstance)); getTransactionService().complete(); } @Test public void testGetDefinitiveArchiveReadPersistenceService() { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); assertNotNull(persistenceService); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/archive/model/TestLogBuilder.java ================================================ /** * 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.archive.model; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; /** * @author Elias Ricken de Medeiros */ public class TestLogBuilder extends CRUDELogBuilder implements SPersistenceLogBuilder { @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(0, objectId); return this; } @Override protected String getActionTypePrefix() { return "TEST"; } @Override protected void checkExtraRules(final SQueriableLog log) { // nothing here } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/archive/model/TestLogBuilderFactory.java ================================================ /** * 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.archive.model; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Elias Ricken de Medeiros */ public class TestLogBuilderFactory extends CRUDELogBuilderFactory implements SPersistenceLogBuilderFactory { @Override public TestLogBuilder createNewInstance() { return new TestLogBuilder(); } @Override public String getObjectIdKey() { return "numericIndex1"; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/authentication/AuthenticationServiceIT.java ================================================ /** * 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.authentication; import static org.junit.Assert.assertNull; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SUser; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class AuthenticationServiceIT extends CommonBPMServicesTest { private static GenericAuthenticationService authService; private static IdentityService identityService; @Before public void setup() { identityService = getServiceAccessor().getIdentityService(); authService = getServiceAccessor().getAuthenticationService(); } @Test public void testCheckValidUser() throws Exception { final String username = "john"; final String password = "bpm"; final SUser user = createUser(username, password); getTransactionService().begin(); Map credentials = new HashMap(); credentials.put(AuthenticationConstants.BASIC_PASSWORD, password); credentials.put(AuthenticationConstants.BASIC_USERNAME, username); authService.checkUserCredentials(credentials); getTransactionService().complete(); deleteUser(user); } private SUser createUser(final String username, final String password) throws Exception { getTransactionService().begin(); final SUser.SUserBuilder userBuilder = SUser.builder().userName(username).password(password); final SUser user = identityService.createUser(userBuilder.build()); getTransactionService().complete(); return user; } private void deleteUser(final SUser user) throws Exception { getTransactionService().begin(); identityService.deleteUser(user); getTransactionService().complete(); } @Test public void testCheckUserWithWrongPassword() throws Exception { final String username = "james"; final String password = "bpm"; final SUser user = createUser(username, password); getTransactionService().begin(); Map credentials = new HashMap(); credentials.put(AuthenticationConstants.BASIC_PASSWORD, "wrong"); credentials.put(AuthenticationConstants.BASIC_USERNAME, username); final String userNameResult = authService.checkUserCredentials(credentials); getTransactionService().complete(); assertNull(userNameResult); deleteUser(user); } @Test public void testCheckNonExistentUser() throws Exception { final String username = "anonyme"; final String password = "bpm"; getTransactionService().begin(); Map credentials = new HashMap(); credentials.put(AuthenticationConstants.BASIC_PASSWORD, password); credentials.put(AuthenticationConstants.BASIC_USERNAME, username); final String userNameResult = authService.checkUserCredentials(credentials); getTransactionService().complete(); assertNull(userNameResult); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/ActorMappingServiceIT.java ================================================ /** * 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.bpm; import static org.junit.Assert.*; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.SActorNotFoundException; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.api.impl.IdentityAPIImpl; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.transaction.STransactionException; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class ActorMappingServiceIT extends CommonBPMServicesTest { private ActorMappingService actorMappingService; private Group mainGroup; private Group parentGroup; private Group childGroup; @Before public void setup() throws Exception { actorMappingService = getServiceAccessor().getActorMappingService(); mainGroup = createGroup("main"); parentGroup = createGroup("parent"); childGroup = createGroup("child", "/parent"); } @After public void tearDown() throws DeletionException { try { getTransactionService().begin(); new IdentityAPIImpl() .deleteGroups(Arrays.asList(childGroup.getId(), parentGroup.getId(), mainGroup.getId())); getTransactionService().complete(); } catch (STransactionException e) { throw new DeletionException(e); } } @Test(expected = SActorNotFoundException.class) public void cannotGetAnUnknownActor() throws Exception { getTransactionService().executeInTransaction(() -> actorMappingService.getActor(0)); } @Test public void getAnActor() throws SBonitaException { final Set actors = new HashSet<>(); final String manager = "Manager"; final long scopeId = 12; final SActor actor = SActor.builder().name(manager).scopeId(scopeId).initiator(false).build(); actors.add(actor); getTransactionService().begin(); try { actorMappingService.addActors(actors); } finally { getTransactionService().complete(); } getTransactionService().begin(); try { final SActor sActor = actorMappingService.getActor(manager, scopeId); assertNotNull(sActor); assertEquals(manager, sActor.getName()); assertEquals(scopeId, sActor.getScopeId()); actorMappingService.deleteActors(scopeId); } finally { getTransactionService().complete(); } } @Test public void getActorsFromActorIds() throws SBonitaException { final Set actors = new HashSet<>(); final String manager = "Manager"; final long scopeId = 12; final SActor actor = SActor.builder().name(manager).scopeId(scopeId).initiator(false).build(); actors.add(actor); final String manager2 = "Leader"; final long scopeId2 = 12; final SActor actor2 = SActor.builder().name(manager2).scopeId(scopeId2).initiator(false).build(); actors.add(actor2); getTransactionService().begin(); try { actorMappingService.addActors(actors); } finally { getTransactionService().complete(); } getTransactionService().begin(); long actor1id, actor2id; try { final SActor sActor1 = actorMappingService.getActor(manager, scopeId); assertNotNull(sActor1); assertEquals(manager, sActor1.getName()); assertEquals(scopeId, sActor1.getScopeId()); actor1id = sActor1.getId(); final SActor sActor2 = actorMappingService.getActor(manager2, scopeId2); assertNotNull(sActor2); assertEquals(manager2, sActor2.getName()); assertEquals(scopeId2, sActor2.getScopeId()); actor2id = sActor2.getId(); } finally { getTransactionService().complete(); } getTransactionService().begin(); try { final List actorIds = Arrays.asList(actor1id, actor2id); final List actorsRes = actorMappingService.getActors(actorIds); boolean isHasManager = false; boolean isHasManager2 = false; assertEquals(2, actorsRes.size()); for (final SActor sActor : actorsRes) { if (manager.equals(sActor.getName())) { isHasManager = true; } else { if (manager2.equals(sActor.getName())) { isHasManager2 = true; } } } assertTrue(isHasManager && isHasManager2); actorMappingService.deleteActors(scopeId2); } finally { getTransactionService().complete(); } } @Test public void addAndRemoveAUserOfAnActor() throws Exception { Set actors = new HashSet<>(); final SActor actor = SActor.builder().name("Manager").scopeId(12).initiator(false).build(); actors.add(actor); getTransactionService().begin(); actors = actorMappingService.addActors(actors); final long actorId = actors.iterator().next().getId(); List actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(0, actorMembers.size()); actorMappingService.addUserToActor(actorId, 1); getTransactionService().complete(); getTransactionService().begin(); try { actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(1, actorMembers.size()); checkActorMember(actorMembers.get(0), 1, -1, -1); actorMappingService.deleteActorMember(actorMembers.get(0).getId()); actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(0, actorMembers.size()); actorMappingService.deleteActors(12); } finally { getTransactionService().complete(); } } private void checkActorMember(final SActorMember sActorMember, final long userId, final long groupId, final long roleId) { assertEquals(userId, sActorMember.getUserId()); assertEquals(groupId, sActorMember.getGroupId()); assertEquals(roleId, sActorMember.getRoleId()); } @Test public void addAndRemoveARoleOfAnActor() throws Exception { Set actors = new HashSet<>(); final SActor actor = SActor.builder().name("Manager").scopeId(12).initiator(false).build(); actors.add(actor); getTransactionService().begin(); actors = actorMappingService.addActors(actors); final long actorId = actors.iterator().next().getId(); List actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(0, actorMembers.size()); actorMappingService.addRoleToActor(actorId, 1); getTransactionService().complete(); getTransactionService().begin(); try { actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(1, actorMembers.size()); checkActorMember(actorMembers.get(0), -1, -1, 1); actorMappingService.deleteActorMember(actorMembers.get(0).getId()); actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(0, actorMembers.size()); actorMappingService.deleteActors(12); } finally { getTransactionService().complete(); } } @Test public void addAndRemoveAGroupOfAnActor() throws Exception { Set actors = new HashSet<>(); final SActor actor = SActor.builder().name("Manager").scopeId(12).initiator(false).build(); actors.add(actor); getTransactionService().begin(); actors = actorMappingService.addActors(actors); final long actorId = actors.iterator().next().getId(); List actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(0, actorMembers.size()); actorMappingService.addGroupToActor(actorId, mainGroup.getId()); getTransactionService().complete(); getTransactionService().begin(); try { actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(1, actorMembers.size()); checkActorMember(actorMembers.get(0), -1, mainGroup.getId(), -1); actorMappingService.deleteActorMember(actorMembers.get(0).getId()); actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(0, actorMembers.size()); actorMappingService.deleteActors(12); } finally { getTransactionService().complete(); } } @Test public void addAndRemoveAGroupWithSubGroupsOfAnActor() throws Exception { Set actors = new HashSet<>(); final SActor actor = SActor.builder().name("Manager").scopeId(12).initiator(false).build(); actors.add(actor); getTransactionService().begin(); actors = actorMappingService.addActors(actors); final long actorId = actors.iterator().next().getId(); List actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(0, actorMembers.size()); actorMappingService.addGroupToActor(actorId, parentGroup.getId()); getTransactionService().complete(); getTransactionService().begin(); try { actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(2, actorMembers.size()); checkActorMember(actorMembers.get(0), -1, parentGroup.getId(), -1); checkActorMember(actorMembers.get(1), -1, childGroup.getId(), -1); actorMappingService.deleteActorMember(actorMembers.get(0).getId()); actorMappingService.deleteActorMember(actorMembers.get(1).getId()); actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(0, actorMembers.size()); actorMappingService.deleteActors(12); } finally { getTransactionService().complete(); } } @Test public void addAndRemoveAMembershipOfAnActor() throws Exception { Set actors = new HashSet<>(); final SActor actor = SActor.builder().name("Manager").scopeId(12).initiator(false).build(); actors.add(actor); getTransactionService().begin(); actors = actorMappingService.addActors(actors); final long actorId = actors.iterator().next().getId(); List actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(0, actorMembers.size()); actorMappingService.addRoleAndGroupToActor(actorId, 1, mainGroup.getId()); getTransactionService().complete(); getTransactionService().begin(); try { actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(1, actorMembers.size()); checkActorMember(actorMembers.get(0), -1, mainGroup.getId(), 1); actorMappingService.deleteActorMember(actorMembers.get(0).getId()); actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(0, actorMembers.size()); actorMappingService.deleteActors(12); } finally { getTransactionService().complete(); } } @Test public void addAndRemoveAMembershipOfAnActorWithSubGroups() throws Exception { Set actors = new HashSet<>(); final SActor actor = SActor.builder().name("Manager").scopeId(12).initiator(false).build(); actors.add(actor); getTransactionService().begin(); actors = actorMappingService.addActors(actors); final long actorId = actors.iterator().next().getId(); List actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(0, actorMembers.size()); actorMappingService.addRoleAndGroupToActor(actorId, 1, parentGroup.getId()); getTransactionService().complete(); getTransactionService().begin(); try { actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(2, actorMembers.size()); checkActorMember(actorMembers.get(0), -1, parentGroup.getId(), 1); checkActorMember(actorMembers.get(1), -1, childGroup.getId(), 1); actorMappingService.deleteActorMember(actorMembers.get(0).getId()); actorMappingService.deleteActorMember(actorMembers.get(1).getId()); actorMembers = actorMappingService.getActorMembers(actorId, 0, 10); Assert.assertEquals(0, actorMembers.size()); actorMappingService.deleteActors(12); } finally { getTransactionService().complete(); } } @Test public void countActorMembers() throws Exception { final long scopeId = 12; SActor actor = SActor.builder().name("Manager").scopeId(scopeId).initiator(false).build(); getTransactionService().begin(); actor = actorMappingService.addActor(actor); final List actorMembers = actorMappingService.getActorMembers(actor.getId(), 0, 10); Assert.assertEquals(0, actorMembers.size()); actorMappingService.addRoleToActor(actor.getId(), 41); actorMappingService.addUserToActor(actor.getId(), 7); actorMappingService.addGroupToActor(actor.getId(), mainGroup.getId()); actorMappingService.addGroupToActor(actor.getId(), childGroup.getId()); actorMappingService.addUserToActor(actor.getId(), 83); final long numberOfActorMembers = actorMappingService.getNumberOfActorMembers(actor.getId()); assertEquals(5L, numberOfActorMembers); actorMappingService.deleteActors(12); getTransactionService().complete(); } @Test public void getNumberOfRolesOfActorShouldNotCountMemberships() throws Exception { final long scopeId = 12; getTransactionService().begin(); final IdentityAPIImpl identityAPI = new IdentityAPIImpl(); final Role role1 = identityAPI.createRole("roletest"); final Role role2 = identityAPI.createRole("role2test"); getTransactionService().complete(); SActor actor = SActor.builder().name("ActorRoleTest").scopeId(scopeId).initiator(false).build(); getTransactionService().begin(); actor = actorMappingService.addActor(actor); final List actorMembers = actorMappingService.getActorMembers(actor.getId(), 0, 10); Assert.assertEquals(0, actorMembers.size()); actorMappingService.addRoleAndGroupToActor(actor.getId(), role1.getId(), mainGroup.getId()); actorMappingService.addRoleToActor(actor.getId(), role2.getId()); final long numberOfActorMembers = actorMappingService.getNumberOfRolesOfActor(actor.getId()); getTransactionService().complete(); assertEquals(1L, numberOfActorMembers); // clean-up: getTransactionService().begin(); actorMappingService.deleteActors(12); identityAPI.deleteRole(role1.getId()); identityAPI.deleteRole(role2.getId()); getTransactionService().complete(); } @Test public void getNumberOfGroupsOfActorShouldNotCountMemberships() throws Exception { final long scopeId = 12; SActor actor = SActor.builder().name("ActorGroupTest").scopeId(scopeId).initiator(false).build(); getTransactionService().begin(); actor = actorMappingService.addActor(actor); final List actorMembers = actorMappingService.getActorMembers(actor.getId(), 0, 10); Assert.assertEquals(0, actorMembers.size()); actorMappingService.addRoleAndGroupToActor(actor.getId(), 21, mainGroup.getId()); actorMappingService.addGroupToActor(actor.getId(), mainGroup.getId()); final long numberOfActorMembers = actorMappingService.getNumberOfGroupsOfActor(actor.getId()); assertEquals(1L, numberOfActorMembers); actorMappingService.deleteActors(12); getTransactionService().complete(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/CategoryServiceIT.java ================================================ /** * 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.bpm; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.category.CategoryService; import org.bonitasoft.engine.core.category.exception.SCategoryAlreadyExistsException; import org.bonitasoft.engine.core.category.exception.SCategoryCreationException; import org.bonitasoft.engine.core.category.exception.SCategoryNotFoundException; import org.bonitasoft.engine.core.category.model.SCategory; import org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilder; import org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.transaction.TransactionService; import org.junit.Before; import org.junit.Test; /** * @author Yanyan Liu */ public class CategoryServiceIT extends CommonBPMServicesTest { private static CategoryService categoryService; private static TransactionService transactionService; @Before public void setup() { categoryService = getServiceAccessor().getCategoryService(); transactionService = getTransactionService(); } @Test public void createCategory() throws Exception { final String name = "categoryName"; final String description = "test create category"; transactionService.begin(); final SCategory category = categoryService.createCategory(name, description); assertNotNull(category); assertEquals(name, category.getName()); assertEquals(description, category.getDescription()); categoryService.deleteCategory(category.getId()); transactionService.complete(); } @Test(expected = SCategoryAlreadyExistsException.class) public void createCategoryWithSCategoryAlreadyExistsException() throws Exception { final String name = "categoryTestExceptionName"; final String description = "test create category with SCategoryAlreadyExistsException"; transactionService.begin(); final SCategory category = categoryService.createCategory(name, description); assertNotNull(category); assertEquals(name, category.getName()); try { categoryService.createCategory(name, description); } finally { categoryService.deleteCategory(category.getId()); transactionService.complete(); } } @Test(expected = SCategoryCreationException.class) public void createCategoryWithSCategoryCreationException() throws Exception { final String name = null; final String description = "test create category with SCategoryCreationException"; transactionService.begin(); try { categoryService.createCategory(name, description); } finally { transactionService.complete(); } } @Test public void getCategory() throws Exception { final String name = "categoryName"; final String description = "test retrieve category"; transactionService.begin(); // create final SCategory category = categoryService.createCategory(name, description); assertNotNull(category); // retrieve test final SCategory retrievedCategory = categoryService.getCategory(category.getId()); assertNotNull(retrievedCategory); assertEquals(name, retrievedCategory.getName()); assertEquals(description, retrievedCategory.getDescription()); // delete categoryService.deleteCategory(category.getId()); transactionService.complete(); } @Test public void getCategoryByName() throws Exception { final String name = "categoryName"; final String description = "test get category by name"; transactionService.begin(); // create final SCategory category1 = categoryService.createCategory(name, description); assertNotNull(category1); // retrieve test final SCategory category2 = categoryService.getCategoryByName(category1.getName()); assertNotNull(category2); assertEquals(category1.getId(), category2.getId()); assertEquals(name, category2.getName()); assertEquals(description, category2.getDescription()); // delete categoryService.deleteCategory(category2.getId()); transactionService.complete(); } @Test public void updateCategory() throws Exception { final String name = "categoryName"; final String description = "test update category"; transactionService.begin(); // create final SCategory category1 = categoryService.createCategory(name, description); assertNotNull(category1); final long categoryId = category1.getId(); // update final String newName = "updatedName"; final String newDescription = "updatedDescription"; final SCategoryUpdateBuilder updateBuilder = BuilderFactory.get(SCategoryUpdateBuilderFactory.class) .createNewInstance(); updateBuilder.updateName(newName).updateDescription(newDescription); categoryService.updateCategory(categoryId, updateBuilder.done()); transactionService.complete(); transactionService.begin(); // test final SCategory updatedCategory = categoryService.getCategory(categoryId); assertNotNull(updatedCategory); assertEquals(newName, updatedCategory.getName()); assertEquals(newDescription, updatedCategory.getDescription()); // delete categoryService.deleteCategory(categoryId); transactionService.complete(); } @Test(expected = SCategoryNotFoundException.class) public void updateCategoryWithSCategoryNotFoundException() throws Exception { final long categoryId = 1; final String newName = "updatedName"; final String newDescription = "updatedDescription"; final SCategoryUpdateBuilder updateBuilder = BuilderFactory.get(SCategoryUpdateBuilderFactory.class) .createNewInstance(); updateBuilder.updateName(newName).updateDescription(newDescription); transactionService.begin(); categoryService.updateCategory(categoryId, updateBuilder.done()); transactionService.complete(); } @Test public void deleteCategory() throws Exception { final String name = "categoryName_delete"; final String description = "test delete category"; transactionService.begin(); // create final SCategory category = categoryService.createCategory(name, description); assertNotNull(category); assertEquals(name, category.getName()); // delete test categoryService.deleteCategory(category.getId()); transactionService.complete(); transactionService.begin(); try { categoryService.getCategory(category.getId()); fail(); } catch (final SCategoryNotFoundException ignored) { } transactionService.complete(); } @Test public void getNumberOfCategories() throws Exception { transactionService.begin(); long count = categoryService.getNumberOfCategories(); assertEquals(0, count); // create final String name = "categoryName"; final String description = "category description"; final List categoryList = createCategories(3, name, description); assertNotNull(categoryList); assertEquals(3, categoryList.size()); count = categoryService.getNumberOfCategories(); assertEquals(3, count); // delete test for (final SCategory category : categoryList) { categoryService.deleteCategory(category.getId()); } transactionService.complete(); } @Test // this should be client test public void getCategoriesInOrder() throws Exception { final String name = "categoryName"; final String description = "test get categories in order"; transactionService.begin(); // create final List categoryList = createCategories(5, name, description); assertNotNull(categoryList); assertEquals(5, categoryList.size()); // test name ASC final List categoryList1 = categoryService.getCategories(0, 2, "name", OrderByType.ASC); assertNotNull(categoryList1); assertEquals(2, categoryList1.size()); assertEquals("categoryName1", categoryList1.get(0).getName()); assertEquals("categoryName2", categoryList1.get(1).getName()); final List categoryList2 = categoryService.getCategories(1, 2, "name", OrderByType.ASC); assertNotNull(categoryList2); assertEquals(2, categoryList2.size()); assertEquals("categoryName2", categoryList2.get(0).getName()); assertEquals("categoryName3", categoryList2.get(1).getName()); final List categoryList3 = categoryService.getCategories(3, 2, "name", OrderByType.ASC); assertNotNull(categoryList3); assertEquals(2, categoryList3.size()); assertEquals("categoryName4", categoryList3.get(0).getName()); assertEquals("categoryName5", categoryList3.get(1).getName()); final List categoryList4 = categoryService.getCategories(5, 2, "name", OrderByType.ASC); assertEquals(0, categoryList4.size()); // test name DESC final List categoryList5 = categoryService.getCategories(0, 2, "name", OrderByType.DESC); assertNotNull(categoryList5); assertEquals(2, categoryList5.size()); assertEquals("categoryName5", categoryList5.get(0).getName()); assertEquals("categoryName4", categoryList5.get(1).getName()); // delete test for (final SCategory category : categoryList) { categoryService.deleteCategory(category.getId()); } transactionService.complete(); } @Test(expected = SCategoryNotFoundException.class) public void addProcessDefinitionToCategoryWithSCategoryNotFoundException() throws Exception { // generate the meaningful of ProcessDefinition id final SProcessDefinition processDefinition = createSProcessDefinition("processName", "test category not found exceptioin"); transactionService.begin(); try { categoryService.addProcessDefinitionToCategory(1, processDefinition.getId()); } finally { transactionService.complete(); // Clean-up deleteSProcessDefinition(processDefinition); } } @Test public void getNumberOfCategories4Process() throws Exception { // generate a meaningful processDefinitionId final long processDefinitionId = createSProcessDefinition("processName", "test get number of categories of process").getId(); transactionService.begin(); long count = categoryService.getNumberOfCategoriesOfProcess(processDefinitionId); assertEquals(0, count); final String name = "categoryName"; final String description = "category description"; // create category final List categoryList = createCategories(2, name, description); assertNotNull(categoryList); assertEquals(2, categoryList.size()); // add process definition info to category for (final SCategory category : categoryList) { categoryService.addProcessDefinitionToCategory(category.getId(), processDefinitionId); } // check count = categoryService.getNumberOfCategoriesOfProcess(processDefinitionId); assertEquals(2, count); // delete category and process definition for (final SCategory category : categoryList) { categoryService.deleteCategory(category.getId()); } transactionService.complete(); // Clean-up deleteSProcessDefinition(processDefinitionId); } @Test public void getNumberOfCategoriesUnrelatedToProcess() throws Exception { final List processDefinitions = createSProcessDefinitions(2, "processName", "test get number of categories of process"); transactionService.begin(); // generate a meaningful processDefinitionId final long processDefinitionId = processDefinitions.get(0).getId(); long count = categoryService.getNumberOfCategoriesUnrelatedToProcess(processDefinitionId); assertEquals(0, count); final String name = "categoryName"; final String description = "category description"; // create category final List categoryList = createCategories(4, name, description); assertNotNull(categoryList); assertEquals(4, categoryList.size()); // add process definition info to category categoryService.addProcessDefinitionToCategory(categoryList.get(0).getId(), processDefinitionId); categoryService.addProcessDefinitionToCategory(categoryList.get(1).getId(), processDefinitionId); // check count = categoryService.getNumberOfCategoriesUnrelatedToProcess(processDefinitionId); assertEquals(2, count); count = categoryService.getNumberOfCategoriesUnrelatedToProcess(processDefinitions.get(1).getId()); assertEquals(4, count); // delete category and process definition for (final SCategory category : categoryList) { categoryService.deleteCategory(category.getId()); } transactionService.complete(); // Clean-up deleteSProcessDefinitions(processDefinitions); } @Test public void getCategoriesUnrelatedToProcess() throws Exception { final List processDefinitions = createSProcessDefinitions(2, "processName", "test get number of categories of process"); transactionService.begin(); // generate a meaningful processDefinitionId final long processDefinitionId = processDefinitions.get(0).getId(); List categories = categoryService.getCategoriesUnrelatedToProcessDefinition(processDefinitionId, 0, 4, OrderByType.ASC); assertEquals(0, categories.size()); final String name = "categoryName"; final String description = "category description"; // create category final List categoryList = createCategories(4, name, description); assertNotNull(categoryList); assertEquals(4, categoryList.size()); // add process definition info to category categoryService.addProcessDefinitionToCategory(categoryList.get(0).getId(), processDefinitionId); categoryService.addProcessDefinitionToCategory(categoryList.get(1).getId(), processDefinitionId); // check categories = categoryService.getCategoriesUnrelatedToProcessDefinition(processDefinitionId, 0, 4, OrderByType.ASC); assertEquals(2, categories.size()); assertEquals(categoryList.get(2).getId(), categories.get(0).getId()); assertEquals(categoryList.get(3).getId(), categories.get(1).getId()); categories = categoryService.getCategoriesUnrelatedToProcessDefinition(processDefinitions.get(1).getId(), 0, 4, OrderByType.ASC); assertEquals(4, categories.size()); assertEquals(categoryList.get(0).getId(), categories.get(0).getId()); assertEquals(categoryList.get(1).getId(), categories.get(1).getId()); assertEquals(categoryList.get(2).getId(), categories.get(2).getId()); assertEquals(categoryList.get(3).getId(), categories.get(3).getId()); // delete category and process definition for (final SCategory category : categoryList) { categoryService.deleteCategory(category.getId()); } transactionService.complete(); // Clean-up deleteSProcessDefinitions(processDefinitions); } @Test public void getCategoriesOfProcessDefinition() throws Exception { final SProcessDefinition processDefinition = createSProcessDefinitions(1, "processName", "test get categores of process definition").get(0); transactionService.begin(); // generate the meaningful processDefinition id and delete it in the end final long processDefinitionId = processDefinition.getId(); List categoryList = categoryService.getCategoriesOfProcessDefinition(processDefinitionId, 0, 2, OrderByType.ASC); assertEquals(0, categoryList.size()); final String name = "categoryName"; final String description = "test get categories of process definition"; // create categories, add process definition to categories categoryList = createCategories(5, name, description); transactionService.complete(); transactionService.begin(); for (final SCategory category : categoryList) { categoryService.addProcessDefinitionToCategory(category.getId(), processDefinitionId); } transactionService.complete(); transactionService.begin(); // test final List categoryList1 = categoryService.getCategoriesOfProcessDefinition(processDefinitionId, 0, 2, OrderByType.ASC); assertNotNull(categoryList1); assertEquals(2, categoryList1.size()); assertEquals("categoryName1", categoryList1.get(0).getName()); assertEquals("categoryName2", categoryList1.get(1).getName()); final List categoryList2 = categoryService.getCategoriesOfProcessDefinition(processDefinitionId, 1, 2, OrderByType.ASC); assertNotNull(categoryList2); assertEquals(2, categoryList2.size()); assertEquals("categoryName2", categoryList2.get(0).getName()); assertEquals("categoryName3", categoryList2.get(1).getName()); final List categoryList3 = categoryService.getCategoriesOfProcessDefinition(processDefinitionId, 2, 2, OrderByType.ASC); assertNotNull(categoryList3); assertEquals(2, categoryList3.size()); assertEquals("categoryName3", categoryList3.get(0).getName()); assertEquals("categoryName4", categoryList3.get(1).getName()); final List categoryList4 = categoryService.getCategoriesOfProcessDefinition(processDefinitionId, 4, 2, OrderByType.ASC); assertNotNull(categoryList4); assertEquals(1, categoryList4.size()); assertEquals("categoryName5", categoryList4.get(0).getName()); final List categories = categoryService.getCategoriesOfProcessDefinition(processDefinitionId, 5, 2, OrderByType.ASC); assertTrue(categories.isEmpty()); final List categoryList5 = categoryService.getCategoriesOfProcessDefinition(processDefinitionId, 0, 3, OrderByType.DESC); assertNotNull(categoryList5); assertEquals(3, categoryList5.size()); assertEquals("categoryName5", categoryList5.get(0).getName()); assertEquals("categoryName4", categoryList5.get(1).getName()); assertEquals("categoryName3", categoryList5.get(2).getName()); // delete categories and process for (final SCategory category : categoryList) { categoryService.deleteCategory(category.getId()); } transactionService.complete(); // Clean-up deleteSProcessDefinition(processDefinitionId); } private List createCategories(final int count, final String name, final String description) throws Exception { final List categoryList = new ArrayList(); for (int i = 1; i <= count; i++) { final SCategory category = categoryService.createCategory(name + i, description + i); categoryList.add(category); } return categoryList; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/DocumentServiceIT.java ================================================ /** * 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.bpm; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.Assert.assertEquals; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.model.SDocument; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.core.document.model.builder.SDocumentBuilder; import org.bonitasoft.engine.core.document.model.builder.SDocumentBuilderFactory; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.transaction.TransactionService; import org.junit.Before; import org.junit.Test; /** * @author Nicolas Chabanoles * @author Celine Souchet */ public class DocumentServiceIT extends CommonBPMServicesTest { private static final int NUMBER_OF_DOCUMENT = 10; private static DocumentService documentService; private static TransactionService transactionService; private static final long processInstanceId = 123L; private static String documentNameKey; @Before public void before() { documentService = getServiceAccessor().getDocumentService(); transactionService = getTransactionService(); documentNameKey = SDocument.NAME; } @Test public void attachDocumentToProcessInstanceWithContentTest() throws SBonitaException { transactionService.begin(); final SDocument document = buildProcessDocumentWithContent(1, "theContent".getBytes()); final SMappedDocument result = documentService.attachDocumentToProcessInstance(document, processInstanceId, "documentName", "the description"); transactionService.complete(); assertEquals(document.getAuthor(), result.getAuthor()); assertEquals(document.getFileName(), result.getFileName()); assertEquals(document.getMimeType(), result.getMimeType()); assertEquals(document.getCreationDate(), result.getCreationDate()); assertEquals(document.getUrl(), result.getUrl()); assertEquals(document.getId(), result.getDocumentId()); assertEquals("documentName", result.getName()); assertEquals("the description", result.getDescription()); assertEquals(processInstanceId, result.getProcessInstanceId()); // Clean up delete(result); } @Test public void attachDocumentToProcessInstanceWithUrlTest() throws SBonitaException { transactionService.begin(); final SDocument document = buildProcessDocumentWithUrl(1); final SMappedDocument result = documentService.attachDocumentToProcessInstance(document, processInstanceId, "documentName", "the description"); transactionService.complete(); assertEquals(document.getAuthor(), result.getAuthor()); assertEquals(document.getFileName(), result.getFileName()); assertEquals(document.getMimeType(), result.getMimeType()); assertEquals(document.getCreationDate(), result.getCreationDate()); assertEquals("documentName", result.getName()); assertEquals(processInstanceId, result.getProcessInstanceId()); assertEquals(document.getUrl(), result.getUrl()); assertEquals(-1, result.getIndex()); // Clean up delete(result); } @Test public void attachDocumentToProcessInList() throws SBonitaException { transactionService.begin(); final SDocument document0 = buildProcessDocumentWithUrl(1); final SDocument document1 = buildProcessDocumentWithUrl(1); final SMappedDocument result0 = documentService.attachDocumentToProcessInstance(document0, processInstanceId, "documentName", "the description", 0); final SMappedDocument result1 = documentService.attachDocumentToProcessInstance(document1, processInstanceId, "documentName", "the description", 1); transactionService.complete(); assertEquals(document0.getAuthor(), result0.getAuthor()); assertEquals(document0.getFileName(), result0.getFileName()); assertEquals(document0.getMimeType(), result0.getMimeType()); assertEquals(document0.getCreationDate(), result0.getCreationDate()); assertEquals("documentName", result0.getName()); assertEquals(processInstanceId, result0.getProcessInstanceId()); assertEquals(document0.getUrl(), result0.getUrl()); assertEquals(0, result0.getIndex()); assertEquals(1, result1.getIndex()); // Clean up delete(result0); delete(result1); } @Test public void getDocumentByIdTest() throws SBonitaException { final SMappedDocument sDocument = createDocumentMapping(); final SMappedDocument result = getDocumentMapping(sDocument); assertEquals(sDocument.getId(), result.getId()); // Clean up delete(result); } @Test public void getDocumentByNameAndProcessIdTest() throws SBonitaException { final SMappedDocument sDocument = createDocumentMapping(); transactionService.begin(); final SMappedDocument result = documentService.getMappedDocument(sDocument.getProcessInstanceId(), sDocument.getName()); transactionService.complete(); assertEquals(sDocument.getId(), result.getId()); assertEquals(sDocument.getName(), result.getName()); assertEquals(sDocument.getProcessInstanceId(), result.getProcessInstanceId()); // Clean up delete(result); } @Test public void getNumberOfDocumentMappingsForProcessInstanceTest() throws SBonitaException { final List list = createDocumentMappings(); transactionService.begin(); final long result = documentService.getNumberOfDocumentsOfProcessInstance(processInstanceId); transactionService.complete(); assertEquals(NUMBER_OF_DOCUMENT, result); // Clean up delete(list); } @Test public void getDocumentMappingsForProcessInstanceTest() throws SBonitaException { final List list = createDocumentMappings(); transactionService.begin(); final List result = documentService.getDocumentsOfProcessInstance( list.get(0).getProcessInstanceId(), 0, NUMBER_OF_DOCUMENT + 1, "name", OrderByType.ASC); transactionService.complete(); assertEquals(NUMBER_OF_DOCUMENT, result.size()); assertEquals(list.get(0).getId(), result.get(0).getId()); assertEquals(list.get(0).getName(), result.get(0).getName()); assertEquals(list.get(1).getId(), result.get(1).getId()); assertEquals(list.get(1).getName(), result.get(1).getName()); assertEquals(list.get(2).getId(), result.get(2).getId()); assertEquals(list.get(2).getName(), result.get(2).getName()); assertEquals(list.get(3).getId(), result.get(3).getId()); assertEquals(list.get(3).getName(), result.get(3).getName()); assertEquals(list.get(4).getId(), result.get(4).getId()); assertEquals(list.get(4).getName(), result.get(4).getName()); assertEquals(list.get(5).getId(), result.get(5).getId()); assertEquals(list.get(5).getName(), result.get(5).getName()); assertEquals(list.get(6).getId(), result.get(6).getId()); assertEquals(list.get(6).getName(), result.get(6).getName()); assertEquals(list.get(7).getId(), result.get(7).getId()); assertEquals(list.get(7).getName(), result.get(7).getName()); assertEquals(list.get(8).getId(), result.get(8).getId()); assertEquals(list.get(8).getName(), result.get(8).getName()); assertEquals(list.get(9).getId(), result.get(9).getId()); assertEquals(list.get(9).getName(), result.get(9).getName()); // Clean up delete(list); } private SDocument buildProcessDocumentWithContent(final int i, final byte[] documentContent) { final SDocumentBuilder builder = new SDocumentBuilderFactory().createNewInstance("getContentTest.txt", "text/plain", i); builder.setHasContent(true); builder.setContent(documentContent); return builder.done(); } private SDocument buildProcessDocumentWithUrl(final int i) { final SDocumentBuilder builder = new SDocumentBuilderFactory().createNewInstance("getContentTest.txt", "text/plain", i); builder.setHasContent(false); builder.setURL("theUrl"); return builder.done(); } private SMappedDocument createDocumentMapping() throws SBonitaException { transactionService.begin(); final SDocument document = buildProcessDocumentWithContent(1, "content".getBytes()); final SMappedDocument doc = documentService.attachDocumentToProcessInstance(document, processInstanceId, "documentName", "the description"); transactionService.complete(); return doc; } private List createDocumentMappings() throws SBonitaException { transactionService.begin(); final List list = new ArrayList<>(10); for (int i = 0; i < NUMBER_OF_DOCUMENT; i++) { final SDocument document = buildProcessDocumentWithContent(i, "content".getBytes()); list.add(documentService.attachDocumentToProcessInstance(document, processInstanceId, "documentName", "the description")); } transactionService.complete(); return list; } private SMappedDocument getDocumentMapping(final SMappedDocument sDocument) throws SBonitaException { transactionService.begin(); final SMappedDocument result = documentService.getMappedDocument(sDocument.getId()); transactionService.complete(); return result; } private void delete(final List sDocuments) throws SBonitaException { transactionService.begin(); for (final SMappedDocument sDocument : sDocuments) { documentService.deleteMappedDocument(sDocument); documentService.deleteDocument(sDocument.getDocumentId()); } transactionService.complete(); } private void delete(final SMappedDocument mappedDocument) throws SBonitaException { transactionService.begin(); documentService.deleteMappedDocument(mappedDocument); documentService.deleteDocument(mappedDocument.getDocumentId()); transactionService.complete(); } @Test public void getDocumentContentTest() throws SBonitaException { final byte[] documentContent = "this is the content of the document".getBytes(); final SMappedDocument sProcessDocument = createAndAttachDocumentToProcessInstanceWithContent(documentContent); final byte[] storedContents = getDocumentContent(sProcessDocument); assertEquals(documentContent.length, storedContents.length); // Clean up delete(sProcessDocument); } @Test public void getNumberOfDocumentsOfProcessInstanceTest() throws SBonitaException { final List list = createAndAttachDocumentToProcessInstances(); transactionService.begin(); final long result = documentService.getNumberOfDocumentsOfProcessInstance(list.get(0).getProcessInstanceId()); transactionService.complete(); assertEquals(NUMBER_OF_DOCUMENT, result); // Clean up delete(list); } @Test public void getDocumentsOfProcessInstanceTest() throws SBonitaException { final List list = createAndAttachDocumentToProcessInstances(); transactionService.begin(); final List result = documentService.getDocumentsOfProcessInstance( list.get(0).getProcessInstanceId(), 0, NUMBER_OF_DOCUMENT + 1, documentNameKey, OrderByType.ASC); transactionService.complete(); assertEquals(NUMBER_OF_DOCUMENT, result.size()); assertEquals(list.get(0).getId(), result.get(0).getId()); assertEquals(list.get(0).getName(), result.get(0).getName()); assertEquals(list.get(1).getId(), result.get(1).getId()); assertEquals(list.get(1).getName(), result.get(1).getName()); assertEquals(list.get(2).getId(), result.get(2).getId()); assertEquals(list.get(2).getName(), result.get(2).getName()); assertEquals(list.get(3).getId(), result.get(3).getId()); assertEquals(list.get(3).getName(), result.get(3).getName()); assertEquals(list.get(4).getId(), result.get(4).getId()); assertEquals(list.get(4).getName(), result.get(4).getName()); assertEquals(list.get(5).getId(), result.get(5).getId()); assertEquals(list.get(5).getName(), result.get(5).getName()); assertEquals(list.get(6).getId(), result.get(6).getId()); assertEquals(list.get(6).getName(), result.get(6).getName()); assertEquals(list.get(7).getId(), result.get(7).getId()); assertEquals(list.get(7).getName(), result.get(7).getName()); assertEquals(list.get(8).getId(), result.get(8).getId()); assertEquals(list.get(8).getName(), result.get(8).getName()); assertEquals(list.get(9).getId(), result.get(9).getId()); assertEquals(list.get(9).getName(), result.get(9).getName()); // Clean up delete(list); } @Test public void removeDocumentsTest() throws Exception { //given final byte[] documentContent = "this is the content of the document".getBytes(); final List list = createAndAttachDocumentToProcessInstancesWithContent(documentContent); //when delete(list); //then for (final SMappedDocument sProcessDocument : list) { transactionService.begin(); assertThatExceptionOfType(SObjectNotFoundException.class) .isThrownBy(() -> documentService.getMappedDocument(sProcessDocument.getId())); assertThatExceptionOfType(SObjectNotFoundException.class) .isThrownBy(() -> documentService.getDocument(sProcessDocument.getDocumentId())); transactionService.complete(); } } private SMappedDocument createAndAttachDocumentToProcessInstanceWithContent(final byte[] documentContent) throws SBonitaException { transactionService.begin(); final SDocument document = buildProcessDocumentWithContent(1, documentContent); final SMappedDocument doc = documentService.attachDocumentToProcessInstance(document, processInstanceId, "documentName", "the description"); transactionService.complete(); return doc; } private List createAndAttachDocumentToProcessInstancesWithContent(final byte[] documentContent) throws SBonitaException { transactionService.begin(); final List list = new ArrayList<>(10); for (int i = 0; i < NUMBER_OF_DOCUMENT; i++) { final SDocument document = buildProcessDocumentWithContent(i, documentContent); final SMappedDocument doc = documentService.attachDocumentToProcessInstance(document, processInstanceId, "documentName", "the description"); list.add(doc); } transactionService.complete(); return list; } private List createAndAttachDocumentToProcessInstances() throws SBonitaException { transactionService.begin(); final List list = new ArrayList<>(10); for (int i = 0; i < NUMBER_OF_DOCUMENT; i++) { final SDocument document = buildProcessDocumentWithUrl(i); final SMappedDocument doc = documentService.attachDocumentToProcessInstance(document, processInstanceId, "documentName", "the description"); list.add(doc); } transactionService.complete(); return list; } private byte[] getDocumentContent(final SMappedDocument sProcessDocument) throws SBonitaException { transactionService.begin(); final byte[] storedContents = documentService .getDocumentContent(String.valueOf(sProcessDocument.getDocumentId())); transactionService.complete(); return storedContents; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/EventInstanceRepositoryIT.java ================================================ /** * 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.bpm; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import java.time.Instant; import java.util.List; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.transaction.TransactionService; import org.junit.Before; import org.junit.Test; public class EventInstanceRepositoryIT extends CommonBPMServicesTest { private EventInstanceRepository eventInstanceRepository; private TransactionService transactionService; private final long oneMinuteAgo = Instant.now().minusSeconds(60).toEpochMilli(); @Before public void before() throws Exception { eventInstanceRepository = getServiceAccessor().getEventInstanceRepository(); transactionService = getTransactionService(); transactionService.executeInTransaction(() -> { List messageInstanceIdOlderThanCreationDate = eventInstanceRepository .getMessageInstanceIdOlderThanCreationDate(System.currentTimeMillis(), new QueryOptions(0, 1000)); System.out.println("ids" + messageInstanceIdOlderThanCreationDate); eventInstanceRepository.deleteMessageInstanceByIds(messageInstanceIdOlderThanCreationDate); return null; }); } @Test public void should_create_message_with_a_creation_date() throws Exception { SMessageInstance sMessageInstance = createMessageInstance("myMessage"); SMessageInstance createdMessage = transactionService.executeInTransaction(() -> { eventInstanceRepository.createMessageInstance(sMessageInstance); return eventInstanceRepository.getMessageInstance(sMessageInstance.getId()); }); assertThat(createdMessage.getId()).isGreaterThan(0); assertThat(createdMessage.getCreationDate()).isGreaterThan(0); } private SMessageInstance createMessageInstance(String myMessage) { return new SMessageInstance(myMessage, "targetProcess", "targetFlowNode", 12345L, "fnName"); } @Test public void should_get_older_message_with_creationDate_and_no_filters() throws Exception { SMessageInstance myMessageNow = createMessageInstance("myMessage"); SMessageInstance myMessageOld2 = createMessageInstance("myMessage2"); SMessageInstance myMessageOld = createMessageInstance("myMessage"); myMessageOld2.setCreationDate(oneMinuteAgo); myMessageOld.setCreationDate(oneMinuteAgo); List ids = transactionService.executeInTransaction(() -> { eventInstanceRepository.createMessageInstance(myMessageNow); eventInstanceRepository.createMessageInstance(myMessageOld); eventInstanceRepository.createMessageInstance(myMessageOld2); return eventInstanceRepository.getMessageInstanceIdOlderThanCreationDate(oneMinuteAgo, null); }); assertThat(ids).containsExactly(myMessageOld.getId(), myMessageOld2.getId()); } @Test public void should_get_older_message_with_creationDate_messageName_filter() throws Exception { SMessageInstance myMessageNow = createMessageInstance("myMessage"); SMessageInstance myMessageOld2 = createMessageInstance("myMessage2"); SMessageInstance myMessageOld = createMessageInstance("myMessage"); myMessageOld2.setCreationDate(oneMinuteAgo); myMessageOld.setCreationDate(oneMinuteAgo); List ids = transactionService.executeInTransaction(() -> { QueryOptions queryOptions = new QueryOptions(0, 100, emptyList(), singletonList(new FilterOption(SMessageInstance.class, "messageName", "myMessage")), null); eventInstanceRepository.createMessageInstance(myMessageNow); eventInstanceRepository.createMessageInstance(myMessageOld); eventInstanceRepository.createMessageInstance(myMessageOld2); return eventInstanceRepository.getMessageInstanceIdOlderThanCreationDate(oneMinuteAgo, queryOptions); }); assertThat(ids).containsExactly(myMessageOld.getId()); } @Test public void should_delete_a_message_with_given_id() throws Exception { SMessageInstance sMessageInstance = createMessageInstance("myMessage"); SMessageInstance sMessageInstance2 = createMessageInstance("myMessage2"); transactionService.executeInTransaction(() -> { eventInstanceRepository.createMessageInstance(sMessageInstance); eventInstanceRepository.createMessageInstance(sMessageInstance2); return null; }); transactionService.executeInTransaction(() -> { eventInstanceRepository.deleteMessageInstanceByIds(singletonList(sMessageInstance.getId())); return null; }); assertThat(getMessageInstance(sMessageInstance.getId())).isNull(); assertThat(getMessageInstance(sMessageInstance2.getId())).isNotNull(); } private SMessageInstance getMessageInstance(long id) throws Exception { return transactionService.executeInTransaction(() -> eventInstanceRepository.getMessageInstance(id)); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/GatewayExecutionLocalIT.java ================================================ /** * 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.bpm; import static org.junit.Assert.assertEquals; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.junit.Test; public class GatewayExecutionLocalIT extends TestWithUser { @Test public void exclusiveGatewayFailed() throws Exception { final Expression scriptExpression = new ExpressionBuilder() .createGroovyScriptExpression("mycondition", "fzdfsdfsdfsdfsdf", Boolean.class.getName()); final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_exclusive_gateway", PROCESS_VERSION) .addActor(ACTOR_NAME).addAutomaticTask("step1").addUserTask("step2", ACTOR_NAME) .addUserTask("step3", ACTOR_NAME).addGateway("gateway1", GatewayType.EXCLUSIVE) .addTransition("step1", "gateway1") .addTransition("gateway1", "step2", scriptExpression).addDefaultTransition("gateway1", "step3") .getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final FlowNodeInstance failFlowNodeInstance = waitForFlowNodeInFailedState(processInstance); assertEquals("gateway1", failFlowNodeInstance.getName()); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/GatewayInstanceServiceIT.java ================================================ /** * 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.bpm; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SGatewayType; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeDeletionException; import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilderFactory; import org.junit.Test; /** * @author Feng Hui * @author Zhao Na */ public class GatewayInstanceServiceIT extends CommonBPMServicesTest { protected void deleteGatewayInstance(final SGatewayInstance gatewayInstance) throws SBonitaException { getTransactionService().begin(); try { getServiceAccessor().getActivityInstanceService().deleteFlowNodeInstance(gatewayInstance); } catch (final SBonitaException e) { throw new SFlowNodeDeletionException(e); } finally { getTransactionService().complete(); } } @Test public void testCreateAndGetGatewayInstance() throws SBonitaException { final SGatewayInstance gatewayInstance = BuilderFactory.get(SGatewayInstanceBuilderFactory.class) .createNewInstance("Gateway1", 1, 1, 1, SGatewayType.EXCLUSIVE, 2, 3, 3).setStateId(1) .setHitBys("a,b,c").done(); insertGatewayInstance(gatewayInstance); final SGatewayInstance gatewayInstanceRes = getGatewayInstanceFromDB(gatewayInstance.getId()); checkGateway(gatewayInstance, gatewayInstanceRes, 2, 3); deleteGatewayInstance(gatewayInstanceRes); } private SGatewayInstance getGatewayInstanceFromDB(final Long gatewayId) throws SBonitaException { getTransactionService().begin(); final SGatewayInstance gatewayInstanceRes = getServiceAccessor().getGatewayInstanceService() .getGatewayInstance(gatewayId); getTransactionService().complete(); return gatewayInstanceRes; } private void checkGateway(final SGatewayInstance gatewayInstance, final SGatewayInstance gatewayInstanceRes, final long expectedProcessDefinitionId, final long expectedProcessInstanceId) { assertNotNull(gatewayInstance); final SGatewayInstanceBuilderFactory gatewayInstanceBuilderFact = BuilderFactory .get(SGatewayInstanceBuilderFactory.class); final long actualProcessDefinitionId = gatewayInstanceRes .getLogicalGroup(gatewayInstanceBuilderFact.getProcessDefinitionIndex()); final long actualProcessInstanceId = gatewayInstanceRes .getLogicalGroup(gatewayInstanceBuilderFact.getRootProcessInstanceIndex()); assertEquals(expectedProcessDefinitionId, actualProcessDefinitionId); assertEquals(expectedProcessInstanceId, actualProcessInstanceId); assertEquals(gatewayInstance, gatewayInstanceRes); } private void updateGatewayState(final SGatewayInstance gatewayInstance, final int stateId) throws SBonitaException { getTransactionService().begin(); final SGatewayInstance gatewayInstance2 = getServiceAccessor().getGatewayInstanceService() .getGatewayInstance(gatewayInstance.getId()); getServiceAccessor().getGatewayInstanceService().setState(gatewayInstance2, stateId); getTransactionService().complete(); } private void updateGatewayHitbys(final SGatewayInstance gatewayInstance, final long transitionIndex) throws SBonitaException { getTransactionService().begin(); final SGatewayInstance gatewayInstance2 = getServiceAccessor().getGatewayInstanceService() .getGatewayInstance(gatewayInstance.getId()); getServiceAccessor().getGatewayInstanceService().hitTransition(gatewayInstance2, transitionIndex); getTransactionService().complete(); } // @Test public void testCheckMergingCondition() { // it's implement need to be improved } @Test public void testSetState() throws SBonitaException { final SGatewayInstance gatewayInstance = BuilderFactory.get(SGatewayInstanceBuilderFactory.class) .createNewInstance("Gateway1", 1, 1, 1, SGatewayType.EXCLUSIVE, 2, 3, 3).setStateId(1) .setHitBys("a,b,c").done(); insertGatewayInstance(gatewayInstance); final SGatewayInstance gatewayInstanceRes = getGatewayInstanceFromDB(gatewayInstance.getId()); checkGateway(gatewayInstance, gatewayInstanceRes, 2, 3); updateGatewayState(gatewayInstanceRes, 2); final SGatewayInstance gatewayInstanceRes2 = getGatewayInstanceFromDB(gatewayInstance.getId()); assertNotNull(gatewayInstanceRes2); assertEquals(2, gatewayInstanceRes2.getStateId()); deleteGatewayInstance(gatewayInstanceRes); } @Test public void testHitTransition() throws SBonitaException { final SGatewayInstance gatewayInstance = BuilderFactory.get(SGatewayInstanceBuilderFactory.class) .createNewInstance("Gateway1", 1, 1, 1, SGatewayType.EXCLUSIVE, 2, 3, 3).setStateId(1) .setHitBys("1,2,3").done(); insertGatewayInstance(gatewayInstance); final SGatewayInstance gatewayInstanceRes = getGatewayInstanceFromDB(gatewayInstance.getId()); checkGateway(gatewayInstance, gatewayInstanceRes, 2, 3); updateGatewayHitbys(gatewayInstanceRes, 4); final SGatewayInstance gatewayInstanceRes2 = getGatewayInstanceFromDB(gatewayInstance.getId()); assertNotNull(gatewayInstanceRes2); assertEquals("1,2,3,4", gatewayInstanceRes2.getHitBys()); deleteGatewayInstance(gatewayInstanceRes); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/NodeConfigurationIT.java ================================================ /** * 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.bpm; import static org.assertj.core.api.Assertions.assertThat; import java.util.List; import org.bonitasoft.engine.commons.PlatformRestartHandler; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.handler.SchedulerServiceRestartHandler; import org.bonitasoft.engine.platform.configuration.NodeConfiguration; import org.junit.Before; import org.junit.Test; public class NodeConfigurationIT extends CommonBPMServicesTest { public NodeConfiguration nodeConfiguration; @Before public void setup() throws BonitaException { nodeConfiguration = getServiceAccessor().getPlatformConfiguration(); } @Test public void should_have_at_least_one_restart_handler() { List platformRestartHandlers = nodeConfiguration.getPlatformRestartHandlers(); assertThat(platformRestartHandlers).hasSize(1); assertThat(platformRestartHandlers.get(0)).isInstanceOf(SchedulerServiceRestartHandler.class); } @Test public void should_clear_sessions() { assertThat(nodeConfiguration.shouldClearSessions()).isTrue(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/OperationServiceIT.java ================================================ /** * 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.bpm; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.OperationService; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.SOperatorType; import org.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilderFactory; import org.bonitasoft.engine.core.operation.model.builder.SOperationBuilderFactory; import org.bonitasoft.engine.data.ParentContainerResolverImpl; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilder; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory; import org.bonitasoft.engine.transaction.UserTransactionService; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Zhang Bole */ public class OperationServiceIT extends CommonBPMServicesTest { private UserTransactionService transactionService; private OperationService operationService; private DataInstanceService dataInstanceService; private ParentContainerResolverImpl parentContainerResolver; @Before public void setup() { transactionService = getTransactionService(); dataInstanceService = getServiceAccessor().getDataInstanceService(); operationService = getServiceAccessor().getOperationService(); parentContainerResolver = (ParentContainerResolverImpl) getServiceAccessor().getParentContainerResolver(); parentContainerResolver.setAllowUnknownContainer(true); } @After public void after() { parentContainerResolver.setAllowUnknownContainer(false); } /** * Assign a new value to a String Variable. Using an expression which is a constant. * variableName = "afterUpdate" * * @throws Exception */ @Test public void executeOperationUsingStringConstantExpression() throws Exception { final String dataInstanceName = "variableName"; final long containerId = 11L; final String containerType = "miniTask"; final String defaultValue = "beforeUpdate"; final String newConstantValue = "afterUpdate"; createStringDataInstance(dataInstanceName, containerId, containerType, defaultValue, defaultValue); SDataInstance dataInstance = getDataInstance(dataInstanceName, containerId, containerType); assertEquals(defaultValue, dataInstance.getValue()); final SOperation operation; final Map expressionContexts = new HashMap<>(); expressionContexts.put("containerId", containerId); expressionContexts.put("containerType", containerType); operation = buildAssignmentOperation(dataInstanceName, newConstantValue); executeOperation(operation, containerId, containerType, expressionContexts); dataInstance = getDataInstance(dataInstanceName, containerId, containerType); assertEquals(newConstantValue, dataInstance.getValue()); deleteDataInstance(dataInstance); } /** * Assign a new value to a List Variable. Using an expression which is a constant. * variableName.add("afterUpdate"); * * @throws Exception */ @SuppressWarnings("unchecked") @Test public void executeOperationUsingJavaMethodConstantExpression() throws Exception { final String dataInstanceName = "variableName"; final long containerId = 12L; final String containerType = "miniTask"; final String defaultValueExpressionContent = "new ArrayList();"; final String newConstantValue = "stringAddedIntoTheList"; createListDataInstance(dataInstanceName, containerId, containerType, defaultValueExpressionContent, new ArrayList()); SDataInstance dataInstance = getDataInstance(dataInstanceName, containerId, containerType); assertTrue(dataInstance.getValue() instanceof ArrayList); final SOperation operation; final Map expressionContexts = new HashMap<>(); expressionContexts.put("containerId", containerId); expressionContexts.put("containerType", containerType); operation = buildJavaMethodOperation(dataInstanceName, newConstantValue); executeOperation(operation, containerId, containerType, expressionContexts); dataInstance = getDataInstance(dataInstanceName, containerId, containerType); assertTrue(dataInstance.getValue() instanceof ArrayList); final List list = (ArrayList) dataInstance.getValue(); assertTrue(list.contains(newConstantValue)); // clean deleteDataInstance(dataInstance); } private void createListDataInstance(final String dataInstanceName, final long containerId, final String containerType, final String defaultValueExpressionConstant, final Serializable defaultValue) throws Exception { final String description = null; final SDataInstance dataInstance = buildDataInstance(dataInstanceName, ArrayList.class.getName(), description, defaultValueExpressionConstant, containerId, containerType, false, SExpression.TYPE_READ_ONLY_SCRIPT, SExpression.GROOVY, defaultValue); insertDataInstance(dataInstance); } private SOperation buildJavaMethodOperation(final String dataInstanceName, final String newConstantValue) throws SInvalidExpressionException { final SLeftOperand leftOperand = BuilderFactory.get(SLeftOperandBuilderFactory.class).createNewInstance() .setName(dataInstanceName).done(); final SExpression expression = BuilderFactory.get(SExpressionBuilderFactory.class).createNewInstance() .setContent(newConstantValue) .setExpressionType(ExpressionType.TYPE_CONSTANT.name()).setReturnType(String.class.getName()).done(); return BuilderFactory.get(SOperationBuilderFactory.class).createNewInstance() .setOperator("add:" + Object.class.getName()).setLeftOperand(leftOperand) .setType(SOperatorType.JAVA_METHOD).setRightOperand(expression).done(); } private SOperation buildAssignmentOperation(final String dataInstanceName, final String newConstantValue) throws SInvalidExpressionException { final SLeftOperand leftOperand = BuilderFactory.get(SLeftOperandBuilderFactory.class).createNewInstance() .setName(dataInstanceName).done(); final SExpression expression = BuilderFactory.get(SExpressionBuilderFactory.class).createNewInstance() .setContent(newConstantValue) .setReturnType(String.class.getName()).setExpressionType(ExpressionType.TYPE_CONSTANT.name()) .setReturnType(String.class.getName()).done(); return BuilderFactory.get(SOperationBuilderFactory.class).createNewInstance().setOperator("=") .setLeftOperand(leftOperand) .setType(SOperatorType.ASSIGNMENT) .setRightOperand(expression).done(); } private void executeOperation(final SOperation operation, final long containerId, final String containerType, final Map expressionContexts) throws Exception { transactionService.executeInTransaction((Callable) () -> { final SExpressionContext sExpressionContext = new SExpressionContext(); sExpressionContext.setSerializableInputValues(expressionContexts); sExpressionContext.setContainerId(containerId); sExpressionContext.setContainerType(containerType); operationService.execute(operation, containerId, containerType, sExpressionContext); return null; }); } private SDataInstance getDataInstance(final String dataInstanceName, final long containerId, final String containerType) throws Exception { return transactionService.executeInTransaction( () -> dataInstanceService.getDataInstance(dataInstanceName, containerId, containerType, parentContainerResolver)); } private void createStringDataInstance(final String instanceName, final long containerId, final String containerType, final String defaultValueExpressionContent, final Serializable currentDataInstanceValue) throws Exception { final SDataInstance dataInstance = buildDataInstance(instanceName, String.class.getName(), "testUpdate", defaultValueExpressionContent, containerId, containerType, false, SExpression.TYPE_CONSTANT, null, currentDataInstanceValue); insertDataInstance(dataInstance); } private SDataInstance buildDataInstance(final String instanceName, final String className, final String description, final String defaultValueExpressionContent, final long containerId, final String containerType, final boolean isTransient, final String expressionType, final String expressionInterpreter, final Serializable currentDataInstanceValue) throws SBonitaException { // create definition final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class) .createNewInstance(instanceName, className); initializeBuilder(dataDefinitionBuilder, description, defaultValueExpressionContent, className, isTransient, expressionType, expressionInterpreter); final SDataDefinition dataDefinition = dataDefinitionBuilder.done(); // create data instance final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition); return dataInstanceBuilder.setContainerId(containerId).setContainerType(containerType) .setValue(currentDataInstanceValue).done(); } private void insertDataInstance(final SDataInstance dataInstance) throws Exception { transactionService.executeInTransaction((Callable) () -> { dataInstanceService.createDataInstance(dataInstance); return null; }); } private void initializeBuilder(final SDataDefinitionBuilder dataDefinitionBuilder, final String description, final String defaultValueExpressionContent, final String defaultValueExprReturnType, final boolean isTransient, final String expressionType, final String interpreter) throws SInvalidExpressionException { SExpression expression = null; if (defaultValueExpressionContent != null) { // create expression final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class) .createNewInstance(); // this discrimination'll be changed. expreBuilder.setContent(defaultValueExpressionContent).setExpressionType(expressionType) .setReturnType(defaultValueExprReturnType); if (interpreter != null) { expreBuilder.setInterpreter(interpreter); } expression = expreBuilder.done(); } dataDefinitionBuilder.setDescription(description); dataDefinitionBuilder.setTransient(isTransient); dataDefinitionBuilder.setDefaultValue(expression); } private void deleteDataInstance(final SDataInstance dataInstance) throws Exception { transactionService.executeInTransaction((Callable) () -> { dataInstanceService.deleteDataInstance(dataInstance); return null; }); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/ProcessDefinitionServiceIT.java ================================================ /** * 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.bpm; import static org.junit.Assert.*; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; 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.builder.SProcessDefinitionDeployInfoUpdateBuilderFactory; import org.bonitasoft.engine.identity.UserSearchDescriptor; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.session.SessionService; import org.junit.Before; import org.junit.Test; /** * @author Celine Souchet */ public class ProcessDefinitionServiceIT extends CommonBPMServicesTest { private ProcessDefinitionService processDefinitionService; private ActorMappingService actorMappingService; @Before public void before() { processDefinitionService = getServiceAccessor().getProcessDefinitionService(); actorMappingService = getServiceAccessor().getActorMappingService(); } private SessionService getSessionService() { return getServiceAccessor().getSessionService(); } @Test(expected = IllegalArgumentException.class) public void storeNullProcessDefinition() throws Exception { getTransactionService().begin(); processDefinitionService.store(null); getTransactionService().complete(); } @Test public void addedDisplayName() throws Exception { final SProcessDefinition sProcessDefinition = createSProcessDefinition("myProcessName", "1.0"); getTransactionService().begin(); final SProcessDefinitionDeployInfo processDefinitionDeployInfo = processDefinitionService .getProcessDeploymentInfo(sProcessDefinition.getId()); assertEquals("myProcessName", processDefinitionDeployInfo.getName()); assertEquals("myProcessName", processDefinitionDeployInfo.getDisplayName()); // display name should be the same as name assertEquals("1.0", processDefinitionDeployInfo.getVersion()); assertEquals(ConfigurationState.RESOLVED.name(), processDefinitionDeployInfo.getConfigurationState()); assertEquals(ActivationState.ENABLED.name(), processDefinitionDeployInfo.getActivationState()); getTransactionService().complete(); // clean-up deleteSProcessDefinition(sProcessDefinition); } @Test public void updateProcessDefinitionDeployInfo() throws Exception { // create process definition final SProcessDefinition sProcessDefinition = createSProcessDefinition("myProcessName", "1.0"); final Long processId = sProcessDefinition.getId(); final String updatedDisplayName = "updateDisplayName"; getTransactionService().begin(); // update processDefinitionDeployInfo final EntityUpdateDescriptor updateDescriptor = BuilderFactory .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class).createNewInstance() .updateDisplayName(updatedDisplayName) .updateActivationState(ActivationState.ENABLED).done(); processDefinitionService.updateProcessDefinitionDeployInfo(processId, updateDescriptor); // check and do assert final SProcessDefinitionDeployInfo processDefinitionDeployInfo = processDefinitionService .getProcessDeploymentInfo(processId); assertEquals("myProcessName", processDefinitionDeployInfo.getName()); assertEquals(updatedDisplayName, processDefinitionDeployInfo.getDisplayName()); assertNotNull(processDefinitionDeployInfo.getLastUpdateDate()); assertEquals(ConfigurationState.RESOLVED.name(), processDefinitionDeployInfo.getConfigurationState()); assertEquals(ActivationState.ENABLED.name(), processDefinitionDeployInfo.getActivationState()); getTransactionService().complete(); // clean-up deleteSProcessDefinition(sProcessDefinition); } @Test(expected = SProcessDefinitionNotFoundException.class) public void updateProcessDefinitionDeployInfoThrowException() throws Exception { final String updatedDisplayName = "updateDisplayName"; // create process definition final SProcessDefinition sProcessDefinition = createSProcessDefinition("myProcessName", "1.0"); getTransactionService().begin(); // update processDefinitionDeployInfo with wrong processId final EntityUpdateDescriptor updateDescriptor = BuilderFactory .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class).createNewInstance() .updateDisplayName(updatedDisplayName).done(); try { processDefinitionService.updateProcessDefinitionDeployInfo(sProcessDefinition.getId() + 1, updateDescriptor); } finally { getTransactionService().complete(); // clean-up deleteSProcessDefinition(sProcessDefinition); } } @Test public void ifDeployedByIsTheUserId() throws Exception { final SProcessDefinition sProcessDefinition = createSProcessDefinition("testIfDeployedByIsTheUserId", "1.0"); getTransactionService().begin(); final Long processId = sProcessDefinition.getId(); final SProcessDefinitionDeployInfo processDefinitionDeployInfo = processDefinitionService .getProcessDeploymentInfo(processId); assertEquals(processDefinitionDeployInfo.getDeployedBy(), getSessionService().getSession(getAPISession().getId()).getUserId()); getTransactionService().complete(); assertEquals(processDefinitionDeployInfo.getDeployedBy(), getSessionService().getSession(getAPISession().getId()).getUserId()); getTransactionService().begin(); actorMappingService.deleteActors(processId); processDefinitionService.disableProcessDeploymentInfo(processId); processDefinitionService.delete(processId); getTransactionService().complete(); } @Test public void getProcessDefIds() throws Exception { final List sProcessDefinitions = createSProcessDefinitions(25, "testGetProcessDefIds", "0.0"); getTransactionService().begin(); List processDefIds = processDefinitionService.getProcessDefinitionIds(0, 10); assertEquals(10, processDefIds.size()); processDefIds = processDefinitionService.getProcessDefinitionIds(0, 20); assertEquals(20, processDefIds.size()); processDefIds = processDefinitionService.getProcessDefinitionIds(0, 25); assertEquals(25, processDefIds.size()); getTransactionService().complete(); // clean-up deleteSProcessDefinitions(sProcessDefinitions); } @Test public void getNumberOfUsersWhoCanStartProcessWithActorInitiator() throws Exception { final SUser sUser1 = createEnabledSUser("firstname1", "lastname1", "pwd1"); final SUser sUser2 = createEnabledSUser("firstname2", "lastname2", "pwd2"); final SProcessDefinition sProcessDefinition = createSProcessDefinitionWithSActor("process1", "1.0", "actor1", true, Arrays.asList(sUser1, sUser2)); getTransactionService().begin(); final QueryOptions searchOptions = new QueryOptions(0, 5); final long result = processDefinitionService .getNumberOfUsersWhoCanStartProcessDeploymentInfo(sProcessDefinition.getId(), searchOptions); assertEquals(2, result); getTransactionService().complete(); // clean-up deleteSProcessDefinition(sProcessDefinition); deleteSUsers(sUser1, sUser2); } @Test public void getNumberOfUsersWhoCanStartProcessWithActorNotInitiator() throws Exception { final SUser sUser1 = createEnabledSUser("firstname1", "lastname1", "pwd1"); final SUser sUser2 = createEnabledSUser("firstname2", "lastname2", "pwd2"); final SProcessDefinition sProcessDefinition = createSProcessDefinitionWithSActor("process1", "1.0", "actor1", false, Arrays.asList(sUser1, sUser2)); getTransactionService().begin(); final QueryOptions searchOptions = new QueryOptions(0, 5); final long result = processDefinitionService .getNumberOfUsersWhoCanStartProcessDeploymentInfo(sProcessDefinition.getId(), searchOptions); assertEquals(0, result); getTransactionService().complete(); // clean-up deleteSProcessDefinition(sProcessDefinition); deleteSUsers(sUser1, sUser2); } @Test public void getNumberOfUsersWhoCanStartProcessWithActorInitiatorAndFilterManagedBy() throws Exception { final SUser sUser1 = createEnabledSUser("firstname1", "lastname1", "pwd1"); final SUser sUser2 = createEnabledSUser("firstname2", "lastname2", "pwd2", sUser1.getId()); final SUser sUser3 = createEnabledSUser("firstname3", "lastname3", "pwd3", sUser2.getId()); final SUser sUser4 = createEnabledSUser("firstname4", "lastname4", "pwd4"); final SProcessDefinition sProcessDefinition = createSProcessDefinitionWithSActor("process1", "1.0", "actor1", true, Arrays.asList(sUser2, sUser3, sUser4)); getTransactionService().begin(); final FilterOption filterManagedBy = new FilterOption(SUser.class, UserSearchDescriptor.MANAGER_USER_ID, sUser1.getId()); final QueryOptions searchOptions = new QueryOptions(0, 5, null, Arrays.asList(filterManagedBy), null); final long result = processDefinitionService .getNumberOfUsersWhoCanStartProcessDeploymentInfo(sProcessDefinition.getId(), searchOptions); assertEquals(1, result); getTransactionService().complete(); // clean-up deleteSProcessDefinition(sProcessDefinition); deleteSUsers(sUser1, sUser2); } @Test public void searchUsersWhoCanStartProcessWithActorInitiator() throws Exception { final SUser sUser1 = createEnabledSUser("firstname1", "lastname1", "pwd1"); final SUser sUser2 = createEnabledSUser("firstname2", "lastname2", "pwd2"); final SProcessDefinition sProcessDefinition = createSProcessDefinitionWithSActor("process1", "1.0", "actor1", true, Arrays.asList(sUser1, sUser2)); getTransactionService().begin(); final QueryOptions searchOptions = new QueryOptions(0, 5, SUser.class, UserSearchDescriptor.FIRST_NAME, OrderByType.ASC); final List result = processDefinitionService .searchUsersWhoCanStartProcessDeploymentInfo(sProcessDefinition.getId(), searchOptions); assertEquals(sUser1, result.get(0)); assertEquals(sUser2, result.get(1)); getTransactionService().complete(); // clean-up deleteSProcessDefinition(sProcessDefinition); deleteSUsers(sUser1, sUser2); } @Test public void searchUsersWhoCanStartProcessWithActorNotInitiator() throws Exception { final SUser sUser1 = createEnabledSUser("firstname1", "lastname1", "pwd1"); final SUser sUser2 = createEnabledSUser("firstname2", "lastname2", "pwd2"); final SProcessDefinition sProcessDefinition = createSProcessDefinitionWithSActor("process1", "1.0", "actor1", false, Arrays.asList(sUser1, sUser2)); getTransactionService().begin(); final QueryOptions searchOptions = new QueryOptions(0, 5, SUser.class, UserSearchDescriptor.FIRST_NAME, OrderByType.ASC); final List result = processDefinitionService .searchUsersWhoCanStartProcessDeploymentInfo(sProcessDefinition.getId(), searchOptions); assertTrue("Users are added to a actor that isn't initiator", result.isEmpty()); getTransactionService().complete(); // clean-up deleteSProcessDefinition(sProcessDefinition); deleteSUsers(sUser1, sUser2); } @Test public void searchUsersWhoCanStartProcessWithActorInitiatorAndFilterManagedBy() throws Exception { final SUser sUser1 = createEnabledSUser("firstname1", "lastname1", "pwd1"); final SUser sUser2 = createEnabledSUser("firstname2", "lastname2", "pwd2", sUser1.getId()); final SUser sUser3 = createEnabledSUser("firstname3", "lastname3", "pwd3", sUser2.getId()); final SUser sUser4 = createEnabledSUser("firstname4", "lastname4", "pwd4"); final SProcessDefinition sProcessDefinition = createSProcessDefinitionWithSActor("process1", "1.0", "actor1", true, Arrays.asList(sUser2, sUser3, sUser4)); getTransactionService().begin(); final FilterOption filterManagedBy = new FilterOption(SUser.class, UserSearchDescriptor.MANAGER_USER_ID, sUser1.getId()); final OrderByOption orderByFirstName = new OrderByOption(SUser.class, UserSearchDescriptor.FIRST_NAME, OrderByType.ASC); final QueryOptions searchOptions = new QueryOptions(0, 5, Arrays.asList(orderByFirstName), Arrays.asList(filterManagedBy), null); final List result = processDefinitionService .searchUsersWhoCanStartProcessDeploymentInfo(sProcessDefinition.getId(), searchOptions); getTransactionService().complete(); assertEquals(sUser2, result.get(0)); // clean-up deleteSProcessDefinition(sProcessDefinition); deleteSUsers(sUser1, sUser2); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/ProcessInstanceServiceIT.java ================================================ /** * 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.bpm; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.Assert.assertEquals; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.CallableWithException; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.core.document.model.SDocument; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.core.document.model.builder.SDocumentBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilderFactory; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.data.instance.exception.SDataInstanceNotFoundException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.transaction.STransactionCommitException; import org.bonitasoft.engine.transaction.STransactionCreationException; import org.bonitasoft.engine.transaction.STransactionRollbackException; import org.bonitasoft.engine.transaction.TransactionService; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Emmanuel Duchastenier * @author Yanyan Liu */ public class ProcessInstanceServiceIT extends CommonBPMServicesTest { private final static Logger LOGGER = LoggerFactory.getLogger(CommonBPMServicesTest.class); private TransactionService transactionService; private ProcessInstanceService processInstanceService; private ActivityInstanceService activityInstanceService; @Before public void setup() { transactionService = getTransactionService(); processInstanceService = getServiceAccessor().getProcessInstanceService(); activityInstanceService = getServiceAccessor().getActivityInstanceService(); } /** * Clean up of all existing process instances */ private void cleanUpAllProcessInstances() { try { List processInstances = inTx(() -> getFirstProcessInstances(1000)); inTx(() -> { for (final SProcessInstance sProcessInstance : processInstances) { processInstanceService.deleteProcessInstance(sProcessInstance); } return null; }); List archives = inTx(() -> getFirstArchivedProcessInstances(1000)); inTx(() -> { for (final SAProcessInstance saProcessInstance : archives) { processInstanceService.deleteArchivedProcessInstance(saProcessInstance); } return null; }); } catch (final Exception e) { LOGGER.error("Error during clean-up. Ignoring..."); } } private T inTx(CallableWithException callable) throws Exception { transactionService.begin(); final T result = callable.call(); transactionService.complete(); return result; } @Test public void testGetNumberOfProcessInstances() throws STransactionCreationException, STransactionCommitException, SProcessInstanceCreationException, STransactionRollbackException, SBonitaReadException { transactionService.begin(); final long processDefinitionId = 123L; SProcessInstance sProcessInstance = SProcessInstance.builder() .name("an instance name") .processDefinitionId(processDefinitionId).build(); processInstanceService.createProcessInstance(sProcessInstance); final QueryOptions queryOptions = new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS); final long processInstanceNumber = processInstanceService.getNumberOfProcessInstances(queryOptions); transactionService.complete(); // first test with one process: assertEquals(1, processInstanceNumber); transactionService.begin(); // second test with 100 processes: for (int i = 0; i < 100; i++) { sProcessInstance = SProcessInstance.builder() .name("process instance " + i) .processDefinitionId(processDefinitionId).build(); processInstanceService.createProcessInstance(sProcessInstance); } final long numberOfProcessInstances = processInstanceService.getNumberOfProcessInstances(queryOptions); transactionService.complete(); assertEquals(101, numberOfProcessInstances); // clean up: cleanUpAllProcessInstances(); } @Test public void getCorrectProcessInstancesOrder() throws Exception { // Creation of the process instances we want to retrieve: transactionService.begin(); final long processDefinitionId = 123L; final SProcessInstance sProcessInstance0 = SProcessInstance.builder() .name("instance name 0") .processDefinitionId(processDefinitionId).build(); final SProcessInstance sProcessInstance1 = SProcessInstance.builder() .name("instance name 1") .processDefinitionId(processDefinitionId).build(); final SProcessInstance sProcessInstance2 = SProcessInstance.builder() .name("instance name 2") .processDefinitionId(processDefinitionId).build(); processInstanceService.createProcessInstance(sProcessInstance0); processInstanceService.setState(sProcessInstance0, ProcessInstanceState.STARTED); // to ensure the date is not exactly the same as the previous one: Thread.sleep(5); processInstanceService.createProcessInstance(sProcessInstance1); processInstanceService.setState(sProcessInstance1, ProcessInstanceState.STARTED); // to ensure the date is not exactly the same as the previous one: Thread.sleep(5); processInstanceService.createProcessInstance(sProcessInstance2); processInstanceService.setState(sProcessInstance2, ProcessInstanceState.STARTED); transactionService.complete(); // Retrieval of the previously created process instances: transactionService.begin(); final List processInstances = getFirstProcessInstances(20); transactionService.complete(); // Verification of the number of process instances retrieved: assertEquals(3, processInstances.size()); // Verification of the order: assertEquals(sProcessInstance0.getId(), processInstances.get(2).getId()); assertEquals(sProcessInstance1.getId(), processInstances.get(1).getId()); assertEquals(sProcessInstance2.getId(), processInstances.get(0).getId()); // clean up: cleanUpAllProcessInstances(); } @Test public void testSetState() { // TODO: not yet implemented } @Test public void testGetChildInstanceIdsOfProcessInstance() throws Exception { // first create parent process instance transactionService.begin(); final SCallActivityInstanceBuilderFactory sCallActivityInstanceBuilder = BuilderFactory .get(SCallActivityInstanceBuilderFactory.class); final long processDefinitionId = 123L; final SProcessInstance parentProcessInstance = SProcessInstance.builder() .name("an instance name") .processDefinitionId(processDefinitionId).build(); processInstanceService.createProcessInstance(parentProcessInstance); transactionService.complete(); transactionService.begin(); // second create 10 child processes: SActivityInstance activityInstance = sCallActivityInstanceBuilder .createNewCallActivityInstance("callActivity", 1, parentProcessInstance.getContainerId(), parentProcessInstance.getContainerId(), processDefinitionId, parentProcessInstance.getId(), parentProcessInstance.getId()) .done(); activityInstanceService.createActivityInstance(activityInstance); final List childInstanceIds = new ArrayList<>(); SProcessInstance childProcessInstance; for (int i = 0; i < 10; i++) { childProcessInstance = SProcessInstance.builder() .name("child process instance " + i) .processDefinitionId(processDefinitionId) .containerId(parentProcessInstance.getId()).callerId(activityInstance.getId()) .callerType(SFlowNodeType.CALL_ACTIVITY).build(); processInstanceService.createProcessInstance(childProcessInstance); childInstanceIds.add(childProcessInstance.getId()); } transactionService.complete(); // test get child by paging, order by name ASC final String nameField = SProcessInstance.NAME_KEY; transactionService.begin(); final List childInstanceIdList1 = processInstanceService.getChildInstanceIdsOfProcessInstance( parentProcessInstance.getId(), 0, 4, nameField, OrderByType.ASC); assertEquals(4, childInstanceIdList1.size()); assertEquals(childInstanceIds.get(0), childInstanceIdList1.get(0)); assertEquals(childInstanceIds.get(1), childInstanceIdList1.get(1)); assertEquals(childInstanceIds.get(2), childInstanceIdList1.get(2)); assertEquals(childInstanceIds.get(3), childInstanceIdList1.get(3)); final List childInstanceIdList2 = processInstanceService.getChildInstanceIdsOfProcessInstance( parentProcessInstance.getId(), 4, 4, nameField, OrderByType.ASC); assertEquals(4, childInstanceIdList2.size()); assertEquals(childInstanceIds.get(4), childInstanceIdList2.get(0)); assertEquals(childInstanceIds.get(5), childInstanceIdList2.get(1)); assertEquals(childInstanceIds.get(6), childInstanceIdList2.get(2)); assertEquals(childInstanceIds.get(7), childInstanceIdList2.get(3)); final List childInstanceIdList3 = processInstanceService.getChildInstanceIdsOfProcessInstance( parentProcessInstance.getId(), 8, 4, nameField, OrderByType.ASC); assertEquals(2, childInstanceIdList3.size()); assertEquals(childInstanceIds.get(8), childInstanceIdList3.get(0)); assertEquals(childInstanceIds.get(9), childInstanceIdList3.get(1)); // test DESC final List childInstanceIdList4 = processInstanceService.getChildInstanceIdsOfProcessInstance( parentProcessInstance.getId(), 0, 4, nameField, OrderByType.DESC); assertEquals(4, childInstanceIdList4.size()); assertEquals(childInstanceIds.get(9), childInstanceIdList4.get(0)); assertEquals(childInstanceIds.get(8), childInstanceIdList4.get(1)); assertEquals(childInstanceIds.get(7), childInstanceIdList4.get(2)); assertEquals(childInstanceIds.get(6), childInstanceIdList4.get(3)); activityInstance = activityInstanceService.getActivityInstance(activityInstance.getId()); activityInstanceService.deleteFlowNodeInstance(activityInstance); transactionService.complete(); // clean up: cleanUpAllProcessInstances(); } @Test public void testGetNumberOfChildInstancesOfProcessInstance() throws Exception { // first create parent process instance and test transactionService.begin(); final SCallActivityInstanceBuilderFactory sCallActivityInstanceBuilder = BuilderFactory .get(SCallActivityInstanceBuilderFactory.class); final long processDefinitionId = 123L; final SProcessInstance parentProcessInstance = SProcessInstance.builder() .name("an instance name") .processDefinitionId(processDefinitionId).build(); processInstanceService.createProcessInstance(parentProcessInstance); long numberOfChild = processInstanceService .getNumberOfChildInstancesOfProcessInstance(parentProcessInstance.getId()); transactionService.complete(); assertEquals(0, numberOfChild); transactionService.begin(); // second create 10 child processes: final List childInstanceIds = new ArrayList<>(); SProcessInstance childProcessInstance; SActivityInstance activityInstance = sCallActivityInstanceBuilder .createNewCallActivityInstance("callActivity", 1, parentProcessInstance.getContainerId(), parentProcessInstance.getContainerId(), processDefinitionId, parentProcessInstance.getId(), parentProcessInstance.getId()) .done(); activityInstanceService.createActivityInstance(activityInstance); for (int i = 0; i < 10; i++) { childProcessInstance = SProcessInstance.builder() .name("child process instance " + i) .processDefinitionId(processDefinitionId) .containerId(parentProcessInstance.getId()).callerId(activityInstance.getId()) .callerType(SFlowNodeType.CALL_ACTIVITY).build(); processInstanceService.createProcessInstance(childProcessInstance); childInstanceIds.add(childProcessInstance.getId()); } numberOfChild = processInstanceService .getNumberOfChildInstancesOfProcessInstance(parentProcessInstance.getId()); activityInstanceService.deleteFlowNodeInstance(activityInstance); transactionService.complete(); assertEquals(childInstanceIds.size(), numberOfChild); // clean up: cleanUpAllProcessInstances(); } @Test public void testDeleteProcessInstance() throws Exception { //given createProcessInstanceInTransaction(123L, "an instance name"); //when cleanUpAllProcessInstances(); //then assertEquals(0, getNumberOfProcessInstances()); } @Test public void testDeleteProcessInstanceAlsoDeleteDataInstances() throws Exception { final long processDefinitionId = 123123123L; final String processName = "myProcInst"; // create a process instance final SProcessInstance processInstance = createProcessInstanceInTransaction(processDefinitionId, processName); // create a data instance having the process instance as container final SDataInstance globalDataInstance = createDataInTransaction("myData", String.class.getName(), processInstance.getId(), DataInstanceContainer.PROCESS_INSTANCE); // create a automatic task final SActivityInstance taskInstance = createSAutomaticTaskInstance( "auto", 1234L, processInstance.getId(), processDefinitionId, processInstance.getId()); final SDataInstance localDataInstance = createDataInTransaction("myLocalData", String.class.getName(), taskInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE); // delete the process instance: the data instance is supposed to be deleted at same time deleteSProcessInstance(processInstance); // check that no more data is available for the deleted process instance and flow node instance assertEquals(0, getNumberOfProcessInstances()); checkDataDoesNotExist(globalDataInstance); checkDataDoesNotExist(localDataInstance); checkFlowNodeDoesNotExist(taskInstance); } @Test public void testDeleteProcessInstanceAlsoDeleteDocumentMappings() throws Exception { //given final long processDefinitionId = 123123123L; final String processName = "myProcInst"; // create a process instance final SProcessInstance processInstance = createProcessInstanceInTransaction(processDefinitionId, processName); // attached a document to the process instance SDocument document = new SDocumentBuilderFactory() .createNewProcessDocument("myDocument", "mimeType", 1234L, "content".getBytes()).done(); transactionService.begin(); SMappedDocument mappedDocument = getServiceAccessor().getDocumentService() .attachDocumentToProcessInstance(document, processInstance.getId(), document.getFileName(), "a description"); transactionService.complete(); //when deleteSProcessInstance(processInstance); //then assertEquals(0, getNumberOfProcessInstances()); transactionService.begin(); // assert document mapping does not exist assertThatExceptionOfType(SObjectNotFoundException.class) .isThrownBy(() -> getServiceAccessor().getDocumentService().getMappedDocument(mappedDocument.getId())); transactionService.complete(); } private void checkDataDoesNotExist(final SDataInstance dataInstance) { assertThatExceptionOfType(SDataInstanceNotFoundException.class) .as("the data instance was not deleted") .isThrownBy(() -> getDataInstanceInTransaction(dataInstance.getId())); } private void checkFlowNodeDoesNotExist(final SFlowNodeInstance flowNodeInstance) { assertThatExceptionOfType(SFlowNodeNotFoundException.class) .as("the flowNode instance was not deleted") .isThrownBy(() -> getFlowNodeInstance(flowNodeInstance.getId())); } private SProcessInstance createProcessInstanceInTransaction(final long process_definition_id, final String processName) throws STransactionCreationException, SProcessInstanceCreationException, STransactionCommitException, STransactionRollbackException { getTransactionService().begin(); // Creation of a process instance: final SProcessInstance processInstance = SProcessInstance.builder() .name(processName) .processDefinitionId(process_definition_id).build(); processInstanceService.createProcessInstance(processInstance); getTransactionService().complete(); return processInstance; } private SDataInstance createDataInTransaction(final String dataName, final String dataType, final long containerId, final DataInstanceContainer containerType) throws SBonitaException { getTransactionService().begin(); final SDataDefinitionBuilder dataDefBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class) .createNewInstance(dataName, dataType); final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefBuilder.done()); dataInstanceBuilder.setContainerId(containerId); dataInstanceBuilder.setContainerType(containerType.name()); final SDataInstance dataInstance = dataInstanceBuilder.done(); getServiceAccessor().getDataInstanceService().createDataInstance(dataInstance); getTransactionService().complete(); return dataInstance; } private SDataInstance getDataInstanceInTransaction(final long dataInstanceId) throws SBonitaException { SDataInstance dataInstance; getTransactionService().begin(); try { dataInstance = getServiceAccessor().getDataInstanceService().getDataInstance(dataInstanceId); } finally { getTransactionService().complete(); } return dataInstance; } private long getNumberOfProcessInstances() throws SBonitaException { transactionService.begin(); final QueryOptions queryOptions = new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS); final long processInstanceNumber = processInstanceService.getNumberOfProcessInstances(queryOptions); transactionService.complete(); return processInstanceNumber; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/RecoveryMechanismIT.java ================================================ /** * Copyright (C) 2020 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.bpm; import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.TimeUnit; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.tenant.restart.RecoveryService; import org.bonitasoft.engine.test.CommonAPILocalIT; import org.junit.After; import org.junit.Before; import org.junit.Test; public class RecoveryMechanismIT extends CommonAPILocalIT { private RecoveryService recoveryService; @Before public void before() throws BonitaException { loginWithTechnicalUser(); recoveryService = getServiceAccessor().lookup("recoveryService"); recoveryService.setConsiderElementsOlderThan("PT0S"); } @After public void after() { recoveryService.setConsiderElementsOlderThan("PT1H"); } @Test public void should_recover_elements_after_incident() throws Exception { ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("processRecovered", "1.0"); definitionBuilder.addAutomaticTask("auto1").addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(100)); ProcessDefinition processDefinition = deployAndEnableProcess(definitionBuilder.done()); ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); TimeUnit.MILLISECONDS.sleep(100); getServiceAccessor().getWorkExecutorService().pause(); getServiceAccessor().getWorkExecutorService().resume(); TimeUnit.MILLISECONDS.sleep(500); assertThat(getProcessAPI().getProcessInstance(processInstance.getId()).getState()) .isEqualToIgnoringCase(ProcessInstanceState.STARTED.name()); recoveryService.recoverAllElements(); waitForProcessToFinish(processInstance); recoveryService.recoverAllElements(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/SupervisorServiceIT.java ================================================ /** * 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.bpm; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.supervisor.mapping.SSupervisorNotFoundException; import org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; import org.bonitasoft.engine.transaction.TransactionService; import org.junit.Assert; import org.junit.Before; import org.junit.Test; /** * @author Yanyan Liu * @author Elias Ricken de Medeiros */ public class SupervisorServiceIT extends CommonBPMServicesTest { private TransactionService transactionService; private static SupervisorMappingService supervisorService; private IdentityService identityService; final long processDefId = 123L; @Before public void setup() { this.transactionService = getTransactionService(); supervisorService = getServiceAccessor().getSupervisorService(); this.identityService = getServiceAccessor().getIdentityService(); } @Test public void testCreateAndGetSupervisor() throws Exception { final SUser user = createSUser("zé", "bpm"); final SProcessSupervisor createdSupervisor = createUserSupervisors(Collections.singletonList(user)).get(0); final SProcessSupervisor gotSupervisor = getSSupervisor(createdSupervisor.getId()); Assert.assertEquals(createdSupervisor, gotSupervisor); deleteSupervisor(createdSupervisor); deleteSUser(user); } @Test public void testCreateRoleSupervisor() throws Exception { final SRole role = createSRole("role1"); final SProcessSupervisor createdSupervisor = createRoleSupervisors(Collections.singletonList(role)).get(0); final SProcessSupervisor gotSupervisor = getSSupervisor(createdSupervisor.getId()); Assert.assertEquals(createdSupervisor, gotSupervisor); deleteSupervisor(createdSupervisor); deleteSRole(role); } private SGroup createSGroup(final String groupName) throws SBonitaException { this.transactionService.begin(); final SGroup group = SGroup.builder().name(groupName).build(); this.identityService.createGroup(group, null, null); this.transactionService.complete(); return group; } @Test public void testCreateGroupSupervisor() throws Exception { final SGroup group = createSGroup("group1"); final SProcessSupervisor createdSupervisor = createGroupSupervisors(Collections.singletonList(group)).get(0); final SProcessSupervisor gotSupervisor = getSSupervisor(createdSupervisor.getId()); Assert.assertEquals(createdSupervisor, gotSupervisor); deleteSupervisor(createdSupervisor); deleteSGroup(group); } @Test public void testCreateMembershipSupervisor() throws Exception { final SRole role = createSRole("role1"); final SGroup group = createSGroup("group1"); final SProcessSupervisor createdSupervisor = createRoleAndGroupSupervisors( Collections.singletonMap(role.getId(), group.getId())).get(0); final SProcessSupervisor gotSupervisor = getSSupervisor(createdSupervisor.getId()); Assert.assertEquals(createdSupervisor, gotSupervisor); // clean-up deleteSupervisor(gotSupervisor); deleteSGroup(group); deleteSRole(role); } // FIXME with hibernate the exception arrives only when transaction.complete is called, // with mybatis the exception arrives before calling transaction.complete // we don't get the same exception. todo: investigate this and not use generic exception @Test(expected = Exception.class) public void testCreateSupervisorWithSSupervisorCreationException() throws Exception { final SUser user = createSUser("zeca", "bpm"); final SProcessSupervisor createdSupervisor = createUserSupervisors(Collections.singletonList(user)).get(0); try { createUserSupervisors(Collections.singletonList(user)); } finally { // clean-up deleteSupervisor(createdSupervisor); deleteSUser(user); } } @Test(expected = SSupervisorNotFoundException.class) public void testGetSupervisorThrowException() throws Exception { this.transactionService.begin(); supervisorService.getProcessSupervisor(-1); this.transactionService.complete(); } @Test(expected = SSupervisorNotFoundException.class) public void testDeleteSupervisor() throws Exception { final SUser user = createSUser("zeca", "bpm"); final SProcessSupervisor createdSupervisor = createUserSupervisors(Collections.singletonList(user)).get(0); this.transactionService.begin(); final SProcessSupervisor gotSupervisor = supervisorService.getProcessSupervisor(createdSupervisor.getId()); Assert.assertEquals(createdSupervisor, gotSupervisor); supervisorService.deleteProcessSupervisor(gotSupervisor.getId()); try { supervisorService.getProcessSupervisor(createdSupervisor.getId()); Assert.fail("supervisor not deleted successfully!"); } finally { this.transactionService.complete(); deleteSUser(user); } } @Test(expected = SSupervisorNotFoundException.class) public void testDeleteSupervisorThrowException() throws Exception { this.transactionService.begin(); supervisorService.deleteProcessSupervisor(-1); this.transactionService.complete(); } private List createUserSupervisors(final List users) throws Exception { final List supervisorList = new ArrayList<>(); for (final SUser sUser : users) { final SProcessSupervisor supervisor = SProcessSupervisor.builder().processDefId(this.processDefId) .userId(sUser.getId()).build(); final SProcessSupervisor createdSupervisor = createSupervisor(supervisor); supervisorList.add(createdSupervisor); } return supervisorList; } private SProcessSupervisor createSupervisor(final SProcessSupervisor supervisor) throws SBonitaException { this.transactionService.begin(); SProcessSupervisor createdSupervisor; try { createdSupervisor = supervisorService.createProcessSupervisor(supervisor); } finally { this.transactionService.complete(); } return createdSupervisor; } private List createRoleSupervisors(final List roles) throws Exception { final List supervisorList = new ArrayList<>(); this.transactionService.begin(); for (final SRole sRole : roles) { final SProcessSupervisor supervisor = SProcessSupervisor.builder().processDefId(this.processDefId) .roleId(sRole.getId()).build(); final SProcessSupervisor createdSupervisor = supervisorService.createProcessSupervisor(supervisor); supervisorList.add(createdSupervisor); } this.transactionService.complete(); return supervisorList; } private List createGroupSupervisors(final List groups) throws Exception { final List supervisorList = new ArrayList<>(); this.transactionService.begin(); for (final SGroup sGroup : groups) { final SProcessSupervisor supervisor = SProcessSupervisor.builder().processDefId(this.processDefId) .groupId(sGroup.getId()).build(); final SProcessSupervisor createdSupervisor = supervisorService.createProcessSupervisor(supervisor); supervisorList.add(createdSupervisor); } this.transactionService.complete(); return supervisorList; } private List createRoleAndGroupSupervisors(final Map roleGroupMap) throws Exception { final List supervisorList = new ArrayList<>(); this.transactionService.begin(); for (final Entry roleGroup : roleGroupMap.entrySet()) { final SProcessSupervisor supervisor = SProcessSupervisor.builder().processDefId(this.processDefId) .roleId(roleGroup.getKey()) .groupId(roleGroup.getValue()).build(); final SProcessSupervisor createdSupervisor = supervisorService.createProcessSupervisor(supervisor); supervisorList.add(createdSupervisor); } this.transactionService.complete(); return supervisorList; } @Test public void testSearchProcessDefSupervisorsInOrder() throws Exception { final List users = new ArrayList<>(5); users.add(createSUser("roberto", "bpm")); users.add(createSUser("joao", "bpm")); users.add(createSUser("maria", "bpm")); users.add(createSUser("paula", "bpm")); users.add(createSUser("julia", "bpm")); final List createdSupervisorList = createUserSupervisors(users); assertEquals(5, createdSupervisorList.size()); this.transactionService.begin(); final List orderByOptions = Collections .singletonList(new OrderByOption(SProcessSupervisor.class, SProcessSupervisor.USER_ID_KEY, OrderByType.DESC)); final List filterOptions = Collections .singletonList(new FilterOption(SProcessSupervisor.class, SProcessSupervisor.PROCESS_DEF_ID_KEY, this.processDefId)); // test ASC final QueryOptions searchOptions = new QueryOptions(0, 6, orderByOptions, filterOptions, null); final List gotSupervisorList1 = supervisorService.searchProcessSupervisors(searchOptions); assertEquals(5, gotSupervisorList1.size()); assertEquals(createdSupervisorList.get(4).getId(), gotSupervisorList1.get(0).getId()); assertEquals(createdSupervisorList.get(3).getId(), gotSupervisorList1.get(1).getId()); assertEquals(createdSupervisorList.get(2).getId(), gotSupervisorList1.get(2).getId()); assertEquals(createdSupervisorList.get(1).getId(), gotSupervisorList1.get(3).getId()); assertEquals(createdSupervisorList.get(0).getId(), gotSupervisorList1.get(4).getId()); for (final SProcessSupervisor supervisor : createdSupervisorList) { supervisorService.deleteProcessSupervisor(supervisor.getId()); } this.transactionService.complete(); deleteSUsers(users); } private boolean isUserProcessSupervisor(final SUser user) throws SBonitaException { this.transactionService.begin(); final boolean isUserSupervisor = supervisorService.isProcessSupervisor(this.processDefId, user.getId()); this.transactionService.complete(); return isUserSupervisor; } @Test public void testIsUserProcessSupervisor() throws Exception { final SUser user = createSUser("paula", "bpm"); assertFalse(isUserProcessSupervisor(user)); final SProcessSupervisor userSupervisor = createUserSupervisors(Collections.singletonList(user)).get(0); assertTrue(isUserProcessSupervisor(user)); deleteSupervisor(userSupervisor); deleteSUser(user); } @Test public void testIsUserProcessSupervisorFromGroup() throws Exception { final SUser user = createSUser("paula", "bpm"); final SGroup group = createSGroup("group1"); final SRole role = createSRole("role1"); createSUserMembership(user, group, role); assertFalse(isUserProcessSupervisor(user)); final SProcessSupervisor supervisor = createGroupSupervisors(Collections.singletonList(group)).get(0); assertTrue(isUserProcessSupervisor(user)); deleteSupervisor(supervisor); deleteSUser(user); deleteSGroup(group); deleteSRole(role); } @Test public void testIsUserProcessSupervisorFromRole() throws Exception { final SUser user = createSUser("paula", "bpm"); final SGroup group = createSGroup("group1"); final SRole role = createSRole("role1"); createSUserMembership(user, group, role); assertFalse(isUserProcessSupervisor(user)); final SProcessSupervisor supervisor = createRoleSupervisors(Collections.singletonList(role)).get(0); assertTrue(isUserProcessSupervisor(user)); deleteSupervisor(supervisor); deleteSUser(user); deleteSGroup(group); deleteSRole(role); } @Test public void testIsUserProcessSupervisorFromRoleAndGroup() throws Exception { final SUser user1 = createSUser("paula", "bpm"); final SUser user2 = createSUser("julia", "bpm"); final SGroup group1 = createSGroup("group1"); final SGroup group2 = createSGroup("group2"); final SRole role1 = createSRole("role1"); final SRole role2 = createSRole("role2"); createSUserMembership(user1, group1, role1); createSUserMembership(user2, group1, role2); createSUserMembership(user2, group2, role1); assertFalse(isUserProcessSupervisor(user1)); assertFalse(isUserProcessSupervisor(user2)); final SProcessSupervisor supervisor = createRoleAndGroupSupervisors( Collections.singletonMap(role1.getId(), group1.getId())).get(0); assertTrue(isUserProcessSupervisor(user1)); assertFalse(isUserProcessSupervisor(user2)); deleteSupervisor(supervisor); deleteSUsers(user1, user2); deleteSGroups(group1, group2); deleteSRoles(role1, role2); } private SProcessSupervisor getSSupervisor(final long supervisorId) throws SBonitaException { this.transactionService.begin(); final SProcessSupervisor supervisor = supervisorService.getProcessSupervisor(supervisorId); this.transactionService.complete(); return supervisor; } private void deleteSupervisor(final SProcessSupervisor supervisor) throws SBonitaException { this.transactionService.begin(); supervisorService.deleteProcessSupervisor(supervisor); this.transactionService.complete(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/connector/ConnectorExecutionsLocalIT.java ================================================ /** * 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.bpm.connector; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.archive.model.TestLogBuilder; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.TimerType; import org.bonitasoft.engine.bpm.flownode.impl.internal.FlowElementContainerDefinitionImpl; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.connectors.ConnectorExecutionIT; import org.bonitasoft.engine.connectors.TestConnector; import org.bonitasoft.engine.connectors.TestConnector3; import org.bonitasoft.engine.connectors.VariableStorage; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.operation.LeftOperand; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.test.BlockingConnector; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.test.TestStates; import org.bonitasoft.engine.test.WaitUntil; import org.junit.Test; /** * @author Baptiste Mesta * @author Celine Souchet * @author Elias Ricken de Medeiros */ @SuppressWarnings("javadoc") public class ConnectorExecutionsLocalIT extends ConnectorExecutionIT { protected ServiceAccessor getServiceAccessor() { try { return ServiceAccessorSingleton.getInstance(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } @Test public void executeConnectorOnFinishOfAnAutomaticActivityWithDataAsInput() throws Exception { final String valueOfInput1 = "valueOfInput1"; final String dataName = "myData1"; final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder().createNewInstance( "executeConnectorOnFinishOfAnAutomaticActivityWithDataAsInput", "1.0"); designProcessDefinition.addShortTextData(dataName, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); designProcessDefinition.addActor(ACTOR_NAME); designProcessDefinition.addAutomaticTask("step1") .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_FINISH) .addInput(TestConnector.INPUT1, new ExpressionBuilder().createDataExpression(dataName, String.class.getName())); designProcessDefinition.addUserTask("step2", ACTOR_NAME); designProcessDefinition.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(designProcessDefinition, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance, "step2"); waitForVariableStorage(TestConnector.INPUT1, valueOfInput1); disableAndDeleteProcess(processDefinition); } @Test public void executeSeveralConnectorsOnUserTaskOnStart() throws Exception { final String valueOfInput1 = "valueOfInput1"; final String valueOfInput2 = "valueOfInput2"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeSeveralConnectorsOnUserTaskOnStart", "1.0"); processBuilder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder taskBuilder = new UserTaskDefinitionBuilder(processBuilder, (FlowElementContainerDefinitionImpl) processBuilder .getProcess().getProcessContainer(), "step1", ACTOR_NAME); taskBuilder .addConnector("myConnector1", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_ENTER) .addInput(TestConnector3.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); taskBuilder .addConnector("myConnector2", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_ENTER) .addInput(TestConnector3.INPUT2, new ExpressionBuilder().createConstantStringExpression(valueOfInput2)); processBuilder.addUserTask("step2", ACTOR_NAME).addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector3(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1); waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2); assignAndExecuteStep(step1Id, user); waitForUserTaskAndExecuteIt(processInstance, "step2", user); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Test public void executeSeveralConnectorsOnAutomaticTaskOnStart() throws Exception { final String valueOfInput1 = "valueOfInput1"; final String valueOfInput2 = "valueOfInput2"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance( "executeSeveralConnectorsOnAutomaticTaskOnStart", "1.0"); processBuilder.addActor(ACTOR_NAME); final AutomaticTaskDefinitionBuilder taskBuilder = new AutomaticTaskDefinitionBuilder(processBuilder, (FlowElementContainerDefinitionImpl) processBuilder.getProcess().getProcessContainer(), "step1"); taskBuilder .addConnector("myConnector1", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_ENTER) .addInput(TestConnector3.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); taskBuilder .addConnector("myConnector2", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_ENTER) .addInput(TestConnector3.INPUT2, new ExpressionBuilder().createConstantStringExpression(valueOfInput2)); processBuilder.addAutomaticTask("step2").addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector3(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1); waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Test public void executeSeveralConnectorsOnUserTaskOnFinish() throws Exception { final String valueOfInput1 = "valueOfInput1"; final String valueOfInput2 = "valueOfInput2"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeSeveralConnectorsOnUserTaskOnFinish", "1.0"); processBuilder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder taskBuilder = processBuilder.addUserTask("step1", ACTOR_NAME); taskBuilder.addConnector("myConnector1", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_FINISH).addInput(TestConnector3.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); taskBuilder.addConnector("myConnector2", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_FINISH).addInput(TestConnector3.INPUT2, new ExpressionBuilder().createConstantStringExpression(valueOfInput2)); processBuilder.addUserTask("step2", ACTOR_NAME).addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector3(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, "step1", user); final long step2Id = waitForUserTask(processInstance, "step2"); waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1); waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2); assignAndExecuteStep(step2Id, userId); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Test public void executeSeveralConnectorsOnAutomaticTaskOnFinish() throws Exception { final String valueOfInput1 = "valueOfInput1"; final String valueOfInput2 = "valueOfInput2"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance( "executeSeveralConnectorsOnAutomaticTaskOnFinish", "1.0"); processBuilder.addActor(ACTOR_NAME); final AutomaticTaskDefinitionBuilder taskBuilder = processBuilder.addAutomaticTask("step1"); taskBuilder.addConnector("myConnector1", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_FINISH).addInput("input1", new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); taskBuilder.addConnector("myConnector2", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_FINISH).addInput("input2", new ExpressionBuilder().createConstantStringExpression(valueOfInput2)); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector3(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1); waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Test public void executeSeveralConnectorsOnStartAndOnFinishWithDataInput() throws Exception { final String valueOfInput1 = "valueOfInput1"; final String valueOfInput2 = "valueOfInput2"; final String valueOfInput3 = "valueOfInput3"; final String valueOfInput4 = "valueOfInput4"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeSeveralConnectorsOnStartAndOnFinish", "1.0"); processBuilder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder taskBuilder = processBuilder.addUserTask("step1", ACTOR_NAME); taskBuilder .addConnector("myConnector1", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_ENTER) .addInput(TestConnector3.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); taskBuilder .addConnector("myConnector2", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_ENTER) .addInput(TestConnector3.INPUT2, new ExpressionBuilder().createConstantStringExpression(valueOfInput2)); taskBuilder.addConnector("myConnector3", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_FINISH).addInput(TestConnector3.INPUT3, new ExpressionBuilder().createConstantStringExpression(valueOfInput3)); taskBuilder.addConnector("myConnector4", "org.bonitasoft.connector.testConnector3", "1.0", ConnectorEvent.ON_FINISH).addInput(TestConnector3.INPUT4, new ExpressionBuilder().createConstantStringExpression(valueOfInput4)); processBuilder.addUserTask("step2", ACTOR_NAME).addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector3(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1); waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2); assignAndExecuteStep(step1Id, userId); final long step2Id = waitForUserTask(processInstance, "step2"); waitForVariableStorage(TestConnector3.INPUT1, valueOfInput1); waitForVariableStorage(TestConnector3.INPUT2, valueOfInput2); assignAndExecuteStep(step2Id, userId); waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Test public void executeConnectorOnFinishOfAnAutomaticActivity() throws Exception { final String valueOfInput1 = "valueOfInput1"; final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( "executeConnectorOnStartOfAnAutomaticActivity", "1.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addAutomaticTask("step1") .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_FINISH) .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processDefinitionBuilder, ACTOR_NAME, user); final ProcessInstance startProcess = getProcessAPI().startProcess(processDefinition.getId()); waitForVariableStorage(TestConnector.INPUT1, valueOfInput1); waitForProcessToFinish(startProcess); disableAndDeleteProcess(processDefinition); } @Test public void executeConnectorOnEnterOfAnUserTask() throws Exception { final String valueOfInput1 = "valueOfInput1"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance( "executeConnectorOnStartOfAnUserActivity", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask("step1", ACTOR_NAME) .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_ENTER) .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); getProcessAPI().startProcess(processDefinition.getId()); waitForVariableStorage(TestConnector.INPUT1, valueOfInput1); disableAndDeleteProcess(processDefinition); } @Test public void connectorsAreDeletedAfterTaskCompletion() throws Exception { // deploy process final String taskName = "step1"; final ProcessDefinition processDefinition = deployProcessWithConnectorOnUserTask(user, taskName); // start the process final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // wait for step containing the connector and execute it final long step1Id = waitForUserTaskAndExecuteIt(processInstance, taskName, user); waitForUserTask(processInstance, "step2"); // check that there are no more connector instances final SearchResult searchResult = searchConnectors(step1Id, ConnectorInstance.FLOWNODE_TYPE, 10); assertEquals(0, searchResult.getCount()); // clean up disableAndDeleteProcess(processDefinition); } private SearchResult searchConnectors(final long containerId, final String containerType, final int maxResults) throws SearchException { final SearchOptionsBuilder optionsBuilder = new SearchOptionsBuilder(0, maxResults); optionsBuilder.filter(ConnectorInstancesSearchDescriptor.CONTAINER_ID, containerId); optionsBuilder.filter(ConnectorInstancesSearchDescriptor.CONTAINER_TYPE, containerType); return getProcessAPI().searchConnectorInstances(optionsBuilder.done()); } private ProcessDefinition deployProcessWithConnectorOnUserTask(final User user, final String taskName) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance( "executeConnectorOnStartOfAnAutomaticActivity", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask(taskName, ACTOR_NAME) .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_ENTER) .addInput("input1", new ExpressionBuilder().createConstantStringExpression("valueOfInput1")); processBuilder.addUserTask("step2", ACTOR_NAME).addTransition(taskName, "step2"); return deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); } @Test public void executeConnectorOnFinishOfAnUserTask() throws Exception { final String valueOfInput1 = "valueOfInput1"; // Configure process and human tasks final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnFinishOfAnUserTask", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask("step1", ACTOR_NAME) .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_FINISH) .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); processBuilder.addUserTask("step2", ACTOR_NAME); processBuilder.addTransition("step1", "step2"); // Deploy and start process final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // Assign human task with connector final long step1Id = waitForUserTaskAndAssignIt(processInstance, "step1", user).getId(); // Check that the "input1" variable has no value for "valueOfInput1" final WaitUntil waitUntil = waitForVariableStorage(50, 800, TestConnector.INPUT1, valueOfInput1); assertFalse(waitUntil.waitUntil()); // Run Started state of the human task getProcessAPI().executeFlowNode(step1Id); waitForArchivedActivity(step1Id, TestStates.NORMAL_FINAL); // Check that the "input1" variable has value for "valueOfInput1", in Started state of human task waitForVariableStorage(TestConnector.INPUT1, valueOfInput1); // Remove all for the end disableAndDeleteProcess(processDefinition); } @Test public void executeConnectorOnFinishStateOfAnUserTaskWithTimerEvent() throws Exception { final String valueOfInput1 = "valueOfInput1"; // Configure process and human tasks final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance( "executeConnectorOnFinishStateOfAnUserTaskWithTimerEvent", "1.0"); processBuilder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processBuilder.addUserTask("step1", ACTOR_NAME); userTaskDefinitionBuilder .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_FINISH) .addInput( TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); processBuilder.addStartEvent("start"); userTaskDefinitionBuilder.addBoundaryEvent("timer", true).addTimerEventTriggerDefinition(TimerType.DURATION, new ExpressionBuilder().createConstantLongExpression(30000)); userTaskDefinitionBuilder.addUserTask("exceptionStep", ACTOR_NAME); processBuilder.addEndEvent("end"); processBuilder.addUserTask("step2", ACTOR_NAME); processBuilder.addTransition("start", "step1"); processBuilder.addTransition("step1", "step2"); processBuilder.addTransition("step2", "end"); processBuilder.addTransition("timer", "exceptionStep"); // Deploy and start process final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); // Assign human task with connector final ActivityInstance step1 = waitForUserTaskAndAssignIt(processInstance, "step1", user); // Check that the "input1" variable has no value for "valueOfInput1", in Ready state of human task final WaitUntil waitUntil = waitForVariableStorage(50, 800, TestConnector.INPUT1, valueOfInput1); assertFalse(waitUntil.waitUntil()); // Run Started state of the human task getProcessAPI().executeFlowNode(step1.getId()); waitForUserTask(processInstance, "step2"); // Check that the "input1" variable has value for "valueOfInput1", in Started state of human task waitForVariableStorage(TestConnector.INPUT1, valueOfInput1); // Remove all for the end disableAndDeleteProcess(processDefinition); } @SuppressWarnings("deprecation") private WaitUntil waitForVariableStorage(final int repeatEach, final int timeout, final String inputName, final String valueOfInput) { final WaitUntil waitUntil = new WaitUntil(repeatEach, timeout, false) { @Override protected boolean check() { return VariableStorage.getInstance().getVariableValue(inputName).equals(valueOfInput); } }; return waitUntil; } @SuppressWarnings("deprecation") private void waitForVariableStorage(final String inputName, final String valueOfInput) throws Exception { final WaitUntil waitUntil = waitForVariableStorage(50, 5000, inputName, valueOfInput); assertTrue(waitUntil.waitUntil()); } @Test public void executeConnectorOnEnterOfProcess() throws Exception { final String valueOfInput1 = "valueOfInput1"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance( "executeConnectorOnStartOfAnAutomaticActivity", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_ENTER) .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); processBuilder.addAutomaticTask("step1"); processBuilder.addUserTask("step2", ACTOR_NAME); processBuilder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForVariableStorage(TestConnector.INPUT1, valueOfInput1); waitForUserTask(processInstance, "step2"); disableAndDeleteProcess(processDefinition); } @Test public void executeConnectorOnFinishOfAProcess() throws Exception { final String valueOfInput = "valueOfInput1"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance( "executeConnectorOnStartOfAnAutomaticActivity", PROCESS_VERSION); processBuilder.addActor(ACTOR_NAME); processBuilder .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_FINISH) .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput)); processBuilder.addUserTask("step1", ACTOR_NAME); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); final WaitUntil waitUntil = waitForVariableStorage(50, 800, TestConnector.INPUT1, valueOfInput); assertFalse(waitUntil.waitUntil()); assignAndExecuteStep(step1Id, userId); waitForVariableStorage(TestConnector.INPUT1, valueOfInput); // Clean up waitForProcessToFinish(processInstance); disableAndDeleteProcess(processDefinition); } @Test public void connectorsAreDeletedAfterProcessCompletion() throws Exception { // deploy the a process with a connector final String taskName = "step1"; final ProcessDefinition processDefinition = deployProcessWithConnectorOnFinish(user, taskName); // execute the process final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTaskAndExecuteIt(processInstance, taskName, user); waitForProcessToFinish(processInstance); // check there are no connector instances final SearchResult searchResult = searchConnectors(processInstance.getId(), ConnectorInstance.PROCESS_TYPE, 10); assertEquals(0, searchResult.getCount()); // clean up disableAndDeleteProcess(processDefinition); } private ProcessDefinition deployProcessWithConnectorOnFinish(final User user, final String taskName) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance( "executeConnectorOnStartOfAnAutomaticActivity", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_FINISH) .addInput("input1", new ExpressionBuilder().createConstantStringExpression("valueOfInput1")); processBuilder.addUserTask(taskName, ACTOR_NAME); return deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); } @Test public void executeConnectorOnEnterOfAnAutomaticActivity() throws Exception { final String valueOfInput1 = "valueOfInput1"; final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnStartOfAnAutomaticActivity", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder.addAutomaticTask("step1") .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_ENTER) .addInput(TestConnector.INPUT1, new ExpressionBuilder().createConstantStringExpression(valueOfInput1)); processBuilder.addUserTask("step2", ACTOR_NAME); processBuilder.addTransition("step1", "step2"); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForVariableStorage(TestConnector.INPUT1, valueOfInput1); waitForUserTask(processInstance, "step2"); disableAndDeleteProcess(processDefinition); } @Test public void executeMissingImplConnectorOnProcessInstance() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnActivityInstance", "1.0"); designProcessDefinition.addActor(ACTOR_NAME).addUserTask("step1", ACTOR_NAME) .addConnector("UnkownConnector", "unkownConnectorId", "1.0.0", ConnectorEvent.ON_ENTER); final ProcessDefinition processDefinition = getProcessAPI().deploy( new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition.done()).done()); getProcessAPI().addUserToActor(ACTOR_NAME, processDefinition, userId); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ConfigurationState.UNRESOLVED, processDeploymentInfo.getConfigurationState()); final List processResolutionProblems = getProcessAPI() .getProcessResolutionProblems(processDefinition.getId()); assertThat(processResolutionProblems).extracting("resource").contains("connector"); deleteProcess(processDefinition); } @Test public void executeMissingClassConnectorOnProcessInstance() throws Exception { final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder() .createNewInstance("executeConnectorOnActivityInstance", "1.0"); processDefBuilder.addActor(ACTOR_NAME).addUserTask("step1", ACTOR_NAME) .addConnector("UnkownClassConnector", "unkownClassConnectorDef", "1.0.0", ConnectorEvent.ON_ENTER); final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition( processDefBuilder.done()); businessArchiveBuilder.addConnectorImplementation( new BarResource("UnknownClassConnector.impl", IOUtils.toByteArray(TestLogBuilder.class .getResourceAsStream("/org/bonitasoft/engine/connectors/UnknownClassConnector.impl")))); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForFlowNodeInFailedState(processInstance, "step1"); disableAndDeleteProcess(processDefinition); } @Test public void executeConnectorWithCustomOutputTypeOnActivity() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("testConnectorWithExecutionTooLong", "1.0"); designProcessDefinition.addActor(ACTOR_NAME); designProcessDefinition.addShortTextData("value", null); designProcessDefinition .addAutomaticTask("step1") .addConnector("myConnector1", "connectorWithCustomType", "1.0.0", ConnectorEvent.ON_ENTER) .addOutput( new OperationBuilder() .createNewInstance() .setLeftOperand("value", LeftOperand.TYPE_DATA) .setType(OperatorType.ASSIGNMENT) .setRightOperand( new ExpressionBuilder().createGroovyScriptExpression("script", "output.getValue()", String.class.getName(), new ExpressionBuilder().createInputExpression("output", "org.connector.custom.CustomType"))) .done()); designProcessDefinition.addUserTask("step2", ACTOR_NAME); designProcessDefinition.addTransition("step1", "step2"); final List resources = new ArrayList<>(); addResource(resources, "/org/bonitasoft/engine/connectors/TestConnectorWithCustomType.impl", "TestConnectorWithCustomType.impl"); addResource(resources, "/org/bonitasoft/engine/connectors/connector-with-custom-type.bak", "connector-with-custom-type.jar"); final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchiveBuilder.addConnectorImplementation(resources.get(0)); businessArchiveBuilder.addClasspathResource(resources.get(1)); businessArchiveBuilder.setProcessDefinition(designProcessDefinition.done()); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, user); final ProcessInstance process = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(process, "step2"); assertEquals("value", getProcessAPI().getProcessDataInstance("value", process.getId()).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void executeConnectorWithCustomOutputTypeOnProcess() throws Exception { final ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("testConnectorWithExecutionTooLong", "1.0"); designProcessDefinition.addActor(ACTOR_NAME); designProcessDefinition.addShortTextData("value", null); designProcessDefinition .addConnector("myConnector1", "connectorWithCustomType", "1.0.0", ConnectorEvent.ON_ENTER).addOutput( new OperationBuilder() .createNewInstance() .setLeftOperand("value", LeftOperand.TYPE_DATA) .setType(OperatorType.ASSIGNMENT) .setRightOperand( new ExpressionBuilder().createGroovyScriptExpression("script", "output.getValue()", String.class.getName(), new ExpressionBuilder().createInputExpression("output", "org.connector.custom.CustomType"))) .done()); designProcessDefinition.addAutomaticTask("step1"); designProcessDefinition.addUserTask("step2", ACTOR_NAME); designProcessDefinition.addTransition("step1", "step2"); final List resources = new ArrayList<>(); addResource(resources, "/org/bonitasoft/engine/connectors/TestConnectorWithCustomType.impl", "TestConnectorWithCustomType.impl"); addResource(resources, "/org/bonitasoft/engine/connectors/connector-with-custom-type.bak", "connector-with-custom-type.jar"); final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchiveBuilder.addConnectorImplementation(resources.get(0)); businessArchiveBuilder.addClasspathResource(resources.get(1)); businessArchiveBuilder.setProcessDefinition(designProcessDefinition.done()); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, user); final ProcessInstance process = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(process, "step2"); assertEquals("value", getProcessAPI().getProcessDataInstance("value", process.getId()).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void getConnectorImplementationWorksAfterCacheCleared() throws Exception { final String valueOfInput1 = "valueOfInput1"; // Configure process and human tasks final Expression input1Expression = new ExpressionBuilder().createConstantStringExpression(valueOfInput1); final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance( "executeConnectorOnStartOfAnAutomaticActivity", "1.0"); processBuilder.addActor(ACTOR_NAME); processBuilder.addUserTask("step1", ACTOR_NAME) .addConnector("myConnector", "org.bonitasoft.connector.testConnector", "1.0", ConnectorEvent.ON_FINISH) .addInput(TestConnector.INPUT1, input1Expression); processBuilder.addUserTask("step2", ACTOR_NAME); processBuilder.addTransition("step1", "step2"); // Deploy and start process final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnector(processBuilder, ACTOR_NAME, user); assertTrue(getProcessAPI().getProcessResolutionProblems(processDefinition.getId()).isEmpty()); final SessionAccessor sessionAccessor = ServiceAccessorFactory.getInstance().createSessionAccessor(); sessionAccessor.setSessionId(getSession().getId()); final CacheService cacheservice = getServiceAccessor().getCacheService(); cacheservice.clearAll(); sessionAccessor.deleteSessionId(); assertTrue(getProcessAPI().getProcessResolutionProblems(processDefinition.getId()).isEmpty()); disableAndDeleteProcess(processDefinition); } @Test public void executeFailedNonSerializableOutputConnectorOnEnterOfAnAutomaticActivity() throws Exception { executeFailedNonSerializableOutputConnectorOfAnAutomaticActivity(ConnectorEvent.ON_ENTER); } @Test public void executeFailedNonSerializableOutputConnectorOnFinishOfAnAutomaticActivity() throws Exception { executeFailedNonSerializableOutputConnectorOfAnAutomaticActivity(ConnectorEvent.ON_FINISH); } protected void executeFailedNonSerializableOutputConnectorOfAnAutomaticActivity(final ConnectorEvent connectorEvent) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithNonSerializableOutputConnector", "1.0"); processBuilder.addStartEvent("start").addEndEvent("end"); processBuilder.addActor(ACTOR_NAME); processBuilder.addData("resultProcess", Object.class.getName(), new ExpressionBuilder().createConstantLongExpression(0L)); final AutomaticTaskDefinitionBuilder autoTask = processBuilder.addAutomaticTask("Step1"); processBuilder.addTransition("start", "end"); autoTask.addConnector("nonSerializableFailedConnector", "org.bonitasoft.connector.testConnectorWithNotSerializableOutput", "1.0", connectorEvent) .addOutput( new OperationBuilder().createNewInstance() .setLeftOperand("resultProcess", LeftOperand.TYPE_DATA) .setType(OperatorType.ASSIGNMENT) .setRightOperand(new ExpressionBuilder().createInputExpression("output1", Object.class.getName())) .done()); final ProcessDefinition processDefinition = deployProcessWithActorAndTestConnectorWithNotSerializableOutput( processBuilder, ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForFlowNodeInFailedState(processInstance, "Step1"); // Clean-up Thread.sleep(1000); disableAndDeleteProcess(processDefinition); } @Test public void executeExpressionAtProcessInstantiationOnProcessInInitializing() throws Exception { // will block the connector final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder() .createNewInstance("processWithBlockingConnector", "1.0"); processBuilder.addConnector("myConnector", "blocking-connector", "1.0", ConnectorEvent.ON_ENTER); processBuilder.addData("a", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("avalue")); processBuilder.addData("b", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("bvalue")); processBuilder.addData("c", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("cvalue")); final AutomaticTaskDefinitionBuilder addAutomaticTask = processBuilder.addAutomaticTask("step1"); addAutomaticTask.addOperation(new OperationBuilder().createSetDataOperation("a", new ExpressionBuilder().createConstantStringExpression("changed"))); addAutomaticTask.addOperation(new OperationBuilder().createSetDataOperation("b", new ExpressionBuilder().createConstantStringExpression("changed"))); addAutomaticTask.addOperation(new OperationBuilder().createSetDataOperation("c", new ExpressionBuilder().createConstantStringExpression("changed"))); // Add user task for confirmation form evaluation: processBuilder.addActor(ACTOR_NAME); final String userTaskName = "userTaskWithOnFinishConnector"; final UserTaskDefinitionBuilder userTaskDef = processBuilder.addUserTask(userTaskName, ACTOR_NAME); userTaskDef.addConnector("myConnector2", "blocking-connector", "1.0", ConnectorEvent.ON_FINISH); final BusinessArchive businessArchive = new BusinessArchiveBuilder() .createNewBusinessArchive() .setProcessDefinition(processBuilder.done()) .addConnectorImplementation( new BarResource("blocking-connector.impl", BuildTestUtil.buildConnectorImplementationFile("blocking-connector", "1.0", "blocking-connector-impl", "1.0", BlockingConnector.class.getName()))) .done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user); BlockingConnector.semaphore.acquire(); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final Map> expressions = new HashMap<>(2); expressions.put( new ExpressionBuilder().createGroovyScriptExpression("ascripte", "a+b+c", String.class.getName(), new ExpressionBuilder().createDataExpression("a", String.class.getName()), new ExpressionBuilder().createDataExpression("b", String.class.getName()), new ExpressionBuilder().createDataExpression("c", String.class.getName())), Collections.emptyMap()); expressions.put(new ExpressionBuilder().createDataExpression("a", String.class.getName()), Collections.emptyMap()); final Map evaluateExpressionsAtProcessInstantiation = getProcessAPI() .evaluateExpressionsAtProcessInstanciation( processInstance.getId(), expressions); assertEquals("avaluebvaluecvalue", evaluateExpressionsAtProcessInstantiation.get("ascripte")); assertEquals("avalue", evaluateExpressionsAtProcessInstantiation.get("a")); BlockingConnector.semaphore.release(); final long userTaskId = waitForUserTask(processInstance.getId(), userTaskName); BlockingConnector.semaphore.acquire(); assignAndExecuteStep(userTaskId, userId); // Try to evaluate expression on non-completed activity: final Expression engineConstantExpr = new ExpressionBuilder() .createEngineConstant(ExpressionConstants.PROCESS_INSTANCE_ID); final Map> exprToEvaluate = new HashMap<>(1); exprToEvaluate.put(engineConstantExpr, Collections.emptyMap()); final Map evaluatedExpressions = getProcessAPI() .evaluateExpressionsOnCompletedActivityInstance(userTaskId, exprToEvaluate); assertEquals(processInstance.getId(), ((Long) evaluatedExpressions.get("processInstanceId")).longValue()); // Release the connector for the user task to complete: BlockingConnector.semaphore.release(); waitForProcessToFinish(processInstance.getId()); getProcessAPI().evaluateExpressionsAtProcessInstanciation(processInstance.getId(), expressions); assertEquals("avaluebvaluecvalue", evaluateExpressionsAtProcessInstantiation.get("ascripte")); assertEquals("avalue", evaluateExpressionsAtProcessInstantiation.get("a")); disableAndDeleteProcess(processDefinition); } @Override public ProcessDefinition deployProcessWithActorAndTestConnector( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user, null, "TestConnector.impl", TestConnector.class, "TestConnector.jar"); } @Override public ProcessDefinition deployProcessWithActorAndTestConnector3( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user, null, "TestConnector3.impl", TestConnector3.class, "TestConnector3.jar"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/connector/ConnectorInstanceServiceIT.java ================================================ /** * 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.bpm.connector; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import java.util.List; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.connector.ConnectorService; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Baptiste Mesta */ public class ConnectorInstanceServiceIT extends CommonBPMServicesTest { private ConnectorInstanceService connectorInstanceService; @Before public void setUp() { connectorInstanceService = getServiceAccessor().getConnectorInstanceService(); } @After public void tearDown() throws Exception { cleanConnectorInstances(); } private void cleanConnectorInstances() throws Exception { getTransactionService().begin(); final QueryOptions queryOptions = new QueryOptions(0, 100, SConnectorInstance.class, "id", OrderByType.ASC); List connetorInstances = null; do { connetorInstances = connectorInstanceService.searchConnectorInstances(queryOptions); for (final SConnectorInstance connectorInstance : connetorInstances) { connectorInstanceService.deleteConnectorInstance(connectorInstance); } } while (!connetorInstances.isEmpty()); getTransactionService().complete(); } private SConnectorInstance createConnectorInTransaction(final String name, final long containerId, final String containerType, final String connectorId, final String version, final ConnectorEvent activationEvent, final int executionOrder) throws SBonitaException { final SConnectorInstance connectorInstance = SConnectorInstance.builder().name(name) .containerId(containerId) .containerType(containerType) .connectorId(connectorId) .version(version) .activationEvent(activationEvent) .state(ConnectorState.TO_BE_EXECUTED.name()) .executionOrder(executionOrder).build(); getTransactionService().begin(); connectorInstanceService.createConnectorInstance(connectorInstance); getTransactionService().complete(); return connectorInstance; } private SConnectorInstance getNextInTransaction(final long containerId, final String containerType, final ConnectorEvent activationEvent) throws SBonitaException { getTransactionService().begin(); final SConnectorInstance connectorInstance = connectorInstanceService.getNextExecutableConnectorInstance( containerId, containerType, activationEvent); getTransactionService().complete(); return connectorInstance; } private void setStateIntransaction(final long connectorId, final String state) throws SBonitaException { getTransactionService().begin(); final SConnectorInstance connectorInstance = connectorInstanceService.getConnectorInstance(connectorId); connectorInstanceService.setState(connectorInstance, state); getTransactionService().complete(); } @Test public void testGetNextExecutableConnectorInstance() throws Exception { final long containerId = 1L; final String containerType = SConnectorInstance.FLOWNODE_TYPE; final ConnectorEvent activationEvent = ConnectorEvent.ON_ENTER; // insert two connector instances final SConnectorInstance connectorA = createConnectorInTransaction("a", containerId, containerType, "myConnector-1", "1.0", activationEvent, 1); final SConnectorInstance connectorB = createConnectorInTransaction("b", containerId, containerType, "myConnector-2", "1.0", activationEvent, 2); // check that the next connector to be executed is the first inserted checkNextExecutableConnector(containerId, containerType, activationEvent, "a"); // mark the first connector as done: the next connector must be the second one setStateIntransaction(connectorA.getId(), ConnectorService.DONE); checkNextExecutableConnector(containerId, containerType, activationEvent, "b"); // mark the second connector as done: no more connectors are expected setStateIntransaction(connectorB.getId(), ConnectorService.DONE); assertNull(getNextInTransaction(containerId, containerType, activationEvent)); } @Test public void testGetNextExecutableConnectorInstanceWithFaillingConnectors() throws Exception { final long containerId = 1L; final String containerType = SConnectorInstance.FLOWNODE_TYPE; final ConnectorEvent activationEvent = ConnectorEvent.ON_ENTER; // insert two connector instances final SConnectorInstance connectorA = createConnectorInTransaction("a", containerId, containerType, "myConnector-1", "1.0", activationEvent, 1); createConnectorInTransaction("b", containerId, containerType, "myConnector-2", "1.0", activationEvent, 2); // check that the next connector to be executed is the first inserted checkNextExecutableConnector(containerId, containerType, activationEvent, "a"); // put the first connector in the state to re execute: the next connector must remain the first connector setStateIntransaction(connectorA.getId(), ConnectorService.TO_RE_EXECUTE); checkNextExecutableConnector(containerId, containerType, activationEvent, "a"); } private void checkNextExecutableConnector(final long containerId, final String containerType, final ConnectorEvent activationEvent, final String connectorName) throws SBonitaException { final SConnectorInstance nextConnectorInstance = getNextInTransaction(containerId, containerType, activationEvent); assertEquals(connectorName, nextConnectorInstance.getName()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/event/EventInstanceServiceIT.java ================================================ /** * 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.bpm.event; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingSignalEventBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.transaction.UserTransactionService; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class EventInstanceServiceIT extends CommonBPMServicesTest { private UserTransactionService userTransactionService; private EventInstanceService eventInstanceService; @Before public void setup() { userTransactionService = getServiceAccessor().getUserTransactionService(); eventInstanceService = getServiceAccessor().getEventInstanceService(); } private void checkStartEventInstance(final SEventInstance expectedEventInstance, final SEventInstance actualEventInstance) { assertTrue(actualEventInstance instanceof SStartEventInstance); checkEventInstance(expectedEventInstance, actualEventInstance); } private void checkEndEventInstance(final SEventInstance expectedEventInstance, final SEventInstance actualEventInstance) { assertTrue(actualEventInstance instanceof SEndEventInstance); checkEventInstance(expectedEventInstance, actualEventInstance); } private void checkIntermediateCatchEventInstance(final SEventInstance expectedEventInstance, final SEventInstance actualEventInstance) { assertTrue(actualEventInstance instanceof SIntermediateCatchEventInstance); checkEventInstance(expectedEventInstance, actualEventInstance); } private void checkBoundaryEventInstance(final SEventInstance expectedEventInstance, final SEventInstance actualEventInstance) { assertTrue(actualEventInstance instanceof SBoundaryEventInstance); final SBoundaryEventInstance expectedBoundary = (SBoundaryEventInstance) expectedEventInstance; final SBoundaryEventInstance actualBoundary = (SBoundaryEventInstance) actualEventInstance; assertEquals(expectedBoundary.getActivityInstanceId(), actualBoundary.getActivityInstanceId()); checkEventInstance(expectedEventInstance, actualEventInstance); } private void checkIntermediateThrowEventInstance(final SEventInstance expectedEventInstance, final SEventInstance actualEventInstance) { assertTrue(actualEventInstance instanceof SIntermediateThrowEventInstance); checkEventInstance(expectedEventInstance, actualEventInstance); } private void checkEventInstance(final SEventInstance expectedEventInstance, final SEventInstance actualEventInstance) { final SEndEventInstanceBuilderFactory eventInstanceBuilderFact = BuilderFactory .get(SEndEventInstanceBuilderFactory.class); final int processDefinitionIndex = eventInstanceBuilderFact.getProcessDefinitionIndex(); final int processInstanceIndex = eventInstanceBuilderFact.getRootProcessInstanceIndex(); assertEquals(expectedEventInstance.getId(), actualEventInstance.getId()); assertEquals(expectedEventInstance.getName(), actualEventInstance.getName()); assertEquals(expectedEventInstance.getParentContainerId(), actualEventInstance.getParentContainerId()); assertEquals(expectedEventInstance.getStateId(), actualEventInstance.getStateId()); assertEquals(expectedEventInstance.getLogicalGroup(processDefinitionIndex), actualEventInstance.getLogicalGroup(processDefinitionIndex)); assertEquals(expectedEventInstance.getLogicalGroup(processInstanceIndex), actualEventInstance.getLogicalGroup(processInstanceIndex)); } private List getEventInstances(final long processInstanceId, final int fromIndex, final int maxResult) throws Exception { return getEventInstances(processInstanceId, fromIndex, maxResult, BuilderFactory.get(SStartEventInstanceBuilderFactory.class).getNameKey(), OrderByType.ASC); } private List getEventInstances(final long processInstanceId, final int fromIndex, final int maxResult, final String fieldName, final OrderByType orderByType) throws Exception { return userTransactionService.executeInTransaction( () -> eventInstanceService.getEventInstances(processInstanceId, fromIndex, maxResult, fieldName, orderByType)); } private List getActivityBoundaryEventInstances(final long activityId, final int fromIndex, final int maxResults) throws Exception { return userTransactionService.executeInTransaction( () -> eventInstanceService.getActivityBoundaryEventInstances(activityId, fromIndex, maxResults)); } private void createWaitingEvent(final SWaitingEvent waitingEvent) throws Exception { userTransactionService.executeInTransaction((Callable) () -> { eventInstanceService.createWaitingEvent(waitingEvent); return null; }); } protected void deleteWaitingEvents(final SProcessInstance processInstance) throws Exception { userTransactionService.executeInTransaction((Callable) () -> { eventInstanceService.deleteWaitingEvents(processInstance); return null; }); } private SEventInstance createBoundaryEventInstance(final String eventName, final long flowNodeDefinitionId, final long rootProcessInstanceId, final long processDefinitionId, final long parentProcessInstanceId, final long activityInstanceId, final boolean isInterrupting) throws SBonitaException { final SEventInstance eventInstance = BuilderFactory .get(SBoundaryEventInstanceBuilderFactory.class) .createNewBoundaryEventInstance(eventName, isInterrupting, flowNodeDefinitionId, rootProcessInstanceId, parentProcessInstanceId, processDefinitionId, rootProcessInstanceId, parentProcessInstanceId, activityInstanceId) .done(); createSEventInstance(eventInstance); return eventInstance; } @Test public void testCreateAndRetrieveStartEventInstanceFromRootContainer() throws Exception { final SProcessInstance processInstance = createSProcessInstance(); List eventInstances = getEventInstances(processInstance.getId(), 0, 5); assertTrue(eventInstances.isEmpty()); final SEventInstance startEventInstance = createSStartEventInstance("startEvent", 1, processInstance.getId(), 5, processInstance.getId()); eventInstances = getEventInstances(processInstance.getId(), 0, 5); assertEquals(1, eventInstances.size()); checkStartEventInstance(startEventInstance, eventInstances.get(0)); deleteSProcessInstance(processInstance); eventInstances = getEventInstances(processInstance.getId(), 0, 5); assertTrue(eventInstances.isEmpty()); } @Test public void testCreateAndRetrieveEndEventInstance() throws Exception { final SProcessInstance processInstance = createSProcessInstance(); List eventInstances = getEventInstances(processInstance.getId(), 0, 5); assertTrue(eventInstances.isEmpty()); final SEventInstance eventInstance = createSEndEventInstance("EndEvent", 1, processInstance.getId(), 5, processInstance.getId()); eventInstances = getEventInstances(processInstance.getId(), 0, 5); assertEquals(1, eventInstances.size()); checkEndEventInstance(eventInstance, eventInstances.get(0)); deleteSProcessInstance(processInstance); eventInstances = getEventInstances(processInstance.getId(), 0, 5); assertTrue(eventInstances.isEmpty()); } @Test public void testCreateAndRetrieveIntermediateCatchEventInstance() throws Exception { final SProcessInstance processInstance = createSProcessInstance(); List eventInstances = getEventInstances(processInstance.getId(), 0, 5); assertTrue(eventInstances.isEmpty()); final SEventInstance eventInstance = createSIntermediateCatchEventInstance("IntermediateCatchEvent", 1, processInstance.getId(), 5, processInstance.getId()); eventInstances = getEventInstances(processInstance.getId(), 0, 5); assertEquals(1, eventInstances.size()); checkIntermediateCatchEventInstance(eventInstance, eventInstances.get(0)); deleteSProcessInstance(processInstance); eventInstances = getEventInstances(processInstance.getId(), 0, 5); assertTrue(eventInstances.isEmpty()); } @Test public void testCreateAndRetrieveBoundaryEventInstance() throws Exception { final SProcessInstance processInstance = createSProcessInstance(); List eventInstances = getEventInstances(processInstance.getId(), 0, 5); assertTrue(eventInstances.isEmpty()); final int activityInstanceId = 10; final SEventInstance eventInstance = createBoundaryEventInstance("BoundaryEvent", 1, processInstance.getId(), 5, processInstance.getId(), activityInstanceId, true); eventInstances = getEventInstances(processInstance.getId(), 0, 5); assertEquals(1, eventInstances.size()); checkBoundaryEventInstance(eventInstance, eventInstances.get(0)); deleteSProcessInstance(processInstance); eventInstances = getEventInstances(processInstance.getId(), 0, 5); assertTrue(eventInstances.isEmpty()); } @Test public void testGetActivityBoundaryEventInstances() throws Exception { final SProcessInstance processInstance = createSProcessInstance(); final long processDefinitionId = 5; final SActivityInstance automaticTaskInstance = createSAutomaticTaskInstance("auto1", 1, processInstance.getId(), processDefinitionId, processInstance.getId()); final long activityInstanceId = automaticTaskInstance.getId(); List boundaryEventInstances = getActivityBoundaryEventInstances(activityInstanceId, 0, 1); assertTrue(boundaryEventInstances.isEmpty()); final SEventInstance eventInstance1 = createBoundaryEventInstance("BoundaryEvent1", 2, processInstance.getId(), processDefinitionId, processInstance.getId(), activityInstanceId, true); final SEventInstance eventInstance2 = createBoundaryEventInstance("BoundaryEvent2", 3, processInstance.getId(), processDefinitionId, processInstance.getId(), activityInstanceId, true); boundaryEventInstances = getActivityBoundaryEventInstances(activityInstanceId, 0, 3); assertEquals(2, boundaryEventInstances.size()); checkBoundaryEventInstance(eventInstance1, boundaryEventInstances.get(0)); checkBoundaryEventInstance(eventInstance2, boundaryEventInstances.get(1)); deleteSProcessInstance(processInstance); boundaryEventInstances = getActivityBoundaryEventInstances(activityInstanceId, 0, 1); assertTrue(boundaryEventInstances.isEmpty()); } @Test public void testCreateAndRetrieveIntermediateThrowEventInstance() throws Exception { final SProcessInstance processInstance = createSProcessInstance(); List eventInstances = getEventInstances(processInstance.getId(), 0, 5); assertTrue(eventInstances.isEmpty()); final SEventInstance eventInstance = createSIntermediateThrowEventInstance("IntermediateThrowEvent", 1, processInstance.getId(), 5, processInstance.getId()); eventInstances = getEventInstances(processInstance.getId(), 0, 5); assertEquals(1, eventInstances.size()); checkIntermediateThrowEventInstance(eventInstance, eventInstances.get(0)); deleteSProcessInstance(processInstance); eventInstances = getEventInstances(processInstance.getId(), 0, 5); assertTrue(eventInstances.isEmpty()); } @Test public void testGetEventInstancesOrderByNameAsc() throws Exception { final SProcessInstance processInstance = createSProcessInstance(); final SEventInstance eventInstance1 = createSEndEventInstance("EndEvent1", 1, processInstance.getId(), 5, processInstance.getId()); final SEventInstance eventInstance2 = createSEndEventInstance("EndEvent2", 1, processInstance.getId(), 5, processInstance.getId()); final List eventInstances = getEventInstances(processInstance.getId(), 0, 5, BuilderFactory.get(SEndEventInstanceBuilderFactory.class) .getNameKey(), OrderByType.ASC); assertEquals(2, eventInstances.size()); checkEndEventInstance(eventInstance1, eventInstances.get(0)); checkEndEventInstance(eventInstance2, eventInstances.get(1)); deleteSProcessInstance(processInstance); } @Test public void testGetEventInstancesOrderByNameDesc() throws Exception { final SProcessInstance processInstance = createSProcessInstance(); final SEventInstance eventInstance1 = createSEndEventInstance("EndEvent1", 1, processInstance.getId(), 5, processInstance.getId()); final SEventInstance eventInstance2 = createSEndEventInstance("EndEvent2", 1, processInstance.getId(), 5, processInstance.getId()); final List eventInstances = getEventInstances(processInstance.getId(), 0, 5, BuilderFactory.get(SEndEventInstanceBuilderFactory.class) .getNameKey(), OrderByType.DESC); assertEquals(2, eventInstances.size()); checkEndEventInstance(eventInstance2, eventInstances.get(0)); checkEndEventInstance(eventInstance1, eventInstances.get(1)); deleteSProcessInstance(processInstance); } private List searchWaitingEvents(final Class clazz, final QueryOptions searchOptions) throws Exception { return getTransactionService() .executeInTransaction(() -> eventInstanceService.searchWaitingEvents(clazz, searchOptions)); } private long getNumberOfWaitingEvents(final Class clazz, final QueryOptions countOptions) throws Exception { return getTransactionService() .executeInTransaction(() -> eventInstanceService.getNumberOfWaitingEvents(clazz, countOptions)); } @Test public void testSearchWaitingEvents() throws Exception { final SProcessInstance processInstance = createSProcessInstance(); final SWaitingErrorEventBuilderFactory waitingErrorEventBuilder = BuilderFactory .get(SWaitingErrorEventBuilderFactory.class); final SEventInstance eventInstance = createSIntermediateCatchEventInstance("intermediate", 1, processInstance.getId(), 5, processInstance.getId()); final Class waitingEventClass = SWaitingEvent.class; final String processDefinitionIdKey = waitingErrorEventBuilder.getProcessDefinitionIdKey(); final String flowNodeInstanceIdKey = waitingErrorEventBuilder.getFlowNodeInstanceIdKey(); final long eventInstanceId = eventInstance.getId(); checkWaitingEvents(0, waitingEventClass, processDefinitionIdKey, flowNodeInstanceIdKey, eventInstanceId); final SWaitingMessageEvent messageWaitingEvent = BuilderFactory.get(SWaitingMessageEventBuilderFactory.class) .createNewWaitingMessageIntermediateEventInstance(5, processInstance.getId(), processInstance.getId(), eventInstanceId, "m1", processInstance.getName(), eventInstance.getFlowNodeDefinitionId(), eventInstance.getName()) .done(); createWaitingEvent(messageWaitingEvent); final SWaitingSignalEvent waitingSignalEvent = BuilderFactory.get(SWaitingSignalEventBuilderFactory.class) .createNewWaitingSignalIntermediateEventInstance(5, processInstance.getId(), processInstance.getId(), eventInstanceId, "go", processInstance.getName(), eventInstance.getFlowNodeDefinitionId(), eventInstance.getName()) .done(); createWaitingEvent(waitingSignalEvent); // search with SWaitingEvent checkWaitingEvents(2, waitingEventClass, processDefinitionIdKey, flowNodeInstanceIdKey, eventInstanceId); // search with SWaitingMessageEvent, SWaitingSignalEvent checkWaitingEvents(1, SWaitingMessageEvent.class, processDefinitionIdKey, flowNodeInstanceIdKey, eventInstanceId); checkWaitingEvents(1, SWaitingSignalEvent.class, processDefinitionIdKey, flowNodeInstanceIdKey, eventInstanceId); deleteWaitingEvents(processInstance); deleteSProcessInstance(processInstance); checkWaitingEvents(0, waitingEventClass, processDefinitionIdKey, flowNodeInstanceIdKey, eventInstanceId); } private void checkWaitingEvents(final int expectedNbOfWaitingEvents, final Class clazz, final String processDefinitionIdKey, final String flowNodeInstanceIdKey, final long eventInstanceId) throws Exception { final int maxResults = Math.max(expectedNbOfWaitingEvents + 1, 10); final QueryOptions queryOptions = getQueryOptions(clazz, 0, maxResults, processDefinitionIdKey, OrderByType.ASC, flowNodeInstanceIdKey, eventInstanceId); final QueryOptions countOptions = getCountOptions(clazz, flowNodeInstanceIdKey, eventInstanceId); final List waitingErrorEvents = searchWaitingEvents(clazz, queryOptions); final long numberOfWaitingErrorEvents = getNumberOfWaitingEvents(clazz, countOptions); assertEquals(expectedNbOfWaitingEvents, numberOfWaitingErrorEvents); assertEquals(expectedNbOfWaitingEvents, waitingErrorEvents.size()); } private QueryOptions getQueryOptions(final Class clazz, final int fromIndex, final int maxResult, final String orderByField, final OrderByType orderByType, final String filterKey, final Object filterValue) { final OrderByOption orderByOption = new OrderByOption(clazz, orderByField, orderByType); final FilterOption filterOption = new FilterOption(clazz, filterKey, filterValue); final QueryOptions boundaryQueryOptions = new QueryOptions(fromIndex, maxResult, Collections.singletonList(orderByOption), Collections.singletonList(filterOption), null); return boundaryQueryOptions; } private QueryOptions getCountOptions(final Class clazz, final String filterKey, final Object filterValue) { final FilterOption filterOption = new FilterOption(clazz, filterKey, filterValue); final List emptyOrderByOptions = Collections.emptyList(); final QueryOptions countOptions = new QueryOptions(0, 1, emptyOrderByOptions, Collections.singletonList(filterOption), null); return countOptions; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/event/LocalInterruptingTimerBoundaryEventIT.java ================================================ /** * 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.bpm.event; import static org.assertj.core.api.Assertions.assertThat; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.TimerType; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.event.AbstractEventIT; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.test.TestStates; import org.junit.Before; import org.junit.Test; public class LocalInterruptingTimerBoundaryEventIT extends AbstractEventIT { private static final String TIMER_EVENT_PREFIX = "Timer_Ev_"; private ServiceAccessor serviceAccessor; protected static void setSessionInfo(final APISession session) throws Exception { final SessionAccessor sessionAccessor = ServiceAccessorFactory.getInstance().createSessionAccessor(); sessionAccessor.setSessionId(session.getId()); } @Before public void setUp() throws Exception { serviceAccessor = ServiceAccessorFactory.getInstance().createServiceAccessor(); } private boolean containsTimerJob(final String jobName) throws Exception { setSessionInfo(getSession()); return serviceAccessor.getTransactionService().executeInTransaction(() -> serviceAccessor.getSchedulerService() .getJobs().stream().anyMatch(serverJobName -> serverJobName.contains(jobName))); } private List getJobs() throws Exception { return serviceAccessor.getTransactionService() .executeInTransaction(() -> serviceAccessor.getSchedulerService().getJobs()); } private String getJobName(final long eventInstanceId) { return TIMER_EVENT_PREFIX + eventInstanceId; } @Test // when the boundary event is not triggered we will have the same behavior for interrupting and non-interrupting events; only interrupting will be tested public void timerBoundaryEventNotTriggered() throws Exception { // given final long timerDuration = 20000; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEvent(timerDuration, true, "step1", "exceptionStep", "step2"); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final FlowNodeInstance timer = waitForFlowNodeInWaitingState(processInstance, "timer", false); final long boundaryId = timer.getId(); assertThat(containsTimerJob(getJobName(boundaryId))).isTrue(); // when waitForUserTaskAndExecuteIt(processInstance, "step1", user); // then waitForFlowNodeInState(processInstance, "timer", TestStates.ABORTED, false); assertThat(containsTimerJob(getJobName(boundaryId))).isFalse(); waitForUserTaskAndExecuteIt(processInstance, "step2", user); waitForProcessToFinish(processInstance); checkFlowNodeWasntExecuted(processInstance.getId(), "exceptionStep"); disableAndDeleteProcess(processDefinition); } @Test // when the boundary event is not triggered we will have the same behavior for interrupting and non-interrupting events; only interrupting will be tested public void timerBoundaryEventNotTriggeredOnCallActivity() throws Exception { // given final long timerDuration = 20000; final String simpleProcessName = "targetProcess"; final String simpleTaskName = "stepCA"; // deploy a simple process p1 final ProcessDefinition targetProcessDefinition = deployAndEnableSimpleProcess(simpleProcessName, simpleTaskName); // deploy a process, p2, with a call activity calling p1. The call activity has an interrupting timer boundary event final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEventOnCallActivity( timerDuration, true, simpleProcessName); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final FlowNodeInstance timer = waitForFlowNodeInWaitingState(processInstance, "timer", false); final long boundaryId = timer.getId(); assertThat(containsTimerJob(getJobName(boundaryId))).isTrue(); // when waitForUserTaskAndExecuteIt(processInstance, "stepCA", user); // then waitForFlowNodeInState(processInstance, "timer", TestStates.ABORTED, false); assertThat(containsTimerJob(getJobName(boundaryId))).isFalse(); waitForUserTaskAndExecuteIt(processInstance, PARENT_PROCESS_USER_TASK_NAME, user); waitForProcessToFinish(processInstance); checkFlowNodeWasntExecuted(processInstance.getId(), EXCEPTION_STEP); disableAndDeleteProcess(processDefinition); disableAndDeleteProcess(targetProcessDefinition); } @Test // when the boundary event is not triggered we will have the same behavior for interrupting and non-interrupting events; only interrupting will be tested public void timerBoundaryEventNotTriggeredOnSequentialMultiInstance() throws Exception { // given final long timerDuration = 20000; final int loopCardinality = 1; final boolean isSequential = true; final ProcessDefinition processDefinition = deployAndEnableProcessMultiInstanceWithBoundaryEvent(timerDuration, true, "step1", loopCardinality, isSequential, "step2", "exceptionStep"); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final FlowNodeInstance timer = waitForFlowNodeInWaitingState(processInstance, "timer", false); final long boundaryId = timer.getId(); assertThat(containsTimerJob(getJobName(boundaryId))).isTrue(); // when waitForUserTasksAndExecuteIt("step1", processInstance, loopCardinality); // then waitForFlowNodeInState(processInstance, "timer", TestStates.ABORTED, false); assertThat(containsTimerJob(getJobName(boundaryId))).isFalse(); waitForUserTaskAndExecuteIt(processInstance, "step2", user); waitForProcessToFinish(processInstance); checkFlowNodeWasntExecuted(processInstance.getId(), "exceptionStep"); disableAndDeleteProcess(processDefinition); } @Test // when the boundary event is not triggered we will have the same behavior for interrupting and non-interrupting events; only interrupting will be tested public void timerBoundaryEventNotTriggeredOnParallelMultiInstance() throws Exception { // given final long timerDuration = 20000; final int loopCardinality = 2; final boolean isSequential = false; final ProcessDefinition processDefinition = deployAndEnableProcessMultiInstanceWithBoundaryEvent(timerDuration, true, "step1", loopCardinality, isSequential, "step2", "exceptionStep"); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final FlowNodeInstance timer = waitForFlowNodeInWaitingState(processInstance, "timer", false); final long boundaryId = timer.getId(); assertThat(containsTimerJob(getJobName(boundaryId))).isTrue(); // when waitForUserTasksAndExecuteIt("step1", processInstance, loopCardinality); // then waitForFlowNodeInState(processInstance, "timer", TestStates.ABORTED, false); assertThat(containsTimerJob(getJobName(boundaryId))).isFalse(); waitForUserTaskAndExecuteIt(processInstance, "step2", user); waitForProcessToFinish(processInstance); checkFlowNodeWasntExecuted(processInstance.getId(), "exceptionStep"); disableAndDeleteProcess(processDefinition); } @Test // when the boundary event is not triggered we will have the same behavior for interrupting and non-interrupting events; only interrupting will be tested public void timerBoundaryEventNotTriggeredOnLoopActivity() throws Exception { // given final long timerDuration = 20000; final int loopMax = 1; final ProcessDefinition processDefinition = deployAndEnableProcessWithBoundaryTimerEventOnLoopActivity( timerDuration, true, loopMax, "step1", "step2", "exceptionStep"); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final FlowNodeInstance timer = waitForFlowNodeInWaitingState(processInstance, "timer", false); final long boundaryId = timer.getId(); assertThat(containsTimerJob(getJobName(boundaryId))).isTrue(); // when waitForUserTasksAndExecuteIt("step1", processInstance, loopMax); // then waitForFlowNodeInState(processInstance, "timer", TestStates.ABORTED, false); assertThat(containsTimerJob(getJobName(boundaryId))).isFalse(); waitForUserTaskAndExecuteIt(processInstance, "step2", user); waitForProcessToFinish(processInstance); checkFlowNodeWasntExecuted(processInstance.getId(), "exceptionStep"); disableAndDeleteProcess(processDefinition); } @Test public void deleteProcessInstanceAlsoDeleteChildrenProcessesEvents() throws Exception { // deploy a simple process with BoundaryEvent P1 List processDefinitions = new ArrayList<>(); final String simpleStepName = "simpleStep"; final ProcessDefinition simpleProcess = deployAndEnableSimpleProcessWithBoundaryEvent("simpleProcess", simpleStepName); processDefinitions.add(simpleProcess); // To clean in the end // deploy a process P2 containing a call activity calling P1 final String intermediateStepName = "intermediateStep1"; final String intermediateCallActivityName = "intermediateCall"; final ProcessDefinition intermediateProcess = deployAndEnableProcessWithCallActivity("intermediateProcess", simpleProcess.getName(), intermediateStepName, intermediateCallActivityName); processDefinitions.add(intermediateProcess); // To clean in the end // deploy a process P3 containing a call activity calling P2 final String rootStepName = "rootStep1"; final String rootCallActivityName = "rootCall"; final ProcessDefinition rootProcess = deployAndEnableProcessWithCallActivity("rootProcess", intermediateProcess.getName(), rootStepName, rootCallActivityName); processDefinitions.add(rootProcess); // To clean in the end // start P3, the call activities will start instances of P2 a and P1 final ProcessInstance rootProcessInstance = getProcessAPI().startProcess(rootProcess.getId()); waitForUserTask(rootProcessInstance, simpleStepName); List allJobs = getJobs(); //make sure timer events are created assertThat(allJobs.stream().anyMatch(job -> job.contains(TIMER_EVENT_PREFIX))).isTrue(); // delete the root process instance getProcessAPI().deleteProcessInstance(rootProcessInstance.getId()); // check that the instances of p1 and p2 were deleted List processInstances = getProcessAPI().getProcessInstances(0, 10, ProcessInstanceCriterion.NAME_ASC); assertThat(processInstances).isEmpty(); // check that archived flow nodes were deleted. List taskInstances = getProcessAPI().getArchivedActivityInstances( rootProcessInstance.getId(), 0, 100, ActivityInstanceCriterion.DEFAULT); assertThat(taskInstances).isEmpty(); //check the quartz events got deleted correctly allJobs = getJobs(); for (String job : allJobs) { // There might be a few of those left in the DB, it should be the only ones assertThat(job).isEqualToIgnoringCase("CleanInvalidSessions"); } //cleanup disableAndDeleteProcess(processDefinitions); } private ProcessDefinition deployAndEnableSimpleProcessWithBoundaryEvent(final String processName, final String userTaskName) throws Exception { final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(processName, "1.0"); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addStartEvent("tStart"); processDefBuilder.addUserTask(userTaskName, ACTOR_NAME).addBoundaryEvent("TheBoundaryEvent") .addTimerEventTriggerDefinition(TimerType.DURATION, new ExpressionBuilder().createConstantLongExpression(6000L)); processDefBuilder.addEndEvent("tEnd"); processDefBuilder.addEndEvent("tBoundaryEnd"); processDefBuilder.addTransition("TheBoundaryEvent", "tBoundaryEnd"); processDefBuilder.addTransition("tStart", userTaskName); processDefBuilder.addTransition(userTaskName, "tEnd"); return deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, user); } private ProcessDefinition deployAndEnableProcessWithCallActivity(final String processName, final String targetProcessName, final String userTaskName, final String callActivityName) throws Exception { final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression(targetProcessName); final ProcessDefinitionBuilder processDefBuilder = new ProcessDefinitionBuilder().createNewInstance(processName, "1.0"); processDefBuilder.addActor(ACTOR_NAME); processDefBuilder.addStartEvent("start"); processDefBuilder.addCallActivity(callActivityName, targetProcessNameExpr, null); processDefBuilder.addUserTask(userTaskName, ACTOR_NAME); processDefBuilder.addEndEvent("end"); processDefBuilder.addTransition("start", callActivityName); processDefBuilder.addTransition(callActivityName, userTaskName); processDefBuilder.addTransition(userTaskName, "end"); return deployAndEnableProcessWithActor(processDefBuilder.done(), ACTOR_NAME, user); } @Test public void timerBoundaryEvent_should_not_trigger_and_be_deleted_at_flownode_abortion() throws Exception { final int timerDuration = 20_000;//long enough not to trigger final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("pTimerBoundary", "2.0"); processDefinitionBuilder.addActor(ACTOR_NAME); processDefinitionBuilder.addStartEvent("start"); final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder.addUserTask("step1", ACTOR_NAME); userTaskDefinitionBuilder.addBoundaryEvent("Boundary timer").addTimerEventTriggerDefinition(TimerType.DURATION, new ExpressionBuilder().createConstantLongExpression(timerDuration)); userTaskDefinitionBuilder.addUserTask("exceptionStep", ACTOR_NAME); processDefinitionBuilder.addUserTask("step2", ACTOR_NAME); processDefinitionBuilder.addEndEvent("end").addTerminateEventTrigger(); processDefinitionBuilder.addEndEvent("end2").addTerminateEventTrigger(); processDefinitionBuilder.addTransition("start", "step1"); processDefinitionBuilder.addTransition("start", "step2"); processDefinitionBuilder.addTransition("step1", "end"); processDefinitionBuilder.addTransition("step2", "end2"); processDefinitionBuilder.addTransition("Boundary timer", "exceptionStep"); processDefinitionBuilder.addTransition("exceptionStep", "end"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); waitForUserTask(processInstance.getId(), "step1"); waitForUserTaskAssignAndExecuteIt(processInstance, "step2", user, Map.of()); waitForProcessToFinish(processInstance); assertThat(getJobs()).isEmpty(); disableAndDeleteProcess(processDefinition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/event/LocalTimerEventIT.java ================================================ /** * 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.bpm.event; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import java.util.List; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.TimerType; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.test.CommonAPILocalIT; import org.bonitasoft.engine.test.TestStates; import org.bonitasoft.engine.transaction.TransactionService; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class LocalTimerEventIT extends CommonAPILocalIT { private static final String TIMER_EVENT_PREFIX = "Timer_Ev_"; @Before public void setUp() throws BonitaException { loginWithTechnicalUser(); } @After public void tearDown() throws BonitaException { logout(); } @Test public void timerJobsAreDeleteOnDisable() throws Exception { final ProcessDefinitionBuilder definitionBuilder = new ProcessDefinitionBuilder().createNewInstance("proc", "1.0"); final String timerEventName = "startTimer"; definitionBuilder.addStartEvent(timerEventName).addTimerEventTriggerDefinition(TimerType.DURATION, new ExpressionBuilder().createConstantLongExpression(2000)); definitionBuilder.addAutomaticTask("auto"); definitionBuilder.addTransition(timerEventName, "auto"); final ProcessDefinition processDefinition = deployAndEnableProcess(definitionBuilder.done()); final String jobName = getJobName(processDefinition, timerEventName); assertTrue(containsTimerJob(jobName)); getProcessAPI().disableProcess(processDefinition.getId()); assertFalse(containsTimerJob(jobName)); getProcessAPI().enableProcess(processDefinition.getId()); assertTrue(containsTimerJob(jobName)); disableAndDeleteProcess(processDefinition.getId()); assertFalse(containsTimerJob(jobName)); } private boolean containsTimerJob(final String jobName) throws Exception { setSessionInfo(getSession()); final SchedulerService schedulerService = getServiceAccessor().getSchedulerService(); final TransactionService transactionService = getServiceAccessor().getTransactionService(); return transactionService.executeInTransaction(() -> { final List jobs = schedulerService.getJobs(); for (final String serverJobName : jobs) { if (serverJobName.contains(jobName)) { return true; } } return false; }); } private String getJobName(final ProcessDefinition processDefinition, final String timerEventName) { return TIMER_EVENT_PREFIX + processDefinition.getId() + timerEventName; } private String getJobName(final long eventInstanceId) { return TIMER_EVENT_PREFIX + eventInstanceId; } private ProcessDefinition deployProcessWithTimerIntermediateCatchEvent(final TimerType timerType, final Expression timerValue, final String stepName) throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("My Process with start event", "1.0"); processDefinitionBuilder.addIntermediateCatchEvent("intermediateCatchEvent") .addTimerEventTriggerDefinition(timerType, timerValue); processDefinitionBuilder.addAutomaticTask(stepName); processDefinitionBuilder.addEndEvent("end"); processDefinitionBuilder.addTransition("intermediateCatchEvent", stepName); processDefinitionBuilder.addTransition(stepName, "end"); final ProcessDefinition definition = deployAndEnableProcess(processDefinitionBuilder.getProcess()); final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(definition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); return definition; } @Test public void cancelProcessInstanceWithTimerIntermediateCatchEvent() throws Exception { // given final int timerTrigger = 20000; // the timer intermediate catch event will wait 20 seconds final Expression timerExpression = new ExpressionBuilder().createConstantLongExpression(timerTrigger); final ProcessDefinition definition = deployProcessWithTimerIntermediateCatchEvent(TimerType.DURATION, timerExpression, "step"); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); final FlowNodeInstance intermediateCatchEvent = waitForFlowNodeInWaitingState(processInstance, "intermediateCatchEvent", false); final Long floNodeInstanceId = intermediateCatchEvent.getId(); final String jobName = getJobName(floNodeInstanceId); assertThat(containsTimerJob(jobName)).isTrue(); // when getProcessAPI().cancelProcessInstance(processInstance.getId()); // then waitForFlowNodeInState(processInstance, "intermediateCatchEvent", TestStates.CANCELLED, false); assertThat(containsTimerJob(jobName)).isFalse(); waitForProcessToBeInState(processInstance, ProcessInstanceState.CANCELLED); checkWasntExecuted(processInstance, "step"); disableAndDeleteProcess(definition); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/failure/FlowNodeFailureIT.java ================================================ /** * 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.bpm.failure; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.process.*; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; public class FlowNodeFailureIT extends TestWithUser { private ServiceAccessor serviceAccessor; private ProcessDefinition processDefinition; @Override @Before public void before() throws Exception { super.before(); serviceAccessor = ServiceAccessorFactory.getInstance().createServiceAccessor(); DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_failed_flownode", PROCESS_VERSION) .addActor(ACTOR_NAME) .addAutomaticTask("step1") .addOperation(new LeftOperandBuilder().createDataLeftOperand("myData"), OperatorType.ASSIGNMENT, "", new ExpressionBuilder() .createGroovyScriptExpression("my-failing-script", "throw new RuntimeException('Failed !')", String.class.getName())) .getProcess(); processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, ACTOR_NAME, user); } @After public void after() throws Exception { disableAndDeleteProcess(processDefinition); super.after(); } @Test public void create_a_failure_on_flownode_operation_exception() throws Exception { // Given a process failing on a flownode operation final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(processDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); // When the flownode is executed final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); final FlowNodeInstance failFlowNodeInstance = waitForFlowNodeInFailedState(processInstance); // Then a failure is created assertEquals("step1", failFlowNodeInstance.getName()); var failureService = ServiceAccessorFactory.getInstance().createServiceAccessor().getBpmFailureService(); var failures = serviceAccessor.getTransactionService() .executeInTransaction(() -> failureService.getFlowNodeFailures(failFlowNodeInstance.getId(), 5)); assertThat(failures).hasSize(1); var failure = failures.get(0); assertThat(failure.getScope()) .isEqualTo(ScopedException.OPERATION); assertThat(failure.getContext()) .isEqualTo("expression::my-failing-script"); assertThat(failure.getErrorMessage()) .isEqualTo("RuntimeException: Failed !"); var processInstanceFailures = serviceAccessor.getTransactionService() .executeInTransaction(() -> failureService.getProcessInstanceFailures(processInstance.getId(), 5)); assertThat(processInstanceFailures).hasSize(1); assertThat(processInstanceFailures.get(0)) .isEqualTo(failures.get(0)); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/failure/ProcessInstanceFailureIT.java ================================================ /** * 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.bpm.failure; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.junit.Assert.assertEquals; import java.io.IOException; import java.util.concurrent.TimeUnit; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.process.*; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.connectors.TestConnectorThatThrowException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; public class ProcessInstanceFailureIT extends TestWithUser { private ServiceAccessor serviceAccessor; private ProcessDefinition failingProcessDefinition; @Override @Before public void before() throws Exception { super.before(); serviceAccessor = ServiceAccessorFactory.getInstance().createServiceAccessor(); failingProcessDefinition = getFailingProcessDefinition("My_Process_with_failing_connector"); } @After public void after() throws Exception { disableAndDeleteProcess(failingProcessDefinition); super.after(); } private ProcessDefinition getFailingProcessDefinition(String processName) throws InvalidProcessDefinitionException, IOException, BonitaException, InvalidBusinessArchiveFormatException { final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance(processName, PROCESS_VERSION) .addActor(ACTOR_NAME) .addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_FINISH) .addAutomaticTask("step1") .getProcess(); final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition( designProcessDefinition); businessArchiveBuilder.addConnectorImplementation( getResource("/org/bonitasoft/engine/connectors/TestConnectorThatThrowException.impl", "TestConnectorThatThrowException.impl")); businessArchiveBuilder .addClasspathResource(BuildTestUtil .generateJarAndBuildBarResource(TestConnectorThatThrowException.class, "TestConnectorThatThrowException.jar")); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, user); return processDefinition; } @Test public void create_a_failure_on_process_connector_exception() throws Exception { // Given a process failing on a flownode operation final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(failingProcessDefinition.getId()); assertEquals(ActivationState.ENABLED, processDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI().startProcess(processDeploymentInfo.getProcessId()); waitForProcessToBeInState(processInstance, ProcessInstanceState.ERROR); // Then a failure is created var failureService = ServiceAccessorFactory.getInstance().createServiceAccessor().getBpmFailureService(); var failures = serviceAccessor.getTransactionService() .executeInTransaction(() -> failureService.getProcessInstanceFailures(processInstance.getId(), 5)); assertThat(failures).hasSize(1); var failure = failures.get(0); assertThat(failure.getScope()) .isEqualTo(ScopedException.CONNECTOR); assertThat(failure.getContext()) .isEqualTo( "testConnectorThatThrowException::testConnectorThatThrowException::on_finish//input-validation"); assertThat(failure.getErrorMessage()) .isEqualTo("ConnectorValidationException: bad kind of exception"); //There are no subprocess so no subprocess failure should be retrieved var subProcessInstanceFailures = serviceAccessor.getTransactionService() .executeInTransaction( () -> failureService.getChildProcessInstancesFailures(processInstance.getId(), 5)); assertThat(subProcessInstanceFailures).hasSize(0); } @Test public void create_a_failure_on_sub_process_connector_exception() throws Exception { // Given a process failing on a flownode operation final DesignProcessDefinition mainProcessDesign = new ProcessDefinitionBuilder() .createNewInstance("My_Process_with_call_activity", PROCESS_VERSION) .addActor(ACTOR_NAME) .addCallActivity("call activity", new ExpressionBuilder().createConstantStringExpression(failingProcessDefinition.getName()), new ExpressionBuilder().createConstantStringExpression(PROCESS_VERSION)) .getProcess(); final ProcessDefinition mainProcessDefinition = deployAndEnableProcessWithActor(mainProcessDesign, ACTOR_NAME, user); try { final ProcessDeploymentInfo mainProcessDeploymentInfo = getProcessAPI() .getProcessDeploymentInfo(mainProcessDefinition.getId()); assertEquals(ActivationState.ENABLED, mainProcessDeploymentInfo.getActivationState()); final ProcessInstance processInstance = getProcessAPI() .startProcess(mainProcessDeploymentInfo.getProcessId()); waitForProcessToBeInState(processInstance, ProcessInstanceState.STARTED); SearchOptions SearchOption = new SearchOptionsBuilder(0, 1) .filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, failingProcessDefinition.getId()) .done(); await().atMost(3, TimeUnit.SECONDS) .until(() -> !getProcessAPI().searchProcessInstances(SearchOption).getResult().isEmpty()); ProcessInstance subProcessInstance = getProcessAPI().searchProcessInstances(SearchOption).getResult() .get(0); waitForProcessToBeInState(subProcessInstance, ProcessInstanceState.ERROR); // Then a failure is created in the subprocess var failureService = ServiceAccessorFactory.getInstance().createServiceAccessor().getBpmFailureService(); var subProcessInstanceFailures = serviceAccessor.getTransactionService() .executeInTransaction( () -> failureService.getChildProcessInstancesFailures(processInstance.getId(), 5)); assertThat(subProcessInstanceFailures).hasSize(1); var failure = subProcessInstanceFailures.get(0); assertThat(failure.getScope()) .isEqualTo(ScopedException.CONNECTOR); assertThat(failure.getContext()) .isEqualTo( "testConnectorThatThrowException::testConnectorThatThrowException::on_finish//input-validation"); assertThat(failure.getErrorMessage()) .isEqualTo("ConnectorValidationException: bad kind of exception"); //No failure as root process instance level var mainProcessInstanceFailures = serviceAccessor.getTransactionService() .executeInTransaction(() -> failureService.getProcessInstanceFailures(processInstance.getId(), 5)); assertThat(mainProcessInstanceFailures).hasSize(0); //cancel process instance to archive it getProcessAPI().cancelProcessInstance(processInstance.getId()); waitForProcessToBeInState(processInstance, ProcessInstanceState.CANCELLED); // Now let's check the archived version, for sub-process... var archSubProcessInstanceFailures = serviceAccessor.getTransactionService() .executeInTransaction( () -> failureService.getArchivedChildProcessInstancesFailures(processInstance.getId(), 5)); assertThat(archSubProcessInstanceFailures).hasSize(1); // ... and for the main process: var archMainProcessInstanceFailures = serviceAccessor.getTransactionService() .executeInTransaction( () -> failureService.getArchivedProcessInstanceFailures(processInstance.getId(), 5)); assertThat(archMainProcessInstanceFailures).hasSize(0); } finally { disableAndDeleteProcess(mainProcessDefinition); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/flownode/FlowNodeInstanceServiceIT.java ================================================ /** * 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.bpm.flownode; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.function.ToIntFunction; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SGatewayType; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; 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.SMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilderFactory; import org.bonitasoft.engine.lock.BonitaLock; import org.bonitasoft.engine.lock.LockService; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.transaction.UserTransactionService; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class FlowNodeInstanceServiceIT extends CommonBPMServicesTest { private UserTransactionService userTransactionService; private ActivityInstanceService activityInstanceService; @Before public void setup() { userTransactionService = getServiceAccessor().getUserTransactionService(); activityInstanceService = getServiceAccessor().getActivityInstanceService(); } private long getNbFlowNodeInstances(final QueryOptions countOptions) throws Exception { return userTransactionService.executeInTransaction( () -> activityInstanceService.getNumberOfFlowNodeInstances(SFlowNodeInstance.class, countOptions)); } @Test public void searchFlowNodeInstances() throws Exception { final SStartEventInstanceBuilderFactory startEventInstanceBuilder = BuilderFactory .get(SStartEventInstanceBuilderFactory.class); final SProcessInstance procInst1 = createSProcessInstance(); final SProcessInstance procInst2 = createSProcessInstance(); final OrderByOption oderByOption = new OrderByOption(SFlowNodeInstance.class, startEventInstanceBuilder.getNameKey(), OrderByType.ASC); final List filterOptions = Collections.emptyList(); final QueryOptions queryOptions = new QueryOptions(0, 10, Collections.singletonList(oderByOption), filterOptions, null); // search: no result expected List flowNodeInstances = searchFlowNodeInstances(queryOptions); assertTrue("There should not be any flownode instance instead of " + flowNodeInstances.size(), flowNodeInstances.isEmpty()); // create flow nodes createFlowNodeInstances(procInst1, procInst2); // search: created flow nodes must be retrieved flowNodeInstances = searchFlowNodeInstances(queryOptions); assertEquals(10, flowNodeInstances.size()); // delete process instances deleteSProcessInstance(procInst1); deleteSProcessInstance(procInst2); flowNodeInstances = searchFlowNodeInstances(queryOptions); assertEquals(0, flowNodeInstances.size()); } @Test public void searchFlowNodeInstancesWithFilter() throws Exception { final SStartEventInstanceBuilderFactory startEventInstanceBuilder = BuilderFactory .get(SStartEventInstanceBuilderFactory.class); final SProcessInstance procInst1 = createSProcessInstance(); final SProcessInstance procInst2 = createSProcessInstance(); final OrderByOption oderByOption = new OrderByOption(SFlowNodeInstance.class, startEventInstanceBuilder.getNameKey(), OrderByType.ASC); final FilterOption filterOption = new FilterOption(SFlowNodeInstance.class, startEventInstanceBuilder.getParentProcessInstanceKey(), procInst1.getId()); final QueryOptions queryOptions = new QueryOptions(0, 10, Collections.singletonList(oderByOption), Collections.singletonList(filterOption), null); final QueryOptions countOptions = new QueryOptions(0, 10, null, Collections.singletonList(filterOption), null); // search: no result expected List flowNodeInstances = searchFlowNodeInstances(queryOptions); long nbFlowNodeInstances = getNbFlowNodeInstances(countOptions); assertTrue(flowNodeInstances.isEmpty()); assertEquals(0, nbFlowNodeInstances); // create flow nodes createFlowNodeInstances(procInst1, procInst2); // search: created flow nodes must be retrieved flowNodeInstances = searchFlowNodeInstances(queryOptions); nbFlowNodeInstances = getNbFlowNodeInstances(countOptions); assertEquals(7, flowNodeInstances.size()); assertEquals(7, nbFlowNodeInstances); deleteSProcessInstance(procInst1); deleteSProcessInstance(procInst2); } private void createFlowNodeInstances(final SProcessInstance procInst1, final SProcessInstance procInst2) throws SBonitaException { // add flow nodes to procInst 1 createSStartEventInstance("startEvent", 1, procInst1.getId(), 5, procInst1.getId()); createSIntermediateCatchEventInstance("intermediateCatchEvent", 2, procInst1.getId(), 5, procInst1.getId()); createSIntermediateThrowEventInstance("intermediateThrowEvent", 3, procInst1.getId(), 5, procInst1.getId()); createSEndEventInstance("endEvent", 4, procInst1.getId(), 5, procInst1.getId()); final SGatewayInstance gatewayInstance = BuilderFactory.get(SGatewayInstanceBuilderFactory.class) .createNewInstance("Gateway1", 5, procInst1.getId(), procInst1.getId(), SGatewayType.EXCLUSIVE, 2, procInst1.getId(), procInst1.getId()) .setStateId(1).setHitBys("a,b,c").done(); insertGatewayInstance(gatewayInstance); createSUserTaskInstance("userTask", 6, procInst1.getId(), 5, procInst1.getId(), 10); createSAutomaticTaskInstance("autoTask", 7, procInst1.getId(), 5, procInst1.getId()); // add flow nodes to procInst 2 createSStartEventInstance("startEvent", 8, procInst2.getId(), 5, procInst2.getId()); createSAutomaticTaskInstance("autoTask", 9, procInst2.getId(), 5, procInst2.getId()); createSEndEventInstance("endEvent", 10, procInst2.getId(), 5, procInst2.getId()); } @Test public void isTaskPendingForUser() throws Exception { long flowNodeDefinitionId = 12355467L; long processDefinitionId = 123445566L; long rootProcessInstanceID = 7754L; long actorId = 5589L; SUserTaskInstance step1 = createSUserTaskInstance("step1", flowNodeDefinitionId, -1, processDefinitionId, rootProcessInstanceID, actorId); long userId = 4411L; //given getTransactionService().begin(); final SPendingActivityMapping mapping = SPendingActivityMapping.builder().activityId(step1.getId()) .userId(userId).build(); activityInstanceService.addPendingActivityMappings(mapping); getTransactionService().complete(); // //when getTransactionService().begin(); boolean taskPendingForUser = activityInstanceService.isTaskPendingForUser(step1.getId(), userId); getTransactionService().complete(); //then assertTrue("task should be pending", taskPendingForUser); // clean-up: getTransactionService().begin(); activityInstanceService.deleteAllPendingMappings(); activityInstanceService.deleteFlowNodeInstance(step1); getTransactionService().complete(); } @Test public void lastUpdateDate_should_be_persisted_when_modifying_flowNode() throws Exception { // given: create a user task long flowNodeDefinitionId = 12355467L; long processDefinitionId = 123445566L; long rootProcessInstanceID = 7754L; long actorId = 5589L; SUserTaskInstance task = createSUserTaskInstance("taskWithLastUpdateDate", flowNodeDefinitionId, -1, processDefinitionId, rootProcessInstanceID, actorId); // Verify initial lastUpdateDate is set during creation getTransactionService().begin(); SFlowNodeInstance initialTask = activityInstanceService.getFlowNodeInstance(task.getId()); long initialLastUpdateDate = initialTask.getLastUpdateDate(); getTransactionService().complete(); assertThat(initialLastUpdateDate).as("Initial lastUpdateDate should be > 0").isGreaterThan(0); // Wait a bit to ensure timestamp difference Thread.sleep(10); // when: modify the flow node using setExecuting getTransactionService().begin(); SFlowNodeInstance taskToModify = activityInstanceService.getFlowNodeInstance(task.getId()); activityInstanceService.setExecuting(taskToModify); getTransactionService().complete(); // then: re-fetch from database and verify lastUpdateDate was updated getTransactionService().begin(); SFlowNodeInstance updatedTask = activityInstanceService.getFlowNodeInstance(task.getId()); long updatedLastUpdateDate = updatedTask.getLastUpdateDate(); getTransactionService().complete(); assertThat(updatedLastUpdateDate).as("Updated lastUpdateDate should be > 0").isGreaterThan(0); assertThat(updatedLastUpdateDate).as("lastUpdateDate should be updated after modification") .isGreaterThanOrEqualTo(initialLastUpdateDate); // clean-up: getTransactionService().begin(); activityInstanceService.deleteFlowNodeInstance(updatedTask); getTransactionService().complete(); } @Test public void lastUpdateDate_should_be_persisted_when_assigning_humanTask_with_strict_query() throws Exception { // given: create a user task long flowNodeDefinitionId = 12355467L; long processDefinitionId = 123445566L; long rootProcessInstanceID = 7754L; long actorId = 5589L; SUserTaskInstance task = createSUserTaskInstance("taskForAssignment", flowNodeDefinitionId, -1, processDefinitionId, rootProcessInstanceID, actorId); // Verify initial lastUpdateDate is set during creation getTransactionService().begin(); SFlowNodeInstance initialTask = activityInstanceService.getFlowNodeInstance(task.getId()); long initialLastUpdateDate = initialTask.getLastUpdateDate(); getTransactionService().complete(); assertThat(initialLastUpdateDate).as("Initial lastUpdateDate should be > 0").isGreaterThan(0); // Wait a bit to ensure timestamp difference Thread.sleep(10); // when: assign the task using assignHumanTaskIfNotAssigned (uses updateStrictHuman query) long userId = 12345L; getTransactionService().begin(); activityInstanceService.assignHumanTaskIfNotAssigned(task.getId(), userId); getTransactionService().complete(); // then: re-fetch from database and verify lastUpdateDate was updated getTransactionService().begin(); SFlowNodeInstance assignedTask = activityInstanceService.getFlowNodeInstance(task.getId()); long assignedLastUpdateDate = assignedTask.getLastUpdateDate(); long assigneeId = ((SUserTaskInstance) assignedTask).getAssigneeId(); getTransactionService().complete(); assertThat(assigneeId).as("Assignee should be set").isEqualTo(userId); assertThat(assignedLastUpdateDate).as("Assigned lastUpdateDate should be > 0").isGreaterThan(0); assertThat(assignedLastUpdateDate).as("lastUpdateDate should be updated after assignment") .isGreaterThanOrEqualTo(initialLastUpdateDate); // clean-up: getTransactionService().begin(); activityInstanceService.deleteFlowNodeInstance(assignedTask); getTransactionService().complete(); } // Thread-safety tests for the multi-instance counter updates. Verify that atomic SQL // (SET counter = counter + :n) prevents lost updates both without the process instance // lock and with it (the production Lock → Tx → Work pattern). @Test public void concurrent_addMultiInstanceNumberOfCompletedActivities_without_lock_should_not_lose_updates() throws Exception { runConcurrentMiCounterTest( "miActivity", /* useLock */ false, (svc, mi) -> svc.addMultiInstanceNumberOfCompletedActivities(mi, 1), SMultiInstanceActivityInstance::getNumberOfCompletedInstances, "completions"); } @Test public void concurrent_addMultiInstanceNumberOfTerminatedActivities_without_lock_should_not_lose_updates() throws Exception { runConcurrentMiCounterTest( "miActivityTerminated", /* useLock */ false, (svc, mi) -> svc.addMultiInstanceNumberOfTerminatedActivities(mi, 1), SMultiInstanceActivityInstance::getNumberOfTerminatedInstances, "terminations"); } @Test public void concurrent_addMultiInstanceNumberOfCompletedActivities_with_lock_should_not_lose_updates() throws Exception { runConcurrentMiCounterTest( "miActivityLockedCompleted", /* useLock */ true, (svc, mi) -> svc.addMultiInstanceNumberOfCompletedActivities(mi, 1), SMultiInstanceActivityInstance::getNumberOfCompletedInstances, "completions"); } @Test public void concurrent_addMultiInstanceNumberOfTerminatedActivities_with_lock_should_not_lose_updates() throws Exception { runConcurrentMiCounterTest( "miActivityLockedTerminated", /* useLock */ true, (svc, mi) -> svc.addMultiInstanceNumberOfTerminatedActivities(mi, 1), SMultiInstanceActivityInstance::getNumberOfTerminatedInstances, "terminations"); } private void runConcurrentMiCounterTest( final String activityName, final boolean useLock, final MiCounterOp op, final ToIntFunction counterGetter, final String counterLabel) throws Exception { final int threadCount = 50; final SProcessInstance processInstance = createSProcessInstance(); try { final SMultiInstanceActivityInstance miActivity = createMultiInstanceActivity( activityName, processInstance, threadCount); final LockService lockService = useLock ? getServiceAccessor().getLockService() : null; final String objectType = SFlowElementsContainerType.PROCESS.name(); final List errors = new CopyOnWriteArrayList<>(); final CyclicBarrier barrier = new CyclicBarrier(threadCount); final CountDownLatch done = new CountDownLatch(threadCount); final ExecutorService executor = Executors.newFixedThreadPool(threadCount); final long sessionId = getSessionAccessor().getSessionId(); for (int i = 0; i < threadCount; i++) { executor.submit(() -> { try { getSessionAccessor().setSessionId(sessionId); barrier.await(30, TimeUnit.SECONDS); final BonitaLock lock = useLock ? lockService.lock(processInstance.getId(), objectType) : null; try { getTransactionService().begin(); try { final SMultiInstanceActivityInstance freshMi = (SMultiInstanceActivityInstance) activityInstanceService .getFlowNodeInstance(miActivity.getId()); op.apply(activityInstanceService, freshMi); getTransactionService().complete(); } catch (final Exception e) { getTransactionService().setRollbackOnly(); getTransactionService().complete(); throw e; } } finally { if (useLock) { lockService.unlock(lock); } } } catch (final Throwable t) { errors.add(t); } finally { done.countDown(); } }); } done.await(60, TimeUnit.SECONDS); executor.shutdown(); executor.awaitTermination(10, TimeUnit.SECONDS); assertThat(errors).as("Unexpected errors in worker threads").isEmpty(); getTransactionService().begin(); final SMultiInstanceActivityInstance result = (SMultiInstanceActivityInstance) activityInstanceService .getFlowNodeInstance(miActivity.getId()); getTransactionService().complete(); assertThat(counterGetter.applyAsInt(result)) .as("All %d %s should be counted", threadCount, counterLabel) .isEqualTo(threadCount); assertThat(result.getNumberOfActiveInstances()) .as("Active instances should reach 0") .isEqualTo(0); } finally { deleteSProcessInstance(processInstance); } } @FunctionalInterface private interface MiCounterOp { void apply(ActivityInstanceService svc, SMultiInstanceActivityInstance mi) throws SBonitaException; } private SMultiInstanceActivityInstance createMultiInstanceActivity( final String name, final SProcessInstance processInstance, final int numberOfActiveInstances) throws SBonitaException { final SMultiInstanceActivityInstance miActivity = new SMultiInstanceActivityInstance( name, 1L, processInstance.getId(), processInstance.getId(), 1L, processInstance.getId(), false); miActivity.setNumberOfActiveInstances(numberOfActiveInstances); miActivity.setLoopCardinality(numberOfActiveInstances); miActivity.setStateId(28); // EXECUTING state miActivity.setLogicalGroup(3, processInstance.getId()); // parent process instance getTransactionService().begin(); activityInstanceService.createActivityInstance(miActivity); getTransactionService().complete(); return miActivity; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/process/AddCommentConnector.java ================================================ /** * 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.bpm.process; import org.bonitasoft.engine.connector.AbstractConnector; import org.bonitasoft.engine.connector.ConnectorException; import org.bonitasoft.engine.exception.CreationException; public class AddCommentConnector extends AbstractConnector { @Override protected void executeBusinessLogic() throws ConnectorException { try { getAPIAccessor().getProcessAPI().addProcessComment(getExecutionContext().getRootProcessInstanceId(), "comment added by connector"); } catch (CreationException e) { throw new ConnectorException(e); } } @Override public void validateInputParameters() { } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/bpm/process/DeleteProcessInstancesIT.java ================================================ /** * 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.bpm.process; import static java.util.Arrays.asList; import static java.util.Map.entry; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.awaitility.Awaitility.await; import static org.bonitasoft.engine.data.instance.api.DataInstanceContainer.ACTIVITY_INSTANCE; import static org.bonitasoft.engine.data.instance.api.DataInstanceContainer.PROCESS_INSTANCE; import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.actorMapping.Actor; import org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.contract.FileInputValue; import org.bonitasoft.engine.bpm.contract.Type; import org.bonitasoft.engine.bpm.document.DocumentValue; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.core.contract.data.SContractDataNotFoundException; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.model.SLightDocument; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.test.CommonAPILocalIT; import org.bonitasoft.engine.transaction.UserTransactionService; import org.junit.Test; /** * Verify that api methods delete process instances and all its elements */ public class DeleteProcessInstancesIT extends CommonAPILocalIT { @Test public void should_delete_complete_archived_process_instances() throws Exception { loginWithTechnicalUser(); User user = createUser("deleteProcessInstanceIT", "bpm"); ProcessDefinition mainProcess = createMainProcessDefinition(); ProcessDefinition sub1 = createSubProcessDefinition1(); ProcessDefinition sub2 = createSubProcessDefinition2(); List processInstances = new ArrayList<>(); List userTaskInstances = new ArrayList<>(); for (int i = 0; i < 2; i++) { long id = getProcessAPI().startProcessWithInputs(mainProcess.getId(), Map.ofEntries(entry("simpleInput1", "singleInputValue"), entry("myFile", new FileInputValue("testFile", "testFile".getBytes())))) .getId(); var archivedFileInput = await().until(() -> inTx( () -> getServiceAccessor().getContractDataService().getArchivedProcessDataValue(id, "myFile")), Objects::nonNull); assertThat(archivedFileInput) .as("File input content of archived contract data should be null") .isInstanceOf(FileInputValue.class) .extracting("content") .isNull(); long userTask1 = waitForUserTask(id, "userTask1"); userTaskInstances.add(userTask1); getProcessAPI().assignUserTask(userTask1, user.getId()); getProcessAPI().executeUserTask(userTask1, Collections.singletonMap("simpleInputTask", "simpleInputTaskValue")); waitForProcessToFinish(id); processInstances.add(id); } List allFlowNodesBeforeDelete = getAllFlowNodes(); List allArchFlowNodesBeforeDelete = getAllArchFlowNodes(); List allArchProcessInstancesBeforeDelete = getAllProcessInstances(); getProcessAPI().deleteArchivedProcessInstancesInAllStates(processInstances); inTx(() -> { for (Long userTaskInstance : userTaskInstances) { try { getServiceAccessor().getContractDataService().getArchivedUserTaskDataValue(userTaskInstance, "simpleInputTask"); fail("should have deleted archived contract data on activity instance"); } catch (SContractDataNotFoundException e) { //ok } } for (Long processInstance : processInstances) { try { getServiceAccessor().getContractDataService().getArchivedProcessDataValue(processInstance, "simpleInput1"); fail("should have deleted archived contract data on process instance"); } catch (SContractDataNotFoundException e) { //ok } } assertSoftly((soft) -> { try { soft.assertThat(allFlowNodesBeforeDelete).isEmpty(); soft.assertThat(searchAllArchProcessInstances()).isEmpty(); soft.assertThat(searchAllArchFlowNodes()).isEmpty(); soft.assertThat( getServiceAccessor().getCommentService().searchArchivedComments(new QueryOptions(0, 1000))) .isEmpty(); soft.assertThat(getServiceAccessor().getConnectorInstanceService().searchArchivedConnectorInstance( new QueryOptions(0, 100, SAConnectorInstance.class, null, null), getServiceAccessor().getReadPersistenceService())).isEmpty(); soft.assertThat(getServiceAccessor().getDocumentService() .getNumberOfArchivedDocuments(new QueryOptions(0, 100))).isEqualTo(0); for (SAFlowNodeInstance flowNodeInstance : allArchFlowNodesBeforeDelete) { soft.assertThat(getServiceAccessor().getDataInstanceService().getLocalSADataInstances( flowNodeInstance.getSourceObjectId(), ACTIVITY_INSTANCE.toString(), 0, 1)).isEmpty(); } for (SAProcessInstance processInstance : allArchProcessInstancesBeforeDelete) { soft.assertThat(getServiceAccessor().getDataInstanceService().getLocalSADataInstances( processInstance.getSourceObjectId(), PROCESS_INSTANCE.toString(), 0, 1)).isEmpty(); } } catch (Exception e) { throw new RuntimeException(e); } }); return null; }); disableAndDeleteProcess(asList(mainProcess, sub1, sub2)); } @Test public void should_delete_process_instance_currently_executing() throws Exception { loginWithTechnicalUser(); User user = createUser("deleteProcessInstanceIT", "bpm"); ProcessDefinition mainProcess = createMainProcessDefinition(); ProcessDefinition sub1 = createSubProcessDefinition1(); ProcessDefinition sub2 = createSubProcessDefinitionWithUserTask(user); long processInstanceId = getProcessAPI().startProcessWithInputs(mainProcess.getId(), Map.ofEntries(entry("simpleInput1", "singleInputValue"), entry("myFile", new FileInputValue("testFile", "testFile".getBytes())))) .getId(); waitForUserTask(processInstanceId, "userTask1"); waitForUserTask("taskOfSubProcess"); waitForUserTask("taskOfSubProcess"); final SLightDocument document = getDocument(processInstanceId); assertThat(document).isNotNull(); Thread.sleep(200); getProcessAPI().deleteProcessInstance(processInstanceId); assertSoftly((soft) -> { try { soft.assertThat(getAllFlowNodes()).isEmpty(); soft.assertThat(getAllArchFlowNodes()).isEmpty(); soft.assertThat(getAllProcessInstances()).isEmpty(); } catch (Exception e) { throw new RuntimeException(e); } }); assertThrows(SObjectNotFoundException.class, () -> getDocumentContent(document.getId())); disableAndDeleteProcess(asList(mainProcess, sub1, sub2)); } private SLightDocument getDocument(long processInstanceId) throws Exception { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DocumentService documentService = serviceAccessor.getDocumentService(); final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService(); final Long documentId = userTransactionService.executeInTransaction( () -> documentService.getMappedDocument(processInstanceId, "myDoc").getDocumentId()); return getDocumentContent(documentId); } private SLightDocument getDocumentContent(long documentId) throws Exception { final ServiceAccessor serviceAccessor = getServiceAccessor(); return serviceAccessor.getUserTransactionService().executeInTransaction( () -> serviceAccessor.getDocumentService().getDocument(documentId)); } protected List getAllProcessInstances() throws Exception { return getServiceAccessor() .getUserTransactionService().executeInTransaction(this::searchAllArchProcessInstances); } protected List getAllArchFlowNodes() throws Exception { return getServiceAccessor().getUserTransactionService() .executeInTransaction(this::searchAllArchFlowNodes); } protected List getAllFlowNodes() throws Exception { return getServiceAccessor().getUserTransactionService() .executeInTransaction(this::searchAllFlowNodes); } protected ProcessDefinition createMainProcessDefinition() throws Exception { ProcessDefinitionBuilder mainProcessBuilder = new ProcessDefinitionBuilder() .createNewInstance("mainProcess", "1.0"); mainProcessBuilder.addContract() .addInput("simpleInput1", Type.TEXT, "a simple input") .addFileInput("myFile", "A file input"); mainProcessBuilder.addActor("actor"); mainProcessBuilder.addUserTask("userTask1", "actor").addContract().addInput("simpleInputTask", Type.TEXT, "a simple task input"); ActorMapping actorMapping = new ActorMapping(); Actor actor = new Actor("actor"); actorMapping.addActor(actor); actor.addUser("deleteProcessInstanceIT"); mainProcessBuilder .addDocumentDefinition("myDoc").addInitialValue(docValueExpr()) .addStartEvent("start1") .addConnector("connector1", "myConnector", "1.0", ConnectorEvent.ON_ENTER) .addAutomaticTask("autoWithConnector") .addConnector("connector1", "myConnector", "1.0", ConnectorEvent.ON_ENTER) .addAutomaticTask("autoWithData").addShortTextData("activityData", s("activityDataValue")) .addCallActivity("call1", s("subProcess"), s("1.0")); mainProcessBuilder.addCallActivity("call2", s("subProcess"), s("2.0")) .addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(2)); return deployAndEnableProcess(barWithConnector(mainProcessBuilder .getProcess()).setActorMapping(actorMapping).done()); } protected ProcessDefinition createSubProcessDefinition1() throws Exception { return getProcessAPI().deployAndEnableProcess(barWithConnector(new ProcessDefinitionBuilder() .createNewInstance("subProcess", "1.0") .addDocumentDefinition("myDoc").addInitialValue(docValueExpr()) .addStartEvent("start1") .addConnector("connector1", "myConnector", "1.0", ConnectorEvent.ON_ENTER) .addAutomaticTask("autoWithConnector") .addConnector("connector1", "myConnector", "1.0", ConnectorEvent.ON_ENTER) .addAutomaticTask("autoWithData").addShortTextData("activityData", s("activityDataValue")) .addCallActivity("sub2", s("subProcess"), s("2.0")).getProcess()).done()); } protected ProcessDefinition createSubProcessDefinition2() throws Exception { return getProcessAPI().deployAndEnableProcess(barWithConnector(new ProcessDefinitionBuilder() .createNewInstance("subProcess", "2.0") .addDocumentDefinition("myDoc").addInitialValue(docValueExpr()) .addStartEvent("start1") .addConnector("connector1", "myConnector", "1.0", ConnectorEvent.ON_ENTER) .addAutomaticTask("autoWithConnector") .addConnector("connector1", "myConnector", "1.0", ConnectorEvent.ON_ENTER) .addAutomaticTask("autoWithData").addShortTextData("activityData", s("activityDataValue")).getProcess()) .done()); } protected ProcessDefinition createSubProcessDefinitionWithUserTask(User user) throws Exception { return deployAndEnableProcessWithActor(new ProcessDefinitionBuilder() .createNewInstance("subProcess", "2.0") .addActor("actor") .addStartEvent("start1") .addUserTask("taskOfSubProcess", "actor") .addTransition("start1", "taskOfSubProcess").getProcess(), "actor", user); } private List searchAllArchProcessInstances() throws SBonitaReadException { return getServiceAccessor().getProcessInstanceService() .searchArchivedProcessInstances(new QueryOptions(0, 1000)); } private List searchAllArchFlowNodes() throws org.bonitasoft.engine.persistence.SBonitaReadException { return getServiceAccessor().getActivityInstanceService() .searchArchivedFlowNodeInstances(SAFlowNodeInstance.class, new QueryOptions(0, 1000)); } private List searchAllFlowNodes() throws org.bonitasoft.engine.persistence.SBonitaReadException { return getServiceAccessor().getActivityInstanceService() .searchFlowNodeInstances(SFlowNodeInstance.class, new QueryOptions(0, 1000)); } private Expression docValueExpr() throws InvalidExpressionException { return new ExpressionBuilder().createGroovyScriptExpression("docValue", "new org.bonitasoft.engine.bpm.document.DocumentValue(\"hello3\".getBytes(),\"plain/text\",\"file1.txt\")", DocumentValue.class.getName()); } private BusinessArchiveBuilder barWithConnector(DesignProcessDefinition process) throws Exception { byte[] connectorImplementationFile = BuildTestUtil.buildConnectorImplementationFile("myConnector", "1.0", "impl1", "1.0", AddCommentConnector.class.getName()); return new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(process) .addConnectorImplementation(new BarResource("connector.impl", connectorImplementationFile)); } private Expression s(String s) throws InvalidExpressionException { return new ExpressionBuilder().createConstantStringExpression(s); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/business/application/importer/ApplicationImporterIT.java ================================================ /** * 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.business.application.importer; import static org.assertj.core.api.Assertions.assertThat; import java.util.List; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.api.impl.SessionInfos; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationState; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.profile.ProfileService; import org.bonitasoft.engine.profile.model.SProfile; import org.junit.After; import org.junit.Before; import org.junit.Test; public class ApplicationImporterIT extends CommonBPMServicesTest { private ApplicationImporter applicationImporter; private ApplicationService applicationService; private PageService pageService; private ProfileService profileService; private static final String APP_1_TOKEN = "app1"; private static final String APP_2_TOKEN = "app2"; private static final String APP_3_TOKEN = "app3"; private static final String APP_4_TOKEN = "app4"; @Before public void setUp() { applicationImporter = getServiceAccessor().getApplicationImporter(); applicationService = getServiceAccessor().getApplicationService(); pageService = getServiceAccessor().getPageService(); profileService = getServiceAccessor().getProfileService(); } @After public void tearDown() throws Exception { deleteApplication(APP_1_TOKEN); deleteApplication(APP_2_TOKEN); deleteApplication(APP_3_TOKEN); deleteApplication(APP_4_TOKEN); getTransactionService().executeInTransaction(() -> { SPage page = pageService.getPageByName("custompage_mynewcustompage"); if (page != null) { pageService.deletePage(page.getId()); } return page; }); } private void deleteApplication(String appToken) throws Exception { getTransactionService().executeInTransaction(() -> { SApplication app = applicationService.getApplicationByToken(appToken); if (app != null) { applicationService.forceDeleteApplication(app); } return app; }); } @Test public void one_application_link_should_be_imported_successfully() throws Exception { //given String xmlToImport = "/applications-importer/oneApplicationLink.xml"; // ensure applications did not exist initially: assertAppNotExists(APP_1_TOKEN); //when List importStatuses = importApplicationsFromXml(xmlToImport); //then assertThat(importStatuses).hasSize(1); assertThat(importStatuses.get(0).getName()).isEqualTo(APP_1_TOKEN); assertThat(importStatuses.get(0).getStatus()).isEqualTo(ImportStatus.Status.ADDED); assertThat(importStatuses.get(0).getErrors()).isEmpty(); assertAppLink1(); } @Test public void multiple_application_links_should_be_imported_successfully() throws Exception { //given String xmlToImport = "/applications-importer/multipleApplicationLinks.xml"; // ensure applications did not exist initially: assertAppNotExists(APP_1_TOKEN); assertAppNotExists(APP_2_TOKEN); //when List importStatuses = importApplicationsFromXml(xmlToImport); //then assertThat(importStatuses).hasSize(2); assertThat(importStatuses.get(0).getName()).isEqualTo(APP_1_TOKEN); assertThat(importStatuses.get(0).getStatus()).isEqualTo(ImportStatus.Status.ADDED); assertThat(importStatuses.get(0).getErrors()).isEmpty(); assertThat(importStatuses.get(1).getName()).isEqualTo(APP_2_TOKEN); assertThat(importStatuses.get(1).getStatus()).isEqualTo(ImportStatus.Status.ADDED); assertThat(importStatuses.get(1).getErrors()).isEmpty(); assertAppLink1(); assertAppLink2(); } @Test public void mixed_legacy_and_application_links_should_be_imported_successfully() throws Exception { //given String xmlToImport = "/applications-importer/mixedLegacyAndApplicationLinks.xml"; // ensure applications did not exist initially: assertAppNotExists(APP_1_TOKEN); assertAppNotExists(APP_2_TOKEN); assertAppNotExists(APP_3_TOKEN); assertAppNotExists(APP_4_TOKEN); // create page mandatory for app3 createDummyPage(); //when List importStatuses = importApplicationsFromXml(xmlToImport); //then assertThat(importStatuses).hasSize(4); assertThat(importStatuses.get(0).getName()).isEqualTo(APP_1_TOKEN); assertThat(importStatuses.get(0).getStatus()).isEqualTo(ImportStatus.Status.ADDED); assertThat(importStatuses.get(0).getErrors()).isEmpty(); assertThat(importStatuses.get(1).getName()).isEqualTo(APP_2_TOKEN); assertThat(importStatuses.get(1).getStatus()).isEqualTo(ImportStatus.Status.ADDED); assertThat(importStatuses.get(1).getErrors()).isEmpty(); assertThat(importStatuses.get(2).getName()).isEqualTo(APP_3_TOKEN); assertThat(importStatuses.get(2).getStatus()).isEqualTo(ImportStatus.Status.ADDED); assertThat(importStatuses.get(2).getErrors()).isEmpty(); assertThat(importStatuses.get(3).getName()).isEqualTo(APP_4_TOKEN); assertThat(importStatuses.get(3).getStatus()).isEqualTo(ImportStatus.Status.ADDED); assertThat(importStatuses.get(3).getErrors()).isEmpty(); assertAppLink1(); assertAppLink2(); assertAppLink3(); assertAppLink4(); } @Test public void mixed_legacy_and_application_links_should_be_imported_successfully_twice() throws Exception { //given String xmlToImport = "/applications-importer/mixedLegacyAndApplicationLinks.xml"; ApplicationImportStrategy updateStrategy = (a1, a2) -> ApplicationImportStrategy.ImportStrategy.REPLACE; // create page mandatory for app3 createDummyPage(); //when List importStatuses = importApplicationsFromXml(xmlToImport); assertThat(importStatuses).hasSize(4); // and re-import try (var xmlAsStream = this.getClass().getResourceAsStream(xmlToImport)) { assertThat(xmlAsStream).isNotNull(); importStatuses = getTransactionService() .executeInTransaction(() -> applicationImporter.importApplications(xmlAsStream.readAllBytes(), null, null, SessionInfos.getUserIdFromSession(), updateStrategy)); } //then assertThat(importStatuses).hasSize(4); assertThat(importStatuses.get(0).getName()).isEqualTo(APP_1_TOKEN); assertThat(importStatuses.get(0).getStatus()).isEqualTo(ImportStatus.Status.REPLACED); assertThat(importStatuses.get(0).getErrors()).isEmpty(); assertThat(importStatuses.get(1).getName()).isEqualTo(APP_2_TOKEN); assertThat(importStatuses.get(1).getStatus()).isEqualTo(ImportStatus.Status.REPLACED); assertThat(importStatuses.get(1).getErrors()).isEmpty(); assertThat(importStatuses.get(2).getName()).isEqualTo(APP_3_TOKEN); assertThat(importStatuses.get(2).getStatus()).isEqualTo(ImportStatus.Status.REPLACED); assertThat(importStatuses.get(2).getErrors()).isEmpty(); assertThat(importStatuses.get(3).getName()).isEqualTo(APP_4_TOKEN); assertThat(importStatuses.get(3).getStatus()).isEqualTo(ImportStatus.Status.REPLACED); assertThat(importStatuses.get(3).getErrors()).isEmpty(); assertAppLink1(); assertAppLink2(); assertAppLink3(); assertAppLink4(); } private List importApplicationsFromXml(String xmlToImport) throws Exception { try (var xmlAsStream = this.getClass().getResourceAsStream(xmlToImport)) { assertThat(xmlAsStream).isNotNull(); return getTransactionService() .executeInTransaction(() -> applicationImporter.importApplications(xmlAsStream.readAllBytes(), null, null, SessionInfos.getUserIdFromSession(), null)); } } private void assertAppNotExists(String appToken) throws Exception { SApplication existingApp1 = getTransactionService() .executeInTransaction(() -> applicationService.getApplicationByToken(appToken)); assertThat(existingApp1).isNull(); } private void assertAppLink1() throws Exception { SApplication app = getTransactionService() .executeInTransaction(() -> applicationService.getApplicationByToken(APP_1_TOKEN)); assertThat(app).isNotNull(); assertThat(app.getDisplayName()).isEqualTo("Application 1"); assertThat(app.getDescription()).isEqualTo("Description of Application 1"); assertThat(app.getVersion()).isEqualTo("1.0"); assertThat(app.getIconPath()).isEqualTo("/app1.jpg"); assertThat(app.getCreatedBy()).isEqualTo(-1L); assertThat(app.getState()).isEqualTo(SApplicationState.ACTIVATED.name()); assertThat(getProfile(app.getProfileId()).getName()).isEqualTo("User"); assertThat(app.getInternalProfile()).isNull(); assertThat(app.getHomePageId()).isNull(); assertThat(app.getLayoutId()).isNull(); assertThat(app.getThemeId()).isNull(); assertThat(app.isEditable()).isTrue(); assertThat(app.isLink()).isTrue(); } private SProfile getProfile(Long profileId) throws Exception { return getTransactionService().executeInTransaction(() -> profileService.getProfile(profileId)); } private void assertAppLink2() throws Exception { SApplication app = getTransactionService() .executeInTransaction(() -> applicationService.getApplicationByToken(APP_2_TOKEN)); assertThat(app).isNotNull(); assertThat(app.getDisplayName()).isEqualTo("Application 2"); assertThat(app.getDescription()).isNull(); assertThat(app.getVersion()).isEqualTo("1.1"); assertThat(app.getIconPath()).isNull(); assertThat(app.getCreatedBy()).isEqualTo(-1L); assertThat(app.getState()).isEqualTo(SApplicationState.DEACTIVATED.name()); assertThat(app.getProfileId()).isNull(); assertThat(app.getInternalProfile()).isNull(); assertThat(app.getHomePageId()).isNull(); assertThat(app.getLayoutId()).isNull(); assertThat(app.getThemeId()).isNull(); assertThat(app.isEditable()).isTrue(); assertThat(app.isLink()).isTrue(); } private void assertAppLink3() throws Exception { SApplication app = getTransactionService() .executeInTransaction(() -> applicationService.getApplicationByToken(APP_3_TOKEN)); assertThat(app).isNotNull(); assertThat(app.getDisplayName()).isEqualTo("Application 3"); assertThat(app.getDescription()).isEqualTo("Description of Application 3"); assertThat(app.getVersion()).isEqualTo("2.0"); assertThat(app.getIconPath()).isEqualTo("/app3.jpg"); assertThat(app.getCreatedBy()).isEqualTo(-1L); assertThat(app.getState()).isEqualTo(SApplicationState.ACTIVATED.name()); assertThat(app.getHomePageId()).isNotNull(); assertThat(getProfile(app.getProfileId()).getName()).isEqualTo("User"); assertThat(app.getInternalProfile()).isNull(); assertThat(app.getLayoutId()).isNotNull(); assertThat(app.getThemeId()).isNotNull(); assertThat(app.isEditable()).isTrue(); assertThat(app.isLink()).isFalse(); } private void assertAppLink4() throws Exception { SApplication app = getTransactionService() .executeInTransaction(() -> applicationService.getApplicationByToken(APP_4_TOKEN)); assertThat(app).isNotNull(); assertThat(app.getDisplayName()).isEqualTo("Application 4"); assertThat(app.getDescription()).isNull(); assertThat(app.getVersion()).isEqualTo("2.0"); assertThat(app.getIconPath()).isNull(); assertThat(app.getCreatedBy()).isEqualTo(-1L); assertThat(app.getState()).isEqualTo(SApplicationState.DEACTIVATED.name()); assertThat(app.getProfileId()).isNull(); assertThat(app.getInternalProfile()).isNull(); assertThat(app.getHomePageId()).isNull(); assertThat(app.getLayoutId()).isNotNull(); assertThat(app.getThemeId()).isNotNull(); assertThat(app.isEditable()).isTrue(); assertThat(app.isLink()).isFalse(); } private void createDummyPage() throws Exception { try (var contentStream = this.getClass().getResourceAsStream("/applications-importer/dummy-bizapp-page.zip")) { assertThat(contentStream).isNotNull(); getTransactionService().executeInTransaction(() -> pageService.addPage(contentStream.readAllBytes(), "custompage_mynewcustompage", SessionInfos.getUserIdFromSession())); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/business/data/BDRepositoryLocalIT.java ================================================ /** * 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.business.data; import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import java.io.Serializable; import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.bdm.BusinessObjectModelConverter; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.Query; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.RelationField; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.business.data.impl.EntityManagerFactoryAware; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.platform.setup.PlatformSetup; import org.junit.After; import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; public class BDRepositoryLocalIT extends CommonAPIIT { private static final String FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER = "findByFirstNameAndLastNameNewOrder"; private static final String BIZ_GRENOBLE_ADDRESS = "bizGrenobleAddress"; private static final String BIZ_SF_ADDRESS = "bizSfAddress"; private static final String BIZ_ROME_ADDRESS = "bizRomeAddress"; private static final String BIZ_EMPLOYEE = "bizEmployee"; private static final String PROCESS_NAME = "lazy"; private static final String VERSION = "1.0"; private static final String TASK_AUTOMATIC_TASK_TO_INIT_BIZ_DATA = "automaticTaskToInitBizData"; private static final String TASK_TO_CALL_JAVA_METHOD_OPERATION = "automaticTaskToCallJavaMethodOperation"; private static final String TASK_HUMAN_TASK = "humanTask"; private static final String CITY_SF = "S.F."; private static final String CITY_GRENOBLE = "Grenoble"; private static final String CITY_ROME = "Rome"; private static final String COUNTRY_ITALY = "Italy"; private static final String COUNTRY_FRANCE = "France"; private static final String COUNTRY_USA = "USA"; private static final String BDM_PACKAGE_PREFIX = "com.company.model"; public static final String PRODUCT_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + ".pojo.Product"; public static final String PRODUCT_CATALOG_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + ".pojo.ProductCatalog"; private static final String ADDRESS_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + ".pojo.Address"; private static final String EMPLOYEE_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + ".pojo.Employee"; private static final String COUNTRY_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + ".pojo.Country"; public static final String PERSON_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + ".pojo.Person"; private User matti; private File clientFolder; private ClassLoader contextClassLoaderBeforeAddingBPMClientZip; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Before public void setUp() throws Exception { clientFolder = temporaryFolder.newFolder(); loginWithTechnicalUser(); matti = createUser("matti", "bpm"); final BusinessObjectModelConverter converter = new BusinessObjectModelConverter(); final byte[] zip = converter.zip(buildCustomBOM()); getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); getTenantAdministrationAPI().updateBusinessDataModel(zip); getTenantAdministrationAPI().resume(); // needed for remote testing addClientBDMZipToClassLoader(); } @After public void tearDown() throws Exception { resumeClassloader(); try { FileUtils.deleteDirectory(clientFolder); } catch (final Exception e) { clientFolder.deleteOnExit(); } if (!getTenantAdministrationAPI().isPaused()) { getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); getTenantAdministrationAPI().resume(); } resumeClassloader(); deleteUser(matti); logout(); } private void resumeClassloader() { Thread.currentThread().setContextClassLoader(contextClassLoaderBeforeAddingBPMClientZip); } @Test public void should_get_lazy_object_outside_a_transaction() throws Exception { // given final Expression addressGrenobleExpression = createGrovyExpressionThatCreateAddressWithCityName(CITY_GRENOBLE, COUNTRY_FRANCE); final Expression addressSfExpression = createGrovyExpressionThatCreateAddressWithCityName(CITY_SF, COUNTRY_USA); final Expression addressRomeExpression = createGrovyExpressionThatCreateAddressWithCityName(CITY_ROME, COUNTRY_ITALY); final Expression employeeExpression = createGroovyExpressionThatCreateEmployeeWithOneAddress( BIZ_GRENOBLE_ADDRESS); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder().createNewInstance( PROCESS_NAME, VERSION); processDefinitionBuilder.addBusinessData(BIZ_EMPLOYEE, EMPLOYEE_QUALIFIED_NAME, null); processDefinitionBuilder.addBusinessData(BIZ_GRENOBLE_ADDRESS, ADDRESS_QUALIFIED_NAME, addressGrenobleExpression); processDefinitionBuilder.addBusinessData(BIZ_SF_ADDRESS, ADDRESS_QUALIFIED_NAME, addressSfExpression); processDefinitionBuilder.addBusinessData(BIZ_ROME_ADDRESS, ADDRESS_QUALIFIED_NAME, addressRomeExpression); processDefinitionBuilder.addActor(ACTOR_NAME); // create employee and addresses processDefinitionBuilder.addAutomaticTask(TASK_AUTOMATIC_TASK_TO_INIT_BIZ_DATA) .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(BIZ_SF_ADDRESS), OperatorType.ASSIGNMENT, null, null, addressSfExpression) .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(BIZ_GRENOBLE_ADDRESS), OperatorType.ASSIGNMENT, null, null, addressGrenobleExpression) .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(BIZ_ROME_ADDRESS), OperatorType.ASSIGNMENT, null, null, addressRomeExpression) .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(BIZ_EMPLOYEE), OperatorType.ASSIGNMENT, null, null, employeeExpression); // call java operation to add address to employee processDefinitionBuilder.addAutomaticTask(TASK_TO_CALL_JAVA_METHOD_OPERATION) .addOperation( new OperationBuilder().createBusinessDataSetAttributeOperation(BIZ_EMPLOYEE, "addToAddresses", ADDRESS_QUALIFIED_NAME, new ExpressionBuilder().createBusinessDataExpression(BIZ_ROME_ADDRESS, ADDRESS_QUALIFIED_NAME))); // waiting task processDefinitionBuilder.addUserTask(TASK_HUMAN_TASK, ACTOR_NAME); // transitions processDefinitionBuilder.addTransition(TASK_AUTOMATIC_TASK_TO_INIT_BIZ_DATA, TASK_TO_CALL_JAVA_METHOD_OPERATION); processDefinitionBuilder.addTransition(TASK_TO_CALL_JAVA_METHOD_OPERATION, TASK_HUMAN_TASK); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDefinitionBuilder.done(), ACTOR_NAME, matti); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); final HumanTaskInstance humanTaskInstance = waitForUserTaskAndGetIt(processInstance, TASK_HUMAN_TASK); // then verifyLazyAddressesCount(humanTaskInstance, 2); verifySimpleFieldInAddresses(humanTaskInstance, CITY_GRENOBLE); verifyEagerCountryFieldInAddresses(humanTaskInstance, COUNTRY_FRANCE); // cleanup disableAndDeleteProcess(definition.getId()); } private void addClientBDMZipToClassLoader() throws Exception { contextClassLoaderBeforeAddingBPMClientZip = Thread.currentThread().getContextClassLoader(); final byte[] clientBDMZip = getTenantAdministrationAPI().getClientBDMZip(); final ClassLoader classLoaderWithBDM = new ClassloaderRefresher().loadClientModelInClassloader(clientBDMZip, contextClassLoaderBeforeAddingBPMClientZip, EMPLOYEE_QUALIFIED_NAME, clientFolder); Thread.currentThread().setContextClassLoader(classLoaderWithBDM); } private void verifyLazyAddressesCount(final HumanTaskInstance humanTaskInstance, final int expectedCount) throws Exception { final Map map = new HashMap<>(); final Map> expressions = new HashMap<>(); final Expression createQueryBusinessDataExpression = new ExpressionBuilder().createQueryBusinessDataExpression( "expression Name", "Employee." + FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER, EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createConstantStringExpression("firstName", "Alphonse"), new ExpressionBuilder().createConstantStringExpression("lastName", "Dupond")); final Expression countExpression = new ExpressionBuilder().createGroovyScriptExpression("countExpression", "myEmployee.getAddresses().size()", Integer.class.getName(), new ExpressionBuilder().createInputExpression("myEmployee", EMPLOYEE_QUALIFIED_NAME)); expressions.put(createQueryBusinessDataExpression, map); final Map evaluateExpressionsAtProcessInstanciation = getProcessAPI() .evaluateExpressionsOnActivityInstance( humanTaskInstance.getId(), expressions); final Serializable businessData = evaluateExpressionsAtProcessInstanciation .get(createQueryBusinessDataExpression.getName()); final Map> expressions2 = new HashMap<>(); expressions2.put(countExpression, Collections.singletonMap("myEmployee", businessData)); final Map evaluateExpressionsOnActivityInstance = getProcessAPI() .evaluateExpressionsOnActivityInstance( humanTaskInstance.getId(), expressions2); final Serializable serializable = evaluateExpressionsOnActivityInstance.get("countExpression"); assertThat(serializable).as("should get " + expectedCount + " address count").isEqualTo(expectedCount); } private void verifyEagerCountryFieldInAddresses(final HumanTaskInstance humanTaskInstance, final String expectedCountry) throws Exception { final Map map = new HashMap<>(); final Map> mapGetEmployee = new HashMap<>(); final Expression getEmployeeExpression = new ExpressionBuilder().createQueryBusinessDataExpression( "expression Name", "Employee." + FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER, EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createConstantStringExpression("firstName", "Alphonse"), new ExpressionBuilder().createConstantStringExpression("lastName", "Dupond")); final String queryName = getEmployeeExpression.getName(); final String getCountryExpressionName = "country"; final String script = "myEmployee.getAddresses().get(0).getCountry().getName()"; final Expression getCountryExpression = new ExpressionBuilder().createGroovyScriptExpression( getCountryExpressionName, script, String.class.getName(), new ExpressionBuilder().createInputExpression("myEmployee", EMPLOYEE_QUALIFIED_NAME)); mapGetEmployee.put(getEmployeeExpression, map); final Map getEmployeeResultMap = getProcessAPI().evaluateExpressionsOnActivityInstance( humanTaskInstance.getId(), mapGetEmployee); final Serializable returnedEmployee = getEmployeeResultMap.get(queryName); final Map> mapGetCountry = new HashMap<>(); mapGetCountry.put(getCountryExpression, Collections.singletonMap("myEmployee", returnedEmployee)); final Map getCountryResultMap = getProcessAPI().evaluateExpressionsOnActivityInstance( humanTaskInstance.getId(), mapGetCountry); final Serializable serializable = getCountryResultMap.get(getCountryExpressionName); assertThat(serializable).as("should get " + expectedCountry + " address count").isEqualTo(expectedCountry); } private void verifySimpleFieldInAddresses(final HumanTaskInstance humanTaskInstance, final String expectedCity) throws Exception { final Map map = new HashMap<>(); final Map> mapGetEmployee = new HashMap<>(); final Expression getEmployeeExpression = new ExpressionBuilder().createQueryBusinessDataExpression( "expression Name", "Employee." + FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER, EMPLOYEE_QUALIFIED_NAME, new ExpressionBuilder().createConstantStringExpression("firstName", "Alphonse"), new ExpressionBuilder().createConstantStringExpression("lastName", "Dupond")); final String queryName = getEmployeeExpression.getName(); final String cityExpression = "city"; final Expression getCountryExpression = new ExpressionBuilder().createGroovyScriptExpression(cityExpression, "myEmployee.getAddresses().get(0).getCity()", String.class.getName(), new ExpressionBuilder().createInputExpression("myEmployee", EMPLOYEE_QUALIFIED_NAME)); mapGetEmployee.put(getEmployeeExpression, map); final Map getEmployeeResultMap = getProcessAPI().evaluateExpressionsOnActivityInstance( humanTaskInstance.getId(), mapGetEmployee); final Serializable returnedEmployee = getEmployeeResultMap.get(queryName); final Map> mapGetCountry = new HashMap<>(); mapGetCountry.put(getCountryExpression, Collections.singletonMap("myEmployee", returnedEmployee)); final Map cityResultMap = getProcessAPI().evaluateExpressionsOnActivityInstance( humanTaskInstance.getId(), mapGetCountry); final Serializable cityResult = cityResultMap.get(cityExpression); assertThat(cityResult).as("should get city name" + expectedCity).isEqualTo(expectedCity); } private Expression createGroovyExpressionThatCreateEmployeeWithOneAddress(final String businessDataAddressName) throws InvalidExpressionException { String script = "import " + EMPLOYEE_QUALIFIED_NAME + "; import " + ADDRESS_QUALIFIED_NAME + "; Employee e = new Employee(); e.firstName = 'Alphonse'; e.lastName = 'Dupond'; e.addToAddresses(" + businessDataAddressName + "); return e;"; return new ExpressionBuilder().createGroovyScriptExpression("createNewEmployee", script, EMPLOYEE_QUALIFIED_NAME, createBusinessDataExpressionWithName(businessDataAddressName)); } private Expression createBusinessDataExpressionWithName(final String businessDataName) throws InvalidExpressionException { Expression createBusinessDataExpression; createBusinessDataExpression = new ExpressionBuilder().createBusinessDataExpression(businessDataName, ADDRESS_QUALIFIED_NAME); return createBusinessDataExpression; } private Expression createGrovyExpressionThatCreateAddressWithCityName(final String city, final String country) throws InvalidExpressionException { final Expression addressExpression; final StringBuilder builder = new StringBuilder(); builder.append("import ") .append(ADDRESS_QUALIFIED_NAME) .append("; ") .append("import ") .append(COUNTRY_QUALIFIED_NAME) .append("; ") .append("Country country = new Country(); ") .append("country.name='") .append(country) .append("'; ") .append("Address address = new Address();") .append("address.street='32, rue Gustave Eiffel'; ") .append("address.city='") .append(city) .append("'; ") .append("address.country = country; ") .append("address;"); addressExpression = new ExpressionBuilder().createGroovyScriptExpression("createNewAddress", builder.toString(), ADDRESS_QUALIFIED_NAME); return addressExpression; } private BusinessObjectModel buildCustomBOM() { final SimpleField name = new SimpleField(); name.setName("name"); name.setType(FieldType.STRING); final BusinessObject countryBO = new BusinessObject(); countryBO.setQualifiedName(COUNTRY_QUALIFIED_NAME); countryBO.addField(name); final SimpleField street = new SimpleField(); street.setName("street"); street.setType(FieldType.STRING); final SimpleField city = new SimpleField(); city.setName("city"); city.setType(FieldType.STRING); final RelationField country = new RelationField(); country.setType(RelationField.Type.AGGREGATION); country.setFetchType(RelationField.FetchType.EAGER); country.setName("country"); country.setCollection(Boolean.FALSE); country.setNullable(Boolean.TRUE); country.setReference(countryBO); final BusinessObject addressBO = new BusinessObject(); addressBO.setQualifiedName(ADDRESS_QUALIFIED_NAME); addressBO.addField(street); addressBO.addField(city); addressBO.addField(country); final RelationField addresses = new RelationField(); addresses.setType(RelationField.Type.AGGREGATION); addresses.setFetchType(RelationField.FetchType.LAZY); addresses.setName("addresses"); addresses.setCollection(Boolean.TRUE); addresses.setNullable(Boolean.TRUE); addresses.setReference(addressBO); final SimpleField firstName = new SimpleField(); firstName.setName("firstName"); firstName.setType(FieldType.STRING); firstName.setLength(Integer.valueOf(10)); final SimpleField lastName = new SimpleField(); lastName.setName("lastName"); lastName.setType(FieldType.STRING); lastName.setNullable(Boolean.FALSE); final SimpleField phoneNumbers = new SimpleField(); phoneNumbers.setName("phoneNumbers"); phoneNumbers.setType(FieldType.STRING); phoneNumbers.setLength(Integer.valueOf(10)); phoneNumbers.setCollection(Boolean.TRUE); final BusinessObject employee = new BusinessObject(); employee.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); employee.addField(firstName); employee.addField(lastName); employee.addField(phoneNumbers); employee.addField(addresses); employee.setDescription("Describe a simple employee"); employee.addUniqueConstraint("uk_fl", "firstName", "lastName"); final Query findByFirstNAmeAndLastNameNewOrder = employee.addQuery(FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER, "SELECT e FROM Employee e WHERE e.firstName =:firstName AND e.lastName = :lastName ORDER BY e.lastName", List.class.getName()); findByFirstNAmeAndLastNameNewOrder.addQueryParameter("firstName", String.class.getName()); findByFirstNAmeAndLastNameNewOrder.addQueryParameter("lastName", String.class.getName()); employee.addIndex("IDX_LSTNM", "lastName"); final BusinessObject person = new BusinessObject(); person.setQualifiedName(PERSON_QUALIFIED_NAME); person.addField(firstName); person.addField(lastName); person.addField(phoneNumbers); person.setDescription("Describe a simple person"); person.addUniqueConstraint("uk_fl", "firstName", "lastName"); final BusinessObject productBO = new BusinessObject(); productBO.setQualifiedName(PRODUCT_QUALIFIED_NAME); productBO.addField(name); final RelationField products = new RelationField(); products.setType(RelationField.Type.AGGREGATION); products.setFetchType(RelationField.FetchType.LAZY); products.setName("products"); products.setCollection(Boolean.TRUE); products.setNullable(Boolean.TRUE); products.setReference(productBO); final BusinessObject catalogBO = new BusinessObject(); catalogBO.setQualifiedName(PRODUCT_CATALOG_QUALIFIED_NAME); catalogBO.addField(name); catalogBO.addField(products); final BusinessObjectModel model = new BusinessObjectModel(); model.addBusinessObject(employee); model.addBusinessObject(person); model.addBusinessObject(addressBO); model.addBusinessObject(countryBO); model.addBusinessObject(productBO); model.addBusinessObject(catalogBO); return model; } @Test public void deploy_a_BDR_and_verify_sequence_behaviour_by_DBVendor() throws Exception { String dbVendor = PlatformSetup.getPropertyBonitaBdmDbVendor(); Assume.assumeTrue("We don't test sequence behaviour on h2", !dbVendor.equals("h2")); switch (dbVendor) { case "postgres": assertThat(execute_native_sql("SELECT 0 FROM pg_class where relname = 'hibernate_sequence'")) .containsOnly(0); break; case "oracle": assertThat(((List) execute_native_sql( "SELECT COUNT(*) FROM user_sequences WHERE sequence_name = 'HIBERNATE_SEQUENCE'")).get(0) .intValue()).isEqualTo(1); break; case "mysql": assertThat(Arrays.toString((Object[]) execute_native_sql("describe EMPLOYEE").get(0))) .contains("auto_increment", "persistenceId"); break; case "sqlserver": assertThat(Arrays.toString((Object[]) execute_native_sql("exec sp_columns EMPLOYEE").get(0))) .contains("bigint identity", "persistenceId"); break; } } private List execute_native_sql(String query) throws Exception { ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance(); return serviceAccessor.getUserTransactionService().executeInTransaction( () -> ((EntityManagerFactoryAware) serviceAccessor.getBusinessDataRepository()) .getEntityManagerFactory().createEntityManager().createNativeQuery(query).getResultList()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/business/data/DataRetentionBdmTrackingIT.java ================================================ /** * Copyright (C) 2026 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.business.data; import static org.assertj.core.api.Assertions.assertThat; import java.util.List; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.engine.transaction.UserTransactionService; import org.junit.Before; import org.junit.Test; /** * Integration test verifying that a tracking record is inserted into * {@code data_retention_bdm_tracking} when a BDM object is created via process execution. */ public class DataRetentionBdmTrackingIT extends CommonAPIIT { private static final String INVOICE_QUALIFIED_NAME = "com.company.model.Invoice"; private static final String BIZ_INVOICE = "bizInvoice"; private UserTransactionService userTransactionService; private DataRetentionBdmTrackingRepository trackingRepository; @Before public void setUp() throws Exception { ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance(); userTransactionService = serviceAccessor.getUserTransactionService(); trackingRepository = serviceAccessor.lookup(DataRetentionBdmTrackingRepository.class); loginWithTechnicalUser(); createUser("testUser", "bpm"); installBusinessDataModel(buildBom()); } // No @After needed: CommonAPIIT.clean() handles process definitions, users, BDM, trackings, and logout @Test public void should_insert_tracking_record_when_bdm_object_is_created_via_process() throws Exception { // given — a process that creates an Invoice BDM object var groovyScript = "import " + INVOICE_QUALIFIED_NAME + "; Invoice invoice = new Invoice(); invoice.reference = 'INV-001'; return invoice;"; var initExpression = new ExpressionBuilder().createGroovyScriptExpression( "createInvoice", groovyScript, INVOICE_QUALIFIED_NAME); var builder = new ProcessDefinitionBuilder().createNewInstance("TrackingTestProcess", "1.0"); builder.addActor("actor"); builder.addBusinessData(BIZ_INVOICE, INVOICE_QUALIFIED_NAME, null); builder.addAutomaticTask("createInvoice") .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(BIZ_INVOICE), OperatorType.ASSIGNMENT, null, null, initExpression); builder.addUserTask("wait", "actor"); builder.addTransition("createInvoice", "wait"); var definition = deployAndEnableProcessWithActor(builder.done(), "actor", getIdentityAPI().getUserByUserName("testUser")); // when — start the process (BDM object is created in the automatic task) ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); waitForUserTaskAndGetIt(processInstance, "wait"); // then — verify a tracking record was inserted via the repository List trackingRecords = userTransactionService.executeInTransaction( () -> trackingRepository.getByClassname(INVOICE_QUALIFIED_NAME)); assertThat(trackingRecords).hasSize(1); SDataRetentionBdmTracking tracking = trackingRecords.get(0); assertThat(tracking.getDataId()).isGreaterThan(0); assertThat(tracking.getDataClassname()).isEqualTo(INVOICE_QUALIFIED_NAME); assertThat(tracking.getCreatedAt()).isPositive(); assertThat(tracking.getLastModifiedAt()).isEqualTo(tracking.getCreatedAt()); } @Test public void should_not_insert_duplicate_tracking_on_update() throws Exception { // given — a process that creates then updates an Invoice BDM object var createScript = "import " + INVOICE_QUALIFIED_NAME + "; Invoice invoice = new Invoice(); invoice.reference = 'INV-002'; return invoice;"; var createExpression = new ExpressionBuilder().createGroovyScriptExpression( "createInvoice", createScript, INVOICE_QUALIFIED_NAME); var builder = new ProcessDefinitionBuilder().createNewInstance("TrackingUpdateProcess", "1.0"); builder.addActor("actor"); builder.addBusinessData(BIZ_INVOICE, INVOICE_QUALIFIED_NAME, null); // step 1: create the BDM object builder.addAutomaticTask("createInvoice") .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(BIZ_INVOICE), OperatorType.ASSIGNMENT, null, null, createExpression); // step 2: update the same BDM object (set a new reference) var updateScript = BIZ_INVOICE + ".reference = 'INV-002-UPDATED'; return " + BIZ_INVOICE + ";"; var updateExpression = new ExpressionBuilder().createGroovyScriptExpression( "updateInvoice", updateScript, INVOICE_QUALIFIED_NAME, new ExpressionBuilder().createBusinessDataExpression(BIZ_INVOICE, INVOICE_QUALIFIED_NAME)); builder.addAutomaticTask("updateInvoice") .addOperation(new LeftOperandBuilder().createBusinessDataLeftOperand(BIZ_INVOICE), OperatorType.ASSIGNMENT, null, null, updateExpression); builder.addUserTask("wait", "actor"); builder.addTransition("createInvoice", "updateInvoice"); builder.addTransition("updateInvoice", "wait"); var definition = deployAndEnableProcessWithActor(builder.done(), "actor", getIdentityAPI().getUserByUserName("testUser")); // when ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); waitForUserTaskAndGetIt(processInstance, "wait"); // then — still only one tracking record (update does not create a second one) List trackingRecords = userTransactionService.executeInTransaction( () -> trackingRepository.getByClassname(INVOICE_QUALIFIED_NAME)); assertThat(trackingRecords).hasSize(1); } private static BusinessObjectModel buildBom() { var reference = new SimpleField(); reference.setName("reference"); reference.setType(FieldType.STRING); var invoiceBO = new BusinessObject(); invoiceBO.setQualifiedName(INVOICE_QUALIFIED_NAME); invoiceBO.addField(reference); var bom = new BusinessObjectModel(); bom.addBusinessObject(invoiceBO); return bom; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/business/data/TransactionTimeoutEntityManagerIT.java ================================================ /** * 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.business.data; import static org.assertj.core.api.Assertions.assertThat; import java.lang.reflect.Field; import javax.persistence.EntityManager; import javax.transaction.Status; import javax.transaction.TransactionManager; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.bdm.BusinessObjectModelConverter; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.business.data.impl.JPABusinessDataRepositoryImpl; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.engine.transaction.JTATransactionServiceImpl; import org.bonitasoft.engine.transaction.STransactionException; import org.bonitasoft.engine.transaction.TransactionService; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.test.util.AopTestUtils; /** * Integration tests for EntityManager lifecycle under various JTA transaction outcomes. * Uses the full Bonita engine with real Narayana JTA transactions to reproduce actual behavior, * particularly the transaction timeout scenario (BPA-321). */ @Slf4j public class TransactionTimeoutEntityManagerIT extends CommonAPIIT { private JPABusinessDataRepositoryImpl bdmRepository; private TransactionService transactionService; @Before public void setUp() throws Exception { loginWithTechnicalUser(); // Deploy a minimal BDM so the EntityManagerFactory is created final BusinessObjectModelConverter converter = new BusinessObjectModelConverter(); final byte[] zip = converter.zip(buildMinimalBOM()); getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); getTenantAdministrationAPI().updateBusinessDataModel(zip); getTenantAdministrationAPI().resume(); // Get the real JPABusinessDataRepositoryImpl from the engine // Unwrap Spring AOP proxy (created by BusinessDataRepositoryEventAspect) to access internal fields via reflection ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance(); bdmRepository = AopTestUtils.getTargetObject(serviceAccessor.getBusinessDataRepository()); transactionService = serviceAccessor.getTransactionService(); } @After public void tearDown() throws Exception { // Clean up ThreadLocal to avoid leaking to the next test clearManagersThreadLocal(); if (!getTenantAdministrationAPI().isPaused()) { getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); getTenantAdministrationAPI().resume(); } logout(); } /** * Scenario (a): Nominal — transaction commits successfully. * The EntityManager is created, used, and properly cleaned up by afterCompletion(STATUS_COMMITTED). */ @Test public void afterCompletion_should_cleanup_entityManager_on_successful_commit() throws Exception { //given transactionService.begin(); //when — trigger getEntityManager() which registers the RemoveEntityManagerSynchronization bdmRepository.getEntityClassNames(); EntityManager emDuringTx = getManagersThreadLocalValue(); assertThat(emDuringTx).as("EM should exist during transaction").isNotNull(); assertThat(emDuringTx.isOpen()).isTrue(); transactionService.complete(); //then — afterCompletion(STATUS_COMMITTED) should have closed and removed the EM EntityManager emAfterCommit = getManagersThreadLocalValue(); assertThat(emAfterCommit).as("EM should be removed from ThreadLocal after commit").isNull(); } /** * Scenario (b): Application rollback — transaction is rolled back explicitly. * The EntityManager should be cleaned up by afterCompletion(STATUS_ROLLEDBACK). */ @Test public void afterCompletion_should_cleanup_entityManager_on_explicit_rollback() throws Exception { //given transactionService.begin(); bdmRepository.getEntityClassNames(); EntityManager emDuringTx = getManagersThreadLocalValue(); assertThat(emDuringTx).as("EM should exist during transaction").isNotNull(); //when rollbackTransaction(); //then — afterCompletion(STATUS_ROLLEDBACK) should have closed and removed the EM EntityManager emAfterRollback = getManagersThreadLocalValue(); assertThat(emAfterRollback).as("EM should be removed from ThreadLocal after rollback").isNull(); } /** * Scenario (c): Transaction marked rollback-only (setRollbackOnly), then commit attempt. * commit() triggers a rollback. The EntityManager should still be cleaned up. */ @Test public void afterCompletion_should_cleanup_entityManager_on_rollback_only_commit_attempt() throws Exception { //given transactionService.begin(); bdmRepository.getEntityClassNames(); EntityManager emDuringTx = getManagersThreadLocalValue(); assertThat(emDuringTx).isNotNull(); //when rollbackTransaction(); //then — afterCompletion(STATUS_ROLLEDBACK) should have closed and removed the EM EntityManager emAfter = getManagersThreadLocalValue(); assertThat(emAfter).as("EM should be removed from ThreadLocal after rollback-only commit").isNull(); } /** * Scenario (d): Transaction timeout — Narayana's TransactionReaper aborts the transaction. * This is the BPA-321 scenario. The reaper calls afterCompletion on its own thread, * so ThreadLocal.get() returns null and the EM is NOT cleaned up. *

* After the timeout, the defensive getEntityManager() should detect the stale EM * (not joined to any transaction) and discard it, creating a fresh one for the next tx. */ @Test public void getEntityManager_should_detect_stale_entityManager_after_transaction_timeout_and_create_fresh_one() throws Exception { //given — start a transaction with a 1-second timeout TransactionManager txManager = getTransactionManager(); txManager.setTransactionTimeout(1); transactionService.begin(); // Trigger EntityManager creation within the transaction bdmRepository.getEntityClassNames(); EntityManager emDuringTx = getManagersThreadLocalValue(); assertThat(emDuringTx).as("EM should exist during transaction").isNotNull(); //when — wait for the timeout to fire log.info("Waiting for transaction timeout (1s)..."); Thread.sleep(2000); // The transaction should have been aborted by the reaper int status = txManager.getStatus(); log.info("Transaction status after timeout: {}", status); assertThat(status).as("Transaction should be rolled back or no longer active") .isIn(Status.STATUS_ROLLEDBACK, Status.STATUS_NO_TRANSACTION, Status.STATUS_MARKED_ROLLBACK); // The stale EM may still be in the ThreadLocal (the reaper thread couldn't clean it) EntityManager staleEM = getManagersThreadLocalValue(); log.info("Stale EM still in ThreadLocal after timeout: {}", staleEM != null); // Clean up the timed-out transaction if needed if (txManager.getStatus() != Status.STATUS_NO_TRANSACTION) { try { rollbackTransaction(); } catch (Exception e) { log.info("Rollback of timed-out tx: {}", e.getMessage()); } } //then — start a new transaction and verify the defensive getEntityManager() works txManager.setTransactionTimeout(0); // restore default timeout transactionService.begin(); // The defensive getEntityManager() should detect the stale EM and create a fresh one bdmRepository.getEntityClassNames(); EntityManager freshEM = getManagersThreadLocalValue(); assertThat(freshEM).as("A fresh EntityManager should be provided for the new transaction").isNotNull(); assertThat(freshEM.isOpen()).isTrue(); assertThat(freshEM.isJoinedToTransaction()).isTrue(); // If the stale EM was still around, the fresh EM should be a different instance if (staleEM != null) { assertThat(freshEM).as("Fresh EM should be a different instance than the stale one") .isNotSameAs(staleEM); } rollbackTransaction(); } // --- Helpers --- private BusinessObjectModel buildMinimalBOM() { SimpleField nameField = new SimpleField(); nameField.setName("name"); nameField.setType(FieldType.STRING); BusinessObject bo = new BusinessObject(); bo.setQualifiedName("com.company.model.pojo.SimpleEntity"); bo.addField(nameField); BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(bo); return bom; } @SuppressWarnings("unchecked") private ThreadLocal getManagersThreadLocal() { try { Field managersField = JPABusinessDataRepositoryImpl.class.getDeclaredField("managers"); managersField.setAccessible(true); return (ThreadLocal) managersField.get(bdmRepository); } catch (Exception e) { throw new RuntimeException("Failed to access managers ThreadLocal", e); } } private EntityManager getManagersThreadLocalValue() { return getManagersThreadLocal().get(); } private void clearManagersThreadLocal() { getManagersThreadLocal().remove(); } private TransactionManager getTransactionManager() { try { Field txMgrField = JTATransactionServiceImpl.class.getDeclaredField("txManager"); txMgrField.setAccessible(true); return (TransactionManager) txMgrField.get(transactionService); } catch (Exception e) { throw new RuntimeException("Failed to access txManager field", e); } } private void rollbackTransaction() throws STransactionException { transactionService.setRollbackOnly(); transactionService.complete(); // will trigger rollback since it's marked as rollback-only } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/cache/ehcache/CacheConfigurationIT.java ================================================ /** * Copyright (C) 2022 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.cache.ehcache; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.junit.Before; import org.junit.Test; /** * @author Emmanuel Duchastenier */ public class CacheConfigurationIT extends CommonBPMServicesTest { EhCacheCacheService cacheService; @Before public void setUp() { cacheService = (EhCacheCacheService) getServiceAccessor().getPlatformCacheService(); } @Test public void all_required_cache_configurations_should_exist() { assertThat(cacheService.getCacheConfigurationNames()).containsExactlyInAnyOrder( "CONFIGURATION_FILES_CACHE", "USER_FILTER", "transient_data", "GROOVY_SCRIPT_CACHE_NAME", "_PROCESSDEF", "SYNCHRO_SERVICE_CACHE", "parameters", "DEFAULT_PLATFORM", "CONNECTOR", "application-token"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/ClassLoaderIT.java ================================================ /** * 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.classloader; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.io.IOUtil.generateJar; import static org.junit.Assert.assertEquals; import java.io.IOException; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.tuple.Pair; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.bdm.BusinessObjectModelConverter; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.connectors.TestConnector3; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.filter.user.TestFilterWithAutoAssign; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.engine.test.BuildTestUtil; import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; public class ClassLoaderIT extends TestWithUser { @Rule public SystemOutRule systemOutRule = new SystemOutRule().enableLog(); @After public void cleanUp() throws Exception { String businessDataModelVersion = getTenantAdministrationAPI().getBusinessDataModelVersion(); if (businessDataModelVersion != null) { getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); getTenantAdministrationAPI().resume(); } } @Test public void should_refresh_classloader_only_once_on_deploy_process() throws Exception { BusinessArchive businessArchive = createProcessWithDependencies(); User user = getIdentityAPI().createUser("john", "bpm"); systemOutRule.clearLog(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user); String processDeployLog = systemOutRule.getLog(); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); final ActivityInstance task = waitForUserTaskAndGetIt(processInstance, "step1"); assertEquals("stringFromPublicMethod", task.getDisplayName()); assertThat(processDeployLog) .containsOnlyOnce("Refreshing classloader PROCESS:" + processDefinition.getId()); } public String toString() { return "MyObject"; } @Test public void should_be_able_to_execute_scripts_on_processes_having_same_classes() throws Exception { byte[] myObjectJar1 = generateJar( Pair.of("MyObject", "public interface MyObject extends java.io.Serializable {}"), Pair.of("MyObjectImpl1", "public class MyObjectImpl1 implements MyObject {\n" + " public String toString() {\n" + " return \"MyObjectImpl\";\n" + " }\n" + "}")); byte[] myObjectJar2 = generateJar( Pair.of("MyObject", "public interface MyObject extends java.io.Serializable {}"), Pair.of("MyObjectImpl2", "public class MyObjectImpl2 implements MyObject {\n" + " public String toString() {\n" + " return \"MyObjectImpl\";\n" + " }\n" + "}")); BusinessArchive bar1 = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance("process1 with MyObject", "1.0") .addData("myObject", "MyObject", new ExpressionBuilder().createGroovyScriptExpression("s1", "new MyObjectImpl1()", "MyObject")) .getProcess()) .addClasspathResource(new BarResource("myObjectJar.jar", myObjectJar1)).done(); BusinessArchive bar2 = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance("process2 with MyObject", "1.0") .addData("myObject", "MyObject", new ExpressionBuilder().createGroovyScriptExpression("s1", "new MyObjectImpl2()", "MyObject")) .getProcess()) .addClasspathResource(new BarResource("myObjectJar.jar", myObjectJar2)).done(); ProcessDefinition processDefinition1 = deployAndEnableProcess(bar1); ProcessDefinition processDefinition2 = deployAndEnableProcess(bar2); ProcessInstance processInstance1 = getProcessAPI().startProcess(processDefinition1.getId()); waitForProcessToFinish(processInstance1); ProcessInstance processInstance2 = getProcessAPI().startProcess(processDefinition2.getId()); waitForProcessToFinish(processInstance2); } @Test public void should_refresh_classloader_only_once_on_deploy_bdm() throws Exception { loginWithTechnicalUser(); final BusinessObjectModelConverter converter = new BusinessObjectModelConverter(); final byte[] zip = converter.zip(buildCustomBOM()); getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); systemOutRule.clearLog(); getTenantAdministrationAPI().updateBusinessDataModel(zip); String deployBDMLog = systemOutRule.getLog(); getTenantAdministrationAPI().resume(); assertThat(deployBDMLog).containsOnlyOnce("Refreshing classloader TENANT"); } @Test public void should_not_refresh_classloader_on_delete_process_definition() throws Exception { BusinessArchive businessArchive = createProcessWithDependencies(); User user = getIdentityAPI().createUser("john", "bpm"); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, user); systemOutRule.clearLog(); getProcessAPI().disableAndDeleteProcessDefinition(processDefinition.getId()); String processDeployLog = systemOutRule.getLog(); assertThat(processDeployLog) .doesNotContain("Refreshing classloader PROCESS:" + processDefinition.getId()); } @Test public void should_be_able_to_fix_groovy_script_by_updating_dependency_in_platform_classloader() throws Exception { PlatformSession session = loginOnPlatform(); PlatformAPIAccessor.getPlatformCommandAPI(session).addDependency("hello-there-1.0.0.jar", generateJar("Hello", "public class Hello{", "public String hello(){", "return \"Hello there!\";", "}", "}")); logoutOnPlatform(session); loginWithTechnicalUser(); ProcessDefinitionBuilder designProcessDefinition = new ProcessDefinitionBuilder().createNewInstance( "processWithDisplayName", "1.0"); designProcessDefinition.addActor(ACTOR_NAME); designProcessDefinition.addUserTask("step0", ACTOR_NAME).addDisplayName( new ExpressionBuilder().createGroovyScriptExpression("groovyExpr", "new Hello().hello()", String.class.getName())); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition.done(), ACTOR_NAME, user); ProcessInstance p1 = getProcessAPI().startProcess(processDefinition.getId()); assertThat(waitForUserTaskAndGetIt(p1, "step0").getDisplayName()).isEqualTo("Hello there!"); session = loginOnPlatform(); PlatformAPIAccessor.getPlatformCommandAPI(session).removeDependency("hello-there-1.0.0.jar"); PlatformAPIAccessor.getPlatformCommandAPI(session).addDependency("hello-there-1.0.1.jar", generateJar("Hello", "public class Hello{", "public String hello(){", "return \"Hello there! General Kenobi.\";", "}", "}")); logoutOnPlatform(session); loginWithTechnicalUser(); ProcessInstance p2 = getProcessAPI().startProcess(processDefinition.getId()); assertThat(waitForUserTaskAndGetIt(p2, "step0").getDisplayName()).isEqualTo("Hello there! General Kenobi."); disableAndDeleteProcess(processDefinition); } private BusinessObjectModel buildCustomBOM() { final SimpleField name = new SimpleField(); name.setName("name"); name.setType(FieldType.STRING); final BusinessObject somethings = new BusinessObject(); somethings.setQualifiedName("org.acme.pojo.Something"); somethings.addField(name); final BusinessObjectModel model = new BusinessObjectModel(); model.addBusinessObject(somethings); return model; } private BusinessArchive createProcessWithDependencies() throws InvalidExpressionException, InvalidProcessDefinitionException, IOException, InvalidBusinessArchiveFormatException { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder(); final ProcessDefinitionBuilder pBuilder = processDefinitionBuilder.createNewInstance("emptyProcess", String.valueOf(System.currentTimeMillis())); pBuilder.addShortTextData("aData", new ExpressionBuilder().createGroovyScriptExpression("myScript", "new org.bonitasoft.engine.test.TheClassOfMyLibrary().aPublicMethod()", String.class.getName())); pBuilder.addActor(ACTOR_NAME) .addUserTask("step1", ACTOR_NAME) .addDisplayName( new ExpressionBuilder().createGroovyScriptExpression("myScript", "new org.bonitasoft.engine.test.TheClassOfMyLibrary().aPublicMethod()", String.class.getName())); final DesignProcessDefinition designProcessDefinition = pBuilder.done(); final BusinessArchiveBuilder builder = new BusinessArchiveBuilder() .createNewBusinessArchive() .setProcessDefinition(designProcessDefinition); builder.addClasspathResource(new BarResource("mylibrary.jar", IOUtils.toByteArray(CommonAPIIT.class.getResourceAsStream("/mylibrary-jar.bak")))); builder.addClasspathResource(BuildTestUtil.generateJarAndBuildBarResource(TestFilterWithAutoAssign.class, "TestFilterWithAutoAssign.jar")); builder.addClasspathResource( BuildTestUtil.generateJarAndBuildBarResource(TestConnector3.class, "TestConnector3.jar")); return builder.done(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/ClassLoaderServiceIT.java ================================================ /** * 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.classloader; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import static org.bonitasoft.engine.commons.io.IOUtil.generateJar; import static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS; import static org.bonitasoft.engine.dependency.model.ScopeType.TENANT; import static org.junit.Assert.*; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; import java.util.Collections; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.CallableWithException; import org.bonitasoft.engine.RunnableWithException; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.io.IOUtil; import org.bonitasoft.engine.dependency.DependencyService; import org.bonitasoft.engine.dependency.SDependencyException; import org.bonitasoft.engine.dependency.model.SPlatformDependency; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.test.util.TestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros, Charles Souillard, Baptiste Mesta */ @Slf4j public class ClassLoaderServiceIT extends CommonBPMServicesTest { private DependencyService dependencyService; private DependencyService platformDependencyService; private ClassLoaderService classLoaderService; private static final long ID1 = 1; private static final long ID2 = 2; @After public void tearDown() throws Exception { TestUtil.closeTransactionIfOpen(getTransactionService()); getTransactionService().begin(); dependencyService.deleteDependencies(ID1, PROCESS); dependencyService.deleteDependencies(ID1, TENANT); dependencyService.deleteDependencies(ID2, PROCESS); dependencyService.deleteDependencies(ID2, TENANT); platformDependencyService.deleteDependencies(ClassLoaderIdentifier.GLOBAL_ID, ClassLoaderIdentifier.GLOBAL_TYPE); getTransactionService().complete(); classLoaderService.stop(); classLoaderService = null; dependencyService = null; platformDependencyService = null; } @Before public void setUp() throws SBonitaException { classLoaderService = getServiceAccessor().getClassLoaderService(); dependencyService = getServiceAccessor().getDependencyService(); platformDependencyService = getServiceAccessor().getPlatformDependencyService(); classLoaderService.start(); } private T inTx(CallableWithException runnable) throws Exception { getTransactionService().begin(); try { return runnable.call(); } finally { getTransactionService().complete(); } } private void inTx(RunnableWithException runnable) throws Exception { getTransactionService().begin(); try { runnable.run(); } finally { getTransactionService().complete(); } } private void initializeClassLoaderService() throws Exception { inTx(() -> { createPlatformDependency("globalResource", "globalResource.jar", generateJar(GlobalClass1.class, GlobalClass2.class, SharedClass1.class)); createDependency(ID1, PROCESS, "LocalResource1", "LocalResource1.jar", generateJar(LocalClass1.class, LocalClass2.class)); createDependency(ID2, PROCESS, "LocalResource1", "LocalResource1.jar", generateJar(LocalClass1.class, LocalClass2.class)); createDependency(ID1, PROCESS, "LocalResource2", "LocalResource2.jar", generateJar(LocalClass3.class, LocalClass4.class, SharedClass1.class)); }); } private void addNotInPathDependencies() throws Exception { inTx(() -> { createPlatformDependency("NotInPathGlobal", "NotInPathGlobal.jar", IOUtil.getAllContentFrom(ClassLoaderServiceIT.class.getResource("NotInPathGlobal.jar"))); createPlatformDependency("NotInPathShared", "NotInPathShared.jar", IOUtil.getAllContentFrom(ClassLoaderServiceIT.class.getResource("NotInPathShared.jar"))); createDependency(ID1, PROCESS, "NotInPathLocal", "NotInPathLocal.jar", IOUtil.getAllContentFrom(ClassLoaderServiceIT.class.getResource("NotInPathLocal.jar"))); }); } private long createDependency(final long artifactId, final ScopeType artifactType, final String name, final String fileName, final byte[] value) throws SDependencyException, SClassLoaderException { long id = dependencyService.createMappedDependency(name, value, fileName, artifactId, artifactType).getId(); classLoaderService.refreshClassLoaderImmediately(identifier(artifactType, artifactId)); log.info("Created {}:{} dependency with {}", artifactType, artifactId, fileName); return id; } private long createPlatformDependency(final String name, final String fileName, final byte[] value) throws SDependencyException, SClassLoaderException { final SPlatformDependency dependency = new SPlatformDependency(name, fileName, value); platformDependencyService.createMappedDependency(name, value, fileName, ClassLoaderIdentifier.GLOBAL_ID, ClassLoaderIdentifier.GLOBAL_TYPE); classLoaderService.refreshClassLoaderImmediately(ClassLoaderIdentifier.GLOBAL); log.info("Created platform dependency with {}", fileName); return dependency.getId(); } private void initializeClassLoaderServiceWithTwoApplications() throws Exception { getTransactionService().begin(); createPlatformDependency("globalResource", "globalResource.jar", generateJar(GlobalClass1.class, SharedClass1.class)); createDependency(ID1, PROCESS, "LocalResource111", "LocalResource1.jar", generateJar(LocalClass1.class)); createDependency(ID2, PROCESS, "LocalResource211", "LocalResource1.jar", generateJar(LocalClass1.class)); createDependency(ID1, PROCESS, "LocalResource123", "LocalResource3.jar", generateJar(LocalClass3.class)); createDependency(ID1, TENANT, "LocalResource122", "LocalResource2.jar", generateJar(LocalClass2.class)); getTransactionService().complete(); } @Test public void testLoadGlobalClassUsingGlobalClassLoader() throws Exception { initializeClassLoaderService(); // getTransactionService().begin(); final ClassLoader globalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL); final Class clazz = globalClassLoader.loadClass("org.bonitasoft.engine.classloader.GlobalClass1"); final ClassLoader classLoader = clazz.getClassLoader(); // getTransactionService().complete(); checkGlobalClassLoader(classLoader); assertSameClassloader(globalClassLoader, classLoader); } @Test public void testLoadLocalClassUsingLocalClassLoader() throws Exception { initializeClassLoaderService(); // getTransactionService().begin(); final ClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1)); final Class clazz = localClassLoader.loadClass("org.bonitasoft.engine.classloader.LocalClass1"); final ClassLoader classLoader = clazz.getClassLoader(); checkLocalClassLoader(classLoader); assertSameClassloader(localClassLoader, classLoader); // getTransactionService().complete(); } @Test public void testLoadGlobalClassUsingLocalClassLoader() throws Exception { initializeClassLoaderService(); // getTransactionService().begin(); final ClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1)); final Class clazz = localClassLoader.loadClass("org.bonitasoft.engine.classloader.GlobalClass1"); final ClassLoader classLoader = clazz.getClassLoader(); checkGlobalClassLoader(classLoader); assertNotSameClassloader(localClassLoader, classLoader); // getTransactionService().complete(); } @Test public void testLoadSharedClassUsingLocalClassLoader() throws Exception { initializeClassLoaderService(); // getTransactionService().begin(); final ClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1)); final Class clazz = localClassLoader.loadClass("org.bonitasoft.engine.classloader.SharedClass1"); final ClassLoader classLoader = clazz.getClassLoader(); checkLocalClassLoader(classLoader); assertSameClassloader(localClassLoader, classLoader); // getTransactionService().complete(); } @Test public void testLoadSharedClassUsingGlobalClassLoader() throws Exception { initializeClassLoaderService(); // getTransactionService().begin(); final ClassLoader globalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL); final Class clazz = globalClassLoader.loadClass("org.bonitasoft.engine.classloader.SharedClass1"); final ClassLoader classLoader = clazz.getClassLoader(); checkGlobalClassLoader(classLoader); assertSameClassloader(globalClassLoader, classLoader); // getTransactionService().complete(); } @Test public void testLoadOnlyInPathClassUsingGlobalClassLoader() throws Exception { initializeClassLoaderService(); // getTransactionService().begin(); final ClassLoader classLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL); final Class clazz = classLoader.loadClass("org.bonitasoft.engine.classloader.OnlyInPathClass1"); assertFalse(isBonitaClassLoader(clazz.getClassLoader())); // getTransactionService().complete(); } @Test public void testLoadOnlyInPathClassUsingLocalClassLoader() throws Exception { initializeClassLoaderService(); // getTransactionService().begin(); final ClassLoader classLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1)); final Class clazz = classLoader.loadClass("org.bonitasoft.engine.classloader.OnlyInPathClass1"); assertFalse(isBonitaClassLoader(clazz.getClassLoader())); // getTransactionService().complete(); } @Test public void testLoadLocalClassUsingGlobalClassLoader() throws Exception { initializeClassLoaderService(); // getTransactionService().begin(); final ClassLoader virtualGlobalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL); final Class clazz = virtualGlobalClassLoader.loadClass("org.bonitasoft.engine.classloader.LocalClass1"); assertFalse(isBonitaClassLoader(clazz.getClassLoader())); // getTransactionService().complete(); } @Test public void testGlobalClassLoaderIsSingleForTwoLocalClassLoaders() throws Exception { initializeClassLoaderService(); // getTransactionService().begin(); final ClassLoader localClassLoaderP1 = classLoaderService .getClassLoader(identifier(PROCESS, ID1)); final Class clazzP1 = localClassLoaderP1.loadClass("org.bonitasoft.engine.classloader.GlobalClass1"); final ClassLoader classLoader = clazzP1.getClassLoader(); checkGlobalClassLoader(classLoader); assertFalse(localClassLoaderP1 == classLoader); final ClassLoader localClassLoaderP2 = classLoaderService .getClassLoader(identifier(PROCESS, ID2)); final Class clazzP2 = localClassLoaderP2.loadClass("org.bonitasoft.engine.classloader.GlobalClass1"); final ClassLoader classLoader2 = clazzP2.getClassLoader(); checkGlobalClassLoader(classLoader2); assertFalse(localClassLoaderP2 == classLoader2); // verify if they are the same object (same reference) assertSame(classLoader, classLoader2); // getTransactionService().complete(); } @Test public void testLoadLocalClassUsingUsingBadLocalClassLoader() throws Exception { initializeClassLoaderService(); // getTransactionService().begin(); final ClassLoader classLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID2)); final Class clazz = classLoader.loadClass("org.bonitasoft.engine.classloader.LocalClass3"); assertFalse(isBonitaClassLoader(clazz.getClassLoader())); // getTransactionService().complete(); } @Test public void testLoadSharedClassUsingBadLocalClassLoader() throws Exception { initializeClassLoaderService(); // Should not be found with ID2 (bad scope): final ClassLoader classLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID2)); final Class clazz = classLoader.loadClass("org.bonitasoft.engine.classloader.SharedClass1"); final ClassLoader classLoader2 = clazz.getClassLoader(); checkGlobalClassLoader(classLoader2); assertNotSame(classLoader, classLoader2); } @Test public void testRemoveLocalClassLoader() throws Exception { initializeClassLoaderService(); final ClassLoader localClassLoader1 = classLoaderService.getClassLoader(identifier(PROCESS, ID1)); final ClassLoader localClassLoader2 = classLoaderService.getClassLoader(identifier(PROCESS, ID2)); classLoaderService.removeLocalClassloader(identifier(PROCESS, ID1)); assertNotSameClassloader(localClassLoader1, classLoaderService.getClassLoader(identifier(PROCESS, ID1))); classLoaderService.removeLocalClassloader(identifier(PROCESS, ID2)); assertNotSameClassloader(localClassLoader2, classLoaderService.getClassLoader(identifier(PROCESS, ID2))); } @Test public void testAddResourcesToGlobalClassLoader() throws Exception { initializeClassLoaderService(); final ClassLoader globalClassLoaderBefore = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL); //GlobalClass 3 is in the app classloader assertThat(globalClassLoaderBefore.loadClass("org.bonitasoft.engine.classloader.GlobalClass3").getClassLoader()) .isNotInstanceOf(BonitaClassLoader.class); inTx(() -> { createPlatformDependency("newlib", "newlib.jar", generateJar(GlobalClass3.class)); }); ClassLoader globalClassLoaderAfter = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL); final ClassLoader classLoader2 = globalClassLoaderAfter .loadClass("org.bonitasoft.engine.classloader.GlobalClass3").getClassLoader(); checkGlobalClassLoader(classLoader2); assertThat(((BonitaClassLoader) globalClassLoaderBefore).isDestroyed()).isTrue(); assertThat(globalClassLoaderBefore).isNotEqualTo(globalClassLoaderAfter); assertSameClassloader(globalClassLoaderAfter, classLoader2); } @Test public void testAddResourcesToLocalClassLoader() throws Exception { initializeClassLoaderService(); //at first, the local classloader does contains GlobalClass2, only the root classloader contains the GlobalClass2 ClassLoader classLoaderBefore = inTx( () -> classLoaderService.getClassLoader(identifier(PROCESS, ID1))); checkGlobalClassLoader( classLoaderBefore.loadClass("org.bonitasoft.engine.classloader.GlobalClass2").getClassLoader()); //add a dependency with GlobalClass2 and re-ask for the classloader ClassLoader classLoaderAfter = inTx(() -> { createDependency(ID1, PROCESS, "newlib", "newlib.jar", generateJar(GlobalClass2.class)); return classLoaderService.getClassLoader(identifier(PROCESS, ID1)); }); checkGlobalClassLoader( classLoaderBefore.loadClass("org.bonitasoft.engine.classloader.GlobalClass2").getClassLoader()); checkLocalClassLoader( classLoaderAfter.loadClass("org.bonitasoft.engine.classloader.GlobalClass2").getClassLoader()); assertThat(classLoaderBefore) .isNotEqualTo(classLoaderService.getClassLoader(identifier(PROCESS, ID1))); assertThat(((BonitaClassLoader) classLoaderBefore).isDestroyed()).isTrue(); assertThat(classLoaderAfter) .isEqualTo(classLoaderService.getClassLoader(identifier(PROCESS, ID1))); } @Test public void testResetGlobalClassLoader() throws Exception { initializeClassLoaderService(); inTx(() -> { createPlatformDependency("newlib", "newlib.jar", generateJar(GlobalClass3.class)); }); inTx(() -> { ClassLoader globalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL); Class clazz = globalClassLoader.loadClass("org.bonitasoft.engine.classloader.GlobalClass3"); final ClassLoader classLoader = clazz.getClassLoader(); checkGlobalClassLoader(classLoader); assertSameClassloader(globalClassLoader, classLoader); platformDependencyService.deleteDependencies(ClassLoaderIdentifier.GLOBAL_ID, ClassLoaderIdentifier.GLOBAL_TYPE); classLoaderService.refreshClassLoaderImmediately(ClassLoaderIdentifier.GLOBAL); }); inTx(() -> { ClassLoader globalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL); Class clazz = globalClassLoader.loadClass("org.bonitasoft.engine.classloader.GlobalClass3"); // dependency is not in the bonita classloader anymore but only in the Test classloader assertFalse(isBonitaClassLoader(clazz.getClassLoader())); }); } @Test public void testLoadNotInPathGlobalClassUsingGlobalClassLoader() throws Exception { initializeClassLoaderService(); addNotInPathDependencies(); // getTransactionService().begin(); final ClassLoader globalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL); final Class clazz = globalClassLoader.loadClass("org.bonitasoft.classloader.test.NotInPathGlobalClass1"); final ClassLoader classLoader = clazz.getClassLoader(); checkGlobalClassLoader(classLoader); assertSameClassloader(globalClassLoader, classLoader); // getTransactionService().complete(); } @Test public void testLoadNotInPathGlobalClassUsingLocalClassLoader() throws Exception { initializeClassLoaderService(); addNotInPathDependencies(); // getTransactionService().begin(); final ClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1)); final Class clazz = localClassLoader.loadClass("org.bonitasoft.classloader.test.NotInPathGlobalClass1"); checkGlobalClassLoader(clazz.getClassLoader()); assertNotSameClassloader(localClassLoader, clazz.getClassLoader()); assertSameClassloader(classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL), clazz.getClassLoader()); // getTransactionService().complete(); } @Test public void testLoadNotInPathLocalClassUsingLocalClassLoader() throws Exception { initializeClassLoaderService(); addNotInPathDependencies(); // getTransactionService().begin(); final ClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1)); final Class clazz = localClassLoader.loadClass("org.bonitasoft.classloader.test.NotInPathLocalClass1"); final ClassLoader classLoader = clazz.getClassLoader(); checkLocalClassLoader(classLoader); assertSameClassloader(localClassLoader, classLoader); // getTransactionService().complete(); } @Test(expected = ClassNotFoundException.class) public void testLoadNotInPathLocalClassUsingWrongLocalClassLoader() throws Exception { initializeClassLoaderService(); addNotInPathDependencies(); // getTransactionService().begin(); final ClassLoader classLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID2)); // getTransactionService().complete(); classLoader.loadClass("org.bonitasoft.classloader.test.NotInPathLocalClass1"); fail("load class with wrong classloader"); } @Test public void testLoadNotInPathSharedClassUsingGlobalClassLoader() throws Exception { initializeClassLoaderService(); addNotInPathDependencies(); // getTransactionService().begin(); final ClassLoader globalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL); final Class clazz = globalClassLoader.loadClass("org.bonitasoft.classloader.test.NotInPathSharedClass1"); final ClassLoader classLoader = clazz.getClassLoader(); checkGlobalClassLoader(classLoader); assertSameClassloader(globalClassLoader, classLoader); // getTransactionService().complete(); } @Test public void testLoadNotInPathSharedClassUsingLocalClassLoader() throws Exception { initializeClassLoaderService(); addNotInPathDependencies(); // getTransactionService().begin(); final ClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1)); final Class clazz = localClassLoader.loadClass("org.bonitasoft.classloader.test.NotInPathSharedClass1"); final ClassLoader classLoader = clazz.getClassLoader(); checkGlobalClassLoader(classLoader); // getTransactionService().complete(); } @Test public void loadResource() throws Exception { initializeClassLoaderService(); getTransactionService().begin(); final URL resourceFile = ClassLoaderServiceIT.class.getResource("resource.txt"); final byte[] resourceFileContent = IOUtil.getAllContentFrom(resourceFile); createPlatformDependency("resource", "resource.txt", resourceFileContent); getTransactionService().complete(); final ClassLoader virtualGlobalClassLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL); final InputStream resourceStream = virtualGlobalClassLoader.getResourceAsStream("resource.txt"); assertEquals(resourceFileContent.length, IOUtil.getAllContentFrom(resourceStream).length); resourceStream.close(); } @Test public void different_applications_should_have_same_global_classLoader() throws Exception { initializeClassLoaderServiceWithTwoApplications(); final ClassLoader process1Classloader = classLoaderService.getClassLoader(identifier(PROCESS, ID1)); final ClassLoader tenant1Classloader = classLoaderService.getClassLoader(ClassLoaderIdentifier.TENANT); final Class sharedClassLoadedFromProcess1 = process1Classloader .loadClass("org.bonitasoft.engine.classloader.SharedClass1"); final Class sharedClassLoadedFromTenant1 = tenant1Classloader .loadClass("org.bonitasoft.engine.classloader.SharedClass1"); checkGlobalClassLoader(sharedClassLoadedFromProcess1.getClassLoader()); assertSameClassloader(classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL), sharedClassLoadedFromProcess1.getClassLoader()); checkGlobalClassLoader(sharedClassLoadedFromTenant1.getClassLoader()); assertSameClassloader(classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL), sharedClassLoadedFromTenant1.getClassLoader()); } private void checkGlobalClassLoader(final ClassLoader classLoader) { assertThat(((BonitaClassLoader) classLoader).getIdentifier()).isEqualTo(ClassLoaderIdentifier.GLOBAL); } private void checkLocalClassLoader(final ClassLoader classLoader) { assertThat(((BonitaClassLoader) classLoader).getIdentifier()).isNotEqualTo(ClassLoaderIdentifier.GLOBAL); } private boolean isBonitaClassLoader(final ClassLoader classLoader) { return classLoader instanceof BonitaClassLoader; } private void assertSameClassloader(final ClassLoader classLoader1, final ClassLoader classLoader2) { assertThat(classLoader1).isNotNull().isEqualTo(classLoader2); } private void assertNotSameClassloader(final ClassLoader classLoader1, final ClassLoader classLoader2) { assertThat(classLoader1).isNotNull().isNotEqualTo(classLoader2); } @Test public void should_getResource_point_to_existing_file_after_classloader_refresh() throws Exception { //given a classloader that is refreshed getTransactionService().begin(); Map jarResources = Collections.singletonMap("test.xml", "content".getBytes()); byte[] jarContent = generateJar(jarResources); dependencyService.createMappedDependency("myResource.jar", jarContent, "myResource.jar", ID1, PROCESS); getTransactionService().complete(); getTransactionService().begin(); classLoaderService.refreshClassLoaderImmediately(identifier(PROCESS, ID1)); getTransactionService().complete(); getTransactionService().begin(); classLoaderService.refreshClassLoaderImmediately(identifier(PROCESS, ID1)); getTransactionService().complete(); //when ClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, ID1)); URL resource = localClassLoader.getResource("test.xml"); assertThat(resource).isNotNull(); String stringUrl = resource.toExternalForm(); URL url = new URL(stringUrl); //then assertThat(url).isNotNull(); String contentFromUrlOfTheClassLoader = readUrl(resource); String contentFromUrlRecreated = readUrl(url); assertThat(contentFromUrlOfTheClassLoader).isEqualTo("content"); assertThat(contentFromUrlRecreated).isEqualTo("content"); } @Test public void in_case_of_rollback_classloader_should_end_in_same_state_after_calling_refreshClassloaderImmediatelyWithRollback() throws Exception { getTransactionService().begin(); dependencyService.createMappedDependency("myResource.jar", IOUtil.generateJar(Collections.singletonMap("test.xml", "content".getBytes())), "myResource.jar", ID1, PROCESS); classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, ID1)); getTransactionService().complete(); assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource("test.xml")).isNotNull(); getTransactionService().begin(); dependencyService.createMappedDependency("myResource2.jar", IOUtil.generateJar(Collections.singletonMap("test2.xml", "content".getBytes())), "myResource2.jar", ID1, PROCESS); classLoaderService.refreshClassLoaderImmediatelyWithRollback(identifier(PROCESS, ID1)); assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource("test2.xml")).isNotNull(); getTransactionService().setRollbackOnly(); getTransactionService().complete(); assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource("test.xml")).isNotNull(); assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource("test2.xml")).isNull(); } @Test public void refreshClassloaderImmediatelyWithRollback_should_update_classloader_if_transaction_is_committed() throws Exception { getTransactionService().begin(); dependencyService.createMappedDependency("myResource.jar", IOUtil.generateJar(Collections.singletonMap("test.xml", "content".getBytes())), "myResource.jar", ID1, PROCESS); classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, ID1)); getTransactionService().complete(); assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource("test.xml")).isNotNull(); getTransactionService().begin(); dependencyService.createMappedDependency("myResource2.jar", IOUtil.generateJar(Collections.singletonMap("test2.xml", "content".getBytes())), "myResource2.jar", ID1, PROCESS); classLoaderService.refreshClassLoaderImmediatelyWithRollback(identifier(PROCESS, ID1)); assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource("test2.xml")).isNotNull(); getTransactionService().complete(); assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource("test.xml")).isNotNull(); assertThat(classLoaderService.getClassLoader(identifier(PROCESS, ID1)).getResource("test2.xml")).isNotNull(); } private String readUrl(URL resource) throws IOException { URLConnection urlConnection = resource.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); String inputLine; StringBuilder content = new StringBuilder(); while ((inputLine = in.readLine()) != null) content.append(inputLine); in.close(); return content.toString(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/GlobalClass1.java ================================================ /** * 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.classloader; /** * Only for test */ public class GlobalClass1 { } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/GlobalClass2.java ================================================ /** * 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.classloader; /** * @author Elias Ricken de Medeiros */ public class GlobalClass2 { } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/GlobalClass3.java ================================================ /** * 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.classloader; public class GlobalClass3 { } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/LocalClass1.java ================================================ /** * 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.classloader; /** * @author Elias Ricken de Medeiros */ public class LocalClass1 { } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/LocalClass2.java ================================================ /** * 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.classloader; /** * @author Elias Ricken de Medeiros */ public class LocalClass2 { } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/LocalClass3.java ================================================ /** * 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.classloader; /** * @author Elias Ricken de Medeiros */ public class LocalClass3 { } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/LocalClass4.java ================================================ /** * 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.classloader; /** * @author Elias Ricken de Medeiros */ public class LocalClass4 { } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/OnlyInPathClass1.java ================================================ /** * 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.classloader; /** * @author Elias Ricken de Medeiros */ public class OnlyInPathClass1 { } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/classloader/SharedClass1.java ================================================ /** * 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.classloader; /** * @author Elias Ricken de Medeiros */ public class SharedClass1 { } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/command/CommandServiceIT.java ================================================ /** * 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.command; import static org.junit.Assert.*; import java.util.List; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.command.model.SCommand; import org.bonitasoft.engine.command.model.SCommandCriterion; import org.bonitasoft.engine.command.model.SCommandUpdateBuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Zhang Bole */ public class CommandServiceIT extends CommonBPMServicesTest { private CommandService commandService; @Before public void setup() { commandService = getServiceAccessor().getCommandService(); } @After public void restoreDefaultCommands() throws SBonitaException { getTransactionService().begin(); getServiceAccessor().getCommandService().start(); getTransactionService().complete(); } @Test(expected = SCommandAlreadyExistsException.class) public void testSCommandAlreadyExistsException() throws Exception { getTransactionService().begin(); final SCommand command = SCommand.builder() .name("createCommand") .description("this is a command") .implementation("command implementation") .build(); commandService.create(command); try { commandService.create(command); } finally { commandService.delete("createCommand"); getTransactionService().complete(); } } @Test(expected = SCommandNotFoundException.class) public void testSCommandNotFoundException() throws Exception { getTransactionService().begin(); try { commandService.get("a"); } finally { getTransactionService().complete(); } } @Test public void testCreateCommand() throws Exception { getTransactionService().begin(); final SCommand command1 = SCommand.builder() .name("createCommand") .description("this is a command") .implementation("command implementation") .build(); commandService.create(command1); final SCommand command2 = commandService.get("createCommand"); assertNotNull("can't find the category after adding it", command2); assertEquals("can't retrieve the same category", command1.getName(), command2.getName()); assertEquals("can't retrieve the same category", command1.getId(), command2.getId()); commandService.delete("createCommand"); getTransactionService().complete(); } @Test(expected = SCommandNotFoundException.class) public void testDeleteCommand() throws Exception { getTransactionService().begin(); final SCommand command = SCommand.builder() .name("testCommandDelete") .description("this is a command") .implementation("command implementation").build(); commandService.create(command); commandService.delete("testCommandDelete"); try { commandService.get("testCommandDelete"); } finally { getTransactionService().complete(); } } @Test public void testDeleteAll() throws Exception { getTransactionService().begin(); final int initialNbOfCommands = commandService.getAllCommands(0, 500, SCommandCriterion.NAME_ASC).size(); final SCommand command1 = SCommand.builder() .name("createCommand1") .description("this is a command") .implementation("command implementation").build(); final SCommand command2 = SCommand.builder() .name("createCommand2") .description("this is a command") .implementation("command implementation").build(); final SCommand command3 = SCommand.builder() .name("createCommand3") .description("this is a command") .implementation("command implementation").build(); commandService.create(command1); commandService.create(command2); commandService.create(command3); final List commands1 = commandService.getAllCommands(0, 500, SCommandCriterion.NAME_ASC); assertEquals(3 + initialNbOfCommands, commands1.size()); commandService.deleteAll(); final List commands2 = commandService.getAllCommands(0, 500, SCommandCriterion.NAME_ASC); assertTrue(commands2.isEmpty()); getTransactionService().setRollbackOnly(); // so that existing commands are restored getTransactionService().complete(); } @Test public void testUpdateCommand() throws Exception { getTransactionService().begin(); final SCommand oldCommand = SCommand.builder() .name("old") .description("this is an old command") .implementation("command implementation") .build(); commandService.create(oldCommand); assertEquals("old", oldCommand.getName()); assertEquals("this is an old command", oldCommand.getDescription()); final String commandName = "new"; final EntityUpdateDescriptor updateDescriptor = BuilderFactory.get(SCommandUpdateBuilderFactory.class) .createNewInstance().updateName(commandName) .updateDescription("this is a new command").done(); commandService.update(oldCommand, updateDescriptor); final SCommand newCommand = commandService.get(commandName); assertEquals("new", newCommand.getName()); assertEquals("this is a new command", newCommand.getDescription()); commandService.delete(commandName); getTransactionService().complete(); } @Test public void testget() throws Exception { getTransactionService().begin(); final SCommand sCommand = SCommand.builder() .name("commandOne") .description("this is a command") .implementation("command implementation") .build(); commandService.create(sCommand); final SCommand command = commandService.get("commandOne"); assertEquals("commandOne", command.getName()); assertEquals("this is a command", command.getDescription()); commandService.delete("commandOne"); getTransactionService().complete(); } @Test public void testGetCommandsWithCriterion() throws Exception { getTransactionService().begin(); final int initialNbOfCommands = commandService.getAllCommands(0, 500, SCommandCriterion.NAME_ASC).size(); final SCommand sCommand1 = SCommand.builder() .name("aaaaa") .description("this is command1") .implementation("command implementation") .build(); final SCommand sCommand2 = SCommand.builder() .name("aaaab") .description("this is command2") .implementation("command implementation") .build(); final SCommand sCommand3 = SCommand.builder() .name("aaaac") .description("this is command3") .implementation("command implementation") .build(); commandService.create(sCommand1); commandService.create(sCommand2); commandService.create(sCommand3); final List commands = commandService.getAllCommands(0, 500, SCommandCriterion.NAME_ASC); assertEquals(3 + initialNbOfCommands, commands.size()); assertEquals("aaaaa", commands.get(0).getName()); assertEquals("aaaab", commands.get(1).getName()); assertEquals("aaaac", commands.get(2).getName()); commandService.delete(sCommand1.getId()); commandService.delete(sCommand2.getId()); commandService.delete(sCommand3.getId()); getTransactionService().complete(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/core/form/impl/FormMappingServiceIT.java ================================================ /** * 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.core.form.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; import java.util.List; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.core.form.FormMappingService; import org.bonitasoft.engine.core.form.SFormMapping; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.form.FormMappingTarget; import org.bonitasoft.engine.form.FormMappingType; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.page.URLAdapterConstants; import org.bonitasoft.engine.test.CommonTestUtil; import org.bonitasoft.engine.transaction.TransactionService; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Baptiste Mesta */ public class FormMappingServiceIT extends CommonBPMServicesTest { public static final String PAGE_NAME = "custompage_coucou"; public FormMappingService formMappingService; private PageService pageService; private TransactionService transactionService; private SPage page; private ProcessDefinitionService processDefinitionService; private SProcessDefinition p1; private SProcessDefinition p2; @Before public void setup() throws Exception { processDefinitionService = getServiceAccessor().getProcessDefinitionService(); transactionService = getTransactionService(); formMappingService = getServiceAccessor().getFormMappingService(); pageService = getServiceAccessor().getPageService(); transactionService.begin(); p1 = buildSProcessDefinition("P1", "1.0"); p2 = buildSProcessDefinition("P2", "1.0"); page = pageService.addPage( CommonTestUtil.createTestPageContent(PAGE_NAME, "coucou depuis la page", "C'était juste pour dire coucou"), "mySuperPage.zip", 54L); transactionService.complete(); } @After public void tearDown() throws Exception { clearFormMapping(); } protected void clearFormMapping() throws Exception { transactionService.begin(); for (SFormMapping sFormMapping : formMappingService.list(0, 1000)) { formMappingService.delete(sFormMapping); } pageService.deletePage(page.getId()); processDefinitionService.delete(p1.getId()); processDefinitionService.delete(p2.getId()); transactionService.complete(); } @Test public void createAndListFormMapping() throws Exception { //given transactionService.begin(); formMappingService.create(p1.getId(), "step1", FormMappingType.TASK.getId(), "INTERNAL", PAGE_NAME); formMappingService.create(p1.getId(), null, FormMappingType.PROCESS_START.getId(), "URL", "http://bit.coin"); formMappingService.create(p1.getId(), null, FormMappingType.PROCESS_OVERVIEW.getId(), "LEGACY", null); formMappingService.create(p2.getId(), null, FormMappingType.PROCESS_OVERVIEW.getId(), "UNDEFINED", null); transactionService.complete(); transactionService.begin(); List list = formMappingService.list(p1.getId(), 0, 10); List listAll = formMappingService.list(0, 10); transactionService.complete(); assertThat(list).extracting("type").containsExactlyInAnyOrder(FormMappingType.TASK.getId(), FormMappingType.PROCESS_START.getId(), FormMappingType.PROCESS_OVERVIEW.getId()); assertThat(list).extracting("task").containsExactlyInAnyOrder("step1", null, null); assertThat(list).extracting("pageMapping.url").containsExactlyInAnyOrder(null, "http://bit.coin", null); assertThat(list).extracting("pageMapping.urlAdapter").containsExactlyInAnyOrder(null, URLAdapterConstants.EXTERNAL_URL_ADAPTER, URLAdapterConstants.LEGACY_URL_ADAPTER); assertThat(list).extracting("pageMapping.pageId").containsExactlyInAnyOrder(page.getId(), null, null); // assertThat(list).extracting("pageMapping.key").containsExactlyInAnyOrder(); assertThat(listAll).extracting("type").containsExactlyInAnyOrder(FormMappingType.TASK.getId(), FormMappingType.PROCESS_START.getId(), FormMappingType.PROCESS_OVERVIEW.getId(), FormMappingType.PROCESS_OVERVIEW.getId()); assertThat(listAll).extracting("processDefinitionId").containsExactlyInAnyOrder(p1.getId(), p1.getId(), p1.getId(), p2.getId()); } @Test public void create_and_get_FormMapping() throws Exception { transactionService.begin(); SFormMapping taskForm = formMappingService.create(p1.getId(), "step1", FormMappingType.TASK.getId(), "URL", "http://bit.coin"); transactionService.complete(); transactionService.begin(); SFormMapping sFormMapping = formMappingService.get(taskForm.getId()); SFormMapping sFormMappingByProperties = formMappingService.get(p1.getId(), FormMappingType.TASK.getId(), "step1"); transactionService.complete(); assertThat(sFormMapping).isEqualTo(taskForm).isEqualTo(sFormMappingByProperties); } @Test public void create_and_get_by_key_FormMapping() throws Exception { transactionService.begin(); SFormMapping taskForm = formMappingService.create(p1.getId(), "step1", FormMappingType.TASK.getId(), "URL", "http://bit.coin"); transactionService.complete(); transactionService.begin(); SFormMapping sFormMapping = formMappingService.get(taskForm.getPageMapping().getKey()); SFormMapping sFormMappingByProperties = formMappingService.get(p1.getId(), FormMappingType.TASK.getId(), "step1"); transactionService.complete(); assertThat(sFormMapping).isEqualTo(taskForm).isEqualTo(sFormMappingByProperties); } @Test public void create_and_get_FormMapping_with_no_task() throws Exception { transactionService.begin(); SFormMapping taskForm = formMappingService.create(p1.getId(), "task", FormMappingType.TASK.getId(), "URL", "http://bit.coin"); transactionService.complete(); transactionService.begin(); SFormMapping sFormMapping = formMappingService.get(taskForm.getId()); SFormMapping sFormMappingByProperties = formMappingService.get(p1.getId(), FormMappingType.TASK.getId()); transactionService.complete(); assertThat(sFormMapping).isEqualTo(taskForm); assertThat(sFormMappingByProperties).isEqualTo(sFormMapping); } @Test public void delete_FormMapping() throws Exception { transactionService.begin(); SFormMapping taskForm = formMappingService.create(p1.getId(), "step1", FormMappingType.TASK.getId(), "URL", "http://bit.coin"); transactionService.complete(); transactionService.begin(); formMappingService.delete(formMappingService.get(taskForm.getId())); transactionService.complete(); transactionService.begin(); try { formMappingService.get(taskForm.getId()); fail("should have thrown a not found Exception"); } catch (SObjectNotFoundException e) { //ok } transactionService.complete(); } @Test public void update_FormMapping() throws Exception { transactionService.begin(); SFormMapping taskForm = formMappingService.create(p1.getId(), "step1", FormMappingType.TASK.getId(), "URL", "http://bit.coin"); transactionService.complete(); transactionService.begin(); SFormMapping sFormMapping = formMappingService.get(taskForm.getId()); formMappingService.update(sFormMapping, "newFormName", null); transactionService.complete(); transactionService.begin(); SFormMapping updatedInDatabase = formMappingService.get(taskForm.getId()); transactionService.complete(); assertThat(sFormMapping).isEqualTo(updatedInDatabase); assertThat(updatedInDatabase.getPageMapping().getUrl()).isEqualTo("newFormName"); assertThat(updatedInDatabase.getTarget()).isEqualTo(FormMappingTarget.URL.name()); assertThat(updatedInDatabase.getLastUpdateDate()).isGreaterThan(taskForm.getLastUpdateDate()); Thread.sleep(10); transactionService.begin(); SFormMapping reupdated = formMappingService.get(taskForm.getId()); formMappingService.update(reupdated, null, page.getId()); transactionService.complete(); assertThat(reupdated.getPageMapping().getUrl()).isNull(); assertThat(reupdated.getPageMapping().getPageId()).isEqualTo(page.getId()); assertThat(reupdated.getTarget()).isEqualTo(FormMappingTarget.INTERNAL.name()); assertThat(reupdated.getLastUpdateDate()).isGreaterThan(updatedInDatabase.getLastUpdateDate()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/core/form/impl/custom/CustomAuthorizationRuleMappingImpl.java ================================================ /** * 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.core.form.impl.custom; import static org.bonitasoft.engine.page.AuthorizationRuleConstants.*; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.core.form.AuthorizationRuleMapping; /** * @author Laurent Leseigneur */ public class CustomAuthorizationRuleMappingImpl implements AuthorizationRuleMapping { @Override public List getProcessStartRuleKeys() { return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_ACTOR_INITIATOR); } @Override public List getProcessOverviewRuleKeys() { return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_PROCESS_INITIATOR, IS_TASK_PERFORMER, IS_INVOLVED_IN_PROCESS_INSTANCE, IS_PROCESS_INITIATOR + "_CUSTOM"); } @Override public List getTaskRuleKeys() { return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_TASK_AVAILABLE_FOR_USER); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/core/form/impl/custom/CustomIsProcessInitiatorRule.java ================================================ /** * 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.core.form.impl.custom; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.page.AuthorizationRule; import org.bonitasoft.engine.page.AuthorizationRuleConstants; import org.bonitasoft.engine.page.AuthorizationRuleWithParameters; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; /** * @author Laurent Leseigneur */ public class CustomIsProcessInitiatorRule extends AuthorizationRuleWithParameters implements AuthorizationRule { private final ProcessInstanceService processInstanceService; private final SessionService sessionService; private final SessionAccessor sessionAccessor; // autowired by spring public CustomIsProcessInitiatorRule(ProcessInstanceService processInstanceService, SessionService sessionService, SessionAccessor sessionAccessor) { this.processInstanceService = processInstanceService; this.sessionAccessor = sessionAccessor; this.sessionService = sessionService; } @Override public boolean isAllowed(String key, Map context) throws SExecutionException { //add business logic here return true; } @Override public String getId() { return AuthorizationRuleConstants.IS_PROCESS_INITIATOR + "_CUSTOM"; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/data/instance/DataInstanceIntegrationLocalIT.java ================================================ /** * 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.data.instance; import static org.junit.Assert.assertNotNull; import java.io.IOException; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.process.Employee; import org.bonitasoft.engine.test.BuildTestUtil; import org.bonitasoft.engine.test.CommonAPILocalIT; import org.junit.After; import org.junit.Before; import org.junit.Test; public class DataInstanceIntegrationLocalIT extends CommonAPILocalIT { private User cebolinha; private User cascao; @Before public void before() throws Exception { loginWithTechnicalUser(); cebolinha = createUser("cebolinha", "bpm"); cascao = createUser("cascao", "bpm"); } @After public void after() throws Exception { deleteUsers(cebolinha, cascao); logout(); } @Test public void processWithCustomData() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("processWithCustomData", "1.0"); builder.addUserTask("step1", "actor"); builder.addActor("actor"); final Expression expression = new ExpressionBuilder().createGroovyScriptExpression("initEmployee", "import org.bonitasoft.engine.process.Employee; \n" + "return new Employee(\"Tähtikuja\", \"firstName48\")", "org.bonitasoft.engine.process.Employee"); builder.addData("address", "org.bonitasoft.engine.process.Employee", expression); final BusinessArchive businessArchive = addClasspathRessource(builder).done(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, "actor", cascao); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); assertNotNull(getProcessAPI().getProcessDataInstance("address", processInstance.getId()).getValue()); disableAndDeleteProcess(processDefinition); } @Test public void callActivityWith2CustomDatasOnActivityUsingSameExternalLibrairy() throws Exception { final String targetProcessName = "targetProcess"; final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression(targetProcessName); final Expression targetProcessVersionExpr = new ExpressionBuilder() .createConstantStringExpression(PROCESS_VERSION); // Build target process final Expression defaultValueForEmployeeOnTStep1 = new ExpressionBuilder().createGroovyScriptExpression( "initEmployee", "import org.bonitasoft.engine.process.Employee; \n" + "return new Employee(\"Tähtikuja\", \"firstName48\")", "org.bonitasoft.engine.process.Employee"); final ProcessDefinitionBuilder targetProcessDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(targetProcessName, PROCESS_VERSION); targetProcessDefinitionBuilder.addActor(ACTOR_NAME).addStartEvent("tStart"); targetProcessDefinitionBuilder.addUserTask("tStep1", ACTOR_NAME).addData("data", Employee.class.getName(), defaultValueForEmployeeOnTStep1); targetProcessDefinitionBuilder.addEndEvent("tEnd"); targetProcessDefinitionBuilder.addTransition("tStart", "tStep1").addTransition("tStep1", "tEnd"); final ProcessDefinition targetProcessDefinition = deployAndEnableProcessWithActor( addClasspathRessource(targetProcessDefinitionBuilder).done(), ACTOR_NAME, cebolinha); // Build main process final Expression defaultValueForEmployeeOnStep1 = new ExpressionBuilder().createGroovyScriptExpression( "initEmployee", "import org.bonitasoft.engine.process.Employee; \n" + "return new Employee(\"name\", \"firstName\")", "org.bonitasoft.engine.process.Employee"); final ProcessDefinitionBuilder callingProcessDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("callingProcess", PROCESS_VERSION); callingProcessDefinitionBuilder.addActor(ACTOR_NAME).addStartEvent("start"); callingProcessDefinitionBuilder.addUserTask("step1", ACTOR_NAME).addData("data", Employee.class.getName(), defaultValueForEmployeeOnStep1); callingProcessDefinitionBuilder.addCallActivity("callActivity", targetProcessNameExpr, targetProcessVersionExpr) .addDisplayName(new ExpressionBuilder().createConstantStringExpression("callActivityDisplayName")) .addDescription("callActivityDescription") .addDisplayDescription( new ExpressionBuilder().createConstantStringExpression("callActivityDisplayDescription")); callingProcessDefinitionBuilder.addEndEvent("end"); callingProcessDefinitionBuilder.addTransition("start", "step1").addTransition("step1", "callActivity") .addTransition("callActivity", "end"); final ProcessDefinition callingProcessDefinition = deployAndEnableProcessWithActor( addClasspathRessource(callingProcessDefinitionBuilder).done(), ACTOR_NAME, cascao); final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDefinition.getId()); final long step1Id = waitForUserTask(callingProcessInstance, "step1"); assertNotNull(getProcessAPI().getActivityDataInstance("data", step1Id).getValue()); assignAndExecuteStep(step1Id, cascao.getId()); final long tStep1Id = waitForUserTask(callingProcessInstance, "tStep1"); assertNotNull(getProcessAPI().getActivityDataInstance("data", tStep1Id).getValue()); assignAndExecuteStep(tStep1Id, cebolinha.getId()); waitForProcessToFinish(callingProcessInstance); // Clean up disableAndDeleteProcess(callingProcessDefinition, targetProcessDefinition); } @Test public void callActivityWithMappingAnd2CustomDatasOnProcessUsingSameExternalLibrairy() throws Exception { // getCommandAPI().addDependency("Employee.jar", IOUtil.generateJar(Employee.class)); final String dataName = "data"; final String dataType = Employee.class.getName(); final String targetProcessName = "targetProcess"; final Expression targetProcessNameExpr = new ExpressionBuilder() .createConstantStringExpression(targetProcessName); final Expression targetProcessVersionExpr = new ExpressionBuilder() .createConstantStringExpression(PROCESS_VERSION); // Build target process final ProcessDefinitionBuilder targetProcessDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance(targetProcessName, PROCESS_VERSION); targetProcessDefinitionBuilder.addActor(ACTOR_NAME + 2).addStartEvent("tStart"); targetProcessDefinitionBuilder.addUserTask("tStep1", ACTOR_NAME + 2); targetProcessDefinitionBuilder.addData(dataName, dataType, null); targetProcessDefinitionBuilder.addEndEvent("tEnd").addTerminateEventTrigger(); targetProcessDefinitionBuilder.addTransition("tStart", "tStep1").addTransition("tStep1", "tEnd"); final ProcessDefinition targetProcessDefinition = deployAndEnableProcessWithActor( addClasspathRessource(targetProcessDefinitionBuilder).done(), ACTOR_NAME + 2, cebolinha); // Build main process final Expression defaultValueForEmployeeOnStep1 = new ExpressionBuilder().createGroovyScriptExpression( "initEmployee", "import org.bonitasoft.engine.process.Employee; \n" + "return new Employee(\"name\", \"firstName\")", "org.bonitasoft.engine.process.Employee"); final ProcessDefinitionBuilder callingProcessDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("callingProcess", PROCESS_VERSION); callingProcessDefinitionBuilder.addActor(ACTOR_NAME).addStartEvent("start"); callingProcessDefinitionBuilder.addData(dataName, dataType, defaultValueForEmployeeOnStep1); callingProcessDefinitionBuilder.addUserTask("Step1", ACTOR_NAME); final CallActivityBuilder callActivity = callingProcessDefinitionBuilder.addCallActivity("callActivity", targetProcessNameExpr, targetProcessVersionExpr); final Expression rightOperand = new ExpressionBuilder().createDataExpression(dataName, dataType); callActivity.addDataInputOperation( BuildTestUtil.buildOperation(dataName, false, OperatorType.ASSIGNMENT, "=", rightOperand)); callActivity.addDataOutputOperation( BuildTestUtil.buildOperation(dataName, false, OperatorType.ASSIGNMENT, "=", rightOperand)); callingProcessDefinitionBuilder.addEndEvent("end"); callingProcessDefinitionBuilder.addTransition("start", "callActivity").addTransition("callActivity", "Step1") .addTransition("Step1", "end"); final ProcessDefinition callingProcessDefinition = deployAndEnableProcessWithActor( addClasspathRessource(callingProcessDefinitionBuilder).done(), ACTOR_NAME, cebolinha); final ProcessInstance callingProcessInstance = getProcessAPI().startProcess(callingProcessDefinition.getId()); final HumanTaskInstance tStep1 = waitForUserTaskAndGetIt(callingProcessInstance, "tStep1"); assertNotNull(getProcessAPI().getProcessDataInstance(dataName, tStep1.getParentProcessInstanceId()).getValue()); assignAndExecuteStep(tStep1, cebolinha.getId()); final HumanTaskInstance step1 = waitForUserTaskAndGetIt(callingProcessInstance, "Step1"); assertNotNull(getProcessAPI().getProcessDataInstance(dataName, step1.getParentProcessInstanceId()).getValue()); assignAndExecuteStep(step1, cebolinha.getId()); waitForProcessToFinish(callingProcessInstance); // Clean up disableAndDeleteProcess(callingProcessDefinition, targetProcessDefinition); } private BusinessArchiveBuilder addClasspathRessource(final ProcessDefinitionBuilder targetProcessDefinitionBuilder) throws InvalidProcessDefinitionException, IOException { final BusinessArchiveBuilder archiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition( targetProcessDefinitionBuilder.done()); archiveBuilder.addClasspathResource(new BarResource("Employee.jar", IOUtil.generateJar(Employee.class))); return archiveBuilder; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/data/instance/DataInstanceServiceIT.java ================================================ /** * 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.data.instance; import static org.junit.Assert.*; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.cache.ehcache.EhCacheCacheService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.data.ParentContainerResolverImpl; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory; import org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilderFactory; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.data.instance.api.impl.DataInstanceServiceImpl; import org.bonitasoft.engine.data.instance.exception.SDataInstanceNotFoundException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder; import org.bonitasoft.engine.data.instance.model.SXMLDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SADataInstance; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.ExpressionService; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilder; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Zhao Na * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet * @author Emmanuel Duchastenier */ @SuppressWarnings("javadoc") public class DataInstanceServiceIT extends CommonBPMServicesTest { private static final Map EMPTY_RESOLVED_EXPRESSIONS = Collections.emptyMap(); protected ExpressionService expressionService; protected DataInstanceService dataInstanceService; protected ParentContainerResolverImpl parentContainerResolver; @Before public void setupDataInstanceService() { final Recorder recorder = getServiceAccessor().getRecorder(); final ReadPersistenceService persistenceService = getServiceAccessor().getReadPersistenceService(); final ArchiveService archiveService = getServiceAccessor().getArchiveService(); expressionService = getServiceAccessor().getExpressionService(); parentContainerResolver = (ParentContainerResolverImpl) getServiceAccessor().getParentContainerResolver(); dataInstanceService = new DataInstanceServiceImpl(recorder, persistenceService, archiveService); parentContainerResolver.setAllowUnknownContainer(true); final EhCacheCacheService cacheService = (EhCacheCacheService) getServiceAccessor().getCacheService(); if (cacheService.isStopped()) { try { cacheService.start(); } catch (final SBonitaException e) { throw new RuntimeException(e); } } } @After public void tearDown() { final EhCacheCacheService cacheService = (EhCacheCacheService) getServiceAccessor().getCacheService(); try { cacheService.stop(); cacheService.start(); } catch (final SBonitaException e) { throw new RuntimeException(e); } parentContainerResolver.setAllowUnknownContainer(false); } public SDataInstance buildDataInstance(final String instanceName, final String className, final String description, final String content, final long containerId, final String containerType, final boolean isTransient) throws SBonitaException { // create definition final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class) .createNewInstance(instanceName, className); initializeBuilder(dataDefinitionBuilder, description, content, className, isTransient); final SDataDefinition dataDefinition = dataDefinitionBuilder.done(); final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition); evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder); return dataInstanceBuilder.setContainerId(containerId).setContainerType(containerType).done(); } private SDataInstance buildDataInstanceConstant(final String instanceName, final String className, final String description, final String content, final long containerId, final String containerType, final boolean isTransient) throws SBonitaException { // create definition final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class) .createNewInstance(instanceName, className); initializeBuilderConstant(dataDefinitionBuilder, description, content, className, isTransient); final SDataDefinition dataDefinition = dataDefinitionBuilder.done(); // create datainstance final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition); evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder); return dataInstanceBuilder.setContainerId(containerId).setContainerType(containerType).done(); } private void evaluateDefaultValueOf(final SDataDefinition dataDefinition, final SDataInstanceBuilder dataInstanceBuilder) throws SBonitaException { final SExpression expression = dataDefinition.getDefaultValueExpression(); if (expression != null) { dataInstanceBuilder.setValue((Serializable) expressionService.evaluate(expression, Collections.singletonMap("processDefinitionId", 546L), EMPTY_RESOLVED_EXPRESSIONS, ContainerState.ACTIVE)); } } private SDataInstance buildLongTextDataInstance(final String instanceName, final String description, final String content, final long containerId, final String containerType, final Boolean isTransient) throws SBonitaException { // create definition final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class) .createNewTextData(instanceName) .setAsLongText(true); initializeBuilder(dataDefinitionBuilder, description, content, String.class.getName(), isTransient); final SDataDefinition dataDefinition = dataDefinitionBuilder.done(); final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition) .setContainerId(containerId).setContainerType(containerType); // create data instance evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder); return dataInstanceBuilder.done(); } @Test public void testCreateAndRetrieveDateDataInstance() throws Exception { final long time = System.currentTimeMillis(); verifyCreateAndRetrieveDataInstance("createDate", Date.class.getName(), "creates new Date", "new java.util.Date(" + time + ")", 9L, "process", false, new Date(time)); } @Test public void createAndRetrieveNullDateDataInstanceShouldBeSupported() throws Exception { verifyCreateAndRetrieveDataInstance("createNullDate", Date.class.getName(), "creates a null Date", "null", 9L, "process", false, null); } // private SDataInstance buildDateDataInstance(final String instanceName, final String description, final String content, final long containerId, // final String containerType, final Boolean isTransient) throws SBonitaException { // // create definition // final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class).createNewInstance(instanceName, Date.class.getName()); // // initializeBuilder(dataDefinitionBuilder, description, content, Date.class.getName(), isTransient); // final SDataDefinition dataDefinition = dataDefinitionBuilder.done(); // final SDataInstanceBuilder dataInstanceBuilder = BuilderFactory.get(SDataInstanceBuilderFactory.class).createNewInstance(dataDefinition) // .setContainerId(containerId).setContainerType(containerType); // // create data instance // evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder); // return dataInstanceBuilder.done(); // } private SDataInstance buildXMLDataInstance(final String instanceName, final String description, final String namespace, final String xmlElement, final String content, final long containerId, final String containerType) throws SBonitaException { // create definition final SDataDefinition dataDefinition = buildDataDefinition(instanceName, description, namespace, xmlElement, content, String.class.getName()); // create data instance final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition) .setContainerId(containerId).setContainerType(containerType); evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder); return dataInstanceBuilder.done(); } private SDataDefinition buildDataDefinition(final String instanceName, final String description, final String namespace, final String xmlElement, final String content, final String defaultValueReturnType) throws SInvalidExpressionException { final SXMLDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory .get(SXMLDataDefinitionBuilderFactory.class).createNewXMLData(instanceName) .setNamespace(namespace).setElement(xmlElement); dataDefinitionBuilder.setDescription(description); dataDefinitionBuilder.setTransient(false); SExpression expression = null; if (content != null) { // create expression final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class) .createNewInstance(); // this discrimination'll be changed. expreBuilder.setContent(content).setReturnType(defaultValueReturnType) .setExpressionType(SExpression.TYPE_READ_ONLY_SCRIPT) .setInterpreter(SExpression.GROOVY); expression = expreBuilder.done(); } dataDefinitionBuilder.setDefaultValue(expression); return dataDefinitionBuilder.done(); } private void initializeBuilder(final SDataDefinitionBuilder dataDefinitionBuilder, final String description, final String content, final String defaultValueReturnType, final boolean isTransient) throws SInvalidExpressionException { SExpression expression = null; if (content != null) { // create expression final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class) .createNewInstance(); // this discrimination'll be changed. expreBuilder.setContent(content).setReturnType(defaultValueReturnType) .setExpressionType(SExpression.TYPE_READ_ONLY_SCRIPT) .setInterpreter(SExpression.GROOVY); expression = expreBuilder.done(); } dataDefinitionBuilder.setDescription(description); dataDefinitionBuilder.setTransient(isTransient); dataDefinitionBuilder.setDefaultValue(expression); } private void initializeBuilderConstant(final SDataDefinitionBuilder dataDefinitionBuilder, final String description, final String content, final String defaultValueReturnType, final boolean isTransient) throws SInvalidExpressionException { SExpression expression = null; if (content != null) { // create expression final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class) .createNewInstance(); // this discrimination'll be changed. expreBuilder.setContent(content).setReturnType(defaultValueReturnType) .setExpressionType(SExpression.TYPE_CONSTANT); expression = expreBuilder.done(); } dataDefinitionBuilder.setDescription(description); dataDefinitionBuilder.setTransient(isTransient); dataDefinitionBuilder.setDefaultValue(expression); } private void checkDataInstance(final SDataInstance dataInstance, final String name, final String description, final boolean isTransient, final String className, final Serializable value, final long containerId, final String containerType) { assertEquals(name, dataInstance.getName()); assertEquals(description, dataInstance.getDescription()); assertEquals(isTransient, dataInstance.isTransientData()); assertEquals(className, dataInstance.getClassName()); assertEquals(value, dataInstance.getValue()); assertEquals(containerId, dataInstance.getContainerId()); assertEquals(containerType, dataInstance.getContainerType()); } private void checkXMLDataInstance(final SXMLDataInstance dataInstance, final String name, final String description, final boolean isTransient, final String className, final Serializable value, final long containerId, final String containerType, final String namespace, final String element) { assertEquals(namespace, dataInstance.getNamespace()); assertEquals(element, dataInstance.getElement()); checkDataInstance(dataInstance, name, description, isTransient, className, value, containerId, containerType); } private EntityUpdateDescriptor getUpdateDescriptor(final String description, final Serializable newValue) { // update data instance final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor(); updateDescriptor.addField(SDataInstance.VALUE, newValue); updateDescriptor.addField(SDataInstance.DESCRIPTION, description); return updateDescriptor; } private void insertDataInstance(final SDataInstance dataInstance) throws SBonitaException { getTransactionService().begin(); // create data instance dataInstanceService.createDataInstance(dataInstance); getTransactionService().complete(); } private SDataInstance getDataInstance(final long dataInstanceId) throws SBonitaException { getTransactionService().begin(); final SDataInstance dataInstanceRes = dataInstanceService.getDataInstance(dataInstanceId); getTransactionService().complete(); return dataInstanceRes; } private SDataInstance getDataInstanceByNameAndContainer(final String dataName, final long containerId, final String containerType) throws SBonitaException { getTransactionService().begin(); // get the data instance by several conditions final SDataInstance dataInstanceRes = dataInstanceService.getLocalDataInstance(dataName, containerId, containerType); getTransactionService().complete(); return dataInstanceRes; } private String getLongText() { final StringBuilder stb = new StringBuilder(); for (int i = 0; i < 255; i++) { stb.append(i); } return stb.toString(); } private void deleteDataInstance(final SDataInstance dataInstance) throws SBonitaException { getTransactionService().begin(); dataInstanceService.deleteDataInstance(dataInstance); getTransactionService().complete(); } private void updateDataInstance(final String dataName, final Long containerId, final String containerType, final String newDescription, final Serializable value) throws SBonitaException { getTransactionService().begin(); // retrieve the data instance final SDataInstance dataInstanceRes = dataInstanceService.getLocalDataInstance(dataName, containerId, containerType); // update the data instance and this step must be with an activity data Instance in same transaction. final EntityUpdateDescriptor updateDescriptor = getUpdateDescriptor(newDescription, value); dataInstanceService.updateDataInstance(dataInstanceRes, updateDescriptor); getTransactionService().complete(); } private void verifyCreateAndRetrieveDataInstance(final String name, final String classType, final String description, final String content, final Long containerId, final String containerType, final Boolean isTransient, final Serializable checkValue) throws Exception { final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId, containerType, isTransient); insertDataInstance(dataInstance); final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType()); checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId, containerType); deleteDataInstance(dataInstanceRes); } private void verifyCreateAndRetrieveDataInstanceConstant(final String name, final String classType, final String description, final String content, final Long containerId, final String containerType, final Boolean isTransient, final Serializable checkValue) throws Exception { final SDataInstance dataInstance = buildDataInstanceConstant(name, classType, description, content, containerId, containerType, isTransient); insertDataInstance(dataInstance); final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType()); checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId, containerType); deleteDataInstance(dataInstanceRes); } private void verifyCreateAndRetrieveDoubleDataInstance(final boolean isTransient) throws Exception { verifyCreateAndRetrieveDataInstance("createDouble1", Double.class.getName(), "testCreateDescriptionForDouble1", "new Double(5.0)", 6L, "process", isTransient, 5.0); } private void verifyCreateAndRetrieveFloatDataInstance(final boolean isTransient) throws Exception { verifyCreateAndRetrieveDataInstance("createFloat1", Float.class.getName(), "testCreateDescriptionForFloat1", "new Float(5.0)", 6L, "process", isTransient, 5.0F); } @Test public void getDataInstancesWithTransientAndNonTransientData() throws Exception { final long containerId = 4444L; final String containerType = "ActivityScope"; final String instance1Name = "non-transient"; final SDataInstance data1Instance = buildDataInstance(instance1Name, Integer.class.getName(), "some non transient data", null, containerId, containerType, false); insertDataInstance(data1Instance); final String instance2Name = "transient"; final SDataInstance data2Instance = buildDataInstance(instance2Name, Integer.class.getName(), "some transient data", null, containerId, containerType, true); insertDataInstance(data2Instance); getTransactionService().begin(); getTransactionService().complete(); final List dataNames = new ArrayList<>(2); dataNames.add(instance1Name); dataNames.add(instance2Name); getTransactionService().begin(); final List dataInstances = dataInstanceService.getDataInstances(dataNames, containerId, containerType, parentContainerResolver); getTransactionService().complete(); assertEquals(2, dataInstances.size()); Assertions.assertThat(dataInstances).extracting("name").containsOnly(instance1Name, instance2Name); } @Test public void getSADataInstancesWithEmptyList() throws Exception { getTransactionService().begin(); final List dataInstances = dataInstanceService.getSADataInstances(13544L, "dummyContainerType", parentContainerResolver, Collections.emptyList(), 1111111L); getTransactionService().complete(); assertEquals(0, dataInstances.size()); } @Test public void createDataWithComplexName() throws Exception { final SDataInstance dataInstance = buildDataInstance("FirstLetterUpperCase", Integer.class.getName(), null, "987456321", 4444L, "ActivityScope", false); insertDataInstance(dataInstance); assertTrue(0 != dataInstance.getId()); final SDataInstance dataInstance2 = buildDataInstance("var with spaces", Double.class.getName(), null, "221d", 4654L, "DummyScopeType", false); insertDataInstance(dataInstance2); assertTrue(0 != dataInstance2.getId()); } @Test public void testCreateAndRetrieveDoubleInstance() throws Exception { verifyCreateAndRetrieveDoubleDataInstance(false); } @Test public void testCreateAndRetrieveDoubleInstanceTransient() throws Exception { verifyCreateAndRetrieveDoubleDataInstance(true); } @Test public void testCreateAndRetrieveFloatInstance() throws Exception { verifyCreateAndRetrieveFloatDataInstance(false); } @Test public void testCreateAndRetrieveFloatInstanceTransient() throws Exception { verifyCreateAndRetrieveFloatDataInstance(true); } private void verifyCreateAndRetrieveBooleanDataInstance(final boolean isTransient) throws Exception { verifyCreateAndRetrieveDataInstance("createBoolean1", Boolean.class.getName(), "testCreateDescriptionForBoolean1", "true", 6L, "process", isTransient, true); } @Test public void testCreateAndRetrieveBooleanInstance() throws Exception { verifyCreateAndRetrieveBooleanDataInstance(false); } @Test public void testCreateAndRetrieveBooleanInstanceTransient() throws Exception { verifyCreateAndRetrieveBooleanDataInstance(true); } @Test public void testCreateAndRetrieveIntegerInstance() throws Exception { verifyCreateAndRetrieveDataInstance("createInteger", Integer.class.getName(), "testCreateDescription", "1+2", 7L, "process", false, 3); } @Test public void testCreateAndRetrieveIntegerInstanceTransient() throws Exception { verifyCreateAndRetrieveDataInstance("createInteger", Integer.class.getName(), "testCreateDescription", "1+2", 7L, "process", true, 3); } @Test public void testCreateAndRetrieveLongInstance() throws Exception { verifyCreateAndRetrieveDataInstance("createLong", Long.class.getName(), "testCreateDescription", "new Long(2)", 8L, "process", false, 2L); } @Test public void testCreateAndRetrieveLongInstanceTransient() throws Exception { verifyCreateAndRetrieveDataInstance("createLong", Long.class.getName(), "testCreateDescription", "new Long(2)", 8L, "process", true, 2L); } @Test public void testCreateAndRetrieveShortTextInstance() throws Exception { verifyCreateAndRetrieveDataInstance("createString", String.class.getName(), "testCreateDescription", "'test123'", 9L, "process", false, "test123"); } @Test public void testCreateAndRetrieveShortTextInstanceTransient() throws Exception { verifyCreateAndRetrieveDataInstance("createString", String.class.getName(), "testCreateDescription", "'test123'", 9L, "process", true, "test123"); } private void verifyCreateAndRetrieveLongTextData(final boolean isTransient) throws Exception { final String longText = getLongText(); final SDataInstance longTextDataInstance = buildLongTextDataInstance("longTextData", "A very long text", getStringForExpression(longText), 11L, "process", isTransient); insertDataInstance(longTextDataInstance); final SDataInstance dataInstance = getDataInstanceByNameAndContainer(longTextDataInstance.getName(), longTextDataInstance.getContainerId(), longTextDataInstance.getContainerType()); checkDataInstance(dataInstance, "longTextData", "A very long text", isTransient, String.class.getName(), longText, 11L, "process"); deleteDataInstance(dataInstance); } @Test public void testCreateAndRetrieveLongTextData() throws Exception { verifyCreateAndRetrieveLongTextData(false); } @Test public void testCreateAndRetrieveLongTextDataTransient() throws Exception { verifyCreateAndRetrieveLongTextData(true); } @Test public void testCreateAndRetrieveBlobDataInstanceWithoutDefaultValue() throws Exception { verifyCreateAndRetrieveDataInstance("blobTextData", LightEmployee.class.getName(), "A custom java object as blob", "return new org.bonitasoft.engine.data.instance.LightEmployee(\"manu\", \"duch\", 35)", 15L, "process", false, new LightEmployee("manu", "duch", 35)); } @Test public void testCreateAndRetrieveBlobDataInstanceWithoutDefaultValueTransient() throws Exception { verifyCreateAndRetrieveDataInstance("blobTextData", LightEmployee.class.getName(), "A custom java object as blob", "return new org.bonitasoft.engine.data.instance.LightEmployee(\"manu\", \"duch\", 53)", 15L, "process", true, new LightEmployee("manu", "duch", 53)); } @Test public void testCreateBooleanInstanceWithoutDefaultValue() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createBoolean2", Boolean.class.getName(), "testCreateDescriptionForBoolean2", "true", 11L, "process", false, true); } @Test public void testCreateBooleanInstanceWithoutDefaultValueTransient() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createBoolean2", Boolean.class.getName(), "testCreateDescriptionForBoolean2", "false", 11L, "process", true, false); } @Test public void testCreateDoubleInstanceWithoutDefaultValue() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createDouble2", Double.class.getName(), "testCreateDescriptionForDouble2", "37.0", 12L, "task", false, 37.0); } @Test public void testCreateDoubleInstanceWithoutDefaultValueTransient() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createDouble2", Double.class.getName(), "testCreateDescriptionForDouble2", "73.0", 12L, "task", true, 73.0); } @Test public void testCreateFloatInstanceWithoutDefaultValue() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createFloat2", Float.class.getName(), "testCreateDescriptionForFloat2", "34.F", 12L, "task", false, 34.F); } @Test public void testCreateFloatInstanceWithoutDefaultValueTransient() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createFloat2", Float.class.getName(), "testCreateDescriptionForFloat2", "43.F", 12L, "task", true, 43.F); } @Test public void testCreateIntegerInstanceWithoutDefaultValue() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createInteger2", Integer.class.getName(), "testCreateDescriptionForInteger2", "32", 13L, "task1", false, 32); } @Test public void testCreateIntegerInstanceWithoutDefaultValueTransient() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createInteger2", Integer.class.getName(), "testCreateDescriptionForInteger2", "23", 13L, "task1", true, 23); } @Test public void testCreateLongInstanceWithoutDefaultValue() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createLong2", Long.class.getName(), "testCreateDescriptionForLong2", "47", 14L, "task2", false, 47L); } @Test public void testCreateLongInstanceWithoutDefaultValueTransient() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createLong2", Long.class.getName(), "testCreateDescriptionForLong2", "74", 14L, "task2", true, 74L); } @Test public void testCreateShortTextInstanceWithoutDefaultValue() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createString2", String.class.getName(), "testCreateDescriptionForString2", "strTest", 15L, "task3", false, "strTest"); } @Test public void testCreateShortTextInstanceWithoutDefaultValueTransient() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createString2", String.class.getName(), "testCreateDescriptionForString2", "strTest2", 15L, "task3", true, "strTest2"); } private void verifyDeleteDataInstance(final String name, final String classType, final String description, final String content, final Long containerId, final String containerType, final Boolean isTransient, final Serializable checkValue, final String assertContent) throws Exception { final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId, containerType, isTransient); insertDataInstance(dataInstance); final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId()); assertNotNull(dataInstanceRes); checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId, containerType); deleteDataInstance(dataInstance); final SDataInstance dataDeletedRes = getDataInstance(dataInstance.getId()); assertNull(assertContent, dataDeletedRes); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteBooleanInstance() throws Exception { verifyDeleteDataInstance("deleteBoolean1", Boolean.class.getName(), "testDeleteDescriptionForBoolean", "true", 1L, "task", false, true, "the Boolean instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteTransientBooleanInstance() throws Exception { verifyDeleteDataInstance("deleteBoolean1", Boolean.class.getName(), "testDeleteDescriptionForBoolean", "true", 1L, "task", true, true, "the Boolean instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteDoubleInstance() throws Exception { verifyDeleteDataInstance("deleteDouble1", Double.class.getName(), "testDeleteDescriptionForDouble", "1.23D", 1L, "task", false, 1.23D, "the Double instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteFloatInstance() throws Exception { verifyDeleteDataInstance("deleteFloat1", Float.class.getName(), "testDeleteDescriptionForFloat", "1.23F", 1L, "task", false, 1.23F, "the Float instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testTransientDeleteDoubleInstance() throws Exception { verifyDeleteDataInstance("deleteDouble1", Double.class.getName(), "testDeleteDescriptionForDouble", "1.23D", 1L, "task", true, 1.23D, "the Double instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testTransientDeleteFloatInstance() throws Exception { verifyDeleteDataInstance("deleteFloat1", Float.class.getName(), "testDeleteDescriptionForFloat", "1.23F", 1L, "task", true, 1.23F, "the Float instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteIntegerInstance() throws Exception { verifyDeleteDataInstance("deleteInteger1", Integer.class.getName(), "testDeleteDescriptionForInteger", "8+4", 1L, "task", false, 12, "the Integer instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testTransientDeleteIntegerInstance() throws Exception { verifyDeleteDataInstance("deleteInteger1", Integer.class.getName(), "testDeleteDescriptionForInteger", "8+4", 1L, "task", true, 12, "the Integer instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteLongInstance() throws Exception { verifyDeleteDataInstance("deleteLong1", Long.class.getName(), "testDeleteDescriptionForLong", "12L", 1L, "task", false, 12L, "the Long instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testTransientDeleteLongInstance() throws Exception { verifyDeleteDataInstance("deleteLong1", Long.class.getName(), "testDeleteDescriptionForLong", "12L", 1L, "task", true, 12L, "the Long instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteShortTextInstance() throws Exception { verifyDeleteDataInstance("deleteString1", String.class.getName(), "testDeleteDescriptionForString", "'test123'", 1L, "task", false, "test123", "the Short Text instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testTransientDeleteShortTextInstance() throws Exception { verifyDeleteDataInstance("deleteString1", String.class.getName(), "testDeleteDescriptionForString", "'test123'", 1L, "task", true, "test123", "the Short Text instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteBlobInstance() throws Exception { final LightEmployee employee = new LightEmployee("firstName", "lastName", 30); verifyDeleteDataInstance("blobData", LightEmployee.class.getName(), "A custom java object", "new org.bonitasoft.engine.data.instance.LightEmployee(\"firstName\", \"lastName\", 30)", 11L, "processTask", false, employee, "the blob data instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testTransientDeleteBlobInstance() throws Exception { final LightEmployee employee = new LightEmployee("firstName", "lastName", 30); verifyDeleteDataInstance("blobData", LightEmployee.class.getName(), "A custom java object", "new org.bonitasoft.engine.data.instance.LightEmployee(\"firstName\", \"lastName\", 30)", 11L, "processTask", true, employee, "the blob data instance was not deleted"); } private void verifyDeleteLongTextData(final Boolean isTransient) throws Exception { final String longText = getLongText(); final SDataInstance longTextDataInstance = buildLongTextDataInstance("longTextData", "A very long text", getStringForExpression(longText), 0, null, isTransient); insertDataInstance(longTextDataInstance); final SDataInstance dataInstance = getDataInstance(longTextDataInstance.getId()); assertNotNull(dataInstance); deleteDataInstance(dataInstance); final SDataInstance dataInstanceRes = getDataInstance(longTextDataInstance.getId()); assertNull("the Long Text instance was not deleted", dataInstanceRes); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteLongTextData() throws Exception { verifyDeleteLongTextData(false); } @Test(expected = SDataInstanceNotFoundException.class) public void testTransientDeleteLongTextData() throws Exception { verifyDeleteLongTextData(true); } private long verifyUpdateDataInstance(final String name, final String classType, final String description, final String content, final Long containerId, final String containerType, final Boolean isTransient, final String updateDescription, final Serializable updateValue) throws Exception { final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId, containerType, isTransient); insertDataInstance(dataInstance); updateDataInstance(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType(), updateDescription, updateValue); final long dataInstanceId = dataInstance.getId(); final SDataInstance dataInstanceRes = getDataInstance(dataInstanceId); checkDataInstance(dataInstanceRes, name, updateDescription, isTransient, classType, updateValue, containerId, containerType); deleteDataInstance(dataInstanceRes); return dataInstanceId; } @Test public void testUpdateBooleanInstance() throws Exception { verifyUpdateDataInstance("updateBoolean", Boolean.class.getName(), "testUpdateDescription", "true", 11L, "miniTask", false, "testUpdateDescription123456", false); } @Test public void testTransientUpdateBooleanInstance() throws Exception { verifyUpdateDataInstance("updateBoolean", Boolean.class.getName(), "testUpdateDescription", "true", 11L, "miniTask", true, "testUpdateDescription123456", false); } @Test public void testUpdateDoubleInstance() throws Exception { verifyUpdateDataInstance("updateDouble", Double.class.getName(), "testUpdateDescription", "new Double(5.0)", 11L, "miniTask", false, "testUpdateDescription4", 7.0); } @Test public void testTransientUpdateDoubleInstance() throws Exception { verifyUpdateDataInstance("updateDouble", Double.class.getName(), "testUpdateDescription", "new Double(5.0)", 11L, "miniTask", true, "testUpdateDescription4", 7.0); } @Test public void testUpdateFloatInstance() throws Exception { verifyUpdateDataInstance("updateFloat", Float.class.getName(), "testUpdateDescription", "new Float(5.0)", 11L, "miniTask", false, "testUpdateDescription4", 7.0F); } @Test public void testTransientUpdateFloatInstance() throws Exception { verifyUpdateDataInstance("updateFloat", Float.class.getName(), "testUpdateDescription", "new Float(5.0)", 11L, "miniTask", true, "testUpdateDescription4", 7.0F); } @Test public void testUpdateIntegerInstance() throws Exception { verifyUpdateDataInstance("updateInteger", Integer.class.getName(), "testUpdateDescription", "new Integer(5)", 11L, "miniTask", false, "testUpdateDescription2", 8); } @Test public void testTransientUpdateIntegerInstance() throws Exception { verifyUpdateDataInstance("updateInteger", Integer.class.getName(), "testUpdateDescription", "new Integer(5)", 11L, "miniTask", true, "testUpdateDescription2", 8); } @Test public void testUpdateLongInstance() throws Exception { verifyUpdateDataInstance("updateLong", Long.class.getName(), "testUpdateDescription", "new Long(5)", 11L, "miniTask", false, "testUpdateDescription2", 6L); } @Test public void testTransientUpdateLongInstance() throws Exception { verifyUpdateDataInstance("updateLong", Long.class.getName(), "testUpdateDescription", "new Long(5)", 11L, "miniTask", true, "testUpdateDescription2", 6L); } @Test public void testUpdateShortTextInstance() throws Exception { verifyUpdateDataInstance("updateShortText", String.class.getName(), "testUpdateDescription", "'123qwe'", 11L, "miniTask", false, "testUpdateDescription2", "123qwe2"); } @Test public void testTransientUpdateShortTextInstance() throws Exception { verifyUpdateDataInstance("updateShortText", String.class.getName(), "testUpdateDescription", "'123qwe'", 11L, "miniTask", true, "testUpdateDescription2", "123qwe2"); } private void verifyUpdateLongTextDataInstance(final String name, final String classType, final String description, final String content, final Long containerId, final String containerType, final Boolean isTransient, final String updateDescription, final Serializable updateValue) throws Exception { final SDataInstance dataInstance = buildLongTextDataInstance(name, description, content, containerId, containerType, isTransient); insertDataInstance(dataInstance); updateDataInstance(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType(), updateDescription, updateValue); final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId()); checkDataInstance(dataInstanceRes, name, updateDescription, isTransient, classType, updateValue, containerId, containerType); deleteDataInstance(dataInstanceRes);// dataInstance has no id here. } @Test public void testUpdateLongTextData() throws Exception { final String longText = getLongText(); verifyUpdateLongTextDataInstance("longTextData", String.class.getName(), "A very long text", getStringForExpression(longText), 11L, "processTask", false, "Updated description for long text", longText + "Updated"); } @Test public void testTransientUpdateLongTextData() throws Exception { final String longText = getLongText(); verifyUpdateLongTextDataInstance("longTextData", String.class.getName(), "A very long text", getStringForExpression(longText), 11L, "processTask", true, "Updated description for long text", longText + "Updated"); } @Test public void testUpdateBlobDataInstance() throws Exception { final LightEmployee employee = new LightEmployee("firstName", "lastName", 30); verifyUpdateDataInstance("blobTextData", LightEmployee.class.getName(), "A custom java object", null, 11L, "processTask", false, "A custom java updated", employee); } @Test public void testTransientUpdateBlobDataInstance() throws Exception { final LightEmployee employee = new LightEmployee("firstName", "lastName", 30); verifyUpdateDataInstance("blobTextData", LightEmployee.class.getName(), "A custom java object", null, 11L, "processTask", true, "A custom java updated", employee); } private void verifyCreateAndGetDataInstance(final String name, final String classType, final String description, final String content, final Long containerId, final String containerType, final Boolean isTransient, final Serializable checkValue) throws Exception { final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId, containerType, isTransient); insertDataInstance(dataInstance); final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType()); checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId, containerType); deleteDataInstance(dataInstanceRes); } @Test public void testGetDataIntegerInstance() throws Exception { verifyCreateAndGetDataInstance("getInteger", Integer.class.getName(), "testgetDescription", "new Integer(2)", 20L, "processInstance", false, 2); } @Test public void testGetTransientIntegerDataInstance() throws Exception { verifyCreateAndGetDataInstance("getInteger", Integer.class.getName(), "testgetDescription", "new Integer(2)", 20L, "processInstance", true, 2); } @Test public void testGetDataBooleanInstance() throws Exception { verifyCreateAndGetDataInstance("getBoolean", Boolean.class.getName(), "testgetDescription", "true", 20L, "processInstance", false, true); } @Test public void testGetTransientBooleanDataInstance() throws Exception { verifyCreateAndGetDataInstance("getBoolean", Boolean.class.getName(), "testgetDescription", "true", 20L, "processInstance", true, true); } @Test public void testGetDataShortTextInstance() throws Exception { verifyCreateAndGetDataInstance("getShortText", String.class.getName(), "testgetDescription", "'1122aabb'", 20L, "processInstance", false, "1122aabb"); } @Test public void testGetTransientShortTextDataInstance() throws Exception { verifyCreateAndGetDataInstance("getShortText", String.class.getName(), "testgetDescription", "'1122aabb'", 20L, "processInstance", true, "1122aabb"); } @Test public void testGetLongDataInstance() throws Exception { verifyCreateAndGetDataInstance("getLong", Long.class.getName(), "testgetDescription", "new Long(2)", 20L, "processInstance", false, 2L); } @Test public void testGetTransientLongDataInstance() throws Exception { verifyCreateAndGetDataInstance("getLong", Long.class.getName(), "testgetDescription", "new Long(2)", 20L, "processInstance", true, 2L); } @Test public void testGetDataDoubleInstance() throws Exception { verifyCreateAndGetDataInstance("getDouble", Double.class.getName(), "testgetDescription", "new Double(2.0)", 20L, "processInstance", false, 2.0); } @Test public void testGetTransientDoubleDataInstance() throws Exception { verifyCreateAndGetDataInstance("getDouble", Double.class.getName(), "testgetDescription", "new Double(2.0)", 20L, "processInstance", true, 2.0); } @Test public void testGetDataFloatInstance() throws Exception { verifyCreateAndGetDataInstance("getFloat", Float.class.getName(), "testgetDescription", "new Float(2.0)", 20L, "processInstance", false, 2.0F); } @Test public void testGetTransientFloatDataInstance() throws Exception { verifyCreateAndGetDataInstance("getFloat", Float.class.getName(), "testgetDescription", "new Float(2.0)", 20L, "processInstance", true, 2.0F); } @Test public void testGetLongTextData() throws Exception { verifyCreateAndRetrieveLongTextData(false); } @Test public void testGetLongTextDataTransient() throws Exception { verifyCreateAndRetrieveLongTextData(true); } @Test public void testGetBlobData() throws Exception { final LightEmployee employee = new LightEmployee("firstName", "lastName", 30); verifyCreateAndGetDataInstance("blobTextData", LightEmployee.class.getName(), "A custom java object2", "new org.bonitasoft.engine.data.instance.LightEmployee(\"firstName\", \"lastName\", 30)", 3L, "processTaskDefinition", false, employee); } @Test public void testGetBlobDataTransient() throws Exception { final LightEmployee employee = new LightEmployee("firstName", "lastName", 30); verifyCreateAndGetDataInstance("blobTextData", LightEmployee.class.getName(), "A custom java object3", "new org.bonitasoft.engine.data.instance.LightEmployee(\"firstName\", \"lastName\", 30)", 11L, "processTask", true, employee); } @Test public void testCreateAndRetrieveXMLDataById() throws Exception { final String xmlContent = buildSimpleXML1(); final long containerId = 15; final String containerType = "ActivityInstance"; final SDataInstance dataInstance = buildXMLDataInstance("xmlVar", "This is a xml variable", "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement", getStringForExpression(xmlContent), containerId, containerType); insertDataInstance(dataInstance); // get the data instance by several conditions final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId()); assertTrue(dataInstanceRes instanceof SXMLDataInstance); checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, "xmlVar", "This is a xml variable", false, String.class.getName(), xmlContent, containerId, containerType, "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement"); deleteDataInstance(dataInstanceRes); } private String getStringForExpression(final String str) { return "'" + str + "'"; } private String buildSimpleXML1() { return "" + "" + "value" + "" + ""; } private String buildSimpleXML2() { return "" + "" + ""; } @Test public void testUpdateXMLData() throws Exception { final String xmlContent = buildSimpleXML1(); final long containerId = 15; final String containerType = "ActivityInstance"; final SDataInstance dataInstance = buildXMLDataInstance("xmlVar", "This is a xml variable", "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement", getStringForExpression(xmlContent), containerId, containerType); insertDataInstance(dataInstance); // get the data instance by several conditions SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId()); assertTrue(dataInstanceRes instanceof SXMLDataInstance); checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, "xmlVar", "This is a xml variable", false, String.class.getName(), xmlContent, containerId, containerType, "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement"); final String updatedContent = buildSimpleXML2(); updateDataInstance(dataInstanceRes.getName(), dataInstanceRes.getContainerId(), dataInstanceRes.getContainerType(), "This is a xml variable after update", updatedContent); dataInstanceRes = getDataInstance(dataInstance.getId()); checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, "xmlVar", "This is a xml variable after update", false, String.class.getName(), updatedContent, containerId, containerType, "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement"); deleteDataInstance(dataInstanceRes); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteXMLData() throws Exception { final String xmlContent = buildSimpleXML1(); final long containerId = 15; final String containerType = "ActivityInstance"; final SDataInstance dataInstance = buildXMLDataInstance("xmlVar", "This is a xml variable", "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement", getStringForExpression(xmlContent), containerId, containerType); insertDataInstance(dataInstance); // get the data instance by several conditions final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId()); assertTrue(dataInstanceRes instanceof SXMLDataInstance); checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, "xmlVar", "This is a xml variable", false, String.class.getName(), xmlContent, containerId, containerType, "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement"); deleteDataInstance(dataInstanceRes); getDataInstance(dataInstance.getId()); } @Test public void testRetrieveXMLDataByNameAndContainer() throws Exception { final String xmlContent = buildSimpleXML1(); final long containerId = 16; final String containerType = "ActivityInstance"; final SDataInstance dataInstance = buildXMLDataInstance("xmlVar", "This is a xml variable", "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement", getStringForExpression(xmlContent), containerId, containerType); insertDataInstance(dataInstance); // get the data instance by several conditions final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer("xmlVar", containerId, containerType); assertTrue(dataInstanceRes instanceof SXMLDataInstance); checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, "xmlVar", "This is a xml variable", false, String.class.getName(), xmlContent, containerId, containerType, "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement"); deleteDataInstance(dataInstanceRes); } @Test public void testGetSADataInstance() throws Exception { final String classType = Integer.class.getName(); final SDataInstance dataInstance = buildDataInstance("updateInteger", classType, "testUpdateDescription", "new Integer(5)", 111L, "miniTask", false); insertDataInstance(dataInstance); Thread.sleep(10); final long beforeUpdateDate = System.currentTimeMillis(); Thread.sleep(10); updateDataInstance(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType(), "testUpdateDescription2", 8); final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId()); checkDataInstance(dataInstanceRes, "updateInteger", "testUpdateDescription2", false, classType, 8, 111L, "miniTask"); deleteDataInstance(dataInstanceRes); final long dataInstanceId = dataInstance.getId(); getTransactionService().begin(); try { final SADataInstance beforeUpdate = dataInstanceService.getSADataInstance(dataInstanceId, beforeUpdateDate); assertEquals(5, beforeUpdate.getValue()); assertTrue(beforeUpdate.getArchiveDate() <= beforeUpdateDate); final SADataInstance afterUpdate = dataInstanceService.getSADataInstance(dataInstanceId, System.currentTimeMillis()); assertEquals(8, afterUpdate.getValue()); assertTrue(afterUpdate.getArchiveDate() <= System.currentTimeMillis() && afterUpdate.getArchiveDate() >= beforeUpdateDate); } finally { getTransactionService().complete(); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/data/instance/LightEmployee.java ================================================ /** * 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.data.instance; import java.io.Serializable; public class LightEmployee implements Serializable { private static final long serialVersionUID = 3544356825663515645L; private final String firstName; private final String lastName; private final int age; public LightEmployee(final String firstName, final String lastName, final int age) { super(); this.firstName = firstName; this.lastName = lastName; this.age = age; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public int getAge() { return age; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((firstName == null) ? 0 : firstName.hashCode()); result = prime * result + ((lastName == null) ? 0 : lastName.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final LightEmployee other = (LightEmployee) obj; if (age != other.age) { return false; } if (firstName == null) { if (other.firstName != null) { return false; } } else if (!firstName.equals(other.firstName)) { return false; } if (lastName == null) { if (other.lastName != null) { return false; } } else if (!lastName.equals(other.lastName)) { return false; } return true; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/data/instance/TransientDataInstanceServiceIT.java ================================================ /** * 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.data.instance; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.cache.ehcache.EhCacheCacheService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.data.instance.TransientDataService; import org.bonitasoft.engine.core.data.instance.impl.TransientDataServiceImpl; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory; import org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilderFactory; import org.bonitasoft.engine.data.instance.exception.SDataInstanceNotFoundException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder; import org.bonitasoft.engine.data.instance.model.SXMLDataInstance; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.ExpressionService; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilder; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Zhao Na * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet * @author Emmanuel Duchastenier */ @SuppressWarnings("javadoc") public class TransientDataInstanceServiceIT extends CommonBPMServicesTest { private static final Map EMPTY_RESOLVED_EXPRESSIONS = Collections.emptyMap(); protected ExpressionService expressionService; protected EhCacheCacheService cacheService; protected TransientDataService dataInstanceService; @Before public void setup() { expressionService = getServiceAccessor().getExpressionService(); cacheService = (EhCacheCacheService) getServiceAccessor().getCacheService(); if (cacheService.isStopped()) { try { cacheService.start(); } catch (SBonitaException e) { throw new RuntimeException(e); } } dataInstanceService = new TransientDataServiceImpl(cacheService, getServiceAccessor().getExpressionResolverService(), getServiceAccessor().getActivityInstanceService(), getServiceAccessor().getProcessDefinitionService()); } @After public void after() { try { cacheService.stop(); cacheService.start(); } catch (SBonitaException e) { throw new RuntimeException(e); } } private SDataInstance buildDataInstance(final String instanceName, final String className, final String description, final String content, final long containerId, final String containerType, final boolean isTransient) throws SBonitaException { // create definition final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class) .createNewInstance(instanceName, className); initializeBuilder(dataDefinitionBuilder, description, content, className, isTransient); final SDataDefinition dataDefinition = dataDefinitionBuilder.done(); // create data instance final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition); evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder); return dataInstanceBuilder.setContainerId(containerId).setContainerType(containerType).done(); } private SDataInstance buildDataInstanceConstant(final String instanceName, final String className, final String description, final String content, final long containerId, final String containerType, final boolean isTransient) throws SBonitaException { // create definition final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class) .createNewInstance(instanceName, className); initializeBuilderConstant(dataDefinitionBuilder, description, content, className, isTransient); final SDataDefinition dataDefinition = dataDefinitionBuilder.done(); // create datainstance final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition); evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder); return dataInstanceBuilder.setContainerId(containerId).setContainerType(containerType).done(); } private void evaluateDefaultValueOf(final SDataDefinition dataDefinition, final SDataInstanceBuilder dataInstanceBuilder) throws SBonitaException { final SExpression expression = dataDefinition.getDefaultValueExpression(); if (expression != null) { dataInstanceBuilder.setValue((Serializable) expressionService.evaluate(expression, Collections.singletonMap("processDefinitionId", 546l), EMPTY_RESOLVED_EXPRESSIONS, ContainerState.ACTIVE)); } } private SDataInstance buildLongTextDataInstance(final String instanceName, final String description, final String content, final long containerId, final String containerType, final Boolean isTransient) throws SBonitaException { // create definition final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class) .createNewTextData(instanceName) .setAsLongText(true); initializeBuilder(dataDefinitionBuilder, description, content, String.class.getName(), isTransient); final SDataDefinition dataDefinition = dataDefinitionBuilder.done(); final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition) .setContainerId(containerId).setContainerType(containerType); // create data instance evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder); return dataInstanceBuilder.done(); } private SDataInstance buildXMLDataInstance(final String instanceName, final String description, final String namespace, final String xmlElement, final String content, final long containerId, final String containerType) throws SBonitaException { // create definition final SDataDefinition dataDefinition = buildDataDefinition(instanceName, description, namespace, xmlElement, content, String.class.getName()); // create data instance final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition) .setContainerId(containerId).setContainerType(containerType); evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder); return dataInstanceBuilder.done(); } private SDataDefinition buildDataDefinition(final String instanceName, final String description, final String namespace, final String xmlElement, final String content, final String defaultValueReturnType) throws SInvalidExpressionException { final SXMLDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory .get(SXMLDataDefinitionBuilderFactory.class).createNewXMLData(instanceName) .setNamespace(namespace).setElement(xmlElement); dataDefinitionBuilder.setDescription(description); dataDefinitionBuilder.setTransient(false); SExpression expression = null; if (content != null) { // create expression final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class) .createNewInstance(); // this discrimination'll be changed. expreBuilder.setContent(content).setReturnType(defaultValueReturnType) .setExpressionType(SExpression.TYPE_READ_ONLY_SCRIPT) .setInterpreter(SExpression.GROOVY); expression = expreBuilder.done(); } dataDefinitionBuilder.setDefaultValue(expression); return dataDefinitionBuilder.done(); } private void initializeBuilder(final SDataDefinitionBuilder dataDefinitionBuilder, final String description, final String content, final String defaultValueReturnType, final boolean isTransient) throws SInvalidExpressionException { SExpression expression = null; if (content != null) { // create expression final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class) .createNewInstance(); // this discrimination'll be changed. expreBuilder.setContent(content).setReturnType(defaultValueReturnType) .setExpressionType(SExpression.TYPE_READ_ONLY_SCRIPT) .setInterpreter(SExpression.GROOVY); expression = expreBuilder.done(); } dataDefinitionBuilder.setDescription(description); dataDefinitionBuilder.setTransient(isTransient); dataDefinitionBuilder.setDefaultValue(expression); } private void initializeBuilderConstant(final SDataDefinitionBuilder dataDefinitionBuilder, final String description, final String content, final String defaultValueReturnType, final boolean isTransient) throws SInvalidExpressionException { SExpression expression = null; if (content != null) { // create expression final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class) .createNewInstance(); // this discrimination'll be changed. expreBuilder.setContent(content).setReturnType(defaultValueReturnType) .setExpressionType(SExpression.TYPE_CONSTANT); expression = expreBuilder.done(); } dataDefinitionBuilder.setDescription(description); dataDefinitionBuilder.setTransient(isTransient); dataDefinitionBuilder.setDefaultValue(expression); } private void checkDataInstance(final SDataInstance dataInstance, final String name, final String description, final boolean isTransient, final String className, final Serializable value, final long containerId, final String containerType) { assertEquals(name, dataInstance.getName()); assertEquals(description, dataInstance.getDescription()); assertEquals(isTransient, dataInstance.isTransientData()); assertEquals(className, dataInstance.getClassName()); assertEquals(value, dataInstance.getValue()); assertEquals(containerId, dataInstance.getContainerId()); assertEquals(containerType, dataInstance.getContainerType()); } private void checkXMLDataInstance(final SXMLDataInstance dataInstance, final String name, final String description, final boolean isTransient, final String className, final Serializable value, final long containerId, final String containerType, final String namespace, final String element) { assertEquals(namespace, dataInstance.getNamespace()); assertEquals(element, dataInstance.getElement()); checkDataInstance(dataInstance, name, description, isTransient, className, value, containerId, containerType); } private EntityUpdateDescriptor getUpdateDescriptor(final String description, final Serializable newValue) { // update data instance final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor(); updateDescriptor.addField(SDataInstance.VALUE, newValue); updateDescriptor.addField(SDataInstance.DESCRIPTION, description); return updateDescriptor; } private void insertDataInstance(final SDataInstance dataInstance) throws SBonitaException { getTransactionService().begin(); try { // create data instance dataInstanceService.createDataInstance(dataInstance); } finally { getTransactionService().complete(); } } private SDataInstance getDataInstance(final long dataInstanceId) throws SBonitaException { getTransactionService().begin(); try { return dataInstanceService.getDataInstance(dataInstanceId); } finally { getTransactionService().complete(); } } private SDataInstance getDataInstanceByNameAndContainer(final String dataName, final long containerId, final String containerType) throws SBonitaException { getTransactionService().begin(); try { // get the data instance by several conditions return dataInstanceService.getDataInstance(dataName, containerId, containerType); } finally { getTransactionService().complete(); } } private String getLongText() { final StringBuilder stb = new StringBuilder(); for (int i = 0; i < 255; i++) { stb.append(i); } return stb.toString(); } private void deleteDataInstance(final SDataInstance dataInstance) throws SBonitaException { getTransactionService().begin(); try { dataInstanceService.deleteDataInstance(dataInstance); } finally { getTransactionService().complete(); } } private void updateDataInstance(final String dataName, final Long containerId, final String containerType, final String newDescription, final Serializable value) throws SBonitaException { getTransactionService().begin(); try { // retrieve the data instance final SDataInstance dataInstanceRes = dataInstanceService.getDataInstance(dataName, containerId, containerType); // update the data instance and this step must be with an activity data Instance in same transaction. final EntityUpdateDescriptor updateDescriptor = getUpdateDescriptor(newDescription, value); dataInstanceService.updateDataInstance(dataInstanceRes, updateDescriptor); } finally { getTransactionService().complete(); } } private void verifyCreateAndRetrieveDataInstance(final String name, final String classType, final String description, final String content, final Long containerId, final String containerType, final Boolean isTransient, final Serializable checkValue) throws Exception { final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId, containerType, isTransient); insertDataInstance(dataInstance); final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType()); checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId, containerType); deleteDataInstance(dataInstanceRes); } private void verifyCreateAndRetrieveDataInstanceConstant(final String name, final String classType, final String description, final String content, final Long containerId, final String containerType, final Boolean isTransient, final Serializable checkValue) throws Exception { final SDataInstance dataInstance = buildDataInstanceConstant(name, classType, description, content, containerId, containerType, isTransient); insertDataInstance(dataInstance); final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType()); checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId, containerType); deleteDataInstance(dataInstanceRes); } private void verifyCreateAndRetrieveDoubleDataInstance(final boolean isTransient) throws Exception { verifyCreateAndRetrieveDataInstance("createDouble1", Double.class.getName(), "testCreateDescriptionForDouble1", "new Double(5.0)", 6L, "process", isTransient, 5.0); } private void verifyCreateAndRetrieveFloatDataInstance(final boolean isTransient) throws Exception { verifyCreateAndRetrieveDataInstance("createFloat1", Float.class.getName(), "testCreateDescriptionForFloat1", "new Float(5.0)", 6L, "process", isTransient, 5.0F); } @Test public void getDataInstancesWithTransientAndNonTransientData() throws Exception { final long containerId = 4444L; final String containerType = "ActivityScope"; final String instance1Name = "non-transient"; final SDataInstance data1Instance = buildDataInstance(instance1Name, Integer.class.getName(), "some non transient data", null, containerId, containerType, false); insertDataInstance(data1Instance); final String instance2Name = "transient"; final SDataInstance data2Instance = buildDataInstance(instance2Name, Integer.class.getName(), "some transient data", null, containerId, containerType, true); insertDataInstance(data2Instance); final List dataNames = new ArrayList<>(2); dataNames.add(instance1Name); dataNames.add(instance2Name); getTransactionService().begin(); List dataInstances = null; try { dataInstances = dataInstanceService.getDataInstances(dataNames, containerId, containerType); } finally { getTransactionService().complete(); } assertEquals(2, dataInstances.size()); Assertions.assertThat(dataInstances).extracting("name").containsOnly(instance1Name, instance2Name); } @Test public void createDataWithComplexName() throws Exception { final SDataInstance dataInstance = buildDataInstance("FirstLetterUpperCase", Integer.class.getName(), null, "987456321", 4444L, "ActivityScope", false); insertDataInstance(dataInstance); assertTrue(0 != dataInstance.getId()); final SDataInstance dataInstance2 = buildDataInstance("var with spaces", Double.class.getName(), null, "221d", 4654L, "DummyScopeType", false); insertDataInstance(dataInstance2); assertTrue(0 != dataInstance2.getId()); } @Test public void testCreateAndRetrieveDoubleInstance() throws Exception { verifyCreateAndRetrieveDoubleDataInstance(false); } @Test public void testCreateAndRetrieveDoubleInstanceTransient() throws Exception { verifyCreateAndRetrieveDoubleDataInstance(true); } @Test public void testCreateAndRetrieveFloatInstance() throws Exception { verifyCreateAndRetrieveFloatDataInstance(false); } @Test public void testCreateAndRetrieveFloatInstanceTransient() throws Exception { verifyCreateAndRetrieveFloatDataInstance(true); } private void verifyCreateAndRetrieveBooleanDataInstance(final boolean isTransient) throws Exception { verifyCreateAndRetrieveDataInstance("createBoolean1", Boolean.class.getName(), "testCreateDescriptionForBoolean1", "true", 6L, "process", isTransient, true); } @Test public void testCreateAndRetrieveBooleanInstance() throws Exception { verifyCreateAndRetrieveBooleanDataInstance(false); } @Test public void testCreateAndRetrieveBooleanInstanceTransient() throws Exception { verifyCreateAndRetrieveBooleanDataInstance(true); } @Test public void testCreateAndRetrieveIntegerInstance() throws Exception { verifyCreateAndRetrieveDataInstance("createInteger", Integer.class.getName(), "testCreateDescription", "1+2", 7L, "process", false, 3); } @Test public void testCreateAndRetrieveIntegerInstanceTransient() throws Exception { verifyCreateAndRetrieveDataInstance("createInteger", Integer.class.getName(), "testCreateDescription", "1+2", 7L, "process", true, 3); } @Test public void testCreateAndRetrieveLongInstance() throws Exception { verifyCreateAndRetrieveDataInstance("createLong", Long.class.getName(), "testCreateDescription", "new Long(2)", 8L, "process", false, 2L); } @Test public void testCreateAndRetrieveLongInstanceTransient() throws Exception { verifyCreateAndRetrieveDataInstance("createLong", Long.class.getName(), "testCreateDescription", "new Long(2)", 8L, "process", true, 2L); } @Test public void testCreateAndRetrieveShortTextInstance() throws Exception { verifyCreateAndRetrieveDataInstance("createString", String.class.getName(), "testCreateDescription", "'test123'", 9L, "process", false, "test123"); } @Test public void testCreateAndRetrieveShortTextInstanceTransient() throws Exception { verifyCreateAndRetrieveDataInstance("createString", String.class.getName(), "testCreateDescription", "'test123'", 9L, "process", true, "test123"); } private void verifyCreateAndRetrieveLongTextData(final boolean isTransient) throws Exception { final String longText = getLongText(); final SDataInstance longTextDataInstance = buildLongTextDataInstance("longTextData", "A very long text", getStringForExpression(longText), 11L, "process", isTransient); insertDataInstance(longTextDataInstance); final SDataInstance dataInstance = getDataInstanceByNameAndContainer(longTextDataInstance.getName(), longTextDataInstance.getContainerId(), longTextDataInstance.getContainerType()); checkDataInstance(dataInstance, "longTextData", "A very long text", isTransient, String.class.getName(), longText, 11L, "process"); deleteDataInstance(dataInstance); } @Test public void testCreateAndRetrieveLongTextData() throws Exception { verifyCreateAndRetrieveLongTextData(false); } @Test public void testCreateAndRetrieveLongTextDataTransient() throws Exception { verifyCreateAndRetrieveLongTextData(true); } @Test public void testCreateAndRetrieveBlobDataInstanceWithoutDefaultValue() throws Exception { verifyCreateAndRetrieveDataInstance("blobTextData", LightEmployee.class.getName(), "A custom java object as blob", "return new org.bonitasoft.engine.data.instance.LightEmployee(\"manu\", \"duch\", 35)", 15L, "process", false, new LightEmployee("manu", "duch", 35)); } @Test public void testCreateAndRetrieveBlobDataInstanceWithoutDefaultValueTransient() throws Exception { verifyCreateAndRetrieveDataInstance("blobTextData", LightEmployee.class.getName(), "A custom java object as blob", "return new org.bonitasoft.engine.data.instance.LightEmployee(\"manu\", \"duch\", 53)", 15L, "process", true, new LightEmployee("manu", "duch", 53)); } @Test public void testCreateBooleanInstanceWithoutDefaultValue() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createBoolean2", Boolean.class.getName(), "testCreateDescriptionForBoolean2", "true", 11L, "process", false, true); } @Test public void testCreateBooleanInstanceWithoutDefaultValueTransient() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createBoolean2", Boolean.class.getName(), "testCreateDescriptionForBoolean2", "false", 11L, "process", true, false); } @Test public void testCreateDoubleInstanceWithoutDefaultValue() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createDouble2", Double.class.getName(), "testCreateDescriptionForDouble2", "37.0", 12L, "task", false, 37.0); } @Test public void testCreateDoubleInstanceWithoutDefaultValueTransient() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createDouble2", Double.class.getName(), "testCreateDescriptionForDouble2", "73.0", 12L, "task", true, 73.0); } @Test public void testCreateFloatInstanceWithoutDefaultValue() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createFloat2", Float.class.getName(), "testCreateDescriptionForFloat2", "34.F", 12L, "task", false, 34.F); } @Test public void testCreateFloatInstanceWithoutDefaultValueTransient() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createFloat2", Float.class.getName(), "testCreateDescriptionForFloat2", "43.F", 12L, "task", true, 43.F); } @Test public void testCreateIntegerInstanceWithoutDefaultValue() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createInteger2", Integer.class.getName(), "testCreateDescriptionForInteger2", "32", 13L, "task1", false, 32); } @Test public void testCreateIntegerInstanceWithoutDefaultValueTransient() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createInteger2", Integer.class.getName(), "testCreateDescriptionForInteger2", "23", 13L, "task1", true, 23); } @Test public void testCreateLongInstanceWithoutDefaultValue() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createLong2", Long.class.getName(), "testCreateDescriptionForLong2", "47", 14L, "task2", false, 47L); } @Test public void testCreateLongInstanceWithoutDefaultValueTransient() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createLong2", Long.class.getName(), "testCreateDescriptionForLong2", "74", 14L, "task2", true, 74L); } @Test public void testCreateShortTextInstanceWithoutDefaultValue() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createString2", String.class.getName(), "testCreateDescriptionForString2", "strTest", 15L, "task3", false, "strTest"); } @Test public void testCreateShortTextInstanceWithoutDefaultValueTransient() throws Exception { verifyCreateAndRetrieveDataInstanceConstant("createString2", String.class.getName(), "testCreateDescriptionForString2", "strTest2", 15L, "task3", true, "strTest2"); } private void verifyDeleteDataInstance(final String name, final String classType, final String description, final String content, final Long containerId, final String containerType, final Boolean isTransient, final Serializable checkValue, final String assertContent) throws Exception { final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId, containerType, isTransient); insertDataInstance(dataInstance); final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId()); assertNotNull(dataInstanceRes); checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId, containerType); deleteDataInstance(dataInstance); final SDataInstance dataDeletedRes = getDataInstance(dataInstance.getId()); assertNull(assertContent, dataDeletedRes); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteBooleanInstance() throws Exception { verifyDeleteDataInstance("deleteBoolean1", Boolean.class.getName(), "testDeleteDescriptionForBoolean", "true", 1L, "task", false, true, "the Boolean instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteTransientBooleanInstance() throws Exception { verifyDeleteDataInstance("deleteBoolean1", Boolean.class.getName(), "testDeleteDescriptionForBoolean", "true", 1L, "task", true, true, "the Boolean instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteDoubleInstance() throws Exception { verifyDeleteDataInstance("deleteDouble1", Double.class.getName(), "testDeleteDescriptionForDouble", "1.23D", 1L, "task", false, 1.23D, "the Double instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteFloatInstance() throws Exception { verifyDeleteDataInstance("deleteFloat1", Float.class.getName(), "testDeleteDescriptionForFloat", "1.23F", 1L, "task", false, 1.23F, "the Float instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testTransientDeleteDoubleInstance() throws Exception { verifyDeleteDataInstance("deleteDouble1", Double.class.getName(), "testDeleteDescriptionForDouble", "1.23D", 1L, "task", true, 1.23D, "the Double instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testTransientDeleteFloatInstance() throws Exception { verifyDeleteDataInstance("deleteFloat1", Float.class.getName(), "testDeleteDescriptionForFloat", "1.23F", 1L, "task", true, 1.23F, "the Float instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteIntegerInstance() throws Exception { verifyDeleteDataInstance("deleteInteger1", Integer.class.getName(), "testDeleteDescriptionForInteger", "8+4", 1L, "task", false, 12, "the Integer instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testTransientDeleteIntegerInstance() throws Exception { verifyDeleteDataInstance("deleteInteger1", Integer.class.getName(), "testDeleteDescriptionForInteger", "8+4", 1L, "task", true, 12, "the Integer instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteLongInstance() throws Exception { verifyDeleteDataInstance("deleteLong1", Long.class.getName(), "testDeleteDescriptionForLong", "12L", 1L, "task", false, 12L, "the Long instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testTransientDeleteLongInstance() throws Exception { verifyDeleteDataInstance("deleteLong1", Long.class.getName(), "testDeleteDescriptionForLong", "12L", 1L, "task", true, 12L, "the Long instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteShortTextInstance() throws Exception { verifyDeleteDataInstance("deleteString1", String.class.getName(), "testDeleteDescriptionForString", "'test123'", 1L, "task", false, "test123", "the Short Text instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testTransientDeleteShortTextInstance() throws Exception { verifyDeleteDataInstance("deleteString1", String.class.getName(), "testDeleteDescriptionForString", "'test123'", 1L, "task", true, "test123", "the Short Text instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteBlobInstance() throws Exception { final LightEmployee employee = new LightEmployee("firstName", "lastName", 30); verifyDeleteDataInstance("blobData", LightEmployee.class.getName(), "A custom java object", "new org.bonitasoft.engine.data.instance.LightEmployee(\"firstName\", \"lastName\", 30)", 11L, "processTask", false, employee, "the blob data instance was not deleted"); } @Test(expected = SDataInstanceNotFoundException.class) public void testTransientDeleteBlobInstance() throws Exception { final LightEmployee employee = new LightEmployee("firstName", "lastName", 30); verifyDeleteDataInstance("blobData", LightEmployee.class.getName(), "A custom java object", "new org.bonitasoft.engine.data.instance.LightEmployee(\"firstName\", \"lastName\", 30)", 11L, "processTask", true, employee, "the blob data instance was not deleted"); } private void verifyDeleteLongTextData(final Boolean isTransient) throws Exception { final String longText = getLongText(); final SDataInstance longTextDataInstance = buildLongTextDataInstance("longTextData", "A very long text", getStringForExpression(longText), 0, null, isTransient); insertDataInstance(longTextDataInstance); final SDataInstance dataInstance = getDataInstance(longTextDataInstance.getId()); assertNotNull(dataInstance); deleteDataInstance(dataInstance); final SDataInstance dataInstanceRes = getDataInstance(longTextDataInstance.getId()); assertNull("the Long Text instance was not deleted", dataInstanceRes); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteLongTextData() throws Exception { verifyDeleteLongTextData(false); } @Test(expected = SDataInstanceNotFoundException.class) public void testTransientDeleteLongTextData() throws Exception { verifyDeleteLongTextData(true); } private long verifyUpdateDataInstance(final String name, final String classType, final String description, final String content, final Long containerId, final String containerType, final Boolean isTransient, final String updateDescription, final Serializable updateValue) throws Exception { final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId, containerType, isTransient); insertDataInstance(dataInstance); updateDataInstance(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType(), updateDescription, updateValue); final long dataInstanceId = dataInstance.getId(); final SDataInstance dataInstanceRes = getDataInstance(dataInstanceId); checkDataInstance(dataInstanceRes, name, updateDescription, isTransient, classType, updateValue, containerId, containerType); deleteDataInstance(dataInstanceRes); return dataInstanceId; } @Test public void testUpdateBooleanInstance() throws Exception { verifyUpdateDataInstance("updateBoolean", Boolean.class.getName(), "testUpdateDescription", "true", 11L, "miniTask", false, "testUpdateDescription123456", false); } @Test public void testTransientUpdateBooleanInstance() throws Exception { verifyUpdateDataInstance("updateBoolean", Boolean.class.getName(), "testUpdateDescription", "true", 11L, "miniTask", true, "testUpdateDescription123456", false); } @Test public void testUpdateDoubleInstance() throws Exception { verifyUpdateDataInstance("updateDouble", Double.class.getName(), "testUpdateDescription", "new Double(5.0)", 11L, "miniTask", false, "testUpdateDescription4", 7.0); } @Test public void testTransientUpdateDoubleInstance() throws Exception { verifyUpdateDataInstance("updateDouble", Double.class.getName(), "testUpdateDescription", "new Double(5.0)", 11L, "miniTask", true, "testUpdateDescription4", 7.0); } @Test public void testUpdateFloatInstance() throws Exception { verifyUpdateDataInstance("updateFloat", Float.class.getName(), "testUpdateDescription", "new Float(5.0)", 11L, "miniTask", false, "testUpdateDescription4", 7.0F); } @Test public void testTransientUpdateFloatInstance() throws Exception { verifyUpdateDataInstance("updateFloat", Float.class.getName(), "testUpdateDescription", "new Float(5.0)", 11L, "miniTask", true, "testUpdateDescription4", 7.0F); } @Test public void testUpdateIntegerInstance() throws Exception { verifyUpdateDataInstance("updateInteger", Integer.class.getName(), "testUpdateDescription", "new Integer(5)", 11L, "miniTask", false, "testUpdateDescription2", 8); } @Test public void testTransientUpdateIntegerInstance() throws Exception { verifyUpdateDataInstance("updateInteger", Integer.class.getName(), "testUpdateDescription", "new Integer(5)", 11L, "miniTask", true, "testUpdateDescription2", 8); } @Test public void testUpdateLongInstance() throws Exception { verifyUpdateDataInstance("updateLong", Long.class.getName(), "testUpdateDescription", "new Long(5)", 11L, "miniTask", false, "testUpdateDescription2", 6L); } @Test public void testTransientUpdateLongInstance() throws Exception { verifyUpdateDataInstance("updateLong", Long.class.getName(), "testUpdateDescription", "new Long(5)", 11L, "miniTask", true, "testUpdateDescription2", 6L); } @Test public void testUpdateShortTextInstance() throws Exception { verifyUpdateDataInstance("updateShortText", String.class.getName(), "testUpdateDescription", "'123qwe'", 11L, "miniTask", false, "testUpdateDescription2", "123qwe2"); } @Test public void testTransientUpdateShortTextInstance() throws Exception { verifyUpdateDataInstance("updateShortText", String.class.getName(), "testUpdateDescription", "'123qwe'", 11L, "miniTask", true, "testUpdateDescription2", "123qwe2"); } private void verifyUpdateLongTextDataInstance(final String name, final String classType, final String description, final String content, final Long containerId, final String containerType, final Boolean isTransient, final String updateDescription, final Serializable updateValue) throws Exception { final SDataInstance dataInstance = buildLongTextDataInstance(name, description, content, containerId, containerType, isTransient); insertDataInstance(dataInstance); updateDataInstance(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType(), updateDescription, updateValue); final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId()); checkDataInstance(dataInstanceRes, name, updateDescription, isTransient, classType, updateValue, containerId, containerType); deleteDataInstance(dataInstanceRes);// dataInstance has no id here. } @Test public void testUpdateLongTextData() throws Exception { final String longText = getLongText(); verifyUpdateLongTextDataInstance("longTextData", String.class.getName(), "A very long text", getStringForExpression(longText), 11L, "processTask", false, "Updated description for long text", longText + "Updated"); } @Test public void testTransientUpdateLongTextData() throws Exception { final String longText = getLongText(); verifyUpdateLongTextDataInstance("longTextData", String.class.getName(), "A very long text", getStringForExpression(longText), 11L, "processTask", true, "Updated description for long text", longText + "Updated"); } @Test public void testUpdateBlobDataInstance() throws Exception { final LightEmployee employee = new LightEmployee("firstName", "lastName", 30); verifyUpdateDataInstance("blobTextData", LightEmployee.class.getName(), "A custom java object", null, 11L, "processTask", false, "A custom java updated", employee); } @Test public void testTransientUpdateBlobDataInstance() throws Exception { final LightEmployee employee = new LightEmployee("firstName", "lastName", 30); verifyUpdateDataInstance("blobTextData", LightEmployee.class.getName(), "A custom java object", null, 11L, "processTask", true, "A custom java updated", employee); } private void verifyCreateAndGetDataInstance(final String name, final String classType, final String description, final String content, final Long containerId, final String containerType, final Boolean isTransient, final Serializable checkValue) throws Exception { final SDataInstance dataInstance = buildDataInstance(name, classType, description, content, containerId, containerType, isTransient); insertDataInstance(dataInstance); final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType()); checkDataInstance(dataInstanceRes, name, description, isTransient, classType, checkValue, containerId, containerType); deleteDataInstance(dataInstanceRes); } @Test public void testGetDataIntegerInstance() throws Exception { verifyCreateAndGetDataInstance("getInteger", Integer.class.getName(), "testgetDescription", "new Integer(2)", 20L, "processInstance", false, 2); } @Test public void testGetTransientIntegerDataInstance() throws Exception { verifyCreateAndGetDataInstance("getInteger", Integer.class.getName(), "testgetDescription", "new Integer(2)", 20L, "processInstance", true, 2); } @Test public void testGetDataBooleanInstance() throws Exception { verifyCreateAndGetDataInstance("getBoolean", Boolean.class.getName(), "testgetDescription", "true", 20L, "processInstance", false, true); } @Test public void testGetTransientBooleanDataInstance() throws Exception { verifyCreateAndGetDataInstance("getBoolean", Boolean.class.getName(), "testgetDescription", "true", 20L, "processInstance", true, true); } @Test public void testGetDataShortTextInstance() throws Exception { verifyCreateAndGetDataInstance("getShortText", String.class.getName(), "testgetDescription", "'1122aabb'", 20L, "processInstance", false, "1122aabb"); } @Test public void testGetTransientShortTextDataInstance() throws Exception { verifyCreateAndGetDataInstance("getShortText", String.class.getName(), "testgetDescription", "'1122aabb'", 20L, "processInstance", true, "1122aabb"); } @Test public void testGetLongDataInstance() throws Exception { verifyCreateAndGetDataInstance("getLong", Long.class.getName(), "testgetDescription", "new Long(2)", 20L, "processInstance", false, 2L); } @Test public void testGetTransientLongDataInstance() throws Exception { verifyCreateAndGetDataInstance("getLong", Long.class.getName(), "testgetDescription", "new Long(2)", 20L, "processInstance", true, 2L); } @Test public void testGetDataDoubleInstance() throws Exception { verifyCreateAndGetDataInstance("getDouble", Double.class.getName(), "testgetDescription", "new Double(2.0)", 20L, "processInstance", false, 2.0); } @Test public void testGetTransientDoubleDataInstance() throws Exception { verifyCreateAndGetDataInstance("getDouble", Double.class.getName(), "testgetDescription", "new Double(2.0)", 20L, "processInstance", true, 2.0); } @Test public void testGetDataFloatInstance() throws Exception { verifyCreateAndGetDataInstance("getFloat", Float.class.getName(), "testgetDescription", "new Float(2.0)", 20L, "processInstance", false, 2.0F); } @Test public void testGetTransientFloatDataInstance() throws Exception { verifyCreateAndGetDataInstance("getFloat", Float.class.getName(), "testgetDescription", "new Float(2.0)", 20L, "processInstance", true, 2.0F); } @Test public void testGetLongTextData() throws Exception { verifyCreateAndRetrieveLongTextData(false); } @Test public void testGetLongTextDataTransient() throws Exception { verifyCreateAndRetrieveLongTextData(true); } @Test public void testGetBlobData() throws Exception { final LightEmployee employee = new LightEmployee("firstName", "lastName", 30); verifyCreateAndGetDataInstance("blobTextData", LightEmployee.class.getName(), "A custom java object2", "new org.bonitasoft.engine.data.instance.LightEmployee(\"firstName\", \"lastName\", 30)", 3L, "processTaskDefinition", false, employee); } @Test public void testGetBlobDataTransient() throws Exception { final LightEmployee employee = new LightEmployee("firstName", "lastName", 30); verifyCreateAndGetDataInstance("blobTextData", LightEmployee.class.getName(), "A custom java object3", "new org.bonitasoft.engine.data.instance.LightEmployee(\"firstName\", \"lastName\", 30)", 11L, "processTask", true, employee); } @Test public void testCreateAndRetrieveXMLDataById() throws Exception { final String xmlContent = buildSimpleXML1(); final long containerId = 15; final String containerType = "ActivityInstance"; final SDataInstance dataInstance = buildXMLDataInstance("xmlVar", "This is a xml variable", "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement", getStringForExpression(xmlContent), containerId, containerType); insertDataInstance(dataInstance); // get the data instance by several conditions final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId()); assertTrue(dataInstanceRes instanceof SXMLDataInstance); checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, "xmlVar", "This is a xml variable", false, String.class.getName(), xmlContent, containerId, containerType, "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement"); deleteDataInstance(dataInstanceRes); } private String getStringForExpression(final String str) { return "'" + str + "'"; } private String buildSimpleXML1() { final StringBuilder stb = new StringBuilder(); stb.append(""); stb.append(""); stb.append("value"); stb.append(""); stb.append(""); final String xmlContent = stb.toString(); return xmlContent; } private String buildSimpleXML2() { final StringBuilder stb = new StringBuilder(); stb.append(""); stb.append(""); stb.append(""); final String xmlContent = stb.toString(); return xmlContent; } @Test public void testUpdateXMLData() throws Exception { final String xmlContent = buildSimpleXML1(); final long containerId = 15; final String containerType = "ActivityInstance"; final SDataInstance dataInstance = buildXMLDataInstance("xmlVar", "This is a xml variable", "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement", getStringForExpression(xmlContent), containerId, containerType); insertDataInstance(dataInstance); // get the data instance by several conditions SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId()); assertTrue(dataInstanceRes instanceof SXMLDataInstance); checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, "xmlVar", "This is a xml variable", false, String.class.getName(), xmlContent, containerId, containerType, "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement"); final String updatedContent = buildSimpleXML2(); updateDataInstance(dataInstanceRes.getName(), dataInstanceRes.getContainerId(), dataInstanceRes.getContainerType(), "This is a xml variable after update", updatedContent); dataInstanceRes = getDataInstance(dataInstance.getId()); checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, "xmlVar", "This is a xml variable after update", false, String.class.getName(), updatedContent, containerId, containerType, "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement"); deleteDataInstance(dataInstanceRes); } @Test(expected = SDataInstanceNotFoundException.class) public void testDeleteXMLData() throws Exception { final String xmlContent = buildSimpleXML1(); final long containerId = 15; final String containerType = "ActivityInstance"; final SDataInstance dataInstance = buildXMLDataInstance("xmlVar", "This is a xml variable", "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement", getStringForExpression(xmlContent), containerId, containerType); insertDataInstance(dataInstance); // get the data instance by several conditions final SDataInstance dataInstanceRes = getDataInstance(dataInstance.getId()); assertTrue(dataInstanceRes instanceof SXMLDataInstance); checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, "xmlVar", "This is a xml variable", false, String.class.getName(), xmlContent, containerId, containerType, "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement"); deleteDataInstance(dataInstanceRes); getDataInstance(dataInstance.getId()); } @Test public void testRetrieveXMLDataByNameAndContainer() throws Exception { final String xmlContent = buildSimpleXML1(); final long containerId = 16; final String containerType = "ActivityInstance"; final SDataInstance dataInstance = buildXMLDataInstance("xmlVar", "This is a xml variable", "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement", getStringForExpression(xmlContent), containerId, containerType); insertDataInstance(dataInstance); // get the data instance by several conditions final SDataInstance dataInstanceRes = getDataInstanceByNameAndContainer("xmlVar", containerId, containerType); assertTrue(dataInstanceRes instanceof SXMLDataInstance); checkXMLDataInstance((SXMLDataInstance) dataInstanceRes, "xmlVar", "This is a xml variable", false, String.class.getName(), xmlContent, containerId, containerType, "org.bonitasoft.engine.data.instance.model.SDataInstance", "xmlElement"); deleteDataInstance(dataInstanceRes); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/data/model/Address.java ================================================ /** * 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.data.model; import java.io.Serializable; public class Address implements Serializable { private static final long serialVersionUID = 1L; private long id; private String street; private String city; private int zipCode; public Address(final long id, final String street, final String city, final int zipCode) { super(); this.id = id; this.street = street; this.city = city; this.zipCode = zipCode; } public long getId() { return id; } public String getStreet() { return street; } public String getCity() { return city; } public int getZipCode() { return zipCode; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/data/model/Employee.java ================================================ /** * 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.data.model; import java.io.Serializable; import java.util.ArrayList; import java.util.List; public class Employee extends LightEmployee implements Serializable { private static final long serialVersionUID = 1L; private List

addresses; public Employee(final long id, final String firstName, final String lastName, final int age) { super(id, firstName, lastName, age); } public List
getAddresses() { return addresses; } public void addAddress(final Address address) { if (this.addresses == null) { this.addresses = new ArrayList
(); } this.addresses.add(address); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/data/model/LightEmployee.java ================================================ /** * 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.data.model; public class LightEmployee { private long id; private String firstName; private String lastName; private int age; public LightEmployee(final long id, final String firstName, final String lastName, final int age) { super(); this.id = id; this.firstName = firstName; this.lastName = lastName; this.age = age; } public long getId() { return id; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public int getAge() { return age; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/dependency/DependencyServiceIT.java ================================================ /** * 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.dependency; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; import java.util.List; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.dependency.model.AbstractSDependency; import org.bonitasoft.engine.dependency.model.ScopeType; import org.junit.Test; /** * @author Charles Souillard */ public class DependencyServiceIT extends CommonBPMServicesTest { private static DependencyService dependencyService; public DependencyServiceIT() { dependencyService = getServiceAccessor().getDependencyService(); } private final String defaultName = "abc"; private final String defaultFileName = "dfv.cu"; private final byte[] defaultValue = new byte[] { 12, 33 }; @Test public void testLifeCycle() throws Exception { getTransactionService().begin(); AbstractSDependency mappedDependency = dependencyService.createMappedDependency(defaultName, defaultValue, defaultFileName, 2L, ScopeType.PROCESS); List dependencyIds = dependencyService.getDependencyIds(2L, ScopeType.PROCESS, 0, 1); assertThat(mappedDependency.getFileName()).isEqualTo(defaultFileName); assertThat(mappedDependency.getName()).isEqualTo("2_" + defaultName); assertThat(mappedDependency.getValue()).isEqualTo(defaultValue); assertThat(dependencyIds.get(0)).isEqualTo(mappedDependency.getId()); dependencyService.deleteDependencies(2L, ScopeType.PROCESS); try { dependencyService.getDependency(mappedDependency.getId()); fail("dependency with id: " + mappedDependency.getId() + " must not be found!"); } catch (final SDependencyNotFoundException e) { // OK } getTransactionService().complete(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/expression/ExpressionServiceIT.java ================================================ /** * 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.expression; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import java.io.Serializable; import java.math.BigDecimal; 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.concurrent.Callable; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.cache.ehcache.EhCacheCacheService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.data.ParentContainerResolverImpl; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.impl.GroovyScriptExpressionExecutorCacheStrategy; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilder; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Zhao na */ public class ExpressionServiceIT extends CommonBPMServicesTest { private static final long DEFINITION_ID_VALUE = 123L; private static final Map EMPTY_RESOLVED_EXPRESSIONS = Collections.emptyMap(); private static DataInstanceService dataInstanceService; private static ExpressionService expressionService; private static EhCacheCacheService cacheService; private ParentContainerResolverImpl containerResolver; @Before public void setup() { expressionService = getServiceAccessor().getExpressionService(); dataInstanceService = getServiceAccessor().getDataInstanceService(); containerResolver = (ParentContainerResolverImpl) getServiceAccessor().getParentContainerResolver(); containerResolver.setAllowUnknownContainer(true); cacheService = (EhCacheCacheService) getServiceAccessor().getCacheService(); if (cacheService.isStopped()) { try { cacheService.start(); } catch (final SBonitaException e) { throw new RuntimeException(e); } } } @After public void tearDown() { try { cacheService.stop(); cacheService.start(); } catch (final SBonitaException e) { throw new RuntimeException(e); } containerResolver.setAllowUnknownContainer(false); } private synchronized ExpressionService getExpressionService() { return expressionService; } private Object evaluate(final SExpression expression, final Map dependencyValues, final Map resolvedExpressions) throws Exception { return getTransactionService().executeInTransaction( () -> expressionService.evaluate(expression, dependencyValues, resolvedExpressions, ContainerState.ACTIVE)); } @Test public void evaluateStrConstant() throws Exception { // string final String strContent = "aabb*&^%$#@()!~> dependencyValues = new HashMap<>(); dependencyValues.put("containerId", containerId); dependencyValues.put("containerType", containerType); try { final String dataName = "abcd"; final SDataInstance dataInstance = buildDataInstance(dataName, String.class.getName(), "data_description", "'some data initial value'", containerId, containerType, false); insertDataInstance(dataInstance); dataExpr = buildExpression(dataName, SExpression.TYPE_VARIABLE, Integer.class.getName(), null, null); } catch (final Exception e) { fail("Unexpected exception " + e.getMessage()); } evaluate(dataExpr, dependencyValues, EMPTY_RESOLVED_EXPRESSIONS); } @Test(expected = SExpressionEvaluationException.class) public void evaluateWrongTypeGroovyExpression() throws Exception { final String strContent = "return new ArrayList();"; final SExpression strExpr = buildExpression(strContent, SExpression.TYPE_READ_ONLY_SCRIPT, Long.class.getName(), SExpression.GROOVY, null); evaluate(strExpr, EMPTY_RESOLVED_EXPRESSIONS); } @Test public void evaluateCorrectSuperTypeGroovyExpression() throws Exception { final String strContent = "return new ArrayList();"; final SExpression strExpr = buildExpression(strContent, SExpression.TYPE_READ_ONLY_SCRIPT, List.class.getName(), SExpression.GROOVY, null); evaluate(strExpr, EMPTY_RESOLVED_EXPRESSIONS); } @Test public void checkGroovyScriptStrategyUsesCache() throws Exception { //given final String strContent = "return \"junit test checkGroovyScriptStrategyUsesCache\""; final SExpression strExpr = buildExpression(strContent, SExpression.TYPE_READ_ONLY_SCRIPT, String.class.getName(), SExpression.GROOVY, null); final String cacheKey = GroovyScriptExpressionExecutorCacheStrategy.SCRIPT_KEY + strContent.hashCode(); assertThat(cacheService.get(GroovyScriptExpressionExecutorCacheStrategy.GROOVY_SCRIPT_CACHE_NAME, cacheKey)) .as("should not contains key").isNull(); //when evaluate(strExpr, EMPTY_RESOLVED_EXPRESSIONS); //then assertThat(cacheService.get(GroovyScriptExpressionExecutorCacheStrategy.GROOVY_SCRIPT_CACHE_NAME, cacheKey)) .as("should contains key").isNotNull(); } @Test @SuppressWarnings("unchecked") public void evaluateListExpression() throws Exception { final String strConstant = "abcd"; final String dataName = "myLocalVariable"; final String dataValue = "ManuDataValue"; final long containerId = 1498674654675968768L; final String containerType = "localEvaluationContext"; insertDataInstance(buildDataInstance(dataName, String.class.getName(), "Some dummy data description", "'" + dataValue + "'", containerId, containerType, false)); final SExpression exprConstant = buildExpression(strConstant, SExpression.TYPE_CONSTANT, String.class.getName(), null, null); final SExpression exprData = buildExpression(dataName, SExpression.TYPE_VARIABLE, String.class.getName(), null, null); final SExpression expr = buildExpression(null, SExpression.TYPE_LIST, List.class.getName(), null, Arrays.asList(exprConstant, exprData)); final Map ctx = new HashMap<>(2); ctx.put("containerId", containerId); ctx.put("containerType", containerType); final Map evaluatedExpressions = new HashMap<>(2); evaluatedExpressions.put(exprConstant.getDiscriminant(), evaluate(exprConstant, EMPTY_RESOLVED_EXPRESSIONS)); evaluatedExpressions.put(exprData.getDiscriminant(), evaluate(exprData, ctx, EMPTY_RESOLVED_EXPRESSIONS)); final Object res = evaluate(expr, null, evaluatedExpressions); assertEquals(strConstant, ((List) res).get(0)); assertEquals(dataValue, ((List) res).get(1)); } @Test public void evaluateDoubleConstant() throws Exception { // Double final String doubleContent = "10.3"; final SExpression doubExpr = buildExpression(doubleContent, SExpression.TYPE_CONSTANT, Double.class.getName(), null, null); final Object expressionResult = evaluate(doubExpr, EMPTY_RESOLVED_EXPRESSIONS); assertEquals(10.3, expressionResult); } @Test(expected = SInvalidExpressionException.class) public void evaluateDoubleEmptyConstant() throws Exception { final String strContent = ""; final SExpression strExpr = buildExpression(strContent, SExpression.TYPE_CONSTANT, Double.class.getName(), null, null); evaluateAndCheckResult(strExpr, strContent, EMPTY_RESOLVED_EXPRESSIONS); } @Test public void evaluateDoubleTypeNoDecimal() throws Exception { // Double final String doubleContent = "10."; final SExpression doubExpr = buildExpression(doubleContent, SExpression.TYPE_CONSTANT, Double.class.getName(), null, null); final Object expressionResult = evaluate(doubExpr, EMPTY_RESOLVED_EXPRESSIONS); assertEquals(10.0, expressionResult); } @Test public void evaluateDoubleTypeNoRadical() throws Exception { // Double final String doubleContent = ".3"; final SExpression doubExpr = buildExpression(doubleContent, SExpression.TYPE_CONSTANT, Double.class.getName(), null, null); final Object expressionResult = evaluate(doubExpr, EMPTY_RESOLVED_EXPRESSIONS); assertEquals(0.3, expressionResult); } @Test public void evaluateLongConstant() throws Exception { // Long final String longContent = "123000000"; final SExpression longExpr = buildExpression(longContent, SExpression.TYPE_CONSTANT, Long.class.getName(), null, null); final Object expressionResult = evaluate(longExpr, EMPTY_RESOLVED_EXPRESSIONS); assertEquals(123000000L, expressionResult); } @Test(expected = SInvalidExpressionException.class) public void evaluateLongEmptyConstant() throws Exception { final String longContent = ""; final SExpression longExpr = buildExpression(longContent, SExpression.TYPE_CONSTANT, Long.class.getName(), null, null); evaluate(longExpr, EMPTY_RESOLVED_EXPRESSIONS); } @Test(expected = SExpressionDependencyMissingException.class) public void evaluatePatternExpressionWithException() throws Exception { final String expressionContent = "Expression with missing dependencies"; final List dependencies = new ArrayList<>(1); dependencies.add(buildExpression("value_will_be_missing", SExpression.TYPE_CONSTANT, Double.class.getName(), null, null)); final SExpression simplePatternExpression = newPatternExpression(expressionContent, dependencies); evaluate(simplePatternExpression, EMPTY_RESOLVED_EXPRESSIONS); } @Test public void evaluatePatternExpressionWithoutDependencies() throws Exception { final String expressionContent = "Default expression with no dependencies"; final SExpression simplePatternExpression = newPatternExpression(expressionContent, null); final Object expressionResult = evaluate(simplePatternExpression, EMPTY_RESOLVED_EXPRESSIONS); assertEquals(expressionContent, expressionResult); } @Test(expected = SInvalidExpressionException.class) public void evaluateEmptyPatternExpression() throws Exception { final long processInstanceId = 123456L; final String data1Content = "emptyPatternData"; insertDataInstance(buildDataInstance(data1Content, String.class.getName(), "emptyPatternData_description", "'EXPRESSION'", processInstanceId, "process", false)); final SExpression data1Expr = buildExpression(data1Content, SExpression.TYPE_VARIABLE, String.class.getName(), null, null); final String constant2Content = "123456789"; final SExpression intExpr = buildExpression(constant2Content, SExpression.TYPE_CONSTANT, Integer.class.getName(), null, null); final List dependencyExpresssions = new ArrayList<>(2); dependencyExpresssions.add(data1Expr); dependencyExpresssions.add(intExpr); final String expressionContent = " "; final SExpression patternExpression = newPatternExpression(expressionContent, dependencyExpresssions); final Map expressionEvalContext = new HashMap<>(2); expressionEvalContext.put(data1Content, "EXPRESSION"); expressionEvalContext.put(constant2Content, 2); evaluate(patternExpression, expressionEvalContext, EMPTY_RESOLVED_EXPRESSIONS); } @Test public void evaluateStrGroovy() throws Exception { // String final String groovyContentStr = "'a'+1+'b'"; final SExpression groovyContentExpr = buildExpression(groovyContentStr, SExpression.TYPE_READ_ONLY_SCRIPT, String.class.getName(), SExpression.GROOVY, null); final Object expressionResult = evaluate(groovyContentExpr, EMPTY_RESOLVED_EXPRESSIONS); assertEquals("a1b", expressionResult); } @Test public void evaluateNumericGroovy() throws Exception { // numeric final String groovyContentNum = "((1+2)*2)/3"; final SExpression groovyNumExpr = buildExpression(groovyContentNum, SExpression.TYPE_READ_ONLY_SCRIPT, BigDecimal.class.getName(), SExpression.GROOVY, null); final Object expressionResult = evaluate(groovyNumExpr, EMPTY_RESOLVED_EXPRESSIONS); assertEquals(new BigDecimal(2), expressionResult); } @Test(expected = SInvalidExpressionException.class) public void evaluateStrEmptyGroovy() throws Exception { // String final String groovyContentStr = " "; final SExpression groovyContentExpr = buildExpression(groovyContentStr, SExpression.TYPE_READ_ONLY_SCRIPT, String.class.getName(), SExpression.GROOVY, null); final Object expressionResult = evaluate(groovyContentExpr, EMPTY_RESOLVED_EXPRESSIONS); assertEquals("a1b", expressionResult); } @Test(expected = SExpressionTypeUnknownException.class) public void evaluateUnkonwnExpressionType() throws Exception { final String expressionContent = "str"; final SExpression expression = buildExpression(expressionContent, "TYPE_UNKNOWN", Boolean.class.getName(), null, null); final Map map = new HashMap<>(); map.put(expressionContent, "abc"); evaluate(expression, map, EMPTY_RESOLVED_EXPRESSIONS); } @Test(expected = SExpressionEvaluationException.class) public void evaluateUnknownProperty() throws Exception { // numeric final String groovyContentNum = "variable1"; final SExpression groovyNumExpr = buildExpression(groovyContentNum, SExpression.TYPE_READ_ONLY_SCRIPT, BigDecimal.class.getName(), SExpression.GROOVY, null); evaluate(groovyNumExpr, EMPTY_RESOLVED_EXPRESSIONS); } private SExpression newPatternExpression(final String content, final List dependencies) { return buildExpression(content, SExpression.TYPE_PATTERN, String.class.getName(), null, dependencies); } private void initializeBuilder(final SDataDefinitionBuilder dataDefinitionBuilder, final String description, final String content, final boolean isTransient, final String defaultValueReturnType) { SExpression expression = null; if (content != null) { // create expression final SExpressionBuilder expreBuilder = BuilderFactory.get(SExpressionBuilderFactory.class) .createNewInstance(); // this discrimination'll be changed. expreBuilder.setContent(content).setExpressionType(SExpression.TYPE_READ_ONLY_SCRIPT) .setInterpreter(SExpression.GROOVY) .setReturnType(defaultValueReturnType); try { expression = expreBuilder.done(); } catch (final SInvalidExpressionException e) { throw new IllegalArgumentException(e); } } dataDefinitionBuilder.setDescription(description); dataDefinitionBuilder.setTransient(isTransient); dataDefinitionBuilder.setDefaultValue(expression); } private SDataInstance buildDataInstance(final String instanceName, final String className, final String description, final String content, final long containerId, final String containerType, final boolean isTransient) throws Exception { // create definition final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class) .createNewInstance(instanceName, className); initializeBuilder(dataDefinitionBuilder, description, content, isTransient, className); final SDataDefinition dataDefinition = dataDefinitionBuilder.done(); // create datainstance final SDataInstanceBuilder dataInstanceBuilder = SDataInstanceBuilder.createNewInstance(dataDefinition); evaluateDefaultValueOf(dataDefinition, dataInstanceBuilder); return dataInstanceBuilder.setContainerId(containerId).setContainerType(containerType).done(); } private void evaluateDefaultValueOf(final SDataDefinition dataDefinition, final SDataInstanceBuilder dataInstanceBuilder) throws Exception { final SExpression expression = dataDefinition.getDefaultValueExpression(); if (expression != null) { dataInstanceBuilder.setValue((Serializable) evaluate(expression, EMPTY_RESOLVED_EXPRESSIONS)); } } private void insertDataInstance(final SDataInstance dataInstance) throws Exception { getTransactionService().executeInTransaction((Callable) () -> { dataInstanceService.createDataInstance(dataInstance); return null; }); } private void verifyEvaluateVariable(final String expressionContent, final Class classType, final String description, final Long containerId, final String containerType, final String value, final Boolean isTransient, final Serializable hopeValue) throws Exception { // create data final SDataInstance dataInstance = buildDataInstance(expressionContent, classType.getName(), description, value, containerId, containerType, isTransient); // insert data insertDataInstance(dataInstance); // definite expression final SExpression variableExpr = buildExpression(expressionContent, SExpression.TYPE_VARIABLE, classType.getName(), null, null); final Map dependencyValues = new HashMap<>(); dependencyValues.put("containerId", containerId); dependencyValues.put("containerType", containerType); final Object resultValue = evaluate(variableExpr, dependencyValues, EMPTY_RESOLVED_EXPRESSIONS); if (resultValue != null) { assertEquals(classType, resultValue.getClass()); } assertEquals(hopeValue, resultValue); } @Test public void evaluateShortTextVariableDB() throws Exception { verifyEvaluateVariable("userName", String.class, "123456qwewr", 11L, "process", "'Edward'", false, "Edward"); } @Test public void evaluateShortTextVariableDBWithNoDefaultValue() throws Exception { verifyEvaluateVariable("nullableData", String.class, "data that is possibly null", 21L, "process", null, false, null); } @Test public void evaluateShortTextVariableCache() throws Exception { verifyEvaluateVariable("userName", String.class, "123456qwewr", 12L, "process", "'Edward'", true, "Edward"); } @Test public void evaluateNumericVariableDB() throws Exception { verifyEvaluateVariable("pageSum", Integer.class, "evaluate Numeric Variable DB", 12L, "task", "100", false, 100); } @Test public void evaluateNumericVariableCache() throws Exception { verifyEvaluateVariable("pageSum", Integer.class, "evaluate Numeric Variabl eCache", 13L, "task", "100", true, 100); } @Test(expected = SInvalidExpressionException.class) public void evaluateShortTextEmptyVariableDB() throws Exception { verifyEvaluateVariable("", String.class, "evaluate ShortText Empty Variable DB", 23456L, "container_16", "'Edward'", false, "Edward"); } @Test(expected = SInvalidExpressionException.class) public void evaluateShortTextEmptyVariableCache() throws Exception { verifyEvaluateVariable(" ", String.class, "evaluate ShortText Empty Variable Cache", 34567L, "container_17", "'Edward'", true, "Edward"); } private SExpression buildExpression(final String content, final String expressionType, final String returnType, final String interpreter, final List dependencies) { final SExpressionBuilder eb = BuilderFactory.get(SExpressionBuilderFactory.class).createNewInstance(); eb.setName(content); eb.setContent(content); eb.setExpressionType(expressionType); eb.setInterpreter(interpreter); eb.setReturnType(returnType); eb.setDependencies(dependencies); try { return eb.done(); } catch (final SInvalidExpressionException e) { throw new IllegalArgumentException(e); } } private Object evaluate(final SExpression expression, final Map resolvedExpressions) throws Exception { return getTransactionService().executeInTransaction(() -> { final Map dependencyValues = new HashMap<>(); dependencyValues.put(ExpressionExecutorStrategy.DEFINITION_ID, DEFINITION_ID_VALUE); return getExpressionService().evaluate(expression, dependencyValues, resolvedExpressions, ContainerState.ACTIVE); }); } private void evaluateAndCheckResult(final SExpression expression, final Object expectedValue, final Map resolvedExpression) throws Exception { final Object expressionResult = evaluate(expression, resolvedExpression); assertEquals(expectedValue, expressionResult); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/identity/IdentityServiceIT.java ================================================ /** * 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.identity; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.identity.model.SContactInfo; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.identity.model.builder.SContactInfoUpdateBuilderFactory; import org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilderFactory; import org.bonitasoft.engine.identity.model.builder.SGroupUpdateBuilderFactory; import org.bonitasoft.engine.identity.model.builder.SRoleUpdateBuilderFactory; import org.bonitasoft.engine.identity.model.builder.SUserUpdateBuilderFactory; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SearchFields; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.test.util.TestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros */ public class IdentityServiceIT extends CommonBPMServicesTest { private static IdentityService identityService; @Before public void before() { identityService = getServiceAccessor().getIdentityService(); } @After public void tearDown() throws Exception { TestUtil.closeTransactionIfOpen(getTransactionService()); getTransactionService().begin(); deleteUserMemberships(); deleteRoles(); deleteGroups(); deleteUsers(); getTransactionService().complete(); } private void deleteUserMemberships() throws SIdentityException { final List memberships = identityService.getUserMemberships(0, 5000); for (final SUserMembership sMembership : memberships) { identityService.deleteUserMembership(sMembership); } } @Test public void testAddUser() throws Exception { getTransactionService().begin(); final SUser user = SUser.builder().userName("john").password("bpm").build(); identityService.createUser(user); getTransactionService().complete(); getTransactionService().begin(); final SUser user2 = identityService.getUserByUserName("john"); assertNotNull("can't find the user after adding it", user2); assertEquals(user.getUserName(), user2.getUserName()); assertNotSame(user.getPassword(), user2.getPassword()); assertEquals(user.getCreationDate(), user2.getCreationDate()); // all fields getTransactionService().complete(); } @Test public void testAddUserWithId() throws Exception { getTransactionService().begin(); final SUser user = SUser.builder().userName("testAddUserWithId").password("bpm").build(); assertEquals(0, user.getId()); final SUser updatedUser = identityService.createUser(user); assertNotSame("The identifier must be set after adding a user", 0, updatedUser.getId()); getTransactionService().complete(); } @Test public void testAddUsersWithoutIds() throws Exception { getTransactionService().begin(); final SUser user1 = SUser.builder().userName("testAddUsersWithoutIds7").password("bpm").build(); identityService.createUser(user1); getTransactionService().complete(); getTransactionService().begin(); final SUser user2 = SUser.builder().userName("testAddUsersWithoutIds2").password("bpm").build(); final SUser user3 = SUser.builder().userName("testAddUsersWithoutIds3").password("bpm").build(); final SUser user4 = SUser.builder().userName("testAddUsersWithoutIds4").password("bpm").build(); final SUser user5 = SUser.builder().userName("testAddUsersWithoutIds5").password("bpm").build(); final SUser user6 = SUser.builder().userName("testAddUsersWithoutIds6").password("bpm").build(); identityService.createUser(user2); identityService.createUser(user3); identityService.createUser(user4); identityService.createUser(user5); identityService.createUser(user6); getTransactionService().complete(); } @Test public void testGetUserByUserName() throws Exception { getTransactionService().begin(); final String username = "myUser"; final SUser user = SUser.builder().userName(username).password(username).build(); identityService.createUser(user); final SUser user2 = identityService.getUserByUserName(username); getTransactionService().complete(); assertNotNull("can't find the user after adding it", user2); assertEquals("Does not retrieved the good user", username, user2.getUserName()); } @Test(expected = SIdentityException.class) public void testGetUserByUsernameNotExists() throws Exception { getTransactionService().begin(); final String username = "unexistingError"; identityService.getUserByUserName(username); getTransactionService().complete(); } @Test public void testGetRoleByName() throws Exception { getTransactionService().begin(); final String roleName = "myRole"; final SRole role = SRole.builder().name(roleName).build(); identityService.createRole(role, null, null); final SRole role2 = identityService.getRoleByName(roleName); getTransactionService().complete(); assertNotNull("can't find the role after adding it", role2); assertEquals("Does not retrieved the good role", roleName, role2.getName()); } @Test public void testGetProfileMetadataByName() throws Exception { getTransactionService().begin(); final String name = "MyProfileMetadata"; final SCustomUserInfoDefinition metadata = SCustomUserInfoDefinition.builder().name(name).build(); identityService.createCustomUserInfoDefinition(metadata); final SCustomUserInfoDefinition metadata2 = identityService.getCustomUserInfoDefinitionByName(name); assertNotNull("can't find the profile metadata after adding it", metadata2); assertEquals("Does not retrieved the good profile metadata", name, metadata2.getName()); getTransactionService().complete(); } /* * Getters that use objects Ids */ @Test public void testGetUser() throws Exception { getTransactionService().begin(); final SUser.SUserBuilder userBuilder = SUser.builder().userName("Seppo").password("kikoo"); final SUser seppo = identityService.createUser(userBuilder.build()); final SUser user2 = identityService.getUser(seppo.getId()); getTransactionService().complete(); assertNotNull("can't find the user after adding it", user2); assertEquals("Does not retrieved the good user", seppo.getId(), user2.getId()); } @Test(expected = SIdentityException.class) public void testGetUnexistingUser() throws Exception { getTransactionService().begin(); identityService.getUser(1254863); getTransactionService().complete(); } @Test public void testGetRole() throws Exception { getTransactionService().begin(); final long id = new Date().getTime(); final SRole testGetRole = SRole.builder().name("testGetRole").id(id).build(); identityService.createRole(testGetRole, null, null); final SRole role2 = identityService.getRole(id); getTransactionService().complete(); assertNotNull("can't find the role after adding it", role2); assertEquals("Does not retrieved the good role", id, role2.getId()); } @Test public void testGetGroup() throws Exception { getTransactionService().begin(); final long id = new Date().getTime(); final SGroup testGetGroup = SGroup.builder().name("testGetGroup").id(id).build(); identityService.createGroup(testGetGroup, null, null); getTransactionService().complete(); getTransactionService().begin(); final SGroup group2 = identityService.getGroup(id); getTransactionService().complete(); assertNotNull("can't find the group after adding it", group2); assertEquals("Does not retrieved the good group", id, group2.getId()); } @Test public void testGetGroupByPath() throws Exception { getTransactionService().begin(); final SGroup group = SGroup.builder().name("R&D").build(); identityService.createGroup(group, null, null); final SGroup subGroup = SGroup.builder().parentPath(group.getPath()).name("R&D").build(); identityService.createGroup(subGroup, null, null); getTransactionService().complete(); getTransactionService().begin(); SGroup actual = identityService.getGroupByPath("R&D"); assertEquals(group, actual); actual = identityService.getGroupByPath("/R&D"); assertEquals(group, actual); actual = identityService.getGroupByPath("/R&D/R&D"); assertEquals(subGroup, actual); getTransactionService().complete(); } @Test public void testGetProfileMetadata() throws Exception { getTransactionService().begin(); final long id = new Date().getTime(); final SCustomUserInfoDefinition testGetProfileMetadata = SCustomUserInfoDefinition.builder() .name("testGetProfileMetadata").id(id).build(); identityService.createCustomUserInfoDefinition(testGetProfileMetadata); final SCustomUserInfoDefinition metadata2 = identityService.getCustomUserInfoDefinition(id); getTransactionService().complete(); assertNotNull("can't find the metadata after adding it", metadata2); assertEquals("Does not retrieved the good metadata", id, metadata2.getId()); } @Test public void testGetUsers() throws Exception { getTransactionService().begin(); final SUser user1 = SUser.builder().userName("Akseli").password("kikoo").build(); final long id1 = identityService.createUser(user1).getId(); final SUser user2 = SUser.builder().userName("Anja").password("kikoo").build(); final long id2 = identityService.createUser(user2).getId(); final List retrievedUsers = identityService.getUsers(Arrays.asList(id1, id2)); getTransactionService().complete(); assertNotNull("can't find the users after adding them", retrievedUsers); assertEquals("bad number of retrieved users", 2, retrievedUsers.size()); assertTrue("does not contains user 1", retrievedUsers.get(0).getId() == id1 || retrievedUsers.get(1).getId() == id1); assertTrue("does not contains user 2", retrievedUsers.get(1).getId() == id2 || retrievedUsers.get(0).getId() == id2); } @Test public void testGetUsersFromNullListIds() throws Exception { final SUser eetu = SUser.builder().userName("Eetu").password("kikoo").build(); final SUser inkeri = SUser.builder().userName("Inkeri").password("kikoo").build(); getTransactionService().begin(); identityService.createUser(eetu); identityService.createUser(inkeri); final List retrievedUsers = identityService.getUsers(null); getTransactionService().complete(); assertEquals(0, retrievedUsers.size()); } @Test public void testGetUsersFromEmptyListIds() throws Exception { final SUser lauri = SUser.builder().userName("lauri").password("kikoo").build(); final SUser mika = SUser.builder().userName("mika").password("kikoo").build(); getTransactionService().begin(); identityService.createUser(lauri); identityService.createUser(mika); final List retrievedUsers = identityService.getUsers(Collections.emptyList()); getTransactionService().complete(); assertEquals(0, retrievedUsers.size()); } @Test public void testGetRoles() throws Exception { getTransactionService().begin(); final long id1 = new Date().getTime(); final SRole role1 = SRole.builder().name("testGetRoles1").id(id1).build(); identityService.createRole(role1, null, null); final long id2 = id1 + 1L; final SRole role2 = SRole.builder().name("testGetRoles2").id(id2).build(); identityService.createRole(role2, null, null); final List retrievedUsers = identityService.getRoles(Arrays.asList(new Long[] { id1, id2 })); getTransactionService().complete(); assertNotNull("can't find the roles after adding them", retrievedUsers); assertEquals("bad number of retrieved roles", 2, retrievedUsers.size()); assertTrue("does not contains role 1", retrievedUsers.get(0).getId() == id1 || retrievedUsers.get(1).getId() == id1); assertTrue("does not contains role 2", retrievedUsers.get(1).getId() == id2 || retrievedUsers.get(0).getId() == id2); } @Test public void testGetRolesFromNullListids() throws Exception { getTransactionService().begin(); final long id1 = new Date().getTime(); final SRole role1 = SRole.builder().name("testGetRoles1").id(id1).build(); identityService.createRole(role1, null, null); final long id2 = id1 + 1L; final SRole role2 = SRole.builder().name("testGetRoles2").id(id2).build(); identityService.createRole(role2, null, null); final List retrievedUsers = identityService.getRoles(null); getTransactionService().complete(); assertEquals(0, retrievedUsers.size()); } @Test public void testGetRolesFromEmptyListIds() throws Exception { getTransactionService().begin(); final long id1 = new Date().getTime(); final SRole role1 = SRole.builder().name("testGetRoles1").id(id1).build(); identityService.createRole(role1, null, null); final long id2 = id1 + 1L; final SRole role2 = SRole.builder().name("testGetRoles2").id(id2).build(); identityService.createRole(role2, null, null); final List retrievedUsers = identityService.getRoles(Collections.emptyList()); getTransactionService().complete(); assertEquals(0, retrievedUsers.size()); } @Test public void testGetGroups() throws Exception { getTransactionService().begin(); final long id1 = new Date().getTime(); final SGroup group1 = SGroup.builder().name("testGetGroups1").id(id1).build(); identityService.createGroup(group1, null, null); final long id2 = id1 + 1L; final SGroup group2 = SGroup.builder().name("testGetGroups2").id(id2).build(); identityService.createGroup(group2, null, null); final List retrievedGroups = identityService.getGroups(Arrays.asList(new Long[] { id1, id2 })); getTransactionService().complete(); assertNotNull("can't find the groups after adding them", retrievedGroups); assertEquals("bad number of retrieved groups", 2, retrievedGroups.size()); assertTrue("does not contains group 1", retrievedGroups.get(0).getId() == id1 || retrievedGroups.get(1).getId() == id1); assertTrue("does not contains group 2", retrievedGroups.get(1).getId() == id2 || retrievedGroups.get(0).getId() == id2); } @Test public void testGetGroupsFromNullListIds() throws Exception { getTransactionService().begin(); final long id1 = new Date().getTime(); final SGroup group1 = SGroup.builder().name("testGetGroups1").id(id1).build(); identityService.createGroup(group1, null, null); final long id2 = id1 + 1L; final SGroup group2 = SGroup.builder().name("testGetGroups2").id(id2).build(); identityService.createGroup(group2, null, null); final List retrievedGroups = identityService.getGroups(null); getTransactionService().complete(); assertEquals(0, retrievedGroups.size()); } @Test public void testGetGroupsFromEmptyListIds() throws Exception { getTransactionService().begin(); final long id1 = new Date().getTime(); final SGroup group1 = SGroup.builder().name("testGetGroups1").id(id1).build(); identityService.createGroup(group1, null, null); final long id2 = id1 + 1L; final SGroup group2 = SGroup.builder().name("testGetGroups2").id(id2).build(); identityService.createGroup(group2, null, null); final List retrievedGroups = identityService.getGroups(Collections.emptyList()); getTransactionService().complete(); assertEquals(0, retrievedGroups.size()); } @Test public void testGetRolesPaginated() throws Exception { getTransactionService().begin(); long id; SRole role; final long time = new Date().getTime(); for (int i = 0; i < 30; i++) { id = time + i; role = SRole.builder().name("testGetRolesPaginated" + i).id(id).build(); identityService.createRole(role, null, null); } List retrievedRoles = identityService.getRoles(5, 5); assertNotNull("can't find the roles after adding them", retrievedRoles); assertEquals("bad number of retrieved roles", 5, retrievedRoles.size()); retrievedRoles = identityService.getRoles(0, 20); getTransactionService().complete(); assertNotNull("can't find the roles after adding them", retrievedRoles); assertEquals("bad number of retrieved roles", 20, retrievedRoles.size()); } @Test public void testGetRolesOrderByName() throws Exception { getTransactionService().begin(); createRoles(10, "testGetRolesOrderByName_name", "testGetRolesOrderByName_label"); List retrievedRoles = identityService.getRoles(5, 5, SRole.NAME, OrderByType.DESC); assertNotNull("can't find the roles after adding them", retrievedRoles); assertEquals("bad number of retrieved roles", 5, retrievedRoles.size()); assertTrue("not in descending order", retrievedRoles.get(1).getName().compareTo(retrievedRoles.get(2).getName()) > 0); retrievedRoles = identityService.getRoles(5, 5, SRole.NAME, OrderByType.ASC); getTransactionService().complete(); assertNotNull("can't find the roles after adding them", retrievedRoles); assertEquals("bad number of retrieved roles", 5, retrievedRoles.size()); assertTrue( "not in asc order: first= " + retrievedRoles.get(0).getName() + " second = " + retrievedRoles.get(3).getName(), retrievedRoles.get(0) .getName().compareTo(retrievedRoles.get(3).getName()) < 0); } @Test public void testGetRolesOrderByLabel() throws Exception { getTransactionService().begin(); deleteUsers(); deleteRoles(); createRoles(10, "testGetRolesOrderByLabel_name", "testGetRolesOrderByLabel_label"); List retrievedRoles = identityService.getRoles(5, 5, SRole.DISPLAY_NAME, OrderByType.DESC); assertNotNull("can't find the roles after adding them", retrievedRoles); assertEquals("bad number of retrieved roles", 5, retrievedRoles.size()); assertTrue("not in descending order", retrievedRoles.get(1).getDisplayName().compareTo(retrievedRoles.get(2).getDisplayName()) > 0); retrievedRoles = identityService.getRoles(5, 5, SRole.DISPLAY_NAME, OrderByType.ASC); getTransactionService().complete(); assertNotNull("can't find the roles after adding them", retrievedRoles); assertEquals("bad number of retrieved roles", 5, retrievedRoles.size()); assertTrue("not in asc order: first= ", retrievedRoles.get(0).getDisplayName().compareTo(retrievedRoles.get(3).getDisplayName()) < 0); } private List createRoles(final int i, final String baseName, final String baseLabel) throws SIdentityException { final ArrayList results = new ArrayList<>(); for (int j = 0; j < i; j++) { final SRole role = SRole.builder().name(baseName + j).displayName(baseLabel + j).build(); identityService.createRole(role, null, null); results.add(role); } return results; } private void deleteRoles() throws SIdentityException { final List roles = identityService.getRoles(0, 5000); for (final SRole sRole : roles) { identityService.deleteRole(sRole); } } @Test public void testGetNumberOfRoles() throws Exception { getTransactionService().begin(); final long numberOfRoles = identityService.getNumberOfRoles(); long id; SRole role; final long time = new Date().getTime(); for (int i = 0; i < 5; i++) { id = time + i; role = SRole.builder().name("testGetNumberOfRoles" + i).id(id).build(); identityService.createRole(role, null, null); } assertEquals("bad count of roles", numberOfRoles + 5, identityService.getNumberOfRoles()); getTransactionService().complete(); } /* * Method that helps to retrieve groups */ @Test public void testGetGroupsPaginated() throws Exception { getTransactionService().begin(); long id; SGroup group; final long time = new Date().getTime(); for (int i = 0; i < 30; i++) { id = time + i; group = SGroup.builder().name("testGetGroupsPaginated" + i).id(id).build(); identityService.createGroup(group, null, null); } List retrievedGroups = identityService.getGroups(5, 5); assertNotNull("can't find the groups after adding them", retrievedGroups); assertEquals("bad number of retrieved groups", 5, retrievedGroups.size()); retrievedGroups = identityService.getGroups(0, 20); getTransactionService().complete(); assertNotNull("can't find the groups after adding them", retrievedGroups); assertEquals("bad number of retrieved groups", 20, retrievedGroups.size()); } @Test public void testGetGroupsOrderByName() throws Exception { getTransactionService().begin(); deleteGroups(); createGroups(10, "testGetGroupsOrderByName_name", "testGetGroupsOrderByName_label", null); getTransactionService().complete(); getTransactionService().begin(); List retrievedGroups = identityService.getGroups(5, 5, SGroup.NAME, OrderByType.DESC); assertNotNull("can't find the groups after adding them", retrievedGroups); assertEquals("bad number of retrieved roles", 5, retrievedGroups.size()); assertTrue("not in descending order", retrievedGroups.get(1).getName().compareTo(retrievedGroups.get(2).getName()) > 0); retrievedGroups = identityService.getGroups(5, 5, SGroup.NAME, OrderByType.ASC); getTransactionService().complete(); assertNotNull("can't find the groups after adding them", retrievedGroups); assertEquals("bad number of retrieved groups", 5, retrievedGroups.size()); assertTrue("not in descending order", retrievedGroups.get(0).getName().compareTo(retrievedGroups.get(3).getName()) < 0); } @Test public void testGetGroupsOrderByLabel() throws Exception { getTransactionService().begin(); deleteGroups(); createGroups(10, "testGetGroupsOrderByLabel_name", "testGetGroupsOrderByLabel_label", null); List retrievedGroups = identityService.getGroups(5, 5, SGroup.DISPLAY_NAME, OrderByType.DESC); assertNotNull("can't find the groups after adding them", retrievedGroups); assertEquals("bad number of retrieved roles", 5, retrievedGroups.size()); assertTrue("not in descending order", retrievedGroups.get(1).getName().compareTo(retrievedGroups.get(2).getName()) > 0); retrievedGroups = identityService.getGroups(5, 5, SGroup.DISPLAY_NAME, OrderByType.ASC); getTransactionService().complete(); assertNotNull("can't find the groups after adding them", retrievedGroups); assertEquals("bad number of retrieved groups", 5, retrievedGroups.size()); assertTrue("not in descending order", retrievedGroups.get(0).getDisplayName().compareTo(retrievedGroups.get(3).getDisplayName()) < 0); } private List createGroups(final int i, final String basename, final String baseLabel, final SGroup g) throws SIdentityException { final List groups = new ArrayList<>(); for (int j = 0; j < i; j++) { final SGroup.SGroupBuilder inst = SGroup.builder().name(basename + j).displayName(baseLabel + j); if (g != null) { inst.parentPath(g.getPath()); } final SGroup group = inst.build(); identityService.createGroup(group, null, null); groups.add(group); } return groups; } private void deleteGroups() throws SIdentityException { final List groups = identityService.getGroups(0, 5000); for (final SGroup sGroup : groups) { identityService.deleteGroup(sGroup); } } @Test public void testGetNumberOfGroups() throws Exception { getTransactionService().begin(); final long numberOfGroups = identityService.getNumberOfGroups(); long id; SGroup group; final long time = new Date().getTime(); for (int i = 0; i < 5; i++) { id = time + i; group = SGroup.builder().name("testGetNumberOfGroups" + i).id(id).build(); identityService.createGroup(group, null, null); } assertEquals("bad count of groups", numberOfGroups + 5, identityService.getNumberOfGroups()); getTransactionService().complete(); } @Test public void testGetGroupChildrenPaginated() throws Exception { getTransactionService().begin(); deleteGroups(); final List groups = createGroups(1, "testGetGroupChildrenPaginated_name", "testGetGroupChildrenPaginated_label", null); identityService.getGroup(groups.get(0).getId()); final SGroup parentGroup = groups.iterator().next(); createGroups(5, "testGetGroupChildrenPaginatedChildren_name", "testGetGroupChildrenPaginatedChildren_label", parentGroup); getTransactionService().complete(); getTransactionService().begin(); final List retrievedGroups = identityService.getGroupChildren(parentGroup.getId(), 0, 5); getTransactionService().complete(); assertNotNull("can't find the groups after adding them", retrievedGroups); assertEquals("bad number of retrieved groups", 5, retrievedGroups.size()); } @Test public void testGetGroupChildrenWithCriterion() throws Exception { getTransactionService().begin(); deleteGroups(); final List groups = createGroups(1, "testGetGroupChildrenWithCriterion_name", "testGetGroupChildrenWithCriterion_label", null); final SGroup parentGroup = groups.iterator().next(); createGroups(5, "testGetGroupChildrenWithCriterionChildren_name", "testGetGroupChildrenWithCriterionChildren_label", parentGroup); getTransactionService().complete(); getTransactionService().begin(); List retrievedGroups = identityService.getGroupChildren(parentGroup.getId(), 0, 3, SGroup.NAME, OrderByType.DESC); assertNotNull("can't find the groups after adding them", retrievedGroups); assertEquals("bad number of retrieved groups", 3, retrievedGroups.size()); assertTrue("not in descending order", retrievedGroups.get(1).getName().compareTo(retrievedGroups.get(2).getName()) > 0); retrievedGroups = identityService.getGroupChildren(parentGroup.getId(), 0, 3, SGroup.NAME, OrderByType.ASC); getTransactionService().complete(); assertNotNull("can't find the groups after adding them", retrievedGroups); assertEquals("bad number of retrieved groups", 3, retrievedGroups.size()); assertTrue("not in descending order", retrievedGroups.get(0).getName().compareTo(retrievedGroups.get(1).getName()) < 0); } @Test public void testGetNumberOfGroupChildren() throws Exception { getTransactionService().begin(); deleteGroups(); final List groups = createGroups(1, "testGetNumberOfGroupChildren_name", "testGetNumberOfGroupChildren_label", null); final SGroup parentGroup = groups.iterator().next(); createGroups(5, "testGetNumberOfGroupChildrenChildren_name", "testGetNumberOfGroupChildrenChildren_label", parentGroup); getTransactionService().complete(); getTransactionService().begin(); assertEquals("bad count of groups", 5, identityService.getNumberOfGroupChildren(parentGroup.getId())); getTransactionService().complete(); } /* * Method that helps to retrieve profile metadata */ @Test public void testGetProfileMetadataPaginated() throws Exception { getTransactionService().begin(); long id; SCustomUserInfoDefinition metadata; final long time = new Date().getTime(); for (int i = 0; i < 30; i++) { id = time + i; metadata = SCustomUserInfoDefinition.builder().name("testGetProfileMetadataPaginated" + i) .id(id).build(); identityService.createCustomUserInfoDefinition(metadata); } List retrievedMetadata = identityService.getCustomUserInfoDefinitions(5, 5); assertNotNull("can't find the groups after adding them", retrievedMetadata); assertEquals("bad number of retrieved groups", 5, retrievedMetadata.size()); retrievedMetadata = identityService.getCustomUserInfoDefinitions(0, 20); getTransactionService().complete(); assertNotNull("can't find the groups after adding them", retrievedMetadata); assertEquals("bad number of retrieved groups", 20, retrievedMetadata.size()); } @Test public void testGetNumberOfCustomUserInfoDefinition() throws Exception { getTransactionService().begin(); final long numberOfMetadata = identityService.getNumberOfCustomUserInfoDefinition(); long id; SCustomUserInfoDefinition info; final long time = new Date().getTime(); for (int i = 0; i < 30; i++) { id = time + 50L + i; info = SCustomUserInfoDefinition.builder().name("testGetNumberOfCustomUserInfoDefinition" + i) .id(id).build(); identityService.createCustomUserInfoDefinition(info); } assertEquals("bad count of custom user info definition", numberOfMetadata + 30, identityService.getNumberOfCustomUserInfoDefinition()); getTransactionService().complete(); } /* * Method that helps to retrieve users */ @Test public void testGetUsersWithRolePaginated() throws Exception { getTransactionService().begin(); final SRole role = SRole.builder().name("testGetUsersWithRole").build(); identityService.createRole(role, null, null); final List groups = createGroups(2, "testGetUsersWithRoleGroup", "testGetUsersWithRoleGroup", null); for (int i = 0; i < 10; i++) { final SUser.SUserBuilder userBuilder = SUser.builder().userName("user" + i).password("kikoo"); final SUser user = identityService.createUser(userBuilder.build()); final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId()) .groupId(groups.get(0).getId()).roleId(role.getId()) .build(); identityService.createUserMembership(userMembership1); } for (int i = 10; i < 20; i++) { final SUser.SUserBuilder userBuilder = SUser.builder().userName("user" + i).password("kikoo"); final SUser user = identityService.createUser(userBuilder.build()); final SUserMembership userMembership2 = SUserMembership.builder().userId(user.getId()) .groupId(groups.get(1).getId()).roleId(role.getId()) .build(); identityService.createUserMembership(userMembership2); } final List usersWithRole = identityService.getUsersWithRole(role.getId(), 10, 10); getTransactionService().complete(); assertEquals("not all user were retrieved", 10, usersWithRole.size()); } @Test public void testGetUsersWithRoleWithCriterion() throws Exception { getTransactionService().begin(); final SRole role = SRole.builder().name("testGetUsersWithRole").build(); identityService.createRole(role, null, null); final List groups = createGroups(2, "testGetUsersWithRoleGroup", "testGetUsersWithRoleGroup", null); SUser user; for (int i = 0; i < 10; i++) { user = SUser.builder().userName("user" + i).password("kikoo").build(); final SUser user2 = identityService.createUser(user); final SUserMembership userMembership1 = SUserMembership.builder().userId(user2.getId()) .groupId(groups.get(0).getId()).roleId(role.getId()) .build(); identityService.createUserMembership(userMembership1); } for (int i = 10; i < 20; i++) { user = SUser.builder().userName("user" + i).password("kikoo").build(); final SUser user2 = identityService.createUser(user); final SUserMembership userMembership2 = SUserMembership.builder().userId(user2.getId()) .groupId(groups.get(1).getId()).roleId(role.getId()) .build(); identityService.createUserMembership(userMembership2); } List usersWithRole = identityService.getUsersWithRole(role.getId(), 10, 10, SUser.USER_NAME, OrderByType.DESC); assertEquals("not all user were retrieved", 10, usersWithRole.size()); assertTrue("not in descending order", usersWithRole.get(1).getUserName().compareTo(usersWithRole.get(2).getUserName()) > 0); usersWithRole = identityService.getUsersWithRole(role.getId(), 10, 10, SUser.USER_NAME, OrderByType.ASC); getTransactionService().complete(); assertEquals("not all user were retrieved", 10, usersWithRole.size()); assertTrue("not in asc order", usersWithRole.get(1).getUserName().compareTo(usersWithRole.get(2).getUserName()) < 0); } @Test public void testGetActiveAndInactiveUsersWithRole() throws Exception { getTransactionService().begin(); final SRole role = SRole.builder().name("testGetActiveAndInactiveUsersWithRole").build(); identityService.createRole(role, null, null); final List groups = createGroups(2, "testGetUsersWithRoleGroup", "testGetUsersWithRoleGroup", null); SUser user; for (int i = 0; i < 10; i++) { user = SUser.builder().userName("inactive user" + i).password("kikoo").enabled(false).build(); final SUser user2 = identityService.createUser(user); final SUserMembership userMembership1 = SUserMembership.builder().userId(user2.getId()) .groupId(groups.get(0).getId()).roleId(role.getId()) .build(); identityService.createUserMembership(userMembership1); } for (int i = 10; i < 20; i++) { user = SUser.builder().userName("active user" + i).enabled(true).password("kikoo").build(); final SUser user2 = identityService.createUser(user); final SUserMembership userMembership2 = SUserMembership.builder().userId(user2.getId()) .groupId(groups.get(1).getId()).roleId(role.getId()) .build(); identityService.createUserMembership(userMembership2); } List activeUsersWithRole = identityService .getActiveUsersWithRole(role.getId(), 0, 20, SUser.USER_NAME, OrderByType.DESC); List inactiveUsersByRole = identityService .getInactiveUsersWithRole(role.getId(), 0, 20, SUser.USER_NAME, OrderByType.DESC); assertThat(inactiveUsersByRole.size()).isEqualTo(10); assertThat(activeUsersWithRole.size()).isEqualTo(10); for (int i = 0; i < 10; i++) { assertThat(inactiveUsersByRole.get(i).getUserName()).contains("inactive user"); assertThat(activeUsersWithRole.get(i).getUserName()).contains("active user"); } getTransactionService().complete(); } @Test public void testGetNumberOfUsersByRole() throws Exception { getTransactionService().begin(); final SRole role = SRole.builder().name("testGetNumberOfUsersByRole").build(); identityService.createRole(role, null, null); final long numberOfUsersByRole = identityService.getNumberOfUsersByRole(role.getId()); final SGroup group = SGroup.builder().name("testGetUsersByGroup").build(); identityService.createGroup(group, null, null); SUser user; for (int i = 0; i < 5; i++) { user = SUser.builder().userName("testGetNumberOfUsersByRole" + i).password("kikoo").build(); final SUser user2 = identityService.createUser(user); final SUserMembership userMembership = SUserMembership.builder().userId(user2.getId()) .groupId(group.getId()).roleId(role.getId()).build(); identityService.createUserMembership(userMembership); } assertEquals("not the good number of users by role", numberOfUsersByRole + 5, identityService.getNumberOfUsersByRole(role.getId())); getTransactionService().complete(); } @Test public void testGetUsersInGroupPaginated() throws Exception { getTransactionService().begin(); final SRole role = SRole.builder().name("testGetUsersByRole").build(); identityService.createRole(role, null, null); final List groups = createGroups(2, "testGetUsersByRoleGroup", "testGetUsersByRoleGroup", null); SUser user; for (int i = 0; i < 5; i++) { final SUser.SUserBuilder userBuilder = SUser.builder().userName("user" + i).password("kikoo"); user = identityService.createUser(userBuilder.build()); final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId()) .groupId(groups.get(0).getId()).roleId(role.getId()) .build(); identityService.createUserMembership(userMembership1); } for (int i = 5; i < 10; i++) { final SUser.SUserBuilder userBuilder = SUser.builder().userName("user" + i).password("kikoo"); user = identityService.createUser(userBuilder.build()); final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId()) .groupId(groups.get(1).getId()).roleId(role.getId()) .build(); identityService.createUserMembership(userMembership1); } final List usersByGroup = identityService.getUsersInGroup(groups.get(0).getId(), 1, 2, "id", null); getTransactionService().complete(); assertEquals("not the good number of user with the role", 2, usersByGroup.size()); } @Test public void testGetUsersInGroupwithCriterion() throws Exception { getTransactionService().begin(); final SRole role = SRole.builder().name("testGetUsersByRole").build(); identityService.createRole(role, null, null); final List groups = createGroups(2, "testGetUsersByRoleGroup", "testGetUsersByRoleGroup", null); SUser user; for (int i = 0; i < 5; i++) { final SUser.SUserBuilder userBuilder = SUser.builder().userName("user" + i).password("kikoo"); user = identityService.createUser(userBuilder.build()); final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId()) .groupId(groups.get(0).getId()).roleId(role.getId()) .build(); identityService.createUserMembership(userMembership1); } for (int i = 5; i < 10; i++) { final SUser.SUserBuilder userBuilder = SUser.builder().userName("user" + i).password("kikoo"); user = identityService.createUser(userBuilder.build()); final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId()) .groupId(groups.get(1).getId()).roleId(role.getId()) .build(); identityService.createUserMembership(userMembership1); } List usersByGroup = identityService.getUsersInGroup(groups.get(0).getId(), 1, 2, SUser.USER_NAME, OrderByType.DESC); assertEquals("not the good number of user with the role", 2, usersByGroup.size()); assertTrue("not in descending order", usersByGroup.get(0).getUserName().compareTo(usersByGroup.get(1).getUserName()) > 0); usersByGroup = identityService.getUsersInGroup(groups.get(0).getId(), 1, 2, SUser.USER_NAME, OrderByType.ASC); getTransactionService().complete(); assertEquals("not all user were retrieved", 2, usersByGroup.size()); assertTrue("not in asc order", usersByGroup.get(0).getUserName().compareTo(usersByGroup.get(1).getUserName()) < 0); } @Test public void testGetActiveAndInactiveUsersInGroup() throws Exception { getTransactionService().begin(); final SRole role = SRole.builder().name("testGetActiveAndInactiveUsersByGroup").build(); identityService.createRole(role, null, null); final List groups = createGroups(2, "testGetActiveAndInactiveUsersByGroup", "testGetActiveAndInactiveUsersByGroup", null); SUser user; for (int i = 0; i < 5; i++) { final SUser.SUserBuilder userBuilder = SUser.builder().userName("inactive user" + i).password("kikoo") .enabled(false); user = identityService.createUser(userBuilder.build()); final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId()) .groupId(groups.get(0).getId()).roleId(role.getId()) .build(); identityService.createUserMembership(userMembership1); } for (int i = 5; i < 10; i++) { final SUser.SUserBuilder userBuilder = SUser.builder().userName("active user " + i).password("kikoo") .enabled(true); user = identityService.createUser(userBuilder.build()); final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId()) .groupId(groups.get(1).getId()).roleId(role.getId()) .build(); identityService.createUserMembership(userMembership1); } for (int i = 10; i < 15; i++) { final SUser.SUserBuilder userBuilder = SUser.builder().userName("active user " + i).password("kikoo") .enabled(true); user = identityService.createUser(userBuilder.build()); final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId()) .groupId(groups.get(0).getId()).roleId(role.getId()) .build(); identityService.createUserMembership(userMembership1); } final List activeUsersByGroup1 = identityService.getActiveUsersInGroup(groups.get(0).getId(), 0, 2, null, null); final List inactiveUsersByGroup1 = identityService.getInactiveUsersInGroup(groups.get(0).getId(), 0, 8, null, null); assertThat(activeUsersByGroup1.size()).isEqualTo(2); assertThat(inactiveUsersByGroup1.size()).isEqualTo(5); final List activeUsersByGroup2 = identityService.getActiveUsersInGroup(groups.get(1).getId(), 0, 3, null, null); final List inactiveUsersByGroup2 = identityService.getInactiveUsersInGroup(groups.get(1).getId(), 0, 2, null, null); assertThat(activeUsersByGroup2.size()).isEqualTo(3); assertThat(inactiveUsersByGroup2.size()).isEqualTo(0); final List activeUsersByGroup3 = identityService.getActiveUsersInGroup(groups.get(0).getId(), 0, 500, null, null); final List inactiveUsersByGroup3 = identityService.getInactiveUsersInGroup(groups.get(0).getId(), 0, 1000, null, null); assertThat(activeUsersByGroup3.size()).isEqualTo(5); assertThat(inactiveUsersByGroup3.size()).isEqualTo(5); for (int i = 0; i < 5; i++) { assertThat(activeUsersByGroup3.get(i).getUserName()).contains("active user"); assertThat(inactiveUsersByGroup3.get(i).getUserName()).contains("inactive user"); } getTransactionService().complete(); } @Test public void testGetNumberOfUsersByGroup() throws Exception { getTransactionService().begin(); final SRole role = SRole.builder().name("testGetUsersByRole").build(); identityService.createRole(role, null, null); final List groups = createGroups(2, "testGetUsersByRoleGroup", "testGetUsersByRoleGroup", null); SUser user; for (int i = 0; i < 5; i++) { final SUser.SUserBuilder userBuilder = SUser.builder().userName("user" + i).password("kikoo"); user = identityService.createUser(userBuilder.build()); final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId()) .groupId(groups.get(0).getId()).roleId(role.getId()) .build(); identityService.createUserMembership(userMembership1); } for (int i = 5; i < 10; i++) { final SUser.SUserBuilder userBuilder = SUser.builder().userName("user" + i).password("kikoo"); user = identityService.createUser(userBuilder.build()); final SUserMembership userMembership1 = SUserMembership.builder().userId(user.getId()) .groupId(groups.get(1).getId()).roleId(role.getId()) .build(); identityService.createUserMembership(userMembership1); } final long id = groups.get(0).getId(); assertEquals("not the good number of users by group", 5, identityService.getNumberOfUsersByGroup(id)); getTransactionService().complete(); } @Test public void testGetNumberOfUsersByMembership() throws Exception { getTransactionService().begin(); final SGroup group = createGroups(1, "testGetUserByMembership", "testGetUserByMembership", null).iterator() .next(); final SRole role = createRoles(1, "testGetUserByMembership", "testGetUserByMembership").iterator().next(); final List users = createUsers(5, "getMembershipUsers"); for (final SUser sUser : users) { final SUserMembership userMembership = SUserMembership.builder().userId(sUser.getId()) .groupId(group.getId()).roleId(role.getId()).build(); identityService.createUserMembership(userMembership); } assertEquals("not the good number of user by membership", 5, identityService.getNumberOfUsersByMembership(group.getId(), role.getId())); getTransactionService().complete(); } @Test public void testGetUsersPaginated() throws Exception { getTransactionService().begin(); createUsers(20, "testGetUsersPaginated"); List users = identityService.getUsers(5, 10); final SUser user1 = users.get(0); assertEquals("returned list have not the correct size", 10, users.size()); users = identityService.getUsers(6, 10); final SUser user2 = users.get(0); getTransactionService().complete(); assertEquals("returned list have not the correct size", 10, users.size()); assertNotSame("from index not working", user1.getId(), user2.getId()); } @Test public void testGetUsersOrderByUserName() throws Exception { getTransactionService().begin(); createUsers(10, "testGetUsersOrderByUserName"); List users = identityService.getUsers(5, 10, SUser.USER_NAME, OrderByType.DESC); assertTrue("not in desc order", users.get(0).getUserName().compareTo(users.get(users.size() - 1).getUserName()) > 0); users = identityService.getUsers(5, 10, SUser.USER_NAME, OrderByType.ASC); getTransactionService().complete(); assertTrue("not in asc order", users.get(0).getUserName().compareTo(users.get(users.size() - 1).getUserName()) < 0); } @Test public void testGetUsersOrderByFirstName() throws Exception { getTransactionService().begin(); deleteUsers(); createUsers(10, "testGetUsersOrderByFirstName"); List users = identityService.getUsers(5, 10, SUser.FIRST_NAME, OrderByType.DESC); assertTrue("not in desc order", users.get(0).getFirstName().compareTo(users.get(users.size() - 1).getFirstName()) > 0); users = identityService.getUsers(5, 10, SUser.FIRST_NAME, OrderByType.ASC); getTransactionService().complete(); assertTrue("not in asc order", users.get(0).getFirstName().compareTo(users.get(users.size() - 1).getFirstName()) < 0); } @Test public void testGetUsersOrderByLastName() throws Exception { getTransactionService().begin(); deleteUsers(); createUsers(10, "testGetUsersOrderByLastName"); List users = identityService.getUsers(5, 10, SUser.LAST_NAME, OrderByType.DESC); assertTrue("not in desc order", users.get(0).getLastName().compareTo(users.get(users.size() - 1).getLastName()) > 0); users = identityService.getUsers(5, 10, SUser.LAST_NAME, OrderByType.ASC); getTransactionService().complete(); assertTrue("not in asc order", users.get(0).getLastName().compareTo(users.get(users.size() - 1).getLastName()) < 0); } private List createUsers(final int i, final String baseUsername) throws SIdentityException { final List ids = new ArrayList<>(); for (int j = 0; j < i; j++) { final SUser user = SUser.builder().userName(baseUsername + j).firstName("firstName" + j) .lastName("lastName" + j).password("password" + j).build(); ids.add(identityService.createUser(user)); } return ids; } private void deleteUsers() throws SIdentityException { final List users = identityService.getUsers(0, 5000); for (final SUser sUser : users) { identityService.deleteUser(sUser); } } @Test public void testGetNumberOfUsers() throws Exception { getTransactionService().begin(); final long numberOfUsers = identityService.getNumberOfUsers(); final SUser user = SUser.builder().userName("testGetNumberOfUsers").password("kikoo").build(); identityService.createUser(user); assertEquals(numberOfUsers + 1, identityService.getNumberOfUsers()); getTransactionService().complete(); } @Test public void testGetUsersWithManager() throws Exception { getTransactionService().begin(); createUsers(11, "testGetUsersWithManager"); final SUser manager = identityService.getUsers(0, 1).get(0); final List users = identityService.getUsers(1, 10); for (final SUser user : users) { final EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SUserUpdateBuilderFactory.class) .createNewInstance() .updateManagerUserId(manager.getId()).done(); identityService.updateUser(user, changeDescriptor); } final long id = manager.getId(); final List usersWithManager = identityService.getUsersWithManager(id, 0, 20, null, null); getTransactionService().complete(); assertEquals("did not retrieved all user having the manager", 10, usersWithManager.size()); for (final SUser sUser : usersWithManager) { assertEquals("One of the user have not the good manager", manager.getId(), sUser.getManagerUserId()); } } @Test public void testGetActiveAndInactiveUsersWithManager() throws Exception { getTransactionService().begin(); createUsers(31, "testGetActiveUsersWithManager"); final SUser manager = identityService.getUsers(0, 1).get(0); final List activeUsers = identityService.getUsers(0, 15); final List inactiveUsers = identityService.getUsers(15, 5); for (final SUser user : activeUsers) { final EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SUserUpdateBuilderFactory.class) .createNewInstance() .updateManagerUserId(manager.getId()).updateEnabled(true).done(); identityService.updateUser(user, changeDescriptor); } for (final SUser user : inactiveUsers) { final EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SUserUpdateBuilderFactory.class) .createNewInstance() .updateManagerUserId(manager.getId()).updateEnabled(false).done(); identityService.updateUser(user, changeDescriptor); } final long id = manager.getId(); final List activeUsersWithManager = identityService.getActiveUsersWithManager(id, 0, 20, null, null); final List inactiveUsersWithManager = identityService.getInactiveUsersWithManager(id, 0, 20, null, null); getTransactionService().complete(); assertThat(activeUsersWithManager.size()).isEqualTo(15); assertThat(inactiveUsersWithManager.size()).isEqualTo(5); for (final SUser sUser : activeUsersWithManager) { assertThat(sUser.getManagerUserId()).isEqualTo(manager.getId()); } for (final SUser sUser : inactiveUsersWithManager) { assertThat(sUser.getManagerUserId()).isEqualTo(manager.getId()); } } @Test // FIXME change name public void testUpdateUserDoesNotChangeManagerId() throws Exception { getTransactionService().begin(); createUsers(3, "testGetUpdateUserDoesNotChangeanagerId"); final List users = identityService.getUsers(0, 3); assertEquals(3, users.size()); final SUser manager = users.get(0); SUser user = users.get(1); final SUser newManager = users.get(2); EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SUserUpdateBuilderFactory.class) .createNewInstance().updateManagerUserId(manager.getId()) .done(); identityService.updateUser(user, changeDescriptor); final long id = manager.getId(); final List usersWithManager = identityService.getUsersWithManager(id, 0, 20, null, null); assertEquals("did not retrieved all user having the manager", 1, usersWithManager.size()); for (final SUser sUser : usersWithManager) { assertEquals("One of the user have not the good manager", manager.getId(), sUser.getManagerUserId()); } changeDescriptor = BuilderFactory.get(SUserUpdateBuilderFactory.class).createNewInstance() .updateFirstName("kevin").done(); identityService.updateUser(user, changeDescriptor); user = identityService.getUser(user.getId()); assertEquals("kevin", user.getFirstName()); assertEquals(manager.getId(), user.getManagerUserId()); changeDescriptor = BuilderFactory.get(SUserUpdateBuilderFactory.class).createNewInstance() .updateManagerUserId(newManager.getId()).done(); identityService.updateUser(user, changeDescriptor); user = identityService.getUser(user.getId()); assertEquals(newManager.getId(), user.getManagerUserId()); getTransactionService().complete(); } @Test public void testUpdateUser() throws Exception { getTransactionService().begin(); SUser.SUserBuilder userBuilder = null; userBuilder = SUser.builder().userName("testUpdateUser").password("kikoo") .firstName("Update").lastName("User"); final SUser user = identityService.createUser(userBuilder.build()); final String password = user.getPassword(); final SContactInfo contactInfo = SContactInfo.builder().userId(user.getId()).personal(true).address("Somewhere") .building("AA11") .city("Taiwan").build(); identityService.createUserContactInfo(contactInfo); final EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SUserUpdateBuilderFactory.class) .createNewInstance() .updateUserName("testUpdateUser2").updatePassword("lol") .updateFirstName("updated").updateLastName("user2").updateEnabled(true).done(); final String newAddress = "SomeWhereElse"; final String newCity = "Ouarzazate"; final String newBuilding = "BB22"; final String country = "Marrocco"; final String email = "other@fifi.org"; final String faxNumber = "99999999"; final String mobileNumber = "77777777"; final String phoneNumber = "555555"; final String room = "Room2"; final String state = "State2"; final String website = "website2"; final String zipCode = "zipCode2"; final EntityUpdateDescriptor updateContactInfo = BuilderFactory.get(SContactInfoUpdateBuilderFactory.class) .createNewInstance() .updateAddress(newAddress).updateCity(newCity) .updateBuilding(newBuilding).updateCountry(country).updateEmail(email).updateFaxNumber(faxNumber) .updateMobileNumber(mobileNumber) .updatePhoneNumber(phoneNumber).updateRoom(room).updateState(state).updateWebsite(website) .updateZipCode(zipCode).done(); identityService.updateUser(user, changeDescriptor); identityService.updateUserContactInfo(contactInfo, updateContactInfo); final SUser user2 = identityService.getUser(user.getId()); final SContactInfo contactInfo2 = identityService.getUserContactInfo(user2.getId(), true); getTransactionService().complete(); assertEquals("user was not updated", user, user2); assertEquals("testUpdateUser2", user2.getUserName()); assertNotSame(password, user2.getPassword()); // FIXME replace password by user.getPassword() assertEquals("updated", user2.getFirstName()); assertEquals("user2", user2.getLastName()); assertEquals(user2.getId(), (long) contactInfo2.getUserId()); assertEquals(newAddress, contactInfo2.getAddress()); assertEquals(newBuilding, contactInfo2.getBuilding()); assertEquals(newCity, contactInfo2.getCity()); assertEquals(country, contactInfo2.getCountry()); assertEquals(email, contactInfo2.getEmail()); assertEquals(faxNumber, contactInfo2.getFaxNumber()); assertEquals(mobileNumber, contactInfo2.getMobileNumber()); assertEquals(phoneNumber, contactInfo2.getPhoneNumber()); assertEquals(room, contactInfo2.getRoom()); assertEquals(state, contactInfo2.getState()); assertEquals(website, contactInfo2.getWebsite()); assertEquals(zipCode, contactInfo2.getZipCode()); } @Test public void testAddProfileMetadata() throws Exception { getTransactionService().begin(); final long metadataId = new Date().getTime(); final SCustomUserInfoDefinition metadata = SCustomUserInfoDefinition.builder().id(metadataId) .name("testAddProfileMetadata").build(); identityService.createCustomUserInfoDefinition(metadata); final SCustomUserInfoDefinition metadata2 = identityService.getCustomUserInfoDefinition(metadataId); getTransactionService().complete(); assertNotNull("can't retrieve the metadata", metadata2); assertEquals("retrieved not the good metadata", metadata.getId(), metadata2.getId()); } @Test public void testUpdateProfileMetadata() throws Exception { getTransactionService().begin(); final SCustomUserInfoDefinition metadata = identityService.getCustomUserInfoDefinitions(0, 1).get(0); final long metadataId = metadata.getId(); final String newName = "theNewName"; final EntityUpdateDescriptor changeDescriptor = BuilderFactory .get(SCustomUserInfoDefinitionUpdateBuilderFactory.class).createNewInstance() .updateName(newName).done(); identityService.updateCustomUserInfoDefinition(metadata, changeDescriptor); final SCustomUserInfoDefinition metadata2 = identityService.getCustomUserInfoDefinition(metadataId); getTransactionService().complete(); assertNotNull("can't retrieve the metadata", metadata2); assertEquals("retrieved not the good metadata", metadata.getId(), metadata2.getId()); assertEquals("metadata not updated", newName, metadata2.getName()); } @Test public void testAddRole() throws Exception { getTransactionService().begin(); final long id = new Date().getTime(); final SRole role = SRole.builder().id(id).name("testAddRole").build(); identityService.createRole(role, null, null); final SRole role2 = identityService.getRole(id); getTransactionService().complete(); assertNotNull("can't find the added role", role2); assertEquals("not the good role was added", role.getName(), role2.getName()); } @Test public void testUpdateRole() throws Exception { getTransactionService().begin(); final SRole role = createRoles(1, "firstName", "firstLabel").get(0); final long id = role.getId(); final String newName = "newRoleName"; final EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SRoleUpdateBuilderFactory.class) .createNewInstance().updateName(newName).done(); identityService.updateRole(role, changeDescriptor, null); final SRole role2 = identityService.getRole(id); getTransactionService().complete(); assertNotNull("can't find the updated role", role2); assertEquals("not udpated", newName, role2.getName()); } @Test public void testAddGroup() throws Exception { getTransactionService().begin(); final long id = new Date().getTime(); final SGroup group = SGroup.builder().id(id).name("testAddGroup").build(); identityService.createGroup(group, null, null); final SGroup group2 = identityService.getGroup(id); getTransactionService().complete(); assertNotNull("can't find the added group", group2); assertEquals("not the good group was added", group.getName(), group2.getName()); } @Test public void testUpdateGroup() throws Exception { getTransactionService().begin(); final SGroup group = createGroups(1, "firstName", "firstLabel", null).get(0); final long id = group.getId(); final String newName = "newGroupName"; final EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SGroupUpdateBuilderFactory.class) .createNewInstance().updateName(newName).done(); identityService.updateGroup(group, changeDescriptor, null); final SGroup group2 = identityService.getGroup(id); getTransactionService().complete(); assertNotNull("can't find the updated group", group2); assertEquals("not udpated", newName, group2.getName()); } /* * Methods that delete objects from the identityService module */ @Test(expected = SIdentityException.class) public void testDeleteUser() throws Exception { getTransactionService().begin(); final SUser user = createUsers(1, "testDeleteUser").get(0); final long id = user.getId(); identityService.deleteUser(user); assertNull("the user was not deleted", identityService.getUser(id)); getTransactionService().complete(); } @Test(expected = SIdentityException.class) public void testDeleteProfileMetadata() throws Exception { getTransactionService().begin(); final SCustomUserInfoDefinition metadataDefinition = SCustomUserInfoDefinition.builder() .name("kikooMetadata").build(); identityService.createCustomUserInfoDefinition(metadataDefinition); final long id = metadataDefinition.getId(); identityService.deleteCustomUserInfoDefinition(metadataDefinition); assertNull("the profile metadata was not deleted", identityService.getCustomUserInfoDefinition(id)); getTransactionService().complete(); } @Test(expected = SIdentityException.class) public void testDeleteRole() throws Exception { getTransactionService().begin(); final SRole role = createRoles(1, "testDeleteRole", "testDeleteRole").get(0); final long id = role.getId(); identityService.deleteRole(role); assertNull("the role was not deleted", identityService.getRole(id)); getTransactionService().complete(); } @Test(expected = SIdentityException.class) public void testDeleteGroup() throws Exception { getTransactionService().begin(); final List groups = createGroups(1, "testDeleteGroup_name", "testDeleteGroup_label", null); getTransactionService().complete(); getTransactionService().begin(); final SGroup group = groups.iterator().next(); assertNotNull(group); final long id = group.getId(); identityService.deleteGroup(group); assertNull("the group was not deleted", identityService.getGroup(id)); getTransactionService().complete(); } /* * Methods to add/remove/set memberships to user */ @Test public void testAddMembershipToUser() throws Exception { getTransactionService().begin(); final List groups = createGroups(1, "testAddMembershipToUser_name", "testAddMembershipToUser_label", null); createRoles(1, "testAddMembershipToUser_name", "testAddMembershipToUser_label"); final SGroup group = groups.iterator().next(); final SRole role = identityService.getRoleByName("testAddMembershipToUser_name0"); createUsers(1, "testAddMembershipToUser"); SUser user = identityService.getUserByUserName("testAddMembershipToUser0"); assertNotNull(user); getTransactionService().complete(); getTransactionService().begin(); user = identityService.getUserByUserName("testAddMembershipToUser0"); final int size = identityService .getUserMembershipsOfUser(user.getId(), 0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS).size(); final SUserMembership userMembership = SUserMembership.builder().userId(user.getId()).groupId(group.getId()) .roleId(role.getId()).build(); identityService.createUserMembership(userMembership); getTransactionService().complete(); getTransactionService().begin(); final SUser user2 = identityService.getUser(user.getId()); assertEquals("membership not added", size + 1, identityService.getUserMembershipsOfUser(user2.getId(), 0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS) .size()); getTransactionService().complete(); } @Test public void testRemoveMembershipFromUser() throws Exception { getTransactionService().begin(); deleteGroups(); final List groups = createGroups(1, "testRemoveMembershipFromUser_name", "testRemoveMembershipFromUser_label", null); createRoles(1, "testRemoveMembershipFromUser_name", "testRemoveMembershipFromUser_label"); final SGroup group = groups.iterator().next(); final SRole role = identityService.getRoleByName("testRemoveMembershipFromUser_name0"); createUsers(1, "testRemoveMembershipFromUser"); SUser user = identityService.getUserByUserName("testRemoveMembershipFromUser0"); assertNotNull(user); getTransactionService().complete(); getTransactionService().begin(); user = identityService.getUserByUserName("testRemoveMembershipFromUser0"); final SUserMembership userMembership = SUserMembership.builder().userId(user.getId()).groupId(group.getId()) .roleId(role.getId()).build(); identityService.createUserMembership(userMembership); getTransactionService().complete(); getTransactionService().begin(); user = identityService.getUserByUserName("testRemoveMembershipFromUser0"); final int size = identityService .getUserMembershipsOfUser(user.getId(), 0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS).size(); assertTrue("no membership on user", size >= 1); final SUserMembership userMembership2 = identityService.getUserMembership(user.getId(), group.getId(), role.getId()); identityService.deleteUserMembership(userMembership2); final SUser user2 = identityService.getUser(user.getId()); assertEquals("no membership on user", size - 1, identityService.getUserMembershipsOfUser(user2.getId(), 0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS) .size()); getTransactionService().complete(); } @Test public void searchUsersWithWildcards() throws Exception { getTransactionService().begin(); final SUser user1 = identityService.createUser(SUser.builder().userName("user1") .firstName("firstname1") .lastName("lastname1").password("lkh").build()); final SUser user2 = identityService.createUser(SUser.builder().userName("user2") .firstName("firstname2") .lastName("lastname2").password("mlbxcvjmsdkljf").build()); getTransactionService().complete(); final Map, Set> userAllFields = new HashMap<>(); final Set fields = new HashSet<>(4); fields.add("userName"); fields.add("firstName"); fields.add("lastName"); fields.add("jobTitle"); userAllFields.put(SUser.class, fields); final QueryOptions queryOptions = new QueryOptions(0, 10, Arrays.asList(new OrderByOption(SUser.class, "userName", OrderByType.ASC)), new ArrayList<>(0), new SearchFields(Arrays.asList("#"), userAllFields)); getTransactionService().begin(); final List result = identityService.searchUsers(queryOptions); assertEquals(0, result.size()); getTransactionService().complete(); // clean-up getTransactionService().begin(); identityService.deleteUser(user1); identityService.deleteUser(user2); getTransactionService().complete(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/job/AddJobCommand.java ================================================ /** * 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.job; import java.io.Serializable; import java.util.Date; import java.util.Map; import org.bonitasoft.engine.command.RuntimeCommand; import org.bonitasoft.engine.command.SCommandExecutionException; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; import org.bonitasoft.engine.scheduler.model.SJobDescriptor; import org.bonitasoft.engine.scheduler.trigger.OneShotTrigger; import org.bonitasoft.engine.scheduler.trigger.Trigger; import org.bonitasoft.engine.service.ServiceAccessor; public class AddJobCommand extends RuntimeCommand { @Override public Serializable execute(final Map parameters, final ServiceAccessor serviceAccessor) throws SCommandExecutionException { final SchedulerService schedulerService = serviceAccessor.getSchedulerService(); final Trigger trigger = new OneShotTrigger("OneShot", new Date()); final SJobDescriptor jobDescriptor = SJobDescriptor.builder() .jobClassName(ThrowsExceptionJob.class.getName()) .jobName("ThrowsExceptionJob") .description("Throw an exception when 'throwException'=true") .build(); try { schedulerService.schedule(jobDescriptor, trigger); return null; } catch (final SSchedulerException sse) { throw new SCommandExecutionException(sse); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/job/JobExecutionIT.java ================================================ /** * 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.job; import static java.util.Collections.*; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.awaitility.Durations.ONE_MINUTE; import static org.awaitility.Durations.ONE_SECOND; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import org.bonitasoft.engine.connectors.VariableStorage; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.scheduler.JobService; import org.bonitasoft.engine.scheduler.model.SJobDescriptor; import org.bonitasoft.engine.scheduler.model.SJobLog; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.test.CommonAPILocalIT; import org.bonitasoft.engine.test.WaitUntil; import org.bonitasoft.engine.transaction.UserTransactionService; import org.junit.After; import org.junit.Before; import org.junit.Test; // because of waituntil but its the only class where we use failed jobs... so i don't want to add a handler and so on // only for jobs public class JobExecutionIT extends CommonAPILocalIT { private static final String THROWS_EXCEPTION_JOB = "ThrowsExceptionJob"; @Before public void before() throws Exception { loginWithTechnicalUser(); createUser(USERNAME, PASSWORD); logout(); loginOnDefaultTenantWith(USERNAME, PASSWORD); setSessionInfo(getSession()); } @After public void after() throws Exception { VariableStorage.clearAll(); deleteUser(USERNAME); logout(); cleanSession(); } @Test public void getFailedJobs_should_return_zero_when_there_are_no_failed_jobs() { final List failedJobs = getProcessAPI().getFailedJobs(0, 100); assertThat(failedJobs).isEmpty(); } @Test public void retryAJob_should_execute_again_a_failed_job_and_clean_related_job_logs_and_jobDescriptor_if_not_recurrent() throws Exception { //given getCommandAPI().register("except", "Throws Exception when scheduling a job", AddJobCommand.class.getName()); try { getCommandAPI().execute("except", emptyMap()); final FailedJob failedJob = waitForFailedJob(); assertThat(failedJob.getJobName()).isEqualTo(THROWS_EXCEPTION_JOB); assertThat(failedJob.getNumberOfFailures()).isEqualTo(1); assertThat(failedJob.getDescription()).isEqualTo("Throw an exception when 'throwException'=true"); final List jobDescriptors = waitForJobDescriptorsToHaveSize(1); waitForJobLogsToHaveSize(jobDescriptors.get(0).getId(), 1); //when getProcessAPI().replayFailedJob(failedJob.getJobDescriptorId(), singletonMap("throwException", Boolean.FALSE)); //then assertJobWasExecutedWithSuccess(); waitForJobDescriptorsToHaveSize(0); waitForJobLogsToHaveSize(jobDescriptors.get(0).getId(), 0); // clean up: deleteJobLogsAndDescriptors(failedJob.getJobDescriptorId()); } finally { getCommandAPI().unregister("except"); } } private void waitForJobLogsToHaveSize(final long jobDescriptorId, final int nbOfExpectedJobLogs) { final QueryOptions options = new QueryOptions(0, 1, null, singletonList(new FilterOption(SJobLog.class, "jobDescriptorId", jobDescriptorId)), null); await().until(() -> { setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved final ServiceAccessor serviceAccessor = getServiceAccessor(); final UserTransactionService transactionService = serviceAccessor.getUserTransactionService(); final JobService jobService = serviceAccessor.getJobService(); return transactionService.executeInTransaction(() -> jobService.searchJobLogs(options)); }, hasSize(nbOfExpectedJobLogs)); } private List waitForJobDescriptorsToHaveSize(final int nbOfExpectedJobDescriptors) { final List filters = singletonList( new FilterOption(SJobDescriptor.class, "jobClassName", ThrowsExceptionJob.class.getName())); final QueryOptions queryOptions = new QueryOptions(0, 1, null, filters, null); return await().until(() -> { setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved final ServiceAccessor serviceAccessor = getServiceAccessor(); final UserTransactionService transactionService = serviceAccessor.getUserTransactionService(); final JobService jobService = serviceAccessor.getJobService(); return transactionService.executeInTransaction(() -> jobService.searchJobDescriptors(queryOptions)); }, hasSize(nbOfExpectedJobDescriptors)); } @Test 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()); try { getCommandAPI().execute("except", Map.of()); // Job can trigger in up to 'org.quartz.scheduler.idleWaitTime' milliseconds, so better wait long enough: FailedJob failedJob = await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until( () -> getProcessAPI().getFailedJobs(0, 100), hasSize(1)).get(0); //when getProcessAPI().replayFailedJob(failedJob.getJobDescriptorId()); //then // Job can trigger in up to 'org.quartz.scheduler.idleWaitTime' milliseconds, so better wait long enough: failedJob = await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND) .until(() -> getProcessAPI().getFailedJobs(0, 100), hasSize(1)).get(0); assertThat(failedJob.getJobName()).isEqualTo(THROWS_EXCEPTION_JOB); assertThat(failedJob.getDescription()).isEqualTo("Throw an exception when 'throwException'=true"); assertThat(failedJob.getLastMessage()).contains( "org.bonitasoft.engine.scheduler.exception.SJobExecutionException: This job throws an arbitrary exception"); assertThat(failedJob.getNumberOfFailures()).isEqualTo(1); deleteJobLogsAndDescriptors(failedJob.getJobDescriptorId()); } finally { getCommandAPI().unregister("except"); } } private void deleteJobLogsAndDescriptors(final long jobDescriptorId) throws Exception { setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved final ServiceAccessor serviceAccessor = getServiceAccessor(); final UserTransactionService transactionService = serviceAccessor.getUserTransactionService(); final JobService jobService = serviceAccessor.getJobService(); transactionService.executeInTransaction((Callable) () -> { jobService.deleteJobLogs(jobDescriptorId); jobService.deleteJobDescriptor(jobDescriptorId); return null; }); } private FailedJob waitForFailedJob() throws Exception { new WaitUntil(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT) { @Override protected boolean check() { return getFailedJob() != null; } }.waitUntil(); final FailedJob failedJob = getFailingJob(); assertThat(failedJob).isNotNull(); assertThat(failedJob.getNumberOfFailures()).isPositive(); return failedJob; } private void assertJobWasExecutedWithSuccess() throws Exception { new WaitUntil(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT) { @Override protected boolean check() { final FailedJob failedJob = getFailingJob(); return failedJob == null; } }.waitUntil(); final FailedJob failedJob = getFailingJob(); assertThat(failedJob).isNull(); } private FailedJob getFailedJob() { final FailedJob failedJob = getFailingJob(); if (failedJob != null && failedJob.getNumberOfFailures() > 0) { return failedJob; } return null; } private FailedJob getFailingJob() { final List failedJobs = getProcessAPI().getFailedJobs(0, 100); FailedJob searchJob = null; for (final FailedJob failedJob : failedJobs) { if (failedJob.getJobName().equals(JobExecutionIT.THROWS_EXCEPTION_JOB)) { searchJob = failedJob; } } return searchJob; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/job/ThrowsExceptionJob.java ================================================ /** * 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.job; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.scheduler.StatelessJob; import org.bonitasoft.engine.scheduler.exception.SJobExecutionException; /** * @author Elias Ricken de Medeiros */ public class ThrowsExceptionJob implements StatelessJob { private static final long serialVersionUID = 3528070481384646426L; private boolean throwException = true; @Override public String getDescription() { return "Job that throws a exception"; } @Override public void execute() throws SJobExecutionException { if (throwException) { throw new SJobExecutionException( "This job throws an arbitrary exception if parameter 'throwException' is provided."); } } @Override public String getName() { return "exception"; } @Override public void setAttributes(final Map attributes) { final Boolean result = (Boolean) attributes.get("throwException"); if (result != null) { throwException = result; } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/login/LoginAPIIT.java ================================================ /** * 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.login; import static org.junit.Assert.*; import java.io.IOException; import java.util.Date; import lombok.SneakyThrows; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.api.LoginAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserUpdater; import org.bonitasoft.engine.platform.LoginException; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.engine.session.SessionNotFoundException; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class LoginAPIIT extends CommonAPIIT { private static PlatformSession platformSession; @Before public void before() throws BonitaException, IOException { platformSession = loginOnPlatform(); } @After public void after() throws BonitaException { logoutOnPlatform(platformSession); } @Test(expected = SessionNotFoundException.class) public void testSessionNotFoundExceptionIsThrownAfterSessionDeletion() throws Exception { loginWithTechnicalUser(); // login to create a session final long sessionId = getSession().getId(); // delete the session created by the login deleteSession(sessionId); // will throw SessionNotFoundException getLoginAPI().logout(getSession()); } @SneakyThrows private void deleteSession(final long sessionId) { ServiceAccessorFactory.getInstance().createServiceAccessor() .getSessionService() .deleteSession(sessionId); } @Test(expected = LoginException.class) public void loginFailsWithNullUsername() throws BonitaException { final LoginAPI loginTenant = TenantAPIAccessor.getLoginAPI(); loginTenant.login(null, null); } @Test(expected = LoginException.class) public void loginFailsWithEmptyUsername() throws BonitaException { final LoginAPI loginTenant = TenantAPIAccessor.getLoginAPI(); loginTenant.login("", null); } @Test(expected = LoginException.class) public void loginFailsWithNullPassword() throws BonitaException { final LoginAPI loginTenant = TenantAPIAccessor.getLoginAPI(); loginTenant.login("matti", null); } @Test(expected = LoginException.class) public void loginFailsWithWrongPassword() throws BonitaException { loginWithTechnicalUser(); final String userName = "Truc"; createUser(userName, "goodPassword"); try { final LoginAPI loginTenant = TenantAPIAccessor.getLoginAPI(); loginTenant.login(userName, "WrongPassword"); fail("Should not be reached"); } finally { loginWithTechnicalUser(); getIdentityAPI().deleteUser(userName); } } @Test(expected = LoginException.class) public void loginFailsWithEmptyPassword() throws BonitaException { final LoginAPI loginTenant = TenantAPIAccessor.getLoginAPI(); loginTenant.login("matti", ""); } @Test public void userLoginDefaultTenant() throws BonitaException, InterruptedException { loginWithTechnicalUser(); final String userName = "matti"; final String password = "tervetuloa"; createUser(userName, password); final Date now = new Date(); Thread.sleep(300); loginOnDefaultTenantWith(userName, password); final User user = getIdentityAPI().getUserByUserName(userName); getIdentityAPI().deleteUser(userName); assertEquals(userName, user.getUserName()); assertTrue(now.before(user.getLastConnection())); logout(); } @Test public void loginOnDefaultTenantWithExistingUserAndCheckId() throws BonitaException { loginWithTechnicalUser(); final String userName = "corvinus"; final String password = "underworld"; final User user = createUser(userName, password); final LoginAPI loginTenant = TenantAPIAccessor.getLoginAPI(); final APISession login = loginTenant.login(userName, password); assertTrue("userId should be valuated", user.getId() != -1); assertEquals(user.getId(), login.getUserId()); getIdentityAPI().deleteUser(user.getId()); logout(); } @Test public void loginOnDefaultTenantWithNonTechnicalUser() throws BonitaException { loginWithTechnicalUser(); final User user = createUser("matti", "kieli"); logout(); loginOnDefaultTenantWith("matti", "kieli"); assertTrue("Should be logged in as a NON-Technical user", !getSession().isTechnicalUser()); logout(); loginWithTechnicalUser(); getIdentityAPI().deleteUser(user.getId()); logout(); } @Test public void loginOnDefaultTenantWithTechnicalUser() throws BonitaException { loginWithTechnicalUser(); assertTrue("Should be logged in as Technical user", getSession().isTechnicalUser()); logout(); } @Test(expected = LoginException.class) public void unableToLoginWhenTheUserIsDisable() throws BonitaException { loginWithTechnicalUser(); final String userName = "matti"; final String password = "bpm"; final User user = getIdentityAPI().createUser(userName, password); final UserUpdater updater = new UserUpdater(); updater.setEnabled(false); getIdentityAPI().updateUser(user.getId(), updater); final LoginAPI loginTenant = TenantAPIAccessor.getLoginAPI(); try { loginTenant.login(userName, password); fail("It is not possible to login when the user is disable."); } finally { getIdentityAPI().deleteUser(user.getId()); logout(); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/page/PageMappingServiceIT.java ================================================ /** * 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.page; import static org.assertj.core.api.Assertions.assertThat; import java.util.Collections; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.transaction.TransactionService; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; /** * @author Baptiste Mesta */ public class PageMappingServiceIT extends CommonBPMServicesTest { private static final long PAGE_ID = 12345L; private static final long NEW_PAGE_ID = 88854L; private TransactionService transactionService; @Rule public ExpectedException expectedException = ExpectedException.none(); private PageMappingService pageMappingService; @Before public void setUp() { transactionService = getTransactionService(); pageMappingService = getServiceAccessor().getPageMappingService(); } @After public void tearDown() { } @Test public void getByKey() throws Exception { transactionService.begin(); final SPageMapping internal = pageMappingService.create("getByKey/process1/12.0", PAGE_ID, Collections.emptyList()); final SPageMapping external = pageMappingService.create("getByKey/process2/12.0", "http://www.google.com", null, Collections.emptyList()); final SPageMapping externalWithAdapter = pageMappingService.create("getByKey/process3/12.0", "http://www.google.com", "theAdapter", Collections.emptyList()); transactionService.complete(); transactionService.begin(); final SPageMapping internalPersisted = pageMappingService.get("getByKey/process1/12.0"); final SPageMapping externalPersisted = pageMappingService.get("getByKey/process2/12.0"); final SPageMapping exterAdaPersisted = pageMappingService.get("getByKey/process3/12.0"); transactionService.complete(); assertThat(internalPersisted).isEqualTo(internal); assertThat(internalPersisted.getPageId()).isEqualTo(PAGE_ID); assertThat(internalPersisted.getUrl()).isNull(); assertThat(externalPersisted).isEqualTo(external); assertThat(externalPersisted.getUrl()).isEqualTo("http://www.google.com"); assertThat(exterAdaPersisted).isEqualTo(externalWithAdapter); assertThat(exterAdaPersisted.getUrl()).isEqualTo("http://www.google.com"); assertThat(exterAdaPersisted.getUrlAdapter()).isEqualTo("theAdapter"); } @Test public void delete() throws Exception { transactionService.begin(); final SPageMapping internal = pageMappingService.create("delete/process1/12.0", PAGE_ID, Collections.emptyList()); transactionService.complete(); transactionService.begin(); pageMappingService.delete(internal); transactionService.complete(); try { expectedException.expect(SObjectNotFoundException.class); transactionService.begin(); pageMappingService.get(internal.getKey()); } finally { transactionService.complete(); } } @Test public void update() throws Exception { final String key = "theKey/process1/12.0"; transactionService.begin(); pageMappingService.create(key, PAGE_ID, Collections.emptyList()); transactionService.complete(); transactionService.begin(); final SPageMapping pageMapping1 = pageMappingService.get(key); pageMappingService.update(pageMapping1, NEW_PAGE_ID); transactionService.complete(); transactionService.begin(); final SPageMapping updated = pageMappingService.get(key); transactionService.complete(); assertThat(updated).isEqualTo(pageMapping1); assertThat(updated.getPageId()).isEqualTo(NEW_PAGE_ID); assertThat(updated.getUrl()).isNull(); assertThat(updated.getUrlAdapter()).isNull(); Thread.sleep(10); transactionService.begin(); final SPageMapping reUpdated = pageMappingService.get(key); pageMappingService.update(reUpdated, "http://www.yahoo.com", "adapterURL"); transactionService.complete(); assertThat(reUpdated.getKey()).isEqualTo(key); assertThat(reUpdated.getPageId()).isNull(); assertThat(reUpdated.getUrl()).isEqualTo("http://www.yahoo.com"); assertThat(reUpdated.getUrlAdapter()).isEqualTo("adapterURL"); assertThat(reUpdated.getLastUpdateDate()).isGreaterThan(updated.getLastUpdateDate()); assertThat(reUpdated.getLastUpdatedBy()).isEqualTo(-1); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/persistence/PersistenceIT.java ================================================ /** * Copyright (C) 2016 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.persistence; import static org.junit.Assert.fail; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.transaction.STransactionCommitException; import org.bonitasoft.engine.transaction.STransactionCreationException; import org.bonitasoft.engine.transaction.STransactionRollbackException; import org.junit.Before; import org.junit.Test; public class PersistenceIT extends CommonBPMServicesTest { private ReadPersistenceService persistenceService; private Recorder recorder; @Before public void before() { persistenceService = getServiceAccessor().getReadPersistenceService(); recorder = getServiceAccessor().getRecorder(); } @Test public void testIfOneFailAllFail() throws Exception { Long numberOfUsersBefore = getNumberOfUsers(); getTransactionService().begin(); final SUser user = new SUser(); user.setUserName("SUserImpl1FN"); user.setPassword("SUserImpl1LN"); recorder.recordInsert(new InsertRecord(user), "USER"); try { persistenceService.selectOne(new SelectOneDescriptor("wrong query", null, SUser.class)); fail("Exception expected"); } catch (final Exception e) { getTransactionService().setRollbackOnly(); } finally { getTransactionService().complete(); } Assertions.assertThat(getNumberOfUsers()).isEqualTo(numberOfUsersBefore); } private Long getNumberOfUsers() throws STransactionCreationException, SBonitaReadException, STransactionCommitException, STransactionRollbackException { getTransactionService().begin(); final Long nbOfSUserImpl = persistenceService .selectOne(new SelectOneDescriptor<>("getNumberOfSUser", null, SUser.class, Long.class)); getTransactionService().complete(); return nbOfSUserImpl; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/platform/auth/PlatformAuthenticationServiceIT.java ================================================ /** * 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.platform.auth; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.platform.authentication.PlatformAuthenticationService; import org.bonitasoft.engine.platform.authentication.SInvalidPasswordException; import org.bonitasoft.engine.platform.authentication.SInvalidUserException; import org.junit.Before; import org.junit.Test; public class PlatformAuthenticationServiceIT extends CommonBPMServicesTest { private PlatformAuthenticationService platformAuthService; @Before public void setup() { platformAuthService = getServiceAccessor().getPlatformAuthenticationService(); } @Test public void testCheckValidUser() throws Exception { final String username = "platformAdmin"; final String password = "platform"; //getTransactionService().begin(); platformAuthService.checkUserCredentials(username, password); //getTransactionService().complete(); } @Test(expected = SInvalidPasswordException.class) public void testCheckUserWithWrongPassword() throws Exception { final String username = "platformAdmin"; //getTransactionService().begin(); platformAuthService.checkUserCredentials(username, "wrong"); //getTransactionService().complete(); } @Test(expected = SInvalidUserException.class) public void testCheckNonExistentUser() throws Exception { final String username = "anonyme"; final String password = "bpm"; //getTransactionService().begin(); platformAuthService.checkUserCredentials(username, password); //getTransactionService().complete(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/platform/command/PlatformCommandServiceIT.java ================================================ /** * 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.platform.command; import static org.junit.Assert.*; import java.util.List; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.platform.command.model.SPlatformCommand; import org.bonitasoft.engine.platform.command.model.SPlatformCommandUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.junit.Before; import org.junit.Test; /** * @author Zhang Bole */ public class PlatformCommandServiceIT extends CommonBPMServicesTest { private PlatformCommandService platformCommandService; @Before public void setup() { platformCommandService = getServiceAccessor().getPlatformCommandService(); } @Test(expected = SPlatformCommandAlreadyExistsException.class) public void testSPlatformCommandAlreadyExistsException() throws Exception { getTransactionService().begin(); final SPlatformCommand sPlatformCommand = new SPlatformCommand("createCommand", "this is a command", "command implementation"); platformCommandService.create(sPlatformCommand); try { platformCommandService.create(sPlatformCommand); } finally { platformCommandService.delete("createCommand"); getTransactionService().complete(); } } @Test(expected = SPlatformCommandNotFoundException.class) public void testSPlatformCommandNotFoundException() throws Exception { getTransactionService().begin(); try { platformCommandService.getPlatformCommand("a"); } finally { getTransactionService().complete(); } } @Test public void testCreatePlatformCommand() throws Exception { getTransactionService().begin(); final SPlatformCommand command1 = new SPlatformCommand("createCommand", "this is a command", "command implementation"); platformCommandService.create(command1); final SPlatformCommand command2 = platformCommandService.getPlatformCommand("createCommand"); assertNotNull("can't find the category after adding it", command2); assertEquals("can't retrieve the same category", command1.getName(), command2.getName()); assertEquals("can't retrieve the same category", command1.getId(), command2.getId()); platformCommandService.delete("createCommand"); getTransactionService().complete(); } @Test(expected = SPlatformCommandNotFoundException.class) public void testDeletePlatformCommand() throws Exception { getTransactionService().begin(); final SPlatformCommand sPlatformCommand = new SPlatformCommand("testCommandDelete", "this is a command", "command implementation"); platformCommandService.create(sPlatformCommand); platformCommandService.delete("testCommandDelete"); try { platformCommandService.getPlatformCommand("testCommandDelete"); } finally { getTransactionService().complete(); } } @Test public void testDeleteAll() throws Exception { getTransactionService().begin(); final SPlatformCommand command1 = new SPlatformCommand("createCommand1", "this is a command", "command implementation"); final SPlatformCommand command2 = new SPlatformCommand("createCommand2", "this is a command", "command implementation"); final SPlatformCommand command3 = new SPlatformCommand("createCommand3", "this is a command", "command implementation"); platformCommandService.create(command1); platformCommandService.create(command2); platformCommandService.create(command3); final QueryOptions queryOptions = new QueryOptions(0, 500, SPlatformCommand.class, "name", OrderByType.ASC); final List commands1 = platformCommandService.getPlatformCommands(queryOptions); assertEquals(3, commands1.size()); platformCommandService.deleteAll(); final List commands2 = platformCommandService.getPlatformCommands(queryOptions); assertTrue(commands2.isEmpty()); getTransactionService().complete(); } @Test public void testUpdatePlatformCommand() throws Exception { getTransactionService().begin(); final SPlatformCommand oldCommand = new SPlatformCommand("old", "this is an old command", "command implementation"); platformCommandService.create(oldCommand); assertEquals("old", oldCommand.getName()); assertEquals("this is an old command", oldCommand.getDescription()); final String commandName = "new"; final EntityUpdateDescriptor updateDescriptor = BuilderFactory.get(SPlatformCommandUpdateBuilderFactory.class) .createNewInstance().updateName(commandName) .updateDescription("this is a new command").done(); platformCommandService.update(oldCommand, updateDescriptor); final SPlatformCommand newCommand = platformCommandService.getPlatformCommand(commandName); assertEquals("new", newCommand.getName()); assertEquals("this is a new command", newCommand.getDescription()); platformCommandService.delete(commandName); getTransactionService().complete(); } @Test public void testGetPlatformCommandByName() throws Exception { getTransactionService().begin(); final SPlatformCommand sPlatformCommand = new SPlatformCommand("commandOne", "this is a command", "command implementation"); platformCommandService.create(sPlatformCommand); final SPlatformCommand command = platformCommandService.getPlatformCommand("commandOne"); assertEquals("commandOne", command.getName()); assertEquals("this is a command", command.getDescription()); platformCommandService.delete("commandOne"); getTransactionService().complete(); } @Test public void testGetPlatformCommandsWithCriterion() throws Exception { getTransactionService().begin(); final SPlatformCommand sPlatformCommand1 = new SPlatformCommand("commandB", "this is command1", "command implementation"); final SPlatformCommand sPlatformCommand2 = new SPlatformCommand("commandC", "this is command2", "command implementation"); final SPlatformCommand sPlatformCommand3 = new SPlatformCommand("commandA", "this is command3", "command implementation"); platformCommandService.create(sPlatformCommand1); platformCommandService.create(sPlatformCommand2); platformCommandService.create(sPlatformCommand3); final QueryOptions queryOptions = new QueryOptions(0, 5, SPlatformCommand.class, "name", OrderByType.ASC); final List commands = platformCommandService.getPlatformCommands(queryOptions); assertEquals(3, commands.size()); assertEquals("commandA", commands.get(0).getName()); assertEquals("commandB", commands.get(1).getName()); assertEquals("commandC", commands.get(2).getName()); platformCommandService.deleteAll(); getTransactionService().complete(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/platform/login/PlatformLoginServiceIT.java ================================================ /** * 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.platform.login; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.core.platform.login.PlatformLoginService; import org.bonitasoft.engine.core.platform.login.SInvalidPlatformCredentialsException; import org.bonitasoft.engine.platform.session.SSessionNotFoundException; import org.bonitasoft.engine.platform.session.model.SPlatformSession; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros * @author Emmanuel Duchastenier */ public class PlatformLoginServiceIT extends CommonBPMServicesTest { private PlatformLoginService platformLoginService; @Before public void setup() { platformLoginService = getServiceAccessor().getPlatformLoginService(); } @Test public void testLoginLogout() throws Exception { final String username = "platformAdmin"; final String password = "platform"; final SPlatformSession session = platformLoginService.login(username, password); assertNotNull(session); assertEquals(username, session.getUserName()); platformLoginService.logout(session.getId()); } @Test(expected = SInvalidPlatformCredentialsException.class) public void testLoginBadUser() throws Exception { final String username = "noAdmin"; final String password = "platform"; platformLoginService.login(username, password); } @Test(expected = SInvalidPlatformCredentialsException.class) public void testLoginBadPassword() throws Exception { final String username = "platformAdmin"; final String password = "wrong"; platformLoginService.login(username, password); } @Test(expected = SSessionNotFoundException.class) public void testLogoutWrongSession() throws Exception { platformLoginService.logout(System.currentTimeMillis()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/profile/ProfileServiceIT.java ================================================ /** * 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.profile; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SUserCreationException; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.profile.model.SProfileMember; import org.junit.Assert; import org.junit.Before; import org.junit.Test; /** * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros */ public class ProfileServiceIT extends CommonBPMServicesTest { private static ProfileService profileService; private static IdentityService identityService; @Before public void setup() { profileService = getServiceAccessor().getProfileService(); identityService = getServiceAccessor().getIdentityService(); } @Test(expected = SProfileNotFoundException.class) public void cannotGetAnUnknownProfile() throws SBonitaException { try { getTransactionService().begin(); profileService.getProfile(10); Assert.fail(); } finally { getTransactionService().complete(); } } @Test public void getProfile() throws SBonitaException { getTransactionService().begin(); final SProfile profile = SProfile.builder() .name("profile1") .isDefault(true).build(); final SProfile createdProfile = profileService.createProfile(profile); final SProfile gotProfile = profileService.getProfile(createdProfile.getId()); Assert.assertEquals(createdProfile, gotProfile); profileService.deleteProfile(gotProfile); try { profileService.getProfile(createdProfile.getId()); Assert.fail(); } catch (final SProfileNotFoundException spnfe) { getTransactionService().complete(); } } @Test public void getUserProfile() throws SBonitaException { getTransactionService().begin(); final SProfile profile = profileService.createProfile(SProfile.builder() .name("profile1") .isDefault(true) .build()); final List orderByOptions = getOrderByOptions(); final QueryOptions queryOptions = new QueryOptions(0, 10, orderByOptions, Collections.singletonList(new FilterOption(SProfileMember.class, "profileId", profile.getId())), null); List profileMembers = profileService.searchProfileMembers("ForUser", queryOptions); Assert.assertEquals(0, profileMembers.size()); SUser.SUserBuilder userBuilder = SUser.builder().userName("john").password("bpm") .firstName("John").lastName("Doe"); final SUser john = identityService.createUser(userBuilder.build()); userBuilder = SUser.builder().userName("jane").password("bpm").firstName("Jane") .lastName("Doe"); final SUser jane = identityService.createUser(userBuilder.build()); final SProfileMember johnProfileMember = profileService.addUserToProfile(profile.getId(), john.getId(), "John", "Doe", "john"); final SProfileMember janeProfileMember = profileService.addUserToProfile(profile.getId(), jane.getId(), "Jane", "Doe", "jane"); profileMembers = profileService.searchProfileMembers("ForUser", queryOptions); Assert.assertEquals(2, profileMembers.size()); profileService.deleteProfileMember(johnProfileMember); profileService.deleteProfileMember(janeProfileMember); profileMembers = profileService.searchProfileMembers("ForUser", queryOptions); Assert.assertEquals(0, profileMembers.size()); identityService.deleteUser(john); identityService.deleteUser(jane); profileService.deleteProfile(profile); getTransactionService().complete(); } private List getOrderByOptions() { final List orderByOptions = new ArrayList(1); orderByOptions.add(new OrderByOption(SUser.class, SUser.FIRST_NAME, OrderByType.ASC)); return orderByOptions; } private SUser createUser(final String username, final String password) throws SUserCreationException { final SUser.SUserBuilder userBuilder = SUser.builder().userName(username).password(password); return identityService.createUser(userBuilder.build()); } @Test public void getProfileOfUserFrom() throws SBonitaException { getTransactionService().begin(); final SProfile profile = profileService.createProfile(SProfile.builder() .name("profile1") .build()); final SUser john = createUser("john", "bpm"); final SUser jane = createUser("jane", "bpm"); List profilesOfUser = profileService.searchProfilesOfUser(john.getId(), 0, 10, "name", OrderByType.ASC); Assert.assertEquals(0, profilesOfUser.size()); final SProfileMember johnProfileMember = profileService.addUserToProfile(profile.getId(), john.getId(), "John", "Doe", "john"); final SProfileMember janeProfileMember = profileService.addUserToProfile(profile.getId(), jane.getId(), "Jane", "Doe", "jane"); profilesOfUser = profileService.searchProfilesOfUser(john.getId(), 0, 10, "name", OrderByType.ASC); Assert.assertEquals(1, profilesOfUser.size()); final QueryOptions countOptions = new QueryOptions(0, 10, null, Collections.singletonList(new FilterOption(SProfileMember.class, "profileId", profile .getId())), null); Assert.assertEquals(2, profileService.getNumberOfProfileMembers("ForUser", countOptions)); profileService.deleteProfileMember(johnProfileMember); profileService.deleteProfileMember(janeProfileMember); profilesOfUser = profileService.searchProfilesOfUser(john.getId(), 0, 10, "name", OrderByType.ASC); Assert.assertEquals(0, profilesOfUser.size()); identityService.deleteUser(john); identityService.deleteUser(jane); profileService.deleteProfile(profile); getTransactionService().complete(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/recorder/RecorderIT.java ================================================ /** * 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.recorder; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.archive.model.TestLogBuilderFactory; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.services.QueriableLoggerService; import org.bonitasoft.engine.test.util.TestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros, Yanyan Liu */ public class RecorderIT extends CommonBPMServicesTest { private static final String TEST_DELETE = "TEST_DELETED"; private static final String TEST_UPDATE = "TEST_UPDATED"; private static final String TEST_CREATED = "TEST_CREATED"; private final static int SLEEP_TIME = 200; private final static String SUSER_IMPL = "SUSER_IMPL"; protected ReadPersistenceService persistenceService; protected Recorder recorder; protected SchedulerService scheduler; protected EventService eventService; protected QueriableLoggerService loggerService; private TestLogBuilderFactory logModelBuilderFactory; @Before public void setUp() throws Exception { persistenceService = getServiceAccessor().getReadPersistenceService(); recorder = getServiceAccessor().getRecorder(); eventService = getServiceAccessor().getEventService(); loggerService = getServiceAccessor().getQueriableLoggerService(); scheduler = getServiceAccessor().getSchedulerService(); TestUtil.stopScheduler(scheduler, getTransactionService()); TestUtil.startScheduler(scheduler); } @After public void tearDown() throws Exception { TestUtil.closeTransactionIfOpen(getTransactionService()); } private SUser buildSUserImpl(final String firstName, final String lastName) { final SUser sUser = new SUser(); sUser.setUserName(firstName); sUser.setPassword(lastName); return sUser; } private List getLogs(final long indexValue, final String actionType) throws SBonitaReadException { final List filters = new ArrayList<>(2); filters.add(getActionTypeFilterOption(actionType)); filters.add(new FilterOption(SQueriableLog.class, getLogModelBuilderFactory().getObjectIdKey(), indexValue)); final List orders = Collections .singletonList(new OrderByOption(SQueriableLog.class, "id", OrderByType.ASC)); final QueryOptions opts = new QueryOptions(0, 10, orders, filters, null); return loggerService.searchLogs(opts); } private FilterOption getActionTypeFilterOption(final String actionType) { return new FilterOption(SQueriableLog.class, SQueriableLog.ACTION_TYPE, actionType); } private List getLogs(final String actionType) throws SBonitaReadException { final List filters = Collections.singletonList(getActionTypeFilterOption(actionType)); final List orders = Collections .singletonList(new OrderByOption(SQueriableLog.class, "id", OrderByType.ASC)); return loggerService.searchLogs(new QueryOptions(0, 10, orders, filters, null)); } private void checkSUserImpl(final SUser expectedSUser, final SUser retrievedSUser) { assertNotNull(retrievedSUser); assertEquals(expectedSUser, retrievedSUser); } private SUser getUserByUsername(final String firstName) throws SBonitaReadException { return getPersistenceService() .selectOne(new SelectOneDescriptor("getUserByUserName", Collections.singletonMap("userName", (Object) firstName), SUser.class)); } @Test public void testNotLogOnInsertRecordWhenBTXRolledBack() throws Exception { getTransactionService().begin(); final SelectOneDescriptor selectDescriptor = new SelectOneDescriptor<>("getUserByUserName", Collections.singletonMap("userName", (Object) "firstName"), SUser.class); SUser retrievedSUser = getPersistenceService().selectOne(selectDescriptor); assertNull("Should not have any SUSER_IMPL in DB before test", retrievedSUser); final String firstName = "Laurent"; final SUser sUser = buildSUserImpl(firstName, "Vaills"); recorder.recordInsert(new InsertRecord(sUser), SUSER_IMPL); // set rollback getTransactionService().setRollbackOnly(); getTransactionService().complete(); Thread.sleep(SLEEP_TIME); // The transaction has been rolled back no SUserImpl nor log should have been inserted. getTransactionService().begin(); retrievedSUser = getPersistenceService().selectOne(selectDescriptor); assertNull(retrievedSUser); final List retrievedLogs = getLogs(TEST_CREATED); assertEquals(0, retrievedLogs.size()); getTransactionService().complete(); } @Test public void testNotLogOnUpdateRecordWhenBTXRolledBack() throws Exception { // add sUserImpl using persistence service getTransactionService().begin(); final SUser sUser = buildSUserImpl("firstName", "lastName"); recorder.recordInsert(new InsertRecord(sUser), SUSER_IMPL); getTransactionService().complete(); // update sUserImpl (fail) getTransactionService().begin(); final SUser sUserToUpdate = getUserByUsername("firstName"); assertNotNull(sUserToUpdate); final Map stringObjectMap = Collections.singletonMap("userName", "firstName"); recorder.recordUpdate(UpdateRecord.buildSetFields(sUserToUpdate, stringObjectMap), SUSER_IMPL); getTransactionService().setRollbackOnly(); getTransactionService().complete(); // wait Thread.sleep(SLEEP_TIME); // query getTransactionService().begin(); final SUser retrivedSUser = getPersistenceService().selectOne( new SelectOneDescriptor("getUserByUserName", Collections.singletonMap("userName", (Object) "firstNameUpdate"), SUser.class)); assertNull(retrivedSUser); final List retrievedLogs = getLogs(TEST_UPDATE); assertEquals(0, retrievedLogs.size()); final SUser addedSUser = getUserByUsername("firstName"); recorder.recordDelete(new DeleteRecord(addedSUser), SUSER_IMPL); getTransactionService().complete(); } @Test public void testNotLogOnDeleteRecordIfBTXRolledBack() throws Exception { // add sUserImpl using persistence service getTransactionService().begin(); final SUser sUser = buildSUserImpl("firstName", "lastName"); recorder.recordInsert(new InsertRecord(sUser), SUSER_IMPL); getTransactionService().complete(); // delete sUserImpl using recorder getTransactionService().begin(); final SUser sUserToDelete = getUserByUsername("firstName"); assertNotNull(sUserToDelete); recorder.recordDelete(new DeleteRecord(sUserToDelete), SUSER_IMPL); getTransactionService().setRollbackOnly(); getTransactionService().complete(); // wait Thread.sleep(SLEEP_TIME); // query getTransactionService().begin(); final SUser retrievedSUser = getUserByUsername("firstName"); checkSUserImpl(sUser, retrievedSUser); final List retrievedLogs = getLogs(retrievedSUser.getId(), TEST_DELETE); assertEquals(0, retrievedLogs.size()); getTransactionService().complete(); // clean up: getTransactionService().begin(); recorder.recordDelete(new DeleteRecord(retrievedSUser), SUSER_IMPL); getTransactionService().complete(); } protected ReadPersistenceService getPersistenceService() { return persistenceService; } protected TestLogBuilderFactory getLogModelBuilderFactory() { if (logModelBuilderFactory == null) { logModelBuilderFactory = new TestLogBuilderFactory(); } return logModelBuilderFactory; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/resources/TenantResourcesServiceIT.java ================================================ /** * Copyright (C) 2016 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.resources; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.transaction.TransactionService; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Baptiste Mesta */ public class TenantResourcesServiceIT extends CommonBPMServicesTest { private static TenantResourcesService tenantResourcesService; private static TransactionService transactionService; @Before public void before() { tenantResourcesService = getServiceAccessor().getTenantResourcesService(); transactionService = getTransactionService(); } @After public void after() throws Exception { transactionService.executeInTransaction(() -> { tenantResourcesService.removeAll(TenantResourceType.BDM); return null; }); } @Test public void should_create_and_get_resource_work() throws Exception { transactionService.begin(); //given tenantResourcesService.add("myResource", TenantResourceType.BDM, "theResourceContent".getBytes(), -1); //when transactionService.complete(); transactionService.begin(); STenantResource myResource = tenantResourcesService.get(TenantResourceType.BDM, "myResource"); //then assertThat(myResource.getName()).isEqualTo("myResource"); assertThat(myResource.getType()).isEqualTo(TenantResourceType.BDM); assertThat(new String(myResource.getContent())).isEqualTo("theResourceContent"); transactionService.complete(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/impl/JobThatMayThrowErrorOrJobException.java ================================================ /** * 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.scheduler.impl; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SRetryableException; import org.bonitasoft.engine.scheduler.exception.SJobExecutionException; import org.bonitasoft.engine.scheduler.job.GroupJob; import org.bonitasoft.engine.scheduler.job.VariableStorage; public class JobThatMayThrowErrorOrJobException extends GroupJob { static final String TYPE = "type"; static final String ERROR = "ERROR"; static final String JOBEXCEPTION = "JOBEXCEPTION"; static final String NO_EXCEPTION = "NO_EXCEPTION"; static final String FAIL_ONCE = "FAIL_ONCE"; static final String FAIL_ONCE_WITH_RETRYABLE = "FAIL_ONCE_WITH_RETRYABLE"; private boolean throwsJobExecutionException; private boolean throwsError; private boolean failOnce; private boolean failOnceWithRetryable; private final VariableStorage variableStorage = VariableStorage.getInstance(); @Override public String getDescription() { return "throw error"; } @Override public void setAttributes(Map attributes) { super.setAttributes(attributes); Serializable type = attributes.get(TYPE); throwsJobExecutionException = JOBEXCEPTION.equals(type); throwsError = ERROR.equals(type); failOnce = FAIL_ONCE.equals(type); failOnceWithRetryable = FAIL_ONCE_WITH_RETRYABLE.equals(type); } @Override public void execute() throws SJobExecutionException { if (failOnce) { if (variableStorage.getVariableValue("nbJobException", 0) == 0) { variableStorage.setVariable("nbJobException", 1); throw new SJobExecutionException("Failing only once"); } } if (failOnceWithRetryable) { if (variableStorage.getVariableValue("nbJobException", 0) == 0) { variableStorage.setVariable("nbJobException", 1); throw new SRetryableException("Failing only once"); } } if (throwsError) { variableStorage.setVariable("nbError", variableStorage.getVariableValue("nbError", 0) + 1); throw new Error("an Error"); } if (throwsJobExecutionException) { variableStorage.setVariable("nbJobException", variableStorage.getVariableValue("nbJobException", 0) + 1); throw new SJobExecutionException("a Job exception"); } variableStorage.setVariable("nbSuccess", variableStorage.getVariableValue("nbSuccess", 0) + 1); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/impl/SchedulerServiceIT.java ================================================ /** * 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.scheduler.impl; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.awaitility.Durations.*; import static org.bonitasoft.engine.scheduler.impl.JobThatMayThrowErrorOrJobException.*; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.junit.Assert.*; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.stream.Collectors; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.scheduler.JobService; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.scheduler.job.ReleaseWaitersJob; import org.bonitasoft.engine.scheduler.job.VariableStorage; import org.bonitasoft.engine.scheduler.model.SFailedJob; import org.bonitasoft.engine.scheduler.model.SJobDescriptor; import org.bonitasoft.engine.scheduler.model.SJobParameter; import org.bonitasoft.engine.scheduler.trigger.OneShotTrigger; import org.bonitasoft.engine.scheduler.trigger.Trigger; import org.bonitasoft.engine.scheduler.trigger.UnixCronTrigger; import org.bonitasoft.engine.scheduler.trigger.UnixCronTriggerForTest; import org.bonitasoft.engine.test.util.TestUtil; import org.bonitasoft.engine.transaction.UserTransactionService; import org.bonitasoft.engine.util.FunctionalMatcher; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.junit.After; import org.junit.Before; import org.junit.Test; public class SchedulerServiceIT extends CommonBPMServicesTest { private SchedulerService schedulerService; private JobService jobService; private UserTransactionService userTransactionService; private final VariableStorage storage = VariableStorage.getInstance(); @Before public void before() throws Exception { schedulerService = getServiceAccessor().getSchedulerService(); userTransactionService = getServiceAccessor().getUserTransactionService(); jobService = getServiceAccessor().getJobService(); TestUtil.stopScheduler(schedulerService, getTransactionService()); TestUtil.startScheduler(schedulerService); } @After public void after() throws Exception { storage.clear(); userTransactionService.executeInTransaction(() -> { schedulerService.deleteJobs(); return null; }); } @Test public void canRestartTheSchedulerAfterShutdown() throws Exception { schedulerService.stop(); assertTrue(schedulerService.isStopped()); schedulerService.start(); assertTrue(schedulerService.isStarted()); } @Test public void doNotExecuteAFutureJob() throws Exception { final Date future = new Date(System.currentTimeMillis() + 10000000); final String variableName = "myVar"; final SJobDescriptor jobDescriptor = SJobDescriptor.builder() .jobClassName("org.bonitasoft.engine.scheduler.job.IncrementVariableJob") .jobName("IncrementVariableJob").build(); final List parameters = new ArrayList<>(); parameters.add(SJobParameter.builder().key("jobName").value("testDoNotExecuteAFutureJob").build()); parameters.add(SJobParameter.builder().key("variableName").value(variableName).build()); parameters.add(SJobParameter.builder().key("throwExceptionAfterNIncrements").value(-1).build()); final Trigger trigger = new OneShotTrigger("events", future, 10); getTransactionService().begin(); schedulerService.schedule(jobDescriptor, parameters, trigger); getTransactionService().complete(); Thread.sleep(200); assertNull(storage.getVariableValue(variableName)); } @Test public void doNotThrowAnExceptionWhenDeletingAnUnknownJob() throws Exception { getTransactionService().begin(); final boolean deleted = schedulerService.delete("MyJob"); getTransactionService().complete(); assertFalse(deleted); } /* * We must ensure that: * * pause jobs * * trigger new job are not executed * * resume the jobs resume it really */ @Test public void pause_and_resume_jobs_of_a_tenant() throws Exception { final String jobName = "ReleaseWaitersJob"; Date now = new Date(); SJobDescriptor jobDescriptor = SJobDescriptor.builder() .jobClassName(ReleaseWaitersJob.class.getName()).jobName(jobName + "1").build(); List parameters = new ArrayList<>(); parameters.add(SJobParameter.builder().key("jobName").value(jobName).build()); parameters.add(SJobParameter.builder().key("jobKey").value("1").build()); Trigger trigger = new UnixCronTriggerForTest("events", now, 10, "0/1 * * * * ?"); // trigger it getTransactionService().begin(); schedulerService.schedule(jobDescriptor, parameters, trigger); getTransactionService().complete(); ReleaseWaitersJob.waitForJobToExecuteOnce(); // pause getTransactionService().begin(); schedulerService.pauseJobs(); getTransactionService().complete(); Thread.sleep(100); ReleaseWaitersJob.checkNotExecutedDuring(1500); } @Test public void should_be_able_to_list_job_that_failed_because_of_an_Error() throws Exception { // schedule a job that throws an Error schedule(jobDescriptor(JobThatMayThrowErrorOrJobException.class, "MyJob"), new OneShotTrigger("triggerJob", new Date(System.currentTimeMillis() + 100)), singletonMap(TYPE, ERROR)); //we have failed job List failedJobs = await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND) .until(() -> inTx(() -> jobService.getFailedJobs(0, 100)), hasSize(1)); assertThat(failedJobs).hasOnlyOneElementSatisfying(f -> assertThat(f.getLastMessage()).contains("an Error")); } @Test public void should_be_able_to_restart_a_job_that_failed_because_of_a_SJobExecutionException() throws Exception { // schedule a job that throws a SJobExecutionException schedule(jobDescriptor(JobThatMayThrowErrorOrJobException.class, "MyJob"), new OneShotTrigger("triggerJob", new Date(System.currentTimeMillis() + 10)), singletonMap(TYPE, JOBEXCEPTION)); SJobDescriptor persistedJobDescriptor = getFirstPersistedJob(); // we should have a failed job: // Job can trigger in up to 'org.quartz.scheduler.idleWaitTime' milliseconds, so better wait long enough: List failedJobs = await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> { System.out.println("condition not here yet. Waiting again..."); return inTx(() -> jobService.getFailedJobs(0, 3)); }, hasSize(1)); assertThat(failedJobs.get(0).getLastMessage()).contains("a Job exception"); // small sleep because quartz does not always immediately delete the associated trigger (done in the quartz Thread) // because of that, it can cause issues when rescheduling (Foreign key violation) Thread.sleep(100); // reschedule the job: should be no more exception inTx(() -> { schedulerService.retryJobThatFailed(persistedJobDescriptor.getId(), toJobParameterList(singletonMap(TYPE, NO_EXCEPTION))); return null; }); await().pollDelay(FIVE_HUNDRED_MILLISECONDS).atMost(ONE_MINUTE).pollInterval(ONE_SECOND) .until(() -> storage.getVariableValue("nbSuccess", 0).equals(1)); } @Test public void should_be_able_to_restart_a_cron_job_that_failed_because_of_a_SJobExecutionException() throws Exception { // schedule a job that throws a SJobExecutionException schedule(jobDescriptor(JobThatMayThrowErrorOrJobException.class, "MyJob"), new UnixCronTrigger("triggerJob", new Date(System.currentTimeMillis() + 100), "* * * * * ?"), singletonMap(TYPE, JOBEXCEPTION)); SJobDescriptor persistedJobDescriptor = getFirstPersistedJob(); //ensure there is more than one failure: i.e. cron is still triggering new jobs await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> storage.getVariableValue("nbJobException", 0), isGreaterThan(1)); //wait a little because the failure is registered later... Callable> getFailedJobs = () -> { try { return inTx(() -> jobService.getFailedJobs(0, 100)); } catch (Exception e) { throw new RuntimeException(e); } }; List sFailedJobs = await().until(getFailedJobs, new BaseMatcher<>() { @Override public boolean matches(Object item) { List list = (List) item; return list.size() == 1 && list.get(0).getNumberOfFailures() > 1; } @Override public void describeTo(Description description) { } }); assertThat(sFailedJobs).hasSize(1); //ensure we trace the number of failure assertThat(sFailedJobs.get(0).getNumberOfFailures()).isGreaterThan(1); //reschedule the job: no more exception inTx(() -> { schedulerService.retryJobThatFailed(persistedJobDescriptor.getId(), toJobParameterList(singletonMap(TYPE, NO_EXCEPTION))); return null; }); //ensure there is more than one success: i.e. cron is still triggering new jobs await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> storage.getVariableValue("nbSuccess", 0), isGreaterThan(1)); //ensure no more failed job is present assertThat(inTx(() -> jobService.getFailedJobs(0, 100))).isEmpty(); } @Test public void should_keep_a_failed_job_when_failing_once() throws Exception { // schedule a job that throws a SJobExecutionException schedule(jobDescriptor(JobThatMayThrowErrorOrJobException.class, "MyJob"), new UnixCronTrigger("triggerJob", new Date(System.currentTimeMillis() + 100), "* * * * * ?"), singletonMap(TYPE, FAIL_ONCE)); SJobDescriptor persistedJobDescriptor = getFirstPersistedJob(); //this job fail only the first time await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> storage.getVariableValue("nbJobException", 0), isGreaterThan(0)); await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> storage.getVariableValue("nbSuccess", 0), isGreaterThan(0)); List sFailedJobs = inTx(() -> jobService.getFailedJobs(0, 100)); assertThat(sFailedJobs).hasSize(1); //ensure we trace the number of failure assertThat(sFailedJobs.get(0).getNumberOfFailures()).isEqualTo(1); //reschedule the job: no more exception inTx(() -> { schedulerService.retryJobThatFailed(persistedJobDescriptor.getId(), toJobParameterList(singletonMap(TYPE, NO_EXCEPTION))); return null; }); //ensure there is more than one success: i.e. cron is still triggering new jobs await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> storage.getVariableValue("nbSuccess", 0), isGreaterThan(1)); //ensure no more failed job is present assertThat(inTx(() -> jobService.getFailedJobs(0, 100))).isEmpty(); } @Test public void should_let_quartz_retry_a_job_that_failed_because_of_a_SRetryableException() throws Exception { // schedule one shot job that throws a SJobExecutionException schedule(jobDescriptor(JobThatMayThrowErrorOrJobException.class, "MyJob"), new OneShotTrigger("triggerJob", new Date(System.currentTimeMillis() + 100)), singletonMap(TYPE, FAIL_ONCE_WITH_RETRYABLE)); SJobDescriptor persistedJobDescriptor = getFirstPersistedJob(); //this job fail once and is immediately retried, even if its a one shot job await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> storage.getVariableValue("nbJobException", 0), isGreaterThan(0)); await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).until(() -> storage.getVariableValue("nbSuccess", 0), isGreaterThan(0)); List sFailedJobs = inTx(() -> jobService.getFailedJobs(0, 100)); //no error traced, it was retried assertThat(sFailedJobs).hasSize(0); } private FunctionalMatcher isGreaterThan(int i) { return t -> t > i; } private SJobDescriptor getFirstPersistedJob() throws Exception { return inTx(() -> jobService.searchJobDescriptors(new QueryOptions(0, 1))).get(0); } private T inTx(Callable callable) throws Exception { return userTransactionService.executeInTransaction(() -> callable.call()); } private SJobDescriptor jobDescriptor(Class jobClass, String jobName) { return SJobDescriptor.builder() .jobClassName(jobClass.getName()).jobName(jobName).build(); } private void schedule(SJobDescriptor jobDescriptor, Trigger trigger, Map parameters) throws Exception { List parametersList = toJobParameterList(parameters); inTx(() -> { schedulerService.schedule(jobDescriptor, parametersList, trigger); return null; }); } private List toJobParameterList(Map parameters) { return parameters.entrySet().stream() .map(e -> SJobParameter.builder().key(e.getKey()).value(e.getValue()).build()) .collect(Collectors.toList()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/impl/WaitForIncrementJobToHaveValue.java ================================================ /** * 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.scheduler.impl; import static org.junit.Assert.assertTrue; import java.util.Date; import org.bonitasoft.engine.scheduler.job.IncrementItselfJob; /** * @author Baptiste Mesta */ public class WaitForIncrementJobToHaveValue { private final int value; private final int timeout; private final int repeatEach; public WaitForIncrementJobToHaveValue(final int repeatEach, final int timeout, int value) { assertTrue("timeout is not big enough", repeatEach < timeout); this.repeatEach = repeatEach; this.timeout = timeout; this.value = value; } /** * @param timeout * @param value */ public WaitForIncrementJobToHaveValue(final int timeout, final int value) { this(10, timeout, value); } boolean check() { return IncrementItselfJob.getValue() == value; } public boolean waitFor() throws InterruptedException { final long limit = new Date().getTime() + timeout; while (new Date().getTime() < limit) { Thread.sleep(repeatEach); if (check()) { return true; } } return check(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/DoNothingJob.java ================================================ /** * 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.scheduler.job; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.scheduler.StatelessJob; /** * @author Elias Ricken de Medeiros */ public class DoNothingJob implements StatelessJob { private static final long serialVersionUID = 5253574298401130601L; @Override public String getName() { return "doNothing"; } @Override public String getDescription() { return "DoNothing"; } @Override public void execute() { } @Override public void setAttributes(final Map attributes) { } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/GroupJob.java ================================================ /** * 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.scheduler.job; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.scheduler.StatelessJob; /** * @author Matthieu Chaffotte */ public abstract class GroupJob implements StatelessJob { private static final long serialVersionUID = 1L; private String jobName; @Override public String getName() { return jobName; } @Override public void setAttributes(final Map attributes) { jobName = (String) attributes.get("jobName"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/IncrementItselfJob.java ================================================ /** * 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.scheduler.job; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; /** * @author Baptiste Mesta */ public class IncrementItselfJob extends GroupJob { private static final long serialVersionUID = 3707724945060118636L; private static int value = 0; private static List executionDates = new ArrayList(); @Override public void execute() { value++; addToExecutionDates(new Date(System.currentTimeMillis())); } public static int getValue() { return value; } public static void reset() { value = 0; executionDates = new ArrayList(); } public static synchronized List getExecutionDates() { return new ArrayList(executionDates); } synchronized void addToExecutionDates(Date date) { executionDates.add(date); } @Override public String getDescription() { return "Increment itself "; } @Override public void setAttributes(final Map attributes) { super.setAttributes(attributes); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/IncrementVariableJob.java ================================================ /** * 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.scheduler.job; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.scheduler.exception.SJobExecutionException; /** * @author Matthieu Chaffotte */ public class IncrementVariableJob extends GroupJob { private static final long serialVersionUID = 3707724945060118636L; private String variableName; private int throwExceptionAfterNIncrements; @Override public void execute() throws SJobExecutionException { synchronized (IncrementVariableJob.class) { final VariableStorage storage = VariableStorage.getInstance(); final Integer value = (Integer) storage.getVariableValue(variableName); if (value == null) { storage.setVariable(variableName, 1); } else if (value + 1 == throwExceptionAfterNIncrements) { throw new SJobExecutionException("Increment reached"); } else { storage.setVariable(variableName, value + 1); } } } @Override public String getDescription() { return "Increment the variable " + variableName; } @Override public void setAttributes(final Map attributes) { super.setAttributes(attributes); variableName = (String) attributes.get("variableName"); throwExceptionAfterNIncrements = (Integer) attributes.get("throwExceptionAfterNIncrements"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/JobSemaphore.java ================================================ /** * 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.scheduler.job; import java.util.concurrent.Semaphore; class JobSemaphore extends Semaphore { /** * */ private static final long serialVersionUID = 1L; String key; public JobSemaphore(final int permits) { super(permits); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/ReleaseWaitersJob.java ================================================ /** * 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.scheduler.job; import java.io.Serializable; import java.util.Date; import java.util.Map; import java.util.concurrent.TimeUnit; /** * @author Baptiste Mesta */ public class ReleaseWaitersJob extends GroupJob { private static final long serialVersionUID = 3707724945060118636L; private static JobSemaphore semaphore; private static String jobKey; @Override public void execute() { System.out.println("ReleaseWaitersJob EXECUTES at time " + new Date().getSeconds()); if (semaphore != null) { semaphore.key = jobKey; semaphore.release(); } } @Override public String getDescription() { return "release a semaphore"; } @Override public void setAttributes(final Map attributes) { super.setAttributes(attributes); jobKey = (String) attributes.get("jobKey"); } /* * create a new semaphore and wait for the release to be called */ public static void waitForJobToExecuteOnce() throws Exception { semaphore = new JobSemaphore(1); semaphore.acquire(); boolean acquired = semaphore.tryAcquire(30, TimeUnit.SECONDS); if (!acquired) { throw new Exception("job was not triggered"); } } public static void checkNotExecutedDuring(final int milliseconds) throws Exception { semaphore = new JobSemaphore(1); semaphore.acquire(); boolean acquired = semaphore.tryAcquire(milliseconds, TimeUnit.MILLISECONDS); if (acquired) { throw new Exception("job " + jobKey + " was triggered"); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/ThrowsExceptionJob.java ================================================ /** * 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.scheduler.job; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.scheduler.StatelessJob; import org.bonitasoft.engine.scheduler.exception.SJobExecutionException; /** * @author Elias Ricken de Medeiros */ public class ThrowsExceptionJob implements StatelessJob { private static final long serialVersionUID = 1L; public static final String THROW_EXCEPTION = "throwException"; private Boolean throwException = null; @Override public String getDescription() { return "Job that throws an exception"; } @Override public void execute() throws SJobExecutionException { if (throwException != null && throwException) { throw new SJobExecutionException("This job throws an arbitrary exception"); } } @Override public String getName() { return "ThrowsExceptionJob"; } @Override public void setAttributes(final Map attributes) { final Boolean result = (Boolean) attributes.get(THROW_EXCEPTION); if (result != null) { throwException = result; } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/UpdateVariable.java ================================================ /** * 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.scheduler.job; import java.io.Serializable; import java.util.Map; /** * @author Matthieu Chaffotte */ public class UpdateVariable extends GroupJob { private static final long serialVersionUID = 8379781766551862114L; private String variableName; private Object variableValue; public UpdateVariable(final String variableName, final Object variableValue) { super(); this.variableName = variableName; this.variableValue = variableValue; } @Override public void execute() { final VariableStorage storage = VariableStorage.getInstance(); storage.setVariable(variableName, variableValue); } @Override public String getDescription() { return "Change the value of " + variableName + " with " + variableValue; } @Override public void setAttributes(final Map attributes) { super.setAttributes(attributes); variableName = (String) attributes.get("variableName"); variableValue = attributes.get("variableValue"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/job/VariableStorage.java ================================================ /** * 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.scheduler.job; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * @author Matthieu Chaffotte */ public class VariableStorage implements Serializable { private static final long serialVersionUID = -4195221111626812999L; private final Object lock = new Object(); private final Map variables; public static final VariableStorage INSTANCE = new VariableStorage(); private VariableStorage() { variables = new HashMap<>(); } public static VariableStorage getInstance() { return INSTANCE; } public void setVariable(final String name, final Object value) { synchronized (lock) { variables.put(name, value); } } public Object getVariableValue(final String name) { return getVariableValue(name, null); } public T getVariableValue(final String name, T defaultValue) { synchronized (lock) { return ((T) variables.getOrDefault(name, defaultValue)); } } public static void clearAll() { INSTANCE.clear(); } public void clear() { synchronized (lock) { variables.clear(); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/scheduler/trigger/UnixCronTriggerForTest.java ================================================ /** * 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.scheduler.trigger; import java.util.Date; /** * @author Matthieu Chaffotte */ public class UnixCronTriggerForTest extends OneShotTrigger implements CronTrigger { private final String expression; private final Date endDate; public UnixCronTriggerForTest(final String name, final Date startDate, final int priority, final String expression) { super(name, startDate, priority); this.expression = expression; this.endDate = null; } public UnixCronTriggerForTest(final String name, final Date startDate, final int priority, final String expression, final MisfireRestartPolicy misfireRestartPolicy) { super(name, startDate, priority, misfireRestartPolicy); this.expression = expression; this.endDate = null; } public UnixCronTriggerForTest(final String name, final Date startDate, final int priority, final String expression, final Date endDate) { super(name, startDate, priority); this.expression = expression; this.endDate = endDate; } @Override public String getExpression() { return this.expression; } @Override public Date getEndDate() { return this.endDate; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/session/PlatformSessionServiceIT.java ================================================ /** * 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.session; import static org.junit.Assert.*; import java.util.Date; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.platform.session.PlatformSessionService; import org.bonitasoft.engine.platform.session.SSessionNotFoundException; import org.bonitasoft.engine.platform.session.model.SPlatformSession; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class PlatformSessionServiceIT extends CommonBPMServicesTest { private static PlatformSessionService sessionService; @Before public void before() { sessionService = getServiceAccessor().getPlatformSessionService(); } @Test public void testCreateSession() throws Exception { final String username = "platformAdmin"; final Date before = new Date(); final SPlatformSession session = sessionService.createSession(username); assertNotNull(session); assertNotNull(session.getCreationDate()); assertTrue(before.getTime() <= session.getCreationDate().getTime()); assertTrue(session.getDuration() > 0); assertEquals(sessionService.getSessionsDuration(), session.getDuration()); assertEquals(session.getLastRenewDate().getTime() + session.getDuration(), session.getExpirationDate().getTime()); assertEquals(username, session.getUserName()); } @Test public void testIsValid() throws Exception { final String username = "platformAdmin"; long sessionsDuration = sessionService.getSessionsDuration(); try { sessionService.setSessionDuration(1000); final SPlatformSession session = sessionService.createSession(username); assertNotNull(session); assertEquals(1000, session.getDuration()); assertTrue(sessionService.isValid(session.getId())); Thread.sleep(session.getDuration() + 1); assertFalse(sessionService.isValid(session.getId())); } finally { sessionService.setSessionDuration(sessionsDuration); } } @Test public void testGetSession() throws Exception { final String username = "platformAdmin"; final SPlatformSession session = sessionService.createSession(username); assertNotNull(session); final SPlatformSession retrievedSession = sessionService.getSession(session.getId()); assertEquals(session, retrievedSession); } @Test(expected = SSessionNotFoundException.class) public void testDeleteInvalidSession() throws Exception { sessionService.deleteSession(System.currentTimeMillis()); } @Test(expected = SSessionNotFoundException.class) public void testDeleteSession() throws Exception { final String username = "platformAdmin"; final SPlatformSession session = sessionService.createSession(username); assertNotNull(session); SPlatformSession retrievedSession = sessionService.getSession(session.getId()); assertEquals(session, retrievedSession); sessionService.deleteSession(session.getId()); sessionService.getSession(session.getId()); } @Test public void testrenewSession() throws Exception { final String username = "matti"; final SPlatformSession session = sessionService.createSession(username); Thread.sleep(100); sessionService.renewSession(session.getId()); final SPlatformSession session2 = sessionService.getSession(session.getId()); assertTrue(session2.getExpirationDate().after(session.getExpirationDate())); assertTrue(session2.getLastRenewDate().after(session.getLastRenewDate())); assertEquals(session2.getLastRenewDate().getTime() + session2.getDuration(), session2.getExpirationDate().getTime()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/session/SessionServiceIT.java ================================================ /** * 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.session; import static org.junit.Assert.*; import java.util.Date; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.session.model.SSession; import org.bonitasoft.engine.test.util.TestUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros, Yanyan Liu */ public class SessionServiceIT extends CommonBPMServicesTest { private SessionService sessionService; @Before public void setup() { sessionService = getServiceAccessor().getSessionService(); sessionService.setSessionDuration(sessionService.getDefaultSessionDuration()); } @After public void tearDown() throws Exception { TestUtil.closeTransactionIfOpen(getTransactionService()); getSessionAccessor().deleteSessionId(); } @Test public void testSessionUserIdForUnknownUser() throws Exception { final String username = "DonaldDuck"; final SSession session = createSession(username); assertNotNull(session); assertEquals(-1, session.getUserId()); } @Test public void testCreateSession() throws Exception { final String username = "john"; final Date before = new Date(); final SSession session = createSession(username); assertNotNull(session); assertNotNull(session.getCreationDate()); assertTrue(before.getTime() <= session.getCreationDate().getTime()); assertTrue(session.getDuration() > 0); assertEquals(sessionService.getSessionDuration(), session.getDuration()); assertEquals(session.getLastRenewDate().getTime() + session.getDuration(), session.getExpirationDate().getTime()); assertEquals(username, session.getUserName()); } private SSession createSession(final String username) throws SBonitaException { getTransactionService().begin(); final SSession session = sessionService.createSession(username); getTransactionService().complete(); return session; } @Test public void should_session_be_valid() throws Exception { final String username = "john"; sessionService.setSessionDuration(1000); final SSession session = createSession(username); assertNotNull(session); assertTrue(sessionService.isValid(session.getId())); Thread.sleep(session.getDuration() + 1); assertFalse(sessionService.isValid(session.getId())); } @Test public void testRenewSession() throws Exception { final String username = "matti"; final SSession session = createSession(username); Thread.sleep(10); // getTransactionService().begin(); sessionService.renewSession(session.getId()); // getTransactionService().complete(); // getTransactionService().begin(); final SSession session2 = sessionService.getSession(session.getId()); // getTransactionService().complete(); assertTrue(session2.getExpirationDate().after(session.getExpirationDate())); assertTrue(session2.getLastRenewDate().after(session.getLastRenewDate())); assertEquals(session2.getLastRenewDate().getTime() + session2.getDuration(), session2.getExpirationDate().getTime()); } @Test public void testGetSession() throws Exception { final String username = "john"; final SSession session = createSession(username); assertNotNull(session); final SSession retrievedSession = sessionService.getSession(session.getId()); assertEquals(session, retrievedSession); } @Test(expected = SSessionNotFoundException.class) public void testCleanInvalidSessions() throws Exception { final String username = "john"; sessionService.setSessionDuration(1); final SSession invalidSession = createSession(username); assertNotNull(invalidSession); // the session will expires Thread.sleep(10); sessionService.cleanInvalidSessions(); // throw exception sessionService.getSession(invalidSession.getId()); } @Test(expected = SSessionNotFoundException.class) public void testDeleteSession() throws Exception { final String username = "john"; final SSession session = createSession(username); assertNotNull(session); SSession retrievedSession = sessionService.getSession(session.getId()); assertEquals(session, retrievedSession); sessionService.deleteSession(session.getId()); try { sessionService.getSession(session.getId()); } finally { // restore deleted session: createSession(username); } } @Test(expected = SSessionNotFoundException.class) public void testDeleteWrongSession() throws Exception { sessionService.deleteSession(System.currentTimeMillis()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/tenant/TenantMaintenanceLocalIT.java ================================================ /** * 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.tenant; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.engine.work.WorkService; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; /** * @author Baptiste Mesta * @author Celine Souchet */ public class TenantMaintenanceLocalIT extends TestWithUser { @Rule public final SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests(); @Test public void should_pause_tenant_then_stop_start_node_start_pageService_again_but_dont_restart_elements_but_resume_restart_them() throws Exception { // given: tenant is paused WorkService workService = getServiceAccessor().getWorkService(); assertFalse(workService.isStopped()); logoutThenloginAs(USERNAME, PASSWORD); final ProcessDefinitionBuilder pdb = new ProcessDefinitionBuilder().createNewInstance("loop process def", "1.0"); pdb.addAutomaticTask("step1").addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(100)); final DesignProcessDefinition dpd = pdb.done(); final ProcessDefinition pd = deployAndEnableProcess(dpd); ProcessInstance processInstance = getProcessAPI().startProcess(pd.getId()); logoutThenlogin(); getTenantAdministrationAPI().pause(); assertTrue(workService.isStopped()); logout(); // clear logs before restarting the node systemOutRule.clearLog(); // when: we stop and start the node stopAndStartPlatform(); // assert that provided mandatory pages have re-imported again even though the tenant is paused assertTrue(systemOutRule.getLog().contains("Import of Bonita mandatory pages completed")); // then: work service is not running workService = getServiceAccessor().getWorkService(); assertTrue(workService.isStopped()); // cleanup loginWithTechnicalUser(); getTenantAdministrationAPI().resume(); waitForProcessToFinish(processInstance); disableAndDeleteProcess(pd); } protected ServiceAccessor getServiceAccessor() { return ServiceAccessorSingleton.getInstance(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/APIMethodLocalIT.java ================================================ /** * 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 org.bonitasoft.engine.api.impl.CommandAPIImpl; import org.bonitasoft.engine.api.impl.IdentityAPIImpl; import org.bonitasoft.engine.api.impl.PlatformAPIImpl; import org.bonitasoft.engine.api.impl.PlatformCommandAPIImpl; import org.bonitasoft.engine.api.impl.ProcessAPIImpl; import org.junit.Test; public class APIMethodLocalIT extends APITestUtil { @Test public void checkAllMethodsOfCommandAPIContainsSerializableParameters() { checkAllParametersAreSerializable(CommandAPIImpl.class); } @Test public void checkAllMethodsOfPlatformCommandAPIContainsSerializableParameters() { checkAllParametersAreSerializable(PlatformCommandAPIImpl.class); } @Test public void checkAllMethodsOfPlatformAPIContainsSerializableParameters() { checkAllParametersAreSerializable(PlatformAPIImpl.class); } @Test public void checkAllMethodsOfIdentityAPIContainsSerializableParameters() { checkAllParametersAreSerializable(IdentityAPIImpl.class); } @Test public void checkAllMethodsOfProcessAPIContainsSerializableParameters() { checkAllParametersAreSerializable(ProcessAPIImpl.class); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/BPMLocalIT.java ================================================ /** * 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 static org.junit.Assert.*; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.api.ApiAccessType; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.LoginAPI; import org.bonitasoft.engine.api.PlatformAPI; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.flownode.ActivityExecutionException; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException; import org.bonitasoft.engine.bpm.flownode.ActivityStates; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.connectors.VariableStorage; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping; import org.bonitasoft.engine.dependency.DependencyService; import org.bonitasoft.engine.dependency.model.AbstractSDependency; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.platform.Platform; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.engine.session.impl.APISessionImpl; import org.bonitasoft.engine.transaction.UserTransactionService; import org.bonitasoft.engine.util.APITypeManager; import org.junit.After; import org.junit.Before; import org.junit.Test; public class BPMLocalIT extends CommonAPILocalIT { public static Semaphore semaphore1 = new Semaphore(1); public static Semaphore semaphore2 = new Semaphore(1); private User john; @Before public void before() throws Exception { loginWithTechnicalUser(); john = createUser(USERNAME, PASSWORD); logout(); loginOnDefaultTenantWith(USERNAME, PASSWORD); setSessionInfo(getSession()); } @After public void after() throws Exception { VariableStorage.clearAll(); deleteUser(USERNAME); logout(); cleanSession(); } @Test(expected = InvalidSessionException.class) public void useAFakeSessionId() throws BonitaException { final LoginAPI loginAPI = getLoginAPI(); final APISession session = loginAPI.login(DEFAULT_TECHNICAL_LOGGER_USERNAME, DEFAULT_TECHNICAL_LOGGER_PASSWORD); final APISession fakeSession = new APISessionImpl(session.getId() + 1, session.getCreationDate(), session.getDuration(), session.getUserName(), session.getUserId()); final IdentityAPI identityAPI = TenantAPIAccessor.getIdentityAPI(fakeSession); identityAPI.getGroup(12); } @Test public void checkProcessCommentAreArchived() throws Exception { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SCommentService commentService = serviceAccessor.getCommentService(); final UserTransactionService transactionService = serviceAccessor.getUserTransactionService(); final ProcessDefinitionBuilder processDef = new ProcessDefinitionBuilder() .createNewInstance("processToTestComment", "1.0"); processDef.addStartEvent("start"); processDef.addUserTask("step1", ACTOR_NAME); processDef.addEndEvent("end"); processDef.addTransition("start", "step1"); processDef.addTransition("step1", "end"); processDef.addActor(ACTOR_NAME); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDef.done(), ACTOR_NAME, john); setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved final Callable getNumberOfComments = () -> commentService .getNumberOfComments(QueryOptions.countQueryOptions()); final Callable getNumberOfArchivedComments = () -> commentService .getNumberOfArchivedComments(QueryOptions.countQueryOptions()); assertEquals(0, (long) transactionService.executeInTransaction(getNumberOfComments)); final long numberOfInitialArchivedComments = transactionService .executeInTransaction(getNumberOfArchivedComments); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); getProcessAPI().addProcessComment(processInstance.getId(), "kikoo lol"); setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved assertEquals(1, (long) transactionService.executeInTransaction(getNumberOfComments)); assertEquals(numberOfInitialArchivedComments, (long) transactionService.executeInTransaction(getNumberOfArchivedComments)); assignAndExecuteStep(step1Id, john); waitForProcessToFinish(processInstance); setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved assertEquals(0, (long) transactionService.executeInTransaction(getNumberOfComments)); assertEquals(numberOfInitialArchivedComments + 2, (long) transactionService.executeInTransaction(getNumberOfArchivedComments)); disableAndDeleteProcess(definition); } @Test public void checkPendingMappingAreDeleted() throws Exception { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final UserTransactionService transactionService = serviceAccessor.getUserTransactionService(); final ProcessDefinitionBuilder processDef = new ProcessDefinitionBuilder() .createNewInstance("processToTestComment", "1.0"); processDef.addShortTextData("kikoo", new ExpressionBuilder().createConstantStringExpression("lol")); processDef.addStartEvent("start"); processDef.addUserTask("step1", ACTOR_NAME).addShortTextData("kikoo2", new ExpressionBuilder().createConstantStringExpression("lol")); processDef.addUserTask("step2", ACTOR_NAME); processDef.addEndEvent("end"); processDef.addTransition("start", "step1"); processDef.addTransition("step1", "step2"); processDef.addTransition("step2", "end"); processDef.addActor(ACTOR_NAME); final ProcessDefinition definition = deployAndEnableProcessWithActor(processDef.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved final Callable> getPendingMappings = () -> { final QueryOptions queryOptions = new QueryOptions(0, 100, SPendingActivityMapping.class, "id", OrderByType.ASC); return activityInstanceService.getPendingMappings(step1Id, queryOptions); }; List mappings = transactionService.executeInTransaction(getPendingMappings); assertEquals(1, mappings.size()); assignAndExecuteStep(step1Id, john.getId()); waitForUserTask(processInstance, "step2"); setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved mappings = transactionService.executeInTransaction(getPendingMappings); assertEquals(0, mappings.size()); disableAndDeleteProcess(definition); } @Test public void checkDependenciesAreDeletedWhenProcessIsDeleted() throws Exception { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DependencyService dependencyService = serviceAccessor.getDependencyService(); final UserTransactionService transactionService = serviceAccessor.getUserTransactionService(); final ProcessDefinitionBuilder processDef = new ProcessDefinitionBuilder() .createNewInstance("processToTestTransitions", "1.0"); processDef.addStartEvent("start"); processDef.addUserTask("step1", ACTOR_NAME); processDef.addEndEvent("end"); processDef.addTransition("start", "step1"); processDef.addTransition("step1", "end"); processDef.addActor(ACTOR_NAME); final byte[] content = new byte[] { 1, 2, 3, 4, 5, 6, 7 }; final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDef.done()) .addClasspathResource(new BarResource("myDep", content)).done(); final ProcessDefinition definition = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); List dependencyIds = transactionService .executeInTransaction(new GetDependenciesIds(getSession(), definition.getId(), dependencyService, 0, 100)); assertEquals(1, dependencyIds.size()); final AbstractSDependency dependency = transactionService .executeInTransaction(new GetSDependency(dependencyIds.get(0), dependencyService)); assertTrue(dependency.getName().endsWith("myDep")); assertTrue(Arrays.equals(content, dependency.getValue())); assignAndExecuteStep(step1Id, john); waitForProcessToFinish(processInstance); disableAndDeleteProcess(definition); dependencyIds = transactionService.executeInTransaction( new GetDependenciesIds(getSession(), definition.getId(), dependencyService, 0, 100)); assertEquals(0, dependencyIds.size()); } @Test public void checkMoreThan20DependenciesAreDeletedWhenProcessIsDeleted() throws Exception { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DependencyService dependencyService = serviceAccessor.getDependencyService(); final UserTransactionService transactionService = serviceAccessor.getUserTransactionService(); final ProcessDefinitionBuilder processDef = new ProcessDefinitionBuilder() .createNewInstance("processToTestTransitions", "1.0"); processDef.addStartEvent("start").addUserTask("step1", ACTOR_NAME).addEndEvent("end"); processDef.addTransition("start", "step1").addTransition("step1", "end"); processDef.addActor(ACTOR_NAME); final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDef.done()); for (int i = 0; i < 25; i++) { final byte[] content = new byte[] { 1, 2, 3, 4, 5, 6, 7, (byte) (i >>> 24), (byte) (i >> 16 & 0xff), (byte) (i >> 8 & 0xff), (byte) (i & 0xff) }; businessArchiveBuilder.addClasspathResource(new BarResource("myDep" + i, content)); } final ProcessDefinition definition = deployAndEnableProcessWithActor(businessArchiveBuilder.done(), ACTOR_NAME, john); final ProcessInstance processInstance = getProcessAPI().startProcess(definition.getId()); final long step1Id = waitForUserTask(processInstance, "step1"); setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved List dependencyIds = transactionService .executeInTransaction(new GetDependenciesIds(getSession(), definition.getId(), dependencyService, 0, 100)); assertEquals(25, dependencyIds.size()); final AbstractSDependency dependency = transactionService .executeInTransaction(new GetSDependency(dependencyIds.get(0), dependencyService)); assertNotNull(dependency); assignAndExecuteStep(step1Id, john.getId()); waitForProcessToFinish(processInstance); disableAndDeleteProcess(definition); setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved dependencyIds = transactionService.executeInTransaction( new GetDependenciesIds(getSession(), definition.getId(), dependencyService, 0, 100)); assertEquals(0, dependencyIds.size()); } @Test public void deletingProcessDeletesActors() throws Exception { final ServiceAccessor serviceAccessor = getServiceAccessor(); final UserTransactionService transactionService = serviceAccessor.getUserTransactionService(); final String userTaskName = "actNaturally"; final ProcessDefinition definition = deployAndEnableProcessWithOneHumanTask("deletingProcessDeletesActors", "CandidateForOscarReward", userTaskName); final ProcessInstance processInstanceId = getProcessAPI().startProcess(definition.getId()); waitForUserTask(processInstanceId, userTaskName); disableAndDeleteProcess(definition); setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved final List actors = transactionService.executeInTransaction(() -> { final QueryOptions queryOptions = new QueryOptions(0, 1, SActor.class, "id", OrderByType.ASC); return getServiceAccessor().getActorMappingService().getActors(definition.getId(), queryOptions); }); // Check there is no actor left: assertEquals(0, actors.size()); } @Test public void deletingProcessDeletesActorMappings() throws Exception { final ServiceAccessor serviceAccessor = getServiceAccessor(); final UserTransactionService transactionService = serviceAccessor.getUserTransactionService(); final String userTaskName = "actNaturally"; final ProcessDefinition definition = deployAndEnableProcessWithOneHumanTask( "deletingProcessDeletesActorMappings", "CandidateForOscarReward", userTaskName); final ProcessInstance processInstanceId = getProcessAPI().startProcess(definition.getId()); waitForUserTask(processInstanceId, userTaskName); disableAndDeleteProcess(definition); setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved final List actorMembers = transactionService .executeInTransaction( () -> getServiceAccessor().getActorMappingService().getActorMembersOfUser(john.getId(), 0, 1)); // Check there is no actor left: assertEquals(0, actorMembers.size()); } private ProcessDefinition deployAndEnableProcessWithOneHumanTask(final String processName, final String actorName, final String userTaskName) throws Exception { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder(); processBuilder.createNewInstance(processName, "1.0"); processBuilder.addActor(actorName).addDescription(actorName + " description"); processBuilder.addStartEvent("startEvent"); processBuilder.addUserTask(userTaskName, actorName); processBuilder.addEndEvent("endEvent"); processBuilder.addTransition("startEvent", userTaskName); processBuilder.addTransition(userTaskName, "endEvent"); return deployAndEnableProcessWithActor(processBuilder.done(), actorName, john); } @Test public void restartHandlerTests() throws Exception { /* * process with blocking connector */ final ProcessDefinitionBuilder builder1 = new ProcessDefinitionBuilder().createNewInstance("p1", "1.0"); builder1.addActor(ACTOR_NAME); builder1.addUserTask("step1", ACTOR_NAME); builder1.addAutomaticTask("step2").addConnector("myConnector", "blocking-connector", "1.0", ConnectorEvent.ON_ENTER); builder1.addTransition("step1", "step2"); builder1.addUserTask("ustep2", ACTOR_NAME); builder1.addTransition("step2", "ustep2"); final BusinessArchive businessArchive = new BusinessArchiveBuilder() .createNewBusinessArchive() .setProcessDefinition(builder1.done()) .addConnectorImplementation( new BarResource("blocking-connector.impl", BuildTestUtil.buildConnectorImplementationFile("blocking-connector", "1.0", "blocking-connector-impl", "1.0", BlockingConnector.class.getName()))) .done(); final ProcessDefinition p1 = deployAndEnableProcessWithActor(businessArchive, ACTOR_NAME, john); /* * process with blocking operation (executing work) */ final ProcessDefinitionBuilder builder2 = new ProcessDefinitionBuilder().createNewInstance("p2", "1.0"); final String blockingGroovyScript1 = "org.bonitasoft.engine.test.BPMLocalIT.tryAcquireSemaphore1();\nreturn \"done\";"; builder2.addActor(ACTOR_NAME); builder2.addShortTextData("data", null); builder2.addUserTask("step1", ACTOR_NAME); builder2.addAutomaticTask("step2").addOperation( new OperationBuilder().createSetDataOperation("data", new ExpressionBuilder().createGroovyScriptExpression("blockingGroovyScript1", blockingGroovyScript1, String.class.getName()))); builder2.addTransition("step1", "step2"); builder2.addUserTask("ustep2", ACTOR_NAME); builder2.addTransition("step2", "ustep2"); final ProcessDefinition p2 = deployAndEnableProcessWithActor(builder2.done(), ACTOR_NAME, john); /* * process with blocking transition (notify work) */ final ProcessDefinitionBuilder builder3 = new ProcessDefinitionBuilder().createNewInstance("p3", "1.0"); final String blockingGroovyScript2 = "org.bonitasoft.engine.test.BPMLocalIT.tryAcquireSemaphore2();\nreturn true;"; builder3.addActor(ACTOR_NAME); builder3.addUserTask("step1", ACTOR_NAME); builder3.addAutomaticTask("step2"); builder3.addTransition("step1", "step2", new ExpressionBuilder().createGroovyScriptExpression("blockingGroovyScript2", blockingGroovyScript2, Boolean.class.getName())); builder3.addUserTask("ustep2", ACTOR_NAME); builder3.addTransition("step2", "ustep2"); final ProcessDefinition p3 = deployAndEnableProcessWithActor(builder3.done(), ACTOR_NAME, john); // Block all 3 tasks BlockingConnector.semaphore.acquire(); semaphore1.acquire(); semaphore2.acquire(); final ProcessInstance pi1 = getProcessAPI().startProcess(p1.getId()); final ProcessInstance pi2 = getProcessAPI().startProcess(p2.getId()); final ProcessInstance pi3 = getProcessAPI().startProcess(p3.getId()); waitForUserTaskAndExecuteIt(pi1, "step1", john); waitForUserTaskAndExecuteIt(pi2, "step1", john); waitForUserTaskAndExecuteIt(pi3, "step1", john); logout(); final PlatformSession loginPlatform = loginOnPlatform(); final PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(loginPlatform); // stop node and in the same time release the semaphores to unlock works final Thread thread = new Thread(() -> { try { Thread.sleep(200); } catch (final InterruptedException e) { e.printStackTrace(); } BlockingConnector.semaphore.release(); semaphore1.release(); semaphore2.release(); }); thread.start(); platformAPI.stopNode(); // release them (work will fail, node is stopped) thread.join(1000); Thread.sleep(50); platformAPI.startNode(); logoutOnPlatform(loginPlatform); loginWithTechnicalUser(); //during stop node some flow node can be put in failed state retryFailedFlowNodes(); // check we have all task ready waitForPendingTasks(john.getId(), 3); disableAndDeleteProcess(p1.getId()); disableAndDeleteProcess(p2.getId()); disableAndDeleteProcess(p3.getId()); } private void retryFailedFlowNodes() throws SearchException, ActivityInstanceNotFoundException, ActivityExecutionException { List failedFlowNodes = getFailedFlowNodes(); for (FlowNodeInstance failedFlowNode : failedFlowNodes) { getProcessAPI().retryTask(failedFlowNode.getId()); } } private List getFailedFlowNodes() throws SearchException { SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 3); builder.filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, ActivityStates.FAILED_STATE); SearchResult searchResult = getProcessAPI().searchFlowNodeInstances(builder.done()); return searchResult.getResult(); } public static void tryAcquireSemaphore1() throws InterruptedException { System.out.println("tryAcquire semaphore1"); semaphore1.tryAcquire(15, TimeUnit.SECONDS); semaphore1.release(); System.out.println("release semaphore1"); } public static void tryAcquireSemaphore2() throws InterruptedException { System.out.println("tryAcquire semaphore2"); semaphore2.tryAcquire(15, TimeUnit.SECONDS); semaphore2.release(); System.out.println("release semaphore2"); } @Test public void getPlatformVersion() throws BonitaException, IOException { logout(); final PlatformSession platformSession = loginOnPlatform(); final PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(platformSession); final Platform platform = platformAPI.getPlatform(); logoutOnPlatform(platformSession); loginOnDefaultTenantWith(USERNAME, PASSWORD); final String platformVersionToTest = getBonitaVersion(); assertNotNull("can't find the platform", platform); assertEquals("platformAdmin", platform.getCreatedBy()); assertEquals(platformVersionToTest, platform.getVersion()); assertEquals(platformVersionToTest, platform.getInitialVersion()); } public static String getBonitaVersion() throws IOException { String version = System.getProperty("bonita.version");// works in maven if (version == null) { // when running tests in eclipse get it from the pom.xml final File file = new File("pom.xml"); final String pomContent = Files.readString(file.toPath()); final Pattern pattern = Pattern.compile("(.*)"); final Matcher matcher = pattern.matcher(pomContent); matcher.find(); version = matcher.group(1); } return version; } private static class GetDependenciesIds implements Callable> { private final APISession session; private final long processDefinitionId; private final DependencyService dependencyService; private final int startIndex; private final int maxResult; public GetDependenciesIds(final APISession session, final long processDefinitionId, final DependencyService dependencyService, final int startIndex, final int maxResult) { this.session = session; this.processDefinitionId = processDefinitionId; this.dependencyService = dependencyService; this.startIndex = startIndex; this.maxResult = maxResult; } @Override public List call() throws Exception { setSessionInfo(session); // the session was cleaned by api call. This must be improved return dependencyService.getDependencyIds(processDefinitionId, ScopeType.PROCESS, startIndex, maxResult); } } private static class GetSDependency implements Callable { private final long dependencyId; private final DependencyService dependencyService; public GetSDependency(final long dependencyId, final DependencyService dependencyService) { this.dependencyId = dependencyId; this.dependencyService = dependencyService; } @Override public AbstractSDependency call() throws Exception { return dependencyService.getDependency(dependencyId); } } @Test public void should_warn_when_setting_remote_connection_with_local_engine() throws Exception { //when Logger logger = Logger.getLogger(APITypeManager.class.getName()); TestHandler testHandler = new TestHandler(); logger.addHandler(testHandler); APITypeManager.setAPITypeAndParams(ApiAccessType.HTTP, Collections.emptyMap()); //then String log = testHandler.getLogs(); APITypeManager.setAPITypeAndParams(ApiAccessType.LOCAL, Collections.emptyMap()); logger.removeHandler(testHandler); Assertions.assertThat(log).contains( "You are declaring an API access to Bonita Engine as a remote connection, whereas it looks like you are running in the same JVM."); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/BlockingConnector.java ================================================ /** * 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.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import org.bonitasoft.engine.connector.AbstractConnector; public class BlockingConnector extends AbstractConnector { public static Semaphore semaphore = new Semaphore(1); @Override public void validateInputParameters() { } @Override protected void executeBusinessLogic() { try { System.out.println("Try aqcuire in connector"); semaphore.tryAcquire(15, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } semaphore.release(); System.out.println("semaphore in connector released"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/CommonAPILocalIT.java ================================================ /** * 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.util.concurrent.Callable; import org.bonitasoft.engine.CommonAPIIT; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; /** * @author Baptiste Mesta */ public class CommonAPILocalIT extends CommonAPIIT { protected ServiceAccessor getServiceAccessor() { try { return ServiceAccessorSingleton.getInstance(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } protected static void setSessionInfo(final APISession session) throws Exception { final SessionAccessor sessionAccessor = ServiceAccessorFactory.getInstance().createSessionAccessor(); sessionAccessor.setSessionId(session.getId()); } protected static void cleanSession() throws Exception { final SessionAccessor sessionAccessor = ServiceAccessorFactory.getInstance().createSessionAccessor(); sessionAccessor.deleteSessionId(); } protected T inTx(Callable callable) throws Exception { return getServiceAccessor().getUserTransactionService().executeInTransaction(callable); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/PermissionAPIIT.java ================================================ /** * 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 static org.assertj.core.api.Assertions.assertThat; import java.util.Collections; import java.util.Map; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.api.permission.APICallContext; import org.junit.Test; /** * @author Baptiste Mesta */ public class PermissionAPIIT extends TestWithUser { @Test public void should_allow_with_provided_dynamic_rule() throws Exception { APICallContext apiCallContext = new APICallContext("GET", "bpm", "process", null, "", "body"); //when boolean isAllowedWithoutFilter = getPermissionAPI() .isAuthorized(apiCallContext); //then assertThat(isAllowedWithoutFilter).isFalse(); //given apiCallContext = getApiCallContextWithUserFilter(getSession().getUserId()); //when boolean isAllowedWithCurrentUserFilter = getPermissionAPI() .isAuthorized(apiCallContext); //then assertThat(isAllowedWithCurrentUserFilter).isTrue(); //given apiCallContext = getApiCallContextWithUserFilter(99999L); //when boolean isAllowedWithOtherUserFilter = getPermissionAPI() .isAuthorized(apiCallContext); //then assertThat(isAllowedWithOtherUserFilter).isFalse(); } private static APICallContext getApiCallContextWithUserFilter(long userId) { return new APICallContext("GET", "bpm", "process", null, "", "body") { @Override public Map getFilters() { return Collections.singletonMap("user_id", String.valueOf(userId)); } }; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/ProcessArchiveIT.java ================================================ /** * 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 static org.junit.Assert.*; import java.util.List; import java.util.concurrent.Callable; import org.bonitasoft.engine.bpm.comment.Comment; import org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceNotFoundException; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.data.instance.model.archive.SADataInstance; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.transaction.UserTransactionService; import org.junit.After; import org.junit.Before; import org.junit.Test; public class ProcessArchiveIT extends CommonAPILocalIT { private User john; @Before public void beforeTest() throws BonitaException { loginWithTechnicalUser(); john = createUser(USERNAME, "bpm"); } @After public void afterTest() throws BonitaException { deleteUser(john); logout(); } @Test() public void deleteProcessDefinitionDeleteArchivedInstancesWithDataAndComments() throws Exception { setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved final ServiceAccessor serviceAccessor = getServiceAccessor(); final SCommentService commentService = serviceAccessor.getCommentService(); final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService(); final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService(); final long initialNumberOfArchivedProcessInstance = getProcessAPI().getNumberOfArchivedProcessInstances(); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("ProcessToDelete", "1.0"); processDefinitionBuilder.addActor("actor"); processDefinitionBuilder.addShortTextData("procData", new ExpressionBuilder().createConstantStringExpression("procDataValue")); final UserTaskDefinitionBuilder userTaskDefinitionBuilder = processDefinitionBuilder.addUserTask("step1", "actor"); userTaskDefinitionBuilder.addOperation(new LeftOperandBuilder().createNewInstance("procData").done(), OperatorType.ASSIGNMENT, "=", null, new ExpressionBuilder().createConstantStringExpression("updated proc value")); userTaskDefinitionBuilder.addOperation(new LeftOperandBuilder().createNewInstance("activityData").done(), OperatorType.ASSIGNMENT, "=", null, new ExpressionBuilder().createConstantStringExpression("updated a value")); processDefinitionBuilder.addShortTextData("activityData", new ExpressionBuilder().createConstantStringExpression("activityDataBalue")).getProcess(); final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, "actor", john); final ProcessInstance p1 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance p2 = getProcessAPI().startProcess(processDefinition.getId()); final ProcessInstance p3 = getProcessAPI().startProcess(processDefinition.getId()); final long step1Id = waitForUserTask(p1, "step1"); getProcessAPI().addProcessComment(p1.getId(), "A cool comment on p1"); getProcessAPI().addProcessComment(p2.getId(), "A cool comment on p2"); getProcessAPI().addProcessComment(p3.getId(), "A cool comment on p3"); final List comments = getProcessAPI().searchComments( new SearchOptionsBuilder(0, 10).filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, p1.getId()).done()) .getResult(); assertEquals(1, comments.size()); assertEquals("A cool comment on p1", comments.get(0).getContent()); setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved userTransactionService.executeInTransaction((Callable) () -> { assertEquals(3, commentService.getNumberOfComments(QueryOptions.countQueryOptions())); assertEquals(0, commentService.getNumberOfArchivedComments(QueryOptions.countQueryOptions())); return null; }); final DataInstance activityDataInstance = getProcessAPI().getActivityDataInstance("activityData", step1Id); final DataInstance processDataInstance = getProcessAPI().getProcessDataInstance("procData", p1.getId()); assertNotNull(activityDataInstance); assignAndExecuteStep(step1Id, john.getId()); waitForUserTaskAndExecuteIt(p2, "step1", john); waitForUserTaskAndExecuteIt(p3, "step1", john); userTransactionService.executeInTransaction((Callable) () -> { final SADataInstance saActDataInstances = dataInstanceService .getSADataInstance(activityDataInstance.getId(), System.currentTimeMillis()); assertNotNull(saActDataInstances); final SADataInstance saProcDataInstances = dataInstanceService .getSADataInstance(processDataInstance.getId(), System.currentTimeMillis()); assertNotNull(saProcDataInstances); return null; }); waitForProcessToFinish(p1); waitForProcessToFinish(p2); waitForProcessToFinish(p3); assertEquals(initialNumberOfArchivedProcessInstance + 3, getProcessAPI().getNumberOfArchivedProcessInstances()); setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved userTransactionService.executeInTransaction((Callable) () -> { assertEquals(0, commentService.getNumberOfComments(QueryOptions.countQueryOptions())); // 3 comments + 3 system comments assertEquals(6, commentService.getNumberOfArchivedComments(QueryOptions.countQueryOptions())); return null; }); disableAndDeleteProcess(processDefinition); assertEquals(initialNumberOfArchivedProcessInstance, getProcessAPI().getNumberOfArchivedProcessInstances()); setSessionInfo(getSession()); // the session was cleaned by api call. This must be improved userTransactionService.executeInTransaction((Callable) () -> { final SADataInstance saActDataInstances = dataInstanceService .getSADataInstance(activityDataInstance.getId(), System.currentTimeMillis()); final SADataInstance saProcDataInstances = dataInstanceService .getSADataInstance(processDataInstance.getId(), System.currentTimeMillis()); assertNull(saActDataInstances); assertNull(saProcDataInstances); assertEquals(0, commentService.getNumberOfComments(QueryOptions.countQueryOptions())); assertEquals(0, commentService.getNumberOfArchivedComments(QueryOptions.countQueryOptions())); return null; }); cleanSession(); } @Test public void archivedFlowNodeInstance() throws Exception { logout(); loginOnDefaultTenantWith(USERNAME, "bpm"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("ProcessToDelete", "1.0"); processDefinitionBuilder.addActor("actor"); processDefinitionBuilder.addUserTask("step1", "actor").addDescription("My Description") .addDisplayName(new ExpressionBuilder().createConstantStringExpression("My Display Name")) .addDisplayDescriptionAfterCompletion( new ExpressionBuilder().createConstantStringExpression("My Display Description")); final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.getProcess(); final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(designProcessDefinition, "actor", john); final ProcessDefinitionBuilder callingProcess = new ProcessDefinitionBuilder().createNewInstance("Caller", "1.0"); callingProcess.addCallActivity("call", new ExpressionBuilder().createConstantStringExpression("ProcessToDelete"), new ExpressionBuilder().createConstantStringExpression("1.0")); final ProcessDefinition callingProcessDef = deployAndEnableProcess(callingProcess.getProcess()); final ProcessInstance p1 = getProcessAPI().startProcess(callingProcessDef.getId()); final ActivityInstance userTask = waitForUserTaskAndExecuteAndGetIt(p1, "step1", john); waitForProcessToFinish(p1); waitForArchivedActivity(userTask.getId(), TestStates.NORMAL_FINAL); final ArchivedActivityInstance archivedUserTask = getProcessAPI().getArchivedActivityInstance(userTask.getId()); assertEquals("My Description", archivedUserTask.getDescription()); assertEquals("My Display Description", archivedUserTask.getDisplayDescription()); assertEquals("My Display Name", archivedUserTask.getDisplayName()); assertEquals("step1", archivedUserTask.getName()); assertEquals(archivedUserTask.getParentContainerId(), userTask.getParentContainerId()); assertEquals(archivedUserTask.getRootContainerId(), userTask.getRootContainerId()); assertEquals(archivedUserTask.getFlownodeDefinitionId(), userTask.getFlownodeDefinitionId()); assertEquals(archivedUserTask.getType(), userTask.getType()); disableAndDeleteProcess(processDefinition); disableAndDeleteProcess(callingProcessDef); } @Test(expected = ArchivedFlowNodeInstanceNotFoundException.class) public void getArchivedFlowNodeInstanceNotFound() throws ArchivedFlowNodeInstanceNotFoundException { getProcessAPI().getArchivedFlowNodeInstance(123456789L); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/ProcessWithExpressionLocalIT.java ================================================ /** * 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 static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.TestWithUser; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.expression.*; import org.bonitasoft.engine.operation.LeftOperand; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.process.Employee; import org.bonitasoft.engine.process.Secretary; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.junit.Test; /** * @author Baptiste Mesta */ public class ProcessWithExpressionLocalIT extends TestWithUser { private ProcessDefinition processDefinition; private ProcessDefinition deployEmptyProcess() throws Exception { final DesignProcessDefinition done = new ProcessDefinitionBuilder() .createNewInstance("emptyProcess", String.valueOf(System.currentTimeMillis())) .done(); return deployAndEnableProcess(done); } @Test public void evaluateObjectComparisonWithGreaterThan() throws Exception { final Employee emp1 = new Employee("Doe", "John", 4); final Employee emp2 = new Employee("Doe", "Jane", 1); final Expression exprOperand1 = new ExpressionBuilder().createDataExpression("emp1", Employee.class.getName()); final Expression exprOperand2 = new ExpressionBuilder().createDataExpression("emp2", Employee.class.getName()); final Map inputValues = new HashMap<>(2); inputValues.put("emp1", emp1); inputValues.put("emp2", emp2); final ProcessDefinition processDefinition = deployEmptyProcess(); final Expression expression1 = new ExpressionBuilder().createComparisonExpression("GreaterThan", exprOperand1, ComparisonOperator.GREATER_THAN, exprOperand2); final Expression expression2 = new ExpressionBuilder().createComparisonExpression("GreaterThan", exprOperand2, ComparisonOperator.GREATER_THAN, exprOperand1); assertEquals(true, getProcessAPI().evaluateExpressionOnProcessDefinition(expression1, inputValues, processDefinition.getId())); assertEquals(false, getProcessAPI().evaluateExpressionOnProcessDefinition(expression2, inputValues, processDefinition.getId())); disableAndDeleteProcess(processDefinition); } @Test public void evaluateObjectComparisonWithEquals() throws Exception { final Employee emp1 = new Employee("Doe", "John", 3); final Employee emp2 = new Employee("Doe", "John", 3); final Employee emp3 = new Employee("Doe", "John", 4); final Expression exprOperand1 = new ExpressionBuilder().createDataExpression("emp1", Employee.class.getName()); final Expression exprOperand2 = new ExpressionBuilder().createDataExpression("emp2", Employee.class.getName()); final Expression exprOperand3 = new ExpressionBuilder().createDataExpression("emp3", Employee.class.getName()); final Map inputValues = new HashMap<>(3); inputValues.put("emp1", emp1); inputValues.put("emp2", emp2); inputValues.put("emp3", emp3); final ProcessDefinition processDefinition = deployEmptyProcess(); final Expression expression1 = new ExpressionBuilder().createComparisonExpression("Equals", exprOperand1, ComparisonOperator.EQUALS, exprOperand2); final Expression expression2 = new ExpressionBuilder().createComparisonExpression("Equals2", exprOperand1, ComparisonOperator.EQUALS, exprOperand3); assertEquals(true, getProcessAPI().evaluateExpressionOnProcessDefinition(expression1, inputValues, processDefinition.getId())); assertEquals(false, getProcessAPI().evaluateExpressionOnProcessDefinition(expression2, inputValues, processDefinition.getId())); disableAndDeleteProcess(processDefinition); } @Test public void evaluateComparisonExpressionWithObjectAndParentObject() throws Exception { final Employee employee = new Employee("Smith", "Ashley", 2); final Secretary secretary = new Secretary("Smith", "Ashley", 2); final Expression exprOperand1 = new ExpressionBuilder().createDataExpression("emp1", Employee.class.getName()); final Expression exprOperand2 = new ExpressionBuilder().createDataExpression("emp2", Secretary.class.getName()); final Map inputValues = new HashMap<>(2); inputValues.put("emp1", employee); inputValues.put("emp2", secretary); final ProcessDefinition processDefinition = deployEmptyProcess(); final Expression expression = new ExpressionBuilder().createComparisonExpression("Equals", exprOperand1, ComparisonOperator.EQUALS, exprOperand2); assertEquals(true, getProcessAPI().evaluateExpressionOnProcessDefinition(expression, inputValues, processDefinition.getId())); disableAndDeleteProcess(processDefinition); } @Test public void should_operation_with_transient_data_reevaluate_the_definition_if_lost() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance( "processWithTransientData", String.valueOf(System.currentTimeMillis())); builder.addActor("actor"); builder.addUserTask("step1", "actor") .addData("tData", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("The default value")) .isTransient(); processDefinition = deployAndEnableProcessWithActor(builder.done(), "actor", user); getProcessAPI().startProcess(processDefinition.getId()); final HumanTaskInstance step1 = waitForUserTaskAndGetIt("step1"); // evaluate the expression of the transient data, it should return the default value assertThat(evaluateTransientDataWithExpression(step1).get("tData")).isEqualTo("The default value"); // update it using operation updateDataWithOperation(step1, "The updated value", "tData"); // evaluate it: it should return the updated value assertThat(evaluateTransientDataWithExpression(step1).get("tData")).isEqualTo("The updated value"); // clear the cache getServiceAccessor().getCacheService().clear("transient_data"); // evaluate it: it should return the default value assertThat(evaluateTransientDataWithExpression(step1).get("tData")).isEqualTo("The default value"); disableAndDeleteProcess(processDefinition); } protected ServiceAccessor getServiceAccessor() { return ServiceAccessorSingleton.getInstance(); } private void updateDataWithOperation(final HumanTaskInstance step1, final String value, final String name) throws InvalidExpressionException, UpdateException { final Operation operation = new OperationBuilder().createNewInstance() .setLeftOperand(name, LeftOperand.TYPE_TRANSIENT_DATA) .setRightOperand(new ExpressionBuilder().createConstantStringExpression(value)) .setType(OperatorType.ASSIGNMENT).done(); getProcessAPI().updateActivityInstanceVariables(singletonList(operation), step1.getId(), null); } private Map evaluateTransientDataWithExpression(final HumanTaskInstance step1) throws ExpressionEvaluationException, InvalidExpressionException { final Map> expressionMap = new HashMap<>(); expressionMap.put(new ExpressionBuilder().createTransientDataExpression("tData", String.class.getName()), Collections.emptyMap()); return getProcessAPI().evaluateExpressionsOnActivityInstance(step1.getId(), expressionMap); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/test/TestHandler.java ================================================ /** * 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.util.logging.Handler; import java.util.logging.LogRecord; /** * @author Baptiste Mesta */ public class TestHandler extends Handler { private StringBuilder stb = new StringBuilder(); @Override public void publish(LogRecord record) { stb.append(record.getMessage()).append('\n'); } @Override public void flush() { } @Override public void close() throws SecurityException { stb = null; } public String getLogs() { return stb.toString(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/java/org/bonitasoft/engine/work/WorkServiceIT.java ================================================ /** * Copyright (C) 2020 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.work; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.bonitasoft.engine.bpm.CommonBPMServicesTest; import org.bonitasoft.engine.commons.exceptions.SRetryableException; import org.bonitasoft.engine.execution.work.BPMWorkFactory; import org.junit.Before; import org.junit.Test; /** * @author Baptiste Mesta. */ public class WorkServiceIT extends CommonBPMServicesTest { private RetryingWorkExecutorService workExecutorService; private BPMWorkFactory workFactory; private int originalDelay; @Before public void before() { this.workExecutorService = (RetryingWorkExecutorService) getServiceAccessor().getWorkExecutorService(); this.workFactory = getServiceAccessor().getBPMWorkFactory(); originalDelay = workExecutorService.getDelay(); workExecutorService.setDelay(1); } @Override public void after() throws Exception { workExecutorService.setDelay(originalDelay); super.after(); } @Test public void should_retry_work_that_fails_2_times() throws Exception { AtomicInteger failureCounter = new AtomicInteger(0); AtomicBoolean workDone = new AtomicBoolean(false); BonitaWork myFailingWork = new BonitaWork() { @Override public String getDescription() { return null; } @Override public CompletableFuture work(Map context) throws Exception { if (failureCounter.get() < 2) { failureCounter.incrementAndGet(); throw new SRetryableException(new Exception("")); } workDone.set(true); return CompletableFuture.completedFuture(null); } @Override public void handleFailure(Throwable e, Map context) throws Exception { } }; workFactory.addExtension("MyFailingWork", workDescriptor -> myFailingWork); workExecutorService.execute(WorkDescriptor.create("MyFailingWork")); //work should complete await().until(workDone::get); //work should have failed 2 times assertThat(failureCounter.get()).isEqualTo(2); } @Test public void should_retry_work_that_always_fails() throws Exception { AtomicInteger failureCounter = new AtomicInteger(0); AtomicBoolean handleFailureCalled = new AtomicBoolean(false); BonitaWork myFailingWork = new BonitaWork() { @Override public String getDescription() { return null; } @Override public CompletableFuture work(Map context) throws Exception { failureCounter.incrementAndGet(); throw new SRetryableException(new Exception("")); } @Override public void handleFailure(Throwable e, Map context) throws Exception { handleFailureCalled.set(true); } }; workFactory.addExtension("MyFailingWork", workDescriptor -> myFailingWork); workExecutorService.execute(WorkDescriptor.create("MyFailingWork")); //work should complete await().until(handleFailureCalled::get); //work should be retried 11 times before failing completely assertThat(failureCounter.get()).isEqualTo(11); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/resources/applications-importer/mixedLegacyAndApplicationLinks.xml ================================================ Application 1 Description of Application 1 /app1.jpg Application 2 Application 3 Description of Application 3 /app3.jpg Menu level 1 Menu level 1.1 Empty menu Application 4 ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/resources/applications-importer/multipleApplicationLinks.xml ================================================ Application 1 Description of Application 1 /app1.jpg Application 2 ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/resources/applications-importer/oneApplicationLink.xml ================================================ Application 1 Description of Application 1 /app1.jpg ================================================ FILE: bonita-integration-tests/bonita-integration-tests-local/src/test/resources/org/bonitasoft/engine/classloader/resource.txt ================================================ Text resource ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/build.gradle ================================================ plugins { id('bonita-tests') } dependencies { implementation project(':bpm:bonita-web-server') implementation project(':bonita-test-api') implementation libs.slf4jApi implementation libs.commonsIO implementation libs.jakartaServletApi implementation libs.junit4 implementation libs.springTest testImplementation libs.hamcrest testImplementation libs.mockitoCore testImplementation libs.assertj testImplementation libs.awaitility testImplementation libs.tomcatEmbedCore } java { withSourcesJar() } group = 'org.bonitasoft.console' description = 'Bonita Integration Tests Web' ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/AbstractJUnitTest.java ================================================ /** * Copyright (C) 2022 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.test.toolkit; import org.bonitasoft.engine.test.junit.BonitaEngineRule; import org.bonitasoft.test.toolkit.organization.AdminUser; import org.bonitasoft.test.toolkit.organization.TestToolkitCtx; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.junit.After; import org.junit.Before; import org.junit.Rule; /** * Template of JUnit Test. Do initialization as well as necessary clean up. * * @author Vincent Elcrin, Anthony Birembaut */ public abstract class AbstractJUnitTest { @Rule public BonitaEngineRule bonitaEngineRule = createBonitaEngineRule(); protected BonitaEngineRule createBonitaEngineRule() { return BonitaEngineRule.create().withCleanAfterTest(); } @Before public final void aaSetUp() throws Exception { getContext().check(); getContext().setInitiator(getInitiator()); testSetUp(); } @After public final void zzTearDown() throws Exception { testTearDown(); final AdminUser adminUser = TestToolkitCtx.getInstance().getAdminUser(); for (TestUser testUser : TestUserFactory.getInstance().getUserList().values()) { adminUser.delete(testUser); } getContext().clearSession(); } /** * Initiator is a convenient notion to set a test user as source of all transactions done with the framework via its * API Session. * It still can be overridden for all methods of the framework and also can be set for the session with * {@link TestToolkitCtx#setInitiator(TestUser)} * * @return * @throws Exception */ protected abstract TestUser getInitiator(); /** * Define context to use during the test. Two implementations exist. TestToolkitCtx and TestToolkitCtx (SP version) * * @return */ protected abstract TestToolkitCtx getContext(); /** * JUnit's {@link Before} implementation. * * @throws Exception */ protected abstract void testSetUp() throws Exception; /** * JUnit's {@link After} implementation. * * @throws Exception */ protected abstract void testTearDown() throws Exception; } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/AbstractManualTask.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.bpm; /** * @author Vincent Elcrin */ public abstract class AbstractManualTask { public abstract long getId(); public abstract String getName(); public abstract String getDescription(); // ///////////////////////////////////////////////////////////////////////////// // / Test state // ///////////////////////////////////////////////////////////////////////////// } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/ProcessVariable.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.bpm; import java.util.Date; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.InvalidExpressionException; /** * @author Colin PUY */ public class ProcessVariable { private final String name; private final Class clazz; private final Expression defaultValue; public static ProcessVariable aStringVariable(String name, String defaultValue) throws InvalidExpressionException { return new ProcessVariable(name, String.class, new ExpressionBuilder().createConstantStringExpression(defaultValue)); } public static ProcessVariable aLongVariable(String name, long defaultValue) throws InvalidExpressionException { return new ProcessVariable(name, Long.class, new ExpressionBuilder().createConstantLongExpression(defaultValue)); } public static ProcessVariable aDateVariable(String name, String defaultValue) throws InvalidExpressionException { return new ProcessVariable(name, Date.class, new ExpressionBuilder().createConstantDateExpression(defaultValue)); } public ProcessVariable(String name, Class clazz, Expression defaultValue) { this.name = name; this.clazz = clazz; this.defaultValue = defaultValue; } public String getName() { return name; } public String getClassName() { return clazz.getName(); } public Expression getDefaultValue() { return defaultValue; } public static ProcessVariable createLongVariable(long value) throws InvalidExpressionException { return new ProcessVariable("aLongVariable", Long.class, new ExpressionBuilder().createConstantLongExpression(value)); } public static ProcessVariable createIntVariable(int value) throws InvalidExpressionException { return new ProcessVariable("aIntVariable", Integer.class, new ExpressionBuilder().createConstantIntegerExpression(value)); } public static ProcessVariable createStringVariable(String value) throws InvalidExpressionException { return new ProcessVariable("aStringVariable", String.class, new ExpressionBuilder().createConstantStringExpression(value)); } public static ProcessVariable createBooleanVariable(Boolean value) throws InvalidExpressionException { return new ProcessVariable("aBooleanVariable", Boolean.class, new ExpressionBuilder().createConstantBooleanExpression(value)); } public static ProcessVariable createDoubleVariable(Double value) throws InvalidExpressionException { return new ProcessVariable("aDoubleVariable", Double.class, new ExpressionBuilder().createConstantDoubleExpression(value)); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestActor.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.bpm; /** * @author Vincent Elcrin */ public interface TestActor { /** * Id used by the process to define the actor */ long getId(); } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestCase.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.bpm; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.ActivityStates; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.test.toolkit.exception.NextActivityIsNotAllowedStateException; import org.bonitasoft.test.toolkit.exception.NoActivityLeftException; import org.bonitasoft.test.toolkit.exception.TestToolkitException; import org.bonitasoft.test.toolkit.organization.TestToolkitCtx; import org.bonitasoft.test.toolkit.organization.TestUser; /** * @author Vincent Elcrin */ public class TestCase { private final ProcessInstance processInstance; public final static int GET_NEXT_NB_ATTEMPT = 30; public final static int SLEEP_TIME_MS = 100; public final static String READY_STATE = "started"; public TestCase(final ProcessInstance instance) { processInstance = instance; } /** * Wait until the process return the state in parameter */ public void waitProcessState(final APISession apiSession, final String state) { final ProcessAPI processAPI = TestProcess.getProcessAPI(apiSession); ProcessInstance instance = null; for (int i = 0; i < GET_NEXT_NB_ATTEMPT; i++) { try { instance = processAPI.getProcessInstance(processInstance.getId()); if (instance != null && state.equals(instance.getState())) { break; } Thread.sleep(SLEEP_TIME_MS); } catch (final Exception e) { throw new TestToolkitException("Can't get process instance <" + processInstance.getId() + ">.", e); } } if (instance == null || !state.equals(instance.getState())) { throw new TestToolkitException( "Instance <" + processInstance.getId() + "> has not reached the expected state <" + state + ">."); } } /** * Search and get next human task */ public TestHumanTask getNextHumanTask(final APISession apiSession) { final ProcessAPI processAPI = TestProcess.getProcessAPI(apiSession); final SearchOptionsBuilder searchOptBuilder = new SearchOptionsBuilder(0, 1); searchOptBuilder.filter(HumanTaskInstanceSearchDescriptor.STATE_NAME, ActivityStates.READY_STATE); /* * Get next workable human task. (e.g. not in initialization state) */ HumanTaskInstance humanTask = null; SearchResult result = null; for (int i = 0; i < GET_NEXT_NB_ATTEMPT; i++) { try { result = processAPI.searchHumanTaskInstances(searchOptBuilder.done()); if (!result.getResult().isEmpty()) { humanTask = result.getResult().get(0); break; } Thread.sleep(SLEEP_TIME_MS); } catch (final InvalidSessionException e) { throw new TestToolkitException("Can't search human task instances. Invalid session", e); } catch (final SearchException e) { throw new TestToolkitException("Can't search human task instances", e); } catch (final InterruptedException e) { throw new TestToolkitException("Interrupted during searching process", e); } } if (humanTask != null) { return new TestHumanTask(humanTask); } else { if (result.getResult().size() > 0) { throw new NextActivityIsNotAllowedStateException(result.getResult().get(0)); } else { throw new NoActivityLeftException(); } } } public TestHumanTask getNextHumanTask() { return getNextHumanTask(TestToolkitCtx.getInstance().getInitiator().getSession()); } public ProcessInstance getProcessInstance() { return processInstance; } private ArchivedProcessInstance getArchive(final APISession apiSession) { final ProcessAPI processAPI = TestProcess.getProcessAPI(apiSession); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 1); searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, getId()); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.ARCHIVE_DATE, Order.DESC); SearchResult searchArchivedProcessInstances; try { searchArchivedProcessInstances = processAPI .searchArchivedProcessInstancesInAllStates(searchOptionsBuilder.done()); } catch (final SearchException se) { throw new TestToolkitException("Can't get process instance archived for <" + getId() + ">", se); } if (searchArchivedProcessInstances != null && searchArchivedProcessInstances.getCount() > 0) { return searchArchivedProcessInstances.getResult().get(0); } else { throw new TestToolkitException("Can't get process instance archived for <" + getId() + ">"); } } public ArchivedProcessInstance getArchive(final TestUser initiator) { return getArchive(initiator.getSession()); } public ArchivedProcessInstance getArchive() { return getArchive(TestToolkitCtx.getInstance().getInitiator()); } public long getId() { return processInstance.getId(); } // /////////////////////////////////////////////////////////////////// // / Execution // /////////////////////////////////////////////////////////////////// public void execute(final APISession apiSession) { try { final TestHumanTask nextActivityInstance = getNextHumanTask(apiSession); if (nextActivityInstance != null) { nextActivityInstance.execute(apiSession); } } catch (final NoActivityLeftException e) { // there were no activity in the process } } public void execute(final TestUser user) { execute(user.getSession()); } public void execute() { execute(TestToolkitCtx.getInstance().getInitiator()); } // /////////////////////////////////////////////////////////////////// // / Comments // /////////////////////////////////////////////////////////////////// private void addComment(final APISession apiSession, final String content) { final ProcessAPI processAPI = TestProcess.getProcessAPI(apiSession); try { processAPI.addProcessComment(processInstance.getId(), content); } catch (final Exception e) { throw new TestToolkitException("Can't add comment to <" + processInstance.getId() + ">", e); } } private void addComment(final TestUser initiator, final String content) { addComment(initiator.getSession(), content); } public void addComments(final TestUser initiator, final int nbOfComments, final String content) { for (int i = 0; i < nbOfComments; i++) { addComment(initiator, content + i); } } public void addComment(final String content) { addComment(TestToolkitCtx.getInstance().getInitiator(), content); } public void cancel() { TestUser testUser = TestToolkitCtx.getInstance().getInitiator(); final ProcessAPI processAPI = TestProcess.getProcessAPI(testUser.getSession()); try { processAPI.cancelProcessInstance(processInstance.getId()); } catch (ProcessInstanceNotFoundException e) { throw new TestToolkitException("Can't get process instance <" + getId() + ">", e); } catch (UpdateException e) { throw new TestToolkitException("Can't update process instance <" + getId() + ">", e); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestCaseFactory.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.bpm; import org.bonitasoft.test.toolkit.organization.TestUser; /** * @author Colin PUY */ public class TestCaseFactory { public static TestCase createRandomCase(TestUser initiator) { return TestProcessFactory.createRandomResolvedProcess(initiator).startCase(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestCategory.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.bpm; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.engine.bpm.category.CategoryCriterion; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.test.toolkit.exception.TestToolkitException; import org.bonitasoft.test.toolkit.organization.TestToolkitCtx; /** * @author Séverin Moussel */ public class TestCategory { private final Category category; /** * Default Constructor. */ public TestCategory(final Category category) { this.category = category; } /** * Default Constructor. */ public TestCategory(final APISession apiSession, final String name, final String description) { this(createCategory(apiSession, name, description)); } public TestCategory(final String name, final String description) { this(TestToolkitCtx.getInstance().getInitiator().getSession(), name, description); } private static Category createCategory(final APISession apiSession, final String name, final String description) { try { return TenantAPIAccessor.getProcessAPI(apiSession).createCategory(name, description); } catch (final Exception e) { throw new TestToolkitException("Can't create category.", e); } } public void delete(final APISession apiSession) { try { TenantAPIAccessor.getProcessAPI(apiSession).deleteCategory(this.category.getId()); } catch (final Exception e) { throw new TestToolkitException("Can't delete category", e); } } public void delete() { try { delete(TestToolkitCtx.getInstance().getInitiator().getSession()); } catch (final Exception e) { throw new TestToolkitException("Can't delete category", e); } } public Category getCategory() { return this.category; } public static List getAll(final APISession apiSession) { try { final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(apiSession); final int nbCat = (int) processAPI.getNumberOfCategories(); final List catList = processAPI.getCategories(0, nbCat, CategoryCriterion.NAME_ASC); final List testCatList = new ArrayList<>(); for (final Category cat : catList) { testCatList.add(new TestCategory(cat)); } return testCatList; } catch (final Exception e) { throw new TestToolkitException("Can't get categories", e); } } public long getId() { return this.category.getId(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestCategoryFactory.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.bpm; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import org.bonitasoft.engine.session.APISession; /** * @author Séverin Moussel */ public class TestCategoryFactory { private final Map categories = new HashMap<>(); private TestCategory _getCategory(final String name, final String description) { if (!this.categories.containsKey(name)) { this.categories.put(name, new TestCategory(name, description)); } return this.categories.get(name); } private static String getRandomString() { return String.valueOf(new Random().nextLong()); } private static final TestCategoryFactory instance = new TestCategoryFactory(); public static TestCategoryFactory getInstance() { return instance; } public static TestCategory getCategory(final String name, final String description) { return getInstance()._getCategory(name, description); } public static List getCategories(final int count) { final List results = new ArrayList<>(count); for (int i = 0; i < count; i++) { results.add(getRandomCategory()); } return results; } public static List getAllCategories(final APISession apiSession) { final List all = TestCategory.getAll(apiSession); for (TestCategory testCategory : all) { getInstance().getCategoriesList().put(testCategory.getCategory().getName(), testCategory); } return all; } public static TestCategory getRandomCategory() { return getCategory(getRandomString(), getRandomString()); } public void clear() { for (TestCategory testCategory : getCategoriesList().values()) { testCategory.delete(); } getCategoriesList().clear(); } /** * @return the userList */ private Map getCategoriesList() { return this.categories; } public void check() { if (!getCategoriesList().isEmpty()) { throw new RuntimeException(this.getClass().getName() + " cannot be reset because the list is not empty: " + getCategoriesList()); } } public static void removeTestCategoryFromList(TestCategory category) { getInstance().getCategoriesList().remove(category.getCategory().getName()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestHumanTask.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.bpm; import java.util.Collections; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.data.DataNotFoundException; import org.bonitasoft.engine.bpm.flownode.ActivityExecutionException; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.test.toolkit.exception.TestToolkitException; import org.bonitasoft.test.toolkit.organization.TestToolkitCtx; import org.bonitasoft.test.toolkit.organization.TestUser; /** * @author Vincent Elcrin */ public class TestHumanTask extends AbstractManualTask { private HumanTaskInstance humanTaskInstance; private final static int GET_NEXT_NB_ATTEMPT = 30; private final static int SLEEP_TIME_MS = 100; /** * Default Constructor. */ public TestHumanTask(final ActivityInstance activityInstance) { assert activityInstance instanceof HumanTaskInstance; humanTaskInstance = (HumanTaskInstance) activityInstance; } public HumanTaskInstance getHumanTaskInstance() { return humanTaskInstance; } /** * @return the processInstance */ private HumanTaskInstance fetchHumanTaskInstance(final APISession apiSession) { try { return TestProcess.getProcessAPI(apiSession).getHumanTaskInstance(getId()); } catch (final Exception e) { throw new TestToolkitException("Can't get humanTask instance for <" + getId() + ">", e); } } public void refreshHumanTaskInstanceInstance() { humanTaskInstance = fetchHumanTaskInstance(TestToolkitCtx.getInstance().getInitiator().getSession()); } public DataInstance getDataInstance(final String dataName) { try { return TestProcess.getProcessAPI(TestToolkitCtx.getInstance().getInitiator().getSession()) .getActivityDataInstance(dataName, humanTaskInstance.getId()); } catch (final DataNotFoundException e) { throw new TestToolkitException("Unable to find dataInstance " + dataName, e); } } /* * (non-Javadoc) * @see org.bonitasoft.test.AbstractManualTask#getId() */ @Override public long getId() { return humanTaskInstance.getId(); } /* * (non-Javadoc) * @see org.bonitasoft.test.toolkit.bpm.AbstractManualTask#getDescription() */ @Override public String getDescription() { return humanTaskInstance.getDescription(); } /* * (non-Javadoc) * @see org.bonitasoft.test.toolkit.bpm.AbstractManualTask#getName() */ @Override public String getName() { return humanTaskInstance.getName(); } // ///////////////////////////////////////////////////////////////////////////// // / Assign // ///////////////////////////////////////////////////////////////////////////// private TestHumanTask assignTo(final APISession apiSession, final TestUser user) { final ProcessAPI processAPI = TestProcess.getProcessAPI(apiSession); try { processAPI.assignUserTask(humanTaskInstance.getId(), user.getId()); } catch (final Exception e) { throw new TestToolkitException("Can't assign user", e); } return this; } public TestHumanTask assignTo(final TestUser initiator, final TestUser user) { return assignTo(initiator.getSession(), user); } public TestHumanTask assignTo(final TestUser user) { return assignTo(TestToolkitCtx.getInstance().getInitiator(), user); } // //////////////////////////////////////////////////////////////////////////// // / Execute // //////////////////////////////////////////////////////////////////////////// public void executeUserTask(final TestUser executor) { final ProcessAPI processAPI = TestProcess.getProcessAPI(executor.getSession()); try { processAPI.executeUserTask(humanTaskInstance.getId(), Collections.emptyMap()); } catch (final Exception e) { throw new TestToolkitException("Can't execute user task <" + humanTaskInstance.getId() + ">.", e); } } public void execute(final APISession apiSession) { final ProcessAPI processAPI = TestProcess.getProcessAPI(apiSession); try { processAPI.executeFlowNode(humanTaskInstance.getId()); } catch (final Exception e) { throw new TestToolkitException("Can't execute activity <" + humanTaskInstance.getId() + ">.", e); } } public void archive(final APISession apiSession) { try { execute(apiSession); } catch (final TestToolkitException e) { if (!(e.getCause() instanceof ActivityExecutionException)) { throw e; } } } public void archive(final TestUser initiator) { archive(initiator.getSession()); } public void archive() { archive(TestToolkitCtx.getInstance().getInitiator()); } // ///////////////////////////////////////////////////////////////////////////////// // Convenient method // ///////////////////////////////////////////////////////////////////////////////// public void waitState(final String state) { for (int i = 0; i < GET_NEXT_NB_ATTEMPT; i++) { try { Thread.sleep(SLEEP_TIME_MS); } catch (final InterruptedException e) { throw new TestToolkitException( "Problem while waiting for state <" + state + "> for human task <" + getId() + ">. Interrupted", e); } refreshHumanTaskInstanceInstance(); if (getHumanTaskInstance() != null && state.equals(getHumanTaskInstance().getState())) { break; } } if (getHumanTaskInstance() == null || !state.equals(getHumanTaskInstance().getState())) { throw new TestToolkitException( "Expected state <" + state + "> has not been reached for human task<" + getId() + ">."); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestProcess.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.bpm; import static java.util.stream.IntStream.range; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.actor.ActorCriterion; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.form.model.FormMappingModel; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.engine.bpm.category.CategoryCriterion; import org.bonitasoft.engine.bpm.flownode.ActivityDefinition; import org.bonitasoft.engine.bpm.flownode.UserTaskDefinition; import org.bonitasoft.engine.bpm.form.FormMappingDefinitionBuilder; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.form.FormMappingTarget; import org.bonitasoft.engine.form.FormMappingType; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.test.toolkit.exception.TestToolkitException; import org.bonitasoft.test.toolkit.organization.TestGroup; import org.bonitasoft.test.toolkit.organization.TestRole; import org.bonitasoft.test.toolkit.organization.TestToolkitCtx; import org.bonitasoft.test.toolkit.organization.TestUser; /** * @author Vincent Elcrin */ public class TestProcess { private final ProcessDefinition processDefinition; private boolean enabled = false; private final List actors = new ArrayList<>(); public TestProcess(final APISession apiSession, final ProcessDefinitionBuilder processDefinitionBuilder) { this.processDefinition = createProcessDefinition(apiSession, processDefinitionBuilder); } public TestProcess(final ProcessDefinitionBuilder processDefinitionBuilder) { this(getSession(), processDefinitionBuilder); } public TestProcess(final APISession apiSession, final BusinessArchiveBuilder businessArchiveBuilder) { this.processDefinition = deployProcessDefinition(apiSession, businessArchiveBuilder); } public TestProcess(final BusinessArchiveBuilder businessArchiveBuilder) { this(getSession(), businessArchiveBuilder); } private static APISession getSession() { return TestToolkitCtx.getInstance().getInitiator().getSession(); } /** * Create an archive and deploy process */ private ProcessDefinition createProcessDefinition(final APISession apiSession, final ProcessDefinitionBuilder processDefinitionBuilder) { try { return deployProcessDefinition(apiSession, new BusinessArchiveBuilder().createNewBusinessArchive() .setFormMappings(createDefaultProcessFormMapping(processDefinitionBuilder.getProcess())) .setProcessDefinition(processDefinitionBuilder.done())); } catch (final InvalidProcessDefinitionException e) { throw new TestToolkitException("Invalid process definition", e); } } public static FormMappingModel createDefaultProcessFormMapping(DesignProcessDefinition designProcessDefinition) { FormMappingModel formMappingModel = new FormMappingModel(); formMappingModel.addFormMapping(FormMappingDefinitionBuilder .buildFormMapping("http://url.com", FormMappingType.PROCESS_START, FormMappingTarget.URL).build()); formMappingModel.addFormMapping(FormMappingDefinitionBuilder .buildFormMapping("http://url.com", FormMappingType.PROCESS_OVERVIEW, FormMappingTarget.URL).build()); for (ActivityDefinition activityDefinition : designProcessDefinition.getFlowElementContainer() .getActivities()) { if (activityDefinition instanceof UserTaskDefinition) { formMappingModel.addFormMapping(FormMappingDefinitionBuilder .buildFormMapping("http://url.com", FormMappingType.TASK, FormMappingTarget.URL) .withTaskname(activityDefinition.getName()).build()); } } return formMappingModel; } /** * Deploy process from the archive */ private ProcessDefinition deployProcessDefinition(final APISession apiSession, final BusinessArchiveBuilder businessArchiveBuilder) { try { return getProcessAPI(apiSession).deploy(businessArchiveBuilder.done()); } catch (final Exception e) { throw new TestToolkitException("Can't deploy business archive.", e); } } protected static ProcessAPI getProcessAPI(final APISession apiSession) { ProcessAPI processAPI; try { processAPI = TenantAPIAccessor.getProcessAPI(apiSession); } catch (final InvalidSessionException e) { throw new TestToolkitException("Can't get process API. Invalid session", e); } catch (final BonitaHomeNotSetException e) { throw new TestToolkitException("Can't get process API. Bonita home not set", e); } catch (final ServerAPIException e) { throw new TestToolkitException("Can't get process API. Server API exception", e); } catch (final UnknownAPITypeException e) { throw new TestToolkitException("Can't get process API. Unknown API type", e); } return processAPI; } public ProcessDefinition getProcessDefinition() { return this.processDefinition; } public long getId() { return this.processDefinition.getId(); } /** * Set process enablement */ protected TestProcess setEnable(final APISession apiSession, final boolean enabled) { if (enabled && !this.enabled) { enableProcess(apiSession); } else if (!enabled && this.enabled) { disableProcess(apiSession); } this.enabled = enabled; return this; } protected void delete(final APISession apiSession) { try { // Delete all process instances long nbDeletedProcessInstances; do { nbDeletedProcessInstances = getProcessAPI(apiSession).deleteProcessInstances(processDefinition.getId(), 0, 100); } while (nbDeletedProcessInstances > 0); // Delete all archived process instances long nbDeletedArchivedProcessInstances; do { nbDeletedArchivedProcessInstances = getProcessAPI(apiSession) .deleteArchivedProcessInstances(processDefinition.getId(), 0, 100); } while (nbDeletedArchivedProcessInstances > 0); getProcessAPI(apiSession).deleteProcessDefinition(processDefinition.getId()); } catch (DeletionException e) { throw new TestToolkitException("Can't delete process <" + this.processDefinition.getId() + "> with name " + this.processDefinition.getName(), e); } } private void enableProcess(APISession apiSession) { try { getProcessAPI(apiSession).enableProcess(this.processDefinition.getId()); } catch (Exception e) { throw new TestToolkitException("Can't enable process <" + this.processDefinition.getId() + ">", e); } } private void disableProcess(APISession apiSession) { try { getProcessAPI(apiSession).disableProcess(this.processDefinition.getId()); } catch (Exception e) { throw new TestToolkitException("Can't disable process <" + this.processDefinition.getId() + ">", e); } } public TestProcess setEnable(final TestUser initiator, final boolean enabled) { return setEnable(initiator.getSession(), enabled); } public void delete(final TestUser initiator) { delete(initiator.getSession()); } public TestProcess enable() { return setEnable(TestToolkitCtx.getInstance().getInitiator(), true); } public TestProcess disable() { return setEnable(TestToolkitCtx.getInstance().getInitiator(), false); } public void delete() { delete(TestToolkitCtx.getInstance().getInitiator()); } /** * Add actors to enable process *

* TODO: Need to evolve to choose on which Actors category the actor will be added */ private void addActor(final APISession apiSession, final TestUser actor) { final ProcessAPI processAPI = getProcessAPI(apiSession); ActorInstance processActor; try { processActor = processAPI .getActors(this.processDefinition.getId(), 0, Integer.MAX_VALUE, ActorCriterion.NAME_ASC) .get(this.actors.size()); processAPI.addUserToActor(processActor.getId(), actor.getUser().getId()); this.actors.add(processActor); } catch (final Exception e) { throw new TestToolkitException("Can't get actors for <" + this.processDefinition.getId() + ">.", e); } } public TestProcess addActor(final TestGroup actor) { return addActor(TestToolkitCtx.getInstance().getInitiator().getSession(), actor); } private TestProcess addActor(final APISession apiSession, final TestGroup actor) { try { final ProcessAPI processAPI = getProcessAPI(apiSession); final ActorInstance processActor = processAPI .getActors(this.processDefinition.getId(), 0, Integer.MAX_VALUE, ActorCriterion.NAME_ASC).get( this.actors.size()); processAPI.addGroupToActor(processActor.getId(), actor.getId()); this.actors.add(processActor); } catch (final IndexOutOfBoundsException e) { final String message = "can't add actor to process " + this.processDefinition.getId() + " process definition has only " + this.actors.size() + " actors"; throw new TestToolkitException(message, e); } catch (final Exception e) { throw new TestToolkitException("can't add actor to process " + this.processDefinition.getId(), e); } return this; } public TestProcess addActor(final TestRole actor) { return addActor(TestToolkitCtx.getInstance().getInitiator().getSession(), actor); } private TestProcess addActor(final APISession apiSession, final TestRole actor) { try { final ProcessAPI processAPI = getProcessAPI(apiSession); final ActorInstance processActor = processAPI .getActors(this.processDefinition.getId(), 0, Integer.MAX_VALUE, ActorCriterion.NAME_ASC).get( this.actors.size()); processAPI.addRoleToActor(processActor.getId(), actor.getId()); this.actors.add(processActor); } catch (final IndexOutOfBoundsException e) { final String message = "can't add actor to process " + this.processDefinition.getId() + " process definition has only " + this.actors.size() + " actors"; throw new TestToolkitException(message, e); } catch (final Exception e) { throw new TestToolkitException("can't add actor to process " + this.processDefinition.getId(), e); } return this; } public TestProcess addActor(final TestUser initiator, final TestUser actor) { addActor(initiator.getSession(), actor); return this; } public TestProcess addActor(final TestUser actor) { return addActor(TestToolkitCtx.getInstance().getInitiator(), actor); } private TestCase startCase(final APISession apiSession) { setEnable(apiSession, true); TestCase testCase = new TestCase(createProcessInstance(apiSession)); testCase.waitProcessState(apiSession, TestCase.READY_STATE); return testCase; } protected ProcessInstance createProcessInstance(final APISession apiSession) { try { return getProcessAPI(apiSession).startProcess(apiSession.getUserId(), processDefinition.getId()); } catch (final Exception e) { throw new TestToolkitException("Can't start process <" + processDefinition.getId() + ">", e); } } public TestCase startCase(final TestUser initiator) { return startCase(initiator.getSession()); } public TestCase startCase() { return startCase(TestToolkitCtx.getInstance().getInitiator()); } /** * Start several cases */ public void startCases(final int number) { range(0, number).forEach(i -> startCase()); } // ///////////////////////////////////////////////////////////////////////////////////////// // / SUPERVISOR // ///////////////////////////////////////////////////////////////////////////////////////// /** * Add a user as process supervisor */ private void addSupervisor(final APISession apiSession, final TestUser user) { try { getProcessAPI(apiSession).createProcessSupervisorForUser(this.processDefinition.getId(), user.getId()); } catch (final Exception e) { throw new TestToolkitException("Unable to add supervisor", e); } } public void addSupervisor(final TestUser initiator, final TestUser user) { addSupervisor(initiator.getSession(), user); } public void addSupervisor(final TestUser user) { addSupervisor(TestToolkitCtx.getInstance().getInitiator(), user); } /** * Add a role as process supervisor */ private void addSupervisor(final APISession apiSession, final TestRole role) { try { getProcessAPI(apiSession).createProcessSupervisorForRole(this.processDefinition.getId(), role.getId()); } catch (final Exception e) { throw new TestToolkitException("Unable to add supervisor", e); } } public void addSupervisor(final TestUser initiator, final TestRole role) { addSupervisor(initiator.getSession(), role); } public void addSupervisor(final TestRole role) { addSupervisor(TestToolkitCtx.getInstance().getInitiator(), role); } /** * Add a group as process supervisor */ private TestProcess addSupervisor(final APISession apiSession, final TestGroup group) { try { getProcessAPI(apiSession).createProcessSupervisorForGroup(this.processDefinition.getId(), group.getId()); } catch (final Exception e) { throw new TestToolkitException("Unable to add supervisor", e); } return this; } public TestProcess addSupervisor(final TestUser initiator, final TestGroup group) { return addSupervisor(initiator.getSession(), group); } public TestProcess addSupervisor(final TestGroup group) { return addSupervisor(TestToolkitCtx.getInstance().getInitiator(), group); } /** * Add a memebership as process supervisor */ private TestProcess addSupervisor(final APISession apiSession, final TestGroup group, final TestRole role) { try { getProcessAPI(apiSession).createProcessSupervisorForMembership(this.processDefinition.getId(), group.getId(), role.getId()); } catch (final Exception e) { throw new TestToolkitException("Unable to add supervisor", e); } return this; } public void addSupervisor(final TestUser initiator, final TestGroup group, final TestRole role) { addSupervisor(initiator.getSession(), group, role); } public void addSupervisor(final TestGroup group, final TestRole role) { addSupervisor(TestToolkitCtx.getInstance().getInitiator(), group, role); } public void addCategory(final long categoryId) { try { TenantAPIAccessor.getProcessAPI(getSession()).addCategoriesToProcess(getId(), List.of(categoryId)); } catch (final Exception e) { throw new TestToolkitException("Can't add this process to this category. " + e.getMessage(), e); } } public List getCategories() { try { final List categories = TenantAPIAccessor.getProcessAPI(getSession()) .getCategoriesOfProcessDefinition(getId(), 0, 100, CategoryCriterion.NAME_ASC); final List results = new ArrayList<>(categories.size()); for (final Category category : categories) { results.add(new TestCategory(category)); } return results; } catch (final Exception e) { throw new TestToolkitException("Can't get categories", e); } } public List getActors() { return this.actors; } public List listOpenCases() throws SearchException { List processInstances = searchOpenedProcessInstances(); return convertToCasesList(processInstances); } public List listAllOpenCases() throws SearchException { List processInstances = searchProcessInstances(); return convertToCasesList(processInstances); } private List convertToCasesList(List processInstances) { List cases = new ArrayList<>(); for (ProcessInstance instance : processInstances) { cases.add(new TestCase(instance)); } return cases; } private List searchProcessInstances() throws SearchException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 100); builder.filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, getProcessDefinition().getId()); builder.differentFrom(ProcessInstanceSearchDescriptor.STATE_ID, ProcessInstanceState.COMPLETED.getId()); return getProcessAPI(getSession()).searchProcessInstances(builder.done()).getResult(); } private List searchOpenedProcessInstances() throws SearchException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 100); builder.filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, getProcessDefinition().getId()); return getProcessAPI(getSession()).searchOpenProcessInstances(builder.done()).getResult(); } public void deleteCases() throws Exception { final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(getSession()); int repeatNb = 10; boolean repeat; long sleep = 500; Exception latestException = null; do { repeat = false; repeatNb--; final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100000) .filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinition.getId()) .sort(ProcessInstanceSearchDescriptor.ID, Order.ASC).done(); for (ProcessInstance pi : processAPI.searchProcessInstances(searchOptions).getResult()) { try { processAPI.deleteProcessInstance(pi.getId()); } catch (DeletionException e) { //ignore as it may be due a process instance finishing its execution repeat = true; latestException = e; } } try { processAPI.deleteArchivedProcessInstances(processDefinition.getId(), 0, 10000); } catch (DeletionException e) { //ignore as it may be due a process instance finishing its execution repeat = true; latestException = e; } Thread.sleep(sleep); } while (repeat && repeatNb > 0); if (repeat) { throw latestException; } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/TestProcessFactory.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.bpm; import java.io.InputStream; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.UUID; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessActivationException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.test.toolkit.bpm.process.TestProcessConnector; import org.bonitasoft.test.toolkit.exception.TestToolkitException; import org.bonitasoft.test.toolkit.organization.TestUser; /** * @author Vincent Elcrin */ public class TestProcessFactory { protected static final String DEFAULT_HUMAN_TASK_PROCESS_NAME = "Default human task process"; protected static final String PROCESS_WITH_DOCUMENT_ATTACHED = "Process with document attached"; protected static final String PROCESS_CALL_ACTIVTY = "Process call activity"; private final Map processList; private static TestProcessFactory instance; /** * Default Constructor. */ public TestProcessFactory() { processList = new HashMap<>(); } public static TestProcessFactory getInstance() { if (instance == null) { instance = new TestProcessFactory(); } return instance; } private static String getRandomString() { return String.valueOf(new Random().nextLong()); } public void clear() throws Exception { for (TestProcess testProcess : processList.values()) { clear(testProcess); } processList.clear(); } /** * @return the processList */ protected Map getProcessList() { return processList; } // /////////////////////////////////////////////////////////////////////////////////// // / Process definitions // /////////////////////////////////////////////////////////////////////////////////// protected static ProcessDefinitionBuilder getDefaultProcessDefinitionBuilder(final String processName) { return getDefaultProcessDefinitionBuilder(processName, "1.0"); } public static ProcessDefinitionBuilder getDefaultProcessDefinitionBuilder(final String processName, final String version) { final ProcessDefinitionBuilder processDefinitionBuidler = new ProcessDefinitionBuilder() .createNewInstance(processName, version); processDefinitionBuidler.addActor("Employees", true) .addDescription("This a default process") .addStartEvent("Start") .addUserTask("Activity 1", "Employees") .addEndEvent("Finish") .addTransition("Start", "Activity 1") .addTransition("Activity 1", "Finish"); return processDefinitionBuidler; } protected static BusinessArchiveBuilder getBusinessArchiveWithDocumentBuilder(final String processName) { final ProcessDefinitionBuilder processDefinitionBuidler = new ProcessDefinitionBuilder() .createNewInstance(processName, "1.0"); processDefinitionBuidler.addDocumentDefinition("Document 1667").addContentFileName("filename.txt") .addFile("attachedfile.txt"); processDefinitionBuidler.addActor("Employees", true) .addStartEvent("Start") .addUserTask("Activity 1", "Employees") .addEndEvent("Finish") .addTransition("Start", "Activity 1") .addTransition("Activity 1", "Finish"); try { return new BusinessArchiveBuilder().createNewBusinessArchive() .setFormMappings(TestProcess.createDefaultProcessFormMapping(processDefinitionBuidler.getProcess())) .addDocumentResource( new BarResource("attachedfile.txt", "thisisthecontentofthedocumentattached".getBytes())) .setProcessDefinition(processDefinitionBuidler.done()); } catch (final InvalidProcessDefinitionException e) { throw new TestToolkitException("Invalid process definition", e); } } private static ProcessDefinitionBuilder getCallActivityProcessDefinitionBuilder( final ProcessDefinition processToStartViaCallActivity) { Expression expressionName; Expression expressionVersion; try { expressionName = new ExpressionBuilder().createNewInstance("process name") .setExpressionType(ExpressionType.TYPE_CONSTANT) .setReturnType(String.class.getName()) .setContent(processToStartViaCallActivity.getName()).done(); expressionVersion = new ExpressionBuilder().createNewInstance("process version") .setExpressionType(ExpressionType.TYPE_CONSTANT) .setReturnType(String.class.getName()) .setContent(processToStartViaCallActivity.getVersion()).done(); } catch (final InvalidExpressionException e) { throw new TestToolkitException("Invalid expression definition", e); } final ProcessDefinitionBuilder processDefinitionBuidler = new ProcessDefinitionBuilder() .createNewInstance(PROCESS_CALL_ACTIVTY, "1.0"); processDefinitionBuidler.addActor("Employees", true) .addStartEvent("Start") .addCallActivity("Call Activity", expressionName, expressionVersion) .addEndEvent("Finish") .addTransition("Start", "Call Activity") .addTransition("Call Activity", "Finish"); return processDefinitionBuidler; } // ////////////////////////////////////////////////////////////////////////////////// // / Factory accessors // ////////////////////////////////////////////////////////////////////////////////// public static TestProcess createRandomResolvedProcess(final TestUser actor) { return getRandomHumanTaskProcess().addActor(actor); } /** * Variables : * - variable1 : String * - variable2 : Long * - variable3 : Date */ public static TestHumanTask createActivityWithVariables(TestUser initiator) throws InvalidExpressionException { final String processName = "processName"; final ProcessDefinitionBuilder processDefinitionBuidler = new ProcessDefinitionBuilder().createNewInstance( processName + (String.valueOf(UUID.randomUUID().getLeastSignificantBits()).substring(0, 5)), "1.0"); processDefinitionBuidler.addActor("Employees", true) .addDescription("This a default process") .addStartEvent("Start") .addUserTask("Activity 1", "Employees") .addData("variable1", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("defaultValue")) .addData("variable2", Long.class.getName(), new ExpressionBuilder().createConstantLongExpression(1)) .addData("variable3", Date.class.getName(), new ExpressionBuilder().createConstantDateExpression("428558400000")) .addEndEvent("Finish") .addTransition("Start", "Activity 1") .addTransition("Activity 1", "Finish"); final TestProcess testProcess = new TestProcess(processDefinitionBuidler); getInstance().getProcessList().put(processName, testProcess); return testProcess.addActor(initiator).enable().startCase().getNextHumanTask().assignTo(initiator); } /** * This process contains only a human task */ public static TestProcess getDefaultHumanTaskProcess() { return getHumanTaskProcess(DEFAULT_HUMAN_TASK_PROCESS_NAME); } public static TestProcess createProcessWithConnector(final TestProcessConnector testConnector) { final String aProcessWithConnector = "aProcessWithConnector"; final ProcessDefinitionBuilder processBuilder = getDefaultProcessDefinitionBuilder(aProcessWithConnector); processBuilder.addConnector(testConnector.getName(), testConnector.getId(), testConnector.getVersion(), testConnector.getConnectorEvent()); try { final InputStream stream = TestProcessFactory.class .getResourceAsStream(testConnector.getResourceFilePath()); final BarResource connectorResource = new BarResource(testConnector.getResourceFileName(), IOUtils.toByteArray(stream)); stream.close(); final BusinessArchiveBuilder barBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processBuilder.done()); barBuilder.addConnectorImplementation(connectorResource); final TestProcess testProcess = new TestProcess(barBuilder); getInstance().getProcessList().put(aProcessWithConnector, testProcess); return testProcess; } catch (final Exception e) { throw new TestToolkitException("Unable to create a process with connector", e); } } public static TestProcess getProcessWithDocumentAttached() { if (getInstance().getProcessList().get(PROCESS_WITH_DOCUMENT_ATTACHED) == null) { final TestProcess testProcess = new TestProcess( getBusinessArchiveWithDocumentBuilder(PROCESS_WITH_DOCUMENT_ATTACHED)); getInstance().getProcessList().put(PROCESS_WITH_DOCUMENT_ATTACHED, testProcess); } return getInstance().getProcessList().get(PROCESS_WITH_DOCUMENT_ATTACHED); } public static TestProcess createProcessWith3Actors() { final String processWith3Actors = "processWith3Actors"; final ProcessDefinitionBuilder builder = getDefaultProcessDefinitionBuilder(processWith3Actors); builder.addActor("actor2").addDescription("description actor2").addActor("actor3") .addDescription("description actor3"); final TestProcess testProcess = new TestProcess(builder); getInstance().getProcessList().put(processWith3Actors, testProcess); return testProcess; } public static TestProcess createProcessWithVariables(final String processName, final ProcessVariable... variables) { final ProcessDefinitionBuilder builder = getDefaultProcessDefinitionBuilder(processName); for (final ProcessVariable variable : variables) { builder.addData(variable.getName(), variable.getClassName(), variable.getDefaultValue()); } final TestProcess testProcess = new TestProcess(builder); getInstance().getProcessList().put(processName, testProcess); return testProcess; } public static TestProcess getRandomHumanTaskProcess() { return getHumanTaskProcess(getRandomString()); } /** * This process contains only a human task */ public static TestProcess getHumanTaskProcess(final String processName) { if (getInstance().getProcessList().get(processName) == null) { final TestProcess testProcess = new TestProcess(getDefaultProcessDefinitionBuilder(processName)); getInstance().getProcessList().put(processName, testProcess); } return getInstance().getProcessList().get(processName); } /** * This process contains a call activity */ public static TestProcess getCallActivityProcess(final ProcessDefinition processToStartViaCallActivity) { if (getInstance().getProcessList().get(PROCESS_CALL_ACTIVTY) == null) { final TestProcess testProcess = new TestProcess( getCallActivityProcessDefinitionBuilder(processToStartViaCallActivity)); getInstance().getProcessList().put(PROCESS_CALL_ACTIVTY, testProcess); } return getInstance().getProcessList().get(PROCESS_CALL_ACTIVTY); } public void check() { if (!getProcessList().isEmpty()) { throw new RuntimeException( this.getClass().getName() + " cannot be reset because the list is not empty: " + getProcessList()); } } private void clear(TestProcess testProcess) throws Exception { testProcess.deleteCases(); try { testProcess.disable(); } catch (TestToolkitException e) { if (!(e.getCause() instanceof ProcessActivationException)) { throw e; } //ignore as the process can be disabled } testProcess.delete(); } public void delete(TestProcess testProcess) throws Exception { clear(testProcess); getProcessList().remove(testProcess.getProcessDefinition().getName()); } public void remove(TestProcess testProcess) { getProcessList().remove(testProcess.getProcessDefinition().getName()); } public void add(TestProcess testProcess) { getProcessList().put(testProcess.getProcessDefinition().getName(), testProcess); } /** * @param parameters * */ public static TestProcess createProcessWithParameters(Map parameters) { ProcessDefinitionBuilder processBuilder = getDefaultProcessDefinitionBuilder("aProcessWithParameters"); for (Map.Entry parameter : parameters.entrySet()) { processBuilder.addParameter(parameter.getKey(), parameter.getValue()); } try { BusinessArchiveBuilder barBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setParameters(parameters).setProcessDefinition(processBuilder.done()); final TestProcess testProcess = new TestProcess(barBuilder); getInstance().getProcessList().put(testProcess.getProcessDefinition().getName(), testProcess); return testProcess; } catch (InvalidProcessDefinitionException e) { throw new TestToolkitException("Unable to create a process with parameters", e); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/process/TestActorMemberFactory.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.bpm.process; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.actor.ActorMember; import org.bonitasoft.test.toolkit.organization.TestGroup; import org.bonitasoft.test.toolkit.organization.TestRole; import org.bonitasoft.test.toolkit.organization.TestToolkitCtx; import org.bonitasoft.test.toolkit.organization.TestUser; /** * @author Colin PUY */ public class TestActorMemberFactory { public static ActorMember createMembershipActorMember(long actorId, TestGroup testGroup, TestRole testRole) throws Exception { return getProcessAPI().addRoleAndGroupToActor(actorId, testRole.getId(), testGroup.getId()); } public static ActorMember createUserActorMember(long actorId, TestUser testUser) throws Exception { return getProcessAPI().addUserToActor(actorId, testUser.getId()); } public static ActorMember createGroupActorMember(long actorId, TestGroup testGroup) throws Exception { return getProcessAPI().addGroupToActor(actorId, testGroup.getId()); } private static ProcessAPI getProcessAPI() throws Exception { return TenantAPIAccessor.getProcessAPI(TestToolkitCtx.getInstance().getInitiator().getSession()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/process/TestProcessConnector.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.bpm.process; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; /** * @author Colin PUY */ public class TestProcessConnector { private final String name; private final String id; private final String version; private final String implementationClassname; private final String implementationId; private final ConnectorEvent connectorEvent; private final String resourceFileName; private final String resourceFilePath; private final List dependencies = new ArrayList<>(); public TestProcessConnector(String name, String id, String version, String implementationClassname, String implementationId, ConnectorEvent connectorEvent, String resourceFileName, String resourceFilePath) { super(); this.name = name; this.id = id; this.version = version; this.implementationClassname = implementationClassname; this.implementationId = implementationId; this.connectorEvent = connectorEvent; this.resourceFileName = resourceFileName; this.resourceFilePath = resourceFilePath; } public String getName() { return name; } public String getId() { return id; } public String getVersion() { return version; } public ConnectorEvent getConnectorEvent() { return connectorEvent; } public String getResourceFileName() { return resourceFileName; } public String getResourceFilePath() { return resourceFilePath; } public String getImplementationClassname() { return implementationClassname; } public String getImplementationId() { return implementationId; } public TestProcessConnector addDependency(String dependency) { dependencies.add(dependency); return this; } public List getDependencies() { return dependencies; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/bpm/process/TestProcessConnectorFactory.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.bpm.process; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; /** * @author Colin PUY */ public final class TestProcessConnectorFactory { public static TestProcessConnector getDefaultConnector() { TestProcessConnector testProcessConnector = new TestProcessConnector("aConnector", "org.bonitasoft.test.toolkit.connector.TestConnector", "1.0", "org.bonitasoft.test.toolkit.connector.TestConnector", "org.bonitasoft.test.toolkit.connector.testConnector", ConnectorEvent.ON_ENTER, "TestConnector.impl", "/org/bonitasoft/test/toolkit/connector/TestConnector.impl"); testProcessConnector.addDependency("aDependency.jar").addDependency("anOtherDependency.jar"); return testProcessConnector; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/exception/NextActivityIsNotAllowedStateException.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.exception; import java.text.MessageFormat; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; /** * @author Vincent Elcrin */ public class NextActivityIsNotAllowedStateException extends TestToolkitException { /** * */ private static final long serialVersionUID = 8542205212242675994L; /** * Default Constructor. */ public NextActivityIsNotAllowedStateException(final HumanTaskInstance humanTask) { super(MessageFormat.format("Activity with id {0} is the state {1} which is not allowed.", humanTask.getId(), humanTask.getState())); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/exception/NoActivityLeftException.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.exception; /** * @author Vincent Elcrin */ public class NoActivityLeftException extends TestToolkitException { /** * */ private static final long serialVersionUID = 5201304240169814990L; /** * Default Constructor. */ public NoActivityLeftException() { super("The case is archived"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/exception/TestToolkitException.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.exception; /** * @author Vincent Elcrin */ public class TestToolkitException extends RuntimeException { /** * ID used by serialization. */ private static final long serialVersionUID = -2678731898708770494L; /** * Default constructor. */ public TestToolkitException() { super(); } /** * Default constructor. * * @param message */ public TestToolkitException(final String message) { super(message); } /** * Default constructor. * * @param message */ public TestToolkitException(final String message, final Exception e) { super(message, e); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/AdminUser.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.organization; /** * @author Vincent Elcrin */ public final class AdminUser extends TestUser { private static final String USERNAME = "install"; private static final String PASSWORD = "install"; /** * Default Constructor. * * @throws Exception */ protected AdminUser() { super(USERNAME, PASSWORD); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/IdentityAccessor.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.organization; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.test.toolkit.exception.TestToolkitException; /** * @author Colin PUY */ public class IdentityAccessor { public IdentityAPI getIdentityAPI(final APISession apiSession) { try { return TenantAPIAccessor.getIdentityAPI(apiSession); } catch (final Exception e) { throw new TestToolkitException("Can't get identy api", e); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestGroup.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.organization; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.GroupCreator; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.test.toolkit.bpm.TestActor; import org.bonitasoft.test.toolkit.exception.TestToolkitException; /** * @author Vincent Elcrin */ public class TestGroup implements TestActor { private final Group group; public TestGroup(final GroupCreator creator) { this.group = createGroup(TestToolkitCtx.getInstance().getInitiator().getSession(), creator); } private Group createGroup(APISession apiSession, GroupCreator creator) { final IdentityAPI identityAPI = new IdentityAccessor().getIdentityAPI(apiSession); try { return identityAPI.createGroup(creator); } catch (final Exception e) { throw new TestToolkitException("Can't create group", e); } } public void delete() throws Exception { final IdentityAPI identityAPI = new IdentityAccessor() .getIdentityAPI(TestToolkitCtx.getInstance().getInitiator().getSession()); try { identityAPI.deleteGroup(this.group.getId()); } catch (final Exception e) { throw new TestToolkitException("Can't delete group", e); } } public long getId() { return this.group.getId(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestGroupFactory.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.organization; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import org.bonitasoft.engine.identity.GroupCreator; /** * @author Séverin Moussel */ public class TestGroupFactory { private static final String NAME_R_AND_D = "RetD"; private static final String DESCRIPTION_R_AND_D = "The team that drinks all the coffee"; private static final String NAME_WEB = "Web"; private static final String DESCRIPTION_WEB = "The team that also drinks all the beer"; private final Map groupList; private static TestGroupFactory instance; /** * Default Constructor. */ public TestGroupFactory() { this.groupList = new HashMap<>(); } public static TestGroupFactory getInstance() { if (instance == null) { instance = new TestGroupFactory(); } return instance; } public void clear() throws Exception { for (TestGroup testGroup : this.groupList.values()) { testGroup.delete(); } this.groupList.clear(); } /** * @return the userList */ private Map getGroupList() { return this.groupList; } public static List createRandomGroups(final int nbOfGroups) { final List results = new ArrayList<>(); for (int i = 0; i < nbOfGroups; i++) { results.add(createGroup(getRandomString(), getRandomString())); } return results; } private static String getRandomString() { return String.valueOf(new Random().nextLong()); } // //////////////////////////////////////////////////////////////////////////////// // / Group's creation // //////////////////////////////////////////////////////////////////////////////// /** * @param name * @param description * @return */ public static TestGroup createGroup(final String name, final String description) { if (getInstance().getGroupList().get(name) == null) { final GroupCreator groupBuilder = new GroupCreator(name).setDescription(description); getInstance().getGroupList().put(name, new TestGroup(groupBuilder)); } return getInstance().getGroupList().get(name); } public static TestGroup getRAndD() { return createGroup(NAME_R_AND_D, DESCRIPTION_R_AND_D); } public static TestGroup getWeb() { return createGroup(NAME_WEB, DESCRIPTION_WEB); } public void check() { if (!getGroupList().isEmpty()) { throw new RuntimeException( this.getClass().getName() + " cannot be reset because the list is not empty: " + getGroupList()); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestMembership.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.organization; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.test.toolkit.bpm.TestActor; import org.bonitasoft.test.toolkit.exception.TestToolkitException; /** * @author Séverin Moussel */ public class TestMembership implements TestActor { public TestMembership(final APISession apiSession, final long userId, final long groupId, final long roleId) { createMembership(apiSession, userId, groupId, roleId); } public TestMembership(final long userId, final long groupId, final long roleId) { this(TestToolkitCtx.getInstance().getInitiator().getSession(), userId, groupId, roleId); } // ////////////////////////////////////////////////////////////////////////////////// // / Membership creation // ////////////////////////////////////////////////////////////////////////////////// private void createMembership(final APISession apiSession, final long userId, final long groupId, final long roleId) { final IdentityAPI identityAPI = TestUser.getIdentityAPI(apiSession); try { identityAPI.addUserMembership(userId, groupId, roleId); } catch (final Exception e) { throw new TestToolkitException( "Can't create membership <" + userId + "/" + groupId + "/" + roleId + "/" + ">", e); } } public long getId() { throw new IllegalStateException("Never called!"); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestMembershipFactory.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.organization; /** * @author Séverin Moussel */ public class TestMembershipFactory { public static void assignMembership(final TestUser user, final TestGroup group, final TestRole role) { new TestMembership(user.getId(), group.getId(), role.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestRole.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.organization; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.RoleCreator; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.test.toolkit.bpm.TestActor; import org.bonitasoft.test.toolkit.exception.TestToolkitException; /** * @author Séverin Moussel */ public class TestRole extends IdentityAccessor implements TestActor { private final Role role; private final APISession apiSession; public TestRole(RoleCreator creator) { this(TestToolkitCtx.getInstance().getInitiator().getSession(), creator); } public TestRole(APISession apiSession, RoleCreator creator) { this.apiSession = apiSession; this.role = createRole(creator); /* * System.err.println("\n\n"); * System.err.println("Building role: " + role.getName()); * Thread.dumpStack(); * System.err.println("\n\n"); */ } private Role createRole(RoleCreator creator) { try { return getIdentityAPI(apiSession).createRole(creator); } catch (final Exception e) { throw new TestToolkitException("Can't create role <" + creator + ">", e); } } public Role getRole() { return this.role; } public long getId() { return this.role.getId(); } public void delete() { try { getIdentityAPI(apiSession).deleteRole(this.role.getId()); } catch (final Exception e) { throw new TestToolkitException("Can't delete role <" + this.role.getId() + ">", e); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestRoleFactory.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.organization; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import org.bonitasoft.engine.identity.RoleCreator; /** * @author Séverin Moussel */ public class TestRoleFactory { private static final String NAME_MANAGER = "Manager"; private static final String DESCRIPTION_MANAGER = "Team manager"; private static final String NAME_DEVELOPER = "Developer"; private static final String DESCRIPTION_DEVELOPER = "The man who write code and drink a lot of coffee"; private final Map roleList; private static TestRoleFactory instance; private TestRoleFactory() { this.roleList = new HashMap<>(); } public static TestRoleFactory getInstance() { if (instance == null) { instance = new TestRoleFactory(); } return instance; } public void clear() { for (TestRole testRole : this.roleList.values()) { testRole.delete(); } this.roleList.clear(); } /** * @return the userList */ private Map getRoleList() { return this.roleList; } public List createRandomRoles(final int nbOfRoles) { final List results = new ArrayList<>(); for (int i = 0; i < nbOfRoles; i++) { final String name = getRandomString(); RoleCreator creator = new RoleCreator(name).setDescription(getRandomString()); final TestRole testRole = new TestRole(creator); results.add(testRole); getInstance().getRoleList().put(name, testRole); } return results; } private static String getRandomString() { return String.valueOf(new Random().nextLong()); } // //////////////////////////////////////////////////////////////////////////////// // / Role's creation // //////////////////////////////////////////////////////////////////////////////// public static TestRole createRole(final String name, final String description) { if (getInstance().getRoleList().get(name) == null) { RoleCreator creator = new RoleCreator(name).setDescription(description); getInstance().getRoleList().put(name, new TestRole(creator)); } return getInstance().getRoleList().get(name); } public static TestRole getManager() { return createRole(NAME_MANAGER, DESCRIPTION_MANAGER); } public static TestRole getDeveloper() { return createRole(NAME_DEVELOPER, DESCRIPTION_DEVELOPER); } public void check() { if (!getRoleList().isEmpty()) { throw new RuntimeException( this.getClass().getName() + " cannot be reset because the list is not empty: " + getRoleList()); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestToolkitCtx.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.organization; import java.util.HashMap; import java.util.Map; import org.bonitasoft.test.toolkit.bpm.TestCategoryFactory; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; /** * @author Vincent Elcrin */ public class TestToolkitCtx { protected static final String ADMIN_USER = "adminuser"; protected static final String INITIATOR = "initiator"; protected static TestToolkitCtx instance; protected final Map sessionsVariables; protected TestToolkitCtx() { this.sessionsVariables = new HashMap<>(); } /** * Singleton */ public static TestToolkitCtx getInstance() { if (instance == null) { instance = new TestToolkitCtx(); } return instance; } public void clearSession() throws Exception { /* * Clear Factories */ clearFactories(); /* * Clear session's variables */ this.sessionsVariables.clear(); } protected void clearFactories() throws Exception { TestUserFactory.getInstance().clear(); TestProcessFactory.getInstance().clear(); TestGroupFactory.getInstance().clear(); TestRoleFactory.getInstance().clear(); TestCategoryFactory.getInstance().clear(); } protected void checkFactories() { TestUserFactory.getInstance().check(); TestProcessFactory.getInstance().check(); TestGroupFactory.getInstance().check(); TestRoleFactory.getInstance().check(); TestCategoryFactory.getInstance().check(); } /** * Admin user use login and password used during default tenant creation * * @return The Admin user */ public AdminUser getAdminUser() { if (!this.sessionsVariables.containsKey(ADMIN_USER)) { this.sessionsVariables.put(ADMIN_USER, new AdminUser()); } return (AdminUser) this.sessionsVariables.get(ADMIN_USER); } /** * Initiator is a convenient notion to set a test user as source of all transactions done with the framework via its * API Session. * * @param initiator */ public void setInitiator(final TestUser initiator) { this.sessionsVariables.put(INITIATOR, initiator); } public TestUser getInitiator() { if (!this.sessionsVariables.containsKey(INITIATOR)) { setInitiator(getAdminUser()); } return (TestUser) this.sessionsVariables.get(INITIATOR); } public void check() throws Exception { checkFactories(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestUser.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.organization; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.LoginAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.identity.ContactDataCreator; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserCreator; import org.bonitasoft.engine.platform.LogoutException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.engine.session.SessionNotFoundException; import org.bonitasoft.test.toolkit.bpm.TestActor; import org.bonitasoft.test.toolkit.exception.TestToolkitException; /** * @author Vincent Elcrin */ public class TestUser implements TestActor { private final String userName; private final String password; private User user; private APISession apiSession; private boolean loggedIn = false; protected TestUser(final String userName, final String password) { this.userName = userName; this.password = password; } public TestUser(final APISession apiSession, final String userName, final String password) { this.user = createUser(apiSession, userName, password); this.userName = userName; this.password = password; /* * System.err.println("\n\n"); * System.err.println("Building user: " + user.getUserName()); * Thread.dumpStack(); * System.err.println("\n\n"); */ } public TestUser(final APISession apiSession, UserCreator userCreator, final ContactDataCreator personalInfoCreator, final ContactDataCreator professionalInfoCreator) { this.user = createUser(apiSession, userCreator, personalInfoCreator, professionalInfoCreator); this.userName = (String) userCreator.getFields().get(UserCreator.UserField.NAME); this.password = (String) userCreator.getFields().get(UserCreator.UserField.PASSWORD); /* * System.err.println("\n\n"); * System.err.println("Building user: " + user.getUserName()); * Thread.dumpStack(); * System.err.println("\n\n"); */ } public TestUser(final APISession apiSession, final UserCreator userBuilder) { this(apiSession, userBuilder, new ContactDataCreator(), new ContactDataCreator()); } private APISession logIn(final String userName, final String password) { LoginAPI loginAPI; APISession apiSession; try { loginAPI = TenantAPIAccessor.getLoginAPI(); apiSession = loginAPI.login(userName, password); } catch (final Exception e) { throw new TestToolkitException("Can't log user <" + userName + "> in", e); } this.loggedIn = true; return apiSession; } private void logOut(final APISession apiSession) { LoginAPI loginAPI; try { loginAPI = TenantAPIAccessor.getLoginAPI(); loginAPI.logout(apiSession); } catch (final BonitaHomeNotSetException e) { throw new TestToolkitException("Can't get api to log out. Bonita home not set", e); } catch (final ServerAPIException e) { throw new TestToolkitException("Can't get api to log out. Server api exception", e); } catch (final UnknownAPITypeException e) { throw new TestToolkitException("Can't get api to log out. Unknown api type", e); } catch (final LogoutException e) { throw new TestToolkitException("Can't get log out user <" + apiSession.getUserName() + ">", e); } catch (SessionNotFoundException e) { throw new TestToolkitException("Can't find the session to log out", e); } this.loggedIn = false; } public APISession logIn() { this.apiSession = logIn(this.userName, this.password); return this.apiSession; } public void logOut() { if (this.apiSession != null) { logOut(this.apiSession); } } public APISession getSession() { if (!this.loggedIn) { logIn(); } return this.apiSession; } protected static IdentityAPI getIdentityAPI(final APISession apiSession) { IdentityAPI identityAPI; try { identityAPI = TenantAPIAccessor.getIdentityAPI(apiSession); } catch (final InvalidSessionException e) { throw new TestToolkitException("Can't get identity api. Invalid session", e); } catch (final BonitaHomeNotSetException e) { throw new TestToolkitException("Can't get identity api. Bonita home not set", e); } catch (final ServerAPIException e) { throw new TestToolkitException("Can't get identity api. Server api exception", e); } catch (final UnknownAPITypeException e) { throw new TestToolkitException("Can't get identity api. Unknown api type", e); } return identityAPI; } // ////////////////////////////////////////////////////////////////////////////////// // / Users creation // ////////////////////////////////////////////////////////////////////////////////// /** * Create user with only username & password */ private User createUser(final APISession apiSession, final String userName, final String password) { final IdentityAPI identityAPI = getIdentityAPI(apiSession); User newUser; try { newUser = identityAPI.createUser(userName, password); } catch (final AlreadyExistsException e) { try { newUser = identityAPI.getUserByUserName(userName); } catch (final Exception e1) { throw new TestToolkitException("Can't get user <" + userName + ">", e); } } catch (final Exception e) { throw new TestToolkitException("Can't create user <" + userName + ">", e); } return newUser; } /** * Create user with user engine's builder */ private User createUser(final APISession apiSession, final UserCreator creator, final ContactDataCreator personalInfoCreator, final ContactDataCreator professionalInfoBuilder) { final IdentityAPI identityAPI = getIdentityAPI(apiSession); User newUser; try { try { creator.setPersonalContactData(personalInfoCreator); creator.setPersonalContactData(professionalInfoBuilder); newUser = identityAPI.createUser(creator); } catch (final AlreadyExistsException e) { try { String userName = (String) creator.getFields().get(UserCreator.UserField.NAME); newUser = identityAPI.getUserByUserName(userName); } catch (final NotFoundException getEx) { throw new TestToolkitException("User <" + userName + "> not found", e); } } catch (final CreationException e) { throw new TestToolkitException("Can't create user", e); } } catch (final InvalidSessionException e) { throw new TestToolkitException("Can't get identity api to create user. Invalid session", e); } return newUser; } public TestUser createUser(final UserCreator userBuilder) { return new TestUser(getSession(), userBuilder); } public TestUser createUser(final String userName, final String password) { return new TestUser(getSession(), userName, password); } public void delete(final TestUser testUser) { final IdentityAPI identityAPI = getIdentityAPI(getSession()); try { identityAPI.deleteUser(testUser.getUser().getId()); } catch (final Exception e) { throw new TestToolkitException("Can't delete user", e); } } public User getUser() { return this.user; } public long getId() { return this.user.getId(); } public String getUserName() { return userName; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/organization/TestUserFactory.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.organization; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.identity.ContactDataCreator; import org.bonitasoft.engine.identity.UserCreator; /** * @author Vincent Elcrin */ public class TestUserFactory { private static final String JOHN_CARPENTER_LOGIN = "john.carpenter"; private static final String TEAM_MANAGER_LOGIN = "team.manager"; private static final String RIDLEY_SCOTT_LOGIN = "ridley.scott"; private static final String MR_SPECHAR_LOGIN = "#*éàâäëêé~çÞšŒØÃ�Æ"; private final Map userList; private static TestUserFactory instance; /** * Default Constructor. */ public TestUserFactory() { this.userList = new HashMap<>(); } public static TestUserFactory getInstance() { if (instance == null) { instance = new TestUserFactory(); } return instance; } public void clear() { this.userList.clear(); } /** * @return the userList */ public Map getUserList() { return userList; } // //////////////////////////////////////////////////////////////////////////////// // / User's creation // //////////////////////////////////////////////////////////////////////////////// public static TestUser getJohnCarpenter() { if (getInstance().getUserList().get(JOHN_CARPENTER_LOGIN) == null) { UserCreator creator = new UserCreator(JOHN_CARPENTER_LOGIN, "bpm") .setFirstName("John") .setLastName("Carpenter") .setJobTitle("Director"); getInstance().getUserList().put(JOHN_CARPENTER_LOGIN, new TestUser(TestToolkitCtx.getInstance().getAdminUser().logIn(), creator)); TestToolkitCtx.getInstance().getAdminUser().logOut(); } return getInstance().getUserList().get(JOHN_CARPENTER_LOGIN); } public static TestUser getTeamManagerUser() { if (getInstance().getUserList().get(TEAM_MANAGER_LOGIN) == null) { UserCreator creator = new UserCreator(TEAM_MANAGER_LOGIN, "bpm") .setFirstName("Team") .setLastName("Manager") .setJobTitle("Team Manager"); getInstance().getUserList().put(TEAM_MANAGER_LOGIN, new TestUser(TestToolkitCtx.getInstance().getAdminUser().logIn(), creator)); TestToolkitCtx.getInstance().getAdminUser().logOut(); } return getInstance().getUserList().get(TEAM_MANAGER_LOGIN); } public static Map getManagedUsers(int nbManagedUsers) { for (int i = 0; i < nbManagedUsers; i++) { String firstName = "managed"; String lastName = "user" + i; UserCreator creator = new UserCreator(firstName + "." + lastName, "bpm") .setFirstName(firstName) .setLastName(lastName) .setManagerUserId(getTeamManagerUser().getId()); getInstance().getUserList().put(firstName + "." + lastName, new TestUser(TestToolkitCtx.getInstance().getAdminUser().logIn(), creator)); TestToolkitCtx.getInstance().getAdminUser().logOut(); } return getInstance().getUserList(); } public static TestUser getRidleyScott() { if (getInstance().getUserList().get(RIDLEY_SCOTT_LOGIN) == null) { final UserCreator userCreator = new UserCreator(RIDLEY_SCOTT_LOGIN, "bpm") .setFirstName("Ridley") .setLastName("Scott") .setJobTitle("Director"); final ContactDataCreator personalContactData = new ContactDataCreator() .setAddress("address") .setBuilding("building") .setCity("city") .setCountry("country") .setEmail("ridley.scott@gmail.com") .setFaxNumber("123456789") .setMobileNumber("9876543214") .setRoom("1408") .setState("51") .setWebsite("http://www.prometheus-movie.com") .setZipCode("BT7 1GU"); final ContactDataCreator professionalContactData = new ContactDataCreator() .setAddress("address pro") .setBuilding("building pro") .setCity("city pro") .setCountry("country pro") .setEmail("ridley.scott.pro@gmail.com") .setFaxNumber("0123456789") .setMobileNumber("98765432140") .setRoom("1408-b") .setState("42") .setWebsite("http://www.imdb.com/title/tt0078748") .setZipCode("38000"); getInstance().getUserList().put(RIDLEY_SCOTT_LOGIN, new TestUser(TestToolkitCtx.getInstance().getAdminUser().logIn(), userCreator, personalContactData, professionalContactData)); TestToolkitCtx.getInstance().getAdminUser().logOut(); } return getInstance().getUserList().get(RIDLEY_SCOTT_LOGIN); } public static TestUser getMrSpechar() { if (getInstance().getUserList().get(MR_SPECHAR_LOGIN) == null) { final UserCreator userBuilder = new UserCreator(MR_SPECHAR_LOGIN, "ëêé~Ã") .setFirstName("#*éàâäëêé~çÞšŒØÃ�Æ") .setLastName("àâäë") .setJobTitle("©Ã Ã¢Ã¤Ã«Ãª"); getInstance().getUserList().put(MR_SPECHAR_LOGIN, new TestUser(TestToolkitCtx.getInstance().getAdminUser().logIn(), userBuilder)); TestToolkitCtx.getInstance().getAdminUser().logOut(); } return getInstance().getUserList().get(MR_SPECHAR_LOGIN); } public void check() { if (!getUserList().isEmpty()) { throw new RuntimeException( this.getClass().getName() + " cannot be reset because the list is not empty: " + getUserList()); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/server/MockHttpServletRequest.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.server; import java.io.BufferedReader; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.Principal; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.Locale; import java.util.Map; import javax.servlet.AsyncContext; import javax.servlet.DispatcherType; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpUpgradeHandler; import javax.servlet.http.Part; /** * @author Nicolas Chabanoles */ public class MockHttpServletRequest implements HttpServletRequest { HttpSession session = null; Map parametersMap; Map attributesMap; HttpServletRequest req = null; String pathInfo = null; public MockHttpServletRequest() { parametersMap = new HashMap<>(); attributesMap = new HashMap<>(); } @Override public String getAuthType() { if (this.req != null) { return this.req.getAuthType(); } return null; } @Override public String getContextPath() { if (this.req != null) { return this.req.getContextPath(); } return null; } @Override public Cookie[] getCookies() { if (this.req != null) { return this.req.getCookies(); } return null; } @Override public long getDateHeader(final String anName) { if (this.req != null) { return this.req.getDateHeader(anName); } return 0; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getHeader(java.lang.String) */ @Override public String getHeader(final String anName) { if (this.req != null) { return this.req.getHeader(anName); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getHeaderNames() */ @Override @SuppressWarnings("unchecked") public Enumeration getHeaderNames() { if (this.req != null) { return this.req.getHeaderNames(); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getHeaders(java.lang.String) */ @Override @SuppressWarnings("unchecked") public Enumeration getHeaders(final String anName) { if (this.req != null) { return this.req.getHeaders(anName); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getIntHeader(java.lang.String) */ @Override public int getIntHeader(final String anName) { if (this.req != null) { return this.req.getIntHeader(anName); } return 0; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getMethod() */ @Override public String getMethod() { if (this.req != null) { return this.req.getMethod(); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getPathInfo() */ @Override public String getPathInfo() { if (pathInfo != null) { return this.pathInfo; } else if (this.req != null) { return this.req.getPathInfo(); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getPathTranslated() */ @Override public String getPathTranslated() { if (this.req != null) { return this.req.getPathTranslated(); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getQueryString() */ @Override public String getQueryString() { if (this.req != null) { return this.req.getQueryString(); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getRemoteUser() */ @Override public String getRemoteUser() { if (this.req != null) { return this.req.getRemoteUser(); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getRequestURI() */ @Override public String getRequestURI() { if (this.req != null) { return this.req.getRequestURI(); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getRequestURL() */ @Override public StringBuffer getRequestURL() { if (this.req != null) { return this.req.getRequestURL(); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getRequestedSessionId() */ @Override public String getRequestedSessionId() { if (this.req != null) { return this.req.getRequestedSessionId(); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getServletPath() */ @Override public String getServletPath() { if (this.req != null) { return this.req.getRequestedSessionId(); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getSession() */ @Override public HttpSession getSession() { if (this.req != null) { this.session = this.req.getSession(); } if (this.session == null) { this.session = new MockHttpSession(); } return this.session; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getSession(boolean) */ @Override public HttpSession getSession(final boolean anCreate) { if (anCreate) { if (this.req != null) { this.session = this.req.getSession(anCreate); } else { this.session = new MockHttpSession(); } } return this.session; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#getUserPrincipal() */ @Override public Principal getUserPrincipal() { if (this.req != null) { return this.req.getUserPrincipal(); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromCookie() */ @Override public boolean isRequestedSessionIdFromCookie() { if (this.req != null) { return this.req.isRequestedSessionIdFromCookie(); } return false; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromURL() */ @Override public boolean isRequestedSessionIdFromURL() { if (this.req != null) { return this.req.isRequestedSessionIdFromURL(); } return false; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdFromUrl() */ @Override @Deprecated public boolean isRequestedSessionIdFromUrl() { if (this.req != null) { return this.req.isRequestedSessionIdFromUrl(); } return false; } @Override public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { return false; } @Override public void login(String username, String password) throws ServletException { } @Override public void logout() throws ServletException { } @Override public Collection getParts() throws IOException, ServletException { return null; } @Override public Part getPart(String name) throws IOException, ServletException { return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#isRequestedSessionIdValid() */ @Override public boolean isRequestedSessionIdValid() { if (this.req != null) { return this.req.isRequestedSessionIdValid(); } return false; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletRequest#isUserInRole(java.lang.String) */ @Override public boolean isUserInRole(final String anRole) { if (this.req != null) { return this.req.isUserInRole(anRole); } return false; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getAttribute(java.lang.String) */ @Override public Object getAttribute(final String aName) { if (this.req != null) { return this.req.getAttribute(aName); } else { return attributesMap.get(aName); } } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getAttributeNames() */ @Override @SuppressWarnings("unchecked") public Enumeration getAttributeNames() { if (this.req != null) { return this.req.getAttributeNames(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getCharacterEncoding() */ @Override public String getCharacterEncoding() { if (this.req != null) { return this.req.getCharacterEncoding(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getContentLength() */ @Override public int getContentLength() { if (this.req != null) { return this.req.getContentLength(); } return 0; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getContentType() */ @Override public String getContentType() { if (this.req != null) { return this.req.getContentType(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getInputStream() */ @Override public ServletInputStream getInputStream() throws IOException { if (this.req != null) { return this.req.getInputStream(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getLocalAddr() */ @Override public String getLocalAddr() { if (this.req != null) { return this.req.getLocalAddr(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getLocalName() */ @Override public String getLocalName() { if (this.req != null) { return this.req.getLocalName(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getLocalPort() */ @Override public int getLocalPort() { if (this.req != null) { return this.req.getLocalPort(); } return 0; } @Override public ServletContext getServletContext() { return null; } @Override public AsyncContext startAsync() throws IllegalStateException { return null; } @Override public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException { return null; } @Override public boolean isAsyncStarted() { return false; } @Override public boolean isAsyncSupported() { return false; } @Override public AsyncContext getAsyncContext() { return null; } @Override public DispatcherType getDispatcherType() { return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getLocale() */ @Override public Locale getLocale() { if (this.req != null) { return this.req.getLocale(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getLocales() */ @Override @SuppressWarnings("unchecked") public Enumeration getLocales() { if (this.req != null) { return this.req.getLocales(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getParameter(java.lang.String) */ @Override public String getParameter(final String aName) { final String[] strings = this.parametersMap.get(aName); if (strings != null && strings.length > 0) { return strings[0]; } else { return null; } } public void setParameter(final String anName, final String value) { this.parametersMap.put(anName, new String[] { value }); } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getParameterMap() */ @Override @SuppressWarnings("unchecked") public Map getParameterMap() { if (req == null) { return this.parametersMap; } else { return this.req.getParameterMap(); } } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getParameterNames() */ @Override @SuppressWarnings("unchecked") public Enumeration getParameterNames() { if (this.req != null) { return this.req.getParameterNames(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getParameterValues(java.lang.String) */ @Override public String[] getParameterValues(final String anName) { if (this.req != null) { return this.req.getParameterValues(anName); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getProtocol() */ @Override public String getProtocol() { if (this.req != null) { return this.req.getProtocol(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getReader() */ @Override public BufferedReader getReader() throws IOException { if (this.req != null) { return this.req.getReader(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getRealPath(java.lang.String) */ @Override @Deprecated public String getRealPath(final String anPath) { if (this.req != null) { return this.req.getRealPath(anPath); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getRemoteAddr() */ @Override public String getRemoteAddr() { if (this.req != null) { return this.req.getRemoteAddr(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getRemoteHost() */ @Override public String getRemoteHost() { if (this.req != null) { return this.req.getRemoteHost(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getRemotePort() */ @Override public int getRemotePort() { if (this.req != null) { return this.req.getRemotePort(); } return 0; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getRequestDispatcher(java.lang.String) */ @Override public RequestDispatcher getRequestDispatcher(final String anPath) { if (this.req != null) { return this.req.getRequestDispatcher(anPath); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getScheme() */ @Override public String getScheme() { if (this.req != null) { return this.req.getScheme(); } return "http"; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getServerName() */ @Override public String getServerName() { if (this.req != null) { return this.req.getServerName(); } return "localhost"; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#getServerPort() */ @Override public int getServerPort() { if (this.req != null) { return this.req.getServerPort(); } return 8080; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#isSecure() */ @Override public boolean isSecure() { if (this.req != null) { return this.req.isSecure(); } return false; } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#removeAttribute(java.lang.String) */ @Override public void removeAttribute(final String anName) { if (this.req != null) { this.req.removeAttribute(anName); } } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#setAttribute(java.lang.String, java.lang.Object) */ @Override public void setAttribute(final String aName, final Object aValue) { if (this.req != null) { this.req.setAttribute(aName, aValue); } else { this.attributesMap.put(aName, aValue); } } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#setCharacterEncoding(java.lang.String) */ @Override public void setCharacterEncoding(final String anEnv) throws UnsupportedEncodingException { if (this.req != null) { this.req.setCharacterEncoding(anEnv); } } /* * (non-Javadoc) * @see javax.servlet.ServletRequest#setCharacterEncoding(java.lang.String) */ public void setPathInfo(final String pathInfo) { this.pathInfo = pathInfo; } @Override public long getContentLengthLong() { if (this.req != null) { return this.req.getContentLengthLong(); } return 0; } @Override public String changeSessionId() { if (this.req != null) { return this.req.changeSessionId(); } return null; } @Override public T upgrade(Class handlerClass) throws IOException, ServletException { return null; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/server/MockHttpServletResponse.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.server; import java.io.IOException; import java.io.PrintWriter; import java.util.Collection; import java.util.Locale; import javax.servlet.ServletOutputStream; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; /** * @author Rohart Bastien */ public class MockHttpServletResponse implements HttpServletResponse { HttpServletResponse res = null; public MockHttpServletResponse() { } @Override public String getCharacterEncoding() { if (res != null) { return res.getCharacterEncoding(); } return null; } @Override public String getContentType() { if (res != null) { return res.getContentType(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletResponse#getOutputStream() */ @Override public ServletOutputStream getOutputStream() throws IOException { if (res != null) { return res.getOutputStream(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletResponse#getWriter() */ @Override public PrintWriter getWriter() throws IOException { if (res != null) { return res.getWriter(); } return null; } /* * (non-Javadoc) * @see javax.servlet.ServletResponse#setCharacterEncoding(java.lang.String) */ @Override public void setCharacterEncoding(String charset) { res.setCharacterEncoding(charset); } /* * (non-Javadoc) * @see javax.servlet.ServletResponse#setContentLength(int) */ @Override public void setContentLength(int len) { res.setContentLength(len); } /* * (non-Javadoc) * @see javax.servlet.ServletResponse#setContentType(java.lang.String) */ @Override public void setContentType(String type) { res.setContentType(type); } /* * (non-Javadoc) * @see javax.servlet.ServletResponse#setBufferSize(int) */ @Override public void setBufferSize(int size) { res.setBufferSize(size); } /* * (non-Javadoc) * @see javax.servlet.ServletResponse#getBufferSize() */ @Override public int getBufferSize() { if (res != null) { return res.getBufferSize(); } return 0; } /* * (non-Javadoc) * @see javax.servlet.ServletResponse#flushBuffer() */ @Override public void flushBuffer() throws IOException { res.flushBuffer(); } /* * (non-Javadoc) * @see javax.servlet.ServletResponse#resetBuffer() */ @Override public void resetBuffer() { res.resetBuffer(); } /* * (non-Javadoc) * @see javax.servlet.ServletResponse#isCommitted() */ @Override public boolean isCommitted() { if (res.isCommitted()) { return true; } return false; } /* * (non-Javadoc) * @see javax.servlet.ServletResponse#reset() */ @Override public void reset() { res.reset(); } /* * (non-Javadoc) * @see javax.servlet.ServletResponse#setLocale(java.util.Locale) */ @Override public void setLocale(Locale loc) { res.setLocale(loc); } /* * (non-Javadoc) * @see javax.servlet.ServletResponse#getLocale() */ @Override public Locale getLocale() { if (res != null) { return res.getLocale(); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#addCookie(javax.servlet.http.Cookie) */ @Override public void addCookie(Cookie cookie) { if (cookie != null) { res.addCookie(cookie); } } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#containsHeader(java.lang.String) */ @Override public boolean containsHeader(String name) { if (res.containsHeader(name)) { return true; } return false; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#encodeURL(java.lang.String) */ @Override public String encodeURL(String url) { if (res != null) { return res.encodeURL(url); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#encodeRedirectURL(java.lang.String) */ @Override public String encodeRedirectURL(String url) { if (res != null) { return res.encodeRedirectURL(url); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#encodeUrl(java.lang.String) */ @Override public String encodeUrl(String url) { if (res != null) { return res.encodeUrl(url); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#encodeRedirectUrl(java.lang.String) */ @Override public String encodeRedirectUrl(String url) { if (res != null) { return res.encodeRedirectUrl(url); } return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#sendError(int, java.lang.String) */ @Override public void sendError(int sc, String msg) throws IOException { res.sendError(sc, msg); } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#sendError(int) */ @Override public void sendError(int sc) throws IOException { res.sendError(sc); } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#sendRedirect(java.lang.String) */ @Override public void sendRedirect(String location) throws IOException { res.sendRedirect(location); } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#setDateHeader(java.lang.String, long) */ @Override public void setDateHeader(String name, long date) { res.setDateHeader(name, date); } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#addDateHeader(java.lang.String, long) */ @Override public void addDateHeader(String name, long date) { res.addDateHeader(name, date); } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#setHeader(java.lang.String, java.lang.String) */ @Override public void setHeader(String name, String value) { res.setHeader(name, value); } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#addHeader(java.lang.String, java.lang.String) */ @Override public void addHeader(String name, String value) { if (res != null) { res.addHeader(name, value); } } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#setIntHeader(java.lang.String, int) */ @Override public void setIntHeader(String name, int value) { res.setIntHeader(name, value); } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#addIntHeader(java.lang.String, int) */ @Override public void addIntHeader(String name, int value) { res.addIntHeader(name, value); } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#setStatus(int) */ @Override public void setStatus(int sc) { res.setStatus(sc); } /* * (non-Javadoc) * @see javax.servlet.http.HttpServletResponse#setStatus(int, java.lang.String) */ @Override public void setStatus(int sc, String sm) { res.setStatus(sc, sm); } @Override public int getStatus() { if (res != null) { return res.getStatus(); } return 0; } @Override public String getHeader(String name) { if (res != null) { return res.getHeader(name); } return null; } @Override public Collection getHeaders(String name) { if (res != null) { return res.getHeaders(name); } return null; } @Override public Collection getHeaderNames() { if (res != null) { return res.getHeaderNames(); } return null; } @Override public void setContentLengthLong(long len) { if (res != null) { res.setContentLengthLong(len); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/test/toolkit/server/MockHttpSession.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.server; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionContext; /** * @author Ruiheng Fan */ public class MockHttpSession implements HttpSession { Map attributesMap = new HashMap<>(); String id = new Date().getTime() + ""; /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#getCreationTime() */ @Override public long getCreationTime() { // TODO Auto-generated method stub return 0; } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#getId() */ @Override public String getId() { return this.id; } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#getLastAccessedTime() */ @Override public long getLastAccessedTime() { // TODO Auto-generated method stub return 0; } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#getServletContext() */ @Override public ServletContext getServletContext() { // TODO Auto-generated method stub return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#setMaxInactiveInterval(int) */ @Override public void setMaxInactiveInterval(final int interval) { // TODO Auto-generated method stub } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#getMaxInactiveInterval() */ @Override public int getMaxInactiveInterval() { // TODO Auto-generated method stub return 0; } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#getSessionContext() */ @Override public HttpSessionContext getSessionContext() { // TODO Auto-generated method stub return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#getAttribute(java.lang.String) */ @Override public Object getAttribute(final String name) { return this.attributesMap.get(name); } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#getValue(java.lang.String) */ @Override public Object getValue(final String name) { // TODO Auto-generated method stub return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#getAttributeNames() */ @Override public Enumeration getAttributeNames() { // TODO Auto-generated method stub return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#getValueNames() */ @Override public String[] getValueNames() { // TODO Auto-generated method stub return null; } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#setAttribute(java.lang.String, java.lang.Object) */ @Override public void setAttribute(final String name, final Object value) { this.attributesMap.put(name, value); } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#putValue(java.lang.String, java.lang.Object) */ @Override public void putValue(final String name, final Object value) { // TODO Auto-generated method stub } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#removeAttribute(java.lang.String) */ @Override public void removeAttribute(final String name) { // TODO Auto-generated method stub } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#removeValue(java.lang.String) */ @Override public void removeValue(final String name) { // TODO Auto-generated method stub } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#invalidate() */ @Override public void invalidate() { // TODO Auto-generated method stub } /* * (non-Javadoc) * @see javax.servlet.http.HttpSession#isNew() */ @Override public boolean isNew() { // TODO Auto-generated method stub return false; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/web/rest/server/api/page/builder/PageItemBuilder.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.page.builder; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; import java.util.Date; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.engine.page.Page; import org.bonitasoft.web.rest.model.portal.page.PageItem; import org.bonitasoft.web.rest.server.datastore.page.PageDatastore; /** * @author Fabio Lombardi */ public class PageItemBuilder { protected long id = 1L; protected String urlToken = "custompage_aName"; protected String displayName = "aDisplayName"; protected String description = "aDescription"; protected boolean isProvided = false; protected Date creation_date = new Date(1); protected long createdBy = 1L; protected Date last_update_date = new Date(1); protected String contentName = "page.zip"; protected long updatedBy = 1L; public static PageItemBuilder aPageItem() { return new PageItemBuilder(); } public PageItem build() throws IOException, URISyntaxException, ServerAPIException, BonitaHomeNotSetException, UnknownAPITypeException { final PageItem item = new PageItem(); item.setId(id); item.setUrlToken(urlToken); item.setDisplayName(displayName); item.setDescription(description); item.setIsProvided(isProvided); item.setCreationDate(creation_date); item.setCreatedByUserId(createdBy); item.setLastUpdateDate(last_update_date); item.setUpdatedByUserId(updatedBy); item.setContentName(contentName); String zipFileName = "/page.zip"; final URL zipFileUrl = getClass().getResource(zipFileName); final File zipFile = new File(zipFileUrl.toURI()); //store page zip into database String pageZipKey = PlatformAPIAccessor.getTemporaryContentAPI() .storeTempFile(new FileContent(zipFile.getName(), new FileInputStream(zipFile), "application/zip")); item.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, pageZipKey); return item; } public PageItemBuilder fromEngineItem(final Page page) { id = page.getId(); urlToken = page.getName(); displayName = page.getDisplayName(); description = page.getDescription(); isProvided = page.isProvided(); creation_date = page.getInstallationDate(); createdBy = page.getInstalledBy(); last_update_date = page.getLastModificationDate(); updatedBy = page.getLastUpdatedBy(); contentName = page.getContentName(); return this; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/web/test/AbstractConsoleTest.java ================================================ /** * Copyright (C) 2022 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.web.test; import static junit.framework.Assert.assertTrue; import javax.servlet.http.HttpSession; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.test.toolkit.server.MockHttpServletRequest; import org.bonitasoft.test.toolkit.server.MockHttpServletResponse; import org.bonitasoft.web.rest.server.BonitaRestAPIServlet; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.FlowNodeConverter; import org.bonitasoft.web.rest.server.framework.APIServletCall; import org.bonitasoft.web.toolkit.client.data.item.Item; /** * @author Vincent Elcrin */ public abstract class AbstractConsoleTest extends AbstractJUnitWebTest { /* * (non-Javadoc) * @see org.bonitasoft.console.common.server.AbstractJUnitWebTest#webTestSetUp() */ @Override public void webTestSetUp() throws Exception { FlowNodeConverter.setFlowNodeConverter(new FlowNodeConverter()); new BonitaRestAPIServlet(); consoleTestSetUp(); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } public APIServletCall getAPICaller(final APISession apiSession, final String apiPath) { // Get the httpSession and set attributes final MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest(); mockHttpServletRequest.setPathInfo(apiPath); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); final HttpSession httpSession = mockHttpServletRequest.getSession(); httpSession.setAttribute(USERNAME_SESSION_PARAM, apiSession.getUserName()); httpSession.setAttribute(API_SESSION_PARAM_KEY, apiSession); // Initialize APIUser for HTTP requests of the API return new APIServletCall(mockHttpServletRequest, mockHttpServletResponse); } public abstract void consoleTestSetUp() throws Exception; protected void assertItemEquals(Item expectedItem, Item actual) { assertTrue("expected { " + expectedItem + "} \n actual {" + actual + "}", areEquals(expectedItem, actual)); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/java/org/bonitasoft/web/test/AbstractJUnitWebTest.java ================================================ /** * Copyright (C) 2022 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.web.test; import org.bonitasoft.console.common.server.i18n.I18n; import org.bonitasoft.console.common.server.utils.PlatformManagementUtils; import org.bonitasoft.test.toolkit.AbstractJUnitTest; import org.bonitasoft.test.toolkit.organization.TestToolkitCtx; import org.bonitasoft.web.toolkit.client.data.item.Item; /** * @author Vincent Elcrin */ public abstract class AbstractJUnitWebTest extends AbstractJUnitTest { /** * the request param for the username */ public static final String USERNAME_SESSION_PARAM = "username"; public static final String API_SESSION_PARAM_KEY = "apiSession"; @Override protected void testSetUp() throws Exception { // toolkit initialization I18n.getInstance(); new PlatformManagementUtils().initializePlatformConfiguration(); webTestSetUp(); } @Override protected TestToolkitCtx getContext() { return TestToolkitCtx.getInstance(); } @Override protected void testTearDown() { webTestTearDown(); } public abstract void webTestSetUp() throws Exception; protected void webTestTearDown() { } protected boolean areEquals(Item item1, Item item2) { return item1.getAttributes().equals(item2.getAttributes()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/resources/logback.xml ================================================ %d{HH:mm:ss.SSS} [%thread] %-5level \(%file:%line\) %method - %msg%n ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/resources/org/bonitasoft/test/toolkit/connector/TestConnector.impl ================================================ org.bonitasoft.test.toolkit.connector.TestConnector 1.0 org.bonitasoft.test.toolkit.connector.TestConnector org.bonitasoft.test.toolkit.connector.testConnector 1.0 aDependency.jar anOtherDependency.jar ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/main/resources/org/bonitasoft/test/toolkit/connector/TestConnector.java ================================================ /** * Copyright (C) 2022 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.test.toolkit.connector; import org.bonitasoft.engine.connector.AbstractConnector; import org.bonitasoft.engine.connector.ConnectorException; import org.bonitasoft.engine.connector.ConnectorValidationException; /** * @author Colin PUY */ public class TestConnector extends AbstractConnector { public void validateInputParameters() throws ConnectorValidationException { // Do Nothing } @Override protected void executeBusinessLogic() throws ConnectorException { // Do Nothing } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/console/common/server/auth/impl/standard/StandardAuthenticationManagerImplIT.java ================================================ /** * Copyright (C) 2022 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.console.common.server.auth.impl.standard; import static org.junit.Assert.fail; import org.bonitasoft.console.common.server.auth.AuthenticationFailedException; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; import org.bonitasoft.console.common.server.login.credentials.StandardCredentials; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.test.toolkit.server.MockHttpServletRequest; import org.bonitasoft.web.test.AbstractJUnitWebTest; import org.junit.Test; /** * @author Rohart Bastien */ public class StandardAuthenticationManagerImplIT extends AbstractJUnitWebTest { private static final String TECHNICAL_USER_USERNAME = "install"; private static final String TECHNICAL_USER_PASSWORD = "install"; @Override public void webTestSetUp() { } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Test public void testLogin() { final HttpServletRequestAccessor request = new HttpServletRequestAccessor(new MockHttpServletRequest()); try { new StandardAuthenticationManagerImpl() .authenticate(request, new StandardCredentials(TECHNICAL_USER_USERNAME, TECHNICAL_USER_PASSWORD)); } catch (final AuthenticationFailedException e) { fail("Cannot login " + e); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/console/common/server/filter/PathTraversalProtectionIT.java ================================================ /** * Copyright (C) 2026 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.console.common.server.filter; import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED; import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.util.Comparator; import java.util.stream.Stream; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.catalina.Context; import org.apache.catalina.startup.Tomcat; import org.apache.tomcat.util.descriptor.web.FilterDef; import org.apache.tomcat.util.descriptor.web.FilterMap; import org.bonitasoft.console.common.server.login.filter.AuthenticationFilter; import org.bonitasoft.console.common.server.login.filter.RestAPIAuthorizationFilter; import org.bonitasoft.console.common.server.login.filter.TokenValidatorFilter; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; /** * Integration test verifying that path traversal attacks using semicolons (..;) * are blocked when running in an embedded Tomcat container (the production container). *

* Each filter stub extends the real filter class, inheriting the actual * {@code doFilter()} → {@code matchExcludePatterns()} → * {@code URLExcludePattern} + {@code PathSanitizer} code path. * Only {@code proceedWithFiltering()} is overridden to short-circuit engine dependencies. *

* This tests the CVE-233 fix end-to-end: *

    *
  • Tomcat's URL parsing and semicolon handling
  • *
  • Filter chain execution with REQUEST and FORWARD dispatchers
  • *
  • PathSanitizer-based defense in URLExcludePattern
  • *
*/ public class PathTraversalProtectionIT { private static final int HTTP_TIMEOUT_MS = 5000; private static Tomcat tomcat; private static File baseDir; private static int port; @BeforeClass public static void startTomcat() throws Exception { tomcat = new Tomcat(); tomcat.setPort(0); baseDir = new File(System.getProperty("java.io.tmpdir"), "tomcat-it-" + System.nanoTime()); tomcat.setBaseDir(baseDir.getAbsolutePath()); Context context = tomcat.addContext("/bonita", null); // Register filters in the same order as web.xml addFilter(context, "TokenValidatorFilter", StubTokenValidatorFilter.class, "/apps/*", "REQUEST", "FORWARD"); addFilter(context, "AuthenticationFilter", StubAuthenticationFilter.class, "/apps/*", "REQUEST", "FORWARD"); addFilter(context, "RestAPIAuthorizationFilter", StubRestAPIAuthorizationFilter.class, "/API/*", "REQUEST", "FORWARD"); // Register servlets Tomcat.addServlet(context, "apps", new StubOkServlet()); context.addServletMappingDecoded("/apps/*", "apps"); Tomcat.addServlet(context, "serverAPI", new StubForbiddenServlet()); context.addServletMappingDecoded("/serverAPI/*", "serverAPI"); tomcat.start(); port = tomcat.getConnector().getLocalPort(); } @AfterClass public static void stopTomcat() throws Exception { if (tomcat != null) { tomcat.stop(); tomcat.destroy(); } if (baseDir != null && baseDir.exists()) { try (Stream paths = Files.walk(baseDir.toPath())) { paths.sorted(Comparator.reverseOrder()) .map(Path::toFile) .forEach(File::delete); } } } /** * Semicolon traversal that stays within the /apps/* context after Tomcat normalization. * Tomcat strips path parameters and normalizes '..' segments, routing the request to * /apps/serverAPI/test. The filter chain runs and blocks the request (401). */ @Test public void semicolonTraversalStayingInContextShouldBeBlocked() throws Exception { // /apps/A/B/../../serverAPI/test normalizes to /apps/serverAPI/test (within /apps/*) int status = get("/bonita/apps/A/B/..;/..;/serverAPI/test"); assertThat(status).as("Semicolon traversal staying within /apps/* context must be blocked") .isNotEqualTo(200); } /** * Percent-encoded semicolons (%3b) are NOT decoded by Tomcat during routing, so * the filter receives the raw URL. URLExcludePattern URL-decodes then strips path * parameters via PathSanitizer, correctly detecting the traversal. */ @Test public void percentEncodedSemicolonTraversalShouldBeBlocked() throws Exception { int status = get("/bonita/apps/..%3b/..%3b/serverAPI/test"); assertThat(status).as("Percent-encoded semicolon traversal must not succeed") .isNotEqualTo(200); } /** * Deep traversal that exploits the filter exclude pattern. The URL is crafted so * that the raw path matches an exclude pattern (e.g. apps/.+/API/system/session), * but after PathSanitizer strips semicolons and normalizes '..', the resolved path * no longer matches the exclude pattern. */ @Test public void deepTraversalViaExcludePatternShouldBeBlocked() throws Exception { int status = get("/bonita/apps/FAKE/API/system/session/..;/..;/..;/..;/serverAPI/x"); assertThat(status).as("Deep traversal exploiting exclude pattern must not succeed") .isNotEqualTo(200); } @Test public void normalAppRequestShouldRequireAuthentication() throws Exception { int status = get("/bonita/apps/myapp"); assertThat(status).as("Normal app request should be blocked by auth filter") .isEqualTo(SC_UNAUTHORIZED); } @Test public void urlWithEncodedSpacesShouldPassThrough() throws Exception { int status = get("/bonita/apps/My%20App/API/system/i18ntranslation"); assertThat(status).as("URL with encoded spaces in an excluded path should reach the servlet") .isEqualTo(200); } @Test public void legitimateExcludedUrlShouldPassThrough() throws Exception { int status = get("/bonita/apps/myapp/API/system/i18ntranslation"); assertThat(status).as("Legitimate excluded URL should reach the servlet") .isEqualTo(200); } // --- Helper methods --- private static int get(String path) throws IOException { URL url = new URL("http://localhost:" + port + path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setInstanceFollowRedirects(false); conn.setConnectTimeout(HTTP_TIMEOUT_MS); conn.setReadTimeout(HTTP_TIMEOUT_MS); try { return conn.getResponseCode(); } finally { conn.disconnect(); } } private static void addFilter(Context ctx, String name, Class clazz, String urlPattern, String... dispatchers) { FilterDef filterDef = new FilterDef(); filterDef.setFilterName(name); filterDef.setFilterClass(clazz.getName()); ctx.addFilterDef(filterDef); FilterMap filterMap = new FilterMap(); filterMap.setFilterName(name); filterMap.addURLPattern(urlPattern); for (String d : dispatchers) { filterMap.setDispatcher(d); } ctx.addFilterMap(filterMap); } // --- Stub filters (public static for Tomcat reflection) --- public static class StubTokenValidatorFilter extends TokenValidatorFilter { @Override public void proceedWithFiltering(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { ((HttpServletResponse) response).setStatus(SC_UNAUTHORIZED); } } public static class StubAuthenticationFilter extends AuthenticationFilter { @Override public void proceedWithFiltering(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { ((HttpServletResponse) response).setStatus(SC_UNAUTHORIZED); } } public static class StubRestAPIAuthorizationFilter extends RestAPIAuthorizationFilter { @Override public void proceedWithFiltering(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException { ((HttpServletResponse) response).setStatus(SC_UNAUTHORIZED); } } // --- Stub servlets --- public static class StubOkServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setStatus(200); resp.getWriter().write("OK"); } } public static class StubForbiddenServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setStatus(HttpServletResponse.SC_FORBIDDEN); resp.getWriter().write("FORBIDDEN"); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/WaitUntil.java ================================================ /** * Copyright (C) 2022 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.web.rest.server; import static org.junit.Assert.assertTrue; import java.util.Date; /** * @author Baptiste Mesta */ public abstract class WaitUntil { private final int timeout; private final int repeatEach; private final boolean throwExceptions; /** * @param repeatEach * time to wait for before retrying, if condition not fullfilled, in milliseconds * @param timeout * max time to wait for, in milliseconds */ public WaitUntil(final int repeatEach, final int timeout) { this(repeatEach, timeout, true); } /** * @param repeatEach * time to wait for before retrying, if condition not fullfilled, in milliseconds * @param timeout * max time to wait for, in milliseconds * @param throwExceptions * can the check condition throw exceptions? */ public WaitUntil(final int repeatEach, final int timeout, final boolean throwExceptions) { this.throwExceptions = throwExceptions; assertTrue("timeout is not big enough", repeatEach < timeout); this.repeatEach = repeatEach; this.timeout = timeout; } public boolean waitUntil() throws Exception { final long limit = new Date().getTime() + timeout; while (new Date().getTime() < limit) { Thread.sleep(repeatEach); if (checkCondition()) { return true; } } return checkCondition(); } protected boolean checkCondition() throws Exception { if (throwExceptions) { return check(); } else { try { return check(); } catch (final Exception e) { // do nothing } return false; } }; /** * Condition to check for. * * @return true if condition is true, false otherwise. * @throws Exception */ protected abstract boolean check() throws Exception; } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/application/APIApplicationIT.java ================================================ /** * 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.web.rest.server.api.application; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.web.rest.server.api.page.builder.PageItemBuilder.aPageItem; import static org.junit.Assert.*; import static org.mockito.Mockito.spy; import java.io.InputStream; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.business.application.Application; import org.bonitasoft.engine.business.application.ApplicationImportPolicy; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.application.*; import org.bonitasoft.web.rest.model.portal.page.PageItem; import org.bonitasoft.web.rest.server.api.applicationpage.APIApplicationDataStoreFactory; import org.bonitasoft.web.rest.server.datastore.application.ApplicationDataStoreCreator; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.After; import org.junit.Assert; import org.junit.Test; public class APIApplicationIT extends AbstractConsoleTest { public static final String HOME_PAGE_ZIP = "/homePage.zip"; public static final String LAYOUT_ZIP = "/layout.zip"; public static final String THEME_ZIP = "/theme.zip"; public static final String APP_LINK_DESCRIPTOR = "/appLinkDescriptor.xml"; private APIApplication apiApplication; private APISession session; @Override public void consoleTestSetUp() throws Exception { session = getInitiator().getSession(); apiApplication = spy( new APIApplication(new ApplicationDataStoreCreator(), new APIApplicationDataStoreFactory())); apiApplication.setCaller(getAPICaller(session, "API/living/application")); } private PageAPI getPageAPI() throws Exception { return TenantAPIAccessor.getCustomPageAPI(session); } private ApplicationAPI getApplicationAPI() throws Exception { return TenantAPIAccessor.getApplicationAPI(session); } @After public void cleanPagesAndApplications() throws Exception { final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100000).done(); List apps = getApplicationAPI().searchApplications(searchOptions).getResult(); apps.stream().map(Application::getId).forEach(t -> { try { getApplicationAPI().deleteApplication(t); } catch (Exception e) { e.printStackTrace(); } }); List pages = getPageAPI().searchPages(searchOptions).getResult(); var ids = pages.stream().map(Page::getId).toList(); getPageAPI().deletePages(ids); } private PageItem addPage(String pageFileName) throws Exception { final PageItem pageItem = aPageItem().build(); final InputStream pageInputStream = getClass().getResourceAsStream(pageFileName); final byte[] pageContent = IOUtils.toByteArray(pageInputStream); return addPageItemToRepository(pageItem.getContentName(), pageContent); } private PageItem addPageItemToRepository(final String pageContentName, final byte[] pageContent) throws Exception { return aPageItem().fromEngineItem(getPageAPI().createPage(pageContentName, pageContent)).build(); } @Test public void should_add_ApplicationLink() { // Given final ApplicationLinkItem linkItem = ApplicationLinkDefinition.get().createItem(); linkItem.setToken("tokenLink"); linkItem.setDisplayName("Link"); linkItem.setVersion("1.0"); linkItem.setProfileId(2L); linkItem.setState("ACTIVATED"); // When var createdLink = apiApplication.add(linkItem); // Then Map attributes = new HashMap<>(linkItem.getAttributes().size()); linkItem.getAttributes().keySet().forEach(k -> attributes.put(k, createdLink.getAttributes().get(k))); Assert.assertEquals(new HashMap<>(linkItem.getAttributes()), attributes); assertThat(createdLink.isLink()).isTrue(); } @Test public void should_add_LegacyApplication() throws Exception { // Given var pair = createLegacyApplicationWithOriginal(); var original = pair.getKey(); var legacyApp = pair.getValue(); // Then Map attributes = new HashMap<>(original.getAttributes().size()); original.getAttributes().keySet().forEach(k -> attributes.put(k, legacyApp.getAttributes().get(k))); Assert.assertEquals(new HashMap<>(original.getAttributes()), attributes); assertThat(legacyApp.isLink()).isFalse(); } @Test public void should_update_ApplicationLink() throws Exception { // Given getApplicationAPI().importApplications( IOUtils.toByteArray(getClass().getResourceAsStream(APP_LINK_DESCRIPTOR)), ApplicationImportPolicy.REPLACE_DUPLICATES); ApplicationLinkItem linkItem = (ApplicationLinkItem) apiApplication .search(0, 1, null, null, Collections.singletonMap("token", "app1")).getResults().get(0); Map attributes = Map.of(AbstractApplicationItem.ATTRIBUTE_DISPLAY_NAME, "Link Updated"); // When var updatedLink = apiApplication.update(linkItem.getId(), attributes); // Then updated assertEquals("Link Updated", updatedLink.getDisplayName()); assertEquals("Link Updated", apiApplication.get(linkItem.getId()).getDisplayName()); assertThat(linkItem.isLink()).isTrue(); } @Test public void should_update_LegacyApplication() throws Exception { // Given var legacyApp = createLegacyApplication(); // When Map attributes = Map.of(AbstractApplicationItem.ATTRIBUTE_DISPLAY_NAME, "Legacy Updated"); var updatedItem = apiApplication.update(legacyApp.getId(), attributes); // Then assertEquals("Legacy Updated", updatedItem.getDisplayName()); assertThat(updatedItem.isLink()).isFalse(); } @Test public void should_search_not_support_filtering_on_ApplicationLinks() throws Exception { // Given var legacyApp = createLegacyApplication(); // When final String search = legacyApp.getDisplayName(); final String orders = ApplicationItem.ATTRIBUTE_TOKEN + " DESC"; final HashMap filters = new HashMap<>(); filters.put(ApplicationItem.ATTRIBUTE_LINK, "true"); // Then assertThrows( "Expected exception: The search does not support filtering on application links in this edition.", BonitaRuntimeException.class, () -> apiApplication.search(0, 1, search, orders, filters)); } private ApplicationItem createLegacyApplication() throws Exception { return createLegacyApplicationWithOriginal().getValue(); } /** * Create a legacy application. * * @return a pair with the constructed item as key and the api call result as value. * @throws Exception exception during creation */ private Entry createLegacyApplicationWithOriginal() throws Exception { addPage(HOME_PAGE_ZIP); final PageItem layout = addPage(LAYOUT_ZIP); final PageItem theme = addPage(THEME_ZIP); final ApplicationItem legacyItem = ApplicationDefinition.get().createItem(); legacyItem.setToken("tokenLegacy"); legacyItem.setDisplayName("Legacy"); legacyItem.setVersion("1.0"); legacyItem.setProfileId(2L); legacyItem.setState("ACTIVATED"); legacyItem.setLayoutId(layout.getId().toLong()); legacyItem.setThemeId(theme.getId().toLong()); return Collections.singletonMap(legacyItem, (ApplicationItem) apiApplication.add(legacyItem)).entrySet() .iterator().next(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APIArchivedCaseIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.cases; import java.util.ArrayList; import java.util.Arrays; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.test.toolkit.bpm.TestCase; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseItem; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Assert; import org.junit.Test; /** * @author Séverin Moussel */ public class APIArchivedCaseIT extends AbstractConsoleTest { /** * @return */ private APIArchivedCase getAPIArchivedCase() { final APIArchivedCase api = new APIArchivedCase(); api.setCaller(getAPICaller(getInitiator().getSession(), "API/bpm/archivedCase")); return api; } @Override public void consoleTestSetUp() throws Exception { } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GET // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * @return */ private TestCase initArchivedCaseForGet() { final TestCase testArchivedCase = TestProcessFactory.getDefaultHumanTaskProcess() .addActor(getInitiator()) .startCase(); testArchivedCase.getNextHumanTask().assignTo(getInitiator()).archive(); return testArchivedCase; } private void assertEquals(final String message, final ArchivedProcessInstance engineItem, final ArchivedCaseItem consoleItem) { Assert.assertEquals(message, engineItem.getId(), consoleItem.getId().toLong().longValue()); Assert.assertEquals(message, engineItem.getLastUpdate().toString(), consoleItem.getLastUpdateDate().toString()); Assert.assertEquals(message, engineItem.getState(), consoleItem.getState()); Assert.assertEquals(message, engineItem.getStartDate(), consoleItem.getStartDate()); Assert.assertEquals(message, engineItem.getStartedBy(), (long) consoleItem.getStartedByUserId().toLong()); Assert.assertEquals(message, engineItem.getEndDate(), consoleItem.getEndDate()); Assert.assertEquals(message, engineItem.getProcessDefinitionId(), (long) consoleItem.getProcessId().toLong()); Assert.assertNotNull(message, consoleItem.getArchivedDate()); } @Test public void testGetArchivedCase() { final TestCase testCase = initArchivedCaseForGet(); final ArchivedProcessInstance archivedProcessInstance = testCase.getArchive(); final ArchivedCaseItem caseItem = getAPIArchivedCase().runGet(APIID.makeAPIID(archivedProcessInstance.getId()), new ArrayList<>(), new ArrayList<>()); Assert.assertNotNull("ArchivedCase not found", caseItem); assertEquals("Wrong case found" + ". Expected=" + archivedProcessInstance + ". Found=" + caseItem.toString(), archivedProcessInstance, caseItem); } @Test public void testGetArchivedCaseWithDeploys() { final TestCase testCase = initArchivedCaseForGet(); final ArchivedProcessInstance archivedProcessInstance = testCase.getArchive(); final ArchivedCaseItem caseItem = getAPIArchivedCase().runGet(APIID.makeAPIID(archivedProcessInstance.getId()), Arrays.asList(ArchivedCaseItem.ATTRIBUTE_PROCESS_ID, ArchivedCaseItem.ATTRIBUTE_STARTED_BY_USER_ID), new ArrayList<>()); Assert.assertNotNull("Failed to deploy process", caseItem.getProcess()); Assert.assertEquals("Wrong process deployed", testCase.getProcessInstance().getName(), caseItem.getProcess().getName()); Assert.assertNotNull("Failed to deploy intiator user", caseItem.getStartedByUserId()); Assert.assertEquals("Wrong process deployed", getInitiator().getUserName(), caseItem.getStartedByUser().getUserName()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APIArchivedCommentIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.cases; import static org.junit.Assert.assertEquals; import java.util.HashMap; import org.bonitasoft.test.toolkit.bpm.TestCase; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCommentItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Test; /** * @author Paul AMAR */ public class APIArchivedCommentIT extends AbstractConsoleTest { private APIArchivedComment apiArchivedComment; @Override public void consoleTestSetUp() throws Exception { this.apiArchivedComment = new APIArchivedComment(); this.apiArchivedComment .setCaller(getAPICaller(TestUserFactory.getJohnCarpenter().getSession(), "API/bpm/archivedComment")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Test public void testSearch() throws Exception { TestCase aCase = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator()).startCase(); aCase.getNextHumanTask().assignTo(getInitiator()); aCase.addComments(getInitiator(), 12, "mon Commentaire"); aCase.execute(); final ItemSearchResult mesResultats = this.apiArchivedComment.search(0, 12, "", "", new HashMap<>()); assertEquals(mesResultats.getLength(), 12); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseDocumentAnotherIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.cases; import static junit.framework.Assert.assertEquals; import java.util.HashMap; import java.util.Map; import org.bonitasoft.test.toolkit.bpm.TestCase; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Test; /** * @author Colin PUY */ public class APICaseDocumentAnotherIT extends AbstractConsoleTest { private APICaseDocument apiCaseDocument; @Override public void consoleTestSetUp() throws Exception { apiCaseDocument = new APICaseDocument(); apiCaseDocument.setCaller(getAPICaller(getInitiator().getSession(), "API/bpm/caseDocument")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Test public void caseDocumentsCanBeCountedByCaseId() throws Exception { TestCase startCase = TestProcessFactory.getProcessWithDocumentAttached().addActor(getInitiator()).startCase(); Map caseIdfilter = buildCaseIdFilter(startCase.getId()); ItemSearchResult searchResult = apiCaseDocument.runSearch(0, 0, null, null, caseIdfilter, null, null); assertEquals(1L, searchResult.getTotal()); } private Map buildCaseIdFilter(long caseId) { Map filters = new HashMap<>(); filters.put(CaseDocumentItem.ATTRIBUTE_CASE_ID, String.valueOf(caseId)); return filters; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseDocumentIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.cases; import java.io.File; import java.io.FileInputStream; import java.util.HashMap; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.test.toolkit.bpm.TestCase; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Assert; import org.junit.Test; /** * @author Vincent Elcrin */ public class APICaseDocumentIT extends AbstractConsoleTest { private APICaseDocument apiCaseDocument; private Document expectedDocument; /* * (non-Javadoc) * @see org.bonitasoft.console.server.AbstractConsoleTest#consoleTestSetUp() */ @Override public void consoleTestSetUp() throws Exception { apiCaseDocument = new APICaseDocument(); apiCaseDocument.setCaller(getAPICaller(TestUserFactory.getJohnCarpenter().getSession(), "API/bpm/caseDocument")); // create process with attached document final TestCase testCase = TestProcessFactory.getProcessWithDocumentAttached() .addActor(getInitiator()) .startCase(); expectedDocument = TenantAPIAccessor.getProcessAPI(getInitiator().getSession()) .getLastDocument(testCase.getId(), "Document 1667"); } private void assertDocumentsMatch(final Document document, final CaseDocumentItem caseDocumentItem) { Assert.assertEquals("Name is different", document.getName(), caseDocumentItem.getName()); Assert.assertEquals("File name is different", document.getContentFileName(), caseDocumentItem.getFileName()); Assert.assertEquals("Author is different", document.getAuthor(), (long) caseDocumentItem.getSubmittedBy().toLong()); Assert.assertEquals("Mime type is different", document.getContentMimeType(), caseDocumentItem.getMIMEType()); Assert.assertEquals("Content storage id is different", document.getContentStorageId(), caseDocumentItem.getStorageId()); Assert.assertEquals("Creation date is different", document.getCreationDate(), caseDocumentItem.getCreationDate()); Assert.assertEquals("Id is different", document.getId(), (long) caseDocumentItem.getId().toLong()); Assert.assertEquals("Process instance id is different", document.getProcessInstanceId(), (long) caseDocumentItem.getCaseId().toLong()); Assert.assertEquals("Url is different", document.getUrl(), caseDocumentItem.getURL()); } /* * (non-Javadoc) * @see org.bonitasoft.test.toolkit.AbstractJUnitTest#getInitiator() */ @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Test public void testAPISearch() { final ItemSearchResult res = apiCaseDocument.search(0, 10, null, null, new HashMap<>()); Assert.assertNotNull(res); Assert.assertNotNull(res.getResults()); Assert.assertTrue(res.getResults().size() > 0); assertDocumentsMatch(expectedDocument, res.getResults().get(0)); } @Test public void testAPISearchSupervisedResultEmpty() { final HashMap filters = new HashMap<>(); filters.put(CaseDocumentItem.FILTER_SUPERVISOR_ID, String.valueOf(getInitiator().getId())); final ItemSearchResult res = apiCaseDocument.search(0, 10, null, null, filters); Assert.assertNotNull(res); Assert.assertNotNull(res.getResults()); Assert.assertTrue(res.getResults().size() == 0); } @Test public void testAPISearchSupervised() throws Exception { // set initiator as process supervisor TestProcessFactory.getProcessWithDocumentAttached() .addSupervisor(getInitiator()); final HashMap filters = new HashMap<>(); filters.put(CaseDocumentItem.FILTER_SUPERVISOR_ID, String.valueOf(getInitiator().getId())); final ItemSearchResult res = apiCaseDocument.search(0, 10, null, null, filters); Assert.assertNotNull(res); Assert.assertNotNull(res.getResults()); Assert.assertTrue(res.getResults().size() > 0); assertDocumentsMatch(expectedDocument, res.getResults().get(0)); } @Test public void testAPIGet() { assertDocumentsMatch(expectedDocument, apiCaseDocument.get(APIID.makeAPIID(expectedDocument.getId()))); } @Test public void testAPICaseDocumentUpdateUrl() { final HashMap attributes = new HashMap<>(); attributes.put(CaseDocumentItem.ATTRIBUTE_CASE_ID, String.valueOf(expectedDocument.getProcessInstanceId())); attributes.put(CaseDocumentItem.ATTRIBUTE_NAME, expectedDocument.getName()); attributes.put(CaseDocumentItem.ATTRIBUTE_CONTENT_FILENAME, expectedDocument.getContentFileName()); attributes.put(CaseDocumentItem.ATTRIBUTE_CONTENT_MIMETYPE, expectedDocument.getContentMimeType()); attributes.put(CaseDocumentItem.ATTRIBUTE_URL, "newurl"); Assert.assertEquals("newurl", apiCaseDocument.update(APIID.makeAPIID(expectedDocument.getId()), attributes).getURL()); } @Test public void testAPICaseDocumentUpdateFile() throws Exception { final HashMap attributes = new HashMap<>(); attributes.put(CaseDocumentItem.ATTRIBUTE_CASE_ID, String.valueOf(expectedDocument.getProcessInstanceId())); attributes.put(CaseDocumentItem.ATTRIBUTE_NAME, expectedDocument.getName()); attributes.put(CaseDocumentItem.ATTRIBUTE_CONTENT_FILENAME, expectedDocument.getContentFileName()); attributes.put(CaseDocumentItem.ATTRIBUTE_CONTENT_MIMETYPE, expectedDocument.getContentMimeType()); final File tmpDir = WebBonitaConstantsUtils.getTenantInstance().getTempFolder(); tmpDir.mkdirs(); final File file = new File(tmpDir, "thisismynewfile.doc"); file.createNewFile(); String fileKey = PlatformAPIAccessor.getTemporaryContentAPI() .storeTempFile(new FileContent("thisismynewfile.doc", new FileInputStream(file), "text/plain")); attributes.put(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, fileKey); CaseDocumentItem doc = apiCaseDocument.update(APIID.makeAPIID(expectedDocument.getId()), attributes); Assert.assertNotNull("Failed while updating the case document", doc); } @Test(expected = APIException.class) public void testMalformedUpdate() { apiCaseDocument.update(APIID.makeAPIID(expectedDocument.getId()), new HashMap<>()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.cases; import static org.bonitasoft.test.toolkit.bpm.ProcessVariable.*; import static org.bonitasoft.web.rest.model.builder.bpm.cases.CaseItemBuilder.aCaseItem; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException; import org.bonitasoft.test.toolkit.bpm.*; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.cases.CaseItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Assert; import org.junit.Test; /** * @author Séverin Moussel */ public class APICaseIT extends AbstractConsoleTest { private APICase apiCase; @Override public void consoleTestSetUp() throws Exception { apiCase = new APICase(); apiCase.setCaller(getAPICaller(getInitiator().getSession(), "API/bpm/case")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GET // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private ProcessInstance getProcessInstance(final APIID caseId) throws Exception { try { return TenantAPIAccessor.getProcessAPI(getInitiator().getSession()).getProcessInstance(caseId.toLong()); } catch (final ProcessInstanceNotFoundException e) { return null; } } private void assertEquals(final String message, final ProcessInstance engineItem, final CaseItem consoleItem) { Assert.assertEquals(message, engineItem.getId(), consoleItem.getId().toLong().longValue()); Assert.assertEquals(message, engineItem.getLastUpdate(), consoleItem.getLastUpdateDate()); Assert.assertEquals(message, engineItem.getState(), consoleItem.getState()); Assert.assertEquals(message, engineItem.getStartDate(), consoleItem.getStartDate()); Assert.assertEquals(message, engineItem.getStartedBy(), (long) consoleItem.getStartedByUserId().getPartAsLong(0).longValue()); Assert.assertEquals(message, engineItem.getEndDate(), consoleItem.getEndDate()); Assert.assertEquals(message, engineItem.getProcessDefinitionId(), (long) consoleItem.getProcessId().toLong()); Assert.assertEquals(message, engineItem.getCallerId(), (long) consoleItem.getCallerId().getPartAsLong(0).longValue()); } @Test public void testGetCase() { final TestCase testCase = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator()).startCase(); final CaseItem caseItem = apiCase.runGet(APIID.makeAPIID(testCase.getId()), new ArrayList<>(), new ArrayList<>()); Assert.assertNotNull("Case not found", caseItem); assertEquals("Wrong case found", testCase.getProcessInstance(), caseItem); } @Test public void should_get_child_process_with_callerId() throws Exception { final TestProcess childProcess = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator()) .enable(); TestProcess parentProcess = TestProcessFactory.getCallActivityProcess(childProcess.getProcessDefinition()) .addActor(getInitiator()).enable(); try { final TestCase parentCase = parentProcess.startCase(); TestHumanTask humanTask = parentCase.getNextHumanTask(); final CaseItem childCaseItem = apiCase.runGet( APIID.makeAPIID(humanTask.getHumanTaskInstance().getParentProcessInstanceId()), new ArrayList<>(), new ArrayList<>()); Assert.assertNotNull("Case not found", childCaseItem); assertEquals("Wrong case found", getProcessInstance(childCaseItem.getId()), childCaseItem); } finally { TestProcessFactory.getInstance().delete(parentProcess); TestProcessFactory.getInstance().delete(childProcess); } } @Test public void testGetCaseWithDeploys() { final TestProcess testProcess = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator()); final TestCase testCase = testProcess.startCase(); final CaseItem caseItem = apiCase.runGet(APIID.makeAPIID(testCase.getId()), Arrays.asList(CaseItem.ATTRIBUTE_PROCESS_ID, CaseItem.ATTRIBUTE_STARTED_BY_USER_ID), new ArrayList<>()); Assert.assertNotNull("Failed to deploy process", caseItem.getProcess()); Assert.assertEquals("Wrong process deployed", testCase.getProcessInstance().getName(), caseItem.getProcess().getName()); Assert.assertNotNull("Failed to deploy intiator user", caseItem.getStartedByUserId()); Assert.assertEquals("Wrong process deployed", getInitiator().getUserName(), caseItem.getStartedByUser().getUserName()); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SEARCH // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Init cases for search */ private void initCasesForSearch() { final TestProcess testProcess = TestProcessFactory.getDefaultHumanTaskProcess(); testProcess.addActor(TestUserFactory.getMrSpechar()); // 13 cases for current User and 1 for another user testProcess.startCases(13); testProcess.startCase(TestUserFactory.getMrSpechar()); // Another process final TestProcess testProcess2 = TestProcessFactory.getHumanTaskProcess("SecondProcess"); testProcess2.addActor(getInitiator()); // 1 case for current User testProcess2.startCase(); } /** * Test search for user (Initiator) */ @Test public void testSearchByInitiator() { initCasesForSearch(); // Search for current User final Map filters = new HashMap<>(); filters.put(CaseItem.ATTRIBUTE_STARTED_BY_USER_ID, String.valueOf(getInitiator().getId())); final ItemSearchResult caseItems = apiCase.runSearch(0, 10, null, null, filters, null, null); checkSearchResults(caseItems, 10, 14); } /** * Test search for user, with only worked on cases */ @Test public void testSearchUserWorkedOn() { initCasesForSearch(); // Search for current User final Map filters = new HashMap<>(); filters.put(CaseItem.FILTER_USER_ID, String.valueOf(getInitiator().getId())); final ItemSearchResult caseItems = apiCase.runSearch(0, 10, null, null, filters, null, null); checkSearchResults(caseItems, 10, 14); } /** * Test search for user by process */ @Test public void testSearchByProcess() { initCasesForSearch(); final TestProcess testProcess3 = TestProcessFactory.getHumanTaskProcess("ThirdProcess"); testProcess3.addActor(getInitiator()); testProcess3.startCases(5); // Search for current User final Map filters = new HashMap<>(); filters.put(CaseItem.ATTRIBUTE_PROCESS_ID, String.valueOf(testProcess3.getId())); final ItemSearchResult caseItems = apiCase.runSearch(0, 3, null, null, filters, null, null); checkSearchResults(caseItems, 3, 5); } /** * Admin test cases */ @Test public void testSearchAdministrator() { initCasesForSearch(); final ItemSearchResult caseItems = apiCase.runSearch(0, 10, null, null, null, null, null); // On more process (testProcess3) checkSearchResults(caseItems, 10, 15); } /** * Process Owner test cases */ @Test public void testSearchProcessOwner() { initCasesForSearch(); final TestProcess testProcess3 = TestProcessFactory.getHumanTaskProcess("ThirdProcess"); testProcess3.addActor(getInitiator()); testProcess3.addSupervisor(TestUserFactory.getRidleyScott()); testProcess3.startCases(5); // Search for current User final Map filters = new HashMap<>(); filters.put(CaseItem.FILTER_SUPERVISOR_ID, String.valueOf(TestUserFactory.getRidleyScott().getId())); final ItemSearchResult caseItems = apiCase.runSearch(0, 10, null, null, filters, null, null); checkSearchResults(caseItems, 10, 5); } // @Test This test needs to be fixed. Has it ever passed? public void testSearchTeamManager() { final Map managedList = TestUserFactory.getManagedUsers(1); final TestProcess testProcess4 = TestProcessFactory.getHumanTaskProcess("ProcessTeamManager"); testProcess4.addActor(managedList.get("managed.user0")); testProcess4.startCases(25); final TestProcess testProcess5 = TestProcessFactory.getHumanTaskProcess("Process five"); testProcess5.addActor(TestUserFactory.getRidleyScott()); testProcess5.startCases(10); // Search for current User final Map filters = new HashMap<>(); filters.put(CaseItem.FILTER_TEAM_MANAGER_ID, String.valueOf(TestUserFactory.getTeamManagerUser().getId())); final ItemSearchResult caseItems = apiCase.runSearch(0, 100, null, null, filters, null, null); checkSearchResults(caseItems, 100, 25); } private void checkSearchResults(final ItemSearchResult caseItems, final int nbResultsByPageExpected, final int nbTotalResultsExpected) { assertTrue("Empty search results", caseItems.getLength() > 0); Assert.assertEquals("Wrong page size", caseItems.getLength(), nbResultsByPageExpected); Assert.assertEquals("Wrong Total size", caseItems.getTotal(), nbTotalResultsExpected); } @Test public void we_can_start_a_case() throws Exception { final TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator()).enable(); final CaseItem item = apiCase.runAdd(aCaseItem().withProcessId(process.getId()).build()); final ProcessInstance instance = getProcessInstance(item.getId()); assertThat(instance.getProcessDefinitionId(), is(item.getProcessId().toLong())); } @Test public void we_can_start_a_case_with_user() throws Exception { final TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess() .addActor(TestUserFactory.getRidleyScott()).enable(); final CaseItem item = apiCase.runAdd(aCaseItem().withProcessId(process.getId()) .withUserId(TestUserFactory.getRidleyScott().getId()).build()); final ProcessInstance instance = getProcessInstance(item.getId()); assertThat(instance.getProcessDefinitionId(), is(item.getProcessId().toLong())); assertThat(instance.getStartedBy(), is(TestUserFactory.getRidleyScott().getId())); } @Test public void we_can_start_a_case_with_variables() throws Exception { final String jsonVariables = "[" + "{\"name\": \"stringVariable\", \"value\": \"newValue\"}," + "{\"name\": \"longVariable\", \"value\": 9}," + "{\"name\": \"dateVariable\", \"value\": 349246800000}" + "]"; final TestProcess process = createProcessWithVariables(aStringVariable("stringVariable", "firstValue"), aLongVariable("longVariable", 1L), aDateVariable("dateVariable", "428558400000")); final CaseItem item = apiCase .runAdd(aCaseItem().withProcessId(process.getId()).withVariables(jsonVariables).build()); final ProcessInstance instance = getProcessInstance(item.getId()); assertThat(instance.getProcessDefinitionId(), is(item.getProcessId().toLong())); assertThat((String) getProcessDataInstanceValue("stringVariable", instance.getId()), equalTo("newValue")); assertThat((Long) getProcessDataInstanceValue("longVariable", instance.getId()), equalTo(9L)); assertThat((Date) getProcessDataInstanceValue("dateVariable", instance.getId()), equalTo(new Date(349246800000L))); } private TestProcess createProcessWithVariables(final ProcessVariable... processVariables) { return TestProcessFactory.createProcessWithVariables("processWithVariables", processVariables) .addActor(getInitiator()).enable(); } private Serializable getProcessDataInstanceValue(final String dataName, final long processInstanceId) throws Exception { return TenantAPIAccessor.getProcessAPI(getInitiator().getSession()) .getProcessDataInstance(dataName, processInstanceId).getValue(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseVariableIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.cases; import static org.bonitasoft.test.toolkit.bpm.ProcessVariable.*; import static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID; import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.bonitasoft.test.toolkit.bpm.ProcessVariable; import org.bonitasoft.test.toolkit.bpm.TestCase; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.cases.CaseVariableDefinition; import org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Test; /** * @author Colin PUY */ public class APICaseVariableIT extends AbstractConsoleTest { private APICaseVariable apiCaseVariable; @Override public void consoleTestSetUp() throws Exception { apiCaseVariable = new APICaseVariable(); apiCaseVariable.setCaller(getAPICaller(getInitiator().getSession(), "API/bpm/caseVariable")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } private APIID buildAPIID(TestCase aCase, ProcessVariable expectedVariable) { APIID apiid = makeAPIID(Arrays.asList(String.valueOf(aCase.getId()), expectedVariable.getName())); apiid.setItemDefinition(CaseVariableDefinition.get()); return apiid; } private TestCase createCaseWithVariable(ProcessVariable... expectedVariable) { return TestProcessFactory.createProcessWithVariables("aProcessWithVariablesVariable", expectedVariable) .addActor(getInitiator()).startCase(); } private Map buildUpdateAttributes(String newValue, String type) { Map attributes = new HashMap<>(); attributes.put(CaseVariableItem.ATTRIBUTE_VALUE, newValue); attributes.put(CaseVariableItem.ATTRIBUTE_TYPE, type); return attributes; } private Map buildCaseIdFilter(TestCase aCase) { Map filters = new HashMap<>(); filters.put(CaseVariableItem.ATTRIBUTE_CASE_ID, String.valueOf(aCase.getId())); return filters; } @Test public void weCanUpdateALongValue() throws Exception { ProcessVariable expectedVariable = createLongVariable(1L); TestCase aCase = createCaseWithVariable(expectedVariable); String newLongValue = "2"; Map attributes = buildUpdateAttributes(newLongValue, Long.class.getName()); CaseVariableItem variable = apiCaseVariable.runUpdate(buildAPIID(aCase, expectedVariable), attributes); assertEquals(newLongValue, variable.getValue()); } @Test public void weCanUpdateAnIntegerValue() throws Exception { ProcessVariable expectedVariable = createIntVariable(1); TestCase aCase = createCaseWithVariable(expectedVariable); String newIntValue = "2"; Map attributes = buildUpdateAttributes(newIntValue, Integer.class.getName()); CaseVariableItem variable = apiCaseVariable.runUpdate(buildAPIID(aCase, expectedVariable), attributes); assertEquals(newIntValue, variable.getValue()); } @Test public void weCanUpdateABooleanValue() throws Exception { ProcessVariable expectedVariable = createBooleanVariable(true); TestCase aCase = createCaseWithVariable(expectedVariable); String newBooleanValue = "false"; Map attributes = buildUpdateAttributes(newBooleanValue, Boolean.class.getName()); CaseVariableItem variable = apiCaseVariable.runUpdate(buildAPIID(aCase, expectedVariable), attributes); assertEquals(newBooleanValue, variable.getValue()); } @Test public void weCanUpdateAStringValue() throws Exception { ProcessVariable expectedVariable = createStringVariable("aString"); TestCase aCase = createCaseWithVariable(expectedVariable); String newStringValue = "aNewString"; Map attributes = buildUpdateAttributes(newStringValue, String.class.getName()); CaseVariableItem variable = apiCaseVariable.runUpdate(buildAPIID(aCase, expectedVariable), attributes); assertEquals(newStringValue, variable.getValue()); } @Test public void weCanUpdateADoubleValue() throws Exception { ProcessVariable expectedVariable = createDoubleVariable(12.3d); TestCase aCase = createCaseWithVariable(expectedVariable); String newDoubleValue = "46.35"; Map attributes = buildUpdateAttributes(newDoubleValue, Double.class.getName()); CaseVariableItem variable = apiCaseVariable.runUpdate(buildAPIID(aCase, expectedVariable), attributes); assertEquals(newDoubleValue, variable.getValue()); } @Test public void search() throws Exception { TestCase aCase = createCaseWithVariable(createLongVariable(1L), createIntVariable(1), createStringVariable("hello")); Map filters = buildCaseIdFilter(aCase); ItemSearchResult searchResults = apiCaseVariable.runSearch(0, 2, null, null, filters, null, null); assertEquals(3L, searchResults.getTotal()); assertEquals(2, searchResults.getLength()); assertEquals(2, searchResults.getResults().size()); } @Test public void getReturnACaseVariable() throws Exception { ProcessVariable expectedVariable = createLongVariable(1L); TestCase aCase = createCaseWithVariable(expectedVariable); APIID apiid = buildAPIID(aCase, expectedVariable); CaseVariableItem variable = apiCaseVariable.runGet(apiid, null, null); assertEquals(expectedVariable.getName(), variable.getName()); assertEquals(expectedVariable.getClassName(), variable.getType()); assertEquals(expectedVariable.getDefaultValue().getContent(), variable.getValue()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICommentIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.cases; import static org.junit.Assert.assertEquals; import java.util.HashMap; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.test.toolkit.bpm.TestCase; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.cases.CommentItem; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Test; public class APICommentIT extends AbstractConsoleTest { private APIComment apiComment; private final String content = "Commentaire du processus par défault"; private TestCase testCase; /* * (non-Javadoc) * @see org.bonitasoft.console.server.AbstractJUnitWebTest#webTestSetUp() */ @Override public void consoleTestSetUp() throws Exception { this.apiComment = new APIComment(); this.apiComment.setCaller(getAPICaller(getInitiator().getSession(), "API/bpm/comment")); testCase = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator()).startCase(); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Test public void testAddCommentItem() throws Exception { final CommentItem commentItem = createCommentItem(this.content); this.apiComment.add(commentItem); Assertions.assertThat( TenantAPIAccessor.getProcessAPI(getInitiator().getSession()) .searchComments( new SearchOptionsBuilder(0, 10) .filter(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, testCase.getId()).done()) .getCount()) .isEqualTo(1); } @Test public void testSearchCommentItem() { testCase.addComments(getInitiator(), 3, this.content); // Set the filters final HashMap filters = new HashMap<>(); filters.put(CommentItem.ATTRIBUTE_USER_ID, String.valueOf(getInitiator().getId())); filters.put(CommentItem.ATTRIBUTE_PROCESS_INSTANCE_ID, String.valueOf(testCase.getId())); // Search the CommentItem final CommentItem item = this.apiComment.search(0, 10, null, null, filters).getResults().get(0); assertEquals("Find the wrong CommentItem with APIComment", this.content + "0", item.getAttributeValue(CommentItem.ATTRIBUTE_CONTENT)); } /** * @param comment */ private CommentItem createCommentItem(final String comment) { final CommentItem item = new CommentItem(); item.setProcessInstanceId(testCase.getId()); item.setContent(comment); return item; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APIActivityIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.test.toolkit.bpm.TestHumanTask; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem; import org.bonitasoft.web.rest.server.WaitUntil; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Test; public class APIActivityIT extends AbstractConsoleTest { private static final String JSON_UPDATE_VARIABLES = "[" + "{\"name\": \"variable1\", \"value\": \"newValue\"}," + "{\"name\": \"variable2\", \"value\": 9}," + "{\"name\": \"variable3\", \"value\": 349246800000}" + "]"; private APIActivity apiActivity; @Override public void consoleTestSetUp() throws Exception { apiActivity = new APIActivity(); apiActivity.setCaller(getAPICaller(TestUserFactory.getJohnCarpenter().getSession(), "API/bpm/activity")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Test public void api_can_update_activity_variables() throws Exception { final TestHumanTask activity = TestProcessFactory.createActivityWithVariables(getInitiator()); final Map attributes = new HashMap<>(); attributes.put(ActivityItem.ATTRIBUTE_VARIABLES, JSON_UPDATE_VARIABLES); apiActivity.runUpdate(makeAPIID(activity.getId()), attributes); assertThat(activity.getDataInstance("variable1").getValue(), is((Serializable) "newValue")); assertThat(activity.getDataInstance("variable2").getValue(), is((Serializable) 9L)); assertThat(activity.getDataInstance("variable3").getValue(), is((Serializable) new Date(349246800000L))); } @Test public void api_can_update_variables_and_terminate_activity() throws Exception { final TestHumanTask activity = TestProcessFactory.createActivityWithVariables(getInitiator()); final Map attributes = new HashMap<>(); attributes.put(ActivityItem.ATTRIBUTE_VARIABLES, JSON_UPDATE_VARIABLES); attributes.put(ActivityItem.ATTRIBUTE_STATE, ActivityItem.VALUE_STATE_COMPLETED); apiActivity.runUpdate(makeAPIID(activity.getId()), attributes); final ArchivedActivityInstance archivedActivityInstance = getArchivedDataInstance(activity); assertThat(archivedActivityInstance.getState(), is(ActivityItem.VALUE_STATE_COMPLETED)); // Can't manage to do variable verification because of asynchronous engine update ... // assertThat(getArchivedDataInstanceValue("variable1", archivedActivityInstance), is((Serializable) "newValue")); // assertThat(getArchivedDataInstanceValue("variable2", archivedActivityInstance), is((Serializable) 9L)); // assertThat(getArchivedDataInstanceValue("variable3", archivedActivityInstance), is((Serializable) new Date(349246800000L))); } // private Serializable getArchivedDataInstanceValue(String dataName, ArchivedActivityInstance archivedActivityInstance) throws Exception { // return getProcessAPI().getArchivedActivityDataInstance(dataName, archivedActivityInstance.getSourceObjectId()).getValue(); // } /** * Activity state is updated asynchronously - need to wait... :-( */ private ArchivedActivityInstance getArchivedDataInstance(final TestHumanTask activity) throws Exception { if (new WaitUntil(50, 3000) { @Override protected boolean check() throws Exception { try { final ArchivedActivityInstance instance = getProcessAPI() .getArchivedActivityInstance(activity.getId()); return ActivityItem.VALUE_STATE_COMPLETED.equals(instance.getState()); } catch (final ActivityInstanceNotFoundException e) { return false; } } }.waitUntil()) { return getProcessAPI().getArchivedActivityInstance(activity.getId()); } else { throw new Exception("can't get archived task"); } } private ProcessAPI getProcessAPI() throws Exception { return TenantAPIAccessor.getProcessAPI(getInitiator().getSession()); } @Test public void api_can_search_with_default_search_order() throws Exception { //given TestProcessFactory.createActivityWithVariables(getInitiator()); //when final ItemSearchResult searchResult = apiActivity.runSearch(0, 1, null, apiActivity.defineDefaultSearchOrder(), null, null, null); //then assertThat(searchResult.getResults()).as("should be able to search with default search order").isNotEmpty(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APIHumanTaskIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.bonitasoft.test.toolkit.bpm.TestCase; import org.bonitasoft.test.toolkit.bpm.TestHumanTask; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Test; public class APIHumanTaskIT extends AbstractConsoleTest { public APIHumanTask apiHumanTask; private TestHumanTask testHumanTask; /* * (non-Javadoc) * @see org.bonitasoft.console.server.AbstractJUnitWebTest#webTestSetUp() */ @Override public void consoleTestSetUp() throws Exception { testHumanTask = TestProcessFactory.getDefaultHumanTaskProcess() .addActor(TestUserFactory.getJohnCarpenter()) .startCase() .getNextHumanTask(); createAPIHumanTask(); } /* * (non-Javadoc) * @see org.bonitasoft.test.toolkit.AbstractJUnitTest#getInitiator() */ @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Test public void testGetDatastore() { assertNotNull("Is not possible to retrieve the dataStore", apiHumanTask.getDefaultDatastore()); } @Test public void testGetHumanTaskItem() { final ArrayList deploys = new ArrayList<>(); deploys.add(HumanTaskItem.ATTRIBUTE_PROCESS_ID); final ArrayList counters = new ArrayList<>(); final APIID apiId = APIID.makeAPIID(testHumanTask.getId()); final HumanTaskItem humanTaskItem = apiHumanTask.runGet(apiId, deploys, counters); assertEquals("Not possible to get the APIHUmanTaskItem ", humanTaskItem.getName(), testHumanTask.getName()); assertEquals("Not possible to get the APIHUmanTaskItem ", humanTaskItem.getDescription(), testHumanTask.getDescription()); } @Test public void testUpdateHumanTaskItem() { final APIID apiId = APIID.makeAPIID(testHumanTask.getId()); // Update the humanTaskItem attributes final HashMap attributes = new HashMap<>(); attributes.put(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID, String.valueOf(TestUserFactory.getJohnCarpenter().getId())); apiHumanTask.update(apiId, attributes); final HumanTaskItem updateHumanTaskItem = apiHumanTask.get(apiId); assertNotSame("Attributes are not updated", updateHumanTaskItem.getAssignedId(), TestUserFactory.getJohnCarpenter().getId()); } @Test public void testSearch() { // Set the filters final HashMap filters = new HashMap<>(); filters.put(HumanTaskItem.ATTRIBUTE_ID, String.valueOf(testHumanTask.getId())); // Search the humanTaskItem final ArrayList deploys = new ArrayList<>(); deploys.add(HumanTaskItem.ATTRIBUTE_PROCESS_ID); final ArrayList counters = new ArrayList<>(); final HumanTaskItem foundHumanTaskItem = apiHumanTask.runSearch(0, 1, null, null, filters, deploys, counters) .getResults().get(0); assertEquals("Can't search the humanTaskItem", testHumanTask.getName(), foundHumanTaskItem.getName()); } @Test /** * Check that the paging system works fine */ public void testHumanTaskItemSearchPaging() throws InterruptedException { final long before = apiHumanTask.runSearch(0, 10, null, apiHumanTask.defineDefaultSearchOrder(), new HashMap<>(), new ArrayList<>(), new ArrayList<>()).getTotal(); // Setup : insert enough tasks to have 2 pages var instances = new ArrayList(); for (int i = 0; i < 15; i++) { try { instances.add(TestProcessFactory.getDefaultHumanTaskProcess().startCase()); } catch (final Exception e) { fail("Can't start process [" + e.getLocalizedMessage() + "]"); } } await("Wait for the tasks to be created") .until(() -> instances.stream().allMatch(testCase -> testCase.getNextHumanTask() != null)); // Setup: retrieve the needed APIs // this.apiHumanTask = new APIHumanTask(); // final APIServletCall caller = new APIServletCall(mockHttpServletRequest, mockHttpServletResponse); // this.apiHumanTask.setCaller(caller); // Search for page 2 (1 in zero based) final ItemSearchResult search = apiHumanTask.runSearch(1, 10, null, apiHumanTask.defineDefaultSearchOrder(), new HashMap<>(), new ArrayList<>(), new ArrayList<>()); assertThat(search.getResults()).hasSizeGreaterThan(2); assertThat(search.getTotal()).isGreaterThan(before); } @Test /** * Check when assigned a task to me this task is in available list * * @throws Exception */ public void testAssignedTaskInAvailable() { testHumanTask.assignTo(TestUserFactory.getJohnCarpenter()); final ArrayList deploys = new ArrayList<>(); deploys.add(HumanTaskItem.ATTRIBUTE_PROCESS_ID); final ArrayList counters = new ArrayList<>(); final HashMap filters = new HashMap<>(); filters.put(HumanTaskItem.FILTER_USER_ID, String.valueOf(TestUserFactory.getJohnCarpenter().getId())); final List listHumanTaskItem = apiHumanTask .runSearch(0, 1, null, null, filters, deploys, counters).getResults(); assertEquals("HumanTask assigned to me not in available list", 1, listHumanTaskItem.size()); } /** * Initialize APIHumanTask */ private void createAPIHumanTask() { apiHumanTask = new APIHumanTask(); apiHumanTask.setCaller(getAPICaller(TestUserFactory.getJohnCarpenter().getSession(), "API/bpm/humanTask")); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APITaskIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode; import static org.assertj.core.api.Assertions.assertThat; import java.util.Date; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.test.toolkit.bpm.TestHumanTask; import org.bonitasoft.test.toolkit.bpm.TestProcess; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.flownode.TaskItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Test; public class APITaskIT extends AbstractConsoleTest { private APITask apiTask; @Override public void consoleTestSetUp() throws Exception { apiTask = new APITask(); apiTask.setCaller(getAPICaller(TestUserFactory.getJohnCarpenter().getSession(), "API/bpm/task")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } private TestHumanTask createActivityWithVariables() throws InvalidExpressionException { final ProcessDefinitionBuilder processDefinitionBuidler = new ProcessDefinitionBuilder() .createNewInstance("processName", "1.0"); processDefinitionBuidler.addActor("Employees", true) .addDescription("This a default process") .addStartEvent("Start") .addUserTask("Activity 1", "Employees") .addData("variable1", String.class.getName(), new ExpressionBuilder().createConstantStringExpression("defaultValue")) .addData("variable2", Long.class.getName(), new ExpressionBuilder().createConstantLongExpression(1)) .addData("variable3", Date.class.getName(), new ExpressionBuilder().createConstantDateExpression("428558400000")) .addEndEvent("Finish"); return new TestProcess(processDefinitionBuidler).addActor(getInitiator()).setEnable(getInitiator(), true) .startCase().getNextHumanTask() .assignTo(getInitiator()); } @Test public void api_can_search_with_default_search_order() throws Exception { //given createActivityWithVariables(); //when final ItemSearchResult searchResultWithNoOrder = apiTask.runSearch(0, 1, null, null, null, null, null); //then assertThat(searchResultWithNoOrder).as("should be able to search with default search order").isNotNull(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedActivityIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode.archive; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.test.toolkit.bpm.TestProcess; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem; import org.bonitasoft.web.rest.server.WaitUntil; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Assert; import org.junit.Test; public class APIArchivedActivityIT extends AbstractConsoleTest { private APIArchivedActivity apiArchivedActivity; @Override public void consoleTestSetUp() throws Exception { apiArchivedActivity = new APIArchivedActivity(); apiArchivedActivity.setCaller(getAPICaller(getInitiator().getSession(), "API/bpm/archivedActivity")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } private HumanTaskInstance initArchivedHumanTaskInstance() throws Exception { final TestProcess defaultHumanTaskProcess = TestProcessFactory.getDefaultHumanTaskProcess(); defaultHumanTaskProcess.addActor(getInitiator()); final ProcessInstance processInstance = defaultHumanTaskProcess.startCase(getInitiator()).getProcessInstance(); waitPendingHumanTask(); // Retrieve a humanTaskInstance final HumanTaskInstance humanTaskInstance = getProcessAPI() .getPendingHumanTaskInstances(getInitiator().getId(), 0, 10, null).get(0); getProcessAPI().assignUserTask(humanTaskInstance.getId(), getInitiator().getId()); waitAssignedHumanTask(); getProcessAPI().executeFlowNode(humanTaskInstance.getId()); waitArchivedActivityInstance(processInstance.getId()); return humanTaskInstance; } private ProcessAPI getProcessAPI() throws Exception { return TenantAPIAccessor.getProcessAPI(getInitiator().getSession()); } /** * Wait the process contain PendingHumanTaskInstance */ private void waitPendingHumanTask() throws Exception { Assert.assertTrue("no pending task instances are found", new WaitUntil(50, 3000) { @Override protected boolean check() throws Exception { return getProcessAPI().getPendingHumanTaskInstances(getInitiator().getId(), 0, 10, null).size() >= 1; } }.waitUntil()); } private void waitAssignedHumanTask() throws Exception { Assert.assertTrue("Human task hasnt been assign", new WaitUntil(50, 3000) { @Override protected boolean check() throws Exception { return getProcessAPI().getAssignedHumanTaskInstances(getInitiator().getId(), 0, 10, ActivityInstanceCriterion.DEFAULT).size() >= 1; } }.waitUntil()); } /** * Wait the process contain ArchivedHumanTaskInstance */ private void waitArchivedActivityInstance(final long processInstanceId) throws Exception { Assert.assertTrue("no archived task instances are found", new WaitUntil(50, 3000) { @Override protected boolean check() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, processInstanceId); return getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done()).getCount() >= 1L; } }.waitUntil()); } @Test public void testSearchWithDefaultOrder() throws Exception { verifySearhWithOrder(apiArchivedActivity.defineDefaultSearchOrder()); } @Test public void testSearchWithNoOrder() throws Exception { verifySearhWithOrder(null); } private void verifySearhWithOrder(final String order) throws Exception { //given initArchivedHumanTaskInstance(); //when final ItemSearchResult search = apiArchivedActivity.runSearch(0, 1, null, order, null, null, null); //then assertThat(search.getResults()).as("should get results").isNotEmpty(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedHumanTaskIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode.archive; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.util.ArrayList; import java.util.HashMap; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.test.toolkit.bpm.TestProcess; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem; import org.bonitasoft.web.rest.server.WaitUntil; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Assert; import org.junit.Test; public class APIArchivedHumanTaskIT extends AbstractConsoleTest { private APIArchivedHumanTask apiArchivedHumanTask; @Override public void consoleTestSetUp() throws Exception { apiArchivedHumanTask = new APIArchivedHumanTask(); apiArchivedHumanTask.setCaller(getAPICaller(getInitiator().getSession(), "API/bpm/archivedHumanTask")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } private HumanTaskInstance initArchivedHumanTaskInstance() throws Exception { final TestProcess defaultHumanTaskProcess = TestProcessFactory.getDefaultHumanTaskProcess(); defaultHumanTaskProcess.addActor(getInitiator()); final ProcessInstance processInstance = defaultHumanTaskProcess.startCase(getInitiator()).getProcessInstance(); waitPendingHumanTask(processInstance.getId()); // Retrieve a humanTaskInstance final HumanTaskInstance humanTaskInstance = getProcessAPI() .getPendingHumanTaskInstances(getInitiator().getId(), 0, 10, null).get(0); getProcessAPI().assignUserTask(humanTaskInstance.getId(), getInitiator().getId()); waitAssignedHumanTask(); getProcessAPI().executeFlowNode(humanTaskInstance.getId()); waitArchivedActivityInstance(processInstance.getId()); return humanTaskInstance; } private ProcessAPI getProcessAPI() throws Exception { return TenantAPIAccessor.getProcessAPI(getInitiator().getSession()); } private ArrayList getProcessIdDeploy() { final ArrayList deploys = new ArrayList<>(); deploys.add(HumanTaskItem.ATTRIBUTE_PROCESS_ID); return deploys; } private HashMap getNameFilter(final HumanTaskInstance humanTaskInstance) { final HashMap filters = new HashMap<>(); filters.put(ArchivedHumanTaskItem.ATTRIBUTE_NAME, humanTaskInstance.getName()); return filters; } /** * Wait the process contain PendingHumanTaskInstance */ private void waitPendingHumanTask(final long processInstanceId) throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstanceId); Assert.assertTrue("no pending task instances are found", new WaitUntil(50, 3000) { @Override protected boolean check() throws Exception { return getProcessAPI().searchPendingTasksForUser(getInitiator().getId(), searchOptionsBuilder.done()) .getCount() >= 1; } }.waitUntil()); } private void waitAssignedHumanTask() throws Exception { Assert.assertTrue("Human task hasnt been assign", new WaitUntil(50, 3000) { @Override protected boolean check() throws Exception { return getProcessAPI().getAssignedHumanTaskInstances(getInitiator().getId(), 0, 10, ActivityInstanceCriterion.DEFAULT).size() >= 1; } }.waitUntil()); } /** * Wait the process contain ArchivedHumanTaskInstance */ private void waitArchivedActivityInstance(final long processInstanceId) throws Exception { Assert.assertTrue("no archived task instances are found", new WaitUntil(50, 3000) { @Override protected boolean check() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, processInstanceId); return getProcessAPI().searchArchivedActivities(searchOptionsBuilder.done()).getCount() >= 1L; } }.waitUntil()); } @Test public void testGetArchivedHumanTask() throws Exception { final HumanTaskInstance humanTaskInstance = initArchivedHumanTaskInstance(); final ArrayList deploys = getProcessIdDeploy(); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); searchOptionsBuilder.filter(ArchivedActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, humanTaskInstance.getRootContainerId()); final ArchivedActivityInstance archivedActivityInstance = getProcessAPI() .searchArchivedActivities(searchOptionsBuilder.done()).getResult().get(0); final ArchivedHumanTaskItem archivedHumanTaskItem = apiArchivedHumanTask .runGet(makeAPIID(archivedActivityInstance.getId()), deploys, new ArrayList<>()); assertEquals("Can't get the good archivedTaskItem", archivedHumanTaskItem.getName(), humanTaskInstance.getName()); } @Test public void testSearchArchivedHumanTask() throws Exception { final HumanTaskInstance humanTaskInstance = initArchivedHumanTaskInstance(); final ArrayList deploys = getProcessIdDeploy(); final HashMap filters = getNameFilter(humanTaskInstance); final ArchivedHumanTaskItem archivedHumanTaskItem = apiArchivedHumanTask.runSearch(0, 1, null, null, filters, deploys, new ArrayList<>()).getResults().get(0); assertNotNull("Can't find the good archivedTaskItem", archivedHumanTaskItem); } @Test public void testGetDatastore() { assertNotNull("Can't get the Datastore", apiArchivedHumanTask.getDefaultDatastore()); } @Test public void archivedHumanTasksCanBeSortedByReachedStateDate() throws Exception { shouldSearchArchivedHumaTaskWithOrder(ArchivedHumanTaskItem.ATTRIBUTE_REACHED_STATE_DATE + " DESC"); } @Test public void testSearchWithDefaultOrder() throws Exception { shouldSearchArchivedHumaTaskWithOrder(apiArchivedHumanTask.defineDefaultSearchOrder()); } private void shouldSearchArchivedHumaTaskWithOrder(final String orders) throws Exception { //given final HumanTaskInstance humanTaskInstance = initArchivedHumanTaskInstance(); final HashMap filters = getNameFilter(humanTaskInstance); //when final ItemSearchResult search = apiArchivedHumanTask.runSearch(0, 1, null, orders, filters, null, null); //then assertThat(search.getResults()).as("should get results").isNotEmpty(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIActorIntegrationIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import static java.util.Arrays.asList; import static org.bonitasoft.web.rest.model.bpm.process.ActorItem.ATTRIBUTE_DESCRIPTION; import static org.bonitasoft.web.rest.model.bpm.process.ActorItem.ATTRIBUTE_PROCESS_ID; import static org.bonitasoft.web.rest.model.bpm.process.ActorItem.COUNTER_GROUPS; import static org.bonitasoft.web.rest.model.bpm.process.ActorItem.COUNTER_MEMBERSHIPS; import static org.bonitasoft.web.rest.model.bpm.process.ActorItem.COUNTER_ROLES; import static org.bonitasoft.web.rest.model.bpm.process.ActorItem.COUNTER_USERS; import static org.bonitasoft.web.rest.model.builder.bpm.process.ActorItemBuilder.anActorItem; import static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID; import static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName.ATTRIBUTE_DISPLAY_NAME; import static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName.ATTRIBUTE_NAME; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.test.toolkit.bpm.TestProcess; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.bpm.process.TestActorMemberFactory; import org.bonitasoft.test.toolkit.organization.TestGroupFactory; import org.bonitasoft.test.toolkit.organization.TestRoleFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.process.ActorItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Test; /** * @author Colin PUY */ public class APIActorIntegrationIT extends AbstractConsoleTest { private APIActor apiActor; @Override public void consoleTestSetUp() throws Exception { apiActor = new APIActor(); apiActor.setCaller(getAPICaller(getInitiator().getSession(), "API/bpm/actor")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } private ActorItem getFromEngine(final long actorId) throws Exception { return anActorItem().fromActorInstance(getProcessAPI().getActor(actorId)).build(); } private ProcessAPI getProcessAPI() throws Exception { return TenantAPIAccessor.getProcessAPI(getInitiator().getSession()); } @Test public void testGet() throws Exception { final ActorInstance actor = TestProcessFactory.getDefaultHumanTaskProcess() .addActor(TestUserFactory.getJohnCarpenter()).getActors().get(0); final ActorItem fetchedActorItem = apiActor.runGet(makeAPIID(actor.getId()), null, null); assertItemEquals(getFromEngine(actor.getId()), fetchedActorItem); } @Test public void testGetWithDeploys() { final TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess() .addActor(TestUserFactory.getJohnCarpenter()); final long actorId = process.getActors().get(0).getId(); final ActorItem fetchedActorItem = apiActor.runGet(makeAPIID(actorId), List.of(ATTRIBUTE_PROCESS_ID), null); assertNotNull(fetchedActorItem.getProcess()); assertEquals(fetchedActorItem.getProcess().getId(), process.getId()); } @Test public void testGetWithCounters() { //given final TestUser johnCarpenter = TestUserFactory.getJohnCarpenter(); final TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess().addActor(johnCarpenter); final long actorId = process.getActors().get(0).getId(); //when final List counters = asList(COUNTER_USERS, COUNTER_GROUPS, COUNTER_ROLES, COUNTER_MEMBERSHIPS); final ActorItem fetchedActorItem = apiActor.runGet(makeAPIID(actorId), null, counters); //then assertEquals(1L, (long) fetchedActorItem.getNbSelectedUsers()); assertEquals(0L, (long) fetchedActorItem.getNbSelectedGroups()); assertEquals(0L, (long) fetchedActorItem.getNbSelectedRoles()); assertEquals(0L, (long) fetchedActorItem.getNbSelectedMembershipss()); } @Test public void getCanCountNumberOfMembershipForActor() throws Exception { final ActorInstance actor = TestProcessFactory.getDefaultHumanTaskProcess() .addActor(TestUserFactory.getJohnCarpenter()).getActors().get(0); TestActorMemberFactory.createMembershipActorMember(actor.getId(), TestGroupFactory.getRAndD(), TestRoleFactory.getDeveloper()); final List counters = List.of(COUNTER_MEMBERSHIPS); final ActorItem fetchedActorItem = apiActor.runGet(makeAPIID(actor.getId()), null, counters); assertEquals(1L, (long) fetchedActorItem.getNbSelectedMembershipss()); } @Test public void testUpdate() throws Exception { final TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess() .addActor(TestUserFactory.getJohnCarpenter()); final Map attributes = buildUpdateAttributes("newDescription", "newDisplayName"); final long actorId = process.getActors().get(0).getId(); final ActorItem updatedItem = apiActor.runUpdate(makeAPIID(actorId), attributes); assertItemEquals(getFromEngine(actorId), updatedItem); assertEquals("newDisplayName", updatedItem.getDisplayName()); assertEquals("newDescription", updatedItem.getDescription()); } private Map buildUpdateAttributes(final String description, final String displayName) { return Map.of(ATTRIBUTE_DESCRIPTION, description, ATTRIBUTE_DISPLAY_NAME, displayName); } @Test public void testSearchCanBePaginatedAndOrdered() { final TestProcess process = TestProcessFactory.createProcessWith3Actors() .addActor(TestUserFactory.getJohnCarpenter()) .addActor(TestGroupFactory.getRAndD()) .addActor(TestRoleFactory.getDeveloper()); final HashMap filters = new HashMap<>(); filters.put(ATTRIBUTE_PROCESS_ID, String.valueOf(process.getId())); final String order = ATTRIBUTE_NAME + " ASC"; final ItemSearchResult searchResult = apiActor.search(0, 2, null, order, filters); assertEquals(3L, searchResult.getTotal()); assertEquals(2, searchResult.getResults().size()); final String result1Name = searchResult.getResults().get(0).getName(); final String result2Name = searchResult.getResults().get(1).getName(); assertTrue(isAscendantOrder(result1Name, result2Name)); } private boolean isAscendantOrder(final String name1, final String name2) { return name1.compareTo(name2) < 0; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIActorMemberIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.bonitasoft.web.rest.model.bpm.process.ActorMemberItem.ATTRIBUTE_ACTOR_ID; import static org.bonitasoft.web.rest.model.builder.bpm.process.ActorMemberItemBuilder.anActorMemberItem; import static org.bonitasoft.web.rest.model.identity.MemberType.GROUP; import static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.FILTER_MEMBER_TYPE; import static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import java.util.HashMap; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.actor.ActorMember; import org.bonitasoft.engine.bpm.actor.ActorNotFoundException; import org.bonitasoft.test.toolkit.bpm.TestProcess; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.bpm.process.TestActorMemberFactory; import org.bonitasoft.test.toolkit.organization.TestGroupFactory; import org.bonitasoft.test.toolkit.organization.TestRoleFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.process.ActorMemberItem; import org.bonitasoft.web.rest.model.identity.MemberType; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Test; /** * @author Colin PUY */ public class APIActorMemberIT extends AbstractConsoleTest { private APIActorMember apiActorMember; @Override public void consoleTestSetUp() throws Exception { apiActorMember = new APIActorMember(); apiActorMember.setCaller(getAPICaller(getInitiator().getSession(), "API/bpm/actorMember")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } private ActorMemberItem fetchUserActorMember(final long actorId, final long userId) throws Exception { try { final List actorMembers = getProcessAPI().getActorMembers(actorId, 0, Integer.MAX_VALUE); for (final ActorMember actorMember : actorMembers) { if (actorMember.getUserId() == userId) { return anActorMemberItem().fromActorMember(actorMember, actorId).build(); } } } catch (final ActorNotFoundException e) { return null; } return null; } private ProcessAPI getProcessAPI() throws Exception { return TenantAPIAccessor.getProcessAPI(getInitiator().getSession()); } private HashMap buildActorIdFilter(final long actorId) { final HashMap filters = new HashMap<>(); filters.put(ATTRIBUTE_ACTOR_ID, String.valueOf(actorId)); return filters; } private HashMap buildMemberTypeFilter(final long actorId, final MemberType memberType) { final HashMap filters = buildActorIdFilter(actorId); filters.put(FILTER_MEMBER_TYPE, memberType.name()); return filters; } @Test public void testAdd() throws Exception { final TestProcess process = TestProcessFactory.getRandomHumanTaskProcess() .addActor(TestUserFactory.getMrSpechar()); final long actorId = process.getActors().get(0).getId(); final long userId = getInitiator().getId(); final ActorMemberItem addedItem = apiActorMember .runAdd(anActorMemberItem().withActorId(actorId).withuserId(userId).build()); final ActorMemberItem expectedItem = fetchUserActorMember(actorId, userId); assertItemEquals(expectedItem, addedItem); } @Test public void testDelete() throws Exception { final TestUser aUser = getInitiator(); final ActorInstance actor = TestProcessFactory.getRandomHumanTaskProcess() .addActor(TestUserFactory.getMrSpechar()).getActors().get(0); final ActorMember addedMember = TestActorMemberFactory.createUserActorMember(actor.getId(), aUser); apiActorMember.runDelete(asList(makeAPIID(addedMember.getId()))); assertNull(fetchUserActorMember(actor.getId(), aUser.getId())); } @Test public void searchCanBeFilteredByActorIdAndMemberType() throws Exception { final ActorInstance actor = TestProcessFactory.getRandomHumanTaskProcess() .addActor(TestUserFactory.getMrSpechar()).getActors().get(0); TestActorMemberFactory.createUserActorMember(actor.getId(), getInitiator()); final ActorMember addedMember = TestActorMemberFactory.createGroupActorMember(actor.getId(), TestGroupFactory.getRAndD()); final HashMap filter = buildMemberTypeFilter(actor.getId(), GROUP); final ItemSearchResult searchResult = apiActorMember.runSearch(0, 10, null, null, filter, null, null); assertEquals(1L, searchResult.getTotal()); assertItemEquals(anActorMemberItem().fromActorMember(addedMember, actor.getId()).build(), searchResult.getResults().get(0)); } @Test public void searchCanBeFilteredByActorIdAndMemberTypeMemberShip() throws Exception { final ActorInstance actor = TestProcessFactory.getRandomHumanTaskProcess() .addActor(TestUserFactory.getMrSpechar()).getActors().get(0); final ActorMember addedMember = TestActorMemberFactory.createMembershipActorMember(actor.getId(), TestGroupFactory.getWeb(), TestRoleFactory.getManager()); final HashMap filters = buildMemberTypeFilter(actor.getId(), MemberType.MEMBERSHIP); final ItemSearchResult searchResult = apiActorMember.runSearch(0, 10, null, null, filters, null, null); assertEquals(1L, searchResult.getTotal()); assertItemEquals(anActorMemberItem().fromActorMember(addedMember, actor.getId()).build(), searchResult.getResults().get(0)); } @Test public void testSearchCanBePaginated() throws Exception { final TestProcess process = TestProcessFactory.getRandomHumanTaskProcess() .addActor(TestUserFactory.getMrSpechar()); final long actorId = process.getActors().get(0).getId(); TestActorMemberFactory.createUserActorMember(actorId, getInitiator()); TestActorMemberFactory.createGroupActorMember(actorId, TestGroupFactory.getWeb()); final ItemSearchResult searchResult = apiActorMember.runSearch(1, 1, null, null, buildActorIdFilter(actorId), null, null); // when adding actor to process, it seems to create automaticaly an actorMember for this user // so total is 3 even we add only 2 ActorMember assertEquals(3L, searchResult.getTotal()); assertEquals(1, searchResult.getResults().size()); } @Test public void testSearchWithDeploy() throws Exception { final ActorInstance actor = TestProcessFactory.getRandomHumanTaskProcess() .addActor(TestUserFactory.getMrSpechar()).getActors().get(0); TestActorMemberFactory.createUserActorMember(actor.getId(), getInitiator()); final HashMap filters = buildActorIdFilter(actor.getId()); final List deploy = singletonList(ATTRIBUTE_ACTOR_ID); final ItemSearchResult searchResult = apiActorMember.runSearch(0, 1, null, null, filters, deploy, null); assertNotNull(searchResult.getResults().get(0).getActor()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APICategoryIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import static junit.framework.Assert.assertNull; import static org.bonitasoft.web.rest.model.builder.bpm.process.CategoryItemBuilder.aCategoryItem; import static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import junit.framework.Assert; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.test.toolkit.bpm.TestCategory; import org.bonitasoft.test.toolkit.bpm.TestCategoryFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.process.CategoryItem; import org.bonitasoft.web.rest.server.datastore.bpm.process.CategoryDatastore; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.After; import org.junit.Test; /** * @author Nicolas Tith */ public class APICategoryIT extends AbstractConsoleTest { private APICategory api; @Override public void consoleTestSetUp() throws Exception { api = new APICategory(); api.setCaller(getAPICaller(getInitiator().getSession(), "API/bpm/category")); } @Override protected TestUser getInitiator() { return TestUserFactory.getRidleyScott(); } @After public void cleanCategoriesInDB() { final List allCategories = TestCategoryFactory.getAllCategories(getInitiator().getSession()); for (final TestCategory category : allCategories) { TestCategoryFactory.removeTestCategoryFromList(category); category.delete(); } } /** * Fetch a Category by id from engine * * @return the category or null if not found */ private CategoryItem getFromEngine(final long categoryId) { try { return new CategoryDatastore(getInitiator().getSession()).get(makeAPIID(categoryId)); } catch (final APIException e) { if (e instanceof APIItemNotFoundException) { return null; } throw e; } } @Test public void testSearchCategoryItem() { final List catList = TestCategoryFactory.getCategories(3); // Search the CommentItem final List actualCatList = api.runSearch(0, 10, null, null, new HashMap<>(), new ArrayList<>(), new ArrayList<>()).getResults(); Assert.assertNotNull("Categories not found", actualCatList); Assert.assertTrue(catList.size() == 3); } @Test public void addCategoryTest() { //before // API call final CategoryItem categoryItem = new CategoryItem(); categoryItem.setName("categoryTest"); categoryItem.setDescription("categoryDescription"); api.runAdd(categoryItem); // Check final List catList = TestCategoryFactory.getAllCategories(getInitiator().getSession()); final int nbOfCategories = catList.size(); String message = "No categories added. " + nbOfCategories + " categories found. Categories are: \n"; for (final TestCategory testCategory : catList) { message += " catgeory with id " + testCategory.getId() + ": " + testCategory.getCategory().getName() + "\n"; } Assert.assertEquals(message, 1, nbOfCategories); final Category resultCategory = catList.get(0).getCategory(); Assert.assertEquals("Wrong category found (not same name)", categoryItem.getName(), resultCategory.getName()); Assert.assertEquals("Wrong category found (not same description)", categoryItem.getDescription(), resultCategory.getDescription()); } @Test public void updateCategoryTest() { final String newDescription = "Lorem ipsum dolor sit amet"; // Init final TestCategory category = TestCategoryFactory.getRandomCategory(); // Update final Map updates = new HashMap<>(); updates.put(CategoryItem.ATTRIBUTE_DESCRIPTION, newDescription); api.runUpdate(APIID.makeAPIID(category.getCategory().getId()), updates); // Get final CategoryItem output = api.runGet(APIID.makeAPIID(category.getCategory().getId()), new ArrayList<>(), new ArrayList<>()); Assert.assertNotNull("Category not found", output); Assert.assertEquals("Update of category failed", newDescription, output.getDescription()); } @Test public void getCategoryTest() { // Init final TestCategory category = TestCategoryFactory.getRandomCategory(); // API Call final CategoryItem catItem = api.runGet(APIID.makeAPIID(category.getId()), new ArrayList<>(), new ArrayList<>()); Assert.assertNotNull("Category not found", category); Assert.assertEquals("Wrong category description found", category.getCategory().getDescription(), catItem.getDescription()); Assert.assertEquals("Wrong category found", category.getCategory().getName(), catItem.getName()); } @Test public void deleteCategoryTest() { final TestCategory category = TestCategoryFactory.getRandomCategory(); api.runDelete(List.of(makeAPIID(category.getId()))); assertNull(getFromEngine(category.getId())); TestCategoryFactory.removeTestCategoryFromList(category); } @Test(expected = APIForbiddenException.class) public void addingTwiceSameCategoryIsForbidden() { //given final CategoryItem categoryItem = aCategoryItem().build(); //when then exception api.runAdd(categoryItem); api.runAdd(categoryItem); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessCategoryIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import java.util.Arrays; import java.util.List; import junit.framework.Assert; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.test.toolkit.bpm.TestCategory; import org.bonitasoft.test.toolkit.bpm.TestCategoryFactory; import org.bonitasoft.test.toolkit.bpm.TestProcess; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.process.ProcessCategoryItem; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Test; /** * @author Séverin Moussel */ public class APIProcessCategoryIT extends AbstractConsoleTest { private APIProcessCategory api; @Override public void consoleTestSetUp() throws Exception { this.api = new APIProcessCategory(); this.api.setCaller(getAPICaller(getInitiator().getSession(), "API/bpm/processCategory")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Test public void addProcessCategoryTest() { // Init final TestProcess process = TestProcessFactory.getRandomHumanTaskProcess(); final TestCategory category = TestCategoryFactory.getRandomCategory(); // API call final ProcessCategoryItem processCategory = new ProcessCategoryItem(); processCategory.setProcessId(process.getId()); processCategory.setCategoryId(category.getId()); this.api.runAdd(processCategory); // Check final List categories = process.getCategories(); Assert.assertEquals("No categories added", 1, categories.size()); final Category resultCategory = categories.get(0).getCategory(); Assert.assertEquals("Wrong category found", category.getCategory().getName(), resultCategory.getName()); Assert.assertEquals("Wrong category found", category.getCategory().getDescription(), resultCategory.getDescription()); } @Test public void deleteProcessCategoryTest() { // Init final TestProcess process = TestProcessFactory.getRandomHumanTaskProcess(); final TestCategory category = TestCategoryFactory.getRandomCategory(); process.addCategory(category.getId()); // API call this.api.runDelete(Arrays.asList(APIID.makeAPIID(process.getId(), category.getId()))); // Check final List categories = process.getCategories(); Assert.assertEquals("No categories deleted", 0, categories.size()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessConnectorDependencyIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import static java.util.Arrays.asList; import static junit.framework.Assert.assertEquals; import static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_NAME; import static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_VERSION; import static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_PROCESS_ID; import static org.junit.Assert.assertNotNull; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.test.toolkit.bpm.TestProcess; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.bpm.process.TestProcessConnector; import org.bonitasoft.test.toolkit.bpm.process.TestProcessConnectorFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Test; /** * @author Colin PUY */ public class APIProcessConnectorDependencyIT extends AbstractConsoleTest { private APIProcessConnectorDependency apiProcessConnectorDependency; @Override public void consoleTestSetUp() throws Exception { apiProcessConnectorDependency = new APIProcessConnectorDependency(); apiProcessConnectorDependency .setCaller(getAPICaller(getInitiator().getSession(), "API/bpm/processConnectorDependency")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } private Map buildFilters(long processId, String connectorId, String connectorVersion) { Map filters = new HashMap<>(); filters.put(ATTRIBUTE_PROCESS_ID, String.valueOf(processId)); filters.put(ATTRIBUTE_CONNECTOR_NAME, connectorId); filters.put(ATTRIBUTE_CONNECTOR_VERSION, connectorVersion); return filters; } @Test public void testSearch() throws Exception { TestProcessConnector defaultConnector = TestProcessConnectorFactory.getDefaultConnector(); TestProcess processWithConnector = TestProcessFactory.createProcessWithConnector(defaultConnector); Map filters = buildFilters(processWithConnector.getId(), defaultConnector.getId(), defaultConnector.getVersion()); ItemSearchResult search = apiProcessConnectorDependency.runSearch(0, 10, "", null, filters, null, null); assertEquals(defaultConnector.getDependencies().get(0), search.getResults().get(0).getFilename()); assertEquals(defaultConnector.getDependencies().get(1), search.getResults().get(1).getFilename()); assertEquals(defaultConnector.getDependencies().size(), search.getTotal()); } @Test public void testSearchWithDeploys() throws Exception { TestProcessConnector defaultConnector = TestProcessConnectorFactory.getDefaultConnector(); TestProcess processWithConnector = TestProcessFactory.createProcessWithConnector(defaultConnector); Map filters = buildFilters(processWithConnector.getId(), defaultConnector.getId(), defaultConnector.getVersion()); List deploys = asList(ATTRIBUTE_PROCESS_ID); ItemSearchResult search = apiProcessConnectorDependency.runSearch(0, 10, "", null, filters, deploys, null); for (ProcessConnectorDependencyItem item : search.getResults()) { assertNotNull(item.getProcess()); assertEquals((long) processWithConnector.getId(), (long) item.getProcess().getId().toLong()); } } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessConnectorIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; import java.util.HashMap; import java.util.Map; import org.bonitasoft.test.toolkit.bpm.TestProcess; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.bpm.process.TestProcessConnector; import org.bonitasoft.test.toolkit.bpm.process.TestProcessConnectorFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDefinition; import org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Test; /** * @author Colin PUY */ public class APIProcessConnectorIT extends AbstractConsoleTest { private APIProcessConnector apiProcessConnector; @Override public void consoleTestSetUp() throws Exception { apiProcessConnector = new APIProcessConnector(); apiProcessConnector.setCaller(getAPICaller(getInitiator().getSession(), "API/bpm/processConnector")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Test public void testSearch() { TestProcessConnector defaultConnector = TestProcessConnectorFactory.getDefaultConnector(); TestProcess processWithConnector = TestProcessFactory.createProcessWithConnector(defaultConnector); Map filters = new HashMap<>(); filters.put(ProcessConnectorItem.ATTRIBUTE_PROCESS_ID, String.valueOf(processWithConnector.getId())); ItemSearchResult search = apiProcessConnector.runSearch(0, 10, "", null, filters, null, null); ProcessConnectorItem expectedItem = toProcessConnectorItem(defaultConnector, processWithConnector.getId()); assertTrue(areEquals(expectedItem, search.getResults().get(0))); assertEquals(1L, search.getTotal()); } @Test public void testGet() { TestProcessConnector defaultConnector = TestProcessConnectorFactory.getDefaultConnector(); TestProcess processWithConnector = TestProcessFactory.createProcessWithConnector(defaultConnector); APIID apiid = anApiId(processWithConnector.getId(), defaultConnector.getId(), defaultConnector.getVersion()); ProcessConnectorItem processConnectorItem = apiProcessConnector.runGet(apiid, null, null); ProcessConnectorItem expectedItem = toProcessConnectorItem(defaultConnector, processWithConnector.getId()); assertTrue(areEquals(expectedItem, processConnectorItem)); } private APIID anApiId(long processId, String connectorId, String connectorVersion) { APIID apiid = APIID.makeAPIID(String.valueOf(processId), connectorId, connectorVersion); apiid.setItemDefinition(ProcessConnectorDefinition.get()); return apiid; } private ProcessConnectorItem toProcessConnectorItem(TestProcessConnector testProcessConnector, long processId) { ProcessConnectorItem item = new ProcessConnectorItem(); item.setName(testProcessConnector.getId()); item.setVersion(testProcessConnector.getVersion()); item.setProcessId(processId); item.setImplementationName(testProcessConnector.getImplementationId()); item.setImplementationVersion(testProcessConnector.getVersion()); item.setClassname(testProcessConnector.getImplementationClassname()); return item; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import static org.junit.Assert.assertEquals; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.test.toolkit.bpm.TestProcess; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.process.ProcessItem; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Test; public class APIProcessIT extends AbstractConsoleTest { private APIProcess apiProcess; @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Override public void consoleTestSetUp() throws Exception { apiProcess = new APIProcess(); apiProcess.setCaller(getAPICaller(TestUserFactory.getJohnCarpenter().getSession(), "API/bpm/process")); } /** * Add a process uploaded * * @throws Exception */ @Test public void testAddProcessItem() throws Exception { final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(TestUserFactory.getJohnCarpenter().getSession()); final List before = processAPI.getProcessDeploymentInfos(0, 10, ProcessDeploymentInfoCriterion.DEFAULT); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance("Test process", "1.0").done()) .done(); final String fileKey = writeBarToUploads("addProcessTest", businessArchive); // use api to deploy process uploaded final ProcessItem item = new ProcessItem(); item.setAttribute("fileupload", fileKey); apiProcess.add(item); // check the process has been correctly uploaded String assertMessage = "Can't add a ProcessItem to APIProcess. "; int actualSize = -1; final List processDeploymentInfos = processAPI.getProcessDeploymentInfos(0, 10, ProcessDeploymentInfoCriterion.DEFAULT); if (processDeploymentInfos != null) { actualSize = processDeploymentInfos.size(); for (ProcessDeploymentInfo processDeploymentInfo : processDeploymentInfos) { assertMessage += "\nprocessDeploymentInfo=" + processDeploymentInfo; } } else { assertMessage += "processDeploymentInfos is null."; } assertEquals(assertMessage, 1, actualSize - before.size()); } /** * Update state of an enabled process to disabled * * @throws Exception */ @Test public void testUpdateProcessItem() throws Exception { final APIID processDefinitionId = APIID.makeAPIID(TestProcessFactory.getDefaultHumanTaskProcess() .addActor(getInitiator()) .enable() .getId()); // assert process is well enabled final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(TestUserFactory.getJohnCarpenter().getSession()); final String expectedState = processAPI.getProcessDeploymentInfos(0, 1, ProcessDeploymentInfoCriterion.DEFAULT) .get(0).getActivationState().name(); assertEquals("Process should start enabled", ProcessItem.VALUE_ACTIVATION_STATE_ENABLED, expectedState); // use process api to update the state final Map attributes = new HashMap<>(); attributes.put(ProcessItem.ATTRIBUTE_ACTIVATION_STATE, ProcessItem.VALUE_ACTIVATION_STATE_DISABLED); final ProcessItem processItem = apiProcess.update(processDefinitionId, attributes); // check the process is disabled (resolved) assertEquals( "Can't update a processItem with APIProcess <" + processItem.getActivationState() + " - " + ProcessItem.VALUE_ACTIVATION_STATE_DISABLED + ">", processItem.getActivationState(), ProcessItem.VALUE_ACTIVATION_STATE_DISABLED); } /** * Get a process * * @throws Exception */ @Test public void testGetProcessItem() throws Exception { final APIID processDefinitionId = APIID.makeAPIID(TestProcessFactory.getDefaultHumanTaskProcess() .addActor(getInitiator()) .getId()); final ArrayList deploys = new ArrayList<>(); final ArrayList counters = new ArrayList<>(); assertEquals("Can't get a processItem with APIProcess", apiProcess.runGet(processDefinitionId, deploys, counters).getName(), TestProcessFactory.getDefaultHumanTaskProcess().getProcessDefinition().getName()); assertEquals("Can't get a processItem with APIProcess", apiProcess.runGet(processDefinitionId, deploys, counters).getDescription(), TestProcessFactory.getDefaultHumanTaskProcess().getProcessDefinition().getDescription()); } /** * Search process by its id * * @throws Exception */ @Test public void testSearchProcessItemForUser() throws Exception { final APIID processDefinitionId = APIID.makeAPIID(TestProcessFactory.getDefaultHumanTaskProcess() .addActor(getInitiator()) .enable() .getId()); // Set the filters final HashMap filters = new HashMap<>(); filters.put(ProcessItem.FILTER_USER_ID, String.valueOf(TestUserFactory.getJohnCarpenter().getId())); // Search the ProcessItem final ArrayList deploys = new ArrayList<>(); final ArrayList counters = new ArrayList<>(); final ProcessItem processItem = apiProcess .runSearch(0, 10, null, ProcessItem.ATTRIBUTE_DISPLAY_NAME + " ASC", filters, deploys, counters) .getResults().get(0); assertEquals( "Can't search a processItem with APIProcess <" + processDefinitionId + " - " + processItem.getId().toLong() + ">", processDefinitionId, processItem.getId().toLong()); } /* * Create a temporary file, contain a businessArchive * @return File key from temporary content * @param String * prefix path for the temporary file * @param BusinessArchive * businessArchive write in the temporary file */ private static String writeBarToUploads(final String barName, final BusinessArchive businessArchive) { String fileKey = null; try { File tempFile = File.createTempFile(barName, ".bar", WebBonitaConstantsUtils.getTenantInstance().getTempFolder()); tempFile.delete(); BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, tempFile); // write into database fileKey = PlatformAPIAccessor.getTemporaryContentAPI() .storeTempFile(new FileContent("thisismynewfile.doc", new FileInputStream(tempFile), "application/octet-stream")); } catch (final IOException | BonitaHomeNotSetException | ServerAPIException | UnknownAPITypeException e) { e.printStackTrace(); } return fileKey; } /** * Get the latest process version * * @throws Exception */ @Test public void testGetLastProcessVersion() throws Exception { // create 3 version of a process final TestProcess p1 = new TestProcess( TestProcessFactory.getDefaultProcessDefinitionBuilder("multipleVersionsProcess", "aVersion")); TestProcessFactory.getInstance().add(p1); final TestProcess p2 = new TestProcess( TestProcessFactory.getDefaultProcessDefinitionBuilder("multipleVersionsProcess", "aVersion2")); TestProcessFactory.getInstance().add(p2); final TestProcess p3 = new TestProcess( TestProcessFactory.getDefaultProcessDefinitionBuilder("multipleVersionsProcess", "anOtherVersion")); TestProcessFactory.getInstance().add(p3); // map actor John Carpenter on the created processes, then set enable p1.addActor(TestUserFactory.getJohnCarpenter()).enable(); p2.addActor(TestUserFactory.getJohnCarpenter()).enable(); p3.addActor(TestUserFactory.getJohnCarpenter()).enable(); // Set the filters final HashMap filters = new HashMap<>(); filters.put(ProcessItem.FILTER_USER_ID, String.valueOf(TestUserFactory.getJohnCarpenter().getId())); filters.put(ProcessItem.ATTRIBUTE_DISPLAY_NAME, "multipleVersionsProcess"); // search the last version of a process final List resultList = apiProcess .runSearch(0, 1, null, ProcessItem.ATTRIBUTE_DEPLOYMENT_DATE + " DESC", filters, null, null) .getResults(); // get the first element final ProcessItem searchedProcessItem = resultList.get(0); assertEquals("multipleVersionsProcess", searchedProcessItem.getDisplayName()); assertEquals("anOtherVersion", searchedProcessItem.getVersion()); //Because TestProcessFactory is based on names, at least 2 out of the three above processes should be cleaned manually. // This could be improved later in TestProcessFactory TestProcessFactory.getInstance().delete(p1); TestProcessFactory.getInstance().delete(p2); TestProcessFactory.getInstance().delete(p3); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessResolutionProblemIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import junit.framework.Assert; import org.bonitasoft.test.toolkit.bpm.TestProcess; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.process.ProcessResolutionProblemItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.junit.Test; /** * @author Séverin Moussel */ public class APIProcessResolutionProblemIT extends AbstractConsoleTest { @Override public void consoleTestSetUp() throws Exception { } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private APIProcessResolutionProblem getAPI() { final APIProcessResolutionProblem api = new APIProcessResolutionProblem(); api.setCaller(getAPICaller(getInitiator().getSession(), "API/bpm/processResolutionProblem")); return api; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TESTS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Test public void testSearchWithResults() { final TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess(); final ItemSearchResult results = getAPI().runSearch( 0, 100, null, null, MapUtil.asMap(new Arg(ProcessResolutionProblemItem.FILTER_PROCESS_ID, process.getId())), null, null); Assert.assertFalse("No resolution issues found", results.getResults().size() == 0); Assert.assertTrue("Wrong number of resolution issues found", results.getResults().size() == 1); } @Test public void testSearchWithoutResults() { final TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess(); process.addActor(getInitiator()); final ItemSearchResult results = getAPI().runSearch( 0, 100, null, null, MapUtil.asMap(new Arg(ProcessResolutionProblemItem.FILTER_PROCESS_ID, process.getId())), null, null); Assert.assertTrue("Resolution issues found", results.getResults().size() == 0); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/organization/APIGroupIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import static org.bonitasoft.web.rest.model.builder.organisation.GroupItemBuilder.aGroup; import static org.mockito.Mockito.spy; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Files; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.identity.GroupItem; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Assert; import org.junit.Test; public class APIGroupIT extends AbstractConsoleTest { private APIGroup apiGroup; @Override public void consoleTestSetUp() throws Exception { apiGroup = spy(new APIGroup()); apiGroup.setCaller(getAPICaller(getInitiator().getSession(), "API/identity/group")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Test(expected = APIException.class) public void addingTwiceSameGroupIsForbidden() { final GroupItem groupItem = aGroup().build(); apiGroup.add(groupItem); apiGroup.add(groupItem); } @Test public void should_update_group_icon() throws ServerAPIException, BonitaHomeNotSetException, UnknownAPITypeException, IOException { GroupItem input = new GroupItem(); input.setName("Developper"); input.setDescription("The guys who drink a lot of coffee"); input = apiGroup.runAdd(input); final APIID id = input.getId(); Assert.assertNotNull("Failed to add a new role", input); input = new GroupItem(); //store icon into database File file = File.createTempFile("tmp", ".png"); Files.writeString(file.toPath(), "content"); String iconFileKey = PlatformAPIAccessor.getTemporaryContentAPI() .storeTempFile(new FileContent("icon.png", new FileInputStream(file), "img/png")); input.setIcon(iconFileKey); input = apiGroup.runUpdate(id, input.getAttributes()); Assert.assertNotNull("Failed while updating the group", input); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/organization/APIMembershipIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.test.toolkit.organization.TestGroup; import org.bonitasoft.test.toolkit.organization.TestGroupFactory; import org.bonitasoft.test.toolkit.organization.TestMembershipFactory; import org.bonitasoft.test.toolkit.organization.TestRole; import org.bonitasoft.test.toolkit.organization.TestRoleFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.identity.MembershipItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Assert; import org.junit.Test; /** * @author Séverin Moussel * @author Colin PUY */ public class APIMembershipIT extends AbstractConsoleTest { private APIMembership apiMembership; @Override public void consoleTestSetUp() throws Exception { apiMembership = new APIMembership(); apiMembership.setCaller(getAPICaller(getInitiator().getSession(), "API/identity/membership")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } private void checkSearchResults(final ItemSearchResult membershipItems, final int nbResultsByPageExpected, final int nbTotalResultsExpected) { assertTrue("Empty search results", membershipItems.getLength() > 0); assertEquals("Wrong page size", membershipItems.getLength(), nbResultsByPageExpected); assertEquals("Wrong Total size", membershipItems.getTotal(), nbTotalResultsExpected); } @Test public void testAdd() { // Add final MembershipItem input = new MembershipItem(); input.setUserId(getInitiator().getId()); input.setGroupId(TestGroupFactory.getWeb().getId()); input.setRoleId(TestRoleFactory.getManager().getId()); final MembershipItem output = apiMembership.runAdd(input); Assert.assertNotNull("Failed to add a new membership", input); assertEquals("Wrong membership inserted", input.getUserId(), output.getUserId()); assertEquals("Wrong membership inserted", input.getGroupId(), output.getGroupId()); assertEquals("Wrong membership inserted", input.getRoleId(), output.getRoleId()); } private void beforeSearch() { final List roles = TestRoleFactory.getInstance().createRandomRoles(5); final List groups = TestGroupFactory.createRandomGroups(5); final TestUser user1 = getInitiator(); final TestUser user2 = TestUserFactory.getRidleyScott(); for (final TestRole role : roles) { for (final TestGroup group : groups) { TestMembershipFactory.assignMembership(user1, group, role); TestMembershipFactory.assignMembership(user2, group, role); } } } @Test public void testSearch() { beforeSearch(); final Map filters = new HashMap<>(); filters.put(MembershipItem.ATTRIBUTE_USER_ID, String.valueOf(getInitiator().getId())); final ItemSearchResult searchResults = apiMembership.runSearch(0, 12, null, null, filters, null, null); checkSearchResults(searchResults, 12, 25); } @Test public void testDeploys() { beforeSearch(); final Map filters = new HashMap<>(); filters.put(MembershipItem.ATTRIBUTE_USER_ID, String.valueOf(getInitiator().getId())); final ItemSearchResult searchResults = apiMembership.runSearch( 0, 11, null, null, filters, Arrays.asList( MembershipItem.ATTRIBUTE_USER_ID, MembershipItem.ATTRIBUTE_ROLE_ID, MembershipItem.ATTRIBUTE_GROUP_ID, MembershipItem.ATTRIBUTE_ASSIGNED_BY_USER_ID), null); checkSearchResults(searchResults, 11, 25); final MembershipItem firstMembership = searchResults.getResults().get(0); Assert.assertNotNull("Failed to deploy user_id", firstMembership.getUser()); assertEquals("Wrong user deployed", getInitiator().getUser().getUserName(), firstMembership.getUser().getUserName()); Assert.assertNotNull("Failed to deploy role_id", firstMembership.getRole()); Assert.assertNotNull("Failed to deploy group_id", firstMembership.getGroup()); Assert.assertNotNull("Failed to deploy assigned_by_user_id", firstMembership.getAssignedByUser()); } @Test public void testDelete() { // INIT final TestRole roleManager = TestRoleFactory.getManager(); final TestRole roleDeveloper = TestRoleFactory.getDeveloper(); final TestGroup groupWeb = TestGroupFactory.createRandomGroups(1).get(0); final TestUser user = getInitiator(); TestMembershipFactory.assignMembership(user, groupWeb, roleManager); TestMembershipFactory.assignMembership(user, groupWeb, roleDeveloper); // ACTION apiMembership.runDelete( List.of(APIID.makeAPIID( getInitiator().getId(), groupWeb.getId(), roleManager.getId()))); // CHECK RESULT final Map filters = new HashMap<>(); filters.put(MembershipItem.ATTRIBUTE_USER_ID, String.valueOf(getInitiator().getId())); final ItemSearchResult searchResults = apiMembership.runSearch(0, 12, null, null, filters, null, null); checkSearchResults(searchResults, 12, 1); } @Test(expected = APIForbiddenException.class) public void addingTwiceSameMembershipIsForbidden() { MembershipItem input = new MembershipItem(); input.setUserId(getInitiator().getId()); input.setGroupId(TestGroupFactory.getWeb().getId()); input.setRoleId(TestRoleFactory.getManager().getId()); apiMembership.runAdd(input); apiMembership.runAdd(input); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/organization/APIPersonalContactDataIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import static org.junit.Assert.assertEquals; import java.util.HashMap; import java.util.Map; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.identity.PersonalContactDataItem; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Test; /** * @author Paul AMAR */ public class APIPersonalContactDataIT extends AbstractConsoleTest { private APIPersonalContactData apiPersonalContactData; @Override public void consoleTestSetUp() throws Exception { this.apiPersonalContactData = new APIPersonalContactData(); this.apiPersonalContactData.setCaller(getAPICaller(TestUserFactory.getRidleyScott().getSession(), "API/identity/personalcontactdata")); } @Test public void getPersonalContactData() { final PersonalContactDataItem result = this.apiPersonalContactData .get(APIID.makeAPIID(TestUserFactory.getRidleyScott().getId())); assertEquals(result.getAttributes().size(), 13); } @Test public void updatePersonalContactData() { final Map attributes = new HashMap<>(); // Set all the fields. attributes.put(PersonalContactDataItem.ATTRIBUTE_EMAIL, "email"); attributes.put(PersonalContactDataItem.ATTRIBUTE_PHONE, "phone"); attributes.put(PersonalContactDataItem.ATTRIBUTE_MOBILE, "mobile"); attributes.put(PersonalContactDataItem.ATTRIBUTE_FAX, "fax"); attributes.put(PersonalContactDataItem.ATTRIBUTE_BUILDING, "building"); attributes.put(PersonalContactDataItem.ATTRIBUTE_ROOM, "room"); attributes.put(PersonalContactDataItem.ATTRIBUTE_ADDRESS, "address"); attributes.put(PersonalContactDataItem.ATTRIBUTE_ZIPCODE, "zipcode"); attributes.put(PersonalContactDataItem.ATTRIBUTE_CITY, "city"); attributes.put(PersonalContactDataItem.ATTRIBUTE_STATE, "state"); attributes.put(PersonalContactDataItem.ATTRIBUTE_COUNTRY, "country"); attributes.put(PersonalContactDataItem.ATTRIBUTE_WEBSITE, "website"); this.apiPersonalContactData.update(APIID.makeAPIID(TestUserFactory.getRidleyScott().getId()), attributes); final PersonalContactDataItem result = this.apiPersonalContactData .get(APIID.makeAPIID(TestUserFactory.getRidleyScott().getId())); assertEquals(result.getBuilding(), "building"); assertEquals(result.getPhoneNumber(), "phone"); assertEquals(result.getMobileNumber(), "mobile"); assertEquals(result.getFaxNumber(), "fax"); assertEquals(result.getRoom(), "room"); assertEquals(result.getAddress(), "address"); assertEquals(result.getZipCode(), "zipcode"); assertEquals(result.getCountry(), "country"); assertEquals(result.getState(), "state"); assertEquals(result.getEmail(), "email"); assertEquals(result.getWebsite(), "website"); } @Test public void addPersonalContactData() { final TestUser user = getInitiator().createUser("user", "pwd"); final PersonalContactDataItem res = new PersonalContactDataItem(); res.setAddress("New address"); res.setId(user.getId()); this.apiPersonalContactData.add(res); final PersonalContactDataItem result = this.apiPersonalContactData.get(APIID.makeAPIID(user.getId())); assertEquals(result.getCity(), null); assertEquals(result.getAddress(), "New address"); assertEquals(result.getBuilding(), null); } @Override protected TestUser getInitiator() { return TestUserFactory.getRidleyScott(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/organization/APIProfessionalContactDataIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import static org.bonitasoft.web.rest.model.builder.identity.ContactDataBuilder.aContactData; import static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID; import static org.junit.Assert.assertEquals; import java.util.List; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserCreator; import org.bonitasoft.engine.identity.UserCriterion; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.builder.identity.ContactDataBuilder; import org.bonitasoft.web.rest.model.identity.ProfessionalContactDataItem; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.After; import org.junit.Test; /** * @author Paul AMAR */ public class APIProfessionalContactDataIT extends AbstractConsoleTest { private APIProfessionalContactData apiProfessionalContactData; @After public void cleanUsersInDB() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, DeletionException { final IdentityAPI identityAPI = TenantAPIAccessor.getIdentityAPI(TestUserFactory.getRidleyScott().getSession()); List users = identityAPI.getUsers(0, 200, UserCriterion.FIRST_NAME_ASC); while (!users.isEmpty()) { for (final User user : users) { identityAPI.deleteUser(user.getId()); } users = identityAPI.getUsers(0, 200, UserCriterion.FIRST_NAME_ASC); } } @Override public void consoleTestSetUp() throws Exception { apiProfessionalContactData = new APIProfessionalContactData(); apiProfessionalContactData.setCaller( getAPICaller(TestUserFactory.getRidleyScott().getSession(), "API/identity/professionalcontactdata")); } @Override protected TestUser getInitiator() { return TestUserFactory.getRidleyScott(); } protected TestUser createUserWithProfessionnalContactData(final ContactDataBuilder aContactData) { final UserCreator userCreator = new UserCreator("aUser", "aPassword"); userCreator.setProfessionalContactData(aContactData.toContactDataCreator()); return getInitiator().createUser(userCreator); } @Test public void getProfessionalContactData_return_professional_contact_data_of_user_with_given_id() { final ContactDataBuilder aContactData = aContactData(); final TestUser user = createUserWithProfessionnalContactData(aContactData); final ProfessionalContactDataItem result = apiProfessionalContactData.get(makeAPIID(user.getId())); final ProfessionalContactDataItem expectedItem = aContactData.toProfessionalContactDataItem(); assertEquals(expectedItem.getEmail(), result.getEmail()); assertEquals(expectedItem.getPhoneNumber(), result.getPhoneNumber()); assertEquals(expectedItem.getMobileNumber(), result.getMobileNumber()); assertEquals(expectedItem.getFaxNumber(), result.getFaxNumber()); assertEquals(expectedItem.getBuilding(), result.getBuilding()); assertEquals(expectedItem.getRoom(), result.getRoom()); assertEquals(expectedItem.getAddress(), result.getAddress()); assertEquals(expectedItem.getZipCode(), result.getZipCode()); assertEquals(expectedItem.getCity(), result.getCity()); assertEquals(expectedItem.getState(), result.getState()); assertEquals(expectedItem.getCountry(), result.getCountry()); assertEquals(expectedItem.getWebsite(), result.getWebsite()); } @Test public void updateProfessionalContactData_update_professional_contact_data_of_given_user() { final TestUser user = createUserWithProfessionnalContactData(aContactData()); final ProfessionalContactDataItem contactDataItem = aContactData().withAddress("anOtherAddress") .toProfessionalContactDataItem(); final ProfessionalContactDataItem updatedItem = apiProfessionalContactData.update(makeAPIID(user.getId()), contactDataItem.getAttributes()); final ProfessionalContactDataItem expectedItem = apiProfessionalContactData.get(makeAPIID(user.getId())); assertItemEquals(expectedItem, updatedItem); } @Test public void addProfessionalContactData_add_professional_contact_data_to_a_user() { final TestUser user = getInitiator().createUser("user", "pwd"); final ProfessionalContactDataItem res = new ProfessionalContactDataItem(); res.setAddress("New address"); res.setId(user.getId()); apiProfessionalContactData.add(res); final ProfessionalContactDataItem result = apiProfessionalContactData.get(APIID.makeAPIID(user.getId())); assertEquals(result.getCity(), null); assertEquals(result.getAddress(), "New address"); assertEquals(result.getBuilding(), null); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/organization/APIRoleIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.test.toolkit.organization.TestUserFactory.getJohnCarpenter; import static org.bonitasoft.test.toolkit.organization.TestUserFactory.getMrSpechar; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.spy; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Files; import java.util.*; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.test.toolkit.organization.*; import org.bonitasoft.web.rest.model.identity.RoleItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Assert; import org.junit.Test; /** * @author Séverin Moussel */ public class APIRoleIT extends AbstractConsoleTest { @Override public void consoleTestSetUp() throws Exception { } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } /** * @return */ private APIRole getAPIRole() { final APIRole apiRole = new APIRole(); apiRole.setCaller(getAPICaller(getInitiator().getSession(), "API/identity/role")); return apiRole; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GET / ADD // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void assertItemEquals(final String message, final RoleItem expected, final RoleItem actual) { Assert.assertEquals(message, expected.getAttributes(), actual.getAttributes()); } @Test public void testAddAndGet() { // Add RoleItem input = new RoleItem(); input.setName("Developper"); input.setDescription("The guys who drink a lot of coffee"); input = getAPIRole().runAdd(input); Assert.assertNotNull("Failed to add a new role", input); // Get final RoleItem output = getAPIRole().runGet(input.getId(), new ArrayList<>(), new ArrayList<>()); Assert.assertNotNull("Role not found", output); assertItemEquals("Wrong role found", input, output); getAPIRole().runDelete(Arrays.asList(input.getId())); } @Test public void testDeploys() { // Add RoleItem input = new RoleItem(); input.setName("Developper"); input.setDescription("The guys who drink a lot of coffee"); input = getAPIRole().runAdd(input); Assert.assertNotNull("Failed to add a new role", input); // Get final RoleItem output = getAPIRole().runGet( input.getId(), Arrays.asList(RoleItem.ATTRIBUTE_CREATED_BY_USER_ID), new ArrayList<>()); Assert.assertNotNull("Role not found", output); assertItemEquals("Wrong role found", input, output); Assert.assertNotNull("Failed to deploy initiator user", output.getCreatedByUserId()); Assert.assertEquals("Wrong process deployed", getInitiator().getUserName(), output.getCreatedByUser().getUserName()); getAPIRole().runDelete(Arrays.asList(input.getId())); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SEARCH // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Test public void testSearch() throws Exception { TestRoleFactory.getInstance().createRandomRoles(13); final ItemSearchResult roleItems = getAPIRole().runSearch(0, 10, null, null, null, null, null); checkSearchResults(roleItems, 10, 13); } /** * @param roleItems */ private void checkSearchResults(final ItemSearchResult roleItems, final int nbResultsByPageExpected, final int nbTotalResultsExpected) { Assert.assertTrue("Empty search results", roleItems.getLength() > 0); Assert.assertTrue("Wrong page size", roleItems.getLength() == nbResultsByPageExpected); Assert.assertTrue("Wrong Total size", roleItems.getTotal() == nbTotalResultsExpected); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DELETE // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Test public void testDeleteOne() throws Exception { TestRoleFactory.getInstance().createRandomRoles(13); final ItemSearchResult roleItems = getAPIRole().runSearch(0, 10, null, null, null, null, null); getAPIRole().runDelete(Arrays.asList(roleItems.getResults().get(0).getId())); final ItemSearchResult roleItemsAfter = getAPIRole().runSearch(0, 10, null, null, null, null, null); Assert.assertEquals("Failed to delete one role", 12, roleItemsAfter.getTotal()); } @Test public void testDeleteMultiple() throws Exception { TestRoleFactory.getInstance().createRandomRoles(13); final ItemSearchResult roleItems = getAPIRole().runSearch(0, 10, null, null, null, null, null); getAPIRole().runDelete(Arrays.asList( roleItems.getResults().get(1).getId(), roleItems.getResults().get(0).getId())); final ItemSearchResult roleItemsAfter = getAPIRole().runSearch(0, 10, null, null, null, null, null); Assert.assertEquals("Failed to delete multiple roles", 11, roleItemsAfter.getTotal()); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UPDATE // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Test public void testUpdate() throws Exception { final String newDescription = "Lorem ipsum dolor sit amet"; // Add RoleItem input = new RoleItem(); input.setName("Developper"); input.setDescription("The guys who drink a lot of coffee"); input = getAPIRole().runAdd(input); Assert.assertNotNull("Failed to add a new role", input); // Update final Map updates = new HashMap<>(); updates.put(RoleItem.ATTRIBUTE_DESCRIPTION, newDescription); getAPIRole().runUpdate(input.getId(), updates); // Get final RoleItem output = getAPIRole().runGet(input.getId(), new ArrayList<>(), new ArrayList<>()); Assert.assertNotNull("Role not found", output); Assert.assertEquals("Update of role failed", newDescription, output.getDescription()); getAPIRole().runDelete(Arrays.asList(input.getId())); } @Test public void should_update_role_icon() throws IOException, ServerAPIException, BonitaHomeNotSetException, UnknownAPITypeException { // Add RoleItem input = new RoleItem(); final APIRole spyApiRole = spy(getAPIRole()); input.setName("Developper"); input.setDescription("The guys who drink a lot of coffee"); input = spyApiRole.runAdd(input); final APIID id = input.getId(); assertThat(input).isNotNull(); input = new RoleItem(); //store icon into database File file = File.createTempFile("tmp", ".png"); Files.writeString(file.toPath(), "content"); String iconFileKey = PlatformAPIAccessor.getTemporaryContentAPI() .storeTempFile(new FileContent("icon.png", new FileInputStream(file), "img/png")); input.setIcon(iconFileKey); try { input = spyApiRole.runUpdate(id, input.getAttributes()); assertThat(input).isNotNull(); } finally { spyApiRole.runDelete(Arrays.asList(id)); } } @Test public void weCanCountAllUsersInAGroup() throws Exception { final Role roleWith2Users = createRoleWithAssignedUsers(getJohnCarpenter(), getMrSpechar()); final List counters = asList(RoleItem.COUNTER_NUMBER_OF_USERS); final RoleItem roleItem = getAPIRole().runGet(APIID.makeAPIID(roleWith2Users.getId()), null, counters); assertEquals(2L, (long) roleItem.getNumberOfUsers()); } private Role createRoleWithAssignedUsers(final TestUser... users) { final TestGroup aGroup = TestGroupFactory.getRAndD(); final TestRole aRole = TestRoleFactory.getDeveloper(); for (final TestUser user : users) { TestMembershipFactory.assignMembership(user, aGroup, aRole); } return aRole.getRole(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/organization/APIUserAnotherIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import javax.servlet.http.HttpSession; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.identity.UserCreator; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.test.toolkit.server.MockHttpServletRequest; import org.bonitasoft.test.toolkit.server.MockHttpServletResponse; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.rest.server.framework.APIServletCall; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasCreator; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate; import org.junit.After; import org.junit.Assert; import org.junit.Test; /** * @author Vincent Elcrin */ public class APIUserAnotherIT extends AbstractConsoleTest { private final static String PATH_INFO = "API/identity/user"; /** * the request param for the username */ public static final String USERNAME_SESSION_PARAM = "username"; public static final String API_SESSION_PARAM_KEY = "apiSession"; /** * Tested API */ private APIUser apiUser; // magic number. Look at testSearchUser for details. private static final int NUM_EXPECTED_USERS = 12; private HashMap expectedUsers; private static final List userAttributesList = Arrays.asList( UserItem.ATTRIBUTE_FIRSTNAME, UserItem.ATTRIBUTE_LASTNAME, UserItem.ATTRIBUTE_PASSWORD, UserItem.ATTRIBUTE_USERNAME, UserItem.ATTRIBUTE_TITLE, UserItem.ATTRIBUTE_JOB_TITLE); private static final List searchableAttributesList = Arrays.asList( UserItem.ATTRIBUTE_FIRSTNAME, UserItem.ATTRIBUTE_LASTNAME, UserItem.ATTRIBUTE_USERNAME, UserItem.ATTRIBUTE_JOB_TITLE); @Override public void consoleTestSetUp() throws Exception { this.apiUser = createAPIUser(getInitiator().getSession()); createUsersViaEngineAPI(getInitiator().getSession(), NUM_EXPECTED_USERS); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @After public void deleteUsers() throws Exception { final IdentityAPI identityAPI = TenantAPIAccessor.getIdentityAPI(getInitiator().getSession()); final Iterator it = this.expectedUsers.values().iterator(); while (it.hasNext()) { identityAPI.deleteUser(it.next().getId().toLong()); } } /** * Create api user. * Used to migrate other test to the new user API. * * @param apiSession * @return */ public static APIUser createAPIUser(final APISession apiSession) { // Get the httpSession and set attributes final MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest(); mockHttpServletRequest.setPathInfo(PATH_INFO); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); final HttpSession httpSession = mockHttpServletRequest.getSession(); httpSession.setAttribute(USERNAME_SESSION_PARAM, "admin"); httpSession.setAttribute(API_SESSION_PARAM_KEY, apiSession); // Initialize APIUser for HTTP requests of the API final APIUser apiUser = new APIUser(); final APIServletCall caller = new APIServletCall(mockHttpServletRequest, mockHttpServletResponse); apiUser.setCaller(caller); return apiUser; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CONVENIENCE METHODS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Create user containing all attributes listed in userAttributeList with attributeKey_userNumber for value * * @param userNumber * @return */ private UserItem createCompleteUser(final List userAttributesList, final int userNumber) { final HashMap attributes = new HashMap<>(); for (final String attribute : userAttributesList) { if (UserItem.ATTRIBUTE_LAST_CONNECTION_DATE.equals(attribute) || ItemHasCreator.ATTRIBUTE_CREATION_DATE.equals(attribute) || ItemHasLastUpdateDate.ATTRIBUTE_LAST_UPDATE_DATE.equals(attribute)) { final Date date = new Date(); attributes.put(attribute, date.toString()); } else { attributes.put(attribute, attribute + "_" + userNumber); } } final UserItem user = new UserItem(); user.setAttributes(attributes); return user; } /** * Create numberExpectedUser of users via the engine api * * @param numberExpectedUser * @throws Exception */ private void createUsersViaEngineAPI(final APISession apiSession, final int numberExpectedUser) throws Exception { final IdentityAPI identityAPI = TenantAPIAccessor.getIdentityAPI(apiSession); this.expectedUsers = new HashMap<>(); for (int i = 0; i < numberExpectedUser; i++) { final UserItem newUser = createCompleteUser(userAttributesList, i); final APIID newUserId = APIID.makeAPIID(identityAPI.createUser(buildEngineUser(newUser)) .getId()); newUser.setId(newUserId); this.expectedUsers.put(i, newUser); } } /** * Assert that listed attributes of expected user are equals to the actual ones. * * @param userAttributesList * : list of attribute to test * @param expected * : Expected user * @param actual * : User tested against */ private void assertUserEquals(final List userAttributesList, final UserItem expected, final UserItem actual) { final ArrayList localAttributeList = new ArrayList<>(); localAttributeList.addAll(userAttributesList); // password do not come back from the api. localAttributeList.remove(UserItem.ATTRIBUTE_PASSWORD); // test for (final String attribute : localAttributeList) { Assert.assertEquals(attribute + " isnt equals to the attribute setted previously", expected.getAttributeValue(attribute), actual.getAttributeValue(attribute)); } } @Test public void testGetUser() { final Iterator it = this.expectedUsers.values().iterator(); while (it.hasNext()) { final UserItem expectedUser = it.next(); final UserItem anUser = this.apiUser.runGet(expectedUser.getId(), new ArrayList<>(), new ArrayList<>()); // tests Assert.assertNotNull(anUser); assertUserEquals(userAttributesList, expectedUser, anUser); } } @Test public void testSearchUser() { final Iterator it = this.expectedUsers.values().iterator(); while (it.hasNext()) { final UserItem expectedUser = it.next(); for (final String attribute : searchableAttributesList) { final List results = this.apiUser .runSearch(0, 10, expectedUser.getAttributeValue(attribute), null, new HashMap<>(), new ArrayList<>(), new ArrayList<>()) .getResults(); // tests Assert.assertNotNull(results); if (expectedUser.getAttributeValue(attribute).endsWith("_1")) { // we created 12 users so the search of attribute_1 should return _1, _10 and _11 Assert.assertEquals( "Not the right number of result for <" + attribute + "> - value: " + expectedUser.getAttributeValue(attribute), 3, results.size()); } else { Assert.assertEquals( "Not the right number of result for <" + attribute + "> - value: " + expectedUser.getAttributeValue(attribute), 1, results.size()); assertUserEquals(userAttributesList, expectedUser, results.get(0)); } } } } public UserCreator buildEngineUser(final UserItem user) throws NumberFormatException { if (user == null) { throw new IllegalArgumentException("The user must be not null!"); } final UserCreator userCreator = new UserCreator(user.getAttributeValue(UserItem.ATTRIBUTE_USERNAME), user.getAttributeValue(UserItem.ATTRIBUTE_PASSWORD)) .setFirstName(user.getAttributeValue(UserItem.ATTRIBUTE_FIRSTNAME)) .setLastName(user.getAttributeValue(UserItem.ATTRIBUTE_LASTNAME)) .setTitle(user.getAttributeValue(UserItem.ATTRIBUTE_TITLE)) .setIconPath(user.getAttributeValue(UserItem.ATTRIBUTE_ICON)) .setJobTitle(user.getAttributeValue(UserItem.ATTRIBUTE_JOB_TITLE)); // .setPersonalData(personalInfo.done()) // .setProfessionalData(professionalInfo.done()); final String managerId = user.getAttributeValue(UserItem.ATTRIBUTE_MANAGER_ID); if (managerId != null && !managerId.isEmpty()) { userCreator.setManagerUserId(Long.valueOf(managerId)); } return userCreator; } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/organization/APIUserIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import static java.util.Collections.EMPTY_LIST; import static java.util.Collections.EMPTY_MAP; import static org.junit.Assert.assertTrue; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Test; /** * @author Colin PUY */ @SuppressWarnings("unchecked") public class APIUserIT extends AbstractConsoleTest { private static final String ASCENDING = " asc"; private static final String DESCENDING = " desc"; private APIUser apiUser; @Override public void consoleTestSetUp() throws Exception { apiUser = new APIUser(); apiUser.setCaller(getAPICaller(getInitiator().getSession(), "API/identity/user")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } private boolean lowerThan(final String string1, final String string2) { return string1.compareTo(string2) < 0; } private boolean upperThan(final String string1, final String string2) { return string1.compareTo(string2) > 0; } @Test public void searchCanBeOrderdByFirstNameAscending() throws Exception { TestUserFactory.getRidleyScott(); TestUserFactory.getJohnCarpenter(); final ItemSearchResult searchResult = apiUser.runSearch(0, 10, null, UserItem.ATTRIBUTE_FIRSTNAME + ASCENDING, EMPTY_MAP, EMPTY_LIST, EMPTY_LIST); final String firstUserFirstName = searchResult.getResults().get(0).getFirstName(); final String secondUserFirstName = searchResult.getResults().get(1).getFirstName(); assertTrue(lowerThan(firstUserFirstName, secondUserFirstName)); } @Test public void searchCanBeOrderdByFirstNameDescending() throws Exception { TestUserFactory.getRidleyScott(); TestUserFactory.getJohnCarpenter(); final ItemSearchResult searchResult = apiUser.runSearch(0, 10, null, UserItem.ATTRIBUTE_FIRSTNAME + DESCENDING, EMPTY_MAP, EMPTY_LIST, EMPTY_LIST); final String firstUserFirstName = searchResult.getResults().get(0).getFirstName(); final String secondUserFirstName = searchResult.getResults().get(1).getFirstName(); assertTrue(upperThan(firstUserFirstName, secondUserFirstName)); } @Test public void searchCanBeOrderdByLastNameAscending() throws Exception { TestUserFactory.getRidleyScott(); TestUserFactory.getJohnCarpenter(); final ItemSearchResult searchResult = apiUser.runSearch(0, 10, null, UserItem.ATTRIBUTE_LASTNAME + ASCENDING, EMPTY_MAP, EMPTY_LIST, EMPTY_LIST); final String firstUserLastName = searchResult.getResults().get(0).getLastName(); final String secondUserLastName = searchResult.getResults().get(1).getLastName(); assertTrue(lowerThan(firstUserLastName, secondUserLastName)); } @Test public void searchCanBeOrderdByLastNameDescending() throws Exception { TestUserFactory.getRidleyScott(); TestUserFactory.getJohnCarpenter(); final ItemSearchResult searchResult = apiUser.runSearch(0, 10, null, UserItem.ATTRIBUTE_LASTNAME + DESCENDING, EMPTY_MAP, EMPTY_LIST, EMPTY_LIST); final String firstUserLastName = searchResult.getResults().get(0).getLastName(); final String secondUserLastName = searchResult.getResults().get(1).getLastName(); assertTrue(upperThan(firstUserLastName, secondUserLastName)); } @Test public void searchCanBeOrderdByUserNameAscending() throws Exception { TestUserFactory.getRidleyScott(); TestUserFactory.getJohnCarpenter(); final ItemSearchResult searchResult = apiUser.runSearch(0, 10, null, UserItem.ATTRIBUTE_USERNAME + ASCENDING, EMPTY_MAP, EMPTY_LIST, EMPTY_LIST); final String firstUserUserName = searchResult.getResults().get(0).getUserName(); final String secondUserUserName = searchResult.getResults().get(1).getUserName(); assertTrue(lowerThan(firstUserUserName, secondUserUserName)); } @Test public void searchCanBeOrderdByUserNameDescending() throws Exception { TestUserFactory.getRidleyScott(); TestUserFactory.getJohnCarpenter(); final ItemSearchResult searchResult = apiUser.runSearch(0, 10, null, UserItem.ATTRIBUTE_USERNAME + DESCENDING, EMPTY_MAP, EMPTY_LIST, EMPTY_LIST); final String firstUserUserName = searchResult.getResults().get(0).getUserName(); final String secondUserUserName = searchResult.getResults().get(1).getUserName(); assertTrue(upperThan(firstUserUserName, secondUserUserName)); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/page/APIPageIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.page; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.web.rest.server.api.page.builder.PageItemBuilder.aPageItem; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.io.File; import java.io.FileInputStream; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.portal.page.PageItem; import org.bonitasoft.web.rest.server.datastore.page.PageDatastore; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.After; import org.junit.Test; public class APIPageIT extends AbstractConsoleTest { public static final String NEW_PAGE_ZIP = "/newPage.zip"; private APIPage apiPage; @After public void cleanPages() throws Exception { final SearchOptions searchOptions = new SearchOptionsBuilder(0, 100000).done(); for (Page page : getPageAPI().searchPages(searchOptions).getResult()) { final List ids = new ArrayList<>(); ids.add(APIID.makeAPIID(page.getId())); apiPage.delete(ids); } } @Override public void consoleTestSetUp() throws Exception { apiPage = new APIPage(); final APISession session = getInitiator().getSession(); apiPage.setCaller(getAPICaller(session, "API/portal/page")); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } private PageAPI getPageAPI() throws Exception { return TenantAPIAccessor.getCustomPageAPI(getInitiator().getSession()); } @Test public void runAdd_a_page_to_the_repository() throws Exception { // Given final PageItem expectedPage = aPageItem().build(); final PageItem addedPage = apiPage.add(expectedPage); // Validate assertNotNull(addedPage); assertThat(addedPage.getUrlToken()).isEqualTo(expectedPage.getUrlToken()); assertThat(addedPage.getDescription()).isEqualTo(expectedPage.getDescription()); assertThat(addedPage.getDisplayName()).isEqualTo(expectedPage.getDisplayName()); assertThat(addedPage.getContentName()).isEqualTo(expectedPage.getContentName()); } @Test public void runGet_a_page_from_the_repository() throws Exception { // Given final PageItem expectedItem = addNewPage(NEW_PAGE_ZIP); // When final PageItem getItem = apiPage.get(expectedItem.getId()); // Validate assertEquals(expectedItem.getUrlToken(), getItem.getUrlToken()); } @Test(expected = APIException.class) public void runGet_not_existing_page_rise_exception() throws Exception { // When final APIID notExistingPageId = aPageItem().build().getId(); apiPage.get(notExistingPageId); } @Test(expected = APIException.class) public void runDelete_then_get_page_rise_exception() throws Exception { // Given final PageItem pageToBeRemoved = addNewPage(NEW_PAGE_ZIP); final List ids = new ArrayList<>(); ids.add(pageToBeRemoved.getId()); // When apiPage.delete(ids); apiPage.get(pageToBeRemoved.getId()); } @Test public void runUpdate_with_new_page_content_change_it() throws Exception { // Given final PageItem pageToBeUpdated = addNewPage(NEW_PAGE_ZIP); String oldPageKey = pageToBeUpdated.getAttributeValue(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE); assertNotNull(oldPageKey); //store new page zip into database File file = new File(getClass().getResource(NEW_PAGE_ZIP).toURI()); String pageZipKey = PlatformAPIAccessor.getTemporaryContentAPI() .storeTempFile(new FileContent(file.getName(), new FileInputStream(file), "application/zip")); // When final Map attributes = new HashMap<>(); attributes.put(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, pageZipKey); final PageItem updatedPage = apiPage.update(pageToBeUpdated.getId(), attributes); // Validate assertNotNull(updatedPage); assertThat(updatedPage.getUrlToken()).as("url token").isEqualTo("custompage_groovyexampletest"); assertThat(updatedPage.getDisplayName()).as("display name").isEqualTo("Groovy example page_test"); assertThat(updatedPage.getDescription()).as("description") .isEqualTo("Groovy class example of custom page source structure (in English)._test"); } private PageItem addNewPage(String pageFileName) throws Exception { final PageItem pageItem = aPageItem().build(); final URL zipFileUrl = getClass().getResource(pageFileName); final File zipFile = new File(zipFileUrl.toURI()); FileUtils.copyFileToDirectory(zipFile, WebBonitaConstantsUtils.getTenantInstance().getTempFolder()); final byte[] pageContent = FileUtils.readFileToByteArray(new File(zipFileUrl.toURI())); return addPageItemToRepository(pageItem.getContentName(), pageContent); } private PageItem addPageItemToRepository(final String pageContentName, final byte[] pageContent) throws Exception { return aPageItem().fromEngineItem(getPageAPI().createPage(pageContentName, pageContent)).build(); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/CommentDatastoreIntegrationIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import java.util.HashMap; import org.bonitasoft.test.toolkit.bpm.TestCase; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.cases.CommentItem; import org.bonitasoft.web.rest.server.datastore.bpm.cases.CommentDatastore; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Test; public class CommentDatastoreIntegrationIT extends AbstractConsoleTest { private CommentDatastore commentDatastore; @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Override public void consoleTestSetUp() throws Exception { commentDatastore = new CommentDatastore(TestUserFactory.getJohnCarpenter().getSession()); } private TestCase aCase() { return TestProcessFactory.getDefaultHumanTaskProcess().addActor(TestUserFactory.getJohnCarpenter()).startCase(); } private HashMap filterByCaseId(long caseId) { HashMap filters = new HashMap<>(); filters.put(CommentItem.ATTRIBUTE_PROCESS_INSTANCE_ID, String.valueOf(caseId)); return filters; } @Test public void comment_datastore_can_do_a_paginated_search() throws Exception { TestCase aCase = aCase(); aCase.addComment("Comment 1"); aCase.addComment("Comment 2"); aCase.addComment("Comment 3"); final ItemSearchResult results = commentDatastore.search(0, 2, null, null, filterByCaseId(aCase.getId())); assertThat(results.getTotal(), is(3L)); assertThat(results.getResults().size(), is(2)); assertThat(results.getResults().get(0).getContent(), is("Comment 1")); assertThat(results.getResults().get(1).getContent(), is("Comment 2")); } @Test public void comment_datastore_can_search_comments_with_special_characters() throws Exception { String specialCharComment = "#*éàâäëêé~çÞšŒØÃ�Æ"; TestCase aCase = aCase(); aCase.addComment(specialCharComment); final ItemSearchResult results = commentDatastore.search(0, 10, null, null, filterByCaseId(aCase.getId())); assertThat(results.getResults().get(0).getContent(), is(specialCharComment)); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDatastoreIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import static org.awaitility.Awaitility.await; import static org.junit.Assert.assertEquals; import java.util.HashMap; import java.util.concurrent.TimeUnit; import org.bonitasoft.test.toolkit.bpm.TestCase; import org.bonitasoft.test.toolkit.bpm.TestProcess; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseItem; import org.bonitasoft.web.rest.model.bpm.cases.CaseItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Test; /** * @author ROHART Bastien */ public class ArchivedCaseDatastoreIT extends AbstractConsoleTest { private ArchivedCaseDatastore archivedCaseDatastore; /* * (non-Javadoc) * @see org.bonitasoft.console.server.AbstractConsoleTest#consoleTestSetUp() */ @Override public void consoleTestSetUp() throws Exception { archivedCaseDatastore = new ArchivedCaseDatastore(getInitiator().getSession()); } /* * (non-Javadoc) * @see org.bonitasoft.test.toolkit.AbstractJUnitTest#getInitiator() */ @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Test public void twoPoolsWithOneWithACallActivityArchivedCaseTest() throws Exception { TestProcess subprocess = TestProcessFactory.getDefaultHumanTaskProcess(); subprocess.addActor(getInitiator()).enable(); // start subprocess case via call activity TestProcess rootProcess = TestProcessFactory.getCallActivityProcess(subprocess.getProcessDefinition()); var rootInstance = rootProcess.addActor(getInitiator()).enable().startCase(); await().atMost(5, TimeUnit.SECONDS).until(() -> !subprocess.listAllOpenCases().isEmpty()); // archive process 1 case TestCase testCaseRootProcess = rootProcess.listOpenCases().get(0); testCaseRootProcess.getNextHumanTask().assignTo(getInitiator()).executeUserTask(getInitiator()); // asynchronous, wait subprocess to be archived await().atMost(5, TimeUnit.SECONDS).until(() -> rootInstance.getArchive() != null); // Search for archived Cases ItemSearchResult itemSearchResult = archivedCaseDatastore.search(0, 100, null, null, new HashMap<>()); assertEquals("2 cases started but one via call activity so only 1 should be retrieved", 1, itemSearchResult.getResults().size()); // Search for archived Cases with caller any filter HashMap filters = new HashMap<>(); filters.put(CaseItem.FILTER_CALLER, "any"); ItemSearchResult anyCallerSearchResult = archivedCaseDatastore.search(0, 100, null, null, filters); assertEquals("Subprocesses should be retrieved as well", 2, anyCallerSearchResult.getResults().size()); TestProcessFactory.getInstance().delete(rootProcess); TestProcessFactory.getInstance().delete(subprocess); } @Test public void searchArchivedSubCases() throws Exception { TestProcess subprocess = TestProcessFactory.getDefaultHumanTaskProcess(); subprocess.addActor(getInitiator()).enable(); // start subprocess case via call activity TestProcess rootProcess = TestProcessFactory.getCallActivityProcess(subprocess.getProcessDefinition()); var rootInstance = rootProcess.addActor(getInitiator()).enable().startCase(); await().atMost(5, TimeUnit.SECONDS).until(() -> !subprocess.listAllOpenCases().isEmpty()); // archive process 1 case TestCase testCaseRootProcess = rootProcess.listOpenCases().get(0); testCaseRootProcess.getNextHumanTask().assignTo(getInitiator()).executeUserTask(getInitiator()); await().atMost(5, TimeUnit.SECONDS).until(() -> rootInstance.getArchive() != null); // Filters for archived Cases HashMap filters = new HashMap<>(); filters.put(ArchivedCaseItem.ATTRIBUTE_ROOT_CASE_ID, String.valueOf(testCaseRootProcess.getId())); filters.put(CaseItem.FILTER_CALLER, "any"); ItemSearchResult itemSearchResult = archivedCaseDatastore.search(0, 100, null, null, filters); assertEquals("Filtering on root case ID, only the subcase should be retrieved", 1, itemSearchResult.getResults().size()); assertEquals(subprocess.getProcessDefinition().getId(), itemSearchResult.getResults().get(0).getProcessId().toLong().longValue()); TestProcessFactory.getInstance().delete(rootProcess); TestProcessFactory.getInstance().delete(subprocess); } @Test public void search_cancelled_archived_case() throws Exception { TestProcess process = TestProcessFactory.getDefaultHumanTaskProcess(); // start process var processInstance = process.addActor(getInitiator()).enable().startCase(); await().atMost(5, TimeUnit.SECONDS).until(() -> !process.listOpenCases().isEmpty()); // archive process 1 case TestCase testCaseProcess = process.listOpenCases().get(0); // cancel the case processInstance.cancel(); // asynchronous, wait process to be archived await().atMost(5, TimeUnit.SECONDS).until(() -> processInstance.getArchive() != null); // Search for cancelled archived Cases ItemSearchResult searchResult = archivedCaseDatastore.search(0, 100, null, null, new HashMap<>()); assertEquals(1, searchResult.getResults().size()); // Search for cancelled archived Cases with caller any filter HashMap filters = new HashMap<>(); filters.put(CaseItem.FILTER_CALLER, "any"); ItemSearchResult anyCallerSearchResult = archivedCaseDatastore.search(0, 100, null, null, filters); assertEquals(1, anyCallerSearchResult.getResults().size()); TestProcessFactory.getInstance().delete(process); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDatastoreIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import static org.junit.Assert.assertEquals; import java.util.HashMap; import org.bonitasoft.test.toolkit.bpm.TestCase; import org.bonitasoft.test.toolkit.bpm.TestProcess; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.cases.CaseItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Test; /** * @author ROHART Bastien */ public class CaseDatastoreIT extends AbstractConsoleTest { private CaseDatastore caseDatastore; @Override public void consoleTestSetUp() throws Exception { caseDatastore = new CaseDatastore(getInitiator().getSession()); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Test public void twoPoolsWithOneWithACallActivityCaseTest() throws Exception { final long before = caseDatastore.search(0, 100, null, null, new HashMap<>()).getTotal(); final TestProcess process2 = TestProcessFactory.getDefaultHumanTaskProcess(); process2.addActor(getInitiator()); process2.enable(); // start process1 case via call activity final TestProcess process1 = TestProcessFactory.getCallActivityProcess(process2.getProcessDefinition()); final TestCase parentCase = process1.addActor(getInitiator()).startCase(); //wait for process instance to be in a "stable" state parentCase.getNextHumanTask(); // Filters for Opened Cases final ItemSearchResult itemSearchResult = caseDatastore.search(0, 100, null, null, new HashMap<>()); assertEquals("2 cases started but one via call activity so only 1 should be opened", 1, itemSearchResult.getResults().size() - before); TestProcessFactory.getInstance().delete(process1); TestProcessFactory.getInstance().delete(process2); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/HumanTaskDatastoreIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID; import static org.junit.Assert.assertEquals; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.test.toolkit.bpm.TestHumanTask; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorEngine; import org.junit.Test; public class HumanTaskDatastoreIT extends AbstractConsoleTest { private HumanTaskDatastore humanTaskDatastore; @Override public void consoleTestSetUp() throws Exception { humanTaskDatastore = new HumanTaskDatastore(getInitiator().getSession()); } private HumanTaskItem fetchHumanTask(final long taskId) throws Exception { final HumanTaskInstance humanTaskInstance = (HumanTaskInstance) TenantAPIAccessor .getProcessAPI(getInitiator().getSession()).getActivityInstance(taskId); return HumanTaskDatastore.fillConsoleItem(new HumanTaskItem(), humanTaskInstance); } @Test public void task_priority_can_be_changed() throws Exception { final TestHumanTask humanTask = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator()) .startCase().getNextHumanTask(); final HumanTaskItem humanTaskItem = new HumanTaskItem(); humanTaskItem.setId(makeAPIID(humanTask.getId())); humanTaskItem.setPriority(HumanTaskItem.VALUE_PRIORITY_ABOVE_NORMAL); ValidatorEngine.validate(humanTaskItem); humanTaskDatastore.update(humanTaskItem.getId(), humanTaskItem.getAttributes()); final HumanTaskItem fetchedTask = fetchHumanTask(humanTask.getId()); assertEquals(HumanTaskItem.VALUE_PRIORITY_ABOVE_NORMAL, fetchedTask.getPriority()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/engineclient/CaseEngineClientIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import static junit.framework.Assert.assertEquals; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.test.toolkit.bpm.TestCaseFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Test; /** * @author Colin PUY */ public class CaseEngineClientIT extends AbstractConsoleTest { private CaseEngineClient caseEngineClient; @Override public void consoleTestSetUp() throws Exception { caseEngineClient = new CaseEngineClient(TenantAPIAccessor.getProcessAPI(getInitiator().getSession())); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Test public void testCountNumberOfOpenCases() throws Exception { final long before = caseEngineClient.countOpenedCases(); start2cases(); long numberOfOpenCases = caseEngineClient.countOpenedCases(); assertEquals(2L, numberOfOpenCases - before); } private void start2cases() { TestCaseFactory.createRandomCase(getInitiator()); TestCaseFactory.createRandomCase(getInitiator()); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/engineclient/HumanTaskEngineClientIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import static junit.framework.Assert.assertEquals; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.test.toolkit.bpm.TestCaseFactory; import org.bonitasoft.test.toolkit.bpm.TestHumanTask; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem; import org.bonitasoft.web.test.AbstractConsoleTest; import org.junit.Test; /** * @author Colin PUY */ public class HumanTaskEngineClientIT extends AbstractConsoleTest { private HumanTaskEngineClient humanTaskEngineClient; @Override public void consoleTestSetUp() throws Exception { humanTaskEngineClient = new HumanTaskEngineClient(TenantAPIAccessor.getProcessAPI(getInitiator().getSession())); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } @Test public void testCountOpenedTasks() throws Exception { final long before = humanTaskEngineClient.countOpenedHumanTasks(); create2openedTasks(); long openedTasks = humanTaskEngineClient.countOpenedHumanTasks(); assertEquals(2L, openedTasks - before); } private void create2openedTasks() throws InterruptedException { TestHumanTask task1 = TestCaseFactory.createRandomCase(getInitiator()).getNextHumanTask() .assignTo(getInitiator()); TestHumanTask task2 = TestCaseFactory.createRandomCase(getInitiator()).getNextHumanTask() .assignTo(getInitiator()); task1.waitState(HumanTaskItem.VALUE_STATE_READY); task2.waitState(HumanTaskItem.VALUE_STATE_READY); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/engineclient/ProcessEngineClientIT.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import static java.util.Arrays.asList; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static org.junit.Assert.assertNull; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.test.toolkit.bpm.TestProcess; import org.bonitasoft.test.toolkit.bpm.TestProcessFactory; import org.bonitasoft.test.toolkit.organization.TestUser; import org.bonitasoft.test.toolkit.organization.TestUserFactory; import org.bonitasoft.web.test.AbstractConsoleTest; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.junit.Test; /** * @author Colin PUY */ public class ProcessEngineClientIT extends AbstractConsoleTest { private ProcessEngineClient processEngineClient; @Override public void consoleTestSetUp() throws Exception { processEngineClient = new ProcessEngineClient(TenantAPIAccessor.getProcessAPI(getInitiator().getSession())); } @Override protected TestUser getInitiator() { return TestUserFactory.getJohnCarpenter(); } private ProcessDefinition getProcessDefinition(final long processId) throws Exception { try { return TenantAPIAccessor.getProcessAPI(getInitiator().getSession()).getProcessDefinition(processId); } catch (final ProcessDefinitionNotFoundException e) { return null; } } @Test public void testCountResolvedProcesses() throws Exception { final long before = processEngineClient.countResolvedProcesses(); create2resolvedProcesses(); final long resolvedProcesses = processEngineClient.countResolvedProcesses(); assertEquals(2L, resolvedProcesses - before); } private void create2resolvedProcesses() { TestProcessFactory.createRandomResolvedProcess(getInitiator()); TestProcessFactory.createRandomResolvedProcess(getInitiator()); } @Test public void testDeleteProcesses() throws Exception { final TestProcess deployedProcess = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator()); processEngineClient.deleteDisabledProcesses(asList(deployedProcess.getId())); assertNull(getProcessDefinition(deployedProcess.getId())); TestProcessFactory.getInstance().remove(deployedProcess); } @Test(expected = APIItemNotFoundException.class) public void getProcessDeploymentInfo_return_api_not_found_exception_if_process_is_not_found() throws Exception { final long unknownProcessId = 1L; processEngineClient.getProcessDeploymentInfo(unknownProcessId); } @Test public void getProcessDeploymentInfo_return_info_if_process_is_found() throws Exception { final TestProcess deployedProcess = TestProcessFactory.getDefaultHumanTaskProcess().addActor(getInitiator()); final ProcessDeploymentInfo processDeploymentInfo = processEngineClient .getProcessDeploymentInfo(deployedProcess.getId()); assertNotNull(processDeploymentInfo); } } ================================================ FILE: bonita-integration-tests/bonita-integration-tests-web/src/test/resources/appLinkDescriptor.xml ================================================ Application 1 Description of Application 1 /app1.jpg ================================================ FILE: bonita-integration-tests/bonita-query-tests/build.gradle ================================================ plugins { id 'bonita-docker-database' } dependencies { testAnnotationProcessor libs.lombok testImplementation libs.lombok testImplementation libs.assertj testImplementation libs.h2 testImplementation libs.springBeans testImplementation libs.springTx testImplementation libs.springTest testImplementation libs.springJdbc testImplementation libs.springOrm testImplementation project(":bpm:bonita-server") testImplementation project(":bpm:bonita-common") testRuntimeOnly libs.tomcatDbcp } databaseIntegrationTest { include "**/*Test.class" } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/application/ApplicationQueriesTest.java ================================================ /** * 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.application; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.test.persistence.builder.ApplicationBuilder.anApplication; import static org.bonitasoft.engine.test.persistence.builder.ApplicationMenuBuilder.anApplicationMenu; import static org.bonitasoft.engine.test.persistence.builder.ApplicationPageBuilder.anApplicationPage; import static org.bonitasoft.engine.test.persistence.builder.GroupBuilder.aGroup; import static org.bonitasoft.engine.test.persistence.builder.PageBuilder.aPage; import static org.bonitasoft.engine.test.persistence.builder.ProfileBuilder.aProfile; import static org.bonitasoft.engine.test.persistence.builder.ProfileMemberBuilder.aProfileMember; import static org.bonitasoft.engine.test.persistence.builder.RoleBuilder.aRole; import static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser; import static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership; import java.util.List; import org.bonitasoft.engine.business.application.model.AbstractSApplication; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.page.AbstractSPage; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.test.persistence.repository.ApplicationRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; /** * @author Elias Ricken de Medeiros */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class ApplicationQueriesTest { @Autowired private ApplicationRepository repository; @Test public void getApplicationByToken_returns_the_application_with_the_given_token() { //given repository.add(anApplication().withToken("app1").withDisplayName("my app1").withDisplayName("my app1") .withVersion("1.0").withPath("app1").build()); final AbstractSApplication application2 = repository .add(anApplication().withToken("app2").withDisplayName("my app2").withDisplayName("my app2") .withDisplayName("my app2").withVersion("1.0").withPath("/app2") .build()); repository.add(anApplication().withToken("app3").withDisplayName("my app3").withDisplayName("my app3") .withVersion("1.0").withPath("app3").build()); //when final SApplication retrievedApp = repository.getApplicationByToken("app2"); //then assertThat(retrievedApp).isEqualTo(repository.getById(SApplication.class, application2.getId())); } @Test public void getApplication_returns_the_application_with_the_given_id() { //given repository.add(anApplication().withToken("app1").withDisplayName("my app1").withDisplayName("my app1") .withVersion("1.0").withPath("app1").build()); final AbstractSApplication application2 = repository .add(anApplication().withToken("app2").withDisplayName("my app2").withDisplayName("my app2") .withVersion("1.0").withPath("app1").build()); repository.add(anApplication().withToken("app3").withDisplayName("my app3").withDisplayName("my app3") .withVersion("1.0").withPath("app1").build()); //when final SApplicationWithIcon retrievedApp = repository.getById(SApplicationWithIcon.class, application2.getId()); //then assertThat(retrievedApp).isEqualTo(application2); } @Test public void getApplicationPageById_should_return_the_applicationPage_identified_by_the_given_id() { //given final AbstractSApplication application1 = repository .add(anApplication().withToken("app1").withDisplayName("my app1").withDisplayName("my app") .withVersion("1.0").withPath("/app1").build()); final AbstractSPage page = repository .add(aPage().withName("MyPage").withContent("The content".getBytes()).build()); repository.add(anApplicationPage().withToken("FirstPage").withApplicationId(application1.getId()) .withPageId(page.getId()).build()); final SApplicationPage secondPageApp = repository .add(anApplicationPage().withToken("SecondPage").withApplicationId(application1.getId()) .withPageId(page.getId()).build()); repository.add(anApplicationPage().withToken("ThirdPage").withApplicationId(application1.getId()) .withPageId(page.getId()).build()); //when final SApplicationPage retrievedAppPage = repository.getApplicationPage(secondPageApp.getId()); //then assertThat(retrievedAppPage).isEqualTo(secondPageApp); } @Test public void getApplicationPageByNameAnApplicationName_should_return_the_applicationPage_with_the_given_name_in_the_given_application() { //given final AbstractSApplication application1 = repository .add(anApplication().withToken("app1").withDisplayName("my app1").withDisplayName("my app") .withVersion("1.0").withPath("/app1") .build()); final AbstractSApplication application2 = repository .add(anApplication().withToken("app2").withDisplayName("my app2").withDisplayName("my app") .withVersion("1.0").withPath("/app2") .build()); final AbstractSPage page = repository .add(aPage().withName("MyPage").withContent("The content".getBytes()).build()); repository.add(anApplicationPage().withToken("FirstPage").withApplicationId(application1.getId()) .withPageId(page.getId()).build()); final SApplicationPage secondPageApp1 = repository .add(anApplicationPage().withToken("SecondPage").withApplicationId(application1.getId()) .withPageId(page.getId()).build()); repository.add(anApplicationPage().withToken("FirstPage").withApplicationId(application2.getId()) .withPageId(page.getId()).build()); repository.add(anApplicationPage().withToken("SecondPage").withApplicationId(application2.getId()) .withPageId(page.getId()).build()); //when final SApplicationPage retrievedAppPage = repository.getApplicationPageByTokenAndApplicationToken("app1", "SecondPage"); //then assertThat(retrievedAppPage).isEqualTo(secondPageApp1); } @Test public void getApplicationPageByTokenAndApplicationId_should_return_the_applicationPage_with_the_given_name_in_the_given_application() { //given final AbstractSApplication application1 = repository .add(anApplication().withToken("app1").withDisplayName("my app1").withDisplayName("my app") .withVersion("1.0").withPath("/app1") .build()); final AbstractSApplication application2 = repository .add(anApplication().withToken("app2").withDisplayName("my app2").withDisplayName("my app") .withVersion("1.0").withPath("/app2") .build()); final AbstractSPage page = repository .add(aPage().withName("MyPage").withContent("The content".getBytes()).build()); repository.add(anApplicationPage().withToken("FirstPage").withApplicationId(application1.getId()) .withPageId(page.getId()).build()); final SApplicationPage secondPageApp1 = repository .add(anApplicationPage().withToken("SecondPage").withApplicationId(application1.getId()) .withPageId(page.getId()).build()); repository.add(anApplicationPage().withToken("FirstPage").withApplicationId(application2.getId()) .withPageId(page.getId()).build()); repository.add(anApplicationPage().withToken("SecondPage").withApplicationId(application2.getId()) .withPageId(page.getId()).build()); //when final SApplicationPage retrievedAppPage = repository .getApplicationPageByTokenAndApplicationId(application1.getId(), "SecondPage"); //then assertThat(retrievedAppPage).isEqualTo(secondPageApp1); } @Test public void getAllPagesForProfile_should_return_layouts_and_all_pages_related_to_application() { //given //used by app1 and app2 SProfile firstProfile = repository.add(aProfile().withName("firstProfile").build()); //used by app3 SProfile secondProfile = repository.add(aProfile().withName("secondProfile").build()); //not used SProfile thirdProfile = repository.add(aProfile().withName("thirdProfile").build()); AbstractSPage layoutApp1 = repository .add(aPage().withName("layoutApp1").withContent("The content".getBytes()).build()); AbstractSPage themeApp2 = repository .add(aPage().withName("themeApp2").withContent("The content".getBytes()).build()); AbstractSPage layoutApp4 = repository .add(aPage().withName("layoutApp4").withContent("The content".getBytes()).build()); AbstractSPage themeApp4 = repository .add(aPage().withName("themeApp4").withContent("The content".getBytes()).build()); final AbstractSApplication application1 = repository.add(anApplication().withToken("app1") .withDisplayName("my app1") .withDisplayName("my app1") .withVersion("1.0").withPath("/app1").withProfile(firstProfile.getId()).withLayout(layoutApp1.getId()) .build()); final AbstractSApplication application2 = repository.add(anApplication().withToken("app2") .withDisplayName("my app2") .withDisplayName("my app2") .withVersion("1.0").withPath("/app2").withProfile(firstProfile.getId()).withTheme(themeApp2.getId()) .build()); final AbstractSApplication application3 = repository .add(anApplication().withToken("app3").withDisplayName("my app3").withDisplayName("my app3") .withVersion("1.0").withPath("/app3").withProfile(secondProfile.getId()) .build()); final AbstractSApplication application4 = repository.add(anApplication().withToken("app4") .withDisplayName("my app4") .withDisplayName("my app4") .withVersion("1.0").withPath("/app4").withLayout(layoutApp4.getId()).withTheme(themeApp4.getId()) .build()); final AbstractSPage page1 = repository .add(aPage().withName("page1").withContent("The content".getBytes()).build()); final AbstractSPage page2 = repository .add(aPage().withName("page2").withContent("The content".getBytes()).build()); final AbstractSPage page3 = repository .add(aPage().withName("page3").withContent("The content".getBytes()).build()); final AbstractSPage page4 = repository .add(aPage().withName("page4").withContent("The content".getBytes()).build()); final AbstractSPage page5 = repository .add(aPage().withName("page5").withContent("The content".getBytes()).build()); final AbstractSPage page6 = repository .add(aPage().withName("page6").withContent("The content".getBytes()).build()); //app1 has layout layoutApp1 and references page1 and page2 repository.add(anApplicationPage().withToken("FirstPageApp1").withApplicationId(application1.getId()) .withPageId(page1.getId()).build()); repository.add(anApplicationPage().withToken("SecondPageApp1").withApplicationId(application1.getId()) .withPageId(page1.getId()).build()); repository.add(anApplicationPage().withToken("ThirdPageApp1").withApplicationId(application1.getId()) .withPageId(page2.getId()).build()); //app2 has layout themeApp2 and references page3 and page4 repository.add(anApplicationPage().withToken("FirstPageApp2").withApplicationId(application2.getId()) .withPageId(page3.getId()).build()); repository.add(anApplicationPage().withToken("SecondPageApp2").withApplicationId(application2.getId()) .withPageId(page4.getId()).build()); //app3 has no layout and references page4 and page5 repository.add(anApplicationPage().withToken("FirstPageApp3").withApplicationId(application3.getId()) .withPageId(page4.getId()).build()); repository.add(anApplicationPage().withToken("SecondPageApp3").withApplicationId(application3.getId()) .withPageId(page5.getId()).build()); //app3 has layout layoutApp4, themeApp4 and references page6 repository.add(anApplicationPage().withToken("FirstPageApp4").withApplicationId(application4.getId()) .withPageId(page6.getId()).build()); //when List pagesForProfile = repository.getAllPagesForProfile(firstProfile.getId()); //then assertThat(pagesForProfile).containsExactlyInAnyOrder("layoutApp1", "page1", "page2", "page3", "page4", "themeApp2"); //when pagesForProfile = repository.getAllPagesForProfile(secondProfile.getId()); //then assertThat(pagesForProfile).containsExactlyInAnyOrder("page4", "page5"); //when pagesForProfile = repository.getAllPagesForProfile(thirdProfile.getId()); //then assertThat(pagesForProfile).isEmpty(); } @Test public void getApplicationHomePage_should_return_the_applicationPage_set_as_home_page_for_the_given_application() { //given final AbstractSApplication application = repository .add(anApplication().withToken("app1").withDisplayName("my app1").withDisplayName("my app") .withVersion("1.0").withPath("/app1") .build()); final AbstractSPage page = repository .add(aPage().withName("MyPage").withContent("The content".getBytes()).build()); final SApplicationPage firstPage = repository .add(anApplicationPage().withToken("FirstPage").withApplicationId(application.getId()) .withPageId(page.getId()).build()); repository.add(anApplicationPage().withToken("SecondPage").withApplicationId(application.getId()) .withPageId(page.getId()).build()); application.setHomePageId(firstPage.getId()); repository.update(application); //when final SApplicationPage retrievedAppPage = repository.getApplicationHomePage(application.getId()); //then assertThat(retrievedAppPage).isEqualTo(firstPage); } @Test public void getApplicationMenu_by_id_should_return_the_application_menu_identified_by_the_given_id() { //given final AbstractSApplication application = repository .add(anApplication().withToken("app1").withDisplayName("my app1").withDisplayName("my app") .withVersion("1.0").withPath("/app1") .build()); final AbstractSPage page = repository .add(aPage().withName("MyPage").withContent("The content".getBytes()).build()); final SApplicationPage appPage = repository .add(anApplicationPage().withToken("FirstPage").withApplicationId(application.getId()) .withPageId(page.getId()).build()); final SApplicationMenu menu = repository .add(anApplicationMenu().withApplicationId(application.getId()).withApplicationPageId(appPage.getId()) .withDisplayName("menu app1").withIndex(1) .build()); //when final SApplicationMenu retrievedMenu = repository.getApplicationMenu(menu.getId()); //then assertThat(retrievedMenu).isEqualTo(menu); } @Test public void getLastIndexForRootMenu_should_return_last_used_index() { //given final AbstractSApplication application = repository .add(anApplication().withToken("app1").withDisplayName("my app1").withDisplayName("my app") .withVersion("1.0").withPath("/app1") .build()); final AbstractSPage page = repository .add(aPage().withName("MyPage").withContent("The content".getBytes()).build()); final SApplicationPage appPage = repository .add(anApplicationPage().withToken("FirstPage").withApplicationId(application.getId()) .withPageId(page.getId()).build()); repository.add(anApplicationMenu().withApplicationId(application.getId()).withApplicationPageId(appPage.getId()) .withDisplayName("menu app1") .withIndex(1) .build()); //when final int lastIndex = repository.getLastIndexForRootMenu(); //then assertThat(lastIndex).isEqualTo(1); } @Test public void getLastIndexForChildMenu_should_return_last_used_index_by_children_of_a_given_parent() { //given final AbstractSApplication application = repository .add(anApplication().withToken("app1").withDisplayName("my app1").withDisplayName("my app") .withVersion("1.0").withPath("/app1") .build()); final AbstractSPage page = repository .add(aPage().withName("MyPage").withContent("The content".getBytes()).build()); final SApplicationPage appPage = repository .add(anApplicationPage().withToken("FirstPage").withApplicationId(application.getId()) .withPageId(page.getId()).build()); final SApplicationMenu parentMenu = repository .add(anApplicationMenu().withApplicationId(application.getId()).withApplicationPageId(appPage.getId()) .withDisplayName("menu app1").withIndex(1) .build()); repository.add(anApplicationMenu().withApplicationId(application.getId()).withParentId(parentMenu.getId()) .withApplicationPageId(appPage.getId()) .withDisplayName("menu app1").withIndex(1) .build()); //when final int lastIndex = repository.getLastIndexForChildOf(parentMenu.getId()); //then assertThat(lastIndex).isEqualTo(1); } @Test public void getApplicationOfUser_returns_the_application_for_the_given_user() { //given SUser user1 = repository.add(aUser().withId(1L).withUserName("walter.bates").build()); SUser user2 = repository.add(aUser().withId(2L).withUserName("helen.kelly").build()); SUser user3 = repository.add(aUser().withId(3L).withUserName("daniela.angelo").build()); SUser user4 = repository.add(aUser().withId(4L).withUserName("jan.fisher").build()); final SProfile profile1 = repository.add(aProfile().withName("firstProfile").build()); repository.add(aProfileMember().withUserId(user1.getId()).withProfileId(profile1.getId()).build()); repository.add(aProfileMember().withUserId(user3.getId()).withProfileId(profile1.getId()).build()); final SProfile profile2 = repository.add(aProfile().withName("secondProfile").build()); repository.add(aProfileMember().withUserId(user2.getId()).withProfileId(profile2.getId()).build()); repository.add(aProfileMember().withUserId(user3.getId()).withProfileId(profile2.getId()).build()); long application1 = repository .add(anApplication().withToken("app1").withDisplayName("my app1").withDisplayName("my app1") .withVersion("1.0").withPath("app1").withProfile(profile1.getId()).build()) .getId(); long application2 = repository .add(anApplication().withToken("app2").withDisplayName("my app2").withDisplayName("my app2") .withDisplayName("my app2").withVersion("1.0").withPath("/app2").withProfile(profile1.getId()) .build()) .getId(); long application3 = repository .add(anApplication().withToken("app3").withDisplayName("my app3").withDisplayName("my app3") .withVersion("1.0").withPath("app3").withProfile(profile2.getId()).build()) .getId(); repository.flush(); //then assertThat(repository.getNumberOfApplicationOfUser(user1.getId())).isEqualTo(2); assertThat(repository.getNumberOfApplicationOfUser(user2.getId())).isEqualTo(1); assertThat(repository.getNumberOfApplicationOfUser(user3.getId())).isEqualTo(3); assertThat(repository.getNumberOfApplicationOfUser(user4.getId())).isEqualTo(0); assertThat(repository.getNumberOfApplicationOfUser(5L)).isEqualTo(0); assertThat(repository.searchApplicationOfUser(user1.getId())).extracting("id").containsExactlyInAnyOrder( application1, application2); assertThat(repository.searchApplicationOfUser(user2.getId())).extracting("id").containsExactly(application3); assertThat(repository.searchApplicationOfUser(user3.getId())).extracting("id").containsExactlyInAnyOrder( application1, application2, application3); assertThat(repository.searchApplicationOfUser(user4.getId())).isEmpty(); assertThat(repository.searchApplicationOfUser(5L)).isEmpty(); } @Test public void getApplicationOfUser_returns_the_application_only_once_even_if_mapped_twice() { //given SUser user = repository.add(aUser().withId(1L).withUserName("walter.bates").build()); SGroup group = repository.add(aGroup().forGroupId(20L).forGroupName("Group1").build()); repository.add(aUserMembership().forUser(user.getId()).memberOf(group.getId(), -1L).build()); final SProfile profile1 = repository.add(aProfile().withName("firstProfile").build()); repository.add(aProfileMember().withUserId(user.getId()).withProfileId(profile1.getId()).build()); repository.add(aProfileMember().withGroupId(group.getId()).withProfileId(profile1.getId()).build()); long applicationId = repository .add(anApplication().withToken("app1").withDisplayName("my app1").withDisplayName("my app1") .withVersion("1.0").withPath("app1").withProfile(profile1.getId()).build()) .getId(); repository.flush(); //then assertThat(repository.getNumberOfApplicationOfUser(user.getId())).isEqualTo(1); final List applicationList = repository.searchApplicationOfUser(user.getId()); assertThat(applicationList).hasSize(1); assertThat(applicationList.get(0).getId()).isEqualTo(applicationId); } @Test public void searchApplicationOfUser_returns_the_application_for_the_given_user_mapped_through_group() { //given SUser user1 = repository.add(aUser().withId(1L).withUserName("walter.bates").build()); SUser user2 = repository.add(aUser().withId(2L).withUserName("helen.kelly").build()); SUser user3 = repository.add(aUser().withId(3L).withUserName("daniela.angelo").build()); SUser user4 = repository.add(aUser().withId(4L).withUserName("jan.fisher").build()); SUser user5 = repository.add(aUser().withId(5L).withUserName("antonio.banderas").build()); SGroup group1 = repository.add(aGroup().forGroupId(20L).forGroupName("Group1").build()); SGroup group2 = repository .add(aGroup().forGroupId(21L).forGroupName("Group2").forParentPath("/Group1").build()); SGroup group3 = repository.add(aGroup().forGroupId(22L).forGroupName("Group3").forParentPath("").build()); final SProfile profile1 = repository.add(aProfile().withName("firstProfile").build()); repository.add(aProfileMember().withGroupId(group1.getId()).withProfileId(profile1.getId()).build()); repository.add(aUserMembership().forUser(user1.getId()).memberOf(group1.getId(), -1L).build()); repository.add(aUserMembership().forUser(user3.getId()).memberOf(group1.getId(), -1L).build()); final SProfile profile2 = repository.add(aProfile().withName("secondProfile").build()); repository.add(aProfileMember().withGroupId(group2.getId()).withProfileId(profile2.getId()).build()); repository.add(aUserMembership().forUser(user2.getId()).memberOf(group2.getId(), -1L).build()); repository.add(aUserMembership().forUser(user3.getId()).memberOf(group2.getId(), -1L).build()); repository.add(aUserMembership().forUser(user4.getId()).memberOf(group3.getId(), -1L).build()); final AbstractSApplication application1 = repository .add(anApplication().withToken("app1").withDisplayName("my app1").withDisplayName("my app1") .withVersion("1.0").withPath("app1").withProfile(profile1.getId()).build()); final AbstractSApplication application2 = repository .add(anApplication().withToken("app2").withDisplayName("my app2").withDisplayName("my app2") .withDisplayName("my app2").withVersion("1.0").withPath("/app2").withProfile(profile1.getId()) .build()); final AbstractSApplication application3 = repository .add(anApplication().withToken("app3").withDisplayName("my app3").withDisplayName("my app3") .withVersion("1.0").withPath("app3").withProfile(profile2.getId()).build()); repository.flush(); //then assertThat(repository.searchApplicationOfUser(user1.getId())).extracting("id") .containsExactlyInAnyOrder(application1.getId(), application2.getId()); assertThat(repository.searchApplicationOfUser(user2.getId())).extracting("id") .containsExactly(application3.getId()); assertThat(repository.searchApplicationOfUser(user3.getId())).extracting("id") .containsExactlyInAnyOrder(application1.getId(), application2.getId(), application3.getId()); assertThat(repository.searchApplicationOfUser(user4.getId())).isEmpty(); assertThat(repository.searchApplicationOfUser(user5.getId())).isEmpty(); assertThat(repository.searchApplicationOfUser(6L)).isEmpty(); assertThat(repository.getNumberOfApplicationOfUser(user1.getId())).isEqualTo(2); assertThat(repository.getNumberOfApplicationOfUser(user2.getId())).isEqualTo(1); assertThat(repository.getNumberOfApplicationOfUser(user3.getId())).isEqualTo(3); assertThat(repository.getNumberOfApplicationOfUser(user4.getId())).isEqualTo(0); assertThat(repository.getNumberOfApplicationOfUser(user5.getId())).isEqualTo(0); assertThat(repository.getNumberOfApplicationOfUser(6L)).isEqualTo(0); } @Test public void getApplicationOfUser_returns_the_application_for_the_given_user_mapped_through_role() { //given SUser user1 = repository.add(aUser().withId(1L).withUserName("walter.bates").build()); SUser user2 = repository.add(aUser().withId(2L).withUserName("helen.kelly").build()); SUser user3 = repository.add(aUser().withId(3L).withUserName("daniela.angelo").build()); SUser user4 = repository.add(aUser().withId(4L).withUserName("jan.fisher").build()); SUser user5 = repository.add(aUser().withId(5L).withUserName("antonio.banderas").build()); SRole role1 = repository.add(aRole().forRoleId(40L).forRoleName("Role1").build()); SRole role2 = repository.add(aRole().forRoleId(41L).forRoleName("Role2").build()); SRole role3 = repository.add(aRole().forRoleId(42L).forRoleName("Role3").build()); final SProfile profile1 = repository.add(aProfile().withName("firstProfile").build()); repository.add(aProfileMember().withRoleId(role1.getId()).withProfileId(profile1.getId()).build()); repository.add(aUserMembership().forUser(user1.getId()).memberOf(-1L, role1.getId()).build()); repository.add(aUserMembership().forUser(user3.getId()).memberOf(-1L, role1.getId()).build()); final SProfile profile2 = repository.add(aProfile().withName("secondProfile").build()); repository.add(aProfileMember().withRoleId(role2.getId()).withProfileId(profile2.getId()).build()); repository.add(aUserMembership().forUser(user2.getId()).memberOf(-1L, role2.getId()).build()); repository.add(aUserMembership().forUser(user3.getId()).memberOf(-1L, role2.getId()).build()); repository.add(aUserMembership().forUser(user4.getId()).memberOf(-1L, role3.getId()).build()); long application1 = repository .add(anApplication().withToken("app1").withDisplayName("my app1").withDisplayName("my app1") .withVersion("1.0").withPath("app1").withProfile(profile1.getId()).build()) .getId(); long application2 = repository .add(anApplication().withToken("app2").withDisplayName("my app2").withDisplayName("my app2") .withDisplayName("my app2").withVersion("1.0").withPath("/app2").withProfile(profile1.getId()) .build()) .getId(); long application3 = repository .add(anApplication().withToken("app3").withDisplayName("my app3").withDisplayName("my app3") .withVersion("1.0").withPath("app3").withProfile(profile2.getId()).build()) .getId(); repository.flush(); //then assertThat(repository.searchApplicationOfUser(user1.getId())).extracting("id").containsExactlyInAnyOrder( application1, application2); assertThat(repository.searchApplicationOfUser(user2.getId())).extracting("id").containsExactly(application3); assertThat(repository.searchApplicationOfUser(user3.getId())).extracting("id").containsExactlyInAnyOrder( application1, application2, application3); assertThat(repository.searchApplicationOfUser(user4.getId())).isEmpty(); assertThat(repository.searchApplicationOfUser(user5.getId())).isEmpty(); assertThat(repository.searchApplicationOfUser(6L)).isEmpty(); //then assertThat(repository.getNumberOfApplicationOfUser(user1.getId())).isEqualTo(2); assertThat(repository.getNumberOfApplicationOfUser(user2.getId())).isEqualTo(1); assertThat(repository.getNumberOfApplicationOfUser(user3.getId())).isEqualTo(3); assertThat(repository.getNumberOfApplicationOfUser(user4.getId())).isEqualTo(0); assertThat(repository.getNumberOfApplicationOfUser(user5.getId())).isEqualTo(0); assertThat(repository.getNumberOfApplicationOfUser(6L)).isEqualTo(0); } @Test public void getApplicationOfUser_returns_the_application_for_the_given_user_mapped_through_group_and_role() { //given SUser user1 = repository.add(aUser().withId(1L).withUserName("walter.bates").build()); SUser user2 = repository.add(aUser().withId(2L).withUserName("helen.kelly").build()); SUser user3 = repository.add(aUser().withId(3L).withUserName("daniela.angelo").build()); SUser user4 = repository.add(aUser().withId(4L).withUserName("jan.fisher").build()); SUser user5 = repository.add(aUser().withId(5L).withUserName("antonio.banderas").build()); SGroup group1 = repository.add(aGroup().forGroupId(20L).forGroupName("Group1").build()); SGroup group2 = repository .add(aGroup().forGroupId(21L).forGroupName("Group2").forParentPath("/Group1").build()); SGroup group3 = repository.add(aGroup().forGroupId(22L).forGroupName("Group3").forParentPath("").build()); SRole role1 = repository.add(aRole().forRoleId(40L).forRoleName("Role1").build()); SRole role2 = repository.add(aRole().forRoleId(41L).forRoleName("Role2").build()); SRole role3 = repository.add(aRole().forRoleId(42L).forRoleName("Role3").build()); final SProfile profile1 = repository.add(aProfile().withName("firstProfile").build()); repository.add(aProfileMember().withGroupId(group1.getId()).withRoleId(role1.getId()) .withProfileId(profile1.getId()).build()); repository.add(aUserMembership().forUser(user1.getId()).memberOf(group1.getId(), role1.getId()).build()); repository.add(aUserMembership().forUser(user3.getId()).memberOf(group1.getId(), role1.getId()).build()); final SProfile profile2 = repository.add(aProfile().withName("secondProfile").build()); repository.add(aProfileMember().withGroupId(group2.getId()).withRoleId(role2.getId()) .withProfileId(profile2.getId()).build()); repository.add(aUserMembership().forUser(user2.getId()).memberOf(group2.getId(), role2.getId()).build()); repository.add(aUserMembership().forUser(user3.getId()).memberOf(group2.getId(), role2.getId()).build()); repository.add(aUserMembership().forUser(user4.getId()).memberOf(group3.getId(), role3.getId()).build()); long application1 = repository .add(anApplication().withToken("app1").withDisplayName("my app1").withDisplayName("my app1") .withVersion("1.0").withPath("app1").withProfile(profile1.getId()).build()) .getId(); long application2 = repository .add(anApplication().withToken("app2").withDisplayName("my app2").withDisplayName("my app2") .withDisplayName("my app2").withVersion("1.0").withPath("/app2").withProfile(profile1.getId()) .build()) .getId(); long application3 = repository .add(anApplication().withToken("app3").withDisplayName("my app3").withDisplayName("my app3") .withVersion("1.0").withPath("app3").withProfile(profile2.getId()).build()) .getId(); repository.flush(); //then assertThat(repository.searchApplicationOfUser(user1.getId())).extracting("id").containsExactlyInAnyOrder( application1, application2); assertThat(repository.searchApplicationOfUser(user2.getId())).extracting("id").containsExactly(application3); assertThat(repository.searchApplicationOfUser(user3.getId())).extracting("id").containsExactlyInAnyOrder( application1, application2, application3); assertThat(repository.searchApplicationOfUser(user4.getId())).isEmpty(); assertThat(repository.searchApplicationOfUser(user5.getId())).isEmpty(); assertThat(repository.searchApplicationOfUser(6L)).isEmpty(); //then assertThat(repository.getNumberOfApplicationOfUser(user1.getId())).isEqualTo(2); assertThat(repository.getNumberOfApplicationOfUser(user2.getId())).isEqualTo(1); assertThat(repository.getNumberOfApplicationOfUser(user3.getId())).isEqualTo(3); assertThat(repository.getNumberOfApplicationOfUser(user4.getId())).isEqualTo(0); assertThat(repository.getNumberOfApplicationOfUser(user5.getId())).isEqualTo(0); assertThat(repository.getNumberOfApplicationOfUser(6L)).isEqualTo(0); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/contract/data/ContractDataTest.java ================================================ /** * 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.core.contract.data; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.commons.Pair.pair; import java.util.List; import java.util.Map; import org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper; import org.bonitasoft.engine.test.persistence.repository.ContractDataRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; /** * author Emmanuel Duchastenier */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class ContractDataTest { @Autowired private ContractDataRepository repository; @Autowired private JdbcTemplate jdbcTemplate; @Test public void should_be_able_to_add_contract_data() { repository.add(SProcessContractData.builder().name("myProcessContractData").scopeId(123L) .value("SerializedValue").build()); repository.add( STaskContractData.builder().name("myTaskContractData").scopeId(124L).value("SerializedValue").build()); repository.flush(); List> contractData = jdbcTemplate .query("SELECT KIND, NAME, SCOPEID, VAL from contract_data", new JdbcRowMapper("SCOPEID")); assertThat(contractData).hasSize(2); assertThat(contractData).anySatisfy(c -> { assertThat(c.get("KIND")).isEqualTo("PROCESS"); assertThat(c.get("NAME")).isEqualTo("myProcessContractData"); assertThat(c.get("SCOPEID")).isEqualTo(123L); assertThat(c.get("VAL")).isEqualTo("SerializedValue"); }); assertThat(contractData).anySatisfy(c -> { assertThat(c.get("KIND")).isEqualTo("TASK"); assertThat(c.get("NAME")).isEqualTo("myTaskContractData"); assertThat(c.get("SCOPEID")).isEqualTo(124L); assertThat(c.get("VAL")).isEqualTo("SerializedValue"); }); } @Test public void should_be_able_to_retrieve_process_and_task_contract_data() { SProcessContractData processContractData = repository.add(SProcessContractData.builder() .name("myProcessContractData").scopeId(123L).value("SerializedValue").build()); STaskContractData taskContractData = repository.add(STaskContractData.builder() .name("myTaskContractData").scopeId(124L).value("SerializedValue").build()); assertThat(repository.selectOne("getContractDataByProcessInstanceId", pair("scopeId", 123L))) .isEqualTo(processContractData); assertThat(repository.selectOne("getContractDataByUserTaskId", pair("scopeId", 124L))) .isEqualTo(taskContractData); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/form/FormMappingTest.java ================================================ /** * 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.core.form; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.commons.Pair.pair; import java.util.List; import java.util.Map; import org.bonitasoft.engine.page.SPageMapping; import org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper; import org.bonitasoft.engine.test.persistence.repository.TestRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class FormMappingTest { @Autowired TestRepository testRepository; @Autowired private JdbcTemplate jdbcTemplate; @Test public void should_be_able_to_add_form_mapping() { SPageMapping pageMapping = SPageMapping.builder().id(1).build(); testRepository.add(pageMapping); testRepository.add(SFormMapping.builder().pageMapping(pageMapping).type(3).task("task1").target("target1") .lastUpdateDate(200L).lastUpdatedBy(100L).processDefinitionId(2L).build()); testRepository.add(SFormMapping.builder().pageMapping(pageMapping).type(4).task("task2").target("target2") .processDefinitionId(3L).build()); testRepository.flush(); List> formMapping = jdbcTemplate.query("SELECT * from form_mapping", new JdbcRowMapper("PAGE_MAPPING_ID", "LASTUPDATEDATE", "LASTUPDATEDBY", "PROCESS")); assertThat(formMapping).hasSize(2); assertThat(formMapping).anySatisfy(c -> { assertThat(c.get("TASK")).isEqualTo("task1"); assertThat(c.get("TYPE")).isEqualTo(3); assertThat(c.get("PAGE_MAPPING_ID")).isEqualTo(1L); assertThat(c.get("LASTUPDATEDATE")).isEqualTo(200L); assertThat(c.get("LASTUPDATEDBY")).isEqualTo(100L); assertThat(c.get("PROCESS")).isEqualTo(2L); assertThat(c.get("TARGET")).isEqualTo("target1"); }); assertThat(formMapping).anySatisfy(c -> { assertThat(c.get("TASK")).isEqualTo("task2"); assertThat(c.get("PAGE_MAPPING_ID")).isEqualTo(1L); assertThat(c.get("TYPE")).isEqualTo(4); assertThat(c.get("PROCESS")).isEqualTo(3L); assertThat(c.get("TARGET")).isEqualTo("target2"); }); } @Test public void should_be_able_to_retrieve_formMapping_with_process_definition_id() { SFormMapping formMapping = testRepository.add(SFormMapping.builder().processDefinitionId(123L).build()); SFormMapping formMapping1 = testRepository.add(SFormMapping.builder().processDefinitionId(124L).build()); assertThat(testRepository.selectOne("getFormMappingsOfProcessDefinition", pair("processDefinitionId", 123L))) .isEqualTo(formMapping); assertThat(testRepository.selectOne("getFormMappingsOfProcessDefinition", pair("processDefinitionId", 124L))) .isEqualTo(formMapping1); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/ActorMappingTest.java ================================================ /** * 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.core.process.instance.model; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.test.persistence.builder.ActorBuilder.anActor; import static org.bonitasoft.engine.test.persistence.builder.ActorMemberBuilder.anActorMember; import static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser; import static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.test.persistence.repository.UserMembershipRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; /** * author Emmanuel Duchastenier */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class ActorMappingTest { @Autowired private UserMembershipRepository repository; @Test public void userWithUserMappingShouldBeReturned() { final SActor actor = repository.add(anActor().build()); long unusedRoleId = 111L; long unusedGroupId = 222L; final SUser user = repository.add(aUser().withId(1L).build()); SActorMember actorMember = repository.add(anActorMember().forActor(actor).withUserId(user.getId()).build()); repository.add(aUserMembership().forUser(user).memberOf(unusedGroupId, unusedRoleId).build()); List actorMemberIds = new ArrayList(1); actorMemberIds.add(actorMember.getId()); Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(user.getId(), actorMemberIds); assertThat(members).isEqualTo(1L); } @Test public void userWithRoleMappingShouldBeReturned() { final SActor actor = repository.add(anActor().build()); long aRoleId = 111L; long unusedGroupId = 222L; SActorMember actorMember = repository.add(anActorMember().forActor(actor).withRoleId(aRoleId).build()); final SUser user = repository.add(aUser().withId(22L).build()); repository.add(aUserMembership().forUser(user).memberOf(unusedGroupId, aRoleId).build()); List actorMemberIds = new ArrayList(1); actorMemberIds.add(actorMember.getId()); Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(user.getId(), actorMemberIds); assertThat(members).isEqualTo(1L); } @Test public void userWithGroupMappingShouldBeReturned() { final SActor actor = repository.add(anActor().build()); long unusedRoleId = 111L; long aGroupId = 222L; SActorMember actorMember = repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).build()); final SUser user = repository.add(aUser().withId(333L).build()); repository.add(aUserMembership().forUser(user).memberOf(aGroupId, unusedRoleId).build()); List actorMemberIds = new ArrayList(1); actorMemberIds.add(actorMember.getId()); Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(user.getId(), actorMemberIds); assertThat(members).isEqualTo(1L); } @Test public void userWithGroupAndRoleMappingShouldBeReturned() { final SActor actor = repository.add(anActor().build()); long aRoleId = 111L; long aGroupId = 222L; SActorMember actorMember = repository .add(anActorMember().forActor(actor).withRoleId(aRoleId).withGroupId(aGroupId).build()); final SUser user = repository.add(aUser().withId(4444L).build()); repository.add(aUserMembership().forUser(user).memberOf(aGroupId, aRoleId).build()); Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(user.getId(), Arrays.asList(actorMember.getId())); assertThat(members).isEqualTo(1L); } @Test public void managerOfUserWithUserMappingShouldBeReturned() { final SActor actor = repository.add(anActor().build()); long unusedRoleId = 111L; long unusedGroupId = 222L; final SUser manager = repository.add(aUser().withId(2L).build()); final SUser user = repository.add(aUser().withId(5555L).withManager(manager.getId()).build()); SActorMember actorMember = repository.add(anActorMember().forActor(actor).withUserId(user.getId()).build()); repository.add(aUserMembership().forUser(user).memberOf(unusedGroupId, unusedRoleId).build()); Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(manager.getId(), Arrays.asList(actorMember.getId())); assertThat(members).isEqualTo(1L); } @Test public void managerOfUserWithRoleMappingShouldBeReturned() { final SActor actor = repository.add(anActor().build()); long aRoleId = 111L; long unusedGroupId = 222L; SActorMember actorMember = repository.add(anActorMember().forActor(actor).withRoleId(aRoleId).build()); final SUser manager = repository.add(aUser().withId(2L).build()); final SUser user = repository.add(aUser().withId(66666L).withManager(manager.getId()).build()); repository.add(aUserMembership().forUser(user).memberOf(unusedGroupId, aRoleId).build()); Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(manager.getId(), Arrays.asList(actorMember.getId())); assertThat(members).isEqualTo(1L); } @Test public void managerOfUserWithGroupMappingShouldBeReturned() { final SActor actor = repository.add(anActor().build()); long unusedRoleId = 111L; long aGroupId = 222L; SActorMember actorMember = repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).build()); final SUser manager = repository.add(aUser().withId(2L).build()); final SUser user = repository.add(aUser().withId(77777L).withManager(manager.getId()).build()); repository.add(aUserMembership().forUser(user).memberOf(aGroupId, unusedRoleId).build()); Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(manager.getId(), Arrays.asList(actorMember.getId())); assertThat(members).isEqualTo(1L); } @Test public void managerOfUserWithGroupAndRoleMappingShouldBeReturned() { final SActor actor = repository.add(anActor().build()); long aRoleId = 111L; long aGroupId = 222L; SActorMember actorMember = repository .add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build()); final SUser manager = repository.add(aUser().withId(2L).build()); final SUser user = repository.add(aUser().withId(888888L).withManager(manager.getId()).build()); repository.add(aUserMembership().forUser(user).memberOf(aGroupId, aRoleId).build()); Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(manager.getId(), Arrays.asList(actorMember.getId())); assertThat(members).isEqualTo(1L); } @Test public void userWithNoMatchingGroupAndRoleShouldNotBeReturned() { final SActor actor = repository.add(anActor().build()); long aRoleId = 111L; long aGroupId = 222L; SActorMember actorMember = repository .add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build()); final SUser user = repository.add(aUser().withId(99999999L).build()); repository.add(aUserMembership().forUser(user).memberOf(aGroupId, 16545L).build()); Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(user.getId(), Arrays.asList(actorMember.getId())); assertThat(members).isEqualTo(0L); } @Test public void userWithNoMembershipButWithDirectActorMappingShouldBeReturned() { final SActor actor = repository.add(anActor().build()); long aRoleId = 111L; long aGroupId = 222L; final SUser user = repository.add(aUser().withId(132457L).build()); SActorMember actorMember = repository.add(anActorMember().forActor(actor).withUserId(user.getId()).build()); Long members = repository.getNumberOfUserMembersForUserOrManagerForActorMembers(user.getId(), Arrays.asList(actorMember.getId())); assertThat(members).isEqualTo(1L); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/ArchiveFlowNodeInstanceTest.java ================================================ /** * 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.core.process.instance.model; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.commons.Pair.pair; import java.util.Map; import org.bonitasoft.engine.core.process.definition.model.SGatewayType; import org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.test.persistence.repository.FlowNodeInstanceRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class ArchiveFlowNodeInstanceTest { @Autowired private FlowNodeInstanceRepository repository; @Autowired private JdbcTemplate jdbcTemplate; @Test public void should_save_and_get_SAAutomaticTaskInstance() { SAFlowNodeInstance entity = new SAAutomaticTaskInstance(); SAFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getArchivedFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM arch_flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("auto"); } @Test public void should_save_and_get_SAUserTaskInstance_with_task_priority() { SAUserTaskInstance entity = new SAUserTaskInstance(); entity.setPriority(STaskPriority.ABOVE_NORMAL); SAUserTaskInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getArchivedFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM arch_flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(((Number) flowNodeAsMap.get("PRIORITY")).intValue()).isEqualTo(3); } @Test public void should_save_and_get_SAGatewayInstance_with_gateway_type() { SAGatewayInstance entity = new SAGatewayInstance(); entity.setGatewayType(SGatewayType.INCLUSIVE); SAFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getArchivedFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM arch_flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("GATEWAYTYPE")).isEqualTo("INCLUSIVE"); } @Test public void should_save_and_get_SACallActivityInstance() { SAFlowNodeInstance entity = new SACallActivityInstance(); SAFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getArchivedFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM arch_flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("call"); } @Test public void should_save_and_get_SAGatewayInstance() { SAFlowNodeInstance entity = new SAGatewayInstance(); SAFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getArchivedFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM arch_flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("gate"); } @Test public void should_save_and_get_SALoopActivityInstance() { SAFlowNodeInstance entity = new SALoopActivityInstance(); SAFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getArchivedFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM arch_flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("loop"); } @Test public void should_save_and_get_SAManualTaskInstance() { SAFlowNodeInstance entity = new SAManualTaskInstance(); SAFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getArchivedFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM arch_flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("manual"); } @Test public void should_save_and_get_SAMultiInstanceActivityInstance() { SAFlowNodeInstance entity = new SAMultiInstanceActivityInstance(); SAFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getArchivedFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM arch_flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("multi"); } @Test public void should_save_and_get_SAReceiveTaskInstance() { SAFlowNodeInstance entity = new SAReceiveTaskInstance(); SAFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getArchivedFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM arch_flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("receive"); } @Test public void should_save_and_get_SASendTaskInstance() { SAFlowNodeInstance entity = new SASendTaskInstance(); SAFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getArchivedFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM arch_flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("send"); } @Test public void should_save_and_get_SASubProcessActivityInstance() { SAFlowNodeInstance entity = new SASubProcessActivityInstance(); SAFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getArchivedFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM arch_flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("subProc"); } @Test public void should_save_and_get_SAUserTaskInstance() { SAFlowNodeInstance entity = new SAUserTaskInstance(); SAFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getArchivedFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM arch_flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("user"); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/ArchiveProcessInstanceQueriesTest.java ================================================ /** * 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.core.process.instance.model; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.bonitasoft.engine.commons.Pair.mapOf; import static org.bonitasoft.engine.commons.Pair.pair; import static org.junit.Assert.*; import java.util.Arrays; import java.util.List; import java.util.Map; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAFlowNodeSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessSimpleRefBusinessDataInstance; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper; import org.bonitasoft.engine.test.persistence.repository.ProcessInstanceRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class ArchiveProcessInstanceQueriesTest { private static final long PROCESS_INSTANCE_ID = 43578923425L; private static final long FLOW_NODE_INSTANCE_ID = 342678L; @Autowired private ProcessInstanceRepository repository; @Autowired private JdbcTemplate jdbcTemplate; @Test public void getArchivedProcessInstancesInAllStates_should_return_archived_process_instances_when_exist() { // Given final SAProcessInstance saProcessInstance1 = repository.add(buildSAProcessInstance(1L)); final SAProcessInstance saProcessInstance2 = repository.add(buildSAProcessInstance(2L)); // When final List archivedProcessInstances = repository .getArchivedProcessInstancesInAllStates(Arrays.asList(1L, 2L)); // Then assertFalse("The list of archived process instance must not be empty !!", archivedProcessInstances.isEmpty()); assertEquals("The first element of the list must to have as id 1", saProcessInstance1, archivedProcessInstances.get(0)); assertEquals("The second element of the list must to have as id 2", saProcessInstance2, archivedProcessInstances.get(1)); } @Test public void getArchivedProcessInstancesInAllStates_should_return_empty_list_when_no_archived_process_instances_with_ids() { // Given // When final List archivedProcessInstances = repository .getArchivedProcessInstancesInAllStates(Arrays.asList(1L, 2L)); // Then assertTrue("The list of archived process instance must be empty !!", archivedProcessInstances.isEmpty()); } @Test public void should_save_and_get_multi_business_data_reference_for_archived_process() { SAProcessMultiRefBusinessDataInstance multiRefBusinessDataInstance = new SAProcessMultiRefBusinessDataInstance(); multiRefBusinessDataInstance.setDataIds(Arrays.asList(23L, 25L, 27L)); multiRefBusinessDataInstance.setProcessInstanceId(PROCESS_INSTANCE_ID); multiRefBusinessDataInstance.setName("myMultiProcData"); multiRefBusinessDataInstance.setDataClassName("someDataClassName"); multiRefBusinessDataInstance = repository.add(multiRefBusinessDataInstance); repository.flush(); PersistentObject multiRefBusinessData = repository.selectOne("getSARefBusinessDataInstance", pair("processInstanceId", PROCESS_INSTANCE_ID), pair("name", "myMultiProcData")); Map multiRefBusinessDataAsMap = jdbcTemplate .queryForObject( "SELECT ID, KIND, NAME, DATA_CLASSNAME, DATA_ID, ORIG_PROC_INST_ID, ORIG_FN_INST_ID FROM arch_ref_biz_data_inst WHERE orig_proc_inst_id=" + PROCESS_INSTANCE_ID + " AND name='myMultiProcData'", new JdbcRowMapper("ID", "DATA_ID", "ORIG_PROC_INST_ID", "ORIG_FN_INST_ID")); List> dataIds = jdbcTemplate .query("SELECT ID, IDX, DATA_ID FROM arch_multi_biz_data WHERE id=" + multiRefBusinessDataInstance.getId(), new JdbcRowMapper("ID", "IDX", "DATA_ID")); assertThat(((SAProcessMultiRefBusinessDataInstance) multiRefBusinessData).getDataIds()) .isEqualTo(Arrays.asList(23L, 25L, 27L)); assertThat(multiRefBusinessData).isEqualTo(multiRefBusinessDataInstance); assertThat(multiRefBusinessDataAsMap).containsOnly( entry("ID", multiRefBusinessDataInstance.getId()), entry("KIND", "proc_multi_ref"), entry("NAME", "myMultiProcData"), entry("DATA_CLASSNAME", "someDataClassName"), entry("DATA_ID", null), entry("ORIG_PROC_INST_ID", PROCESS_INSTANCE_ID), entry("ORIG_FN_INST_ID", null)); assertThat(dataIds).containsExactly( mapOf(pair("ID", multiRefBusinessDataInstance.getId()), pair("IDX", 0L), pair("DATA_ID", 23L)), mapOf(pair("ID", multiRefBusinessDataInstance.getId()), pair("IDX", 1L), pair("DATA_ID", 25L)), mapOf(pair("ID", multiRefBusinessDataInstance.getId()), pair("IDX", 2L), pair("DATA_ID", 27L))); } @Test public void should_save_and_get_single_business_data_reference_for_archived_process() { SAProcessSimpleRefBusinessDataInstance singleRef = new SAProcessSimpleRefBusinessDataInstance(); singleRef.setDataId(43L); singleRef.setProcessInstanceId(PROCESS_INSTANCE_ID); singleRef.setName("mySingleData"); singleRef.setDataClassName("someDataClassName"); singleRef = repository.add(singleRef); repository.flush(); PersistentObject singleRefFromQuery = repository.selectOne("getSARefBusinessDataInstance", pair("processInstanceId", PROCESS_INSTANCE_ID), pair("name", "mySingleData")); Map multiRefBusinessDataAsMap = jdbcTemplate .queryForObject( "SELECT ID, KIND, NAME, DATA_CLASSNAME, DATA_ID, ORIG_PROC_INST_ID, ORIG_FN_INST_ID FROM arch_ref_biz_data_inst WHERE orig_proc_inst_id=" + PROCESS_INSTANCE_ID + " AND name='mySingleData'", new JdbcRowMapper("ID", "DATA_ID", "ORIG_PROC_INST_ID", "ORIG_FN_INST_ID")); assertThat(singleRefFromQuery).isEqualTo(singleRef); assertThat(multiRefBusinessDataAsMap).containsOnly( entry("ID", singleRef.getId()), entry("KIND", "proc_simple_ref"), entry("NAME", "mySingleData"), entry("DATA_CLASSNAME", "someDataClassName"), entry("DATA_ID", 43L), entry("ORIG_PROC_INST_ID", PROCESS_INSTANCE_ID), entry("ORIG_FN_INST_ID", null)); } @Test public void should_save_and_get_single_business_data_reference_for_flow_node_of_archived_process() { SAFlowNodeSimpleRefBusinessDataInstance singleRef = new SAFlowNodeSimpleRefBusinessDataInstance(); singleRef.setDataId(43L); singleRef.setFlowNodeInstanceId(FLOW_NODE_INSTANCE_ID); singleRef.setName("mySingleData"); singleRef.setDataClassName("someDataClassName"); singleRef = repository.add(singleRef); repository.flush(); PersistentObject singleRefFromQuery = repository.selectOne("getSAFlowNodeRefBusinessDataInstance", pair("flowNodeInstanceId", FLOW_NODE_INSTANCE_ID), pair("name", "mySingleData")); Map multiRefBusinessDataAsMap = jdbcTemplate .queryForObject( "SELECT ID, KIND, NAME, DATA_CLASSNAME, DATA_ID, ORIG_PROC_INST_ID, ORIG_FN_INST_ID FROM arch_ref_biz_data_inst WHERE orig_fn_inst_id=" + FLOW_NODE_INSTANCE_ID + " AND name='mySingleData'", new JdbcRowMapper("ID", "DATA_ID", "ORIG_PROC_INST_ID", "ORIG_FN_INST_ID")); assertThat(singleRefFromQuery).isEqualTo(singleRef); assertThat(multiRefBusinessDataAsMap).containsOnly( entry("ID", singleRef.getId()), entry("KIND", "fn_simple_ref"), entry("NAME", "mySingleData"), entry("DATA_CLASSNAME", "someDataClassName"), entry("DATA_ID", 43L), entry("ORIG_PROC_INST_ID", null), entry("ORIG_FN_INST_ID", FLOW_NODE_INSTANCE_ID)); } private SAProcessInstance buildSAProcessInstance(final long id) { final SAProcessInstance saProcessInstance = new SAProcessInstance(); saProcessInstance.setId(id); saProcessInstance.setSourceObjectId(id); saProcessInstance.setName("process" + id); return saProcessInstance; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/BPMEventQueriesTest.java ================================================ /** * 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.core.process.instance.model; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType.INTERMEDIATE_THROW_EVENT; import static org.bonitasoft.engine.test.persistence.builder.MessageInstanceBuilder.aMessageInstance; import static org.bonitasoft.engine.test.persistence.builder.WaitingMessageEventBuilder.aWaitingEvent; import java.time.Instant; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.test.persistence.repository.BPMEventRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class BPMEventQueriesTest { private static final int SOME_NUMBER_OF_MESSAGE_INSTANCES = 5; private static final int MORE_THAN_DEFAULT_PAGE_SIZE = 42; @Autowired private BPMEventRepository bPMEventRepository; @Autowired private JdbcTemplate jdbcTemplate; @Test public void getInProgressMessageInstancesShouldOnlyConsiderHandledMessages() { // given: for (int i = 0; i < SOME_NUMBER_OF_MESSAGE_INSTANCES; i++) { bPMEventRepository.add(aMessageInstance().handled(true).build()); } // Add instances that should be ignored by getInProgressMessageInstances(): bPMEventRepository.add(aMessageInstance().handled(false).build()); bPMEventRepository.add(aMessageInstance().handled(false).build()); // when: List inProgressMessageInstances = bPMEventRepository.getInProgressMessageInstances(); // then: assertThat(inProgressMessageInstances).hasSize(SOME_NUMBER_OF_MESSAGE_INSTANCES); } @Test public void resetMessageInstancesShouldResetAllHandledFlagToFalse() { // given: for (int i = 0; i < MORE_THAN_DEFAULT_PAGE_SIZE; i++) { bPMEventRepository.add(aMessageInstance().handled(true).build()); } // when: bPMEventRepository.resetProgressMessageInstances(); // then: assertThat(bPMEventRepository.getInProgressMessageInstances()).hasSize(0); } @Test public void getInProgressWaitingEventsShouldOnlyConsiderInProgressElements() { // given: for (int i = 0; i < SOME_NUMBER_OF_MESSAGE_INSTANCES; i++) { bPMEventRepository.add(aWaitingEvent().inProgress(true).build()); } // Add instances that should be ignored by getInProgressWaitingEvents(): bPMEventRepository.add(aWaitingEvent().inProgress(false).build()); bPMEventRepository.add(aWaitingEvent().inProgress(false).build()); // when: List inProgressWaitingEvents = bPMEventRepository.getInProgressWaitingEvents(); // then: assertThat(inProgressWaitingEvents).hasSize(SOME_NUMBER_OF_MESSAGE_INSTANCES); } @Test public void resetWaitingEventsShouldResetAllInProgressFlagToFalse() { // given: for (int i = 0; i < MORE_THAN_DEFAULT_PAGE_SIZE; i++) { bPMEventRepository.add(aWaitingEvent().inProgress(true).build()); } // when: bPMEventRepository.resetInProgressWaitingEvents(); // then: assertThat(bPMEventRepository.getInProgressWaitingEvents()).hasSize(0); } @Test public void resetWaitingEventsShouldReturnNumberOfRowsUpdated() { // given: for (int i = 0; i < MORE_THAN_DEFAULT_PAGE_SIZE; i++) { bPMEventRepository.add(aWaitingEvent().inProgress(true).build()); } bPMEventRepository.add(aWaitingEvent().inProgress(false).build()); bPMEventRepository.add(aWaitingEvent().inProgress(false).build()); // when: int resetInProgressWaitingEvents = bPMEventRepository.resetInProgressWaitingEvents(); // then: assertThat(resetInProgressWaitingEvents).as("wrong result").isEqualTo(MORE_THAN_DEFAULT_PAGE_SIZE); } @Test public void resetMessageInstancesShouldReturnNumberOfRowsUpdated() { // given: for (int i = 0; i < MORE_THAN_DEFAULT_PAGE_SIZE; i++) { bPMEventRepository.add(aMessageInstance().handled(true).build()); } bPMEventRepository.add(aMessageInstance().handled(false).build()); bPMEventRepository.add(aMessageInstance().handled(false).build()); // when: int resetMessageInstances = bPMEventRepository.resetProgressMessageInstances(); // then: assertThat(resetMessageInstances).as("wrong result").isEqualTo(MORE_THAN_DEFAULT_PAGE_SIZE); } @Test public void should_return_message_older_than_creationDate() { SMessageInstance messageInstance = bPMEventRepository.add(aMessageInstance().creationDate(100).build()); SMessageInstance messageInstance1 = bPMEventRepository.add(aMessageInstance().creationDate(200).build()); bPMEventRepository.add(aMessageInstance().creationDate(300).build()); List messageInstanceIds = bPMEventRepository.getMessageInstanceIdOlderThanCreationDate(200); // then: assertThat(messageInstanceIds).containsOnly(messageInstance.getId(), messageInstance1.getId()); } @Test public void should_delete_message_with_given_ids() { SMessageInstance messageInstance = bPMEventRepository.add(aMessageInstance().creationDate(100).build()); SMessageInstance messageInstance1 = bPMEventRepository.add(aMessageInstance().creationDate(200).build()); SMessageInstance messageInstance2 = bPMEventRepository.add(aMessageInstance().creationDate(300).build()); bPMEventRepository.deleteMessageInstanceByIds(Arrays.asList(messageInstance.getId(), messageInstance1.getId())); List messageInstanceIds = bPMEventRepository .getMessageInstanceIdOlderThanCreationDate(Instant.now().toEpochMilli()); // then: assertThat(messageInstanceIds).containsExactly(messageInstance2.getId()); } @Test public void should_get_waitingEvents_by_eventType() { // Given final SWaitingEvent sWaitingEvent = bPMEventRepository .add(aWaitingEvent().withEventType(INTERMEDIATE_THROW_EVENT).build()); bPMEventRepository.flush(); final String eventType = jdbcTemplate.queryForObject("select eventType from waiting_event", String.class); assertThat(eventType).isEqualTo("INTERMEDIATE_THROW_EVENT"); assertThat(sWaitingEvent.getEventType()).isEqualTo(INTERMEDIATE_THROW_EVENT); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/CommentsTest.java ================================================ /** * 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.core.process.instance.model; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.assertj.core.groups.Tuple.tuple; import static org.bonitasoft.engine.commons.Pair.pair; import java.util.Map; import org.bonitasoft.engine.core.process.comment.model.SHumanComment; import org.bonitasoft.engine.core.process.comment.model.SSystemComment; import org.bonitasoft.engine.core.process.comment.model.archive.SAComment; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper; import org.bonitasoft.engine.test.persistence.repository.CommentRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; /** * @author Danila Mazour */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class CommentsTest { private static final long JACK_ID = 783L; private static final long JOHN_ID = 784L; private static final long PROCESS1_ID = 123L; private static final long PROCESS2_ID = 124L; @Autowired private CommentRepository repository; @Autowired private JdbcTemplate jdbcTemplate; //Those tests currently verify that the queries returning UserMemberships correctly retrieve the groupParentPath when building the Usermembership objects @Test public void should_getComments_of_process_instance() { repository.add(new SHumanComment(PROCESS1_ID, "comment1", JACK_ID)); repository.add(new SHumanComment(PROCESS1_ID, "comment2", JOHN_ID)); repository.add(new SHumanComment(PROCESS1_ID, "comment3", JACK_ID)); repository.add(new SSystemComment(PROCESS1_ID, "comment4")); repository.add(new SHumanComment(PROCESS2_ID, "comment5", JACK_ID)); assertThat(repository.getCommentsOfProcessInstance(PROCESS1_ID)).extracting("content", "userId", "class") .containsExactlyInAnyOrder( tuple("comment1", JACK_ID, SHumanComment.class), tuple("comment2", JOHN_ID, SHumanComment.class), tuple("comment3", JACK_ID, SHumanComment.class), tuple("comment4", null, SSystemComment.class)); } @Test public void should_save_and_get_SComment() { SHumanComment comment1 = repository.add(new SHumanComment(PROCESS1_ID, "comment1", JACK_ID)); SSystemComment comment2 = repository.add(new SSystemComment(PROCESS2_ID, "comment2")); repository.flush(); Map comment1AsMap = jdbcTemplate .queryForObject( "SELECT ID, KIND, CONTENT, POSTDATE, PROCESSINSTANCEID, USERID FROM process_comment WHERE processInstanceId = " + PROCESS1_ID, new JdbcRowMapper("ID", "POSTDATE", "PROCESSINSTANCEID", "USERID")); Map comment2AsMap = jdbcTemplate .queryForObject( "SELECT ID, KIND, CONTENT, POSTDATE, PROCESSINSTANCEID, USERID FROM process_comment WHERE processInstanceId = " + PROCESS2_ID, new JdbcRowMapper("ID", "POSTDATE", "PROCESSINSTANCEID", "USERID")); assertThat(comment1AsMap).containsOnly( entry("ID", comment1.getId()), entry("KIND", "human"), entry("CONTENT", "comment1"), entry("POSTDATE", comment1.getPostDate()), entry("PROCESSINSTANCEID", PROCESS1_ID), entry("USERID", JACK_ID)); assertThat(comment2AsMap).containsOnly( entry("ID", comment2.getId()), entry("KIND", "system"), entry("CONTENT", "comment2"), entry("POSTDATE", comment2.getPostDate()), entry("PROCESSINSTANCEID", PROCESS2_ID), entry("USERID", null)); } @Test public void should_save_and_get_SAComment() { SAComment comment1 = repository.add(new SAComment(new SHumanComment(PROCESS1_ID, "comment1", JACK_ID))); SAComment comment2 = repository.add(new SAComment(new SSystemComment(PROCESS2_ID, "comment2"))); repository.flush(); PersistentObject comment1FromQuery = repository.selectOne("getArchivedCommentById", pair("id", comment1.getId())); PersistentObject comment2FromQuery = repository.selectOne("getArchivedCommentById", pair("id", comment2.getId())); Map comment1AsMap = jdbcTemplate .queryForObject( "SELECT ID, SOURCEOBJECTID, ARCHIVEDATE, CONTENT, POSTDATE, PROCESSINSTANCEID, USERID FROM arch_process_comment WHERE processInstanceId = " + PROCESS1_ID, new JdbcRowMapper("ID", "SOURCEOBJECTID", "ARCHIVEDATE", "POSTDATE", "PROCESSINSTANCEID", "USERID")); Map comment2AsMap = jdbcTemplate .queryForObject( "SELECT ID, SOURCEOBJECTID, ARCHIVEDATE, CONTENT, POSTDATE, PROCESSINSTANCEID, USERID FROM arch_process_comment WHERE processInstanceId = " + PROCESS2_ID, new JdbcRowMapper("ID", "SOURCEOBJECTID", "ARCHIVEDATE", "POSTDATE", "PROCESSINSTANCEID", "USERID")); assertThat(comment1FromQuery).isEqualTo(comment1); assertThat(comment2FromQuery).isEqualTo(comment2); assertThat(comment1AsMap).containsOnly( entry("ID", comment1.getId()), entry("SOURCEOBJECTID", 0L), entry("ARCHIVEDATE", 0L), entry("CONTENT", "comment1"), entry("POSTDATE", comment1.getPostDate()), entry("PROCESSINSTANCEID", PROCESS1_ID), entry("USERID", JACK_ID)); assertThat(comment2AsMap).containsOnly( entry("ID", comment2.getId()), entry("SOURCEOBJECTID", 0L), entry("ARCHIVEDATE", 0L), entry("CONTENT", "comment2"), entry("POSTDATE", comment2.getPostDate()), entry("PROCESSINSTANCEID", PROCESS2_ID), entry("USERID", null)); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/ConnectorInstanceQueriesTest.java ================================================ /** * 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.core.process.instance.model; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bpm.connector.ConnectorEvent.ON_ENTER; import static org.bonitasoft.engine.bpm.connector.ConnectorEvent.ON_FINISH; import static org.bonitasoft.engine.test.persistence.builder.ConnectorInstanceBuilder.aConnectorInstance; import java.util.List; import org.assertj.core.api.Condition; import org.assertj.core.util.Lists; import org.bonitasoft.engine.bpm.connector.ConnectorState; import org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance; import org.bonitasoft.engine.test.persistence.repository.ConnectorInstanceRepository; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; /** * @author Julien Reboul */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class ConnectorInstanceQueriesTest { @Autowired private ConnectorInstanceRepository repository; @Autowired private JdbcTemplate jdbcTemplate; private SAbstractConnectorInstance expectedConnector1; private SAbstractConnectorInstance expectedConnector2; private String containerType; private long containerId; private SAbstractConnectorInstance expectedConnector3; private SAbstractConnectorInstance expectedConnector4; private SAbstractConnectorInstance expectedConnector5; private SAbstractConnectorInstance connectorInstanceOfDifferentContainer; private SAbstractConnectorInstance expectedConnector6; /** * */ @Before public void setUp() { containerType = "Pouet"; containerId = 1L; long differentContainerId = 5L; expectedConnector1 = aConnectorInstance().setContainerId(containerId).setContainerType(containerType) .setActivationEvent(ON_FINISH) .withFailureInfo(false).setState(ConnectorState.EXECUTING.toString()).build(); expectedConnector2 = aConnectorInstance().setContainerId(containerId).setContainerType(containerType) .setActivationEvent(ON_ENTER) .setState(ConnectorState.EXECUTING.toString()).withFailureInfo(false).setExecutionOrder(1) .build(); expectedConnector3 = aConnectorInstance().setContainerId(containerId).setContainerType(containerType) .setActivationEvent(ON_ENTER) .setState(ConnectorState.DONE.toString()).withFailureInfo(false) .build(); expectedConnector4 = aConnectorInstance().setContainerId(containerId).setContainerType(containerType) .setActivationEvent(ON_ENTER) .setState(ConnectorState.TO_BE_EXECUTED.toString()).withFailureInfo(false).setExecutionOrder(2) .build(); expectedConnector5 = aConnectorInstance().setContainerId(containerId).setContainerType(containerType) .setActivationEvent(ON_ENTER) .setState(ConnectorState.FAILED.toString()).withFailureInfo(false) .build(); expectedConnector6 = aConnectorInstance().setContainerId(containerId).setContainerType(containerType) .setActivationEvent(ON_FINISH) .setState(ConnectorState.FAILED.toString()).withFailureInfo(false) .build(); expectedConnector1.setId(10L); expectedConnector2.setId(2L); expectedConnector3.setId(7L); expectedConnector4.setId(160L); expectedConnector5.setId(1L); expectedConnector6.setId(6L); repository.add(expectedConnector1); repository.add(expectedConnector2); repository.add(expectedConnector3); repository.add(expectedConnector4); repository.add(expectedConnector5); repository.add(expectedConnector6); connectorInstanceOfDifferentContainer = repository .add(aConnectorInstance().setContainerId(differentContainerId).setContainerType(containerType) .setActivationEvent(ON_ENTER) .withFailureInfo(false).build());// unexpected connector on different container } @Test public void getConnectorInstances() { List connectors = repository.getConnectorInstances(containerId, containerType); assertThat(connectors).containsOnly(expectedConnector1, expectedConnector2, expectedConnector6, expectedConnector3, expectedConnector4, expectedConnector5); } @Test public void getConnectorInstancesOrderedById() { List connectors = repository .getConnectorInstancesOrderedById(containerId, containerType); assertThat(connectors).containsExactly(expectedConnector5, expectedConnector2, expectedConnector6, expectedConnector3, expectedConnector1, expectedConnector4); } @Test public void getNumberOfConnectorInstances() { long nbOfConnectors = repository .getNumberOfConnectorInstances(containerId, containerType); assertThat(nbOfConnectors).isEqualTo(6); } @Test public void getNextExecutableConnectorInstance() { SConnectorInstance connector = repository .getNextExecutableConnectorInstance(containerId, containerType, ON_ENTER); assertThat(connector).isSameAs(expectedConnector2); } @Test public void searchSConnectorInstance() { List connectors = repository .searchSConnectorInstance(); assertThat(connectors).containsOnly(expectedConnector1, expectedConnector2, expectedConnector3, expectedConnector4, expectedConnector5, expectedConnector6, connectorInstanceOfDifferentContainer); } @Test public void getNumberOfSConnectorInstance() { long nbOfConnectors = repository .getNumberOfSConnectorInstance(); assertThat(nbOfConnectors).isEqualTo(7); } @Test public void getConnectorInstancesWithState() { List connectors = repository .getConnectorInstances(containerId, containerType, ON_ENTER, ConnectorState.TO_BE_EXECUTED.toString()); assertThat(connectors).containsOnly(expectedConnector4); } @Test public void getConnectorInstanceWithFailureInfo() { List connectors = repository .getConnectorInstanceWithFailureInfo(containerId); final List idsToFind = Lists.newArrayList(expectedConnector1.getId(), expectedConnector2.getId(), expectedConnector3.getId(), expectedConnector4.getId(), expectedConnector5.getId(), expectedConnector6.getId()); assertThat(connectors).areExactly(6, new Condition() { @Override public boolean matches(SConnectorInstanceWithFailureInfo connector) { return idsToFind.remove(connector.getId()); } }); assertThat(idsToFind).isEmpty(); } @Test public void getConnectorInstancesWithFailureInState() { List connectors = repository .getConnectorInstancesWithFailureInfo(containerId, containerType, ConnectorState.FAILED.toString()); final List idsToFind = Lists.newArrayList(expectedConnector5.getId(), expectedConnector6.getId()); assertThat(connectors).hasSize(2); assertThat(idsToFind).contains(connectors.get(0).getId()); assertThat(idsToFind).contains(connectors.get(1).getId()); } @Test public void should_store_ACTIVATION_EVENT_as_string() { final SAbstractConnectorInstance connectorInstance = repository .add(aConnectorInstance().setActivationEvent(ON_ENTER).build()); repository.flush(); final String activationEvent = jdbcTemplate.queryForObject( "select activationEvent from connector_instance where id = " + connectorInstance.getId(), String.class); assertThat(activationEvent).isEqualTo("ON_ENTER"); assertThat(connectorInstance.getActivationEvent()).isEqualTo(ON_ENTER); } @Test public void should_store_ACTIVATION_EVENT_as_string_of_archived_connetor() { SAConnectorInstance entity = new SAConnectorInstance(); entity.setActivationEvent(ON_FINISH); final SAConnectorInstance connectorInstance = repository.add(entity); repository.flush(); final String activationEvent = jdbcTemplate.queryForObject( "select activationEvent from arch_connector_instance where id = " + connectorInstance.getId(), String.class); assertThat(activationEvent).isEqualTo("ON_FINISH"); assertThat(connectorInstance.getActivationEvent()).isEqualTo(ON_FINISH); } @Test public void should_store_stack_trace_of_connector() { SConnectorInstanceWithFailureInfo sConnectorInstanceWithFailureInfo = new SConnectorInstanceWithFailureInfo(); sConnectorInstanceWithFailureInfo.setStackTrace("a stack trace"); final SConnectorInstanceWithFailureInfo connectorInstance = repository.add(sConnectorInstanceWithFailureInfo); repository.flush(); final String stackTrace = jdbcTemplate.queryForObject( "select stackTrace from connector_instance where id = " + connectorInstance.getId(), String.class); assertThat(stackTrace).isEqualTo("a stack trace"); assertThat(connectorInstance.getStackTrace()).isEqualTo("a stack trace"); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/CustomUserInfoQueriesTest.java ================================================ /** * 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.core.process.instance.model; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.test.persistence.builder.CustomUserInfoDefinitionBuilder.aCustomUserInfoDefinition; import static org.bonitasoft.engine.test.persistence.builder.CustomUserInfoValueBuilder.aCustomUserInfoValue; import static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser; import java.util.List; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.test.persistence.repository.CustomUserInfoRepository; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; /** * @author Elias Ricken de Medeiros */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class CustomUserInfoQueriesTest { private static final String DEVELOPER_NAME = "developer"; private static final String SKILLS_NAME = "skills"; @Autowired private CustomUserInfoRepository repository; private SUser user1; private SUser user2; private SUser user3; private SUser user4; private SCustomUserInfoDefinition skills; private SCustomUserInfoDefinition developer; @Before public void setUp() { user1 = repository.add(aUser().withUserName("john").build()); user2 = repository.add(aUser().withUserName("peter").build()); user3 = repository.add(aUser().withUserName("paul").build()); user4 = repository.add(aUser().withUserName("mike").build()); skills = repository.add(aCustomUserInfoDefinition().withName(SKILLS_NAME).build()); developer = repository.add(aCustomUserInfoDefinition().withName(DEVELOPER_NAME).build()); } @Test public void query_getUserIdsWithCustomUserInfo_should_return_empty_list_if_no_users_has_the_chosen_custom_user_info() { // when final List userIds = repository.getUserIdsWithCustomUserInfo(SKILLS_NAME, "Java", false); // then assertThat(userIds).isEmpty(); } @Test public void query_getUserIdsWithCustomUserInfo_should_return_only_ids_of_users_with_chosen_custom_user_info() { // given repository.add(aCustomUserInfoValue().withUserId(user1.getId()).withCustomUserInfoDefinitionId(skills.getId()) .withValue("Java").build()); repository.add(aCustomUserInfoValue().withUserId(user2.getId()) .withCustomUserInfoDefinitionId(developer.getId()).withValue("Java").build()); repository.add(aCustomUserInfoValue().withUserId(user3.getId()).withCustomUserInfoDefinitionId(skills.getId()) .withValue("C").build()); repository.add(aCustomUserInfoValue().withUserId(user4.getId()).withCustomUserInfoDefinitionId(skills.getId()) .withValue("Java").build()); // when final List userIds = repository.getUserIdsWithCustomUserInfo(SKILLS_NAME, "Java", false); // then assertThat(userIds).hasSize(2); assertThat(userIds).contains(user1.getId()); assertThat(userIds).contains(user4.getId()); } @Test public void query_getUserIdsWithCustomUserInfoContains_should_return_ids_of_users_with_chosen_custom_user_info_partial_match() { // given repository.add(aCustomUserInfoValue().withUserId(user1.getId()).withCustomUserInfoDefinitionId(skills.getId()) .withValue("Java").build()); repository.add(aCustomUserInfoValue().withUserId(user2.getId()) .withCustomUserInfoDefinitionId(developer.getId()).withValue("Java").build()); repository.add(aCustomUserInfoValue().withUserId(user3.getId()).withCustomUserInfoDefinitionId(skills.getId()) .withValue("C").build()); repository.add(aCustomUserInfoValue().withUserId(user4.getId()).withCustomUserInfoDefinitionId(skills.getId()) .withValue("Java").build()); // when final List userIdsWholeValue = repository.getUserIdsWithCustomUserInfo(SKILLS_NAME, "Java", true); final List userIdsPartialValue = repository.getUserIdsWithCustomUserInfo(SKILLS_NAME, "av", true); // then assertThat(userIdsWholeValue).hasSize(2); assertThat(userIdsWholeValue).contains(user1.getId()); assertThat(userIdsWholeValue).contains(user4.getId()); assertThat(userIdsPartialValue).hasSize(2); assertThat(userIdsPartialValue).contains(user1.getId()); assertThat(userIdsPartialValue).contains(user4.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/EventTriggerInstanceQueriesTest.java ================================================ /** * 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.core.process.instance.model; import static org.junit.Assert.assertEquals; import java.util.List; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance; import org.bonitasoft.engine.test.persistence.repository.ProcessInstanceRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class EventTriggerInstanceQueriesTest { @Autowired private ProcessInstanceRepository repository; @Test public void getNumberOfTimerEventTriggerInstances_should_return_number_of_event_trigger_instance_attached_to_the_process_instance() { // Given final long processInstanceId = 9634L; final SIntermediateCatchEventInstance eventInstanceImpl = (SIntermediateCatchEventInstance) repository .add(buildSIntermediateCatchEventInstanceImpl(101L, "name", processInstanceId)); repository.add(buildSTimerEventTriggerInstance(87L, eventInstanceImpl.getId(), "name", "jobTriggerName")); // Then final long numberOfEventTriggerInstances = repository.getNumberOfTimerEventTriggerInstances(processInstanceId, null); // When assertEquals(1, numberOfEventTriggerInstances); } @Test public void getNumberOfTimerEventTriggerInstances_should_return_number_of_event_trigger_instance_attached_to_the_process_instance_filter_by_event_instance_name() { // Given final long processInstanceId = 9634L; final SIntermediateCatchEventInstance eventInstanceImpl = (SIntermediateCatchEventInstance) repository .add(buildSIntermediateCatchEventInstanceImpl(101L, "name", processInstanceId)); repository.add(buildSTimerEventTriggerInstance(96L, eventInstanceImpl.getId(), "name", "jobTriggerName")); final SIntermediateCatchEventInstance eventInstanceImpl2 = (SIntermediateCatchEventInstance) repository .add(buildSIntermediateCatchEventInstanceImpl(104L, "toto", processInstanceId)); repository.add(buildSTimerEventTriggerInstance(98L, eventInstanceImpl2.getId(), "toto", "plop")); // Then final long numberOfEventTriggerInstances = repository.getNumberOfTimerEventTriggerInstances(processInstanceId, "name"); // When assertEquals(1, numberOfEventTriggerInstances); } @Test public void searchTimerEventTriggerInstances_should_return_event_trigger_instance_attached_to_the_process_instance() { // Given final long processInstanceId = 9634L; final SIntermediateCatchEventInstance eventInstanceImpl = (SIntermediateCatchEventInstance) repository .add(buildSIntermediateCatchEventInstanceImpl(101L, "name", processInstanceId)); repository.add(buildSTimerEventTriggerInstance(87L, eventInstanceImpl.getId(), "name", "jobTriggerName")); // Then final List sTimerEventTriggerInstances = repository .searchTimerEventTriggerInstances(processInstanceId, null); // When assertEquals(1, sTimerEventTriggerInstances.size()); } @Test public void searchTimerEventTriggerInstances_should_should_return_event_trigger_instance_attached_to_the_process_instance_filter_by_event_instance_name() { // Given final long processInstanceId = 9634L; final SIntermediateCatchEventInstance eventInstanceImpl = (SIntermediateCatchEventInstance) repository .add(buildSIntermediateCatchEventInstanceImpl(101L, "name", processInstanceId)); repository.add(buildSTimerEventTriggerInstance(96L, eventInstanceImpl.getId(), "name", "jobTriggerName")); final SIntermediateCatchEventInstance eventInstanceImpl2 = (SIntermediateCatchEventInstance) repository .add(buildSIntermediateCatchEventInstanceImpl(103L, "toto", processInstanceId)); repository.add(buildSTimerEventTriggerInstance(98L, eventInstanceImpl2.getId(), "toto", "plop")); // Then final List sTimerEventTriggerInstances = repository .searchTimerEventTriggerInstances(processInstanceId, "toto"); // When assertEquals(1, sTimerEventTriggerInstances.size()); assertEquals(98L, sTimerEventTriggerInstances.get(0).getId()); } private STimerEventTriggerInstance buildSTimerEventTriggerInstance(final long id, final long eventInstanceId, final String eventInstanceName, final String jobTriggerName) { final STimerEventTriggerInstance sTimerEventTriggerInstance = new STimerEventTriggerInstance(eventInstanceId, eventInstanceName, 96L, jobTriggerName); sTimerEventTriggerInstance.setId(id); return sTimerEventTriggerInstance; } private SIntermediateCatchEventInstance buildSIntermediateCatchEventInstanceImpl(final long id, final String name, final long processInstanceId) { final SIntermediateCatchEventInstance sIntermediateCatchEventInstance = new SIntermediateCatchEventInstance( name, 9, 6, 8, 4, 2); sIntermediateCatchEventInstance.setLogicalGroup(3, processInstanceId); sIntermediateCatchEventInstance.setId(id); return sIntermediateCatchEventInstance; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/FlowNodeInstanceTest.java ================================================ /** * 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.core.process.instance.model; import static java.time.Instant.now; import static java.time.temporal.ChronoUnit.DAYS; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.commons.Pair.pair; import static org.bonitasoft.engine.core.process.definition.model.SGatewayType.PARALLEL; import static org.bonitasoft.engine.test.persistence.builder.ActorBuilder.anActor; import static org.bonitasoft.engine.test.persistence.builder.ActorMemberBuilder.anActorMember; import static org.bonitasoft.engine.test.persistence.builder.BoundaryInstanceBuilder.aBoundary; import static org.bonitasoft.engine.test.persistence.builder.GatewayInstanceBuilder.aGatewayInstanceBuilder; import static org.bonitasoft.engine.test.persistence.builder.LoopActivityInstanceBuilder.aLoopActivity; import static org.bonitasoft.engine.test.persistence.builder.PendingActivityMappingBuilder.aPendingActivityMapping; import static org.bonitasoft.engine.test.persistence.builder.ProcessInstanceBuilder.aProcessInstance; import static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser; import static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership; import static org.bonitasoft.engine.test.persistence.builder.UserTaskInstanceBuilder.aUserTask; import static org.bonitasoft.engine.test.persistence.builder.archive.ArchivedUserTaskInstanceBuilder.anArchivedUserTask; import java.time.Duration; import java.util.List; import java.util.Map; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.test.persistence.repository.FlowNodeInstanceRepository; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class FlowNodeInstanceTest { private static final long GROUP_FOR_BOB_ID = 74L; private static final long GROUP_FOR_JOHN_ID = 9875L; private static final long JOHN_ID = 654L; private static final long BOB_ID = 697L; private static final long PROCESS_DEFINITION_ID = 111L; private static final long ROOT_PROCESS_INSTANCE_ID = 963L; private static final long NORMAL_HUMAN_INSTANCE_ID = 743L; @Autowired private FlowNodeInstanceRepository repository; @Autowired private JdbcTemplate jdbcTemplate; @Before public void before() { repository.add(aUser().withId(JOHN_ID).build()); repository.add(aUser().withId(BOB_ID).build()); // Create process final String processNameSupervisedByJohn = "Process definition"; buildAndCreateProcessDefinition(6L, PROCESS_DEFINITION_ID, processNameSupervisedByJohn); repository.add( aProcessInstance().withProcessDefinitionId(PROCESS_DEFINITION_ID).withName(processNameSupervisedByJohn) .withId(ROOT_PROCESS_INSTANCE_ID).build()); } protected void buildAndCreateProcessDefinition(final long id, final long processDefinitionId, final String processName) { final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = new SProcessDefinitionDeployInfo(); sProcessDefinitionDeployInfo.setId(id); sProcessDefinitionDeployInfo.setName(processName); sProcessDefinitionDeployInfo.setVersion("version"); sProcessDefinitionDeployInfo.setProcessId(processDefinitionId); repository.add(sProcessDefinitionDeployInfo); } @Test public void getFlowNodeInstanceIdsToRecover_should_return_ids_of_flow_nodes_that_needs_to_be_recovered() { // given repository .add(aUserTask().withName("normalTask1").withStateExecuting(false).withStable(true).withTerminal(false) .build()); repository .add(aUserTask().withName("executingTask").withStateExecuting(true).withStable(true).withTerminal(false) .build()); repository.add( aUserTask().withName("notStableTask").withStateExecuting(false).withStable(false).withTerminal(true) .build()); final SFlowNodeInstance terminal = repository .add(aUserTask().withName("terminalTask").withStateExecuting(false).withStable(true).withTerminal(true) .build()); repository.add(aUserTask().withName("failedTask").withStateExecuting(true).withStable(true).withTerminal(true) .withStateId(3).build()); repository.add(aUserTask().withName("normalTask2").withStateExecuting(false).withStable(true) .withTerminal(false).build()); repository.add(aGatewayInstanceBuilder().withName("gateway_completed").withStateId(2).withStable(true) .withTerminal(true).withHitBys("FINISH:12").build()); repository.add(aGatewayInstanceBuilder().withName("gateway_initializing_but_not_finished1").withStateId(61) .withStable(false).withHitBys("1").build()); repository.add(aBoundary().withName("abortingBoundary").withActivity(terminal.getId()).withStateId(10) .withStateExecuting(false).withStable(true) .withTerminal(false).withStateName("WAITING") .withStateCategory(SStateCategory.ABORTING).build()); repository.add(aBoundary().withName("cancellingBoundary").withActivity(terminal.getId()).withStateId(10) .withStateExecuting(false).withStable(true) .withTerminal(false).withStateName("WAITING") .withStateCategory(SStateCategory.CANCELLING).build()); repository.add(aBoundary().withName("errorBoundary").withActivity(terminal.getId()).withStateId(10) .withStateExecuting(false).withStable(true) .withTerminal(false).withStateName("WAITING") .withStateCategory(SStateCategory.NORMAL).build()); // when final QueryOptions options = new QueryOptions(0, 10); final List nodeToRestart = repository.getFlowNodeInstanceIdsToRecover(Duration.ZERO, options); // then assertThat(nodeToRestart.stream() .map(id -> repository.getSession() .get(SFlowNodeInstance.class, id).getName())) .containsOnly("executingTask", "notStableTask", "terminalTask", "abortingBoundary", "cancellingBoundary"); } @Test public void getGatewayInstanceIdsToRecover_should_return_ids_of_gateways_flagged_as_FINISH() { // given repository.add(aGatewayInstanceBuilder().withName("gateway_initializing_but_not_finished1").withStateId(61) .withStable(false).withHitBys("1").build()); repository.add(aGatewayInstanceBuilder().withName("gateway_initializing_but_not_finished2").withStateId(61) .withStable(false).withHitBys("1,2").build()); repository.add(aGatewayInstanceBuilder().withName("gateway_initializing_but_finished").withStateId(61) .withStable(false).withHitBys("FINISH:12").build()); repository.add(aGatewayInstanceBuilder().withName("gateway_failed").withStateExecuting(true).withStable(true) .withTerminal(true).withStateId(3).build()); repository.add(aGatewayInstanceBuilder().withName("gateway_completed").withStateId(2).withStable(true) .withTerminal(true).withHitBys("FINISH:12").build()); repository.add(aGatewayInstanceBuilder().withName("gateway_aborting").withStateId(61).withStable(true) .withHitBys("1,2").withStateCategory(SStateCategory.ABORTING).build()); repository.add(aGatewayInstanceBuilder().withName("gateway_cancelling").withStateId(61).withStable(true) .withHitBys("1,2").withStateCategory(SStateCategory.ABORTING).build()); repository.flush(); // when final QueryOptions options = new QueryOptions(0, 10); final List nodeToRestart = repository.getGatewayInstanceIdsToRecover(Duration.ZERO, options); // then assertThat(nodeToRestart.stream() .map(id -> (repository.getSession().get(SFlowNodeInstance.class, id)).getName())) .containsOnly( "gateway_initializing_but_finished", "gateway_completed", "gateway_aborting", "gateway_cancelling"); } @Test public void getGatewayInstanceIdsToRecover_should_return_ids_of_gateways_flagged_as_FINISH_and_older_than_given_duration() { // given repository.add(aGatewayInstanceBuilder().withName("gateway_initializing_but_finished") .withLastUpdateDate(now().minusSeconds(200).toEpochMilli()).withStateId(61).withStable(false) .withHitBys("FINISH:12").build()); repository.add(aGatewayInstanceBuilder().withName("gateway_completed").withStateId(2).withStable(true) .withLastUpdateDate(now().minusSeconds(600).toEpochMilli()).withTerminal(true).withHitBys("FINISH:12") .build()); repository.add(aGatewayInstanceBuilder().withName("gateway_aborting") .withLastUpdateDate(now().minus(2, DAYS).toEpochMilli()).withStateId(61).withStable(true) .withHitBys("1,2").withStateCategory(SStateCategory.ABORTING).build()); repository.flush(); // when final QueryOptions options = new QueryOptions(0, 10); final List nodeToRestart = repository.getGatewayInstanceIdsToRecover(Duration.ofSeconds(500), options); // then assertThat(nodeToRestart.stream() .map(id -> (repository.getSession().get(SFlowNodeInstance.class, id)).getName())) .containsOnly( "gateway_completed", "gateway_aborting"); } @Test public void getFlowNodeInstanceIdsToRecover_should_return_only_element_older_than_given_duration() { // given long now = System.currentTimeMillis(); SFlowNodeInstance oldTask1 = repository .add(aUserTask().withName("oldTask1").withTerminal(true) .withLastUpdateDate(now().minus(2, DAYS).toEpochMilli()).build()); SFlowNodeInstance oldTask2 = repository .add(aUserTask().withName("oldTask2").withTerminal(true) .withLastUpdateDate(now().minusSeconds(600).toEpochMilli()).build()); repository .add(aUserTask().withName("recentTask1").withTerminal(true) .withLastUpdateDate(now().minusSeconds(400).toEpochMilli()).build()); repository.add(aUserTask().withName("recentTask2").withTerminal(true).withLastUpdateDate(now).build()); // when List nodeToRestart = repository.getFlowNodeInstanceIdsToRecover(Duration.ofSeconds(500), new QueryOptions(0, 10)); // then assertThat(nodeToRestart).containsOnly(oldTask1.getId(), oldTask2.getId()); } // For @Test public void getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_assigned_tasks_to_the_user() { // Given buildAndAddAssignedTasks(); // When final long numberOfHumanTaskInstances = repository .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID, JOHN_ID); // Then assertThat(numberOfHumanTaskInstances).isEqualTo(1); } @Test public void getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_the_user() { // Given buildAndAddTasksWithPendingMappingForUser(); // When final long numberOfHumanTaskInstances = repository .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID, JOHN_ID); // Then assertThat(numberOfHumanTaskInstances).isEqualTo(1); } @Test public void getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_the_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUser(); // When final long numberOfHumanTaskInstances = repository .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID, JOHN_ID); // Then assertThat(numberOfHumanTaskInstances).isEqualTo(1); } @Test public void getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_the_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser(); // When final long numberOfHumanTaskInstances = repository .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID, JOHN_ID); // Then assertThat(numberOfHumanTaskInstances).isEqualTo(1); } @Test public void searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_assigned_tasks_to_the_user() { // Given buildAndAddAssignedTasks(); // When final List sHumanTaskInstances = repository .searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID, JOHN_ID); // Then assertThat(sHumanTaskInstances).hasSize(1); assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID); } @Test public void searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_the_user() { // Given buildAndAddTasksWithPendingMappingForUser(); // When final List sHumanTaskInstances = repository .searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID, JOHN_ID); // Then assertThat(sHumanTaskInstances).hasSize(1); assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID); } @Test public void searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_the_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUser(); // When final List sHumanTaskInstances = repository .searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID, JOHN_ID); // Then assertThat(sHumanTaskInstances).hasSize(1); assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID); } @Test public void searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_the_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser(); // When final List sHumanTaskInstances = repository .searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor(PROCESS_DEFINITION_ID, JOHN_ID); // Then assertThat(sHumanTaskInstances).hasSize(1); assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID); } // All @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_assigned_tasks() { // Given buildAndAddAssignedTasks(); // When final long numberOfHumanTaskInstances = repository .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID); // Then assertThat(numberOfHumanTaskInstances).isEqualTo(2); } @Test public void getNumberOfFlowNodesOfProcessDefinitionInAllStates_should_return_results_aggregated_by_name_and_state() { buildAndAddUserTaskWithProcessDefinitionId("step1", 15L, 28, "executing"); buildAndAddUserTaskWithProcessDefinitionId("step1", 15L, 28, "executing"); buildAndAddUserTaskWithProcessDefinitionId("step1", 15L, 3, "failed"); buildAndAddUserTaskWithProcessDefinitionId("step1", 1L, 29, "someflownodeStateName"); // should not be selected because it is a flownode of another process buildAndAddUserTaskWithProcessDefinitionId("step2", 15L, 3, "failed"); buildAndAddUserTaskWithProcessDefinitionId("step1", 15L, 4, "ready"); List counters = repository .getNumberOfFlowNodesOfProcessDefinitionInAllStates(15L); assertThat(counters).hasSize(4); assertThat(counters.get(0).getFlownodeName()).isEqualTo("step1"); assertThat(counters.get(0).getStateName()).isEqualTo("executing"); assertThat(counters.get(0).getNumberOf()).isEqualTo(2L); assertThat(counters.get(1).getFlownodeName()).isEqualTo("step1"); assertThat(counters.get(1).getStateName()).isEqualTo("failed"); assertThat(counters.get(1).getNumberOf()).isEqualTo(1L); assertThat(counters.get(2).getFlownodeName()).isEqualTo("step1"); assertThat(counters.get(2).getStateName()).isEqualTo("ready"); assertThat(counters.get(2).getNumberOf()).isEqualTo(1L); assertThat(counters.get(3).getFlownodeName()).isEqualTo("step2"); assertThat(counters.get(3).getStateName()).isEqualTo("failed"); assertThat(counters.get(3).getNumberOf()).isEqualTo(1L); } private SFlowNodeInstance buildAndAddUserTaskWithProcessDefinitionId(final String taskName, final long processDefinitionId, int stateId, String stateName) { return repository .add(aUserTask().withName(taskName).withStateExecuting(false).withStable(true).withTerminal(false) .withLogicalGroup1(processDefinitionId) .withStateId(stateId).withStateName(stateName).build()); } @Test public void getNumberOfFlowNodesInAllStates_should_return_results_aggregated_by_name_and_state() { buildAndAddUserTaskWithParentAndRootProcessInstanceId("step1", 147L, 0L, 28, "executing"); buildAndAddUserTaskWithParentAndRootProcessInstanceId("step1", 147L, 0L, 28, "executing"); buildAndAddUserTaskWithParentAndRootProcessInstanceId("step1", 147L, 0L, 3, "failed"); buildAndAddUserTaskWithParentAndRootProcessInstanceId("step1", 0L, 147L, 29, "someflownodeStateName"); // should not be selected because it is a flownode in a sub-process buildAndAddUserTaskWithParentAndRootProcessInstanceId("step2", 147L, 0L, 3, "failed"); buildAndAddUserTaskWithParentAndRootProcessInstanceId("step1", 147L, 0L, 4, "ready"); List counters = repository.getNumberOfFlowNodesInAllStates(147L); assertThat(counters).hasSize(4); assertThat(counters.get(0).getFlownodeName()).isEqualTo("step1"); assertThat(counters.get(0).getStateName()).isEqualTo("executing"); assertThat(counters.get(0).getNumberOf()).isEqualTo(2L); assertThat(counters.get(1).getFlownodeName()).isEqualTo("step1"); assertThat(counters.get(1).getStateName()).isEqualTo("failed"); assertThat(counters.get(1).getNumberOf()).isEqualTo(1L); assertThat(counters.get(2).getFlownodeName()).isEqualTo("step1"); assertThat(counters.get(2).getStateName()).isEqualTo("ready"); assertThat(counters.get(2).getNumberOf()).isEqualTo(1L); assertThat(counters.get(3).getFlownodeName()).isEqualTo("step2"); assertThat(counters.get(3).getStateName()).isEqualTo("failed"); assertThat(counters.get(3).getNumberOf()).isEqualTo(1L); } private SFlowNodeInstance buildAndAddUserTaskWithParentAndRootProcessInstanceId(final String taskName, final long containingProcessInstanceId, final long rootProcessInstanceId, int stateId, String stateName) { return repository .add(aUserTask().withName(taskName).withStateExecuting(false).withStable(true).withTerminal(false) .withLogicalGroup4(containingProcessInstanceId).withLogicalGroup2(rootProcessInstanceId) .withStateId(stateId).withStateName(stateName).build()); } @Test public void getNumberOfArchivedFlowNodesInAllStates_should_return_results_aggregated_by_name_and_state() { buildAndAddArchivedUserTaskWithParentAndRootProcessInstanceId("step1", 147L, 0L, 2, "completed", true); buildAndAddArchivedUserTaskWithParentAndRootProcessInstanceId("step1", 147L, 0L, 2, "completed", true); buildAndAddArchivedUserTaskWithParentAndRootProcessInstanceId("step1", 147L, 0L, 2, "completed", false); // should not be selected because non-terminal state buildAndAddArchivedUserTaskWithParentAndRootProcessInstanceId("step1", 0L, 147L, 221, "not_used", true); // should not be selected because it is a flownode in a sub-process buildAndAddArchivedUserTaskWithParentAndRootProcessInstanceId("step2", 147L, 0L, 2, "completed", true); buildAndAddArchivedUserTaskWithParentAndRootProcessInstanceId("step1", 147L, 0L, 4, "aborted", true); List counters = repository.getNumberOfArchivedFlowNodesInAllStates(147L); assertThat(counters).hasSize(3); assertThat(counters.get(0).getFlownodeName()).isEqualTo("step1"); assertThat(counters.get(0).getStateName()).isEqualTo("aborted"); assertThat(counters.get(0).getNumberOf()).isEqualTo(1L); assertThat(counters.get(1).getFlownodeName()).isEqualTo("step1"); assertThat(counters.get(1).getStateName()).isEqualTo("completed"); assertThat(counters.get(1).getNumberOf()).isEqualTo(2L); assertThat(counters.get(2).getFlownodeName()).isEqualTo("step2"); assertThat(counters.get(2).getStateName()).isEqualTo("completed"); assertThat(counters.get(2).getNumberOf()).isEqualTo(1L); } private SAFlowNodeInstance buildAndAddArchivedUserTaskWithParentAndRootProcessInstanceId(String taskName, long containingProcessInstanceId, long rootProcessInstanceId, int stateId, String stateName, boolean terminal) { return repository.add(anArchivedUserTask().withName(taskName).withLogicalGroup4(containingProcessInstanceId) .withLogicalGroup2(rootProcessInstanceId) .withStateId(stateId).withStateName(stateName).withTerminal(terminal).build()); } @Test public void getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_user() { // Given buildAndAddTasksWithPendingMappingForUser(); // When final long numberOfHumanTaskInstances = repository .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID); // Then assertThat(numberOfHumanTaskInstances).isEqualTo(2); } @Test public void getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUser(); // When final long numberOfHumanTaskInstances = repository .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID); // Then assertThat(numberOfHumanTaskInstances).isEqualTo(2); } @Test public void getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser(); // When final long numberOfHumanTaskInstances = repository .getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID); // Then assertThat(numberOfHumanTaskInstances).isEqualTo(2); } @Test public void searchSHumanTaskInstanceAssignedAndPendingByRootProcess_should_return_number_of_process_definition_supervised_if_one_instance_has_assigned_tasks() { // Given buildAndAddAssignedTasks(); // When final List sHumanTaskInstances = repository .searchSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID); // Then assertThat(sHumanTaskInstances).hasSize(2); assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID); } @Test public void searchSHumanTaskInstanceAssignedAndPendingByRootProcess_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_user() { // Given buildAndAddTasksWithPendingMappingForUser(); // When final List sHumanTaskInstances = repository .searchSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID); // Then assertThat(sHumanTaskInstances).hasSize(2); assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID); } @Test public void searchSHumanTaskInstanceAssignedAndPendingByRootProcess_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUser(); // When final List sHumanTaskInstances = repository .searchSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID); // Then assertThat(sHumanTaskInstances).hasSize(2); assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID); } @Test public void searchSHumanTaskInstanceAssignedAndPendingByRootProcess_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser(); // When final List sHumanTaskInstances = repository .searchSHumanTaskInstanceAssignedAndPendingByRootProcess(PROCESS_DEFINITION_ID); // Then assertThat(sHumanTaskInstances).hasSize(2); assertThat(sHumanTaskInstances.get(0).getId()).isEqualTo(NORMAL_HUMAN_INSTANCE_ID); } @Test public void getActiveGatewayInstance_should_return_gateway_if_not_finished() { // Given final SGatewayInstance gatewayInstance = aGatewayInstanceBuilder().withHitBys("1,2").withName("gate1") .withTerminal(false) .withLogicalGroup4(ROOT_PROCESS_INSTANCE_ID).build(); repository.add(gatewayInstance); // When final SGatewayInstance gate1 = repository.getActiveGatewayInstanceOfProcess(ROOT_PROCESS_INSTANCE_ID, "gate1"); // Then assertThat(gate1).isEqualTo(gatewayInstance); } @Test public void getActiveGatewayInstance_should_not_return_gateway_if_finished() { // Given repository .add(aGatewayInstanceBuilder().withHitBys("FINISH:1").withTerminal(true).withName("gate1") .withLogicalGroup4(ROOT_PROCESS_INSTANCE_ID).build()); // When final SGatewayInstance gate1 = repository.getActiveGatewayInstanceOfProcess(ROOT_PROCESS_INSTANCE_ID, "gate1"); // Then assertThat(gate1).isNull(); } @Test public void getActiveGatewayInstance_should_not_return_gateway_if_wrong_name() { // Given final SGatewayInstance gatewayInstance = aGatewayInstanceBuilder().withHitBys("1,2") .withName("notTheGoodGateway").withTerminal(false) .withLogicalGroup4(ROOT_PROCESS_INSTANCE_ID).build(); repository.add(gatewayInstance); // When final SGatewayInstance gate1 = repository.getActiveGatewayInstanceOfProcess(ROOT_PROCESS_INSTANCE_ID, "gate1"); // Then assertThat(gate1).isNull(); } @Test public void should_get_flownodeInstances_by_stateCategory() { // Given final SFlowNodeInstance sFlowNodeInstance = repository .add(aUserTask().withName("executingTask").withStateExecuting(true).build()); repository.flush(); final String stateCategory = jdbcTemplate.queryForObject("select stateCategory from flownode_instance", String.class); final String priority = jdbcTemplate.queryForObject("select priority from flownode_instance", String.class); assertThat(stateCategory).isEqualTo("NORMAL"); assertThat(priority).isEqualTo("2"); } @Test public void should_getDirectChildrenOfProcessInstance() { // Given //is directly contained in the process "5" repository.add(aUserTask().withLogicalGroup3(0).withLogicalGroup4(5).withName("multiInstance") .withStateExecuting(true).build()); //is directly contained in the activity "6" that is in the process "5" repository.add(aUserTask().withLogicalGroup3(6).withLogicalGroup4(5).withName("callActivity") .withStateExecuting(true).build()); repository.flush(); List flowNodes = repository.selectList("getDirectChildrenOfProcessInstance", pair("parentProcessInstanceId", 5L)); assertThat(flowNodes).hasSize(1).anyMatch(f -> f.getName().equals("multiInstance")); } @Test public void should_getAllChildrenOfProcessInstance() { // Given //is directly contained in the process "5" repository.add(aUserTask().withLogicalGroup3(0).withLogicalGroup4(5).withName("multiInstance") .withStateExecuting(true).build()); //is directly contained in the activity "6" that is in the process "5" repository.add(aUserTask().withLogicalGroup3(6).withLogicalGroup4(5).withName("callActivity") .withStateExecuting(true).build()); repository.flush(); List flowNodes = repository.selectList("getAllChildrenOfProcessInstance", pair("parentProcessInstanceId", 5L)); assertThat(flowNodes).hasSize(2) .anyMatch(f -> f.getName().equals("multiInstance")) .anyMatch(f -> f.getName().equals("callActivity")); } @Test public void should_have_loopCounter_on_loop_Activity() { // Given final SLoopActivityInstance sLoopActivityInstance = (SLoopActivityInstance) repository .add((PersistentObject) aLoopActivity().withLoopCounter(6).build()); repository.flush(); final int loopCounter = jdbcTemplate.queryForObject("select loop_counter from flownode_instance", Integer.class); repository.getById(sLoopActivityInstance.getId()); assertThat(loopCounter).isEqualTo(6); assertThat(sLoopActivityInstance.getLoopCounter()).isEqualTo(6); } @Test public void should_get_gateway_instances_by_gateway_type() { // Given final SGatewayInstance gatewayInstance = repository .add(aGatewayInstanceBuilder().withGatewayType(PARALLEL).build()); repository.flush(); final String gatewayType = jdbcTemplate.queryForObject("select gatewayType from flownode_instance", String.class); assertThat(gatewayType).isEqualTo("PARALLEL"); assertThat(gatewayInstance.getGatewayType()).isEqualTo(PARALLEL); } private void buildAndAddAssignedTasks() { // Tasks OK assigned to John repository .add(aUserTask().withName("normalTask1").withStateExecuting(false).withStable(true).withTerminal(false) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).withAssigneeId(JOHN_ID) .withId(NORMAL_HUMAN_INSTANCE_ID).build()); // Tasks KO assigned to john & OK not assigned repository .add(aUserTask().withName("executingTask").withStateExecuting(true).withStable(true).withTerminal(false) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).withAssigneeId(JOHN_ID).build()); repository.add( aUserTask().withName("notStableTask").withStateExecuting(false).withStable(false).withTerminal(true) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).withAssigneeId(JOHN_ID).build()); repository .add(aUserTask().withName("terminalTask").withStateExecuting(false).withStable(true).withTerminal(true) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).withAssigneeId(JOHN_ID).build()); buildAndAddNormalTask("normalTask4", ROOT_PROCESS_INSTANCE_ID); // Tasks OK assigned to Bob repository .add(aUserTask().withName("normalTask2").withStateExecuting(false).withStable(true).withTerminal(false) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).withAssigneeId(BOB_ID).build()); } private void buildAndAddTasksWithPendingMappingForUser() { // Tasks OK not assigned & pending for John final SFlowNodeInstance normalTask1 = repository .add(aUserTask().withName("normalTask1").withStateExecuting(false).withStable(true).withTerminal(false) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).withId(NORMAL_HUMAN_INSTANCE_ID).build()); repository.add(aPendingActivityMapping().withUserId(JOHN_ID).withActivityId(normalTask1.getId()).build()); // Tasks KO not assigned & pending for john, and OK not assigned & not pending final SFlowNodeInstance executingTask = buildAndAddExecutingTask(); repository.add(aPendingActivityMapping().withUserId(JOHN_ID).withActivityId(executingTask.getId()).build()); final SFlowNodeInstance notStableTask = buildAndAddNotStableTask(); repository.add(aPendingActivityMapping().withUserId(JOHN_ID).withActivityId(notStableTask.getId()).build()); final SFlowNodeInstance terminalTask = buildAndAddTerminalTask(); repository.add(aPendingActivityMapping().withUserId(JOHN_ID).withActivityId(terminalTask.getId()).build()); buildAndAddNormalTask("normalTask4", ROOT_PROCESS_INSTANCE_ID); // Tasks OK not assigned & pending for Bob final SFlowNodeInstance normalTask4 = buildAndAddNormalTask("normalTask2", ROOT_PROCESS_INSTANCE_ID); repository.add(aPendingActivityMapping().withUserId(BOB_ID).withActivityId(normalTask4.getId()).build()); } private void buildAndAddTasksWithPendingMappingForActorMappedToUser() { final SActor actorForJohn = repository.add(anActor().build()); repository.add(anActorMember().forActor(actorForJohn).withUserId(JOHN_ID).build()); final SActor actorForBob = repository.add(anActor().build()); repository.add(anActorMember().forActor(actorForBob).withUserId(BOB_ID).build()); buildAndAddTasksWithPendingMappingForActor(actorForJohn, actorForBob); } private void buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser() { final long roleId = 1235L; repository.add(aUserMembership().forUser(JOHN_ID).memberOf(GROUP_FOR_JOHN_ID, roleId).build()); repository.add(aUserMembership().forUser(BOB_ID).memberOf(GROUP_FOR_BOB_ID, roleId).build()); final SActor actorForJohn = repository.add(anActor().build()); repository.add(anActorMember().forActor(actorForJohn).withGroupId(GROUP_FOR_JOHN_ID).build()); final SActor actorForBob = repository.add(anActor().build()); repository.add(anActorMember().forActor(actorForBob).withGroupId(GROUP_FOR_BOB_ID).build()); buildAndAddTasksWithPendingMappingForActor(actorForJohn, actorForBob); } private void buildAndAddTasksWithPendingMappingForActor(final SActor actorForJohn, final SActor actorForBob) { // Tasks OK not assigned & pending for John final SFlowNodeInstance normalTask1 = repository .add(aUserTask().withName("normalTask1").withStateExecuting(false).withStable(true).withTerminal(false) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).withId(NORMAL_HUMAN_INSTANCE_ID).build()); repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(normalTask1.getId()) .build()); // Tasks KO not assigned & pending for john, and OK not assigned & not pending final SFlowNodeInstance executingTask = buildAndAddExecutingTask(); repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(executingTask.getId()) .build()); final SFlowNodeInstance notStableTask = buildAndAddNotStableTask(); repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(notStableTask.getId()) .build()); final SFlowNodeInstance terminalTask = buildAndAddTerminalTask(); repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(terminalTask.getId()) .build()); buildAndAddNormalTask("normalTask4", ROOT_PROCESS_INSTANCE_ID); // Tasks OK not assigned & pending for Bob final SFlowNodeInstance normalTask4 = buildAndAddNormalTask("normalTask2", ROOT_PROCESS_INSTANCE_ID); repository.add( aPendingActivityMapping().withActorId(actorForBob.getId()).withActivityId(normalTask4.getId()).build()); } private SFlowNodeInstance buildAndAddNormalTask(final String taskName, final long rootProcessInstanceId) { return repository .add(aUserTask().withName(taskName).withStateExecuting(false).withStable(true).withTerminal(false) .withRootProcessInstanceId(rootProcessInstanceId).build()); } private SFlowNodeInstance buildAndAddExecutingTask() { return repository.add(aUserTask().withName("executingTask").withStateExecuting(true).withStable(true) .withTerminal(false).withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).build()); } private SFlowNodeInstance buildAndAddNotStableTask() { return repository.add(aUserTask().withName("notStableTask").withStateExecuting(false).withStable(false) .withTerminal(true).withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).build()); } private SFlowNodeInstance buildAndAddTerminalTask() { return repository.add(aUserTask().withName("terminalTask").withStateExecuting(false).withStable(true) .withTerminal(true).withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID).build()); } @Test public void should_save_and_get_SAutomaticTaskInstance() { SFlowNodeInstance entity = new SAutomaticTaskInstance(); SFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getSFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("auto"); } @Test public void should_save_and_get_SCallActivityInstance() { SFlowNodeInstance entity = new SCallActivityInstance(); SFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getSFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("call"); } @Test public void should_save_and_get_SGatewayInstance() { SFlowNodeInstance entity = new SGatewayInstance(); SFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getSFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("gate"); } @Test public void should_save_and_get_SLoopActivityInstance() { SFlowNodeInstance entity = new SLoopActivityInstance(); SFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getSFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("loop"); } @Test public void should_save_and_get_SManualTaskInstance() { SFlowNodeInstance entity = new SManualTaskInstance(); SFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getSFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("manual"); } @Test public void should_save_and_get_SMultiInstanceActivityInstance() { SFlowNodeInstance entity = new SMultiInstanceActivityInstance(); SFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getSFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("multi"); } @Test public void should_save_and_get_SReceiveTaskInstance() { SFlowNodeInstance entity = new SReceiveTaskInstance(); SFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getSFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("receive"); } @Test public void should_save_and_get_SSendTaskInstance() { SFlowNodeInstance entity = new SSendTaskInstance(); SFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getSFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("send"); } @Test public void should_save_and_get_SSubProcessActivityInstance() { SFlowNodeInstance entity = new SSubProcessActivityInstance(); SFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getSFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("subProc"); } @Test public void should_save_and_get_SUserTaskInstance() { SFlowNodeInstance entity = new SUserTaskInstance(); SFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getSFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("user"); } @Test public void should_save_and_get_SBoundaryEventInstance() { SFlowNodeInstance entity = new SBoundaryEventInstance(); SFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getSFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("boundaryEvent"); } @Test public void should_save_and_get_SEndEventInstance() { SFlowNodeInstance entity = new SEndEventInstance(); SFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getSFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("endEvent"); } @Test public void should_save_and_get_SIntermediateCatchEventInstance() { SFlowNodeInstance entity = new SIntermediateCatchEventInstance(); SFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getSFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("intermediateCatchEvent"); } @Test public void should_save_and_get_SIntermediateThrowEventInstance() { SFlowNodeInstance entity = new SIntermediateThrowEventInstance(); SFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getSFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("intermediateThrowEvent"); } @Test public void should_save_and_get_SStartEventInstance() { SFlowNodeInstance entity = new SStartEventInstance(); SFlowNodeInstance flowNode = repository.add(entity); repository.flush(); PersistentObject flowNodeFromQuery = repository.selectOne("getSFlowNodeInstanceById", pair("id", flowNode.getId())); Map flowNodeAsMap = jdbcTemplate .queryForMap("SELECT * FROM flownode_instance where id = " + flowNode.getId()); assertThat(flowNodeFromQuery).isEqualTo(flowNode); assertThat(flowNodeAsMap.get("KIND")).isEqualTo("startEvent"); } @Test public void should_save_and_get_STimerEventTriggerInstance() { STimerEventTriggerInstance entity = new STimerEventTriggerInstance(); entity.setEventInstanceName("eventInstanceName"); STimerEventTriggerInstance trigger = repository.add(entity); repository.flush(); PersistentObject triggerFromQuery = repository.selectOne("getEventTriggerInstanceById", pair("id", trigger.getId())); Map triggerAsMap = jdbcTemplate .queryForMap("SELECT * FROM event_trigger_instance where id = " + trigger.getId()); assertThat(triggerFromQuery).isEqualTo(trigger); assertThat(triggerAsMap.get("EVENTINSTANCENAME")).isEqualTo("eventInstanceName"); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/ProcessDefinitionQueriesTest.java ================================================ /** * 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.core.process.instance.model; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.test.persistence.builder.ActorBuilder.anActor; import static org.bonitasoft.engine.test.persistence.builder.ActorMemberBuilder.anActorMember; import static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser; import static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership; import java.util.List; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.test.persistence.repository.ProcessInstanceRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class ProcessDefinitionQueriesTest { private static final long PROCESS_ID = 45354312L; private static final long ROLE_ID = 222222L; @Autowired private ProcessInstanceRepository repository; /* * Tests for queries: * getNumberOfSUserWhoCanStartProcess * searchSUserWhoCanStartProcess */ @Test public void searchSUserWhoCanStartProcess_should_return_users_having_the_right_user_membership() { long G1 = 333331L; long G2 = 333332L; long G3 = 333333L; long G4 = 333334L; long R2 = 222225L; long R3 = 222226L; long R4 = 222227L; final SUser john = repository.add(aUser().withUserName("john").withId(1L).build()); final SUser paul = repository.add(aUser().withUserName("paul").withId(2L).build()); final SUser walter = repository.add(aUser().withUserName("walter").withId(3L).build()); final SUser marie = repository.add(aUser().withUserName("marie").withId(4L).build()); final SUser helen = repository.add(aUser().withUserName("helen").withId(5L).build()); final SUser jobs = repository.add(aUser().withUserName("jobs").withId(6L).build()); final SActor actor = repository.add(anActor().withScopeId(PROCESS_ID).whoIsInitiator().build()); repository.add(anActorMember().forActor(actor).withUserId(helen.getId()).build()); repository.add(anActorMember().forActor(actor).withGroupId(G1).withRoleId(ROLE_ID).build()); repository.add(anActorMember().forActor(actor).withRoleId(R3).build()); repository.add(anActorMember().forActor(actor).withGroupId(G3).build()); repository.add(aUserMembership().forUser(john).memberOf(G1, ROLE_ID).build()); repository.add(aUserMembership().forUser(paul).memberOf(G1, ROLE_ID).build()); repository.add(aUserMembership().forUser(walter).memberOf(G2, R3).build()); repository.add(aUserMembership().forUser(marie).memberOf(G3, R2).build()); repository.add(aUserMembership().forUser(jobs).memberOf(G4, R4).build()); // John is mapped through membership, but also through direct mapping. // He should only be returned once: repository.add(anActorMember().forActor(actor).withUserId(john.getId()).build()); final List users = repository.searchSUserWhoCanStartProcess(PROCESS_ID); assertThat(users).hasSize(5).containsOnly(john, paul, walter, marie, helen); long numberOfSUserWhoCanStartProcess = repository.getNumberOfSUserWhoCanStartProcess(PROCESS_ID); assertThat(numberOfSUserWhoCanStartProcess).isEqualTo(5); } @Test public void searchSUserWhoCanStartProcess_should_return_users_mapped_on_initiator_actor_of_the_process() { SUser john = repository.add(aUser().withUserName("john").withId(1L).build()); repository.add(aUser().withUserName("paul").withId(2L).build()); SActor actor = repository.add(anActor().withScopeId(PROCESS_ID).whoIsInitiator().build()); repository.add(anActorMember().forActor(actor).withUserId(john.getId()).build()); List users = repository.searchSUserWhoCanStartProcess(PROCESS_ID); assertThat(users).hasSize(1).contains(john); long numberOfSUserWhoCanStartProcess = repository.getNumberOfSUserWhoCanStartProcess(PROCESS_ID); assertThat(numberOfSUserWhoCanStartProcess).isEqualTo(1); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/ProcessDeploymentInfoQueriesTest.java ================================================ /** * 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.core.process.instance.model; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.commons.Pair.pair; import static org.bonitasoft.engine.test.persistence.builder.ActorBuilder.anActor; import static org.bonitasoft.engine.test.persistence.builder.ActorMemberBuilder.anActorMember; import static org.bonitasoft.engine.test.persistence.builder.PendingActivityMappingBuilder.aPendingActivityMapping; import static org.bonitasoft.engine.test.persistence.builder.ProcessInstanceBuilder.aProcessInstance; import static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser; import static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership; import static org.bonitasoft.engine.test.persistence.builder.UserTaskInstanceBuilder.aUserTask; import java.util.List; import java.util.Map; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDesignContent; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; import org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper; import org.bonitasoft.engine.test.persistence.repository.ProcessDeploymentInfoRepository; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class ProcessDeploymentInfoQueriesTest { private static final long GROUP_FOR_BOB_ID = 74L; private static final long GROUP_FOR_JOHN_ID = 9875L; private static final long GROUP_FOR_SUPERVISOR_FOR_BOB_ID = 369L; private static final long GROUP_FOR_SUPERVISOR_FOR_JOHN_ID = 7453L; private static final long JOHN_ID = 654L; private static final long BOB_ID = 697L; private static final long PAUL_ID = 94L; private static final long JACK_ID = 63L; private static final long PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN = 111L; private static final long PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS = 112L; private static final long PROCESS_DEFINITION_ID_SUPERVISED_BY_BOB = 113L; private static final long PROCESS_DEFINITION_ID_NOT_SUPERVISED = 114L; private static final long ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN = 963L; private static final long ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS = 1234L; private static final long ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_BOB = 1258L; private static final long ROOT_PROCESS_INSTANCE_ID_NOT_SUPERVISED = 1269L; @Autowired private ProcessDeploymentInfoRepository repository; @Autowired private JdbcTemplate jdbcTemplate; @Before public void before() { repository.add(aUser().withId(JOHN_ID).build()); repository.add(aUser().withId(BOB_ID).build()); repository.add(aUser().withId(PAUL_ID).build()); repository.add(aUser().withId(JACK_ID).build()); // Create process supervised by john final String processNameSupervisedByJohn = "Process supervised by John"; buildAndCreateProcessDefinition(6L, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN, processNameSupervisedByJohn); repository.add(aProcessInstance().withProcessDefinitionId(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN) .withName(processNameSupervisedByJohn) .withId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN).build()); // Create process supervised by john with only KO taks final String processNameSupervisedByJohnWithOnlyKOTasks = "Process supervised by John with only KO taks"; buildAndCreateProcessDefinition(8L, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS, processNameSupervisedByJohnWithOnlyKOTasks); repository.add( aProcessInstance().withProcessDefinitionId(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS) .withName(processNameSupervisedByJohnWithOnlyKOTasks) .withId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS).build()); // Create process supervised by bob final String processNameSupervisedByBob = "Process supervised by Bob"; buildAndCreateProcessDefinition(10L, PROCESS_DEFINITION_ID_SUPERVISED_BY_BOB, processNameSupervisedByBob); repository.add(aProcessInstance().withProcessDefinitionId(PROCESS_DEFINITION_ID_SUPERVISED_BY_BOB) .withName(processNameSupervisedByBob) .withId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_BOB).build()); // Create not supervised process final String processNameNotSupervised = "Process not supervised"; buildAndCreateProcessDefinition(12L, PROCESS_DEFINITION_ID_NOT_SUPERVISED, processNameNotSupervised); repository.add(aProcessInstance().withProcessDefinitionId(PROCESS_DEFINITION_ID_NOT_SUPERVISED) .withName(processNameNotSupervised) .withId(ROOT_PROCESS_INSTANCE_ID_NOT_SUPERVISED).build()); } protected void buildAndCreateProcessDefinition(final long id, long processDefinitionId, final String processName) { final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = new SProcessDefinitionDeployInfo(); sProcessDefinitionDeployInfo.setId(id); sProcessDefinitionDeployInfo.setName(processName); sProcessDefinitionDeployInfo.setVersion("version"); sProcessDefinitionDeployInfo.setProcessId(processDefinitionId); repository.add(sProcessDefinitionDeployInfo); } // For @Test public void getNumberOfProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_assigned_tasks_to_the_user() { // Given buildAndAddAssignedTasks(); // When final long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1); } @Test public void getNumberOfProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_the_user() { // Given buildAndAddTasksWithPendingMappingForUser(); // When long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1); } @Test public void getNumberOfProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_the_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUser(); // When long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1); } @Test public void getNumberOfProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_the_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser(); // When long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1); } @Test public void searchProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_assigned_tasks_to_the_user() { // Given buildAndAddAssignedTasks(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } @Test public void searchProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_the_user() { // Given buildAndAddTasksWithPendingMappingForUser(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } @Test public void searchProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_the_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUser(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } @Test public void searchProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor_should_return_number_of_process_definition_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_the_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(JACK_ID); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } // Supervised By @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_assigned_tasks() { // Given buildAndAddAssignedTasks(); buildAndAddSupervisorMappedToUser(); // When final long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1); } @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_assigned_tasks() { // Given buildAndAddAssignedTasks(); buildAndAddSupervisorMappedToUserMembershipMappedToUser(); // When final long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1); } @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_pending_tasks_for_a_user() { // Given buildAndAddTasksWithPendingMappingForUser(); buildAndAddSupervisorMappedToUser(); // When long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1); } @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_pending_tasks_for_a_user() { // Given buildAndAddTasksWithPendingMappingForUser(); buildAndAddSupervisorMappedToUserMembershipMappedToUser(); // When long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1); } @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUser(); buildAndAddSupervisorMappedToUser(); // When long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1); } @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUser(); buildAndAddSupervisorMappedToUserMembershipMappedToUser(); // When long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1); } @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser(); buildAndAddSupervisorMappedToUser(); // When long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1); } @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser(); buildAndAddSupervisorMappedToUserMembershipMappedToUser(); // When long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_assigned_tasks() { // Given buildAndAddAssignedTasks(); buildAndAddSupervisorMappedToUser(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_assigned_tasks() { // Given buildAndAddAssignedTasks(); buildAndAddSupervisorMappedToUserMembershipMappedToUser(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_pending_tasks_for_a_user() { // Given buildAndAddTasksWithPendingMappingForUser(); buildAndAddSupervisorMappedToUser(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_pending_tasks_for_a_user() { // Given buildAndAddTasksWithPendingMappingForUser(); buildAndAddSupervisorMappedToUserMembershipMappedToUser(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUser(); buildAndAddSupervisorMappedToUser(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUser(); buildAndAddSupervisorMappedToUserMembershipMappedToUser(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_user_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser(); buildAndAddSupervisorMappedToUser(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy_should_return_number_of_process_definition_supervised_by_userMembership_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser(); buildAndAddSupervisorMappedToUserMembershipMappedToUser(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(JOHN_ID); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } // All (for admin) @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_assigned_tasks() { // Given buildAndAddAssignedTasks(); buildAndAddSupervisorMappedToUser(); // When final long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(4); } @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_user() { // Given buildAndAddTasksWithPendingMappingForUser(); buildAndAddSupervisorMappedToUser(); // When long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(4); } @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUser(); buildAndAddSupervisorMappedToUser(); // When long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1); } @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser(); buildAndAddSupervisorMappedToUser(); // When long numberOfProcessDefinitionDeployInfos = repository .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(); // Then assertThat(numberOfProcessDefinitionDeployInfos).isEqualTo(1); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_assigned_tasks() { // Given buildAndAddAssignedTasks(); buildAndAddSupervisorMappedToUser(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(4); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_user() { // Given buildAndAddTasksWithPendingMappingForUser(); buildAndAddSupervisorMappedToUser(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(4); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUser(); buildAndAddSupervisorMappedToUser(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks_should_return_number_of_process_definition_supervised_if_one_instance_has_pending_tasks_for_a_actor_with_a_membership_mapped_to_a_user_as_member() { // Given buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser(); buildAndAddSupervisorMappedToUser(); // When final List sProcessDefinitionDeployInfos = repository .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(); // Then assertThat(sProcessDefinitionDeployInfos.size()).isEqualTo(1); assertThat(sProcessDefinitionDeployInfos.get(0).getProcessId()) .isEqualTo(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); } private void buildAndAddSupervisorMappedToUser() { repository.add(new SProcessSupervisor(3, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN, JOHN_ID, -1, -1)); repository.add(new SProcessSupervisor(4, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS, JOHN_ID, -1, -1)); repository.add(new SProcessSupervisor(5, PROCESS_DEFINITION_ID_SUPERVISED_BY_BOB, BOB_ID, -1, -1)); } private void buildAndAddSupervisorMappedToUserMembershipMappedToUser() { final long roleId = 1295L; repository.add(aUserMembership().forUser(JOHN_ID).memberOf(GROUP_FOR_SUPERVISOR_FOR_JOHN_ID, roleId).build()); repository.add(aUserMembership().forUser(BOB_ID).memberOf(GROUP_FOR_SUPERVISOR_FOR_BOB_ID, roleId).build()); repository.add(new SProcessSupervisor(3, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN, -1, GROUP_FOR_SUPERVISOR_FOR_JOHN_ID, -1)); repository.add(new SProcessSupervisor(4, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS, -1, GROUP_FOR_SUPERVISOR_FOR_JOHN_ID, -1)); repository.add(new SProcessSupervisor(5, PROCESS_DEFINITION_ID_SUPERVISED_BY_BOB, -1, GROUP_FOR_SUPERVISOR_FOR_BOB_ID, -1)); } private void buildAndAddAssignedTasks() { // Tasks OK assigned to John repository .add(aUserTask().withName("normalTask1").withStateExecuting(false).withStable(true).withTerminal(false) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN).withAssigneeId(JACK_ID) .withStateId(4).withProcessDefinition(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN).build()); repository .add(aUserTask().withName("normalTask2").withStateExecuting(false).withStable(true).withTerminal(false) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN).withAssigneeId(JACK_ID) .withStateId(4).withProcessDefinition(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN).build()); repository .add(aUserTask().withName("normalTask3").withStateExecuting(false).withStable(true).withTerminal(false) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN).withAssigneeId(JACK_ID) .withStateId(4).withProcessDefinition(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN).build()); // Tasks KO assigned to john & OK not assigned repository .add(aUserTask().withName("executingTask").withStateExecuting(true).withStable(true).withTerminal(false) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS) .withAssigneeId(JACK_ID) .withProcessDefinition(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS).build()); repository.add( aUserTask().withName("notStableTask").withStateExecuting(false).withStable(false).withTerminal(true) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS) .withAssigneeId(JACK_ID) .withProcessDefinition(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS).build()); repository.add( aUserTask().withName("notStableTask").withStateExecuting(false).withStable(false).withTerminal(true) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS) .withAssigneeId(JACK_ID).withStateId(4) .withProcessDefinition(PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS).build()); repository .add(aUserTask().withName("terminalTask").withStateExecuting(false).withStable(true).withTerminal(true) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS) .withAssigneeId(JACK_ID).build()); buildAndAddNormalTask("normalTask1", ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS); // Tasks OK assigned to Bob repository.add(aUserTask().withName("normalTask1").withStateExecuting(false).withStable(true) .withTerminal(false) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_BOB).withStateId(4) .withProcessDefinition(PROCESS_DEFINITION_ID_SUPERVISED_BY_BOB).withAssigneeId(PAUL_ID).build()); // Tasks OK assigned to Bob, process not supervised repository .add(aUserTask().withName("normalTask1").withStateExecuting(false).withStable(true).withTerminal(false) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_NOT_SUPERVISED).withStateId(4) .withProcessDefinition(PROCESS_DEFINITION_ID_NOT_SUPERVISED).withAssigneeId(PAUL_ID).build()); } private void buildAndAddTasksWithPendingMappingForUser() { // Tasks OK not assigned & pending for John final SFlowNodeInstance normalTask1 = buildAndAddNormalTask("normalTask1", ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); repository.add(aPendingActivityMapping().withUserId(JACK_ID).withActivityId(normalTask1.getId()).build()); final SFlowNodeInstance normalTask2 = buildAndAddNormalTask("normalTask2", ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); repository.add(aPendingActivityMapping().withUserId(JACK_ID).withActivityId(normalTask2.getId()).build()); final SFlowNodeInstance normalTask3 = buildAndAddNormalTask("normalTask3", ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); repository.add(aPendingActivityMapping().withUserId(JACK_ID).withActivityId(normalTask3.getId()).build()); // Tasks KO not assigned & pending for john, and OK not assigned & not pending final SFlowNodeInstance executingTask = buildAndAddExecutingTask(); repository.add(aPendingActivityMapping().withUserId(JACK_ID).withActivityId(executingTask.getId()).build()); final SFlowNodeInstance notStableTask = buildAndAddNotStableTask(); repository.add(aPendingActivityMapping().withUserId(JACK_ID).withActivityId(notStableTask.getId()).build()); final SFlowNodeInstance terminalTask = buildAndAddTerminalTask(); repository.add(aPendingActivityMapping().withUserId(JACK_ID).withActivityId(terminalTask.getId()).build()); buildAndAddNormalTask("normalTask1", ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS); // Tasks OK not assigned & pending for Bob final SFlowNodeInstance normalTask4 = buildAndAddNormalTask("normalTask1", ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_BOB, PROCESS_DEFINITION_ID_SUPERVISED_BY_BOB); repository.add(aPendingActivityMapping().withUserId(PAUL_ID).withActivityId(normalTask4.getId()).build()); // Tasks OK not assigned & pending for Bob, process not supervised final SFlowNodeInstance normalTask5 = buildAndAddNormalTask("normalTask1", ROOT_PROCESS_INSTANCE_ID_NOT_SUPERVISED, PROCESS_DEFINITION_ID_NOT_SUPERVISED); repository.add(aPendingActivityMapping().withUserId(PAUL_ID).withActivityId(normalTask5.getId()).build()); } private void buildAndAddTasksWithPendingMappingForActorMappedToUser() { final SActor actorForJohn = repository.add(anActor().build()); repository.add(anActorMember().forActor(actorForJohn).withUserId(JACK_ID).build()); final SActor actorForBob = repository.add(anActor().build()); repository.add(anActorMember().forActor(actorForBob).withUserId(PAUL_ID).build()); buildAndAddTasksWithPendingMappingForActor(actorForJohn, actorForBob); } private void buildAndAddTasksWithPendingMappingForActorMappedToUserMembershipMappedToUser() { final long roleId = 1235L; repository.add(aUserMembership().forUser(JACK_ID).memberOf(GROUP_FOR_JOHN_ID, roleId).build()); repository.add(aUserMembership().forUser(PAUL_ID).memberOf(GROUP_FOR_BOB_ID, roleId).build()); final SActor actorForJohn = repository.add(anActor().build()); repository.add(anActorMember().forActor(actorForJohn).withGroupId(GROUP_FOR_JOHN_ID).build()); final SActor actorForBob = repository.add(anActor().build()); repository.add(anActorMember().forActor(actorForBob).withGroupId(GROUP_FOR_BOB_ID).build()); buildAndAddTasksWithPendingMappingForActor(actorForJohn, actorForBob); } private void buildAndAddTasksWithPendingMappingForActor(final SActor actorForJohn, final SActor actorForBob) { // Tasks OK not assigned & pending for John final SFlowNodeInstance normalTask1 = buildAndAddNormalTask("normalTask1", ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(normalTask1.getId()) .build()); final SFlowNodeInstance normalTask2 = buildAndAddNormalTask("normalTask2", ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(normalTask2.getId()) .build()); final SFlowNodeInstance normalTask3 = buildAndAddNormalTask("normalTask3", ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(normalTask3.getId()) .build()); // Tasks KO not assigned & pending for john, and OK not assigned & not pending final SFlowNodeInstance executingTask = buildAndAddExecutingTask(); repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(executingTask.getId()) .build()); final SFlowNodeInstance notStableTask = buildAndAddNotStableTask(); repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(notStableTask.getId()) .build()); final SFlowNodeInstance terminalTask = buildAndAddTerminalTask(); repository.add(aPendingActivityMapping().withActorId(actorForJohn.getId()).withActivityId(terminalTask.getId()) .build()); buildAndAddNormalTask("normalTask1", ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); // Tasks OK not assigned & pending for Bob final SFlowNodeInstance normalTask4 = buildAndAddNormalTask("normalTask1", ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_BOB, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); repository.add( aPendingActivityMapping().withActorId(actorForBob.getId()).withActivityId(normalTask4.getId()).build()); // Tasks OK not assigned & pending for Bob, process not supervised final SFlowNodeInstance normalTask5 = buildAndAddNormalTask("normalTask1", ROOT_PROCESS_INSTANCE_ID_NOT_SUPERVISED, PROCESS_DEFINITION_ID_SUPERVISED_BY_JOHN); repository.add( aPendingActivityMapping().withActorId(actorForBob.getId()).withActivityId(normalTask5.getId()).build()); } private SFlowNodeInstance buildAndAddNormalTask(final String taskName, final long rootProcessInstanceId, long processDefinitionId) { return repository .add(aUserTask().withName(taskName).withStateExecuting(false).withStable(true).withTerminal(false) .withStateId(4).withRootProcessInstanceId(rootProcessInstanceId) .withProcessDefinition(processDefinitionId).build()); } private SFlowNodeInstance buildAndAddExecutingTask() { return repository.add(aUserTask().withName("executingTask").withStateExecuting(true).withStable(true) .withTerminal(false) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS).build()); } private SFlowNodeInstance buildAndAddNotStableTask() { return repository.add(aUserTask().withName("notStableTask").withStateExecuting(false).withStable(false) .withTerminal(true) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS).build()); } private SFlowNodeInstance buildAndAddTerminalTask() { return repository.add(aUserTask().withName("terminalTask").withStateExecuting(false).withStable(true) .withTerminal(true) .withRootProcessInstanceId(ROOT_PROCESS_INSTANCE_ID_SUPERVISED_BY_JOHN_WITH_ONLY_KO_TASKS).build()); } protected void buildAndCreateProcessDefinition(final long id, long processDefinitionId, final String processName, String processVersion, String activationState) { final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = new SProcessDefinitionDeployInfo(); sProcessDefinitionDeployInfo.setId(id); sProcessDefinitionDeployInfo.setName(processName); sProcessDefinitionDeployInfo.setVersion(processVersion); sProcessDefinitionDeployInfo.setProcessId(processDefinitionId); sProcessDefinitionDeployInfo.setActivationState(activationState); repository.add(sProcessDefinitionDeployInfo); } @Test public void getProcessDefinitionDeployInfosByName_should_not_retrieve_disabled_processes() { // given: buildAndCreateProcessDefinition(1L, 44657531564675L, "ChildProcess", "1.0", "DISABLED"); // when: final List processes = repository .getProcessDefinitionDeployInfosByName("ChildProcess"); // then: assertThat(processes).hasSize(0); } @Test public void getProcessDefinitionDeployInfosByName_should_retrieve_an_enabled_process_even_if_not_most_recent() throws Exception { // given: buildAndCreateProcessDefinition(2L, 4465753156777L, "ChildProcess", "1.0", "ENABLED"); Thread.sleep(20L); buildAndCreateProcessDefinition(3L, 4465753158885L, "ChildProcess", "2.0", "DISABLED"); // when: final List processes = repository .getProcessDefinitionDeployInfosByName("ChildProcess"); // then: assertThat(processes).hasSize(1); assertThat(processes.get(0).getVersion()).isEqualTo("1.0"); } @Test public void should_save_and_retrieve_process_definition_content() { SProcessDefinitionDesignContent content = repository .add(SProcessDefinitionDesignContent.builder().content("content").build()); SProcessDefinitionDeployInfo processDefinition = repository.add(SProcessDefinitionDeployInfo.builder() .name("MyProcessWithContent") .processId(123456L) .version("1.12") .description("a normal process") .displayName("My process with content") .displayDescription("A normal process display description") .configurationState("OK") .activationState("ACTIVATED") .designContent(content).build()); PersistentObject persistentObject = repository.selectOne("getDeployInfoByProcessDefId", pair("processId", 123456L)); assertThat(persistentObject).isEqualTo(processDefinition); assertThat(((SProcessDefinitionDeployInfo) persistentObject).getDesignContent().getContent()) .isEqualTo("content"); } @Test public void should_reference_design_content_using_id() { SProcessDefinitionDesignContent content = repository .add(SProcessDefinitionDesignContent.builder().content("content").build()); repository.add(SProcessDefinitionDeployInfo.builder() .name("MyProcessWithContent") .processId(123456L) .version("1.12") .description("a normal process") .displayName("My process with content") .displayDescription("A normal process display description") .configurationState("OK") .activationState("ACTIVATED") .designContent(content).build()); repository.flush(); Map processDefinitionMap = jdbcTemplate.queryForObject( "SELECT name, CONTENT_ID FROM process_definition WHERE processId=123456", new JdbcRowMapper("CONTENT_ID")); Map processContentMap = jdbcTemplate.queryForObject("SELECT ID FROM process_content", new JdbcRowMapper("ID")); assertThat(processDefinitionMap.get("NAME")).isEqualTo("MyProcessWithContent"); assertThat(processDefinitionMap.get("CONTENT_ID")).isEqualTo(content.getId()); assertThat(processContentMap.get("ID")).isEqualTo(content.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/ProcessInstanceQueriesTest.java ================================================ /** * 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.core.process.instance.model; import static java.time.Instant.now; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.bonitasoft.engine.bpm.process.ProcessInstanceState.*; import static org.bonitasoft.engine.commons.Pair.mapOf; import static org.bonitasoft.engine.commons.Pair.pair; import static org.bonitasoft.engine.core.process.definition.model.SFlowNodeType.RECEIVE_TASK; import static org.bonitasoft.engine.test.persistence.builder.ActorBuilder.anActor; import static org.bonitasoft.engine.test.persistence.builder.ActorMemberBuilder.anActorMember; import static org.bonitasoft.engine.test.persistence.builder.CallActivityInstanceBuilder.aCallActivityInstanceBuilder; import static org.bonitasoft.engine.test.persistence.builder.GatewayInstanceBuilder.aGatewayInstanceBuilder; import static org.bonitasoft.engine.test.persistence.builder.PendingActivityMappingBuilder.aPendingActivityMapping; import static org.bonitasoft.engine.test.persistence.builder.ProcessInstanceBuilder.aProcessInstance; import static org.bonitasoft.engine.test.persistence.builder.SupervisorBuilder.aSupervisor; import static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser; import static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership; import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.List; import java.util.Map; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper; import org.bonitasoft.engine.test.persistence.repository.ProcessInstanceRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class ProcessInstanceQueriesTest { private static final long aGroupId = 654L; private static final long anotherGroupId = 9875L; private static final long aRoleId = 1235L; private static final long anotherRoleId = 956L; private static final long PROCESS_INSTANCE_ID = 43578923425L; private static final long FLOW_NODE_INSTANCE_ID = 342678L; @Autowired private ProcessInstanceRepository repository; @Autowired private JdbcTemplate jdbcTemplate; @Test public void getPossibleUserIdsOfPendingTasks_should_return_users_mapped_through_user_filters() { final SUser expectedUser = repository.add(aUser().withId(1L).build()); repository.add(aUser().withId(2L).build()); // not expected user final SPendingActivityMapping pendingActivity = repository .add(aPendingActivityMapping().withUserId(expectedUser.getId()).build()); final List userIds = repository.getPossibleUserIdsOfPendingTasks(pendingActivity.getActivityId()); assertThat(userIds).containsOnly(expectedUser.getId()); } @Test public void isTaskPendingForUser_should_be_true_when_mapped_using_pending_mapping() { final SUser expectedUser = repository.add(aUser().withId(1L).build()); repository.add(aUser().withId(2L).build()); // not expected user final SPendingActivityMapping pendingActivity = repository .add(aPendingActivityMapping().withUserId(expectedUser.getId()).build()); boolean taskPendingForUser = repository.isTaskPendingForUser(pendingActivity.getActivityId(), expectedUser.getId()); assertThat(taskPendingForUser).isTrue(); } @Test public void isTaskPendingForUser_should_be_true_when_mapped_using_actor() { final SUser expectedUser = repository.add(aUser().build()); SActor actor = repository.add(anActor().build()); SActorMember actorMember = repository .add(anActorMember().withUserId(expectedUser.getId()).forActor(actor).build()); final SPendingActivityMapping pendingActivity = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); boolean taskPendingForUser = repository.isTaskPendingForUser(pendingActivity.getActivityId(), expectedUser.getId()); assertThat(taskPendingForUser).isTrue(); } @Test public void isTaskPendingForUser_should_be_true_when_mapped_using_actor_having_role() { final SUser expectedUser = repository.add(aUser().build()); repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build()); SActor actor = repository.add(anActor().build()); repository.add(anActorMember().withRoleId(aRoleId).forActor(actor).build()); final SPendingActivityMapping pendingActivity = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); boolean taskPendingForUser = repository.isTaskPendingForUser(pendingActivity.getActivityId(), expectedUser.getId()); assertThat(taskPendingForUser).isTrue(); } @Test public void isTaskPendingForUser_should_be_true_when_mapped_using_actor_having_group() { final SUser expectedUser = repository.add(aUser().build()); repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build()); SActor actor = repository.add(anActor().build()); repository.add(anActorMember().withGroupId(aGroupId).forActor(actor).build()); final SPendingActivityMapping pendingActivity = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); boolean taskPendingForUser = repository.isTaskPendingForUser(pendingActivity.getActivityId(), expectedUser.getId()); assertThat(taskPendingForUser).isTrue(); } @Test public void isTaskPendingForUser_should_be_true_when_mapped_using_actor_having_membership() { final SUser expectedUser = repository.add(aUser().build()); repository.add(aUserMembership().forUser(expectedUser).memberOf(anotherGroupId, anotherRoleId).build()); SActor actor = repository.add(anActor().build()); repository.add(anActorMember().withGroupId(anotherGroupId).withRoleId(anotherRoleId).forActor(actor).build()); final SPendingActivityMapping pendingActivity = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); boolean taskPendingForUser = repository.isTaskPendingForUser(pendingActivity.getActivityId(), expectedUser.getId()); assertThat(taskPendingForUser).isTrue(); } @Test public void isTaskPendingForUser_should_be_false_when_not_pending() { final SUser expectedUser = repository.add(aUser().withId(1L).build()); SUser notPendingUser = repository.add(aUser().withId(2L).build());// not expected user final SPendingActivityMapping pendingActivity = repository .add(aPendingActivityMapping().withUserId(expectedUser.getId()).build()); boolean taskPendingForUser = repository.isTaskPendingForUser(pendingActivity.getActivityId(), notPendingUser.getId()); assertThat(taskPendingForUser).isFalse(); } @Test public void isTaskPendingForUser() { final SUser expectedUser = repository.add(aUser().withId(1L).build()); repository.add(aUser().withId(2L).build()); // not expected user final SPendingActivityMapping pendingActivity = repository .add(aPendingActivityMapping().withUserId(expectedUser.getId()).build()); boolean taskPendingForUser = repository.isTaskPendingForUser(pendingActivity.getActivityId(), expectedUser.getId()); assertThat(taskPendingForUser).isTrue(); } @Test public void getPossibleUserIdsOfPendingTasks_should_return_users_mapped_through_his_userid_in_actormember() { final SActor actor = repository.add(anActor().build()); final SPendingActivityMapping addedPendingMapping = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); final SUser expectedUser = repository.add(aUser().withId(1L).build()); repository.add(anActorMember().forActor(actor).withUserId(expectedUser.getId()).build()); repository.add(aUser().withId(2L).build()); // not expected user final List userIds = repository.getPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId()); assertThat(userIds).containsOnly(expectedUser.getId()); } @Test public void getPossibleUserIdsOfPendingTasks_should_return_users_mapped_through_his_groupid_in_actormember() { final SActor actor = repository.add(anActor().build()); final SPendingActivityMapping addedPendingMapping = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).build()); final SUser expectedUser = repository.add(aUser().withId(1L).build()); repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build()); final SUser notExpectedUser = repository.add(aUser().withId(2L).build()); repository.add(aUserMembership().forUser(notExpectedUser).memberOf(anotherGroupId, aRoleId).build()); final List userIds = repository.getPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId()); assertThat(userIds).containsOnly(expectedUser.getId()); } @Test public void getPossibleUserIdsOfPendingTasks_should_return_users_mapped_through_his_roleid_in_actormember() { final SActor actor = repository.add(anActor().build()); final SPendingActivityMapping addedPendingMapping = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); repository.add(anActorMember().forActor(actor).withRoleId(aRoleId).build()); final SUser expectedUser = repository.add(aUser().withId(1L).build()); repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build()); final SUser notexpectedUser = repository.add(aUser().withId(2L).build()); repository.add(aUserMembership().forUser(notexpectedUser).memberOf(aGroupId, anotherRoleId).build()); final List userIds = repository.getPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId()); assertThat(userIds).containsOnly(expectedUser.getId()); } @Test public void getPossibleUserIdsOfPendingTasks_should_return_users_mapped_through_his_membership_in_actormember() { final SUser expectedUser = repository.add(aUser().withId(1L).build()); final SUser expectedUser2 = repository.add(aUser().withId(4L).build()); final SUser notExpectedUser = repository.add(aUser().withId(2L).build()); final SUser notExpectedUser2 = repository.add(aUser().withId(3L).build()); final SActor actor = repository.add(anActor().build()); final SPendingActivityMapping addedPendingMapping = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build()); repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(expectedUser2).memberOf(aGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(notExpectedUser).memberOf(anotherGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(notExpectedUser2).memberOf(aGroupId, anotherRoleId).build()); final List userIds = repository.getPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId()); assertThat(userIds).containsOnly(expectedUser.getId(), expectedUser2.getId()); } @Test public void getPossibleUserIdsOfPendingTasks_return_userIds_ordered_by_userName() { final SUser john = repository.add(aUser().withUserName("john").withId(1L).build()); final SUser paul = repository.add(aUser().withUserName("paul").withId(2L).build()); final SUser walter = repository.add(aUser().withUserName("walter").withId(3L).build()); final SUser marie = repository.add(aUser().withUserName("marie").withId(4L).build()); final SActor actor = repository.add(anActor().build()); final SPendingActivityMapping addedPendingMapping = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build()); repository.add(aUserMembership().forUser(john).memberOf(aGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(paul).memberOf(aGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(walter).memberOf(aGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(marie).memberOf(aGroupId, aRoleId).build()); final List userIds = repository.getPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId()); assertThat(userIds).containsExactly(john.getId(), marie.getId(), paul.getId(), walter.getId()); } @Test public void getNumberOfSUserWhoCanStartPendingTask_should_return_users_mapped_through_user_filters() { final SUser expectedUser = repository.add(aUser().withId(1L).build()); repository.add(aUser().withId(2L).build()); // not expected user final SPendingActivityMapping pendingActivity = repository .add(aPendingActivityMapping().withUserId(expectedUser.getId()).build()); final long numberOfUsers = repository.getNumberOfSUserWhoCanStartPendingTask(pendingActivity.getActivityId()); assertThat(numberOfUsers).isEqualTo(1); } @Test public void getNumberOfSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_userid_in_actormember() { final SActor actor = repository.add(anActor().build()); final SPendingActivityMapping addedPendingMapping = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); final SUser expectedUser = repository.add(aUser().withId(1L).build()); repository.add(anActorMember().forActor(actor).withUserId(expectedUser.getId()).build()); repository.add(aUser().withId(2L).build()); // not expected user final long numberOfUsers = repository .getNumberOfSUserWhoCanStartPendingTask(addedPendingMapping.getActivityId()); assertThat(numberOfUsers).isEqualTo(1); } @Test public void getNumberOfSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_groupid_in_actormember() { final SActor actor = repository.add(anActor().build()); final SPendingActivityMapping addedPendingMapping = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).build()); final SUser expectedUser = repository.add(aUser().withId(1L).build()); repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build()); final SUser notExpectedUser = repository.add(aUser().withId(2L).build()); repository.add(aUserMembership().forUser(notExpectedUser).memberOf(anotherGroupId, aRoleId).build()); final long numberOfUsers = repository .getNumberOfSUserWhoCanStartPendingTask(addedPendingMapping.getActivityId()); assertThat(numberOfUsers).isEqualTo(1); } @Test public void getNumberOfSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_roleid_in_actormember() { final SActor actor = repository.add(anActor().build()); final SPendingActivityMapping addedPendingMapping = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); repository.add(anActorMember().forActor(actor).withRoleId(aRoleId).build()); final SUser expectedUser = repository.add(aUser().withId(1L).build()); repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build()); final SUser notexpectedUser = repository.add(aUser().withId(2L).build()); repository.add(aUserMembership().forUser(notexpectedUser).memberOf(aGroupId, anotherRoleId).build()); final long numberOfUsers = repository .getNumberOfSUserWhoCanStartPendingTask(addedPendingMapping.getActivityId()); assertThat(numberOfUsers).isEqualTo(1); } @Test public void getNumberOfSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_membership_in_actormember() { final SUser expectedUser = repository.add(aUser().withId(1L).build()); final SUser expectedUser2 = repository.add(aUser().withId(4L).build()); final SUser notExpectedUser = repository.add(aUser().withId(2L).build()); final SUser notExpectedUser2 = repository.add(aUser().withId(3L).build()); final SActor actor = repository.add(anActor().build()); final SPendingActivityMapping addedPendingMapping = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build()); repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(expectedUser2).memberOf(aGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(notExpectedUser).memberOf(anotherGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(notExpectedUser2).memberOf(aGroupId, anotherRoleId).build()); final long numberOfUsers = repository .getNumberOfSUserWhoCanStartPendingTask(addedPendingMapping.getActivityId()); assertThat(numberOfUsers).isEqualTo(2); } @Test public void getNumberOfSUserWhoCanStartPendingTask_return_userIds_ordered_by_userName() { final SUser john = repository.add(aUser().withUserName("john").withId(1L).build()); final SUser paul = repository.add(aUser().withUserName("paul").withId(2L).build()); final SUser walter = repository.add(aUser().withUserName("walter").withId(3L).build()); final SUser marie = repository.add(aUser().withUserName("marie").withId(4L).build()); final SActor actor = repository.add(anActor().build()); final SPendingActivityMapping addedPendingMapping = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build()); repository.add(aUserMembership().forUser(john).memberOf(aGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(paul).memberOf(aGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(walter).memberOf(aGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(marie).memberOf(aGroupId, aRoleId).build()); final long numberOfUsers = repository .getNumberOfSUserWhoCanStartPendingTask(addedPendingMapping.getActivityId()); assertThat(numberOfUsers).isEqualTo(4); } @Test public void searchSUserWhoCanStartPendingTask_should_return_users_mapped_through_user_filters() { final SUser expectedUser = repository.add(aUser().withId(1L).build()); repository.add(aUser().withId(2L).build()); // not expected user final SPendingActivityMapping pendingActivity = repository .add(aPendingActivityMapping().withUserId(expectedUser.getId()).build()); final List userIds = repository.searchPossibleUserIdsOfPendingTasks(pendingActivity.getActivityId()); assertThat(userIds).containsOnly(expectedUser); } @Test public void searchSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_userid_in_actormember() { final SActor actor = repository.add(anActor().build()); final SPendingActivityMapping addedPendingMapping = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); final SUser expectedUser = repository.add(aUser().withId(1L).build()); repository.add(anActorMember().forActor(actor).withUserId(expectedUser.getId()).build()); repository.add(aUser().withId(2L).build()); // not expected user final List userIds = repository.searchPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId()); assertThat(userIds).containsOnly(expectedUser); } @Test public void searchSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_groupid_in_actormember() { final SActor actor = repository.add(anActor().build()); final SPendingActivityMapping addedPendingMapping = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).build()); final SUser expectedUser = repository.add(aUser().withId(1L).build()); repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build()); final SUser notExpectedUser = repository.add(aUser().withId(2L).build()); repository.add(aUserMembership().forUser(notExpectedUser).memberOf(anotherGroupId, aRoleId).build()); final List userIds = repository.searchPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId()); assertThat(userIds).containsOnly(expectedUser); } @Test public void searchSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_roleid_in_actormember() { final SActor actor = repository.add(anActor().build()); final SPendingActivityMapping addedPendingMapping = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); repository.add(anActorMember().forActor(actor).withRoleId(aRoleId).build()); final SUser expectedUser = repository.add(aUser().withId(1L).build()); repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build()); final SUser notexpectedUser = repository.add(aUser().withId(2L).build()); repository.add(aUserMembership().forUser(notexpectedUser).memberOf(aGroupId, anotherRoleId).build()); final List userIds = repository.searchPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId()); assertThat(userIds).containsOnly(expectedUser); } @Test public void searchSUserWhoCanStartPendingTask_should_return_users_mapped_through_his_membership_in_actormember() { final SUser expectedUser = repository.add(aUser().withId(1L).build()); final SUser expectedUser2 = repository.add(aUser().withId(4L).build()); final SUser notExpectedUser = repository.add(aUser().withId(2L).build()); final SUser notExpectedUser2 = repository.add(aUser().withId(3L).build()); final SActor actor = repository.add(anActor().build()); final SPendingActivityMapping addedPendingMapping = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build()); repository.add(aUserMembership().forUser(expectedUser).memberOf(aGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(expectedUser2).memberOf(aGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(notExpectedUser).memberOf(anotherGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(notExpectedUser2).memberOf(aGroupId, anotherRoleId).build()); final List userIds = repository.searchPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId()); assertThat(userIds).containsOnly(expectedUser, expectedUser2); } @Test public void searchSUserWhoCanStartPendingTask_return_userIds_ordered_by_userName() { final SUser john = repository.add(aUser().withUserName("john").withId(1L).build()); final SUser paul = repository.add(aUser().withUserName("paul").withId(2L).build()); final SUser walter = repository.add(aUser().withUserName("walter").withId(3L).build()); final SUser marie = repository.add(aUser().withUserName("marie").withId(4L).build()); final SActor actor = repository.add(anActor().build()); final SPendingActivityMapping addedPendingMapping = repository .add(aPendingActivityMapping().withActorId(actor.getId()).build()); repository.add(anActorMember().forActor(actor).withGroupId(aGroupId).withRoleId(aRoleId).build()); repository.add(aUserMembership().forUser(john).memberOf(aGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(paul).memberOf(aGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(walter).memberOf(aGroupId, aRoleId).build()); repository.add(aUserMembership().forUser(marie).memberOf(aGroupId, aRoleId).build()); final List users = repository.searchPossibleUserIdsOfPendingTasks(addedPendingMapping.getActivityId()); assertThat(users).hasSize(4).contains(john, marie, paul, walter); } @Test public void searchSingleChildrenSProcessInstanceOfProcessInstance_return_processInstancesIds() { final SProcessInstance parentPI = repository .add(aProcessInstance().withContainerId(1).withName("test Parent Process Instance") .withStateId(STARTED.getId()).build()); final SCallActivityInstance callActivity = (SCallActivityInstance) repository.add(aCallActivityInstanceBuilder() .withLogicalGroup4(parentPI.getId()) .withName("call Activity") .build()); final SProcessInstance childPI = repository .add(aProcessInstance().withContainerId(1).withName("test Child Process Instance") .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY) .withStateId(STARTED.getId()).build()); assertThat(repository.countChildrenInstanceIdsOfProcessInstance(parentPI.getId())).isEqualTo(1); final List piIds = repository.getChildrenInstanceIdsOfProcessInstance(parentPI.getId()); assertThat(piIds).isNotEmpty().containsExactly(childPI.getId()); } @Test public void searchOnlyChildrenSProcessInstanceOfProcessInstance_return_processInstancesIdsFromCallActivity() { final SProcessInstance parentPI = repository .add(aProcessInstance().withContainerId(1).withName("test Parent Process Instance") .withStateId(STARTED.getId()).build()); final SCallActivityInstance callActivity = (SCallActivityInstance) repository.add(aCallActivityInstanceBuilder() .withLogicalGroup4(parentPI.getId()) .withName("call Activity") .build()); final SGatewayInstance gatewayActivity = (SGatewayInstance) repository .add(aGatewayInstanceBuilder().withLogicalGroup4(parentPI.getId()) .withName("call Activity") .build()); final SProcessInstance childPI = repository .add(aProcessInstance().withContainerId(1).withName("test Child Process Instance") .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY) .withStateId(STARTED.getId()).build()); repository.add(aProcessInstance().withContainerId(1).withName("test Parent Process Instance") .withCallerId(gatewayActivity.getId()).withCallerType(SFlowNodeType.GATEWAY) .withStateId(STARTED.getId()).build()); assertThat(repository.countChildrenInstanceIdsOfProcessInstance(parentPI.getId())).isEqualTo(1); final List piIds = repository.getChildrenInstanceIdsOfProcessInstance(parentPI.getId()); assertThat(piIds).isNotEmpty().containsExactly(childPI.getId()); } @Test public void searchChildProcessInstanceOfProcessInstance_return_processInstancesIdsNotGrandChildren() { final SProcessInstance parentPI = repository .add(aProcessInstance().withContainerId(1).withName("test Parent Process Instance") .withStateId(STARTED.getId()).build()); final SCallActivityInstance callActivity = (SCallActivityInstance) repository.add(aCallActivityInstanceBuilder() .withLogicalGroup4(parentPI.getId()) .withName("call Activity") .build()); final SProcessInstance childPI = repository .add(aProcessInstance().withContainerId(1).withName("test Child Process Instance Started") .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY) .withStateId(STARTED.getId()).build()); final SCallActivityInstance callSubActivity = (SCallActivityInstance) repository .add(aCallActivityInstanceBuilder() .withLogicalGroup4(childPI.getId()) .withName("call Activity") .build()); repository.add(aProcessInstance().withContainerId(1).withName("test Grand Child Process Instance Started") .withCallerId(callSubActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY) .withStateId(ProcessInstanceState.CANCELLED.getId()).build()); assertThat(repository.countChildrenInstanceIdsOfProcessInstance(parentPI.getId())).isEqualTo(1); final List piIds = repository.getChildrenInstanceIdsOfProcessInstance(parentPI.getId()); assertThat(piIds).isNotEmpty().containsOnly(childPI.getId()); } @Test public void searchGrandChildProcessInstanceOfChildProcessInstance_return_processInstancesIdsNotChild() { final SProcessInstance parentPI = repository .add(aProcessInstance().withContainerId(1).withName("test Parent Process Instance") .withStateId(STARTED.getId()).build()); final SCallActivityInstance callActivity = (SCallActivityInstance) repository.add(aCallActivityInstanceBuilder() .withLogicalGroup4(parentPI.getId()) .withName("call Activity") .build()); final SProcessInstance childPI = repository .add(aProcessInstance().withContainerId(1).withName("test Child Process Instance Started") .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY) .withStateId(STARTED.getId()).build()); final SCallActivityInstance callSubActivity = (SCallActivityInstance) repository .add(aCallActivityInstanceBuilder() .withLogicalGroup4(childPI.getId()) .withName("call Activity") .build()); final SProcessInstance grandChildPI = repository .add(aProcessInstance().withContainerId(1).withName("test Grand Child Process Instance Started") .withCallerId(callSubActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY) .withStateId(ProcessInstanceState.CANCELLED.getId()).build()); assertThat(repository.countChildrenInstanceIdsOfProcessInstance(childPI.getId())).isEqualTo(1); final List piIds = repository.getChildrenInstanceIdsOfProcessInstance(childPI.getId()); assertThat(piIds).isNotEmpty().containsOnly(grandChildPI.getId()); } @Test public void searchChildrenSProcessInstanceOfProcessInstance_return_processInstancesIds() { final SProcessInstance parentPI = repository .add(aProcessInstance().withContainerId(1).withName("test Parent Process Instance") .withStateId(STARTED.getId()).build()); final SCallActivityInstance callActivity = (SCallActivityInstance) repository.add(aCallActivityInstanceBuilder() .withLogicalGroup4(parentPI.getId()) .withName("call Activity") .build()); final SProcessInstance childPI1 = repository .add(aProcessInstance().withContainerId(1).withName("test Child Process Instance Started") .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY) .withStateId(STARTED.getId()).build()); final SProcessInstance childPI2 = repository .add(aProcessInstance().withContainerId(1).withName("test Child Process Instance Started") .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY) .withStateId(ProcessInstanceState.COMPLETED.getId()).build()); final SProcessInstance childPI3 = repository .add(aProcessInstance().withContainerId(1).withName("test Child Process Instance Started") .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY) .withStateId(ProcessInstanceState.CANCELLED.getId()).build()); assertThat(repository.countChildrenInstanceIdsOfProcessInstance(parentPI.getId())).isEqualTo(3); final List piIds = repository.getChildrenInstanceIdsOfProcessInstance(parentPI.getId()); assertThat(piIds).isNotEmpty().containsOnly(childPI1.getId(), childPI2.getId(), childPI3.getId()); } @Test public void searchChildSProcessInstanceOfProcessInstance_return_processInstancesIdsFromParentPIOnly() { final SProcessInstance parentPI = repository .add(aProcessInstance().withContainerId(1).withName("test Parent Process Instance") .withStateId(STARTED.getId()).build()); repository.add(aProcessInstance().withContainerId(1).withName("test Process Instance Independent Started") .withStateId(STARTED.getId()).build()); repository.add(aProcessInstance().withContainerId(1).withName("test Process Instance Independent Completed") .withStateId(ProcessInstanceState.COMPLETED.getId()).build()); repository.add(aProcessInstance().withContainerId(1).withName("test Process Instance Independent Cancelled") .withStateId(ProcessInstanceState.CANCELLED.getId()).build()); final SCallActivityInstance callActivity = (SCallActivityInstance) repository.add(aCallActivityInstanceBuilder() .withLogicalGroup4(parentPI.getId()) .withName("call Activity") .build()); final SProcessInstance childPI = repository .add(aProcessInstance().withContainerId(1).withName("test Child Process Instance") .withCallerId(callActivity.getId()).withCallerType(SFlowNodeType.CALL_ACTIVITY) .withStateId(STARTED.getId()).build()); assertThat(repository.countChildrenInstanceIdsOfProcessInstance(parentPI.getId())).isEqualTo(1); final List piIds = repository.getChildrenInstanceIdsOfProcessInstance(parentPI.getId()); assertThat(piIds).isNotEmpty().containsExactly(childPI.getId()); } @Test public void getNumberOfProcessInstances_should_return_the_number_of_running_instances_of_a_process_definition() { repository.add(aProcessInstance().withProcessDefinitionId(45l).build()); repository.add(aProcessInstance().withProcessDefinitionId(45l).build()); repository.add(aProcessInstance().withProcessDefinitionId(45l).build()); repository.add(aProcessInstance().withProcessDefinitionId(12l).build()); assertThat(repository.getNumberOfProcessInstances(45l)).isEqualTo(3); } @Test public void getNumberOfSProcessInstanceFailedShouldAcceptExtraFilters() { // Given repository.add(buildFailedProcessInstance(1, 777777L)); repository.add(buildFailedProcessInstance(2, 777777L)); repository.add(buildFailedProcessInstance(3, 888888L)); // When final long numberOfSProcessInstanceFailed = repository .getNumberOfSProcessInstanceFailedForProcessDefinition(777777L); // Then assertEquals(2, numberOfSProcessInstanceFailed); } @Test public void searchSProcessInstanceFailedShouldAcceptExtraFilters() { // Given repository.add(buildFailedProcessInstance(1, 777777L)); repository.add(buildFailedProcessInstance(2, 777777L)); repository.add(buildFailedProcessInstance(3, 888888L)); // When final List sProcessInstanceFailed = repository .searchSProcessInstanceFailedForProcessDefinition(777777L); // Then assertEquals(2, sProcessInstanceFailed.size()); } @Test public void getNumberOfSProcessInstanceFailed_should_return_number_of_distinct_process_instances() { // Given repository.add(buildFailedProcessInstance(1)); final SProcessInstance processInstanceWithFailedFlowNode = new SProcessInstance("process2", 10L); processInstanceWithFailedFlowNode.setId(2); repository.add(processInstanceWithFailedFlowNode); repository.add(buildFailedGateway(852, processInstanceWithFailedFlowNode.getId())); final SProcessInstance failedProcessInstanceWithFailedFlowNode = repository.add(buildFailedProcessInstance(3)); repository.add(buildFailedGateway(56, failedProcessInstanceWithFailedFlowNode.getId())); // When final long numberOfSProcessInstanceFailed = repository.getNumberOfSProcessInstanceFailed(); // Then assertEquals(3, numberOfSProcessInstanceFailed); } @Test public void getNumberOfSProcessInstanceFailed_should_return_number_of_failed_process_instances() { // Given repository.add(buildFailedProcessInstance(1)); // When final long numberOfSProcessInstanceFailed = repository.getNumberOfSProcessInstanceFailed(); // Then assertEquals(1, numberOfSProcessInstanceFailed); } @Test public void getNumberOfSProcessInstanceFailed_should_return_number_of_process_instances_with_failed_flow_nodes() { // Given final SProcessInstance processInstanceWithFailedFlowNode = new SProcessInstance("process2", 10L); processInstanceWithFailedFlowNode.setId(2); repository.add(processInstanceWithFailedFlowNode); repository.add(buildFailedGateway(1, processInstanceWithFailedFlowNode.getId())); // When final long numberOfSProcessInstanceFailed = repository.getNumberOfSProcessInstanceFailed(); // Then assertEquals(1, numberOfSProcessInstanceFailed); } @Test public void searchSProcessInstanceFailed_return_distinct_process_instances() { // Given final SProcessInstance failedProcessInstance = repository.add(buildFailedProcessInstance(1)); final SProcessInstance processInstanceWithFailedFlowNode = SProcessInstance.builder().name("process2") .processDefinitionId(10L) .id(2) .build(); repository.add(processInstanceWithFailedFlowNode); repository.add(buildFailedGateway(1, processInstanceWithFailedFlowNode.getId())); final SProcessInstance failedProcessInstanceWithFailedFlowNode = repository.add(buildFailedProcessInstance(3)); repository.add(buildFailedGateway(2, failedProcessInstanceWithFailedFlowNode.getId())); // When final List failedSProcessInstance = repository.searchSProcessInstanceFailed(); // Then assertThat(failedSProcessInstance).containsOnly(failedProcessInstance, failedProcessInstanceWithFailedFlowNode, processInstanceWithFailedFlowNode); } @Test public void searchSProcessInstanceFailed_return_failed_process_instances() { // Given final SProcessInstance failedProcessInstance = repository.add(buildFailedProcessInstance(1)); // When final List failedSProcessInstance = repository.searchSProcessInstanceFailed(); // Then assertEquals(1, failedSProcessInstance.size()); assertEquals(failedProcessInstance, failedSProcessInstance.get(0)); } @Test public void searchSProcessInstanceFailed_return_process_instances_with_failed_flow_nodes() { // Given final SProcessInstance processInstanceWithFailedFlowNode = new SProcessInstance("process2", 10L); processInstanceWithFailedFlowNode.setId(2); repository.add(processInstanceWithFailedFlowNode); repository.add(buildFailedGateway(1, processInstanceWithFailedFlowNode.getId())); // When final List failedSProcessInstance = repository.searchSProcessInstanceFailed(); // Then assertEquals(1, failedSProcessInstance.size()); assertEquals(processInstanceWithFailedFlowNode, failedSProcessInstance.get(0)); } @Test public void getNumberOfSProcessInstanceFailedAndSupervisedBy_should_return_number_of_distinct_process_instances() { // Given final long userId = 2L; repository.add(buildFailedProcessInstance(1)); repository.add(aSupervisor().withProcessDefinitionId(9L).withUserId(userId).build()); repository.add(aProcessInstance().withProcessDefinitionId(8L).withContainerId(1) .withName("test Parent Process Instance") .withStateId(STARTED.getId()).build()); repository.add(aSupervisor().withProcessDefinitionId(8L).withUserId(userId).build()); SProcessInstance processInstanceWithFailedFlowNode = new SProcessInstance("process2", 10L); processInstanceWithFailedFlowNode.setId(2); repository.add(processInstanceWithFailedFlowNode); repository.add(buildFailedGateway(852, processInstanceWithFailedFlowNode.getId())); repository.add(buildFailedProcessInstance(3, 11L)); processInstanceWithFailedFlowNode = new SProcessInstance("process2", 15L); processInstanceWithFailedFlowNode.setId(4); repository.add(processInstanceWithFailedFlowNode); repository.add(buildFailedGateway(853, processInstanceWithFailedFlowNode.getId())); repository.add(buildStartedProcessInstance(5, 15L)); repository.add(aSupervisor().withProcessDefinitionId(15L).withUserId(userId).build()); // When final long numberOfSProcessInstanceFailed = repository.getNumberOfFailedSProcessInstanceSupervisedBy(userId); // Then assertEquals(2, numberOfSProcessInstanceFailed); } @Test public void getSProcessInstanceFailedAndSupervisedBy_should_return_number_of_distinct_process_instances() { // Given final long userId = 2L; repository.add(buildFailedProcessInstance(1)); repository.add(aSupervisor().withProcessDefinitionId(9L).withUserId(userId).build()); repository.add(aProcessInstance().withProcessDefinitionId(8L).withContainerId(1) .withName("test Parent Process Instance") .withStateId(STARTED.getId()).build()); repository.add(aSupervisor().withProcessDefinitionId(8L).withUserId(userId).build()); SProcessInstance processInstanceWithFailedFlowNode = new SProcessInstance("process2", 10L); processInstanceWithFailedFlowNode.setId(2); repository.add(processInstanceWithFailedFlowNode); repository.add(buildFailedGateway(852, processInstanceWithFailedFlowNode.getId())); repository.add(buildFailedProcessInstance(3, 11L)); processInstanceWithFailedFlowNode = new SProcessInstance("process2", 15L); processInstanceWithFailedFlowNode.setId(4); repository.add(processInstanceWithFailedFlowNode); repository.add(buildFailedGateway(853, processInstanceWithFailedFlowNode.getId())); repository.add(buildStartedProcessInstance(5, 15L)); repository.add(aSupervisor().withProcessDefinitionId(15L).withUserId(userId).build()); // When final List sProcessInstanceFailedList = repository .searchFailedSProcessInstanceSupervisedBy(userId); // Then assertThat(sProcessInstanceFailedList).hasSize(2).extracting("id").contains(1L, 4L); } private SGatewayInstance buildFailedGateway(final long gatewayId, final long parentProcessInstanceId) { final SGatewayInstance sGatewayInstance = new SGatewayInstance(); sGatewayInstance.setId(gatewayId); sGatewayInstance.setStateId(3); sGatewayInstance.setLogicalGroup(3, parentProcessInstanceId); return sGatewayInstance; } private SProcessInstance buildFailedProcessInstance(final long processInstanceId) { return buildFailedProcessInstance(processInstanceId, 9L); } private SProcessInstance buildStartedProcessInstance(final long processInstanceId, final long processDefinitionId) { return SProcessInstance.builder().name("process" + processInstanceId).processDefinitionId(processDefinitionId) .id(processInstanceId).stateId(STARTED.getId()).build(); } private SProcessInstance buildFailedProcessInstance(final long processInstanceId, final long processDefinitionId) { return SProcessInstance.builder().name("process" + processInstanceId).processDefinitionId(processDefinitionId) .id(processInstanceId) .stateId(7).build(); } @Test public void should_get_processInstances_by_callertype_and_stateCategory() { SProcessInstance sProcessInstance = buildStartedProcessInstance(12L, 102L); sProcessInstance.setCallerType(RECEIVE_TASK); repository.add(sProcessInstance); repository.flush(); final String stateCategory = jdbcTemplate.queryForObject("select stateCategory from process_instance", String.class); final String callerType = jdbcTemplate.queryForObject("select callerType from process_instance", String.class); assertThat(stateCategory).isEqualTo("NORMAL"); assertThat(callerType).isEqualTo("RECEIVE_TASK"); } @Test public void should_save_and_get_multi_business_data_reference_for_process() { SProcessMultiRefBusinessDataInstance multiRefBusinessDataInstance = new SProcessMultiRefBusinessDataInstance(); multiRefBusinessDataInstance.setDataIds(Arrays.asList(23L, 25L, 27L)); multiRefBusinessDataInstance.setProcessInstanceId(PROCESS_INSTANCE_ID); multiRefBusinessDataInstance.setName("myMultiProcData"); multiRefBusinessDataInstance.setDataClassName("someDataClassName"); multiRefBusinessDataInstance = repository.add(multiRefBusinessDataInstance); repository.flush(); PersistentObject multiRefBusinessData = repository.selectOne("getSRefBusinessDataInstance", pair("processInstanceId", PROCESS_INSTANCE_ID), pair("name", "myMultiProcData")); Map multiRefBusinessDataAsMap = jdbcTemplate .queryForObject( "SELECT ID, KIND, NAME, DATA_CLASSNAME, DATA_ID, PROC_INST_ID, FN_INST_ID FROM ref_biz_data_inst WHERE proc_inst_id=" + PROCESS_INSTANCE_ID + " AND name='myMultiProcData'", new JdbcRowMapper("ID", "DATA_ID", "PROC_INST_ID", "FN_INST_ID")); List> dataIds = jdbcTemplate.query( "SELECT ID, IDX, DATA_ID FROM multi_biz_data WHERE id=" + multiRefBusinessDataInstance.getId(), new JdbcRowMapper("ID", "IDX", "DATA_ID")); assertThat(((SProcessMultiRefBusinessDataInstance) multiRefBusinessData).getDataIds()) .isEqualTo(Arrays.asList(23L, 25L, 27L)); assertThat(multiRefBusinessData).isEqualTo(multiRefBusinessDataInstance); assertThat(multiRefBusinessDataAsMap).containsOnly( entry("ID", multiRefBusinessDataInstance.getId()), entry("KIND", "proc_multi_ref"), entry("NAME", "myMultiProcData"), entry("DATA_CLASSNAME", "someDataClassName"), entry("DATA_ID", null), entry("PROC_INST_ID", PROCESS_INSTANCE_ID), entry("FN_INST_ID", null)); assertThat(dataIds).containsExactly( mapOf(pair("ID", multiRefBusinessDataInstance.getId()), pair("IDX", 0L), pair("DATA_ID", 23L)), mapOf(pair("ID", multiRefBusinessDataInstance.getId()), pair("IDX", 1L), pair("DATA_ID", 25L)), mapOf(pair("ID", multiRefBusinessDataInstance.getId()), pair("IDX", 2L), pair("DATA_ID", 27L))); } @Test public void should_save_and_get_single_business_data_reference_for_process() { SProcessSimpleRefBusinessDataInstance singleRef = new SProcessSimpleRefBusinessDataInstance(); singleRef.setDataId(43L); singleRef.setProcessInstanceId(PROCESS_INSTANCE_ID); singleRef.setName("mySingleData"); singleRef.setDataClassName("someDataClassName"); singleRef = repository.add(singleRef); repository.flush(); PersistentObject singleRefFromQuery = repository.selectOne("getSRefBusinessDataInstance", pair("processInstanceId", PROCESS_INSTANCE_ID), pair("name", "mySingleData")); Map multiRefBusinessDataAsMap = jdbcTemplate .queryForObject( "SELECT ID, KIND, NAME, DATA_CLASSNAME, DATA_ID, PROC_INST_ID, FN_INST_ID FROM ref_biz_data_inst WHERE proc_inst_id=" + PROCESS_INSTANCE_ID + " AND name='mySingleData'", new JdbcRowMapper("ID", "DATA_ID", "PROC_INST_ID", "FN_INST_ID")); assertThat(singleRefFromQuery).isEqualTo(singleRef); assertThat(multiRefBusinessDataAsMap).containsOnly( entry("ID", singleRef.getId()), entry("KIND", "proc_simple_ref"), entry("NAME", "mySingleData"), entry("DATA_CLASSNAME", "someDataClassName"), entry("DATA_ID", 43L), entry("PROC_INST_ID", PROCESS_INSTANCE_ID), entry("FN_INST_ID", null)); } @Test public void should_save_and_get_single_business_data_reference_for_flow_node() { SFlowNodeSimpleRefBusinessDataInstance singleRef = new SFlowNodeSimpleRefBusinessDataInstance(); singleRef.setDataId(43L); singleRef.setFlowNodeInstanceId(FLOW_NODE_INSTANCE_ID); singleRef.setName("mySingleData"); singleRef.setDataClassName("someDataClassName"); singleRef = repository.add(singleRef); repository .flush(); PersistentObject singleRefFromQuery = repository.selectOne("getSFlowNodeRefBusinessDataInstance", pair("flowNodeInstanceId", FLOW_NODE_INSTANCE_ID), pair("name", "mySingleData")); Map multiRefBusinessDataAsMap = jdbcTemplate .queryForObject( "SELECT ID, KIND, NAME, DATA_CLASSNAME, DATA_ID, PROC_INST_ID, FN_INST_ID FROM ref_biz_data_inst WHERE fn_inst_id=" + FLOW_NODE_INSTANCE_ID + " AND name='mySingleData'", new JdbcRowMapper("ID", "DATA_ID", "PROC_INST_ID", "FN_INST_ID")); assertThat(singleRefFromQuery).isEqualTo(singleRef); assertThat(multiRefBusinessDataAsMap).containsOnly( entry("ID", singleRef.getId()), entry("KIND", "fn_simple_ref"), entry("NAME", "mySingleData"), entry("DATA_CLASSNAME", "someDataClassName"), entry("DATA_ID", 43L), entry("PROC_INST_ID", null), entry("FN_INST_ID", FLOW_NODE_INSTANCE_ID)); } @Test public void should_return_process_instance_ids_to_restart_that_older_than_max_last_update_date() { long now = System.currentTimeMillis(); SProcessInstance oldProcess1 = SProcessInstance.builder().name("oldProcess1").stateId(INITIALIZING.getId()) .lastUpdate(now().minusSeconds(60).toEpochMilli()).build(); SProcessInstance oldProcess2 = SProcessInstance.builder().name("oldProcess2").stateId(INITIALIZING.getId()) .lastUpdate(now().minusSeconds(70).toEpochMilli()).build(); SProcessInstance recentProcess1 = SProcessInstance.builder().name("recentProcess1") .stateId(INITIALIZING.getId()).lastUpdate(now().minusSeconds(10).toEpochMilli()).build(); SProcessInstance recentProcess2 = SProcessInstance.builder().name("recentProcess2") .stateId(INITIALIZING.getId()).lastUpdate(now).build(); repository.add(oldProcess1, oldProcess2, recentProcess1, recentProcess2); List processInstanceIdsToRestart = repository .getProcessInstanceIdsToRecover(now().minusSeconds(30).toEpochMilli()); assertThat(processInstanceIdsToRestart).containsOnly(oldProcess1.getId(), oldProcess2.getId()); } @Test public void should_return_process_instance_ids_to_restart_are_in_the_expected_states() { SProcessInstance process1 = SProcessInstance.builder().id(1).name("process1").stateId(INITIALIZING.getId()) .build(); SProcessInstance process2 = SProcessInstance.builder().id(2).name("process2").stateId(COMPLETING.getId()) .build(); SProcessInstance process3 = SProcessInstance.builder().id(3).name("process3").stateId(COMPLETED.getId()) .build(); SProcessInstance process4 = SProcessInstance.builder().id(4).name("process4").stateId(CANCELLED.getId()) .build(); SProcessInstance process5 = SProcessInstance.builder().id(5).name("process5").stateId(ABORTED.getId()).build(); SProcessInstance process6 = SProcessInstance.builder().id(6).name("process6").stateId(STARTED.getId()).build(); SProcessInstance process7 = SProcessInstance.builder().id(7).name("process7").stateId(ERROR.getId()).build(); SProcessInstance process8 = SProcessInstance.builder().id(8).name("process8").stateId(ABORTING.getId()).build(); repository.add(process1, process2, process3, process4, process5, process6, process7, process8); List processInstanceIdsToRestart = repository.getProcessInstanceIdsToRecover(System.currentTimeMillis()); assertThat(processInstanceIdsToRestart).containsOnly(1L, 2L, 3L, 4L, 5L); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/RoleTest.java ================================================ /** * 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.core.process.instance.model; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.test.persistence.builder.RoleBuilder.aRole; import org.bonitasoft.engine.test.persistence.repository.RoleRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; /** * @author Danila Mazour */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class RoleTest { @Autowired private RoleRepository repository; @Test public void should_get_role_by_name() { repository.add(aRole().forRoleId(10L).forRoleName("MyRole10").build()); repository.add(aRole().forRoleId(11L).forRoleName("MyRole11").build()); assertThat(repository.getRoleByName("MyRole10").getId()).isEqualTo(10L); assertThat(repository.getRoleByName("MyRole11").getId()).isEqualTo(11L); assertThat(repository.getRoleByName("NonExistingRole")).isNull(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/SADataInstanceQueriesTest.java ================================================ /** * 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.core.process.instance.model; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.commons.Pair.pair; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import org.bonitasoft.engine.data.instance.model.SBlobDataInstance; import org.bonitasoft.engine.data.instance.model.SBooleanDataInstance; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.SDateDataInstance; import org.bonitasoft.engine.data.instance.model.SDoubleDataInstance; import org.bonitasoft.engine.data.instance.model.SFloatDataInstance; import org.bonitasoft.engine.data.instance.model.SIntegerDataInstance; import org.bonitasoft.engine.data.instance.model.SLongDataInstance; import org.bonitasoft.engine.data.instance.model.SLongTextDataInstance; import org.bonitasoft.engine.data.instance.model.SShortTextDataInstance; import org.bonitasoft.engine.data.instance.model.SXMLDataInstance; import org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SABlobDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SABooleanDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SADataInstance; import org.bonitasoft.engine.data.instance.model.archive.SADateDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SADoubleDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SAFloatDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SAIntegerDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SALongDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SAShortTextDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SAXMLObjectDataInstance; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper; import org.bonitasoft.engine.test.persistence.repository.SADataInstanceRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class SADataInstanceQueriesTest { private static final long CONTAINER_ID = 12345L; @Autowired private SADataInstanceRepository repository; @Autowired private JdbcTemplate jdbcTemplate; private void addSADataInstance(final int id, final String name, final int containerId, final String value, final long archiveDate, final long sourceObjectId) { repository.add(SAShortTextDataInstance.builder() .id(id) .name(name) .className(String.class.getName()) .containerId(containerId) .containerType("PROCESS_INSTANCE") .value(value) .archiveDate(archiveDate) .sourceObjectId(sourceObjectId).build()); } private SDataInstance.SDataInstanceBuilder fillCommonValues( SDataInstance.SDataInstanceBuilder builder) { return builder.className("theClassName") .containerId(CONTAINER_ID) .containerType("ContainerType") .description("My data"); } private SADataInstance.SADataInstanceBuilder fillCommonValues( SADataInstance.SADataInstanceBuilder builder) { return builder.className("theClassName") .containerId(CONTAINER_ID) .containerType("ContainerType") .description("My data"); } protected Map getDataUsingJDBC(SDataInstance dataInstance) { return jdbcTemplate.queryForObject("SELECT * FROM data_instance where id = " + dataInstance.getId(), new JdbcRowMapper(List.of("LONGVALUE"), List.of("BOOLEANVALUE"), List.of("DOUBLEVALUE"))); } protected Map getDataUsingJDBC(SADataInstance dataInstance) { return jdbcTemplate.queryForObject("SELECT * FROM arch_data_instance where id = " + dataInstance.getId(), new JdbcRowMapper(List.of("LONGVALUE"), List.of("BOOLEANVALUE"), List.of("DOUBLEVALUE"))); } protected PersistentObject getDataInstance(String dataName) { return repository.selectOne("getDataInstancesByNameAndContainer", pair("name", dataName), pair("containerId", CONTAINER_ID), pair("containerType", "ContainerType")); } protected PersistentObject getSADataInstance(String dataName) { return repository.selectOne("getLastSADataInstanceByContainer", pair("dataName", dataName), pair("containerId", CONTAINER_ID), pair("containerType", "ContainerType")); } @Test public void should_save_and_get_SXMLDataInstance() { SDataInstance dataInstance = repository.add(fillCommonValues(SXMLDataInstance.builder() .name("myXmlData") .element("theElement") .namespace("theNameSpace") .value("")).build()); PersistentObject persistentObject = getDataInstance("myXmlData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SXMLDataInstance.class); assertThat(dataAsMap).containsEntry("CLOBVALUE", ""); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SXMLDataInstanceImpl"); } @Test public void should_save_and_get_SShortTextDataInstance() { SDataInstance dataInstance = repository.add(fillCommonValues(SShortTextDataInstance.builder() .name("myTextData") .value("shortText")).build()); PersistentObject persistentObject = getDataInstance("myTextData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SShortTextDataInstance.class); assertThat(dataAsMap).containsEntry("SHORTTEXTVALUE", "shortText"); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SShortTextDataInstanceImpl"); } @Test public void should_save_and_get_SSShortTextDataInstance() { SADataInstance dataInstance = repository.add(fillCommonValues(SAShortTextDataInstance.builder() .name("myTextData") .value("shortText")).build()); PersistentObject persistentObject = getSADataInstance("myTextData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SAShortTextDataInstance.class); assertThat(dataAsMap).containsEntry("SHORTTEXTVALUE", "shortText"); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SAShortTextDataInstanceImpl"); } @Test public void should_save_and_get_SLongDataInstance() { SDataInstance dataInstance = repository.add(fillCommonValues(SLongDataInstance.builder() .name("myLongData") .value(1234567890L)).build()); PersistentObject persistentObject = getDataInstance("myLongData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SLongDataInstance.class); assertThat(dataAsMap).containsEntry("LONGVALUE", 1234567890L); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SLongDataInstanceImpl"); } @Test public void should_save_and_get_SDoubleDataInstance() { SDataInstance dataInstance = repository.add(fillCommonValues(SDoubleDataInstance.builder() .name("myDoubleData") .value(1234567890.0)).build()); PersistentObject persistentObject = getDataInstance("myDoubleData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SDoubleDataInstance.class); assertThat(dataAsMap).containsEntry("DOUBLEVALUE", 1234567890.0d); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SDoubleDataInstanceImpl"); } @Test public void should_save_and_get_SXMLObjectDataInstance() { SDataInstance dataInstance = repository.add(fillCommonValues(SXMLObjectDataInstance.builder() .name("myXmlData") .value("MyString")).build()); PersistentObject persistentObject = getDataInstance("myXmlData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SXMLObjectDataInstance.class); assertThat(dataAsMap).containsEntry("CLOBVALUE", "MyString"); assertThat(((SXMLObjectDataInstance) persistentObject).getValue()).isEqualTo("MyString"); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SXMLObjectDataInstanceImpl"); } @Test public void should_save_and_get_SIntegerDataInstance() { SDataInstance dataInstance = repository.add(fillCommonValues(SIntegerDataInstance.builder() .name("myIntData") .value(1234567890)).build()); PersistentObject persistentObject = getDataInstance("myIntData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SIntegerDataInstance.class); assertThat(dataAsMap).containsEntry("INTVALUE", 1234567890); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SIntegerDataInstanceImpl"); } @Test public void should_save_and_get_SDateDataInstance() { Date date = new Date(); SDataInstance dataInstance = repository.add(fillCommonValues(SDateDataInstance.builder() .name("myDateData") .value(date)).build()); PersistentObject persistentObject = getDataInstance("myDateData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SDateDataInstance.class); assertThat(dataAsMap).containsEntry("LONGVALUE", date.getTime()); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SDateDataInstanceImpl"); } @Test public void should_save_and_get_SLongTextDataInstance() { Date date = new Date(); SDataInstance dataInstance = repository.add(fillCommonValues(SLongTextDataInstance.builder() .name("myTextData") .value("lonnnnnnnnng text")).build()); PersistentObject persistentObject = getDataInstance("myTextData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SLongTextDataInstance.class); assertThat(dataAsMap).containsEntry("CLOBVALUE", "lonnnnnnnnng text"); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SLongTextDataInstanceImpl"); } @Test public void should_save_and_get_SFloatDataInstance() { SDataInstance dataInstance = repository.add(fillCommonValues(SFloatDataInstance.builder() .name("myFloatData") .value(1f)).build()); PersistentObject persistentObject = getDataInstance("myFloatData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SFloatDataInstance.class); assertThat(((Number) dataAsMap.get("FLOATVALUE")).floatValue()).isEqualTo(1.0f); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SFloatDataInstanceImpl"); } @Test public void should_save_and_get_SBlobDataInstance() { SDataInstance dataInstance = repository.add(fillCommonValues(SBlobDataInstance.builder() .name("myBlob") .value(new byte[] { 1, 2, 3 })).build()); PersistentObject persistentObject = getDataInstance("myBlob"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SBlobDataInstance.class); assertThat(dataAsMap.get("BLOBVALUE")).isEqualTo(new byte[] { 1, 2, 3 }); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SBlobDataInstanceImpl"); } @Test public void should_save_and_get_SBooleanDataInstance() { SDataInstance dataInstance = repository.add(fillCommonValues(SBooleanDataInstance.builder() .name("myBoolean") .value(true)).build()); PersistentObject persistentObject = getDataInstance("myBoolean"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SBooleanDataInstance.class); assertThat(dataAsMap.get("BOOLEANVALUE")).isEqualTo(true); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SBooleanDataInstanceImpl"); } @Test public void should_save_and_get_SALongDataInstance() { SADataInstance dataInstance = repository.add(fillCommonValues(SALongDataInstance.builder() .name("myLongData") .value(1234567890L)).build()); PersistentObject persistentObject = getSADataInstance("myLongData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SALongDataInstance.class); assertThat(dataAsMap).containsEntry("LONGVALUE", 1234567890L); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SALongDataInstanceImpl"); } @Test public void should_save_and_get_SADoubleDataInstance() { SADataInstance dataInstance = repository.add(fillCommonValues(SADoubleDataInstance.builder() .name("myDoubleData") .value(1234567890.0)).build()); PersistentObject persistentObject = getSADataInstance("myDoubleData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SADoubleDataInstance.class); assertThat(dataAsMap).containsEntry("DOUBLEVALUE", 1234567890.0d); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SADoubleDataInstanceImpl"); } @Test public void should_save_and_get_SAXMLObjectDataInstance() { SADataInstance dataInstance = repository.add(fillCommonValues(SAXMLObjectDataInstance.builder() .name("myXmlData") .value("MyString")).build()); PersistentObject persistentObject = getSADataInstance("myXmlData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SAXMLObjectDataInstance.class); assertThat(dataAsMap).containsEntry("CLOBVALUE", "MyString"); assertThat(((SAXMLObjectDataInstance) persistentObject).getValue()).isEqualTo("MyString"); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SAXMLObjectDataInstanceImpl"); } @Test public void should_save_and_get_SAIntegerDataInstance() { SADataInstance dataInstance = repository.add(fillCommonValues(SAIntegerDataInstance.builder() .name("myIntData") .value(1234567890)).build()); PersistentObject persistentObject = getSADataInstance("myIntData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SAIntegerDataInstance.class); assertThat(dataAsMap).containsEntry("INTVALUE", 1234567890); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SAIntegerDataInstanceImpl"); } @Test public void should_save_and_get_SADateDataInstance() { Date date = new Date(); SADataInstance dataInstance = repository.add(fillCommonValues(SADateDataInstance.builder() .name("myDateData") .value(date)).build()); PersistentObject persistentObject = getSADataInstance("myDateData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SADateDataInstance.class); assertThat(dataAsMap).containsEntry("LONGVALUE", date.getTime()); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SADateDataInstanceImpl"); } @Test public void should_save_and_get_SALongTextDataInstance() { SADataInstance dataInstance = repository.add(fillCommonValues(SALongTextDataInstance.builder() .name("myTextData") .value("lonnnnnnnnng text")).build()); PersistentObject persistentObject = getSADataInstance("myTextData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SALongTextDataInstance.class); assertThat(dataAsMap).containsEntry("CLOBVALUE", "lonnnnnnnnng text"); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SALongTextDataInstanceImpl"); } @Test public void should_save_and_get_SAFloatDataInstance() { SADataInstance dataInstance = repository.add(fillCommonValues(SAFloatDataInstance.builder() .name("myFloatData") .value(1f)).build()); PersistentObject persistentObject = getSADataInstance("myFloatData"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SAFloatDataInstance.class); assertThat(((Number) dataAsMap.get("FLOATVALUE")).floatValue()).isEqualTo(1.0f); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SAFloatDataInstanceImpl"); } @Test public void should_save_and_get_SABlobDataInstance() { SADataInstance dataInstance = repository.add(fillCommonValues(SABlobDataInstance.builder() .name("myBlob") .value(new byte[] { 1, 2, 3 })).build()); PersistentObject persistentObject = getSADataInstance("myBlob"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SABlobDataInstance.class); assertThat(dataAsMap.get("BLOBVALUE")).isEqualTo(new byte[] { 1, 2, 3 }); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SABlobDataInstanceImpl"); } @Test public void should_save_and_get_SABooleanDataInstance() { SADataInstance dataInstance = repository.add(fillCommonValues(SABooleanDataInstance.builder() .name("myBoolean") .value(true)).build()); PersistentObject persistentObject = getSADataInstance("myBoolean"); Map dataAsMap = getDataUsingJDBC(dataInstance); assertThat(persistentObject).isEqualTo(dataInstance); assertThat(persistentObject).isInstanceOf(SABooleanDataInstance.class); assertThat(dataAsMap.get("BOOLEANVALUE")).isEqualTo(true); assertThat(dataAsMap).containsEntry("DISCRIMINANT", "SABooleanDataInstanceImpl"); } @Test public void getSADataInstancesByDataInstanceIdAndArchiveDate_should_return_an_empty_list_if_not_data_defined() { final List dataInstanceIds = new ArrayList<>(); dataInstanceIds.add(4L); final long time = System.currentTimeMillis(); final List dataInstances = repository .getSADataInstancesByDataInstanceIdAndArchiveDate(dataInstanceIds, time); assertThat(dataInstances).isEmpty(); } @Test public void getSADataInstancesByDataInstanceIdAndArchiveDate_should_return_an_empty_list_if_not_matching_identifiers() { addSADataInstance(1025244, "identifiant", 41008, null, 1411051738348L, 205093L); final List dataInstanceIds = new ArrayList<>(); dataInstanceIds.add(4L); final List dataInstances = repository .getSADataInstancesByDataInstanceIdAndArchiveDate(dataInstanceIds, 1411051738348L); assertThat(dataInstances).isEmpty(); } @Test public void getSADataInstancesByDataInstanceIdAndArchiveDate_should_return_an_empty_list_if_not_matching_time() { final long archiveDate = 1411051738348L; addSADataInstance(1025244, "identifiant", 41008, null, archiveDate, 205093L); addSADataInstance(1025259, "identifiant", 41008, "matti", archiveDate + 1500, 205093L); final List dataInstanceIds = new ArrayList<>(); dataInstanceIds.add(205093L); final long time = 0L; final List dataInstances = repository .getSADataInstancesByDataInstanceIdAndArchiveDate(dataInstanceIds, time); assertThat(dataInstances).isEmpty(); } @Test public void getSADataInstancesByDataInstanceIdAndArchiveDate_should_get_the_latest_upate_of_all_data_updates() { final long archiveDate = 1411051738348L; addSADataInstance(1025244, "identifiant", 41008, null, archiveDate, 205093L); addSADataInstance(1025259, "identifiant", 41008, "matti", archiveDate + 1500, 205093L); addSADataInstance(1025356, "identifiant", 41008, "hannu", archiveDate + 2500, 205093L); final List dataInstanceIds = new ArrayList<>(); dataInstanceIds.add(205093L); final List dataInstances = repository .getSADataInstancesByDataInstanceIdAndArchiveDate(dataInstanceIds, archiveDate + 5000); assertThat(dataInstances).hasSize(1); assertThat(dataInstances.get(0).getId()).isEqualTo(1025356); } @Test public void getSADataInstancesByDataInstanceIdAndArchiveDate_should_get_the_latest_upate_of_a_frame_of_data_updates() { final long archiveDate = 1411051738348L; addSADataInstance(1025244, "identifiant", 41008, null, archiveDate, 205093L); addSADataInstance(1025259, "identifiant", 41008, "matti", archiveDate + 1500, 205093L); addSADataInstance(1025356, "identifiant", 41008, "hannu", archiveDate + 2500, 205093L); final List dataInstanceIds = new ArrayList<>(); dataInstanceIds.add(205093L); final List dataInstances = repository .getSADataInstancesByDataInstanceIdAndArchiveDate(dataInstanceIds, archiveDate + 2000); assertThat(dataInstances).hasSize(1); assertThat(dataInstances.get(0).getId()).isEqualTo(1025259); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/SupervisorQueriesTest.java ================================================ /** * 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.core.process.instance.model; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser; import java.util.List; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; import org.bonitasoft.engine.test.persistence.repository.SupervisorRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class SupervisorQueriesTest { private static final long supervisorId = 1; private static final long processDefId = 3; private static final long userId = 4; private static final long groupId = 5; private static final long roleId = 6; @Autowired private SupervisorRepository repository; @Test public void searchSProcessSupervisorWithSUserSGroupSRole_should_return_supervisors_mapped_through_user() { SUser user = aUser().withId(userId).build(); repository.add(user); final SRole sRole = new SRole(); sRole.setId(roleId); repository.add(sRole); final SGroup sGroup = new SGroup(); sGroup.setId(groupId); repository.add(sGroup); final SProcessSupervisor expectedSProcessSupervisor = repository .add(new SProcessSupervisor(supervisorId, processDefId, userId, -1, -1)); repository.add(new SProcessSupervisor(2, processDefId, userId, groupId, roleId)); final List sProcessSupervisors = repository.searchSProcessSupervisorWithSUserSGroupSRole(); assertThat(sProcessSupervisors).containsOnly(expectedSProcessSupervisor.getId()); } @Test public void searchSProcessSupervisorWithSUserSGroupSRole_should_return_supervisors_mapped_through_group() { SUser user = aUser().withId(userId).build(); repository.add(user); final SRole sRole = new SRole(); sRole.setId(roleId); repository.add(sRole); final SGroup sGroup = new SGroup(); sGroup.setId(groupId); repository.add(sGroup); final SProcessSupervisor expectedSProcessSupervisor = repository .add(new SProcessSupervisor(supervisorId, processDefId, 0, groupId, 0)); repository.add(new SProcessSupervisor(2, processDefId, userId, groupId, roleId)); final List sProcessSupervisors = repository.searchSProcessSupervisorWithSUserSGroupSRole(); assertThat(sProcessSupervisors).containsOnly(expectedSProcessSupervisor.getId()); } @Test public void searchSProcessSupervisorWithSUserSGroupSRole_should_return_supervisors_mapped_through_role() { SUser user = aUser().withId(userId).build(); repository.add(user); final SRole sRole = new SRole(); sRole.setId(roleId); repository.add(sRole); final SGroup sGroup = new SGroup(); sGroup.setId(groupId); repository.add(sGroup); final SProcessSupervisor expectedSProcessSupervisor = repository .add(new SProcessSupervisor(supervisorId, processDefId, 0, 0, roleId)); repository.add(new SProcessSupervisor(2, processDefId, userId, groupId, roleId)); final List sProcessSupervisors = repository.searchSProcessSupervisorWithSUserSGroupSRole(); assertThat(sProcessSupervisors).containsOnly(expectedSProcessSupervisor.getId()); } @Test public void searchSProcessSupervisorWithSUserSGroupSRole_should_return_supervisors_mapped_through_group_and_role() { SUser user = aUser().withId(userId).build(); repository.add(user); final SRole sRole = new SRole(); sRole.setId(roleId); repository.add(sRole); final SGroup sGroup = new SGroup(); sGroup.setId(groupId); repository.add(sGroup); final SProcessSupervisor expectedSProcessSupervisor = repository .add(new SProcessSupervisor(supervisorId, processDefId, 0, groupId, roleId)); repository.add(new SProcessSupervisor(2, processDefId, userId, groupId, roleId)); final List sProcessSupervisors = repository.searchSProcessSupervisorWithSUserSGroupSRole(); assertThat(sProcessSupervisors).containsOnly(expectedSProcessSupervisor.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/UserMembershipTest.java ================================================ /** * 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.core.process.instance.model; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.test.persistence.builder.GroupBuilder.aGroup; import static org.bonitasoft.engine.test.persistence.builder.RoleBuilder.aRole; import static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser; import static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership; import java.util.List; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.test.persistence.repository.UserMembershipRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; /** * @author Danila Mazour */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class UserMembershipTest { @Autowired private UserMembershipRepository repository; //Those tests currently verify that the queries returning UserMemberships correctly retrieve the groupParentPath when building the Usermembership objects @Test public void getUserMembershipsByGroup_should_fill_in_groupParentPath() { SUser user = repository.add(aUser().withId(1L).withUserName("dummy username").build()); SGroup group = repository .add(aGroup().forGroupId(258L).forGroupName("dummy groupName").forParentPath("bonita/devList").build()); SRole role = repository.add(aRole().forRoleId(259L).forRoleName("dummy roleName").build()); SUserMembership sUserMembership = aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build(); repository.add(sUserMembership); List userMemberships = repository.getUserMembershipsByGroup(group); assertThat(userMemberships).hasSize(1); SUserMembership userMembership = (SUserMembership) userMemberships.get(0); assertThat(userMembership.getGroupParentPath()).isEqualTo("bonita/devList"); } @Test public void getUserMembershipsByRole_should_fill_in_groupParentPath() { SUser user = repository.add(aUser().withId(1L).withUserName("dummy username").build()); SGroup group = repository .add(aGroup().forGroupId(258L).forGroupName("dummy groupName").forParentPath("bonita/devList").build()); SRole role = repository.add(aRole().forRoleId(259L).forRoleName("dummy roleName").build()); SUserMembership sUserMembership = aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build(); repository.add(sUserMembership); List userMemberships = repository.getUserMembershipsByRole(role); assertThat(userMemberships).hasSize(1); SUserMembership userMembership = (SUserMembership) userMemberships.get(0); assertThat(userMembership.getGroupParentPath()).isEqualTo("bonita/devList"); } @Test public void getUserMembershipsOfUser_should_fill_in_groupParentPath() { SUser user = repository.add(aUser().withId(1L).withUserName("dummy username").build()); SGroup group = repository .add(aGroup().forGroupId(258L).forGroupName("dummy groupName").forParentPath("bonita/devList").build()); SRole role = repository.add(aRole().forRoleId(259L).forRoleName("dummy roleName").build()); SUserMembership sUserMembership = aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build(); repository.add(sUserMembership); List userMemberships = repository.getUserMembershipsOfUser(user.getId()); assertThat(userMemberships).hasSize(1); SUserMembership userMembership = (SUserMembership) userMemberships.get(0); assertThat(userMembership.getGroupParentPath()).isEqualTo("bonita/devList"); } @Test public void getUserMembershipWithIds_should_fill_in_groupParentPath() { SUser user = repository.add(aUser().withId(1L).withUserName("dummy username").build()); SGroup group = repository .add(aGroup().forGroupId(258L).forGroupName("dummy groupName").forParentPath("bonita/devList").build()); SRole role = repository.add(aRole().forRoleId(259L).forRoleName("dummy roleName").build()); SUserMembership sUserMembership = aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build(); repository.add(sUserMembership); List userMemberships = repository.getUserMembershipWithIds(role.getId(), group.getId(), user.getId()); assertThat(userMemberships).hasSize(1); SUserMembership userMembership = (SUserMembership) userMemberships.get(0); assertThat(userMembership.getGroupParentPath()).isEqualTo("bonita/devList"); } @Test public void getUserMemberships_should_fill_transient_fields() { SUser user = repository.add(aUser().withId(1L).withUserName("dummy username").build()); SGroup group = repository .add(aGroup().forGroupId(258L).forGroupName("dummy groupName").forParentPath("bonita/devList").build()); SRole role = repository.add(aRole().forRoleId(259L).forRoleName("dummy roleName").build()); SUserMembership sUserMembership = aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build(); repository.add(sUserMembership); List userMemberships = repository.getUserMemberships(); assertThat(userMemberships).hasSize(1); SUserMembership userMembership = (SUserMembership) userMemberships.get(0); assertThat(userMembership.getGroupParentPath()).isEqualTo("bonita/devList"); assertThat(userMembership.getRoleName()).isEqualTo("dummy roleName"); assertThat(userMembership.getGroupName()).isEqualTo("dummy groupName"); assertThat(userMembership.getUsername()).isEqualTo("dummy username"); } @Test public void getSUserMembershipById_should_fill_in_groupParentPath() { SUser user = repository.add(aUser().withId(1L).withUserName("dummy username").build()); SGroup group = repository .add(aGroup().forGroupId(258L).forGroupName("dummy groupName").forParentPath("bonita/devList").build()); SRole role = repository.add(aRole().forRoleId(259L).forRoleName("dummy roleName").build()); SUserMembership sUserMembership = aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build(); repository.add(sUserMembership); List userMemberships = repository.getSUserMembershipById(sUserMembership.getId()); assertThat(userMemberships).hasSize(1); SUserMembership userMembership = (SUserMembership) userMemberships.get(0); assertThat(userMembership.getGroupParentPath()).isEqualTo("bonita/devList"); } @Test public void searchUserMembership_should_fill_in_groupParentPath() { SUser user = repository.add(aUser().withId(1L).withUserName("dummy username").build()); SGroup group = repository .add(aGroup().forGroupId(258L).forGroupName("dummy groupName").forParentPath("bonita/devList").build()); SRole role = repository.add(aRole().forRoleId(259L).forRoleName("dummy roleName").build()); SUserMembership sUserMembership = aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build(); repository.add(sUserMembership); List userMemberships = repository.searchUserMembership(); assertThat(userMemberships).hasSize(1); SUserMembership userMembership = (SUserMembership) userMemberships.get(0); assertThat(userMembership.getGroupParentPath()).isEqualTo("bonita/devList"); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/core/process/instance/model/UserTest.java ================================================ /** * 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.core.process.instance.model; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserLogin; import org.bonitasoft.engine.test.persistence.repository.UserRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; /** * @author Danila Mazour */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class UserTest { @Autowired private UserRepository repository; //Those tests currently verify that the queries returning UserMemberships correctly retrieve the groupParentPath when building the Usermembership objects @Test public void should_retrieve_user_login() { SUser user = aUser().withId(124L).withUserName("walter.bates").build(); repository.add(user); repository.flush(); SUserLogin userLogin = SUserLogin.builder().id(124L).lastConnection(1234567L).sUser(user).build(); user.setSUserLogin(userLogin); repository.add(userLogin); repository.flush(); assertThat(repository.getUserByName("walter.bates").getSUserLogin().getLastConnection()).isEqualTo(1234567L); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/document/DocumentQueryTest.java ================================================ /** * 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.document; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.core.document.model.SDocument; import org.bonitasoft.engine.core.document.model.SDocumentMapping; import org.bonitasoft.engine.core.document.model.SLightDocument; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.core.document.model.archive.SAMappedDocument; import org.bonitasoft.engine.test.persistence.repository.DocumentRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class DocumentQueryTest { private static final long PROCESS_INSTANCE_ID = 6666666666666666L; @Autowired private DocumentRepository repository; @Test public void getLightDocument_should_read_previously_saved_document() { // given repository.add( SLightDocument.builder().id(99L).author(11L).hasContent(true).fileName("myFile.pdf") .mimeType("application/pdf").author(22L) .build()); //when final SLightDocument document = repository.getById(SLightDocument.class, 99L); // //then assertThat(document.getFileName()).isEqualTo("myFile.pdf"); assertThat(document.getMimeType()).isEqualTo("application/pdf"); assertThat(document.getAuthor()).isEqualTo(22L); } @Test public void getDocumentWithContent_should_retrieve_previously_saved_document() { // given final byte[] binaryDocContent = "someBinaryContent".getBytes(); repository.add( SDocument.builder().id(666L).hasContent(true).fileName("myFile.pdf") .mimeType("application/pdf").content(binaryDocContent) .build()); //when final SDocument document = repository.getById(SDocument.class, 666L); // //then assertThat(document.getFileName()).isEqualTo("myFile.pdf"); assertThat(document.getMimeType()).isEqualTo("application/pdf"); assertThat(document.getContent()).isEqualTo(binaryDocContent); } @Test public void getDocumentMapping_should_retrieve_previously_saved_document_mapping() { // given: repository.add(SDocumentMapping.builder().id(14L) .name("myDocMapping").description("doc mapping description").documentId(111L) .index(9).processInstanceId(987987987987L).version("2.0") .build()); // when: SDocumentMapping documentMapping = repository.getById(SDocumentMapping.class, 14L); // then: assertThat(documentMapping.getName()).isEqualTo("myDocMapping"); assertThat(documentMapping.getDescription()).isEqualTo("doc mapping description"); assertThat(documentMapping.getIndex()).isEqualTo(9); assertThat(documentMapping.getProcessInstanceId()).isEqualTo(987987987987L); assertThat(documentMapping.getVersion()).isEqualTo("2.0"); assertThat(documentMapping.getDocumentId()).isEqualTo(111L); } @Test public void getSMappedDocumentOfProcessWithName_should_retrieve_previously_saved_mapped_document() { // given: final SLightDocument docContent = SLightDocument.builder() .id(666L).hasContent(true).fileName("myFile.pdf").mimeType("application/pdf") .build(); repository.add(docContent); repository.add(SMappedDocument.builder().id(14L) .name("myMappedDocument").description("doc desc") .index(-1).processInstanceId(444888444488844L).version("1.7") .document(docContent) .documentId(docContent.getId()) .build()); // when: SMappedDocument mappedDocument = repository.getSMappedDocumentOfProcessWithName("myMappedDocument", 444888444488844L); // then: assertThat(mappedDocument.getName()).isEqualTo("myMappedDocument"); assertThat(mappedDocument.getDescription()).isEqualTo("doc desc"); assertThat(mappedDocument.getIndex()).isEqualTo(-1); assertThat(mappedDocument.getProcessInstanceId()).isEqualTo(444888444488844L); assertThat(mappedDocument.getVersion()).isEqualTo("1.7"); assertThat(mappedDocument.getDocument()).isEqualTo(docContent); } @Test public void getSAMappedDocumentOfProcessWithName_should_retrieve_previously_saved_archived_mapped_document() { // given: final SLightDocument docContent = SLightDocument.builder() .id(915L).hasContent(true).fileName("myFile.txt").mimeType("test/plain") .build(); repository.add(docContent); repository.add(SAMappedDocument.builder().id(32L) .name("archivedMappedDoc").description("doc desc") .index(-1).processInstanceId(PROCESS_INSTANCE_ID).version("1.7") .document(docContent) .documentId(docContent.getId()) .archiveDate(System.currentTimeMillis()) .build()); // when: SAMappedDocument archivedMappedDocument = repository.getSAMappedDocumentOfProcessWithName("archivedMappedDoc", PROCESS_INSTANCE_ID); // then: assertThat(archivedMappedDocument.getName()).isEqualTo("archivedMappedDoc"); assertThat(archivedMappedDocument.getDescription()).isEqualTo("doc desc"); assertThat(archivedMappedDocument.getIndex()).isEqualTo(-1); assertThat(archivedMappedDocument.getProcessInstanceId()).isEqualTo(PROCESS_INSTANCE_ID); assertThat(archivedMappedDocument.getVersion()).isEqualTo("1.7"); assertThat(archivedMappedDocument.getDocument()).isEqualTo(docContent); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/page/PageQueriesTest.java ================================================ /** * 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.page; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.test.persistence.builder.PageBuilder.aPage; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.test.persistence.repository.PageRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class PageQueriesTest { @Autowired private PageRepository repository; @Test public void getPageContent_should_return_the_content_of_the_page() { // given final AbstractSPage page = repository .add(aPage().withName("MyPage").withContent("The content".getBytes()).build()); //when final SPageWithContent pageContent = repository.getPageContent(page.getId()); // //then assertThat(pageContent.getContent()).isEqualTo("The content".getBytes()); assertThat(pageContent.getId()).isEqualTo(page.getId()); } @Test public void getPageByName_should_return_the_page_having_the_name() { // given repository.add(aPage().withName("MyPage1").withContent("The content".getBytes()).build()); final AbstractSPage page2 = repository .add(aPage().withName("MyPage2").withContent("The content".getBytes()).build()); //when final AbstractSPage pageByName = repository.getPageByName("MyPage2"); // //then assertThat(pageByName.getId()).isEqualTo(page2.getId()); } @Test public void should_getPageByName_return_the_page_without_processDefinitionId() { //given repository.add(aPage().withName("aPage").withContent("content of the global page".getBytes()).build()); repository.add(aPage().withName("aPage").withContent("content of the process page 1".getBytes()) .withProcessDefinitionId(123L).build()); repository.add(aPage().withName("anOtherPage").withContent("content of the process page 2".getBytes()) .withProcessDefinitionId(123L).build()); //when SPage globalPage = repository.getPageByName("aPage"); SPage processPage1 = repository.getPageByNameAndProcessDefinitionId("aPage", 123L); SPage processPage2 = repository.getPageByNameAndProcessDefinitionId("anOtherPage", 123L); //then assertThat(new String(repository.getPageContent(globalPage.getId()).getContent())) .isEqualTo("content of the global page"); assertThat(new String(repository.getPageContent(processPage1.getId()).getContent())) .isEqualTo("content of the process page 1"); assertThat(new String(repository.getPageContent(processPage2.getId()).getContent())) .isEqualTo("content of the process page 2"); } @Test public void getPageByNameAndProcessDefinition_should_return_the_page_having_the_name() { // given final AbstractSPage myPage1 = repository.add(aPage() .withName("MyPage1") .withProcessDefinitionId(1L) .build()); assertThat(myPage1).as("should add the page").isNotNull(); //when final SPage pageByName = repository.getPageByNameAndProcessDefinitionId("MyPage1", 1L); // //then assertThat(pageByName).as("should retrieve the page").isNotNull(); } @Test public void getPageByProcessDefinition_should_filter_results() { // given final AbstractSPage myPage1 = repository.add(aPage() .withName("MyPage1") .withProcessDefinitionId(1L) .withContentType(ContentType.FORM) .build()); repository.add(aPage() .withName("AnotherPage") .withProcessDefinitionId(2L) .withContentType(ContentType.FORM) .build()); assertThat(myPage1).as("should add the page").isNotNull(); //when final List results = repository.getPageByProcessDefinitionId(1L); // //then assertThat(results).as("should retrieve the page").hasSize(1); assertThat(results.get(0).getName()).as("should retrieve the right page").isEqualTo(myPage1.getName()); assertThat(results.get(0).getProcessDefinitionId()).as("should retrieve the right page") .isEqualTo(myPage1.getProcessDefinitionId()); assertThat(results.get(0).getContentType()).as("should retrieve the right page") .isEqualTo(myPage1.getContentType()); } @Test public void should_retrieve_SPageMapping_previously_saved() { // given: final List authorizationRules = new ArrayList<>(Arrays.asList("rule1", "rule2", "rule3")); SPageMapping pageMapping = SPageMapping.builder().pageId(2L).key("myKey").url("http://www/example.com") .urlAdapter("legacy").build(); pageMapping.setPageAuthorizationRules(authorizationRules); repository.add(pageMapping); // when: final SPageMapping mapping = repository.getPageMappingByKey("myKey"); // then: assertThat(mapping).extracting("pageId", "key", "url", "urlAdapter") .containsOnly(2L, "myKey", "http://www/example.com", "legacy"); assertThat(mapping.getPageAuthorizationRules()).isEqualTo(authorizationRules); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/parameter/ParameterTest.java ================================================ /** * 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.parameter; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.bonitasoft.engine.commons.Pair.pair; import java.util.Map; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper; import org.bonitasoft.engine.test.persistence.repository.TestRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class ParameterTest { @Autowired private TestRepository testRepository; @Autowired private JdbcTemplate jdbcTemplate; @Test public void should_save_and_get_parameter() { SParameter sParameter = testRepository.add(new SParameter("parameterName", "StringValue", 12345L)); PersistentObject parameterFromQuery = testRepository.selectOne("getParameterByName", pair("name", "parameterName"), pair("processDefinitionId", 12345L)); testRepository.flush(); Map parameterAsMap = jdbcTemplate .queryForObject("SELECT * FROM proc_parameter WHERE name = 'parameterName'", new JdbcRowMapper("ID", "PROCESS_ID")); assertThat(parameterFromQuery).isEqualTo(sParameter); assertThat(parameterAsMap).containsOnly( entry("ID", sParameter.getId()), entry("PROCESS_ID", 12345L), entry("NAME", "parameterName"), entry("VALUE", "StringValue")); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/platform/command/model/PlatformCommandTest.java ================================================ /** * 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.platform.command.model; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.bonitasoft.engine.commons.Pair.pair; import java.util.Map; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper; import org.bonitasoft.engine.test.persistence.repository.PlatformRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class PlatformCommandTest { @Autowired private PlatformRepository repository; @Autowired private JdbcTemplate jdbcTemplate; @Test public void should_save_and_get_SPlatformCommand() { SPlatformCommand platformCommand = repository.add(SPlatformCommand.builder() .name("myPlatformCommand") .implementation("com.acme.PlatformCommandClass") .description("a custom command on platform") .build()); repository.flush(); PersistentObject platformCommandFromQuery = repository.selectOneOnPlatform("getPlatformCommandByName", pair("name", "myPlatformCommand")); Map platformCommandAsMap = jdbcTemplate.queryForObject("SELECT * FROM platformCommand", new JdbcRowMapper("ID")); assertThat(platformCommandFromQuery).isEqualTo(platformCommand); assertThat(platformCommandAsMap).containsOnly( entry("ID", platformCommand.getId()), entry("NAME", "myPlatformCommand"), entry("DESCRIPTION", "a custom command on platform"), entry("IMPLEMENTATION", "com.acme.PlatformCommandClass")); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/platform/model/impl/PlatformTest.java ================================================ /** * 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.platform.model.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import java.util.List; import java.util.Map; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.platform.model.SPlatform; import org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper; import org.bonitasoft.engine.test.persistence.repository.PlatformRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class PlatformTest { @Autowired private PlatformRepository repository; @Autowired private JdbcTemplate jdbcTemplate; @Test public void should_save_and_get_SPlatform() { SPlatform platform = repository.add(SPlatform.builder() .initialBonitaVersion("5.9.0") .dbSchemaVersion("1.2") .applicationVersion("0.0.0") .created(345L) .information("some infos XYZ") .createdBy("The almighty") .maintenanceEnabled(false) .build()); repository.flush(); PersistentObject platformFromQuery = repository.selectOneOnPlatform("getPlatform"); Map platformAsMap = jdbcTemplate.queryForObject("SELECT * FROM platform", new JdbcRowMapper(List.of("ID", "CREATED"), List.of("MAINTENANCE_MESSAGE_ACTIVE", "MAINTENANCE_ENABLED"))); assertThat(platformFromQuery).isEqualTo(platform); assertThat(platformAsMap).containsOnly( entry("ID", platform.getId()), entry("CREATED", 345L), entry("CREATED_BY", "The almighty"), entry("INITIAL_BONITA_VERSION", "5.9.0"), entry("APPLICATION_VERSION", "0.0.0"), entry("MAINTENANCE_MESSAGE", null), entry("MAINTENANCE_MESSAGE_ACTIVE", false), entry("VERSION", "1.2"), entry("INFORMATION", "some infos XYZ"), entry("MAINTENANCE_ENABLED", false)); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/profile/ProfilesTest.java ================================================ /** * Copyright (C) 2017 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.profile; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; import static org.bonitasoft.engine.test.persistence.builder.GroupBuilder.aGroup; import static org.bonitasoft.engine.test.persistence.builder.ProfileBuilder.aProfile; import static org.bonitasoft.engine.test.persistence.builder.ProfileMemberBuilder.aProfileMember; import static org.bonitasoft.engine.test.persistence.builder.RoleBuilder.aRole; import static org.bonitasoft.engine.test.persistence.builder.UserBuilder.aUser; import static org.bonitasoft.engine.test.persistence.builder.UserMembershipBuilder.aUserMembership; import java.util.List; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.test.persistence.repository.ProfileRepository; import org.bonitasoft.engine.test.persistence.repository.UserMembershipRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; /** * @author Emmanuel Duchastenier */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class ProfilesTest { @SuppressWarnings("unused") @Autowired private ProfileRepository repository; @SuppressWarnings("unused") @Autowired private UserMembershipRepository userMembershipRepository; @Test public void profile_with_user_mapped_should_be_retrieved() { // given: final SUser user = aUser().build(); repository.add(user); final String profileName = "retrieved"; final SProfile profile = aProfile().withName(profileName).build(); repository.add(profile); repository.add(aProfileMember().withProfileId(profile.getId()).withUserId(user.getId()).build()); // when: final List profiles = repository.getProfilesOfUser(user.getId()); // then: assertThat(profiles).hasSize(1).extracting("id", "name").containsExactly(tuple(profile.getId(), profileName)); } @Test public void profile_with_role_mapped_should_be_retrieved() { // given: final SRole role = aRole().build(); final SUser user = aUser().build(); repository.add(role); repository.add(user); userMembershipRepository.add(aUserMembership().forUser(user).memberOf(-1, role.getId()).build()); final String profileName = "retrieved"; final SProfile profile = aProfile().withName(profileName).build(); repository.add(profile); repository.add(aProfileMember().withProfileId(profile.getId()).withRoleId(role.getId()).build()); // when: final List profiles = repository.getProfilesOfUser(user.getId()); // then: assertThat(profiles).hasSize(1).extracting("id", "name").containsExactly(tuple(profile.getId(), profileName)); } @Test public void no_profileWithNavigation_should_be_retrieved_for_system_user() { // given: final SRole role = aRole().build(); repository.add(role); final SProfile profile = aProfile().withName("not retrieved").build(); repository.add(profile); repository.add(aProfileMember().withProfileId(profile.getId()).withRoleId(role.getId()).build()); // when: final List profiles = repository.getProfilesOfUser(-1); // then: assertThat(profiles).hasSize(0); } @Test public void profile_with_group_mapped_should_be_retrieved() { // given: final SGroup group = aGroup().build(); final SUser user = aUser().build(); repository.add(group); repository.add(user); userMembershipRepository.add(aUserMembership().forUser(user).memberOf(group.getId(), -1).build()); final String profileName = "retrieved"; final SProfile profile = aProfile().withName(profileName).build(); repository.add(profile); repository.add(aProfileMember().withProfileId(profile.getId()).withGroupId(group.getId()).build()); // when: final List profiles = repository.getProfilesOfUser(user.getId()); // then: assertThat(profiles).hasSize(1).extracting("id", "name").containsExactly(tuple(profile.getId(), profileName)); } @Test public void profile_with_membership_mapped_should_be_retrieved() { // given: final SGroup group = aGroup().build(); final SRole role = aRole().build(); final SUser user = aUser().build(); repository.add(group); repository.add(role); repository.add(user); userMembershipRepository.add(aUserMembership().forUser(user).memberOf(group.getId(), role.getId()).build()); final String profileName = "retrieved"; final SProfile profile = aProfile().withName(profileName).build(); repository.add(profile); repository.add(aProfileMember().withProfileId(profile.getId()).withGroupId(group.getId()) .withRoleId(role.getId()).build()); // when: final List profiles = repository.getProfilesOfUser(user.getId()); // then: assertThat(profiles).hasSize(1).extracting("id", "name").containsExactly(tuple(profile.getId(), profileName)); } @Test public void profile_without_user_mapped_should_not_be_retrieved() { // given: final SUser user = aUser().build(); repository.add(user); final String profileName = "should not be retrieved"; final SProfile profile = aProfile().withName(profileName).build(); repository.add(profile); // when: final List profiles = repository.getProfilesOfUser(user.getId()); // then: assertThat(profiles).hasSize(0); } @Test public void profile_without_given_user_mapped_should_not_be_retrieved() { // given: final SUser userMapped = aUser().build(); final SUser userAsked = aUser().build(); repository.add(userMapped); final String profileName = "should not be retrieved"; final SProfile profile = aProfile().withName(profileName).build(); repository.add(profile); repository.add(aProfileMember().withProfileId(profile.getId()).withUserId(userMapped.getId()).build()); // when: final List profiles = repository.getProfilesOfUser(userAsked.getId()); // then: assertThat(profiles).hasSize(0); } @Test public void no_profile_should_be_retrieved_for_system_user() { // given: final SRole role = aRole().build(); repository.add(role); final SProfile profile = aProfile().withName("not retrieved").build(); repository.add(profile); repository.add(aProfileMember().withProfileId(profile.getId()).withRoleId(role.getId()).build()); // when: final List profiles = repository.getProfilesOfUser(-1); // then: assertThat(profiles).hasSize(0); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/queriablelogger/model/QueriableLogTest.java ================================================ /** * 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.queriablelogger.model; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.data.MapEntry.entry; import java.util.List; import java.util.Map; import org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper; import org.bonitasoft.engine.test.persistence.repository.TestRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class QueriableLogTest { @Autowired TestRepository testRepository; @Autowired private JdbcTemplate jdbcTemplate; @Test public void should_be_able_to_add_queriable_log() { SQueriableLog queriableLog = SQueriableLog.builder() .initializeNow()// .id(1) .userId("userId1") .actionType("actionType1") // .actionScope("actionScope1") // .productVersion("productVersion1") // .clusterNode("clusterNode1") // .severity(SQueriableLogSeverity.BUSINESS) // .actionScope("actionScope1") // .callerClassName("callerClassName1") // .callerMethodName("callerMethodName1") // .rawMessage("message1").build(); SQueriableLog queriableLog1 = SQueriableLog.builder() // .initializeNow() .id(2) .userId("userId2") .productVersion("productVersion2") // .actionType("actionType2") // .actionScope("actionScope2") // .clusterNode("clusterNode2") // .severity(SQueriableLogSeverity.BUSINESS) // .actionScope("actionScope2") // .callerClassName("callerClassName2") // .callerMethodName("callerMethodName2") // .rawMessage("message2").build(); testRepository.add(queriableLog); testRepository.add(queriableLog1); testRepository.flush(); List> queriableLogs = jdbcTemplate.query("SELECT * from queriable_log", new JdbcRowMapper("ID", "NUMERICINDEX1", "NUMERICINDEX2", "NUMERICINDEX3", "NUMERICINDEX4", "NUMERICINDEX5", "THREADNUMBER", "LOG_TIMESTAMP")); assertThat(queriableLogs).hasSize(2); assertThat(queriableLogs.stream().filter(m -> m.get("ID").equals(1L)).findFirst().get()).containsOnly( entry("ACTIONSCOPE", "actionScope1"), entry("ACTIONSTATUS", -1), entry("ACTIONTYPE", "actionType1"), entry("CALLERCLASSNAME", "callerClassName1"), entry("CALLERMETHODNAME", "callerMethodName1"), entry("CLUSTERNODE", "clusterNode1"), entry("NUMERICINDEX1", -1L), entry("NUMERICINDEX2", -1L), entry("NUMERICINDEX3", -1L), entry("NUMERICINDEX4", -1L), entry("NUMERICINDEX5", -1L), entry("PRODUCTVERSION", "productVersion1"), entry("SEVERITY", "BUSINESS"), entry("RAWMESSAGE", "message1"), entry("THREADNUMBER", queriableLog.getThreadNumber()), entry("USERID", "userId1"), entry("WEEKOFYEAR", queriableLog.getWeekOfYear()), entry("WHATMONTH", queriableLog.getMonth()), entry("DAYOFYEAR", queriableLog.getDayOfYear()), entry("LOG_TIMESTAMP", queriableLog.getTimeStamp()), entry("ID", 1L), entry("WHATYEAR", queriableLog.getYear())); assertThat(queriableLogs.stream().filter(m -> m.get("ID").equals(2L)).findFirst().get()).containsOnly( entry("ACTIONSCOPE", "actionScope2"), entry("ACTIONSTATUS", -1), entry("ACTIONTYPE", "actionType2"), entry("CALLERCLASSNAME", "callerClassName2"), entry("CALLERMETHODNAME", "callerMethodName2"), entry("CLUSTERNODE", "clusterNode2"), entry("NUMERICINDEX1", -1L), entry("NUMERICINDEX2", -1L), entry("NUMERICINDEX3", -1L), entry("NUMERICINDEX4", -1L), entry("NUMERICINDEX5", -1L), entry("PRODUCTVERSION", "productVersion2"), entry("SEVERITY", "BUSINESS"), entry("RAWMESSAGE", "message2"), entry("THREADNUMBER", queriableLog.getThreadNumber()), entry("USERID", "userId2"), entry("WEEKOFYEAR", queriableLog1.getWeekOfYear()), entry("WHATMONTH", queriableLog1.getMonth()), entry("DAYOFYEAR", queriableLog1.getDayOfYear()), entry("LOG_TIMESTAMP", queriableLog1.getTimeStamp()), entry("ID", 2L), entry("WHATYEAR", queriableLog1.getYear()) ); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/resources/DependencyServiceQueriesTest.java ================================================ /** * Copyright (C) 2017 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.resources; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.bonitasoft.engine.commons.Pair.pair; import static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS; import static org.bonitasoft.engine.dependency.model.ScopeType.TENANT; import static org.junit.Assert.fail; import java.util.Map; import java.util.Random; import org.bonitasoft.engine.dependency.model.DependencyContent; import org.bonitasoft.engine.dependency.model.SDependency; import org.bonitasoft.engine.dependency.model.SDependencyMapping; import org.bonitasoft.engine.dependency.model.SPlatformDependency; import org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper; import org.bonitasoft.engine.test.persistence.repository.DependencyRepository; import org.bonitasoft.engine.test.persistence.repository.PlatformRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; /** * @author Danila Mazour */ @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class DependencyServiceQueriesTest { @Autowired private DependencyRepository repository; @Autowired private PlatformRepository platformRepository; @Autowired private JdbcTemplate jdbcTemplate; private static String getSaltString() { String SALTCHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; StringBuilder salt = new StringBuilder(); Random rnd = new Random(); while (salt.length() < 10) { // length of the random string. int index = (int) (rnd.nextFloat() * SALTCHARS.length()); salt.append(SALTCHARS.charAt(index)); } return salt.toString(); } @Test public void should_retrieve_the_correct_dependencyId_from_tenant_and_filename() { SDependency resource_to_retrieve = repository.add(SDependency.builder() .name(getSaltString()) .fileName("aFileName.zip") .value_("toutou".getBytes()).build()); repository.add(SDependency.builder() .name(getSaltString()) .fileName("file.lst") .value_("toutou".getBytes()).build()); repository.add(SDependencyMapping.builder() .artifactId(666L) .artifactType(TENANT) .dependencyId(resource_to_retrieve.getId()).build()); Long dependencyId = repository.getDependencyIdFromArtifact(666L, TENANT, "aFileName.zip"); assertThat(dependencyId).as("dependency id").isEqualTo(resource_to_retrieve.getId()); } @Test public void should_retrieve_nothing_when_BDM_not_deployed() { SDependency resource_to_not_retrieve = repository.add(SDependency.builder() .name(getSaltString()) .fileName("aFileName.zip") .value_("toutou".getBytes()) .build()); repository.add(SDependencyMapping.builder() .artifactId(666L) .artifactType(TENANT) .dependencyId(resource_to_not_retrieve.getId()).build()); Long dependencyId = repository.getDependencyIdFromArtifact(666L, TENANT, "anotherFileName.zip"); assertThat(dependencyId).as("dependency id").isNull(); } @Test public void should_retrieve_nothing_when_the_DB_isEmpty() { Long dependencyId = repository.getDependencyIdFromArtifact(666L, TENANT, "aFileName.zip"); assertThat(dependencyId).as("dependency id").isNull(); } @Test public void should_retrieve_dependency_content_only_using_disconnected_objects() throws Exception { SDependency aDependency = repository.add(SDependency.builder() .name(getSaltString()) .fileName("aFileName.zip") .value_("toutou".getBytes()).build()); DependencyContent dependencyContentOnly = repository.getDependencyContentOnly(aDependency.getId()); assertThat(dependencyContentOnly.getContent()).isEqualTo("toutou".getBytes()); assertThat(dependencyContentOnly.getFileName()).isEqualTo("aFileName.zip"); assertThat(repository.getSession().getIdentifier(aDependency)).isNotNull(); try { repository.getSession().getIdentifier(dependencyContentOnly); fail("should fail because the object should not be in the hibernate session"); } catch (Exception ignored) { } } @Test public void should_save_and_get_dependency() { SDependency aDependency = repository.add(SDependency.builder() .name("dependencyName") .fileName("aFileName.jar") .description("description of the jar") .value_("jarContent".getBytes()).build()); PersistentObject dependencyFromQuery = repository.selectOne("getDependencyByName", pair("name", "dependencyName")); Map dependencyAsMap = jdbcTemplate.queryForObject("SELECT * FROM dependency", new JdbcRowMapper("ID")); assertThat(dependencyFromQuery).isEqualTo(aDependency); assertThat(dependencyAsMap).containsOnly( entry("ID", aDependency.getId()), entry("NAME", "dependencyName"), entry("DESCRIPTION", "description of the jar"), entry("FILENAME", "aFileName.jar"), entry("VALUE_", "jarContent".getBytes())); } @Test public void should_save_and_get_dependency_mapping() { SDependency aDependency = repository.add(SDependency.builder() .name("dependencyName") .fileName("aFileName.jar") .description("description of the jar") .value_("jarContent".getBytes()).build()); SDependencyMapping dependencyMapping = repository.add(SDependencyMapping.builder() .artifactId(567L) .artifactType(PROCESS) .dependencyId(aDependency.getId()).build()); PersistentObject dependencyMappingFromQuery = repository.selectOne("getDependencyMappingsByDependency", pair("dependencyId", aDependency.getId())); Map dependencyMappingAsMap = jdbcTemplate .queryForObject("SELECT * FROM dependencymapping WHERE dependencyId=" + aDependency.getId(), new JdbcRowMapper("ID", "ARTIFACTID", "DEPENDENCYID")); assertThat(dependencyMappingFromQuery).isEqualTo(dependencyMapping); assertThat(dependencyMappingAsMap).containsOnly( entry("ID", dependencyMapping.getId()), entry("ARTIFACTID", 567L), entry("ARTIFACTTYPE", "PROCESS"), entry("DEPENDENCYID", aDependency.getId())); } @Test public void should_save_and_get_platform_dependency() { SPlatformDependency aDependency = platformRepository.add(SPlatformDependency.builder() .name("dependencyName") .fileName("aFileName.jar") .description("description of the jar") .value_("jarContent".getBytes()).build()); PersistentObject dependencyFromQuery = platformRepository.selectOneOnPlatform( "getPlatformDependencyByName", pair("name", "dependencyName")); Map dependencyAsMap = jdbcTemplate.queryForObject("SELECT * FROM pdependency", new JdbcRowMapper("ID")); assertThat(dependencyFromQuery).isEqualTo(aDependency); assertThat(dependencyAsMap).containsOnly( entry("ID", aDependency.getId()), entry("NAME", "dependencyName"), entry("DESCRIPTION", "description of the jar"), entry("FILENAME", "aFileName.jar"), entry("VALUE_", "jarContent".getBytes())); } @Test public void should_save_and_get_platform_dependency_mapping() { SPlatformDependency aDependency = platformRepository.add(SPlatformDependency.builder() .name("dependencyName") .fileName("aFileName.jar") .description("description of the jar") .value_("jarContent".getBytes()).build()); SPlatformDependencyMapping dependencyMapping = platformRepository.add(SPlatformDependencyMapping.builder() .artifactId(567L) .artifactType(PROCESS) .dependencyId(aDependency.getId()).build()); PersistentObject dependencyMappingFromQuery = platformRepository.selectOneOnPlatform( "getPlatformDependencyMappingsByDependency", pair("dependencyId", aDependency.getId())); Map dependencyMappingAsMap = jdbcTemplate .queryForObject("SELECT * FROM pdependencymapping WHERE dependencyId=" + aDependency.getId(), new JdbcRowMapper("ID", "ARTIFACTID", "DEPENDENCYID")); assertThat(dependencyMappingFromQuery).isEqualTo(dependencyMapping); assertThat(dependencyMappingAsMap).containsOnly( entry("ID", dependencyMapping.getId()), entry("ARTIFACTID", 567L), entry("ARTIFACTTYPE", "PROCESS"), entry("DEPENDENCYID", aDependency.getId())); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/resources/ProcessResourceServiceQueriesTest.java ================================================ /** * Copyright (C) 2015 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.resources; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; import static org.bonitasoft.engine.test.persistence.builder.BARResourceBuilder.aBARResource; import java.util.List; import org.bonitasoft.engine.test.persistence.repository.ProcessResourceRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class ProcessResourceServiceQueriesTest { @Autowired private ProcessResourceRepository repository; @Test public void getBarResource_should_get_one_resource() { // given SBARResource resource = repository .add(aBARResource().withName("MyResource").withContent("The content".getBytes()) .withProcessDefinitionId(345L).withType(BARResourceType.EXTERNAL).build()); repository.add(aBARResource().withName("MyResource2").withContent("The content@".getBytes()) .withProcessDefinitionId(346L).withType(BARResourceType.EXTERNAL).build()); //when SBARResource myResource = repository.getBARResource(345L, BARResourceType.EXTERNAL, "MyResource"); // //then assertThat(myResource.getContent()).isEqualTo("The content".getBytes()); assertThat(myResource.getId()).isEqualTo(resource.getId()); } @Test public void getBarResourceOfType_should_get_all_resource_of_type() { // given SBARResource resource1 = repository .add(aBARResource().withName("MyResource").withContent("The content".getBytes()) .withProcessDefinitionId(345L).withType(BARResourceType.EXTERNAL).build()); repository.add(aBARResource().withName("MyResource2").withContent("The content@".getBytes()) .withProcessDefinitionId(346L).withType(BARResourceType.EXTERNAL).build()); SBARResource resource2 = repository .add(aBARResource().withName("MyResource3").withContent("The content3".getBytes()) .withProcessDefinitionId(345L).withType(BARResourceType.EXTERNAL).build()); repository.add(aBARResource().withName("MyConnector").withContent("The content".getBytes()) .withProcessDefinitionId(345L).withType(BARResourceType.CONNECTOR).build()); //when List myResources = repository.getBARResourcesOfType(345L, BARResourceType.EXTERNAL); // //then assertThat(myResources).extracting("id", "content").containsOnly( tuple(resource1.getId(), resource1.getContent()), tuple(resource2.getId(), resource2.getContent())); } @Test public void getNumberBarResourceOfType_should_get_all_resource_of_type() { // given repository.add(aBARResource().withName("MyResource").withContent("The content".getBytes()) .withProcessDefinitionId(345L).withType(BARResourceType.USER_FILTER).build()); repository.add(aBARResource().withName("MyResource2").withContent("The content@".getBytes()) .withProcessDefinitionId(346L).withType(BARResourceType.USER_FILTER).build()); repository.add(aBARResource().withName("MyResource3").withContent("The content3".getBytes()) .withProcessDefinitionId(345L).withType(BARResourceType.USER_FILTER).build()); repository.add(aBARResource().withName("MyConnector").withContent("The content".getBytes()) .withProcessDefinitionId(345L).withType(BARResourceType.CONNECTOR).build()); //when long myResources = repository.getNumberOfBARResourcesOfType(345L, BARResourceType.USER_FILTER); // //then assertThat(myResources).isEqualTo(2); } @Test public void getBarResourceLightOfType_should_get_all_resource_of_type_without_content() { // given SBARResource resource1 = repository .add(aBARResource().withName("MyResource").withContent("The content".getBytes()) .withProcessDefinitionId(345L).withType(BARResourceType.EXTERNAL).build()); repository.add(aBARResource().withName("MyResource2").withContent("The content@".getBytes()) .withProcessDefinitionId(346L).withType(BARResourceType.EXTERNAL).build()); SBARResource resource2 = repository .add(aBARResource().withName("MyResource3").withContent("The content3".getBytes()) .withProcessDefinitionId(345L).withType(BARResourceType.EXTERNAL).build()); repository.add(aBARResource().withName("MyConnector").withContent("The content".getBytes()) .withProcessDefinitionId(345L).withType(BARResourceType.CONNECTOR).build()); //when List myResources = repository.getBARResourcesLightOfType(345L, BARResourceType.EXTERNAL); // //then assertThat(myResources).extracting("id").containsOnly(resource1.getId(), resource2.getId()); for (SBARResourceLight myResource : myResources) { assertThat(myResource).isInstanceOf(SBARResourceLight.class); assertThat(myResource).isNotInstanceOf(SBARResource.class); } } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/resources/TenantResourceServiceQueriesTest.java ================================================ /** * Copyright (C) 2016 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.resources; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; import static org.bonitasoft.engine.test.persistence.builder.TenantResourceBuilder.aTenantResource; import java.util.List; import org.bonitasoft.engine.test.persistence.repository.TenantResourceRepository; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class TenantResourceServiceQueriesTest { @Autowired private TenantResourceRepository repository; @Autowired private JdbcTemplate jdbcTemplate; @Test public void getTenantResource_should_get_one_resource() { // given STenantResource resource = repository.add(aTenantResource().withName("MyResource") .withContent("The content".getBytes()).withType(TenantResourceType.BDM) .withState(STenantResourceState.INSTALLED).lastUpdatedBy(135L).withLastUpdateDate(1973L).build()); repository.add(aTenantResource().withName("MyResource2").withContent("The content@".getBytes()) .withType(TenantResourceType.BDM).build()); // This check is to ensure we added the annotation @Enumerated(EnumType.STRING) on field 'state', // because by default Hibernate converts ENUMs with their ordinal value when storing to Database: repository.flush(); assertThat(jdbcTemplate.queryForObject("select state from tenant_resource where name = 'MyResource'", String.class)).isEqualTo("INSTALLED"); assertThat( jdbcTemplate.queryForObject("select type from tenant_resource where name = 'MyResource'", String.class)) .isEqualTo("BDM"); //when STenantResource myResource = repository.getTenantResource(TenantResourceType.BDM, "MyResource"); // //then assertThat(myResource.getContent()).isEqualTo("The content".getBytes()); assertThat(myResource.lastUpdateDate).isEqualTo(1973L); assertThat(myResource.lastUpdatedBy).isEqualTo(135L); assertThat(myResource.state).isEqualTo(STenantResourceState.INSTALLED); assertThat(myResource.getId()).isEqualTo(resource.getId()); } @Test public void getTenantResourceOfType_should_get_all_resource_of_type() { // given STenantResource resource1 = repository.add(aTenantResource().withName("MyResource") .withContent("The content".getBytes()).withType(TenantResourceType.BDM).build()); STenantResource resource2 = repository.add(aTenantResource().withName("MyResource2") .withContent("The content@".getBytes()).withType(TenantResourceType.BDM).build()); STenantResource resource3 = repository.add(aTenantResource().withName("MyResource3") .withContent("The content3".getBytes()).withType(TenantResourceType.BDM).build()); STenantResource resource4 = repository.add(aTenantResource().withName("MyConnector") .withContent("The content".getBytes()).withType(TenantResourceType.BDM).build()); //when List myResources = repository.getTenantResourcesOfType(TenantResourceType.BDM); // //then assertThat(myResources).extracting("id", "content").containsOnly( tuple(resource1.getId(), resource1.getContent()), tuple(resource2.getId(), resource2.getContent()), tuple(resource3.getId(), resource3.getContent()), tuple(resource4.getId(), resource4.getContent())); } @Test public void getNumberTenantResourceOfType_should_get_all_resource_of_type() { // given repository.add(aTenantResource().withName("MyResource").withContent("The content".getBytes()) .withType(TenantResourceType.BDM).build()); repository.add(aTenantResource().withName("MyResource2").withContent("The content@".getBytes()) .withType(TenantResourceType.BDM).build()); repository.add(aTenantResource().withName("MyResource3").withContent("The content3".getBytes()) .withType(TenantResourceType.BDM).build()); repository.add(aTenantResource().withName("MyConnector").withContent("The content".getBytes()) .withType(TenantResourceType.BDM).build()); // This resources should be excluded by the type filter: repository.add(aTenantResource().withName("excluded").withContent("binary".getBytes()) .withType(TenantResourceType.BDM_ACCESS_CTRL).build()); //when long myResources = repository.getNumberOfTenantResourcesOfType(TenantResourceType.BDM); // //then assertThat(myResources).isEqualTo(4); } @Test public void getTenantResourceLightOfType_should_get_all_resource_of_type_without_content() { // given STenantResource resource1 = repository.add(aTenantResource().withName("MyResource") .withContent("The content".getBytes()).withType(TenantResourceType.BDM).build()); // This resources should be excluded by the type filter: repository.add(aTenantResource().withName("excluded").withContent("binary".getBytes()) .withType(TenantResourceType.BDM_ACCESS_CTRL).build()); STenantResource resource2 = repository.add(aTenantResource().withName("MyResource2") .withContent("The content@".getBytes()).withType(TenantResourceType.BDM).build()); STenantResource resource3 = repository.add(aTenantResource().withName("MyResource3") .withContent("The content3".getBytes()).withType(TenantResourceType.BDM).build()); STenantResource resource4 = repository.add(aTenantResource().withName("MyConnector") .withContent("The content".getBytes()).withType(TenantResourceType.BDM).build()); //when List myResources = repository.getTenantResourcesLightOfType(TenantResourceType.BDM); // //then assertThat(myResources).extracting("id").containsOnly(resource1.getId(), resource2.getId(), resource3.getId(), resource4.getId()); for (STenantResourceLight myResource : myResources) { assertThat(myResource).isInstanceOf(STenantResourceLight.class); assertThat(myResource).isNotInstanceOf(STenantResource.class); } } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/scheduler/SchedulerQueryTest.java ================================================ /** * 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.scheduler; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.bonitasoft.engine.commons.Pair.pair; import static org.bonitasoft.engine.test.persistence.builder.JobDescriptorBuilder.aJobDescriptor; import static org.bonitasoft.engine.test.persistence.builder.JobLogBuilder.aJobLog; import java.util.List; import java.util.Map; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.scheduler.model.SFailedJob; import org.bonitasoft.engine.scheduler.model.SJobDescriptor; import org.bonitasoft.engine.scheduler.model.SJobLog; import org.bonitasoft.engine.scheduler.model.SJobParameter; import org.bonitasoft.engine.test.persistence.jdbc.JdbcRowMapper; import org.bonitasoft.engine.test.persistence.repository.JobRepository; import org.hibernate.internal.util.SerializationHelper; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) @Transactional public class SchedulerQueryTest { @Autowired private JobRepository jobRepository; @Autowired private JdbcTemplate jdbcTemplate; @Test public void getFailedJobsShouldRetrieveAFailedJobIfThereIsAJobLogAndAJobDescriptor() { // given: SJobDescriptor addJobDescriptor = jobRepository.add(aJobDescriptor().build()); jobRepository.add(aJobLog().withJobDescriptorId(addJobDescriptor.getId()).build()); // when: List failedJobs = jobRepository.getFailedJobs(new QueryOptions(0, 10)); // then: assertThat(failedJobs).hasSize(1); } @Test public void getFailedJobsShouldRetrieveZeroFailedJobIfThereIsNoJobLog() { // given: jobRepository.add(aJobDescriptor().build()); // when: List failedJobs = jobRepository.getFailedJobs(new QueryOptions(0, 10)); // then: assertThat(failedJobs).hasSize(0); } @Test public void getFailedJobsShouldRetrieveZeroFailedJobIfThereIsNoJobDescriptor() { // given: jobRepository.add(aJobLog().withJobDescriptorId(1234566789L).build()); // when: List failedJobs = jobRepository.getFailedJobs(new QueryOptions(0, 10)); // then: assertThat(failedJobs).hasSize(0); } @Test public void should_save_and_get_SJobDescriptor() { SJobDescriptor jobDescriptor = jobRepository.add(SJobDescriptor.builder() .jobClassName("com.bonitasoft.JobClass") .description("a job") .jobName("job name") .build()); jobRepository.flush(); Long numberOfSJobDescriptor = jobRepository.selectCount("getNumberOfSJobDescriptor"); PersistentObject searchSJobDescriptor = jobRepository.selectOne("searchSJobDescriptor"); Map jobDescriptorAsMap = jdbcTemplate.queryForObject("SELECT * FROM job_desc", new JdbcRowMapper("ID")); assertThat(numberOfSJobDescriptor).isEqualTo(1); assertThat(searchSJobDescriptor).isEqualTo(jobDescriptor); assertThat(jobDescriptorAsMap).containsOnly( entry("ID", jobDescriptor.getId()), entry("JOBCLASSNAME", "com.bonitasoft.JobClass"), entry("JOBNAME", "job name"), entry("DESCRIPTION", "a job")); } @Test public void should_save_and_get_SJobParameter() { SJobParameter jobParameter = jobRepository.add(SJobParameter.builder() .jobDescriptorId(1234L) .key("paramKeyName") .value("Some string value") .build()); Long numberOfSJobParameters = jobRepository.selectCount("getNumberOfSJobParameter"); PersistentObject jobParameterFromQuery = jobRepository.selectOne("getJobParameters", pair("jobDescriptorId", 1234L)); Map jobParameterAsMap = jdbcTemplate.queryForObject("SELECT * FROM job_param", new JdbcRowMapper("ID", "JOBDESCRIPTORID")); assertThat(numberOfSJobParameters).isEqualTo(1); assertThat(jobParameterFromQuery).isEqualTo(jobParameter); assertThat(jobParameterAsMap).containsOnly( entry("ID", jobParameter.getId()), entry("JOBDESCRIPTORID", 1234L), entry("KEY_", "paramKeyName"), entry("VALUE_", SerializationHelper.serialize("Some string value"))); } @Test public void should_save_and_get_SJobLog() { SJobLog jobLog = jobRepository.add(SJobLog.builder() .jobDescriptorId(1234L) .lastMessage("the last message") .retryNumber(13L) .lastUpdateDate(555555L) .build()); Long numberOfSJobLog = jobRepository.selectCount("getNumberOfSJobLog"); PersistentObject jobLogFromQuery = jobRepository.selectOne("searchSJobLog"); Map jobLogAsMap = jdbcTemplate.queryForObject("SELECT * FROM job_log", new JdbcRowMapper("ID", "JOBDESCRIPTORID", "RETRYNUMBER", "LASTUPDATEDATE")); assertThat(numberOfSJobLog).isEqualTo(1); assertThat(jobLogFromQuery).isEqualTo(jobLog); assertThat(jobLogAsMap).containsOnly( entry("ID", jobLog.getId()), entry("JOBDESCRIPTORID", 1234L), entry("RETRYNUMBER", 13L), entry("LASTUPDATEDATE", 555555L), entry("LASTMESSAGE", "the last message")); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/TestLocalSessionFactoryBuilder.java ================================================ /** * 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.persistence; import org.bonitasoft.engine.persistence.PostgresMaterializedBlobType; import org.bonitasoft.engine.persistence.PostgresMaterializedClobType; import org.bonitasoft.engine.persistence.PostgresXMLType; import org.bonitasoft.engine.persistence.XMLType; import org.bonitasoft.engine.services.Vendor; import org.hibernate.Interceptor; import org.hibernate.SessionFactory; import org.springframework.orm.hibernate5.LocalSessionFactoryBean; import org.springframework.orm.hibernate5.LocalSessionFactoryBuilder; public class TestLocalSessionFactoryBuilder extends LocalSessionFactoryBean { private Interceptor interceptor; @Override protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) { Vendor vendor = Vendor.fromHibernateConfiguration(sfb); //register type before loading mappings/entities, type should be present before loading JPA entities switch (vendor) { case ORACLE, OTHER, SQLSERVER: sfb.registerTypeOverride(XMLType.INSTANCE); break; case MYSQL: System.setProperty("hibernate.dialect.storage_engine", "innodb"); sfb.registerTypeOverride(XMLType.INSTANCE); break; case POSTGRES: sfb.registerTypeOverride(new PostgresMaterializedBlobType()); sfb.registerTypeOverride(new PostgresMaterializedClobType()); sfb.registerTypeOverride(PostgresXMLType.INSTANCE); break; } if (interceptor != null) { sfb.setInterceptor(interceptor); } return super.buildSessionFactory(sfb); } public void setInterceptor(Interceptor interceptor) { this.interceptor = interceptor; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ActivityInstanceBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; /** * @author Julien Reboul */ public abstract class ActivityInstanceBuilder> extends FlowNodeInstanceBuilder { protected long abortedByBoundaryEventId = 0; public B withAbortedByBoundary(final long abortedByBoundaryEventId) { this.abortedByBoundaryEventId = abortedByBoundaryEventId; return thisBuilder; } @Override protected T fill(T persistent) { super.fill(persistent); persistent.setAbortedByBoundary(abortedByBoundaryEventId); return persistent; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ActorBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.actor.mapping.model.SActor; public class ActorBuilder extends PersistentObjectBuilder { private long scopeId; private boolean initiator; @Override ActorBuilder getThisBuilder() { return this; } public ActorBuilder withScopeId(long scopeId) { this.scopeId = scopeId; return this; } public ActorBuilder whoIsInitiator() { this.initiator = true; return this; } public static ActorBuilder anActor() { return new ActorBuilder(); } @Override SActor _build() { SActor sActor = new SActor(); sActor.setInitiator(initiator); sActor.setScopeId(scopeId); return sActor; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ActorMemberBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.actor.mapping.model.SActorMember; public class ActorMemberBuilder extends PersistentObjectBuilder { private long actorId; private Long userId; private Long groupId; private Long roleId; @Override ActorMemberBuilder getThisBuilder() { return this; } public static ActorMemberBuilder anActorMember() { return new ActorMemberBuilder(); } @Override SActorMember _build() { SActorMember actorMember = new SActorMember(); actorMember.setActorId(actorId); if (userId != null) actorMember.setUserId(userId); if (groupId != null) actorMember.setGroupId(groupId); if (roleId != null) actorMember.setRoleId(roleId); return actorMember; } public ActorMemberBuilder forActor(SActor actor) { this.actorId = actor.getId(); return this; } public ActorMemberBuilder withUserId(long userId) { this.userId = userId; return this; } public ActorMemberBuilder withGroupId(long groupId) { this.groupId = groupId; return this; } public ActorMemberBuilder withRoleId(long roleId) { this.roleId = roleId; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ApplicationBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.business.application.model.AbstractSApplication; import org.bonitasoft.engine.business.application.model.SApplicationState; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; /** * @author Elias Ricken de Medeiros */ public class ApplicationBuilder extends PersistentObjectBuilder { private String name; private String version; private String path; private String displayName; private Long layoutId; private Long themeId; private Long profileId; public static ApplicationBuilder anApplication() { return new ApplicationBuilder(); } @Override SApplicationWithIcon _build() { SApplicationWithIcon application = new SApplicationWithIcon(name, displayName, version, System.currentTimeMillis(), 21, SApplicationState.DEACTIVATED.name()); application.setIconPath(path); application.setProfileId(profileId); application.setThemeId(themeId); application.setLayoutId(layoutId); return application; } public ApplicationBuilder withToken(final String name) { this.name = name; return this; } public ApplicationBuilder withDisplayName(final String displayName) { this.displayName = displayName; return this; } public ApplicationBuilder withVersion(final String version) { this.version = version; return this; } public ApplicationBuilder withPath(final String path) { this.path = path; return this; } public ApplicationBuilder withLayout(final Long layoutId) { this.layoutId = layoutId; return this; } public ApplicationBuilder withTheme(final Long themeId) { this.themeId = themeId; return this; } public ApplicationBuilder withProfile(final long profileId) { this.profileId = profileId; return this; } @Override ApplicationBuilder getThisBuilder() { return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ApplicationMenuBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.business.application.model.SApplicationMenu; /** * @author Elias Ricken de Medeiros */ public class ApplicationMenuBuilder extends PersistentObjectBuilder { public static ApplicationMenuBuilder anApplicationMenu() { return new ApplicationMenuBuilder(); } private String displayName; private long applicationId; private Long applicationPageId; private int index; private Long parentId; @Override SApplicationMenu _build() { final SApplicationMenu menu = new SApplicationMenu(displayName, applicationId, applicationPageId, index); menu.setParentId(parentId); return menu; } public ApplicationMenuBuilder withDisplayName(final String displayName) { this.displayName = displayName; return this; } public ApplicationMenuBuilder withApplicationId(final Long applicationId) { this.applicationId = applicationId; return this; } public ApplicationMenuBuilder withApplicationPageId(final Long applicationPageId) { this.applicationPageId = applicationPageId; return this; } public ApplicationMenuBuilder withIndex(final int index) { this.index = index; return this; } public ApplicationMenuBuilder withParentId(final long parentId) { this.parentId = parentId; return this; } @Override ApplicationMenuBuilder getThisBuilder() { return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ApplicationPageBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.business.application.model.SApplicationPage; /** * @author Elias Ricken de Medeiros */ public class ApplicationPageBuilder extends PersistentObjectBuilder { private long applicationId; private long pageId; private String token; public static ApplicationPageBuilder anApplicationPage() { return new ApplicationPageBuilder(); } @Override SApplicationPage _build() { return new SApplicationPage(applicationId, pageId, token); } public ApplicationPageBuilder withApplicationId(final long applicationId) { this.applicationId = applicationId; return this; } public ApplicationPageBuilder withPageId(final long pageId) { this.pageId = pageId; return this; } public ApplicationPageBuilder withToken(final String token) { this.token = token; return this; } @Override ApplicationPageBuilder getThisBuilder() { return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/BARResourceBuilder.java ================================================ /** * Copyright (C) 2015 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.persistence.builder; import org.bonitasoft.engine.resources.BARResourceType; import org.bonitasoft.engine.resources.SBARResource; public class BARResourceBuilder extends PersistentObjectBuilder { private String name; private Long processDefinitionId; private byte[] content; private BARResourceType type; public static BARResourceBuilder aBARResource() { return new BARResourceBuilder(); } @Override public SBARResource _build() { return new SBARResource(name, type, processDefinitionId, content); } public BARResourceBuilder withName(final String name) { this.name = name; return this; } public BARResourceBuilder withType(final BARResourceType type) { this.type = type; return this; } public BARResourceBuilder withContent(final byte[] content) { this.content = content; return this; } public BARResourceBuilder withProcessDefinitionId(final Long processDefinitionId) { this.processDefinitionId = processDefinitionId; return this; } @Override BARResourceBuilder getThisBuilder() { return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/BoundaryInstanceBuilder.java ================================================ /** * Copyright (C) 2016 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.persistence.builder; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; /** * @author Baptiste Mesta */ public class BoundaryInstanceBuilder extends FlowNodeInstanceBuilder { private long activityInstanceId; public static BoundaryInstanceBuilder aBoundary() { return new BoundaryInstanceBuilder(); } @Override BoundaryInstanceBuilder getThisBuilder() { return this; } @Override SBoundaryEventInstance _build() { final SBoundaryEventInstance boundaryEventInstance = new SBoundaryEventInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); boundaryEventInstance.setActivityInstanceId(activityInstanceId); return boundaryEventInstance; } public BoundaryInstanceBuilder withActivity(final long activityId) { this.activityInstanceId = activityId; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/CallActivityInstanceBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance; /** * @author Julien Reboul */ public class CallActivityInstanceBuilder extends ActivityInstanceBuilder { @Override CallActivityInstanceBuilder getThisBuilder() { return this; } public static CallActivityInstanceBuilder aCallActivityInstanceBuilder() { return new CallActivityInstanceBuilder(); } @Override SCallActivityInstance _build() { return new SCallActivityInstance(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ConnectorInstanceBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.core.process.instance.model.SAbstractConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo; /** * @author Julien Reboul */ public class ConnectorInstanceBuilder extends PersistentObjectBuilder { private boolean withFailureInfo = false; private String state; private String connectorId; private ConnectorEvent activationEvent; private long containerId; private String containerType; private String version; private int executionOrder; private String exceptionMessage; private String stackTrace; private String name; public static ConnectorInstanceBuilder aConnectorInstance() { return new ConnectorInstanceBuilder(); } @Override ConnectorInstanceBuilder getThisBuilder() { return this; } @Override SAbstractConnectorInstance _build() { SAbstractConnectorInstance connectorInstance; if (withFailureInfo) { connectorInstance = new SConnectorInstanceWithFailureInfo(); ((SConnectorInstanceWithFailureInfo) connectorInstance).setExceptionMessage(exceptionMessage); ((SConnectorInstanceWithFailureInfo) connectorInstance).setStackTrace(stackTrace); } else { connectorInstance = new SConnectorInstance(); } connectorInstance.setState(state); connectorInstance.setConnectorId(connectorId); connectorInstance.setActivationEvent(activationEvent); connectorInstance.setContainerId(containerId); connectorInstance.setContainerType(containerType); connectorInstance.setVersion(version); connectorInstance.setExecutionOrder(executionOrder); connectorInstance.setName(name); return connectorInstance; } public ConnectorInstanceBuilder setName(final String name) { this.name = name; return this; } public ConnectorInstanceBuilder setState(final String state) { this.state = state; return this; } public ConnectorInstanceBuilder withFailureInfo(final boolean withFailureInfo) { this.withFailureInfo = withFailureInfo; return this; } public ConnectorInstanceBuilder setConnectorId(final String connectorId) { this.connectorId = connectorId; return this; } public ConnectorInstanceBuilder setActivationEvent(final ConnectorEvent activationEvent) { this.activationEvent = activationEvent; return this; } public ConnectorInstanceBuilder setContainerId(final long containerId) { this.containerId = containerId; return this; } public ConnectorInstanceBuilder setContainerType(final String containerType) { this.containerType = containerType; return this; } public ConnectorInstanceBuilder setVersion(final String version) { this.version = version; return this; } public ConnectorInstanceBuilder setExecutionOrder(final int executionOrder) { this.executionOrder = executionOrder; return this; } public ConnectorInstanceBuilder setExceptionMessage(String message) { this.exceptionMessage = message; return this; } public ConnectorInstanceBuilder setStackTrace(String stackTrace) { this.stackTrace = stackTrace; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/CustomUserInfoDefinitionBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; /** * @author Elias Ricken de Medeiros */ public class CustomUserInfoDefinitionBuilder extends PersistentObjectBuilder { private String name; public static CustomUserInfoDefinitionBuilder aCustomUserInfoDefinition() { return new CustomUserInfoDefinitionBuilder(); } @Override CustomUserInfoDefinitionBuilder getThisBuilder() { return this; } @Override SCustomUserInfoDefinition _build() { SCustomUserInfoDefinition infoDef = new SCustomUserInfoDefinition(); infoDef.setName(name); return infoDef; } public CustomUserInfoDefinitionBuilder withName(String name) { this.name = name; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/CustomUserInfoValueBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.identity.model.SCustomUserInfoValue; /** * @author Elias Ricken de Medeiros */ public class CustomUserInfoValueBuilder extends PersistentObjectBuilder { private long infoDefId; private long userId; private String value; public static CustomUserInfoValueBuilder aCustomUserInfoValue() { return new CustomUserInfoValueBuilder(); } @Override CustomUserInfoValueBuilder getThisBuilder() { return this; } @Override SCustomUserInfoValue _build() { SCustomUserInfoValue infoValueImpl = new SCustomUserInfoValue(); infoValueImpl.setUserId(userId); infoValueImpl.setDefinitionId(infoDefId); infoValueImpl.setValue(value); return infoValueImpl; } public CustomUserInfoValueBuilder withCustomUserInfoDefinitionId(long infoDefId) { this.infoDefId = infoDefId; return this; } public CustomUserInfoValueBuilder withUserId(long userId) { this.userId = userId; return this; } public CustomUserInfoValueBuilder withValue(String value) { this.value = value; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/FlowNodeInstanceBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; public abstract class FlowNodeInstanceBuilder> extends PersistentObjectBuilder { protected int stateId; protected String stateName; protected long lastUpdateDate; protected String name; protected int loopCounter; protected long executedBy; protected long executedBySubstitute; protected boolean stateExecuting; protected long flowNodeDefinitionId; protected long rootContainerId; protected long parentContainerId; protected SStateCategory stateCategory = SStateCategory.NORMAL; protected String description; protected long logicalGroup1; protected long logicalGroup2; protected long logicalGroup3; protected long logicalGroup4; protected boolean terminal; protected boolean stable; @Override protected T fill(final T persistent) { super.fill(persistent); persistent.setDescription(description); persistent.setExecutedBy(executedBy); persistent.setExecutedBySubstitute(executedBySubstitute); persistent.setFlowNodeDefinitionId(flowNodeDefinitionId); persistent.setLastUpdateDate(lastUpdateDate); persistent.setLoopCounter(loopCounter); persistent.setName(name); persistent.setParentContainerId(parentContainerId); persistent.setRootContainerId(rootContainerId); persistent.setStable(stable); persistent.setStateCategory(stateCategory); persistent.setStateExecuting(stateExecuting); persistent.setStateId(stateId); persistent.setStateName(stateName); persistent.setTerminal(terminal); persistent.setLogicalGroup(0, logicalGroup1); persistent.setLogicalGroup(1, logicalGroup2); persistent.setLogicalGroup(2, logicalGroup3); persistent.setLogicalGroup(3, logicalGroup4); return persistent; } public B withFlowNodeDefinitionId(final long flowNodeDefinitionId) { this.flowNodeDefinitionId = flowNodeDefinitionId; return thisBuilder; } public B withTerminal(final boolean terminal) { this.terminal = terminal; return thisBuilder; } public B withStable(final boolean stable) { this.stable = stable; return thisBuilder; } public B withStateExecuting(final boolean stateExecuting) { this.stateExecuting = stateExecuting; return thisBuilder; } public B withExecutedBy(final long executedBy) { this.executedBy = executedBy; return thisBuilder; } public B withExecutedBySubstitute(final long executedBySubstitute) { this.executedBySubstitute = executedBySubstitute; return thisBuilder; } public B withId(final long id) { this.id = id; return thisBuilder; } public B withName(final String name) { this.name = name; return thisBuilder; } public B withStateName(final String stateName) { this.stateName = stateName; return thisBuilder; } public B withDescription(final String description) { this.description = description; return thisBuilder; } public B withStateId(final int stateId) { this.stateId = stateId; return thisBuilder; } public B withLastUpdateDate(final long lastUpdateDate) { this.lastUpdateDate = lastUpdateDate; return thisBuilder; } public B withRootContainerId(final long containerId) { this.rootContainerId = containerId; return thisBuilder; } public B withParentContainerId(final long processInstanceId) { this.parentContainerId = processInstanceId; return thisBuilder; } public B withLoopCounter(final int loopCounter) { this.loopCounter = loopCounter; return thisBuilder; } public B withStateCategory(final SStateCategory stateCategory) { this.stateCategory = stateCategory; return thisBuilder; } public B withLogicalGroup1(final long logicalGroup) { this.logicalGroup1 = logicalGroup; return thisBuilder; } public B withLogicalGroup2(final long logicalGroup) { this.logicalGroup2 = logicalGroup; return thisBuilder; } public B withLogicalGroup3(final long logicalGroup) { this.logicalGroup3 = logicalGroup; return thisBuilder; } public B withLogicalGroup4(final long logicalGroup) { this.logicalGroup4 = logicalGroup; return thisBuilder; } public B withProcessDefinition(long processDefinitionId) { return withLogicalGroup1(processDefinitionId); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/GatewayInstanceBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.core.process.definition.model.SGatewayType; import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance; /** * @author Julien Reboul */ public class GatewayInstanceBuilder extends FlowNodeInstanceBuilder { private SGatewayType gatewayType; private String hitBys = ""; public static GatewayInstanceBuilder aGatewayInstanceBuilder() { return new GatewayInstanceBuilder(); } private GatewayInstanceBuilder() { } @Override GatewayInstanceBuilder getThisBuilder() { return this; } @Override SGatewayInstance _build() { return new SGatewayInstance(); } @Override protected SGatewayInstance fill(SGatewayInstance persistent) { super.fill(persistent); persistent.setHitBys(hitBys); persistent.setGatewayType(gatewayType); return persistent; } public GatewayInstanceBuilder withGatewayType(final SGatewayType gatewayType) { this.gatewayType = gatewayType; return thisBuilder; } public GatewayInstanceBuilder withHitBys(final String hitBys) { this.hitBys = hitBys; return thisBuilder; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/GroupBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.identity.model.SGroup; /** * @author Danila Mazour */ public class GroupBuilder extends PersistentObjectBuilder { private String name; private String parentPath; public static GroupBuilder aGroup() { return new GroupBuilder(); } @Override GroupBuilder getThisBuilder() { return this; } @Override SGroup _build() { SGroup group = new SGroup(); group.setId(this.id); group.setName(this.name); group.setParentPath(parentPath); return group; } public GroupBuilder forParentPath(String path) { this.parentPath = path; return this; } public GroupBuilder forGroupName(String name) { this.name = name; return this; } public GroupBuilder forGroupId(Long id) { this.id = id; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/JobDescriptorBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.scheduler.model.SJobDescriptor; /** * @author Emmanuel Duchastenier */ public class JobDescriptorBuilder extends PersistentObjectBuilder { public static JobDescriptorBuilder aJobDescriptor() { return new JobDescriptorBuilder(); } @Override JobDescriptorBuilder getThisBuilder() { return this; } @Override SJobDescriptor _build() { return new SJobDescriptor(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/JobLogBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.scheduler.model.SJobLog; /** * @author Emmanuel Duchastenier */ public class JobLogBuilder extends PersistentObjectBuilder { long jobDescriptorId; public static JobLogBuilder aJobLog() { return new JobLogBuilder(); } @Override JobLogBuilder getThisBuilder() { return this; } @Override SJobLog _build() { // lastUpdateDate must not be null for the SFailedJobImpl constructor to be called normally: SJobLog sJobLog = new SJobLog(jobDescriptorId); sJobLog.setLastUpdateDate(System.currentTimeMillis()); return sJobLog; } public JobLogBuilder withJobDescriptorId(final long jobDescriptorId) { this.jobDescriptorId = jobDescriptorId; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/LoopActivityInstanceBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance; public class LoopActivityInstanceBuilder extends FlowNodeInstanceBuilder { public static LoopActivityInstanceBuilder aLoopActivity() { return new LoopActivityInstanceBuilder(); } @Override LoopActivityInstanceBuilder getThisBuilder() { return this; } @Override SLoopActivityInstance _build() { final SLoopActivityInstance loopActivityInstance = new SLoopActivityInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); loopActivityInstance.setLoopCounter(loopCounter); return loopActivityInstance; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/MessageInstanceBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance; /** * @author Emmanuel Duchastenier * @author Laurent Leseigneur */ public class MessageInstanceBuilder extends PersistentObjectBuilder { public static MessageInstanceBuilder aMessageInstance() { return new MessageInstanceBuilder(); } @Override MessageInstanceBuilder getThisBuilder() { return this; } private boolean handled; private long creationDate; @Override SMessageInstance _build() { SMessageInstance messageInstance = new SMessageInstance(); messageInstance.setHandled(handled); messageInstance.setCreationDate(creationDate); return messageInstance; } public MessageInstanceBuilder handled(final boolean handled) { this.handled = handled; return this; } public MessageInstanceBuilder creationDate(final long creationDate) { this.creationDate = creationDate; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/PageBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.page.AbstractSPage; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.page.SPageWithContent; public class PageBuilder extends PersistentObjectBuilder { private String name; private String description; private String displayName; private long installationDate; private long installedBy; private boolean provided; private long lastModificationDate; private long lastUpdatedBy; private String contentName; private String contentType; private long processDefinitionId; private byte[] content; private boolean editable; public static PageBuilder aPage() { return new PageBuilder(); } @Override public SPageWithContent _build() { final SPage sPage = new SPage(name, description, displayName, installationDate, installedBy, provided, editable, lastModificationDate, lastUpdatedBy, contentName); sPage.setProcessDefinitionId(processDefinitionId); return new SPageWithContent(sPage, content); } public PageBuilder withName(final String name) { this.name = name; return this; } public PageBuilder withDescription(final String description) { this.description = description; return this; } public PageBuilder withDisplayName(final String displayName) { this.displayName = displayName; return this; } public PageBuilder withInstallationDate(final long installationDate) { this.installationDate = installationDate; return this; } public PageBuilder withInstalledBy(final long installedBy) { this.installedBy = installedBy; return this; } public PageBuilder withProvided(final boolean provided) { this.provided = provided; return this; } public PageBuilder withLastModificationDate(final long lastModificationDate) { this.lastModificationDate = lastModificationDate; return this; } public PageBuilder withLastUpdatedBy(final long lastUpdatedBy) { this.lastUpdatedBy = lastUpdatedBy; return this; } public PageBuilder withContentName(final String contentName) { this.contentName = contentName; return this; } public PageBuilder withContent(final byte[] content) { this.content = content; return this; } public PageBuilder withContentType(final String contentType) { this.contentType = contentType; return this; } public PageBuilder withProcessDefinitionId(final Long processDefinitionId) { this.processDefinitionId = processDefinitionId; return this; } public PageBuilder makeEditable(Boolean editable) { this.editable = editable; return this; } @Override PageBuilder getThisBuilder() { return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/PendingActivityMappingBuilder.java ================================================ /** * 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.persistence.builder; import java.util.Random; import org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping; public class PendingActivityMappingBuilder extends PersistentObjectBuilder { private long activityId = new Random().nextLong(); private long userId; private long actorId; public static PendingActivityMappingBuilder aPendingActivityMapping() { return new PendingActivityMappingBuilder(); } @Override PendingActivityMappingBuilder getThisBuilder() { return this; } @Override SPendingActivityMapping _build() { return SPendingActivityMapping.builder().activityId(activityId) .userId(userId) .actorId(actorId).build(); } public PendingActivityMappingBuilder withUserId(final long userId) { this.userId = userId; return this; } public PendingActivityMappingBuilder withActorId(final long actorId) { this.actorId = actorId; return this; } public PendingActivityMappingBuilder withActivityId(final long activityId) { this.activityId = activityId; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/PersistentObjectBuilder.java ================================================ /** * 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.persistence.builder; import java.util.Random; import org.bonitasoft.engine.persistence.PersistentObject; public abstract class PersistentObjectBuilder> { protected long id = new Random().nextLong(); protected T persistentObject; protected B thisBuilder; public PersistentObjectBuilder() { thisBuilder = getThisBuilder(); } public T build() { persistentObject = _build(); fill(persistentObject); return persistentObject; } protected T fill(T persistent) { persistent.setId(id); return persistent; } abstract B getThisBuilder(); abstract T _build(); } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ProcessInstanceBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; public class ProcessInstanceBuilder extends PersistentObjectBuilder { private long processDefinitionId; private String name; private String description; private int stateId; private long startDate; private long startedBy; private long startedBySubstitute; private long endDate; private long lastUpdate; private long containerId; private long rootProcessInstanceId = -1; private long callerId = -1; private SFlowNodeType callerType; private long interruptingEventId = -1; private SStateCategory stateCategory; @Override ProcessInstanceBuilder getThisBuilder() { return this; } public static ProcessInstanceBuilder aProcessInstance() { return new ProcessInstanceBuilder(); } @Override SProcessInstance _build() { SProcessInstance processInstance = new SProcessInstance(name, processDefinitionId); processInstance.setCallerId(this.callerId); processInstance.setCallerType(this.callerType); processInstance.setContainerId(this.containerId); processInstance.setDescription(description); processInstance.setEndDate(endDate); processInstance.setInterruptingEventId(interruptingEventId); processInstance.setLastUpdate(lastUpdate); processInstance.setRootProcessInstanceId(rootProcessInstanceId); processInstance.setStartDate(startDate); processInstance.setStartedBy(startedBy); processInstance.setStartedBySubstitute(startedBySubstitute); processInstance.setStateCategory(stateCategory); processInstance.setStateId(stateId); return processInstance; } public ProcessInstanceBuilder withId(long id) { this.id = id; return this; } public ProcessInstanceBuilder withName(String name) { this.name = name; return this; } public ProcessInstanceBuilder withProcessDefinitionId(long processDefinitionId) { this.processDefinitionId = processDefinitionId; return this; } public ProcessInstanceBuilder withDescription(final String description) { this.description = description; return this; } public ProcessInstanceBuilder withStateId(final int stateId) { this.stateId = stateId; return this; } public ProcessInstanceBuilder withStartDate(final long startDate) { this.startDate = startDate; return this; } public ProcessInstanceBuilder withStartedBy(final long startedBy) { this.startedBy = startedBy; return this; } public ProcessInstanceBuilder withStartedBySubstitute(final long startedBySubstitute) { this.startedBySubstitute = startedBySubstitute; return this; } public ProcessInstanceBuilder withEndDate(final long endDate) { this.endDate = endDate; return this; } public ProcessInstanceBuilder withLastUpdate(final long lastUpdate) { this.lastUpdate = lastUpdate; return this; } public ProcessInstanceBuilder withContainerId(final long containerId) { this.containerId = containerId; return this; } public ProcessInstanceBuilder withRootProcessInstanceId(final long rootProcessInstanceId) { this.rootProcessInstanceId = rootProcessInstanceId; return this; } public ProcessInstanceBuilder withCallerId(final long callerId) { this.callerId = callerId; return this; } public ProcessInstanceBuilder withCallerType(final SFlowNodeType callerType) { this.callerType = callerType; return this; } public ProcessInstanceBuilder withInterruptingEventId(final long interruptingEventId) { this.interruptingEventId = interruptingEventId; return this; } public ProcessInstanceBuilder withStateCategory(final SStateCategory processStateCategory) { stateCategory = processStateCategory; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ProfileBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.profile.model.SProfile; /** * @author Elias Ricken de Medeiros */ public class ProfileBuilder extends PersistentObjectBuilder { private String name; public static ProfileBuilder aProfile() { return new ProfileBuilder(); } @Override ProfileBuilder getThisBuilder() { return this; } @Override SProfile _build() { return SProfile.builder().name(name).build(); } public ProfileBuilder withName(String name) { this.name = name; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/ProfileMemberBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.profile.model.SProfileMember; /** * @author Emmanuel Duchastenier */ public class ProfileMemberBuilder extends PersistentObjectBuilder { private long userId = -1; private long groupId = -1; private long roleId = -1; private long profileId; public static ProfileMemberBuilder aProfileMember() { return new ProfileMemberBuilder(); } @Override ProfileMemberBuilder getThisBuilder() { return this; } @Override SProfileMember _build() { return SProfileMember.builder() .profileId(profileId) .groupId(groupId) .roleId(roleId) .userId(userId).build(); } public ProfileMemberBuilder withProfileId(long profileId) { this.profileId = profileId; return this; } public ProfileMemberBuilder withUserId(long userId) { this.userId = userId; return this; } public ProfileMemberBuilder withGroupId(long groupId) { this.groupId = groupId; return this; } public ProfileMemberBuilder withRoleId(long roleId) { this.roleId = roleId; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/RoleBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.identity.model.SRole; /** * @author Danila Mazour */ public class RoleBuilder extends PersistentObjectBuilder { private String name; public static RoleBuilder aRole() { return new RoleBuilder(); } @Override RoleBuilder getThisBuilder() { return this; } @Override SRole _build() { SRole role = new SRole(); role.setName(this.name); role.setId(this.id); return role; } public RoleBuilder forRoleName(String name) { this.name = name; return this; } public RoleBuilder forRoleId(Long id) { this.id = id; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/SupervisorBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; public class SupervisorBuilder extends PersistentObjectBuilder { private long processDefId; private long groupId = 0; private long roleId = 0; private long userId = 0; public static SupervisorBuilder aSupervisor() { return new SupervisorBuilder(); } @Override SupervisorBuilder getThisBuilder() { return this; } @Override public SProcessSupervisor _build() { final SProcessSupervisor user = new SProcessSupervisor(); user.setId(id); user.setGroupId(groupId); user.setRoleId(roleId); user.setUserId(userId); user.setProcessDefId(processDefId); return user; } public SupervisorBuilder withProcessDefinitionId(final long processDefId) { this.processDefId = processDefId; return this; } public SupervisorBuilder withId(final long id) { this.id = id; return this; } public SupervisorBuilder withGroupId(final long groupId) { this.groupId = groupId; return this; } public SupervisorBuilder withUserId(final long userId) { this.userId = userId; return this; } public SupervisorBuilder withroleId(final long roleId) { this.roleId = roleId; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/TenantResourceBuilder.java ================================================ /** * Copyright (C) 2016 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.persistence.builder; import org.bonitasoft.engine.resources.STenantResource; import org.bonitasoft.engine.resources.STenantResourceState; import org.bonitasoft.engine.resources.TenantResourceType; public class TenantResourceBuilder extends PersistentObjectBuilder { private String name; private byte[] content; private TenantResourceType type; private long lastUpdatedBy; private long lastUpdateDate; private STenantResourceState state; public static TenantResourceBuilder aTenantResource() { return new TenantResourceBuilder(); } @Override public STenantResource _build() { return new STenantResource(name, type, content, lastUpdatedBy, lastUpdateDate, state); } public TenantResourceBuilder withName(final String name) { this.name = name; return this; } public TenantResourceBuilder withType(final TenantResourceType type) { this.type = type; return this; } public TenantResourceBuilder withContent(final byte[] content) { this.content = content; return this; } public TenantResourceBuilder lastUpdatedBy(final long userId) { this.lastUpdatedBy = userId; return this; } public TenantResourceBuilder withState(STenantResourceState state) { this.state = state; return this; } public TenantResourceBuilder withLastUpdateDate(long date) { this.lastUpdateDate = date; return this; } @Override TenantResourceBuilder getThisBuilder() { return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/UserBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.identity.model.SUser; public class UserBuilder extends PersistentObjectBuilder { private String userName = "userName" + id; private long managerUserId = 0; public static UserBuilder aUser() { return new UserBuilder(); } @Override UserBuilder getThisBuilder() { return this; } @Override public SUser _build() { SUser user = new SUser(); user.setFirstName("aFirstName" + id); user.setLastName("aLastName" + id); user.setUserName(userName); user.setManagerUserId(managerUserId); user.setEnabled(true); return user; } public UserBuilder withId(long id) { this.id = id; return this; } public UserBuilder withUserName(String userName) { this.userName = userName; return this; } public UserBuilder withManager(long managerUserId) { this.managerUserId = managerUserId; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/UserMembershipBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserMembership; public class UserMembershipBuilder extends PersistentObjectBuilder { private long groupId; private long userId; private long roleId; public static UserMembershipBuilder aUserMembership() { return new UserMembershipBuilder(); } @Override UserMembershipBuilder getThisBuilder() { return this; } @Override SUserMembership _build() { SUserMembership membership = new SUserMembership(); membership.setGroupId(groupId); membership.setUserId(userId); membership.setRoleId(roleId); return membership; } public UserMembershipBuilder forUser(final SUser user) { this.userId = user.getId(); return this; } public UserMembershipBuilder forUser(final long userId) { this.userId = userId; return this; } public UserMembershipBuilder memberOf(final long groupId, final long roleId) { this.groupId = groupId; this.roleId = roleId; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/UserTaskInstanceBuilder.java ================================================ /** * 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.persistence.builder; import org.bonitasoft.engine.core.process.instance.model.STaskPriority; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; /** * @author Julien Reboul */ public class UserTaskInstanceBuilder extends ActivityInstanceBuilder { public static UserTaskInstanceBuilder aUserTask() { return new UserTaskInstanceBuilder(); } private long assigneeId; private long actorId = 10; @Override UserTaskInstanceBuilder getThisBuilder() { return this; } @Override SUserTaskInstance _build() { final SUserTaskInstance userTaskInstanceImpl = new SUserTaskInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, actorId, STaskPriority.NORMAL, logicalGroup1, logicalGroup2); userTaskInstanceImpl.setAssigneeId(assigneeId); return userTaskInstanceImpl; } public UserTaskInstanceBuilder withRootProcessInstanceId(final long rootProcessInstanceId) { this.logicalGroup2 = rootProcessInstanceId; return this; } public UserTaskInstanceBuilder withAssigneeId(final long assigneeId) { this.assigneeId = assigneeId; return this; } public UserTaskInstanceBuilder withActorId(final long actorId) { this.actorId = actorId; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/WaitingMessageEventBuilder.java ================================================ /** * 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.persistence.builder; import static org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory.PROGRESS_FREE_KEY; import static org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory.PROGRESS_IN_TREATMENT_KEY; import org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent; /** * @author Emmanuel Duchastenier * @author Laurent Leseigneur */ public class WaitingMessageEventBuilder extends PersistentObjectBuilder { private SWaitingMessageEvent event = new SWaitingMessageEvent(); private boolean inProgress; public static WaitingMessageEventBuilder aWaitingEvent() { return new WaitingMessageEventBuilder(); } @Override WaitingMessageEventBuilder getThisBuilder() { return this; } @Override SWaitingMessageEvent _build() { event.setProgress(inProgress ? PROGRESS_IN_TREATMENT_KEY : PROGRESS_FREE_KEY); return event; } public WaitingMessageEventBuilder withEventType(SBPMEventType eventType) { event.setEventType(eventType); return this; } public WaitingMessageEventBuilder inProgress(final boolean inProgress) { this.inProgress = inProgress; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/archive/ArchivedFlowNodeInstanceBuilder.java ================================================ /** * 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.persistence.builder.archive; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; public abstract class ArchivedFlowNodeInstanceBuilder> extends ArchivedPersistentObjectBuilder { protected int stateId; protected String stateName; protected long lastUpdateDate; protected String name; protected int loopCounter; protected long executedBy; protected long executedBySubstitute; protected long flowNodeDefinitionId; protected long rootContainerId; protected long parentContainerId; protected String description; protected long logicalGroup1; protected long logicalGroup2; protected long logicalGroup3; protected long logicalGroup4; protected boolean terminal; protected boolean stable; @Override protected T fill(final T persistent) { super.fill(persistent); persistent.setDescription(description); persistent.setExecutedBy(executedBy); persistent.setExecutedBySubstitute(executedBySubstitute); persistent.setFlowNodeDefinitionId(flowNodeDefinitionId); persistent.setLastUpdateDate(lastUpdateDate); persistent.setName(name); persistent.setParentContainerId(parentContainerId); persistent.setRootContainerId(rootContainerId); persistent.setStable(stable); persistent.setStateId(stateId); persistent.setStateName(stateName); persistent.setTerminal(terminal); persistent.setLogicalGroup(0, logicalGroup1); persistent.setLogicalGroup(1, logicalGroup2); persistent.setLogicalGroup(2, logicalGroup3); persistent.setLogicalGroup(3, logicalGroup4); return persistent; } public B withFlowNodeDefinitionId(final long flowNodeDefinitionId) { this.flowNodeDefinitionId = flowNodeDefinitionId; return thisBuilder; } public B withTerminal(final boolean terminal) { this.terminal = terminal; return thisBuilder; } public B withStable(final boolean stable) { this.stable = stable; return thisBuilder; } public B withExecutedBy(final long executedBy) { this.executedBy = executedBy; return thisBuilder; } public B withExecutedBySubstitute(final long executedBySubstitute) { this.executedBySubstitute = executedBySubstitute; return thisBuilder; } public B withId(final long id) { this.id = id; return thisBuilder; } public B withName(final String name) { this.name = name; return thisBuilder; } public B withStateName(final String stateName) { this.stateName = stateName; return thisBuilder; } public B withDescription(final String description) { this.description = description; return thisBuilder; } public B withStateId(final int stateId) { this.stateId = stateId; return thisBuilder; } public B withLastUpdateDate(final long lastUpdateDate) { this.lastUpdateDate = lastUpdateDate; return thisBuilder; } public B withRootContainerId(final long containerId) { this.rootContainerId = containerId; return thisBuilder; } public B withParentContainerId(final long processInstanceId) { this.parentContainerId = processInstanceId; return thisBuilder; } public B withLoopCounter(final int loopCounter) { this.loopCounter = loopCounter; return thisBuilder; } public B withLogicalGroup1(final long logicalGroup) { this.logicalGroup1 = logicalGroup; return thisBuilder; } public B withLogicalGroup2(final long logicalGroup) { this.logicalGroup2 = logicalGroup; return thisBuilder; } public B withLogicalGroup3(final long logicalGroup) { this.logicalGroup3 = logicalGroup; return thisBuilder; } public B withLogicalGroup4(final long logicalGroup) { this.logicalGroup4 = logicalGroup; return thisBuilder; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/archive/ArchivedPersistentObjectBuilder.java ================================================ /** * 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.persistence.builder.archive; import java.util.Random; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; public abstract class ArchivedPersistentObjectBuilder> { protected long id = new Random().nextLong(); protected T persistentObject; protected B thisBuilder; public ArchivedPersistentObjectBuilder() { thisBuilder = getThisBuilder(); } public T build() { persistentObject = _build(); fill(persistentObject); return persistentObject; } protected T fill(T persistent) { persistent.setId(id); return persistent; } abstract B getThisBuilder(); abstract T _build(); } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/builder/archive/ArchivedUserTaskInstanceBuilder.java ================================================ /** * 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.persistence.builder.archive; import org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance; /** * @author Emmanuel Duchastenier */ public class ArchivedUserTaskInstanceBuilder extends ArchivedFlowNodeInstanceBuilder { public static ArchivedUserTaskInstanceBuilder anArchivedUserTask() { return new ArchivedUserTaskInstanceBuilder(); } private long assigneeId; @Override ArchivedUserTaskInstanceBuilder getThisBuilder() { return this; } @Override SAUserTaskInstance _build() { final SAUserTaskInstance userTaskInstanceImpl = new SAUserTaskInstance(); userTaskInstanceImpl.setAssigneeId(assigneeId); return userTaskInstanceImpl; } public ArchivedUserTaskInstanceBuilder withAssigneeId(final long assigneeId) { this.assigneeId = assigneeId; return this; } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/jdbc/JdbcRowMapper.java ================================================ /** * 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.test.persistence.jdbc; import java.sql.Blob; import java.sql.Clob; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.springframework.jdbc.core.RowMapper; /** * Spring JDBC RowMapper that converts column names and values as Hibernate woulds: *
    *
  • converts column names to upper case so that tests can assert on column name whatever the DB vendor
  • *
  • converts some column values to long, double, int or boolean if needed, notably on Oracle where types seem to be * harder to detect
  • *
  • converts Clob and Blob to String and byte[] respectively
  • *
* * @author Emmanuel Duchastenier */ @Slf4j public class JdbcRowMapper implements RowMapper> { private final String dbVendor = System.getProperty("sysprop.bonita.db.vendor", "h2"); private List columnsToConvertToLong = new ArrayList<>(); // column names must be upper case private List columnsToConvertToBoolean = new ArrayList<>(); // column names must be upper case private List columnsToConvertToDouble = new ArrayList<>(); // column names must be upper case private final Map converters = Map.of( "h2", new DefaultValueConverter(), "postgres", new DefaultValueConverter(), "mysql", new DefaultValueConverter(), "sqlserver", new DefaultValueConverter(), "oracle", new OracleValueConverter()); public JdbcRowMapper() { log.debug("Detected DB vendor: {}", dbVendor); } /** * @param columnsToConvertToLong column names must be upper case */ public JdbcRowMapper(String... columnsToConvertToLong) { this(); this.columnsToConvertToLong.addAll(List.of(columnsToConvertToLong)); } /** * @param columnsToConvertToLong column names must be upper case * @param columnsToConvertToBoolean column names must be upper case */ public JdbcRowMapper(List columnsToConvertToLong, List columnsToConvertToBoolean) { this(); this.columnsToConvertToLong = columnsToConvertToLong; this.columnsToConvertToBoolean = columnsToConvertToBoolean; } public JdbcRowMapper(List columnsToConvertToLong, List columnsToConvertToBoolean, List columnsToConvertToDouble) { this(columnsToConvertToLong, columnsToConvertToBoolean); this.columnsToConvertToDouble = columnsToConvertToDouble; } @Override public Map mapRow(ResultSet rs, int rowNum) throws SQLException { Map result = new HashMap<>(); int columnCount = rs.getMetaData().getColumnCount(); for (int i = 1; i <= columnCount; i++) { final Object object = rs.getObject(i); Object value = converters.get(dbVendor).getValue(rs, object, i); result.put(rs.getMetaData().getColumnName(i).toUpperCase(), value); } return result; } @FunctionalInterface public interface JdbcValueConverter { Object getValue(ResultSet resultSet, Object object, int rowNum) throws SQLException; } class DefaultValueConverter implements JdbcValueConverter { @Override public Object getValue(ResultSet rs, Object object, int columnNumber) throws SQLException { if (object == null) { return null; } if (object instanceof Clob) { final Clob clob = rs.getClob(columnNumber); return clob.getSubString(1, (int) clob.length()); } if (object instanceof Blob) { final Blob blob = rs.getBlob(columnNumber); return blob.getBytes(1, (int) blob.length()); } // if value must be converted to double, let's do it: final String upperCaseColumnName = rs.getMetaData().getColumnName(columnNumber).toUpperCase(); if (columnsToConvertToDouble.contains(upperCaseColumnName)) { return ((Number) object).doubleValue(); } // if value must be converted to long, let's do it: if (columnsToConvertToLong.contains(upperCaseColumnName)) { return ((Number) object).longValue(); } else if (object instanceof Number) { // if value can be converted to int, let's do it: try { return ((Number) object).intValue(); } catch (Exception ignored) { } } return object; } } class OracleValueConverter extends DefaultValueConverter { @Override public Object getValue(ResultSet rs, Object object, int columnNumber) throws SQLException { if (object != null && columnsToConvertToBoolean.contains(rs.getMetaData().getColumnName(columnNumber).toUpperCase())) { return "1".equals(rs.getString(columnNumber)); } else { return super.getValue(rs, object, columnNumber); } } } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/ApplicationRepository.java ================================================ /** * 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.persistence.repository; import java.util.List; import org.bonitasoft.engine.business.application.model.AbstractSApplication; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; /** * @author Elias Ricken de Medeiros */ @Repository public class ApplicationRepository extends TestRepository { public ApplicationRepository(final SessionFactory sessionFactory) { super(sessionFactory); } public SApplication getApplicationByToken(final String token) { final Query namedQuery = getNamedQuery("getApplicationByToken"); namedQuery.setParameter("token", token); return (SApplication) namedQuery.uniqueResult(); } public SApplicationPage getApplicationPage(final long applicationPageId) { final Query namedQuery = getNamedQuery("getApplicationPageById"); namedQuery.setParameter("id", applicationPageId); return (SApplicationPage) namedQuery.uniqueResult(); } public SApplicationPage getApplicationPageByTokenAndApplicationToken(final String applicationToken, final String applicationPageToken) { final Query namedQuery = getNamedQuery("getApplicationPageByTokenAndApplicationToken"); namedQuery.setParameter("applicationToken", applicationToken); namedQuery.setParameter("applicationPageToken", applicationPageToken); return (SApplicationPage) namedQuery.uniqueResult(); } public SApplicationPage getApplicationPageByTokenAndApplicationId(final long applicationId, final String applicationPageToken) { final Query namedQuery = getNamedQuery("getApplicationPageByTokenAndApplicationId"); namedQuery.setParameter("applicationId", applicationId); namedQuery.setParameter("applicationPageToken", applicationPageToken); return (SApplicationPage) namedQuery.uniqueResult(); } public SApplicationPage getApplicationHomePage(final long applicationId) { final Query namedQuery = getNamedQuery("getApplicationHomePage"); namedQuery.setParameter("applicationId", applicationId); return (SApplicationPage) namedQuery.uniqueResult(); } public SApplicationMenu getApplicationMenu(final long applicationMenuId) { final Query namedQuery = getNamedQuery("getApplicationMenuById"); namedQuery.setParameter("id", applicationMenuId); return (SApplicationMenu) namedQuery.uniqueResult(); } public int getLastIndexForRootMenu() { final Query namedQuery = getNamedQuery("getLastIndexForRootMenu"); return (Integer) namedQuery.uniqueResult(); } public int getLastIndexForChildOf(final long parentMenuId) { final Query namedQuery = getNamedQuery("getLastIndexForChildOf"); namedQuery.setParameter("parentId", parentMenuId); return (Integer) namedQuery.uniqueResult(); } public List getAllPagesForProfile(final long profileId) { final Query namedQuery = getNamedQuery("getAllPagesForProfile"); namedQuery.setParameter("profileId", profileId); return namedQuery.list(); } public Long getNumberOfApplicationOfUser(final long userId) { final Query namedQuery = getNamedQuery("getNumberOfSApplicationOfUser"); namedQuery.setParameter("userId", userId); return (Long) namedQuery.uniqueResult(); } public List searchApplicationOfUser(final long userId) { final Query namedQuery = getNamedQuery("searchSApplicationOfUser"); namedQuery.setParameter("userId", userId); return namedQuery.list(); } public void update(final AbstractSApplication application) { getSession().update(application); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/BPMEventRepository.java ================================================ /** * 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.persistence.repository; import static org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl.QUERY_RESET_IN_PROGRESS_WAITING_EVENTS; import java.util.List; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; /** * @author Emmanuel Duchastenier */ @Repository public class BPMEventRepository extends TestRepository { public BPMEventRepository(final SessionFactory sessionFactory) { super(sessionFactory); } @SuppressWarnings("unchecked") public List getInProgressMessageInstances() { Query namedQuery = getNamedQuery("getInProgressMessageInstances"); return namedQuery.list(); } public int resetProgressMessageInstances() { Query namedQuery = getNamedQuery("resetProgressMessageInstances"); return namedQuery.executeUpdate(); } @SuppressWarnings("unchecked") public List getInProgressWaitingEvents() { Query namedQuery = getNamedQuery("getInProgressWaitingEvents"); return namedQuery.list(); } public int resetInProgressWaitingEvents() { Query namedQuery = getNamedQuery(QUERY_RESET_IN_PROGRESS_WAITING_EVENTS); return namedQuery.executeUpdate(); } @SuppressWarnings("unchecked") public int deleteMessageInstanceByIds(List ids) { Query namedQuery = getNamedQuery("deleteMessageInstanceByIds"); namedQuery.setParameterList("ids", ids); return namedQuery.executeUpdate(); } @SuppressWarnings("unchecked") public List getMessageInstanceIdOlderThanCreationDate(long creationDate) { Query namedQuery = getNamedQuery("getMessageInstanceIdOlderThanCreationDate"); namedQuery.setParameter("creationDate", creationDate); return namedQuery.list(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/CommentRepository.java ================================================ /** * Copyright (C) 2017 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.persistence.repository; import java.util.List; import org.bonitasoft.engine.core.process.comment.model.SComment; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; /** * @author Emmanuel Duchastenier */ @Repository public class CommentRepository extends TestRepository { public CommentRepository(SessionFactory sessionFactory) { super(sessionFactory); } public List getCommentsOfProcessInstance(long processInstanceId) { final Query namedQuery = getNamedQuery("getSComments"); namedQuery.setParameter("processInstanceId", processInstanceId); return namedQuery.list(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/ConnectorInstanceRepository.java ================================================ /** * 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.persistence.repository; import java.util.List; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.core.process.instance.model.SAbstractConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; @Repository public class ConnectorInstanceRepository extends TestRepository { public ConnectorInstanceRepository(SessionFactory sessionFactory) { super(sessionFactory); } @SuppressWarnings("unchecked") public List getConnectorInstances(final long containerId, final String containerType) { Query namedQuery = getNamedQuery("getConnectorInstances"); namedQuery.setParameter("containerId", containerId); namedQuery.setParameter("containerType", containerType); return namedQuery.list(); } @SuppressWarnings("unchecked") public List getConnectorInstancesOrderedById(final long containerId, final String containerType) { Query namedQuery = getNamedQuery("getConnectorInstancesOrderedById"); namedQuery.setParameter("containerId", containerId); namedQuery.setParameter("containerType", containerType); return namedQuery.list(); } public List getConnectorInstancesWithFailureInfo(final long containerId, final String containerType, String state) { Query namedQuery = getNamedQuery("getConnectorInstancesWithFailureInfoInState"); namedQuery.setParameter("containerId", containerId); namedQuery.setParameter("containerType", containerType); namedQuery.setParameter("state", state); return namedQuery.list(); } public long getNumberOfConnectorInstances(final long containerId, final String containerType) { Query namedQuery = getNamedQuery("getNumberOfConnectorInstances"); namedQuery.setParameter("containerId", containerId); namedQuery.setParameter("containerType", containerType); return ((Number) namedQuery.uniqueResult()).longValue(); } @SuppressWarnings("unchecked") public SConnectorInstance getNextExecutableConnectorInstance(final long containerId, final String containerType, final ConnectorEvent activationEvent) { Query namedQuery = getNamedQuery("getNextExecutableConnectorInstance"); namedQuery.setParameter("containerId", containerId); namedQuery.setParameter("containerType", containerType); namedQuery.setParameter("activationEvent", activationEvent); List results = namedQuery.list(); return (results != null && results.size() > 0) ? results.get(0) : null; } @SuppressWarnings("unchecked") public List searchSConnectorInstance() { Query namedQuery = getNamedQuery("searchSConnectorInstance"); return namedQuery.list(); } public long getNumberOfSConnectorInstance() { Query namedQuery = getNamedQuery("getNumberOfSConnectorInstance"); return ((Number) namedQuery.uniqueResult()).longValue(); } @SuppressWarnings("unchecked") public List getConnectorInstances(final long containerId, final String containerType, final ConnectorEvent activationEvent, String state) { Query namedQuery = getNamedQuery("getConnectorInstancesWithState"); namedQuery.setParameter("containerId", containerId); namedQuery.setParameter("containerType", containerType); namedQuery.setParameter("activationEvent", activationEvent); namedQuery.setParameter("state", state); return namedQuery.list(); } @SuppressWarnings("unchecked") public List getConnectorInstanceWithFailureInfo(final long containerId) { Query namedQuery = getNamedQuery("getConnectorInstanceWithFailureInfo"); namedQuery.setParameter("id", containerId); return namedQuery.list(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/ContractDataRepository.java ================================================ /** * 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.persistence.repository; import org.hibernate.SessionFactory; import org.springframework.stereotype.Repository; @Repository public class ContractDataRepository extends TestRepository { public ContractDataRepository(SessionFactory sessionFactory) { super(sessionFactory); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/CustomUserInfoRepository.java ================================================ /** * 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.persistence.repository; import java.util.List; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; /** * @author Elias Ricken de Medeiros */ @Repository public class CustomUserInfoRepository extends TestRepository { public CustomUserInfoRepository(SessionFactory sessionFactory) { super(sessionFactory); } public List getUserIdsWithCustomUserInfo(String userInfoName, String userInfoValue, boolean partialMatch) { Query namedQuery; if (partialMatch) { namedQuery = getNamedQuery("getUserIdsWithCustomUserInfoContains"); userInfoValue = "%" + userInfoValue + "%"; } else { namedQuery = getNamedQuery("getUserIdsWithCustomUserInfo"); } namedQuery.setParameter("userInfoName", userInfoName); namedQuery.setParameter("userInfoValue", userInfoValue); return namedQuery.list(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/DependencyRepository.java ================================================ /** * Copyright (C) 2017 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.persistence.repository; import org.bonitasoft.engine.dependency.model.DependencyContent; import org.bonitasoft.engine.dependency.model.ScopeType; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; /** * @author Danila Mazour */ @Repository public class DependencyRepository extends TestRepository { public DependencyRepository(final SessionFactory sessionFactory) { super(sessionFactory); } public Long getDependencyIdFromArtifact(Long artifactId, ScopeType artifactType, String fileName) { final Query namedQuery = getNamedQuery("getIdOfDependencyOfArtifact"); namedQuery.setParameter("artifactId", artifactId); namedQuery.setParameter("artifactType", artifactType); namedQuery.setParameter("fileName", fileName); return (Long) namedQuery.uniqueResult(); } public DependencyContent getDependencyContentOnly(long id) { return (DependencyContent) getNamedQuery("getDependencyContentOnly") .setParameter("id", id).uniqueResult(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/DocumentRepository.java ================================================ /** * 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.persistence.repository; import static java.lang.System.currentTimeMillis; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.core.document.model.archive.SAMappedDocument; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; /** * @author Emmanuel Duchastenier */ @Repository public class DocumentRepository extends TestRepository { public DocumentRepository(final SessionFactory sessionFactory) { super(sessionFactory); } public SMappedDocument getSMappedDocumentOfProcessWithName(String name, long processInstanceId) { final Query namedQuery = getNamedQuery("getSMappedDocumentOfProcessWithName"); namedQuery.setParameter("name", name); namedQuery.setParameter("processInstanceId", processInstanceId); return (SMappedDocument) namedQuery.uniqueResult(); } public SAMappedDocument getSAMappedDocumentOfProcessWithName(String name, long processInstanceId) { final Query namedQuery = getNamedQuery("getArchivedDocumentList"); namedQuery.setParameter("name", name); namedQuery.setParameter("processInstanceId", processInstanceId); namedQuery.setParameter("time", currentTimeMillis() - 100000); return (SAMappedDocument) namedQuery.uniqueResult(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/FlowNodeInstanceRepository.java ================================================ /** * 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.persistence.repository; import java.time.Duration; import java.util.List; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter; import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; /** * @author Elias Ricken de Medeiros */ @Repository public class FlowNodeInstanceRepository extends TestRepository { public FlowNodeInstanceRepository(final SessionFactory sessionFactory) { super(sessionFactory); } public SFlowNodeInstance getById(long id) { final Query namedQuery = getNamedQuery("getSFlowNodeInstanceById"); namedQuery.setParameter("id", id); return (SFlowNodeInstance) namedQuery.uniqueResult(); } @SuppressWarnings("unchecked") public List getFlowNodeInstanceIdsToRecover(Duration considerElementsOlderThan, final QueryOptions queryOptions) { final Query namedQuery = getNamedQuery("getFlowNodeInstanceIdsToRecover"); namedQuery.setMaxResults(queryOptions.getNumberOfResults()); namedQuery.setFirstResult(queryOptions.getFromIndex()); namedQuery.setParameter("maxLastUpdate", System.currentTimeMillis() - considerElementsOlderThan.toMillis()); return (List) namedQuery.list(); } @SuppressWarnings("unchecked") public List getGatewayInstanceIdsToRecover(Duration considerElementsOlderThan, final QueryOptions queryOptions) { final Query namedQuery = getNamedQuery("getGatewayInstanceIdsToRecover"); namedQuery.setMaxResults(queryOptions.getNumberOfResults()); namedQuery.setFirstResult(queryOptions.getFromIndex()); namedQuery.setParameter("maxLastUpdate", System.currentTimeMillis() - considerElementsOlderThan.toMillis()); return (List) namedQuery.list(); } @SuppressWarnings("unchecked") public SGatewayInstance getActiveGatewayInstanceOfProcess(long parentProcessInstanceId, String name) { final Query namedQuery = getNamedQuery("getActiveGatewayInstanceOfProcess"); namedQuery.setParameter("parentProcessInstanceId", parentProcessInstanceId); namedQuery.setParameter("name", name); return (SGatewayInstance) namedQuery.uniqueResult(); } public long getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor(final long rootProcessDefinitionId, final long userId) { final Query namedQuery = getNamedQuery("getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcessFor"); namedQuery.setParameter("userId", userId); namedQuery.setParameter("rootProcessDefinitionId", rootProcessDefinitionId); return ((Number) namedQuery.uniqueResult()).longValue(); } @SuppressWarnings("unchecked") public List searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor( final long rootProcessDefinitionId, final long userId) { Query namedQuery = getNamedQuery("searchSHumanTaskInstanceAssignedAndPendingByRootProcessFor"); namedQuery = getSession().createQuery(namedQuery.getQueryString() + " ORDER BY a.name"); namedQuery.setParameter("userId", userId); namedQuery.setParameter("rootProcessDefinitionId", rootProcessDefinitionId); return namedQuery.list(); } public long getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess(final long rootProcessDefinitionId) { final Query namedQuery = getNamedQuery("getNumberOfSHumanTaskInstanceAssignedAndPendingByRootProcess"); namedQuery.setParameter("rootProcessDefinitionId", rootProcessDefinitionId); return ((Number) namedQuery.uniqueResult()).longValue(); } @SuppressWarnings("unchecked") public List searchSHumanTaskInstanceAssignedAndPendingByRootProcess( final long rootProcessDefinitionId) { Query namedQuery = getNamedQuery("searchSHumanTaskInstanceAssignedAndPendingByRootProcess"); namedQuery = getSession().createQuery(namedQuery.getQueryString() + " ORDER BY a.name"); namedQuery.setParameter("rootProcessDefinitionId", rootProcessDefinitionId); return namedQuery.list(); } @SuppressWarnings("unchecked") public List getNumberOfArchivedFlowNodesInAllStates(long processInstanceId) { Query namedQuery = getNamedQuery("getNumberOfArchivedFlowNodesInAllStates"); namedQuery.setParameter("parentProcessInstanceId", processInstanceId); return (List) namedQuery.list(); } @SuppressWarnings("unchecked") public List getNumberOfFlowNodesInAllStates(long processInstanceId) { Query namedQuery = getNamedQuery("getNumberOfFlowNodesInAllStates"); namedQuery.setParameter("parentProcessInstanceId", processInstanceId); return (List) namedQuery.list(); } @SuppressWarnings("unchecked") public List getNumberOfFlowNodesOfProcessDefinitionInAllStates( long processDefinitionId) { Query namedQuery = getNamedQuery("getNumberOfFlowNodesOfProcessDefinitionInAllStates"); namedQuery.setParameter("processDefinitionId", processDefinitionId); return (List) namedQuery.list(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/JobRepository.java ================================================ /** * 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.persistence.repository; import java.util.List; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.scheduler.model.SFailedJob; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; /** * @author Emmanuel Duchastenier */ @Repository public class JobRepository extends TestRepository { public JobRepository(final SessionFactory sessionFactory) { super(sessionFactory); } public List getFailedJobs(final QueryOptions queryOptions) { final Query namedQuery = getNamedQuery("getFailedJobs"); namedQuery.setMaxResults(queryOptions.getNumberOfResults()); namedQuery.setFirstResult(queryOptions.getFromIndex()); return namedQuery.list(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/PageRepository.java ================================================ /** * 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.persistence.repository; import java.util.List; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.page.SPageMapping; import org.bonitasoft.engine.page.SPageWithContent; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; @Repository public class PageRepository extends TestRepository { public PageRepository(final SessionFactory sessionFactory) { super(sessionFactory); } public SPageWithContent getPageContent(final long id) { final Query namedQuery = getNamedQuery("getPageContent"); namedQuery.setParameter("id", id); return (SPageWithContent) namedQuery.uniqueResult(); } public SPage getPageByName(final String name) { final Query namedQuery = getNamedQuery("getPageByName"); namedQuery.setParameter("pageName", name); return (SPage) namedQuery.uniqueResult(); } public SPage getPageByNameAndProcessDefinitionId(final String name, long processDefinitionId) { final Query namedQuery = getNamedQuery("getPageByNameAndProcessDefinitionId"); namedQuery.setParameter("pageName", name); namedQuery.setParameter("processDefinitionId", processDefinitionId); return (SPage) namedQuery.uniqueResult(); } public List getPageByProcessDefinitionId(long processDefinitionId) { final Query namedQuery = getNamedQuery("getPageByProcessDefinitionId"); namedQuery.setParameter("processDefinitionId", processDefinitionId); return namedQuery.list(); } public SPageMapping getPageMappingByKey(final String key) { final Query namedQuery = getNamedQuery("getPageMappingByKey"); namedQuery.setParameter("key", key); return (SPageMapping) namedQuery.uniqueResult(); } public SPageMapping getPageMappingByPageId(final long pageId) { final Query namedQuery = getNamedQuery("getPageMappingByPageId"); namedQuery.setParameter("pageId", pageId); return (SPageMapping) namedQuery.uniqueResult(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/PlatformRepository.java ================================================ /** * 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.persistence.repository; import java.util.Random; import org.bonitasoft.engine.commons.Pair; import org.bonitasoft.engine.persistence.PersistentObject; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; @Repository public class PlatformRepository extends TestRepository { public PlatformRepository(SessionFactory sessionFactory) { super(sessionFactory); } public PersistentObject selectOneOnPlatform(String queryName, Pair... parameters) { Query namedQuery = getSession().getNamedQuery(queryName); for (Pair parameter : parameters) { namedQuery.setParameter(((String) parameter.getKey()), parameter.getValue()); } return ((PersistentObject) namedQuery.uniqueResult()); } public T add(T entity) { if (entity.getId() <= 0) { entity.setId(new Random().nextLong()); } getSession().save(entity); return (T) getSession().get(entity.getClass(), entity.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/ProcessDeploymentInfoRepository.java ================================================ /** * 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.persistence.repository; import java.util.List; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; @Repository public class ProcessDeploymentInfoRepository extends TestRepository { public ProcessDeploymentInfoRepository(SessionFactory sessionFactory) { super(sessionFactory); } @SuppressWarnings("unchecked") public List searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor( final long userId) { final Query namedQuery = getNamedQuery("searchSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor"); namedQuery.setParameter("userId", userId); return namedQuery.list(); } public long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(final long userId) { final Query namedQuery = getNamedQuery( "getNumberOfSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksFor"); namedQuery.setParameter("userId", userId); return ((Number) namedQuery.uniqueResult()).longValue(); } @SuppressWarnings("unchecked") public List searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy( final long userId) { final Query namedQuery = getNamedQuery( "searchSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksSupervisedBy"); namedQuery.setParameter("userId", userId); return namedQuery.list(); } public long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(final long userId) { final Query namedQuery = getNamedQuery( "getNumberOfSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasksSupervisedBy"); namedQuery.setParameter("userId", userId); return ((Number) namedQuery.uniqueResult()).longValue(); } @SuppressWarnings("unchecked") public List searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks() { Query namedQuery = getNamedQuery("searchSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasks"); namedQuery = getSession().createQuery(namedQuery.getQueryString() + " ORDER BY process_definition.id"); return namedQuery.list(); } public long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks() { final Query namedQuery = getNamedQuery( "getNumberOfSProcessDefinitionDeployInfoWithAssignedOrPendingHumanTasks"); return ((Number) namedQuery.uniqueResult()).longValue(); } public List getProcessDefinitionDeployInfosByName(String processName) { final Query namedQuery = getNamedQuery("getProcessDefinitionDeployInfosByName"); namedQuery.setParameter("name", processName); return namedQuery.list(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/ProcessInstanceRepository.java ================================================ /** * 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.persistence.repository; import java.util.List; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance; import org.bonitasoft.engine.identity.model.SUser; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; @Repository public class ProcessInstanceRepository extends TestRepository { public ProcessInstanceRepository(final SessionFactory sessionFactory) { super(sessionFactory); } @SuppressWarnings("unchecked") public List getPossibleUserIdsOfPendingTasks(final long activityInstanceId) { final Query namedQuery = getNamedQuery("getPossibleUserIdsOfPendingTasks"); namedQuery.setParameter("humanTaskInstanceId", activityInstanceId); return namedQuery.list(); } @SuppressWarnings("unchecked") public List searchPossibleUserIdsOfPendingTasks(final long activityInstanceId) { final Query namedQuery = getNamedQuery("searchSUserWhoCanStartPendingTask"); namedQuery.setParameter("humanTaskInstanceId", activityInstanceId); return namedQuery.list(); } public long getNumberOfSUserWhoCanStartPendingTask(final long activityInstanceId) { final Query namedQuery = getNamedQuery("getNumberOfSUserWhoCanStartPendingTask"); namedQuery.setParameter("humanTaskInstanceId", activityInstanceId); return ((Number) namedQuery.uniqueResult()).longValue(); } @SuppressWarnings("unchecked") public List searchSUserWhoCanStartProcess(final long processId) { final Query namedQuery = getNamedQuery("searchSUserWhoCanStartProcess"); namedQuery.setParameter("processId", processId); namedQuery.setParameter("trueValue", true); return namedQuery.list(); } public long getNumberOfSUserWhoCanStartProcess(final long processId) { final Query namedQuery = getNamedQuery("getNumberOfSUserWhoCanStartProcess"); namedQuery.setParameter("processId", processId); namedQuery.setParameter("trueValue", true); return ((Number) namedQuery.uniqueResult()).longValue(); } public boolean isTaskPendingForUser(final long activityInstanceId, final long userId) { final Query namedQuery = getNamedQuery("isTaskPendingForUser"); namedQuery.setParameter("humanTaskInstanceId", activityInstanceId); namedQuery.setParameter("userId", userId); return ((Number) namedQuery.uniqueResult()).longValue() == 1; } public long countChildrenInstanceIdsOfProcessInstance(final long processInstanceId) { final Query namedQuery = getNamedQuery("getNumberOfChildInstancesOfProcessInstance"); namedQuery.setParameter("processInstanceId", processInstanceId); return ((Number) namedQuery.uniqueResult()).longValue(); } @SuppressWarnings("unchecked") public List getChildrenInstanceIdsOfProcessInstance(final long processInstanceId) { final Query namedQuery = getNamedQuery("getChildInstanceIdsOfProcessInstance"); namedQuery.setParameter("processInstanceId", processInstanceId); return namedQuery.list(); } public long getNumberOfFailedSProcessInstanceSupervisedBy(final long userId) { final Query namedQuery = getNamedQuery("getNumberOfSProcessInstanceFailedAndSupervisedBy"); namedQuery.setParameter("userId", userId); return ((Number) namedQuery.uniqueResult()).longValue(); } public List searchFailedSProcessInstanceSupervisedBy(final long userId) { final Query namedQuery = getNamedQuery("searchSProcessInstanceFailedAndSupervisedBy"); namedQuery.setParameter("userId", userId); return namedQuery.list(); } public long getNumberOfSProcessInstanceFailed() { final Query namedQuery = getNamedQuery("getNumberOfSProcessInstanceFailed"); return ((Number) namedQuery.uniqueResult()).longValue(); } public long getNumberOfSProcessInstanceFailedForProcessDefinition(final long processDefinitionId) { Query namedQuery = getNamedQuery("getNumberOfSProcessInstanceFailed"); namedQuery = getSession() .createQuery(namedQuery.getQueryString() + " AND p.processDefinitionId = " + processDefinitionId); return ((Number) namedQuery.uniqueResult()).longValue(); } @SuppressWarnings("unchecked") public List searchSProcessInstanceFailedForProcessDefinition(final long processDefinitionId) { Query namedQuery = getNamedQuery("searchSProcessInstanceFailed"); namedQuery = getSession() .createQuery(namedQuery.getQueryString() + " AND p.processDefinitionId = " + processDefinitionId); return namedQuery.list(); } @SuppressWarnings("unchecked") public List searchSProcessInstanceFailed() { final Query namedQuery = getNamedQuery("searchSProcessInstanceFailed"); return namedQuery.list(); } public long getNumberOfProcessInstances(final long processDefinitionId) { final Query namedQuery = getNamedQuery("countProcessInstancesOfProcessDefinition"); namedQuery.setParameter("processDefinitionId", processDefinitionId); return (Long) namedQuery.uniqueResult(); } @SuppressWarnings("unchecked") public List getArchivedProcessInstancesInAllStates(final List sourceProcessInstanceIds) { final Query namedQuery = getNamedQuery("getArchivedProcessInstancesInAllStates"); namedQuery.setParameterList("sourceObjectIds", sourceProcessInstanceIds); return namedQuery.list(); } public long getNumberOfTimerEventTriggerInstances(final long processInstanceId, final String jobTriggerName) { Query namedQuery = getNamedQuery("getNumberOfSTimerEventTriggerInstanceByProcessInstance"); if (jobTriggerName != null) { namedQuery = getSession() .createQuery(namedQuery.getQueryString() + " AND e.name = '" + jobTriggerName + "'"); } namedQuery.setParameter("processInstanceId", processInstanceId); return ((Number) namedQuery.uniqueResult()).longValue(); } @SuppressWarnings("unchecked") public List searchTimerEventTriggerInstances(final long processInstanceId, final String jobTriggerName) { Query namedQuery = getNamedQuery("searchSTimerEventTriggerInstanceByProcessInstance"); if (jobTriggerName != null) { namedQuery = getSession() .createQuery(namedQuery.getQueryString() + " AND e.name = '" + jobTriggerName + "'"); } namedQuery.setParameter("processInstanceId", processInstanceId); return namedQuery.list(); } public List getProcessInstanceIdsToRecover(final long maxLastUpdate) { final Query namedQuery = getNamedQuery("getProcessInstanceIdsToRecover"); namedQuery.setParameter("maxLastUpdate", maxLastUpdate); return namedQuery.list(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/ProcessResourceRepository.java ================================================ /** * Copyright (C) 2015 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.persistence.repository; import java.util.List; import org.bonitasoft.engine.resources.BARResourceType; import org.bonitasoft.engine.resources.SBARResource; import org.bonitasoft.engine.resources.SBARResourceLight; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; @Repository @SuppressWarnings("unchecked") public class ProcessResourceRepository extends TestRepository { public ProcessResourceRepository(final SessionFactory sessionFactory) { super(sessionFactory); } public SBARResource getBARResource(long processDefinitionId, BARResourceType type, String name) { final Query namedQuery = getNamedQuery("getBARResource"); namedQuery.setParameter("processDefinitionId", processDefinitionId); namedQuery.setParameter("type", type); namedQuery.setParameter("name", name); return (SBARResource) namedQuery.uniqueResult(); } public List getBARResourcesOfType(long processDefinitionId, BARResourceType type) { final Query namedQuery = getNamedQuery("getBARResourcesOfType"); namedQuery.setParameter("processDefinitionId", processDefinitionId); namedQuery.setParameter("type", type); return namedQuery.list(); } public List getBARResourcesLightOfType(long processDefinitionId, BARResourceType type) { final Query namedQuery = getNamedQuery("getBARResourcesLightOfType"); namedQuery.setParameter("processDefinitionId", processDefinitionId); namedQuery.setParameter("type", type); return namedQuery.list(); } public long getNumberOfBARResourcesOfType(long processDefinitionId, BARResourceType type) { final Query namedQuery = getNamedQuery("getNumberOfBARResourcesOfType"); namedQuery.setParameter("processDefinitionId", processDefinitionId); namedQuery.setParameter("type", type); return (long) namedQuery.uniqueResult(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/ProfileRepository.java ================================================ /** * Copyright (C) 2017 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.persistence.repository; import java.util.List; import org.bonitasoft.engine.profile.model.SProfile; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; /** * @author Emmanuel Duchastenier */ @Repository public class ProfileRepository extends TestRepository { public ProfileRepository(SessionFactory sessionFactory) { super(sessionFactory); } public List getProfilesOfUser(long userId) { final Query namedQuery = getNamedQuery("getProfilesOfUser"); namedQuery.setParameter("userId", userId); return namedQuery.list(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/RoleRepository.java ================================================ /** * 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.persistence.repository; import org.bonitasoft.engine.identity.model.SRole; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; /** * @author Emmanuel Duchastenier */ @Repository public class RoleRepository extends TestRepository { public RoleRepository(SessionFactory sessionFactory) { super(sessionFactory); } public SRole getRoleByName(String name) { Query namedQuery = getNamedQuery("getRoleByName"); namedQuery.setParameter("name", name); return ((SRole) namedQuery.uniqueResult()); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/SADataInstanceRepository.java ================================================ /** * 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.persistence.repository; import java.util.List; import org.bonitasoft.engine.data.instance.model.archive.SADataInstance; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; @Repository public class SADataInstanceRepository extends TestRepository { public SADataInstanceRepository(final SessionFactory sessionFactory) { super(sessionFactory); } public List getSADataInstancesByDataInstanceIdAndArchiveDate(final List dataInstanceIds, final long time) { final Query namedQuery = getNamedQuery("getSADataInstancesByDataInstanceIdAndArchiveDate"); namedQuery.setParameterList("dataInstanceIds", dataInstanceIds); namedQuery.setParameter("time", time); return namedQuery.list(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/SupervisorRepository.java ================================================ /** * 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.persistence.repository; import java.util.List; import org.hibernate.SessionFactory; import org.springframework.stereotype.Repository; @Repository public class SupervisorRepository extends TestRepository { public SupervisorRepository(SessionFactory sessionFactory) { super(sessionFactory); } @SuppressWarnings("unchecked") public List searchSProcessSupervisorWithSUserSGroupSRole() { return getNamedQuery("searchSProcessSupervisorwithSUserSGroupSRole").list(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/TenantResourceRepository.java ================================================ /** * Copyright (C) 2016 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.persistence.repository; import java.util.List; import org.bonitasoft.engine.resources.STenantResource; import org.bonitasoft.engine.resources.STenantResourceLight; import org.bonitasoft.engine.resources.TenantResourceType; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; @Repository public class TenantResourceRepository extends TestRepository { public TenantResourceRepository(final SessionFactory sessionFactory) { super(sessionFactory); } public STenantResource getTenantResource(TenantResourceType type, String name) { final Query namedQuery = getNamedQuery("getTenantResource"); namedQuery.setParameter("type", type); namedQuery.setParameter("name", name); return (STenantResource) namedQuery.uniqueResult(); } public List getTenantResourcesOfType(TenantResourceType type) { final Query namedQuery = getNamedQuery("getTenantResourcesOfType"); namedQuery.setParameter("type", type); return namedQuery.list(); } public List getTenantResourcesLightOfType(TenantResourceType type) { final Query namedQuery = getNamedQuery("getTenantResourcesLightOfType"); namedQuery.setParameter("type", type); return namedQuery.list(); } public long getNumberOfTenantResourcesOfType(TenantResourceType type) { final Query namedQuery = getNamedQuery("getNumberOfTenantResourcesOfType"); namedQuery.setParameter("type", type); return (long) namedQuery.uniqueResult(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/TestRepository.java ================================================ /** * 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.persistence.repository; import java.util.List; import java.util.Random; import org.bonitasoft.engine.commons.Pair; import org.bonitasoft.engine.persistence.PersistentObject; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; /** * Test Repository * Need to be used in a transactional context */ @Repository public class TestRepository { private final SessionFactory sessionFactory; public TestRepository(final SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public Session getSession() { return sessionFactory.getCurrentSession(); } protected Query getNamedQuery(final String queryName) { return getSession().getNamedQuery(queryName); } public void flush() { getSession().flush(); } @SuppressWarnings("unchecked") public T getById(final Class clazz, long id) { return (T) getSession().get(clazz, id); } public Long selectCount(String queryName, Pair... parameters) { Query namedQuery = getNamedQuery(queryName); setParameters(namedQuery, parameters); return ((Long) namedQuery.uniqueResult()); } public PersistentObject selectOne(String queryName, Pair... parameters) { Query namedQuery = getNamedQuery(queryName); setParameters(namedQuery, parameters); return ((PersistentObject) namedQuery.uniqueResult()); } public List selectList(String name, Pair... parameters) { Query namedQuery = getNamedQuery(name); setParameters(namedQuery, parameters); return namedQuery.list(); } protected void setParameters(Query namedQuery, Pair[] parameters) { for (Pair parameter : parameters) { namedQuery.setParameter(((String) parameter.getKey()), parameter.getValue()); } } /** * Utility method to run native queries on tables while developping tests: * e.g. * `repository.list("SELECT user_.* from user_ where user_.username = 'walter.bates'", SUser.class)` * will return the list of object SUser from database */ public List list(String sqlQuery, Class type) { return getSession().createSQLQuery(sqlQuery).addEntity(type).list(); } public void add(T... entities) { for (T entity : entities) { add(entity); } } public T add(T entity) { if (entity.getId() <= 0) { entity.setId(new Random().nextLong()); } getSession().save(entity); return (T) getSession().get(entity.getClass(), entity.getId()); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/UserMembershipRepository.java ================================================ /** * 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.persistence.repository; import java.util.List; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; /** * @author Emmanuel Duchastenier */ @Repository public class UserMembershipRepository extends TestRepository { public UserMembershipRepository(SessionFactory sessionFactory) { super(sessionFactory); } public Long getNumberOfUserMembersForUserOrManagerForActorMembers(long userId, final List actorMemberIds) { Query namedQuery = getNamedQuery("getNumberOfUserMembersForUserOrManagerForActorMembers"); namedQuery.setParameter("userId", userId); namedQuery.setParameterList("actorMemberIds", actorMemberIds); return ((Number) namedQuery.uniqueResult()).longValue(); } public List getUserMembershipsByGroup(SGroup group) { Query namedQuery = getNamedQuery("getUserMembershipsByGroup"); namedQuery.setParameter("groupId", group.getId()); return namedQuery.list(); } public List getUserMembershipsByRole(SRole role) { Query namedQuery = getNamedQuery("getUserMembershipsByRole"); namedQuery.setParameter("roleId", role.getId()); return namedQuery.list(); } public List getUserMembershipsOfUser(long userId) { Query namedQuery = getNamedQuery("getUserMembershipsOfUser"); namedQuery.setParameter("userId", userId); return namedQuery.list(); } public List getUserMembershipWithIds(long roleId, long groupId, long userId) { Query namedQuery = getNamedQuery("getUserMembershipWithIds"); namedQuery.setParameter("userId", userId); namedQuery.setParameter("roleId", roleId); namedQuery.setParameter("groupId", groupId); return namedQuery.list(); } public List getUserMemberships() { Query namedQuery = getNamedQuery("getUserMemberships"); return namedQuery.list(); } public List getSUserMembershipById(long id) { Query namedQuery = getNamedQuery("getSUserMembershipById"); namedQuery.setParameter("id", id); return namedQuery.list(); } public List searchUserMembership() { Query namedQuery = getNamedQuery("searchUserMembership"); return namedQuery.list(); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/java/org/bonitasoft/engine/test/persistence/repository/UserRepository.java ================================================ /** * 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.persistence.repository; import org.bonitasoft.engine.identity.model.SUser; import org.hibernate.SessionFactory; import org.hibernate.query.Query; import org.springframework.stereotype.Repository; /** * @author Emmanuel Duchastenier */ @Repository public class UserRepository extends TestRepository { public UserRepository(SessionFactory sessionFactory) { super(sessionFactory); } public SUser getUserByName(String userName) { Query namedQuery = getNamedQuery("getUserByUserName"); namedQuery.setParameter("userName", userName); return ((SUser) namedQuery.uniqueResult()); } } ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/resources/datasource/datasource-dependency-h2.xml ================================================ org.hibernate.dialect.H2Dialect org.h2.jdbcx.JdbcDataSource jdbc:h2:mem:bonita;DB_CLOSE_ON_EXIT=FALSE;AUTOCOMMIT=OFF;IGNORECASE=TRUE;DATABASE_TO_LOWER=TRUE; sa ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/resources/datasource/datasource-dependency-postgres.xml ================================================ org.hibernate.dialect.PostgreSQL10Dialect org.postgresql.xa.PGXADataSource localhost 5432 bonita bonita bonita jdbc:postgresql://${db.server.name}:${db.server.port}/${db.database.name} ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: bonita-integration-tests/bonita-query-tests/src/test/resources/testContext.xml ================================================ org.bonitasoft.engine.persistence org.bonitasoft.engine.identity.model.SContactInfo org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition org.bonitasoft.engine.identity.model.SCustomUserInfoValue org.bonitasoft.engine.identity.model.SGroup org.bonitasoft.engine.identity.model.SHavingIcon org.bonitasoft.engine.identity.SIcon org.bonitasoft.engine.identity.model.SRole org.bonitasoft.engine.identity.model.SUser org.bonitasoft.engine.identity.model.SUserLogin org.bonitasoft.engine.identity.model.SUserMembership org.bonitasoft.engine.parameter.SParameter org.bonitasoft.engine.actor.mapping.model.SActor org.bonitasoft.engine.actor.mapping.model.SActorMember org.bonitasoft.engine.business.application.model.SApplication org.bonitasoft.engine.business.application.model.SApplicationWithIcon org.bonitasoft.engine.business.application.model.SApplicationPage org.bonitasoft.engine.business.application.model.SApplicationMenu org.bonitasoft.engine.platform.model.SPlatform org.bonitasoft.engine.platform.command.model.SPlatformCommand org.bonitasoft.engine.core.category.model.SCategory org.bonitasoft.engine.core.category.model.SProcessCategoryMapping org.bonitasoft.engine.core.contract.data.SContractData org.bonitasoft.engine.core.contract.data.SProcessContractData org.bonitasoft.engine.core.contract.data.STaskContractData org.bonitasoft.engine.core.contract.data.SAContractData org.bonitasoft.engine.core.contract.data.SAProcessContractData org.bonitasoft.engine.core.contract.data.SATaskContractData org.bonitasoft.engine.core.process.comment.model.SComment org.bonitasoft.engine.core.process.comment.model.SHumanComment org.bonitasoft.engine.core.process.comment.model.SSystemComment org.bonitasoft.engine.core.process.comment.model.archive.SAComment org.bonitasoft.engine.dependency.model.SDependency org.bonitasoft.engine.dependency.model.SDependencyMapping org.bonitasoft.engine.dependency.model.SPlatformDependency org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping org.bonitasoft.engine.profile.model.SProfile org.bonitasoft.engine.profile.model.SProfileMember org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor org.bonitasoft.engine.data.instance.model.archive.SABlobDataInstance org.bonitasoft.engine.data.instance.model.archive.SABooleanDataInstance org.bonitasoft.engine.data.instance.model.archive.SADataInstance org.bonitasoft.engine.data.instance.model.archive.SADateDataInstance org.bonitasoft.engine.data.instance.model.archive.SADoubleDataInstance org.bonitasoft.engine.data.instance.model.archive.SAFloatDataInstance org.bonitasoft.engine.data.instance.model.archive.SAIntegerDataInstance org.bonitasoft.engine.data.instance.model.archive.SALongDataInstance org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance org.bonitasoft.engine.data.instance.model.archive.SAShortTextDataInstance org.bonitasoft.engine.data.instance.model.archive.SAXMLDataInstance org.bonitasoft.engine.data.instance.model.archive.SAXMLObjectDataInstance org.bonitasoft.engine.data.instance.model.SXMLDataInstance org.bonitasoft.engine.data.instance.model.SLongDataInstance org.bonitasoft.engine.data.instance.model.SDoubleDataInstance org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance org.bonitasoft.engine.data.instance.model.SIntegerDataInstance org.bonitasoft.engine.data.instance.model.SDateDataInstance org.bonitasoft.engine.data.instance.model.SLongTextDataInstance org.bonitasoft.engine.data.instance.model.SFloatDataInstance org.bonitasoft.engine.data.instance.model.SDataInstance org.bonitasoft.engine.data.instance.model.SBooleanDataInstance org.bonitasoft.engine.data.instance.model.SBlobDataInstance org.bonitasoft.engine.data.instance.model.SShortTextDataInstance org.bonitasoft.engine.resources.SBARResourceLight org.bonitasoft.engine.resources.SBARResource org.bonitasoft.engine.resources.STenantResourceLight org.bonitasoft.engine.resources.STenantResource org.bonitasoft.engine.scheduler.model.SJobDescriptor org.bonitasoft.engine.scheduler.model.SJobParameter org.bonitasoft.engine.scheduler.model.SJobLog org.bonitasoft.engine.core.contract.data.SContractData org.bonitasoft.engine.core.contract.data.SProcessContractData org.bonitasoft.engine.core.contract.data.STaskContractData org.bonitasoft.engine.core.contract.data.SAContractData org.bonitasoft.engine.core.form.SFormMapping org.bonitasoft.engine.core.contract.data.SAProcessContractData org.bonitasoft.engine.core.contract.data.SATaskContractData org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDesignContent org.bonitasoft.engine.core.document.model.SLightDocument org.bonitasoft.engine.temporary.content.STemporaryContent org.bonitasoft.engine.core.document.model.SDocument org.bonitasoft.engine.core.document.model.SDocumentMapping org.bonitasoft.engine.core.document.model.SMappedDocument org.bonitasoft.engine.core.document.model.archive.SAMappedDocument org.bonitasoft.engine.core.document.model.archive.SADocumentMapping org.bonitasoft.engine.page.SPage org.bonitasoft.engine.page.SPageWithContent org.bonitasoft.engine.page.SPageMapping org.bonitasoft.engine.core.process.instance.model.SProcessInstance org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance org.bonitasoft.engine.core.process.instance.model.SActivityInstance org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance org.bonitasoft.engine.core.process.instance.model.SGatewayInstance org.bonitasoft.engine.core.process.instance.model.SConnectorInstance org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo org.bonitasoft.engine.core.process.instance.model.event.SEventInstance org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SAEventInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SACatchEventInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SAStartEventInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SAIntermediateCatchEventInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SABoundaryEventInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SAThrowEventInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SAIntermediateThrowEventInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SAEndEventInstance org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.archive.business.data.SASimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessSimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAFlowNodeSimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessMultiRefBusinessDataInstance org.bonitasoft.engine.queriablelogger.model.SQueriableLog /org/bonitasoft/engine/core/process/definition/model/impl/hibernate/process.definition.queries.hbm.xml /org/bonitasoft/engine/core/process/instance/model/impl/hibernate/process.instance.queries.hbm.xml /org/bonitasoft/engine/supervisor/mapping/model/impl/hibernate/supervisor.queries.hbm.xml /org/bonitasoft/engine/actor/mapping/model/impl/hibernate/actor.queries.hbm.xml /org/bonitasoft/engine/identity/model/impl/hibernate/identity.queries.hbm.xml /org/bonitasoft/engine/scheduler/impl/hibernate/schedulerimpl.queries.hbm.xml /org/bonitasoft/engine/data/instance/model/impl/hibernate/data.instance.queries.hbm.xml /org/bonitasoft/engine/data/instance/model/impl/hibernate/archived.data.instance.queries.hbm.xml /org/bonitasoft/engine/profile/model/impl/hibernate/profile.queries.hbm.xml /org/bonitasoft/engine/dependency/model/impl/hibernate/dependency.queries.hbm.xml /org/bonitasoft/engine/dependency/model/impl/hibernate/platform-dependency.queries.hbm.xml /org/bonitasoft/engine/platform/model/impl/hibernate/platform.queries.hbm.xml org/bonitasoft/engine/platform/command/model/impl/hibernate/platformCommand.queries.hbm.xml /org/bonitasoft/engine/core/process/instance/model/impl/hibernate/archived.process.instance.queries.hbm.xml /org/bonitasoft/engine/page/impl/hibernate/page.queries.hbm.xml /org/bonitasoft/engine/parameter/parameter.queries.hbm.xml /org/bonitasoft/engine/business/application/impl/hibernate/application.queries.hbm.xml /org/bonitasoft/engine/resources/hibernate/resources.queries.hbm.xml /org/bonitasoft/engine/temporary/content/hibernate/temporary.content.queries.hbm.xml /org/bonitasoft/engine/core/process/comment/model/impl/hibernate/comment.queries.hbm.xml /org/bonitasoft/engine/core/process/comment/model/impl/hibernate/archive.comment.queries.hbm.xml /org/bonitasoft/engine/core/contract/data/model/impl/hibernate/contract.queries.hbm.xml org/bonitasoft/engine/core/document/model/impl/hibernate/document.queries.hbm.xml org/bonitasoft/engine/core/document/model/impl/hibernate/archive.document.queries.hbm.xml /org/bonitasoft/engine/core/form/impl/form-mapping.queries.hbm.xml create-drop ${db.hibernate.dialect} false false false ================================================ FILE: bonita-integration-tests/bonita-test-utils/build.gradle ================================================ dependencies { api project(':bpm:bonita-client') api project(':bpm:bonita-common') api project(':bonita-test-api') api libs.commonsIO api libs.xmlunit api libs.assertj implementation project(':bpm:bonita-core:bonita-process-engine') implementation libs.junit4 implementation libs.springTest } publishing { publications { mavenJava(MavenPublication) { from project.components.java } } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/connectors/TestConnectorEngineExecutionContext.java ================================================ /** * 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.connectors; import org.bonitasoft.engine.connector.AbstractConnector; import org.bonitasoft.engine.expression.ExpressionConstants; /** * @author Elias Ricken de Medeiros */ public class TestConnectorEngineExecutionContext extends AbstractConnector { @Override public void validateInputParameters() { } @Override protected void executeBusinessLogic() { setOutputParameter(ExpressionConstants.TASK_ASSIGNEE_ID.getEngineConstantName(), getExecutionContext().getTaskAssigneeId()); } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/matchers/NameMatcher.java ================================================ /** * 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.matchers; import org.bonitasoft.engine.bpm.NamedElement; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; /** * @author Baptiste Mesta */ public class NameMatcher extends BaseMatcher { private final String name; /** * @param name */ public NameMatcher(final String name) { this.name = name; } public static NameMatcher nameIs(final String name) { return new NameMatcher(name); } @Override public boolean matches(final Object item) { return name.equals(((NamedElement) item).getName()); } @Override public void describeTo(final Description description) { } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/APITestUtil.java ================================================ /** * 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 static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder.aBusinessArchive; import static org.junit.Assert.*; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.lang.reflect.Method; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.api.APIClient; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.BusinessDataAPI; import org.bonitasoft.engine.api.CommandAPI; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.MaintenanceAPI; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.PermissionAPI; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.ProfileAPI; import org.bonitasoft.engine.api.TenantAdministrationAPI; import org.bonitasoft.engine.api.platform.PlatformInformationAPI; import org.bonitasoft.engine.bdm.BusinessObjectModelConverter; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.Query; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.RelationField; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.bpm.actor.ActorCriterion; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.actor.ActorNotFoundException; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.engine.bpm.category.CategoryCriterion; import org.bonitasoft.engine.bpm.data.ArchivedDataInstance; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.document.DocumentCriterion; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException; import org.bonitasoft.engine.bpm.flownode.ActivityStates; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceNotFoundException; import org.bonitasoft.engine.bpm.flownode.GatewayInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.bpm.process.ProcessEnablementException; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.business.application.IApplication; import org.bonitasoft.engine.command.CommandDescriptor; import org.bonitasoft.engine.command.CommandExecutionException; import org.bonitasoft.engine.command.CommandNotFoundException; import org.bonitasoft.engine.command.CommandParameterizationException; import org.bonitasoft.engine.command.CommandSearchDescriptor; import org.bonitasoft.engine.connector.AbstractConnector; import org.bonitasoft.engine.connectors.TestConnectorEngineExecutionContext; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.GroupCreator; import org.bonitasoft.engine.identity.GroupCriterion; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.RoleCriterion; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserCreator; import org.bonitasoft.engine.identity.UserCriterion; import org.bonitasoft.engine.identity.UserMembership; import org.bonitasoft.engine.maintenance.MaintenanceDetails; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.page.PageSearchDescriptor; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.engine.tenant.TenantResource; import org.bonitasoft.engine.test.check.CheckNbOfArchivedActivities; import org.bonitasoft.engine.test.check.CheckNbOfArchivedActivityInstances; import org.bonitasoft.engine.test.check.CheckNbOfOpenActivities; import org.bonitasoft.engine.test.check.CheckNbOfProcessInstances; import org.bonitasoft.engine.test.check.CheckNbPendingTaskOf; import org.bonitasoft.engine.test.check.CheckProcessInstanceIsArchived; import org.bonitasoft.engine.test.wait.WaitForArchivedActivity; import org.bonitasoft.engine.test.wait.WaitForCompletedArchivedStep; import org.bonitasoft.engine.test.wait.WaitForEvent; import org.bonitasoft.engine.test.wait.WaitForFinalArchivedActivity; import org.bonitasoft.engine.test.wait.WaitForPendingTasks; import org.custommonkey.xmlunit.DetailedDiff; import org.custommonkey.xmlunit.XMLUnit; import org.junit.After; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; /** * @author Emmanuel Duchastenier * @author Frederic Bouquet * @author Celine Souchet */ public class APITestUtil extends PlatformTestUtil { public static final Logger LOGGER = LoggerFactory.getLogger(APITestUtil.class); public static final int DEFAULT_REPEAT_EACH = 500; // Use 'sysprop.bonita.test.timeout.millis' to overwrite: public static final int DEFAULT_TIMEOUT; public static final String ACTOR_NAME = BuildTestUtil.ACTOR_NAME; public static final String PROCESS_VERSION = BuildTestUtil.PROCESS_VERSION; public static final String PROCESS_NAME = BuildTestUtil.PROCESS_NAME; public static final String PASSWORD = BuildTestUtil.PASSWORD; public static final String USERNAME = BuildTestUtil.USERNAME; public static final String GROUP_NAME = BuildTestUtil.GROUP_NAME; public static final String ROLE_NAME = BuildTestUtil.ROLE_NAME; public static final String FIND_BY_HIRE_DATE_RANGE = "findByHireDateRange"; public static final String COUNT_FOR_FIND_BY_HIRE_DATE_RANGE = "countForFindByHireDateRange"; private static final String BDM_PACKAGE_PREFIX = "com.company.model"; protected static final String COUNTRY_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + ".Country"; protected static final String ADDRESS_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + ".Address"; protected static final String DOG_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + ".Dog"; protected static final String CAT_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + ".Cat"; protected static final String EMPLOYEE_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + ".Employee"; protected static final String PRODUCT_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + ".Product"; protected static final String PRODUCT_CATALOG_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + ".ProductCatalog"; protected static final String GET_EMPLOYEE_BY_LAST_NAME_QUERY_NAME = "findByLastName"; protected static final String GET_EMPLOYEE_BY_PHONE_NUMBER_QUERY_NAME = "findByPhoneNumber"; protected static final String FIND_BY_FIRST_NAME_FETCH_ADDRESSES = "findByFirstNameFetchAddresses"; protected static final String FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER = "findByFirstNameAndLastNameNewOrder"; protected static final String COUNT_EMPLOYEE = "countEmployee"; protected static final String COUNT_ADDRESS = "countAddress"; private static final String PERSON_QUALIFIED_NAME = BDM_PACKAGE_PREFIX + ".Person"; protected static final String FIND_EMPLOYEE_WITH_FIRSTNAMES = "findEmployeeWithFirstNames"; private static final List DEFAULT_METHODS = Arrays.asList("wait", "equals", "toString", "hashCode", "getClass", "notify", "notifyAll"); private final APIClient apiClient = new APIClient(); static { final String strTimeout = System.getProperty("sysprop.bonita.test.timeout.millis"); if (strTimeout != null) { LOGGER.info("Using overridden test timeout value {} ms", strTimeout); DEFAULT_TIMEOUT = Integer.parseInt(strTimeout); } else { DEFAULT_TIMEOUT = (int) Duration.ofSeconds(30).toMillis(); } } @After public void clearSynchroRepository() { try { loginWithTechnicalUser(); if (getTenantAdministrationAPI().isPaused()) { getTenantAdministrationAPI().resume(); } ClientEventUtil.clearRepo(getCommandAPI()); logout(); } catch (final Exception e) { e.printStackTrace(); } } public void loginOnDefaultTenantWith(final String userName, final String password) throws BonitaException { getApiClient().login(userName, password); } public void loginWithTechnicalUser() throws BonitaException { getApiClient().login(DEFAULT_TECHNICAL_LOGGER_USERNAME, DEFAULT_TECHNICAL_LOGGER_PASSWORD); } public BusinessDataAPI getBusinessDataAPI() { return getApiClient().getBusinessDataAPI(); } public void logout() throws BonitaException { getApiClient().logout(); } public void logoutThenlogin() throws BonitaException { logout(); loginWithTechnicalUser(); } public void logoutThenloginAs(final String userName, final String password) throws BonitaException { logout(); loginOnDefaultTenantWith(userName, password); } public void addMappingOfActorsForRole(final String actorName, final long roleId, final ProcessDefinition definition) throws BonitaException { getProcessAPI().addRoleToActor(actorName, definition, roleId); } public void addMappingOfActorsForRoleAndGroup(final String actorName, final long roleId, final long groupId, final ProcessDefinition definition) throws BonitaException { getProcessAPI().addRoleAndGroupToActor(actorName, definition, roleId, groupId); } /** * First actor means "first one in Alphanumerical order !" */ public void addUserToFirstActorOfProcess(final long userId, final ProcessDefinition processDefinition) throws BonitaException { final List actors = getProcessAPI().getActors(processDefinition.getId(), 0, 1, ActorCriterion.NAME_ASC); final ActorInstance actor = actors.get(0); getProcessAPI().addUserToActor(actor.getId(), userId); } public ActorInstance getActor(final String actorName, final ProcessDefinition processDefinition) throws ActorNotFoundException { final List actors = getProcessAPI().getActors(processDefinition.getId(), 0, 50, ActorCriterion.NAME_ASC); final ActorInstance actorInstance = getActorInstance(actors, actorName); if (actorInstance == null) { throw new ActorNotFoundException(actorName, processDefinition); } return actorInstance; } private ActorInstance getActorInstance(final List actors, final String actorName) { for (final ActorInstance actor : actors) { if (actor.getName().equals(actorName)) { return actor; } } return null; } public User createUser(final String userName, final String password) throws BonitaException { return getIdentityAPI().createUser(userName, password); } public User createUser(final String userName, final String password, final String firstName, final String lastName) throws BonitaException { return getIdentityAPI().createUser(userName, password, firstName, lastName); } public User createUser(final UserCreator creator) throws BonitaException { return getIdentityAPI().createUser(creator); } public User createUser(final String userName, final long managerId) throws BonitaException { return createUser(userName, "bpm", managerId); } public User createUser(final String userName, final String password, final long managerId) throws BonitaException { final UserCreator creator = new UserCreator(userName, password); creator.setManagerUserId(managerId); return getIdentityAPI().createUser(creator); } public User createUserAndLogin(final String userName, final String password) throws BonitaException { final User user = getIdentityAPI().createUser(userName, password); logout(); loginOnDefaultTenantWith(userName, password); return user; } public void deleteUsers(final User... users) throws BonitaException { deleteUsers(Arrays.asList(users)); } public void deleteUsers(final List users) throws BonitaException { if (users != null) { for (final User user : users) { getIdentityAPI().deleteUser(user.getId()); } } } public void deleteGroups(final Group... groups) throws BonitaException { deleteGroups(Arrays.asList(groups)); } public void deleteGroups(final List groups) throws BonitaException { if (groups != null) { for (final Group group : groups) { getIdentityAPI().deleteGroup(group.getId()); } } } public void deleteRoles(final Role... roles) throws BonitaException { deleteRoles(Arrays.asList(roles)); } public void deleteRoles(final List roles) throws BonitaException { if (roles != null) { for (final Role role : roles) { getIdentityAPI().deleteRole(role.getId()); } } } public void deleteUserMembership(final long id) throws BonitaException { getIdentityAPI().deleteUserMembership(id); } public void deleteUserMemberships(final UserMembership... userMemberships) throws BonitaException { deleteUserMemberships(Arrays.asList(userMemberships)); } public void deleteUserMemberships(final List userMemberships) throws BonitaException { if (userMemberships != null) { for (final UserMembership userMembership : userMemberships) { getIdentityAPI().deleteUserMembership(userMembership.getId()); } } } public UserMembership createUserMembership(final String userName, final String roleName, final String groupName) throws BonitaException { return getIdentityAPI().addUserMembership(getIdentityAPI().getUserByUserName(userName).getId(), getIdentityAPI().getGroupByPath(groupName).getId(), getIdentityAPI().getRoleByName(roleName).getId()); } public void deleteProcess(final ProcessDefinition... processDefinitions) throws BonitaException { deleteProcess(Arrays.asList(processDefinitions)); } public void deleteProcess(final ProcessDefinition processDefinition) throws BonitaException { deleteProcess(processDefinition.getId()); } public void deleteProcess(final List processDefinitions) throws BonitaException { if (processDefinitions != null) { for (final ProcessDefinition processDefinition : processDefinitions) { deleteProcess(processDefinition); } } } public void deleteProcess(final long processDefinitionId) throws BonitaException { deleteProcessInstanceAndArchived(processDefinitionId); getProcessAPI().deleteProcessDefinition(processDefinitionId); } public void deleteUser(final String userName) throws BonitaException { getIdentityAPI().deleteUser(userName); } public void deleteUser(final User user) throws BonitaException { getIdentityAPI().deleteUser(user.getId()); } public void deleteCategories(final List categories) throws BonitaException { for (final Category category : categories) { getProcessAPI().deleteCategory(category.getId()); } } public ProcessDefinition deployAndEnableProcess(final DesignProcessDefinition designProcessDefinition) throws BonitaException { return deployAndEnableProcess(createNewBusinessArchive(designProcessDefinition)); } public ProcessDefinition deployAndEnableProcess(final BusinessArchive businessArchive) throws BonitaException { final ProcessDefinition processDefinition = deployProcess(businessArchive); enableProcess(processDefinition); return processDefinition; } public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition, final String actorName, final User user) throws BonitaException { return deployAndEnableProcessWithActor(designProcessDefinition, Collections.singletonList(actorName), Collections.singletonList(user)); } public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition, final String actorName, final List users) throws BonitaException { return deployAndEnableProcessWithActor(createNewBusinessArchive(designProcessDefinition), actorName, users); } public ProcessDefinition deployAndEnableProcessWithActor(final BusinessArchive businessArchive, final String actorName, final List users) throws BonitaException { final ProcessDefinition processDefinition = deployProcess(businessArchive); for (final User user : users) { getProcessAPI().addUserToActor(actorName, processDefinition, user.getId()); } enableProcess(processDefinition); return processDefinition; } public ProcessDefinition deployAndEnableProcessWithActorAndParameters( final DesignProcessDefinition designProcessDefinition, final String actorName, final User user, final Map parameters) throws BonitaException { return deployAndEnableProcessWithActorAndParameters(designProcessDefinition, Collections.singletonList(actorName), Collections.singletonList(user), parameters); } public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition, final List actorsName, final List users) throws BonitaException { return deployAndEnableProcessWithActor( createNewBusinessArchive(designProcessDefinition), actorsName, users); } public ProcessDefinition deployAndEnableProcessWithActor(final BusinessArchive businessArchive, final List actorsName, final List users) throws BonitaException { final ProcessDefinition processDefinition = deployProcess(businessArchive); for (int i = 0; i < users.size(); i++) { getProcessAPI().addUserToActor(actorsName.get(i), processDefinition, users.get(i).getId()); } enableProcess(processDefinition); return processDefinition; } public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition, final Map> actorUsers) throws BonitaException { return deployAndEnableProcessWithActor( createNewBusinessArchive(designProcessDefinition), actorUsers); } public ProcessDefinition deployAndEnableProcessWithActor(final BusinessArchive businessArchive, final Map> actorUsers) throws BonitaException { final ProcessDefinition processDefinition = deployProcess(businessArchive); for (final Entry> actorUser : actorUsers.entrySet()) { final String actorName = actorUser.getKey(); final List users = actorUser.getValue(); for (final User user : users) { getProcessAPI().addUserToActor(actorName, processDefinition, user.getId()); } } enableProcess(processDefinition); return processDefinition; } public ProcessDefinition deployProcess(final BusinessArchive businessArchive) throws BonitaException { return getProcessAPI().deploy(businessArchive); } private void enableProcess(final ProcessDefinition processDefinition) throws ProcessDefinitionNotFoundException, ProcessEnablementException { try { getProcessAPI().enableProcess(processDefinition.getId()); } catch (final ProcessEnablementException e) { final List problems = getProcessAPI().getProcessResolutionProblems(processDefinition.getId()); throw new ProcessEnablementException("not resolved: " + problems); } } public ProcessDefinition deployAndEnableProcessWithActor(final BusinessArchive businessArchive, final String actorName, final User user) throws BonitaException { return deployAndEnableProcessWithActor(businessArchive, Collections.singletonList(actorName), Collections.singletonList(user)); } public ProcessDefinition deployAndEnableProcessWithActorAndParameters( final DesignProcessDefinition designProcessDefinition, final List actorsName, final List users, final Map parameters) throws BonitaException { return deployAndEnableProcessWithActor( createNewBusinessArchive(designProcessDefinition, parameters), actorsName, users); } public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition, final String actorName, final Group group) throws BonitaException { final ProcessDefinition processDefinition = deployProcess(createNewBusinessArchive(designProcessDefinition)); getProcessAPI().addGroupToActor(actorName, group.getId(), processDefinition); getProcessAPI().enableProcess(processDefinition.getId()); return processDefinition; } public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition, final String actorName, final Group... groups) throws BonitaException { final ProcessDefinition processDefinition = deployProcess(createNewBusinessArchive(designProcessDefinition)); for (final Group group : groups) { getProcessAPI().addGroupToActor(actorName, group.getId(), processDefinition); } getProcessAPI().enableProcess(processDefinition.getId()); return processDefinition; } public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition, final String actorName, final List groups, final List users) throws BonitaException { final ProcessDefinition processDefinition = deployProcess(createNewBusinessArchive(designProcessDefinition)); for (final Group group : groups) { getProcessAPI().addGroupToActor(actorName, group.getId(), processDefinition); } for (User user : users) { getProcessAPI().addUserToActor(actorName, processDefinition, user.getId()); } getProcessAPI().enableProcess(processDefinition.getId()); return processDefinition; } public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition, final String actorName, final Role role) throws BonitaException { final ProcessDefinition processDefinition = deployProcess(createNewBusinessArchive(designProcessDefinition)); addMappingOfActorsForRole(actorName, role.getId(), processDefinition); getProcessAPI().enableProcess(processDefinition.getId()); return processDefinition; } public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition, final String actorName, final Role... roles) throws BonitaException { final ProcessDefinition processDefinition = deployProcess(createNewBusinessArchive(designProcessDefinition)); for (final Role role : roles) { addMappingOfActorsForRole(actorName, role.getId(), processDefinition); } getProcessAPI().enableProcess(processDefinition.getId()); return processDefinition; } public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition, final String actorName, final Role role, final Group group) throws BonitaException { final ProcessDefinition processDefinition = deployProcess(createNewBusinessArchive(designProcessDefinition)); addMappingOfActorsForRoleAndGroup(actorName, role.getId(), group.getId(), processDefinition); getProcessAPI().enableProcess(processDefinition.getId()); return processDefinition; } public ProcessDefinition deployAndEnableProcessWithActor(final DesignProcessDefinition designProcessDefinition, final String actorName, final long userId) throws BonitaException { final ProcessDefinition processDefinition = deployProcess(createNewBusinessArchive(designProcessDefinition)); getProcessAPI().addUserToActor(actorName, processDefinition, userId); getProcessAPI().enableProcess(processDefinition.getId()); return processDefinition; } public ProcessDefinition deployAndEnableProcessWithConnector( final ProcessDefinitionBuilder processDefinitionBuilder, final List connectorImplementations, final List generateConnectorDependencies) throws BonitaException { try { final BusinessArchiveBuilder businessArchiveBuilder = BuildTestUtil .buildBusinessArchiveWithConnectorAndUserFilter(processDefinitionBuilder, connectorImplementations, generateConnectorDependencies, Collections.emptyList()); return deployAndEnableProcess(businessArchiveBuilder.done()); } catch (InvalidProcessDefinitionException | InvalidBusinessArchiveFormatException e) { throw new BonitaException(e); } } public ProcessDefinition deployAndEnableProcessWithConnector( final ProcessDefinitionBuilder processDefinitionBuilder, final String connectorImplName, final Class clazz, final String jarName) throws BonitaException, IOException { return deployAndEnableProcessWithConnector(processDefinitionBuilder, Collections.singletonList(BuildTestUtil.getContentAndBuildBarResource(connectorImplName, clazz)), Collections.singletonList(BuildTestUtil.generateJarAndBuildBarResource(clazz, jarName))); } public ProcessDefinition deployAndEnableProcessWithActorAndConnector( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user, final String name, final Class clazz, final String jarName) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user, Collections.singletonList(BuildTestUtil.getContentAndBuildBarResource(name, clazz)), Collections.singletonList(BuildTestUtil.generateJarAndBuildBarResource(clazz, jarName)), null); } public ProcessDefinition deployAndEnableProcessWithActorAndTestConnectorEngineExecutionContext( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnector(processDefinitionBuilder, actorName, user, "TestConnectorEngineExecutionContext.impl", TestConnectorEngineExecutionContext.class, "TestConnectorEngineExecutionContext.jar"); } public ProcessDefinition deployAndEnableProcessWithActorAndConnectorAndParameter( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user, final List connectorImplementations, final List generateConnectorDependencies, final Map parameters) throws BonitaException { try { final BusinessArchiveBuilder businessArchiveBuilder = BuildTestUtil .buildBusinessArchiveWithConnectorAndUserFilter(processDefinitionBuilder, connectorImplementations, generateConnectorDependencies, Collections.emptyList()); if (parameters != null) { businessArchiveBuilder.setParameters(parameters); } return deployAndEnableProcessWithActor(businessArchiveBuilder.done(), actorName, user); } catch (InvalidProcessDefinitionException | InvalidBusinessArchiveFormatException e) { throw new BonitaException(e); } } public ProcessDefinition deployAndEnableProcessWithActorAndConnectorAndUserFilter( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user, final List connectorImplementations, final List generateConnectorDependencies, final List userFilters) throws BonitaException { try { final BusinessArchiveBuilder businessArchiveBuilder = BuildTestUtil .buildBusinessArchiveWithConnectorAndUserFilter(processDefinitionBuilder, connectorImplementations, generateConnectorDependencies, userFilters); return deployAndEnableProcessWithActor(businessArchiveBuilder.done(), actorName, user); } catch (InvalidProcessDefinitionException | InvalidBusinessArchiveFormatException e) { throw new BonitaException(e); } } public ProcessDefinition deployAndEnableProcessWithActorAndConnectorAndParameter( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user, final Map parameters, final String name, final Class clazz, final String jarName) throws BonitaException, IOException { return deployAndEnableProcessWithActorAndConnectorAndParameter(processDefinitionBuilder, actorName, user, Collections.singletonList(BuildTestUtil.getContentAndBuildBarResource(name, clazz)), Collections.singletonList(BuildTestUtil.generateJarAndBuildBarResource(clazz, jarName)), parameters); } public ProcessDefinition deployAndEnableProcessWithActorAndUserFilter( final ProcessDefinitionBuilder processDefinitionBuilder, final String actorName, final User user, final List generateFilterDependencies, final List userFilters) throws BonitaException { return deployAndEnableProcessWithActorAndConnectorAndUserFilter(processDefinitionBuilder, actorName, user, Collections. emptyList(), generateFilterDependencies, userFilters); } private static BusinessArchive createNewBusinessArchive(final DesignProcessDefinition designProcessDefinition) throws BonitaException { try { return aBusinessArchive().setProcessDefinition(designProcessDefinition).done(); } catch (InvalidBusinessArchiveFormatException e) { throw new BonitaException(e); } } private static BusinessArchive createNewBusinessArchive(final DesignProcessDefinition designProcessDefinition, final Map parameters) throws BonitaException { try { return aBusinessArchive().setProcessDefinition(designProcessDefinition).setParameters(parameters).done(); } catch (InvalidBusinessArchiveFormatException e) { throw new BonitaException(e); } } public void disableAndDeleteProcess(final ProcessDefinition processDefinition) throws BonitaException { if (processDefinition != null) { disableAndDeleteProcess(processDefinition.getId()); } } public void disableAndDeleteProcess(final long processDefinitionId) throws BonitaException { var processApi = getProcessAPI(); if (processApi.getProcessDeploymentInfo(processDefinitionId).getActivationState() == ActivationState.ENABLED) { processApi.disableProcess(processDefinitionId); } // Delete all process instances long nbDeletedProcessInstances; do { nbDeletedProcessInstances = processApi.deleteProcessInstances(processDefinitionId, 0, 100); } while (nbDeletedProcessInstances > 0); // Search all root archived process instances ids for the given processDefinition var rootArchivedProcessInstancesIds = processApi .searchArchivedProcessInstancesInAllStates(new SearchOptionsBuilder(0, Integer.MAX_VALUE) .filter(ArchivedProcessInstancesSearchDescriptor.PROCESS_DEFINITION_ID, processDefinitionId) .differentFrom(ArchivedProcessInstancesSearchDescriptor.CALLER_ID, "-1") .done()) .getResult() .stream() .map(ArchivedProcessInstance::getRootProcessInstanceId) .collect(Collectors.toList()); if (!rootArchivedProcessInstancesIds.isEmpty()) { processApi.deleteArchivedProcessInstancesInAllStates(rootArchivedProcessInstancesIds); } // Delete all archived process instances long nbDeletedArchivedProcessInstances; do { nbDeletedArchivedProcessInstances = processApi.deleteArchivedProcessInstances(processDefinitionId, 0, 100); } while (nbDeletedArchivedProcessInstances > 0); getProcessAPI().deleteProcessDefinition(processDefinitionId); } public void disableAndDeleteProcess(final ProcessDefinition... processDefinitions) throws BonitaException { disableAndDeleteProcess(Arrays.asList(processDefinitions)); } public void disableAndDeleteProcess(final List processDefinitions) throws BonitaException { if (processDefinitions != null) { for (final ProcessDefinition processDefinition : processDefinitions) { disableAndDeleteProcess(processDefinition); } } } public void disableAndDeleteProcessById(final List processDefinitionIds) throws BonitaException { if (processDefinitionIds != null) { for (final Long id : processDefinitionIds) { disableAndDeleteProcess(id); } } } public void deleteProcessInstanceAndArchived(final long processDefinitionId) throws BonitaException { while (getProcessAPI().deleteArchivedProcessInstances(processDefinitionId, 0, 500) != 0); while (getProcessAPI().deleteProcessInstances(processDefinitionId, 0, 500) != 0); } public void deleteProcessInstanceAndArchived(final ProcessDefinition... processDefinitions) throws BonitaException { deleteProcessInstanceAndArchived(Arrays.asList(processDefinitions)); } public void deleteProcessInstanceAndArchived(final List processDefinitions) throws BonitaException { if (processDefinitions != null) { for (final ProcessDefinition processDefinition : processDefinitions) { deleteProcessInstanceAndArchived(processDefinition.getId()); } } } public APISession getSession() { return getApiClient().getSession(); } protected APIClient getApiClient() { return apiClient; } public static boolean containsState(final List instances, final TestStates state) { for (final ArchivedProcessInstance pi : instances) { if (state.getStateName().equals(pi.getState())) { return true; } } return false; } public Group createGroup(final String groupName) throws CreationException { return createGroup(groupName, null); } public Group createGroup(final String groupName, final String parentGroupPath) throws CreationException { return getIdentityAPI().createGroup(groupName, parentGroupPath); } public Group createGroup(final String name, final String displayName, final String description) throws CreationException { final GroupCreator groupCreator = new GroupCreator(name); groupCreator.setDisplayName(displayName).setDescription(description); return getIdentityAPI().createGroup(groupCreator); } public Role createRole(final String roleName) throws BonitaException { return getIdentityAPI().createRole(roleName); } public void assignAndExecuteStep(final ActivityInstance activityInstance, final long userId) throws BonitaException { assignAndExecuteStep(activityInstance.getId(), userId); } public void assignAndExecuteStep(final ActivityInstance activityInstance, final User user) throws BonitaException { assignAndExecuteStep(activityInstance.getId(), user.getId()); } public void assignAndExecuteStep(final long activityInstanceId, final User user) throws BonitaException { assignAndExecuteStep(activityInstanceId, user.getId()); } public void assignAndExecuteStep(final long activityInstanceId, final long userId) throws BonitaException { getProcessAPI().assignUserTask(activityInstanceId, userId); getProcessAPI().executeUserTask(activityInstanceId, null); } public HumanTaskInstance waitForUserTaskAndExecuteAndGetIt(final String taskName, final User user) throws Exception { final HumanTaskInstance humanTaskInstance = waitForUserTaskAndGetIt(taskName); assignAndExecuteStep(humanTaskInstance, user); return humanTaskInstance; } public long waitForUserTaskAndExecuteIt(final String taskName, final User user) throws Exception { final long humanTaskInstanceId = waitForUserTask(taskName); assignAndExecuteStep(humanTaskInstanceId, user); return humanTaskInstanceId; } public HumanTaskInstance waitForUserTaskAndExecuteAndGetIt(final ProcessInstance processInstance, final String taskName, final User user) throws Exception { final HumanTaskInstance humanTaskInstance = waitForUserTaskAndGetIt(processInstance.getId(), taskName, DEFAULT_TIMEOUT); assignAndExecuteStep(humanTaskInstance, user); return humanTaskInstance; } public long waitForUserTaskAndExecuteIt(final ProcessInstance processInstance, final String taskName, final User user) throws Exception { final long humanTaskInstanceId = waitForUserTask(processInstance.getId(), taskName, DEFAULT_TIMEOUT); assignAndExecuteStep(humanTaskInstanceId, user); return humanTaskInstanceId; } public HumanTaskInstance waitForUserTaskAndAssignIt(final String taskName, final User user) throws Exception { final HumanTaskInstance humanTaskInstance = waitForUserTaskAndGetIt(taskName); getProcessAPI().assignUserTask(humanTaskInstance.getId(), user.getId()); return humanTaskInstance; } public HumanTaskInstance waitForUserTaskAndAssignIt(final ProcessInstance processInstance, final String taskName, final User user) throws Exception { final HumanTaskInstance humanTaskInstance = waitForUserTaskAndGetIt(processInstance.getId(), taskName); getProcessAPI().assignUserTask(humanTaskInstance.getId(), user.getId()); return humanTaskInstance; } public HumanTaskInstance waitForUserTaskAssignAndExecuteIt(final ProcessInstance processInstance, final String taskName, final User user, Map inputs) throws Exception { final HumanTaskInstance humanTaskInstance = waitForUserTaskAndGetIt(processInstance.getId(), taskName); getProcessAPI().assignAndExecuteUserTask(user.getId(), humanTaskInstance.getId(), inputs); return humanTaskInstance; } public HumanTaskInstance waitForUserTaskAndGetIt(final ProcessInstance processInstance, final String taskName) throws Exception { return waitForUserTaskAndGetIt(processInstance.getId(), taskName, DEFAULT_TIMEOUT); } public HumanTaskInstance waitForUserTaskAndGetIt(final long processInstanceId, final String taskName) throws Exception { return waitForUserTaskAndGetIt(processInstanceId, taskName, DEFAULT_TIMEOUT); } public HumanTaskInstance waitForUserTaskAndGetIt(final String taskName) throws Exception { return waitForUserTaskAndGetIt(-1, taskName, DEFAULT_TIMEOUT); } public long waitForUserTask(final long processInstanceId, final String taskName) throws Exception { return waitForUserTask(processInstanceId, taskName, DEFAULT_TIMEOUT); } public long waitForUserTask(final String taskName) throws Exception { return waitForUserTask(-1, taskName, DEFAULT_TIMEOUT); } private HumanTaskInstance waitForUserTaskAndGetIt(final long processInstanceId, final String taskName, final int timeout) throws Exception { final long activityInstanceId = waitForUserTask(processInstanceId, taskName, timeout); final HumanTaskInstance getHumanTaskInstance = getHumanTaskInstance(activityInstanceId); assertNotNull(getHumanTaskInstance); return getHumanTaskInstance; } private long waitForUserTask(final long processInstanceId, final String taskName, final int timeout) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException, TimeoutException { final Map readyTaskEvent; if (processInstanceId > 0) { readyTaskEvent = ClientEventUtil.getReadyFlowNodeEvent(processInstanceId, taskName); } else { readyTaskEvent = ClientEventUtil.getReadyFlowNodeEvent(taskName); } return ClientEventUtil.executeWaitServerCommand(getCommandAPI(), readyTaskEvent, timeout); } private HumanTaskInstance getHumanTaskInstance(final Long id) throws ActivityInstanceNotFoundException, RetrieveException { if (id != null) { return getProcessAPI().getHumanTaskInstance(id); } throw new RuntimeException("no id returned for human task "); } private ActivityInstance getActivityInstance(final Long id) throws ActivityInstanceNotFoundException, RetrieveException { if (id != null) { return getProcessAPI().getActivityInstance(id); } throw new RuntimeException("no id returned for activity instance "); } private FlowNodeInstance getFlowNodeInstance(final Long id) throws RuntimeException { try { return getProcessAPI().getFlowNodeInstance(id); } catch (final FlowNodeInstanceNotFoundException e) { throw new RuntimeException("no id returned for flow node instance "); } } public long waitForUserTask(final ProcessInstance processInstance, final String taskName) throws Exception { return waitForUserTask(processInstance.getId(), taskName, DEFAULT_TIMEOUT); } public void deleteSupervisor(final long id) throws BonitaException { getProcessAPI().deleteSupervisor(id); } public void deleteSupervisor(final ProcessSupervisor supervisor) throws BonitaException { getProcessAPI().deleteSupervisor(supervisor.getSupervisorId()); } @Deprecated public List waitForPendingTasks(final long userId, final int nbPendingTasks) throws Exception { final WaitForPendingTasks waitUntil = new WaitForPendingTasks(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, nbPendingTasks, userId, getProcessAPI()); assertTrue("no pending user task instances are found", waitUntil.waitUntil()); return waitUntil.getResults(); } @Deprecated public List waitForPendingTasks(final User user, final int nbPendingTasks) throws Exception { return waitForPendingTasks(user.getId(), nbPendingTasks); } public StartProcessUntilStep startProcessAndWaitForTask(final long processDefinitionId, final String taskName) throws Exception { final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinitionId); final ActivityInstance task = waitForUserTaskAndGetIt(processInstance.getId(), taskName); return new StartProcessUntilStep(processInstance, task); } @Deprecated public ArchivedActivityInstance waitForArchivedActivity(final long activityId, final TestStates state) throws Exception { final WaitForArchivedActivity waitForArchivedActivity = new WaitForArchivedActivity(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, activityId, state, getProcessAPI()); assertTrue(waitForArchivedActivity.waitUntil()); final ArchivedActivityInstance archivedActivityInstance = waitForArchivedActivity.getArchivedActivityInstance(); assertNotNull(archivedActivityInstance); return archivedActivityInstance; } @Deprecated public void waitForCompletedArchivedStep(final String value, final long id, final String displayName, final String displayDescription) throws Exception { final WaitForCompletedArchivedStep waitUntil = new WaitForCompletedArchivedStep(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, value, id, getProcessAPI()); assertTrue(waitUntil.waitUntil()); final ArchivedHumanTaskInstance saUserTaskInstance = waitUntil.getArchivedTask(); assertEquals(displayName, saUserTaskInstance.getDisplayName()); assertEquals(displayDescription, saUserTaskInstance.getDisplayDescription()); } public void waitForProcessToFinish(final ProcessInstance processInstance) throws Exception { waitForProcessToFinish(processInstance.getId()); } public void waitForProcessToFinish(final long processInstanceId) throws Exception { waitForProcessToFinish(processInstanceId, DEFAULT_TIMEOUT); } private void waitForProcessToFinish(final long processInstanceId, final int timeout) throws Exception { ClientEventUtil.executeWaitServerCommand(getCommandAPI(), ClientEventUtil.getProcessInstanceFinishedEvent(processInstanceId), timeout); } public void waitForProcessToBeInState(final ProcessInstance processInstance, final ProcessInstanceState state) throws Exception { waitForProcessToBeInState(processInstance.getId(), state); } public void waitForProcessToBeInState(final long processInstanceId, final ProcessInstanceState state) throws Exception { waitForProcessToBeInState(processInstanceId, state, DEFAULT_TIMEOUT); } public void waitForProcessToBeInState(final long processInstanceId, final ProcessInstanceState state, final int timeoutInMillis) throws Exception { ClientEventUtil.executeWaitServerCommand(getCommandAPI(), ClientEventUtil.getProcessInstanceInState(processInstanceId, state.getId()), timeoutInMillis); } public void waitForInitializingProcess() throws Exception { ClientEventUtil.executeWaitServerCommand(getCommandAPI(), ClientEventUtil.getProcessInstanceInState(ProcessInstanceState.INITIALIZING.getId()), DEFAULT_TIMEOUT); } private Long waitForFlowNode(final long processInstanceId, final TestStates state, final String flowNodeName, final boolean useRootProcessInstance, final int timeoutInMillis) throws Exception { Map params; if (useRootProcessInstance) { params = ClientEventUtil.getFlowNodeInState(processInstanceId, state.getStateName(), flowNodeName); } else { params = ClientEventUtil.getFlowNodeInStateWithParentId(processInstanceId, state.getStateName(), flowNodeName); } return ClientEventUtil.executeWaitServerCommand(getCommandAPI(), params, timeoutInMillis); } public FlowNodeInstance waitForFlowNodeInReadyState(final ProcessInstance processInstance, final String flowNodeName, final boolean useRootProcessInstance) throws Exception { final Long flowNodeInstanceId = waitForFlowNode(processInstance.getId(), TestStates.READY, flowNodeName, useRootProcessInstance, DEFAULT_TIMEOUT); return getFlowNode(flowNodeInstanceId); } public FlowNodeInstance waitForFlowNodeInExecutingState(final ProcessInstance processInstance, final String flowNodeName, final boolean useRootProcessInstance) throws Exception { final Long activityId = waitForFlowNodeInState(processInstance, flowNodeName, TestStates.EXECUTING, useRootProcessInstance); return getFlowNodeInstance(activityId); } public ArchivedActivityInstance waitForActivityInCompletedState(final ProcessInstance processInstance, final String flowNodeName, final boolean useRootProcessInstance) throws Exception { final Long activityId = waitForFlowNodeInState(processInstance, flowNodeName, TestStates.NORMAL_FINAL, useRootProcessInstance); // we must wait for the activity to be completely archived: final WaitForArchivedActivity waitForArchivedActivity = new WaitForArchivedActivity(30, 8000, activityId, TestStates.NORMAL_FINAL, getProcessAPI()); assertTrue(waitForArchivedActivity.waitUntil()); return waitForArchivedActivity.getArchivedActivityInstance(); } public FlowNodeInstance waitForFlowNodeInWaitingState(final ProcessInstance processInstance, final String flowNodeName, final boolean useRootProcessInstance) throws Exception { final Long activityId = waitForFlowNodeInState(processInstance, flowNodeName, TestStates.WAITING, useRootProcessInstance); return getFlowNodeInstance(activityId); } public Long waitForFlowNodeInState(final ProcessInstance processInstance, final String flowNodeName, final TestStates state, final boolean useRootProcessInstance) throws Exception { return waitForFlowNode(processInstance.getId(), state, flowNodeName, useRootProcessInstance, DEFAULT_TIMEOUT); } public ActivityInstance waitForTaskInState(final ProcessInstance processInstance, final String flowNodeName, final TestStates state) throws Exception { final Long activityId = waitForFlowNode(processInstance.getId(), state, flowNodeName, true, DEFAULT_TIMEOUT); return getActivityInstance(activityId); } private FlowNodeInstance getFlowNode(final Long flowNodeInstanceId) throws FlowNodeInstanceNotFoundException { final FlowNodeInstance flowNodeInstance = getProcessAPI().getFlowNodeInstance(flowNodeInstanceId); assertNotNull(flowNodeInstance); return flowNodeInstance; } public ActivityInstance waitForTaskToFail(final ProcessInstance processInstance) throws Exception { final Long activityId = ClientEventUtil.executeWaitServerCommand(getCommandAPI(), ClientEventUtil.getFlowNodeInState(processInstance.getId(), TestStates.FAILED.getStateName()), DEFAULT_TIMEOUT); return getActivityInstance(activityId); } public FlowNodeInstance waitForFlowNodeInFailedState(final ProcessInstance processInstance) throws Exception { final Long activityId = ClientEventUtil.executeWaitServerCommand(getCommandAPI(), ClientEventUtil.getFlowNodeInState(processInstance.getId(), TestStates.FAILED.getStateName()), DEFAULT_TIMEOUT); return getFlowNodeInstance(activityId); } public void waitForFlowNodeInFailedState(final String flowNodeInstanceName) throws Exception { final long failedTaskId = ClientEventUtil.executeWaitServerCommand(getCommandAPI(), ClientEventUtil.getFlowNodeInState(TestStates.FAILED.getStateName(), flowNodeInstanceName), DEFAULT_TIMEOUT); assertNotNull(failedTaskId); } public FlowNodeInstance waitForFlowNodeInFailedState(final ProcessInstance processInstance, final String flowNodeName) throws Exception { final Long flowNodeInstanceId = waitForFlowNodeInState(processInstance, flowNodeName, TestStates.FAILED, true); return getFlowNodeInstance(flowNodeInstanceId); } public GatewayInstance waitForGateway(final ProcessInstance processInstance, final String name) throws Exception { final Map readyTaskEvent = ClientEventUtil.getFlowNode(processInstance.getId(), name); final Long activityInstanceId = ClientEventUtil.executeWaitServerCommand(getCommandAPI(), readyTaskEvent, DEFAULT_REPEAT_EACH * DEFAULT_TIMEOUT); final FlowNodeInstance flowNodeInstance = getProcessAPI().getFlowNodeInstance(activityInstanceId); assertNotNull(flowNodeInstance); if (flowNodeInstance instanceof GatewayInstance) { return (GatewayInstance) flowNodeInstance; } return null; } @Deprecated public WaitForFinalArchivedActivity waitForFinalArchivedActivity(final String activityName, final ProcessInstance processInstance) throws Exception { final WaitForFinalArchivedActivity waitForFinalArchivedActivity = new WaitForFinalArchivedActivity( DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, activityName, processInstance.getId(), getProcessAPI()); assertTrue(activityName + " should be finished and archived", waitForFinalArchivedActivity.waitUntil()); return waitForFinalArchivedActivity; } @Deprecated public WaitForEvent waitForEventInWaitingState(final ProcessInstance processInstance, final String eventName) throws Exception { return waitForEvent(processInstance, eventName, TestStates.WAITING); } @Deprecated public WaitForEvent waitForEvent(final ProcessInstance processInstance, final String eventName, final TestStates state) throws Exception { return waitForEvent(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, processInstance.getId(), eventName, state); } @Deprecated private WaitForEvent waitForEvent(final int repeatEach, final int timeout, final long processInstanceId, final String eventName, final TestStates state) throws Exception { final WaitForEvent waitForEvent = new WaitForEvent(repeatEach, timeout, eventName, processInstanceId, state, getProcessAPI()); assertTrue("Expected 1 activities in " + state + " state", waitForEvent.waitUntil()); return waitForEvent; } @Deprecated public void checkNbOfOpenTasks(final ProcessInstance processInstance, final String message, final int expectedNbOfOpenActivities) throws Exception { final CheckNbOfOpenActivities checkNbOfOpenActivities = new CheckNbOfOpenActivities(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, false, processInstance, expectedNbOfOpenActivities, getProcessAPI()); checkNbOfOpenActivities.waitUntil(); assertEquals(message, expectedNbOfOpenActivities, checkNbOfOpenActivities.getNumberOfOpenActivities()); } @Deprecated public CheckNbPendingTaskOf checkNbPendingTaskOf(final int nbOfPendingTasks, final User user) throws Exception { return checkNbPendingTaskOf(false, nbOfPendingTasks, user); } @Deprecated public CheckNbPendingTaskOf checkNbPendingTaskOf(final boolean throwExceptions, final int nbOfPendingTasks, final User user) throws Exception { return checkNbPendingTaskOf(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, throwExceptions, nbOfPendingTasks, user); } @Deprecated private CheckNbPendingTaskOf checkNbPendingTaskOf(final int repeatEach, final int timeout, final boolean throwExceptions, final int nbOfPendingTasks, final User user) throws Exception { final CheckNbPendingTaskOf checkNbPendingTaskOf = new CheckNbPendingTaskOf(getProcessAPI(), repeatEach, timeout, throwExceptions, nbOfPendingTasks, user); assertTrue("There isn't " + nbOfPendingTasks + " pending task", checkNbPendingTaskOf.waitUntil()); return checkNbPendingTaskOf; } @Deprecated public void checkNbOfOpenActivities(final ProcessInstance processInstance, final int nbActivities) throws Exception { checkNbOfOpenActivities(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, processInstance, nbActivities); } @Deprecated private void checkNbOfOpenActivities(final int repeatEach, final int timeout, final ProcessInstance processInstance, final int nbActivities) throws Exception { assertTrue("Expected " + nbActivities + " OPEN activities for process instance", new CheckNbOfOpenActivities(repeatEach, timeout, true, processInstance, nbActivities, getProcessAPI()).waitUntil()); } @Deprecated public void checkProcessInstanceIsArchived(final ProcessInstance processInstance) throws Exception { checkProcessInstanceIsArchived(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, processInstance); } @Deprecated private void checkProcessInstanceIsArchived(final int repeatEach, final int timeout, final ProcessInstance processInstance) throws Exception { assertTrue(new CheckProcessInstanceIsArchived(repeatEach, timeout, processInstance.getId(), getProcessAPI()) .waitUntil()); } @Deprecated public void checkNbOfArchivedActivityInstances(final ProcessInstance processInstance, final int expected) throws Exception { checkNbOfArchivedActivityInstances(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, processInstance, expected); } @Deprecated private void checkNbOfArchivedActivityInstances(final int repeatEach, final int timeout, final ProcessInstance processInstance, final int expected) throws Exception { assertTrue( new CheckNbOfArchivedActivityInstances(repeatEach, timeout, processInstance, expected, getProcessAPI()) .waitUntil()); } @Deprecated public void checkNbOfArchivedActivities(final ProcessInstance processInstance, final int nbAbortedActivities) throws Exception { checkNbOfArchivedActivities(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, processInstance, nbAbortedActivities); } @Deprecated private void checkNbOfArchivedActivities(final int repeatEach, final int timeout, final ProcessInstance processInstance, final int nbAbortedActivities) throws Exception { final CheckNbOfArchivedActivities checkNbOfActivities = new CheckNbOfArchivedActivities(getProcessAPI(), repeatEach, timeout, true, processInstance, nbAbortedActivities, TestStates.ABORTED); final boolean waitUntil = checkNbOfActivities.waitUntil(); assertTrue("Expected " + nbAbortedActivities + " in the aboted state. But was " + checkNbOfActivities.getResult().size(), waitUntil); } @Deprecated public CheckNbOfProcessInstances checkNbOfProcessInstances(final int nbOfProcInst) throws Exception { final CheckNbOfProcessInstances checkNbOfProcessInstances = new CheckNbOfProcessInstances(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, nbOfProcInst, getProcessAPI()); assertTrue(checkNbOfProcessInstances.waitUntil()); return checkNbOfProcessInstances; } @Deprecated public CheckNbOfProcessInstances checkNbOfProcessInstances(final int nbOfProcInst, final ProcessInstanceCriterion orderBy) throws Exception { return checkNbOfProcessInstances(DEFAULT_REPEAT_EACH, DEFAULT_TIMEOUT, nbOfProcInst, orderBy); } @Deprecated private CheckNbOfProcessInstances checkNbOfProcessInstances(final int repeatEach, final int timeout, final int nbOfProcInst, final ProcessInstanceCriterion orderBy) throws Exception { final CheckNbOfProcessInstances checkNbOfProcessInstances = new CheckNbOfProcessInstances(repeatEach, timeout, nbOfProcInst, orderBy, getProcessAPI()); assertTrue(checkNbOfProcessInstances.waitUntil()); return checkNbOfProcessInstances; } public void checkFlowNodeWasntExecuted(final long processInstancedId, final String flowNodeName) { final List archivedActivityInstances = getProcessAPI().getArchivedActivityInstances( processInstancedId, 0, 200, ActivityInstanceCriterion.DEFAULT); for (final ArchivedActivityInstance archivedActivityInstance : archivedActivityInstances) { assertNotEquals(flowNodeName, archivedActivityInstance.getName()); } } public void checkWasntExecuted(final ProcessInstance parentProcessInstance, final String flowNodeName) throws InvalidSessionException, SearchException { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 20); searchOptionsBuilder.filter(ArchivedFlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, parentProcessInstance.getId()); searchOptionsBuilder.filter(ArchivedFlowNodeInstanceSearchDescriptor.NAME, flowNodeName); final SearchResult searchArchivedActivities = getProcessAPI() .searchArchivedFlowNodeInstances(searchOptionsBuilder.done()); assertEquals(0, searchArchivedActivities.getCount()); } public void skipTask(final long activityId) throws UpdateException { getProcessAPI().setActivityStateByName(activityId, ActivityStates.SKIPPED_STATE); } public void skipTasks(final ProcessInstance processInstance) throws UpdateException { final List activityInstances = getProcessAPI().getActivities(processInstance.getId(), 0, 10); for (final ActivityInstance activityInstance : activityInstances) { final long activityInstanceId = activityInstance.getId(); skipTask(activityInstanceId); } } public void updateActivityInstanceVariablesWithOperations(final String updatedValue, final long activityInstanceId, final String dataName, final boolean isTransient) throws InvalidExpressionException, UpdateException { final Operation stringOperation = BuildTestUtil.buildStringOperation(dataName, updatedValue, isTransient); final List operations = new ArrayList<>(); operations.add(stringOperation); getProcessAPI().updateActivityInstanceVariables(operations, activityInstanceId, null); } protected void cleanCategories() throws DeletionException { final long numberOfCategories = getProcessAPI().getNumberOfCategories(); if (numberOfCategories > 0) { final List categories = getProcessAPI().getCategories(0, 5000, CategoryCriterion.NAME_ASC); for (final Category category : categories) { getProcessAPI().deleteCategory(category.getId()); } } } protected void cleanProcessDefinitions() throws BonitaException { final List processes = getProcessAPI().getProcessDeploymentInfos(0, 200, ProcessDeploymentInfoCriterion.DEFAULT); for (final ProcessDeploymentInfo processDeploymentInfo : processes) { if (ActivationState.ENABLED.equals(processDeploymentInfo.getActivationState())) { getProcessAPI().disableProcess(processDeploymentInfo.getProcessId()); } getProcessAPI().deleteProcessDefinition(processDeploymentInfo.getProcessId()); } } protected void cleanSupervisors() throws SearchException, DeletionException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 200); builder.sort(ProcessSupervisorSearchDescriptor.ID, Order.ASC); final List supervisors = getProcessAPI().searchProcessSupervisors(builder.done()) .getResult(); for (final ProcessSupervisor supervisor : supervisors) { getProcessAPI().deleteSupervisor(supervisor.getSupervisorId()); } } protected void cleanApplications() throws SearchException, DeletionException { final SearchResult applications = getApplicationAPI() .searchIApplications(new SearchOptionsBuilder(0, 100).done()); for (IApplication application : applications.getResult()) { if (application.isEditable()) { getApplicationAPI().deleteApplication(application.getId()); } } } protected void cleanPages() throws SearchException, DeletionException { getPageAPI().deletePages(getPageAPI() .searchPages(new SearchOptionsBuilder(0, 100) .filter(PageSearchDescriptor.PROVIDED, false).done()) .getResult() .stream().map(Page::getId).collect(Collectors.toList())); } protected void cleanProcessInstances() throws DeletionException { final List processInstances = getProcessAPI().getProcessInstances(0, 1000, ProcessInstanceCriterion.DEFAULT); if (!processInstances.isEmpty()) { for (final ProcessInstance processInstance : processInstances) { getProcessAPI().deleteProcessInstance(processInstance.getId()); } } } protected void cleanArchiveProcessInstances() throws DeletionException { final List archivedProcessInstances = getProcessAPI().getArchivedProcessInstances(0, 1000, ProcessInstanceCriterion.DEFAULT); if (!archivedProcessInstances.isEmpty()) { for (final ArchivedProcessInstance archivedProcessInstance : archivedProcessInstances) { getProcessAPI().deleteArchivedProcessInstancesInAllStates(archivedProcessInstance.getSourceObjectId()); } } } protected void cleanGroups() throws DeletionException { final long numberOfGroups = getIdentityAPI().getNumberOfGroups(); if (numberOfGroups > 0) { final List groups = getIdentityAPI().getGroups(0, Long.valueOf(numberOfGroups).intValue(), GroupCriterion.NAME_ASC); for (final Group group : groups) { getIdentityAPI().deleteGroup(group.getId()); } } } protected void cleanRoles() throws DeletionException { final long numberOfRoles = getIdentityAPI().getNumberOfRoles(); if (numberOfRoles > 0) { final List roles = getIdentityAPI().getRoles(0, Long.valueOf(numberOfRoles).intValue(), RoleCriterion.NAME_ASC); for (final Role role : roles) { getIdentityAPI().deleteRole(role.getId()); } } } protected void cleanUsers() throws DeletionException { final long numberOfUsers = getIdentityAPI().getNumberOfUsers(); if (numberOfUsers > 0) { final List users = getIdentityAPI().getUsers(0, Long.valueOf(numberOfUsers).intValue(), UserCriterion.USER_NAME_ASC); for (final User user : users) { getIdentityAPI().deleteUser(user.getId()); } } } protected void cleanCommands() throws SearchException, CommandNotFoundException, DeletionException { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 1000); searchOptionsBuilder.filter(CommandSearchDescriptor.SYSTEM, false); searchOptionsBuilder.differentFrom(CommandSearchDescriptor.NAME, ClientEventUtil.ADD_HANDLER_COMMAND); searchOptionsBuilder.differentFrom(CommandSearchDescriptor.NAME, ClientEventUtil.WAIT_SERVER_COMMAND); final SearchResult searchCommands = getCommandAPI() .searchCommands(searchOptionsBuilder.done()); final List commands = searchCommands.getResult(); if (searchCommands.getCount() > 0) { for (final CommandDescriptor command : commands) { getCommandAPI().unregister(command.getName()); } } } protected void cleanBdm() throws BonitaException { TenantAdministrationAPI tenantAdministrationAPI = getTenantAdministrationAPI(); MaintenanceAPI maintenanceAPI = getMaintenanceAPI(); if (tenantAdministrationAPI.getBusinessDataModelResource() != TenantResource.NONE) { if (maintenanceAPI.getMaintenanceDetails().getMaintenanceState() == MaintenanceDetails.State.DISABLED) { maintenanceAPI.enableMaintenanceMode(); } try { tenantAdministrationAPI.cleanAndUninstallBusinessDataModel(); } finally { maintenanceAPI.disableMaintenanceMode(); } } } /** * Deploys a Business Data Model, replacing any previously installed one. * Handles pausing/resuming the tenant around the installation. * * @param bom the business object model to deploy */ protected String installBusinessDataModel(BusinessObjectModel bom) throws Exception { final byte[] zip = new BusinessObjectModelConverter().zip(bom); getTenantAdministrationAPI().pause(); getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel(); var bdmVersion = getTenantAdministrationAPI().updateBusinessDataModel(zip); getTenantAdministrationAPI().resume(); return bdmVersion; } public PlatformInformationAPI getPlatformInformationAPI() { return getApiClient().getPlatformInformationAPI(); } public ProcessAPI getProcessAPI() { return getApiClient().getProcessAPI(); } public IdentityAPI getIdentityAPI() { return getApiClient().getIdentityAPI(); } public CommandAPI getCommandAPI() { return getApiClient().getCommandAPI(); } public ProfileAPI getProfileAPI() { return getApiClient().getProfileAPI(); } public PermissionAPI getPermissionAPI() { return getApiClient().getPermissionAPI(); } public PageAPI getPageAPI() { return getApiClient().getCustomPageAPI(); } public ApplicationAPI getLivingApplicationAPI() { return getApiClient().getLivingApplicationAPI(); } public ApplicationAPI getApplicationAPI() { return getApiClient().getApplicationAPI(); } public TenantAdministrationAPI getTenantAdministrationAPI() { return getApiClient().getTenantAdministrationAPI(); } public MaintenanceAPI getMaintenanceAPI() { return getApiClient().getMaintenanceAPI(); } public void deleteSupervisors(final List processSupervisors) throws BonitaException { if (processSupervisors != null) { for (final ProcessSupervisor processSupervisor : processSupervisors) { deleteSupervisor(processSupervisor.getSupervisorId()); } } } public void buildAndAttachDocument(final ProcessInstance processInstance) throws BonitaException { final String documentName = String.valueOf(System.currentTimeMillis()); final Document doc = BuildTestUtil.buildDocument(documentName); buildAndAttachDocument(processInstance, documentName, doc.getContentFileName()); } public void buildAndAttachDocument(final ProcessInstance processInstance, final String documentName, final String fileName) throws BonitaException { final Document doc = BuildTestUtil.buildDocument(documentName); getProcessAPI().attachDocument(processInstance.getId(), documentName, fileName, doc.getContentMimeType(), documentName.getBytes()); } public String getAttachmentDocumentName(final ProcessInstance processInstance) throws BonitaException { final Document attachment = getAttachmentWithoutItsContent(processInstance); return attachment.getName(); } public Document getAttachmentWithoutItsContent(final ProcessInstance processInstance) throws BonitaException { final List attachments = getProcessAPI().getLastVersionOfDocuments(processInstance.getId(), 0, 1, DocumentCriterion.DEFAULT); assertTrue("No attachments found!", attachments != null && attachments.size() == 1); return attachments.get(0); } public ProcessInstance deployAndEnableProcessWithActorAndStartIt(final BusinessArchive businessArchive, final User user) throws BonitaException { final ProcessDefinition processDefinition = deployAndEnableProcessWithActor(businessArchive, BuildTestUtil.ACTOR_NAME, user); final ProcessInstance processInstance = getProcessAPI().startProcess(processDefinition.getId()); assertNotNull(processInstance); return processInstance; } public ArchivedDataInstance getArchivedDataInstance(final List archivedDataInstances, final String dataName) { return archivedDataInstances.stream() .filter(archDataInstance -> archDataInstance.getName().equals(dataName)) .findFirst() .orElseThrow(() -> new AssertionError("No archived data instance found for name: " + dataName)); } public List createNbProcessDefinitionWithHumanAndAutomaticAndDeployWithActor(final int nbProcess, final User user, final List stepNames, final List isHuman) throws BonitaException { try { final List processDefinitions = new ArrayList<>(); final List designProcessDefinitions = BuildTestUtil .buildNbProcessDefinitionWithHumanAndAutomatic(nbProcess, stepNames, isHuman); for (final DesignProcessDefinition designProcessDefinition : designProcessDefinitions) { processDefinitions .add(deployAndEnableProcessWithActor(designProcessDefinition, BuildTestUtil.ACTOR_NAME, user)); } return processDefinitions; } catch (InvalidProcessDefinitionException e) { throw new BonitaException(e); } } protected void assertThatXmlHaveNoDifferences(final String xmlPrettyFormatExpected, final String xmlPrettyFormatExported) throws SAXException, IOException { XMLUnit.setIgnoreWhitespace(true); XMLUnit.setIgnoreAttributeOrder(true); final DetailedDiff diff = new DetailedDiff( XMLUnit.compareXML(xmlPrettyFormatExported, xmlPrettyFormatExpected)); final List allDifferences = diff.getAllDifferences(); assertThat(allDifferences).as("should have no differences between:\n%s\n and:\n%s\n", xmlPrettyFormatExpected, xmlPrettyFormatExported).isEmpty(); } public BarResource getBarResource(final String path, final String name, Class clazz) throws IOException { try (final InputStream stream = clazz.getResourceAsStream(path)) { assertThat(stream).isNotNull(); final byte[] byteArray = IOUtils.toByteArray(stream); return new BarResource(name, byteArray); } } protected byte[] createTestPageContent(final String pageName, final String displayName, final String description) throws Exception { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { final ZipOutputStream zos = new ZipOutputStream(baos); zos.putNextEntry(new ZipEntry("Index.groovy")); zos.write("return \"\";".getBytes()); zos.putNextEntry(new ZipEntry("page.properties")); zos.write(("name=" + pageName + "\n" + "displayName=" + displayName + "\n" + "description=" + description + "\n").getBytes()); zos.closeEntry(); return baos.toByteArray(); } } protected BusinessObjectModel buildBOM() { final SimpleField name = new SimpleField(); name.setName("name"); name.setType(FieldType.STRING); final SimpleField age = new SimpleField(); age.setName("age"); age.setType(FieldType.INTEGER); final BusinessObject countryBO = new BusinessObject(); countryBO.setQualifiedName(COUNTRY_QUALIFIED_NAME); countryBO.addField(name); countryBO.addUniqueConstraint("uk_name", "name"); final SimpleField street = new SimpleField(); street.setName("street"); street.setType(FieldType.STRING); final SimpleField city = new SimpleField(); city.setName("city"); city.setType(FieldType.STRING); final RelationField country = new RelationField(); country.setType(RelationField.Type.AGGREGATION); country.setFetchType(RelationField.FetchType.LAZY); country.setName("country"); country.setCollection(Boolean.FALSE); country.setNullable(Boolean.TRUE); country.setReference(countryBO); final BusinessObject addressBO = new BusinessObject(); addressBO.setQualifiedName(ADDRESS_QUALIFIED_NAME); addressBO.addField(street); addressBO.addField(city); addressBO.addField(country); addressBO.addQuery(COUNT_ADDRESS, "SELECT count(a) FROM Address a", Long.class.getName()); addressBO.addUniqueConstraint("addressUK_with_relation", "city", "country"); final BusinessObject dogBO = new BusinessObject(); dogBO.setQualifiedName(DOG_QUALIFIED_NAME); dogBO.addField(name); dogBO.addField(age); final BusinessObject catBO = new BusinessObject(); catBO.setQualifiedName(CAT_QUALIFIED_NAME); catBO.addField(name); catBO.addField(age); final RelationField addresses = new RelationField(); addresses.setType(RelationField.Type.AGGREGATION); addresses.setFetchType(RelationField.FetchType.EAGER); addresses.setName("addresses"); addresses.setCollection(Boolean.TRUE); addresses.setNullable(Boolean.TRUE); addresses.setReference(addressBO); final RelationField address = new RelationField(); address.setType(RelationField.Type.AGGREGATION); address.setFetchType(RelationField.FetchType.LAZY); address.setName("address"); address.setCollection(Boolean.FALSE); address.setNullable(Boolean.TRUE); address.setReference(addressBO); final RelationField dog = new RelationField(); dog.setType(RelationField.Type.COMPOSITION); dog.setFetchType(RelationField.FetchType.EAGER); dog.setName("dog"); dog.setCollection(Boolean.FALSE); dog.setNullable(Boolean.TRUE); dog.setReference(dogBO); final RelationField cat = new RelationField(); cat.setType(RelationField.Type.COMPOSITION); cat.setFetchType(RelationField.FetchType.LAZY); //bug on lazy attribute in composition starting with a capital letter, see: https://bonitasoft.atlassian.net/browse/BS-16031 cat.setName("Cat"); cat.setCollection(Boolean.FALSE); cat.setNullable(Boolean.TRUE); cat.setReference(catBO); final SimpleField firstName = new SimpleField(); firstName.setName("firstName"); firstName.setType(FieldType.STRING); firstName.setLength(10); final SimpleField birthDate = new SimpleField(); birthDate.setName("birthDate"); birthDate.setType(FieldType.LOCALDATE); birthDate.setNullable(Boolean.TRUE); final SimpleField lastName = new SimpleField(); lastName.setName("lastName"); lastName.setType(FieldType.STRING); lastName.setNullable(Boolean.FALSE); final SimpleField phoneNumbers = new SimpleField(); phoneNumbers.setName("phoneNumbers"); phoneNumbers.setType(FieldType.STRING); phoneNumbers.setLength(10); phoneNumbers.setCollection(Boolean.TRUE); final SimpleField hireDate = new SimpleField(); hireDate.setName("hireDate"); hireDate.setType(FieldType.DATE); final SimpleField booleanField = new SimpleField(); booleanField.setName("booleanField"); booleanField.setType(FieldType.BOOLEAN); final BusinessObject employee = new BusinessObject(); employee.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); employee.addField(hireDate); employee.addField(booleanField); employee.addField(firstName); employee.addField(lastName); employee.addField(phoneNumbers); employee.addField(addresses); employee.addField(address); employee.addField(dog); employee.addField(cat); employee.addField(birthDate); employee.setDescription("Describe a simple employee"); employee.addUniqueConstraint("uk_fl", "firstName", "lastName"); final Query getEmployeeByPersistId = employee .addQuery("findByPersistId", "SELECT e FROM Employee e WHERE e.persistenceId=:id", EMPLOYEE_QUALIFIED_NAME); getEmployeeByPersistId.addQueryParameter("id", Long.class.getName()); final Query getEmployeeByPhoneNumber = employee.addQuery(GET_EMPLOYEE_BY_PHONE_NUMBER_QUERY_NAME, "SELECT e FROM Employee e WHERE :phoneNumber IN ELEMENTS(e.phoneNumbers)", List.class.getName()); getEmployeeByPhoneNumber.addQueryParameter("phoneNumber", String.class.getName()); final Query findByFirstNAmeAndLastNameNewOrder = employee.addQuery(FIND_BY_FIRST_NAME_AND_LAST_NAME_NEW_ORDER, "SELECT e FROM Employee e WHERE e.firstName =:firstName AND e.lastName = :lastName ORDER BY e.lastName", List.class.getName()); findByFirstNAmeAndLastNameNewOrder.addQueryParameter("firstName", String.class.getName()); findByFirstNAmeAndLastNameNewOrder.addQueryParameter("lastName", String.class.getName()); final Query findByFirstNameFetchAddresses = employee.addQuery(FIND_BY_FIRST_NAME_FETCH_ADDRESSES, "SELECT e FROM Employee e INNER JOIN FETCH e.addresses WHERE e.firstName =:firstName ORDER BY e.lastName", List.class.getName()); findByFirstNameFetchAddresses.addQueryParameter("firstName", String.class.getName()); final Query findByHireDate = employee.addQuery(FIND_BY_HIRE_DATE_RANGE, "SELECT e FROM Employee e WHERE e.hireDate >=:date1 and e.hireDate <=:date2", List.class.getName()); findByHireDate.addQueryParameter("date1", Date.class.getName()); findByHireDate.addQueryParameter("date2", Date.class.getName()); final Query countForFindByHireDate = employee.addQuery(COUNT_FOR_FIND_BY_HIRE_DATE_RANGE, "SELECT count(e) FROM Employee e WHERE e.hireDate >=:date1 and e.hireDate <=:date2", Long.class.getName()); countForFindByHireDate.addQueryParameter("date1", Date.class.getName()); countForFindByHireDate.addQueryParameter("date2", Date.class.getName()); employee.addQuery(COUNT_EMPLOYEE, "SELECT COUNT(e) FROM Employee e", Long.class.getName()); final Query findEmployeesWithFirstNames = employee.addQuery(FIND_EMPLOYEE_WITH_FIRSTNAMES, "SELECT e FROM Employee e WHERE e.firstName IN (:firstNames) ORDER BY e.firstName", List.class.getName()); findEmployeesWithFirstNames.addQueryParameter("firstNames", String[].class.getName()); employee.addIndex("IDX_LSTNM", "lastName"); employee.addIndex("IDX_LSTNM", "address"); final BusinessObject person = new BusinessObject(); person.setQualifiedName(PERSON_QUALIFIED_NAME); person.addField(hireDate); person.addField(firstName); person.addField(lastName); person.addField(phoneNumbers); person.setDescription("Describe a simple person"); person.addUniqueConstraint("uk_fl", "firstName", "lastName"); final BusinessObject productBO = new BusinessObject(); productBO.setQualifiedName(PRODUCT_QUALIFIED_NAME); productBO.addField(name); final RelationField products = new RelationField(); products.setType(RelationField.Type.AGGREGATION); products.setFetchType(RelationField.FetchType.LAZY); products.setName("products"); products.setCollection(Boolean.TRUE); products.setNullable(Boolean.TRUE); products.setReference(productBO); final SimpleField releaseYear = new SimpleField(); releaseYear.setName("releaseYear"); releaseYear.setType(FieldType.STRING); final BusinessObject editionBO = new BusinessObject(); editionBO.setQualifiedName("com.company.model.Edition"); editionBO.addField(releaseYear); final RelationField editionField = new RelationField(); editionField.setType(RelationField.Type.COMPOSITION); editionField.setFetchType(RelationField.FetchType.EAGER); editionField.setName("editions"); editionField.setCollection(Boolean.TRUE); editionField.setNullable(Boolean.TRUE); editionField.setReference(editionBO); final BusinessObject catalogBO = new BusinessObject(); catalogBO.setQualifiedName(PRODUCT_CATALOG_QUALIFIED_NAME); catalogBO.addField(name); catalogBO.addField(products); catalogBO.addField(editionField); final BusinessObjectModel model = new BusinessObjectModel(); model.addBusinessObject(employee); model.addBusinessObject(person); model.addBusinessObject(addressBO); model.addBusinessObject(dogBO); model.addBusinessObject(catBO); model.addBusinessObject(countryBO); model.addBusinessObject(productBO); model.addBusinessObject(editionBO); model.addBusinessObject(catalogBO); return model; } protected void checkAllParametersAreSerializable(final Class api) { final Method[] methods = api.getMethods(); for (final Method method : methods) { if (!isADefaultMethod(method)) { final Class[] parameterTypes = method.getParameterTypes(); for (final Class parameterType : parameterTypes) { if (!parameterType.isPrimitive() && !Collection.class.isAssignableFrom(parameterType) && !Map.class.isAssignableFrom(parameterType)) { final boolean assignableFrom = Serializable.class.isAssignableFrom(parameterType); assertTrue( "Method: " + method.getName() + " of API: " + api.getName() + " contains an unserializable parameter " + parameterType, assignableFrom); } } final Class returnType = method.getReturnType(); if (!returnType.isPrimitive() && !Collection.class.isAssignableFrom(returnType) && !Map.class.isAssignableFrom(returnType)) { final boolean assignableFrom = Serializable.class.isAssignableFrom(returnType); assertTrue( "Method: " + method.getName() + " of API: " + api.getName() + " contains an unserializable return type " + returnType, assignableFrom); } } } } private boolean isADefaultMethod(final Method method) { return DEFAULT_METHODS.contains(method.getName()); } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/BDMTestUtil.java ================================================ /** * Copyright (C) 2021 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.util.function.Consumer; import javax.xml.bind.JAXBException; import org.bonitasoft.engine.bdm.BusinessObjectModelConverter; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.xml.sax.SAXException; public class BDMTestUtil { public static byte[] getZip(BusinessObjectModel model) throws IOException, JAXBException, SAXException { BusinessObjectModelConverter converter = new BusinessObjectModelConverter(); return converter.zip(model); } public static BusinessObjectModel buildSimpleBom(final String boQualifiedName) { return businessObjectModel(bom -> { bom.addBusinessObject(businessObject(boQualifiedName, (businessObject) -> { businessObject.addField(stringField("aField")); })); }); } public static BusinessObjectModel businessObjectModel(Consumer apply) { BusinessObjectModel businessObjectModel = new BusinessObjectModel(); apply.accept(businessObjectModel); return businessObjectModel; } public static BusinessObject businessObject(String boQualifiedName, Consumer apply) { final BusinessObject bo = new BusinessObject(); bo.setQualifiedName(boQualifiedName); apply.accept(bo); return bo; } public static SimpleField stringField(String name) { final SimpleField field = new SimpleField(); field.setName(name); field.setType(FieldType.STRING); return field; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/BuildTestUtil.java ================================================ /** * 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 static org.junit.Assert.assertNotNull; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.impl.BoundaryEventDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ConnectorDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.DocumentBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.SubProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.connector.Connector; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.operation.LeftOperand; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; public class BuildTestUtil { public static final String ACTOR_NAME = "Employee actor"; public static final String PROCESS_VERSION = "1.0"; public static final String PROCESS_NAME = "ProcessName"; public static final String EVENT_SUB_PROCESS_NAME = "eventSubProcess"; public static final String DESCRIPTION = "Coding all-night-long"; public static final String PASSWORD = "bpm"; public static final String USERNAME = "william.jobs"; public static final String GROUP_NAME = "R&D"; public static final String ROLE_NAME = "Developper"; public static byte[] buildConnectorImplementationFile(final String definitionId, final String definitionVersion, final String implementationId, final String implementationVersion, final String implementationClassname, String... dependencies) throws UnsupportedEncodingException { final StringBuilder stb = new StringBuilder(); stb.append("\n"); stb.append( "\n"); stb.append("\t").append(definitionId).append("\n"); stb.append("\t").append(definitionVersion).append("\n"); stb.append("\t").append(implementationClassname) .append("\n"); stb.append("\t").append(implementationId).append("\n"); stb.append("\t").append(implementationVersion).append("\n"); if (dependencies.length > 0) { stb.append("\t\n"); for (String dependency : dependencies) { stb.append("\t\t").append(dependency).append("\n"); } stb.append("\t\n"); } stb.append(""); return stb.toString().getBytes("UTF-8"); } public static BarResource generateConnectorImplementation(String definitionId, String definitionVersion, Class connectorClass) throws UnsupportedEncodingException { return new BarResource( connectorClass.getSimpleName() + ".impl", BuildTestUtil.buildConnectorImplementationFile(definitionId, definitionVersion, definitionId + "-impl", "generated", connectorClass.getName())); } public static BarResource generateConnectorImplementation(String definitionId, String definitionVersion, String className, String... dependencies) throws UnsupportedEncodingException { return new BarResource( className + ".impl", BuildTestUtil.buildConnectorImplementationFile(definitionId, definitionVersion, definitionId + "-impl", "generated", className, dependencies)); } public static BusinessArchiveBuilder buildBusinessArchiveWithConnectorAndUserFilter( final ProcessDefinitionBuilder processDefinitionBuilder, final List connectorImplementations, final List generateConnectorDependencies, final List userFilters) throws InvalidProcessDefinitionException { final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition( processDefinitionBuilder.done()); for (final BarResource barResource : connectorImplementations) { businessArchiveBuilder.addConnectorImplementation(barResource); } for (final BarResource barResource : generateConnectorDependencies) { businessArchiveBuilder.addClasspathResource(barResource); } for (final BarResource barResource : userFilters) { businessArchiveBuilder.addUserFilters(barResource); } return businessArchiveBuilder; } public static ProcessDefinitionBuilder buildProcessDefinitionWithCallActivity(final String processName, final Expression targetProcessExpr, final Expression targetVersionExpr) { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName, PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addStartEvent("start"); builder.addCallActivity("callActivity", targetProcessExpr, targetVersionExpr); builder.addEndEvent("end").addTerminateEventTrigger(); builder.addTransition("start", "callActivity").addTransition("callActivity", "end"); return builder; } public static ProcessDefinitionBuilder buildProcessDefinitionWithHumanTaskAndErrorEndEvent(final String processName, final String taskName) { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName, PROCESS_VERSION); builder.addActor(ACTOR_NAME); builder.addStartEvent("start"); builder.addUserTask(taskName, ACTOR_NAME); builder.addEndEvent("end").addErrorEventTrigger("errorCode"); builder.addTransition("start", taskName).addTransition(taskName, "end"); return builder; } public static ProcessDefinitionBuilder buildProcessDefinitionWithAutomaticTaskAndErrorEndEvent( final String processName) { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName, PROCESS_VERSION); builder.addStartEvent("start"); builder.addAutomaticTask("step"); builder.addEndEvent("end").addErrorEventTrigger("errorCode"); builder.addTransition("start", "step").addTransition("step", "end"); return builder; } public static ProcessDefinitionBuilder buildProcessDefinitionWithAutomaticTaskAndFailedGroovyScript( final String processName) throws InvalidExpressionException { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName, PROCESS_VERSION); builder.addStartEvent("start"); builder.addAutomaticTask("activityThatFail").addData("data", String.class.getName(), new ExpressionBuilder().createGroovyScriptExpression("script", "throw new java.lang.RuntimeException()", String.class.getName())); builder.addEndEvent("end"); builder.addTransition("start", "activityThatFail").addTransition("activityThatFail", "end"); return builder; } public static ProcessDefinitionBuilder buildProcessDefinitionWithFailedConnector(final String processName) throws InvalidExpressionException { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName, PROCESS_VERSION); final ConnectorDefinitionBuilder connectorDefinitionBuilder = builder .addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER) .throwErrorEventWhenFailed("errorCode"); connectorDefinitionBuilder.addInput("kind", new ExpressionBuilder().createConstantStringExpression("plop")); builder.addStartEvent("start").addAutomaticTask("AutomaticStep").addEndEvent("end"); builder.addTransition("start", "AutomaticStep").addTransition("AutomaticStep", "end"); return builder; } public static ProcessDefinitionBuilder buildProcessDefinitionWithAutomaticTaskAndFailedConnector( final String processName) throws InvalidExpressionException { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName, PROCESS_VERSION); builder.addStartEvent("start"); final ConnectorDefinitionBuilder connectorDefinitionBuilder = builder.addAutomaticTask("AutomaticStep") .addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER) .throwErrorEventWhenFailed("errorCode"); connectorDefinitionBuilder.addInput("kind", new ExpressionBuilder().createConstantStringExpression("plop")); builder.addEndEvent("end"); builder.addTransition("start", "AutomaticStep").addTransition("AutomaticStep", "end"); return builder; } public static ProcessDefinitionBuilder buildProcessDefinitionWithUserTaskAndFailedConnector( final String processName) throws InvalidExpressionException { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName, PROCESS_VERSION); builder.addStartEvent("start"); builder.addActor(ACTOR_NAME).addUserTask("StepBeforeFailedConnector", ACTOR_NAME); final ConnectorDefinitionBuilder connectorDefinitionBuilder = builder.addAutomaticTask("AutomaticStep") .addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_ENTER) .throwErrorEventWhenFailed("errorCode"); connectorDefinitionBuilder.addInput("kind", new ExpressionBuilder().createConstantStringExpression("plop")); builder.addEndEvent("end"); builder.addTransition("start", "StepBeforeFailedConnector") .addTransition("StepBeforeFailedConnector", "AutomaticStep") .addTransition("AutomaticStep", "end"); return builder; } public static ProcessDefinitionBuilder buildProcessDefinitionWithMultiInstanceUserTaskAndFailedConnector( final String processName, final String userTaskName) throws InvalidExpressionException { final String errorCode = "mistake"; final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance(processName, PROCESS_VERSION); builder.addActor(ACTOR_NAME); final UserTaskDefinitionBuilder userTaskBuilder = builder.addUserTask(userTaskName, ACTOR_NAME); userTaskBuilder.addMultiInstance(false, new ExpressionBuilder().createConstantIntegerExpression(3)); final ConnectorDefinitionBuilder connectorDefinitionBuilder = userTaskBuilder .addConnector("testConnectorThatThrowException", "testConnectorThatThrowException", "1.0", ConnectorEvent.ON_FINISH) .throwErrorEventWhenFailed(errorCode); connectorDefinitionBuilder.addInput("kind", new ExpressionBuilder().createConstantStringExpression("plop")); final BoundaryEventDefinitionBuilder boundaryEvent = userTaskBuilder.addBoundaryEvent("error", true); boundaryEvent.addErrorEventTrigger(errorCode); builder.addUserTask("normalFlow", ACTOR_NAME); builder.addUserTask("errorFlow", ACTOR_NAME); builder.addTransition(userTaskName, "normalFlow").addTransition("error", "errorFlow"); return builder; } public static void buildErrorEventSubProcessWithUserTask(final String taskName, final ProcessDefinitionBuilder builder) { final SubProcessDefinitionBuilder subProcessBuilder = builder.addSubProcess(EVENT_SUB_PROCESS_NAME, true) .getSubProcessBuilder(); subProcessBuilder.addStartEvent("SubStart").addErrorEventTrigger(); subProcessBuilder.addUserTask(taskName, ACTOR_NAME); subProcessBuilder.addEndEvent("SubEnd"); subProcessBuilder.addTransition("SubStart", taskName).addTransition(taskName, "SubEnd"); } public static Document buildDocument(final String documentName) { final String now = String.valueOf(System.currentTimeMillis()); final String fileName = now + ".txt"; final DocumentBuilder builder = new DocumentBuilder().createNewInstance(documentName, false); builder.setFileName(fileName); builder.setContentMimeType("plain/text"); builder.setDescription("a generated document for tests"); return builder.done(); } public static SearchOptionsBuilder buildSearchOptions(final long processDefId, final int pageIndex, final int numberOfResults, final String orderByField, final Order order) { final SearchOptionsBuilder builder = new SearchOptionsBuilder(pageIndex, numberOfResults); builder.filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefId); builder.sort(orderByField, order); return builder; } public static SearchOptionsBuilder buildSearchOptions(final int pageIndex, final int numberOfResults, final String orderByField, final Order order) { final SearchOptionsBuilder builder = new SearchOptionsBuilder(pageIndex, numberOfResults); builder.sort(orderByField, order); return builder; } public static Operation buildStringOperation(final String dataInstanceName, final String newConstantValue, final boolean isTransient) throws InvalidExpressionException { final LeftOperand leftOperand = new LeftOperandBuilder().createNewInstance().setName(dataInstanceName) .setType(isTransient ? LeftOperand.TYPE_TRANSIENT_DATA : LeftOperand.TYPE_DATA).done(); final Expression expression = new ExpressionBuilder().createConstantStringExpression(newConstantValue); final Operation operation; operation = new OperationBuilder().createNewInstance().setOperator("=").setLeftOperand(leftOperand) .setType(OperatorType.ASSIGNMENT) .setRightOperand(expression).done(); return operation; } public static Operation buildIntegerOperation(final String dataInstanceName, final int newConstantValue) throws InvalidExpressionException { final LeftOperand leftOperand = new LeftOperandBuilder().createNewInstance().setName(dataInstanceName).done(); final Expression expression = new ExpressionBuilder().createConstantIntegerExpression(newConstantValue); final Operation operation; operation = new OperationBuilder().createNewInstance().setOperator("=").setLeftOperand(leftOperand) .setType(OperatorType.ASSIGNMENT) .setRightOperand(expression).done(); return operation; } public static Operation buildOperation(final String dataName, final boolean isTransient, final OperatorType operatorType, final String operator, final Expression rightOperand) { final OperationBuilder operationBuilder = new OperationBuilder().createNewInstance(); operationBuilder.setOperator(operator); operationBuilder.setRightOperand(rightOperand); operationBuilder.setType(operatorType); operationBuilder.setLeftOperand(new LeftOperandBuilder().createNewInstance(dataName) .setType(isTransient ? LeftOperand.TYPE_TRANSIENT_DATA : LeftOperand.TYPE_DATA).done()); return operationBuilder.done(); } public static Operation buildAssignOperation(final String dataInstanceName, final String expressionContent, final ExpressionType expressionType, final String returnType) throws InvalidExpressionException { final LeftOperand leftOperand = new LeftOperandBuilder().createNewInstance().setName(dataInstanceName).done(); final Expression expression = new ExpressionBuilder().createNewInstance(dataInstanceName) .setContent(expressionContent) .setExpressionType(expressionType).setReturnType(returnType).done(); return new OperationBuilder().createNewInstance().setOperator("=").setLeftOperand(leftOperand) .setType(OperatorType.ASSIGNMENT) .setRightOperand(expression).done(); } public static List buildActorAndDescription(final ProcessDefinitionBuilder processBuilder, final int nbActor) { final List actorList = new ArrayList<>(); for (int i = 1; i <= nbActor; i++) { final String actorName = ACTOR_NAME + i; processBuilder.addActor(actorName).addDescription(DESCRIPTION + i); actorList.add(actorName); } return actorList; } public static DesignProcessDefinition buildProcessDefinitionWithActorAndThreeHumanStepsAndThreeTransition() throws InvalidProcessDefinitionException { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(PROCESS_NAME, PROCESS_VERSION); processBuilder.addActor(BuildTestUtil.ACTOR_NAME).addDescription(DESCRIPTION); // 1 instance of process def: return processBuilder.addAutomaticTask("step1").addUserTask("step2", BuildTestUtil.ACTOR_NAME) .addUserTask("step3", BuildTestUtil.ACTOR_NAME) .addUserTask("step4", BuildTestUtil.ACTOR_NAME).addTransition("step1", "step2") .addTransition("step1", "step3").addTransition("step1", "step4") .getProcess(); } public static List buildNbProcessDefinitionWithHumanAndAutomatic(final int nbProcess, final List stepNames, final List isHuman) throws InvalidProcessDefinitionException { final List processDefinitions = new ArrayList<>(); for (int i = 0; i < nbProcess; i++) { String processName = PROCESS_NAME; if (i >= 0 && i < 10) { processName += "0"; } final DesignProcessDefinition designProcessDefinition = buildProcessDefinitionWithHumanAndAutomaticSteps( processName + i, PROCESS_VERSION + i, stepNames, isHuman); processDefinitions.add(designProcessDefinition); } return processDefinitions; } public static DesignProcessDefinition buildProcessDefinitionWithHumanAndAutomaticSteps(final String processName, final String processVersion, final List stepNames, final List isHuman, final String actorName, final boolean addActorInitiator) throws InvalidProcessDefinitionException { return buildProcessDefinitionWithHumanAndAutomaticSteps(processName, processVersion, stepNames, isHuman, actorName, addActorInitiator, false); } public static DesignProcessDefinition buildProcessDefinitionWithHumanAndAutomaticSteps(final String processName, final String processVersion, final List stepNames, final List isHuman) throws InvalidProcessDefinitionException { return buildProcessDefinitionWithHumanAndAutomaticSteps(processName, processVersion, stepNames, isHuman, ACTOR_NAME, false); } public static DesignProcessDefinition buildProcessDefinitionWithHumanAndAutomaticSteps(final List stepNames, final List isHuman) throws InvalidProcessDefinitionException { return buildProcessDefinitionWithHumanAndAutomaticSteps(PROCESS_NAME, PROCESS_VERSION, stepNames, isHuman, ACTOR_NAME, false); } public static DesignProcessDefinition buildProcessDefinitionWithHumanAndAutomaticSteps(final String processName, final String processVersion, final List stepNames, final List isHuman, final String actorName, final boolean addActorInitiator, final boolean parallelActivities) throws InvalidProcessDefinitionException { final ProcessDefinitionBuilder processBuilder = new ProcessDefinitionBuilder().createNewInstance(processName, processVersion); if (!isHuman.isEmpty() && isHuman.contains(true)) { processBuilder.addActor(actorName); if (addActorInitiator) { processBuilder.setActorInitiator(actorName); } } for (int i = 0; i < stepNames.size(); i++) { final String stepName = stepNames.get(i); if (isHuman.get(i)) { processBuilder.addUserTask(stepName, actorName); } else { processBuilder.addAutomaticTask(stepName); } } if (!parallelActivities) { for (int i = 0; i < stepNames.size() - 1; i++) { processBuilder.addTransition(stepNames.get(i), stepNames.get(i + 1)); } } return processBuilder.done(); } public static byte[] generateContent(final Document doc) { return doc.getName().getBytes(); } public static BarResource generateJarAndBuildBarResource(final Class clazz, final String name) throws IOException { final byte[] data = IOUtil.generateJar(clazz); return new BarResource(name, data); } public static BarResource getContentAndBuildBarResource(final String name, final Class clazz) throws IOException { final InputStream stream = clazz.getResourceAsStream(name); assertNotNull(stream); final byte[] byteArray = IOUtils.toByteArray(stream); stream.close(); return new BarResource(name, byteArray); } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/CommonTestUtil.java ================================================ /** * 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.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** * author Emmanuel Duchastenier */ public class CommonTestUtil { /** * @param pageName * @param displayName * @param description * @param extraProperties some property to add in page.properties file. eg: value=data * @return * @throws IOException */ public static byte[] createTestPageContent(final String pageName, final String displayName, final String description, final String... extraProperties) throws IOException { try (final ByteArrayOutputStream baos = new ByteArrayOutputStream(); ZipOutputStream zos = new ZipOutputStream(baos)) { zos.putNextEntry(new ZipEntry("Index.groovy")); zos.write("return \"\";".getBytes()); zos.putNextEntry(new ZipEntry("page.properties")); final StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("name="); stringBuilder.append(pageName); stringBuilder.append("\n"); stringBuilder.append("displayName="); stringBuilder.append(displayName); stringBuilder.append("\n"); stringBuilder.append("description="); stringBuilder.append(description); stringBuilder.append("\n"); for (final String extraProperty : extraProperties) { stringBuilder.append(extraProperty); stringBuilder.append("\n"); } zos.write(stringBuilder.toString().getBytes(StandardCharsets.UTF_8)); zos.closeEntry(); return baos.toByteArray(); } } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/PlatformTestUtil.java ================================================ /** * 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 org.bonitasoft.engine.api.APIClient; import org.bonitasoft.engine.api.LoginAPI; import org.bonitasoft.engine.api.PlatformAPI; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.api.PlatformLoginAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; 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"; public static final String DEFAULT_TECHNICAL_LOGGER_PASSWORD = "install"; public PlatformSession loginOnPlatform() throws BonitaException { final PlatformLoginAPI platformLoginAPI = getPlatformLoginAPI(); return platformLoginAPI.login("platformAdmin", "platform"); } public void logoutOnPlatform(final PlatformSession session) throws BonitaException { final PlatformLoginAPI platformLoginAPI = getPlatformLoginAPI(); platformLoginAPI.logout(session); } public PlatformLoginAPI getPlatformLoginAPI() throws BonitaException { return PlatformAPIAccessor.getPlatformLoginAPI(); } public LoginAPI getLoginAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getLoginAPI(); } public PlatformAPI getPlatformAPI(final PlatformSession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return PlatformAPIAccessor.getPlatformAPI(session); } protected void stopAndStartPlatform() throws BonitaException { final PlatformSession loginPlatform = loginOnPlatform(); final PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(loginPlatform); platformAPI.stopNode(); platformAPI.startNode(); logoutOnPlatform(loginPlatform); } public void deployCommandsOnDefaultTenant() throws BonitaException { APIClient apiClient = new APIClient(); apiClient.login(DEFAULT_TECHNICAL_LOGGER_USERNAME, DEFAULT_TECHNICAL_LOGGER_PASSWORD); ClientEventUtil.deployCommand(apiClient.getSession()); 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 }; } } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/Repeat.java ================================================ /** * 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.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author Laurent Leseigneur *

use this rule to play x times the same junit test and add rule {@link RepeatRule}.

* Example:
* *
 * {@code
 * @Rule public RepeatRule repeatRule = new RepeatRule();
 * @Repeat(times = 100)
 *               public void testName() throws Exception {
 *               ...
 *               }
 *               }
 *         
*/ @Retention(RetentionPolicy.RUNTIME) @Target({ java.lang.annotation.ElementType.METHOD }) public @interface Repeat { public abstract int times(); } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/RepeatRule.java ================================================ /** * 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 org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; /** * @author Laurent Leseigneur *

use this rule to play x times the same junit test and add rule {@link Repeat}.

* Example:
* *
 * {@code
 * @Rule public RepeatRule repeatRule = new RepeatRule();
 * @Repeat(times = 100)
 * public void testName() throws Exception {
 *   ...
 * }
 * }
 *         
*/ public class RepeatRule implements TestRule { private static class RepeatStatement extends Statement { private final int times; private final Statement statement; private RepeatStatement(final int times, final Statement statement) { this.times = times; this.statement = statement; } @Override public void evaluate() throws Throwable { for (int i = 0; i < times; i++) { statement.evaluate(); } } } @Override public Statement apply(final Statement statement, final Description description) { Statement result = statement; final Repeat repeat = description.getAnnotation(Repeat.class); if (repeat != null) { final int times = repeat.times(); result = new RepeatStatement(times, statement); } return result; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/StartProcessUntilStep.java ================================================ /** * 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 org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.process.ProcessInstance; public class StartProcessUntilStep { private final ProcessInstance processInstance; private final ActivityInstance activityInstance; public StartProcessUntilStep(final ProcessInstance processInstance, final ActivityInstance activityInstance) { super(); this.processInstance = processInstance; this.activityInstance = activityInstance; } public ProcessInstance getProcessInstance() { return processInstance; } public ActivityInstance getActivityInstance() { return activityInstance; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/TestStates.java ================================================ /** * 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; /** * @author Baptiste Mesta * @author Celine Souchet */ public enum TestStates { INITIALIZING("initializing"), NORMAL_FINAL("completed"), READY("ready"), SKIPPED("skipped"), WAITING("waiting"), FAILED("failed"), ABORTED("aborted"), EXECUTING("executing"), CANCELLED("cancelled"), INTERRUPTED("interrupted"); private final String stateName; private TestStates(final String stateName) { this.stateName = stateName; } public String getStateName() { return stateName; } @Override public String toString() { return stateName; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/WaitUntil.java ================================================ /** * 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.util.Date; /** * @author Baptiste Mesta */ @Deprecated public abstract class WaitUntil { private final int timeout; private final int repeatEach; private final boolean throwExceptions; /** * @param repeatEach * time to wait for before retrying, if condition not fullfilled, in milliseconds * @param timeout * max time to wait for, in milliseconds */ public WaitUntil(final int repeatEach, final int timeout) { this(repeatEach, timeout, true); } /** * @param repeatEach * time to wait for before retrying, if condition not fullfilled, in milliseconds * @param timeout * max time to wait for, in milliseconds * @param throwExceptions * can the check condition throw exceptions? */ public WaitUntil(final int repeatEach, final int timeout, final boolean throwExceptions) { this.throwExceptions = throwExceptions; if (repeatEach > timeout) { throw new AssertionError("timeout " + timeout + " cannot be smaller than repeatEach " + repeatEach); } this.repeatEach = repeatEach; this.timeout = timeout; } public boolean waitUntil() throws Exception { final long limit = new Date().getTime() + timeout; while (new Date().getTime() < limit) { Thread.sleep(repeatEach); if (checkCondition()) { return true; } } return checkCondition(); } protected boolean checkCondition() throws Exception { if (throwExceptions) { return check(); } try { return check(); } catch (final Exception e) { // do nothing } return false; } /** * Condition to check for. * * @return true if condition is true, false otherwise. * @throws Exception */ protected abstract boolean check() throws Exception; } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbAssignedTaskOf.java ================================================ /** * 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.check; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.test.WaitUntil; /** * @author Elias Ricken de Medeiros */ public final class CheckNbAssignedTaskOf extends WaitUntil { private final int nbActivities; private final User user; private final ProcessAPI processAPI; private List assignedHumanTaskInstances; public CheckNbAssignedTaskOf(final ProcessAPI processAPI, final int repeatEach, final int timeout, final boolean throwExceptions, final int nbActivities, final User user) { super(repeatEach, timeout, throwExceptions); this.nbActivities = nbActivities; this.user = user; this.processAPI = processAPI; } @Override protected boolean check() { assignedHumanTaskInstances = processAPI.getAssignedHumanTaskInstances(user.getId(), 0, Math.max(nbActivities, 20), ActivityInstanceCriterion.NAME_ASC); return assignedHumanTaskInstances.size() == nbActivities; } /** * @return the pendingHumanTaskInstances */ public List getAssingnedHumanTaskInstances() { return assignedHumanTaskInstances; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbOfActivities.java ================================================ /** * 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.check; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.test.TestStates; import org.bonitasoft.engine.test.WaitUntil; /** * @author Baptiste Mesta */ @Deprecated public final class CheckNbOfActivities extends WaitUntil { private final ProcessAPI processAPI; private final ProcessInstance processInstance; private final int nbActivities; private Set result; private String activityState = null; @Deprecated public CheckNbOfActivities(final ProcessAPI processAPI, final int repeatEach, final int timeout, final boolean throwExceptions, final ProcessInstance processInstance, final int nbActivities) { super(repeatEach, timeout, throwExceptions); this.processInstance = processInstance; this.nbActivities = nbActivities; this.processAPI = processAPI; } @Deprecated public CheckNbOfActivities(final ProcessAPI processAPI, final int repeatEach, final int timeout, final boolean throwExceptions, final ProcessInstance processInstance, final int nbActivities, final TestStates state) { this(processAPI, repeatEach, timeout, throwExceptions, processInstance, nbActivities); activityState = state.getStateName(); } @Override protected boolean check() { final List activities = processAPI.getActivities(processInstance.getId(), 0, 200); result = new HashSet(activities.size()); // The number of activities is the one expected... if (activityState != null) { for (final ActivityInstance activityInstance : activities) { // ... and all states are equal to the expected one: if (activityInstance.getState().equals(activityState)) { result.add(activityInstance); } } } else { result.addAll(activities); } final boolean check = result.size() == nbActivities; return check;// get activities with a state } public Set getResult() { return result; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbOfArchivedActivities.java ================================================ /** * 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.check; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.test.TestStates; import org.bonitasoft.engine.test.WaitUntil; @Deprecated public final class CheckNbOfArchivedActivities extends WaitUntil { private final ProcessAPI processAPI; private final ProcessInstance processInstance; private final int nbActivities; private Set result; private String activityState = null; @Deprecated public CheckNbOfArchivedActivities(final ProcessAPI processAPI, final int repeatEach, final int timeout, final boolean throwExceptions, final ProcessInstance processInstance, final int nbActivities) { super(repeatEach, timeout, throwExceptions); this.processInstance = processInstance; this.nbActivities = nbActivities; this.processAPI = processAPI; } @Deprecated public CheckNbOfArchivedActivities(final ProcessAPI processAPI, final int repeatEach, final int timeout, final boolean throwExceptions, final ProcessInstance processInstance, final int nbActivities, final TestStates state) { this(processAPI, repeatEach, timeout, throwExceptions, processInstance, nbActivities); activityState = state.getStateName(); } @Override protected boolean check() { final List activities = processAPI.getArchivedActivityInstances( processInstance.getId(), 0, 200, ActivityInstanceCriterion.NAME_ASC); result = new HashSet(activities.size()); // The number of activities is the one expected... if (activityState != null) { for (final ArchivedActivityInstance activityInstance : activities) { // ... and all states are equal to the expected one: if (activityInstance.getState().equals(activityState)) { result.add(activityInstance); } } } else { result.addAll(activities); } final boolean check = result.size() == nbActivities; return check;// get activities with a state } public Set getResult() { return result; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbOfArchivedActivityInstances.java ================================================ /** * 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.check; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.test.WaitUntil; /** * @author Baptiste Mesta */ @Deprecated public final class CheckNbOfArchivedActivityInstances extends WaitUntil { private final ProcessAPI processAPI; private final ProcessInstance processInstance1; private final int expected; @Deprecated public CheckNbOfArchivedActivityInstances(final int repeatEach, final int timeout, final ProcessInstance processInstance, final int expected, final ProcessAPI processAPI) { super(repeatEach, timeout, false); this.processInstance1 = processInstance; this.expected = expected; this.processAPI = processAPI; } @Override protected boolean check() { return processAPI .getArchivedActivityInstances(processInstance1.getId(), 0, 100, ActivityInstanceCriterion.DEFAULT) .size() == expected; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbOfHumanTasks.java ================================================ /** * 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.check; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.WaitUntil; /** * @author Emmanuel Duchastenier */ @Deprecated public final class CheckNbOfHumanTasks extends WaitUntil { private final ProcessAPI processAPI; private final long nbOfHumanTasks; private final SearchOptions searchOptions; private SearchResult humanTaskInstances; @Deprecated public CheckNbOfHumanTasks(final int repeatEach, final int timeout, final boolean throwExceptions, final long nbOfHumanTasks, final SearchOptions searchOptions, final ProcessAPI processAPI) { super(repeatEach, timeout, throwExceptions); this.nbOfHumanTasks = nbOfHumanTasks; this.searchOptions = searchOptions; this.processAPI = processAPI; } @Override protected boolean check() throws Exception { humanTaskInstances = processAPI.searchHumanTaskInstances(searchOptions); return nbOfHumanTasks == humanTaskInstances.getCount(); } public SearchResult getHumanTaskInstances() { return humanTaskInstances; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbOfOpenActivities.java ================================================ /** * 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.check; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.test.WaitUntil; /** * @author Emmanuel Duchastenier */ @Deprecated public final class CheckNbOfOpenActivities extends WaitUntil { private final ProcessAPI processAPI; private final ProcessInstance processInstance; private final int nbActivities; @Deprecated public CheckNbOfOpenActivities(final int repeatEach, final int timeout, final boolean throwExceptions, final ProcessInstance processInstance, final int nbActivities, final ProcessAPI processAPI) { super(repeatEach, timeout, throwExceptions); this.processInstance = processInstance; this.nbActivities = nbActivities; this.processAPI = processAPI; } @Override protected boolean check() throws Exception { final int activityNumber = processAPI.getNumberOfOpenedActivityInstances(processInstance.getId()); // The number of activities must be the one expected: return activityNumber == nbActivities; } public int getNumberOfOpenActivities() { return nbActivities; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbOfProcessInstances.java ================================================ /** * 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.check; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion; import org.bonitasoft.engine.test.WaitUntil; @Deprecated public final class CheckNbOfProcessInstances extends WaitUntil { private final ProcessAPI processAPI; private final int nbOfProcInst; private List result; private final ProcessInstanceCriterion orderBy; @Deprecated public CheckNbOfProcessInstances(final int repeatEach, final int timeout, final int nbOfProcInst, final ProcessInstanceCriterion orderBy, final ProcessAPI processAPI) { super(repeatEach, timeout); this.nbOfProcInst = nbOfProcInst; this.orderBy = orderBy; this.processAPI = processAPI; } @Deprecated public CheckNbOfProcessInstances(final int repeatEach, final int timeout, final int nbOfProcInst, final ProcessAPI processAPI) { this(repeatEach, timeout, nbOfProcInst, ProcessInstanceCriterion.NAME_ASC, processAPI); } @Override protected boolean check() { result = processAPI.getProcessInstances(0, nbOfProcInst + 1, orderBy); return nbOfProcInst == result.size(); } public List getResult() { return result; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbPendingTaskOf.java ================================================ /** * 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.check; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.test.WaitUntil; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @deprecated use {@link org.bonitasoft.engine.test.APITestUtil} .waitFor... */ @Deprecated public final class CheckNbPendingTaskOf extends WaitUntil { private final int nbActivities; private final User user; private final ProcessAPI processAPI; private List pendingHumanTaskInstances; private final ActivityInstanceCriterion orderBy; public CheckNbPendingTaskOf(final ProcessAPI processAPI, final int repeatEach, final int timeout, final boolean throwExceptions, final int nbActivities, final User user) { this(processAPI, repeatEach, timeout, throwExceptions, nbActivities, user, ActivityInstanceCriterion.NAME_ASC); } public CheckNbPendingTaskOf(final ProcessAPI processAPI, final int repeatEach, final int timeout, final boolean throwExceptions, final int nbActivities, final User user, final ActivityInstanceCriterion orderBy) { super(repeatEach, timeout, throwExceptions); this.nbActivities = nbActivities; this.user = user; this.processAPI = processAPI; this.orderBy = orderBy; } @Override protected boolean check() { pendingHumanTaskInstances = processAPI.getPendingHumanTaskInstances(user.getId(), 0, Math.max(nbActivities, 20), orderBy); return pendingHumanTaskInstances.size() == nbActivities; } public List getPendingHumanTaskInstances() { return pendingHumanTaskInstances; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckNbPendingTasksForUserUsingSearch.java ================================================ /** * 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.check; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.WaitUntil; /** * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public final class CheckNbPendingTasksForUserUsingSearch extends WaitUntil { private final int nbTasks; private final long userId; private final ProcessAPI processAPI; private List pendingHumanTasks; private final SearchOptions searchOptions; public CheckNbPendingTasksForUserUsingSearch(final ProcessAPI processAPI, final int repeatEach, final int timeout, final boolean throwExceptions, final int nbTasks, final long userId, final SearchOptions searchOptions) { super(repeatEach, timeout, throwExceptions); this.nbTasks = nbTasks; this.userId = userId; this.processAPI = processAPI; this.searchOptions = searchOptions; } @Override protected boolean check() throws Exception { final SearchResult searchResult = processAPI.searchPendingTasksForUser(userId, searchOptions); pendingHumanTasks = searchResult.getResult(); return searchResult.getCount() == nbTasks; } public List getPendingHumanTasks() { return pendingHumanTasks; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/check/CheckProcessInstanceIsArchived.java ================================================ /** * 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.check; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.test.WaitUntil; /** * @author Baptiste Mesta */ @Deprecated public final class CheckProcessInstanceIsArchived extends WaitUntil { private final ProcessAPI processAPI; private final long processInstance; @Deprecated public CheckProcessInstanceIsArchived(final int repeatEach, final int timeout, final long processInstance, final ProcessAPI processAPI) { super(repeatEach, timeout, false); this.processInstance = processInstance; this.processAPI = processAPI; } @Override protected boolean check() throws Exception { ArchivedProcessInstance archivedProcessInstance; archivedProcessInstance = processAPI.getFinalArchivedProcessInstance(processInstance); return archivedProcessInstance != null && archivedProcessInstance.getEndDate() != null; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForActivity.java ================================================ /** * 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.wait; import java.util.Iterator; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.test.WaitUntil; @Deprecated public class WaitForActivity extends WaitUntil { private final String activityName; private final long processInstanceId; private String state = null; private ActivityInstance result; private final ProcessAPI processAPI; @Deprecated public WaitForActivity(final int repeatEach, final int timeout, final String activityName, final long processInstanceId, final ProcessAPI processAPI) { super(repeatEach, timeout); this.activityName = activityName; this.processInstanceId = processInstanceId; this.processAPI = processAPI; } @Deprecated public WaitForActivity(final int repeatEach, final int timeout, final String activityName, final long processInstanceId, final String state, final ProcessAPI processAPI) { super(repeatEach, timeout); this.activityName = activityName; this.processInstanceId = processInstanceId; this.state = state; this.processAPI = processAPI; } @Override protected boolean check() { final List activityInstances = processAPI.getActivities(processInstanceId, 0, 10); final Iterator iterator = activityInstances.iterator(); boolean found = false; while (iterator.hasNext() && !found) { final ActivityInstance activityInstance = iterator.next(); if (activityInstance.getName().equals(activityName)) { if (state == null || state.equals(activityInstance.getState())) { result = activityInstance; found = true; } } } return found; } public ActivityInstance getResult() { return result; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForArchivedActivity.java ================================================ /** * 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.wait; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.test.TestStates; import org.bonitasoft.engine.test.WaitUntil; @Deprecated public class WaitForArchivedActivity extends WaitUntil { private final long activityId; private ArchivedActivityInstance archivedActivityInstance; private final String stateName; private final ProcessAPI processAPI; public WaitForArchivedActivity(final int repeatEach, final int timeout, final long activityId, final TestStates state, final ProcessAPI processAPI) { super(repeatEach, timeout, false); this.activityId = activityId; this.stateName = state.getStateName(); this.processAPI = processAPI; } @Override protected boolean check() throws BonitaException { archivedActivityInstance = processAPI.getArchivedActivityInstance(activityId); return stateName.equals(archivedActivityInstance.getState()); } public ArchivedActivityInstance getArchivedActivityInstance() { return archivedActivityInstance; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForAssignedStep.java ================================================ /** * 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.wait; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.test.WaitUntil; public class WaitForAssignedStep extends WaitUntil { private final ProcessAPI processAPI; private final String userTaskName; private final long processInstanceId; private final long userId; private HumanTaskInstance result; public WaitForAssignedStep(final ProcessAPI processAPI, final String userTaskName, final long processInstanceId, final long userId) { super(100, 1500); this.processAPI = processAPI; this.userTaskName = userTaskName; this.processInstanceId = processInstanceId; this.userId = userId; } @Override protected boolean check() { final List taskInstances = processAPI.getAssignedHumanTaskInstances(userId, 0, 20, ActivityInstanceCriterion.DEFAULT); for (final HumanTaskInstance taskInstance : taskInstances) { if (taskInstance.getName().equals(userTaskName) && taskInstance.getParentProcessInstanceId() == processInstanceId) { result = taskInstance; return true; } } return false; } public HumanTaskInstance getResult() { return result; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForCompletedArchivedStep.java ================================================ /** * 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.wait; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.ActivityStates; import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.WaitUntil; /** * @author Baptiste Mesta */ @Deprecated public final class WaitForCompletedArchivedStep extends WaitUntil { private final String value; private final long id; private final ProcessAPI processApi; private ArchivedHumanTaskInstance archivedHumanTaskInstance; public WaitForCompletedArchivedStep(final int repeatEach, final int timeout, final String stepName, final long processDefinitionId, final ProcessAPI processApi) { super(repeatEach, timeout); this.processApi = processApi; value = stepName; id = processDefinitionId; } @Override protected boolean check() throws Exception { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, id); builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.NAME, value); builder.filter(ArchivedHumanTaskInstanceSearchDescriptor.STATE_NAME, ActivityStates.COMPLETED_STATE); final SearchResult searchArchivedTasks = processApi .searchArchivedHumanTasks(builder.done()); final boolean ok = searchArchivedTasks.getCount() >= 1; if (ok) { archivedHumanTaskInstance = searchArchivedTasks.getResult().get(0); } return ok; } public ArchivedHumanTaskInstance getArchivedTask() { return archivedHumanTaskInstance; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForDataValue.java ================================================ /** * 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.wait; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.test.WaitUntil; /** * @author Sébastien Chevassu * @author Celine Souchet */ public class WaitForDataValue extends WaitUntil { private final ProcessAPI processAPI; private final long processInstanceId; private final String dataName; private final String valueExpected; public WaitForDataValue(final int repeatEach, final int timeout, final long processInstanceId, final String dataName, final String valueExpected, final ProcessAPI processAPI) { super(repeatEach, timeout, false); this.processAPI = processAPI; this.processInstanceId = processInstanceId; this.dataName = dataName; this.valueExpected = valueExpected; } @Override protected boolean check() throws Exception { final String value = (String) processAPI.getProcessDataInstance(dataName, processInstanceId).getValue(); return value.equals(valueExpected); } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForEvent.java ================================================ /** * 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.wait; import java.util.Iterator; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.EventCriterion; import org.bonitasoft.engine.bpm.flownode.EventInstance; import org.bonitasoft.engine.test.TestStates; import org.bonitasoft.engine.test.WaitUntil; @Deprecated public class WaitForEvent extends WaitUntil { private final String eventName; private final long processInstanceId; private String state = null; private EventInstance result; private final ProcessAPI processAPI; @Deprecated public WaitForEvent(final int repeatEach, final int timeout, final String eventName, final long processInstanceId, final ProcessAPI processAPI) { super(repeatEach, timeout); this.eventName = eventName; this.processInstanceId = processInstanceId; this.processAPI = processAPI; } @Deprecated public WaitForEvent(final int repeatEach, final int timeout, final String eventName, final long processInstanceId, final TestStates state, final ProcessAPI processAPI) { this(repeatEach, timeout, eventName, processInstanceId, processAPI); this.state = state.getStateName(); } @Override protected boolean check() { final List eventInstances = processAPI.getEventInstances(processInstanceId, 0, 10, EventCriterion.NAME_ASC); boolean found = false; final Iterator iterator = eventInstances.iterator(); while (iterator.hasNext() && !found) { final EventInstance eventInstance = iterator.next(); if (eventInstance.getName().equals(eventName)) { if (state == null) { found = true; result = eventInstance; } else { found = state.equals(eventInstance.getState()); result = eventInstance; } } } return found; } public EventInstance getResult() { return result; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForFinalArchivedActivity.java ================================================ /** * 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.wait; import java.util.Iterator; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.test.TestStates; import org.bonitasoft.engine.test.WaitUntil; @Deprecated public class WaitForFinalArchivedActivity extends WaitUntil { private final String activityName; private final long processInstanceId; private ArchivedActivityInstance result; private final ProcessAPI processAPI; @Deprecated public WaitForFinalArchivedActivity(final int repeatEach, final int timeout, final String activityName, final long processInstanceId, final ProcessAPI processAPI) { super(repeatEach, timeout); this.activityName = activityName; this.processInstanceId = processInstanceId; this.processAPI = processAPI; } @Override protected boolean check() { final List activityInstances = processAPI.getArchivedActivityInstances( processInstanceId, 0, 100, ActivityInstanceCriterion.NAME_ASC); final Iterator iterator = activityInstances.iterator(); boolean found = false; while (iterator.hasNext() && !found) { final ArchivedActivityInstance activityInstance = iterator.next(); if (activityInstance.getName().equals(activityName) && activityInstance.getState().equals(TestStates.NORMAL_FINAL.getStateName())) { result = activityInstance; found = true; } } return found; } public ArchivedActivityInstance getResult() { return result; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForFlowNode.java ================================================ /** * 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.wait; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.StateCategory; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.test.WaitUntil; @Deprecated public class WaitForFlowNode extends WaitUntil { private final String name; private final long processInstanceId; private String state = null; private StateCategory stateCategory = null; private FlowNodeInstance result; private final boolean useRootProcessInstance; private final ProcessAPI processAPI; @Deprecated public WaitForFlowNode(final int repeatEach, final int timeout, final String name, final long processInstanceId, final boolean useRootProcessInstance, final ProcessAPI processAPI) { super(repeatEach, timeout); this.name = name; this.processInstanceId = processInstanceId; this.useRootProcessInstance = useRootProcessInstance; this.processAPI = processAPI; } @Deprecated public WaitForFlowNode(final int repeatEach, final int timeout, final String name, final long processInstanceId, final String state, final boolean rootProcessInstance, final ProcessAPI processAPI) { this(repeatEach, timeout, name, processInstanceId, rootProcessInstance, processAPI); this.state = state; } @Deprecated public WaitForFlowNode(final int repeatEach, final int timeout, final String name, final long processInstanceId, final StateCategory stateCategory, final boolean rootProcessInstance, final ProcessAPI processAPI) { this(repeatEach, timeout, name, processInstanceId, rootProcessInstance, processAPI); this.stateCategory = stateCategory; } @Override protected boolean check() throws Exception { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); if (useRootProcessInstance) { searchOptionsBuilder.filter(FlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, processInstanceId); } else { searchOptionsBuilder.filter(FlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, processInstanceId); } searchOptionsBuilder.filter(FlowNodeInstanceSearchDescriptor.NAME, name); final SearchResult searchResult = processAPI .searchFlowNodeInstances(searchOptionsBuilder.done()); final boolean found = searchResult.getCount() > 0; boolean check = found; if (found) { result = searchResult.getResult().get(0); if (state != null) { check = state.equals(result.getState()); } if (stateCategory != null) { check = stateCategory.equals(result.getStateCategory()); } } return check; } public FlowNodeInstance getResult() { return result; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForPendingTasks.java ================================================ /** * 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.wait; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.test.WaitUntil; /** * @author Baptiste Mesta */ @Deprecated public final class WaitForPendingTasks extends WaitUntil { private final int nbPendingTasks; private final long userId; private List results; private final ProcessAPI processAPI; public WaitForPendingTasks(final int repeatEach, final int timeout, final int nbPendingTasks, final long userId, final ProcessAPI processAPI) { super(repeatEach, timeout); this.nbPendingTasks = nbPendingTasks; this.userId = userId; this.processAPI = processAPI; } @Override protected boolean check() { results = processAPI.getPendingHumanTaskInstances(userId, 0, nbPendingTasks, null); return results.size() == nbPendingTasks; } public List getResults() { return results; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitForStep.java ================================================ /** * 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.wait; import java.util.Iterator; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.test.TestStates; import org.bonitasoft.engine.test.WaitUntil; @Deprecated public class WaitForStep extends WaitUntil { private final String stepName; private final long processInstanceId; private long activityInstanceId; private ActivityInstance result; private String state = null; private final ProcessAPI processAPI; @Deprecated public WaitForStep(final int repeatEach, final int timeout, final String stepName, final long processInstanceId, final ProcessAPI processAPI) { super(repeatEach, timeout); this.stepName = stepName; this.processInstanceId = processInstanceId; this.processAPI = processAPI; } @Deprecated public WaitForStep(final int repeatEach, final int timeout, final String stepName, final long processInstanceId, final TestStates state, final ProcessAPI processAPI) { super(repeatEach, timeout); this.stepName = stepName; this.processInstanceId = processInstanceId; this.state = state.getStateName(); this.processAPI = processAPI; } @Override protected boolean check() { final List openedActivityInstances = processAPI.getOpenActivityInstances(processInstanceId, 0, 10, ActivityInstanceCriterion.DEFAULT); final Iterator iterator = openedActivityInstances.iterator(); boolean found = false; while (iterator.hasNext() && !found) { final ActivityInstance activityInstance = iterator.next(); if (activityInstance.getName().equals(stepName)) { if (state == null || state.equals(activityInstance.getState())) { activityInstanceId = activityInstance.getId(); result = activityInstance; found = true; } } } return found; } public long getStepId() { return activityInstanceId; } public ActivityInstance getResult() { return result; } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/java/org/bonitasoft/engine/test/wait/WaitProcessToFinishAndBeArchived.java ================================================ /** * 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.wait; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.test.APITestUtil; import org.bonitasoft.engine.test.TestStates; import org.bonitasoft.engine.test.WaitUntil; @Deprecated public final class WaitProcessToFinishAndBeArchived extends WaitUntil { private final ProcessInstance processInstance; private final ProcessAPI processAPI; private final TestStates state; @Deprecated public WaitProcessToFinishAndBeArchived(final int repeatEach, final int timeout, final boolean throwExceptions, final ProcessInstance processInstance, final ProcessAPI processAPI, final TestStates state) { super(repeatEach, timeout, throwExceptions); this.processInstance = processInstance; this.processAPI = processAPI; this.state = state; } @Deprecated public WaitProcessToFinishAndBeArchived(final int repeatEach, final int timeout, final boolean throwExceptions, final ProcessInstance processInstance, final ProcessAPI processAPI) { this(repeatEach, timeout, throwExceptions, processInstance, processAPI, TestStates.NORMAL_FINAL); } @Deprecated public WaitProcessToFinishAndBeArchived(final int repeatEach, final int timeout, final ProcessInstance processInstance, final ProcessAPI processAPI) { this(repeatEach, timeout, false, processInstance, processAPI); } @Override protected boolean check() { final List archivedProcessInstances = processAPI .getArchivedProcessInstances(processInstance.getId(), 0, 200); return APITestUtil.containsState(archivedProcessInstances, state); } } ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorEngineExecutionContext.def ================================================ 1.0 org.bonitasoft.connector.testConnectorEngineExecutionContext xxx.png other org/bonitasoft/engine/connectors/TestConnectorEngineExecutionContext.png taskAssigneeId ================================================ FILE: bonita-integration-tests/bonita-test-utils/src/main/resources/org/bonitasoft/engine/connectors/TestConnectorEngineExecutionContext.impl ================================================ org.bonitasoft.connector.testConnectorEngineExecutionContext 1.0 org.bonitasoft.engine.connectors.TestConnectorEngineExecutionContext org.bonitasoft.connector.testConnectorEngineExecutionContext 1.0 TestConnectorEngineExecutionContext.jar ================================================ FILE: bonita-test-api/build.gradle ================================================ dependencies { api libs.commonsIO api project(':bonita-engine-standalone') api libs.junit4 api(project(':platform:platform-resources')) implementation libs.springTest // for http tests: compileOnly(libs.jettyServer) compileOnly(libs.jettyServlet) compileOnly(project(':bpm:bonita-api:bonita-server-api-http')) annotationProcessor libs.lombok compileOnly libs.lombok testImplementation project(':bpm:bonita-server') testImplementation libs.mockitoCore testImplementation libs.assertj } publishing { publications { mavenJava(MavenPublication) { from project.components.java } } } ================================================ FILE: bonita-test-api/src/main/java/org/bonitasoft/engine/test/ClientEventUtil.java ================================================ /** * 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.Serializable; import java.util.Arrays; 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.CommandDescriptor; import org.bonitasoft.engine.command.CommandExecutionException; import org.bonitasoft.engine.command.CommandNotFoundException; import org.bonitasoft.engine.command.CommandParameterizationException; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.APISession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility methods to get maps that match process as wanted * * @author Baptiste Mesta */ public class ClientEventUtil { public static final String ADD_HANDLER_COMMAND = "addHandlerCommand"; public static final String WAIT_SERVER_COMMAND = "waitServerCommand"; 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"; private static final Logger LOGGER = LoggerFactory.getLogger(ClientEventUtil.class.getName()); public static void undeployCommand(final APISession apiSession) throws BonitaException { final CommandAPI commandAPI = TenantAPIAccessor.getCommandAPI(apiSession); try { commandAPI.unregister(WAIT_SERVER_COMMAND); commandAPI.unregister(ADD_HANDLER_COMMAND); LOGGER.debug("commands undeployed"); } catch (final BonitaException e) { // ok LOGGER.debug("error undeploying command (maybe already undeployed?) " + e.getMessage()); } } public static void deployCommand(final APISession apiSession) throws BonitaException { final CommandAPI commandAPI = TenantAPIAccessor.getCommandAPI(apiSession); final Map parameters = new HashMap<>(1); final CommandDescriptor waitServerCommand; try { waitServerCommand = commandAPI.register(WAIT_SERVER_COMMAND, WAIT_SERVER_COMMAND, "org.bonitasoft.engine.synchro.WaitServerCommand"); } catch (AlreadyExistsException e) { return; } final CommandDescriptor addHandlerCommand = commandAPI.register(ADD_HANDLER_COMMAND, ADD_HANDLER_COMMAND, "org.bonitasoft.engine.synchro.AddHandlerCommand"); parameters.put("commands", (Serializable) Arrays.asList(waitServerCommand.getId(), addHandlerCommand.getId())); commandAPI.execute(ADD_HANDLER_COMMAND, parameters); LOGGER.debug("commands deployed"); } public 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(WAIT_SERVER_COMMAND, parameters); } catch (final CommandExecutionException e) { LOGGER.error("error while executing wait server command for element:" + event, e); if (e.getMessage().toLowerCase().contains("timeout")) { throw new TimeoutException("Timeout (" + defaultTimeout + " ms) when looking for element: " + event); } throw e; } } public static void clearRepo(final CommandAPI commandAPI) { final Map parameters = new HashMap<>(1); parameters.put("clear", true); try { commandAPI.execute(WAIT_SERVER_COMMAND, parameters); } catch (final BonitaException e) { // ok LOGGER.debug("error undeploying command (maybe already undeployed?) " + e.getMessage()); } } public static Map getReadyFlowNodeEvent(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 getReadyFlowNodeEvent(final String taskName) { final Map map = new HashMap<>(5); map.put(TYPE, FLOW_NODE); map.put(NAME, taskName); map.put(STATE_ID, 4); return map; } public static Map getFlowNodeInState(final String state, final String taskName) { final Map map = new HashMap<>(5); map.put(TYPE, FLOW_NODE); map.put(NAME, taskName); map.put(STATE, state); return map; } public static Map getFlowNodeInState(final long rootContainerId, final String state, final String flowNodeName) { final Map map = new HashMap<>(4); map.put(TYPE, FLOW_NODE); map.put(ROOT_CONTAINER_ID, rootContainerId); map.put(STATE, state); map.put(NAME, flowNodeName); return map; } public static Map getFlowNode(final long rootContainerId, final String flowNodeName) { final Map map = new HashMap<>(4); map.put(TYPE, FLOW_NODE); map.put(ROOT_CONTAINER_ID, rootContainerId); map.put(NAME, flowNodeName); return map; } public static Map getFlowNodeInState(final long rootContainerId, final String stateName) { final Map map = new HashMap<>(3); map.put(TYPE, FLOW_NODE); map.put(ROOT_CONTAINER_ID, rootContainerId); map.put(STATE, stateName); return map; } public static Map getFlowNodeInStateWithParentId(final long processInstanceId, final String state, final String flowNodeName) { final Map map = new HashMap<>(4); map.put(TYPE, FLOW_NODE); map.put(PARENT_CONTAINER_ID, processInstanceId); map.put(STATE, state); map.put(NAME, flowNodeName); return map; } public static Map getProcessInstanceInState(final long processInstanceId, final int stateId) { final HashMap map = new HashMap<>(3); map.put(TYPE, PROCESS); map.put(ID, processInstanceId); map.put(STATE_ID, stateId); return map; } public static Map getProcessInstanceInState(final int stateId) { final HashMap map = new HashMap<>(3); map.put(TYPE, PROCESS); map.put(STATE_ID, stateId); 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; } } ================================================ FILE: bonita-test-api/src/main/java/org/bonitasoft/engine/test/TestDatabaseConfigurator.java ================================================ /** * 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 static org.bonitasoft.engine.BonitaEngine.BONITA_BDM_DB_VENDOR; import static org.bonitasoft.engine.BonitaEngine.BONITA_DB_VENDOR; import static org.bonitasoft.engine.DefaultBonitaDatabaseConfigurations.defaultConfiguration; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.BonitaDatabaseConfiguration; @Slf4j class TestDatabaseConfigurator { /** * construct a database configuration bases on system properties * sysprop.bonita.db.vendor * db.vendor * db.url * db.driverClass * db.user * db.password * If some properties are not set use the default one from the db vendor */ BonitaDatabaseConfiguration getDatabaseConfiguration() { String dbVendor = System.getProperty(BONITA_DB_VENDOR, System.getProperty("db.vendor")); if (nullOrEmpty(dbVendor)) { log.info("System property '{}' or 'db.vendor' is not set, will use h2", BONITA_DB_VENDOR); dbVendor = "h2"; } String url = System.getProperty("db.url"); String driverClass = System.getProperty("db.driverClass"); String user = System.getProperty("db.user"); String password = System.getProperty("db.password"); BonitaDatabaseConfiguration.BonitaDatabaseConfigurationBuilder builder = BonitaDatabaseConfiguration.builder(); builder.dbVendor(dbVendor); BonitaDatabaseConfiguration defaultConfig = defaultConfiguration(dbVendor, "bonita"); builder.url(nullOrEmpty(url) ? defaultConfig.getUrl() : url); if (!nullOrEmpty(driverClass)) { builder.driverClassName(driverClass); } builder.user(nullOrEmpty(user) ? defaultConfig.getUser() : user); builder.password(password == null ? defaultConfig.getPassword() : password); return builder.build(); } BonitaDatabaseConfiguration getBDMDatabaseConfiguration() { String dbVendor = System.getProperty(BONITA_BDM_DB_VENDOR, System.getProperty("bdm.db.vendor", System.getProperty(BONITA_DB_VENDOR, System.getProperty("db.vendor")))); if (nullOrEmpty(dbVendor)) { log.info("System property '{}' or 'db.vendor' is not set, will use h2", BONITA_DB_VENDOR); dbVendor = "h2"; } String url = System.getProperty("bdm.db.url", System.getProperty("db.url")); String driverClass = System.getProperty("bdm.db.driverClass", System.getProperty("db.driverClass")); String user = System.getProperty("bdm.db.user", System.getProperty("db.user")); String password = System.getProperty("bdm.db.password", System.getProperty("db.password")); BonitaDatabaseConfiguration.BonitaDatabaseConfigurationBuilder builder = BonitaDatabaseConfiguration.builder(); builder.dbVendor(dbVendor); BonitaDatabaseConfiguration defaultConfig = defaultConfiguration(dbVendor, "business_data"); builder.url(nullOrEmpty(url) ? defaultConfig.getUrl() : url); if (!nullOrEmpty(driverClass)) { builder.driverClassName(driverClass); } builder.user(nullOrEmpty(user) ? defaultConfig.getUser() : user); builder.password(password == null ? defaultConfig.getPassword() : password); return builder.build(); } private static boolean nullOrEmpty(String s) { return s == null || s.isEmpty(); } } ================================================ FILE: bonita-test-api/src/main/java/org/bonitasoft/engine/test/TestEngine.java ================================================ /** * 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 org.bonitasoft.engine.BonitaDatabaseConfiguration; /** * @author Baptiste Mesta */ public interface TestEngine { String TECHNICAL_USER_NAME = "install"; String TECHNICAL_USER_PASSWORD = "install"; boolean start() throws Exception; void stop() throws Exception; void clearData() throws Exception; void setDropOnStart(boolean dropOnStart); void setBonitaDatabaseProperties(BonitaDatabaseConfiguration database); void setBusinessDataDatabaseProperties(BonitaDatabaseConfiguration database); } ================================================ FILE: bonita-test-api/src/main/java/org/bonitasoft/engine/test/TestEngineImpl.java ================================================ /** * 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 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.context.annotation.Profile; 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(); private BonitaDatabaseConfiguration bonitaDatabaseConfiguration; private BonitaDatabaseConfiguration businessDataDatabaseConfiguration; private final TestDatabaseConfigurator testDatabaseConfigurator = new TestDatabaseConfigurator(); BonitaHttpServer httpServer = new BonitaHttpServer(); private static TestEngineImpl createTestEngine() { return new TestEngineImpl(EngineStarter.create(), new EngineCommander()); } private final EngineStarter engineStarter; private final EngineCommander engineCommander; private boolean started = false; public static TestEngine getInstance() { return INSTANCE; } protected TestEngineImpl(EngineStarter engineStarter, EngineCommander engineCommander) { this.engineStarter = engineStarter; this.engineCommander = engineCommander; } protected static void replaceInstance(TestEngineImpl newTestEngine) { if (INSTANCE.started) { throw new IllegalStateException("trying to replace an already started instance"); } INSTANCE = newTestEngine; } /** * start the engine and return if it was effectively started * * @throws Exception if Engine cannot be started */ @Override public boolean start() throws Exception { initializeDatabaseConfigurations(); if (!started) { doStart(); started = true; // Starting HTTP server must be done AFTER engine start, as the API Type will be overridden to HTTP // (See EngineStarter logic): httpServer.startIfNeeded(); return true; } return false; } private void initializeDatabaseConfigurations() { if (bonitaDatabaseConfiguration == null || businessDataDatabaseConfiguration == null) { if (bonitaDatabaseConfiguration == null) { bonitaDatabaseConfiguration = testDatabaseConfigurator.getDatabaseConfiguration(); engineStarter.setBonitaDatabaseConfiguration(bonitaDatabaseConfiguration); } if (businessDataDatabaseConfiguration == null) { businessDataDatabaseConfiguration = testDatabaseConfigurator.getBDMDatabaseConfiguration(); engineStarter.setBusinessDataDatabaseConfiguration(businessDataDatabaseConfiguration); } } } protected synchronized void doStart() throws Exception { engineStarter.start(); } @Override public void stop() throws Exception { if (started) { doStop(); started = false; httpServer.stopIfNeeded(); } } private void doStop() throws Exception { engineStarter.stop(); } @Override public void clearData() throws Exception { engineCommander.clearData(); } @Override public void setDropOnStart(boolean dropOnStart) { engineStarter.setDropOnStart(dropOnStart); } @Override public void setBonitaDatabaseProperties(BonitaDatabaseConfiguration configuration) { bonitaDatabaseConfiguration = configuration; } @Override public void setBusinessDataDatabaseProperties(BonitaDatabaseConfiguration database) { this.businessDataDatabaseConfiguration = database; } /** * Configuration class used to override bean definitions for test purposes. */ @Configuration @Profile("!update-tool") // to allow to remove this default bypass in Update Tool tests (and so test it in the real condition) static class TestConfiguration { @Bean ProcessStarterVerifier processStarterVerifierImpl() { return processInstance -> { // Override this bean to disable the process starter verifier }; } } } ================================================ FILE: bonita-test-api/src/main/java/org/bonitasoft/engine/test/http/BonitaHttpServer.java ================================================ /** * Copyright (C) 2020 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.http; import java.util.HashMap; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.ApiAccessType; import org.bonitasoft.engine.util.APITypeManager; import org.eclipse.jetty.server.ServerConnector; /** * Handles a jetty HTTP server and registers Bonita APIs in HTTP mode, * if tests are requested to be configured that way. * To request that, start Engine with system property '-Dorg.bonitasoft.engine.access.mode=http'. * * @author Emmanuel Duchastenier */ @Slf4j public class BonitaHttpServer { private JettyServer jettyServer; public void startIfNeeded() throws Exception { if ("http".equals(System.getProperty("org.bonitasoft.engine.access.mode", null))) { log.info("Starting Http Server for Bonita Engine..."); jettyServer = new JettyServer(); jettyServer.start(); setClientApiToHTTP(((ServerConnector) jettyServer.getServer().getConnectors()[0]).getLocalPort()); } } public static void setClientApiToHTTP(int localPort) { Map params = new HashMap<>(); params.put("server.url", "http://localhost:" + localPort); params.put("application.name", "bonita"); APITypeManager.setAPITypeAndParams(ApiAccessType.HTTP, params); } public void stopIfNeeded() throws Exception { if (jettyServer != null) { jettyServer.stop(); } } } ================================================ FILE: bonita-test-api/src/main/java/org/bonitasoft/engine/test/http/JettyServer.java ================================================ /** * Copyright (C) 2020 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.http; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.internal.servlet.HttpAPIServlet; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletHandler; /** * Handles a jetty HTTP server and registers Bonita APIs in HTTP mode, * if tests are requested to be configured that way. * To request that, start Engine with system property '-Dorg.bonitasoft.engine.access.mode=http'. * * @author Emmanuel Duchastenier */ @Slf4j public class JettyServer { private Server server; public void start() throws Exception { server = startJettyServer(); } public Server startJettyServer() throws Exception { // Running our HttpAPIServlet in a Jetty server: Server jettyServer = new Server(0);// 0=random port ServletHandler handler = new ServletHandler(); jettyServer.setHandler(handler); handler.addServletWithMapping(HttpAPIServlet.class, "/bonita/serverAPI/*"); jettyServer.start(); return jettyServer; } public void stop() throws Exception { if (server != null) { server.stop(); server.join(); } } Server getServer() { return server; } } ================================================ FILE: bonita-test-api/src/main/java/org/bonitasoft/engine/test/internal/EngineCommander.java ================================================ /** * 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.internal; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.BusinessDataAPI; import org.bonitasoft.engine.api.CommandAPI; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.LoginAPI; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.PermissionAPI; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.ProfileAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.api.TenantAdministrationAPI; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.engine.bpm.category.CategoryCriterion; import org.bonitasoft.engine.bpm.comment.ArchivedComment; import org.bonitasoft.engine.bpm.comment.Comment; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.WaitingEvent; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.command.CommandDescriptor; import org.bonitasoft.engine.command.CommandNotFoundException; import org.bonitasoft.engine.command.CommandSearchDescriptor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.GroupCriterion; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.RoleCriterion; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserCriterion; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.test.ClientEventUtil; import org.bonitasoft.engine.test.TestEngineImpl; import org.slf4j.LoggerFactory; /** * @author Baptiste Mesta */ public class EngineCommander { private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(EngineCommander.class.getName()); private APISession session; private ProcessAPI processAPI; private IdentityAPI identityAPI; private CommandAPI commandAPI; private ProfileAPI profileAPI; private PermissionAPI permissionAPI; private PageAPI pageAPI; private ApplicationAPI applicationAPI; private TenantAdministrationAPI tenantManagementCommunityAPI; private BusinessDataAPI businessDataAPI; public APISession getSession() { return session; } public void loginOnDefaultTenantWithDefaultTechnicalUser() throws BonitaException { final LoginAPI loginAPI = getLoginAPI(); setSession(loginAPI.login(TestEngineImpl.TECHNICAL_USER_NAME, TestEngineImpl.TECHNICAL_USER_NAME)); setAPIs(); } protected void setAPIs() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { setIdentityAPI(TenantAPIAccessor.getIdentityAPI(getSession())); setProcessAPI(TenantAPIAccessor.getProcessAPI(getSession())); setCommandAPI(TenantAPIAccessor.getCommandAPI(getSession())); setProfileAPI(TenantAPIAccessor.getProfileAPI(getSession())); setPermissionAPI(TenantAPIAccessor.getPermissionAPI(getSession())); setPageAPI(TenantAPIAccessor.getCustomPageAPI(getSession())); setApplicationAPI(TenantAPIAccessor.getLivingApplicationAPI(getSession())); setTenantManagementCommunityAPI(TenantAPIAccessor.getTenantAdministrationAPI(getSession())); setBusinessDataAPI(TenantAPIAccessor.getBusinessDataAPI(getSession())); } public void setBusinessDataAPI(final BusinessDataAPI businessDataAPI) { this.businessDataAPI = businessDataAPI; } public void logoutOnTenant() throws BonitaException { final LoginAPI loginAPI = getLoginAPI(); loginAPI.logout(session); setSession(null); setIdentityAPI(null); setProcessAPI(null); setCommandAPI(null); setProfileAPI(null); setPermissionAPI(null); setApplicationAPI(null); setTenantManagementCommunityAPI(null); setPageAPI(null); setBusinessDataAPI(null); } public LoginAPI getLoginAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getLoginAPI(); } public BusinessDataAPI getBusinessDataAPI() { return businessDataAPI; } public ProcessAPI getProcessAPI() { return processAPI; } public void setProcessAPI(final ProcessAPI processAPI) { this.processAPI = processAPI; } public IdentityAPI getIdentityAPI() { return identityAPI; } public void setIdentityAPI(final IdentityAPI identityAPI) { this.identityAPI = identityAPI; } public CommandAPI getCommandAPI() { return commandAPI; } public void setCommandAPI(final CommandAPI commandAPI) { this.commandAPI = commandAPI; } public ProfileAPI getProfileAPI() { return profileAPI; } public void setProfileAPI(final ProfileAPI profileAPI) { this.profileAPI = profileAPI; } public PermissionAPI getPermissionAPI() { return permissionAPI; } public void setPermissionAPI(final PermissionAPI permissionAPI) { this.permissionAPI = permissionAPI; } public void setSession(final APISession session) { this.session = session; } protected void setPageAPI(final PageAPI pageAPI) { this.pageAPI = pageAPI; } public PageAPI getPageAPI() { return pageAPI; } public ApplicationAPI getApplicationAPI() { return applicationAPI; } public void setApplicationAPI(final ApplicationAPI applicationAPI) { this.applicationAPI = applicationAPI; } public TenantAdministrationAPI getTenantAdministrationAPI() { return tenantManagementCommunityAPI; } public void setTenantManagementCommunityAPI(final TenantAdministrationAPI tenantManagementCommunityAPI) { this.tenantManagementCommunityAPI = tenantManagementCommunityAPI; } public void clearData() throws Exception { loginOnDefaultTenantWithDefaultTechnicalUser(); final List messages = new ArrayList<>(); messages.addAll(checkNoCommands()); messages.addAll(checkNoFlowNodes()); messages.addAll(checkNoArchivedFlowNodes()); messages.addAll(checkNoComments()); messages.addAll(checkNoArchivedComments()); messages.addAll(checkNoWaitingEvent()); messages.addAll(checkNoProcessInstances()); messages.addAll(checkNoArchivedProcessInstances()); messages.addAll(checkNoProcessDefinitions()); messages.addAll(checkNoCategories()); messages.addAll(checkNoUsers()); messages.addAll(checkNoGroups()); messages.addAll(checkNoRoles()); messages.addAll(checkNoSupervisors()); logoutOnTenant(); if (!messages.isEmpty()) { LOGGER.warn("Engine was not clean after test. We cleaned for you the following elements:"); for (String message : messages) { LOGGER.warn(message); } } } public List checkNoCategories() throws DeletionException { final List messages = new ArrayList<>(); final long numberOfCategories = getProcessAPI().getNumberOfCategories(); if (numberOfCategories > 0) { final List categories = getProcessAPI().getCategories(0, 5000, CategoryCriterion.NAME_ASC); final StringBuilder categoryBuilder = new StringBuilder("Categories were still present: "); for (final Category category : categories) { categoryBuilder.append(category.getName()).append(", "); getProcessAPI().deleteCategory(category.getId()); } messages.add(categoryBuilder.toString()); } return messages; } public List checkNoFlowNodes() throws SearchException { final List messages = new ArrayList<>(); final SearchOptionsBuilder build = new SearchOptionsBuilder(0, 1000); final SearchResult searchResult = getProcessAPI().searchFlowNodeInstances(build.done()); final List flowNodeInstances = searchResult.getResult(); if (searchResult.getCount() > 0) { final StringBuilder messageBuilder = new StringBuilder("FlowNodes were still present: "); for (final FlowNodeInstance flowNodeInstance : flowNodeInstances) { messageBuilder.append("{").append(flowNodeInstance.getName()).append(" - ") .append(flowNodeInstance.getType()).append("}").append(", "); } messages.add(messageBuilder.toString()); } return messages; } public List checkNoArchivedFlowNodes() throws SearchException { final List messages = new ArrayList<>(); final SearchOptionsBuilder build = new SearchOptionsBuilder(0, 1000); final SearchResult searchResult = getProcessAPI() .searchArchivedFlowNodeInstances(build.done()); final List archivedFlowNodeInstances = searchResult.getResult(); if (searchResult.getCount() > 0) { final StringBuilder messageBuilder = new StringBuilder("Archived flowNodes were still present: "); for (final ArchivedFlowNodeInstance archivedFlowNodeInstance : archivedFlowNodeInstances) { messageBuilder.append(archivedFlowNodeInstance.getName()).append(", "); } messages.add(messageBuilder.toString()); } return messages; } public List checkNoComments() throws SearchException { final List messages = new ArrayList<>(); final SearchOptionsBuilder build = new SearchOptionsBuilder(0, 1000); final SearchResult searchResult = getProcessAPI().searchComments(build.done()); final List comments = searchResult.getResult(); if (searchResult.getCount() > 0) { final StringBuilder messageBuilder = new StringBuilder("Comments were still present: "); for (final Comment comment : comments) { messageBuilder.append(comment.getContent()).append(", "); } messages.add(messageBuilder.toString()); } return messages; } public List checkNoArchivedComments() throws SearchException { final List messages = new ArrayList<>(); final SearchOptionsBuilder build = new SearchOptionsBuilder(0, 1000); final SearchResult searchResult = getProcessAPI().searchArchivedComments(build.done()); final List archivedComments = searchResult.getResult(); if (searchResult.getCount() > 0) { final StringBuilder messageBuilder = new StringBuilder("Archived comments were still present: "); for (final ArchivedComment archivedComment : archivedComments) { messageBuilder.append(archivedComment.getName()).append(", "); } messages.add(messageBuilder.toString()); } return messages; } public List checkNoProcessDefinitions() throws BonitaException { final List messages = new ArrayList<>(); final List processes = getProcessAPI().getProcessDeploymentInfos(0, 200, ProcessDeploymentInfoCriterion.DEFAULT); if (processes.size() > 0) { final StringBuilder processBuilder = new StringBuilder("Process Definitions were still active: "); for (final ProcessDeploymentInfo processDeploymentInfo : processes) { processBuilder.append(processDeploymentInfo.getId()).append(", "); if (ActivationState.ENABLED.equals(processDeploymentInfo.getActivationState())) { getProcessAPI().disableProcess(processDeploymentInfo.getProcessId()); } getProcessAPI().deleteProcessDefinition(processDeploymentInfo.getProcessId()); } messages.add(processBuilder.toString()); } return messages; } public List checkNoWaitingEvent() throws BonitaException { final List messages = new ArrayList<>(); final Map parameters = new HashMap<>(1); parameters.put("searchOptions", new SearchOptionsBuilder(0, 200).done()); @SuppressWarnings("unchecked") final List waitingEvents = ((SearchResult) getCommandAPI() .execute("searchWaitingEventsCommand", parameters)).getResult(); if (waitingEvents.size() > 0) { final StringBuilder messageBuilder = new StringBuilder( "Waiting Events are still present (not deleted automatically): "); for (final WaitingEvent waitingEvent : waitingEvents) { messageBuilder.append("[process instance:").append(waitingEvent.getProcessName()) .append(", flow node instance:") .append(waitingEvent.getFlowNodeInstanceId()).append("]").append( ", "); } messages.add(messageBuilder.toString()); } return messages; } public List checkNoSupervisors() throws SearchException, DeletionException { final List messages = new ArrayList<>(); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 200); builder.sort(ProcessSupervisorSearchDescriptor.ID, Order.ASC); final List supervisors = getProcessAPI().searchProcessSupervisors(builder.done()) .getResult(); if (supervisors.size() > 0) { final StringBuilder processBuilder = new StringBuilder("Process Supervisors were still active: "); for (final ProcessSupervisor supervisor : supervisors) { processBuilder.append(supervisor.getSupervisorId()).append(", "); getProcessAPI().deleteSupervisor(supervisor.getSupervisorId()); } messages.add(processBuilder.toString()); } return messages; } public List checkNoProcessInstances() throws DeletionException { final List messages = new ArrayList<>(); final List processInstances = getProcessAPI().getProcessInstances(0, 1000, ProcessInstanceCriterion.DEFAULT); if (!processInstances.isEmpty()) { final StringBuilder stb = new StringBuilder("Process instances were still present: "); for (final ProcessInstance processInstance : processInstances) { stb.append(processInstance).append(", "); getProcessAPI().deleteProcessInstance(processInstance.getId()); } messages.add(stb.toString()); } return messages; } public List checkNoArchivedProcessInstances() throws DeletionException { final List messages = new ArrayList<>(); final List archivedProcessInstances = getProcessAPI().getArchivedProcessInstances(0, 1000, ProcessInstanceCriterion.DEFAULT); if (!archivedProcessInstances.isEmpty()) { final StringBuilder stb = new StringBuilder("Archived process instances were still present: "); for (final ArchivedProcessInstance archivedProcessInstance : archivedProcessInstances) { stb.append(archivedProcessInstance).append(", "); getProcessAPI().deleteArchivedProcessInstancesInAllStates(archivedProcessInstance.getSourceObjectId()); } messages.add(stb.toString()); } return messages; } public List checkNoGroups() throws DeletionException { final List messages = new ArrayList<>(); final long numberOfGroups = getIdentityAPI().getNumberOfGroups(); if (numberOfGroups > 0) { final List groups = getIdentityAPI().getGroups(0, Long.valueOf(numberOfGroups).intValue(), GroupCriterion.NAME_ASC); final StringBuilder groupBuilder = new StringBuilder("Groups were still present: "); for (final Group group : groups) { groupBuilder.append(group.getId()).append(", "); getIdentityAPI().deleteGroup(group.getId()); } messages.add(groupBuilder.toString()); } return messages; } public List checkNoRoles() throws DeletionException { final List messages = new ArrayList<>(); final long numberOfRoles = getIdentityAPI().getNumberOfRoles(); if (numberOfRoles > 0) { final List roles = getIdentityAPI().getRoles(0, Long.valueOf(numberOfRoles).intValue(), RoleCriterion.NAME_ASC); final StringBuilder roleBuilder = new StringBuilder("Roles were still present: "); for (final Role role : roles) { roleBuilder.append(role.getId()).append(", "); getIdentityAPI().deleteRole(role.getId()); } messages.add(roleBuilder.toString()); } return messages; } public List checkNoUsers() throws DeletionException { final List messages = new ArrayList<>(); final long numberOfUsers = getIdentityAPI().getNumberOfUsers(); if (numberOfUsers > 0) { final List users = getIdentityAPI().getUsers(0, Long.valueOf(numberOfUsers).intValue(), UserCriterion.USER_NAME_ASC); final StringBuilder userBuilder = new StringBuilder("Users were still present: "); for (final User user : users) { userBuilder.append(user.getId()).append(", "); getIdentityAPI().deleteUser(user.getId()); } messages.add(userBuilder.toString()); } return messages; } public List checkNoCommands() throws SearchException, CommandNotFoundException, DeletionException { final List messages = new ArrayList<>(); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 1000); searchOptionsBuilder.filter(CommandSearchDescriptor.SYSTEM, false); searchOptionsBuilder.differentFrom(CommandSearchDescriptor.NAME, ClientEventUtil.ADD_HANDLER_COMMAND); searchOptionsBuilder.differentFrom(CommandSearchDescriptor.NAME, ClientEventUtil.WAIT_SERVER_COMMAND); final SearchResult searchCommands = getCommandAPI() .searchCommands(searchOptionsBuilder.done()); final List commands = searchCommands.getResult(); if (searchCommands.getCount() > 0) { final StringBuilder commandBuilder = new StringBuilder("Commands were still present: "); for (final CommandDescriptor command : commands) { commandBuilder.append(command.getName()).append(", "); getCommandAPI().unregister(command.getName()); } messages.add(commandBuilder.toString()); } return messages; } } ================================================ FILE: bonita-test-api/src/main/java/org/bonitasoft/engine/test/internal/EngineStarter.java ================================================ /** * 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.internal; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.lang.management.ManagementFactory; import java.nio.file.Files; 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; import org.bonitasoft.engine.api.*; import org.bonitasoft.engine.event.PlatformStartedEvent; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.platform.LoginException; import org.bonitasoft.engine.platform.LogoutException; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.engine.session.SessionNotFoundException; import org.bonitasoft.engine.test.ClientEventUtil; import org.bonitasoft.engine.test.TestEngineImpl; import org.bonitasoft.engine.util.APITypeManager; import org.bonitasoft.platform.database.DatabaseVendor; import org.bonitasoft.platform.setup.PlatformSetup; import org.bonitasoft.platform.setup.PlatformSetupAccessor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Baptiste Mesta */ public class EngineStarter { private static final String DATABASE_DIR = "org.bonitasoft.h2.database.dir"; protected static final Logger LOGGER = LoggerFactory.getLogger(EngineStarter.class.getName()); private static boolean hasFailed = false; private boolean dropOnStart = true; private final BonitaEngine engine; protected EngineStarter(BonitaEngine engine) { this.engine = engine; } public static EngineStarter create() { return new EngineStarter(new BonitaEngine()); } public static EngineStarter create(BonitaEngine engine) { return new EngineStarter(engine); } public void start() throws Exception { if (hasFailed) { throw new IllegalStateException("Engine has previously failed to start"); } try { LOGGER.info("====================================================="); LOGGER.info("============== Starting Bonita Engine ============="); LOGGER.info("====================================================="); final long startTime = System.currentTimeMillis(); System.setProperty("com.arjuna.ats.arjuna.common.propertiesFile", "jbossts-properties.xml"); if (System.getProperty("org.bonitasoft.engine.api-type") == null) { //force it to local if not specified APITypeManager.setAPITypeAndParams(ApiAccessType.LOCAL, Collections.emptyMap()); } if (APITypeManager.getAPIType().equals(ApiAccessType.LOCAL)) { prepareEnvironment(); setupPlatform(); engine.start(); } deployCommandsOnDefaultTenant(); LOGGER.info("==== Finished initialization (took " + (System.currentTimeMillis() - startTime) / 1000 + "s) ==="); LOGGER.debug("Publishing platform started event"); ServiceAccessorFactory.getInstance().createServiceAccessor().publishEvent(new PlatformStartedEvent()); } catch (Exception e) { hasFailed = true; throw e; } } protected void setupPlatform() throws Exception { 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 { LOGGER.info("========= PREPARE ENVIRONMENT ======="); String dbVendor = setSystemPropertyIfNotSet(PlatformSetup.BONITA_DB_VENDOR_PROPERTY, DatabaseVendor.H2.getValue()); //is h2 and not started outside if (DatabaseVendor.H2.equalsValue(dbVendor)) { setSystemPropertyIfNotSet(DATABASE_DIR, "build/database"); } engine.initializeEnvironment(); } protected void shutdown() throws Exception { undeployCommands(); deleteTenantAndPlatform(); } protected void deleteTenantAndPlatform() throws Exception { LOGGER.info("========= CLEAN PLATFORM ======="); final PlatformSession session = loginOnPlatform(); final PlatformAPI platformAPI = getPlatformAPI(session); if (platformAPI.isNodeStarted()) { platformAPI.stopNode(); } logoutOnPlatform(session); engine.stop(); } protected PlatformAPI getPlatformAPI(PlatformSession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return PlatformAPIAccessor.getPlatformAPI(session); } protected void checkThreadsAreStopped() throws InterruptedException { LOGGER.info("========= CHECK ENGINE IS SHUTDOWN ======="); final Set keySet = Thread.getAllStackTraces().keySet(); List expectedThreads = new ArrayList<>(); List cacheManagerThreads = new ArrayList<>(); List unexpectedThreads = new ArrayList<>(); for (Thread thread : keySet) { if (isExpectedThread(thread)) { expectedThreads.add(thread); } else { if (isCacheManager(thread)) { cacheManagerThreads.add(thread); } else { unexpectedThreads.add(thread); } } } // Only 1 cache manager thread is allowed // there is no clean way to kill it, a shutdown hook is doing this // killing it using hibernate implementation classes is causing weird behaviours int nbOfThreads = keySet.size(); int nbOfExpectedThreads = expectedThreads.size() + 2; boolean fail = nbOfThreads > nbOfExpectedThreads; LOGGER.info(nbOfThreads + " threads are alive. " + nbOfExpectedThreads + " are expected."); if (cacheManagerThreads.size() > 2) { LOGGER.info( "Only 1 CacheManager thread is expected (HibernatePersistenceService) but " + cacheManagerThreads.size() + " are found:"); for (Thread thread : cacheManagerThreads) { printThread(thread); } } if (!unexpectedThreads.isEmpty()) { LOGGER.info("The following list of threads is not expected:"); for (Thread thread : unexpectedThreads) { printThread(thread); } } if (fail) { throw new IllegalStateException( "Some threads are still active : \nCacheManager potential issues:" + cacheManagerThreads + "\nOther threads:" + unexpectedThreads); } LOGGER.info("All engine threads are stopped properly"); } private boolean isCacheManager(Thread thread) { return thread.getName().contains("ehcache") || thread.getName().contains("Ehcache"); } private void printThread(final Thread thread) { LOGGER.info("\n"); LOGGER.info("Thread is still alive:" + thread.getName()); for (StackTraceElement stackTraceElement : thread.getStackTrace()) { LOGGER.info(" at " + stackTraceElement); } } private boolean isExpectedThread(final Thread thread) { final String name = thread.getName(); final ThreadGroup threadGroup = thread.getThreadGroup(); if (threadGroup != null && threadGroup.getName().equals("system")) { return true; } final List startWithFilter = Arrays.asList("H2 ", "Timer-0" /* postgres driver related */, "Transaction Reaper Worker 0", "Transaction Reaper", "main", "Reference Handler", "Signal Dispatcher", "Finalizer", "com.google.common.base.internal.Finalizer", "process reaper", "ReaderThread", "Abandoned connection cleanup thread", "Monitor Ctrl-Break"/* Intellij */, "daemon-shutdown", "surefire-forkedjvm", "Restlet", "hz.bonita"); for (final String prefix : startWithFilter) { if (name.startsWith(prefix)) { return true; } } //shutdown hook not executed in main thread return thread.getId() == Thread.currentThread().getId(); } protected PlatformLoginAPI getPlatformLoginAPI() throws BonitaException { return PlatformAPIAccessor.getPlatformLoginAPI(); } protected PlatformSession loginOnPlatform() throws BonitaException { final PlatformLoginAPI platformLoginAPI = getPlatformLoginAPI(); return platformLoginAPI.login("platformAdmin", "platform"); } protected void deployCommandsOnDefaultTenant() throws BonitaException { final LoginAPI loginAPI = getLoginAPI(); final APISession session = login(loginAPI); ClientEventUtil.deployCommand(session); logout(loginAPI, session); } private void logout(LoginAPI loginAPI, APISession session) throws SessionNotFoundException, LogoutException { loginAPI.logout(session); } private APISession login(LoginAPI loginAPI) throws LoginException { return loginAPI.login(TestEngineImpl.TECHNICAL_USER_NAME, TestEngineImpl.TECHNICAL_USER_PASSWORD); } protected LoginAPI getLoginAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getLoginAPI(); } protected void logoutOnPlatform(final PlatformSession session) throws BonitaException { final PlatformLoginAPI platformLoginAPI = getPlatformLoginAPI(); platformLoginAPI.logout(session); } protected static String setSystemPropertyIfNotSet(final String property, final String value) { final String finalValue = System.getProperty(property, value); System.setProperty(property, finalValue); return finalValue; } protected void undeployCommands() throws BonitaException { final LoginAPI loginAPI = getLoginAPI(); final APISession session = login(loginAPI); ClientEventUtil.undeployCommand(session); logout(loginAPI, session); } public void stop() throws Exception { LOGGER.info("====================================================="); LOGGER.info("============ CLEANING OF TEST ENVIRONMENT ==========="); LOGGER.info("====================================================="); shutdown(); checkTempFoldersAreCleaned(); checkThreadsAreStopped(); } protected void checkTempFoldersAreCleaned() throws IOException { final List folders = getTemporaryFolders(); removeLicensesFolderAndDeleteIt(folders); StringBuilder builder = new StringBuilder(); for (File folder : folders) { builder.append("["); builder.append(folder.getName()); builder.append("] "); } if (!folders.isEmpty()) { throw new IllegalStateException("Temporary configuration folders are not cleaned:" + builder.toString()); } LOGGER.info("Temporary configuration folder is cleaned"); } private List getTemporaryFolders() { File tempFolder = new File(IOUtil.TMP_DIRECTORY); FilenameFilter filter = (file, s) -> s.startsWith("bonita_"); return new ArrayList<>(Arrays.asList(tempFolder.listFiles(filter))); } private void removeLicensesFolderAndDeleteIt(List list) throws IOException { Iterator iterator = list.iterator(); while (iterator.hasNext()) { File tempFolder = iterator.next(); //folder of licenses not deleted because the shutdown hook that delete temp files //is executed as the same time as the shutdown hook that stops the engine if (tempFolder.getName().contains("bonita_engine") && tempFolder.getName().contains(ManagementFactory.getRuntimeMXBean().getName())) { Path licenses = tempFolder.toPath().resolve("licenses"); if (Files.exists(licenses)) { FileUtils.deleteDirectory(licenses.toFile()); } if (tempFolder.list().length == 0) { FileUtils.deleteDirectory(tempFolder); //remove this directory because there was only the licenses there iterator.remove(); } } } } public void setDropOnStart(boolean dropOnStart) { this.dropOnStart = dropOnStart; } public boolean isDropOnStart() { return dropOnStart; } public void setBonitaDatabaseConfiguration(BonitaDatabaseConfiguration database) { engine.setBonitaDatabaseConfiguration(database); } public void setBusinessDataDatabaseConfiguration(BonitaDatabaseConfiguration bonitaDatabaseConfiguration) { engine.setBusinessDataDatabaseConfiguration(bonitaDatabaseConfiguration); } } ================================================ FILE: bonita-test-api/src/main/java/org/bonitasoft/engine/test/junit/BonitaEngineRule.java ================================================ /** * 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.junit; import java.lang.reflect.InvocationTargetException; import org.bonitasoft.engine.test.TestEngine; import org.bonitasoft.engine.test.TestEngineImpl; import org.junit.rules.MethodRule; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.Statement; /** * @author Baptiste Mesta */ public class BonitaEngineRule implements MethodRule { private final TestEngine testEngine; private boolean cleanAfterTest; protected BonitaEngineRule(TestEngine testEngine) { this.testEngine = testEngine; } public static BonitaEngineRule create() { try { var testEngineSpClazz = BonitaEngineRule.class.getClassLoader() .loadClass("com.bonitasoft.engine.test.TestEngineSP"); TestEngine testEngineSpInstance = (TestEngine) testEngineSpClazz.getMethod("getInstance").invoke(null); return new BonitaEngineRule(testEngineSpInstance); } catch (ClassNotFoundException e) { // Use Community TestEngine } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { throw new RuntimeException(e); } return new BonitaEngineRule(TestEngineImpl.getInstance()); } public static BonitaEngineRule createWith(TestEngine testEngine) { return new BonitaEngineRule(testEngine); } public BonitaEngineRule withCleanAfterTest() { cleanAfterTest = true; return this; } // Used by Migration: public BonitaEngineRule reuseExistingPlatform() { testEngine.setDropOnStart(false); return this; } @Override public Statement apply(Statement statement, FrameworkMethod method, Object target) { Statement newStatement = new WithTestEngine(statement, getTestEngine()); if (cleanAfterTest) { newStatement = new WithCleanAfterTest(newStatement, getTestEngine()); } return newStatement; } private static class WithTestEngine extends Statement { private final Statement statement; private final TestEngine testEngine; public WithTestEngine(Statement statement, TestEngine testEngine) { this.statement = statement; this.testEngine = testEngine; } @Override public void evaluate() throws Throwable { startEngine(); statement.evaluate(); } private void startEngine() throws Exception { final boolean start = testEngine.start(); if (start) { Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { testEngine.stop(); } catch (Exception e) { e.printStackTrace(); } })); } } } protected TestEngine getTestEngine() { return testEngine; } private static class WithCleanAfterTest extends Statement { private final Statement statement; private final TestEngine testEngine; public WithCleanAfterTest(Statement statement, TestEngine testEngine) { this.statement = statement; this.testEngine = testEngine; } @Override public void evaluate() throws Throwable { statement.evaluate(); testEngine.clearData(); } } } ================================================ FILE: bonita-test-api/src/main/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: bonita-test-api/src/test/java/org/bonitasoft/engine/test/TestEngineImplTest.java ================================================ /** * 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 static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import org.bonitasoft.engine.test.internal.EngineCommander; import org.bonitasoft.engine.test.internal.EngineStarter; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class TestEngineImplTest { @Mock private EngineStarter engineStarter; @Mock private EngineCommander engineCommander; @InjectMocks private TestEngineImpl testEngine; @Test public void should_start_do_nothing_second_time() throws Exception { //when testEngine.start(); testEngine.start(); //then verify(engineStarter, times(1)).start(); } } ================================================ FILE: bonita-test-api/src/test/java/org/bonitasoft/engine/test/internal/EngineStarterTest.java ================================================ /** * 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.internal; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; import org.bonitasoft.engine.BonitaEngine; import org.bonitasoft.engine.api.PlatformAPI; import org.bonitasoft.engine.api.PlatformLoginAPI; import org.bonitasoft.engine.session.PlatformSession; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class EngineStarterTest { @Mock private PlatformSession platformSession; @Mock private PlatformAPI platformAPI; @Mock private PlatformLoginAPI platformLoginAPI; @Mock private BonitaEngine bonitaEngine; private EngineStarter engineStarter; @Before public void before() throws Exception { engineStarter = spy(EngineStarter.create(bonitaEngine)); doReturn(platformSession).when(platformLoginAPI).login(anyString(), anyString()); doReturn(platformLoginAPI).when(engineStarter).getPlatformLoginAPI(); doReturn(platformAPI).when(engineStarter).getPlatformAPI(any(PlatformSession.class)); doNothing().when(engineStarter).undeployCommands(); doNothing().when(engineStarter).checkTempFoldersAreCleaned(); doNothing().when(engineStarter).checkThreadsAreStopped(); } @Test public void should_stop_node() throws Exception { //given doReturn(true).when(platformAPI).isNodeStarted(); //when engineStarter.stop(); //then verify(platformAPI).stopNode(); verify(bonitaEngine).stop(); } } ================================================ FILE: bonita-test-api/src/test/java/org/bonitasoft/engine/test/junit/BonitaEngineRuleTest.java ================================================ /** * 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.junit; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.BonitaDatabaseConfiguration; import org.bonitasoft.engine.test.TestEngine; import org.junit.Rule; import org.junit.Test; /** * @author Baptiste Mesta */ public class BonitaEngineRuleTest { private final MyTestEngine testEngineToInject = new MyTestEngine(); @Rule public BonitaEngineRule bonitaEngineRule = BonitaEngineRule.createWith(testEngineToInject); @Test public void should_TestEngine_be_started() { assertThat(testEngineToInject.isStarted).isTrue(); } private static class MyTestEngine implements TestEngine { public boolean isStarted; @Override public boolean start() { isStarted = true; return false; } @Override public void stop() { } @Override public void clearData() { } @Override public void setDropOnStart(boolean dropOnStart) { } @Override public void setBonitaDatabaseProperties(BonitaDatabaseConfiguration database) { } @Override public void setBusinessDataDatabaseProperties(BonitaDatabaseConfiguration database) { } } } ================================================ FILE: bpm/bonita-api/bonita-server-api-http/build.gradle ================================================ dependencies { api project(':platform:platform-resources') api project(':bpm:bonita-core:bonita-process-engine') api project(':services:bonita-platform-session') api project(':services:bonita-session') api project(':bpm:bonita-common') api libs.xstream api libs.commonsFileUpload api libs.commonsIO testImplementation libs.springTest testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.systemRules testRuntimeOnly libs.springWebMvc compileOnly libs.jakartaServletApi testImplementation libs.jakartaServletApi } ================================================ FILE: bpm/bonita-api/bonita-server-api-http/src/main/java/org/bonitasoft/engine/api/internal/servlet/EngineInitializerListener.java ================================================ /** * 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.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; import org.springframework.context.ApplicationContext; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; /** * Servlet container entry point that orchestrates the complete Bonita Engine initialization sequence. *

* This listener is triggered by the servlet container (Tomcat) during application startup * and shutdown. It coordinates the following initialization phases: *

    *
  1. Platform Setup Phase: Initializes the database schema and configuration via {@link PlatformSetup}
  2. *
  3. Engine Initialization Phase: Starts the engine and loads all services via {@link EngineInitializer}
  4. *
  5. Web Context Phase: Creates the web application context with the engine context as parent
  6. *
*

* Responsibilities: *

    *
  • Invokes {@link PlatformSetup#init()} to create/update database tables and configuration
  • *
  • Calls {@link EngineInitializer#initializeEngine()} to start the platform node
  • *
  • Creates an {@link AnnotationConfigWebApplicationContext} with the engine Spring context as parent
  • *
  • Registers the web context in servlet context for access by REST controllers and filters
  • *
  • Handles graceful shutdown via {@link EngineInitializer#unloadEngine()}
  • *
  • Supports update-only mode via {@code bonita.runtime.startup.update-only} property
  • *
*

* The resulting Spring context hierarchy is: * *

 * Engine Context (parent) → contains all engine services (100+ beans)
 *     └── Web Context (child) → contains REST API controllers, filters, web config
 * 
* * @see EngineInitializer * @see PlatformSetup * @see ServiceAccessorFactory */ public class EngineInitializerListener implements ServletContextListener { static final String UPDATE_ONLY_STARTUP_PROPERTY = "bonita.runtime.startup.update-only"; private static final Logger log = LoggerFactory.getLogger(EngineInitializerListener.class); @Override public void contextInitialized(final ServletContextEvent event) { var engineInitializer = getEngineInitializer(); try { var webApplicationContext = initializeWebApplicationContext(event, engineInitializer); boolean updateOnly = webApplicationContext.getEnvironment().getProperty(UPDATE_ONLY_STARTUP_PROPERTY, Boolean.class, Boolean.FALSE); if (updateOnly) { log.info("'{}' enabled. Shutting down JVM.", UPDATE_ONLY_STARTUP_PROPERTY); engineInitializer.unloadEngine(); exit(0); } } catch (final Throwable e) { try { engineInitializer.unloadEngine(); } catch (Exception ex) { log.warn("Error while unloading the Engine", ex); } log.error("Error occurred while initializing the Engine. Shutting down JVM...", e); exit(1); } } AnnotationConfigWebApplicationContext initializeWebApplicationContext(ServletContextEvent event, EngineInitializer engineInitializer) throws Exception { getPlatformSetup().init(); // init tables and default configuration engineInitializer.initializeEngine(); ApplicationContext engineContext = ServiceAccessorFactory.getInstance() .createServiceAccessor() .getContext(); AnnotationConfigWebApplicationContext webApplicationContext = initializeWebContext(event, engineContext); webApplicationContext.refresh(); return webApplicationContext; } protected PlatformSetup getPlatformSetup() throws NamingException { return PlatformSetupAccessor.getInstance().getPlatformSetup(); } void exit(int code) { System.exit(code); } protected AnnotationConfigWebApplicationContext initializeWebContext(ServletContextEvent event, ApplicationContext engineContext) { AnnotationConfigWebApplicationContext webApplicationContext = new AnnotationConfigWebApplicationContext(); webApplicationContext.setParent(engineContext); webApplicationContext.setServletContext(event.getServletContext()); //required for login configuration beans (Brute force protection, etc.) webApplicationContext.scan("org.bonitasoft.web.server"); //A web application context needs to be referenced in the Servlet context so that servlet and filters beans handled by Spring web can use it event.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, webApplicationContext); return webApplicationContext; } protected EngineInitializer getEngineInitializer() { return new EngineInitializer(); } @Override public void contextDestroyed(final ServletContextEvent event) { try { getEngineInitializer().unloadEngine(); } catch (final Throwable e) { log.error("Error while unloading the Engine", e); } } } ================================================ FILE: bpm/bonita-api/bonita-server-api-http/src/main/java/org/bonitasoft/engine/api/internal/servlet/HttpAPIServlet.java ================================================ /** * 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.api.internal.servlet; import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileUploadException; import org.bonitasoft.engine.properties.BooleanProperty; /** * @author Julien Mege * @author Baptiste Mesta * @author Emmanuel Duchastenier */ public class HttpAPIServlet extends HttpServlet { public static final String PROPERTY_TO_ENABLE_HTTP_API = "http.api"; private static final long serialVersionUID = 4936475894513095747L; private BooleanProperty httpApi; @Override public void init() throws ServletException { httpApi = new BooleanProperty("Http API", PROPERTY_TO_ENABLE_HTTP_API, true); } @Override protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { // Defense-in-depth: reject FORWARD dispatches to /serverAPI/*. // The /serverAPI endpoint is an internal engine HTTP API protected by a web.xml // security-constraint, but security-constraints only apply to REQUEST dispatches // (Servlet 3.0 spec). A path traversal attack could forward requests here from // the /apps/* or /API/* namespaces, bypassing the security-constraint entirely. if (req.getDispatcherType() == javax.servlet.DispatcherType.FORWARD) { resp.sendError(SC_FORBIDDEN); return; } if (!httpApi.isEnabled()) { resp.sendError(SC_FORBIDDEN); return; } callHttpApi(req, resp); } void callHttpApi(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { try { new HttpAPIServletCall(req, resp).doPost(); } catch (final FileUploadException e) { throw new ServletException(e); } } } ================================================ FILE: bpm/bonita-api/bonita-server-api-http/src/main/java/org/bonitasoft/engine/api/internal/servlet/HttpAPIServletCall.java ================================================ /** * 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.api.internal.servlet; import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.io.IOException; import java.io.InvalidClassException; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Stream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.thoughtworks.xstream.security.ForbiddenClassException; import org.apache.commons.fileupload.FileUploadException; import org.bonitasoft.engine.api.impl.ServerAPIFactory; import org.bonitasoft.engine.api.internal.ServerAPI; import org.bonitasoft.engine.api.internal.ServerWrappedException; import org.bonitasoft.engine.api.internal.servlet.impl.XmlConverter; import org.bonitasoft.engine.exception.StackTraceTransformer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Julien Mege * @author Matthieu Chaffotte */ public class HttpAPIServletCall extends ServletCall { private static final Logger LOGGER = LoggerFactory.getLogger(HttpAPIServletCall.class); // JEP 290 filter rejection message — part of JDK internal format since JDK 9 // (see java.io.ObjectInputStream); may need updating if a future JDK changes this wording. private static final String JEP290_FILTER_REJECTED_MSG = "filter status: REJECTED"; private static final String SLASH = "/"; private static final String ARRAY = "[]"; private static final String NULL = "null"; private static final String BYTE_ARRAY = "==ByteArray=="; private static final String CLASS_NAME_PARAMETERS = "classNameParameters"; private static final String PARAMETERS_VALUES = "parametersValues"; private static final String OPTIONS = "options"; private XmlConverter xmlConverter; public HttpAPIServletCall(final HttpServletRequest request, final HttpServletResponse response) throws FileUploadException, IOException { super(request, response); xmlConverter = new XmlConverter(); } @Override protected String getResponseContentType() { return "application/xml;charset=UTF-8"; } @Override public void doGet() { error("GET method forbidden", HttpServletResponse.SC_FORBIDDEN); } @Override public void doPost() { try { String apiInterfaceName = null; String methodName = null; final String[] pathParams = getRequestURL().split(SLASH); if (pathParams != null && pathParams.length >= 2) { apiInterfaceName = pathParams[pathParams.length - 2]; methodName = pathParams[pathParams.length - 1]; } final String options = this.getParameter(OPTIONS); final String parametersValues = this.getParameter(PARAMETERS_VALUES); final String parametersClasses = this.getParameter(CLASS_NAME_PARAMETERS); Map myOptions = new HashMap<>(); if (isNotBlank(options)) { myOptions = xmlConverter.fromXML(options); } List myClassNameParameters = new ArrayList<>(); if (parametersClasses != null && !parametersClasses.isEmpty() && !parametersClasses.equals(ARRAY)) { myClassNameParameters = xmlConverter.fromXML(parametersClasses); } Object[] myParametersValues = new Object[0]; if (parametersValues != null && !parametersValues.isEmpty() && !parametersValues.equals(NULL)) { myParametersValues = xmlConverter.fromXML(parametersValues); if (myParametersValues != null && !(myParametersValues.length == 0)) { final Iterator binaryParameters = getBinaryParameters().iterator(); for (int i = 0; i < myParametersValues.length; i++) { final Object parameter = myParametersValues[i]; if (BYTE_ARRAY.equals(parameter)) { myParametersValues[i] = deserialize(binaryParameters.next()); } } } } final Object invokeMethod; try { invokeMethod = getServerAPI().invokeMethod(myOptions, apiInterfaceName, methodName, myClassNameParameters, myParametersValues); } catch (ServerWrappedException e) { // merge stack trace of the server exception throw StackTraceTransformer.mergeStackTraces(e); } String invokeMethodSerialized = null; if (invokeMethod != null) { invokeMethodSerialized = xmlConverter.toXML(invokeMethod); } // add charset avoid encoding problems output(invokeMethodSerialized); } catch (final Exception e) { error(toResponse(e), HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } // Visible for testing ServerAPI getServerAPI() { return ServerAPIFactory.getServerAPI(); } @Override public void doPut() { error("PUT method forbidden", HttpServletResponse.SC_FORBIDDEN); } @Override public void doDelete() { error("DELETE method forbidden", HttpServletResponse.SC_FORBIDDEN); } private String toResponse(final Exception exception) { Throwable result = exception; if (exception instanceof ServerWrappedException) { result = exception.getCause(); } if (isDeserializationSecurityException(result)) { LOGGER.warn("Blocked suspected deserialization attack on {}", getRequestURL(), result); // Serialize a plain String rather than a BonitaRuntimeException to avoid // XStream's ThrowableConverter hitting Java module access restrictions // when walking exception class hierarchies (e.g. IOException.serialVersionUID). return xmlConverter.toXML("Invalid request"); } return xmlConverter.toXML(result); } private boolean isDeserializationSecurityException(Throwable t) { // Check message to distinguish JEP 290 filter rejections from normal // InvalidClassException causes (e.g. serialVersionUID mismatch) return Stream.iterate(t, Objects::nonNull, Throwable::getCause) .anyMatch(cause -> cause instanceof ForbiddenClassException || (cause instanceof InvalidClassException && cause.getMessage() != null && cause.getMessage().contains(JEP290_FILTER_REJECTED_MSG))); } } ================================================ FILE: bpm/bonita-api/bonita-server-api-http/src/main/java/org/bonitasoft/engine/api/internal/servlet/ServletCall.java ================================================ /** * 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.api.internal.servlet; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputFilter; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.io.IOUtils; /** * @author Severin Moussel */ public abstract class ServletCall { private static final String BINARY_PARAMETER = "binaryParameter"; /** * JEP 290 deserialization filter. * The binary parameter path (==ByteArray== multipart attachments) deserializes objects with * ObjectInputStream. Without a filter, an attacker can send crafted payloads containing * gadget-chain classes (URLDNS, commons-collections4, JNDI, etc.) to achieve SSRF or RCE. * This allowlist restricts deserialization to the only types legitimately sent through this path: * - org.bonitasoft.** / com.bonitasoft.** : BusinessArchive and its full object graph * - java.lang.* : String, Long, Integer, etc. (not subpackages like java.lang.reflect) * - java.util.** : HashMap, ArrayList, Collections inner classes * - java.math.* : BigDecimal, BigInteger (process variables) * - !* : reject everything else */ private static final ObjectInputFilter DESERIALIZATION_FILTER = ObjectInputFilter.Config .createFilter( "org.bonitasoft.**;com.bonitasoft.**;java.lang.*;java.util.**;java.math.*;!*"); private String inputStream = null; /** * The parameters of the URL.
* Result of the parsing of the query string : "?a=b&c=d&..." */ protected Map parameters = null; /** * The request made to access this servletCall. */ private final HttpServletRequest request; /** * The response to return. */ private final HttpServletResponse response; private final List binaryParameters; /** * Default constructor. * * @param request * The request made to access this servletCall. * @param response * The response to return. * @throws IOException * @throws FileUploadException */ protected ServletCall(final HttpServletRequest request, final HttpServletResponse response) throws FileUploadException, IOException { super(); this.request = request; this.response = response; parameters = new HashMap<>(); binaryParameters = new ArrayList<>(); if (ServletFileUpload.isMultipartContent(request)) { final ServletFileUpload upload = new ServletFileUpload(); // Parse the request final FileItemIterator iter = upload.getItemIterator(request); while (iter.hasNext()) { try { final FileItemStream item = iter.next(); try (var stream = item.openStream()) { String fieldName = item.getFieldName(); if (fieldName.startsWith(BINARY_PARAMETER)) { binaryParameters.add(stream.readAllBytes()); } else { parameters.put(fieldName, new String(stream.readAllBytes(), StandardCharsets.UTF_8)); } } } catch (final Exception t) { throw new IOException(t); } } } else { final Map parameterMap = this.request.getParameterMap(); final Set> entrySet = parameterMap.entrySet(); for (final Entry entry : entrySet) { parameters.put(entry.getKey(), entry.getValue()[0]); } } } /** * @return the binaryParameters */ public List getBinaryParameters() { return binaryParameters; } public byte[] serialize(final Object obj) throws IOException { final ByteArrayOutputStream b = new ByteArrayOutputStream(); try (final ObjectOutputStream o = new ObjectOutputStream(b)) { o.writeObject(obj); } return b.toByteArray(); } public Object deserialize(final byte[] bytes) throws IOException, ClassNotFoundException { final ByteArrayInputStream b = new ByteArrayInputStream(bytes); try (final ObjectInputStream o = new ObjectInputStream(b)) { o.setObjectInputFilter(DESERIALIZATION_FILTER); return o.readObject(); } } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // PARAMETERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Get the current call's HttpSession * * @return This method returns the session from the current call. */ public HttpSession getHttpSession() { return request.getSession(); } /** * @see javax.servlet.http.HttpServletRequest#getQueryString() */ public String getQueryString() { return request.getQueryString(); } /** * Reconstruct the URL the client used to make the request. * The returned URL contains a protocol, server name, port * number, and server path, but it does not include query * string parameters. * * @return This method returns the reconstructed URL */ public final String getRequestURL() { return request.getRequestURL().toString(); } /** * Read the input stream and set it in a String */ public final String getInputStream() { if (inputStream == null) { try { final BufferedReader reader = request.getReader(); final StringBuilder sb = new StringBuilder(); String line = reader.readLine(); while (line != null) { sb.append(line + "\n"); line = reader.readLine(); } reader.close(); inputStream = sb.toString(); } catch (final IOException e) { throw new RuntimeException("Can't read input Stream.", e); } } return inputStream; } /** * Count the number of parameters passed in the URL * * @return This method returns the number of parameters in the URL */ public final int countParameters() { return parameters.size(); } /** * Get a parameter values by its name * * @param name * The name of the parameter (case sensitive) * @return This method returns the values of a parameter as a list of String or null if the parameter isn't defined */ public final List getParameterAsList(final String name) { return getParameterAsList(name, (String) null); } /** * Get a parameter values by its name * * @param name * The name of the parameter (case sensitive) * @param defaultValue * The value to return if the parameter isn't define * @return This method returns the values of a parameter as a list of String */ public final List getParameterAsList(final String name, final String defaultValue) { if (parameters.containsKey(name)) { return Arrays.asList(parameters.get(name)); } if (defaultValue != null) { final List results = new ArrayList(); results.add(defaultValue); return results; } return null; } /** * Get a parameter first value by its name * * @param name * The name of the parameter (case sensitive) * @return This method returns the first value of a parameter as a String or null if the parameter isn't define */ public final String getParameter(final String name) { return getParameter(name, (String) null); } /** * Get a parameter first value by its name * * @param name * The name of the parameter (case sensitive) * @param defaultValue * The value to return if the parameter isn't define * @return This method returns the first value of a parameter as a String */ public final String getParameter(final String name, final String defaultValue) { if (parameters.containsKey(name)) { return parameters.get(name); } return defaultValue; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GENERATE RESPONSES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Write into the output header. * * @param name * The name of the header to write. * @param value * The value of the header to write. */ protected final void head(final String name, final String value) { response.addHeader(name, value); } /** * Output a file * * @param file * The file to output */ protected final void output(final File file) { try { output(new FileInputStream(file)); } catch (final FileNotFoundException e) { error("Can not download file.", HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } /** * Output a stream as a file * * @param stream * The stream to output * @param filename * The name of the file to retrieve with the stream. */ protected void output(final InputStream stream, final String filename) { response.addHeader("Content-Disposition", "attachment; filename=" + filename + ";"); output(stream); } /** * Output a stream as a file * * @param stream * The stream to output */ protected void output(final InputStream stream) { response.setContentType("application/octet-stream"); try { IOUtils.copy(stream, response.getOutputStream()); } catch (final IOException e) { error("Can not download stream.", HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } protected void error(final String message, final int errorCode) { output(message); response.setStatus(errorCode); } /** * Write into the output * * @param string * The string to output */ protected final void output(final String string) { final PrintWriter outputWriter = getOutputWriter(); // Skip the write on null rather than letting PrintWriter.print(null) emit the literal "null": // wrapping response writers that track content length (e.g. Spring Session's // SaveContextPrintWriter via OnCommittedResponseWrapper.trackContentLength) NPE on that path (BPA-443) if (string != null) { outputWriter.print(string); } outputWriter.flush(); outputWriter.close(); } /** * Write into the output * * @param object * An object that will be transform into JSon */ protected final void output(final Object object) { final PrintWriter outputWriter = getOutputWriter(); // FIXME use xstream // Skip on null: object.toString() would NPE. Symmetric with output(String) if (object != null) { outputWriter.print(object.toString()); } outputWriter.flush(); outputWriter.close(); } /** * The outputWriter in which to write the response String. */ private PrintWriter outputWriter = null; private PrintWriter getOutputWriter() { if (outputWriter == null) { response.setContentType(getResponseContentType()); try { outputWriter = response.getWriter(); } catch (final IOException e) { throw new RuntimeException(e); } } return outputWriter; } protected String getResponseContentType() { return "application/json;charset=UTF-8"; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // REQUEST ENTRY POINTS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Entry point for GET and SEARCH */ public abstract void doGet(); /** * Entry point for CREATE */ public abstract void doPost(); /** * Entry point for UPDATE */ public abstract void doPut(); /** * Entry point for DELETE */ public abstract void doDelete(); } ================================================ FILE: bpm/bonita-api/bonita-server-api-http/src/main/java/org/bonitasoft/engine/api/internal/servlet/impl/XmlConverter.java ================================================ /** * 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.api.internal.servlet.impl; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.StringReader; import java.io.StringWriter; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.security.AnyTypePermission; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.xml.XStreamDenyList; // TODO duplicated with the implementation in org.bonitasoft.engine.api.impl // The only difference is the settings of Xstream // TODO xstream instance should also ignored unknowns elements as the client to avoid issue in the future (fill a task) public class XmlConverter { private static volatile XStream xstream; private static XStream getXStream() { if (xstream == null) { synchronized (XmlConverter.class) { if (xstream == null) { xstream = createXStream(); } } } return xstream; } private static XStream createXStream() { var xs = new XStream(); xs.addPermission(AnyTypePermission.ANY); // Block known deserialization gadget chain libraries to prevent RCE. // A strict allowlist is not possible here because API parameters can contain BDM types with arbitrary packages. xs.denyTypesByWildcard(XStreamDenyList.getDenyPatterns()); // ignore fields suppressedExceptions causing exceptions in some cases xs.omitField(Throwable.class, "suppressedExceptions"); return xs; } // Package-private for testing static void reset() { xstream = null; } public String toXML(final Object object) { final StringWriter stringWriter = new StringWriter(); try (final ObjectOutputStream out = getXStream().createObjectOutputStream(stringWriter)) { out.writeObject(object); } catch (IOException e) { throw new BonitaRuntimeException("Unable to serialize object " + object, e); } return stringWriter.toString(); } @SuppressWarnings("unchecked") public T fromXML(final String object) { try (final StringReader xmlReader = new StringReader(object); final ObjectInputStream in = getXStream().createObjectInputStream(xmlReader)) { return (T) in.readObject(); } catch (final ClassNotFoundException | IOException | RuntimeException e) { // Do not include the XML payload in the error message to prevent // information disclosure of attacker-controlled input throw new BonitaRuntimeException("Unable to deserialize object", e); } } } ================================================ FILE: bpm/bonita-api/bonita-server-api-http/src/test/java/org/bonitasoft/engine/api/internal/servlet/EngineInitializerListenerTest.java ================================================ /** * Copyright (C) 2023 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.api.internal.servlet; import static org.mockito.Mockito.*; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import org.bonitasoft.engine.EngineInitializer; 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.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import org.springframework.mock.env.MockEnvironment; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class EngineInitializerListenerTest { @Spy private EngineInitializerListener listener; @Mock private AnnotationConfigWebApplicationContext context; @Mock private EngineInitializer engineInitializer; private MockEnvironment env; @BeforeEach void setUp() throws Exception { doReturn(engineInitializer).when(listener).getEngineInitializer(); env = new MockEnvironment(); when(context.getEnvironment()).thenReturn(env); doReturn(context).when(listener).initializeWebApplicationContext(any(), any()); doNothing().when(listener).exit(anyInt()); } @Test void doNotExit() { listener.contextInitialized(new ServletContextEvent(mock(ServletContext.class))); verify(listener, never()).exit(anyInt()); } @Test void exitNormally() { env.setProperty(EngineInitializerListener.UPDATE_ONLY_STARTUP_PROPERTY, Boolean.TRUE.toString()); listener.contextInitialized(new ServletContextEvent(mock(ServletContext.class))); verify(listener).exit(0); } @Test void exitOnError() throws Exception { doThrow(IllegalStateException.class).when(listener).initializeWebApplicationContext(any(), any()); listener.contextInitialized(new ServletContextEvent(mock(ServletContext.class))); verify(listener).exit(1); } } ================================================ FILE: bpm/bonita-api/bonita-server-api-http/src/test/java/org/bonitasoft/engine/api/internal/servlet/HttpAPIServletCallTest.java ================================================ /** * 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.api.internal.servlet; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; import java.io.InvalidClassException; import java.lang.reflect.InaccessibleObjectException; import java.time.Instant; import java.util.Date; import java.util.HashMap; import com.thoughtworks.xstream.security.ForbiddenClassException; import org.bonitasoft.engine.api.internal.ServerAPI; import org.bonitasoft.engine.api.internal.ServerWrappedException; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.session.impl.APISessionImpl; import org.junit.Rule; import org.junit.Test; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletContext; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; public class HttpAPIServletCallTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Test public void should_manage_login_request() throws Exception { //given: MockHttpServletRequest request = MockMvcRequestBuilders .post("http://localhost/serverAPI/com.bonitasoft.engine.api.LoginAPI/login") .param("options", "" + " " + "") .param("classNameParameters", "" + " " + " " + " java.lang.String" + " java.lang.String" + " " + " " + "") .param("parametersValues", "" + " " + " install" + " install" + " " + "") .buildRequest(new MockServletContext()); MockHttpServletResponse response = new MockHttpServletResponse(); HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response)); ServerAPI serverAPI = mock(ServerAPI.class); doReturn(serverAPI).when(httpAPIServletCall).getServerAPI(); APISessionImpl apiSession = new APISessionImpl(-2241174137745053814L, date("2018-06-07T15:10:09.132Z"), 3600000, "install", -1); apiSession.setTechnicalUser(true); when(serverAPI.invokeMethod(new HashMap<>(), "com.bonitasoft.engine.api.LoginAPI", "login", asList(String.class.getName(), String.class.getName()) // , new Object[] { "install", "install" })) // .thenReturn(apiSession); //when: httpAPIServletCall.doPost(); //then: assertThat(response.getStatus()).as("Response status").isEqualTo(200); assertThat(response.getContentType()).as("Response content type").isEqualTo("application/xml;charset=UTF-8"); assertThat(response.getContentAsString()).as("Response content").isEqualToIgnoringWhitespace("" + " " + " -2241174137745053814" + " 2018-06-07 15:10:09.132 UTC" + " 3600000" + " install" + " -1" + " true" + " " + ""); } @Test public void should_sanitize_error_response_when_xstream_deny_list_blocks_type() throws Exception { // given: a ForbiddenClassException from XStream deny list (simulated via mock to avoid // Java module access issues with XStream's FieldDictionary on Java 17+) MockHttpServletRequest request = MockMvcRequestBuilders .post("http://localhost/serverAPI/org.bonitasoft.engine.api.LoginAPI/login") .param("options", "") .buildRequest(new MockServletContext()); MockHttpServletResponse response = new MockHttpServletResponse(); HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response)); ServerAPI serverAPI = mock(ServerAPI.class); doReturn(serverAPI).when(httpAPIServletCall).getServerAPI(); ForbiddenClassException forbidden = new ForbiddenClassException(java.net.URL.class); when(serverAPI.invokeMethod(any(), any(), any(), any(), any())) .thenThrow(new BonitaRuntimeException("Unable to deserialize object", forbidden)); // when: httpAPIServletCall.doPost(); // then: response should contain a generic error, not security mechanism details // Note: status code assertion omitted — ServletCall.error() flushes the output before // calling setStatus(), so MockHttpServletResponse commits with 200 before the 500 is set. // In production, the real servlet container buffers the response so 500 is effective. String content = response.getContentAsString(); assertThat(content).contains("Invalid request"); assertThat(content).doesNotContain("ForbiddenClassException"); assertThat(content).doesNotContain("java.net.URL"); // NoPermission is the XStream security error type name — must not leak to clients assertThat(content).doesNotContain("NoPermission"); } @Test public void should_return_full_error_response_for_non_security_exceptions() throws Exception { // given: a request that triggers a non-security exception from the server API MockHttpServletRequest request = MockMvcRequestBuilders .post("http://localhost/serverAPI/org.bonitasoft.engine.api.LoginAPI/login") .param("options", "") .buildRequest(new MockServletContext()); MockHttpServletResponse response = new MockHttpServletResponse(); HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response)); ServerAPI serverAPI = mock(ServerAPI.class); doReturn(serverAPI).when(httpAPIServletCall).getServerAPI(); when(serverAPI.invokeMethod(any(), any(), any(), any(), any())) .thenThrow(new BonitaRuntimeException("Something went wrong")); // when: httpAPIServletCall.doPost(); // then: response should contain the real exception, not the sanitized "Invalid request" String content = response.getContentAsString(); assertThat(content).contains("BonitaRuntimeException"); assertThat(content).contains("Something went wrong"); assertThat(content).doesNotContain("Invalid request"); } @Test public void should_sanitize_error_response_when_jep290_filter_rejects_class() throws Exception { // given: a request where the JEP 290 ObjectInputFilter rejects a class during deserialization MockHttpServletRequest request = MockMvcRequestBuilders .post("http://localhost/serverAPI/org.bonitasoft.engine.api.LoginAPI/login") .param("options", "") .buildRequest(new MockServletContext()); MockHttpServletResponse response = new MockHttpServletResponse(); HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response)); ServerAPI serverAPI = mock(ServerAPI.class); doReturn(serverAPI).when(httpAPIServletCall).getServerAPI(); InvalidClassException ice = new InvalidClassException("com.evil.Payload", "filter status: REJECTED"); when(serverAPI.invokeMethod(any(), any(), any(), any(), any())) .thenThrow(new RuntimeException("deserialization failed", ice)); // when: httpAPIServletCall.doPost(); // then: response should be sanitized String content = response.getContentAsString(); assertThat(content).contains("Invalid request"); assertThat(content).doesNotContain("com.evil.Payload"); assertThat(content).doesNotContain("filter status: REJECTED"); } @Test public void should_sanitize_error_response_when_security_exception_is_deeply_nested() throws Exception { // given: ForbiddenClassException wrapped two levels deep in the cause chain MockHttpServletRequest request = MockMvcRequestBuilders .post("http://localhost/serverAPI/org.bonitasoft.engine.api.LoginAPI/login") .param("options", "") .buildRequest(new MockServletContext()); MockHttpServletResponse response = new MockHttpServletResponse(); HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response)); ServerAPI serverAPI = mock(ServerAPI.class); doReturn(serverAPI).when(httpAPIServletCall).getServerAPI(); ForbiddenClassException forbidden = new ForbiddenClassException(java.net.URL.class); RuntimeException nested = new RuntimeException("conversion failed", new RuntimeException("inner wrapper", forbidden)); when(serverAPI.invokeMethod(any(), any(), any(), any(), any())).thenThrow(nested); // when: httpAPIServletCall.doPost(); // then: response should be sanitized despite the deep nesting String content = response.getContentAsString(); assertThat(content).contains("Invalid request"); assertThat(content).doesNotContain("ForbiddenClassException"); assertThat(content).doesNotContain("java.net.URL"); } @Test public void should_sanitize_error_response_when_security_exception_wrapped_in_ServerWrappedException() throws Exception { // given: a ForbiddenClassException wrapped in ServerWrappedException (production wire-transport path) MockHttpServletRequest request = MockMvcRequestBuilders .post("http://localhost/serverAPI/org.bonitasoft.engine.api.LoginAPI/login") .param("options", "") .buildRequest(new MockServletContext()); MockHttpServletResponse response = new MockHttpServletResponse(); HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response)); ServerAPI serverAPI = mock(ServerAPI.class); doReturn(serverAPI).when(httpAPIServletCall).getServerAPI(); ForbiddenClassException forbidden = new ForbiddenClassException(java.net.URL.class); when(serverAPI.invokeMethod(any(), any(), any(), any(), any())) .thenThrow(new ServerWrappedException(forbidden)); // when: httpAPIServletCall.doPost(); // then: response should be sanitized despite ServerWrappedException wrapping String content = response.getContentAsString(); assertThat(content).contains("Invalid request"); assertThat(content).doesNotContain("ForbiddenClassException"); assertThat(content).doesNotContain("java.net.URL"); } @Test public void should_not_sanitize_error_response_for_non_jep290_InvalidClassException() throws Exception { // given: an InvalidClassException caused by serialVersionUID mismatch (not a security rejection) MockHttpServletRequest request = MockMvcRequestBuilders .post("http://localhost/serverAPI/org.bonitasoft.engine.api.LoginAPI/login") .param("options", "") .buildRequest(new MockServletContext()); MockHttpServletResponse response = new MockHttpServletResponse(); HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response)); ServerAPI serverAPI = mock(ServerAPI.class); doReturn(serverAPI).when(httpAPIServletCall).getServerAPI(); InvalidClassException ice = new InvalidClassException("com.example.MyClass", "local class incompatible: stream classdesc serialVersionUID = 123, local class serialVersionUID = 456"); when(serverAPI.invokeMethod(any(), any(), any(), any(), any())) .thenThrow(new RuntimeException("deserialization failed", ice)); // when: try { httpAPIServletCall.doPost(); } catch (InaccessibleObjectException e) { // On Java 17+, XStream fails to serialize IOException-derived exceptions // due to module access restrictions (same issue documented in toResponse()). // This is expected and not what we're testing. } // then: response should NOT contain the sanitized "Invalid request" message, // proving isDeserializationSecurityException correctly returned false String content = response.getContentAsString(); assertThat(content).doesNotContain("Invalid request"); } @Test public void should_write_empty_body_when_output_object_is_null() throws Exception { // BPA-443 defence-in-depth: output(Object) is the sibling of output(String) and // carries the same null hazard — object.toString() NPEs if object is null. Same guard. // given: MockHttpServletRequest request = MockMvcRequestBuilders .post("http://localhost/serverAPI/org.bonitasoft.engine.api.LoginAPI/logout") .buildRequest(new MockServletContext()); MockHttpServletResponse response = new MockHttpServletResponse(); HttpAPIServletCall httpAPIServletCall = new HttpAPIServletCall(request, response); // when: httpAPIServletCall.output((Object) null); // then: assertThat(response.getContentAsString()) .as("Null Object must produce empty body, not NPE or literal \"null\"") .isEmpty(); } @Test public void should_write_empty_body_when_server_api_returns_null_for_void_method() throws Exception { // BPA-443: when a void API method is invoked (e.g. logout, retryTask), the server-side // invokeMethod returns null. Writing "null" via PrintWriter.print(null) yields an NPE // inside response wrappers that track content length (e.g. Spring Session's // SaveContextPrintWriter). Guard against this at the source: null → empty body. // given: MockHttpServletRequest request = MockMvcRequestBuilders .post("http://localhost/serverAPI/com.bonitasoft.engine.api.LoginAPI/logout") .param("options", "") .buildRequest(new MockServletContext()); MockHttpServletResponse response = new MockHttpServletResponse(); HttpAPIServletCall httpAPIServletCall = spy(new HttpAPIServletCall(request, response)); ServerAPI serverAPI = mock(ServerAPI.class); doReturn(serverAPI).when(httpAPIServletCall).getServerAPI(); when(serverAPI.invokeMethod(any(), any(), any(), any(), any())).thenReturn(null); // when: httpAPIServletCall.doPost(); // then: assertThat(response.getStatus()).as("Response status").isEqualTo(200); assertThat(response.getContentAsString()) .as("Response body for void method must be empty, not the literal string \"null\"") .isEmpty(); } // ================================================================================================================= // UTILS // ================================================================================================================= private static Date date(String date) { return Date.from(Instant.parse(date)); } } ================================================ FILE: bpm/bonita-api/bonita-server-api-http/src/test/java/org/bonitasoft/engine/api/internal/servlet/HttpAPIServletTest.java ================================================ /** * Copyright (C) 2021 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.api.internal.servlet; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.EnvironmentVariables; import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; public class HttpAPIServletTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Mock HttpServletRequest request; @Mock HttpServletResponse response; @Rule public EnvironmentVariables envVar = new EnvironmentVariables(); @Rule public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); @Test public void should_send_403_when_disabled_using_env() throws Exception { HttpAPIServlet httpAPIServlet = spy(new HttpAPIServlet()); envVar.set("HTTP_API", "false"); httpAPIServlet.init(); httpAPIServlet.doPost(request, response); verify(response).sendError(403); } @Test public void should_send_403_when_disabled_using_props() throws Exception { System.setProperty("http.api", "false"); HttpAPIServlet httpAPIServlet = spy(new HttpAPIServlet()); httpAPIServlet.init(); httpAPIServlet.doPost(request, response); verify(response).sendError(403); } @Test public void should_not_send_403_when_enabled_using_props() throws Exception { System.setProperty("http.api", "true"); HttpAPIServlet httpAPIServlet = spy(new HttpAPIServlet()); doNothing().when(httpAPIServlet).callHttpApi(any(), any()); httpAPIServlet.init(); httpAPIServlet.doPost(request, response); verify(response, never()).sendError(anyInt()); } @Test public void should_be_enabled_by_default() throws Exception { HttpAPIServlet httpAPIServlet = spy(new HttpAPIServlet()); doNothing().when(httpAPIServlet).callHttpApi(any(), any()); httpAPIServlet.init(); httpAPIServlet.doPost(request, response); verify(response, never()).sendError(anyInt()); } @Test public void should_send_403_when_dispatch_type_is_forward() throws Exception { HttpAPIServlet httpAPIServlet = spy(new HttpAPIServlet()); doReturn(javax.servlet.DispatcherType.FORWARD).when(request).getDispatcherType(); httpAPIServlet.init(); httpAPIServlet.doPost(request, response); verify(response).sendError(403); verify(httpAPIServlet, never()).callHttpApi(any(), any()); } @Test public void should_allow_request_dispatch_type() throws Exception { HttpAPIServlet httpAPIServlet = spy(new HttpAPIServlet()); doReturn(javax.servlet.DispatcherType.REQUEST).when(request).getDispatcherType(); doNothing().when(httpAPIServlet).callHttpApi(any(), any()); httpAPIServlet.init(); httpAPIServlet.doPost(request, response); verify(response, never()).sendError(anyInt()); } } ================================================ FILE: bpm/bonita-api/bonita-server-api-http/src/test/java/org/bonitasoft/engine/api/internal/servlet/ServletCallDeserializationFilterTest.java ================================================ /** * Copyright (C) 2026 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.api.internal.servlet; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.io.ByteArrayOutputStream; import java.io.InvalidClassException; import java.io.ObjectOutputStream; import java.net.URL; import java.util.HashMap; import org.junit.Before; import org.junit.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; public class ServletCallDeserializationFilterTest { private ServletCall servletCall; @Before public void setUp() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); servletCall = new HttpAPIServletCall(request, response); } @Test public void should_deserialize_byte_array() throws Exception { byte[] original = new byte[] { 1, 2, 3, 4, 5 }; byte[] serialized = serializeObject(original); Object result = servletCall.deserialize(serialized); assertThat(result).isInstanceOf(byte[].class); assertThat((byte[]) result).isEqualTo(original); } @Test public void should_deserialize_java_util_types() throws Exception { HashMap original = new HashMap<>(); original.put("key", "value"); byte[] serialized = serializeObject(original); Object result = servletCall.deserialize(serialized); assertThat(result).isInstanceOf(HashMap.class); assertThat(((HashMap) result).get("key")).isEqualTo("value"); } @Test public void should_deserialize_bonita_engine_types() throws Exception { org.bonitasoft.engine.exception.CreationException original = new org.bonitasoft.engine.exception.CreationException( "test error"); byte[] serialized = serializeObject(original); Object result = servletCall.deserialize(serialized); assertThat(result).isInstanceOf(org.bonitasoft.engine.exception.CreationException.class); assertThat(((Exception) result).getMessage()).isEqualTo("test error"); } @Test public void should_reject_java_net_URL() throws Exception { HashMap malicious = new HashMap<>(); malicious.put("url", new URL("http://example.com")); byte[] serialized = serializeObject(malicious); assertThatThrownBy(() -> servletCall.deserialize(serialized)) .isInstanceOf(InvalidClassException.class) .hasMessageContaining("REJECTED"); } @Test public void should_reject_java_net_InetAddress() throws Exception { java.net.InetAddress malicious = java.net.InetAddress.getByName("127.0.0.1"); byte[] serialized = serializeObject(malicious); assertThatThrownBy(() -> servletCall.deserialize(serialized)) .isInstanceOf(InvalidClassException.class) .hasMessageContaining("REJECTED"); } private static byte[] serializeObject(Object obj) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(bos)) { oos.writeObject(obj); } return bos.toByteArray(); } } ================================================ FILE: bpm/bonita-api/bonita-server-api-http/src/test/java/org/bonitasoft/engine/api/internal/servlet/impl/XmlConverterTest.java ================================================ /** * Copyright (C) 2026 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.api.internal.servlet.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import com.thoughtworks.xstream.security.ForbiddenClassException; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.RestoreSystemProperties; public class XmlConverterTest { @Rule public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); private final XmlConverter xmlConverter = new XmlConverter(); @After public void resetXStream() { XmlConverter.reset(); } @Test public void should_reject_deserialization_of_gadget_chain_types() { // given: XML payload referencing a known gadget chain type String maliciousXml = "" + "" + "" + "" + ""; // when: Throwable thrown = catchThrowable(() -> xmlConverter.fromXML(maliciousXml)); // then: assertThat(thrown) .isInstanceOf(BonitaRuntimeException.class) .hasCauseInstanceOf(ForbiddenClassException.class); } @Test public void should_reject_deserialization_of_javax_script_gadget_chain_types() { // given: XML payload referencing a denied gadget chain type String maliciousXml = "" + "" + ""; // when: Throwable thrown = catchThrowable(() -> xmlConverter.fromXML(maliciousXml)); // then: assertThat(thrown) .isInstanceOf(BonitaRuntimeException.class) .hasCauseInstanceOf(ForbiddenClassException.class); } @Test public void should_allow_deserialization_of_bonita_types() { // given: XML payload with a legitimate Bonita type String xml = "" + "" + "test" + "" + ""; // when: Object result = xmlConverter.fromXML(xml); // then: assertThat(result).isNotNull(); } @Test public void should_reject_deserialization_of_java_net_URL() { // given: XML payload containing a java.net.URL element (URLDNS gadget chain) String maliciousXml = "" + "" + "http" + "attacker.example.com" + "80" + "/" + "" + ""; // when: Throwable thrown = catchThrowable(() -> xmlConverter.fromXML(maliciousXml)); // then: assertThat(thrown) .isInstanceOf(BonitaRuntimeException.class) .hasCauseInstanceOf(ForbiddenClassException.class); } @Test public void should_use_custom_deny_list_from_system_property() { // given: custom deny list via system property XmlConverter.reset(); System.setProperty("bonita.xstream.deny.packages", "java.awt.**"); // when: attempting to deserialize a type matching the custom deny pattern Throwable thrown = catchThrowable(() -> new XmlConverter().fromXML("")); // then: assertThat(thrown) .isInstanceOf(BonitaRuntimeException.class) .hasCauseInstanceOf(ForbiddenClassException.class); } } ================================================ FILE: bpm/bonita-client/build.gradle ================================================ import org.bonitasoft.engine.gradle.PomUtils plugins { id "java-library" id "bonita-tests" } dependencies { api project(':bpm:bonita-common') api libs.httpComponentsClient api libs.xstream api libs.httpComponentsMime testImplementation libs.assertj testImplementation libs.logback testImplementation libs.mockitoCore testImplementation libs.jettyServer testImplementation libs.jettySecurity } tasks.register("testsJar", Jar) { archiveClassifier = 'tests' from(sourceSets.test.output) } tasks.register("sourcesJar", Jar) { from sourceSets.main.allJava archiveClassifier = 'sources' } tasks.register("javadocJar", Jar) { from javadoc archiveClassifier = 'javadoc' } publishing { publications { mavenJava(MavenPublication) { from project.components.java artifact project.sourcesJar artifact project.javadocJar artifact project.testsJar pom { pom -> name = "Bonita Client" description = "Bonita Client is the Jar used to interact with a running Bonita Engine" PomUtils.pomCommunityPublication(pom) } } } } ================================================ FILE: bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/APIClient.java ================================================ /** * 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.api; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; import java.util.Map; import org.bonitasoft.engine.api.impl.ClientInterceptor; import org.bonitasoft.engine.api.impl.LocalServerAPIFactory; import org.bonitasoft.engine.api.internal.ServerAPI; import org.bonitasoft.engine.api.platform.PlatformInformationAPI; import org.bonitasoft.engine.bdm.BusinessObjectDaoCreationException; import org.bonitasoft.engine.bdm.dao.BusinessObjectDAO; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.platform.LoginException; import org.bonitasoft.engine.platform.LogoutException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.SessionNotFoundException; import org.bonitasoft.engine.util.APITypeManager; /** * Bonita Community Edition APIs client.
*

*

    *
  • {@link IdentityAPI},
  • *
  • {@link ProcessAPI},
  • *
  • {@link CommandAPI},
  • *
  • {@link ProfileAPI},
  • *
  • {@link TenantAdministrationAPI},
  • *
  • {@link PageAPI},
  • *
  • {@link ApplicationAPI},
  • *
  • {@link PermissionAPI},
  • *
  • {@link BusinessDataAPI} (deprecated as of 7.3),
  • *
  • {@link MaintenanceAPI},
  • *
* * @author Nicolas Chabanoles */ public class APIClient { private static final String IMPL_SUFFIX = "Impl"; protected APISession session; public APIClient() { session = null; } public APIClient(APISession session) { this.session = session; } public APISession getSession() { return session; } ServerAPI getServerAPI() throws ServerAPIException, UnknownAPITypeException { try { final ApiAccessType apiType = APITypeManager.getAPIType(); Map parameters; return switch (apiType) { case LOCAL -> LocalServerAPIFactory.getServerAPI(); case HTTP -> { parameters = APITypeManager.getAPITypeParameters(); yield new HTTPServerAPI(parameters); } }; } catch (IOException e) { throw new ServerAPIException(e); } } protected T getAPI(final Class apiClass) { ensureSessionExists(); try { final ClientInterceptor clientInterceptor = new ClientInterceptor(apiClass.getName(), getServerAPI(), session); @SuppressWarnings("unchecked") final T api = (T) Proxy.newProxyInstance(APIAccessor.class.getClassLoader(), new Class[] { apiClass }, clientInterceptor); return api; } catch (ServerAPIException | UnknownAPITypeException e) { throw new IllegalStateException(e.getMessage(), e); } } private void ensureSessionExists() { if (session == null) { throw new IllegalStateException("You must call login() prior to accessing any API."); } } protected LoginAPI getLoginAPI() { return getLoginAPI(LoginAPI.class); } /** * This method serves the purpose to remove confusion between getAPI() when a session is mandatory, and this one, * where no session is needed to access the * API class. * * @param apiClass the API to retrieve * @param The type of the API, extending {@link org.bonitasoft.engine.api.LoginAPI} * @return the retrieved API * @throws IllegalStateException if the API cannot be retrieved. */ protected T getLoginAPI(Class apiClass) { try { final ClientInterceptor interceptor = new ClientInterceptor(apiClass.getName(), getServerAPI()); @SuppressWarnings("unchecked") final T api = (T) Proxy.newProxyInstance(APIAccessor.class.getClassLoader(), new Class[] { apiClass }, interceptor); return api; } catch (ServerAPIException | UnknownAPITypeException e) { throw new IllegalStateException(e.getMessage(), e); } } /** * Connects a user, identified by his (her) username and password, in order to use API methods of a tenant. * * @param username the user name * @param password the password * @throws LoginException occurs when an exception is thrown during the login (userName does not exist, or couple * (userName, * password) is incorrect) * @since 7.2 */ public void login(String username, String password) throws LoginException { session = getLoginAPI(LoginAPI.class).login(username, password); } /** * Connects a user, identified by credentials, in order to use API methods of a tenant. * * @param credentials the credentials to login with. Can be username / password couple, SSO ticket, ... depending on * the implementation.
* By default possible map keys are: *
    *
  • Basic Authentication: authentication.username and authentication.password
  • *
  • CAS Authentication: ticket and service
  • *
  • Please refer to specific documentation regarding the Authentication Service in use, to know what the * credentials must contain.
  • *
* @throws LoginException occurs when an exception is thrown during the login (userName does not exist, or couple * (userName, * password) is incorrect) * @since 10.3 */ public void login(final Map credentials) throws LoginException { session = getLoginAPI(LoginAPI.class).login(credentials); } /** * Disconnect user from tenant APIs. * * @since 7.2 */ public void logout() throws LogoutException { try { if (session != null) { getLoginAPI().logout(session); session = null; } } catch (SessionNotFoundException ignored) { // If the session is not found on server, then the client is already logged out. // Do nothing } } /** * Get an implementation instance of the DAO Interface. * * @param daoInterface the interface of the DAO * @return the implementation of the DAO * @throws BusinessObjectDaoCreationException if the factory is not able to instantiate the DAO */ public T getDAO(final Class daoInterface) throws BusinessObjectDaoCreationException { ensureSessionExists(); if (daoInterface == null) { throw new IllegalArgumentException("daoInterface is null"); } if (!daoInterface.isInterface()) { throw new IllegalArgumentException(daoInterface.getName() + " is not an interface"); } Class daoImplClass; try { daoImplClass = loadClass(daoInterface); if (daoImplClass != null) { final Constructor constructor = daoImplClass.getConstructor(APISession.class); return constructor.newInstance(session); } } catch (final ClassNotFoundException | SecurityException | NoSuchMethodException | IllegalArgumentException | InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new BusinessObjectDaoCreationException(e); } throw new BusinessObjectDaoCreationException("No Implementation of the DAO available."); } /** * Loads the class of the {@link BusinessObjectDAO} according to its class name. *

* The loading is done in the current Thread ClassLoader. * * @param daoInterface the DAO's interface * @return the Implementation class of the BusinessObjectDAO * @throws ClassNotFoundException if the implementation class name is unknown by the current Thread ClassLoader */ @SuppressWarnings("unchecked") protected Class loadClass(final Class daoInterface) throws ClassNotFoundException { final String implementationClassName = daoInterface.getName() + IMPL_SUFFIX; return (Class) Class.forName(implementationClassName, true, Thread.currentThread().getContextClassLoader()); } /** * Get API to manage the organization, i.e., users, groups and roles. * * @since 7.2 */ public IdentityAPI getIdentityAPI() { return getAPI(IdentityAPI.class); } /** * Get API to manage the business processes. * * @since 7.2 */ public ProcessAPI getProcessAPI() { return getAPI(ProcessAPI.class); } /** * Get API to manage commands and Tenant level dependencies. * * @since 7.2 */ public CommandAPI getCommandAPI() { return getAPI(CommandAPI.class); } /** * Get API to manage portal user profiles. * * @since 7.2 */ public ProfileAPI getProfileAPI() { return getAPI(ProfileAPI.class); } /** * Get API to manage the tenant your are logged on. * * @since 7.2 */ public TenantAdministrationAPI getTenantAdministrationAPI() { return getAPI(TenantAdministrationAPI.class); } /** * Get API to manage portal pages. * * @since 7.2 */ public PageAPI getCustomPageAPI() { return getAPI(PageAPI.class); } /** * Get API to manage Living Applications. * * @since 7.2 */ public ApplicationAPI getLivingApplicationAPI() { return getAPI(ApplicationAPI.class); } /** * Get API to dynamically check REST API call access right. * * @since 7.2 */ public PermissionAPI getPermissionAPI() { return getAPI(PermissionAPI.class); } /** * Get API to access Business Data related to processes. * * @since 7.2 * @deprecated as of 7.3, see {@link BusinessDataAPI} for replacements */ @Deprecated(since = "7.3") public BusinessDataAPI getBusinessDataAPI() { return getAPI(BusinessDataAPI.class); } /** * Get API to manage Bonita Applications. * * @since 7.10 */ public ApplicationAPI getApplicationAPI() { return getAPI(ApplicationAPI.class); } /** * Get API to store and retrieve temporary content like uploaded files. * For internal usage only. * * @since 9.0 */ @Internal public TemporaryContentAPI getTemporaryContentAPI() { return getAPI(TemporaryContentAPI.class); } /** * Get API to manage Platform maintenance. * * @since 9.0 */ public MaintenanceAPI getMaintenanceAPI() { return getAPI(MaintenanceAPI.class); } /** * Get API that retrieves information on the platform: basic information in a Community edition, * More detailed information on license in a Subscription edition. * * @since 10.2.0 */ public PlatformInformationAPI getPlatformInformationAPI() { return getAPI(PlatformInformationAPI.class); } } ================================================ FILE: bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/ApiAccessType.java ================================================ /** * 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.api; /** * Enum that defines how APIs should be accessed */ public enum ApiAccessType { /** * Local access to the api, it means that the local JVM is running the engine. */ LOCAL, /** * Access a remote engine using through a servlet. The remote engine should have the HTTP_API env property set to * true. */ HTTP } ================================================ FILE: bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/BonitaStackTraceElementConverter.java ================================================ /** * 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.api; import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.extended.StackTraceElementConverter; import com.thoughtworks.xstream.converters.extended.StackTraceElementFactory; /** * Override default stack trace element parser to avoid having big exception when there is parsing issues * * @author Baptiste Mesta */ public class BonitaStackTraceElementConverter extends StackTraceElementConverter { private static final StackTraceElementFactory FACTORY = new StackTraceElementFactory(); @Override public Object fromString(final String str) { try { return super.fromString(str); } catch (ConversionException e) { return FACTORY.element(str, " ", " ", -3); } } } ================================================ FILE: bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/HTTPServerAPI.java ================================================ /** * 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.api; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.UndeclaredThrowableException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.HttpResponseException; import org.apache.http.client.ResponseHandler; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.entity.mime.content.ByteArrayBody; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.client.BasicResponseHandler; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; import org.bonitasoft.engine.api.impl.XmlConverter; import org.bonitasoft.engine.api.internal.ServerAPI; import org.bonitasoft.engine.api.internal.ServerWrappedException; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.digest.DigestUtils; import org.bonitasoft.engine.exception.StackTraceTransformer; /** * Call the remote engine using HTTP post * That class serialize api call parameters in a XML body and post it to the serverAPI servlet like this * /serverAPI/[api interface name]/[method name] */ public class HTTPServerAPI implements ServerAPI { private static final long serialVersionUID = -3375874140999200702L; private static final ContentType XML_UTF_8 = ContentType.create("application/xml", UTF_8); private static final String CLASS_NAME_PARAMETERS = "classNameParameters"; private static final String OPTIONS = "options"; private static final String PARAMETERS_VALUES = "parametersValues"; private static final String BINARY_PARAMETER = "binaryParameter"; private static final String BYTE_ARRAY = "==ByteArray=="; private static final char SLASH = '/'; private static final String SERVER_API = "/serverAPI/"; // package-private for testing purpose static final String SERVER_URL = "server.url"; private static final String BASIC_AUTHENTICATION_ACTIVE = "basicAuthentication.active"; private static final String BASIC_AUTHENTICATION_USERNAME = "basicAuthentication.username"; private static final String BASIC_AUTHENTICATION_PASSWORD = "basicAuthentication.password"; // package-private for testing purpose static final String APPLICATION_NAME = "application.name"; static final String CONNECTIONS_MAX = "connections.max"; private final String serverUrl; private final String applicationName; private final boolean basicAuthenticationActive; private final String basicAuthenticationUserName; private final String basicAuthenticationPassword; private static HttpClient httpclient; private static final ResponseHandler RESPONSE_HANDLER = new BasicResponseHandler(); private final XmlConverter xmlConverter; public HTTPServerAPI(final Map parameters) { xmlConverter = new XmlConverter(); // initialize httpclient in the constructor to avoid incompatibility when running tests: // java.security.NoSuchAlgorithmException: class configured for SSLContext: sun.security.ssl.SSLContextImpl$TLS10Context not a SSLContext if (httpclient == null) { httpclient = createHttpClient(parameters); } serverUrl = parameters.get(SERVER_URL); applicationName = parameters.get(APPLICATION_NAME); basicAuthenticationActive = "true".equalsIgnoreCase(parameters.get(BASIC_AUTHENTICATION_ACTIVE)); basicAuthenticationUserName = parameters.get(BASIC_AUTHENTICATION_USERNAME); basicAuthenticationPassword = parameters.get(BASIC_AUTHENTICATION_PASSWORD); } private HttpClient createHttpClient(final Map parameters) { HttpClientBuilder builder = HttpClientBuilder.create(); try { int connectionsMax = Integer.parseInt(parameters.getOrDefault(CONNECTIONS_MAX, "20")); // we have only one route, use same number for connection max and max per route builder.setMaxConnPerRoute(connectionsMax); builder.setMaxConnTotal(connectionsMax); } catch (NumberFormatException e) { throw new IllegalArgumentException( "Client connection pool size '" + CONNECTIONS_MAX + "' must be set to a number"); } return builder.build(); } @Override public Object invokeMethod(final Map options, final String apiInterfaceName, final String methodName, final List classNameParameters, final Object[] parametersValues) throws ServerWrappedException { String response = null; try { response = executeHttpPost(options, apiInterfaceName, methodName, classNameParameters, parametersValues); return checkInvokeMethodReturn(response); } catch (final UndeclaredThrowableException e) { throw new ServerWrappedException(e); } catch (final Throwable e) { final StackTraceElement[] stackTrace = new Exception().getStackTrace(); StackTraceTransformer.addStackTo(e, stackTrace); throw new ServerWrappedException(e.getMessage() + " / response: " + response, e); } } // package-private for testing purpose Object checkInvokeMethodReturn(final String response) throws Throwable { Object invokeMethodReturn = null; if (response != null && !response.isEmpty() && !"null".equals(response)) { invokeMethodReturn = xmlConverter.fromXML(response); if (invokeMethodReturn instanceof Throwable) { throw (Throwable) invokeMethodReturn; } } return invokeMethodReturn; } // package-private for testing purpose String executeHttpPost(final Map options, final String apiInterfaceName, final String methodName, final List classNameParameters, final Object[] parametersValues) throws IOException { final HttpPost httpost = createHttpPost(options, apiInterfaceName, methodName, classNameParameters, parametersValues); try { return httpclient.execute(httpost, RESPONSE_HANDLER); } catch (final ClientProtocolException e) { String httpCodeMessage = ""; // required as the http code is not included in the exception message if (e instanceof HttpResponseException) { final int statusCode = ((HttpResponseException) e).getStatusCode(); httpCodeMessage = format(" (http code: %s)", statusCode); } throw new IOException("Error while executing POST request" + httpCodeMessage + " <" + httpost + ">", e); } } private final HttpPost createHttpPost(final Map options, final String apiInterfaceName, final String methodName, final List classNameParameters, final Object[] parametersValues) throws IOException { final HttpEntity httpEntity = buildEntity(options, classNameParameters, parametersValues); final StringBuilder sBuilder = new StringBuilder(serverUrl); sBuilder.append(SLASH).append(applicationName).append(SERVER_API).append(apiInterfaceName).append(SLASH) .append(methodName); final HttpPost httpPost = new HttpPost(sBuilder.toString()); httpPost.setEntity(httpEntity); // Basic authentication if (basicAuthenticationActive) { final StringBuilder credentials = new StringBuilder(); credentials.append(basicAuthenticationUserName).append(":").append(basicAuthenticationPassword); final String encodedCredentials = DigestUtils .encodeBase64AsUtf8String(credentials.toString().getBytes(UTF_8)); httpPost.setHeader("Authorization", "Basic " + encodedCredentials); } return httpPost; } // package-private for testing purpose final HttpEntity buildEntity(final Map options, final List classNameParameters, final Object[] parametersValues) throws IOException { final HttpEntity httpEntity; // if we have a business archive we use multipart to have the business archive attached as a binary content (it can be big) if (classNameParameters.contains(BusinessArchive.class.getName()) || classNameParameters.contains(byte[].class.getName())) { final List bytearrayParameters = new ArrayList<>(); MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create().setBoundary(null).setCharset(UTF_8); entityBuilder.addPart(OPTIONS, new StringBody(xmlConverter.toXML(options), XML_UTF_8)); entityBuilder.addPart(CLASS_NAME_PARAMETERS, new StringBody(xmlConverter.toXML(classNameParameters), XML_UTF_8)); for (int i = 0; i < parametersValues.length; i++) { final Object parameterValue = parametersValues[i]; if (parameterValue instanceof BusinessArchive || parameterValue instanceof byte[]) { parametersValues[i] = BYTE_ARRAY; bytearrayParameters.add(parameterValue); } } entityBuilder.addPart(PARAMETERS_VALUES, new StringBody(xmlConverter.toXML(parametersValues), XML_UTF_8)); int i = 0; for (final Object object : bytearrayParameters) { entityBuilder.addPart(BINARY_PARAMETER + i, new ByteArrayBody(serialize(object), BINARY_PARAMETER + i)); i++; } httpEntity = entityBuilder.build(); } else { final List nvps = new ArrayList<>(); nvps.add(new BasicNameValuePair(OPTIONS, xmlConverter.toXML(options))); nvps.add(new BasicNameValuePair(CLASS_NAME_PARAMETERS, xmlConverter.toXML(classNameParameters))); nvps.add(new BasicNameValuePair(PARAMETERS_VALUES, xmlConverter.toXML(parametersValues))); httpEntity = new UrlEncodedFormEntity(nvps, UTF_8.name()); } return httpEntity; } private static byte[] serialize(final Object obj) throws IOException { final ByteArrayOutputStream b = new ByteArrayOutputStream(); final ObjectOutputStream o = new ObjectOutputStream(b); o.writeObject(obj); return b.toByteArray(); } } ================================================ FILE: bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/PlatformAPIAccessor.java ================================================ /** * 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.api; import java.io.IOException; import java.lang.reflect.Proxy; import java.util.Map; import org.bonitasoft.engine.api.impl.ClientInterceptor; import org.bonitasoft.engine.api.impl.LocalServerAPIFactory; import org.bonitasoft.engine.api.internal.ServerAPI; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.engine.util.APITypeManager; /** * Accessor class that retrieve Platform APIs *

* All APIs given by this class are relevant to the platform only. *

    *
  • {@link PlatformAPI}
  • *
  • {@link PlatformCommandAPI}
  • *
  • {@link PlatformLoginAPI}
  • *
* * @author Matthieu Chaffotte */ public class PlatformAPIAccessor { static ServerAPI getServerAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { try { final ApiAccessType apiType = APITypeManager.getAPIType(); Map parameters; switch (apiType) { case LOCAL: return LocalServerAPIFactory.getServerAPI(); case HTTP: parameters = APITypeManager.getAPITypeParameters(); return new HTTPServerAPI(parameters); default: throw new UnknownAPITypeException("Unsupported API Type: " + apiType); } } catch (IOException e) { throw new ServerAPIException(e); } } /** * Reload the configuration of the Bonita home from the file system * It allows to change in runtime the Bonita engine your client application uses */ public static void refresh() { APITypeManager.refresh(); } /** * @return the {@link PlatformLoginAPI} * @throws BonitaHomeNotSetException * @throws ServerAPIException * @throws UnknownAPITypeException */ public static PlatformLoginAPI getPlatformLoginAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(PlatformLoginAPI.class); } static T getAPI(final Class clazz, final PlatformSession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { final ServerAPI serverAPI = getServerAPI(); final ClientInterceptor sessionInterceptor = new ClientInterceptor(clazz.getName(), serverAPI, session); return (T) Proxy.newProxyInstance(APIAccessor.class.getClassLoader(), new Class[] { clazz }, sessionInterceptor); } static T getAPI(final Class clazz) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { final ServerAPI serverAPI = getServerAPI(); final ClientInterceptor sessionInterceptor = new ClientInterceptor(clazz.getName(), serverAPI); return (T) Proxy.newProxyInstance(APIAccessor.class.getClassLoader(), new Class[] { clazz }, sessionInterceptor); } /** * @param session * a {@link PlatformSession} created using the {@link PlatformLoginAPI} * @return * the {@link PlatformAPI} * @throws InvalidSessionException * @throws BonitaHomeNotSetException * @throws ServerAPIException * @throws UnknownAPITypeException */ public static PlatformAPI getPlatformAPI(final PlatformSession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(PlatformAPI.class, session); } /** * @param session * a {@link PlatformSession} created using the {@link PlatformLoginAPI} * @return * the {@link PlatformCommandAPI} * @throws BonitaHomeNotSetException * @throws ServerAPIException * @throws UnknownAPITypeException * @throws InvalidSessionException */ public static PlatformCommandAPI getPlatformCommandAPI(final PlatformSession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(PlatformCommandAPI.class, session); } public static TemporaryContentAPI getTemporaryContentAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(TemporaryContentAPI.class); } } ================================================ FILE: bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/TcpDestination.java ================================================ /** * 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.api; public class TcpDestination { private final String host; private final int port; public TcpDestination(final String host, final int port) { this.host = host; this.port = port; } public String getHost() { return host; } public int getPort() { return port; } @Override public String toString() { return "TcpDestination [host=" + host + ", port=" + port + "]"; } } ================================================ FILE: bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/TenantAPIAccessor.java ================================================ /** * 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.api; import java.lang.reflect.Proxy; import org.bonitasoft.engine.api.impl.ClientInterceptor; import org.bonitasoft.engine.api.internal.ServerAPI; import org.bonitasoft.engine.api.platform.PlatformInformationAPI; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.util.APITypeManager; /** * Accessor class that retrieve APIs in Bonita Community Edition. *
    *
  • {@link ProcessAPI},
  • *
  • {@link CommandAPI},
  • *
  • {@link IdentityAPI},
  • *
  • {@link LoginAPI}
  • *
* * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros */ public final class TenantAPIAccessor { private static ServerAPI getServerAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return PlatformAPIAccessor.getServerAPI(); } /** * Refreshes the way the engine client communicates to the engine server. * * @see APITypeManager * @see ApiAccessType */ public static void refresh() { APITypeManager.refresh(); } private static T getAPI(final Class clazz, final APISession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { final ServerAPI serverAPI = getServerAPI(); final ClientInterceptor sessionInterceptor = new ClientInterceptor(clazz.getName(), serverAPI, session); return (T) Proxy.newProxyInstance(APIAccessor.class.getClassLoader(), new Class[] { clazz }, sessionInterceptor); } private static T getAPI(final Class clazz) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { final ServerAPI serverAPI = getServerAPI(); final ClientInterceptor sessionInterceptor = new ClientInterceptor(clazz.getName(), serverAPI); return (T) Proxy.newProxyInstance(APIAccessor.class.getClassLoader(), new Class[] { clazz }, sessionInterceptor); } public static LoginAPI getLoginAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(LoginAPI.class); } public static IdentityAPI getIdentityAPI(final APISession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(IdentityAPI.class, session); } public static ProcessAPI getProcessAPI(final APISession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(ProcessAPI.class, session); } public static CommandAPI getCommandAPI(final APISession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(CommandAPI.class, session); } public static ProfileAPI getProfileAPI(final APISession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(ProfileAPI.class, session); } public static PermissionAPI getPermissionAPI(final APISession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(PermissionAPI.class, session); } public static PageAPI getCustomPageAPI(final APISession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(PageAPI.class, session); } public static ApplicationAPI getLivingApplicationAPI(final APISession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(ApplicationAPI.class, session); } public static TenantAdministrationAPI getTenantAdministrationAPI(final APISession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(TenantAdministrationAPI.class, session); } /** * @deprecated as of 7.3, see {@link BusinessDataAPI} for replacements */ @Deprecated(since = "7.3") public static BusinessDataAPI getBusinessDataAPI(APISession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(BusinessDataAPI.class, session); } public static ApplicationAPI getApplicationAPI(final APISession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(ApplicationAPI.class, session); } public static MaintenanceAPI getMaintenanceAPI(final APISession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getAPI(MaintenanceAPI.class, session); } public static PlatformInformationAPI getPlatformInformationAPI(final APISession session) throws ServerAPIException, BonitaHomeNotSetException, UnknownAPITypeException { return getAPI(PlatformInformationAPI.class, session); } } ================================================ FILE: bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/impl/LocalServerAPIFactory.java ================================================ /** * 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.api.impl; import org.bonitasoft.engine.api.internal.ServerAPI; import org.bonitasoft.engine.exception.ServerAPIException; /** * @author Matthieu Chaffotte * @author Emmanuel Duchastenier * @author Celine Souchet */ public class LocalServerAPIFactory { private static Class forName = null; private LocalServerAPIFactory() { // For Sonar } static { try { forName = Class.forName("org.bonitasoft.engine.api.impl.ServerAPIFactory"); } catch (ClassNotFoundException e) { e.printStackTrace(System.err); throw new ExceptionInInitializerError(e); } } public static ServerAPI getServerAPI() throws ServerAPIException { try { return (ServerAPI) forName.getMethod("getServerAPI").invoke(null); } catch (final Exception e) { throw new ServerAPIException(e); } } } ================================================ FILE: bpm/bonita-client/src/main/java/org/bonitasoft/engine/api/impl/XmlConverter.java ================================================ /** * 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.api.impl; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.StringReader; import java.io.StringWriter; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.security.AnyTypePermission; import org.bonitasoft.engine.api.BonitaStackTraceElementConverter; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.xml.XStreamDenyList; public class XmlConverter { private static volatile XStream xstream; private static XStream getXStream() { if (xstream == null) { synchronized (XmlConverter.class) { if (xstream == null) { xstream = createXStream(); } } } return xstream; } private static XStream createXStream() { var xs = new XStream(); xs.ignoreUnknownElements(); xs.addPermission(AnyTypePermission.ANY); // Block known deserialization gadget chain libraries to prevent RCE. // A strict allowlist is not possible here because API responses can contain BDM types with arbitrary packages. xs.denyTypesByWildcard(XStreamDenyList.getDenyPatterns()); xs.registerConverter(new BonitaStackTraceElementConverter(), XStream.PRIORITY_VERY_HIGH); return xs; } // Package-private for testing static void reset() { xstream = null; } public String toXML(final Object object) { final StringWriter stringWriter = new StringWriter(); try (final ObjectOutputStream out = getXStream().createObjectOutputStream(stringWriter)) { out.writeObject(object); } catch (IOException e) { throw new BonitaRuntimeException("Unable to serialize object " + object, e); } return stringWriter.toString(); } @SuppressWarnings("unchecked") public T fromXML(final String object) { try (final StringReader xmlReader = new StringReader(object); final ObjectInputStream in = getXStream().createObjectInputStream(xmlReader)) { return (T) in.readObject(); } catch (final ClassNotFoundException | IOException | RuntimeException e) { // Do not include the XML payload in the error message to prevent // information disclosure of attacker-controlled input throw new BonitaRuntimeException("Unable to deserialize object", e); } } } ================================================ FILE: bpm/bonita-client/src/main/java/org/bonitasoft/engine/bdm/BusinessObjectDAOFactory.java ================================================ /** * 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.bdm; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import org.bonitasoft.engine.bdm.dao.BusinessObjectDAO; import org.bonitasoft.engine.session.APISession; /** * A factory to create Data Access Objects (DAO). These DAOs interact with * {@link org.bonitasoft.engine.bdm.model.BusinessObject}s. * * @author Romain Bioteau * @author Matthieu Chaffotte */ public class BusinessObjectDAOFactory { private static final String IMPL_SUFFIX = "Impl"; /** * Creates the implementation of the DAO for the given session. * * @param session the current opened session * @param daoInterface the interface of the DAO * @return the implementation of the DAO * @throws BusinessObjectDaoCreationException if the factory is not able to instantiate the DAO */ public T createDAO(final APISession session, final Class daoInterface) throws BusinessObjectDaoCreationException { if (session == null) { throw new IllegalArgumentException("session is null"); } if (daoInterface == null) { throw new IllegalArgumentException("daoInterface is null"); } if (!daoInterface.isInterface()) { throw new IllegalArgumentException(daoInterface.getName() + " is not an interface"); } final String daoClassName = daoInterface.getName(); Class daoImplClass = null; try { daoImplClass = loadClass(daoClassName); } catch (final ClassNotFoundException e) { throw new BusinessObjectDaoCreationException(e); } if (daoImplClass != null) { try { final Constructor constructor = daoImplClass.getConstructor(APISession.class); return constructor.newInstance(session); } catch (final SecurityException e) { throw new BusinessObjectDaoCreationException(e); } catch (final NoSuchMethodException e) { throw new BusinessObjectDaoCreationException(e); } catch (final IllegalArgumentException e) { throw new BusinessObjectDaoCreationException(e); } catch (final InstantiationException e) { throw new BusinessObjectDaoCreationException(e); } catch (final IllegalAccessException e) { throw new BusinessObjectDaoCreationException(e); } catch (final InvocationTargetException e) { throw new BusinessObjectDaoCreationException(e); } } return null; } /** * Loads the class of the {@link BusinessObjectDAO} according to its class name. *

* The loading is done in the current Thread. * * @param daoClassName the name of the class of the DAO * @return the class of the BusinessObjectDAO * @throws ClassNotFoundException if the daoClassName is unknown by the current Thread */ @SuppressWarnings("unchecked") protected Class loadClass(final String daoClassName) throws ClassNotFoundException { return (Class) Class.forName(toDaoImplClassName(daoClassName), true, Thread.currentThread().getContextClassLoader()); } private String toDaoImplClassName(final String daoClassName) { return daoClassName + IMPL_SUFFIX; } } ================================================ FILE: bpm/bonita-client/src/main/java/org/bonitasoft/engine/bdm/BusinessObjectDaoCreationException.java ================================================ /** * 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.bdm; import org.bonitasoft.engine.exception.CreationException; /** * Thrown to indicate that the DAO was not created. * * @author Romain Bioteau * @author Matthieu Chaffotte */ public class BusinessObjectDaoCreationException extends CreationException { private static final long serialVersionUID = 1L; /** * Constructs a BusinessObjectDaoCreationException with the specified cause. * * @param cause the cause */ public BusinessObjectDaoCreationException(final Throwable cause) { super(cause); } /** * Constructs a BusinessObjectDaoCreationException with the specified message. * * @param message the explanations of the exception */ public BusinessObjectDaoCreationException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-client/src/main/java/org/bonitasoft/engine/bdm/package-info.java ================================================ /** * Copyright (C) 2015 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. **/ /** *

* Provides classes and interfaces for business data models (BDM). *

*/ package org.bonitasoft.engine.bdm; ================================================ FILE: bpm/bonita-client/src/main/java/org/bonitasoft/engine/exception/UnableToReadBonitaClientConfiguration.java ================================================ /** * 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.exception; /** * @author Elias Ricken de Medeiros */ public class UnableToReadBonitaClientConfiguration extends BonitaException { private static final long serialVersionUID = -7494844524785100125L; public UnableToReadBonitaClientConfiguration(final String message) { super(message); } public UnableToReadBonitaClientConfiguration(final String message, final Throwable cause) { super(message, cause); } public UnableToReadBonitaClientConfiguration(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-client/src/main/java/org/bonitasoft/engine/exception/UnknownAPITypeException.java ================================================ /** * 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.exception; /** * @author Elias Ricken de Medeiros */ public class UnknownAPITypeException extends BonitaException { private static final long serialVersionUID = 7203710840971376884L; public UnknownAPITypeException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-client/src/main/java/org/bonitasoft/engine/util/APITypeManager.java ================================================ /** * 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.util; import static org.bonitasoft.engine.api.ApiAccessType.HTTP; import static org.bonitasoft.engine.api.ApiAccessType.LOCAL; import java.io.File; import java.io.IOException; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.logging.Logger; import org.bonitasoft.engine.api.ApiAccessType; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.io.PropertiesManager; /** * Specify how the client communicate with the engine. There are three ways of doing it: *
    *
  • Using Java System Properties
  • *
  • Programmatically
  • *
  • DEPRECATED: using a file inside bonita-home. See online * documentation.
  • *
*

Using Java System Properties

*

* Use System property -Dorg.bonitasoft.engine.api-type= *

    *
  • LOCAL: *

    connect to the server in the local JVM (default). No other configuration is necessary.

    *
  • *
  • HTTP *

    * connect to the server using HTTP. You must also specify: *

      *
    • -Dorg.bonitasoft.engine.api-type.server.url=HTTP_SERVER_URL, e.g. * http://localhost:8080
    • *
    • -Dorg.bonitasoft.engine.api-type.application.name=WEBAPP_NAME, this is the name of the web * application, e.g. bonita
    • *
    * Optionally you can specify the maximum number of connections (JVM-wide) using * -Dorg.bonitasoft.engine.api-type.connections.max=CONNECTIONS_MAX *

    *
  • *
  • TCP *

    * not recommended, only for testing purpose. *

  • *
*

*

Programmatically

*
    *
  • LOCAL access: *

    APITypeManager.setAPITypeAndParams(ApiAccessType.LOCAL, null);

    *
  • *
  • HTTP access: * *
     * HashMap parameters = new HashMap<>();
     * parameters.put("server.url", "http://myserver.com:8080");
     * parameters.put("application.name", "bonita-application");
     * parameters.put("connections.max", "5");
     * APITypeManager.setAPITypeAndParams(ApiAccessType.HTTP, parameters);
     * 
    * *
  • *
* * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @see Online * documentation on Client configuration */ public class APITypeManager { private static final Logger LOGGER = Logger.getLogger(APITypeManager.class.getName()); private static final String API_TYPE = "org.bonitasoft.engine.api-type"; private static ApiAccessType apiAccessType = null; private static Map apiTypeParameters = null; public static ApiAccessType getAPIType() throws ServerAPIException, UnknownAPITypeException, IOException { if (apiAccessType == null) { final String apiType = getAPITypeFromProperties(); if (LOCAL.name().equalsIgnoreCase(apiType)) { apiAccessType = LOCAL; } else if (HTTP.name().equalsIgnoreCase(apiType)) { apiAccessType = HTTP; } else { throw new UnknownAPITypeException("Invalid API type: " + apiType); } } return apiAccessType; } private static String getAPITypeFromProperties() throws IOException { String property = getProperties().get(API_TYPE); if (property != null) { return property; } return LOCAL.name(); } public static Map getAPITypeParameters() throws ServerAPIException, IOException { if (apiTypeParameters == null) { final Map properties = getProperties(); apiTypeParameters = new HashMap<>(properties.size()); for (Map.Entry property : properties.entrySet()) { if (API_TYPE.equals(property.getKey())) { continue; } apiTypeParameters.put(property.getKey(), property.getValue()); } } return apiTypeParameters; } public static void setAPITypeAndParams(final ApiAccessType type, final Map parameters) { warnIfUsingRemoteConnectionWithLocalEngine(type); apiAccessType = type; apiTypeParameters = new HashMap<>(); if (parameters != null) { apiTypeParameters.putAll(parameters); } } private static void warnIfUsingRemoteConnectionWithLocalEngine(ApiAccessType type) { if (type != null && type != LOCAL) { try { Thread.currentThread().getContextClassLoader() .loadClass("org.bonitasoft.engine.api.impl.ProcessAPIImpl"); LOGGER.warning( "You are declaring an API access to Bonita Engine as a remote connection, whereas it looks like you are running in the same JVM. You should use LOCAL connection, using constant 'ApiAccessType.LOCAL'"); } catch (ClassNotFoundException ignored) { //no warning } } } private static Map getProperties() throws IOException { Map properties = getPropertiesFromSystemProperties(); Properties propertiesFromBonitaHome = getPropertiesFromBonitaHome(); for (String property : propertiesFromBonitaHome.stringPropertyNames()) { if (!properties.containsKey(property)) { properties.put(property, propertiesFromBonitaHome.getProperty(property)); } } return properties; } private static Map getPropertiesFromSystemProperties() { Map properties = new HashMap<>(); String apiType = System.getProperty("org.bonitasoft.engine.api-type"); if (apiType != null) { properties.put("org.bonitasoft.engine.api-type", apiType); } addParameter(properties, "org.bonitasoft.engine.api-type.", "server.url"); addParameter(properties, "org.bonitasoft.engine.api-type.", "application.name"); addParameter(properties, "org.bonitasoft.engine.api-type.", "connections.max"); addParameter(properties, "org.bonitasoft.engine.api-type.", "basicAuthentication.active"); addParameter(properties, "org.bonitasoft.engine.api-type.", "basicAuthentication.username"); addParameter(properties, "org.bonitasoft.engine.api-type.", "basicAuthentication.password"); return properties; } private static void addParameter(Map properties, String parameterPrefix, String parameterName) { String parameter = System.getProperty(parameterPrefix + parameterName); if (parameter != null) { properties.put(parameterName, parameter); } } /* * LEGACY MODE */ private static Properties getPropertiesFromBonitaHome() throws IOException { String bonitaHomePath = System.getProperty("bonita.home"); if (bonitaHomePath == null || bonitaHomePath.isEmpty()) { return new Properties(); } File clientFolder = new File(bonitaHomePath.trim(), "engine-client"); final Properties result = new Properties(); addPropertiesFrom(clientFolder, result, "work", "bonita-client-community.properties"); addPropertiesFrom(clientFolder, result, "conf", "bonita-client-custom.properties"); return result; } private static void addPropertiesFrom(File clientFolder, Properties result, String... paths) throws IOException { File folder = Paths.get(clientFolder.getPath(), paths).toFile(); if (folder.exists()) { final Properties defaultProperties = PropertiesManager.getProperties(folder); result.putAll(defaultProperties); } } public static void refresh() { apiAccessType = null; apiTypeParameters = null; } } ================================================ FILE: bpm/bonita-client/src/test/java/org/bonitasoft/engine/api/APIClientTest.java ================================================ /** * 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.api; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.doReturn; import java.io.IOException; import org.bonitasoft.engine.api.internal.ServerAPI; import org.bonitasoft.engine.api.internal.ServerWrappedException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.platform.LoginException; import org.bonitasoft.engine.platform.LogoutException; import org.bonitasoft.engine.session.APISession; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Nicolas Chabanoles on 18/11/15. */ @RunWith(MockitoJUnitRunner.class) public class APIClientTest { private static final String VALID_USERNAME = "username"; private static final String VALID_PASSWORD = "password"; @Spy APIClient client; @Mock ServerAPI server; @Mock APISession session; @Rule public ExpectedException expectedEx = ExpectedException.none(); @Before public void before() throws IOException, ServerAPIException, UnknownAPITypeException, ServerWrappedException { doReturn(server).when(client).getServerAPI(); doReturn(session).when(server).invokeMethod(anyMap(), eq("org.bonitasoft.engine.api.LoginAPI"), eq("login"), anyList(), eq(new Object[] { VALID_USERNAME, VALID_PASSWORD })); } @Test public void should_throw_exception_when_accessing_api_without_being_loggedIn() { expectedEx.expect(IllegalStateException.class); expectedEx.expectMessage("You must call login() prior to accessing any API."); client.getProcessAPI(); } @Test public void should_login_create_a_session() throws LoginException { client.login(VALID_USERNAME, VALID_PASSWORD); assertThat(client.getSession()).isNotNull(); } @Test public void should_logout_destroy_session() throws LoginException, LogoutException { client.login(VALID_USERNAME, VALID_PASSWORD); assertThat(client.getSession()).isNotNull(); client.logout(); assertThat(client.getSession()).isNull(); } @Test public void should_get_ProcessAPI_from_server() throws LoginException { client.login(VALID_USERNAME, VALID_PASSWORD); ProcessAPI processAPI = client.getProcessAPI(); assertThat(processAPI).isNotNull(); } @Test public void should_get_IdentityAPI_from_server() throws LoginException { client.login(VALID_USERNAME, VALID_PASSWORD); IdentityAPI api = client.getIdentityAPI(); assertThat(api).isNotNull(); } @Test public void should_get_CommandAPI_from_server() throws LoginException { client.login(VALID_USERNAME, VALID_PASSWORD); CommandAPI api = client.getCommandAPI(); assertThat(api).isNotNull(); } @Test public void should_get_ProfileAPI_from_server() throws LoginException { client.login(VALID_USERNAME, VALID_PASSWORD); ProfileAPI api = client.getProfileAPI(); assertThat(api).isNotNull(); } @Test public void should_get_TenantAdministrationAPI_from_server() throws LoginException { client.login(VALID_USERNAME, VALID_PASSWORD); TenantAdministrationAPI api = client.getTenantAdministrationAPI(); assertThat(api).isNotNull(); } @Test public void should_get_PageAPI_from_server() throws LoginException { client.login(VALID_USERNAME, VALID_PASSWORD); PageAPI api = client.getCustomPageAPI(); assertThat(api).isNotNull(); } @Test public void should_get_LivingApplicationAPI_from_server() throws LoginException { client.login(VALID_USERNAME, VALID_PASSWORD); ApplicationAPI api = client.getLivingApplicationAPI(); assertThat(api).isNotNull(); } @Test public void should_get_PermissionAPI_from_server() throws LoginException { client.login(VALID_USERNAME, VALID_PASSWORD); PermissionAPI api = client.getPermissionAPI(); assertThat(api).isNotNull(); } @Test public void should_get_BusinessDataAPI_from_server() throws LoginException { client.login(VALID_USERNAME, VALID_PASSWORD); BusinessDataAPI api = client.getBusinessDataAPI(); assertThat(api).isNotNull(); } @Test public void should_get_ApplicationAPI_from_server() throws LoginException { client.login(VALID_USERNAME, VALID_PASSWORD); ApplicationAPI api = client.getApplicationAPI(); assertThat(api).isNotNull(); } @Test public void should_throw_exception_when_accessing_api_after_logout() throws LoginException, LogoutException { client.login(VALID_USERNAME, VALID_PASSWORD); client.logout(); expectedEx.expect(IllegalStateException.class); expectedEx.expectMessage("You must call login() prior to accessing any API."); client.getProcessAPI(); } @Test public void should_return_session_used_at_creation() { APIClient clientToTest = new APIClient(session); assertThat(clientToTest.getSession()).isEqualTo(session); } @Test public void should_return_session_created_at_login() throws LoginException { client.login(VALID_USERNAME, VALID_PASSWORD); assertThat(client.getSession()).isEqualTo(session); } @Test public void should_newly_created_client_has_no_session() { assertThat(client.getSession()).isNull(); } } ================================================ FILE: bpm/bonita-client/src/test/java/org/bonitasoft/engine/api/BusinessObjectDAOUsingAPIClientTest.java ================================================ /** * 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.api; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.bdm.BusinessObjectDaoCreationException; import org.bonitasoft.engine.bdm.DummyDAO; import org.bonitasoft.engine.bdm.DummyDAOImpl; import org.bonitasoft.engine.bdm.dao.BusinessObjectDAO; import org.bonitasoft.engine.session.APISession; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Nicolas Chabanoles on 30/11/15. */ @RunWith(MockitoJUnitRunner.class) public class BusinessObjectDAOUsingAPIClientTest { @Spy @InjectMocks APIClient client; @Mock APISession session; @Rule public ExpectedException expectedEx = ExpectedException.none(); @Test public void should_create_dao_throw_IllegalArgmumentException_for_null_interface() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("daoInterface is null"); client.getDAO(null); } @Test public void should_create_dao_throw_IllegalArgmumentException_if_daoInterface_is_not_an_interface() throws Exception { expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage("DummyDAOImpl is not an interface"); client.getDAO(DummyDAOImpl.class); } @Test public void should_create_dao_throw_BusinessObjectDaoCreationException_if_daoImpl_not_in_classpath() throws Exception { Mockito.doThrow(ClassNotFoundException.class).when(client).loadClass(BusinessObjectDAO.class); expectedEx.expect(BusinessObjectDaoCreationException.class); expectedEx.expectMessage(""); client.getDAO(BusinessObjectDAO.class); } @Test public void should_create_dao_throw_BusinessObjectDaoCreationException_if_daoImpl_has_no_constructor_with_session() throws Exception { Mockito.doReturn(DummyDAOWithoutConstructorImpl.class).when(client) .loadClass(ArgumentMatchers.any(Class.class)); expectedEx.expect(BusinessObjectDaoCreationException.class); expectedEx.expectMessage(""); client.getDAO(DummyDAO.class); } @Test public void should_create_dao_return_implementation() throws Exception { DummyDAO daoInstance = client.getDAO(DummyDAO.class); Assertions.assertThat(daoInstance).isNotNull(); } class DummyDAOWithoutConstructorImpl implements DummyDAO { } } ================================================ FILE: bpm/bonita-client/src/test/java/org/bonitasoft/engine/api/HTTPServerAPIIT.java ================================================ /** * 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.api; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import java.io.Serializable; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.engine.api.internal.ServerWrappedException; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.security.LoginService; import org.eclipse.jetty.security.authentication.BasicAuthenticator; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.security.Constraint; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; /** * @author Emmanuel Duchastenier * @author Ludovic Bertin */ public class HTTPServerAPIIT { private static final String APPLICATION_NAME = "HTTPServerAPITest"; private static String baseResourceUrl; private static Server server; private final Map options = new HashMap<>(); private final String apiInterfaceName = "someInterface"; private final String methodName = "someMethod"; final List classNameParameters = new ArrayList<>(); @BeforeClass public static void startJetty() throws Exception { // Launch server on random port server = new Server(0); // Simple login service URL realmPropertyURL = HTTPServerAPIIT.class.getClassLoader().getResource("myRealm.properties"); LoginService loginService = new HashLoginService("MyRealm", realmPropertyURL.toString()); server.addBean(loginService); // Security handler. ConstraintSecurityHandler security = new ConstraintSecurityHandler(); server.setHandler(security); // Add security constraint Constraint constraint = new Constraint(); constraint.setName("auth"); constraint.setAuthenticate(true); constraint.setRoles(new String[] { "user", "admin" }); // Bind constraint ConstraintMapping mapping = new ConstraintMapping(); mapping.setPathSpec("/*"); mapping.setConstraint(constraint); // Configure the security handler security.setConstraintMappings(Collections.singletonList(mapping)); security.setAuthenticator(new BasicAuthenticator()); security.setLoginService(loginService); // Simulate Bonita engine part BonitaHandler bonitaHandler = new BonitaHandler(); security.setHandler(bonitaHandler); // start server server.start(); // retrieve port int actualPort = ((ServerConnector) server.getConnectors()[0]).getLocalPort(); baseResourceUrl = "http://localhost:" + actualPort; } @AfterClass public static void stopJetty() throws Exception { server.stop(); } @Test public void invokeMethodWithBasicAuthentication() throws Exception { Map configuration = new HashMap<>(); configuration.put("server.url", baseResourceUrl); configuration.put("application.name", APPLICATION_NAME); configuration.put("connections.max", "2"); configuration.put("basicAuthentication.active", "true"); configuration.put("basicAuthentication.username", "john"); configuration.put("basicAuthentication.password", "doe"); final HTTPServerAPI httpServerAPI = new HTTPServerAPI(configuration); httpServerAPI.invokeMethod(options, apiInterfaceName, methodName, classNameParameters, null); } @Test public void invokeBasicAuthenticationWithWrongCredentialsShouldFail() { Map configuration = new HashMap<>(); configuration.put("server.url", baseResourceUrl); configuration.put("application.name", APPLICATION_NAME); configuration.put("basicAuthentication.active", "true"); configuration.put("basicAuthentication.username", "john"); configuration.put("basicAuthentication.password", "__LENNON__"); final HTTPServerAPI httpServerAPI = new HTTPServerAPI(configuration); Throwable thrown = catchThrowable(() -> httpServerAPI.invokeMethod(options, apiInterfaceName, methodName, classNameParameters, null)); assertThat(thrown).isInstanceOf(ServerWrappedException.class) .hasMessageStartingWith("Error while executing POST request (http code: 401)"); } private static final class BonitaHandler extends AbstractHandler { @Override public void handle(final String s, final Request baseRequest, final HttpServletRequest request, final HttpServletResponse response) { assertThat(request.getUserPrincipal().getName()).isEqualTo("john"); response.setContentType("text/html;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); baseRequest.setHandled(true); } } } ================================================ FILE: bpm/bonita-client/src/test/java/org/bonitasoft/engine/api/HTTPServerAPITest.java ================================================ /** * 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.api; import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import static org.mockito.Mockito.*; import java.io.ByteArrayOutputStream; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.UndeclaredThrowableException; import java.net.URLDecoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.http.HttpEntity; import org.apache.http.client.HttpClient; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.bonitasoft.engine.api.internal.ServerWrappedException; import org.bonitasoft.engine.exception.BonitaException; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; /** * @author Celine Souchet */ public class HTTPServerAPITest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); private HTTPServerAPI httpServerAPI; @Before public void initialize() { HashMap map = new HashMap<>(); map.put(HTTPServerAPI.SERVER_URL, "localhost:8080"); map.put(HTTPServerAPI.APPLICATION_NAME, "bonita"); map.put(HTTPServerAPI.CONNECTIONS_MAX, "3"); httpServerAPI = new HTTPServerAPI(map); } /** * BPA-443: locks down the wire-format tolerance for void engine HTTP API methods. * Both wire shapes produced by server versions past and present must deserialize to * null on the client — otherwise a server change between "null" and "" would break * the client silently. */ @Test public void should_return_null_for_empty_and_null_literal_response_body() throws Throwable { assertThat(httpServerAPI.checkInvokeMethodReturn("")) .as("Empty body (current server wire shape for void methods)").isNull(); assertThat(httpServerAPI.checkInvokeMethodReturn("null")) .as("Literal \"null\" body (legacy server wire shape for void methods)").isNull(); assertThat(httpServerAPI.checkInvokeMethodReturn(null)) .as("Null response (defensive)").isNull(); } @Test public void should_have_max_connections_configured() throws Exception { Field httpclient = httpServerAPI.getClass().getDeclaredField("httpclient"); httpclient.setAccessible(true); HttpClient httpClient = (HttpClient) httpclient.get(null); Field connManager = httpClient.getClass().getDeclaredField("connManager"); connManager.setAccessible(true); PoolingHttpClientConnectionManager connectionManager = (PoolingHttpClientConnectionManager) connManager .get(httpClient); assertThat(connectionManager.getDefaultMaxPerRoute()).isEqualTo(3); assertThat(connectionManager.getMaxTotal()).isEqualTo(3); } @Test public void should_invoke_method_catch_and_wrap_UndeclaredThrowableException() throws Throwable { //given: final Map options = new HashMap<>(); final String apiInterfaceName = "apiInterfaceName"; final String methodName = "methodName"; final List classNameParameters = new ArrayList<>(); final Object[] parametersValues = null; final HTTPServerAPI httpServerAPI = mock(HTTPServerAPI.class); final String response = "response"; doReturn(response).when(httpServerAPI).executeHttpPost(eq(options), eq(apiInterfaceName), eq(methodName), eq(classNameParameters), eq(parametersValues)); doThrow(new UndeclaredThrowableException(new BonitaException("Bonita exception"), "Exception plop")) .when(httpServerAPI).checkInvokeMethodReturn(eq(response)); // Let's call it for real: doCallRealMethod().when(httpServerAPI).invokeMethod(options, apiInterfaceName, methodName, classNameParameters, parametersValues); //when: Throwable thrown = catchThrowable( () -> httpServerAPI.invokeMethod(options, apiInterfaceName, methodName, classNameParameters, parametersValues)); //then: assertThat(thrown).as("Thrown exception").isInstanceOf(ServerWrappedException.class) .hasMessageContaining("java.lang.reflect.UndeclaredThrowableException: Exception plop") .hasRootCauseInstanceOf(BonitaException.class); } @Test public void should_build_entity_serialize_simple_parameters() throws Exception { HttpEntity entity = httpServerAPI.buildEntity(emptyMap(), asList("param1", "param2"), new Object[] { "Välue1", singletonMap("key", "välue") }); String content = new String(entity.getContent().readAllBytes(), StandardCharsets.UTF_8); String decodedContent = URLDecoder.decode(content, "UTF-8"); assertThat(decodedContent).as("Decoded content").contains("välue", "Välue1"); } @Test public void should_build_entity_serialize_byte_array_parameters() throws Exception { HttpEntity entity = httpServerAPI.buildEntity(emptyMap(), asList(String.class.getName(), Map.class.getName(), byte[].class.getName()), new Object[] { "Välue36", singletonMap("key", "välue"), new byte[] {} }); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); entity.writeTo(outputStream); byte[] content = outputStream.toByteArray(); String contentAsString = new String(content, Charset.forName("UTF-8")); assertThat(contentAsString).as("Content").contains("välue", "Välue36"); } } ================================================ FILE: bpm/bonita-client/src/test/java/org/bonitasoft/engine/api/impl/XmlConverterTest.java ================================================ /** * 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.api.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import java.io.IOException; import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.security.ForbiddenClassException; import org.bonitasoft.engine.bpm.actor.impl.ActorDefinitionImpl; import org.bonitasoft.engine.bpm.businessdata.impl.BusinessDataDefinitionImpl; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.junit.Before; import org.junit.Test; public class XmlConverterTest { private XmlConverter xmlConverter; @Before public void setUp() { xmlConverter = new XmlConverter(); } @Test public void should_fromXML_serialize_entity() { // given: XmlConverter xmlConverter = new XmlConverter(); String xml = new StringBuilder("") .append("") .append("test") .append("").toString(); // when: ActorDefinitionImpl actor = xmlConverter.fromXML(xml); // then: assertThat(actor.getName()).isEqualTo("test"); } @Test public void should_fromXML_serialize_entity_when_xml_contains_unknown_elements() { // given: String xml = new StringBuilder("") .append("") .append("test with unknown xml node") .append("test") .append("").toString(); // when: ActorDefinitionImpl actor = xmlConverter.fromXML(xml); // then: assertThat(actor.getName()).isEqualTo("test with unknown xml node"); } @Test public void should_fromXML_always_throws_a_bonita_exception_when_the_xml_is_malformed() { // given: XmlConverter xmlConverter = new XmlConverter(); String xml = new StringBuilder("") .append("") .append("test") .append("").toString(); // when: Throwable thrown = catchThrowable(() -> xmlConverter.fromXML(xml)); // then: assertThat(thrown) .isInstanceOf(BonitaRuntimeException.class) .hasMessage("Unable to deserialize object") .hasCauseInstanceOf(ConversionException.class); } @Test public void should_reject_deserialization_of_gadget_chain_types() { // given: XML payload referencing a denied gadget chain type String maliciousXml = "" + "" + ""; // when: Throwable thrown = catchThrowable(() -> xmlConverter.fromXML(maliciousXml)); // then: assertThat(thrown) .isInstanceOf(BonitaRuntimeException.class) .hasCauseInstanceOf(ForbiddenClassException.class); } @Test public void should_reject_deserialization_of_java_net_URL() { // given: XML payload containing a java.net.URL element (URLDNS gadget chain) String maliciousXml = "" + "" + "http" + "attacker.example.com" + "80" + "/" + "" + ""; // when: Throwable thrown = catchThrowable(() -> xmlConverter.fromXML(maliciousXml)); // then: assertThat(thrown) .isInstanceOf(BonitaRuntimeException.class) .hasCauseInstanceOf(ForbiddenClassException.class); } @Test public void should_use_custom_deny_list_from_system_property() { try { // given: custom deny list via system property XmlConverter.reset(); System.setProperty("bonita.xstream.deny.packages", "java.awt.**"); // when: attempting to deserialize a type matching the custom deny pattern Throwable thrown = catchThrowable(() -> new XmlConverter().fromXML("")); // then: assertThat(thrown) .isInstanceOf(BonitaRuntimeException.class) .hasCauseInstanceOf(ForbiddenClassException.class); } finally { System.clearProperty("bonita.xstream.deny.packages"); XmlConverter.reset(); } } @Test public void should_toXml_deserialize_entity() throws IOException { //given: BusinessDataDefinitionImpl businessDataDefinition = new BusinessDataDefinitionImpl("my name", null); businessDataDefinition.setId(12L); businessDataDefinition.setDescription("my description"); //when: String xml = xmlConverter.toXML(businessDataDefinition); //then: assertThat(xml).isEqualTo("\n" + " \n" + " 12\n" + " my name\n" + " my description\n" + " false\n" + " \n" + ""); } } ================================================ FILE: bpm/bonita-client/src/test/java/org/bonitasoft/engine/bar/BusinessArchiveTest.java ================================================ /** * 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.bar; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.bonitasoft.engine.bpm.actor.ActorDefinition; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory; import org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException; import org.bonitasoft.engine.bpm.bar.ProcessDefinitionBARContribution; import org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping; import org.bonitasoft.engine.bpm.bar.form.model.FormMappingDefinition; import org.bonitasoft.engine.bpm.bar.form.model.FormMappingModel; import org.bonitasoft.engine.bpm.connector.ConnectorDefinition; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.contract.Type; import org.bonitasoft.engine.bpm.data.DataDefinition; import org.bonitasoft.engine.bpm.data.TextDataDefinition; import org.bonitasoft.engine.bpm.document.DocumentDefinition; import org.bonitasoft.engine.bpm.flownode.ActivityDefinition; import org.bonitasoft.engine.bpm.flownode.AutomaticTaskDefinition; import org.bonitasoft.engine.bpm.flownode.BoundaryEventDefinition; import org.bonitasoft.engine.bpm.flownode.CallActivityDefinition; import org.bonitasoft.engine.bpm.flownode.CatchMessageEventTriggerDefinition; import org.bonitasoft.engine.bpm.flownode.EndEventDefinition; import org.bonitasoft.engine.bpm.flownode.GatewayType; import org.bonitasoft.engine.bpm.flownode.MultiInstanceLoopCharacteristics; import org.bonitasoft.engine.bpm.flownode.SendTaskDefinition; import org.bonitasoft.engine.bpm.flownode.StandardLoopCharacteristics; import org.bonitasoft.engine.bpm.flownode.ThrowMessageEventTriggerDefinition; import org.bonitasoft.engine.bpm.flownode.TimerType; import org.bonitasoft.engine.bpm.flownode.UserTaskDefinition; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.impl.AutomaticTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.CallActivityBuilder; import org.bonitasoft.engine.bpm.process.impl.CatchMessageEventTriggerDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ContractDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.MultiInstanceLoopCharacteristicsBuilder; import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.SendTaskDefinitionBuilder; import org.bonitasoft.engine.bpm.process.impl.ThrowMessageEventTriggerBuilder; import org.bonitasoft.engine.bpm.process.impl.UserTaskDefinitionBuilder; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.form.FormMappingTarget; import org.bonitasoft.engine.form.FormMappingType; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.operation.LeftOperand; import org.bonitasoft.engine.operation.LeftOperandBuilder; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.operation.OperatorType; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; /** * @author Baptiste Mesta * @author Celine Souchet * @author Emmanuel Duchastenier */ public class BusinessArchiveTest { private static final String ASSIGN_OPERATOR = "="; private File tempFolder; private File barFile; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Rule public ExpectedException expectedEx = ExpectedException.none(); @Before public void before() throws IOException { tempFolder = temporaryFolder.newFolder(); this.barFile = File.createTempFile("barFile", ".bar", tempFolder); barFile.delete(); } @Test public void should_be_able_to_read_BusinessArchive_with_any_hash() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyBOSProcess", "1.0"); final DesignProcessDefinition process = processDefinitionBuilder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(process).done(); BusinessArchiveFactory.writeBusinessArchiveToFolder(businessArchive, tempFolder); IOUtil.writeContentToFile("random process infos", getFile("process-infos.txt")); BusinessArchiveFactory.readBusinessArchive(tempFolder); } private File getFile(final String fileName) { return new File(tempFolder, fileName); } @Test public void createBusinessArchiveFolder() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcess", "1.0"); final DesignProcessDefinition process = processDefinitionBuilder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(process).done(); BusinessArchiveFactory.writeBusinessArchiveToFolder(businessArchive, tempFolder); assertTrue(tempFolder.exists()); assertTrue(tempFolder.isDirectory()); final File file = getFile(ProcessDefinitionBARContribution.PROCESS_DEFINITION_XML); assertTrue(file.exists()); assertFalse(file.isDirectory()); } @Test public void createBusinessArchiveFileFromFolder() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcess", "1.0"); final DesignProcessDefinition process = processDefinitionBuilder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(process).done(); BusinessArchiveFactory.writeBusinessArchiveToFolder(businessArchive, tempFolder); BusinessArchiveFactory.businessArchiveFolderToFile(barFile, tempFolder.getAbsolutePath()); assertTrue(barFile.exists()); final InputStream inputStream = new FileInputStream(barFile); final BusinessArchive businessArchive2; try { businessArchive2 = BusinessArchiveFactory.readBusinessArchive(inputStream); } finally { inputStream.close(); } final ProcessDefinition result = businessArchive2.getProcessDefinition(); assertEquals(process, result); } @Test public void createBusinessArchiveFromFile() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcess", "1.0"); final DesignProcessDefinition process = processDefinitionBuilder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(process).done(); BusinessArchiveFactory.writeBusinessArchiveToFolder(businessArchive, tempFolder); BusinessArchiveFactory.businessArchiveFolderToFile(barFile, tempFolder.getAbsolutePath()); assertTrue(barFile.exists()); final BusinessArchive businessArchive2 = BusinessArchiveFactory.readBusinessArchive(barFile); final ProcessDefinition result = businessArchive2.getProcessDefinition(); assertEquals(process, result); } @Test(expected = InvalidBusinessArchiveFormatException.class) public void readInvalidBusinessArchive() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcess", "1.0"); final DesignProcessDefinition process = processDefinitionBuilder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(process).done(); BusinessArchiveFactory.writeBusinessArchiveToFolder(businessArchive, tempFolder); final File file = getFile(ProcessDefinitionBARContribution.PROCESS_DEFINITION_XML); file.delete(); file.createNewFile(); final FileWriter fileWriter = new FileWriter(file); fileWriter.write("test"); fileWriter.flush(); fileWriter.close(); BusinessArchiveFactory.readBusinessArchive(tempFolder); } @Test(expected = IOException.class) public void createBusinessArchiveFolderWithInvalidPath() throws Exception { BusinessArchiveFactory.readBusinessArchive(new File("$$$an invalidPath@//\\ùù%%%")); } @Test public void createBusinessArchiveWithProcessDefinition() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcess", "1.0"); final DesignProcessDefinition process = processDefinitionBuilder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(process).done(); final ProcessDefinition result = businessArchive.getProcessDefinition(); assertEquals(process, result); } @Test(expected = InvalidBusinessArchiveFormatException.class) public void createEmptyBusinessArchive() throws Exception { new BusinessArchiveBuilder().createNewBusinessArchive().done(); } @Test public void exportBusinessArchiveAsFile() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcess", "1.0"); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDefinitionBuilder.done()) .done(); BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile); assertTrue(barFile.exists()); assertFalse(barFile.isDirectory()); } @Test(expected = IOException.class) public void exportBusinessArchiveAsFileOnExistingFile() throws Exception { barFile = temporaryFolder.newFile(); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcess", "1.0"); final DesignProcessDefinition process = processDefinitionBuilder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(process).done(); BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile); } @Test public void readBusinessArchive() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcess", "1.0"); final DesignProcessDefinition process = processDefinitionBuilder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(process).done(); BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile); final InputStream inputStream = new FileInputStream(barFile); final BusinessArchive businessArchive2; try { businessArchive2 = BusinessArchiveFactory.readBusinessArchive(inputStream); } finally { inputStream.close(); } final ProcessDefinition result = businessArchive2.getProcessDefinition(); assertEquals(process, result); } @Test(expected = InvalidBusinessArchiveFormatException.class) public void importOldBusinessArchiveFail() throws Exception { final InputStream resourceAsStream = this.getClass().getResourceAsStream("MyProcess--1.0.bar"); try { BusinessArchiveFactory.readBusinessArchive(resourceAsStream); } finally { resourceAsStream.close(); } } @Test(expected = InvalidBusinessArchiveFormatException.class) public void importOldBusinessArchiveFileFail() throws Exception { final InputStream inputStream = this.getClass().getResourceAsStream("MyProcess--1.0.bar"); final OutputStream out = new FileOutputStream(barFile); try { final byte buf[] = new byte[1024]; int len; while ((len = inputStream.read(buf)) > 0) { out.write(buf, 0, len); } } finally { out.close(); inputStream.close(); } BusinessArchiveFactory.readBusinessArchive(barFile); } @Test public void addingEmptyDocument_to_BusinessArchive_should_throw_exception() throws Exception { final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance("EmptyDoc", "7.3").done()); expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage( "You are trying to add file documents/resume.pdf with empty content into the BusinessArchive (bar file)." + " Either add content to this file, or remove it from the resources."); builder.addDocumentResource(new BarResource("resume.pdf", new byte[] {})); } @Test public void addingEmptyResource_to_BusinessArchive_should_throw_exception() throws Exception { final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance("Dummy", "11.01").done()); expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage( "You are trying to add file resources/dummy.txt with empty content into the BusinessArchive (bar file)." + " Either add content to this file, or remove it from the resources."); builder.addExternalResource(new BarResource("dummy.txt", new byte[] {})); } @Test public void addingNullResource_to_BusinessArchive_should_throw_exception() throws Exception { final BusinessArchiveBuilder builder = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(new ProcessDefinitionBuilder().createNewInstance("Dummy", "11.02").done()); expectedEx.expect(IllegalArgumentException.class); expectedEx.expectMessage( "You are trying to add file resources/dummy.txt with empty content into the BusinessArchive (bar file)." + " Either add content to this file, or remove it from the resources."); builder.addExternalResource(new BarResource("dummy.txt", null)); } @Test public void manageBusinessArchiveResources() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcess", "1.0"); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(processDefinitionBuilder.done()) .addExternalResource(new BarResource("dummy.txt", new byte[] { 'a', 'b', 'c', 'd' })).done(); // Add a resource to the biz archive: BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile); // read from the file final BusinessArchive readBusinessArchive = BusinessArchiveFactory.readBusinessArchive(barFile); assertTrue("Added resource not found in BusinessArchive", readBusinessArchive.getResources().containsKey("resources/dummy.txt")); } @Test public void formMappingInBarShouldBeWrittenAndReadProperly() throws Exception { final DesignProcessDefinition designProcessDefinition = new ProcessDefinitionBuilder() .createNewInstance("MethCookingPlanning", "Season 5").done(); final FormMappingModel formMappingModel = new FormMappingModel(); formMappingModel.addFormMapping(new FormMappingDefinition("/?myPageTokenID", FormMappingType.PROCESS_START, FormMappingTarget.INTERNAL)); formMappingModel.addFormMapping(new FormMappingDefinition("someExternalPage", FormMappingType.TASK, FormMappingTarget.URL, "requestTask")); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition) .setFormMappings(formMappingModel).done(); BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile); final BusinessArchive readBusinessArchive = BusinessArchiveFactory.readBusinessArchive(barFile); assertThat(readBusinessArchive.getFormMappingModel().getFormMappings()) .as("Form Mapping should be found in BusinessArchive").hasSize(2); } /* * Changed to work with the new system of actorMapping storage. Note that the test may have lost his purpose * since you cannot give the BusinessArchive garbage as an actorMapping, you need an actual actorMapping class * object */ @Test public void putActorMappingInBar() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("ProductionPlanning", "3.1"); final DesignProcessDefinition designProcessDefinition = processDefinitionBuilder.done(); ActorMapping actorMapping = new ActorMapping(); // Add a resource to the biz archive: final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition) .setActorMapping(actorMapping).done(); BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile); // read from the file final BusinessArchive readBusinessArchive = BusinessArchiveFactory.readBusinessArchive(barFile); // final ProcessDefinition processDefinition = processAPI.deploy(readBusinessArchive); assertEquals(actorMapping, readBusinessArchive.getActorMapping()); } @Test public void readProcessFromBusinessArchive() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcess", "1.0"); processDefinitionBuilder.addDocumentDefinition("testDoc").addContentFileName("testFile.txt") .addFile("testFile.txt").addDescription("desc") .addMimeType("text/plain") .addInitialValue(new ExpressionBuilder().createConstantStringExpression("plop")); processDefinitionBuilder.addDocumentDefinition("testDocUrl").addContentFileName("testFile.txt") .addUrl("http://test.com/testFile.txt") .addDescription("desc"); processDefinitionBuilder.addDescription("a 2-lines\ndescription"); processDefinitionBuilder.addDisplayDescription( "A very good and clean description that will be displayed in user xp\nwith multilines"); processDefinitionBuilder.addDisplayName("Truck Handling Process"); processDefinitionBuilder.addActor("Truck Driver").addDescription("A man that is driving big trucks"); processDefinitionBuilder.setActorInitiator("Truck Driver"); processDefinitionBuilder.addStartEvent("start1").addTimerEventTriggerDefinition(TimerType.CYCLE, new ExpressionBuilder().createConstantStringExpression("*/3 * * * * ?")); // No Java operation, so empty string passed: processDefinitionBuilder .addAutomaticTask("auto1") .addOperation(new LeftOperandBuilder().createNewInstance().setName("testData").done(), OperatorType.ASSIGNMENT, ASSIGN_OPERATOR, null, new ExpressionBuilder().createConstantBooleanExpression(true)) .addConnector("conn1", "connId1", "1.0.0", ConnectorEvent.ON_FINISH).ignoreError(); processDefinitionBuilder .addManualTask("manual1", "Truck Driver") .addPriority("urgent") .addExpectedDuration(5000000) .addDescription("description of manual task1") .addDisplayDescription( new ExpressionBuilder().createConstantStringExpression( "this is an urgent task that will take more than one hour to be done")) .addDisplayName(new ExpressionBuilder().createConstantStringExpression("Urgent task")) .addDisplayDescriptionAfterCompletion( new ExpressionBuilder().createConstantStringExpression("this is a done task that was urgent")); processDefinitionBuilder.addIntermediateCatchEvent("intermediateTimerEvent").addTimerEventTriggerDefinition( TimerType.DURATION, new ExpressionBuilder().createConstantLongExpression(1000)); final UserTaskDefinitionBuilder addUserTask = processDefinitionBuilder.addUserTask("user1", "Truck Driver"); final String ERROR_CODE = "errorToBeCaught"; addUserTask.addConnector("conn2", "connId2", "1.0.0", ConnectorEvent.ON_ENTER) .throwErrorEventWhenFailed(ERROR_CODE); addUserTask.addUserFilter("myUserFilter", "org.bonitasoft.test.user.filter", "1.0.0"); addUserTask.addData("testData", String.class.getName(), null); addUserTask.addShortTextData("shortText", new ExpressionBuilder().createConstantStringExpression("shortText")); addUserTask.addLongTextData("longText", new ExpressionBuilder().createConstantStringExpression("longText")); processDefinitionBuilder.addGateway("gate1", GatewayType.INCLUSIVE).addDefaultTransition("user1"); processDefinitionBuilder.addEndEvent("end1"); processDefinitionBuilder.addTransition("start1", "auto1", new ExpressionBuilder().createConstantBooleanExpression(true)); processDefinitionBuilder.addTransition("auto1", "intermediateTimerEvent"); processDefinitionBuilder.addTransition("intermediateTimerEvent", "user1"); processDefinitionBuilder.addTransition("user1", "gate1"); processDefinitionBuilder.addTransition("user1", "end1"); processDefinitionBuilder .addConnector("conn3", "connId3", "1.0.0", ConnectorEvent.ON_FINISH) .ignoreError() .addInput("input1", new ExpressionBuilder().createConstantBooleanExpression(true)) .addOutput(new LeftOperandBuilder().createNewInstance().setName("testData").done(), OperatorType.ASSIGNMENT, ASSIGN_OPERATOR, null, new ExpressionBuilder().createConstantBooleanExpression(true)); processDefinitionBuilder .addData("myData", "java.lang.Boolean", new ExpressionBuilder().createConstantBooleanExpression(true)) .addDescription("My boolean data"); final DesignProcessDefinition process = processDefinitionBuilder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(process) .addDocumentResource(new BarResource("testFile.txt", new byte[] { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 })) .done(); BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile); final BusinessArchive businessArchive2; try (InputStream inputStream = new FileInputStream(barFile)) { businessArchive2 = BusinessArchiveFactory.readBusinessArchive(inputStream); } final DesignProcessDefinition result = businessArchive2.getProcessDefinition(); final List documentDefinitions = result.getFlowElementContainer().getDocumentDefinitions(); final DocumentDefinition testDoc1 = documentDefinitions.get(0); assertEquals("testDoc", testDoc1.getName()); assertEquals("desc", testDoc1.getDescription()); assertEquals("testFile.txt", testDoc1.getFile()); assertEquals("text/plain", testDoc1.getContentMimeType()); assertEquals("plop", testDoc1.getInitialValue().getContent()); final DocumentDefinition testDoc2 = documentDefinitions.get(1); assertEquals("testDocUrl", testDoc2.getName()); assertEquals("desc", testDoc2.getDescription()); assertEquals("http://test.com/testFile.txt", testDoc2.getUrl()); assertEquals("application/octet-stream", testDoc2.getContentMimeType()); assertEquals(process.getName(), result.getName()); assertEquals(process.getVersion(), result.getVersion()); assertEquals(process.getDescription(), result.getDescription()); assertEquals(process.getDisplayName(), result.getDisplayName()); assertEquals(process.getDisplayDescription(), result.getDisplayDescription()); assertEquals(process.getFlowElementContainer().getStartEvents().size(), result.getFlowElementContainer().getStartEvents().size()); assertEquals(process.getFlowElementContainer().getStartEvents().get(0), result.getFlowElementContainer().getStartEvents().get(0)); assertEquals(process.getFlowElementContainer().getIntermediateCatchEvents().size(), result.getFlowElementContainer().getIntermediateCatchEvents() .size()); assertEquals(process.getFlowElementContainer().getIntermediateCatchEvents().get(0), result.getFlowElementContainer().getIntermediateCatchEvents() .get(0)); assertEquals(process.getFlowElementContainer().getEndEvents().size(), result.getFlowElementContainer().getEndEvents().size()); assertEquals(process.getFlowElementContainer().getEndEvents().get(0), result.getFlowElementContainer().getEndEvents().get(0)); assertEquals(process.getActorsList().size(), result.getActorsList().size()); assertEquals(process.getActorsList().iterator().next(), result.getActorsList().iterator().next()); assertEquals(process.getFlowElementContainer().getActivities().size(), result.getFlowElementContainer().getActivities().size()); final Iterator iterator = result.getFlowElementContainer().getActivities().iterator(); final Iterator iterator2 = process.getFlowElementContainer().getActivities().iterator(); final ActivityDefinition procAct1 = iterator2.next(); final ActivityDefinition procAct2 = iterator2.next(); final ActivityDefinition procAct3 = iterator2.next(); ActivityDefinition orgAuto1 = null; ActivityDefinition orgUser1 = null; ActivityDefinition orgManual1 = null; List asList = Arrays.asList(procAct1, procAct2, procAct3); for (final ActivityDefinition activityDefinition : asList) { if (activityDefinition.getName().equals("auto1")) { orgAuto1 = activityDefinition; } else if (activityDefinition.getName().equals("user1")) { orgUser1 = activityDefinition; } else if (activityDefinition.getName().equals("manual1")) { orgManual1 = activityDefinition; } } final ActivityDefinition resAct1 = iterator.next(); final ActivityDefinition resAct2 = iterator.next(); final ActivityDefinition resAct3 = iterator.next(); ActivityDefinition auto1 = null; ActivityDefinition user1 = null; ActivityDefinition manual1 = null; asList = Arrays.asList(resAct1, resAct2, resAct3); for (final ActivityDefinition activityDefinition : asList) { if (activityDefinition.getName().equals("auto1")) { auto1 = activityDefinition; } else if (activityDefinition.getName().equals("user1")) { user1 = activityDefinition; } else if (activityDefinition.getName().equals("manual1")) { manual1 = activityDefinition; } } assertNotNull("user task not found", user1); assertNotNull("auto task not found", auto1); assertNotNull("manual task not found", manual1); assertEquals("user task not same", orgUser1, user1); assertEquals("auto task not same", orgAuto1, auto1); assertEquals("manual task not same", orgManual1, manual1); assertEquals(3, user1.getDataDefinitions().size()); final DataDefinition dataDefinition1 = user1.getDataDefinitions().get(0); final DataDefinition dataDefinition2 = user1.getDataDefinitions().get(1); final DataDefinition dataDefinition3 = user1.getDataDefinitions().get(2); assertEquals("testData", dataDefinition1.getName()); assertEquals("shortText", dataDefinition2.getName()); assertEquals("longText", dataDefinition3.getName()); assertTrue(dataDefinition2 instanceof TextDataDefinition); assertTrue(dataDefinition3 instanceof TextDataDefinition); assertFalse(((TextDataDefinition) dataDefinition2).isLongText()); assertTrue(((TextDataDefinition) dataDefinition3).isLongText()); assertEquals(1, auto1.getOperations().size()); assertEquals(auto1.getOperations().get(0), auto1.getOperations().get(0)); assertTrue(procAct2.equals(resAct1) || procAct2.equals(resAct2) || procAct2.equals(resAct3)); assertEquals(process.getFlowElementContainer().getGatewaysList().size(), result.getFlowElementContainer().getGatewaysList().size()); assertEquals(process.getFlowElementContainer().getGatewaysList().iterator().next(), result.getFlowElementContainer().getGatewaysList().iterator() .next()); assertEquals(process.getFlowElementContainer().getTransitions().size(), result.getFlowElementContainer().getTransitions().size()); assertThat(result.getProcessContainer().getFlowNode("start1").getOutgoingTransitions().get(0).getCondition() .getContent()) .as("the condition on the transition was not kept").isEqualTo("true"); assertEquals(process.getFlowElementContainer().getConnectors().size(), result.getFlowElementContainer().getConnectors().size()); boolean connectorWithInputOutputOk = false; for (final ConnectorDefinition connector : result.getFlowElementContainer().getConnectors()) { final Operation operation = connector.getOutputs().get(0); if ("conn3".equals(connector.getName()) && "true".equals(connector.getInputs().get("input1").getContent()) && "testData".equals(operation.getLeftOperand().getName()) && OperatorType.ASSIGNMENT.equals(operation.getType()) && ASSIGN_OPERATOR.equals(operation.getOperator()) && "true".equals(operation.getRightOperand().getContent())) { connectorWithInputOutputOk = true; break; } } assertTrue("the input/output on the connector was not kept", connectorWithInputOutputOk); final Iterator resultConnectors = result.getFlowElementContainer().getConnectors() .iterator(); for (final ConnectorDefinition connectorDef : process.getFlowElementContainer().getConnectors()) { final ConnectorDefinition resultConn = resultConnectors.next(); assertEquals(connectorDef, resultConn); } assertEquals(process.getFlowElementContainer().getDataDefinitions().size(), result.getFlowElementContainer().getDataDefinitions().size()); assertEquals(process.getFlowElementContainer().getDataDefinitions().iterator().next(), result.getFlowElementContainer().getDataDefinitions().iterator() .next()); final ActivityDefinition sourceActivity = process.getFlowElementContainer().getActivity("user1"); final ActivityDefinition resultActivity = result.getFlowElementContainer().getActivity("user1"); assertTrue(resultActivity instanceof UserTaskDefinition); assertEquals(((UserTaskDefinition) sourceActivity).getUserFilter(), ((UserTaskDefinition) resultActivity).getUserFilter()); final List processActivities = process.getFlowElementContainer().getActivities(); final Iterator itResultActs = result.getFlowElementContainer().getActivities().iterator(); for (final ActivityDefinition processActivity : processActivities) { final ActivityDefinition resultAct = itResultActs.next(); final List processActivityConnectors = processActivity.getConnectors(); final Iterator itResultCon = resultAct.getConnectors().iterator(); for (final ConnectorDefinition connectorDefinition : processActivityConnectors) { final ConnectorDefinition nextResultConnector = itResultCon.next(); assertEquals(connectorDefinition.getFailAction(), nextResultConnector.getFailAction()); } } } @Test public void readProcessWithContract() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("contract", "1.0"); builder.addActor("myActor"); final ContractDefinitionBuilder contractDefinitionBuilder = builder.addUserTask("step1", "myActor") .addContract(); createContract(contractDefinitionBuilder); createContract(builder.addContract()); final DesignProcessDefinition process = builder.getProcess(); final DesignProcessDefinition result = getDesignProcessDefinition(builder); assertThat(process).isEqualTo(result); } void createContract(final ContractDefinitionBuilder contractDefinitionBuilder) { contractDefinitionBuilder.addInput("numberOfDays", Type.INTEGER, null).addConstraint("Mystical constraint", "true", null, "numberOfDays"); contractDefinitionBuilder.addComplexInput("complex", "a complex input") .addInput("childText", Type.TEXT, "a text simple input") .addInput("childDecimal", Type.DECIMAL, "a decimal simple input"); } @Test public void readProcessWithMessageEventsFromBusinessArchive() throws Exception { final Expression conditionKey = new ExpressionBuilder().createConstantStringExpression("coditionKey"); final Expression trueExpression = new ExpressionBuilder().createConstantBooleanExpression(true); final Expression displayNameExpression = new ExpressionBuilder().createConstantStringExpression("dataToSend"); final LeftOperandBuilder leftOperandBuilder = new LeftOperandBuilder(); leftOperandBuilder.createNewInstance().setName("var1"); final OperationBuilder opb = new OperationBuilder(); opb.createNewInstance().setOperator(ASSIGN_OPERATOR).setRightOperand(trueExpression) .setType(OperatorType.ASSIGNMENT) .setLeftOperand(leftOperandBuilder.done()); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("MyProcess", "1.0"); builder.addBooleanData("var1", null); builder.addStartEvent("start1").addMessageEventTrigger("m1").addOperation(opb.done()); builder.addAutomaticTask("auto1"); final CatchMessageEventTriggerDefinitionBuilder catchMessageEventTriggerDefinitionBuilder = builder .addIntermediateCatchEvent("waitForMessage") .addMessageEventTrigger("m2"); catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression); catchMessageEventTriggerDefinitionBuilder.addOperation(opb.done()); // create expression for target process/flowNode Expression targetProcess = new ExpressionBuilder().createDataExpression("p3", String.class.getName()); final Expression receiveMessage = new ExpressionBuilder().createDataExpression("receiveMessage", String.class.getName()); builder.addIntermediateThrowEvent("sendMessage").addMessageEventTrigger("m4", targetProcess, receiveMessage); targetProcess = new ExpressionBuilder().createConstantStringExpression("p2"); final Expression waitMessage = new ExpressionBuilder().createConstantStringExpression("waitMessage"); final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder = builder.addEndEvent("end1") .addMessageEventTrigger("m2", targetProcess, waitMessage); throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression); throwMessageEventTriggerBuilder.addMessageContentExpression(displayNameExpression, trueExpression); builder.addTransition("start1", "auto1", trueExpression); builder.addTransition("auto1", "waitForMessage"); builder.addTransition("waitForMessage", "sendMessage"); builder.addTransition("sendMessage", "end1"); final DesignProcessDefinition process = builder.getProcess(); final DesignProcessDefinition result = getDesignProcessDefinition(builder); checkProcessForMessagesEvents(process, result); } @Test(expected = InvalidProcessDefinitionException.class) public void tooMuchCorrelationOnCatchMessage() throws Exception { final Expression conditionKey = new ExpressionBuilder().createConstantStringExpression("coditionKey"); final Expression trueExpression = new ExpressionBuilder().createConstantBooleanExpression(true); final Expression displayNameExpression = new ExpressionBuilder().createConstantStringExpression("dataToSend"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcess", "1.0"); processDefinitionBuilder.addStartEvent("start1").addMessageEventTrigger("m1"); processDefinitionBuilder.addAutomaticTask("auto1"); final CatchMessageEventTriggerDefinitionBuilder catchMessageEventTriggerDefinitionBuilder = processDefinitionBuilder .addIntermediateCatchEvent( "waitForMessage") .addMessageEventTrigger("m2"); catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression); catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression); catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression); catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression); catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression); catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression); // create expression for target process/flowNode Expression targetProcess = new ExpressionBuilder().createDataExpression("p3", String.class.getName()); final Expression receiveMessage = new ExpressionBuilder().createDataExpression("receiveMessage", String.class.getName()); processDefinitionBuilder.addIntermediateThrowEvent("sendMessage").addMessageEventTrigger("m4", targetProcess, receiveMessage); targetProcess = new ExpressionBuilder().createConstantStringExpression("p2"); final Expression waitMessage = new ExpressionBuilder().createConstantStringExpression("waitMessage"); final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder = processDefinitionBuilder .addEndEvent("end1").addMessageEventTrigger("m2", targetProcess, waitMessage); throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression); throwMessageEventTriggerBuilder.addMessageContentExpression(displayNameExpression, trueExpression); processDefinitionBuilder.addTransition("start1", "auto1", trueExpression); processDefinitionBuilder.addTransition("auto1", "waitForMessage"); processDefinitionBuilder.addTransition("waitForMessage", "sendMessage"); processDefinitionBuilder.addTransition("sendMessage", "end1"); processDefinitionBuilder.done(); } @Test(expected = InvalidProcessDefinitionException.class) public void tooMuchCorrelationOnThrowMessage() throws Exception { final Expression conditionKey = new ExpressionBuilder().createConstantStringExpression("coditionKey"); final Expression trueExpression = new ExpressionBuilder().createConstantBooleanExpression(true); final Expression displayNameExpression = new ExpressionBuilder().createConstantStringExpression("dataToSend"); final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcess", "1.0"); processDefinitionBuilder.addStartEvent("start1").addMessageEventTrigger("m1"); processDefinitionBuilder.addAutomaticTask("auto1"); final CatchMessageEventTriggerDefinitionBuilder catchMessageEventTriggerDefinitionBuilder = processDefinitionBuilder .addIntermediateCatchEvent( "waitForMessage") .addMessageEventTrigger("m2"); catchMessageEventTriggerDefinitionBuilder.addCorrelation(conditionKey, trueExpression); // create expression for target process/flowNode Expression targetProcess = new ExpressionBuilder().createDataExpression("p3", String.class.getName()); final Expression receiveMessage = new ExpressionBuilder().createDataExpression("receiveMessage", String.class.getName()); processDefinitionBuilder.addIntermediateThrowEvent("sendMessage").addMessageEventTrigger("m4", targetProcess, receiveMessage); targetProcess = new ExpressionBuilder().createConstantStringExpression("p2"); final Expression waitMessage = new ExpressionBuilder().createConstantStringExpression("waitMessage"); final ThrowMessageEventTriggerBuilder throwMessageEventTriggerBuilder = processDefinitionBuilder .addEndEvent("end1").addMessageEventTrigger("m2", targetProcess, waitMessage); throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression); throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression); throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression); throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression); throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression); throwMessageEventTriggerBuilder.addCorrelation(conditionKey, trueExpression); throwMessageEventTriggerBuilder.addMessageContentExpression(displayNameExpression, trueExpression); processDefinitionBuilder.addTransition("start1", "auto1", trueExpression); processDefinitionBuilder.addTransition("auto1", "waitForMessage"); processDefinitionBuilder.addTransition("waitForMessage", "sendMessage"); processDefinitionBuilder.addTransition("sendMessage", "end1"); processDefinitionBuilder.done(); } @Test public void readProcessWithCallActivityFromBusinessArchive() throws Exception { final Expression fromCallerData = new ExpressionBuilder().createDataExpression("var1", Boolean.class.getName()); final LeftOperand dataInputLeftOp = new LeftOperandBuilder().createNewInstance("data1").done(); final Operation dataInputOperation = getOperation(fromCallerData, dataInputLeftOp); final Expression fromCallableElementData = new ExpressionBuilder().createDataExpression("data2", Integer.class.getName()); final LeftOperand dataOutputOp = new LeftOperandBuilder().createNewInstance("var2").done(); final Operation dataOutputOperation = getOperation(fromCallableElementData, dataOutputOp); final Expression targetProcess = new ExpressionBuilder().createConstantStringExpression("MyProcess2"); final Expression processVersion = new ExpressionBuilder().createConstantStringExpression("1.0"); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("MyProcess", "1.0"); builder.addBooleanData("var1", null); builder.addIntegerData("var2", null); builder.addStartEvent("start1"); final CallActivityBuilder callActivityBuilder = builder.addCallActivity("callActivity", targetProcess, processVersion); final Expression supportCaseIdExpression = new ExpressionBuilder().createConstantLongExpression(206L); final String supportCaseIdInputName = "supportCaseId"; callActivityBuilder.addProcessStartContractInput(supportCaseIdInputName, supportCaseIdExpression); callActivityBuilder.addDataInputOperation(dataInputOperation); callActivityBuilder.addDataOutputOperation(dataOutputOperation); builder.addEndEvent("end1"); builder.addTransition("start1", "callActivity"); builder.addTransition("callActivity", "end1"); final DesignProcessDefinition process = builder.getProcess(); final DesignProcessDefinition result = getDesignProcessDefinition(builder); checkProcessForCallActivity(process, result); final CallActivityDefinition callActivity = (CallActivityDefinition) result.getFlowElementContainer() .getActivity("callActivity"); assertThat(callActivity.getProcessStartContractInputs().get(supportCaseIdInputName) .isEquivalent(supportCaseIdExpression)).isTrue(); } @Test public void readProcessWithMultiInstanceFromBusinessArchive() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder() .createNewInstance("ProcessWithMultiInstances", "1.0"); builder.addData("inputList", List.class.getName(), null).addData("outputList", List.class.getName(), null); final AutomaticTaskDefinitionBuilder automaticTaskBuilder = builder.addAutomaticTask("auto1"); automaticTaskBuilder.addShortTextData("input", null).addShortTextData("output", null); final MultiInstanceLoopCharacteristicsBuilder multiInstance1 = automaticTaskBuilder.addMultiInstance(false, "inputList"); multiInstance1.addDataInputItemRef("input"); multiInstance1.addDataOutputItemRef("output"); multiInstance1.addLoopDataOutputRef("outputList"); final MultiInstanceLoopCharacteristicsBuilder multiInstance2 = builder.addAutomaticTask("auto2") .addMultiInstance(true, new ExpressionBuilder().createConstantIntegerExpression(5)); multiInstance2.addCompletionCondition(new ExpressionBuilder().createConstantBooleanExpression(false)); builder.addAutomaticTask("auto3").addLoop(true, new ExpressionBuilder().createConstantBooleanExpression(true), new ExpressionBuilder().createConstantIntegerExpression(5)); final DesignProcessDefinition result = getDesignProcessDefinition(builder); final AutomaticTaskDefinition auto1 = (AutomaticTaskDefinition) result.getFlowElementContainer() .getFlowNode("auto1"); final MultiInstanceLoopCharacteristics multi1 = (MultiInstanceLoopCharacteristics) auto1 .getLoopCharacteristics(); assertEquals(false, multi1.isSequential()); assertEquals("inputList", multi1.getLoopDataInputRef()); assertEquals("outputList", multi1.getLoopDataOutputRef()); assertEquals("input", multi1.getDataInputItemRef()); assertEquals("output", multi1.getDataOutputItemRef()); final AutomaticTaskDefinition auto2 = (AutomaticTaskDefinition) result.getFlowElementContainer() .getFlowNode("auto2"); final MultiInstanceLoopCharacteristics multi2 = (MultiInstanceLoopCharacteristics) auto2 .getLoopCharacteristics(); assertEquals(true, multi2.isSequential()); assertEquals("5", multi2.getLoopCardinality().getContent()); assertEquals("false", multi2.getCompletionCondition().getContent()); final AutomaticTaskDefinition auto3 = (AutomaticTaskDefinition) result.getFlowElementContainer() .getFlowNode("auto3"); final StandardLoopCharacteristics loop2 = (StandardLoopCharacteristics) auto3.getLoopCharacteristics(); assertEquals(true, loop2.isTestBefore()); assertEquals("true", loop2.getLoopCondition().getContent()); assertEquals("5", loop2.getLoopMax().getContent()); } @Test public void readProcessWithBoundaryEventsFromBusinessArchive() throws Exception { final Expression timerExpression = new ExpressionBuilder().createConstantLongExpression(1000); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("MyProcess", "1.0"); builder.addStartEvent("start1"); final UserTaskDefinitionBuilder userTaskDefinitionBuilder = builder.addUserTask("userTask", "delivery"); userTaskDefinitionBuilder.addBoundaryEvent("b1", true).addTimerEventTriggerDefinition(TimerType.DURATION, timerExpression); userTaskDefinitionBuilder.addBoundaryEvent("b2", true).addMessageEventTrigger("m1"); userTaskDefinitionBuilder.addBoundaryEvent("b3", true).addErrorEventTrigger("e1"); builder.addAutomaticTask("exceptionFlowB1"); builder.addAutomaticTask("exceptionFlowB2"); builder.addAutomaticTask("exceptionFlowB3"); builder.addEndEvent("end1"); builder.addTransition("start1", "userTask"); builder.addTransition("userTask", "end1"); builder.addTransition("b1", "exceptionFlowB1"); builder.addTransition("b2", "exceptionFlowB2"); builder.addTransition("b3", "exceptionFlowB3"); final DesignProcessDefinition process = builder.getProcess(); final DesignProcessDefinition result = getDesignProcessDefinition(builder); checkProcessForBoundaryEvents(process, result, true); } @Test public void readProcessWithNonInterruptingBoundaryEventsFromBusinessArchive() throws Exception { final Expression timerExpression = new ExpressionBuilder().createConstantLongExpression(1000); final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("MyProcess", "1.0"); builder.addStartEvent("start1"); final UserTaskDefinitionBuilder userTaskDefinitionBuilder = builder.addUserTask("userTask", "delivery"); userTaskDefinitionBuilder.addBoundaryEvent("b1", false).addTimerEventTriggerDefinition(TimerType.DURATION, timerExpression); builder.addAutomaticTask("exceptionFlowB1"); builder.addEndEvent("end1"); builder.addTransition("start1", "userTask"); builder.addTransition("userTask", "end1"); builder.addTransition("b1", "exceptionFlowB1"); final DesignProcessDefinition process = builder.getProcess(); final DesignProcessDefinition result = getDesignProcessDefinition(builder); checkProcessForBoundaryEvents(process, result, false); } @Test public void readProcessWithSendTaskFromBusinessArchive() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("MyProcess", "1.0"); final SendTaskDefinitionBuilder sendTaskDefinitionBuilder = builder.addSendTask("sendTask", "messageName", new ExpressionBuilder().createConstantStringExpression("processName")); sendTaskDefinitionBuilder .setTargetFlowNode(new ExpressionBuilder().createConstantStringExpression("flowNodeName")); sendTaskDefinitionBuilder.addCorrelation(new ExpressionBuilder().createConstantStringExpression("un"), new ExpressionBuilder().createConstantStringExpression("value")); sendTaskDefinitionBuilder.addMessageContentExpression( new ExpressionBuilder().createConstantStringExpression("myData"), new ExpressionBuilder().createConstantStringExpression("dataValue")); final DesignProcessDefinition result = getDesignProcessDefinition(builder); final SendTaskDefinition flowNode = (SendTaskDefinition) result.getFlowElementContainer() .getFlowNode("sendTask"); assertEquals("sendTask", flowNode.getName()); final ThrowMessageEventTriggerDefinition messageTrigger = flowNode.getMessageTrigger(); assertEquals("processName", messageTrigger.getTargetProcess().getContent()); assertEquals("messageName", messageTrigger.getMessageName()); assertEquals("flowNodeName", messageTrigger.getTargetFlowNode().getContent()); assertEquals("myData", messageTrigger.getDataDefinitions().get(0).getName()); assertEquals("java.lang.String", messageTrigger.getDataDefinitions().get(0).getClassName()); assertEquals("dataValue", messageTrigger.getDataDefinitions().get(0).getDefaultValueExpression().getContent()); assertEquals("un", messageTrigger.getCorrelations().get(0).getKey().getContent()); assertEquals("value", messageTrigger.getCorrelations().get(0).getValue().getContent()); } @Test(expected = InvalidProcessDefinitionException.class) public void unableToExportSendTaskWithNoMessageName() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("MyProcess", "1.0"); final SendTaskDefinitionBuilder sendTaskDefinitionBuilder = builder.addSendTask("sendTask", null, new ExpressionBuilder().createConstantStringExpression("processName")); sendTaskDefinitionBuilder .setTargetFlowNode(new ExpressionBuilder().createConstantStringExpression("flowNodeName")); sendTaskDefinitionBuilder.addCorrelation(new ExpressionBuilder().createConstantStringExpression("un"), new ExpressionBuilder().createConstantStringExpression("value")); sendTaskDefinitionBuilder.addMessageContentExpression( new ExpressionBuilder().createConstantStringExpression("myData"), new ExpressionBuilder().createConstantStringExpression("dataValue")); builder.done(); } @Test public void readProcessWithThowErrorEventFromBusinessArchive() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("MyProcess", "1.0"); builder.addStartEvent("start1"); builder.addAutomaticTask("a1"); builder.addEndEvent("end1").addErrorEventTrigger("e1"); builder.addTransition("start1", "a1"); builder.addTransition("a1", "end1"); final DesignProcessDefinition process = builder.getProcess(); final DesignProcessDefinition result = getDesignProcessDefinition(builder); assertEquals(process.getName(), result.getName()); assertEquals(process.getVersion(), result.getVersion()); assertEquals(process.getFlowElementContainer().getStartEvents(), result.getFlowElementContainer().getStartEvents()); assertEquals(1, result.getFlowElementContainer().getStartEvents().size()); assertEquals(1, process.getFlowElementContainer().getActivities().size()); assertEquals(1, result.getFlowElementContainer().getActivities().size()); final List resultEndEvents = result.getFlowElementContainer().getEndEvents(); assertEquals(process.getFlowElementContainer().getEndEvents(), resultEndEvents); assertEquals(1, result.getFlowElementContainer().getEndEvents().size()); final EndEventDefinition endEventDefinition = resultEndEvents.get(0); assertEquals(1, endEventDefinition.getEventTriggers().size()); assertEquals(1, endEventDefinition.getErrorEventTriggerDefinitions().size()); } private Operation getOperation(final Expression rightOperand, final LeftOperand leftOperand) { final OperationBuilder opb = new OperationBuilder().createNewInstance(); opb.setLeftOperand(leftOperand); opb.setOperator(ASSIGN_OPERATOR); opb.setRightOperand(rightOperand); opb.setType(OperatorType.ASSIGNMENT); return opb.done(); } private void checkProcessForCallActivity(final DesignProcessDefinition process, final DesignProcessDefinition result) { assertEquals(process.getName(), result.getName()); assertEquals(process.getVersion(), result.getVersion()); assertEquals(process.getFlowElementContainer().getStartEvents(), result.getFlowElementContainer().getStartEvents()); assertEquals(1, result.getFlowElementContainer().getStartEvents().size()); assertEquals(1, result.getFlowElementContainer().getActivities().size()); assertEquals(1, process.getFlowElementContainer().getActivities().size()); assertEquals(process.getFlowElementContainer().getActivities().iterator().next(), result.getFlowElementContainer().getActivities().iterator().next()); assertEquals(process.getFlowElementContainer().getEndEvents(), result.getFlowElementContainer().getEndEvents()); assertEquals(1, result.getFlowElementContainer().getEndEvents().size()); } private void checkProcessForBoundaryEvents(final DesignProcessDefinition process, final DesignProcessDefinition result, final boolean isInterrupting) { assertEquals(process.getName(), result.getName()); assertEquals(process.getVersion(), result.getVersion()); assertEquals(process.getFlowElementContainer().getStartEvents(), result.getFlowElementContainer().getStartEvents()); assertEquals(1, result.getFlowElementContainer().getStartEvents().size()); assertEquals(process.getFlowElementContainer().getActivities().size(), result.getFlowElementContainer().getActivities().size()); final ActivityDefinition resultActivity = result.getFlowElementContainer().getActivity("userTask"); final ActivityDefinition origActivity = process.getFlowElementContainer().getActivity("userTask"); assertEquals(origActivity, resultActivity); assertEquals(origActivity.getBoundaryEventDefinitions().size(), resultActivity.getBoundaryEventDefinitions().size()); for (final BoundaryEventDefinition boundary : resultActivity.getBoundaryEventDefinitions()) { assertEquals(1, boundary.getEventTriggers().size()); assertEquals(isInterrupting, boundary.isInterrupting()); } assertEquals(process.getFlowElementContainer().getEndEvents(), result.getFlowElementContainer().getEndEvents()); assertEquals(1, result.getFlowElementContainer().getEndEvents().size()); } private void checkProcessForMessagesEvents(final DesignProcessDefinition process, final DesignProcessDefinition result) { assertEquals(process.getName(), result.getName()); assertEquals(process.getVersion(), result.getVersion()); assertEquals(process.getFlowElementContainer().getStartEvents().size(), result.getFlowElementContainer().getStartEvents().size()); assertEquals(1, result.getFlowElementContainer().getStartEvents().size()); assertEquals(process.getFlowElementContainer().getStartEvents().get(0), result.getFlowElementContainer().getStartEvents().get(0)); assertEquals(1, result.getFlowElementContainer().getStartEvents().get(0).getMessageEventTriggerDefinitions().size()); assertEquals( process.getFlowElementContainer().getStartEvents().get(0).getMessageEventTriggerDefinitions().get(0) .getOperations(), result .getFlowElementContainer().getStartEvents().get(0).getMessageEventTriggerDefinitions().get(0) .getOperations()); assertEquals(1, result.getFlowElementContainer().getStartEvents().get(0).getMessageEventTriggerDefinitions() .get(0).getOperations().size()); assertEquals(process.getFlowElementContainer().getIntermediateCatchEvents().size(), result.getFlowElementContainer().getIntermediateCatchEvents() .size()); assertEquals(1, result.getFlowElementContainer().getIntermediateCatchEvents().size()); assertEquals(process.getFlowElementContainer().getIntermediateCatchEvents().get(0), result.getFlowElementContainer().getIntermediateCatchEvents() .get(0)); assertEquals(1, result.getFlowElementContainer().getIntermediateCatchEvents().get(0) .getMessageEventTriggerDefinitions().size()); final CatchMessageEventTriggerDefinition expectedCatchMessageEventTrigger = process.getFlowElementContainer() .getIntermediateCatchEvents().get(0) .getMessageEventTriggerDefinitions().get(0); final CatchMessageEventTriggerDefinition actualCatchMessageEventTrigger = result.getFlowElementContainer() .getIntermediateCatchEvents().get(0) .getMessageEventTriggerDefinitions().get(0); assertEquals(expectedCatchMessageEventTrigger.getCorrelations(), actualCatchMessageEventTrigger.getCorrelations()); assertEquals(1, actualCatchMessageEventTrigger.getCorrelations().size()); assertEquals(expectedCatchMessageEventTrigger.getOperations(), actualCatchMessageEventTrigger.getOperations()); assertEquals(1, actualCatchMessageEventTrigger.getOperations().size()); assertEquals(process.getFlowElementContainer().getIntermediateThrowEvents().size(), result.getFlowElementContainer().getIntermediateThrowEvents() .size()); assertEquals(1, result.getFlowElementContainer().getIntermediateThrowEvents().size()); assertEquals(process.getFlowElementContainer().getIntermediateThrowEvents().get(0), result.getFlowElementContainer().getIntermediateThrowEvents() .get(0)); assertEquals(1, result.getFlowElementContainer().getIntermediateThrowEvents().get(0) .getMessageEventTriggerDefinitions().size()); assertEquals(process.getFlowElementContainer().getEndEvents().size(), result.getFlowElementContainer().getEndEvents().size()); assertEquals(1, result.getFlowElementContainer().getEndEvents().size()); assertEquals(process.getFlowElementContainer().getEndEvents().get(0), result.getFlowElementContainer().getEndEvents().get(0)); assertEquals(1, result.getFlowElementContainer().getEndEvents().get(0).getMessageEventTriggerDefinitions().size()); final ThrowMessageEventTriggerDefinition actualThrowMessage = result.getFlowElementContainer().getEndEvents() .get(0) .getMessageEventTriggerDefinitions() .get(0); final ThrowMessageEventTriggerDefinition expectedThrowMessage = process.getFlowElementContainer().getEndEvents() .get(0) .getMessageEventTriggerDefinitions() .get(0); assertEquals(expectedThrowMessage.getCorrelations(), actualThrowMessage.getCorrelations()); assertEquals(1, actualThrowMessage.getCorrelations().size()); assertEquals(expectedThrowMessage.getDataDefinitions(), actualThrowMessage.getDataDefinitions()); assertEquals(1, actualThrowMessage.getDataDefinitions().size()); assertEquals(process.getActorsList().size(), result.getActorsList().size()); assertEquals(process.getFlowElementContainer().getActivities().size(), result.getFlowElementContainer().getActivities().size()); assertEquals(process.getFlowElementContainer().getGatewaysList().size(), result.getFlowElementContainer().getGatewaysList().size()); assertEquals(process.getFlowElementContainer().getTransitions().size(), result.getFlowElementContainer().getTransitions().size()); assertEquals(process.getFlowElementContainer().getDataDefinitions().size(), result.getFlowElementContainer().getDataDefinitions().size()); } @Test public void checkErrorMessageOnInvalidTransition() { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcess", "1.0"); processDefinitionBuilder.addTransition("unknown1", "unknown2"); try { processDefinitionBuilder.done(); fail("should have thrown an " + InvalidProcessDefinitionException.class.getSimpleName()); } catch (final InvalidProcessDefinitionException e) { assertTrue(e.getMessage().contains("unknown1")); assertTrue(e.getMessage().contains("unknown2")); } } @Test public void readProcessWithAnActorWithADescription() throws Exception { final ProcessDefinitionBuilder builder = new ProcessDefinitionBuilder().createNewInstance("MyProcessTT", "1.0"); builder.addActor("Truck Driver").addDescription("desc"); builder.addUserTask("step", "Truck Driver"); final DesignProcessDefinition result = getDesignProcessDefinition(builder); final List actors = result.getActorsList(); assertEquals(1, actors.size()); final ActorDefinition actor = actors.iterator().next(); assertEquals("Truck Driver", actor.getName()); assertEquals("desc", actor.getDescription()); } private DesignProcessDefinition getDesignProcessDefinition(final ProcessDefinitionBuilder builder) throws Exception { final DesignProcessDefinition process = builder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(process).done(); BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile); final InputStream inputStream = new FileInputStream(barFile); try { final BusinessArchive businessArchive2 = BusinessArchiveFactory.readBusinessArchive(inputStream); return businessArchive2.getProcessDefinition(); } finally { inputStream.close(); } } @Test public void readProcessWithActorWithoutDescription() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcessTT", "1.0"); processDefinitionBuilder.addActor("Truck Driver"); processDefinitionBuilder.addStartEvent("start1"); processDefinitionBuilder.addAutomaticTask("auto1").addConnector("conn1", "connId1", "1.0.0", ConnectorEvent.ON_FINISH); processDefinitionBuilder.addUserTask("user1", "Truck Driver").addConnector("conn2", "1.0.0", "connId2", ConnectorEvent.ON_ENTER); processDefinitionBuilder.addGateway("gate1", GatewayType.INCLUSIVE).addDefaultTransition("user1"); processDefinitionBuilder.addEndEvent("end1"); processDefinitionBuilder.addTransition("start1", "auto1"); processDefinitionBuilder.addTransition("auto1", "user1"); processDefinitionBuilder.addTransition("user1", "gate1"); processDefinitionBuilder.addTransition("user1", "end1"); processDefinitionBuilder.addConnector("conn3", "connId3", "1.0.0", ConnectorEvent.ON_FINISH); final DesignProcessDefinition process = processDefinitionBuilder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(process).done(); BusinessArchiveFactory.writeBusinessArchiveToFile(businessArchive, barFile); final InputStream inputStream = new FileInputStream(barFile); final BusinessArchive businessArchive2; try { businessArchive2 = BusinessArchiveFactory.readBusinessArchive(inputStream); } finally { inputStream.close(); } final DesignProcessDefinition result = businessArchive2.getProcessDefinition(); assertEquals(process.getName(), result.getName()); assertEquals(process.getVersion(), result.getVersion()); assertEquals(process.getFlowElementContainer().getStartEvents().size(), result.getFlowElementContainer().getStartEvents().size()); assertEquals(process.getFlowElementContainer().getStartEvents().get(0), result.getFlowElementContainer().getStartEvents().get(0)); assertEquals(process.getFlowElementContainer().getEndEvents().size(), result.getFlowElementContainer().getEndEvents().size()); assertEquals(process.getFlowElementContainer().getEndEvents().get(0), result.getFlowElementContainer().getEndEvents().get(0)); assertEquals(process.getActorsList().size(), result.getActorsList().size()); assertEquals(process.getActorsList().iterator().next(), result.getActorsList().iterator().next()); assertEquals(process.getFlowElementContainer().getActivities().size(), result.getFlowElementContainer().getActivities().size()); final Iterator iterator = result.getFlowElementContainer().getActivities().iterator(); final Iterator iterator2 = process.getFlowElementContainer().getActivities().iterator(); final ActivityDefinition procAct1 = iterator2.next(); final ActivityDefinition procAct2 = iterator2.next(); final ActivityDefinition resAct1 = iterator.next(); final ActivityDefinition resAct2 = iterator.next(); assertTrue(procAct1.equals(resAct1) || procAct1.equals(resAct2)); assertTrue(procAct2.equals(resAct1) || procAct2.equals(resAct2)); assertEquals(process.getFlowElementContainer().getGatewaysList().size(), result.getFlowElementContainer().getGatewaysList().size()); assertEquals(process.getFlowElementContainer().getGatewaysList().iterator().next(), result.getFlowElementContainer().getGatewaysList().iterator() .next()); assertEquals(process.getFlowElementContainer().getTransitions().size(), result.getFlowElementContainer().getTransitions().size()); assertEquals(process.getFlowElementContainer().getConnectors().size(), result.getFlowElementContainer().getConnectors().size()); assertEquals(process.getFlowElementContainer().getConnectors().iterator().next(), result.getFlowElementContainer().getConnectors().iterator().next()); } @Test(expected = InvalidBusinessArchiveFormatException.class) public void readInvalidProcessFromBusinessArchive() throws Exception { final ProcessDefinitionBuilder processDefinitionBuilder = new ProcessDefinitionBuilder() .createNewInstance("MyProcess", "1.0"); processDefinitionBuilder.addActor("Truck Driver").addDescription("A man that is driving bigs trucks"); processDefinitionBuilder.addStartEvent("start1"); processDefinitionBuilder.addAutomaticTask("auto1").addConnector("conn1", "connId1", "1.0.0", ConnectorEvent.ON_FINISH); processDefinitionBuilder.addUserTask("user1", "Truck Driver").addConnector("conn2", "connId2", "1.0.0", ConnectorEvent.ON_ENTER); processDefinitionBuilder.addGateway("gate1", GatewayType.INCLUSIVE).addDefaultTransition("user1"); processDefinitionBuilder.addEndEvent("end1"); processDefinitionBuilder.addTransition("start1", "auto1"); processDefinitionBuilder.addTransition("auto1", "user1"); processDefinitionBuilder.addTransition("user1", "gate1"); processDefinitionBuilder.addTransition("user1", "end1"); processDefinitionBuilder.addConnector("conn3", "connId3", "1.0.0", ConnectorEvent.ON_FINISH); processDefinitionBuilder.addParameter("myParam", String.class.getName()) .addDescription("an important parameter"); final DesignProcessDefinition process = processDefinitionBuilder.done(); final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(process).done(); BusinessArchiveFactory.writeBusinessArchiveToFolder(businessArchive, tempFolder); final File file = getFile(ProcessDefinitionBARContribution.PROCESS_DEFINITION_XML); String fileContent = Files.readString(file.toPath()); fileContent = fileContent.replace(" apiTypeParameters = APITypeManager.getAPITypeParameters(); assertThat(apiTypeParameters).isEmpty(); } @Test public void should_getAPITypeParameters_when_set_with_system_properties_works() throws Exception { //given System.setProperty("org.bonitasoft.engine.api-type.server.url", "localhost"); System.setProperty("org.bonitasoft.engine.api-type.application.name", "bonita"); System.setProperty("org.bonitasoft.engine.api-type.connections.max", "12"); System.setProperty("org.bonitasoft.engine.api-type.basicAuthentication.active", "true"); System.setProperty("org.bonitasoft.engine.api-type.basicAuthentication.username", "someUser"); System.setProperty("org.bonitasoft.engine.api-type.basicAuthentication.password", "secret"); //when Map apiTypeParameters = APITypeManager.getAPITypeParameters(); //then assertThat(apiTypeParameters).containsOnly(entry("server.url", "localhost"), entry("application.name", "bonita"), entry("connections.max", "12"), entry("basicAuthentication.active", "true"), entry("basicAuthentication.username", "someUser"), entry("basicAuthentication.password", "secret")); } @Test public void should_getAPITypeParameters_when_set_with_bonita_home_community_properties_works() throws Exception { //given File bonitaHome = temporaryFolder.newFolder(); System.setProperty("bonita.home", bonitaHome.getAbsolutePath()); writePropertiesFile(bonitaHome, "work" + File.separator + "bonita-client-community.properties", "server.url = localhost"); //when Map apiTypeParameters = APITypeManager.getAPITypeParameters(); //then assertThat(apiTypeParameters).containsOnly(entry("server.url", "localhost")); } @Test public void should_getAPITypeParameters_when_set_with_bonita_home_custom_properties_works() throws Exception { //given File bonitaHome = temporaryFolder.newFolder(); System.setProperty("bonita.home", bonitaHome.getAbsolutePath()); writePropertiesFile(bonitaHome, "conf" + File.separator + "bonita-client-custom.properties", "server.url = localhost"); //when Map apiTypeParameters = APITypeManager.getAPITypeParameters(); //then assertThat(apiTypeParameters).containsOnly(entry("server.url", "localhost")); } @Test public void should_getAPITypeParameters_when_set_with_bonita_home_community_and_custom_properties_works() throws Exception { //given File bonitaHome = temporaryFolder.newFolder(); System.setProperty("bonita.home", bonitaHome.getAbsolutePath()); writePropertiesFile(bonitaHome, "work" + File.separator + "bonita-client-community.properties", "server.url = other"); writePropertiesFile(bonitaHome, "conf" + File.separator + "bonita-client-custom.properties", "server.url = localhost"); //when Map apiTypeParameters = APITypeManager.getAPITypeParameters(); //then assertThat(apiTypeParameters).containsOnly(entry("server.url", "localhost")); } @Test public void should_getAPITypeParameters_when_set_programmatically_should_work() throws Exception { //given APITypeManager.setAPITypeAndParams(ApiAccessType.HTTP, Collections.singletonMap("server.url", "localhost")); //when Map apiTypeParameters = APITypeManager.getAPITypeParameters(); //then assertThat(apiTypeParameters).containsOnly(entry("server.url", "localhost")); } private void writePropertiesFile(File bonitaHome, String fileName, String content) throws IOException { File file = new File( bonitaHome.getAbsolutePath() + File.separator + "engine-client" + File.separator + fileName); file.getParentFile().mkdirs(); Files.write(file.toPath(), content.getBytes(StandardCharsets.UTF_8)); } @Test(expected = UnknownAPITypeException.class) public void should_getAPIType_when_set_with_bad_system_properties_throw_exception() throws Exception { //given System.setProperty("org.bonitasoft.engine.api-type", "BAD"); //when APITypeManager.getAPIType(); } @Test(expected = UnknownAPITypeException.class) public void should_getAPIType_when_set_with_bad_bonita_home_community_properties_throw_exception() throws Exception { //given File bonitaHome = temporaryFolder.newFolder(); System.setProperty("bonita.home", bonitaHome.getAbsolutePath()); writePropertiesFile(bonitaHome, "work" + File.separator + "bonita-client-community.properties", "org.bonitasoft.engine.api-type = BAD"); //when APITypeManager.getAPIType(); } } ================================================ FILE: bpm/bonita-client/src/test/resources/myRealm.properties ================================================ john: doe ,admin ================================================ FILE: bpm/bonita-common/build.gradle ================================================ import org.bonitasoft.engine.gradle.PomUtils apply plugin: 'java-test-fixtures' dependencies { api platform(libs.jacksonBom) api platform(libs.bonitaArtifactsModelBom) api "org.bonitasoft.engine:bonita-business-archive" api "org.bonitasoft.engine:bonita-business-object-model" api "org.bonitasoft.engine:bonita-profile-model" api "org.bonitasoft.engine:bonita-organization-model" api "org.bonitasoft.engine:bonita-application-model" api "com.fasterxml.jackson.core:jackson-databind" api libs.slf4jApi api "com.fasterxml.jackson.core:jackson-annotations" runtimeOnly "com.sun.activation:jakarta.activation" compileOnly project(':bpm:bonita-sap-jco-connector-api') compileOnly libs.lombok annotationProcessor libs.lombok testImplementation project(':bpm:bonita-sap-jco-connector-api') testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.logback testImplementation libs.jmockit testImplementation(libs.jsonUnit) { exclude(group: "org.slf4j", module: "slf4j-api") } testFixturesApi("org.bonitasoft.engine:bonita-business-object-model") { artifact { classifier = 'tests' } } testFixturesImplementation libs.assertj } tasks.register("testsJar", Jar) { archiveClassifier = 'tests' from(sourceSets.test.output) } tasks.register("sourcesJar", Jar) { from sourceSets.main.allJava archiveClassifier = 'sources' } tasks.register("javadocJar", Jar) { from javadoc archiveClassifier = 'javadoc' } // Disable publishing of test fixtures variants because their dependencies are published as optional dependencies // It prevents consumers (e.g. bonita-distrib) to retrieve some transitive dependencies (e.g. bonita-business-object-model) components.java.withVariantsFromConfiguration(configurations.testFixturesApiElements) { skip() } components.java.withVariantsFromConfiguration(configurations.testFixturesRuntimeElements) { skip() } publishing { publications { mavenJava(MavenPublication) { from project.components.java artifact project.sourcesJar artifact project.javadocJar artifact project.testsJar pom { pom -> name = "Bonita Common" description = "Bonita Common is the useful layer common to bonita-client and bonita-server" PomUtils.pomCommunityPublication(pom) } } } } test { jvmArgs "-javaagent:${classpath.find { it.name.contains("jmockit") }.absolutePath}" } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/InvalidFileFormatException.java ================================================ /** * 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; /** * @author Danila Mazour */ public interface InvalidFileFormatException { Throwable getCause(); String getMessage(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/APIAccessor.java ================================================ /** * 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.api; import java.io.Serializable; /** * Gives access to some common APIs. * * @author Matthieu Chaffotte * @author Celine Souchet * @author Emmanuel Duchastenier */ public interface APIAccessor extends Serializable { /** * Gives access to the IdentityAPI * * @return an IdentityAPI */ IdentityAPI getIdentityAPI(); /** * Gives access to the ProcessAPI * * @return an ProcessAPI */ ProcessAPI getProcessAPI(); /** * Gives access to the CommandAPI * * @return an CommandAPI */ CommandAPI getCommandAPI(); /** * Gives access to the ProfileAPI * * @return an ProfileAPI */ ProfileAPI getProfileAPI(); /** * Gives access to the PermissionAPI * * @return an PermissionAPI */ PermissionAPI getPermissionAPI(); /** * Gives access to the PageAPI * * @return an PageAPI */ PageAPI getCustomPageAPI(); /** * Gives access to the ApplicationAPI * * @return an ApplicationAPI */ ApplicationAPI getLivingApplicationAPI(); /** * Gives access to the BusinessDataAPI * * @return an BusinessDataAPI * @deprecated as of 7.3, see {@link BusinessDataAPI} for replacements */ @Deprecated(since = "7.3") BusinessDataAPI getBusinessDataAPI(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ApplicationAPI.java ================================================ /** * 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.api; import java.util.List; import org.bonitasoft.engine.business.application.Application; import org.bonitasoft.engine.business.application.ApplicationCreator; import org.bonitasoft.engine.business.application.ApplicationImportPolicy; import org.bonitasoft.engine.business.application.ApplicationLink; import org.bonitasoft.engine.business.application.ApplicationLinkCreator; import org.bonitasoft.engine.business.application.ApplicationLinkUpdater; import org.bonitasoft.engine.business.application.ApplicationMenu; import org.bonitasoft.engine.business.application.ApplicationMenuCreator; import org.bonitasoft.engine.business.application.ApplicationMenuNotFoundException; import org.bonitasoft.engine.business.application.ApplicationMenuUpdater; import org.bonitasoft.engine.business.application.ApplicationNotFoundException; import org.bonitasoft.engine.business.application.ApplicationPage; import org.bonitasoft.engine.business.application.ApplicationPageNotFoundException; import org.bonitasoft.engine.business.application.ApplicationUpdater; import org.bonitasoft.engine.business.application.IApplication; import org.bonitasoft.engine.business.application.Icon; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.ExportException; import org.bonitasoft.engine.exception.ImportException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; /** * This API allows to list and manage Bonita Living Applications ({@link IApplication}). * * @author Elias Ricken de Medeiros * @see org.bonitasoft.engine.business.application.IApplication * @see org.bonitasoft.engine.business.application.ApplicationLink * @see org.bonitasoft.engine.business.application.Application * @since 6.4 */ public interface ApplicationAPI { /** * Creates a new {@link Application} based on the supplied {@link ApplicationCreator} * * @param applicationCreator creator describing characteristics of application to be created * @return the created Application * @throws AlreadyExistsException if an application already exists with the same name * @throws CreationException if an error occurs during the creation * @see Application * @see ApplicationCreator * @deprecated as of 9.0.0, Applications should be created at startup. */ @Deprecated(since = "9.0.0") Application createApplication(ApplicationCreator applicationCreator) throws AlreadyExistsException, CreationException; /** * Creates a new {@link ApplicationLink} based on the supplied {@link ApplicationLinkCreator} * * @param applicationLinkCreator creator describing characteristics of application link to be created * @return the created ApplicationLink * @throws AlreadyExistsException if an application already exists with the same name * @throws CreationException if an error occurs during the creation * @see ApplicationLink * @see ApplicationLinkCreator * @deprecated as of 9.0.0, Applications should be created at startup. This also concerns application links * introduced in 10.2.0. */ @Deprecated(since = "10.2.0") ApplicationLink createApplicationLink(ApplicationLinkCreator applicationLinkCreator) throws AlreadyExistsException, CreationException; /** * Retrieves an {@link Application} from its identifier. * * @param applicationId the application identifier * @return an Application from its identifier. * @throws ApplicationNotFoundException if no application is found for the given identifier * @see Application * @deprecated as of 10.2.0, use {@link #getIApplication(long)} instead to include application links. */ @Deprecated(since = "10.2.0") default Application getApplication(final long applicationId) throws ApplicationNotFoundException { var res = getIApplication(applicationId); if (res instanceof Application legacy) { return legacy; } else { throw new ApplicationNotFoundException(applicationId); } } /** * Retrieves an {@link IApplication} from its identifier. * * @param applicationId the application identifier * @return an Application from its identifier. * @throws ApplicationNotFoundException if no application is found for the given identifier * @see IApplication */ IApplication getIApplication(final long applicationId) throws ApplicationNotFoundException; /** * Retrieves an {@link Application} from its token. * * @param applicationToken the application token used in the URL * @return an Application from its token. * @throws ApplicationNotFoundException if no application is found for the given token * @see Application * @deprecated as of 10.2.0, use {@link #getIApplicationByToken(String)} instead to include application links. */ @Deprecated(since = "10.2.0") default Application getApplicationByToken(final String applicationToken) throws ApplicationNotFoundException { var app = getIApplicationByToken(applicationToken); if (app instanceof Application legacy) { return legacy; } else { throw new ApplicationNotFoundException(applicationToken); } } /** * Retrieves an {@link IApplication} from its token. * * @param applicationToken the application token used in the URL * @return an Application from its token. * @throws ApplicationNotFoundException if no application is found for the given token * @see IApplication */ IApplication getIApplicationByToken(final String applicationToken) throws ApplicationNotFoundException; /** * Deletes an {@link IApplication} by its identifier. All related * {@link org.bonitasoft.engine.business.application.ApplicationPage}s and * {@link org.bonitasoft.engine.business.application.ApplicationMenu}s will be automatically deleted. * * @param applicationId the Application identifier * @throws DeletionException if an error occurs during the deletion * @see IApplication * @see org.bonitasoft.engine.business.application.ApplicationPage * @see org.bonitasoft.engine.business.application.ApplicationMenu */ void deleteApplication(long applicationId) throws DeletionException; /** * Updates an {@link Application} based on the information supplied by the {@link ApplicationUpdater} * * @param applicationId a long representing the application identifier * @param updater an ApplicationUpdater describing the fields to be updated. * @return the Application as it is after the update. * @throws ApplicationNotFoundException if no Application is found for the given id * @throws AlreadyExistsException if another IApplication already exists with the new name value * @throws UpdateException if an error occurs during the update * @see Application * @see ApplicationUpdater * @deprecated as of 9.0.0, Applications should be updated at startup. */ @Deprecated(since = "9.0.0") Application updateApplication(long applicationId, ApplicationUpdater updater) throws ApplicationNotFoundException, UpdateException, AlreadyExistsException; /** * Updates an {@link ApplicationLink} based on the information supplied by the {@link ApplicationLinkUpdater} * * @param applicationId a long representing the application identifier * @param updater an ApplicationLinkUpdater describing the fields to be updated. * @return the ApplicationLink as it is after the update. * @throws ApplicationNotFoundException if no ApplicationLink is found for the given id * @throws AlreadyExistsException if another IApplication already exists with the new name value * @throws UpdateException if an error occurs during the update * @see ApplicationLink * @see ApplicationLinkUpdater * @deprecated as of 9.0.0, Applications should be updated at startup. This also concerns application links * introduced in 10.2.0. */ @Deprecated(since = "10.2.0") ApplicationLink updateApplicationLink(long applicationId, ApplicationLinkUpdater updater) throws ApplicationNotFoundException, UpdateException, AlreadyExistsException; /** * Searches for {@link Application}s with specific search criteria. Use * {@link org.bonitasoft.engine.business.application.ApplicationSearchDescriptor} to * know the available filters. * * @param searchOptions the search criteria. See {@link SearchOptions} for details. * @return a {@link SearchResult} containing the number and the list of applications matching the search criteria. * @throws SearchException if an error occurs during search * @see Application * @see org.bonitasoft.engine.business.application.ApplicationSearchDescriptor * @see SearchOptions * @see SearchResult * @deprecated as of 10.2.0, use {@link #searchIApplications(SearchOptions)} instead to include application links. */ @Deprecated(since = "10.2.0") SearchResult searchApplications(final SearchOptions searchOptions) throws SearchException; /** * Searches for {@link IApplication}s with specific search criteria. Use * {@link org.bonitasoft.engine.business.application.ApplicationSearchDescriptor} to * know the available filters. * * @param searchOptions the search criteria. See {@link SearchOptions} for details. * @return a {@link SearchResult} containing the number and the list of applications matching the search criteria. * @throws SearchException if an error occurs during search * @see IApplication * @see org.bonitasoft.engine.business.application.ApplicationSearchDescriptor * @see SearchOptions * @see SearchResult */ SearchResult searchIApplications(final SearchOptions searchOptions) throws SearchException; /** * Creates an {@link ApplicationPage} * * @param applicationId the identifier of the {@link org.bonitasoft.engine.business.application.Application} to * which the * {@link org.bonitasoft.engine.page.Page} will be associated * @param pageId the identifier of Page to be associated to the Application * @param token the token that this Page will take in this ApplicationPage. The token must * be unique for a given application and * should contain only alpha numeric characters and the following special characters '-', '.', '_' or '~'. In * addition, the following words are reserved * key words and cannot be used * as token: 'api', 'content', 'theme'. * @return the created {@link ApplicationPage} * @throws AlreadyExistsException if the token is already used by another ApplicationPage on this * Application * @throws CreationException if an error occurs during the creation * @throws ApplicationNotFoundException if the referenced application does not exist. * @see ApplicationPage * @see Application * @see org.bonitasoft.engine.page.Page * @deprecated as of 9.0.0, Application page should be created at startup. */ @Deprecated(since = "9.0.0") ApplicationPage createApplicationPage(long applicationId, long pageId, String token) throws AlreadyExistsException, CreationException, ApplicationNotFoundException; /** * Retrieves the {@link ApplicationPage} for the given {@code Application} token and {@code ApplicationPage} token * * @param applicationToken the Application name * @param applicationPageToken the ApplicationPage token * @return the {@link ApplicationPage} for the given {@code Application} token and {@code ApplicationPage} token * @throws ApplicationPageNotFoundException if no {@link ApplicationPage} is found for the given * Application token and * ApplicationPage token * @see ApplicationPage */ ApplicationPage getApplicationPage(String applicationToken, String applicationPageToken) throws ApplicationPageNotFoundException; /** * Retrieves the {@link ApplicationPage} from its identifier * * @param applicationPageId the {@code ApplicationPage} identifier * @return the {@link ApplicationPage} from its identifier * @throws ApplicationPageNotFoundException if no {@link ApplicationPage} is found for the given identifier * @see ApplicationPage */ ApplicationPage getApplicationPage(long applicationPageId) throws ApplicationPageNotFoundException; /** * Deletes an {@link ApplicationPage} by its identifier. All related * {@link org.bonitasoft.engine.business.application.ApplicationMenu} will be * automatically deleted. * * @param applicationPageId the {@code ApplicationPage} identifier * @throws DeletionException if an error occurs during the deletion * @see ApplicationPage * @see org.bonitasoft.engine.business.application.ApplicationMenu */ void deleteApplicationPage(long applicationPageId) throws DeletionException; /** * Searches for {@link ApplicationPage}s with specific search criteria. * * @param searchOptions the search criteria. See {@link SearchOptions} for details. Use * {@link org.bonitasoft.engine.business.application.ApplicationPageSearchDescriptor} to know the available * filters. * @return a {@link SearchResult} containing the number and the list of * {@code org.bonitasoft.engine.business.application.ApplicationPageSearchDescriptor}s * matching the search criteria. * @throws SearchException if an error occurs during the search execution * @see ApplicationPage * @see org.bonitasoft.engine.business.application.ApplicationPageSearchDescriptor * @see SearchOptions * @see SearchResult */ SearchResult searchApplicationPages(final SearchOptions searchOptions) throws SearchException; /** * Defines which {@link ApplicationPage} will represent the {@link Application} home page * * @param applicationId the {@code Application} identifier * @param applicationPageId the identifier of the {@code ApplicationPage} to be used as home page * @throws UpdateException if an error occurs during the update * @throws ApplicationNotFoundException if no {@code Application} is found with the given id * @see Application * @see ApplicationPage * @deprecated as of 9.0.0, Application home page should be defined at startup. */ @Deprecated(since = "9.0.0") void setApplicationHomePage(long applicationId, long applicationPageId) throws UpdateException, ApplicationNotFoundException; /** * Retrieves the {@link ApplicationPage} defined as the {@link Application} home page * * @param applicationId the {@code Application} identifier * @return the t{@code ApplicationPage} defined as {@code Application} home page * @throws ApplicationPageNotFoundException if no home page is found for the given application * @see Application * @see ApplicationPage */ ApplicationPage getApplicationHomePage(long applicationId) throws ApplicationPageNotFoundException; /** * Creates a {@link ApplicationMenu} based on the supplied {@link ApplicationMenuCreator}. The new created * {@code ApplicationMenu} will be ordered at the * last position of its level with an auto generated index. * * @param applicationMenuCreator creator describing the characteristics of the {@code ApplicationMenu} to be created * @return the created {@code ApplicationMenu} * @throws CreationException if an error occurs during the creation * @see ApplicationMenu * @see ApplicationMenuCreator * @deprecated as of 9.0.0, Application menu should be created at startup. */ @Deprecated(since = "9.0.0") ApplicationMenu createApplicationMenu(ApplicationMenuCreator applicationMenuCreator) throws CreationException; /** * Updates an {@link org.bonitasoft.engine.business.application.ApplicationMenu} based on the information supplied * by the * {@link org.bonitasoft.engine.business.application.ApplicationMenuUpdater}. *

* When the {@code ApplicationMenu} index is updated all other {@code ApplicationMenu}s in the same level will have * indexes automatically updated in order * to keep indexes coherency. For instance, when an {@code ApplicationMenu} is moved from index 4 to index 2, the * {@code ApplicationMenu} previously at * index 2 will be moved to index 3 and the {@code ApplicationMenu} previously at index 3 will be moved to index 4. *

* * @param applicationMenuId the {@code ApplicationMenu} identifier * @param updater the {@code ApplicationMenuUpdater} describing the fields to be updated. * @return the {@code ApplicationMenu} up to date * @throws ApplicationMenuNotFoundException if no {@code ApplicationMenu} is found for the given identifier * @throws UpdateException if an exception occurs during the update * @see org.bonitasoft.engine.business.application.ApplicationMenu * @see org.bonitasoft.engine.business.application.ApplicationMenuUpdater * @deprecated as of 9.0.0, Application menu should be updated at startup. */ @Deprecated(since = "9.0.0") ApplicationMenu updateApplicationMenu(long applicationMenuId, ApplicationMenuUpdater updater) throws ApplicationMenuNotFoundException, UpdateException; /** * Retrieves the {@link ApplicationMenu} from its identifier * * @param applicationMenuId the {@code ApplicationMenu} menu identifier * @return the {@code ApplicationMenu} from its identifier * @throws ApplicationMenuNotFoundException if no {@code ApplicationMenu} is found for the given identifier * @see ApplicationMenu */ ApplicationMenu getApplicationMenu(long applicationMenuId) throws ApplicationMenuNotFoundException; /** * Deletes an {@link ApplicationMenu} by its identifier. All children {@code ApplicationMenu} will be automatically * deleted. *

* When an {@code ApplicationMenu} is deleted all others {@code ApplicationMenu}s having index greater than the * index of deleted {@code ApplicationMenu} in * the same level will be automatically updated in order to keep indexes coherency. *

* * @param applicationMenuId the {@code ApplicationMenu} identifier * @throws DeletionException if an error occurs during the deletion * @see ApplicationMenu */ void deleteApplicationMenu(long applicationMenuId) throws DeletionException; /** * Searches for {@link ApplicationMenu}s with specific search criteria. * * @param searchOptions the search criteria. See {@link SearchOptions} for details. Use * {@link org.bonitasoft.engine.business.application.ApplicationMenuSearchDescriptor} to know the available * filters * @return a {@link SearchResult} containing the number and the list of {@code ApplicationMenu}s matching the search * criteria. * @throws SearchException if an error occurs during search * @see ApplicationMenu * @see SearchOptions * @see org.bonitasoft.engine.business.application.ApplicationMenuSearchDescriptor * @see SearchResult */ SearchResult searchApplicationMenus(final SearchOptions searchOptions) throws SearchException; /** * Return all pages names that can be accessed by the profile through applications. * The portal use this method to calculate all permissions for a user. * * @param profileId * the id of the profile * @return * list of page name accessible by the profile through applications */ List getAllPagesForProfile(long profileId); /** * Returns all pages names that can be accessed by the profile through applications. * The portal use this method to calculate all permissions for a user. *
* WARNING: this method is Experimental, it might change in later versions * * @param profile * the name of the profile * @return * list of page name accessible by the profile through applications * @since 7.8 */ @Experimental List getAllPagesForProfile(String profile); /** * Exports the {@link IApplication}s which identifier is in * {@code applicationIds} * * @param applicationIds the identifiers of {@code Application}s to be exported * @return a byte array representing the content of XML file containing the exported {@code Application}s * @throws ExportException if an exception occurs during the export. * @see org.bonitasoft.engine.business.application.IApplication */ byte[] exportApplications(long... applicationIds) throws ExportException; /** * Imports {@link org.bonitasoft.engine.business.application.Application}s based on a XML file content. *

* Before importing {@code Application}s ensure that all {@link org.bonitasoft.engine.profile.Profile}s referenced * by {@code Application}s and all * {@link org.bonitasoft.engine.page.Page}s referenced by * {@link org.bonitasoft.engine.business.application.ApplicationPage}s are available. *

    *
  • When the {@code Profile} does not exist the {@code Application} will be imported, but no {@code Profile} will * be associated to it. An * {@link org.bonitasoft.engine.api.ImportError} will be added to the {@link org.bonitasoft.engine.api.ImportStatus} * related to this {@code Application}. *
  • *
  • When a {@code Page} does not exist the related {@code ApplicationPage} and * {@link org.bonitasoft.engine.business.application.ApplicationMenu}s * pointing to this {@code ApplicationPage} will not be created. An {@code ImportError} will be added to the * {@code ImportStatus} related to the * {@code Application} containing this {@code ApplicationPage}.
  • *
*

* * @param xmlContent a byte array representing the content of XML file containing the applications to be imported. * @param policy the {@link org.bonitasoft.engine.business.application.ApplicationImportPolicy} used to execute the * import * @return a {@link java.util.List} of {@link org.bonitasoft.engine.api.ImportStatus} representing the * {@code ImportStatus} for each imported * {@code Application} * @throws ImportException if an error occurs during the import * @throws org.bonitasoft.engine.exception.AlreadyExistsException if one of applications being imported already * exists and the policy * {@code ApplicationImportPolicy.FAIL_ON_DUPLICATES} is used * @see org.bonitasoft.engine.business.application.Application * @see org.bonitasoft.engine.business.application.ApplicationImportPolicy * @see org.bonitasoft.engine.api.ImportStatus * @see org.bonitasoft.engine.api.ImportError * @see org.bonitasoft.engine.business.application.ApplicationPage * @see org.bonitasoft.engine.business.application.ApplicationMenu * @see org.bonitasoft.engine.profile.Profile * @see org.bonitasoft.engine.page.Page * @deprecated as of 9.0.0, Applications should be imported at startup. */ @Deprecated(since = "9.0.0") List importApplications(final byte[] xmlContent, final ApplicationImportPolicy policy) throws ImportException, AlreadyExistsException; /** * Return the icon associated to the application * * @param applicationId id of the application * @return Icon of the application or null or there is no icon for the application * @throws ApplicationNotFoundException when there is no application having that id */ Icon getIconOfApplication(long applicationId) throws ApplicationNotFoundException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/BusinessDataAPI.java ================================================ /** * 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.api; import java.util.List; import org.bonitasoft.engine.bpm.data.DataNotFoundException; import org.bonitasoft.engine.business.data.BusinessDataReference; /** * This API allows to list the {@link org.bonitasoft.engine.business.data.BusinessDataReference} related to specific * {@link org.bonitasoft.engine.bpm.process.ProcessInstance} * * @author Elias Ricken de Medeiros * @author Laurent Leseigneur * @since 7.0.0 * @see org.bonitasoft.engine.business.data.BusinessDataReference * @see org.bonitasoft.engine.bpm.process.ProcessInstance */ public interface BusinessDataAPI { /** * Returns the {@link org.bonitasoft.engine.business.data.BusinessDataReference} of the named business data of the * process instance. The value is returned in a DataInstance object. * * @param businessDataName * The name of the business data * @param processInstanceId * The identifier of the process instance * @return the reference of the business data * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws org.bonitasoft.engine.bpm.data.DataNotFoundException * If the specified business data value cannot be found. * @deprecated As of 7.3, replaced by {@link ProcessAPI#getProcessInstanceExecutionContext(long)} * ({@link ProcessAPI#getArchivedProcessInstanceExecutionContext(long)} for * archived process instances). In * the map returned by {@link ProcessAPI#getProcessInstanceExecutionContext(long)} you can get * {@link BusinessDataReference} by using * "yourBusinessDataName_ref" key (business data name as declared in * the process definition followed by "_ref" suffix). */ @Deprecated(since = "7.3") BusinessDataReference getProcessBusinessDataReference(String businessDataName, long processInstanceId) throws DataNotFoundException; /** * Lists the paginated {@link BusinessDataReference}s of the process instance order by identifier. * * @param processInstanceId * The identifier of the process instance * @param startIndex * the index of the first result (starting from 0). * @param maxResults * the maximum number of result per page * @return the paginated references of the business data * @deprecated As of 7.3, replaced by {@link ProcessAPI#getProcessInstanceExecutionContext(long)} * ({@link ProcessAPI#getArchivedProcessInstanceExecutionContext(long)} for * archived process instances). In * the map returned by {@link ProcessAPI#getProcessInstanceExecutionContext(long)} you can get * {@link BusinessDataReference} by using * "yourBusinessDataName_ref" key (business data name as declared in * the process definition followed by "_ref" suffix). */ @Deprecated(since = "7.3") List getProcessBusinessDataReferences(long processInstanceId, int startIndex, int maxResults); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/CommandAPI.java ================================================ /** * 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.api; import java.io.Serializable; import java.util.List; import java.util.Map; import org.bonitasoft.engine.command.CommandCriterion; import org.bonitasoft.engine.command.CommandDescriptor; import org.bonitasoft.engine.command.CommandExecutionException; import org.bonitasoft.engine.command.CommandNotFoundException; import org.bonitasoft.engine.command.CommandParameterizationException; import org.bonitasoft.engine.command.CommandUpdater; import org.bonitasoft.engine.command.DependencyNotFoundException; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; /** * Manipulate tenant commands. A command can be registered, unregistered, and executed with parameters. *

* Commands are used to extend engine behavior, and are classes that are called from this API and executed on the server * side.
* in the execute method of this class. *

*

* A command is composed of a jar containing at least one class that implements * org.bonitasoft.engine.command.RuntimeCommand. * The behavior of the command must be defined in the execute method of this class.
* RuntimeCommand is a class available only in bonita-server.jar. In order to create the jar you will need to have a * dependency on that jar. *

* The jar containing the command class must be added to the engine using the {@link CommandAPI#addDependency} method * with a name to identify the dependency so * that it can be removed later.
* Then the command must be registered using {@link CommandAPI#register(String, String, String)} with a name to identify * it and an implementation that is the * fully qualified name of the command class.
* After registration, the command can be executed using {@link CommandAPI#execute(long, Map)} with the id returned by * the register method or * {@link CommandAPI#execute(String, Map)} with the name of the command and with a map of parameters required by the * command.
* Finally the command can be removed using both {@link CommandAPI#unregister(long)} or * {@link CommandAPI#unregister(String)} and * {@link CommandAPI#removeDependency(String)}

* *
 * Code example:
* * In this example we deploy a command named "myCommandName". The class that implements RuntimeCommand is org.bonitasoft.engine.command.IntegerCommand and * is contained in the jar we deploy using CommandAPI.addDependency. *
*
* {@code * byte[] byteArray = /* read the jar containing the command as a byte array * / * * //deploy * getCommandAPI().addDependency("myCommandDependency", byteArray); * getCommandAPI().register("myCommandName", "Retrieving the integer value", "org.bonitasoft.engine.command.IntegerCommand"); * * //execute * final Map parameters = new HashMap(); * parameters.put("aParamterName", "aParameterValue"); * parameters.put("anIntParameter", 42); * Integer theResultOfTheCommandExecution = (Integer) getCommandAPI().execute("myCommandName", parameters); * * //undeploy * getCommandAPI().unregister("myCommandName"); * getCommandAPI().removeDependency("myCommandDependency"); * } *
* * @author Matthieu Chaffotte * @author Yanyan Liu * @author Celine Souchet * @author Emmanuel Duchastenier * @author Baptiste Mesta * @author Emmanuel Duchastenier * @see CommandDescriptor * @see #register(String, String, String) * @see #unregister(long) * @see #addDependency(String, byte[]) * @see #removeDependency(String) * @since 6.0.0 * @version 6.4.1 */ public interface CommandAPI { /** * Add a dependency to the tenant scope. * * @param name * The name of the dependency. * @param jar * The JAR content of the dependency. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws AlreadyExistsException * If a dependency with the same name already exists * @throws CreationException * If an other problem occurs * @since 6.0 */ void addDependency(String name, byte[] jar) throws AlreadyExistsException, CreationException; /** * Remove a dependency to the tenant scope. * * @param name * The name of the dependency. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws DependencyNotFoundException * If the name does not refer to any existing dependency * @throws DeletionException * If an other problem occurs * @since 6.0 */ void removeDependency(String name) throws DependencyNotFoundException, DeletionException; /** * Create a new command. * * @param name * The name of the command * @param description * The description of the command * @param implementation * The name of the implementation class of the command. It will be used when executing the command. This * class is inside the jar of a dependency. * @return The descriptor of the newly created command * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws AlreadyExistsException * If a command with the same name already exists * @throws CreationException * If an other problem occurs * @since 6.0 */ CommandDescriptor register(String name, String description, String implementation) throws AlreadyExistsException, CreationException; /** * Execute a command according to its name and a map of parameters. * * @param name * The name of the command * @param parameters * The parameters of the command * @return The result of the command execution. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws CommandNotFoundException * If the name does not refer to any existing command * @throws CommandParameterizationException * If a parameter of the command is not correct * @throws CommandExecutionException * If an other problem occurs * @since 6.0 */ Serializable execute(String name, Map parameters) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException; /** * Execute a command according to its name and a map of parameters. During the execution of this method, the * command's implementation will have to manage * itself its transactions. * * @param name * The name of the command * @param parameters * The parameters of the command * @return The result of the command execution. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws CommandNotFoundException * If the name does not refer to any existing command * @throws CommandParameterizationException * If a parameter of the command is not correct * @throws CommandExecutionException * If an other problem occurs * @since 6.2 */ Serializable executeWithUserTransactions(String name, Map parameters) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException; /** * Execute a command according to its id and a map of parameters. * * @param commandId * The identifier of the command * @param parameters * The parameters of the command * @return The result of the command execution. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws CommandNotFoundException * If the name does not refer to any existing command * @throws CommandParameterizationException * If a parameter of the command is not correct * @throws CommandExecutionException * If an other problem occurs * @since 6.0 */ Serializable execute(long commandId, Map parameters) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException; /** * Execute a command according to its id and a map of parameters. During the execution of this method, the command's * implementation * will have to manage itself its transactions. * * @param commandId * The identifier of the command * @param parameters * The parameters of the command * @return The result of the command execution. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws CommandNotFoundException * If the name does not refer to any existing command * @throws CommandParameterizationException * If a parameter of the command is not correct * @throws CommandExecutionException * If an other problem occurs * @since 6.2 */ Serializable executeWithUserTransactions(long commandId, Map parameters) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException; /** * Delete a command through its name. * * @param name * The name of the command * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws CommandNotFoundException * If the name does not refer to any existing command. * @throws DeletionException * If an other problem occurs * @since 6.0 */ void unregister(String name) throws CommandNotFoundException, DeletionException; /** * Get the descriptor of the command. * * @param name * The name of the command * @return The descriptor of the command * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws CommandNotFoundException * If an other problem occurs * @since 6.0 */ CommandDescriptor getCommand(String name) throws CommandNotFoundException; /** * Get the paginated list of the descriptors of the command according to the sort criterion. * * @param startIndex * The index of the first element to be retrieved (it starts from zero) * @param maxResults * The number of {@link CommandDescriptor} to get. * @param sort * The sorting criterion of the list. * @return The paginated list of descriptors of the command * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @since 6.0 */ List getAllCommands(int startIndex, int maxResults, CommandCriterion sort); /** * Update a command according to the update descriptor. * * @param name * The name of the command * @param updateDescriptor * The update descriptor (containing the fields to update & their new value). * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws CommandNotFoundException * If the name does not refer to any existing command * @throws UpdateException * If an other problem occurs. * @since 6.0 */ void update(String name, CommandUpdater updateDescriptor) throws CommandNotFoundException, UpdateException; /** * Delete all commands. * * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws DeletionException * If an other problem occurs. * @since 6.0 */ void unregisterAll() throws DeletionException; /** * Get the list of the descriptor of the user commands (no system command). * * @param startIndex * The index of the first element to be retrieved (it starts from zero) * @param maxResults * The number of {@link CommandDescriptor} to get. * @param sort * The sorting criterion of the list. * @return The paginated list of descriptors of the command * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @since 6.0 */ List getUserCommands(final int startIndex, final int maxResults, final CommandCriterion sort); /** * Get the descriptor of the command by its identifier. * * @param commandId * The identifier of command * @return The descriptor of the command * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws CommandNotFoundException * If the identifier does not refer to any existing command. * @since 6.0 */ CommandDescriptor get(long commandId) throws CommandNotFoundException; /** * Update a command according to the update descriptor. * * @param commandId * The identifier of command to update. * @param updateDescriptor * The update descriptor * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws CommandNotFoundException * If the identifier does not refer to any existing command. * @throws UpdateException * If an other problem occurs. * @since 6.0 */ void update(long commandId, CommandUpdater updateDescriptor) throws CommandNotFoundException, UpdateException; /** * Delete a command through its id. * * @param commandId * The identifier of command * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws CommandNotFoundException * If the name does not refer to any existing command. * @throws DeletionException * If an other problem occurs. * @since 6.0 */ void unregister(long commandId) throws CommandNotFoundException, DeletionException; /** * Search commands corresponding to the criteria. * * @param searchOptions * The criterion used during the search * @return A {@link SearchResult} containing the descriptor of the commands. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws SearchException * If an other problem occurs. * @since 6.0 */ SearchResult searchCommands(SearchOptions searchOptions) throws SearchException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/CustomUserInfoAPI.java ================================================ /** * 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.api; import java.util.List; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.CustomUserInfo; import org.bonitasoft.engine.identity.CustomUserInfoDefinition; import org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator; import org.bonitasoft.engine.identity.CustomUserInfoValue; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; /** * CustomUserInfoAPI forms part of the {@link OrganizationAPI} and gives access to all the Administration operations * available on * {@link org.bonitasoft.engine.identity.CustomUserInfoDefinition} and * {@link org.bonitasoft.engine.identity.CustomUserInfoValue}: creation, * deletion and retrieve methods * * @author Vincent Elcrin * @see org.bonitasoft.engine.api.OrganizationAPI * @see org.bonitasoft.engine.identity.CustomUserInfoDefinition * @see org.bonitasoft.engine.identity.CustomUserInfoValue * @since 6.3 */ public interface CustomUserInfoAPI { /** * Creates a new {@link org.bonitasoft.engine.identity.CustomUserInfoDefinition} that will be available for all * {@link org.bonitasoft.engine.identity.User} * s in the organization. In order to set a value for the new created {@code CustomUserInfoDefinition} for a specif * {@link org.bonitasoft.engine.identity.User} use the method {@link #setCustomUserInfoValue(long, long, String)}. *

*

Example:

* *
     *
     * CustomUserInfoDefinitionCreator creator = new CustomUserInfoDefinitionCreator("Skills", "The user skills");
     * CustomUserInfoDefinition userInfoDef = identityAPI.createCustomUserInfoDefinition(creator);
     * 
* *

* * @param creator * A {@link org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator} describing all information about * the {@code CustomUserInfoDefinition} to * be created * @return The created {@code CustomUserInfoDefinition} * @throws AlreadyExistsException * If a {@code CustomUserInfoDefinition} already exists with the same name. * @throws CreationException * If an error occurs during the creation * @see org.bonitasoft.engine.identity.CustomUserInfoDefinition * @see org.bonitasoft.engine.identity.User * @see org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator * @since 6.3 */ CustomUserInfoDefinition createCustomUserInfoDefinition(CustomUserInfoDefinitionCreator creator) throws AlreadyExistsException, CreationException; /** * Retrieves the list of {@link CustomUserInfoDefinition} according to the given pagination criteria, ordered by * name. * * @param startIndex * The index for the first element to be retrieved (starts from zero) * @param maxResult * The maximum number of elements to be retrieved. * @return The list of {@code CustomUserInfoDefinition} according to the given pagination criteria, ordered by name. * @since 6.3 * @see org.bonitasoft.engine.identity.CustomUserInfoDefinition */ List getCustomUserInfoDefinitions(int startIndex, int maxResult); /** * Retrieves the number of existing {@link org.bonitasoft.engine.identity.CustomUserInfoDefinition}s. * * @return The number of existing {@code CustomUserInfoDefinition}s. * @since 6.3 * @see org.bonitasoft.engine.identity.CustomUserInfoDefinition */ long getNumberOfCustomInfoDefinitions(); /** * Deletes the {@link CustomUserInfoDefinition} identified by the given id. All {@link CustomUserInfoValue} related * to this {@code CustomUserInfoDefinition} * will be deleted as well. * * @param id * The identifier of the {@code CustomUserInfoDefinition} * @throws DeletionException * If an error occurs during deletion * @since 6.3 * @see org.bonitasoft.engine.identity.CustomUserInfoDefinition * @see org.bonitasoft.engine.identity.CustomUserInfoValue */ void deleteCustomUserInfoDefinition(long id) throws DeletionException; /** * Retrieves the list of {@link CustomUserInfo} for the given user, ordered by * {@link org.bonitasoft.engine.identity.CustomUserInfoDefinition} name. For * {@code CustomUserInfo}s which have {@code CustomUserInfoDefinition} without a related * {@link CustomUserInfoValue}, the field value will be null. * * @param userId * The identifier of the {@link org.bonitasoft.engine.identity.User} * @param startIndex * The index of the first element to be retrieved (it starts from zero) * @param maxResult * The maximum elements to be retrieved. * @return The list of {@link CustomUserInfo} for the given {@code User}, ordered by * {@code CustomUserInfoDefinition} name. * @see CustomUserInfoDefinition * @see CustomUserInfoValue * @see org.bonitasoft.engine.identity.CustomUserInfo * @see org.bonitasoft.engine.identity.User * @since 6.3 */ List getCustomUserInfo(long userId, int startIndex, int maxResult); /** * Searches {@link org.bonitasoft.engine.identity.CustomUserInfoValue}s according to the criteria contained in the * given * {@link org.bonitasoft.engine.search.SearchOptions}. In order to know which fields can be used in filters and * sorting, please refer to * {@link org.bonitasoft.engine.identity.CustomUserInfoValueSearchDescriptor}. *

* Example: searches the first 10 {@code CustomUserInfoValue}s having the given {@code CustomUserInfoDefinition} * (referenced by its identifier) with the * given value. The result is ordered by the related {@code User} identifier: * *
     * SearchOptionsBuilder optionsBuilder = new SearchOptionsBuilder(0, 10);
     * optionsBuilder.filter(CustomUserInfoValueSearchDescriptor.DEFINITION_ID, userInfoDefinition.getId());
     * optionsBuilder.filter(CustomUserInfoValueSearchDescriptor.VALUE, value);
     * optionsBuilder.sort(CustomUserInfoValueSearchDescriptor.USER_ID, Order.ASC);
     * SearchResult<CustomUserInfoValue> searchResult = identityAPI.searchCustomUserInfoValues(optionsBuilder.done());
     * 
* * @param options * The {@link org.bonitasoft.engine.search.SearchOptions} containing the search criteria * @return The {@link org.bonitasoft.engine.search.SearchResult} containing the number and the list of * {@code CustomUserInfoValue}s matching the criteria * @since 6.3 * @see org.bonitasoft.engine.search.SearchOptions * @see org.bonitasoft.engine.identity.CustomUserInfoValue * @see org.bonitasoft.engine.identity.CustomUserInfoValueSearchDescriptor * @see org.bonitasoft.engine.search.SearchResult */ SearchResult searchCustomUserInfoValues(SearchOptions options); /** * Defines the value of a {@link org.bonitasoft.engine.identity.CustomUserInfoDefinition} for a given * {@link org.bonitasoft.engine.identity.User}. * * @param definitionId * The identifier of the {@code CustomUserInfoDefinition} * @param userId * The identifier of the {@code User} * @param value * The {@code Custom User Information} value * @return a {@link org.bonitasoft.engine.identity.CustomUserInfoValue} representing the value of the given * {@code CustomUserInfoDefinition} for the given * {@code User} * @throws org.bonitasoft.engine.exception.UpdateException * When an error occurs during the update. * @since 6.3 * @see org.bonitasoft.engine.identity.CustomUserInfoDefinition#getId() * @see org.bonitasoft.engine.identity.User#getId() * @see org.bonitasoft.engine.identity.CustomUserInfoValue */ CustomUserInfoValue setCustomUserInfoValue(long definitionId, long userId, String value) throws UpdateException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/DocumentAPI.java ================================================ /** * 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.api; import java.util.List; import org.bonitasoft.engine.bpm.document.ArchivedDocument; import org.bonitasoft.engine.bpm.document.ArchivedDocumentNotFoundException; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.document.DocumentAttachmentException; import org.bonitasoft.engine.bpm.document.DocumentCriterion; import org.bonitasoft.engine.bpm.document.DocumentException; import org.bonitasoft.engine.bpm.document.DocumentNotFoundException; import org.bonitasoft.engine.bpm.document.DocumentValue; import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.identity.UserNotFoundException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; /** * Manipulate documents that are attached to a process instance. *

* A document can be stored directly with the process instance, or by reference. If you store a document by reference, * the process instance contains a document * object that has metadata describing the document: its name, content MimeType, the name of the file of the document, * and a URL giving the location of the * document. The choice of direct local storage or storage by reference depends on the performance profile of document * access within an instance of the process. * If you require frequent and rapid access to update the document and the document is not large, use direct storage. If * the document is large or is not * accessed frequently within a process instance, or is not updated by the process, store it by reference. *

* Multiple versions of a document can be stored. You can retrieve the latest version or the version that was current at * a given milestone (for example process * instantiation, or activity completion). * * @author Emmanuel Duchastenier * @author Baptiste Mesta */ public interface DocumentAPI { /** * Attach a document by reference to the specified process instance. *

* The document itself does not contain content but is a reference to external content specified by its URL. * The author of the document will be the user currently calling the API method. Note that the operations of a task * are not executed by the assigned user, * but directly by the System. It means that calling this method directly from an operation inside a task (through a * groovy script for example) will attach a * document whose author is the user System. *

* * @param processInstanceId * The identifier of the process instance * @param documentName * The name of the document * @param fileName * The filename of the document content * @param mimeType * The MimeType of the document content (optional) * @param url * The URL of the document content * @return a document object * @throws ProcessInstanceNotFoundException * If the identifier does not refer to an existing process instance. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws DocumentAttachmentException * when an error occurs while attaching the document * @since 6.0 */ Document attachDocument(long processInstanceId, String documentName, String fileName, String mimeType, String url) throws ProcessInstanceNotFoundException, DocumentAttachmentException; /** * Attach a new document version to a process instance. *

* Depending on the DocumentValue given the document will be internal (with content) or external (with url). * The document state is archived and is then updated to the new version *

* * @param documentId * The identifier of the document to update * @param documentValue * The value of the document * @return a document object * @throws ProcessInstanceNotFoundException * If the identifier does not refer to an existing process instance. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws DocumentAttachmentException * when an error occurs while attaching the document * @throws org.bonitasoft.engine.exception.AlreadyExistsException * If the document already exists. * @since 6.4.0 */ Document updateDocument(long documentId, DocumentValue documentValue) throws ProcessInstanceNotFoundException, DocumentAttachmentException, AlreadyExistsException; /** * Attach a new document to a process instance. *
* Depending on the DocumentValue given the document will be internal (with content) or external (with url). *
    *
  1. If the target document is a list of document then we append it to the list
  2. *
  3. If the target document is a list of document and the index is set on the document value then we insert the * element in the list at the specified * index
  4. *
  5. If the target single document or is non existent in the definition we create it
  6. *
  7. If the target single document and is already existent an exception is thrown
  8. *
* The author of the document will be the user currently calling the API method. Note that the operations of a task * are not executed by the assigned user, * but directly by the System. It means that calling this method directly from an operation inside a task (through a * groovy script for example) will attach add * a document whose author is the user System. * * @param processInstanceId * The identifier of the process instance * @param documentName * The name of the document * @param description * The description of the document * @param documentValue * The value of the document * @return a document object * @throws ProcessInstanceNotFoundException * If the identifier does not refer to an existing process instance. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws DocumentAttachmentException * when an error occurs while attaching the document * @throws org.bonitasoft.engine.exception.AlreadyExistsException * If the document already exists. * @since 6.4.0 */ Document addDocument(long processInstanceId, String documentName, String description, DocumentValue documentValue) throws ProcessInstanceNotFoundException, DocumentAttachmentException, AlreadyExistsException; /** * Attach the given document to the specified process instance. *

* The content is stored to enable later retrieval. * The author of the document will be the user currently calling the API method. Note that the operations of a task * are not executed by the assigned user, * but directly by the System. It means that calling this method directly from an operation inside a task (through a * groovy script for example) will attach a * document whose author is the user System. *

* * @param processInstanceId * The identifier of the process instance * @param documentName * The name of the document * @param fileName * The name of the file containing the document * @param mimeType * The MimeType of the document content (optional) * @param documentContent * The content of the document * @return a document object * @throws ProcessInstanceNotFoundException ** If the identifier does not refer to an existing process instance. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws DocumentAttachmentException * when an error occurs while attaching the document * @since 6.0 */ Document attachDocument(long processInstanceId, String documentName, String fileName, String mimeType, byte[] documentContent) throws ProcessInstanceNotFoundException, DocumentAttachmentException; /** * Attach a new version of a document by reference to the specified process instance. The referenced document is * a new version of the named document. * The author of the document will be the user currently calling the API method. Note that the operations of a task * are not executed by the assigned user, * but directly by the System. It means that calling this method directly from an operation inside a task (through a * groovy script for example) will attach a * document whose author is the user System. * * @param processInstanceId * The identifier of the process instance * @param documentName * The name of the document * @param fileName * The name of the file containing the document * @param mimeType * The MimeType of the document content (optional) * @param url * The URL of the document content * @return a document object * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws DocumentAttachmentException * when an error occurs while attaching the new version of the document * @since 6.0 */ Document attachNewDocumentVersion(long processInstanceId, String documentName, String fileName, String mimeType, String url) throws DocumentAttachmentException; /** * Attach a new document version to the specified process instance. The document is a new version of the named * document. *

* The content is stored to enable later retrieval. * The author of the document will be the user currently calling the API method. Note that the operations of a task * are not executed by the assigned user, * but directly by the System. It means that calling this method directly from an operation inside a task (through a * groovy script for example) will attach a * document whose author is the user System. *

* * @param processInstanceId * The identifier of the process instance * @param documentName * The name of the document * @param contentFileName * The name of the file containing the content of the document * @param contentMimeType * The MimeType of the document content (optional) * @param documentContent * The content of the document * @return a document object * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws DocumentAttachmentException * when an error occurs while attaching the new version of the document * @since 6.0 */ Document attachNewDocumentVersion(long processInstanceId, String documentName, String contentFileName, String contentMimeType, byte[] documentContent) throws DocumentAttachmentException; /** * Get the document with the specified identifier. * * @param documentId * The identifier of the document to retrieve * @return a document object * @throws DocumentNotFoundException * If the specified identifier does not refer to an existing document. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @since 6.0 */ Document getDocument(long documentId) throws DocumentNotFoundException; /** * Remove the document with the specified identifier and returns it. *

* this archive and delete mapping on the process, i.e. the content of the document itself will be kept in database, * use * {@link #deleteContentOfArchivedDocument} to delete the content *

* * @param documentId * The identifier of the document to retrieve * @return the removed document object * @throws DocumentNotFoundException * If the specified identifier does not refer to an existing document. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @since 6.4.0 */ Document removeDocument(long documentId) throws DocumentNotFoundException, DeletionException; /** * Get the latest version of all documents attached to the specified process instance. * * @param processInstanceId * The identifier of the process instance * @param pageIndex * The index of the page * @param numberPerPage * The number of documents to list per page * @param pagingCriterion * The sort criterion for the returned list * @return the matching list of documents * a paginated list of the latest version of each document attached to the process instance * @throws ProcessInstanceNotFoundException * If the identifier does not refer to an existing process instance. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws DocumentException * when any other error occurs during document handling * @since 6.0 */ List getLastVersionOfDocuments(long processInstanceId, int pageIndex, int numberPerPage, DocumentCriterion pagingCriterion) throws ProcessInstanceNotFoundException, DocumentException; /** * Get content of the document with the specified identifier. * * @param storageId * The identifier of the document to retrieve the content from * @return document content as a byte array * @throws DocumentNotFoundException * If the specified identifier does not refer to an existing document. * @throws org.bonitasoft.engine.session.InvalidSessionException * when the session is note valid * @since 6.0 */ byte[] getDocumentContent(String storageId) throws DocumentNotFoundException; /** * Get the last version of the named document for the specified process instance. * This method does not work on archived process instances. * * @param processInstanceId * The identifier of the process instance that the document is attached to * @param documentName * The name of the document * @return a document object * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws DocumentNotFoundException * If the specified documentName does not refer to an existing document attached to this process instance * @since 6.0 */ Document getLastDocument(long processInstanceId, String documentName) throws DocumentNotFoundException; /** * Get the version of the named document that was current when the specified process instance is instantiated. * This method search in the archives for the document at the date of the process instantiation. * * @param processInstanceId * The identifier of the process instance * @param documentName * The name of the document * @return a document object * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws DocumentNotFoundException * If the specified documentName does not refer to a document attached to the specified process instance. * @since 6.0 */ Document getDocumentAtProcessInstantiation(long processInstanceId, String documentName) throws DocumentNotFoundException; /** * Get the version of the named document when the specified activity completed. * This method search in archives for the document at the date of the activity completion. * * @param activityInstanceId * The identifier of the activity instance * @param documentName * The name of the document * @return a document object * @throws DocumentNotFoundException * If the specified documentName does not refer to an existing document attached to the process instance * that contains the activity. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @since 6.0 */ Document getDocumentAtActivityInstanceCompletion(long activityInstanceId, String documentName) throws DocumentNotFoundException; /** * Get the number of documents attached to the specified process instance. A document with multiple versions is * counted once. * * @param processInstanceId * The process instance identifier * @return the number of documents in the specified process instance * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws DocumentException * when an error occurs during document handling * @since 6.0 */ long getNumberOfDocuments(long processInstanceId) throws DocumentException; /** * Search for documents that match the search options. * * @param searchOptions * A {@link SearchOptions} object defining the search options * @return the matching document list and its total number * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws SearchException * when an error occurs during the search * @since 6.0 */ SearchResult searchDocuments(SearchOptions searchOptions) throws SearchException; /** * Search for documents that match the search options and are supervised by the specified user. * * @param userId * The identifier of the supervising user * @param searchOptions * A {@link SearchOptions} object defining the search options * @return the list of matching documents and the number of such documents * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws UserNotFoundException * when the specified userId does not refer to an existing user * @throws SearchException * when an error occurs during the search * @since 6.0 */ SearchResult searchDocumentsSupervisedBy(long userId, SearchOptions searchOptions) throws UserNotFoundException, SearchException; /** * Search for archived documents that meet the search options. An archived document is a document that is not the * latest version. * -- ex: Retrieve documents of a given archived case -- * *
     * {@code
     *
     * public List retrieveDocuments(DocumentAPI documentAPI,
     *         ArchivedProcessInstance archivedProcessInstance) {
     *     SearchOptions searchOptions = new SearchOptionsBuilder(0, Integer.MAX_VALUE)
     *             .filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID,
     *                     archivedProcessInstance.getSourceObjectId())
     *             .done();
     *     return documentAPI.searchArchivedDocuments(searchOptions).getResult();
     * }
     * }
     * 
* * @param searchOptions * A {@link SearchOptions} object defining the search options * @return the matching archived document list and its total number * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws SearchException * when an error occurs during the search * @since 6.0 */ SearchResult searchArchivedDocuments(SearchOptions searchOptions) throws SearchException; /** * Search for archived documents that match the search options and are supervised by the specified user. An archived * document is a document that is not the * latest version. * * @param userId * The identifier of the supervising user * @param searchOptions * A {@link SearchOptions} object defining the search options * @return the matching archived document list and its total number * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws UserNotFoundException * when the specified userId does not refer to an existing user * @throws SearchException * when an error occurs during the search * @since 6.0 */ SearchResult searchArchivedDocumentsSupervisedBy(long userId, SearchOptions searchOptions) throws UserNotFoundException, SearchException; /** * Get an ArchivedDocument based on it's id. * * @param archivedProcessDocumentId * The identifier of the document * @return an archived document * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws ArchivedDocumentNotFoundException * when the specified identifier does not refer to an archived document * @since 6.0 */ ArchivedDocument getArchivedProcessDocument(final long archivedProcessDocumentId) throws ArchivedDocumentNotFoundException; /** * Get the latest version of the document with the specified identifier. * * @param sourceObjectId * The identifier of the document * @return an archived document * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws ArchivedDocumentNotFoundException * If the identifier does not refer to any existing archived document. * @since 6.0 */ ArchivedDocument getArchivedVersionOfProcessDocument(long sourceObjectId) throws ArchivedDocumentNotFoundException; /** * Get a document list that have the specified name on the process * * @param processInstanceId * The identifier of the process instance that contains the list * @param name * The name of the document * @param fromIndex * The index of the first element to be retrieved (it starts from zero) * @param numberOfResult * The max number of result to get * @return * The document list * @throws DocumentNotFoundException * @since 6.4.0 */ List getDocumentList(long processInstanceId, String name, int fromIndex, int numberOfResult) throws DocumentNotFoundException; /** * Set the document list on a specified process * * @param processInstanceId * The identifier of the process instance that contains the list * @param name * The name of the document list * @param documentsValues the values to set the list with * @throws DocumentException * If an error occurs * @see org.bonitasoft.engine.bpm.process.ProcessInstance#getId() * @since 6.4.0 */ void setDocumentList(long processInstanceId, String name, List documentsValues) throws DocumentNotFoundException, DocumentException; /** * Remove the content of an archived document while keeping it's metadata. *

* After calling this method you will not be able to retrieve the content of the document since it will be erased * from the database. * This method can be useful for keeping history of a document without overloading the database. *

* * @param archivedDocumentId * The identifier of the archived document to remove content on * @throws DocumentNotFoundException * If the identifier does not refer to any existing archived document. * @throws DocumentException * If an error occurs * @see ArchivedDocument#getId() * @since 6.4.0 */ void deleteContentOfArchivedDocument(long archivedDocumentId) throws DocumentException, DocumentNotFoundException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/Experimental.java ================================================ /** * 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.api; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.CLASS; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * An experimental API that can be used but might change in any version of Bonita. * Also no guarantee is provided on the correct behavior of this API. * * @author Baptiste Mesta. */ @Retention(value = CLASS) @Target(value = { TYPE, METHOD, PACKAGE }) public @interface Experimental { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/GroupAPI.java ================================================ /** * 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.api; import java.util.List; import java.util.Map; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.GroupCreator; import org.bonitasoft.engine.identity.GroupCriterion; import org.bonitasoft.engine.identity.GroupNotFoundException; import org.bonitasoft.engine.identity.GroupUpdater; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; /** * GroupAPI forms part of the {@link OrganizationAPI} and gives access to all the Administration operations available on * Groups: creation, deletion, updating, * search, etc... * * @author Zhao Na * @author Bole Zhang * @author Matthieu Chaffotte * @author Hongwen Zang * @see Group */ public interface GroupAPI { /** * Creates a group from its name and parent path. *

* If the group is a top level one, the parent path must be null. *

* * @param name * the name of the group * @param parentPath * the parent path of the group (null means no parent) * @return the created group * @throws AlreadyExistsException * If the couple name/parentPath is already taken by an existing group * @throws CreationException * If an exception occurs during the group creation * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ Group createGroup(String name, String parentPath) throws AlreadyExistsException, CreationException; /** * Creates a group. *

* It takes the values of the creator in order to create a group. *

* * @param creator * the group creator * @return the created group * @throws AlreadyExistsException * If the couple name/parentPath is already taken by an existing group * @throws CreationException * If an exception occurs during group creation * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ Group createGroup(GroupCreator creator) throws AlreadyExistsException, CreationException; /** * Updates the group according to the updater values. *

* This method also allow to update the icon of the group. * When you update it, the iconId will be set on the group and you can later get it using * {@link IdentityAPI#getIcon(long)}. * Changing the content of the icon will create a new icon and change the iconId of the group. *

* * @param groupId * the identifier of the group * @param updater * the group updater * @return the updated group * @throws GroupNotFoundException * If the group identifier does not refer to an existing group * @throws UpdateException * If an exception occurs during the group update * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ Group updateGroup(long groupId, GroupUpdater updater) throws GroupNotFoundException, UpdateException, AlreadyExistsException; /** * Deletes the group. * * @param groupId * the identifier of the group * @throws DeletionException * If an exception occurs during the group deletion * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ void deleteGroup(long groupId) throws DeletionException; /** * Deletes the groups. * * @param groupIds * the list of group identifiers * @throws DeletionException * If an exception occurs during the group deletion * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ void deleteGroups(List groupIds) throws DeletionException; /** * Retrieves the group. * * @param groupId * the group identifier * @return the group * @throws GroupNotFoundException * If the group identifier does not refer to an existing group * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the group retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ Group getGroup(long groupId) throws GroupNotFoundException; /** * Retrieves the group according to its full path. * * @param groupPath * the full path of the group (parentPath/name) * @return the group * @throws GroupNotFoundException * If the group path does not refer to an existing group * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the group retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ Group getGroupByPath(final String groupPath) throws GroupNotFoundException; /** * Returns the total number of groups. * * @return the total number of groups * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the count retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ long getNumberOfGroups(); /** * Retrieves the paginated list of groups. *

* It retrieves from the startIndex to the startIndex + maxResults. *

* * @param startIndex * the start index * @param maxResults * the max number of groups * @param criterion * the sorting criterion * @return the list of groups * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the group retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ List getGroups(int startIndex, int maxResults, GroupCriterion criterion); /** * Retrieves the groups. *

* The map contains the couples groupId/Group. * If a group does not exists, no exception is thrown and no value is added in the map. *

* * @param groupIds * the identifiers of the groups * @return the groups * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the group retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ Map getGroups(List groupIds); /** * Searches groups according to the criteria containing in the options. * * @param options * the search criteria * @return the search result * @throws SearchException * If an exception occurs during the group searching * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ SearchResult searchGroups(SearchOptions options) throws SearchException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/IdentityAPI.java ================================================ /** * 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.api; /** * The Interface IdentityAPI. * * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Feng Hui * @author Bole Zhang * @author Yanyan Liu */ public interface IdentityAPI extends UserAPI, RoleAPI, GroupAPI, MembershipAPI, OrganizationAPI, CustomUserInfoAPI { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ImportError.java ================================================ /** * 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.api; import java.io.Serializable; /** * @author Baptiste Mesta * @since 6.3.1 */ public class ImportError implements Serializable { private static final long serialVersionUID = 1L; public enum Type { USER, GROUP, ROLE, PAGE, PROFILE, APPLICATION_PAGE, LAYOUT, THEME } private final String name; private final Type type; public ImportError(final String name, final Type type) { super(); this.name = name; this.type = type; } public String getName() { return name; } public Type getType() { return type; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("ImportError [name="); builder.append(name); builder.append(", type="); builder.append(type); builder.append("]"); return builder.toString(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (name == null ? 0 : name.hashCode()); result = prime * result + (type == null ? 0 : type.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final ImportError other = (ImportError) obj; if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } if (type != other.type) { return false; } return true; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ImportStatus.java ================================================ /** * 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.api; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import lombok.Data; /** * This object represents the status of the import of an entity * e.g. for an import of profile this object can be ImportStatus [name=MyProfile, status=ADDED, errors=[]] * * @author Baptiste Mesta * @since 6.3.1 */ @Data public class ImportStatus implements Serializable { public enum Status { ADDED, REPLACED, SKIPPED } private final String name; private Status status = Status.ADDED; private List errors = new ArrayList<>(); public void addError(final ImportError error) { errors.add(error); } public void addErrors(final List errors) { this.errors.addAll(errors); } public void addErrorsIfNotExists(final List errors) { for (ImportError importError : errors) { if (importError != null && !getErrors().contains(importError)) { addError(importError); } } } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/Internal.java ================================================ /** * 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.api; /** * Element annotated with @Internal are for internal usage only and may change without any warning * * @author Baptiste Mesta */ public @interface Internal { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/Logger.java ================================================ /** * 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.api; /** * A logger given to {@link org.bonitasoft.engine.api.permission.PermissionRule} * * @author Baptiste Mesta */ public interface Logger { void trace(String message, Throwable t); void trace(String message); void debug(String message, Throwable t); void debug(String message); void info(String message, Throwable t); void info(String message); void warning(String message, Throwable t); void warning(String message); void error(String message, Throwable t); void error(String message); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/LoginAPI.java ================================================ /** * 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.api; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.platform.LoginException; import org.bonitasoft.engine.platform.LogoutException; import org.bonitasoft.engine.platform.UnknownUserException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.SessionNotFoundException; /** * The LoginAPI allows to log in (and out) onto the Engine. This is a mandatory step to go further using the Engine * APIs. * Other Engine APIs are only accessible through the returned APISession. * * @author Matthieu Chaffotte * @author Zhang Bole * @see APISession */ @NoSessionRequired public interface LoginAPI { /** * Connects the user in order to use API methods of the default tenant. *

* Brute-force protection for web-based login requests is handled at the servlet layer, * which enforces per-user rate limiting with HTTP 429 responses after repeated failures. * Callers using the Java API directly (embedded engine, without the web layer) should * implement their own rate-limiting or account lockout mechanism if the login endpoint * is exposed to untrusted input. * * @param userName * the user name * @param password * the password * @return the session to use with other tenant API methods * @throws LoginException * occurs when an exception is thrown during login * @throws UnknownUserException * occurs when the user trying to login is unknown to the engine */ APISession login(String userName, String password) throws LoginException, UnknownUserException; /** * Connects the user in order to use API methods of the default tenant. *

* Brute-force protection for web-based login requests is handled at the servlet layer, * which enforces per-user rate limiting with HTTP 429 responses after repeated failures. * Callers using the Java API directly (embedded engine, without the web layer) should * implement their own rate-limiting or account lockout mechanism if the login endpoint * is exposed to untrusted input. * * @param credentials * the properties to use to login * @return the session to use with other tenant API methods * @throws LoginException * occurs when an exception is thrown during login */ APISession login(Map credentials) throws LoginException; /** * Disconnects the logged user on a tenant according to the given session. * * @param session * the tenant session * @throws SessionNotFoundException * if the given session is not found on the server side. This may occur when the session has expired. * @throws LogoutException * occurs when an exception is thrown during the logout */ void logout(APISession session) throws SessionNotFoundException, LogoutException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/MaintenanceAPI.java ================================================ /** * Copyright (C) 2023 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.api; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.maintenance.MaintenanceDetails; import org.bonitasoft.engine.maintenance.MaintenanceDetailsNotFoundException; import org.bonitasoft.engine.platform.PlatformNotFoundException; /** * This API gives access to maintenance administration tasks such as enabling maintenance mode and enabling/disabling * maintenance message. */ public interface MaintenanceAPI { /** * Retrieve platform maintenance details * * @return MaintenanceInfo * @throws MaintenanceDetailsNotFoundException * @throws PlatformNotFoundException */ MaintenanceDetails getMaintenanceDetails() throws MaintenanceDetailsNotFoundException, PlatformNotFoundException; /** * Enable maintenance mode * This method replaces {@link TenantAdministrationAPI#pause()} * When maintenance mode is enabled, All BPM and BDM APIs are not accessible. * * @throws UpdateException * if maintenance state cannot be updated. */ void enableMaintenanceMode() throws UpdateException; /** * Disable maintenance mode * This method replaces {@link TenantAdministrationAPI#resume()} * * @throws UpdateException * if maintenance state cannot be updated. */ void disableMaintenanceMode() throws UpdateException; /** * Update maintenance message * This message will be displayed in bonita apps if enabled * * @throws UpdateException * if maintenance message cannot be updated. */ void updateMaintenanceMessage(String message) throws UpdateException; /** * Enable maintenance message * * @throws UpdateException * if maintenance message cannot be enabled. */ void enableMaintenanceMessage() throws UpdateException; /** * Disable maintenance message * * @throws UpdateException * if maintenance message cannot be disabled. */ void disableMaintenanceMessage() throws UpdateException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/MembershipAPI.java ================================================ /** * 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.api; import java.util.List; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.MembershipNotFoundException; import org.bonitasoft.engine.identity.UserMembership; import org.bonitasoft.engine.identity.UserMembershipCriterion; /** * MembershipAPI forms part of the {@link OrganizationAPI} and gives access to all the Administration operations * available on UserMemberships: * creation, deletion, updating, retrieval, etc... * * @author Matthieu Chaffotte * @author Emmanuel Duchastenier * @see UserMembership */ public interface MembershipAPI { /** * Associates the user with the group and the role. * The association is called a user membership. * * @param userId * the identifier of the user * @param groupId * the identifier of the group * @param roleId * the identifier of the role * @return the user membership * @throws AlreadyExistsException * If the triplet userId/groupId/roleId is already taken by an existing user membership * @throws CreationException * If an exception occurs during the user membership creation * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ UserMembership addUserMembership(long userId, long groupId, long roleId) throws AlreadyExistsException, CreationException; /** * Associates the users with the group and the role. * The association is called a user membership. * * @param userIds * the identifiers of the users * @param groupId * the identifier of the group * @param roleId * the identifier of the role * @throws AlreadyExistsException * If the triplet userId/groupId/roleId is already taken by an existing user membership * @throws CreationException * If an exception occurs during the user membership creation * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ void addUserMemberships(List userIds, long groupId, long roleId) throws AlreadyExistsException, CreationException; /** * Changes the association of the user membership. * It associates the user membership to the new role and group identifiers. * * @param userMembershipId * the identifier of the user membership * @param newGroupId * the identifier of the new group * @param newRoleId * the identifier of the new role * @return the updated user membership * @throws MembershipNotFoundException * If the identifier of the user membership does not refer to an existing user membership * @throws UpdateException * If an exception occurs during the user membership update * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ UserMembership updateUserMembership(long userMembershipId, long newGroupId, long newRoleId) throws MembershipNotFoundException, UpdateException; /** * Deletes the user membership. * * @param userMembershipId * the identifier of the user membership * @throws DeletionException * If an exception occurs during the user membership deletion * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ void deleteUserMembership(long userMembershipId) throws DeletionException; /** * Deletes the user membership. * * @param userId * the identifier of the user * @param groupId * the identifier of the group * @param roleId * the identifier of the role * @throws DeletionException * If an exception occurs during the user membership deletion * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ void deleteUserMembership(long userId, long groupId, long roleId) throws DeletionException; /** * Deletes the user memberships. * * @param userIds * the identifiers of the users * @param groupId * the identifier of the group * @param roleId * the identifier of the role * @throws DeletionException * If an exception occurs during the user membership deletion * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ void deleteUserMemberships(List userIds, long groupId, long roleId) throws DeletionException; /** * Retrieves the user membership. * * @param membershipId * the identifier of the user membership to retrieve. * @return the found UserMembership with the provided id * @throws MembershipNotFoundException * If the identifier of the user membership does not refer to an existing user membership * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the user membership retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) */ UserMembership getUserMembership(long membershipId) throws MembershipNotFoundException; /** * Returns the total number of memberships of the user. * * @param userId * the identifier of the user * @return the total number of memberships of the user * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the count retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) */ long getNumberOfUserMemberships(long userId); /** * Retrieves the paginated list of user memberships of the user. * It retrieves from the startIndex to the startIndex + maxResults. * * @param userId * the identifier of the user * @param startIndex * the start index * @param maxResults * the max number of user memberships * @param criterion * the sorting criterion * @return the paginated list of user memberships * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the count retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ List getUserMemberships(long userId, int startIndex, int maxResults, UserMembershipCriterion criterion); /** * Retrieves the paginated list of user memberships of the group. * It retrieves from the startIndex to the startIndex + maxResults. * * @param groupId * the identifier of the group * @param startIndex * the start index * @param maxResults * the max number of user memberships * @return the paginated list of user memberships * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the count retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ List getUserMembershipsByGroup(long groupId, final int startIndex, final int maxResults); /** * Retrieves the paginated list of user memberships of the role. * It retrieves from the startIndex to the startIndex + maxResults. * * @param roleId * the identifier of the role * @param startIndex * the start index * @param maxResults * the max number of user memberships * @return the paginated list of user memberships * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the count retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ List getUserMembershipsByRole(long roleId, int startIndex, int maxResults); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/NoSessionRequired.java ================================================ /** * 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.api; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Indicates that a Bonita API method does not need to have a valid session to be called. * No session required means that there is no transaction. * * @author Emmanuel Duchastenier */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface NoSessionRequired { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/OrganizationAPI.java ================================================ /** * 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.api; import java.util.List; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.business.application.Application; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.identity.ImportPolicy; import org.bonitasoft.engine.identity.OrganizationExportException; import org.bonitasoft.engine.identity.OrganizationImportException; import org.bonitasoft.engine.identity.UserUpdater; /** * Manages the Organization, that is the users, groups, roles, memberships, through import / export methods. * * @author Yanyan Liu * @author Matthieu Chaffotte * @author Emmanuel Duchastenier */ public interface OrganizationAPI { /** * Deletes the organization. *

* It deletes all user memberships, roles, groups, users and custom user info. *

*

Use this method with caution: some artifacts like {@link Application}s and {@link DesignProcessDefinition}s * may present display problems in the Bonita * BPM Portal if the referenced user was deleted. Note that you can disable a user instead of deleting it. To do so, * use the method * {@link IdentityAPI#updateUser(long, UserUpdater)} to set the attribute 'enabled' to false

. * * @throws DeletionException * If an exception occurs during the organization deletion * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0.0 * @see IdentityAPI#updateUser(long, UserUpdater) * @see Application * @see DesignProcessDefinition */ void deleteOrganization() throws DeletionException; /** * Imports the organization using the {@link ImportPolicy#MERGE_DUPLICATES} policy. *

* An organization is composed by users, roles, groups and user memberships. *

* * @param organizationContent * the XML content of the organization * @throws OrganizationImportException * If an exception occurs during the organization import * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0.0 */ void importOrganization(String organizationContent) throws OrganizationImportException; /** * Imports the organization. Returns the error/info messages about what occurred during the process. * Functionally this method informs the end-user of particular non-critical events that occurred during the import. *

* Ex: If a group in the organization contains an illegal character in their name, the method will import all the * other * groups in the organization, and return a List with one String : "The group name (...) contains the illegal * character (...). The group has not been imported" *

*

* An organization is composed by users, roles, groups and user memberships. *

* * @return List of warning messages * @param organizationContent * the XML content of the organization * @param policy * the import policy * @throws OrganizationImportException * If an exception occurs during the organization import * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) */ List importOrganizationWithWarnings(String organizationContent, ImportPolicy policy) throws OrganizationImportException; /** * Exports the organization. *

* An organization is composed by users, roles, groups and user memberships. *

* * @return the organization contented in an XML format * @throws OrganizationExportException * If an exception occurs during the organization export * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0.0 */ String exportOrganization() throws OrganizationExportException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/PageAPI.java ================================================ /** * 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.api; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.Properties; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.ExecutionException; import org.bonitasoft.engine.exception.InvalidPageTokenException; import org.bonitasoft.engine.exception.InvalidPageZipContentException; import org.bonitasoft.engine.exception.InvalidPageZipInconsistentException; import org.bonitasoft.engine.exception.InvalidPageZipMissingAPropertyException; import org.bonitasoft.engine.exception.InvalidPageZipMissingIndexException; import org.bonitasoft.engine.exception.InvalidPageZipMissingPropertiesException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UnauthorizedAccessException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.exception.UpdatingWithInvalidPageTokenException; import org.bonitasoft.engine.exception.UpdatingWithInvalidPageZipContentException; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.page.PageCreator; import org.bonitasoft.engine.page.PageNotFoundException; import org.bonitasoft.engine.page.PageURL; import org.bonitasoft.engine.page.PageUpdater; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; /** * This API gives access to all page features. Page is a way to add pages on portal. *

* Also allows to manipulate Pages, through creation, deletion, search. *

* * @author Laurent Leseigneur * @see org.bonitasoft.engine.page.Page */ public interface PageAPI { /** * Retrieves a page from its ID. * * @param pageId * the Identifier of the page to retrieve * @return the found page * @throws org.bonitasoft.engine.page.PageNotFoundException * if no page can be found with the provided ID. */ Page getPage(final long pageId) throws PageNotFoundException; /** * Retrieves a page from its name. * * @param name * the name of the page to retrieve * @return the found page * @throws PageNotFoundException * if no page can be found with the provided page or is assigned with a process definition. */ Page getPageByName(final String name) throws PageNotFoundException; /** * Retrieves a page from its name and processDefinitionId. * * @param name * the name of the page to retrieve * @param processDefinitionId * the process definition ID associated to the page * @return the found page * @throws PageNotFoundException * if no page can be found with the provided name and process definition ID. */ Page getPageByNameAndProcessDefinitionId(final String name, long processDefinitionId) throws PageNotFoundException; /** * Retrieves the binary content of a page. * * @param pageId * the ID of the page to extract the content for. * @return * the binary content of the page. * @throws PageNotFoundException * if no page can be found with the provided ID. */ byte[] getPageContent(final long pageId) throws PageNotFoundException; /** * Searches for pages with specific search criteria. * * @param searchOptions * the search options for the search. See {@link org.bonitasoft.engine.search.SearchOptions} for search * option details. * @return the SearchResult containing * @throws org.bonitasoft.engine.exception.SearchException * if a problem occurs during the search. */ SearchResult searchPages(final SearchOptions searchOptions) throws SearchException; /** * Creates a custom page. * Note that if called from an operation in a task, the author of the page will always be the user System. * * @param pageCreator * the creator object to instantiate the new page. * @param content * the binary content of the page. * @return the newly created page. * @throws org.bonitasoft.engine.exception.AlreadyExistsException * if a page with this name already exists. * @throws org.bonitasoft.engine.exception.CreationException * if an error occurs during the creation. * @deprecated as of 9.0.0. Page content should be created at startup. */ @Deprecated(since = "9.0.0") Page createPage(final PageCreator pageCreator, final byte[] content) throws AlreadyExistsException, CreationException, InvalidPageTokenException, InvalidPageZipContentException; /** * Updates a custom page. * * @param pageId * the Identifier of the page to update * @param pageUpdater * the creator object to instantiate the new page. * @return the newly created page. * @throws org.bonitasoft.engine.exception.UpdateException * if an error occurs during the update. * @throws org.bonitasoft.engine.exception.AlreadyExistsException * if a page with this name already exists. * @deprecated as of 9.0.0. Page content should be updated at startup. */ @Deprecated(since = "9.0.0") Page updatePage(final long pageId, final PageUpdater pageUpdater) throws UpdateException, AlreadyExistsException, UpdatingWithInvalidPageTokenException, UpdatingWithInvalidPageZipContentException; /** * Updates a custom page content. * it read the page.properties inside to update the page properties * * @param pageId * the Identifier of the page to update * @param content * the binary content of the page. * @throws org.bonitasoft.engine.exception.UpdateException * if an error occurs during the update. * @deprecated as of 9.0.0. Page content should be updated at startup. */ @Deprecated(since = "9.0.0") void updatePageContent(final long pageId, final byte[] content) throws UpdateException, UpdatingWithInvalidPageTokenException, UpdatingWithInvalidPageZipContentException; /** * Deletes a page identified by its ID. * * @param pageId * the page identifier to delete. * @throws org.bonitasoft.engine.exception.DeletionException * if a problem occurs during deletion. */ void deletePage(final long pageId) throws DeletionException; /** * Deletes a list of pages, given by their IDs. * * @param pageIds * a list of page identifiers to delete. * @throws org.bonitasoft.engine.exception.DeletionException * if a problem occurs during deletion. */ void deletePages(final List pageIds) throws DeletionException; /** * create a page using the given content * the content must contain a page.properties file that contains information on the page: * name, displayName and description. Be aware that this method does not update your web permission-mappings. * It means that rest api extensions created with this method will not be accessible from the portal. * To avoid the problem, either use the create * page rest api or upload the extensions directly from Bonita Portal. * Note that if called from an operation in a task, the author of the page will always be the user System. * * @param contentName * name of the zip file containing the page * @param content * content of the zip file containing the page * @return * the created page * @throws org.bonitasoft.engine.exception.AlreadyExistsException * if a page with the same name already exists * @throws org.bonitasoft.engine.exception.CreationException * @since 6.3.1 * @deprecated as of 9.0.0. Page content should be created at startup. */ @Deprecated(since = "9.0.0") Page createPage(String contentName, byte[] content) throws AlreadyExistsException, CreationException, InvalidPageTokenException, InvalidPageZipContentException; /** * Read the content of the page zip file check it is consistent and return it's properties * * @param content * content of the zip file containing the page * @return * the properties of the page * @since 6.4.0 */ Properties getPageProperties(byte[] content, boolean checkIfItAlreadyExists) throws InvalidPageTokenException, AlreadyExistsException, InvalidPageZipMissingPropertiesException, InvalidPageZipMissingIndexException, InvalidPageZipInconsistentException, InvalidPageZipMissingAPropertyException; /** * Resolves a Page URL from a specific key. * * @param key the key of the page to resolve. * @return the PageURL containing the pageId or the complete * @throws NotFoundException if the key does not match anything. * @see PageURL the structured PageURL that points to the Page or URL */ PageURL resolvePageOrURL(String key, Map context, boolean executeAuthorizationRules) throws NotFoundException, UnauthorizedAccessException, ExecutionException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/PermissionAPI.java ================================================ /** * 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.api; import java.util.Set; import org.bonitasoft.engine.api.permission.APICallContext; import org.bonitasoft.engine.exception.ExecutionException; /** * Handle permissions of users * * @author Baptiste Mesta */ public interface PermissionAPI { /** * Checks if the REST API request defined in the {@link APICallContext} is authorized for the logged in user * * @param apiCallContext * contains all the attributes of the request * @return true or false depending if the user it authorized to make the call or not * @throws ExecutionException if there was an error while executing the authorization checks */ boolean isAuthorized(APICallContext apiCallContext) throws ExecutionException; /** * Returns the REST permissions required to access a REST resource (the expected format for the resource key is * |/ e.g. GET|identity/user) * * @param resourceKey * the resource identifier. The expected format is |/ (e.g. * GET|identity/user) * @return a Set of permissions, as Strings. e.g. ["organization_visualization"] */ Set getResourcePermissions(String resourceKey); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/PlatformAPI.java ================================================ /** * 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.api; import java.util.Map; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.platform.*; /** * Manage the platform. *

* The platform is the base on which runs the engine.
* It mainly handles the creation of tables in database and also allow to start/stop a Node which is the current Virtual * machine on which runs the engine. There * is only one platform for a running Bonita Engine. *

* * @author Elias Ricken de Medeiros * @author Lu Kai * @author Zhang Bole * @author Matthieu Chaffotte * @author Emmanuel Duchastenier */ public interface PlatformAPI { /** * Initialize the platform. * The running environment of Bonita Engine is initialized and marked as activated.
* Business elements linked to the execution are initialized, after this step the technical user will be able to * connect to the engine and to import the * * @throws org.bonitasoft.engine.session.InvalidSessionException * occurs if the API session is invalid, e.g session has expired. * @throws CreationException * occurs when an exception is thrown during platform creation * @deprecated Not useful anymore, initialization is done by the setup tool */ @Deprecated(forRemoval = true, since = "8.0.0") void initializePlatform() throws CreationException; /** * Starts the node. *

* The node is the currently Java Virtual Machine on which Bonita Engine is running *

* Starting the node make the Scheduler service to start and restart elements that were not finished by the Work * service on the previous shutdown. * * @throws org.bonitasoft.engine.session.InvalidSessionException * occurs if API Session is invalid, e.g session has expired. * @throws StartNodeException * occurs when an exception is thrown during the activation of the node */ void startNode() throws StartNodeException; /** * Stops the node. *

* The node is the currently Java Virtual Machine on which Bonita Engine is running *

* Stopping the node make the Scheduler service to stop. * * @throws org.bonitasoft.engine.session.InvalidSessionException * occurs if API Session is invalid, e.g session has expired. * @throws StopNodeException * occurs when an exception is thrown during the stop of the node */ void stopNode() throws StopNodeException; /** * Get the platform. * * @return the Platform object * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws PlatformNotFoundException * occurs when the identifier does not refer to an existing platform */ Platform getPlatform() throws PlatformNotFoundException; /** * Check if the platform created or not. * * @return true if the platform exists * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws PlatformNotFoundException * occurs when the identifier does not refer to an existing platform */ boolean isPlatformCreated() throws PlatformNotFoundException; /** * Get the state of the platform of the current node * * @return {@link PlatformState#STARTED} or {@link PlatformState#STOPPED} depending on the scheduler state * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws PlatformNotFoundException * occurs when the identifier does not refer to an existing platform */ PlatformState getPlatformState() throws PlatformNotFoundException; /** * Is the current node started? * * @return true if the node is started, false if not started or if its state cannot be determined. * @since 6.1 */ boolean isNodeStarted(); /** * Reschedules triggers which are in error state. * * @throws UpdateException * If an exception occurs during the scheduling * @since 6.2 */ void rescheduleErroneousTriggers() throws UpdateException; /** * INTERNAL USE ONLY * get client configuration files of the platform * * @return the client platform configuration files as a map containing file name and file content * @since 7.3 */ @Internal Map getClientPlatformConfigurations(); /** * INTERNAL USE ONLY * Get client configuration files * * @return the client tenants configuration files as a map of "file name" and "file content" * @since 7.3 */ @Internal Map getClientTenantConfigurations(); /** * INTERNAL USE ONLY * get a specific client configuration file * * @return file content * @since 7.3 */ @Internal byte[] getClientTenantConfiguration(String file); /** * INTERNAL USE ONLY * update a single client configuration file * * @param file file name to update * @param content the new content of the file * @since 7.3 */ @Internal void updateClientTenantConfigurationFile(String file, byte[] content) throws UpdateException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/PlatformCommandAPI.java ================================================ /** * 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.api; import java.io.Serializable; import java.util.List; import java.util.Map; import org.bonitasoft.engine.command.CommandCriterion; import org.bonitasoft.engine.command.CommandDescriptor; import org.bonitasoft.engine.command.CommandExecutionException; import org.bonitasoft.engine.command.CommandNotFoundException; import org.bonitasoft.engine.command.CommandParameterizationException; import org.bonitasoft.engine.command.CommandUpdater; import org.bonitasoft.engine.command.DependencyNotFoundException; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.UpdateException; /** * Manipulates a platform command. A command can be registered, unregistered and executed with parameters.
* These commands are executed in a platform scope, see {@link CommandAPI} for an explanation of how to deploy, execute, * ... a command. The only * difference between the {@link CommandAPI} and the {@link PlatformCommandAPI} is that a platform command must extend * {@code org.bonitasoft.engine.command.RuntimeCommand}. * * @author Matthieu Chaffotte * @author Emmanuel Duchastenier * @see CommandDescriptor */ public interface PlatformCommandAPI { /** * Adds a dependency. * * @param name * the dependency name. * @param jar * the JAR content * @throws org.bonitasoft.engine.session.InvalidSessionException * occurs when the session is not valid * @throws AlreadyExistsException * occurs when the dependency name is already used for another dependency * @throws CreationException * if a problem occurs when creating the dependency */ void addDependency(String name, byte[] jar) throws AlreadyExistsException, CreationException; /** * Removes the dependency. * * @param name * the dependency name. * @throws org.bonitasoft.engine.session.InvalidSessionException * if the current platform session is not valid * @throws DependencyNotFoundException * if no dependency can be found with the specified name * @throws DeletionException * if a problem occurs when deleting the dependency */ void removeDependency(String name) throws DependencyNotFoundException, DeletionException; /** * Adds a command and its descriptor. * * @param name * the command name * @param description * the command description * @param implementation * the implementation class which will be used when executing the command * @return the descriptor of the command * @throws org.bonitasoft.engine.session.InvalidSessionException * occurs when the session is not valid * @throws AlreadyExistsException * occurs when the command name is already used for another command * @throws CreationException * if a problem occurs when registering the command */ CommandDescriptor register(String name, String description, String implementation) throws AlreadyExistsException, CreationException; /** * Executes a command according to its name and the list of parameters. * * @param name * the command name * @param parameters * the parameters (specific to the command to execute) * @return the result of the command execution * @throws org.bonitasoft.engine.session.InvalidSessionException * occurs when the session is not valid * @throws CommandNotFoundException * occurs when the name does not refer to any existing command * @throws CommandParameterizationException * when command parameters are not correct * @throws CommandExecutionException * occurs when an exception is thrown during command execution */ Serializable execute(String name, Map parameters) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException; /** * Deletes a command and its descriptor. * * @param name * the command name * @throws org.bonitasoft.engine.session.InvalidSessionException * occurs when the session is not valid * @throws CommandNotFoundException * occurs when the name does not refer to an existing command * @throws DeletionException * occurs when an exception is thrown during command deletion */ void unregister(String name) throws CommandNotFoundException, DeletionException; /** * Returns the command descriptor corresponding to the given command name. * * @param commandName * the name of the command * @return the descriptor of the command * @throws org.bonitasoft.engine.session.InvalidSessionException * occurs when the session is not valid * @throws CommandNotFoundException * occurs when the command name does not refer to an existing command. * @since 6.2.1 */ CommandDescriptor getCommand(String commandName) throws CommandNotFoundException; /** * Returns the paginated list of command descriptors according to the sort criterion. * * @param startIndex * the start index * @param maxResults * the number of {@link CommandDescriptor} to retrieve * @param sort * the sorting criterion * @return the paginated list of command descriptors * @throws org.bonitasoft.engine.session.InvalidSessionException * occurs when the session is not valid */ List getCommands(int startIndex, int maxResults, CommandCriterion sort); /** * Updates a command according to the update descriptor. * * @param name * the command name * @param updater * the update descriptor * @throws org.bonitasoft.engine.session.InvalidSessionException * if the session is not valid * @throws CommandNotFoundException * occurs when the name does not refer to any existing command * @throws UpdateException * occurs when an exception is thrown during command update */ void update(String name, CommandUpdater updater) throws CommandNotFoundException, UpdateException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/PlatformLoginAPI.java ================================================ /** * 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.api; import org.bonitasoft.engine.platform.InvalidPlatformCredentialsException; import org.bonitasoft.engine.platform.PlatformLoginException; import org.bonitasoft.engine.platform.PlatformLogoutException; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.engine.session.SessionNotFoundException; /** * Manage the login on the platform. *

* Using this API you can obtain a {@link PlatformSession} that can be used to retreive a PlatformAPIAccessor. *

* PlatformSession gives access to platform APIs only: *

    *
  • {@link PlatformAPI}
  • *
  • {@link PlatformCommandAPI}
  • *
* * @author Matthieu Chaffotte * @author Baptiste Mesta * @author Emmanuel Duchastenier */ @NoSessionRequired public interface PlatformLoginAPI { /** * Login with username and password of the platform administrator defined in {@code bonita-platform.properties} * * @param userName * the platform administrator name * @param password * the platform administrator password * @return * the session created for you, can be used to retrieve platform APIs * @throws PlatformLoginException * occurs when an exception is thrown during login the platform * @throws InvalidPlatformCredentialsException * occurs when thr username or password is not valid */ PlatformSession login(String userName, String password) throws PlatformLoginException, InvalidPlatformCredentialsException; /** * Logout from a platform. * * @param session * the platform session to logout from. * @throws PlatformLogoutException * occurs when an exception is thrown during logout the platform * @throws SessionNotFoundException * if the session is not found on the server side. This may occurs when the session has expired. */ void logout(PlatformSession session) throws PlatformLogoutException, SessionNotFoundException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ProcessAPI.java ================================================ /** * 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.api; /** * Contains all methods that handle processes. * Using this API you can handle: *
    *
  • {@link ProcessRuntimeAPI Execution of processes}: start process, retrieve tasks, execute tasks, retrieve * data...
  • *
  • {@link ProcessManagementAPI Management of processes}: Deploy/Undeploy processes, enable/disable process...
  • *
  • {@link DocumentAPI Documents}: create, list, retrieve documents
  • *
* * @see ProcessRuntimeAPI * @see ProcessManagementAPI * @see DocumentAPI * @author Baptiste Mesta */ public interface ProcessAPI extends ProcessManagementAPI, ProcessRuntimeAPI, DocumentAPI { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ProcessManagementAPI.java ================================================ /** * 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.api; import java.io.FileNotFoundException; import java.util.List; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.actor.ActorCriterion; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.actor.ActorMappingExportException; import org.bonitasoft.engine.bpm.actor.ActorMappingImportException; import org.bonitasoft.engine.bpm.actor.ActorMember; import org.bonitasoft.engine.bpm.actor.ActorNotFoundException; import org.bonitasoft.engine.bpm.actor.ActorUpdater; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.engine.bpm.category.CategoryCriterion; import org.bonitasoft.engine.bpm.category.CategoryNotFoundException; import org.bonitasoft.engine.bpm.category.CategoryUpdater; import org.bonitasoft.engine.bpm.connector.ConnectorCriterion; import org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor; import org.bonitasoft.engine.bpm.connector.ConnectorNotFoundException; import org.bonitasoft.engine.bpm.data.DataDefinition; import org.bonitasoft.engine.bpm.flownode.ActivityDefinitionNotFoundException; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.parameter.ParameterCriterion; import org.bonitasoft.engine.bpm.parameter.ParameterInstance; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.InvalidProcessDefinitionException; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.bpm.process.ProcessActivationException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessDeployException; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoUpdater; import org.bonitasoft.engine.bpm.process.ProcessEnablementException; import org.bonitasoft.engine.bpm.process.ProcessExportException; import org.bonitasoft.engine.bpm.process.ProcessResourceNotFoundException; import org.bonitasoft.engine.bpm.process.V6FormDeployException; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.FormMappingNotFoundException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.form.FormMapping; import org.bonitasoft.engine.form.FormMappingSearchDescriptor; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; /** * This API deals with definition objects such as {@link ProcessDefinition}, {@link ProcessDeploymentInfo}, * {@link Category}, ... * It enables interaction with the lifecycle of the process definition. * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Yanyan Liu * @author Hongwen Zang * @author Zhang Bole * @author Elias Ricken de Medeiros * @author Celine Souchet * @author Arthur Freycon * @author Emmanuel Duchastenier * @version 6.3.5 * @since 6.0.0 */ public interface ProcessManagementAPI { /** * Deploys a {@link BusinessArchive} which contains a {@link DesignProcessDefinition} and its dependencies. * * @param businessArchive * The archive to deploy. * @return The process definition. * @throws AlreadyExistsException * If a process with same name and version is already deployed. * @throws ProcessDeployException * If an exception occurs when deploying the archive. * @throws V6FormDeployException * If the deployed process contains v6 forms, not supported anymore. * @see BusinessArchive * @since 6.0 */ ProcessDefinition deploy(BusinessArchive businessArchive) throws AlreadyExistsException, ProcessDeployException, V6FormDeployException; /** * Deploys a simple {@link DesignProcessDefinition} (without any dependencies). * * @param designProcessDefinition * The description of a process definition. * @return The process definition corresponding of the description. * @throws AlreadyExistsException * If a process with same name and version is already deployed. * @throws ProcessDeployException * If an exception occurs when deploying the process. * @see #deploy(BusinessArchive) * @since 6.0 */ ProcessDefinition deploy(DesignProcessDefinition designProcessDefinition) throws AlreadyExistsException, ProcessDeployException; /** * Enables the process definition. * * @param processDefinitionId * The process definition identifier. * @throws ProcessDefinitionNotFoundException * If the identifier does not refer to an existing process definition. * @throws ProcessEnablementException * If an exception occurs during the process enablement. * @since 6.0 */ void enableProcess(long processDefinitionId) throws ProcessDefinitionNotFoundException, ProcessEnablementException; /** * Disables the process definition by giving its identifier. A process can only be disabled if it is enabled. * * @param processDefinitionId * The process definition identifier. * @throws ProcessDefinitionNotFoundException * If the identifier does not refer to an existing process definition. * @throws ProcessActivationException * If an exception occurs during the process disablement. * @since 6.0 */ void disableProcess(long processDefinitionId) throws ProcessDefinitionNotFoundException, ProcessActivationException; /** * Returns the process definition by giving its identifier. * If the identifier is null, a ProcessDefinitionNotFoundException is thrown. * * @param processDefinitionId * The identifier of the process definition. * @return The process definition referenced by the identifier. * @throws ProcessDefinitionNotFoundException * If the identifier does not refer to an existing process definition. * @throws RetrieveException * If an exception occurs when getting the process definition. * @since 6.0 */ ProcessDefinition getProcessDefinition(long processDefinitionId) throws ProcessDefinitionNotFoundException; /** * Deletes a process definition by giving its identifier. A process can only be deleted if it is disabled and it has * no more existing process instances. * * @param processDefinitionId * The identifier of the process definition. * @throws DeletionException * If an exception occurs during process deletion. * @since 6.1 */ void deleteProcessDefinition(long processDefinitionId) throws DeletionException; /** * Deletes process definitions by giving their identifiers. If any specified identifier does not refer to a real * process definition, or if an exception * occurs, no process definition is deleted. All instances of given processes must be deleted prior to calling this * operation. * * @param processDefinitionIds * The list of identifiers of process definitions. * @throws DeletionException * If an exception occurs during process deletion. * @see #deleteProcessDefinition(long) * @since 6.1 */ void deleteProcessDefinitions(List processDefinitionIds) throws DeletionException; /** * Deploys, enables and returns a process. * * @param designProcessDefinition * The description of a process definition. * @return ProcessDefinition the process definition corresponding of the description. * @throws AlreadyExistsException * If a process with same name and version was already deployed. * @throws ProcessEnablementException * If a process cannot be enabled. * @throws InvalidProcessDefinitionException * If the designProcessDefinition is invalid. * @throws ProcessDeployException * If an exception occurs when deploying the process. * @see #deploy(DesignProcessDefinition) * @see #enableProcess(long) * @since 6.0 */ ProcessDefinition deployAndEnableProcess(DesignProcessDefinition designProcessDefinition) throws ProcessDeployException, ProcessEnablementException, AlreadyExistsException, InvalidProcessDefinitionException; /** * Deploys and enables a process by giving a {@link BusinessArchive}. * * @param businessArchive * The archive ready to deploy. * @return ProcessDefinition Process definition by given a business archive. * @throws ProcessDeployException * If an exception occurs when deploying the archive. * @throws AlreadyExistsException * If a process with same name and same version already exists. * @throws ProcessEnablementException * If a process cannot be enabled. */ ProcessDefinition deployAndEnableProcess(BusinessArchive businessArchive) throws ProcessDeployException, ProcessEnablementException, AlreadyExistsException; /** * Returns a list of problems if the process is configured incorrectly or the configuration is incomplete. * * @param processDefinitionId * The process definition identifier. * @return a list of problems or an empty list. * @throws ProcessDefinitionNotFoundException * If the identifier does not refer to an existing process definition. * @throws RetrieveException * If an exception occurs when getting the problems of the process definition. * @since 6.0 */ List getProcessResolutionProblems(long processDefinitionId) throws ProcessDefinitionNotFoundException; /** * Gets the current number of process definitions in all states. * * @return The number of process definitions. * @throws RetrieveException * If an exception occurs when getting the number of the process definitions. * @since 6.0 */ long getNumberOfProcessDeploymentInfos(); /** * Gets the deployment information of a process definition by giving the process definition identifier. * * @param processDefinitionId * The process definition identifier. * @return The deployment information of the process definition. * @throws ProcessDefinitionNotFoundException * If the identifier does not refer to an existing process definition. * @throws RetrieveException * If an exception occurs when getting the process deployment information. * @since 6.0 */ ProcessDeploymentInfo getProcessDeploymentInfo(long processDefinitionId) throws ProcessDefinitionNotFoundException; /** * Updates the process deployment information for a specified process. * * @param processDefinitionId * The process definition identifier. * @param processDeploymentInfoUpdater * The description which describe how to update the process deployment information. * @throws ProcessDefinitionNotFoundException * If the identifier does not refer to an existing process definition. * @throws UpdateException * If an exception occurs when updating the process deployment information. * @since 6.0 * @deprecated as of 9.0.0, Process should be updated at startup. */ @Deprecated(since = "9.0.0") void updateProcessDeploymentInfo(long processDefinitionId, ProcessDeploymentInfoUpdater processDeploymentInfoUpdater) throws ProcessDefinitionNotFoundException, UpdateException; /** * Returns a paged list of process deployment information for a number of processes. * * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of process deployment information results per page. * @param sortCriterion * The sorting criterion. * @return The ordered list of process deployment informations. * @throws RetrieveException * If an exception occurs when getting the process deployment informations. * @since 6.0 */ List getProcessDeploymentInfos(int startIndex, int maxResults, ProcessDeploymentInfoCriterion sortCriterion); /** * Returns the number of actors in a process definition. * * @param processDefinitionId * The process definition identifier. * @return The number of actors in the process. * @throws ProcessDefinitionNotFoundException * If the identifier does not refer to an existing process definition. * @since 6.0 */ int getNumberOfActors(long processDefinitionId) throws ProcessDefinitionNotFoundException; /** * Returns the actor. * * @param actorId * The identifier of the actor. * @return The actor. * @throws ActorNotFoundException * If an identifier does not refer to an existing actor. * @since 6.0 */ ActorInstance getActor(long actorId) throws ActorNotFoundException; /** * Returns a paged list of actors in a process. * * @param processDefinitionId * The process definition identifier. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of actors per page. * @param sort * The sorting criterion. * @return The ordered list of actors. * @since 6.0 */ List getActors(long processDefinitionId, int startIndex, int maxResults, ActorCriterion sort); /** * Returns a paged list of members of an actor. * An actor member can be a user, a role, a group, or a membership. An actor member is created when a user, role, * group, or membership is mapped to the * actor. * No ordering must be assumed on the list of results. * * @param actorId * The identifier of the actor. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of actor members per page. * @return The list of actor members. * @since 6.0 */ List getActorMembers(long actorId, int startIndex, int maxResults); /** * Counts the number of members mapped to the actor. * An actor member can be a user, a role, a group, or a membership. An actor member is created when a user, role, * group, or membership is mapped to the * actor. * * @param actorId * The identifier of the actor. * @return The number of actors members of the actor * @since 6.0 */ long getNumberOfActorMembers(long actorId); /** * Counts the number of users mapped to the actor. * * @param actorId * The identifier of the actor. * @return The number of users mapped to the actor. * @since 6.0 */ long getNumberOfUsersOfActor(long actorId); /** * Counts the number of roles mapped to the actor. * * @param actorId * The identifier of the actor. * @return The number of roles mapped to the actor. * @since 6.0 */ long getNumberOfRolesOfActor(long actorId); /** * Counts the number of groups mapped to the actor. * * @param actorId * The identifier of the actor. * @return The number of groups mapped to the actor. * @since 6.0 */ long getNumberOfGroupsOfActor(long actorId); /** * Counts the number of memberships mapped to the actor. * * @param actorId * The identifier of the actor. * @return The total number of user memberships mapped to an actor * @since 6.0 */ long getNumberOfMembershipsOfActor(long actorId); /** * Updates the actor. * * @param actorId * The identifier of the actor. * @param actorUpdater * The descriptor which contains the fields to update. * @return The actor. * @throws ActorNotFoundException * If an identifier does not refer to an existing actor. * @throws UpdateException * If an exception occurs when updating the actor. * @since 6.0 * @deprecated as of 9.0.0, Actor should be updated at startup. */ @Deprecated(since = "9.0.0") ActorInstance updateActor(long actorId, ActorUpdater actorUpdater) throws ActorNotFoundException, UpdateException; /** * Maps the user to the actor. The user will be mapped to the actor as an {@link ActorMember}. * * @param actorId * The identifier of the actor. * @param userId * The identifier of the user. * @return The couple actor/user as an actor member. * @throws CreationException * If an exception occurs when creating the actor mapping. * @throws AlreadyExistsException * If the association already exists. * @see IdentityAPI#getUser(long) * @since 6.0 */ ActorMember addUserToActor(long actorId, long userId) throws CreationException, AlreadyExistsException; /** * Maps a user to the actor of the process definition. The user will be mapped to the actor as an * {@link ActorMember}. * * @param actorName * The name of the actor. * @param processDefinition * The process definition. * @param userId * The identifier of the user. * @return The couple actor/user as an actor member. * @throws ActorNotFoundException * If the name does not refer to an existing actor of the process definition. * @throws CreationException * If an exception occurs when creating the actor mapping. * @throws AlreadyExistsException * If the association already exists. * @see IdentityAPI#getUser(long) * @since 6.0 */ ActorMember addUserToActor(String actorName, ProcessDefinition processDefinition, long userId) throws ActorNotFoundException, CreationException, AlreadyExistsException; /** * Maps the group to the actor. * * @param actorId * The identifier of the actor. * @param groupId * The identifier of the group. * @return The couple actor/group as an actor member. * @throws CreationException * If the exception occurs when creating the actor mapping. * @throws AlreadyExistsException * If the association already exists. * @see IdentityAPI#getGroup(long) * @since 6.0 */ ActorMember addGroupToActor(long actorId, long groupId) throws CreationException, AlreadyExistsException; /** * Maps the group to the actor of the process definition. * * @param actorName * The name of the actor. * @param groupId * The identifier of the group. * @param processDefinition * The process definition. * @return The couple actor/group as an actor member. * @throws ActorNotFoundException * If the name does not refer to an existing actor of the process definition. * @throws CreationException * If an exception occurs when creating the actor mapping. * @throws AlreadyExistsException * If the association already exists. * @since 6.0 */ ActorMember addGroupToActor(String actorName, long groupId, ProcessDefinition processDefinition) throws ActorNotFoundException, CreationException, AlreadyExistsException; /** * Maps the role to the actor. * * @param actorId * The identifier of the actor. * @param roleId * The identifier of the role. * @return The couple actor/role as an actor member. * @throws CreationException * If an exception occurs when creating the actor mapping. * @since 6.0 */ ActorMember addRoleToActor(long actorId, long roleId) throws CreationException; /** * Maps the role to the actor of the process definition. * * @param actorName * The name of the actor. * @param processDefinition * The process definition. * @param roleId * The identifier of the role. * @return The couple actor/role as an actor member. * @throws ActorNotFoundException * If the name does not refer to an existing actor of the process definition. * @throws CreationException * If an exception occurs when creating the actor mapping. * @since 6.0 */ ActorMember addRoleToActor(String actorName, ProcessDefinition processDefinition, long roleId) throws ActorNotFoundException, CreationException; /** * Maps the role and the group to the actor. * * @param actorId * The identifier of the actor. * @param roleId * The identifier of the role. * @param groupId * The identifier of the group. * @return The tuple actor/role/group as an actor member. * @throws CreationException * If an exception occurs when creating the actor mapping. * @since 6.0 */ ActorMember addRoleAndGroupToActor(long actorId, long roleId, long groupId) throws CreationException; /** * Maps the role and the group to the actor of the process definition. * * @param actorName * The name of the actor. * @param processDefinition * The process definition. * @param roleId * The identifier of the role. * @param groupId * The identifier of the role. * @return The tuple actor/role/group as an actor member. * @throws ActorNotFoundException * If the actor name does not refer to an existing actor in the process definition. * @throws CreationException * If an exception occurs when creating the actor mapping. * @since 6.0 */ ActorMember addRoleAndGroupToActor(String actorName, ProcessDefinition processDefinition, long roleId, long groupId) throws ActorNotFoundException, CreationException; /** * Deletes the actor member. This removes the mapping between the user, group, role, or membership and the actor. * The user, group, role, or membership is not removed from the organization. * * @param actorMemberId * The identifier of the actor member * @throws DeletionException * If an exception occurs when deleting the actor mapping. * @since 6.0 */ void removeActorMember(long actorMemberId) throws DeletionException; /** * Imports into the process definition an actor mapping in XML format. * * @param processDefinitionId * The identifier of the process. * @param xmlContent * The XML content of the mapping. If null, nothing is performed. * @throws ActorMappingImportException * If an exception occurs when importing the actor mapping. * @since 6.0 */ void importActorMapping(long processDefinitionId, String xmlContent) throws ActorMappingImportException; /** * Imports to the process definition, the actor mapping in XML format as a byte array. * * @param processDefinitionId * The identifier of the process. * @param actorMappingXML * The XML content of the mapping as a byte array. If null, nothing is performed. * @throws ActorMappingImportException * If an exception occurs when importing the actor mapping. * @since 6.0 */ void importActorMapping(long processDefinitionId, byte[] actorMappingXML) throws ActorMappingImportException; /** * Exports the actor mapping of the process definition. The result contains the mapping in XML format. * * @param processDefinitionId * The identifier of the process. * @return The XML content of the mapping. * @throws ActorMappingExportException * If an exception occurs when exporting the actor mapping. * @since 6.0 */ String exportActorMapping(long processDefinitionId) throws ActorMappingExportException; /** * Adds a category. * A category is a string that can be assigned to processes, to make it easier * to identify sets of related processes. For example, you could have * a category called hr to identify all HR processes, or a category called * finance to identify all processes used in the purchasing and accounts departments. * * @param name * The name of the category. * @param description * The description of the category. * @return The category. * @throws AlreadyExistsException * If a category already exists with the given name. * @throws CreationException * If an exception occurs when creating the category. * @since 6.0 */ Category createCategory(String name, String description) throws AlreadyExistsException, CreationException; /** * Counts the number of categories. * * @return The number of categories. * @since 6.0 */ long getNumberOfCategories(); /** * Returns a paged list of categories. * * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of categories. * @param sortCriterion * The sorting criterion. * @return The ordered list of categories. * @since 6.0 */ List getCategories(int startIndex, int maxResults, CategoryCriterion sortCriterion); /** * Returns the category. * * @param categoryId * The identifier of the category. * @return The category. * @throws CategoryNotFoundException * If the identifier does not refer to an existing category. * @since 6.0 */ Category getCategory(long categoryId) throws CategoryNotFoundException; /** * Associates the process definition with the category. * * @param categoryId * The identifier of the category. * @param processDefinitionId * The identifier of the process definition. * @throws AlreadyExistsException * If the association category/process already exists. * @throws CreationException * If an exception occurs while adding the process to the category. * @since 6.0 */ void addProcessDefinitionToCategory(long categoryId, long processDefinitionId) throws AlreadyExistsException, CreationException; /** * Associates a list of process definitions with the category. * * @param categoryId * The identifier of the category. * @param processDefinitionIds * The identifiers of the process definitions. * @throws AlreadyExistsException * If an association category/process already exists. * @throws CreationException * If an exception occurs while adding the process to the category. * @since 6.0 */ void addProcessDefinitionsToCategory(long categoryId, List processDefinitionIds) throws AlreadyExistsException, CreationException; /** * Counts the number of categories of the process definition, that is, the number of categories to which the process * belongs. * * @param processDefinitionId * The identifier of the process definition. * @return The number of categories of the process. * @since 6.0 */ long getNumberOfCategories(long processDefinitionId); /** * Counts the number of process deployment information entries of the category. * This is the number of deployed processes in the specified category. * * @param categoryId * The identifier of the category. * @return The number of process deployment informations of the category. * @since 6.0 */ long getNumberOfProcessDefinitionsOfCategory(long categoryId); /** * Returns the paged list of process deployment information items for the category. * * @param categoryId * The identifier of the category. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of process deployment information. * @param sort * The sorting criterion. * @return The ordered list of process deployment informations of the category. * @since 6.0 */ List getProcessDeploymentInfosOfCategory(long categoryId, int startIndex, int maxResults, ProcessDeploymentInfoCriterion sort); /** * Get categories from process definition * * @param processDefinitionId * The identifier of the process definition. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of categories. * @param sort * The sorting criterion. * @return The ordered list of categories of the process definition. * @since 6.0 */ List getCategoriesOfProcessDefinition(long processDefinitionId, int startIndex, int maxResults, CategoryCriterion sort); /** * Updates the category according to the updater values. * * @param categoryId * The identifier of the category. * @param updater * The role updater. * @throws CategoryNotFoundException * If the category identifier does not refer to an existing category. * @throws UpdateException * If an exception occurs during the category update. * @since 6.0 */ void updateCategory(long categoryId, CategoryUpdater updater) throws CategoryNotFoundException, UpdateException; /** * Deletes a category and its associations. It does not delete the associated process definitions. * * @param categoryId * The identifier of the category. * @throws DeletionException * If an exception occurs when deleting the category. * @since 6.0 */ void deleteCategory(long categoryId) throws DeletionException; /** * Deletes the associations of all the process definitions related to the category given as input parameter * respecting the pagination parameters. * It does not delete the associated process definitions. * * @param categoryId * The identifier of the category. * @param startIndex * The index * @param maxResults * The max number of elements to retrieve per page * @return The number of elements that have been removed * @throws DeletionException * If an error occurs while removing the process definitions of category. * @since 6.1 */ long removeProcessDefinitionsFromCategory(long categoryId, int startIndex, int maxResults) throws DeletionException; /** * Deletes the associations of categories related the process definition given as input parameter respecting the * pagination parameters. * The process definition and categories are not deleted, but there is no longer an association between them. * * @param processDefinitionId * The identifier of the process definition. * @param startIndex * The index * @param maxResults * The max number of elements to retrieve per page * @return The number of elements that have been removed * @throws DeletionException * If an error occurs while removing the process definition from category. * @since 6.1 */ long removeCategoriesFromProcessDefinition(long processDefinitionId, int startIndex, int maxResults) throws DeletionException; /** * Counts the number of process definitions which have no category. * * @return The number of process definitions which have no category. * @since 6.0 */ long getNumberOfUncategorizedProcessDefinitions(); /** * Returns the paged list of process deployment information items which have no category. * * @param startIndex * The number of the page (the first page number is 0). * @param maxResults * The number of categories. * @param sortCriterion * The sorting criterion. * @return The ordered list of process deployment informations. * @since 6.0 */ List getUncategorizedProcessDeploymentInfos(int startIndex, int maxResults, ProcessDeploymentInfoCriterion sortCriterion); /** * Returns the paged list of data definitions of the activity of the process definition. * * @param processDefinitionId * The identifier of the process definition. * @param activityName * The name of the activity. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of data definitions. * @return The ordered list of data definitions. * @throws ProcessDefinitionNotFoundException * If the identifier does not refer to an existing process definition. * @throws ActivityDefinitionNotFoundException * If the name does not refer to an existing activity. * @since 6.0 */ List getActivityDataDefinitions(long processDefinitionId, String activityName, int startIndex, int maxResults) throws ProcessDefinitionNotFoundException, ActivityDefinitionNotFoundException; /** * Counts the number of data definitions of the activity of the process definition. * * @param processDefinitionId * The identifier of the process definition. * @param activityName * The name of the activity. * @return The number of data definitions of the activity of the process definition. * @throws ProcessDefinitionNotFoundException * If the identifier does not refer to an existing process definition. * @throws ActivityDefinitionNotFoundException * If the name does not refer to an existing activity. * @since 6.0 */ int getNumberOfActivityDataDefinitions(long processDefinitionId, String activityName) throws ProcessDefinitionNotFoundException, ActivityDefinitionNotFoundException; /** * Returns the paged list of data definitions of the process definition. * * @param processDefinitionId * The identifier of the process definition. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of data definitions. * @return The ordered list of data definitions. * @throws ProcessDefinitionNotFoundException * If the identifier does not refer to an existing process definition. * @since 6.0 */ List getProcessDataDefinitions(long processDefinitionId, int startIndex, int maxResults) throws ProcessDefinitionNotFoundException; /** * Counts the number of data definitions of the process definition. * * @param processDefinitionId * The identifier of the process definition. * @return The number of data definitions of the process definition. * @throws ProcessDefinitionNotFoundException * If the identifier does not refer to an existing process definition. * @since 6.0 */ int getNumberOfProcessDataDefinitions(long processDefinitionId) throws ProcessDefinitionNotFoundException; /** * Retrieves resources inside .bar (or BusinessArchive) file representing the deployed process. * Resources to retrieved are specified with a * file names pattern. The pattern format must be relative to the root of the business archive, without starting * with a '/' character. The pattern can * contain forward slashes after the first character. * eg. if you have an image in resources/folder/image.jpg in your bar file, you can call: * *
     * processAPI.getProcessResources(processDefinitionId, "resources/folder/image.jpg");
     * 
* * to retrieve a map with one entry: the key is the path of the resource, the value is the binary content of the * resource. * *
     * eg.processAPI().getProcessResources(processDefinitionId, {@literal "resources/folder/.*}\\.jpg")
     * 
* * would retrieve all .jpg files in the folder `resources/folder`. The key in the map is the path for each matching * resource. * * @param processDefinitionId * The identifier of the process definition. * @param filenamesPattern * The pattern to retrieve the resources. * @return The map containing the pairs (name, content) of the matching files. * @throws RetrieveException * If an exception occurs when getting the resources of the process definition. * @since 6.0 * @see #exportBarProcessContentUnderHome(long) * @see BusinessArchive#getResources(String) */ Map getProcessResources(long processDefinitionId, String filenamesPattern) throws RetrieveException; /** * Get a resource from the process. * Can only retrieve resources stored in the 'resources' folder of the business archive * * @param processDefinitionId if of the process definition * @param fileName name of the file to retrieve inside the resources folder of the business archive * @return the content of the file * @throws RetrieveException * @throws FileNotFoundException */ byte[] getExternalProcessResource(long processDefinitionId, String fileName) throws RetrieveException, FileNotFoundException; /** * Get a document resource from the process. * Can only retrieve resources stored in the 'documents' folder of the business archive * * @param processDefinitionId id of the process definition * @param fileName name of the file to retrieve inside the documents folder of the business archive * @return the content of the file * @throws RetrieveException if the resource cannot be read from the database * @throws ProcessResourceNotFoundException if no resource with the given name exists in the process definition */ byte[] getDocumentProcessResource(long processDefinitionId, String fileName) throws RetrieveException, ProcessResourceNotFoundException; /** * Returns the identifier of the most recently deployed process definition with the given name. This method does not * take into consideration the process * version, but only its deployment date. * * @param processName * The process definition name. * @return The identifier of the most recently deployed process definition with the given name. * @throws ProcessDefinitionNotFoundException * If the identifier does not refer to an existing process definition. * @since 6.0 */ long getLatestProcessDefinitionId(String processName) throws ProcessDefinitionNotFoundException; /** * Returns the states of the flow node type. Flow nodes are activities, gateways, or events. * * @param nodeType * The flow node type. * @return The set of the states of the flow node type. * @since 6.0 */ Set getSupportedStates(FlowNodeType nodeType); /** * Returns the identifier of the process definition with the specified name and version. * * @param name * The name of the process definition. * @param version * The version of the process definition. * @return The identifier of the process definition. * @throws ProcessDefinitionNotFoundException * If the name and version do not refer to an existing process definition. * @since 6.0 */ long getProcessDefinitionId(String name, String version) throws ProcessDefinitionNotFoundException; /** * Returns the paged list of process deployment information items that the actors can start. * * @param actorIds * The identifiers of the actors. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of process deployment informations. * @param sortingCriterion * The sort criterion * @return The ordered list of process deployment informations. * @since 6.0 */ List getStartableProcessDeploymentInfosForActors(Set actorIds, int startIndex, int maxResults, ProcessDeploymentInfoCriterion sortingCriterion); /** * Checks whether the actors are allowed to start the process definition. * * @param processDefinitionId * The identifier of the process definition. * @param actorIds * The identifiers of the actors. * @return true if the actors are allowed to start the process definition; false otherwise. * @since 6.0 */ boolean isAllowedToStartProcess(long processDefinitionId, Set actorIds); /** * Returns the actor initiator of the process definition. * * @param processDefinitionId * The identifier of the process definition. * @return The actor initiator of the process definition. * @throws ActorNotFoundException * If the process definition does not have an actor initiator. * @throws ProcessDefinitionNotFoundException * If the process definition corresponding to the given identifier is not found * @since 6.0 */ ActorInstance getActorInitiator(long processDefinitionId) throws ActorNotFoundException, ProcessDefinitionNotFoundException; /** * Searches for the number and the list of processes which have been recently started by the user. * * @param userId * The identifier of the user. * @param searchOptions * The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants * @see ProcessDeploymentInfoSearchDescriptor * @return The number and the list of processes which have been recently started by the user. * @throws SearchException * If an exception occurs when getting the processes. * @since 6.0 */ SearchResult searchProcessDeploymentInfosStartedBy(long userId, SearchOptions searchOptions) throws SearchException; /** * Searches for the number and the list of processes that the user can start. * * @param userId * The identifier of the user. * @param searchOptions * The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants * @see ProcessDeploymentInfoSearchDescriptor * @return The number and the list of processes that the user can start. * @throws SearchException * If an exception occurs when getting the processes. * @since 6.3.3 */ SearchResult searchProcessDeploymentInfosCanBeStartedBy(long userId, SearchOptions searchOptions) throws SearchException; /** * Searches for the number and the list of process definitions. * * @param searchOptions * The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants * @see ProcessDeploymentInfoSearchDescriptor * @return matching process deployment information results. * @throws SearchException * If an exception occurs when getting the processes. * @since 6.0 */ SearchResult searchProcessDeploymentInfos(SearchOptions searchOptions) throws SearchException; /** * Associates the categories to the process definition. * * @param processDefinitionId * The identifier of the process definition. * @param categoryIds * The identifiers of the categories. * @throws AlreadyExistsException * If the association category/process has already added. * @throws CreationException * If an exception occurs when associating the process with the categories. * @since 6.0 */ void addCategoriesToProcess(long processDefinitionId, List categoryIds) throws AlreadyExistsException, CreationException; /** * Dissociates the categories from the process definition. The process definition itself is unchanged. * * @param processDefinitionId * The identifier of the process definition. * @param categoryIds * The identifiers of the categories. * @throws DeletionException * If an exception occurs when dissociating the categories from the process definition. * @since 6.0 */ void removeCategoriesFromProcess(long processDefinitionId, List categoryIds) throws DeletionException; /** * Searches for the number and the list of uncategorized processes. * * @param searchOptions * The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants * @see ProcessDeploymentInfoSearchDescriptor * @return The number and the list of uncategorized processes. * @throws SearchException * If an exception occurs when searching the process deployment information. * @since 6.0 */ SearchResult searchUncategorizedProcessDeploymentInfos(SearchOptions searchOptions) throws SearchException; /** * Searches for the number and the list of uncategorized processes supervised by the user. * * @param userId * The identifier of the user. * @param searchOptions * The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants * @see ProcessDeploymentInfoSearchDescriptor * @return The number and the list of uncategorized processes. * @throws SearchException * If an exception occurs when searching the process deployment information. * @since 6.0 */ SearchResult searchUncategorizedProcessDeploymentInfosSupervisedBy(long userId, SearchOptions searchOptions) throws SearchException; /** * Searches the number and the list of processes that the user can start which have no category. * * @param userId * The identifier of the user. * @param searchOptions * The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants * @see ProcessDeploymentInfoSearchDescriptor * @return The number and the list of uncategorized processes that the user can start. * @throws SearchException * If an exception occurs when searching the process deployment information. * @since 6.3.3 */ SearchResult searchUncategorizedProcessDeploymentInfosCanBeStartedBy(long userId, SearchOptions searchOptions) throws SearchException; /** * Returns the process deployment information of the process definitions. * * @param processDefinitionIds * The identifiers of the process definitions. * @return The process deployment information of the process definitions, order by name ascending. * @since 6.0 */ Map getProcessDeploymentInfosFromIds(List processDefinitionIds); /** * Returns the implementation of a connector of the process definition. * * @param processDefinitionId * The identifier of the process definition. * @param connectorName * The name of the connector. * @param connectorVersion * The version of the connector. * @return The description of the connector implementation. * @throws ConnectorNotFoundException * If an exception occurs when getting the connector implementation. * @since 6.0 */ ConnectorImplementationDescriptor getConnectorImplementation(long processDefinitionId, String connectorName, String connectorVersion) throws ConnectorNotFoundException; /** * Returns a paged list of connector implementation descriptors for the process definition. * * @param processDefinitionId * The identifier of the process definition. * @param startIndex * The index of the first result (starting from 0). * @param maxsResults * The maximum number of connector implementations. * @param sortingCriterion * The sort criterion. * @return The ordered list of connector implementation descriptors of the process definition. * @since 6.0 */ List getConnectorImplementations(long processDefinitionId, int startIndex, int maxsResults, ConnectorCriterion sortingCriterion); /** * Returns the number of connector implementations of the process definition. * * @param processDefinitionId * The identifier of the process definition. * @return The number of connector implementation of the process definition. * @since 6.0 */ long getNumberOfConnectorImplementations(final long processDefinitionId); /** * Returns the actor instances. * * @param actorIds * The identifiers of the actors. * @return The actor instances. (key=actorID, value=actor instance) * @since 6.0 */ Map getActorsFromActorIds(List actorIds); /** * Returns the processes for which a specified group is the only mapped actor. * This is be called before deleting a group from the organization, to make sure that * there are no processes that would become unresolved as a result of removing the group. * A process that has no actor mapping is unresolved and cannot be started. * * @param groupId * The identifier of the group. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of processes. * @param sortingCriterion * The sort criterion. * @return The processes that the group is the last actor. * @since 6.0 */ List getProcessDeploymentInfosWithActorOnlyForGroup(long groupId, int startIndex, int maxResults, ProcessDeploymentInfoCriterion sortingCriterion); /** * Returns the processes for which one of the listed groups is the only mapped actors. * This is be called before deleting a group from the organization, to make sure that * there are no processes that would become unresolved as a result of removing one of the listed groups. * A process that has no actor mapping is unresolved and cannot be started. * * @param groupIds * The identifiers of the groups. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of processes. * @param sortingCriterion * The sort criterion. * @return The processes that the groups are the last actor(s). * @since 6.0 */ List getProcessDeploymentInfosWithActorOnlyForGroups(List groupIds, int startIndex, int maxResults, ProcessDeploymentInfoCriterion sortingCriterion); /** * Returns the processes for which the role is the only mapped actor. * This is be called before deleting a role from the organization, to make sure that * there are no processes that would become unresolved as a result of removing the role. * A process that has no actor mapping is unresolved and cannot be started. * * @param roleId * The identifier of the role. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of processes. * @param sortingCriterion * The sort criterion. * @return The processes that the role is the last actor. * @since 6.0 */ List getProcessDeploymentInfosWithActorOnlyForRole(long roleId, int startIndex, int maxResults, ProcessDeploymentInfoCriterion sortingCriterion); /** * Returns the processes for which one of the listed roles is the only mapped actors. * This is be called before deleting a role from the organization, to make sure that * there are no processes that would become unresolved as a result of removing one of the listed roles. * A process that has no actor mapping is unresolved and cannot be started. * * @param roleIds * The identifiers of the roles. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of processes. * @param sortingCriterion * The sort criterion. * @return The processes that the roles are actor(s). * @since 6.0 */ List getProcessDeploymentInfosWithActorOnlyForRoles(List roleIds, int startIndex, int maxResults, ProcessDeploymentInfoCriterion sortingCriterion); /** * Returns the processes for which the user is the only mapped actor. * This is be called before deleting a user from the organization, to make sure that * there are no processes that would become unresolved as a result of removing the user. * A process that has no actor mapping is unresolved and cannot be started. * * @param userId * The identifier of the user. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of processes. * @param sortingCriterion * The sort criterion. * @return The processes that the user is the last actor. * @see #getProcessDeploymentInfosWithActorOnlyForUsers(List, int, int, ProcessDeploymentInfoCriterion) * @since 6.0 */ List getProcessDeploymentInfosWithActorOnlyForUser(long userId, int startIndex, int maxResults, ProcessDeploymentInfoCriterion sortingCriterion); /** * Returns the processes for which one of the listed users is the only mapped actor. * * @param userIds * The identifiers of the users. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of processes. * @param sortingCriterion * The sort criterion. * @return The processes that the users are the last actor(s). * @see #getProcessDeploymentInfosWithActorOnlyForUser(long, int, int, ProcessDeploymentInfoCriterion) * @since 6.0 */ List getProcessDeploymentInfosWithActorOnlyForUsers(List userIds, int startIndex, int maxResults, ProcessDeploymentInfoCriterion sortingCriterion); /** * Returns a specific process definition that include informations such as tasks definition, actors... * * @param processDefinitionId * Identifier of process definition * @return The corresponding process definition with informations. * @throws ProcessDefinitionNotFoundException * If the process definition doesn't exist. * @since 6.1 */ DesignProcessDefinition getDesignProcessDefinition(long processDefinitionId) throws ProcessDefinitionNotFoundException; /** * Searches the number and the list of processes supervised by the user. * * @param userId * The identifier of the user. * @param searchOptions * The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants * @see ProcessDeploymentInfoSearchDescriptor * @return The number and the list of processes supervised by the user. * @throws SearchException * If an exception occurs when getting the process deployment information. */ SearchResult searchProcessDeploymentInfosSupervisedBy(long userId, SearchOptions searchOptions) throws SearchException; /** * Search for process definitions that can be started by users managed by a specific user. * * @param managerUserId * The identifier of the manager. * @param searchOptions * The search criteria. Use ProcessDeploymentInfoSearchDescriptor constants * @see ProcessDeploymentInfoSearchDescriptor * @return * The list of process definitions that have at least one initiator who is mapped to a user to who reports * to the specified manager. * @throws SearchException * If an exception occurs when getting the process deployment information. * @since 6.3.3 */ SearchResult searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(long managerUserId, SearchOptions searchOptions) throws SearchException; /** * Adds the user as a supervisor of the process. * A supervisor of a process is responsible for what happens to the process. A supervisor can see * the tasks in the process, and can carry out process administration. A supervisor is defined in a * ProcessSupervisor * object as a mapping of users, groups, or roles to the process supervisor (similar to actor mapping). * A process has one ProcessSupervisor; however, as this can be mapped to several users, either explicitly or by * mapping groups or roles, the process can be supervised by several people. * * @param processDefinitionId * The identifier of the process definition. * @param userId * The identifier of the user. * @return The user as a process supervisor. * @throws CreationException * If an exception occurs when creating the process supervisor. * @throws AlreadyExistsException * If the user is already the process supervisor. * @since 6.0 */ ProcessSupervisor createProcessSupervisorForUser(long processDefinitionId, long userId) throws CreationException, AlreadyExistsException; /** * Adds the role as a supervisor of the process. * A supervisor of a process is responsible for what happens to the process. A supervisor can see * the tasks in the process, and can carry out process administration. A supervisor is defined in a * ProcessSupervisor * object as a mapping of users, groups, or roles to the process supervisor (similar to actor mapping). * A process has one ProcessSupervisor; however, as this can be mapped to several users, either explicitly or by * mapping groups or roles, the process can be supervised by several people. * * @param processDefinitionId * The identifier of the process definition. * @param roleId * The identifier of the role. * @return The role as a supervisor of the process. * @throws CreationException * If an exception occurs when creating the process supervisor. * @throws AlreadyExistsException * If the provided role is already a supervisor for the provided process. * @since 6.0 */ ProcessSupervisor createProcessSupervisorForRole(long processDefinitionId, long roleId) throws CreationException, AlreadyExistsException; /** * Adds the group as a supervisor of the process. * A supervisor of a process is responsible for what happens to the process. A supervisor can see * the tasks in the process, and can carry out process administration. A supervisor is defined in a * ProcessSupervisor * object as a mapping of users, groups, or roles to the process supervisor (similar to actor mapping). * A process has one ProcessSupervisor; however, as this can be mapped to several users, either explicitly or by * mapping groups or roles, the process can be supervised by several people. * * @param processDefinitionId * The identifier of the process definition. * @param groupId * The identifier of the group. * @return The group as a supervisor of the process. * @throws CreationException * If an exception occurs when creating the process supervisor. * @throws AlreadyExistsException * If the provided group is already a supervisor for the provided process. * @since 6.0 */ ProcessSupervisor createProcessSupervisorForGroup(long processDefinitionId, long groupId) throws CreationException, AlreadyExistsException; /** * Adds the membership as a supervisor of the process. * A supervisor of a process is responsible for what happens to the process. A supervisor can see * the tasks in the process, and can carry out process administration. A supervisor is defined in a * ProcessSupervisor * object as a mapping of users, groups, or roles to the process supervisor (similar to actor mapping). * A process has one ProcessSupervisor; however, as this can be mapped to several users, either explicitly or by * mapping groups or roles, the process can be supervised by several people. * * @param processDefinitionId * The identifier of the process definition. * @param groupId * The identifier of the group. * @param roleId * The identifier of the role. * @return The membership as a supervisor of the process. * @throws CreationException * If an exception occurs when creating the process supervisor. * @throws AlreadyExistsException * If the provided membership (group + role) is already a supervisor for the provided process. * @since 6.0 */ ProcessSupervisor createProcessSupervisorForMembership(long processDefinitionId, long groupId, long roleId) throws CreationException, AlreadyExistsException; /** * Checks whether the user is the process supervisor. * * @param processDefinitionId * The identifier of the process definition. * @param userId * The identifier of the user. * @return true if the user is currently a supervisor of the process; false otherwise. * @since 6.0 */ boolean isUserProcessSupervisor(long processDefinitionId, long userId); /** * Deletes a process supervisor. * * @param supervisorId * The identifier of the {@link ProcessSupervisor}. * @throws DeletionException * If an exception occurs when deleting the process supervisor. * @since 6.0 */ void deleteSupervisor(long supervisorId) throws DeletionException; /** * Delete the {@link ProcessSupervisor} object that is identified by this processDefinitionId, userId, roleId and * groupId. *

* e.g. to delete the process supervisor that is set for userId 12 and process id 255 call deleteSupervisor(255, 12, * null, null) *

* be careful if the user is supervisor because he is in e.g. a group of super visor calling this method with the * userId will do nothing, you must find the * {@link ProcessSupervisor} that link the user to the process * * @param processDefinitionId * The Identifier of the process definition to delete the supervisor for. * @param userId * The Id of the user used as a supervisor for the given process. Can be null. * @param roleId * The Id of the role used as a supervisor for the given process. Can be null. * @param groupId * The Id of the group used as a supervisor for the given process. Can be null. * @throws DeletionException * If a problem occurs while deleting the supervisor for the given process. * @since 6.0 */ void deleteSupervisor(Long processDefinitionId, Long userId, Long roleId, Long groupId) throws DeletionException; /** * Searches for the number and the list of processes supervisors. * * @param searchOptions * The search criteria. Use ProcessSupervisorSearchDescriptor constants * @see ProcessSupervisorSearchDescriptor * @return The number and the list of processes supervisors. * @throws SearchException * If an exception occurs when getting the processes supervisors. * @since 6.0 */ SearchResult searchProcessSupervisors(SearchOptions searchOptions) throws SearchException; /** * Returns a paged list of categories that are not associated with the process definition. * * @param processDefinitionId * The identifier of the process definition. * @param startIndex * The number of the page (the first page number is 0). * @param maxResults * The maximum number of categories. * @param sortingCriterion * The sort criterion. * @return The categories that are not associated with the process definition. */ List getCategoriesUnrelatedToProcessDefinition(long processDefinitionId, int startIndex, int maxResults, CategoryCriterion sortingCriterion); /** * Counts the number of process definitions that do not belong to the category. * * @param categoryId * The identifier of the category. * @return The number of process definitions that have not the category. */ long getNumberOfProcessDeploymentInfosUnrelatedToCategory(long categoryId); /** * Returns the paginated list of process deployment information items of the category. * * @param categoryId * The identifier of the category. * @param startIndex * The number of the page (the first page number is 0). * @param maxResults * The number of process deployment informations. * @param sortingCriterion * The sort criterion. * @return A list of process unrelated to the category which has categoryId as id * @since 6.0 */ List getProcessDeploymentInfosUnrelatedToCategory(long categoryId, int startIndex, int maxResults, ProcessDeploymentInfoCriterion sortingCriterion); /** * Searches for the number and the list of users who can start the process. * Note: managerUserId is a possible filter. * * @param processDefinitionId * The identifier of the process definition. * @param searchOptions * The search criteria. Use UserSearchDescriptor constants * @see ProcessDeploymentInfoSearchDescriptor * @return The number and the list of users who can start the process. * @throws SearchException * If an exception occurs when getting the users. * @since 6.0 */ SearchResult searchUsersWhoCanStartProcessDefinition(long processDefinitionId, SearchOptions searchOptions) throws SearchException; /** * Get process deployment information from a list of processInstance id * * @param processInstanceIds * Identifier of the processInstance * @return A map of <processInstantsIds,ProcessDeploymentInfos>, ordered by the name of the process ascending * @since 6.0 */ Map getProcessDeploymentInfosFromProcessInstanceIds(List processInstanceIds); /** * Get process deployment information from a list of archived processInstance ids * * @param archivedProcessInstantsIds * Identifier of the archived process instance * @return A map of <archivedProcessInstantsIds,ProcessDeploymentInfos> * @since 6.0 */ Map getProcessDeploymentInfosFromArchivedProcessInstanceIds( List archivedProcessInstantsIds); /** * Export processes of bar under home by a processDefinition id * * @param processDefinitionId * Identifier of the processDefinition * @return An array of byte * @throws ProcessExportException * If an export problem occurs * @since 6.0 */ byte[] exportBarProcessContentUnderHome(long processDefinitionId) throws ProcessExportException; /** * Disables and deletes the process. Wrapping method for methods {@link #disableProcess(long)} and * {@link #deleteProcessDefinition(long)}; * * @param processDefinitionId * The process definition identifier. * @throws ProcessDefinitionNotFoundException * If the identifier does not refer to an existing process definition. * @throws ProcessActivationException * If an exception occurs while disabling the process. * @throws DeletionException * If an exception occurs while deleting the process. * @see #disableProcess(long) * @see #deleteProcessDefinition(long) * @since 6.1 */ void disableAndDeleteProcessDefinition(long processDefinitionId) throws ProcessDefinitionNotFoundException, ProcessActivationException, DeletionException; /** * Lists the possible users (candidates) of the specified human task definition. *
* If the task contains a user filter, it is not executed. * Users are ordered by user name. * An empty list is returned if: * - the task or the process does not exist * - the flow node is not a human task * * @param processDefinitionId * The identifier of process definition * @param humanTaskName * The name of the human task * @param startIndex * The start index * @param maxResults * The list of users * @return The list of users. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @throws RetrieveException * If an exception occurs while retrieving the users * @since 6.1 */ List getPossibleUsersOfHumanTask(long processDefinitionId, String humanTaskName, int startIndex, int maxResults); /** * Retrieves the list of user identifiers for the given actor and process. * * @param processDefinitionId * The process definition identifier * @param actorName * The actor name * @param startIndex * The start index (the first valid value is zero) * @param maxResults * The max number of user identifiers to be retrieved * @return Retrieves the list of user identifiers for the given actor and process. * @since 6.3.2 */ List getUserIdsForActor(long processDefinitionId, String actorName, int startIndex, int maxResults); /** * Purges the classLoader of the process definition. The process must be disable and no instances should run, * otherwise an UpdateException is thrown. * * @param processDefinitionId * The identifier of the process definition. * @throws ProcessDefinitionNotFoundException * If the identifier does not refer to an existing process definition. * @throws UpdateException * If the process is not disable or if instances are still running * @since 6.4.0 */ void purgeClassLoader(long processDefinitionId) throws ProcessDefinitionNotFoundException, UpdateException; /** * Gets how many parameters the process definition contains. * * @param processDefinitionId * The identifier of the processDefinition * @return The number of parameters of a process definition * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @since 7.0.0 */ int getNumberOfParameterInstances(long processDefinitionId); /** * Get a parameter instance by process definition UUID * * @param processDefinitionId * The identifier of the processDefinition * @param parameterName * The parameter name for get ParameterInstance * @return The ParameterInstance of the process with processDefinitionUUID and name parameterName * @throws org.bonitasoft.engine.exception.NotFoundException * Error thrown if the given parameter is not found. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @since 7.0.0 */ ParameterInstance getParameterInstance(long processDefinitionId, String parameterName) throws NotFoundException; /** * Returns the parameters of a process definition or an empty map if the process does not contain any parameter. * * @param processDefinitionId * The identifier of the processDefinition * @param startIndex * The index of the page to be returned. First page has index 0. * @param maxResults * The number of result per page. Maximum number of result returned. * @param sort * The criterion to sort the result * @return The ordered list of parameter instances * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @since 7.0.0 */ List getParameterInstances(long processDefinitionId, int startIndex, int maxResults, ParameterCriterion sort); /** * Search for form mapping * * @param searchOptions * The search criteria. Use FormMappingSearchDescriptor constants * @see FormMappingSearchDescriptor * @return the result of the search * @see org.bonitasoft.engine.form.FormMappingSearchDescriptor * @see org.bonitasoft.engine.form.FormMappingType * @since 7.0.0 */ SearchResult searchFormMappings(SearchOptions searchOptions) throws SearchException; /** * @param formMappingId * the id of the form mapping to get * @return * the form mapping * @throws FormMappingNotFoundException * @since 7.0.0 */ FormMapping getFormMapping(final long formMappingId) throws FormMappingNotFoundException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ProcessRuntimeAPI.java ================================================ /** * 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.api; import java.io.Serializable; import java.util.Date; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.comment.ArchivedComment; import org.bonitasoft.engine.bpm.comment.Comment; import org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance; import org.bonitasoft.engine.bpm.connector.ConnectorExecutionException; import org.bonitasoft.engine.bpm.connector.ConnectorInstance; import org.bonitasoft.engine.bpm.connector.ConnectorNotFoundException; import org.bonitasoft.engine.bpm.contract.ContractDefinition; import org.bonitasoft.engine.bpm.contract.ContractViolationException; import org.bonitasoft.engine.bpm.data.ArchivedDataInstance; import org.bonitasoft.engine.bpm.data.ArchivedDataNotFoundException; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.data.DataNotFoundException; import org.bonitasoft.engine.bpm.flownode.*; import org.bonitasoft.engine.bpm.process.*; import org.bonitasoft.engine.exception.*; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionEvaluationException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserNotFoundException; import org.bonitasoft.engine.job.FailedJob; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.InvalidSessionException; /** * ProcessRuntimeAPI deals with Process runtime notions such as starting a new instance of a process, * retrieving and executing tasks, accessing to * all types of tasks, assigning a user to a task, retrieving archived versions of a task, accessing / updating data / * variable values, adding / retrieving * process comments ... * It generally allows all BPM runtime actions, that is, once process instances are running of finished executing. * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Yanyan Liu * @author Zhao Na * @author Frederic Bouquet * @author Elias Ricken de Medeiros * @author Celine Souchet * @author Emmanuel Duchastenier */ public interface ProcessRuntimeAPI { /** * List all open root process instances. * * @param searchOptions * The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor} for * valid fields for searching and sorting. * @return A {@link ProcessInstance} object. * @throws SearchException * If an exception occurs when getting the list of tasks. * @since 6.0 */ SearchResult searchOpenProcessInstances(SearchOptions searchOptions) throws SearchException; /** * List all process instances. * * @param searchOptions * The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor} for * valid fields for searching and sorting. * @return A {@link ProcessInstance} object. * @throws SearchException * If an exception occurs when getting the list of {@link ProcessInstance}. * @since 6.2 */ SearchResult searchProcessInstances(SearchOptions searchOptions) throws SearchException; /** * List all process instances with at least one failed task or the * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} state. * * @param searchOptions * The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor} for * valid fields for searching and sorting. * @return A {@link ProcessInstance} object. * @throws SearchException * If an exception occurs when getting the list of {@link ProcessInstance}. * @since 6.4.0 */ SearchResult searchFailedProcessInstances(SearchOptions searchOptions) throws SearchException; /** * List all process instances with at least one failed task or the * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} state that * are supervised by the given user. * If the specified userId does not correspond to a user, an empty SearchResult is returned. * * @param userId * The identifier of the user. * @param searchOptions * The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor} for * valid fields for searching and sorting. * @return The list of failed process instances supervised by the specified user. * @throws SearchException * If an exception occurs when getting the list of process instances. * @since 7.0 */ SearchResult searchFailedProcessInstancesSupervisedBy(long userId, SearchOptions searchOptions) throws SearchException; /** * List all open process instances supervised by a user. * If the specified userId does not correspond to a user, an empty SearchResult is returned. * * @param userId * The identifier of the user. * @param searchOptions * The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor} for * valid fields for searching and sorting. * @return The list of process instances supervised by the specified user. * @throws SearchException * If an exception occurs when getting the list of process instances. * @since 6.0 */ SearchResult searchOpenProcessInstancesSupervisedBy(long userId, SearchOptions searchOptions) throws SearchException; /** * Get the number of process data instances by process id. * * @param processInstanceId * The identifier of the process instance. * @return The number of process data instances. * @throws ProcessInstanceNotFoundException * If the specified ProcessInstance does not refer to a process instance. * @since 6.0 */ long getNumberOfProcessDataInstances(long processInstanceId) throws ProcessInstanceNotFoundException; /** * Get the number of activity data instances by activity id. * * @param activityInstanceId * The identifier of the activity instance. * @return The number of activity data instances. * @throws ActivityInstanceNotFoundException * If the specified activity instance does not refer to an activity instance. * @since 6.0 */ long getNumberOfActivityDataInstances(long activityInstanceId) throws ActivityInstanceNotFoundException; /** * Get a paged list of all process instances. * * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of results per page. * @param criterion * The sort criterion. * @return The list of process instances. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @since 6.0 */ List getProcessInstances(int startIndex, int maxResults, ProcessInstanceCriterion criterion); /** * Get a paged list of archived completed process instances. * * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of results per page. * @param criterion * The sort criterion. * @return The list of archived process instances. * @since 6.0 * @deprecated since 10.3 use {@link #getCompletedProcessInstances(int, int, ProcessInstanceCriterion)} instead */ @Deprecated(since = "10.3.0", forRemoval = true) List getArchivedProcessInstances(int startIndex, int maxResults, ProcessInstanceCriterion criterion); /** * Get a paged list of archived completed process instances. * * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of results per page. * @param criterion * The sort criterion. * @return The list of archived process instances. * @since 10.3 */ List getCompletedProcessInstances(final int startIndex, final int maxResults, final ProcessInstanceCriterion criterion); /** * Get a paged list of archived activity instances for a process instance. * * @param sourceProcessInstanceId * The identifier of the source process instance (used as root container id of the archived activity * instances). * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of result per page. * @param criterion * The sort criterion. * @return The list of archived activity instances. * @since 6.0 */ List getArchivedActivityInstances(long sourceProcessInstanceId, int startIndex, int maxResults, ActivityInstanceCriterion criterion); /** * Retrieve a paged list of open activities for a given process instance. * * @param processInstanceId * The identifier of the process instance. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of results per page. * @param criterion * The sort criterion. * @return The list of activity instances. * @since 6.0 */ List getOpenActivityInstances(long processInstanceId, int startIndex, int maxResults, ActivityInstanceCriterion criterion); /** * Get the total number of open activity instances by process instance id. * * @param processInstanceId * The identifier of the process instance. * @return The number of open activity instances. * @throws ProcessInstanceNotFoundException * If no matching process instance is found for parameter processInstanceId * @since 6.0 */ int getNumberOfOpenedActivityInstances(long processInstanceId) throws ProcessInstanceNotFoundException; /** * Get the number of open process instances. * An open process instance is a process instance that has not been archived. * * @return The total number of open process instances. * @since 6.0 */ long getNumberOfProcessInstances(); /** * Get the number of archived completed process instances. * Root process instances in state COMPLETED are counted. Process instances started by call activities won't be * counted. * * @return The number of archived process instances. * @since 6.0 * @deprecated since 10.3 use {@link #getNumberOfCompletedProcessInstances()} instead */ @Deprecated(since = "10.3.0", forRemoval = true) long getNumberOfArchivedProcessInstances(); /** * Get the number of archived completed process instances. * Root process instances in state COMPLETED are counted. Process instances started by call activities won't be * counted. * * @return The number of archived process instances. * @since 10.3 */ long getNumberOfCompletedProcessInstances(); /** * Delete the specified process instance. * * @param processInstanceId * The identifier of the process instance to delete. * @throws org.bonitasoft.engine.exception.ProcessInstanceHierarchicalDeletionException * If a process instance cannot be deleted because of a parent that is still active. * @throws DeletionException * If an error occurs during deletion. * @since 6.0 */ void deleteProcessInstance(long processInstanceId) throws DeletionException; /** * Delete active process instances, and their elements, of process definition given as input parameter respecting * the pagination parameters. * Passing {@link Integer#MAX_VALUE} as maxResults is discouraged as the amount of operations may be large and may * thus result in timeout operation. * Instead, to delete all Process instances of a specific process definition, should you should use a loop and * delete instances in bulk. * * @param processDefinitionId * Identifier of the processDefinition * @param startIndex * The index * @param maxResults * The max number of elements to retrieve per page * @return The number of elements that have been deleted * @throws DeletionException * If a process instance can't be deleted because of a parent that is still active * @since 6.1 */ long deleteProcessInstances(long processDefinitionId, int startIndex, int maxResults) throws DeletionException; /** * Delete archived process instances of process definition given as input parameter respecting the pagination * parameters. * Passing {@link Integer#MAX_VALUE} as maxResults is discouraged as the amount of operations may be large and may * thus result in timeout operation. * Instead, to delete all archived process instances of a specific process definition, you should use a loop and * delete archived instances in bulk. * * @param processDefinitionId * Identifier of the processDefinition * @param startIndex * The index * @param maxResults * The max number of elements to retrieve per page * @return The number of rows deleted from the archived process instance table related to the cases that were * deleted * @throws DeletionException * If there is some e.g. connection issue to the database * @since 6.1 */ long deleteArchivedProcessInstances(long processDefinitionId, int startIndex, int maxResults) throws DeletionException; /** * Delete all archived process instance (different states) of the source identifier list. * This work only with process instances that are root (not called by an other process). Using this method with ids * of called processes (using call activity) will not delete them. * * @param sourceProcessInstanceIds * Identifiers corresponding to {@link ArchivedProcessInstance#getSourceObjectId()}. These ids needs to be * ids of root process instances * @return The number of {@link ArchivedProcessInstance}s that have been deleted in any state. For example, process * instance can be archived in several * states: Cancelled, Aborted, Completed, Failed * @throws DeletionException * If there is some e.g. connection issue to the database * @since 6.4.0 */ long deleteArchivedProcessInstancesInAllStates(List sourceProcessInstanceIds) throws DeletionException; /** * Delete all archived process instance (different states) corresponding to the source identifier. * This work only with process instances that are root (not called by an other process). Using this method with id * of called process (using call activity) will not delete it. * * @param sourceProcessInstanceId * Identifier corresponding to {@link ArchivedProcessInstance#getSourceObjectId()}. These id needs to be the * id of a root process instances * @return The number of {@link ArchivedProcessInstance}s that have been deleted in any state. For example, process * instance can be archived in several * states: Cancelled, Aborted, Completed, Failed * @throws DeletionException * If there is some e.g. connection issue to the database * @since 6.4.0 */ long deleteArchivedProcessInstancesInAllStates(long sourceProcessInstanceId) throws DeletionException; /** * Start an instance of the process with the specified process definition, using the current session user. * Note: If the process instantiation * contract requires inputs, you must use {@link #startProcessWithInputs(long, Map)} instead. * * @param processDefinitionId * The identifier of the process definition for which an instance will be started. * @return An instance of the process. * @throws ProcessDefinitionNotFoundException * If no matching process definition is found. * @throws ProcessActivationException * If an exception occurs during activation. * @throws ProcessExecutionException * If a problem occurs when starting the process. * @since 6.0 */ ProcessInstance startProcess(long processDefinitionId) throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException; /** * Instantiates a process.
* The process variables will be initialized by the initialVariables parameter. Note: If the * process instantiation contract requires * inputs, you must use {@link #startProcessWithInputs(long, Map)} instead, transforming * initialVariables parameter as follows: in the process, * define process variables initial values (expressions) with contract inputs. * * @param processDefinitionId * The identifier of the processDefinition * @param initialVariables * The couples of initial variable/value * @return A ProcessInstance object * @throws ProcessDefinitionNotFoundException * If The identifier of process definition does not refer to any existing process definition * @throws ProcessExecutionException * If the process fails to start * @throws ProcessActivationException * If the process is disable * @since 6.1 */ ProcessInstance startProcess(long processDefinitionId, Map initialVariables) throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException; /** * Start an instance of the process with the specified process definition id, and set the initial values of the data * with the given operations. Note: * If the process instantiation contract requires inputs, you must use {@link #startProcessWithInputs(long, Map)} * instead. The best practice is to define * contract inputs in the process definition, and use these contract inputs in operations, instead of providing * operations at start time. * * @param processDefinitionId * The identifier of the process definition for which an instance will be started. * @param operations * The operations to execute to set the initial values of the data. * @param context * The context in which operations are executed. * @return An instance of the process. * @throws ProcessDefinitionNotFoundException * If no matching process definition is found. * @throws ProcessActivationException * If an exception occurs during activation. * @throws ProcessExecutionException * If a problem occurs when starting the process. * @since 6.0 */ ProcessInstance startProcess(long processDefinitionId, List operations, Map context) throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException; /** * Start an instance of the process with the specified process definition id on behalf of a given user. Note: * If the process instantiation contract * requires inputs, you must use {@link #startProcessWithInputs(long, long, Map)} instead. * * @param userId * The user id of the user. * @param processDefinitionId * The identifier of the process definition for which an instance will be started. * @return An instance of the process. * @throws UserNotFoundException * If the given user does not exist. * @throws ProcessDefinitionNotFoundException * If no matching process definition is found. * @throws ProcessActivationException * If a problem occurs when starting the process. * @throws ProcessExecutionException * If an execution problem occurs when starting the process. * @since 6.0 */ ProcessInstance startProcess(long userId, long processDefinitionId) throws UserNotFoundException, ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException; /** * Start an instance of the process with the specified process definition id on behalf of a given user, and set the * initial values of the data with the * given operations. Note: If the process instantiation contract requires inputs, you must use * {@link #startProcessWithInputs(long, long, Map)} * instead. The best practice is to design contract inputs in the process definition, and use these * contract inputs in operations, instead of providing operations at start time. * * @param userId * The identifier of the user. * @param processDefinitionId * The identifier of the process definition for which an instance will be started. * @param operations * The operations to execute to set the initial values of the data. * @param context * The context in which the operations are executed. * @return An instance of the process. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws ProcessDefinitionNotFoundException * If no matching process definition is found. * @throws ProcessActivationException * If an exception occurs during activation. * @throws UserNotFoundException * If there is no user with the specified userId. * @throws ProcessExecutionException * If a problem occurs when starting the process. * @since 6.0 */ ProcessInstance startProcess(long userId, long processDefinitionId, List operations, Map context) throws UserNotFoundException, ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException; /** * Start an instance of the process with the specified process definition id on behalf of a given user, and set the * initial values of the data with the * given initialVariables. Note: If the process instantiation contract requires inputs, you must use * {@link #startProcessWithInputs(long, long, Map)} * instead, using contract inputs stored in process variables, instead of using directly initialVariables. * * @param userId * The identifier of the user. * @param processDefinitionId * The identifier of the process definition for which an instance will be started. * @param initialVariables * The couples of initial variable/value * @return An instance of the process. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws ProcessDefinitionNotFoundException * If no matching process definition is found. * @throws ProcessActivationException * If an exception occurs during activation. * @throws ProcessExecutionException * If a problem occurs when starting the process. * @since 6.0 */ ProcessInstance startProcess(final long userId, final long processDefinitionId, final Map initialVariables) throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException; /** * Start an instance of the process with the specified process definition id, and provides inputs to fulfill Process * Contract. * See {@link org.bonitasoft.engine.bpm.contract.ContractDefinition} for details on contracts. * * @param processDefinitionId * The identifier of the process definition for which an instance will be started. * @param instantiationInputs * The couples of input name/value that allows to start a process with an instantiation contract. * @return An instance of the process. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws ProcessDefinitionNotFoundException * If no matching process definition is found. * @throws ProcessActivationException * If an exception occurs during activation. * @throws ProcessExecutionException * If a problem occurs when starting the process. * @throws ContractViolationException * If inputs don't fit with task contract * @since 7.0.0 */ ProcessInstance startProcessWithInputs(final long processDefinitionId, final Map instantiationInputs) throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException, ContractViolationException; /** * Start an instance of the process with the specified process definition id on behalf of a given user, and provides * inputs to fulfill Process Contract. * See {@link org.bonitasoft.engine.bpm.contract.ContractDefinition} for details on contracts. * * @param userId The identifier of the user in the name of whom the process is started. * @param processDefinitionId * The identifier of the process definition for which an instance will be started. * @param instantiationInputs * The couples of input name/value that allows to start a process with an instantiation contract. * @return An instance of the process. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws ProcessDefinitionNotFoundException * If no matching process definition is found. * @throws ProcessActivationException * If an exception occurs during activation. * @throws ProcessExecutionException * If a problem occurs when starting the process. * @throws ContractViolationException * If inputs don't fit with process contract * @since 7.0.0 */ ProcessInstance startProcessWithInputs(final long userId, final long processDefinitionId, final Map instantiationInputs) throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException, ContractViolationException; /** *

* Executes a flow node that is in a stable state. * Will move the activity to the next stable state and then continue the execution of the process. *

*

* Use this method only if you need to manually control the execution of a flownode. * DO NOT USE THIS TO EXECUTE HUMAN TASKS. Use {@link #executeUserTask(long, Map)} instead. *

* * @param flownodeInstanceId * The identifier of the flow node to execute. * @throws FlowNodeExecutionException * If an execution exception occurs. * @since 6.0 */ void executeFlowNode(long flownodeInstanceId) throws FlowNodeExecutionException; /** *

* Executes a flow node that is in a stable state on behalf of a given user * Will make the flow node go in the next stable state and then continue the execution of the process * If userId equals 0, the logged-in user is declared as the executer of the flow node. * The user, who executed the flow node on behalf of a given user, is declared as a executer delegate. *

*

* Use this method only if you need to manually control the execution of a flownode. * DO NOT USE THIS TO EXECUTE HUMAN TASKS. Use {@link #executeUserTask(long, long, Map)} instead. *

* * @param userId * The identifier of the user for which you want to execute the flow node * @param flownodeInstanceId * The identifier of the flow node to execute * @throws FlowNodeExecutionException * If an execution exception occurs * @since 6.0.1 */ void executeFlowNode(long userId, long flownodeInstanceId) throws FlowNodeExecutionException; /** * Returns all activities (active and finished) of a process instance. * * @param processInstanceId * The identifier of the process instance, * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of results to get. * @return The matching set of activity instances. * @since 6.0 */ List getActivities(long processInstanceId, int startIndex, int maxResults); /** * Get the specified process instance. * * @param processInstanceId * The identifier of the process instance. * @return The matching instance of process. * @throws ProcessInstanceNotFoundException * If there is no process instance with the specified identifier. * @since 6.0 */ ProcessInstance getProcessInstance(long processInstanceId) throws ProcessInstanceNotFoundException; /** * Get the specified activity instance. * * @param activityInstanceId * The identifier of the activity instance. * @return The matching activity instance. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws ActivityInstanceNotFoundException * If the activity cannot be found. * @throws org.bonitasoft.engine.exception.RetrieveException * If the activity instance cannot be retrieved. * @since 6.0 */ ActivityInstance getActivityInstance(long activityInstanceId) throws ActivityInstanceNotFoundException; /** * Get a specified flow node instance. * * @param flowNodeInstanceId * The identifier of the flow node instance. * @return The matching flow node instance. * @throws FlowNodeInstanceNotFoundException * If the given flow node instance does not exist. * @since 6.0 */ FlowNodeInstance getFlowNodeInstance(final long flowNodeInstanceId) throws FlowNodeInstanceNotFoundException; /** * Get an activity instance that is archived. * * @param sourceActivityInstanceId * The identifier of the source activity instance. * @return The matching archived activity instance. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws ActivityInstanceNotFoundException * If the archived activity instance cannot be found. * @throws org.bonitasoft.engine.exception.RetrieveException * If the archived activity instance cannot be retrieved. * @since 6.0 */ ArchivedActivityInstance getArchivedActivityInstance(long sourceActivityInstanceId) throws ActivityInstanceNotFoundException; /** * Get the list of human task instances assigned to the specified user. * * @param userId * The identifier of the user. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of elements to get per page. * @param criterion * The sort criterion. * @return The matching list of task instances. * @throws org.bonitasoft.engine.session.InvalidSessionException * Occurs when the session is invalid. * @throws org.bonitasoft.engine.exception.RetrieveException * If a task instance cannot be retrieved. * @since 6.0 */ List getAssignedHumanTaskInstances(long userId, int startIndex, int maxResults, ActivityInstanceCriterion criterion); /** * Get the list of pending human task instances available to the specified user. * A human task is pending for a given user if it is not yet assigned and if the * user is a candidate either through an {@link org.bonitasoft.engine.bpm.actor.ActorMember} or through a * {@link org.bonitasoft.engine.filter.UserFilter}. * * @param userId * The identifier of the user. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of elements to get per page. * @param pagingCriterion * The criterion for sorting the items over pages. * @return The list of matching task instances. * @throws org.bonitasoft.engine.session.InvalidSessionException * Occurs when the session is invalid. * @since 6.0 */ List getPendingHumanTaskInstances(long userId, int startIndex, int maxResults, ActivityInstanceCriterion pagingCriterion); /** * Count the total number of human task instances assigned to the specified user. * * @param userId * The identifier of a user. * @return A number of human task instances assigned. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws org.bonitasoft.engine.exception.RetrieveException * If an error occurs while retrieving an instance of an activity. * @since 6.0 */ long getNumberOfAssignedHumanTaskInstances(long userId); /** * For a specified list of users, get the number of pending tasks. * * @param userIds * A list of user identifiers. * @return A map with userId as key and number of tasks as value. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws org.bonitasoft.engine.exception.RetrieveException * can't retrieve an instance of activity * @since 6.0 */ Map getNumberOfOpenTasks(List userIds); /** * Count the number of pending human task instances available to a specified user. * * @param userId * The identifier of a user. * @return A number of pending human task instances. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws org.bonitasoft.engine.exception.RetrieveException * If an error occurs while retrieving an instance of an activity. * @since 6.0 */ long getNumberOfPendingHumanTaskInstances(long userId); /** * Retrieve a human task instance by the corresponding activity instance id. * * @param activityInstanceId * The identifier of the activity instance. * @return The matching instance of human task. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws ActivityInstanceNotFoundException * If the human task cannot be found. * @throws org.bonitasoft.engine.exception.RetrieveException * If an error occurs while retrieving the instance of the activity. * @since 6.0 */ HumanTaskInstance getHumanTaskInstance(long activityInstanceId) throws ActivityInstanceNotFoundException; /** * Get a list of event instances related to a process instance that match the specified conditions. * * @param rootContainerId * The identifier of the containing root process instance. * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of results to get. * @param sortingType * The criterion for sorting event instances. * @return The matching list of event instances. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @since 6.0 */ List getEventInstances(long rootContainerId, int startIndex, int maxResults, EventCriterion sortingType); /** * Assign a task to a user with given user identifier. * * @param userTaskId * The identifier of the user task. * @param userId * The identifier of the user. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws UpdateException * If an error occurs while updating the activity instance. * @since 6.0 */ void assignUserTask(long userTaskId, long userId) throws UpdateException; /** * Assign a task to a user with given user identifier if the task is not currently assigned to another user. * * @param userTaskId * The identifier of the user task. * @param userId * The identifier of the user. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws UpdateException * If an error occurs while updating the activity instance. * @since 7.6 */ void assignUserTaskIfNotAssigned(long userTaskId, long userId) throws UpdateException; /** * Updates the actors of the user task. It evaluates again the eligible users for that task. * * @param userTaskId * The identifier of the user task * @throws UpdateException * If an exception occurs during the evaluation of actors. * @since 6.1 */ void updateActorsOfUserTask(long userTaskId) throws UpdateException; /** * Returns all data of a process instance. * * @param processInstanceId * The identifier of the process instance. * @param startIndex * The index of the page of results to get (starting from 0). * @param maxResults * The maximum number of results to get. * @return The matching list of dataInstances. * @since 6.0 */ List getProcessDataInstances(long processInstanceId, int startIndex, int maxResults); /** * Get the value of named data item from a specified process instance. * The value is returned in a DataInstance object. * * @param dataName * The name of the data item. * @param processInstanceId * The identifier of the process instance. * @return An instance of the data * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws DataNotFoundException * If the specified data value cannot be found. * @since 6.0 */ DataInstance getProcessDataInstance(String dataName, long processInstanceId) throws DataNotFoundException; /** *

* Update the value of a named process variable of a specified process instance. *

*

* Warning: there is a known limitation about the usage of this method: if the new data value is of a custom * data type AND * the call to this method is performed from a java client configured to access Bonita Engine through HTTP, * the API method call fails because the class of the custom data type cannot be loaded yet. * The workaround to this limitation is to put the jar file containing your custom data * type classes into the classloader of Bonita ([BONITA_INSTALLATION_FOLDER]/server/lib/bonita folder in a standard * installation), * and REMOVE it from the libraries of all the processes that use them. *

*

* If the call is made from a standard Bonita bundle, the calls to Engine APIs are performed locally (and not * through HTTP), * so it does NOT fall into this limitation. So in a standard Bonita installation, it is safe to call this * method in code like * Rest API Extensions, Groovy scripts, Custom connector implementations... *

* * @param dataName * The name of the data item. * @param processInstanceId * The identifier of the process instance. * @param dataValue * The new value for the data item. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws UpdateException * If a problem occurs while updating the data value. * @since 6.0 */ void updateProcessDataInstance(String dataName, long processInstanceId, Serializable dataValue) throws UpdateException; /** *

* Update the value of named process variables of a specified process instance. *

*

* Warning: there is a known limitation about the usage of this method: if the new data value is of a custom * data type AND * the call to this method is performed from a java client configure to access Bonita Engine through HTTP, * the API method call fails because the class of the custom data type cannot be loaded yet. * The workaround to this limitation is to put the jar file containing your custom data * type classes into the classloader of Bonita ([BONITA_INSTALLATION_FOLDER]/server/lib/bonita folder in a standard * installation), * and REMOVE it from the libraries of all the processes that use them. *

*

* If the call is made from a standard Bonita bundle, the calls to Engine APIs are performed locally (and not * through HTTP), * so it does NOT fall into this limitation. So in a standard Bonita installation, it is safe to call this * method in code like * Rest API Extensions, Groovy scripts, Custom connector implementations... *

* * @param processInstanceId * The identifier of the process instance. * @param dataNameValues * The mapping between the data name and its value to update to. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws UpdateException * If a problem occurs while updating the data value. * @since 6.2.3 */ void updateProcessDataInstances(final long processInstanceId, final Map dataNameValues) throws UpdateException; /** * Get a list of the data instances from a specified activity instance. * * @param activityInstanceId * The identifier of the activity instance. * @param startIndex * The index of the first result (starting at 0). * @param maxResults * The maximum number of results to get. * @return The list of matching DataInstances. * @since 6.0 */ List getActivityDataInstances(long activityInstanceId, int startIndex, int maxResults); /** * Get a named data instance from a specified activity instance. * * @param dataName * The name of the data item. * @param activityInstanceId * The identifier of the activity instance. * @return An instance of data. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws DataNotFoundException * If the specified data value cannot be found. * @since 6.0 */ DataInstance getActivityDataInstance(String dataName, long activityInstanceId) throws DataNotFoundException; /** *

* Update the value of a named activity variable of a specified activity instance. *

*

* Warning: there is a known limitation about the usage of this method: if the new data value is of a custom * data type AND * the call to this method is performed from a java client configure to access Bonita Engine through HTTP, * the API method call fails because the class of the custom data type cannot be loaded yet. * The workaround to this limitation is to put the jar file containing your custom data * type classes into the classloader of Bonita ([BONITA_INSTALLATION_FOLDER]/server/lib/bonita folder in a standard * installation), * and REMOVE it from the libraries of all the processes that use them. * Or use {@link ProcessRuntimeAPI#updateActivityInstanceVariables(List, long, Map)} instead *

*

* If the call is made from a standard Bonita bundle, the calls to Engine APIs are performed locally (and not * through HTTP), * so it does NOT fall into this limitation. So in a standard Bonita installation, it is safe to call this * method in code like * Rest API Extensions, Groovy scripts, Custom connector implementations... *

* * @param dataName * The name of the data instance. * @param activityInstanceId * The identifier of the activity instance. * @param dataValue * The new value of the data to set. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws UpdateException * If an error occurs during the update. * @since 6.0 */ void updateActivityDataInstance(String dataName, long activityInstanceId, Serializable dataValue) throws UpdateException; /** * Update the value of a named transient data instance in a specified activity instance. * * @param dataName * The name of the data instance. * @param activityInstanceId * The identifier of the activity instance. * @param dataValue * The new value of the data to set. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws UpdateException * If an error occurs during the update. * @since 6.0 */ void updateActivityTransientDataInstance(String dataName, long activityInstanceId, Serializable dataValue) throws UpdateException; /** * Get a list of the transient data instances from a specified activity instance. * * @param activityInstanceId * The identifier of the activity instance. * @param startIndex * The index of the first result (starting at 0). * @param maxResults * The maximum number of results to get. * @return The list of matching DataInstances. * @since 6.0 */ List getActivityTransientDataInstances(long activityInstanceId, int startIndex, int maxResults); /** * Get a named transient data instance from a specified activity instance. * * @param dataName * The name of the data item. * @param activityInstanceId * The identifier of the activity instance. * @return An instance of data. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws DataNotFoundException * If the specified data value cannot be found. * @since 6.0 */ DataInstance getActivityTransientDataInstance(String dataName, long activityInstanceId) throws DataNotFoundException; /** * Get the date when the specified activity instance reached the given state. * * @param activityInstanceId * The identifier of the activity instance. * @param state * The state of interest. * @return The date at which the activity instance reached the state. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws org.bonitasoft.engine.exception.RetrieveException * If an error occurs while retrieving the activity instance. * @since 6.0 */ Date getActivityReachedStateDate(long activityInstanceId, String state); /** *

* Update the value of named activity variables of a specified activity instance. *

*

* Warning: there is a known limitation about the usage of this method: if the new data value is of a custom * data type AND * the call to this method is performed from a java client configure to access Bonita Engine through HTTP, * the API method call fails because the class of the custom data type cannot be loaded yet. * The workaround to this limitation is to put the jar file containing your custom data * type classes into the classloader of Bonita ([BONITA_INSTALLATION_FOLDER]/server/lib/bonita folder in a standard * installation), * and REMOVE it from the libraries of all the processes that use them. * Or use {@link ProcessRuntimeAPI#updateActivityInstanceVariables(List, long, Map)} instead *

*

* If the call is made from a standard Bonita bundle, the calls to Engine APIs are performed locally (and not * through HTTP), * so it does NOT fall into this limitation. So in a standard Bonita installation, it is safe to call this * method in code like Rest API Extensions, Groovy scripts, Custom connector implementations... *

* * @param activityInstanceId * The activity identifier. * @param variables * A map which contains several pairs of variable name and value. * @throws UpdateException * If a problem occurs while updating one of the data instance value. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @since 6.0 */ void updateActivityInstanceVariables(long activityInstanceId, Map variables) throws UpdateException; /** * Update the values of variables in an activity instance using expressions. * * @param operations * A sequence of operations on expressions that update the values variables. * @param activityInstanceId * The identifier of the activity instance. * @param expressionContexts * Store all information identifying the container that the data belongs to. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws UpdateException * If an error occurs during the update. * @since 6.0 */ void updateActivityInstanceVariables(List operations, long activityInstanceId, Map expressionContexts) throws UpdateException; /** * Update the due date of a task. * * @param userTaskId * The identifier of the task to update. * @param dueDate * The new due date for the task. * @throws UpdateException * If the activity does not exist or the update cannot be fulfilled. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @since 6.0 */ void updateDueDateOfTask(long userTaskId, Date dueDate) throws UpdateException; /** * Get an instance of a task asssigned to a given user for the specified process instance. * * @param processInstanceId * The identifier of the process instance. * @param userId * The identifier of the user. * @return The identifier of a user task from the process instance that is assigned to the user. * @throws ProcessInstanceNotFoundException * If the given process instance does not exist. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws UserNotFoundException * If there is no user with the specified id. * @throws org.bonitasoft.engine.exception.RetrieveException * If an error occurs happen while retrieving the activity instance. * @since 6.0 */ long getOneAssignedUserTaskInstanceOfProcessInstance(long processInstanceId, long userId) throws ProcessInstanceNotFoundException, UserNotFoundException; /** * Get an instance of a task asssigned to a given user for the specified process definition. * * @param processDefinitionId * The identifier of the process definition. * @param userId * The identifier of a user. * @return The identifier of a user task from the process definition that is assigned to the user. * @throws ProcessDefinitionNotFoundException * If the given process definition does not exist. * @throws UserNotFoundException * If the given user does not exist. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws org.bonitasoft.engine.exception.RetrieveException * If an error occurs happen while retrieving the activity instance. * @since 6.0 */ long getOneAssignedUserTaskInstanceOfProcessDefinition(long processDefinitionId, long userId) throws ProcessDefinitionNotFoundException, UserNotFoundException; /** * Get the state of a specified activity instance. * * @param activityInstanceId * The identifier of the activity instance. * @return The state of the activity instance. * @throws ActivityInstanceNotFoundException * If the activity cannot be found. * @since 6.0 */ String getActivityInstanceState(long activityInstanceId) throws ActivityInstanceNotFoundException; /** * Check whether a specified task can be executed by a given user. * * @param activityInstanceId * The identifier of the activity instance. * @param userId * The identifier of a user. * @return A flag that indicates whether task can be executed by the user. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws ActivityInstanceNotFoundException * If the activity cannot be found. * @throws UserNotFoundException * If there is no user with the specified userId. * @throws org.bonitasoft.engine.exception.RetrieveException * If an error occurs happen while retrieving the activity instance. * @since 6.0 */ boolean canExecuteTask(long activityInstanceId, long userId) throws ActivityInstanceNotFoundException, UserNotFoundException; /** * Release a task (unclaim or unassign). After the operation, the task is in the pending task list. * * @param userTaskId * The identifier of the user task. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws ActivityInstanceNotFoundException * If the activity cannot be found. * @throws UpdateException * If a problem occurs while release (un-assigning) the user task. * @since 6.0 */ void releaseUserTask(long userTaskId) throws ActivityInstanceNotFoundException, UpdateException; /** * List the archived process instances for the specified process instance. * A process instance is archived when it changes state, so there are several archived process instances for each * process instance. * * @param processInstanceId * The identifier of the process instance. * @param startIndex * The index of the page of results to get. * @param maxResults * The maximum number of results to get. * @return The list of archived process instances. * @throws org.bonitasoft.engine.session.InvalidSessionException * If no current valid session is found. * @throws org.bonitasoft.engine.exception.RetrieveException * If the search fails because an archived process instance cannot be read. * @since 6.0 */ List getArchivedProcessInstances(long processInstanceId, int startIndex, int maxResults); /** * Get the last archived instance of a process instance. * A process instance is archived when it changes state, so there are several archived process instances for each * process instance. * The last archived instance is returned. * * @param sourceProcessInstanceId * The identifier of the source process instance, i.e. not an archived version, the original process instance * id. * @return The archived process instance. * @throws ArchivedProcessInstanceNotFoundException * If no archived process instance can be found with the provided Id. * @throws org.bonitasoft.engine.session.InvalidSessionException * If no current valid session is found. * @throws org.bonitasoft.engine.exception.RetrieveException * If the search fails because an archived process instance cannot be read. * @since 6.0 */ ArchivedProcessInstance getFinalArchivedProcessInstance(long sourceProcessInstanceId) throws ArchivedProcessInstanceNotFoundException; /** * Set the state of an activity instance. * * @param activityInstanceId * The identifier of the activity instance. * @param stateId * The identifier of the required state. * @throws org.bonitasoft.engine.session.InvalidSessionException * If no current valid session is found. * @throws UpdateException * If an error occurs during the update. * @since 6.0 */ void setActivityStateById(long activityInstanceId, int stateId) throws UpdateException; /** * Set the state of an activity instance. * * @param activityInstanceId * The identifier of the activity instance. * @param state * The name of the required state. * @throws org.bonitasoft.engine.session.InvalidSessionException * If no current valid session is found. * @throws UpdateException * If an error occurs during the update. * @since 6.0 */ void setActivityStateByName(long activityInstanceId, String state) throws UpdateException; /** * Set a state of a process instance. * * @param processInstance * The process instance. * @param state * The name of the required state. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws UpdateException * If an error occurs during the update. * @since 6.0 */ void setProcessInstanceState(ProcessInstance processInstance, String state) throws UpdateException; /** * Set the priority of a user task. * * @param userTaskInstanceId * The identifier of user task instance. * @param priority * The new priority of this task. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws UpdateException * If an error occurs during the update. * @since 6.0 */ void setTaskPriority(long userTaskInstanceId, TaskPriority priority) throws UpdateException; /** * Execute a connector in a specified processDefinition. * * @param connectorDefinitionId * The identifier of connector definition. * @param connectorDefinitionVersion * The version of the connector definition. * @param connectorInputParameters * The expressions related to the connector input paramters. * @param inputValues * The parameters values for expression needed when evaluating the connector. * @param processDefinitionId * The identifier of the process definition. * @return A map with connector parameter names and parameter value objects. * @throws ConnectorExecutionException * If an error occurs during connector execution. * @throws ConnectorNotFoundException * If there is no connector definition with the specified identifier or version. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @since 6.0 */ Map executeConnectorOnProcessDefinition(String connectorDefinitionId, String connectorDefinitionVersion, Map connectorInputParameters, Map> inputValues, long processDefinitionId) throws ConnectorExecutionException, ConnectorNotFoundException; /** * Execute a connector in a specified processDefinition with operations. * * @param connectorDefinitionId * The identifier of connector definition. * @param connectorDefinitionVersion * The version of the connector definition. * @param connectorInputParameters * The expressions related to the connector input parameters. * @param inputValues * The parameters values for expression needed when evaluating the connector. * @param operations * The operations used when executing the connector. * @param operationInputValues * The input values for the operations. * @param processDefinitionId * The identifier of the process definition. * @return A map with connector parameter names and parameter value objects after operations and connector * execution. * @throws ConnectorExecutionException * If an error occurs during connector execution. * @throws ConnectorNotFoundException * If there is no connector definition with the specified identifier or version. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @since 6.0 */ Map executeConnectorOnProcessDefinition(String connectorDefinitionId, String connectorDefinitionVersion, Map connectorInputParameters, Map> inputValues, List operations, Map operationInputValues, long processDefinitionId) throws ConnectorExecutionException, ConnectorNotFoundException; /** * Search the archived human tasks for tasks that match the search options. * * @param searchOptions * The search conditions and the options for sorting and paging the results. See * {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid * fields * for searching and sorting. * @return The archived human tasks that match the search conditions. * @throws SearchException * If the search could not be completed correctly. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @since 6.0 */ SearchResult searchArchivedHumanTasks(SearchOptions searchOptions) throws SearchException; /** * Search the assigned human tasks for tasks that match the search options and are administered by the specified * user. * * @param managerUserId * The identifier of the user. * @param searchOptions * The search conditions and the options for sorting and paging the results. See * {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for * searching and sorting. * @return The assigned human tasks that match the search conditions and are supervised by the user. * @throws SearchException * If there is an error in the search conditions. * @since 6.0 */ SearchResult searchAssignedTasksManagedBy(long managerUserId, SearchOptions searchOptions) throws SearchException; /** * Search the pending human tasks for tasks that match the search options and are supervised by the specified user. * * @param userId * The identifier of the user. * @param searchOptions * The search conditions and the options for sorting and paging the results. See * {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for * searching and sorting. * @return The pending human tasks that match the search conditions and are supervised by the user. * @throws SearchException * If there is an error in the search conditions. * @since 6.0 */ SearchResult searchPendingTasksSupervisedBy(long userId, SearchOptions searchOptions) throws SearchException; /** * Search the pending human tasks for tasks available to the specified user. * * @param userId * The identifier of the user. * @param searchOptions * The search conditions and the options for sorting and paging the results. See * {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for * searching and sorting. * @return The pending human tasks that match the search conditions and are available to the user. * @throws SearchException * If there is an error in the search conditions. * @since 6.0 */ SearchResult searchPendingTasksForUser(long userId, SearchOptions searchOptions) throws SearchException; /** * Search the pending human tasks assigned to a specified user. * * @param userId * The identifier of the user. * @param searchOptions * The search conditions and the options for sorting and paging the results. See * {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for * searching and sorting. * @return The pending human tasks that match the search conditions and are assigned to the user. * @throws SearchException * If there is an error in the search conditions. * @since 7.5.5 */ SearchResult searchPendingTasksAssignedToUser(long userId, SearchOptions searchOptions) throws SearchException; /** * Search the pending human tasks for tasks that match the search options and are managed by the specified user. * * @param managerUserId * The identifier of the user. * @param searchOptions * The search conditions and the options for sorting and paging the results. See * {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for * searching and sorting. * @return The pending human tasks that match the search conditions and are managed by the user. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws SearchException * If there is an error in the search conditions. * @since 6.0 */ SearchResult searchPendingTasksManagedBy(long managerUserId, SearchOptions searchOptions) throws SearchException; /** * Search the assigned and pending human tasks for the specified user, on the specified root process definition, * corresponding to the options. * * @param rootProcessDefinitionId * The identifier of the root process definition * @param userId * The identifier of the user * @param searchOptions * The search conditions and the options for sorting and paging the results. See * {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for * searching and sorting. * @return The assigned and pending human tasks * @throws SearchException * If there is an error in the search conditions. * @since 6.3.3 */ SearchResult searchAssignedAndPendingHumanTasksFor(final long rootProcessDefinitionId, final long userId, final SearchOptions searchOptions) throws SearchException; /** * Search the assigned and pending human tasks for any user, on the specified root process definition, corresponding * to the options. * * @param rootProcessDefinitionId * The identifier of the root process definition * @param searchOptions * The search conditions and the options for sorting and paging the results. See * {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for * searching and sorting. * @return The assigned and pending human tasks * @throws SearchException * If there is an error in the search conditions. * @since 6.3.3 */ SearchResult searchAssignedAndPendingHumanTasks(final long rootProcessDefinitionId, final SearchOptions searchOptions) throws SearchException; /** * Search the assigned and pending human tasks for any user corresponding to the options. * * @param searchOptions The search conditions and the options for sorting and paging the results. See * {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for * searching and sorting. * @return The assigned and pending human tasks * @throws SearchException If there is an error in the search conditions. * @since 7.6.1 */ SearchResult searchAssignedAndPendingHumanTasks(final SearchOptions searchOptions) throws SearchException; /** * Get the number of assigned and pending overdue tasks for the specified users. * * @param userIds * A list of user identifiers. * @return A map of user identifiers and numbers of overdue tasks. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @since 6.0 */ Map getNumberOfOverdueOpenTasks(List userIds); /** * Cancels the process instance and all of its active flow nodes. * * @param processInstanceId * The identifier of the root process instance. * @throws ProcessInstanceNotFoundException * If the process instance identifier does not refer to a process instance. * @throws UpdateException * If an exception occurs during the process instance canceling. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ void cancelProcessInstance(long processInstanceId) throws ProcessInstanceNotFoundException, UpdateException; /** * Reset the state of a failed {@link org.bonitasoft.engine.bpm.flownode.FlowNodeInstance} to its previous state and * then execute it. Pre-condition: the * {@code FlowNodeInstance} must be in state FAILED. If this condition is not respected, a * ActivityExecutionException is thrown. *

If the {@code FlowNodeInstance} contains failed * {@link org.bonitasoft.engine.bpm.connector.ConnectorInstance}s, they will be re-executed. In the case * where the connector execution fails again, the {@code FlowNodeInstance} will remain in failed state. There is not * counter on the number of * re-executions

* * @param activityInstanceId * The identifier of the {@code FlowNodeInstance} to be re-retried. * @throws org.bonitasoft.engine.session.InvalidSessionException * when session is invalid, e.g. the session has expired. * @throws ActivityInstanceNotFoundException * when no {@code FlowNodeInstance} is found with the specified identifier. * @throws ActivityExecutionException * occurs if the current Flownode is not in FAILED state, or while resetting the state, or while executing * the {@code FlowNodeInstance}. * @since 6.0 * @see org.bonitasoft.engine.bpm.flownode.FlowNodeInstance * @see org.bonitasoft.engine.bpm.connector.ConnectorInstance */ void retryTask(long activityInstanceId) throws ActivityInstanceNotFoundException, ActivityExecutionException; /** * When a matching BPM event couple messageInstance / waitingMessageEvent fails to execute and no failure handling * has been successful, a log message * indicates that this method can be called to try again the execution. The necessary parameters are also indicated. * * @param messageInstanceId the ID of the message instance to try to trigger again. * @param waitingMessageEventId the ID of the waiting message event to try to trigger again. * @throws ExecutionException if the execution failed. A more precise cause is given in the getCause() method. */ void executeMessageCouple(long messageInstanceId, long waitingMessageEventId) throws ExecutionException; /** * Evaluate an expression in the context of the specified process. * Some context values can also be provided * * @param expression * The expression to evaluate. * @param context * The context values that are provided for evaluating the expression. * @param processDefinitionId * The identifier of the process definition in which the expression is evaluated. * @return The result of the evaluation. * @throws org.bonitasoft.engine.session.InvalidSessionException * If there is no current valid session. * @throws ExpressionEvaluationException * If an error occurs while evaluating the expression. * @since 6.0 */ Serializable evaluateExpressionOnProcessDefinition(Expression expression, Map context, long processDefinitionId) throws ExpressionEvaluationException; /** * Get the number of comments matching the search conditions. * * @param searchOptions * The search conditions and the options for sorting and paging the results. * @return The number of comments matching the search conditions. * @throws SearchException * If the search could not be completed correctly. * @since 6.0 */ long countComments(SearchOptions searchOptions) throws SearchException; /** * Get the number of attachments matching the search conditions. * * @param searchOptions * The search conditions and the options for sorting and paging the results. * @return The number of attachments matching the search conditions. * @throws SearchException * If the search could not be completed correctly. * @since 6.0 */ long countAttachments(SearchOptions searchOptions) throws SearchException; /** * Send a BPMN signal event. Invoking this method acts as executing a Throw Signal Event. * * @param signalName * The signal name. * @throws org.bonitasoft.engine.session.InvalidSessionException * If there is no current valid session. * @throws SendEventException * If an exception occurs while sending signal. * @since 6.0 */ void sendSignal(String signalName) throws SendEventException; /** * Send a BPMN message event. Invoking this method acts as executing a Throw Message Event. * * @param messageName * The message name. * @param targetProcess * An expression representing the target process name. * @param targetFlowNode * An expression representing the target flow node name. Optional parameter that can be used to explicitly * set the target flow node if the * message is caught by different flow nodes in the same process. If not necessary, pass null value. * @param messageContent * A key->value map containing the message data, with the data name as key. * @throws org.bonitasoft.engine.session.InvalidSessionException * If there is no current valid session. * @throws SendEventException * If an exception occurs while sending message. * @since 6.0 */ void sendMessage(String messageName, Expression targetProcess, Expression targetFlowNode, Map messageContent) throws SendEventException; /** * Send a BPMN message event, with message correlation. Invoking this method acts as executing a Throw Message * Event. * * @param messageName * The message name. * @param targetProcess * An expression representing the target process name. * @param targetFlowNode * An expression representing the target flow node name. Optional parameter that can be used to explicitly * set the target flow node if the * message is caught by different flow nodes in the same process. If not necessary, pass null value. * @param messageContent * A key->value map containing the message data, with the data name as key. * @param correlations * The message correlations (five maximum). * @throws org.bonitasoft.engine.session.InvalidSessionException * If there is no current valid session. * @throws SendEventException * If there are too many correlations (more than 5) or an exception occurs while sending message. * @since 6.0 */ void sendMessage(String messageName, Expression targetProcess, Expression targetFlowNode, Map messageContent, Map correlations) throws SendEventException; /** * Delete messages until date and matching to search condition. * * @param creationDate * The date until the messages will be deleted. * @param searchOptions * The optional search conditions for retrieve messages that will be removed. * @return The number of message deleted * @throws ExecutionException * If the deletion could not be completed correctly. * @since 7.10 */ int deleteMessageByCreationDate(long creationDate, SearchOptions searchOptions) throws ExecutionException; /** * Retrieve an ArchivedProcessInstance specified by its identifier. * * @param archivedProcessInstanceId * The identifier of the ArchivedProcessInstance to be retrieved. * @return The ArchivedProcessInstance instance. * @throws ArchivedProcessInstanceNotFoundException * If the ArchivedProcessInstance was not found. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws org.bonitasoft.engine.exception.RetrieveException * If an error occurs while trying to retrieve the ArchivedProcessInstance. * @since 6.0 */ ArchivedProcessInstance getArchivedProcessInstance(long archivedProcessInstanceId) throws ArchivedProcessInstanceNotFoundException; /** * Retrieve an ArchivedFlowNodeInstance specified by its identifier. * * @param archivedFlowNodeInstanceId * The identifier of the ArchivedFlowNodeInstance to be retrieved. * @return The ArchivedFlowNodeInstance instance. * @throws ArchivedFlowNodeInstanceNotFoundException * If the ArchivedFlowNodeInstance was not found. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws org.bonitasoft.engine.exception.RetrieveException * If an error occurs while trying to retrieve the ArchivedFlowNodeInstance. * @since 6.0 */ ArchivedFlowNodeInstance getArchivedFlowNodeInstance(long archivedFlowNodeInstanceId) throws ArchivedFlowNodeInstanceNotFoundException; /** * Retrieve an ArchivedComment specified by its identifier. * * @param archivedCommentId * The identifier of the ArchivedComment to be retrieved. * @return The ArchivedComment instance. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws org.bonitasoft.engine.exception.RetrieveException * If an error occurs while trying to retrieve the ArchivedComment. * @throws NotFoundException * If no ArchivedComment was found with the specified archivedCommentId. * @since 6.0 */ ArchivedComment getArchivedComment(long archivedCommentId) throws NotFoundException; /** * Search for connector instances. * * @param searchOptions * The search conditions and the options for sorting and paging the results. See * {@link org.bonitasoft.engine.bpm.connector.ConnectorInstancesSearchDescriptor} for valid fields for * searching and sorting. * @return The {@link SearchResult} containing the ConnectorInstances matching the search options. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws SearchException * If the search could not be completed correctly. * @since 6.0 */ SearchResult searchConnectorInstances(SearchOptions searchOptions) throws SearchException; /** * Search for archived connector instances. * * @param searchOptions * The search options parameters. See * {@link org.bonitasoft.engine.bpm.connector.ArchiveConnectorInstancesSearchDescriptor} for valid fields for * searching and sorting. * @return The {@link SearchResult} containing the ArchivedConnectorInstances matching the search * options. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws SearchException * If the search could not be completed correctly. * @since 6.0 */ SearchResult searchArchivedConnectorInstances(SearchOptions searchOptions) throws SearchException; /** * List the named human tasks belonging to the specified process instance. * * @param rootProcessInstanceId * The identifier of the root process instance. * @param taskName * The name of the required human tasks. * @param startIndex * The result start index (strating from 0). * @param maxResults * The maximum number of results to retrieve. * @return The list of matching human task instances. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @since 6.0 */ List getHumanTaskInstances(long rootProcessInstanceId, String taskName, int startIndex, int maxResults); /** * Return the last created human task instance with the specified name for the given process instance. * * @param rootProcessInstanceId * The identifier of the root process instance. * @param taskName * The name of the required human task. * @return A HumanTaskInstance, in its latest state. * @throws NotFoundException * If no current task with provided name is found. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @since 6.0 */ HumanTaskInstance getLastStateHumanTaskInstance(long rootProcessInstanceId, String taskName) throws NotFoundException; /** * Search for archived activity instances in terminal states. Archived activity instances in intermediate states are * not considered. * * @param searchOptions * The criterion used to search for archived activity instances. See * {@link org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor} for valid fields for * searching and sorting. * @return A {@link SearchResult} containing the search result. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws SearchException * If an exception occurs during the search. * @since 6.0 */ SearchResult searchArchivedActivities(SearchOptions searchOptions) throws SearchException; /** * Search for activity instances. * * @param searchOptions * The criterion used to search for activity instances. See * {@link org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor} for valid * fields for searching and sorting. * @return A {@link SearchResult} containing the search result. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws SearchException * If the search could not be completed correctly. * @since 6.0 */ SearchResult searchActivities(SearchOptions searchOptions) throws SearchException; /** * Search for flow node instances (activities, gateways and events). * * @param searchOptions * The criterion used to search for flow node instances. See * {@link org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor} for valid * fields for searching and sorting. * @return A {@link SearchResult} containing the search result * @throws org.bonitasoft.engine.session.InvalidSessionException * If the ession is invalid, e.g session has expired. * @throws SearchException * If an exception occurs during the search. * @since 6.0 */ SearchResult searchFlowNodeInstances(SearchOptions searchOptions) throws SearchException; /** * Search for archived flow node instances (activities, gateways and events) * * @param searchOptions * The options used to search for flow node instances. See * {@link org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor} for * valid fields for searching and sorting. * @return A {@link SearchResult} containing the search result. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g session has expired. * @throws SearchException * If an exception occurs during the search. * @see ArchivedFlowNodeInstance * @since 6.0 */ SearchResult searchArchivedFlowNodeInstances(SearchOptions searchOptions) throws SearchException; /** * Search for all tasks available to a specified user. * A task is available to a user if is assigned to the user or it is pending for that user. * * @param userId * The identifier of the user for whom the tasks are available. * @param searchOptions * The options used to search for tasks. See * {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for * searching and sorting. * @return The list of tasks matching the search options. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the current session is invalid. * @throws SearchException * If an exception occurs during the search. * @since 6.0 */ SearchResult searchMyAvailableHumanTasks(long userId, SearchOptions searchOptions) throws SearchException; /** * Search for all tasks assigned to the user or taken by others users, or pending for that user. * * @param userId * The identifier of the user for whom the tasks are available. * @param searchOptions * The options used to search for tasks. See * {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid fields for * searching and sorting. * @return The list of tasks matching the search options. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the current session is invalid. * @throws SearchException * If an exception occurs during the search. * @since 7.15 */ SearchResult searchPendingOrAssignedToUserOrAssignedToOthersTasks(long userId, SearchOptions searchOptions) throws SearchException; /** * Search for comments related to the specified process instance. * * @param searchOptions * The options used to search for comments. See * {@link org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor} for valid fields for searching * and sorting. * @return The matching comments. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws SearchException * If an exception occurs during the search. * @since 6.0 */ SearchResult searchComments(SearchOptions searchOptions) throws SearchException; /** * Add a comment on a process instance. Be aware that operations inside tasks are executed by the System, not the * task assignee. * Therefore, when called from an operation inside a task, the user "System" will be marked as having made the * comment. If you wish to add a comment made by the assigned user in those places, * use addProcessCommentOnBehalfOfUser. * * @param processInstanceId * The identifier of the process instance. * @param comment * The content of the comment. * @return The newly created comment. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws CreationException * If the parameter processInstanceId does not refer to any active process instance (existing and * non-archived). * @since 6.1 */ Comment addProcessComment(final long processInstanceId, final String comment) throws CreationException; /** * Add a comment on a process instance, on behalf of an user. Mainly intended to be used in groovy scripts when the * behavior of the addProcessComment might be unsatisfactory. * * @param processInstanceId * The identifier of the process instance. * @param comment * The content of the comment. * @param userId * The user that will be said to have made the comment * @return The newly created comment. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws CreationException * If the parameter processInstanceId does not refer to any active process instance (existing and * non-archived). * @since 7.7 */ Comment addProcessCommentOnBehalfOfUser(final long processInstanceId, final String comment, long userId) throws CreationException; /** * Get all the comments managed by the specified user. * A comment is considered to be managed by user A if one or more of the following conditions is true: * - the author of the comment is a subordinate of user A (A the author's manager). * - the comment belongs to a process started by a subordinate of user A. * - the comment belongs to a process where at least one human task is assigned to a subordinate of user A. * * @param managerUserId * The identifier of the user. * @param searchOptions * The options used to search for comments. See * {@link org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor} for valid fields for searching and * sorting. * @return The comments managed by the user that match the search options. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws SearchException * If the search could not be completed correctly. * @since 6.0 */ SearchResult searchCommentsManagedBy(long managerUserId, SearchOptions searchOptions) throws SearchException; /** * Get the comments on process instances that the specified user can access. * * @param userId * The identifier of the user. * @param searchOptions * The options used to search for comments. See * {@link org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor} for valid fields for searching and * sorting. * @return The comments on process instances that the user can access. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws SearchException * If the search could not be completed correctly. * @since 6.0 */ SearchResult searchCommentsInvolvingUser(long userId, SearchOptions searchOptions) throws SearchException; /** * Get the children instances (sub process or call activity) of a process instance. The returned list is paginated. * It does not return the process instance of the given id (itself). * * @param processInstanceId * The identifier of the process instance. * @param startIndex * The index of the page to be returned (starting at 0). * @param maxResults * The maximum number of results per page. * @param criterion * The criterion used to sort the result. * @return The list of children instance identifiers. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @since 6.0 */ List getChildrenInstanceIdsOfProcessInstance(long processInstanceId, int startIndex, int maxResults, ProcessInstanceCriterion criterion); /** * Check whether a specific user is involved in a given process instance.
* User A is involved with a process instance if any of the following is true: *
    *
  • user A has started the process instance
  • *
  • a task in the process instance is assigned to user A
  • *
  • a task in the process instance is pending for user A
  • *
  • a task in the process instance has been performed by user A
  • *
* This method also applies to completed instances of process. * * @param userId * The identifier of the user. * @param processInstanceId * The identifier of the process instance. * @return True if the user is involved with the process instance. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws ProcessInstanceNotFoundException * If there is no processInstance with the specified identifier. * @throws UserNotFoundException * If there is no user with the specified identifier. * @since 6.0 * @see #isManagerOfUserInvolvedInProcessInstance(long, long) */ boolean isInvolvedInProcessInstance(long userId, long processInstanceId) throws ProcessInstanceNotFoundException, UserNotFoundException; /** * Check whether a specific user is involved in a given human task instance.
* User A is involved with a human task instance if any of the following is true: *
    *
  • the human task instance is assigned to user A
  • *
  • the human task instance is pending for user A
  • *
* * @param userId * The identifier of the user. * @param humanTaskInstanceId * The identifier of the human task instance. * @return True if the user is involved with the human task instance. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws ActivityInstanceNotFoundException * If there is no the human task instance with the specified identifier. * @since 6.5.1 * @see #isManagerOfUserInvolvedInProcessInstance(long, long) */ boolean isInvolvedInHumanTaskInstance(long userId, long humanTaskInstanceId) throws ActivityInstanceNotFoundException, UserNotFoundException; /** * Check whether a specific user has at least one subordinate (person he / she is the manager of) involved in a * given process instance.
* User A is involved with a process instance if any of the following is true: *
    *
  • user A has started the process instance
  • *
  • a task in the process instance is assigned to user A
  • *
  • a task in the process instance is pending for user A
  • *
  • a task in the process instance has been performed by user A
  • *
* This method also applies to completed instances of process. * * @param managerUserId the ID of the manager of the user involved. * @param processInstanceId the ID of the process instance we are interested in. * @return true if the specified manager has subordinates involved in the given process instance. * @throws ProcessInstanceNotFoundException if the process instance does not exist. * @throws BonitaException if an error occurred while searching for users involved. * @since 6.4.2 * @see #isInvolvedInProcessInstance(long, long) */ boolean isManagerOfUserInvolvedInProcessInstance(long managerUserId, long processInstanceId) throws BonitaException; /** * Get the process instance id from an activity instance id. * * @param activityInstanceId * The identifier of the activity instance. * @return The corresponding process instance id. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws ProcessInstanceNotFoundException * If there is no process instance with the specified identifier. * @since 6.0 */ long getProcessInstanceIdFromActivityInstanceId(long activityInstanceId) throws ProcessInstanceNotFoundException; /** * Get the process definition id from an process instance id. * * @param processInstanceId * The identifier of the process instance. * @return The corresponding process definition id. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws ProcessDefinitionNotFoundException * If there is no process definition with the specified identifier. * @since 6.0 */ long getProcessDefinitionIdFromProcessInstanceId(long processInstanceId) throws ProcessDefinitionNotFoundException; /** * Get the process definition id from an activity instance id. * * @param activityInstanceId * The identifier of the activity instance. * @return The corresponding process definition id. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws ProcessDefinitionNotFoundException * If no ProcessDefinition have an id corresponding to the parameter. * @since 6.0 */ long getProcessDefinitionIdFromActivityInstanceId(long activityInstanceId) throws ProcessDefinitionNotFoundException; /** * Search for archived comments. * -- ex: Retrieve comments of a given archived case -- * *
     * {@code
     *
     * public List retrieveComments(ProcessRuntimeAPI processRuntimeAPI,
     *         ArchivedProcessInstance archivedProcessInstance) {
     *     SearchOptions searchOptions = new SearchOptionsBuilder(0, Integer.MAX_VALUE)
     *             .filter(ArchivedCommentsSearchDescriptor.PROCESS_INSTANCE_ID,
     *                     archivedProcessInstance.getSourceObjectId())
     *             .done();
     *     return processRuntimeAPI.searchArchivedComments(searchOptions).getResult();
     * }
     * }
     * 
* * @param searchOptions * The options used to search for comments. See * {@link org.bonitasoft.engine.bpm.comment.ArchivedCommentsSearchDescriptor} for valid fields for * searching and sorting. * @throws InvalidSessionException * If the session is invalid, e.g. the session has expired. * @return The ArchivedComment items that match the search options. * @throws SearchException * If the search could not be completed correctly. * @since 6.0 */ SearchResult searchArchivedComments(SearchOptions searchOptions) throws SearchException; /** * Search for archived human tasks managed by the specified user. * * @param managerUserId * The identifier of the user manager, * @param searchOptions * The options used to search for tasks. See * {@link org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstanceSearchDescriptor} for valid fields * for searching and sorting. * @return The archived humanTask instances managed by the specified user that match the search options. * @throws SearchException * If the search could not be completed correctly. * @since 6.0 */ SearchResult searchArchivedHumanTasksManagedBy(long managerUserId, SearchOptions searchOptions) throws SearchException; /** * Search for open process instances that the specified user can access. * * @param userId * The identifier of the user. * @param searchOptions * The options used to search for process instance. See * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor} for valid fields * for searching and sorting. * @return The ProcessInstances that match the search options. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws SearchException * If the search could not be completed correctly. * @since 6.0 */ SearchResult searchOpenProcessInstancesInvolvingUser(long userId, SearchOptions searchOptions) throws SearchException; /** * Search for open process instances that all subordinates of the specified user can access. * * @param managerUserId * The identifier of the user manager. * @param searchOptions * The search options (pagination, filter, order sort). See * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor} for valid * fields for searching and sorting. * @return The ProcessInstances that match the search options. * @throws SearchException * If the search could not be completed correctly. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @since 6.0 */ SearchResult searchOpenProcessInstancesInvolvingUsersManagedBy(long managerUserId, SearchOptions searchOptions) throws SearchException; /** * Search for archived root process instances. Only archived process instances in states COMPLETED, ABORTED, * CANCELED and FAILED will be retrieved. Process instances started by call activities won't be retrieved. * See {@link #searchArchivedProcessInstancesInAllStates(SearchOptions) searchArchivedProcessInstancesInAllStates} * to search amoung any archived instance. * * @param searchOptions * The search options (pagination, filter, order sort). * @return The archived process instances that match the search options. See * {@link org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor} for valid fields for * searching and sorting. * @throws SearchException * If the search could not be full filled correctly * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @since 6.0 */ SearchResult searchArchivedProcessInstances(SearchOptions searchOptions) throws SearchException; /** * Search for archived process instances (root and intermediate levels) in all states (even intermediate states). * Depending on used filters several * ArchivedProcessInstance will be * retrieved for a single ProcessInstance (one for each reached state). * * @param searchOptions * The search options (pagination, filter, order sort). See * {@link org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor} for * valid fields for searching and sorting. * @return The archived process instances in all states that match the search options. * @throws SearchException * If the search could not be completed correctly. * @since 6.2 */ SearchResult searchArchivedProcessInstancesInAllStates(SearchOptions searchOptions) throws SearchException; /** * Search for archived process instances supervised by the specified user. * * @param userId * The identifier of the user. * @param searchOptions * The search options (pagination, filter, order sort). See * {@link org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor} for * valid fields for searching and sorting. * @return The archived process instances supervised by the user that match the search options. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws SearchException * If the search could not be completed correctly. * @since 6.0 */ SearchResult searchArchivedProcessInstancesSupervisedBy(long userId, SearchOptions searchOptions) throws SearchException; /** * Search for archived process instances that the specified user can access. * * @param userId * The identifier of the user. * @param searchOptions * The search options (pagination, filter, order sort). See * {@link org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor} for * valid fields for searching and sorting. * @return The archived process instances that the user can access that match the search options. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws SearchException * If the search could not be completed correctly. * @since 6.0 */ SearchResult searchArchivedProcessInstancesInvolvingUser(long userId, SearchOptions searchOptions) throws SearchException; /** * Search for human task instances. * * @param searchOptions * The search options (pagination, filter, order sort). See * {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid * fields for searching and sorting. * @return The human task instances that match the search options. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws SearchException * If the search could not be completed correctly. * @since 6.0 */ SearchResult searchHumanTaskInstances(SearchOptions searchOptions) throws SearchException; /** * Search for tasks assigned to users supervised by the specified user. * * @param supervisorId * The identifier of supervising user. * @param searchOptions * The search options (pagination, filter, order sort). See * {@link org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor} for valid * fields for searching and sorting. * @return The human task instances that match the search options. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws SearchException * If the search could not be completed correctly. * @since 6.0 */ SearchResult searchAssignedTasksSupervisedBy(long supervisorId, SearchOptions searchOptions) throws SearchException; /** * Search for archived tasks assigned to users supervised by the specified user. * * @param supervisorId * The identifier of the supervising user. * @param searchOptions * The search options (pagination, filter, order sort). See * {@link org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstanceSearchDescriptor} for * valid fields for searching and sorting. * @return The archived human task instances that match the search options. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid, e.g. the session has expired. * @throws SearchException * If the search could not be completed correctly. * @since 6.0 */ SearchResult searchArchivedHumanTasksSupervisedBy(long supervisorId, SearchOptions searchOptions) throws SearchException; /** * Evaluate expressions with values valid at process instantiation scope. * * @param processInstanceId * The identifier of the process instance. * @param expressions * The map of expressions to evaluate. * @return The result of the expression execution. Content of the resulting map depends on the incoming expression * map. The returned key is The name of the * expression (or its content if name is empty), the returned value is the evaluated expression result. * @throws org.bonitasoft.engine.session.InvalidSessionException * Generic exception thrown if API Session is invalid, e.g session has expired. * @throws ExpressionEvaluationException * Occurs when an exception is thrown during expression evaluation. * @since 6.0 */ Map evaluateExpressionsAtProcessInstanciation(long processInstanceId, Map> expressions) throws ExpressionEvaluationException; /** * Evaluate expressions with values valid on a completed process instance scope. * * @param processInstanceId * The identifier of the process instance. * @param expressions * The map of expressions to evaluate. * @return The result of the expression execution. Content of the resulting map depends on the incoming expression * map. The returned key is The name of the * expression (or its content if name is empty), the returned value is the evaluated expression result. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the API session is invalid, e.g session has expired. * @throws ExpressionEvaluationException * Occurs when an exception is thrown during expression evaluation. * @since 6.0 */ Map evaluateExpressionOnCompletedProcessInstance(long processInstanceId, Map> expressions) throws ExpressionEvaluationException; /** * Evaluate expressions with values valid on a process instance scope. * * @param processInstanceId * The identifier of the process instance. * @param expressions * The map of expressions to evaluate. * @return The result of the expression execution. Content of the resulting map depends on the incoming expression * map. The returned key is The name of the * expression (or its content if name is empty), the returned value is the evaluated expression result. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the API session is invalid, e.g session has expired. * @throws ExpressionEvaluationException * Occurs when an exception is thrown during expression evaluation. * @since 6.0 */ Map evaluateExpressionsOnProcessInstance(long processInstanceId, Map> expressions) throws ExpressionEvaluationException; /** * Evaluate expressions with values valid on a process definition scope. * * @param processDefinitionId * The identifier of the process definition. * @param expressions * The map of expressions to evaluate. * @return The result of the expression execution. Content of the resulting map depends on the incoming expression * map. The returned key is The name of the * expression (or its content if name is empty), the returned value is the evaluated expression result. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the API session is invalid, e.g session has expired. * @throws ExpressionEvaluationException * Occurs when an exception is thrown during expression evaluation. * @since 6.0 */ Map evaluateExpressionsOnProcessDefinition(long processDefinitionId, Map> expressions) throws ExpressionEvaluationException; /** * Evaluate expressions with values valid on an activity instance scope. * * @param activityInstanceId * The identifier of the activity instance. * @param expressions * The map of expressions to evaluate. * @return The result of the expression execution. Content of the resulting map depends on the incoming expression * map. The returned key is The name of the * expression (or its content if name is empty), the returned value is the evaluated expression result. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the API session is invalid, e.g session has expired. * @throws ExpressionEvaluationException * Occurs when an exception is thrown during expression evaluation. * @since 6.0 */ Map evaluateExpressionsOnActivityInstance(long activityInstanceId, Map> expressions) throws ExpressionEvaluationException; /** * Evaluate expressions with values valid on a completed activity instance scope. * * @param activityInstanceId * The identifier of the activity instance. * @param expressions * The map of expressions to evaluate. * @return The result of the expression execution. Content of the resulting map depends on the incoming expression * map. The returned key is The name of the * expression (or its content if name is empty), the returned value is the evaluated expression result. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the API session is invalid, e.g session has expired. * @throws ExpressionEvaluationException * Occurs when an exception is thrown during expression evaluation. * @since 6.0 */ Map evaluateExpressionsOnCompletedActivityInstance(long activityInstanceId, Map> expressions) throws ExpressionEvaluationException; /** * Returns the list of jobs that failed. * * @param startIndex * The result start index (starting from 0). * @param maxResults * The maximum number of results to retrieve. * @return The list of failed jobs. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.1 */ List getFailedJobs(int startIndex, int maxResults); /** * Replay a job that failed. * Job that failed can be found using {@link #getFailedJobs(int, int)} * This will remove the traces of Failed Jobs and restart the job once. * If you wish to re-execute multiple times the job that failed in case a recurrent job failed multiple times, * call this method several times. * * @param jobDescriptorId * The identifier of the job descriptor. * @since 6.1 */ void replayFailedJob(final long jobDescriptorId) throws ExecutionException; /** * Update parameters of a job and replay it. * The specified parameters replace the stored parameters. i.e. If the job is recurrent, all * future executions of this job will use the newly specified parameters. * * @param jobDescriptorId * The identifier of the job descriptor. * @param parameters * The job parameters. * @since 6.1 * @deprecated since 7.9, use {@link #replayFailedJob(long)} instead, parameters should not be modified. */ @Deprecated(since = "7.9", forRemoval = true) void replayFailedJob(final long jobDescriptorId, Map parameters) throws ExecutionException; /** * Gets the last archived data instance of the named data of the specified process instance. * * @param dataName * The name of the data * @param sourceProcessInstanceId * The identifier of the source process instance (used as root container id of the archived activity * instances). * @return An archived instance of data. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @throws ArchivedDataNotFoundException * If the specified data cannot be found. * @since 6.1 */ ArchivedDataInstance getArchivedProcessDataInstance(String dataName, long sourceProcessInstanceId) throws ArchivedDataNotFoundException; /** * Gets the last archived data instance of the named data of the specified activity instance. * * @param dataName * The name of the data * @param sourceActivityInstanceId * The identifier of the source activity instance * @return An archived instance of data. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @throws ArchivedDataNotFoundException * If the specified data cannot be found * @since 6.1 */ ArchivedDataInstance getArchivedActivityDataInstance(String dataName, long sourceActivityInstanceId) throws ArchivedDataNotFoundException; /** * Lists the last archived instances of data of the specified process instance. * * @param sourceProcessInstanceId * The identifier of the source process instance (used as root container id of the archived activity * instances). * @param startIndex * The start index * @param maxResults * The max number of archived data instances * @return The list of archived data instances. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs while retrieving the archived instances of data * @since 6.1 */ List getArchivedProcessDataInstances(long sourceProcessInstanceId, int startIndex, int maxResults); /** * Lists the last archived instances of data of the specified activity instance. * * @param sourceActivityInstanceId * The identifier of the source activity instance * @param startIndex * The start index * @param maxResults * The max number of archived data instances * @return The list of archived data instances. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs while retrieving the archived instances of data * @since 6.1 */ List getArchivedActivityDataInstances(long sourceActivityInstanceId, int startIndex, int maxResults); /** * Lists the possible users (candidates) of the specified human task instance. * Users are ordered by user name. * * @param humanTaskInstanceId * The identifier of the human task instance * @param startIndex * The start index * @param maxResults * The max number of users * @return The list of users. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs while retrieving the users * @since 6.1 */ List getPossibleUsersOfPendingHumanTask(long humanTaskInstanceId, int startIndex, int maxResults); /** * Lists the possible users (candidates) that can execute the specified human task instance. * Users are ordered by user name. * * @param humanTaskInstanceId * The identifier of the human task instance * @param searchOptions * the search options. See {@link org.bonitasoft.engine.identity.UserSearchDescriptor} for valid fields for * searching and sorting. * @return The list of users. * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs while retrieving the users * @since 6.3 */ SearchResult searchUsersWhoCanExecutePendingHumanTask(final long humanTaskInstanceId, SearchOptions searchOptions); /** * Search process definitions that have instances with assigned or pending human tasks for a specific user. * The tasks are in stable state, not in terminal/executing state. * * @param userId * The identifier of the user. * @param searchOptions * The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor} * for valid fields for searching and * sorting. * @return The list of process definitions * @throws SearchException * if an exception occurs when getting the process deployment information. * @since 6.3.3 */ SearchResult searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(long userId, SearchOptions searchOptions) throws SearchException; /** * Search process definitions supervised by a specific user, that have instances with assigned or pending human * tasks. * The tasks are in stable state, not in terminal/executing state. * * @param supervisorId * The identifier of the user. * @param searchOptions * The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor} * for valid fields for searching and * sorting. * @return The list of process definitions * @throws SearchException * if an exception occurs when getting the process deployment information. * @since 6.3.3 */ SearchResult searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy( long supervisorId, SearchOptions searchOptions) throws SearchException; /** * Search process definitions that have instances with assigned or pending human tasks. * The tasks are in stable state, not in terminal/executing state. * * @param searchOptions * The search criterion. See {@link org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor} * for valid fields for searching and * sorting. * @return The list of process definitions * @throws SearchException * If an exception occurs when getting the process deployment information. * @since 6.3.3 */ SearchResult searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks( SearchOptions searchOptions) throws SearchException; /** * Retrieve, for a given process definition, the current counters on active (not archived) flownodes. * * @param processDefinitionId ID of the process definition for which to retrieve the current indicators. * @return A map of counters: the key is the name of the flownode, as defined at design-time. the value is the * current counters for this flownode, that is, * a map of <state name, number of current flownode in that state> (possible state names are: ready, * executing, waiting, initializing, failed, completing) * If no results, returns an empty Map. * @throws ProcessDefinitionNotFoundException * If not process definition exists with the ID processDefinitionId. * @since 7.15.0 */ Map> getActiveFlownodeStateCountersForProcessDefinition(final long processDefinitionId) throws ProcessDefinitionNotFoundException; /** * Retrieve, for a given process instance, the current counters on flownodes. Please note: this method does not * count the flownodes of sub-process instances * of the given process instance. * * @param processInstanceId ID of the process instance for which to retrieve the current indicators. * @return A map of counters: the key is the name of the flownode, as defined at design-time. the value is the * current counters for this flownode, that is, * a map of <state name, number of current flownode in that state> (possible state names are: ready, * executing, waiting, initializing, failed, completing, completed, skipped, cancelled, aborted) * If no results, returns an empty Map. * @throws RetrieveException * If not process instance exists with the ID processInstanceId. * @since 6.5.0 */ Map> getFlownodeStateCounters(long processInstanceId); /** * Search the {@link TimerEventTriggerInstance} on the specific {@link ProcessInstance}. * * @param searchOptions * The search criterion. * @return The list of the timer event triggers * @throws SearchException * If an exception occurs when getting the timer event triggers. * @since 6.4.0 */ SearchResult searchTimerEventTriggerInstances(long processInstanceId, SearchOptions searchOptions) throws SearchException; /** * Change the date of the execution of a specific {@link TimerEventTriggerInstance}. * * @param timerEventTriggerInstanceId * The identifier of the {@link TimerEventTriggerInstance} to update * @param executionDate * The new date of the execution of the {@link TimerEventTriggerInstance} * @return The first fire time of the newly scheduled trigger is returned * @throws TimerEventTriggerInstanceNotFoundException * If the {@link TimerEventTriggerInstance} doesn't exist * @throws UpdateException * If an exception occurs when updating the {@link TimerEventTriggerInstance} * @since 6.4.0 */ Date updateExecutionDateOfTimerEventTriggerInstance(long timerEventTriggerInstanceId, Date executionDate) throws TimerEventTriggerInstanceNotFoundException, UpdateException; /** * Gets the contract of the user task. * * @param userTaskId the identifier of the user task. * @return the contract of the user task * @throws UserTaskNotFoundException * if identifier does not refer to a real user task. */ ContractDefinition getUserTaskContract(long userTaskId) throws UserTaskNotFoundException; /** * Gets the process instantiation contract for a given process definition. * * @param processDefinitionId the identifier of the process definition. * @return the contract of the given process * @throws ProcessDefinitionNotFoundException * if identifier does not refer to an existing process definition. */ ContractDefinition getProcessContract(long processDefinitionId) throws ProcessDefinitionNotFoundException; /** * Executes a user task that is in a stable state. * Will move the activity to the next stable state and then continue the execution of the process. * * @param userTaskInstanceId * The identifier of the user task to execute. * @param inputs * the inputs used for user task execution * @throws UserTaskNotFoundException * If user task to execute is not found * @throws ContractViolationException * If inputs don't fit with task contract * @throws FlowNodeExecutionException * If an execution exception occurs * @since 7.0 */ void executeUserTask(long userTaskInstanceId, Map inputs) throws UserTaskNotFoundException, ContractViolationException, FlowNodeExecutionException; /** * Executes a user task that is in a stable state on behalf of a given user * Will make the task go in the next stable state and then continue the execution of the process * If userId equals 0, the logged-in user is declared as the executer of the task. * The user, who executed the task on behalf of a given user, is declared as a executer delegate. * * @param userId * The identifier of the user for which you want to execute the flow node * @param userTaskInstanceId * The identifier of the user task to execute * @param inputs * the input used for user task execution * @throws UserTaskNotFoundException * If user task to execute is not found * @throws ContractViolationException * If inputs don't fit with task contract * @throws FlowNodeExecutionException * If an execution exception occurs * @since 7.0 */ void executeUserTask(long userId, long userTaskInstanceId, Map inputs) throws UserTaskNotFoundException, ContractViolationException, FlowNodeExecutionException; /** * Assign a task to a user with given user identifier and executes a user task that is in a stable state on behalf * of a given user * Will make the task go in the next stable state and then continue the execution of the process * If userId equals 0, the logged-in user is declared as the executer of the task. * The user, who executed the task on behalf of a given user, is declared as a executer delegate. * * @param userId * The identifier of the user for which you want to assign and execute the flow node * @param userTaskInstanceId * The identifier of the user task to execute * @param inputs * the input used for user task execution * @throws UpdateException * If an assignment exception occurs * @throws UserTaskNotFoundException * If user task to execute is not found * @throws ContractViolationException * If inputs don't fit with task contract * @throws FlowNodeExecutionException * If an execution exception occurs * @since 7.9 */ void assignAndExecuteUserTask(long userId, long userTaskInstanceId, Map inputs) throws UpdateException, UserTaskNotFoundException, ContractViolationException, FlowNodeExecutionException; /** * Gets the value of the variable of the user task contract. * * @param userTaskInstanceId * The identifier of the user task * @param name * The name of the variable * @return The identifier of the user task * @throws ContractDataNotFoundException if no data found for the given user task instance and name. */ Serializable getUserTaskContractVariableValue(long userTaskInstanceId, String name) throws ContractDataNotFoundException; /** * Gets the value of a process instantiation input, during the phase of initializing. For instance, if a connector * on_enter fails, this method can be called * to check the current value. * * @param processInstanceId The identifier of the process instance * @param name The name of the process input to retrieve * @return The identifier of the user task * @throws ContractDataNotFoundException if no data found for the given process instance and name. */ Serializable getProcessInputValueDuringInitialization(long processInstanceId, String name) throws ContractDataNotFoundException; /** * Gets the value of a process instantiation input, after initialization has finished. Requires Archiving feature to * be enabled (default behaviour). * * @param processInstanceId The identifier of the process instance * @param name The name of the process input to retrieve * @return The identifier of the user task * @throws ContractDataNotFoundException if identifier does not refer to an existing process instance. */ Serializable getProcessInputValueAfterInitialization(long processInstanceId, String name) throws ContractDataNotFoundException; /** * return the context defined in the process definition for this user task instance. Context includes: *
    *
  • Business data references (see {@link org.bonitasoft.engine.business.data.BusinessDataReference}, * {@link org.bonitasoft.engine.business.data.SimpleBusinessDataReference} and * {@link org.bonitasoft.engine.business.data.MultipleBusinessDataReference}). * Key of * data reference is the name of the business data as declared in process definition followed by "_ref". *
  • Documents reference (see {@link org.bonitasoft.engine.bpm.document.Document}). Naming convention is the same * as for business data ("_ref" suffix). *
  • For multi-instantiated task only: iterator name with "_ref" suffix (type of the value is * {@link org.bonitasoft.engine.business.data.BusinessDataReference}). By default: multiInstanceIterator_ref. Only * exist if iterator value is set using * a business variable. *
  • *
* * @param userTaskInstanceId the id of the user task instance * @return a map containing the evaluated context * @throws UserTaskNotFoundException if userTaskInstanceId does not reference any existing task. */ Map getUserTaskExecutionContext(long userTaskInstanceId) throws UserTaskNotFoundException, ExpressionEvaluationException; /** * return the context defined in the process definition for this user task instance. Context includes: *
    *
  • Business data references (see {@link org.bonitasoft.engine.business.data.BusinessDataReference}, * {@link org.bonitasoft.engine.business.data.SimpleBusinessDataReference} and * {@link org.bonitasoft.engine.business.data.MultipleBusinessDataReference}). * Key of * data reference is the name of the business data as declared in process definition followed by "_ref". *
  • Documents reference (see {@link org.bonitasoft.engine.bpm.document.Document}). Naming convention is the same * as for business data ("_ref" suffix). *
  • For multi-instantiated task only: iterator name with "_ref" suffix (type of the value is * {@link org.bonitasoft.engine.business.data.BusinessDataReference}). By default: multiInstanceIterator_ref. Only * exist if iterator value is set using * a business variable. *
  • *
* * @param archivedUserTaskInstanceId the id of the archived version of the user task instance * @return a map containing the evaluated context * @throws UserTaskNotFoundException if archivedUserTaskInstanceId does not reference any existing * archived task. */ Map getArchivedUserTaskExecutionContext(long archivedUserTaskInstanceId) throws UserTaskNotFoundException, ExpressionEvaluationException; /** * return the context defined in the process definition for this process instance. Context includes: *
    *
  • Business data references (see {@link org.bonitasoft.engine.business.data.BusinessDataReference}, * {@link org.bonitasoft.engine.business.data.SimpleBusinessDataReference} and * {@link org.bonitasoft.engine.business.data.MultipleBusinessDataReference}). * Key of * data reference is the name of the business data as declared in process definition followed by "_ref". *
  • Documents reference (see {@link org.bonitasoft.engine.bpm.document.Document}). Naming convention is the same * as for business data ("_ref" suffix). *
* * @param processInstanceId the id of the process instance * @return a map containing the evaluated context * @throws ProcessInstanceNotFoundException if processInstanceId does not reference any existing * process. */ Map getProcessInstanceExecutionContext(long processInstanceId) throws ProcessInstanceNotFoundException, ExpressionEvaluationException; /** * return the context defined in the process definition for this process instance. Context includes: *
    *
  • Business data references (see {@link org.bonitasoft.engine.business.data.BusinessDataReference}, * {@link org.bonitasoft.engine.business.data.SimpleBusinessDataReference} and * {@link org.bonitasoft.engine.business.data.MultipleBusinessDataReference}). * Key of * data reference is the name of the business data as declared in process definition followed by "_ref". *
  • Documents reference (see {@link org.bonitasoft.engine.bpm.document.Document}). Naming convention is the same * as for business data ("_ref" suffix). *
* * @param archivedProcessInstanceId the id of the archived version of a process instance. You can use * {@link #getFinalArchivedProcessInstance(long)} to get * the id of an archived instance based on the id of the same instance while it was running. * @return a map containing the evaluated context * @throws ProcessInstanceNotFoundException if archivedProcessInstanceId does not reference any * existing process. */ Map getArchivedProcessInstanceExecutionContext(long archivedProcessInstanceId) throws ProcessInstanceNotFoundException, ExpressionEvaluationException; /** * Update an instance of process with the given processInstanceId. * * @param processInstanceId * Identifier of the process instance * @param updater * including new value of all attributes adaptable * @return the updated process instance * @throws ProcessInstanceNotFoundException if no process instance can be found with id processInstanceId. * @throws UpdateException * if an error is thrown while updating the process instance. * @throws InvalidSessionException * if the session is invalid, e.g. the session has expired. * @since 6.0 */ ProcessInstance updateProcessInstance(long processInstanceId, org.bonitasoft.engine.bpm.process.impl.ProcessInstanceUpdater updater) throws ProcessInstanceNotFoundException, UpdateException; /** * Update a search key of a process instance, also known as string index. * * @param processInstanceId * identifier of the process instance * @param index the search to update * @param value * the new value for the search key * @return the updated process instance * @throws ProcessInstanceNotFoundException * Error thrown if no process instance have an id corresponding to the value of processInstanceId parameter. * @throws UpdateException * if an error is thrown while updating the process instance. * @throws InvalidSessionException * if the session is invalid, e.g. the session has expired. * @since 7.12.0 */ ProcessInstance updateProcessInstanceIndex(long processInstanceId, Index index, String value) throws ProcessInstanceNotFoundException, UpdateException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ProfileAPI.java ================================================ /** * 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.api; import java.util.List; import java.util.Map; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.profile.ProfileCriterion; import org.bonitasoft.engine.profile.ProfileMember; import org.bonitasoft.engine.profile.ProfileMemberCreator; import org.bonitasoft.engine.profile.ProfileNotFoundException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; /** * Profiles are a notion used in Bonita Portal to give and control access to some specific features of the Bonita suite. * Profiles are associated to Bonita Identity / Organization notions: users, groups, roles, memberships. * ProfileAPI gives access to some of the * profile administration: adding / removing members to / from a profile, retrieving / searching for profiles.
* Full control on profiles is part of Subscription editions of Bonita suite. * * @author Celine Souchet * @author Matthieu Chaffotte * @see SearchResult SearchResult for general knowledege on Search mechanism in Bonita. */ public interface ProfileAPI { /** * Retrieves the profile. * * @param id * The identifier of the profile * @return the searched profile * @throws ProfileNotFoundException * If the identifier does not refer to an existing profile * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the profile retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ Profile getProfile(long id) throws ProfileNotFoundException; /** * Retrieves the profiles of the user. * * @param userId * The identifier of the user * @param startIndex * The index of the first result (starting from 0). * @param maxResults * The maximum number of elements to get per page. * @param criterion * The criterion for sorting the items over pages. * @return The paginated and ordered profiles of the user * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the profile retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.3.2 */ List getProfilesForUser(long userId, int startIndex, int maxResults, ProfileCriterion criterion); /** * Searches for {@link Profile}s with specific search criteria. Use * {@link org.bonitasoft.engine.profile.ProfileSearchDescriptor} to * know the available filters. * * @param options * The search criteria * @return a {@link SearchResult} containing the list of {@code Profile}s matching the search criteria. * @throws SearchException * If an exception occurs during the profile searching * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 * @see Profile * @see org.bonitasoft.engine.profile.ProfileSearchDescriptor * @see SearchResult */ SearchResult searchProfiles(SearchOptions options) throws SearchException; /** * Retrieves the number of profile members for the profiles. The map contains the couples * profileId/numberOfProfileMembers. *

* If a profile does not exist, no exception is thrown and no value is added in the map. *

* * @param profileIds * The identifiers of the profiles * @return the number of profile members for the profiles * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the profile retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ Map getNumberOfProfileMembers(List profileIds); /** * Searches for {@link ProfileMember}s with specific search criteria. Use * {@link org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor} to * know the available filters. * * @param memberType * The member type, it can be: user, role, group, roleAndGroup. * @param options * The search criteria * @return a {@link SearchResult} containing the list of {@code ProfileMember}s matching the search criteria. * @throws SearchException * If an exception occurs during the profile searching * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 * @see ProfileMember * @see org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor * @see SearchResult */ SearchResult searchProfileMembers(String memberType, SearchOptions options) throws SearchException; /** * Creates a profile member. * * @param profileId * The identifier of the profile * @param userId * The identifier of the user * @param groupId * The identifier of the group * @param roleId * The identifier of the role * @return the created profile member * @throws AlreadyExistsException * If the tuple profileId/userId/roleId/groupId is already taken by an existing profile member * @throws CreationException * If an exception occurs during the profile member creation * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ ProfileMember createProfileMember(Long profileId, Long userId, Long groupId, Long roleId) throws CreationException, AlreadyExistsException; /** * Creates a profile member. *

* It takes the values of the creator in order to create the profile member. *

* * @param creator * The profile member to create * @return the created profile member * @throws AlreadyExistsException * If the tuple profileId/userId/roleId/groupId is already taken by an existing profile member * @throws CreationException * If an exception occurs during the profile member creation * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ ProfileMember createProfileMember(ProfileMemberCreator creator) throws CreationException, AlreadyExistsException; /** * Deletes the profile member. * * @param id * The identifier of the profile member * @throws DeletionException * If an exception occurs during the profile member deletion * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ void deleteProfileMember(Long id) throws DeletionException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/RoleAPI.java ================================================ /** * 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.api; import java.util.List; import java.util.Map; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.RoleCreator; import org.bonitasoft.engine.identity.RoleCriterion; import org.bonitasoft.engine.identity.RoleNotFoundException; import org.bonitasoft.engine.identity.RoleUpdater; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; /** * RoleAPI forms part of the {@link OrganizationAPI} and gives access to all the Administration operations available on * Roles: creation, deletion, * search, etc... * * @author Feng Hui * @author Matthieu Chaffotte * @author Hongwen Zang * @author Emmanuel Duchastenier * @see SearchResult SearchResult for general knowledege on Search mechanism in Bonita. * @see Role */ public interface RoleAPI { /** * Creates a role. * * @param roleName * the name of the role * @return the created role * @throws AlreadyExistsException * If the name is already taken by an existing role * @throws CreationException * If an exception occurs during the role creation * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ Role createRole(String roleName) throws AlreadyExistsException, CreationException; /** * Create the role. *

* It takes the values of the creator in order to create the role. *

* * @param creator * the role creator * @return the created role. * @throws AlreadyExistsException * If the name is already taken by an existing role * @throws CreationException * If an exception occurs during the role creation * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ Role createRole(RoleCreator creator) throws AlreadyExistsException, CreationException; /** * Updates the group according to the updater values. *

* This method also allow to update the icon of the role. * When you update it, the iconId will be set on the role and you can later get it using * {@link IdentityAPI#getIcon(long)}. * Changing the content of the icon will create a new icon and change the iconId of the role. *

* * @param roleId * the identifier of the role * @param updater * the role updater * @return the updated role * @throws RoleNotFoundException * If the role identifier does not refer to an existing role * @throws UpdateException * If an exception occurs during the role update * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ Role updateRole(long roleId, RoleUpdater updater) throws RoleNotFoundException, UpdateException; /** * Deletes the role. * * @param roleId * the role identifier * @throws DeletionException * If an exception occurs during the role deletion * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ void deleteRole(long roleId) throws DeletionException; /** * Deletes the roles. * * @param roleIds * the list of role identifiers * @throws DeletionException * If an exception occurs during the role deletion * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ void deleteRoles(List roleIds) throws DeletionException; /** * Retrieves the role. * * @param roleId * the identifier of the role * @return the role * @throws RoleNotFoundException * If the role identifier does not refer to an existing role * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the role retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ Role getRole(long roleId) throws RoleNotFoundException; /** * Retrieves the role. * * @param roleName * the name of the role. * @return the role. * @throws RoleNotFoundException * If the role name does not refer to an existing role * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the role retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ Role getRoleByName(String roleName) throws RoleNotFoundException; /** * Returns the total number of roles. * * @return the total number of roles * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the count retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ long getNumberOfRoles(); /** * Retrieves the paginated list of roles. *

* It retrieves from the startIndex to the startIndex + maxResults. *

* * @param startIndex * the start index * @param maxResults * the max number of roles * @param criterion * the sorting criterion * @return the paginated list of roles * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the role retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ List getRoles(int startIndex, int maxResults, RoleCriterion criterion); /** * Retrieves the roles. The map contains the couples roleId/Role. *

* If a role does not exists, no exception is thrown and no value is added in the map. *

* * @param roleIds * the identifiers of the roles * @return the roles * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the role retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ Map getRoles(List roleIds); /** * Searches roles according to the criteria containing in the options. * * @param options * the search criteria * @return the search result * @throws SearchException * If an exception occurs during the role searching * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ SearchResult searchRoles(SearchOptions options) throws SearchException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/TenantAdministrationAPI.java ================================================ /** * 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.api; import org.bonitasoft.engine.business.data.BusinessDataRepositoryDeploymentException; import org.bonitasoft.engine.business.data.BusinessDataRepositoryException; import org.bonitasoft.engine.business.data.InvalidBusinessDataModelException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.tenant.TenantResource; /** * This API gives access to tenant administration tasks. * * @author Matthieu Chaffotte * @author Baptiste Mesta */ public interface TenantAdministrationAPI { /** A String key to designate the functional scope of updating the BDM */ public static final String UPDATE_BDM = "UpdateBDM"; /** * @return * true if the tenant is paused. * @deprecated since 9.0.0, use {@link MaintenanceAPI#getMaintenanceDetails()} instead. */ @Deprecated(since = "9.0.0", forRemoval = true) boolean isPaused(); /** * Pause the tenant so nothing is executed anymore. * when the tenant is paused: * Only technical user can login when the tenant is paused. * All users connected are disconnected (apart from the technical user). * Only IdentityAPI and ProfileAPI are accessible. * * @deprecated since 9.0.0, use {@link MaintenanceAPI#enableMaintenanceMode()} instead. * @throws org.bonitasoft.engine.exception.UpdateException * if the tenant cannot be paused. */ @Deprecated(since = "9.0.0", forRemoval = true) void pause() throws UpdateException; /** * Resume the tenant to a normal state after a pause. * * @deprecated since 9.0.0, use {@link MaintenanceAPI#disableMaintenanceMode()} instead. * @throws org.bonitasoft.engine.exception.UpdateException * if the tenant cannot be resumed. */ @Deprecated(since = "9.0.0", forRemoval = true) void resume() throws UpdateException; /** * Uninstalls the business data model. * * @throws BusinessDataRepositoryDeploymentException * if the deployment cannot be fulfilled completely. */ void uninstallBusinessDataModel() throws BusinessDataRepositoryDeploymentException; /** * Update the business data model. * * @param zip * the binary content of the business object model. * @return the version of the Business Data Model just deployed. * @throws InvalidBusinessDataModelException * if the Business Data Model content passed as parameter is invalid. * @throws BusinessDataRepositoryDeploymentException * if the deployment cannot be fulfilled completely. * @deprecated as of 9.0.0. The BDM should only be updated at startup. */ @Deprecated(since = "9.0.0") String updateBusinessDataModel(final byte[] zip) throws InvalidBusinessDataModelException, BusinessDataRepositoryDeploymentException; /** * Deletes all business data and uninstalls the business data model. * * @throws BusinessDataRepositoryDeploymentException * if the deployment cannot be fulfilled completely. */ void cleanAndUninstallBusinessDataModel() throws BusinessDataRepositoryDeploymentException; /** * Returns the generated BDM zip. * This zip contains the jars with BDM Pojos and DAOs. * usage: * byte[] clientBDMZip = getTenantAdministrationAPI().getClientBDMZip(); * clientBDMZip will typically contain : "README.md", "example-pom.xml", "bdm-dao.jar", "bdm-model.jar", "bom.zip". * See online a Java * code example * on how to extract those artefacts from the Zip file. * * @return zip content of the deployed client Business data model, null if no Business data model has been deployed * @throws BusinessDataRepositoryException * if the Business Data Model cannot be retrieved. */ byte[] getClientBDMZip() throws BusinessDataRepositoryException; /** * Returns the current Business Data Model version, if any, or null if no Business Data Model is currently deployed. * * @return the current Business Data Model version, if any, or null if no Business Data Model is currently deployed * @throws BusinessDataRepositoryException * if the BDM version cannot be retrieved properly. */ String getBusinessDataModelVersion() throws BusinessDataRepositoryException; /** * Retrieves the BDM resource, as a tenant-level resource. Or NONE if no BDM is installed. * * @return a TenantResource representing the current BDM on the current tenant, * or TenantResource.NONE if no BDM is installed. * @since 7.7 */ TenantResource getBusinessDataModelResource(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/UserAPI.java ================================================ /** * 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.api; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.business.application.Application; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.ContactData; import org.bonitasoft.engine.identity.Icon; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserCreator; import org.bonitasoft.engine.identity.UserCriterion; import org.bonitasoft.engine.identity.UserNotFoundException; import org.bonitasoft.engine.identity.UserUpdater; import org.bonitasoft.engine.identity.UserWithContactData; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; /** * UserAPI forms part of the {@link OrganizationAPI} and gives access to all the Administration operations available on * Users: creation, deletion, certain * specific getXXX() methods, generic search methods, etc... * It also to retrieve user ContactData. * * @author Feng Hui * @author Matthieu Chaffotte * @author Emmanuel Duchastenier * @author Celine Souchet * @see ContactData * @see SearchResult SearchResult for general knowledege on Search mechanism in Bonita. */ public interface UserAPI { /** * Creates a user. * The password can't be empty. * * @param userName * The name of the user * @param password * The password of the user * @return The created user * @throws AlreadyExistsException * If the name is already taken by an existing user * @throws CreationException * If an exception occurs during the user creation * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ User createUser(String userName, String password) throws AlreadyExistsException, CreationException; /** * Creates a user. * The password can't be empty. * * @param userName * The name of the user * @param password * The password of the user * @param firstName * The first name of the user * @param lastName * The last name of the user * @return The created user * @throws AlreadyExistsException * If the name is already taken by an existing user * @throws CreationException * If an exception occurs during the user creation * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ User createUser(String userName, String password, String firstName, String lastName) throws AlreadyExistsException, CreationException; /** * Creates a user. * It takes the values of the creator in order to create the user. * * @param creator * The user to create * @return The created user * @throws AlreadyExistsException * If the name is already taken by an existing user * @throws CreationException * If an exception occurs during the user creation * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ User createUser(UserCreator creator) throws AlreadyExistsException, CreationException; /** * Updates the user according to the updater values. *

* This method also allow to update the icon of the user. * When you update it, the iconId will be set on the user and you can later get it using * {@link IdentityAPI#getIcon(long)}. * Changing the content of the icon will create a new icon and change the iconId of the user. *

* * @param userId * The identifier of the user * @param updater * The user updater * @return The updated user * @throws UserNotFoundException * If the user identifier does not refer to an existing user * @throws UpdateException * If an exception occurs during the user update * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ User updateUser(long userId, UserUpdater updater) throws UserNotFoundException, UpdateException; /** * Deletes the user. *

*

Use this method with caution: some artifacts like {@link Application}s and {@link DesignProcessDefinition}s * may present display problems in the Bonita * BPM Portal if the referenced user was deleted. Note that you can disable a user instead of deleting it. To do so, * use the method * {@link #updateUser(long, UserUpdater)} to set the attribute 'enabled' to false

. * * @param userId * The identifier of the user * @throws DeletionException * If an exception occurs during the user deletion * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @see #updateUser(long, UserUpdater) * @see Application * @see DesignProcessDefinition * @since 6.0 */ void deleteUser(long userId) throws DeletionException; /** * Deletes the user. *

*

Use this method with caution: some artifacts like {@link Application}s and {@link DesignProcessDefinition}s * may present display problems in the Bonita * BPM Portal if the referenced user was deleted. Note that you can disable a user instead of deleting it. To do so, * use the method * {@link #updateUser(long, UserUpdater)} to set the attribute 'enabled' to false

. * * @param userName * The name of the user * @throws DeletionException * If an exception occurs during the user deletion * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @see #updateUser(long, UserUpdater) * @see Application * @see DesignProcessDefinition * @since 6.0 */ void deleteUser(String userName) throws DeletionException; /** * Deletes the users. *

*

Use this method with caution: some artifacts like {@link Application}s and {@link DesignProcessDefinition}s * may present display problems in the Bonita * BPM Portal if the referenced user was deleted. Note that you can disable a user instead of deleting it. To do so, * use the method * {@link #updateUser(long, UserUpdater)} to set the attribute 'enabled' to false

. * * @param userIds * The identifiers of the users * @throws DeletionException * If an exception occurs during the user deletion * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @see #updateUser(long, UserUpdater) * @see Application * @see DesignProcessDefinition * @since 6.0 */ void deleteUsers(List userIds) throws DeletionException; /** * Retrieves the user. * It throws a {@link UserNotFoundException} if the user identifier equals the technical user identifier (-1). * * @param userId * The identifier of the user * @return The searched user * @throws UserNotFoundException * If the user identifier does not refer to an existing user * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the user retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ User getUser(long userId) throws UserNotFoundException; /** * Retrieves the user. * * @param userName * The name of the user * @return The role * @throws UserNotFoundException * If the user name does not refer to an existing user * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the role retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ User getUserByUserName(String userName) throws UserNotFoundException; /** * Retrieves the professional details of the user. * * @param userId * The identifier of the user * @return The user and the professional details * @throws UserNotFoundException * If the user identifier does not refer to an existing user, or is -1 (the technical user identifier) * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs while retrieving the user * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.1 */ UserWithContactData getUserWithProfessionalDetails(long userId) throws UserNotFoundException; /** * Retrieves the contact data (personal or professional) of the user. * * @param userId * The identifier of the user * @param personal * true if the contact data is the personal one * @return The contact data * @throws UserNotFoundException * If the user name does not refer to an existing user * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the role retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ ContactData getUserContactData(long userId, boolean personal) throws UserNotFoundException; /** * Returns the total number of users. * * @return The total number of users * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the count retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ long getNumberOfUsers(); /** * Retrieves the paginated list of users. It retrieves from the startIndex to the startIndex + maxResults. * * @param startIndex * The start index * @param maxResults * The max number of users * @param criterion * The sorting criterion * @return The paginated list of users * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the user retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ List getUsers(int startIndex, int maxResults, UserCriterion criterion); /** * Retrieves the users. The map contains the couples userId/User. * If a user does not exists, no exception is thrown and no value is added in the map. * * @param userIds * The identifiers of the users * @return The users * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the user retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ Map getUsers(List userIds); /** * Retrieves the identifiers of the named users. The map contains the couples userName/User. * If a user does not exists, no exception is thrown and no value is added in the map. * * @param userNames * The names of the users * @return The users ordered by the user name ascending * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the user retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.1 */ Map getUsersByUsernames(List userNames); /** * Searches users according to the criteria containing in the options. * * @param options * The search criteria * @return The search result * @throws SearchException * If an exception occurs during the user searching * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ SearchResult searchUsers(SearchOptions options) throws SearchException; /** * Returns the total number of users of the role. * * @param roleId * The identifier of the role * @return The total number of users of the role * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the count retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ long getNumberOfUsersInRole(long roleId); /** * Retrieves the paginated list of users, regardless if they are active or not, in a given role. * It retrieves from the startIndex to the startIndex + maxResults. * * @param roleId * The identifier of the role * @param startIndex * The start index * @param maxResults * The max number of users * @param criterion * The sorting criterion * @return The paginated list of users * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the retrieving of users * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ List getUsersInRole(long roleId, int startIndex, int maxResults, UserCriterion criterion); /** * Retrieves the paginated list of active users in role. * It retrieves from the startIndex to the startIndex + maxResults. * * @param roleId * The identifier of the role * @param startIndex * The start index * @param maxResults * The max number of users * @param criterion * The sorting criterion * @return The paginated list of users * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the retrieving of users * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 7.6 */ List getActiveUsersInRole(long roleId, int startIndex, int maxResults, UserCriterion criterion); /** * Retrieves the paginated list of inactive users in role. * It retrieves from the startIndex to the startIndex + maxResults. * * @param roleId * The identifier of the role * @param startIndex * The start index * @param maxResults * The max number of users * @param criterion * The sorting criterion * @return The paginated list of users * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the retrieving of users * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 7.6 */ List getInactiveUsersInRole(long roleId, int startIndex, int maxResults, UserCriterion criterion); /** * Returns the total number of users of the group. * * @param groupId * The identifier of the group * @return The total number of users of the group * @throws BonitaException * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the count retrieving * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ long getNumberOfUsersInGroup(long groupId) throws BonitaException; /** * Retrieves the paginated list of users, both active and inactive, in a given group. * It retrieves from the startIndex to the startIndex + maxResults. * * @param groupId * The identifier of the group * @param startIndex * The start index * @param maxResults * The max number of users * @param criterion * The sorting criterion * @return The paginated list of users * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the retrieving of users * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 6.0 */ List getUsersInGroup(long groupId, int startIndex, int maxResults, UserCriterion criterion); /** * Retrieves the paginated list of active users in groups. * It retrieves from the startIndex to the startIndex + maxResults. * * @param groupId * The identifier of the group * @param startIndex * The start index * @param maxResults * The max number of users * @param criterion * The sorting criterion * @return The paginated list of users * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the retrieving of users * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 7.6 */ List getActiveUsersInGroup(long groupId, int startIndex, int maxResults, UserCriterion criterion); /** * Retrieves the paginated list of inactive users in groups. * It retrieves from the startIndex to the startIndex + maxResults. * * @param groupId * The identifier of the group * @param startIndex * The start index * @param maxResults * The max number of users * @param criterion * The sorting criterion * @return The paginated list of users * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the retrieving of users * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 7.6 */ List getInactiveUsersInGroup(long groupId, int startIndex, int maxResults, UserCriterion criterion); /** * Retrieves the paginated list of users of a manager. * It retrieves from the startIndex to the startIndex + maxResults. * * @param managerId * The identifier of the manager * @param startIndex * The start index * @param maxResults * The max number of users * @param criterion * The sorting criterion * @return The paginated list of users * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the retrieving of users * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 7.6 */ List getUsersWithManager(long managerId, int startIndex, int maxResults, UserCriterion criterion); /** * Retrieves the paginated list of active users of a manager. * It retrieves from the startIndex to the startIndex + maxResults. * * @param managerId * The identifier of the manager * @param startIndex * The start index * @param maxResults * The max number of users * @param criterion * The sorting criterion * @return The paginated list of users * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the retrieving of users * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 7.6 */ List getActiveUsersWithManager(long managerId, int startIndex, int maxResults, UserCriterion criterion); /** * Retrieves the paginated list of inactive users of a manager. * It retrieves from the startIndex to the startIndex + maxResults. * * @param managerId * The identifier of the manager * @param startIndex * The start index * @param maxResults * The max number of users * @param criterion * The sorting criterion * @return The paginated list of users * @throws org.bonitasoft.engine.exception.RetrieveException * If an exception occurs during the retrieving of users * @throws org.bonitasoft.engine.session.InvalidSessionException * If the session is invalid (expired, unknown, ...) * @since 7.6 */ List getInactiveUsersWithManager(long managerId, int startIndex, int maxResults, UserCriterion criterion); /** * Retrieves the list of user identifiers containing the chosen custom user information with the given value. * * @param infoName * The custom user information name * @param infoValue * The custom user information value * @param usePartialMatch * Defines whether the custom user information value should use a partial match. * @param startIndex * The start index (the first valid value is zero) * @param maxResults * The max number of user identifiers to be retrieved * @return the list of user identifiers containing the chosen custom user information with the given value. * @since 6.3.2 */ List getUserIdsWithCustomUserInfo(String infoName, String infoValue, boolean usePartialMatch, int startIndex, int maxResults); /** * get the icon having specified id * * @param id * the id of the icon * @return the icon with its content * @throws NotFoundException * @since 7.3.0 */ Icon getIcon(long id) throws NotFoundException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/impl/ClientInterceptor.java ================================================ /** * 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.api.impl; import static java.util.logging.Level.FINEST; import java.io.Serializable; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; import org.bonitasoft.engine.api.internal.ServerAPI; import org.bonitasoft.engine.api.internal.ServerWrappedException; import org.bonitasoft.engine.session.Session; /** * API classes given to the client are proxies. That class is the {@link InvocationHandler} given when proxying the * client api classes * Its purpose is to keep the client session and give it to the {@link ServerAPI} call */ public class ClientInterceptor implements InvocationHandler, Serializable { private static final long serialVersionUID = -6284726148297940515L; private final ServerAPI api; private final String interfaceName; private final Session session; private static final Logger LOGGER = Logger.getLogger(ClientInterceptor.class.getName()); public ClientInterceptor(final String interfaceName, final ServerAPI api, final Session session) { this.api = api; this.interfaceName = interfaceName; this.session = session; } public ClientInterceptor(final String interfaceName, final ServerAPI api) { this(interfaceName, api, null); } @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { try { final Class[] parameterTypes = method.getParameterTypes(); final List classNameParameters = new ArrayList<>(); for (final Class parameterType : parameterTypes) { classNameParameters.add(parameterType.getName()); } if (LOGGER.isLoggable(FINEST)) { LOGGER.log(FINEST, "Calling method " + method.getName() + " on API " + this.api.getClass().getName()); } Map options; options = new HashMap<>(); options.put("session", this.session); // invoke ServerAPI unique method final Object object = this.api.invokeMethod(options, this.interfaceName, method.getName(), classNameParameters, args); if (LOGGER.isLoggable(FINEST)) { LOGGER.log(FINEST, "Quitting method " + method.getName() + " on API " + this.api.getClass().getName()); } return object; } catch (final ServerWrappedException | RemoteException e) { if (LOGGER.isLoggable(FINEST)) { LOGGER.log(FINEST, "Quitting method " + method.getName() + " on API " + this.api.getClass().getName() + " with exception " + e.getMessage()); } throw e.getCause(); } } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/internal/ServerAPI.java ================================================ /** * 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.api.internal; import java.io.Serializable; import java.rmi.Remote; import java.rmi.RemoteException; import java.util.List; import java.util.Map; /** * This interface defines a common entry point for APIs to be called by engine client. * Depending on what is the client connection mode, the client retrieve an implementation of this interface to call the * server side API. */ public interface ServerAPI extends Serializable, Remote { Object invokeMethod(final Map options, final String apiInterfaceName, final String methodName, final List classNameParameters, final Object[] parametersValues) throws ServerWrappedException, RemoteException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/internal/ServerWrappedException.java ================================================ /** * 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.api.internal; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class ServerWrappedException extends Exception { private static final long serialVersionUID = 2098815926771801085L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ServerWrappedException(final Throwable cause) { super(cause); } /** * Constructs a new exception with the specified detail message and cause. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ServerWrappedException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/internal/package-info.java ================================================ /** * Copyright (C) 2015 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. **/ /** *

* contains {@link org.bonitasoft.engine.api.internal.ServerAPI} interface *

*/ package org.bonitasoft.engine.api.internal; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/package-info.java ================================================ /** * Copyright (C) 2015 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. **/ /** *

* The Bonita project has a clear separation between the API the users should be interacting with and the actual * implementation classes. *
* The public API exposes most of the features we believe "normal" users can safely use and should remain rather stable * across releases. *
* Expert users can still access internal classes but should be aware that they should know what they are doing and that * the internal API might still change in the future. *

*/ package org.bonitasoft.engine.api; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/permission/APICallContext.java ================================================ /** * 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.api.permission; import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Context of a call made on a REST API * * @author Baptiste Mesta */ public class APICallContext implements Serializable { private static final long serialVersionUID = 1L; public static final String FILTER_KEY = "f"; public static final String SEARCH_TERM_KEY = "s"; public static final String DELIMITER = "&"; /* * The http method */ private String method; /* * the api name */ private String apiName; /* * the resource name */ private String resourceName; /* * id of the resource, can be multiple */ private String resourceId; /* * query string of the api call */ private String queryString; /* * body of the api call */ private String body; private Map filters = new HashMap<>(); private final Map parameters = new HashMap<>(); private String searchTerm; /** * @param method * the HTTP method * @param apiName * the name of the api * @param resourceName * the name of the resource * @param resourceId * the id (or multiple id) of the resource if specified */ public APICallContext(String method, String apiName, String resourceName, String resourceId) { this(method, apiName, resourceName, resourceId, null, null); } /** * @param method * the HTTP method * @param apiName * the name of the api * @param resourceName * the name of the resource * @param resourceId * the id (or multiple id) of the resource if specified * @param queryString * the query string of the api context if specified * @param body * the body string of the api context if specified */ public APICallContext(String method, String apiName, String resourceName, String resourceId, String queryString, String body) { this.method = method; this.apiName = apiName; this.resourceName = resourceName; this.resourceId = resourceId; this.queryString = queryString; parseQueryString(queryString); this.body = body; } private void parseQueryString(String queryString) { this.filters = new HashMap<>(); if (queryString == null) { return; } for (String element : queryString.split(DELIMITER)) { int indexOfEquals = element.indexOf("="); if (indexOfEquals > 0 && indexOfEquals + 1 < element.length()) { String key = element.substring(0, indexOfEquals); String value = element.substring(indexOfEquals + 1); if (FILTER_KEY.equals(key)) { addFilter(value); } else if (SEARCH_TERM_KEY.equals(key)) { searchTerm = value; } if (parameters.containsKey(key)) { String[] currentValue = parameters.get(key); String[] newValue = Arrays.copyOf(currentValue, currentValue.length + 1); newValue[currentValue.length] = value; parameters.put(key, newValue); } else { parameters.put(key, new String[] { value }); } } } } private void addFilter(String value) { int indexOfEncodedEquals = Math.max(value.indexOf("%3d"), value.indexOf("%3D")); if (indexOfEncodedEquals > 0) { addFilterIfValueNotBlank(value, indexOfEncodedEquals, 3); } else if (value.indexOf("=") > 0) { addFilterIfValueNotBlank(value, value.indexOf("="), 1); } } private void addFilterIfValueNotBlank(String value, int indexOfEquals, int separatorSize) { if (indexOfEquals + separatorSize < value.length()) { filters.put(value.substring(0, indexOfEquals), value.substring(indexOfEquals + separatorSize)); } } /** * empty constructor */ public APICallContext() { } public String getMethod() { return method; } /** * @return true if method is GET */ public boolean isGET() { return "GET".equals(method); } /** * @return true if method is PUT */ public boolean isPUT() { return "PUT".equals(method); } /** * @return true if method is POST */ public boolean isPOST() { return "POST".equals(method); } /** * @return true if method is DELETE */ public boolean isDELETE() { return "DELETE".equals(method); } public void setMethod(String method) { this.method = method; } /** * @return the name of API the resource is part of */ public String getApiName() { return apiName; } public void setApiName(String apiName) { this.apiName = apiName; } /** * @return the name of the main resource (if a subresource is queried, its name will be part of the compound * resource ID) */ public String getResourceName() { return resourceName; } public void setResourceName(String resourceName) { this.resourceName = resourceName; } /** * @return the list of identifiers composing the resource ID. This is useful for compound resource IDs (the * identifiers are separated with a / in the URL) */ public List getCompoundResourceId() { return resourceId == null ? Collections.emptyList() : Arrays.asList(resourceId.split("/")); } /** * @return the full resource ID as a string. If the resource ID is compound, it will return as it is in the URL * (with a / as separator between the identifiers) */ public String getResourceId() { return resourceId; } public void setResourceId(String resourceId) { this.resourceId = resourceId; } /** * @return the full query string */ public String getQueryString() { return queryString; } public void setQueryString(String queryString) { this.queryString = queryString; parseQueryString(queryString); } /** * @return the body of the request as a String */ public String getBody() { return body; } public void setBody(String body) { this.body = body; } /** * @return a Map containing the filters of the request as they are set in the query string (f parameter) */ public Map getFilters() { return filters; } /** * @return a string containing the search term of the request as it is set in the query string (s parameter) */ public String getSearchTerm() { return searchTerm; } /** * @return a Map containing the parameters of the request as they are set in the query string (including the filters * and search terms) */ public Map getParameters() { return parameters; } @Override public String toString() { return "APICallContext{" + "method='" + method + '\'' + ", apiName='" + apiName + '\'' + ", resourceName='" + resourceName + '\'' + ", resourceId='" + resourceId + '\'' + ", queryString='" + queryString + '\'' + ", body='" + body + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; APICallContext that = (APICallContext) o; if (apiName != null ? !apiName.equals(that.apiName) : that.apiName != null) return false; if (body != null ? !body.equals(that.body) : that.body != null) return false; if (filters != null ? !filters.equals(that.filters) : that.filters != null) return false; if (parameters != null ? !parameters.equals(that.parameters) : that.parameters != null) return false; if (method != null ? !method.equals(that.method) : that.method != null) return false; if (queryString != null ? !queryString.equals(that.queryString) : that.queryString != null) return false; if (resourceId != null ? !resourceId.equals(that.resourceId) : that.resourceId != null) return false; if (resourceName != null ? !resourceName.equals(that.resourceName) : that.resourceName != null) return false; if (searchTerm != null ? !searchTerm.equals(that.searchTerm) : that.searchTerm != null) return false; return true; } @Override public int hashCode() { int result = method != null ? method.hashCode() : 0; result = 31 * result + (apiName != null ? apiName.hashCode() : 0); result = 31 * result + (resourceName != null ? resourceName.hashCode() : 0); result = 31 * result + (resourceId != null ? resourceId.hashCode() : 0); result = 31 * result + (queryString != null ? queryString.hashCode() : 0); result = 31 * result + (body != null ? body.hashCode() : 0); result = 31 * result + (filters != null ? filters.hashCode() : 0); result = 31 * result + (parameters != null ? parameters.hashCode() : 0); result = 31 * result + (searchTerm != null ? searchTerm.hashCode() : 0); return result; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/permission/PermissionRule.java ================================================ /** * 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.api.permission; import org.bonitasoft.engine.api.APIAccessor; import org.bonitasoft.engine.api.Logger; import org.bonitasoft.engine.session.APISession; /** * Class to extend when implementing permission rule to be checked by {@link org.bonitasoft.engine.api.PermissionAPI} * * @author Baptiste Mesta */ public interface PermissionRule { /** * Called by the engine when using * {@link org.bonitasoft.engine.api.PermissionAPI#isAuthorized(APICallContext)} * * @param apiSession * the api session from the user doing the api call * @param apiCallContext * the context of the api call * @param apiAccessor * an accessor to call apis * @return * true if the user is allowed to access the api or false otherwise */ boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/platform/PlatformInformationAPI.java ================================================ /** * 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.api.platform; import java.util.Map; import org.bonitasoft.engine.platform.PlatformNotFoundException; public interface PlatformInformationAPI { Map getPlatformInformation() throws PlatformNotFoundException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/result/ExecutionResult.java ================================================ /** * 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.api.result; import static java.util.Arrays.asList; import static java.util.Objects.requireNonNull; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import org.bonitasoft.engine.api.result.Status.Level; /** * Contains a list of {@link Status} as a result of an API execution */ public class ExecutionResult implements Serializable { public static final ExecutionResult OK = new ExecutionResult(); private List statuses = new ArrayList<>(); public ExecutionResult(Status... statuses) { this(asList(requireNonNull(statuses))); } public ExecutionResult(List statusList) { this.statuses.addAll(requireNonNull(statusList)); } public void addStatus(Status... statuses) { this.statuses.addAll(asList(requireNonNull(statuses))); } public boolean isOk() { return statuses.isEmpty() || statuses.stream() .allMatch(status -> status.getLevel() == Level.OK); } public boolean hasErrors() { return statuses.stream() .anyMatch(status -> status.getLevel() == Level.ERROR); } public boolean hasWarnings() { return statuses.stream() .anyMatch(status -> status.getLevel() == Level.WARNING); } public boolean hasInfo() { return statuses.stream() .anyMatch(status -> status.getLevel() == Level.INFO); } public List getErrors() { return statuses.stream() .filter(status -> status.getLevel() == Level.ERROR) .collect(Collectors.toList()); } public List getWarnings() { return statuses.stream() .filter(status -> status.getLevel() == Level.WARNING) .collect(Collectors.toList()); } public List getInfo() { return statuses.stream() .filter(status -> status.getLevel() == Level.INFO) .collect(Collectors.toList()); } public List getAllStatus() { return Collections.unmodifiableList(statuses); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/result/Status.java ================================================ /** * Copyright (C) 2017 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.api.result; import static java.util.Objects.requireNonNull; import java.io.Serializable; import java.util.Map; import lombok.ToString; /** * A status represents a generic result of some action. * It has a severity level, a technical message (non internationalized), * a unique status code used for internationalization and * an optional context saving the implicated item (eg: a data or process name) */ @ToString public class Status implements Serializable { public enum Level { OK, ERROR, WARNING, INFO } private Level level; private String message; private StatusContext context; private StatusCode code; public static Status okStatus() { return new Status(Level.OK, StatusCode.OK, "", null); } public static Status errorStatus(StatusCode code, String message) { return errorStatus(code, message, null); } public static Status errorStatus(StatusCode code, String message, Map context) { return new Status(Level.ERROR, code, message, context); } public static Status warningStatus(StatusCode code, String message) { return warningStatus(code, message, null); } public static Status warningStatus(StatusCode code, String message, Map context) { return new Status(Level.WARNING, code, message, context); } public static Status infoStatus(StatusCode code, String message) { return infoStatus(code, message, null); } public static Status infoStatus(StatusCode code, String message, Map context) { return new Status(Level.INFO, code, message, context); } private Status(Level level, StatusCode code, String message, Map context) { this.level = level; this.code = code; this.message = requireNonNull(message); this.context = (context != null ? new StatusContext(context) : new StatusContext()); } public StatusCode getCode() { return code; } public String getMessage() { return message; } public Level getLevel() { return level; } public StatusContext getContext() { return context; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/result/StatusCode.java ================================================ /** * Copyright (C) 2017 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.api.result; public enum StatusCode { /***************************** * ACCESS CONTROL STATUS CODE *****************************/ /** * Everything is fine */ OK, /** * Failed to deploy BDM Access Control */ BDM_ACCESS_CONTROL_INSTALLATION_ERROR, /** * Uploaded file is a null byte array. Not supported. */ BDM_ACCESS_CONTROL_FILE_EMPTY, /** * Detected a duplication of attributes in a BDM Access Control rule */ BDM_ACCESS_CONTROL_ATTRIBUTES_DUPLICATION_ERROR, /** * Detected a duplication of static profiles in a BDM Access Control rule */ BDM_ACCESS_CONTROL_STATIC_PROFILES_DUPLICATION_ERROR, /** * Detected a duplication of rule name in a BDM Access Control set of Business Object rules */ BDM_ACCESS_CONTROL_RULE_NAME_DUPLICATION_ERROR, /** * A BDM Access Control rule refers to an unknown Business Object attribute */ BDM_ACCESS_CONTROL_UNKNOWN_ATTRIBUTE_REFERENCE, /** * A BDM Access Control rule refers to an unknown Business Object */ BDM_ACCESS_CONTROL_UNKNOWN_BUSINESS_OBJECT_REFERENCE, /** * A BDM Access Control rule refers to an unknown profile */ BDM_ACCESS_CONTROL_UNKNOWN_PROFILE_REFERENCE, /** * A BDM Access Control rule grants access to no attribute at all */ BDM_ACCESS_CONTROL_ZERO_ATTRIBUTE_GRANTED, /** * A Business Object has no rule defined at all */ BDM_ACCESS_CONTROL_BUSINESS_OBJECT_WITHOUT_RULE, /** * A Business Object has empty rules */ BDM_ACCESS_CONTROL_BUSINESS_OBJECT_WITH_EMPTY_RULES, /** * A BDM Access Control rule grants access to no profile at all */ BDM_ACCESS_CONTROL_ZERO_PROFILE_GRANTED, /** * There is no BDM installed */ BDM_NOT_INSTALLED, /** * An object in the BDM is referenced as if it was in a composition relationship, when it is not. */ BDM_ACCESS_CONTROL_NON_COMPOSED_OBJECT_REFERENCED_IN_COMPOSITION, /** * An object used in composition in the BDM has rules on the first level of the Access Control file. */ BDM_ACCESS_CONTROL_FIRST_LEVEL_RULES_ON_COMPOSED_OBJECT, /** * The naming of rules and objects in the access control file is inconsistent */ BDM_ACCESS_CONTROL_INCONSISTENT_NAME_IN_FILE, /********************************** * Business Data Model Status Code **********************************/ DUPLICATE_CONSTRAINT_OR_INDEX_NAME, UNIQUE_CONSTRAINT_WITHOUT_NAME, INVALID_SQL_IDENTIFIER_NAME, DISCOURAGED_SQL_IDENTIFIER_NAME, UNIQUE_CONSTRAINT_WITHOUT_FIELD, FIELD_WITHOUT_NAME, QUERY_WITHOUT_NAME, INVALID_JAVA_IDENTIFIER_NAME, DISCOURAGED_JAVA_IDENTIFIER_NAME, QUERY_NAME_LENGTH_TO_HIGH, QUERY_WITHOUT_CONTENT, QUERY_WITHOUT_RETURN_TYPE, QUERY_PARAMETER_WITHOUT_NAME, FORBIDDEN_QUERY_PARAMETER_NAME, QUERY_PARAMETER_WITHOUT_CLASS_NAME, INDEX_WITHOUT_NAME, INDEX_WITHOUT_FIELD, INVALID_FIELD_IDENTIFIER, DISCOURAGED_FIELD_IDENTIFIER, BUSINESS_OBJECT_WITHOUT_NAME, RESERVED_PACKAGE_NAME, INVALID_CHARACTER_IN_BUSINESS_OBJECT_NAME, BUSINESS_OBJECT_WITHOUT_FIELD, DUPLICATE_QUERY_NAME, DUPLICATE_CONSTRAINT_NAME, UNKNOWN_FIELD_IN_CONSTRAINT, EMPTY_BDM, SEVERAL_COMPOSITION_REFERENCE_FOR_A_BUSINESS_OBJECT, CIRCULAR_COMPOSITION_REFERENCE, BUSINESS_OBJECT_USED_IN_COMPOSITION_AND_AGGREGATION, MULTIPLE_AGGREGATION_RELATION_TO_ITSELF, DUPLICATE_BUSINESS_OBJECT_NAME, /** * Living application deployment */ LIVING_APP_DEPLOYMENT, LIVING_APP_REFERENCES_UNKNOWN_APPLICATION_PAGE, LIVING_APP_REFERENCES_UNKNOWN_PAGE, LIVING_APP_REFERENCES_UNKNOWN_PROFILE, LIVING_APP_REFERENCES_UNKNOWN_LAYOUT, LIVING_APP_REFERENCES_UNKNOWN_THEME, PROCESS_DEPLOYMENT_CREATE_NEW, PROCESS_DEPLOYMENT_SKIP_INSTALL, PROCESS_DEPLOYMENT_REPLACE_EXISTING, PROCESS_DEPLOYMENT_ENABLEMENT_OK, PROCESS_DEPLOYMENT_ENABLEMENT_KO, PROCESS_DEPLOYMENT_DISABLEMENT_OK, PROCESS_DEPLOYMENT_IMPOSSIBLE_UNRESOLVED, PAGE_DEPLOYMENT_CREATE_NEW, PAGE_DEPLOYMENT_UPDATE_EXISTING, /**************************** * Organization Status Codes ***************************/ /** * General warning when something goes wrong when importing Organization */ ORGANIZATION_IMPORT_WARNING } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/result/StatusContext.java ================================================ /** * Copyright (C) 2017 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.api.result; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * @author Emmanuel Duchastenier */ public class StatusContext extends HashMap { public static final String BUSINESS_OBJECT_NAME_KEY = "businessObjectName"; public static final String ACCESS_RULE_NAME_KEY = "accessRuleName"; public static final String ATTRIBUTE_NAME_KEY = "attributeName"; public static final String PROFILE_NAME_KEY = "profileName"; public static final String BDM_ARTIFACT_KEY = "bdmArtifact"; public static final String BDM_ARTIFACT_NAME_KEY = "bdmArtifactName"; public static final String INVALID_NAME_KEY = "invalidName"; public static final String LIVING_APPLICATION_TOKEN_KEY = "livingApplicationToken"; public static final String LIVING_APPLICATION_IMPORT_STATUS_KEY = "livingApplicationImportStatus"; public static final String LIVING_APPLICATION_INVALID_ELEMENT_NAME = "livingApplicationInvalidElementName"; public static final String LIVING_APPLICATION_INVALID_ELEMENT_TYPE = "livingApplicationInvalidElementType"; public static final String PROCESS_NAME_KEY = "processName"; public static final String PROCESS_VERSION_KEY = "processVersion"; public static final String PROCESS_RESOLUTION_PROBLEM_RESOURCE_TYPE_KEY = "processResolutionProblemResourceType"; public static final String PROCESS_RESOLUTION_PROBLEM_RESOURCE_ID_KEY = "processResolutionProblemResourceId"; public static final String PROCESS_RESOLUTION_PROBLEM_DESCRIPTION_KEY = "processResolutionProblemDescription"; public static final String PROCESS_DEPLOYMENT_FAILURE_REASON_KEY = "processVersion"; public static final String PAGE_NAME_KEY = "pageName"; public StatusContext() { super(); } public StatusContext(Map context) { super(context); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/BusinessObjectModelValidationException.java ================================================ /** * 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.bdm; import java.io.Serial; import org.bonitasoft.engine.api.result.Status; import org.bonitasoft.engine.api.result.Status.Level; import org.bonitasoft.engine.bdm.validator.ValidationStatus; /** * @author Romain Bioteau */ public class BusinessObjectModelValidationException extends Exception { @Serial private static final long serialVersionUID = 1L; private final ValidationStatus validationStatus; public BusinessObjectModelValidationException(final ValidationStatus validationStatus) { this.validationStatus = validationStatus; } @Override public String getMessage() { final StringBuilder sb = new StringBuilder(); validationStatus.getStatuses().stream().filter(status -> Level.ERROR.equals(status.getLevel())) .map(Status::getMessage).forEach(s -> sb.append("\n- ").append(s)); return sb.toString(); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/Entity.java ================================================ /** * 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.bdm; import java.io.Serializable; /** * Generic interface for a Business Object. * * @author Romain Bioteau * @author Matthieu Chaffotte * @since 7.0 */ public interface Entity extends Serializable { Long getPersistenceId(); Long getPersistenceVersion(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/dao/BusinessObjectDAO.java ================================================ /** * 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.bdm.dao; /** * Common interface for business object DAOs. * * @author Romain Bioteau * @since 7.0 */ public interface BusinessObjectDAO { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/lazy/LazyLoaded.java ================================================ /** * 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.bdm.lazy; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author Colin Puy */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LazyLoaded { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/serialization/BusinessDataObjectMapper.java ================================================ /** * Copyright (C) 2017 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.bdm.serialization; import java.io.IOException; import java.io.Serializable; import java.io.StringWriter; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.util.List; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.module.SimpleModule; import org.bonitasoft.engine.bdm.Entity; public class BusinessDataObjectMapper { protected ObjectMapper objectMapper; public BusinessDataObjectMapper() { objectMapper = new ObjectMapper(); // avoid to fail when serializing proxy (proxy will be recreated client side) see BS-16031 objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); SimpleModule module = new SimpleModule(); module.addSerializer(LocalDate.class, new CustomLocalDateSerializer()); module.addSerializer(LocalDateTime.class, new CustomLocalDateTimeSerializer()); module.addSerializer(OffsetDateTime.class, new CustomOffsetDateTimeSerializer()); module.addDeserializer(LocalDate.class, new CustomLocalDateDeserializer()); module.addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer()); module.addDeserializer(OffsetDateTime.class, new CustomOffsetDateTimeDeserializer()); objectMapper.registerModule(module); } public void writeValue(StringWriter writer, Entity entity) throws IOException { objectMapper.writeValue(writer, entity); } public void writeValue(StringWriter writer, List entities) throws IOException { objectMapper.writeValue(writer, entities); } public List readListValue(byte[] result, Class loadClass) throws IOException { return objectMapper.readValue(result, objectMapper.getTypeFactory().constructCollectionType(List.class, loadClass)); } public T readValue(byte[] result, Class loadClass) throws IOException { return objectMapper.readValue(result, loadClass); } public byte[] writeValueAsBytes(Serializable result) throws IOException { return objectMapper.writeValueAsBytes(result); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateDeserializer.java ================================================ /** * Copyright (C) 2017 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.bdm.serialization; import java.io.IOException; import java.time.LocalDate; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; /** * @author Emmanuel Duchastenier */ public class CustomLocalDateDeserializer extends StdDeserializer { public CustomLocalDateDeserializer() { super(LocalDate.class); } @Override public LocalDate deserialize(JsonParser p, DeserializationContext context) throws IOException { final String value = p.readValueAs(String.class); if (value == null) { return null; } return LocalDate.parse(value); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateSerializer.java ================================================ /** * Copyright (C) 2017 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.bdm.serialization; import java.io.IOException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdSerializer; /** * @author Emmanuel Duchastenier */ public class CustomLocalDateSerializer extends StdSerializer { public CustomLocalDateSerializer() { this(null); } public CustomLocalDateSerializer(Class t) { super(t); } @Override public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeString((value != null) ? DateTimeFormatter.ISO_LOCAL_DATE.format(value) : null); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateTimeDeserializer.java ================================================ /** * Copyright (C) 2017 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.bdm.serialization; import java.io.IOException; import java.time.LocalDateTime; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; /** * @author Emmanuel Duchastenier */ public class CustomLocalDateTimeDeserializer extends StdDeserializer { public CustomLocalDateTimeDeserializer() { super(LocalDateTime.class); } @Override public LocalDateTime deserialize(JsonParser p, DeserializationContext context) throws IOException { final String value = p.readValueAs(String.class); if (value == null) { return null; } return LocalDateTime.parse(value); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateTimeSerializer.java ================================================ /** * Copyright (C) 2017 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.bdm.serialization; import java.io.IOException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdSerializer; /** * @author Emmanuel Duchastenier */ public class CustomLocalDateTimeSerializer extends StdSerializer { public CustomLocalDateTimeSerializer() { this(null); } public CustomLocalDateTimeSerializer(Class t) { super(t); } @Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeString((value != null) ? DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(value) : null); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/serialization/CustomOffsetDateTimeDeserializer.java ================================================ /** * 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.bdm.serialization; import java.io.IOException; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; /** * @author Danila Mazour */ public class CustomOffsetDateTimeDeserializer extends StdDeserializer { public CustomOffsetDateTimeDeserializer() { super(LocalDateTime.class); } @Override public OffsetDateTime deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { final String value = jp.readValueAs(String.class); if (value == null) { return null; } return OffsetDateTime.parse(value).withOffsetSameInstant(ZoneOffset.UTC); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/serialization/CustomOffsetDateTimeSerializer.java ================================================ /** * 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.bdm.serialization; import java.io.IOException; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdSerializer; /** * @author Danila Mazour */ public class CustomOffsetDateTimeSerializer extends StdSerializer { public CustomOffsetDateTimeSerializer() { this(null); } public CustomOffsetDateTimeSerializer(Class t) { super(t); } @Override public void serialize(OffsetDateTime value, JsonGenerator jgen, SerializerProvider provider) throws IOException { jgen.writeString((value != null) ? DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(value.withOffsetSameInstant(ZoneOffset.UTC)) : null); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/BusinessObjectModelValidator.java ================================================ /** * 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.bdm.validator; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.Query; import org.bonitasoft.engine.bdm.model.UniqueConstraint; import org.bonitasoft.engine.bdm.validator.rule.BusinessObjectModelValidationRule; import org.bonitasoft.engine.bdm.validator.rule.BusinessObjectValidationRule; import org.bonitasoft.engine.bdm.validator.rule.FieldValidationRule; import org.bonitasoft.engine.bdm.validator.rule.IndexValidationRule; import org.bonitasoft.engine.bdm.validator.rule.MultipleAggregationToItselfValidationRule; import org.bonitasoft.engine.bdm.validator.rule.QueryParameterValidationRule; import org.bonitasoft.engine.bdm.validator.rule.QueryValidationRule; import org.bonitasoft.engine.bdm.validator.rule.SimpleFieldValidationRule; import org.bonitasoft.engine.bdm.validator.rule.UniqueConstraintValidationRule; import org.bonitasoft.engine.bdm.validator.rule.UniqueSimpleNameValidationRule; import org.bonitasoft.engine.bdm.validator.rule.ValidationRule; import org.bonitasoft.engine.bdm.validator.rule.composition.AggregationAndCompositionValidationRule; import org.bonitasoft.engine.bdm.validator.rule.composition.CyclicCompositionValidationRule; import org.bonitasoft.engine.bdm.validator.rule.composition.UniquenessCompositionValidationRule; /** * @author Romain Bioteau */ public class BusinessObjectModelValidator { private final List> rules = new ArrayList<>(); public BusinessObjectModelValidator() { rules.add(new BusinessObjectModelValidationRule()); rules.add(new BusinessObjectValidationRule()); rules.add(new FieldValidationRule()); rules.add(new SimpleFieldValidationRule()); rules.add(new UniqueConstraintValidationRule()); rules.add(new IndexValidationRule()); rules.add(new QueryValidationRule()); rules.add(new QueryParameterValidationRule()); rules.add(new MultipleAggregationToItselfValidationRule()); rules.add(new UniquenessCompositionValidationRule()); rules.add(new CyclicCompositionValidationRule()); rules.add(new AggregationAndCompositionValidationRule()); rules.add(new UniqueSimpleNameValidationRule()); } public ValidationStatus validate(final BusinessObjectModel bom) { final Set objectsToValidate = buildModelTree(bom); final ValidationStatus status = new ValidationStatus(); for (final Object modelElement : objectsToValidate) { for (final ValidationRule rule : rules) { if (rule.appliesTo(modelElement)) { status.addValidationStatus(rule.checkRule(modelElement)); } } } return status; } private Set buildModelTree(final BusinessObjectModel bom) { final Set objectsToValidate = new HashSet<>(); objectsToValidate.add(bom); for (final BusinessObject bo : bom.getBusinessObjects()) { objectsToValidate.add(bo); objectsToValidate.addAll(bo.getFields()); final List uniqueConstraints = bo.getUniqueConstraints(); objectsToValidate.addAll(uniqueConstraints); final List queries = bo.getQueries(); for (final Query q : queries) { objectsToValidate.add(q); objectsToValidate.addAll(q.getQueryParameters()); } objectsToValidate.addAll(bo.getIndexes()); } return objectsToValidate; } public List> getRules() { return Collections.unmodifiableList(rules); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/UniqueNameValidator.java ================================================ /** * 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.bdm.validator; import java.io.Serializable; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.api.result.StatusCode; import org.bonitasoft.engine.api.result.StatusContext; import org.bonitasoft.engine.bdm.model.NamedElement; /** * @author Colin PUY */ public class UniqueNameValidator { public ValidationStatus validate(Collection namedElements, String namedElementTypePluralForm) { ValidationStatus status = new ValidationStatus(); Set duplicateNames = findDuplicateNames(namedElements); Map context = new HashMap<>(); context.put(StatusContext.BDM_ARTIFACT_KEY, namedElementTypePluralForm); for (String name : duplicateNames) { context.put(StatusContext.BDM_ARTIFACT_NAME_KEY, name); status.addError(StatusCode.DUPLICATE_CONSTRAINT_OR_INDEX_NAME, String.format("There are at least two %s with the same name : %s", namedElementTypePluralForm, name), context); } return status; } private Set findDuplicateNames(Collection list) { Set duplicates = new LinkedHashSet<>(); Set uniqueNames = new HashSet<>(); for (NamedElement t : list) { if (!uniqueNames.add(t.getName())) { duplicates.add(t.getName()); } } return duplicates; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/ValidationStatus.java ================================================ /** * 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.bdm.validator; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import org.bonitasoft.engine.api.result.Status; import org.bonitasoft.engine.api.result.StatusCode; /** * @author Romain Bioteau */ public class ValidationStatus { private final List statusList; public ValidationStatus() { statusList = new ArrayList<>(); } public void addError(StatusCode code, final String error) { if (error == null || error.isEmpty()) { throw new IllegalArgumentException("error message cannot be null or empty"); } statusList.add(Status.errorStatus(code, error)); } public void addError(StatusCode code, final String error, Map context) { if (error == null || error.isEmpty()) { throw new IllegalArgumentException("error message cannot be null or empty"); } statusList.add(Status.errorStatus(code, error, context)); } public void addWarning(StatusCode code, final String warning) { if (warning == null || warning.isEmpty()) { throw new IllegalArgumentException("warning message cannot be null or empty"); } statusList.add(Status.warningStatus(code, warning)); } public void addWarning(StatusCode code, final String warning, Map context) { if (warning == null || warning.isEmpty()) { throw new IllegalArgumentException("warning message cannot be null or empty"); } statusList.add(Status.warningStatus(code, warning, context)); } public boolean isOk() { return statusList.stream().map(Status::getLevel).noneMatch(Status.Level.ERROR::equals); } public void addValidationStatus(final ValidationStatus status) { statusList.addAll(status.getStatuses()); } /** * @Deprecated since release 7.7.0, replaced by {@link #getStatuses()} */ @Deprecated(since = "7.7.0", forRemoval = true) public List getErrors() { return statusList.stream().filter(status -> Objects.equals(Status.Level.ERROR, status.getLevel())) .map(Status::getMessage).collect(Collectors.toList()); } /** * @Deprecated since release 7.7.0, replaced by {@link #getStatuses()} */ @Deprecated(since = "7.7.0", forRemoval = true) public List getWarnings() { return statusList.stream().filter(status -> Objects.equals(Status.Level.WARNING, status.getLevel())) .map(Status::getMessage).collect(Collectors.toList()); } public List getStatuses() { return statusList; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ValidationStatus that)) return false; return Objects.equals(statusList, that.statusList); } @Override public int hashCode() { return Objects.hash(statusList); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/BusinessObjectModelValidationRule.java ================================================ /** * 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.bdm.validator.rule; import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.api.result.StatusCode; import org.bonitasoft.engine.api.result.StatusContext; import org.bonitasoft.engine.bdm.BDMQueryUtil; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.Query; import org.bonitasoft.engine.bdm.validator.ValidationStatus; /** * @author Romain Bioteau */ public class BusinessObjectModelValidationRule extends ValidationRule { public BusinessObjectModelValidationRule() { super(BusinessObjectModel.class); } @Override public ValidationStatus validate(final BusinessObjectModel bom) { final ValidationStatus status = new ValidationStatus(); if (bom.getBusinessObjects().isEmpty()) { status.addError(StatusCode.EMPTY_BDM, "Business object model must have at least one business object declared"); } else { validateQueries(bom, status); } return status; } private void validateQueries(final BusinessObjectModel bom, final ValidationStatus status) { for (final BusinessObject bo : bom.getBusinessObjects()) { final List lazyQueries = BDMQueryUtil.createProvidedQueriesForLazyField(bom, bo); final Set lazyQueryNames = new HashSet<>(); for (final Query query : lazyQueries) { lazyQueryNames.add(query.getName()); } Map context = new HashMap<>(); context.put(StatusContext.BUSINESS_OBJECT_NAME_KEY, bo.getQualifiedName()); for (final Query q : bo.getQueries()) { if (lazyQueryNames.contains(q.getName())) { context.put(StatusContext.BDM_ARTIFACT_NAME_KEY, q.getName()); status.addError(StatusCode.DUPLICATE_QUERY_NAME, "The query named \"" + q.getName() + "\" already exists for " + bo.getQualifiedName(), context); } } } } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/BusinessObjectValidationRule.java ================================================ /** * 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.bdm.validator.rule; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import javax.lang.model.SourceVersion; import org.bonitasoft.engine.api.result.StatusCode; import org.bonitasoft.engine.api.result.StatusContext; import org.bonitasoft.engine.bdm.BDMQueryUtil; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.Query; import org.bonitasoft.engine.bdm.model.UniqueConstraint; import org.bonitasoft.engine.bdm.model.field.Field; import org.bonitasoft.engine.bdm.validator.SQLNameValidator; import org.bonitasoft.engine.bdm.validator.SQLNameValidator.Grammar; import org.bonitasoft.engine.bdm.validator.ValidationStatus; /** * @author Romain Bioteau */ public class BusinessObjectValidationRule extends ValidationRule { private static final String[] RESERVED_PACKAGE_PREFIX = { "com.bonitasoft.", "org.bonitasoft." }; private static final int MAX_TABLE_NAME_LENGTH = 30; private final SQLNameValidator sqlNameValidator; public BusinessObjectValidationRule() { super(BusinessObject.class); sqlNameValidator = new SQLNameValidator(MAX_TABLE_NAME_LENGTH); } @Override public ValidationStatus validate(final BusinessObject bo) { final ValidationStatus status = new ValidationStatus(); final String qualifiedName = bo.getQualifiedName(); if (qualifiedName == null) { status.addError(StatusCode.BUSINESS_OBJECT_WITHOUT_NAME, "A Business Object must have a qualified name"); return status; } for (String reservedPrefix : RESERVED_PACKAGE_PREFIX) { if (qualifiedName.startsWith(reservedPrefix)) { status.addError(StatusCode.RESERVED_PACKAGE_NAME, String.format("Package %s is reserved. Please choose another package name", reservedPrefix), Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, reservedPrefix)); } } final String simpleName = bo.getSimpleName(); if (!SourceVersion.isName(qualifiedName) || !sqlNameValidator.isValid(simpleName)) { status.addError(StatusCode.INVALID_JAVA_IDENTIFIER_NAME, String.format("%s is not a valid Java qualified name", qualifiedName), Collections.singletonMap(StatusContext.BUSINESS_OBJECT_NAME_KEY, qualifiedName)); return status; } else { var discouragingGrammars = sqlNameValidator.isKeywordDiscouragedBy(simpleName); if (!discouragingGrammars.isEmpty()) { String msg = String.format("%1$s is discouraged as a business object's name. It is a keyword in %2$s.", simpleName, discouragingGrammars.stream().map(Grammar::toString).collect(Collectors.joining(", "))); status.addWarning(StatusCode.DISCOURAGED_JAVA_IDENTIFIER_NAME, msg, Collections.singletonMap(StatusContext.BUSINESS_OBJECT_NAME_KEY, simpleName)); } } if (simpleName.contains("_")) { status.addError(StatusCode.INVALID_CHARACTER_IN_BUSINESS_OBJECT_NAME, "_ is a forbidden character in business object's name"); } if (bo.getFields().isEmpty()) { status.addError(StatusCode.BUSINESS_OBJECT_WITHOUT_FIELD, String.format("%s must have at least one field declared", qualifiedName), Collections.singletonMap(StatusContext.BUSINESS_OBJECT_NAME_KEY, qualifiedName)); } validateConstraints(bo, status); validateQueries(bo, status); return status; } private void validateQueries(final BusinessObject bo, final ValidationStatus status) { final Set queryNames = BDMQueryUtil.getAllProvidedQueriesNameForBusinessObject(bo); Map context = new HashMap<>(); context.put(StatusContext.BUSINESS_OBJECT_NAME_KEY, bo.getQualifiedName()); for (final Query q : bo.getQueries()) { if (queryNames.contains(q.getName())) { context.put(StatusContext.BDM_ARTIFACT_NAME_KEY, q.getName()); status.addError(StatusCode.DUPLICATE_QUERY_NAME, "The query named \"" + q.getName() + "\" already exists for " + bo.getQualifiedName(), context); } else { queryNames.add(q.getName()); } } } private void validateConstraints(final BusinessObject bo, final ValidationStatus status) { final Set constraintNames = new HashSet<>(); Map context = new HashMap<>(); context.put(StatusContext.BUSINESS_OBJECT_NAME_KEY, bo.getQualifiedName()); for (final UniqueConstraint uc : bo.getUniqueConstraints()) { String name = uc.getName(); if (constraintNames.contains(name)) { context.put(StatusContext.BDM_ARTIFACT_NAME_KEY, name); status.addError(StatusCode.DUPLICATE_CONSTRAINT_NAME, "The constraint named \"" + name + "\" already exists for " + bo.getQualifiedName(), context); } else { constraintNames.add(name); } List fieldNames = uc.getFieldNames(); if (fieldNames != null) { for (final String fName : fieldNames) { if (getField(bo, fName) == null) { context.put(StatusContext.BDM_ARTIFACT_NAME_KEY, fName); status.addError(StatusCode.UNKNOWN_FIELD_IN_CONSTRAINT, String.format("The field named %s does not exist in %s", fName, bo.getQualifiedName()), context); } } } } } private Field getField(final BusinessObject bo, final String name) { Field found = null; int index = 0; final List fields = bo.getFields(); while (found == null && index < fields.size()) { final Field field = bo.getFields().get(index); if (field.getName().equals(name)) { found = field; } index++; } return found; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/FieldValidationRule.java ================================================ /** * 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.bdm.validator.rule; import java.util.Collections; import java.util.stream.Collectors; import javax.lang.model.SourceVersion; import org.bonitasoft.engine.api.result.StatusCode; import org.bonitasoft.engine.api.result.StatusContext; import org.bonitasoft.engine.bdm.model.field.Field; import org.bonitasoft.engine.bdm.validator.SQLNameValidator; import org.bonitasoft.engine.bdm.validator.SQLNameValidator.Grammar; import org.bonitasoft.engine.bdm.validator.ValidationStatus; /** * @author Romain Bioteau */ public class FieldValidationRule extends ValidationRule { private static final int MAX_COLUMNAME_LENGTH = 50; private final SQLNameValidator sqlNameValidator; public FieldValidationRule() { super(Field.class); sqlNameValidator = new SQLNameValidator(MAX_COLUMNAME_LENGTH); } @Override public ValidationStatus validate(final Field field) { final ValidationStatus status = new ValidationStatus(); final String name = field.getName(); if (name == null || !SourceVersion.isIdentifier(name) || SourceVersion.isKeyword(name) || isForbiddenIdentifier(name)) { status.addError(StatusCode.INVALID_FIELD_IDENTIFIER, String.format("%s is not a valid field identifier", name), Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name)); return status; } else { var discouragingGrammars = sqlNameValidator.isKeywordDiscouragedBy(name); if (!discouragingGrammars.isEmpty()) { String msg = String.format("%1$s is discouraged as a field identifier. It is a keyword in %2$s.", name, discouragingGrammars.stream().map(Grammar::toString).collect(Collectors.joining(", "))); status.addWarning(StatusCode.DISCOURAGED_FIELD_IDENTIFIER, msg, Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name)); } } return status; } private boolean isForbiddenIdentifier(final String name) { return Field.PERSISTENCE_ID.equalsIgnoreCase(name) || Field.PERSISTENCE_VERSION.equalsIgnoreCase(name) || !sqlNameValidator.isValid(name); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/IndexValidationRule.java ================================================ /** * 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.bdm.validator.rule; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import org.bonitasoft.engine.api.result.StatusCode; import org.bonitasoft.engine.api.result.StatusContext; import org.bonitasoft.engine.bdm.model.Index; import org.bonitasoft.engine.bdm.validator.SQLNameValidator; import org.bonitasoft.engine.bdm.validator.SQLNameValidator.Grammar; import org.bonitasoft.engine.bdm.validator.ValidationStatus; public class IndexValidationRule extends ValidationRule { private static final int MAX_CONSTRAINTNAME_LENGTH = 25; private final SQLNameValidator sqlNameValidator; public IndexValidationRule() { super(Index.class); sqlNameValidator = new SQLNameValidator(MAX_CONSTRAINTNAME_LENGTH); } @Override protected ValidationStatus validate(Index index) { final ValidationStatus status = new ValidationStatus(); final String name = index.getName(); if (name == null || name.isEmpty()) { status.addError(StatusCode.INDEX_WITHOUT_NAME, "An index must have name"); return status; } if (!sqlNameValidator.isValid(name)) { status.addError(StatusCode.INVALID_SQL_IDENTIFIER_NAME, String.format("%s is not a valid SQL identifier", name), Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name)); } else { var discouragingGrammars = sqlNameValidator.isKeywordDiscouragedBy(name); if (!discouragingGrammars.isEmpty()) { String msg = String.format("%1$s is discouraged as an SQL identifier. It is a keyword in %2$s.", name, discouragingGrammars.stream().map(Grammar::toString).collect(Collectors.joining(", "))); status.addWarning(StatusCode.DISCOURAGED_SQL_IDENTIFIER_NAME, msg, Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name)); } } List fieldNames = index.getFieldNames(); if (fieldNames == null || fieldNames.isEmpty()) { status.addError(StatusCode.INDEX_WITHOUT_FIELD, String.format("%s index must have at least one field declared", name), Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name)); } return status; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/MultipleAggregationToItselfValidationRule.java ================================================ /** * 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.bdm.validator.rule; import static org.bonitasoft.engine.api.result.StatusCode.MULTIPLE_AGGREGATION_RELATION_TO_ITSELF; import static org.bonitasoft.engine.bdm.model.field.RelationField.Type.AGGREGATION; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.api.result.StatusContext; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.Field; import org.bonitasoft.engine.bdm.model.field.RelationField; import org.bonitasoft.engine.bdm.validator.ValidationStatus; /** * @author Danila Mazour */ public class MultipleAggregationToItselfValidationRule extends ValidationRule { public MultipleAggregationToItselfValidationRule() { super(BusinessObjectModel.class); } @Override protected ValidationStatus validate(BusinessObjectModel bom) { ValidationStatus validationStatus = new ValidationStatus(); for (BusinessObject bo : bom.getBusinessObjects()) { List boFields = bo.getFields(); for (Field boField : boFields) { if (boField instanceof RelationField relationField) { String fieldReferenceQualifiedName = relationField.getReference().getQualifiedName(); String boQualifiedName = bo.getQualifiedName(); if (relationField.getType() == AGGREGATION && fieldReferenceQualifiedName.equals(boQualifiedName) && relationField.isCollection()) { validationStatus.addError(MULTIPLE_AGGREGATION_RELATION_TO_ITSELF, "The object " + boQualifiedName + " is referencing itself in a multiple aggregation relation.", Collections.singletonMap(StatusContext.BUSINESS_OBJECT_NAME_KEY, boQualifiedName)); } } } } return validationStatus; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/QueryParameterValidationRule.java ================================================ /** * 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.bdm.validator.rule; /** * Tests the validity of Query parameters * * @author Emmanuel Duchastenier */ import java.util.Arrays; import java.util.Collections; import java.util.List; import javax.lang.model.SourceVersion; import org.bonitasoft.engine.api.result.StatusCode; import org.bonitasoft.engine.api.result.StatusContext; import org.bonitasoft.engine.bdm.BDMQueryUtil; import org.bonitasoft.engine.bdm.model.QueryParameter; import org.bonitasoft.engine.bdm.validator.ValidationStatus; public class QueryParameterValidationRule extends ValidationRule { public static final List FORBIDDEN_PARAMETER_NAMES = Arrays.asList(BDMQueryUtil.START_INDEX_PARAM_NAME, BDMQueryUtil.MAX_RESULTS_PARAM_NAME); public QueryParameterValidationRule() { super(QueryParameter.class); } @Override public ValidationStatus validate(final QueryParameter parameter) { final ValidationStatus status = new ValidationStatus(); final String name = parameter.getName(); if (name == null || name.isEmpty()) { status.addError(StatusCode.QUERY_PARAMETER_WITHOUT_NAME, "A parameter must have name"); return status; } if (!SourceVersion.isIdentifier(name)) { status.addError(StatusCode.INVALID_JAVA_IDENTIFIER_NAME, String.format("%s is not a valid Java identifier.", name), Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name)); } if (FORBIDDEN_PARAMETER_NAMES.contains(name)) { status.addError(StatusCode.FORBIDDEN_QUERY_PARAMETER_NAME, String.format("%s is a reserved parameter name. Use a name different from: %s", name, FORBIDDEN_PARAMETER_NAMES), Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name)); } if (parameter.getClassName() == null || parameter.getClassName().isEmpty()) { status.addError(StatusCode.QUERY_PARAMETER_WITHOUT_CLASS_NAME, String.format("%s query parameter must have a classname", name), Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name)); } return status; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/QueryValidationRule.java ================================================ /** * 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.bdm.validator.rule; import java.util.Collections; import javax.lang.model.SourceVersion; import org.bonitasoft.engine.api.result.StatusCode; import org.bonitasoft.engine.api.result.StatusContext; import org.bonitasoft.engine.bdm.model.Query; import org.bonitasoft.engine.bdm.validator.ValidationStatus; public class QueryValidationRule extends ValidationRule { private static final int MAX_QUERY_NAME_LENGTH = 150; public QueryValidationRule() { super(Query.class); } @Override public ValidationStatus validate(final Query query) { final ValidationStatus status = new ValidationStatus(); final String name = query.getName(); if (name == null || name.isEmpty()) { status.addError(StatusCode.QUERY_WITHOUT_NAME, "A query must have name"); return status; } if (!SourceVersion.isIdentifier(name)) { status.addError(StatusCode.INVALID_JAVA_IDENTIFIER_NAME, String.format("%s is not a valid Java identifier.", name), Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name)); } if (name.length() > MAX_QUERY_NAME_LENGTH) { status.addError(StatusCode.QUERY_NAME_LENGTH_TO_HIGH, String.format("%s length must be lower than 150 characters.", name), Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name)); } if (query.getContent() == null || query.getContent().isEmpty()) { status.addError(StatusCode.QUERY_WITHOUT_CONTENT, String.format("%s query must have a content defined", name), Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name)); } if (query.getReturnType() == null || query.getReturnType().isEmpty()) { status.addError(StatusCode.QUERY_WITHOUT_RETURN_TYPE, String.format("%s query must have a return type defined", name), Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name)); } return status; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/SimpleFieldValidationRule.java ================================================ /** * 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.bdm.validator.rule; import java.util.Collections; import org.bonitasoft.engine.api.result.StatusCode; import org.bonitasoft.engine.api.result.StatusContext; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.bdm.validator.ValidationStatus; /** * @author Colin PUY */ public class SimpleFieldValidationRule extends ValidationRule { public SimpleFieldValidationRule() { super(SimpleField.class); } @Override public ValidationStatus validate(SimpleField field) { final ValidationStatus status = new ValidationStatus(); if (field.getType() == null) { status.addError(StatusCode.FIELD_WITHOUT_NAME, String.format("%s must have a type declared", field.getName()), Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, field.getName())); } return status; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/UniqueConstraintValidationRule.java ================================================ /** * 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.bdm.validator.rule; import static java.util.Collections.singletonMap; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import org.bonitasoft.engine.api.result.StatusCode; import org.bonitasoft.engine.api.result.StatusContext; import org.bonitasoft.engine.bdm.model.UniqueConstraint; import org.bonitasoft.engine.bdm.validator.SQLNameValidator; import org.bonitasoft.engine.bdm.validator.SQLNameValidator.Grammar; import org.bonitasoft.engine.bdm.validator.ValidationStatus; public class UniqueConstraintValidationRule extends ValidationRule { private static final int MAX_CONSTRAINTNAME_LENGTH = 25; private final SQLNameValidator sqlNameValidator; public UniqueConstraintValidationRule() { super(UniqueConstraint.class); sqlNameValidator = new SQLNameValidator(MAX_CONSTRAINTNAME_LENGTH); } @Override public ValidationStatus validate(final UniqueConstraint uc) { final ValidationStatus status = new ValidationStatus(); final String name = uc.getName(); if (name == null || name.isEmpty()) { status.addError(StatusCode.UNIQUE_CONSTRAINT_WITHOUT_NAME, "A unique constraint must have name"); return status; } if (!sqlNameValidator.isValid(name)) { status.addError(StatusCode.INVALID_SQL_IDENTIFIER_NAME, String.format("%s is not a valid SQL identifier", name), singletonMap(StatusContext.INVALID_NAME_KEY, name)); } else { var discouragingGrammars = sqlNameValidator.isKeywordDiscouragedBy(name); if (!discouragingGrammars.isEmpty()) { String msg = String.format("%1$s is discouraged as an SQL identifier. It is a keyword in %2$s.", name, discouragingGrammars.stream().map(Grammar::toString).collect(Collectors.joining(", "))); status.addWarning(StatusCode.DISCOURAGED_SQL_IDENTIFIER_NAME, msg, Collections.singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name)); } } List fieldNames = uc.getFieldNames(); if (fieldNames == null || fieldNames.isEmpty()) { status.addError(StatusCode.UNIQUE_CONSTRAINT_WITHOUT_FIELD, String.format("%s unique constraint must have at least one field declared", name), singletonMap(StatusContext.BDM_ARTIFACT_NAME_KEY, name)); } return status; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/UniqueNameValidationRule.java ================================================ /** * 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.bdm.validator.rule; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.validator.UniqueNameValidator; import org.bonitasoft.engine.bdm.validator.ValidationStatus; /** * @author Colin PUY */ public class UniqueNameValidationRule extends ValidationRule { private UniqueNameValidator uniqueNameValidator; public UniqueNameValidationRule(UniqueNameValidator uniqueNameValidator) { super(BusinessObjectModel.class); this.uniqueNameValidator = uniqueNameValidator; } @Override protected ValidationStatus validate(BusinessObjectModel bom) { ValidationStatus validationStatus = new ValidationStatus(); validationStatus .addValidationStatus(uniqueNameValidator.validate(bom.getUniqueConstraints(), "unique contraints")); validationStatus.addValidationStatus(uniqueNameValidator.validate(bom.getIndexes(), "indexes")); return validationStatus; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/UniqueSimpleNameValidationRule.java ================================================ /** * 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.bdm.validator.rule; import static org.bonitasoft.engine.api.result.StatusCode.DUPLICATE_BUSINESS_OBJECT_NAME; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.validator.ValidationStatus; /** * @author Danila Mazour */ public class UniqueSimpleNameValidationRule extends ValidationRule { public UniqueSimpleNameValidationRule() { super(BusinessObjectModel.class); } @Override public ValidationStatus validate(final BusinessObjectModel bom) { final ValidationStatus status = new ValidationStatus(); List businessObjects = bom.getBusinessObjects(); Set businessObjectNames = new HashSet<>(); for (BusinessObject businessObject : businessObjects) { if (!businessObjectNames.add(businessObject.getSimpleName().toLowerCase())) { status.addError(DUPLICATE_BUSINESS_OBJECT_NAME, " There are at least 2 objects in the BDM that are called : " + businessObject.getSimpleName()); } } return status; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/ValidationRule.java ================================================ /** * 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.bdm.validator.rule; /** * Validates an object of type T and returns a validation result of type R * * @param the type of element that this validation rule supports * @param the type of the result of the validation * @author Romain Bioteau * @author Emmanuel Duchastenier */ public abstract class ValidationRule { private Class classToApply; public ValidationRule(Class classToApply) { this.classToApply = classToApply; } public boolean appliesTo(Object modelElement) { return modelElement != null && classToApply.isAssignableFrom(modelElement.getClass()); } protected abstract R validate(T modelElement); @SuppressWarnings("unchecked") public R checkRule(Object modelElement) { if (!appliesTo(modelElement)) { throw new IllegalArgumentException( this.getClass().getName() + " doesn't handle validation for " + modelElement.getClass().getName()); } return validate((T) modelElement); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/composition/AggregationAndCompositionValidationRule.java ================================================ /** * 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.bdm.validator.rule.composition; import static org.bonitasoft.engine.bdm.model.field.RelationField.Type.AGGREGATION; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.api.result.StatusCode; import org.bonitasoft.engine.api.result.StatusContext; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.Field; import org.bonitasoft.engine.bdm.model.field.RelationField; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.bonitasoft.engine.bdm.validator.rule.ValidationRule; /** * @author Danila Mazour */ public class AggregationAndCompositionValidationRule extends ValidationRule { public AggregationAndCompositionValidationRule() { super(BusinessObjectModel.class); } @Override protected ValidationStatus validate(BusinessObjectModel bom) { ValidationStatus validationStatus = new ValidationStatus(); List businessObjects = bom.getBusinessObjects(); List aggregatedBusinessObjects = new ArrayList<>(); List composedBusinessObjects = new ArrayList<>(); List fieldList = new ArrayList<>(); for (BusinessObject bo : businessObjects) fieldList.addAll(bo.getFields()); for (Field field : fieldList) { if (field instanceof RelationField relationField) { if (relationField.getType() == AGGREGATION) { aggregatedBusinessObjects.add(relationField.getReference()); } } } composedBusinessObjects.addAll(bom.getReferencedBusinessObjectsByComposition()); for (BusinessObject composedBo : composedBusinessObjects) { if (aggregatedBusinessObjects.contains(composedBo)) { validationStatus.addWarning(StatusCode.BUSINESS_OBJECT_USED_IN_COMPOSITION_AND_AGGREGATION, String.format( "The object %s is referenced both in composition and in aggregation. This may lead to runtime errors and" + " may lead to unpredictable behaviour of the AccessControl configuration.", composedBo.getQualifiedName()), Collections.singletonMap(StatusContext.BUSINESS_OBJECT_NAME_KEY, composedBo.getQualifiedName())); } } return validationStatus; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/composition/CyclicCompositionValidationRule.java ================================================ /** * 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.bdm.validator.rule.composition; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.result.StatusCode; import org.bonitasoft.engine.api.result.StatusContext; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.bonitasoft.engine.bdm.validator.rule.ValidationRule; /** * Check that there are no circular references on referenced business objects by composition. * * @author Colin PUY */ public class CyclicCompositionValidationRule extends ValidationRule { public CyclicCompositionValidationRule() { super(BusinessObjectModel.class); } @Override protected ValidationStatus validate(BusinessObjectModel bom) { ValidationStatus validationStatus = new ValidationStatus(); for (BusinessObject bo : bom.getBusinessObjects()) { validationStatus.addValidationStatus(validateThatThereIsNoCycleDependencies(bo, new ArrayList<>())); } return validationStatus; } private ValidationStatus validateThatThereIsNoCycleDependencies(BusinessObject bo, List parentBOs) { ValidationStatus validationStatus = new ValidationStatus(); parentBOs.add(bo); Map context = new HashMap<>(); context.put(StatusContext.BDM_ARTIFACT_NAME_KEY, bo.getQualifiedName()); for (BusinessObject businessObject : bo.getReferencedBusinessObjectsByComposition()) { if (parentBOs.contains(businessObject)) { context.put(StatusContext.BUSINESS_OBJECT_NAME_KEY, businessObject.getQualifiedName()); validationStatus.addError(StatusCode.CIRCULAR_COMPOSITION_REFERENCE, String.format( "Business object %s has a circular composition reference to itself or is referenced several times in the object %s", businessObject.getQualifiedName(), bo.getQualifiedName()), context); } else { validationStatus.addValidationStatus(validateThatThereIsNoCycleDependencies(businessObject, parentBOs)); } } return validationStatus; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bdm/validator/rule/composition/UniquenessCompositionValidationRule.java ================================================ /** * 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.bdm.validator.rule.composition; import static java.util.Collections.frequency; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.result.StatusCode; import org.bonitasoft.engine.api.result.StatusContext; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.bonitasoft.engine.bdm.validator.rule.ValidationRule; /** * Check that a composite bo is referenced in only one composition * * @author Colin PUY */ public class UniquenessCompositionValidationRule extends ValidationRule { public UniquenessCompositionValidationRule() { super(BusinessObjectModel.class); } @Override protected ValidationStatus validate(BusinessObjectModel bom) { ValidationStatus validationStatus = new ValidationStatus(); // Make sure there are no duplicates object in composition in the model: final List objectsByComposition = bom.getReferencedBusinessObjectsByComposition(); objectsByComposition.stream() .distinct() .filter(compositeBO -> frequency(objectsByComposition, compositeBO) > 1) .forEach(compositeBO -> validationStatus.addError( StatusCode.SEVERAL_COMPOSITION_REFERENCE_FOR_A_BUSINESS_OBJECT, String.format( "Business object %s is referenced by composition in two business objects, or is referenced several times in a single business object", compositeBO.getQualifiedName()), Map.of(StatusContext.BUSINESS_OBJECT_NAME_KEY, compositeBO.getQualifiedName()))); return validationStatus; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/ArchivedElement.java ================================================ /** * 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.bpm; import java.util.Date; /** * Interface ArchivedElement is the root interface of the archived client model hierarchy of Bonita. * * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @since 6.0.0 */ public interface ArchivedElement { /** * Gets the date when the element was archived. * * @return the archived date */ Date getArchiveDate(); /** * Gets the identifier of the BaseElement that the archived element comes from. * * @return the identifier of the source object */ long getSourceObjectId(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/ArchivedRestElement.java ================================================ /** * 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.bpm; import java.util.Date; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonView; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; /** * Interface ArchivedRestElement identifies an archived BonitaObject that can be used in the * REST API. */ public interface ArchivedRestElement extends ArchivedElement { /** * Gets the unique object identifier. * Serialized as a String in json as a Java long can be too big for JavaScript * * @return the unique object identifier */ @JsonSerialize(using = ToStringSerializer.class) @Override long getSourceObjectId(); /** * Gets the date when the element was archived in milliseconds since epoch. * Serialized as a String in json as a Java long can be too big for JavaScript * * @return the unique object identifier */ @JsonView @JsonProperty("archiveDate") @JsonSerialize(using = ToStringSerializer.class) long getArchiveDateAsLong(); /** * Gets the date when the element was archived. * Ignored in Json serialization as it is already serialized as a long * * @return the archived date */ @JsonIgnore @Override Date getArchiveDate(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/BaseRestElement.java ================================================ /** * 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.bpm; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; /** * Interface BaseRestElement identifies a BonitaObject that can be used in the REST API. */ @JsonPropertyOrder(alphabetic = true) public interface BaseRestElement extends BaseElement { /** * Gets the unique object identifier. * Serialized as a String in json as a Java long can be too big for JavaScript * * @return the unique object identifier */ @JsonSerialize(using = ToStringSerializer.class) @Override long getId(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/actor/ActorCriterion.java ================================================ /** * 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.bpm.actor; /** * Criterion to sort {@link ActorInstance}. * * @author Matthieu Chaffotte * @author Celine Souchet * @since 6.0.0 * @version 6.4.1 */ public enum ActorCriterion { /** * By ascending name */ NAME_ASC, /** * By descending name */ NAME_DESC; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/actor/ActorInstance.java ================================================ /** * 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.bpm.actor; import org.bonitasoft.engine.bpm.BaseElement; import org.bonitasoft.engine.bpm.DescriptionElement; /** * Once the {@link org.bonitasoft.engine.bpm.process.ProcessDefinition} is deployed, the associated * {@link ActorDefinition}s are instantiated. * This object represents this instance of {@link ActorDefinition}. * * @author Matthieu Chaffotte * @author Emmanuel Duchastenier * @author Celine Souchet * @since 6.0.0 * @version 6.4.1 */ public interface ActorInstance extends DescriptionElement, BaseElement { /** * Get the identifier of the process definition where this actor is defined. * * @return The identifier of the process definition where this actor is defined. */ long getProcessDefinitionId(); /** * The display name of the actor defined in {@link ActorDefinition#getName()}. * * @return The display name of the actor. */ String getDisplayName(); /** * Can this actor start the process ? * Defined in {@link ActorDefinition#isInitiator()}. * * @return true} if this actor can start the process, false otherwise. */ boolean isInitiator(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/actor/ActorMappingExportException.java ================================================ /** * 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.bpm.actor; import org.bonitasoft.engine.exception.ExecutionException; /** * Thrown when it's not possible to export the actor mappings. * * @author Matthieu Chaffotte * @author Celine Souchet * @since 6.0.0 * @version 6.4.1 */ public class ActorMappingExportException extends ExecutionException { private static final long serialVersionUID = -8085581554578731228L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ActorMappingExportException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/actor/ActorMappingImportException.java ================================================ /** * 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.bpm.actor; import org.bonitasoft.engine.exception.ExecutionException; /** * Thrown when it's not possible to import the actor mappings. * * @author Matthieu Chaffotte * @author Celine Souchet * @since 6.0.0 * @version 6.4.1 */ public class ActorMappingImportException extends ExecutionException { private static final long serialVersionUID = -415010231497905681L; /** * Constructs a new exception with the specified detail message and cause. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ActorMappingImportException(final String message, final Throwable cause) { super(message, cause); } /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ActorMappingImportException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/actor/ActorNotFoundException.java ================================================ /** * 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.bpm.actor; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when it's not possible to find an actor mapping. * * @author Matthieu Chaffotte * @author Celine Souchet * @since 6.0.0 * @version 6.4.1 */ public class ActorNotFoundException extends NotFoundException { private static final long serialVersionUID = 2518419716527606128L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ActorNotFoundException(final Throwable cause) { super(cause); } /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public ActorNotFoundException(final String message) { super(message); } /** * Constructs a new exception and the specified detail message. * * @param actorName * The not found actor * @param processDefinition * The process definition where we search the actor */ public ActorNotFoundException(final String actorName, final ProcessDefinition processDefinition) { super("Actor " + actorName + " not found"); setProcessDefinitionNameOnContext(processDefinition.getName()); setProcessDefinitionIdOnContext(processDefinition.getId()); setProcessDefinitionVersionOnContext(processDefinition.getVersion()); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/actor/ActorUpdater.java ================================================ /** * 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.bpm.actor; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * The descriptor which contains the fields to update an actor. * * @author Matthieu Chaffotte * @author Celine Souchet * @since 6.0.0 * @version 6.4.1 */ public class ActorUpdater implements Serializable { private static final long serialVersionUID = -8812266250085063562L; /** * The fields that can be updated. */ public enum ActorField { /** * Corresponding to the display name of the actor */ DISPLAY_NAME, /** * Corresponding to the description of the actor */ DESCRIPTION; } private final Map fields; /** * The default constructor */ public ActorUpdater() { fields = new HashMap(ActorField.values().length); } /** * Set the new display name. * * @param name * The new display name */ public void setDisplayName(final String name) { fields.put(ActorField.DISPLAY_NAME, name); } /** * Set the new description. * * @param description * The new description */ public void setDescription(final String description) { fields.put(ActorField.DESCRIPTION, description); } /** * Get the fields to update, and the new value. * * @return The map containing the pairs (field, new value) to update. */ public Map getFields() { return fields; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/actor/impl/ActorInstanceImpl.java ================================================ /** * 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.bpm.actor.impl; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.internal.NamedElementImpl; /** * @author Matthieu Chaffotte */ public class ActorInstanceImpl extends NamedElementImpl implements ActorInstance { private static final long serialVersionUID = 8251013663118023803L; private final String displayName; private final long processDefinitionId; private final boolean initiator; private final String description; public ActorInstanceImpl(final String name, final String description, final String displayName, final long processDefinitionId, final boolean initiator) { super(name); this.description = description; this.processDefinitionId = processDefinitionId; this.displayName = displayName; this.initiator = initiator; } @Override public long getProcessDefinitionId() { return this.processDefinitionId; } @Override public String getDisplayName() { return this.displayName; } @Override public boolean isInitiator() { return initiator; } @Override public String getDescription() { return description; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/businessdata/BusinessDataQueryMetadata.java ================================================ /** * 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.bpm.businessdata; import java.io.Serializable; /** * @author Laurent Leseigneur */ public interface BusinessDataQueryMetadata extends Serializable { Integer getStartIndex(); Integer getMaxResults(); Long getCount(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/businessdata/BusinessDataQueryResult.java ================================================ /** * 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.bpm.businessdata; import java.io.Serializable; import org.bonitasoft.engine.search.SearchResult; /** * @author Laurent Leseigneur */ public interface BusinessDataQueryResult extends Serializable, SearchResult { Serializable getJsonResults(); BusinessDataQueryMetadata getBusinessDataQueryMetadata(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/businessdata/impl/BusinessDataQueryMetadataImpl.java ================================================ /** * 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.bpm.businessdata.impl; import org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryMetadata; public class BusinessDataQueryMetadataImpl implements BusinessDataQueryMetadata { private final Integer startIndex; private final Integer maxResults; private final Long count; public BusinessDataQueryMetadataImpl() { this(null, null, null); }; public BusinessDataQueryMetadataImpl(Integer startIndex, Integer maxResults, Long count) { this.startIndex = startIndex; this.maxResults = maxResults; this.count = count; } public Integer getStartIndex() { return startIndex; } public Integer getMaxResults() { return maxResults; } public Long getCount() { return count; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/businessdata/impl/BusinessDataQueryResultImpl.java ================================================ /** * 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.bpm.businessdata.impl; import java.io.Serializable; import java.util.List; import org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryResult; /** * @author Laurent Leseigneur */ public class BusinessDataQueryResultImpl implements BusinessDataQueryResult { private final Serializable jsonResults; private final BusinessDataQueryMetadataImpl businessDataQueryMetadata; public BusinessDataQueryResultImpl() { this(null, null); }; public BusinessDataQueryResultImpl(Serializable jsonResults, BusinessDataQueryMetadataImpl businessDataQueryMetadata) { this.jsonResults = jsonResults; this.businessDataQueryMetadata = businessDataQueryMetadata; } public Serializable getJsonResults() { return jsonResults; } public BusinessDataQueryMetadataImpl getBusinessDataQueryMetadata() { return businessDataQueryMetadata; } @Override public long getCount() { return 0; } @Override public List getResult() { return null; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/category/Category.java ================================================ /** * 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.bpm.category; import java.util.Date; import org.bonitasoft.engine.bpm.BonitaObject; /** * Category forms part of the ProcessDefinition. * * @author Yanyan Liu * @author Matthieu Chaffotte * @author Celine Souchet */ public interface Category extends BonitaObject { /** * @return The identifier of the category */ long getId(); /** * @return The name of the category */ String getName(); /** * @return The description of the category */ String getDescription(); /** * @return The identifier of the user that created the category */ long getCreator(); /** * @return The date of creation of the category */ Date getCreationDate(); /** * @return The last date when the category is updated */ Date getLastUpdate(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/category/CategoryCriterion.java ================================================ /** * 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.bpm.category; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.Sort; /** * Criterion to sort categories * * @author Yanyan Liu * @author Matthieu Chaffotte * @author Celine Souchet */ public enum CategoryCriterion { /** * By ascending name */ NAME_ASC("name", Order.ASC), /** * By descending name */ NAME_DESC("name", Order.DESC); private final String field; private final Order order; CategoryCriterion(final String field, final Order order) { this.field = field; this.order = order; } public Order getOrder() { return order; } public String getField() { return field; } public Sort getSort() { return new Sort(order, field); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/category/CategoryNotFoundException.java ================================================ /** * 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.bpm.category; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when it's not possible to find a category. * The class CategoryNotFoundException is a form of Throwable that indicates conditions that a reasonable application * might want to catch. * The class CategoryNotFoundException that is not also subclasses of {@link RuntimeException} are checked exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Yanyan Liu * @author Matthieu Chaffotte * @author Celine Souchet */ public class CategoryNotFoundException extends NotFoundException { private static final long serialVersionUID = 7030553624142851456L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public CategoryNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/category/CategoryUpdater.java ================================================ /** * 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.bpm.category; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class CategoryUpdater implements Serializable { private static final long serialVersionUID = 8964190598176457557L; /** * The fields that can be updated. */ public enum CategoryField { /** * The name of the field corresponding to the name of the category */ NAME, /** * The name of the field corresponding to the description of the category */ DESCRIPTION; } private final Map fields; /** * The default constructor */ public CategoryUpdater() { fields = new HashMap(5); } /** * Set the new name * * @param name * The new name * @return The CategoryUpdater containing the new name */ public CategoryUpdater setName(final String name) { fields.put(CategoryField.NAME, name); return this; } /** * Set the new description * * @param description * The new description * @return The CategoryUpdater containing the new description */ public CategoryUpdater setDescription(final String description) { fields.put(CategoryField.DESCRIPTION, description); return this; } /** * Get the fields to update, and the new value * * @return The map containing the pairs (field, new value) to update. */ public Map getFields() { return fields; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/category/impl/CategoryImpl.java ================================================ /** * 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.bpm.category.impl; import java.util.Date; import org.bonitasoft.engine.bpm.category.Category; /** * @author Yanyan Liu * @author Matthieu Chaffotte * @author Celine Souchet */ public class CategoryImpl implements Category { private static final long serialVersionUID = 5609899843000875034L; private final long id; private final String name; private String description; private long creator; private Date creationDate; private Date lastUpdate; public CategoryImpl(final long id, final String name) { super(); this.id = id; this.name = name; } @Override public long getId() { return id; } @Override public String getName() { return name; } @Override public String getDescription() { return description; } @Override public long getCreator() { return creator; } @Override public Date getCreationDate() { return creationDate; } @Override public Date getLastUpdate() { return lastUpdate; } public void setDescription(final String description) { this.description = description; } public void setCreator(final long creator) { this.creator = creator; } public void setCreationDate(final Date creationDate) { this.creationDate = creationDate; } public void setLastUpdate(final Date lastUpdate) { this.lastUpdate = lastUpdate; } @Override public String toString() { return "CategoryImpl [id=" + id + ", name=" + name + ", description=" + description + ", creator=" + creator + ", creationDate=" + creationDate + ", lastUpdate=" + lastUpdate + "]"; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/category/package-info.java ================================================ /** * Copyright (C) 2015 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. **/ /** *

* This package contains everything concerning Category : Criterion, Exceptions, Updater... *

*/ package org.bonitasoft.engine.bpm.category; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/comment/ArchivedComment.java ================================================ /** * 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.bpm.comment; import java.util.Date; import org.bonitasoft.engine.bpm.ArchivedElement; import org.bonitasoft.engine.bpm.BaseElement; import org.bonitasoft.engine.bpm.NamedElement; /** * The archived comment associated to a process instance * * @author Zhang Bole * @author Matthieu Chaffotte * @author Celine Souchet */ public interface ArchivedComment extends NamedElement, BaseElement, ArchivedElement { /** * @return The identifier of the user that posted the comment */ Long getUserId(); /** * @return The identifier of the process instance associated to the comment */ long getProcessInstanceId(); /** * @return The date to which the comment was posted. */ Date getPostDate(); /** * @return The content of the comment */ String getContent(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/comment/ArchivedCommentsSearchDescriptor.java ================================================ /** * 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.bpm.comment; /** * The fields on which a search can be made for the archived comments. * * @author Hongwen Zang * @author Celine Souchet */ public class ArchivedCommentsSearchDescriptor { /** * The name of the field corresponding to the identifier of the process instance associated to the comment */ public static final String PROCESS_INSTANCE_ID = "processInstanceId"; /** * The name of the field corresponding to the identifier of the user that posted the comment */ public static final String POSTED_BY_ID = "userId"; /** * The name of the field corresponding to the identifier of the archived comment */ public static final String ID = "id"; /** * The name of the field corresponding to the username of the user that posted the comment */ public static final String USER_NAME = "userName"; /** * The name of the field corresponding to the content of the comment */ public static final String CONTENT = "content"; /** * The name of the field corresponding to the date to which the comment was posted. */ public static final String POSTDATE = "postdate"; /** * The name of the field corresponding to the date to which the comment was archived. */ public static final String ARCHIVE_DATE = "archiveDate"; /** * The name of the field corresponding to the identifier of the comment (not archived) */ public static final String SOURCE_OBJECT_ID = "sourceObjectId"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/comment/Comment.java ================================================ /** * 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.bpm.comment; import org.bonitasoft.engine.bpm.BonitaObject; /** * The comment associated to a process instance * * @author Hongwen Zang * @author Celine Souchet */ public interface Comment extends BonitaObject { /** * @return The identifier of the comment */ long getId(); /** * @return The identifier of the user that posted the comment */ Long getUserId(); /** * @return The identifier of the process instance associated to the comment */ long getProcessInstanceId(); /** * @return The date to which the comment was posted. */ long getPostDate(); /** * @return The content of the comment */ String getContent(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/comment/SearchCommentsDescriptor.java ================================================ /** * 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.bpm.comment; /** * The fields on which a search can be made for the comments. * * @author Hongwen Zang * @author Emmanuel Duchastenier * @author Celine Souchet */ public class SearchCommentsDescriptor { /** * The name of the field corresponding to the identifier of the process instance associated to the comment */ public static final String PROCESS_INSTANCE_ID = "processInstanceId"; /** * The name of the field corresponding to the identifier of the user that posted the comment */ public static final String POSTED_BY_ID = "userId"; /** * The name of the field corresponding to the identifier of the comment */ public static final String ID = "id"; /** * The name of the field corresponding to the username of the user that posted the comment */ public static final String USER_NAME = "userName"; /** * The name of the field corresponding to the content of the comment */ public static final String CONTENT = "content"; /** * The name of the field corresponding to the date to which the comment was posted. */ public static final String POSTDATE = "postdate"; /** * The name of the field corresponding to the kind of the comment */ public static final String KIND = "kind"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/comment/impl/ArchivedCommentImpl.java ================================================ /** * 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.bpm.comment.impl; import java.util.Date; import org.bonitasoft.engine.bpm.comment.ArchivedComment; import org.bonitasoft.engine.bpm.internal.NamedElementImpl; /** * @author Zhang Bole */ public class ArchivedCommentImpl extends NamedElementImpl implements ArchivedComment { private static final long serialVersionUID = -6573747806944970703L; private long id; private Date archiveDate; private long processInstanceId; private long sourceObjectId; private Long userId; private Date postDate; private String content; public ArchivedCommentImpl(final String name) { super(name); } @Override public long getId() { return id; } @Override public void setId(final long id) { this.id = id; } @Override public Date getArchiveDate() { return archiveDate; } public void setArchiveDate(final Date archiveDate) { this.archiveDate = archiveDate; } @Override public long getProcessInstanceId() { return processInstanceId; } public void setProcessInstanceId(final long processInstanceId) { this.processInstanceId = processInstanceId; } @Override public long getSourceObjectId() { return sourceObjectId; } public void setSourceObjectId(final long sourceObjectId) { this.sourceObjectId = sourceObjectId; } @Override public Long getUserId() { return userId; } public void setUserId(final Long userId) { this.userId = userId; } @Override public Date getPostDate() { return postDate; } public void setPostDate(final Date postDate) { this.postDate = postDate; } @Override public String getContent() { return content; } public void setContent(final String content) { this.content = content; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/comment/impl/CommentImpl.java ================================================ /** * 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.bpm.comment.impl; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.bpm.comment.Comment; /** * @author Hongwen Zang * @author Matthieu Chaffotte */ @Data @NoArgsConstructor public class CommentImpl implements Comment { private static final long serialVersionUID = 2599025748483260550L; private long id; private Long userId; private long processInstanceId; private long postDate; private String content; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/comment/package-info.java ================================================ /** * Copyright (C) 2015 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. **/ /** *

* This package contains everything concerning Comment : Archived, Search descriptor... *

*/ package org.bonitasoft.engine.bpm.comment; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ArchiveConnectorInstancesSearchDescriptor.java ================================================ /** * 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.bpm.connector; /** * The fields on which a search can be made for the archived connectors. * * @author Baptiste Mesta * @author Celine Souchet */ public final class ArchiveConnectorInstancesSearchDescriptor { /** * The name of the field corresponding to the name of the connector */ public static final String NAME = "name"; /** * The name of the field corresponding to the state of the connector */ public static final String STATE = "state"; /** * The name of the field corresponding to the event to activate the connector */ public static final String ACTIVATION_EVENT = "activationEvent"; /** * The name of the field corresponding to the type of the container of the connector */ public static final String CONTAINER_TYPE = "containerType"; /** * The name of the field corresponding to the identifier of the container of the connector */ public static final String CONTAINER_ID = "containerId"; /** * The name of the field corresponding to the identifier of the definition of the connector */ public static final String CONNECTOR_DEFINITION_ID = "connectorDefinitionId"; /** * The name of the field corresponding to the version of the definition of the connector */ public static final String CONNECTOR_DEFINITION_VERSION = "connectorDefinitionVersion"; /** * The name of the field corresponding to the date to which the connector was archived. */ public static final String ARCHIVE_DATE = "archiveDate"; /** * The name of the field corresponding to the identifier of the connector (not archived) */ public static final String SOURCE_OBJECT_ID = "sourceObjectId"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ArchivedConnectorInstance.java ================================================ /** * 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.bpm.connector; import org.bonitasoft.engine.bpm.ArchivedElement; import org.bonitasoft.engine.bpm.BaseElement; import org.bonitasoft.engine.bpm.NamedElement; /** * The archived connector * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public interface ArchivedConnectorInstance extends NamedElement, BaseElement, ArchivedElement { /** * Container type : FlowNode */ String FLOWNODE_TYPE = "flowNode"; /** * Container type : Process */ String PROCESS_TYPE = "process"; /** * @return The identifier of the container of the connector */ long getContainerId(); /** * @return The type of the container of the connector. The connector can be on a FlowNode or a Process. */ String getContainerType(); /** * @return The identifier of the container of the connector */ String getConnectorId(); /** * @return The version of the connector */ String getVersion(); /** * @return The event to activate the connector */ ConnectorEvent getActivationEvent(); /** * @return The state of the connector */ ConnectorState getState(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorCriterion.java ================================================ /** * 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.bpm.connector; /** * Criterion to sort connectors * * @author Yanyan Liu * @author Celine Souchet */ public enum ConnectorCriterion { /** * By ascending identifier of connector definition */ DEFINITION_ID_ASC, /** * By descending identifier of connector definition */ DEFINITION_ID_DESC, /** * By ascending version of connector definition */ DEFINITION_VERSION_ASC, /** * By descending version of connector definition */ DEFINITION_VERSION_DESC, /** * By ascending identifier of connector implementation */ IMPLEMENTATION_ID_ASC, /** * By descending identifier of connector implementation */ IMPLEMENTATION_ID_DESC, /** * By ascending version of connector implementation */ IMPLEMENTATION_VERSION_ASC, /** * By descending version of connector implementation */ IMPLEMENTATIONN_VERSION_DESC, /** * By ascending class name of connector implementation */ IMPLEMENTATIONN_CLASS_NAME_ACS, /** * By descending class name of connector implementation */ IMPLEMENTATIONN_CLASS_NAME_DESC, /** * By descending identifier of connector implementation */ DEFAULT } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorDefinitionWithInputValues.java ================================================ /** * 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.bpm.connector; import java.io.Serializable; import java.util.Map; /** * @author Baptiste Mesta */ public interface ConnectorDefinitionWithInputValues extends Serializable { /** * @return * the connector definition */ ConnectorDefinition getConnectorDefinition(); /** * @return * the input values to evaluate input expressions of the connector */ Map> getInputValues(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorExecutionException.java ================================================ /** * 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.bpm.connector; import org.bonitasoft.engine.exception.ExecutionException; /** * Thrown when it's not possible to execute correctly the connector. * The class ConnectorExecutionException is a form of Throwable that indicates conditions that a reasonable application * might want to catch. * The class ConnectorExecutionException that is not also subclasses of {@link RuntimeException} are checked exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class ConnectorExecutionException extends ExecutionException { private static final long serialVersionUID = -5951610392778908072L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ConnectorExecutionException(final Throwable cause) { super(cause); } /** * Constructs a new exception with the specified detail message and cause. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ConnectorExecutionException(final String message, final Throwable cause) { super(message, cause); } /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public ConnectorExecutionException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorImplementationDescriptor.java ================================================ /** * 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.bpm.connector; import java.io.Serializable; import java.util.List; /** * The fields on which a search can be made for the connector implementation. * * @author Yanyan Liu * @author Celine Souchet */ public class ConnectorImplementationDescriptor implements Serializable { private static final long serialVersionUID = -3988746732940581935L; /** * The name of the field corresponding to the class name of the implementation of the connector */ public static final String IMPLEMENTATION_CLASS_NAME = "implementationClassName"; /** * The name of the field corresponding to the identifier of the connector */ public static final String ID = "id"; /** * The name of the field corresponding to the version of the connector */ public static final String VERSIOIN = "version"; /** * The name of the field corresponding to the identifier of the definition of the connector */ public static final String DEFINITION_ID = "definitionId"; /** * The name of the field corresponding to the version of the definition of the connector */ public static final String DEFINITION_VERSION = "definitionVersion"; private String implementationClassName; private String id; private String version; private String definitionId; private String definitionVersion; private List jarDependencies; /** * The default constructor */ public ConnectorImplementationDescriptor() { super(); } /** * @param implementationClassName * The implementation of the connector * @param id * The identifier of the connector * @param version * The version of the connector * @param definitionId * The identifier of the definition of the connector * @param definitionVersion * The version of the definition of the connector * @param jarDependencies * The dependencies of the connector (path of the JAR files) */ public ConnectorImplementationDescriptor(final String implementationClassName, final String id, final String version, final String definitionId, final String definitionVersion, final List jarDependencies) { super(); this.implementationClassName = implementationClassName; this.id = id; this.version = version; this.definitionId = definitionId; this.definitionVersion = definitionVersion; this.jarDependencies = jarDependencies; } /** * @return The implementation of the connector */ public String getImplementationClassName() { return implementationClassName; } /** * @return * The identifier of the connector */ public String getId() { return id; } /** * @return The version of the connector */ public String getVersion() { return version; } /** * @return * The identifier of the definition of the connector */ public String getDefinitionId() { return definitionId; } /** * @return * The version of the definition of the connector */ public String getDefinitionVersion() { return definitionVersion; } /** * @return The list of the dependencies of the connector (path of the JAR files) */ public List getJarDependencies() { return jarDependencies; } @Override public String toString() { return "ConnectorImplementation [implementationClassName=" + implementationClassName + ", id=" + id + ", version=" + version + ", definitionId=" + definitionId + ", definitionVersion=" + definitionVersion + ", jarDependencies=" + jarDependencies + "]"; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorInstance.java ================================================ /** * 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.bpm.connector; import org.bonitasoft.engine.bpm.BaseElement; import org.bonitasoft.engine.bpm.NamedElement; /** * Represents a connector, once instanciated by the containinig activity or process at runtime. * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public interface ConnectorInstance extends NamedElement, BaseElement { /** * */ String FLOWNODE_TYPE = "flowNode"; /** * */ String PROCESS_TYPE = "process"; /** * @return The identifier of the containing element (process or activity) */ long getContainerId(); /** * @return The type of the connector container (PROCESS or ACTIVITY) */ String getContainerType(); /** * @return The identifier of the connector. */ String getConnectorId(); /** * @return the version of the connector. */ String getVersion(); /** * @return where this connector should be activated. * @see ConnectorEvent */ ConnectorEvent getActivationEvent(); /** * @return the state of the connector ({@link ConnectorState#TO_BE_EXECUTED} before the first execution) * @see ConnectorState */ ConnectorState getState(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorInstanceCriterion.java ================================================ /** * 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.bpm.connector; /** * @author Baptiste Mesta */ public enum ConnectorInstanceCriterion { NAME_ASC, NAME_DESC, CONTAINER_ID_ASC, CONTAINER_ID__DESC, CONNECTOR_ID_ASC, CONNECTOR_ID__DESC, VERSION_ASC, VERSION_DESC, ACTIVATION_EVENT_ASC, ACTIVATION_EVENT_DESC, STATE_ASC, STATE_DESC, DEFAULT } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorInstanceNotFoundException.java ================================================ /** * 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.bpm.connector; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when it's not possible to find the connector instance. * The class ConnectorInstanceNotFoundException is a form of Throwable that indicates conditions that a reasonable * application might want to catch. * The class ConnectorInstanceNotFoundException that is not also subclasses of {@link RuntimeException} are checked * exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class ConnectorInstanceNotFoundException extends NotFoundException { private static final long serialVersionUID = -2090512000101549113L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ConnectorInstanceNotFoundException(final Throwable cause) { super(cause); } /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public ConnectorInstanceNotFoundException(final String message) { super(message); } /** * Constructs a new exception with the specified detail message and cause. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ConnectorInstanceNotFoundException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorInstanceWithFailureInfo.java ================================================ /** * 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.bpm.connector; /** * @author Elias Ricken de Medeiros */ public interface ConnectorInstanceWithFailureInfo extends ConnectorInstance { String getExceptionMessage(); String getStackTrace(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorInstancesSearchDescriptor.java ================================================ /** * 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.bpm.connector; /** * @author Baptiste Mesta */ public final class ConnectorInstancesSearchDescriptor { public static final String NAME = "name"; // use ConnectorState..name() for the value when using STATE as filter: public static final String STATE = "state"; public static final String ACTIVATION_EVENT = "activationEvent"; public static final String CONTAINER_TYPE = "containerType"; public static final String CONTAINER_ID = "containerId"; public static final String CONNECTOR_DEFINITION_ID = "connectorDefinitionId"; public static final String CONNECTOR_DEFINITION_VERSION = "connectorDefinitionVersion"; public static final String EXECUTION_ORDER = "executionOrder"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorNotFoundException.java ================================================ /** * 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.bpm.connector; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when a referenced connector cannot be found (generally from its ID + Version). * The class ConnectorNotFoundException is a form of Throwable that indicates conditions that a reasonable application * might want to catch. * The class ConnectorNotFoundException that is not also subclasses of {@link RuntimeException} are checked exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Yanyan Liu * @author Emmanuel Duchastenier * @author Celine Souchet */ public class ConnectorNotFoundException extends NotFoundException { private static final long serialVersionUID = 5630437277994804677L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ConnectorNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorState.java ================================================ /** * 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.bpm.connector; /** * Current state of a connector instance. * * @author Baptiste Mesta */ public enum ConnectorState { TO_BE_EXECUTED, EXECUTING, TO_RE_EXECUTE, DONE, FAILED, SKIPPED } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/ConnectorStateReset.java ================================================ /** * 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.bpm.connector; /** * @author Baptiste Mesta */ public enum ConnectorStateReset { TO_RE_EXECUTE, SKIPPED } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/InvalidConnectorImplementationException.java ================================================ /** * 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.bpm.connector; import org.bonitasoft.engine.exception.BonitaException; /** * Thrown when the connector implementation is invalid. * The class InvalidConnectorImplementationException is a form of Throwable that indicates conditions that a reasonable * application might want to catch. * The class InvalidConnectorImplementationException that is not also subclasses of {@link RuntimeException} are checked * exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class InvalidConnectorImplementationException extends BonitaException { private static final long serialVersionUID = -3389338645646919222L; /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public InvalidConnectorImplementationException(final String message) { super(message); } /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public InvalidConnectorImplementationException(final Throwable cause) { super(cause); } /** * Constructs a new exception with the specified detail message and cause. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public InvalidConnectorImplementationException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/InvalidEvaluationConnectorConditionException.java ================================================ /** * 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.bpm.connector; import org.bonitasoft.engine.exception.BonitaException; /** * Thrown when the evaluation of the condition of the connector instance is invalid. * The class InvalidEvaluationConnectorConditionException is a form of Throwable that indicates conditions that a * reasonable application might want to catch. * The class InvalidEvaluationConnectorConditionException that is not also subclasses of {@link RuntimeException} are * checked exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Zhao Na * @author Matthieu Chaffotte * @author Celine Souchet */ public class InvalidEvaluationConnectorConditionException extends BonitaException { private static final long serialVersionUID = -7035298849808114112L; /** * Constructs a new exception with the two conditions to compare * * @param condition1 * The first condition * @param condition2 * The second condition */ public InvalidEvaluationConnectorConditionException(final int condition1, final int condition2) { super(condition1 + " is not equal to " + condition2 + " ."); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/impl/ArchivedConnectorInstanceImpl.java ================================================ /** * 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.bpm.connector.impl; import java.util.Date; import org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.connector.ConnectorState; import org.bonitasoft.engine.bpm.internal.NamedElementImpl; /** * @author Baptiste Mesta */ public class ArchivedConnectorInstanceImpl extends NamedElementImpl implements ArchivedConnectorInstance { private static final long serialVersionUID = 1740487116886845229L; private final Date archiveDate; private final long containerId; private final String containerType; private final String connectorId; private final String version; private final ConnectorEvent activationEvent; private final ConnectorState state; private final long sourceObjectId; public ArchivedConnectorInstanceImpl(final String name, final Date archiveDate, final long containerId, final String containerType, final String connectorId, final String version, final ConnectorEvent activationEvent, final ConnectorState state, final long sourceObjectId) { super(name); this.archiveDate = archiveDate; this.containerId = containerId; this.containerType = containerType; this.connectorId = connectorId; this.version = version; this.activationEvent = activationEvent; this.state = state; this.sourceObjectId = sourceObjectId; } /** * @return the archiveDate */ @Override public Date getArchiveDate() { return archiveDate; } /** * @return the containerId */ @Override public long getContainerId() { return containerId; } /** * @return the containerType */ @Override public String getContainerType() { return containerType; } /** * @return the connectorId */ @Override public String getConnectorId() { return connectorId; } /** * @return the version */ @Override public String getVersion() { return version; } /** * @return the activationEvent */ @Override public ConnectorEvent getActivationEvent() { return activationEvent; } /** * @return the state */ @Override public ConnectorState getState() { return state; } @Override public long getSourceObjectId() { return sourceObjectId; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + (activationEvent == null ? 0 : activationEvent.hashCode()); result = prime * result + (archiveDate == null ? 0 : archiveDate.hashCode()); result = prime * result + (connectorId == null ? 0 : connectorId.hashCode()); result = prime * result + (int) (containerId ^ containerId >>> 32); result = prime * result + (containerType == null ? 0 : containerType.hashCode()); result = prime * result + (int) (sourceObjectId ^ sourceObjectId >>> 32); result = prime * result + (state == null ? 0 : state.hashCode()); result = prime * result + (version == null ? 0 : version.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } final ArchivedConnectorInstanceImpl other = (ArchivedConnectorInstanceImpl) obj; if (activationEvent != other.activationEvent) { return false; } if (archiveDate == null) { if (other.archiveDate != null) { return false; } } else if (!archiveDate.equals(other.archiveDate)) { return false; } if (connectorId == null) { if (other.connectorId != null) { return false; } } else if (!connectorId.equals(other.connectorId)) { return false; } if (containerId != other.containerId) { return false; } if (containerType == null) { if (other.containerType != null) { return false; } } else if (!containerType.equals(other.containerType)) { return false; } if (sourceObjectId != other.sourceObjectId) { return false; } if (state != other.state) { return false; } if (version == null) { if (other.version != null) { return false; } } else if (!version.equals(other.version)) { return false; } return true; } @Override public String toString() { return "ArchivedConnectorInstance [archiveDate=" + archiveDate + ", containerId=" + containerId + ", containerType=" + containerType + ", connectorId=" + connectorId + ", version=" + version + ", activationEvent=" + activationEvent + ", state=" + state + ", sourceObjectId=" + sourceObjectId + ", name=" + getName() + "]"; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/impl/ConnectorDefinitionWithInputValuesImpl.java ================================================ /** * 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.bpm.connector.impl; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.bpm.connector.ConnectorDefinition; import org.bonitasoft.engine.bpm.connector.ConnectorDefinitionWithInputValues; /** * @author Baptiste Mesta */ public class ConnectorDefinitionWithInputValuesImpl implements ConnectorDefinitionWithInputValues { private static final long serialVersionUID = -8750501172227766274L; private final ConnectorDefinition connectorDefinition; private final Map> inputValues; public ConnectorDefinitionWithInputValuesImpl(final ConnectorDefinition connectorDefinition, final Map> inputValues) { super(); this.connectorDefinition = connectorDefinition; this.inputValues = inputValues; } @Override public ConnectorDefinition getConnectorDefinition() { return connectorDefinition; } @Override public Map> getInputValues() { return inputValues; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/impl/ConnectorInstanceImpl.java ================================================ /** * 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.bpm.connector.impl; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.connector.ConnectorInstance; import org.bonitasoft.engine.bpm.connector.ConnectorState; import org.bonitasoft.engine.bpm.internal.NamedElementImpl; /** * @author Baptiste Mesta */ public class ConnectorInstanceImpl extends NamedElementImpl implements ConnectorInstance { private static final long serialVersionUID = 2148709030350403891L; private final long containerId; private final String containerType; private final String connectorId; private final String version; private final ConnectorState state; private final ConnectorEvent activationEvent; public ConnectorInstanceImpl(final String name, final long containerId, final String containerType, final String connectorId, final String version, final ConnectorState state, final ConnectorEvent activationEvent) { super(name); this.containerId = containerId; this.containerType = containerType; this.connectorId = connectorId; this.version = version; this.state = state; this.activationEvent = activationEvent; } @Override public long getContainerId() { return containerId; } @Override public String getContainerType() { return containerType; } @Override public String getConnectorId() { return connectorId; } @Override public String getVersion() { return version; } @Override public ConnectorEvent getActivationEvent() { return activationEvent; } @Override public ConnectorState getState() { return state; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/connector/impl/ConnectorInstanceWithFailureInfoImpl.java ================================================ /** * 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.bpm.connector.impl; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.connector.ConnectorInstanceWithFailureInfo; import org.bonitasoft.engine.bpm.connector.ConnectorState; /** * @author Elias Ricken de Medeiros */ public class ConnectorInstanceWithFailureInfoImpl extends ConnectorInstanceImpl implements ConnectorInstanceWithFailureInfo { private static final long serialVersionUID = 7158777025106286625L; public ConnectorInstanceWithFailureInfoImpl(final String name, final long containerId, final String containerType, final String connectorId, String version, ConnectorState state, final ConnectorEvent activationEvent, final String exceptionMessage, final String stackTrace) { super(name, containerId, containerType, connectorId, version, state, activationEvent); this.exceptionMessage = exceptionMessage; this.stackTrace = stackTrace; } private final String exceptionMessage; private final String stackTrace; @Override public String getExceptionMessage() { return exceptionMessage; } @Override public String getStackTrace() { return stackTrace; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/contract/ContractViolationException.java ================================================ /** * 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.bpm.contract; import java.util.List; import org.bonitasoft.engine.exception.BonitaException; /** * Thrown when the {@link ContractDefinition} is not fulfilled. * * @author Matthieu Chaffotte * @since 7.0 */ public class ContractViolationException extends BonitaException { private static final long serialVersionUID = -5733414795158022044L; private final List explanations; private final String simpleMessage; /** * Constructs an ContractViolationException with the specified detail message and the explanations. * * @param message the specified detail message * @param explanations the explanations */ public ContractViolationException(final String simpleMessage, final String message, final List explanations, final Throwable e) { super(message, e); this.simpleMessage = simpleMessage; this.explanations = explanations; } /** * Returns the explanations of why the contract is not fulfilled. * * @return the explanations */ public List getExplanations() { return explanations; } public String getSimpleMessage() { return simpleMessage; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/ArchivedDataInstance.java ================================================ /** * 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.bpm.data; import org.bonitasoft.engine.bpm.ArchivedElement; /** * Represents an archived {@link DataInstance}. * * @author Celine Souchet * @version 6.4.1 * @since 6.0.0 */ public interface ArchivedDataInstance extends DataInstance, ArchivedElement { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/ArchivedDataNotFoundException.java ================================================ /** * 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.bpm.data; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when it's not possible to find the archived data instance. * * @author Matthieu Chaffotte * @author Celine Souchet * @since 6.0.0 * @version 6.4.1 */ public class ArchivedDataNotFoundException extends NotFoundException { private static final long serialVersionUID = -1668767134016412650L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ArchivedDataNotFoundException(final Throwable cause) { super(cause); } public ArchivedDataNotFoundException(String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/DataInstance.java ================================================ /** * 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.bpm.data; import java.io.Serializable; import org.bonitasoft.engine.bpm.BaseElement; import org.bonitasoft.engine.bpm.NamedElement; /** * This object represents this instance of {@link DataDefinition} generated when the associated * {@link org.bonitasoft.engine.bpm.process.ProcessInstance} or * {@link org.bonitasoft.engine.bpm.flownode.FlowNodeInstance} is instantiated. * * @author Feng Hui * @author Celine Souchet * @since 6.0.0 * @version 6.4.1 */ public interface DataInstance extends NamedElement, BaseElement { /** * Get the description of the data defined in {@link DataDefinition}. * * @return The description of the data. * @since 6.0.0 * @see DataDefinition#getDescription() */ String getDescription(); /** * Get the class name of the type of the data defined in {@link DataDefinition}. * * @return The class name of the type of the data. * @since 6.0.0 * @see DataDefinition#getClassName() */ String getClassName(); /** * Is it transient? * * @return true if the data is transient, false otherwise. * @since 6.0.0 * @see DataDefinition#isTransientData() */ Boolean isTransientData(); /** * Get the value of the data. * * @return The value of the data. * @since 6.0.0 */ Serializable getValue(); /** * Get the identifier of the element where the data is defined. The element can be a * {@link org.bonitasoft.engine.bpm.process.ProcessInstance} or a * {@link org.bonitasoft.engine.bpm.flownode.FlowNodeInstance}. * * @return The identifier of the container of the data. * @since 6.0.0 * @see org.bonitasoft.engine.bpm.process.ProcessInstance#getId() * @see org.bonitasoft.engine.bpm.flownode.FlowNodeInstance#getId() */ long getContainerId(); /** * Get the type of the element where the data is defined. * The list of value for this field is : *
    *
  • * PROCESS_INSTANCE *
  • *
  • * ACTIVITY_INSTANCE *
  • *
  • * MESSAGE_INSTANCE *
  • *
* * @return The type of the container of the data. * @since 6.0.0 */ String getContainerType(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/DataNotFoundException.java ================================================ /** * 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.bpm.data; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when it's not possible to find the data. * * @author Matthieu Chaffotte * @author Celine Souchet * @since 6.0.0 * @version 6.4.1 */ public class DataNotFoundException extends NotFoundException { private static final long serialVersionUID = 882339840751819686L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public DataNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/ArchivedDataInstanceImpl.java ================================================ /** * 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.bpm.data.impl; import java.io.Serializable; import java.util.Date; import org.bonitasoft.engine.bpm.data.ArchivedDataInstance; /** * @author Matthieu Chaffotte */ public class ArchivedDataInstanceImpl extends DataInstanceImpl implements ArchivedDataInstance { private static final long serialVersionUID = 2770043188433234604L; private Date archiveDate; private long sourceObjectId; private Serializable value; @Override public Date getArchiveDate() { return archiveDate; } public void setArchiveDate(final Date archiveDate) { this.archiveDate = archiveDate; } @Override public long getSourceObjectId() { return sourceObjectId; } public void setSourceObjectId(final long sourceObjectId) { this.sourceObjectId = sourceObjectId; } @Override public Serializable getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = value; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/BlobDataInstanceImpl.java ================================================ /** * 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.bpm.data.impl; import java.io.Serializable; import org.bonitasoft.engine.bpm.data.DataDefinition; /** * @author Feng Hui * @author Matthieu Chaffotte */ public class BlobDataInstanceImpl extends DataInstanceImpl { private static final long serialVersionUID = 4369510522836874048L; private Serializable value; public BlobDataInstanceImpl() { super(); } public BlobDataInstanceImpl(final DataDefinition dataDefinition) { super(dataDefinition); } public BlobDataInstanceImpl(final DataDefinition dataDefinition, final Serializable value) { super(dataDefinition); this.value = value; } @Override public Serializable getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = value; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/BooleanDataInstanceImpl.java ================================================ /** * 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.bpm.data.impl; import java.io.Serializable; import org.bonitasoft.engine.bpm.data.DataDefinition; /** * @author Feng Hui * @author Matthieu Chaffotte */ public class BooleanDataInstanceImpl extends DataInstanceImpl { private static final long serialVersionUID = 4369510522836874048L; private Boolean value; public BooleanDataInstanceImpl() { super(); } public BooleanDataInstanceImpl(final DataDefinition dataDefinition) { super(dataDefinition); } public BooleanDataInstanceImpl(final DataDefinition dataDefinition, final Boolean value) { super(dataDefinition); this.value = value; } @Override public void setValue(final Serializable value) { this.value = (Boolean) value; } @Override public Boolean getValue() { return value; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/DataInstanceImpl.java ================================================ /** * 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.bpm.data.impl; import java.io.Serial; import java.io.Serializable; import lombok.Setter; import org.bonitasoft.engine.bpm.data.DataDefinition; import org.bonitasoft.engine.bpm.data.DataInstance; /** * @author Feng Hui * @author Matthieu Chaffotte */ @Setter public abstract class DataInstanceImpl implements DataInstance { @Serial private static final long serialVersionUID = -3752347909196691889L; private long id; private String name; private String description; private boolean transientData; private String className; private long containerId; private String containerType; public DataInstanceImpl() { super(); } public DataInstanceImpl(final DataDefinition dataDefinition) { name = dataDefinition.getName(); description = dataDefinition.getDescription(); transientData = dataDefinition.isTransientData(); className = dataDefinition.getClassName(); } public void setDataTypeClassName(final String className) { this.className = className; } @Override public long getId() { return id; } @Override public String getName() { return name; } @Override public String getDescription() { return description; } @Override public abstract Serializable getValue(); public abstract void setValue(Serializable value); @Override public Boolean isTransientData() { return transientData; } @Override public String getClassName() { return className; } @Override public long getContainerId() { return containerId; } @Override public String getContainerType() { return containerType; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/DateDataInstanceImpl.java ================================================ /** * 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.bpm.data.impl; import java.io.Serializable; import java.util.Date; import org.bonitasoft.engine.bpm.data.DataDefinition; /** * @author Feng Hui * @author Matthieu Chaffotte */ public class DateDataInstanceImpl extends DataInstanceImpl { private static final long serialVersionUID = 4369510522836874048L; private Date value; public DateDataInstanceImpl() { super(); } public DateDataInstanceImpl(final DataDefinition dataDefinition) { super(dataDefinition); } public DateDataInstanceImpl(final DataDefinition dataDefinition, final Date value) { super(dataDefinition); this.value = value; } @Override public Date getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = (Date) value; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/DoubleDataInstanceImpl.java ================================================ /** * 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.bpm.data.impl; import java.io.Serializable; import org.bonitasoft.engine.bpm.data.DataDefinition; /** * @author Feng Hui * @author Matthieu Chaffotte */ public class DoubleDataInstanceImpl extends DataInstanceImpl { private static final long serialVersionUID = 4369510522836874048L; private Double value; public DoubleDataInstanceImpl() { super(); } public DoubleDataInstanceImpl(final DataDefinition dataDefinition) { super(dataDefinition); } public DoubleDataInstanceImpl(final DataDefinition dataDefinition, final Double value) { super(dataDefinition); this.value = value; } @Override public void setValue(final Serializable value) { this.value = (Double) value; } @Override public Double getValue() { return value; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/FloatDataInstanceImpl.java ================================================ /** * 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.bpm.data.impl; import java.io.Serializable; import org.bonitasoft.engine.bpm.data.DataDefinition; /** * @author Celine Souchet * @author Matthieu Chaffotte */ public class FloatDataInstanceImpl extends DataInstanceImpl { private static final long serialVersionUID = 4369510522836874048L; private Float value; public FloatDataInstanceImpl() { super(); } public FloatDataInstanceImpl(final DataDefinition dataDefinition) { super(dataDefinition); } public FloatDataInstanceImpl(final DataDefinition dataDefinition, final Float value) { super(dataDefinition); this.value = value; } @Override public void setValue(final Serializable value) { this.value = (Float) value; } @Override public Float getValue() { return value; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/IntegerDataInstanceImpl.java ================================================ /** * 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.bpm.data.impl; import java.io.Serializable; import org.bonitasoft.engine.bpm.data.DataDefinition; /** * @author Feng Hui * @author Matthieu Chaffotte */ public class IntegerDataInstanceImpl extends DataInstanceImpl { private static final long serialVersionUID = 4369510522836874048L; private Integer value; public IntegerDataInstanceImpl() { super(); } public IntegerDataInstanceImpl(final DataDefinition dataDefinition) { super(dataDefinition); } public IntegerDataInstanceImpl(final DataDefinition dataDefinition, final Integer value) { super(dataDefinition); this.value = value; } @Override public Integer getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = (Integer) value; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/LongDataInstanceImpl.java ================================================ /** * 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.bpm.data.impl; import java.io.Serializable; import org.bonitasoft.engine.bpm.data.DataDefinition; /** * @author Feng Hui * @author Matthieu Chaffotte */ public class LongDataInstanceImpl extends DataInstanceImpl { private static final long serialVersionUID = 4369510522836874048L; private Long value; public LongDataInstanceImpl() { super(); } public LongDataInstanceImpl(final DataDefinition dataDefinition) { super(dataDefinition); } public LongDataInstanceImpl(final DataDefinition dataDefinition, final Long value) { super(dataDefinition); this.value = value; } @Override public Long getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = (Long) value; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/LongTextDataInstanceImpl.java ================================================ /** * 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.bpm.data.impl; import java.io.Serializable; import org.bonitasoft.engine.bpm.data.DataDefinition; /** * @author Feng Hui * @author Matthieu Chaffotte */ public class LongTextDataInstanceImpl extends ShortTextDataInstanceImpl { private static final long serialVersionUID = -3072085875249177140L; public LongTextDataInstanceImpl() { super(); } public LongTextDataInstanceImpl(final DataDefinition dataDefinition) { super(dataDefinition); } public LongTextDataInstanceImpl(final DataDefinition dataDefinition, final Serializable value) { super(dataDefinition, (String) value); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/data/impl/ShortTextDataInstanceImpl.java ================================================ /** * 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.bpm.data.impl; import java.io.Serializable; import org.bonitasoft.engine.bpm.data.DataDefinition; /** * @author Feng Hui * @author Matthieu Chaffotte */ public class ShortTextDataInstanceImpl extends DataInstanceImpl { private static final long serialVersionUID = 919336967044796183L; private String value; public ShortTextDataInstanceImpl() { super(); } public ShortTextDataInstanceImpl(final DataDefinition dataDefinition) { super(dataDefinition); } public ShortTextDataInstanceImpl(final DataDefinition dataDefinition, final String value) { super(dataDefinition); this.value = value; } @Override public Serializable getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = (String) value; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/ArchivedDocument.java ================================================ /** * 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.bpm.document; import org.bonitasoft.engine.bpm.ArchivedElement; /** * @author Zhang Bole * @author Matthieu Chaffotte */ public interface ArchivedDocument extends ArchivedElement, Document { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/ArchivedDocumentNotFoundException.java ================================================ /** * 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.bpm.document; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when it's not possible to find the archived document. * The class ArchivedDocumentNotFoundException is a form of Throwable that indicates conditions that a reasonable * application might want to catch. * The class ArchivedDocumentNotFoundException that is not also subclasses of {@link RuntimeException} are checked * exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Zhang Bole * @author Matthieu Chaffotte * @author Celine Souchet */ public class ArchivedDocumentNotFoundException extends NotFoundException { private static final long serialVersionUID = 2913387213088474673L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ArchivedDocumentNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/ArchivedDocumentsSearchDescriptor.java ================================================ /** * 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.bpm.document; /** * @author Zhang Bole */ public final class ArchivedDocumentsSearchDescriptor { public static final String PROCESSINSTANCE_ID = "processinstanceid"; public static final String SOURCEOBJECT_ID = "sourceObjectId"; public static final String ARCHIVE_DATE = "archiveDate"; public static final String DOCUMENT_NAME = "documentName"; public static final String DOCUMENT_DESCRIPTION = "documentDescription"; public static final String DOCUMENT_VERSION = "version"; public static final String DOCUMENT_AUTHOR = "documentAuthor"; public static final String DOCUMENT_CREATIONDATE = "documentCreationDate"; public static final String DOCUMENT_HAS_CONTENT = "documentHasContent"; public static final String DOCUMENT_CONTENT_FILENAME = "documentContentFileName"; public static final String DOCUMENT_CONTENT_MIMETYPE = "documentContentMimeType"; public static final String CONTENT_STORAGE_ID = "contentStorageId"; public static final String DOCUMENT_URL = "documentURL"; public static final String LIST_INDEX = "index"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/DocumentAttachmentException.java ================================================ /** * 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.bpm.document; import org.bonitasoft.engine.exception.ExecutionException; /** * Thrown when it's not possible to attach a document to a process. * The class DocumentAttachmentException is a form of Throwable that indicates conditions that a reasonable application * might want to catch. * The class DocumentAttachmentException that is not also subclasses of {@link RuntimeException} are checked exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Nicolas Chabanoles * @author Celine Souchet */ public class DocumentAttachmentException extends ExecutionException { private static final long serialVersionUID = -3376881143165731702L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public DocumentAttachmentException(final Throwable cause) { super(cause); } public DocumentAttachmentException(String s) { super(s); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/DocumentCriterion.java ================================================ /** * 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.bpm.document; /** * @author Baptiste Mesta */ public enum DocumentCriterion { NAME_ASC, NAME_DESC, AUTHOR_ASC, AUTHOR_DESC, CREATION_DATE_ASC, CREATION_DATE_DESC, FILENAME_ASC, FILENAME_DESC, MIMETYPE_ASC, MIMETYPE_DESC, URL_ASC, URL_DESC, DEFAULT; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/DocumentException.java ================================================ /** * 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.bpm.document; import org.bonitasoft.engine.exception.BonitaException; /** * Thrown when there is an error on the document. * The class DocumentException is a form of Throwable that indicates conditions that a reasonable application might want * to catch. * The class DocumentException that is not also subclasses of {@link RuntimeException} are checked exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Emmanuel Duchastenier * @author Celine Souchet */ public class DocumentException extends BonitaException { private static final long serialVersionUID = 146457960113640054L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public DocumentException(final Throwable cause) { super(cause); } /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public DocumentException(final String message) { super(message); } /** * Constructs a new exception with the specified detail message and cause. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public DocumentException(final String message, final Exception cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/DocumentNotFoundException.java ================================================ /** * 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.bpm.document; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when it's not possible to find the document. * The class DocumentNotFoundException is a form of Throwable that indicates conditions that a reasonable application * might want to catch. * The class DocumentNotFoundException that is not also subclasses of {@link RuntimeException} are checked exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Emmanuel Duchastenier * @author Celine Souchet */ public class DocumentNotFoundException extends NotFoundException { private static final long serialVersionUID = -1501286386852952410L; /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public DocumentNotFoundException(String message) { super(message); } /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public DocumentNotFoundException(final Throwable cause) { super(cause); } /** * Constructs a new exception with the specified detail message and cause. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public DocumentNotFoundException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/DocumentValue.java ================================================ /** * 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.bpm.document; import java.io.Serializable; import java.util.Objects; /** * Class that holds a content + mime type and a name OR the url if it's an external document. * It is used by DOCUMENT_CREATE_UPDATE operations to create or update a document stored in bonita * * @author Baptiste Mesta */ public class DocumentValue implements Serializable { private static final long serialVersionUID = -637083535741620642L; private byte[] content; private String mimeType; private String fileName; private String url; private boolean hasContent; private Long documentId; private boolean hasChanged; private int index = -1; /** * Represent the value of a document. Content, mime type and file name are given * * @param content * content of the document * @param mimeType * mime type of the document * @param fileName * file name of the document * @throws IllegalArgumentException * if the content or the fileName is null or empty */ public DocumentValue(final byte[] content, final String mimeType, final String fileName) { super(); this.content = content; this.mimeType = mimeType; this.fileName = fileName; hasContent = true; hasChanged = false; documentId = null; checkIfTheFileNameIsFilledForADocumentWithContent(); } /** * Represent the value of an external document, only the url is given * * @param url * url of the document */ public DocumentValue(final String url) { super(); this.url = url; hasContent = false; hasChanged = false; documentId = null; } /** * Represent an existing document that did not changed. * It is used only in operations to update document list. * * @param documentId * the id of the existing document (mapping) */ public DocumentValue(final long documentId) { super(); hasChanged = false; this.documentId = documentId; } /** * Represent an existing document that changed with the content and metadata in parameters. * It is used only in operations to update document list. * * @param documentId * the id of the existing document (mapping) * @param content * content of the document * @param mimeType * mime type of the document * @param fileName * file name of the document * @throws IllegalArgumentException * if the content or the fileName is null or empty */ public DocumentValue(final long documentId, final byte[] content, final String mimeType, final String fileName) { super(); hasContent = true; this.content = content; this.mimeType = mimeType; this.fileName = fileName; hasChanged = true; this.documentId = documentId; checkIfTheFileNameIsFilledForADocumentWithContent(); } /** * Represent an existing document that changed to an external document. * It is used only in operations to update document list. * * @param documentId * the id of the existing document (mapping) */ public DocumentValue(final long documentId, final String url) { super(); this.url = url; hasContent = false; hasChanged = true; this.documentId = documentId; } public byte[] getContent() { return content; } public String getMimeType() { return mimeType; } public String getFileName() { return fileName; } public String getUrl() { return url; } /** * @return true if the document to create is stored internally or externally with an URL */ public boolean hasContent() { return hasContent; } public void setContent(final byte[] content) { hasContent = true; this.content = content; checkIfTheFileNameIsFilledForADocumentWithContent(); } private void checkIfTheFileNameIsFilledForADocumentWithContent() { if (hasContent && (fileName == null || fileName.isEmpty())) { throw new IllegalArgumentException("The fileName field must be filled when the content is filled !!"); } } public void setMimeType(final String mimeType) { this.mimeType = mimeType; } public void setFileName(final String fileName) { this.fileName = fileName; checkIfTheFileNameIsFilledForADocumentWithContent(); } public void setUrl(final String url) { this.url = url; hasContent = false; } public void setHasContent(final boolean hasContent) { this.hasContent = hasContent; } /** * Indicate which document will be updated
* Used only when updating list of document * * @return the id of the document to update */ public Long getDocumentId() { return documentId; } public void setDocumentId(final Long documentId) { this.documentId = documentId; } /** * If the document value updates an existing document, this getter tels us if the content is modified and should be * updated * * @return true if the content of the original document has changed */ public boolean hasChanged() { return hasChanged; } public void setHasChanged(final boolean hasChanged) { this.hasChanged = hasChanged; } public int getIndex() { return index; } /** * Index of were to put the document inside the list. Only used when using API method attach document. * * @param index * index in the list */ public DocumentValue setIndex(final int index) { this.index = index; return this; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DocumentValue that = (DocumentValue) o; return Objects.equals(hasContent, that.hasContent) && Objects.equals(hasChanged, that.hasChanged) && Objects.equals(index, that.index) && Objects.equals(content, that.content) && Objects.equals(mimeType, that.mimeType) && Objects.equals(fileName, that.fileName) && Objects.equals(url, that.url) && Objects.equals(documentId, that.documentId); } @Override public int hashCode() { return Objects.hash(content, mimeType, fileName, url, hasContent, documentId, hasChanged, index); } @Override public String toString() { return "DocumentValue{" + "mimeType='" + mimeType + '\'' + ", fileName='" + fileName + '\'' + ", url='" + url + '\'' + ", hasContent=" + hasContent + ", documentId=" + documentId + ", hasChanged=" + hasChanged + ", index=" + index + '}'; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/DocumentsSearchDescriptor.java ================================================ /** * 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.bpm.document; /** * @author Zhang Bole */ public final class DocumentsSearchDescriptor { public static final String PROCESSINSTANCE_ID = "processinstanceid"; public static final String DOCUMENT_NAME = "documentName"; public static final String DOCUMENT_VERSION = "version"; public static final String DOCUMENT_DESCRIPTION = "documentDescription"; public static final String DOCUMENT_AUTHOR = "documentAuthor"; public static final String DOCUMENT_CREATIONDATE = "documentCreationDate"; public static final String DOCUMENT_HAS_CONTENT = "documentHasContent"; public static final String DOCUMENT_CONTENT_FILENAME = "documentContentFileName"; public static final String DOCUMENT_CONTENT_MIMETYPE = "documentContentMimeType"; public static final String CONTENT_STORAGE_ID = "contentStorageId"; public static final String DOCUMENT_URL = "documentURL"; public static final String LIST_INDEX = "index"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/document/impl/ArchivedDocumentImpl.java ================================================ /** * 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.bpm.document.impl; import java.util.Date; import org.bonitasoft.engine.bpm.document.ArchivedDocument; /** * @author Zhang Bole * @author Baptiste Mesta */ public class ArchivedDocumentImpl extends DocumentImpl implements ArchivedDocument { private static final long serialVersionUID = -6573747806944970704L; private Date archiveDate; private long sourceObjectId; public ArchivedDocumentImpl() { super(); } public ArchivedDocumentImpl(final String name) { super(); setName(name); } @Override public Date getArchiveDate() { return archiveDate; } @Override public long getSourceObjectId() { return sourceObjectId; } public void setArchiveDate(final Date archiveDate) { this.archiveDate = archiveDate; } public void setSourceObjectId(final long sourceObjectId) { this.sourceObjectId = sourceObjectId; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } if (!super.equals(o)) { return false; } final ArchivedDocumentImpl that = (ArchivedDocumentImpl) o; if (sourceObjectId != that.sourceObjectId) { return false; } if (archiveDate != null ? !archiveDate.equals(that.archiveDate) : that.archiveDate != null) { return false; } return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (archiveDate != null ? archiveDate.hashCode() : 0); result = 31 * result + (int) (sourceObjectId ^ sourceObjectId >>> 32); return result; } @Override public String toString() { return "ArchivedDocumentImpl{" + "archiveDate=" + archiveDate + ", sourceObjectId=" + sourceObjectId + super.toString() + "} "; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ActivityDefinitionNotFoundException.java ================================================ /** * 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.bpm.flownode; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when it's not possible to find the activity definition. * The class ActivityDefinitionNotFoundException is a form of Throwable that indicates conditions that a reasonable * application might want to catch. * The class ActivityDefinitionNotFoundException that is not also subclasses of {@link RuntimeException} are checked * exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Matthieu Chaffotte * @author Celine Souchet */ public class ActivityDefinitionNotFoundException extends NotFoundException { private static final long serialVersionUID = -5346930807820225783L; /** * Constructs a new exception with the specified detail message. * * @param activityName * The name of the searched activity definition */ public ActivityDefinitionNotFoundException(final String activityName) { super("Activity '" + activityName + "' not found"); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ActivityExecutionException.java ================================================ /** * 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.bpm.flownode; /** * Thrown when it's not possible to execute the activity. * The class ActivityExecutionException is a form of Throwable that indicates conditions that a reasonable application * might want to catch. * The class ActivityExecutionException that is not also subclasses of {@link RuntimeException} are checked exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class ActivityExecutionException extends FlowNodeExecutionException { private static final long serialVersionUID = -1112307466303034453L; /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public ActivityExecutionException(final String message) { super(message); } /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ActivityExecutionException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ActivityInstance.java ================================================ /** * 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.bpm.flownode; /** * Corresponding to a instance of {@link ActivityDefinition}. * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public interface ActivityInstance extends FlowNodeInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ActivityInstanceCriterion.java ================================================ /** * 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.bpm.flownode; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public enum ActivityInstanceCriterion { /** * Last update ascending order */ LAST_UPDATE_ASC, /** * Started date ascending order */ REACHED_STATE_DATE_ASC, /** * Expected End Date ascending order */ EXPECTED_END_DATE_ASC, /** * Name ascending order */ NAME_ASC, /** * Priority ascending order */ PRIORITY_ASC, /** * Last update descending order */ LAST_UPDATE_DESC, /** * Started date descending order */ REACHED_STATE_DATE_DESC, /** * Expected End Date descending order */ EXPECTED_END_DATE_DESC, /** * Name descending order */ NAME_DESC, /** * Priority descending order */ PRIORITY_DESC, /** * */ DEFAULT } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ActivityInstanceNotFoundException.java ================================================ /** * 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.bpm.flownode; import java.io.Serial; import org.bonitasoft.engine.exception.NotFoundException; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class ActivityInstanceNotFoundException extends NotFoundException { @Serial private static final long serialVersionUID = -5980531959067888526L; /** * Constructs a new exception with the specified detail message. * * @param activityInstanceId * The identifier of the searched activity displayed in the detail message (which is saved for later * retrieval by the * {@link Throwable#getMessage()} method). */ public ActivityInstanceNotFoundException(final long activityInstanceId) { super("activity with id " + activityInstanceId + " not found"); } /** * Constructs a new exception with the specified detail message and cause. * * @param activityInstanceId * The identifier of the searched activity displayed in the detail message (which is saved for later * retrieval by the * {@link Throwable#getMessage()} method). * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ActivityInstanceNotFoundException(final long activityInstanceId, final Exception cause) { super("activity with id " + activityInstanceId + " not found", cause); } /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ActivityInstanceNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ActivityInstanceSearchDescriptor.java ================================================ /** * 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.bpm.flownode; /** * @author Yanyan Liu */ public class ActivityInstanceSearchDescriptor { public static final String NAME = "name"; public static final String STATE_NAME = "state"; public static final String SUPERVISOR_ID = "supervisorId"; public static final String USER_ID = "userId"; public static final String GROUP_ID = "groupId"; public static final String ROLE_ID = "roleId"; public static final String PROCESS_DEFINITION_ID = "processDefinitionId"; public static final String PROCESS_INSTANCE_ID = "processInstanceId"; public static final String PARENT_PROCESS_INSTANCE_ID = "parentProcessInstanceId"; public static final String PARENT_ACTIVITY_INSTANCE_ID = "parentActivityInstanceId"; public static final String PARENT_CONTAINER_ID = "parentContainerId"; public static final String DISPLAY_NAME = "displayName"; /** * Use this field to match a specific type of flow node.
* Activity type filter can only be used once per query.
* Example of invalid query:
*
    *
  • * .leftParenthesis() * .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.USER_TASK) * .or() * .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.HUMAN_TASK) * .rightParenthesis() * .done(); *
  • *
*/ public static final String ACTIVITY_TYPE = "activityType"; public static final String LAST_MODIFICATION_DATE = "lastUpdateDate"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ActivityStates.java ================================================ /** * 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.bpm.flownode; import java.util.HashSet; import java.util.Set; /** * Temporary class to be substituted by ActivityStateService. * * @author Emmanuel Duchastenier * @author Celine Souchet */ public class ActivityStates { /** * The state when a activity failed. */ public static final String FAILED_STATE = "failed"; /** * The state when a activity is initializing. */ public static final String INITIALIZING_STATE = "initializing"; /** * The state when a activity is ready. */ public static final String READY_STATE = "ready"; /** * The state when a activity is executing. */ public static final String EXECUTING_STATE = "executing"; /** * The state when a activity is completing. */ public static final String COMPLETING_STATE = "completing"; /** * The state when a activity is completed. */ public static final String COMPLETED_STATE = "completed"; /** * The state when a activity is waiting. */ public static final String WAITING_STATE = "waiting"; /** * The state when a activity is skipped. */ public static final String SKIPPED_STATE = "skipped"; /** * The state when a activity is cancelled. */ public static final String CANCELLED_STATE = "cancelled"; /** * The state when a activity is aborted. */ public static final String ABORTED_STATE = "aborted"; /** * The state when the subtask of a activity are cancelling. * * @deprecated this should not be used anymore client-side. */ @Deprecated(since = "7.12.0") public static final String CANCELLING_SUBTASKS_STATE = "cancelling subtasks"; /** * The set of the states */ public static final Set STATES = new HashSet<>(); static { STATES.add(FAILED_STATE); STATES.add(INITIALIZING_STATE); STATES.add(READY_STATE); STATES.add(EXECUTING_STATE); STATES.add(COMPLETING_STATE); STATES.add(COMPLETED_STATE); STATES.add(WAITING_STATE); STATES.add(SKIPPED_STATE); STATES.add(CANCELLED_STATE); STATES.add(CANCELLING_SUBTASKS_STATE); STATES.add(ABORTED_STATE); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedActivityInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Baptiste Mesta * @author Matthieu Chaffote */ public interface ArchivedActivityInstance extends ArchivedFlowNodeInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedActivityInstanceNotFoundException.java ================================================ /** * 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.bpm.flownode; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when it's not possible to find the archived activity instance. * The class ArchivedActivityInstanceNotFoundException is a form of Throwable that indicates conditions that a * reasonable application might want to catch. * The class ArchivedActivityInstanceNotFoundException that is not also subclasses of {@link RuntimeException} are * checked exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Zhao Na * @author Matthieu Chaffotte * @author Celine Souchet */ public class ArchivedActivityInstanceNotFoundException extends NotFoundException { private static final long serialVersionUID = -2775691872771374110L; /** * Constructs a new exception with the specified detail message. * * @param activityInstanceId * The identifier of the archived activity instance include on the detail message (which is saved for later * retrieval by the * {@link Throwable#getMessage()} method). */ public ArchivedActivityInstanceNotFoundException(final long activityInstanceId) { super("Archive activity with id " + activityInstanceId + " not found"); } /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ArchivedActivityInstanceNotFoundException(final Exception cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedActivityInstanceSearchDescriptor.java ================================================ /** * 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.bpm.flownode; /** * @author Yanyan Liu * @author Elias Ricken de Medeiros */ public final class ArchivedActivityInstanceSearchDescriptor { public static final String NAME = "name"; public static final String PRIORITY = "priority"; public static final String STATE_NAME = "state"; public static final String SUPERVISOR_ID = "supervisorId"; public static final String ASSIGNEE_ID = "assigneeId"; public static final String PROCESS_DEFINITION_ID = "processDefinitionId"; public static final String ROOT_PROCESS_INSTANCE_ID = "rootProcessInstanceId"; public static final String PARENT_PROCESS_INSTANCE_ID = "parentProcessInstanceId"; public static final String PARENT_ACTIVITY_INSTANCE_ID = "parentActivityInstanceId"; public static final String DISPLAY_NAME = "displayName"; public static final String ACTIVITY_TYPE = "activityType"; public static final String REACHED_STATE_DATE = "reachedStateDate"; public static final String SOURCE_OBJECT_ID = "sourceObjectId"; public static final String ARCHIVE_DATE = "archiveDate"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedAutomaticTaskInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface ArchivedAutomaticTaskInstance extends ArchivedActivityInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedCallActivityInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface ArchivedCallActivityInstance extends ArchivedActivityInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedFlowElementInstance.java ================================================ /** * 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.bpm.flownode; import org.bonitasoft.engine.bpm.ArchivedElement; import org.bonitasoft.engine.bpm.BaseElement; import org.bonitasoft.engine.bpm.DescriptionElement; /** * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public interface ArchivedFlowElementInstance extends DescriptionElement, BaseElement, ArchivedElement { long getRootContainerId(); long getParentContainerId(); /** * Is this flow element instance currently aborting? * * @return true if this flow element instance is currently aborting, false otherwise * @since 6.0 */ boolean isAborting(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedFlowNodeInstance.java ================================================ /** * 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.bpm.flownode; import java.util.Date; import org.bonitasoft.engine.bpm.ArchivedElement; import org.bonitasoft.engine.bpm.BaseElement; import org.bonitasoft.engine.bpm.NamedElement; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public interface ArchivedFlowNodeInstance extends NamedElement, BaseElement, ArchivedElement { long getParentContainerId(); long getRootContainerId(); long getProcessDefinitionId(); long getProcessInstanceId(); /** * @return the parent activity instance id * @since 6.2 */ long getParentActivityInstanceId(); String getState(); FlowNodeType getType(); String getDisplayName(); String getDisplayDescription(); /** * @return The identifier of the user who executed the flow node * @since 6.0.1 */ long getExecutedBy(); /** * @return The identifier of the substitute user (as Process manager or Administrator) who executed the flow node. * @since 6.3.0 */ long getExecutedBySubstitute(); String getDescription(); long getFlownodeDefinitionId(); /** * @return true if this flow element is in a terminal state (= is finished) */ boolean isTerminal(); /** * @return the Date when this Archived flownode reached this state. */ Date getReachedStateDate(); /** * @return the Date when this Archived flownode was last updated. */ Date getLastUpdateDate(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedFlowNodeInstanceNotFoundException.java ================================================ /** * 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.bpm.flownode; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when it's not possible to find the archived flow node instance. * The class ArchivedFlowNodeInstanceNotFoundException is a form of Throwable that indicates conditions that a * reasonable application might want to catch. * The class ArchivedFlowNodeInstanceNotFoundException that is not also subclasses of {@link RuntimeException} are * checked exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Emmanuel Duchastenier * @author Celine Souchet */ public class ArchivedFlowNodeInstanceNotFoundException extends NotFoundException { private static final long serialVersionUID = -5164644976553290738L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ArchivedFlowNodeInstanceNotFoundException(final Throwable cause) { super(cause); } /** * Constructs a new exception with the specified detail message. * * @param id * The identifier of the archived flow node include in the detail message (which is saved for later retrieval * by the * {@link Throwable#getMessage()} method). */ public ArchivedFlowNodeInstanceNotFoundException(final long id) { super("Archived flow node with id " + id + " not found"); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedFlowNodeInstanceSearchDescriptor.java ================================================ /** * 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.bpm.flownode; /** * @author Emmanuel Duchastenier * @author Elias Ricken de Medeiros */ public class ArchivedFlowNodeInstanceSearchDescriptor { public static final String NAME = "name"; public static final String STATE_NAME = "state"; public static final String PROCESS_DEFINITION_ID = "processDefinitionId"; public static final String PARENT_PROCESS_INSTANCE_ID = "parentProcessInstanceId"; public static final String PARENT_ACTIVITY_INSTANCE_ID = "parentActivityInstanceId"; public static final String ROOT_PROCESS_INSTANCE_ID = "rootProcessInsanceId"; public static final String DISPLAY_NAME = "displayName"; public static final String FLOW_NODE_TYPE = "kind"; public static final String ORIGINAL_FLOW_NODE_ID = "sourceObjectId"; public static final String TERMINAL = "terminal"; public static final String REACHED_STATE_DATE = "reachedStateDate"; public static final String ARCHIVE_DATE = "archiveDate"; public static final String STATE_ID = "stateId"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedGatewayInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Emmanuel Duchastenier */ public interface ArchivedGatewayInstance extends ArchivedFlowNodeInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedHumanTaskInstance.java ================================================ /** * 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.bpm.flownode; import java.util.Date; /** * @author Baptiste Mesta */ public interface ArchivedHumanTaskInstance extends ArchivedTaskInstance { long getActorId(); long getAssigneeId(); TaskPriority getPriority(); Date getExpectedEndDate(); Date getClaimedDate(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedHumanTaskInstanceSearchDescriptor.java ================================================ /** * 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.bpm.flownode; /** * @author Julien Mege * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public final class ArchivedHumanTaskInstanceSearchDescriptor { private ArchivedHumanTaskInstanceSearchDescriptor() { // Utility class } public static final String NAME = "name"; public static final String PRIORITY = "priority"; public static final String STATE_NAME = "state"; public static final String SUPERVISOR_ID = "supervisorId"; public static final String ASSIGNEE_ID = "assigneeId"; public static final String PROCESS_DEFINITION_ID = "processDefinitionId"; public static final String ORIGINAL_HUMAN_TASK_ID = "sourceObjectId"; public static final String ROOT_PROCESS_INSTANCE_ID = "rootProcessInstanceId"; public static final String PARENT_PROCESS_INSTANCE_ID = "parentProcessInstanceId"; public static final String PARENT_ACTIVITY_INSTANCE_ID = "parentActivityInstanceId"; public static final String DISPLAY_NAME = "displayName"; public static final String REACHED_STATE_DATE = "reachedStateDate"; public static final String TERMINAL = "terminal"; public static final String ARCHIVE_DATE = "archiveDate"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedLoopActivityInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Baptiste Mesta */ public interface ArchivedLoopActivityInstance extends ArchivedActivityInstance { int getLoopCounter(); int getLoopMax(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedManualTaskInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Baptiste Mesta */ public interface ArchivedManualTaskInstance extends ArchivedHumanTaskInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedMultiInstanceActivityInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Baptiste Mesta */ public interface ArchivedMultiInstanceActivityInstance extends ArchivedActivityInstance { boolean isSequential(); String getLoopDataInputRef(); String getLoopDataOutputRef(); String getDataInputItemRef(); String getDataOutputItemRef(); int getLoopCardinality(); int getNumberOfInstances(); int getNumberOfActiveInstances(); int getNumberOfCompletedInstances(); int getNumberOfTerminatedInstances(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedReceiveTaskInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Julien Molinaro */ public interface ArchivedReceiveTaskInstance extends ArchivedActivityInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedSendTaskInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Baptiste Mesta */ public interface ArchivedSendTaskInstance extends ArchivedActivityInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedSubProcessActivityInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface ArchivedSubProcessActivityInstance extends ArchivedActivityInstance { boolean isTriggeredByEvent(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedTaskInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Julien Mege */ public interface ArchivedTaskInstance extends ArchivedActivityInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ArchivedUserTaskInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Julien Mege */ public interface ArchivedUserTaskInstance extends ArchivedHumanTaskInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/AutomaticTaskInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Baptiste Mesta */ public interface AutomaticTaskInstance extends TaskInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/BPMEventType.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public enum BPMEventType { START_EVENT, INTERMEDIATE_CATCH_EVENT, INTERMEDIATE_THROW_EVENT, END_EVENT, BOUNDARY_EVENT, EVENT_SUB_PROCESS } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/BoundaryEventInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface BoundaryEventInstance extends CatchEventInstance { String getActivityName(); long getActivityInstanceId(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/CallActivityInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface CallActivityInstance extends ActivityInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/CatchEventInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface CatchEventInstance extends EventInstance { boolean isInterrupting(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/EndEventInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface EndEventInstance extends ThrowEventInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/EventCriterion.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public enum EventCriterion { NAME_DESC, NAME_ASC; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/EventInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface EventInstance extends FlowNodeInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/EventTriggerInstance.java ================================================ /** * 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.bpm.flownode; import org.bonitasoft.engine.bpm.BaseElement; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ public interface EventTriggerInstance extends BaseElement { /** * Return the identifier of the {@link EventInstance} which it is attached. * * @return The identifier of the {@link EventInstance} * @since 6.4.0 */ long getEventInstanceId(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/EventTriggerInstanceSearchDescriptor.java ================================================ /** * 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.bpm.flownode; /** * Fields to filter the {@link EventTriggerInstance} on search. * * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ public class EventTriggerInstanceSearchDescriptor { public static final String EVENT_INSTANCE_ID = "eventInstanceId"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/FlowElementInstance.java ================================================ /** * 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.bpm.flownode; import org.bonitasoft.engine.bpm.NamedElement; /** * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public interface FlowElementInstance extends NamedElement { long getRootContainerId(); long getParentContainerId(); /** * Is this flow element instance currently aborting? * * @return true if this flow element instance is currently aborting, false otherwise * @since 6.0 */ boolean isAborting(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/FlowElementInstanceSearchDescriptor.java ================================================ /** * 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.bpm.flownode; /** * @author Emmanuel Duchastenier */ public class FlowElementInstanceSearchDescriptor { public static final String NAME = "name"; public static final String DESCRIPTION = "description"; // public static final String STATE_NAME = "state"; public static final String FLOW_NODE_TYPE = "flowNodeType"; // kind? } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/FlowNodeExecutionException.java ================================================ /** * 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.bpm.flownode; import org.bonitasoft.engine.exception.ExecutionException; /** * Thrown when a BPM flow node fails to execute. * * @author Emmanuel Duchastenier * @author Celine Souchet */ public class FlowNodeExecutionException extends ExecutionException { private static final long serialVersionUID = 1873896404277831296L; /** * Constructs a new exception with the specified detail message. * * @param message * The detail message why the flow node failed to execute(which is saved for later retrieval by the * {@link Throwable#getMessage()} method). */ public FlowNodeExecutionException(final String message) { super(message); } /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause of the flow node failure (which is saved for later retrieval by the {@link Throwable#getCause()} * method). (A null value is * permitted, and indicates that the cause is nonexistent or unknown.) */ public FlowNodeExecutionException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/FlowNodeInstance.java ================================================ /** * 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.bpm.flownode; import java.util.Date; import org.bonitasoft.engine.bpm.BaseElement; import org.bonitasoft.engine.bpm.DescriptionElement; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Celine Souchet * @since 6.0.0 * @version 6.4.1 */ public interface FlowNodeInstance extends DescriptionElement, BaseElement { /** * Returns the task's direct container ID. For a sub-task or CallActivity, would point to the containing activity ID * of the current element. * For a normal Task / Activity, would point to the ID of the process instance containing the task / activity. * For a multi-instanciated task, each task has its parentContainerId pointing to the containing multi-instance task * (basic container for all task * instances). * * @return the ID of the direct containing element (activity instance of process instance). */ long getParentContainerId(); /** * Returns the root container ID. It is the ID of the root-level containing process instance. * * @return the root container ID. */ long getRootContainerId(); /** * Returns the ID of the process definition where this FlowNodeInstance is defined. * * @return the ID of the process definition. */ long getProcessDefinitionId(); /** * Always returns the directly containing process instance ID (at the lower level, if several levels of containing * processes). * * @return the ID of the lowest-level containing process instance. */ long getParentProcessInstanceId(); /** * Returns the name of the state the flow node instance is in. *

* List of existing states: *

* States that are transitional: *

    *
  • initializing: preparing for execution
  • *
  • executing: executing logic, e.g. operations
  • *
  • completing: executing logic before completion, e.g. ON_FINISH connectors of call activity
  • *
  • completing activity with boundary: completing related boundary events
  • *
  • cancelling: waiting for related elements to be cancelled
  • *
  • aborting: waiting for related elements to be aborted
  • *
  • aborting activity with boundary: waiting for boundaries to be aborted
  • *
  • aborting call activity: waiting for called element to be aborted
  • *
  • canceling call activity: waiting for called element to be cancelled
  • *
  • cancelling subtasks: waiting for cancellation of sub tasks
  • *
*

* States where flow node is waiting for something: *

    *
  • ready: human task is ready to be executed
  • *
  • waiting: flow node is waiting for a non-human interaction, e.g. a BPMN message
  • *
  • failed: when an error occurred, flow node can be skipped or replayed
  • *
*

* Final States: *

    *
  • completed: final state
  • *
  • aborted: flow node was aborted by another flow of the process
  • *
  • skipped: flow node was manually skipped
  • *
  • interrupted: final state, interrupted
  • *
  • cancelled: flow node was cancelled by a human
  • *
* * @return this FlowNodeInstance state */ String getState(); StateCategory getStateCategory(); /** * Returns the FlowNodeType that precises the concrete type of this FlowNodeInstance. * * @return the FlowNodeType */ FlowNodeType getType(); String getDisplayDescription(); String getDisplayName(); /** * @return The identifier of the user who executed the flow node * @since 6.0.1 */ long getExecutedBy(); /** * @return The identifier of the substitute user (as Process manager or Administrator) who executed the flow node. * @since 6.3.0 */ long getExecutedBySubstitute(); /** * Returns the ID of the flow node definition of this instance. * * @return the ID of the flow node definition that this FlowNodeInstance is an instance of. */ long getFlownodeDefinitionId(); /** * @return The date when the flownode instance reached its state ({@link #getState()}) */ Date getReachedStateDate(); /** * @return The last date when the activity instance was updated */ Date getLastUpdateDate(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/FlowNodeInstanceNotFoundException.java ================================================ /** * 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.bpm.flownode; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when it's not possible to find the flow node instance. * The class FlowNodeInstanceNotFoundException is a form of Throwable that indicates conditions that a reasonable * application might want to catch. * The class FlowNodeInstanceNotFoundException that is not also subclasses of {@link RuntimeException} are checked * exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Celine Souchet */ public class FlowNodeInstanceNotFoundException extends NotFoundException { private static final long serialVersionUID = -854820983838093797L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public FlowNodeInstanceNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/FlowNodeInstanceSearchDescriptor.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public class FlowNodeInstanceSearchDescriptor { public static final String NAME = "name"; public static final String STATE_NAME = "state"; public static final String PROCESS_DEFINITION_ID = "processDefinitionId"; public static final String PARENT_PROCESS_INSTANCE_ID = "parentProcessInstanceId"; public static final String PARENT_ACTIVITY_INSTANCE_ID = "parentActivityInstanceId"; public static final String ROOT_PROCESS_INSTANCE_ID = "rootProcessInstanceId"; public static final String DISPLAY_NAME = "displayName"; public static final String STATE_CATEGORY = "stateCategory"; public static final String LAST_UPDATE_DATE = "lastUpdateDate"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/FlowNodeType.java ================================================ /** * 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.bpm.flownode; /** * @author Zhang Bole * @author Baptitse Mesta * @author Elias Ricken de Medeiros * @author Yanyan Liu * @author Matthieu Chaffotte */ public enum FlowNodeType { AUTOMATIC_TASK, HUMAN_TASK, USER_TASK, MANUAL_TASK, SEND_TASK, RECEIVE_TASK, CALL_ACTIVITY, LOOP_ACTIVITY, MULTI_INSTANCE_ACTIVITY, SUB_PROCESS, GATEWAY, START_EVENT, INTERMEDIATE_CATCH_EVENT, BOUNDARY_EVENT, INTERMEDIATE_THROW_EVENT, END_EVENT; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/GatewayInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Baptiste Mesta */ public interface GatewayInstance extends FlowNodeInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/HumanTaskInstance.java ================================================ /** * 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.bpm.flownode; import java.util.Date; /** * @author Baptiste Mesta */ public interface HumanTaskInstance extends TaskInstance { long getActorId(); long getAssigneeId(); TaskPriority getPriority(); Date getExpectedEndDate(); Date getClaimedDate(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/HumanTaskInstanceSearchDescriptor.java ================================================ /** * 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.bpm.flownode; /** * @author Julien Mege * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public final class HumanTaskInstanceSearchDescriptor { public static final String NAME = "name"; public static final String PRIORITY = "priority"; public static final String DUE_DATE = "dueDate"; public static final String STATE_NAME = "state"; public static final String SUPERVISOR_ID = "supervisorId"; /** * User who supervises the process definition */ public static final String USER_ID = "userId"; /** * Group who supervises the process definition */ public static final String GROUP_ID = "groupId"; /** * Role who supervises the process definition */ public static final String ROLE_ID = "roleId"; public static final String PROCESS_DEFINITION_ID = "processDefinitionId"; public static final String PROCESS_INSTANCE_ID = "processInstanceId"; public static final String ROOT_PROCESS_INSTANCE_ID = "rootProcessInstanceId"; public static final String PARENT_PROCESS_INSTANCE_ID = "parentProcessInstanceId"; public static final String PARENT_ACTIVITY_INSTANCE_ID = "parentActivityInstanceId"; public static final String ASSIGNEE_ID = "assigneeId"; public static final String PARENT_CONTAINER_ID = "parentContainerId"; public static final String DISPLAY_NAME = "displayName"; public static final String REACHED_STATE_DATE = "reachedStateDate"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/IntermediateCatchEventInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface IntermediateCatchEventInstance extends CatchEventInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/IntermediateThrowEventInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface IntermediateThrowEventInstance extends ThrowEventInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/LoopActivityInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface LoopActivityInstance extends ActivityInstance { int getLoopCounter(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ManualTaskInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Matthieu Chaffotte */ public interface ManualTaskInstance extends HumanTaskInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/MultiInstanceActivityInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Baptiste Mesta */ public interface MultiInstanceActivityInstance extends ActivityInstance { boolean isSequential(); String getLoopDataInputRef(); String getLoopDataOutputRef(); String getDataInputItemRef(); String getDataOutputItemRef(); int getLoopCardinality(); int getNumberOfInstances(); int getNumberOfActiveInstances(); int getNumberOfCompletedInstances(); int getNumberOfTerminatedInstances(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ReceiveTaskInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Julien Molinaro */ public interface ReceiveTaskInstance extends TaskInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/SendEventException.java ================================================ /** * 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.bpm.flownode; import org.bonitasoft.engine.exception.ExecutionException; /** * @author Elias Ricken de Medeiros */ public class SendEventException extends ExecutionException { private static final long serialVersionUID = 5262064474736586736L; public SendEventException(final Throwable cause) { super(cause); } public SendEventException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/SendTaskInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Matthieu Chaffotte */ public interface SendTaskInstance extends TaskInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/StartEventInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface StartEventInstance extends CatchEventInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/StateCategory.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public enum StateCategory { /** * The state is part of normal states */ NORMAL, /** * The state is part of aborting states */ ABORTING, /** * The state is part o canceling states */ CANCELLING } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/SubProcessActivityInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface SubProcessActivityInstance extends ActivityInstance { boolean isTriggeredByEvent(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/TaskInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Baptiste Mesta */ public interface TaskInstance extends ActivityInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/ThrowEventInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface ThrowEventInstance extends EventInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/TimerEventTriggerInstance.java ================================================ /** * 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.bpm.flownode; import java.util.Date; /** * Represent the instance of the {@link TimerEventTriggerDefinition} (only for the type {@link TimerType#DATE} and * {@link TimerType#DURATION}) * * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ public interface TimerEventTriggerInstance extends EventTriggerInstance { /** * Return the date of the execution of the trigger. * * @return The date of the execution of the trigger. * @since 6.4.0 */ Date getExecutionDate(); /** * Return the name of the {@link EventInstance} which it is attached. * * @return The name of the {@link EventInstance} * @since 6.4.0 */ String getEventInstanceName(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/TimerEventTriggerInstanceNotFoundException.java ================================================ /** * 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.bpm.flownode; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when it's not possible to find the {@link TimerEventTriggerInstance}. * * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ public class TimerEventTriggerInstanceNotFoundException extends NotFoundException { private static final long serialVersionUID = 1L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public TimerEventTriggerInstanceNotFoundException(final Throwable cause) { super(cause); } /** * @param timerEventTriggerInstanceId * The identifier of the timer trigger instance who doesn't find */ public TimerEventTriggerInstanceNotFoundException(final long timerEventTriggerInstanceId) { super("Can't find the TimerEventInstance with id=" + timerEventTriggerInstanceId); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/TimerEventTriggerInstanceSearchDescriptor.java ================================================ /** * 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.bpm.flownode; /** * Fields to filter the {@link TimerEventTriggerInstance} on search. * * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ public class TimerEventTriggerInstanceSearchDescriptor extends EventTriggerInstanceSearchDescriptor { public static final String EVENT_INSTANCE_NAME = "name"; public static final String EXECUTION_DATE = "executionDate"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/UserTaskInstance.java ================================================ /** * 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.bpm.flownode; /** * @author Matthieu Chaffotte */ public interface UserTaskInstance extends HumanTaskInstance { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/UserTaskNotFoundException.java ================================================ /** * 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.bpm.flownode; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when it's not possible to find the user task. * The class UserTaskNotFoundException is a form of Throwable that indicates conditions that a reasonable application * might want to catch. * The class UserTaskNotFoundException that is not also subclasses of {@link RuntimeException} are checked exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Zhang Bole * @author Matthieu Chaffotte * @author Celine Souchet */ public class UserTaskNotFoundException extends NotFoundException { private static final long serialVersionUID = -1165526736427481646L; /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public UserTaskNotFoundException(final String message) { super(message); } public UserTaskNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/WaitingErrorEvent.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface WaitingErrorEvent extends WaitingEvent { String getErrorCode(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/WaitingEvent.java ================================================ /** * 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.bpm.flownode; import org.bonitasoft.engine.bpm.BonitaObject; /** * @author Zhao Na * @author Elias Ricken de Medeiros * @author Celine Souchet * @author Matthieu Chaffotte */ public interface WaitingEvent extends BonitaObject { BPMEventType getEventType(); String getProcessName(); long getProcessDefinitionId(); long getRootProcessInstanceId(); long getParentProcessInstanceId(); long getFlowNodeDefinitionId(); long getFlowNodeInstanceId(); boolean isActive(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/WaitingEventSearchDescriptor.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public class WaitingEventSearchDescriptor { public static final String BPM_EVENT_TYPE = "bpmEventType"; public static final String PROCESS_NAME = "processName"; public static final String FLOW_NODE_NAME = "flowNodeName"; public static final String PROCESS_DEFINITION_ID = "processDefinitionId"; public static final String ROOT_PROCESS_INSTANCE_ID = "rootProcessInstanceId"; public static final String PARENT_PROCESS_INSTANCE_ID = "parentProcessInstanceId"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/WaitingMessageEvent.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface WaitingMessageEvent extends WaitingEvent { String getMessageName(); boolean isLocked(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/WaitingSignalEvent.java ================================================ /** * 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.bpm.flownode; /** * @author Elias Ricken de Medeiros */ public interface WaitingSignalEvent extends WaitingEvent { String getSignalName(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ActivityInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public abstract class ActivityInstanceImpl extends FlowNodeInstanceImpl implements ActivityInstance { private static final long serialVersionUID = 8518693723223444468L; public ActivityInstanceImpl(final String name, final long flownodeDefinitionId) { super(name, flownodeDefinitionId); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedActivityInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public abstract class ArchivedActivityInstanceImpl extends ArchivedFlowNodeInstanceImpl implements ArchivedActivityInstance { private static final long serialVersionUID = 2457027970594869050L; public ArchivedActivityInstanceImpl(final String name) { super(name); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedAutomaticTaskInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.ArchivedAutomaticTaskInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class ArchivedAutomaticTaskInstanceImpl extends ArchivedActivityInstanceImpl implements ArchivedAutomaticTaskInstance { private static final long serialVersionUID = 9035445836798276633L; public ArchivedAutomaticTaskInstanceImpl(final String name) { super(name); } @Override public FlowNodeType getType() { return FlowNodeType.AUTOMATIC_TASK; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedCallActivityInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.ArchivedCallActivityInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; /** * @author Elias Ricken de Medeiros */ public class ArchivedCallActivityInstanceImpl extends ArchivedActivityInstanceImpl implements ArchivedCallActivityInstance { private static final long serialVersionUID = 3788310745899679306L; public ArchivedCallActivityInstanceImpl(final String name) { super(name); } @Override public FlowNodeType getType() { return FlowNodeType.CALL_ACTIVITY; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedFlowElementInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowElementInstance; import org.bonitasoft.engine.bpm.internal.NamedElementImpl; /** * @author Emmanuel Duchastenier */ public abstract class ArchivedFlowElementInstanceImpl extends NamedElementImpl implements ArchivedFlowElementInstance { private static final long serialVersionUID = -8382446613679794971L; private long parentContainerId; private long rootContainerId; private boolean aborting; private String description; public ArchivedFlowElementInstanceImpl(final String name) { super(name); } @Override public long getRootContainerId() { return rootContainerId; } public void setRootContainerId(final long rootContainerId) { this.rootContainerId = rootContainerId; } @Override public long getParentContainerId() { return parentContainerId; } public void setParentContainerId(final long parentContainerId) { this.parentContainerId = parentContainerId; } @Override public boolean isAborting() { return aborting; } public void setAborting(final boolean aborting) { this.aborting = aborting; } @Override public String getDescription() { return description; } /** * @param description * the description to set */ public void setDescription(final String description) { this.description = description; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedFlowNodeInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import java.util.Date; import java.util.Objects; import lombok.ToString; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.bpm.internal.NamedElementImpl; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ @ToString(callSuper = true) public abstract class ArchivedFlowNodeInstanceImpl extends NamedElementImpl implements ArchivedFlowNodeInstance { private static final long serialVersionUID = -6573747806944970703L; private long parentContainerId; private Date archiveDate; private String state; private long rootContainerId; private long processDefinitionId; private long processInstanceId; private long parentActivityInstanceId; private String displayName; private String displayDescription; private long sourceObjectId; private String description; private long executedBy; private long executedBySubstitute; private long flownodeDefinitionId; private boolean terminal; protected Date reachedStateDate; protected Date lastUpdateDate; public ArchivedFlowNodeInstanceImpl(final String name) { super(name); } @Override public long getParentContainerId() { return parentContainerId; } public void setParentContainerId(long parentContainerId) { this.parentContainerId = parentContainerId; } @Override public Date getArchiveDate() { return archiveDate; } public void setArchiveDate(Date archiveDate) { this.archiveDate = archiveDate; } @Override public String getState() { return state; } public void setState(String state) { this.state = state; } @Override public long getRootContainerId() { return rootContainerId; } public void setRootContainerId(long rootContainerId) { this.rootContainerId = rootContainerId; } @Override public long getProcessDefinitionId() { return processDefinitionId; } public void setProcessDefinitionId(long processDefinitionId) { this.processDefinitionId = processDefinitionId; } @Override public long getProcessInstanceId() { return processInstanceId; } public void setProcessInstanceId(long processInstanceId) { this.processInstanceId = processInstanceId; } @Override public long getParentActivityInstanceId() { return parentActivityInstanceId; } public void setParentActivityInstanceId(long parentActivityInstanceId) { this.parentActivityInstanceId = parentActivityInstanceId; } @Override public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } @Override public String getDisplayDescription() { return displayDescription; } public void setDisplayDescription(String displayDescription) { this.displayDescription = displayDescription; } @Override public long getSourceObjectId() { return sourceObjectId; } public void setSourceObjectId(long sourceObjectId) { this.sourceObjectId = sourceObjectId; } @Override public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @Override public long getExecutedBy() { return executedBy; } public void setExecutedBy(long executedBy) { this.executedBy = executedBy; } @Override public long getExecutedBySubstitute() { return executedBySubstitute; } public void setExecutedBySubstitute(long executedBySubstitute) { this.executedBySubstitute = executedBySubstitute; } @Override public long getFlownodeDefinitionId() { return flownodeDefinitionId; } public void setFlownodeDefinitionId(long flownodeDefinitionId) { this.flownodeDefinitionId = flownodeDefinitionId; } @Override public boolean isTerminal() { return terminal; } public void setTerminal(boolean terminal) { this.terminal = terminal; } public Date getReachedStateDate() { return reachedStateDate; } public void setReachedStateDate(Date reachedStateDate) { this.reachedStateDate = reachedStateDate; } @Override public int hashCode() { return Objects.hash(super.hashCode(), parentContainerId, archiveDate, state, rootContainerId, processDefinitionId, processInstanceId, parentActivityInstanceId, displayName, displayDescription, sourceObjectId, description, executedBy, executedBySubstitute, flownodeDefinitionId, terminal, reachedStateDate, lastUpdateDate); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ArchivedFlowNodeInstanceImpl that)) return false; if (!super.equals(o)) return false; return Objects.equals(parentContainerId, that.parentContainerId) && Objects.equals(rootContainerId, that.rootContainerId) && Objects.equals(processDefinitionId, that.processDefinitionId) && Objects.equals(processInstanceId, that.processInstanceId) && Objects.equals(parentActivityInstanceId, that.parentActivityInstanceId) && Objects.equals(sourceObjectId, that.sourceObjectId) && Objects.equals(executedBy, that.executedBy) && Objects.equals(executedBySubstitute, that.executedBySubstitute) && Objects.equals(flownodeDefinitionId, that.flownodeDefinitionId) && Objects.equals(terminal, that.terminal) && Objects.equals(archiveDate, that.archiveDate) && Objects.equals(state, that.state) && Objects.equals(displayName, that.displayName) && Objects.equals(displayDescription, that.displayDescription) && Objects.equals(description, that.description) && Objects.equals(reachedStateDate, that.reachedStateDate) && Objects.equals(lastUpdateDate, that.lastUpdateDate); } @Override public Date getLastUpdateDate() { return lastUpdateDate; } public void setLastUpdateDate(final Date lastUpdateDate) { this.lastUpdateDate = lastUpdateDate; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedGatewayInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.ArchivedGatewayInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; /** * @author Emmanuel Duchastenier */ public class ArchivedGatewayInstanceImpl extends ArchivedFlowNodeInstanceImpl implements ArchivedGatewayInstance { private static final long serialVersionUID = 9086744510541764127L; public ArchivedGatewayInstanceImpl(final String name) { super(name); } @Override public FlowNodeType getType() { return FlowNodeType.GATEWAY; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedHumanTaskInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import java.util.Date; import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.TaskPriority; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public abstract class ArchivedHumanTaskInstanceImpl extends ArchivedActivityInstanceImpl implements ArchivedHumanTaskInstance { private static final long serialVersionUID = -5566250139796838252L; private long actorId; private long assigneeId; private Date claimedDate; private TaskPriority priority; private Date expectedEndDate; public ArchivedHumanTaskInstanceImpl(final String name) { super(name); } @Override public long getActorId() { return actorId; } @Override public long getAssigneeId() { return assigneeId; } @Override public Date getClaimedDate() { return claimedDate; } public void setClaimedDate(Date claimedDate) { this.claimedDate = claimedDate; } @Override public Date getExpectedEndDate() { return expectedEndDate; } @Override public TaskPriority getPriority() { return priority; } public void setActorId(final long actorId) { this.actorId = actorId; } public void setAssigneeId(final long assigneeId) { this.assigneeId = assigneeId; } public void setPriority(final TaskPriority priority) { this.priority = priority; } public void setExpectedEndDate(final Date expectedEndDate) { this.expectedEndDate = expectedEndDate; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("ArchivedHumanTaskInstanceImpl [actorId="); builder.append(actorId); builder.append(", assigneeId="); builder.append(assigneeId); builder.append(", priority="); builder.append(priority); builder.append(", expectedEndDate="); builder.append(expectedEndDate); builder.append(", getReachedStateDate()="); builder.append(getReachedStateDate()); builder.append(", getLastUpdateDate()="); builder.append(getLastUpdateDate()); builder.append(", getArchiveDate()="); builder.append(getArchiveDate()); builder.append(", getState()="); builder.append(getState()); builder.append(", getParentContainerId()="); builder.append(getParentContainerId()); builder.append(", getRootContainerId()="); builder.append(getRootContainerId()); builder.append(", getProcessDefinitionId()="); builder.append(getProcessDefinitionId()); builder.append(", getProcessInstanceId()="); builder.append(getProcessInstanceId()); builder.append(", getName()="); builder.append(getName()); builder.append(", getId()="); builder.append(getId()); builder.append("]"); return builder.toString(); } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + (int) (actorId ^ actorId >>> 32); result = prime * result + (int) (assigneeId ^ assigneeId >>> 32); result = prime * result + (expectedEndDate == null ? 0 : expectedEndDate.hashCode()); result = prime * result + (priority == null ? 0 : priority.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } final ArchivedHumanTaskInstanceImpl other = (ArchivedHumanTaskInstanceImpl) obj; if (actorId != other.actorId) { return false; } if (assigneeId != other.assigneeId) { return false; } if (expectedEndDate == null) { if (other.expectedEndDate != null) { return false; } } else if (!expectedEndDate.equals(other.expectedEndDate)) { return false; } if (priority != other.priority) { return false; } return true; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedLoopActivityInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.ArchivedLoopActivityInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; /** * @author Baptiste Mesta */ public class ArchivedLoopActivityInstanceImpl extends ArchivedActivityInstanceImpl implements ArchivedLoopActivityInstance { private static final long serialVersionUID = -1606721131308631806L; private int loopCounter; private int loopMax; public ArchivedLoopActivityInstanceImpl(final String name) { super(name); } @Override public FlowNodeType getType() { return FlowNodeType.LOOP_ACTIVITY; } @Override public int getLoopCounter() { return loopCounter; } @Override public int getLoopMax() { return loopMax; } public void setLoopCounter(final int loopCounter) { this.loopCounter = loopCounter; } public void setLoopMax(final int loopMax) { this.loopMax = loopMax; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + loopCounter; result = prime * result + loopMax; return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } final ArchivedLoopActivityInstanceImpl other = (ArchivedLoopActivityInstanceImpl) obj; if (loopCounter != other.loopCounter) { return false; } if (loopMax != other.loopMax) { return false; } return true; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedManualTaskInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.ArchivedManualTaskInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; /** * @author Baptiste Mesta */ public class ArchivedManualTaskInstanceImpl extends ArchivedHumanTaskInstanceImpl implements ArchivedManualTaskInstance { private static final long serialVersionUID = -5611907403212645478L; public ArchivedManualTaskInstanceImpl(final String name) { super(name); } @Override public FlowNodeType getType() { return FlowNodeType.MANUAL_TASK; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedMultiInstanceActivityInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.ArchivedMultiInstanceActivityInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class ArchivedMultiInstanceActivityInstanceImpl extends ArchivedActivityInstanceImpl implements ArchivedMultiInstanceActivityInstance { private static final long serialVersionUID = -8302189436621268820L; private final boolean sequential; private final String loopDataInputRef; private final String loopDataOutputRef; private final String dataInputItemRef; private final String dataOutputItemRef; private final int numberOfActiveInstances; private final int numberOfCompletedInstances; private final int numberOfTerminatedInstances; private final int loopCardinality; public ArchivedMultiInstanceActivityInstanceImpl(final String name, final long flownodeDefinitionId, final boolean sequential, final String loopDataInputRef, final String loopDataOutputRef, final String dataInputItemRef, final String dataOutputItemRef, final int numberOfActiveInstances, final int numberOfCompletedInstances, final int numberOfTerminatedInstances, final int loopCardinality) { super(name); setFlownodeDefinitionId(flownodeDefinitionId); this.sequential = sequential; this.loopDataInputRef = loopDataInputRef; this.loopDataOutputRef = loopDataOutputRef; this.dataInputItemRef = dataInputItemRef; this.dataOutputItemRef = dataOutputItemRef; this.numberOfActiveInstances = numberOfActiveInstances; this.numberOfCompletedInstances = numberOfCompletedInstances; this.numberOfTerminatedInstances = numberOfTerminatedInstances; this.loopCardinality = loopCardinality; } /** * @return the sequential */ @Override public boolean isSequential() { return sequential; } /** * @return the loopDataInputRef */ @Override public String getLoopDataInputRef() { return loopDataInputRef; } /** * @return the loopDataOutputRef */ @Override public String getLoopDataOutputRef() { return loopDataOutputRef; } /** * @return the dataInputItemRef */ @Override public String getDataInputItemRef() { return dataInputItemRef; } /** * @return the dataOutputItemRef */ @Override public String getDataOutputItemRef() { return dataOutputItemRef; } /** * @return the numberOfActiveInstances */ @Override public int getNumberOfActiveInstances() { return numberOfActiveInstances; } /** * @return the numberOfCompletedInstances */ @Override public int getNumberOfCompletedInstances() { return numberOfCompletedInstances; } /** * @return the numberOfTerminatedInstances */ @Override public int getNumberOfTerminatedInstances() { return numberOfTerminatedInstances; } /** * @return the loopCardinality */ @Override public int getLoopCardinality() { return loopCardinality; } @Override public FlowNodeType getType() { return FlowNodeType.MULTI_INSTANCE_ACTIVITY; } @Override public int getNumberOfInstances() { return numberOfActiveInstances + numberOfCompletedInstances + numberOfTerminatedInstances; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedReceiveTaskInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.ArchivedReceiveTaskInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; /** * @author Julien Molinaro */ public class ArchivedReceiveTaskInstanceImpl extends ArchivedHumanTaskInstanceImpl implements ArchivedReceiveTaskInstance { private static final long serialVersionUID = 7671768841192077283L; public ArchivedReceiveTaskInstanceImpl(final String name) { super(name); } @Override public FlowNodeType getType() { return FlowNodeType.RECEIVE_TASK; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedSendTaskInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.ArchivedSendTaskInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; /** * @author Baptiste Mesta */ public class ArchivedSendTaskInstanceImpl extends ArchivedHumanTaskInstanceImpl implements ArchivedSendTaskInstance { private static final long serialVersionUID = 7671768841192077283L; public ArchivedSendTaskInstanceImpl(final String name) { super(name); } @Override public FlowNodeType getType() { return FlowNodeType.SEND_TASK; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedSubProcessActivityInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.ArchivedSubProcessActivityInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; /** * @author Elias Ricken de Medeiros */ public class ArchivedSubProcessActivityInstanceImpl extends ArchivedActivityInstanceImpl implements ArchivedSubProcessActivityInstance { private static final long serialVersionUID = 3524292074421289601L; private final boolean triggeredByEvent; public ArchivedSubProcessActivityInstanceImpl(final String name, final boolean triggeredByEvent) { super(name); this.triggeredByEvent = triggeredByEvent; } @Override public FlowNodeType getType() { return FlowNodeType.SUB_PROCESS; } @Override public boolean isTriggeredByEvent() { return triggeredByEvent; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ArchivedUserTaskInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.ArchivedUserTaskInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; /** * @author Julien Mege */ public class ArchivedUserTaskInstanceImpl extends ArchivedHumanTaskInstanceImpl implements ArchivedUserTaskInstance { private static final long serialVersionUID = -5611907403212645478L; public ArchivedUserTaskInstanceImpl(final String name) { super(name); } @Override public FlowNodeType getType() { return FlowNodeType.USER_TASK; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/AutomaticTaskInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.AutomaticTaskInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; /** * @author Baptiste Mesta * @author Celine Souchet */ public class AutomaticTaskInstanceImpl extends TaskInstanceImpl implements AutomaticTaskInstance { private static final long serialVersionUID = 4187673605124017954L; public AutomaticTaskInstanceImpl(final String name, final long flownodeDefinitionId) { super(name, flownodeDefinitionId); } @Override public FlowNodeType getType() { return FlowNodeType.AUTOMATIC_TASK; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/BoundaryEventInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.BoundaryEventInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class BoundaryEventInstanceImpl extends CatchEventInstanceImpl implements BoundaryEventInstance { private static final long serialVersionUID = 1371653839686575195L; private String activityName; private long activityInstanceId; public BoundaryEventInstanceImpl(final String name, final long flownodeDefinitionId, final long activityInstanceId) { super(name, flownodeDefinitionId); this.activityInstanceId = activityInstanceId; } @Override public FlowNodeType getType() { return FlowNodeType.BOUNDARY_EVENT; } @Override public String getActivityName() { return activityName; } public void setActivityName(final String activityName) { this.activityName = activityName; } @Override public long getActivityInstanceId() { return activityInstanceId; } public void setActivityInstanceId(final long activityInstanceId) { this.activityInstanceId = activityInstanceId; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + (activityName == null ? 0 : activityName.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } final BoundaryEventInstanceImpl other = (BoundaryEventInstanceImpl) obj; if (activityName == null) { if (other.activityName != null) { return false; } } else if (!activityName.equals(other.activityName)) { return false; } return true; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/CallActivityInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.CallActivityInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class CallActivityInstanceImpl extends ActivityInstanceImpl implements CallActivityInstance { private static final long serialVersionUID = -1289654446660321732L; public CallActivityInstanceImpl(final String name, final long flownodeDefinitionId) { super(name, flownodeDefinitionId); } @Override public FlowNodeType getType() { return FlowNodeType.CALL_ACTIVITY; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/CatchEventInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.CatchEventInstance; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public abstract class CatchEventInstanceImpl extends EventInstanceImpl implements CatchEventInstance { private static final long serialVersionUID = 86467318586829475L; private boolean interrupting; public CatchEventInstanceImpl(final String name, final long flownodeDefinitionId) { super(name, flownodeDefinitionId); } @Override public boolean isInterrupting() { return interrupting; } public void setInterrupting(final boolean interrupting) { this.interrupting = interrupting; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + (interrupting ? 1231 : 1237); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } final CatchEventInstanceImpl other = (CatchEventInstanceImpl) obj; if (interrupting != other.interrupting) { return false; } return true; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/EndEventInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.EndEventInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class EndEventInstanceImpl extends ThrowEventInstanceImpl implements EndEventInstance { private static final long serialVersionUID = 5709016495551185514L; public EndEventInstanceImpl(final String name, final long flownodeDefinitionId) { super(name, flownodeDefinitionId); } @Override public FlowNodeType getType() { return FlowNodeType.END_EVENT; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/EventInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.EventInstance; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public abstract class EventInstanceImpl extends FlowNodeInstanceImpl implements EventInstance { private static final long serialVersionUID = 8286176446126292113L; public EventInstanceImpl(final String name, final long flownodeDefinitionId) { super(name, flownodeDefinitionId); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/EventTriggerInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import java.util.Objects; import org.bonitasoft.engine.bpm.flownode.EventTriggerInstance; import org.bonitasoft.engine.bpm.internal.BaseDefinitionElementImpl; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ public class EventTriggerInstanceImpl extends BaseDefinitionElementImpl implements EventTriggerInstance { private static final long serialVersionUID = 1894571490582208753L; private long eventInstanceId; public EventTriggerInstanceImpl(final long id, final long eventInstanceId) { super(); setId(id); this.eventInstanceId = eventInstanceId; } @Override public long getEventInstanceId() { return this.eventInstanceId; } protected void setEventInstanceId(final long eventInstanceId) { this.eventInstanceId = eventInstanceId; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; EventTriggerInstanceImpl that = (EventTriggerInstanceImpl) o; return Objects.equals(eventInstanceId, that.eventInstanceId); } @Override public int hashCode() { return Objects.hash(super.hashCode(), eventInstanceId); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/FlowElementInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.FlowElementInstance; import org.bonitasoft.engine.bpm.internal.NamedElementImpl; /** * @author Emmanuel Duchastenier */ public abstract class FlowElementInstanceImpl extends NamedElementImpl implements FlowElementInstance { private static final long serialVersionUID = -8382446613679794971L; private long parentContainerId; private long rootContainerId; private boolean aborting; public FlowElementInstanceImpl(final FlowElementInstance flowElementInstance) { super(flowElementInstance.getName()); rootContainerId = flowElementInstance.getRootContainerId(); parentContainerId = flowElementInstance.getRootContainerId(); } @Override public long getRootContainerId() { return rootContainerId; } public void setRootContainerId(final long rootContainerId) { this.rootContainerId = rootContainerId; } @Override public long getParentContainerId() { return parentContainerId; } public void setParentContainerId(final long parentContainerId) { this.parentContainerId = parentContainerId; } @Override public boolean isAborting() { return aborting; } public void setAborting(final boolean aborting) { this.aborting = aborting; } @Override public String toString() { final StringBuilder stb = new StringBuilder(super.toString()); stb.append("parentContainerId: "); stb.append(parentContainerId); stb.append("\n"); stb.append("rootContainerId: "); stb.append(rootContainerId); stb.append("\n"); stb.append("aborting: "); stb.append(aborting); stb.append("\n"); return stb.toString(); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/FlowNodeInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import java.util.Date; import java.util.Objects; import lombok.ToString; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.StateCategory; import org.bonitasoft.engine.bpm.internal.NamedElementImpl; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta * @author Celine Souchet */ @ToString(callSuper = true) public abstract class FlowNodeInstanceImpl extends NamedElementImpl implements FlowNodeInstance { private static final long serialVersionUID = -6573747806944970703L; private long parentContainerId; private String state; private StateCategory stateCategory; private long rootContainerId; private long processDefinitionId; private long parentProcessInstanceId; private String displayDescription; private String displayName; private String description; private long executedBy; private long executedBySubstitute; private long flownodeDefinitionId; private Date reachedStateDate; private Date lastUpdateDate; public FlowNodeInstanceImpl(final String name, final long flownodeDefinitionId) { super(name); this.flownodeDefinitionId = flownodeDefinitionId; } @Override public long getParentContainerId() { return parentContainerId; } public void setParentContainerId(long parentContainerId) { this.parentContainerId = parentContainerId; } @Override public String getState() { return state; } public void setState(String state) { this.state = state; } @Override public StateCategory getStateCategory() { return stateCategory; } public void setStateCategory(StateCategory stateCategory) { this.stateCategory = stateCategory; } @Override public long getRootContainerId() { return rootContainerId; } public void setRootContainerId(long rootContainerId) { this.rootContainerId = rootContainerId; } @Override public long getProcessDefinitionId() { return processDefinitionId; } public void setProcessDefinitionId(long processDefinitionId) { this.processDefinitionId = processDefinitionId; } @Override public long getParentProcessInstanceId() { return parentProcessInstanceId; } public void setParentProcessInstanceId(long parentProcessInstanceId) { this.parentProcessInstanceId = parentProcessInstanceId; } @Override public String getDisplayDescription() { return displayDescription; } public void setDisplayDescription(String displayDescription) { this.displayDescription = displayDescription; } @Override public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } @Override public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @Override public long getExecutedBy() { return executedBy; } public void setExecutedBy(long executedBy) { this.executedBy = executedBy; } @Override public long getExecutedBySubstitute() { return executedBySubstitute; } public void setExecutedBySubstitute(long executedBySubstitute) { this.executedBySubstitute = executedBySubstitute; } @Override public long getFlownodeDefinitionId() { return flownodeDefinitionId; } public void setFlownodeDefinitionId(long flownodeDefinitionId) { this.flownodeDefinitionId = flownodeDefinitionId; } public Date getReachedStateDate() { return reachedStateDate; } public void setReachedSateDate(final Date reachedStateDate) { this.reachedStateDate = reachedStateDate; } @Override public Date getLastUpdateDate() { return lastUpdateDate; } public void setLastUpdateDate(final Date lastUpdateDate) { this.lastUpdateDate = lastUpdateDate; } @Override public int hashCode() { return Objects.hash(super.hashCode(), parentContainerId, state, stateCategory, rootContainerId, processDefinitionId, parentProcessInstanceId, displayDescription, displayName, description, executedBy, executedBySubstitute, flownodeDefinitionId, reachedStateDate, lastUpdateDate); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof FlowNodeInstanceImpl that)) return false; if (!super.equals(o)) return false; return Objects.equals(parentContainerId, that.parentContainerId) && Objects.equals(rootContainerId, that.rootContainerId) && Objects.equals(processDefinitionId, that.processDefinitionId) && Objects.equals(parentProcessInstanceId, that.parentProcessInstanceId) && Objects.equals(executedBy, that.executedBy) && Objects.equals(executedBySubstitute, that.executedBySubstitute) && Objects.equals(flownodeDefinitionId, that.flownodeDefinitionId) && Objects.equals(state, that.state) && Objects.equals(stateCategory, that.stateCategory) && Objects.equals(displayDescription, that.displayDescription) && Objects.equals(displayName, that.displayName) && Objects.equals(description, that.description) && Objects.equals(reachedStateDate, that.reachedStateDate) && Objects.equals(lastUpdateDate, that.lastUpdateDate); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/GatewayInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import lombok.ToString; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.GatewayInstance; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ @ToString(callSuper = true) public class GatewayInstanceImpl extends FlowNodeInstanceImpl implements GatewayInstance { private static final long serialVersionUID = 8722950382913966260L; public GatewayInstanceImpl(final String name, final long flownodeDefinitionId) { super(name, flownodeDefinitionId); } @Override public FlowNodeType getType() { return FlowNodeType.GATEWAY; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/HumanTaskInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import java.util.Date; import lombok.ToString; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.TaskPriority; /** * @author Baptiste Mesta * @author Celine Souchet */ @ToString(callSuper = true) public abstract class HumanTaskInstanceImpl extends TaskInstanceImpl implements HumanTaskInstance { private static final long serialVersionUID = 5988594900787242204L; private final long actorId; private long assigneeId; private TaskPriority priority; private Date expectedEndDate; private Date claimedDate; public HumanTaskInstanceImpl(final String name, final long flownodeDefinitionId, final long actorId) { super(name, flownodeDefinitionId); this.actorId = actorId; } public void setAssigneeId(final long assigneeId) { this.assigneeId = assigneeId; } @Override public long getActorId() { return actorId; } @Override public long getAssigneeId() { return assigneeId; } @Override public TaskPriority getPriority() { return priority; } @Override public Date getExpectedEndDate() { return expectedEndDate; } @Override public Date getClaimedDate() { return claimedDate; } public void setPriority(final TaskPriority priority) { this.priority = priority; } public void setExpectedEndDate(final Date expectedEndDate) { this.expectedEndDate = expectedEndDate; } public void setClaimedDate(final Date claimedDate) { this.claimedDate = claimedDate; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/IntermediateCatchEventInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.IntermediateCatchEventInstance; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class IntermediateCatchEventInstanceImpl extends CatchEventInstanceImpl implements IntermediateCatchEventInstance { private static final long serialVersionUID = -1163487409085885481L; public IntermediateCatchEventInstanceImpl(final String name, final long flownodeDefinitionId) { super(name, flownodeDefinitionId); } @Override public FlowNodeType getType() { return FlowNodeType.INTERMEDIATE_CATCH_EVENT; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/IntermediateThrowEventInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.IntermediateThrowEventInstance; /** * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class IntermediateThrowEventInstanceImpl extends ThrowEventInstanceImpl implements IntermediateThrowEventInstance { private static final long serialVersionUID = -3256549925414354772L; public IntermediateThrowEventInstanceImpl(final String name, final long flownodeDefinitionId) { super(name, flownodeDefinitionId); } @Override public FlowNodeType getType() { return FlowNodeType.INTERMEDIATE_THROW_EVENT; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/LoopActivityInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.LoopActivityInstance; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class LoopActivityInstanceImpl extends ActivityInstanceImpl implements LoopActivityInstance { private static final long serialVersionUID = -1901009347639323157L; private final int loopCounter; public LoopActivityInstanceImpl(final String name, final long flownodeDefinitionId, final int loopCounter) { super(name, flownodeDefinitionId); this.loopCounter = loopCounter; } @Override public FlowNodeType getType() { return FlowNodeType.LOOP_ACTIVITY; } @Override public int getLoopCounter() { return loopCounter; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ManualTaskInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.ManualTaskInstance; /** * @author Baptiste Mesta * @author Celine Souchet */ public class ManualTaskInstanceImpl extends HumanTaskInstanceImpl implements ManualTaskInstance { private static final long serialVersionUID = -1791657324743303807L; public ManualTaskInstanceImpl(final String name, final long flownodeDefinitionId, final long actorId) { super(name, flownodeDefinitionId, actorId); } @Override public FlowNodeType getType() { return FlowNodeType.MANUAL_TASK; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/MultiInstanceActivityInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.MultiInstanceActivityInstance; /** * @author Baptiste Mesta */ public class MultiInstanceActivityInstanceImpl extends ActivityInstanceImpl implements MultiInstanceActivityInstance { private static final long serialVersionUID = -5723988334123367841L; private final boolean sequential; private final String loopDataInputRef; private final String loopDataOutputRef; private final String dataInputItemRef; private final String dataOutputItemRef; private final int numberOfActiveInstances; private final int numberOfCompletedInstances; private final int numberOfTerminatedInstances; private final int loopCardinality; public MultiInstanceActivityInstanceImpl(final String name, final long flownodeDefinitionId, final boolean sequential, final String loopDataInputRef, final String loopDataOutputRef, final String dataInputItemRef, final String dataOutputItemRef, final int numberOfActiveInstances, final int numberOfCompletedInstances, final int numberOfTerminatedInstances, final int loopCardinality) { super(name, flownodeDefinitionId); this.sequential = sequential; this.loopDataInputRef = loopDataInputRef; this.loopDataOutputRef = loopDataOutputRef; this.dataInputItemRef = dataInputItemRef; this.dataOutputItemRef = dataOutputItemRef; this.numberOfActiveInstances = numberOfActiveInstances; this.numberOfCompletedInstances = numberOfCompletedInstances; this.numberOfTerminatedInstances = numberOfTerminatedInstances; this.loopCardinality = loopCardinality; } /** * @return the sequential */ @Override public boolean isSequential() { return sequential; } /** * @return the loopDataInputRef */ @Override public String getLoopDataInputRef() { return loopDataInputRef; } /** * @return the loopDataOutputRef */ @Override public String getLoopDataOutputRef() { return loopDataOutputRef; } /** * @return the dataInputItemRef */ @Override public String getDataInputItemRef() { return dataInputItemRef; } /** * @return the dataOutputItemRef */ @Override public String getDataOutputItemRef() { return dataOutputItemRef; } /** * @return the numberOfActiveInstances */ @Override public int getNumberOfActiveInstances() { return numberOfActiveInstances; } /** * @return the numberOfCompletedInstances */ @Override public int getNumberOfCompletedInstances() { return numberOfCompletedInstances; } /** * @return the numberOfTerminatedInstances */ @Override public int getNumberOfTerminatedInstances() { return numberOfTerminatedInstances; } /** * @return the loopCardinality */ @Override public int getLoopCardinality() { return loopCardinality; } @Override public FlowNodeType getType() { return FlowNodeType.MULTI_INSTANCE_ACTIVITY; } @Override public int getNumberOfInstances() { return numberOfActiveInstances + numberOfCompletedInstances + numberOfTerminatedInstances; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ReceiveTaskInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.ReceiveTaskInstance; /** * @author Julien Molinaro */ public class ReceiveTaskInstanceImpl extends TaskInstanceImpl implements ReceiveTaskInstance { private static final long serialVersionUID = 4187673605124017954L; public ReceiveTaskInstanceImpl(final String name, final long flownodeDefinitionId) { super(name, flownodeDefinitionId); } @Override public FlowNodeType getType() { return FlowNodeType.RECEIVE_TASK; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/SendTaskInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.SendTaskInstance; /** * @author Matthieu Chaffotte */ public class SendTaskInstanceImpl extends TaskInstanceImpl implements SendTaskInstance { private static final long serialVersionUID = -3792840569021028433L; public SendTaskInstanceImpl(final String name, final long flownodeDefinitionId) { super(name, flownodeDefinitionId); } @Override public FlowNodeType getType() { return FlowNodeType.SEND_TASK; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/StartEventInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.StartEventInstance; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class StartEventInstanceImpl extends CatchEventInstanceImpl implements StartEventInstance { private static final long serialVersionUID = 1162574672955389640L; public StartEventInstanceImpl(final String name, final long flownodeDefinitionId) { super(name, flownodeDefinitionId); } @Override public FlowNodeType getType() { return FlowNodeType.START_EVENT; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/SubProcessActivityInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.SubProcessActivityInstance; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SubProcessActivityInstanceImpl extends ActivityInstanceImpl implements SubProcessActivityInstance { private static final long serialVersionUID = 2652709303320068129L; private final boolean triggeredByEvent; public SubProcessActivityInstanceImpl(final String name, final long flownodeDefinitionId, final boolean triggeredByEvent) { super(name, flownodeDefinitionId); this.triggeredByEvent = triggeredByEvent; } @Override public FlowNodeType getType() { return FlowNodeType.SUB_PROCESS; } @Override public boolean isTriggeredByEvent() { return triggeredByEvent; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/TaskInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import org.bonitasoft.engine.bpm.flownode.TaskInstance; /** * @author Baptiste Mesta * @author Celine Souchet */ public abstract class TaskInstanceImpl extends ActivityInstanceImpl implements TaskInstance { private static final long serialVersionUID = -6250330349559993047L; public TaskInstanceImpl(final String name, final long flownodeDefinitionId) { super(name, flownodeDefinitionId); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/ThrowEventInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public abstract class ThrowEventInstanceImpl extends EventInstanceImpl { private static final long serialVersionUID = 2454133141873552710L; public ThrowEventInstanceImpl(final String name, final long flownodeDefinitionId) { super(name, flownodeDefinitionId); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/TimerEventTriggerInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import java.util.Date; import org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstance; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ public class TimerEventTriggerInstanceImpl extends EventTriggerInstanceImpl implements TimerEventTriggerInstance { private static final long serialVersionUID = -1000995843357026775L; private Date executionDate; private final String eventInstanceName; public TimerEventTriggerInstanceImpl(final long id, final long eventInstanceId, final String eventInstanceName, final Date executionDate) { super(id, eventInstanceId); this.eventInstanceName = eventInstanceName; setExecutionDate(executionDate); } @Override public Date getExecutionDate() { return executionDate; } public void setExecutionDate(final Date executionDate) { this.executionDate = executionDate; } @Override public String getEventInstanceName() { return eventInstanceName; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + (eventInstanceName == null ? 0 : eventInstanceName.hashCode()); result = prime * result + (executionDate == null ? 0 : executionDate.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } final TimerEventTriggerInstanceImpl other = (TimerEventTriggerInstanceImpl) obj; if (eventInstanceName == null) { if (other.eventInstanceName != null) { return false; } } else if (!eventInstanceName.equals(other.eventInstanceName)) { return false; } if (executionDate == null) { if (other.executionDate != null) { return false; } } else if (!executionDate.equals(other.executionDate)) { return false; } return true; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("TimerEventTriggerInstanceImpl [id=").append(getId()).append(", executionDate=") .append(executionDate).append(", eventInstanceName=") .append(eventInstanceName).append(", eventInstanceId=").append(getEventInstanceId()).append("]"); return builder.toString(); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/UserTaskInstanceImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import lombok.ToString; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.bpm.flownode.UserTaskInstance; /** * @author Matthieu Chaffotte * @author Celine Souchet */ @ToString(callSuper = true) public class UserTaskInstanceImpl extends HumanTaskInstanceImpl implements UserTaskInstance { private static final long serialVersionUID = -1791657324743303807L; /** * @param name * @param flownodeDefinitionId * @param actorId */ public UserTaskInstanceImpl(final String name, final long flownodeDefinitionId, final long actorId) { super(name, flownodeDefinitionId, actorId); } @Override public FlowNodeType getType() { return FlowNodeType.USER_TASK; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/WaitingErrorEventImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import lombok.ToString; import org.bonitasoft.engine.bpm.flownode.BPMEventType; import org.bonitasoft.engine.bpm.flownode.WaitingErrorEvent; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ @ToString(callSuper = true) public class WaitingErrorEventImpl extends WaitingEventImpl implements WaitingErrorEvent { private static final long serialVersionUID = 2453504392749981399L; private String errorCode; public WaitingErrorEventImpl() { } public WaitingErrorEventImpl(final BPMEventType eventType, final long processdefinitionId, final String processName, final long flowNodeDefinitionId, final String errorCode) { super(eventType, processdefinitionId, processName, flowNodeDefinitionId); this.errorCode = errorCode; } @Override public String getErrorCode() { return errorCode; } public void setErrorCode(final String errorCode) { this.errorCode = errorCode; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/WaitingEventImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import lombok.ToString; import org.bonitasoft.engine.bpm.flownode.BPMEventType; import org.bonitasoft.engine.bpm.flownode.WaitingEvent; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ @ToString public abstract class WaitingEventImpl implements WaitingEvent { private static final long serialVersionUID = -4479053804299166959L; private BPMEventType eventType; private String processName; private long flowNodeDefinitionId = -1; private long processDefinitionId; private long rootProcessInstanceId = -1; private long parentProcessInstanceId = -1; private long flowNodeInstanceId = -1; private boolean active = true; public WaitingEventImpl() { } public WaitingEventImpl(final BPMEventType eventType, final long processDefinitionId, final String processName, final long flowNodeDefinitionId) { this.eventType = eventType; this.processName = processName; this.flowNodeDefinitionId = flowNodeDefinitionId; this.processDefinitionId = processDefinitionId; } public WaitingEventImpl(final BPMEventType eventType) { this.eventType = eventType; } @Override public BPMEventType getEventType() { return eventType; } public void setEventType(final BPMEventType eventType) { this.eventType = eventType; } @Override public String getProcessName() { return processName; } @Override public long getFlowNodeDefinitionId() { return flowNodeDefinitionId; } public void setProcessName(final String processName) { this.processName = processName; } public void setFlowNodeDefinitionId(final long flowNodeDefinitionId) { this.flowNodeDefinitionId = flowNodeDefinitionId; } @Override public long getProcessDefinitionId() { return processDefinitionId; } public void setProcessDefinitionId(final long processDefinitionId) { this.processDefinitionId = processDefinitionId; } @Override public long getRootProcessInstanceId() { return rootProcessInstanceId; } public void setRootProcessInstanceId(final long processInstanceId) { rootProcessInstanceId = processInstanceId; } @Override public long getParentProcessInstanceId() { return parentProcessInstanceId; } public void setParentProcessInstanceId(final long parentProcessInstanceId) { this.parentProcessInstanceId = parentProcessInstanceId; } @Override public long getFlowNodeInstanceId() { return flowNodeInstanceId; } public void setFlowNodeInstanceId(final long flowNodeInstanceId) { this.flowNodeInstanceId = flowNodeInstanceId; } @Override public boolean isActive() { return active; } public void setActive(final boolean active) { this.active = active; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/WaitingMessageEventImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import lombok.ToString; import org.bonitasoft.engine.bpm.flownode.BPMEventType; import org.bonitasoft.engine.bpm.flownode.WaitingMessageEvent; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ @ToString(callSuper = true) public class WaitingMessageEventImpl extends WaitingEventImpl implements WaitingMessageEvent { private static final long serialVersionUID = 6036375135350657130L; private String messageName; private boolean locked = false; public WaitingMessageEventImpl() { } public WaitingMessageEventImpl(final BPMEventType eventType, final long processdefinitionId, final String processName, final long flowNodeDefinitionId, final String messageName) { super(eventType, processdefinitionId, processName, flowNodeDefinitionId); this.messageName = messageName; } @Override public String getMessageName() { return messageName; } public void setMessageName(final String messageName) { this.messageName = messageName; } @Override public boolean isLocked() { return locked; } public void setLocked(final boolean locked) { this.locked = locked; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/flownode/impl/internal/WaitingSignalEventImpl.java ================================================ /** * 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.bpm.flownode.impl.internal; import lombok.ToString; import org.bonitasoft.engine.bpm.flownode.BPMEventType; import org.bonitasoft.engine.bpm.flownode.WaitingSignalEvent; /** * @author Zhao Na * @author Matthieu Chaffotte * @author Celine Souchet */ @ToString(callSuper = true) public class WaitingSignalEventImpl extends WaitingEventImpl implements WaitingSignalEvent { private static final long serialVersionUID = 6899269154378362388L; private String signalName; public WaitingSignalEventImpl() { super(); } public WaitingSignalEventImpl(final BPMEventType eventType, final long processdefinitionId, final String processName, final long flowNodeDefinitionId, final String signalName) { super(eventType, processdefinitionId, processName, flowNodeDefinitionId); this.signalName = signalName; } @Override public String getSignalName() { return signalName; } public void setSignalName(final String signalName) { this.signalName = signalName; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/parameter/ParameterCriterion.java ================================================ /** * 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.bpm.parameter; /** * @author Matthieu Chaffotte */ public enum ParameterCriterion { NAME_ASC, NAME_DESC; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/parameter/ParameterInstance.java ================================================ /** * 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.bpm.parameter; import org.bonitasoft.engine.bpm.DescriptionElement; public interface ParameterInstance extends DescriptionElement { Object getValue(); String getType(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/parameter/impl/ParameterImpl.java ================================================ /** * 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.bpm.parameter.impl; import org.bonitasoft.engine.bpm.internal.NamedElementImpl; import org.bonitasoft.engine.bpm.parameter.ParameterInstance; /** * @author Matthieu Chaffotte */ public class ParameterImpl extends NamedElementImpl implements ParameterInstance { private static final long serialVersionUID = 4096607590317516470L; private final String description; private final Object value; private final String type; public ParameterImpl(final String name, final String description, final Object value, final String type) { super(name); this.description = description; this.value = value; this.type = type; } @Override public String getDescription() { return description; } @Override public Object getValue() { return value; } @Override public String getType() { return type; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ActivationState.java ================================================ /** * 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.bpm.process; /** *

Activation state of a process.
* A {@link ProcessDefinition} can be enabled or disabled, which toggles on/off the possibility to start a new instance * of the process.

*

Use {@link ProcessDeploymentInfo#getActivationState()} to retrieve the activation state for a process.

* * @see ProcessDeploymentInfo#getActivationState() * @author Celine Souchet * @author Emmanuel Duchastenier * @version 6.3.5 * @since 6.0.0 */ public enum ActivationState { /** * The {@link ProcessDeploymentInfo} is enabled and instances of the process can be started. */ ENABLED, /** * The {@link ProcessDeploymentInfo} is disabled and no instance can be started. */ DISABLED } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ArchivedProcessInstance.java ================================================ /** * 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.bpm.process; import java.util.Date; import org.bonitasoft.engine.bpm.ArchivedElement; import org.bonitasoft.engine.bpm.BaseElement; import org.bonitasoft.engine.bpm.NamedElement; /** * Represents an archived instance of a process. * Gives access to the information of the instance, whereas the state, the definition, the archived date of the process * instance... * * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet * @version 6.3.5 * @since 6.0.0 */ public interface ArchivedProcessInstance extends NamedElement, BaseElement, ArchivedElement { /** * Get the state of this process instance when it was archived. * * @return The state of this process instance when it was archived. * @see ProcessInstanceState * @since 6.0.0 */ String getState(); /** * Get the identifier of the state of this process instance when it was archived. * * @return The identifier of the state of this process instance when it was archived. * @see ProcessInstanceState * @since 6.0.0 */ int getStateId(); /** * Get the date when this process instance was started. * * @return The date when this process instance was started. * @since 6.0.0 */ Date getStartDate(); /** * Get the identifier of the user who started this process instance. * * @return The identifier of the user who started this process instance. * @since 6.0.0 */ long getStartedBy(); /** * Get the identifier of the substitute user (as Process manager or Administrator) who started this process * instance. * * @return The identifier of the substitute user (as Process manager or Administrator) who started this process * instance. * @since 6.3.0 */ long getStartedBySubstitute(); /** * Get the date when this process instance was finished. * It equals to null, if this process instance is not finished. * * @return The date when this process instance was finished. * @since 6.0.0 */ Date getEndDate(); /** * Get the date of the last update of this process instance, when the process instance was archived. * * @return The date of the last update of this process instance, when the process instance was archived. * @since 6.0.0 */ Date getLastUpdate(); /** * Get the identifier of the definition of this process. * * @return The identifier of the definition of this process. * @see ProcessDefinition#getId() * @since 6.0.0 */ long getProcessDefinitionId(); /** * Get the description of this process instance, when the process instance was archived. * * @return The description of this process instance, when the process instance was archived. * @since 6.0.0 */ String getDescription(); /** * Get the identifier of the root {@link ProcessInstance} of this process instance. * Is -1 if this process instance is not a child of another process instance. * * @return The identifier of the root {@link ProcessInstance} of this process instance. * @see ProcessInstance#getId() * @see ArchivedProcessInstance#getSourceObjectId() * @since 6.0.0 */ long getRootProcessInstanceId(); /** * Get the identifier of the flow node instance who starts this process instance. * Is -1 if this process instance is not a child of another process instance. * * @return The identifier of the flow node instance who starts this process instance. * @see org.bonitasoft.engine.bpm.flownode.CallActivityInstance#getId() * @see SubProcessDefinition#getId() * @since 6.0.0 */ long getCallerId(); /** * The index must be between 1 and 5. * * @param index * The index of the value * @return The value of the search key corresponding to the parameter * @exception IndexOutOfBoundsException * It's thrown if the parameter is not between 1 and 5. * @since 6.4.0 */ String getStringIndexValue(int index); /** * The index must be between 1 and 5. * * @param index * The index of the label * @return The label of the search key corresponding to the parameter * @exception IndexOutOfBoundsException * It's thrown if the parameter is not between 1 and 5. * @since 6.4.0 */ String getStringIndexLabel(int index); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ArchivedProcessInstanceNotFoundException.java ================================================ /** * 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.bpm.process; import org.bonitasoft.engine.exception.NotFoundException; /** *

Thrown when it's not possible to find the archived process instance.

*

The class ArchivedProcessInstanceNotFoundException is a form of Throwable that indicates conditions that a * reasonable application might want to catch.
* The class ArchivedProcessInstanceNotFoundException that is not also subclasses of {@link RuntimeException} are * checked exceptions.
* Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary.

* * @author Zhao Na * @author Matthieu Chaffotte * @author Celine Souchet * @version 6.3.5 * @since 6.0.0 */ public class ArchivedProcessInstanceNotFoundException extends NotFoundException { private static final long serialVersionUID = 8745379570098982138L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ArchivedProcessInstanceNotFoundException(final Throwable cause) { super(cause); } /** * Constructs a new exception and its message * * @param archivedProcessInstanceId * The identifier of the archived process instance not found */ public ArchivedProcessInstanceNotFoundException(final long archivedProcessInstanceId) { super("Archived process instance with id <" + archivedProcessInstanceId + "> not found"); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ArchivedProcessInstancesSearchDescriptor.java ================================================ /** * 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.bpm.process; /** * Search descriptors are used to filter / sort results of a generic search.
* ArchivedProcessInstancesSearchDescriptor defines the fields that can be used as filters or sort fields on * List<ArchivedProcessInstance> returning methods. * * @author Yanyan Liu * @author Matthieu Chaffotte * @author Celine Souchet * @see org.bonitasoft.engine.api.ProcessRuntimeAPI * @see ArchivedProcessInstance * @version 6.3.5 * @since 6.0.0 */ public class ArchivedProcessInstancesSearchDescriptor { /** * The field corresponding to the date of the archiving of the process instance. */ public static final String ARCHIVE_DATE = "archiveDate"; /** * The field corresponding to the identifier of the archived process instance. */ public static final String ID = "id"; /** * The field corresponding to the name of the process. */ public static final String NAME = "name"; /** * The field corresponding to the identifier of the process definition. */ public static final String PROCESS_DEFINITION_ID = "processDefinitionId"; /** * The field corresponding to the identifier of the root process instance. */ public static final String ROOT_PROCESS_INSTANCE_ID = "rootProcessInstanceId"; /** * The field corresponding to the identifier of the state of the archived process instance. */ public static final String STATE_ID = "stateId"; /** * The field corresponding to the identifier of the running process instance. */ public static final String SOURCE_OBJECT_ID = "sourceObjectId"; /** * The field corresponding to the date of the end of the process instance. */ public static final String END_DATE = "endDate"; /** * The field corresponding to the identifier of the user who started the process instance. */ public static final String STARTED_BY = "startedBy"; /** * The field corresponding to the identifier of the user who started the process instance for the user in * {@link ProcessInstance#getStartedBy()}. */ public static final String STARTED_BY_SUBSTITUTE = "startedBySubstitute"; /** * The field corresponding to the date when the process instance is started. */ public static final String START_DATE = "startDate"; /** * The field corresponding to the last date of the updating of the process instance. */ public static final String LAST_UPDATE = "lastUpdate"; /** * The field corresponding to the identifier of the user who supervised the process instance. */ public static final String USER_ID = "userId"; /** * The field corresponding to the identifier of the group who supervised the process instance. */ public static final String GROUP_ID = "groupId"; /** * The field corresponding to the identifier of the role who supervised the process instance. */ public static final String ROLE_ID = "roleId"; /** * The field corresponding to the identifier of the user assignee to a user task of the process instance. */ public static final String ASSIGNEE_ID = "assigneeId"; /** * The field corresponding to the identifier of the flow node that starts the process instance. */ public static final String CALLER_ID = "callerId"; public static final String STRING_INDEX_1 = "stringIndex1"; public static final String STRING_INDEX_2 = "stringIndex2"; public static final String STRING_INDEX_3 = "stringIndex3"; public static final String STRING_INDEX_4 = "stringIndex4"; public static final String STRING_INDEX_5 = "stringIndex5"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ConfigurationState.java ================================================ /** * 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.bpm.process; /** * Autoset by Bonita Engine. Determines if a process is resolved / unresolved, that is, if a process can be started or * not. * Reasons for unresolved processes are: *
    *
  • Not all actors of the process have mappings to users / groups / roles / memberships
  • *
  • Some business data used in the process are not available on the currently deployed Business Data Model version * (Subscription editions only)
  • *
  • Not all connectors used in the process have an implementation defined
  • *
  • Not all user filters used in the process have an implementation defined
  • *
  • Not all parameters used in the process have a value defined (Subscription editions only)
  • *
*

Use {@link ProcessDeploymentInfo#getConfigurationState()} to retrieve the configuration state for a process.

* * @see ProcessDeploymentInfo#getConfigurationState() * @author Celine Souchet * @author Emmanuel Duchastenier * @version 6.3.5 * @since 6.0.0 */ public enum ConfigurationState { /** * The process is unresolved, for one or more of the causes defined above. */ UNRESOLVED, /** * The process is resolved, and can be started to create a new instance. */ RESOLVED } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/IllegalProcessStateException.java ================================================ /** * 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.bpm.process; import org.bonitasoft.engine.exception.BonitaException; /** *

Thrown when the process instance is in illegal state.

*

The class IllegalProcessStateException is a form of Throwable that indicates conditions that a reasonable * application might want to catch.
* The class IllegalProcessStateException that is not also subclasses of {@link RuntimeException} are checked * exceptions.
* Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary.

* * @author Matthieu Chaffotte * @author Celine Souchet * @version 6.3.5 * @since 6.0.0 */ public class IllegalProcessStateException extends BonitaException { private static final long serialVersionUID = -7194818098032678910L; /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public IllegalProcessStateException(final String message) { super(message); } /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public IllegalProcessStateException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/Index.java ================================================ /** * 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.bpm.process; /** * Use to update the string indexes. * * @author Emmanuel Duchastenier * @since 7.12.0 * @see ProcessInstance#getStringIndex1() * @see ProcessInstance#getStringIndex2() * @see ProcessInstance#getStringIndex3() * @see ProcessInstance#getStringIndex4() * @see ProcessInstance#getStringIndex5() * @see org.bonitasoft.engine.api.ProcessAPI#updateProcessInstanceIndex(long, Index, String) */ public enum Index { /** * Corresponding to the first search key */ FIRST, /** * Corresponding to the second search key */ SECOND, /** * Corresponding to the third search key */ THIRD, /** * Corresponding to the fourth search key */ FOURTH, /** * Corresponding to the fifth search key */ FIFTH } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/Problem.java ================================================ /** * 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.bpm.process; import java.io.Serializable; /** * A Problem explains the issue in the {@link DesignProcessDefinition} when it is not well designed. * It relates to : *
    *
  • structural problems such as a {@link org.bonitasoft.engine.bpm.flownode.BoundaryEventDefinition} without an * exception flow,
  • *
  • naming problems such as two {@link org.bonitasoft.engine.bpm.flownode.FlowNodeDefinition}s with the same * name,
  • *
  • type problems such as an {@link org.bonitasoft.engine.expression.Expression} which has not the right type,
  • *
  • ...
  • *
* * @author Matthieu Chaffotte * @author Celine Souchet * @version 6.3.5 * @since 6.0.0 */ public interface Problem extends Serializable { /** * Defines a severity level. * * @author Matthieu Chaffotte */ public enum Level { /** * The {@link DesignProcessDefinition} cannot be deployed. */ ERROR, /** * The {@link DesignProcessDefinition} can be deployed and used but contains something that should be updated. */ WARNING, /** * Not currently used. */ INFO } /** * Returns the severity of the problem. * * @return the severity */ Level getLevel(); /** * Returns the resource/concept name of the problem. * It can be related to {@link org.bonitasoft.engine.bpm.data.DataDefinition}, * {@link org.bonitasoft.engine.bpm.flownode.FlowNodeDefinition}, ... * * @return the resource name */ String getResource(); /** * Returns the resource identifier. * Generally, it is the name of the entity that has the problem in the {@link DesignProcessDefinition}. * For example, the name of the {@link org.bonitasoft.engine.bpm.flownode.UserTaskDefinition}. * * @return the resource identifier */ String getResourceId(); /** * Returns the description/explanation of the problem. * * @return the explanation of the problem */ String getDescription(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessActivationException.java ================================================ /** * 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.bpm.process; import org.bonitasoft.engine.exception.ExecutionException; /** * Thrown when a process definition cannot be enabled / disabled, or when a * {@link org.bonitasoft.engine.api.ProcessAPI#startProcess(long)} (and its variants) * cannot be performed * because the process definition is not enabled. * * @author Baptiste Mesta * @author Emmanuel Duchastenier * @author Celine Souchet * @see org.bonitasoft.engine.api.ProcessAPI#startProcess(long) * @see org.bonitasoft.engine.api.ProcessAPI#startProcess(long, long) * @see org.bonitasoft.engine.api.ProcessAPI#startProcess(long, java.util.Map) * @see org.bonitasoft.engine.api.ProcessAPI#startProcess(long, java.util.List, java.util.Map) * @see org.bonitasoft.engine.api.ProcessAPI#startProcess(long, long, java.util.Map) * @see org.bonitasoft.engine.api.ProcessAPI#startProcess(long, long, java.util.List, java.util.Map) * @version 6.3.5 * @since 6.0.0 */ public class ProcessActivationException extends ExecutionException { private static final long serialVersionUID = -425713003229819771L; /** * Constructs a new exception with the specified detail cause. * * @param e * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ProcessActivationException(final Exception e) { super(e); } /** * Constructs a new exception with the specified detail message and cause. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ProcessActivationException(final String message, final Throwable cause) { super(message, cause); } /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public ProcessActivationException(final String message) { super(message); } /** * Constructs a new exception and the message with the given informations of the process definition with the * problem. * * @param processDefinitionId * The identifier of the process definition * @param name * The name of the process definition * @param version * The version of the process definition */ public ProcessActivationException(final long processDefinitionId, final String name, final String version) { super("The process definition is not enabled !!"); setProcessDefinitionIdOnContext(processDefinitionId); setProcessDefinitionNameOnContext(name); setProcessDefinitionVersionOnContext(version); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessDefinitionNotFoundException.java ================================================ /** * 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.bpm.process; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when a reference to a nonexistant {@link ProcessDefinition} is made, generally through its ID. * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet * @version 6.3.5 * @since 6.0.0 */ public class ProcessDefinitionNotFoundException extends NotFoundException { private static final long serialVersionUID = -6469281629161643837L; /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public ProcessDefinitionNotFoundException(final String message) { super(message); } /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ProcessDefinitionNotFoundException(final Throwable cause) { super(cause); } /** * Constructs a new exception with the specified detail cause, and constructs the message with the identifier of the * process definition. * * @param processDefinitionId * The identifier of the process definition * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ProcessDefinitionNotFoundException(final long processDefinitionId, final Throwable cause) { super("Process definition not found with id: " + processDefinitionId, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessDeployException.java ================================================ /** * 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.bpm.process; import org.bonitasoft.engine.exception.ExecutionException; /** * Thrown when a process fails to deploy. * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Emmanuel Duchastenier * @author Celine Souchet * @version 6.3.5 * @since 6.0.0 */ public class ProcessDeployException extends ExecutionException { private static final long serialVersionUID = 3104389074405599228L; /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public ProcessDeployException(final String message) { super(message); } public ProcessDeployException(final String message, final Throwable cause) { super(message, cause); } /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ProcessDeployException(final Throwable cause) { super(cause); } /** * Get the identifier of the process definition who failed. * * @return The identifier of the process definition who failed. * @deprecated always return 0, as the processDefinitionId cannot be determined if process failed to deploy */ @Deprecated(forRemoval = true, since = "8.0") public Long getProcessDefinitionId() { return 0L; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessDeploymentInfo.java ================================================ /** * 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.bpm.process; import java.util.Date; import org.bonitasoft.engine.bpm.BaseElement; import org.bonitasoft.engine.bpm.DescriptionElement; /** * Gives access to the {@link ProcessDefinition} deployment information.
* A ProcessDeploymentInfo has a {@link ConfigurationState}, which says if the process is * resolved (all its basic configuration has a * proper value), or unresolved (some configuration has to be done before the process can be activated). *

* A ProcessDeploymentInfo has an {@link ActivationState}, which says if the process was set to * enabled (logged users can start * instances of this process), or disabled (no start can be performed on the process). * * @author Baptiste Mesta * @author Yanyan Liu * @author Hongwen Zang * @author Celine Souchet * @author Emmanuel Duchastenier * @see ProcessDefinition * @see ConfigurationState * @see ActivationState */ public interface ProcessDeploymentInfo extends DescriptionElement, BaseElement { /** * Retrieves the {@link ProcessDefinition} identifier * * @return a long representing the ProcessDefinition identifier * @see ProcessDefinition */ long getProcessId(); /** * Retrieves the {@link ProcessDefinition} version * * @return a String representing the ProcessDefinition version. * @see ProcessDefinition */ String getVersion(); /** * Retrieves the {@link ProcessDefinition} display description. Unlike description that is static, the * display description can be * updated via * {@link org.bonitasoft.engine.api.ProcessManagementAPI#updateProcessDeploymentInfo(long, ProcessDeploymentInfoUpdater)}. *

* When set, this field is used by the Bonita Portal in the place of description. * * @return a String representing the {@link ProcessDefinition} display description. * @see ProcessDefinition * @see org.bonitasoft.engine.api.ProcessManagementAPI#updateProcessDeploymentInfo(long, * ProcessDeploymentInfoUpdater) */ String getDisplayDescription(); /** * Retrieves the Date when the underlining {@link ProcessDefinition} was deployed. * * @return the Date when the underlining ProcessDefinition was deployed. * @see ProcessDefinition */ Date getDeploymentDate(); /** * Retrieves the identifier of the Bonita {@link org.bonitasoft.engine.identity.User} which deployed the * {@link ProcessDefinition} * * @return a long representing the identifier of the Bonita user which deployed the process. * @see org.bonitasoft.engine.identity.User * @see ProcessDefinition */ long getDeployedBy(); /** * Retrieves the {@link ProcessDefinition} display name. Unlike name that is static, the display * name can be * updated via * {@link org.bonitasoft.engine.api.ProcessManagementAPI#updateProcessDeploymentInfo(long, ProcessDeploymentInfoUpdater)}. *

* When set this field is used by the Bonita Portal in the place of name. * * @return a String representing the ProcessDefinition display name. * @see ProcessDefinition * @see org.bonitasoft.engine.api.ProcessManagementAPI#updateProcessDeploymentInfo(long, * ProcessDeploymentInfoUpdater) */ String getDisplayName(); /** * Retrieves the date of the last update statement * * @return the date of the last update statement */ Date getLastUpdateDate(); /** * Retrieves the process icon path. The path is relative to the bonita.home folder. * * @return a String representing the process icon path. */ String getIconPath(); /** * Retrieves the {@link ProcessDefinition} {@link ConfigurationState}. * * @return the ProcessDefinition ConfigurationState * @see ProcessDefinition * @see ConfigurationState */ ConfigurationState getConfigurationState(); /** * Retrieves the {@link ProcessDefinition} {@link ActivationState}. * * @return the ProcessDefinition ActivationState */ ActivationState getActivationState(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessDeploymentInfoCriterion.java ================================================ /** * 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.bpm.process; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.Sort; /** * Sort criterion used to specify the sort order of the {@link ProcessDeploymentInfo}.
* Used by {@link org.bonitasoft.engine.api.ProcessManagementAPI} methods like * {@link org.bonitasoft.engine.api.ProcessManagementAPI#getProcessDeploymentInfos(int, int, ProcessDeploymentInfoCriterion)} * to * indicate in what order we should return the list of the results. * * @author Baptiste Mesta * @author Celine Souchet * @version 6.3.5 * @since 6.0.0 */ public enum ProcessDeploymentInfoCriterion { /** * Process name ascending order */ NAME_ASC(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC), /** * Process version ascending order */ VERSION_ASC(ProcessDeploymentInfoSearchDescriptor.VERSION, Order.ASC), /** * Process activation state ascending order */ ACTIVATION_STATE_ASC(ProcessDeploymentInfoSearchDescriptor.ACTIVATION_STATE, Order.ASC), /** * Process configuration state ascending order */ CONFIGURATION_STATE_ASC(ProcessDeploymentInfoSearchDescriptor.CONFIGURATION_STATE, Order.ASC), /** * Process name ascending order */ NAME_DESC(ProcessDeploymentInfoSearchDescriptor.NAME, Order.DESC), /** * Process version ascending order */ VERSION_DESC(ProcessDeploymentInfoSearchDescriptor.VERSION, Order.DESC), /** * Process activation state descending order */ ACTIVATION_STATE_DESC(ProcessDeploymentInfoSearchDescriptor.ACTIVATION_STATE, Order.DESC), /** * Process configuration state descending order */ CONFIGURATION_STATE_DESC(ProcessDeploymentInfoSearchDescriptor.CONFIGURATION_STATE, Order.DESC), /** * Default criterion : {@link ProcessDeploymentInfoCriterion#NAME_ASC} */ DEFAULT(ProcessDeploymentInfoSearchDescriptor.NAME, Order.ASC); private final String field; private final Order order; ProcessDeploymentInfoCriterion(final String field, final Order order) { this.field = field; this.order = order; } /** * Get the {@link Order} corresponding to this object. * * @return The {@link Order} corresponding to this object. * @since 6.3.5 */ public Order getOrder() { return order; } /** * Get the name of the field used to sort the {@link ProcessDeploymentInfo}, corresponding to this object. * * @return The name of the field corresponding to this object. * @since 6.3.5 */ public String getField() { return field; } /** * Build the {@link Sort} corresponding to this object. * * @return The {@link Sort} corresponding to this object. * @since 6.3.5 */ public Sort getSort() { return new Sort(order, field); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessDeploymentInfoSearchDescriptor.java ================================================ /** * 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.bpm.process; /** * Search descriptors are used to filter / sort results of a generic search.
* ProcessDeploymentInfoSearchDescriptor defines the fields that can be used as filters or sort fields on * List<ProcessDeploymentInfo> * returning methods. * * @author Zhao Na * @author Celine Souchet * @author Matthieu Chaffotte * @see org.bonitasoft.engine.api.ProcessAPI * @see ProcessDeploymentInfo * @see ProcessDefinition * @version 6.3.5 * @since 6.0.0 */ public class ProcessDeploymentInfoSearchDescriptor { /** * The field corresponding to the identifier of the process in the database. */ public static final String ID = "id"; /** * The field corresponding to the name of the process. */ public static final String NAME = "name"; /** * The field corresponding to the version of the process. */ public static final String VERSION = "version"; /** * The field corresponding to the date of the deployment of the process. */ public static final String DEPLOYMENT_DATE = "deploymentDate"; /** * The field corresponding to the identifier of the user who deployed the process. */ public static final String DEPLOYED_BY = "deployedBy"; /** * The field corresponding to the activation state of the process. * To filter on this field, example : * {@code searchOptionsBuilder.filter(ProcessDeploymentInfoSearchDescriptor.ACTIVATION_STATE, ActivationState.ENABLED.name());} */ public static final String ACTIVATION_STATE = "activationState"; /** * The field corresponding to the configuration state of the process. */ public static final String CONFIGURATION_STATE = "configurationState"; /** * The field corresponding to the identifier of the process definition (in the bonita home). */ public static final String PROCESS_ID = "processId"; /** * The field corresponding to the display name of the process. */ public static final String DISPLAY_NAME = "displayName"; /** * The field corresponding to the last date of the updating of the process. */ public static final String LAST_UPDATE_DATE = "lastUpdateDate"; /** * The field corresponding to the identifier of the category of the process. */ public static final String CATEGORY_ID = "categoryId"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessDeploymentInfoUpdater.java ================================================ /** * 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.bpm.process; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * Updater object to update {@link ProcessDeploymentInfo}. * * @author Emmanuel Duchastenier * @author Zhang Bole * @author Celine Souchet * @version 6.3.5 * @since 6.0.0 */ public class ProcessDeploymentInfoUpdater implements Serializable { private static final long serialVersionUID = 8000868488852784706L; /** * Fields that can be updated on a {@link ProcessDeploymentInfo}. * * @author Emmanuel Duchastenier */ public enum ProcessDeploymentInfoField { /** * Display name of the process */ DISPLAY_NAME, /** * Display description of the process */ DISPLAY_DESCRIPTION, /** * Path of the icon of the process */ ICONPATH } private final Map fields; /** * Construct the updater object with no field to update. */ public ProcessDeploymentInfoUpdater() { fields = new HashMap(ProcessDeploymentInfoField.values().length); } /** * Set the new display name of the process definition. * * @param name * The new display name of the process definition. * @since 6.3.5 */ public void setDisplayName(final String name) { fields.put(ProcessDeploymentInfoField.DISPLAY_NAME, name); } /** * Set the new display description of the process definition. * * @param description * The new display description of the process definition. * @since 6.3.5 */ public void setDisplayDescription(final String description) { fields.put(ProcessDeploymentInfoField.DISPLAY_DESCRIPTION, description); } /** * Set the new icon path of the process definition. * * @param iconPath * The new icon path of the process definition. * @since 6.3.5 */ public void setIconPath(final String iconPath) { fields.put(ProcessDeploymentInfoField.ICONPATH, iconPath); } /** * Get the map of the fields to update with their new value. * * @return The map of the fields to update with their new value. * @since 6.3.5 */ public Map getFields() { return fields; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessEnablementException.java ================================================ /** * 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.bpm.process; import org.bonitasoft.engine.exception.ExecutionException; /** * Thrown when a process cannot be enabled. This can happen if the process is already enabled, or if an unexpected error * occurs on methods like * {@link org.bonitasoft.engine.api.ProcessAPI#enableProcess(long)}. * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet * @version 6.4.1 * @since 6.0.0 */ public class ProcessEnablementException extends ExecutionException { private static final long serialVersionUID = 7175486722623260153L; /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public ProcessEnablementException(final String message) { super(message); } /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ProcessEnablementException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessExecutionException.java ================================================ /** * 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.bpm.process; import lombok.Getter; import org.bonitasoft.engine.exception.ExecutionException; /** * Thrown when a process instance fails when it's starting. * * @author Frédéric Bouquet * @author Celine Souchet * @version 6.3.5 * @since 6.0.0 */ public class ProcessExecutionException extends ExecutionException { private static final long serialVersionUID = 4412292065541283593L; @Getter private long retryAfter = -1L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ProcessExecutionException(Throwable cause) { super(cause); } public ProcessExecutionException(Throwable cause, long retryAfter) { super(cause); this.retryAfter = retryAfter; } /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public ProcessExecutionException(String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessExportException.java ================================================ /** * 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.bpm.process; import org.bonitasoft.engine.exception.ExecutionException; /** * Thrown when the exporting of the content of the process definition under the bonita home fails. * * @author Elias Ricken de Medeiros * @author Celine Souchet * @version 6.3.5 * @since 6.0.0 */ public class ProcessExportException extends ExecutionException { private static final long serialVersionUID = -6577758579083715914L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ProcessExportException(final Throwable cause) { super(cause); } /** * Constructs a new exception with the specified detail message and cause. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ProcessExportException(final String message, final Throwable cause) { super(message, cause); } /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public ProcessExportException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessInstance.java ================================================ /** * 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.bpm.process; import java.util.Date; import org.bonitasoft.engine.bpm.BaseElement; import org.bonitasoft.engine.bpm.NamedElement; /** * Represents a running instance of a process. * Gives access to the information of the instance, whereas the state, the definition of the process instance... * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet * @version 6.3.5 * @since 6.0.0 */ public interface ProcessInstance extends NamedElement, BaseElement { /** * Get the state of this process instance. * * @return The current state of this process instance. * @see ProcessInstanceState * @since 6.0.0 */ String getState(); /** * Get the date when this process instance was started. * * @return The date when this process instance was started. * @since 6.0.0 */ Date getStartDate(); /** * Get the identifier of the user who started this process instance. * * @return The identifier of the user who started this process instance. * @since 6.0.0 */ long getStartedBy(); /** * Get the identifier of the substitute user (as Process manager or Administrator) who started this process * instance. * * @return The identifier of the substitute user (as Process manager or Administrator) who started this process * instance. * @since 6.3.0 */ long getStartedBySubstitute(); /** * Get the date when this process instance was finished. * It equals to null, if this process instance is not finished. * * @return The date when this process instance was finished. * @since 6.0.0 */ Date getEndDate(); /** * Get the date of the last update of this process instance. * * @return The date of the last update of this process instance. * @since 6.0.0 */ Date getLastUpdate(); /** * Get the identifier of the definition of this process. * * @return The identifier of the definition of this process. * @see ProcessDefinition#getId() * @since 6.0.0 */ long getProcessDefinitionId(); /** * Get the description of this process instance. * * @return The description of this process instance. * @since 6.0.0 */ String getDescription(); /** * Get the identifier of the root {@link ProcessInstance} of this process instance. * Is -1 if this process instance is not a child of another process instance. * * @return The identifier of the root process instance of this process instance. * @see ProcessInstance#getId() * @see ArchivedProcessInstance#getSourceObjectId() * @since 6.0.0 */ long getRootProcessInstanceId(); /** * Get the identifier of the flow node instance who starts this process instance. * Is -1 if this process instance is not a child of another process instance. * * @return The identifier of the flow node instance who starts this process instance. * @see org.bonitasoft.engine.bpm.flownode.CallActivityInstance#getId() * @see SubProcessDefinition#getId() * @since 6.0.0 */ long getCallerId(); /** * Also known as search key, this field is used to extend the meta data of the process instance. * It's the value corresponding to the search key label. * Return an empty string, if there is no first search key. * * @return The first search key of this process instance. * @see ProcessInstance#getStringIndexLabel(int) * @since 6.0.0 */ String getStringIndex1(); /** * Also known as search key, this field is used to extend the meta data of the process instance. * It's the value corresponding to the search key label. * Return an empty string, if there is no second search key. * * @return The second search key of this process instance. * @see ProcessInstance#getStringIndexLabel(int) * @since 6.0.0 */ String getStringIndex2(); /** * Also known as search key, this field is used to extend the meta data of the process instance. * It's the value corresponding to the search key label. * Return an empty string, if there is no third search key. * * @return The third search key of this process instance. * @see ProcessInstance#getStringIndexLabel(int) * @since 6.0.0 */ String getStringIndex3(); /** * Also known as search key, this field is used to extend the meta data of the process instance. * It's the value corresponding to the search key label. * Return an empty string, if there is no fourth search key. * * @return The fourth search key of this process instance. * @see ProcessInstance#getStringIndexLabel(int) * @since 6.0.0 */ String getStringIndex4(); /** * Also known as search key, this field is used to extend the meta data of the process instance. * It's the value corresponding to the search key label. * Return an empty string, if there is no fifth search key. * * @return The fifth search key of this process instance. * @see ProcessInstance#getStringIndexLabel(int) * @since 6.0.0 */ String getStringIndex5(); /** * The index must be between 1 and 5. * * @param index * The index of the label * @return The label of the search key corresponding to the parameter * @exception IndexOutOfBoundsException * It's thrown if the parameter is not between 1 and 5. * @since 6.0.0 */ String getStringIndexLabel(int index); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessInstanceCriterion.java ================================================ /** * 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.bpm.process; /** * Sort criterion used to specify the sort order of the {@link ProcessInstance}.
* Used by {@link org.bonitasoft.engine.api.ProcessRuntimeAPI} methods like * {@link org.bonitasoft.engine.api.ProcessRuntimeAPI#getProcessInstances(int, int, ProcessInstanceCriterion)} to * indicate in what order we should return the * list of the results. * * @author Emmanuel Duchastenier * @author Yanyan Liu * @author Celine Souchet * @version 6.3.5 * @since 6.0.0 * @see org.bonitasoft.engine.api.ProcessRuntimeAPI */ public enum ProcessInstanceCriterion { /** * Process State ascending order */ STATE_ASC, /** * Process State descending order */ STATE_DESC, /** * Process instance name ascending order */ NAME_ASC, /** * Process instance name descending order */ NAME_DESC, /** * Creation date ascending order */ CREATION_DATE_ASC, /** * Creation date descending order */ CREATION_DATE_DESC, /** * Last update ascending order */ LAST_UPDATE_ASC, /** * Last update descending order */ LAST_UPDATE_DESC, /** * Archive date ascending order */ ARCHIVE_DATE_ASC, /** * Archive date descending order */ ARCHIVE_DATE_DESC, /** * Default sort criterion */ DEFAULT; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessInstanceNotFoundException.java ================================================ /** * 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.bpm.process; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when a reference to an inexistant {@link ProcessInstance} is made, generally through its ID. * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class ProcessInstanceNotFoundException extends NotFoundException { private static final long serialVersionUID = 2829055979970789709L; /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public ProcessInstanceNotFoundException(final String message) { super(message); } /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ProcessInstanceNotFoundException(final Throwable cause) { super(cause); } /** * Constructs a new exception and the message with the identifier of the process instance. * * @param processInstanceId * The identifier of the process instance */ public ProcessInstanceNotFoundException(final long processInstanceId) { super("No process instance found !!"); setProcessInstanceIdOnContext(processInstanceId); } /** * Constructs a new exception and the message with the identifier of the process instance. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) * @param processInstanceId * The identifier of the process instance */ public ProcessInstanceNotFoundException(final Throwable cause, final long processInstanceId) { super(cause); setProcessInstanceIdOnContext(processInstanceId); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessInstanceSearchDescriptor.java ================================================ /** * 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.bpm.process; /** * Search descriptors are used to filter / sort results of a generic search.
* ProcessInstanceSearchDescriptor defines the fields that can be used as filters or sort fields on * List<ProcessInstance> returning * methods. * * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Emmanuel Duchastenier * @author Celine Souchet * @see org.bonitasoft.engine.api.ProcessRuntimeAPI * @see ProcessInstance * @version 6.3.5 * @since 6.0.0 */ public class ProcessInstanceSearchDescriptor { /** * The field corresponding to the identifier of the process definition. */ public static final String PROCESS_DEFINITION_ID = "processDefinitionId"; /** * The field corresponding to the name of the process. */ public static final String NAME = "name"; /** * The field corresponding to the identifier of the process instance. */ public static final String ID = "id"; /** * The field corresponding to the identifier of the root process instance. */ public static final String ROOT_PROCESS_INSTANCE_ID = "rootProcessInstanceId"; /** * The field corresponding to the identifier of the user assignee to a user task of the process instance. */ public static final String ASSIGNEE_ID = "assigneeId"; /** * The field corresponding to the identifier of the user who started the process instance. */ public static final String STARTED_BY = "startedBy"; /** * The field corresponding to the identifier of the user who started the process instance for the user in * {@link ProcessInstance#getStartedBy()}. */ public static final String STARTED_BY_SUBSTITUTE = "startedBySubstitute"; /** * The field corresponding to the date when the process instance is started. */ public static final String START_DATE = "startDate"; /** * The field corresponding to the date when the process instance completed. */ public static final String END_DATE = "endDate"; /** * The field corresponding to the last date of the updating of the process instance. */ public static final String LAST_UPDATE = "lastUpdate"; /** * The field corresponding to the identifier of the user who supervised the process instance. */ public static final String PROCESS_SUPERVISOR_USER_ID = "userId"; /** * The field corresponding to the identifier of the group who supervised the process instance. */ public static final String PROCESS_SUPERVISOR_GROUP_ID = "groupId"; /** * The field corresponding to the identifier of the role who supervised the process instance. */ public static final String PROCESS_SUPERVISOR_ROLE_ID = "roleId"; /** * The field corresponding to the identifier of the state of the process instance. */ public static final String STATE_ID = "stateId"; /** * The field corresponding to the name of the state of the process instance. * The value can be of the type String or ProcessInstanceState. * * @see ProcessInstanceState */ public static final String STATE_NAME = "stateName"; /** * The field corresponding to the identifier of the flow node that starts the process instance. */ public static final String CALLER_ID = "callerId"; /** * The field corresponding to the first search key of the process instance. */ public static final String STRING_INDEX_1 = "index1"; /** * The field corresponding to the second search key of the process instance. */ public static final String STRING_INDEX_2 = "index2"; /** * The field corresponding to the third search key of the process instance. */ public static final String STRING_INDEX_3 = "index3"; /** * The field corresponding to the fourth search key of the process instance. */ public static final String STRING_INDEX_4 = "index4"; /** * The field corresponding to the fifth search key of the process instance. */ public static final String STRING_INDEX_5 = "index5"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessInstanceState.java ================================================ /** * 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.bpm.process; import java.util.HashMap; import java.util.Map; /** * @author Celine Souchet * @version 6.3.5 * @since 6.0.0 * @see ProcessInstance#getState() */ public enum ProcessInstanceState { /** * The initializing state. Id=0 */ INITIALIZING(0), /** * The started state. Id=1 */ STARTED(1), /** * The suspended state. Id=2 */ SUSPENDED(2), /** * The cancelled state. Id=3 */ CANCELLED(3), /** * The aborted state. Id=4 */ ABORTED(4), /** * The completing state. Id=5 */ COMPLETING(5), /** * The completed state. Id=6 */ COMPLETED(6), /** * The error state. Id=7 */ ERROR(7), /** * The aborting state. Id=11 */ ABORTING(11); private static Map map = new HashMap(11); private int id; private ProcessInstanceState(final int id) { this.id = id; } /** * Get the identifier corresponding to the state. * * @return The identifier corresponding to the state. * @since 6.3.5 */ public int getId() { return id; } /** * Get the {@link ProcessInstanceState} corresponding to the identifier. * * @return The {@link ProcessInstanceState} corresponding to the identifier. * @since 6.3.5 */ public static ProcessInstanceState getFromId(final int id) { if (!map.containsKey(id)) { map.put(id, fromIdToProcessInstanceState(id)); } return map.get(id); } private static ProcessInstanceState fromIdToProcessInstanceState(final int id) { for (final ProcessInstanceState state : values()) { if (id == state.getId()) { return state; } } return null; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/ProcessResourceNotFoundException.java ================================================ /** * Copyright (C) 2026 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.bpm.process; import org.bonitasoft.engine.exception.NotFoundException; /** * Thrown when a resource cannot be found in a process definition's business archive. */ public class ProcessResourceNotFoundException extends NotFoundException { private static final long serialVersionUID = 3032565336554438921L; public ProcessResourceNotFoundException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/V6FormDeployException.java ================================================ /** * 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.bpm.process; /** * Thrown when a process fails to deploy because of V6 form presence in the bar to deploy. * * @author Danila Mazour * @since 7.8.0 */ public class V6FormDeployException extends ProcessDeployException { /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public V6FormDeployException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/impl/ProcessInstanceUpdater.java ================================================ /** * Copyright (C) 2020 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.bpm.process.impl; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * Updater object to update the string indexes. * * @author Danila Mazour * @author Emmanuel Duchastenier * @since 7.12.0 * @see org.bonitasoft.engine.bpm.process.ProcessInstance */ public class ProcessInstanceUpdater implements Serializable { public enum ProcessInstanceField { /** * Corresponding to the first search key */ STRING_INDEX_1, /** * Corresponding to the second search key */ STRING_INDEX_2, /** * Corresponding to the third search key */ STRING_INDEX_3, /** * Corresponding to the fourth search key */ STRING_INDEX_4, /** * Corresponding to the fifth search key */ STRING_INDEX_5 } private final Map fields; /** * Default Constructor with no field to update. */ public ProcessInstanceUpdater() { fields = new HashMap<>(); } /** * Set the new value for the first search key, also known as string index. * * @param stringIndex * The new value for the first search key. */ public void setStringIndex1(final String stringIndex) { fields.put(ProcessInstanceField.STRING_INDEX_1, stringIndex); } /** * Set the new value for the second search key, also known as string index. * * @param stringIndex * The new value for the second search key. */ public void setStringIndex2(final String stringIndex) { fields.put(ProcessInstanceField.STRING_INDEX_2, stringIndex); } /** * Set the new value for the third search key, also known as string index. * * @param stringIndex * The new value for the third search key. */ public void setStringIndex3(final String stringIndex) { fields.put(ProcessInstanceField.STRING_INDEX_3, stringIndex); } /** * Set the new value for the fourth search key, also known as string index. * * @param stringIndex * The new value for the fourth search keyx. */ public void setStringIndex4(final String stringIndex) { fields.put(ProcessInstanceField.STRING_INDEX_4, stringIndex); } /** * Set the new value for the fifth search key, also known as string index. * * @param stringIndex * The new value for the fifth search key. */ public void setStringIndex5(final String stringIndex) { fields.put(ProcessInstanceField.STRING_INDEX_5, stringIndex); } /** * Get the map of the fields to update and their new value. * * @return The map of the fields to update and their new value. */ public Map getFields() { return fields; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/impl/internal/ArchivedProcessInstanceImpl.java ================================================ /** * 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.bpm.process.impl.internal; import java.util.Date; import org.bonitasoft.engine.bpm.internal.NamedElementImpl; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class ArchivedProcessInstanceImpl extends NamedElementImpl implements ArchivedProcessInstance { private static final long serialVersionUID = -1924361771241157184L; private String state; private Date startDate; private long startedBy; private long startedBySubstitute; private Date endDate; private Date archiveDate; private Date lastUpdate; private long sourceObjectId; private int stateId; private long processDefinitionId; private String description; private long rootProcessInstanceId; private long callerId; private String stringIndexValue1; private String stringIndexValue2; private String stringIndexValue3; private String stringIndexValue4; private String stringIndexValue5; private String stringIndexLabel1; private String stringIndexLabel2; private String stringIndexLabel3; private String stringIndexLabel4; private String stringIndexLabel5; public ArchivedProcessInstanceImpl(final String name) { super(name); } @Override public String getState() { return state; } public void setState(final String state) { this.state = state; } @Override public Date getStartDate() { return startDate; } public void setStartDate(final Date startDate) { this.startDate = startDate; } @Override public long getStartedBy() { return startedBy; } public void setStartedBy(final long startedBy) { this.startedBy = startedBy; } @Override public long getStartedBySubstitute() { return startedBySubstitute; } public void setStartedBySubstitute(final long startedBySubstitute) { this.startedBySubstitute = startedBySubstitute; } @Override public Date getEndDate() { return endDate; } public void setEndDate(final Date endDate) { this.endDate = endDate; } @Override public Date getArchiveDate() { return archiveDate; } public void setArchiveDate(final Date archiveDate) { this.archiveDate = archiveDate; } @Override public Date getLastUpdate() { return lastUpdate; } public void setLastUpdate(final Date lastUpdate) { this.lastUpdate = lastUpdate; } @Override public long getSourceObjectId() { return sourceObjectId; } public void setSourceObjectId(final long sourceObjectId) { this.sourceObjectId = sourceObjectId; } @Override public int getStateId() { return stateId; } public void setStateId(final int stateId) { this.stateId = stateId; } @Override public long getProcessDefinitionId() { return processDefinitionId; } public void setProcessDefinitionId(final long processDefinitionId) { this.processDefinitionId = processDefinitionId; } @Override public String getDescription() { return description; } public void setDescription(final String description) { this.description = description; } @Override public long getRootProcessInstanceId() { return rootProcessInstanceId; } public void setRootProcessInstanceId(final long rootProcessInstanceId) { this.rootProcessInstanceId = rootProcessInstanceId; } @Override public long getCallerId() { return callerId; } public void setCallerId(final long callerId) { this.callerId = callerId; } public void setStringIndexValue(final int index, final String value) { switch (index) { case 1: stringIndexValue1 = value; break; case 2: stringIndexValue2 = value; break; case 3: stringIndexValue3 = value; break; case 4: stringIndexValue4 = value; break; case 5: stringIndexValue5 = value; break; default: throw new IndexOutOfBoundsException("string index value must be between 1 and 5 (included)"); } } @Override public String getStringIndexValue(final int index) { switch (index) { case 1: return stringIndexValue1; case 2: return stringIndexValue2; case 3: return stringIndexValue3; case 4: return stringIndexValue4; case 5: return stringIndexValue5; default: throw new IndexOutOfBoundsException("string index value must be between 1 and 5 (included)"); } } public void setStringIndexLabel(final int index, final String label) { switch (index) { case 1: stringIndexLabel1 = label; break; case 2: stringIndexLabel2 = label; break; case 3: stringIndexLabel3 = label; break; case 4: stringIndexLabel4 = label; break; case 5: stringIndexLabel5 = label; break; default: throw new IndexOutOfBoundsException("string index label must be between 1 and 5 (included)"); } } @Override public String getStringIndexLabel(final int index) { switch (index) { case 1: return stringIndexLabel1; case 2: return stringIndexLabel2; case 3: return stringIndexLabel3; case 4: return stringIndexLabel4; case 5: return stringIndexLabel5; default: throw new IndexOutOfBoundsException("string index label must be between 1 and 5 (included)"); } } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + (archiveDate == null ? 0 : archiveDate.hashCode()); result = prime * result + (int) (callerId ^ callerId >>> 32); result = prime * result + (endDate == null ? 0 : endDate.hashCode()); result = prime * result + (lastUpdate == null ? 0 : lastUpdate.hashCode()); result = prime * result + (int) (processDefinitionId ^ processDefinitionId >>> 32); result = prime * result + (int) (rootProcessInstanceId ^ rootProcessInstanceId >>> 32); result = prime * result + (int) (sourceObjectId ^ sourceObjectId >>> 32); result = prime * result + (startDate == null ? 0 : startDate.hashCode()); result = prime * result + (int) (startedBy ^ startedBy >>> 32); result = prime * result + (int) (startedBySubstitute ^ startedBySubstitute >>> 32); result = prime * result + (state == null ? 0 : state.hashCode()); result = prime * result + stateId; return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } final ArchivedProcessInstanceImpl other = (ArchivedProcessInstanceImpl) obj; if (archiveDate == null) { if (other.archiveDate != null) { return false; } } else if (!archiveDate.equals(other.archiveDate)) { return false; } if (callerId != other.callerId) { return false; } if (endDate == null) { if (other.endDate != null) { return false; } } else if (!endDate.equals(other.endDate)) { return false; } if (lastUpdate == null) { if (other.lastUpdate != null) { return false; } } else if (!lastUpdate.equals(other.lastUpdate)) { return false; } if (processDefinitionId != other.processDefinitionId) { return false; } if (rootProcessInstanceId != other.rootProcessInstanceId) { return false; } if (sourceObjectId != other.sourceObjectId) { return false; } if (startDate == null) { if (other.startDate != null) { return false; } } else if (!startDate.equals(other.startDate)) { return false; } if (startedBy != other.startedBy) { return false; } if (startedBySubstitute != other.startedBySubstitute) { return false; } if (state == null) { if (other.state != null) { return false; } } else if (!state.equals(other.state)) { return false; } if (stateId != other.stateId) { return false; } return true; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/impl/internal/ProblemImpl.java ================================================ /** * 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.bpm.process.impl.internal; import org.bonitasoft.engine.bpm.process.Problem; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class ProblemImpl implements Problem { private static final long serialVersionUID = 267956968537265036L; private final Level level; private final String resourceId; private final String resource; private final String description; public ProblemImpl(final Level level, final String resourceId, final String resource, final String description) { super(); this.level = level; this.resourceId = resourceId; this.resource = resource; this.description = description; } public ProblemImpl(final Level level, final long resourceId, final String resource, final String description) { super(); this.level = level; this.resourceId = String.valueOf(resourceId); this.resource = resource; this.description = description; } @Override public Level getLevel() { return level; } @Override public String getResourceId() { return resourceId; } @Override public String getResource() { return resource; } @Override public String getDescription() { return description; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (description == null ? 0 : description.hashCode()); result = prime * result + (level == null ? 0 : level.hashCode()); result = prime * result + (resource == null ? 0 : resource.hashCode()); result = prime * result + (resourceId == null ? 0 : resourceId.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final ProblemImpl other = (ProblemImpl) obj; if (description == null) { if (other.description != null) { return false; } } else if (!description.equals(other.description)) { return false; } if (level != other.level) { return false; } if (resource == null) { if (other.resource != null) { return false; } } else if (!resource.equals(other.resource)) { return false; } if (resourceId == null) { if (other.resourceId != null) { return false; } } else if (!resourceId.equals(other.resourceId)) { return false; } return true; } @Override public String toString() { return "ProblemImpl [level=" + level + ", resourceId=" + resourceId + ", resource=" + resource + ", description=" + description + "]"; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/impl/internal/ProcessDeploymentInfoImpl.java ================================================ /** * 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.bpm.process.impl.internal; import java.util.Date; import org.bonitasoft.engine.bpm.internal.NamedElementImpl; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; /** * @author Baptiste Mesta * @author Hongwen Zang * @author Celine Souchet */ public class ProcessDeploymentInfoImpl extends NamedElementImpl implements ProcessDeploymentInfo { private static final long serialVersionUID = 1138141364845684630L; private final String version; private final String displayDescription; private final Date deploymentDate; private final long deployedBy; private final ConfigurationState configurationState; private final ActivationState activationState; private final long processId; private final String displayName; private final Date lastUpdateDate; private final String iconPath; private final String description; public ProcessDeploymentInfoImpl(final long id, final long processId, final String name, final String version, final String description, final Date deploymentDate, final long deployedBy, final ActivationState activationState, final ConfigurationState configurationState, final String displayName, final Date lastUpdateDate, final String iconPath, final String displayDescription) { super(name); this.description = description; this.displayDescription = displayDescription; setId(id); this.processId = processId; this.version = version; this.deploymentDate = deploymentDate; this.deployedBy = deployedBy; this.configurationState = configurationState; this.activationState = activationState; this.displayName = displayName; this.lastUpdateDate = lastUpdateDate; this.iconPath = iconPath; } @Override public String getVersion() { return version; } @Override public String getDisplayDescription() { return displayDescription; } @Override public Date getDeploymentDate() { return deploymentDate; } @Override public long getDeployedBy() { return deployedBy; } @Override public long getProcessId() { return processId; } @Override public String getDisplayName() { return displayName; } @Override public Date getLastUpdateDate() { return lastUpdateDate; } @Override public String getIconPath() { return iconPath; } @Override public String getDescription() { return description; } @Override public ConfigurationState getConfigurationState() { return configurationState; } @Override public ActivationState getActivationState() { return activationState; } @Override public String toString() { return "ProcessDeploymentInfoImpl{" + "version='" + version + '\'' + ", displayDescription='" + displayDescription + '\'' + ", deploymentDate=" + deploymentDate + ", deployedBy=" + deployedBy + ", configurationState=" + configurationState + ", activationState=" + activationState + ", processId=" + processId + ", displayName='" + displayName + '\'' + ", lastUpdateDate=" + lastUpdateDate + ", iconPath='" + iconPath + '\'' + ", description='" + description + '\'' + ", namedElement='" + super.toString() + '\'' + '}'; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/process/impl/internal/ProcessInstanceImpl.java ================================================ /** * 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.bpm.process.impl.internal; import java.util.Date; import lombok.ToString; import org.bonitasoft.engine.bpm.internal.NamedElementImpl; import org.bonitasoft.engine.bpm.process.ProcessInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ @ToString(callSuper = true) public class ProcessInstanceImpl extends NamedElementImpl implements ProcessInstance { private static final long serialVersionUID = 7745012384930764581L; private String state; private Date startDate; private long startedBy; private long startedBySubstitute; private Date endDate; private Date lastUpdate; private long processDefinitionId; private String description; private long rootProcessInstanceId; private long callerId; private String stringIndex1; private String stringIndex2; private String stringIndex3; private String stringIndex4; private String stringIndex5; private String stringIndexLabel1; private String stringIndexLabel2; private String stringIndexLabel3; private String stringIndexLabel4; private String stringIndexLabel5; public ProcessInstanceImpl(final String name) { super(name); } @Override public String getState() { return state; } public void setState(final String state) { this.state = state; } @Override public Date getStartDate() { return startDate; } public void setStartDate(final Date startDate) { this.startDate = startDate; } @Override public long getStartedBy() { return startedBy; } public void setStartedBy(final long startedBy) { this.startedBy = startedBy; } @Override public long getStartedBySubstitute() { return startedBySubstitute; } public void setStartedBySubstitute(final long startedBySubstitute) { this.startedBySubstitute = startedBySubstitute; } @Override public Date getEndDate() { return endDate; } public void setEndDate(final Date endDate) { this.endDate = endDate; } @Override public Date getLastUpdate() { return lastUpdate; } public void setLastUpdate(final Date lastUpdate) { this.lastUpdate = lastUpdate; } @Override public long getProcessDefinitionId() { return processDefinitionId; } public void setProcessDefinitionId(final long processDefinitionId) { this.processDefinitionId = processDefinitionId; } @Override public String getDescription() { return description; } public void setDescription(final String description) { this.description = description; } @Override public long getRootProcessInstanceId() { return rootProcessInstanceId; } public void setRootProcessInstanceId(final long rootProcessInstanceId) { this.rootProcessInstanceId = rootProcessInstanceId; } @Override public long getCallerId() { return callerId; } public void setCallerId(final long callerId) { this.callerId = callerId; } @Override public String getStringIndex1() { return stringIndex1; } public void setStringIndex1(final String stringIndex1) { this.stringIndex1 = stringIndex1; } @Override public String getStringIndex2() { return stringIndex2; } public void setStringIndex2(final String stringIndex2) { this.stringIndex2 = stringIndex2; } @Override public String getStringIndex3() { return stringIndex3; } public void setStringIndex3(final String stringIndex3) { this.stringIndex3 = stringIndex3; } @Override public String getStringIndex4() { return stringIndex4; } public void setStringIndex4(final String stringIndex4) { this.stringIndex4 = stringIndex4; } @Override public String getStringIndex5() { return stringIndex5; } public void setStringIndex5(final String stringIndex5) { this.stringIndex5 = stringIndex5; } public String getStringIndexLabel1() { return stringIndexLabel1; } public void setStringIndexLabel1(final String stringIndexLabel1) { this.stringIndexLabel1 = stringIndexLabel1; } public String getStringIndexLabel2() { return stringIndexLabel2; } public void setStringIndexLabel2(final String stringIndexLabel2) { this.stringIndexLabel2 = stringIndexLabel2; } public String getStringIndexLabel3() { return stringIndexLabel3; } public void setStringIndexLabel3(final String stringIndexLabel3) { this.stringIndexLabel3 = stringIndexLabel3; } public String getStringIndexLabel4() { return stringIndexLabel4; } public void setStringIndexLabel4(final String stringIndexLabel4) { this.stringIndexLabel4 = stringIndexLabel4; } public String getStringIndexLabel5() { return stringIndexLabel5; } public void setStringIndexLabel5(final String stringIndexLabel5) { this.stringIndexLabel5 = stringIndexLabel5; } public void setStringIndexLabel(final int index, final String label) { switch (index) { case 1: stringIndexLabel1 = label; break; case 2: stringIndexLabel2 = label; break; case 3: stringIndexLabel3 = label; break; case 4: stringIndexLabel4 = label; break; case 5: stringIndexLabel5 = label; break; default: throw new IndexOutOfBoundsException("search key label must be between 1 and 5 (included)"); } } @Override public String getStringIndexLabel(final int index) { switch (index) { case 1: return stringIndexLabel1; case 2: return stringIndexLabel2; case 3: return stringIndexLabel3; case 4: return stringIndexLabel4; case 5: return stringIndexLabel5; default: throw new IndexOutOfBoundsException("search key label must be between 1 and 5 (included)"); } } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/supervisor/ProcessSupervisor.java ================================================ /** * 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.bpm.supervisor; import org.bonitasoft.engine.bpm.BonitaObject; /** * A supervisor of a process is responsible for what happens to the process. A supervisor can see * the tasks in the process, and can carry out process administration. A supervisor is defined in a ProcessSupervisor * object as a mapping of users, groups, or roles to the process supervisor (similar to actor mapping). * A process has one ProcessSupervisor; however, as this can be mapped to several users, either explicitly or by * mapping groups or roles, the process can be supervised by several people. * * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Celine Souchet * @author Emmanuel Duchastenier */ public interface ProcessSupervisor extends BonitaObject { long getSupervisorId(); long getProcessDefinitionId(); long getUserId(); long getGroupId(); long getRoleId(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/supervisor/ProcessSupervisorSearchDescriptor.java ================================================ /** * 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.bpm.supervisor; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class ProcessSupervisorSearchDescriptor { public static final String USER_ID = "userId"; public static final String GROUP_ID = "groupId"; public static final String ROLE_ID = "roleId"; public static final String ID = "id"; public static final String PROCESS_DEFINITION_ID = "processDefId"; // public static final String USER_FISRT_NAME = "firstName"; // // public static final String USER_LAST_NAME = "lastName"; // // public static final String USERNAME = "userName"; // // public static final String GROUP_NAME = "name"; // // public static final String GROUP_PARENT_PATH = "parentPath"; // // public static final String ROLE_NAME = "name"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/supervisor/SupervisorNotFoundException.java ================================================ /** * Copyright (C) 2021 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.bpm.supervisor; import org.bonitasoft.engine.exception.NotFoundException; /** * @author Anthony Birembaut */ public class SupervisorNotFoundException extends NotFoundException { private static final long serialVersionUID = 1829055978870789719L; /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public SupervisorNotFoundException(final String message) { super(message); } /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public SupervisorNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/supervisor/impl/ProcessSupervisorImpl.java ================================================ /** * 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.bpm.supervisor.impl; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor; /** * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class ProcessSupervisorImpl implements ProcessSupervisor { private static final long serialVersionUID = 1034289223722146165L; private long supervisorId; private long processDefinitionId; private long userId; private long groupId; private long roleId; public ProcessSupervisorImpl(final long supervisorId, final long processDefinitionId) { super(); this.supervisorId = supervisorId; this.processDefinitionId = processDefinitionId; } public ProcessSupervisorImpl() { } @Override public long getSupervisorId() { return supervisorId; } @Override public long getProcessDefinitionId() { return processDefinitionId; } @Override public long getUserId() { return userId; } public void setId(final long supervisorId) { this.supervisorId = supervisorId; } public void setProcessDefinitionId(final long processDefinitionId) { this.processDefinitionId = processDefinitionId; } public void setUserId(final long userId) { this.userId = userId; } @Override public long getGroupId() { return groupId; } public void setGroupId(final long groupId) { this.groupId = groupId; } @Override public long getRoleId() { return roleId; } public void setRoleId(final long roleId) { this.roleId = roleId; } public void setSupervisorId(final long supervisorId) { this.supervisorId = supervisorId; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/bpm/supervisor/package-info.java ================================================ /** * Copyright (C) 2015 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.bpm.supervisor; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/AbstractApplicationCreator.java ================================================ /** * 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.business.application; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.profile.Profile; /** * Describes the information about an {@link IApplication} to be created * * @see IApplication * @deprecated This class should no longer be used. Since 9.0.0, Applications should be created at startup. * @param the concrete subtype of {@link AbstractApplicationCreator} (for returning self, subclasses must ensure to * use the concrete instance) */ @Deprecated(since = "10.2.0") public abstract class AbstractApplicationCreator> implements Serializable { private static final long serialVersionUID = -8837280485050745580L; private final Map fields; /** * Creates an instance of ApplicationCreator containing mandatory information. *

The created {@link Application} will used the default layout.

* * @param token the {@code Application} token. The token will be part of application URL. It cannot be null or empty * and should contain only alpha numeric * characters and the following special characters '-', '.', '_' or '~'. In addition, the following words are * reserved key words and cannot be used * as token: 'api', 'content', 'theme'. * @param displayName the Application display name. It cannot be null or empty * @param version the Application version * @see Application */ public AbstractApplicationCreator(final String token, final String displayName, final String version) { fields = new HashMap<>(3); fields.put(ApplicationField.TOKEN, token); fields.put(ApplicationField.VERSION, version); fields.put(ApplicationField.DISPLAY_NAME, displayName); } /** * Retrieves the {@link Application} token * * @return the Application token * @see Application */ public String getToken() { return (String) fields.get(ApplicationField.TOKEN); } /** * Defines the {@link Application} description and returns the current ApplicationCreator * * @param description the Application description * @return the current ApplicationCreator * @see Application */ public T setDescription(final String description) { fields.put(ApplicationField.DESCRIPTION, description); return (T) this; } /** * Defines the {@link Application} icon path and returns the current ApplicationCreator * * @param iconPath the Application icon path * @return the current ApplicationCreator * @see Application * @deprecated since 7.13.0, use {@link #setIcon(String, byte[])} */ @Deprecated(since = "7.13.0") public T setIconPath(final String iconPath) { fields.put(ApplicationField.ICON_PATH, iconPath); return (T) this; } /** * Defines the icon for the {@link Application}. * * @param fileName of the icon * @param content of the icon * @return the current builder * @since 7.13.0 */ public T setIcon(String fileName, byte[] content) { fields.put(ApplicationField.ICON_FILE_NAME, fileName); fields.put(ApplicationField.ICON_CONTENT, content); return (T) this; } /** * Defines the identifier of the {@link Profile} related to this {@link Application} and returns the current * ApplicationCreator * * @param profileId the Profile identifier * @return the current ApplicationCreator * @see Application * @see Profile */ public T setProfileId(final Long profileId) { fields.put(ApplicationField.PROFILE_ID, profileId); return (T) this; } /** * Retrieves all fields defined in this ApplicationCreator * * @return a {@link Map}<{@link ApplicationField}, {@link Serializable}> containing all fields defined in this * ApplicationCreator * @see ApplicationField */ public Map getFields() { return fields; } /** Test whether application is an application link. */ public abstract boolean isLink(); @Override public int hashCode() { final int prime = 31; int result = getClass().hashCode(); result = prime * result + (fields == null ? 0 : fields.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final AbstractApplicationCreator other = (AbstractApplicationCreator) obj; if (fields == null) { if (other.fields != null) { return false; } } else if (!fields.equals(other.fields)) { return false; } return true; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/AbstractApplicationUpdater.java ================================================ /** * 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.business.application; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.profile.Profile; /** * Allows to define which {@link IApplication} fields will be updated * * @see IApplication * @deprecated This class should no longer be used. Since 9.0.0, Applications should be updated at startup. * @param the concrete subtype of {@link AbstractApplicationUpdater} (for returning self, subclasses must ensure to * use the concrete instance) */ @Deprecated(since = "10.2.0") public abstract class AbstractApplicationUpdater> implements Serializable { private static final long serialVersionUID = -5301609836484089900L; private final Map fields; /** * Creates an instance of ApplicationUpdater */ public AbstractApplicationUpdater() { fields = new HashMap<>(8); } /** * Retrieves all fields to be updated * * @return a {@link Map}<{@link ApplicationField}, {@link Serializable}> containing all fields to be updated * @see ApplicationField */ public Map getFields() { return fields; } /** * Defines the new value for the {@link IApplication} token. It cannot be empty or null and should contain only * alpha numeric characters and the following special characters '-', '.', '_' or '~'. * * @param token the new value for the {@code Application} token * @return the current {@code ApplicationUpdater} * @see IApplication */ public T setToken(final String token) { fields.put(ApplicationField.TOKEN, token); return (T) this; } /** * Defines the new value for the {@link IApplication} display name. It cannot be empty or null. * * @param displayName the new value for the {@code Application} display name * @return the current {@code ApplicationUpdater} * @see IApplication */ public T setDisplayName(final String displayName) { fields.put(ApplicationField.DISPLAY_NAME, displayName); return (T) this; } /** * Defines the new value for the {@link IApplication} version * * @param version the new value for the {@code Application} version * @return the current {@code ApplicationUpdater} * @see IApplication */ public T setVersion(final String version) { fields.put(ApplicationField.VERSION, version); return (T) this; } /** * Defines the new value for the {@link IApplication} description * * @param description the new value for the {@code Application} description * @return the current {@code ApplicationUpdater} * @see IApplication */ public T setDescription(final String description) { fields.put(ApplicationField.DESCRIPTION, description); return (T) this; } /** * Defines the new value for the {@link IApplication} icon path * * @param iconPath the new value for the {@code Application} icon path * @return the current {@code ApplicationUpdater} * @see IApplication * @deprecated since 7.13.0, use {@link #setIcon(String, byte[])} */ @Deprecated(since = "7.13.0") public T setIconPath(final String iconPath) { fields.put(ApplicationField.ICON_PATH, iconPath); return (T) this; } /** * Defines the new icon for the {@link IApplication}. *

* The icons are accessible using {@link org.bonitasoft.engine.api.ApplicationAPI#getIconOfApplication(long)} * Calling that method with {@code setIcon(null, null)} will remove the icon. * * @param iconFileName of the icon * @param content of the icon * @return the current builder * @since 7.13.0 */ public T setIcon(String iconFileName, byte[] content) { fields.put(ApplicationField.ICON_FILE_NAME, iconFileName); fields.put(ApplicationField.ICON_CONTENT, content); return (T) this; } /** * Defines the new value for the {@link IApplication} state * * @param state the new value for the {@code Application} state * @return the current {@code ApplicationUpdater} * @see IApplication */ public T setState(final String state) { fields.put(ApplicationField.STATE, state); return (T) this; } /** * Defines the identifier of the new {@link Profile} associated to the {@link IApplication} * * @param profileId the identifier of {@code Profile} associated to the {@code Application} * @return the current {@code ApplicationUpdater} * @see IApplication * @see Profile */ public T setProfileId(final Long profileId) { fields.put(ApplicationField.PROFILE_ID, profileId); return (T) this; } /** * Determines if this updater has at least one field to update * * @return true if there is at least one field to update; false otherwise */ public boolean hasFields() { return !getFields().isEmpty(); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationCreator.java ================================================ /** * 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.business.application; /** * Describes the information about a Legacy {@link Application} to be created * * @author Elias Ricken de Medeiros * @see Application * @since 7.0.0 * @deprecated This class should no longer be used. Since 9.0.0, Applications should be created at startup. */ @Deprecated(since = "10.2.0") public class ApplicationCreator extends AbstractApplicationCreator { private static final long serialVersionUID = -916041825489100271L; /** * Creates an instance of ApplicationCreator containing mandatory information. *

The created {@link Application} will used the default layout.

* * @param token the {@code Application} token. The token will be part of application URL. It cannot be null or empty * and should contain only alpha numeric * characters and the following special characters '-', '.', '_' or '~'. In addition, the following words are * reserved key words and cannot be used * as token: 'api', 'content', 'theme'. * @param displayName the Application display name. It cannot be null or empty * @param version the Application version * @see Application */ public ApplicationCreator(final String token, final String displayName, final String version) { super(token, displayName, version); } @Override public boolean isLink() { return false; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationField.java ================================================ /** * 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.business.application; import org.bonitasoft.engine.profile.Profile; /** * Contains fields that can be used by {@link ApplicationCreator} and {@link ApplicationUpdater} * * @author Elias Ricken de Medeiros * @see ApplicationCreator * @see ApplicationUpdater * @since 7.0.0 * @deprecated This class should no longer be used. Since 9.0.0, Applications should be updated at startup. */ @Deprecated(since = "10.2.0") public enum ApplicationField { /** * References the {@link IApplication} token * * @see IApplication */ TOKEN, /** * References the {@link IApplication} display name * * @see IApplication */ DISPLAY_NAME, /** * References the {@link IApplication} version * * @see IApplication */ VERSION, /** * References the {@link IApplication} description * * @see IApplication */ DESCRIPTION, /** * References the {@link IApplication} icon path * * @see IApplication * @deprecated since 7.13.0, use {@link #ICON_CONTENT} and {@link #ICON_FILE_NAME} instead */ @Deprecated(since = "7.13.0") ICON_PATH, /** * byte array content of the icon of the {@link IApplication} * * @since 7.13.0 */ ICON_CONTENT, /** * Filename of the icon of the {@link IApplication} * * @since 7.13.0 */ ICON_FILE_NAME, /** * References the {@link IApplication} state * * @see IApplication */ STATE, /** * References the identifier of the {@link Profile} associated to the {@link IApplication} * * @see IApplication * @see Profile */ PROFILE_ID, /** * References the identifier of the {@link ApplicationPage} defined as the {@link Application} home page * * @see org.bonitasoft.engine.business.application.ApplicationPage * @see org.bonitasoft.engine.business.application.Application */ HOME_PAGE_ID(Application.class), /** * References the identifier of the {@link org.bonitasoft.engine.page.Page} defined as the {@link Application} * layout. * * @see org.bonitasoft.engine.page.Page * @see org.bonitasoft.engine.business.application.Application * @since 7.0.0 */ LAYOUT_ID(Application.class), /** * References the identifier of the {@link org.bonitasoft.engine.page.Page} defined as the {@link Application} * theme. * * @see org.bonitasoft.engine.page.Page * @see org.bonitasoft.engine.business.application.Application * @since 7.0.0 */ THEME_ID(Application.class); /** The class which support this type of field */ private Class supportingClass; /** * Private Constructor for fields which are suitable for all application types. */ private ApplicationField() { this(IApplication.class); } /** * Private Constructor for fields which are suitable only for a particular application type (e.g. Legacy, but not * Link). * * @param appropriateClazz the class which support this type of field. */ private ApplicationField(Class appropriateClazz) { supportingClass = appropriateClazz; } /** * Test whether this application field is suitable for a particular application type * * @param clazz the application type to test (usually {@link Application} for legacy applications of * {@link ApplicationLink}) * @return */ public boolean isForClass(Class clazz) { return supportingClass.isAssignableFrom(clazz); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationImportPolicy.java ================================================ /** * 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.business.application; /** * Contains available policies during {@link Application}s import * * @author Elias Ricken de Medeiros * @see org.bonitasoft.engine.business.application.Application */ public enum ApplicationImportPolicy { /** * Import will fail on import an existent {@link org.bonitasoft.engine.business.application.Application} or * {@link org.bonitasoft.engine.business.application.ApplicationPage} */ FAIL_ON_DUPLICATES, /** * Import will replace the applications that exist with the same token. */ REPLACE_DUPLICATES } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationLinkCreator.java ================================================ /** * 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.business.application; import java.io.Serializable; import java.util.Map; /** * Describes the information about an {@link ApplicationLink} to be created * * @see ApplicationLink * @since 10.2.0 * @deprecated This class should no longer be used. Since 9.0.0, Applications should be created at startup. */ @Deprecated(since = "10.2.0") public class ApplicationLinkCreator extends AbstractApplicationCreator { private static final long serialVersionUID = 5045658936235401181L; private transient Map fieldsCheckedMap; /** * Creates an instance of ApplicationCreator containing mandatory information. * * @param token the {@code ApplicationLink} token. The token will be part of application URL. It cannot be null or * empty and should contain only alpha numeric * characters and the following special characters '-', '.', '_' or '~'. In addition, the following words are * reserved key words and cannot be used * as token: 'api', 'content', 'theme'. * @param displayName the ApplicationLink display name. It cannot be null or empty * @param version the ApplicationLink version * @see ApplicationLink */ public ApplicationLinkCreator(final String token, final String displayName, final String version) { super(token, displayName, version); } @Override public boolean isLink() { return true; } @Override public Map getFields() { // make sure no field for Legacy Application and not suitable for Application Link will get inserted there if (fieldsCheckedMap == null) { fieldsCheckedMap = new CheckedApplicationFieldMap(super.getFields(), k -> k.isForClass(ApplicationLink.class)); } return fieldsCheckedMap; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationLinkUpdater.java ================================================ /** * 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.business.application; import java.io.Serializable; import java.util.Map; /** * Allows to define which {@link ApplicationLink} fields will be updated * * @see ApplicationLink * @since 10.2.0 * @deprecated This class should no longer be used. Since 9.0.0, Applications should be updated at startup. */ @Deprecated(since = "10.2.0") public class ApplicationLinkUpdater extends AbstractApplicationUpdater { private static final long serialVersionUID = 1732835829535757371L; private transient Map fieldsCheckedMap; @Override public Map getFields() { // make sure no field for Legacy Application and not suitable for Application Link will get inserted there if (fieldsCheckedMap == null) { fieldsCheckedMap = new CheckedApplicationFieldMap(super.getFields(), k -> k.isForClass(ApplicationLink.class)); } return fieldsCheckedMap; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationMenuCreator.java ================================================ /** * 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.business.application; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Describes the information about an {@link ApplicationMenu} to be created * * @author Elias Ricken de Medeiros * @since 6.4 * @see ApplicationMenu */ public class ApplicationMenuCreator implements Serializable { private static final long serialVersionUID = 5253969343647340983L; private final Map fields; /** * Creates an instance of {@code ApplicationMenuCreator} * * @param applicationId the identifier of related {@link org.bonitasoft.engine.business.application.Application} * @param displayName the {@link org.bonitasoft.engine.business.application.ApplicationMenu} display name * @param applicationPageId the identifier of related * {@link org.bonitasoft.engine.business.application.ApplicationPage} * @see ApplicationMenu */ public ApplicationMenuCreator(final Long applicationId, final String displayName, final Long applicationPageId) { this(applicationId, displayName); fields.put(ApplicationMenuField.APPLICATION_PAGE_ID, applicationPageId); } /** * Creates an instance of {@code ApplicationMenuCreator} * * @param applicationId the identifier of related {@link Application} * @param displayName the {@link ApplicationMenu} display name * @see ApplicationMenu * @see org.bonitasoft.engine.business.application.Application */ public ApplicationMenuCreator(final Long applicationId, final String displayName) { fields = new HashMap(4); fields.put(ApplicationMenuField.DISPLAY_NAME, displayName); fields.put(ApplicationMenuField.APPLICATION_ID, applicationId); } /** * Defines the identifier of parent {@link ApplicationMenu} * * @param parentId the identifier of parent {@code ApplicationMenu} * @return */ public ApplicationMenuCreator setParentId(final long parentId) { fields.put(ApplicationMenuField.PARENT_ID, parentId); return this; } /** * Retrieves the identifier of the parent {@link ApplicationMenu}. If no parent is defined this method will return * null. * * @return the identifier of the parent {@code ApplicationMenu} or null if no parent is defined * @see org.bonitasoft.engine.business.application.ApplicationMenu */ public Long getParentId() { return (Long) fields.get(ApplicationMenuField.PARENT_ID); } /** * Retrieves all fields defined in this {@code ApplicationMenuCreator} * * @return a {@link Map}<{@link ApplicationMenuField}, {@link Serializable}> containing all fields defined in this * {@code ApplicationMenuCreator} */ public Map getFields() { return Collections.unmodifiableMap(fields); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationMenuField.java ================================================ /** * 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.business.application; /** * Contains fields used by {@link ApplicationMenuCreator} and {@link ApplicationMenuUpdater} * * @author Elias Ricken de Medeiros * @since 6.4 */ public enum ApplicationMenuField { /** * References the {@link ApplicationMenu} display name * * @see ApplicationMenu */ DISPLAY_NAME, /** * References the identifier of {@link Application} related to the {@link ApplicationMenu} * * @see ApplicationMenu * @see Application */ APPLICATION_ID, /** * References the identifier of {@link ApplicationPage} related to the {@link ApplicationMenu} * * @see ApplicationMenu * @see ApplicationPage */ APPLICATION_PAGE_ID, /** * References the identifier of parent {@link ApplicationMenu} * * @see ApplicationMenu */ PARENT_ID, /** * References the {@link ApplicationMenu} index * * @see ApplicationMenu */ INDEX; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationMenuNotFoundException.java ================================================ /** * 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.business.application; import org.bonitasoft.engine.exception.NotFoundException; /** * @author Elias Ricken de Medeiros */ public class ApplicationMenuNotFoundException extends NotFoundException { private static final long serialVersionUID = -5011088434149162619L; public ApplicationMenuNotFoundException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationMenuSearchDescriptor.java ================================================ /** * 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.business.application; import org.bonitasoft.engine.search.SearchOptions; /** * Defines the fields that can be used in the {@link SearchOptions} when searching for {@link ApplicationMenu}s * * @author Elias Ricken de Medeiros * @since 6.4 * @see SearchOptions * @see ApplicationMenu * @see ApplicationAPI#searchApplicationMenus(SearchOptions) */ public class ApplicationMenuSearchDescriptor { /** * Used to filter or order by {@link ApplicationMenu} identifier * * @see ApplicationMenu */ public static final String ID = "id"; /** * Used to filter or order by {@link ApplicationMenu} display name * * @see ApplicationMenu */ public static final String DISPLAY_NAME = "displayName"; /** * Used to filter or order by the identifier of {@link ApplicationPage} related to the {@link ApplicationMenu} * * @see ApplicationMenu * @see ApplicationPage */ public static final String APPLICATION_PAGE_ID = "applicationPageId"; /** * Used to filter or order by the identifier of {@link Application} containing the {@link ApplicationMenu} * * @see ApplicationMenu * @see Application */ public static final String APPLICATION_ID = "applicationId"; /** * Used to filter or order by {@link ApplicationMenu} index * * @see ApplicationMenu */ public static final String INDEX = "index"; /** * Used to filter or order by the identifier of parent {@link ApplicationMenu} * * @see ApplicationMenu */ public static final String PARENT_ID = "parentId"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationMenuUpdater.java ================================================ /** * 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.business.application; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * @author Elias Ricken de Medeiros */ public class ApplicationMenuUpdater implements Serializable { private final Map fields; public ApplicationMenuUpdater() { fields = new HashMap(4); } /** * Retrieves all fields to be updated * * @return a {@link Map}<{@link ApplicationMenuField}, {@link Serializable}> containing all fields to be updated * @see ApplicationMenuField */ public Map getFields() { return fields; } /** * Defines the identifier of the new {@link ApplicationPage} related to the * {@link org.bonitasoft.engine.business.application.ApplicationMenu}. Use * {@code null} to reference no {@code ApplicationPage}. * * @param applicationPageId the identifier of new related {@code ApplicationPage} * @return the current {@code ApplicationMenuUpdater} * @see org.bonitasoft.engine.business.application.ApplicationPage * @see org.bonitasoft.engine.business.application.ApplicationMenu */ public ApplicationMenuUpdater setApplicationPageId(final Long applicationPageId) { fields.put(ApplicationMenuField.APPLICATION_PAGE_ID, applicationPageId); return this; } /** * Defines the new value for the {@link ApplicationMenu} display name * * @param displayName the new value for the {@code ApplicationMenu} display name * @return the current {@code ApplicationMenuUpdater} * @see ApplicationMenu */ public ApplicationMenuUpdater setDisplayName(final String displayName) { fields.put(ApplicationMenuField.DISPLAY_NAME, displayName); return this; } /** * Defines the new value for the {@link ApplicationMenu} index * * @param index the new value for the {@code ApplicationMenu} index * @return the current {@code ApplicationMenuUpdater} * @see ApplicationMenu */ public ApplicationMenuUpdater setIndex(final int index) { fields.put(ApplicationMenuField.INDEX, index); return this; } /** * Defines the identifier of the new parent {@link ApplicationMenu}.Use {@code null} to reference no * {@code ApplicationMenu} * * @param parentId the identifier of the new parent {@link ApplicationMenu} * @return the current {@code ApplicationMenuUpdater} * @see ApplicationMenu */ public ApplicationMenuUpdater setParentId(final Long parentId) { fields.put(ApplicationMenuField.PARENT_ID, parentId); return this; } /** * Determines if this updater has at least one field to update * * @return true if there is at least one field to update; false otherwise */ public boolean hasFields() { return !getFields().isEmpty(); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationNotFoundException.java ================================================ /** * 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.business.application; import org.bonitasoft.engine.exception.NotFoundException; /** * @author Elias Ricken de Medeiros */ public class ApplicationNotFoundException extends NotFoundException { private static final long serialVersionUID = -4073233652785845623L; public ApplicationNotFoundException(final long applicationId) { super("Unable to find the application with id '" + applicationId + "'"); } public ApplicationNotFoundException(final String applicationToken) { super("Unable to find the application with token '" + applicationToken + "'"); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationPageNotFoundException.java ================================================ /** * 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.business.application; import org.bonitasoft.engine.exception.NotFoundException; /** * @author Elias Ricken de Medeiros */ public class ApplicationPageNotFoundException extends NotFoundException { private static final long serialVersionUID = -5011088434149162619L; public ApplicationPageNotFoundException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationPageSearchDescriptor.java ================================================ /** * 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.business.application; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.search.SearchOptions; /** * Defines the fields that can be used in the {@link SearchOptions} when searching for {@link ApplicationPage}s * * @author Elias Ricken de Medeiros * @since 6.4 * @see ApplicationPage * @see SearchOptions * @see ApplicationAPI#searchApplicationPages(SearchOptions) */ public class ApplicationPageSearchDescriptor { /** * Used to filter or order by {@link ApplicationPage} identifier * * @see ApplicationPage */ public static final String ID = "id"; /** * Used to filter or order by {@link ApplicationPage} token * * @see ApplicationPage */ public static final String TOKEN = "token"; /** * Used to filter or order by the identifier of {@link Application} associated to the {@link ApplicationPage} * * @see ApplicationPage * @see Application */ public static final String APPLICATION_ID = "applicationId"; /** * Used to filter or order by the identifier of {@link Page} referenced by the {@link ApplicationPage} * * @see ApplicationPage * @see Page */ public static final String PAGE_ID = "pageId"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationSearchDescriptor.java ================================================ /** * 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.business.application; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.search.SearchOptions; /** * Defines the fields that can be used in the {@link org.bonitasoft.engine.search.SearchOptions} when searching for * {@link Application}s * * @author Elias Ricken de Medeiros * @since 6.4 * @see org.bonitasoft.engine.search.SearchOptions * @see Application * @see org.bonitasoft.engine.api.ApplicationAPI#searchApplications(SearchOptions) */ public class ApplicationSearchDescriptor { /** * Used to filter or order by the Application identifier */ public static final String ID = "id"; /** * Used to filter or order by the Application token */ public static final String TOKEN = "token"; /** * Used to filter or order by the Application display name */ public static final String DISPLAY_NAME = "displayName"; /** * Used to filter or order by the Application version */ public static final String VERSION = "version"; /** * Used to filter or order by the Application icon path */ public static final String ICON_PATH = "iconPath"; /** * Used to filter or order by the Application creation date */ public static final String CREATION_DATE = "creationDate"; /** * Used to filter or order by the identifier of the user that created the Application */ public static final String CREATED_BY = "createdBy"; /** * Used to filter or order by the Application last update date */ public static final String LAST_UPDATE_DATE = "lastUpdateDate"; /** * Used to filter or order by the identifier of the user that last updated the Application */ public static final String UPDATED_BY = "updatedBy"; /** * Used to filter or order by the Application state. The possible values are * {@link ApplicationState#ACTIVATED#name()} and * {@link ApplicationState#DEACTIVATED#name()} * * @see ApplicationState */ public static final String STATE = "state"; /** * Used to filter or order by the identifier of {@link org.bonitasoft.engine.profile.Profile} associated to the * {@link org.bonitasoft.engine.business.application.Application}. * * @see org.bonitasoft.engine.profile.Profile * @see org.bonitasoft.engine.business.application.Application */ public static final String PROFILE_ID = "profileId"; /** * Used to filter or order by the identifier of {@link Page} set as {@link Application} layout. * * @since 7.0.0 * @see Page * @see Application */ public static final String LAYOUT_ID = "layoutId"; /** * Used to filter or order by the identifier of {@link Page} set as {@link Application} theme. * * @since 7.0.0 * @see Page * @see Application */ public static final String THEME_ID = "themeId"; /** * Used to filter by the identifier of {@link User} that has access to the {@link Application}. * * @since 7.13.0 * @see User * @see Application */ public static final String USER_ID = "userId"; /** * Used to filter or order Application items by legacy or link criteria * * @since 10.2.0 for some Subscription editions only */ public static final String LINK = "link"; // keeping this constant here for convenience reasons // having a subscription descriptor with just this field would not be user-friendly } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationUpdater.java ================================================ /** * 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.business.application; /** * Allows to define which {@link Application} fields will be updated * * @author Elias Ricken de Medeiros * @see Application * @since 7.0.0 * @deprecated This class should no longer be used. Since 9.0.0, Applications should be updated at startup. */ @Deprecated(since = "10.2.0") public class ApplicationUpdater extends AbstractApplicationUpdater { private static final long serialVersionUID = 4565052647320534796L; /** * Defines the identifier of the new {@link org.bonitasoft.engine.business.application.ApplicationPage} defined as * the {@link Application} home page * * @param applicationPageId the identifier of {@code ApplicationPage} associated to the {@code Application} * @return the current {@code ApplicationUpdater} * @see Application * @see org.bonitasoft.engine.business.application.ApplicationPage */ public ApplicationUpdater setHomePageId(final Long applicationPageId) { getFields().put(ApplicationField.HOME_PAGE_ID, applicationPageId); return this; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/CheckedApplicationFieldMap.java ================================================ /** * 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.business.application; import java.io.Serializable; import java.text.MessageFormat; import java.util.Collection; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.function.Predicate; /** * A Map which first checks that the key {@link ApplicationField} is correct. */ class CheckedApplicationFieldMap implements Map { private final Map m; private final Predicate isValidKey; CheckedApplicationFieldMap(Map m, Predicate isValidKey) { this.m = Objects.requireNonNull(m); this.isValidKey = Objects.requireNonNull(isValidKey); } private ApplicationField checkKey(ApplicationField key) { if (!isValidKey.test(key)) { throw new IllegalArgumentException( MessageFormat.format("Attempt to insert {0} in a specialized map which does not support it.", key.name())); } return key; } @Override public int size() { return m.size(); } @Override public boolean isEmpty() { return m.isEmpty(); } @Override public boolean containsKey(Object key) { return m.containsKey(key); } @Override public boolean containsValue(Object value) { return m.containsValue(value); } @Override public Serializable get(Object key) { return m.get(key); } @Override public Serializable put(ApplicationField key, Serializable value) { return m.put(checkKey(key), value); } @Override public Serializable remove(Object key) { return m.remove(key); } @Override public void putAll(Map map) { map.keySet().forEach(this::checkKey); m.putAll(map); } @Override public void clear() { m.clear(); } @Override public Set keySet() { return m.keySet(); } @Override public Collection values() { return m.values(); } @Override public Set> entrySet() { return m.entrySet(); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/InternalProfiles.java ================================================ /** * Copyright (C) 2021 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.business.application; public enum InternalProfiles { INTERNAL_PROFILE_SUPER_ADMIN("_BONITA_INTERNAL_PROFILE_SUPER_ADMIN", ApplicationVisibility.TECHNICAL_USER), INTERNAL_PROFILE_ALL("_BONITA_INTERNAL_PROFILE_ALL", ApplicationVisibility.ALL); InternalProfiles(String profileName, ApplicationVisibility applicationVisibility) { this.profileName = profileName; this.applicationVisibility = applicationVisibility; } private final String profileName; private final ApplicationVisibility applicationVisibility; public String getProfileName() { return profileName; } public ApplicationVisibility getApplicationVisibility() { return applicationVisibility; } public static ApplicationVisibility getApplicationVisibilityByProfileName(String profileName) { for (InternalProfiles internalProfile : InternalProfiles.values()) { if (internalProfile.getProfileName().equals(profileName)) { return internalProfile.applicationVisibility; } } return ApplicationVisibility.RESTRICTED; } public static InternalProfiles getInternalProfileByProfileName(String profileName) { for (InternalProfiles internalProfile : InternalProfiles.values()) { if (internalProfile.getProfileName().equals(profileName)) { return internalProfile; } } return null; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/BusinessDataCrudOperationException.java ================================================ /** * Copyright (C) 2025 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.business.data; /** * Exception indicating an error occurred during CRUD (Create, Read, Update, Delete) operations on business data. * This exception is a specific type of {@link BusinessDataRepositoryException} and is used to signal issues * related to the manipulation of business data records in a repository. * * @author Emmanuel Duchastenier */ public class BusinessDataCrudOperationException extends BusinessDataRepositoryException { public BusinessDataCrudOperationException(String message) { super(message); } public BusinessDataCrudOperationException(Throwable cause) { super(cause); } public BusinessDataCrudOperationException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/BusinessDataNotFoundException.java ================================================ /** * Copyright (C) 2025 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.business.data; import org.bonitasoft.engine.exception.NotFoundException; public class BusinessDataNotFoundException extends NotFoundException { public BusinessDataNotFoundException(String message) { super(message); } public BusinessDataNotFoundException(Throwable cause) { super(cause); } public BusinessDataNotFoundException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/BusinessDataReference.java ================================================ /** * 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.business.data; import java.io.Serializable; /** * A BusinessDataReference defines all needed fields to retrieve a business data. * * @author Matthieu Chaffotte */ public interface BusinessDataReference extends Serializable { /** * Returns the name of the business data. * * @return the name of the business data */ String getName(); /** * Returns the type of the business data. * * @return the type of the business data */ String getType(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/BusinessDataRepositoryDeploymentException.java ================================================ /** * 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.business.data; /** * Thrown to indicate that the Business Data Model deployment failed. * * @author Colin Puy * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public class BusinessDataRepositoryDeploymentException extends BusinessDataRepositoryException { private static final long serialVersionUID = 1L; public BusinessDataRepositoryDeploymentException(String message) { super(message); } /** * Constructs a BusinessDataRepositoryDeploymentException with the specified cause. * * @param cause the cause */ public BusinessDataRepositoryDeploymentException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/BusinessDataRepositoryException.java ================================================ /** * 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.business.data; import org.bonitasoft.engine.exception.BonitaException; /** * Thrown if an exception occurs dealing with the Business Data Repository. * * @author Romain Bioteau * @author Matthieu Chaffotte */ public class BusinessDataRepositoryException extends BonitaException { private static final long serialVersionUID = -1056166500737611443L; /** * Constructs a BusinessDataRepositoryException with the specified cause. * * @param cause the cause */ public BusinessDataRepositoryException(final Throwable cause) { super(cause); } /** * Constructs a BusinessDataRepositoryException with the specified detail message. * * @param message the detail message */ public BusinessDataRepositoryException(final String message) { super(message); } /** * Constructs a BusinessDataRepositoryException with the specified detail message and cause. * * @param message the detail message * @param cause the cause */ public BusinessDataRepositoryException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/InvalidBusinessDataModelException.java ================================================ /** * 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.business.data; import java.io.Serial; /** * Thrown to indicate that the Business Data Model is invalid. So it cannot be deployed. * * @author Colin Puy * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public class InvalidBusinessDataModelException extends BusinessDataRepositoryException { @Serial private static final long serialVersionUID = 1L; /** * Constructs an InvalidBusinessDataModelException with the specified cause. * * @param cause the cause */ public InvalidBusinessDataModelException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/MultipleBusinessDataReference.java ================================================ /** * 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.business.data; import java.util.List; /** * A MultipleBusinessDataReference is a reference of a * {@link org.bonitasoft.engine.bpm.businessdata.BusinessDataDefinition} which is multiple. * * @author Matthieu Chaffotte */ public interface MultipleBusinessDataReference extends BusinessDataReference { /** * Lists the business data identifiers. * * @return the business data identifiers. */ List getStorageIds(); /** * Lists the business data identifiers. * * @return the business data identifiers as String . */ List getStorageIdsAsString(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/SimpleBusinessDataReference.java ================================================ /** * 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.business.data; /** * A SimpleBusinessDataReference is a reference of a * {@link org.bonitasoft.engine.bpm.businessdata.BusinessDataDefinition} which is not multiple. * * @author Matthieu Chaffotte */ public interface SimpleBusinessDataReference extends BusinessDataReference { /** * Returns the identifier of the business data. * It can be null, if no business data is attached to the reference. * * @return the identifier of the business data */ Long getStorageId(); /** * Returns the identifier of the business data. * It can be null, if no business data is attached to the reference. * * @return the identifier of the business data as String */ String getStorageIdAsString(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/impl/BusinessDataReferenceImpl.java ================================================ /** * 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.business.data.impl; import java.util.Objects; import org.bonitasoft.engine.business.data.BusinessDataReference; /** * @author Matthieu Chaffotte */ public class BusinessDataReferenceImpl implements BusinessDataReference { private static final long serialVersionUID = -6913883854275484141L; private final String name; private final String type; public BusinessDataReferenceImpl(final String name, final String type) { super(); this.name = name; this.type = type; } @Override public String getName() { return name; } @Override public String getType() { return type; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BusinessDataReferenceImpl that = (BusinessDataReferenceImpl) o; return Objects.equals(name, that.name) && Objects.equals(type, that.type); } @Override public int hashCode() { return Objects.hash(name, type); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/impl/MultipleBusinessDataReferenceImpl.java ================================================ /** * 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.business.data.impl; import java.util.ArrayList; import java.util.List; import java.util.Objects; import com.fasterxml.jackson.annotation.JsonProperty; import org.bonitasoft.engine.business.data.MultipleBusinessDataReference; /** * @author Matthieu Chaffotte */ public class MultipleBusinessDataReferenceImpl extends BusinessDataReferenceImpl implements MultipleBusinessDataReference { private static final long serialVersionUID = -8221290488745270659L; private final List storageIds; @JsonProperty("storageIds_string") private final List storageIdsAsString; public MultipleBusinessDataReferenceImpl(final String name, final String type, final List storageIds) { super(name, type); this.storageIds = new ArrayList<>(); this.storageIdsAsString = new ArrayList<>(); for (final Long storageId : storageIds) { this.storageIds.add(storageId); if (storageId == null) { this.storageIdsAsString.add(null); } else { this.storageIdsAsString.add(storageId.toString()); } } } @Override public List getStorageIds() { return storageIds; } @Override public List getStorageIdsAsString() { return storageIdsAsString; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; MultipleBusinessDataReferenceImpl that = (MultipleBusinessDataReferenceImpl) o; return Objects.equals(storageIds, that.storageIds); } @Override public int hashCode() { return Objects.hash(super.hashCode(), storageIds, storageIdsAsString); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/impl/SimpleBusinessDataReferenceImpl.java ================================================ /** * 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.business.data.impl; import java.util.Objects; import com.fasterxml.jackson.annotation.JsonProperty; import org.bonitasoft.engine.business.data.SimpleBusinessDataReference; /** * @author Matthieu Chaffotte */ public class SimpleBusinessDataReferenceImpl extends BusinessDataReferenceImpl implements SimpleBusinessDataReference { private static final long serialVersionUID = -434357449996998735L; private final Long storageId; @JsonProperty("storageId_string") private String storageIdAsString = null; public SimpleBusinessDataReferenceImpl(final String name, final String type, final Long storageId) { super(name, type); this.storageId = storageId; if (storageId != null) { this.storageIdAsString = storageId.toString(); } } @Override public Long getStorageId() { return storageId; } @Override public String getStorageIdAsString() { return storageIdAsString; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; SimpleBusinessDataReferenceImpl that = (SimpleBusinessDataReferenceImpl) o; return Objects.equals(storageId, that.storageId); } @Override public int hashCode() { return Objects.hash(super.hashCode(), storageId, storageIdAsString); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/data/package-info.java ================================================ /** * Copyright (C) 2015 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.business.data; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandCriterion.java ================================================ /** * 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.command; /** * Sort criterion used to specify the sort order of the {@link org.bonitasoft.engine.command.CommandDescriptor}.
* Used by {@link org.bonitasoft.engine.api.CommandAPI#getAllCommands(int, int, CommandCriterion)} and $ * {@link org.bonitasoft.engine.api.CommandAPI#getUserCommands(int, int, CommandCriterion)} indicate in what order we * should return the list of the results. * * @author Matthieu Chaffotte * @since 6.0.0 */ public enum CommandCriterion { NAME_ASC, NAME_DESC } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandDescriptor.java ================================================ /** * 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.command; import org.bonitasoft.engine.bpm.BonitaObject; /** * Represents the descriptor of a (tenant) command or a platform command. * * @author Zhang Bole * @author Emmanuel Duchastenier * @see org.bonitasoft.engine.api.CommandAPI * @since 6.0.0 */ public interface CommandDescriptor extends BonitaObject { /** * Get the identifier of this CommandDescriptor * * @return the if of this CommandDescriptor */ long getId(); /** * Get the name of the command * * @return the name of the command */ String getName(); /** * Get the description of the command * * @return the description of the command */ String getDescription(); /** * Get the implementation class name of the command * * @return the implementation class name of the command */ String getImplementation(); /** * Is the command a default system command or a custom command. * * @return true if this is a default system command, false otherwise. */ boolean isSystemCommand(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandDescriptorImpl.java ================================================ /** * 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.command; /** * @author Zhang Bole * @author Matthieu Chaffotte */ public class CommandDescriptorImpl implements CommandDescriptor { private static final long serialVersionUID = -8798112164975720185L; private long id; private String name; private String description; private String implementation; private boolean system; public CommandDescriptorImpl() { super(); } public CommandDescriptorImpl(final String name, final String description, final String implementation) { this.name = name; this.description = description; this.implementation = implementation; } CommandDescriptorImpl(final CommandDescriptor command) { name = command.getName(); description = command.getDescription(); implementation = command.getImplementation(); system = command.isSystemCommand(); } @Override public long getId() { return id; } @Override public String getName() { return name; } @Override public String getDescription() { return description; } @Override public String getImplementation() { return implementation; } @Override public boolean isSystemCommand() { return system; } public void setId(final long id) { this.id = id; } public void setName(final String name) { this.name = name; } public void setDescription(final String description) { this.description = description; } public void setImplementation(final String implementation) { this.implementation = implementation; } public void setSystem(final boolean system) { this.system = system; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (description == null ? 0 : description.hashCode()); result = prime * result + (int) (id ^ id >>> 32); result = prime * result + (implementation == null ? 0 : implementation.hashCode()); result = prime * result + (name == null ? 0 : name.hashCode()); result = prime * result + (system ? 1231 : 1237); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final CommandDescriptorImpl other = (CommandDescriptorImpl) obj; if (description == null) { if (other.description != null) { return false; } } else if (!description.equals(other.description)) { return false; } if (id != other.id) { return false; } if (implementation == null) { if (other.implementation != null) { return false; } } else if (!implementation.equals(other.implementation)) { return false; } if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } if (system != other.system) { return false; } return true; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandExecutionException.java ================================================ /** * 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.command; import org.bonitasoft.engine.exception.ExecutionException; /** * Happens when an exception occurs during the execution of a command * * @author Matthieu Chaffotte * @since 6.0.0 */ public class CommandExecutionException extends ExecutionException { private static final long serialVersionUID = 5863266847298950866L; public CommandExecutionException(final String message) { super(message); } public CommandExecutionException(final Throwable cause) { super(cause); } public CommandExecutionException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandNotFoundException.java ================================================ /** * 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.command; import org.bonitasoft.engine.exception.NotFoundException; /** * Happens when a command is not found * * @author Matthieu Chaffotte * @since 6.0.0 */ public class CommandNotFoundException extends NotFoundException { private static final long serialVersionUID = -8387839876633445358L; public CommandNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandParameterizationException.java ================================================ /** * 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.command; import org.bonitasoft.engine.exception.BonitaException; /** * Happens when an a command is executed with wrong parameters * * @author Matthieu Chaffotte * @since 6.0.0 */ public class CommandParameterizationException extends BonitaException { private static final long serialVersionUID = 5878600837095857930L; public CommandParameterizationException(final String message) { super(message); } public CommandParameterizationException(final Throwable cause) { super(cause); } public CommandParameterizationException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandSearchDescriptor.java ================================================ /** * 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.command; /** * Contains constants to search for commands using * {@link org.bonitasoft.engine.api.CommandAPI#searchCommands(org.bonitasoft.engine.search.SearchOptions)} * * @author Yanyan Liu * @see org.bonitasoft.engine.api.CommandAPI#searchCommands(org.bonitasoft.engine.search.SearchOptions) * @since 6.0.0 */ public class CommandSearchDescriptor { public static final String ID = "id"; public static final String NAME = "name"; public static final String DESCRIPTION = "description"; public static final String IMPLEMENTATION = "implementation"; public static final String SYSTEM = "system"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/CommandUpdater.java ================================================ /** * 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.command; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * Updater for Commands
* * @author Matthieu Chaffotte * @see org.bonitasoft.engine.api.CommandAPI#update(long, CommandUpdater) * @see org.bonitasoft.engine.api.CommandAPI#update(String, CommandUpdater) */ public class CommandUpdater implements Serializable { private static final long serialVersionUID = 1326464578602375090L; public enum CommandField { NAME, DESCRIPTION } private final Map fields; public CommandUpdater() { fields = new HashMap(2); } public void setName(final String name) { fields.put(CommandField.NAME, name); } public void setDescription(final String description) { fields.put(CommandField.DESCRIPTION, description); } public Map getFields() { return fields; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/DependencyNotFoundException.java ================================================ /** * 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.command; import org.bonitasoft.engine.exception.NotFoundException; /** * @author Matthieu Chaffotte */ public class DependencyNotFoundException extends NotFoundException { private static final long serialVersionUID = 4495595833583575078L; public DependencyNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/command/package-info.java ================================================ /** * Copyright (C) 2015 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.command; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/connector/AbstractConnector.java ================================================ /** * 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.connector; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.api.APIAccessor; /** * @author Feng Hui * @author Romain Bioteau * @author Baptiste Mesta * @author Matthieu Chaffotte */ public abstract class AbstractConnector implements Connector { private final Map inputParameters; private final Map outputParameters; protected APIAccessor apiAccessor; private EngineExecutionContext executionContext; public AbstractConnector() { inputParameters = new HashMap(); outputParameters = new HashMap(); } @Override public void setInputParameters(final Map parameters) { inputParameters.putAll(parameters); } protected Object getInputParameter(final String paramName) { return inputParameters.get(paramName); } /** * get the input parameter or the default value if the parameter is not set * * @param parameterKey * name of the parameter * @param defaultValue * value of the parameter if not set * @return * the value of the parameter */ protected Object getInputParameter(final String parameterKey, final Serializable defaultValue) { final Object param = getInputParameter(parameterKey); return param == null ? defaultValue : param; } protected void setOutputParameter(final String paramName, final Object value) { outputParameters.put(paramName, value); } protected Map getOutputParameters() { return outputParameters; } @Override public final Map execute() throws ConnectorException { executeBusinessLogic(); return getOutputParameters(); } @Override public void connect() throws ConnectorException { // default implementation do nothing } @Override public void disconnect() throws ConnectorException { // default implementation do nothing } protected abstract void executeBusinessLogic() throws ConnectorException; public void setAPIAccessor(final APIAccessor apiAccessor) { this.apiAccessor = apiAccessor; } public APIAccessor getAPIAccessor() { return apiAccessor; } public EngineExecutionContext getExecutionContext() { return executionContext; } public void setExecutionContext(final EngineExecutionContext executionContext) { this.executionContext = executionContext; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/connector/Connector.java ================================================ /** * 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.connector; import java.util.Map; /** * @author Feng Hui */ public interface Connector { /** * Set the input parameter for a connector. * * @param parameters * parameters is a map with parameter names and their value. */ void setInputParameters(Map parameters); /** * Validate the input parameters. Check the parameters types and boundaries. * * @throws ConnectorValidationException * when the input parameters are not valid */ void validateInputParameters() throws ConnectorValidationException; /** * Execute the connector. * * @return the connector outputs map corresponding to the output definition. * @throws ConnectorException * when something went wrong during connector execution */ Map execute() throws ConnectorException; /** * Called by the engine before the connector is executed * This method can be implemented by connectors to handle here opening of connections like database connection * * @throws ConnectorException * when something went wrong during connector connection */ void connect() throws ConnectorException; /** * Called by the engine after the connector and its output operations are executed * This method can be implemented by connectors to close connections here. * The typical use of this is to be able to return connected objects that will be used in output operation and then * disconnect them. * * @throws ConnectorException * when something went wrong during connector disconnection */ void disconnect() throws ConnectorException; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/connector/ConnectorException.java ================================================ /** * 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.connector; import org.bonitasoft.engine.exception.BonitaException; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class ConnectorException extends BonitaException { private static final long serialVersionUID = -4711678695785171528L; public ConnectorException(final String message) { super(message); } public ConnectorException(final Throwable cause) { super(cause); } public ConnectorException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/connector/ConnectorValidationException.java ================================================ /** * 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.connector; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.exception.BonitaException; /** * @author Baptiste Mesta */ public class ConnectorValidationException extends BonitaException { private static final long serialVersionUID = -7641578992494154217L; public ConnectorValidationException(final String message) { super(message); } public ConnectorValidationException(final Connector connector, final String... messages) { super(getMessage(connector, Arrays.asList(messages))); } public ConnectorValidationException(final Connector connector, final List messages) { super(getMessage(connector, messages)); } private static String getMessage(final Connector connector, final List messages) { final StringBuilder mergedMessages = new StringBuilder("Error validating connector "); mergedMessages.append(connector.getClass().getName()); mergedMessages.append(":\n"); for (final String message : messages) { mergedMessages.append(message); mergedMessages.append('\n'); } return mergedMessages.toString(); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/connector/EngineExecutionContext.java ================================================ /** * 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.connector; import java.io.Serializable; import org.bonitasoft.engine.expression.ExpressionConstants; /** * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public class EngineExecutionContext implements Serializable { private static final long serialVersionUID = -57239373625030379L; private long processDefinitionId = -1; private long activityInstanceId = -1; private long processInstanceId = -1; private long rootProcessInstanceId; private long taskAssigneeId = -1; /** * @return the current process definition ID */ public long getProcessDefinitionId() { return processDefinitionId; } public void setProcessDefinitionId(final long processDefinitionId) { this.processDefinitionId = processDefinitionId; } public long getProcessInstanceId() { return processInstanceId; } /** * Refers to the directly including process instance. * * @param processInstanceId * the ID of the directly including process instance */ public void setProcessInstanceId(final long processInstanceId) { this.processInstanceId = processInstanceId; } /** * Refers to the directly including process instance. * * @return the ID of the directly including process instance */ public long getActivityInstanceId() { return activityInstanceId; } public void setActivityInstanceId(final long activityInstanceId) { this.activityInstanceId = activityInstanceId; } /** * Refers to the top-level including process instance. * * @param rootProcessInstanceId * the ID of the top-level including process instance */ public void setRootProcessInstanceId(final long rootProcessInstanceId) { this.rootProcessInstanceId = rootProcessInstanceId; } /** * Refers to the top-level including process instance. * * @return * the ID of the top-level including process instance */ public long getRootProcessInstanceId() { return rootProcessInstanceId; } public Serializable getExpressionConstant(final ExpressionConstants constant) { switch (constant) { case ACTIVITY_INSTANCE_ID: return activityInstanceId; case PROCESS_INSTANCE_ID: return processInstanceId; case PROCESS_DEFINITION_ID: return processDefinitionId; case ROOT_PROCESS_INSTANCE_ID: return rootProcessInstanceId; case ENGINE_EXECUTION_CONTEXT: return this; case TASK_ASSIGNEE_ID: return taskAssigneeId; default: return -1; } } public long getTaskAssigneeId() { return taskAssigneeId; } public void setTaskAssigneeId(final long taskAssigneeId) { this.taskAssigneeId = taskAssigneeId; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/connector/sap/SAPMonoDestinationDataProvider.java ================================================ /** * Copyright (C) 2020 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.connector.sap; import java.util.Properties; import com.sap.conn.jco.ext.DestinationDataEventListener; import com.sap.conn.jco.ext.DestinationDataProvider; import com.sap.conn.jco.ext.Environment; /** * Used by SAP connector in order to synchronize SAP {@code com.sap.conn.jco.ext.DestinationDataProvider} used * Warning: Internal use only! * This class is subject to change without notice * * @author Aurelien Pupier */ public class SAPMonoDestinationDataProvider implements DestinationDataProvider { private static SAPMonoDestinationDataProvider destinationDataProvider = null; private DestinationDataEventListener listener; private Properties properties; private final String destinationName; private SAPMonoDestinationDataProvider(final String destinationName) { super(); this.destinationName = destinationName; } /** * BE CAREFUL: only one destinationName is possible * * @param destinationName * @return */ public static synchronized SAPMonoDestinationDataProvider getInstance(final String destinationName) throws IllegalStateException { // TODO: handle several destinationName correctly if (destinationDataProvider == null) { destinationDataProvider = new SAPMonoDestinationDataProvider(destinationName); Environment.registerDestinationDataProvider(destinationDataProvider); } else if (!destinationName.equals(destinationDataProvider.getDestinationName())) { throw new IllegalStateException( "You can use only one SAP destination (and they should use the configuration). The current one is named " + destinationDataProvider.getDestinationName()); } return destinationDataProvider; } @Override public Properties getDestinationProperties(final String destinationName) { if (destinationName.equals(this.destinationName) && properties != null) { return properties; } throw new RuntimeException("Destination " + destinationName + " is not available"); } @Override public void setDestinationDataEventListener(final DestinationDataEventListener eventListener) { listener = eventListener; } @Override public boolean supportsEvents() { return true; } public void changeProperties(final Properties properties) { if (properties == null) { listener.deleted(destinationName); this.properties = null; } else { if (listener != null && !properties.equals(this.properties)) { listener.updated(destinationName); } this.properties = properties; } } public String getDestinationName() { return destinationName; } /** * Use it carefully!!! If someone retrieved an Instance and you clear it it will break everything, currently usage * planned only for test purpose. */ public static void clear() { if (destinationDataProvider != null) { Environment.unregisterDestinationDataProvider(destinationDataProvider); destinationDataProvider = null; } } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/digest/DigestUtils.java ================================================ /** * 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.digest; import static java.nio.charset.StandardCharsets.UTF_8; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; import org.bonitasoft.engine.api.Internal; @Internal public class DigestUtils { public static String encodeBase64AsUtf8String(byte[] bytes) { return new String(Base64.getEncoder().encode(bytes), UTF_8); } public static byte[] md5(String source) { return digest("MD5", source); } public static byte[] sha1(String source) { return digest("SHA1", source); } private static byte[] digest(String algorithm, String source) { return getDigest(algorithm).digest(utf8Bytes(source)); } private static MessageDigest getDigest(String algorithm) { try { return MessageDigest.getInstance(algorithm); } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException(e); } } private static byte[] utf8Bytes(String string) { if (string == null) { return null; } return string.getBytes(UTF_8); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/APIImplementationNotFoundException.java ================================================ /** * 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.exception; /** * Thrown when it's not possible to find a API implementation. * The class APIImplementationNotFoundException is a form of Throwable that indicates conditions that a reasonable * application might want to catch. * The class APIImplementationNotFoundException that is not also subclasses of {@link RuntimeException} are checked * exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Matthieu Chaffotte * @author Celine Souchet */ public class APIImplementationNotFoundException extends BonitaException { private static final long serialVersionUID = -8951476276466102532L; /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public APIImplementationNotFoundException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/AlreadyExistsException.java ================================================ /** * 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.exception; /** * Thrown when it's not possible to create a object, because it already exists. * The class AlreadyExistsException is a form of Throwable that indicates conditions that a reasonable application might * want to catch. * The class AlreadyExistsException that is not also subclasses of {@link RuntimeException} are checked exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Matthieu Chaffotte * @author Celine Souchet */ public class AlreadyExistsException extends CreationException { private static final long serialVersionUID = 6709231545504567159L; private String name; /** * Constructs a new exception with the specified detail cause. * * @param cause The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null * value is permitted, and indicates that the * cause is nonexistent or unknown.) */ public AlreadyExistsException(final Throwable cause) { super(cause); } /** * Constructs a new exception with the specified detail message. * * @param message The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} * method). */ public AlreadyExistsException(final String message) { super(message); } /** * Constructs a new exception with the specified detail message and name of existing object. * * @param message The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} * method). * @param name the name of the conflicting object * @since 6.4 */ public AlreadyExistsException(final String message, String name) { super(message); this.name = name; } /** * Retrieves the name of conflicting object or null if the name was not supplied * * @return the name of conflicting object or null if the name was not supplied. * @since 6.4 */ public String getName() { return name; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ApplicationInstallationException.java ================================================ /** * 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.exception; public class ApplicationInstallationException extends RuntimeException { public ApplicationInstallationException(String message) { super(message); } public ApplicationInstallationException(String message, Throwable cause) { super(message, cause); } @Override public String getMessage() { String message = super.getMessage(); Throwable cause = getCause(); if (cause != null) { message += " - cause: " + cause.getClass().getSimpleName() + " - " + cause.getMessage(); } return message; } @Override public String getLocalizedMessage() { // ensure consistency return getMessage(); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/BonitaContextException.java ================================================ /** * 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.exception; /** * Exception wrapper providing context on the exception. * * @author Aurelien Pupier * @author Celine Souchet */ public interface BonitaContextException { String getUserName(); void setUserName(String userName); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/BonitaException.java ================================================ /** * 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.exception; import java.io.Serializable; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; /** * The class BonitaException and its subclasses are a form of Throwable that indicates conditions that a reasonable * application might want to catch. * The class BonitaException and its subclasses that are not also subclasses of {@link RuntimeException} are checked * exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Matthieu Chaffotte * @author Baptiste Mesta * @author Celine Souchet * @author Aurelien Pupier */ public class BonitaException extends Exception implements BonitaContextException { private static final long serialVersionUID = -5413586694735909486L; private final Map context = new TreeMap<>(); private String userName = ""; /** * Constructs a new exception with the specified detail message and cause. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public BonitaException(final String message, final Throwable cause) { super(message, cause); } /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public BonitaException(final String message) { super(message); } /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public BonitaException(final Throwable cause) { super(cause); } /** * @see org.bonitasoft.engine.exception.BonitaContextException#getUserName() */ @Override public String getUserName() { return userName; } /** * @see org.bonitasoft.engine.exception.BonitaContextException#setUserName(java.lang.String) */ @Override public void setUserName(final String userName) { this.userName = userName; } /** * @return The context of the exception * @since 6.3 */ public Map getContext() { return context; } /** * @param id * The identifier of the process definition to set * @since 6.3 */ public void setProcessDefinitionIdOnContext(final Long id) { context.put(ExceptionContext.PROCESS_DEFINITION_ID, id); } /** * @param name * The name of the process definition to set * @since 6.3 */ public void setProcessDefinitionNameOnContext(final String name) { context.put(ExceptionContext.PROCESS_NAME, name); } /** * @param version * The version of the process definition to set * @since 6.3 */ public void setProcessDefinitionVersionOnContext(final String version) { context.put(ExceptionContext.PROCESS_VERSION, version); } /** * @param id * The identifier of the process instance to set * @since 6.3 */ public void setProcessInstanceIdOnContext(final Long id) { context.put(ExceptionContext.PROCESS_INSTANCE_ID, id); } /** * @param id * The identifier of the root process instance to set * @since 6.3 */ public void setRootProcessInstanceIdOnContext(final Long id) { context.put(ExceptionContext.ROOT_PROCESS_INSTANCE_ID, id); } /** * @param id * The identifier of the connector definition * @since 6.3 */ public void setConnectorDefinitionIdOnContext(final String id) { context.put(ExceptionContext.CONNECTOR_DEFINITION_ID, id); } /** * @param name * The class name of the implementation of the connector definition to set * @since 6.3 */ public void setConnectorDefinitionImplementationClassNameOnContext(final String name) { context.put(ExceptionContext.CONNECTOR_DEFINITION_IMPLEMENTATION_CLASS_NAME, name); } /** * @param version * The version of the connector definition * @since 6.3 */ public void setConnectorDefinitionVersionOnContext(final String version) { context.put(ExceptionContext.CONNECTOR_DEFINITION_VERSION, version); } /** * @param activationEvent * The event which activates the connector to set * @since 6.3 */ public void setConnectorActivationEventOnContext(final String activationEvent) { context.put(ExceptionContext.CONNECTOR_ACTIVATION_EVENT, activationEvent); } /** * @param id * The identifier of the connector instance to set * @since 6.3 */ public void setConnectorInstanceIdOnContext(final long id) { context.put(ExceptionContext.CONNECTOR_INSTANCE_ID, id); } /** * @param id * The identifier of the flow node definition to set * @since 6.3 */ public void setFlowNodeDefinitionIdOnContext(final long id) { context.put(ExceptionContext.FLOW_NODE_DEFINITION_ID, id); } /** * @param id * The identifier of the flow node instance to set * @since 6.3 */ public void setFlowNodeInstanceIdOnContext(final long id) { context.put(ExceptionContext.FLOW_NODE_INSTANCE_ID, id); } /** * @param name * The name of the flow node to set * @since 6.3 */ public void setFlowNodeNameOnContext(final String name) { context.put(ExceptionContext.FLOW_NODE_NAME, name); } /** * @param name * The name of the message instance to set * @since 6.3 */ public void setMessageInstanceNameOnContext(final String name) { context.put(ExceptionContext.MESSAGE_INSTANCE_NAME, name); } /** * @param name * The target process name of the message instance to set * @since 6.3 */ public void setMessageInstanceTargetProcessOnContext(final String name) { context.put(ExceptionContext.MESSAGE_INSTANCE_TARGET_PROCESS_NAME, name); } /** * @param name * The target flow node name of the message instance to set * @since 6.3 */ public void setMessageInstanceTargetFlowNodeOnContext(final String name) { context.put(ExceptionContext.MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME, name); } /** * @param eventType * The event type of the waiting message instance to set * @since 6.3 */ public void setWaitingMessageEventTypeOnContext(final String eventType) { context.put(ExceptionContext.WAITING_MESSAGE_INSTANCE_TYPE, eventType); } /** * @param id * The identifier of the document * @since 6.3 */ public void setDocumentIdOnContext(final long id) { context.put(ExceptionContext.DOCUMENT_ID, id); } /** * @param userId * The identifier of the user * @since 6.3 */ public void setUserIdOnContext(final Long userId) { context.put(ExceptionContext.USER_ID, userId); } /** * @param groupId * The identifier of the group * @since 6.3 */ public void setGroupIdOnContext(final Long groupId) { context.put(ExceptionContext.GROUP_ID, groupId); } /** * @param roleId * The identifier of the role * @since 6.3 */ public void setRoleIdOnContext(final Long roleId) { context.put(ExceptionContext.ROLE_ID, roleId); } /** * @param name * The name of the data * @since 6.4.2 */ public void setDataName(final String name) { context.put(ExceptionContext.DATA_NAME, name); } /** * @param dataClassName * The class name of the data * @since 6.4.2 */ public void setDataClassName(final String dataClassName) { context.put(ExceptionContext.DATA_CLASS_NAME, dataClassName); } @Override public String getMessage() { final StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(getUserNameMessage()); appendContextMessage(stringBuilder); stringBuilder.append(super.getMessage()); return stringBuilder.toString(); } private void appendContextMessage(final StringBuilder stringBuilder) { if (!context.isEmpty()) { for (final Entry entry : context.entrySet()) { stringBuilder.append(entry.getKey() + "=" + entry.getValue() + " | "); } } } private String getUserNameMessage() { return userName != null && !userName.isEmpty() ? "USERNAME=" + userName + " | " : ""; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/BonitaHomeConfigurationException.java ================================================ /** * 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.exception; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros */ public class BonitaHomeConfigurationException extends BonitaException { private static final long serialVersionUID = -6473712535410061835L; public BonitaHomeConfigurationException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/BonitaHomeNotSetException.java ================================================ /** * 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.exception; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros */ public class BonitaHomeNotSetException extends BonitaException { private static final long serialVersionUID = -6473712535410061835L; public BonitaHomeNotSetException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/BonitaRuntimeException.java ================================================ /** * 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.exception; /** * @author Matthieu Chaffotte * @author Celine Souchet * @author Aurelien Pupier */ public class BonitaRuntimeException extends RuntimeException implements BonitaContextException { private static final long serialVersionUID = -5413586694735909486L; private String userName = ""; public BonitaRuntimeException(final String message) { super(message); } public BonitaRuntimeException(final String message, final Throwable cause) { super(message, cause); } public BonitaRuntimeException(final Throwable cause) { super(cause); } public BonitaRuntimeException(final String message, final Throwable cause, final boolean enableSuppression, final boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } /** * @see org.bonitasoft.engine.exception.BonitaContextException#getUserName() */ @Override public String getUserName() { return userName; } /** * @see org.bonitasoft.engine.exception.BonitaContextException#setUserName(java.lang.String) */ @Override public void setUserName(String userName) { this.userName = userName; } @Override public String getMessage() { return getUserNameMessage() + super.getMessage(); } private String getUserNameMessage() { return userName != null && !userName.isEmpty() ? "USERNAME=" + userName + " | " : ""; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ClassLoaderException.java ================================================ /** * 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.exception; /** * @author Elias Ricken de Medeiros */ public class ClassLoaderException extends BonitaException { private static final long serialVersionUID = 6760479336490227757L; public ClassLoaderException(final String message) { super(message); } public ClassLoaderException(final Throwable t) { super(t); } public ClassLoaderException(final String message, final Exception e) { super(message, e); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ContractDataNotFoundException.java ================================================ /** * 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.exception; /** * Thrown when a process / user task contract data cannot be found. * author Emmanuel Duchastenier */ public class ContractDataNotFoundException extends NotFoundException { public ContractDataNotFoundException(Throwable cause) { super(cause); } public ContractDataNotFoundException(String message) { super(message); } public ContractDataNotFoundException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/CreationException.java ================================================ /** * 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.exception; /** * @author Matthieu Chaffotte */ public class CreationException extends BonitaException { private static final long serialVersionUID = 7462326307205938638L; public CreationException(final Throwable cause) { super(cause); } public CreationException(final String message, final Throwable cause) { super(message, cause); } public CreationException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/DeletionException.java ================================================ /** * 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.exception; /** * @author Matthieu Chaffotte */ public class DeletionException extends BonitaException { private static final long serialVersionUID = -5157179430526887606L; public DeletionException(final Throwable cause) { super(cause); } public DeletionException(final String message) { super(message); } public DeletionException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ExceptionContext.java ================================================ /** * 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.exception; /** * To define the context of an exception in the message. * * @author Celine Souchet */ public enum ExceptionContext { /** * Corresponding to the identifier of the process definition */ PROCESS_DEFINITION_ID, /** * Corresponding to the name of the process definition */ PROCESS_NAME, /** * Corresponding to the version of the process definition */ PROCESS_VERSION, /** * Corresponding to the identifier of the process instance */ PROCESS_INSTANCE_ID, /** * Corresponding to the identifier of the root process instance */ ROOT_PROCESS_INSTANCE_ID, /** * Corresponding to the identifier of the flow node definition */ FLOW_NODE_DEFINITION_ID, /** * Corresponding to the identifier of the flow node instance */ FLOW_NODE_INSTANCE_ID, /** * Corresponding to the name of the flow node */ FLOW_NODE_NAME, /** * Corresponding to the name of the Message Instance */ MESSAGE_INSTANCE_NAME, /** * Corresponding to the target process name of the Message Instance */ MESSAGE_INSTANCE_TARGET_PROCESS_NAME, /** * Corresponding to the target flow node name of the Message Instance */ MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME, /** * Corresponding to the event type of the Waiting Message Instance */ WAITING_MESSAGE_INSTANCE_TYPE, /** * Corresponding to the identifier of the connector definition */ CONNECTOR_DEFINITION_ID, /** * Corresponding to the class name of the implementation of the connector definition */ CONNECTOR_DEFINITION_IMPLEMENTATION_CLASS_NAME, /** * Corresponding to the version of the connector definition */ CONNECTOR_DEFINITION_VERSION, /** * Corresponding to the event which activates the connector */ CONNECTOR_ACTIVATION_EVENT, /** * Corresponding to the identifier of the connector instance */ CONNECTOR_INSTANCE_ID, /** * Corresponding to the identifier of the user */ USER_ID, /** * Corresponding to the identifier of the group */ GROUP_ID, /** * Corresponding to the identifier of the role */ ROLE_ID, /** * Corresponding to the identifier of the document */ DOCUMENT_ID, /** * Corresponding to the name of the data */ DATA_NAME, /** * Corresponding to the class name of the data */ DATA_CLASS_NAME; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ExecutionException.java ================================================ /** * 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.exception; /** * Thrown when a problem occurs: *
    *
  • while executing a BPM entity (activity, process, event, ...)
  • *
  • while executing an administration operation (startPlatform, activateTenant, etc.)
  • *
  • ...
  • *
* The class ExecutionException and its subclasses are a form of Throwable that indicates conditions that a reasonable * application might want to catch. * The class ExecutionException and its subclasses that are not also subclasses of {@link RuntimeException} are checked * exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Matthieu Chaffotte * @author Emmanuel Duchastenier * @author Celine Souchet */ public class ExecutionException extends BonitaException { private static final long serialVersionUID = -424782295937066031L; /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ExecutionException(final Throwable cause) { super(cause); } /** * Constructs a new exception with the specified detail message and cause. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ExecutionException(final String message, final Throwable cause) { super(message, cause); } /** * Constructs a new exception with the specified detail message. * * @param message * The detail message (which is saved for later retrieval by the {@link Throwable#getMessage()} method). */ public ExecutionException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ExportException.java ================================================ /** * 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.exception; /** * Thrown when it's not possible to import an element. * * @author Elias Ricken de Medeiros * @since 6.4 */ public class ExportException extends BonitaException { /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ExportException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/FormMappingNotFoundException.java ================================================ /** * 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.exception; /** * @author Baptiste Mesta */ public class FormMappingNotFoundException extends NotFoundException { public FormMappingNotFoundException(String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ImportException.java ================================================ /** * 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.exception; /** * Thrown when it's not possible to import an element. * * @author Elias Ricken de Medeiros */ public class ImportException extends BonitaException { /** * Constructs a new exception with the specified detail cause. * * @param cause * The cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A null value * is permitted, and indicates that the * cause is nonexistent or unknown.) */ public ImportException(final Throwable cause) { super(cause); } /** * Constructs a new exception with the specified message. * * @param message * The message (which is saved for later retrieval by the {@link Throwable#getMessage()} method) */ public ImportException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/IncorrectParameterException.java ================================================ /** * 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.exception; /** * Generic Bonita exception for all invalid Parameter when calling a constructor or a method on Bonita objects. * * @author Emmanuel Duchastenier */ public class IncorrectParameterException extends BonitaException { private static final long serialVersionUID = -5875088313056748595L; public IncorrectParameterException(final String message) { super(message); } public IncorrectParameterException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidGroupNameException.java ================================================ /** * 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.exception; /** * @author Danila Mazour */ public class InvalidGroupNameException extends CreationException { public InvalidGroupNameException(String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidPageTokenException.java ================================================ /** * 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.exception; public class InvalidPageTokenException extends CreationException { private static final long serialVersionUID = -4521026642699202555L; public InvalidPageTokenException(final String message, final Throwable cause) { super(message, cause); } public InvalidPageTokenException(String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidPageZipContentException.java ================================================ /** * 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.exception; public class InvalidPageZipContentException extends CreationException { private static final long serialVersionUID = -4521026642699202555L; public InvalidPageZipContentException(final String message, final Throwable cause) { super(message, cause); } public InvalidPageZipContentException(String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidPageZipInconsistentException.java ================================================ /** * 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.exception; /** * @author Baptiste Mesta */ public class InvalidPageZipInconsistentException extends InvalidPageZipContentException { public InvalidPageZipInconsistentException(String message) { super(message); } public InvalidPageZipInconsistentException(String string, Exception e) { super(string, e); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidPageZipMissingAPropertyException.java ================================================ /** * 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.exception; /** * @author Baptiste Mesta */ public class InvalidPageZipMissingAPropertyException extends InvalidPageZipContentException { public InvalidPageZipMissingAPropertyException(String fields) { super("Missing fields in the page.properties: " + fields); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidPageZipMissingIndexException.java ================================================ /** * 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.exception; /** * @author Baptiste Mesta */ public class InvalidPageZipMissingIndexException extends InvalidPageZipContentException { public InvalidPageZipMissingIndexException() { super("Missing Index.groovy or index.html"); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidPageZipMissingPropertiesException.java ================================================ /** * 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.exception; /** * @author Baptiste Mesta */ public class InvalidPageZipMissingPropertiesException extends InvalidPageZipContentException { public InvalidPageZipMissingPropertiesException() { super("Missing page.propeties"); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/InvalidXMLException.java ================================================ /** * 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.exception; /** * @author Danila Mazour */ public class InvalidXMLException extends BonitaException { public InvalidXMLException(String msg, Exception e) { super(msg, e); } public InvalidXMLException(Exception e) { super(e); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/MissingServiceException.java ================================================ /** * 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.exception; /** * Happens when a service is missing from the configuration * * @author Baptiste Mesta */ public class MissingServiceException extends RuntimeException { private static final long serialVersionUID = 1L; public MissingServiceException(final String string) { super(string); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/NotFoundException.java ================================================ /** * 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.exception; /** * @author Matthieu Chaffotte */ public class NotFoundException extends BonitaException { private static final long serialVersionUID = -1056166500737611443L; public NotFoundException(final Throwable cause) { super(cause); } public NotFoundException(final String message) { super(message); } public NotFoundException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/NotSerializableException.java ================================================ /** * 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.exception; /** * @author Celine Souchet */ public class NotSerializableException extends BonitaException { private static final long serialVersionUID = -5541516162117265707L; public NotSerializableException(final String connectorDefinitionId, final Throwable e) { super("Connector " + connectorDefinitionId + " executed successfully but output cannot be read correctly. See Bonita Engine log file for technical details.", e); } public NotSerializableException(final String connectorDefinitionId, final String connectorDefinitionVersion, final String key, final Object value) { super(createMessage(connectorDefinitionId, connectorDefinitionVersion, key, value)); } private static String createMessage(final String connectorDefinitionId, final String connectorDefinitionVersion, final String key, final Object value) { final StringBuilder stringBuilder = new StringBuilder("the connector "); stringBuilder.append(connectorDefinitionId); stringBuilder.append(' '); stringBuilder.append(connectorDefinitionVersion); stringBuilder.append(" have an unserializable output and was called directly from the api. name="); stringBuilder.append(key); stringBuilder.append(" value="); stringBuilder.append(value.toString()); return stringBuilder.toString(); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ProcessInstanceHierarchicalDeletionException.java ================================================ /** * 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.exception; /** * Happens when a process instance can't be there is a parent process instance that is still active * Delete this parent process first. * * @author Baptiste Mesta */ public class ProcessInstanceHierarchicalDeletionException extends DeletionException { private static final long serialVersionUID = -5157179430526887606L; private final long processInstanceId; public ProcessInstanceHierarchicalDeletionException(final String message, final long processInstanceId) { super(message); this.processInstanceId = processInstanceId; } /** * @return the processInstanceId that is the parent (root) of the process we try to delete */ public long getProcessInstanceId() { return processInstanceId; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/RetrieveException.java ================================================ /** * 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.exception; /** * @author Matthieu Chaffotte */ public class RetrieveException extends BonitaRuntimeException { private static final long serialVersionUID = -9045954776853609628L; public RetrieveException(final Throwable cause) { super(cause); } public RetrieveException(final String message) { super(message); } public RetrieveException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/SearchException.java ================================================ /** * 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.exception; /** * @author Baptiste Mesta */ public class SearchException extends BonitaException { private static final long serialVersionUID = 1700504009097375021L; public SearchException(final Throwable cause) { super(cause); } public SearchException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/ServerAPIException.java ================================================ /** * 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.exception; /** * @author Matthieu Chaffotte */ public class ServerAPIException extends BonitaException { private static final long serialVersionUID = 3589250940630558801L; public ServerAPIException(final Throwable cause) { super(cause); } public ServerAPIException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/StackTraceTransformer.java ================================================ /** * 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.exception; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.api.internal.ServerWrappedException; /** * Transform an exception having causes in one single exception with merged stack trace * This is done in order to avoid to throw to client server side exception with unknown class in client side * * @author Baptiste Mesta */ public class StackTraceTransformer { private final ServerWrappedException e; static Field field; static { try { field = Throwable.class.getDeclaredField("cause"); field.setAccessible(true); } catch (final NoSuchFieldException e) { e.printStackTrace(); } catch (final SecurityException e) { e.printStackTrace(); } } public StackTraceTransformer(final ServerWrappedException e) { this.e = e; } /** * @param e * the ServerWrappedException given by the server api * @return * a safe to throw to client ServerWrappedException */ public static ServerWrappedException mergeStackTraces(final ServerWrappedException e) { try { return new StackTraceTransformer(e).merge(); } catch (final Exception e1) { System.err .println("Unable to throw the root exception: " + e1.getClass().getName() + ": " + e1.getMessage()); e1.printStackTrace(); return new ServerWrappedException(new BonitaRuntimeException( "Unable to throw the root exception because of (see log for the original stack trace)", e)); } } public static void addStackTo(final Throwable e, final StackTraceElement[] clientStackTrace) { final StackTraceElement[] causeStack = e.getStackTrace(); final StackTraceElement[] newStack = new StackTraceElement[causeStack.length + clientStackTrace.length + 1]; System.arraycopy(clientStackTrace, 0, newStack, 0, clientStackTrace.length); newStack[clientStackTrace.length] = new StackTraceElement( "\t< ========== Beginning of the server stack trace ========== >", " ", " ", -3); System.arraycopy(causeStack, 0, newStack, clientStackTrace.length + 1, causeStack.length); e.setStackTrace(newStack); } private ServerWrappedException merge() throws Exception { final Throwable cause = e.getCause(); if (field != null) { transfertStack(cause, cause); field.set(cause, null); return e; } Throwable newCause; if (cause.getMessage() != null) { newCause = cause.getClass().getConstructor(String.class).newInstance(e.getMessage()); } else { newCause = cause.getClass().newInstance(); } transfertStack(newCause, cause); return new ServerWrappedException(newCause); } private void transfertStack(final Throwable mergeStackInside, final Throwable cause) { Throwable subCause = cause.getCause(); if (subCause == null) { // no stak to merge return; } final StackTraceElement[] currentStack = cause.getStackTrace(); final List causesStacks = new ArrayList(); final List framesInCommons = new ArrayList(); final List exceptions = new ArrayList(); int causeslength = 0; StackTraceElement[] lastStack = currentStack; do { final StackTraceElement[] trace = subCause.getStackTrace(); causesStacks.add(trace); exceptions.add(subCause); int m = trace.length - 1; int n = lastStack.length - 1; while (m >= 0 && n >= 0 && trace[m].equals(lastStack[n])) { m--; n--; } final int framesInCommon = trace.length - 1 - m; framesInCommons.add(framesInCommon); lastStack = trace; // add remove the frames in common to the total length but add one to put the "...23 more" if there is some in common causeslength += trace.length + 1 - framesInCommon + (framesInCommon == 0 ? 0 : 1); } while ((subCause = subCause.getCause()) != null); final StackTraceElement[] mergedStackTrace = new StackTraceElement[currentStack.length + causeslength]; System.arraycopy(currentStack, 0, mergedStackTrace, 0, currentStack.length); int current = currentStack.length; int i = 0; for (final StackTraceElement[] stackTraceElements : causesStacks) { final Integer framesInCommon = framesInCommons.get(i); mergedStackTrace[current] = new StackTraceElement("\tCaused by: " + exceptions.get(i).getClass().getName(), ": " + exceptions.get(i).getMessage() + " ", " ", -3); current++; System.arraycopy(stackTraceElements, 0, mergedStackTrace, current, stackTraceElements.length - framesInCommon); current += stackTraceElements.length - framesInCommon; if (framesInCommon != 0) { mergedStackTrace[current] = new StackTraceElement("... " + framesInCommon + " more", " ", " ", -3); current++; } i++; } mergeStackInside.setStackTrace(mergedStackTrace); } /* * Reduce stack length */ } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/TenantStatusException.java ================================================ /** * 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.exception; import java.io.Serial; /** * Thrown when we try to access on a paused tenant an API method that cannot be called on a paused tenant, or when we * try to access on a running tenant an API * method that cannot be called on a running tenant. * * @author Emmanuel Duchastenier */ public class TenantStatusException extends BonitaRuntimeException { @Serial private static final long serialVersionUID = 1L; /** * @param message * the exception message */ public TenantStatusException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/UnauthorizedAccessException.java ================================================ /** * 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.exception; /** * @author Baptiste Mesta */ public class UnauthorizedAccessException extends BonitaException { public UnauthorizedAccessException(String message, Throwable cause) { super(message, cause); } public UnauthorizedAccessException(String message) { super(message); } public UnauthorizedAccessException(Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/UnavailableLockException.java ================================================ /** * Copyright (C) 2023 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.exception; /** * This exception indicates an update can not be performed because it requires a * functional lock which is already taken by another invocation. Hence this * update request should be rejected with a human-readable explanation. */ public class UnavailableLockException extends UpdateException { private static final long serialVersionUID = -1034429723916469306L; public UnavailableLockException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/UnknownElementType.java ================================================ /** * 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.exception; /** * @author Baptiste Mesta */ public class UnknownElementType extends BonitaRuntimeException { private static final long serialVersionUID = 5458401765450999673L; public UnknownElementType(final String type) { super("Unknown element type: " + type); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/UpdateException.java ================================================ /** * 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.exception; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class UpdateException extends BonitaException { private static final long serialVersionUID = -6529401526595518267L; public UpdateException(final Throwable cause) { super(cause); } public UpdateException(final String message) { super(message); } public UpdateException(final String message, final Throwable cause) { super(message, cause); } public UpdateException() { super("The update descriptor does not contain field updates"); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/UpdatingWithInvalidPageTokenException.java ================================================ /** * 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.exception; public class UpdatingWithInvalidPageTokenException extends UpdateException { private static final long serialVersionUID = -4521026642699202555L; public UpdatingWithInvalidPageTokenException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/UpdatingWithInvalidPageZipContentException.java ================================================ /** * 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.exception; public class UpdatingWithInvalidPageZipContentException extends UpdateException { private static final long serialVersionUID = -4521026642699202555L; public UpdatingWithInvalidPageZipContentException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/exception/package-info.java ================================================ /** * Copyright (C) 2015 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.exception; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/expression/ExpressionEvaluationException.java ================================================ /** * 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.expression; import org.bonitasoft.engine.exception.BonitaException; /** * @author Baptiste Mesta * @author Celine Souchet */ public class ExpressionEvaluationException extends BonitaException { private static final long serialVersionUID = 7295745453567432910L; private String expressionName; /** * @param cause * @param expressionName * The expression's name that failed on the evaluation. */ public ExpressionEvaluationException(final Throwable cause, final String expressionName) { super(cause); this.expressionName = expressionName; } public ExpressionEvaluationException(Throwable cause) { super(cause); } /** * Return empty or null, when the context of evaluation is wrong. * * @return The expression's name that failed on the evaluation. */ public String getExpressionName() { return expressionName; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/filter/AbstractUserFilter.java ================================================ /** * 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.filter; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.api.APIAccessor; import org.bonitasoft.engine.connector.ConnectorValidationException; import org.bonitasoft.engine.connector.EngineExecutionContext; /** * @author Feng Hui * @author Romain Bioteau * @author Baptiste Mesta */ public abstract class AbstractUserFilter implements UserFilter { private final Map inputParameters; private APIAccessor apiAccessor; private EngineExecutionContext executionContext; public AbstractUserFilter() { inputParameters = new HashMap(); } @Override public void setInputParameters(final Map parameters) { inputParameters.putAll(parameters); } protected Object getInputParameter(final String paramName) throws IllegalStateException { return inputParameters.get(paramName); } @SuppressWarnings("unchecked") protected T getOptinalInputParameter(final String paramName) { return (T) inputParameters.get(paramName); } protected String getStringInputParameter(final String paramName) { return (String) getInputParameter(paramName); } protected void validateStringInputParameterIsNotNulOrEmpty(final String paramName) throws ConnectorValidationException { final String paramValue = (String) getInputParameter(paramName); if (paramValue == null || "".equals(paramValue.trim())) { throw new ConnectorValidationException("The input parameter '" + paramName + "' cannot be null or empty"); } } /** * {@inheritDoc} Default implementation returns true */ @Override public boolean shouldAutoAssignTaskIfSingleResult() { return true; } public void setAPIAccessor(final APIAccessor apiAccessor) { this.apiAccessor = apiAccessor; } public void setExecutionContext(final EngineExecutionContext executionContext) { this.executionContext = executionContext; } public APIAccessor getAPIAccessor() { return apiAccessor; } public EngineExecutionContext getExecutionContext() { return executionContext; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/filter/UserFilter.java ================================================ /** * 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.filter; import java.util.List; import java.util.Map; import org.bonitasoft.engine.connector.ConnectorValidationException; /** * The execution of this filter returns a list of userId. * It is used for the filtering of who can claim a UserTask based on its actorId * * @author Baptiste Mesta */ public interface UserFilter { /** * Set the input parameter for the filter. * * @param parameters * parameters is a map with parameter names and their value. */ void setInputParameters(Map parameters); /** * Validate the input parameters. Check the parameters types and boundaries. * * @throws ConnectorValidationException */ void validateInputParameters() throws ConnectorValidationException; /** * Execute the filter. * * @param actorName * the actor name of the task * @return the connector outputs map corresponding to the output definition. * @throws UserFilterException */ List filter(String actorName) throws UserFilterException; /** * This method make the engine assign automatically the task if the result of {@link #filter(String)} is only one * element. * i.e. when the task is filtered only for a single user * * @return true if we should assign task when there is only one result. */ boolean shouldAutoAssignTaskIfSingleResult(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/filter/UserFilterException.java ================================================ /** * 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.filter; import org.bonitasoft.engine.exception.BonitaException; /** * @author Baptiste Mesta */ public class UserFilterException extends BonitaException { private static final long serialVersionUID = -5462275472282384752L; public UserFilterException(final Throwable cause) { super(cause); } public UserFilterException(final String message) { super(message); } public UserFilterException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/form/FormMapping.java ================================================ /** * 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.form; import java.util.Date; import java.util.Objects; import org.bonitasoft.engine.bpm.BaseElement; /** * @author Baptiste Mesta */ public class FormMapping implements BaseElement { private static final long serialVersionUID = 1L; private long id; private long processDefinitionId; private FormMappingType type; private FormMappingTarget target; private String task; private Long pageId; private String pageURL; private String pageMappingKey; private long lastUpdatedBy; private Date lastUpdateDate; private boolean formRequired; public FormMapping() { } public FormMapping(long processDefinitionId, FormMappingType type, String task, String pageMappingKey) { this.processDefinitionId = processDefinitionId; this.type = type; this.task = task; this.pageMappingKey = pageMappingKey; } public FormMapping(long processDefinitionId, FormMappingType type, String pageMappingKey) { this.type = type; this.processDefinitionId = processDefinitionId; this.pageMappingKey = pageMappingKey; } public long getProcessDefinitionId() { return processDefinitionId; } public void setProcessDefinitionId(long processDefinitionId) { this.processDefinitionId = processDefinitionId; } public String getPageMappingKey() { return pageMappingKey; } public void setPageMappingKey(String pageMappingKey) { this.pageMappingKey = pageMappingKey; } @Override public long getId() { return id; } public void setId(long id) { this.id = id; } public String getTask() { return task; } public void setTask(String task) { this.task = task; } public Long getPageId() { return pageId; } public void setPageId(Long pageId) { this.pageId = pageId; } public String getURL() { return pageURL; } public void setPageURL(String pageURL) { this.pageURL = pageURL; } public FormMappingType getType() { return type; } public void setType(FormMappingType type) { this.type = type; } public long getLastUpdatedBy() { return lastUpdatedBy; } public void setLastUpdatedBy(long lastUpdatedBy) { this.lastUpdatedBy = lastUpdatedBy; } public Date getLastUpdateDate() { return lastUpdateDate; } public void setLastUpdateDate(Date lastUpdateDate) { this.lastUpdateDate = lastUpdateDate; } public FormMappingTarget getTarget() { return target; } public void setTarget(FormMappingTarget target) { this.target = target; } public boolean isFormRequired() { return formRequired; } public void setFormRequired(boolean required) { this.formRequired = required; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof FormMapping that)) return false; return Objects.equals(id, that.id) && Objects.equals(processDefinitionId, that.processDefinitionId) && Objects.equals(lastUpdatedBy, that.lastUpdatedBy) && Objects.equals(type, that.type) && Objects.equals(target, that.target) && Objects.equals(task, that.task) && Objects.equals(pageId, that.pageId) && Objects.equals(pageURL, that.pageURL) && Objects.equals(pageMappingKey, that.pageMappingKey) && Objects.equals(lastUpdateDate, that.lastUpdateDate); } @Override public int hashCode() { return Objects.hash(id, processDefinitionId, type, target, task, pageId, pageURL, pageMappingKey, lastUpdatedBy, lastUpdateDate); } @Override public String toString() { return "FormMapping{" + "id=" + id + ", processDefinitionId=" + processDefinitionId + ", type=" + type + ", target=" + target + ", task='" + task + '\'' + ", pageId=" + pageId + ", pageURL='" + pageURL + '\'' + ", pageMappingKey='" + pageMappingKey + '\'' + ", lastUpdatedBy=" + lastUpdatedBy + ", lastUpdateDate=" + lastUpdateDate + '}'; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/form/FormMappingSearchDescriptor.java ================================================ /** * 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.form; /** * @author Baptiste Mesta */ public final class FormMappingSearchDescriptor { public static final String ID = "id"; public static final String PROCESS_DEFINITION_ID = "processDefinitionId"; /** * @see org.bonitasoft.engine.form.FormMappingType */ public static final String TYPE = "type"; public static final String TASK = "task"; public static final String PAGE_ID = "pageId"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/home/BonitaHome.java ================================================ /** * 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.home; /** * Utility class to retrieve the bonita home based on the system property bonita.home *

* The bonita home is the folder containing all configuration files and working directories *

* * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @since 6.0.0 */ public abstract class BonitaHome { public static final String BONITA_HOME = "bonita.home"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/home/package-info.java ================================================ /** * Copyright (C) 2015 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. **/ /** *

* Contains classes related to the Bonita Home. *

* * @since 6.0.0 */ package org.bonitasoft.engine.home; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/ContactDataCreator.java ================================================ /** * 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.identity; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * Represents a helper for creating {@link ContactData}. Chaining is possible with this creator to ease the ContactData * creation. *
* For instance, new ContactDataCreator().setEmail("john.doe@bonitasoft.com").setPhoneNumber("012456789"); * * @author Matthieu Chaffotte * @author Celine Souchet * @since 6.0.0 */ public class ContactDataCreator implements Serializable { private static final long serialVersionUID = -1414989152963184543L; /** * Represents the available {@link ContactData} fields */ public enum ContactDataField { EMAIL, PHONE, MOBILE, FAX, BUILDING, ROOM, ADDRESS, ZIP_CODE, CITY, STATE, COUNTRY, WEBSITE; } private final Map fields; /** * Create a new creator instance */ public ContactDataCreator() { fields = new HashMap(5); } /** * @param email * The contact email address to create * @return The current {@link ContactDataCreator} */ public ContactDataCreator setEmail(final String email) { fields.put(ContactDataField.EMAIL, email); return this; } /** * @param phoneNumber * The contact phone number to create * @return The current {@link ContactDataCreator} */ public ContactDataCreator setPhoneNumber(final String phoneNumber) { fields.put(ContactDataField.PHONE, phoneNumber); return this; } /** * @param mobileNumber * The contact mobile number to create * @return The current {@link ContactDataCreator} */ public ContactDataCreator setMobileNumber(final String mobileNumber) { fields.put(ContactDataField.MOBILE, mobileNumber); return this; } /** * @param faxNumber * The contact fax number to create * @return The current {@link ContactDataCreator} */ public ContactDataCreator setFaxNumber(final String faxNumber) { fields.put(ContactDataField.FAX, faxNumber); return this; } /** * @param building * The contact building to create * @return The current {@link ContactDataCreator} */ public ContactDataCreator setBuilding(final String building) { fields.put(ContactDataField.BUILDING, building); return this; } /** * @param room * The contact room to create * @return The current {@link ContactDataCreator} */ public ContactDataCreator setRoom(final String room) { fields.put(ContactDataField.ROOM, room); return this; } /** * @param address * The contact address to create * @return The current {@link ContactDataCreator} */ public ContactDataCreator setAddress(final String address) { fields.put(ContactDataField.ADDRESS, address); return this; } /** * @param zipCode * The contact ZIP code to create * @return The current {@link ContactDataCreator} */ public ContactDataCreator setZipCode(final String zipCode) { fields.put(ContactDataField.ZIP_CODE, zipCode); return this; } /** * @param city * The contact city to create * @return The current {@link ContactDataCreator} */ public ContactDataCreator setCity(final String city) { fields.put(ContactDataField.CITY, city); return this; } /** * @param state * The contact state to create * @return The current {@link ContactDataCreator} */ public ContactDataCreator setState(final String state) { fields.put(ContactDataField.STATE, state); return this; } /** * @param country * The contact country to create * @return The current {@link ContactDataCreator} */ public ContactDataCreator setCountry(final String country) { fields.put(ContactDataField.COUNTRY, country); return this; } /** * @param website * The contact web site address to create * @return The current {@link ContactDataCreator} */ public ContactDataCreator setWebsite(final String website) { fields.put(ContactDataField.WEBSITE, website); return this; } /** * @return The current contact data information to create */ public Map getFields() { return fields; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/ContactDataUpdater.java ================================================ /** * 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.identity; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * represents a helper for updating a {@link ContactData} * * @author Emmanuel Duchastenier * @author Matthieu Chaffotte * @author Celine Souchet * @see ContactData * @since 6.0.0 */ public class ContactDataUpdater implements Serializable { private static final long serialVersionUID = 7871478791386229141L; /** * The available contact fields */ public enum ContactDataField { EMAIL, PHONE, MOBILE, FAX, BUILDING, ROOM, ADDRESS, ZIP_CODE, CITY, STATE, COUNTRY, WEBSITE } private final Map fields; /** * Default Constructor. */ public ContactDataUpdater() { fields = new HashMap(5); } /** * @param email * The contact email to create */ public void setEmail(final String email) { fields.put(ContactDataField.EMAIL, email); } /** * @param phoneNumber * The contact phone number to create */ public void setPhoneNumber(final String phoneNumber) { fields.put(ContactDataField.PHONE, phoneNumber); } /** * @param mobileNumber * The contact mobile number to create */ public void setMobileNumber(final String mobileNumber) { fields.put(ContactDataField.MOBILE, mobileNumber); } /** * @param faxNumber * The contact fax number to create */ public void setFaxNumber(final String faxNumber) { fields.put(ContactDataField.FAX, faxNumber); } /** * @param building * The contact building to create */ public void setBuilding(final String building) { fields.put(ContactDataField.BUILDING, building); } /** * @param room * The contact room to create */ public void setRoom(final String room) { fields.put(ContactDataField.ROOM, room); } /** * @param address * The contact address to create */ public void setAddress(final String address) { fields.put(ContactDataField.ADDRESS, address); } /** * @param zipCode * The contact ZIP code to create */ public void setZipCode(final String zipCode) { fields.put(ContactDataField.ZIP_CODE, zipCode); } /** * @param city * The contact city to create */ public void setCity(final String city) { fields.put(ContactDataField.CITY, city); } /** * @param state * The contact state to create */ public void setState(final String state) { fields.put(ContactDataField.STATE, state); } /** * @param country * The contact country to create */ public void setCountry(final String country) { fields.put(ContactDataField.COUNTRY, country); } /** * @param website * The contact web site address to create */ public void setWebsite(final String website) { fields.put(ContactDataField.WEBSITE, website); } /** * @return The current contact data information to update */ public Map getFields() { return fields; } /** * @return True if there are some contact data to update */ public boolean hasFields() { return !fields.isEmpty(); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/CustomUserInfoDefinitionCreator.java ================================================ /** * 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.identity; import java.io.Serializable; import java.util.Objects; /** * represents a helper for creating a {@link CustomUserInfoDefinition}. * * @author Vincent Elcrin * @see CustomUserInfoDefinition * @since 6.3.1 */ public class CustomUserInfoDefinitionCreator implements Serializable { private static final long serialVersionUID = 6929368716340973445L; private String name; private String description; /** * creates a new {@link CustomUserInfoDefinitionCreator} with the specified name * * @param name the name to set */ public CustomUserInfoDefinitionCreator(final String name) { this.name = name; } /** * creates a new {@link CustomUserInfoDefinitionCreator} with the specified name and description * * @param name the name to set * @param description the description to set */ public CustomUserInfoDefinitionCreator(final String name, final String description) { this(name); this.description = description; } /** * @return the {@link CustomUserInfoDefinitionCreator}'s name to create */ public String getName() { return name; } /** * @return the {@link CustomUserInfoDefinitionCreator}'s description to create */ public String getDescription() { return description; } /** * @param name the {@link CustomUserInfoDefinitionCreator}'s name to create */ public void setName(final String name) { this.name = name; } /** * @param description the {@link CustomUserInfoDefinitionCreator}'s name to create */ public void setDescription(final String description) { this.description = description; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CustomUserInfoDefinitionCreator that = (CustomUserInfoDefinitionCreator) o; return Objects.equals(name, that.name) && Objects.equals(description, that.description); } @Override public int hashCode() { return Objects.hash(name, description); } @Override public String toString() { return "CustomUserInfoDefinitionCreator{" + "name='" + name + '\'' + ", description='" + description + '\'' + '}'; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/CustomUserInfoValueSearchDescriptor.java ================================================ /** * 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.identity; /** * Defines the fields that can be used as filters and soring by * {@link org.bonitasoft.engine.api.CustomUserInfoAPI#searchCustomUserInfoValues(org.bonitasoft.engine.search.SearchOptions)}. * * @author Vincent Elcrin * @since 6.3.1 * @see org.bonitasoft.engine.api.CustomUserInfoAPI#searchCustomUserInfoValues(org.bonitasoft.engine.search.SearchOptions) */ public final class CustomUserInfoValueSearchDescriptor { /** * Refers to {@link org.bonitasoft.engine.identity.CustomUserInfoValue#getUserId()} * * @see CustomUserInfoValue#getUserId() */ public static final String USER_ID = "userId"; /** * Refers to {@link org.bonitasoft.engine.identity.CustomUserInfoValue#getDefinitionId()} * * @see CustomUserInfoValue#getDefinitionId() */ public static final String DEFINITION_ID = "definitionId"; /** * Refers to {@link org.bonitasoft.engine.identity.CustomUserInfoValue#getValue()} * * @see CustomUserInfoValue#getValue() */ public static final String VALUE = "value"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/CustomUserInfoValueUpdater.java ================================================ /** * 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.identity; import java.io.Serializable; /** * represents a helper for updating {@link CustomUserInfoValue} * * @author Vincent Elcrin * @see CustomUserInfoValue * @since 6.3.1 */ public class CustomUserInfoValueUpdater implements Serializable { private static final long serialVersionUID = -2699448153857398426L; private final String value; /** * creates a new instance of {@link CustomUserInfoValueUpdater} with a value to update on a * {@link CustomUserInfoValue} * * @param value the value to update on a {@link CustomUserInfoValue} */ public CustomUserInfoValueUpdater(final String value) { this.value = value; } /** * @return the value to update on a {@link CustomUserInfoValue} */ public String getValue() { return value; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/GroupCreator.java ================================================ /** * 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.identity; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * represent a helper for creating a {@link Group} * * @author Matthieu Chaffotte * @author Celine Souchet * @see Group * @since 6.0.0 */ public class GroupCreator implements Serializable { private static final long serialVersionUID = -1546623947528297571L; /** * represents the available {@link Group} field */ public enum GroupField { NAME, DISPLAY_NAME, DESCRIPTION, @Deprecated ICON_NAME, @Deprecated ICON_PATH, PARENT_PATH, ICON_FILENAME, ICON_CONTENT } private final Map fields; /** * creates a new {@link GroupCreator} with a group name to create * * @param name * The name of the group that will be created */ public GroupCreator(final String name) { fields = new HashMap<>(3); fields.put(GroupField.NAME, name); } /** * @param parentPath * The group's parent path to create */ public void setParentPath(final String parentPath) { fields.put(GroupField.PARENT_PATH, parentPath); } /** * @param displayName * The group's display to create */ public GroupCreator setDisplayName(final String displayName) { fields.put(GroupField.DISPLAY_NAME, displayName); return this; } /** * @param description * The group's description to create */ public GroupCreator setDescription(final String description) { fields.put(GroupField.DESCRIPTION, description); return this; } /** * @param iconName * The group's icon name to create * @deprecated since 7.3.0 use #setIcon */ @Deprecated public GroupCreator setIconName(final String iconName) { return this; } /** * @param iconPath * The group's icon file path to create * @deprecated since 7.3.0 use #setIcon */ @Deprecated public GroupCreator setIconPath(final String iconPath) { return this; } public GroupCreator setIcon(String filename, byte[] content) { fields.put(GroupField.ICON_FILENAME, filename); fields.put(GroupField.ICON_CONTENT, content); return this; } /** * @return The information associated with the group to create */ public Map getFields() { return fields; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; GroupCreator that = (GroupCreator) o; return Objects.equals(fields, that.fields); } @Override public int hashCode() { return Objects.hash(fields); } @Override public String toString() { return "GroupCreator{" + "fields=" + fields + '}'; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/GroupCriterion.java ================================================ /** * 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.identity; /** * represents group criterion sort orders * * @author Lu Kai * @author Matthieu Chaffotte * @see Group * @since 6.0.0 */ public enum GroupCriterion { /** * Name ascending order */ NAME_ASC, /** * Label ascending order */ LABEL_ASC, /** * Name descending order */ NAME_DESC, /** * Label descending order */ LABEL_DESC; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/GroupNotFoundException.java ================================================ /** * 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.identity; import org.bonitasoft.engine.exception.NotFoundException; /** * thrown when a {@link Group} is not found in the organization * * @author Kai Lu * @author Matthieu Chaffotte * @see Group * @since 6.0.0 */ public class GroupNotFoundException extends NotFoundException { private static final long serialVersionUID = -3825194748072808390L; /** * creates a new instance with the cause of the exception * * @param cause the cause that raised this exception */ public GroupNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/GroupSearchDescriptor.java ================================================ /** * 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.identity; /** * holds constants about {@link Group} search filters * * @author Matthieu Chaffotte * @see Group * @since 6.0.0 */ public class GroupSearchDescriptor { /** filter search on Group's id */ public static final String ID = "id"; /** filter search on Group's name */ public static final String NAME = "name"; /** filter search on Group's parent path */ public static final String PARENT_PATH = "parentPath"; /** filter search on Group's display name */ public static final String DISPLAY_NAME = "displayName"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/GroupUpdater.java ================================================ /** * 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.identity; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * represent a helper fpr updating a {@link Group} * * @author Matthieu Chaffotte * @author Celine Souchet * @see Group * @since 6.0.0 */ public class GroupUpdater implements Serializable { private static final long serialVersionUID = 728214104237982027L; /** * represents the available {@link Group} fields */ public enum GroupField { NAME, DISPLAY_NAME, DESCRIPTION, @Deprecated ICON_NAME, @Deprecated ICON_PATH, PARENT_PATH, ICON_FILENAME, ICON_CONTENT } private final Map fields; /** * Default Constructor. */ public GroupUpdater() { fields = new HashMap<>(3); } /** * @param name * The group's name to update */ public GroupUpdater updateName(final String name) { fields.put(GroupField.NAME, name); return this; } /** * @param displayName * The group's display name to update */ public GroupUpdater updateDisplayName(final String displayName) { fields.put(GroupField.DISPLAY_NAME, displayName); return this; } /** * @param description * The group's description to update */ public GroupUpdater updateDescription(final String description) { fields.put(GroupField.DESCRIPTION, description); return this; } /** * @param iconName * The group's icon name to update * @deprecated since 7.3.0 use #updateIcon */ @Deprecated public GroupUpdater updateIconName(final String iconName) { return this; } /** * @param iconPath * The group's icon path to update * @deprecated since 7.3.0 use #updateIcon */ @Deprecated public GroupUpdater updateIconPath(final String iconPath) { return this; } public GroupUpdater updateIcon(String filename, byte[] content) { fields.put(GroupField.ICON_FILENAME, filename); fields.put(GroupField.ICON_CONTENT, content); return this; } /** * @param parentPath * The group's parent path to update */ public GroupUpdater updateParentPath(final String parentPath) { fields.put(GroupField.PARENT_PATH, parentPath); return this; } /** * @return The group's fields to update */ public Map getFields() { return fields; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; GroupUpdater that = (GroupUpdater) o; return Objects.equals(fields, that.fields); } @Override public int hashCode() { return Objects.hash(fields); } @Override public String toString() { return "GroupUpdater{" + "fields=" + fields + '}'; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/ImportPolicy.java ================================================ /** * 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.identity; /** * Define how to handle conflicts when an organization is imported. *
* Check {@link org.bonitasoft.engine.api.OrganizationAPI#importOrganization(String)} and * {@link org.bonitasoft.engine.api.OrganizationAPI#importOrganization(String, ImportPolicy)} to see the usage. * * @see org.bonitasoft.engine.api.OrganizationAPI * @author Baptiste Mesta * @since 6.0.0 */ public enum ImportPolicy { /** * Existing items in current organization are updated to have the values of the item in the given organization */ MERGE_DUPLICATES, /** * If an item already exists the import fail and is reverted to previous state */ FAIL_ON_DUPLICATES, /** * Existing items are kept */ IGNORE_DUPLICATES } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/InvalidOrganizationFileFormatException.java ================================================ /** * 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.identity; import org.bonitasoft.engine.InvalidFileFormatException; /** * thrown to indicate an organization import error caused by a parsing error * * @author Danila Mazour * @since 7.9.0 */ public class InvalidOrganizationFileFormatException extends OrganizationImportException implements InvalidFileFormatException { /** * create a new exception instance with the given message * * @param message the exception message */ public InvalidOrganizationFileFormatException(final String message) { super(message); } /** * creates a new exception instance with the given exception as cause * * @param cause the exception cause */ public InvalidOrganizationFileFormatException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/MembershipNotFoundException.java ================================================ /** * 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.identity; import org.bonitasoft.engine.exception.NotFoundException; /** * thrown when a {@link UserMembership} is not found * * @author Matthieu Chaffotte * @see UserMembership * @since 6.0.0 */ public class MembershipNotFoundException extends NotFoundException { private static final long serialVersionUID = -264860778869560830L; /** * creates a new instance of the exception with the given cause * * @param cause the cause of the exception */ public MembershipNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/OrganizationExportException.java ================================================ /** * 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.identity; import org.bonitasoft.engine.exception.ExecutionException; /** * thrown to indicate an organization export error * * @author Yanyan Liu * @author Matthieu Chaffotte * @since 6.0.0 */ public class OrganizationExportException extends ExecutionException { private static final long serialVersionUID = 3608804813319882731L; /** * creates a new exception instance with the given exception as cause * * @param cause the exception cause */ public OrganizationExportException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/OrganizationImportException.java ================================================ /** * 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.identity; import org.bonitasoft.engine.exception.ExecutionException; /** * thrown to indicate an organization import error * * @author Yanyan Liu * @author Matthieu Chaffotte * @since 6.0.0 */ public class OrganizationImportException extends ExecutionException { private static final long serialVersionUID = -6086310296760629566L; /** * create a new exception instance with the given message * * @param message the exception message */ public OrganizationImportException(final String message) { super(message); } /** * creates a new exception instance with the given exception as cause * * @param cause the exception cause */ public OrganizationImportException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/RoleCreator.java ================================================ /** * 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.identity; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * represents a helper for creating a {@link Role}. Chaining is possible with this creator to ease the {@link Role} * creation. *
* For instance, new RoleCreator("member").setDisplayName("Member").setIconName("userIcon"); * * @author Matthieu Chaffotte * @author Celine Souchet * @see Role * @since 6.0.0 */ public class RoleCreator implements Serializable { private static final long serialVersionUID = -1414989152963184543L; /** * represents the available {@link Role} field */ public enum RoleField { NAME, DISPLAY_NAME, DESCRIPTION, @Deprecated ICON_NAME, @Deprecated ICON_PATH, ICON_FILENAME, ICON_CONTENT } private final Map fields; /** * create a new creator instance with a given role name * * @param name * The name of the role to create */ public RoleCreator(final String name) { fields = new HashMap<>(5); fields.put(RoleField.NAME, name); } /** * @param displayName * The role's display name to create * @return The current {@link RoleCreator} */ public RoleCreator setDisplayName(final String displayName) { fields.put(RoleField.DISPLAY_NAME, displayName); return this; } /** * @param description * The role's description to create * @return The current {@link RoleCreator} */ public RoleCreator setDescription(final String description) { fields.put(RoleField.DESCRIPTION, description); return this; } /** * @param iconName * The role's icon name to create * @return The current {@link RoleCreator} * @deprecated since 7.3.0 use #setIcon */ @Deprecated public RoleCreator setIconName(final String iconName) { return this; } /** * @param iconPath * The role's icon path to create * @return The current {@link RoleCreator} * @deprecated since 7.3.0 use #setIcon */ @Deprecated public RoleCreator setIconPath(final String iconPath) { return this; } /** * set the icon on the role to be created * * @param filename the filename of the icon * @param content the content of the icon * @return the role created */ public RoleCreator setIcon(String filename, byte[] content) { fields.put(RoleField.ICON_FILENAME, filename); fields.put(RoleField.ICON_CONTENT, content); return this; } /** * @return The current role's information to create */ public Map getFields() { return fields; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RoleCreator that = (RoleCreator) o; return Objects.equals(fields, that.fields); } @Override public int hashCode() { return Objects.hash(fields); } @Override public String toString() { return "RoleCreator{" + "fields=" + fields + '}'; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/RoleCriterion.java ================================================ /** * 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.identity; /** * list the available {@link Role} sort orders * * @author Yanyan Liu * @see Role * @since 6.0.0 */ public enum RoleCriterion { /** * Name ascending order */ NAME_ASC, /** * Label ascending order */ DISPLAY_NAME_ASC, /** * Name descending order */ NAME_DESC, /** * Label descending order */ DISPLAY_NAME_DESC, } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/RoleNotFoundException.java ================================================ /** * 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.identity; import org.bonitasoft.engine.exception.NotFoundException; /** * thrown when a {@link Role} is not found in organization * * @author Bole Zhang * @author Matthieu Chaffotte * @see Role * @since 6.0.0 */ public class RoleNotFoundException extends NotFoundException { private static final long serialVersionUID = 7582998881291080021L; /** * creates a new exception instance with the given exception as cause * * @param cause the exception cause */ public RoleNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/RoleSearchDescriptor.java ================================================ /** * 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.identity; /** * holds constants about {@link Role} search filters. * * @author Matthieu Chaffotte * @see Role * @since 6.0.0 */ public class RoleSearchDescriptor { /** filter search on Role's id */ public static final String ID = "id"; /** filter search on Role's name */ public static final String NAME = "name"; /** filter search on Role's display name */ public static final String DISPLAY_NAME = "displayName"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/RoleUpdater.java ================================================ /** * 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.identity; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * represents a helper for updating a {@link Role}. Chaining is possible with this updator to ease the {@link Role} * update. *
* For instance, new RoleUpdater("member").setDisplayName("Member").setIconName("userIcon"); * * @author Matthieu Chaffotte * @author Celine Souchet * @see Role * @since 6.0.0 */ public class RoleUpdater implements Serializable { private static final long serialVersionUID = 728214104237982027L; /** * represent the available {@link Role} fields */ public enum RoleField { NAME, DISPLAY_NAME, DESCRIPTION, @Deprecated ICON_NAME, @Deprecated ICON_PATH, ICON_FILENAME, ICON_CONTENT } private final Map fields; /** * Default Constructor. */ public RoleUpdater() { fields = new HashMap<>(5); } /** * @param name * The role's new name to update * @return The current {@link RoleUpdater} for chaining purpose */ public RoleUpdater setName(final String name) { fields.put(RoleField.NAME, name); return this; } /** * @param displayName * The role's display name to update * @return The current {@link RoleUpdater} for chaining purpose */ public RoleUpdater setDisplayName(final String displayName) { fields.put(RoleField.DISPLAY_NAME, displayName); return this; } /** * @param description * The role's description to update * @return The current {@link RoleUpdater} for chaining purpose */ public RoleUpdater setDescription(final String description) { fields.put(RoleField.DESCRIPTION, description); return this; } /** * @param iconName * The role's icon name to update * @return The current {@link RoleUpdater} for chaining purpose * @deprecated since 7.3.0 use #setIcon */ @Deprecated public RoleUpdater setIconName(final String iconName) { return this; } /** * @param iconPath * The role's icon path to update * @return The current {@link RoleUpdater} for chaining purpose * @deprecated since 7.3.0 use #setIcon */ @Deprecated public RoleUpdater setIconPath(final String iconPath) { return this; } /** * set the icon on the role to be created * * @param filename the filename of the icon * @param content the content of the icon * @return the role created */ public RoleUpdater setIcon(String filename, byte[] content) { fields.put(RoleUpdater.RoleField.ICON_FILENAME, filename); fields.put(RoleUpdater.RoleField.ICON_CONTENT, content); return this; } /** * @return The role's fields to update */ public Map getFields() { return fields; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RoleUpdater that = (RoleUpdater) o; return Objects.equals(fields, that.fields); } @Override public int hashCode() { return Objects.hash(fields); } @Override public String toString() { return "RoleUpdater{" + "fields=" + fields + '}'; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/UserCreator.java ================================================ /** * 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.identity; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Objects; import org.bonitasoft.engine.identity.ContactDataCreator.ContactDataField; /** * represents a helper for creating {@link User}. Chaining is possible with this creator to ease the {@link User} * creation. *
* For instance, new UserCreator("john.doe", "password").setFirstName("John").setLastName("Doe"); * * @author Matthieu Chaffotte * @see User * @since 6.0.0 */ public class UserCreator implements Serializable { private static final long serialVersionUID = -1414989152963184543L; /** * represents the available {@link User} field */ public enum UserField { NAME, PASSWORD, FIRST_NAME, LAST_NAME, @Deprecated ICON_NAME, @Deprecated ICON_PATH, TITLE, JOB_TITLE, MANAGER_ID, ENABLED, ICON_FILENAME, ICON_CONTENT } private final Map fields; private final Map persoFields; private final Map proFields; /** * create a new creator instance with a given user name and password * * @param name the name of the user to create * @param password the password of the user to create */ public UserCreator(final String name, final String password) { fields = new HashMap<>(5); fields.put(UserField.NAME, name); fields.put(UserField.PASSWORD, password); persoFields = new HashMap<>(); proFields = new HashMap<>(); } /** * @param firstName the user's firstname to create * @return the current {@link UserCreator} */ public UserCreator setFirstName(final String firstName) { fields.put(UserField.FIRST_NAME, firstName); return this; } /** * @param lastName the user's lastName to create * @return the current {@link UserCreator} */ public UserCreator setLastName(final String lastName) { fields.put(UserField.LAST_NAME, lastName); return this; } /** * @param iconName the user's icon name to create * @return the current {@link UserCreator} * @deprecated since 7.3.0 use #setIcon */ @Deprecated public UserCreator setIconName(final String iconName) { return this; } /** * @param iconPath the user's icon path to create * @return the current {@link UserCreator} * @deprecated since 7.3.0 use #setIcon */ @Deprecated public UserCreator setIconPath(final String iconPath) { return this; } /** * @param title the user's title to create * @return the current {@link UserCreator} */ public UserCreator setTitle(final String title) { fields.put(UserField.TITLE, title); return this; } /** * @param jobTitle the user's jobTitle to create * @return the current {@link UserCreator} */ public UserCreator setJobTitle(final String jobTitle) { fields.put(UserField.JOB_TITLE, jobTitle); return this; } /** * @param managerUserId the user's manager id to create * @return the current {@link UserCreator} */ public UserCreator setManagerUserId(final long managerUserId) { fields.put(UserField.MANAGER_ID, managerUserId); return this; } /** * @param enabled enabled Boolean set to true if the user is enabled inside the organization * @return the current {@link UserCreator} */ public UserCreator setEnabled(final boolean enabled) { fields.put(UserField.ENABLED, enabled); return this; } /** * @return the current user information to create */ public Map getFields() { return fields; } /** * @return the current user personal information to create */ public Map getPersoFields() { return persoFields; } /** * @return the current user professional information to create */ public Map getProFields() { return proFields; } /** * @param creator the user's personal contact information to create * @return the current {@link UserCreator} */ public UserCreator setPersonalContactData(final ContactDataCreator creator) { if (creator != null && creator.getFields() != null) { persoFields.putAll(creator.getFields()); } return this; } /** * @param creator the user's professional contact information to create * @return the current {@link UserCreator} */ public UserCreator setProfessionalContactData(final ContactDataCreator creator) { if (creator != null && creator.getFields() != null) { proFields.putAll(creator.getFields()); } return this; } public UserCreator setIcon(String filename, byte[] content) { fields.put(UserField.ICON_FILENAME, filename); fields.put(UserField.ICON_CONTENT, content); return this; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UserCreator that = (UserCreator) o; return Objects.equals(fields, that.fields) && Objects.equals(persoFields, that.persoFields) && Objects.equals(proFields, that.proFields); } @Override public int hashCode() { return Objects.hash(fields, persoFields, proFields); } @Override public String toString() { return "UserCreator{" + "fields=" + fields + ", persoFields=" + persoFields + ", proFields=" + proFields + '}'; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/UserCriterion.java ================================================ /** * 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.identity; /** * lists the available {@link User} sort orders * * @author Matthieu Chaffotte * @see User * @since 6.0.0 */ public enum UserCriterion { /** * First name ascending order */ FIRST_NAME_ASC, /** * Last name ascending order */ LAST_NAME_ASC, /** * User name ascending order */ USER_NAME_ASC, /** * First name descending order */ FIRST_NAME_DESC, /** * Last name descending order */ LAST_NAME_DESC, /** * user name descending order */ USER_NAME_DESC; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/UserMembershipCriterion.java ================================================ /** * 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.identity; /** * lists the available {@link User} sort orders * * @author Bole Zhang * @author Matthieu Chaffotte * @author Baptiste Mesta * @see UserMembership * @since 6.0.0 */ public enum UserMembershipCriterion { /** * roleName ascending order */ ROLE_NAME_ASC, /** * groupName ascending order */ GROUP_NAME_ASC, /** * roleName descending order */ ROLE_NAME_DESC, /** * groupName descending order */ GROUP_NAME_DESC, /** * assigned date ascending order */ ASSIGNED_DATE_ASC, /** * assigned date descending order */ ASSIGNED_DATE_DESC } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/UserNotFoundException.java ================================================ /** * 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.identity; import org.bonitasoft.engine.exception.NotFoundException; /** * thrown when a {@link User} is not found in the organization * * @author Matthieu Chaffotte * @see User * @since 6.0.0 */ public class UserNotFoundException extends NotFoundException { private static final long serialVersionUID = 8392620780257647179L; /** * creates a new exception instance with the given message * * @param message the exception message */ public UserNotFoundException(final String message) { super(message); } /** * creates a new exception instance with the given exception as cause * * @param cause the exception cause */ public UserNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/UserSearchDescriptor.java ================================================ /** * 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.identity; /** * holds constants about {@link User} search filters. * * @author Matthieu Chaffotte * @author Emmanuel Duchastenier * @author Anthony Birembaut * @see User * @since 6.0.0 */ public final class UserSearchDescriptor { /** filter search on User's id */ public static final String ID = "id"; /** filter search on User's username */ public static final String USER_NAME = "userName"; /** filter search on User's firstname */ public static final String FIRST_NAME = "firstName"; /** filter search on User's lastname */ public static final String LAST_NAME = "lastName"; /** filter search on User's group id */ public static final String GROUP_ID = "groupId"; /** filter search on User's role id */ public static final String ROLE_ID = "roleId"; /** filter search on the User's manager user id */ public static final String MANAGER_USER_ID = "managerUserId"; /** filter search on User's activation */ public static final String ENABLED = "enabled"; /** filter search on User's last connection date */ public static final String LAST_CONNECTION = "lastConnection"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/UserUpdater.java ================================================ /** * 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.identity; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * represents a helper for updating a {@link User}. Chaining is possible with this updator to ease the {@link User} * update. *
* For instance, new UserUpdater.setUsername("john.doe").setFirstname("John").setLastname("Doe"); * * @author Matthieu Chaffotte * @author Celine Souchet * @see User * @since 6.0.0 */ public class UserUpdater implements Serializable { private static final long serialVersionUID = 4565052647320534796L; /** * represent the available {@link User} fields */ public enum UserField { USER_NAME, PASSWORD, FIRST_NAME, LAST_NAME, @Deprecated ICON_NAME, @Deprecated ICON_PATH, TITLE, JOB_TITLE, MANAGER_ID, ENABLED, ICON_FILENAME, ICON_CONTENT } private final Map fields; private ContactDataUpdater persoContactUpdater; private ContactDataUpdater proContactUpdater; /** * Default Constructor. */ public UserUpdater() { fields = new HashMap<>(5); } /** * @param name the user's username to update * @return the current {@link UserUpdater} for chaining purpose */ public UserUpdater setUserName(final String name) { fields.put(UserField.USER_NAME, name); return this; } /** * @param password the user's password to update * @return the current {@link UserUpdater} for chaining purpose */ public UserUpdater setPassword(final String password) { fields.put(UserField.PASSWORD, password); return this; } /** * @param firstName the user's firstname to update * @return the current {@link UserUpdater} for chaining purpose */ public UserUpdater setFirstName(final String firstName) { fields.put(UserField.FIRST_NAME, firstName); return this; } /** * @param lastName the user's lastname to update * @return the current {@link UserUpdater} for chaining purpose */ public UserUpdater setLastName(final String lastName) { fields.put(UserField.LAST_NAME, lastName); return this; } /** * @param iconName the user's icon name to update * @return the current {@link UserUpdater} for chaining purpose * @deprecated since 7.3.0 use #setIcon */ @Deprecated public UserUpdater setIconName(final String iconName) { return this; } /** * @param managerId the user's manager id to update * @return the current {@link UserUpdater} for chaining purpose */ public UserUpdater setManagerId(final long managerId) { fields.put(UserField.MANAGER_ID, managerId); return this; } /** * @param iconPath the user's icon path to update * @return the current {@link UserUpdater} for chaining purpose * @deprecated since 7.3.0 use #setIcon */ @Deprecated public UserUpdater setIconPath(final String iconPath) { return this; } /** * @param title the user's title to update * @return the current {@link UserUpdater} for chaining purpose */ public UserUpdater setTitle(final String title) { fields.put(UserField.TITLE, title); return this; } /** * @param jobTitle the user's job title to update * @return the current {@link UserUpdater} for chaining purpose */ public UserUpdater setJobTitle(final String jobTitle) { fields.put(UserField.JOB_TITLE, jobTitle); return this; } /** * @param enabled allow to know if the current user is enabled inside organization * @return the current {@link UserUpdater} for chaining purpose */ public UserUpdater setEnabled(final boolean enabled) { fields.put(UserField.ENABLED, enabled); return this; } /** * @return the current user information to udpate */ public Map getFields() { return fields; } /** * @param persoContactUpdater the user's personal contact information to update * @return the current {@link UserUpdater} for chaining purpose */ public UserUpdater setPersonalContactData(final ContactDataUpdater persoContactUpdater) { this.persoContactUpdater = persoContactUpdater; return this; } /** * @param proContactUpdater the user's professional contact information to update * @return the current {@link UserUpdater} for chaining purpose */ public UserUpdater setProfessionalContactData(final ContactDataUpdater proContactUpdater) { this.proContactUpdater = proContactUpdater; return this; } /** * @return the user's personal contact updater object */ public ContactDataUpdater getPersoContactUpdater() { return persoContactUpdater; } /** * @return the professional contact updater object */ public ContactDataUpdater getProContactUpdater() { return proContactUpdater; } /** * Has this updater at least one field to update (directly or in its personal / professional contact data)? * * @return true if there is at least one field to update */ public boolean hasFields() { return !getFields().isEmpty() || getPersoContactUpdater() != null && getPersoContactUpdater().hasFields() || getProContactUpdater() != null && getProContactUpdater().hasFields(); } public UserUpdater setIcon(String filename, byte[] content) { fields.put(UserField.ICON_FILENAME, filename); fields.put(UserField.ICON_CONTENT, content); return this; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UserUpdater that = (UserUpdater) o; return Objects.equals(fields, that.fields) && Objects.equals(persoContactUpdater, that.persoContactUpdater) && Objects.equals(proContactUpdater, that.proContactUpdater); } @Override public int hashCode() { return Objects.hash(fields, persoContactUpdater, proContactUpdater); } @Override public String toString() { return "UserUpdater{" + "fields=" + fields + ", persoContactUpdater=" + persoContactUpdater + ", proContactUpdater=" + proContactUpdater + '}'; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/identity/package-info.java ================================================ /** * Copyright (C) 2015 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. **/ /** *

Manages information about an organization, that is, the set of users who can act in processes and the groups and * the organization's roles and the relation * between them. *

Contains helpers to create and updates organization's groups, roles, users and memberships

* * @see org.bonitasoft.engine.identity.Group * @see org.bonitasoft.engine.identity.Role * @see org.bonitasoft.engine.identity.User * @see org.bonitasoft.engine.identity.UserMembership * @since 6.0.0 */ package org.bonitasoft.engine.identity; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/io/FileContent.java ================================================ /** * Copyright (C) 2023 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.io; import java.io.InputStream; public class FileContent { protected String fileName; protected InputStream inputStream; protected String mimeType; protected long size = -1L; public FileContent(String fileName, InputStream inputStream, String mimeType) { this.fileName = fileName; this.inputStream = inputStream; this.mimeType = mimeType; } public FileContent(String fileName, InputStream inputStream, String mimeType, long size) { this.fileName = fileName; this.inputStream = inputStream; this.mimeType = mimeType; this.size = size; } public String getFileName() { return fileName; } public InputStream getInputStream() { return inputStream; } public String getMimeType() { return mimeType; } public long getSize() { return size; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/io/FileOperations.java ================================================ /** * 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.io; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.UUID; import java.util.zip.ZipFile; /** * @author Baptiste Mesta * @author Emmanuel Duchastenier * @author Danila Mazour */ public class FileOperations { private FileOperations() { // private constructor } /** * Retrieve the content of a file inside a ZIP file. * * @param zip ZIP file to parse * @param filePath path of the file to search inside the ZIP * @return the bytes read from the searched file * @throws FileNotFoundException if the ZIP file does not contain the searched file * @throws java.util.zip.ZipException if a ZIP format error has occurred (e.g. the input zip file is not a ZIP) * @throws IOException if an I/O error has occurred */ public static byte[] getFileFromZip(File zip, String filePath) throws IOException { try (var zipFile = new ZipFile(zip)) { var entry = zipFile.getEntry(filePath); if (entry == null) { throw new FileNotFoundException(String.format("'%s' not found in %s", filePath, zip.getName())); } try (var is = zipFile.getInputStream(entry)) { return is.readAllBytes(); } } } public static void updateFileContent(File zip, String filePath, InputStream newContent) throws IOException { Path zipFilePath = zip.toPath(); try (FileSystem fs = FileSystems.newFileSystem(zipFilePath, (ClassLoader) null)) { Path source = fs.getPath(filePath); Path temp = fs.getPath("./temp_" + UUID.randomUUID().toString()); Files.write(temp, newContent.readAllBytes(), StandardOpenOption.CREATE_NEW); Files.move(temp, source, REPLACE_EXISTING); } } public static String read(File file) throws IOException { return Files.readString(file.toPath(), UTF_8); } public static byte[] readFully(File file) throws IOException { return Files.readAllBytes(file.toPath()); } public static byte[] readFully(InputStream in) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buf = new byte[1024]; int n; while ((n = in.read(buf)) > 0) { out.write(buf, 0, n); } return out.toByteArray(); } public static boolean isBarFile(String fileName) { return fileName.endsWith(".bar"); } public static boolean isXmlFile(String fileName) { return fileName.endsWith(".xml"); } public static boolean isZipFile(File file) { return file.getName().endsWith(".zip"); } public static InputStream asInputStream(byte[] bytes) { return new ByteArrayInputStream(bytes); } public static InputStream asInputStream(String content) { return new ByteArrayInputStream(content.getBytes(UTF_8)); } public static InputStream resource(String name) { return FileOperations.class.getResourceAsStream(name); } /** * Gets the contents of a classpath resource as a byte array. */ public static byte[] resourceAsBytes(String name) throws IOException { return resource(name).readAllBytes(); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/io/IOUtil.java ================================================ /** * Copyright (C) 2023 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.io; import static java.util.Arrays.stream; import static java.util.Collections.emptyList; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.net.URI; import java.net.URL; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import javax.tools.ToolProvider; import lombok.extern.slf4j.Slf4j; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ @Slf4j public class IOUtil { private IOUtil() { // Utility class } public static final String TMP_DIRECTORY = System.getProperty("java.io.tmpdir"); private static final int BUFFER_SIZE = 100000; private static final String CLASS_EXT = ".class"; public static byte[] generateJar(final Class... classes) throws IOException { return generateJar(getResources(classes)); } public static byte[] generateJar(String className, String... content) throws IOException { return generateJar(emptyList(), new AbstractMap.SimpleEntry<>(className, String.join("\n", content))); } static class StringJavaFileObject extends SimpleJavaFileObject { private final String sourceCode; StringJavaFileObject(String className, String content) { super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); this.sourceCode = content; } public CharSequence getCharContent(boolean var1) { return this.sourceCode; } } public static byte[] generateJar(Map.Entry... classFiles) throws IOException { return generateJar(emptyList(), classFiles); } public static byte[] generateJar(List additionalJar, String className, String... content) throws IOException { return generateJar(additionalJar, new AbstractMap.SimpleEntry<>(className, String.join("\n", content))); } public static byte[] generateJar(List additionalJar, Map.Entry... classFiles) throws IOException { List sourceFiles = stream(classFiles).map(classFile -> { StringJavaFileObject sourceFile = new StringJavaFileObject(classFile.getKey(), classFile.getValue()); return sourceFile; }).collect(Collectors.toList()); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(null, null, null); updateClassPath(additionalJar, standardFileManager); standardFileManager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(Files.createTempDirectory("compile-test").toFile())); JavaCompiler.CompilationTask run = compiler.getTask(null, standardFileManager, null, null, null, sourceFiles); Boolean call = run.call(); if (!call) { throw new IllegalArgumentException("Unable to compile the file, see logs"); } Map resources = new HashMap<>(); for (Map.Entry classFile : classFiles) { String className = classFile.getKey(); JavaFileObject javaFileForInput = standardFileManager.getJavaFileForInput(StandardLocation.CLASS_OUTPUT, className, JavaFileObject.Kind.CLASS); byte[] bytes = Files.readAllBytes(Paths.get(javaFileForInput.toUri())); resources.put(className.replace(".", "/") + ".class", bytes); } return generateJar(resources); } private static void updateClassPath(List additionalJar, StandardJavaFileManager standardFileManager) throws IOException { Iterable location = standardFileManager.getLocation(StandardLocation.CLASS_PATH); ArrayList classPath = new ArrayList<>(); location.forEach(classPath::add); additionalJar.stream().map(Path::toFile).forEach(classPath::add); standardFileManager.setLocation(StandardLocation.CLASS_PATH, classPath); } public static Map getResources(final Class... classes) throws IOException { if (classes == null || classes.length == 0) { final String message = "No classes available"; throw new IOException(message); } final Map resources = new HashMap<>(); for (final Class clazz : classes) { resources.put(clazz.getName().replace(".", "/") + CLASS_EXT, getClassData(clazz)); for (final Class internalClass : clazz.getDeclaredClasses()) { resources.put(internalClass.getName().replace(".", "/") + CLASS_EXT, getClassData(internalClass)); } } return resources; } public static byte[] getClassData(final Class clazz) throws IOException { if (clazz == null) { final String message = "Class is null"; throw new IOException(message); } final String resource = clazz.getName().replace('.', '/') + CLASS_EXT; byte[] data; try (InputStream inputStream = clazz.getClassLoader().getResourceAsStream(resource)) { if (inputStream == null) { throw new IOException( "Impossible to get stream from class: " + clazz.getName() + ", className= " + resource); } data = IOUtil.getAllContentFrom(inputStream); } return data; } public static byte[] generateJar(final Map resources) throws IOException { if (resources == null || resources.isEmpty()) { final String message = "No resources available"; throw new IOException(message); } ByteArrayOutputStream baos = null; JarOutputStream jarOutStream = null; try { baos = new ByteArrayOutputStream(); jarOutStream = new JarOutputStream(new BufferedOutputStream(baos)); for (final Map.Entry resource : resources.entrySet()) { jarOutStream.putNextEntry(new JarEntry(resource.getKey())); jarOutStream.write(resource.getValue()); } jarOutStream.flush(); baos.flush(); } finally { if (jarOutStream != null) { jarOutStream.close(); } if (baos != null) { baos.close(); } } return baos.toByteArray(); } /** * Return the whole underlying stream content into a single String. * Warning: the whole content of stream will be kept in memory!! Use with * care! * * @param in the stream to read * @return the whole content of the stream in a single String. * @throws IOException if an I/O exception occurs */ public static byte[] getAllContentFrom(final InputStream in) throws IOException { if (in == null) { throw new IOException("The InputStream is null!"); } final byte[] buffer = new byte[BUFFER_SIZE]; final byte[] resultArray; try (BufferedInputStream bis = new BufferedInputStream(in); ByteArrayOutputStream result = new ByteArrayOutputStream()) { int amountRead; while ((amountRead = bis.read(buffer)) > 0) { result.write(buffer, 0, amountRead); } resultArray = result.toByteArray(); result.flush(); } return resultArray; } /** * Equivalent to {@link #getAllContentFrom(InputStream) getAllContentFrom(new * FileInputStream(file))}; * * @param file the file to read * @return the whole content of the file in a single String. * @throws IOException If an I/O exception occurs */ public static byte[] getAllContentFrom(final File file) throws IOException { try (InputStream in = new FileInputStream(file)) { return getAllContentFrom(in); } } /** * Return the whole underlying stream content into a single String. * Warning: the whole content of stream will be kept in memory!! Use with * care! * * @param url the URL to read * @return the whole content of the stream in a single String. * @throws IOException if an I/O exception occurs */ public static byte[] getAllContentFrom(final URL url) throws IOException { try (InputStream in = url.openStream()) { return getAllContentFrom(in); } } public static File createTempDirectory(final URI directoryPath) { final File tmpDir = new File(directoryPath); tmpDir.setReadable(true); tmpDir.setWritable(true); mkdirs(tmpDir); Files.isSymbolicLink(tmpDir.toPath()); try { Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { final boolean deleted = deleteDir(tmpDir); if (!deleted) { var errorMsg = "Unable to delete directory: " + tmpDir; log.error(errorMsg); if (!tmpDir.exists()) { throw new FileNotFoundException("Directory does not exist: " + tmpDir); } throw new IOException(errorMsg); } } catch (final IOException e) { throw new RuntimeException(e); } })); } catch (IllegalStateException ignored) { // happen in case of hook already registered and when shutting down } return tmpDir; } public static boolean deleteDir(final File dir) throws IOException { return deleteDir(dir, 1, 0); } public static boolean deleteDir(final File dir, final int attempts, final long sleepTime) throws IOException { if (dir != null) { boolean result = true; if (!dir.exists()) { return true; //already deleted } if (!dir.isDirectory()) { throw new IOException("Unable to delete directory: " + dir + ", it is not a directory"); } for (final File file : dir.listFiles()) { if (file.isDirectory()) { result &= deleteDir(file, attempts, sleepTime); } else { result &= deleteFile(file, attempts, sleepTime); } } return result && deleteFile(dir, attempts, sleepTime); } return false; } public static boolean deleteFile(final File f, final int attempts, final long sleepTime) { int retries = attempts; while (retries > 0) { if (f.delete()) { break; } retries--; try { Thread.sleep(sleepTime); } catch (final InterruptedException ignored) { } } return retries > 0; } public static byte[] zip(final Map files) throws IOException { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { for (final Entry file : files.entrySet()) { zos.putNextEntry(new ZipEntry(file.getKey())); zos.write(file.getValue()); zos.closeEntry(); } return baos.toByteArray(); } } public static void unzipToFolder(final InputStream inputStream, final File outputFolder) throws IOException { try (ZipInputStream zipInputstream = new ZipInputStream(inputStream)) { extractZipEntries(zipInputstream, outputFolder); } } private static void mkdirs(final File file) { if (!file.exists()) { file.mkdirs(); } } private static void extractZipEntries(final ZipInputStream zipInputstream, final File outputFolder) throws IOException { final String canonicalDestDir = outputFolder.getCanonicalPath() + File.separator; ZipEntry zipEntry; while ((zipEntry = zipInputstream.getNextEntry()) != null) { try { // For each entry, a file is created in the output directory "folder" final File outputFile = new File(outputFolder.getAbsolutePath(), zipEntry.getName()); // Prevent Zip Slip: ensure the resolved path stays within the output folder if (!outputFile.getCanonicalPath().startsWith(canonicalDestDir)) { throw new IOException("Zip entry is outside of the target directory"); } // If the entry is a directory, it creates in the output folder, and we go to the next entry (continue). if (zipEntry.isDirectory()) { mkdirs(outputFile); continue; } writeZipInputToFile(zipInputstream, outputFile); } finally { zipInputstream.closeEntry(); } } } private static void writeZipInputToFile(final ZipInputStream zipInputstream, final File outputFile) throws FileNotFoundException, IOException { // The input is a file. An FileOutputStream is created to write the content of the new file. mkdirs(outputFile.getParentFile()); try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile)) { // The contents of the new file, that is read from the ZipInputStream using a buffer (byte []), is written. int bytesRead; final byte[] buffer = new byte[BUFFER_SIZE]; while ((bytesRead = zipInputstream.read(buffer)) > -1) { fileOutputStream.write(buffer, 0, bytesRead); } fileOutputStream.flush(); } catch (final IOException ioe) { // In case of error, the file is deleted outputFile.delete(); throw ioe; } } public static void writeContentToFile(final String content, final File outputFile) throws IOException { final FileOutputStream fileOutput = new FileOutputStream(outputFile); writeContentToFileOutputStream(content, fileOutput); } public static void writeContentToFileOutputStream(final String content, final FileOutputStream fileOutput) throws IOException { try (OutputStreamWriter out = new OutputStreamWriter(fileOutput, StandardCharsets.UTF_8)) { out.write(content); out.flush(); } finally { fileOutput.close(); } } public static void write(final File file, final byte[] fileContent) throws IOException { try (FileOutputStream fos = new FileOutputStream(file); BufferedOutputStream bos = new BufferedOutputStream(fos)) { bos.write(fileContent); bos.flush(); } } public static byte[] getContent(final File file) throws IOException { try (FileInputStream fin = new FileInputStream(file); FileChannel ch = fin.getChannel()) { final int size = (int) ch.size(); final MappedByteBuffer buf = ch.map(MapMode.READ_ONLY, 0, size); final byte[] bytes = new byte[size]; buf.get(bytes); return bytes; } } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/io/IOUtils.java ================================================ /** * 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.io; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; import java.util.HashMap; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import javax.xml.XMLConstants; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; /** * @author Matthieu Chaffotte */ public class IOUtils { public static byte[] zip(final String fileName, final byte[] fileContent) throws IOException { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ZipOutputStream zos = new ZipOutputStream(baos); try { zos.putNextEntry(new ZipEntry(fileName)); zos.write(fileContent); } finally { zos.closeEntry(); zos.close(); } return baos.toByteArray(); } public static Map unzip(final byte[] zippedContent) throws IOException { final ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zippedContent)); final Map resources = new HashMap<>(); try { ZipEntry entry = zis.getNextEntry(); while (entry != null) { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { int len; final byte[] buffer = new byte[1024]; while ((len = zis.read(buffer)) > 0) { baos.write(buffer, 0, len); } } finally { baos.close(); } resources.put(entry.getName(), baos.toByteArray()); entry = zis.getNextEntry(); } } finally { zis.closeEntry(); zis.close(); } return resources; } public static File createTempDirectory(final String prefix) throws IOException { final File tmpDirectory = File.createTempFile(prefix, null); return createDirectory(tmpDirectory); } public static File createSubDirectory(final File directory, final String child) { final File subDir = new File(directory, child); return createDirectory(subDir); } public static File createDirectoryIfNotExists(File dir) { if (!dir.exists()) { return createDirectory(dir); } return dir; } private static File createDirectory(final File dir) { dir.delete(); dir.mkdir(); return dir; } public static void saveDocument(final Document document, final File destination) throws IOException, TransformerException { if (document == null) { throw new IllegalArgumentException("Document should not be null."); } final TransformerFactory transformerFactory = TransformerFactory.newInstance(); try { transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // security-compliant transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); // security-compliant } catch (IllegalArgumentException e) { //ignored, if not supported by the implementation } final Transformer tf = transformerFactory.newTransformer(); tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); tf.setOutputProperty(OutputKeys.INDENT, "yes"); try (FileOutputStream fos = new FileOutputStream(destination)) { final StreamResult outputTarget = new StreamResult(fos); tf.transform(new DOMSource(document), outputTarget); } } public static File createTempFile(String name, String suffix, byte[] content) throws IOException { return Files.write(Files.createTempFile(name, suffix), content).toFile(); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/io/PropertiesManager.java ================================================ /** * 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.io; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; import java.util.Properties; /** * @author Matthieu Chaffotte * @author Frederic Bouquet * @author Celine Souchet */ public class PropertiesManager { public static void saveProperties(final Properties properties, final File file) throws IOException { try (FileOutputStream outputStream = new FileOutputStream(file)) { properties.store(outputStream, "Storing modified properties"); } } public static Properties getProperties(final URL url) throws IOException { try (InputStreamReader reader = new InputStreamReader(url.openStream())) { return getProperties(reader); } } public static Properties getProperties(final String fileName) throws IOException { return getProperties(new File(fileName)); } public static Properties getProperties(final File file) throws IOException { try (FileReader reader = new FileReader(file)) { return getProperties(reader); } } private static Properties getProperties(final Reader reader) throws IOException { final Properties properties = new Properties(); properties.load(reader); return properties; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/job/FailedJob.java ================================================ /** * 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.job; import java.io.Serializable; import java.util.Date; /** * Represent failure(s) that happened to a certain job (e.g. Timer execution) */ public interface FailedJob extends Serializable { long getJobDescriptorId(); String getJobName(); String getDescription(); /** * @return the exception thrown by the last failing execution */ String getLastMessage(); /** * @return the number of times a job failed before replaying it manually */ int getNumberOfFailures(); /** * @return the Date of the last failure */ Date getLastUpdateDate(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/job/impl/FailedJobImpl.java ================================================ /** * 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.job.impl; import java.util.Date; import org.bonitasoft.engine.job.FailedJob; /** * @author Matthieu Chaffotte */ public class FailedJobImpl implements FailedJob { private static final long serialVersionUID = 8739476111495272543L; private final long jobDescriptorId; private final String jobName; private String description; private Date lastUpdateDate; private String lastMessage; private int numberOfFailures; public FailedJobImpl(final long jobDescriptorId, final String jobName) { this.jobDescriptorId = jobDescriptorId; this.jobName = jobName; } public void setLastUpdateDate(final Date lastUpdateDate) { this.lastUpdateDate = lastUpdateDate; } public void setDescription(final String description) { this.description = description; } public void setLastMessage(final String lastMessage) { this.lastMessage = lastMessage; } public void setNumberOfFailures(int numberOfFailures) { this.numberOfFailures = numberOfFailures; } @Override public long getJobDescriptorId() { return jobDescriptorId; } @Override public String getJobName() { return jobName; } @Override public String getDescription() { return description; } @Override public String getLastMessage() { return lastMessage; } @Override public int getNumberOfFailures() { return numberOfFailures; } @Override public Date getLastUpdateDate() { return lastUpdateDate; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/job/package-info.java ================================================ /** * Copyright (C) 2015 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.job; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/maintenance/MaintenanceDetails.java ================================================ /** * Copyright (C) 2023 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.maintenance; import org.bonitasoft.engine.bpm.BonitaObject; /** * This object holds maintenance details such as maintenance state and message. */ public interface MaintenanceDetails extends BonitaObject { public State getMaintenanceState(); public String getMaintenanceMessage(); public Boolean isMaintenanceMessageActive(); /** * Activation state of platform maintenance */ public enum State { ENABLED, DISABLED } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/maintenance/MaintenanceDetailsNotFoundException.java ================================================ /** * Copyright (C) 2023 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.maintenance; import org.bonitasoft.engine.exception.NotFoundException; public class MaintenanceDetailsNotFoundException extends NotFoundException { public MaintenanceDetailsNotFoundException(String message) { super(message); } public MaintenanceDetailsNotFoundException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/maintenance/impl/MaintenanceDetailsImpl.java ================================================ /** * Copyright (C) 2023 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.maintenance.impl; import lombok.Builder; import org.bonitasoft.engine.maintenance.MaintenanceDetails; @Builder public class MaintenanceDetailsImpl implements MaintenanceDetails { private State maintenanceState; private String maintenanceMessage; private boolean maintenanceMessageActive; public State getMaintenanceState() { return maintenanceState; } @Override public String getMaintenanceMessage() { return maintenanceMessage; } public Boolean isMaintenanceMessageActive() { return maintenanceMessageActive; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/AuthorizationRuleConstants.java ================================================ /** * 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.page; /** * author Emmanuel Duchastenier */ public class AuthorizationRuleConstants { public static final String IS_ADMIN = "IS_ADMIN"; public static final String IS_PROCESS_OWNER = "IS_PROCESS_OWNER"; public static final String IS_PROCESS_INITIATOR = "IS_PROCESS_INITIATOR"; public static final String IS_INVOLVED_IN_PROCESS_INSTANCE = "IS_INVOLVED_IN_PROCESS_INSTANCE"; public static final String IS_MANAGER_OF_USER_INVOLVED_IN_PROCESS_INSTANCE = "IS_MANAGER_OF_USER_INVOLVED_IN_PROCESS_INSTANCE"; public static final String IS_TASK_AVAILABLE_FOR_USER = "IS_TASK_AVAILABLE_FOR_USER"; public static final String IS_ACTOR_INITIATOR = "IS_ACTOR_INITIATOR"; public static final String IS_TASK_PERFORMER = "IS_TASK_PERFORMER"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/ContentType.java ================================================ /** * 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.page; /** * @author Laurent Leseigneur */ public interface ContentType { String PAGE = "page"; String FORM = "form"; String API_EXTENSION = "apiExtension"; String LAYOUT = "layout"; String THEME = "theme"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/Page.java ================================================ /** * 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.page; import java.util.Date; import org.bonitasoft.engine.bpm.BaseElement; /** * A Page is a way to store, amongst other things, a binary content. * It is used by Bonita Portal to display specific custom content * * @author Laurent Leseigneur */ public interface Page extends BaseElement { /** * Get the name of this Page. * * @return the logical name of this Page. */ String getName(); /** * Get the display name of this Page. * * @return the display name of this Page. */ String getDisplayName(); /** * Is this page provided by default. * * @return true if this page is provided by default, false otherwise. */ boolean isProvided(); /** * Get the description of this Page. * * @return the description of this Page. */ String getDescription(); /** * Get the date when this page was initially installed into the Engine. * * @return the date when this page was initially installed into the Engine. */ Date getInstallationDate(); /** * Get the ID of the user that installed the page. * * @return the ID of the user that installed the page, or -1 if it is a default page. */ long getInstalledBy(); /** * Get the date when this page was last modified. * * @return the date when this page was last modified. */ Date getLastModificationDate(); /** * Get the userId of the user that last updated this page. * * @return the user id of the user that last updated this page. */ long getLastUpdatedBy(); /** * Get the name of the zip file. * * @return the name of the zip file of this Page. */ String getContentName(); /** * Get the type of this Page. * * @return the type of this Page. see {@link ContentType} for available values * @since 7.0 */ String getContentType(); /** * Get the process definition ID of this Page. * * @return the process definition ID of this Page, or null if not set. * @since 7.0 */ Long getProcessDefinitionId(); /** * Whether this page is editable * * @return Whether this page is editable */ boolean isEditable(); /** * Whether this page is removable * * @return Whether this page is removable */ boolean isRemovable(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/PageCreator.java ================================================ /** * 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.page; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * @author Laurent Leseigneur */ public class PageCreator implements Serializable { public enum PageField { NAME, DISPLAY_NAME, DESCRIPTION, CONTENT_NAME, CONTENT_TYPE, PROCESS_DEFINITION_ID } private static final long serialVersionUID = 8174091386958635983L; private final Map fields; public PageCreator(final String name, final String zipName) { fields = new HashMap<>(); fields.put(PageField.NAME, name); fields.put(PageField.CONTENT_NAME, zipName); setContentType(ContentType.PAGE); } public PageCreator(String name, String zipName, String contentType, Long processDefinitionId) { this(name, zipName); setContentType(contentType); setProcessDefinitionId(processDefinitionId); } public String getName() { return fields.get(PageField.NAME).toString(); } public PageCreator setDescription(final String description) { fields.put(PageField.DESCRIPTION, description); return this; } public PageCreator setDisplayName(final String displayName) { fields.put(PageField.DISPLAY_NAME, displayName); return this; } public PageCreator setContentType(final String contentType) { fields.put(PageField.CONTENT_TYPE, contentType); return this; } public PageCreator setProcessDefinitionId(final Long processDefinitionId) { fields.put(PageField.PROCESS_DEFINITION_ID, processDefinitionId); return this; } public Map getFields() { return fields; } @Override public String toString() { return "PageCreator [fields=" + fields + "]"; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/PageNotFoundException.java ================================================ /** * Copyright (C) 2015 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.page; import org.bonitasoft.engine.exception.NotFoundException; /** * @author Laurent Leseigneur */ public class PageNotFoundException extends NotFoundException { private static final long serialVersionUID = 2842457668242337487L; public enum PageAttribute { NAME, MAPPING_KEY } /** * @deprecated Use {@link #PageNotFoundException(String, PageAttribute)} constructor instead. */ @Deprecated public PageNotFoundException(final String name) { this(name, PageAttribute.NAME); } public PageNotFoundException(final String value, PageAttribute pageAttribute) { super(String.format("Unable to find page with %s: %s", pageAttribute.name().toLowerCase(), value)); } public PageNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/PageSearchDescriptor.java ================================================ /** * 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.page; /** * @author Laurent Leseigneur */ public class PageSearchDescriptor { public static final String ID = "id"; public static final String NAME = "name"; public static final String DISPLAY_NAME = "displayName"; public static final String INSTALLED_BY = "installedBy"; public static final String PROVIDED = "provided"; public static final String INSTALLATION_DATE = "installationDate"; public static final String LAST_MODIFICATION_DATE = "lastModificationDate"; public static final String CONTENT_TYPE = "contentType"; public static final String PROCESS_DEFINITION_ID = "processDefinitionId"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/PageURL.java ================================================ /** * 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.page; import java.io.Serializable; import java.util.Objects; /** * author Emmanuel Duchastenier */ public class PageURL implements Serializable { private String url; private Long pageId; public String getUrl() { return url; } public Long getPageId() { return pageId; } public PageURL(String url, Long pageId) { this.url = url; this.pageId = pageId; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PageURL pageURL = (PageURL) o; return Objects.equals(url, pageURL.url) && Objects.equals(pageId, pageURL.pageId); } @Override public int hashCode() { return Objects.hash(url, pageId); } @Override public String toString() { return "PageURL{" + "url='" + url + '\'' + ", pageId=" + pageId + '}'; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/PageUpdater.java ================================================ /** * 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.page; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * @author Laurent Leseigneur */ public class PageUpdater implements Serializable { private static final long serialVersionUID = 4295108162470507415L; public enum PageUpdateField { /** * @deprecated since bonita 7.13 the update of name is no more supported, it will not be taken into account on * the page update **/ @Deprecated NAME, DISPLAY_NAME, DESCRIPTION, CONTENT_NAME, CONTENT_TYPE, PROCESS_DEFINITION_ID } private final Map fields; public PageUpdater() { fields = new HashMap<>(); } public PageUpdater setDescription(final String description) { fields.put(PageUpdateField.DESCRIPTION, description); return this; } /** * @deprecated since bonita 7.13 the update of name is no more supported, it will not be taken into account on the * page update */ @Deprecated public PageUpdater setName(final String name) { fields.put(PageUpdateField.NAME, name); return this; } public PageUpdater setDisplayName(final String displayName) { fields.put(PageUpdateField.DISPLAY_NAME, displayName); return this; } public PageUpdater setContentName(final String contentName) { fields.put(PageUpdateField.CONTENT_NAME, contentName); return this; } public PageUpdater setContentType(final String contentType) { fields.put(PageUpdateField.CONTENT_TYPE, contentType); return this; } public PageUpdater setProcessDefinitionId(final Long processDefinitionId) { fields.put(PageUpdateField.PROCESS_DEFINITION_ID, processDefinitionId); return this; } public Map getFields() { return fields; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/URLAdapterConstants.java ================================================ /** * 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.page; /** * @author Baptiste Mesta */ public interface URLAdapterConstants { String LEGACY_URL_ADAPTER = "legacy"; String EXTERNAL_URL_ADAPTER = "external"; String CONTEXT_PATH = "contextPath"; // String IS_ADMIN = "isAmin"; String LOCALE = "locale"; String QUERY_PARAMETERS = "queryParameters"; String ID_QUERY_PARAM = "id"; String ASSIGN_TASK_QUERY_PARAM = "assignTask"; String USER_QUERY_PARAM = "user"; String TENANT_QUERY_PARAM = "tenant"; String MODE_QUERY_PARAM = "mode"; String AUTO_INSTANTIATE_QUERY_PARAM = "autoInstantiate"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/page/impl/PageImpl.java ================================================ /** * 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.page.impl; import java.util.Date; import lombok.ToString; import org.bonitasoft.engine.page.Page; /** * @author Laurent Leseigneur */ @ToString public class PageImpl implements Page { private static final long serialVersionUID = 5785414687043871169L; private final long pageId; private final String name; private final boolean provided; private final String description; private final Date installationDate; private final long installedBy; private final Date lastModificationDate; private final String displayName; private final long lastUpdatedBy; private final String contentName; private final String contentType; private final Long processDefinitionId; private final boolean editable; private final boolean removable; /** * Builds a Page that is editable and removable by default */ public PageImpl(final long pageId, final String name, final String displayName, final boolean provided, final String description, final long installationDate, final long installedBy, final long lastModificationDate, final long lastUpdatedBy, final String zipName, String contentType, Long processDefinitionId) { this(pageId, name, displayName, provided, true, true, description, installationDate, installedBy, lastModificationDate, lastUpdatedBy, zipName, contentType, processDefinitionId); } public PageImpl(final long pageId, final String name, final String displayName, final boolean provided, boolean editable, boolean removable, final String description, final long installationDate, final long installedBy, final long lastModificationDate, final long lastUpdatedBy, final String zipName, String contentType, Long processDefinitionId) { this.pageId = pageId; this.name = name; this.displayName = displayName; this.provided = provided; this.description = description; this.lastUpdatedBy = lastUpdatedBy; this.contentName = zipName; this.contentType = contentType; this.processDefinitionId = processDefinitionId; this.installationDate = new Date(installationDate); this.installedBy = installedBy; this.lastModificationDate = new Date(lastModificationDate); this.editable = editable; this.removable = removable; } @Override public long getId() { return pageId; } @Override public String getName() { return name; } @Override public boolean isProvided() { return provided; } @Override public String getDescription() { return description; } @Override public Date getInstallationDate() { return installationDate; } @Override public long getInstalledBy() { return installedBy; } @Override public Date getLastModificationDate() { return lastModificationDate; } @Override public String getDisplayName() { return displayName; } public long getPageId() { return pageId; } @Override public long getLastUpdatedBy() { return lastUpdatedBy; } @Override public String getContentName() { return contentName; } @Override public String getContentType() { return contentType; } @Override public Long getProcessDefinitionId() { return processDefinitionId; } @Override public boolean isEditable() { return editable; } @Override public boolean isRemovable() { return removable; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/IllegalNodeStateException.java ================================================ /** * 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.platform; import org.bonitasoft.engine.exception.BonitaRuntimeException; /** * Indicates that an operation incompatible with the current node state was called. * * @author Emmanuel Duchastenier */ public class IllegalNodeStateException extends BonitaRuntimeException { private static final long serialVersionUID = 7043887433404383538L; /** * @param message a String indicating the exception message */ public IllegalNodeStateException(final String message) { super(message); } /** * @param message a String indicating the exception message * @param cause a Throwable indicating the root cause */ public IllegalNodeStateException(final String message, final Throwable cause) { super(message, cause); } /** * @param cause a Throwable indicating the root cause */ public IllegalNodeStateException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/InvalidPlatformCredentialsException.java ================================================ /** * 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.platform; /** * @author Baptiste Mesta */ public class InvalidPlatformCredentialsException extends PlatformLoginException { public InvalidPlatformCredentialsException(String message) { super(message); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/LoginException.java ================================================ /** * 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.platform; import org.bonitasoft.engine.exception.BonitaException; /** * Indicates that a problem occurred during tenant login action * * @author Matthieu Chaffotte */ public class LoginException extends BonitaException { private static final long serialVersionUID = 2644120305805282693L; /** * @param message a String indicating the exception message */ public LoginException(final String message) { super(message); } /** * @param cause a Throwable indicating the root cause */ public LoginException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/LogoutException.java ================================================ /** * 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.platform; import org.bonitasoft.engine.exception.BonitaException; /** * Indicates that a problem occurred during tenant logout action * * @author Matthieu Chaffotte */ public class LogoutException extends BonitaException { private static final long serialVersionUID = -5327606222750615923L; /** * @param message a String indicating the exception message */ public LogoutException(final String message) { super(message); } /** * @param cause a Throwable indicating the root cause */ public LogoutException(final Throwable cause) { super(cause); } /** * @param message a String indicating the exception message * @param cause a Throwable indicating the root cause */ public LogoutException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/NodeNotStartedException.java ================================================ /** * 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.platform; /** * Indicates that an operation needing a started node was called in a stopped node. * * @author Emmanuel Duchastenier */ public class NodeNotStartedException extends IllegalNodeStateException { private static final long serialVersionUID = -1L; public NodeNotStartedException() { super("The current node has not been started yet. Method PlatformAPI.startNode() must be called previously."); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/Platform.java ================================================ /** * 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.platform; import java.io.Serializable; /** * Contains information about the Bonita Platform. *

* The platform is the base on which runs the Bonita Engine. There is only one platform for a * running Bonita * Engine. *

* In order to perform actions on the platform, please, refer to the {@link org.bonitasoft.engine.api.PlatformAPI} * * @author Matthieu Chaffotte * @see org.bonitasoft.engine.api.PlatformAPI * @since 6.0.0 */ public interface Platform extends Serializable { /** * Retrieves the platform version * * @return a String representing the platform version * @see #getInitialVersion() */ String getVersion(); /** * Retrieves the platform initial version. That is, the Bonita version in which you have initially * created the platform before migrating * to the current version. *

* For instance, if you have created your platform in the version 6.1.0 and have migrated to the version 6.3.4 using * the Bonita Migration * Tool, {@code getInitialVersion} will return 6.1.0 and {@link #getVersion()} will return 6.3.4. * * @return a String representing the platform initial version * @see #getVersion() */ String getInitialVersion(); /** * Retrieves the timestamp at which the platform was created * * @return a long representing the timestamp at which the platform was created */ long getCreated(); /** * Retrieves the name of the platform technical user that created the platform * * @return a String representing the name of the platform technical user that created the platform */ String getCreatedBy(); /** * Return whether this platform is in maintenance or not. * When the platform is in maintenance, the services are paused, and most features are disabled. * Only the Technical Admin can log in during the maintenance time. */ boolean isMaintenanceEnabled(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/PlatformLoginException.java ================================================ /** * 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.platform; import org.bonitasoft.engine.exception.BonitaException; /** * Indicates that a problem occurred during platform login action * * @author Matthieu Chaffotte */ public class PlatformLoginException extends BonitaException { private static final long serialVersionUID = 3075478879044920526L; /** * @param message a String indicating the exception message */ public PlatformLoginException(final String message) { super(message); } /** * @param cause a Throwable indicating the root cause */ public PlatformLoginException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/PlatformLogoutException.java ================================================ /** * 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.platform; import org.bonitasoft.engine.exception.BonitaException; /** * Indicates that a problem occurred during tenant logout action * * @author Matthieu Chaffotte */ public class PlatformLogoutException extends BonitaException { private static final long serialVersionUID = 8429124800360665988L; /** * @param message a String indicating the exception message */ public PlatformLogoutException(final String message) { super(message); } /** * @param cause a Throwable indicating the root cause */ public PlatformLogoutException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/PlatformNotFoundException.java ================================================ /** * 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.platform; import org.bonitasoft.engine.exception.NotFoundException; /** * Indicates that the platform does not exist * * @author Lu Kai * @author Emmanuel Duchastenier */ public class PlatformNotFoundException extends NotFoundException { private static final long serialVersionUID = -7397972100656765511L; /** * @param message a String indicating the exception message */ public PlatformNotFoundException(final String message) { super(message); } /** * @param message a String indicating the exception message * @param cause a Throwable indicating the root cause */ public PlatformNotFoundException(final String message, final Throwable cause) { super(message, cause); } /** * @param cause a Throwable indicating the root cause */ public PlatformNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/PlatformState.java ================================================ /** * 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.platform; /** * Describes the possible states of the Platform. */ public enum PlatformState { /** * The Platform is started: * - Services are running * - Tenants that are Activated are STARTED or are STARTING * - Calls can be made on platform APIs */ STARTED, /** * The Platform is transitioning from state STOPPED to state STARTED * - Tenants are still STOPPED ( will be started once the Platform is STARTED) * - No calls can be made on APIs */ STARTING, /** * The Platform is stopped: * - Services are stopped * - All Tenants are also STOPPED * - No call to APIs can be made (except to login and start platform) */ STOPPED, /** * The Platform is transitioning from state STARTED to state STOPPED * - Tenants are STOPPED or STOPPING * - No calls can be made on APIs */ STOPPING, } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/StartNodeException.java ================================================ /** * 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.platform; import org.bonitasoft.engine.exception.ExecutionException; /** * Indicates that a problem occurred when starting the node * * @author Matthieu Chaffotte */ public class StartNodeException extends ExecutionException { private static final long serialVersionUID = 2714223184438785735L; /** * @param message a String indicating the exception message */ public StartNodeException(final String message) { super(message); } /** * @param cause a Throwable indicating the root cause */ public StartNodeException(final Throwable cause) { super(cause); } /** * @param message a String indicating the exception messages * @param cause a Throwable indicating the root cause */ public StartNodeException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/StopNodeException.java ================================================ /** * 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.platform; import org.bonitasoft.engine.exception.ExecutionException; /** * Indicates that a problem occurred when stopping the node * * @author Matthieu Chaffotte */ public class StopNodeException extends ExecutionException { private static final long serialVersionUID = -8344736311926111229L; /** * @param message a String indicating the exception message */ public StopNodeException(final String message) { super(message); } /** * @param cause a Throwable indicating the root cause */ public StopNodeException(final Throwable cause) { super(cause); } /** * @param message a String indicating the exception message * @param cause a Throwable indicating the root cause */ public StopNodeException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/UnknownUserException.java ================================================ /** * 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.platform; /** * Indicates that the user trying to login is unknown to bonita * * @author Anthony Birembaut */ public class UnknownUserException extends LoginException { private static final long serialVersionUID = 9074655520113937276L; /** * @param message a String indicating the exception message */ public UnknownUserException(final String message) { super(message); } /** * @param cause a Throwable indicating the root cause */ public UnknownUserException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/command/package-info.java ================================================ /** * Copyright (C) 2015 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. **/ /** *

* Contains classes related to Platform commands. *

* * @since 6.0.0 */ package org.bonitasoft.engine.platform.command; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/impl/PlatformImpl.java ================================================ /** * 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.platform.impl; import java.io.Serial; import lombok.Getter; import lombok.ToString; import org.bonitasoft.engine.platform.Platform; /** * @author Elias Ricken de Medeiros */ @ToString @Getter public class PlatformImpl implements Platform { @Serial private static final long serialVersionUID = -8493649294374229877L; private final long created; private final String createdBy; private final String initialVersion; private final String version; private final boolean maintenanceEnabled; public PlatformImpl(final String version, final String initialVersion, final String createdBy, final long created, final boolean maintenanceEnabled) { this.version = version; this.initialVersion = initialVersion; this.createdBy = createdBy; this.created = created; this.maintenanceEnabled = maintenanceEnabled; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/platform/package-info.java ================================================ /** * Copyright (C) 2015 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. **/ /** *

* Contains classes and interfaces related to the Bonita Platform. *

* * @since 6.0.0 */ package org.bonitasoft.engine.platform; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/profile/ProfileCriterion.java ================================================ /** * 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.profile; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.Sort; /** * @author Celine Souchet */ public enum ProfileCriterion { /** * Profile identifier ascending order */ ID_ASC(ProfileSearchDescriptor.ID, Order.ASC), /** * Profile identifier descending order */ ID_DESC(ProfileSearchDescriptor.ID, Order.DESC), /** * Profile is default ascending order */ IS_DEFAULT_ASC(ProfileSearchDescriptor.IS_DEFAULT, Order.ASC), /** * Profile is default descending order */ IS_DEFAULT_DESC(ProfileSearchDescriptor.IS_DEFAULT, Order.DESC), /** * Profile name ascending order */ NAME_ASC(ProfileSearchDescriptor.NAME, Order.ASC), /** * Profile name descending order */ NAME_DESC(ProfileSearchDescriptor.NAME, Order.DESC); private final String field; private final Order order; ProfileCriterion(final String field, final Order order) { this.field = field; this.order = order; } public Order getOrder() { return order; } public String getField() { return field; } public Sort getSort() { return new Sort(order, field); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/profile/ProfileMemberCreator.java ================================================ /** * 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.profile; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * @author Celine Souchet */ public class ProfileMemberCreator implements Serializable { private static final long serialVersionUID = -1414989152963184543L; public enum ProfileMemberField { PROFILE_ID, USER_ID, GROUP_ID, ROLE_ID; } private final Map fields; public ProfileMemberCreator(final long profileId) { fields = new HashMap(5); fields.put(ProfileMemberField.PROFILE_ID, profileId); } public ProfileMemberCreator setUserId(final long userId) { fields.put(ProfileMemberField.USER_ID, userId); return this; } public ProfileMemberCreator setGroupId(final long groupId) { fields.put(ProfileMemberField.GROUP_ID, groupId); return this; } public ProfileMemberCreator setRoleId(final long roleId) { fields.put(ProfileMemberField.ROLE_ID, roleId); return this; } public Map getFields() { return fields; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/profile/ProfileMemberNotFoundException.java ================================================ /** * Copyright (C) 2021 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.profile; import org.bonitasoft.engine.exception.NotFoundException; /** * @author Anthony Birembaut */ public class ProfileMemberNotFoundException extends NotFoundException { private static final long serialVersionUID = -7240871235816448511L; public ProfileMemberNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/profile/ProfileMemberSearchDescriptor.java ================================================ /** * 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.profile; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.search.SearchOptions; /** * Defines the fields that can be used in the {@link org.bonitasoft.engine.search.SearchOptions} when searching for * {@link ProfileMember}s * * @author Julien Mege * @author Celine Souchet * @see SearchOptions * @see org.bonitasoft.engine.api.ProfileAPI#searchProfileMembers(String, SearchOptions) */ public final class ProfileMemberSearchDescriptor { /** * Used to filter or order by the {@link ProfileMember} identifier * * @see ProfileMember */ public static final String ID = "id"; /** * Used to filter or order by the identifier of the related {@link Profile} * * @see Profile * @see ProfileMember */ public static final String PROFILE_ID = "profileId"; /** * Used to filter or order by the first part of the display name of {@link ProfileMember} * * @see ProfileMember#getDisplayNamePart1() */ public static final String DISPLAY_NAME_PART1 = "displayNamePart1"; /** * Used to filter or order by the second part of the display name of {@link ProfileMember} * * @see ProfileMember#getDisplayNamePart2() */ public static final String DISPLAY_NAME_PART2 = "displayNamePart2"; /** * Used to filter or order by the third part of the display name of {@link ProfileMember} * * @see ProfileMember#getDisplayNamePart3() */ public static final String DISPLAY_NAME_PART3 = "displayNamePart3"; /** * Used to filter or order by the identifier of the {@link User} related to the {@link ProfileMember} * * @see User * @see ProfileMember */ public static final String USER_ID = "userId"; /** * Used to filter or order by the identifier of the {@link org.bonitasoft.engine.identity.Group} related to the * {@link ProfileMember} * * @see org.bonitasoft.engine.identity.Group * @see ProfileMember */ public static final String GROUP_ID = "groupId"; /** * Used to filter or order by the identifier of the {@link org.bonitasoft.engine.identity.Role} related to the * {@link ProfileMember} * * @see org.bonitasoft.engine.identity.Role * @see ProfileMember */ public static final String ROLE_ID = "roleId"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/profile/ProfileNotFoundException.java ================================================ /** * 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.profile; import org.bonitasoft.engine.exception.NotFoundException; /** * @author Celine Souchet * @author Matthieu Chaffotte */ public class ProfileNotFoundException extends NotFoundException { private static final long serialVersionUID = -7220871135816448510L; public ProfileNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/profile/ProfileSearchDescriptor.java ================================================ /** * 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.profile; import org.bonitasoft.engine.search.SearchOptions; /** * Defines the fields that can be used in the {@link org.bonitasoft.engine.search.SearchOptions} when searching for * {@link Profile}s * * @author Julien Mege * @author Celine Souchet * @see SearchOptions * @see Profile * @see org.bonitasoft.engine.api.ProfileAPI#searchProfiles(SearchOptions) */ public final class ProfileSearchDescriptor { /** * Used to filter or order by the {@link Profile} identifier */ public static final String ID = "id"; /** * Used to filter or order by the {@link Profile} name */ public static final String NAME = "name"; /** * Used to filter or order by the flag {@link Profile#isDefault()} */ public static final String IS_DEFAULT = "isDefault"; } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/Order.java ================================================ /** * 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.search; /** * @author Matthieu Chaffotte */ public enum Order { DESC, ASC, ASC_NULLS_LAST, DESC_NULLS_FIRST, ASC_NULLS_FIRST, DESC_NULLS_LAST } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/SearchFilterOperation.java ================================================ /** * 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.search; /** * @author Emmanuel Duchastenier */ public enum SearchFilterOperation { EQUALS, GREATER_THAN, LESS_THAN, GREATER_OR_EQUAL, LESS_OR_EQUAL, DIFFERENT, BETWEEN, OR, AND, L_PARENTHESIS, R_PARENTHESIS } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/SearchOptions.java ================================================ /** * 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.search; import java.io.Serializable; import java.util.List; import org.bonitasoft.engine.search.impl.SearchFilter; /** * A SearchOptions object helps define the generic options of the search mechanism. * A SearchOptions has a 'start index' field and a 'max results' field that define where to start and where * to stop to return results that match * the provided search criteria. * It is composed of a list of SearchFilter objects defining the restrictive criteria that a result must * match to fulfill the search. * It is also composed of a 'search term', which is a free text that can be search for in a certain amount of fields, * depending on what object is the search * applied on. * Finally, a search can define a list of {@link Sort} options to define the order in which the matching results will be * returned. * Use {@link SearchOptionsBuilder} to build a SearchOptions object. * * @author Matthieu Chaffotte * @author Celine Souchet * @author Emmanuel Duchastenier * @see SearchOptionsBuilder * @see SearchResult */ public interface SearchOptions extends Serializable { /** * Gets the list of SearchFilter objects defining the restrictive criteria that a result must match to * fulfill the search. * * @return the list of SearchFilter objects */ List getFilters(); /** * Gets the search term (free text that can be search for in a certain amount of properties, depending on what * object is the search applied on) * * @return the search term (as a String) that will be searched for. */ String getSearchTerm(); /** * The result start index, that is the first result that matches the search criteria that we want to retrieve. * * @return the defined start index */ int getStartIndex(); /** * The maximum results to return. The actual number can be smaller, if the end of the list has been reached. * * @return The maximum number of results */ int getMaxResults(); /** * Gets the list of sort criteria * * @return the list of Sort to order the results */ List getSorts(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/SearchOptionsBuilder.java ================================================ /** * 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.search; import java.io.Serializable; import java.util.List; import org.bonitasoft.engine.search.impl.SearchFilter; import org.bonitasoft.engine.search.impl.SearchOptionsImpl; /** * Builder for {@link SearchOptions} objects. It can be used to define paged searches returning {@link SearchResult}s. * When several filters are added, implicit {@code AND} operators are used if not specified. * See {@link SearchOptions} for deeper details on search mechanism options. * * @author Matthieu Chaffotte * @author Emmanuel Duchastenier * @author Celine Souchet * @see SearchOptions */ public class SearchOptionsBuilder { private final SearchOptionsImpl options; /** * Builds a new {@link SearchOptions} with results limited to {@code startIndex} and {@code maxResults}. If you are * interested only in the number of * elements matching with the given criteria without knowing the elements details, it's possible to use 0 (zero) as * {@code maxResults}: * {@link SearchResult#getResult()} will send an empty list and {@link SearchResult#getCount()} will return the * number of matching elements. * * @param startIndex the first result to return * @param maxResults the maximum results to return. The actual number can be smaller, if the end of the list has * been reached. * If 0 (zero) or a negative number is provided, only the result count will be pertinent, and the result list * itself will be empty. * @see SearchOptions * @see SearchResult#getResult() * @see SearchResult#getCount() */ public SearchOptionsBuilder(final int startIndex, final int maxResults) { options = new SearchOptionsImpl(startIndex, maxResults); } /** * Creates a new SearchOptionsBuilder from another instance by * * @param searchOptions */ public SearchOptionsBuilder(final SearchOptions searchOptions) { options = new SearchOptionsImpl(searchOptions.getStartIndex(), searchOptions.getMaxResults()); options.setFilters(searchOptions.getFilters()); options.setSorts(searchOptions.getSorts()); options.setSearchTerm(searchOptions.getSearchTerm()); } /** * Filter the results to the specific value for the specific field (equality) * * @param field * The name of the field to filter on. Depending on the search parameter, specify the field by accessing the * relevant xxxSearchDescriptor classes. * For example, HumanTaskInstanceSearchDescriptor.NAME and * HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID. * @param value * the single value to filter on that field name * @return this builder itself * @since 6.0 */ public SearchOptionsBuilder filter(final String field, final Serializable value) { options.addFilter(field, value); return this; } /** * Filters search results with a greaterThan comparison operation. * * @param field * the field name to compare to. * @param value * the value to compare. * @return this builder itself * @see SearchOptionsBuilder#filter(String, java.io.Serializable) for field values */ public SearchOptionsBuilder greaterThan(final String field, final Serializable value) { options.addGreaterThanFilter(field, value); return this; } /** * @param field * @param value * @return this builder itself * @see SearchOptionsBuilder#filter(String, java.io.Serializable) for field values */ public SearchOptionsBuilder greaterOrEquals(final String field, final Serializable value) { options.addGreaterOrEqualsFilter(field, value); return this; } /** * @param field the field that should be less than * @param value * @return this builder itself * @see SearchOptionsBuilder#filter(String, java.io.Serializable) for field values */ public SearchOptionsBuilder lessThan(final String field, final Serializable value) { options.addLessThanFilter(field, value); return this; } /** * @param field the field that should be less or equals * @param value the value * @return this builder itself * @see SearchOptionsBuilder#filter(String, java.io.Serializable) for field values */ public SearchOptionsBuilder lessOrEquals(final String field, final Serializable value) { options.addLessOrEqualsFilter(field, value); return this; } /** * @param field the field that should be between * @param from from this value * @param to to this value * @return this builder itself * @see SearchOptionsBuilder#filter(String, java.io.Serializable) for field values */ public SearchOptionsBuilder between(final String field, final Serializable from, final Serializable to) { options.addBetweenFilter(field, from, to); return this; } /** * @param field * @param value * @return this builder itself * @see SearchOptionsBuilder#filter(String, java.io.Serializable) for field values */ public SearchOptionsBuilder differentFrom(final String field, final Serializable value) { options.addDifferentFromFilter(field, value); return this; } /** * @return this builder itself */ public SearchOptionsBuilder or() { if (options.getFilters().size() == 0) { throw new IllegalArgumentException("OR operator cannot be the first filter in the list."); } options.addOrFilter(); return this; } /** * @return this builder itself */ public SearchOptionsBuilder and() { if (options.getFilters().size() == 0) { throw new IllegalArgumentException("AND operator cannot be the first filter in the list."); } options.addAndFilter(); return this; } public SearchOptionsBuilder leftParenthesis() { options.addLeftParenthesis(); return this; } public SearchOptionsBuilder rightParenthesis() { options.addRightParenthesis(); return this; } /** * @param value the search term * @return this builder itself */ public SearchOptionsBuilder searchTerm(final String value) { options.setSearchTerm(value); return this; } /** * Adds a sort order option to the list of sort options * * @param field * the field name to sort by * @param order * the order of the sort (ASCENDING, DESCENDING) * @return the current SearchOptionsBuilder */ public SearchOptionsBuilder sort(final String field, final Order order) { options.addSort(field, order); return this; } /** * @param filters the filters to set * @return this builder itself */ public SearchOptionsBuilder setFilters(final List filters) { options.setFilters(filters); return this; } /** * @param sorts the sorts to set * @return this builder itself */ public SearchOptionsBuilder setSort(final List sorts) { options.setSorts(sorts); return this; } /** * @return the SearchOptions finally built using this builder. */ public SearchOptions done() { return options; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/SearchResult.java ================================================ /** * 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.search; import java.io.Serializable; import java.util.List; /** * Represents the result of a Search. For details on 'Search mechanism', see {@link SearchOptionsBuilder} and * {@link SearchOptions}. * A SearchResult is composed of a result list {@link #getResult()} that is the paginated list of results * matching the provided criteria, and a * result count {@link #getCount()} that is the total number of results matching the provided criteria. * * @param * the type of the objects being returned by the search. * @author Emmanuel Duchastenier * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @see SearchOptions * @see SearchOptionsBuilder */ public interface SearchResult extends Serializable { /** * Get the total number of matching result in the data base. This number can be greater than the number of elements * retrieved in the search depending on * paging criterion. * * @return The total number of matching result in the data base. * @since 6.0 */ long getCount(); /** * Get the list of elements retrieved by the search. * * @return The list of elements retrieved by the search. * @since 6.0 */ List getResult(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/Sort.java ================================================ /** * 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.search; import java.io.Serializable; /** * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros */ public class Sort implements Serializable { private static final long serialVersionUID = -8803283884140398833L; private final Order order; private final String field; public Sort(final Order order, final String field) { super(); this.order = order; this.field = field; } public Order getOrder() { return order; } public String getField() { return field; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (field == null ? 0 : field.hashCode()); result = prime * result + (order == null ? 0 : order.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Sort other = (Sort) obj; if (field == null) { if (other.field != null) { return false; } } else if (!field.equals(other.field)) { return false; } if (order != other.order) { return false; } return true; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/impl/SearchFilter.java ================================================ /** * 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.search.impl; import java.io.Serializable; import org.bonitasoft.engine.exception.IncorrectParameterException; import org.bonitasoft.engine.search.SearchFilterOperation; /** * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public class SearchFilter implements Serializable { private static final long serialVersionUID = 2476946810762051485L; private String field; private SearchFilterOperation operation; private Serializable value; private Serializable from; private Serializable to; /** * @param field * the field to filter on * @param operation * the operation to filter on * @param value * the value of the field to filter on * @see SearchFilterOperation */ public SearchFilter(final String field, final SearchFilterOperation operation, final Serializable value) { this.field = field; this.operation = operation; this.value = value; } public SearchFilter(final String field, final Serializable from, final Serializable to) { this.field = field; operation = SearchFilterOperation.BETWEEN; this.from = from; this.to = to; } public SearchFilter(final SearchFilterOperation operation) throws IncorrectParameterException { this.operation = operation; if (!isUndefinedFieldNameAuthorized()) { throw new IncorrectParameterException( "search operator can only be AND, OR, L_PARENTHESIS, R_PARENTHESIS on the one-parameter SearchFilter constructor"); } } public boolean isUndefinedFieldNameAuthorized() { switch (operation) { case AND: case OR: case L_PARENTHESIS: case R_PARENTHESIS: return true; default: return false; } } /** * @return the field name */ public String getField() { return field; } /** * @param field * the field name to set */ public void setField(final String field) { this.field = field; } /** * @return the operation */ public SearchFilterOperation getOperation() { return operation; } /** * @param operation * the operation to set */ public void setOperation(final SearchFilterOperation operation) { this.operation = operation; } /** * @return the value */ public Serializable getValue() { return value; } /** * @param value * the value to set */ public void setValue(final Serializable value) { this.value = value; } /** * @return the from */ public Serializable getFrom() { return from; } /** * @return the to */ public Serializable getTo() { return to; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (field == null ? 0 : field.hashCode()); result = prime * result + (from == null ? 0 : from.hashCode()); result = prime * result + (operation == null ? 0 : operation.hashCode()); result = prime * result + (to == null ? 0 : to.hashCode()); result = prime * result + (value == null ? 0 : value.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final SearchFilter other = (SearchFilter) obj; if (field == null) { if (other.field != null) { return false; } } else if (!field.equals(other.field)) { return false; } if (from == null) { if (other.from != null) { return false; } } else if (!from.equals(other.from)) { return false; } if (operation != other.operation) { return false; } if (to == null) { if (other.to != null) { return false; } } else if (!to.equals(other.to)) { return false; } if (value == null) { if (other.value != null) { return false; } } else if (!value.equals(other.value)) { return false; } return true; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/impl/SearchOptionsImpl.java ================================================ /** * 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.search.impl; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.exception.IncorrectParameterException; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchFilterOperation; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.Sort; /** * @author Matthieu Chaffotte * @author Emmanuel Duchastenier * @author Celine Souchet */ public class SearchOptionsImpl implements SearchOptions { private static final long serialVersionUID = 1967932608495889373L; private List filters; private String searchTerm; private final int startIndex; private final int numberOfResults; private List sorts; public SearchOptionsImpl(final int startIndex, final int numberOfResults) { filters = new ArrayList(5); sorts = new ArrayList(2); this.startIndex = startIndex; this.numberOfResults = numberOfResults; } public void setFilters(final List filters) { this.filters = filters; } @Override public List getFilters() { return filters; } @Override public String getSearchTerm() { return searchTerm; } @Override public int getStartIndex() { return startIndex; } @Override public int getMaxResults() { return numberOfResults; } @Override public List getSorts() { return sorts; } public void setSearchTerm(final String value) { searchTerm = value; } public void addGreaterThanFilter(final String field, final Serializable value) { filters.add(new SearchFilter(field, SearchFilterOperation.GREATER_THAN, value)); } public void addGreaterOrEqualsFilter(final String field, final Serializable value) { filters.add(new SearchFilter(field, SearchFilterOperation.GREATER_OR_EQUAL, value)); } public void addLessThanFilter(final String field, final Serializable value) { filters.add(new SearchFilter(field, SearchFilterOperation.LESS_THAN, value)); } public void addLessOrEqualsFilter(final String field, final Serializable value) { filters.add(new SearchFilter(field, SearchFilterOperation.LESS_OR_EQUAL, value)); } public void addBetweenFilter(final String field, final Serializable from, final Serializable to) { filters.add(new SearchFilter(field, from, to)); } public void addDifferentFromFilter(final String field, final Serializable value) { filters.add(new SearchFilter(field, SearchFilterOperation.DIFFERENT, value)); } public void addFilter(final String field, final Serializable value) { filters.add(new SearchFilter(field, SearchFilterOperation.EQUALS, value)); } public final void addOrFilter() { try { filters.add(new SearchFilter(SearchFilterOperation.OR)); } catch (final IncorrectParameterException e) { // Cannot happen as we force a correct value } } public final void addAndFilter() { try { filters.add(new SearchFilter(SearchFilterOperation.AND)); } catch (final IncorrectParameterException e) { // Cannot happen as we force a correct value } } public final void addLeftParenthesis() { try { filters.add(new SearchFilter(SearchFilterOperation.L_PARENTHESIS)); } catch (final IncorrectParameterException e) { // Cannot happen as we force a correct value } } public final void addRightParenthesis() { try { filters.add(new SearchFilter(SearchFilterOperation.R_PARENTHESIS)); } catch (final IncorrectParameterException e) { // Cannot happen as we force a correct value } } public void addSort(final String field, final Order order) { sorts.add(new Sort(order, field)); } public void setSorts(final List sorts) { this.sorts = sorts; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (filters == null ? 0 : filters.hashCode()); result = prime * result + numberOfResults; result = prime * result + (searchTerm == null ? 0 : searchTerm.hashCode()); result = prime * result + (sorts == null ? 0 : sorts.hashCode()); result = prime * result + startIndex; return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final SearchOptionsImpl other = (SearchOptionsImpl) obj; if (filters == null) { if (other.filters != null) { return false; } } else if (!filters.equals(other.filters)) { return false; } if (numberOfResults != other.numberOfResults) { return false; } if (searchTerm == null) { if (other.searchTerm != null) { return false; } } else if (!searchTerm.equals(other.searchTerm)) { return false; } if (sorts == null) { if (other.sorts != null) { return false; } } else if (!sorts.equals(other.sorts)) { return false; } if (startIndex != other.startIndex) { return false; } return true; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/impl/SearchResultImpl.java ================================================ /** * 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.search.impl; import java.io.Serializable; import java.util.List; import org.bonitasoft.engine.search.SearchResult; /** * @author Emmanuel Duchastenier */ public class SearchResultImpl implements SearchResult { private static final long serialVersionUID = -685595668360293014L; private final long count; private final List list; public SearchResultImpl(final long count, final List list) { super(); this.count = count; this.list = list; } @Override public long getCount() { return count; } @Override public List getResult() { return list; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (int) (count ^ count >>> 32); result = prime * result + (list == null ? 0 : list.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final SearchResultImpl other = (SearchResultImpl) obj; if (count != other.count) { return false; } if (list == null) { if (other.list != null) { return false; } } else if (!list.equals(other.list)) { return false; } return true; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/impl/package-info.java ================================================ /** * Copyright (C) 2015 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.search.impl; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/search/package-info.java ================================================ /** * Copyright (C) 2015 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. **/ /** *

* Contains classes to use when invoking search methods *

*/ package org.bonitasoft.engine.search; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/APISession.java ================================================ /** * 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.session; import java.util.List; /** * Informations concerning the connected tenant to the APIs * * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public interface APISession extends Session { /** * @return The list of profiles of the user */ List getProfiles(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/InvalidSessionException.java ================================================ /** * 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.session; import org.bonitasoft.engine.exception.BonitaRuntimeException; /** * @author Matthieu Chaffotte */ public class InvalidSessionException extends BonitaRuntimeException { private static final long serialVersionUID = 652973644884297283L; public InvalidSessionException(final String message) { super(message); } public InvalidSessionException(final Throwable e) { super(e); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/PlatformSession.java ================================================ /** * 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.session; /** * @author Elias Ricken de Medeiros */ public interface PlatformSession extends Session { } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/Session.java ================================================ /** * 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.session; import java.io.Serializable; import java.util.Date; /** * Informations concerning the connected user * * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public interface Session extends Serializable { /** * @return The identifier of the session */ long getId(); /** * @return The creation date of the session */ Date getCreationDate(); /** * @return The duration of the session */ long getDuration(); /** * @return The user name associated to this session */ String getUserName(); /** * @return The identifier of the user associated to this session, if available (-1 if not) */ long getUserId(); /** * @return True if the logged user is the technical one, False otherwise. */ boolean isTechnicalUser(); } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/SessionNotFoundException.java ================================================ /** * 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.session; import org.bonitasoft.engine.exception.BonitaException; /** * @author Elias Ricken de Medeiros */ public class SessionNotFoundException extends BonitaException { private static final long serialVersionUID = -7611408247856380936L; public SessionNotFoundException(final String message) { super(message); } public SessionNotFoundException(final String message, final Throwable cause) { super(message, cause); } public SessionNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/impl/APISessionImpl.java ================================================ /** * 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.session.impl; import java.util.Date; import java.util.List; import lombok.Data; import lombok.EqualsAndHashCode; import org.bonitasoft.engine.session.APISession; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ @Data @EqualsAndHashCode(callSuper = true) public class APISessionImpl extends SessionImpl implements APISession { private List profiles; public APISessionImpl(final long id, final Date creationDate, final long duration, final String userName, final long userId) { super(id, creationDate, duration, userName, userId); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/impl/PlatformSessionImpl.java ================================================ /** * 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.session.impl; import java.util.Date; import lombok.Data; import lombok.EqualsAndHashCode; import org.bonitasoft.engine.session.PlatformSession; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ @Data @EqualsAndHashCode(callSuper = true) public class PlatformSessionImpl extends SessionImpl implements PlatformSession { public PlatformSessionImpl(final long id, final Date creationDate, final long duration, final String userName, final long userId) { super(id, creationDate, duration, userName, userId); } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/impl/SessionImpl.java ================================================ /** * 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.session.impl; import java.util.Date; import lombok.Data; import org.bonitasoft.engine.session.Session; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ @Data public abstract class SessionImpl implements Session { private long id; private Date creationDate; private long duration; private String userName; private long userId; private boolean technicalUser; public SessionImpl(final long id, final Date creationDate, final long duration, final String userName, final long userId) { super(); this.id = id; this.creationDate = creationDate; this.duration = duration; this.userName = userName; this.userId = userId; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/session/package-info.java ================================================ /** * Copyright (C) 2015 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.session; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/tenant/TenantResource.java ================================================ /** * Copyright (C) 2017 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.tenant; import java.time.Instant; import java.time.OffsetDateTime; import java.time.ZoneOffset; import lombok.Getter; import org.bonitasoft.engine.bpm.BonitaObject; /** * @author Emmanuel Duchastenier */ @Getter public class TenantResource implements BonitaObject { public static final TenantResource NONE = new TenantResource(0, "", null, 0, 0, null); private final long id; private final String name; private final TenantResourceType type; private final OffsetDateTime lastUpdateDate; private final long lastUpdatedBy; private final TenantResourceState state; public TenantResource(long id, String name, TenantResourceType type, long lastUpdateDate, long lastUpdatedBy, TenantResourceState state) { this.id = id; this.name = name; this.type = type; this.lastUpdateDate = OffsetDateTime.ofInstant(Instant.ofEpochMilli(lastUpdateDate), ZoneOffset.UTC); this.lastUpdatedBy = lastUpdatedBy; this.state = state; } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/tenant/TenantResourceState.java ================================================ /** * Copyright (C) 2017 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.tenant; /** * @author Emmanuel Duchastenier */ public enum TenantResourceState { INSTALLING, INSTALLED } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/tenant/TenantResourceType.java ================================================ /** * Copyright (C) 2017 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.tenant; /** * @author Emmanuel Duchastenier */ public enum TenantResourceType { BDM, BDM_ACCESS_CTRL } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/util/package-info.java ================================================ /** * Copyright (C) 2015 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.util; ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/xml/DocumentManager.java ================================================ /** * 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.xml; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.xml.sax.SAXException; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class DocumentManager { private DocumentManager() { // For Sonar } public static Document generateDocument(final String s) throws ParserConfigurationException, SAXException, IOException { final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); try { documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // security-compliant documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // security-compliant } catch (IllegalArgumentException e) { //ignored, if not supported by the implementation } final DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder(); try (InputStream is = new ByteArrayInputStream(s.getBytes())) { return builder.parse(is, null); } } public static String getDocumentContent(final Document document) throws TransformerFactoryConfigurationError, TransformerException, IOException { final TransformerFactory transformerFactory = TransformerFactory.newInstance(); try { transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // security-compliant transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); // security-compliant } catch (IllegalArgumentException e) { //ignored, if not supported by the implementation } final Transformer transformer = transformerFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); try (StringWriter writer = new StringWriter()) { final StreamResult result = new StreamResult(writer); final DOMSource source = new DOMSource(document); transformer.transform(source, result); return result.getWriter().toString(); } } } ================================================ FILE: bpm/bonita-common/src/main/java/org/bonitasoft/engine/xml/XStreamDenyList.java ================================================ /** * Copyright (C) 2026 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.xml; import java.util.Arrays; /** * Configurable XStream deserialization deny list. *

* Provides the list of wildcard patterns used by {@code XStream.denyTypesByWildcard()} to block * known deserialization gadget chain libraries and prevent RCE. *

* The deny list can be overridden at runtime via the system property * {@value #DENY_PACKAGES_PROPERTY} (comma-separated wildcard patterns). * When set, it fully replaces the hardcoded defaults. *

* Important: The system property is read at first XStream usage. It should be set at * JVM startup (e.g. via {@code -D} flag) before any serialization/deserialization occurs. */ public final class XStreamDenyList { public static final String DENY_PACKAGES_PROPERTY = "bonita.xstream.deny.packages"; private static final String[] DEFAULT_DENY_PATTERNS = { "org.apache.commons.collections.functors.**", "org.apache.commons.collections4.functors.**", "org.apache.commons.collections4.comparators.**", "org.apache.commons.beanutils.**", "com.sun.org.apache.xalan.**", "com.sun.org.apache.bcel.**", "javassist.**", "groovy.lang.MethodClosure", "groovy.lang.GroovyShell", "groovy.lang.GroovyClassLoader", "org.codehaus.groovy.runtime.**", "javax.script.**", "java.net.URL*", "java.net.InetAddress*", "javax.naming.**", "com.sun.jndi.**", "java.rmi.**", "sun.rmi.**", "com.mchange.**", "org.apache.xalan.**", }; private XStreamDenyList() { } /** * Returns the deny patterns to use, reading from the system property if set, * falling back to hardcoded defaults otherwise. * * @return a fresh copy of the deny patterns array */ public static String[] getDenyPatterns() { String property = System.getProperty(DENY_PACKAGES_PROPERTY); if (property != null && !property.isBlank()) { return parsePatterns(property); } return DEFAULT_DENY_PATTERNS.clone(); } private static String[] parsePatterns(String commaDelimited) { return Arrays.stream(commaDelimited.trim().split("\\s*,\\s*")) .filter(s -> !s.isEmpty()) .toArray(String[]::new); } } ================================================ FILE: bpm/bonita-common/src/main/javadoc/overview.html ================================================ package.html

This is an overview of Bonita Community common API.

================================================ FILE: bpm/bonita-common/src/main/javadoc/stylesheet.css ================================================ /* Javadoc style sheet */ /* Overall document style */ body { background-color:#ffffff; color:#003355; font-family:"Trebuchet MS",Arial,Helvetica,sans-serif; font-size:76%; margin:0; } a:link, a:visited { text-decoration:none; color:#4c6b87; } a:hover, a:focus { text-decoration:none; color:#DD0033; } a:hover{ text-decoration: underline; } a:active { text-decoration:none; color:#4c6b87; } a[name] { color:#353833; } a[name]:hover { text-decoration:none; color:#353833; } pre { font-size:1.3em; } h1 { font-size: 36px; } h2 { font-size:1.5em; } h3 { font-size:1.4em; } h4 { font-size:1.3em; } h5 { font-size:1.2em; } h6 { font-size:1.1em; } ul { list-style-type:disc; } code, tt { font-size:1.2em; } dt code { font-size:1.2em; } table tr td dt code { font-size:1.2em; vertical-align:top; } sup { font-size:.6em; } /* Document title and Copyright styles */ .clear { clear:both; height:0px; overflow:hidden; } .aboutLanguage { float:right; padding:0px 21px; font-size:.8em; z-index:200; margin-top:-7px; } .legalCopy { margin-left:.5em; } .bar a, .bar a:link, .bar a:visited, .bar a:active { color:#FFFFFF; text-decoration:none; } .bar a:hover, .bar a:focus { color:#DD0033; } .tab { background-color:#0066FF; background-image:url(resources/titlebar.gif); background-position:left top; background-repeat:no-repeat; color:#ffffff; padding:8px; width:5em; font-weight:bold; } /* Navigation bar styles */ .bar { background:#00395A; color:#FFFFFF; padding:.8em .5em .4em .8em; height:auto;/*height:1.8em;*/ font-size:1em; margin:0; } .topNav, .bottomNav { background: #00395A; color:#FFFFFF; float:left; padding:0; width:100%; clear:right; overflow: visible; height: 45px; } .subNav { background-color:#dee3e9; border-bottom:1px solid #9eadc0; float:left; width:100%; overflow:hidden; } .subNav div { clear:left; float:left; padding:0 0 5px 6px; } ul.navList, ul.subNavList { font-size: 15px; margin:0 auto; padding:0; height: 100%; width: 960px; } ul.navList li{ vertical-align: middle; text-align: center; list-style:none; float:left; height: 100%; padding: 0 16px; line-height: 45px; } ul.navList li a{ height: 100%; display: block; } ul.subNavList li{ list-style:none; float:left; font-size:90%; padding-right: 5px; } .topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { color:#FFFFFF; text-decoration:none; } .topNav a:hover, .bottomNav a:hover, .topNav li:hover, .bottomNav li:hover{ text-decoration:none; background: #DD0033; } .navBarCell1Rev { background-color:#DD0033; color:#FFFFFF; margin: 0; position: relative; } .topNav .navBarCell1Rev:after { top: 100%; left:50%; border: solid transparent; content: " "; height: 0; width: 0; position: absolute; pointer-events: none; border-color: rgba(221, 0, 51, 0); border-top-color: #DD0033; border-width: 10px; margin-left: -10px; } .bottomNav .navBarCell1Rev:after { bottom: 100%; left: 50%; border: solid transparent; content: " "; height: 0; width: 0; position: absolute; pointer-events: none; border-color: rgba(221, 0, 51, 0); border-bottom-color: #DD0033; border-width: 10px; margin-left: -10px; } /* Page header and footer styles */ .header, .footer { clear:both; margin:0 20px; padding:5px 0 0 0; } .indexHeader { margin:10px; position:relative; } .indexHeader h1 { font-size:1.3em; } .title { color:#2c4557; margin:10px 0; } .subTitle { margin:5px 0 0 0; } .header ul { margin:0 0 25px 0; padding:0; } .footer ul { margin:20px 0 5px 0; } .header ul li, .footer ul li { list-style:none; font-size:1.2em; } /* Heading styles */ div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { background-color:#dee3e9; border-top:1px solid #9eadc0; border-bottom:1px solid #9eadc0; margin:0 0 6px -8px; padding:2px 5px; } ul.blockList ul.blockList ul.blockList li.blockList h3 { background-color:#dee3e9; border-top:1px solid #9eadc0; border-bottom:1px solid #9eadc0; margin:0 0 6px -8px; padding:2px 5px; } ul.blockList ul.blockList li.blockList h3 { padding:0; margin:15px 0; } ul.blockList li.blockList h2 { padding:0px 0 20px 0; } /* Page layout container styles */ .contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { clear:both; padding:10px 20px; position:relative; } .indexContainer { margin:10px; position:relative; font-size:1.0em; } .indexContainer h2 { font-size:1.1em; padding:0 0 3px 0; } .indexContainer ul { margin:0; padding:0; } .indexContainer ul li { list-style:none; } .contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { font-size:1.1em; font-weight:bold; margin:10px 0 0 0; color:#DD0033; } .contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { margin:10px 0 10px 20px; } .serializedFormContainer dl.nameValue dt { margin-left:1px; font-size:1.1em; display:inline; font-weight:bold; } .serializedFormContainer dl.nameValue dd { margin:0 0 0 1px; font-size:1.1em; display:inline; } /* List styles */ ul.horizontal li { display:inline; font-size:0.9em; } ul.inheritance { margin:0; padding:0; } ul.inheritance li { display:inline; list-style:none; } ul.inheritance li ul.inheritance { margin-left:15px; padding-left:15px; padding-top:1px; } ul.blockList, ul.blockListLast { margin:10px 0 10px 0; padding:0; } ul.blockList li.blockList, ul.blockListLast li.blockList { list-style:none; margin-bottom:25px; } ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { padding:0px 20px 5px 10px; border:1px solid #9eadc0; background-color:#f9f9f9; } ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { padding:0 0 5px 8px; background-color:#ffffff; border:1px solid #9eadc0; border-top:none; } ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { margin-left:0; padding-left:0; padding-bottom:15px; border:none; border-bottom:1px solid #9eadc0; } ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { list-style:none; border-bottom:none; padding-bottom:0; } table tr td dl, table tr td dl dt, table tr td dl dd { margin-top:0; margin-bottom:1px; } /* Table styles */ .contentContainer table, .classUseContainer table, .constantValuesContainer table { border-bottom:1px solid #9eadc0; width:100%; } .contentContainer ul li table, .classUseContainer ul li table, .constantValuesContainer ul li table { width:100%; } .contentContainer .description table, .contentContainer .details table { border-bottom:none; } .contentContainer ul li table th.colOne, .contentContainer ul li table th.colFirst, .contentContainer ul li table th.colLast, .classUseContainer ul li table th, .constantValuesContainer ul li table th, .contentContainer ul li table td.colOne, .contentContainer ul li table td.colFirst, .contentContainer ul li table td.colLast, .classUseContainer ul li table td, .constantValuesContainer ul li table td{ vertical-align:top; padding-right:20px; } .contentContainer ul li table th.colLast, .classUseContainer ul li table th.colLast,.constantValuesContainer ul li table th.colLast, .contentContainer ul li table td.colLast, .classUseContainer ul li table td.colLast,.constantValuesContainer ul li table td.colLast, .contentContainer ul li table th.colOne, .classUseContainer ul li table th.colOne, .contentContainer ul li table td.colOne, .classUseContainer ul li table td.colOne { padding-right:3px; } .overviewSummary caption, .packageSummary caption, .contentContainer ul.blockList li.blockList caption, .summary caption, .classUseContainer caption, .constantValuesContainer caption { position:relative; text-align:left; background-repeat:no-repeat; color:#FFFFFF; font-weight:bold; clear:none; overflow:hidden; padding:0px; margin:0px; } caption a:link, caption a:hover, caption a:active, caption a:visited { color:#FFFFFF; } .contentContainer > ul.blockList caption span{ color: #DD0033; border: 1px solid #DD0033; } .contentContainer > ul:nth-of-type(9n+1).blockList caption span{ border: 1px solid #1A2574; color: #1A2574; } .contentContainer > ul:nth-of-type(9n+2).blockList caption span { border: 1px solid #77BB33; color: #77BB33; } .contentContainer > ul:nth-of-type(9n+3).blockList caption span { border: 1px solid #F89828; color: #F89828; } .contentContainer > ul:nth-of-type(9n+4).blockList caption span { border: 1px solid #DD0033; color: #DD0033; } .contentContainer > ul:nth-of-type(9n+5).blockList caption span { border: 1px solid #1A2574; color: #1A2574; } .contentContainer > ul:nth-of-type(9n+6).blockList caption span { border: 1px solid #77BB33; color: #77BB33; } .contentContainer > ul:nth-of-type(9n+7).blockList caption span { border: 1px solid #F89828; color: #F89828; } .contentContainer > ul:nth-of-type(9n+8).blockList caption span { border: 1px solid #DD0033; color: #DD0033; } .contentContainer > ul:nth-of-type(9n+9).blockList caption span { border: 1px solid #1A2574; color: #1A2574; } .overviewSummary caption span, .packageSummary caption span, .contentContainer ul.blockList li.blockList caption span, .summary caption span, .classUseContainer caption span, .constantValuesContainer caption span { white-space:nowrap; padding: 2px 4px; display:block; float:left; height:18px; margin-bottom: 10px; } .overviewSummary .tabEnd, .packageSummary .tabEnd, .contentContainer ul.blockList li.blockList .tabEnd, .summary .tabEnd, .classUseContainer .tabEnd, .constantValuesContainer .tabEnd { display:none; } ul.blockList ul.blockList li.blockList table { margin:0 0 12px 0px; width:100%; } .tableSubHeadingColor { background-color: #EEEEFF; } .altColor { background-color:#E6EBF1; } .altColor:hover, .rowColor:hover{ background-color:#F09383; } .altColor:hover a, .rowColor:hover a,.rowColor:hover code{ color:#FFFFFF; text-decoration: none; } .rowColor { background-color:#ffffff; } .overviewSummary td, .packageSummary td, .contentContainer ul.blockList li.blockList td, .summary td, .classUseContainer td, .constantValuesContainer td { text-align:left; padding:3px 3px 3px 7px; } th.colFirst, th.colLast, th.colOne, .constantValuesContainer th { background:#dee3e9; border-top:1px solid #9eadc0; border-bottom:1px solid #9eadc0; text-align:left; padding:3px 3px 3px 7px; } td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { font-weight:bold; } td.colFirst, th.colFirst { border-left:1px solid #9eadc0; white-space:nowrap; } td.colLast, th.colLast { border-right:1px solid #9eadc0; } td.colOne, th.colOne { border-right:1px solid #9eadc0; border-left:1px solid #9eadc0; } table.overviewSummary { padding:0px; margin-left:0px; } table.overviewSummary td.colFirst, table.overviewSummary th.colFirst, table.overviewSummary td.colOne, table.overviewSummary th.colOne { width:25%; vertical-align:middle; } table.packageSummary td.colFirst, table.overviewSummary th.colFirst { width:25%; vertical-align:middle; } /* Content styles */ .description pre { margin-top:0; } .deprecatedContent { margin:0; padding:10px 0; } .docSummary { padding:0; } /* Formatting effect styles */ .sourceLineNo { color:green; padding:0 30px 0 0; } h1.hidden { visibility:hidden; overflow:hidden; font-size:.9em; } .block { display:block; margin:3px 0 0 0; } .strong { font-weight:bold; } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/api/permission/APICallContextTest.java ================================================ /** * 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.api.permission; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import java.util.List; import java.util.Map; import org.junit.Test; public class APICallContextTest { @Test public void getFilters() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setQueryString("p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d104&d=processId"); final Map filters = apiCallContext.getFilters(); assertThat(filters).containsOnly(entry("user_id", "104"), entry("state", "ready")); } @Test public void getFilters_with_multi_value() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setQueryString("p=0&c=10&f=state%3dready,completed&f=user_id%3d104"); final Map filters = apiCallContext.getFilters(); assertThat(filters).containsOnly(entry("user_id", "104"), entry("state", "ready,completed")); } @Test public void getFilters_with_uncoded_equals() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setQueryString("p=0&c=10&f=state=ready,completed&f=user_id=104"); final Map filters = apiCallContext.getFilters(); assertThat(filters).containsOnly(entry("user_id", "104"), entry("state", "ready,completed")); } @Test public void getFilters_with_UpperCase_in_separator() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setQueryString("p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3D104&d=processId"); final Map filters = apiCallContext.getFilters(); assertThat(filters).containsOnly(entry("user_id", "104"), entry("state", "ready")); } @Test public void getCompoundResourceId() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setResourceId("1/2/3"); final List compoundResourceId = apiCallContext.getCompoundResourceId(); assertThat(compoundResourceId).containsExactly("1", "2", "3"); } @Test public void getCompoundResourceId_when_single_id() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setResourceId("1"); final List compoundResourceId = apiCallContext.getCompoundResourceId(); assertThat(compoundResourceId).containsExactly("1"); } @Test public void getCompoundResourceId_when_no_resource_id() { final APICallContext apiCallContext = new APICallContext(); final List compoundResourceId = apiCallContext.getCompoundResourceId(); assertThat(compoundResourceId).isEmpty(); } @Test public void getFilters_with_corrupt_filters() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setQueryString("p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d&d=processId"); final Map filters = apiCallContext.getFilters(); assertThat(filters).containsOnly(entry("state", "ready")); } @Test public void getFilters_with_corrupt_filters_not_encoded() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setQueryString("p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id=&d=processId"); final Map filters = apiCallContext.getFilters(); assertThat(filters).containsOnly(entry("state", "ready")); } @Test public void getFilters_with_value_not_encoded() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setQueryString("p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id=101&d=processId"); final Map filters = apiCallContext.getFilters(); assertThat(filters).containsOnly(entry("state", "ready"), entry("user_id", "101")); } @Test public void getSearchTerm() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setQueryString("p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d104&d=processId&s=toto"); final String searchTerm = apiCallContext.getSearchTerm(); assertThat(searchTerm).isEqualTo("toto"); } @Test public void should_return_allparameters() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setQueryString("p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d3&d=processId¶m=1"); final Map parameters = apiCallContext.getParameters(); assertThat(parameters).contains(entry("p", new String[] { "0" })); assertThat(parameters).contains(entry("c", new String[] { "10" })); assertThat(parameters).contains(entry("o", new String[] { "priority%20DESC" })); assertThat(parameters).contains(entry("f", new String[] { "state%3dready", "user_id%3d3" })); assertThat(parameters).contains(entry("d", new String[] { "processId" })); assertThat(parameters).contains(entry("param", new String[] { "1" })); } @Test public void getSearchTerm_with_corrupt_search_term() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setQueryString("p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d104&d=processId&s="); final String searchTerm = apiCallContext.getSearchTerm(); assertThat(searchTerm).isEqualTo(null); } @Test public void getSearchTerm_with_corrupt_query() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setQueryString("p=0&c=10&o=priority%20DESC&f=state%3dready&f=user_id%3d104&d=processId&s"); final String searchTerm = apiCallContext.getSearchTerm(); assertThat(searchTerm).isEqualTo(null); } @Test public void construct_with_null_body_and_query() { final APICallContext apiCallContext = new APICallContext("GET", "identity", "user", "1"); final String searchTerm = apiCallContext.getSearchTerm(); final Map filters = apiCallContext.getFilters(); final String body = apiCallContext.getBody(); assertThat(searchTerm).isEqualTo(null); assertThat(filters).isEmpty(); assertThat(body).isEqualTo(null); } @Test public void should_isGETMethod_return_true() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setMethod("GET"); final boolean getMethod = apiCallContext.isGET(); assertThat(getMethod).isTrue(); } @Test public void should_isGETMethod_return_false() { final APICallContext apiCallContext = new APICallContext(); final boolean getMethod = apiCallContext.isGET(); assertThat(getMethod).isFalse(); } @Test public void should_isPOSTMethod_return_true() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setMethod("POST"); final boolean getMethod = apiCallContext.isPOST(); assertThat(getMethod).isTrue(); } @Test public void should_isPOSTMethod_return_false() { final APICallContext apiCallContext = new APICallContext(); final boolean getMethod = apiCallContext.isPOST(); assertThat(getMethod).isFalse(); } @Test public void should_isPUTMethod_return_true() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setMethod("PUT"); final boolean getMethod = apiCallContext.isPUT(); assertThat(getMethod).isTrue(); } @Test public void should_isPUTMethod_return_false() { final APICallContext apiCallContext = new APICallContext(); final boolean getMethod = apiCallContext.isPUT(); assertThat(getMethod).isFalse(); } @Test public void should_isDELETEMethod_return_true() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setMethod("DELETE"); final boolean getMethod = apiCallContext.isDELETE(); assertThat(getMethod).isTrue(); } @Test public void should_isDELETEMethod_return_false() { final APICallContext apiCallContext = new APICallContext(); final boolean getMethod = apiCallContext.isDELETE(); assertThat(getMethod).isFalse(); } @Test public void should_equals_works() { final APICallContext apiCallContext = new APICallContext(); apiCallContext.setMethod("GET"); apiCallContext.setApiName("apiName"); apiCallContext.setResourceName("myResource"); apiCallContext.setResourceId("125"); assertThat(apiCallContext).isEqualTo(new APICallContext("GET", "apiName", "myResource", "125")); assertThat(apiCallContext.hashCode()) .isEqualTo(new APICallContext("GET", "apiName", "myResource", "125").hashCode()); assertThat(apiCallContext.toString()) .isEqualTo(new APICallContext("GET", "apiName", "myResource", "125").toString()); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/BusinessDataObjectMapperTest.java ================================================ /** * 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.bdm.serialization; import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; import static org.assertj.core.api.Assertions.assertThat; import java.io.StringWriter; import java.time.LocalDate; import org.bonitasoft.engine.bdm.serialization.model.Invoice; import org.junit.Test; public class BusinessDataObjectMapperTest { private final BusinessDataObjectMapper objectMapper = new BusinessDataObjectMapper(); @Test public void should_serialize_object_with_custom_local_date_and_time_serializers() throws Exception { // given: Invoice invoice = new Invoice(); invoice.setCustomerName("Bonitasoft"); invoice.setDate(LocalDate.of(2018, 8, 15)); invoice.setComments(null); String expectedJson; try (var inputStream = BusinessDataObjectMapperTest.class.getResourceAsStream("simpleInvoice.json")) { assertThat(inputStream).isNotNull(); expectedJson = new String(inputStream.readAllBytes()); } // when: StringWriter writer = new StringWriter(); objectMapper.writeValue(writer, invoice); // then: assertThatJson(writer.toString()).as("Serialization uses date and time custom serializers") .isEqualTo(expectedJson); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateDeserializerTest.java ================================================ /** * 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.bdm.serialization; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import java.time.LocalDate; import com.fasterxml.jackson.core.JsonParser; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Emmanuel Duchastenier */ @RunWith(MockitoJUnitRunner.class) public class CustomLocalDateDeserializerTest { @Mock private JsonParser jsonParser; @Test public void deserialize_should_convert_to_ISO_8601() throws Exception { // given: doReturn("2018-09-01").when(jsonParser).readValueAs(String.class); final CustomLocalDateDeserializer serializer = new CustomLocalDateDeserializer(); // when: final LocalDate deserialized = serializer.deserialize(jsonParser, null); // then: assertThat(deserialized).isEqualTo(LocalDate.of(2018, 9, 1)); } @Test public void deserialize_should_return_null_for_a_null_date() throws Exception { // given: doReturn(null).when(jsonParser).readValueAs(String.class); // when: final LocalDate localDate = new CustomLocalDateDeserializer().deserialize(jsonParser, null); // then: assertThat(localDate).isNull(); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateSerializerTest.java ================================================ /** * 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.bdm.serialization; import static org.mockito.Mockito.verify; import java.time.LocalDate; import com.fasterxml.jackson.core.JsonGenerator; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Emmanuel Duchastenier */ @RunWith(MockitoJUnitRunner.class) public class CustomLocalDateSerializerTest { @Mock JsonGenerator jsonGenerator; @Test public void serialize_should_write_date_to_ISO_8601() throws Exception { // given: final CustomLocalDateSerializer serializer = new CustomLocalDateSerializer(); // when: serializer.serialize(LocalDate.of(2018, 8, 15), jsonGenerator, null); // then: verify(jsonGenerator).writeString("2018-08-15"); } @Test public void serialize_should_write_null_for_a_null_date() throws Exception { // when: new CustomLocalDateSerializer().serialize(null, jsonGenerator, null); // then: verify(jsonGenerator).writeString((String) null); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateTimeDeserializerTest.java ================================================ /** * 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.bdm.serialization; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import java.time.LocalDateTime; import com.fasterxml.jackson.core.JsonParser; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Emmanuel Duchastenier */ @RunWith(MockitoJUnitRunner.class) public class CustomLocalDateTimeDeserializerTest { @Mock private JsonParser jsonParser; @Test public void deserialize_should_convert_to_ISO_8601() throws Exception { // given: doReturn("2018-09-01T09:21:08").when(jsonParser).readValueAs(String.class); final CustomLocalDateTimeDeserializer serializer = new CustomLocalDateTimeDeserializer(); // when: final LocalDateTime deserialized = serializer.deserialize(jsonParser, null); // then: assertThat(deserialized).isEqualTo(LocalDateTime.of(2018, 9, 1, 9, 21, 8)); } @Test public void deserialize_should_return_null_for_a_null_date() throws Exception { // given: doReturn(null).when(jsonParser).readValueAs(String.class); // when: final LocalDateTime localDateTimeTime = new CustomLocalDateTimeDeserializer().deserialize(jsonParser, null); // then: assertThat(localDateTimeTime).isNull(); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/CustomLocalDateTimeSerializerTest.java ================================================ /** * 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.bdm.serialization; import static org.mockito.Mockito.verify; import java.time.LocalDateTime; import com.fasterxml.jackson.core.JsonGenerator; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Emmanuel Duchastenier */ @RunWith(MockitoJUnitRunner.class) public class CustomLocalDateTimeSerializerTest { @Mock JsonGenerator jsonGenerator; @Test public void serialize_should_write_date_to_ISO_8601() throws Exception { // given: final CustomLocalDateTimeSerializer serializer = new CustomLocalDateTimeSerializer(); // when: serializer.serialize(LocalDateTime.of(2018, 10, 23, 11, 21, 7), jsonGenerator, null); // then: verify(jsonGenerator).writeString("2018-10-23T11:21:07"); } @Test public void serialize_should_write_null_for_a_null_date() throws Exception { // when: new CustomLocalDateTimeSerializer().serialize(null, jsonGenerator, null); // then: verify(jsonGenerator).writeString((String) null); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/CustomOffsetDateTimeDeserializerTest.java ================================================ /** * 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.bdm.serialization; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import com.fasterxml.jackson.core.JsonParser; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Emmanuel Duchastenier */ @RunWith(MockitoJUnitRunner.class) public class CustomOffsetDateTimeDeserializerTest { @Mock private JsonParser jsonParser; @Test public void deserialize_should_convert_to_UTC() throws Exception { // given: doReturn("2018-09-01T09:21:08+07:00").when(jsonParser).readValueAs(String.class); final CustomOffsetDateTimeDeserializer serializer = new CustomOffsetDateTimeDeserializer(); // when: final OffsetDateTime deserialized = serializer.deserialize(jsonParser, null); // then: assertThat(deserialized) .isEqualTo(OffsetDateTime.of(LocalDateTime.of(2018, 9, 1, 2, 21, 8), ZoneOffset.ofHours(0))); } @Test public void deserialize_should_return_null_for_a_null_date() throws Exception { // given: doReturn(null).when(jsonParser).readValueAs(String.class); // when: final OffsetDateTime offsetDateTime = new CustomOffsetDateTimeDeserializer().deserialize(jsonParser, null); // then: assertThat(offsetDateTime).isNull(); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/CustomOffsetDateTimeSerializerTest.java ================================================ /** * 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.bdm.serialization; import static org.mockito.Mockito.verify; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import com.fasterxml.jackson.core.JsonGenerator; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Emmanuel Duchastenier */ @RunWith(MockitoJUnitRunner.class) public class CustomOffsetDateTimeSerializerTest { @Mock JsonGenerator jsonGenerator; @Test public void serialize_should_write_date_converted_to_UTC() throws Exception { // given: final CustomOffsetDateTimeSerializer serializer = new CustomOffsetDateTimeSerializer(); // when: serializer.serialize(OffsetDateTime.of(LocalDateTime.of(2018, 10, 23, 11, 21, 7), ZoneOffset.ofHours(2)), jsonGenerator, null); // then: verify(jsonGenerator).writeString("2018-10-23T09:21:07Z"); } @Test public void serialize_should_write_null_for_a_null_date() throws Exception { // when: new CustomOffsetDateTimeSerializer().serialize(null, jsonGenerator, null); // then: verify(jsonGenerator).writeString((String) null); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/serialization/model/Invoice.java ================================================ /** * 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.bdm.serialization.model; import java.time.LocalDate; import org.bonitasoft.engine.bdm.Entity; public class Invoice implements Entity { private Long persistenceId; private Long persistenceVersion; private String customerName; private LocalDate date; private Object comments; @Override public Long getPersistenceId() { return persistenceId; } @Override public Long getPersistenceVersion() { return persistenceVersion; } public String getCustomerName() { return customerName; } public void setCustomerName(String customerName) { this.customerName = customerName; } public LocalDate getDate() { return date; } public void setDate(LocalDate date) { this.date = date; } public Object getComments() { return comments; } public void setComments(Object comments) { this.comments = comments; } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/BusinessObjectModelValidatorTest.java ================================================ /** * 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.bdm.validator; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bdm.validator.assertion.RuleOfCondition.ruleOf; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.bdm.validator.rule.composition.CyclicCompositionValidationRule; import org.bonitasoft.engine.bdm.validator.rule.composition.UniquenessCompositionValidationRule; import org.junit.Before; import org.junit.Test; /** * @author Romain Bioteau */ public class BusinessObjectModelValidatorTest { private BusinessObjectModelValidator validator; @Before public void setUp() { validator = new BusinessObjectModelValidator(); } @Test public void shouldConstructor_FillListOfRules() { assertThat(validator.getRules()).isNotEmpty(); } @Test public void shouldValidate_ReturnsAValidStatus() { BusinessObjectModel bom = new BusinessObjectModel(); BusinessObject bo = new BusinessObject(); bo.setQualifiedName("org.bonita.Car"); SimpleField nameField = new SimpleField(); nameField.setName("bmw"); nameField.setType(FieldType.STRING); bo.addField(nameField); bom.addBusinessObject(bo); assertThat(validator.validate(bom).isOk()).isTrue(); } @Test public void shouldValidate_ReturnsAFailedStatus() { BusinessObjectModel bom = new BusinessObjectModel(); BusinessObject bo = new BusinessObject(); bo.setQualifiedName("org.bonita.Car"); SimpleField fieldWithTwoErrors = new SimpleField(); fieldWithTwoErrors.setName("bmw 5"); bo.getFields().add(fieldWithTwoErrors); bom.addBusinessObject(bo); ValidationStatus validationStatus = validator.validate(bom); assertThat(validationStatus.isOk()).isFalse(); assertThat(validationStatus.getErrors()).hasSize(2); } @Test public void should_validate_cyclic_composition() { assertThat(validator.getRules()).haveAtLeast(1, ruleOf(CyclicCompositionValidationRule.class)); } @Test public void should_validate_composition_uniqueness() { assertThat(validator.getRules()).haveAtLeast(1, ruleOf(UniquenessCompositionValidationRule.class)); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/UniqueNameValidatorTest.java ================================================ /** * 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.bdm.validator; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat; import java.util.List; import org.bonitasoft.engine.bdm.model.NamedElement; import org.junit.Before; import org.junit.Test; public class UniqueNameValidatorTest { private UniqueNameValidator validator; @Before public void initValidator() { validator = new UniqueNameValidator(); } @Test public void should_validate_a_list_with_no_duplicated_names() { List listWithNoDuplicatedNames = asList(aNamedElement("aName"), aNamedElement("anOtherName"), aNamedElement("yetDifferentName")); ValidationStatus status = validator.validate(listWithNoDuplicatedNames, "named elements"); assertThat(status).isOk(); } @Test public void should_not_validate_a_list_duplicated_names() { String duplicatedName = "duplicatedName"; List listWithNoDuplicatedNames = asList(aNamedElement("notDuplicatedName"), aNamedElement(duplicatedName), aNamedElement(duplicatedName), aNamedElement(duplicatedName)); ValidationStatus status = validator.validate(listWithNoDuplicatedNames, "named elements"); assertThat(status).isNotOk(); assertThat(status.getErrors()).hasSize(1); } public NamedElement aNamedElement(String name) { return new FakeNamedElement(name); } public static class FakeNamedElement implements NamedElement { private final String name; public FakeNamedElement(String name) { this.name = name; } @Override public String getName() { return name; } } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/ValidationStatusTest.java ================================================ /** * 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.bdm.validator; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Before; import org.junit.Test; /** * @author Romain Bioteau */ public class ValidationStatusTest { private ValidationStatus validationStatus; @Before public void setUp() { validationStatus = new ValidationStatus(); } @Test public void isOk_should_return_true() { assertThat(validationStatus.isOk()).isTrue(); } @Test public void isOk_should_return_false_if_status_contains_errors() { validationStatus.addError(null, "an error"); assertThat(validationStatus.isOk()).isFalse(); } @Test public void isOk_should_return_false_if_add_a_status_with_errors() { ValidationStatus status = new ValidationStatus(); status.addError(null, "an error"); validationStatus.addValidationStatus(status); assertThat(validationStatus.isOk()).isFalse(); } @Test(expected = IllegalArgumentException.class) public void shouldAddError_throw_an_IllegalArgumentException_for_null_input() { validationStatus.addError(null, null); } @Test(expected = IllegalArgumentException.class) public void shouldAddError_throw_an_IllegalArgumentException_for_empty_input() { validationStatus.addError(null, null); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/BusinessObjectModelValidationRuleTest.java ================================================ /** * 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.bdm.validator.rule; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat; import java.util.List; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.UniqueConstraint; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.RelationField; import org.bonitasoft.engine.bdm.model.field.RelationField.FetchType; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.junit.Before; import org.junit.Test; /** * @author Romain Bioteau */ public class BusinessObjectModelValidationRuleTest { private BusinessObjectModelValidationRule businessObjectModelValidationRule; @Before public void setUp() { businessObjectModelValidationRule = new BusinessObjectModelValidationRule(); } @Test public void should_apply_to_businessObjectModel() { assertThat(businessObjectModelValidationRule.appliesTo(new BusinessObject())).isFalse(); assertThat(businessObjectModelValidationRule.appliesTo(new SimpleField())).isFalse(); assertThat(businessObjectModelValidationRule.appliesTo(new UniqueConstraint())).isFalse(); assertThat(businessObjectModelValidationRule.appliesTo(new BusinessObjectModel())).isTrue(); } @Test(expected = IllegalArgumentException.class) public void shouddCheckRule_throw_IllegalArgumentException() { businessObjectModelValidationRule.checkRule(new SimpleField()); } @Test public void should_validate_that_bom_has_at_least_one_businessObject() { final BusinessObjectModel bom = new BusinessObjectModel(); final ValidationStatus validationStatus = businessObjectModelValidationRule.validate(bom); assertThat(validationStatus).isNotOk(); } @Test public void should_return_a_valid_status_when_bom_is_valid() { final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(new BusinessObject()); final ValidationStatus validationStatus = businessObjectModelValidationRule.validate(bom); assertThat(validationStatus).isOk(); } @Test public void should_return_a_error_status_when_bom_contains_invalid_query_names() { final BusinessObjectModel bom = new BusinessObjectModel(); final BusinessObject employee = new BusinessObject(); employee.setQualifiedName("Employee"); final BusinessObject address = new BusinessObject(); address.setQualifiedName("Address"); final SimpleField street = new SimpleField(); street.setName("street"); street.setType(FieldType.STRING); address.addField(street); final RelationField addresses = new RelationField(); addresses.setName("addresses"); addresses.setCollection(true); addresses.setFetchType(FetchType.LAZY); addresses.setReference(address); employee.addField(addresses); address.addQuery("findAddressesByEmployeePersistenceId", "", List.class.getName());//Duplicated query name bom.addBusinessObject(employee); bom.addBusinessObject(address); final ValidationStatus validationStatus = businessObjectModelValidationRule.validate(bom); assertThat(validationStatus).isNotOk(); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/BusinessObjectValidationRuleTest.java ================================================ /** * 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.bdm.validator.rule; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO; import static org.bonitasoft.engine.bdm.builder.FieldBuilder.aBooleanField; import static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat; import java.util.List; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.UniqueConstraint; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.junit.Before; import org.junit.Test; /** * @author Romain Bioteau */ public class BusinessObjectValidationRuleTest { private BusinessObjectValidationRule businessObjectValidationRule; @Before public void setUp() { businessObjectValidationRule = new BusinessObjectValidationRule(); } @Test public void should_apply_to_businessObject() { assertThat(businessObjectValidationRule.appliesTo(new BusinessObjectModel())).isFalse(); assertThat(businessObjectValidationRule.appliesTo(new SimpleField())).isFalse(); assertThat(businessObjectValidationRule.appliesTo(new UniqueConstraint())).isFalse(); assertThat(businessObjectValidationRule.appliesTo(new BusinessObject())).isTrue(); } @Test(expected = IllegalArgumentException.class) public void shouddCheckRule_throw_IllegalArgumentException() { businessObjectValidationRule.checkRule(new SimpleField()); } @Test public void should_validate_that_qualified_name_is_not_null() { final BusinessObject bo = new BusinessObject(); bo.setQualifiedName(null); final ValidationStatus validationStatus = businessObjectValidationRule.validate(bo); assertThat(validationStatus).isNotOk(); } @Test public void should_validate_that_qualified_name_is_not_starting_by_org_bonitasoft() throws Exception { checkQualifiedNameValidationStatus("com.bonitasoft.Forbidden", false); checkQualifiedNameValidationStatus("com.bonitasoftextended", true); checkQualifiedNameValidationStatus("org.com.bonitasoft.model", true); checkQualifiedNameValidationStatus("org.bonitasoft.model", false); checkQualifiedNameValidationStatus("org.bonitasoftextended", true); } private void checkQualifiedNameValidationStatus(final String qualifiedName, final boolean expectedValidation) { // given final BusinessObject bo = aValidBusinesObject(); bo.setQualifiedName(qualifiedName); // when final ValidationStatus validationStatus = businessObjectValidationRule.validate(bo); // then if (expectedValidation) { assertThat(validationStatus).isOk("should valid business object with qualified name:" + qualifiedName); } else { assertThat(validationStatus) .isNotOk("should not valid business object with qualified name:" + qualifiedName); } } @Test public void shouldCheckRule_returns_valid_status() { final BusinessObject bo = aValidBusinesObject(); ValidationStatus validationStatus = businessObjectValidationRule.validate(bo); assertThat(validationStatus).isOk(); bo.addUniqueConstraint("_UC_1", "firstName"); validationStatus = businessObjectValidationRule.validate(bo); assertThat(validationStatus).isOk(); } private BusinessObject aValidBusinesObject() { final BusinessObject bo = new BusinessObject(); bo.setQualifiedName("org.bonita.Bo"); final SimpleField field = new SimpleField(); field.setName("firstName"); bo.addField(field); return bo; } @Test public void shoudCheckRule_returns_error_status() { final BusinessObject bo = new BusinessObject(); bo.setQualifiedName("org.bonita.Bo2"); ValidationStatus validationStatus = businessObjectValidationRule.validate(bo); assertThat(validationStatus).isNotOk(); bo.setQualifiedName("org.bonita.Bo 2"); final SimpleField field = new SimpleField(); field.setName("firstName"); bo.addField(field); validationStatus = businessObjectValidationRule.validate(bo); assertThat(validationStatus).isNotOk(); bo.setQualifiedName("org.bonita.Bo2"); bo.addUniqueConstraint("_UC_1", "dontExists"); validationStatus = businessObjectValidationRule.validate(bo); assertThat(validationStatus).isNotOk(); } @Test public void shoudCheckRule_returns_error_status_for_duplicated_query_name() { final BusinessObject bo = new BusinessObject(); bo.setQualifiedName("org.bonita.Bo2"); final SimpleField field = new SimpleField(); field.setName("firstName"); bo.addField(field); bo.addQuery("toto", "titi", List.class.getName()); bo.addQuery("toto", "titi", List.class.getName()); final ValidationStatus validationStatus = businessObjectValidationRule.validate(bo); assertThat(validationStatus).isNotOk(); } @Test public void shoudCheckRule_returns_error_status_for_duplicated_constraint_name() { final BusinessObject bo = new BusinessObject(); bo.setQualifiedName("org.bonita.Bo2"); final SimpleField field = new SimpleField(); field.setName("firstName"); bo.addField(field); bo.addUniqueConstraint("toto", "firstName"); bo.addUniqueConstraint("toto", "firstName"); final ValidationStatus validationStatus = businessObjectValidationRule.validate(bo); assertThat(validationStatus).isNotOk(); } @Test public void should_validate_that_simple_name_contain_no_underscore() { final BusinessObject businessObject = aBO("Name_withUnderscore").withField(aBooleanField("field")).build(); final ValidationStatus validationStatus = businessObjectValidationRule.validate(businessObject); assertThat(validationStatus).isNotOk(); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/FieldValidationRuleTest.java ================================================ /** * 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.bdm.validator.rule; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.UniqueConstraint; import org.bonitasoft.engine.bdm.model.field.Field; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.junit.Before; import org.junit.Test; /** * @author Romain Bioteau */ public class FieldValidationRuleTest { private FieldValidationRule fieldValidationRule; @Before public void setUp() { fieldValidationRule = new FieldValidationRule(); } private Field aFieldWithName(String name) { FakeField field = new FakeField(); field.setName(name); return field; } @Test public void should_apply_to_fields() { assertThat(fieldValidationRule.appliesTo(new BusinessObjectModel())).isFalse(); assertThat(fieldValidationRule.appliesTo(new BusinessObject())).isFalse(); assertThat(fieldValidationRule.appliesTo(new UniqueConstraint())).isFalse(); assertThat(fieldValidationRule.appliesTo(aFieldWithName("aName"))).isTrue(); } @Test(expected = IllegalArgumentException.class) public void shouldCheckRule_throw_IllegalArgumentException() { fieldValidationRule.checkRule(new BusinessObject()); } @Test public void should_return_a_valid_status_when_name_is_not_a_forbidden_one() { Field field = aFieldWithName("aGoodName"); ValidationStatus validationStatus = fieldValidationRule.validate(field); assertThat(validationStatus).isOk(); } @Test public void should_validate_that_name_is_not_empty() { Field field = aFieldWithName(""); ValidationStatus validationStatus = fieldValidationRule.validate(field); assertThat(validationStatus).isNotOk(); } @Test public void should_validate_that_name_is_not_null() { Field field = aFieldWithName(null); ValidationStatus validationStatus = fieldValidationRule.validate(field); assertThat(validationStatus).isNotOk(); } @Test public void should_validate_that_name_has_no_whitespace() { Field field = aFieldWithName("with whitespaces "); ValidationStatus validationStatus = fieldValidationRule.validate(field); assertThat(validationStatus).isNotOk(); } @Test public void should_validate_that_name_is_not_a_java_keyword() { Field field = aFieldWithName("import"); ValidationStatus validationStatus = fieldValidationRule.validate(field); assertThat(validationStatus).isNotOk(); } @Test public void should_validate_that_name_is_not_persistenceId_wich_is_a_business_data_model_keyword() { Field field = aFieldWithName(Field.PERSISTENCE_ID.toUpperCase()); ValidationStatus validationStatus = fieldValidationRule.validate(field); assertThat(validationStatus).isNotOk(); } @Test public void should_validate_that_name_is_not_persistenceVersion_wich_is_a_business_data_model_keyword() { Field field = aFieldWithName(Field.PERSISTENCE_VERSION.toLowerCase()); ValidationStatus validationStatus = fieldValidationRule.validate(field); assertThat(validationStatus).isNotOk(); } /** * Fake field used for tests. Extending abstract class Field with no extras attributes * * @author Colin PUY */ private class FakeField extends Field { } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/IndexValidationRuleTest.java ================================================ /** * 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.bdm.validator.rule; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bdm.builder.IndexBuilder.anIndex; import org.bonitasoft.engine.bdm.model.Index; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.junit.Test; public class IndexValidationRuleTest { @Test public void should_add_an_error_message_when_index_fields_are_null() throws Exception { final Index index = anIndex().withName("nameIndex").build(); ValidationStatus status = new IndexValidationRule().checkRule(index); assertThat(status.getErrors()).contains("nameIndex index must have at least one field declared"); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/MultipleAggregationToItselfValidationRuleTest.java ================================================ /** * 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.bdm.validator.rule; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO; import static org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder.aBOM; import static org.bonitasoft.engine.bdm.model.field.RelationField.FetchType.EAGER; import org.bonitasoft.engine.bdm.builder.FieldBuilder; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.junit.Before; import org.junit.Test; /** * @author Danila Mazour */ public class MultipleAggregationToItselfValidationRuleTest { private MultipleAggregationToItselfValidationRule rule; @Before public void setUp() { rule = new MultipleAggregationToItselfValidationRule(); } @Test public void shouldDetectMultipleAggregationToItself() { BusinessObject daughter = aBO("daughter").build(); daughter = aBO("daughter") .withField(new FieldBuilder.RelationFieldBuilder().withName("daughter").aggregation().multiple() .referencing(daughter).fetchType( EAGER)) .build(); final BusinessObjectModel bom = aBOM().withBOs(daughter).build(); final ValidationStatus validationStatus = rule.validate(bom); assertThat(validationStatus.isOk()).isFalse(); assertThat(validationStatus.getErrors()).hasSize(1); assertThat(validationStatus.getWarnings()).hasSize(0); assertThat(validationStatus.getErrors().get(0)) .isEqualTo("The object daughter is referencing itself in a multiple aggregation relation."); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/QueryParameterValidationRuleTest.java ================================================ /** * 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.bdm.validator.rule; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.bdm.model.QueryParameter; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.junit.Test; /** * @author Emmanuel Duchastenier */ public class QueryParameterValidationRuleTest { @Test public void checkRuleShouldForbidStartIndexAsQueryParameterName() { QueryParameter queryParam = new QueryParameter("startIndex", Object.class.getName()); ValidationStatus status = new QueryParameterValidationRule().validate(queryParam); assertThat(status.getErrors().get(0)).contains("is a reserved parameter name"); } @Test public void checkRuleShouldForbidMaxResultsAsQueryParameterName() { QueryParameter queryParam = new QueryParameter("maxResults", Object.class.getName()); ValidationStatus status = new QueryParameterValidationRule().validate(queryParam); assertThat(status.getErrors()).hasSize(1); assertThat(status.getErrors().get(0)).contains("is a reserved parameter name"); } @Test public void aQueryParameterNameShouldHaveAName() { QueryParameter queryParam = new QueryParameter(); ValidationStatus status = new QueryParameterValidationRule().validate(queryParam); assertThat(status.getErrors().get(0)).contains("must have name"); } @Test public void aQueryParameterNameShouldHaveANonEmptyName() { QueryParameter queryParam = new QueryParameter("", Object.class.getName()); ValidationStatus status = new QueryParameterValidationRule().validate(queryParam); assertThat(status.getErrors().get(0)).contains("must have name"); } @Test public void aQueryParameterNameShouldHaveAVAlidJavaIdentifierName() { QueryParameter queryParam = new QueryParameter("1_manu", Object.class.getName()); ValidationStatus status = new QueryParameterValidationRule().validate(queryParam); assertThat(status.getErrors().get(0)).contains("is not a valid Java identifier"); } @Test public void aQueryParameterClassNameShouldHaveAName() { QueryParameter queryParam = new QueryParameter("aValidParamName", null); ValidationStatus status = new QueryParameterValidationRule().validate(queryParam); assertThat(status.getErrors().get(0)).contains("query parameter must have a classname"); } @Test public void aQueryParameterClassNameShouldHaveANonEmptyName() { QueryParameter queryParam = new QueryParameter("aValidParamName", ""); ValidationStatus status = new QueryParameterValidationRule().validate(queryParam); assertThat(status.getErrors().get(0)).contains("query parameter must have a classname"); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/QueryValidationRuleTest.java ================================================ /** * 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.bdm.validator.rule; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat; import java.util.List; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.Query; import org.bonitasoft.engine.bdm.model.UniqueConstraint; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.junit.Before; import org.junit.Test; /** * @author Romain Bioteau */ public class QueryValidationRuleTest { private QueryValidationRule queryValidationRule; /** * @throws java.lang.Exception */ @Before public void setUp() { queryValidationRule = new QueryValidationRule(); } @Test public void shouldAppliesTo_UniqueConstraint() { assertThat(queryValidationRule.appliesTo(new BusinessObjectModel())).isFalse(); assertThat(queryValidationRule.appliesTo(new BusinessObject())).isFalse(); assertThat(queryValidationRule.appliesTo(new SimpleField())).isFalse(); assertThat(queryValidationRule.appliesTo(new UniqueConstraint())).isFalse(); assertThat(queryValidationRule.appliesTo(new Query())).isTrue(); } @Test(expected = IllegalArgumentException.class) public void shouldCheckRule_throw_IllegalArgumentException() { queryValidationRule.checkRule(new BusinessObject()); } @Test public void should_validate_that_query_name_is_a_valid_java_identifier() { Query query = new Query(); query.setName(" a not valid java identifier"); ValidationStatus validationStatus = queryValidationRule.validate(query); assertThat(validationStatus).isNotOk(); } @Test public void shouldCheckRule_return_valid_status() { Query q = new Query("findByName", "Select toto where titi = toto", List.class.getName()); ValidationStatus checkRule = queryValidationRule.validate(q); assertThat(checkRule.isOk()).isTrue(); } @Test public void shouldCheckRule_return_error_status_if_no_name() { Query q = new Query("", "Select toto where titi = toto", List.class.getName()); ValidationStatus checkRule = queryValidationRule.validate(q); assertThat(checkRule.isOk()).isFalse(); q = new Query(null, "Select toto where titi = toto", List.class.getName()); checkRule = queryValidationRule.validate(q); assertThat(checkRule.isOk()).isFalse(); } @Test public void shouldCheckRule_return_error_status_if_name_too_long() { Query q = new Query( "dsfhsdjkhfdjskfhjksdhfjksdhfjkshfjksdhfjksdhfjksdhfjksdhfjkdsfhjkdshfjjkdshfjskdhfjksdhfdjskhfhfjkhsdfkjsdhfksduzeiryzueiryuzieryuigsdhjgfhjgriuzegrjkg", "Select toto where titi = toto", List.class.getName()); ValidationStatus checkRule = queryValidationRule.validate(q); assertThat(checkRule.isOk()).isFalse(); } @Test public void shouldCheckRule_return_error_status_if_no_content() { Query q = new Query("toto", "", List.class.getName()); ValidationStatus checkRule = queryValidationRule.validate(q); assertThat(checkRule.isOk()).isFalse(); q = new Query("toto", null, List.class.getName()); checkRule = queryValidationRule.validate(q); assertThat(checkRule.isOk()).isFalse(); } @Test public void shouldCheckRule_return_error_status_if_no_returnType() { Query q = new Query("toto", "select", ""); ValidationStatus checkRule = queryValidationRule.validate(q); assertThat(checkRule.isOk()).isFalse(); q = new Query("toto", "select", ""); checkRule = queryValidationRule.validate(q); assertThat(checkRule.isOk()).isFalse(); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/SimpleFieldValidationRuleTest.java ================================================ /** * 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.bdm.validator.rule; import static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.junit.Before; import org.junit.Test; /** * @author Colin PUY */ public class SimpleFieldValidationRuleTest { private SimpleFieldValidationRule simpleFieldValidationRule; @Before public void setUp() { simpleFieldValidationRule = new SimpleFieldValidationRule(); } @Test public void should_validate_that_type_is_not_empty() { SimpleField simpleField = new SimpleField(); ValidationStatus validationStatus = simpleFieldValidationRule.validate(simpleField); assertThat(validationStatus).isNotOk(); } @Test public void should_return_a_valid_status_when_type_is_filled() { SimpleField simpleField = new SimpleField(); simpleField.setType(FieldType.BOOLEAN); ValidationStatus validationStatus = simpleFieldValidationRule.validate(simpleField); assertThat(validationStatus).isOk(); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/UniqueConstraintValidationRuleTest.java ================================================ /** * 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.bdm.validator.rule; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat; import java.util.Arrays; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.UniqueConstraint; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.junit.Before; import org.junit.Test; /** * @author Romain Bioteau */ public class UniqueConstraintValidationRuleTest { private UniqueConstraintValidationRule uniqueConstraintValidationRule; @Before public void setUp() { uniqueConstraintValidationRule = new UniqueConstraintValidationRule(); } @Test public void shoudAppliesTo_UniqueConstraint() { assertThat(uniqueConstraintValidationRule.appliesTo(new BusinessObjectModel())).isFalse(); assertThat(uniqueConstraintValidationRule.appliesTo(new BusinessObject())).isFalse(); assertThat(uniqueConstraintValidationRule.appliesTo(new SimpleField())).isFalse(); assertThat(uniqueConstraintValidationRule.appliesTo(new UniqueConstraint())).isTrue(); } @Test(expected = IllegalArgumentException.class) public void shouddCheckRule_throw_IllegalArgumentException() { uniqueConstraintValidationRule.checkRule(new BusinessObject()); } @Test public void should_return_a_valid_status() { UniqueConstraint uc = new UniqueConstraint(); uc.setName("MY_CONSTRAINT_"); uc.setFieldNames(Arrays.asList("f1")); ValidationStatus validationStatus = uniqueConstraintValidationRule.validate(uc); assertThat(validationStatus).isOk(); } @Test public void should_add_an_error_message_when_constraint_name_is_null() throws Exception { UniqueConstraint uc = new UniqueConstraint(); uc.setName(null); uc.setFieldNames(Arrays.asList("f1")); ValidationStatus validationStatus = uniqueConstraintValidationRule.validate(uc); assertThat(validationStatus).isNotOk().hasError("A unique constraint must have name"); } @Test public void should_add_an_error_message_when_constraint_name_contains_whitespaces() throws Exception { UniqueConstraint uc = new UniqueConstraint(); uc.setName("with whitespaces"); uc.setFieldNames(Arrays.asList("f1")); ValidationStatus validationStatus = uniqueConstraintValidationRule.validate(uc); assertThat(validationStatus).isNotOk().hasError("with whitespaces is not a valid SQL identifier"); } @Test public void should_add_an_error_message_when_constraint_name_is_empty() throws Exception { UniqueConstraint uc = new UniqueConstraint(); uc.setName(""); uc.setFieldNames(Arrays.asList("f1")); ValidationStatus validationStatus = uniqueConstraintValidationRule.validate(uc); assertThat(validationStatus).isNotOk().hasError("A unique constraint must have name"); } @Test public void should_add_an_error_message_when_constraint_fields_are_null() throws Exception { UniqueConstraint uc = new UniqueConstraint(); uc.setName("MY_CONSTRAINT_"); uc.setFieldNames(null); ValidationStatus validationStatus = uniqueConstraintValidationRule.validate(uc); assertThat(validationStatus).isNotOk() .hasError("MY_CONSTRAINT_ unique constraint must have at least one field declared"); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/UniqueNameValidationRuleTest.java ================================================ /** * 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.bdm.validator.rule; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO; import static org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder.aBOM; import static org.bonitasoft.engine.bdm.builder.IndexBuilder.anIndex; import static org.bonitasoft.engine.bdm.builder.UniqueConstraintBuilder.aUniqueConstraint; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Collection; import org.bonitasoft.engine.api.result.StatusCode; import org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.Index; import org.bonitasoft.engine.bdm.model.UniqueConstraint; import org.bonitasoft.engine.bdm.validator.UniqueNameValidator; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Colin PUY */ @RunWith(MockitoJUnitRunner.class) public class UniqueNameValidationRuleTest { @Mock private UniqueNameValidator uniqueNameValidator; private UniqueNameValidationRule validationRule; @Before @SuppressWarnings("unchecked") public void initValidationRule() { when(uniqueNameValidator.validate(any(Collection.class), any(String.class))).thenReturn(new ValidationStatus()); validationRule = new UniqueNameValidationRule(uniqueNameValidator); } private BusinessObject aBoWithConstraints(final UniqueConstraint... uniqueConstraints) { BusinessObjectBuilder boBuilder = aBO("aBoWithConstraints"); for (final UniqueConstraint uniqueConstraint : uniqueConstraints) { boBuilder = boBuilder.withUniqueConstraint(uniqueConstraint); } return boBuilder.build(); } private BusinessObject aBoWithIndexes(final Index... indexes) { BusinessObjectBuilder bo = aBO("aBoWithIndexes"); for (final Index index : indexes) { bo = bo.withIndex(index); } return bo.build(); } private ValidationStatus anErrorStatus() { final ValidationStatus validationStatus = new ValidationStatus(); validationStatus.addError(StatusCode.DUPLICATE_CONSTRAINT_OR_INDEX_NAME, "an error"); return validationStatus; } @Test public void should_validate_names_unicity_for_unique_constraints() { final UniqueConstraint uniqueConstraint = aUniqueConstraint().withName("aUniqueConstraint").build(); final UniqueConstraint uniqueConstraint2 = aUniqueConstraint().withName("anotherUniqueConstraint").build(); final BusinessObject bo = aBoWithConstraints(uniqueConstraint, uniqueConstraint2); validationRule.checkRule(aBOM().withBO(bo).build()); verify(uniqueNameValidator).validate(asList(uniqueConstraint, uniqueConstraint2), "unique contraints"); } @Test public void should_validate_names_unicity_for_indexes() { final Index index = anIndex().withName("anIndex").build(); final Index anotherIndex = anIndex().withName("anotherIndex").build(); final BusinessObjectModel bom = aBOM().withBO(aBoWithIndexes(index, anotherIndex)).build(); validationRule.checkRule(bom); verify(uniqueNameValidator).validate(asList(index, anotherIndex), "indexes"); } @Test public void should_concatenate_validation_errors() { final Index index = anIndex().withName("index").build(); final UniqueConstraint uniqueConstraint = aUniqueConstraint().withName("constraint").build(); final BusinessObjectModel bom = aBOM() .withBO(aBO("bo").withIndex(index).withUniqueConstraint(uniqueConstraint).build()).build(); when(uniqueNameValidator.validate(eq(asList(index)), anyString())).thenReturn(anErrorStatus()); when(uniqueNameValidator.validate(eq(asList(uniqueConstraint)), anyString())).thenReturn(anErrorStatus()); final ValidationStatus checkRule = validationRule.checkRule(bom); assertThat(checkRule.getErrors()).hasSize(2); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/UniqueSimpleNameValidationRuleTest.java ================================================ /** * 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.bdm.validator.rule; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.api.result.Status.Level.ERROR; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.junit.Before; import org.junit.Test; /** * @author Danila Mazour */ public class UniqueSimpleNameValidationRuleTest { private UniqueSimpleNameValidationRule uniqueSimpleNameValidationRule; private BusinessObject customerPackageA; private BusinessObject customerPackageB; private BusinessObject customerUpperCase; private BusinessObject client; @Before public void setUp() { uniqueSimpleNameValidationRule = new UniqueSimpleNameValidationRule(); createObjects(); } private void createObjects() { customerPackageA = new BusinessObject(); customerPackageB = new BusinessObject(); customerUpperCase = new BusinessObject(); client = new BusinessObject(); customerUpperCase.setQualifiedName("net.company.model.CUSTOMER"); customerPackageA.setQualifiedName("com.company.model.Customer"); customerPackageB.setQualifiedName("net.company.model.Customer"); client.setQualifiedName("org.company.model.Client"); } @Test public void should_return_error_when_same_business_object_name() { //given BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(customerPackageA); bom.addBusinessObject(customerPackageB); //when ValidationStatus validationStatus = uniqueSimpleNameValidationRule.validate(bom); //then assertThat(validationStatus.getStatuses().size()).isEqualTo(1); assertThat(validationStatus.getStatuses().get(0).getLevel()).isEqualTo(ERROR); } @Test public void should_return_error_when_same_business_object_name_but_upperCase() { //given BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(customerPackageA); bom.addBusinessObject(customerUpperCase); //when ValidationStatus validationStatus = uniqueSimpleNameValidationRule.validate(bom); //then assertThat(validationStatus.getStatuses().size()).isEqualTo(1); assertThat(validationStatus.getStatuses().get(0).getLevel()).isEqualTo(ERROR); } @Test public void should_return_a_valid_status_when_no_identical_object_name() { //given BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(customerPackageA); bom.addBusinessObject(client); //when ValidationStatus validationStatus = uniqueSimpleNameValidationRule.validate(bom); //then assertThat(validationStatus.getStatuses()).isEmpty(); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/ValidationRuleTest.java ================================================ /** * 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.bdm.validator.rule; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.junit.Test; /** * @author Colin PUY */ public class ValidationRuleTest { @Test public void should_apply_to_type_parameter_class() { ExceptionRule objectRule = new ExceptionRule(); boolean apply = objectRule.appliesTo(new Exception()); assertThat(apply).isTrue(); } @Test public void should_apply_to_type_parameter_subclass() { ExceptionRule objectRule = new ExceptionRule(); boolean apply = objectRule.appliesTo(new RuntimeException()); assertThat(apply).isTrue(); } @Test public void should_not_apply_to_other_type() { ExceptionRule objectRule = new ExceptionRule(); boolean apply = objectRule.appliesTo(new Throwable()); assertThat(apply).isFalse(); } @Test public void should_not_apply_to_null() { ExceptionRule objectRule = new ExceptionRule(); boolean apply = objectRule.appliesTo(null); assertThat(apply).isFalse(); } @Test(expected = IllegalArgumentException.class) public void should_throw_exception_when_trying_to_check_rule_on_object_that_rule_cannot_apply_on() { ExceptionRule objectRule = new ExceptionRule(); objectRule.checkRule(new String()); } @Test public void should_validate_object_according_to_the_implemented_validation_strategy() { ValidationStatus expectedValidationStatus = new ValidationStatus(); ExceptionRule objectRule = new ExceptionRule(expectedValidationStatus); ValidationStatus status = objectRule.checkRule(new Exception()); assertThat(status).isEqualTo(expectedValidationStatus); } /** * ValidationRule test implementation - return the given validationStatus * * @author Colin PUY */ private class ExceptionRule extends ValidationRule { private ValidationStatus validationStatus; public ExceptionRule() { super(Exception.class); } public ExceptionRule(ValidationStatus validationStatus) { this(); this.validationStatus = validationStatus; } @Override protected ValidationStatus validate(Exception modelElement) { return validationStatus; } } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/composition/AggregationAndCompositionValidationRuleTest.java ================================================ /** * 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.bdm.validator.rule.composition; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO; import static org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder.aBOM; import static org.bonitasoft.engine.bdm.builder.FieldBuilder.aCompositionField; import static org.bonitasoft.engine.bdm.builder.FieldBuilder.anAggregationField; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.junit.Before; import org.junit.Test; /** * @author Danila Mazour */ public class AggregationAndCompositionValidationRuleTest { private AggregationAndCompositionValidationRule rule; @Before public void setUp() { rule = new AggregationAndCompositionValidationRule(); } @Test public void shouldDetectAggregationAndComposition() { final BusinessObject daughter = aBO("daughter").build(); final BusinessObject mother = aBO("mother").withField(aCompositionField("daughter", daughter)).build(); final BusinessObject grandMother = aBO("grandMother").withField(anAggregationField("daughter", daughter)) .build(); final BusinessObjectModel bom = aBOM().withBOs(grandMother, mother, daughter).build(); final ValidationStatus validationStatus = rule.validate(bom); assertThat(validationStatus.isOk()).isTrue(); assertThat(validationStatus.getErrors()).isEmpty(); assertThat(validationStatus.getWarnings().size()).isEqualTo(1); assertThat(validationStatus.getWarnings().get(0)).isEqualTo( "The object daughter is referenced both in composition and in aggregation. This may lead to runtime errors and may lead to unpredictable behaviour of the AccessControl configuration."); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/composition/CyclicCompositionValidationRuleTest.java ================================================ /** * 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.bdm.validator.rule.composition; import static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO; import static org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder.aBOM; import static org.bonitasoft.engine.bdm.builder.FieldBuilder.aCompositionField; import static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.junit.Before; import org.junit.Test; /** * @author Colin PUY */ public class CyclicCompositionValidationRuleTest { private CyclicCompositionValidationRule rule; @Before public void initRule() { rule = new CyclicCompositionValidationRule(); } @Test public void should_validate_that_a_composite_object_cannot_have_one_of_its_ancestor_as_a_child() { final BusinessObject daughter = aBO("daughter").build(); final BusinessObject mother = aBO("mother").withField(aCompositionField("daughter", daughter)).build(); final BusinessObject grandMother = aBO("grandMother").withField(aCompositionField("mother", mother)).build(); daughter.addField(aCompositionField("forbiddenChild", grandMother)); final BusinessObjectModel bom = aBOM().withBOs(grandMother, mother, daughter).build(); final ValidationStatus validationStatus = rule.validate(bom); assertThat(validationStatus).isNotOk(); } @Test public void should_validate_that_a_bo_cannot_compose_itself() { final BusinessObject daughter = aBO("daughter").build(); daughter.addField(aCompositionField("toto", daughter)); final BusinessObjectModel bom = aBOM().withBOs(daughter).build(); final ValidationStatus validationStatus = rule.validate(bom); assertThat(validationStatus).isNotOk(); } @Test public void should_validate_that_a_bom_with_the_same_bo_composed_two_times_is_invalid() { //given final BusinessObject wheel = aBO("wheel").build(); final BusinessObject bycicle = aBO("bicycle").withField(aCompositionField("frontwheel", wheel)) .withField(aCompositionField("backwheel", wheel)).build(); final BusinessObjectModel bom = aBOM().withBOs(wheel, bycicle).build(); //when ValidationStatus validationStatus = rule.validate(bom); //then assertThat(validationStatus).isNotOk(); assertThat(validationStatus).hasError("Business object " + "wheel" + " has a circular composition reference to itself or is referenced several times in the object " + "bicycle"); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bdm/validator/rule/composition/UniquenessCompositionValidationRuleTest.java ================================================ /** * 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.bdm.validator.rule.composition; import static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO; import static org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder.aBOM; import static org.bonitasoft.engine.bdm.builder.FieldBuilder.aCompositionField; import static org.bonitasoft.engine.bdm.builder.FieldBuilder.anAggregationField; import static org.bonitasoft.engine.bdm.validator.assertion.ValidationStatusAssert.assertThat; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.junit.Before; import org.junit.Test; /** * @author Colin PUY */ public class UniquenessCompositionValidationRuleTest { private UniquenessCompositionValidationRule rule; @Before public void initRule() { rule = new UniquenessCompositionValidationRule(); } @Test public void should_validate_that_a_bom_with_no_relation_fields_is_valid() { final BusinessObjectModel bom = aBOM().withBO(aBO("aBo").build()).build(); final ValidationStatus validationStatus = rule.validate(bom); assertThat(validationStatus).isOk(); } @Test public void should_validate_that_a_bom_with_no_composition_is_valid() { final BusinessObject aggregated = aBO("aggregated").build(); final BusinessObject bo = aBO("aBo").withField(anAggregationField("aggreg", aggregated)).build(); final BusinessObjectModel bom = aBOM().withBOs(bo, aggregated).build(); final ValidationStatus validationStatus = rule.validate(bom); assertThat(validationStatus).isOk(); } @Test public void should_validate_that_a_bom_with_composed_bo_in_only_one_bo_is_valid() { final BusinessObject composite = aBO("composite").build(); final BusinessObject bo = aBO("aBo").withField(aCompositionField("composite", composite)).build(); final BusinessObject composite2 = aBO("composite2").build(); final BusinessObject bo2 = aBO("aBo2").withField(aCompositionField("composite2", composite2)).build(); final BusinessObjectModel bom = aBOM().withBOs(bo, composite, bo2, composite2).build(); final ValidationStatus validationStatus = rule.validate(bom); assertThat(validationStatus).isOk(); } @Test public void should_validate_that_a_bom_with_a_bo_composed_in_two_bos_is_invalid() { final BusinessObject composite = aBO("composite").build(); final BusinessObject firstBO = aBO("firstBO").withField(aCompositionField("boOneComposite", composite)).build(); final BusinessObject secondBO = aBO("secondBO").withField(aCompositionField("boTwoComposite", composite)) .build(); final BusinessObjectModel bom = aBOM().withBOs(firstBO, secondBO, composite).build(); final ValidationStatus validationStatus = rule.validate(bom); assertThat(validationStatus).isNotOk(); } @Test public void should_validate_that_a_bom_with_the_same_bo_composed_two_times_is_invalid() { //given final BusinessObject wheel = aBO("wheel").build(); final BusinessObject bycicle = aBO("bicycle").withField(aCompositionField("frontwheel", wheel)) .withField(aCompositionField("backwheel", wheel)).build(); final BusinessObjectModel bom = aBOM().withBOs(wheel, bycicle).build(); //when ValidationStatus validationStatus = rule.validate(bom); //then assertThat(validationStatus).isNotOk(); assertThat(validationStatus).hasError("Business object " + "wheel" + " is referenced by composition in two business objects, or is referenced several times in a single business object"); assertThat(validationStatus).hasErrorSize(1); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/bpm/document/DocumentValueTest.java ================================================ /** * 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.bpm.document; import static org.junit.Assert.assertEquals; import org.junit.Test; /** * @author Celine Souchet * @version 6.4.2 * @since 6.4.2 */ public class DocumentValueTest { /** * Test method for * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(byte[], java.lang.String, java.lang.String)}. */ @Test(expected = IllegalArgumentException.class) public final void cant_construct_DocumentValue_with_content_and_mimeType_without_file_name() { new DocumentValue("content".getBytes(), "mimeType", null); } /** * Test method for * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(byte[], java.lang.String, java.lang.String)}. */ @Test(expected = IllegalArgumentException.class) public final void cant_construct_DocumentValue_with_content_and_mimeType_with_empty_file_name() { new DocumentValue("content".getBytes(), "mimeType", ""); } /** * Test method for * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(byte[], java.lang.String, java.lang.String)}. */ @Test public final void should_be_able_to_construct_DocumentValue_without_content_but_with_mimeType_and_file_name() { new DocumentValue(null, "mimeType", "filename"); } /** * Test method for * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(byte[], java.lang.String, java.lang.String)}. */ @Test public final void should_be_able_to_construct_DocumentValue_with_empty_content_with_mimeType_and_file_name() { new DocumentValue("".getBytes(), "mimeType", "filename"); } /** * Test method for * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(byte[], java.lang.String, java.lang.String)}. */ @Test public final void can_construct_DocumentValue_with_content_and_mimeType_and_file_name() { new DocumentValue("tyjiy".getBytes(), "mimeType", "filename"); } /** * Test method for * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(long, byte[], java.lang.String, java.lang.String)}. */ @Test(expected = IllegalArgumentException.class) public final void cant_construct_DocumentValue_with_documentId_and_content_and_mimeType_without_file_name() { new DocumentValue(1, "content".getBytes(), "mimeType", null); } /** * Test method for * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(long, byte[], java.lang.String, java.lang.String)}. */ @Test(expected = IllegalArgumentException.class) public final void cant_construct_DocumentValue_with_documentId_and_content_and_mimeType_with_empty_file_name() { new DocumentValue(1, "content".getBytes(), "mimeType", ""); } /** * Test method for * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(long, byte[], java.lang.String, java.lang.String)}. */ @Test public final void should_be_able_to_construct_DocumentValue_without_content_with_documentId_and_mimeType_and_file_name() { new DocumentValue(1, null, "mimeType", "filename"); } /** * Test method for * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(long, byte[], java.lang.String, java.lang.String)}. */ @Test public final void should_be_able_to_construct_DocumentValue_with_empty_content_with_documentId_and_mimeType_and_file_name() { new DocumentValue(1, "".getBytes(), "mimeType", "filename"); } /** * Test method for * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(long, byte[], java.lang.String, java.lang.String)}. */ @Test public final void can_construct_DocumentValue_with_content_and_documentId_and_mimeType_and_file_name() { new DocumentValue(1, "plop".getBytes(), "mimeType", "filename"); } /** * Test method for {@link org.bonitasoft.engine.bpm.document.DocumentValue#setContent(byte[])}. */ @Test(expected = IllegalArgumentException.class) public final void setContent_should_throw_exception_if_filename_is_empty_and_content_not() { // Given final DocumentValue documentValue = new DocumentValue(2); // When documentValue.setContent("plop".getBytes()); } /** * Test method for {@link org.bonitasoft.engine.bpm.document.DocumentValue#setContent(byte[])}. */ @Test(expected = IllegalArgumentException.class) public final void setContent_should_throw_exception_if_filename_is_null_and_content_not() { // Given final DocumentValue documentValue = new DocumentValue(2); // When documentValue.setContent("plop".getBytes()); } /** * Test method for {@link org.bonitasoft.engine.bpm.document.DocumentValue#setContent(byte[])}. */ @Test public final void setContent_should_set_content_with_filename_and_content() { // Given final DocumentValue documentValue = new DocumentValue("yujyt".getBytes(), "mimeType", "filename"); final byte[] content = "plop".getBytes(); // When documentValue.setContent(content); // Then assertEquals(content, documentValue.getContent()); } /** * Test method for {@link org.bonitasoft.engine.bpm.document.DocumentValue#setFileName(java.lang.String)}. */ @Test(expected = IllegalArgumentException.class) public final void setFileName_should_throw_exception_if_filename_is_empty_and_content_not() { // Given final DocumentValue documentValue = new DocumentValue("yujyt".getBytes(), "mimeType", "filename"); // When documentValue.setFileName(""); } /** * Test method for {@link org.bonitasoft.engine.bpm.document.DocumentValue#setFileName(java.lang.String)}. */ @Test(expected = IllegalArgumentException.class) public final void setFileName_should_throw_exception_if_filename_is_null_and_content_not() { // Given final DocumentValue documentValue = new DocumentValue("yujyt".getBytes(), "mimeType", "filename"); // When documentValue.setFileName(null); } /** * Test method for {@link org.bonitasoft.engine.bpm.document.DocumentValue#setFileName(java.lang.String)}. */ @Test public final void setFileName_should_set_content_with_filename_and_content() { // Given final DocumentValue documentValue = new DocumentValue("yujyt".getBytes(), "mimeType", "filename"); final String fileName = "new"; // When documentValue.setFileName(fileName); // Then assertEquals(fileName, documentValue.getFileName()); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/business/application/ApplicationLinkUpdaterTest.java ================================================ /** * 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.business.application; import static org.junit.Assert.assertThrows; import org.junit.Test; public class ApplicationLinkUpdaterTest { @Test public void should_not_update_home_page_field() { ApplicationLinkUpdater linkUpdater = new ApplicationLinkUpdater(); assertThrows(IllegalArgumentException.class, () -> linkUpdater.getFields().put(ApplicationField.HOME_PAGE_ID, 2L)); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/business/application/ApplicationUpdaterTest.java ================================================ /** * Copyright (C) 2021 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.business.application; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import org.junit.Test; public class ApplicationUpdaterTest { @Test public void should_set_icon_fields_when_updating_icon() { ApplicationUpdater applicationUpdater = new ApplicationUpdater(); applicationUpdater.setIcon("myIcon.png", new byte[] { 1, 2, 3 }); assertThat(applicationUpdater.getFields()).containsOnly( entry(ApplicationField.ICON_FILE_NAME, "myIcon.png"), entry(ApplicationField.ICON_CONTENT, new byte[] { 1, 2, 3 })); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/connector/sap/SAPMonoDestinationDataProviderTest.java ================================================ /** * Copyright (C) 2020 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.connector.sap; import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.sap.conn.jco.ext.DestinationDataProvider; import com.sap.conn.jco.ext.Environment; import mockit.Mock; import mockit.MockUp; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * IMPORTANT: this test class uses jmockit. If you run it in your IDE, you may need to configure a java agent * (its * seems working out of the box in IntelliJ). See the maven pom file. * * @author Aurelien Pupier * @author Baptiste Mesta */ public class SAPMonoDestinationDataProviderTest { @Before public void setupMocks() { // Ugly stuff that does not respect the "don't mock code you don't own" new MockUp() { @Mock public void registerDestinationDataProvider(DestinationDataProvider destinationDataProvider) { // do nothing } @Mock public void unregisterDestinationDataProvider(DestinationDataProvider destinationDataProvider) { // do nothing } }; } @Before public void setup() { SAPMonoDestinationDataProvider.clear(); } @After public void tearDown() { SAPMonoDestinationDataProvider.clear(); } @Test public void should_fail_to_set_multitple_different_destinations() throws IllegalStateException { SAPMonoDestinationDataProvider.getInstance("name1"); // 1st one, should succeed assertThatThrownBy(() -> SAPMonoDestinationDataProvider.getInstance("name2")) .isInstanceOf(IllegalStateException.class) .hasMessageStartingWith("You can use only one SAP destination"); } @Test public void should_succeed_to_set_the_same_destination_several_times() throws IllegalStateException { SAPMonoDestinationDataProvider.getInstance("nameSimilar"); SAPMonoDestinationDataProvider.getInstance("nameSimilar"); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/digest/DigestUtilsTest.java ================================================ /** * 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.digest; import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.bonitasoft.engine.digest.DigestUtils.*; import org.junit.Test; public class DigestUtilsTest { @Test public void should_encode_base64_when_source_with_accents() { assertThat(encodeBase64AsUtf8String(bytes("I love Bonita héhé"))).isEqualTo("SSBsb3ZlIEJvbml0YSBow6low6k="); } @Test public void should_generate_md5_when_source_with_accents() { assertThat(md5("I lôve Bonita hïhà")) .isEqualTo(new byte[] { 32, 69, -29, -53, 5, 12, -97, 109, 27, -99, 16, -27, 35, 71, 92, -46 }); } @Test public void should_generate_md5_fail_when_source_is_null() { assertThatThrownBy(() -> md5(null)).isInstanceOf(NullPointerException.class); } @Test public void should_generate_sha1_when_source_with_accents() { assertThat(sha1("You Lik~e Bonita ù?")) .isEqualTo(new byte[] { 87, 92, -26, 118, -34, -105, -48, 1, 124, 85, 62, 10, -24, -87, -11, -22, 112, 69, -51, 63 }); } @Test public void should_generate_sha1_fail_when_source_is_null() { assertThatThrownBy(() -> sha1(null)).isInstanceOf(NullPointerException.class); } private static byte[] bytes(String string) { return string.getBytes(UTF_8); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/exception/BonitaExceptionTest.java ================================================ /** * 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.exception; import org.junit.Test; /** * @author Emmanuel Duchastenier */ public class BonitaExceptionTest { @Test public void newBonitaExceptionWithNullCauseShouldNotThrowNPE() { new BonitaException("any message", null); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/exception/StackTraceTransformerTest.java ================================================ /** * 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.exception; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.lang.reflect.Field; import java.util.HashSet; import java.util.logging.Level; import java.util.logging.Logger; import org.bonitasoft.engine.api.internal.ServerWrappedException; import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException; import org.junit.Test; public class StackTraceTransformerTest { @Test public void merge_stack_keep_only_current_exception() { ServerWrappedException e = new ServerWrappedException( new IllegalStateException(new NoClassDefFoundError("org.Unknown"))); ServerWrappedException newE = StackTraceTransformer.mergeStackTraces(e); assertNull(newE.getCause().getCause()); } @Test public void merge_stack_keep_stack_of_cause() { ProcessInstanceNotFoundException cause3 = new ProcessInstanceNotFoundException("the process"); BonitaRuntimeException cause2 = new BonitaRuntimeException("org.Unknown", cause3); IllegalStateException cause = new IllegalStateException(cause2); ServerWrappedException e = new ServerWrappedException(cause); ServerWrappedException newE = StackTraceTransformer.mergeStackTraces(e); assertTrue(containsStack(newE.getCause().getStackTrace(), "BonitaRuntimeException")); assertTrue(containsStack(newE.getCause().getStackTrace(), "ProcessInstanceNotFoundException")); } @Test public void merge_stack_reduce_stack_length() throws Exception { ProcessInstanceNotFoundException cause3 = new ProcessInstanceNotFoundException("the process"); BonitaRuntimeException cause2 = new BonitaRuntimeException("org.Unknown", cause3); IllegalStateException cause = new IllegalStateException(cause2); ServerWrappedException e = new ServerWrappedException(cause); cause.printStackTrace(); ServerWrappedException newE = StackTraceTransformer.mergeStackTraces(e); newE.getCause().printStackTrace(); noDuplicate(newE.getCause()); } private void noDuplicate(final Throwable throwable) throws Exception { HashSet hashSet = new HashSet(); for (StackTraceElement stackTraceElement : throwable.getStackTrace()) { if (!hashSet.add(stackTraceElement) && stackTraceElement.toString().contains("org.bonitasoft")) { Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, "the stacktrace contains 2 times " + stackTraceElement, throwable); throw new Exception("the stacktrace contains 2 times " + stackTraceElement, throwable); } } } private boolean containsStack(final StackTraceElement[] stackTrace, final String string) { for (StackTraceElement stackTraceElement : stackTrace) { if (stackTraceElement.getClassName().contains(string)) { return true; } } return false; } @Test public void merge_stack_keep_message_of_cause() { ProcessInstanceNotFoundException cause3 = new ProcessInstanceNotFoundException("the process"); BonitaRuntimeException cause2 = new BonitaRuntimeException("org.Unknown", cause3); IllegalStateException cause = new IllegalStateException(cause2); ServerWrappedException e = new ServerWrappedException(cause); ServerWrappedException newE = StackTraceTransformer.mergeStackTraces(e); assertTrue(containsMessage(newE.getCause().getStackTrace(), "org.Unknown")); assertTrue(containsMessage(newE.getCause().getStackTrace(), "the process")); } @Test public void merge_stack_when_no_cause() { ProcessInstanceNotFoundException cause = new ProcessInstanceNotFoundException("the process"); ServerWrappedException e = new ServerWrappedException(cause); ServerWrappedException newE = StackTraceTransformer.mergeStackTraces(e); assertEquals("the process", cause.getMessage()); assertEquals(cause, newE.getCause()); } @Test public void merge_stack_work_even_with_security_restriction() { ProcessInstanceNotFoundException cause3 = new ProcessInstanceNotFoundException("the process"); BonitaRuntimeException cause2 = new BonitaRuntimeException("org.Unknown", cause3); IllegalStateException cause = new IllegalStateException(cause2); ServerWrappedException e = new ServerWrappedException(cause); Field field = StackTraceTransformer.field; StackTraceTransformer.field = null; ServerWrappedException newE = StackTraceTransformer.mergeStackTraces(e); StackTraceTransformer.field = field; assertTrue(containsMessage(newE.getCause().getStackTrace(), "org.Unknown")); assertTrue(containsMessage(newE.getCause().getStackTrace(), "the process")); } private boolean containsMessage(final StackTraceElement[] stackTrace, final String string) { for (StackTraceElement stackTraceElement : stackTrace) { if (stackTraceElement.getMethodName().contains(string)) { return true; } } return false; } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/expression/ExpressionEvaluationExceptionTest.java ================================================ /** * 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.expression; import static org.junit.Assert.assertEquals; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class ExpressionEvaluationExceptionTest { @Mock private Throwable cause; private final String expressionName = "plop"; /** * Test method for {@link org.bonitasoft.engine.expression.ExpressionEvaluationException#getExpressionName()}. */ @Test public final void getExpressionName() { final ExpressionEvaluationException expressionEvaluationException = new ExpressionEvaluationException(cause, expressionName); final String result = expressionEvaluationException.getExpressionName(); assertEquals(expressionName, result); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/filter/AbstractUserFilterTest.java ================================================ /** * 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.filter; import static org.assertj.core.api.Assertions.assertThat; import java.util.List; import org.junit.After; import org.junit.Before; import org.junit.Test; public class AbstractUserFilterTest { private AbstractUserFilter abstractUserFilterTest; @Before public void setUp() { abstractUserFilterTest = new AbstractUserFilter() { @Override public void validateInputParameters() { } @SuppressWarnings("unused") @Override public List filter(final String actorName) { return null; } }; } @After public void tearDown() { } @Test public void should_getInputParameter_return_null_if_map_does_not_contain_key() throws Exception { assertThat(abstractUserFilterTest.getInputParameter("aInput")).isNull(); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/io/FileOperationsTest.java ================================================ /** * 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.io; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.io.FileAndContentUtils.*; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; /** * @author Baptiste Mesta */ public class FileOperationsTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void should_extract_file_from_zip() throws Exception { File zipFile = aZipWithFiles(); byte[] contentOfFile2 = FileOperations.getFileFromZip(zipFile, "/file2"); assertThat(new String(contentOfFile2)).isEqualTo("the content of file 2"); } private File aZipWithFiles() throws IOException { File zipFile; byte[] zip = zip(file("/file1", "the content of file 1"), file("/file2", "the content of file 2")); zipFile = temporaryFolder.newFile(); Files.write(zipFile.toPath(), zip); return zipFile; } @Test(expected = FileNotFoundException.class) public void should_throw_file_not_found_when_zip_do_not_contains_file() throws Exception { byte[] zip = zip(file("/file1", "the content of file 1")); File zipFile = temporaryFolder.newFile(); Files.write(zipFile.toPath(), zip); FileOperations.getFileFromZip(zipFile, "/file2"); } @Test(expected = IOException.class) public void should_throw_IOException_when_file_is_not_a_zip() throws Exception { File zipFile = temporaryFolder.newFile(); Files.write(zipFile.toPath(), new byte[] { 1, 2, 3 }); FileOperations.getFileFromZip(zipFile, "toto"); } @Test public void should_getContent_of_file_in_sub_folder() throws Exception { byte[] zip = zip(file("/file1", "the content of file 1"), directory("/sub/"), file("/sub/file2", "the content of file 2")); File zipFile = temporaryFolder.newFile(); Files.write(zipFile.toPath(), zip); byte[] contentOfFile2 = FileOperations.getFileFromZip(zipFile, "/sub/file2"); assertThat(new String(contentOfFile2)).isEqualTo("the content of file 2"); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/page/PageCreatorTest.java ================================================ /** * 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.page; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.page.PageCreator.PageField; import org.junit.Test; public class PageCreatorTest { private static final String ZIP_FILE_NAME = "content.zip"; private static final String DISPLAY_NAME = "display name"; private static final String NAME = "page name"; private static final String DESCRIPTION = "page description"; public static final long PROCESS_DEFINITION_ID = 123L; @Test public void should_create_page_with_default_content_type() { // given final PageCreator pageCreator = new PageCreator(NAME, ZIP_FILE_NAME).setDisplayName(DISPLAY_NAME) .setDescription(DESCRIPTION); // when final Map fields = pageCreator.getFields(); // then assertThat(fields).as("should set content type").containsOnly(entry(PageField.NAME, NAME), entry(PageField.DISPLAY_NAME, DISPLAY_NAME), entry(PageField.DESCRIPTION, DESCRIPTION), entry(PageField.CONTENT_TYPE, ContentType.PAGE), entry(PageField.CONTENT_NAME, ZIP_FILE_NAME)); assertThat(pageCreator.getName()).isEqualTo(NAME); } @Test public void should_create_page_with_process_definition() { // given final PageCreator pageCreator = new PageCreator(NAME, ZIP_FILE_NAME).setDisplayName(DISPLAY_NAME) .setDescription(DESCRIPTION) .setProcessDefinitionId(PROCESS_DEFINITION_ID) .setContentType(ContentType.FORM); // when final Map fields = pageCreator.getFields(); // then assertThat(fields).as("should set content type").containsOnly(entry(PageField.NAME, NAME), entry(PageField.DISPLAY_NAME, DISPLAY_NAME), entry(PageField.DESCRIPTION, DESCRIPTION), entry(PageField.CONTENT_NAME, ZIP_FILE_NAME), entry(PageField.CONTENT_TYPE, ContentType.FORM), entry(PageField.PROCESS_DEFINITION_ID, PROCESS_DEFINITION_ID)); assertThat(pageCreator.getName()).isEqualTo(NAME); } @Test public void should_create_page_with_form_content_type() { // given final PageCreator pageCreator = new PageCreator(NAME, ZIP_FILE_NAME, ContentType.FORM, 12345L) .setDisplayName(DISPLAY_NAME); // when final Map fields = pageCreator.getFields(); // then assertThat(fields).as("should set content type").containsOnly( entry(PageField.NAME, NAME), entry(PageField.DISPLAY_NAME, DISPLAY_NAME), entry(PageField.CONTENT_TYPE, ContentType.FORM), entry(PageField.PROCESS_DEFINITION_ID, 12345L), entry(PageField.CONTENT_NAME, ZIP_FILE_NAME)); } @Test public void should_print_all_fields() { // given final PageCreator pageCreator = new PageCreator(NAME, ZIP_FILE_NAME, ContentType.FORM, 12345L) .setDisplayName(DISPLAY_NAME).setDescription( DESCRIPTION); // when final Map fields = pageCreator.getFields(); // then assertThat(pageCreator).as("should print human readable to string") .hasToString("PageCreator [fields=" + fields + "]"); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/page/PageUpdaterTest.java ================================================ /** * 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.page; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import java.io.Serializable; import java.util.Map; import org.junit.Test; public class PageUpdaterTest { private static final String ZIP_FILE_NAME = "content.zip"; private static final String DISPLAY_NAME = "display name"; private static final String NAME = "page name"; private static final String DESCRIPTION = "page description"; public static final long PROCESS_DEFINITION_ID = 1L; @Test public void should_create_page_with_default_fields() { // given final PageUpdater pageUpdater = new PageUpdater().setName(NAME).setContentName(ZIP_FILE_NAME) .setDisplayName(DISPLAY_NAME).setDescription(DESCRIPTION) .setContentType(ContentType.FORM).setProcessDefinitionId(PROCESS_DEFINITION_ID); // when final Map fields = pageUpdater.getFields(); // then assertThat(fields).as("should set content type").containsOnly(entry(PageUpdater.PageUpdateField.NAME, NAME), entry(PageUpdater.PageUpdateField.DISPLAY_NAME, DISPLAY_NAME), entry(PageUpdater.PageUpdateField.DESCRIPTION, DESCRIPTION), entry(PageUpdater.PageUpdateField.CONTENT_TYPE, ContentType.FORM), entry(PageUpdater.PageUpdateField.CONTENT_NAME, ZIP_FILE_NAME), entry(PageUpdater.PageUpdateField.PROCESS_DEFINITION_ID, PROCESS_DEFINITION_ID), entry(PageUpdater.PageUpdateField.NAME, NAME)); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/page/impl/PageImplAssert.java ================================================ /** * 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.page.impl; import static java.lang.String.format; import java.util.Date; import org.assertj.core.api.AbstractAssert; /** * {@link PageImpl} specific assertions - Generated by CustomAssertionGenerator. */ public class PageImplAssert extends AbstractAssert { /** * Creates a new {@link PageImplAssert} to make assertions on actual PageImpl. * * @param actual the PageImpl we want to make assertions on. */ public PageImplAssert(PageImpl actual) { super(actual, PageImplAssert.class); } /** * An entry point for PageImplAssert to follow AssertJ standard assertThat() statements.
* With a static import, one's can write directly : assertThat(myPageImpl) and get specific assertion * with code completion. * * @param actual the PageImpl we want to make assertions on. * @return a new {@link PageImplAssert} */ public static PageImplAssert assertThat(PageImpl actual) { return new PageImplAssert(actual); } /** * Verifies that the actual PageImpl's contentName is equal to the given one. * * @param contentName the given contentName to compare the actual PageImpl's contentName to. * @return this assertion object. * @throws AssertionError - if the actual PageImpl's contentName is not equal to the given one. */ public PageImplAssert hasContentName(String contentName) { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> contentName to be:\n <%s>\n but was:\n <%s>", actual, contentName, actual.getContentName()); // check if (!actual.getContentName().equals(contentName)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual PageImpl's contentType is equal to the given one. * * @param contentType the given contentType to compare the actual PageImpl's contentType to. * @return this assertion object. * @throws AssertionError - if the actual PageImpl's contentType is not equal to the given one. */ public PageImplAssert hasContentType(String contentType) { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> contentType to be:\n <%s>\n but was:\n <%s>", actual, contentType, actual.getContentType()); // check if (!actual.getContentType().equals(contentType)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual PageImpl's description is equal to the given one. * * @param description the given description to compare the actual PageImpl's description to. * @return this assertion object. * @throws AssertionError - if the actual PageImpl's description is not equal to the given one. */ public PageImplAssert hasDescription(String description) { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> description to be:\n <%s>\n but was:\n <%s>", actual, description, actual.getDescription()); // check if (!actual.getDescription().equals(description)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual PageImpl's displayName is equal to the given one. * * @param displayName the given displayName to compare the actual PageImpl's displayName to. * @return this assertion object. * @throws AssertionError - if the actual PageImpl's displayName is not equal to the given one. */ public PageImplAssert hasDisplayName(String displayName) { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> displayName to be:\n <%s>\n but was:\n <%s>", actual, displayName, actual.getDisplayName()); // check if (!actual.getDisplayName().equals(displayName)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual PageImpl's id is equal to the given one. * * @param id the given id to compare the actual PageImpl's id to. * @return this assertion object. * @throws AssertionError - if the actual PageImpl's id is not equal to the given one. */ public PageImplAssert hasId(long id) { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> id to be:\n <%s>\n but was:\n <%s>", actual, id, actual.getId()); // check if (actual.getId() != id) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual PageImpl's installationDate is equal to the given one. * * @param installationDate the given installationDate to compare the actual PageImpl's installationDate to. * @return this assertion object. * @throws AssertionError - if the actual PageImpl's installationDate is not equal to the given one. */ public PageImplAssert hasInstallationDate(Date installationDate) { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> installationDate to be:\n <%s>\n but was:\n <%s>", actual, installationDate, actual.getInstallationDate()); // check if (!actual.getInstallationDate().equals(installationDate)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual PageImpl's installedBy is equal to the given one. * * @param installedBy the given installedBy to compare the actual PageImpl's installedBy to. * @return this assertion object. * @throws AssertionError - if the actual PageImpl's installedBy is not equal to the given one. */ public PageImplAssert hasInstalledBy(long installedBy) { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> installedBy to be:\n <%s>\n but was:\n <%s>", actual, installedBy, actual.getInstalledBy()); // check if (actual.getInstalledBy() != installedBy) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual PageImpl's lastModificationDate is equal to the given one. * * @param lastModificationDate the given lastModificationDate to compare the actual PageImpl's lastModificationDate * to. * @return this assertion object. * @throws AssertionError - if the actual PageImpl's lastModificationDate is not equal to the given one. */ public PageImplAssert hasLastModificationDate(Date lastModificationDate) { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> lastModificationDate to be:\n <%s>\n but was:\n <%s>", actual, lastModificationDate, actual.getLastModificationDate()); // check if (!actual.getLastModificationDate().equals(lastModificationDate)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual PageImpl's lastUpdatedBy is equal to the given one. * * @param lastUpdatedBy the given lastUpdatedBy to compare the actual PageImpl's lastUpdatedBy to. * @return this assertion object. * @throws AssertionError - if the actual PageImpl's lastUpdatedBy is not equal to the given one. */ public PageImplAssert hasLastUpdatedBy(long lastUpdatedBy) { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> lastUpdatedBy to be:\n <%s>\n but was:\n <%s>", actual, lastUpdatedBy, actual.getLastUpdatedBy()); // check if (actual.getLastUpdatedBy() != lastUpdatedBy) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual PageImpl's name is equal to the given one. * * @param name the given name to compare the actual PageImpl's name to. * @return this assertion object. * @throws AssertionError - if the actual PageImpl's name is not equal to the given one. */ public PageImplAssert hasName(String name) { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> name to be:\n <%s>\n but was:\n <%s>", actual, name, actual.getName()); // check if (!actual.getName().equals(name)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual PageImpl's pageId is equal to the given one. * * @param pageId the given pageId to compare the actual PageImpl's pageId to. * @return this assertion object. * @throws AssertionError - if the actual PageImpl's pageId is not equal to the given one. */ public PageImplAssert hasPageId(long pageId) { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> pageId to be:\n <%s>\n but was:\n <%s>", actual, pageId, actual.getPageId()); // check if (actual.getPageId() != pageId) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual PageImpl's processDefinitionId is equal to the given one. * * @param processDefinitionId the given processDefinitionId to compare the actual PageImpl's processDefinitionId to. * @return this assertion object. * @throws AssertionError - if the actual PageImpl's processDefinitionId is not equal to the given one. */ public PageImplAssert hasProcessDefinitionId(Long processDefinitionId) { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> processDefinitionId to be:\n <%s>\n but was:\n <%s>", actual, processDefinitionId, actual.getProcessDefinitionId()); // check if (!actual.getProcessDefinitionId().equals(processDefinitionId)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual PageImpl is provided. * * @return this assertion object. * @throws AssertionError - if the actual PageImpl is not provided. */ public PageImplAssert isProvided() { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("Expected actual PageImpl to be provided but was not.", actual); // check if (!actual.isProvided()) throw new AssertionError(errorMessage); // return the current assertion for method chaining return this; } /** * Verifies that the actual PageImpl is editable. * * @return this assertion object. * @throws AssertionError - if the actual PageImpl is not provided. */ public PageImplAssert isEditable() { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("Expected actual PageImpl to be editable but was not.", actual); // check if (!actual.isEditable()) throw new AssertionError(errorMessage); // return the current assertion for method chaining return this; } /** * Verifies that the actual PageImpl is removable. * * @return this assertion object. * @throws AssertionError - if the actual PageImpl is not provided. */ public PageImplAssert isRemovable() { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("Expected actual PageImpl to be removable but was not.", actual); // check if (!actual.isRemovable()) throw new AssertionError(errorMessage); // return the current assertion for method chaining return this; } /** * Verifies that the actual PageImpl is not provided. * * @return this assertion object. * @throws AssertionError - if the actual PageImpl is provided. */ public PageImplAssert isNotProvided() { // check that actual PageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("Expected actual PageImpl not to be provided but was.", actual); // check if (actual.isProvided()) throw new AssertionError(errorMessage); // return the current assertion for method chaining return this; } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/page/impl/PageImplTest.java ================================================ /** * 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.page.impl; import static org.assertj.core.api.Assertions.assertThat; import java.util.Date; import org.bonitasoft.engine.page.ContentType; import org.junit.Test; public class PageImplTest { private static final long USER_ID = 1L; private static final String DESCRIPTION = "description"; private static final boolean PROVIDED = true; private static final boolean EDITABLE = true; private static final boolean REMOVABLE = true; private static final String NAME = "name"; private static final String DISPLAY_NAME = "display name"; public static final long PROCESS_DEFINITION_ID = 456789L; @Test public void should_set_all_fields() { Date date = new Date(1); Date modificationDate = new Date(2); PageImplAssert .assertThat( new PageImpl(-1L, NAME, DISPLAY_NAME, PROVIDED, EDITABLE, REMOVABLE, DESCRIPTION, date.getTime(), USER_ID, modificationDate.getTime(), USER_ID, "content.zip", ContentType.PAGE, null)) .hasId(-1L) .hasName(NAME) .hasDisplayName(DISPLAY_NAME) .isProvided() .isEditable() .isRemovable() .hasDescription(DESCRIPTION).hasInstallationDate(date) .hasLastModificationDate(modificationDate) .hasContentType(ContentType.PAGE); } @Test public void should_set_all_form_fields() { Date date = new Date(1); Date modificationDate = new Date(2); PageImplAssert .assertThat( new PageImpl(-1l, NAME, DISPLAY_NAME, PROVIDED, EDITABLE, REMOVABLE, DESCRIPTION, date.getTime(), USER_ID, modificationDate.getTime(), USER_ID, "content.zip", ContentType.FORM, PROCESS_DEFINITION_ID)) .hasId(-1l) .hasName(NAME) .hasDisplayName(DISPLAY_NAME) .isProvided() .isRemovable() .isEditable() .hasDescription(DESCRIPTION).hasInstallationDate(date) .hasLastModificationDate(modificationDate) .hasProcessDefinitionId(PROCESS_DEFINITION_ID) .hasContentType(ContentType.FORM); } @Test public void should_display_all_fields_in_to_string() { Date date = new Date(1); Date modificationDate = new Date(2); assertThat( new PageImpl(-1L, NAME, DISPLAY_NAME, PROVIDED, EDITABLE, REMOVABLE, DESCRIPTION, date.getTime(), USER_ID, modificationDate.getTime(), USER_ID, "content.zip", ContentType.FORM, PROCESS_DEFINITION_ID).toString()) .contains(String.valueOf(PROCESS_DEFINITION_ID)).contains( ContentType.FORM); } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/search/impl/SearchOptionsImplTest.java ================================================ /** * 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.search.impl; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class SearchOptionsImplTest { @Test public void equals_should_check_the_whole_objects() { final SearchOptionsImpl options1 = buildSearchOptions(); final SearchOptionsImpl options2 = buildSearchOptions(); assertThat(options1).isEqualTo(options2); } @Test public void hashCode_should_check_the_whole_objects() { final SearchOptionsImpl options1 = buildSearchOptions(); final SearchOptionsImpl options2 = buildSearchOptions(); assertThat(options1.hashCode()).isEqualTo(options2.hashCode()); } private SearchOptionsImpl buildSearchOptions() { final SearchOptionsImpl options = new SearchOptionsImpl(0, 2000); options.addFilter("field1", "value"); options.addFilter("field3", 8); options.addFilter("field2", true); return options; } } ================================================ FILE: bpm/bonita-common/src/test/java/org/bonitasoft/engine/util/IOUtilTest.java ================================================ /** * 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.util; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.HashMap; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.bonitasoft.engine.io.IOUtil; import org.junit.Test; /** * @author Baptiste Mesta */ public class IOUtilTest { private static String lineSeparator = System.getProperty("line.separator"); @Test public void testGetResources() throws Exception { final Map resources = IOUtil.getResources(IOUtilTest.class, IOUtil.class); assertNotNull(resources.get(IOUtil.class.getName().replace('.', '/') + ".class")); } @Test public void testGetClassData() throws Exception { assertNotNull(IOUtil.getClassData(this.getClass())); } @Test public void testGetAllContentFromInputStream() throws Exception { final byte[] bytes = "theContent\nVeryGreatContent".getBytes(); final ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); final byte[] read = IOUtil.getAllContentFrom(inputStream); inputStream.close(); assertArrayEquals(bytes, read); } @Test public void testGetAllContentFromFile() throws Exception { final File file = File.createTempFile("test", "test"); IOUtil.writeContentToFile("theContent\nVeryGreatContent", file); assertArrayEquals("theContent\nVeryGreatContent".getBytes(), IOUtil.getAllContentFrom(file)); file.delete(); } @Test public void testGetAllContentFromURL() throws Exception { final File file = File.createTempFile("test", "test"); IOUtil.writeContentToFile("theContent\nVeryGreatContent", file); assertArrayEquals("theContent\nVeryGreatContent".getBytes(), IOUtil.getAllContentFrom(file.toURI().toURL())); file.delete(); } @Test public void testDeleteDirFile() throws Exception { final File folder = File.createTempFile("folder", "test"); folder.delete(); folder.mkdir(); final File file = new File(folder, "aFile"); IOUtil.writeContentToFile("content", file); assertTrue(IOUtil.deleteDir(folder)); assertFalse(file.exists()); assertFalse(folder.exists()); } @Test public void testDeleteDirFileWithRetry() throws Exception { final File folder = File.createTempFile("folder", "test"); folder.delete(); folder.mkdir(); final File file = new File(folder, "aFile"); IOUtil.writeContentToFile("content", file); assertTrue(IOUtil.deleteDir(folder, 5, 1)); assertFalse(file.exists()); assertFalse(folder.exists()); } @Test public void testDeleteFile() { final File file = mock(File.class); when(file.delete()).thenReturn(true); final boolean deleteFile = IOUtil.deleteFile(file, 2, 1); assertTrue(deleteFile); } @Test public void testDeleteFileNotDeleted() { final File file = mock(File.class); when(file.delete()).thenReturn(false); final boolean deleteFile = IOUtil.deleteFile(file, 2, 1); assertFalse(deleteFile); } @Test public void testDeleteFiledeletedAfterFewTry() { final File file = mock(File.class); when(file.delete()).thenReturn(false, false, true); final boolean deleteFile = IOUtil.deleteFile(file, 6, 1); assertTrue(deleteFile); } @Test public void testZip() throws Exception { final HashMap hashMap = new HashMap(2); hashMap.put("file1.txt", "content1".getBytes()); hashMap.put("file2.txt", "content2\ncontent2".getBytes()); final byte[] zip = IOUtil.zip(hashMap); assertNotNull(zip); } @Test public void testWriteFileString() throws Exception { final File file = File.createTempFile("test", "test"); IOUtil.writeContentToFile("theContent\ncontent", file); assertEquals("theContent\ncontent", new String(IOUtil.getAllContentFrom(file))); file.delete(); } @Test public void unzipToFolder() throws Exception { final HashMap hashMap = new HashMap(2); hashMap.put("file1.txt", "content1".getBytes()); hashMap.put("file2.txt", "content2\ncontent2".getBytes()); final byte[] zip = IOUtil.zip(hashMap); final File folder = File.createTempFile("folder", "tmp"); folder.delete(); folder.mkdirs(); IOUtil.unzipToFolder(new ByteArrayInputStream(zip), folder); final String[] files = folder.list(); assertEquals(2, files.length); assertEquals("content1", new String(IOUtil.getAllContentFrom(new File(folder, "file1.txt")))); assertEquals("content2\ncontent2", new String(IOUtil.getAllContentFrom(new File(folder, "file2.txt")))); IOUtil.deleteDir(folder); } @Test public void unzipToFolder_should_reject_zip_slip_file_attack() throws Exception { final byte[] maliciousZip = createZipWithEntry("../../evil.txt", "malicious content"); final File folder = createTempFolder(); try { assertThatThrownBy(() -> IOUtil.unzipToFolder(new ByteArrayInputStream(maliciousZip), folder)) .isInstanceOf(IOException.class) .hasMessageContaining("outside of the target directory"); } finally { IOUtil.deleteDir(folder); } } @Test public void unzipToFolder_should_reject_zip_slip_directory_attack() throws Exception { final byte[] maliciousZip = createZipWithDirectoryEntry("../../evil_dir/"); final File folder = createTempFolder(); try { assertThatThrownBy(() -> IOUtil.unzipToFolder(new ByteArrayInputStream(maliciousZip), folder)) .isInstanceOf(IOException.class) .hasMessageContaining("outside of the target directory"); } finally { IOUtil.deleteDir(folder); } } @Test public void unzipToFolder_should_allow_legitimate_nested_paths() throws Exception { final HashMap hashMap = new HashMap<>(1); hashMap.put("subdir/nested/file.txt", "nested content".getBytes()); final byte[] zip = IOUtil.zip(hashMap); final File folder = createTempFolder(); try { IOUtil.unzipToFolder(new ByteArrayInputStream(zip), folder); final File nestedFile = new File(folder, "subdir/nested/file.txt"); assertThat(nestedFile).exists(); assertThat(new String(IOUtil.getAllContentFrom(nestedFile))).isEqualTo("nested content"); } finally { IOUtil.deleteDir(folder); } } private static File createTempFolder() throws IOException { return Files.createTempDirectory("folder").toFile(); } private static byte[] createZipWithEntry(String entryName, String content) throws IOException { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { zos.putNextEntry(new ZipEntry(entryName)); zos.write(content.getBytes()); zos.closeEntry(); } return baos.toByteArray(); } private static byte[] createZipWithDirectoryEntry(String dirName) throws IOException { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { zos.putNextEntry(new ZipEntry(dirName)); zos.closeEntry(); } return baos.toByteArray(); } } ================================================ FILE: bpm/bonita-common/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: bpm/bonita-common/src/test/resources/org/bonitasoft/engine/bdm/serialization/simpleInvoice.json ================================================ { "persistenceId": null, "persistenceVersion": null, "customerName": "Bonitasoft", "date": "2018-08-15", "comments": null } ================================================ FILE: bpm/bonita-common/src/testFixtures/java/org/bonitasoft/engine/bdm/validator/assertion/RuleOfCondition.java ================================================ /** * 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.bdm.validator.assertion; import org.assertj.core.api.Condition; import org.bonitasoft.engine.bdm.validator.ValidationStatus; import org.bonitasoft.engine.bdm.validator.rule.ValidationRule; /** * @author Colin PUY */ public class RuleOfCondition extends Condition> { public static RuleOfCondition ruleOf(Class> ruleClass) { return new RuleOfCondition(ruleClass); } private Class ruleClass; public RuleOfCondition(Class ruleClass) { this.ruleClass = ruleClass; } @Override public boolean matches(ValidationRule rule) { return rule.getClass().equals(ruleClass); } } ================================================ FILE: bpm/bonita-common/src/testFixtures/java/org/bonitasoft/engine/bdm/validator/assertion/ValidationStatusAssert.java ================================================ /** * 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.bdm.validator.assertion; import static org.bonitasoft.engine.api.result.Status.Level.ERROR; import java.util.Objects; import org.assertj.core.api.AbstractAssert; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.api.result.Status; import org.bonitasoft.engine.bdm.validator.ValidationStatus; public class ValidationStatusAssert extends AbstractAssert { protected ValidationStatusAssert(ValidationStatus actual) { super(actual, ValidationStatusAssert.class); } public static ValidationStatusAssert assertThat(ValidationStatus actual) { return new ValidationStatusAssert(actual); } public ValidationStatusAssert isOk() { Assertions.assertThat(actual.isOk()).isTrue(); return this; } public ValidationStatusAssert isOk(String description) { Assertions.assertThat(actual.isOk()).as(description).isTrue(); return this; } public ValidationStatusAssert isNotOk() { Assertions.assertThat(actual.isOk()).isFalse(); return this; } public ValidationStatusAssert isNotOk(String description) { Assertions.assertThat(actual.isOk()).as(description).isFalse(); return this; } public ValidationStatusAssert hasError(String errorMessage) { Assertions.assertThat( actual.getStatuses().stream().filter(status -> Objects.equals(ERROR, status.getLevel())) .map(Status::getMessage).toList()) .contains(errorMessage); return this; } public ValidationStatusAssert hasErrorSize(int size) { Assertions.assertThat(actual.getStatuses().stream() .filter(status -> Objects.equals(ERROR, status.getLevel())) .count()).isEqualTo(size); return this; } } ================================================ FILE: bpm/bonita-common/src/testFixtures/java/org/bonitasoft/engine/io/FileAndContentUtils.java ================================================ /** * 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.io; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** * @author Baptiste Mesta. */ public class FileAndContentUtils { public static byte[] zip(FileAndContent... files) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); ZipOutputStream zos = new ZipOutputStream(baos)) { for (FileAndContent file : files) { ZipEntry e = new ZipEntry(file.getFileName()); zos.putNextEntry(e); if (!e.isDirectory()) { zos.write(file.getContent()); } zos.closeEntry(); } zos.finish(); return baos.toByteArray(); } } public static FileAndContent file(String fileName, String content) { return new FileAndContent(fileName, content.getBytes()); } public static FileAndContent file(String fileName, byte[] content) { return new FileAndContent(fileName, content); } public static FileAndContent directory(String fileName) { return new FileAndContent(fileName, null); } public static FileAndContent file(String fileName, InputStream content) throws IOException { return new FileAndContent(fileName, content.readAllBytes()); } public static class FileAndContent { private String fileName; private byte[] content; public FileAndContent(String fileName, byte[] content) { this.fileName = fileName; this.content = content; } public String getFileName() { return fileName; } public byte[] getContent() { return content; } } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/build.gradle ================================================ dependencies { api project(':services:bonita-identity') api project(':services:bonita-commons') api project(':services:bonita-builder') api project(':services:bonita-events') api project(':services:bonita-log') api project(':services:bonita-persistence') testImplementation libs.mockitoCore testImplementation libs.assertj annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/ActorMappingService.java ================================================ /** * 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.actor.mapping; import java.util.List; import java.util.Set; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet * @since 6.0 */ public interface ActorMappingService { String ACTOR = "ACTOR"; String ACTOR_MEMBER = "ACTOR_MEMBER"; /** * Create an actor by given actor * * @param actor * The given actor without id * @return the new created actor with id * @throws SActorCreationException */ SActor addActor(SActor actor) throws SActorCreationException; /** * Create actors by given actors * * @param actors * The given actors without IDs * @return The set of the new created actors * @throws SActorCreationException */ Set addActors(Set actors) throws SActorCreationException; /** * Get actor by actor id * If the actor by a given actorId is not found, it will throw SActorNotFoundException * * @param actorId * Id of actor * @return SActor object response to the given actorId * @throws SActorNotFoundException * @throws SBonitaReadException */ SActor getActor(long actorId) throws SActorNotFoundException, SBonitaReadException; /** * Get actor by actor name and scope id * If the actor by a given actorName and scopeId is not found, it will throw SActorNotFoundException * * @param actorName * Name of actor * @param scopeId * Id of scope, it can be processDefinitionId * @return SActor object corresponding to the given actorName and scopeId * @throws SActorNotFoundException * Error thrown if no actor have an id corresponding to the parameter. */ SActor getActor(String actorName, long scopeId) throws SActorNotFoundException; /** * Get a list of all actors for the id specified user in certain scopes specified by scopeIds * * @param scopeIds * Ids of scope, it can be processDefinitionId * @param userId * Id of user which is added to actor * @return The list of SActor Objects * @throws SBonitaReadException */ List getActors(Set scopeIds, Long userId) throws SBonitaReadException; /** * Update actor by its id * If the actor by a given actorId is not found, it will throw processDefinitionNotFountExcetion * * @param actorId * Id of actor * @param updateDescriptor * Update description * @return the updated actor * @throws SActorNotFoundException * Error thrown if no actor have an id corresponding to the parameter actorId. * @throws SActorUpdateException * Error thrown if has exceptions while try to update an actor * @throws SBonitaReadException */ SActor updateActor(long actorId, EntityUpdateDescriptor updateDescriptor) throws SActorNotFoundException, SActorUpdateException, SBonitaReadException; /** * Delete actors in the id specified scope * * @param scopeId * Id of scope, it can be processDefinitionId * @throws SActorDeletionException * Error thrown if has exceptions while try to delete actors */ void deleteActors(long scopeId) throws SActorDeletionException; /** * Add the userId specified user to the actorId specified actor * * @param actorId * Id of actor * @param userId * Id of user * @return SActorMember object * @throws SActorNotFoundException * Error thrown if no actor have an id corresponding to the parameter actorId. * @throws SActorMemberCreationException * Error thrown if has exceptions while try to create the SActorMember object */ SActorMember addUserToActor(long actorId, long userId) throws SActorNotFoundException, SActorMemberCreationException; /** * Add the groupId specified group to the actorId specified actor * * @param actorId * Id of actor * @param groupId * Id of group * @return SActorMember object * @throws SActorNotFoundException * Error thrown if no actor have an id corresponding to the parameter actorId. * @throws SActorMemberCreationException * Error thrown if has exceptions while try to create the SActorMember object */ SActorMember addGroupToActor(long actorId, long groupId) throws SActorNotFoundException, SActorMemberCreationException; /** * Add the roleId specified role to the actorId specified actor * * @param actorId * Id of actor * @param roleId * Id of role * @return SActorMember object * @throws SActorNotFoundException * Error thrown if no actor have an id corresponding to the parameter actorId. * @throws SActorMemberCreationException * Error thrown if has exceptions while try to create the SActorMember object */ SActorMember addRoleToActor(long actorId, long roleId) throws SActorNotFoundException, SActorMemberCreationException; /** * Add the roleId and groupId specified relationship to the actorId specified actor * * @param actorId * Id of actor * @param roleId * Id of role * @param groupId * Id of group * @return SActorMember object * @throws SActorNotFoundException * Error thrown if no actor have an id corresponding to the parameter actorId. * @throws SActorMemberCreationException * Error thrown if has exceptions while try to create the SActorMember object */ SActorMember addRoleAndGroupToActor(long actorId, long roleId, long groupId) throws SActorNotFoundException, SActorMemberCreationException; /** * Remove actorMember for the give actorMemberId * * @param actorMemberId * Id of actorMember * @throws SActorMemberNotFoundException * Error thrown if no actorMember have an id corresponding to the parameter actorMemberId. * @throws SActorMemberDeletionException * Error thrown if has exceptions while try to remove the SActorMember object */ SActorMember deleteActorMember(long actorMemberId) throws SActorMemberNotFoundException, SActorMemberDeletionException; /** * Remove an actor member * * @param actorMember * the actorMember to remove * @throws SActorMemberDeletionException * Error thrown if has exceptions while try to remove the SActorMember object */ void deleteActorMember(final SActorMember actorMember) throws SActorMemberDeletionException; /** * Get list of SActorMember objects by pagination * * @param actorId * Id of actor * @param index * Index of the record to be retrieved from. First record has pageNumber 0. * @param numberOfActorMembers * Number of result we want to get. Maximum number of result returned. * @return List of SActorMember objects, ordered by id ascending * @throws SBonitaReadException */ List getActorMembers(long actorId, int index, int numberOfActorMembers) throws SBonitaReadException; /** * Get number of ActorMembers for give actorId * * @param actorId * Id of actor * @return the number of ActorMembers * @throws SBonitaReadException */ long getNumberOfActorMembers(long actorId) throws SBonitaReadException; /** * Get a list of SActorMember objects for given userId * * @param userId * Id of user * @param fromIndex * Index of the record to be retrieved from. First record has pageNumber 0. * @param numberOfActorMembers * Number of result we want to get. Maximum number of result returned. * @return List of SActorMember objects, ordered by id ascending * @throws SBonitaReadException */ List getActorMembersOfUser(long userId, int fromIndex, int numberOfActorMembers) throws SBonitaReadException; /** * Get a list of SActorMember objects for given groupId * * @param groupId * Id of group * @return a list of SActorMember objects, ordered by id ascending * @throws SBonitaReadException */ List getActorMembersOfGroup(long groupId, int index, int numberOfActorMembers) throws SBonitaReadException; /** * Get a list of SActorMember objects for given roleId * * @param roleId * Id of role * @return a list of SActorMember objects, ordered by id ascending * @throws SBonitaReadException */ List getActorMembersOfRole(long roleId, int fromIndex, int numberOfActorMembers) throws SBonitaReadException; /** * Is a specified user allowed to start a process? * * @param userId * Id of user * @param processDefinitionId Id of processDefinition * @return a list of SActor objects * @throws SBonitaReadException */ boolean canUserStartProcessDefinition(long userId, long processDefinitionId) throws SBonitaReadException; /** * Get a list of actors by the given list of actor ids * * @param actorIds * the list of actor ids to retrieve * @return a list of actors * @throws SActorNotFoundException * @throws SBonitaReadException */ List getActors(List actorIds) throws SActorNotFoundException, SBonitaReadException; /** * Get paginated actors * * @param processDefinitionId * identifier of process definition * @return the list of actors * @throws SBonitaReadException */ List getActors(long processDefinitionId, QueryOptions queryOptions) throws SBonitaReadException; /** * Return the number of users corresponding to an actor * * @param actorId * the id of the actor to retrieve the users from * @return Number of users mapped to actor */ long getNumberOfUsersOfActor(long actorId); /** * Get the number of roles of an actor * * @param actorId * the id corresponding to an actor * @return Number of roles mapped to actor */ long getNumberOfRolesOfActor(long actorId); /** * Get the number of groups corresponding to an actor * * @param actorId * the id of the actor to retrieve the groups from * @return Number of groups mapped to actor * @throws RuntimeException */ long getNumberOfGroupsOfActor(long actorId) throws RuntimeException; /** * Get the number of memberships (role and group) of an actor * * @param actorId * the id of the actor to retrieve the memberships from * @return Number of memberships mapped to actor */ long getNumberOfMembershipsOfActor(long actorId); /** * Delete all actor members for the connected tenant * * @throws SActorMemberDeletionException * @since 6.1 */ void deleteAllActorMembers() throws SActorMemberDeletionException; List getPossibleUserIdsOfActorId(long actorId, int startIndex, int maxResults) throws SBonitaReadException; /** * Get the actor member * * @param actorId * The identifier of the actor * @param userId * The identifier of the user * @param groupId * The identifier of the group * @param roleId * The identifier of the role * @return The corresponding actor member * @throws SBonitaReadException * @since 6.3 */ SActorMember getActorMember(long actorId, long userId, long groupId, long roleId) throws SBonitaReadException; } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorCreationException.java ================================================ /** * 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.actor.mapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SActorCreationException extends SBonitaException { private static final long serialVersionUID = -3098152902768681292L; public SActorCreationException(final String message) { super(message); } public SActorCreationException(final Throwable cause) { super(cause); } public SActorCreationException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorDeletionException.java ================================================ /** * 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.actor.mapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SActorDeletionException extends SBonitaException { private static final long serialVersionUID = -7837485664040613405L; public SActorDeletionException(final String message) { super(message); } public SActorDeletionException(final Throwable cause) { super(cause); } public SActorDeletionException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorMemberAlreadyExistsException.java ================================================ /** * 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.actor.mapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SActorMemberAlreadyExistsException extends SBonitaException { private static final long serialVersionUID = 4634806521985418000L; public SActorMemberAlreadyExistsException(final String message) { super(message); } public SActorMemberAlreadyExistsException(final Throwable cause) { super(cause); } public SActorMemberAlreadyExistsException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorMemberCreationException.java ================================================ /** * 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.actor.mapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SActorMemberCreationException extends SBonitaException { private static final long serialVersionUID = -6902253339463831324L; public SActorMemberCreationException(final String message) { super(message); } public SActorMemberCreationException(final Throwable cause) { super(cause); } public SActorMemberCreationException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorMemberDeletionException.java ================================================ /** * 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.actor.mapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SActorMemberDeletionException extends SBonitaException { private static final long serialVersionUID = 7422218038178486137L; public SActorMemberDeletionException(final String message) { super(message); } public SActorMemberDeletionException(final Throwable cause) { super(cause); } public SActorMemberDeletionException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorMemberNotFoundException.java ================================================ /** * 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.actor.mapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SActorMemberNotFoundException extends SBonitaException { private static final long serialVersionUID = -2713597313687259749L; public SActorMemberNotFoundException(final String message) { super(message); } public SActorMemberNotFoundException(final Throwable cause) { super(cause); } public SActorMemberNotFoundException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorNotFoundException.java ================================================ /** * 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.actor.mapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SActorNotFoundException extends SBonitaException { private static final long serialVersionUID = 6582240316506445320L; public SActorNotFoundException(final String message) { super(message); } public SActorNotFoundException(final Throwable cause) { super(cause); } public SActorNotFoundException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/SActorUpdateException.java ================================================ /** * 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.actor.mapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SActorUpdateException extends SBonitaException { private static final long serialVersionUID = -8419335214835063318L; public SActorUpdateException(final String message) { super(message); } public SActorUpdateException(final Throwable cause) { super(cause); } public SActorUpdateException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/impl/ActorMappingServiceImpl.java ================================================ /** * 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.actor.mapping.impl; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.SActorCreationException; import org.bonitasoft.engine.actor.mapping.SActorDeletionException; import org.bonitasoft.engine.actor.mapping.SActorMemberCreationException; import org.bonitasoft.engine.actor.mapping.SActorMemberDeletionException; import org.bonitasoft.engine.actor.mapping.SActorMemberNotFoundException; import org.bonitasoft.engine.actor.mapping.SActorNotFoundException; import org.bonitasoft.engine.actor.mapping.SActorUpdateException; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.actor.mapping.model.SActorLogBuilder; import org.bonitasoft.engine.actor.mapping.model.SActorLogBuilderFactory; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.actor.mapping.persistence.SelectDescriptorBuilder; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SIdentityException; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteAllRecord; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; /** * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class ActorMappingServiceImpl implements ActorMappingService { private static final int BATCH_SIZE = 100; private final ReadPersistenceService persistenceService; private final Recorder recorder; private final EventService eventService; private final QueriableLoggerService queriableLoggerService; private final IdentityService identityService; public ActorMappingServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder, final EventService eventService, final QueriableLoggerService queriableLoggerService, final IdentityService identityService) { this.persistenceService = persistenceService; this.recorder = recorder; this.eventService = eventService; this.queriableLoggerService = queriableLoggerService; this.identityService = identityService; } @Override public Set addActors(final Set actors) throws SActorCreationException { final Set sActors = new HashSet(); for (final SActor actor : actors) { sActors.add(addActor(actor)); } return sActors; } @Override public SActor addActor(final SActor actor) throws SActorCreationException { final SActorLogBuilder logBuilder = getQueriableLog(ActionType.CREATED, "Creating a new actor"); try { recorder.recordInsert(new InsertRecord(actor), ACTOR); initiateLogBuilder(actor.getId(), SQueriableLog.STATUS_OK, logBuilder, "addActor"); return actor; } catch (final SRecorderException re) { initiateLogBuilder(actor.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "addActor"); throw new SActorCreationException(re); } } private SActorLogBuilder getQueriableLog(final ActionType actionType, final String message) { final SActorLogBuilder logBuilder = BuilderFactory.get(SActorLogBuilderFactory.class).createNewInstance(); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); return logBuilder; } private void initializeLogBuilder(final T logBuilder, final String message) { logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message); } private void updateLog(final ActionType actionType, final T logBuilder) { logBuilder.setActionType(actionType); } @Override public SActor getActor(final long actorId) throws SActorNotFoundException, SBonitaReadException { final SelectByIdDescriptor selectByIdDescriptor = SelectDescriptorBuilder.getActor(actorId); final SActor actor = persistenceService.selectById(selectByIdDescriptor); if (actor == null) { throw new SActorNotFoundException(actorId + " does not refer to any actor"); } return actor; } @Override public long getNumberOfUsersOfActor(final long actorId) { final SelectOneDescriptor descriptor = SelectDescriptorBuilder.getNumberOfUsersOfActor(actorId); try { return persistenceService.selectOne(descriptor); } catch (final SBonitaReadException bre) { throw new RuntimeException(bre); } } @Override public long getNumberOfRolesOfActor(final long actorId) { final SelectOneDescriptor descriptor = SelectDescriptorBuilder.getNumberOfRolesOfActor(actorId); try { return persistenceService.selectOne(descriptor); } catch (final SBonitaReadException bre) { throw new RuntimeException(bre); } } @Override public long getNumberOfGroupsOfActor(final long actorId) { final SelectOneDescriptor descriptor = SelectDescriptorBuilder.getNumberOfGroupsOfActor(actorId); try { return persistenceService.selectOne(descriptor); } catch (final SBonitaReadException bre) { throw new RuntimeException(bre); } } @Override public long getNumberOfMembershipsOfActor(final long actorId) { final SelectOneDescriptor descriptor = SelectDescriptorBuilder.getNumberOfMembershipsOfActor(actorId); try { return persistenceService.selectOne(descriptor); } catch (final SBonitaReadException bre) { throw new RuntimeException(bre); } } @Override public List getActors(final List actorIds) throws SBonitaReadException { if (actorIds == null || actorIds.isEmpty()) { return Collections.emptyList(); } return persistenceService.selectList(SelectDescriptorBuilder.getElementsByIds(SActor.class, "Actor", actorIds)); } @Override public SActor getActor(final String actorName, final long scopeId) throws SActorNotFoundException { final SelectOneDescriptor selectOneDescriptor = SelectDescriptorBuilder.getActor(actorName, scopeId); try { final SActor actor = persistenceService.selectOne(selectOneDescriptor); if (actor == null) { throw new SActorNotFoundException( "Actor not found with name: " + actorName + " of scopeId: " + scopeId); } return actor; } catch (final SBonitaReadException bre) { throw new SActorNotFoundException(bre); } } @Override public SActor updateActor(final long actorId, final EntityUpdateDescriptor descriptor) throws SActorNotFoundException, SActorUpdateException, SBonitaReadException { final SActor actor = getActor(actorId); final SActorLogBuilder logBuilder = getQueriableLog(ActionType.UPDATED, "Updating an actor"); try { recorder.recordUpdate(UpdateRecord.buildSetFields(actor, descriptor), ACTOR); initiateLogBuilder(actorId, SQueriableLog.STATUS_OK, logBuilder, "updateActor"); } catch (final SRecorderException e) { initiateLogBuilder(actorId, SQueriableLog.STATUS_FAIL, logBuilder, "updateActor"); throw new SActorUpdateException(e); } return actor; } @Override public void deleteActors(final long scopeId) throws SActorDeletionException { try { final QueryOptions queryOptions = new QueryOptions(0, 100, SActor.class, "id", OrderByType.ASC); List actors = getActors(scopeId, queryOptions); while (!actors.isEmpty()) { for (final SActor actor : actors) { // First delete its members: deleteActorMembers(actor); // then the actor itself: deleteActor(actor); } actors = getActors(scopeId, queryOptions); } } catch (final SBonitaReadException bre) { throw new SActorDeletionException(bre); } catch (final SActorMemberDeletionException e) { throw new SActorDeletionException(e); } } private void deleteActorMembers(final SActor actor) throws SBonitaReadException, SActorMemberDeletionException { List actorMembers; do { actorMembers = getActorMembers(actor.getId(), 0, BATCH_SIZE); for (final SActorMember sActorMember : actorMembers) { deleteActorMember(sActorMember); } } while (actorMembers.size() > 0); } private void deleteActor(final SActor actor) throws SActorDeletionException { final SActorLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, "Deleting an actor"); try { recorder.recordDelete(new DeleteRecord(actor), ACTOR); initiateLogBuilder(actor.getId(), SQueriableLog.STATUS_OK, logBuilder, "deleteActor"); } catch (final SRecorderException re) { initiateLogBuilder(actor.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "deleteActor"); throw new SActorDeletionException(re); } } @Override public List getActors(final Set scopeIds, final Long userId) throws SBonitaReadException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getFullActorsListOfUser(scopeIds, userId); return persistenceService.selectList(descriptor); } @Override public SActorMember addUserToActor(final long actorId, final long userId) throws SActorMemberCreationException { final SActorMember actorMember = new SActorMember(); actorMember.setActorId(actorId); actorMember.setUserId(userId); return addActorMember(actorMember); } private SActorMember addActorMember(final SActorMember actorMember) throws SActorMemberCreationException { final SActorLogBuilder logBuilder = getQueriableLog(ActionType.CREATED, "Creating a new actor member"); try { recorder.recordInsert(new InsertRecord(actorMember), ACTOR_MEMBER); initiateLogBuilder(actorMember.getId(), SQueriableLog.STATUS_OK, logBuilder, "addActorMember"); return actorMember; } catch (final SRecorderException re) { initiateLogBuilder(actorMember.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "addActorMember"); throw new SActorMemberCreationException(re); } } @Override public SActorMember addGroupToActor(final long actorId, final long groupId) throws SActorNotFoundException, SActorMemberCreationException { try { final SActorMember addActorMember = addOnlyThisGroupToActor(actorId, groupId); int i = 0; List groupChildren; do { groupChildren = identityService.getGroupChildren(groupId, i, BATCH_SIZE); for (final SGroup child : groupChildren) { // Only insert if sub-group not mapped already to this Actor : if (getNumberOfActorMembersOfGroupWithActor(child.getId(), actorId) == 0) { addGroupToActor(actorId, child.getId()); } } i += BATCH_SIZE; } while (groupChildren.size() == BATCH_SIZE); return addActorMember; } catch (final SIdentityException e) { throw new SActorMemberCreationException(e); } catch (final SBonitaReadException e) { throw new SActorMemberCreationException(e); } } private SActorMember addOnlyThisGroupToActor(final long actorId, final long groupId) throws SActorMemberCreationException { final SActorMember actorMember = new SActorMember(); actorMember.setActorId(actorId); actorMember.setGroupId(groupId); return addActorMember(actorMember); } @Override public SActorMember addRoleToActor(final long actorId, final long roleId) throws SActorMemberCreationException { final SActorMember actorMember = new SActorMember(); actorMember.setActorId(actorId); actorMember.setRoleId(roleId); return addActorMember(actorMember); } @Override public SActorMember addRoleAndGroupToActor(final long actorId, final long roleId, final long groupId) throws SActorNotFoundException, SActorMemberCreationException { try { final SActorMember addActorMember = addOnlyThisRoleAndGroupToActor(actorId, roleId, groupId); int i = 0; List groupChildren; do { groupChildren = identityService.getGroupChildren(groupId, i, BATCH_SIZE); for (final SGroup child : groupChildren) { addRoleAndGroupToActor(actorId, roleId, child.getId()); } i += BATCH_SIZE; } while (groupChildren.size() == BATCH_SIZE); return addActorMember; } catch (final SIdentityException e) { throw new SActorMemberCreationException(e); } } private SActorMember addOnlyThisRoleAndGroupToActor(final long actorId, final long roleId, final long groupId) throws SActorMemberCreationException { final SActorMember actorMember = new SActorMember(); actorMember.setActorId(actorId); actorMember.setRoleId(roleId); actorMember.setGroupId(groupId); return addActorMember(actorMember); } @Override public SActorMember deleteActorMember(final long actorMemberId) throws SActorMemberNotFoundException, SActorMemberDeletionException { final SActorLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, "Deleting an actor member"); try { final SActorMember actorMember = getActorMember(actorMemberId); deleteActorMember(actorMember); return actorMember; } catch (final SBonitaReadException e) { initiateLogBuilder(actorMemberId, SQueriableLog.STATUS_FAIL, logBuilder, "removeActorMember"); throw new SActorMemberDeletionException(e); } } @Override public void deleteActorMember(final SActorMember sActorMember) throws SActorMemberDeletionException { final SActorLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, "Deleting an actor member"); final long actorMemberId = sActorMember.getId(); try { recorder.recordDelete(new DeleteRecord(sActorMember), ACTOR_MEMBER); initiateLogBuilder(actorMemberId, SQueriableLog.STATUS_OK, logBuilder, "removeActorMember"); } catch (final SRecorderException re) { initiateLogBuilder(actorMemberId, SQueriableLog.STATUS_FAIL, logBuilder, "removeActorMember"); throw new SActorMemberDeletionException(re); } } private SActorMember getActorMember(final long actorMemberId) throws SActorMemberNotFoundException, SBonitaReadException { final SelectByIdDescriptor selectByIdDescriptor = SelectDescriptorBuilder .getActorMember(actorMemberId); final SActorMember actor = persistenceService.selectById(selectByIdDescriptor); if (actor == null) { throw new SActorMemberNotFoundException(actorMemberId + " does not refer to any actor member"); } return actor; } @Override public SActorMember getActorMember(final long actorId, final long userId, final long groupId, final long roleId) throws SBonitaReadException { final SelectOneDescriptor descriptor = SelectDescriptorBuilder.getActorMember(actorId, userId, groupId, roleId); return persistenceService.selectOne(descriptor); } @Override public long getNumberOfActorMembers(final long actorId) throws SBonitaReadException { final SelectOneDescriptor descriptor = SelectDescriptorBuilder.getNumberOfActorMembers(actorId); return persistenceService.selectOne(descriptor); } @Override public List getActorMembers(final long actorId, final int fromIndex, final int numberOfActorMembers) throws SBonitaReadException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getActorMembers(actorId, fromIndex, numberOfActorMembers); return persistenceService.selectList(descriptor); } @Override public List getActorMembersOfUser(final long userId, final int fromIndex, final int numberOfActorMembers) throws SBonitaReadException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getActorMembersOfUser(userId, fromIndex, numberOfActorMembers); return persistenceService.selectList(descriptor); } @Override public List getActorMembersOfGroup(final long groupId, final int fromIndex, final int numberOfActorMembers) throws SBonitaReadException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getActorMembersOfGroup(groupId, fromIndex, numberOfActorMembers); return persistenceService.selectList(descriptor); } private long getNumberOfActorMembersOfGroupWithActor(final long groupId, final long actorId) throws SBonitaReadException { final SelectOneDescriptor descriptor = SelectDescriptorBuilder .getNumberOfActorMembersOfGroupWithActor(groupId, actorId); return persistenceService.selectOne(descriptor); } @Override public List getActorMembersOfRole(final long roleId, final int fromIndex, final int numberOfActorMembers) throws SBonitaReadException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getActorMembersOfRole(roleId, fromIndex, numberOfActorMembers); return persistenceService.selectList(descriptor); } @Override public boolean canUserStartProcessDefinition(final long userId, final long processDefinitionId) throws SBonitaReadException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getActorMembersInitiatorForProcess( processDefinitionId, 0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS); final List actorMembersForProcess = persistenceService.selectList(descriptor); final int BATCH_SIZE = 80; boolean found = false; while (!found && actorMembersForProcess.size() > 0) { found = 0 < persistenceService .selectOne(SelectDescriptorBuilder.getNumberOfUserMembersForUserOrManagerForActorMembers(userId, retrieveFirstResultsAndRemoveFromOriginalList(BATCH_SIZE, actorMembersForProcess))); } return found; } private List retrieveFirstResultsAndRemoveFromOriginalList(int howMany, List ids) { List subList = new ArrayList(howMany); for (int i = 0; i < howMany && ids.size() > 0; i++) { subList.add(ids.remove(0)); } return subList; } @Override public List getActors(final long processDefinitionId, final QueryOptions queryOptions) throws SBonitaReadException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getActorsOfScope(processDefinitionId, queryOptions); return persistenceService.selectList(descriptor); } private void initiateLogBuilder(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder, final String callerMethodName) { logBuilder.actionScope(String.valueOf(objectId)); logBuilder.actionStatus(sQueriableLogStatus); logBuilder.objectId(objectId); final SQueriableLog log = logBuilder.build(); if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) { queriableLoggerService.log(this.getClass().getName(), callerMethodName, log); } } @Override public void deleteAllActorMembers() throws SActorMemberDeletionException { try { final DeleteAllRecord record = new DeleteAllRecord(SActorMember.class, null); recorder.recordDeleteAll(record); } catch (final SRecorderException e) { throw new SActorMemberDeletionException("Can't delete all actor members.", e); } } @Override public List getPossibleUserIdsOfActorId(final long actorId, final int startIndex, final int maxResults) throws SBonitaReadException { final Map parameters = Collections.singletonMap("actorId", (Object) actorId); final SelectListDescriptor descriptor = new SelectListDescriptor("getPossibleUserIdsOfActorId", parameters, SActor.class, new QueryOptions( startIndex, maxResults)); return persistenceService.selectList(descriptor); } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/impl/SActorLogBuilderFactoryImpl.java ================================================ /** * 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.actor.mapping.impl; import org.bonitasoft.engine.actor.mapping.model.SActorLogBuilder; import org.bonitasoft.engine.actor.mapping.model.SActorLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Matthieu Chaffotte */ public class SActorLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SActorLogBuilderFactory { public SActorLogBuilder createNewInstance() { return new SActorLogBuilderImpl(); } @Override public String getObjectIdKey() { return "numericIndex1"; } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/impl/SActorLogBuilderImpl.java ================================================ /** * 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.actor.mapping.impl; import org.bonitasoft.engine.actor.mapping.model.SActorLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Matthieu Chaffotte */ public class SActorLogBuilderImpl extends CRUDELogBuilder implements SActorLogBuilder { private static final int ACTOR_INDEX = 0; @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(ACTOR_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return "ACTOR"; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(ACTOR_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + "Actor Id"); } } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SActor.java ================================================ /** * 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.actor.mapping.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "actor") public class SActor implements PersistentObject { @Id private long id; @Column private long scopeId; @Column private String name; @Column private String displayName; @Column private String description; @Column private boolean initiator; public SActor(final String name, final long scopeId, final boolean initiator) { this.name = name; this.scopeId = scopeId; this.initiator = initiator; } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SActorFilter.java ================================================ /** * 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.actor.mapping.model; /** * @author Baptiste Mesta */ public interface SActorFilter { } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SActorLogBuilder.java ================================================ /** * 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.actor.mapping.model; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Matthieu Chaffotte */ public interface SActorLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SActorLogBuilderFactory.java ================================================ /** * 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.actor.mapping.model; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Matthieu Chaffotte */ public interface SActorLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { @Override SActorLogBuilder createNewInstance(); } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SActorMember.java ================================================ /** * 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.actor.mapping.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @Entity @Table(name = "actormember") public class SActorMember implements PersistentObject { @Id private long id; @Column private long actorId; @Column private long userId = -1; @Column private long groupId = -1; @Column private long roleId = -1; } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SActorUpdateBuilder.java ================================================ /** * 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.actor.mapping.model; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Matthieu Chaffotte */ public interface SActorUpdateBuilder { EntityUpdateDescriptor done(); SActorUpdateBuilder updateDisplayName(final String displayName); SActorUpdateBuilder updateDescription(final String description); } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SActorUpdateBuilderFactory.java ================================================ /** * 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.actor.mapping.model; /** * @author Matthieu Chaffotte */ public interface SActorUpdateBuilderFactory { SActorUpdateBuilder createNewInstance(); } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/SMemberType.java ================================================ /** * 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.actor.mapping.model; /** * @author Matthieu Chaffotte */ public enum SMemberType { USER, ROLE, GROUP, MEMBERSHIP; } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/impl/SActorUpdateBuilderFactoryImpl.java ================================================ /** * 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.actor.mapping.model.impl; import org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilder; import org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Matthieu Chaffotte */ public class SActorUpdateBuilderFactoryImpl implements SActorUpdateBuilderFactory { public SActorUpdateBuilder createNewInstance() { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); return new SActorUpdateBuilderImpl(descriptor); } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/model/impl/SActorUpdateBuilderImpl.java ================================================ /** * 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.actor.mapping.model.impl; import org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Matthieu Chaffotte */ public class SActorUpdateBuilderImpl implements SActorUpdateBuilder { private final EntityUpdateDescriptor descriptor; public SActorUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) { super(); this.descriptor = descriptor; } @Override public EntityUpdateDescriptor done() { return this.descriptor; } @Override public SActorUpdateBuilder updateDisplayName(final String displayName) { this.descriptor.addField("displayName", displayName); return this; } @Override public SActorUpdateBuilder updateDescription(final String description) { this.descriptor.addField("description", description); return this; } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/java/org/bonitasoft/engine/actor/mapping/persistence/SelectDescriptorBuilder.java ================================================ /** * 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.actor.mapping.persistence; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class SelectDescriptorBuilder { public static SelectByIdDescriptor getActor(final long actorId) { return new SelectByIdDescriptor<>(SActor.class, actorId); } public static SelectOneDescriptor getActor(final String actorName, final long scopeId) { final Map parameters = new HashMap<>(); parameters.put("name", actorName); parameters.put("scopeId", scopeId); return new SelectOneDescriptor<>("getActorFromNameAndScopeId", parameters, SActor.class); } public static SelectByIdDescriptor getActorMember(final long actorMemberId) { return new SelectByIdDescriptor<>(SActorMember.class, actorMemberId); } public static SelectOneDescriptor getActorMember(final long actorId, final long userId, final long groupId, final long roleId) { final Map parameters = new HashMap<>(); parameters.put("actorId", actorId); parameters.put("userId", userId); parameters.put("groupId", groupId); parameters.put("roleId", roleId); return new SelectOneDescriptor<>("getActorMember", parameters, SActorMember.class); } public static SelectListDescriptor getActorMembers(final int fromIndex, final int numberOfElements) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements); final Map parameters = Collections.emptyMap(); return new SelectListDescriptor<>("getActorMembers", parameters, SActorMember.class, queryOptions); } public static SelectListDescriptor getActorMembers(final long actorId, final int fromIndex, final int numberOfElements) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements); final Map parameters = Collections.singletonMap("actorId", (Object) actorId); return new SelectListDescriptor<>("getActorMembersOfActor", parameters, SActorMember.class, queryOptions); } public static SelectListDescriptor getActorMembersOfGroup(final long groupId, final int fromIndex, final int numberOfActorMembers) { final Map parameters = new HashMap<>(); parameters.put("groupId", groupId); final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfActorMembers); return new SelectListDescriptor<>("getActorMembersOfGroup", parameters, SActorMember.class, queryOptions); } public static SelectListDescriptor getActorMembersOfRole(final long roleId, final int fromIndex, final int numberOfActorMembers) { final Map parameters = new HashMap<>(); parameters.put("roleId", roleId); final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfActorMembers); return new SelectListDescriptor<>("getActorMembersOfRole", parameters, SActorMember.class, queryOptions); } public static SelectListDescriptor getActorMembersOfUser(final long userId, final int fromIndex, final int numberOfActorMembers) { final Map parameters = new HashMap<>(); parameters.put("userId", userId); final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfActorMembers); return new SelectListDescriptor<>("getActorMembersOfUser", parameters, SActorMember.class, queryOptions); } public static SelectListDescriptor getActorMembersInitiatorForProcess(final long processDefinitionId, final int index, final int numberPerPage) { final Map parameters = new HashMap<>(1); parameters.put("processDefinitionId", processDefinitionId); final QueryOptions queryOptions = new QueryOptions(index, numberPerPage, SActorMember.class, "id", OrderByType.ASC); return new SelectListDescriptor<>("getActorMembersInitiatorForProcess", parameters, SActorMember.class, queryOptions); } public static SelectOneDescriptor getNumberOfUserMembersForUserOrManagerForActorMembers(final long userId, final List actorMemberIds) { final Map parameters = new HashMap<>(2); parameters.put("userId", userId); parameters.put("actorMemberIds", actorMemberIds); return new SelectOneDescriptor<>("getNumberOfUserMembersForUserOrManagerForActorMembers", parameters, SUserMembership.class); } public static SelectListDescriptor getActorsOfScope(final long scopeId, final QueryOptions queryOptions) { final Map parameters = Collections.singletonMap("scopeId", (Object) scopeId); return new SelectListDescriptor<>("getActorsOfScope", parameters, SActor.class, queryOptions); } public static SelectListDescriptor getElementsByIds(final Class clazz, final String elementName, final Collection ids) { final QueryOptions queryOptions = new QueryOptions(0, ids.size(), clazz, "id", OrderByType.ASC); final Map parameters = Collections.singletonMap("ids", (Object) ids); return new SelectListDescriptor<>("get" + elementName + "sByIds", parameters, clazz, queryOptions); } public static SelectListDescriptor getFullActorsListOfUser(final Set scopeIds, final long userId) { final Map parameters = new HashMap<>(); parameters.put("scopeIds", scopeIds); parameters.put("userId", userId); final QueryOptions queryOptions = new QueryOptions(SActor.class, "name", OrderByType.ASC); return new SelectListDescriptor<>("getActorsOfUser", parameters, SActor.class, queryOptions); } public static SelectOneDescriptor getNumberOfActorMembers(final long actorId) { final Map parameters = Collections.singletonMap("actorId", (Object) actorId); return new SelectOneDescriptor<>("getNumberOfActorMembersOfActor", parameters, SActorMember.class); } public static SelectOneDescriptor getNumberOfActorMembersOfGroupWithActor(final long groupId, final long actorId) { final Map parameters = new HashMap<>(2); parameters.put("groupId", groupId); parameters.put("actorId", actorId); return new SelectOneDescriptor<>("getNumberOfActorMembersOfGroupWithActor", parameters, SActorMember.class); } public static SelectOneDescriptor getNumberOfGroupsOfActor(final long actorId) { final Map parameters = Collections.singletonMap("actorId", (Object) actorId); return new SelectOneDescriptor<>("getNumberOfGroupsOfActor", parameters, SActorMember.class); } public static SelectOneDescriptor getNumberOfMembershipsOfActor(final long actorId) { final Map parameters = Collections.singletonMap("actorId", (Object) actorId); return new SelectOneDescriptor<>("getNumberOfMembershipsOfActor", parameters, SActorMember.class); } public static SelectOneDescriptor getNumberOfRolesOfActor(final long actorId) { final Map parameters = Collections.singletonMap("actorId", (Object) actorId); return new SelectOneDescriptor<>("getNumberOfRolesOfActor", parameters, SActorMember.class); } public static SelectOneDescriptor getNumberOfUsersOfActor(final long actorId) { final Map parameters = Collections.singletonMap("actorId", (Object) actorId); return new SelectOneDescriptor<>("getNumberOfUsersOfActor", parameters, SActorMember.class); } } ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/main/resources/org/bonitasoft/engine/actor/mapping/model/impl/hibernate/actor.queries.hbm.xml ================================================ SELECT actor FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor WHERE actor.id IN (:ids) SELECT actor FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor WHERE actor.scopeId = :scopeId AND actor.name = :name SELECT actor FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor WHERE actor.scopeId = :scopeId SELECT COUNT(actormember.id) FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actormember.actorId = :actorId SELECT actormember FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actormember.actorId = :actorId ORDER BY actormember.id ASC SELECT actormember FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actormember.actorId = :actorId AND actormember.userId = :userId AND actormember.groupId = :groupId AND actormember.roleId = :roleId ORDER BY actormember.id ASC SELECT actormember FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember ORDER BY actormember.id ASC SELECT actor FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND actor.scopeId IN (:scopeIds) AND ( actormember.userId = :userId OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) SELECT COUNT(user.id) FROM org.bonitasoft.engine.identity.model.SUser AS user, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actormember.actorId = :actorId AND user.id = actormember.userId SELECT COUNT(role.id) FROM org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actormember.actorId = :actorId AND role.id = actormember.roleId AND actormember.groupId = -1 SELECT COUNT(group_.id) FROM org.bonitasoft.engine.identity.model.SGroup AS group_, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actormember.actorId = :actorId AND group_.id = actormember.groupId AND actormember.roleId = -1 SELECT COUNT(actormember.id) FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actormember.actorId = :actorId AND actormember.userId = -1 AND actormember.groupId > -1 AND actormember.roleId > -1 SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND actor.scopeId =:processDefinitionId AND actor.initiator = TRUE SELECT count(*) FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS am, org.bonitasoft.engine.identity.model.SUser AS user WHERE (user.id = :userId OR user.managerUserId = :userId) AND am.id IN (:actorMemberIds) AND ( am.userId = user.id OR exists ( SELECT um.id FROM org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = user.id AND ( (am.groupId = um.groupId AND am.roleId <= 0) OR (am.roleId = um.roleId AND am.groupId <= 0) OR (am.groupId = um.groupId AND am.roleId = um.roleId) ) ) ) SELECT actormember FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actormember.userId = :userId ORDER BY actormember.id ASC SELECT actormember FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actormember.groupId = :groupId ORDER BY actormember.id ASC SELECT count(actormember.id) FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actormember.actorId = :actorId AND actormember.groupId = :groupId AND actormember.roleId = -1 AND actormember.userId = -1 SELECT actormember FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actormember.roleId = :roleId ORDER BY actormember.id ASC SELECT user_.id, user_.username FROM ( SELECT user_.id, user_.username FROM user_, actormember, actor WHERE actor.id = :actorId AND actor.id = actormember.actorId AND actormember.userId = user_.id UNION ( SELECT user_.id, user_.username FROM user_, actormember, actor, user_membership um WHERE actor.id = :actorId AND um.userId = user_.id AND actor.id = actormember.actorId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) user_ ORDER BY user_.username ================================================ FILE: bpm/bonita-core/bonita-actor-mapping/src/test/java/org/bonitasoft/engine/actor/mapping/impl/ActorMappingServiceImplTest.java ================================================ /** * 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.actor.mapping.impl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.actor.mapping.SActorCreationException; import org.bonitasoft.engine.actor.mapping.SActorDeletionException; import org.bonitasoft.engine.actor.mapping.SActorMemberDeletionException; import org.bonitasoft.engine.actor.mapping.SActorNotFoundException; import org.bonitasoft.engine.actor.mapping.SActorUpdateException; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilder; import org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilderFactory; import org.bonitasoft.engine.actor.mapping.persistence.SelectDescriptorBuilder; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteAllRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class ActorMappingServiceImplTest { @Mock private Recorder recorder; @Mock private ReadPersistenceService persistenceService; @Mock private EventService eventService; @Mock private QueriableLoggerService queriableLoggerService; @Mock private IdentityService identityService; @InjectMocks private ActorMappingServiceImpl actorMappingServiceImpl; /** * Test method for {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActor(long)}. * * @throws SBonitaReadException * @throws SActorNotFoundException */ @Test public final void getActorById() throws SActorNotFoundException, SBonitaReadException { final SActor actor = mock(SActor.class); when(persistenceService.selectById(ArgumentMatchers.> any())).thenReturn(actor); Assert.assertEquals(actor, actorMappingServiceImpl.getActor(456L)); } @Test(expected = SActorNotFoundException.class) public final void getActorByIdNotExists() throws SBonitaReadException, SActorNotFoundException { when(persistenceService.selectById(ArgumentMatchers.> any())).thenReturn(null); actorMappingServiceImpl.getActor(456L); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getNumberOfActorMembers(long)}. * * @throws SBonitaReadException */ @Test public final void getNumberOfActorMembers() throws SBonitaReadException { final long actorId = 456L; final long numberOfActorMemebers = 1L; when(persistenceService.selectOne(ArgumentMatchers.> any())) .thenReturn(numberOfActorMemebers); Assert.assertEquals(numberOfActorMemebers, actorMappingServiceImpl.getNumberOfActorMembers(actorId)); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getNumberOfUsersOfActor(long)}. * * @throws SBonitaReadException */ @Test public final void getNumberOfUsersOfActor() throws SBonitaReadException { final long numberOfUsersOfActor = 155L; when(persistenceService.selectOne(ArgumentMatchers.> any())) .thenReturn(numberOfUsersOfActor); Assert.assertEquals(numberOfUsersOfActor, actorMappingServiceImpl.getNumberOfUsersOfActor(456L)); } @Test(expected = RuntimeException.class) public final void getNumberOfUsersOfActorThrowException() throws SBonitaReadException { when(persistenceService.selectOne(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); actorMappingServiceImpl.getNumberOfUsersOfActor(456L); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getNumberOfRolesOfActor(long)}. * * @throws SBonitaReadException */ @Test public final void getNumberOfRolesOfActor() throws SBonitaReadException { final long numberOfRolesOfActor = 155L; when(persistenceService.selectOne(ArgumentMatchers.> any())) .thenReturn(numberOfRolesOfActor); Assert.assertEquals(numberOfRolesOfActor, actorMappingServiceImpl.getNumberOfRolesOfActor(456L)); } @Test(expected = RuntimeException.class) public final void getNumberOfRolesOfActorThrowException() throws SBonitaReadException { when(persistenceService.selectOne(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); actorMappingServiceImpl.getNumberOfRolesOfActor(456L); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getNumberOfGroupsOfActor(long)}. * * @throws SBonitaReadException */ @Test public final void getNumberOfGroupsOfActor() throws SBonitaReadException { final long numberOfGroupsOfActor = 155L; when(persistenceService.selectOne(ArgumentMatchers.> any())) .thenReturn(numberOfGroupsOfActor); Assert.assertEquals(numberOfGroupsOfActor, actorMappingServiceImpl.getNumberOfGroupsOfActor(456L)); } @Test(expected = RuntimeException.class) public final void getNumberOfGroupsOfActorThrowException() throws SBonitaReadException { when(persistenceService.selectOne(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); actorMappingServiceImpl.getNumberOfGroupsOfActor(456L); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getNumberOfMembershipsOfActor(long)}. * * @throws SBonitaReadException */ @Test public final void getNumberOfMembershipsOfActor() throws SBonitaReadException { final long numberOfGroupsOfActor = 155L; when(persistenceService.selectOne(ArgumentMatchers.> any())) .thenReturn(numberOfGroupsOfActor); Assert.assertEquals(numberOfGroupsOfActor, actorMappingServiceImpl.getNumberOfMembershipsOfActor(456L)); } @Test(expected = RuntimeException.class) public final void getNumberOfMembershipsOfActorThrowException() throws SBonitaReadException { when(persistenceService.selectOne(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); actorMappingServiceImpl.getNumberOfMembershipsOfActor(456L); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActor(java.lang.String, long)}. * * @throws SBonitaReadException * @throws SActorNotFoundException */ @Test public final void getActorByNameAndScopeId() throws SActorNotFoundException, SBonitaReadException { final SActor actor = mock(SActor.class); when(persistenceService.selectOne(ArgumentMatchers.> any())).thenReturn(actor); Assert.assertEquals(actor, actorMappingServiceImpl.getActor("actorName", 69L)); } @Test(expected = SActorNotFoundException.class) public final void getActorByNameAndScopeIdNotExists() throws SActorNotFoundException, SBonitaReadException { when(persistenceService.selectOne(ArgumentMatchers.> any())).thenReturn(null); actorMappingServiceImpl.getActor("actorName", 69L); } @Test(expected = SActorNotFoundException.class) public final void getActorByNameAndScopeIdThrowException() throws SActorNotFoundException, SBonitaReadException { when(persistenceService.selectOne(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); actorMappingServiceImpl.getActor("actorName", 69L); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActorMembers(long, int, int)}. * * @throws SBonitaReadException */ @Test public final void getActorMembersByActorPaginated() throws SBonitaReadException { final List actors = new ArrayList(); when(persistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(actors); Assert.assertEquals(actors, actorMappingServiceImpl.getActorMembers(4115L, 0, 1)); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActorMembersOfGroup(long, int, int)}. * * @throws SBonitaReadException */ @Test public final void getActorMembersOfGroup() throws SBonitaReadException { final List actors = new ArrayList(6); when(persistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(actors); Assert.assertEquals(actors, actorMappingServiceImpl.getActorMembersOfGroup(41L, 0, 1)); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActorMembersOfRole(long, int, int)}. * * @throws SBonitaReadException */ @Test public final void getActorMembersOfRole() throws SBonitaReadException { final List actors = new ArrayList(3); when(persistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(actors); Assert.assertEquals(actors, actorMappingServiceImpl.getActorMembersOfRole(41L, 0, 1)); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActorMember(long, long, long, long)}. */ @Test public final void getActorMember() throws SBonitaReadException { // Given final SActorMember actor = mock(SActorMember.class); final long actorId = 1L; final long userId = 2L; final long groupId = 3L; final long roleId = 4L; when(persistenceService.selectOne(SelectDescriptorBuilder.getActorMember(actorId, userId, groupId, roleId))) .thenReturn(actor); // When final SActorMember sActorMember = actorMappingServiceImpl.getActorMember(actorId, userId, groupId, roleId); // Then Assert.assertEquals(actor, sActorMember); } @Test(expected = SBonitaReadException.class) public final void getActorMemberThrowException() throws SBonitaReadException { // Given final long actorId = 1L; final long userId = 2L; final long groupId = 3L; final long roleId = 4L; when(persistenceService.selectOne(SelectDescriptorBuilder.getActorMember(actorId, userId, groupId, roleId))) .thenThrow(new SBonitaReadException("plop")); // When actorMappingServiceImpl.getActorMember(actorId, userId, groupId, roleId); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActors(java.util.List)}. * * @throws SBonitaReadException */ @Test public final void getActorsByListOfIds() throws SBonitaReadException { final List actors = new ArrayList(3); when(persistenceService.selectList(ArgumentMatchers.> any())).thenReturn(actors); final List actorIds = new ArrayList(1); actorIds.add(589L); Assert.assertEquals(actors, actorMappingServiceImpl.getActors(actorIds)); } @Test public final void getActorsByListOfIdsWithEmptyList() throws SBonitaReadException { Assert.assertEquals(Collections.emptyList(), actorMappingServiceImpl.getActors(new ArrayList(0))); } @Test public final void getActorsByListOfIdsWithNullList() throws SBonitaReadException { Assert.assertEquals(Collections.emptyList(), actorMappingServiceImpl.getActors(null)); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActors(long, org.bonitasoft.engine.persistence.QueryOptions)} * . * * @throws SBonitaReadException */ @Test public final void getActors() throws SBonitaReadException { final QueryOptions queryOptions = new QueryOptions(0, 100, SActor.class, "id", OrderByType.ASC); final List actors = new ArrayList(3); when(persistenceService.selectList(ArgumentMatchers.> any())).thenReturn(actors); Assert.assertEquals(actors, actorMappingServiceImpl.getActors(41564L, queryOptions)); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#canUserStartProcessDefinition(long, long)}. * * @throws SBonitaReadException */ @Test public final void shouldBeAllowedToStartProcessDefinition() throws SBonitaReadException { final List actorMembers = new ArrayList(1); actorMembers.add(123L); when(persistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(actorMembers); when(persistenceService.selectOne(ArgumentMatchers.> any())).thenReturn(3L); Assertions.assertThat(actorMappingServiceImpl.canUserStartProcessDefinition(315L, 5484L)) .as("Should be allowed to start Process").isTrue(); } @Test public final void shouldNotBeAllowedToStartProcessDefinitionIfNoActorMembers() throws SBonitaReadException { final List actorMembers = new ArrayList(0); when(persistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(actorMembers); Assertions.assertThat(actorMappingServiceImpl.canUserStartProcessDefinition(315L, 5484L)) .as("Should NOT be allowed to start Process").isFalse(); } @Test public final void shouldNotBeAllowedToStartProcessDefinitionIfNoUserMemberships() throws SBonitaReadException { final List actorMembers = new ArrayList(1); actorMembers.add(123L); when(persistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(actorMembers); when(persistenceService.selectOne(ArgumentMatchers.> any())).thenReturn(0L); Assertions.assertThat(actorMappingServiceImpl.canUserStartProcessDefinition(315L, 5484L)) .as("Should NOT be allowed to start Process").isFalse(); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#getActors(java.util.Set, java.lang.Long)}. * * @throws SBonitaReadException */ @Test public final void getActorsByScopeIdsAndUserId() throws SBonitaReadException { final List actors = new ArrayList(3); when(persistenceService.selectList(ArgumentMatchers.> any())).thenReturn(actors); Assert.assertEquals(actors, actorMappingServiceImpl.getActors(new HashSet(), 5484L)); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#addActors(java.util.Set)}. * * @throws SActorCreationException */ @Test public final void addActors() throws SActorCreationException { final Set actors = new HashSet(); actors.add(mock(SActor.class)); final ActorMappingServiceImpl mockedActorMappingServiceImpl = mock(ActorMappingServiceImpl.class, withSettings().spiedInstance(actorMappingServiceImpl)); final SActor sActor = mock(SActor.class); when(mockedActorMappingServiceImpl.addActor(any(SActor.class))).thenReturn(sActor); // Let's call it for real: doCallRealMethod().when(mockedActorMappingServiceImpl).addActors(actors); final Set result = mockedActorMappingServiceImpl.addActors(actors); assertNotNull(result); assertEquals(1, result.size()); assertEquals(sActor, result.toArray()[0]); // and check methods are called: verify(mockedActorMappingServiceImpl, times(1)).addActor(any(SActor.class)); } @Test public final void addActorsEmptyList() throws SActorCreationException { final Set actors = new HashSet(); final Set result = actorMappingServiceImpl.addActors(actors); assertNotNull(result); assertEquals(0, result.size()); } @Test(expected = SActorCreationException.class) public final void addActorsThrowException() throws SActorCreationException { final Set actors = new HashSet(); actors.add(mock(SActor.class)); final ActorMappingServiceImpl mockedActorMappingServiceImpl = mock(ActorMappingServiceImpl.class, withSettings().spiedInstance(actorMappingServiceImpl)); when(mockedActorMappingServiceImpl.addActor(any(SActor.class))).thenThrow(new SActorCreationException("")); // Let's call it for real: doCallRealMethod().when(mockedActorMappingServiceImpl).addActors(actors); mockedActorMappingServiceImpl.addActors(actors); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#addActor(org.bonitasoft.engine.actor.mapping.model.SActor)}. * * @throws Exception */ @Test public final void addActor() throws Exception { final SActor sActor = mock(SActor.class); doReturn(1L).when(sActor).getId(); final SActor result = actorMappingServiceImpl.addActor(sActor); assertNotNull(result); assertEquals(sActor, result); } @Test(expected = IllegalArgumentException.class) public final void addNullActor() throws Exception { actorMappingServiceImpl.addActor(null); } /** * Test method for * {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#updateActor(long, org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor)}. * * @throws SBonitaReadException * @throws SActorUpdateException * @throws SActorNotFoundException * @throws SRecorderException */ @Test public final void updateActor() throws SActorNotFoundException, SActorUpdateException, SBonitaReadException { final SActor sActor = mock(SActor.class); final SActorUpdateBuilder sActorUpdateBuilder = BuilderFactory.get(SActorUpdateBuilderFactory.class) .createNewInstance(); sActorUpdateBuilder.updateDescription("newDescription"); sActorUpdateBuilder.updateDisplayName("newDisplayName"); doReturn(sActor).when(persistenceService).selectById(ArgumentMatchers.> any()); final SActor result = actorMappingServiceImpl.updateActor(3, sActorUpdateBuilder.done()); assertNotNull(result); assertEquals(sActor, result); } @Test(expected = SActorNotFoundException.class) public final void updateActorNotExists() throws SActorUpdateException, SActorNotFoundException, SBonitaReadException { final SActorUpdateBuilder sActorUpdateBuilder = BuilderFactory.get(SActorUpdateBuilderFactory.class) .createNewInstance(); doReturn(null).when(persistenceService).selectById(ArgumentMatchers.> any()); actorMappingServiceImpl.updateActor(4, sActorUpdateBuilder.done()); } @Test(expected = SActorUpdateException.class) public final void updateActorThrowException() throws SActorUpdateException, SActorNotFoundException, SBonitaReadException, SRecorderException { final SActor sActor = mock(SActor.class); final SActorUpdateBuilder sActorUpdateBuilder = BuilderFactory.get(SActorUpdateBuilderFactory.class) .createNewInstance(); sActorUpdateBuilder.updateDescription("newDescription"); sActorUpdateBuilder.updateDisplayName("newDisplayName"); doReturn(sActor).when(persistenceService).selectById(any()); doThrow(new SRecorderException("plop")).when(recorder).recordUpdate(any(UpdateRecord.class), nullable(String.class)); actorMappingServiceImpl.updateActor(3, sActorUpdateBuilder.done()); } /** * Test method for {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#deleteActors(long)}. * * @throws Exception */ @Test public final void deleteActors() throws Exception { final int scopeId = 9; final SActor sActor = mock(SActor.class); doReturn(3L).when(sActor).getId(); final List sActorMembers = new ArrayList(); final SActorMember sActorMember = mock(SActorMember.class); doReturn(4L).when(sActorMember).getId(); sActorMembers.add(sActorMember); doReturn(Arrays.asList(sActor)).doReturn(new ArrayList()).when(persistenceService) .selectList(ArgumentMatchers.> any()); doReturn(sActorMembers).doReturn(new ArrayList()).when(persistenceService) .selectList(SelectDescriptorBuilder.getActorMembers(3, 0, 50)); actorMappingServiceImpl.deleteActors(scopeId); } @Test public final void deleteNoActorMembers() throws SBonitaReadException, SRecorderException, SActorDeletionException { final int scopeId = 9; final SActor sActor = mock(SActor.class); doReturn(3L).when(sActor).getId(); final List sActorMembers = new ArrayList(); doReturn(Arrays.asList(sActor)).doReturn(new ArrayList()).when(persistenceService) .selectList(ArgumentMatchers.> any()); doReturn(sActorMembers).when(persistenceService).selectList(SelectDescriptorBuilder.getActorMembers(3, 0, 50)); actorMappingServiceImpl.deleteActors(scopeId); } @Test public final void deleteNoActors() throws SBonitaReadException, SRecorderException, SActorDeletionException { final int scopeId = 9; doReturn(new ArrayList()).when(persistenceService) .selectList(ArgumentMatchers.> any()); actorMappingServiceImpl.deleteActors(scopeId); } /** * Test method for {@link org.bonitasoft.engine.actor.mapping.impl.ActorMappingServiceImpl#deleteAllActorMembers()}. * * @throws SRecorderException * @throws SActorMemberDeletionException */ @Test public final void deleteAllActorMembers() throws SRecorderException, SActorMemberDeletionException { doNothing().when(recorder).recordDeleteAll(any(DeleteAllRecord.class)); actorMappingServiceImpl.deleteAllActorMembers(); } @Test(expected = SActorMemberDeletionException.class) public final void deleteAllActorMembersThrowException() throws SRecorderException, SActorMemberDeletionException { doThrow(new SRecorderException("plop")).when(recorder).recordDeleteAll(any(DeleteAllRecord.class)); actorMappingServiceImpl.deleteAllActorMembers(); } } ================================================ FILE: bpm/bonita-core/bonita-category/build.gradle ================================================ dependencies { api project(':services:bonita-session') api project(':services:bonita-commons') api project(':services:bonita-builder') api project(':services:bonita-events') api project(':services:bonita-log') api project(':services:bonita-persistence') testImplementation libs.mockitoCore annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/CategoryService.java ================================================ /** * 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.core.category; import java.util.List; import org.bonitasoft.engine.core.category.exception.SCategoryAlreadyExistsException; import org.bonitasoft.engine.core.category.exception.SCategoryCreationException; import org.bonitasoft.engine.core.category.exception.SCategoryDeletionException; import org.bonitasoft.engine.core.category.exception.SCategoryException; import org.bonitasoft.engine.core.category.exception.SCategoryInProcessAlreadyExistsException; import org.bonitasoft.engine.core.category.exception.SCategoryNotFoundException; import org.bonitasoft.engine.core.category.exception.SIndexOutOfRangeException; import org.bonitasoft.engine.core.category.exception.SPageOutOfRangeException; import org.bonitasoft.engine.core.category.model.SCategory; import org.bonitasoft.engine.core.category.model.SProcessCategoryMapping; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Yanyan Liu * @author Matthieu Chaffotte * @author Celine Souchet * @since 6.0 */ public interface CategoryService { String CATEGORY = "CATEGORY"; /** * Create a category by give name and description * * @param name * The name of category * @param description * The description of category * @return the category object with id * @throws SCategoryAlreadyExistsException * Error thrown if category is already exist * @throws SCategoryCreationException * Error thrown if has exceptions during the category creation. */ SCategory createCategory(String name, String description) throws SCategoryAlreadyExistsException, SCategoryCreationException; /** * Get category by its id * * @param id * Identifier of the category * @return a category object * @throws SCategoryNotFoundException * Error thrown if no category have an id corresponding to the parameter. */ SCategory getCategory(long id) throws SCategoryNotFoundException; /** * Get category by its name * * @param name * Name of the category * @return a category object * @throws SCategoryNotFoundException * Error thrown if no category have a name corresponding to the parameter. */ SCategory getCategoryByName(String name) throws SCategoryNotFoundException; /** * Update a category by its id * * @param categoryId * Identifier of the category * @param newCategory * An category object used to update the categoryId specified category * @throws SCategoryNotFoundException * Error thrown if no category have an id corresponding to the parameter. * @throws SCategoryException * Error thrown if has exception during the category update */ void updateCategory(long categoryId, EntityUpdateDescriptor descriptor) throws SCategoryNotFoundException, SCategoryException; /** * Delete a category by its id * * @param categoryId * Identifier of the category * @throws SCategoryNotFoundException * Error thrown if no category have an id corresponding to the parameter. * @throws SCategoryDeletionException * Error thrown if has exception during the category deletion */ void deleteCategory(long categoryId) throws SCategoryNotFoundException, SCategoryDeletionException; /** * Get the total number of categories * * @return The total number of Categories * @throws SCategoryException */ long getNumberOfCategories() throws SCategoryException; /** * Retrieves a list of categories, The returned list is paginated * * @param fromIndex * Index of the record to be returned. First record has index 0. * @param numberOfCategories * Number of categories per page. Maximum number of categories returned. * @param field * The field used by the list order * @param order * ASC or DESC * @return The list of category objects * @throws SPageOutOfRangeException * Error thrown if page is out of the range. * @throws SCategoryException * Error thrown if has exception during the category retrieve */ List getCategories(int fromIndex, int numberOfCategories, String field, OrderByType order) throws SPageOutOfRangeException, SCategoryException; /** * Add a process definition to a category * * @param categoryId * Identifier of the category * @param processDefinitionId * Identifier of the process definition * @throws SCategoryNotFoundException * Error thrown if no category have an id corresponding to the parameter. * @throws SCategoryException * Error thrown if has exception during the adding action */ void addProcessDefinitionToCategory(long categoryId, long processDefinitionId) throws SCategoryNotFoundException, SCategoryInProcessAlreadyExistsException, SCategoryException; /** * Add process definitions to a category * * @param categoryId * Identifier of the category * @param processDefinitionIds * Identifier of the process definition * @throws SCategoryNotFoundException * Error thrown if no category have an id corresponding to the parameter. * @throws SCategoryException * Error thrown if has exception during the adding action * @throws SCategoryInProcessAlreadyExistsException */ void addProcessDefinitionsToCategory(long categoryId, List processDefinitionIds) throws SCategoryNotFoundException, SCategoryException, SCategoryInProcessAlreadyExistsException; /** * Get number of categories of the specific process definition * * @param processDefinitionId * Identifier of the process definition * @return number of categories * @throws SCategoryException * Error thrown if has exception during the category number retrieve action */ long getNumberOfCategoriesOfProcess(long processDefinitionId) throws SCategoryException; /** * Get categories for specific process definition, the result list is paginated * * @param processId * Identifier of the process definition * @param fromIndex * Start index of satisfied record * @param numberOfCategories * Number of categories per page. Maximum number of categories returned. * @param order * Criterion for order, default order by name * @return The matching list of category * @throws SCategoryException * Error thrown if has exception during the category retrieve action * @throws SIndexOutOfRangeException * Error thrown if index is out of the range. */ List getCategoriesOfProcessDefinition(long processId, int fromIndex, int numberOfCategories, OrderByType order) throws SCategoryException, SIndexOutOfRangeException; /** * Get number of categorized processes * * @param processIds * Identifier of the process definition * @return the number of categorized processes * @throws SCategoryException */ long getNumberOfCategorizedProcessIds(List processIds) throws SCategoryException; /** * Get the number of process definition for specific category * * @param categoryId * Identifier of the category * @return The number of process definition for specific category * @throws SCategoryNotFoundException * Error thrown if no category have an id corresponding to the parameter. * @throws SCategoryException * Error thrown if has exception during the process definition id retrieve */ long getNumberOfProcessDeploymentInfosOfCategory(long categoryId) throws SBonitaReadException; /** * Remove specific categories for specific process definition * * @param processId * Identifier of the process definition * @param categoryIds * Identifiers of the categories * @throws SCategoryException */ void removeCategoriesFromProcessDefinition(long processId, List categoryIds) throws SCategoryException; /** * Get categories not attached for specific process definition, the result list is paginated * * @param processDefinitionId * @param fromIndex * @param numberOfCategories * @param order * @return The matching list of category * @throws SCategoryException */ List getCategoriesUnrelatedToProcessDefinition(long processDefinitionId, int fromIndex, int numberOfCategories, OrderByType order) throws SCategoryException; /** * Get number of categories not attached of the specific process definition * * @param processDefinitionId * @return number of categories * @throws SCategoryException * Error thrown if has exception during the category number retrieve action */ long getNumberOfCategoriesUnrelatedToProcess(long processDefinitionId) throws SCategoryException; /** * Search process category mappings corresponding to criteria * * @param queryOptions * @return List of process category mappings * @throws SBonitaReadException * @since 6.1 */ List searchProcessCategoryMappings(QueryOptions queryOptions) throws SBonitaReadException; /** * @param mappings * @return Number of deleted category mapping * @since 6.1 */ long deleteProcessCategoryMappings(List mappings); } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/SCategoryCriterion.java ================================================ /** * 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.core.category; /** * @author Yanyan Liu */ public enum SCategoryCriterion { NAME_DESC, NAME_ASC; } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SCategoryAlreadyExistsException.java ================================================ /** * 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.core.category.exception; /** * @author Yanyan Liu */ public class SCategoryAlreadyExistsException extends SCategoryException { private static final long serialVersionUID = 8463473790848613835L; public SCategoryAlreadyExistsException(final String message) { super(message); } public SCategoryAlreadyExistsException(final String message, final Throwable cause) { super(message, cause); } public SCategoryAlreadyExistsException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SCategoryCreationException.java ================================================ /** * 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.core.category.exception; /** * @author Yanyan Liu */ public class SCategoryCreationException extends SCategoryException { private static final long serialVersionUID = -6791829619840778679L; public SCategoryCreationException(final String message, final Throwable cause) { super(message, cause); } public SCategoryCreationException(final String message) { super(message); } public SCategoryCreationException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SCategoryDeletionException.java ================================================ /** * 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.core.category.exception; /** * @author Yanyan Liu */ public class SCategoryDeletionException extends SCategoryException { private static final long serialVersionUID = 2659887815564433751L; public SCategoryDeletionException(final String message, final Throwable cause) { super(message, cause); } public SCategoryDeletionException(final String message) { super(message); } public SCategoryDeletionException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SCategoryException.java ================================================ /** * 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.core.category.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Yanyan Liu */ public class SCategoryException extends SBonitaException { private static final long serialVersionUID = -7279379575300389907L; public SCategoryException(final String message, final Throwable cause) { super(message, cause); } public SCategoryException(final String message) { super(message); } public SCategoryException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SCategoryInProcessAlreadyExistsException.java ================================================ /** * 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.core.category.exception; /** * @author Matthieu Chaffotte */ public class SCategoryInProcessAlreadyExistsException extends SCategoryException { private static final long serialVersionUID = -6455208415564288711L; public SCategoryInProcessAlreadyExistsException(final Throwable cause) { super(cause); } public SCategoryInProcessAlreadyExistsException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SCategoryNotFoundException.java ================================================ /** * 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.core.category.exception; /** * @author Yanyan Liu */ public class SCategoryNotFoundException extends SCategoryException { private static final long serialVersionUID = 5143299844735860984L; public SCategoryNotFoundException(final String message, final Throwable cause) { super(message, cause); } public SCategoryNotFoundException(final Throwable cause) { super(cause); } public SCategoryNotFoundException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SIndexOutOfRangeException.java ================================================ /** * 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.core.category.exception; /** * @author Yanyan Liu */ public class SIndexOutOfRangeException extends SCategoryException { private static final long serialVersionUID = 5146584159115092732L; public SIndexOutOfRangeException(final String message, final Throwable cause) { super(message, cause); } public SIndexOutOfRangeException(final String message) { super(message); } public SIndexOutOfRangeException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/exception/SPageOutOfRangeException.java ================================================ /** * 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.core.category.exception; /** * @author Yanyan Liu */ public class SPageOutOfRangeException extends SCategoryException { private static final long serialVersionUID = -7486082562017547179L; public SPageOutOfRangeException(final String message, final Throwable cause) { super(message, cause); } public SPageOutOfRangeException(final String message) { super(message); } public SPageOutOfRangeException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/impl/CategoryServiceImpl.java ================================================ /** * 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.core.category.impl; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.category.CategoryService; import org.bonitasoft.engine.core.category.exception.SCategoryAlreadyExistsException; import org.bonitasoft.engine.core.category.exception.SCategoryCreationException; import org.bonitasoft.engine.core.category.exception.SCategoryDeletionException; import org.bonitasoft.engine.core.category.exception.SCategoryException; import org.bonitasoft.engine.core.category.exception.SCategoryInProcessAlreadyExistsException; import org.bonitasoft.engine.core.category.exception.SCategoryNotFoundException; import org.bonitasoft.engine.core.category.model.SCategory; import org.bonitasoft.engine.core.category.model.SProcessCategoryMapping; import org.bonitasoft.engine.core.category.model.builder.SCategoryLogBuilder; import org.bonitasoft.engine.core.category.model.builder.SCategoryLogBuilderFactory; import org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilderFactory; import org.bonitasoft.engine.core.category.persistence.SelectDescriptorBuilder; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; /** * @author Yanyan Liu * @author Matthieu Chaffotte * @author Celine Souchet */ public class CategoryServiceImpl implements CategoryService { private final ReadPersistenceService persistenceService; private final Recorder recorder; private final EventService eventService; private final SessionService sessionService; private final ReadSessionAccessor sessionAccessor; private final QueriableLoggerService queriableLoggerService; public CategoryServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder, final EventService eventService, final SessionService sessionService, final ReadSessionAccessor sessionAccessor, final QueriableLoggerService queriableLoggerService) { super(); this.persistenceService = persistenceService; this.recorder = recorder; this.eventService = eventService; this.sessionService = sessionService; this.sessionAccessor = sessionAccessor; this.queriableLoggerService = queriableLoggerService; } @Override public SCategory createCategory(final String name, final String description) throws SCategoryAlreadyExistsException, SCategoryCreationException { if (name == null) { throw new SCategoryCreationException("Category name can not be null!"); } try { getCategoryByName(name); throw new SCategoryAlreadyExistsException("Category with name " + name + " already exists!"); } catch (final SCategoryNotFoundException scnfe) { return addCategory(name, description); } } private SCategory addCategory(final String name, final String description) throws SCategoryCreationException { final SCategoryLogBuilder logBuilder = getQueriableLog(ActionType.CREATED, "Creating a new category with name " + name); final long creator; creator = getCreator(); final long now = System.currentTimeMillis(); final SCategory sCategory = SCategory.builder().name(name) .creator(creator) .creationDate(now) .lastUpdateDate(now).description(description).build(); final InsertRecord insertRecord = new InsertRecord(sCategory); try { recorder.recordInsert(insertRecord, CATEGORY); initiateLogBuilder(insertRecord.getEntity().getId(), SQueriableLog.STATUS_OK, logBuilder, "addCategory"); return sCategory; } catch (final SRecorderException e) { initiateLogBuilder(insertRecord.getEntity().getId(), SQueriableLog.STATUS_FAIL, logBuilder, "addCategory"); throw new SCategoryCreationException(e); } } private long getCreator() { return sessionService.getLoggedUserFromSession(sessionAccessor); } @Override public SCategory getCategory(final long id) throws SCategoryNotFoundException { final SelectByIdDescriptor selectByIdDescriptor = SelectDescriptorBuilder.getCategory(id); try { final SCategory category = persistenceService.selectById(selectByIdDescriptor); if (category == null) { throw new SCategoryNotFoundException(id + " does not refer to any category"); } return category; } catch (final SBonitaReadException bre) { throw new SCategoryNotFoundException(bre); } } @Override public SCategory getCategoryByName(final String name) throws SCategoryNotFoundException { final SelectOneDescriptor descriptor = SelectDescriptorBuilder.getCategory(name); try { final SCategory category = persistenceService.selectOne(descriptor); if (category == null) { throw new SCategoryNotFoundException("Category not found with name: " + name); } return category; } catch (final SBonitaReadException bre) { throw new SCategoryNotFoundException(bre); } } @Override public void updateCategory(final long categoryId, final EntityUpdateDescriptor descriptor) throws SCategoryException { final SCategoryLogBuilder logBuilder = getQueriableLog(ActionType.UPDATED, "Updating category"); final SCategory persistedCategory = getCategory(categoryId); try { recorder.recordUpdate(UpdateRecord.buildSetFields(persistedCategory, descriptor), CATEGORY); initiateLogBuilder(categoryId, SQueriableLog.STATUS_OK, logBuilder, "updateCategory"); } catch (final SRecorderException e) { initiateLogBuilder(categoryId, SQueriableLog.STATUS_FAIL, logBuilder, "updateCategory"); throw new SCategoryException("Can't update category " + persistedCategory, e); } catch (final Exception e) { throw new SCategoryException("Can't update category " + persistedCategory, e); } } @Override public void deleteCategory(final long categoryId) throws SCategoryNotFoundException, SCategoryDeletionException { final SCategory sCategory = getCategory(categoryId); final SCategoryLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, "Deleting a category"); try { recorder.recordDelete(new DeleteRecord(sCategory), CATEGORY); initiateLogBuilder(categoryId, SQueriableLog.STATUS_OK, logBuilder, "deleteCategory"); } catch (final SRecorderException e) { initiateLogBuilder(categoryId, SQueriableLog.STATUS_FAIL, logBuilder, "deleteCategory"); throw new SCategoryDeletionException("Can't delete process category " + sCategory, e); } } @Override public long getNumberOfCategories() throws SCategoryException { try { return persistenceService .selectOne(SelectDescriptorBuilder.getNumberOfElement("Category", SCategory.class)); } catch (final SBonitaReadException e) { throw new SCategoryException("Can't get the number of process category", e); } } @Override public List getCategories(final int fromIndex, final int numberOfCategories, final String field, final OrderByType order) throws SCategoryException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getCategories(field, order, fromIndex, numberOfCategories); try { return persistenceService.selectList(descriptor); } catch (final SBonitaReadException e) { throw new SCategoryException(e); } } @Override public void addProcessDefinitionToCategory(final long categoryId, final long processDefinitionId) throws SCategoryException { final SCategory category = getCategory(categoryId); if (isCategoryExistsInProcess(categoryId, processDefinitionId)) { throw new SCategoryInProcessAlreadyExistsException( "The category '" + category.getName() + "' with the id = " + categoryId + " is already in process with the id = " + processDefinitionId); } final SProcessCategoryMapping mapping = BuilderFactory.get(SProcessCategoryMappingBuilderFactory.class) .createNewInstance(categoryId, processDefinitionId).done(); final String logMessage = "Creating a new category mapping {categoryId:" + categoryId + " --> processDefinitionId:" + processDefinitionId + "}"; final SCategoryLogBuilder logBuilder = getQueriableLog(ActionType.CREATED, logMessage); try { recorder.recordInsert(new InsertRecord(mapping), CATEGORY); initiateLogBuilder(categoryId, SQueriableLog.STATUS_OK, logBuilder, "addProcessDefinitionToCategory"); } catch (final SRecorderException e) { initiateLogBuilder(categoryId, SQueriableLog.STATUS_FAIL, logBuilder, "addProcessDefinitionToCategory"); throw new SCategoryException(e); } } private boolean isCategoryExistsInProcess(final long categoryId, final long processDefinitionId) throws SCategoryException { final SelectOneDescriptor descriptor = SelectDescriptorBuilder.isCategoryExistsInProcess(categoryId, processDefinitionId); try { return persistenceService.selectOne(descriptor) == 0 ? false : true; } catch (final SBonitaReadException e) { throw new SCategoryException(e); } } @Override public void addProcessDefinitionsToCategory(final long categoryId, final List processDefinitionIds) throws SCategoryException { for (final long processDefinitionId : processDefinitionIds) { addProcessDefinitionToCategory(categoryId, processDefinitionId); } } @Override public long getNumberOfCategoriesOfProcess(final long processDefinitionId) throws SCategoryException { final SelectOneDescriptor descriptor = SelectDescriptorBuilder .getNumberOfCategoriesOfProcess(processDefinitionId); try { return persistenceService.selectOne(descriptor); } catch (final SBonitaReadException e) { throw new SCategoryException(e); } } @Override public long getNumberOfCategoriesUnrelatedToProcess(final long processDefinitionId) throws SCategoryException { final SelectOneDescriptor descriptor = SelectDescriptorBuilder .getNumberOfCategoriesUnrelatedToProcess(processDefinitionId); try { return persistenceService.selectOne(descriptor); } catch (final SBonitaReadException e) { throw new SCategoryException(e); } } @Override public List getCategoriesOfProcessDefinition(final long processDefinitionId, final int fromIndex, final int numberOfCategories, final OrderByType order) throws SCategoryException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getCategoriesOfProcess( processDefinitionId, fromIndex, numberOfCategories, order); try { return persistenceService.selectList(descriptor); } catch (final SBonitaReadException e) { throw new SCategoryException(e); } } @Override public List getCategoriesUnrelatedToProcessDefinition(final long processDefinitionId, final int fromIndex, final int numberOfCategories, final OrderByType order) throws SCategoryException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getCategoriesUnrelatedToProcess( processDefinitionId, fromIndex, numberOfCategories, order); try { return persistenceService.selectList(descriptor); } catch (final SBonitaReadException e) { throw new SCategoryException(e); } } @Override public void removeCategoriesFromProcessDefinition(final long processDefinitionId, final List categoryIds) throws SCategoryException { final SelectListDescriptor descriptor = SelectDescriptorBuilder .getCategoryMappingOfProcessAndCategories(processDefinitionId, categoryIds, 0, 100); try { List mappings = persistenceService.selectList(descriptor); while (!mappings.isEmpty()) { deleteProcessCategoryMappings(mappings); mappings = persistenceService.selectList(descriptor); } } catch (final SBonitaReadException e) { throw new SCategoryException(e); } } @Override public List searchProcessCategoryMappings(final QueryOptions queryOptions) throws SBonitaReadException { return persistenceService.searchEntity(SProcessCategoryMapping.class, queryOptions, null); } @Override public long deleteProcessCategoryMappings(final List mappings) { long nbDeleted = 0; for (final SProcessCategoryMapping mapping : mappings) { try { deleteProcessCategoryMapping(mapping); nbDeleted = +1; } catch (final SBonitaException e) { // FIXME : Nothing to do, or add logs ?? } } return nbDeleted; } private void deleteProcessCategoryMapping(final SProcessCategoryMapping mapping) throws SCategoryException { final String logMessage = "Deleting a category mapping {processDefinitionId:" + mapping.getProcessId() + " --> categoryId" + mapping.getCategoryId() + "}"; final SCategoryLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, logMessage); try { recorder.recordDelete(new DeleteRecord(mapping), CATEGORY); initiateLogBuilder(mapping.getId(), SQueriableLog.STATUS_OK, logBuilder, "deleteProcessCategoryMapping"); } catch (final SRecorderException e) { initiateLogBuilder(mapping.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "deleteProcessCategoryMapping"); throw new SCategoryException(e); } } private SCategoryLogBuilder getQueriableLog(final ActionType actionType, final String message) { final SCategoryLogBuilder logBuilder = BuilderFactory.get(SCategoryLogBuilderFactory.class).createNewInstance(); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); return logBuilder; } private void initializeLogBuilder(final T logBuilder, final String message) { logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message); } private void updateLog(final ActionType actionType, final T logBuilder) { logBuilder.setActionType(actionType); } @Override public long getNumberOfCategorizedProcessIds(final List processIds) throws SCategoryException { if (processIds == null || processIds.size() <= 0) { return 0; // Should return 0 or throw Exception? } final SelectOneDescriptor descriptor = SelectDescriptorBuilder .getNumberOfCategorizedProcessIds(processIds); try { return persistenceService.selectOne(descriptor); } catch (final SBonitaReadException e) { throw new SCategoryException(e); } } @Override public long getNumberOfProcessDeploymentInfosOfCategory(final long categoryId) throws SBonitaReadException { final Map parameters = Collections.singletonMap("categoryId", (Object) categoryId); final SelectOneDescriptor descriptor = new SelectOneDescriptor( "getNumberOfProcessDefinitionsOfCategory", parameters, SProcessCategoryMapping.class, Long.class); return persistenceService.selectOne(descriptor); } private void initiateLogBuilder(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder, final String callerMethodName) { logBuilder.actionScope(String.valueOf(objectId)); logBuilder.actionStatus(sQueriableLogStatus); logBuilder.objectId(objectId); final SQueriableLog log = logBuilder.build(); if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) { queriableLoggerService.log(this.getClass().getName(), callerMethodName, log); } } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/SCategory.java ================================================ /** * 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.core.category.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Yanyan Liu * @author Matthieu Chaffotte * @author Celine Souchet */ @Data @NoArgsConstructor @AllArgsConstructor @Builder(toBuilder = true) @Entity @Table(name = "category") public class SCategory implements PersistentObject { public static final String ID = "id"; public static final String NAME = "name"; public static final String DESCRIPTION = "description"; public static final String CREATOR = "creator"; public static final String CREATION_DATE = "creationDate"; public static final String LAST_UPDATE_DATE = "lastUpdateDate"; @Id private long id; @Column private String name; @Column private String description; @Column private long creator; @Column private long creationDate; @Column private long lastUpdateDate; public SCategory(final String name) { this.name = name; } public SCategory(final SCategory category) { this.id = category.getId(); this.name = category.getName(); this.description = category.getDescription(); this.creator = category.getCreator(); this.creationDate = category.getCreationDate(); this.lastUpdateDate = category.getLastUpdateDate(); } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/SProcessCategoryMapping.java ================================================ /** * 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.core.category.model; import javax.persistence.Cacheable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @Entity @Table(name = "processcategorymapping") @Cacheable(false) public class SProcessCategoryMapping implements PersistentObject { @Id private long id; @Column private long categoryId; @Column private long processId; public SProcessCategoryMapping(final long categoryId, final long processId) { this.categoryId = categoryId; this.processId = processId; } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/SCategoryLogBuilder.java ================================================ /** * 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.core.category.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Yanyan Liu */ public interface SCategoryLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/SCategoryLogBuilderFactory.java ================================================ /** * 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.core.category.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Yanyan Liu */ public interface SCategoryLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { SCategoryLogBuilder createNewInstance(); } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/SCategoryUpdateBuilder.java ================================================ /** * 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.core.category.model.builder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Yanyan Liu */ public interface SCategoryUpdateBuilder { SCategoryUpdateBuilder updateName(final String name); SCategoryUpdateBuilder updateDescription(final String description); EntityUpdateDescriptor done(); } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/SCategoryUpdateBuilderFactory.java ================================================ /** * 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.core.category.model.builder; /** * @author Yanyan Liu */ public interface SCategoryUpdateBuilderFactory { SCategoryUpdateBuilder createNewInstance(); } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/SProcessCategoryMappingBuilder.java ================================================ /** * 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.core.category.model.builder; import org.bonitasoft.engine.core.category.model.SProcessCategoryMapping; /** * @author Matthieu Chaffotte */ public interface SProcessCategoryMappingBuilder { SProcessCategoryMapping done(); } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/SProcessCategoryMappingBuilderFactory.java ================================================ /** * 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.core.category.model.builder; /** * @author Matthieu Chaffotte */ public interface SProcessCategoryMappingBuilderFactory { SProcessCategoryMappingBuilder createNewInstance(long categoryId, long processId); String getIdKey(); String getCategoryIdKey(); String getProcessIdKey(); } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/impl/SCategoryLogBuilderFactoryImpl.java ================================================ /** * 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.core.category.model.builder.impl; import org.bonitasoft.engine.core.category.model.builder.SCategoryLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Yanyan Liu */ public class SCategoryLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SCategoryLogBuilderFactory { @Override public SCategoryLogBuilderImpl createNewInstance() { return new SCategoryLogBuilderImpl(); } @Override public String getObjectIdKey() { return SCategoryLogIndexesMapper.CATEGORY_INDEX_NAME; } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/impl/SCategoryLogBuilderImpl.java ================================================ /** * 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.core.category.model.builder.impl; import org.bonitasoft.engine.core.category.model.builder.SCategoryLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Yanyan Liu */ public class SCategoryLogBuilderImpl extends CRUDELogBuilder implements SCategoryLogBuilder { private static final String PREFIX = "CATEGORY"; public SCategoryLogBuilderImpl() { super(); } @Override protected String getActionTypePrefix() { return PREFIX; } @Override public SPersistenceLogBuilder objectId(final long objectId) { this.queriableLogBuilder.numericIndex(SCategoryLogIndexesMapper.CATEGORY_INDEX, objectId); return this; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL) { if (log.getNumericIndex(SCategoryLogIndexesMapper.CATEGORY_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + "Category Id"); } } } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/impl/SCategoryLogIndexesMapper.java ================================================ /** * 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.core.category.model.builder.impl; /** * @author Yanyan Liu * @author Matthieu Chaffotte */ public class SCategoryLogIndexesMapper { public static final int CATEGORY_INDEX = 0; public static final String CATEGORY_INDEX_NAME = "numericIndex1"; } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/impl/SCategoryUpdateBuilderFactoryImpl.java ================================================ /** * 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.core.category.model.builder.impl; import org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilder; import org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Yanyan Liu */ public class SCategoryUpdateBuilderFactoryImpl implements SCategoryUpdateBuilderFactory { public SCategoryUpdateBuilder createNewInstance() { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); return new SCategoryUpdateBuilderImpl(descriptor); } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/impl/SCategoryUpdateBuilderImpl.java ================================================ /** * 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.core.category.model.builder.impl; import org.bonitasoft.engine.core.category.model.SCategory; import org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Yanyan Liu */ public class SCategoryUpdateBuilderImpl implements SCategoryUpdateBuilder { private final EntityUpdateDescriptor descriptor; public SCategoryUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) { super(); this.descriptor = descriptor; } @Override public EntityUpdateDescriptor done() { return this.descriptor; } @Override public SCategoryUpdateBuilder updateName(final String name) { this.descriptor.addField(SCategory.NAME, name); return this; } @Override public SCategoryUpdateBuilder updateDescription(final String description) { this.descriptor.addField(SCategory.DESCRIPTION, description); return this; } public static SCategoryUpdateBuilder getInstance() { return new SCategoryUpdateBuilderImpl(null); } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/impl/SProcessCategoryMappingBuilderFactoryImpl.java ================================================ /** * 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.core.category.model.builder.impl; import org.bonitasoft.engine.core.category.model.SProcessCategoryMapping; import org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilder; import org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilderFactory; /** * @author Matthieu Chaffotte */ public class SProcessCategoryMappingBuilderFactoryImpl implements SProcessCategoryMappingBuilderFactory { @Override public SProcessCategoryMappingBuilder createNewInstance(final long categoryId, final long processId) { final SProcessCategoryMapping entity = new SProcessCategoryMapping(categoryId, processId); return new SProcessCategoryMappingBuilderImpl(entity); } @Override public String getIdKey() { return "id"; } @Override public String getCategoryIdKey() { return "categoryId"; } @Override public String getProcessIdKey() { return "processId"; } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/model/builder/impl/SProcessCategoryMappingBuilderImpl.java ================================================ /** * 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.core.category.model.builder.impl; import org.bonitasoft.engine.core.category.model.SProcessCategoryMapping; import org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilder; /** * @author Matthieu Chaffotte */ public class SProcessCategoryMappingBuilderImpl implements SProcessCategoryMappingBuilder { private final SProcessCategoryMapping entity; public SProcessCategoryMappingBuilderImpl(final SProcessCategoryMapping entity) { super(); this.entity = entity; } @Override public SProcessCategoryMapping done() { return entity; } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/java/org/bonitasoft/engine/core/category/persistence/SelectDescriptorBuilder.java ================================================ /** * 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.core.category.persistence; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.core.category.model.SCategory; import org.bonitasoft.engine.core.category.model.SProcessCategoryMapping; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; /** * @author Yanyan Liu * @author Matthieu Chaffotte * @author Celine Souchet */ public class SelectDescriptorBuilder { private static final String PROCESS_ID = "processId"; public static SelectByIdDescriptor getCategory(final long categoryId) { return new SelectByIdDescriptor<>(SCategory.class, categoryId); } public static SelectOneDescriptor getCategory(final String categoryName) { final Map parameters = Collections.singletonMap("name", (Object) categoryName); return new SelectOneDescriptor<>("getCategoryByName", parameters, SCategory.class); } public static SelectOneDescriptor getNumberOfElement(final String elementName, final Class clazz) { final Map parameters = Collections.emptyMap(); return new SelectOneDescriptor<>("getNumberOf" + elementName, parameters, clazz, Long.class); } public static SelectListDescriptor getCategories(final String field, final OrderByType order, final int fromIndex, final int numberOfProcesses) { final Map parameters = Collections.emptyMap(); final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfProcesses, SCategory.class, field, order); return new SelectListDescriptor<>("getCategories", parameters, SCategory.class, queryOptions); } public static SelectListDescriptor getCategoriesOfProcess(final long processId, final int fromIndex, final int numberOfCategories, final OrderByType order) { final Map parameters = Collections.singletonMap(PROCESS_ID, (Object) processId); final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfCategories, SCategory.class, "name", order); return new SelectListDescriptor<>("getCategoriesOfProcess", parameters, SCategory.class, queryOptions); } public static SelectListDescriptor getCategoriesUnrelatedToProcess(final long processId, final int fromIndex, final int numberOfCategories, final OrderByType order) { final Map parameters = Collections.singletonMap(PROCESS_ID, (Object) processId); final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfCategories, SCategory.class, "name", order); return new SelectListDescriptor<>("getCategoriesUnrelatedToProcess", parameters, SCategory.class, queryOptions); } public static SelectOneDescriptor getNumberOfCategoriesOfProcess(final long processId) { final Map parameters = Collections.singletonMap(PROCESS_ID, (Object) processId); return new SelectOneDescriptor<>("getNumberOfCategoriesOfProcess", parameters, SProcessCategoryMapping.class); } public static SelectOneDescriptor getNumberOfCategoriesUnrelatedToProcess(final long processId) { final Map parameters = Collections.singletonMap(PROCESS_ID, (Object) processId); return new SelectOneDescriptor<>("getNumberOfCategoriesUnrelatedToProcess", parameters, SProcessCategoryMapping.class); } public static SelectOneDescriptor getNumberOfCategorizedProcessIds(final List processIds) { final Map parameters = Collections.singletonMap("processIds", (Object) processIds); return new SelectOneDescriptor<>("getNumberOfCategorizedProcessIds", parameters, SProcessCategoryMapping.class, Long.class); } public static SelectOneDescriptor isCategoryExistsInProcess(final long categoryId, final long processDefinitionId) { final Map parameters = new HashMap<>(2); parameters.put("categoryId", categoryId); parameters.put("processDefinitionId", processDefinitionId); return new SelectOneDescriptor<>("isCategoryExistsInProcess", parameters, SProcessCategoryMapping.class); } public static SelectListDescriptor getCategoryMappingOfProcessAndCategories( final long processDefinitionId, final List categoryIds, final int fromIndex, final int maxResults) { final QueryOptions queryOptions = buildQueryOptionsForCategoryMappingOrderedByCategoryId(fromIndex, maxResults, OrderByType.ASC); final Map parameters = new HashMap<>(2); parameters.put("categoryIds", categoryIds); parameters.put("processDefinitionId", processDefinitionId); return new SelectListDescriptor<>("getCategoryMappingOfProcessAndCategories", parameters, SProcessCategoryMapping.class, queryOptions); } public static QueryOptions buildQueryOptionsForCategoryMappingOrderedByCategoryId(final int fromIndex, final int maxResults, final OrderByType order) { return new QueryOptions(fromIndex, maxResults, SProcessCategoryMapping.class, "categoryId", order); } } ================================================ FILE: bpm/bonita-core/bonita-category/src/main/resources/org/bonitasoft/engine/core/category/model/impl/hibernate/category.queries.hbm.xml ================================================ SELECT category FROM org.bonitasoft.engine.core.category.model.SCategory AS category WHERE category.name = :name SELECT COUNT(category.id) FROM org.bonitasoft.engine.core.category.model.SCategory AS category SELECT category FROM org.bonitasoft.engine.core.category.model.SCategory AS category SELECT category FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping, org.bonitasoft.engine.core.category.model.SCategory AS category WHERE categorymapping.processId = :processId AND category.id = categorymapping.categoryId SELECT category FROM org.bonitasoft.engine.core.category.model.SCategory AS category WHERE category.id NOT IN ( SELECT categorymapping.categoryId FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = :processId) SELECT COUNT(category.id) FROM org.bonitasoft.engine.core.category.model.SCategory AS category WHERE category.id NOT IN ( SELECT categorymapping.categoryId FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = :processId) SELECT categorymapping FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping SELECT categorymapping FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = :processDefinitionId AND categorymapping.categoryId IN (:categoryIds) SELECT COUNT(categorymapping.id) FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = :processId SELECT COUNT(categorymapping.id) FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.categoryId = :categoryId SELECT COUNT(categorymapping.id) FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId IN (:processIds) SELECT COUNT(categorymapping.id) FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = :processDefinitionId AND categorymapping.categoryId = :categoryId ================================================ FILE: bpm/bonita-core/bonita-category/src/test/java/org/bonitasoft/engine/core/category/impl/CategoryServiceImplTest.java ================================================ /** * 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.core.category.impl; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.core.category.exception.SCategoryException; import org.bonitasoft.engine.core.category.exception.SCategoryNotFoundException; import org.bonitasoft.engine.core.category.model.SCategory; import org.bonitasoft.engine.core.category.persistence.SelectDescriptorBuilder; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.services.QueriableLoggerService; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * @author Celine Souchet */ public class CategoryServiceImplTest { @Mock private Recorder recorder; @Mock private ReadPersistenceService persistenceService; @Mock private EventService eventService; @Mock private QueriableLoggerService queriableLoggerService; @Mock private SessionService sessionService; @Mock private ReadSessionAccessor sessionAccessor; @InjectMocks private CategoryServiceImpl categoryServiceImpl; @Before public void setUp() { MockitoAnnotations.initMocks(this); } /** * Test method for {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getCategory(long)}. * * @throws SCategoryNotFoundException * @throws SBonitaReadException */ @Test public final void getCategoryById() throws SCategoryNotFoundException, SBonitaReadException { // Given final long id = 456L; final SCategory sCategory = mock(SCategory.class); doReturn(sCategory).when(persistenceService).selectById(SelectDescriptorBuilder.getCategory(id)); // When final SCategory category = categoryServiceImpl.getCategory(id); // Then Assert.assertEquals(sCategory, category); } @Test(expected = SCategoryNotFoundException.class) public final void getCategoryByIdNotExists() throws SBonitaReadException, SCategoryNotFoundException { // Given final long id = 456L; doReturn(null).when(persistenceService).selectById(SelectDescriptorBuilder.getCategory(id)); // When categoryServiceImpl.getCategory(id); } @Test(expected = SCategoryNotFoundException.class) public final void getCategoryByIdThrowException() throws SBonitaReadException, SCategoryNotFoundException { // Given final long id = 456L; doThrow(new SBonitaReadException("")).when(persistenceService) .selectById(SelectDescriptorBuilder.getCategory(id)); // When categoryServiceImpl.getCategory(id); } /** * Test method for * {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getCategories(int, int, java.lang.String, org.bonitasoft.engine.persistence.OrderByType)} * . * * @throws SCategoryException * @throws SBonitaReadException */ @Test public final void getCategories() throws SCategoryException, SBonitaReadException { // Given final List sCategories = new ArrayList(); final String field = "field"; final OrderByType order = OrderByType.ASC; final int fromIndex = 0; final int numberOfCategories = 1; doReturn(sCategories).when(persistenceService) .selectList(SelectDescriptorBuilder.getCategories(field, order, fromIndex, numberOfCategories)); // When final List categories = categoryServiceImpl.getCategories(fromIndex, numberOfCategories, field, order); // Then Assert.assertEquals(sCategories, categories); } @Test(expected = SCategoryException.class) public final void getCategoriesThrowException() throws SBonitaReadException, SCategoryException { // Given final String field = "field"; final OrderByType order = OrderByType.ASC; final int fromIndex = 0; final int numberOfCategories = 1; doThrow(new SBonitaReadException("")).when(persistenceService).selectList( SelectDescriptorBuilder.getCategories(field, order, fromIndex, numberOfCategories)); // When categoryServiceImpl.getCategories(fromIndex, numberOfCategories, field, order); } /** * Test method for * {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getCategoriesOfProcessDefinition(long, int, int, org.bonitasoft.engine.persistence.OrderByType)} * . * * @throws SCategoryException * @throws SBonitaReadException */ @Test public final void getCategoriesOfProcessDefinition() throws SCategoryException, SBonitaReadException { // Given final List sCategories = new ArrayList(); final long processDefinitionId = 2L; final int fromIndex = 0; final int numberOfCategories = 1; final OrderByType order = OrderByType.ASC; doReturn(sCategories).when(persistenceService).selectList( SelectDescriptorBuilder.getCategoriesOfProcess(processDefinitionId, fromIndex, numberOfCategories, order)); // When final List categoriesOfProcessDefinition = categoryServiceImpl.getCategoriesOfProcessDefinition( processDefinitionId, fromIndex, numberOfCategories, order); // Then Assert.assertEquals(sCategories, categoriesOfProcessDefinition); } @Test(expected = SCategoryException.class) public final void getCategoriesOfProcessDefinitionThrowException() throws SBonitaReadException, SCategoryException { // Given final long processDefinitionId = 2L; final int fromIndex = 0; final int numberOfCategories = 1; final OrderByType order = OrderByType.ASC; doThrow(new SBonitaReadException("")).when(persistenceService).selectList( SelectDescriptorBuilder.getCategoriesOfProcess(processDefinitionId, fromIndex, numberOfCategories, order)); // When categoryServiceImpl.getCategoriesOfProcessDefinition(processDefinitionId, fromIndex, numberOfCategories, order); } /** * Test method for * {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getCategoryByName(java.lang.String)}. * * @throws SCategoryNotFoundException * @throws SBonitaReadException */ @Test public final void getCategoryByName() throws SCategoryNotFoundException, SBonitaReadException { // Given final SCategory sCategory = mock(SCategory.class); final String name = "name"; doReturn(sCategory).when(persistenceService).selectOne(SelectDescriptorBuilder.getCategory(name)); // When final SCategory categoryByName = categoryServiceImpl.getCategoryByName(name); // Then Assert.assertEquals(sCategory, categoryByName); } @Test(expected = SCategoryNotFoundException.class) public final void getCategoryByNameNotExists() throws SBonitaReadException, SCategoryNotFoundException { // Given final String name = "name"; doReturn(null).when(persistenceService).selectOne(SelectDescriptorBuilder.getCategory(name)); // When categoryServiceImpl.getCategoryByName(name); } @Test(expected = SCategoryNotFoundException.class) public final void getCategoryByNameThrowException() throws SBonitaReadException, SCategoryNotFoundException { // Given final String name = "name"; doThrow(new SBonitaReadException("")).when(persistenceService) .selectOne(SelectDescriptorBuilder.getCategory(name)); // When categoryServiceImpl.getCategoryByName(name); } /** * Test method for {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getNumberOfCategories()}. * * @throws SBonitaReadException * @throws SCategoryException */ @Test public final void getNumberOfCategories() throws SBonitaReadException, SCategoryException { // Given final long numberOfCategories = 3L; doReturn(numberOfCategories).when(persistenceService) .selectOne(SelectDescriptorBuilder.getNumberOfElement("Category", SCategory.class)); // When final long result = categoryServiceImpl.getNumberOfCategories(); // Then Assert.assertEquals(numberOfCategories, result); } @Test(expected = SCategoryException.class) public final void getNumberOfCategoriesThrowException() throws SCategoryException, SBonitaReadException { // Given doThrow(new SBonitaReadException("")).when(persistenceService) .selectOne(SelectDescriptorBuilder.getNumberOfElement("Category", SCategory.class)); // When categoryServiceImpl.getNumberOfCategories(); } /** * Test method for * {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getNumberOfCategoriesOfProcess(long)}. * * @throws SCategoryException * @throws SBonitaReadException */ @Test public final void getNumberOfCategoriesOfProcess() throws SCategoryException, SBonitaReadException { // Given final long numberOfCategories = 3L; final long processDefinitionId = 1589L; doReturn(numberOfCategories).when(persistenceService) .selectOne(SelectDescriptorBuilder.getNumberOfCategoriesOfProcess(processDefinitionId)); // When final long result = categoryServiceImpl.getNumberOfCategoriesOfProcess(processDefinitionId); // Then Assert.assertEquals(numberOfCategories, result); } @Test(expected = SCategoryException.class) public final void getNumberOfCategoriesOfProcessThrowException() throws SCategoryException, SBonitaReadException { // Given final long processDefinitionId = 1589L; doThrow(new SBonitaReadException("")).when(persistenceService) .selectOne(SelectDescriptorBuilder.getNumberOfCategoriesOfProcess(processDefinitionId)); // When categoryServiceImpl.getNumberOfCategoriesOfProcess(processDefinitionId); } /** * Test method for * {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getNumberOfCategoriesUnrelatedToProcess(long)}. * * @throws SCategoryException * @throws SBonitaReadException */ @Test public final void getNumberOfCategoriesUnrelatedToProcess() throws SCategoryException, SBonitaReadException { // Given final long numberOfCategories = 3L; final long processDefinitionId = 1589L; doReturn(numberOfCategories).when(persistenceService) .selectOne(SelectDescriptorBuilder.getNumberOfCategoriesUnrelatedToProcess(processDefinitionId)); // When final long numberOfCategoriesUnrelatedToProcess = categoryServiceImpl .getNumberOfCategoriesUnrelatedToProcess(processDefinitionId); // Then Assert.assertEquals(numberOfCategories, numberOfCategoriesUnrelatedToProcess); } @Test(expected = SCategoryException.class) public final void getNumberOfCategoriesUnrelatedToProcessThrowException() throws SCategoryException, SBonitaReadException { // Given final long processDefinitionId = 1589L; doThrow(new SBonitaReadException("")).when(persistenceService).selectOne( SelectDescriptorBuilder.getNumberOfCategoriesUnrelatedToProcess(processDefinitionId)); // When categoryServiceImpl.getNumberOfCategoriesUnrelatedToProcess(processDefinitionId); } /** * Test method for * {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getNumberOfCategorizedProcessIds(java.util.List)}. * * @throws SCategoryException * @throws SBonitaReadException */ @Test public final void getNumberOfCategorizedProcessIds() throws SCategoryException, SBonitaReadException { // Given final List processIds = new ArrayList(); processIds.add(14654L); final long numberOfCategories = 3L; doReturn(numberOfCategories).when(persistenceService) .selectOne(SelectDescriptorBuilder.getNumberOfCategorizedProcessIds(processIds)); // When final long numberOfCategorizedProcessIds = categoryServiceImpl.getNumberOfCategorizedProcessIds(processIds); // Then Assert.assertEquals(numberOfCategories, numberOfCategorizedProcessIds); } @Test public final void getNumberOfCategorizedProcessIdsWithNullList() throws SCategoryException { // When final long numberOfCategorizedProcessIds = categoryServiceImpl.getNumberOfCategorizedProcessIds(null); // Then Assert.assertEquals(0, numberOfCategorizedProcessIds); } @Test public final void getNumberOfCategorizedProcessIdsWithEmptyList() throws SCategoryException { // When final long numberOfCategorizedProcessIds = categoryServiceImpl .getNumberOfCategorizedProcessIds(new ArrayList()); // Then Assert.assertEquals(0, numberOfCategorizedProcessIds); } @Test(expected = SCategoryException.class) public final void getNumberOfCategorizedProcessIdsThrowException() throws SBonitaReadException, SCategoryException { // Given final List processIds = new ArrayList(); processIds.add(14654L); doThrow(new SBonitaReadException("")).when(persistenceService) .selectOne(SelectDescriptorBuilder.getNumberOfCategorizedProcessIds(processIds)); // When categoryServiceImpl.getNumberOfCategorizedProcessIds(processIds); } /** * Test method for * {@link org.bonitasoft.engine.core.category.impl.CategoryServiceImpl#getCategoriesUnrelatedToProcessDefinition(long, int, int, org.bonitasoft.engine.persistence.OrderByType)} * . */ @Test public final void getCategoriesUnrelatedToProcessDefinition() throws SBonitaReadException, SCategoryException { // Given final List sCategories = new ArrayList(); final long processDefinitionId = 54894L; final int fromIndex = 0; final int numberOfCategories = 10; final OrderByType order = OrderByType.ASC; doReturn(sCategories).when(persistenceService) .selectList(SelectDescriptorBuilder.getCategoriesUnrelatedToProcess(processDefinitionId, fromIndex, numberOfCategories, order)); // When final List categoriesUnrelatedToProcessDefinition = categoryServiceImpl .getCategoriesUnrelatedToProcessDefinition(processDefinitionId, fromIndex, numberOfCategories, order); // Then Assert.assertEquals(sCategories, categoriesUnrelatedToProcessDefinition); } @Test(expected = SCategoryException.class) public final void getCategoriesUnrelatedToProcessDefinitionThrowException() throws SBonitaReadException, SCategoryException { // Given final long processDefinitionId = 54894L; final int fromIndex = 0; final int numberOfCategories = 10; final OrderByType order = OrderByType.ASC; doThrow(new SBonitaReadException("")).when(persistenceService).selectList( SelectDescriptorBuilder.getCategoriesUnrelatedToProcess(processDefinitionId, fromIndex, numberOfCategories, order)); // When categoryServiceImpl.getCategoriesUnrelatedToProcessDefinition(processDefinitionId, fromIndex, numberOfCategories, order); } } ================================================ FILE: bpm/bonita-core/bonita-contract-data/build.gradle ================================================ dependencies { api project(':services:bonita-commons') api project(':services:bonita-builder') api project(':services:bonita-log') api project(':services:bonita-archive') api project(':services:bonita-persistence') implementation platform(libs.bonitaArtifactsModelBom) implementation 'org.bonitasoft.engine:bonita-process-definition-model' testImplementation libs.assertj testImplementation libs.mockitoCore annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/ContractDataService.java ================================================ /** * 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.core.contract.data; import java.io.Serializable; import java.util.List; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Matthieu Chaffotte */ public interface ContractDataService { void addUserTaskData(final long userTaskId, Map data) throws SContractDataCreationException; Serializable getUserTaskDataValue(final long userTaskId, String dataName) throws SContractDataNotFoundException, SBonitaReadException; void deleteUserTaskData(final long userTaskId) throws SContractDataDeletionException; /** * delete all archived user task data from the list of user task ids (sourceObjectIds) * * @param sourceUserTaskIds list of user tasks ids (non-archived) * @throws SContractDataDeletionException */ void deleteArchivedUserTaskData(List sourceUserTaskIds) throws SContractDataDeletionException; void archiveAndDeleteUserTaskData(final long userTaskId, final long archiveDate) throws SObjectModificationException; Serializable getArchivedUserTaskDataValue(final long userTaskId, String dataName) throws SContractDataNotFoundException, SBonitaReadException; void addProcessData(long processInstanceId, Map data) throws SContractDataCreationException; Serializable getProcessDataValue(long processInstanceId, String dataName) throws SContractDataNotFoundException, SBonitaReadException; void deleteProcessData(long processInstanceId) throws SContractDataDeletionException; /** * delete all archived user task data from the list of process instance ids (sourceObjectIds) * * @param sourceProcessInstanceIds list of process instance ids (non-archived) * @throws SContractDataDeletionException */ void deleteArchivedProcessData(List sourceProcessInstanceIds) throws SContractDataDeletionException; void archiveAndDeleteProcessData(long processInstanceId, long archiveDate) throws SObjectModificationException; Serializable getArchivedProcessDataValue(long processInstanceId, String dataName) throws SContractDataNotFoundException, SBonitaReadException; } ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/ContractDataServiceImpl.java ================================================ /** * 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.core.contract.data; import static java.util.Collections.singletonMap; import java.io.Serializable; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.services.QueriableLoggerService; /** * @author Matthieu Chaffotte */ public class ContractDataServiceImpl implements ContractDataService { private static final String PROCESS_CONTRACT_DATA = "PROCESS_CONTRACT_DATA"; private static final String USERTASK_CONTRACT_DATA = "USERTASK_CONTRACT_DATA"; private final ReadPersistenceService persistenceService; private final Recorder recorder; private final QueriableLoggerService queriableLoggerService; private final ArchiveService archiveService; public ContractDataServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder, final QueriableLoggerService queriableLoggerService, final ArchiveService archiveService) { this.persistenceService = persistenceService; this.recorder = recorder; this.queriableLoggerService = queriableLoggerService; this.archiveService = archiveService; } @Override public void addUserTaskData(final long userTaskId, final Map data) throws SContractDataCreationException { if (data == null) { return; } for (final Entry datum : data.entrySet()) { addUserTaskData(new STaskContractData(userTaskId, datum.getKey(), datum.getValue())); } } protected void addUserTaskData(final STaskContractData taskContractData) throws SContractDataCreationException { final SContractDataLogBuilder logBuilder = getQueriableLog(ActionType.CREATED, "Creating a new user task contract data", USERTASK_CONTRACT_DATA); try { recorder.recordInsert(new InsertRecord(taskContractData), USERTASK_CONTRACT_DATA); initiateLogBuilder(taskContractData.getId(), SQueriableLog.STATUS_OK, logBuilder, "addUserTaskData"); } catch (final SRecorderException re) { initiateLogBuilder(taskContractData.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "addUserTaskData"); throw new SContractDataCreationException(re); } } @Override public Serializable getUserTaskDataValue(final long userTaskId, final String dataName) throws SContractDataNotFoundException, SBonitaReadException { final Map parameters = new HashMap(); parameters.put("name", dataName); parameters.put("scopeId", userTaskId); final SelectOneDescriptor descriptor = new SelectOneDescriptor( "getContractDataByUserTaskIdAndDataName", parameters, STaskContractData.class); final STaskContractData contractData = persistenceService.selectOne(descriptor); if (contractData == null) { throw new SContractDataNotFoundException( "No contract data found named: " + dataName + " of user task: " + userTaskId); } return contractData.getValue(); } @Override public void deleteUserTaskData(final long userTaskId) throws SContractDataDeletionException { try { final List contractData = getContractDataOfUserTask(userTaskId); for (final STaskContractData data : contractData) { deleteUserTaskData(data); } } catch (final SBonitaReadException sbre) { throw new SContractDataDeletionException(sbre); } } @Override public void deleteArchivedUserTaskData(List sourceUserTaskIds) throws SContractDataDeletionException { try { archiveService.deleteFromQuery("deleteArchivedTaskContractData", singletonMap("scopeIds", sourceUserTaskIds)); } catch (SRecorderException e) { throw new SContractDataDeletionException(e); } } protected void deleteUserTaskData(final STaskContractData taskContractData) throws SContractDataDeletionException { try { recorder.recordDelete(new DeleteRecord(taskContractData), USERTASK_CONTRACT_DATA); } catch (final SRecorderException sre) { throw new SContractDataDeletionException(sre); } } @Override public void archiveAndDeleteUserTaskData(final long userTaskId, final long archiveDate) throws SObjectModificationException { try { final List contractData = getContractDataOfUserTask(userTaskId); if (!contractData.isEmpty()) { if (archiveService.isArchivable(SContractData.class)) { final ArchiveInsertRecord[] records = buildArchiveUserTaskRecords(contractData); archiveService.recordInserts(archiveDate, records); } for (STaskContractData taskContractData : contractData) { deleteUserTaskData(taskContractData); } } } catch (final SBonitaException sbe) { throw new SObjectModificationException(sbe); } } private ArchiveInsertRecord[] buildArchiveUserTaskRecords(final List taskContractData) { final ArchiveInsertRecord[] records = new ArchiveInsertRecord[taskContractData.size()]; int i = 0; for (final STaskContractData data : taskContractData) { if (data != null) { final SATaskContractData aData = new SATaskContractData(data); records[i] = new ArchiveInsertRecord(aData); i++; } } return records; } private List getContractDataOfUserTask(final long userTaskId) throws SBonitaReadException { final Map parameters = new HashMap(); parameters.put("scopeId", userTaskId); final QueryOptions queryOptions = new QueryOptions(0, 10000); final SelectListDescriptor descriptor = new SelectListDescriptor( "getContractDataByUserTaskId", parameters, STaskContractData.class, queryOptions); return persistenceService.selectList(descriptor); } private SContractDataLogBuilder getQueriableLog(final ActionType actionType, final String message, String contractDataPrefix) { final SContractDataLogBuilder logBuilder = new SContractDataLogBuilder(contractDataPrefix); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); return logBuilder; } private void initializeLogBuilder(final T logBuilder, final String message) { logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message); } private void updateLog(final ActionType actionType, final T logBuilder) { logBuilder.setActionType(actionType); } private void initiateLogBuilder(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder, final String callerMethodName) { logBuilder.actionScope(String.valueOf(objectId)); logBuilder.actionStatus(sQueriableLogStatus); logBuilder.objectId(objectId); final SQueriableLog log = logBuilder.build(); if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) { queriableLoggerService.log(this.getClass().getName(), callerMethodName, log); } } @Override public Serializable getArchivedUserTaskDataValue(final long userTaskId, final String dataName) throws SContractDataNotFoundException, SBonitaReadException { final ReadPersistenceService readPersistenceService = archiveService .getDefinitiveArchiveReadPersistenceService(); final Map parameters = new HashMap(); parameters.put("scopeId", userTaskId); parameters.put("name", dataName); final SelectOneDescriptor descriptor = new SelectOneDescriptor( "getArchivedContractDataByUserTaskIdAndDataName", parameters, SATaskContractData.class); final SATaskContractData contractData = readPersistenceService.selectOne(descriptor); if (contractData == null) { throw new SContractDataNotFoundException( "No contract data found named: " + dataName + " of user task: " + userTaskId); } return contractData.getValue(); } @Override public void addProcessData(final long processInstanceId, final Map data) throws SContractDataCreationException { if (data == null) { return; } for (final Entry datum : data.entrySet()) { addProcessData(new SProcessContractData(processInstanceId, datum.getKey(), datum.getValue())); } } protected void addProcessData(SProcessContractData processContractData) throws SContractDataCreationException { final SContractDataLogBuilder logBuilder = getQueriableLog(ActionType.CREATED, "Creating a new process contract data", PROCESS_CONTRACT_DATA); try { recorder.recordInsert(new InsertRecord(processContractData), PROCESS_CONTRACT_DATA); initiateLogBuilder(processContractData.getId(), SQueriableLog.STATUS_OK, logBuilder, "addProcessData"); } catch (final SRecorderException re) { initiateLogBuilder(processContractData.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "addProcessData"); throw new SContractDataCreationException(re); } } @Override public Serializable getProcessDataValue(final long processInstanceId, final String dataName) throws SContractDataNotFoundException, SBonitaReadException { final Map parameters = new HashMap(); parameters.put("name", dataName); parameters.put("scopeId", processInstanceId); final SelectOneDescriptor descriptor = new SelectOneDescriptor<>( "getContractDataByProcessInstanceIdAndDataName", parameters, SProcessContractData.class); final SProcessContractData contractData = persistenceService.selectOne(descriptor); if (contractData == null) { throw new SContractDataNotFoundException("No contract data found named: " + dataName + " for process instance with id: " + processInstanceId); } return contractData.getValue(); } @Override public void deleteProcessData(final long processInstanceId) throws SContractDataDeletionException { try { final List contractData = getContractDataOfProcess(processInstanceId); for (final SProcessContractData data : contractData) { deleteProcessData(data); } } catch (final SBonitaReadException sbre) { throw new SContractDataDeletionException(sbre); } } @Override public void deleteArchivedProcessData(List sourceProcessInstanceIds) throws SContractDataDeletionException { try { archiveService.deleteFromQuery("deleteArchivedProcessContractData", singletonMap("scopeIds", sourceProcessInstanceIds)); } catch (SRecorderException e) { throw new SContractDataDeletionException(e); } } protected void deleteProcessData(final SProcessContractData processContractData) throws SContractDataDeletionException { try { recorder.recordDelete(new DeleteRecord(processContractData), PROCESS_CONTRACT_DATA); } catch (final SRecorderException sre) { throw new SContractDataDeletionException(sre); } } @Override public void archiveAndDeleteProcessData(final long processInstanceId, final long archiveDate) throws SObjectModificationException { try { final List contractData = getContractDataOfProcess(processInstanceId); if (!contractData.isEmpty()) { if (archiveService.isArchivable(SContractData.class)) { final ArchiveInsertRecord[] records = buildArchiveProcessRecords(contractData); archiveService.recordInserts(archiveDate, records); } for (SProcessContractData processContractData : contractData) { deleteProcessData(processContractData); } } } catch (final SBonitaException sbe) { throw new SObjectModificationException(sbe); } } private ArchiveInsertRecord[] buildArchiveProcessRecords(final List processContractData) { final ArchiveInsertRecord[] records = new ArchiveInsertRecord[processContractData.size()]; int i = 0; for (final SProcessContractData data : processContractData) { if (data != null) { final SAProcessContractData aData = new SAProcessContractData(data); records[i] = new ArchiveInsertRecord(aData); i++; } } return records; } private List getContractDataOfProcess(final long processInstanceId) throws SBonitaReadException { final Map parameters = new HashMap(); parameters.put("scopeId", processInstanceId); final QueryOptions queryOptions = new QueryOptions(0, 10000); final SelectListDescriptor descriptor = new SelectListDescriptor<>( "getContractDataByProcessInstanceId", parameters, SProcessContractData.class, queryOptions); return persistenceService.selectList(descriptor); } @Override public Serializable getArchivedProcessDataValue(final long processInstanceId, final String dataName) throws SContractDataNotFoundException, SBonitaReadException { final ReadPersistenceService readPersistenceService = archiveService .getDefinitiveArchiveReadPersistenceService(); final Map parameters = new HashMap(); parameters.put("scopeId", processInstanceId); parameters.put("name", dataName); final SelectOneDescriptor descriptor = new SelectOneDescriptor<>( "getArchivedContractDataByProcessInstanceIdAndDataName", parameters, SAProcessContractData.class); final SAProcessContractData contractData = readPersistenceService.selectOne(descriptor); if (contractData == null) { throw new SContractDataNotFoundException( "No archived contract data found named: " + dataName + " of process instance: " + processInstanceId); } return contractData.getValue(); } } ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SAContractData.java ================================================ /** * 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.core.contract.data; import java.io.Serializable; import java.util.Collection; import java.util.Map; import javax.persistence.Column; import javax.persistence.DiscriminatorColumn; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.bpm.contract.FileInputValue; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; import org.bonitasoft.engine.persistence.PersistentObject; import org.hibernate.annotations.Type; /** * @author Emmanuel Duchastenier */ @Data @NoArgsConstructor @EqualsAndHashCode @Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "kind") @Table(name = "arch_contract_data") public abstract class SAContractData implements ArchivedPersistentObject { @Id protected long id; @Column protected long archiveDate; @Column protected long sourceObjectId; @Column protected String name; @Column(name = "val") @Type(type = "xml_blob") protected Serializable value; @Column protected long scopeId; protected SAContractData(long sourceObjectId, String name, Serializable value, long scopeId) { this.sourceObjectId = sourceObjectId; this.name = name; this.scopeId = scopeId; this.value = clearFileInputContent(value); } /** * Remove the {@link FileInputValue} content from Archived Contract Data * * @param value, The contract input value * @return The contract input value without file content in case of a {@link FileInputValue} */ private static Serializable clearFileInputContent(Serializable value) { if (value instanceof FileInputValue inputValue) { inputValue.setContent(null); } else if (value instanceof Map) { ((Map) value).values().stream() .filter(Serializable.class::isInstance) .map(Serializable.class::cast) .forEach(v -> clearFileInputContent(v)); } else if (value instanceof Collection) { ((Collection) value).stream() .filter(Serializable.class::isInstance) .map(Serializable.class::cast) .forEach(v -> clearFileInputContent(v)); } return value; } protected SAContractData(SContractData contractData) { this(contractData.getId(), contractData.getName(), contractData.getValue(), contractData.getScopeId()); } @Override public Class getPersistentObjectInterface() { return SContractData.class; } } ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SAProcessContractData.java ================================================ /** * 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.core.contract.data; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * author Emmanuel Duchastenier */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("PROCESS") public class SAProcessContractData extends SAContractData { public SAProcessContractData(SProcessContractData processContract) { super(processContract); } } ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SATaskContractData.java ================================================ /** * 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.core.contract.data; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("TASK") public class SATaskContractData extends SAContractData { public SATaskContractData(final SContractData contractData) { super(contractData); } public SATaskContractData(final STaskContractData taskContractData) { super(taskContractData); } } ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SContractData.java ================================================ /** * 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.core.contract.data; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorColumn; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.persistence.PersistentObject; import org.hibernate.annotations.Type; /** * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "kind") @Table(name = "contract_data") @SuperBuilder public abstract class SContractData implements PersistentObject { @Id private long id; @Column private String name; @Column(name = "val") @Type(type = "xml_blob") private Serializable value; @Column private long scopeId; protected SContractData(final String name, final Serializable value, long scopeId) { this.name = name; this.scopeId = scopeId; this.value = value; } } ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SContractDataCreationException.java ================================================ /** * 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.core.contract.data; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SContractDataCreationException extends SBonitaException { private static final long serialVersionUID = -850750385519813019L; public SContractDataCreationException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SContractDataDeletionException.java ================================================ /** * 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.core.contract.data; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SContractDataDeletionException extends SBonitaException { private static final long serialVersionUID = -6713711673811669252L; public SContractDataDeletionException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SContractDataLogBuilder.java ================================================ /** * 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.core.contract.data; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Matthieu Chaffotte */ public class SContractDataLogBuilder extends CRUDELogBuilder implements SPersistenceLogBuilder, HasCRUDEAction { private static final int CONTRACT_DATA_INDEX = 0; private String contractDataPrefix; // so that this object can be used for usertasks and process as well public SContractDataLogBuilder(String contractDataPrefix) { this.contractDataPrefix = contractDataPrefix; } @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(CONTRACT_DATA_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return contractDataPrefix; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(CONTRACT_DATA_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: Contract data Id"); } } } ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SContractDataNotFoundException.java ================================================ /** * 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.core.contract.data; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SContractDataNotFoundException extends SBonitaException { private static final long serialVersionUID = -6617607757355205604L; public SContractDataNotFoundException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/SProcessContractData.java ================================================ /** * 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.core.contract.data; import java.io.Serializable; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; /** * author Emmanuel Duchastenier */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("PROCESS") @SuperBuilder public class SProcessContractData extends SContractData { public SProcessContractData(final long processInstanceId, final String name, final Serializable value) { super(name, value, processInstanceId); } } ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/main/java/org/bonitasoft/engine/core/contract/data/STaskContractData.java ================================================ /** * 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.core.contract.data; import java.io.Serializable; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; /** * @author Nicolas Tith * @author Emmanuel Duchastenier */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("TASK") @SuperBuilder public class STaskContractData extends SContractData { public STaskContractData(final long userTaskId, final String name, final Serializable value) { super(name, value, userTaskId); } } ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/main/resources/org/bonitasoft/engine/core/contract/data/model/impl/hibernate/contract.queries.hbm.xml ================================================ SELECT tcd FROM org.bonitasoft.engine.core.contract.data.STaskContractData AS tcd WHERE tcd.scopeId = :scopeId SELECT tcd FROM org.bonitasoft.engine.core.contract.data.STaskContractData AS tcd WHERE tcd.scopeId = :scopeId AND name = :name SELECT atcd FROM org.bonitasoft.engine.core.contract.data.SATaskContractData AS atcd WHERE atcd.scopeId = :scopeId AND atcd.name = :name SELECT p FROM org.bonitasoft.engine.core.contract.data.SProcessContractData AS p WHERE p.scopeId = :scopeId SELECT p FROM org.bonitasoft.engine.core.contract.data.SProcessContractData AS p WHERE p.scopeId = :scopeId AND name = :name SELECT ap FROM org.bonitasoft.engine.core.contract.data.SAProcessContractData AS ap WHERE ap.scopeId = :scopeId AND ap.name = :name DELETE FROM org.bonitasoft.engine.core.contract.data.SAProcessContractData AS c WHERE c.scopeId IN (:scopeIds) DELETE FROM org.bonitasoft.engine.core.contract.data.SATaskContractData AS c WHERE c.scopeId IN (:scopeIds) ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/test/java/org/bonitasoft/engine/core/contract/data/ContractDataServiceImplTest.java ================================================ /** * 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.core.contract.data; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.services.QueriableLoggerService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ContractDataServiceImplTest { @Mock private ReadPersistenceService persistenceService; @Mock private Recorder recorder; @Mock private EventService eventService; @Mock private QueriableLoggerService queriableLoggerService; @Mock private ArchiveService archiveService; @InjectMocks private ContractDataServiceImpl contractDataService; @Before public void setUp() { when(archiveService.getDefinitiveArchiveReadPersistenceService()).thenReturn(persistenceService); when(archiveService.isArchivable(SContractData.class)).thenReturn(true); } @Test public void getUserTaskData_returns_the_stored_value() throws Exception { final STaskContractData contractData = new STaskContractData(1983L, "id", 10L); when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(contractData); final Long id = (Long) contractDataService.getUserTaskDataValue(1983L, "id"); assertThat(id).isEqualTo(10L); } @Test(expected = SContractDataNotFoundException.class) public void getUserTaskData_throws_an_exception_when_data_not_found() throws Exception { when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(null); contractDataService.getUserTaskDataValue(1983L, "id"); } @Test(expected = SBonitaReadException.class) public void getUserTaskData_throws_an_exception_with_a_read_exception() throws Exception { when(persistenceService.selectOne(any(SelectOneDescriptor.class))) .thenThrow(new SBonitaReadException("exception")); contractDataService.getUserTaskDataValue(1983L, "id"); } @Test public void addUserTaskData_store_values_for_the_user_task() throws Exception { final STaskContractData contractData = new STaskContractData(1983L, "id", 54L); contractData.setId(10L); when(queriableLoggerService.isLoggable(anyString(), any(SQueriableLogSeverity.class))).thenReturn(false); contractDataService.addUserTaskData(contractData); verify(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); } @Test public void addUserTaskData_accept_null_inputs() throws Exception { contractDataService.addUserTaskData(54L, null); verify(recorder, times(0)).recordInsert(any(InsertRecord.class), nullable(String.class)); } @Test(expected = SContractDataCreationException.class) public void addUserTaskData_throws_an_exception_if_not_able_to_store_the_data() throws Exception { final STaskContractData contractData = new STaskContractData(1983L, "id", 54L); contractData.setId(10L); when(queriableLoggerService.isLoggable(anyString(), any(SQueriableLogSeverity.class))).thenReturn(false); doThrow(new SRecorderException("exception")).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); contractDataService.addUserTaskData(contractData); } @Test public void deleteUserTaskData_deletes_data() throws Exception { final List data = new ArrayList(); data.add(new STaskContractData(1983L, "id", 456478L)); data.add(new STaskContractData(1983L, "id2", 4564456478L)); doReturn(data).when(persistenceService).selectList(any()); contractDataService.deleteUserTaskData(1983L); verify(recorder, times(2)).recordDelete(any(DeleteRecord.class), nullable(String.class)); } @Test(expected = SContractDataDeletionException.class) public void deleteUserTaskData_throws_exception() throws Exception { final List data = new ArrayList(); data.add(new STaskContractData(1983L, "id", 456478L)); data.add(new STaskContractData(1983L, "id2", 4564456478L)); doReturn(data).when(persistenceService).selectList(any()); doThrow(new SRecorderException("exception")).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); contractDataService.deleteUserTaskData(1983L); } @Test(expected = SContractDataDeletionException.class) public void deleteUserTaskData_throws_exception_when_retrieving_data() throws Exception { when(persistenceService.selectList(any(SelectListDescriptor.class))) .thenThrow(new SBonitaReadException("exception")); contractDataService.deleteUserTaskData(1983L); } @Test public void archiveUserTaskData_should_create_archive_data() throws Exception { final long usertTaskId = 1983L; final long time = 1010101010101001L; final List data = new ArrayList(); final STaskContractData scd1 = new STaskContractData(usertTaskId, "id", 456478L); final STaskContractData scd2 = new STaskContractData(usertTaskId, "id2", 4564456478L); data.add(scd1); data.add(scd2); final ArchiveInsertRecord[] archivedata = new ArchiveInsertRecord[2]; archivedata[0] = new ArchiveInsertRecord(new SATaskContractData(scd1)); archivedata[1] = new ArchiveInsertRecord(new SATaskContractData(scd2)); doReturn(data).when(persistenceService).selectList(any()); contractDataService.archiveAndDeleteUserTaskData(usertTaskId, time); verify(archiveService).recordInserts(time, archivedata); } @Test public void archiveContractData_should_not_create_archive_data_when_archivingContractData_is_disabled() throws Exception { final long usertTaskId = 1983L; final long time = 1010101010101001L; final List data = new ArrayList(); final STaskContractData scd1 = new STaskContractData(usertTaskId, "id", 456478L); final STaskContractData scd2 = new STaskContractData(usertTaskId, "id2", 4564456478L); data.add(scd1); data.add(scd2); final ArchiveInsertRecord[] archivedata = new ArchiveInsertRecord[2]; archivedata[0] = new ArchiveInsertRecord(new SATaskContractData(scd1)); archivedata[1] = new ArchiveInsertRecord(new SATaskContractData(scd2)); when(archiveService.isArchivable(SContractData.class)).thenReturn(false); doReturn(data).when(persistenceService).selectList(any()); contractDataService.archiveAndDeleteUserTaskData(usertTaskId, time); verify(archiveService, never()).recordInserts(time, archivedata); } @Test public void archiveUserTaskData_should_not_create_archive_data_if_no_data_defined() throws Exception { final long usertTaskId = 1983L; final long time = 1010101010101001L; final List data = new ArrayList(); doReturn(data).when(persistenceService).selectList(any()); contractDataService.archiveAndDeleteUserTaskData(usertTaskId, time); verifyNoInteractions(archiveService); } @Test(expected = SObjectModificationException.class) public void archiveUserTaskData_should_throw_exception_when_an_exception_occurs_when_getting_data() throws Exception { final long usertTaskId = 1983L; final long time = 1010101010101001L; when(persistenceService.selectList(any(SelectListDescriptor.class))) .thenThrow(new SBonitaReadException("exception")); contractDataService.archiveAndDeleteUserTaskData(usertTaskId, time); } @Test public void getArchivedUserTaskData_returns_the_stored_value() throws Exception { final STaskContractData contractData = new STaskContractData(1983L, "id", 10L); final SATaskContractData saTaskContractData = new SATaskContractData(contractData); saTaskContractData.setArchiveDate(768468743687L); when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(saTaskContractData); final Long id = (Long) contractDataService.getArchivedUserTaskDataValue(1983L, "id"); assertThat(id).isEqualTo(10L); } @Test(expected = SContractDataNotFoundException.class) public void getArchivedUserTaskData_throws_an_exception_when_data_not_found() throws Exception { when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(null); contractDataService.getArchivedUserTaskDataValue(1983L, "id"); } @Test(expected = SBonitaReadException.class) public void getArchivedUserTaskData_throws_an_exception_with_a_read_exception() throws Exception { when(persistenceService.selectOne(any(SelectOneDescriptor.class))) .thenThrow(new SBonitaReadException("exception")); contractDataService.getArchivedUserTaskDataValue(1983L, "id"); } /************* Process Data tests *************/ @Test public void addProcessDataStoreValuesForTheProcessInstance() throws Exception { final SProcessContractData contractData = new SProcessContractData(1983L, "id", 54L); contractData.setId(10L); when(queriableLoggerService.isLoggable(anyString(), any(SQueriableLogSeverity.class))).thenReturn(false); contractDataService.addProcessData(contractData); verify(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); } @Test public void addProcessData_accept_null_inputs() throws Exception { contractDataService.addProcessData(54L, null); verify(recorder, times(0)).recordInsert(any(InsertRecord.class), nullable(String.class)); } @Test(expected = SContractDataCreationException.class) public void addProcessData_throws_an_exception_if_not_able_to_store_the_data() throws Exception { final SProcessContractData contractData = new SProcessContractData(1983L, "id", 54L); contractData.setId(10L); when(queriableLoggerService.isLoggable(anyString(), any(SQueriableLogSeverity.class))).thenReturn(false); doThrow(new SRecorderException("exception")).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); contractDataService.addProcessData(contractData); } @Test public void getProcessDataReturnsTheStoredValue() throws Exception { long processInstanceId = 1117L; final SProcessContractData contractData = new SProcessContractData(processInstanceId, "TheName", "TheValue"); when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(contractData); final String dataValue = (String) contractDataService.getProcessDataValue(processInstanceId, "TheName"); assertThat(dataValue).isEqualTo("TheValue"); } @Test(expected = SContractDataNotFoundException.class) public void getProcessDataShouldThrowContractDataNotFoundWhenNoDataIsReturned() throws Exception { when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(null); contractDataService.getProcessDataValue(147L, null); } @Test(expected = SBonitaReadException.class) public void getProcessData_throws_an_exception_with_a_read_exception() throws Exception { when(persistenceService.selectOne(any(SelectOneDescriptor.class))) .thenThrow(new SBonitaReadException("exception")); contractDataService.getProcessDataValue(1983L, "id"); } @Test public void deleteProcessData_deletes_data() throws Exception { final List data = new ArrayList(); data.add(new SProcessContractData(1983L, "id", 456478L)); data.add(new SProcessContractData(1983L, "id2", 4564456478L)); when(persistenceService.selectList(any(SelectListDescriptor.class))).thenReturn(data); contractDataService.deleteProcessData(1983L); verify(recorder, times(2)).recordDelete(any(DeleteRecord.class), nullable(String.class)); } @Test(expected = SContractDataDeletionException.class) public void deleteProcessData_throws_exception() throws Exception { final List data = new ArrayList(); data.add(new SProcessContractData(1983L, "id", 456478L)); data.add(new SProcessContractData(1983L, "id2", 4564456478L)); when(persistenceService.selectList(any(SelectListDescriptor.class))).thenReturn(data); doThrow(new SRecorderException("exception")).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); contractDataService.deleteProcessData(1983L); } @Test(expected = SContractDataDeletionException.class) public void deleteProcessData_throws_exception_when_retrieving_data() throws Exception { when(persistenceService.selectList(any(SelectListDescriptor.class))) .thenThrow(new SBonitaReadException("exception")); contractDataService.deleteProcessData(1983L); } @Test public void archiveProcessData_should_create_archive_data() throws Exception { final long processInstanceId = 1983L; final long time = 1010101010101001L; final List data = new ArrayList(); final SProcessContractData scd1 = new SProcessContractData(processInstanceId, "id", 456478L); final SProcessContractData scd2 = new SProcessContractData(processInstanceId, "id2", 4564456478L); data.add(scd1); data.add(scd2); final ArchiveInsertRecord[] archivedata = new ArchiveInsertRecord[2]; archivedata[0] = new ArchiveInsertRecord(new SAProcessContractData(scd1)); archivedata[1] = new ArchiveInsertRecord(new SAProcessContractData(scd2)); when(persistenceService.selectList(any(SelectListDescriptor.class))).thenReturn(data); contractDataService.archiveAndDeleteProcessData(processInstanceId, time); verify(archiveService).recordInserts(time, archivedata); } @Test public void archiveProcessData_should_not_create_archive_data_if_no_data_defined() throws Exception { final long usertProcessId = 1983L; final long time = 1010101010101001L; final List data = new ArrayList(); when(persistenceService.selectList(any(SelectListDescriptor.class))).thenReturn(data); contractDataService.archiveAndDeleteProcessData(usertProcessId, time); verifyNoInteractions(archiveService); } @Test(expected = SObjectModificationException.class) public void archiveProcessData_should_throw_exception_when_an_exception_occurs_when_getting_data() throws Exception { final long usertProcessId = 1983L; final long time = 1010101010101001L; when(persistenceService.selectList(any(SelectListDescriptor.class))) .thenThrow(new SBonitaReadException("exception")); contractDataService.archiveAndDeleteProcessData(usertProcessId, time); } @Test public void getArchivedProcessData_returns_the_stored_value() throws Exception { final SProcessContractData contractData = new SProcessContractData(1983L, "id", 10L); final SAProcessContractData saProcessContractData = new SAProcessContractData(contractData); saProcessContractData.setArchiveDate(768468743687L); when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(saProcessContractData); final Long id = (Long) contractDataService.getArchivedProcessDataValue(1983L, "id"); assertThat(id).isEqualTo(10L); } @Test(expected = SContractDataNotFoundException.class) public void getArchivedProcessData_throws_an_exception_when_data_not_found() throws Exception { when(persistenceService.selectOne(any(SelectOneDescriptor.class))).thenReturn(null); contractDataService.getArchivedProcessDataValue(1983L, "id"); } @Test(expected = SBonitaReadException.class) public void getArchivedProcessData_throws_an_exception_with_a_read_exception() throws Exception { when(persistenceService.selectOne(any(SelectOneDescriptor.class))) .thenThrow(new SBonitaReadException("exception")); contractDataService.getArchivedProcessDataValue(1983L, "id"); } } ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/test/java/org/bonitasoft/engine/core/contract/data/SAProcessContractDataTest.java ================================================ /** * 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.core.contract.data; import static org.assertj.core.api.Assertions.assertThat; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.contract.FileInputValue; import org.junit.jupiter.api.Test; class SAProcessContractDataTest { @Test void clearFileInputContent() { var contractData = new SProcessContractData(); contractData.setId(1); contractData.setName("myFile"); contractData.setValue(new FileInputValue("theFile", "content".getBytes())); var archivedContractData = new SAProcessContractData(contractData); assertThat(archivedContractData.getValue()).isInstanceOf(FileInputValue.class) .extracting("content") .isNull(); } @Test void clearMultipleFileInputContent() { var contractData = new SProcessContractData(); contractData.setId(1); contractData.setName("myFile"); contractData.setValue((Serializable) List.of( new FileInputValue("theFile", "content".getBytes()), new FileInputValue("theFile1", "content1".getBytes()))); var archivedContractData = new SAProcessContractData(contractData); assertThat(archivedContractData.getValue()).isInstanceOf(Collection.class); assertThat((Collection) archivedContractData.getValue()) .extracting("content") .containsNull(); } @Test void clearComplexInputWithFileInputContent() { var contractData = new SProcessContractData(); contractData.setId(1); contractData.setName("parent"); contractData.setValue( (Serializable) Map.ofEntries(Map.entry("myFile", new FileInputValue("theFile", "content".getBytes())))); var archivedContractData = new SAProcessContractData(contractData); assertThat(archivedContractData.getValue()).isInstanceOf(Map.class); assertThat((Map) archivedContractData.getValue()) .hasEntrySatisfying("myFile", value -> assertThat(value).isInstanceOf(FileInputValue.class) .extracting("content") .isNull()); } @Test void creatingSAProcessContractDataShouldCopyNonArchivedValues() { long processInstanceId = 555L; String someName = "some_name"; long value = 999999L; final SProcessContractData processContractData = new SProcessContractData(processInstanceId, someName, value); long originalProcessDataId = 7548463269L; processContractData.setId(originalProcessDataId); final SAProcessContractData saProcessContractData = new SAProcessContractData(processContractData); assertThat(saProcessContractData.getId()).isZero(); assertThat(saProcessContractData.getName()).isEqualTo(someName); assertThat(saProcessContractData.getScopeId()).isEqualTo(processInstanceId); assertThat(saProcessContractData.getArchiveDate()).isZero(); assertThat(saProcessContractData.getSourceObjectId()).isEqualTo(originalProcessDataId); assertThat(saProcessContractData.getValue()).isEqualTo(value); } } ================================================ FILE: bpm/bonita-core/bonita-contract-data/src/test/java/org/bonitasoft/engine/core/contract/data/SATaskContractDataTest.java ================================================ /** * 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.core.contract.data; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class SATaskContractDataTest { @Test public void create_should_copy_values() { long userTaskId = 14444L; final STaskContractData taskContractData = new STaskContractData(userTaskId, "id", 15124245748545L); taskContractData.setId(7548463269L); final SATaskContractData saTaskContractData = new SATaskContractData(taskContractData); assertThat(saTaskContractData.getId()).isZero(); assertThat(saTaskContractData.getName()).isEqualTo("id"); assertThat(saTaskContractData.getScopeId()).isEqualTo(userTaskId); assertThat(saTaskContractData.getArchiveDate()).isZero(); assertThat(saTaskContractData.getSourceObjectId()).isEqualTo(7548463269L); assertThat(saTaskContractData.getValue()).isEqualTo(15124245748545L); } } ================================================ FILE: bpm/bonita-core/bonita-core-data/build.gradle ================================================ dependencies { api project(':bpm:bonita-core:bonita-process-instance') api project(':services:bonita-data-instance') api project(':services:bonita-expression') api project(':services:bonita-commons') api libs.commonsLang api libs.commonsText api project(':services:bonita-cache') api project(':services:bonita-persistence') testImplementation libs.assertj testImplementation libs.mockitoCore } ================================================ FILE: bpm/bonita-core/bonita-core-data/src/main/java/org/bonitasoft/engine/core/data/instance/TransientDataService.java ================================================ /** * 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.core.data.instance; import java.util.List; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; public interface TransientDataService { /** * @param dataNames * @param containerId * @param containerType * @return * @throws SDataInstanceException */ List getDataInstances(List dataNames, long containerId, String containerType) throws SDataInstanceException; /** * @param dataInstance * @throws SDataInstanceException */ void createDataInstance(SDataInstance dataInstance) throws SDataInstanceException; /** * @param dataInstance * @param descriptor * @throws SDataInstanceException */ void updateDataInstance(SDataInstance dataInstance, EntityUpdateDescriptor descriptor) throws SDataInstanceException; /** * @param dataInstance * @throws SDataInstanceException */ void deleteDataInstance(SDataInstance dataInstance) throws SDataInstanceException; /** * @param dataInstanceId * @return * @throws SDataInstanceException */ SDataInstance getDataInstance(long dataInstanceId) throws SDataInstanceException; /** * @param dataName * @param containerId * @param containerType * @return * @throws SDataInstanceException */ SDataInstance getDataInstance(String dataName, long containerId, String containerType) throws SDataInstanceException; /** * @param containerId * @param containerType * @param fromIndex * @param numberOfResults * @return * @throws SDataInstanceException */ List getDataInstances(long containerId, String containerType, int fromIndex, int numberOfResults) throws SDataInstanceException; /** * @param dataInstanceIds * @return */ List getDataInstances(List dataInstanceIds); } ================================================ FILE: bpm/bonita-core/bonita-core-data/src/main/java/org/bonitasoft/engine/core/data/instance/impl/TransientDataExpressionExecutorStrategy.java ================================================ /** * 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.core.data.instance.impl; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.bonitasoft.engine.core.data.instance.TransientDataService; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.NonEmptyContentExpressionExecutorStrategy; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta */ public class TransientDataExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy { private final TransientDataService transientDataService; public TransientDataExpressionExecutorStrategy(final TransientDataService transientDataService) { this.transientDataService = transientDataService; } @Override public Object evaluate(final SExpression expression, final Map dependencyValues, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException, SExpressionDependencyMissingException { return evaluate(Arrays.asList(expression), dependencyValues, resolvedExpressions, containerState).get(0); } @Override public ExpressionKind getExpressionKind() { return KIND_TRANSIENT_VARIABLE; } @Override public List evaluate(final List expressions, final Map dependencyValues, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException, SExpressionDependencyMissingException { long containerId; String containerType; final int maxExpressionSize = expressions.size(); final ArrayList dataNames = new ArrayList(maxExpressionSize); final HashMap results = new HashMap(maxExpressionSize); for (final SExpression sExpression : expressions) { final String dataName = sExpression.getContent(); if (dependencyValues.containsKey(dataName)) { final Serializable value = (Serializable) dependencyValues.get(dataName); results.put(dataName, value); } else { dataNames.add(dataName); } } if (dataNames.isEmpty()) { return buildExpressionResultSameOrderAsInputList(expressions, results); } if (dependencyValues != null && dependencyValues.containsKey(CONTAINER_ID_KEY) && dependencyValues.containsKey(CONTAINER_TYPE_KEY)) { String currentData = null; try { containerId = (Long) dependencyValues.get(CONTAINER_ID_KEY); containerType = (String) dependencyValues.get(CONTAINER_TYPE_KEY); for (final String name : dataNames) { currentData = name; SDataInstance dataInstance = transientDataService.getDataInstance(name, containerId, containerType); results.put(dataInstance.getName(), dataInstance.getValue()); } return buildExpressionResultSameOrderAsInputList(expressions, results); } catch (final SDataInstanceException e) { throw new SExpressionEvaluationException("Can't read transient data", e, currentData); } } throw new SExpressionDependencyMissingException( "The context to evaluate the data '" + dataNames + "' was not set"); } private List buildExpressionResultSameOrderAsInputList(final List expressions, final Map results) { return expressions.stream() .map(exp -> results.get(exp.getContent())) .collect(Collectors.toList()); } @Override public boolean mustPutEvaluatedExpressionInContext() { return true; } } ================================================ FILE: bpm/bonita-core/bonita-core-data/src/main/java/org/bonitasoft/engine/core/data/instance/impl/TransientDataServiceImpl.java ================================================ /** * 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.core.data.instance.impl; import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.toList; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.stream.Collectors; import org.apache.commons.text.WordUtils; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.cache.SCacheException; import org.bonitasoft.engine.commons.ClassReflector; import org.bonitasoft.engine.commons.exceptions.SReflectException; import org.bonitasoft.engine.core.data.instance.TransientDataService; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.data.instance.exception.SDataInstanceNotFoundException; import org.bonitasoft.engine.data.instance.exception.SDataInstanceReadException; import org.bonitasoft.engine.data.instance.exception.SUpdateDataInstanceException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder; import org.bonitasoft.engine.data.instance.model.exceptions.SDataInstanceNotWellFormedException; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.expression.exception.SExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Baptiste Mesta */ public class TransientDataServiceImpl implements TransientDataService { private static final Logger log = LoggerFactory.getLogger(TransientDataServiceImpl.class); static final String TRANSIENT_DATA_CACHE_NAME = "transient_data"; private final CacheService cacheService; private final ExpressionResolverService expressionResolverService; private final FlowNodeInstanceService flowNodeInstanceService; private final ProcessDefinitionService processDefinitionService; public TransientDataServiceImpl(final CacheService cacheService, ExpressionResolverService expressionResolverService, FlowNodeInstanceService flowNodeInstanceService, ProcessDefinitionService processDefinitionService) { this.cacheService = cacheService; this.expressionResolverService = expressionResolverService; this.flowNodeInstanceService = flowNodeInstanceService; this.processDefinitionService = processDefinitionService; } @Override public List getDataInstances(final List dataNames, final long containerId, final String containerType) throws SDataInstanceException { final ArrayList data = new ArrayList<>(dataNames.size()); for (final String dataName : dataNames) { data.add(getDataInstance(dataName, containerId, containerType)); } return data; } private static String getKey(final String dataInstanceName, final long containerId, final String containerType) { return dataInstanceName + ":" + containerId + ":" + containerType; } static String getKey(final SDataInstance dataInstance) { return getKey(dataInstance.getName(), dataInstance.getContainerId(), dataInstance.getContainerType()); } @Override public void createDataInstance(final SDataInstance dataInstance) throws SDataInstanceException { try { final String dataInstanceKey = getKey(dataInstance); setId(dataInstance); cacheService.store(TRANSIENT_DATA_CACHE_NAME, dataInstanceKey, dataInstance); } catch (final Exception e) { throw new SDataInstanceException("Impossible to store transient data", e); } } private void setId(final SDataInstance dataInstance) throws SecurityException, IllegalArgumentException, SReflectException { // FIXME: probably the id will be used, so not necessary to be set final long id = Math.abs(UUID.randomUUID().getMostSignificantBits()); ClassReflector.invokeSetter(dataInstance, "setId", long.class, id); } @Override public void updateDataInstance(final SDataInstance dataInstance, final EntityUpdateDescriptor descriptor) throws SDataInstanceException { try { final String key = getKey(dataInstance); for (final Map.Entry field : descriptor.getFields().entrySet()) { try { final String setterName = "set" + WordUtils.capitalize(field.getKey()); ClassReflector.invokeMethodByName(dataInstance, setterName, field.getValue()); } catch (final Exception e) { throw new SUpdateDataInstanceException("Problem while updating entity: " + dataInstance + " with id: " + dataInstance.getId() + " in TransientDataInstanceDataSource.", e); } } cacheService.store(TRANSIENT_DATA_CACHE_NAME, key, dataInstance); } catch (final SCacheException e) { throw new SDataInstanceException("Impossible to update transient data", e); } } @Override public void deleteDataInstance(final SDataInstance dataInstance) throws SDataInstanceException { try { final String key = getKey(dataInstance); cacheService.remove(TRANSIENT_DATA_CACHE_NAME, key); } catch (final SCacheException e) { throw new SDataInstanceException("Impossible to delete transient data", e); } } @Override public SDataInstance getDataInstance(final long dataInstanceId) throws SDataInstanceException { try { final List cacheKeys = getCacheKeys(TRANSIENT_DATA_CACHE_NAME); for (final Object key : cacheKeys) { final SDataInstance dataInstance = (SDataInstance) cacheService.get(TRANSIENT_DATA_CACHE_NAME, key); if (dataInstance != null && dataInstance.getId() == dataInstanceId) { return dataInstance; } } } catch (final SCacheException e) { throw new SDataInstanceException("Impossible to get transient data: ", e); } throw new SDataInstanceNotFoundException("No data found. Id: " + dataInstanceId); } @Override public SDataInstance getDataInstance(final String dataName, final long containerId, final String containerType) throws SDataInstanceException { try { final List cacheKeys = getCacheKeys(TRANSIENT_DATA_CACHE_NAME); final String key = getKey(dataName, containerId, containerType); if (!cacheKeys.contains(key)) { reevaluateTransientData(dataName, containerId, containerType); } return (SDataInstance) cacheService.get(TRANSIENT_DATA_CACHE_NAME, key); } catch (final SCacheException | SProcessDefinitionNotFoundException | SBonitaReadException | SFlowNodeNotFoundException | SFlowNodeReadException | SExpressionException e) { throw new SDataInstanceException("Impossible to get transient data: ", e); } } private List getCacheKeys(final String cacheName) throws SCacheException { List cacheKeys = Collections.emptyList(); if (cacheService.getCachesNames().contains(cacheName)) { cacheKeys = cacheService.getKeys(cacheName); } return cacheKeys; } private void reevaluateTransientData(final String name, final long containerId, final String containerType) throws SProcessDefinitionNotFoundException, SBonitaReadException, SFlowNodeNotFoundException, SFlowNodeReadException, SDataInstanceException, SExpressionException { SFlowNodeInstance flowNodeInstance = flowNodeInstanceService.getFlowNodeInstance(containerId); final long flowNodeDefinitionId = flowNodeInstance.getFlowNodeDefinitionId(); final long processDefinitionId = flowNodeInstance.getProcessDefinitionId(); final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId); final SActivityDefinition flowNode = (SActivityDefinition) processDefinition.getProcessContainer() .getFlowNode(flowNodeDefinitionId); SDataDefinition dataDefinition = getTransientData(containerId).stream() .filter(data -> Objects.equals(name, data.getName())) .findFirst() .orElseThrow(() -> new SDataInstanceNotFoundException( "Transient data was not found and we were unable to reevaluate it because it was not found in the definition, name=<" + name + "> process definition=<" + processDefinition.getName() + "," + processDefinition.getVersion() + "> flow node=<" + flowNode.getName() + ">")); createDataInstance(dataDefinition, containerId, DataInstanceContainer.ACTIVITY_INSTANCE, new SExpressionContext(containerId, containerType, processDefinitionId)); } private List getTransientData(long containerId) throws SFlowNodeNotFoundException, SFlowNodeReadException, SProcessDefinitionNotFoundException, SBonitaReadException { SFlowNodeInstance flowNodeInstance = flowNodeInstanceService.getFlowNodeInstance(containerId); final long flowNodeDefinitionId = flowNodeInstance.getFlowNodeDefinitionId(); final long processDefinitionId = flowNodeInstance.getProcessDefinitionId(); final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId); final SActivityDefinition flowNode = (SActivityDefinition) processDefinition.getProcessContainer() .getFlowNode(flowNodeDefinitionId); return flowNode != null ? flowNode.getSDataDefinitions() .stream() .filter(SDataDefinition::isTransientData) .collect(Collectors.toList()) : Collections.emptyList(); } private void createDataInstance(SDataDefinition dataDefinition, final long containerId, final DataInstanceContainer containerType, final SExpressionContext expressionContext) throws SDataInstanceException, SExpressionException { Serializable dataValue = null; final SExpression defaultValueExpression = dataDefinition.getDefaultValueExpression(); if (defaultValueExpression != null) { log.warn(String.format( "The value of the transient data %s of %s %s is reevaluated from its default value expression.", dataDefinition.getName(), containerId, containerType)); dataValue = (Serializable) expressionResolverService.evaluate(dataDefinition.getDefaultValueExpression(), expressionContext); } else { log.warn("Creating a transient data instance with a null expression is not a good practice."); } try { createDataInstance(SDataInstanceBuilder.createNewInstance(dataDefinition, containerId, containerType.name(), dataValue)); } catch (final SDataInstanceNotWellFormedException e) { throw new SDataInstanceReadException(e); } } @Override public List getDataInstances(final long containerId, final String containerType, final int fromIndex, final int numberOfResults) throws SDataInstanceException { try { return getTransientData(containerId).stream() .skip(fromIndex * numberOfResults) .limit(numberOfResults) .map(data -> { try { return getDataInstance(data.getName(), containerId, containerType); } catch (SDataInstanceException e) { throw new BonitaRuntimeException( String.format("Transient data '%s' not found for container %s with type %s", data.getName(), containerId, containerType), e); } }) .collect(collectingAndThen(toList(), Collections::unmodifiableList)); } catch (SProcessDefinitionNotFoundException | SFlowNodeNotFoundException | SFlowNodeReadException | SBonitaReadException e) { throw new SDataInstanceException( String.format("An error occurred while retrieving transient data for container %s with type %s", containerId, containerType), e); } } @Override public List getDataInstances(final List dataInstanceIds) { final List results = new ArrayList<>(dataInstanceIds.size()); for (final Long dataInstanceId : dataInstanceIds) { try { results.add(getDataInstance(dataInstanceId)); } catch (final SDataInstanceException e) { } } return results; } } ================================================ FILE: bpm/bonita-core/bonita-core-data/src/test/java/org/bonitasoft/engine/core/data/instance/impl/TransientDataServiceImplTest.java ================================================ /** * 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.core.data.instance.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.cache.SCacheException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.SShortTextDataInstance; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.junit.Before; 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.Spy; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class TransientDataServiceImplTest { @Mock private CacheService cacheService; @Mock private ExpressionResolverService expressionResolverService; @Mock private FlowNodeInstanceService flowNodeInstanceService; @Mock private ProcessDefinitionService processDefinitionService; @InjectMocks @Spy private TransientDataServiceImpl transientDataServiceImpl; @Rule public ExpectedException expectedException = ExpectedException.none(); @Before public void before() { when(cacheService.getCachesNames()).thenReturn(Arrays.asList("transient_data")); } @Test public void should_createDataInstance_add_in_cache() throws Exception { // given SShortTextDataInstance data = createData(12, 42, "name", "containerType"); // when transientDataServiceImpl.createDataInstance(data); // then assertThat(data.getId()).isGreaterThan(0); verify(cacheService, times(1)).store("transient_data", "name:42:containerType", data); } private SShortTextDataInstance createData(final long id, final int containerId, final String name, final String containerType) throws SCacheException { SShortTextDataInstance data = new SShortTextDataInstance(); data.setId(id); data.setName(name); data.setTransientData(true); data.setContainerId(containerId); data.setContainerType(containerType); data.setValue("A value"); when(cacheService.get("transient_data", name + ":" + containerId + ":" + containerType)).thenReturn(data); return data; } @Test public void testUpdateDataInstance() throws Exception { // given SShortTextDataInstance data = createData(12, 42, "name", "ctype"); when(cacheService.getKeys("transient_data")).thenReturn(Arrays.asList((Object) "name:42:ctype")); // when EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor(); entityUpdateDescriptor.addField("value", "newValue"); transientDataServiceImpl.updateDataInstance(data, entityUpdateDescriptor); // then assertThat(transientDataServiceImpl.getDataInstance(12).getValue()).isEqualTo("newValue"); verify(cacheService, times(1)).store("transient_data", "name:42:ctype", data); } @Test public void testDeleteDataInstance() throws Exception { SShortTextDataInstance data = createData(12, 42, "name", "ctype"); transientDataServiceImpl.deleteDataInstance(data); verify(cacheService).remove("transient_data", "name:42:ctype"); } @Test public void should_getDataInstanceById_return_the_data() throws Exception { // given SShortTextDataInstance data = createData(12, 42, "name", "ctype"); when(cacheService.getKeys("transient_data")).thenReturn(Arrays.asList((Object) "name:42:ctype")); // when SDataInstance result = transientDataServiceImpl.getDataInstance(12); // then assertThat(result).isEqualTo(data); } @Test public void testGetDataInstanceStringLongString() throws Exception { // given SShortTextDataInstance data = createData(12, 42, "name", "ctype"); when(cacheService.getKeys("transient_data")).thenReturn(Arrays.asList((Object) "name:42:ctype")); // when SDataInstance result = transientDataServiceImpl.getDataInstance("name", 42, "ctype"); // then assertThat(result).isEqualTo(data); } @Test public void should_get_multiple_data_instances_from_a_container() throws Exception { SFlowNodeInstance flowNodeInstance = flowNodeInstance(42, 1); SActivityDefinition activityDefinition = flowNodeDefinition(dataWithName("name", null), dataWithName("name1", null), dataWithName("name2", null)); SProcessDefinition processDef = mock(SProcessDefinition.class); SFlowElementContainerDefinition container = mock(SFlowElementContainerDefinition.class); when(processDef.getProcessContainer()).thenReturn(container); when(container.getFlowNode(42)).thenReturn(activityDefinition); when(processDefinitionService.getProcessDefinition(1)).thenReturn(processDef); when(flowNodeInstanceService.getFlowNodeInstance(42)).thenReturn(flowNodeInstance); SShortTextDataInstance data = createData(12, 42, "name", "ctype"); SShortTextDataInstance data1 = createData(13, 42, "name1", "ctype"); SShortTextDataInstance data2 = createData(14, 42, "name2", "ctype"); when(cacheService.getKeys("transient_data")) .thenReturn(Arrays.asList("name:42:ctype", "name:44:ctype", "name:48:ctype")); List dataInstances = transientDataServiceImpl.getDataInstances(42, "ctype", 0, 10); assertThat(dataInstances.size()).isEqualTo(3); assertThat(dataInstances).contains(data, data1, data2); } @Test public void should_return_empty_data_list_for_flownodes_not_in_definition() throws Exception { //happens when flow node is e.g. a manual task SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl("p", "1"); SFlowNodeInstance flowNodeInstance = flowNodeInstance(42, 1); when(processDefinitionService.getProcessDefinition(1)).thenReturn(processDefinition); when(flowNodeInstanceService.getFlowNodeInstance(42)).thenReturn(flowNodeInstance); List dataInstances = transientDataServiceImpl.getDataInstances(42, "ctype", 0, 10); assertThat(dataInstances).isEmpty(); } private SDataDefinition dataWithName(String dataName, SExpression defaultValueExpression) { SDataDefinition dataDef = mock(SDataDefinition.class); when(dataDef.isTransientData()).thenReturn(true); when(dataDef.getName()).thenReturn(dataName); if (defaultValueExpression != null) { when(dataDef.getDefaultValueExpression()).thenReturn(defaultValueExpression); } return dataDef; } @Test public void should_paginate_result_when_retrieving_multiple_DataInstance() throws Exception { SFlowNodeInstance flowNodeInstance = flowNodeInstance(42, 1); SDataDefinition dataDef = dataWithName("name", null); SActivityDefinition activityDefinition = flowNodeDefinition(dataDef); SProcessDefinition processDef = mock(SProcessDefinition.class); SFlowElementContainerDefinition container = mock(SFlowElementContainerDefinition.class); when(processDef.getProcessContainer()).thenReturn(container); when(container.getFlowNode(42)).thenReturn(activityDefinition); when(processDefinitionService.getProcessDefinition(1)).thenReturn(processDef); when(flowNodeInstanceService.getFlowNodeInstance(42)).thenReturn(flowNodeInstance); when(cacheService.getKeys("transient_data")).thenReturn(Arrays.asList((Object) "name:42:ctype")); assertThat(transientDataServiceImpl.getDataInstances(42, "ctype", 0, 10)).hasSize(1); assertThat(transientDataServiceImpl.getDataInstances(42, "ctype", 0, 1)).hasSize(1); assertThat(transientDataServiceImpl.getDataInstances(42, "ctype", 1, 1)).isEmpty(); } @Test public void should_reevaluate_a_transient_data_instance_if_not_found_in_cache_but_data_definition_exists() throws Exception { // given SFlowNodeInstance flowNodeInstance = flowNodeInstance(42, 1); SExpression defaultValueExpression = mock(SExpression.class); SDataDefinition dataDef = dataWithName("name", defaultValueExpression); SActivityDefinition activityDefinition = flowNodeDefinition(dataDef); SProcessDefinition processDef = mock(SProcessDefinition.class); SFlowElementContainerDefinition container = mock(SFlowElementContainerDefinition.class); when(processDef.getProcessContainer()).thenReturn(container); when(container.getFlowNode(42)).thenReturn(activityDefinition); when(processDefinitionService.getProcessDefinition(1)).thenReturn(processDef); when(flowNodeInstanceService.getFlowNodeInstance(42)).thenReturn(flowNodeInstance); when(expressionResolverService.evaluate(eq(defaultValueExpression), notNull())).thenReturn("new data value"); // when transientDataServiceImpl.getDataInstance("name", 42, "ctype"); // then verify(expressionResolverService).evaluate(eq(defaultValueExpression), notNull()); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(SDataInstance.class); verify(transientDataServiceImpl).createDataInstance(argumentCaptor.capture()); SDataInstance dataInstance = argumentCaptor.getValue(); verify(cacheService).store(TransientDataServiceImpl.TRANSIENT_DATA_CACHE_NAME, TransientDataServiceImpl.getKey(dataInstance), dataInstance); } @Test public void should_throw_a_SDataInstanceException_when_trying_reevaluate_a_data_not_defined() throws Exception { // given SFlowNodeInstance flowNodeInstance = flowNodeInstance(42, 1); SActivityDefinition activityDefinition = flowNodeDefinition(); SProcessDefinition processDef = mock(SProcessDefinition.class); SFlowElementContainerDefinition container = mock(SFlowElementContainerDefinition.class); when(processDef.getProcessContainer()).thenReturn(container); when(container.getFlowNode(42)).thenReturn(activityDefinition); when(processDefinitionService.getProcessDefinition(1)).thenReturn(processDef); when(flowNodeInstanceService.getFlowNodeInstance(42)).thenReturn(flowNodeInstance); // then expectedException.expect(SDataInstanceException.class); // when transientDataServiceImpl.getDataInstance("name", 42, "ctype"); } private SFlowNodeInstance flowNodeInstance(long defId, long processDefId) { SFlowNodeInstance instance = mock(SFlowNodeInstance.class); when(instance.getFlowNodeDefinitionId()).thenReturn(defId); when(instance.getProcessDefinitionId()).thenReturn(processDefId); return instance; } private SActivityDefinition flowNodeDefinition(SDataDefinition... dataDefs) { SActivityDefinition definition = mock(SActivityDefinition.class); if (dataDefs.length > 0) { when(definition.getSDataDefinitions()).thenReturn(Arrays.asList(dataDefs)); } else { when(definition.getSDataDefinitions()).thenReturn(Collections.emptyList()); } return definition; } } ================================================ FILE: bpm/bonita-core/bonita-form-mapping/build.gradle ================================================ dependencies { api project(':bpm:bonita-common') api project(':services:bonita-session') api project(':bpm:bonita-core:bonita-process-definition') api project(':services:bonita-page') api project(':services:bonita-builder') api project(':services:bonita-persistence') api project(':services:bonita-commons') api project(':services:bonita-events') api libs.hibernateCore testImplementation libs.assertj testImplementation libs.mockitoCore annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/AuthorizationRuleMapping.java ================================================ /** * 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.core.form; import java.util.List; /** * @author Laurent Leseigneur */ public interface AuthorizationRuleMapping { /** * @return a list of rule identifiers applied when a form or page is used to start a process */ List getProcessStartRuleKeys(); /** * @return a list of rule identifiers applied when a form or page is used to display process overview */ List getProcessOverviewRuleKeys(); /** * @return a list of rule identifiers applied when a form or page is used to execute a task */ List getTaskRuleKeys(); } ================================================ FILE: bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/FormMappingKeyGenerator.java ================================================ /** * 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.core.form; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; /** * @author Baptiste Mesta */ public interface FormMappingKeyGenerator { String generateKey(long processDefinitionId, String task, Integer type) throws SObjectCreationException; } ================================================ FILE: bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/FormMappingService.java ================================================ /** * 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.core.form; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Baptiste Mesta */ public interface FormMappingService { SFormMapping create(long processDefinitionId, String task, Integer type, String target, String form) throws SObjectCreationException, SBonitaReadException; void update(SFormMapping formMapping, String url, Long pageId) throws SObjectModificationException; void delete(SFormMapping formMapping) throws SObjectModificationException; SFormMapping get(long formMappingId) throws SBonitaReadException, SObjectNotFoundException; SFormMapping get(String key) throws SBonitaReadException, SObjectNotFoundException; SFormMapping get(long processDefinitionId, Integer type, String task) throws SBonitaReadException, SObjectNotFoundException; SFormMapping get(long processDefinitionId, Integer type) throws SBonitaReadException, SObjectNotFoundException; List list(long processDefinitionId, int fromIndex, int numberOfResults) throws SBonitaReadException; List list(int fromIndex, int numberOfResults) throws SBonitaReadException; List searchFormMappings(QueryOptions queryOptions) throws SBonitaReadException; long getNumberOfFormMappings(QueryOptions queryOptions) throws SBonitaReadException; } ================================================ FILE: bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/SFormMapping.java ================================================ /** * 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.core.form; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.form.FormMappingType; import org.bonitasoft.engine.page.SPageMapping; import org.bonitasoft.engine.persistence.PersistentObject; @Entity @Data @AllArgsConstructor @NoArgsConstructor @Builder @Table(name = "form_mapping") public class SFormMapping implements PersistentObject { public static final String TARGET_INTERNAL = "INTERNAL"; public static final String TARGET_URL = "URL"; public static final String TARGET_LEGACY = "LEGACY"; public static final String TARGET_UNDEFINED = "UNDEFINED"; public static final String TARGET_NONE = "NONE"; public static final int TYPE_PROCESS_START = 1; public static final int TYPE_PROCESS_OVERVIEW = 2; public static final int TYPE_TASK = 3; @Id private long id; @Column(name = "process") private long processDefinitionId; private String task; private String target; @ManyToOne @JoinColumn(name = "page_mapping_id", referencedColumnName = "id") private SPageMapping pageMapping; private Integer type; private long lastUpdateDate; private long lastUpdatedBy; public SFormMapping(long processDefinitionId, Integer type, String task, String target) { this.processDefinitionId = processDefinitionId; this.task = task; this.type = type; this.target = target; } public String getProcessElementName() { switch (FormMappingType.getTypeFromId(this.getType())) { case TASK: return this.getTask(); case PROCESS_OVERVIEW: return FormMappingType.PROCESS_OVERVIEW.toString(); case PROCESS_START: return FormMappingType.PROCESS_START.toString(); default: return null; } } } ================================================ FILE: bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/impl/AuthorizationRuleMappingImpl.java ================================================ /** * 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.core.form.impl; import static org.bonitasoft.engine.page.AuthorizationRuleConstants.*; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.core.form.AuthorizationRuleMapping; /** * @author Laurent Leseigneur */ public class AuthorizationRuleMappingImpl implements AuthorizationRuleMapping { @Override public List getProcessStartRuleKeys() { return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_ACTOR_INITIATOR); } @Override public List getProcessOverviewRuleKeys() { return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_PROCESS_INITIATOR, IS_TASK_PERFORMER, IS_INVOLVED_IN_PROCESS_INSTANCE); } @Override public List getTaskRuleKeys() { return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_TASK_AVAILABLE_FOR_USER); } } ================================================ FILE: bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/impl/FormMappingKeyGeneratorImpl.java ================================================ /** * 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.core.form.impl; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.core.form.FormMappingKeyGenerator; import org.bonitasoft.engine.core.form.SFormMapping; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Baptiste Mesta */ public class FormMappingKeyGeneratorImpl implements FormMappingKeyGenerator { private final ProcessDefinitionService processDefinitionService; public FormMappingKeyGeneratorImpl(ProcessDefinitionService processDefinitionService) { this.processDefinitionService = processDefinitionService; } @Override public String generateKey(long processDefinitionId, String task, Integer type) throws SObjectCreationException { SProcessDefinition processDefinition; try { processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId); if (processDefinition == null) { throw new SObjectCreationException("Process with id " + processDefinitionId + " does not exists"); } } catch (SProcessDefinitionNotFoundException | SBonitaReadException e) { throw new SObjectCreationException("Unable to get the process with id " + processDefinitionId, e); } switch (type) { case SFormMapping.TYPE_PROCESS_OVERVIEW: return "processInstance/" + processDefinition.getName() + "/" + processDefinition.getVersion(); case SFormMapping.TYPE_PROCESS_START: return "process/" + processDefinition.getName() + "/" + processDefinition.getVersion(); case SFormMapping.TYPE_TASK: if (task == null || task.isEmpty()) { throw new SObjectCreationException("The task name is not set"); } return "taskInstance/" + processDefinition.getName() + "/" + processDefinition.getVersion() + "/" + task; } throw new SObjectCreationException("Unable to generate the key for the unknown type " + type); } } ================================================ FILE: bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/impl/FormMappingServiceImpl.java ================================================ /** * 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.core.form.impl; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SDeletionException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.core.form.AuthorizationRuleMapping; import org.bonitasoft.engine.core.form.FormMappingKeyGenerator; import org.bonitasoft.engine.core.form.FormMappingService; import org.bonitasoft.engine.core.form.SFormMapping; import org.bonitasoft.engine.form.FormMappingType; import org.bonitasoft.engine.page.PageMappingService; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.page.SPageMapping; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; import org.bonitasoft.engine.session.SSessionNotFoundException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; import org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException; /** * @author Baptiste Mesta */ public class FormMappingServiceImpl implements FormMappingService { public static final String FORM_MAPPING = "FORM_MAPPING"; private final Recorder recorder; private final ReadPersistenceService persistenceService; private final SessionService sessionService; private final ReadSessionAccessor sessionAccessor; private final PageMappingService pageMappingService; private final PageService pageService; private final FormMappingKeyGenerator formMappingKeyGenerator; private final String externalUrlAdapter; private final String legacyUrlAdapter; private final Map> authorizationRulesMap; private final QueriableLoggerService queriableLoggerService; public FormMappingServiceImpl(Recorder recorder, ReadPersistenceService persistenceService, SessionService sessionService, ReadSessionAccessor sessionAccessor, PageMappingService pageMappingService, PageService pageService, FormMappingKeyGenerator formMappingKeyGenerator, String externalUrlAdapter, String legacyUrlAdapter, QueriableLoggerService queriableLoggerService, AuthorizationRuleMapping authorizationRuleMapping) { this.recorder = recorder; this.persistenceService = persistenceService; this.sessionService = sessionService; this.sessionAccessor = sessionAccessor; this.pageMappingService = pageMappingService; this.pageService = pageService; this.formMappingKeyGenerator = formMappingKeyGenerator; this.externalUrlAdapter = externalUrlAdapter; this.legacyUrlAdapter = legacyUrlAdapter; this.queriableLoggerService = queriableLoggerService; authorizationRulesMap = new HashMap<>(3); authorizationRulesMap.put(FormMappingType.PROCESS_START, authorizationRuleMapping.getProcessStartRuleKeys()); authorizationRulesMap.put(FormMappingType.TASK, authorizationRuleMapping.getTaskRuleKeys()); authorizationRulesMap.put(FormMappingType.PROCESS_OVERVIEW, authorizationRuleMapping.getProcessOverviewRuleKeys()); } @Override public SFormMapping create(long processDefinitionId, String task, Integer type, String target, String form) throws SBonitaReadException, SObjectCreationException { if (target == null) { throw new IllegalArgumentException("Form target is null"); } SPageMapping sPageMapping; String key = formMappingKeyGenerator.generateKey(processDefinitionId, task, type); List authorizationRules = buildAuthorizationRules(type); switch (target) { case SFormMapping.TARGET_INTERNAL: sPageMapping = pageMappingService.create(key, getPageIdOrNull(form, processDefinitionId), authorizationRules); break; case SFormMapping.TARGET_URL: sPageMapping = pageMappingService.create(key, form, externalUrlAdapter, authorizationRules); break; case SFormMapping.TARGET_LEGACY: sPageMapping = pageMappingService.create(key, null, legacyUrlAdapter, null); break; case SFormMapping.TARGET_UNDEFINED: sPageMapping = null; break; case SFormMapping.TARGET_NONE: sPageMapping = pageMappingService.create(key, null, authorizationRules); break; default: throw new IllegalArgumentException("Illegal form target " + target); } SFormMapping sFormMapping = new SFormMapping(processDefinitionId, type, task, target); insertFormMapping(sFormMapping, sPageMapping); return sFormMapping; } private List buildAuthorizationRules(Integer type) { return authorizationRulesMap.get(FormMappingType.getTypeFromId(type)); } Long getPageIdOrNull(String form, long processDefinitionId) throws SBonitaReadException { SPage pageByName = pageService.getPageByNameAndProcessDefinitionId(form, processDefinitionId); if (pageByName == null) { pageByName = pageService.getPageByName(form); } return pageByName == null ? null : pageByName.getId(); } private void insertFormMapping(SFormMapping sFormMapping, SPageMapping sPageMapping) throws SObjectCreationException { sFormMapping.setPageMapping(sPageMapping); FormMappingLogBuilder logBuilder = getLogBuilder(ActionType.CREATED); try { recorder.recordInsert(new InsertRecord(sFormMapping), FORM_MAPPING); log(sFormMapping, SQueriableLog.STATUS_OK, logBuilder, "insertFormMapping", "create"); } catch (SRecorderException e) { log(sFormMapping, SQueriableLog.STATUS_FAIL, logBuilder, "insertFormMapping", "failed to create"); throw new SObjectCreationException(e); } } @Override public void update(SFormMapping formMapping, String url, Long pageId) throws SObjectModificationException { String target = getFormMappingTarget(url, pageId); String urlAdapter = checkAndGetUrlAdapter(url); checkThatInternalPageExists(pageId); FormMappingLogBuilder logBuilder = getLogBuilder(ActionType.UPDATED); EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor(); try { Long oldPageId = null; String oldUrlAdapter = null; String oldUrl = null; entityUpdateDescriptor.addField("target", target); entityUpdateDescriptor.addField("lastUpdatedBy", getSessionUserId()); entityUpdateDescriptor.addField("lastUpdateDate", System.currentTimeMillis()); // case where page mapping did not exist already (TARGET == UNDEFINED): if (formMapping.getPageMapping() == null) { SPageMapping sPageMapping = createPageMappingForExistingFormMapping(formMapping, url, pageId); formMapping.setPageMapping(sPageMapping); } else { oldPageId = formMapping.getPageMapping().getPageId(); oldUrlAdapter = formMapping.getPageMapping().getUrlAdapter(); oldUrl = formMapping.getPageMapping().getUrl(); // Update the existing page mapping: entityUpdateDescriptor.addField("pageMapping.url", url); entityUpdateDescriptor.addField("pageMapping.urlAdapter", urlAdapter); entityUpdateDescriptor.addField("pageMapping.pageId", pageId); } recorder.recordUpdate(UpdateRecord.buildSetFields(formMapping, entityUpdateDescriptor), FORM_MAPPING); final String rawMessage = "Previous: pageId=<" + oldPageId + "> urlAdapter=<" + oldUrlAdapter + "> url=<" + oldUrl + ">"; log(formMapping, SQueriableLog.STATUS_OK, logBuilder, "update", truncate(rawMessage)); } catch (SBonitaException e) { log(formMapping, SQueriableLog.STATUS_FAIL, logBuilder, "update", "failed to update"); throw new SObjectModificationException(e); } } String getFormMappingTarget(String url, Long pageId) throws SObjectModificationException { if (url != null && pageId != null) { throw new SObjectModificationException("Can't update the form mapping with both url and pageId"); } String target = SFormMapping.TARGET_NONE; if (url != null) { target = SFormMapping.TARGET_URL; } else if (pageId != null) { target = SFormMapping.TARGET_INTERNAL; } return target; } String truncate(String rawMessage) { return rawMessage.substring(0, Math.min(255, rawMessage.length())); } private void initializeLogBuilder(final T logBuilder) { logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL); } private void updateLog(final ActionType actionType, final T logBuilder) { logBuilder.setActionType(actionType); } private FormMappingLogBuilder getLogBuilder(final ActionType actionType) { final FormMappingLogBuilder logBuilder = new FormMappingLogBuilder(); this.initializeLogBuilder(logBuilder); this.updateLog(actionType, logBuilder); return logBuilder; } private void log(final SFormMapping formMapping, final int sQueriableLogStatus, final FormMappingLogBuilder logBuilder, final String callerMethodName, String rawMessage) { logBuilder.actionScope(String.valueOf(formMapping.getProcessDefinitionId())); logBuilder.actionStatus(sQueriableLogStatus); logBuilder.objectId(formMapping.getId()); logBuilder.rawMessage(rawMessage); final SQueriableLog log = logBuilder.build(); if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) { queriableLoggerService.log(this.getClass().getName(), callerMethodName, log); } } protected SPageMapping createPageMappingForExistingFormMapping(SFormMapping formMapping, String url, Long pageId) throws SObjectCreationException { String key = formMappingKeyGenerator.generateKey(formMapping.getProcessDefinitionId(), formMapping.getTask(), formMapping.getType()); if (url != null) { return pageMappingService.create(key, url, externalUrlAdapter, buildAuthorizationRules(formMapping.getType())); } else { return pageMappingService.create(key, pageId, buildAuthorizationRules(formMapping.getType())); } } protected void checkThatInternalPageExists(Long pageId) throws SObjectModificationException { if (pageId != null) { try { pageService.getPage(pageId); } catch (SBonitaReadException | SObjectNotFoundException e) { throw new SObjectModificationException("the page with id " + pageId + " does not exists"); } } } protected String checkAndGetUrlAdapter(String url) throws SObjectModificationException { if (url == null) { return null; } checkUrlNotEmpty(url); return externalUrlAdapter; } protected void checkUrlNotEmpty(String url) throws SObjectModificationException { if (url.isEmpty()) { throw new SObjectModificationException("Can't have an empty url"); } } private long getSessionUserId() throws SSessionNotFoundException, SessionIdNotSetException { return sessionService.getLoggedUserFromSession(sessionAccessor); } @Override public void delete(SFormMapping formMapping) throws SObjectModificationException { FormMappingLogBuilder logBuilder = getLogBuilder(ActionType.DELETED); try { recorder.recordDelete(new DeleteRecord(formMapping), FORM_MAPPING); if (formMapping.getPageMapping() != null) { pageMappingService.delete(formMapping.getPageMapping()); } log(formMapping, SQueriableLog.STATUS_OK, logBuilder, "delete", "delete"); } catch (SRecorderException | SDeletionException e) { log(formMapping, SQueriableLog.STATUS_FAIL, logBuilder, "delete", "failed to delete"); throw new SObjectModificationException(e); } } @Override public SFormMapping get(long formMappingId) throws SBonitaReadException, SObjectNotFoundException { SFormMapping getFormMappingById = persistenceService.selectById(new SelectByIdDescriptor<>(SFormMapping.class, formMappingId)); if (getFormMappingById == null) { throw new SObjectNotFoundException(formMappingId); } return getFormMappingById; } @Override public SFormMapping get(String key) throws SBonitaReadException, SObjectNotFoundException { return persistenceService.selectOne(new SelectOneDescriptor("getFormMappingByKey", Collections. singletonMap("key", key), SFormMapping.class)); } @Override public SFormMapping get(long processDefinitionId, Integer type, String task) throws SBonitaReadException { Map parameters = new HashMap(3); parameters.put("processDefinitionId", processDefinitionId); parameters.put("type", type); parameters.put("task", task); return persistenceService.selectOne(new SelectOneDescriptor( "getFormMappingOfProcessDefinitionOnTask", parameters, SFormMapping.class)); } @Override public SFormMapping get(long processDefinitionId, Integer type) throws SBonitaReadException { Map parameters = new HashMap(3); parameters.put("processDefinitionId", processDefinitionId); parameters.put("type", type); return persistenceService.selectOne(new SelectOneDescriptor("getFormMappingOfProcessDefinition", parameters, SFormMapping.class)); } @Override public List list(long processDefinitionId, int fromIndex, int numberOfResults) throws SBonitaReadException { Map parameters = new HashMap(3); parameters.put("processDefinitionId", processDefinitionId); return persistenceService.selectList(new SelectListDescriptor( "getFormMappingsOfProcessDefinition", parameters, SFormMapping.class, new QueryOptions( fromIndex, numberOfResults))); } @Override public List list(int fromIndex, int numberOfResults) throws SBonitaReadException { Map parameters = new HashMap(3); return persistenceService.selectList(new SelectListDescriptor("getFormMappings", parameters, SFormMapping.class, new QueryOptions( fromIndex, numberOfResults))); } @Override public long getNumberOfFormMappings(QueryOptions queryOptions) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SFormMapping.class, queryOptions, Collections. emptyMap()); } @Override public List searchFormMappings(QueryOptions queryOptions) throws SBonitaReadException { return persistenceService.searchEntity(SFormMapping.class, queryOptions, Collections. emptyMap()); } private static class FormMappingLogBuilder extends CRUDELogBuilder implements SPersistenceLogBuilder { /* * resulting log example * 1 54 1433257801158 2015 6 153 23 william.jobs 1 7.0.0-SNAPSHOT INTERNAL FORM_MAPPING_UPDATED 1 1 update the * formmapping * org.bonitasoft.engine.core.form.impl.FormMappingServiceImpl update -1 -1 1 -1 -1 */ @Override protected String getActionTypePrefix() { return FORM_MAPPING; } @Override protected void checkExtraRules(SQueriableLog log) { } @Override public SPersistenceLogBuilder objectId(long objectId) { queriableLogBuilder.numericIndex(2, objectId); return this; } } } ================================================ FILE: bpm/bonita-core/bonita-form-mapping/src/main/java/org/bonitasoft/engine/core/form/impl/ManagerInvolvedAuthorizationRuleMappingImpl.java ================================================ /** * Copyright (C) 2017 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.core.form.impl; import static org.bonitasoft.engine.page.AuthorizationRuleConstants.*; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.core.form.AuthorizationRuleMapping; /** * Modified version of the default implementation, with the following change: * For case overview, also authorize access for a manager of a user involved in the case. * * @author Emmanuel Duchastenier * @author Danila Mazour */ public class ManagerInvolvedAuthorizationRuleMappingImpl implements AuthorizationRuleMapping { @Override public List getProcessStartRuleKeys() { return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_ACTOR_INITIATOR); } @Override public List getProcessOverviewRuleKeys() { return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_PROCESS_INITIATOR, IS_TASK_PERFORMER, IS_INVOLVED_IN_PROCESS_INSTANCE, IS_MANAGER_OF_USER_INVOLVED_IN_PROCESS_INSTANCE); } @Override public List getTaskRuleKeys() { return Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_TASK_AVAILABLE_FOR_USER); } } ================================================ FILE: bpm/bonita-core/bonita-form-mapping/src/main/resources/org/bonitasoft/engine/core/form/impl/form-mapping.queries.hbm.xml ================================================ SELECT formmap FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap SELECT formmap FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap WHERE formmap.processDefinitionId = :processDefinitionId SELECT formmap FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap WHERE formmap.processDefinitionId = :processDefinitionId AND formmap.type = :type AND formmap.task = :task SELECT formmap FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap WHERE formmap.pageMapping.key = :key SELECT formmap FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap WHERE formmap.processDefinitionId = :processDefinitionId AND formmap.type = :type SELECT COUNT(formmap.id) FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap SELECT formmap FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap SELECT count(formmap.id) FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap LEFT JOIN formmap.pageMapping AS pagemap SELECT formmap FROM org.bonitasoft.engine.core.form.SFormMapping AS formmap LEFT JOIN formmap.pageMapping AS pagemap ================================================ FILE: bpm/bonita-core/bonita-form-mapping/src/test/java/org/bonitasoft/engine/core/form/impl/FormMappingKeyGeneratorImplTest.java ================================================ /** * 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.core.form.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.core.form.SFormMapping; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; import org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class FormMappingKeyGeneratorImplTest { private static final long PROCESS_DEFINITION_ID = 123456l; private static final String PROCESS_NAME = "myProcess"; private static final String PROCESS_VERSION = "12.589"; @Mock private ProcessDefinitionService processDefinitionService; @InjectMocks private FormMappingKeyGeneratorImpl formMappingKeyGenerator; @Before public void before() throws Exception { SProcessDefinitionImpl toBeReturned = new SProcessDefinitionImpl(PROCESS_NAME, PROCESS_VERSION); doReturn(toBeReturned).when(processDefinitionService).getProcessDefinition(PROCESS_DEFINITION_ID); } @Test public void generateKey_on_process_overview() throws Exception { String generateKey = formMappingKeyGenerator.generateKey(PROCESS_DEFINITION_ID, null, SFormMapping.TYPE_PROCESS_OVERVIEW); assertThat(generateKey).isEqualTo("processInstance/myProcess/12.589"); } @Test(expected = SObjectCreationException.class) public void generateKey_for_un_unknown_type() throws Exception { formMappingKeyGenerator.generateKey(PROCESS_DEFINITION_ID, null, 12); } @Test public void generateKey_on_task() throws Exception { String generateKey = formMappingKeyGenerator.generateKey(PROCESS_DEFINITION_ID, "step1", SFormMapping.TYPE_TASK); assertThat(generateKey).isEqualTo("taskInstance/myProcess/12.589/step1"); } @Test public void generateKey_on_process_start() throws Exception { String generateKey = formMappingKeyGenerator.generateKey(PROCESS_DEFINITION_ID, "step1", SFormMapping.TYPE_PROCESS_START); assertThat(generateKey).isEqualTo("process/myProcess/12.589"); } @Test(expected = SObjectCreationException.class) public void generateKey_when_definition_not_found() throws Exception { formMappingKeyGenerator.generateKey(4444l, "step1", SFormMapping.TYPE_PROCESS_START); } @Test(expected = SObjectCreationException.class) public void generateKey_on_task_when_no_task_name() throws Exception { formMappingKeyGenerator.generateKey(PROCESS_DEFINITION_ID, null, SFormMapping.TYPE_TASK); } @Test(expected = SObjectCreationException.class) public void generateKeyShouldThrowObjectCreationExceptionWhenProcessDefNotFound() throws Exception { doThrow(SProcessDefinitionNotFoundException.class).when(processDefinitionService) .getProcessDefinition(PROCESS_DEFINITION_ID); formMappingKeyGenerator.generateKey(PROCESS_DEFINITION_ID, "someTaskName", SFormMapping.TYPE_TASK); } } ================================================ FILE: bpm/bonita-core/bonita-form-mapping/src/test/java/org/bonitasoft/engine/core/form/impl/FormMappingServiceImplTest.java ================================================ /** * 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.core.form.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.bonitasoft.engine.page.AuthorizationRuleConstants.*; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.Collections; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.core.form.FormMappingKeyGenerator; import org.bonitasoft.engine.core.form.SFormMapping; import org.bonitasoft.engine.events.model.SUpdateEvent; import org.bonitasoft.engine.form.FormMappingType; import org.bonitasoft.engine.page.PageMappingService; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.page.SPageMapping; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * author Emmanuel Duchastenier */ @RunWith(MockitoJUnitRunner.class) public class FormMappingServiceImplTest { private static final long PROCESS_DEFINITION_ID = 456123L; private static final long ID = 154L; private static final Long PAGE_ID = 554L; private static final String EXTERNAL = "external"; private static final String LEGACY = "legacy"; @Mock private Recorder recorder; @Mock private ReadPersistenceService persistenceService; @Mock private SessionService sessionService; @Mock private ReadSessionAccessor sessionAccessor; @Mock private FormMappingKeyGenerator formMappingKeyGenerator; @Mock private PageMappingService pageMappingService; @Mock private QueriableLoggerService queriableLoggerService; @Mock private PageService pageService; @Captor private ArgumentCaptor updateEventCaptor; @Captor private ArgumentCaptor updateRecordCaptor; @Spy private AuthorizationRuleMappingImpl authorizationRuleMapping; @InjectMocks private FormMappingServiceImpl formMappingService; @Before public void before() throws Exception { formMappingService = new FormMappingServiceImpl(recorder, persistenceService, sessionService, sessionAccessor, pageMappingService, pageService, formMappingKeyGenerator, EXTERNAL, LEGACY, queriableLoggerService, authorizationRuleMapping); doThrow(SObjectNotFoundException.class).when(pageService).getPage(anyLong()); doReturn(new SPage("myPage", 0, 0, false, "page.zip")).when(pageService).getPage(PAGE_ID); } @Test public void createFormMappingWithUndefinedTargetShouldNotAddPageMapping() throws Exception { doReturn("mockedKey").when(formMappingKeyGenerator).generateKey(PROCESS_DEFINITION_ID, "someHumanTask", 84); formMappingService.create(PROCESS_DEFINITION_ID, "someHumanTask", 84, SFormMapping.TARGET_UNDEFINED, null); verifyNoInteractions(pageMappingService); } @Test public void createFormMapping_with_none_should_create_empty_form_mapping() throws Exception { doReturn("mockedKey").when(formMappingKeyGenerator).generateKey(PROCESS_DEFINITION_ID, "someHumanTask", 84); formMappingService.create(PROCESS_DEFINITION_ID, "someHumanTask", 84, SFormMapping.TARGET_NONE, null); verify(pageMappingService).create(eq("mockedKey"), isNull(), isNull()); } @Test public void createWithInternalPageShouldCallPageMapping_create() throws Exception { doReturn("theKey").when(formMappingKeyGenerator).generateKey(PROCESS_DEFINITION_ID, "step1", 2); formMappingService.create(PROCESS_DEFINITION_ID, "step1", 2, SFormMapping.TARGET_INTERNAL, null); verify(pageMappingService).create(eq("theKey"), nullable(Long.class), anyList()); } @Test public void createForTaskShouldAddCorrectAuthorizations() throws Exception { doReturn("clef").when(formMappingKeyGenerator).generateKey(PROCESS_DEFINITION_ID, "task", FormMappingType.TASK.getId()); formMappingService.create(PROCESS_DEFINITION_ID, "task", FormMappingType.TASK.getId(), SFormMapping.TARGET_INTERNAL, null); verify(pageMappingService).create("clef", null, Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_TASK_AVAILABLE_FOR_USER)); } @Test public void createForProcessStartShouldAddCorrectAuthorizations() throws Exception { doReturn("keye").when(formMappingKeyGenerator).generateKey(PROCESS_DEFINITION_ID, null, FormMappingType.PROCESS_START.getId()); formMappingService.create(PROCESS_DEFINITION_ID, null, FormMappingType.PROCESS_START.getId(), SFormMapping.TARGET_URL, null); verify(pageMappingService).create("keye", null, EXTERNAL, Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_ACTOR_INITIATOR)); } @Test public void createLegacyFormShouldNotAddCorrectAuthorizations() throws Exception { doReturn("keye").when(formMappingKeyGenerator).generateKey(PROCESS_DEFINITION_ID, null, FormMappingType.PROCESS_START.getId()); formMappingService.create(PROCESS_DEFINITION_ID, null, FormMappingType.PROCESS_START.getId(), SFormMapping.TARGET_LEGACY, null); verify(pageMappingService).create("keye", null, LEGACY, null); } @Test public void createForProcessOverviewShouldAddCorrectAuthorizations() throws Exception { doReturn("clave").when(formMappingKeyGenerator).generateKey(PROCESS_DEFINITION_ID, null, FormMappingType.PROCESS_OVERVIEW.getId()); formMappingService.create(PROCESS_DEFINITION_ID, null, FormMappingType.PROCESS_OVERVIEW.getId(), SFormMapping.TARGET_URL, null); verify(pageMappingService).create("clave", null, EXTERNAL, Arrays.asList(IS_ADMIN, IS_PROCESS_OWNER, IS_PROCESS_INITIATOR, IS_TASK_PERFORMER, IS_INVOLVED_IN_PROCESS_INSTANCE)); } @Test(expected = IllegalArgumentException.class) public void testCreateWithNullTarget() throws Exception { formMappingService.create(1654L, "step1", 1, null, null); } @Test public void testGetNumberOfFormMappings() throws Exception { QueryOptions queryOptions = mock(QueryOptions.class); formMappingService.getNumberOfFormMappings(queryOptions); verify(persistenceService).getNumberOfEntities(SFormMapping.class, queryOptions, Collections. emptyMap()); } @Test public void testGetByKey() throws Exception { formMappingService.get("theKey"); verify(persistenceService).selectOne( new SelectOneDescriptor("getFormMappingByKey", Collections. singletonMap("key", "theKey"), SFormMapping.class)); } @Test public void testSearchFormMappings() throws Exception { QueryOptions queryOptions = mock(QueryOptions.class); formMappingService.searchFormMappings(queryOptions); verify(persistenceService).searchEntity(SFormMapping.class, queryOptions, Collections. emptyMap()); } @Test public void test_update_with_url() throws Exception { SFormMapping formMapping = createFormMapping(ID); formMappingService.update(formMapping, "http://fake.url", null); verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString()); assertThat(updateRecordCaptor.getValue().getFields()).contains(entry("target", SFormMapping.TARGET_URL), entry("pageMapping.url", "http://fake.url"), entry("pageMapping.pageId", null), entry("pageMapping.urlAdapter", EXTERNAL)); } @Test public void test_update_with_no_page_nor_url() throws Exception { SFormMapping formMapping = createFormMapping(ID); formMappingService.update(formMapping, null, null); verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString()); assertThat(updateRecordCaptor.getValue().getFields()).contains(entry("target", SFormMapping.TARGET_NONE), entry("pageMapping.url", null), entry("pageMapping.pageId", null), entry("pageMapping.urlAdapter", null)); } private SFormMapping createFormMapping(long id) { SFormMapping sFormMapping = new SFormMapping(); sFormMapping.setId(id); SPageMapping pageMapping = new SPageMapping(); sFormMapping.setPageMapping(pageMapping); return sFormMapping; } @Test public void test_update_with_page() throws Exception { SFormMapping formMapping = createFormMapping(ID); formMappingService.update(formMapping, null, PAGE_ID); verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString()); assertThat(updateRecordCaptor.getValue().getFields()).contains(entry("target", SFormMapping.TARGET_INTERNAL), entry("pageMapping.url", null), entry("pageMapping.pageId", PAGE_ID), entry("pageMapping.urlAdapter", null)); } @Test(expected = SObjectModificationException.class) public void test_update_with_page_that_does_not_exists() throws Exception { SFormMapping formMapping = createFormMapping(ID); formMappingService.update(formMapping, null, 557441l); } @Test(expected = SObjectModificationException.class) public void test_update_with_invalid_parameters1() throws Exception { SFormMapping formMapping = createFormMapping(ID); formMappingService.update(formMapping, "plop", PAGE_ID); } @Test(expected = SObjectModificationException.class) public void test_update_with_invalid_parameters2() throws Exception { SFormMapping formMapping = createFormMapping(ID); formMappingService.update(formMapping, "", PAGE_ID); } @Test(expected = SObjectModificationException.class) public void test_update_with_invalid_parameters3() throws Exception { SFormMapping formMapping = createFormMapping(ID); formMappingService.update(formMapping, "", null); } } ================================================ FILE: bpm/bonita-core/bonita-form-mapping/src/test/java/org/bonitasoft/engine/core/form/impl/ManagerInvolvedAuthorizationRuleMappingImplTest.java ================================================ /** * 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.core.form.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.page.AuthorizationRuleConstants.IS_MANAGER_OF_USER_INVOLVED_IN_PROCESS_INSTANCE; import java.util.List; import org.junit.Test; /** * @author Emmanuel Duchastenier */ public class ManagerInvolvedAuthorizationRuleMappingImplTest { @Test public void should_allow_manager_of_user_involved() throws Exception { // given: final ManagerInvolvedAuthorizationRuleMappingImpl ruleMapping = new ManagerInvolvedAuthorizationRuleMappingImpl(); // when: final List rules = ruleMapping.getProcessOverviewRuleKeys(); // then: assertThat(rules).contains(IS_MANAGER_OF_USER_INVOLVED_IN_PROCESS_INSTANCE); } } ================================================ FILE: bpm/bonita-core/bonita-home-server/build.gradle ================================================ dependencies { api project(':services:bonita-commons') api project(':bpm:bonita-common') api libs.commonsIO api project(':platform:platform-resources') testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.systemRules testRuntimeOnly libs.logback } ================================================ FILE: bpm/bonita-core/bonita-home-server/src/main/java/org/bonitasoft/engine/home/BonitaHomeServer.java ================================================ /** * 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.home; import static java.util.Collections.singletonList; import static java.util.stream.Collectors.toMap; import static org.bonitasoft.engine.home.FolderMgr.getFolder; import static org.bonitasoft.engine.home.FolderMgr.getPlatformTempFolder; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.net.URI; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; import javax.naming.NamingException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.platform.configuration.ConfigurationService; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.bonitasoft.platform.setup.PlatformSetupAccessor; import org.springframework.core.io.ClassPathResource; /** * Retrieve configuration files from database and from classpath */ public class BonitaHomeServer { public static final BonitaHomeServer INSTANCE = new BonitaHomeServer(); /** * property name of the server api implementation class name */ private static final String SERVER_API_IMPLEMENTATION = "serverApi"; private final ProfileStorage profileStorage; private ConfigurationService configurationService; private BonitaHomeServer() { profileStorage = new ProfileStorage(); } public static BonitaHomeServer getInstance() { return INSTANCE; } private ConfigurationService getConfigurationService() { if (configurationService == null) { //should be given by spring try { configurationService = PlatformSetupAccessor.getConfigurationService(); } catch (NamingException e) { throw new IllegalStateException(e); } } return configurationService; } /** * Properties inheritance is defined like that: *
    *
  1. platform properties in database overrides platform properties in classpath
  2. *
*/ public Properties getPlatformProperties() throws IOException { return mergeProperties(getPropertiesFromClassPath("bonita-platform-community.properties", "bonita-platform-private-community.properties", "bonita-platform-private-sp.properties", "bonita-platform-sp.properties", "bonita-platform-sp-cluster.properties"), getConfigurationService().getPlatformEngineConf()); } /** * Properties inheritance is defined like that: *
    *
  1. tenant properties in database overrides tenant properties in classpath
  2. *
  3. tenant properties in classpath overrides platform properties in database
  4. *
  5. platform properties in database overrides platform properties in classpath
  6. *
*/ public Properties getTenantProperties() throws IOException { Properties allProperties = getPlatformProperties(); Properties tenantProperties = mergeProperties(getPropertiesFromClassPath( "bonita-tenant-community.properties", "bonita-tenant-sp.properties", "bonita-tenant-sp-cluster.properties"), getConfigurationService().getTenantEngineConf()); allProperties.putAll(tenantProperties); return allProperties; } public Properties getPropertiesFromClassPath(String... files) throws IOException { Properties properties = new Properties(); for (String file : files) { Properties fileProperties = new Properties(); ClassPathResource classPathResource = new ClassPathResource(file); if (!classPathResource.exists()) { continue; } fileProperties.load(classPathResource.getInputStream()); for (String property : fileProperties.stringPropertyNames()) { properties.put(property, fileProperties.getProperty(property)); } } return properties; } public List getPlatformConfiguration() { return getAllXmlConfiguration(getConfigurationService().getPlatformEngineConf()); } public List getTenantConfiguration() { return getAllXmlConfiguration(getConfigurationService().getTenantEngineConf()); } private Properties mergeProperties(Properties mergeInto, List configurationFiles) throws IOException { for (BonitaConfiguration bonitaConfiguration : configurationFiles) { if (bonitaConfiguration.getResourceName().endsWith(".properties")) { Properties properties = new Properties(); properties.load(new ByteArrayInputStream(bonitaConfiguration.getResourceContent())); mergeInto.putAll(properties); } } return mergeInto; } private List getAllXmlConfiguration(List configurationFiles) { List configurations = new ArrayList<>(); for (BonitaConfiguration bonitaConfiguration : configurationFiles) { if (bonitaConfiguration.getResourceName().endsWith(".xml")) { configurations.add(bonitaConfiguration); } } return configurations; } /* * ================================================= * process/tenant management * ================================================= */ public ProfileStorage getProfileStorage() { return profileStorage; } /** * get the name of the implementation of {@link org.bonitasoft.engine.api.internal.ServerAPI} based on the current * configuration of * bonita-platform.properties * * @return the name of the class implementing {@link org.bonitasoft.engine.api.internal.ServerAPI} * @throws IllegalStateException if the name of the implementation cannot be retrieved */ public String getServerAPIImplementation() throws IllegalStateException { try { return getPlatformProperties().getProperty(SERVER_API_IMPLEMENTATION); } catch (IOException e) { throw new IllegalStateException(e); } } /* * ================================================= * temporary files * ================================================= */ public File getPlatformTempFile(final String fileName) throws IOException { final Folder tempFolder = getPlatformTempFolder(); final File file = tempFolder.getFile(fileName); file.delete(); file.createNewFile(); return file; } public URI getLocalTemporaryFolder(final String artifactType) throws IOException { return FolderMgr.getPlatformLocalClassLoaderFolder(artifactType).toURI(); } public URI getLocalTemporaryFolder(final String artifactType, final long artifactId) throws IOException { return FolderMgr.getPlatformLocalClassLoaderFolder(artifactType, artifactId).toURI(); } public File getSecurityScriptsFolder() throws BonitaHomeNotSetException, IOException { final Folder tenantSecurityScriptsFolder = getFolder(getPlatformTempFolder(), "security-scripts") .createIfNotExists(); List tenantSecurityScripts = getConfigurationService().getTenantSecurityScripts(); writeBonitaConfiguration(tenantSecurityScriptsFolder.getFile(), tenantSecurityScripts); return tenantSecurityScriptsFolder.getFile(); } private void writeBonitaConfiguration(File folder, List bonitaConfigurations) throws IOException { for (BonitaConfiguration bonitaConfiguration : bonitaConfigurations) { String[] pathArray = bonitaConfiguration.getResourceName().split("/"); Path path = folder.toPath(); for (String pathChunk : pathArray) { path = path.resolve(pathChunk); } path.toFile().getParentFile().mkdirs(); IOUtil.write(path.toFile(), bonitaConfiguration.getResourceContent()); } } public Map getClientPlatformConfigurations() { return getConfigurationService().getPlatformPortalConf().stream() .collect(toMap(BonitaConfiguration::getResourceName, BonitaConfiguration::getResourceContent)); } public Map getTenantPortalConfigurations() { return getConfigurationService().getTenantPortalConf().stream() .collect(toMap(BonitaConfiguration::getResourceName, BonitaConfiguration::getResourceContent)); } public byte[] getTenantPortalConfiguration(String file) { return getConfigurationService().getTenantPortalConfiguration(file).getResourceContent(); } public void updateTenantPortalConfigurationFile(String file, byte[] content) throws UpdateException { BonitaConfiguration configuration = getConfigurationService().getTenantPortalConfiguration(file); if (configuration != null) { configuration.setResourceContent(content); getConfigurationService().storeTenantPortalConf(singletonList(configuration)); } else { throw new UpdateException("Cannot update non-existing configuration file " + file); } } } ================================================ FILE: bpm/bonita-core/bonita-home-server/src/main/java/org/bonitasoft/engine/home/BonitaResource.java ================================================ /** * 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.home; import java.util.Objects; /** * @author Baptiste Mesta */ public class BonitaResource { private final String name; private final byte[] content; public static BonitaResource resource(String name, byte[] content) { return new BonitaResource(name, content); } public BonitaResource(String name, byte[] content) { this.name = name; this.content = content; } public String getName() { return name; } public byte[] getContent() { return content; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BonitaResource that = (BonitaResource) o; return Objects.equals(name, that.name) && Objects.equals(content, that.content); } @Override public int hashCode() { return Objects.hash(name, content); } } ================================================ FILE: bpm/bonita-core/bonita-home-server/src/main/java/org/bonitasoft/engine/home/ProfileStorage.java ================================================ /** * 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.home; import java.io.File; import java.io.IOException; /** * Handles storage and retrieval of profile temporary md5 file. * * @author Baptiste Mesta */ public class ProfileStorage { public File getProfileMD5() throws IOException { Folder tenantWorkFolder = FolderMgr.getPlatformTempFolder(); return tenantWorkFolder.getFile("profiles.md5"); } } ================================================ FILE: bpm/bonita-core/bonita-home-server/src/main/java/org/bonitasoft/engine/home/Util.java ================================================ /** * 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.home; import java.io.File; import org.bonitasoft.engine.commons.StringUtils; /** * @author Charles Souillard */ public class Util { public static String generateRelativeResourcePath(final File folder, final File file) { String path = file.getAbsolutePath().replace(folder.getAbsolutePath(), ""); path = StringUtils.uniformizePathPattern(path); // remove first slash, if any: if (path.startsWith("/")) { path = path.substring(1); } return path; } } ================================================ FILE: bpm/bonita-core/bonita-home-server/src/test/java/org/bonitasoft/engine/home/BonitaHomeServerTest.java ================================================ /** * 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.home; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Properties; import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; import org.bonitasoft.engine.commons.io.IOUtil; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.platform.configuration.ConfigurationService; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class BonitaHomeServerTest { @InjectMocks @Spy BonitaHomeServer bonitaHomeServer; @Mock ConfigurationService configurationService; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void should_updateTenantPortalConfigurationFile_update_the_files() throws Exception { //given doReturn(conf("myFile.properties", "previous content".getBytes())) .when(configurationService).getTenantPortalConfiguration("myFile.properties"); //when bonitaHomeServer.updateTenantPortalConfigurationFile("myFile.properties", "the updated content".getBytes()); //then verify(configurationService).storeTenantPortalConf( Collections.singletonList(conf("myFile.properties", "the updated content".getBytes()))); } @Test(expected = UpdateException.class) public void should_updateTenantPortalConfigurationFile_throws_UpdateException_if_not_found() throws Exception { //given doReturn(null).when(configurationService).getTenantPortalConfiguration("myFile.properties"); //when bonitaHomeServer.updateTenantPortalConfigurationFile("myFile.properties", "the updated content".getBytes()); } private BonitaConfiguration conf(String file1, byte[] bytes) { return new BonitaConfiguration(file1, bytes); } @Test public void should_context_have_properties_overridden_with_database_properties() throws Exception { //given Properties databaseProperties = new Properties(); databaseProperties.setProperty("overriddenProperty", "databaseValue"); databaseProperties.setProperty("databaseProperty", "aValueInDb"); ByteArrayOutputStream out = new ByteArrayOutputStream(); databaseProperties.store(out, ""); doReturn(List.of(new BonitaConfiguration("myProp.properties", out.toByteArray()))) .when(configurationService).getPlatformEngineConf(); Properties classPathProperties = new Properties(); classPathProperties.setProperty("overriddenProperty", "classPathValue"); classPathProperties.setProperty("classPathProperty", "aValueInClassPath"); doReturn(classPathProperties).when(bonitaHomeServer) .getPropertiesFromClassPath(any(String[].class)); //when Properties allProperties = bonitaHomeServer.getPlatformProperties(); //then assertThat(allProperties).containsOnly(entry("overriddenProperty", "databaseValue"), entry("databaseProperty", "aValueInDb"), entry("classPathProperty", "aValueInClassPath")); } @Test public void tenant_properties_should_inherit_from_platform_properties() throws Exception { //given Thread.currentThread().setContextClassLoader(getClassLoaderWithProperties( new BonitaConfiguration("bonita-platform-community.properties", getPropertiesAsByteArray("prop1=prop1PlatformCP", "prop2=prop2PlatformCP", "prop3=prop3PlatformCP", "prop4=prop4PlatformCP")), new BonitaConfiguration("bonita-tenant-community.properties", getPropertiesAsByteArray("prop3=prop3TenantCP", "prop4=prop4TenantCP")))); doReturn(Collections.singletonList(new BonitaConfiguration("platform.properties", getPropertiesAsByteArray("prop2=prop2PlatformDB", "prop3=prop3PlatformDB", "prop4=prop4PlatformDB")))) .when(configurationService).getPlatformEngineConf(); doReturn(Collections.singletonList(new BonitaConfiguration("tenant.properties", getPropertiesAsByteArray("prop4=prop4TenantDB")))) .when(configurationService).getTenantEngineConf(); //when Properties allProperties = bonitaHomeServer.getTenantProperties(); //then assertThat(allProperties).containsOnly( entry("prop1", "prop1PlatformCP"), entry("prop2", "prop2PlatformDB"), entry("prop3", "prop3TenantCP"), entry("prop4", "prop4TenantDB")); } private byte[] getPropertiesAsByteArray(String... propertiesV) { return String.join("\n", propertiesV).getBytes(StandardCharsets.UTF_8); } private ClassLoader getClassLoaderWithProperties(BonitaConfiguration... bonitaConfigurations) throws IOException { File jar1 = temporaryFolder.newFile("myJar1.jar"); FileUtils.writeByteArrayToFile(jar1, IOUtil.generateJar( Arrays.stream(bonitaConfigurations).collect(Collectors.toMap(BonitaConfiguration::getResourceName, BonitaConfiguration::getResourceContent)))); return new URLClassLoader(new URL[] { jar1.toURI().toURL() }, Thread.currentThread().getContextClassLoader()); } @Test public void should_getPropertiesFromClassPath_get_properties_of_all_files_from_classpath() throws Exception { //given File jar1 = temporaryFolder.newFile("myJar1.jar"); FileUtils.writeByteArrayToFile(jar1, IOUtil .generateJar(Collections.singletonMap("myPropertiesFile1.properties", "prop1=value1".getBytes()))); File jar2 = temporaryFolder.newFile("myJar2.jar"); FileUtils.writeByteArrayToFile(jar2, IOUtil .generateJar(Collections.singletonMap("myPropertiesFile2.properties", "prop2=value2".getBytes()))); File jar3 = temporaryFolder.newFile("myJar3.jar"); FileUtils.writeByteArrayToFile(jar3, IOUtil .generateJar(Collections.singletonMap("myPropertiesFile3.properties", "prop3=value3".getBytes()))); ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); URLClassLoader urlClassLoader = new URLClassLoader( new URL[] { jar1.toURI().toURL(), jar2.toURI().toURL(), jar3.toURI().toURL() }, contextClassLoader); try { Thread.currentThread().setContextClassLoader(urlClassLoader); //when Properties propertiesFromClassPath = bonitaHomeServer.getPropertiesFromClassPath( "myPropertiesFile1.properties", "myPropertiesFile2.properties", "anUnexistingProperty.properties"); //then assertThat(propertiesFromClassPath).containsOnly(entry("prop1", "value1"), entry("prop2", "value2")); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } @Test public void should_getTenantPortal_Configuration() throws Exception { //given final String configFile = "a portal config file"; BonitaConfiguration tenantTemplateConf = conf(configFile, "{}".getBytes()); doReturn(tenantTemplateConf).when(configurationService).getTenantPortalConfiguration(configFile); //when byte[] content = bonitaHomeServer.getTenantPortalConfiguration(configFile); //then verify(configurationService).getTenantPortalConfiguration(configFile); assertThat(content).isEqualTo("{}".getBytes()); } } ================================================ FILE: bpm/bonita-core/bonita-home-server/src/test/java/org/bonitasoft/engine/home/ProfileStorageTest.java ================================================ /** * 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.home; import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import java.nio.file.Files; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Laurent Leseigneur */ @RunWith(MockitoJUnitRunner.class) public class ProfileStorageTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();; @Spy ProfileStorage profileStorage = new ProfileStorage(); @Test public void should_return_profiles_md5_file() throws Exception { //given Files.write(profileStorage.getProfileMD5().toPath(), "md5".getBytes()); //when final File profileMD5 = profileStorage.getProfileMD5(); //then assertThat(profileMD5).as("should retrieve file").exists().hasBinaryContent("md5".getBytes()); } } ================================================ FILE: bpm/bonita-core/bonita-home-server/src/test/java/org/bonitasoft/engine/home/UtilTest.java ================================================ /** * 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.home; import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import org.junit.Test; /** * @author Baptiste Mesta */ public class UtilTest { @Test public void generateRelativeResourcePathShouldHandleBackslashOS() { // given: final String pathname = "C:\\hello\\hi\\folder"; final String resourceRelativePath = "resource/toto.lst"; // when: final String generatedRelativeResourcePath = Util.generateRelativeResourcePath(new File(pathname), new File(pathname + File.separator + resourceRelativePath)); // then: assertThat(generatedRelativeResourcePath).isEqualTo(resourceRelativePath); } @Test public void generateRelativeResourcePathShouldHandleNetWorkBackslash() { // given: final String pathname = "\\\\hello\\hi\\folder"; final String resourceRelativePath = "resource/toto.lst"; // when: final String generatedRelativeResourcePath = Util.generateRelativeResourcePath(new File(pathname), new File(pathname + File.separator + resourceRelativePath)); // then: assertThat(generatedRelativeResourcePath).isEqualTo(resourceRelativePath); } @Test public void generateRelativeResourcePathShouldNotContainFirstSlash() { // given: final String pathname = "/home/target/some_folder/"; final String resourceRelativePath = "resource/toto.lst"; // when: final String generatedRelativeResourcePath = Util.generateRelativeResourcePath(new File(pathname), new File(pathname + File.separator + resourceRelativePath)); // then: assertThat(generatedRelativeResourcePath).isEqualTo(resourceRelativePath); } @Test public void generateRelativeResourcePathShouldWorkWithRelativeInitialPath() { // given: final String pathname = "target/nuns"; final String resourceRelativePath = "resource/toto.lst"; // when: final String generatedRelativeResourcePath = Util.generateRelativeResourcePath(new File(pathname), new File(pathname + File.separator + resourceRelativePath)); // then: assertThat(generatedRelativeResourcePath).isEqualTo(resourceRelativePath); } } ================================================ FILE: bpm/bonita-core/bonita-login/build.gradle ================================================ dependencies { api project(':services:bonita-session') api project(':services:bonita-identity') api project(':services:bonita-commons') api project(':bpm:bonita-core:bonita-home-server') api project(':services:bonita-authentication') api project(':services:bonita-profile') api project(':services:bonita-authorization') testImplementation libs.mockitoCore testImplementation libs.assertj testImplementation libs.logback compileOnly libs.lombok annotationProcessor libs.lombok } ================================================ FILE: bpm/bonita-core/bonita-login/src/main/java/org/bonitasoft/engine/core/login/LoginService.java ================================================ /** * 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.core.login; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.identity.SUserNotFoundException; import org.bonitasoft.engine.session.SSessionNotFoundException; import org.bonitasoft.engine.session.model.SSession; /** * @author Matthieu Chaffotte */ public interface LoginService { /** * generic login approach to handle outer authentication service like CAS or OAuth or whatever... * * @param credentials * the parameters to use to login * @return the session created if login succeeds * @throws SLoginException * if login fails * @throws SUserNotFoundException * if the user does not exist in the database */ SSession login(Map credentials) throws SLoginException, SUserNotFoundException; boolean isValid(final long sessionId); void logout(final long sessionId) throws SLoginException, SSessionNotFoundException; } ================================================ FILE: bpm/bonita-core/bonita-login/src/main/java/org/bonitasoft/engine/core/login/SLoginException.java ================================================ /** * 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.core.login; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SLoginException extends SBonitaException { private static final long serialVersionUID = -8130716781791219493L; public SLoginException(final Throwable cause) { super(cause); } public SLoginException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-login/src/main/java/org/bonitasoft/engine/core/login/SecuredLoginServiceImpl.java ================================================ /** * 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.core.login; import static org.bonitasoft.engine.authentication.AuthenticationConstants.BASIC_USERNAME; import static org.bonitasoft.engine.identity.model.builder.impl.SUserUpdateBuilderImpl.updateBuilder; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.engine.authentication.AuthenticationConstants; import org.bonitasoft.engine.authentication.AuthenticationException; import org.bonitasoft.engine.authentication.GenericAuthenticationService; import org.bonitasoft.engine.authorization.PermissionsBuilder; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SUserNotFoundException; import org.bonitasoft.engine.identity.SUserUpdateException; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.profile.ProfileService; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.session.SSessionException; import org.bonitasoft.engine.session.SSessionNotFoundException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.session.model.SSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; /** * @author Matthieu Chaffotte * @author Anthony Birembaut */ @Service public class SecuredLoginServiceImpl implements LoginService { private static final Logger log = LoggerFactory.getLogger(SecuredLoginServiceImpl.class); private final GenericAuthenticationService authenticationService; private final SessionService sessionService; private final IdentityService identityService; private final TechnicalUser technicalUser; private final ProfileService profileService; private final PermissionsBuilder permissionsBuilder; public SecuredLoginServiceImpl( @Qualifier("entryPointAuthenticationService") final GenericAuthenticationService authenticationService, final SessionService sessionService, final IdentityService identityService, TechnicalUser technicalUser, ProfileService profileService, PermissionsBuilder permissionsBuilder) { this.authenticationService = authenticationService; this.sessionService = sessionService; this.identityService = identityService; this.technicalUser = technicalUser; this.profileService = profileService; this.permissionsBuilder = permissionsBuilder; } @Override public SSession login(final Map credentials) throws SLoginException, SUserNotFoundException { debugLog("Logging in"); checkNull(credentials); if (isTechnicalUser(credentials)) { debugLog("Authenticated as technical user"); return createSession(extractUserName(credentials), -1L, true); } String userName = verifyCredentials(credentials); checkIsNotBlank(userName); debugLog("Authenticated as regular user"); SUser user = getUser(userName); checkIsEnabled(user); SSession session = createSession(userName, user.getId(), false); updateLastConnectionDate(user); return session; } private void updateLastConnectionDate(SUser user) throws SLoginException { try { identityService.updateUser(user, updateBuilder().updateLastConnection(System.currentTimeMillis()).done()); } catch (SUserUpdateException e) { throw new SLoginException(e); } } private void checkNull(Map credentials) throws SLoginException { if (credentials == null) { throw new SLoginException("invalid credentials, map is null"); } } private SSession createSession(String userName, long id, boolean isTechnicalUser) throws SLoginException { try { List profilesOfUser = profileService.getProfilesOfUser(id); List profiles = profilesOfUser.stream().map(SProfile::getName).collect(Collectors.toList()); Set permissions = permissionsBuilder.getPermissions(isTechnicalUser, profiles, userName); return sessionService.createSession(id, userName, isTechnicalUser, profiles, permissions); } catch (SSessionException | SBonitaReadException e) { throw new SLoginException(e); } } private void checkIsEnabled(SUser user) throws SLoginException { if (!user.isEnabled()) { throw new SLoginException("Unable to login : the user is disable."); } } private void checkIsNotBlank(String userName) throws SLoginException { if (StringUtils.isBlank(userName)) { debugLog("Authentication failed"); // now we are sure authentication Failed throw new SLoginException("User name or password is not valid!"); } } private SUser getUser(String userName) throws SUserNotFoundException { try { return identityService.getUserByUserName(userName); } catch (SUserNotFoundException e) { debugLog("Unable to find user with username " + userName + " in database."); throw e; } } private String verifyCredentials(Map credentials) throws SLoginException { try { return authenticationService.checkUserCredentials(credentials); } catch (AuthenticationException e) { debugLog("Unable to authenticate user with username " + credentials.get(BASIC_USERNAME)); throw new SLoginException(e); } } private boolean isTechnicalUser(Map credentials) { return technicalUser.getUserName().equals(extractUserName(credentials)) && technicalUser.getPassword() .equals(String.valueOf(credentials.get(AuthenticationConstants.BASIC_PASSWORD))); } private String extractUserName(Map credentials) { if (credentials.containsKey(BASIC_USERNAME) && credentials.get(BASIC_USERNAME) != null) { return String.valueOf(credentials.get(BASIC_USERNAME)); } return null; } @Override public void logout(final long sessionId) throws SSessionNotFoundException { sessionService.deleteSession(sessionId); } @Override public boolean isValid(final long sessionId) { try { return sessionService.isValid(sessionId); } catch (final SSessionNotFoundException e) { return false; } } private void debugLog(String message) { log.debug(message); } } ================================================ FILE: bpm/bonita-core/bonita-login/src/main/java/org/bonitasoft/engine/core/login/TechnicalUser.java ================================================ /** * 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.core.login; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * @author Matthieu Chaffotte */ @lombok.Data @Component public class TechnicalUser { private String userName; private String password; public TechnicalUser( @Value("${bonita.runtime.admin.username}") final String userName, @Value("${bonita.runtime.admin.password}") final String password) { super(); this.userName = userName; this.password = password; } } ================================================ FILE: bpm/bonita-core/bonita-login/src/test/java/org/bonitasoft/engine/core/login/SecuredLoginServiceImplTest.java ================================================ /** * 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.core.login; import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.mockito.Mockito.anyList; import static org.mockito.Mockito.anyMap; import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; import org.bonitasoft.engine.authentication.AuthenticationConstants; import org.bonitasoft.engine.authentication.AuthenticationException; import org.bonitasoft.engine.authentication.GenericAuthenticationService; import org.bonitasoft.engine.authorization.PermissionsBuilder; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SUserNotFoundException; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.profile.ProfileService; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.session.model.SSession; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class SecuredLoginServiceImplTest { private static final String TECH_USER_NAME = "install"; private static final String TECH_USER_PASS = "install"; private static final Long USER_ID = (long) -1; private SecuredLoginServiceImpl securedLoginServiceImpl; @Mock private GenericAuthenticationService genericAuthenticationService; @Mock private SessionService sessionService; @Mock private IdentityService identityService; @Mock private ProfileService profileService; @Mock private PermissionsBuilder permissionsBuilder; @Before public void setUp() throws Exception { securedLoginServiceImpl = new SecuredLoginServiceImpl(genericAuthenticationService, sessionService, identityService, new TechnicalUser(TECH_USER_NAME, TECH_USER_PASS), profileService, permissionsBuilder); //return a session with given arguments when(sessionService.createSession(anyLong(), anyString(), anyBoolean(), anyList(), anySet())) .thenAnswer(invok -> SSession.builder() .id(UUID.randomUUID().getLeastSignificantBits()) .applicationName("myApp") .userId(invok.getArgument(0)) .userName(invok.getArgument(1)) .technicalUser(invok.getArgument(2)) .profiles(invok.getArgument(3)) .build()); } @Test public void testSecuredLoginServiceWithNullCredentials() throws SUserNotFoundException { try { securedLoginServiceImpl.login(null); fail(); } catch (final SLoginException e) { assertThat(e.getMessage()).isEqualToIgnoringCase("invalid credentials, map is null"); } } @Test public void testSecuredLoginServiceWithNullLogin() throws SUserNotFoundException { try { final Map credentials = new HashMap<>(); securedLoginServiceImpl.login(credentials); fail(); } catch (final SLoginException e) { assertThat(e.getMessage()).isEqualToIgnoringCase("User name or password is not valid!"); } } @Test public void testSecuredLoginServiceWithWrongCredentials() { try { final Map credentials = new HashMap<>(); final String login = "login"; final String password = "password"; credentials.put(AuthenticationConstants.BASIC_USERNAME, login); credentials.put(AuthenticationConstants.BASIC_PASSWORD, password); securedLoginServiceImpl.login(credentials); fail(); } catch (final Exception e) { assertThat(e.getMessage()).isEqualToIgnoringCase("User name or password is not valid!"); } } @Test(expected = SUserNotFoundException.class) public void testSecuredLoginServiceWithUnknownUserThrowSUserNotFoundException() throws Exception { final Map credentials = new HashMap<>(); final String login = "login"; final String password = "password"; credentials.put(AuthenticationConstants.BASIC_USERNAME, login); credentials.put(AuthenticationConstants.BASIC_PASSWORD, password); when(genericAuthenticationService.checkUserCredentials(credentials)).thenReturn(login); when(identityService.getUserByUserName(login)).thenThrow(new SUserNotFoundException(login)); securedLoginServiceImpl.login(credentials); } @Test public void testSecuredLoginServiceWithInvalidPlatformCredentials() { final Map credentials = new HashMap<>(); final String password = "poutpout"; credentials.put(AuthenticationConstants.BASIC_USERNAME, TECH_USER_NAME); credentials.put(AuthenticationConstants.BASIC_PASSWORD, password); try { securedLoginServiceImpl.login(credentials); fail(); } catch (final Exception e) { assertThat(e.getMessage()).isEqualToIgnoringCase("User name or password is not valid!"); } } @Test public void testSecuredLoginServiceWithInvalidPlatformCredentialsWithGenericAuthenticationService() throws Exception { final Map credentials = new HashMap<>(); final long userId = -1L; final String login = "julien"; final String password = "julien"; credentials.put(AuthenticationConstants.BASIC_USERNAME, login); credentials.put(AuthenticationConstants.BASIC_PASSWORD, password); when(genericAuthenticationService.checkUserCredentials(anyMap())).thenThrow(new AuthenticationException()); try { securedLoginServiceImpl.login(credentials); } catch (final SLoginException e) { verify(genericAuthenticationService, times(1)).checkUserCredentials(anyMap()); verify(sessionService, times(0)).createSession(userId, login, true); assertThat(e).hasRootCauseExactlyInstanceOf(AuthenticationException.class); return; } fail(); } @Test public void testSecuredLoginServiceWithPlatformCredentialsWithGenericAuthenticationService() throws Exception { final Map credentials = credentials(TECH_USER_NAME, TECH_USER_PASS); final SSession sSession = mock(SSession.class); when(sessionService.createSession(-1L, TECH_USER_NAME, true, emptyList(), emptySet())) .thenReturn(sSession); final SSession sSessionResult = securedLoginServiceImpl.login(credentials); verify(genericAuthenticationService, times(0)).checkUserCredentials(anyMap()); verify(sessionService, times(1)).createSession(-1L, TECH_USER_NAME, true, emptyList(), emptySet()); assertThat(sSessionResult).isSameAs(sSession); } @Test public void testSecuredLoginServiceWithPlatformCredentials() throws Exception { final Map credentials = credentials(TECH_USER_NAME, TECH_USER_PASS); final SSession sSession = mock(SSession.class); when(sessionService.createSession(USER_ID, TECH_USER_NAME, true, emptyList(), emptySet())) .thenReturn(sSession); final SSession sSessionResult = securedLoginServiceImpl.login(credentials); verify(genericAuthenticationService, never()).checkUserCredentials(credentials); verify(sessionService).createSession(USER_ID, TECH_USER_NAME, true, emptyList(), emptySet()); assertThat(sSessionResult).isSameAs(sSession); } @Test public void testSecuredLoginServiceWithStandardUserCredentials() throws Exception { final Map credentials = credentials("julien", "julien"); final SSession sSession = mock(SSession.class); final SUser sUser = mock(SUser.class); doReturn(true).when(sUser).isEnabled(); when(sUser.getId()).thenReturn(112345L); when(genericAuthenticationService.checkUserCredentials(credentials)).thenReturn("julien"); when(sessionService.createSession(112345L, "julien", false, emptyList(), emptySet())) .thenReturn(sSession); when(identityService.getUserByUserName("julien")).thenReturn(sUser); final SSession sSessionResult = securedLoginServiceImpl.login(credentials); verify(genericAuthenticationService, times(1)).checkUserCredentials(credentials); verify(sessionService, times(1)).createSession(112345L, "julien", false, emptyList(), emptySet()); assertThat(sSessionResult).isSameAs(sSession); } @Test public void should_fail_if_no_password_is_provided() { final Map credentials = new HashMap<>(); final String password = null; credentials.put(AuthenticationConstants.BASIC_PASSWORD, password); try { securedLoginServiceImpl.login(credentials); fail(); } catch (final Exception e) { assertThat(e.getMessage()).isEqualToIgnoringCase("User name or password is not valid!"); } } @Test public void should_fail_if_credentials_are_empty() { final Map credentials = new HashMap<>(); try { securedLoginServiceImpl.login(credentials); fail(); } catch (final Exception e) { assertThat(e.getMessage()).isEqualToIgnoringCase("User name or password is not valid!"); } } @Test public void should_fail_if_username_is_blank() throws Exception { havingUser(" ", "password"); try { securedLoginServiceImpl.login(credentials(" ", "password")); fail(); } catch (final Exception e) { assertThat(e.getMessage()).isEqualToIgnoringCase("User name or password is not valid!"); } } @Test public void should_fail_if_password_does_not_match() throws Exception { havingUser("a", "password1"); try { securedLoginServiceImpl.login(credentials("a", "password2")); fail(); } catch (final Exception e) { assertThat(e.getMessage()).isEqualToIgnoringCase("User name or password is not valid!"); } } @Test public void should_fail_when_given_null_credentials() { try { securedLoginServiceImpl.login(null); fail(); } catch (final SLoginException | SUserNotFoundException e) { assertThat(e.getMessage()).isEqualToIgnoringCase("invalid credentials, map is null"); } } @Test public void should_login_with_technical_user() throws Exception { SSession session = securedLoginServiceImpl.login(credentials(TECH_USER_NAME, TECH_USER_PASS)); assertThat(session) .hasFieldOrPropertyWithValue("userName", TECH_USER_NAME) .hasFieldOrPropertyWithValue("userId", -1L) .hasFieldOrPropertyWithValue("technicalUser", true); } @Test public void should_login_with_existing_user() throws Exception { SUser user = havingUser("john", "bpm"); SSession session = securedLoginServiceImpl.login(credentials("john", "bpm")); assertThat(session) .hasFieldOrPropertyWithValue("userName", "john") .hasFieldOrPropertyWithValue("userId", user.getId()) .hasFieldOrPropertyWithValue("technicalUser", false); } @Test public void should_fail_if_user_is_disabled() throws Exception { SUser user = havingUser("john", "bpm"); user.setEnabled(false); try { securedLoginServiceImpl.login(credentials("john", "bpm")); fail(); } catch (SLoginException e) { assertThat(e.getMessage()).isEqualToIgnoringCase("Unable to login : the user is disable."); } } @Test public void should_update_last_connection_date_when_successfully_connected() throws Exception { SUser user = havingUser("john", "bpm"); securedLoginServiceImpl.login(credentials("john", "bpm")); verify(identityService).updateUser(eq(user), argThat(e -> e.getFields().keySet().equals(Collections.singleton("lastConnection")))); } @Test public void should_have_profiles_in_session() throws Exception { SUser user = havingUser("myUser", "myPass"); doReturn(profiles("User", "Administrator")).when(profileService).getProfilesOfUser(user.getId()); SSession session = securedLoginServiceImpl.login(credentials("myUser", "myPass")); assertThat(session.getProfiles()).containsExactlyInAnyOrder("User", "Administrator"); } private List profiles(String... profiles) { return Arrays.stream(profiles) .map(this::profile) .collect(Collectors.toList()); } private SProfile profile(String name) { SProfile sProfile = new SProfile(); sProfile.setName(name); return sProfile; } private Map credentials(String username, String password) { final Map credentials = new HashMap<>(); credentials.put(AuthenticationConstants.BASIC_USERNAME, username); credentials.put(AuthenticationConstants.BASIC_PASSWORD, password); return credentials; } private SUser havingUser(String username, String password) throws Exception { SUser user = new SUser(); user.setId(UUID.randomUUID().getLeastSignificantBits()); user.setUserName(username); user.setPassword(password); user.setEnabled(true); doReturn(user).when(identityService).getUserByUserName(username); doReturn(username).when(genericAuthenticationService) .checkUserCredentials(eq(credentials(username, password))); return user; } } ================================================ FILE: bpm/bonita-core/bonita-parameter/build.gradle ================================================ dependencies { api project(':services:bonita-session') api project(':bpm:bonita-core:bonita-home-server') api project(':services:bonita-commons') api project(':services:bonita-cache') api project(':services:bonita-builder') api project(':services:bonita-persistence') testImplementation libs.mockitoCore testImplementation libs.assertj testImplementation libs.systemRules testRuntimeOnly libs.logback annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: bpm/bonita-core/bonita-parameter/src/main/java/org/bonitasoft/engine/parameter/OrderBy.java ================================================ /** * 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.parameter; /** * @author Matthieu Chaffotte */ public enum OrderBy { NAME_ASC, NAME_DESC, VALUE_ASC, VALUE_DESC; } ================================================ FILE: bpm/bonita-core/bonita-parameter/src/main/java/org/bonitasoft/engine/parameter/ParameterService.java ================================================ /** * 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.parameter; import java.util.List; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Matthieu Chaffotte * @since 6.0 */ public interface ParameterService { /** * Update specific parameter value in a process * * @param processDefinitionId * identifier of processDefinition * @param parameterName * name of the parameter will be updated * @param parameterValue * new value of the parameter * @throws SParameterNameNotFoundException * error thrown if no parameter found for the specific parameterName */ void update(final long processDefinitionId, final String parameterName, final String parameterValue) throws SParameterNameNotFoundException, SBonitaReadException, SObjectModificationException; /** * Merge given parameters with existing ones. * Unknown parameters are ignored. * * @param processDefinitionId * identifier of processDefinition * @param parameters * parameters to merge * @throws SBonitaReadException * error thrown if an error occurred while retrieving the process definition * @throws SObjectModificationException * error thrown if an error occurred while updating the parameter value */ void merge(long processDefinitionId, Map parameters) throws SBonitaReadException, SObjectModificationException; /** * Store all parameters provided to the specific process in file system * * @param processDefinitionId * identifier of processDefinition * @param parameters * parameters will be stored in file system * @throws SParameterProcessNotFoundException * error thrown if no parameters configuration file found in file system */ void addAll(final long processDefinitionId, final Map parameters) throws SParameterProcessNotFoundException, SObjectCreationException, SBonitaReadException, SObjectModificationException; /** * return all parameters in a map * * @param processDefinitionId */ Map getAll(long processDefinitionId) throws SParameterProcessNotFoundException, SBonitaReadException; /** * Delete all parameters for a specific processDefinition * * @param processDefinitionId ID of processDefinition * @throws SParameterProcessNotFoundException * error thrown if no parameters configuration file found in file system */ void deleteAll(final long processDefinitionId) throws SParameterProcessNotFoundException, SBonitaReadException, SObjectModificationException; /** * Get parameters in a specific interval for specific process, this is used for pagination * * @param processDefinitionId * identifier of processDefinition * @param fromIndex * index of the record to be retrieved from. First record has index 0 * @param numberOfResult * number of result we want to get. Maximum number of result returned. * @param order * OrderBy object, contains information to do order * @return a list of SParameter objects * @throws SOutOfBoundException * error throw if fromIndex >= total size of parameters */ List get(final long processDefinitionId, final int fromIndex, final int numberOfResult, final OrderBy order) throws SOutOfBoundException, SBonitaReadException; /** * Get parameter by name in specific process * * @param processDefinitionId * identifier of processDefinition * @param parameterName * name of parameter * @return the parameter or null if it does not exists */ SParameter get(final long processDefinitionId, final String parameterName) throws SBonitaReadException; /** * Get a list of parameters will null values in order in specific process * * @param processDefinitionId * identifier of processDefinition * @param fromIndex * index of the record to be retrieved from. First record has index 0 * @param numberOfResult * number of result we want to get. Maximum number of result returned. * @param order * OrderBy object, contains information to do order * @return a list of parameters * @throws SParameterProcessNotFoundException * error thrown if no parameters configuration file found in file system * @throws SOutOfBoundException * error throw if fromIndex >= total size of parameters */ List getNullValues(final long processDefinitionId, final int fromIndex, final int numberOfResult, final OrderBy order) throws SParameterProcessNotFoundException, SOutOfBoundException, SBonitaReadException; /** * Check if the specific process contains null-valued parameter or not. * * @param processDefinitionId The ID of the process definition * @return true if at least one parameter contains a null value, false otherwise. * @throws SParameterProcessNotFoundException */ boolean containsNullValues(final long processDefinitionId) throws SBonitaReadException; } ================================================ FILE: bpm/bonita-core/bonita-parameter/src/main/java/org/bonitasoft/engine/parameter/ParameterServiceImpl.java ================================================ /** * 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.parameter; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.bpm.bar.ParameterContribution; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; /** * @author Baptiste Mesta */ @Slf4j public class ParameterServiceImpl implements ParameterService { private static final String VALUE_KEY = "value"; private static final String PROCESS_DEFINITION_ID_KEY = "processDefinitionId"; public static final int PAGE_SIZE = 100; public static final String PARAMETER = "PARAMETER"; private final Recorder recorder; private final ReadPersistenceService persistenceService; public ParameterServiceImpl(Recorder recorder, ReadPersistenceService persistenceService) { this.recorder = recorder; this.persistenceService = persistenceService; } @Override public void update(long processDefinitionId, String parameterName, String parameterValue) throws SParameterNameNotFoundException, SBonitaReadException, SObjectModificationException { final SParameter sParameter = get(processDefinitionId, parameterName); if (sParameter == null) { throw new SParameterNameNotFoundException( String.format("No parameter <%s> found in the process <%s>", parameterName, processDefinitionId)); } update(sParameter, parameterValue); } void update(SParameter sParameter, String parameterValue) throws SObjectModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(VALUE_KEY, interpretParameterValue(parameterValue)); try { recorder.recordUpdate(UpdateRecord.buildSetFields(sParameter, descriptor), PARAMETER); } catch (SRecorderException e) { throw new SObjectModificationException(e); } } public void merge(long processDefinitionId, Map parameters) throws SBonitaReadException, SObjectModificationException { for (Entry parameter : parameters.entrySet()) { final SParameter sParameter = get(processDefinitionId, parameter.getKey()); if (sParameter != null) { update(sParameter, parameters.get(parameter.getKey())); } else { log.debug("Parameter <{}> doesn't exist in process definition <{}> and has not been merged.", parameter.getKey(), processDefinitionId); } } } /** * Handle null values. If input is {@link org.bonitasoft.engine.bpm.bar.ParameterContribution#NULL}, convert it to * null */ protected String interpretParameterValue(String s) { return ParameterContribution.NULL.equals(s) ? null : s; } @Override public void addAll(long processDefinitionId, Map parameters) throws SObjectCreationException, SBonitaReadException, SObjectModificationException { for (Map.Entry entry : parameters.entrySet()) { addOrUpdate(processDefinitionId, entry.getKey(), entry.getValue()); } } void addOrUpdate(long processDefinitionId, String name, String value) throws SObjectCreationException, SBonitaReadException, SObjectModificationException { final SParameter currentParameter = get(processDefinitionId, name); if (currentParameter != null) { update(currentParameter, value); } else { add(processDefinitionId, name, value); } } void add(long processDefinitionId, String name, String value) throws SObjectCreationException { final SParameter sParameter = new SParameter(name, interpretParameterValue(value), processDefinitionId); try { recorder.recordInsert(new InsertRecord(sParameter), PARAMETER); } catch (SRecorderException e) { throw new SObjectCreationException(e); } } @Override public Map getAll(long processDefinitionId) throws SParameterProcessNotFoundException, SBonitaReadException { Map parameters = new HashMap<>(); int fromIndex = 0; List sParameters; do { sParameters = get(processDefinitionId, fromIndex, PAGE_SIZE, null); for (SParameter sParameter : sParameters) { parameters.put(sParameter.getName(), sParameter.getValue()); } fromIndex += PAGE_SIZE; } while (sParameters.size() == PAGE_SIZE); return parameters; } @Override public void deleteAll(long processDefinitionId) throws SParameterProcessNotFoundException, SBonitaReadException, SObjectModificationException { List toDelete; do { toDelete = get(processDefinitionId, 0, PAGE_SIZE, null); for (SParameter sParameter : toDelete) { try { recorder.recordDelete(new DeleteRecord(sParameter), PARAMETER); } catch (SRecorderException e) { throw new SObjectModificationException(e); } } } while (toDelete.size() == PAGE_SIZE); } @Override public List get(long processDefinitionId, int fromIndex, int numberOfResult, OrderBy order) throws SBonitaReadException { return persistenceService.selectList( new SelectListDescriptor<>("getParameters", Collections.singletonMap(PROCESS_DEFINITION_ID_KEY, processDefinitionId), SParameter.class, new QueryOptions(fromIndex, numberOfResult, getOrderByOptions(order)))); } @Override public SParameter get(long processDefinitionId, String parameterName) throws SBonitaReadException { final Map parameters = new HashMap<>(); parameters.put(PROCESS_DEFINITION_ID_KEY, processDefinitionId); parameters.put("name", parameterName); return persistenceService .selectOne(new SelectOneDescriptor<>("getParameterByName", parameters, SParameter.class)); } @Override public List getNullValues(long processDefinitionId, int fromIndex, int numberOfResult, OrderBy order) throws SParameterProcessNotFoundException, SBonitaReadException { return persistenceService.selectList(new SelectListDescriptor<>("getParametersWithNullValues", Collections.singletonMap( PROCESS_DEFINITION_ID_KEY, processDefinitionId), SParameter.class, new QueryOptions(fromIndex, numberOfResult, getOrderByOptions(order)))); } private List getOrderByOptions(OrderBy order) { OrderByType type = OrderByType.ASC; String fieldName = "name"; if (order != null) { switch (order) { case VALUE_ASC: fieldName = VALUE_KEY; break; case VALUE_DESC: fieldName = VALUE_KEY; type = OrderByType.DESC; break; case NAME_DESC: type = OrderByType.DESC; break; default: } } return Collections.singletonList(new OrderByOption(SParameter.class, fieldName, type)); } @Override public boolean containsNullValues(long processDefinitionId) throws SBonitaReadException { return !persistenceService.selectList(new SelectListDescriptor("getParametersWithNullValues", Collections.singletonMap(PROCESS_DEFINITION_ID_KEY, processDefinitionId), SParameter.class, new QueryOptions(0, 1))).isEmpty(); } } ================================================ FILE: bpm/bonita-core/bonita-parameter/src/main/java/org/bonitasoft/engine/parameter/SOutOfBoundException.java ================================================ /** * 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.parameter; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SOutOfBoundException extends SBonitaException { private static final long serialVersionUID = -2729330464039642649L; public SOutOfBoundException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-parameter/src/main/java/org/bonitasoft/engine/parameter/SParameter.java ================================================ /** * 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.parameter; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; import org.hibernate.annotations.Type; /** * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @Entity @Table(name = "proc_parameter") public class SParameter implements PersistentObject { @Id private long id; private String name; @Column @Type(type = "materialized_clob") private String value; @Column(name = "process_id") private long processDefinitionId; public SParameter(String name, String value, long processDefinitionId) { this.name = name; this.value = value; this.processDefinitionId = processDefinitionId; } } ================================================ FILE: bpm/bonita-core/bonita-parameter/src/main/java/org/bonitasoft/engine/parameter/SParameterNameNotFoundException.java ================================================ /** * 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.parameter; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SParameterNameNotFoundException extends SBonitaException { private static final long serialVersionUID = 6019783138024113896L; public SParameterNameNotFoundException(final Throwable cause) { super(cause); } public SParameterNameNotFoundException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-parameter/src/main/java/org/bonitasoft/engine/parameter/SParameterProcessNotFoundException.java ================================================ /** * 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.parameter; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SParameterProcessNotFoundException extends SBonitaException { private static final long serialVersionUID = 946351177446570851L; public SParameterProcessNotFoundException(final String message) { super(message); } public SParameterProcessNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-parameter/src/main/resources/org/bonitasoft/engine/parameter/parameter.queries.hbm.xml ================================================ SELECT param FROM org.bonitasoft.engine.parameter.SParameter AS param WHERE param.processDefinitionId = :processDefinitionId AND param.value is null SELECT param FROM org.bonitasoft.engine.parameter.SParameter AS param WHERE param.processDefinitionId = :processDefinitionId AND param.name = :name SELECT param FROM org.bonitasoft.engine.parameter.SParameter AS param WHERE param.processDefinitionId = :processDefinitionId ================================================ FILE: bpm/bonita-core/bonita-parameter/src/test/java/org/bonitasoft/engine/parameter/ParameterServiceImplTest.java ================================================ /** * 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.parameter; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.*; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * author Emmanuel Duchastenier */ @RunWith(MockitoJUnitRunner.class) public class ParameterServiceImplTest { @Rule public SystemOutRule systemOutRule = new SystemOutRule().enableLog(); @Mock private Recorder recorder; @Mock ReadPersistenceService persistenceService; @Captor private ArgumentCaptor> getSelectDescriptor; @InjectMocks @Spy private ParameterServiceImpl parameterService; @Test public void update_should_call_internal_update_with_retrieved_existing_value() throws Exception { final String aParam = "aParam"; final long processDefinitionId = 1544878L; final SParameter sParameter = new SParameter(aParam, "value", processDefinitionId); doReturn(sParameter).when(parameterService).get(processDefinitionId, aParam); final String newValue = "newValue"; parameterService.update(processDefinitionId, aParam, newValue); verify(parameterService).update(sParameter, newValue); } @Test public void update_should_convert_parameter_value() throws Exception { final String newValue = "newValue"; parameterService.update(new SParameter("parameter", "defaultValue", 9874654654L), newValue); verify(parameterService).interpretParameterValue(newValue); } @Test public void add_should_convert_parameter_value() throws Exception { parameterService.add(9874654654L, "parameter", "defaultValue"); verify(parameterService).interpretParameterValue("defaultValue"); } @Test public void interpretParameterValue_should_convert_NULL_VALUES() { assertThat(parameterService.interpretParameterValue("")).isEqualTo(""); assertThat(parameterService.interpretParameterValue(null)).isEqualTo(null); assertThat(parameterService.interpretParameterValue("someValue")).isEqualTo("someValue"); assertThat(parameterService.interpretParameterValue("-==NULLL==-")).isEqualTo(null); } @Test public void update_should_call_recordUpdate_on_recorder() throws Exception { parameterService.update(mock(SParameter.class), "value"); verify(recorder).recordUpdate(any(UpdateRecord.class), nullable(String.class)); } @Test(expected = SParameterNameNotFoundException.class) public void updateNonExistingParameter() throws Exception { parameterService.update(123L, "aParam", "newValue"); } @Test public void addAll_should_call_record_for_all_parameters() throws Exception { HashMap parameters = new HashMap<>(3); parameters.put("param1", "value1"); parameters.put("param2", "value2"); parameters.put("param3", "value3"); parameterService.addAll(123L, parameters); verify(recorder, times(3)).recordInsert(any(InsertRecord.class), nullable(String.class)); } @Test public void deleteAll_should_call_recordDelete() throws Exception { final long processDefinitionId = 123L; doReturn(Collections.singletonList(new SParameter())).when(parameterService).get(eq(processDefinitionId), anyInt(), anyInt(), nullable(OrderBy.class)); parameterService.deleteAll(processDefinitionId); verify(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); } @Test public void containsNullValues_should_return_true_for_non_empty_persistenceService_result() throws Exception { final ArrayList toBeReturned = new ArrayList<>(1); toBeReturned.add(new SParameter()); doReturn(toBeReturned).when(persistenceService).selectList(any()); assertThat(parameterService.containsNullValues(123L)).isTrue(); } @Test public void containsNullValues_should_return_false_for_empty_persistenceService_result() throws Exception { doReturn(Collections.emptyList()).when(persistenceService).selectList(any()); assertThat(parameterService.containsNullValues(123L)).isFalse(); } @Test public void getParameter_should_call_persistenceService() throws Exception { parameterService.get(123L, "aParam"); verify(persistenceService).selectOne(any(SelectOneDescriptor.class)); } @Test public void getNonExistingParameter_should_return_null() throws Exception { assertThat(parameterService.get(123L, "aParam")).isNull(); } @Test public void getParameters_should_call_persistenceService_respecting_given_order() throws Exception { parameterService.get(123L, 0, 10, OrderBy.NAME_DESC); verify(persistenceService).selectList(getSelectDescriptor.capture()); assertThat(getSelectDescriptor.getValue().getQueryOptions().getOrderByOptions().get(0)) .isEqualTo(new OrderByOption(SParameter.class, "name", OrderByType.DESC)); } @Test public void getParameters_should_use_pagination() throws Exception { parameterService.get(16546235L, 2, 7, OrderBy.NAME_ASC); verify(persistenceService).selectList(getSelectDescriptor.capture()); assertThat(getSelectDescriptor.getValue().getQueryOptions().getFromIndex()).isEqualTo(2); assertThat(getSelectDescriptor.getValue().getQueryOptions().getNumberOfResults()).isEqualTo(7); } @Test public void getNullValues_should_call_persistenceService_query_getParametersWithNullValues() throws Exception { final long processDefinitionId = 1561654L; parameterService.getNullValues(processDefinitionId, 0, 12, OrderBy.NAME_ASC); verify(persistenceService).selectList(getSelectDescriptor.capture()); assertThat(getSelectDescriptor.getValue().getQueryName()).isEqualTo("getParametersWithNullValues"); } @Test public void addOrUpdate_should_update_if_parameter_already_exists() throws Exception { final String paramName = "paramName"; final long processDefinitionId = 5457878L; final String paramValue = "paramValue"; final SParameter sParameter = mock(SParameter.class); doReturn(sParameter).when(parameterService).get(processDefinitionId, paramName); parameterService.addOrUpdate(processDefinitionId, paramName, paramValue); verify(parameterService).update(sParameter, paramValue); } @Test public void getAll_should_get_paginated_method() throws Exception { final long processDefinitionId = 4656556L; parameterService.getAll(processDefinitionId); verify(parameterService).get(eq(processDefinitionId), eq(0), anyInt(), nullable(OrderBy.class)); } @Test public void should_merge_parameters_with_existing_parameters() throws Exception { final long processDefinitionId = 4656556L; SParameter hostParameter = sParameter("host", "localhost", processDefinitionId); doReturn(hostParameter).when(parameterService).get(processDefinitionId, "host"); Map parameters = new HashMap<>(); parameters.put("host", "192.168.0.1"); parameters.put("password", "kittycat"); systemOutRule.clearLog(); parameterService.merge(processDefinitionId, parameters); verify(parameterService).update(hostParameter, "192.168.0.1"); verify(parameterService, never()).add(processDefinitionId, "password", "kittycat"); assertThat(systemOutRule.getLog()).contains( "Parameter doesn't exist in process definition <4656556> and has not been merged."); } private SParameter sParameter(String name, String value, long processDefinitionId) { return new SParameter(name, value, processDefinitionId); } } ================================================ FILE: bpm/bonita-core/bonita-platform-login/build.gradle ================================================ dependencies { api project(':services:bonita-commons') api project(':services:bonita-platform-session') api project(':services:bonita-platform-authentication') testImplementation libs.mockitoCore } ================================================ FILE: bpm/bonita-core/bonita-platform-login/src/main/java/org/bonitasoft/engine/core/platform/login/PlatformLoginService.java ================================================ /** * 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.core.platform.login; import org.bonitasoft.engine.platform.session.SSessionNotFoundException; import org.bonitasoft.engine.platform.session.model.SPlatformSession; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @since 6.0 */ public interface PlatformLoginService { /** * login to the platform by userName and password * * @param userName * name of user * @param password * password of user * @return an SPlatformSession object * @see SPlatformSession * @throws SPlatformLoginException */ SPlatformSession login(String userName, String password) throws SPlatformLoginException, SInvalidPlatformCredentialsException; /** * logout the platform by sessionId * * @param sessionId * identifier of platform session * @throws SSessionNotFoundException */ void logout(final long sessionId) throws SSessionNotFoundException; /** * Verify if a session is valid * * @param sessionId * identifier of platform session * @return true if session is valid, false otherwise. */ boolean isValid(final long sessionId); } ================================================ FILE: bpm/bonita-core/bonita-platform-login/src/main/java/org/bonitasoft/engine/core/platform/login/SInvalidPlatformCredentialsException.java ================================================ /** * 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.core.platform.login; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SInvalidPlatformCredentialsException extends SBonitaException { public SInvalidPlatformCredentialsException(Throwable e) { super(e); } } ================================================ FILE: bpm/bonita-core/bonita-platform-login/src/main/java/org/bonitasoft/engine/core/platform/login/SPlatformLoginException.java ================================================ /** * 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.core.platform.login; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SPlatformLoginException extends SBonitaException { private static final long serialVersionUID = 4420091633702041899L; public SPlatformLoginException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-platform-login/src/main/java/org/bonitasoft/engine/core/platform/login/impl/PlatformLoginServiceImpl.java ================================================ /** * 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.core.platform.login.impl; import org.bonitasoft.engine.core.platform.login.PlatformLoginService; import org.bonitasoft.engine.core.platform.login.SInvalidPlatformCredentialsException; import org.bonitasoft.engine.core.platform.login.SPlatformLoginException; import org.bonitasoft.engine.platform.authentication.PlatformAuthenticationService; import org.bonitasoft.engine.platform.authentication.SInvalidPasswordException; import org.bonitasoft.engine.platform.authentication.SInvalidUserException; import org.bonitasoft.engine.platform.session.PlatformSessionService; import org.bonitasoft.engine.platform.session.SSessionException; import org.bonitasoft.engine.platform.session.SSessionNotFoundException; import org.bonitasoft.engine.platform.session.model.SPlatformSession; /** * @author Elias Ricken de Medeiros * @author Feng Hui * @author Matthieu Chaffotte */ public class PlatformLoginServiceImpl implements PlatformLoginService { private final PlatformAuthenticationService authenticationService; private final PlatformSessionService sessionService; public PlatformLoginServiceImpl(final PlatformAuthenticationService platformAuthenticationService, final PlatformSessionService platformSessionService) { authenticationService = platformAuthenticationService; sessionService = platformSessionService; } @Override public SPlatformSession login(final String userName, final String password) throws SPlatformLoginException, SInvalidPlatformCredentialsException { try { authenticationService.checkUserCredentials(userName, password); return sessionService.createSession(userName); } catch (final SInvalidUserException | SInvalidPasswordException e) { throw new SInvalidPlatformCredentialsException(e); } catch (final SSessionException e) { throw new SPlatformLoginException(e); } } @Override public void logout(final long sessionId) throws SSessionNotFoundException { sessionService.deleteSession(sessionId); } @Override public boolean isValid(final long sessionId) { try { return sessionService.isValid(sessionId); } catch (final SSessionNotFoundException ssnfe) { return false; } } } ================================================ FILE: bpm/bonita-core/bonita-platform-login/src/test/java/org/bonitasoft/engine/core/platform/login/impl/PlatformLoginServiceImplTest.java ================================================ /** * 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.core.platform.login.impl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import org.bonitasoft.engine.core.platform.login.SInvalidPlatformCredentialsException; import org.bonitasoft.engine.core.platform.login.SPlatformLoginException; import org.bonitasoft.engine.platform.authentication.PlatformAuthenticationService; import org.bonitasoft.engine.platform.authentication.SInvalidPasswordException; import org.bonitasoft.engine.platform.authentication.SInvalidUserException; import org.bonitasoft.engine.platform.session.PlatformSessionService; import org.bonitasoft.engine.platform.session.SSessionException; import org.bonitasoft.engine.platform.session.SSessionNotFoundException; import org.bonitasoft.engine.platform.session.model.SPlatformSession; import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * @author Celine Souchet */ public class PlatformLoginServiceImplTest { @Mock private PlatformAuthenticationService authenticationService; @Mock private PlatformSessionService sessionService; @InjectMocks private PlatformLoginServiceImpl platformLoginServiceImpl; @Before public void setUp() { MockitoAnnotations.initMocks(this); } /** * Test method for * {@link org.bonitasoft.engine.core.platform.login.impl.PlatformLoginServiceImpl#login(java.lang.String, java.lang.String)}. */ @Test public final void login() throws Exception { final String userName = "userName"; final String password = "pwd"; doNothing().when(authenticationService).checkUserCredentials(userName, password); final SPlatformSession sPlatformSession = mock(SPlatformSession.class); doReturn(sPlatformSession).when(sessionService).createSession(userName); assertEquals(sPlatformSession, platformLoginServiceImpl.login(userName, password)); } @Test(expected = SInvalidPlatformCredentialsException.class) public final void loginThrowSInvalidUserException() throws Exception { final String userName = "userName"; final String password = "pwd"; doThrow(new SInvalidUserException("")).when(authenticationService).checkUserCredentials(userName, password); platformLoginServiceImpl.login(userName, password); } @Test(expected = SInvalidPlatformCredentialsException.class) public final void loginThrowSInvalidPasswordException() throws Exception { final String userName = "userName"; final String password = "pwd"; doThrow(new SInvalidPasswordException("")).when(authenticationService).checkUserCredentials(userName, password); platformLoginServiceImpl.login(userName, password); } @Test(expected = SPlatformLoginException.class) public final void loginThrowSSessionException() throws Exception { final String userName = "userName"; final String password = "pwd"; doNothing().when(authenticationService).checkUserCredentials(userName, password); final SPlatformSession sPlatformSession = mock(SPlatformSession.class); when(sessionService.createSession(userName)).thenReturn(sPlatformSession); doThrow(new SSessionException("")).when(sessionService).createSession(userName); assertEquals(sPlatformSession, platformLoginServiceImpl.login(userName, password)); } /** * Test method for {@link org.bonitasoft.engine.core.platform.login.impl.PlatformLoginServiceImpl#logout(long)}. */ @Test public final void logout() throws SSessionNotFoundException { final long sessionId = 415L; doNothing().when(sessionService).deleteSession(sessionId); platformLoginServiceImpl.logout(sessionId); } @Test(expected = SSessionNotFoundException.class) public final void logoutThrowSSessionException() throws SSessionNotFoundException { final long sessionId = 415L; doThrow(new SSessionNotFoundException("")).when(sessionService).deleteSession(sessionId); platformLoginServiceImpl.logout(sessionId); } /** * Test method for {@link org.bonitasoft.engine.core.platform.login.impl.PlatformLoginServiceImpl#isValid(long)}. */ @Test public final void isValid() throws SSessionNotFoundException { final long sessionId = 415L; doReturn(true).when(sessionService).isValid(sessionId); assertTrue(platformLoginServiceImpl.isValid(sessionId)); } @Test public final void isNotValid() throws SSessionNotFoundException { final long sessionId = 415L; doReturn(false).when(sessionService).isValid(sessionId); assertFalse(platformLoginServiceImpl.isValid(sessionId)); } @Test public final void isValidThrowException() throws SSessionNotFoundException { final long sessionId = 415L; doThrow(new SSessionNotFoundException("")).when(sessionService).isValid(sessionId); assertFalse(platformLoginServiceImpl.isValid(sessionId)); } } ================================================ FILE: bpm/bonita-core/bonita-process-comment/build.gradle ================================================ dependencies { api project(':services:bonita-session') api project(':services:bonita-commons') api project(':services:bonita-builder') api project(':services:bonita-events') api project(':services:bonita-log') api project(':services:bonita-archive') api project(':services:bonita-persistence') annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/api/SCommentAddException.java ================================================ /** * 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.core.process.comment.api; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Hongwen Zang * @author Celine Souchet */ public class SCommentAddException extends SBonitaException { private static final long serialVersionUID = 6387069363737827464L; public SCommentAddException(String message, Exception cause) { super(message, cause); } public SCommentAddException(String message) { super(message); } public SCommentAddException(Exception cause) { super(cause); } public SCommentAddException(long processInstanceId, final String commentType, final Exception e) { super("Can't create a " + commentType + " comment on the process instance.", e); setProcessInstanceIdOnContext(processInstanceId); } } ================================================ FILE: bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/api/SCommentDeletionException.java ================================================ /** * 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.core.process.comment.api; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SCommentDeletionException extends SBonitaException { private static final long serialVersionUID = -7042764134896151527L; public SCommentDeletionException(final String message, final Throwable cause) { super(message, cause); } public SCommentDeletionException(final String message) { super(message); } public SCommentDeletionException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/api/SCommentException.java ================================================ /** * 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.core.process.comment.api; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Hongwen Zang */ public class SCommentException extends SBonitaException { private static final long serialVersionUID = -6492733973957442676L; public SCommentException(String message, Throwable cause) { super(message, cause); } public SCommentException(String message) { super(message); } public SCommentException(Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/api/SCommentNotFoundException.java ================================================ /** * 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.core.process.comment.api; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Hongwen Zang */ public class SCommentNotFoundException extends SBonitaException { private static final long serialVersionUID = -8268354026852102242L; public SCommentNotFoundException(final String message, final Throwable cause) { super(message, cause); } public SCommentNotFoundException(final String message) { super(message); } public SCommentNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/api/SCommentService.java ================================================ /** * 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.core.process.comment.api; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.core.process.comment.model.SComment; import org.bonitasoft.engine.core.process.comment.model.archive.SAComment; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Hongwen Zang * @author Zhang Bole * @author Matthieu Chaffotte * @since 6.0 */ public interface SCommentService { String COMMENT = "COMMENT"; String COMMMENT_IS_DELETED = "deleting a comment"; /** * List all comments related to the specified query options. * * @param options * a QueryOptions object, containing some query conditions * @return a list of SComment objects corresponding to the criteria * @throws SBonitaReadException */ List searchComments(QueryOptions options) throws SBonitaReadException; /** * Number of all comments related to the specified query options. * * @param queryOptions * a QueryOptions object, containing some query conditions * @return number of all comments corresponding to the criteria. * @throws SBonitaReadException */ long getNumberOfComments(QueryOptions queryOptions) throws SBonitaReadException; /** * Add a comment on process instance * * @param processInstanceId * identifier of processInstance * @param comment * the comment you want to add * @param userId * The user that will be said to have made the comment * @throws SCommentAddException */ SComment addComment(long processInstanceId, String comment, long userId) throws SCommentAddException; /** * Add a system comment on process instance * * @param processInstanceId * identifier of processInstance * @param comment * the comment you want to add * @throws SCommentAddException */ SComment addSystemComment(long processInstanceId, String comment) throws SCommentAddException; /** * Get comments for the given processInstance * * @param processInstanceId * identifier of processInstance * @param queryOptions * the query options to filter the results * @return a list of SComment object * @throws SBonitaReadException * in case of read error * @since 6.1 */ List getComments(long processInstanceId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search number of Comment for a specific supervisor * * @param supervisorId * the identifier of supervisor * @param queryOptions * a map of specific parameters of a query * @return number of Comment for a specific supervisor * @throws SBonitaReadException * if a Read exception occurs */ long getNumberOfCommentsSupervisedBy(long supervisorId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search an Comment for a specific supervisor * * @param supervisorId * the identifier of supervisor * @param queryOptions * a map of specific parameters of a query * @return an Comment for a specific supervisor * @throws SBonitaReadException * if a Read exception occurs */ List searchCommentsSupervisedBy(long supervisorId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search number of all the comments on process instants that the user can access * * @param userId * identifier of user * @param searchOptions * a QueryOptions object, containing some query conditions * @return number of all the comments on process instants that the user can access * @throws SBonitaReadException */ long getNumberOfCommentsInvolvingUser(long userId, QueryOptions searchOptions) throws SBonitaReadException; /** * List all the comments on process instants that the user can access * * @param userId * identifier of user * @param queryOptions * a QueryOptions object, containing some query conditions * @return a list of comments on process instants that the user can access * @throws SBonitaReadException */ List searchCommentsInvolvingUser(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search number of the comments visible by delegates of managerUserId * * @param managerUserId * identifier of a manager user * @param searchOptions * a QueryOptions object, containing some query conditions * @return number of the comments visible by delegates of managerUserId * @throws SBonitaReadException */ long getNumberOfCommentsManagedBy(long managerUserId, QueryOptions searchOptions) throws SBonitaReadException; /** * Search comments visible by delegates of managerUserId * * @param managerUserId * identifier of a manager user * @param searchOptions * a QueryOptions object, containing some query conditions * @return a list of comments visible by delegates of managerUserId * @throws SBonitaReadException */ List searchCommentsManagedBy(long managerUserId, QueryOptions searchOptions) throws SBonitaReadException; /** * Search number of archived Comments * * @param searchOptions * @throws SBonitaReadException */ long getNumberOfArchivedComments(QueryOptions searchOptions) throws SBonitaReadException; /** * Search archived Comments * * @param searchOptions * a QueryOptions object, containing some query conditions * @return a list with archived Comments * @throws SBonitaReadException */ List searchArchivedComments(QueryOptions searchOptions) throws SBonitaReadException; /** * Returning true if the system comments are enabled for the specific SystemCommentType. * * @param sct * A Enum for system comments * @return Returning true if the system comments are enabled for the specific SystemCommentType. * @since 6.0 */ boolean isCommentEnabled(SystemCommentType sct); /** * Delete the comment * * @param comment * @throws SCommentDeletionException */ void delete(SComment comment) throws SCommentDeletionException; /** * @param archivedCommentId * @throws SCommentNotFoundException * @throws SBonitaReadException */ SAComment getArchivedComment(long archivedCommentId) throws SCommentNotFoundException, SBonitaReadException; /** * Delete archived comments for specified process instances * * @param processInstanceIds */ void deleteArchivedComments(List processInstanceIds) throws SBonitaException; /** * Delete comments for a specified process instance * * @param processInstanceId * @throws SBonitaException * @since 6.1 */ void deleteComments(long processInstanceId) throws SBonitaException; /** * @param archiveDate * @param sComment * @throws SObjectModificationException * @since 6.4.0 */ void archive(long archiveDate, SComment sComment) throws SObjectModificationException; } ================================================ FILE: bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/api/SystemCommentType.java ================================================ /** * 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.core.process.comment.api; /** * @author Hongwen Zang */ public enum SystemCommentType { STATE_CHANGE, // PRIORITY_CHANGE, // DUE_DATE_CHANGE } ================================================ FILE: bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/api/impl/SCommentServiceImpl.java ================================================ /** * 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.core.process.comment.api.impl; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.commons.NullCheckingUtil; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.core.process.comment.api.SCommentAddException; import org.bonitasoft.engine.core.process.comment.api.SCommentDeletionException; import org.bonitasoft.engine.core.process.comment.api.SCommentNotFoundException; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.comment.api.SystemCommentType; import org.bonitasoft.engine.core.process.comment.model.SComment; import org.bonitasoft.engine.core.process.comment.model.SHumanComment; import org.bonitasoft.engine.core.process.comment.model.SSystemComment; import org.bonitasoft.engine.core.process.comment.model.archive.SAComment; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; /** * @author Hongwen Zang * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class SCommentServiceImpl implements SCommentService { private static final String SUPERVISED_BY = "SupervisedBy"; private static final String INVOLVING_USER = "InvolvingUser"; private static final String MANAGED_BY = "ManagedBy"; private final Recorder recorder; private final ReadPersistenceService persistenceService; private final SessionService sessionService; private final ReadSessionAccessor sessionAccessor; private final Map systemCommentType; private final ArchiveService archiveService; public SCommentServiceImpl(final Recorder recorder, final ReadPersistenceService persistenceService, final ArchiveService archiveService, final SessionService sessionService, final ReadSessionAccessor sessionAccessor, final Map systemCommentType) { super(); this.recorder = recorder; this.persistenceService = persistenceService; this.sessionService = sessionService; this.sessionAccessor = sessionAccessor; this.systemCommentType = systemCommentType; this.archiveService = archiveService; } @Override public List searchComments(final QueryOptions options) throws SBonitaReadException { return persistenceService.searchEntity(SComment.class, options, null); } @Override public long getNumberOfComments(final QueryOptions options) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SComment.class, options, null); } @Override public List getComments(final long processInstanceId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("processInstanceId", (Object) processInstanceId); final SelectListDescriptor selectDescriptor = new SelectListDescriptor("getSComments", parameters, SComment.class, queryOptions); return persistenceService.selectList(selectDescriptor); } @Override public SComment addComment(long processInstanceId, String comment, long userId) throws SCommentAddException { NullCheckingUtil.checkArgsNotNull(processInstanceId); NullCheckingUtil.checkArgsNotNull(comment); try { final SComment sComment = new SHumanComment(processInstanceId, comment, userId); recorder.recordInsert(new InsertRecord(sComment), COMMENT); return sComment; } catch (final SRecorderException e) { throw new SCommentAddException(processInstanceId, "human", e); } } @Override public SComment addSystemComment(final long processInstanceId, final String comment) throws SCommentAddException { NullCheckingUtil.checkArgsNotNull(processInstanceId); NullCheckingUtil.checkArgsNotNull(comment); try { final SComment sComment = new SSystemComment(processInstanceId, comment); recorder.recordInsert(new InsertRecord(sComment), COMMENT); return sComment; } catch (final SRecorderException e) { throw new SCommentAddException(processInstanceId, "system", e); } } @Override public void delete(final SComment comment) throws SCommentDeletionException { NullCheckingUtil.checkArgsNotNull(comment); try { recorder.recordDelete(new DeleteRecord(comment), COMMENT); } catch (final SRecorderException e) { throw new SCommentDeletionException("Can't delete the comment " + comment, e); } } @Override public void deleteComments(final long processInstanceId) throws SBonitaException { final QueryOptions queryOptions = new QueryOptions(0, 100, SComment.class, "id", OrderByType.ASC); List sComments = null; do { sComments = getComments(processInstanceId, queryOptions); if (sComments != null) { for (final SComment sComment : sComments) { delete(sComment); } } } while (sComments != null && sComments.size() > 0); } private long getUserId() { return sessionService.getLoggedUserFromSession(sessionAccessor); } @Override public long getNumberOfCommentsSupervisedBy(final long supervisorId, final QueryOptions queryOptions) throws SBonitaReadException { try { final Map parameters = Collections.singletonMap("supervisorId", (Object) supervisorId); return persistenceService.getNumberOfEntities(SComment.class, SUPERVISED_BY, queryOptions, parameters); } catch (final SBonitaReadException bre) { throw new SBonitaReadException(bre); } } @Override public List searchCommentsSupervisedBy(final long supervisorId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("supervisorId", (Object) supervisorId); return persistenceService.searchEntity(SComment.class, SUPERVISED_BY, queryOptions, parameters); } @Override public long getNumberOfCommentsInvolvingUser(final long userId, final QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userId", (Object) userId); return persistenceService.getNumberOfEntities(SComment.class, INVOLVING_USER, searchOptions, parameters); } @Override public List searchCommentsInvolvingUser(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userId", (Object) userId); return persistenceService.searchEntity(SComment.class, INVOLVING_USER, queryOptions, parameters); } @Override public long getNumberOfCommentsManagedBy(final long managerUserId, final QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("managerUserId", (Object) managerUserId); return persistenceService.getNumberOfEntities(SComment.class, MANAGED_BY, searchOptions, parameters); } @Override public List searchCommentsManagedBy(final long managerUserId, final QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("managerUserId", (Object) managerUserId); return persistenceService.searchEntity(SComment.class, MANAGED_BY, searchOptions, parameters); } @Override public long getNumberOfArchivedComments(final QueryOptions searchOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); return persistenceService.getNumberOfEntities(SAComment.class, searchOptions, null); } @Override public List searchArchivedComments(final QueryOptions searchOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); return persistenceService.searchEntity(SAComment.class, searchOptions, null); } @Override public boolean isCommentEnabled(final SystemCommentType sct) { if (systemCommentType.containsKey(sct)) { return systemCommentType.get(sct); } return false; } @Override public SAComment getArchivedComment(final long archivedCommentId) throws SCommentNotFoundException, SBonitaReadException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); final SAComment selectById = persistenceService.selectById(new SelectByIdDescriptor(SAComment.class, archivedCommentId)); if (selectById == null) { throw new SCommentNotFoundException("Archived comment not found with id=" + archivedCommentId); } return selectById; } @Override public void deleteArchivedComments(List processInstanceIds) throws SBonitaException { archiveService.deleteFromQuery("deleteArchiveCommentsOfProcessInstances", Collections.singletonMap("processInstanceIds", processInstanceIds)); } @Override public void archive(final long archiveDate, final SComment sComment) throws SObjectModificationException { final ArchiveInsertRecord insertRecord = new ArchiveInsertRecord(new SAComment(sComment)); try { archiveService.recordInsert(archiveDate, insertRecord); } catch (final SRecorderException e) { throw new SObjectModificationException("Unable to archive the comment with id = <" + sComment.getId() + ">", e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/model/SComment.java ================================================ /** * 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.core.process.comment.model; import javax.persistence.Column; import javax.persistence.DiscriminatorColumn; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "kind") @Table(name = "process_comment") public class SComment implements PersistentObject { public static final String ID_KEY = "id"; public static final String USERID_KEY = "userId"; public static final String PROCESSINSTANCEID_KEY = "processInstanceId"; public static final String POSTDATE_KEY = "postDate"; public static final String CONTENT_KEY = "content"; public static final String KIND_KEY = "kind"; @Id private long id; @Column private Long userId; @Column private long processInstanceId; @Column private long postDate; @Column private String content; public SComment(final long processInstanceId, final String content) { super(); this.processInstanceId = processInstanceId; this.content = content; this.postDate = System.currentTimeMillis(); } } ================================================ FILE: bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/model/SHumanComment.java ================================================ /** * 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.core.process.comment.model; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("human") public class SHumanComment extends SComment { public SHumanComment(long processInstanceId, String content, Long userId) { super(processInstanceId, content); this.setUserId(userId); } } ================================================ FILE: bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/model/SSystemComment.java ================================================ /** * 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.core.process.comment.model; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("system") public class SSystemComment extends SComment { public SSystemComment(final long processInstanceId, final String content) { super(processInstanceId, content); } } ================================================ FILE: bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/model/archive/SAComment.java ================================================ /** * 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.core.process.comment.model.archive; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.comment.model.SComment; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @Entity @Table(name = "arch_process_comment") public class SAComment implements ArchivedPersistentObject { public static final String ID_KEY = "id"; public static final String USERID_KEY = "userId"; public static final String PROCESSINSTANCEID_KEY = "processInstanceId"; public static final String POSTDATE_KEY = "postDate"; public static final String CONTENT_KEY = "content"; public static final String ARCHIVEDATE_KEY = "archiveDate"; public static final String SOURCEOBJECTID_KEY = "sourceObjectId"; @Id private long id; private Long userId; private long processInstanceId; private long sourceObjectId; private long postDate; private long archiveDate; private String content; public SAComment(final SComment sComment) { content = sComment.getContent(); postDate = sComment.getPostDate(); sourceObjectId = sComment.getId(); processInstanceId = sComment.getProcessInstanceId(); userId = sComment.getUserId(); } @Override public Class getPersistentObjectInterface() { return SComment.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/model/builder/SCommentLogBuilderFactory.java ================================================ /** * 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.core.process.comment.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Hongwen Zang */ public interface SCommentLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { } ================================================ FILE: bpm/bonita-core/bonita-process-comment/src/main/java/org/bonitasoft/engine/core/process/comment/model/builder/impl/SCommentLogBuilderFactoryImpl.java ================================================ /** * 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.core.process.comment.model.builder.impl; import org.bonitasoft.engine.core.process.comment.model.builder.SCommentLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Hongwen Zang * @author Matthieu Chaffotte */ public class SCommentLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SCommentLogBuilderFactory { public static final String COMMENT = "COMMENT"; public static final String COMMENT_INDEX_NAME = "numericIndex1"; @Override public String getObjectIdKey() { return COMMENT_INDEX_NAME; } } ================================================ FILE: bpm/bonita-core/bonita-process-comment/src/main/resources/org/bonitasoft/engine/core/process/comment/model/impl/hibernate/archive.comment.queries.hbm.xml ================================================ SELECT COUNT(DISTINCT saComment.id) FROM org.bonitasoft.engine.core.process.comment.model.archive.SAComment AS saComment WHERE saComment.archiveDate = ( SELECT MAX(sac.archiveDate) FROM org.bonitasoft.engine.core.process.comment.model.archive.SAComment AS sac WHERE sac.sourceObjectId = saComment.sourceObjectId ) SELECT saComment FROM org.bonitasoft.engine.core.process.comment.model.archive.SAComment AS saComment WHERE saComment.archiveDate = ( SELECT MAX(sac.archiveDate) FROM org.bonitasoft.engine.core.process.comment.model.archive.SAComment AS sac WHERE sac.sourceObjectId = saComment.sourceObjectId ) SELECT saComment FROM org.bonitasoft.engine.core.process.comment.model.archive.SAComment AS saComment WHERE saComment.id = :id DELETE FROM org.bonitasoft.engine.core.process.comment.model.archive.SAComment AS saComment WHERE saComment.processInstanceId IN (:processInstanceIds) ================================================ FILE: bpm/bonita-core/bonita-process-comment/src/main/resources/org/bonitasoft/engine/core/process/comment/model/impl/hibernate/comment.queries.hbm.xml ================================================ SELECT comment FROM org.bonitasoft.engine.core.process.comment.model.SComment AS comment SELECT COUNT(comment.id) FROM org.bonitasoft.engine.core.process.comment.model.SComment AS comment SELECT comment FROM org.bonitasoft.engine.core.process.comment.model.SComment AS comment WHERE comment.processInstanceId = (:processInstanceId) SELECT comment FROM org.bonitasoft.engine.core.process.comment.model.SComment AS comment, org.bonitasoft.engine.identity.model.SUser AS user WHERE user.id = comment.userId SELECT count(comment.id) FROM org.bonitasoft.engine.core.process.comment.model.SComment AS comment, org.bonitasoft.engine.identity.model.SUser AS user WHERE user.id = comment.userId SELECT COUNT(DISTINCT comment.id) FROM org.bonitasoft.engine.core.process.comment.model.SHumanComment AS comment, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE ( supervisor.userId = :supervisorId OR supervisor.id IN ( SELECT DISTINCT processsupervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user_membership.userId = :supervisorId AND ( (processsupervisor.groupId = user_membership.groupId AND processsupervisor.roleId <= 0) OR (processsupervisor.roleId = user_membership.roleId AND processsupervisor.groupId <= 0) OR (processsupervisor.roleId = user_membership.roleId AND processsupervisor.groupId = user_membership.groupId) ) ) ) AND ( comment.processInstanceId IN ( SELECT DISTINCT pi.id FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS pi WHERE pi.processDefinitionId = supervisor.processDefId ) OR comment.processInstanceId IN ( SELECT DISTINCT api.sourceObjectId FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS api WHERE api.processDefinitionId = supervisor.processDefId ) ) SELECT DISTINCT comment FROM org.bonitasoft.engine.core.process.comment.model.SHumanComment AS comment, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE ( supervisor.userId = :supervisorId OR supervisor.id IN ( SELECT DISTINCT processsupervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user_membership.userId = :supervisorId AND ( (processsupervisor.groupId = user_membership.groupId AND processsupervisor.roleId <= 0) OR (processsupervisor.roleId = user_membership.roleId AND processsupervisor.groupId <= 0) OR (processsupervisor.roleId = user_membership.roleId AND processsupervisor.groupId = user_membership.groupId) ) ) ) AND ( comment.processInstanceId IN ( SELECT DISTINCT pi.id FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS pi WHERE pi.processDefinitionId = supervisor.processDefId ) OR comment.processInstanceId IN ( SELECT DISTINCT api.sourceObjectId FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS api WHERE api.processDefinitionId = supervisor.processDefId ) ) SELECT count(DISTINCT comment.id) FROM org.bonitasoft.engine.core.process.comment.model.SHumanComment AS comment, org.bonitasoft.engine.identity.model.SUser AS u WHERE comment.userId = u.id AND ( comment.userId = :userId OR comment.processInstanceId IN ( SELECT usertask.logicalGroup2 FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS usertask WHERE usertask.assigneeId = :userId ) OR comment.processInstanceId IN ( SELECT at.logicalGroup2 FROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS at WHERE at.assigneeId = :userId ) OR comment.processInstanceId IN ( SELECT p.id FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.identity.model.SUser AS user WHERE p.startedBy = user.id AND user.id = comment.userId AND user.id = :userId ) OR comment.processInstanceId IN ( SELECT pi.id FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS pi, org.bonitasoft.engine.identity.model.SUser AS user WHERE pi.startedBy = user.id AND user.id = comment.userId AND user.id = :userId )) SELECT DISTINCT comment FROM org.bonitasoft.engine.core.process.comment.model.SHumanComment AS comment, org.bonitasoft.engine.identity.model.SUser AS u WHERE comment.userId = u.id AND (comment.userId = :userId OR comment.processInstanceId IN ( SELECT usertask.logicalGroup2 FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS usertask WHERE usertask.assigneeId = :userId ) OR comment.processInstanceId IN ( SELECT at.logicalGroup2 FROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS at WHERE at.assigneeId = :userId ) OR comment.processInstanceId IN ( SELECT p.id FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.identity.model.SUser AS user WHERE p.startedBy = user.id AND user.id = comment.userId AND user.id = :userId ) OR comment.processInstanceId IN ( SELECT pi.id FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS pi, org.bonitasoft.engine.identity.model.SUser AS user WHERE pi.startedBy = user.id AND user.id = comment.userId AND user.id = :userId )) SELECT count(DISTINCT comment.id) FROM org.bonitasoft.engine.core.process.comment.model.SHumanComment AS comment, org.bonitasoft.engine.identity.model.SUser AS u WHERE u.managerUserId = :managerUserId AND (comment.userId = u.id OR comment.processInstanceId IN ( SELECT usertask.logicalGroup2 FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS usertask WHERE usertask.assigneeId = u.id ) OR comment.processInstanceId IN ( SELECT at.logicalGroup2 FROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS at WHERE at.assigneeId = u.id ) OR comment.processInstanceId IN ( SELECT p.id FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.identity.model.SUser AS user WHERE p.startedBy = u.id ) OR comment.processInstanceId IN ( SELECT pi.sourceObjectId FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS pi, org.bonitasoft.engine.identity.model.SUser AS user WHERE pi.startedBy = u.id ) ) SELECT comment FROM org.bonitasoft.engine.core.process.comment.model.SHumanComment AS comment, org.bonitasoft.engine.identity.model.SUser AS u WHERE u.managerUserId = :managerUserId AND (comment.userId = u.id OR comment.processInstanceId IN ( SELECT usertask.logicalGroup2 FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS usertask WHERE usertask.assigneeId = u.id ) OR comment.processInstanceId IN ( SELECT at.logicalGroup2 FROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS at WHERE at.assigneeId = u.id ) OR comment.processInstanceId IN ( SELECT p.id FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.identity.model.SUser AS user WHERE p.startedBy = u.id ) OR comment.processInstanceId IN ( SELECT pi.sourceObjectId FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS pi, org.bonitasoft.engine.identity.model.SUser AS user WHERE pi.startedBy = u.id ) ) ================================================ FILE: bpm/bonita-core/bonita-process-definition/build.gradle ================================================ dependencies { api project(':bpm:bonita-common') api project(':services:bonita-identity') api project(':services:bonita-expression') api project(':services:bonita-data-definition') api project(':services:bonita-commons') api project(':services:bonita-log') api project(':services:bonita-persistence') api project(':services:bonita-time-tracker') api project(':services:bonita-data-instance') api project(':services:bonita-classloader') api project(':services:bonita-builder') testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.logback annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/expression/control/api/ExpressionResolverService.java ================================================ /** * 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.core.expression.control.api; import java.util.List; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Zhao Na * @author Baptiste Mesta * @since 6.0 */ public interface ExpressionResolverService { /** * Evaluate the specific expression * * @param expression * the expression will be evaluated * @return the evaluated expression result * @throws SExpressionTypeUnknownException * @throws SExpressionEvaluationException * @throws SExpressionDependencyMissingException * @throws SInvalidExpressionException */ Object evaluate(final SExpression expression) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException; /** * Evaluate the specific expression with the given expressionContext. * * @param expression * the expression will be evaluated * @param contextDependency * the expressionContext, it can contain some value informations or evaluated enviorenment for expressions * @return the evaluated expression result * @throws SExpressionTypeUnknownException * @throws SExpressionEvaluationException * @throws SExpressionDependencyMissingException * @throws SInvalidExpressionException */ Object evaluate(final SExpression expression, final SExpressionContext contextDependency) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException; /** * Evaluate the specific expressions with the given expressionContext. * * @param expressions * a list of expressions will be evaluated * @param contextDependency * the expressionContext, it can contain some value information or evaluated environment for expressions * @return the evaluated expression result in same order as expressions parameter * @throws SExpressionTypeUnknownException * @throws SExpressionEvaluationException * @throws SExpressionDependencyMissingException * @throws SInvalidExpressionException */ List evaluate(final List expressions, final SExpressionContext contextDependency) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException; } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/expression/control/api/impl/ExpressionResolverServiceImpl.java ================================================ /** * 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.core.expression.control.api.impl; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.classloader.SClassLoaderException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.ExpressionExecutorStrategy; import org.bonitasoft.engine.expression.ExpressionService; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.tracking.TimeTracker; import org.bonitasoft.engine.tracking.TimeTrackerRecords; /** * @author Zhao Na * @author Emmanuel Duchastenier * @author Baptiste Mesta * @author Celine Souchet */ public class ExpressionResolverServiceImpl implements ExpressionResolverService { private static final SExpressionContext EMPTY_CONTEXT = new SExpressionContext(); private final ExpressionService expressionService; private final ProcessDefinitionService processDefinitionService; private final ClassLoaderService classLoaderService; private final TimeTracker timeTracker; public ExpressionResolverServiceImpl(final ExpressionService expressionService, final ProcessDefinitionService processDefinitionService, final ClassLoaderService classLoaderService, final TimeTracker timeTracker) { this.expressionService = expressionService; this.processDefinitionService = processDefinitionService; this.classLoaderService = classLoaderService; this.timeTracker = timeTracker; } @Override public Object evaluate(final SExpression expression) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { return evaluate(expression, EMPTY_CONTEXT); } @Override public Object evaluate(final SExpression expression, final SExpressionContext evaluationContext) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { final long startTime = System.currentTimeMillis(); try { return evaluateExpressionsFlatten(Collections.singletonList(expression), evaluationContext).get(0); } finally { if (timeTracker.isTrackable(TimeTrackerRecords.EVALUATE_EXPRESSION_INCLUDING_CONTEXT)) { final long endTime = System.currentTimeMillis(); final StringBuilder desc = new StringBuilder(); desc.append("Expression: "); desc.append(expression); desc.append(" - "); desc.append("evaluationContext: "); desc.append(evaluationContext); timeTracker.track(TimeTrackerRecords.EVALUATE_EXPRESSION_INCLUDING_CONTEXT, desc.toString(), endTime - startTime); } } } private List evaluateExpressionsFlatten(final List expressions, final SExpressionContext evaluationContext) throws SInvalidExpressionException, SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException { final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); SExpressionContext newEvaluationContext = EMPTY_CONTEXT; try { final Map dependencyValues = new HashMap<>(); if (evaluationContext != null) { newEvaluationContext = evaluationContext; fillContext(newEvaluationContext, dependencyValues); } loadProcessClassLoader(newEvaluationContext); final Map dataReplacement = new HashMap<>(); // We incrementally build the Map of already resolved expressions: final Map resolvedExpressions = new HashMap<>(); // Let's evaluate all expressions with no dependencies first: resolvedExpressions.putAll(evaluateAllExpressionsWithNoDependencies(dependencyValues, dataReplacement, expressions, newEvaluationContext)); for (final SExpression sExpression : expressions) { if (sExpression != null) { // Then evaluate recursively all remaining expressions: resolvedExpressions.putAll(evaluateExpressionWithResolvedDependencies(sExpression, dependencyValues, dataReplacement, resolvedExpressions, newEvaluationContext.getContainerState())); } } final List results = new ArrayList<>(expressions.size()); for (final SExpression sExpression : expressions) { if (sExpression != null) { final int key = sExpression.getDiscriminant(); final Object res = resolvedExpressions.get(key); if (res == null && !resolvedExpressions.containsKey(key)) { throw new SExpressionEvaluationException("No result found for the expression " + sExpression, sExpression.getName()); } results.add(res); } else { results.add(null); } } return results; } catch (final SProcessDefinitionNotFoundException | SBonitaReadException e) { throw buildSExpressionEvaluationExceptionWhenNotFindProcess(newEvaluationContext, e); } catch (final SClassLoaderException e) { throw new SExpressionEvaluationException(e, null); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } private void loadProcessClassLoader(final SExpressionContext evaluationContext) throws SClassLoaderException { Long processId; if (evaluationContext.getParentProcessDefinitionId() != null) { processId = evaluationContext.getParentProcessDefinitionId(); } else { processId = evaluationContext.getProcessDefinitionId(); } if (processId != null) { Thread.currentThread().setContextClassLoader( classLoaderService.getClassLoader(identifier(ScopeType.PROCESS, processId))); } } private SExpressionEvaluationException buildSExpressionEvaluationExceptionWhenNotFindProcess( final SExpressionContext evaluationContext, final SBonitaException e) { final SExpressionEvaluationException exception = new SExpressionEvaluationException( "The process definition was not found.", e, null); exception.setProcessDefinitionIdOnContext(evaluationContext.getProcessDefinitionId()); return exception; } private Map evaluateAllExpressionsWithNoDependencies(final Map dependencyValues, final Map dataReplacement, final List expressions, final SExpressionContext evaluationContext) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { final Map resolvedExpressions = new HashMap<>(); final Map> expressionMapByKind = flattenDependencies(expressions); final List variableExpressions = expressionMapByKind .get(new ExpressionKind(ExpressionType.TYPE_VARIABLE.name())); if (evaluationContext.isEvaluateInDefinition() && variableExpressions != null && !variableExpressions.isEmpty()) { final SExpression expressionNotProvided = variablesAreAllProvided(variableExpressions, evaluationContext); if (expressionNotProvided != null) { // We forbid the evaluation of expressions of type VARIABLE at process definition level: throw new SExpressionEvaluationException( "Evaluation of expressions of type VARIABLE is forbidden at process definition level.", expressionNotProvided.getName()); } } for (final ExpressionKind kind : ExpressionExecutorStrategy.NO_DEPENDENCY_EXPRESSION_EVALUATION_ORDER) { resolvedExpressions.putAll(evaluateExpressionsOfKind(dependencyValues, expressionMapByKind.get(kind), kind, dataReplacement, resolvedExpressions, evaluationContext.getContainerState())); expressionMapByKind.remove(kind); } return resolvedExpressions; } private SExpression variablesAreAllProvided(final List variableExpressions, final SExpressionContext evaluationContext) { final Iterator iterator = variableExpressions.iterator(); final Map inputValues = evaluationContext.getInputValues(); while (iterator.hasNext()) { final SExpression next = iterator.next(); if (!inputValues.containsKey(next.getContent())) { return next; } } return null; } private Map evaluateExpressionWithResolvedDependencies( final SExpression sExpression, final Map dependencyValues, final Map dataReplacement, final Map alreadyResolvedExpressions, final ContainerState containerState) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { final Map resolvedExpressions = new HashMap<>(alreadyResolvedExpressions); // Evaluate the dependencies first: for (final SExpression dep : sExpression.getDependencies()) { resolvedExpressions.putAll(evaluateExpressionWithResolvedDependencies(dep, dependencyValues, dataReplacement, resolvedExpressions, containerState)); } // Then evaluate the expression itself: if (!resolvedExpressions.containsKey(sExpression.getDiscriminant())) { // Let's evaluate the expression only if it is not already in the list of resolved dependencies: final Object exprResult = expressionService.evaluate(sExpression, dependencyValues, resolvedExpressions, containerState); addResultToMap(resolvedExpressions, dataReplacement, sExpression, exprResult, dependencyValues); } return resolvedExpressions; } private Map evaluateExpressionsOfKind(final Map dependencyValues, final List expressionsOfKind, final ExpressionKind kind, final Map dataReplacement, final Map alreadyResolvedExpressions, final ContainerState containerState) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { final Map resolvedExpressions = new HashMap<>(); if (expressionsOfKind != null) { final List evaluationResults = expressionService.evaluate(kind, expressionsOfKind, dependencyValues, alreadyResolvedExpressions, containerState); final Iterator variableIterator = expressionsOfKind.iterator(); for (final Object evaluationResult : evaluationResults) { final SExpression expression = variableIterator.next(); addResultToMap(resolvedExpressions, dataReplacement, expression, evaluationResult, dependencyValues); } } return resolvedExpressions; } private void addResultToMap(final Map resolvedExpressions, final Map dataReplacement, final SExpression expression, final Object expressionResult, final Map dependencyValues) { resolvedExpressions.put(expression.getDiscriminant(), expressionResult); if (expressionService.mustPutEvaluatedExpressionInContext(expression.getExpressionKind())) { dependencyValues.put(expression.getContent(), expressionResult); } final SExpression replacement = dataReplacement.get(expression); if (replacement != null) { resolvedExpressions.put(replacement.getDiscriminant(), expressionResult); if (expressionService.mustPutEvaluatedExpressionInContext(expression.getExpressionKind())) { dependencyValues.put(replacement.getContent(), expressionResult); } } } private Map> flattenDependencies(final Collection collection) { final Map> expressionMapByKind = new HashMap<>(); for (final SExpression sExpression : collection) { if (sExpression == null) { continue; } final ExpressionKind expressionKind = sExpression.getExpressionKind(); // Get from the map the list of expressions of given ExpressionKind List exprList = expressionMapByKind.get(expressionKind); // If no present, put it in the map if (exprList == null) { exprList = new ArrayList<>(); expressionMapByKind.put(expressionKind, exprList); } if (!exprList.contains(sExpression)) { exprList.add(sExpression); } if (sExpression.getDependencies() != null) { expressionMapByKind.putAll(flattenDependencies(sExpression.getDependencies())); } } return expressionMapByKind; } private void fillContext(final SExpressionContext evaluationContext, final Map dependencyValues) throws SProcessDefinitionNotFoundException, SBonitaReadException { if (evaluationContext.getContainerId() == null && evaluationContext.getProcessDefinitionId() != null) { final SProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(evaluationContext.getProcessDefinitionId()); evaluationContext.setProcessDefinition(processDefinition); evaluationContext.setEvaluateInDefinition(true); } if (evaluationContext.getContainerId() != null) { dependencyValues.put(SExpressionContext.CONTAINER_ID_KEY, evaluationContext.getContainerId()); } if (evaluationContext.getContainerType() != null) { dependencyValues.put(SExpressionContext.CONTAINER_TYPE_KEY, evaluationContext.getContainerType()); } if (evaluationContext.getProcessDefinitionId() != null) { dependencyValues.put(SExpressionContext.PROCESS_DEFINITION_ID_KEY, evaluationContext.getProcessDefinitionId()); } if (evaluationContext.getTime() != 0) { dependencyValues.put(SExpressionContext.TIME_KEY, evaluationContext.getTime()); } if (evaluationContext.getInputValues() != null) { dependencyValues.putAll(evaluationContext.getInputValues()); } } @Override public List evaluate(final List expressions, final SExpressionContext contextDependency) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { return evaluateExpressionsFlatten(expressions, contextDependency); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/expression/control/model/SExpressionContext.java ================================================ /** * 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.core.expression.control.model; import java.io.Serializable; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Zhao Na * @author Matthieu Chaffotte * @author Celine Souchet */ public class SExpressionContext implements Serializable { private static final long serialVersionUID = 6417383664862870145L; public static final String CONTAINER_ID_KEY = "containerId"; public static final String CONTAINER_TYPE_KEY = "containerType"; public static final String TIME_KEY = "time"; public static final String PROCESS_DEFINITION_ID_KEY = "processDefinitionId"; public static final String PROCESS_DEFINITION_KEY = "processDefinition"; private Long containerId; private String containerType; private ContainerState containerState; private long time; private Long processDefinitionId; private Long parentProcessDefinitionId; private SProcessDefinition processDefinition; private Map inputValues; private boolean evaluateInDefinition = false; private Map dataMap; private Map invertedDataMap; public SExpressionContext() { inputValues = new HashMap<>(); } public SExpressionContext(final Long containerId, final String containerType, final Long processDefinitionId) { this(containerId, containerType, processDefinitionId, null); } public SExpressionContext(final long containerId, final String containerType, final Long processDefinitionId, final Map inputValues) { this.containerId = containerId; this.containerType = containerType; this.processDefinitionId = processDefinitionId; if (inputValues == null) { this.inputValues = new HashMap<>(); } else { this.inputValues = new HashMap<>(inputValues); } } public Long getProcessDefinitionId() { return processDefinitionId; } public void setProcessDefinitionId(final Long processDefinitionId) { this.processDefinitionId = processDefinitionId; } public long getTime() { return time; } public void setTime(final long time) { this.time = time; } public Long getContainerId() { return containerId; } public void setContainerId(final Long containerId) { this.containerId = containerId; } public String getContainerType() { return containerType; } public void setContainerType(final String containerType) { this.containerType = containerType; } public ContainerState getContainerState() { return containerState; } public void setContainerState(final ContainerState containerState) { this.containerState = containerState; } public Map getInputValues() { return inputValues; } public SProcessDefinition getProcessDefinition() { return processDefinition; } public void setProcessDefinition(final SProcessDefinition processDefinition) { this.processDefinition = processDefinition; final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final List dataDefinitions = processContainer.getDataDefinitions(); dataMap = new HashMap<>(dataDefinitions.size()); invertedDataMap = new HashMap<>(dataDefinitions.size()); for (final SDataDefinition dataDef : dataDefinitions) { dataMap.put(dataDef.getName(), dataDef.getDefaultValueExpression()); invertedDataMap.put(dataDef.getDefaultValueExpression(), dataDef.getName()); } } public SExpression getDefaultValueFor(final String name) { if (evaluateInDefinition) { return dataMap.get(name); } return null; } public String isDefaultValueOf(final SExpression exp) { if (evaluateInDefinition) { return invertedDataMap.get(exp); } return null; } public void setInputValues(final Map inputValues) { if (inputValues == null) { this.inputValues = new HashMap<>(); } else { this.inputValues = new HashMap<>(inputValues); } } public void putAllInputValues(final Map inputValues) { if (inputValues != null) { this.inputValues.putAll(inputValues); } } public void setSerializableInputValues(final Map inputValues) { if (inputValues != null) { this.inputValues.putAll(inputValues); } } public void setEvaluateInDefinition(final boolean evaluateInDefinition) { this.evaluateInDefinition = evaluateInDefinition; } public boolean isEvaluateInDefinition() { return evaluateInDefinition; } @Override public String toString() { return "context [containerId=" + containerId + ", containerType=" + containerType + ", processDefinitionId=" + processDefinitionId + (processDefinition != null ? ", processDefinition=" + processDefinition.getName() + " -- " + processDefinition.getVersion() : "") + "]"; } public Long getParentProcessDefinitionId() { return parentProcessDefinitionId; } public void setParentProcessDefinitionId(final Long parentProcessDefinitionId) { this.parentProcessDefinitionId = parentProcessDefinitionId; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof SExpressionContext that)) return false; return Objects.equals(time, that.time) && Objects.equals(evaluateInDefinition, that.evaluateInDefinition) && Objects.equals(containerId, that.containerId) && Objects.equals(containerType, that.containerType) && Objects.equals(containerState, that.containerState) && Objects.equals(processDefinitionId, that.processDefinitionId) && Objects.equals(parentProcessDefinitionId, that.parentProcessDefinitionId) && Objects.equals(processDefinition, that.processDefinition) && Objects.equals(inputValues, that.inputValues) && Objects.equals(dataMap, that.dataMap) && Objects.equals(invertedDataMap, that.invertedDataMap); } @Override public int hashCode() { return Objects.hash(containerId, containerType, containerState, time, processDefinitionId, parentProcessDefinitionId, processDefinition, inputValues, evaluateInDefinition, dataMap, invertedDataMap); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/LeftOperandHandler.java ================================================ /** * 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.core.operation; import java.util.List; import java.util.Map; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * Tells the engine how to retrieve and update a left operand having the specified type * e.g. a data left operand handler will get data from database and set the data back in database * * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface LeftOperandHandler { String getType(); /** * @param inputValues contains value(s) given by the strategy to update the left operand with * @param sLeftOperand the left operand * @param newValue the value to set the element with * @param containerId the container id * @param containerType the container type * @throws SOperationExecutionException */ Object update(SLeftOperand sLeftOperand, Map inputValues, Object newValue, long containerId, String containerType) throws SOperationExecutionException; void delete(SLeftOperand sLeftOperand, long containerId, String containerType) throws SOperationExecutionException; /** * retrieve the left operand and put it in context as needed by the left operand * * @param sLeftOperand the left operand * @param leftOperandContainerId the left operand container id. Used to execute the left Operand in the correct * context * @param leftOperandContainerType the left operand container type. Used to execute the left Operand in the correct * context * @param contextToSet the context to add the value i * @throws SBonitaReadException */ void loadLeftOperandInContext(SLeftOperand sLeftOperand, final long leftOperandContainerId, final String leftOperandContainerType, SExpressionContext contextToSet) throws SBonitaReadException; void loadLeftOperandInContext(List sLeftOperandList, final long leftOperandContainerId, final String leftOperandContainerType, SExpressionContext contextToSet) throws SBonitaReadException; } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/OperationExecutorStrategy.java ================================================ /** * 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.core.operation; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SOperation; /** * @author Zhang Bole * @author Elias Ricken de Medeiros * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface OperationExecutorStrategy { /** * Calculates the new value of the left operand base of right operand expression value * * @param operation the operation in progress * @param rightOperandValue * result of the evaluation of right operand expression * @param expressionContext the expression context * @param shouldPersistValue true if the right operand must be persisted (Business Data) * @return * the new value to set the left operand with * @throws SOperationExecutionException */ Object computeNewValueForLeftOperand(SOperation operation, Object rightOperandValue, SExpressionContext expressionContext, final boolean shouldPersistValue) throws SOperationExecutionException; String getOperationType(); /** * Determines if the operation right value should be persisted when the value is evaluated to null. * * @return true if the the evaluated right value should be persisted; false otherwise. */ boolean shouldPersistOnNullValue(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/OperationExecutorStrategyProvider.java ================================================ /** * 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.core.operation; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.SOperatorType; import org.springframework.stereotype.Component; @Component public class OperationExecutorStrategyProvider { private final Map operationStrategies; public OperationExecutorStrategyProvider(final List operationExecutors) { operationStrategies = new HashMap<>(operationExecutors.size()); for (final OperationExecutorStrategy operationExecutorStrategy : operationExecutors) { operationStrategies.put(operationExecutorStrategy.getOperationType(), operationExecutorStrategy); } } public OperationExecutorStrategy getOperationExecutorStrategy(final SOperation operation) throws SOperationExecutionException { final String operatorTypeName = getStrategyKey(operation); final OperationExecutorStrategy operationExecutorStrategy = operationStrategies.get(operatorTypeName); if (operationExecutorStrategy == null) { throw new SOperationExecutionException("Unable to find an executor for operation type " + operatorTypeName); } return operationExecutorStrategy; } protected String getStrategyKey(final SOperation operation) { String key = operation.getType().name(); String leftOperandType = operation.getLeftOperand().getType(); if (leftOperandType.equals(SLeftOperand.TYPE_BUSINESS_DATA) && key.equals(SOperatorType.ASSIGNMENT.name())) { key += "_" + leftOperandType; } return key; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/OperationResult.java ================================================ /** * 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.core.operation; import java.io.Serializable; /** * @author Zhang Bole */ public class OperationResult { private Serializable result; private String returnType; // do we really need it? public OperationResult() { } public OperationResult(final Serializable result, final String returnType) { this.result = result; this.returnType = returnType; } public Serializable getResult() { return result; } public void setResult(final Serializable result) { this.result = result; } public String getReturnType() { return returnType; } public void setReturnType(final String returnType) { this.returnType = returnType; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/OperationService.java ================================================ /** * 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.core.operation; import java.util.List; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SOperation; /** * @author Zhang Bole * @author Elias Ricken de Medeiros * @author Baptiste Mesta * @author Matthieu Chaffotte * @since 6.0 */ public interface OperationService { /** * Execute the given operation in the given context and update data that are in the given data container * * @param operation * the operation to execute * @param dataContainerId * the id of the data container (used for left operand) * @param dataContainerType * the type of the data container (used for left operand) * @param expressionContext * the context in which execute the operation * @throws SOperationExecutionException */ void execute(SOperation operation, long dataContainerId, String dataContainerType, SExpressionContext expressionContext) throws SOperationExecutionException; /** * Execute the given operation in the given context and update data that are in the given data container * * @param operations * the operations to execute * @param leftOperandContainerId * the id of the container (used for left operand) * @param leftOperandContainerType * the type of the container (used for left operand) * @param expressionContext * the context in which execute the operation * @throws SOperationExecutionException */ void execute(List operations, long leftOperandContainerId, final String leftOperandContainerType, SExpressionContext expressionContext) throws SOperationExecutionException; /** * Execute the given operation in the given context and update data that are in the same context * * @param operations * @param expressionContext * @throws SOperationExecutionException */ void execute(List operations, SExpressionContext expressionContext) throws SOperationExecutionException; } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/exception/SOperationExecutionException.java ================================================ /** * 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.core.operation.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Zhang Bole */ public class SOperationExecutionException extends SBonitaException { private static final long serialVersionUID = 4480803850726634142L; public SOperationExecutionException(final String message, final Throwable cause) { super(message, cause); } public SOperationExecutionException(final String message) { super(message); } public SOperationExecutionException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/AssignmentOperationExecutorStrategy.java ================================================ /** * 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.core.operation.impl; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.OperationExecutorStrategy; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.SOperatorType; import org.springframework.stereotype.Component; /** * AssignmentOperationExecutorStrategy is the default Bonita strategy to execute data assignment operations * * @author Zhang Bole * @author Elias Ricken de Medeiros * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Component public class AssignmentOperationExecutorStrategy implements OperationExecutorStrategy { /** * Builds a new AssignmentOperationExecutorStrategy, which is the strategy to execute data assignment operations */ public AssignmentOperationExecutorStrategy() { } @Override public Object computeNewValueForLeftOperand(final SOperation operation, final Object value, final SExpressionContext expressionContext, final boolean shouldPersistValue) throws SOperationExecutionException { // do not check if value is external, see ENGINE-1739 if (operation.getLeftOperand().getType().equals(SLeftOperand.TYPE_DATA)) { checkReturnType(value, operation, expressionContext); } // no processing on the value, just return it return value; } private void checkReturnType(final Object value, final SOperation operation, final SExpressionContext expressionContext) throws SOperationExecutionException { if (value != null) { final String name = operation.getLeftOperand().getName(); final Object object = expressionContext.getInputValues().get(name); /* * if the object is null (data is not initialized) the return type is not checked * but the data instance service should throw an exception */ if (object != null) { final Class dataEffectiveType = object.getClass(); final Class evaluatedReturnedType = value.getClass(); if (!(dataEffectiveType.isAssignableFrom(evaluatedReturnedType) || dataEffectiveType.equals(evaluatedReturnedType))) { throw new SOperationExecutionException( "Incompatible assignment operation type: Left operand " + dataEffectiveType + " is not compatible with right operand " + evaluatedReturnedType + " for expression with name '" + expressionContext + "'"); } } } } @Override public String getOperationType() { return SOperatorType.ASSIGNMENT.name(); } @Override public boolean shouldPersistOnNullValue() { return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/ExternalDataLeftOperandHandler.java ================================================ /** * 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.core.operation.impl; import java.util.List; import java.util.Map; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.LeftOperandHandler; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Component public class ExternalDataLeftOperandHandler implements LeftOperandHandler { @Override public String getType() { return "EXTERNAL_DATA"; } @Override public Object update(final SLeftOperand leftOperand, Map inputValues, final Object newValue, final long containerId, final String containerType) { // nothing to do, the value is already changed in the context return newValue; } @Override public void delete(final SLeftOperand leftOperand, final long containerId, final String containerType) throws SOperationExecutionException { throw new SOperationExecutionException("Deleting an external data is not supported"); } @Override public void loadLeftOperandInContext(final SLeftOperand sLeftOperand, final long leftOperandContainerId, final String leftOperandContainerType, final SExpressionContext expressionContext) { String name = sLeftOperand.getName(); if (!expressionContext.getInputValues().containsKey(name)) { expressionContext.getInputValues().put(name, expressionContext.getInputValues().get(name)); } } @Override public void loadLeftOperandInContext(final List sLeftOperand, final long leftOperandContainerId, final String leftOperandContainerType, final SExpressionContext expressionContext) throws SBonitaReadException { for (SLeftOperand leftOperand : sLeftOperand) { loadLeftOperandInContext(leftOperand, leftOperandContainerId, leftOperandContainerType, expressionContext); } } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/JavaMethodOperationExecutorStrategy.java ================================================ /** * 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.core.operation.impl; import org.bonitasoft.engine.commons.JavaMethodInvoker; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.OperationExecutorStrategy; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SOperation; /** * This stategy is not used directly, * BusinessDataJavaMethodOperationExecutorStrategy is first executed and then delegate to that one. */ public abstract class JavaMethodOperationExecutorStrategy implements OperationExecutorStrategy { public static final String TYPE_JAVA_METHOD = "JAVA_METHOD"; public JavaMethodOperationExecutorStrategy() { } @Override public Object computeNewValueForLeftOperand(final SOperation operation, final Object valueToSetObjectWith, final SExpressionContext expressionContext, final boolean shouldPersistValue) throws SOperationExecutionException { final Object objectToInvokeJavaMethodOn; objectToInvokeJavaMethodOn = extractObjectToInvokeFromContext(operation, expressionContext); final String methodName = extractMethodName(operation); final String operatorType = extractParameterType(operation); try { return new JavaMethodInvoker().invokeJavaMethod(operation.getRightOperand().getReturnType(), valueToSetObjectWith, objectToInvokeJavaMethodOn, methodName, operatorType); } catch (final Exception e) { throw new SOperationExecutionException("Unable to evaluate operation " + operation, e); } } protected String extractParameterType(final SOperation operation) { final String[] split = operation.getOperator().split(":", 2); if (split.length > 1) { return split[1]; } return null; } protected String extractMethodName(final SOperation operation) { final String[] split = operation.getOperator().split(":", 2); return split[0]; } protected Object extractObjectToInvokeFromContext(final SOperation operation, final SExpressionContext expressionContext) throws SOperationExecutionException { final Object objectToInvokeJavaMethodOn; final String dataToSet = operation.getLeftOperand().getName(); objectToInvokeJavaMethodOn = expressionContext.getInputValues().get(dataToSet); if (objectToInvokeJavaMethodOn == null) { throw new SOperationExecutionException("data " + dataToSet + " does not exist"); } return objectToInvokeJavaMethodOn; } @Override public String getOperationType() { return TYPE_JAVA_METHOD; } @Override public boolean shouldPersistOnNullValue() { return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/LeftOperandIndexes.java ================================================ /** * 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.core.operation.impl; /** * @author Elias Ricken de Medeiros */ public class LeftOperandIndexes { private int lastIndex = -1; private int nextIndex = -1; public int getLastIndex() { return lastIndex; } public void setLastIndex(final int lastIndex) { this.lastIndex = lastIndex; } public int getNextIndex() { return nextIndex; } public void setNextIndex(final int nextIndex) { this.nextIndex = nextIndex; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/LeftOperandUpdateStatus.java ================================================ /** * 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.core.operation.impl; import org.bonitasoft.engine.core.operation.model.SOperatorType; /** * @author Elias Ricken de Medeiros */ public class LeftOperandUpdateStatus { private boolean shouldUpdate; public LeftOperandUpdateStatus(SOperatorType operatorType) { shouldUpdate = operatorType != SOperatorType.DELETION; } public boolean shouldUpdate() { return shouldUpdate; } public boolean shouldDelete() { return !shouldUpdate; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/OperationServiceImpl.java ================================================ /** * 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.core.operation.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.LeftOperandHandler; import org.bonitasoft.engine.core.operation.OperationExecutorStrategy; import org.bonitasoft.engine.core.operation.OperationExecutorStrategyProvider; import org.bonitasoft.engine.core.operation.OperationService; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.SOperatorType; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.springframework.stereotype.Service; /** * @author Zhang Bole * @author Elias Ricken de Medeiros * @author Celine Souchet * @author Colin Puy * @author Matthieu Chaffotte */ @Service("operationService") @Slf4j public class OperationServiceImpl implements OperationService { private final Map leftOperandHandlersMap; private final ExpressionResolverService expressionResolverService; private final PersistRightOperandResolver persistRightOperandResolver; private final OperationExecutorStrategyProvider operationExecutorStrategyProvider; public OperationServiceImpl(OperationExecutorStrategyProvider operationExecutorStrategyProvider, List leftOperandHandlers, ExpressionResolverService expressionResolverService, PersistRightOperandResolver persistRightOperandResolver) { super(); this.operationExecutorStrategyProvider = operationExecutorStrategyProvider; this.expressionResolverService = expressionResolverService; this.persistRightOperandResolver = persistRightOperandResolver; leftOperandHandlersMap = new HashMap<>(leftOperandHandlers.size()); for (final LeftOperandHandler leftOperandHandler : leftOperandHandlers) { leftOperandHandlersMap.put(leftOperandHandler.getType(), leftOperandHandler); } } @Override public void execute(final SOperation operation, final long containerId, final String containerType, final SExpressionContext expressionContext) throws SOperationExecutionException { execute(Arrays.asList(operation), containerId, containerType, expressionContext); } @Override public void execute(final List operations, final SExpressionContext expressionContext) throws SOperationExecutionException { execute(operations, expressionContext.getContainerId(), expressionContext.getContainerType(), expressionContext); } @Override public void execute(final List operations, final long leftOperandContainerId, final String leftOperandContainerType, final SExpressionContext expressionContext) throws SOperationExecutionException { if (operations.isEmpty()) { return; } // retrieve all left operand to set and put it in context retrieveLeftOperandsAndPutItInExpressionContextIfNotIn(operations, leftOperandContainerId, leftOperandContainerType, expressionContext); // execute operation and put it in context again final Map leftOperandUpdates = executeOperators(operations, expressionContext); // update data updateLeftOperands(leftOperandUpdates, leftOperandContainerId, leftOperandContainerType, expressionContext); } Map executeOperators(final List operations, final SExpressionContext expressionContext) throws SOperationExecutionException { final Map leftOperandsToUpdate = new HashMap<>(); for (int i = 0; i < operations.size(); i++) { final SOperation currentOperation = operations.get(i); final boolean shouldPersistValue = persistRightOperandResolver.shouldPersistByPosition(i, operations); final LeftOperandUpdateStatus currentUpdateStatus = calculateRightOperandValue(currentOperation, expressionContext, shouldPersistValue); if (shouldUpdateLeftOperandContext(leftOperandsToUpdate, currentOperation.getLeftOperand(), currentUpdateStatus)) { leftOperandsToUpdate.put(currentOperation.getLeftOperand(), currentUpdateStatus); } } return leftOperandsToUpdate; } private LeftOperandUpdateStatus calculateRightOperandValue(final SOperation operation, final SExpressionContext expressionContext, boolean shouldPersistValue) throws SOperationExecutionException { final SLeftOperand leftOperand = operation.getLeftOperand(); final LeftOperandUpdateStatus currentUpdateStatus = new LeftOperandUpdateStatus(operation.getType()); if (currentUpdateStatus.shouldUpdate()) { final OperationExecutorStrategy operationExecutorStrategy = operationExecutorStrategyProvider .getOperationExecutorStrategy(operation); final Object rightOperandValue = evaluateRightOperandExpression(operation, expressionContext, operation.getRightOperand()); shouldPersistValue = persistRightOperandResolver .shouldPersistByValue(rightOperandValue, shouldPersistValue, operationExecutorStrategy.shouldPersistOnNullValue()); final Object value = operationExecutorStrategy.computeNewValueForLeftOperand(operation, rightOperandValue, expressionContext, shouldPersistValue); expressionContext.getInputValues().put(leftOperand.getName(), value); if (log.isDebugEnabled()) { log.debug(buildLogMessage(operation, rightOperandValue, expressionContext)); } } return currentUpdateStatus; } boolean shouldUpdateLeftOperandContext(final Map updateLeftOperands, final SLeftOperand leftOperand, final LeftOperandUpdateStatus currentUpdateStatus) { final LeftOperandUpdateStatus previousStatus = updateLeftOperands.get(leftOperand); return previousStatus == null || !previousStatus.shouldDelete() && currentUpdateStatus.shouldDelete(); } void updateLeftOperands(final Map leftOperandUpdates, final long leftOperandContainerId, final String leftOperandContainerType, final SExpressionContext expressionContext) throws SOperationExecutionException { for (final Entry update : leftOperandUpdates.entrySet()) { final SLeftOperand leftOperand = update.getKey(); final LeftOperandHandler leftOperandHandler = getLeftOperandHandler(leftOperand.getType()); if (update.getValue().shouldUpdate()) { leftOperandHandler.update(leftOperand, expressionContext.getInputValues(), expressionContext.getInputValues().get(leftOperand.getName()), leftOperandContainerId, leftOperandContainerType); } else { leftOperandHandler.delete(leftOperand, leftOperandContainerId, leftOperandContainerType); } } } private LeftOperandHandler getLeftOperandHandler(final String type) throws SOperationExecutionException { final LeftOperandHandler leftOperandHandler = leftOperandHandlersMap.get(type); if (leftOperandHandler == null) { throw new SOperationExecutionException("Left operand type not found: " + type); } return leftOperandHandler; } void retrieveLeftOperandsAndPutItInExpressionContextIfNotIn(final List operations, final long leftOperandContainerId, final String leftOperandContainerType, final SExpressionContext expressionContext) throws SOperationExecutionException { //if the container where we execute the operation is not the same than the container of the expression (call activity data mapping) we skip the loading of left operand final String containerType = expressionContext.getContainerType(); final Long containerId = expressionContext.getContainerId(); if (containerId == null || containerType == null) { return; } final HashMap> leftOperandHashMap = new HashMap<>(); for (final SOperation operation : operations) { if (!operation.getType().equals(SOperatorType.ASSIGNMENT)) { // this operation will set a data, we retrieve it and put it in context final SLeftOperand leftOperand = operation.getLeftOperand(); if (!leftOperandHashMap.containsKey(leftOperand.getType())) { leftOperandHashMap.put(leftOperand.getType(), new ArrayList<>()); } leftOperandHashMap.get(leftOperand.getType()).add(leftOperand); } } for (final Entry> leftOperandByType : leftOperandHashMap.entrySet()) { try { getLeftOperandHandler(leftOperandByType.getKey()) .loadLeftOperandInContext(leftOperandByType.getValue(), leftOperandContainerId, leftOperandContainerType, expressionContext); } catch (final SBonitaReadException e) { throw new SOperationExecutionException( "Unable to retrieve value for operation " + leftOperandByType.getValue(), e); } } } protected Object evaluateRightOperandExpression(final SOperation operation, final SExpressionContext expressionContext, final SExpression sExpression) throws SOperationExecutionException { if (sExpression == null) { return null; } try { return expressionResolverService.evaluate(sExpression, expressionContext); } catch (final ClassCastException e) { throw new SOperationExecutionException( "Unable to execute operation on " + operation.getLeftOperand().getName() + " with a new value which is not Serializable", e); } catch (final SBonitaException e) { throw new SOperationExecutionException(e); } } private String buildLogMessage(final SOperation operation, final Object operationValue, final SExpressionContext expressionContext) { String stb = "Executed operation on container [id: '" + expressionContext.getContainerId() + "', type: '" + expressionContext.getContainerType() + "']. Operation: [left operand: '" + operation.getLeftOperand().getName() + "', operator: '" + operation.getOperator() + "', operation value: '" + operationValue + "']"; return stb; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/OperationsAnalyzer.java ================================================ /** * 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.core.operation.impl; import java.util.Iterator; import java.util.List; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.model.SExpression; import org.springframework.stereotype.Component; /** * @author Elias Ricken de Medeiros */ @Component public class OperationsAnalyzer { /** * Finds the index of operation of type {@link SLeftOperand#TYPE_BUSINESS_DATA} that references the expression of * type * {@link ExpressionType#TYPE_BUSINESS_DATA} with given name directly on the right operand or in transitive * dependencies. * * @param businessDataName the expression content * @param fromIndex index of operation from which the analyse must begins * @param operations list of operations @return the index of operation that references the expression with given * name and type. */ public int findBusinessDataDependencyIndex(String businessDataName, int fromIndex, final List operations) { if (fromIndex >= operations.size()) { return -1; } for (int i = fromIndex; i < operations.size(); i++) { SOperation operation = operations.get(i); SExpression rightOperand = operation.getRightOperand(); if (matches(businessDataName, operation, rightOperand)) { return i; } } return -1; } private boolean matches(final String businessDataName, final SOperation operation, final SExpression expression) { if (expression == null) { return false; } boolean matches = SLeftOperand.TYPE_BUSINESS_DATA.equals(operation.getLeftOperand().getType()) && businessDataName.equals(expression.getContent()) && ExpressionType.TYPE_BUSINESS_DATA.name().equals(expression.getExpressionType()); if (!matches && expression.hasDependencies()) { Iterator iterator = expression.getDependencies().iterator(); while (iterator.hasNext() && !matches) { matches = matches(businessDataName, operation, iterator.next()); } } return matches; } /** * Calculates the next and the last indexes where the left operand located at the given index is used * * @param indexOfCurrentOperation index of current operation * @param operations all operations * @return the next and the last indexes where the left operand located at the given index is used */ public LeftOperandIndexes calculateIndexes(int indexOfCurrentOperation, List operations) { LeftOperandIndexes indexes = new LeftOperandIndexes(); indexes.setLastIndex(indexOfCurrentOperation); SOperation currentOperation = operations.get(indexOfCurrentOperation); //start from operation that follows the current one for (int i = indexOfCurrentOperation + 1; i < operations.size(); i++) { SOperation operation = operations.get(i); if (operation.getLeftOperand().equals(currentOperation.getLeftOperand())) { indexes.setLastIndex(i); if (indexes.getNextIndex() == -1) { indexes.setNextIndex(i); } } } return indexes; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/PersistRightOperandResolver.java ================================================ /** * 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.core.operation.impl; import java.util.List; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.springframework.stereotype.Component; /** * @author Elias Ricken de Medeiros */ @Component public class PersistRightOperandResolver { private OperationsAnalyzer operationsAnalyzer; public PersistRightOperandResolver(final OperationsAnalyzer operationsAnalyzer) { this.operationsAnalyzer = operationsAnalyzer; } /** * Check if the right operand should be persisted based on its position (it's the last operation for the given left * operand) * * @param currentIndex the index of current operation * @param operations the list of all operations * @return true if the right operand should be persisted; false otherwise */ public boolean shouldPersistByPosition(int currentIndex, List operations) { SOperation currentOperation = operations.get(currentIndex); if (!SLeftOperand.TYPE_BUSINESS_DATA.equals(currentOperation.getLeftOperand().getType())) { return false; } LeftOperandIndexes leftOperandIndexes = operationsAnalyzer.calculateIndexes(currentIndex, operations); if (currentIndex == leftOperandIndexes.getLastIndex()) { return true; } //get the index of next operation that will use the current left operand as dependency to set another business data int dependencyIndex = operationsAnalyzer.findBusinessDataDependencyIndex( currentOperation.getLeftOperand().getName(), currentIndex + 1, operations); return dependencyIndex != -1 && dependencyIndex < leftOperandIndexes.getNextIndex(); } /** * Check if the right operand should be persisted based of the right operand value * * @param rightOperandValue the right operand value * @param shouldPersistByPosition should persist based on operation position (position in the list of operations) * @param shouldPersistOnNull should persist on null result * @return true if the right operand should be persisted; false otherwise */ public boolean shouldPersistByValue(Object rightOperandValue, boolean shouldPersistByPosition, final boolean shouldPersistOnNull) { if (rightOperandValue == null && shouldPersistByPosition) { return shouldPersistOnNull; } return shouldPersistByPosition; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/impl/XpathUpdateQueryOperationExecutorStrategy.java ================================================ /** * 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.core.operation.impl; import java.io.IOException; import java.io.StringReader; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.OperationExecutorStrategy; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.xml.DocumentManager; import org.springframework.stereotype.Component; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.Text; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * @author Zhang Bole * @author Matthieu Chaffotte * @author Baptiste Mesta */ @Component public class XpathUpdateQueryOperationExecutorStrategy implements OperationExecutorStrategy { public static final String TYPE_XPATH_UPDATE_QUERY = "XPATH_UPDATE_QUERY"; public XpathUpdateQueryOperationExecutorStrategy() { } @Override public String getOperationType() { return TYPE_XPATH_UPDATE_QUERY; } private String getStringValue(final Object variableValue) { if (variableValue instanceof String) { return (String) variableValue; } return String.valueOf(variableValue); } private boolean isSetAttribute(final String xpathExpression, final Object variableValue) { if (variableValue instanceof Attr) { return true; } final String[] segments = xpathExpression.split("/"); return segments[segments.length - 1].startsWith("@"); } @Override public Object computeNewValueForLeftOperand(final SOperation operation, final Object value, final SExpressionContext expressionContext, final boolean shouldPersistValue) throws SOperationExecutionException { try { final String dataInstanceName = operation.getLeftOperand().getName(); // should be a String because the data is an xml expression final String dataValue = (String) expressionContext.getInputValues().get(dataInstanceName); final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // security-compliant factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // security-compliant } catch (IllegalArgumentException e) { //ignored, if not supported by the implementation } final DocumentBuilder builder = factory.newDocumentBuilder(); final Document document = builder.parse(new InputSource(new StringReader(dataValue))); final XPath xpath = XPathFactory.newInstance().newXPath(); final String xpathExpression = operation.getOperator(); final Node node = (Node) xpath.compile(xpathExpression).evaluate(document, XPathConstants.NODE); if (isSetAttribute(xpathExpression, value)) { if (node == null) { // Create the attribute final String parentPath = xpathExpression.substring(0, xpathExpression.lastIndexOf('/')); final String attributeName = xpathExpression.substring(xpathExpression.lastIndexOf('/') + 2); // +1 for @ final Node parentNode = (Node) xpath.compile(parentPath).evaluate(document, XPathConstants.NODE); if (parentNode instanceof Element element) { if (value instanceof String) { element.setAttribute(attributeName, getStringValue(value)); } else if (value instanceof Attr) { element.setAttribute(((Attr) value).getName(), ((Attr) value).getTextContent()); } } } else if (node instanceof Attr) { // Set an existing attribute if (value instanceof Attr) { node.setTextContent(((Attr) value).getTextContent()); } else { node.setTextContent(getStringValue(value)); } } else if (node instanceof Element) { // add attribute to an element final Attr attr = (Attr) value; ((Element) node).setAttribute(attr.getName(), attr.getValue()); } } else if (node instanceof Text) { node.setTextContent(getStringValue(value)); } else if (node instanceof Element) { Node newNode = null; if (value instanceof Node) { newNode = document.importNode((Node) value, true); } else if (value instanceof String) { newNode = document.importNode( DocumentManager.generateDocument(getStringValue(value)).getDocumentElement(), true); } // if (isAppend) { // node.appendChild(newNode); // } else { // replace final Node parentNode = node.getParentNode(); parentNode.removeChild(node); parentNode.appendChild(newNode); // } } else if (node == null && xpathExpression.endsWith("/text()") && value instanceof String) { final String parentPath = xpathExpression.substring(0, xpathExpression.lastIndexOf('/')); final Node parentNode = (Node) xpath.compile(parentPath).evaluate(document, XPathConstants.NODE); parentNode.appendChild(document.createTextNode(getStringValue(value))); } return DocumentManager.getDocumentContent(document); } catch (final ParserConfigurationException | SAXException | IOException | XPathExpressionException | TransformerFactoryConfigurationError | TransformerException pce) { throw new SOperationExecutionException(pce); } } @Override public boolean shouldPersistOnNullValue() { return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/SLeftOperand.java ================================================ /** * 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.core.operation.model; import java.io.Serializable; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SLeftOperand extends Serializable { String TYPE_EXTERNAL_DATA = "EXTERNAL_DATA"; String TYPE_DOCUMENT = "DOCUMENT"; String TYPE_SEARCH_INDEX = "SEARCH_INDEX"; String TYPE_DATA = "DATA"; String TYPE_BUSINESS_DATA = "BUSINESS_DATA"; String getName(); String getType(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/SOperation.java ================================================ /** * 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.core.operation.model; import java.io.Serializable; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta */ public interface SOperation extends Serializable { SLeftOperand getLeftOperand(); SOperatorType getType(); String getOperator(); SExpression getRightOperand(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/SOperatorType.java ================================================ /** * 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.core.operation.model; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Emmanuel Duchastenier */ public enum SOperatorType { ASSIGNMENT, JAVA_METHOD, XPATH_UPDATE_QUERY, DELETION } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/SLeftOperandBuilder.java ================================================ /** * 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.core.operation.model.builder; import org.bonitasoft.engine.core.operation.model.SLeftOperand; /** * @author Zhang Bole * @author Baptiste Mesta */ public interface SLeftOperandBuilder { SLeftOperandBuilder setName(final String dataName); SLeftOperandBuilder setType(final String type); SLeftOperand done(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/SLeftOperandBuilderFactory.java ================================================ /** * 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.core.operation.model.builder; /** * @author Zhang Bole */ public interface SLeftOperandBuilderFactory { SLeftOperandBuilder createNewInstance(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/SOperationBuilder.java ================================================ /** * 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.core.operation.model.builder; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.SOperatorType; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Zhang Bole * @author Baptiste Mesta */ public interface SOperationBuilder { SOperationBuilder setLeftOperand(final SLeftOperand leftOperand); SOperationBuilder setType(final SOperatorType operatorType); SOperationBuilder setOperator(final String operator); SOperationBuilder setRightOperand(final SExpression rightOperand); SOperation done(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/SOperationBuilderFactory.java ================================================ /** * 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.core.operation.model.builder; /** * @author Zhang Bole * @author Baptiste Mesta */ public interface SOperationBuilderFactory { SOperationBuilder createNewInstance(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/impl/SLeftOperandBuilderFactoryImpl.java ================================================ /** * 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.core.operation.model.builder.impl; import org.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilder; import org.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilderFactory; import org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta */ public class SLeftOperandBuilderFactoryImpl implements SLeftOperandBuilderFactory { @Override public SLeftOperandBuilder createNewInstance() { final SLeftOperandImpl leftOperand = new SLeftOperandImpl(); return new SLeftOperandBuilderImpl(leftOperand); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/impl/SLeftOperandBuilderImpl.java ================================================ /** * 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.core.operation.model.builder.impl; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilder; import org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta */ public class SLeftOperandBuilderImpl implements SLeftOperandBuilder { private final SLeftOperandImpl leftOperand; public SLeftOperandBuilderImpl(final SLeftOperandImpl leftOperand) { super(); this.leftOperand = leftOperand; } @Override public SLeftOperandBuilder setName(final String name) { leftOperand.setName(name); return this; } @Override public SLeftOperand done() { return leftOperand; } @Override public SLeftOperandBuilder setType(final String type) { leftOperand.setType(type); return this; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/impl/SOperationBuilderFactoryImpl.java ================================================ /** * 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.core.operation.model.builder.impl; import org.bonitasoft.engine.core.operation.model.builder.SOperationBuilder; import org.bonitasoft.engine.core.operation.model.builder.SOperationBuilderFactory; import org.bonitasoft.engine.core.operation.model.impl.SOperationImpl; /** * @author Zhang Bole */ public class SOperationBuilderFactoryImpl implements SOperationBuilderFactory { @Override public SOperationBuilder createNewInstance() { final SOperationImpl operation = new SOperationImpl(); return new SOperationBuilderImpl(operation); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/builder/impl/SOperationBuilderImpl.java ================================================ /** * 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.core.operation.model.builder.impl; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.SOperatorType; import org.bonitasoft.engine.core.operation.model.builder.SOperationBuilder; import org.bonitasoft.engine.core.operation.model.impl.SOperationImpl; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Zhang Bole */ public class SOperationBuilderImpl implements SOperationBuilder { private final SOperationImpl operation; public SOperationBuilderImpl(final SOperationImpl operation) { super(); this.operation = operation; } @Override public SOperationBuilder setLeftOperand(final SLeftOperand leftOperand) { operation.setLeftOperand(leftOperand); return this; } @Override public SOperationBuilder setType(final SOperatorType operatorType) { operation.setType(operatorType); return this; } @Override public SOperationBuilder setOperator(final String operator) { operation.setOperator(operator); return this; } @Override public SOperationBuilder setRightOperand(final SExpression rightOperand) { operation.setRightOperand(rightOperand); return this; } @Override public SOperation done() { return operation; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/impl/SLeftOperandImpl.java ================================================ /** * 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.core.operation.model.impl; import org.bonitasoft.engine.core.operation.model.SLeftOperand; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta */ public class SLeftOperandImpl implements SLeftOperand { private static final long serialVersionUID = 1L; private String name; private String type; public SLeftOperandImpl() { type = SLeftOperand.TYPE_DATA; } @Override public String getType() { return type; } public void setType(final String type) { this.type = type; } public void setName(final String name) { this.name = name; } @Override public String getName() { return name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (name == null ? 0 : name.hashCode()); result = prime * result + (type == null ? 0 : type.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } SLeftOperandImpl other = (SLeftOperandImpl) obj; if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } if (type == null) { if (other.type != null) { return false; } } else if (!type.equals(other.type)) { return false; } return true; } @Override public String toString() { return "SLeftOperandImpl [name=" + name + ", type=" + type + "]"; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/operation/model/impl/SOperationImpl.java ================================================ /** * 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.core.operation.model.impl; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.SOperatorType; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta */ public class SOperationImpl implements SOperation { private static final long serialVersionUID = 1L; private SLeftOperand leftOperand; private SOperatorType type; private String operator; private SExpression rightOperand; public void setLeftOperand(final SLeftOperand leftOperand) { this.leftOperand = leftOperand; } public void setOperator(final String operator) { this.operator = operator; } public void setType(final SOperatorType type) { this.type = type; } public void setRightOperand(final SExpression rightOperand) { this.rightOperand = rightOperand; } @Override public SLeftOperand getLeftOperand() { return leftOperand; } @Override public SOperatorType getType() { return type; } @Override public String getOperator() { return operator; } @Override public SExpression getRightOperand() { return rightOperand; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (leftOperand == null ? 0 : leftOperand.hashCode()); result = prime * result + (operator == null ? 0 : operator.hashCode()); result = prime * result + (rightOperand == null ? 0 : rightOperand.hashCode()); result = prime * result + (type == null ? 0 : type.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final SOperationImpl other = (SOperationImpl) obj; if (leftOperand == null) { if (other.leftOperand != null) { return false; } } else if (!leftOperand.equals(other.leftOperand)) { return false; } if (operator == null) { if (other.operator != null) { return false; } } else if (!operator.equals(other.operator)) { return false; } if (rightOperand == null) { if (other.rightOperand != null) { return false; } } else if (!rightOperand.equals(other.rightOperand)) { return false; } if (type != other.type) { return false; } return true; } @Override public String toString() { return "SOperationImpl [leftOperand=" + leftOperand + ", type=" + type + ", operator=" + operator + ", rightOperand=" + rightOperand + "]"; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/ProcessDefinitionService.java ================================================ /** * 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.core.process.definition; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.core.process.definition.exception.SDeletingEnabledProcessException; 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.exception.SProcessDeletionException; import org.bonitasoft.engine.core.process.definition.exception.SProcessDeploymentInfoUpdateException; import org.bonitasoft.engine.core.process.definition.exception.SProcessDisablementException; import org.bonitasoft.engine.core.process.definition.exception.SProcessEnablementException; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Matthieu Chaffotte * @author Yanyan Liu * @author Celine Souchet * @author Arthur Freycon * @since 6.0 */ public interface ProcessDefinitionService { String PROCESSDEFINITION = "PROCESSDEFINITION"; String PROCESSDEFINITION_CONTENT = "PROCESSDEFINITION_CONTENT"; String PROCESSDEFINITION_IS_ENABLED = "PROCESSDEFINITION_IS_ENABLED"; String PROCESSDEFINITION_IS_DISABLED = "PROCESSDEFINITION_IS_DISABLED"; String PROCESSDEFINITION_DEPLOY_INFO = "PROCESSDEFINITION_DEPLOY_INFO"; String PROCESSDEFINITION_IS_RESOLVED = "PROCESSDEFINITION_IS_RESOLVED"; String PROCESSDEFINITION_IS_UNRESOLVED = "PROCESSDEFINITION_IS_UNRESOLVED"; static String PROCESS_CACHE_NAME = "_PROCESSDEF"; String UNCATEGORIZED_SUFFIX = "Uncategorized"; String UNCATEGORIZED_SUPERVISED_BY_SUFFIX = "UncategorizedAndWithSupervisor"; String UNCATEGORIZED_USERCANSTART_SUFFIX = "UncategorizedUserCanStart"; String WHOCANSTART_PROCESS_SUFFIX = "WhoCanStartProcess"; String STARTED_BY_SUFFIX = "StartedBy"; String PROCESS_DEFINITION_ID = "processId"; String USER_ID = "userId"; String ROLE_ID = "roleId"; String GROUP_ID = "groupId"; /** * Store the processDefinition to file system and its deploy info to DB. * * @param designProcessDefinition * the processDefinition will be stored * @return the definition will an id * @throws SProcessDefinitionException */ SProcessDefinition store(DesignProcessDefinition designProcessDefinition) throws SProcessDefinitionException; /** * Get processDefinition by its id * * @param processDefinitionId * identifier of processDefinition * @return the processDefinition corresponding to the parameter processId * @throws SProcessDefinitionNotFoundException * error thrown if no process definition found * @throws SBonitaReadException */ SProcessDefinition getProcessDefinition(long processDefinitionId) throws SProcessDefinitionNotFoundException, SBonitaReadException; /** * Get processDefinition by its id, if it is enabled. Throws SProcessDefinitionException otherwise. * * @param processDefinitionId * The identifier of processDefinition * @return The processDefinition corresponding to the parameter processId * @throws SBonitaReadException * @throws SProcessDefinitionException if process is not enabled. * @since 6.4.0 */ SProcessDefinition getProcessDefinitionIfIsEnabled(long processDefinitionId) throws SBonitaReadException, SProcessDefinitionException; /** * Get deployment info of the process definition having the id given in parameter * * @param processId * id of the process definition on which we want deployment information * @return an SProcessDefinitionDeployInfo object to the process definition * @throws SProcessDefinitionNotFoundException * error thrown if no process definition found * @throws SBonitaReadException */ SProcessDefinitionDeployInfo getProcessDeploymentInfo(long processId) throws SProcessDefinitionNotFoundException, SBonitaReadException; /** * Delete the id specified process definition and its deploy info * * @param processId * identifier of processDefinition * @throws SProcessDefinitionNotFoundException * error thrown if no process definition found * @throws SProcessDeletionException * @throws SDeletingEnabledProcessException * error throw if the process still enabled */ void delete(long processId) throws SProcessDefinitionNotFoundException, SProcessDeletionException, SDeletingEnabledProcessException; /** * Get process definition deploy info in a specific interval with order, this can be used for pagination * * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberPerPage * Number of result we want to get. Maximum number of result returned * @param field * the field user to do order * @param order * ASC or DESC * @return a list of SProcessDefinitionDeployInfo object * @throws SBonitaReadException */ List getProcessDeploymentInfos(int fromIndex, int numberPerPage, String field, OrderByType order) throws SBonitaReadException; /** * Enable the specific process definition, set the process as ENABLED when it is in RESOLVED state. * If process is already enabled, this method fails with SProcessEnablementException * * @param processId * identifier of processDefinition * @throws SProcessDefinitionNotFoundException * error thrown if no process definition found for the given processId * @throws SProcessEnablementException if process is already enabled or if process is not resolved * @see #resolveProcess(long) Resolving a process * @see #enableProcess(long, boolean) */ void enableProcessDeploymentInfo(long processId) throws SProcessDefinitionNotFoundException, SProcessEnablementException; /** * Enable the specific process definition, when it is in RESOLVED state. * * @param processId * identifier of processDefinition * @param failIfAlreadyEnabled should we fail if process is already enabled? * @throws SProcessDefinitionNotFoundException * error thrown if no process definition found for the given processId * @throws SProcessEnablementException if process is already enabled and failIfAlreadyEnabled == true, or if process * is not resolved */ void enableProcess(long processId, boolean failIfAlreadyEnabled) throws SProcessDefinitionNotFoundException, SProcessEnablementException; /** * Disable the process passed as parameter. * If process is already disabled, this method fails with SProcessDisablementException. * * @param processId * identifier of process definition * @throws SProcessDefinitionNotFoundException * error thrown if no process definition found for the given processId * @throws SProcessDisablementException if process is already disabled * @see #disableProcess(long, boolean) */ void disableProcessDeploymentInfo(long processId) throws SProcessDefinitionNotFoundException, SProcessDisablementException; /** * Disable the process passed as parameter. * * @param processId * identifier of process definition * @param failIfAlreadyDisabled should we fail if process is already disabled? * @throws SProcessDefinitionNotFoundException * error thrown if no process definition found for the given processId * @throws SProcessDisablementException if process is already disabled and failIfAlreadyDisabled == true */ void disableProcess(long processId, boolean failIfAlreadyDisabled) throws SProcessDefinitionNotFoundException, SProcessDisablementException; /** * set the process as RESOLVED when it is in UNRESOLVED state * * @param processId * identifier of process definition * @throws SProcessDefinitionNotFoundException * error thrown if no process definition found for the given processId * @throws SProcessDisablementException */ void resolveProcess(long processId) throws SProcessDefinitionNotFoundException, SProcessDisablementException; /** * Gets how many processes are in the given state. * * @param activationState * the activation state * @return number of processes are in the given state or 0; * @throws SBonitaReadException */ long getNumberOfProcessDeploymentInfosByActivationState(ActivationState activationState) throws SBonitaReadException; /** * Gets how many processes are defined. * * @return the number of process definitions; * @throws SBonitaReadException * occurs when an exception is thrown during method execution */ long getNumberOfProcessDeploymentInfos() throws SBonitaReadException; /** * Get the process definition identifiers in the given state. * * @param activationState * the activation state * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfResult * Number of result we want to get. Maximum number of result returned * @return the paginated list of process definition identifiers or an empty list * @throws SBonitaReadException */ List getProcessDefinitionIds(ActivationState activationState, int fromIndex, int numberOfResult) throws SBonitaReadException; /** * Get the process definition identifiers. * * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * * @param numberOfResult * Number of result we want to get. Maximum number of result returned * @return the paginated list of process definition identifiers or an empty list * @throws SBonitaReadException */ List getProcessDefinitionIds(int fromIndex, int numberOfResult) throws SBonitaReadException; /** * Get target flow node for the given source flow node in the specific process * * @param definition * the process definition containing source flow node * @param source * a flow node in process definition * @return target flow node of the given source */ SFlowNodeDefinition getNextFlowNode(SProcessDefinition definition, String source); /** * get sub set of processDefinitionDeployInfos in specific order * * @param processIds * identifiers of process definition * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfProcesses * Number of result we want to get. Maximum number of result returned * @param field * filed user to do order * @param order * ASC or DESC * @return a list of SProcessDefinitionDeployInfo objects * @throws SProcessDefinitionNotFoundException * @throws SBonitaReadException */ List getProcessDeploymentInfos(List processIds, int fromIndex, int numberOfProcesses, String field, OrderByType order) throws SProcessDefinitionNotFoundException, SBonitaReadException; List getProcessDeploymentInfos(List processIds) throws SProcessDefinitionNotFoundException, SBonitaReadException; /** * Get the processDefinitionId of the most recent version of the process * * @param processName * name of process definition * @return the latest process definition * @throws SBonitaReadException */ long getLatestProcessDefinitionId(String processName) throws SBonitaReadException, SProcessDefinitionNotFoundException; /** * Get the processDefinitionId by name and version * * @param name * name of process definition * @param version * version or process definition * @return identifier of process definition * @throws SBonitaReadException * @throws SProcessDefinitionNotFoundException */ long getProcessDefinitionId(String name, String version) throws SBonitaReadException, SProcessDefinitionNotFoundException; /** * Update deployment info of the process definition having the id given in parameter * * @param processId * identifier of process deploy info * @param descriptor * update description * @throws SProcessDefinitionNotFoundException * error thrown when no process deploy info found with the give processId * @throws SProcessDeploymentInfoUpdateException */ SProcessDefinitionDeployInfo updateProcessDefinitionDeployInfo(long processId, EntityUpdateDescriptor descriptor) throws SProcessDefinitionNotFoundException, SProcessDeploymentInfoUpdateException; /** * Search all process deploy info started by the specific user * * @param startedBy * the name of user who started the process * @param searchOptions * a QueryOptions object containing some query conditions * @return a list of SProcessDefinitionDeployInfo objects * @throws SBonitaReadException */ List searchProcessDeploymentInfosStartedBy(long startedBy, QueryOptions searchOptions) throws SBonitaReadException; /** * Get number of all process deploy info started by the specific user * * @param startedBy * the name of user who started the process * @param countOptions * a QueryOptions object containing some query conditions * @return number of all process deploy info to the criteria * @throws SBonitaReadException */ long getNumberOfProcessDeploymentInfosStartedBy(long startedBy, QueryOptions countOptions) throws SBonitaReadException; /** * Search all process definition deploy infos according to the specific search criteria * * @param searchOptions * a QueryOptions object containing search criteria * @return a list of SProcessDefinitionDeployInfo object * @throws SBonitaReadException */ List searchProcessDeploymentInfos(QueryOptions searchOptions) throws SBonitaReadException; /** * Get number of all process definition deploy infos according to the specific search criteria * * @param countOptions * a QueryOptions object containing query criteria * @return number of all process definition deploy infos corresponding to the criteria * @throws SBonitaReadException */ long getNumberOfProcessDeploymentInfos(QueryOptions countOptions) throws SBonitaReadException; /** * Get total number of uncategorized process definitions by given query criteria * * @param countOptions * a QueryOptions object containing query criteria * @return total number of uncategorized process definitions suit to query criteria * @throws SBonitaReadException */ long getNumberOfUncategorizedProcessDeploymentInfos(QueryOptions countOptions) throws SBonitaReadException; /** * Get total number of uncategorized process definitions by given query criteria for specific supervisor * * @param userId * identifier of a supervisor user * @param countOptions * a QueryOptions object containing query criteria * @return number of uncategorized process definitions managed by the specific supervisor * @throws SBonitaReadException */ long getNumberOfUncategorizedProcessDeploymentInfosSupervisedBy(long userId, QueryOptions countOptions) throws SBonitaReadException; /** * Search all uncategorized process definitions according to the search criteria. * * @param searchOptions * a QueryOptions object containing query criteria * @return a list of SProcessDefinitionDeployInfo objects * @throws SBonitaReadException */ List searchUncategorizedProcessDeploymentInfos(QueryOptions searchOptions) throws SBonitaReadException; /** * Search all process definitions for a specific category. * * @param categoryId * Identifier of the category * @param queryOptions * a QueryOptions object containing query criteria * @return a list of SProcessDefinitionDeployInfo objects * @throws SBonitaReadException */ List searchProcessDeploymentInfosOfCategory(long categoryId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search all uncategorized process definitions by given query criteria for specific supervisor * * @param userId * identifier of a supervisor user * @param searchOptions * a QueryOptions object containing query criteria * @return a list of SProcessDefinitionDeployInfo object * @throws SBonitaReadException */ List searchUncategorizedProcessDeploymentInfosSupervisedBy(long userId, QueryOptions searchOptions) throws SBonitaReadException; /** * Search all process definitions for the specific user who can start * * @param userId * identifier of user * @param searchOptions * a QueryOptions object containing query criteria * @return a list of SProcessDefinitionDeployInfo objects * @throws SBonitaReadException */ List searchProcessDeploymentInfosCanBeStartedBy(long userId, QueryOptions searchOptions) throws SBonitaReadException; /** * Get number of all process definitions for the specific user who can start * * @param userId * identifier of user * @param countOptions * a QueryOptions object containing query criteria * @return number of all process definitions for the specific user who can start * @throws SBonitaReadException */ long getNumberOfProcessDeploymentInfosCanBeStartedBy(long userId, QueryOptions countOptions) throws SBonitaReadException; /** * Search all process definitions for the users managed by specific manager, or manager who can start * * @param managerUserId * identifier of manager * @param searchOptions * a QueryOptions object containing query criteria * @return a list of SProcessDefinitionDeployInfo objects * @throws SBonitaReadException */ List searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(long managerUserId, QueryOptions searchOptions) throws SBonitaReadException; /** * Get number of all process definitions for the users managed by specific manager, or manager who can start * * @param managerUserId * identifier of manager * @param countOptions * a QueryOptions object containing query criteria * @return Number of all process definitions for the users managed by specific manager, or manager who can start * @throws SBonitaReadException */ long getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedBy(long managerUserId, QueryOptions countOptions) throws SBonitaReadException; /** * Search all process definitions for the specific user who can perform the "querySuffix" specified action * * @param userId * identifier of user * @param searchOptions * a QueryOptions object containing query criteria * @param querySuffix * query suffix to specify the thing the user can do, it can be "UserSupervised" or "UserCanStart" * @return a list of SProcessDefinitionDeployInfo object * @throws SBonitaReadException */ List searchProcessDeploymentInfos(long userId, QueryOptions searchOptions, String querySuffix) throws SBonitaReadException; /** * Get total number of process definitions for the specific user who can perform the "querySuffix" specified action * * @param userId * identifier of user * @param countOptions * a QueryOptions object containing query criteria * @param querySuffix * query suffix to specify the thing the user can do, it can be "UserSupervised" or "UserCanStart" * @return number of process definitions for the specific user with specific action * @throws SBonitaReadException */ long getNumberOfProcessDeploymentInfos(long userId, QueryOptions countOptions, String querySuffix) throws SBonitaReadException; /** * Search all uncategorized process definitions for the specific user who can start * * @param userId * identifier of user * @param searchOptions * a QueryOptions object containing query criteria * @return a list of SProcessDefinitionDeployInfo object * @throws SBonitaReadException */ List searchUncategorizedProcessDeploymentInfosCanBeStartedBy(long userId, QueryOptions searchOptions) throws SBonitaReadException; /** * Get total number of uncategorized process definitions for the specific user who can start * * @param userId * identifier of user * @param countOptions * a QueryOptions object containing query criteria * @return number of uncategorized process definitions for the specific user who can start * @throws SBonitaReadException */ long getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedBy(long userId, QueryOptions countOptions) throws SBonitaReadException; /** * A list of SProcessDefinitionDeployInfos for the specific processInstances * * @param processInstanceIds * identifier of process instances * @return a map containing identifiers of process instance and the corresponding SProcessDefinitionDeployInfo * object * @throws SBonitaReadException */ Map getProcessDeploymentInfosFromProcessInstanceIds( List processInstanceIds) throws SBonitaReadException; /** * Get A list of SProcessDefinitionDeployInfos for the specific archived processInstances * * @param archivedProcessInstantsIds * identifiers of archived processInstance * @return a map containing identifiers of archived process instance and the corresponding * SProcessDefinitionDeployInfo object * @throws SBonitaReadException */ Map getProcessDeploymentInfosFromArchivedProcessInstanceIds( List archivedProcessInstantsIds) throws SBonitaReadException; /** * Get A list of SProcessDefinitionDeployInfos unrelated to the specific category * * @param categoryId * @param pagingCriterion * @param numberPerPage * @param pageIndex * @return A list of SProcessDefinitionDeployInfos unrelated to the specific category * @throws SBonitaReadException */ List getProcessDeploymentInfosUnrelatedToCategory(long categoryId, int pageIndex, int numberPerPage, ProcessDeploymentInfoCriterion pagingCriterion) throws SBonitaReadException; /** * Get number of SProcessDefinitionDeployInfos unrelated to the specific category * * @param categoryId * @return Number of SProcessDefinitionDeployInfos unrelated to the specific category * @throws SBonitaReadException */ Long getNumberOfProcessDeploymentInfosUnrelatedToCategory(long categoryId) throws SBonitaReadException; /** * Get process definition deploy info in a specific interval with order, this can be used for pagination * * @param queryOptions * object containing query criteria * @return a list of SProcessDefinitionDeployInfo corresponding to the criteria * @throws SBonitaReadException */ List getProcessDeploymentInfos(QueryOptions queryOptions) throws SBonitaReadException; /** * List all processes that contain at least one task which actor is mapped only to the specified group. * * @param groupId * the Id of the group from which to retrieve the processes with tasks only it can do. * @param queryOptions * object containing query criteria * @return the list of matching processes, as a List of SProcessDefinitionDeployInfo * @throws SBonitaReadException * in case a read problem occurs */ List getProcessDeploymentInfosWithActorOnlyForGroup(long groupId, QueryOptions queryOptions) throws SBonitaReadException; /** * List all processes that contain at least one task which actor is mapped only to the specified groups. * * @param groupIds * the Ids of the groups from which to retrieve the processes with tasks only they can do. * @param queryOptions * object containing query criteria * @return the list of matching processes, as a List of SProcessDefinitionDeployInfo * @throws SBonitaReadException * in case a read problem occurs */ List getProcessDeploymentInfosWithActorOnlyForGroups(List groupIds, QueryOptions queryOptions) throws SBonitaReadException; /** * List all processes that contain at least one task which actor is mapped only to the specified role. * * @param roleId * the Id of the role from which to retrieve the processes with tasks only it can do. * @param queryOptions * object containing query criteria * @return the list of matching processes, as a List of SProcessDefinitionDeployInfo * @throws SBonitaReadException * in case a read problem occurs */ List getProcessDeploymentInfosWithActorOnlyForRole(long roleId, QueryOptions queryOptions) throws SBonitaReadException; /** * List all processes that contain at least one task which actor is mapped only to the specified roles. * * @param roleIds * the Ids of the roles from which to retrieve the processes with tasks only they can do. * @param queryOptions * object containing query criteria * @return the list of matching processes, as a List of SProcessDefinitionDeployInfo * @throws SBonitaReadException * in case a read problem occurs */ List getProcessDeploymentInfosWithActorOnlyForRoles(List roleIds, QueryOptions queryOptions) throws SBonitaReadException; /** * List all processes that contain at least one task which actor is mapped only to the specified user. * * @param userId * the Id of the user from which to retrieve the processes with tasks only he / she can do. * @param queryOptions * object containing query criteria * @return the list of matching processes, as a List of SProcessDefinitionDeployInfo * @throws SBonitaReadException * in case a read problem occurs */ List getProcessDeploymentInfosWithActorOnlyForUser(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * List all processes that contain at least one task which actor is mapped only to the specified users. * * @param userIds * the Ids of the users from which to retrieve the processes with tasks only they can do. * @param queryOptions * object containing query criteria * @return the list of matching processes, as a List of SProcessDefinitionDeployInfo * @throws SBonitaReadException * in case a read problem occurs */ List getProcessDeploymentInfosWithActorOnlyForUsers(List userIds, QueryOptions queryOptions) throws SBonitaReadException; /** * Get total number of users according to specific query options, and who can start the given process definition * * @param processDefinitionId * Identifier of the process definition * @param queryOptions * The QueryOptions object containing some query conditions * @return * @throws SBonitaReadException */ long getNumberOfUsersWhoCanStartProcessDeploymentInfo(long processDefinitionId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search users according to specific query options, and who can start the given process definition * * @param processDefinitionId * Identifier of the process definition * @param queryOptions * The QueryOptions object containing some query conditions * @return * @throws SBonitaReadException */ List searchUsersWhoCanStartProcessDeploymentInfo(long processDefinitionId, QueryOptions queryOptions) throws SBonitaReadException; /** * Get the total number of the process definitions that have one or more human tasks assigned/pending for a specific * user. * The tasks are in stable state, not in terminal/executing state. * * @param userId * The identifier of the user. * @param queryOptions * The QueryOptions object containing some query conditions * @return The number of the process definition * @throws SBonitaReadException */ long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search all process definitions that have one or more human tasks assigned/pending for a specific user. * The tasks are in stable state, not in terminal/executing state. * * @param userId * The identifier of the user. * @param queryOptions * The QueryOptions object containing some query conditions * @return The list of process definitions * @throws SBonitaReadException */ List searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Get the total number of the process definitions supervised by a specific user, that have instances with one or * more human tasks assigned/pending. * The tasks are in stable state, not in terminal/executing state. * * @param userId * The identifier of the user. * @param queryOptions * The QueryOptions object containing some query conditions * @return The number of the process definition * @throws SBonitaReadException * if an exception occurs when getting the process deployment information. * @since 6.3.3 */ long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search all process definitions supervised by a specific user, that have instances with one or more human tasks * assigned/pending. * The tasks are in stable state, not in terminal/executing state. * * @param userId * The identifier of the user. * @param queryOptions * The QueryOptions object containing some query conditions * @return The list of process definitions * @throws SBonitaReadException * if an exception occurs when getting the process deployment information. * @since 6.3.3 */ List searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy( long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Get the total number of the process definitions that have instances with one or more human tasks * assigned/pending. * The tasks are in stable state, not in terminal/executing state. * * @param queryOptions * The QueryOptions object containing some query conditions * @return The number of the process definition * @throws SBonitaReadException * if an exception occurs when getting the process deployment information. * @since 6.3.3 */ long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(QueryOptions queryOptions) throws SBonitaReadException; /** * Search all process definitions that have instances with one or more human tasks assigned/pending. * The tasks are in stable state, not in terminal/executing state. * * @param queryOptions * The QueryOptions object containing some query conditions * @return The list of process definitions * @throws SBonitaReadException * if an exception occurs when getting the process deployment information. * @since 6.3.3 */ List searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks( QueryOptions queryOptions) throws SBonitaReadException; /** * Updates the content of an Expression, for a given process definition. Any further use of this expresssion will * then use the new content, as if it was * designed like this in the first place. * * @param processDefinitionId the ID of the process definition on which the Expression content will be updated. * @param expressionDefinitionId the ID of the expression definition to update * @param content the new String content of the expression * @throws SProcessDefinitionNotFoundException if the referenced process definition does not exist. * @throws SObjectModificationException if the update cannot be performed successfully. */ void updateExpressionContent(long processDefinitionId, long expressionDefinitionId, String content) throws SProcessDefinitionNotFoundException, SObjectModificationException; /** * Returns a specific process definition that include informations such as tasks definition, actors... * * @param processDefinitionId * Identifier of process definition * @return The corresponding process definition with informations. * @throws SProcessDefinitionNotFoundException * If the process definition doesn't exist. * @throws SBonitaReadException * If the process definition design cannot be read * @since 7.0 */ DesignProcessDefinition getDesignProcessDefinition(long processDefinitionId) throws SProcessDefinitionNotFoundException, SBonitaReadException; } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/ProcessDefinitionServiceImpl.java ================================================ /** * 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.core.process.definition; import static org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo.*; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import org.bonitasoft.engine.bpm.bar.ProcessDefinitionBARContribution; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.bpm.process.impl.internal.ExpressionFinder; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.cache.SCacheException; import org.bonitasoft.engine.commons.ClassReflector; import org.bonitasoft.engine.commons.NullCheckingUtil; import org.bonitasoft.engine.commons.Pair; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SReflectException; import org.bonitasoft.engine.core.process.definition.exception.SDeletingEnabledProcessException; 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.exception.SProcessDeletionException; import org.bonitasoft.engine.core.process.definition.exception.SProcessDeploymentInfoUpdateException; import org.bonitasoft.engine.core.process.definition.exception.SProcessDisablementException; import org.bonitasoft.engine.core.process.definition.exception.SProcessEnablementException; 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.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDesignContent; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionLogBuilder; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionLogBuilderFactory; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.impl.ExpressionImpl; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Zhao Na * @author Yanyan Liu * @author Hongwen Zang * @author Celine Souchet * @author Arthur Freycon */ public class ProcessDefinitionServiceImpl implements ProcessDefinitionService { private final Recorder recorder; private final ReadPersistenceService persistenceService; private final SessionService sessionService; private final ReadSessionAccessor sessionAccessor; private final QueriableLoggerService queriableLoggerService; private final CacheService cacheService; protected ProcessDefinitionBARContribution processDefinitionBARContribution; public ProcessDefinitionServiceImpl(final Recorder recorder, final ReadPersistenceService persistenceService, final SessionService sessionService, final ReadSessionAccessor sessionAccessor, final QueriableLoggerService queriableLoggerService, CacheService cacheService) { this.recorder = recorder; this.persistenceService = persistenceService; this.sessionService = sessionService; this.sessionAccessor = sessionAccessor; this.queriableLoggerService = queriableLoggerService; this.cacheService = cacheService; processDefinitionBARContribution = new ProcessDefinitionBARContribution(); } @Override public void delete(final long processId) throws SProcessDefinitionNotFoundException, SProcessDeletionException, SDeletingEnabledProcessException { final SProcessDefinitionLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, "Deleting a Process definition"); final SProcessDefinitionDeployInfo processDefinitionDeployInfo; try { processDefinitionDeployInfo = getProcessDeploymentInfo(processId); if (ActivationState.ENABLED.name().equals(processDefinitionDeployInfo.getActivationState())) { throw new SDeletingEnabledProcessException("Process is enabled.", processDefinitionDeployInfo); } } catch (final SBonitaReadException e) { log(processId, SQueriableLog.STATUS_FAIL, logBuilder, "delete"); throw new SProcessDeletionException(e, processId); } try { recorder.recordDelete(new DeleteRecord(processDefinitionDeployInfo), PROCESSDEFINITION); log(processId, SQueriableLog.STATUS_OK, logBuilder, "delete"); } catch (final SRecorderException e) { log(processId, SQueriableLog.STATUS_FAIL, logBuilder, "delete"); throw new SProcessDeletionException(e, processDefinitionDeployInfo); } } @Override public void disableProcessDeploymentInfo(final long processId) throws SProcessDefinitionNotFoundException, SProcessDisablementException { disableProcess(processId, true); } @Override public void disableProcess(long processId, boolean failIfAlreadyDisabled) throws SProcessDefinitionNotFoundException, SProcessDisablementException { SProcessDefinitionDeployInfo processDefinitionDeployInfo; try { processDefinitionDeployInfo = getProcessDeploymentInfo(processId); } catch (final SBonitaReadException e) { throw new SProcessDisablementException(e); } if (failIfAlreadyDisabled && ActivationState.DISABLED.name().equals(processDefinitionDeployInfo.getActivationState())) { throw new SProcessDisablementException("Process " + processDefinitionDeployInfo.getName() + " with version " + processDefinitionDeployInfo.getVersion() + " is already disabled"); } final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY, ActivationState.DISABLED.name()); final SPersistenceLogBuilder logBuilder = getQueriableLog(ActionType.UPDATED, "Disabling the process"); try { update(processId, processDefinitionDeployInfo, getUpdateRecord(descriptor, processDefinitionDeployInfo), PROCESSDEFINITION_IS_DISABLED); log(processDefinitionDeployInfo.getId(), SQueriableLog.STATUS_OK, logBuilder, "disableProcess"); } catch (final SRecorderException | SCacheException e) { log(processDefinitionDeployInfo.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "disableProcess"); throw new SProcessDisablementException(e); } } void updateSProcessDefinitionTimestampInCache(long processId, SProcessDefinitionDeployInfo processDefinitionDeployInfo) throws SCacheException { final Pair fromCache = getSProcessDefinitionFromCache(processId); if (fromCache != null) { storeProcessDefinitionInCache(fromCache.getValue(), processDefinitionDeployInfo.getLastUpdateDate()); } } @Override public void enableProcessDeploymentInfo(final long processId) throws SProcessDefinitionNotFoundException, SProcessEnablementException { enableProcess(processId, true); } @Override public void enableProcess(final long processId, boolean failIfAlreadyEnabled) throws SProcessDefinitionNotFoundException, SProcessEnablementException { SProcessDefinitionDeployInfo processDefinitionDeployInfo; try { processDefinitionDeployInfo = getProcessDeploymentInfo(processId); } catch (final SBonitaReadException e) { throw new SProcessEnablementException(e); } if (failIfAlreadyEnabled && ActivationState.ENABLED.name().equals(processDefinitionDeployInfo.getActivationState())) { throw new SProcessEnablementException("Process " + processDefinitionDeployInfo.getName() + " with version " + processDefinitionDeployInfo.getVersion() + " is already enabled"); } if (ConfigurationState.UNRESOLVED.name().equals(processDefinitionDeployInfo.getConfigurationState())) { throw new SProcessEnablementException("Process " + processDefinitionDeployInfo.getName() + " with version " + processDefinitionDeployInfo.getVersion() + " can't be enabled since all dependencies are not resolved yet"); } final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY, ActivationState.ENABLED.name()); final SPersistenceLogBuilder logBuilder = getQueriableLog(ActionType.UPDATED, "Enabling the process"); try { update(processId, processDefinitionDeployInfo, getUpdateRecord(descriptor, processDefinitionDeployInfo), PROCESSDEFINITION_IS_ENABLED); log(processId, SQueriableLog.STATUS_OK, logBuilder, "enableProcess"); } catch (final SRecorderException | SCacheException e) { log(processId, SQueriableLog.STATUS_FAIL, logBuilder, "enableProcess"); throw new SProcessEnablementException(e); } } private SProcessDefinitionLogBuilder getQueriableLog(final ActionType actionType, final String message) { final SProcessDefinitionLogBuilder logBuilder = BuilderFactory.get(SProcessDefinitionLogBuilderFactory.class) .createNewInstance(); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); return logBuilder; } @Override public List getProcessDeploymentInfos(final int fromIndex, final int numberPerPage, final String field, final OrderByType order) throws SBonitaReadException { final Map emptyMap = Collections.emptyMap(); return persistenceService.selectList( new SelectListDescriptor("", emptyMap, SProcessDefinitionDeployInfo.class, new QueryOptions(fromIndex, numberPerPage, SProcessDefinitionDeployInfo.class, field, order))); } @Override public long getNumberOfProcessDeploymentInfos() throws SBonitaReadException { final Map parameters = Collections.emptyMap(); final SelectOneDescriptor selectDescriptor = new SelectOneDescriptor<>("getNumberOfProcessDefinitions", parameters, SProcessDefinitionDeployInfo.class); return persistenceService.selectOne(selectDescriptor); } @Override public SProcessDefinition getProcessDefinition(final long processId) throws SProcessDefinitionNotFoundException, SBonitaReadException { try { //get from database final SProcessDefinitionDeployInfo processDeploymentInfo = getProcessDeploymentInfo(processId); //get from cache final Pair processWithTimestamp = getSProcessDefinitionFromCache(processId); //read SProcessDefinition if needed if (isSProcessDefinitionUpToDate(processDeploymentInfo, processWithTimestamp)) { return readSProcessDefinitionFromDatabase(processId, processDeploymentInfo); } else { return processWithTimestamp.getValue(); } } catch (IOException | SReflectException | SCacheException e) { throw new SBonitaReadException(e); } } SProcessDefinition readSProcessDefinitionFromDatabase(long processId, SProcessDefinitionDeployInfo processDeploymentInfo) throws IOException, SReflectException, SCacheException { final DesignProcessDefinition objectFromXML = processDefinitionBARContribution .convertXmlToProcess(processDeploymentInfo.getDesignContent() .getContent()); SProcessDefinition sProcessDefinition = convertDesignProcessDefinition(objectFromXML); setIdOnProcessDefinition(sProcessDefinition, processId); storeProcessDefinitionInCache(sProcessDefinition, processDeploymentInfo.getLastUpdateDate()); return sProcessDefinition; } boolean isSProcessDefinitionUpToDate(SProcessDefinitionDeployInfo processDeploymentInfo, Pair processWithTimestamp) { return processWithTimestamp == null || processWithTimestamp.getKey() != processDeploymentInfo.getLastUpdateDate(); } @SuppressWarnings("unchecked") Pair getSProcessDefinitionFromCache(long processId) throws SCacheException { return (Pair) cacheService.get(PROCESS_CACHE_NAME, processId); } @Override public SProcessDefinition getProcessDefinitionIfIsEnabled(final long processDefinitionId) throws SBonitaReadException, SProcessDefinitionException { final SProcessDefinitionDeployInfo deployInfo = getProcessDeploymentInfo(processDefinitionId); if (ActivationState.DISABLED.name().equals(deployInfo.getActivationState())) { throw new SProcessDefinitionException("The process definition is not enabled !!", deployInfo.getProcessId(), deployInfo.getName(), deployInfo.getVersion()); } return getProcessDefinition(processDefinitionId); } private void setIdOnProcessDefinition(final SProcessDefinition sProcessDefinition, long id) throws SReflectException { ClassReflector.invokeSetter(sProcessDefinition, "setId", Long.class, id); } protected long generateId() { return Math.abs(UUID.randomUUID().getLeastSignificantBits()); } @Override public SProcessDefinitionDeployInfo getProcessDeploymentInfo(final long processId) throws SProcessDefinitionNotFoundException, SBonitaReadException { final Map parameters = Collections.singletonMap("processId", (Object) processId); final SelectOneDescriptor descriptor = new SelectOneDescriptor<>( "getDeployInfoByProcessDefId", parameters, SProcessDefinitionDeployInfo.class); final SProcessDefinitionDeployInfo processDefinitionDeployInfo = persistenceService.selectOne(descriptor); if (processDefinitionDeployInfo == null) { throw new SProcessDefinitionNotFoundException("Unable to find the process definition deployment info.", processId); } return processDefinitionDeployInfo; } private void initializeLogBuilder(final T logBuilder, final String message) { logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message); } @Override public SProcessDefinition store(final DesignProcessDefinition designProcessDefinition) throws SProcessDefinitionException { NullCheckingUtil.checkArgsNotNull(designProcessDefinition); // create the runtime process definition final SProcessDefinition definition = convertDesignProcessDefinition(designProcessDefinition); final SProcessDefinitionLogBuilder logBuilder = getQueriableLog(ActionType.CREATED, "Creating a new Process definition"); try { final String processDefinitionContent = getProcessContent(designProcessDefinition); final SProcessDefinitionDesignContent sProcessDefinitionDesignContent = new SProcessDefinitionDesignContent(); sProcessDefinitionDesignContent.setContent(processDefinitionContent); recorder.recordInsert(new InsertRecord(sProcessDefinitionDesignContent), PROCESSDEFINITION_CONTENT); final long processId = generateId(); setIdOnProcessDefinition(definition, processId); final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = createProcessDefinitionDeployInfo( designProcessDefinition, definition, sProcessDefinitionDesignContent, processId); recorder.recordInsert(new InsertRecord(sProcessDefinitionDeployInfo), PROCESSDEFINITION); //storeProcessDefinitionInCache(definition, sProcessDefinitionDeployInfo.getLastUpdateDate()); log(definition.getId(), SQueriableLog.STATUS_OK, logBuilder, "store"); } catch (final Exception e) { log(definition.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "store"); throw new SProcessDefinitionException(e); } return definition; } SProcessDefinitionDeployInfo createProcessDefinitionDeployInfo(DesignProcessDefinition designProcessDefinition, SProcessDefinition definition, SProcessDefinitionDesignContent sProcessDefinitionDesignContent, long processId) { String displayName = designProcessDefinition.getDisplayName(); if (displayName == null || displayName.isEmpty()) { displayName = definition.getName(); } String displayDescription = designProcessDefinition.getDisplayDescription(); if (displayDescription == null || displayDescription.isEmpty()) { displayDescription = definition.getDescription(); } final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = new SProcessDefinitionDeployInfo(); sProcessDefinitionDeployInfo.setName(definition.getName()); sProcessDefinitionDeployInfo.setVersion(definition.getVersion()); sProcessDefinitionDeployInfo.setProcessId(processId); sProcessDefinitionDeployInfo.setDescription(definition.getDescription()); sProcessDefinitionDeployInfo.setDeployedBy(getUserId()); sProcessDefinitionDeployInfo.setDeploymentDate(System.currentTimeMillis()); sProcessDefinitionDeployInfo.setActivationState(ActivationState.DISABLED.name()); sProcessDefinitionDeployInfo.setConfigurationState(ConfigurationState.UNRESOLVED.name()); sProcessDefinitionDeployInfo.setDisplayName(displayName); sProcessDefinitionDeployInfo.setDisplayDescription(displayDescription); sProcessDefinitionDeployInfo.setDesignContent(sProcessDefinitionDesignContent); return sProcessDefinitionDeployInfo; } void storeProcessDefinitionInCache(SProcessDefinition definition, Long lastUpdateDate) throws SCacheException { cacheService.store(PROCESS_CACHE_NAME, definition.getId(), Pair.of(lastUpdateDate, definition)); } String getProcessContent(DesignProcessDefinition designProcessDefinition) throws IOException { return processDefinitionBARContribution.convertProcessToXml(designProcessDefinition); } SProcessDefinition convertDesignProcessDefinition(DesignProcessDefinition designProcessDefinition) { return BuilderFactory.get(SProcessDefinitionBuilderFactory.class).createNewInstance(designProcessDefinition) .done(); } private long getUserId() { return sessionService.getLoggedUserFromSession(sessionAccessor); } private void updateLog(final ActionType actionType, final T logBuilder) { logBuilder.setActionType(actionType); } @Override public void resolveProcess(final long processId) throws SProcessDefinitionNotFoundException, SProcessDisablementException { SProcessDefinitionDeployInfo processDefinitionDeployInfo; try { processDefinitionDeployInfo = getProcessDeploymentInfo(processId); } catch (final SBonitaReadException e) { throw new SProcessDisablementException(e); } if (!ConfigurationState.UNRESOLVED.name().equals(processDefinitionDeployInfo.getConfigurationState())) { throw new SProcessDisablementException("Process " + processDefinitionDeployInfo.getName() + " with version" + processDefinitionDeployInfo.getVersion() + " is not unresolved"); } final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor .addField(SProcessDefinitionDeployInfo.CONFIGURATION_STATE_KEY, ConfigurationState.RESOLVED.name()); final SPersistenceLogBuilder logBuilder = getQueriableLog(ActionType.UPDATED, "Resolved the process"); try { update(processId, processDefinitionDeployInfo, getUpdateRecord(descriptor, processDefinitionDeployInfo), PROCESSDEFINITION_IS_RESOLVED); log(processDefinitionDeployInfo.getId(), SQueriableLog.STATUS_OK, logBuilder, "resolveProcess"); } catch (final SRecorderException | SCacheException e) { log(processDefinitionDeployInfo.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "resolveProcess"); throw new SProcessDisablementException(e); } } @Override public long getNumberOfProcessDeploymentInfosByActivationState(final ActivationState activationState) throws SBonitaReadException { final Map parameters = Collections.singletonMap("activationState", (Object) activationState.name()); final SelectOneDescriptor selectDescriptor = new SelectOneDescriptor<>( "getNumberOfProcessDefinitionsInActivationState", parameters, SProcessDefinitionDeployInfo.class); return persistenceService.selectOne(selectDescriptor); } @Override public List getProcessDefinitionIds(final ActivationState activationState, final int fromIndex, final int numberOfResults) throws SBonitaReadException { final Map parameters = Collections.singletonMap("activationState", (Object) activationState.name()); final List orderByOptions = Collections .singletonList(new OrderByOption(SProcessDefinitionDeployInfo.class, "id", OrderByType.ASC)); final SelectListDescriptor selectDescriptor = new SelectListDescriptor<>( "getProcessDefinitionsIdsInActivationState", parameters, SProcessDefinitionDeployInfo.class, new QueryOptions(fromIndex, numberOfResults, orderByOptions)); return persistenceService.selectList(selectDescriptor); } @Override public List getProcessDefinitionIds(final int fromIndex, final int numberOfResults) throws SBonitaReadException { final Map parameters = Collections.emptyMap(); final List orderByOptions = Collections .singletonList(new OrderByOption(SProcessDefinitionDeployInfo.class, "id", OrderByType.ASC)); final SelectListDescriptor selectDescriptor = new SelectListDescriptor<>("getProcessDefinitionsIds", parameters, SProcessDefinitionDeployInfo.class, new QueryOptions(fromIndex, numberOfResults, orderByOptions)); return persistenceService.selectList(selectDescriptor); } @Override public SFlowNodeDefinition getNextFlowNode(final SProcessDefinition definition, final String source) { final SFlowElementContainerDefinition processContainer = definition.getProcessContainer(); final STransitionDefinition sourceNode = processContainer.getTransition(source); final long targetId = sourceNode.getTarget(); return processContainer.getFlowNode(targetId); } @Override public List getProcessDeploymentInfos(final List processIds, final int fromIndex, final int numberOfProcesses, final String field, final OrderByType order) throws SBonitaReadException { if (processIds == null || processIds.size() == 0) { return Collections.emptyList(); } final Map emptyMap = Collections.singletonMap("processIds", (Object) processIds); final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfProcesses, SProcessDefinitionDeployInfo.class, field, order); return persistenceService.selectList(new SelectListDescriptor( "getSubSetOfProcessDefinitionDeployInfos", emptyMap, SProcessDefinitionDeployInfo.class, queryOptions)); } @Override public List getProcessDeploymentInfos(final List processIds) throws SBonitaReadException { if (processIds == null || processIds.size() == 0) { return Collections.emptyList(); } final QueryOptions queryOptions = new QueryOptions(0, processIds.size(), SProcessDefinitionDeployInfo.class, "name", OrderByType.ASC); final Map emptyMap = Collections.singletonMap("processIds", (Object) processIds); return persistenceService.selectList(new SelectListDescriptor( "getSubSetOfProcessDefinitionDeployInfos", emptyMap, SProcessDefinitionDeployInfo.class, queryOptions)); } @Override public long getLatestProcessDefinitionId(final String processName) throws SBonitaReadException, SProcessDefinitionNotFoundException { final List sProcessDefinitionDeployInfos = getProcessDeploymentInfosOrderByTimeDesc( processName, 0, 1); if (sProcessDefinitionDeployInfos.isEmpty()) { throw new SProcessDefinitionNotFoundException(processName); } return sProcessDefinitionDeployInfos.get(0).getProcessId(); } private List getProcessDeploymentInfosOrderByTimeDesc(final String processName, final int startIndex, final int maxResults) throws SBonitaReadException { final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults, SProcessDefinitionDeployInfo.class, "deploymentDate", OrderByType.DESC); final Map parameters = Collections.singletonMap("name", (Object) processName); final SelectListDescriptor selectDescriptor = new SelectListDescriptor<>( "getProcessDefinitionDeployInfosByName", parameters, SProcessDefinitionDeployInfo.class, queryOptions); return persistenceService.selectList(selectDescriptor); } @Override public long getProcessDefinitionId(final String name, final String version) throws SBonitaReadException, SProcessDefinitionNotFoundException { final Map parameters = new HashMap<>(); parameters.put("name", name); parameters.put("version", version); final Long processDefId = persistenceService .selectOne(new SelectOneDescriptor<>("getProcessDefinitionIdByNameAndVersion", parameters, SProcessDefinitionDeployInfo.class, Long.class)); if (processDefId == null) { final SProcessDefinitionNotFoundException exception = new SProcessDefinitionNotFoundException( "Process definition id not found."); exception.setProcessDefinitionNameOnContext(name); exception.setProcessDefinitionVersionOnContext(version); throw exception; } return processDefId; } @Override public SProcessDefinitionDeployInfo updateProcessDefinitionDeployInfo(final long processId, final EntityUpdateDescriptor descriptor) throws SProcessDefinitionNotFoundException, SProcessDeploymentInfoUpdateException { final String logMessage = "Updating a processDefinitionDeployInfo"; return updateProcessDefinitionDeployInfo(processId, descriptor, logMessage); } SProcessDefinitionDeployInfo updateProcessDefinitionDeployInfo(long processId, EntityUpdateDescriptor descriptor, String logMessage) throws SProcessDefinitionNotFoundException, SProcessDeploymentInfoUpdateException { SProcessDefinitionDeployInfo processDefinitionDeployInfo; try { processDefinitionDeployInfo = getProcessDeploymentInfo(processId); } catch (final SBonitaReadException e) { throw new SProcessDefinitionNotFoundException(e, processId); } final SProcessDefinitionLogBuilder logBuilder = getQueriableLog(ActionType.UPDATED, truncate(logMessage)); try { update(processId, processDefinitionDeployInfo, getUpdateRecord(descriptor, processDefinitionDeployInfo), PROCESSDEFINITION_DEPLOY_INFO); log(processId, SQueriableLog.STATUS_OK, logBuilder, "updateProcessDeploymentInfo"); } catch (final SRecorderException | SCacheException e) { log(processId, SQueriableLog.STATUS_FAIL, logBuilder, "updateProcessDeploymentInfo"); throw new SProcessDeploymentInfoUpdateException(e); } return processDefinitionDeployInfo; } void update(long processId, SProcessDefinitionDeployInfo processDefinitionDeployInfo, UpdateRecord updateRecord, String eventType) throws SRecorderException, SCacheException { recorder.recordUpdate(updateRecord, eventType); if (!updateRecord.getFields().containsKey(SProcessDefinitionDeployInfo.DESIGN_CONTENT)) { updateSProcessDefinitionTimestampInCache(processId, processDefinitionDeployInfo); } } private UpdateRecord getUpdateRecord(final EntityUpdateDescriptor descriptor, final SProcessDefinitionDeployInfo processDefinitionDeployInfo) { final long now = System.currentTimeMillis(); descriptor.addField(LAST_UPDATE_DATE_KEY, now); return UpdateRecord.buildSetFields(processDefinitionDeployInfo, descriptor); } @Override public List searchProcessDeploymentInfosStartedBy(final long startedBy, final QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("startedBy", (Object) startedBy); return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, STARTED_BY_SUFFIX, searchOptions, parameters); } @Override public long getNumberOfProcessDeploymentInfosStartedBy(final long startedBy, final QueryOptions countOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("startedBy", (Object) startedBy); return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, STARTED_BY_SUFFIX, countOptions, parameters); } @Override public List searchProcessDeploymentInfos(final QueryOptions searchOptions) throws SBonitaReadException { return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, searchOptions, null); } @Override public long getNumberOfProcessDeploymentInfos(final QueryOptions countOptions) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, countOptions, null); } @Override public List searchProcessDeploymentInfosCanBeStartedBy(final long userId, final QueryOptions searchOptions) throws SBonitaReadException { return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "UserCanStart", searchOptions, Collections.singletonMap(USER_ID, (Object) userId)); } @Override public long getNumberOfProcessDeploymentInfosCanBeStartedBy(final long userId, final QueryOptions countOptions) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "UserCanStart", countOptions, Collections.singletonMap(USER_ID, (Object) userId)); } @Override public List searchProcessDeploymentInfosCanBeStartedByUsersManagedBy( final long managerUserId, final QueryOptions searchOptions) throws SBonitaReadException { return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "UsersManagedByCanStart", searchOptions, Collections.singletonMap("managerUserId", (Object) managerUserId)); } @Override public long getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedBy(final long managerUserId, final QueryOptions countOptions) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "UsersManagedByCanStart", countOptions, Collections.singletonMap("managerUserId", (Object) managerUserId)); } @Override public List searchProcessDeploymentInfos(final long userId, final QueryOptions searchOptions, final String querySuffix) throws SBonitaReadException { return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, querySuffix, searchOptions, Collections.singletonMap(USER_ID, (Object) userId)); } @Override public long getNumberOfProcessDeploymentInfos(final long userId, final QueryOptions countOptions, final String querySuffix) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, querySuffix, countOptions, Collections.singletonMap(USER_ID, (Object) userId)); } @Override public long getNumberOfUncategorizedProcessDeploymentInfos(final QueryOptions countOptions) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, UNCATEGORIZED_SUFFIX, countOptions, null); } @Override public List searchUncategorizedProcessDeploymentInfos( final QueryOptions searchOptions) throws SBonitaReadException { return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, UNCATEGORIZED_SUFFIX, searchOptions, null); } @Override public long getNumberOfUncategorizedProcessDeploymentInfosSupervisedBy(final long userId, final QueryOptions countOptions) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, UNCATEGORIZED_SUPERVISED_BY_SUFFIX, countOptions, Collections.singletonMap(USER_ID, (Object) userId)); } @Override public List searchUncategorizedProcessDeploymentInfosSupervisedBy(final long userId, final QueryOptions searchOptions) throws SBonitaReadException { return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, UNCATEGORIZED_SUPERVISED_BY_SUFFIX, searchOptions, Collections.singletonMap(USER_ID, (Object) userId)); } @Override public List searchUncategorizedProcessDeploymentInfosCanBeStartedBy(final long userId, final QueryOptions searchOptions) throws SBonitaReadException { return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, UNCATEGORIZED_USERCANSTART_SUFFIX, searchOptions, Collections.singletonMap(USER_ID, (Object) userId)); } @Override public long getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedBy(final long userId, final QueryOptions countOptions) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, UNCATEGORIZED_USERCANSTART_SUFFIX, countOptions, Collections.singletonMap(USER_ID, (Object) userId)); } @Override public Map getProcessDeploymentInfosFromProcessInstanceIds( final List processInstanceIds) throws SBonitaReadException { if (processInstanceIds == null || processInstanceIds.size() == 0) { return Collections.emptyMap(); } final QueryOptions queryOptions = new QueryOptions(0, processInstanceIds.size(), SProcessDefinitionDeployInfo.class, "name", OrderByType.ASC); final Map parameters = Collections.singletonMap("processInstanceIds", (Object) processInstanceIds); final List> result = persistenceService .selectList(new SelectListDescriptor>( "getProcessDeploymentInfoFromProcessInstanceIds", parameters, SProcessDefinitionDeployInfo.class, queryOptions)); if (result != null && result.size() > 0) { return getProcessDeploymentInfosFromMap(result); } return Collections.emptyMap(); } @Override public Map getProcessDeploymentInfosFromArchivedProcessInstanceIds( final List archivedProcessInstantsIds) throws SBonitaReadException { if (archivedProcessInstantsIds == null || archivedProcessInstantsIds.size() == 0) { return Collections.emptyMap(); } final QueryOptions queryOptions = new QueryOptions(0, archivedProcessInstantsIds.size(), SProcessDefinitionDeployInfo.class, "name", OrderByType.ASC); final Map parameters = Collections.singletonMap("archivedProcessInstanceIds", (Object) archivedProcessInstantsIds); final List> result = persistenceService .selectList(new SelectListDescriptor>( "getProcessDeploymentInfoFromArchivedProcessInstanceIds", parameters, SProcessDefinitionDeployInfo.class, queryOptions)); if (result != null && result.size() > 0) { return getProcessDeploymentInfosFromMap(result); } return Collections.emptyMap(); } private Map getProcessDeploymentInfosFromMap( final List> sProcessDeploymentInfos) { final Map mProcessDeploymentInfos = new HashMap<>(); for (final Map sProcessDeploymentInfo : sProcessDeploymentInfos) { final Long archivedProcessInstanceId = (Long) sProcessDeploymentInfo.get("archivedProcessInstanceId"); final Long processInstanceId = (Long) sProcessDeploymentInfo.get("processInstanceId"); final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = buildSProcessDefinitionDeployInfo( sProcessDeploymentInfo); mProcessDeploymentInfos.put(processInstanceId != null ? processInstanceId : archivedProcessInstanceId, sProcessDefinitionDeployInfo); } return mProcessDeploymentInfos; } private SProcessDefinitionDeployInfo buildSProcessDefinitionDeployInfo( final Map sProcessDeploymentInfo) { return SProcessDefinitionDeployInfo.builder() .id((Long) sProcessDeploymentInfo.get(ID_KEY)) .name((String) sProcessDeploymentInfo.get(NAME_KEY)) .version((String) sProcessDeploymentInfo.get(VERSION_KEY)) .description((String) sProcessDeploymentInfo.get(VERSION_KEY)) .displayDescription((String) sProcessDeploymentInfo.get(DISPLAY_DESCRIPTION_KEY)) .activationState((String) sProcessDeploymentInfo.get(ACTIVATION_STATE_KEY)) .configurationState((String) sProcessDeploymentInfo.get(CONFIGURATION_STATE_KEY)) .deployedBy((Long) sProcessDeploymentInfo.get(DEPLOYED_BY_KEY)) .processId((Long) sProcessDeploymentInfo.get(PROCESS_ID_KEY)) .lastUpdateDate((Long) sProcessDeploymentInfo.get(LAST_UPDATE_DATE_KEY)) .displayName((String) sProcessDeploymentInfo.get(DISPLAY_NAME_KEY)) .deploymentDate((Long) sProcessDeploymentInfo.get(DEPLOYMENT_DATE_KEY)) .iconPath((String) sProcessDeploymentInfo.get(ICON_PATH)).build(); } private void log(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder, final String callerMethodName) { logBuilder.actionScope(String.valueOf(objectId)); logBuilder.actionStatus(sQueriableLogStatus); logBuilder.objectId(objectId); final SQueriableLog log = logBuilder.build(); if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) { queriableLoggerService.log(this.getClass().getName(), callerMethodName, log); } } @Override public List getProcessDeploymentInfosUnrelatedToCategory(final long categoryId, final int pageIndex, final int numberPerPage, final ProcessDeploymentInfoCriterion pagingCriterion) throws SBonitaReadException { final Map parameters = Collections.singletonMap("categoryId", (Object) categoryId); final QueryOptions queryOptions = createQueryOptions(pageIndex, numberPerPage, pagingCriterion); final SelectListDescriptor selectDescriptor = new SelectListDescriptor<>( "searchSProcessDefinitionDeployInfoUnrelatedToCategory", parameters, SProcessDefinitionDeployInfo.class, queryOptions); return persistenceService.selectList(selectDescriptor); } @Override public Long getNumberOfProcessDeploymentInfosUnrelatedToCategory(final long categoryId) throws SBonitaReadException { final Map parameters = Collections.singletonMap("categoryId", (Object) categoryId); final SelectOneDescriptor selectDescriptor = new SelectOneDescriptor<>( "getNumberOfSProcessDefinitionDeployInfoUnrelatedToCategory", parameters, SProcessDefinitionDeployInfo.class); return persistenceService.selectOne(selectDescriptor); } @Override public List searchProcessDeploymentInfosOfCategory(final long categoryId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("categoryId", (Object) categoryId); final SelectListDescriptor selectDescriptor = new SelectListDescriptor<>( "searchSProcessDeploymentInfosOfCategory", parameters, SProcessDefinitionDeployInfo.class, queryOptions); return persistenceService.selectList(selectDescriptor); } private QueryOptions createQueryOptions(final int pageIndex, final int numberPerPage, final ProcessDeploymentInfoCriterion pagingCriterion) { String field; OrderByType order; switch (pagingCriterion) { case NAME_ASC: field = SProcessDefinitionDeployInfo.NAME_KEY; order = OrderByType.ASC; break; case NAME_DESC: field = SProcessDefinitionDeployInfo.NAME_KEY; order = OrderByType.DESC; break; case ACTIVATION_STATE_ASC: field = SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY; order = OrderByType.ASC; break; case ACTIVATION_STATE_DESC: field = SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY; order = OrderByType.DESC; break; case CONFIGURATION_STATE_ASC: field = SProcessDefinitionDeployInfo.CONFIGURATION_STATE_KEY; order = OrderByType.ASC; break; case CONFIGURATION_STATE_DESC: field = SProcessDefinitionDeployInfo.CONFIGURATION_STATE_KEY; order = OrderByType.DESC; break; case VERSION_ASC: field = SProcessDefinitionDeployInfo.VERSION_KEY; order = OrderByType.ASC; break; case VERSION_DESC: field = SProcessDefinitionDeployInfo.VERSION_KEY; order = OrderByType.DESC; break; case DEFAULT: default: field = pagingCriterion.getField(); order = OrderByType.valueOf(pagingCriterion.getOrder().name()); break; } return new QueryOptions(pageIndex, numberPerPage, SProcessDefinitionDeployInfo.class, field, order); } @Override public List getProcessDeploymentInfos(final QueryOptions queryOptions) throws SBonitaReadException { return persistenceService .selectList( new SelectListDescriptor("getProcessDefinitionDeployInfos", Collections . emptyMap(), SProcessDefinitionDeployInfo.class, queryOptions)); } @Override public List getProcessDeploymentInfosWithActorOnlyForGroup(final long groupId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap(GROUP_ID, (Object) groupId); final SelectListDescriptor selectDescriptor = new SelectListDescriptor<>( "getProcessesWithActorOnlyForGroup", parameters, SProcessDefinitionDeployInfo.class, queryOptions); return persistenceService.selectList(selectDescriptor); } @Override public List getProcessDeploymentInfosWithActorOnlyForGroups(final List groupIds, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("groupIds", (Object) groupIds); final SelectListDescriptor selectDescriptor = new SelectListDescriptor<>( "getProcessesWithActorOnlyForGroups", parameters, SProcessDefinitionDeployInfo.class, queryOptions); return persistenceService.selectList(selectDescriptor); } @Override public List getProcessDeploymentInfosWithActorOnlyForRole(final long roleId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap(ROLE_ID, (Object) roleId); final SelectListDescriptor selectDescriptor = new SelectListDescriptor<>( "getProcessesWithActorOnlyForRole", parameters, SProcessDefinitionDeployInfo.class, queryOptions); return persistenceService.selectList(selectDescriptor); } @Override public List getProcessDeploymentInfosWithActorOnlyForRoles(final List roleIds, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("roleIds", (Object) roleIds); final SelectListDescriptor selectDescriptor = new SelectListDescriptor<>( "getProcessesWithActorOnlyForRoles", parameters, SProcessDefinitionDeployInfo.class, queryOptions); return persistenceService.selectList(selectDescriptor); } @Override public List getProcessDeploymentInfosWithActorOnlyForUser(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap(USER_ID, (Object) userId); final SelectListDescriptor selectDescriptor = new SelectListDescriptor<>( "getProcessesWithActorOnlyForUser", parameters, SProcessDefinitionDeployInfo.class, queryOptions); return persistenceService.selectList(selectDescriptor); } @Override public List getProcessDeploymentInfosWithActorOnlyForUsers(final List userIds, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userIds", (Object) userIds); final SelectListDescriptor selectDescriptor = new SelectListDescriptor<>( "getProcessesWithActorOnlyForUsers", parameters, SProcessDefinitionDeployInfo.class, queryOptions); return persistenceService.selectList(selectDescriptor); } @Override public long getNumberOfUsersWhoCanStartProcessDeploymentInfo(final long processDefinitionId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap(PROCESS_DEFINITION_ID, (Object) processDefinitionId); return persistenceService.getNumberOfEntities(SUser.class, WHOCANSTART_PROCESS_SUFFIX, queryOptions, parameters); } @Override public List searchUsersWhoCanStartProcessDeploymentInfo(final long processDefinitionId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap(PROCESS_DEFINITION_ID, (Object) processDefinitionId); return persistenceService.searchEntity(SUser.class, WHOCANSTART_PROCESS_SUFFIX, queryOptions, parameters); } @Override public long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap(USER_ID, (Object) userId); return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasksFor", queryOptions, parameters); } @Override public List searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor( final long userId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap(USER_ID, (Object) userId); return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasksFor", queryOptions, parameters); } @Override public long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap(USER_ID, (Object) userId); return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasksSupervisedBy", queryOptions, parameters); } @Override public List searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy( final long userId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap(USER_ID, (Object) userId); return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasksSupervisedBy", queryOptions, parameters); } @Override public long getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(final QueryOptions queryOptions) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasks", queryOptions, null); } @Override public List searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks( final QueryOptions queryOptions) throws SBonitaReadException { return persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasks", queryOptions, null); } @Override public DesignProcessDefinition getDesignProcessDefinition(long processDefinitionId) throws SProcessDefinitionNotFoundException, SBonitaReadException { try { return processDefinitionBARContribution .convertXmlToProcess(getProcessDeploymentInfo(processDefinitionId).getDesignContent().getContent()); } catch (IOException e) { throw new SBonitaReadException(e); } } @Override public void updateExpressionContent(long processDefinitionId, long expressionDefinitionId, String content) throws SProcessDefinitionNotFoundException, SObjectModificationException { try { final DesignProcessDefinition designProcessDefinition = getDesignProcessDefinition(processDefinitionId); final ExpressionImpl expression = (ExpressionImpl) getExpression(designProcessDefinition, expressionDefinitionId); if (expression == null) { throw new SObjectModificationException("No expression with ID " + expressionDefinitionId + " found on process " + designProcessDefinition.getDisplayName() + " (" + designProcessDefinition.getVersion() + ")"); } if (!isValidExpressionTypeToUpdate(expression.getExpressionType())) { throw new SObjectModificationException( "Updating an Expression of type " + expression.getExpressionType() + " is not supported. Only Groovy scripts and constants are allowed."); } final String oldContent = expression.getContent(); expression.setContent(content); final String processDefinitionAsXMLString = getProcessContent(designProcessDefinition); final EntityUpdateDescriptor updateDescriptor = BuilderFactory .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class) .createNewInstance().updateDesignContent(processDefinitionAsXMLString).done(); updateProcessDefinitionDeployInfo(processDefinitionId, updateDescriptor, "Update expression <" + expressionDefinitionId + ">, old content is <" + oldContent + ">"); } catch (IOException e) { throw new SProcessDefinitionNotFoundException(e, processDefinitionId); } catch (SBonitaReadException | SProcessDeploymentInfoUpdateException e) { throw new SObjectModificationException(e); } } String truncate(String logMessage) { return logMessage.substring(0, Math.min(255, logMessage.length())); } protected boolean isValidExpressionTypeToUpdate(String type) { return ExpressionType.TYPE_READ_ONLY_SCRIPT.name().equals(type) || ExpressionType.TYPE_CONSTANT.name().equals(type); } protected Expression getExpression(DesignProcessDefinition processDefinition, long expressionDefinitionId) { final ExpressionFinder expressionFinder = new ExpressionFinder(); expressionFinder.find(processDefinition, expressionDefinitionId); return expressionFinder.getFoundExpression(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/exception/SDeletingEnabledProcessException.java ================================================ /** * 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.core.process.definition.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; /** * @author Baptiste Mesta * @author Celine Souchet */ public class SDeletingEnabledProcessException extends SBonitaException { private static final long serialVersionUID = -8265428109141653941L; public SDeletingEnabledProcessException(final String message, final SProcessDefinitionDeployInfo processDefinitionDeployInfo) { super(message); setProcessDefinitionIdOnContext(processDefinitionDeployInfo.getId()); setProcessDefinitionNameOnContext(processDefinitionDeployInfo.getName()); setProcessDefinitionVersionOnContext(processDefinitionDeployInfo.getVersion()); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/exception/SProcessDefinitionException.java ================================================ /** * 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.core.process.definition.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SProcessDefinitionException extends SBonitaException { private static final long serialVersionUID = -118684656125186636L; public SProcessDefinitionException(final String message) { super(message); } public SProcessDefinitionException(final Throwable cause) { super(cause); } public SProcessDefinitionException(final String message, final Throwable cause) { super(message, cause); } public SProcessDefinitionException(final String message, final long processDefinitionId, final String name, final String version) { super(message); setProcessDefinitionIdOnContext(processDefinitionId); setProcessDefinitionNameOnContext(name); setProcessDefinitionVersionOnContext(version); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/exception/SProcessDefinitionNotFoundException.java ================================================ /** * 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.core.process.definition.exception; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class SProcessDefinitionNotFoundException extends SProcessDefinitionException { private static final long serialVersionUID = 1702422492322010491L; public SProcessDefinitionNotFoundException(final String message, final long id) { super(message); setProcessDefinitionIdOnContext(id); } public SProcessDefinitionNotFoundException(final Throwable cause, final long id) { super(cause); setProcessDefinitionIdOnContext(id); } public SProcessDefinitionNotFoundException(final String message, final Throwable e, final long id) { super(message, e); setProcessDefinitionIdOnContext(id); } public SProcessDefinitionNotFoundException(final Throwable cause, final SProcessDefinitionDeployInfo processDefinitionDeployInfo) { this(cause, processDefinitionDeployInfo.getId()); setProcessDefinitionNameOnContext(processDefinitionDeployInfo.getName()); setProcessDefinitionVersionOnContext(processDefinitionDeployInfo.getVersion()); } public SProcessDefinitionNotFoundException(final String processName) { super("Can't find the process."); setProcessDefinitionNameOnContext(processName); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/exception/SProcessDeletionException.java ================================================ /** * 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.core.process.definition.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; /** * @author Baptiste Mesta * @author Celine Souchet */ public class SProcessDeletionException extends SBonitaException { private static final long serialVersionUID = 4908892108076783889L; public SProcessDeletionException(final Throwable cause, final SProcessDefinitionDeployInfo processDefinitionDeployInfo) { this(cause, processDefinitionDeployInfo.getId()); setProcessDefinitionNameOnContext(processDefinitionDeployInfo.getName()); setProcessDefinitionVersionOnContext(processDefinitionDeployInfo.getVersion()); } public SProcessDeletionException(final Throwable cause, final long processId) { super(cause); setProcessDefinitionIdOnContext(processId); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/exception/SProcessDeploymentInfoUpdateException.java ================================================ /** * 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.core.process.definition.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Yanyan Liu */ public class SProcessDeploymentInfoUpdateException extends SBonitaException { private static final long serialVersionUID = 3938659493530825527L; public SProcessDeploymentInfoUpdateException(final String message) { super(message); } public SProcessDeploymentInfoUpdateException(final Throwable cause) { super(cause); } public SProcessDeploymentInfoUpdateException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/exception/SProcessDisablementException.java ================================================ /** * 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.core.process.definition.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SProcessDisablementException extends SBonitaException { private static final long serialVersionUID = 7858287891716546529L; public SProcessDisablementException(final Throwable cause) { super(cause); } public SProcessDisablementException(final String message) { super(message); } public SProcessDisablementException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/exception/SProcessEnablementException.java ================================================ /** * 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.core.process.definition.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SProcessEnablementException extends SBonitaException { private static final long serialVersionUID = -8844149610202633672L; public SProcessEnablementException(final Throwable cause) { super(cause); } public SProcessEnablementException(final String message) { super(message); } public SProcessEnablementException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SActivityDefinition.java ================================================ /** * 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.core.process.definition.model; import java.util.List; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition; import org.bonitasoft.engine.data.definition.model.SDataDefinition; /** * @author Elias Ricken de Medeiros * @author Zhao Na * @author Matthieu Chaffotte */ public interface SActivityDefinition extends SFlowNodeDefinition { SLoopCharacteristics getLoopCharacteristics(); List getSDataDefinitions(); List getBusinessDataDefinitions(); List getSOperations(); List getBoundaryEventDefinitions(); SBoundaryEventDefinition getBoundaryEventDefinition(String name) throws SBoundaryEventNotFoundException; SBusinessDataDefinition getBusinessDataDefinition(String name); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SActorDefinition.java ================================================ /** * 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.core.process.definition.model; /** * @author Matthieu Chaffotte */ public interface SActorDefinition extends SNamedElement { String getDescription(); boolean isInitiator(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SAutomaticTaskDefinition.java ================================================ /** * 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.core.process.definition.model; /** * @author Baptiste Mesta */ public interface SAutomaticTaskDefinition extends SActivityDefinition { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SBaseElement.java ================================================ /** * 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.core.process.definition.model; import java.io.Serializable; // FIXME put in a common module /** * @author Matthieu Chaffotte */ public interface SBaseElement extends Serializable { Long getId(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SBoundaryEventNotFoundException.java ================================================ /** * 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.core.process.definition.model; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SBoundaryEventNotFoundException extends SBonitaException { private static final long serialVersionUID = -3896999167981089031L; public SBoundaryEventNotFoundException(final String boundaryEventName, final String activityName) { super("No boundary event named " + boundaryEventName + " found in activity " + activityName); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SBusinessDataDefinition.java ================================================ /** * 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.core.process.definition.model; import java.io.Serializable; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Emmanuel Duchastenier */ public interface SBusinessDataDefinition extends Serializable { String getName(); String getDescription(); String getClassName(); SExpression getDefaultValueExpression(); boolean isMultiple(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SCallActivityDefinition.java ================================================ /** * 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.core.process.definition.model; import java.util.List; import java.util.Map; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros */ public interface SCallActivityDefinition extends SActivityDefinition { SExpression getCallableElement(); SExpression getCallableElementVersion(); List getDataInputOperations(); List getDataOutputOperations(); SCallableElementType getCallableElementType(); Map getProcessStartContractInputs(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SCallableElementType.java ================================================ /** * 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.core.process.definition.model; /** * @author Elias Ricken de Medeiros */ public enum SCallableElementType { PROCESS } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SConnectorDefinition.java ================================================ /** * 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.core.process.definition.model; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.connector.FailAction; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta */ public interface SConnectorDefinition extends SNamedElement { String getConnectorId(); String getVersion(); ConnectorEvent getActivationEvent(); Map getInputs(); List getOutputs(); FailAction getFailAction(); String getErrorCode(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SConstraintDefinition.java ================================================ /** * 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.core.process.definition.model; import java.util.List; /** * @author Matthieu Chaffotte */ public interface SConstraintDefinition extends SNamedElement { String getExpression(); String getExplanation(); List getInputNames(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SContextEntry.java ================================================ /** * 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.core.process.definition.model; import java.io.Serializable; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta */ public interface SContextEntry extends Serializable { String getKey(); SExpression getExpression(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SContractDefinition.java ================================================ /** * 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.core.process.definition.model; import java.util.List; /** * @author Matthieu Chaffotte */ public interface SContractDefinition extends SInputContainerDefinition { List getConstraints(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SDocumentDefinition.java ================================================ /** * 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.core.process.definition.model; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SDocumentDefinition extends SNamedElement { /** * URL for an external document * * @return */ String getUrl(); /** * File reference in the process resources * * @return */ String getFile(); /** * mime type of the document's content. */ String getMimeType(); /** * @return */ String getDescription(); /** * @return */ String getFileName(); SExpression getInitialValue(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SDocumentListDefinition.java ================================================ /** * 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.core.process.definition.model; import org.bonitasoft.engine.expression.model.SExpression; /** * A document list is a named element that define a list of document on a process * It contains 0 or more document and have a name to reference it in the process instance * It is initialized when the process start using the {@link #getExpression()} expression * * @author Baptiste Mesta * @since 6.4.0 */ public interface SDocumentListDefinition extends SNamedElement { /** * @return the description of the document list */ String getDescription(); /** * The expression that will be evaluated when we initialize the document list * * @return the initial value expression */ SExpression getExpression(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SFlowElementContainerDefinition.java ================================================ /** * 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.core.process.definition.model; import java.util.List; import java.util.Set; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SIntermediateCatchEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SIntermediateThrowEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition; import org.bonitasoft.engine.data.definition.model.SDataDefinition; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public interface SFlowElementContainerDefinition extends SBaseElement { Set getFlowNodes(); List getBoundaryEvents(); SBoundaryEventDefinition getBoundaryEvent(String name) throws SBoundaryEventNotFoundException; SFlowNodeDefinition getFlowNode(long id); Set getActivities(); Set getTransitions(); STransitionDefinition getTransition(String transitionId); Set getGateways(); SGatewayDefinition getGateway(String name); List getStartEvents(); List getIntermediateCatchEvents(); List getIntermdiateThrowEvents(); List getEndEvents(); List getDataDefinitions(); List getBusinessDataDefinitions(); SBusinessDataDefinition getBusinessDataDefinition(String name); List getDocumentDefinitions(); /** * @param name * the name of the connector definition * @return * the connector definition having that name * @since 6.1 */ SConnectorDefinition getConnectorDefinition(String name); List getConnectors(ConnectorEvent connectorEvent); List getConnectors(); SFlowNodeDefinition getFlowNode(String targetFlowNode); boolean containsInclusiveGateway(); /** * @return * @since 6.4.0 */ Set getSubProcessDefinitions(); /** * @return the document list definitions * @since 6.4.0 */ List getDocumentListDefinitions(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SFlowNodeDefinition.java ================================================ /** * 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.core.process.definition.model; import java.util.List; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Feng Hui * @author Zhao Na * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SFlowNodeDefinition extends SNamedElement { SFlowElementContainerDefinition getParentContainer(); /** * Gets the outgoing transitions of the activity. * * @return the outgoing transitions of the activity */ List getOutgoingTransitions(); /** * Gets the incoming transitions of the activity. * * @return the incoming transitions of the activity */ List getIncomingTransitions(); /** * Checks whether the activity has outgoing transitions. * * @return true if the activity contains outgoing transitions; false otherwise; */ boolean hasOutgoingTransitions(); /** * Checks whether the activity contains incoming transitions. * * @return true if the activity contains incoming transitions; false otherwise */ boolean hasIncomingTransitions(); STransitionDefinition getDefaultTransition(); List getConnectors(); /** * @return * @since 6.3 */ boolean hasConnectors(); /** * @param name * @return * @since 6.1 */ SConnectorDefinition getConnectorDefinition(String name); SFlowNodeType getType(); String getDescription(); SExpression getDisplayDescription(); SExpression getDisplayDescriptionAfterCompletion(); SExpression getDisplayName(); List getConnectors(ConnectorEvent connectorEvent); int getTransitionIndex(Long transitionId); boolean isStartable(); boolean isParalleleOrInclusive(); boolean isExclusive(); boolean isInterrupting(); boolean isBoundaryEvent(); boolean isEventSubProcess(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SFlowNodeType.java ================================================ /** * 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.core.process.definition.model; /** * @author Zhang Bole * @author Baptitse Mesta * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public enum SFlowNodeType { AUTOMATIC_TASK("automatic task"), USER_TASK("user task"), MANUAL_TASK("manual task"), RECEIVE_TASK("receive task"), SEND_TASK("send task"), GATEWAY("gateway"), START_EVENT("start event"), INTERMEDIATE_CATCH_EVENT("intermediate catch event"), BOUNDARY_EVENT("boundary event"), INTERMEDIATE_THROW_EVENT("intermediate throw event"), END_EVENT("end event"), LOOP_ACTIVITY("loop activity"), MULTI_INSTANCE_ACTIVITY("multi-instance activity"), CALL_ACTIVITY("call activity"), SUB_PROCESS("sub process"); private final String value; private SFlowNodeType(final String value) { this.value = value; } public String getValue() { return value; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SGatewayDefinition.java ================================================ /** * 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.core.process.definition.model; /** * @author Feng Hui * @author Matthieu Chaffotte */ public interface SGatewayDefinition extends SFlowNodeDefinition { SGatewayType getGatewayType(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SGatewayType.java ================================================ /** * 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.core.process.definition.model; /** * @author Feng Hui * @author Matthieu Chaffotte */ public enum SGatewayType { PARALLEL, INCLUSIVE, EXCLUSIVE } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SHumanTaskDefinition.java ================================================ /** * 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.core.process.definition.model; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta */ public interface SHumanTaskDefinition extends STaskDefinition { String getActorName(); SUserFilterDefinition getSUserFilterDefinition(); SExpression getExpectedDuration(); String getPriority(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SInputContainerDefinition.java ================================================ /** * 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.core.process.definition.model; import java.util.List; /** * @author Baptiste Mesta */ public interface SInputContainerDefinition extends SBaseElement { List getInputDefinitions(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SInputDefinition.java ================================================ /** * 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.core.process.definition.model; /** * @author Matthieu Chaffotte */ public interface SInputDefinition extends SNamedElement, SInputContainerDefinition { String getDescription(); boolean isMultiple(); SType getType(); boolean hasChildren(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SLoopCharacteristics.java ================================================ /** * 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.core.process.definition.model; import java.io.Serializable; /** * @author Matthieu Chaffotte */ public interface SLoopCharacteristics extends Serializable { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SManualTaskDefinition.java ================================================ /** * 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.core.process.definition.model; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SManualTaskDefinition extends SHumanTaskDefinition { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SMultiInstanceLoopCharacteristics.java ================================================ /** * 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.core.process.definition.model; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SMultiInstanceLoopCharacteristics extends SLoopCharacteristics { boolean isSequential(); SExpression getLoopCardinality(); SExpression getCompletionCondition(); String getLoopDataInputRef(); String getLoopDataOutputRef(); String getDataInputItemRef(); String getDataOutputItemRef(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SNamedElement.java ================================================ /** * 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.core.process.definition.model; // FIXME put in a common module /** * @author Matthieu Chaffotte */ public interface SNamedElement extends SBaseElement { /** * Gets the name of the element. * * @return the element name */ String getName(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SParameterDefinition.java ================================================ /** * 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.core.process.definition.model; /** * @author Matthieu Chaffotte */ public interface SParameterDefinition extends SNamedElement { String getDescription(); String getType(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SProcessDefinition.java ================================================ /** * 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.core.process.definition.model; import java.util.List; import java.util.Set; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public interface SProcessDefinition extends SNamedElement { String getVersion(); String getDescription(); SFlowElementContainerDefinition getProcessContainer(); SActorDefinition getActorInitiator(); Set getActors(); String getStringIndexLabel(int index); SExpression getStringIndexValue(int index); Set getParameters(); SParameterDefinition getParameter(String parameterName); boolean hasConnectors(); SContractDefinition getContract(); List getContext(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SProcessDefinitionDeployInfo.java ================================================ /** * 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.core.process.definition.model; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "process_definition") public class SProcessDefinitionDeployInfo implements PersistentObject { public static final String DESCRIPTION = "description"; public static final String ID_KEY = "id"; public static final String NAME_KEY = "name"; public static final String VERSION_KEY = "version"; public static final String DEPLOYMENT_DATE_KEY = "deploymentDate"; public static final String DEPLOYED_BY_KEY = "deployedBy"; public static final String ACTIVATION_STATE_KEY = "activationState"; public static final String CONFIGURATION_STATE_KEY = "configurationState"; public static final String PROCESS_ID_KEY = "processId"; public static final String DISPLAY_NAME_KEY = "displayName"; public static final String DISPLAY_DESCRIPTION_KEY = "displayDescription"; public static final String LAST_UPDATE_DATE_KEY = "lastUpdateDate"; public static final String ICON_PATH = "iconPath"; public static final String DESIGN_CONTENT = "designContent.content"; public static final String LABEL = "label"; private String name; @Id private long id; private long deploymentDate; private long deployedBy; private String version; private String description; private String configurationState; private String activationState; private long processId; private String displayName; private long lastUpdateDate; private String iconPath; private String displayDescription; @ManyToOne @JoinColumn(name = "content_id", referencedColumnName = "id") private SProcessDefinitionDesignContent designContent; } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SProcessDefinitionDesignContent.java ================================================ /** * Copyright (C) 2015 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.core.process.definition.model; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @AllArgsConstructor @Entity @Table(name = "process_content") @Builder public class SProcessDefinitionDesignContent implements PersistentObject { @Id private long id; private String content; } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SReceiveTaskDefinition.java ================================================ /** * 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.core.process.definition.model; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchMessageEventTriggerDefinition; /** * @author Julien Molinaro */ public interface SReceiveTaskDefinition extends SActivityDefinition { SCatchMessageEventTriggerDefinition getTrigger(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SSendTaskDefinition.java ================================================ /** * 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.core.process.definition.model; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition; /** * @author Baptiste Mesta */ public interface SSendTaskDefinition extends SActivityDefinition { SThrowMessageEventTriggerDefinition getMessageTrigger(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SStandardLoopCharacteristics.java ================================================ /** * 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.core.process.definition.model; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Matthieu Chaffotte */ public interface SStandardLoopCharacteristics extends SLoopCharacteristics { SExpression getLoopCondition(); boolean isTestBefore(); SExpression getLoopMax(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SSubProcessDefinition.java ================================================ /** * 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.core.process.definition.model; /** * @author Matthieu Chaffotte */ public interface SSubProcessDefinition extends SActivityDefinition { boolean isTriggeredByEvent(); SFlowElementContainerDefinition getSubProcessContainer(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/STaskDefinition.java ================================================ /** * 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.core.process.definition.model; /** * @author Baptiste Mesta */ public interface STaskDefinition extends SActivityDefinition { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/STransitionDefinition.java ================================================ /** * 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.core.process.definition.model; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface STransitionDefinition extends SNamedElement { long getSource(); long getTarget(); SExpression getCondition(); boolean hasCondition(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SType.java ================================================ /** * 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.core.process.definition.model; import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.util.Arrays; import java.util.Date; import java.util.List; import org.bonitasoft.engine.bpm.contract.FileInputValue; /** * @author Matthieu Chaffotte */ public enum SType { TEXT(String.class, Character.class), BOOLEAN(Boolean.class), DATE(Date.class), INTEGER(Integer.class, Long.class, BigInteger.class, Short.class, Byte.class), DECIMAL(Float.class, Double.class, BigDecimal.class, Integer.class, Long.class, BigInteger.class, Short.class, Byte.class), BYTE_ARRAY(byte[].class), FILE(FileInputValue.class), LONG(Long.class, Integer.class, BigInteger.class, Short.class, Byte.class), LOCALDATE(LocalDate.class), LOCALDATETIME( LocalDateTime.class), OFFSETDATETIME(OffsetDateTime.class); private final List> assignableTypes; SType(final Class... assignableTypes) { this.assignableTypes = Arrays.asList(assignableTypes); } public boolean validate(final Object object) { if (object == null) { return true; } for (final Class clazz : assignableTypes) { if (object.getClass().isAssignableFrom(clazz)) { return true; } } return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SUserFilterDefinition.java ================================================ /** * 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.core.process.definition.model; import java.util.Map; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta */ public interface SUserFilterDefinition extends SNamedElement { String getUserFilterId(); String getVersion(); Map getInputs(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/SUserTaskDefinition.java ================================================ /** * 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.core.process.definition.model; import java.util.List; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SUserTaskDefinition extends SHumanTaskDefinition { SContractDefinition getContract(); List getContext(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/TransitionState.java ================================================ /** * 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.core.process.definition.model; /** * @author Zhao Na * @author Elias Ricken de Medeiros */ public enum TransitionState { TAKEN, ABORTED } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SActorLogBuilder.java ================================================ /** * 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.core.process.definition.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Matthieu Chaffotte */ public interface SActorLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SActorLogBuilderFactory.java ================================================ /** * 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.core.process.definition.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Matthieu Chaffotte */ public interface SActorLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SBusinessDataDefinitionBuilder.java ================================================ /** * 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.core.process.definition.model.builder; import org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Emmanuel Duchastenier */ public interface SBusinessDataDefinitionBuilder { SBusinessDataDefinitionBuilder setDescription(String description); SBusinessDataDefinitionBuilder setDefaultValue(SExpression expression); SBusinessDataDefinitionBuilder setMultiple(boolean isMultiple); SBusinessDataDefinition done(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SBusinessDataDefinitionBuilderFactory.java ================================================ /** * 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.core.process.definition.model.builder; /** * @author Emmanuel Duchastenier */ public interface SBusinessDataDefinitionBuilderFactory { SBusinessDataDefinitionBuilder createNewInstance(String name, String className); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SProcessDefinitionBuilder.java ================================================ /** * 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.core.process.definition.model.builder; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SProcessDefinitionBuilder { SProcessDefinition done(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SProcessDefinitionBuilderFactory.java ================================================ /** * 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.core.process.definition.model.builder; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SProcessDefinitionBuilderFactory { SProcessDefinitionBuilder createNewInstance(DesignProcessDefinition processDefinition); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SProcessDefinitionDeployInfoUpdateBuilder.java ================================================ /** * 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.core.process.definition.model.builder; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Yanyan Liu * @author Baptiste Mesta * @author Celine Souchet */ public interface SProcessDefinitionDeployInfoUpdateBuilder { EntityUpdateDescriptor done(); SProcessDefinitionDeployInfoUpdateBuilder updateDisplayName(final String displayName); SProcessDefinitionDeployInfoUpdateBuilder updateActivationState(final ActivationState activationState); SProcessDefinitionDeployInfoUpdateBuilder updateConfigurationState(final ConfigurationState configurationState); SProcessDefinitionDeployInfoUpdateBuilder updateIconPath(final String iconPath); SProcessDefinitionDeployInfoUpdateBuilder updateDisplayDescription(String value); SProcessDefinitionDeployInfoUpdateBuilder updateDesignContent(String processDefinitionAsXMLString); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SProcessDefinitionDeployInfoUpdateBuilderFactory.java ================================================ /** * 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.core.process.definition.model.builder; /** * @author Yanyan Liu * @author Baptiste Mesta * @author Celine Souchet */ public interface SProcessDefinitionDeployInfoUpdateBuilderFactory { SProcessDefinitionDeployInfoUpdateBuilder createNewInstance(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SProcessDefinitionLogBuilder.java ================================================ /** * 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.core.process.definition.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Elias Ricken de Medeiros */ public interface SProcessDefinitionLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/SProcessDefinitionLogBuilderFactory.java ================================================ /** * 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.core.process.definition.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Elias Ricken de Medeiros */ public interface SProcessDefinitionLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { SProcessDefinitionLogBuilder createNewInstance(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/ServerModelConvertor.java ================================================ /** * 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.core.process.definition.model.builder; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.businessdata.BusinessDataDefinition; import org.bonitasoft.engine.bpm.data.DataDefinition; import org.bonitasoft.engine.bpm.data.TextDataDefinition; import org.bonitasoft.engine.bpm.data.XMLDataDefinition; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.SOperatorType; import org.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilderFactory; import org.bonitasoft.engine.core.operation.model.builder.SOperationBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory; import org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilderFactory; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory; import org.bonitasoft.engine.operation.Operation; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class ServerModelConvertor { public static SExpression convertExpression(final Expression value) { if (value == null) { return null; } final ArrayList dependencies = new ArrayList(); for (final Expression expression : value.getDependencies()) { dependencies.add(convertExpression(expression)); } try { return BuilderFactory.get(SExpressionBuilderFactory.class).createNewInstance().setName(value.getName()) .setContent(value.getContent()) .setExpressionType(value.getExpressionType()).setInterpreter(value.getInterpreter()) .setReturnType(value.getReturnType()) .setDependencies(dependencies).done(); } catch (final SInvalidExpressionException e) { throw new IllegalArgumentException("Error building SExpression", e); } } public static SOperation convertOperation(final Operation operation) { if (operation == null) { return null; } return BuilderFactory .get(SOperationBuilderFactory.class) .createNewInstance() .setOperator(operation.getOperator()) .setType(SOperatorType.valueOf(operation.getType().name())) .setRightOperand(ServerModelConvertor.convertExpression(operation.getRightOperand())) .setLeftOperand(BuilderFactory.get(SLeftOperandBuilderFactory.class).createNewInstance() .setName(operation.getLeftOperand().getName()) .setType(operation.getLeftOperand().getType()) .done()) .done(); } public static List convertOperations(final List operations) { if (operations == null) { return Collections.emptyList(); } final List sOperations = new ArrayList(operations.size()); for (final Operation operation : operations) { sOperations.add(convertOperation(operation)); } return sOperations; } public static SDataDefinition convertDataDefinition(final DataDefinition dataDefinition) { if (dataDefinition instanceof XMLDataDefinition xmlDataDef) { final SXMLDataDefinitionBuilderFactory fact = BuilderFactory.get(SXMLDataDefinitionBuilderFactory.class); final SXMLDataDefinitionBuilder builder = fact.createNewXMLData(dataDefinition.getName()) .setElement(xmlDataDef.getElement()) .setNamespace(xmlDataDef.getNamespace()); builder.setDefaultValue(ServerModelConvertor.convertExpression(dataDefinition.getDefaultValueExpression())); builder.setDescription(dataDefinition.getDescription()); builder.setTransient(dataDefinition.isTransientData()); return builder.done(); } final SDataDefinitionBuilderFactory fact = BuilderFactory.get(SDataDefinitionBuilderFactory.class); SDataDefinitionBuilder builder; if (dataDefinition instanceof TextDataDefinition textDataDefinition) { builder = fact.createNewTextData(dataDefinition.getName()).setAsLongText(textDataDefinition.isLongText()); } else { builder = fact.createNewInstance(dataDefinition.getName(), dataDefinition.getClassName()); } builder.setDefaultValue(ServerModelConvertor.convertExpression(dataDefinition.getDefaultValueExpression())); builder.setDescription(dataDefinition.getDescription()); builder.setTransient(dataDefinition.isTransientData()); return builder.done(); } public static SBusinessDataDefinition convertBusinessDataDefinition( final BusinessDataDefinition businessDataDefinition) { if (businessDataDefinition == null) { return null; } final SBusinessDataDefinitionBuilder builder = getSBusinessDataDefinitionBuilder(businessDataDefinition); builder.setDefaultValue( ServerModelConvertor.convertExpression(businessDataDefinition.getDefaultValueExpression())); builder.setDescription(businessDataDefinition.getDescription()); builder.setMultiple(businessDataDefinition.isMultiple()); return builder.done(); } protected static SBusinessDataDefinitionBuilder getSBusinessDataDefinitionBuilder( final BusinessDataDefinition businessDataDefinition) { final SBusinessDataDefinitionBuilderFactory fact = BuilderFactory .get(SBusinessDataDefinitionBuilderFactory.class); return fact.createNewInstance(businessDataDefinition.getName(), businessDataDefinition.getClassName()); } public static Map convertContractInputs(Map processStartContractInputs) { final HashMap serverContractInputs = new HashMap<>(processStartContractInputs.size()); for (Map.Entry entry : processStartContractInputs.entrySet()) { serverContractInputs.put(entry.getKey(), convertExpression(entry.getValue())); } return serverContractInputs; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SEndEventDefinitionBuilder.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger; import org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition; /** * @author Baptiste Mesta */ public interface SEndEventDefinitionBuilder { SEndEventDefinition done(); SEndEventDefinitionBuilder addErrorEventTriggerDefinition(SThrowErrorEventTriggerDefinition errorEventTrigger); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SEndEventDefinitionBuilderFactory.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger; /** * @author Baptiste Mesta */ public interface SEndEventDefinitionBuilderFactory { SEndEventDefinitionBuilder createNewInstance(String name); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SThrowErrorEventTriggerDefinitionBuilder.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition; /** * @author Baptiste Mesta */ public interface SThrowErrorEventTriggerDefinitionBuilder { SThrowErrorEventTriggerDefinition done(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SThrowErrorEventTriggerDefinitionBuilderFactory.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger; /** * @author Baptiste Mesta */ public interface SThrowErrorEventTriggerDefinitionBuilderFactory { SThrowErrorEventTriggerDefinitionBuilder createNewInstance(final String errorCode); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SThrowMessageEventTriggerDefinitionBuilder.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros */ public interface SThrowMessageEventTriggerDefinitionBuilder { SThrowMessageEventTriggerDefinitionBuilder addCorrelation(final SExpression key, final SExpression value); SThrowMessageEventTriggerDefinitionBuilder addData(SDataDefinition dataDefinition); SThrowMessageEventTriggerDefinition done(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SThrowMessageEventTriggerDefinitionBuilderFactory.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros */ public interface SThrowMessageEventTriggerDefinitionBuilderFactory { SThrowMessageEventTriggerDefinitionBuilder createNewInstance(final String name, final SExpression targetProcessName, final SExpression targetFlowNodeName); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SThrowSignalEventTriggerDefinitionBuilder.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowSignalEventTriggerDefinition; /** * @author Elias Ricken de Medeiros */ public interface SThrowSignalEventTriggerDefinitionBuilder { SThrowSignalEventTriggerDefinition done(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/SThrowSignalEventTriggerDefinitionBuilderFactory.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger; /** * @author Elias Ricken de Medeiros */ public interface SThrowSignalEventTriggerDefinitionBuilderFactory { SThrowSignalEventTriggerDefinitionBuilder createNewInstance(String signalName); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SEndEventDefinitionBuilderFactoryImpl.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger.impl; import java.util.UUID; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SEndEventDefinitionBuilder; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SEndEventDefinitionBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.event.impl.SEndEventDefinitionImpl; /** * @author Baptiste Mesta */ public class SEndEventDefinitionBuilderFactoryImpl implements SEndEventDefinitionBuilderFactory { @Override public SEndEventDefinitionBuilder createNewInstance(final String name) { final SEndEventDefinitionImpl entity = new SEndEventDefinitionImpl(UUID.randomUUID().getLeastSignificantBits(), name); return new SEndEventDefinitionBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SEndEventDefinitionBuilderImpl.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SEndEventDefinitionBuilder; import org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.impl.SEndEventDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition; /** * @author Baptiste Mesta */ public class SEndEventDefinitionBuilderImpl implements SEndEventDefinitionBuilder { private final SEndEventDefinitionImpl entity; public SEndEventDefinitionBuilderImpl(final SEndEventDefinitionImpl entity) { super(); this.entity = entity; } public SEndEventDefinition done() { return entity; } @Override public SEndEventDefinitionBuilder addErrorEventTriggerDefinition( final SThrowErrorEventTriggerDefinition errorEventTrigger) { entity.addErrorEventTriggerDefinition(errorEventTrigger); return this; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SThrowErrorEventTriggerDefinitionBuilderFactoryImpl.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowErrorEventTriggerDefinitionBuilder; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowErrorEventTriggerDefinitionBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowErrorEventTriggerDefinitionImpl; /** * @author Baptiste Mesta */ public class SThrowErrorEventTriggerDefinitionBuilderFactoryImpl implements SThrowErrorEventTriggerDefinitionBuilderFactory { @Override public SThrowErrorEventTriggerDefinitionBuilder createNewInstance(final String errorCode) { final SThrowErrorEventTriggerDefinition entity = new SThrowErrorEventTriggerDefinitionImpl(errorCode); return new SThrowErrorEventTriggerDefinitionBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SThrowErrorEventTriggerDefinitionBuilderImpl.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowErrorEventTriggerDefinitionBuilder; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition; /** * @author Baptiste Mesta */ public class SThrowErrorEventTriggerDefinitionBuilderImpl implements SThrowErrorEventTriggerDefinitionBuilder { private final SThrowErrorEventTriggerDefinition entity; public SThrowErrorEventTriggerDefinitionBuilderImpl(final SThrowErrorEventTriggerDefinition entity) { super(); this.entity = entity; } @Override public SThrowErrorEventTriggerDefinition done() { return entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SThrowMessageEventTriggerDefinitionBuilderFactoryImpl.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowMessageEventTriggerDefinitionBuilder; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowMessageEventTriggerDefinitionBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowMessageEventTriggerDefinitionImpl; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros */ public class SThrowMessageEventTriggerDefinitionBuilderFactoryImpl implements SThrowMessageEventTriggerDefinitionBuilderFactory { @Override public SThrowMessageEventTriggerDefinitionBuilder createNewInstance(final String name, final SExpression targetProcessName, final SExpression targetFlowNodeName) { final SThrowMessageEventTriggerDefinitionImpl entity = new SThrowMessageEventTriggerDefinitionImpl(); entity.setMessageName(name); entity.setTargetProcess(targetProcessName); entity.setTargetFlowNode(targetFlowNodeName); return new SThrowMessageEventTriggerDefinitionBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SThrowMessageEventTriggerDefinitionBuilderImpl.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowMessageEventTriggerDefinitionBuilder; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SCorrelationDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowMessageEventTriggerDefinitionImpl; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros */ public class SThrowMessageEventTriggerDefinitionBuilderImpl implements SThrowMessageEventTriggerDefinitionBuilder { private final SThrowMessageEventTriggerDefinitionImpl entity; public SThrowMessageEventTriggerDefinitionBuilderImpl(final SThrowMessageEventTriggerDefinitionImpl entity) { super(); this.entity = entity; } @Override public SThrowMessageEventTriggerDefinitionBuilder addCorrelation(final SExpression key, final SExpression value) { entity.addCorrelation(new SCorrelationDefinitionImpl(key, value)); return this; } @Override public SThrowMessageEventTriggerDefinitionBuilder addData(final SDataDefinition dataDefinition) { entity.addDataDefinition(dataDefinition); return this; } @Override public SThrowMessageEventTriggerDefinition done() { return entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SThrowSignalEventTriggerDefinitionBuilderFactoryImpl.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowSignalEventTriggerDefinitionBuilder; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowSignalEventTriggerDefinitionBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowSignalEventTriggerDefinitionImpl; /** * @author Elias Ricken de Medeiros */ public class SThrowSignalEventTriggerDefinitionBuilderFactoryImpl implements SThrowSignalEventTriggerDefinitionBuilderFactory { @Override public SThrowSignalEventTriggerDefinitionBuilder createNewInstance(final String signalName) { final SThrowSignalEventTriggerDefinitionImpl entity = new SThrowSignalEventTriggerDefinitionImpl(signalName); return new SThrowSignalEventTriggerDefinitionBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/event/trigger/impl/SThrowSignalEventTriggerDefinitionBuilderImpl.java ================================================ /** * 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.core.process.definition.model.builder.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowSignalEventTriggerDefinitionBuilder; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowSignalEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowSignalEventTriggerDefinitionImpl; /** * @author Elias Ricken de Medeiros */ public class SThrowSignalEventTriggerDefinitionBuilderImpl implements SThrowSignalEventTriggerDefinitionBuilder { private final SThrowSignalEventTriggerDefinitionImpl entity; public SThrowSignalEventTriggerDefinitionBuilderImpl(final SThrowSignalEventTriggerDefinitionImpl entity) { super(); this.entity = entity; } @Override public SThrowSignalEventTriggerDefinition done() { return entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/ProcessDefinitionLogIndexesMapper.java ================================================ /** * 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.core.process.definition.model.builder.impl; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public class ProcessDefinitionLogIndexesMapper { public static final int PROCESS_DEFINITION_INDEX = 0; public static final String PROCESS_DEFINITION_NAME = "numericIndex1"; } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SBusinessDataDefinitionBuilderFactoryImpl.java ================================================ /** * 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.core.process.definition.model.builder.impl; import org.bonitasoft.engine.core.process.definition.model.builder.SBusinessDataDefinitionBuilder; import org.bonitasoft.engine.core.process.definition.model.builder.SBusinessDataDefinitionBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.impl.SBusinessDataDefinitionImpl; /** * @author Emmanuel Duchastenier */ public class SBusinessDataDefinitionBuilderFactoryImpl implements SBusinessDataDefinitionBuilderFactory { @Override public SBusinessDataDefinitionBuilder createNewInstance(final String name, final String className) { final SBusinessDataDefinitionImpl businessDataDefinitionImpl = new SBusinessDataDefinitionImpl(); businessDataDefinitionImpl.setName(name); businessDataDefinitionImpl.setClassName(className); return new SBusinessDataDefinitionBuilderImpl(businessDataDefinitionImpl); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SBusinessDataDefinitionBuilderImpl.java ================================================ /** * 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.core.process.definition.model.builder.impl; import org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.SBusinessDataDefinitionBuilder; import org.bonitasoft.engine.core.process.definition.model.impl.SBusinessDataDefinitionImpl; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Emmanuel Duchastenier */ public class SBusinessDataDefinitionBuilderImpl implements SBusinessDataDefinitionBuilder { private final SBusinessDataDefinitionImpl businessDataDefinitionImpl; public SBusinessDataDefinitionBuilderImpl(final SBusinessDataDefinitionImpl businessDataDefinitionImpl) { this.businessDataDefinitionImpl = businessDataDefinitionImpl; } public static SBusinessDataDefinitionBuilder getInstance() { return new SBusinessDataDefinitionBuilderImpl(null); } @Override public SBusinessDataDefinitionBuilder setDescription(final String description) { businessDataDefinitionImpl.setDescription(description); return this; } @Override public SBusinessDataDefinitionBuilder setDefaultValue(final SExpression expression) { businessDataDefinitionImpl.setDefaultValueExpression(expression); return this; } @Override public SBusinessDataDefinitionBuilder setMultiple(boolean isMultiple) { businessDataDefinitionImpl.setMultiple(isMultiple); return this; } @Override public SBusinessDataDefinition done() { return businessDataDefinitionImpl; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SProcessDefinitionBuilderFactoryImpl.java ================================================ /** * 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.core.process.definition.model.builder.impl; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionBuilder; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl; /** * @author Baptiste Mesta */ public class SProcessDefinitionBuilderFactoryImpl implements SProcessDefinitionBuilderFactory { @Override public SProcessDefinitionBuilder createNewInstance(final DesignProcessDefinition processDefinition) { final SProcessDefinitionImpl entity = new SProcessDefinitionImpl(processDefinition); return new SProcessDefinitionBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SProcessDefinitionBuilderImpl.java ================================================ /** * 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.core.process.definition.model.builder.impl; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionBuilder; import org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl; /** * @author Baptiste Mesta */ public class SProcessDefinitionBuilderImpl implements SProcessDefinitionBuilder { private final SProcessDefinitionImpl entity; public SProcessDefinitionBuilderImpl(final SProcessDefinitionImpl entity) { super(); this.entity = entity; } @Override public SProcessDefinition done() { return entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SProcessDefinitionDeployInfoUpdateBuilderFactoryImpl.java ================================================ /** * 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.core.process.definition.model.builder.impl; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilder; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Yanyan Liu * @author Zhang Bole * @author Baptiste Mesta * @author Celine Souchet */ public class SProcessDefinitionDeployInfoUpdateBuilderFactoryImpl implements SProcessDefinitionDeployInfoUpdateBuilderFactory { public SProcessDefinitionDeployInfoUpdateBuilder createNewInstance() { return new SProcessDefinitionDeployInfoUpdateBuilderImpl(new EntityUpdateDescriptor()); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SProcessDefinitionDeployInfoUpdateBuilderImpl.java ================================================ /** * 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.core.process.definition.model.builder.impl; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Yanyan Liu * @author Zhang Bole * @author Baptiste Mesta * @author Celine Souchet */ public class SProcessDefinitionDeployInfoUpdateBuilderImpl implements SProcessDefinitionDeployInfoUpdateBuilder { private final EntityUpdateDescriptor descriptor; public SProcessDefinitionDeployInfoUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) { this.descriptor = descriptor; } @Override public EntityUpdateDescriptor done() { return descriptor; } @Override public SProcessDefinitionDeployInfoUpdateBuilder updateDisplayName(final String displayName) { descriptor.addField(SProcessDefinitionDeployInfo.DISPLAY_NAME_KEY, displayName); return this; } @Override public SProcessDefinitionDeployInfoUpdateBuilder updateDisplayDescription(final String description) { descriptor.addField(SProcessDefinitionDeployInfo.DISPLAY_DESCRIPTION_KEY, description); return this; } @Override public SProcessDefinitionDeployInfoUpdateBuilder updateActivationState(final ActivationState activationState) { descriptor.addField(SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY, activationState.name()); return this; } @Override public SProcessDefinitionDeployInfoUpdateBuilder updateConfigurationState( final ConfigurationState configurationState) { descriptor.addField(SProcessDefinitionDeployInfo.CONFIGURATION_STATE_KEY, configurationState.name()); return this; } @Override public SProcessDefinitionDeployInfoUpdateBuilder updateIconPath(final String iconPath) { descriptor.addField(SProcessDefinitionDeployInfo.ICON_PATH, iconPath); return this; } @Override public SProcessDefinitionDeployInfoUpdateBuilder updateDesignContent(String processDefinitionAsXMLString) { descriptor.addField(SProcessDefinitionDeployInfo.DESIGN_CONTENT, processDefinitionAsXMLString); return this; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SProcessDefinitionLogBuilderFactoryImpl.java ================================================ /** * 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.core.process.definition.model.builder.impl; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionLogBuilder; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Baptiste Mesta */ public class SProcessDefinitionLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SProcessDefinitionLogBuilderFactory { @Override public SProcessDefinitionLogBuilder createNewInstance() { return new SProcessDefinitionLogBuilderImpl(); } @Override public String getObjectIdKey() { return ProcessDefinitionLogIndexesMapper.PROCESS_DEFINITION_NAME; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SProcessDefinitionLogBuilderImpl.java ================================================ /** * 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.core.process.definition.model.builder.impl; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Baptiste Mesta */ public class SProcessDefinitionLogBuilderImpl extends CRUDELogBuilder implements SProcessDefinitionLogBuilder { private static final String PROCESS_DEFINITION = "PROCESS_DEFINITION"; @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(ProcessDefinitionLogIndexesMapper.PROCESS_DEFINITION_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return PROCESS_DEFINITION; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL) { if (log.getNumericIndex(ProcessDefinitionLogIndexesMapper.PROCESS_DEFINITION_INDEX) == 0L) { throw new MissingMandatoryFieldsException( "Some mandatory fields are missing: ProcessDefinition deployment info Id"); } } } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SBoundaryEventDefinition.java ================================================ /** * 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.core.process.definition.model.event; /** * @author Elias Ricken de Medeiros */ public interface SBoundaryEventDefinition extends SCatchEventDefinition { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SCatchEventDefinition.java ================================================ /** * 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.core.process.definition.model.event; import java.util.List; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchErrorEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchMessageEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchSignalEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.STimerEventTriggerDefinition; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SCatchEventDefinition extends SEventDefinition { List getTimerEventTriggerDefinitions(); List getMessageEventTriggerDefinitions(); SCatchMessageEventTriggerDefinition getMessageEventTriggerDefinition(final String messageName); List getSignalEventTriggerDefinitions(); List getErrorEventTriggerDefinitions(); SCatchErrorEventTriggerDefinition getErrorEventTriggerDefinition(final String errorCode); boolean isInterrupting(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SEndEventDefinition.java ================================================ /** * 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.core.process.definition.model.event; import java.util.List; import org.bonitasoft.engine.core.process.definition.model.event.trigger.STerminateEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SEndEventDefinition extends SThrowEventDefinition { STerminateEventTriggerDefinition getTerminateEventTriggerDefinition(); List getErrorEventTriggerDefinitions(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SEventDefinition.java ================================================ /** * 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.core.process.definition.model.event; import java.util.List; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition; /** * @author Elias Ricken de Medeiros */ public interface SEventDefinition extends SFlowNodeDefinition { /** * Retrieve an unmodifiable list of event triggers associate to this event. If none, an empty list is returned. * * @return an unmodifiable list of event triggers associate to this event */ List getEventTriggers(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SImplicitThrowEventDefinition.java ================================================ /** * 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.core.process.definition.model.event; /** * @author Elias Ricken de Medeiros */ public interface SImplicitThrowEventDefinition extends SThrowEventDefinition { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SIntermediateCatchEventDefinition.java ================================================ /** * 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.core.process.definition.model.event; /** * @author Elias Ricken de Medeiros */ public interface SIntermediateCatchEventDefinition extends SCatchEventDefinition { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SIntermediateThrowEventDefinition.java ================================================ /** * 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.core.process.definition.model.event; /** * @author Elias Ricken de Medeiros */ public interface SIntermediateThrowEventDefinition extends SThrowEventDefinition { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SStartEventDefinition.java ================================================ /** * 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.core.process.definition.model.event; /** * @author Elias Ricken de Medeiros */ public interface SStartEventDefinition extends SCatchEventDefinition { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/SThrowEventDefinition.java ================================================ /** * 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.core.process.definition.model.event; import java.util.List; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowSignalEventTriggerDefinition; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SThrowEventDefinition extends SEventDefinition { List getMessageEventTriggerDefinitions(); SThrowMessageEventTriggerDefinition getMessageEventTriggerDefinition(final String messageName); List getSignalEventTriggerDefinitions(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SBoundaryEventDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.impl; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.CatchEventDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SBoundaryEventDefinitionImpl extends SCatchEventDefinitionImpl implements SBoundaryEventDefinition { private static final long serialVersionUID = 7591508888593777075L; public SBoundaryEventDefinitionImpl(final long id, final String name) { super(id, name); } public SBoundaryEventDefinitionImpl(final CatchEventDefinition eventDefinition, final Map transitionsMap) { super(eventDefinition, transitionsMap); } @Override public SFlowNodeType getType() { return SFlowNodeType.BOUNDARY_EVENT; } @Override public boolean isStartable() { return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SCatchEventDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.impl; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.CatchEventDefinition; import org.bonitasoft.engine.bpm.flownode.CatchMessageEventTriggerDefinition; import org.bonitasoft.engine.bpm.flownode.ErrorEventTriggerDefinition; import org.bonitasoft.engine.bpm.flownode.SignalEventTriggerDefinition; import org.bonitasoft.engine.bpm.flownode.TimerEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SCatchEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchErrorEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchMessageEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchSignalEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.STimerEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SCatchErrorEventTriggerDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SCatchMessageEventTriggerDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SCatchSignalEventTriggerDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.STimerEventTriggerDefinitionImpl; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public abstract class SCatchEventDefinitionImpl extends SEventDefinitionImpl implements SCatchEventDefinition { private static final long serialVersionUID = -2803848099720886033L; private final List timerEventTriggers; private final List messageEventTriggers; private final List signalEventTriggers; private final List errorEventTriggers; private boolean isInterrupting = true; public SCatchEventDefinitionImpl(final CatchEventDefinition eventDefinition, final Map transitionsMap) { super(eventDefinition, transitionsMap); isInterrupting = eventDefinition.isInterrupting(); timerEventTriggers = new ArrayList( eventDefinition.getTimerEventTriggerDefinitions().size()); for (final TimerEventTriggerDefinition timerTrigger : eventDefinition.getTimerEventTriggerDefinitions()) { addTimerEventTrigger(new STimerEventTriggerDefinitionImpl(timerTrigger)); } messageEventTriggers = new ArrayList( eventDefinition.getMessageEventTriggerDefinitions().size()); for (final CatchMessageEventTriggerDefinition catchMessageTrigger : eventDefinition .getMessageEventTriggerDefinitions()) { addMessageEventTrigger(new SCatchMessageEventTriggerDefinitionImpl(catchMessageTrigger)); } signalEventTriggers = new ArrayList( eventDefinition.getSignalEventTriggerDefinitions().size()); for (final SignalEventTriggerDefinition signalTrigger : eventDefinition.getSignalEventTriggerDefinitions()) { addSignalEventTrigger(new SCatchSignalEventTriggerDefinitionImpl(signalTrigger.getSignalName())); } errorEventTriggers = new ArrayList( eventDefinition.getErrorEventTriggerDefinitions().size()); for (final ErrorEventTriggerDefinition errorTrigger : eventDefinition.getErrorEventTriggerDefinitions()) { addErrorEventTrigger(new SCatchErrorEventTriggerDefinitionImpl(errorTrigger.getErrorCode())); } } public SCatchEventDefinitionImpl(final long id, final String name) { super(id, name); timerEventTriggers = new ArrayList(1); messageEventTriggers = new ArrayList(5); signalEventTriggers = new ArrayList(1); errorEventTriggers = new ArrayList(1); } @Override public List getTimerEventTriggerDefinitions() { return Collections.unmodifiableList(timerEventTriggers); } public void addTimerEventTrigger(final STimerEventTriggerDefinition timerEventTrigger) { timerEventTriggers.add(timerEventTrigger); addEventTriggerDefinition(timerEventTrigger); } @Override public List getMessageEventTriggerDefinitions() { return Collections.unmodifiableList(messageEventTriggers); } public void addMessageEventTrigger(final SCatchMessageEventTriggerDefinition messageEventTrigger) { messageEventTriggers.add(messageEventTrigger); addEventTriggerDefinition(messageEventTrigger); } @Override public SCatchMessageEventTriggerDefinition getMessageEventTriggerDefinition(final String messageName) { final Iterator iterator = messageEventTriggers.iterator(); boolean found = false; SCatchMessageEventTriggerDefinition messageTrigger = null; while (iterator.hasNext() && !found) { final SCatchMessageEventTriggerDefinition sCatchMessageEventTriggerDefinition = iterator.next(); if (sCatchMessageEventTriggerDefinition.getMessageName().equals(messageName)) { found = true; messageTrigger = sCatchMessageEventTriggerDefinition; } } return messageTrigger; } @Override public List getSignalEventTriggerDefinitions() { return Collections.unmodifiableList(signalEventTriggers); } public void addSignalEventTrigger(final SCatchSignalEventTriggerDefinition signalEventTrigger) { signalEventTriggers.add(signalEventTrigger); addEventTriggerDefinition(signalEventTrigger); } public void setInterrupting(final boolean isInterrupting) { this.isInterrupting = isInterrupting; } @Override public boolean isInterrupting() { return isInterrupting; } @Override public List getErrorEventTriggerDefinitions() { return Collections.unmodifiableList(errorEventTriggers); } @Override public SCatchErrorEventTriggerDefinition getErrorEventTriggerDefinition(final String errorCode) { final Iterator iterator = errorEventTriggers.iterator(); boolean found = false; SCatchErrorEventTriggerDefinition trigger = null; while (iterator.hasNext() && !found) { final SCatchErrorEventTriggerDefinition currentTrigger = iterator.next(); if (currentTrigger.getErrorCode() == null && errorCode == null || currentTrigger.getErrorCode() != null && currentTrigger.getErrorCode().equals(errorCode)) { found = true; trigger = currentTrigger; } } return trigger; } public void addErrorEventTrigger(final SCatchErrorEventTriggerDefinition errorEventTrigger) { errorEventTriggers.add(errorEventTrigger); addEventTriggerDefinition(errorEventTrigger); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SEndEventDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.impl; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.EndEventDefinition; import org.bonitasoft.engine.bpm.flownode.TerminateEventTriggerDefinition; import org.bonitasoft.engine.bpm.flownode.ThrowErrorEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; 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.definition.model.event.trigger.STerminateEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.STerminateEventTriggerDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowErrorEventTriggerDefinitionImpl; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public class SEndEventDefinitionImpl extends SThrowEventDefinitionImpl implements SEndEventDefinition { private static final long serialVersionUID = 6671309950777321636L; private STerminateEventTriggerDefinition sTerminateEventTriggerDefinition; private final List sErrorEventTriggerDefinitions; public SEndEventDefinitionImpl(final EndEventDefinition eventDefinition, final Map transitionsMap) { super(eventDefinition, transitionsMap); final TerminateEventTriggerDefinition terminateEventTriggerDefinition = eventDefinition .getTerminateEventTriggerDefinition(); if (terminateEventTriggerDefinition != null) { setTerminateEventTriggerDefinition(new STerminateEventTriggerDefinitionImpl()); } final List errorEventTriggerDefinitions = eventDefinition .getErrorEventTriggerDefinitions(); sErrorEventTriggerDefinitions = new ArrayList( errorEventTriggerDefinitions.size()); for (final ThrowErrorEventTriggerDefinition throwErrorEventTriggerDefinition : errorEventTriggerDefinitions) { addErrorEventTriggerDefinition( new SThrowErrorEventTriggerDefinitionImpl(throwErrorEventTriggerDefinition.getErrorCode())); } } public SEndEventDefinitionImpl(final long id, final String name) { super(id, name); sErrorEventTriggerDefinitions = new ArrayList(1); } @Override public SFlowNodeType getType() { return SFlowNodeType.END_EVENT; } @Override public STerminateEventTriggerDefinition getTerminateEventTriggerDefinition() { return sTerminateEventTriggerDefinition; } public void setTerminateEventTriggerDefinition( final STerminateEventTriggerDefinition sTerminateEventTriggerDefinition) { this.sTerminateEventTriggerDefinition = sTerminateEventTriggerDefinition; if (sTerminateEventTriggerDefinition != null) { addEventTriggerDefinition(sTerminateEventTriggerDefinition); } } @Override public List getErrorEventTriggerDefinitions() { return Collections.unmodifiableList(sErrorEventTriggerDefinitions); } public void addErrorEventTriggerDefinition(final SThrowErrorEventTriggerDefinition errorEventTrigger) { sErrorEventTriggerDefinitions.add(errorEventTrigger); addEventTriggerDefinition(errorEventTrigger); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SEventDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.impl; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.EventDefinition; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.impl.SFlowNodeDefinitionImpl; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public abstract class SEventDefinitionImpl extends SFlowNodeDefinitionImpl implements SEventDefinition { private static final long serialVersionUID = -5019901548085906144L; private final List eventTriggers; public SEventDefinitionImpl(final EventDefinition eventDefinition, final Map transitionsMap) { super(eventDefinition, transitionsMap); // initialize the list have an initial capacity of 1: most of time there will be zero or one event trigger eventTriggers = new ArrayList(1); } public SEventDefinitionImpl(final long id, final String name) { super(id, name); eventTriggers = new ArrayList(1); } @Override public List getEventTriggers() { return Collections.unmodifiableList(eventTriggers); } protected void addEventTriggerDefinition(final SEventTriggerDefinition eventTrigger) { eventTriggers.add(eventTrigger); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SIntermediateCatchEventDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.impl; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.CatchEventDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SIntermediateCatchEventDefinition; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public class SIntermediateCatchEventDefinitionImpl extends SCatchEventDefinitionImpl implements SIntermediateCatchEventDefinition { private static final long serialVersionUID = -5011521635705533392L; public SIntermediateCatchEventDefinitionImpl(final CatchEventDefinition eventDefinition, final Map transitionsMap) { super(eventDefinition, transitionsMap); } public SIntermediateCatchEventDefinitionImpl(final long id, final String name) { super(id, name); } @Override public SFlowNodeType getType() { return SFlowNodeType.INTERMEDIATE_CATCH_EVENT; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SIntermediateThrowEventDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.impl; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.ThrowEventDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SIntermediateThrowEventDefinition; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SIntermediateThrowEventDefinitionImpl extends SThrowEventDefinitionImpl implements SIntermediateThrowEventDefinition { private static final long serialVersionUID = 5010523330087778508L; public SIntermediateThrowEventDefinitionImpl(final ThrowEventDefinition eventDefinition, final Map transitionsMap) { super(eventDefinition, transitionsMap); } public SIntermediateThrowEventDefinitionImpl(final long id, final String name) { super(id, name); } @Override public SFlowNodeType getType() { return SFlowNodeType.INTERMEDIATE_THROW_EVENT; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SStartEventDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.impl; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.StartEventDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SStartEventDefinitionImpl extends SCatchEventDefinitionImpl implements SStartEventDefinition { private static final long serialVersionUID = -8788360140531631436L; public SStartEventDefinitionImpl(final StartEventDefinition eventDefinition, final Map transitionsMap) { super(eventDefinition, transitionsMap); } public SStartEventDefinitionImpl(final long id, final String name) { super(id, name); } @Override public SFlowNodeType getType() { return SFlowNodeType.START_EVENT; } @Override public boolean isStartable() { return getEventTriggers().isEmpty() && super.isStartable(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SThrowEventDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.impl; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.ThrowEventDefinition; import org.bonitasoft.engine.bpm.flownode.ThrowMessageEventTriggerDefinition; import org.bonitasoft.engine.bpm.flownode.ThrowSignalEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SThrowEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowSignalEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowMessageEventTriggerDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowSignalEventTriggerDefinitionImpl; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public abstract class SThrowEventDefinitionImpl extends SEventDefinitionImpl implements SThrowEventDefinition { private static final long serialVersionUID = -3682882065277857160L; private final List sMessageEventTriggerDefinitions; private final List sSignalEventTriggerDefinitions; public SThrowEventDefinitionImpl(final ThrowEventDefinition eventDefinition, final Map transitionsMap) { super(eventDefinition, transitionsMap); final List messageEventTriggerDefinitions = eventDefinition .getMessageEventTriggerDefinitions(); sMessageEventTriggerDefinitions = new ArrayList( messageEventTriggerDefinitions.size()); for (final ThrowMessageEventTriggerDefinition throwMessageEventTriggerDefinition : messageEventTriggerDefinitions) { addMessageEventTriggerDefinition( new SThrowMessageEventTriggerDefinitionImpl(throwMessageEventTriggerDefinition)); } final List signalEventTriggerDefinitions = eventDefinition .getSignalEventTriggerDefinitions(); sSignalEventTriggerDefinitions = new ArrayList( signalEventTriggerDefinitions.size()); for (final ThrowSignalEventTriggerDefinition throwSignalEventTriggerDefinition : signalEventTriggerDefinitions) { addSignalEventTriggerDefinition( new SThrowSignalEventTriggerDefinitionImpl(throwSignalEventTriggerDefinition.getSignalName())); } } public SThrowEventDefinitionImpl(final long id, final String name) { super(id, name); sMessageEventTriggerDefinitions = new ArrayList(5); sSignalEventTriggerDefinitions = new ArrayList(1); } @Override public List getMessageEventTriggerDefinitions() { return Collections.unmodifiableList(sMessageEventTriggerDefinitions); } public void addMessageEventTriggerDefinition( final SThrowMessageEventTriggerDefinition messageEventTriggerDefinition) { addEventTriggerDefinition(messageEventTriggerDefinition); sMessageEventTriggerDefinitions.add(messageEventTriggerDefinition); } @Override public SThrowMessageEventTriggerDefinition getMessageEventTriggerDefinition(final String messageName) { final Iterator iterator = sMessageEventTriggerDefinitions.iterator(); boolean found = false; SThrowMessageEventTriggerDefinition messageTrigger = null; while (iterator.hasNext() && !found) { final SThrowMessageEventTriggerDefinition sThrowMessageEventTriggerDefinition = iterator.next(); if (sThrowMessageEventTriggerDefinition.getMessageName().equals(messageName)) { found = true; messageTrigger = sThrowMessageEventTriggerDefinition; } } return messageTrigger; } @Override public List getSignalEventTriggerDefinitions() { return Collections.unmodifiableList(sSignalEventTriggerDefinitions); } public void addSignalEventTriggerDefinition(final SThrowSignalEventTriggerDefinition signalEventTriggerDefinition) { addEventTriggerDefinition(signalEventTriggerDefinition); sSignalEventTriggerDefinitions.add(signalEventTriggerDefinition); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SCatchErrorEventTriggerDefinition.java ================================================ /** * 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.core.process.definition.model.event.trigger; /** * @author Elias Ricken de Medeiros */ public interface SCatchErrorEventTriggerDefinition extends SErrorEventTriggerDefinition { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SCatchMessageEventTriggerDefinition.java ================================================ /** * 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.core.process.definition.model.event.trigger; import java.util.List; import org.bonitasoft.engine.core.operation.model.SOperation; /** * @author Elias Ricken de Medeiros */ public interface SCatchMessageEventTriggerDefinition extends SMessageEventTriggerDefinition { List getOperations(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SCatchSignalEventTriggerDefinition.java ================================================ /** * 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.core.process.definition.model.event.trigger; /** * @author Matthieu Chaffotte */ public interface SCatchSignalEventTriggerDefinition extends SSignalEventTriggerDefinition { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SCorrelationDefinition.java ================================================ /** * 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.core.process.definition.model.event.trigger; import java.io.Serializable; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta */ public interface SCorrelationDefinition extends Serializable { SExpression getKey(); SExpression getValue(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SErrorEventTriggerDefinition.java ================================================ /** * 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.core.process.definition.model.event.trigger; /** * @author Elias Ricken de Medeiros */ public interface SErrorEventTriggerDefinition extends SEventTriggerDefinition { String getErrorCode(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SEventTriggerDefinition.java ================================================ /** * 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.core.process.definition.model.event.trigger; import org.bonitasoft.engine.core.process.definition.model.SBaseElement; /** * @author Elias Ricken de Medeiros */ public interface SEventTriggerDefinition extends SBaseElement { SEventTriggerType getEventTriggerType(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SEventTriggerType.java ================================================ /** * 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.core.process.definition.model.event.trigger; /** * @author Baptiste Mesta */ public enum SEventTriggerType { TIMER, ERROR, SIGNAL, MESSAGE, TERMINATE } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SMessageEventTriggerDefinition.java ================================================ /** * 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.core.process.definition.model.event.trigger; import java.util.List; /** * @author Elias Ricken de Medeiros */ public interface SMessageEventTriggerDefinition extends SEventTriggerDefinition { String getMessageName(); List getCorrelations(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SSignalEventTriggerDefinition.java ================================================ /** * 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.core.process.definition.model.event.trigger; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SSignalEventTriggerDefinition extends SEventTriggerDefinition { String getSignalName(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/STerminateEventTriggerDefinition.java ================================================ /** * 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.core.process.definition.model.event.trigger; /** * @author Elias Ricken de Medeiros */ public interface STerminateEventTriggerDefinition extends SEventTriggerDefinition { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SThrowErrorEventTriggerDefinition.java ================================================ /** * 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.core.process.definition.model.event.trigger; /** * @author Elias Ricken de Medeiros */ public interface SThrowErrorEventTriggerDefinition extends SErrorEventTriggerDefinition { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SThrowMessageEventTriggerDefinition.java ================================================ /** * 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.core.process.definition.model.event.trigger; import java.util.List; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros * @author Yanyan Liu */ public interface SThrowMessageEventTriggerDefinition extends SMessageEventTriggerDefinition { SExpression getTargetProcess(); SExpression getTargetFlowNode(); List getDataDefinitions(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/SThrowSignalEventTriggerDefinition.java ================================================ /** * 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.core.process.definition.model.event.trigger; /** * @author Matthieu Chaffotte */ public interface SThrowSignalEventTriggerDefinition extends SSignalEventTriggerDefinition { } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/STimerEventTriggerDefinition.java ================================================ /** * 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.core.process.definition.model.event.trigger; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros */ public interface STimerEventTriggerDefinition extends SEventTriggerDefinition { STimerType getTimerType(); SExpression getTimerExpression(); } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/STimerType.java ================================================ /** * 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.core.process.definition.model.event.trigger; /** * @author Elias Ricken de Medeiros */ public enum STimerType { CYCLE, DATE, DURATION } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SCatchErrorEventTriggerDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchErrorEventTriggerDefinition; /** * @author Elias Ricken de Medeiros */ public class SCatchErrorEventTriggerDefinitionImpl extends SErrorEventTriggerDefinitionImpl implements SCatchErrorEventTriggerDefinition { private static final long serialVersionUID = -8087566164595708656L; public SCatchErrorEventTriggerDefinitionImpl(final String errorCode) { super(errorCode); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SCatchMessageEventTriggerDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.trigger.impl; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.bpm.flownode.CatchMessageEventTriggerDefinition; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.SOperatorType; import org.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilderFactory; import org.bonitasoft.engine.core.operation.model.builder.SOperationBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchMessageEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCorrelationDefinition; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.operation.LeftOperand; import org.bonitasoft.engine.operation.Operation; /** * @author Elias Ricken de Medeiros */ public class SCatchMessageEventTriggerDefinitionImpl extends SMessageEventTriggerDefinitionImpl implements SCatchMessageEventTriggerDefinition { private static final long serialVersionUID = 8502224424679479589L; private final List sOperations; public SCatchMessageEventTriggerDefinitionImpl(final String messageName, final List operations, final List correlations) { super(messageName, correlations); sOperations = operations; } public SCatchMessageEventTriggerDefinitionImpl() { sOperations = new ArrayList(); } public SCatchMessageEventTriggerDefinitionImpl(final CatchMessageEventTriggerDefinition messageEventTrigger) { super(messageEventTrigger); final List operations = messageEventTrigger.getOperations(); sOperations = new ArrayList(operations.size()); for (final Operation operation : operations) { sOperations.add(toSOperation(operation)); } } public SCatchMessageEventTriggerDefinitionImpl( SCatchMessageEventTriggerDefinition catchMessageEventTriggerDefinition) { super(catchMessageEventTriggerDefinition); sOperations = catchMessageEventTriggerDefinition.getOperations(); } private SOperation toSOperation(final Operation operation) { final SExpression rightOperand = ServerModelConvertor.convertExpression(operation.getRightOperand()); final SOperatorType operatorType = SOperatorType.valueOf(operation.getType().name()); final SLeftOperand sLeftOperand = toSLeftOperand(operation.getLeftOperand()); final SOperation sOperation = BuilderFactory.get(SOperationBuilderFactory.class).createNewInstance() .setOperator(operation.getOperator()) .setRightOperand(rightOperand).setType(operatorType).setLeftOperand(sLeftOperand).done(); return sOperation; } private SLeftOperand toSLeftOperand(final LeftOperand variableToSet) { return BuilderFactory.get(SLeftOperandBuilderFactory.class).createNewInstance().setName(variableToSet.getName()) .setType(variableToSet.getType()).done(); } @Override public List getOperations() { return Collections.unmodifiableList(sOperations); } public void addOperation(final SOperation operation) { sOperations.add(operation); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SCatchSignalEventTriggerDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchSignalEventTriggerDefinition; /** * @author Matthieu Chaffotte */ public class SCatchSignalEventTriggerDefinitionImpl extends SSignalEventTriggerDefinitionImpl implements SCatchSignalEventTriggerDefinition { private static final long serialVersionUID = 6910759554558831366L; public SCatchSignalEventTriggerDefinitionImpl(final String signalName) { super(signalName); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SCorrelationDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCorrelationDefinition; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta */ public class SCorrelationDefinitionImpl implements SCorrelationDefinition { private static final long serialVersionUID = -8249911809919027354L; private SExpression key; private SExpression value; public SCorrelationDefinitionImpl(final SExpression key, final SExpression value) { super(); this.key = key; this.value = value; } @Override public SExpression getKey() { return key; } @Override public SExpression getValue() { return value; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (key == null ? 0 : key.hashCode()); result = prime * result + (value == null ? 0 : value.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final SCorrelationDefinitionImpl other = (SCorrelationDefinitionImpl) obj; if (key == null) { if (other.key != null) { return false; } } else if (!key.equals(other.key)) { return false; } if (value == null) { if (other.value != null) { return false; } } else if (!value.equals(other.value)) { return false; } return true; } public void setKey(final SExpression key) { this.key = key; } public void setValue(final SExpression value) { this.value = value; } @Override public String toString() { return "SCorrelationDefinitionImpl [key=" + key + ", value=" + value + "]"; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SErrorEventTriggerDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SErrorEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType; /** * @author Elias Ricken de Medeiros */ public abstract class SErrorEventTriggerDefinitionImpl extends SEventTriggerDefinitionImpl implements SErrorEventTriggerDefinition { private static final long serialVersionUID = -8002085238119587513L; private final String errorCode; public SErrorEventTriggerDefinitionImpl(final String errorCode) { this.errorCode = errorCode; } @Override public String getErrorCode() { return errorCode; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (errorCode == null ? 0 : errorCode.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final SErrorEventTriggerDefinitionImpl other = (SErrorEventTriggerDefinitionImpl) obj; if (errorCode == null) { if (other.errorCode != null) { return false; } } else if (!errorCode.equals(other.errorCode)) { return false; } return true; } @Override public SEventTriggerType getEventTriggerType() { return SEventTriggerType.ERROR; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SEventTriggerDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.impl.SBaseElementImpl; /** * @author Elias Ricken de Medeiros */ public abstract class SEventTriggerDefinitionImpl extends SBaseElementImpl implements SEventTriggerDefinition { private static final long serialVersionUID = -5798250125022606994L; } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SMessageEventTriggerDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.trigger.impl; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.bpm.data.DataDefinition; import org.bonitasoft.engine.bpm.data.TextDataDefinition; import org.bonitasoft.engine.bpm.data.XMLDataDefinition; import org.bonitasoft.engine.bpm.flownode.CorrelationDefinition; import org.bonitasoft.engine.bpm.flownode.MessageEventTriggerDefinition; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchMessageEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCorrelationDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SMessageEventTriggerDefinition; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory; import org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilderFactory; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public class SMessageEventTriggerDefinitionImpl extends SEventTriggerDefinitionImpl implements SMessageEventTriggerDefinition { private static final long serialVersionUID = 4603391860834299674L; private String messageName; private final List correlations; public SMessageEventTriggerDefinitionImpl(final String name, final List correlations) { messageName = name; this.correlations = correlations; } public SMessageEventTriggerDefinitionImpl() { correlations = new ArrayList(1); } public SMessageEventTriggerDefinitionImpl(final SCatchMessageEventTriggerDefinition trigger) { messageName = trigger.getMessageName(); correlations = trigger.getCorrelations(); } public SMessageEventTriggerDefinitionImpl(final MessageEventTriggerDefinition messageEventTrigger) { messageName = messageEventTrigger.getMessageName(); correlations = new ArrayList(messageEventTrigger.getCorrelations().size()); for (final CorrelationDefinition correlation : messageEventTrigger.getCorrelations()) { correlations .add(new SCorrelationDefinitionImpl(ServerModelConvertor.convertExpression(correlation.getKey()), ServerModelConvertor.convertExpression(correlation.getValue()))); } } @Override public String getMessageName() { return messageName; } @Override public List getCorrelations() { return Collections.unmodifiableList(correlations); } public void setMessageName(final String name) { messageName = name; } public void addCorrelation(final SCorrelationDefinition correlation) { correlations.add(correlation); } protected SDataDefinition buildSDataDefinition(final DataDefinition dataDefinition) { if (isXMLDataDefinition(dataDefinition)) { final XMLDataDefinition xmlDataDef = (XMLDataDefinition) dataDefinition; final SXMLDataDefinitionBuilderFactory fact = BuilderFactory.get(SXMLDataDefinitionBuilderFactory.class); final SXMLDataDefinitionBuilder builder = fact.createNewXMLData(messageName) .setElement(xmlDataDef.getElement()) .setNamespace(xmlDataDef.getNamespace()); builder.setDefaultValue(ServerModelConvertor.convertExpression(dataDefinition.getDefaultValueExpression())); builder.setDescription(dataDefinition.getDescription()); builder.setTransient(dataDefinition.isTransientData()); return builder.done(); } final SDataDefinitionBuilderFactory fact = BuilderFactory.get(SDataDefinitionBuilderFactory.class); SDataDefinitionBuilder builder = null; if (isTextDataDefinition(dataDefinition)) { final TextDataDefinition textDataDefinition = (TextDataDefinition) dataDefinition; builder = fact.createNewTextData(dataDefinition.getName()).setAsLongText(textDataDefinition.isLongText()); } else { builder = fact.createNewInstance(dataDefinition.getName(), dataDefinition.getClassName()); } builder.setDefaultValue(ServerModelConvertor.convertExpression(dataDefinition.getDefaultValueExpression())); builder.setDescription(dataDefinition.getDescription()); builder.setTransient(dataDefinition.isTransientData()); return builder.done(); } private boolean isXMLDataDefinition(final DataDefinition dataDefinition) { return dataDefinition instanceof XMLDataDefinition; } private boolean isTextDataDefinition(final DataDefinition dataDefinition) { return dataDefinition instanceof TextDataDefinition; } @Override public SEventTriggerType getEventTriggerType() { return SEventTriggerType.MESSAGE; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SSignalEventTriggerDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SSignalEventTriggerDefinition; /** * @author Matthieu Chaffotte */ public class SSignalEventTriggerDefinitionImpl extends SEventTriggerDefinitionImpl implements SSignalEventTriggerDefinition { private static final long serialVersionUID = 1762616027337330056L; private String signalName; public SSignalEventTriggerDefinitionImpl(final String signalName) { super(); this.signalName = signalName; } @Override public String getSignalName() { return signalName; } public void setSignalName(final String name) { signalName = name; } @Override public SEventTriggerType getEventTriggerType() { return SEventTriggerType.SIGNAL; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/STerminateEventTriggerDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType; import org.bonitasoft.engine.core.process.definition.model.event.trigger.STerminateEventTriggerDefinition; /** * @author Matthieu Chaffotte */ public class STerminateEventTriggerDefinitionImpl extends SEventTriggerDefinitionImpl implements STerminateEventTriggerDefinition { private static final long serialVersionUID = -8156042655414582732L; @Override public SEventTriggerType getEventTriggerType() { return SEventTriggerType.TERMINATE; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SThrowErrorEventTriggerDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition; /** * @author Elias Ricken de Medeiros */ public class SThrowErrorEventTriggerDefinitionImpl extends SErrorEventTriggerDefinitionImpl implements SThrowErrorEventTriggerDefinition { private static final long serialVersionUID = -8087566164595708656L; public SThrowErrorEventTriggerDefinitionImpl(final String errorCode) { super(errorCode); } @Override public SEventTriggerType getEventTriggerType() { return SEventTriggerType.ERROR; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SThrowMessageEventTriggerDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.trigger.impl; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.bpm.data.DataDefinition; import org.bonitasoft.engine.bpm.flownode.ThrowMessageEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros * @author Yanyan Liu */ public class SThrowMessageEventTriggerDefinitionImpl extends SMessageEventTriggerDefinitionImpl implements SThrowMessageEventTriggerDefinition { private static final long serialVersionUID = -513177194601607560L; private SExpression targetProcess; private SExpression targetFlowNode; private final List sDataDefinitions; public SThrowMessageEventTriggerDefinitionImpl() { sDataDefinitions = new ArrayList(); } public SThrowMessageEventTriggerDefinitionImpl(final ThrowMessageEventTriggerDefinition throwMessageEventTrigger) { super(throwMessageEventTrigger); final List dataDefinitions = throwMessageEventTrigger.getDataDefinitions(); sDataDefinitions = new ArrayList(dataDefinitions.size()); for (final DataDefinition dataDefinition : dataDefinitions) { sDataDefinitions.add(buildSDataDefinition(dataDefinition)); } targetProcess = ServerModelConvertor.convertExpression(throwMessageEventTrigger.getTargetProcess()); targetFlowNode = ServerModelConvertor.convertExpression(throwMessageEventTrigger.getTargetFlowNode()); } @Override public SExpression getTargetProcess() { return targetProcess; } @Override public SExpression getTargetFlowNode() { return targetFlowNode; } @Override public List getDataDefinitions() { return Collections.unmodifiableList(sDataDefinitions); } public void setTargetProcess(final SExpression targetProcess) { this.targetProcess = targetProcess; } public void setTargetFlowNode(final SExpression targetFlowNode) { this.targetFlowNode = targetFlowNode; } public void addDataDefinition(final SDataDefinition datadefiniton) { sDataDefinitions.add(datadefiniton); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/SThrowSignalEventTriggerDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.trigger.impl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowSignalEventTriggerDefinition; /** * @author Matthieu Chaffotte */ public class SThrowSignalEventTriggerDefinitionImpl extends SSignalEventTriggerDefinitionImpl implements SThrowSignalEventTriggerDefinition { private static final long serialVersionUID = -6873752170541970655L; public SThrowSignalEventTriggerDefinitionImpl(final String name) { super(name); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/event/trigger/impl/STimerEventTriggerDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.event.trigger.impl; import org.bonitasoft.engine.bpm.flownode.TimerEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType; import org.bonitasoft.engine.core.process.definition.model.event.trigger.STimerEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.STimerType; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros */ public class STimerEventTriggerDefinitionImpl extends SEventTriggerDefinitionImpl implements STimerEventTriggerDefinition { private static final long serialVersionUID = 1240658984583267877L; private final STimerType timerType; private final SExpression timerExpression; public STimerEventTriggerDefinitionImpl(final TimerEventTriggerDefinition timerTrigger) { timerType = STimerType.valueOf(timerTrigger.getTimerType().name()); final Expression expression = timerTrigger.getTimerExpression(); timerExpression = ServerModelConvertor.convertExpression(expression); } public STimerEventTriggerDefinitionImpl(final STimerType timerType, final SExpression timerExpression) { this.timerType = timerType; this.timerExpression = timerExpression; } @Override public STimerType getTimerType() { return timerType; } @Override public SExpression getTimerExpression() { return timerExpression; } @Override public SEventTriggerType getEventTriggerType() { return SEventTriggerType.TIMER; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SActivityDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.businessdata.BusinessDataDefinition; import org.bonitasoft.engine.bpm.data.DataDefinition; import org.bonitasoft.engine.bpm.flownode.ActivityDefinition; import org.bonitasoft.engine.bpm.flownode.BoundaryEventDefinition; import org.bonitasoft.engine.bpm.flownode.LoopCharacteristics; import org.bonitasoft.engine.bpm.flownode.MultiInstanceLoopCharacteristics; import org.bonitasoft.engine.bpm.flownode.StandardLoopCharacteristics; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SBoundaryEventNotFoundException; import org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition; import org.bonitasoft.engine.core.process.definition.model.SLoopCharacteristics; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.impl.SBoundaryEventDefinitionImpl; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.operation.Operation; /** * @author Matthieu Chaffotte * @author Frederic Bouquet * @author Celine Souchet */ public abstract class SActivityDefinitionImpl extends SFlowNodeDefinitionImpl implements SActivityDefinition { private static final long serialVersionUID = 8767258220640127769L; protected List sDataDefinitions = new ArrayList(); private final List businessDataDefinitions = new ArrayList(); protected List sOperations = new ArrayList(); protected SLoopCharacteristics loopCharacteristics; private final List sBoundaryEventDefinitions = new ArrayList(); public SActivityDefinitionImpl(final long id, final String name) { super(id, name); } public SActivityDefinitionImpl(final ActivityDefinition activityDefinition, final Map transitionsMap) { super(activityDefinition, transitionsMap); final List dataDefinitions = activityDefinition.getDataDefinitions(); for (final DataDefinition dataDefinition : dataDefinitions) { sDataDefinitions.add(ServerModelConvertor.convertDataDefinition(dataDefinition)); } for (final BusinessDataDefinition businessDataDefinition : activityDefinition.getBusinessDataDefinitions()) { businessDataDefinitions.add(ServerModelConvertor.convertBusinessDataDefinition(businessDataDefinition)); } final List operations = activityDefinition.getOperations(); for (final Operation operation : operations) { sOperations.add(ServerModelConvertor.convertOperation(operation)); } final LoopCharacteristics loop = activityDefinition.getLoopCharacteristics(); if (loop != null) { if (loop instanceof StandardLoopCharacteristics) { loopCharacteristics = new SStandardLoopCharacteristicsImpl((StandardLoopCharacteristics) loop); } else { loopCharacteristics = new SMultiInstanceLoopCharacteristicsImpl( (MultiInstanceLoopCharacteristics) loop); } } addBoundaryEvents(activityDefinition, transitionsMap); } private void addBoundaryEvents(final ActivityDefinition activityDefinition, final Map transitionsMap) { final List boundaryEventDefinitions = activityDefinition.getBoundaryEventDefinitions(); for (final BoundaryEventDefinition boundaryEventDefinition : boundaryEventDefinitions) { addBoundaryEventDefinition(new SBoundaryEventDefinitionImpl(boundaryEventDefinition, transitionsMap)); } } @Override public List getSOperations() { return sOperations; } @Override public List getSDataDefinitions() { return sDataDefinitions; } public void addSDataDefinition(final SDataDefinition sDataDefinition) { sDataDefinitions.add(sDataDefinition); } @Override public List getBoundaryEventDefinitions() { return Collections.unmodifiableList(sBoundaryEventDefinitions); } @Override public SBoundaryEventDefinition getBoundaryEventDefinition(final String name) throws SBoundaryEventNotFoundException { boolean found = false; SBoundaryEventDefinition boundary = null; final Iterator iterator = sBoundaryEventDefinitions.iterator(); while (iterator.hasNext() && !found) { final SBoundaryEventDefinition currentBoundary = iterator.next(); if (currentBoundary.getName().equals(name)) { boundary = currentBoundary; found = true; } } if (boundary == null) { throw new SBoundaryEventNotFoundException(name, getName()); } return boundary; } public void addBoundaryEventDefinition(final SBoundaryEventDefinition boundaryEventDefinition) { sBoundaryEventDefinitions.add(boundaryEventDefinition); } @Override public SLoopCharacteristics getLoopCharacteristics() { return loopCharacteristics; } public void setLoopCharacteristics(final SLoopCharacteristics loopCharacteristics) { this.loopCharacteristics = loopCharacteristics; } @Override public List getBusinessDataDefinitions() { return businessDataDefinitions; } @Override public SBusinessDataDefinition getBusinessDataDefinition(final String name) { if (name == null) { return null; } boolean found = false; SBusinessDataDefinition businessData = null; final Iterator iterator = businessDataDefinitions.iterator(); while (iterator.hasNext() && !found) { final SBusinessDataDefinition currentBusinessData = iterator.next(); if (currentBusinessData.getName().equals(name)) { found = true; businessData = currentBusinessData; } } return businessData; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SActorDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import org.bonitasoft.engine.bpm.actor.ActorDefinition; import org.bonitasoft.engine.core.process.definition.model.SActorDefinition; /** * @author Matthieu Chaffotte */ public class SActorDefinitionImpl extends SNamedElementImpl implements SActorDefinition { private static final long serialVersionUID = -3781827896225458787L; private String description; private boolean initiator; public SActorDefinitionImpl(final ActorDefinition actor) { super(actor.getName()); description = actor.getDescription(); initiator = actor.isInitiator(); } @Override public String getDescription() { return description; } public void setDescription(final String description) { this.description = description; } @Override public boolean isInitiator() { return initiator; } public void setInitiator(boolean initiator) { this.initiator = initiator; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SAutomaticTaskDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.ActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SAutomaticTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; /** * @author Baptiste Mesta * @author Celine Souchet */ public class SAutomaticTaskDefinitionImpl extends SActivityDefinitionImpl implements SAutomaticTaskDefinition { private static final long serialVersionUID = 96851790923787649L; public SAutomaticTaskDefinitionImpl(final ActivityDefinition activityDefinition, final Map transitionsMap) { super(activityDefinition, transitionsMap); } @Override public SFlowNodeType getType() { return SFlowNodeType.AUTOMATIC_TASK; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SBaseElementImpl.java ================================================ /** * 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.core.process.definition.model.impl; import org.bonitasoft.engine.core.process.definition.model.SBaseElement; /** * @author Matthieu Chaffotte */ public class SBaseElementImpl implements SBaseElement { private static final long serialVersionUID = -2401748455848222695L; private Long id; protected enum EQUALS_STATE { CONTINUE, RETURN_FALSE, RETURN_TRUE } @Override public Long getId() { return id; } public void setId(final Long id) { this.id = id; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (id == null ? 0 : id.hashCode()); return result; } protected EQUALS_STATE checkNaiveEquality(final Object obj) { if (this == obj) { return EQUALS_STATE.RETURN_TRUE; } else if (obj == null) { return EQUALS_STATE.RETURN_FALSE; } else if (getClass() != obj.getClass()) { return EQUALS_STATE.RETURN_FALSE; } else { return EQUALS_STATE.CONTINUE; } } @Override public boolean equals(final Object obj) { switch (checkNaiveEquality(obj)) { case RETURN_FALSE: return false; case RETURN_TRUE: return true; case CONTINUE: default: break; } final SBaseElementImpl other = (SBaseElementImpl) obj; if (id == null) { if (other.id != null) { return false; } } else if (!id.equals(other.id)) { return false; } return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SBusinessDataDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Emmanuel Duchastenier */ public class SBusinessDataDefinitionImpl implements SBusinessDataDefinition { private static final long serialVersionUID = 1L; private String name; private String description; private String className; private SExpression defaultValueExpression; private boolean multiple; public SBusinessDataDefinitionImpl() { super(); } @Override public String getName() { return name; } @Override public String getDescription() { return description; } @Override public String getClassName() { return className; } @Override public SExpression getDefaultValueExpression() { return defaultValueExpression; } public void setName(final String name) { this.name = name; } public void setDescription(final String description) { this.description = description; } public void setDefaultValueExpression(final SExpression defaultValueExpression) { this.defaultValueExpression = defaultValueExpression; } public void setClassName(final String className) { this.className = className; } @Override public boolean isMultiple() { return multiple; } public void setMultiple(final boolean multiple) { this.multiple = multiple; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SCallActivityDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.CallActivityDefinition; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.process.definition.model.SCallActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SCallableElementType; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SCallActivityDefinitionImpl extends SActivityDefinitionImpl implements SCallActivityDefinition { private static final long serialVersionUID = -5347512435504138388L; private SExpression callableElement; private SExpression callableElementVersion; private final List dataInputOperations; private final Map contractInputs; private final List dataOutputOperations; private SCallableElementType callableElementType; public SCallActivityDefinitionImpl(final long id, final String name) { super(id, name); dataInputOperations = new ArrayList<>(3); contractInputs = new HashMap<>(); dataOutputOperations = new ArrayList<>(3); } public SCallActivityDefinitionImpl(final CallActivityDefinition activityDefinition, final Map transitionsMap) { super(activityDefinition, transitionsMap); callableElement = ServerModelConvertor.convertExpression(activityDefinition.getCallableElement()); callableElementVersion = ServerModelConvertor.convertExpression(activityDefinition.getCallableElementVersion()); dataInputOperations = ServerModelConvertor.convertOperations(activityDefinition.getDataInputOperations()); contractInputs = ServerModelConvertor.convertContractInputs(activityDefinition.getProcessStartContractInputs()); dataOutputOperations = ServerModelConvertor.convertOperations(activityDefinition.getDataOutputOperations()); callableElementType = SCallableElementType.valueOf(activityDefinition.getCallableElementType().name()); } @Override public SExpression getCallableElement() { return callableElement; } @Override public SExpression getCallableElementVersion() { return callableElementVersion; } @Override public List getDataInputOperations() { return Collections.unmodifiableList(dataInputOperations); } @Override public List getDataOutputOperations() { return Collections.unmodifiableList(dataOutputOperations); } @Override public SCallableElementType getCallableElementType() { return callableElementType; } @Override public SFlowNodeType getType() { return SFlowNodeType.CALL_ACTIVITY; } @Override public Map getProcessStartContractInputs() { return contractInputs; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + (callableElement == null ? 0 : callableElement.hashCode()); result = prime * result + (callableElementType == null ? 0 : callableElementType.hashCode()); result = prime * result + (callableElementVersion == null ? 0 : callableElementVersion.hashCode()); result = prime * result + (dataInputOperations == null ? 0 : dataInputOperations.hashCode()); result = prime * result + (dataOutputOperations == null ? 0 : dataOutputOperations.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } final SCallActivityDefinitionImpl other = (SCallActivityDefinitionImpl) obj; if (callableElement == null) { if (other.callableElement != null) { return false; } } else if (!callableElement.equals(other.callableElement)) { return false; } if (callableElementType != other.callableElementType) { return false; } if (callableElementVersion == null) { if (other.callableElementVersion != null) { return false; } } else if (!callableElementVersion.equals(other.callableElementVersion)) { return false; } if (dataInputOperations == null) { if (other.dataInputOperations != null) { return false; } } else if (!dataInputOperations.equals(other.dataInputOperations)) { return false; } if (dataOutputOperations == null) { if (other.dataOutputOperations != null) { return false; } } else if (!dataOutputOperations.equals(other.dataOutputOperations)) { return false; } return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SConnectorDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.engine.bpm.connector.ConnectorDefinition; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.connector.FailAction; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.operation.Operation; /** * @author Baptiste Mesta */ public class SConnectorDefinitionImpl extends SNamedElementImpl implements SConnectorDefinition { private static final long serialVersionUID = 7953224084604802080L; private final ConnectorEvent activationEvent; private final Map inputs; private final List outputs; private final String connectorId; private final String version; private FailAction failAction; private String errorCode; public SConnectorDefinitionImpl(final ConnectorDefinition connector) { super(connector.getName()); activationEvent = connector.getActivationEvent(); connectorId = connector.getConnectorId(); version = connector.getVersion(); failAction = connector.getFailAction(); errorCode = connector.getErrorCode(); inputs = new HashMap<>(connector.getInputs().size()); for (final Entry input : connector.getInputs().entrySet()) { final Expression value = input.getValue(); if (value != null) { final SExpression sExpression = ServerModelConvertor.convertExpression(value); inputs.put(input.getKey(), sExpression);// creates SExpression } } outputs = new ArrayList<>(connector.getOutputs().size()); for (final Operation operation : connector.getOutputs()) { final SOperation sOperation = ServerModelConvertor.convertOperation(operation); outputs.add(sOperation); } // setId(connector.getId()); TODO : Implement generation of id } public SConnectorDefinitionImpl(final String name, final String connectorId, final String version, final ConnectorEvent activationEvent) { super(name); this.connectorId = connectorId; this.version = version; this.activationEvent = activationEvent; inputs = new HashMap<>(); outputs = new ArrayList<>(); } @Override public String getConnectorId() { return connectorId; } @Override public String getVersion() { return version; } @Override public ConnectorEvent getActivationEvent() { return activationEvent; } @Override public Map getInputs() { return inputs; } @Override public List getOutputs() { return outputs; } public void setErrorCode(final String errorCode) { this.errorCode = errorCode; } @Override public FailAction getFailAction() { return failAction; } @Override public String getErrorCode() { return errorCode; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SConstraintDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.bpm.contract.ConstraintDefinition; import org.bonitasoft.engine.core.process.definition.model.SConstraintDefinition; /** * @author Matthieu Chaffotte */ public class SConstraintDefinitionImpl extends SNamedElementImpl implements SConstraintDefinition { private static final long serialVersionUID = 1663246823634006952L; private final String expression; private final String explanation; private final List inputNames; public SConstraintDefinitionImpl(final String name, final String expression, final String explanation) { super(name); this.explanation = explanation; this.expression = expression; inputNames = new ArrayList<>(); } public SConstraintDefinitionImpl(final ConstraintDefinition rule) { this(rule.getName(), rule.getExpression(), rule.getExplanation()); for (final String inputName : rule.getInputNames()) { inputNames.add(inputName); } } @Override public String getExpression() { return expression; } @Override public String getExplanation() { return explanation; } @Override public List getInputNames() { return inputNames; } public void addInputName(final String inputName) { inputNames.add(inputName); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SContextEntryImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.Objects; import org.bonitasoft.engine.core.process.definition.model.SContextEntry; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta */ public class SContextEntryImpl implements SContextEntry { private String key; private SExpression expression; public SContextEntryImpl(String key, SExpression expression) { this.key = key; this.expression = expression; } @Override public String getKey() { return key; } public void setKey(String key) { this.key = key; } @Override public SExpression getExpression() { return expression; } public void setExpression(SExpression expression) { this.expression = expression; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SContextEntryImpl that = (SContextEntryImpl) o; return Objects.equals(key, that.key) && Objects.equals(expression, that.expression); } @Override public int hashCode() { return Objects.hash(key, expression); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SContractDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.ArrayList; import java.util.List; import java.util.Objects; import org.bonitasoft.engine.bpm.contract.ConstraintDefinition; import org.bonitasoft.engine.bpm.contract.ContractDefinition; import org.bonitasoft.engine.bpm.contract.InputDefinition; import org.bonitasoft.engine.core.process.definition.model.SConstraintDefinition; import org.bonitasoft.engine.core.process.definition.model.SContractDefinition; import org.bonitasoft.engine.core.process.definition.model.SInputDefinition; /** * @author Matthieu Chaffotte */ public class SContractDefinitionImpl extends SBaseElementImpl implements SContractDefinition { private static final long serialVersionUID = -5281686322739618159L; private final List inputDefinitions; private final List constraints; public SContractDefinitionImpl() { super(); inputDefinitions = new ArrayList<>(); constraints = new ArrayList<>(); } public SContractDefinitionImpl(final ContractDefinition contract) { this(); for (final InputDefinition input : contract.getInputs()) { inputDefinitions.add(new SInputDefinitionImpl(input)); } for (final ConstraintDefinition rule : contract.getConstraints()) { constraints.add(new SConstraintDefinitionImpl(rule)); } } @Override public List getInputDefinitions() { return inputDefinitions; } public void addInput(final SInputDefinition input) { inputDefinitions.add(input); } @Override public List getConstraints() { return constraints; } public void addConstraint(final SConstraintDefinition rule) { constraints.add(rule); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; SContractDefinitionImpl that = (SContractDefinitionImpl) o; return Objects.equals(inputDefinitions, that.inputDefinitions) && Objects.equals(constraints, that.constraints); } @Override public String toString() { return "SContractDefinitionImpl{" + "inputDefinitions=" + inputDefinitions + ", constraints=" + constraints + "} " + super.toString(); } @Override public int hashCode() { return Objects.hash(super.hashCode(), inputDefinitions, constraints); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SDocumentDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.Objects; import org.bonitasoft.engine.bpm.document.DocumentDefinition; import org.bonitasoft.engine.core.process.definition.model.SDocumentDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta */ public class SDocumentDefinitionImpl extends SNamedElementImpl implements SDocumentDefinition { private static final long serialVersionUID = 6622182823980202155L; private String url; private String file; private String mimeType; private String description; private String fileName; private SExpression initialValue; /** * @param name */ public SDocumentDefinitionImpl(final String name) { super(name); } /** * @param documentDefinition */ public SDocumentDefinitionImpl(final DocumentDefinition documentDefinition) { super(documentDefinition.getName()); url = documentDefinition.getUrl(); file = documentDefinition.getFile(); description = documentDefinition.getDescription(); mimeType = documentDefinition.getContentMimeType(); fileName = documentDefinition.getFileName(); initialValue = ServerModelConvertor.convertExpression(documentDefinition.getInitialValue()); } @Override public String getUrl() { return url; } @Override public String getFile() { return file; } @Override public String getMimeType() { return mimeType; } @Override public String getDescription() { return description; } public void setMimeType(final String mimeType) { this.mimeType = mimeType; } public void setUrl(final String url) { this.url = url; } public void setFile(final String file) { this.file = file; } public void setDescription(final String description) { this.description = description; } @Override public String getFileName() { return fileName; } public void setFileName(final String fileName) { this.fileName = fileName; } @Override public SExpression getInitialValue() { return initialValue; } public void setInitialValue(SExpression initialValue) { this.initialValue = initialValue; } @Override public String toString() { return "SDocumentDefinitionImpl{" + "url='" + url + '\'' + ", file='" + file + '\'' + ", mimeType='" + mimeType + '\'' + ", description='" + description + '\'' + ", fileName='" + fileName + '\'' + ", initialValue=" + initialValue + "} " + super.toString(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; SDocumentDefinitionImpl that = (SDocumentDefinitionImpl) o; return Objects.equals(url, that.url) && Objects.equals(file, that.file) && Objects.equals(mimeType, that.mimeType) && Objects.equals(description, that.description) && Objects.equals(fileName, that.fileName) && Objects.equals(initialValue, that.initialValue); } @Override public int hashCode() { return Objects.hash(super.hashCode(), url, file, mimeType, description, fileName, initialValue); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SDocumentListDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import org.bonitasoft.engine.bpm.document.DocumentListDefinition; import org.bonitasoft.engine.core.process.definition.model.SDocumentListDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta */ public class SDocumentListDefinitionImpl extends SNamedElementImpl implements SDocumentListDefinition { private static final long serialVersionUID = 1L; private String description; private SExpression expression; /** * @param name * the name of the list */ public SDocumentListDefinitionImpl(final String name) { super(name); } public SDocumentListDefinitionImpl(DocumentListDefinition documentListDefinition) { super(documentListDefinition.getName()); description = documentListDefinition.getDescription(); expression = ServerModelConvertor.convertExpression(documentListDefinition.getExpression()); } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public SExpression getExpression() { return expression; } public void setExpression(SExpression expression) { this.expression = expression; } @Override public String toString() { return "SDocumentDefinitionImpl{" + "description='" + description + '\'' + ", expression=" + expression + "} " + super.toString(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; SDocumentListDefinitionImpl that = (SDocumentListDefinitionImpl) o; if (description != null ? !description.equals(that.description) : that.description != null) return false; if (expression != null ? !expression.equals(that.expression) : that.expression != null) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (description != null ? description.hashCode() : 0); result = 31 * result + (expression != null ? expression.hashCode() : 0); return result; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SFlowElementContainerDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import org.bonitasoft.engine.bpm.businessdata.BusinessDataDefinition; import org.bonitasoft.engine.bpm.connector.ConnectorDefinition; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.data.DataDefinition; import org.bonitasoft.engine.bpm.document.DocumentDefinition; import org.bonitasoft.engine.bpm.document.DocumentListDefinition; import org.bonitasoft.engine.bpm.flownode.ActivityDefinition; import org.bonitasoft.engine.bpm.flownode.CallActivityDefinition; import org.bonitasoft.engine.bpm.flownode.EndEventDefinition; import org.bonitasoft.engine.bpm.flownode.FlowElementContainerDefinition; import org.bonitasoft.engine.bpm.flownode.GatewayDefinition; import org.bonitasoft.engine.bpm.flownode.IntermediateCatchEventDefinition; import org.bonitasoft.engine.bpm.flownode.IntermediateThrowEventDefinition; import org.bonitasoft.engine.bpm.flownode.StartEventDefinition; import org.bonitasoft.engine.bpm.flownode.TransitionDefinition; import org.bonitasoft.engine.bpm.flownode.UserTaskDefinition; import org.bonitasoft.engine.bpm.flownode.impl.internal.AutomaticTaskDefinitionImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.HumanTaskDefinitionImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ManualTaskDefinitionImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ReceiveTaskDefinitionImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.SendTaskDefinitionImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.UserTaskDefinitionImpl; import org.bonitasoft.engine.bpm.process.SubProcessDefinition; import org.bonitasoft.engine.bpm.userfilter.UserFilterDefinition; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; 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.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.SSubProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SIntermediateCatchEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SIntermediateThrowEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.impl.SEndEventDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.event.impl.SIntermediateCatchEventDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.event.impl.SIntermediateThrowEventDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.event.impl.SStartEventDefinitionImpl; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.exception.BonitaRuntimeException; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class SFlowElementContainerDefinitionImpl extends SBaseElementImpl implements SFlowElementContainerDefinition { private static final long serialVersionUID = -40122166157566478L; private final Map> connectorsMap; private final Map allElementsMapString; private final Map gatewaysMap; private final Map allElementsMap; private final Map allConnectorsMap; private final Map transitionsMap; private final Set activities; private final Set subProcessDefinitions; private final Set transitions; private final Set gateways; private final Set allElements; private final List connectors; private final List sStartEvents; private final List sIntermediateCatchEvents; private final List sBoundaryEvents; private final List sEndEvents; private final List sIntermediateThrowEvents; private final List sDataDefinitions; private final List sBusinessDataDefinitions; private final List sDocumentDefinitions; private final List sDocumentListDefinitions; private boolean containsInclusiveGateway = false; public SFlowElementContainerDefinitionImpl() { activities = new HashSet<>(); subProcessDefinitions = new HashSet<>(0); transitions = new HashSet<>(); transitionsMap = new HashMap<>(); gateways = new HashSet<>(); gatewaysMap = new HashMap<>(); allElements = new HashSet<>(); allElementsMap = new HashMap<>(); allElementsMapString = new HashMap<>(); connectors = new ArrayList<>(); connectorsMap = new HashMap<>(2); connectorsMap.put(ConnectorEvent.ON_ENTER, new ArrayList()); connectorsMap.put(ConnectorEvent.ON_FINISH, new ArrayList()); allConnectorsMap = new HashMap<>(2); sStartEvents = new ArrayList<>(); sIntermediateCatchEvents = new ArrayList<>(); sIntermediateThrowEvents = new ArrayList<>(); sEndEvents = new ArrayList<>(); sDataDefinitions = new ArrayList<>(); sBusinessDataDefinitions = new ArrayList<>(); sDocumentDefinitions = new ArrayList<>(); sDocumentListDefinitions = new ArrayList<>(); sBoundaryEvents = new ArrayList<>(); } public SFlowElementContainerDefinitionImpl(final FlowElementContainerDefinition container) { transitions = new HashSet<>(); transitionsMap = new HashMap<>(); for (final TransitionDefinition transition : container.getTransitions()) { final STransitionDefinitionImpl sTransitionDefinitionImpl = new STransitionDefinitionImpl(transition); addTransition(sTransitionDefinitionImpl); } allElements = new HashSet<>(); allElementsMap = new HashMap<>(); allElementsMapString = new HashMap<>(); final List activities2 = container.getActivities(); sBoundaryEvents = new ArrayList<>(); activities = new HashSet<>(activities2.size()); subProcessDefinitions = new HashSet<>(0); initializeActivities(activities2); final List gateways2 = container.getGatewaysList(); gateways = new HashSet<>(gateways2.size()); gatewaysMap = new HashMap<>(gateways2.size()); final Iterator iterator1 = gateways2.iterator(); while (iterator1.hasNext()) { final GatewayDefinition gatewayDefinition = iterator1.next(); final SGatewayDefinitionImpl gateway; gateway = new SGatewayDefinitionImpl(gatewayDefinition, transitionsMap); addGateway(gateway); } final List connectors2 = container.getConnectors(); final List mConnectors = new ArrayList<>(connectors2.size()); connectorsMap = new HashMap<>(2); connectorsMap.put(ConnectorEvent.ON_ENTER, new ArrayList()); connectorsMap.put(ConnectorEvent.ON_FINISH, new ArrayList()); allConnectorsMap = new HashMap<>(2); for (final ConnectorDefinition connector : connectors2) { final SConnectorDefinitionImpl e = new SConnectorDefinitionImpl(connector); mConnectors.add(e); connectorsMap.get(e.getActivationEvent()).add(e); allConnectorsMap.put(e.getName(), e); } connectors = Collections.unmodifiableList(mConnectors); sStartEvents = initializeStartEvents(container.getStartEvents(), transitionsMap); sIntermediateCatchEvents = initializeIntermediateCatchEvents(container.getIntermediateCatchEvents(), transitionsMap); sIntermediateThrowEvents = initializeIntermediateThrowEvents(container.getIntermediateThrowEvents(), transitionsMap); sEndEvents = initializeEndEvents(container.getEndEvents(), transitionsMap); final List businessDataDefinitions = container.getBusinessDataDefinitions(); final List mBusinessDataDefinitions = new ArrayList<>(businessDataDefinitions.size()); for (final BusinessDataDefinition businessDataDefinition : businessDataDefinitions) { mBusinessDataDefinitions.add(ServerModelConvertor.convertBusinessDataDefinition(businessDataDefinition)); } sBusinessDataDefinitions = Collections.unmodifiableList(mBusinessDataDefinitions); final List processDataDefinitions = container.getDataDefinitions(); final List mDataDefinitions = new ArrayList<>(processDataDefinitions.size()); for (final DataDefinition dataDefinition : processDataDefinitions) { mDataDefinitions.add(ServerModelConvertor.convertDataDefinition(dataDefinition)); } sDataDefinitions = Collections.unmodifiableList(mDataDefinitions); final List documentDefinitions2 = container.getDocumentDefinitions(); final List mDocumentDefinitions = new ArrayList<>(documentDefinitions2.size()); for (final DocumentDefinition documentDefinition : documentDefinitions2) { mDocumentDefinitions.add(new SDocumentDefinitionImpl(documentDefinition)); } sDocumentDefinitions = Collections.unmodifiableList(mDocumentDefinitions); final List documentListDefinitions2 = container.getDocumentListDefinitions(); final ArrayList mDocumentListDefinitions = new ArrayList<>( documentListDefinitions2.size()); for (final DocumentListDefinition documentListDefinition : documentListDefinitions2) { mDocumentListDefinitions.add(new SDocumentListDefinitionImpl(documentListDefinition)); } sDocumentListDefinitions = Collections.unmodifiableList(mDocumentListDefinitions); } private void initializeActivities(final List activities2) { final Iterator iterator = activities2.iterator(); while (iterator.hasNext()) { final ActivityDefinition activityDefinition = iterator.next(); final SActivityDefinitionImpl activity; if (activityDefinition instanceof AutomaticTaskDefinitionImpl) { activity = new SAutomaticTaskDefinitionImpl(activityDefinition, transitionsMap); } else if (activityDefinition instanceof HumanTaskDefinitionImpl humanTaskDefinitionImpl) { if (activityDefinition instanceof UserTaskDefinitionImpl) { activity = new SUserTaskDefinitionImpl((UserTaskDefinition) activityDefinition, transitionsMap); } else { activity = new SManualTaskDefinitionImpl((ManualTaskDefinitionImpl) activityDefinition, transitionsMap); } final UserFilterDefinition userFilter = humanTaskDefinitionImpl.getUserFilter(); final SHumanTaskDefinitionImpl sHumanTaskDefinitionImpl = (SHumanTaskDefinitionImpl) activity; if (userFilter != null) { sHumanTaskDefinitionImpl.setUserFilter(new SUserFilterDefinitionImpl(userFilter)); } sHumanTaskDefinitionImpl.setPriority(humanTaskDefinitionImpl.getPriority()); sHumanTaskDefinitionImpl.setExpectedDuration( ServerModelConvertor.convertExpression(humanTaskDefinitionImpl.getExpectedDuration())); } else if (activityDefinition instanceof ReceiveTaskDefinitionImpl) { activity = new SReceiveTaskDefinitionImpl((ReceiveTaskDefinitionImpl) activityDefinition, transitionsMap); } else if (activityDefinition instanceof SendTaskDefinitionImpl) { activity = new SSendTaskDefinitionImpl((SendTaskDefinitionImpl) activityDefinition, transitionsMap); } else if (activityDefinition instanceof CallActivityDefinition) { activity = new SCallActivityDefinitionImpl((CallActivityDefinition) activityDefinition, transitionsMap); } else if (activityDefinition instanceof SubProcessDefinition) { activity = new SSubProcessDefinitionImpl((SubProcessDefinition) activityDefinition); } else { throw new BonitaRuntimeException( "Can't find the client type for " + activityDefinition.getClass().getName()); } addActivity(activity); } } private List initializeEndEvents(final List endEvents, final Map transitionsMap) { final List sEndEvents = new ArrayList<>(endEvents.size()); for (final EndEventDefinition endEventDefinition : endEvents) { final SEndEventDefinitionImpl sEndEvent = new SEndEventDefinitionImpl(endEventDefinition, transitionsMap); sEndEvents.add(sEndEvent); addFlowNode(sEndEvent); } return sEndEvents; } private List initializeStartEvents(final List startEvents, final Map transitionsMap) { final List sStartEvents = new ArrayList<>(startEvents.size()); for (final StartEventDefinition startEventDefinition : startEvents) { final SStartEventDefinitionImpl sStartEventDefinitionImpl = new SStartEventDefinitionImpl( startEventDefinition, transitionsMap); sStartEvents.add(sStartEventDefinitionImpl); addFlowNode(sStartEventDefinitionImpl); } return sStartEvents; } private List initializeIntermediateCatchEvents( final List intermediateCatchEvents, final Map transitionsMap) { final List sIntermediateCatchEvents = new ArrayList<>( intermediateCatchEvents.size()); for (final IntermediateCatchEventDefinition intermediateCatchEventDefinition : intermediateCatchEvents) { final SIntermediateCatchEventDefinitionImpl sIntermediateCatchEvent = new SIntermediateCatchEventDefinitionImpl( intermediateCatchEventDefinition, transitionsMap); sIntermediateCatchEvents.add(sIntermediateCatchEvent); addFlowNode(sIntermediateCatchEvent); } return sIntermediateCatchEvents; } private List initializeIntermediateThrowEvents( final List intermediateThrowEvents, final Map transitionsMap) { final List sIntermediateThrowEvents = new ArrayList<>( intermediateThrowEvents.size()); for (final IntermediateThrowEventDefinition intermediateThrowEventDefinition : intermediateThrowEvents) { final SIntermediateThrowEventDefinitionImpl sIntermediateThrowEvent = new SIntermediateThrowEventDefinitionImpl( intermediateThrowEventDefinition, transitionsMap); sIntermediateThrowEvents.add(sIntermediateThrowEvent); addFlowNode(sIntermediateThrowEvent); } return sIntermediateThrowEvents; } public void addTransition(final STransitionDefinition transition) { transitions.add(transition); transitionsMap.put(transition.getId().toString(), transition); } public void addActivity(final SActivityDefinition activity) { sBoundaryEvents.addAll(activity.getBoundaryEventDefinitions()); for (final SBoundaryEventDefinition boundary : activity.getBoundaryEventDefinitions()) { sBoundaryEvents.add(boundary); allElements.add(boundary); allElementsMap.put(boundary.getId(), boundary); } activities.add(activity); addFlowNode(activity); if (activity instanceof SSubProcessDefinition) { addSubProcess((SSubProcessDefinition) activity); } } public void addEvent(SEventDefinition eventDefinition) { addFlowNode(eventDefinition); } private void addFlowNode(SFlowNodeDefinition flowNodeDefinition) { allElements.add(flowNodeDefinition); allElementsMap.put(flowNodeDefinition.getId(), flowNodeDefinition); allElementsMapString.put(flowNodeDefinition.getName(), flowNodeDefinition); } public void addSubProcess(final SSubProcessDefinition sSubProcessDefinition) { subProcessDefinitions.add(sSubProcessDefinition); } public void addGateway(final SGatewayDefinition gateway) { gateways.add(gateway); if (gateway.getGatewayType() == SGatewayType.INCLUSIVE) { containsInclusiveGateway = true; } gatewaysMap.put(gateway.getName(), gateway); addFlowNode(gateway); } public void addBusinessDataDefinition(final SBusinessDataDefinition businessDataDefinition) { sBusinessDataDefinitions.add(businessDataDefinition); } @Override public Set getGateways() { return gateways; } @Override public SGatewayDefinition getGateway(final String name) { SGatewayDefinition sGatewayDefinition = gatewaysMap.get(name); if (sGatewayDefinition == null) { sGatewayDefinition = getGatewayFromSubProcesses(name); } return sGatewayDefinition; } private SGatewayDefinition getGatewayFromSubProcesses(final String name) { boolean found = false; SGatewayDefinition gateway = null; final Iterator iterator = activities.iterator(); while (iterator.hasNext() && !found) { final SActivityDefinition activityDefinition = iterator.next(); if (SFlowNodeType.SUB_PROCESS.equals(activityDefinition.getType())) { gateway = ((SSubProcessDefinition) activityDefinition).getSubProcessContainer().getGateway(name); if (gateway != null) { found = true; } } } return gateway; } @Override public Set getFlowNodes() { return allElements; } @Override public SFlowNodeDefinition getFlowNode(final long id) { SFlowNodeDefinition flowNodeDefinition = allElementsMap.get(id); if (flowNodeDefinition == null) { flowNodeDefinition = getFlowNodeFromSubProcesses(id); } return flowNodeDefinition; } @Override public SFlowNodeDefinition getFlowNode(final String targetFlowNode) { SFlowNodeDefinition flowNodeDefinition = allElementsMapString.get(targetFlowNode); if (flowNodeDefinition == null) { flowNodeDefinition = getFlowNodeFromSubProcesses(targetFlowNode); } return flowNodeDefinition; } private SFlowNodeDefinition getFlowNodeFromSubProcesses(final long id) { boolean found = false; SFlowNodeDefinition flowNode = null; final Iterator iterator = activities.iterator(); while (iterator.hasNext() && !found) { final SActivityDefinition activityDefinition = iterator.next(); if (SFlowNodeType.SUB_PROCESS.equals(activityDefinition.getType())) { flowNode = ((SSubProcessDefinition) activityDefinition).getSubProcessContainer().getFlowNode(id); if (flowNode != null) { found = true; } } } return flowNode; } private SFlowNodeDefinition getFlowNodeFromSubProcesses(final String targetFlowNode) { boolean found = false; SFlowNodeDefinition flowNode = null; final Iterator iterator = activities.iterator(); while (iterator.hasNext() && !found) { final SActivityDefinition activityDefinition = iterator.next(); if (SFlowNodeType.SUB_PROCESS.equals(activityDefinition.getType())) { flowNode = ((SSubProcessDefinition) activityDefinition).getSubProcessContainer() .getFlowNode(targetFlowNode); if (flowNode != null) { found = true; } } } return flowNode; } @Override public STransitionDefinition getTransition(final String transitionId) { STransitionDefinition transitionDefinition = transitionsMap.get(transitionId); if (transitionDefinition == null) { transitionDefinition = getTransitionFromSubProcesses(transitionId); } return transitionDefinition; } private STransitionDefinition getTransitionFromSubProcesses(final String transitionId) { boolean found = false; STransitionDefinition transition = null; final Iterator iterator = activities.iterator(); while (iterator.hasNext() && !found) { final SActivityDefinition activityDefinition = iterator.next(); if (SFlowNodeType.SUB_PROCESS.equals(activityDefinition.getType())) { transition = ((SSubProcessDefinition) activityDefinition).getSubProcessContainer() .getTransition(transitionId); if (transition != null) { found = true; } } } return transition; } @Override public List getConnectors() { return connectors; } @Override // public SConnectorDefinition getConnectorDefinition(final long id) {// FIXME: Uncomment when generate id public SConnectorDefinition getConnectorDefinition(final String name) { return allConnectorsMap.get(name); } @Override public List getConnectors(final ConnectorEvent connectorEvent) { return connectorsMap.get(connectorEvent); } @Override public List getStartEvents() { return sStartEvents; } @Override public List getIntermediateCatchEvents() { return sIntermediateCatchEvents; } @Override public List getEndEvents() { return sEndEvents; } @Override public List getBusinessDataDefinitions() { return sBusinessDataDefinitions; } @Override public SBusinessDataDefinition getBusinessDataDefinition(final String name) { if (name == null) { return null; } boolean found = false; SBusinessDataDefinition businessData = null; final Iterator iterator = sBusinessDataDefinitions.iterator(); while (iterator.hasNext() && !found) { final SBusinessDataDefinition currentBusinessData = iterator.next(); if (currentBusinessData.getName().equals(name)) { found = true; businessData = currentBusinessData; } } return businessData; } @Override public List getDataDefinitions() { return sDataDefinitions; } @Override public List getIntermdiateThrowEvents() { return Collections.unmodifiableList(sIntermediateThrowEvents); } @Override public List getDocumentDefinitions() { return sDocumentDefinitions; } @Override public Set getActivities() { return activities; } @Override public Set getSubProcessDefinitions() { return subProcessDefinitions; } @Override public Set getTransitions() { return transitions; } @Override public List getBoundaryEvents() { return Collections.unmodifiableList(sBoundaryEvents); } @Override public SBoundaryEventDefinition getBoundaryEvent(final String name) { boolean found = false; SBoundaryEventDefinition boundaryEvent = null; final Iterator iterator = sBoundaryEvents.iterator(); while (iterator.hasNext() && !found) { final SBoundaryEventDefinition currentBoundaryEvent = iterator.next(); if (currentBoundaryEvent.getName().equals(name)) { found = true; boundaryEvent = currentBoundaryEvent; } } return boundaryEvent; } @Override public boolean containsInclusiveGateway() { return containsInclusiveGateway; } /** * @return the document list definitions * @since 6.4.0 */ @Override public List getDocumentListDefinitions() { return sDocumentListDefinitions; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; SFlowElementContainerDefinitionImpl that = (SFlowElementContainerDefinitionImpl) o; return containsInclusiveGateway == that.containsInclusiveGateway && Objects.equals(connectorsMap, that.connectorsMap) && Objects.equals(allElementsMapString, that.allElementsMapString) && Objects.equals(gatewaysMap, that.gatewaysMap) && Objects.equals(allElementsMap, that.allElementsMap) && Objects.equals(allConnectorsMap, that.allConnectorsMap) && Objects.equals(transitionsMap, that.transitionsMap) && Objects.equals(activities, that.activities) && Objects.equals(subProcessDefinitions, that.subProcessDefinitions) && Objects.equals(transitions, that.transitions) && Objects.equals(gateways, that.gateways) && Objects.equals(allElements, that.allElements) && Objects.equals(connectors, that.connectors) && Objects.equals(sStartEvents, that.sStartEvents) && Objects.equals(sIntermediateCatchEvents, that.sIntermediateCatchEvents) && Objects.equals(sBoundaryEvents, that.sBoundaryEvents) && Objects.equals(sEndEvents, that.sEndEvents) && Objects.equals(sIntermediateThrowEvents, that.sIntermediateThrowEvents) && Objects.equals(sDataDefinitions, that.sDataDefinitions) && Objects.equals(sBusinessDataDefinitions, that.sBusinessDataDefinitions) && Objects.equals(sDocumentDefinitions, that.sDocumentDefinitions) && Objects.equals(sDocumentListDefinitions, that.sDocumentListDefinitions); } @Override public int hashCode() { return Objects.hash(super.hashCode(), connectorsMap, allElementsMapString, gatewaysMap, allElementsMap, allConnectorsMap, transitionsMap, activities, subProcessDefinitions, transitions, gateways, allElements, connectors, sStartEvents, sIntermediateCatchEvents, sBoundaryEvents, sEndEvents, sIntermediateThrowEvents, sDataDefinitions, sBusinessDataDefinitions, sDocumentDefinitions, sDocumentListDefinitions, containsInclusiveGateway); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SFlowNodeDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.connector.ConnectorDefinition; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.flownode.FlowNodeDefinition; import org.bonitasoft.engine.bpm.flownode.TransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition; 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.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Feng Hui * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public abstract class SFlowNodeDefinitionImpl extends SNamedElementImpl implements SFlowNodeDefinition { private static final long serialVersionUID = 7475429470423228259L; private final List incomings; private final List outgoings; private STransitionDefinition defaultTransition; private final List connectors; private final Map allConnectorsMap; private String description; private SExpression displayDescription; private SExpression displayDescriptionAfterCompletion; private SExpression displayName; private SFlowElementContainerDefinition parentContainer; private final Map> connectorsMap; public SFlowNodeDefinitionImpl(final FlowNodeDefinition flowNodeDefinition, final Map sTransitionsMap) { super(flowNodeDefinition.getName()); incomings = buildIncomingTransitions(flowNodeDefinition, sTransitionsMap); outgoings = buildOutGoingTransitions(flowNodeDefinition, sTransitionsMap); if (flowNodeDefinition.getDefaultTransition() != null) { defaultTransition = sTransitionsMap.get(String.valueOf(flowNodeDefinition.getDefaultTransition().getId())); } final List connectors2 = flowNodeDefinition.getConnectors(); final ArrayList mConnectors = new ArrayList<>(connectors2.size()); connectorsMap = new HashMap<>(2); connectorsMap.put(ConnectorEvent.ON_ENTER, new ArrayList()); connectorsMap.put(ConnectorEvent.ON_FINISH, new ArrayList()); allConnectorsMap = new HashMap<>(2); for (final ConnectorDefinition connector : connectors2) { final SConnectorDefinitionImpl e = new SConnectorDefinitionImpl(connector); mConnectors.add(e); connectorsMap.get(e.getActivationEvent()).add(e); allConnectorsMap.put(e.getName(), e); } connectors = Collections.unmodifiableList(mConnectors); description = flowNodeDefinition.getDescription(); displayDescription = ServerModelConvertor.convertExpression(flowNodeDefinition.getDisplayDescription()); displayDescriptionAfterCompletion = ServerModelConvertor .convertExpression(flowNodeDefinition.getDisplayDescriptionAfterCompletion()); displayName = ServerModelConvertor.convertExpression(flowNodeDefinition.getDisplayName()); setId(flowNodeDefinition.getId()); } public SFlowNodeDefinitionImpl(final long id, final String name) { super(name); setId(id); incomings = new ArrayList<>(); outgoings = new ArrayList<>(); connectors = new ArrayList<>(); connectorsMap = new HashMap<>(2); connectorsMap.put(ConnectorEvent.ON_ENTER, new ArrayList()); connectorsMap.put(ConnectorEvent.ON_FINISH, new ArrayList()); allConnectorsMap = new HashMap<>(2); } @Override public SFlowElementContainerDefinition getParentContainer() { return parentContainer; } private List buildOutGoingTransitions(final FlowNodeDefinition nodeDefinition, final Map sTransitionsMap) { Iterator iterator; final List outgoingTransitions = nodeDefinition.getOutgoingTransitions(); final List outgoings = new ArrayList<>(); iterator = outgoingTransitions.iterator(); while (iterator.hasNext()) { final TransitionDefinition sTransition = iterator.next(); final STransitionDefinition outgoing = sTransitionsMap.get(String.valueOf(sTransition.getId())); outgoings.add(outgoing); } return outgoings; } private List buildIncomingTransitions(final FlowNodeDefinition nodeDefinition, final Map sTransitionsMap) { final List incomingTransitions = nodeDefinition.getIncomingTransitions(); final List incomings = new ArrayList<>(); final Iterator iterator = incomingTransitions.iterator(); while (iterator.hasNext()) { final TransitionDefinition sTransition = iterator.next(); final STransitionDefinition incoming = sTransitionsMap.get(String.valueOf(sTransition.getId())); incomings.add(incoming); } return incomings; } @Override public List getOutgoingTransitions() { return Collections.unmodifiableList(outgoings); } @Override public List getIncomingTransitions() { return Collections.unmodifiableList(incomings); } @Override public List getConnectors() { return Collections.unmodifiableList(connectors); } @Override public boolean hasConnectors() { return connectors.size() > 0; } @Override // public SConnectorDefinition getConnectorDefinition(final long id) { // FIXME: Uncomment when generate id public SConnectorDefinition getConnectorDefinition(final String name) { return allConnectorsMap.get(name); } @Override public STransitionDefinition getDefaultTransition() { return defaultTransition; } @Override public boolean hasIncomingTransitions() { return !incomings.isEmpty(); } @Override public boolean hasOutgoingTransitions() { return !outgoings.isEmpty(); } @Override public List getConnectors(final ConnectorEvent connectorEvent) { return connectorsMap.get(connectorEvent); } public void addOutgoingTransition(final STransitionDefinition sTransition) { if (!outgoings.contains(sTransition)) { outgoings.add(sTransition); } } public void addIncomingTransition(final STransitionDefinition sTransition) { if (!incomings.contains(sTransition)) { incomings.add(sTransition); } } @Override public String getDescription() { return description; } public void setDescription(final String description) { this.description = description; } @Override public SExpression getDisplayDescription() { return displayDescription; } public void setDisplayDescription(final SExpression displayDescription) { this.displayDescription = displayDescription; } @Override public SExpression getDisplayDescriptionAfterCompletion() { return displayDescriptionAfterCompletion; } @Override public SExpression getDisplayName() { return displayName; } public void setDisplayName(final SExpression displayName) { this.displayName = displayName; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + (connectors == null ? 0 : connectors.hashCode()); result = prime * result + (defaultTransition == null ? 0 : defaultTransition.hashCode()); result = prime * result + (description == null ? 0 : description.hashCode()); result = prime * result + (displayDescription == null ? 0 : displayDescription.hashCode()); result = prime * result + (displayDescriptionAfterCompletion == null ? 0 : displayDescriptionAfterCompletion.hashCode()); result = prime * result + (displayName == null ? 0 : displayName.hashCode()); result = prime * result + (incomings == null ? 0 : incomings.hashCode()); result = prime * result + (outgoings == null ? 0 : outgoings.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } final SFlowNodeDefinitionImpl other = (SFlowNodeDefinitionImpl) obj; if (connectors == null) { if (other.connectors != null) { return false; } } else if (!connectors.equals(other.connectors)) { return false; } if (defaultTransition == null) { if (other.defaultTransition != null) { return false; } } else if (!defaultTransition.equals(other.defaultTransition)) { return false; } if (description == null) { if (other.description != null) { return false; } } else if (!description.equals(other.description)) { return false; } if (displayDescription == null) { if (other.displayDescription != null) { return false; } } else if (!displayDescription.equals(other.displayDescription)) { return false; } if (displayDescriptionAfterCompletion == null) { if (other.displayDescriptionAfterCompletion != null) { return false; } } else if (!displayDescriptionAfterCompletion.equals(other.displayDescriptionAfterCompletion)) { return false; } if (displayName == null) { if (other.displayName != null) { return false; } } else if (!displayName.equals(other.displayName)) { return false; } if (incomings == null) { if (other.incomings != null) { return false; } } else if (!incomings.equals(other.incomings)) { return false; } if (outgoings == null) { if (other.outgoings != null) { return false; } } else if (!outgoings.equals(other.outgoings)) { return false; } return true; } @Override public int getTransitionIndex(final Long transitionId) { int index = 1; boolean found = false; final Iterator iterator = incomings.iterator(); while (!found && iterator.hasNext()) { final STransitionDefinition next = iterator.next(); if (next.getId().equals(transitionId)) { found = true; } else { index++; } } return index; } @Override public boolean isStartable() { return !hasIncomingTransitions(); } @Override public boolean isParalleleOrInclusive() { return false; } @Override public boolean isExclusive() { return false; } @Override public boolean isBoundaryEvent() { return SFlowNodeType.BOUNDARY_EVENT.equals(getType()); } @Override public boolean isInterrupting() { return false; } @Override public boolean isEventSubProcess() { return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SGatewayDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.GatewayDefinition; import org.bonitasoft.engine.bpm.flownode.GatewayType; 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.STransitionDefinition; /** * @author Feng Hui * @author Zhao Na * @author Matthieu Chaffotte * @author Celine Souchet */ public class SGatewayDefinitionImpl extends SFlowNodeDefinitionImpl implements SGatewayDefinition { private static final long serialVersionUID = -5765195373419051716L; private final SGatewayType gatewayType; public SGatewayDefinitionImpl(final GatewayDefinition gatewayDefinition, final Map transitionsMap) { super(gatewayDefinition, transitionsMap); final GatewayType type = gatewayDefinition.getGatewayType(); gatewayType = SGatewayType.valueOf(type.toString()); } public SGatewayDefinitionImpl(final long id, final String name, final SGatewayType gatewayType) { super(id, name); this.gatewayType = gatewayType; } @Override public SGatewayType getGatewayType() { return gatewayType; } @Override public SFlowNodeType getType() { return SFlowNodeType.GATEWAY; } @Override public boolean isParalleleOrInclusive() { final SGatewayType gatewayType = this.getGatewayType(); return SGatewayType.PARALLEL.equals(gatewayType) || SGatewayType.INCLUSIVE.equals(gatewayType); } @Override public boolean isExclusive() { final SGatewayType gatewayType = this.getGatewayType(); return SGatewayType.EXCLUSIVE.equals(gatewayType); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SHumanTaskDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.HumanTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.SHumanTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.SUserFilterDefinition; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta * @author Celine Souchet */ public abstract class SHumanTaskDefinitionImpl extends SActivityDefinitionImpl implements SHumanTaskDefinition { private static final long serialVersionUID = -4475497975786419487L; private final String actorName; private SUserFilterDefinition sUserFilterDefinition; private String priority; private SExpression expectedDuration; public SHumanTaskDefinitionImpl(final HumanTaskDefinition userTaskDefinition, final Map transitionsMap) { super(userTaskDefinition, transitionsMap); actorName = userTaskDefinition.getActorName(); } public SHumanTaskDefinitionImpl(final long id, final String name, final String actorName) { super(id, name); this.actorName = actorName; } @Override public String getActorName() { return actorName; } public void setUserFilter(final SUserFilterDefinition sUserFilterDefinition) { this.sUserFilterDefinition = sUserFilterDefinition; } @Override public SUserFilterDefinition getSUserFilterDefinition() { return sUserFilterDefinition; } @Override public SExpression getExpectedDuration() { return expectedDuration; } @Override public String getPriority() { return priority; } public void setPriority(final String priority) { this.priority = priority; } public void setExpectedDuration(final SExpression expectedDuration) { this.expectedDuration = expectedDuration; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SInputDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.ArrayList; import java.util.List; import java.util.Objects; import org.bonitasoft.engine.bpm.contract.InputDefinition; import org.bonitasoft.engine.bpm.contract.Type; import org.bonitasoft.engine.core.process.definition.model.SInputDefinition; import org.bonitasoft.engine.core.process.definition.model.SType; /** * @author Matthieu Chaffotte */ public class SInputDefinitionImpl extends SNamedElementImpl implements SInputDefinition { private static final long serialVersionUID = -5021740296501498639L; protected final List inputDefinitions; protected final String description; protected final SType type; protected final boolean multiple; public SInputDefinitionImpl(final String name, final String description) { this(name, null, description, false, null); } public SInputDefinitionImpl(final String name, final SType type, final String description, final boolean multiple, final List inputDefinitions) { super(name); this.description = description; this.multiple = multiple; this.type = type; this.inputDefinitions = new ArrayList<>(); if (inputDefinitions != null) { this.inputDefinitions.addAll(inputDefinitions); } } public SInputDefinitionImpl(final String name, final SType type, final String description) { this(name, type, description, false); } public SInputDefinitionImpl(final String name, final SType type, final String description, final boolean multiple) { this(name, type, description, multiple, null); } public SInputDefinitionImpl(final InputDefinition input) { this(input.getName(), convertTypeToSType(input.getType()), input.getDescription(), input.isMultiple(), null); convertAndAddInputDefinitions(input); } public SInputDefinitionImpl(String name, String description, boolean multiple, List inputDefinitions) { this(name, null, description, multiple, inputDefinitions); } protected static SType convertTypeToSType(final Type type2) { if (type2 == null) { return null; } return SType.valueOf(type2.toString()); } private void convertAndAddInputDefinitions(final InputDefinition input) { for (final InputDefinition inputDefinition : input.getInputs()) { inputDefinitions.add(new SInputDefinitionImpl(inputDefinition)); } } @Override public String getDescription() { return description; } @Override public SType getType() { return type; } @Override public boolean hasChildren() { return !inputDefinitions.isEmpty(); } @Override public List getInputDefinitions() { return inputDefinitions; } @Override public boolean isMultiple() { return multiple; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; SInputDefinitionImpl that = (SInputDefinitionImpl) o; return Objects.equals(multiple, that.multiple) && Objects.equals(inputDefinitions, that.inputDefinitions) && Objects.equals(description, that.description) && Objects.equals(type, that.type); } @Override public int hashCode() { return Objects.hash(super.hashCode(), inputDefinitions, description, type, multiple); } @Override public String toString() { return "SInputDefinitionImpl{" + "inputDefinitions=" + inputDefinitions + ", description='" + description + '\'' + ", type=" + type + ", multiple=" + multiple + "} " + super.toString(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SManualTaskDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.ManualTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.SManualTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class SManualTaskDefinitionImpl extends SHumanTaskDefinitionImpl implements SManualTaskDefinition { private static final long serialVersionUID = 4800299070670205477L; /** * @param manualTaskDefinition * @param transitionsMap */ public SManualTaskDefinitionImpl(final ManualTaskDefinition manualTaskDefinition, final Map transitionsMap) { super(manualTaskDefinition, transitionsMap); } @Override public SFlowNodeType getType() { return SFlowNodeType.MANUAL_TASK; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SMultiInstanceLoopCharacteristicsImpl.java ================================================ /** * 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.core.process.definition.model.impl; import org.bonitasoft.engine.bpm.flownode.MultiInstanceLoopCharacteristics; import org.bonitasoft.engine.core.process.definition.model.SMultiInstanceLoopCharacteristics; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta */ public class SMultiInstanceLoopCharacteristicsImpl implements SMultiInstanceLoopCharacteristics { private static final long serialVersionUID = 5900494662430961903L; private boolean isSequential; private SExpression loopCardinality; private SExpression completionCondition; private String loopDataInputRef; private String loopDataOutputRef; private String dataInputItemRef; private String dataOutputItemRef; public SMultiInstanceLoopCharacteristicsImpl( final MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics) { isSequential = multiInstanceLoopCharacteristics.isSequential(); loopDataInputRef = multiInstanceLoopCharacteristics.getLoopDataInputRef(); loopDataOutputRef = multiInstanceLoopCharacteristics.getLoopDataOutputRef(); dataInputItemRef = multiInstanceLoopCharacteristics.getDataInputItemRef(); dataOutputItemRef = multiInstanceLoopCharacteristics.getDataOutputItemRef(); loopCardinality = ServerModelConvertor.convertExpression(multiInstanceLoopCharacteristics.getLoopCardinality()); completionCondition = ServerModelConvertor .convertExpression(multiInstanceLoopCharacteristics.getCompletionCondition()); } @Override public boolean isSequential() { return isSequential; } @Override public SExpression getLoopCardinality() { return loopCardinality; } @Override public SExpression getCompletionCondition() { return completionCondition; } @Override public String getLoopDataInputRef() { return loopDataInputRef; } @Override public String getLoopDataOutputRef() { return loopDataOutputRef; } @Override public String getDataInputItemRef() { return dataInputItemRef; } @Override public String getDataOutputItemRef() { return dataOutputItemRef; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (completionCondition == null ? 0 : completionCondition.hashCode()); result = prime * result + (dataInputItemRef == null ? 0 : dataInputItemRef.hashCode()); result = prime * result + (dataOutputItemRef == null ? 0 : dataOutputItemRef.hashCode()); result = prime * result + (isSequential ? 1231 : 1237); result = prime * result + (loopCardinality == null ? 0 : loopCardinality.hashCode()); result = prime * result + (loopDataInputRef == null ? 0 : loopDataInputRef.hashCode()); result = prime * result + (loopDataOutputRef == null ? 0 : loopDataOutputRef.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final SMultiInstanceLoopCharacteristicsImpl other = (SMultiInstanceLoopCharacteristicsImpl) obj; if (completionCondition == null) { if (other.completionCondition != null) { return false; } } else if (!completionCondition.equals(other.completionCondition)) { return false; } if (dataInputItemRef == null) { if (other.dataInputItemRef != null) { return false; } } else if (!dataInputItemRef.equals(other.dataInputItemRef)) { return false; } if (dataOutputItemRef == null) { if (other.dataOutputItemRef != null) { return false; } } else if (!dataOutputItemRef.equals(other.dataOutputItemRef)) { return false; } if (isSequential != other.isSequential) { return false; } if (loopCardinality == null) { if (other.loopCardinality != null) { return false; } } else if (!loopCardinality.equals(other.loopCardinality)) { return false; } if (loopDataInputRef == null) { if (other.loopDataInputRef != null) { return false; } } else if (!loopDataInputRef.equals(other.loopDataInputRef)) { return false; } if (loopDataOutputRef == null) { if (other.loopDataOutputRef != null) { return false; } } else if (!loopDataOutputRef.equals(other.loopDataOutputRef)) { return false; } return true; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("SMultiInstanceLoopCharacteristicsImpl [isSequential="); builder.append(isSequential); builder.append(", loopCardinality="); builder.append(loopCardinality); builder.append(", completionCondition="); builder.append(completionCondition); builder.append(", loopDataInputRef="); builder.append(loopDataInputRef); builder.append(", loopDataOutputRef="); builder.append(loopDataOutputRef); builder.append(", dataInputItemRef="); builder.append(dataInputItemRef); builder.append(", dataOutputItemRef="); builder.append(dataOutputItemRef); builder.append("]"); return builder.toString(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SNamedElementImpl.java ================================================ /** * 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.core.process.definition.model.impl; import org.bonitasoft.engine.core.process.definition.model.SNamedElement; /** * @author Matthieu Chaffotte */ public class SNamedElementImpl extends SBaseElementImpl implements SNamedElement { private static final long serialVersionUID = 4789196762554891321L; private final String name; public SNamedElementImpl(final String name) { this.name = name; } @Override public String getName() { return name; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + (name == null ? 0 : name.hashCode()); return result; } protected EQUALS_STATE checkFurtherNaiveEquality(final Object obj) { EQUALS_STATE state; if ((state = super.checkNaiveEquality(obj)) != EQUALS_STATE.CONTINUE) { return state; } else if (!super.equals(obj)) { return EQUALS_STATE.RETURN_FALSE; } else { return EQUALS_STATE.CONTINUE; } } @Override public boolean equals(final Object obj) { switch (checkFurtherNaiveEquality(obj)) { case RETURN_FALSE: return false; case RETURN_TRUE: return true; case CONTINUE: default: break; } final SNamedElementImpl other = (SNamedElementImpl) obj; if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SParameterDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import org.bonitasoft.engine.bpm.parameter.ParameterDefinition; import org.bonitasoft.engine.core.process.definition.model.SParameterDefinition; /** * @author Matthieu Chaffotte */ public class SParameterDefinitionImpl extends SNamedElementImpl implements SParameterDefinition { private static final long serialVersionUID = -6048365663287821057L; private String description; private final String type; public SParameterDefinitionImpl(final ParameterDefinition parameterDefinition) { super(parameterDefinition.getName()); description = parameterDefinition.getDescription(); type = parameterDefinition.getType(); } @Override public String getDescription() { return description; } @Override public String getType() { return type; } public void setDescription(final String description) { this.description = description; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SProcessDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.bonitasoft.engine.bpm.actor.ActorDefinition; import org.bonitasoft.engine.bpm.context.ContextEntry; import org.bonitasoft.engine.bpm.contract.ContractDefinition; import org.bonitasoft.engine.bpm.parameter.ParameterDefinition; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SActorDefinition; import org.bonitasoft.engine.core.process.definition.model.SContextEntry; import org.bonitasoft.engine.core.process.definition.model.SContractDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SParameterDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Matthieu Chaffotte * @author Zhao Na * @author Elias Ricken de Medeiros * @author Yanyan Liu * @author Celine Souchet */ public class SProcessDefinitionImpl extends SNamedElementImpl implements SProcessDefinition { private static final long serialVersionUID = -8961400385282752731L; private final String version; private final Set parameters; private final Set actors; private final List context = new ArrayList<>(); private String description; private SActorDefinition sActorInitiator; private SFlowElementContainerDefinition container; private String stringIndexLabel1; private String stringIndexLabel2; private String stringIndexLabel3; private String stringIndexLabel4; private String stringIndexLabel5; private SExpression stringIndexValue1; private SExpression stringIndexValue2; private SExpression stringIndexValue3; private SExpression stringIndexValue4; private SExpression stringIndexValue5; private SContractDefinition contract; public SProcessDefinitionImpl(final DesignProcessDefinition processDefinition) { super(processDefinition.getName()); description = processDefinition.getDescription(); version = processDefinition.getVersion(); actors = new HashSet<>(); stringIndexLabel1 = processDefinition.getStringIndexLabel(1); stringIndexLabel2 = processDefinition.getStringIndexLabel(2); stringIndexLabel3 = processDefinition.getStringIndexLabel(3); stringIndexLabel4 = processDefinition.getStringIndexLabel(4); stringIndexLabel5 = processDefinition.getStringIndexLabel(5); stringIndexValue1 = ServerModelConvertor.convertExpression(processDefinition.getStringIndexValue(1)); stringIndexValue2 = ServerModelConvertor.convertExpression(processDefinition.getStringIndexValue(2)); stringIndexValue3 = ServerModelConvertor.convertExpression(processDefinition.getStringIndexValue(3)); stringIndexValue4 = ServerModelConvertor.convertExpression(processDefinition.getStringIndexValue(4)); stringIndexValue5 = ServerModelConvertor.convertExpression(processDefinition.getStringIndexValue(5)); for (final ActorDefinition actor : processDefinition.getActorsList()) { actors.add(new SActorDefinitionImpl(actor)); } parameters = new HashSet<>(); for (final ParameterDefinition parameterDefinition : processDefinition.getParameters()) { parameters.add(new SParameterDefinitionImpl(parameterDefinition)); } final ActorDefinition actorInitiator = processDefinition.getActorInitiator(); if (actorInitiator != null) { sActorInitiator = new SActorDefinitionImpl(actorInitiator); } final ContractDefinition contract = processDefinition.getContract(); if (contract != null) { setContract(new SContractDefinitionImpl(contract)); } for (ContextEntry contextEntry : processDefinition.getContext()) { context.add(new SContextEntryImpl(contextEntry.getKey(), ServerModelConvertor.convertExpression(contextEntry.getExpression()))); } container = new SFlowElementContainerDefinitionImpl(processDefinition.getFlowElementContainer()); } public SProcessDefinitionImpl(final String name, final String version) { super(name); this.version = version; actors = new HashSet<>(); parameters = new HashSet<>(); container = new SFlowElementContainerDefinitionImpl(); } @Override public String getVersion() { return version; } @Override public Set getParameters() { return parameters; } @Override public SParameterDefinition getParameter(final String parameterName) { final Iterator iterator = parameters.iterator(); SParameterDefinition found = null; while (found == null && iterator.hasNext()) { final SParameterDefinition next = iterator.next(); if (next.getName().equals(parameterName)) { found = next; } } return found; } @Override public Set getActors() { return actors; } public void addActor(final SActorDefinition actor) { actors.add(actor); } @Override public SActorDefinition getActorInitiator() { return sActorInitiator; } @Override public String getDescription() { return description; } public void setDescription(final String description) { this.description = description; } @Override public SFlowElementContainerDefinition getProcessContainer() { return container; } public void setProcessContainer(final SFlowElementContainerDefinition processContainer) { container = processContainer; } @Override public String getStringIndexLabel(final int index) { switch (index) { case 1: return stringIndexLabel1; case 2: return stringIndexLabel2; case 3: return stringIndexLabel3; case 4: return stringIndexLabel4; case 5: return stringIndexLabel5; default: throw new IndexOutOfBoundsException("search key label must be between 1 and 5 (included)"); } } public void setStringIndex(final int index, final String label, final SExpression initialValue) { switch (index) { case 1: stringIndexLabel1 = label; stringIndexValue1 = initialValue; break; case 2: stringIndexLabel2 = label; stringIndexValue2 = initialValue; break; case 3: stringIndexLabel3 = label; stringIndexValue3 = initialValue; break; case 4: stringIndexLabel4 = label; stringIndexValue4 = initialValue; break; case 5: stringIndexLabel5 = label; stringIndexValue5 = initialValue; break; default: throw new IndexOutOfBoundsException("search key label must be between 1 and 5 (included)"); } } @Override public SExpression getStringIndexValue(final int index) { switch (index) { case 1: return stringIndexValue1; case 2: return stringIndexValue2; case 3: return stringIndexValue3; case 4: return stringIndexValue4; case 5: return stringIndexValue5; default: throw new IndexOutOfBoundsException("search key label must be between 1 and 5 (included)"); } } @Override public boolean hasConnectors() { return getProcessContainer().getConnectors().size() > 0; } @Override public SContractDefinition getContract() { return contract; } public void setContract(SContractDefinition contract) { this.contract = contract; } @Override public List getContext() { return context; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SReceiveTaskDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.impl.internal.ReceiveTaskDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.SReceiveTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchMessageEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SCatchMessageEventTriggerDefinitionImpl; /** * @author Julien Molinaro * @author Celine Souchet */ public class SReceiveTaskDefinitionImpl extends SActivityDefinitionImpl implements SReceiveTaskDefinition { private static final long serialVersionUID = 8112705930442175231L; private final SCatchMessageEventTriggerDefinitionImpl trigger; public SReceiveTaskDefinitionImpl(final ReceiveTaskDefinitionImpl activityDefinition, final Map transitionsMap) { super(activityDefinition, transitionsMap); trigger = new SCatchMessageEventTriggerDefinitionImpl(activityDefinition.getTrigger()); } @Override public SFlowNodeType getType() { return SFlowNodeType.RECEIVE_TASK; } @Override public SCatchMessageEventTriggerDefinition getTrigger() { return trigger; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SSendTaskDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.impl.internal.SendTaskDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.SSendTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowMessageEventTriggerDefinitionImpl; /** * @author Baptiste Mesta * @author Celine Souchet */ public class SSendTaskDefinitionImpl extends SActivityDefinitionImpl implements SSendTaskDefinition { private static final long serialVersionUID = 8112705930442175231L; private final SThrowMessageEventTriggerDefinition trigger; public SSendTaskDefinitionImpl(final SendTaskDefinitionImpl activityDefinition, final Map transitionsMap) { super(activityDefinition, transitionsMap); trigger = new SThrowMessageEventTriggerDefinitionImpl(activityDefinition.getMessageTrigger()); } public SSendTaskDefinitionImpl(final long id, final String name, final SThrowMessageEventTriggerDefinition throwMessageEventTriggerDefinition) { super(id, name); trigger = throwMessageEventTriggerDefinition; } @Override public SFlowNodeType getType() { return SFlowNodeType.SEND_TASK; } @Override public SThrowMessageEventTriggerDefinition getMessageTrigger() { return trigger; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SStandardLoopCharacteristicsImpl.java ================================================ /** * 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.core.process.definition.model.impl; import org.bonitasoft.engine.bpm.flownode.StandardLoopCharacteristics; import org.bonitasoft.engine.core.process.definition.model.SStandardLoopCharacteristics; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Matthieu Chaffotte */ public class SStandardLoopCharacteristicsImpl implements SStandardLoopCharacteristics { private static final long serialVersionUID = 2762060718511545677L; private final SExpression loopCondition; private final boolean testBefore; private final SExpression loopMax; public SStandardLoopCharacteristicsImpl(final SExpression loopCondition, final boolean testBefore) { super(); this.loopCondition = loopCondition; this.testBefore = testBefore; loopMax = null; } public SStandardLoopCharacteristicsImpl(final StandardLoopCharacteristics loopCharacteristics) { super(); testBefore = loopCharacteristics.isTestBefore(); final Expression loopMaxExpression = loopCharacteristics.getLoopMax(); loopMax = ServerModelConvertor.convertExpression(loopMaxExpression); final Expression condition = loopCharacteristics.getLoopCondition(); loopCondition = ServerModelConvertor.convertExpression(condition); } @Override public SExpression getLoopCondition() { return loopCondition; } @Override public boolean isTestBefore() { return testBefore; } @Override public SExpression getLoopMax() { return loopMax; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SSubProcessDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import org.bonitasoft.engine.bpm.process.SubProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class SSubProcessDefinitionImpl extends SActivityDefinitionImpl implements SSubProcessDefinition { private static final long serialVersionUID = -244737326513630024L; private final boolean triggeredByEvent; private SFlowElementContainerDefinition subProcessContainer; public SSubProcessDefinitionImpl(final SubProcessDefinition subProcess) { super(subProcess.getId(), subProcess.getName()); triggeredByEvent = subProcess.isTriggeredByEvent(); subProcessContainer = new SFlowElementContainerDefinitionImpl(subProcess.getSubProcessContainer()); } public SSubProcessDefinitionImpl(final long id, final String name, final boolean triggeredByEvent) { super(id, name); this.triggeredByEvent = triggeredByEvent; } @Override public SFlowNodeType getType() { return SFlowNodeType.SUB_PROCESS; } @Override public boolean isTriggeredByEvent() { return triggeredByEvent; } @Override public SFlowElementContainerDefinition getSubProcessContainer() { return subProcessContainer; } public void setSubProcessContainer(final SFlowElementContainerDefinition container) { subProcessContainer = container; } @Override public boolean isStartable() { return !isTriggeredByEvent() && super.isStartable(); } @Override public boolean isEventSubProcess() { return isTriggeredByEvent(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/STransitionDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import org.bonitasoft.engine.bpm.flownode.TransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Matthieu Chaffotte * @author Zhao Na * @author Celine Souchet */ public class STransitionDefinitionImpl extends SNamedElementImpl implements STransitionDefinition { private static final long serialVersionUID = -5150684543828946974L; private final long source; private final long target; private SExpression condition; public STransitionDefinitionImpl(final TransitionDefinition transition) { this(transition.getName(), transition.getSource(), transition.getTarget()); setId(transition.getId()); if (transition.getCondition() != null) { condition = ServerModelConvertor.convertExpression(transition.getCondition()); } } public STransitionDefinitionImpl(final String name) { this(name, -1, -1); } public STransitionDefinitionImpl(final String name, final long source, final long target) { super(name); this.source = source; this.target = target; } @Override public long getSource() { return source; } @Override public long getTarget() { return target; } public void setCondition(final SExpression condition) { this.condition = condition; } @Override public SExpression getCondition() { return condition; } @Override public boolean hasCondition() { return condition != null; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + (condition == null ? 0 : condition.hashCode()); result = prime * result + (int) (source ^ source >>> 32); result = prime * result + (int) (target ^ target >>> 32); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (getClass() != obj.getClass()) { return false; } final STransitionDefinitionImpl other = (STransitionDefinitionImpl) obj; if (condition == null) { if (other.condition != null) { return false; } } else if (!condition.equals(other.condition)) { return false; } if (source != other.source) { return false; } if (target != other.target) { return false; } return true; } @Override public String toString() { return "[" + getName() + "]"; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SUserFilterDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.engine.bpm.userfilter.UserFilterDefinition; import org.bonitasoft.engine.core.process.definition.model.SUserFilterDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta */ public class SUserFilterDefinitionImpl extends SNamedElementImpl implements SUserFilterDefinition { private static final long serialVersionUID = -6045216424839658552L; private final String filterId; private final String version; private final Map inputs; /** * @param userFilter */ public SUserFilterDefinitionImpl(final UserFilterDefinition userFilter) { super(userFilter.getName()); filterId = userFilter.getUserFilterId(); version = userFilter.getVersion(); inputs = new HashMap<>(userFilter.getInputs().size()); for (final Entry input : userFilter.getInputs().entrySet()) { final Expression value = input.getValue(); final SExpression sExpression = ServerModelConvertor.convertExpression(value); inputs.put(input.getKey(), sExpression); } } @Override public String getUserFilterId() { return filterId; } @Override public Map getInputs() { return inputs; } @Override public String getVersion() { return version; } @Override public String toString() { final StringBuilder sb = new StringBuilder("SUserFilterDefinitionImpl{"); sb.append("filterId='").append(filterId).append('\''); sb.append(", version='").append(version).append('\''); sb.append(", inputs=").append(inputs); sb.append('}'); return sb.toString(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/java/org/bonitasoft/engine/core/process/definition/model/impl/SUserTaskDefinitionImpl.java ================================================ /** * 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.core.process.definition.model.impl; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.context.ContextEntry; import org.bonitasoft.engine.bpm.contract.ContractDefinition; import org.bonitasoft.engine.bpm.flownode.UserTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.SContextEntry; import org.bonitasoft.engine.core.process.definition.model.SContractDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.SUserTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.ServerModelConvertor; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class SUserTaskDefinitionImpl extends SHumanTaskDefinitionImpl implements SUserTaskDefinition { private static final long serialVersionUID = 9039679250456947450L; private SContractDefinition contract; private final List context = new ArrayList<>(); public SUserTaskDefinitionImpl(final UserTaskDefinition userTaskDefinition, final Map transitionsMap) { super(userTaskDefinition, transitionsMap); final ContractDefinition contract = userTaskDefinition.getContract(); if (contract != null) { setContract(new SContractDefinitionImpl(contract)); } for (ContextEntry contextEntry : userTaskDefinition.getContext()) { context.add(new SContextEntryImpl(contextEntry.getKey(), ServerModelConvertor.convertExpression(contextEntry.getExpression()))); } } public SUserTaskDefinitionImpl(final long id, final String name, final String actorName) { super(id, name, actorName); } @Override public SFlowNodeType getType() { return SFlowNodeType.USER_TASK; } @Override public SContractDefinition getContract() { if (contract == null) { return new SContractDefinitionImpl(); } return contract; } @Override public List getContext() { return context; } public void setContract(final SContractDefinition contract) { this.contract = contract; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/main/resources/org/bonitasoft/engine/core/process/definition/model/impl/hibernate/process.definition.queries.hbm.xml ================================================ SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE process_definition.processId = :processId SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE process_definition.name = :name AND process_definition.activationState = 'ENABLED' SELECT DISTINCT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE process_definition.processId = actor.scopeId AND actor.id = actormember.actorId AND actormember.userId = :userId AND actormember.actorId NOT IN ( SELECT actorId FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS am2 WHERE am2.userId != actormember.userId ) SELECT DISTINCT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE process_definition.processId = actor.scopeId AND actor.id = actormember.actorId AND actormember.userId IN (:userIds) AND actormember.actorId NOT IN ( SELECT actorId FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS am2 WHERE am2.userId NOT IN (:userIds) ) SELECT DISTINCT process_definition FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actor_member, org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE actor.scopeId = process_definition.processId AND actor.id = actor_member.actorId AND actor_member.roleId = :roleId AND actor_member.actorId NOT IN ( SELECT am2.actorId FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS am2 WHERE am2.roleId != actor_member.roleId ) SELECT DISTINCT process_definition FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actor_member, org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE actor.scopeId = process_definition.processId AND actor.id = actor_member.actorId AND actor_member.roleId IN :roleIds AND actor_member.actorId NOT IN ( SELECT am2.actorId FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS am2 WHERE am2.roleId NOT IN (:roleIds) ) SELECT DISTINCT process_definition FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actor_member, org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE actor.scopeId = process_definition.processId AND actor.id = actor_member.actorId AND actor_member.groupId IN ( SELECT groupe.id FROM org.bonitasoft.engine.identity.model.SGroup AS groupe, org.bonitasoft.engine.identity.model.SGroup AS group0 WHERE ( CONCAT(groupe.parentPath,'/',groupe.name,'/') LIKE CONCAT('/',group0.name,'/','%') OR groupe.id = :groupId ) AND group0.id = :groupId ) AND actor_member.actorId NOT IN ( SELECT am2.actorId FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS am2 WHERE am2.groupId != actor_member.groupId AND am2.groupId NOT IN ( SELECT groupe.id FROM org.bonitasoft.engine.identity.model.SGroup AS groupe, org.bonitasoft.engine.identity.model.SGroup AS group0 WHERE ( CONCAT(groupe.parentPath,'/',groupe.name,'/') LIKE CONCAT('/',group0.name,'/','%') OR groupe.id = :groupId ) AND group0.id = :groupId ) ) SELECT DISTINCT process_definition FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actor_member, org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE actor.scopeId = process_definition.processId AND actor.id = actor_member.actorId AND actor_member.groupId IN ( SELECT groupe.id FROM org.bonitasoft.engine.identity.model.SGroup AS groupe, org.bonitasoft.engine.identity.model.SGroup AS group0 WHERE ( CONCAT(groupe.parentPath,'/',groupe.name,'/') LIKE CONCAT('/',group0.name,'/','%') OR groupe.id IN (:groupIds) ) AND group0.id IN (:groupIds) ) AND actor_member.actorId NOT IN ( SELECT am2.actorId FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS am2 WHERE am2.groupId != actor_member.groupId AND am2.groupId NOT IN ( SELECT groupe.id FROM org.bonitasoft.engine.identity.model.SGroup AS groupe, org.bonitasoft.engine.identity.model.SGroup AS group0 WHERE ( CONCAT(groupe.parentPath,'/',groupe.name,'/') LIKE CONCAT('/',group0.name,'/','%') OR groupe.id IN (:groupIds) ) AND group0.id IN (:groupIds) ) ) SELECT process_definition.processId FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE process_definition.name = :name AND process_definition.version = :version SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition SELECT COUNT(process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition SELECT COUNT(process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE process_definition.activationState = :activationState SELECT process_definition.processId FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition SELECT process_definition.processId FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE process_definition.activationState = :activationState SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE process_definition.processId IN (:processIds) SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE process_definition.activationState = 'ENABLED' AND (process_definition.processId IN ( SELECT aprocessInstance.processDefinitionId FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS aprocessInstance WHERE aprocessInstance.startedBy = :startedBy) OR process_definition.processId IN ( SELECT processInstance.processDefinitionId FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS processInstance WHERE processInstance.startedBy = :startedBy) ) SELECT COUNT(process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE process_definition.activationState = 'ENABLED' AND (process_definition.processId IN ( SELECT aprocessInstance.processDefinitionId FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS aprocessInstance WHERE aprocessInstance.startedBy = :startedBy) OR process_definition.processId IN ( SELECT processInstance.processDefinitionId FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS processInstance WHERE processInstance.startedBy = :startedBy) ) SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition SELECT COUNT(process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition SELECT COUNT(process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE NOT EXISTS ( SELECT categorymapping.id FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = process_definition.processId ) SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE NOT EXISTS ( SELECT categorymapping.id FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = process_definition.processId ) SELECT COUNT(process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE process_definition.processId NOT IN ( SELECT categorymapping.processId FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.categoryId = :categoryId ) SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE process_definition.processId NOT IN ( SELECT categorymapping.processId FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.categoryId = :categoryId ) SELECT COUNT(process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE NOT EXISTS ( SELECT categorymapping.id FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = process_definition.processId ) AND activationState = 'ENABLED' AND process_definition.processId IN ( SELECT actor.scopeId FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor WHERE actor.initiator = TRUE AND actor.id IN ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId = :userId OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)) ) ) ) ) SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE not exists (SELECT categorymapping.id FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = process_definition.processId) AND activationState = 'ENABLED' AND process_definition.processId IN ( SELECT actor.scopeId FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor WHERE actor.initiator = TRUE AND actor.id IN ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId = :userId OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)) ) ) ) ) SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE activationState = 'ENABLED' AND process_definition.processId IN ( SELECT actor.scopeId FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor WHERE actor.initiator = TRUE AND actor.id IN ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId = :userId OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)) ) ) ) ) SELECT COUNT(process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE activationState = 'ENABLED' AND process_definition.processId IN ( SELECT actor.scopeId FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor WHERE actor.initiator = TRUE AND actor.id IN ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId = :userId OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)) ) ) ) ) SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE activationState = 'ENABLED' AND process_definition.processId IN ( SELECT actor.scopeId FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor WHERE actor.initiator = TRUE AND EXISTS ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId = :managerUserId OR EXISTS ( SELECT actormember.userId FROM org.bonitasoft.engine.identity.model.SUser AS user WHERE actormember.userId = user.id AND user.managerUserId = :managerUserId ) OR EXISTS ( SELECT actormember.id FROM org.bonitasoft.engine.identity.model.SUserMembership as um WHERE ( um.userId = :managerUserId OR EXISTS ( SELECT user.id FROM org.bonitasoft.engine.identity.model.SUser AS user WHERE um.userId = user.id AND user.managerUserId = :managerUserId ) ) AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) SELECT COUNT(process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE activationState = 'ENABLED' AND process_definition.processId IN ( SELECT actor.scopeId FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor WHERE actor.initiator = TRUE AND EXISTS ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId = :managerUserId OR EXISTS ( SELECT actormember.userId FROM org.bonitasoft.engine.identity.model.SUser AS user WHERE actormember.userId = user.id AND user.managerUserId = :managerUserId ) OR EXISTS ( SELECT actormember.id FROM org.bonitasoft.engine.identity.model.SUserMembership as um WHERE ( um.userId = :managerUserId OR EXISTS ( SELECT user.id FROM org.bonitasoft.engine.identity.model.SUser AS user WHERE um.userId = user.id AND user.managerUserId = :managerUserId ) ) AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) SELECT COUNT(process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE activationState = 'ENABLED' AND categorymapping.processId = process_definition.processId AND process_definition.processId IN ( SELECT actor.scopeId FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor WHERE actor.initiator = TRUE AND actor.id IN ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId = :userId OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE activationState = 'ENABLED' AND categorymapping.processId = process_definition.processId AND process_definition.processId IN ( SELECT actor.scopeId FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor WHERE actor.initiator = TRUE AND actor.id IN ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId = :userId OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) SELECT COUNT(process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = process_definition.processId AND process_definition.processId IN ( SELECT supervisor.processDefId FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE supervisor.userId = :userId OR ( supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = process_definition.processId AND process_definition.processId IN ( SELECT supervisor.processDefId FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE supervisor.userId = :userId OR ( supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT COUNT(process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE process_definition.processId IN ( SELECT supervisor.processDefId FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE supervisor.userId = :userId OR ( supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE process_definition.processId IN ( SELECT supervisor.processDefId FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE supervisor.userId = :userId OR ( supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT COUNT(process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE not exists (SELECT categorymapping.id FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = process_definition.processId) AND process_definition.processId IN ( SELECT supervisor.processDefId FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE supervisor.userId = :userId OR ( supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition WHERE not exists (SELECT categorymapping.id FROM org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = process_definition.processId) AND process_definition.processId IN ( SELECT supervisor.processDefId FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE supervisor.userId = :userId OR ( supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT new map(p.id AS processInstanceId, process_definition.id AS id,process_definition.processId AS processId,process_definition.name AS name,process_definition.version AS version, process_definition.description AS description,process_definition.deploymentDate AS deploymentDate, process_definition.deployedBy AS deployedBy, process_definition.activationState AS activationState, process_definition.configurationState AS configurationState, process_definition.displayName AS displayName,process_definition.lastUpdateDate AS lastUpdateDate, process_definition.iconPath AS iconPath, process_definition.displayDescription AS displayDescription) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p WHERE process_definition.processId = p.processDefinitionId AND p.id IN (:processInstanceIds) SELECT new map(ap.id AS archivedProcessInstanceId, process_definition.id AS id,process_definition.processId AS processId,process_definition.name AS name,process_definition.version AS version, process_definition.description AS description,process_definition.deploymentDate AS deploymentDate, process_definition.deployedBy AS deployedBy, process_definition.activationState AS activationState, process_definition.configurationState AS configurationState, process_definition.displayName AS displayName,process_definition.lastUpdateDate AS lastUpdateDate, process_definition.iconPath AS iconPath, process_definition.displayDescription AS displayDescription) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap WHERE process_definition.processId = ap.processDefinitionId AND ap.id IN (:archivedProcessInstanceIds) SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = process_definition.processId SELECT COUNT(process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = process_definition.processId SELECT process_definition FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.core.category.model.SProcessCategoryMapping AS categorymapping WHERE categorymapping.processId = process_definition.processId AND categorymapping.categoryId = :categoryId SELECT count(id) as count FROM ( SELECT DISTINCT user_.* FROM actormember JOIN actor ON ( actor.id = actormember.actorid and actor.scopeid = :processId and actor.initiator = :trueValue ) JOIN user_membership ON ( ( (actormember.groupId = user_membership.groupId and actormember.roleId = -1) or (actormember.roleId = user_membership.roleId and actormember.groupId = -1) or (actormember.groupId = user_membership.groupId and actormember.roleId = user_membership.roleId) ) ) JOIN user_ ON ( user_membership.userid = user_.id and user_.enabled = :trueValue ) UNION SELECT DISTINCT user_.* FROM actormember JOIN actor ON ( actor.id = actormember.actorid and actor.scopeid = :processId and actor.initiator = :trueValue ) JOIN user_ ON ( actormember.userid = user_.id and user_.enabled = :trueValue ) ) user_ SELECT user_.* FROM ( SELECT DISTINCT user_.* FROM actormember JOIN actor ON ( actor.id = actormember.actorid and actor.scopeid = :processId and actor.initiator = :trueValue ) JOIN user_membership ON ( ( (actormember.groupId = user_membership.groupId and actormember.roleId = -1) or (actormember.roleId = user_membership.roleId and actormember.groupId = -1) or (actormember.groupId = user_membership.groupId and actormember.roleId = user_membership.roleId) ) ) JOIN user_ ON ( user_membership.userid = user_.id and user_.enabled = :trueValue ) UNION SELECT DISTINCT user_.* FROM actormember JOIN actor ON ( actor.id = actormember.actorid and actor.scopeid = :processId and actor.initiator = :trueValue ) JOIN user_ ON ( actormember.userid = user_.id and user_.enabled = :trueValue ) ) user_ SELECT COUNT(DISTINCT process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE process_definition.processId = p.processDefinitionId AND p.id = a.logicalGroup2 AND a.stable = TRUE AND a.stateExecuting = FALSE AND a.terminal = FALSE AND ( a.assigneeId = :userId OR ( a.assigneeId = 0 AND EXISTS ( SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId = a.id AND ( mapping.userId = :userId OR EXISTS ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE mapping.actorId = actor.id AND actor.id = actormember.actorId AND ( actormember.userId = :userId OR EXISTS ( SELECT um.id FROM org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) ) ) ) SELECT DISTINCT(process_definition) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE process_definition.processId = p.processDefinitionId AND p.id = a.logicalGroup2 AND a.stable = TRUE AND a.stateExecuting = FALSE AND a.terminal = FALSE AND ( a.assigneeId = :userId OR ( a.assigneeId = 0 AND EXISTS ( SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId = a.id AND ( mapping.userId = :userId OR EXISTS ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE mapping.actorId = actor.id AND actor.id = actormember.actorId AND ( actormember.userId = :userId OR EXISTS ( SELECT um.id FROM org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) ) ) ) SELECT COUNT(DISTINCT process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a, org.bonitasoft.engine.identity.model.SUser as user WHERE process_definition.processId = supervisor.processDefId AND (supervisor.userId = :userId OR EXISTS ( SELECT supervisor.id FROM org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) AND process_definition.processId = p.processDefinitionId AND p.id = a.logicalGroup2 AND a.stable = TRUE AND a.stateExecuting = FALSE AND a.terminal = FALSE AND ( a.assigneeId = user.id OR ( a.assigneeId = 0 AND EXISTS ( SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId = a.id AND ( mapping.userId = user.id OR EXISTS ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE mapping.actorId = actor.id AND actor.id = actormember.actorId AND ( actormember.userId = user.id OR EXISTS ( SELECT um.id FROM org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = user.id AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) ) ) ) SELECT DISTINCT(process_definition) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a, org.bonitasoft.engine.identity.model.SUser as user WHERE process_definition.processId = supervisor.processDefId AND (supervisor.userId = :userId OR EXISTS ( SELECT supervisor.id FROM org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) AND process_definition.processId = p.processDefinitionId AND p.id = a.logicalGroup2 AND a.stable = TRUE AND a.stateExecuting = FALSE AND a.terminal = FALSE AND ( a.assigneeId = user.id OR ( a.assigneeId = 0 AND EXISTS ( SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId = a.id AND ( mapping.userId = user.id OR EXISTS ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE mapping.actorId = actor.id AND actor.id = actormember.actorId AND ( actormember.userId = user.id OR EXISTS ( SELECT um.id FROM org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = user.id AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) ) ) ) SELECT COUNT(DISTINCT process_definition.id) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE process_definition.processId = a.logicalGroup1 AND a.stable = TRUE AND a.stateExecuting = FALSE AND a.terminal = FALSE AND a.stateId = 4 SELECT DISTINCT(process_definition) FROM org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo AS process_definition, org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE process_definition.processId = a.logicalGroup1 AND a.stable = TRUE AND a.stateExecuting = FALSE AND a.terminal = FALSE AND a.stateId = 4 ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/expression/control/api/impl/ExpressionResolverServiceImplTest.java ================================================ /** * 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.core.expression.control.api.impl; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.expression.ExpressionService; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.tracking.TimeTracker; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ExpressionResolverServiceImplTest { @Mock private ExpressionService expressionService; @Mock private ProcessDefinitionService processDefinitionService; @Mock private ClassLoaderService classLoaderService; @Mock private TimeTracker timeTracker; @InjectMocks private ExpressionResolverServiceImpl resolverService; @Mock private SExpression expression; @Test public void evaluate_should_load_class_loader_of_process_definition() throws Exception { final long processDefinitionId = 83L; final SExpressionContext context = new SExpressionContext(45L, "PROCESS", processDefinitionId); resolverService.evaluate(expression, context); verify(classLoaderService).getClassLoader(identifier(ScopeType.PROCESS, processDefinitionId)); } @Test public void evaluate_should_load_class_loader_of_parent_process_definition() throws Exception { final long parentProcessDefinitionId = 19L; final long processDefinitionId = 83L; final SExpressionContext context = new SExpressionContext(45L, "PROCESS", processDefinitionId); context.setParentProcessDefinitionId(parentProcessDefinitionId); resolverService.evaluate(expression, context); verify(classLoaderService) .getClassLoader(identifier(ScopeType.PROCESS, parentProcessDefinitionId)); } @Test public void evaluate_should_not_load_class_loader_when_no_definition_is_defined() throws Exception { final SExpressionContext context = new SExpressionContext(); resolverService.evaluate(expression, context); verify(classLoaderService, never()).getClassLoader(any()); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/expression/control/model/SExpressionContextTest.java ================================================ /** * 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.core.expression.control.model; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class SExpressionContextTest { @Test public void should_toString_return_a_human_readable_context() { // given final SExpressionContext expressionContext = new SExpressionContext(123L, "typeOfTheContainer", 456L); final SProcessDefinition processDefinition = mock(SProcessDefinition.class); doReturn("ProcessName").when(processDefinition).getName(); doReturn("1.0").when(processDefinition).getVersion(); doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer(); expressionContext.setProcessDefinition(processDefinition); // when final String string = expressionContext.toString(); // then assertThat(string).isEqualTo( "context [containerId=123, containerType=typeOfTheContainer, processDefinitionId=456, processDefinition=ProcessName -- 1.0]"); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/AssignmentOperationExecutorStrategyTest.java ================================================ /** * 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.core.operation.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Collections; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.SOperatorType; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class AssignmentOperationExecutorStrategyTest { @Mock private DataInstanceService dataInstanceService; @InjectMocks private AssignmentOperationExecutorStrategy assignmentOperationExecutorStrategy; private SLeftOperand leftOperand; private SOperation operation; private String value; private SExpressionContext expressionContext; @Before public void initMocks() { operation = mock(SOperation.class); value = "value"; expressionContext = mock(SExpressionContext.class); leftOperand = mock(SLeftOperand.class); when(operation.getLeftOperand()).thenReturn(leftOperand); when(leftOperand.getName()).thenReturn("var"); } @Test public void testGetValue() throws Exception { when(expressionContext.getInputValues()).thenReturn(Collections. singletonMap("var", "value")); when(leftOperand.getType()).thenReturn(SLeftOperand.TYPE_DATA); Object returnedValue = assignmentOperationExecutorStrategy.computeNewValueForLeftOperand(operation, value, expressionContext, false); assertThat(returnedValue).isEqualTo("value"); } @Test public void testGetValueOnExternalData() throws Exception { // return type is not compatible when(leftOperand.getType()).thenReturn(SLeftOperand.TYPE_EXTERNAL_DATA); Object returnedValue = assignmentOperationExecutorStrategy.computeNewValueForLeftOperand(operation, value, expressionContext, false); assertThat(returnedValue).isEqualTo("value"); } @Test(expected = SOperationExecutionException.class) public void testGetValueWithWrongType() throws Exception { // return type is not compatible when(expressionContext.getInputValues()) .thenReturn(Collections. singletonMap("var", new java.util.TreeMap())); when(leftOperand.getType()).thenReturn(SLeftOperand.TYPE_DATA); assignmentOperationExecutorStrategy.computeNewValueForLeftOperand(operation, value, expressionContext, false); } @Test public void operationType_should_be_ASSIGNMENT() throws Exception { assertThat(assignmentOperationExecutorStrategy.getOperationType()).isEqualTo(SOperatorType.ASSIGNMENT.name()); } @Test public void should_not_persist_on_null() throws Exception { assertThat(assignmentOperationExecutorStrategy.shouldPersistOnNullValue()).isFalse(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/ExternalDataLeftOperandHandlerTest.java ================================================ /** * 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.core.operation.impl; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl; import org.junit.Before; import org.junit.Test; public class ExternalDataLeftOperandHandlerTest { private ExternalDataLeftOperandHandler handler; @Before public void setUp() { handler = new ExternalDataLeftOperandHandler(); } private SLeftOperandImpl createLeftOperand(final String name) { final SLeftOperandImpl sLeftOperand = new SLeftOperandImpl(); sLeftOperand.setName(name); return sLeftOperand; } @Test(expected = SOperationExecutionException.class) public void deleteThrowsAnExceptionNotYetSupported() throws Exception { handler.delete(createLeftOperand("myData"), 45l, "container"); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/JavaMethodOperationExecutorStrategyTest.java ================================================ /** * 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.core.operation.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.expression.model.SExpression; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; /** * @author Elias Ricken de Medeiros */ @RunWith(MockitoJUnitRunner.class) public class JavaMethodOperationExecutorStrategyTest { private JavaMethodOperationExecutorStrategy strategy; @Before public void setUp() throws Exception { strategy = new JavaMethodOperationExecutorStrategy() { }; } @Test(expected = SOperationExecutionException.class) public void dontThrowNPEIfObjectDoesNotExist() throws Exception { final SOperation operation = mock(SOperation.class); final SLeftOperand leftOperand = mock(SLeftOperand.class); final SExpression rightOperand = mock(SExpression.class); when(operation.getLeftOperand()).thenReturn(leftOperand); when(leftOperand.getName()).thenReturn("unknownData"); final SExpressionContext expressionContext = new SExpressionContext(123L, DataInstanceContainer.PROCESS_INSTANCE.name(), 1234L); expressionContext.setInputValues(Collections. emptyMap()); strategy.computeNewValueForLeftOperand(operation, "Update", expressionContext, false); } @Test(expected = SOperationExecutionException.class) public void shouldThrowExceptionWhenInvokeJavaMethodFails() throws Exception { final SOperation operation = mock(SOperation.class); final SLeftOperand leftOperand = mock(SLeftOperand.class); final SExpression rightOperand = mock(SExpression.class); when(operation.getLeftOperand()).thenReturn(leftOperand); when(leftOperand.getName()).thenReturn("myData"); when(operation.getRightOperand()).thenReturn(rightOperand); when(operation.getOperator()).thenReturn("setThing:int"); when(rightOperand.getReturnType()).thenReturn(Object.class.getName()); final SExpressionContext expressionContext = new SExpressionContext(123L, DataInstanceContainer.PROCESS_INSTANCE.name(), 1234L); final Map map = new HashMap<>(); map.put("myData", new MyClassThatThrowException()); expressionContext.setInputValues(map); strategy.computeNewValueForLeftOperand(operation, "Update", expressionContext, false); } @Test public void computeValue() throws Exception { final SOperation operation = mock(SOperation.class); final SLeftOperand leftOperand = mock(SLeftOperand.class); final SExpression rightOperand = mock(SExpression.class); when(operation.getLeftOperand()).thenReturn(leftOperand); when(leftOperand.getName()).thenReturn("myData"); when(operation.getRightOperand()).thenReturn(rightOperand); when(operation.getOperator()).thenReturn("setThing:int"); when(rightOperand.getReturnType()).thenReturn(Integer.class.getName()); final SExpressionContext expressionContext = new SExpressionContext(123L, DataInstanceContainer.PROCESS_INSTANCE.name(), 1234L); final Map map = new HashMap<>(); map.put("myData", new MyClass()); expressionContext.setInputValues(map); final MyClass updated = (MyClass) strategy.computeNewValueForLeftOperand(operation, 12, expressionContext, false); assertEquals(12, updated.getThing()); } @Test public void shouldExtractParameterType() throws Exception { final SOperation operation = mock(SOperation.class); when(operation.getOperator()).thenReturn("setThing"); assertThat(strategy.extractParameterType(operation)).as("should have no return type").isNull(); } @Test public void getOperationType() throws Exception { //when final String operationType = strategy.getOperationType(); //then assertThat(operationType).as("should get operation type") .isEqualTo(JavaMethodOperationExecutorStrategy.TYPE_JAVA_METHOD); } @Test public void should_not_persist_on_null() throws Exception { assertThat(strategy.shouldPersistOnNullValue()).isFalse(); } public class MyClass { private int thing = 0; public void setThing(final int thing) { this.thing = thing; } public int getThing() { return thing; } } public class MyClassThatThrowException { private final int thing = 0; public void setThing(final int thing) { throw new RuntimeException("bad luck"); } public int getThing() { return thing; } } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/LeftOperandIndexesTest.java ================================================ /** * 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.core.operation.impl; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class LeftOperandIndexesTest { @Test public void default_constructor_should_create_index_with_negative_values() throws Exception { //when LeftOperandIndexes indexes = new LeftOperandIndexes(); //then assertThat(indexes.getLastIndex()).isEqualTo(-1); assertThat(indexes.getNextIndex()).isEqualTo(-1); } @Test public void setLastIndex_should_modify_lastIndex() throws Exception { //given LeftOperandIndexes indexes = new LeftOperandIndexes(); //when indexes.setLastIndex(4); //then assertThat(indexes.getLastIndex()).isEqualTo(4); assertThat(indexes.getNextIndex()).isEqualTo(-1); } @Test public void setNextIndex_should_modify_nextIndex() throws Exception { //given LeftOperandIndexes indexes = new LeftOperandIndexes(); //when indexes.setNextIndex(4); //then assertThat(indexes.getLastIndex()).isEqualTo(-1); assertThat(indexes.getNextIndex()).isEqualTo(4); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/LeftOperandUpdateStatusTest.java ================================================ /** * 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.core.operation.impl; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.core.operation.model.SOperatorType; import org.junit.Test; public class LeftOperandUpdateStatusTest { @Test public void shouldUpdate_on_assignment() throws Exception { //given LeftOperandUpdateStatus updateStatus = new LeftOperandUpdateStatus(SOperatorType.ASSIGNMENT); //then assertThat(updateStatus.shouldUpdate()).isTrue(); assertThat(updateStatus.shouldDelete()).isFalse(); } @Test public void shouldUpdate_on_java_method() throws Exception { //given LeftOperandUpdateStatus updateStatus = new LeftOperandUpdateStatus(SOperatorType.JAVA_METHOD); //then assertThat(updateStatus.shouldUpdate()).isTrue(); assertThat(updateStatus.shouldDelete()).isFalse(); } @Test public void shouldUpdate_on_XPath() throws Exception { //given LeftOperandUpdateStatus updateStatus = new LeftOperandUpdateStatus(SOperatorType.XPATH_UPDATE_QUERY); //then assertThat(updateStatus.shouldUpdate()).isTrue(); assertThat(updateStatus.shouldDelete()).isFalse(); } @Test public void shouldDelete_on_deletion() throws Exception { //given LeftOperandUpdateStatus updateStatus = new LeftOperandUpdateStatus(SOperatorType.DELETION); //then assertThat(updateStatus.shouldUpdate()).isFalse(); assertThat(updateStatus.shouldDelete()).isTrue(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/OperationMockBuilder.java ================================================ /** * 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.core.operation.impl; import java.util.List; import org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl; import org.bonitasoft.engine.core.operation.model.impl.SOperationImpl; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.impl.SExpressionImpl; /** * @author Elias Ricken de Medeiros */ public class OperationMockBuilder { public static SExpressionImpl buildExpression(final String content, final ExpressionType type, final List dependencies) { return new SExpressionImpl(content, content, type.name(), "", null, dependencies); } public static SOperationImpl buildMockOperation(final String leftOperandType, final SExpressionImpl rightOperand) { SOperationImpl operation = buildMockOperation(leftOperandType); operation.setRightOperand(rightOperand); return operation; } public static SOperationImpl buildMockOperation(final String leftOperandType) { SOperationImpl operation = new SOperationImpl(); SLeftOperandImpl leftOperand = buildMockLeftOperand(leftOperandType); operation.setLeftOperand(leftOperand); return operation; } public static SLeftOperandImpl buildMockLeftOperand(final String leftOperandType) { SLeftOperandImpl leftOperand = new SLeftOperandImpl(); leftOperand.setType(leftOperandType); return leftOperand; } public static SOperationImpl buildMockOperation(final String leftOperandType, String leftOperandName) { SLeftOperandImpl leftOperand = buildMockLeftOperand(leftOperandType, leftOperandName); return buildMockOperation(leftOperand); } public static SOperationImpl buildMockOperation(final SLeftOperandImpl leftOperand) { SOperationImpl operation = new SOperationImpl(); operation.setLeftOperand(leftOperand); return operation; } public static SLeftOperandImpl buildMockLeftOperand(final String leftOperandType, final String leftOperandName) { SLeftOperandImpl leftOperand = buildMockLeftOperand(leftOperandType); leftOperand.setName(leftOperandName); return leftOperand; } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/OperationServiceImplTest.java ================================================ /** * 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.core.operation.impl; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.core.operation.model.SOperatorType.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; import static org.mockito.Mockito.anyList; import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.anyMap; import static org.mockito.Mockito.anyString; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.LeftOperandHandler; import org.bonitasoft.engine.core.operation.OperationExecutorStrategy; import org.bonitasoft.engine.core.operation.OperationExecutorStrategyProvider; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.SOperatorType; import org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl; import org.bonitasoft.engine.core.operation.model.impl.SOperationImpl; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class OperationServiceImplTest { private static final String TYPE_1 = "type1"; private static final String TYPE_2 = "type2"; private static final String TYPE_3 = "type3"; private static final String TYPE_4 = "type4"; @Mock private ExpressionResolverService expressionResolverService; @Mock private LeftOperandHandler leftOperandHandler1; @Mock private LeftOperandHandler leftOperandHandler2; @Mock private LeftOperandHandler leftOperandHandler3; @Mock private LeftOperandHandler leftOperandHandler4; @Mock private OperationExecutorStrategyProvider operationExecutorStrategyProvider; @Mock private OperationExecutorStrategy assignmentOperationExecutorStrategy; @Mock private OperationExecutorStrategy xPathOperationExecutorStrategy; @Mock private OperationExecutorStrategy javaMethodOperationExecutorStrategy; @Mock private PersistRightOperandResolver persistRightOperandResolver; @Captor private ArgumentCaptor> leftOperandCaptor1; @Captor private ArgumentCaptor> leftOperandCaptor2; private OperationServiceImpl operationServiceImpl; private SLeftOperandImpl buildLeftOperand(final String type, final String dataName) { final SLeftOperandImpl leftOperand = new SLeftOperandImpl(); leftOperand.setType(type); leftOperand.setName(dataName); return leftOperand; } private SOperationImpl buildOperation(final String type, final String dataName, final SOperatorType operatorType) { final SOperationImpl sOperationImpl = new SOperationImpl(); sOperationImpl.setLeftOperand(buildLeftOperand(type, dataName)); sOperationImpl.setType(operatorType); return sOperationImpl; } private SOperationImpl buildOperation(final String type, final String dataName, final SOperatorType operatorType, final SExpression rightOperand) { final SOperationImpl operation = buildOperation(type, dataName, operatorType); operation.setRightOperand(rightOperand); return operation; } @Before public void before() throws SOperationExecutionException { doReturn(TYPE_1).when(leftOperandHandler1).getType(); doReturn(TYPE_2).when(leftOperandHandler2).getType(); doReturn(TYPE_3).when(leftOperandHandler3).getType(); doReturn(TYPE_4).when(leftOperandHandler4).getType(); doReturn(assignmentOperationExecutorStrategy).when(operationExecutorStrategyProvider) .getOperationExecutorStrategy( argThat(operation -> ASSIGNMENT.equals(operation.getType()))); doReturn(xPathOperationExecutorStrategy).when(operationExecutorStrategyProvider).getOperationExecutorStrategy( argThat(operation -> XPATH_UPDATE_QUERY.equals(operation.getType()))); doReturn(javaMethodOperationExecutorStrategy).when(operationExecutorStrategyProvider) .getOperationExecutorStrategy( argThat(operation -> JAVA_METHOD.equals(operation.getType()))); given(assignmentOperationExecutorStrategy.shouldPersistOnNullValue()).willReturn(true); given(xPathOperationExecutorStrategy.shouldPersistOnNullValue()).willReturn(true); given(javaMethodOperationExecutorStrategy.shouldPersistOnNullValue()).willReturn(true); operationServiceImpl = new OperationServiceImpl(operationExecutorStrategyProvider, asList(leftOperandHandler1, leftOperandHandler2, leftOperandHandler3, leftOperandHandler4), expressionResolverService, persistRightOperandResolver); } @Test public void should_UpdateLeftOperands_call_leftOperandHandlers() throws Exception { // given final Map inputValues = new HashMap<>(); inputValues.put("data1", "value1"); inputValues.put("data2", "value2"); final SExpressionContext expressionContext = new SExpressionContext(123L, "containerType", 987L, inputValues); final Map updates = new HashMap<>(); updates.put(buildLeftOperand(TYPE_1, "data1"), new LeftOperandUpdateStatus(ASSIGNMENT)); updates.put(buildLeftOperand(TYPE_2, "data2"), new LeftOperandUpdateStatus(SOperatorType.DELETION)); // when operationServiceImpl.updateLeftOperands(updates, 123, "containerType", expressionContext); // then verify(leftOperandHandler1).update(argThat(leftOperand -> leftOperand.getName().equals("data1")), anyMap(), eq("value1"), eq(123L), eq("containerType")); verify(leftOperandHandler2).delete(argThat(leftOperand -> leftOperand.getName().equals("data2")), eq(123L), eq("containerType")); } @Test public void executeOperator_should_call_OperationStrategies() throws Exception { // given final SExpression sExpression = mock(SExpression.class); final SOperation op1 = buildOperation(TYPE_1, "data1", ASSIGNMENT, sExpression); final SOperation op2 = buildOperation(TYPE_2, "data2", SOperatorType.XPATH_UPDATE_QUERY, sExpression); final SOperation op3 = buildOperation(TYPE_2, "data2", SOperatorType.JAVA_METHOD, sExpression); final Map inputValues = new HashMap<>(); final SExpressionContext expressionContext = new SExpressionContext(123l, "containerType", 987L, inputValues); final List operations = asList(op1, op2, op3); given(persistRightOperandResolver.shouldPersistByPosition(0, operations)).willReturn(true); given(persistRightOperandResolver.shouldPersistByPosition(1, operations)).willReturn(false); given(persistRightOperandResolver.shouldPersistByPosition(2, operations)).willReturn(true); given(persistRightOperandResolver.shouldPersistByValue(any(), eq(true), eq(true))).willReturn(true); given(persistRightOperandResolver.shouldPersistByValue(any(), eq(false), eq(true))).willReturn(false); given(expressionResolverService.evaluate(sExpression, expressionContext)).willReturn(1983L); doReturn("value1").when(assignmentOperationExecutorStrategy).computeNewValueForLeftOperand(op1, 1983L, expressionContext, true); doReturn("value3").when(javaMethodOperationExecutorStrategy).computeNewValueForLeftOperand(op3, 1983L, expressionContext, true); // when operationServiceImpl.executeOperators(operations, expressionContext); // then assertThat(expressionContext.getInputValues().get("data1")).isEqualTo("value1"); assertThat(expressionContext.getInputValues().get("data2")).isEqualTo("value3"); } @Test public void should_retrieveLeftOperandsAndPutItInExpressionContextIfNotIn_do_not_override_value_in_map() throws Exception { // given final SOperation op1 = buildOperation(TYPE_2, "data1", SOperatorType.XPATH_UPDATE_QUERY); final SExpressionContext expressionContext = new SExpressionContext(123l, "containerType", 987L, Collections.singletonMap("data1", "originalValue")); // when operationServiceImpl.retrieveLeftOperandsAndPutItInExpressionContextIfNotIn(asList(op1), 123, "containerType", expressionContext); // then verify(leftOperandHandler2, times(1)).loadLeftOperandInContext(eq(asList(op1.getLeftOperand())), anyLong(), anyString(), any(SExpressionContext.class)); assertThat(expressionContext.getInputValues().get("data1")).isEqualTo("originalValue"); } @Test public void executeOperatorsShouldReturnASingleUpdateForTheSameDataOfTheSameOperator() throws Exception { // given final SOperation operation1 = buildOperation(TYPE_1, "data1", SOperatorType.JAVA_METHOD); final SOperation operation2 = buildOperation(TYPE_1, "data1", SOperatorType.JAVA_METHOD); final List operations = asList(operation1, operation2); final SExpressionContext expressionContext = new SExpressionContext(123l, "containerType", 987L, Collections. singletonMap("data1", "givenValue")); // when final Map updates = operationServiceImpl.executeOperators(operations, expressionContext); // then assertThat(updates).hasSize(1); final SLeftOperandImpl leftOperand = buildLeftOperand("type1", "data1"); assertThat(updates.containsKey(leftOperand)); assertThat(updates.get(leftOperand)) .isEqualToComparingFieldByField(new LeftOperandUpdateStatus(SOperatorType.JAVA_METHOD)); } @Test public void executeOperatorsShouldReturnATwoUpdatesForTheDifferentDataOfTheSameOperator() throws Exception { // given final List operations = new ArrayList<>(); operations.add(buildOperation(TYPE_1, "data2", SOperatorType.JAVA_METHOD)); operations.add(buildOperation(TYPE_1, "data1", SOperatorType.JAVA_METHOD)); final SExpressionContext expressionContext = new SExpressionContext(123l, "containerType", 987L, Collections. singletonMap("data1", "givenValue")); final OperationServiceImpl spy = spy(operationServiceImpl); // when final Map updates = spy.executeOperators(operations, expressionContext); // then assertThat(updates).hasSize(2); final SLeftOperandImpl data2Key = buildLeftOperand("type1", "data2"); final SLeftOperandImpl data1Key = buildLeftOperand("type1", "data1"); assertThat(updates).containsKeys(data2Key, data1Key); assertThat(updates.get(data1Key)) .isEqualToComparingFieldByField(new LeftOperandUpdateStatus(SOperatorType.JAVA_METHOD)); assertThat(updates.get(data2Key)) .isEqualToComparingFieldByField(new LeftOperandUpdateStatus(SOperatorType.JAVA_METHOD)); } @Test public void executeOperationShouldDoBatchGet() throws SOperationExecutionException, SBonitaReadException { //given final List operations = new ArrayList<>(); operations.add(buildOperation(TYPE_1, "data1", SOperatorType.JAVA_METHOD)); operations.add(buildOperation(TYPE_1, "data2", SOperatorType.XPATH_UPDATE_QUERY)); operations.add(buildOperation(TYPE_2, "data3", SOperatorType.JAVA_METHOD)); operations.add(buildOperation(TYPE_2, "data4", SOperatorType.XPATH_UPDATE_QUERY)); operations.add(buildOperation(TYPE_2, "data5", SOperatorType.DELETION)); final HashMap inputValues = new HashMap<>(); final SExpressionContext expressionContext = new SExpressionContext(123l, "containerType", 987L, inputValues); //when operationServiceImpl.retrieveLeftOperandsAndPutItInExpressionContextIfNotIn(operations, 123l/* data container */, "containerType", expressionContext); //then verify(leftOperandHandler1, times(1)).loadLeftOperandInContext(leftOperandCaptor1.capture(), anyLong(), anyString(), eq(expressionContext)); final List value1 = leftOperandCaptor1.getValue(); assertThat(value1).extracting("name").containsOnly("data1", "data2"); verify(leftOperandHandler2, times(1)).loadLeftOperandInContext(leftOperandCaptor2.capture(), anyLong(), anyString(), eq(expressionContext)); final List value2 = leftOperandCaptor2.getValue(); assertThat(value2).extracting("name").containsOnly("data3", "data4", "data5"); } @Test public void should_not_load_assignment_operations() throws SOperationExecutionException, SBonitaReadException { //given final List operations = new ArrayList<>(); operations.add(buildOperation(TYPE_1, "data1", JAVA_METHOD)); operations.add(buildOperation(TYPE_1, "data2", ASSIGNMENT)); operations.add(buildOperation(TYPE_2, "data3", JAVA_METHOD)); operations.add(buildOperation(TYPE_2, "data4", ASSIGNMENT)); operations.add(buildOperation(TYPE_2, "data5", ASSIGNMENT)); final HashMap inputValues = new HashMap<>(); final SExpressionContext expressionContext = new SExpressionContext(123l, "containerType", 987L, inputValues); //when operationServiceImpl.retrieveLeftOperandsAndPutItInExpressionContextIfNotIn(operations, 123l/* data container */, "containerType", expressionContext); //then verify(leftOperandHandler1, times(1)).loadLeftOperandInContext(leftOperandCaptor1.capture(), anyLong(), anyString(), eq(expressionContext)); verify(leftOperandHandler2, times(1)).loadLeftOperandInContext(leftOperandCaptor2.capture(), anyLong(), anyString(), eq(expressionContext)); verify(leftOperandHandler3, times(0)).loadLeftOperandInContext(anyList(), anyLong(), anyString(), eq(expressionContext)); final List value1 = leftOperandCaptor1.getValue(); final List value2 = leftOperandCaptor2.getValue(); assertThat(value1).extracting("name").containsOnly("data1"); assertThat(value2).extracting("name").containsOnly("data3"); } @Test public void should_load_same_data_only_once() throws SOperationExecutionException, SBonitaReadException { //given final List operations = new ArrayList<>(); operations.add(buildOperation(TYPE_1, "data1", SOperatorType.JAVA_METHOD)); operations.add(buildOperation(TYPE_1, "data1", SOperatorType.XPATH_UPDATE_QUERY)); operations.add(buildOperation(TYPE_2, "data2", SOperatorType.JAVA_METHOD)); final HashMap inputValues = new HashMap<>(); final SExpressionContext expressionContext = new SExpressionContext(123l, "containerType", 987L, inputValues); //when operationServiceImpl.retrieveLeftOperandsAndPutItInExpressionContextIfNotIn(operations, 123l/* data container */, "containerType", expressionContext); //then verify(leftOperandHandler1, times(1)).loadLeftOperandInContext(leftOperandCaptor1.capture(), anyLong(), anyString(), eq(expressionContext)); verify(leftOperandHandler2, times(1)).loadLeftOperandInContext(leftOperandCaptor2.capture(), anyLong(), anyString(), eq(expressionContext)); assertThat(leftOperandCaptor1.getValue()).extracting("name").containsOnly("data1"); } @Test public void should_not_update_left_operand_context_when_new_update() throws Exception { //given final Map leftOperands = Collections . singletonMap( buildLeftOperand("type1", "data1"), new LeftOperandUpdateStatus(ASSIGNMENT)); //when final boolean shouldUpdateLeftOperandContext = operationServiceImpl.shouldUpdateLeftOperandContext(leftOperands, buildLeftOperand("type1", "data1"), new LeftOperandUpdateStatus( SOperatorType.JAVA_METHOD)); //then assertThat(shouldUpdateLeftOperandContext).isFalse(); } @Test public void should_not_update_left_operand_context_when_previous_was_a_deletion() throws Exception { //given final Map leftOperands = Collections . singletonMap( buildLeftOperand("type1", "data1"), new LeftOperandUpdateStatus(SOperatorType.DELETION)); //when final boolean shouldUpdateLeftOperandContext = operationServiceImpl.shouldUpdateLeftOperandContext(leftOperands, buildLeftOperand("type1", "data1"), new LeftOperandUpdateStatus( SOperatorType.JAVA_METHOD)); //then assertThat(shouldUpdateLeftOperandContext).isFalse(); } @Test public void should_update_left_operand_context_when_not_in_the_context() throws Exception { //given final Map leftOperands = Collections.emptyMap(); //when final boolean shouldUpdateLeftOperandContext = operationServiceImpl.shouldUpdateLeftOperandContext(leftOperands, buildLeftOperand("type1", "data1"), new LeftOperandUpdateStatus( SOperatorType.JAVA_METHOD)); //then assertThat(shouldUpdateLeftOperandContext).isTrue(); } @Test public void should_update_left_operand_context_when_new_operation_is_deletion() throws Exception { //given final LeftOperandUpdateStatus previousUpdateState = new LeftOperandUpdateStatus(SOperatorType.JAVA_METHOD); final Map leftOperands = Collections . singletonMap( buildLeftOperand("type1", "data1"), previousUpdateState); //when new java method must be persisted again final boolean shouldUpdateLeftOperandContext = operationServiceImpl.shouldUpdateLeftOperandContext(leftOperands, buildLeftOperand("type1", "data1"), new LeftOperandUpdateStatus( SOperatorType.DELETION)); //then assertThat(shouldUpdateLeftOperandContext).isTrue(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/OperationsAnalyzerTest.java ================================================ /** * 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.core.operation.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.core.operation.impl.OperationMockBuilder.buildExpression; import static org.bonitasoft.engine.core.operation.impl.OperationMockBuilder.buildMockLeftOperand; import static org.bonitasoft.engine.core.operation.impl.OperationMockBuilder.buildMockOperation; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.impl.SLeftOperandImpl; import org.bonitasoft.engine.core.operation.model.impl.SOperationImpl; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.impl.SExpressionImpl; import org.junit.Test; public class OperationsAnalyzerTest { private OperationsAnalyzer dependencyFinder = new OperationsAnalyzer(); @Test public void findDependencyIndex_should_return_minus_one_if_index_is_out_of_list() throws Exception { //when int index = dependencyFinder.findBusinessDataDependencyIndex("address", 1, Collections. emptyList()); //then assertThat(index).isEqualTo(-1); } @Test public void findDependencyIndex_should_return_minus_one_if_right_operand_is_null() throws Exception { //when int index = dependencyFinder.findBusinessDataDependencyIndex("address", 0, Arrays. asList( buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, (SExpressionImpl) null))); //then assertThat(index).isEqualTo(-1); } @Test public void findDependencyIndex_should_return_zero_if_expression_is_dependency_of_first_operation() throws Exception { //given String dataName1 = "address1"; String dataName2 = "address2"; ExpressionType type = ExpressionType.TYPE_BUSINESS_DATA; List dependencies = Collections.emptyList(); SExpressionImpl rightOperand1 = buildExpression(dataName1, type, dependencies); SExpressionImpl rightOperand2 = buildExpression(dataName2, type, dependencies); List operations = Arrays. asList( buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand1), buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand2)); //when int index = dependencyFinder.findBusinessDataDependencyIndex(dataName1, 0, operations); //then assertThat(index).isEqualTo(0); } @Test public void findDependencyIndex_should_return_lastIndex_if_expression_is_dependency_of_last_operation() throws Exception { //given String dataName1 = "address1"; String dataName2 = "address2"; ExpressionType type = ExpressionType.TYPE_BUSINESS_DATA; List dependencies = Collections.emptyList(); SExpressionImpl rightOperand1 = buildExpression(dataName1, type, dependencies); SExpressionImpl rightOperand2 = buildExpression(dataName2, type, dependencies); List operations = Arrays. asList( buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand1), buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand2)); //when int index = dependencyFinder.findBusinessDataDependencyIndex(dataName2, 0, operations); //then assertThat(index).isEqualTo(1); } @Test public void findDependencyIndex_should_return_minus_one_if_find_expression_with_same_name_but_different_type() throws Exception { //given String dataName1 = "address1"; List dependencies = Collections.emptyList(); SExpressionImpl rightOperand1 = buildExpression(dataName1, ExpressionType.TYPE_CONDITION, dependencies); List operations = Arrays . asList(buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand1)); //when int index = dependencyFinder.findBusinessDataDependencyIndex(dataName1, 0, operations); //then assertThat(index).isEqualTo(-1); } @Test public void findDependencyIndex_should_return_minus_one_if_expression_is_dependency_at_a_index_before_fromIndex() throws Exception { //given String dataName1 = "address1"; String dataName2 = "address2"; ExpressionType type = ExpressionType.TYPE_BUSINESS_DATA; List dependencies = Collections.emptyList(); SExpressionImpl rightOperand1 = buildExpression(dataName1, type, dependencies); SExpressionImpl rightOperand2 = buildExpression(dataName2, type, dependencies); List operations = Arrays. asList( buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand1), buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand2)); //when int index = dependencyFinder.findBusinessDataDependencyIndex(dataName1, 1, operations); //then assertThat(index).isEqualTo(-1); } @Test public void findDependencyIndex_should_look_at_first_dependency_level() throws Exception { //given String dataName1 = "address1"; ExpressionType type = ExpressionType.TYPE_BUSINESS_DATA; SExpressionImpl dataExpression = buildExpression(dataName1, type, Collections. emptyList()); SExpressionImpl rightOperand = buildExpression("myScript", ExpressionType.TYPE_READ_ONLY_SCRIPT, Collections. singletonList(dataExpression)); List operations = Arrays . asList(buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand)); //when int index = dependencyFinder.findBusinessDataDependencyIndex(dataName1, 0, operations); //then assertThat(index).isEqualTo(0); } @Test public void findDependencyIndex_should_look_at_all_dependency_levels() throws Exception { //given String dataName1 = "address1"; ExpressionType type = ExpressionType.TYPE_BUSINESS_DATA; SExpressionImpl dataExpression = buildExpression(dataName1, type, Collections. emptyList()); SExpressionImpl dependencyL11 = buildExpression("dep11", type, Collections. emptyList()); SExpressionImpl dependencyL12 = buildExpression("dep12", ExpressionType.TYPE_READ_ONLY_SCRIPT, Collections. singletonList(dataExpression)); SExpressionImpl rightOperand = buildExpression("myScript", ExpressionType.TYPE_READ_ONLY_SCRIPT, Arrays. asList(dependencyL11, dependencyL12)); List operations = Arrays . asList(buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, rightOperand)); //when int index = dependencyFinder.findBusinessDataDependencyIndex(dataName1, 0, operations); //then assertThat(index).isEqualTo(0); } @Test public void calculate_indexes_should_return_positive_values_when_left_operand_is_found() throws Exception { //given SLeftOperandImpl data1 = buildMockLeftOperand(SLeftOperand.TYPE_BUSINESS_DATA, "data1"); SOperationImpl op1 = buildMockOperation(data1); SOperationImpl op2 = buildMockOperation(data1); SOperationImpl op3 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, "data2"); SOperationImpl op4 = buildMockOperation(data1); //when LeftOperandIndexes indexes = dependencyFinder.calculateIndexes(0, Arrays. asList(op1, op2, op3, op4)); //then assertThat(indexes.getNextIndex()).isEqualTo(1); assertThat(indexes.getLastIndex()).isEqualTo(3); } @Test public void calculate_indexes_should_return_negative_value_for_nextIndex_when_is_last() throws Exception { //given SLeftOperandImpl data1 = buildMockLeftOperand(SLeftOperand.TYPE_BUSINESS_DATA, "data1"); SOperationImpl op1 = buildMockOperation(data1); SOperationImpl op2 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, "data2"); //when LeftOperandIndexes indexes = dependencyFinder.calculateIndexes(1, Arrays. asList(op1, op2)); //then assertThat(indexes.getNextIndex()).isEqualTo(-1); assertThat(indexes.getLastIndex()).isEqualTo(1); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/PersistRightOperandResolverTest.java ================================================ /** * 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.core.operation.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.core.operation.impl.OperationMockBuilder.buildMockOperation; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.impl.SOperationImpl; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class PersistRightOperandResolverTest { @Mock private OperationsAnalyzer operationsAnalyzer; @InjectMocks private PersistRightOperandResolver resolver; @Test public void should_not_persist_if_not_business_data() throws Exception { //when boolean persist = resolver.shouldPersistByPosition(0, Arrays. asList(buildMockOperation(SLeftOperand.TYPE_DATA))); //then assertThat(persist).isFalse(); } @Test public void should_persist_bo_if_is_last_operation_for_this_left_operand() throws Exception { //given SOperationImpl addressOp1 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, "address"); SOperationImpl employeeOp1 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, "employee"); SOperationImpl addressOp2 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, "address"); List operations = Arrays. asList(addressOp1, employeeOp1, addressOp2); LeftOperandIndexes indexes = new LeftOperandIndexes(); indexes.setLastIndex(2); given(operationsAnalyzer.calculateIndexes(2, operations)).willReturn(indexes); //when boolean persist = resolver.shouldPersistByPosition(2, operations); //then assertThat(persist).isTrue(); verify(operationsAnalyzer, never()).findBusinessDataDependencyIndex(anyString(), anyInt(), anyList()); } @Test public void should_persist_bo_when_next_operation_on_this_left_operand_is_after_next_usage_as_dependency() throws Exception { //given SOperationImpl addressOp1 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, "address"); SOperationImpl employeeOp1 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, "employee"); SOperationImpl addressOp2 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, "address"); SOperationImpl employeeOp2 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, "employee"); List operations = Arrays. asList(addressOp1, employeeOp1, addressOp2, employeeOp2); int currentIndex = 0; given(operationsAnalyzer.findBusinessDataDependencyIndex("address", currentIndex + 1, operations)) .willReturn(1); LeftOperandIndexes indexes = new LeftOperandIndexes(); indexes.setNextIndex(2); given(operationsAnalyzer.calculateIndexes(0, operations)).willReturn(indexes); //when boolean persist = resolver.shouldPersistByPosition(currentIndex, operations); //then assertThat(persist).isTrue(); } @Test public void should_not_persist_bo_when_is_not_last_operation_for_this_left_operand_neither_is_dependency() throws Exception { //given SOperationImpl addressOp1 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, "address"); SOperationImpl employeeOp1 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, "employee"); SOperationImpl addressOp2 = buildMockOperation(SLeftOperand.TYPE_BUSINESS_DATA, "address"); int indexOfCurrentOperation = 0; List operations = Arrays. asList(addressOp1, employeeOp1, addressOp2); LeftOperandIndexes indexes = new LeftOperandIndexes(); indexes.setLastIndex(2); indexes.setNextIndex(2); given(operationsAnalyzer.calculateIndexes(indexOfCurrentOperation, operations)).willReturn(indexes); given(operationsAnalyzer.findBusinessDataDependencyIndex("address", indexOfCurrentOperation + 1, operations)) .willReturn(-1); //when boolean persist = resolver.shouldPersistByPosition(indexOfCurrentOperation, operations); //then assertThat(persist).isFalse(); } @Test public void should_not_persist_when_right_operand_is_null_and_should_not_persist_on_null() throws Exception { //then assertThat(resolver.shouldPersistByValue(null, true, false)).isFalse(); } @Test public void should_not_persist_when_right_operand_is_null_and_should_not_persist_by_position() throws Exception { //then assertThat(resolver.shouldPersistByValue(null, false, true)).isFalse(); } @Test public void should_persist_when_right_operand_is_null_and_should_not_persist_by_position_and_on_null() throws Exception { //then assertThat(resolver.shouldPersistByValue(null, true, true)).isTrue(); } @Test public void should_persist_if_right_operand_is_not_null_and_should_persist_by_position() throws Exception { //then assertThat(resolver.shouldPersistByValue("anyNotNullValue", true, true)).isTrue(); } @Test public void should_not_persist_if_right_operand_is_not_null_and_should_not_persist_by_position() throws Exception { //then assertThat(resolver.shouldPersistByValue("anyNotNullValue", false, true)).isFalse(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/operation/impl/XpathUpdateQueryOperationExecutorStrategyTest.java ================================================ /** * 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.core.operation.impl; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class XpathUpdateQueryOperationExecutorStrategyTest { @Test public void should_not_persist_on_null() throws Exception { assertThat(new XpathUpdateQueryOperationExecutorStrategy().shouldPersistOnNullValue()).isFalse(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/ProcessDefinitionServiceImplTest.java ================================================ /** * 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.core.process.definition; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.*; import java.io.IOException; import java.io.Serializable; 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 org.bonitasoft.engine.bpm.bar.ProcessDefinitionBARContribution; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.bpm.process.impl.internal.DesignProcessDefinitionImpl; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.commons.Pair; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; import org.bonitasoft.engine.core.process.definition.exception.SProcessDeploymentInfoUpdateException; 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.SProcessDefinitionDesignContent; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilder; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.impl.ExpressionImpl; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet * @author Emmanuel Duchastenier */ @RunWith(MockitoJUnitRunner.class) public class ProcessDefinitionServiceImplTest { private static final long PROCESS_DEFINITION_DEPLOY_ID = 3L; private static final long PROCESS_ID = 42L; private static final String THE_PROCESS_XML_CONTENT = "THE PROCESS XML CONTENT"; @Mock private CacheService cacheService; @Mock private ReadPersistenceService persistenceService; @Mock private QueriableLoggerService queriableLoggerService; @Mock private Recorder recorder; @Mock private ReadSessionAccessor sessionAccessor; @Mock private SessionService sessionService; @Mock private ProcessDefinitionBARContribution processDefinitionBARContribution; @InjectMocks @Spy private ProcessDefinitionServiceImpl processDefinitionServiceImpl; private SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo; private DesignProcessDefinition designProcessDefinition; @Before public void before() throws SProcessDefinitionNotFoundException, SBonitaReadException, IOException { sProcessDefinitionDeployInfo = new SProcessDefinitionDeployInfo(); sProcessDefinitionDeployInfo.setId(PROCESS_DEFINITION_DEPLOY_ID); final SProcessDefinitionDesignContent designContent = new SProcessDefinitionDesignContent(); designContent.setContent(THE_PROCESS_XML_CONTENT); sProcessDefinitionDeployInfo.setDesignContent(designContent); doReturn(sProcessDefinitionDeployInfo).when(processDefinitionServiceImpl).getProcessDeploymentInfo(PROCESS_ID); designProcessDefinition = new DesignProcessDefinitionImpl("THE NAME", "THE VERSION"); doReturn(designProcessDefinition).when(processDefinitionBARContribution) .convertXmlToProcess(THE_PROCESS_XML_CONTENT); processDefinitionServiceImpl.processDefinitionBARContribution = processDefinitionBARContribution; } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfos(int, int, java.lang.String, org.bonitasoft.engine.persistence.OrderByType)} * . */ @Test public void getProcessDeploymentInfos() throws Exception { // Given final List sProcessDefinitionDeployInfos = new ArrayList<>(3); doReturn(sProcessDefinitionDeployInfos).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When final List processDeploymentInfos = processDefinitionServiceImpl .getProcessDeploymentInfos(0, 10, "id", OrderByType.ASC); // Then assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos); } @Test(expected = SBonitaReadException.class) public void getProcessDeploymentInfosThrowException() throws Exception { // Given doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getProcessDeploymentInfos(0, 10, "id", OrderByType.ASC); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfos()}. */ @Test public void getNumberOfProcessDeploymentInfos() throws Exception { // Given final long numberOfProcessDeploymentInfos = 9; doReturn(numberOfProcessDeploymentInfos).when(persistenceService) .selectOne(ArgumentMatchers.> any()); // When final long result = processDefinitionServiceImpl.getNumberOfProcessDeploymentInfos(); // Then assertEquals(numberOfProcessDeploymentInfos, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfProcessDeploymentInfosThrowException() throws Exception { // Given doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectOne(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getNumberOfProcessDeploymentInfos(); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfo(long)}. */ @Test public void getProcessDeploymentInfoById() throws Exception { // Given final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class); doReturn(sProcessDefinitionDeployInfo).when(persistenceService) .selectOne(ArgumentMatchers.> any()); // When final SProcessDefinitionDeployInfo result = processDefinitionServiceImpl.getProcessDeploymentInfo(2); // Then assertEquals(sProcessDefinitionDeployInfo, result); } @Test(expected = SBonitaReadException.class) public void getProcessDeploymentInfoByIdThrowException() throws Exception { // Given doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectOne(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getProcessDeploymentInfo(2); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosByActivationState(org.bonitasoft.engine.bpm.process.ActivationState)} * . */ @Test public void getNumberOfProcessDeploymentInfosByActivationState() throws Exception { // Given final long numberOfProcessDeploymentInfos = 9; doReturn(numberOfProcessDeploymentInfos).when(persistenceService) .selectOne(ArgumentMatchers.> any()); // When final long result = processDefinitionServiceImpl .getNumberOfProcessDeploymentInfosByActivationState(ActivationState.DISABLED); // Then assertEquals(numberOfProcessDeploymentInfos, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfProcessDeploymentInfosByActivationStateThrowException() throws Exception { // Given doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectOne(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosByActivationState(ActivationState.DISABLED); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDefinitionIds(org.bonitasoft.engine.bpm.process.ActivationState, int, int)} * . */ @Test public void getProcessDefinitionIdsByActivationState() throws Exception { // Given final List processDefinitionIds = Arrays.asList(3L); doReturn(processDefinitionIds).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When final List result = processDefinitionServiceImpl.getProcessDefinitionIds(ActivationState.DISABLED, 0, 10); // Then assertEquals(processDefinitionIds, result); } @Test(expected = SBonitaReadException.class) public void getProcessDefinitionIdsByActivationStateThrowException() throws Exception { // Given doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getProcessDefinitionIds(ActivationState.DISABLED, 0, 10); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDefinitionIds(int, int)}. */ @Test public void getProcessDefinitionIds() throws Exception { // Given final List processDefinitionIds = Arrays.asList(3L); doReturn(processDefinitionIds).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When final List result = processDefinitionServiceImpl.getProcessDefinitionIds(0, 10); // Then assertEquals(processDefinitionIds, result); } @Test(expected = SBonitaReadException.class) public void getProcessDefinitionIdsThrowException() throws Exception { // Given doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getProcessDefinitionIds(0, 10); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getLatestProcessDefinitionId(java.lang.String)}. */ @Test public void getLatestProcessDefinitionId() throws Exception { // Given final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class); doReturn(6L).when(sProcessDefinitionDeployInfo).getProcessId(); final List sProcessDefinitionDeployInfos = Arrays .asList(sProcessDefinitionDeployInfo); doReturn(sProcessDefinitionDeployInfos).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When final long processDeploymentInfoId = processDefinitionServiceImpl.getLatestProcessDefinitionId("name"); // Then assertEquals(sProcessDefinitionDeployInfos.get(0).getProcessId(), processDeploymentInfoId); } @Test(expected = SBonitaReadException.class) public void getLatestProcessDefinitionIdThrowException() throws Exception { // Given doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getLatestProcessDefinitionId("name"); } /** * Test method for {@link ProcessDefinitionService#getProcessDefinitionId(String, String)}. */ @Test public void getProcessDefinitionId_should_return_id_of_process_definition_with_given_name_and_version() throws Exception { // Given final Map parameters = new HashMap<>(); String name = "proc"; String version = "1.0"; parameters.put("name", name); parameters.put("version", version); SelectOneDescriptor selectOneDescriptor = new SelectOneDescriptor<>( "getProcessDefinitionIdByNameAndVersion", parameters, SProcessDefinitionDeployInfo.class, Long.class); final long processId = 9; doReturn(processId).when(persistenceService).selectOne(selectOneDescriptor); // When final long result = processDefinitionServiceImpl.getProcessDefinitionId(name, version); // Then assertEquals(processId, result); } @Test(expected = SProcessDefinitionNotFoundException.class) public void getProcessDefinitionId_should_return_throw_SProcessDefinitionNotFoundException_when_persistenceSservice_returns_null() throws Exception { // Given final Map parameters = new HashMap<>(); String name = "proc"; String version = "1.0"; parameters.put("name", name); parameters.put("version", version); SelectOneDescriptor selectOneDescriptor = new SelectOneDescriptor<>( "getProcessDefinitionIdByNameAndVersion", parameters, SProcessDefinitionDeployInfo.class, Long.class); doReturn(null).when(persistenceService).selectOne(selectOneDescriptor); // When processDefinitionServiceImpl.getProcessDefinitionId("name", "version"); // Then exception } @Test(expected = SBonitaReadException.class) public void getProcessDefinitionId_should_throw_SBonitaReadException_when_persistenceSservice_throws_exception() throws Exception { // Given doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectOne(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getProcessDefinitionId("name", "version"); } @Test public void getProcessDefinition_from_cache() throws Exception { sProcessDefinitionDeployInfo.setLastUpdateDate(5478L); final SProcessDefinitionImpl sProcessDefinition = new SProcessDefinitionImpl("a", "b"); doReturn(new Pair(5478L, sProcessDefinition)).when(processDefinitionServiceImpl) .getSProcessDefinitionFromCache(PROCESS_ID); final SProcessDefinition processDefinition = processDefinitionServiceImpl.getProcessDefinition(PROCESS_ID); assertThat(processDefinition).as("returned process definition from cache").isEqualTo(sProcessDefinition); verify(cacheService, times(0)).store(anyString(), any(Serializable.class), any()); } @Test public void getProcessDefinition_from_database() throws Exception { sProcessDefinitionDeployInfo.setLastUpdateDate(5478L); final SProcessDefinitionImpl sProcessDefinition = new SProcessDefinitionImpl("a", "b"); doReturn(sProcessDefinition).when(processDefinitionServiceImpl) .convertDesignProcessDefinition(designProcessDefinition); final SProcessDefinition processDefinition = processDefinitionServiceImpl.getProcessDefinition(PROCESS_ID); assertThat(processDefinition).as("returned process definition from database").isEqualTo(sProcessDefinition); verify(cacheService, times(1)).store(anyString(), any(Serializable.class), any()); } @Test public void getProcessDefinition_from_database_when_cache_is_outdated() throws Exception { sProcessDefinitionDeployInfo.setLastUpdateDate(5478L); final SProcessDefinitionImpl processDefinitionB = new SProcessDefinitionImpl("a", "b"); final SProcessDefinitionImpl processDefinitionC = new SProcessDefinitionImpl("a", "c"); doReturn(new Pair(5477L, processDefinitionB)).when(processDefinitionServiceImpl) .getSProcessDefinitionFromCache(PROCESS_ID); doReturn(processDefinitionC).when(processDefinitionServiceImpl) .convertDesignProcessDefinition(designProcessDefinition); final SProcessDefinition processDefinition = processDefinitionServiceImpl.getProcessDefinition(PROCESS_ID); assertThat(processDefinition).as("returned process definition from cache").isEqualTo(processDefinitionC); verify(cacheService, times(1)).store(anyString(), any(Serializable.class), any()); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#updateProcessDefinitionDeployInfo(long, org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor)} * . */ @Test public void updateProcessDefinitionDeployInfo() throws Exception { // Given final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class); final SProcessDefinitionDeployInfoUpdateBuilder updateBuilder = BuilderFactory .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class) .createNewInstance(); updateBuilder.updateDisplayName("newDisplayName"); doReturn(sProcessDefinitionDeployInfo).when(persistenceService) .selectOne(ArgumentMatchers.> any()); // When final SProcessDefinitionDeployInfo result = processDefinitionServiceImpl.updateProcessDefinitionDeployInfo(3, updateBuilder.done()); // Then assertNotNull(result); assertEquals(sProcessDefinitionDeployInfo, result); } @Test public void updateProcessDefinitionDeployInfo_create_business_log() throws Exception { // Given doReturn(true).when(queriableLoggerService).isLoggable(anyString(), any(SQueriableLogSeverity.class)); final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class); final SProcessDefinitionDeployInfoUpdateBuilder updateBuilder = BuilderFactory .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class) .createNewInstance(); updateBuilder.updateDisplayName("newDisplayName"); doReturn(sProcessDefinitionDeployInfo).when(persistenceService) .selectOne(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.updateProcessDefinitionDeployInfo(3, updateBuilder.done(), "the business log"); verify(queriableLoggerService).log(anyString(), eq("updateProcessDeploymentInfo"), ArgumentMatchers. argThat(logs -> logs.getRawMessage().equals("the business log"))); } @Test public void updateProcessDefinitionDeployInfo_truncate_log_when_too_long() throws Exception { // Given doReturn(true).when(queriableLoggerService).isLoggable(anyString(), any(SQueriableLogSeverity.class)); final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class); final SProcessDefinitionDeployInfoUpdateBuilder updateBuilder = BuilderFactory .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class) .createNewInstance(); updateBuilder.updateDisplayName("newDisplayName"); doReturn(sProcessDefinitionDeployInfo).when(persistenceService).selectOne(any()); // When final StringBuilder string1024 = new StringBuilder(); for (int i = 0; i < 1024; i++) { string1024.append("H"); } processDefinitionServiceImpl.updateProcessDefinitionDeployInfo(3, updateBuilder.done(), string1024.toString()); verify(queriableLoggerService).log(anyString(), eq("updateProcessDeploymentInfo"), ArgumentMatchers . argThat(log -> log.getRawMessage().equals(string1024.substring(0, 255)))); } @Test public void updateLastUpdateDateInCache_should_update_the_lastUpdateDate_in_cache_if_exists() throws Exception { // Given final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class); doReturn(5478L).when(sProcessDefinitionDeployInfo).getLastUpdateDate(); final SProcessDefinitionImpl sProcessDefinition = new SProcessDefinitionImpl("a", "b"); sProcessDefinition.setId(56L); doReturn(new Pair(541L, sProcessDefinition)).when(cacheService).get(anyString(), any()); // When processDefinitionServiceImpl.updateSProcessDefinitionTimestampInCache(56L, sProcessDefinitionDeployInfo); // Then verify(cacheService).store(ProcessDefinitionService.PROCESS_CACHE_NAME, 56L, new Pair(5478L, sProcessDefinition)); } @Test public void updateLastUpdateDateInCache_should_update_the_lastUpdateDate_in_cache_if_not_exists() throws Exception { // Given final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class); // When processDefinitionServiceImpl.updateSProcessDefinitionTimestampInCache(3L, sProcessDefinitionDeployInfo); // Then verify(cacheService, times(0)).store(anyString(), any(Serializable.class), any()); } @Test(expected = SProcessDefinitionNotFoundException.class) public final void updateProcessDefinitionDeployInfoNotExists() throws Exception { // Given final SProcessDefinitionDeployInfoUpdateBuilder updateBuilder = BuilderFactory .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class) .createNewInstance(); doReturn(null).when(persistenceService) .selectOne(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.updateProcessDefinitionDeployInfo(4, updateBuilder.done()); } @Test(expected = SProcessDeploymentInfoUpdateException.class) public final void updateProcessDefinitionDeployInfoThrowException() throws Exception { // Given final SProcessDefinitionDeployInfo sProcessDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class); final SProcessDefinitionDeployInfoUpdateBuilder updateBuilder = BuilderFactory .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class) .createNewInstance(); updateBuilder.updateDisplayName("newDisplayName"); doReturn(sProcessDefinitionDeployInfo).when(persistenceService).selectOne(any()); doThrow(new SRecorderException("plop")).when(recorder).recordUpdate(any(UpdateRecord.class), nullable(String.class)); // When processDefinitionServiceImpl.updateProcessDefinitionDeployInfo(3, updateBuilder.done()); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfosStartedBy(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void searchProcessDeploymentInfosStartedBy() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long startedBy = 9; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "StartedBy", options, Collections.singletonMap("startedBy", (Object) startedBy))) .thenReturn(new ArrayList()); // When final List result = processDefinitionServiceImpl .searchProcessDeploymentInfosStartedBy(startedBy, options); // Then assertNotNull(result); } @Test(expected = SBonitaReadException.class) public void searchProcessDeploymentInfosStartedByThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long startedBy = 9; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "StartedBy", options, Collections.singletonMap("startedBy", (Object) startedBy))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.searchProcessDeploymentInfosStartedBy(startedBy, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosStartedBy(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getNumberOfProcessDeploymentInfosStartedBy() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long startedBy = 6; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "StartedBy", options, Collections.singletonMap("startedBy", (Object) startedBy))).thenReturn(1L); // When final long result = processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosStartedBy(startedBy, options); // Then assertEquals(1L, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfProcessDeploymentInfosStartedByThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long startedBy = 6; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "StartedBy", options, Collections.singletonMap("startedBy", (Object) startedBy))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosStartedBy(startedBy, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfos(org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void searchProcessDeploymentInfos() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, options, null)) .thenReturn(new ArrayList()); // When final List result = processDefinitionServiceImpl .searchProcessDeploymentInfos(options); // Then assertNotNull(result); } @Test(expected = SBonitaReadException.class) public void searchProcessDeploymentInfosThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, options, null)) .thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.searchProcessDeploymentInfos(options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfos(org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getNumberOfProcessDeploymentInfosByOptions() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, options, null)).thenReturn(1L); // When final long result = processDefinitionServiceImpl.getNumberOfProcessDeploymentInfos(options); // Then assertEquals(1L, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfProcessDeploymentInfosByOptionsThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, options, null)) .thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.getNumberOfProcessDeploymentInfos(options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfosCanBeStartedBy(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void searchProcessDeploymentInfosCanBeStartedBy() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "UserCanStart", options, Collections.singletonMap("userId", (Object) userId))) .thenReturn(new ArrayList()); // When final List result = processDefinitionServiceImpl .searchProcessDeploymentInfosCanBeStartedBy(userId, options); // Then assertNotNull(result); } @Test(expected = SBonitaReadException.class) public void searchProcessDeploymentInfosCanBeStartedByThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "UserCanStart", options, Collections.singletonMap("userId", (Object) userId))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.searchProcessDeploymentInfosCanBeStartedBy(userId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosCanBeStartedBy(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getNumberOfProcessDeploymentInfosCanBeStartedBy() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "UserCanStart", options, Collections.singletonMap("userId", (Object) userId))).thenReturn(1L); // When final long result = processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosCanBeStartedBy(userId, options); // Then assertEquals(1L, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfProcessDeploymentInfosCanBeStartedByThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "UserCanStart", options, Collections.singletonMap("userId", (Object) userId))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosCanBeStartedBy(userId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void searchProcessDeploymentInfosCanStartForUsersManagedBy() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "UsersManagedByCanStart", options, Collections.singletonMap("managerUserId", (Object) userId))) .thenReturn(new ArrayList()); // When final List result = processDefinitionServiceImpl .searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(userId, options); // Then assertNotNull(result); } @Test(expected = SBonitaReadException.class) public void searchProcessDeploymentInfosCanBeStartedByUsersManagedByThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "UsersManagedByCanStart", options, Collections.singletonMap("managerUserId", (Object) userId))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(userId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedBy(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedBy() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "UsersManagedByCanStart", options, Collections.singletonMap("managerUserId", (Object) userId))).thenReturn(1L); // When final long result = processDefinitionServiceImpl .getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedBy(userId, options); // Then assertEquals(1L, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedByThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "UsersManagedByCanStart", options, Collections.singletonMap("managerUserId", (Object) userId))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedBy(userId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfos(long, org.bonitasoft.engine.persistence.QueryOptions, java.lang.String)} * . */ @Test public void searchProcessDeploymentInfosWithParameters() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; final String querySuffix = "suffix"; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, querySuffix, options, Collections.singletonMap("userId", (Object) userId))) .thenReturn(new ArrayList()); // When final List result = processDefinitionServiceImpl .searchProcessDeploymentInfos(userId, options, querySuffix); // Then assertNotNull(result); } @Test(expected = SBonitaReadException.class) public void searchProcessDeploymentInfosWithParametersThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; final String querySuffix = "suffix"; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, querySuffix, options, Collections.singletonMap("userId", (Object) userId))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.searchProcessDeploymentInfos(userId, options, querySuffix); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfos(long, org.bonitasoft.engine.persistence.QueryOptions, java.lang.String)} * . */ @Test public void getNumberOfProcessDeploymentInfosWithParameters() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; final String querySuffix = "suffix"; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, querySuffix, options, Collections.singletonMap("userId", (Object) userId))).thenReturn(1L); // When final long result = processDefinitionServiceImpl.getNumberOfProcessDeploymentInfos(userId, options, querySuffix); // Then assertEquals(1L, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfProcessDeploymentInfosWithParametersThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; final String querySuffix = "suffix"; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, querySuffix, options, Collections.singletonMap("userId", (Object) userId))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.getNumberOfProcessDeploymentInfos(userId, options, querySuffix); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchUncategorizedProcessDeploymentInfos(org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void searchUncategorizedProcessDeploymentInfos() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "Uncategorized", options, null)) .thenReturn( new ArrayList()); // When final List result = processDefinitionServiceImpl .searchUncategorizedProcessDeploymentInfos(options); // Then assertNotNull(result); } @Test(expected = SBonitaReadException.class) public void searchUncategorizedProcessDeploymentInfosThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "Uncategorized", options, null)) .thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.searchUncategorizedProcessDeploymentInfos(options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfUncategorizedProcessDeploymentInfos(org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getNumberOfUncategorizedProcessDeploymentInfos() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "Uncategorized", options, null)) .thenReturn(1L); // When final long result = processDefinitionServiceImpl.getNumberOfUncategorizedProcessDeploymentInfos(options); // Then assertEquals(1L, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfUncategorizedProcessDeploymentInfosThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "Uncategorized", options, null)) .thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.getNumberOfUncategorizedProcessDeploymentInfos(options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchUncategorizedProcessDeploymentInfosSupervisedBy(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void searchUncategorizedProcessDeploymentInfosSupervisedBy() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "UncategorizedAndWithSupervisor", options, Collections.singletonMap("userId", (Object) userId))) .thenReturn(new ArrayList()); // When final List result = processDefinitionServiceImpl .searchUncategorizedProcessDeploymentInfosSupervisedBy(userId, options); // Then assertNotNull(result); } @Test(expected = SBonitaReadException.class) public void searchUncategorizedProcessDeploymentInfosSupervisedByThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "UncategorizedAndWithSupervisor", options, Collections.singletonMap("userId", (Object) userId))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.searchUncategorizedProcessDeploymentInfosSupervisedBy(userId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfUncategorizedProcessDeploymentInfosSupervisedBy(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getNumberOfUncategorizedProcessDeploymentInfosSupervisedBy() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "UncategorizedAndWithSupervisor", options, Collections.singletonMap("userId", (Object) userId))).thenReturn(1L); // When final long result = processDefinitionServiceImpl .getNumberOfUncategorizedProcessDeploymentInfosSupervisedBy(userId, options); // Then assertEquals(1L, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfUncategorizedProcessDeploymentInfosSupervisedByThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "UncategorizedAndWithSupervisor", options, Collections.singletonMap("userId", (Object) userId))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.getNumberOfUncategorizedProcessDeploymentInfosSupervisedBy(userId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchUncategorizedProcessDeploymentInfosCanBeStartedBy(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void searchUncategorizedProcessDeploymentInfosCanBeStartedBy() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "UncategorizedUserCanStart", options, Collections.singletonMap("userId", (Object) userId))) .thenReturn(new ArrayList()); // When final List result = processDefinitionServiceImpl .searchUncategorizedProcessDeploymentInfosCanBeStartedBy(userId, options); // Then assertNotNull(result); } @Test(expected = SBonitaReadException.class) public void searchUncategorizedProcessDeploymentInfosCanBeStartedByThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "UncategorizedUserCanStart", options, Collections.singletonMap("userId", (Object) userId))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.searchUncategorizedProcessDeploymentInfosCanBeStartedBy(userId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedBy(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedBy() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "UncategorizedUserCanStart", options, Collections.singletonMap("userId", (Object) userId))).thenReturn(1L); // When final long result = processDefinitionServiceImpl .getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedBy(userId, options); // Then assertEquals(1L, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedByThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "UncategorizedUserCanStart", options, Collections.singletonMap("userId", (Object) userId))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedBy(userId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfosUnrelatedToCategory(long, int, int, org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion)} * . */ @Test public void getProcessDeploymentInfosUnrelatedToCategory() throws Exception { // Given final List sProcessDefinitionDeployInfos = new ArrayList<>(3); doReturn(sProcessDefinitionDeployInfos).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When final List processDeploymentInfos = processDefinitionServiceImpl .getProcessDeploymentInfosUnrelatedToCategory(9, 0, 10, ProcessDeploymentInfoCriterion.ACTIVATION_STATE_ASC); // Then assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos); } @Test(expected = SBonitaReadException.class) public void getProcessDeploymentInfosUnrelatedToCategoryThrowException() throws Exception { // Given doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getProcessDeploymentInfosUnrelatedToCategory(9, 0, 10, ProcessDeploymentInfoCriterion.ACTIVATION_STATE_ASC); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosUnrelatedToCategory(long)}. */ @Test public void getNumberOfProcessDeploymentInfosUnrelatedToCategory() throws Exception { // Given final long numberOfProcessDeploymentInfos = 9; doReturn(numberOfProcessDeploymentInfos).when(persistenceService) .selectOne(ArgumentMatchers.> any()); // When final long result = processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosUnrelatedToCategory(9); // Then assertEquals(numberOfProcessDeploymentInfos, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfProcessDeploymentInfosUnrelatedToCategoryThrowException() throws Exception { // Given doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectOne(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosUnrelatedToCategory(9); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfosOfCategory(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void searchProcessDeploymentInfosOfCategory() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final List sProcessDefinitionDeployInfos = new ArrayList<>(3); doReturn(sProcessDefinitionDeployInfos).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When final List processDeploymentInfos = processDefinitionServiceImpl .searchProcessDeploymentInfosOfCategory(9, options); // Then assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos); } @Test(expected = SBonitaReadException.class) public void searchProcessDeploymentInfosOfCategoryThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.searchProcessDeploymentInfosOfCategory(9, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfos(org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getProcessDeploymentInfosWithOptions() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final List sProcessDefinitionDeployInfos = new ArrayList<>(3); doReturn(sProcessDefinitionDeployInfos).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When final List processDeploymentInfos = processDefinitionServiceImpl .getProcessDeploymentInfos(options); // Then assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos); } @Test(expected = SBonitaReadException.class) public void getProcessDeploymentInfosWithOptionsThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getProcessDeploymentInfos(options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfosWithActorOnlyForGroup(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getProcessDeploymentInfosWithActorOnlyForGroup() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final List sProcessDefinitionDeployInfos = new ArrayList<>(3); doReturn(sProcessDefinitionDeployInfos).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When final List processDeploymentInfos = processDefinitionServiceImpl .getProcessDeploymentInfosWithActorOnlyForGroup(9, options); // Then assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos); } @Test(expected = SBonitaReadException.class) public void getProcessDeploymentInfosWithActorOnlyForGroupThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getProcessDeploymentInfosWithActorOnlyForGroup(9, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfosWithActorOnlyForGroups(java.util.List, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getProcessDeploymentInfosWithActorOnlyForGroups() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final List sProcessDefinitionDeployInfos = new ArrayList<>(3); doReturn(sProcessDefinitionDeployInfos).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When final List processDeploymentInfos = processDefinitionServiceImpl .getProcessDeploymentInfosWithActorOnlyForGroups( Arrays.asList(9L), options); // Then assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos); } @Test(expected = SBonitaReadException.class) public void getProcessDeploymentInfosWithActorOnlyForGroupsThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getProcessDeploymentInfosWithActorOnlyForGroups(Arrays.asList(9L), options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfosWithActorOnlyForRole(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getProcessDeploymentInfosWithActorOnlyForRole() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final List sProcessDefinitionDeployInfos = new ArrayList<>(3); doReturn(sProcessDefinitionDeployInfos).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When final List processDeploymentInfos = processDefinitionServiceImpl .getProcessDeploymentInfosWithActorOnlyForRole(9, options); // Then assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos); } @Test(expected = SBonitaReadException.class) public void getProcessDeploymentInfosWithActorOnlyForRoleThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getProcessDeploymentInfosWithActorOnlyForRole(9, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfosWithActorOnlyForRoles(java.util.List, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getProcessDeploymentInfosWithActorOnlyForRoles() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final List sProcessDefinitionDeployInfos = new ArrayList<>(3); doReturn(sProcessDefinitionDeployInfos).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When final List processDeploymentInfos = processDefinitionServiceImpl .getProcessDeploymentInfosWithActorOnlyForRoles( Arrays.asList(9L), options); // Then assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos); } @Test(expected = SBonitaReadException.class) public void getProcessDeploymentInfosWithActorOnlyForRolesThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getProcessDeploymentInfosWithActorOnlyForRoles(Arrays.asList(9L), options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfosWithActorOnlyForUser(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getProcessDeploymentInfosWithActorOnlyForUser() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final List sProcessDefinitionDeployInfos = new ArrayList<>(3); doReturn(sProcessDefinitionDeployInfos).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When final List processDeploymentInfos = processDefinitionServiceImpl .getProcessDeploymentInfosWithActorOnlyForUser(9, options); // Then assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos); } @Test(expected = SBonitaReadException.class) public void getProcessDeploymentInfosWithActorOnlyForUserThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getProcessDeploymentInfosWithActorOnlyForUser(9, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getProcessDeploymentInfosWithActorOnlyForUsers(java.util.List, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getProcessDeploymentInfosWithActorOnlyForUsers() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final List sProcessDefinitionDeployInfos = new ArrayList<>(3); doReturn(sProcessDefinitionDeployInfos).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When final List processDeploymentInfos = processDefinitionServiceImpl .getProcessDeploymentInfosWithActorOnlyForUsers( Arrays.asList(9L), options); // Then assertEquals(sProcessDefinitionDeployInfos, processDeploymentInfos); } @Test(expected = SBonitaReadException.class) public void getProcessDeploymentInfosWithActorOnlyForUsersThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectList(ArgumentMatchers.> any()); // When processDefinitionServiceImpl.getProcessDeploymentInfosWithActorOnlyForUsers(Arrays.asList(9L), options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchUsersWhoCanStartProcessDeploymentInfo(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void searchUsersWhoCanStartProcessDeploymentInfo() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long processDefinitionId = 9; when(persistenceService.searchEntity(SUser.class, "WhoCanStartProcess", options, Collections.singletonMap("processId", (Object) processDefinitionId))) .thenReturn(new ArrayList()); // When final List result = processDefinitionServiceImpl .searchUsersWhoCanStartProcessDeploymentInfo(processDefinitionId, options); // Then assertNotNull(result); } @Test(expected = SBonitaReadException.class) public void searchUsersWhoCanStartProcessDeploymentInfoThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long processDefinitionId = 9; when(persistenceService.searchEntity(SUser.class, "WhoCanStartProcess", options, Collections.singletonMap("processId", (Object) processDefinitionId))) .thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.searchUsersWhoCanStartProcessDeploymentInfo(processDefinitionId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfUsersWhoCanStartProcessDeploymentInfo(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getNumberOfUsersWhoCanStartProcessDeploymentInfo() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long processDefinitionId = 9; when(persistenceService.getNumberOfEntities(SUser.class, "WhoCanStartProcess", options, Collections.singletonMap("processId", (Object) processDefinitionId))).thenReturn(1L); // When final long result = processDefinitionServiceImpl .getNumberOfUsersWhoCanStartProcessDeploymentInfo(processDefinitionId, options); // Then assertEquals(1L, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfUsersWhoCanStartProcessDeploymentInfoThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long processDefinitionId = 9; when(persistenceService.getNumberOfEntities(SUser.class, "WhoCanStartProcess", options, Collections.singletonMap("processId", (Object) processDefinitionId))) .thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.getNumberOfUsersWhoCanStartProcessDeploymentInfo(processDefinitionId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasksFor", options, Collections.singletonMap("userId", (Object) userId))) .thenReturn(new ArrayList()); // When final List result = processDefinitionServiceImpl .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor( userId, options); // Then assertNotNull(result); } @Test(expected = SBonitaReadException.class) public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksForThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasksFor", options, Collections.singletonMap("userId", (Object) userId))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(userId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasksFor", options, Collections.singletonMap("userId", (Object) userId))).thenReturn(1L); // When final long result = processDefinitionServiceImpl .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(userId, options); // Then assertEquals(1L, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksForThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasksFor", options, Collections.singletonMap("userId", (Object) userId))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(userId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasksSupervisedBy", options, Collections.singletonMap("userId", (Object) userId))) .thenReturn(new ArrayList()); // When final List result = processDefinitionServiceImpl .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy( userId, options); // Then assertNotNull(result); } @Test(expected = SBonitaReadException.class) public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedByThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasksSupervisedBy", options, Collections.singletonMap("userId", (Object) userId))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(userId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasksSupervisedBy", options, Collections.singletonMap("userId", (Object) userId))).thenReturn(1L); // When final long result = processDefinitionServiceImpl .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(userId, options); // Then assertEquals(1L, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedByThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); final long userId = 9; when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasksSupervisedBy", options, Collections.singletonMap("userId", (Object) userId))).thenThrow(new SBonitaReadException("")); // When processDefinitionServiceImpl .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(userId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasks", options, null)).thenReturn( new ArrayList()); // When final List result = processDefinitionServiceImpl .searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(options); // Then assertNotNull(result); } @Test(expected = SBonitaReadException.class) public void searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.searchEntity(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasks", options, null)).thenThrow( new SBonitaReadException("")); // When processDefinitionServiceImpl.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.ProcessDefinitionServiceImpl#getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasks", options, null)).thenReturn(1L); // When final long result = processDefinitionServiceImpl .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(options); // Then assertEquals(1L, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksThrowException() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.getNumberOfEntities(SProcessDefinitionDeployInfo.class, "WithAssignedOrPendingHumanTasks", options, null)).thenThrow( new SBonitaReadException("")); // When processDefinitionServiceImpl.getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(options); } @Test(expected = SObjectModificationException.class) public void updateExpressionContentShouldThrowProcessNotFoundIfReadException() throws Exception { final long processDefinitionId = 415L; doThrow(SBonitaReadException.class).when(processDefinitionServiceImpl) .getProcessDeploymentInfo(processDefinitionId); processDefinitionServiceImpl.updateExpressionContent(processDefinitionId, 77L, "string"); } @Test(expected = SObjectModificationException.class) public void updateExpressionContentShouldThrowObjectModificationIfUpdateException() throws Exception { final long processDefinitionId = 415L; final DesignProcessDefinition designProcessDefinition = mock(DesignProcessDefinition.class); doReturn(designProcessDefinition).when(processDefinitionServiceImpl) .getDesignProcessDefinition(processDefinitionId); processDefinitionServiceImpl.updateExpressionContent(processDefinitionId, 77L, "string"); } @Test(expected = SObjectModificationException.class) public void updateExpressionContentShouldThrowObjectModificationIfExpressionNotFound() throws Exception { final long processDefinitionId = 415L; final long expressionDefinitionId = 77L; final DesignProcessDefinition designProcessDefinition = mock(DesignProcessDefinition.class); doReturn(designProcessDefinition).when(processDefinitionServiceImpl) .getDesignProcessDefinition(processDefinitionId); doReturn(null).when(processDefinitionServiceImpl).getExpression(designProcessDefinition, expressionDefinitionId); processDefinitionServiceImpl.updateExpressionContent(processDefinitionId, expressionDefinitionId, "string"); } @Test(expected = SObjectModificationException.class) public void updateExpressionContentShouldThrowObjectModificationUpdateFails() throws Exception { final long processDefinitionId = 415L; final long expressionDefinitionId = 77L; final DesignProcessDefinition designProcessDefinition = mock(DesignProcessDefinition.class); doReturn(designProcessDefinition).when(processDefinitionServiceImpl) .getDesignProcessDefinition(processDefinitionId); doReturn(mock(ExpressionImpl.class)).when(processDefinitionServiceImpl).getExpression(designProcessDefinition, expressionDefinitionId); processDefinitionServiceImpl.updateExpressionContent(processDefinitionId, expressionDefinitionId, "string"); } @Test(expected = SProcessDefinitionNotFoundException.class) public void getDesignProcessDefinition_Should_Throw_Exception_On_Unknown_Process() throws SProcessDefinitionNotFoundException, SBonitaReadException { int processDefinitionId = 456; doThrow(new SProcessDefinitionNotFoundException("impossible to find process")) .when(processDefinitionServiceImpl).getProcessDeploymentInfo( processDefinitionId); processDefinitionServiceImpl.getDesignProcessDefinition(processDefinitionId); } @Test(expected = SBonitaReadException.class) public void getDesignProcessDefinition_Should_Throw_Exception_On_UnparsableContent() throws Exception { int processDefinitionId = 123; SProcessDefinitionDeployInfo processDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class); doReturn(processDefinitionDeployInfo).when(processDefinitionServiceImpl) .getProcessDeploymentInfo(processDefinitionId); SProcessDefinitionDesignContent processDefinitionDesignContent = mock(SProcessDefinitionDesignContent.class); when(processDefinitionDeployInfo.getDesignContent()).thenReturn(processDefinitionDesignContent); when(processDefinitionBARContribution.convertXmlToProcess(null)) .thenThrow(new IOException("impossible to parse content")); processDefinitionServiceImpl.getDesignProcessDefinition(processDefinitionId); } @Test public void getDesignProcessDefinition_Should_return_XML_correctly() throws Exception { int processDefinitionId = 123; SProcessDefinitionDeployInfo processDefinitionDeployInfo = mock(SProcessDefinitionDeployInfo.class); doReturn(processDefinitionDeployInfo).when(processDefinitionServiceImpl) .getProcessDeploymentInfo(processDefinitionId); SProcessDefinitionDesignContent processDefinitionDesignContent = mock(SProcessDefinitionDesignContent.class); when(processDefinitionDeployInfo.getDesignContent()).thenReturn(processDefinitionDesignContent); DesignProcessDefinition designProcessDefinition = mock(DesignProcessDefinition.class); when(processDefinitionBARContribution.convertXmlToProcess(null)).thenReturn(designProcessDefinition); DesignProcessDefinition designProcessDefinitionResult = processDefinitionServiceImpl .getDesignProcessDefinition(processDefinitionId); assertThat(designProcessDefinitionResult).isSameAs(designProcessDefinition); } @Test public void updateShouldWorkForGroovyExpression() throws Exception { final long expressionDefinitionId = 77L; final DesignProcessDefinition designProcessDefinition = mock(DesignProcessDefinition.class); doReturn(designProcessDefinition).when(processDefinitionServiceImpl).getDesignProcessDefinition(PROCESS_ID); final ExpressionImpl expression = mock(ExpressionImpl.class); doReturn(expression).when(processDefinitionServiceImpl).getExpression(designProcessDefinition, expressionDefinitionId); doReturn("someXMLContent").when(processDefinitionServiceImpl).getProcessContent(designProcessDefinition); doReturn(true).when(processDefinitionServiceImpl).isValidExpressionTypeToUpdate(nullable(String.class)); processDefinitionServiceImpl.updateExpressionContent(PROCESS_ID, expressionDefinitionId, "string"); verify(processDefinitionServiceImpl).updateProcessDefinitionDeployInfo(eq(PROCESS_ID), any(EntityUpdateDescriptor.class), eq("Update expression <77>, old content is ")); } @Test(expected = SObjectModificationException.class) public void updateShouldForbidExpressionsOfTypeDifferentFromGroovyAndConstant() throws Exception { final long processDefinitionId = 415L; final long expressionDefinitionId = 77L; final DesignProcessDefinition designProcessDefinition = mock(DesignProcessDefinition.class); doReturn(designProcessDefinition).when(processDefinitionServiceImpl) .getDesignProcessDefinition(processDefinitionId); final ExpressionImpl expression = mock(ExpressionImpl.class); doReturn(ExpressionType.TYPE_VARIABLE.name()).when(expression).getExpressionType(); doReturn(expression).when(processDefinitionServiceImpl).getExpression(designProcessDefinition, expressionDefinitionId); processDefinitionServiceImpl.updateExpressionContent(processDefinitionId, expressionDefinitionId, "string"); } @Test public void isValidExpressionTypeShouldOnlySupportGroovyScriptAndConstant() throws Exception { for (ExpressionType expressionType : ExpressionType.values()) { final boolean isValid = processDefinitionServiceImpl.isValidExpressionTypeToUpdate(expressionType.name()); switch (expressionType) { case TYPE_CONSTANT: case TYPE_READ_ONLY_SCRIPT: assertThat(isValid).as("Expression of type " + expressionType + " should be valid for update") .isTrue(); break; default: assertThat(isValid).as("Expression of type " + expressionType + " should NOT be valid for update") .isFalse(); } } } @Test public void getLatestProcessDefinitionId_should_query_processes_order_by_deploymentDate_DESC() throws Exception { // given: final List processes = Collections . singletonList(new SProcessDefinitionDeployInfo()); doReturn(processes).when(persistenceService) .selectList(ArgumentMatchers.> any()); // when: processDefinitionServiceImpl.getLatestProcessDefinitionId("MySimpleProcess"); // then: final ArgumentCaptor captor = ArgumentCaptor.forClass(SelectListDescriptor.class); verify(persistenceService).selectList(captor.capture()); final OrderByOption orderByOption = captor.getValue().getQueryOptions().getOrderByOptions().get(0); assertThat(orderByOption.getFieldName()).isEqualTo("deploymentDate"); assertThat(orderByOption.getOrderByType()).isEqualTo(OrderByType.DESC); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeBooleanValidationTest.java ================================================ /** * 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.core.process.definition.model; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class STypeBooleanValidationTest { @Test public void boolean_are_valid() throws Exception { boolean validation = SType.BOOLEAN.validate(true); assertThat(validation).isTrue(); } @Test public void Boolean_are_valid() throws Exception { boolean validation = SType.BOOLEAN.validate(Boolean.FALSE); assertThat(validation).isTrue(); } @Test public void null_is_valid() throws Exception { boolean validation = SType.BOOLEAN.validate(null); assertThat(validation).isTrue(); } @Test public void other_type_are_not_valid() throws Exception { boolean integerValidation = SType.BOOLEAN.validate(12); assertThat(integerValidation).isFalse(); boolean stringValidation = SType.BOOLEAN.validate("false"); assertThat(stringValidation).isFalse(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeByteArrayValidationTest.java ================================================ /** * 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.core.process.definition.model; import static org.assertj.core.api.Assertions.assertThat; import java.util.ArrayList; import org.junit.Test; public class STypeByteArrayValidationTest { @Test public void byte_array_is_valid() throws Exception { final boolean validation = SType.BYTE_ARRAY.validate(new byte[] { 0, 1, 0, 0, 1, 0, 1 }); assertThat(validation).isTrue(); } @Test public void Byte_array_is_invalid() throws Exception { final boolean validation = SType.BYTE_ARRAY.validate(new Byte[0]); assertThat(validation).isFalse(); } @Test public void Byte_list_is_invalid() throws Exception { final boolean validation = SType.BYTE_ARRAY.validate(new ArrayList()); assertThat(validation).isFalse(); } @Test public void null_is_valid() throws Exception { final boolean validation = SType.BYTE_ARRAY.validate(null); assertThat(validation).isTrue(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeDateValidationTest.java ================================================ /** * 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.core.process.definition.model; import static org.assertj.core.api.Assertions.assertThat; import java.util.Date; import org.junit.Test; public class STypeDateValidationTest { @Test public void date_are_valid() throws Exception { final boolean validation = SType.DATE.validate(new Date()); assertThat(validation).isTrue(); } @Test public void null_is_valid() throws Exception { final boolean validation = SType.DATE.validate(null); assertThat(validation).isTrue(); } @Test public void other_type_are_not_valid() throws Exception { final boolean stringValidation = SType.DATE.validate("2014/08/09"); assertThat(stringValidation).isFalse(); final boolean longValidation = SType.DATE.validate(1410862853708L); assertThat(longValidation).isFalse(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeDecimalValidationTest.java ================================================ /** * 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.core.process.definition.model; import static org.assertj.core.api.Assertions.assertThat; import java.math.BigDecimal; import org.junit.Test; public class STypeDecimalValidationTest { @Test public void float_are_valid() throws Exception { boolean validation = SType.DECIMAL.validate(45.2f); assertThat(validation).isTrue(); } @Test public void Float_are_valid() throws Exception { boolean validation = SType.DECIMAL.validate(Float.valueOf(47.65f)); assertThat(validation).isTrue(); } @Test public void double_are_valid() throws Exception { boolean validation = SType.DECIMAL.validate(5684.23d); assertThat(validation).isTrue(); } @Test public void Double_are_valid() throws Exception { boolean validation = SType.DECIMAL.validate(Double.valueOf(6548.236d)); assertThat(validation).isTrue(); } @Test public void null_is_valid() throws Exception { boolean validation = SType.DECIMAL.validate(null); assertThat(validation).isTrue(); } @Test public void BigDecimal_are_valid() throws Exception { boolean validation = SType.DECIMAL.validate(BigDecimal.valueOf(1235.321d)); assertThat(validation).isTrue(); } @Test public void Integer_are_valid() throws Exception { boolean intValidation = SType.DECIMAL.validate(53); assertThat(intValidation).isTrue(); } @Test public void other_types_are_not_valid() throws Exception { boolean stringValidation = SType.DECIMAL.validate("54"); assertThat(stringValidation).isFalse(); boolean booleanValidation = SType.DECIMAL.validate(true); assertThat(booleanValidation).isFalse(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeIntegerValidationTest.java ================================================ /** * 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.core.process.definition.model; import static org.assertj.core.api.Assertions.assertThat; import java.math.BigInteger; import org.junit.Test; public class STypeIntegerValidationTest { @Test public void integer_are_valid() throws Exception { boolean validation = SType.INTEGER.validate(12); assertThat(validation).isTrue(); } @Test public void Integer_are_valid() throws Exception { boolean validation = SType.INTEGER.validate(Integer.valueOf(12)); assertThat(validation).isTrue(); } @Test public void long_are_valid() throws Exception { boolean validation = SType.INTEGER.validate(12l); assertThat(validation).isTrue(); } @Test public void Long_are_valid() throws Exception { boolean validation = SType.INTEGER.validate(Long.valueOf(12)); assertThat(validation).isTrue(); } @Test public void BigInteger_are_valid() throws Exception { boolean validation = SType.INTEGER.validate(BigInteger.valueOf(45)); assertThat(validation).isTrue(); } @Test public void short_are_valid() throws Exception { boolean validation = SType.INTEGER.validate((short) 65); assertThat(validation).isTrue(); } @Test public void Short_are_valid() throws Exception { boolean validation = SType.INTEGER.validate(Short.valueOf((short) 87)); assertThat(validation).isTrue(); } @Test public void byte_are_valid() throws Exception { boolean validation = SType.INTEGER.validate((byte) 8); assertThat(validation).isTrue(); } @Test public void Byte_are_valid() throws Exception { boolean validation = SType.INTEGER.validate(Byte.valueOf((byte) 2)); assertThat(validation).isTrue(); } @Test public void null_is_valid() throws Exception { boolean validation = SType.INTEGER.validate(null); assertThat(validation).isTrue(); } @Test public void other_types_are_not_valid() throws Exception { boolean stringValidation = SType.INTEGER.validate("54"); assertThat(stringValidation).isFalse(); boolean doubleValidation = SType.INTEGER.validate(53.2d); assertThat(doubleValidation).isFalse(); boolean booleanValidation = SType.INTEGER.validate(true); assertThat(booleanValidation).isFalse(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeLocalDateTest.java ================================================ /** * 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.core.process.definition.model; import static org.assertj.core.api.Assertions.assertThat; import java.time.LocalDate; import org.junit.Test; /** * @author Danila Mazour */ public class STypeLocalDateTest { @Test public void localDate_are_valid() throws Exception { boolean validation = SType.LOCALDATE.validate(LocalDate.now()); assertThat(validation).isTrue(); } @Test public void null_is_valid() throws Exception { boolean validation = SType.LOCALDATE.validate(null); assertThat(validation).isTrue(); } @Test public void other_type_are_not_valid() throws Exception { boolean integerValidation = SType.LOCALDATE.validate(12); assertThat(integerValidation).isFalse(); boolean stringValidation = SType.LOCALDATE.validate("false"); assertThat(stringValidation).isFalse(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeLocalDateTimeTest.java ================================================ /** * 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.core.process.definition.model; import static org.assertj.core.api.Assertions.assertThat; import java.time.LocalDateTime; import org.junit.Test; /** * @author Danila Mazour */ public class STypeLocalDateTimeTest { @Test public void localDateTime_are_valid() throws Exception { boolean validation = SType.LOCALDATETIME.validate(LocalDateTime.now()); assertThat(validation).isTrue(); } @Test public void null_is_valid() throws Exception { boolean validation = SType.LOCALDATETIME.validate(null); assertThat(validation).isTrue(); } @Test public void other_type_are_not_valid() throws Exception { boolean integerValidation = SType.LOCALDATETIME.validate(12); assertThat(integerValidation).isFalse(); boolean stringValidation = SType.LOCALDATETIME.validate("false"); assertThat(stringValidation).isFalse(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeOffsetDateTimeTest.java ================================================ /** * 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.core.process.definition.model; import static org.assertj.core.api.Assertions.assertThat; import java.time.OffsetDateTime; import org.junit.Test; /** * @author Danila Mazour */ public class STypeOffsetDateTimeTest { @Test public void offsetDateTime_are_valid() throws Exception { boolean validation = SType.OFFSETDATETIME.validate(OffsetDateTime.now()); assertThat(validation).isTrue(); } @Test public void null_is_valid() throws Exception { boolean validation = SType.OFFSETDATETIME.validate(null); assertThat(validation).isTrue(); } @Test public void other_type_are_not_valid() throws Exception { boolean integerValidation = SType.OFFSETDATETIME.validate(12); assertThat(integerValidation).isFalse(); boolean stringValidation = SType.OFFSETDATETIME.validate("false"); assertThat(stringValidation).isFalse(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/STypeTextValidationTest.java ================================================ /** * 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.core.process.definition.model; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class STypeTextValidationTest { @Test public void string_are_valid() throws Exception { boolean validation = SType.TEXT.validate("this is a String"); assertThat(validation).isTrue(); } @Test public void character_are_valid() throws Exception { boolean validation = SType.TEXT.validate('a'); assertThat(validation).isTrue(); } @Test public void null_is_valid() throws Exception { boolean validation = SType.TEXT.validate(null); assertThat(validation).isTrue(); } @Test public void other_types_are_not_valid() throws Exception { boolean intValidation = SType.TEXT.validate(54); assertThat(intValidation).isFalse(); boolean doubleValidation = SType.TEXT.validate(53.2d); assertThat(doubleValidation).isFalse(); boolean booleanValidation = SType.TEXT.validate(true); assertThat(booleanValidation).isFalse(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/builder/ServerModelConvertorTest.java ================================================ /** * 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.core.process.definition.model.builder; import static org.assertj.core.api.Assertions.assertThat; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.impl.ExpressionImpl; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.impl.SExpressionImpl; import org.junit.Test; public class ServerModelConvertorTest { Expression buildClientExpression(final String name, final String content, final String expressionType, final String returnType, final List dependencies) { final ExpressionImpl expression = new ExpressionImpl(); expression.setName(name); expression.setContent(content); expression.setExpressionType(expressionType); expression.setReturnType(returnType); return expression; } @Test public void convert_a_business_query_to_a_server_one() { final String name = "expressionName"; final String content = "getEmployees"; final String expressionType = ExpressionType.TYPE_QUERY_BUSINESS_DATA.name(); final String returnType = "org.bonitasoft.Employee"; final Expression clientExpression = buildClientExpression(name, content, expressionType, returnType, null); final SExpressionImpl expected = new SExpressionImpl(name, content, expressionType, returnType, null, Collections. emptyList()); final SExpression expression = ServerModelConvertor.convertExpression(clientExpression); assertThat(expression).isEqualTo(expected); } @Test public void return_null_when_converting_a_null_business_data() { final SBusinessDataDefinition businessDataDefinition = ServerModelConvertor.convertBusinessDataDefinition(null); assertThat(businessDataDefinition).isNull(); } @Test public void shouldConvertContractInputs() throws Exception { final HashMap contractInputs = new HashMap<>(); final ExpressionImpl expression1 = new ExpressionImpl(); expression1.setReturnType("SomeType"); final ExpressionImpl expression2 = new ExpressionImpl(); expression2.setReturnType("SomeReturnType"); contractInputs.put("inputname1", expression1); contractInputs.put("inputname2", expression2); final Map convertedContractInputs = ServerModelConvertor .convertContractInputs(contractInputs); assertThat(convertedContractInputs.get("inputname1")) .isEqualTo(ServerModelConvertor.convertExpression(expression1)); assertThat(convertedContractInputs.get("inputname2")) .isEqualTo(ServerModelConvertor.convertExpression(expression2)); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/builder/impl/SBusinessDataDefinitionBuilderFactoryImplTest.java ================================================ /** * 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.core.process.definition.model.builder.impl; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.process.definition.model.builder.SBusinessDataDefinitionBuilderFactory; import org.junit.Test; public class SBusinessDataDefinitionBuilderFactoryImplTest { @Test public void getSBusinessDataDefinitionBuilderFactoryInterfaceShouldReturnsSBusinessDataDefinitionBuilderFactoryImpl() { // when: final SBusinessDataDefinitionBuilderFactory factory = BuilderFactory .get(SBusinessDataDefinitionBuilderFactory.class); // then: assertThat(factory).isInstanceOf(SBusinessDataDefinitionBuilderFactoryImpl.class); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SBoundaryEventDefinitionImplTest.java ================================================ /** * 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.core.process.definition.model.event.impl; import static org.junit.Assert.assertTrue; import org.junit.Test; /** * @author Celine Souchet */ public class SBoundaryEventDefinitionImplTest { /** * Test method for * {@link org.bonitasoft.engine.core.process.definition.model.impl.SFlowNodeDefinitionImpl#isBoundaryEvent()}. */ @Test public void boundary_if_is_a_boundary() { final SBoundaryEventDefinitionImpl sBoundaryEventDefinitionImpl = new SBoundaryEventDefinitionImpl(6, "name"); assertTrue(sBoundaryEventDefinitionImpl.isBoundaryEvent()); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/event/impl/SStartEventDefinitionImplTest.java ================================================ /** * 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.core.process.definition.model.event.impl; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SCatchErrorEventTriggerDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.impl.STransitionDefinitionImpl; import org.junit.Test; /** * Created by Vincent Elcrin * Date: 18/12/13 * Time: 14:50 * * @author Celine Souchet */ public class SStartEventDefinitionImplTest { SStartEventDefinitionImpl startEvent = new SStartEventDefinitionImpl(1L, "name"); @Test public void isStartable_return_false_if_start_event_has_trigger_events() { startEvent.addEventTriggerDefinition(new SCatchErrorEventTriggerDefinitionImpl("error")); assertFalse(startEvent.isStartable()); } @Test public void isStartable_return_false_if_start_event_has_incoming_transitions() { startEvent.addIncomingTransition(new STransitionDefinitionImpl("incoming")); assertFalse(startEvent.isStartable()); } @Test public void isStartable_return_true_if_start_event_has_no_incoming_transitions_and_no_trigger_events() { assertTrue(startEvent.isStartable()); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/SCatchEventDefinitionImplTest.java ================================================ /** * 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.core.process.definition.model.impl; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.event.impl.SCatchEventDefinitionImpl; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SCatchEventDefinitionImplTest { private SCatchEventDefinitionImpl catchEvent; @Before public void before() { catchEvent = new SCatchEventDefinitionImpl(9, "name") { private static final long serialVersionUID = 8249595229324418282L; @Override public SFlowNodeType getType() { return SFlowNodeType.AUTOMATIC_TASK; } }; } @Test public void is_interrupting_if_interrupting_catch_event() { catchEvent.setInterrupting(true); assertTrue(catchEvent.isInterrupting()); } @Test public void is_not_interrupting_if_non_interrupting_catch_event() { catchEvent.setInterrupting(false); assertFalse(catchEvent.isInterrupting()); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/SConstraintDefinitionImplTest.java ================================================ /** * 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.core.process.definition.model.impl; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class SConstraintDefinitionImplTest { @Test public void should_add_input_name() throws Exception { //given final SConstraintDefinitionImpl sConstraintDefinition = new SConstraintDefinitionImpl("name", "expression", "explanation"); //when sConstraintDefinition.addInputName("inputName"); //then assertThat(sConstraintDefinition.getInputNames()).isNotNull().hasSize(1).containsExactly("inputName"); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/SFlowNodeDefinitionImplTest.java ================================================ /** * 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.core.process.definition.model.impl; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.junit.Before; import org.junit.Test; /** * @author Vincent Elcrin * @author Celine Souchet */ public class SFlowNodeDefinitionImplTest { private SFlowNodeDefinitionImpl flowNode; @Before public void before() { flowNode = new SFlowNodeDefinitionImpl(1L, "name") { private static final long serialVersionUID = -1297746953646018494L; @Override public SFlowNodeType getType() { return null; } }; } @Test public void isStartable_return_false_if_flow_node_has_incoming_transitions() { flowNode.addIncomingTransition(new STransitionDefinitionImpl("incoming")); assertFalse(flowNode.isStartable()); } @Test public void isStartable_return_true_if_flow_node_has_no_incoming_transitions() { assertTrue(flowNode.isStartable()); } @Test public void is_not_interrupting_if_not_catch_event() { assertFalse(flowNode.isInterrupting()); } @Test public void hasIncommingTransitions_return_true_if_flownode_hasIncommingTransitions() { flowNode.addIncomingTransition(new STransitionDefinitionImpl("incoming")); assertTrue(flowNode.hasIncomingTransitions()); } @Test public void hasIncommingTransitions_return_false_if_flownode_doesnt_have_IncommingTransitions() { assertFalse(flowNode.hasIncomingTransitions()); } @Test public void isEventSubProcess_return_false_if_is_not_sub_process() { assertFalse(flowNode.isEventSubProcess()); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/SGatewayDefinitionImplTest.java ================================================ /** * 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.core.process.definition.model.impl; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.bonitasoft.engine.core.process.definition.model.SGatewayType; import org.junit.Test; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SGatewayDefinitionImplTest { @Test public void not_exclusive_if_parallel_gateway() { final SGatewayDefinitionImpl gateway = new SGatewayDefinitionImpl(6, "name", SGatewayType.PARALLEL); assertFalse(gateway.isExclusive()); } @Test public void not_exclusive_if_inclusive_gateway() { final SGatewayDefinitionImpl gateway = new SGatewayDefinitionImpl(6, "name", SGatewayType.INCLUSIVE); assertFalse(gateway.isExclusive()); } @Test public void exclusive_if_exclusive_gateway() { final SGatewayDefinitionImpl gateway = new SGatewayDefinitionImpl(6, "name", SGatewayType.EXCLUSIVE); assertTrue(gateway.isExclusive()); } @Test public void parallelOrInclusive_if_parallel_gateway() { final SGatewayDefinitionImpl gateway = new SGatewayDefinitionImpl(6, "name", SGatewayType.PARALLEL); assertTrue(gateway.isParalleleOrInclusive()); } @Test public void parallelOrInclusive_if_inclusive_gateway() { final SGatewayDefinitionImpl gateway = new SGatewayDefinitionImpl(6, "name", SGatewayType.PARALLEL); assertTrue(gateway.isParalleleOrInclusive()); } @Test public void not_parallelOrInclusive_if_exclusive_gateway() { final SGatewayDefinitionImpl gateway = new SGatewayDefinitionImpl(6, "name", SGatewayType.EXCLUSIVE); assertFalse(gateway.isParalleleOrInclusive()); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/SInputDefinitionImplTest.java ================================================ /** * 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.core.process.definition.model.impl; import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; import org.bonitasoft.engine.bpm.contract.InputDefinition; import org.bonitasoft.engine.bpm.contract.Type; import org.bonitasoft.engine.bpm.contract.impl.InputDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.SInputDefinition; import org.bonitasoft.engine.core.process.definition.model.SType; import org.junit.Test; public class SInputDefinitionImplTest { private static final String DESCRIPTION = "description"; private static final String NAME = "name"; @Test public void contructor_with_name() throws Exception { //given final SInputDefinitionImpl sInputDefinitionImpl = new SInputDefinitionImpl(NAME, SType.TEXT, DESCRIPTION); //then assertThat(sInputDefinitionImpl.isMultiple()).isFalse(); } @Test public void constructor_with_input_definition() throws Exception { //given final InputDefinition simpleInput = new InputDefinitionImpl(NAME, Type.TEXT, DESCRIPTION, true); //when final SInputDefinitionImpl sInputDefinitionImpl = new SInputDefinitionImpl(simpleInput); //then assertThat(sInputDefinitionImpl.isMultiple()).isTrue(); assertThat(sInputDefinitionImpl.getName()).isEqualTo(NAME); assertThat(sInputDefinitionImpl.getDescription()).isEqualTo(DESCRIPTION); assertThat(sInputDefinitionImpl.getType()).isEqualTo(SType.TEXT); } @Test public void construtor_should_initialize_members() throws Exception { //when final SInputDefinitionImpl sComplexInputDefinitionImpl = new SInputDefinitionImpl(NAME, ""); //then assertThat(sComplexInputDefinitionImpl.isMultiple()).isFalse(); assertThat(sComplexInputDefinitionImpl.getName()).isEqualTo(NAME); assertThat(sComplexInputDefinitionImpl.getInputDefinitions()).isNotNull().isEmpty(); } @Test public void construtor_with_input_definition() throws Exception { //given final SInputDefinition name = new SInputDefinitionImpl("name", SType.TEXT, DESCRIPTION); final SInputDefinition city = new SInputDefinitionImpl("city", SType.TEXT, DESCRIPTION); final SInputDefinition zip = new SInputDefinitionImpl("zip", SType.INTEGER, DESCRIPTION); final SInputDefinition adress = new SInputDefinitionImpl("adress", DESCRIPTION, false, Arrays.asList(city, zip)); //when final SInputDefinitionImpl sComplexInputDefinitionImpl = new SInputDefinitionImpl(NAME, DESCRIPTION, false, Arrays.asList(name, adress)); //then assertThat(sComplexInputDefinitionImpl.isMultiple()).isFalse(); assertThat(sComplexInputDefinitionImpl.getName()).isEqualTo(NAME); assertThat(sComplexInputDefinitionImpl.getInputDefinitions()).isNotEmpty().containsExactly(name, adress); } @Test public void construtor_with_inputDefinition() throws Exception { //when final SInputDefinitionImpl sComplexInputDefinitionImpl = new SInputDefinitionImpl(createComplexInputs()); //then assertThat(sComplexInputDefinitionImpl.isMultiple()).isTrue(); assertThat(sComplexInputDefinitionImpl.getName()).isEqualTo("expense"); assertThat(sComplexInputDefinitionImpl.getDescription()).isEqualTo(DESCRIPTION); assertThat(sComplexInputDefinitionImpl.getInputDefinitions()).as("should contain name, amount and date") .isNotEmpty().hasSize(4); } private InputDefinition createComplexInputs() { final InputDefinition name = new InputDefinitionImpl("name", Type.TEXT, DESCRIPTION); final InputDefinition amount = new InputDefinitionImpl("amount", Type.DECIMAL, DESCRIPTION); final InputDefinition date = new InputDefinitionImpl("date", Type.DATE, DESCRIPTION); final InputDefinition city = new InputDefinitionImpl("city", Type.TEXT, DESCRIPTION); final InputDefinition zip = new InputDefinitionImpl("zip", Type.INTEGER, DESCRIPTION); final InputDefinition adress = new InputDefinitionImpl("adress", DESCRIPTION, Arrays.asList(city, zip)); final InputDefinition expense = new InputDefinitionImpl("expense", DESCRIPTION, true, Arrays.asList(name, amount, date, adress)); return expense; } @Test public void constructor_without_multiple() throws Exception { //given final SInputDefinitionImpl sInputDefinitionImpl = new SInputDefinitionImpl(NAME, DESCRIPTION); //then assertThat(sInputDefinitionImpl.isMultiple()).as("should not be multiple").isFalse(); } @Test public void constructor_with_description_without_multiple() throws Exception { //given final SInputDefinitionImpl sInputDefinitionImpl = new SInputDefinitionImpl(NAME, DESCRIPTION); //then assertThat(sInputDefinitionImpl.isMultiple()).as("should not be multiple").isFalse(); } @Test public void constructor_with_multiple() throws Exception { //given final SInputDefinitionImpl sInputDefinitionImpl = new SInputDefinitionImpl(NAME, SType.BOOLEAN, DESCRIPTION, true); //then assertThat(sInputDefinitionImpl.isMultiple()).as("should be multiple").isTrue(); } @Test public void constructor_with_name_and_description() throws Exception { //given final SInputDefinitionImpl sInputDefinitionImpl = new SInputDefinitionImpl(NAME, DESCRIPTION); //then assertThat(sInputDefinitionImpl.getName()).as("should get name").isEqualTo(NAME); assertThat(sInputDefinitionImpl.getDescription()).as("should get name").isEqualTo(DESCRIPTION); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/SSubProcessDefinitionImplTest.java ================================================ /** * 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.core.process.definition.model.impl; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Test; /** * @author Vincent Elcrin */ public class SSubProcessDefinitionImplTest { SSubProcessDefinitionImpl subProcessTriggeredByEvent = new SSubProcessDefinitionImpl(1L, "name", true); SSubProcessDefinitionImpl subProcess = new SSubProcessDefinitionImpl(1L, "name", false); @Test public void isStartable_return_false_if_sub_process_is_triggered_by_event() { assertFalse(subProcessTriggeredByEvent.isStartable()); } @Test public void isStartable_return_false_if_sub_process_has_incoming_transitions() { subProcess.addIncomingTransition(new STransitionDefinitionImpl("incoming")); assertFalse(subProcess.isStartable()); } @Test public void isStartable_return_true_if_sub_process_has_no_incoming_transitions_and_is_not_triggered_by_event() { assertTrue(subProcess.isStartable()); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/STransitionDefinitionImplTest.java ================================================ /** * 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.core.process.definition.model.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import org.bonitasoft.engine.bpm.flownode.impl.internal.TransitionDefinitionImpl; import org.bonitasoft.engine.expression.model.SExpression; import org.junit.Test; public class STransitionDefinitionImplTest { @Test public void hasCondition_should_return_true_when_condition_is_not_null() throws Exception { //given STransitionDefinitionImpl transition = new STransitionDefinitionImpl("t"); transition.setCondition(mock(SExpression.class)); //when boolean hasCondition = transition.hasCondition(); //then assertThat(hasCondition).isTrue(); } @Test public void hasCondition_should_return_false_when_condition_is_null() throws Exception { //given STransitionDefinitionImpl transition = new STransitionDefinitionImpl("t"); //when boolean hasCondition = transition.hasCondition(); //then assertThat(hasCondition).isFalse(); } @Test public void constructor_should_correctly_set_the_id() { //given STransitionDefinitionImpl transition = new STransitionDefinitionImpl(new TransitionDefinitionImpl()); //when long id = transition.getId(); //then assertThat(id).isNotNull(); } } ================================================ FILE: bpm/bonita-core/bonita-process-definition/src/test/java/org/bonitasoft/engine/core/process/definition/model/impl/SUserTaskDefinitionImplTest.java ================================================ /** * 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.core.process.definition.model.impl; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.core.process.definition.model.SType; import org.junit.Test; /** * @author Celine Souchet */ public class SUserTaskDefinitionImplTest { @Test public void not_exclusive_if_not_gateway() { final SUserTaskDefinitionImpl userTask = new SUserTaskDefinitionImpl(5, "name", "actorName"); assertThat(userTask.isExclusive()).isFalse(); } @Test public void not_parallelOrInclusive_if_not_gateway() { final SUserTaskDefinitionImpl userTask = new SUserTaskDefinitionImpl(5, "name", "actorName"); assertThat(userTask.isParalleleOrInclusive()).isFalse(); } @Test public void aUserTaskWithANullContractReturnsAnEmptyOne() throws Exception { final SUserTaskDefinitionImpl userTask = new SUserTaskDefinitionImpl(5, "name", "actorName"); assertThat(userTask.getContract()).isNotNull().isEqualTo(new SContractDefinitionImpl()); } @Test public void aUserTaskWithAContractReturnsThatOne() throws Exception { final SContractDefinitionImpl contract = new SContractDefinitionImpl(); contract.addInput(new SInputDefinitionImpl("valid", SType.TEXT, "descripti")); final SUserTaskDefinitionImpl userTask = new SUserTaskDefinitionImpl(5, "name", "actorName"); userTask.setContract(contract); assertThat(userTask.getContract()).isNotNull().isEqualTo(contract); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/FlowStatesMapping.txt ================================================ WARNING: If some states change, it requires migration PROCESS 0 Initializing 1 Started 2 Suspended 3 Cancelled 4 Aborted 5 Completing 6 Completed 7 Failed / Error 11 Aborting ACTIVITY (some constants are defined in the State interface) 0 InitializingActivityState 1 ExecutingFlowNodeState 2 CompletedActivityState 3 FailedActivityState 4 ReadyActivityState 9 CompletingActivityState 10 WaitingFlowNodeState 12 SkippedFlowNodeState 13 AbortingSubTaskState 14 CancelledFlowNodeState 15 AbortingFlowNodeContainerState 16 AbortedFlowNodeState 17 CancellingFlowNodeContainerChildrenState 18 CancellingFlowNodeState 19 CancellingCallActivityState 20 AbortingCallActivityState 21 AbortingFlowNodeState 22 CancellingBoundaryAndIntermediateCatchEventState 23 InitializingLoopActivityState 24 ExecutingLoopActivityState 25 ExecutingActivityState 26 ExecutingThrowEventState 27 InitializingMultiInstanceActivityState 28 ExecutingMultiInstanceActivityState 30 CompletingCallActivityState 31 ExecutingCallActivityState 32 InitializingActivityWithBoundaryEventsState 33 InitializingBoundaryEventState 34 AbortingBoundaryEventsOnCompletingActivityState 35 AbortingActivityWithBoundaryState 36 CancellingActivityWithBoundaryState 37 ExecutingAutomaticActivityState 38 AbortingReceiveTaskState 39 CancellingReceiveTaskState 60 AbortingBoundaryAndIntermediateCatchEventState 61 InitializingAndExecutingFlowNodeState 65 ExecutingBoundaryEventState // up-to-date on 2020-09-15 ================================================ FILE: bpm/bonita-core/bonita-process-engine/build.gradle ================================================ plugins { id 'groovy' } dependencies { api platform(libs.bonitaArtifactsModelBom) api libs.semver4j api "org.bonitasoft.engine:bonita-organization-model" api project(':bpm:bonita-core:bonita-home-server') api project(':bpm:bonita-core:bonita-actor-mapping') api project(':bpm:bonita-core:bonita-category') api project(':bpm:bonita-core:bonita-process-instance') api project(':bpm:bonita-core:bonita-contract-data') api project(':bpm:bonita-core:bonita-user-filter') api project(':bpm:bonita-core:bonita-login') api project(':bpm:bonita-core:bonita-process-definition') api project(':bpm:bonita-core:bonita-process-comment') api project(':bpm:bonita-core:bonita-platform-login') api project(':bpm:bonita-core:bonita-core-data') api project(':bpm:bonita-core:bonita-supervisor-mapping') api project(':bpm:bonita-synchro-repository:bonita-synchro-service') api project(':services:bonita-builder') api project(':services:bonita-commons') api project(':services:bonita-archive') api project(':services:bonita-authentication') api project(':services:bonita-authorization') api project(':services:bonita-cache') api project(':services:bonita-classloader') api project(':services:bonita-command') api project(':services:bonita-connector-executor') api project(':services:bonita-data-definition') api project(':services:bonita-data-instance') api project(':services:bonita-events') api project(':services:bonita-expression') api project(':services:bonita-identity') api project(':services:bonita-incident') api project(':services:bonita-lock') api project(':services:bonita-log') api project(':services:bonita-page') api project(':bpm:bonita-core:bonita-parameter') api project(':services:bonita-persistence') api project(':services:bonita-platform') api project(':services:bonita-platform-authentication') api project(':services:bonita-platform-command') api project(':services:bonita-platform-session') api project(':services:bonita-profile') api project(':services:bonita-scheduler') api project(':services:bonita-session') api project(':services:bonita-authorization') api project(':services:bonita-time-tracker') api project(':services:bonita-transaction') api project(':services:bonita-work') api project(':services:bonita-business-application') api project(':bpm:bonita-core:bonita-form-mapping') api project(':services:bonita-business-data:bonita-business-data-api') api project(':services:bonita-resources') api project(':services:bonita-temporary-content') api "com.fasterxml.jackson.core:jackson-databind" api project(':platform:platform-resources') api libs.commonsIO api libs.springContext api libs.springSessionCore api libs.springWeb api libs.bundles.groovy api libs.micrometerCore // Dependency on javax.annotations as it is not provided anymore in Java 11: api(libs.javaxAnnotations) annotationProcessor libs.lombok compileOnly libs.lombok testImplementation libs.junit5params testAnnotationProcessor libs.lombok testImplementation libs.assertj testImplementation libs.systemRules // works with Junit4 testImplementation libs.systemLambda // works with Junit5 testImplementation libs.springTest compileOnly project(':bpm:bonita-common') testImplementation testFixtures(project(':bpm:bonita-common')) testImplementation libs.awaitility testRuntimeOnly libs.logback } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ActorMemberPermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import com.fasterxml.jackson.databind.ObjectMapper import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.exception.NotFoundException import org.bonitasoft.engine.session.APISession /** * * Let a user add an actorMember only if he is process supervisor * *
    *
  • bpm/actorMember
  • *
  • bpm/delegation
  • *
* * * * @author Baptiste Mesta */ class ActorMemberPermissionRule implements PermissionRule { public static final String ACTOR_ID = "actor_id" @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() if (apiCallContext.isPOST()) { return checkPostMethod(apiCallContext, apiAccessor, currentUserId) } else if (apiCallContext.isGET()) { return checkGetMethod(apiCallContext, apiAccessor, currentUserId) } else if (apiCallContext.isDELETE()) { //TODO unable to find an actor member with the API! return false } //it's ok to read return true } private boolean checkPostMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId) { ObjectMapper mapper = new ObjectMapper() def list = mapper.readValue(apiCallContext.getBody(), List.class) for (int i = 0; i < list.size(); i++) { def object = list.get(i) def get = object.get(ACTOR_ID) if(get == null){ continue } def actorId = Long.valueOf(get.toString()) if (actorId <= 0) { continue } def processAPI = apiAccessor.getProcessAPI() try { def actor = processAPI.getActor(actorId) def processDefinitionId = actor.getProcessDefinitionId() if (!processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)) { return false } } catch (NotFoundException e) { return true } } return true } private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId) { try { def filters = apiCallContext.getFilters() if (filters.containsKey(ACTOR_ID)) { def processAPI = apiAccessor.getProcessAPI() def actor = processAPI.getActor(Long.parseLong(filters.get(ACTOR_ID))) def processDefinitionId = actor.getProcessDefinitionId() return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId) } return true } catch (NotFoundException e) { return true } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ActorPermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.bpm.actor.ActorNotFoundException import org.bonitasoft.engine.session.APISession /** * * Let a user manage actors only if he is process supervisor * *
    *
  • bpm/actor
  • *
* * * * @author Anthony Birembaut */ class ActorPermissionRule implements PermissionRule { public static final String PROCESS_ID = "process_id" @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() if (apiCallContext.isGET()) { return checkGetMethod(apiCallContext, apiAccessor, currentUserId) } else if (apiCallContext.isPUT()) { return checkPutMethod(apiCallContext, apiAccessor, currentUserId) } return true } private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId) { def resourceId = apiCallContext.getResourceId() if (resourceId == null || resourceId.isEmpty()) { def filters = apiCallContext.getFilters() if(filters.containsKey(PROCESS_ID)){ def processAPI = apiAccessor.getProcessAPI() return processAPI.isUserProcessSupervisor(Long.valueOf(filters.get(PROCESS_ID)),currentUserId) } return true } else { try { return isProcessOwnerOfTheProcess(apiAccessor, resourceId, currentUserId) } catch (ActorNotFoundException e) { logger.debug("actor does not exists") } return true } } private boolean checkPutMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId) { def resourceId = apiCallContext.getResourceId() try { return isProcessOwnerOfTheProcess(apiAccessor, resourceId, currentUserId) } catch (ActorNotFoundException e) { logger.debug("actor does not exists") return true } } private isProcessOwnerOfTheProcess(APIAccessor apiAccessor, String actorId, long currentUserId) throws ActorNotFoundException { def processAPI = apiAccessor.getProcessAPI() def actor = processAPI.getActor(Long.valueOf(actorId)) return processAPI.isUserProcessSupervisor(actor.getProcessDefinitionId(), currentUserId) } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ApplicationMenuPermissionRule.groovy ================================================ /** * Copyright (C) 2021 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.permissions import org.bonitasoft.engine.api.ApplicationAPI import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.exception.NotFoundException import org.bonitasoft.engine.session.APISession /** * * Secure profile related resources * * can be added to *
    *
  • GET|living/application-menu
  • *
* @author Anthony Birembaut */ class ApplicationMenuPermissionRule extends ApplicationPermissionCommon implements PermissionRule { @Override boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { try { if (apiCallContext.isGET()) { def applicationAPI = apiAccessor.getLivingApplicationAPI() def applicationId = getApplicationId(apiCallContext, applicationAPI) if (applicationId != -1l) { return isAppAllowed(applicationAPI.getApplication(applicationId),apiAccessor,apiSession) } } return false } catch (NotFoundException e) { logger.debug("application menu not found: is allowed") //let the API handle the 404 return true } } private long getApplicationId(APICallContext apiCallContext, ApplicationAPI applicationAPI) { def applicationId = -1l if (apiCallContext.getResourceId() != null) { def menuId = Long.valueOf(apiCallContext.getResourceId()) def applicationMenu = applicationAPI.getApplicationMenu(menuId) applicationId = applicationMenu.getApplicationId() } else { def filters = apiCallContext.getFilters() if (filters.containsKey("applicationId")){ applicationId = Long.valueOf(filters.get("applicationId")) } } return applicationId } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ApplicationPermissionCommon.groovy ================================================ package org.bonitasoft.permissions import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.business.application.Application import org.bonitasoft.engine.business.application.ApplicationVisibility import org.bonitasoft.engine.profile.ProfileCriterion import org.bonitasoft.engine.session.APISession class ApplicationPermissionCommon { boolean isAppAllowed(Application application, APIAccessor apiAccessor, APISession apiSession) { def profileId = application.getProfileId() def profileAPI = apiAccessor.getProfileAPI() def applicationVisibility = application.getVisibility() if (applicationVisibility != null) { if (applicationVisibility == ApplicationVisibility.ALL) { return true } else if (applicationVisibility == ApplicationVisibility.TECHNICAL_USER) { return apiSession.isTechnicalUser() } } def index = 0 def profile def list = [] while ((list = profileAPI.getProfilesForUser(apiSession.getUserId(), index, 100, ProfileCriterion.ID_ASC)).size() == 100 && (profile = list.find { it.getId() == profileId }) == null) { index += 100 } return profile != null || list.find { it.getId() == profileId } != null } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ApplicationPermissionRule.groovy ================================================ /** * Copyright (C) 2021 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.permissions import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.business.application.Application import org.bonitasoft.engine.exception.NotFoundException import org.bonitasoft.engine.session.APISession /** * * Secure profile related resources * * can be added to *
    *
  • GET|living/application
  • *
* @author Anthony Birembaut */ class ApplicationPermissionRule extends ApplicationPermissionCommon implements PermissionRule { @Override boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { try { if (apiCallContext.isGET()) { def application = getApplicationFromIdOrToken(apiCallContext, apiAccessor) if (application != null) { return isAppAllowed(application, apiAccessor, apiSession) } else { return apiSession.getUserId().toString().equals(apiCallContext.getFilters().get("userId")) } } return false } catch (NotFoundException e) { logger.debug("application not found: is allowed") //let the API handle the 404 return true } } private Application getApplicationFromIdOrToken(APICallContext apiCallContext, APIAccessor apiAccessor) { def application def applicationAPI = apiAccessor.getLivingApplicationAPI() if (apiCallContext.getResourceId() != null) { def applicationId = Long.valueOf(apiCallContext.getResourceId()) application = applicationAPI.getApplication(applicationId) } else if (apiCallContext.getFilters().get("token") != null) { application = applicationAPI.getApplicationByToken(apiCallContext.getFilters().get("token")) } return application } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/CaseContextPermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.exception.BonitaException import org.bonitasoft.engine.session.APISession /** * * Let a user access only cases that he is involved in and start cases that he can start * *
    *
  • bpm/case/[id]/context
  • *
  • bpm/archivedCase/[id]/context
  • *
* * @author Anthony Birembaut */ class CaseContextPermissionRule implements PermissionRule { @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() def processAPI = apiAccessor.getProcessAPI() try { def caseId = getCaseId(apiCallContext) if (caseId <= 0) { return true } def originalCaseId if (apiCallContext.getResourceName().startsWith("archived")) { originalCaseId = processAPI.getArchivedProcessInstance(caseId).getSourceObjectId() } else { originalCaseId = caseId } // isInvolvedInProcessInstance() already checks the archived and non-archived involvement def isInvolved = processAPI.isInvolvedInProcessInstance(currentUserId, originalCaseId) || processAPI.isManagerOfUserInvolvedInProcessInstance(currentUserId, originalCaseId) if (isInvolved) { return true } def processDefinitionId if (apiCallContext.getResourceName().startsWith("archived")) { processDefinitionId = processAPI.getArchivedProcessInstance(caseId).getProcessDefinitionId() } else { processDefinitionId = processAPI.getProcessInstance(caseId).getProcessDefinitionId() } return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId) } catch (BonitaException e) { //exception, allow user to have the 404 when the rest api will look for the resource: return true } } private long getCaseId(APICallContext apiCallContext) { def compoundResourceId = apiCallContext.getCompoundResourceId() if (compoundResourceId == null || compoundResourceId.isEmpty()) { return -1L } return Long.valueOf(compoundResourceId.get(0)) } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/CasePermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import com.fasterxml.jackson.databind.ObjectMapper import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.ProcessAPI import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor import org.bonitasoft.engine.exception.BonitaException import org.bonitasoft.engine.exception.NotFoundException import org.bonitasoft.engine.identity.User import org.bonitasoft.engine.identity.UserSearchDescriptor import org.bonitasoft.engine.search.SearchOptionsBuilder import org.bonitasoft.engine.search.SearchResult import org.bonitasoft.engine.session.APISession /** * * Let a user access only cases that he is involved in and start cases that he can start * *
    *
  • bpm/case
  • *
  • bpm/archivedCase
  • *
* * * * @author Baptiste Mesta * @author Anthony Birembaut */ class CasePermissionRule implements PermissionRule { @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() if (apiCallContext.isGET()) { return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger) } else if (apiCallContext.isPOST()) { return checkPostMethod(apiCallContext, apiAccessor, currentUserId, logger) } else if (apiCallContext.isPUT()) { return checkPutMethod(apiCallContext, apiAccessor, currentUserId, logger) } return false } private boolean checkPostMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { ObjectMapper mapper = new ObjectMapper() def map = mapper.readValue(apiCallContext.getBody(), Map.class) def string = map.get("processDefinitionId") if (string == null || string.toString().isEmpty()) { return true } def processDefinitionId = Long.valueOf(string.toString()) if (processDefinitionId <= 0) { return true } def processAPI = apiAccessor.getProcessAPI() def identityAPI = apiAccessor.getIdentityAPI() User user = identityAPI.getUser(currentUserId) SearchOptionsBuilder searchOptionBuilder = new SearchOptionsBuilder(0, 10) searchOptionBuilder.filter(UserSearchDescriptor.USER_NAME, user.getUserName()) SearchResult listUsers = processAPI.searchUsersWhoCanStartProcessDefinition(processDefinitionId, searchOptionBuilder.done()) logger.debug("RuleCase : nb Result [" + listUsers.getCount() + "] ?") def canStart = listUsers.getCount() == 1 logger.debug("RuleCase : User allowed to start? " + canStart) return canStart } private boolean isInvolved(ProcessAPI processAPI, long currentUserId, long processInstanceId) { return processAPI.isInvolvedInProcessInstance(currentUserId, processInstanceId) || processAPI.isManagerOfUserInvolvedInProcessInstance(currentUserId, processInstanceId) } private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { def processAPI = apiAccessor.getProcessAPI() def filters = apiCallContext.getFilters() try { if (apiCallContext.getResourceId() != null) { def processDefinitionId if (apiCallContext.getResourceName().startsWith("archived")) { def archivedProcessInstanceId = Long.valueOf(apiCallContext.getResourceId()) def archivedProcessInstance = processAPI.getArchivedProcessInstance(archivedProcessInstanceId) def processInstanceId = archivedProcessInstance.getSourceObjectId() if (isInvolved(processAPI, currentUserId, processInstanceId)) { return true } processDefinitionId = archivedProcessInstance.getProcessDefinitionId() } else { def processInstanceId = Long.valueOf(apiCallContext.getResourceId()) if (isInvolved(processAPI, currentUserId, processInstanceId)) { return true } processDefinitionId = processAPI.getProcessInstance(processInstanceId).getProcessDefinitionId() } logger.debug("RuleCase : allowed because get on process that user is involved in") return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId) } else { def stringUserId = String.valueOf(currentUserId) if (stringUserId.equals(filters.get("started_by")) || stringUserId.equals(filters.get("user_id")) || stringUserId.equals(filters.get("supervisor_id"))) { logger.debug("RuleCase : allowed because searching filters contains user id") return true } if (filters.containsKey("processDefinitionId")) { return processAPI.isUserProcessSupervisor(Long.valueOf(filters.get("processDefinitionId")), currentUserId) } if ("archivedCase".equals(apiCallContext.getResourceName()) && filters.containsKey("sourceObjectId")) { def sourceCase = Long.valueOf(filters.get("sourceObjectId")) final SearchOptionsBuilder opts = new SearchOptionsBuilder(0, 1) opts.filter(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, sourceCase) def result = processAPI.searchArchivedProcessInstancesInvolvingUser(currentUserId, opts.done()) def archivedProcessInstance = processAPI.getFinalArchivedProcessInstance(sourceCase) return result.getCount() == 1 || processAPI.isUserProcessSupervisor(archivedProcessInstance.getProcessDefinitionId(), currentUserId) } } } catch (BonitaException e) { //exception, allow user to have the 404 when the rest api will look for the resource: return true } return false } private boolean checkPutMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { def processAPI = apiAccessor.getProcessAPI() def processInstanceId = Long.valueOf(apiCallContext.getResourceId()) def processDefinitionId = processAPI.getProcessInstance(processInstanceId).getProcessDefinitionId() return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId) } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/CaseVariablePermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.exception.NotFoundException import org.bonitasoft.engine.session.APISession /** * * Let a user get and update a variable of a case only if he is the process supervisor * *
    *
  • bpm/caseVariable
  • *
* * * * @author Baptiste Mesta */ class CaseVariablePermissionRule implements PermissionRule { @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() def resourceId = apiCallContext.getResourceId() def processAPI = apiAccessor.getProcessAPI() try { if ((apiCallContext.isPUT() || apiCallContext.isGET()) && resourceId != null) { // Resource format: / def caseId = Long.valueOf(resourceId.tokenize("/").first()) def processInstance = processAPI.getProcessInstance(caseId) return processAPI.isUserProcessSupervisor(processInstance.getProcessDefinitionId(), currentUserId) } def filters = apiCallContext.getFilters() if (apiCallContext.isGET() && filters.containsKey("case_id")) { def caseId = Long.valueOf(filters.get("case_id")) def processInstance = processAPI.getProcessInstance(caseId) return processAPI.isUserProcessSupervisor(processInstance.getProcessDefinitionId(), currentUserId) } return false } catch (NotFoundException e) { return true } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/CommentPermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import com.fasterxml.jackson.databind.ObjectMapper import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.ProcessAPI import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor import org.bonitasoft.engine.exception.BonitaException import org.bonitasoft.engine.exception.NotFoundException import org.bonitasoft.engine.search.SearchOptionsBuilder import org.bonitasoft.engine.session.APISession /** * * Let a user access only comments on cases that he is involved in * *
    *
  • bpm/comment
  • *
  • bpm/archivedComment
  • *
* * * * @author Baptiste Mesta */ class CommentPermissionRule implements PermissionRule { @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() if (apiCallContext.isGET()) { return checkGetMethod(apiCallContext, apiAccessor, currentUserId) } else if (apiCallContext.isPOST()) { return checkPostMethod(apiCallContext, apiAccessor, currentUserId, logger) } return false } private boolean checkPostMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { ObjectMapper mapper = new ObjectMapper() def map = mapper.readValue(apiCallContext.getBody(), Map.class) def string = map.get("processInstanceId") if (string == null || string.toString().isEmpty()) { return true } def processInstanceId = Long.valueOf(string.toString()) if (processInstanceId <= 0) { return true } def processAPI = apiAccessor.getProcessAPI() return isInvolved(processAPI, currentUserId, processInstanceId) || isSupervisor(processAPI, processInstanceId, currentUserId) } private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId) { def filters = apiCallContext.getFilters() def stringUserId = String.valueOf(currentUserId) if (stringUserId.equals(filters.get("team_manager_id")) || stringUserId.equals(filters.get("user_id")) || stringUserId.equals(filters.get("supervisor_id"))) { return true } if (filters.containsKey("processInstanceId")) { def processInstanceId = Long.valueOf(filters.get("processInstanceId")) def processAPI = apiAccessor.getProcessAPI() return isInvolved(processAPI, currentUserId, processInstanceId) || isSupervisor(processAPI, processInstanceId, currentUserId) } return false } private boolean isInvolved(ProcessAPI processAPI, long currentUserId, long processInstanceId) { try { return processAPI.isInvolvedInProcessInstance(currentUserId, processInstanceId) || processAPI.isManagerOfUserInvolvedInProcessInstance(currentUserId, processInstanceId) } catch (BonitaException e) { return true } } private boolean isSupervisor(ProcessAPI processAPI, long processInstanceId, long currentUserId) { def processDefinitionId try { processDefinitionId = processAPI.getProcessInstance(processInstanceId).getProcessDefinitionId() } catch (NotFoundException e) { try { processDefinitionId = processAPI.getFinalArchivedProcessInstance(processInstanceId).getProcessDefinitionId() } catch (NotFoundException e1) { return true } } return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId) } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ConnectorInstancePermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceNotFoundException import org.bonitasoft.engine.exception.SearchException import org.bonitasoft.engine.search.SearchOptionsBuilder import org.bonitasoft.engine.session.APISession /** * * Let a user see process configuration only if he is process supervisor * *
    *
  • bpm/connectorInstance
  • *
  • bpm/archivedConnectorInstance
  • *
* * * * @author Anthony Birembaut */ class ConnectorInstancePermissionRule implements PermissionRule { public static final String CONTAINER_ID = "containerId" @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() if (apiCallContext.isGET()) { return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger) } else if (apiCallContext.isPUT()) { //TODO unable to find a connector instance with the API! return false } return true } private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { def filters = apiCallContext.getFilters() if(filters.containsKey(CONTAINER_ID)){ def processAPI = apiAccessor.getProcessAPI() def processID if (apiCallContext.getResourceName().startsWith("archived")) { try { def searchOptions = new SearchOptionsBuilder(0, 1) searchOptions.filter(ArchivedFlowNodeInstanceSearchDescriptor.ORIGINAL_FLOW_NODE_ID, Long.valueOf(filters.get(CONTAINER_ID))) def searchResult = processAPI.searchArchivedFlowNodeInstances(searchOptions.done()) def archivedFlowNodeInstances = searchResult.getResult() if (archivedFlowNodeInstances.isEmpty()) { logger.debug("archived flow node does not exists") return true } else { processID = archivedFlowNodeInstances.get(0).getProcessDefinitionId() } } catch(SearchException e) { logger.debug("error while retrieving the archived flow node") return true } } else { try{ def flowNodeInstance = processAPI.getFlowNodeInstance(Long.valueOf(filters.get(CONTAINER_ID))) processID = flowNodeInstance.getProcessDefinitionId() } catch(FlowNodeInstanceNotFoundException e) { logger.debug("flow node does not exists") return true } } return processAPI.isUserProcessSupervisor(processID,currentUserId) } return false } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/DocumentPermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import com.fasterxml.jackson.databind.ObjectMapper import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.ProcessAPI import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor import org.bonitasoft.engine.exception.BonitaException import org.bonitasoft.engine.exception.NotFoundException import org.bonitasoft.engine.search.SearchOptionsBuilder import org.bonitasoft.engine.session.APISession /** * * Let a user access only document on cases that he is involved in * *
    *
  • bpm/document
  • *
  • bpm/archivedDocument
  • *
  • bpm/caseDocument
  • *
* * * * @author Baptiste Mesta * @author Truc Nguyen */ class DocumentPermissionRule implements PermissionRule { public static final String CASE_ID = "caseId" public static final String ARCHIVED_CASE_ID = "archivedCaseId" @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() def resourceId = apiCallContext.getResourceId() if (resourceId != null) { return checkMethodWithResourceId(resourceId, apiAccessor, currentUserId, logger) } if (apiCallContext.isGET()) { return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger) } else if (apiCallContext.isPOST()) { return checkPostMethod(apiCallContext, apiAccessor, currentUserId, logger) } return false } private boolean checkMethodWithResourceId(String resourceId, APIAccessor apiAccessor, long currentUserId, Logger logger) { def processAPI = apiAccessor.getProcessAPI() try { long documentId = Long.valueOf(resourceId) def processInstanceId = processAPI.getDocument(documentId).getProcessInstanceId() return isInvolved(processAPI, currentUserId, processInstanceId, logger) || isSupervisor(processAPI, currentUserId, processInstanceId, logger) } catch (NumberFormatException e) { logger.debug("documentId " + documentIdStr + " is not a number") return false } } private boolean checkPostMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { ObjectMapper mapper = new ObjectMapper() def map = mapper.readValue(apiCallContext.getBody(), Map.class) def processInstanceIdAsString = map.get(CASE_ID) if (processInstanceIdAsString == null || processInstanceIdAsString.toString().isEmpty()) { return true } def processInstanceId = Long.valueOf(processInstanceIdAsString.toString()) if (processInstanceId <= 0) { return true } try { def processAPI = apiAccessor.getProcessAPI() def processDefinitionId = processAPI.getProcessInstance(processInstanceId).getProcessDefinitionId() return isInvolved(processAPI, currentUserId, processInstanceId, logger) || processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId) } catch (NotFoundException e) { logger.debug("Process instance of document not found.") return true } } private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { def filters = apiCallContext.getFilters() def processAPI = apiAccessor.getProcessAPI() long processInstanceId = -1 long processDefinitionId = -1 def archivedCaseIdAsString = filters.get(ARCHIVED_CASE_ID) if (archivedCaseIdAsString != null) { def archivedCaseId = Long.valueOf(archivedCaseIdAsString) processInstanceId = processAPI.getArchivedProcessInstance(archivedCaseId).getSourceObjectId() processDefinitionId = processAPI.getFinalArchivedProcessInstance(processInstanceId).getProcessDefinitionId() } else { def processInstanceIdAsString = filters.get(CASE_ID) if (processInstanceIdAsString != null) { processInstanceId = Long.valueOf(processInstanceIdAsString) processDefinitionId = processAPI.getProcessInstance(processInstanceId).getProcessDefinitionId() } } if (processInstanceId > 0 && processDefinitionId > 0) { return isInvolved(processAPI, currentUserId, processInstanceId, logger) || processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId) } return false } private boolean isInvolved(ProcessAPI processAPI, long currentUserId, long processInstanceId, Logger logger) { try { return processAPI.isInvolvedInProcessInstance(currentUserId, processInstanceId) || processAPI.isManagerOfUserInvolvedInProcessInstance(currentUserId, processInstanceId) || hasPendingTaskInCurrentSubprocess(processAPI, currentUserId, processInstanceId, logger) } catch (BonitaException e) { logger.debug("Error checking if user is involved in process instance of document.", e) return false } } private boolean isSupervisor(ProcessAPI processAPI, long currentUserId, long processInstanceId, Logger logger) { def processDefinitionId try { processDefinitionId = processAPI.getProcessInstance(processInstanceId).getProcessDefinitionId() } catch (NotFoundException e) { try { processDefinitionId = processAPI.getFinalArchivedProcessInstance(processInstanceId).getProcessDefinitionId() } catch (NotFoundException e1) { logger.debug("Process instance of document not found.") return false } } return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId) } private boolean hasPendingTaskInCurrentSubprocess(ProcessAPI processAPI, long currentUserId, long processInstanceIdAsSubprocess, Logger logger) { try { SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 0) builder.filter(HumanTaskInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, processInstanceIdAsSubprocess) def taskSearchResult = processAPI.searchMyAvailableHumanTasks(currentUserId, builder.done()) return taskSearchResult.getCount() > 0 } catch (BonitaException e) { logger.debug("Error checking if user is involved in process instance of document.", e) return false } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/DownloadDocumentPermissionRule.groovy ================================================ /** * Copyright (C) 2017 BonitaSoft S.A. * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2.0 of the License, or * (at your option) any later version. * This program 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 General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . **/ package org.bonitasoft.permissions import com.fasterxml.jackson.databind.ObjectMapper import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.ProcessAPI import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.bpm.document.DocumentsSearchDescriptor import org.bonitasoft.engine.bpm.document.DocumentNotFoundException import org.bonitasoft.engine.bpm.document.ArchivedDocumentNotFoundException import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor import org.bonitasoft.engine.exception.BonitaException import org.bonitasoft.engine.exception.NotFoundException import org.bonitasoft.engine.search.SearchOptionsBuilder import org.bonitasoft.engine.session.APISession /** * * Let a user access only document on cases that he is involved in * *
    *
  • portal/documentDownload
  • *
  • portal/downloadDocument
  • *
  • portal/formsDocumentDownload
  • *
* * * * @author Anthony Birembaut */ class DownloadDocumentPermissionRule implements PermissionRule { public static final String DOCUMENT_ID_PARAM = "document" public static final String CONTENT_STORAGE_ID_PARAM = "contentStorageId" public static final String FILE_NAME_PARAM = "fileName" @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() if (apiCallContext.isGET()) { def processInstanceId = null def contentStorageId = apiCallContext.getParameters().get(CONTENT_STORAGE_ID_PARAM) def fileName = apiCallContext.getParameters().get(FILE_NAME_PARAM) def documentId = apiCallContext.getParameters().get(DOCUMENT_ID_PARAM) // Download servlets requires either contentStorageId + fileName parameters or a document parameter if (fileName != null && contentStorageId != null && contentStorageId.length > 0) { processInstanceId = getProcessInstanceIdFromContentStorageId(contentStorageId[0], apiAccessor, logger) } else if (documentId != null && documentId.length > 0) { try { long longDocumentId = Long.valueOf(documentId[0]) processInstanceId = getProcessInstanceIdFromDocumentId(longDocumentId, apiAccessor, logger) } catch (NumberFormatException e) { logger.debug("documentId " + documentId + " is not a number") return false } } if (processInstanceId != -1l) { return checkInvolvement(processInstanceId, apiAccessor, currentUserId, logger) } return true } return false } private long getProcessInstanceIdFromDocumentId(long documentId, APIAccessor apiAccessor, Logger logger) { def processInstanceId = -1l def processAPI = apiAccessor.getProcessAPI() try { processInstanceId = processAPI.getDocument(documentId).getProcessInstanceId() } catch (DocumentNotFoundException dnfe) { try { processInstanceId = processAPI.getArchivedVersionOfProcessDocument(documentId).getProcessInstanceId() } catch (ArchivedDocumentNotFoundException e) { logger.debug("No document or archived document found with Id " + documentId) } } return processInstanceId } private long getProcessInstanceIdFromContentStorageId(String contentStorageIdStr, APIAccessor apiAccessor, Logger logger) { def processInstanceId = -1l def processAPI = apiAccessor.getProcessAPI() try { long contentStorageId = Long.valueOf(contentStorageIdStr) SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1) builder.filter(DocumentsSearchDescriptor.CONTENT_STORAGE_ID, contentStorageId) def documentSearchResult = processAPI.searchDocuments(builder.done()) if (documentSearchResult.getCount() > 0) { processInstanceId = documentSearchResult.getResult().get(0).getProcessInstanceId() } else { def archivedDocumentSearchResult = processAPI.searchArchivedDocuments(builder.done()) if (archivedDocumentSearchResult.getCount() > 0) { processInstanceId = archivedDocumentSearchResult.getResult().get(0).getProcessInstanceId() } } if (processInstanceId == -1l) { logger.debug("No document or archived document found for contentStorageId " + contentStorageIdStr) } } catch (NumberFormatException e) { logger.debug("contentStorageId is not a number") } return processInstanceId } private boolean checkInvolvement(long processInstanceId, APIAccessor apiAccessor, long currentUserId, Logger logger) { def processAPI = apiAccessor.getProcessAPI() return isInvolved(processAPI, currentUserId, processInstanceId, logger) || isSupervisor(processAPI, currentUserId, processInstanceId, logger) } private boolean isInvolved(ProcessAPI processAPI, long currentUserId, long processInstanceId, Logger logger) { try { return processAPI.isInvolvedInProcessInstance(currentUserId, processInstanceId) || processAPI.isManagerOfUserInvolvedInProcessInstance(currentUserId, processInstanceId) || hasPendingTaskInCurrentSubprocess(processAPI, currentUserId, processInstanceId, logger) } catch (BonitaException e) { logger.debug("Error checking if user is involved in process instance of document.", e) return false } } private boolean isSupervisor(ProcessAPI processAPI, long currentUserId, long processInstanceId, Logger logger) { def processDefinitionId try { processDefinitionId = processAPI.getProcessInstance(processInstanceId).getProcessDefinitionId() } catch (NotFoundException e) { try { processDefinitionId = processAPI.getFinalArchivedProcessInstance(processInstanceId).getProcessDefinitionId() } catch (NotFoundException e1) { logger.debug("Process instance of document not found.") return false } } return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId) } private boolean hasPendingTaskInCurrentSubprocess(ProcessAPI processAPI, long currentUserId, long processInstanceIdAsSubprocess, Logger logger) { try { SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 0) builder.filter(HumanTaskInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, processInstanceIdAsSubprocess) def taskSearchResult = processAPI.searchMyAvailableHumanTasks(currentUserId, builder.done()) return taskSearchResult.getCount() > 0 } catch (BonitaException e) { logger.debug("Error checking if user is involved in process instance of document.", e) return false } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ProcessConfigurationPermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.session.APISession /** * * Let a user manage process connectors and parameters only if he is process supervisor * *
    *
  • bpm/processConnector
  • *
  • bpm/processParameter
  • *
* * * * @author Anthony Birembaut */ class ProcessConfigurationPermissionRule implements PermissionRule { public static final String PROCESS_ID = "process_id" @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() if (apiCallContext.isGET()) { return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger) } else if (apiCallContext.isPUT()) { return checkPutMethod(apiCallContext, apiAccessor, currentUserId, logger) } return true } private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { def resourceIds = apiCallContext.getCompoundResourceId() if (resourceIds.isEmpty()) { def filters = apiCallContext.getFilters() if(filters.containsKey(PROCESS_ID)){ def processAPI = apiAccessor.getProcessAPI() return processAPI.isUserProcessSupervisor(Long.valueOf(filters.get(PROCESS_ID)),currentUserId) } return false } else { return isProcessOwnerOfTheProcess(apiAccessor, resourceIds, currentUserId) } } private boolean checkPutMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { def resourceIds = apiCallContext.getCompoundResourceId() return isProcessOwnerOfTheProcess(apiAccessor, resourceIds, currentUserId) } private isProcessOwnerOfTheProcess(APIAccessor apiAccessor, List resourceIds, long currentUserId) { def processAPI = apiAccessor.getProcessAPI() def processID = Long.parseLong(resourceIds.get(0)) return processAPI.isUserProcessSupervisor(processID, currentUserId) } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ProcessConnectorDependencyPermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.session.APISession /** * * Let a user see process connector dependency problem only if he is process supervisor * *
    *
  • bpm/processConnectorDependency
  • *
* * * * @author Anthony Birembaut */ class ProcessConnectorDependencyPermissionRule implements PermissionRule { public static final String PROCESS_ID = "connector_process_id" @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() if (apiCallContext.isGET()) { return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger) } return true } private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { def filters = apiCallContext.getFilters() if(filters.containsKey(PROCESS_ID)){ def processAPI = apiAccessor.getProcessAPI() return processAPI.isUserProcessSupervisor(Long.valueOf(filters.get(PROCESS_ID)),currentUserId) } return false } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ProcessInstantiationPermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import com.fasterxml.jackson.databind.ObjectMapper import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor import org.bonitasoft.engine.exception.BonitaException import org.bonitasoft.engine.exception.NotFoundException import org.bonitasoft.engine.identity.User import org.bonitasoft.engine.identity.UserSearchDescriptor import org.bonitasoft.engine.search.SearchOptionsBuilder import org.bonitasoft.engine.search.SearchResult import org.bonitasoft.engine.session.APISession /** * * Let a user access only cases that he is involved in and start cases that he can start * *
    *
  • bpm/process/[id]/contract
  • *
  • bpm/process/[id]/instantiation
  • *
* * * * @author Anthony Birembaut */ class ProcessInstantiationPermissionRule implements PermissionRule { @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() def processDefinitionId = getProcessDefinitionId(apiCallContext) if (processDefinitionId <= 0) { return true } try { def processAPI = apiAccessor.getProcessAPI() def identityAPI = apiAccessor.getIdentityAPI() User user = identityAPI.getUser(currentUserId) SearchOptionsBuilder searchOptionBuilder = new SearchOptionsBuilder(0, 1) if(apiCallContext.getParameters().get("user") != null && apiCallContext.getParameters().get("user").length > 0) { if (!processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)) { return false } searchOptionBuilder.filter(UserSearchDescriptor.ID, apiCallContext.getParameters().get("user")[0]) } else { searchOptionBuilder.filter(UserSearchDescriptor.ID, user.getId()) } SearchResult listUsers = processAPI.searchUsersWhoCanStartProcessDefinition(processDefinitionId, searchOptionBuilder.done()) logger.debug("RuleCase : nb Result [" + listUsers.getCount() + "] ?") def canStart = listUsers.getCount() == 1 logger.debug("RuleCase : User allowed to start? " + canStart) return canStart } catch (NotFoundException e) { //exception, allow user to have the 404 when the rest api will look for the resource: return true } } private long getProcessDefinitionId(APICallContext apiCallContext) { def compoundResourceId = apiCallContext.getCompoundResourceId() if (compoundResourceId == null || compoundResourceId.isEmpty()) { return -1L } return Long.valueOf(compoundResourceId.get(0)) } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ProcessPermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor import org.bonitasoft.engine.search.SearchOptionsBuilder import org.bonitasoft.engine.session.APISession /** * * Let the user do get only on processes he deployed or that he supervised * * * can be added to *
    *
  • bpm/process
  • *
* * * * @author Baptiste Mesta */ class ProcessPermissionRule implements PermissionRule { @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() if (apiCallContext.isGET()) { return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger) } if (apiCallContext.isPUT()) { return checkPutMethod(apiCallContext, apiAccessor, currentUserId, logger) } return false } private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { def processAPI = apiAccessor.getProcessAPI() def filters = apiCallContext.getFilters() def resourceIds = apiCallContext.getCompoundResourceId() if (!resourceIds.isEmpty()) { def processId = Long.parseLong(resourceIds.get(0)) def processDefinition = processAPI.getProcessDeploymentInfo(processId) def deployedByUser = processDefinition.getDeployedBy() == currentUserId if (deployedByUser) { logger.debug("deployed by the current user") return true } def canStart = processAPI.searchProcessDeploymentInfosCanBeStartedBy(currentUserId, new SearchOptionsBuilder(0, 0).filter(ProcessDeploymentInfoSearchDescriptor.PROCESS_ID, processDefinition.getProcessId()).done()) if (canStart.getCount() > 0) { logger.debug("can start the process, so can get it") return true } // look for available tasks with this direct process ID including processes which are started by call activities (subprocesses) def canExecuteTask = processAPI.searchMyAvailableHumanTasks(currentUserId, new SearchOptionsBuilder(0, 0).filter(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processId).done()) if(canExecuteTask.getCount() > 0) { logger.debug("has at least one task on process, so can get it") return true } def isSupervisor = processAPI.isUserProcessSupervisor(processId, currentUserId) if (isSupervisor) { logger.debug("is supervisor of the process") return true } // look for available tasks with root process ID including the subprocesses of the process with this ID def hasTasksInRootOrSubprocesses = processAPI.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(currentUserId, new SearchOptionsBuilder(0, 0).filter(ProcessDeploymentInfoSearchDescriptor.PROCESS_ID, processDefinition.getProcessId()).done()) if (hasTasksInRootOrSubprocesses.getCount() > 0) { logger.debug("can execute process tasks, so can get the process") return true } return false } else { def stringUserId = String.valueOf(currentUserId) if (stringUserId.equals(filters.get("team_manager_id")) || stringUserId.equals(filters.get("supervisor_id")) || stringUserId.equals(filters.get("user_id"))) { logger.debug("allowed because searching filters contains user id") return true } } return false } private boolean checkPutMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { def resourceIds = apiCallContext.getCompoundResourceId() if (!resourceIds.isEmpty()) { def processId = Long.parseLong(resourceIds.get(0)) def processAPI = apiAccessor.getProcessAPI() def isSupervisor = processAPI.isUserProcessSupervisor(processId, currentUserId) if(isSupervisor){ logger.debug("is supervisor of the process") return true } return false } return true } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ProcessResolutionProblemPermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.session.APISession /** * * Let a user see process resolution problem only if he is process supervisor * *
    *
  • bpm/processResolutionProblem
  • *
* * * * @author Anthony Birembaut */ class ProcessResolutionProblemPermissionRule implements PermissionRule { public static final String PROCESS_ID = "process_id" @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() if (apiCallContext.isGET()) { return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger) } return true } private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { def filters = apiCallContext.getFilters() if(filters.containsKey(PROCESS_ID)){ def processAPI = apiAccessor.getProcessAPI() return processAPI.isUserProcessSupervisor(Long.valueOf(filters.get(PROCESS_ID)),currentUserId) } return false } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ProcessSupervisorPermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import com.fasterxml.jackson.databind.ObjectMapper import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.session.APISession /** * * Let a user view and add process only if he is process supervisor * *
    *
  • bpm/processSupervisor
  • *
* * * * @author Anthony Birembaut */ class ProcessSupervisorPermissionRule implements PermissionRule { public static final String PROCESS_ID = "process_id" @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() if (apiCallContext.isPOST()) { return checkPostMethod(apiCallContext, apiAccessor, currentUserId, logger) } else if (apiCallContext.isGET()) { return checkGetMethod(apiCallContext, apiAccessor, currentUserId, logger) } else if (apiCallContext.isDELETE()) { return checkDeleteMethod(apiCallContext, apiAccessor, currentUserId, logger) } //it's ok to read return true } private boolean checkPostMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { ObjectMapper mapper = new ObjectMapper() def map = mapper.readValue(apiCallContext.getBody(), Map.class) def processAPI = apiAccessor.getProcessAPI() def processIdString = map.get("process_id") if (processIdString == null || processIdString.toString().isEmpty()) { return false } def processId = Long.valueOf(processIdString.toString()) if (processId <= 0) { return false } return processAPI.isUserProcessSupervisor(processId, currentUserId) } private boolean checkGetMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { def filters = apiCallContext.getFilters() if (filters.containsKey(PROCESS_ID)) { def processAPI = apiAccessor.getProcessAPI() return processAPI.isUserProcessSupervisor(Long.parseLong(filters.get(PROCESS_ID)), currentUserId) } return true } private boolean checkDeleteMethod(APICallContext apiCallContext, APIAccessor apiAccessor, long currentUserId, Logger logger) { def resourceIds = apiCallContext.getCompoundResourceId() if (!resourceIds.isEmpty()) { def processAPI = apiAccessor.getProcessAPI() return processAPI.isUserProcessSupervisor(Long.parseLong(resourceIds.get(0)), currentUserId) } return true } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/ProfilePermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.exception.NotFoundException import org.bonitasoft.engine.profile.ProfileCriterion import org.bonitasoft.engine.session.APISession /** * * Secure profile related resources * * can be added to *
    *
  • portal/profile
  • *
* @author Baptiste Mesta */ class ProfilePermissionRule implements PermissionRule { @Override boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { try { if (apiCallContext.isGET()) { if(apiCallContext.getResourceId() != null){ def profileId = Long.valueOf(apiCallContext.getResourceId()) def processAPI = apiAccessor.getProfileAPI() def index = 0 def profile def list = [] while ((list = processAPI.getProfilesForUser(apiSession.getUserId(),index,100,ProfileCriterion.ID_ASC)).size() == 100 && (profile = list.find{it.getId() == profileId}) == null){ index += 100 } return profile != null || list.find{it.getId() == profileId} != null } else { return apiSession.getUserId().toString().equals(apiCallContext.getFilters().get("user_id")) } } return false } catch (NotFoundException e) { logger.debug("profile not found: is allowed") //let the API handle the 404 return true } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/TaskExecutionPermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import com.fasterxml.jackson.databind.ObjectMapper import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.ProcessAPI import org.bonitasoft.engine.api.IdentityAPI import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance import org.bonitasoft.engine.bpm.flownode.ArchivedManualTaskInstance import org.bonitasoft.engine.bpm.flownode.FlowNodeType import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance import org.bonitasoft.engine.bpm.flownode.ManualTaskInstance import org.bonitasoft.engine.exception.NotFoundException import org.bonitasoft.engine.identity.User import org.bonitasoft.engine.identity.UserSearchDescriptor import org.bonitasoft.engine.search.SearchOptionsBuilder import org.bonitasoft.engine.session.APISession /** * * Let a user access only tasks that are assigned or pending to him * * * can be added to *
    *
  • bpm/archivedUserTask/[id]/context
  • *
  • bpm/userTask/[id]/context
  • *
  • bpm/userTask/[id]/contract
  • *
  • bpm/userTask/[id]/execution
  • *
* * * @author Anthony Birembaut */ class TaskExecutionPermissionRule implements PermissionRule { private static final String USER_PARAM = "user" @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() def userName = apiSession.getUserName() def processAPI = apiAccessor.getProcessAPI() def identityAPI = apiAccessor.getIdentityAPI() try { return isTaskAccessibleByUser(processAPI, identityAPI, apiCallContext, logger, currentUserId, userName) } catch (NotFoundException e) { logger.debug("flow node not found: is allowed") return true } } protected boolean isTaskAccessibleByUser(ProcessAPI processAPI, IdentityAPI identityAPI, APICallContext apiCallContext, Logger logger, long currentUserId, String username) throws NotFoundException { def taskInstanceId = getTaskInstanceId(apiCallContext) if (taskInstanceId <= 0) { return true } if (apiCallContext.getResourceName().startsWith("archived")) { return isArchivedFlowNodeAccessible(processAPI, taskInstanceId, currentUserId, username, logger) } else { def assignedUser = getAssignedUser(apiCallContext, identityAPI, logger) return isTaskAccessible(processAPI, taskInstanceId, currentUserId, username, assignedUser, logger) } } private User getAssignedUser(APICallContext apiCallContext, IdentityAPI identityAPI, Logger logger) { def assignedId = apiCallContext.getParameters().get(USER_PARAM) if (assignedId != null && assignedId.length > 0) { try { def assignedUserId = Long.valueOf(assignedId[0]) return identityAPI.getUser(assignedUserId) } catch (Exception e) { logger.debug("user Id is not a long or does not match an existing user") } } return null } private boolean isArchivedFlowNodeAccessible(ProcessAPI processAPI, long taskId, long currentUserId, String username, Logger logger) throws NotFoundException { def archivedFlowNodeInstance = processAPI.getArchivedFlowNodeInstance(taskId) if (FlowNodeType.MANUAL_TASK.equals(archivedFlowNodeInstance.getType()) || FlowNodeType.USER_TASK.equals(archivedFlowNodeInstance.getType())) { if (currentUserId == archivedFlowNodeInstance.getExecutedBy()) { return true } //get the last flow node in journal if (archivedFlowNodeInstance.getExecutedBy() == 0){ try { def instance1 = processAPI.getHumanTaskInstance(archivedFlowNodeInstance.getSourceObjectId()) if(currentUserId == instance1.getAssigneeId()){ return true } } catch(NotFoundException e){ //do nothing } } } if (FlowNodeType.MANUAL_TASK.equals(archivedFlowNodeInstance.getType())) { try { def parentTask = processAPI.getHumanTaskInstance(archivedFlowNodeInstance.getParentContainerId()) if (parentTask.assigneeId > 0) { if (parentTask.assigneeId == currentUserId) { return true } } else { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1) builder.filter(UserSearchDescriptor.USER_NAME, username) def searchResult = processAPI.searchUsersWhoCanExecutePendingHumanTask(parentTask.id, builder.done()) if (searchResult.getCount() == 1l) { logger.debug("The parent task is pending for user") return true } } } catch (NotFoundException e) { try { def instance = processAPI.getArchivedActivityInstance(archivedFlowNodeInstance.getParentContainerId()) //return false because it means the parent is not found, not the element itself if ((FlowNodeType.MANUAL_TASK.equals(instance.getType()) || FlowNodeType.USER_TASK.equals(instance.getType())) && instance.assigneeId > 0) { if (instance.assigneeId == currentUserId) { return true } } } catch (NotFoundException e1) { //return false because it means the parent is not found, not the element itself return false } } } def processDefinitionId = archivedFlowNodeInstance.getProcessDefinitionId() return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId) } private boolean isTaskAccessible(ProcessAPI processAPI, long flowNodeId, long currentUserId, String username, User assignedUser, Logger logger) throws NotFoundException { def instance = processAPI.getFlowNodeInstance(flowNodeId) if (FlowNodeType.MANUAL_TASK.equals(instance.getType()) || FlowNodeType.USER_TASK.equals(instance.getType())) { if (instance.assigneeId > 0) { if (instance.assigneeId == currentUserId) { return true } } else { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1) builder.filter(UserSearchDescriptor.USER_NAME, username) def searchResult = processAPI.searchUsersWhoCanExecutePendingHumanTask(flowNodeId, builder.done()) if (searchResult.getCount() == 1l) { logger.debug("The task is pending for user") return true } } //we can access the task if we can access the parent of the subtask if (FlowNodeType.MANUAL_TASK.equals(instance.getType())) { try { def parentTask = processAPI.getHumanTaskInstance(instance.getParentContainerId()) if (parentTask.assigneeId > 0) { if (parentTask.assigneeId == currentUserId) { return true } } else { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1) builder.filter(UserSearchDescriptor.USER_NAME, username) def searchResult = processAPI.searchUsersWhoCanExecutePendingHumanTask(parentTask.id, builder.done()) if (searchResult.getCount() == 1l) { logger.debug("The parent task is pending for user") return true } } } catch (NotFoundException e) { //return false because it means the parent is not found, not the element itself return false } } } def processDefinitionId = instance.getProcessDefinitionId() if (processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)) { if (assignedUser != null){ return isTaskAccessible(processAPI, flowNodeId, assignedUser.getId(), assignedUser.getUserName(), null, logger) } return true } return false } private long getTaskInstanceId(APICallContext apiCallContext) { def compoundResourceId = apiCallContext.getCompoundResourceId() if (compoundResourceId == null || compoundResourceId.isEmpty()) { return -1L } return Long.valueOf(compoundResourceId.get(0)) } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/TaskPermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import com.fasterxml.jackson.databind.ObjectMapper import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.ProcessAPI import org.bonitasoft.engine.api.IdentityAPI import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance import org.bonitasoft.engine.bpm.flownode.ArchivedManualTaskInstance import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance import org.bonitasoft.engine.bpm.flownode.ManualTaskInstance import org.bonitasoft.engine.bpm.flownode.FlowNodeType import org.bonitasoft.engine.exception.NotFoundException import org.bonitasoft.engine.identity.User import org.bonitasoft.engine.identity.UserSearchDescriptor import org.bonitasoft.engine.search.SearchOptionsBuilder import org.bonitasoft.engine.session.APISession /** * * Let a user access only tasks that are assigned or pending to him * * * can be added to *
    *
  • bpm/humanTask
  • *
  • bpm/userTask
  • *
  • bpm/archivedHumanTask
  • *
  • bpm/archivedUserTask
  • *
  • bpm/activity
  • *
  • bpm/activityReplay
  • *
  • bpm/archivedActivity
  • *
  • bpm/task
  • *
  • bpm/archivedTask
  • *
  • bpm/flowNode
  • *
  • bpm/archivedFlowNode
  • *
  • bpm/manualTask
  • *
  • bpm/archivedManualTask
  • *
  • bpm/archivedTask
  • *
* * * @author Baptiste Mesta */ class TaskPermissionRule implements PermissionRule { @Override public boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { long currentUserId = apiSession.getUserId() def userName = apiSession.getUserName() def processAPI = apiAccessor.getProcessAPI() def filters = apiCallContext.getFilters() try { if (apiCallContext.isGET()) { return checkGetMethod(apiCallContext, processAPI, logger, currentUserId, userName, filters) } else if (apiCallContext.isPUT() && apiCallContext.getResourceId() != null) { def assignUser = getAssignedUser(apiCallContext, apiAccessor, logger) return isTaskAccessibleByUser(processAPI, apiCallContext, logger, currentUserId, userName, assignUser) } else if (apiCallContext.isPOST()) { return checkPostMethod(apiCallContext, currentUserId, processAPI, userName, logger) } } catch (NotFoundException e) { logger.debug("flow node not found: is allowed") return true } return true } private User getAssignedUser(APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { if (apiCallContext.getBody() != null) { ObjectMapper mapper = new ObjectMapper() def map = mapper.readValue(apiCallContext.getBody(), Map.class) if (map != null) { def assignedId = map.get("assigned_id") if (assignedId != null && !assignedId.toString().isEmpty()) { try { def assignedUserId = Long.valueOf(assignedId.toString()) def identityAPI = apiAccessor.getIdentityAPI() return identityAPI.getUser(assignedUserId) } catch (Exception e) { logger.debug("assigned Id is not a long or does not match an existing user") } } } } return null } private boolean checkGetMethod(APICallContext apiCallContext, ProcessAPI processAPI, Logger logger, long currentUserId, String userName, Map filters) { if (apiCallContext.getResourceId() != null) { return isTaskAccessibleByUser(processAPI, apiCallContext, logger, currentUserId, userName, null) } else if (hasFilter(currentUserId, filters, "assigned_id") || hasFilter(currentUserId, filters, "user_id") || hasFilter(currentUserId, filters, "hidden_user_id") || hasFilter(currentUserId, filters, "supervisor_id")) { logger.debug("FilterOnUser or FilterOnAssignUser") return true } else if (filters.containsKey("parentTaskId")) { def long parentTaskId = Long.parseLong(filters.get("parentTaskId")) try { return isTaskAccessible(processAPI, filters.get("parentTaskId"), currentUserId, userName, null, logger) } catch (NotFoundException e) { return isArchivedFlowNodeAccessible(processAPI, parentTaskId, currentUserId, userName, logger) } } else if (filters.containsKey("processId")) { def long processId = Long.valueOf(filters.get("processId")) return processAPI.isUserProcessSupervisor(processId, currentUserId) } else if (filters.containsKey("caseId") || filters.containsKey("parentCaseId")) { def long caseId = filters.containsKey("caseId") ? Long.parseLong(filters.get("caseId")) : Long.parseLong(filters.get("parentCaseId")) return processAPI.isUserProcessSupervisor(processAPI.getProcessInstance(caseId).getProcessDefinitionId(), currentUserId) } else { return false } } private boolean checkPostMethod(APICallContext apiCallContext, long currentUserId, ProcessAPI processAPI, String userName, Logger logger) { if ("manualTask".equals(apiCallContext.getResourceName())) { ObjectMapper mapper = new ObjectMapper() def map = mapper.readValue(apiCallContext.getBody(), Map.class) def string = map.get("parentTaskId").toString() if (string == null || string.isEmpty()) { return true } def parentTaskId = Long.valueOf(string) def flowNodeInstance = processAPI.getFlowNodeInstance(parentTaskId) return flowNodeInstance instanceof HumanTaskInstance && flowNodeInstance.getAssigneeId() } return false } private boolean hasFilter(long currentUserId, Map filters, String assigned_id) { return String.valueOf(currentUserId).equals(filters.get(assigned_id)) } protected boolean isTaskAccessibleByUser(ProcessAPI processAPI, APICallContext apiCallContext, Logger logger, long currentUserId, String username, User assignedUser) throws NotFoundException { if (apiCallContext.getResourceName().startsWith("archived")) { return isArchivedFlowNodeAccessible(processAPI, Long.valueOf(apiCallContext.getResourceId()), currentUserId, username, logger) } else { return isTaskAccessible(processAPI, apiCallContext.getResourceId(), currentUserId, username, assignedUser, logger) } } private boolean isArchivedFlowNodeAccessible(ProcessAPI processAPI, long taskId, long currentUserId, String username, Logger logger) throws NotFoundException { def archivedFlowNodeInstance = processAPI.getArchivedFlowNodeInstance(taskId) if (FlowNodeType.MANUAL_TASK.equals(archivedFlowNodeInstance.getType()) || FlowNodeType.USER_TASK.equals(archivedFlowNodeInstance.getType())) { if (currentUserId == archivedFlowNodeInstance.getExecutedBy()) { return true } //get the last flow node in journal if(archivedFlowNodeInstance.getExecutedBy() == 0){ try { def instance1 = processAPI.getHumanTaskInstance(archivedFlowNodeInstance.getSourceObjectId()) if(currentUserId == instance1.getAssigneeId()){ return true } } catch(NotFoundException e){ //do nothing } } } if (FlowNodeType.MANUAL_TASK.equals(archivedFlowNodeInstance.getType())) { try { def parentTask = processAPI.getHumanTaskInstance(archivedFlowNodeInstance.getParentContainerId()) if (parentTask.assigneeId > 0) { if (parentTask.assigneeId == currentUserId) { return true } } else { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1) builder.filter(UserSearchDescriptor.USER_NAME, username) def searchResult = processAPI.searchUsersWhoCanExecutePendingHumanTask(parentTask.id, builder.done()) if (searchResult.getCount() == 1l) { logger.debug("The parent task is pending for user") return true } } } catch (NotFoundException e) { try { def instance = processAPI.getArchivedActivityInstance(archivedFlowNodeInstance.getParentContainerId()) //return false because it means the parent is not found, not the element itself if ((FlowNodeType.MANUAL_TASK.equals(instance.getType()) || FlowNodeType.USER_TASK.equals(instance.getType())) && instance.assigneeId > 0) { if (instance.assigneeId == currentUserId) { return true } } } catch (NotFoundException e1) { //return false because it means the parent is not found, not the element itself return false } } } def processDefinitionId = archivedFlowNodeInstance.getProcessDefinitionId() return processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId) } private boolean isTaskAccessible(ProcessAPI processAPI, String flowNodeIdAsString, long currentUserId, String username, User assignedUser, Logger logger) throws NotFoundException { def long flowNodeId = Long.valueOf(flowNodeIdAsString) def instance = processAPI.getFlowNodeInstance(flowNodeId) if (instance instanceof HumanTaskInstance) { if (instance.assigneeId > 0) { if (instance.assigneeId == currentUserId) { return true } } else { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1) builder.filter(UserSearchDescriptor.USER_NAME, username) def searchResult = processAPI.searchUsersWhoCanExecutePendingHumanTask(flowNodeId, builder.done()) if (searchResult.getCount() == 1l) { logger.debug("The task is pending for user") return true } } //we can access the task if we can access the parent of the subtask if (instance instanceof ManualTaskInstance) { try { def parentTask = processAPI.getHumanTaskInstance(instance.getParentContainerId()) if (parentTask.assigneeId > 0) { if (parentTask.assigneeId == currentUserId) { return true } } else { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1) builder.filter(UserSearchDescriptor.USER_NAME, username) def searchResult = processAPI.searchUsersWhoCanExecutePendingHumanTask(parentTask.id, builder.done()) if (searchResult.getCount() == 1l) { logger.debug("The parent task is pending for user") return true } } } catch (NotFoundException e) { //return false because it means the parent is not found, not the element itself return false } } } def processDefinitionId = instance.getProcessDefinitionId() if (processAPI.isUserProcessSupervisor(processDefinitionId, currentUserId)) { if (assignedUser != null){ return isTaskAccessible(processAPI, flowNodeIdAsString, assignedUser.getId(), assignedUser.getUserName(), null, logger) } return true } return false } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/groovy/org/bonitasoft/permissions/UserPermissionRule.groovy ================================================ /** * Copyright (C) 2017 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.permissions import org.bonitasoft.engine.api.APIAccessor import org.bonitasoft.engine.api.Logger import org.bonitasoft.engine.api.permission.APICallContext import org.bonitasoft.engine.api.permission.PermissionRule import org.bonitasoft.engine.exception.NotFoundException import org.bonitasoft.engine.session.APISession /** * * Let the user access and modify only himself * * can be added to *
    *
  • identity/user
  • *
  • identity/professionalcontactdata
  • *
  • identity/personalcontactdata
  • *
* * @author Baptiste Mesta */ class UserPermissionRule implements PermissionRule { @Override boolean isAllowed(APISession apiSession, APICallContext apiCallContext, APIAccessor apiAccessor, Logger logger) { APISession session = apiSession long currentUserId = session.getUserId() if (apiCallContext.getResourceId() != null) { def resourceId = Long.valueOf(apiCallContext.getResourceId()) if (resourceId.equals(currentUserId)) { return true } return false } else { if (apiCallContext.getQueryString() != null && (apiCallContext.getQueryString().contains("d=professional_data") || apiCallContext.getQueryString().contains("d=personnal_data"))) { return false } def filters = apiCallContext.getFilters() //search by task id for the do for if (filters.containsKey("task_id")) { def taskId = Long.valueOf(filters.get("task_id")) def processAPI = apiAccessor.getProcessAPI() try { def flowNodeInstance = processAPI.getFlowNodeInstance(taskId) return processAPI.isUserProcessSupervisor(flowNodeInstance.getProcessDefinitionId(), currentUserId) } catch (NotFoundException e) { return true } } if (filters.containsKey("process_id")) { def processId = Long.valueOf(filters.get("process_id")) def processAPI = apiAccessor.getProcessAPI() return processAPI.isUserProcessSupervisor(processId, currentUserId) } return false } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/EngineConfiguration.java ================================================ /** * Copyright (C) 2022 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; import java.util.Properties; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import lombok.Data; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.events.impl.EventServiceImpl; import org.bonitasoft.engine.monitoring.ExecutorServiceMetricsProvider; import org.bonitasoft.engine.monitoring.NoOpExecutorServiceMetricsProvider; import org.bonitasoft.engine.persistence.HibernateConfigurationProvider; import org.bonitasoft.engine.persistence.HibernateMetricsBinder; import org.bonitasoft.engine.persistence.HibernatePersistenceService; import org.bonitasoft.engine.persistence.QueryBuilderFactory; import org.bonitasoft.engine.sequence.SequenceManager; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.scheduling.annotation.EnableScheduling; @EnableScheduling @Configuration @ComponentScan("org.bonitasoft.engine") @EnableAspectJAutoProxy // Uses JDK dynamic proxies (interface-based) @Data public class EngineConfiguration { /** * Whether the runtime should exit after its initialization or not. */ @Value("${bonita.runtime.startup.update-only:false}") private boolean updateOnlyAtStartup; @Bean("platformEventService") @ConditionalOnMissingBean(name = "platformEventService") EventService platformEventService() { return new EventServiceImpl(); } @Bean @ConditionalOnMissingBean HibernatePersistenceService persistenceService(final HibernateConfigurationProvider hbmConfigurationProvider, final Properties extraHibernateProperties, final SequenceManager sequenceManager, HibernateMetricsBinder hibernateMetricsBinder, QueryBuilderFactory queryBuilderFactory) { return new HibernatePersistenceService(hbmConfigurationProvider, extraHibernateProperties, sequenceManager, queryBuilderFactory, hibernateMetricsBinder); } @Bean @ConditionalOnMissingBean MeterRegistry meterRegistry() { return new SimpleMeterRegistry(); } @Bean @ConditionalOnMissingBean public HibernateMetricsBinder noOpHibernateMetrics() { return (sessionFactory -> { }); } @Bean @ConditionalOnMissingBean public ExecutorServiceMetricsProvider noOpExecutorServiceBinder() { return new NoOpExecutorServiceMetricsProvider(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/EngineInitializer.java ================================================ /** * 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; import java.io.IOException; import org.bonitasoft.engine.api.PlatformAPI; import org.bonitasoft.engine.api.impl.PlatformAPIImpl; import org.bonitasoft.engine.event.PlatformStartedEvent; import org.bonitasoft.engine.exception.BonitaHomeConfigurationException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.platform.PlatformManager; import org.bonitasoft.engine.platform.PlatformNotFoundException; import org.bonitasoft.engine.platform.session.PlatformSessionService; import org.bonitasoft.engine.platform.session.SSessionException; import org.bonitasoft.engine.platform.session.SSessionNotFoundException; import org.bonitasoft.engine.platform.session.model.SPlatformSession; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Orchestrates the Bonita Engine initialization and manages the platform node lifecycle. *

* This class is responsible for starting and stopping the Bonita Platform node (the current JVM instance). * It creates a temporary platform session, verifies the platform exists in the database, starts all * platform services, and publishes the platform started event. *

* Initialization Flow: *

    *
  1. Obtains platform services via {@link ServiceAccessorFactory}
  2. *
  3. Creates a temporary "SYSTEM" platform session for initialization
  4. *
  5. Instantiates {@link PlatformAPI} for platform operations
  6. *
  7. Verifies the platform is created in the database via {@link PlatformAPI#isPlatformCreated()}
  8. *
  9. Starts the node via {@link PlatformAPI#startNode()}, which triggers {@link PlatformManager#start()}
  10. *
  11. Publishes {@link PlatformStartedEvent} to notify subscribers
  12. *
  13. Deletes the temporary platform session
  14. *
*

* Responsibilities: *

    *
  • Creates and manages temporary platform sessions for initialization/shutdown operations
  • *
  • Coordinates with {@link PlatformAPI} to start/stop the platform node
  • *
  • Publishes platform lifecycle events via {@link org.bonitasoft.engine.service.ServiceAccessor}
  • *
  • Handles graceful shutdown of the engine via {@link #unloadEngine()}
  • *
  • Logs engine edition and data collection information messages
  • *
*

* Note: This class does NOT create the platform or database tables. Platform creation is handled * by {@link org.bonitasoft.platform.setup.PlatformSetup} before this initializer runs. *

* Usage: This class is typically invoked by * {@link org.bonitasoft.engine.api.internal.servlet.EngineInitializerListener} * during servlet container startup. * * @author Baptiste Mesta * @see PlatformAPI * @see PlatformManager * @see ServiceAccessorFactory * @see org.bonitasoft.platform.setup.PlatformSetup */ public class EngineInitializer { protected static final Logger LOGGER = LoggerFactory.getLogger(EngineInitializer.class.getName()); private static PlatformAPI platformAPI; public EngineInitializer() { super(); } public void initializeEngine() throws Exception { LOGGER.info("Initializing Bonita Engine..."); final long before = System.currentTimeMillis(); // create a session to call the engine final ServiceAccessor platformService = getPlatformService(); final PlatformSessionService platformSessionService = platformService.getPlatformSessionService(); final SessionAccessor sessionAccessor = getSessionAccessor(); final long sessionId = createPlatformSession(platformSessionService, sessionAccessor); final PlatformAPI platformAPI = getPlatformAPI(); try { if (!platformAPI.isPlatformCreated()) { throw new PlatformNotFoundException("Can't start or stop platform if it is not created."); } LOGGER.info("Starting node..."); logEditionMessage(); logDataCollectionMessage(); platformAPI.startNode(); LOGGER.info("Node started successfully."); final long after = System.currentTimeMillis(); LOGGER.info("Initialization of Bonita Engine done! (took " + (after - before) + "ms)"); LOGGER.debug("Publishing platform started event"); platformService.publishEvent(new PlatformStartedEvent()); } finally { deletePlatformSession(platformSessionService, sessionAccessor, sessionId); } } protected void logEditionMessage() { LOGGER.info(" ____ _ _ _____ _ _ "); LOGGER.info(" | _ \\ (_) | / ____| (_) | "); LOGGER.info(" | |_) | ___ _ __ _| |_ __ _ | | ___ _ __ ___ _ __ ___ _ _ _ __ _| |_ _ _ "); LOGGER.info(" | _ < / _ \\| '_ \\| | __/ _` | | | / _ \\| '_ ` _ \\| '_ ` _ \\| | | | '_ \\| | __| | | |"); LOGGER.info(" | |_) | (_) | | | | | || (_| | | |___| (_) | | | | | | | | | | | |_| | | | | | |_| |_| |"); LOGGER.info( " |____/ \\___/|_| |_|_|\\__\\__,_| \\_____\\___/|_| |_| |_|_| |_| |_|\\__,_|_| |_|_|\\__|\\__, |"); LOGGER.info(" __/ |"); LOGGER.info(" |___/ "); } public void logDataCollectionMessage() { LOGGER.info("-----------------------------------------------------------------------------------------"); LOGGER.info("Anonymous Data Collection for Product Improvement"); LOGGER.info(""); LOGGER.info("Dear User,"); LOGGER.info(""); LOGGER.info("We collect strictly anonymous usage data from Bonita Studio and the Runtime (production"); LOGGER.info("environment) to help us continuously improve the product and enhance the user experience."); LOGGER.info("The data collected is fully anonymous and cannot be used to identify you in any way."); LOGGER.info(""); LOGGER.info("This data helps us understand how the product is used in both development and production"); LOGGER.info("settings, allowing us to optimize performance, fix bugs, and introduce new features that"); LOGGER.info("benefit all users."); LOGGER.info(""); LOGGER.info("For more information on what data we collect and how to opt-out, please visit our"); LOGGER.info("Product Documentation (https://documentation.ofelia.com/bonita/latest)."); LOGGER.info(""); LOGGER.info("Thank you for supporting the ongoing improvement of our product!"); LOGGER.info("-----------------------------------------------------------------------------------------"); } SessionAccessor getSessionAccessor() throws BonitaHomeNotSetException, IOException, BonitaHomeConfigurationException, ReflectiveOperationException { return getServiceAccessorFactory().createSessionAccessor(); } ServiceAccessor getPlatformService() { try { return getServiceAccessorFactory().createServiceAccessor(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } // Visible for testing PlatformAPI getPlatformAPI() { if (platformAPI == null) { //in local only platformAPI = newPlatformAPI(); } return platformAPI; } protected PlatformAPI newPlatformAPI() { return new PlatformAPIImpl(); } private void deletePlatformSession(final PlatformSessionService platformSessionService, final SessionAccessor sessionAccessor, final long sessionId) throws SSessionNotFoundException { platformSessionService.deleteSession(sessionId); sessionAccessor.deleteSessionId(); } private long createPlatformSession(final PlatformSessionService platformSessionService, final SessionAccessor sessionAccessor) throws SSessionException { final SPlatformSession createSession = platformSessionService.createSession("SYSTEM"); final long sessionId = createSession.getId(); sessionAccessor.setSessionId(sessionId); return sessionId; } public void unloadEngine() throws Exception { LOGGER.info("Stopping Bonita Engine..."); // create a session to call the engine final SessionAccessor sessionAccessor = getSessionAccessor(); final PlatformSessionService platformSessionService = getPlatformService().getPlatformSessionService(); final long sessionId = createPlatformSession(platformSessionService, sessionAccessor); final PlatformAPI platformAPI = getPlatformAPI(); try { if (!platformAPI.isNodeStarted()) { LOGGER.info("Node is not started, nothing to do."); return; } LOGGER.info("Stopping node..."); platformAPI.stopNode(); } catch (final Throwable e) { LOGGER.warn("Error while stopping the platform", e); } finally { deletePlatformSession(platformSessionService, sessionAccessor, sessionId); // after that the engine is unloaded getServiceAccessorFactory().destroyAccessors(); LOGGER.info("Bonita Engine stopped!"); } } ServiceAccessorFactory getServiceAccessorFactory() { return ServiceAccessorFactory.getInstance(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/LocalLoginMechanism.java ================================================ /** * 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; import java.util.Date; import org.bonitasoft.engine.platform.PlatformLoginException; import org.bonitasoft.engine.platform.session.PlatformSessionService; import org.bonitasoft.engine.platform.session.model.SPlatformSession; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.engine.session.impl.PlatformSessionImpl; /** * Use to login on the platform when inside the same JVM * * @author Baptiste Mesta */ //Do not remove //used by reflection when on the same JVM //see org.bonitasoft.console.common.server.utils.PlatformManagementUtils.platformLogin() in bonita-web public class LocalLoginMechanism { public PlatformSession login() throws PlatformLoginException { try { final PlatformSessionService platformSessionService = ServiceAccessorFactory.getInstance() .createServiceAccessor().getPlatformSessionService(); SPlatformSession platformSession = platformSessionService.createSession("local"); final Date creationDate = platformSession.getCreationDate(); return new PlatformSessionImpl(platformSession.getId(), creationDate, platformSession.getDuration(), "local", platformSession.getUserId()); } catch (final Exception e) { throw new PlatformLoginException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/SArchivingException.java ================================================ /** * 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; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SArchivingException extends SBonitaException { private static final long serialVersionUID = -1985173032064351538L; public SArchivingException(final String message, final Throwable cause) { super(message, cause); } public SArchivingException(final String message) { super(message); } public SArchivingException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/APIAccessorImpl.java ================================================ /** * 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.api.impl; import org.bonitasoft.engine.api.APIAccessor; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.BusinessDataAPI; import org.bonitasoft.engine.api.CommandAPI; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.PermissionAPI; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.ProfileAPI; /** * @author Matthieu Chaffotte * @author Celine Souchet * @author Baptiste Mesta */ public class APIAccessorImpl implements APIAccessor { private static final long serialVersionUID = -3602975597536895697L; @Override public IdentityAPI getIdentityAPI() { return new IdentityAPIImpl(); } @Override public ProcessAPI getProcessAPI() { return new ProcessAPIImpl(); } @Override public CommandAPI getCommandAPI() { return new CommandAPIImpl(); } @Override public ProfileAPI getProfileAPI() { return new ProfileAPIImpl(); } @Override public PermissionAPI getPermissionAPI() { return new PermissionAPIImpl(); } public PageAPI getCustomPageAPI() { return new PageAPIImpl(); } @Override public ApplicationAPI getLivingApplicationAPI() { return new ApplicationAPIImpl(); } @Override public BusinessDataAPI getBusinessDataAPI() { return new BusinessDataAPIImpl(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/APIUtils.java ================================================ /** * 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.api.impl; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; /** * @author Baptiste Mesta */ public class APIUtils { protected static long getUserId() { return SessionInfos.getUserIdFromSession(); } protected static ServiceAccessor getServiceAccessor() { try { return ServiceAccessorSingleton.getInstance(); } catch (final Exception e) { throw new RuntimeException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ApplicationAPIImpl.java ================================================ /** * 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.api.impl; import static org.bonitasoft.engine.business.application.ApplicationSearchDescriptor.TOKEN; import static org.bonitasoft.engine.business.application.ApplicationSearchDescriptor.USER_ID; import static org.bonitasoft.engine.business.application.InternalProfiles.INTERNAL_PROFILE_SUPER_ADMIN; import static org.bonitasoft.engine.search.descriptor.SearchApplicationDescriptor.APPLICATION_VISIBILITY; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.api.impl.converter.ApplicationMenuModelConverter; import org.bonitasoft.engine.api.impl.converter.ApplicationModelConverter; import org.bonitasoft.engine.api.impl.converter.ApplicationPageModelConverter; import org.bonitasoft.engine.api.impl.livingapplication.LivingApplicationAPIDelegate; import org.bonitasoft.engine.api.impl.livingapplication.LivingApplicationExporterDelegate; import org.bonitasoft.engine.api.impl.livingapplication.LivingApplicationMenuAPIDelegate; import org.bonitasoft.engine.api.impl.livingapplication.LivingApplicationPageAPIDelegate; import org.bonitasoft.engine.api.impl.transaction.application.SearchApplicationMenus; import org.bonitasoft.engine.api.impl.transaction.application.SearchApplicationPages; import org.bonitasoft.engine.api.impl.transaction.application.SearchApplications; import org.bonitasoft.engine.api.impl.transaction.application.SearchApplicationsOfUser; import org.bonitasoft.engine.business.application.Application; import org.bonitasoft.engine.business.application.ApplicationCreator; import org.bonitasoft.engine.business.application.ApplicationImportPolicy; import org.bonitasoft.engine.business.application.ApplicationLink; import org.bonitasoft.engine.business.application.ApplicationLinkCreator; import org.bonitasoft.engine.business.application.ApplicationLinkUpdater; import org.bonitasoft.engine.business.application.ApplicationMenu; import org.bonitasoft.engine.business.application.ApplicationMenuCreator; import org.bonitasoft.engine.business.application.ApplicationMenuNotFoundException; import org.bonitasoft.engine.business.application.ApplicationMenuUpdater; import org.bonitasoft.engine.business.application.ApplicationNotFoundException; import org.bonitasoft.engine.business.application.ApplicationPage; import org.bonitasoft.engine.business.application.ApplicationPageNotFoundException; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.ApplicationUpdater; import org.bonitasoft.engine.business.application.IApplication; import org.bonitasoft.engine.business.application.Icon; import org.bonitasoft.engine.business.application.converter.ApplicationMenuToNodeConverter; import org.bonitasoft.engine.business.application.converter.ApplicationPageToNodeConverter; import org.bonitasoft.engine.business.application.converter.ApplicationToNodeConverter; import org.bonitasoft.engine.business.application.converter.ApplicationsToNodeContainerConverter; import org.bonitasoft.engine.business.application.converter.NodeToApplicationConverter; import org.bonitasoft.engine.business.application.exporter.ApplicationContainerExporter; import org.bonitasoft.engine.business.application.exporter.ApplicationExporter; import org.bonitasoft.engine.business.application.importer.StrategySelector; import org.bonitasoft.engine.business.application.importer.validator.ApplicationImportValidator; import org.bonitasoft.engine.business.application.importer.validator.ApplicationMenuCreatorValidator; import org.bonitasoft.engine.business.application.importer.validator.ApplicationTokenValidator; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.ExportException; import org.bonitasoft.engine.exception.ImportException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.profile.ProfileService; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.search.descriptor.SearchApplicationDescriptor; import org.bonitasoft.engine.search.descriptor.SearchApplicationMenuDescriptor; import org.bonitasoft.engine.search.descriptor.SearchApplicationPageDescriptor; import org.bonitasoft.engine.search.impl.SearchFilter; import org.bonitasoft.engine.search.impl.SearchResultImpl; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.session.SessionService; /** * @author Elias Ricken de Medeiros */ @AvailableInMaintenanceMode public class ApplicationAPIImpl implements ApplicationAPI { /** * {@inheritDoc} */ @Override @Deprecated(since = "9.0.0") public Application createApplication(final ApplicationCreator applicationCreator) throws CreationException { return getLivingApplicationAPIDelegate().createApplication(applicationCreator); } /** * {@inheritDoc} */ @Override @Deprecated(since = "10.2.0") public ApplicationLink createApplicationLink(final ApplicationLinkCreator applicationLinkCreator) throws CreationException { return getLivingApplicationAPIDelegate().createApplicationLink(applicationLinkCreator); } private LivingApplicationAPIDelegate getLivingApplicationAPIDelegate() { return new LivingApplicationAPIDelegate(getServiceAccessor(), getApplicationModelConverter(getServiceAccessor().getPageService()), SessionInfos.getUserIdFromSession(), new ApplicationTokenValidator()); } protected ApplicationModelConverter getApplicationModelConverter(final PageService pageService) { return new ApplicationModelConverter(pageService); } private LivingApplicationPageAPIDelegate getApplicationPageAPIDelegate() { return new LivingApplicationPageAPIDelegate(getServiceAccessor(), new ApplicationPageModelConverter(), SessionInfos.getUserIdFromSession(), new ApplicationTokenValidator()); } private LivingApplicationMenuAPIDelegate getApplicationMenuAPIDelegate() { return new LivingApplicationMenuAPIDelegate(getServiceAccessor(), new ApplicationMenuModelConverter(), new ApplicationMenuCreatorValidator(), SessionInfos.getUserIdFromSession()); } private LivingApplicationExporterDelegate getLivingApplicationExporterDelegate() { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ApplicationService applicationService = serviceAccessor.getApplicationService(); final PageService pageService = serviceAccessor.getPageService(); ApplicationToNodeConverter applicationToNodeConverter = new ApplicationToNodeConverter( serviceAccessor.getProfileService(), applicationService, new ApplicationPageToNodeConverter(pageService), new ApplicationMenuToNodeConverter(applicationService), pageService); final ApplicationsToNodeContainerConverter applicationsToNodeContainerConverter = new ApplicationsToNodeContainerConverter( applicationToNodeConverter); final ApplicationContainerExporter applicationContainerExporter = new ApplicationContainerExporter(); final ApplicationExporter applicationExporter = new ApplicationExporter(applicationsToNodeContainerConverter, applicationContainerExporter); return new LivingApplicationExporterDelegate(serviceAccessor.getApplicationService(), applicationExporter); } protected NodeToApplicationConverter getNodeToApplicationConverter(final PageService pageService, final ProfileService profileService, final ApplicationImportValidator importValidator) { return new NodeToApplicationConverter(profileService, pageService, importValidator); } @Override public IApplication getIApplication(final long applicationId) throws ApplicationNotFoundException { return getLivingApplicationAPIDelegate().getIApplication(applicationId); } @Override public IApplication getIApplicationByToken(final String applicationToken) throws ApplicationNotFoundException { return getLivingApplicationAPIDelegate().getIApplicationByToken(applicationToken); } @Override public void deleteApplication(final long applicationId) throws DeletionException { getLivingApplicationAPIDelegate().deleteApplication(applicationId); } /** * {@inheritDoc} */ @Override @Deprecated(since = "9.0.0") public Application updateApplication(final long applicationId, final ApplicationUpdater updater) throws ApplicationNotFoundException, UpdateException, AlreadyExistsException { return getLivingApplicationAPIDelegate().updateApplication(applicationId, updater); } /** * {@inheritDoc} */ @Override @Deprecated(since = "10.2.0") public ApplicationLink updateApplicationLink(final long applicationId, final ApplicationLinkUpdater updater) throws ApplicationNotFoundException, UpdateException, AlreadyExistsException { return getLivingApplicationAPIDelegate().updateApplicationLink(applicationId, updater); } protected ServiceAccessor getServiceAccessor() { try { return ServiceAccessorFactory.getInstance().createServiceAccessor(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } /** * @deprecated as of 10.2.0, use {@link #searchIApplications(SearchOptions)} instead to include application links. */ @Deprecated(since = "10.2.0") @Override public SearchResult searchApplications(final SearchOptions searchOptions) throws SearchException { if (searchOptions.getFilters().size() == 1) { // Avoid a search query for a search by token to benefit from the cache optimization final SearchFilter searchFilter = searchOptions.getFilters().get(0); if (TOKEN.equals(searchFilter.getField())) { try { return new SearchResultImpl<>(1, List.of(getApplicationByToken((String) searchFilter.getValue()))); } catch (ApplicationNotFoundException e) { return new SearchResultImpl<>(0, List.of()); } } } final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchApplicationDescriptor appSearchDescriptor = serviceAccessor.getSearchEntitiesDescriptor() .getSearchApplicationDescriptor(); final ApplicationModelConverter converter = getApplicationModelConverter(serviceAccessor.getPageService()); final ApplicationService applicationService = serviceAccessor.getApplicationService(); final Optional filterOnUserId = searchOptions.getFilters().stream() .filter(s -> s.getField().equals(USER_ID)).findFirst(); if (filterOnUserId.isPresent()) { final SearchOptionsBuilder searchOptionsWithoutUserId = new SearchOptionsBuilder(searchOptions) .setFilters(searchOptions.getFilters().stream().filter(s -> !s.getField().equals(USER_ID)) .collect(Collectors.toList())); final String userId = String.valueOf(filterOnUserId.get().getValue()); if (String.valueOf(SessionService.SYSTEM_ID).equals(userId)) { // It's the tenant admin user return getLivingApplicationAPIDelegate().searchIApplications(new SearchApplications( Application.class, applicationService, appSearchDescriptor, searchOptionsWithoutUserId .filter(APPLICATION_VISIBILITY, INTERNAL_PROFILE_SUPER_ADMIN.getProfileName()).done(), converter)); } else { return getLivingApplicationAPIDelegate().searchIApplications(new SearchApplicationsOfUser( Application.class, Long.parseLong(userId), applicationService, appSearchDescriptor, searchOptionsWithoutUserId.done(), converter)); } } return getLivingApplicationAPIDelegate() .searchIApplications(new SearchApplications(Application.class, applicationService, appSearchDescriptor, searchOptions, converter)); } @Override public SearchResult searchIApplications(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchApplicationDescriptor appSearchDescriptor = serviceAccessor.getSearchEntitiesDescriptor() .getSearchApplicationDescriptor(); return internalSearchIApplications(serviceAccessor, appSearchDescriptor, searchOptions); } protected SearchResult internalSearchIApplications(ServiceAccessor serviceAccessor, SearchApplicationDescriptor appSearchDescriptor, SearchOptions searchOptions) throws SearchException { if (searchOptions.getFilters().size() == 1) { // Avoid a search query for a search by token to benefit from the cache optimization final SearchFilter searchFilter = searchOptions.getFilters().get(0); if (TOKEN.equals(searchFilter.getField())) { try { return new SearchResultImpl<>(1, List.of(getIApplicationByToken((String) searchFilter.getValue()))); } catch (ApplicationNotFoundException e) { return new SearchResultImpl<>(0, List.of()); } } } final ApplicationModelConverter converter = getApplicationModelConverter(serviceAccessor.getPageService()); final ApplicationService applicationService = serviceAccessor.getApplicationService(); final Optional filterOnUserId = searchOptions.getFilters().stream() .filter(s -> s.getField().equals(USER_ID)).findFirst(); if (filterOnUserId.isPresent()) { final SearchOptionsBuilder searchOptionsWithoutUserId = new SearchOptionsBuilder(searchOptions) .setFilters(searchOptions.getFilters().stream().filter(s -> !s.getField().equals(USER_ID)) .collect(Collectors.toList())); final String userId = String.valueOf(filterOnUserId.get().getValue()); if (String.valueOf(SessionService.SYSTEM_ID).equals(userId)) { // It's the tenant admin user return getLivingApplicationAPIDelegate() .searchIApplications(SearchApplications.defaultSearchApplications( applicationService, appSearchDescriptor, searchOptionsWithoutUserId .filter(APPLICATION_VISIBILITY, INTERNAL_PROFILE_SUPER_ADMIN.getProfileName()) .done(), converter)); } else { return getLivingApplicationAPIDelegate() .searchIApplications(SearchApplicationsOfUser.defaultSearchApplicationsOfUser( Long.parseLong(userId), applicationService, appSearchDescriptor, searchOptionsWithoutUserId.done(), converter)); } } return getLivingApplicationAPIDelegate() .searchIApplications( SearchApplications.defaultSearchApplications(applicationService, appSearchDescriptor, searchOptions, converter)); } /** * {@inheritDoc} */ @Override @Deprecated(since = "9.0.0") public ApplicationPage createApplicationPage(final long applicationId, final long pageId, final String token) throws CreationException { return getApplicationPageAPIDelegate().createApplicationPage(applicationId, pageId, token); } @Override public ApplicationPage getApplicationPage(final String applicationName, final String applicationPageToken) throws ApplicationPageNotFoundException { return getApplicationPageAPIDelegate().getApplicationPage(applicationName, applicationPageToken); } @Override public SearchResult searchApplicationPages(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchApplicationPageDescriptor appPageSearchDescriptor = serviceAccessor.getSearchEntitiesDescriptor() .getSearchApplicationPageDescriptor(); final ApplicationPageModelConverter converter = new ApplicationPageModelConverter(); final ApplicationService applicationService = serviceAccessor.getApplicationService(); final SearchApplicationPages searchApplicationPages = new SearchApplicationPages(applicationService, converter, appPageSearchDescriptor, searchOptions); return getApplicationPageAPIDelegate().searchApplicationPages(searchApplicationPages); } @Override public ApplicationPage getApplicationPage(final long applicationPageId) throws ApplicationPageNotFoundException { return getApplicationPageAPIDelegate().getApplicationPage(applicationPageId); } @Override public void deleteApplicationPage(final long applicationPageId) throws DeletionException { getApplicationPageAPIDelegate().deleteApplicationPage(applicationPageId); } /** * {@inheritDoc} */ @Override @Deprecated(since = "9.0.0") public void setApplicationHomePage(final long applicationId, final long applicationPageId) throws UpdateException, ApplicationNotFoundException { getApplicationPageAPIDelegate().setApplicationHomePage(applicationId, applicationPageId); } @Override public ApplicationPage getApplicationHomePage(final long applicationId) throws ApplicationPageNotFoundException { return getApplicationPageAPIDelegate().getApplicationHomePage(applicationId); } /** * {@inheritDoc} */ @Override @Deprecated(since = "9.0.0") public ApplicationMenu createApplicationMenu(final ApplicationMenuCreator applicationMenuCreator) throws CreationException { return getApplicationMenuAPIDelegate().createApplicationMenu(applicationMenuCreator); } /** * {@inheritDoc} */ @Override @Deprecated(since = "9.0.0") public ApplicationMenu updateApplicationMenu(final long applicationMenuId, final ApplicationMenuUpdater updater) throws ApplicationMenuNotFoundException, UpdateException { return getApplicationMenuAPIDelegate().updateApplicationMenu(applicationMenuId, updater); } @Override public ApplicationMenu getApplicationMenu(final long applicationMenuId) throws ApplicationMenuNotFoundException { return getApplicationMenuAPIDelegate().getApplicationMenu(applicationMenuId); } @Override public void deleteApplicationMenu(final long applicationMenuId) throws DeletionException { getApplicationMenuAPIDelegate().deleteApplicationMenu(applicationMenuId); } @Override public SearchResult searchApplicationMenus(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ApplicationService applicationService = serviceAccessor.getApplicationService(); final ApplicationMenuModelConverter converter = new ApplicationMenuModelConverter(); final SearchApplicationMenuDescriptor searchDescriptor = serviceAccessor.getSearchEntitiesDescriptor() .getSearchApplicationMenuDescriptor(); final SearchApplicationMenus searchApplicationMenus = new SearchApplicationMenus(applicationService, converter, searchDescriptor, searchOptions); return getApplicationMenuAPIDelegate().searchApplicationMenus(searchApplicationMenus); } @Override public List getAllPagesForProfile(final long profileId) { return getApplicationPageAPIDelegate().getAllPagesForProfile(profileId); } @Override public List getAllPagesForProfile(String profile) { return getApplicationPageAPIDelegate().getAllPagesForProfile(profile); } @Override public byte[] exportApplications(final long... applicationIds) throws ExportException { return getLivingApplicationExporterDelegate().exportApplications(applicationIds); } /** * {@inheritDoc} */ @Override @Deprecated(since = "9.0.0") public List importApplications(final byte[] xmlContent, final ApplicationImportPolicy policy) throws ImportException, AlreadyExistsException { return getServiceAccessor().getApplicationImporter().importApplications(xmlContent, null, null, SessionInfos.getUserIdFromSession(), new StrategySelector().selectStrategy(policy)); } @Override public Icon getIconOfApplication(long applicationId) throws ApplicationNotFoundException { return getLivingApplicationAPIDelegate().getIconOfApplication(applicationId); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/AvailableInMaintenanceMode.java ================================================ /** * 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.api.impl; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Used to identify API methods that can be called if the maintenance mode is enabled. All other API method * calls will be rejected. * Used by the Bonita Engine server interceptor. * * @author Emmanuel Duchastenier * @author Matthieu Chaffotte * @author Anthony Birembaut */ @Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface AvailableInMaintenanceMode { boolean onlyAvailableInMaintenanceMode() default false; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/AvailableOnStoppedNode.java ================================================ /** * 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.api.impl; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Used to identify API methods that can be called if a Node is not started. All other API method calls will be * rejected. * Used by the Bonita Engine server interceptor. * * @author Emmanuel Duchastenier */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AvailableOnStoppedNode { } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/BusinessDataAPIImpl.java ================================================ /** * 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.api.impl; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.api.BusinessDataAPI; import org.bonitasoft.engine.bpm.data.DataNotFoundException; import org.bonitasoft.engine.business.data.BusinessDataReference; import org.bonitasoft.engine.business.data.converter.BusinessDataModelConverter; import org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; /** * @author Elias Ricken de Medeiros */ public class BusinessDataAPIImpl implements BusinessDataAPI { protected ServiceAccessor getServiceAccessor() { return ServiceAccessorSingleton.getInstance(); } @Override public BusinessDataReference getProcessBusinessDataReference(final String businessDataName, final long processInstanceId) throws DataNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); try { final RefBusinessDataService refBusinessDataService = serviceAccessor.getRefBusinessDataService(); final SRefBusinessDataInstance sReference = refBusinessDataService.getRefBusinessDataInstance( businessDataName, processInstanceId); if (sReference instanceof SSimpleRefBusinessDataInstance) { return BusinessDataModelConverter .toSimpleBusinessDataReference((SSimpleRefBusinessDataInstance) sReference); } else { return BusinessDataModelConverter .toMultipleBusinessDataReference((SProcessMultiRefBusinessDataInstance) sReference); } } catch (final SRefBusinessDataInstanceNotFoundException srbdnfe) { throw new DataNotFoundException(srbdnfe); } catch (final SBonitaReadException sbre) { throw new RetrieveException(sbre); } } @Override public List getProcessBusinessDataReferences(final long processInstanceId, final int startIndex, final int maxResults) { final ServiceAccessor serviceAccessor = getServiceAccessor(); try { final RefBusinessDataService refBusinessDataService = serviceAccessor.getRefBusinessDataService(); final List sReferences = refBusinessDataService .getRefBusinessDataInstances(processInstanceId, startIndex, maxResults); final List references = new ArrayList(); for (final SRefBusinessDataInstance sReference : sReferences) { if (sReference instanceof SSimpleRefBusinessDataInstance) { references.add(BusinessDataModelConverter .toSimpleBusinessDataReference((SSimpleRefBusinessDataInstance) sReference)); } else { references.add(BusinessDataModelConverter .toMultipleBusinessDataReference((SProcessMultiRefBusinessDataInstance) sReference)); } } return references; } catch (final SBonitaReadException sbre) { throw new RetrieveException(sbre); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/CommandAPIImpl.java ================================================ /** * 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.api.impl; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.engine.api.CommandAPI; import org.bonitasoft.engine.api.impl.transaction.CustomTransactions; import org.bonitasoft.engine.api.impl.transaction.command.DeleteSCommand; import org.bonitasoft.engine.api.impl.transaction.command.GetCommands; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.classloader.ClassLoaderIdentifier; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.classloader.SClassLoaderException; import org.bonitasoft.engine.command.CommandCriterion; import org.bonitasoft.engine.command.CommandDescriptor; import org.bonitasoft.engine.command.CommandExecutionException; import org.bonitasoft.engine.command.CommandNotFoundException; import org.bonitasoft.engine.command.CommandParameterizationException; import org.bonitasoft.engine.command.CommandService; import org.bonitasoft.engine.command.CommandUpdater; import org.bonitasoft.engine.command.CommandUpdater.CommandField; import org.bonitasoft.engine.command.DependencyNotFoundException; import org.bonitasoft.engine.command.RuntimeCommand; import org.bonitasoft.engine.command.SCommandDeletionException; import org.bonitasoft.engine.command.SCommandExecutionException; import org.bonitasoft.engine.command.SCommandNotFoundException; import org.bonitasoft.engine.command.SCommandParameterizationException; import org.bonitasoft.engine.command.SCommandUpdateException; import org.bonitasoft.engine.command.model.SCommand; import org.bonitasoft.engine.command.model.SCommandCriterion; import org.bonitasoft.engine.command.model.SCommandUpdateBuilder; import org.bonitasoft.engine.command.model.SCommandUpdateBuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.dependency.DependencyService; import org.bonitasoft.engine.dependency.SDependencyAlreadyExistsException; import org.bonitasoft.engine.dependency.SDependencyException; import org.bonitasoft.engine.dependency.SDependencyNotFoundException; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.search.SearchCommands; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.engine.transaction.UserTransactionService; /** * @author Zhang Bole * @author Matthieu Chaffotte * @author Celine Souchet * @author Emmanuel Duchastenier * @author Laurent Vaills */ public class CommandAPIImpl implements CommandAPI { protected static ServiceAccessor getServiceAccessor() { return ServiceAccessorSingleton.getInstance(); } @Override public void addDependency(final String name, final byte[] jar) throws CreationException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DependencyService dependencyService = serviceAccessor.getDependencyService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); try { dependencyService.createMappedDependency(name, jar, name + ".jar", -1L, ScopeType.TENANT); classLoaderService .refreshClassLoaderAfterUpdate(ClassLoaderIdentifier.TENANT); } catch (final SDependencyAlreadyExistsException e) { throw new AlreadyExistsException(e); } catch (final SDependencyException | SClassLoaderException sbe) { throw new CreationException(sbe); } } @Override public void removeDependency(final String name) throws DependencyNotFoundException, DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DependencyService dependencyService = serviceAccessor.getDependencyService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); try { dependencyService.deleteDependency(name); classLoaderService .refreshClassLoaderAfterUpdate(ClassLoaderIdentifier.TENANT); } catch (final SDependencyNotFoundException e) { throw new DependencyNotFoundException(e); } catch (final SBonitaException e) { throw new DeletionException(e); } } @Override public CommandDescriptor register(final String name, final String description, final String implementation) throws CreationException { CommandDescriptor existingCommandDescriptor = null; try { existingCommandDescriptor = getCommand(name); } catch (final CommandNotFoundException notFoundE) { // Nothing to do : no command with that name exists. } if (existingCommandDescriptor != null) { throw new AlreadyExistsException("A command with name \"" + name + "\" already exists"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final CommandService commandService = serviceAccessor.getCommandService(); final SCommand sCommand = SCommand.builder() .name(name) .description(description) .implementation(implementation).isSystem(false).build(); try { commandService.create(sCommand); return ModelConvertor.toCommandDescriptor(sCommand); } catch (final SBonitaException sbe) { throw new CreationException(sbe); } } private RuntimeCommand fetchRuntimeCommand(final SCommandFetcher commandFetcher, final boolean transactionManagedManually) throws SCommandNotFoundException, SCommandParameterizationException { final ServiceAccessor serviceAccessor = getServiceAccessor(); try { final SCommand sCommand; if (transactionManagedManually) { sCommand = commandFetcher.fetchInTransaction(serviceAccessor.getUserTransactionService(), serviceAccessor.getCommandService()); } else { sCommand = commandFetcher.fetch(serviceAccessor.getCommandService()); } final String runtimeCommandClassName = sCommand.getImplementation(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); return (RuntimeCommand) contextClassLoader.loadClass(runtimeCommandClassName).newInstance(); } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException e) { throw new SCommandParameterizationException(e); } } @Override public Serializable execute(final String commandName, final Map parameters) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException { return execute(new SCommandFetcherByName(commandName), parameters); } @Override public Serializable execute(final long commandId, final Map parameters) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException { return execute(new SCommandFetcherById(commandId), parameters); } private Serializable execute(final SCommandFetcher commandFetcher, final Map parameters) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException { return executeCommand(commandFetcher, parameters, false); } @Override @CustomTransactions public Serializable executeWithUserTransactions(final String commandName, final Map parameters) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException { return executeWithUserTransactions(new SCommandFetcherByName(commandName), parameters); } @Override @CustomTransactions public Serializable executeWithUserTransactions(final long commandId, final Map parameters) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException { return executeWithUserTransactions(new SCommandFetcherById(commandId), parameters); } private Serializable executeWithUserTransactions(final SCommandFetcher commandFetcher, final Map parameters) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException { return executeCommand(commandFetcher, parameters, true); } private Serializable executeCommand(final SCommandFetcher commandFetcher, final Map parameters, final boolean transactionManagedManually) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); try { final RuntimeCommand runtimeCommand = fetchRuntimeCommand(commandFetcher, transactionManagedManually); return runtimeCommand.execute(parameters, serviceAccessor); } catch (final SCommandExecutionException scee) { throw new CommandExecutionException(scee); } catch (final SCommandParameterizationException scpe) { throw new CommandParameterizationException(scpe); } catch (final SCommandNotFoundException e) { throw new CommandNotFoundException(e); } } @Override public void unregister(final long commandId) throws CommandNotFoundException, DeletionException { final CommandService commandService = getServiceAccessor().getCommandService(); final DeleteSCommand deleteCommand = new DeleteSCommand(commandService, commandId); unregister(deleteCommand); } @Override public void unregister(final String name) throws CommandNotFoundException, DeletionException { if (name == null) { // FIXME: throw IllegalArgumentException instead, and make bonita interceptor catch all exceptions and wrap it into BonitaRuntimeException: throw new DeletionException("Command name can not be null!"); } final CommandService commandService = getServiceAccessor().getCommandService(); final DeleteSCommand deleteCommand = new DeleteSCommand(commandService, name); unregister(deleteCommand); } private void unregister(final DeleteSCommand deleteCommand) throws CommandNotFoundException, DeletionException { try { deleteCommand.execute(); } catch (final SCommandNotFoundException scnfe) { throw new CommandNotFoundException(scnfe); } catch (final SBonitaException sbe) { throw new DeletionException(sbe); } } @Override public void unregisterAll() throws DeletionException { final CommandService commandService = getServiceAccessor().getCommandService(); try { commandService.deleteAll(); } catch (final SCommandDeletionException sde) { throw new DeletionException(sde); } } @Override public CommandDescriptor get(final long commandId) throws CommandNotFoundException { return getCommand(new SCommandFetcherById(commandId)); } @Override public CommandDescriptor getCommand(final String commandName) throws CommandNotFoundException { return getCommand(new SCommandFetcherByName(commandName)); } private CommandDescriptor getCommand(final SCommandFetcher commandFetcher) throws CommandNotFoundException { final CommandService commandService = getServiceAccessor().getCommandService(); try { final SCommand sCommand = commandFetcher.fetch(commandService); return ModelConvertor.toCommandDescriptor(sCommand); } catch (final SBonitaException e) { throw new CommandNotFoundException(e); } } @Override public List getAllCommands(final int startIndex, final int maxResults, final CommandCriterion sort) { SCommandCriterion sCommandCriterion; if (CommandCriterion.NAME_ASC.equals(sort)) { sCommandCriterion = SCommandCriterion.NAME_ASC; } else { sCommandCriterion = SCommandCriterion.NAME_DESC; } final CommandService commandService = getServiceAccessor().getCommandService(); try { final List commands = commandService.getAllCommands(startIndex, maxResults, sCommandCriterion); return ModelConvertor.toCommandDescriptors(commands); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public void update(final long commandId, final CommandUpdater updateDescriptor) throws UpdateException { update(new SCommandFetcherById(commandId), updateDescriptor); } @Override public void update(final String commandName, final CommandUpdater updateDescriptor) throws UpdateException { update(new SCommandFetcherByName(commandName), updateDescriptor); } private void update(final SCommandFetcher commandFetcher, final CommandUpdater updateDescriptor) throws UpdateException { if (updateDescriptor == null || updateDescriptor.getFields().isEmpty()) { throw new UpdateException("The update descriptor does not contain field updates"); } final SCommandUpdateBuilderFactory fact = BuilderFactory.get(SCommandUpdateBuilderFactory.class); final SCommandUpdateBuilder commandUpdateBuilder = fact.createNewInstance(); final CommandService commandService = getServiceAccessor().getCommandService(); try { final EntityUpdateDescriptor changeDescriptor = getCommandUpdateDescriptor(updateDescriptor, commandUpdateBuilder); final SCommand sCommand = commandFetcher.fetch(commandService); commandService.update(sCommand, changeDescriptor); } catch (final SCommandNotFoundException | SCommandUpdateException e) { throw new UpdateException(e); } } private EntityUpdateDescriptor getCommandUpdateDescriptor(final CommandUpdater updateDescriptor, final SCommandUpdateBuilder commandUpdateBuilder) { final Map fields = updateDescriptor.getFields(); for (final Entry field : fields.entrySet()) { final String value = (String) field.getValue(); switch (field.getKey()) { case NAME: commandUpdateBuilder.updateName(value); break; case DESCRIPTION: commandUpdateBuilder.updateDescription(value); break; default: throw new IllegalStateException(); } } return commandUpdateBuilder.done(); } @Override public List getUserCommands(final int startIndex, final int maxResults, final CommandCriterion sort) { final CommandService commandService = getServiceAccessor().getCommandService(); try { final GetCommands getCommands = new GetCommands(commandService, startIndex, maxResults, sort); getCommands.execute(); return ModelConvertor.toCommandDescriptors(getCommands.getResult()); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public SearchResult searchCommands(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final CommandService commandService = serviceAccessor.getCommandService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchCommands searchCommands = new SearchCommands(commandService, searchEntitiesDescriptor.getSearchCommandDescriptor(), searchOptions); try { searchCommands.execute(); return searchCommands.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } // Utility classes to factorize how we fetch a RuntimeCommand private abstract static class SCommandFetcher { abstract SCommand fetch(final CommandService commandService) throws SCommandNotFoundException; SCommand fetchInTransaction(final UserTransactionService userTransactionService, final CommandService commandService) throws SCommandNotFoundException { try { return userTransactionService.executeInTransaction(() -> fetch(commandService)); } catch (final Exception e) { throw new SCommandNotFoundException(e); } } } private static class SCommandFetcherByName extends SCommandFetcher { private final String commandName; public SCommandFetcherByName(final String commandName) { this.commandName = commandName; } @Override SCommand fetch(final CommandService commandService) throws SCommandNotFoundException { return commandService.get(commandName); } } private static class SCommandFetcherById extends SCommandFetcher { private final long commandId; public SCommandFetcherById(final long commandId) { this.commandId = commandId; } @Override SCommand fetch(final CommandService commandService) throws SCommandNotFoundException { return commandService.get(commandId); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/CustomUserInfoAPIDelegate.java ================================================ /** * 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.api.impl; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.identity.CustomUserInfo; import org.bonitasoft.engine.identity.CustomUserInfoValue; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SIdentityException; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.model.SCustomUserInfoValue; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Vincent Elcrin */ public class CustomUserInfoAPIDelegate { private final IdentityService service; public CustomUserInfoAPIDelegate(final IdentityService service) { this.service = service; } public List list(final long userId, final int startIndex, final int maxResult) throws SIdentityException, SBonitaReadException { List definitions = service.getCustomUserInfoDefinitions(startIndex, maxResult); if (definitions.size() == 0) { return Collections.emptyList(); } Map values = transform(searchCorrespondingValues(userId, definitions)); List info = new ArrayList(); for (SCustomUserInfoDefinition definition : definitions) { info.add(new CustomUserInfo(userId, ModelConvertor.convert(definition), values.get(definition.getId()))); } return info; } private Map transform(final List values) { Map map = new HashMap(); for (SCustomUserInfoValue value : values) { map.put(value.getDefinitionId(), ModelConvertor.convert(value)); } return map; } private List searchCorrespondingValues(final long userId, final List definitions) throws SBonitaReadException { return service.getCustomUserInfoValueOfUserAndDefinitions(userId, getIds(definitions)); } private List getIds(final List definitions) { List ids = new ArrayList(definitions.size()); for (SCustomUserInfoDefinition definition : definitions) { ids.add(definition.getId()); } return ids; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/CustomUserInfoDefinitionAPIDelegate.java ================================================ /** * 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.api.impl; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.identity.CustomUserInfoDefinition; import org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SCustomUserInfoDefinitionAlreadyExistsException; import org.bonitasoft.engine.identity.SIdentityException; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Vincent Elcrin * @author Elias Ricken de Medeiros */ public class CustomUserInfoDefinitionAPIDelegate { private static final int MAX_NAME_LENGHT = 75; private final IdentityService service; public CustomUserInfoDefinitionAPIDelegate(final IdentityService service) { this.service = service; } public CustomUserInfoDefinition create(final CustomUserInfoDefinitionCreator creator) throws CreationException { checkParameter(creator); final SCustomUserInfoDefinition.SCustomUserInfoDefinitionBuilder builder = SCustomUserInfoDefinition.builder(); builder.name(creator.getName()); builder.description(creator.getDescription()); try { return ModelConvertor.convert(service.createCustomUserInfoDefinition(builder.build())); } catch (SCustomUserInfoDefinitionAlreadyExistsException e) { throw new AlreadyExistsException(e.getMessage()); } catch (SIdentityException e) { throw new CreationException(e); } } private void checkParameter(final CustomUserInfoDefinitionCreator creator) throws CreationException { if (creator == null) { throw new CreationException("Can not create null custom user details."); } if (creator.getName() == null || creator.getName().trim().isEmpty()) { throw new CreationException("The definition name cannot be null or empty."); } if (creator.getName().length() > MAX_NAME_LENGHT) { throw new CreationException("The definition name cannot be longer then 75 characters."); } } public void delete(final long id) throws DeletionException { try { service.deleteCustomUserInfoDefinition(id); } catch (SIdentityException e) { throw new DeletionException(e); } } public List list(final int startIndex, final int maxResult) { try { List definitions = new ArrayList(); for (SCustomUserInfoDefinition sDefinition : service.getCustomUserInfoDefinitions(startIndex, maxResult)) { definitions.add(ModelConvertor.convert(sDefinition)); } return definitions; } catch (SIdentityException e) { throw new RetrieveException(e); } } public long count() { try { return service.getNumberOfCustomUserInfoDefinition(); } catch (SIdentityException e) { throw new RetrieveException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/DocumentAPIImpl.java ================================================ /** * 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.api.impl; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.api.DocumentAPI; import org.bonitasoft.engine.api.impl.transaction.document.GetDocumentByNameAtProcessInstantiation; import org.bonitasoft.engine.bpm.document.*; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.api.impl.DocumentHelper; import org.bonitasoft.engine.core.document.model.AbstractSMappedDocument; import org.bonitasoft.engine.core.document.model.SDocument; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.core.document.model.builder.SDocumentBuilderFactory; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance; import org.bonitasoft.engine.exception.*; import org.bonitasoft.engine.persistence.OrderAndField; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; import org.bonitasoft.engine.search.document.SearchArchivedDocuments; import org.bonitasoft.engine.search.document.SearchArchivedDocumentsSupervisedBy; import org.bonitasoft.engine.search.document.SearchDocuments; import org.bonitasoft.engine.search.document.SearchDocumentsSupervisedBy; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; /** * @author Baptiste Mesta */ public class DocumentAPIImpl implements DocumentAPI { public DocumentAPIImpl() { } @Override public Document attachDocument(final long processInstanceId, final String documentName, final String fileName, final String mimeType, final String url) throws DocumentAttachmentException { final DocumentValue documentValue = new DocumentValue(url); documentValue.setFileName(fileName); documentValue.setMimeType(mimeType); try { return addDocument(processInstanceId, documentName, null, documentValue); } catch (final BonitaException e) { throw new DocumentAttachmentException(e); } } /* * If the target document is a list of document then we append it to the list * If the target document is a list of document and the index is set on the document value then we insert the * element in the list at the specified index * If the target single document or is non existent in the definition we create it * If the target single document and is already existent an exception is thrown */ @Override public Document addDocument(final long processInstanceId, final String documentName, final String description, final DocumentValue documentValue) throws DocumentAttachmentException, AlreadyExistsException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DocumentService documentService = serviceAccessor.getDocumentService(); final DocumentHelper documentHelper = new DocumentHelper(documentService, serviceAccessor.getProcessDefinitionService(), serviceAccessor.getProcessInstanceService()); final SDocument sDocument = buildSDocument(documentValue); int index = documentValue.getIndex(); try { if (documentHelper.isListDefinedInDefinition(documentName, processInstanceId)) { final List allDocumentOfTheList = documentHelper .getAllDocumentOfTheList(processInstanceId, documentName); if (index == -1) { index = allDocumentOfTheList.size(); } else { if (index > allDocumentOfTheList.size()) { throw new DocumentAttachmentException( "Can't attach a document on the list " + documentName + " on process instance " + processInstanceId + " the index is out of range, list size is " + allDocumentOfTheList.size()); } for (int i = index; i < allDocumentOfTheList.size(); i++) { documentService.updateDocumentIndex(allDocumentOfTheList.get(i), i + 1); } } } else { if (index >= 0) { throw new DocumentAttachmentException( "Unable to add a document with an index if it is a single document"); } } final SMappedDocument mappedDocument = documentService.attachDocumentToProcessInstance(sDocument, processInstanceId, documentName, description, index); return ModelConvertor.toDocument(mappedDocument, documentService); } catch (final SObjectAlreadyExistsException e) { throw new AlreadyExistsException(e.getMessage()); } catch (final SBonitaException e) { throw new DocumentAttachmentException(e); } } @Override public Document updateDocument(final long documentId, final DocumentValue documentValue) throws DocumentAttachmentException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DocumentService documentService = serviceAccessor.getDocumentService(); try { final SMappedDocument document = documentService.updateDocument(documentId, buildSDocument(documentValue)); return ModelConvertor.toDocument(document, documentService); } catch (final SBonitaException e) { throw new DocumentAttachmentException(e); } } private SDocument buildSDocument(final DocumentValue documentValue) { if (documentValue.hasContent()) { return buildProcessDocument(documentValue.getFileName(), documentValue.getMimeType(), getUserId(), documentValue.getContent()); } return buildExternalProcessDocumentReference(documentValue.getFileName(), documentValue.getMimeType(), getUserId(), documentValue.getUrl()); } private long getUserId() { return APIUtils.getUserId(); } private SDocument buildExternalProcessDocumentReference(final String fileName, final String mimeType, final long authorId, final String url) { return new SDocumentBuilderFactory() .createNewExternalProcessDocumentReference(fileName, mimeType, authorId, url).done(); } private SDocument buildProcessDocument(final String fileName, final String mimeType, final long authorId, final byte[] content) { return new SDocumentBuilderFactory().createNewProcessDocument(fileName, mimeType, authorId, content).done(); } @Override public Document attachDocument(final long processInstanceId, final String documentName, final String fileName, final String mimeType, final byte[] documentContent) throws DocumentAttachmentException { final DocumentValue documentValue = new DocumentValue(documentContent, mimeType, fileName); try { return addDocument(processInstanceId, documentName, null, documentValue); } catch (final BonitaException e) { throw new DocumentAttachmentException(e); } } ServiceAccessor getServiceAccessor() { return ServiceAccessorSingleton.getInstance(); } @Override public Document attachNewDocumentVersion(final long processInstanceId, final String documentName, final String fileName, final String mimeType, final String url) throws DocumentAttachmentException { final DocumentService documentService = getServiceAccessor().getDocumentService(); try { return ModelConvertor.toDocument( documentService.updateDocument(documentService.getMappedDocument(processInstanceId, documentName), buildExternalProcessDocumentReference(fileName, mimeType, getUserId(), url)), documentService); } catch (final Exception e) { throw new DocumentAttachmentException(e); } } @Override public Document attachNewDocumentVersion(final long processInstanceId, final String documentName, final String contentFileName, final String contentMimeType, final byte[] documentContent) throws DocumentAttachmentException { final DocumentService documentService = getServiceAccessor().getDocumentService(); try { return ModelConvertor.toDocument( documentService.updateDocument(documentService.getMappedDocument(processInstanceId, documentName), buildProcessDocument(contentFileName, contentMimeType, getUserId(), documentContent)), documentService); } catch (final Exception e) { throw new DocumentAttachmentException(e); } } @Override public Document getDocument(final long documentId) throws DocumentNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DocumentService documentService = serviceAccessor.getDocumentService(); try { return ModelConvertor.toDocument(documentService.getMappedDocument(documentId), documentService); } catch (final SObjectNotFoundException | SBonitaReadException e) { throw new DocumentNotFoundException(e); } } @Override public List getLastVersionOfDocuments(final long processInstanceId, final int pageIndex, final int numberPerPage, final DocumentCriterion pagingCriterion) throws DocumentException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DocumentService documentService = serviceAccessor.getDocumentService(); final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForDocument(pagingCriterion); try { final List mappedDocuments = documentService.getDocumentsOfProcessInstance( processInstanceId, pageIndex, numberPerPage, orderAndField.getField(), orderAndField.getOrder()); if (mappedDocuments != null && !mappedDocuments.isEmpty()) { final List result = new ArrayList<>(mappedDocuments.size()); for (final SMappedDocument mappedDocument : mappedDocuments) { result.add(ModelConvertor.toDocument(mappedDocument, documentService)); } return result; } return Collections.emptyList(); } catch (final SBonitaReadException e) { throw new DocumentException(e); } } @Override public byte[] getDocumentContent(final String documentStorageId) throws DocumentNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DocumentService documentService = serviceAccessor.getDocumentService(); try { return documentService.getDocumentContent(documentStorageId); } catch (final SObjectNotFoundException sbe) { throw new DocumentNotFoundException(sbe); } } @Override public Document getLastDocument(final long processInstanceId, final String documentName) throws DocumentNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DocumentService documentService = serviceAccessor.getDocumentService(); try { return ModelConvertor.toDocument(documentService.getMappedDocument(processInstanceId, documentName), documentService); } catch (final SObjectNotFoundException | SBonitaReadException sbe) { throw new DocumentNotFoundException(sbe); } } @Override public long getNumberOfDocuments(final long processInstanceId) throws DocumentException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DocumentService documentService = serviceAccessor.getDocumentService(); try { return documentService.getNumberOfDocumentsOfProcessInstance(processInstanceId); } catch (final SBonitaReadException e) { throw new DocumentException(e); } } @Override public Document getDocumentAtProcessInstantiation(final long processInstanceId, final String documentName) throws DocumentNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DocumentService documentService = serviceAccessor.getDocumentService(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); try { final GetDocumentByNameAtProcessInstantiation transactionContent = new GetDocumentByNameAtProcessInstantiation( documentService, processInstanceService, serviceAccessor.getProcessDefinitionService(), searchEntitiesDescriptor, processInstanceId, documentName); transactionContent.execute(); final AbstractSMappedDocument attachment = transactionContent.getResult(); return ModelConvertor.toDocument(attachment, documentService); } catch (final SBonitaException sbe) { throw new DocumentNotFoundException(sbe); } } @Override public Document getDocumentAtActivityInstanceCompletion(final long activityInstanceId, final String documentName) throws DocumentNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DocumentService documentService = serviceAccessor.getDocumentService(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); try { final SAActivityInstance instance = activityInstanceService .getMostRecentArchivedActivityInstance(activityInstanceId); final AbstractSMappedDocument document = documentService.getMappedDocument(instance.getRootContainerId(), documentName, instance.getArchiveDate()); return ModelConvertor.toDocument(document, documentService); } catch (final SBonitaException sbe) { throw new DocumentNotFoundException(sbe); } } @Override public SearchResult searchDocuments(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final DocumentService documentService = serviceAccessor.getDocumentService(); final SearchDocuments searchDocuments = new SearchDocuments(documentService, searchEntitiesDescriptor.getSearchDocumentDescriptor(), searchOptions); try { searchDocuments.execute(); } catch (final SBonitaException e) { throw new SearchException(e); } return searchDocuments.getResult(); } @Override public SearchResult searchDocumentsSupervisedBy(final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final DocumentService documentService = serviceAccessor.getDocumentService(); final SearchDocumentsSupervisedBy searchDocuments = new SearchDocumentsSupervisedBy(documentService, searchEntitiesDescriptor.getSearchDocumentDescriptor(), searchOptions, userId); try { searchDocuments.execute(); } catch (final SBonitaException e) { throw new SearchException(e); } return searchDocuments.getResult(); } @Override public SearchResult searchArchivedDocuments(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final DocumentService documentService = serviceAccessor.getDocumentService(); final SearchArchivedDocuments searchDocuments = new SearchArchivedDocuments(documentService, searchEntitiesDescriptor.getSearchArchivedDocumentDescriptor(), searchOptions); try { searchDocuments.execute(); } catch (final SBonitaException e) { throw new SearchException(e); } return searchDocuments.getResult(); } @Override public SearchResult searchArchivedDocumentsSupervisedBy(final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final DocumentService documentService = serviceAccessor.getDocumentService(); final SearchArchivedDocumentsSupervisedBy searchDocuments = new SearchArchivedDocumentsSupervisedBy(userId, documentService, searchEntitiesDescriptor.getSearchArchivedDocumentDescriptor(), searchOptions); try { searchDocuments.execute(); } catch (final SBonitaException e) { throw new SearchException(e); } return searchDocuments.getResult(); } @Override public ArchivedDocument getArchivedVersionOfProcessDocument(final long sourceObjectId) throws ArchivedDocumentNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DocumentService documentService = serviceAccessor.getDocumentService(); try { return ModelConvertor.toArchivedDocument( documentService.getArchivedVersionOfProcessDocument(sourceObjectId), documentService); } catch (final SObjectNotFoundException e) { throw new ArchivedDocumentNotFoundException(e); } } @Override public ArchivedDocument getArchivedProcessDocument(final long archivedProcessDocumentId) throws ArchivedDocumentNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DocumentService documentService = serviceAccessor.getDocumentService(); try { return ModelConvertor.toArchivedDocument(documentService.getArchivedDocument(archivedProcessDocumentId), documentService); } catch (final SObjectNotFoundException e) { throw new ArchivedDocumentNotFoundException(e); } } @Override public Document removeDocument(final long documentId) throws DocumentNotFoundException, DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DocumentService documentService = serviceAccessor.getDocumentService(); final DocumentHelper documentHelper = new DocumentHelper(documentService, serviceAccessor.getProcessDefinitionService(), serviceAccessor.getProcessInstanceService()); try { final SMappedDocument document = documentService.getMappedDocument(documentId); final int index = document.getIndex(); if (index != -1) { //document is in list final List allDocumentOfTheList = documentHelper .getAllDocumentOfTheList(document.getProcessInstanceId(), document.getName()); for (int i = index + 1; i < allDocumentOfTheList.size(); i++) { documentService.updateDocumentIndex(allDocumentOfTheList.get(i), i - 1); } } // archive and delete mapping on the process but keep the content of the document itself documentService.removeCurrentVersion(document); return ModelConvertor.toDocument(document, documentService); } catch (final SObjectNotFoundException e) { throw new DocumentNotFoundException( "Unable to delete the document " + documentId + " because it does not exists", e); } catch (final SBonitaException e) { throw new DeletionException("Unable to delete the document " + documentId, e); } } @Override public List getDocumentList(final long processInstanceId, final String name, final int fromIndex, final int numberOfResult) throws DocumentNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DocumentService documentService = serviceAccessor.getDocumentService(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final DocumentHelper documentHelper = new DocumentHelper(documentService, processDefinitionService, processInstanceService); try { final List documentList = documentService.getDocumentList(name, processInstanceId, fromIndex, numberOfResult); if (documentList.isEmpty() && !documentHelper.isListDefinedInDefinition(name, processInstanceId)) { throw new DocumentNotFoundException("doc not found"); } return ModelConvertor.toDocuments(new ArrayList<>(documentList), documentService); } catch (final org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException e) { throw new DocumentNotFoundException(e); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } } @Override public void setDocumentList(final long processInstanceId, final String name, final List documentsValues) throws DocumentException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DocumentHelper documentHelper = new DocumentHelper(serviceAccessor.getDocumentService(), serviceAccessor.getProcessDefinitionService(), serviceAccessor.getProcessInstanceService()); try { documentHelper.setDocumentList(documentsValues, name, processInstanceId, getUserId()); } catch (final SBonitaException e) { throw new DocumentException("Unable to set the list " + name + " on process instance " + processInstanceId, e); } } @Override public void deleteContentOfArchivedDocument(final long archivedDocumentId) throws DocumentException, DocumentNotFoundException { final ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance(); final DocumentService documentService = serviceAccessor.getDocumentService(); try { documentService.deleteContentOfArchivedDocument(archivedDocumentId); } catch (final SObjectNotFoundException e) { throw new DocumentNotFoundException("The document with id " + archivedDocumentId + " could not be found", e); } catch (final SBonitaException e) { throw new DocumentException("Unable to delete content of all version of the document " + archivedDocumentId, e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/IdentityAPIImpl.java ================================================ /** * 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.api.impl; import java.io.Serializable; import java.util.*; import java.util.Map.Entry; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.impl.organization.OrganizationAPIDelegate; import org.bonitasoft.engine.api.impl.transaction.actor.GetActor; import org.bonitasoft.engine.api.impl.transaction.identity.*; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.NullCheckingUtil; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.exception.*; import org.bonitasoft.engine.identity.*; import org.bonitasoft.engine.identity.ContactDataUpdater.ContactDataField; import org.bonitasoft.engine.identity.GroupUpdater.GroupField; import org.bonitasoft.engine.identity.RoleUpdater.RoleField; import org.bonitasoft.engine.identity.UserUpdater.UserField; import org.bonitasoft.engine.identity.impl.UserWithContactDataImpl; import org.bonitasoft.engine.identity.model.*; import org.bonitasoft.engine.identity.model.builder.*; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.profile.ProfileService; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; import org.bonitasoft.engine.search.identity.SearchGroups; import org.bonitasoft.engine.search.identity.SearchRoles; import org.bonitasoft.engine.search.identity.SearchUsers; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; /** * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Feng Hui * @author Zhang Bole * @author Yanyan Liu * @author Lu Kai * @author Hongwen Zang * @author Celine Souchet */ @AvailableInMaintenanceMode @Slf4j public class IdentityAPIImpl implements IdentityAPI { protected ServiceAccessor getServiceAccessor() { try { return ServiceAccessorFactory.getInstance().createServiceAccessor(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } protected OrganizationAPIDelegate getOrganizationAPIDelegate() { return OrganizationAPIDelegate.getInstance(); } @Override public User createUser(final String userName, final String password) throws CreationException { final UserCreator creator = new UserCreator(userName, password); creator.setEnabled(true); return createUser(creator); } @Override public User createUser(final String userName, final String password, final String firstName, final String lastName) throws CreationException { final UserCreator creator = new UserCreator(userName, password); creator.setFirstName(firstName).setLastName(lastName); creator.setEnabled(true); return createUser(creator); } @Override public User createUser(final UserCreator creator) throws CreationException { validateUserCreator(creator); final ServiceAccessor serviceAccessor = getServiceAccessor(); IdentityService identityService = serviceAccessor.getIdentityService(); if (creator.getFields().containsKey(UserCreator.UserField.ICON_NAME) || creator.getFields().containsKey(UserCreator.UserField.ICON_PATH)) { log.warn("setIconName and setIconPath are deprecated, use setIcon instead"); } final SUser sUser = ModelConvertor.constructSUser(creator); final SContactInfo personalContactInfo = ModelConvertor.constructSUserContactInfo(creator, sUser.getId(), true); final SContactInfo proContactInfo = ModelConvertor.constructSUserContactInfo(creator, sUser.getId(), false); try { SUser user = identityService.createUser(sUser, personalContactInfo, proContactInfo, (String) creator.getFields().get(UserCreator.UserField.ICON_FILENAME), (byte[]) creator.getFields().get(UserCreator.UserField.ICON_CONTENT)); return ModelConvertor.toUser(user); } catch (final SBonitaException sbe) { throw new CreationException(sbe); } } private void validateUserCreator(UserCreator creator) throws CreationException { if (creator == null) { throw new CreationException("Can not create a null user."); } final Map fields = creator.getFields(); final String userName = (String) fields.get(UserCreator.UserField.NAME); if (userName == null || userName.trim().isEmpty()) { throw new CreationException("The user name cannot be null or empty."); } final String password = (String) fields.get(UserCreator.UserField.PASSWORD); if (password == null || password.trim().isEmpty()) { throw new CreationException("The password cannot be null or empty."); } try { getUserByUserName(userName); throw new AlreadyExistsException("A user with name \"" + userName + "\" already exists"); } catch (final UserNotFoundException ignored) { } } @Override public User updateUser(final long userId, final UserUpdater updater) throws UserNotFoundException, UpdateException { if (updater == null || !updater.hasFields()) { throw new UpdateException("The update descriptor does not contain field updates"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); // User change final EntityUpdateDescriptor userUpdateDescriptor = getUserUpdateDescriptor(updater); // Personal data change final EntityUpdateDescriptor personalDataUpdateDescriptor = getUserContactInfoUpdateDescriptor( updater.getPersoContactUpdater()); // Professional data change final EntityUpdateDescriptor professionalDataUpdateDescriptor = getUserContactInfoUpdateDescriptor( updater.getProContactUpdater()); final EntityUpdateDescriptor iconUpdater = getIconUpdater(updater); try { SUser sUser = identityService.updateUser(userId, userUpdateDescriptor, personalDataUpdateDescriptor, professionalDataUpdateDescriptor, iconUpdater); return ModelConvertor.toUser(sUser); } catch (final SUserNotFoundException e) { throw new UserNotFoundException(e); } catch (final SBonitaException e) { throw new UpdateException(e); } } private EntityUpdateDescriptor getIconUpdater(UserUpdater updater) { EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor(); if (updater.getFields().containsKey(UserField.ICON_CONTENT)) { entityUpdateDescriptor.addField("filename", updater.getFields().get(UserField.ICON_FILENAME)); entityUpdateDescriptor.addField("content", updater.getFields().get(UserField.ICON_CONTENT)); } return entityUpdateDescriptor; } private EntityUpdateDescriptor getIconUpdater(GroupUpdater updater) { EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor(); if (updater.getFields().containsKey(GroupField.ICON_CONTENT)) { entityUpdateDescriptor.addField("filename", updater.getFields().get(GroupField.ICON_FILENAME)); entityUpdateDescriptor.addField("content", updater.getFields().get(GroupField.ICON_CONTENT)); } return entityUpdateDescriptor; } private EntityUpdateDescriptor getIconUpdater(RoleUpdater updater) { EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor(); if (updater.getFields().containsKey(RoleUpdater.RoleField.ICON_CONTENT)) { entityUpdateDescriptor.addField("filename", updater.getFields().get(RoleUpdater.RoleField.ICON_FILENAME)); entityUpdateDescriptor.addField("content", updater.getFields().get(RoleUpdater.RoleField.ICON_CONTENT)); } return entityUpdateDescriptor; } private EntityUpdateDescriptor getUserUpdateDescriptor(final UserUpdater updateDescriptor) { final SUserUpdateBuilder userUpdateBuilder = BuilderFactory.get(SUserUpdateBuilderFactory.class) .createNewInstance(); if (updateDescriptor != null) { final Map fields = updateDescriptor.getFields(); for (final Entry field : fields.entrySet()) { switch (field.getKey()) { case USER_NAME: userUpdateBuilder.updateUserName((String) field.getValue()); break; case PASSWORD: userUpdateBuilder.updatePassword((String) field.getValue()); break; case FIRST_NAME: userUpdateBuilder.updateFirstName((String) field.getValue()); break; case LAST_NAME: userUpdateBuilder.updateLastName((String) field.getValue()); break; case MANAGER_ID: userUpdateBuilder.updateManagerUserId((Long) field.getValue()); break; case ICON_NAME: log.warn("setIconName is deprecated, use setIcon instead"); break; case ICON_PATH: log.warn("setIconPath is deprecated, use setIcon instead"); break; case TITLE: userUpdateBuilder.updateTitle((String) field.getValue()); break; case JOB_TITLE: userUpdateBuilder.updateJobTitle((String) field.getValue()); break; case ENABLED: userUpdateBuilder.updateEnabled((Boolean) field.getValue()); break; case ICON_FILENAME: case ICON_CONTENT: break; default: throw new IllegalStateException(); } } userUpdateBuilder.updateLastUpdate(System.currentTimeMillis()); return userUpdateBuilder.done(); } return null; } private EntityUpdateDescriptor getUserContactInfoUpdateDescriptor(final ContactDataUpdater updater) { final SContactInfoUpdateBuilder updateBuilder = BuilderFactory.get(SContactInfoUpdateBuilderFactory.class) .createNewInstance(); if (updater != null) { final Map fields = updater.getFields(); for (final Entry field : fields.entrySet()) { switch (field.getKey()) { case EMAIL: updateBuilder.updateEmail((String) field.getValue()); break; case PHONE: updateBuilder.updatePhoneNumber((String) field.getValue()); break; case MOBILE: updateBuilder.updateMobileNumber((String) field.getValue()); break; case FAX: updateBuilder.updateFaxNumber((String) field.getValue()); break; case BUILDING: updateBuilder.updateBuilding((String) field.getValue()); break; case ROOM: updateBuilder.updateRoom((String) field.getValue()); break; case ADDRESS: updateBuilder.updateAddress((String) field.getValue()); break; case ZIP_CODE: updateBuilder.updateZipCode((String) field.getValue()); break; case CITY: updateBuilder.updateCity((String) field.getValue()); break; case STATE: updateBuilder.updateState((String) field.getValue()); break; case COUNTRY: updateBuilder.updateCountry((String) field.getValue()); break; case WEBSITE: updateBuilder.updateWebsite((String) field.getValue()); break; default: throw new IllegalStateException(); } } return updateBuilder.done(); } return null; } @Override public void deleteUser(final long userId) throws DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final ProfileService profileService = serviceAccessor.getProfileService(); try { final DeleteUser deleteUser = new DeleteUser(identityService, actorMappingService, profileService, userId); deleteUser.execute(); final Set removedActorIds = deleteUser.getRemovedActorIds(); updateActorProcessDependencies(serviceAccessor, actorMappingService, removedActorIds); } catch (final SBonitaException sbe) { throw new DeletionException(sbe); } } @Override public void deleteUser(final String userName) throws DeletionException { if (userName == null) { throw new DeletionException("User name can not be null!"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final ProfileService profileService = serviceAccessor.getProfileService(); try { final DeleteUser deleteUser = new DeleteUser(identityService, actorMappingService, profileService, userName); deleteUser.execute(); final Set removedActorIds = deleteUser.getRemovedActorIds(); updateActorProcessDependencies(serviceAccessor, actorMappingService, removedActorIds); } catch (final SBonitaException sbe) { throw new DeletionException(sbe); } } @Override public void deleteUsers(final List userIds) throws DeletionException { if (userIds != null && !userIds.isEmpty()) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final ProfileService profileService = serviceAccessor.getProfileService(); try { final DeleteUsers deleteUsers = new DeleteUsers(identityService, actorMappingService, profileService, userIds); deleteUsers.execute(); } catch (final SBonitaException sbe) { throw new DeletionException(sbe); } } } @Override public User getUser(final long userId) throws UserNotFoundException { if (userId == -1) { throw new UserNotFoundException("The technical user is not a usable user"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { final GetSUser transactionContent = new GetSUser(identityService, userId); transactionContent.execute(); return ModelConvertor.toUser(transactionContent.getResult()); } catch (final SUserNotFoundException sunfe) { throw new UserNotFoundException(sunfe); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public User getUserByUserName(final String userName) throws UserNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { final GetSUser transactionContent = new GetSUser(identityService, userName); transactionContent.execute(); return ModelConvertor.toUser(transactionContent.getResult()); } catch (final SUserNotFoundException sunfe) { throw new UserNotFoundException(sunfe); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public UserWithContactData getUserWithProfessionalDetails(final long userId) throws UserNotFoundException { final User user = getUser(userId); final ContactData contactData = getUserContactData(userId, false); return new UserWithContactDataImpl(user, contactData); } @Override public ContactData getUserContactData(final long userId, final boolean personal) throws UserNotFoundException { if (userId == -1) { throw new UserNotFoundException("The technical user is not a usable user"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { final GetSContactInfo txContent = new GetSContactInfo(userId, identityService, personal); txContent.execute(); final SContactInfo result = txContent.getResult(); if (result == null) { return null; } return ModelConvertor.toUserContactData(result); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public long getNumberOfUsers() { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { final GetNumberOfInstance transactionContent = new GetNumberOfInstance("getNumberOfUsers", identityService); transactionContent.execute(); return transactionContent.getResult(); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override // FIXME rewrite ME!!! public List getUsers(final int startIndex, final int maxResults, final UserCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); try { final IdentityService identityService = serviceAccessor.getIdentityService(); return getUsersWithOrder(startIndex, maxResults, criterion, identityService); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public Map getUsers(final List userIds) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final Map users = new HashMap<>(); try { final List sUsers = identityService.getUsers(userIds); for (final SUser sUser : sUsers) { users.put(sUser.getId(), ModelConvertor.toUser(sUser)); } return users; } catch (final SUserNotFoundException sunfe) { throw new RetrieveException(sunfe); } } @Override public Map getUsersByUsernames(final List userNames) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final Map users = new HashMap<>(); try { final List sUsers = identityService.getUsersByUsername(userNames); for (final SUser sUser : sUsers) { users.put(sUser.getUserName(), ModelConvertor.toUser(sUser)); } return users; } catch (final SIdentityException sunfe) { throw new RetrieveException(sunfe); } } @Override public SearchResult searchUsers(final SearchOptions options) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchUsers searchUsers = new SearchUsers(identityService, searchEntitiesDescriptor.getSearchUserDescriptor(), options); try { searchUsers.execute(); return searchUsers.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } @Override public long getNumberOfUsersInRole(final long roleId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { final GetNumberOfUsersInType transactionContentWithResult = new GetNumberOfUsersInType(roleId, "getNumberOfUsersInRole", identityService); transactionContentWithResult.execute(); return transactionContentWithResult.getResult(); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public List getUsersInRole(final long roleId, final int startIndex, final int maxResults, final UserCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); Sort sort = getSortFromCriterion(criterion); try { return ModelConvertor.toUsers( identityService.getUsersWithRole(roleId, startIndex, maxResults, sort.getField(), sort.getOrder())); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public List getActiveUsersInRole(long roleId, int startIndex, int maxResults, UserCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); Sort sort = getSortFromCriterion(criterion); try { return ModelConvertor.toUsers(identityService.getActiveUsersWithRole(roleId, startIndex, maxResults, sort.getField(), sort.getOrder())); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public List getInactiveUsersInRole(long roleId, int startIndex, int maxResults, UserCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); Sort sort = getSortFromCriterion(criterion); try { return ModelConvertor.toUsers(identityService.getInactiveUsersWithRole(roleId, startIndex, maxResults, sort.getField(), sort.getOrder())); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public List getUsersWithManager(long managerId, int startIndex, int maxResults, UserCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); Sort sort = getSortFromCriterion(criterion); try { return ModelConvertor.toUsers(identityService.getUsersWithManager(managerId, startIndex, maxResults, sort.getField(), sort.getOrder())); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public List getActiveUsersWithManager(long managerId, int startIndex, int maxResults, UserCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); Sort sort = getSortFromCriterion(criterion); try { return ModelConvertor.toUsers(identityService.getActiveUsersWithManager(managerId, startIndex, maxResults, sort.getField(), sort.getOrder())); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public List getInactiveUsersWithManager(long managerId, int startIndex, int maxResults, UserCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); Sort sort = getSortFromCriterion(criterion); try { return ModelConvertor.toUsers(identityService.getInactiveUsersWithManager(managerId, startIndex, maxResults, sort.getField(), sort.getOrder())); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public long getNumberOfUsersInGroup(final long groupId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final GetNumberOfUsersInType transactionContentWithResult = new GetNumberOfUsersInType(groupId, "getNumberOfUsersInGroup", identityService); try { transactionContentWithResult.execute(); return transactionContentWithResult.getResult(); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public List getUsersInGroup(final long groupId, final int startIndex, final int maxResults, final UserCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); Sort sort = getSortFromCriterion(criterion); try { return ModelConvertor .toUsers(identityService.getUsersInGroup(groupId, startIndex, maxResults, sort.getField(), sort.getOrder())); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public List getActiveUsersInGroup(long groupId, int startIndex, int maxResults, UserCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); Sort sort = getSortFromCriterion(criterion); try { return ModelConvertor .toUsers(identityService.getActiveUsersInGroup(groupId, startIndex, maxResults, sort.getField(), sort.getOrder())); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public List getInactiveUsersInGroup(long groupId, int startIndex, int maxResults, UserCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); Sort sort = getSortFromCriterion(criterion); try { return ModelConvertor.toUsers(identityService.getInactiveUsersInGroup(groupId, startIndex, maxResults, sort.getField(), sort.getOrder())); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public List getUserIdsWithCustomUserInfo(String infoName, String infoValue, boolean usePartialMatch, int startIndex, int maxResults) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { return identityService.getUserIdsWithCustomUserInfo(infoName, infoValue, usePartialMatch, startIndex, maxResults); } catch (SBonitaException e) { throw new RetrieveException(e); } } @Override public Role createRole(final String roleName) throws CreationException { return createRole(new RoleCreator(roleName)); } @Override public Role createRole(final RoleCreator creator) throws CreationException { if (creator == null) { throw new CreationException("Unable to create a role with a null RoleCreator object"); } if (creator.getFields().get(org.bonitasoft.engine.identity.RoleCreator.RoleField.NAME) == null) { throw new CreationException("Unable to create a role with a null name"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); if (creator.getFields().containsKey(RoleCreator.RoleField.ICON_NAME) || creator.getFields().containsKey(RoleCreator.RoleField.ICON_PATH)) { log.warn("setIconName and setIconPath are deprecated, use setIcon instead"); } final SRole sRole = ModelConvertor.constructSRole(creator); try { getRoleByName(sRole.getName()); throw new AlreadyExistsException("A role named \"" + sRole.getName() + "\" already exists"); } catch (final RoleNotFoundException ignored) { // Ok, role can now be created. } try { identityService.createRole(sRole, (String) creator.getFields().get(RoleCreator.RoleField.ICON_FILENAME), (byte[]) creator.getFields().get(RoleCreator.RoleField.ICON_CONTENT)); return ModelConvertor.toRole(sRole); } catch (final SIdentityException e) { throw new CreationException("Role create exception!", e); } } @Override public Role updateRole(final long roleId, final RoleUpdater updateDescriptor) throws RoleNotFoundException, UpdateException { if (updateDescriptor == null || updateDescriptor.getFields().isEmpty()) { throw new UpdateException("The update descriptor does not contain field updates"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { final EntityUpdateDescriptor changeDescriptor = getRoleUpdateDescriptor(updateDescriptor); return ModelConvertor.toRole(identityService.updateRole(identityService.getRole(roleId), changeDescriptor, getIconUpdater(updateDescriptor))); } catch (final SRoleNotFoundException e) { throw new RoleNotFoundException(e); } catch (SIdentityException e) { throw new UpdateException(e); } } private EntityUpdateDescriptor getRoleUpdateDescriptor(final RoleUpdater updateDescriptor) { final SRoleUpdateBuilder roleUpdateBuilder = BuilderFactory.get(SRoleUpdateBuilderFactory.class) .createNewInstance(); final Map fields = updateDescriptor.getFields(); for (final Entry field : fields.entrySet()) { switch (field.getKey()) { case NAME: roleUpdateBuilder.updateName((String) field.getValue()); break; case DISPLAY_NAME: roleUpdateBuilder.updateDisplayName((String) field.getValue()); break; case DESCRIPTION: roleUpdateBuilder.updateDescription((String) field.getValue()); break; case ICON_NAME: log.warn("setIconName is deprecated, use setIcon instead"); break; case ICON_PATH: log.warn("setIconPath is deprecated, use setIcon instead"); break; default: break; } } roleUpdateBuilder.updateLastUpdate(System.currentTimeMillis()); return roleUpdateBuilder.done(); } @Override public void deleteRole(final long roleId) throws DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final ProfileService profileService = serviceAccessor.getProfileService(); final DeleteRole deleteRole = new DeleteRole(identityService, actorMappingService, profileService, roleId); try { deleteRole.execute(); final Set removedActorIds = deleteRole.getRemovedActorIds(); updateActorProcessDependencies(serviceAccessor, actorMappingService, removedActorIds); } catch (final SRoleNotFoundException ignored) { } catch (final SBonitaException sbe) { throw new DeletionException(sbe); } } @Override public void deleteRoles(final List roleIds) throws DeletionException { try { NullCheckingUtil.checkArgsNotNull(roleIds); } catch (final IllegalArgumentException e) { throw new DeletionException(e); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final ProfileService profileService = serviceAccessor.getProfileService(); final DeleteRoles deleteRoles = new DeleteRoles(identityService, actorMappingService, profileService, roleIds); try { deleteRoles.execute(); } catch (final SBonitaException e) { throw new DeletionException(e); } } @Override public Role getRole(final long roleId) throws RoleNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { return ModelConvertor.toRole(identityService.getRole(roleId)); } catch (SRoleNotFoundException e) { throw new RoleNotFoundException(e); } } @Override public Role getRoleByName(final String roleName) throws RoleNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { return ModelConvertor.toRole(identityService.getRoleByName(roleName)); } catch (final SRoleNotFoundException e) { throw new RoleNotFoundException(e); } } @Override public long getNumberOfRoles() { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { final GetNumberOfInstance getNumberOfInstance = new GetNumberOfInstance("getNumberOfRoles", identityService); getNumberOfInstance.execute(); return getNumberOfInstance.getResult(); } catch (final SBonitaException e) { return 0; } } @Override public List getRoles(final int startIndex, final int maxResults, final RoleCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); String field; OrderByType order; switch (criterion) { case NAME_ASC: field = SRole.NAME; order = OrderByType.ASC; break; case NAME_DESC: field = SRole.NAME; order = OrderByType.DESC; break; case DISPLAY_NAME_ASC: field = SRole.DISPLAY_NAME; order = OrderByType.ASC; break; case DISPLAY_NAME_DESC: field = SRole.DISPLAY_NAME; order = OrderByType.DESC; break; default: throw new IllegalStateException(); } try { final GetRoles getRolesWithOrder = new GetRoles(identityService, startIndex, maxResults, field, order); getRolesWithOrder.execute(); return ModelConvertor.toRoles(getRolesWithOrder.getResult()); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public Map getRoles(final List roleIds) { final Map roles = new HashMap<>(); for (final Long roleId : roleIds) { try { final Role role = getRole(roleId); roles.put(roleId, role); } catch (final RoleNotFoundException e) { // if the role does not exist; skip the role } } return roles; } @Override public SearchResult searchRoles(final SearchOptions options) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchRoles searchRoles = new SearchRoles(identityService, searchEntitiesDescriptor.getSearchRoleDescriptor(), options); try { searchRoles.execute(); return searchRoles.getResult(); } catch (final SBonitaException sbe) { throw new BonitaRuntimeException(sbe); } } @Override public Group createGroup(final String name, final String parentPath) throws CreationException { final GroupCreator groupCreator = new GroupCreator(name); groupCreator.setParentPath(parentPath); return createGroup(groupCreator); } @Override public Group createGroup(final GroupCreator creator) throws CreationException { if (creator == null) { throw new CreationException("Cannot create a null group"); } String groupName = creator.getFields().get(GroupCreator.GroupField.NAME).toString(); if (groupName.contains("/")) { throw new InvalidGroupNameException("Cannot create a group with '/' in its name"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); if (creator.getFields().containsKey(GroupCreator.GroupField.ICON_NAME) || creator.getFields().containsKey(GroupCreator.GroupField.ICON_PATH)) { log.warn("setIconName and setIconPath are deprecated, use setIcon instead"); } final SGroup sGroup = ModelConvertor.constructSGroup(creator); try { identityService.getGroupByPath(sGroup.getPath()); throw new AlreadyExistsException("Group named \"" + sGroup.getName() + "\" already exists"); } catch (final SGroupNotFoundException ignored) { } try { identityService.createGroup(sGroup, (String) creator.getFields().get(GroupCreator.GroupField.ICON_FILENAME), (byte[]) creator.getFields().get(GroupCreator.GroupField.ICON_CONTENT)); } catch (SGroupCreationException e) { throw new CreationException(e); } return ModelConvertor.toGroup(sGroup); } @Override public Group updateGroup(final long groupId, final GroupUpdater updater) throws GroupNotFoundException, UpdateException, AlreadyExistsException { if (updater == null || updater.getFields().isEmpty()) { throw new UpdateException("The update descriptor does not contain field updates"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { checkPathUniqueness(groupId, updater, identityService); final EntityUpdateDescriptor changeDescriptor = getGroupUpdateDescriptor(updater); return ModelConvertor.toGroup( new UpdateGroup(groupId, changeDescriptor, identityService, getIconUpdater(updater)).update()); } catch (final SGroupNotFoundException e) { throw new GroupNotFoundException(e); } catch (final SIdentityException sbe) { throw new UpdateException(sbe); } } private void checkPathUniqueness(long groupId, GroupUpdater updater, IdentityService identityService) throws SGroupNotFoundException, AlreadyExistsException { final Serializable updatedName = updater.getFields().get(GroupField.NAME); SGroup sGroupToBeUpdated = identityService.getGroup(groupId); String name = updatedName != null ? updatedName.toString() : sGroupToBeUpdated.getName(); StringBuilder sb = new StringBuilder(); String parentPath = updater.getFields().get(GroupField.PARENT_PATH) != null ? updater.getFields().get(GroupField.PARENT_PATH).toString() : ""; sb.append(parentPath).append("/").append(name); try { if (updatedName != null) { SGroup group = identityService.getGroupByPath(sb.toString()); if (group.getId() != groupId) { throw new AlreadyExistsException("Group named \"" + name + "\" already exists"); } } } catch (final SGroupNotFoundException ignored) { } } private EntityUpdateDescriptor getGroupUpdateDescriptor(final GroupUpdater updateDescriptor) throws UpdateException { final SGroupUpdateBuilder groupUpdateBuilder = BuilderFactory.get(SGroupUpdateBuilderFactory.class) .createNewInstance(); final Map fields = updateDescriptor.getFields(); for (final Entry field : fields.entrySet()) { final Serializable value = field.getValue(); switch (field.getKey()) { case NAME: groupUpdateBuilder.updateName((String) value); break; case DISPLAY_NAME: groupUpdateBuilder.updateDisplayName((String) value); break; case DESCRIPTION: groupUpdateBuilder.updateDescription((String) value); break; case ICON_NAME: log.warn("updateIconName is deprecated, use updateIcon instead"); break; case ICON_PATH: log.warn("updateIconPath is deprecated, use updateIcon instead"); break; case ICON_CONTENT: case ICON_FILENAME: break; case PARENT_PATH: groupUpdateBuilder .updateParentPath((value != null && ((String) value).isEmpty()) ? null : (String) value); break; default: throw new UpdateException("Invalid field: " + field.getKey().name()); } } groupUpdateBuilder.updateLastUpdate(System.currentTimeMillis()); return groupUpdateBuilder.done(); } @Override public void deleteGroup(final long groupId) throws DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final ProfileService profileService = serviceAccessor.getProfileService(); final DeleteGroup deleteGroup = new DeleteGroup(identityService, actorMappingService, profileService, groupId); try { deleteGroup.execute(); updateActorProcessDependencies(serviceAccessor, actorMappingService, deleteGroup.getRemovedActorIds()); } catch (final SGroupNotFoundException sgnfe) { throw new DeletionException(new GroupNotFoundException(sgnfe)); } catch (final SBonitaException sbe) { throw new DeletionException(sbe); } } @Override public void deleteGroups(final List groupIds) throws DeletionException { if (groupIds == null) { throw new IllegalArgumentException("the list of groups is null"); } if (!groupIds.isEmpty()) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final ProfileService profileService = serviceAccessor.getProfileService(); try { final DeleteGroups deleteGroups = new DeleteGroups(identityService, actorMappingService, profileService, groupIds); deleteGroups.execute(); updateActorProcessDependencies(serviceAccessor, actorMappingService, deleteGroups.getRemovedActorIds()); } catch (final SGroupNotFoundException sgnfe) { throw new DeletionException(new GroupNotFoundException(sgnfe)); } catch (final SBonitaException e) { throw new DeletionException(e); } } } @Override public Group getGroup(final long groupId) throws GroupNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); try { return ModelConvertor.toGroup(serviceAccessor.getIdentityService().getGroup(groupId)); } catch (final SGroupNotFoundException e) { throw new GroupNotFoundException(e); } } @Override public Group getGroupByPath(final String groupPath) throws GroupNotFoundException { try { return ModelConvertor.toGroup(getServiceAccessor().getIdentityService().getGroupByPath(groupPath)); } catch (final SGroupNotFoundException e) { throw new GroupNotFoundException(e); } } @Override public long getNumberOfGroups() { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { final GetNumberOfInstance getNumberOfInstance = new GetNumberOfInstance("getNumberOfGroups", identityService); getNumberOfInstance.execute(); return getNumberOfInstance.getResult(); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public Map getGroups(final List groupIds) { final Map groups = new HashMap<>(); for (final Long groupId : groupIds) { try { final Group group = getGroup(groupId); groups.put(groupId, group); } catch (final GroupNotFoundException e) { // if the group does not exist; skip the group } } return groups; } @Override public List getGroups(final int startIndex, final int maxResults, final GroupCriterion pagingCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); String field; OrderByType order; switch (pagingCriterion) { case NAME_ASC: field = SGroup.NAME; order = OrderByType.ASC; break; case LABEL_ASC: field = SGroup.DISPLAY_NAME; order = OrderByType.ASC; break; case NAME_DESC: field = SGroup.NAME; order = OrderByType.DESC; break; case LABEL_DESC: field = SGroup.DISPLAY_NAME; order = OrderByType.DESC; break; default: throw new IllegalStateException(); } try { final GetGroups getGroups = new GetGroups(identityService, startIndex, maxResults, order, field); getGroups.execute(); return ModelConvertor.toGroups(getGroups.getResult()); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public SearchResult searchGroups(final SearchOptions options) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchGroups searchGroups = new SearchGroups(identityService, searchEntitiesDescriptor.getSearchGroupDescriptor(), options); try { searchGroups.execute(); return searchGroups.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } /** * Check / update process resolution information, for all processes in a list of actor IDs. */ private void updateActorProcessDependencies(final ServiceAccessor serviceAccessor, final ActorMappingService actorMappingService, final Set removedActorIds) throws SBonitaException { final Set processDefinitionIds = new HashSet<>(removedActorIds.size()); for (final Long actorId : removedActorIds) { final GetActor getActor = new GetActor(actorMappingService, actorId); getActor.execute(); final SActor actor = getActor.getResult(); final Long processDefId = actor.getScopeId(); if (!processDefinitionIds.contains(processDefId)) { processDefinitionIds.add(processDefId); serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(actor.getScopeId(), serviceAccessor); } } } private List getUsersWithOrder(final int startIndex, final int maxResults, final UserCriterion pagingCriterion, final IdentityService identityService) throws SIdentityException { final String field = getUserFieldKey(pagingCriterion); final OrderByType order = getUserOrderByType(pagingCriterion); if (field == null) { return ModelConvertor.toUsers(identityService.getUsers(startIndex, maxResults)); } return ModelConvertor.toUsers(identityService.getUsers(startIndex, maxResults, field, order)); } private OrderByType getUserOrderByType(final UserCriterion pagingCriterion) { OrderByType order; switch (pagingCriterion) { case USER_NAME_ASC: order = OrderByType.ASC; break; case FIRST_NAME_ASC: order = OrderByType.ASC; break; case LAST_NAME_ASC: order = OrderByType.ASC; break; case FIRST_NAME_DESC: order = OrderByType.DESC; break; case LAST_NAME_DESC: order = OrderByType.DESC; break; case USER_NAME_DESC: order = OrderByType.DESC; break; default: throw new IllegalStateException(); } return order; } private String getUserFieldKey(final UserCriterion pagingCriterion) { String field; switch (pagingCriterion) { case USER_NAME_ASC: field = SUser.USER_NAME; break; case FIRST_NAME_ASC: field = SUser.FIRST_NAME; break; case LAST_NAME_ASC: field = SUser.LAST_NAME; break; case FIRST_NAME_DESC: field = SUser.FIRST_NAME; break; case LAST_NAME_DESC: field = SUser.LAST_NAME; break; case USER_NAME_DESC: field = SUser.USER_NAME; break; default: throw new IllegalStateException(); } return field; } @Override public UserMembership addUserMembership(final long userId, final long groupId, final long roleId) throws CreationException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final long assignedBy = SessionInfos.getUserIdFromSession(); try { final GetUserMembership getUserMembership = new GetUserMembership(userId, groupId, roleId, identityService); getUserMembership.execute(); if (getUserMembership.getResult() != null) { throw new AlreadyExistsException("A userMembership with userId \"" + userId + "\", groupId \"" + groupId + "\" and roleId \"" + roleId + "\" already exists"); } } catch (final SBonitaException e) { // Membership does not exists but was unable to be created } try { final AddUserMembership createUserMembership = new AddUserMembership(userId, groupId, roleId, assignedBy, identityService); createUserMembership.execute(); final SUserMembership sUserMembership = createUserMembership.getResult(); return ModelConvertor.toUserMembership(sUserMembership); } catch (final SBonitaException sbe) { throw new CreationException(sbe); } } @Override public void addUserMemberships(final List userIds, final long groupId, final long roleId) throws CreationException { // FIXME rewrite final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final long currentUserId = SessionInfos.getUserIdFromSession(); try { final AddUserMemberships transactionContent = new AddUserMemberships(groupId, roleId, userIds, identityService, currentUserId); transactionContent.execute(); } catch (final SBonitaException sbe) { throw new CreationException(sbe); } } @Override public UserMembership updateUserMembership(final long userMembershipId, final long newGroupId, final long newRoleId) throws UpdateException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final EntityUpdateDescriptor changeDescriptor = BuilderFactory.get(SUserMembershipUpdateBuilderFactory.class) .createNewInstance() .updateGroupId(newGroupId).updateRoleId(newRoleId).done(); try { final TransactionContent transactionContent = new UpdateMembershipByRoleIdAndGroupId(userMembershipId, identityService, changeDescriptor); transactionContent.execute(); final GetUserMembership getMembershipAfterUpdate = new GetUserMembership(userMembershipId, identityService); getMembershipAfterUpdate.execute(); final SUserMembership sMembershipAfterUpdate = getMembershipAfterUpdate.getResult(); return ModelConvertor.toUserMembership(sMembershipAfterUpdate); } catch (final SBonitaException sbe) { throw new UpdateException(sbe); } } @Override public void deleteUserMembership(final long userMembershipId) throws DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { identityService.deleteUserMembership(userMembershipId); } catch (final SIdentityException e) { throw new DeletionException(e); } } @Override public void deleteUserMembership(final long userId, final long groupId, final long roleId) throws DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { identityService.deleteLightUserMembership(identityService.getLightUserMembership(userId, groupId, roleId)); } catch (final SIdentityException e) { throw new DeletionException(e); } } @Override public void deleteUserMemberships(final List userIds, final long groupId, final long roleId) throws DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { for (final long userId : userIds) { final SUserMembership userMembership = identityService.getLightUserMembership(userId, groupId, roleId); identityService.deleteLightUserMembership(userMembership); } } catch (final SIdentityException e) { throw new DeletionException(e); } } @Override public UserMembership getUserMembership(final long userMembershipId) throws MembershipNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final GetUserMembership getUserMembership = new GetUserMembership(userMembershipId, identityService); try { getUserMembership.execute(); final SUserMembership sMembership = getUserMembership.getResult(); return ModelConvertor.toUserMembership(sMembership); } catch (final SBonitaException sbe) { throw new MembershipNotFoundException(sbe); } } @Override public long getNumberOfUserMemberships(final long userId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { final TransactionContentWithResult transactionContent = new GetNumberOfUserMemberships(userId, identityService); transactionContent.execute(); return transactionContent.getResult(); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public List getUserMemberships(final long userId, final int startIndex, final int maxResults, final UserMembershipCriterion pagingCrterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); try { final IdentityService identityService = serviceAccessor.getIdentityService(); OrderByOption orderByOption; switch (pagingCrterion) { case ROLE_NAME_DESC: orderByOption = new OrderByOption(SRole.class, SRole.NAME, OrderByType.DESC); break; case GROUP_NAME_ASC: orderByOption = new OrderByOption(SGroup.class, SGroup.NAME, OrderByType.ASC); break; case GROUP_NAME_DESC: orderByOption = new OrderByOption(SGroup.class, SGroup.NAME, OrderByType.DESC); break; // case ASSIGNED_BY_ASC: // orderByOption = new OrderByOption(SUserMembership.class, modelBuilder.getUserMembershipBuilder().getAssignedByKey(), OrderByType.ASC); // break; // case ASSIGNED_BY_DESC: // orderByOption = new OrderByOption(SUserMembership.class, modelBuilder.getUserMembershipBuilder().getAssignedByKey(), OrderByType.DESC); // break; case ASSIGNED_DATE_ASC: orderByOption = new OrderByOption(SUserMembership.class, SUserMembership.ASSIGNED_DATE, OrderByType.ASC); break; case ASSIGNED_DATE_DESC: orderByOption = new OrderByOption(SUserMembership.class, SUserMembership.ASSIGNED_DATE, OrderByType.DESC); break; case ROLE_NAME_ASC: default: orderByOption = new OrderByOption(SRole.class, SRole.NAME, OrderByType.ASC); break; } return getUserMemberships(userId, startIndex, maxResults, orderByOption, identityService); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } private List getUserMemberships(final long userId, final int startIndex, final int maxResults, final OrderByOption orderByOption, final IdentityService identityService) throws SBonitaException { List sUserMemberships; if (userId == -1) { sUserMemberships = identityService.getUserMemberships(startIndex, maxResults, orderByOption); } else if (orderByOption != null) { sUserMemberships = identityService.getUserMembershipsOfUser(userId, startIndex, maxResults, orderByOption); } else { sUserMemberships = identityService.getUserMembershipsOfUser(userId, startIndex, maxResults); } return ModelConvertor.toUserMembership(sUserMemberships); } @Override public List getUserMembershipsByGroup(final long groupId, final int startIndex, final int maxResults) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final GetUserMembershipsOfGroup transactionContentWithResult = new GetUserMembershipsOfGroup(groupId, identityService, startIndex, maxResults); try { transactionContentWithResult.execute(); return ModelConvertor.toUserMembership(transactionContentWithResult.getResult()); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public List getUserMembershipsByRole(final long roleId, final int startIndex, final int maxResults) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { final GetUserMembershipsOfRole transactionContentWithResult = new GetUserMembershipsOfRole(roleId, identityService, startIndex, maxResults); transactionContentWithResult.execute(); return ModelConvertor.toUserMembership(transactionContentWithResult.getResult()); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public void deleteOrganization() throws DeletionException { final OrganizationAPIImpl organizationAPIImpl = new OrganizationAPIImpl(getServiceAccessor(), 100); organizationAPIImpl.deleteOrganization(); } @Override public void importOrganization(final String organizationContent) throws OrganizationImportException { importOrganizationWithWarnings(organizationContent, ImportPolicy.MERGE_DUPLICATES); } @Override public List importOrganizationWithWarnings(String organizationContent, ImportPolicy policy) throws OrganizationImportException { return getOrganizationAPIDelegate().importOrganizationWithWarnings(organizationContent, policy); } @Override public String exportOrganization() throws OrganizationExportException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final int maxResults = 100; final ExportOrganization exportOrganization = new ExportOrganization(serviceAccessor.getIdentityService(), maxResults); try { exportOrganization.execute(); return exportOrganization.getResult(); } catch (final SBonitaException e) { throw new OrganizationExportException(e); } } @Override public CustomUserInfoDefinition createCustomUserInfoDefinition(final CustomUserInfoDefinitionCreator creator) throws CreationException { return createCustomUserInfoDefinitionAPI().create(creator); } @Override public List getCustomUserInfoDefinitions(final int startIndex, final int maxResult) throws RetrieveException { return createCustomUserInfoDefinitionAPI().list(startIndex, maxResult); } @Override public long getNumberOfCustomInfoDefinitions() { return createCustomUserInfoDefinitionAPI().count(); } @Override public void deleteCustomUserInfoDefinition(final long id) throws DeletionException { createCustomUserInfoDefinitionAPI().delete(id); } @Override public List getCustomUserInfo(final long userId, final int startIndex, final int maxResult) { try { return createCustomUserInfoAPI().list(userId, startIndex, maxResult); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public SearchResult searchCustomUserInfoValues(final SearchOptions options) { try { return createCustomUserInfoValueAPI().search( getServiceAccessor().getSearchEntitiesDescriptor().getSearchCustomUserInfoValueDescriptor(), options); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public CustomUserInfoValue setCustomUserInfoValue(final long definitionId, final long userId, final String value) throws UpdateException { try { return ModelConvertor.convert(createCustomUserInfoValueAPI().set(definitionId, userId, value)); } catch (final SBonitaException e) { throw new UpdateException(e); } } @Override public Icon getIcon(long id) throws NotFoundException { try { SIcon icon = getServiceAccessor().getIconService().getIcon(id); if (icon == null) { throw new NotFoundException("unable to find icon with id " + id); } return ModelConvertor.toIcon(icon); } catch (SBonitaReadException e) { throw new RetrieveException(e); } } private Sort getSortFromCriterion(UserCriterion criterion) { Sort sort = new Sort(); switch (criterion) { case FIRST_NAME_ASC: sort.setField(SUser.FIRST_NAME); sort.setOrder(OrderByType.ASC); break; case LAST_NAME_ASC: sort.setField(SUser.LAST_NAME); sort.setOrder(OrderByType.ASC); break; case USER_NAME_ASC: sort.setField(SUser.USER_NAME); sort.setOrder(OrderByType.ASC); break; case FIRST_NAME_DESC: sort.setField(SUser.FIRST_NAME); sort.setOrder(OrderByType.DESC); break; case LAST_NAME_DESC: sort.setField(SUser.LAST_NAME); sort.setOrder(OrderByType.DESC); break; case USER_NAME_DESC: sort.setField(SUser.USER_NAME); sort.setOrder(OrderByType.DESC); break; default: throw new IllegalStateException(); } return sort; } private CustomUserInfoAPIDelegate createCustomUserInfoAPI() { return new CustomUserInfoAPIDelegate(getServiceAccessor().getIdentityService()); } private CustomUserInfoDefinitionAPIDelegate createCustomUserInfoDefinitionAPI() { return new CustomUserInfoDefinitionAPIDelegate(getServiceAccessor().getIdentityService()); } private SCustomUserInfoValueAPI createCustomUserInfoValueAPI() { return new SCustomUserInfoValueAPI(getServiceAccessor().getIdentityService(), BuilderFactory.get(SCustomUserInfoValueUpdateBuilderFactory.class)); } private static class Sort { private String field = null; private OrderByType order = null; private Sort() { } private String getField() { return field; } private void setField(String field) { this.field = field; } private OrderByType getOrder() { return order; } private void setOrder(OrderByType order) { this.order = order; } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/LoginAPIImpl.java ================================================ /** * 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.api.impl; import java.io.IOException; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.api.LoginAPI; import org.bonitasoft.engine.api.impl.transaction.CustomTransactions; import org.bonitasoft.engine.authentication.AuthenticationConstants; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.core.login.LoginService; import org.bonitasoft.engine.core.login.SLoginException; import org.bonitasoft.engine.exception.BonitaHomeConfigurationException; import org.bonitasoft.engine.platform.LoginException; import org.bonitasoft.engine.platform.LogoutException; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.platform.model.SPlatform; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.SSessionNotFoundException; import org.bonitasoft.engine.session.SessionNotFoundException; import org.bonitasoft.engine.session.model.SSession; import org.bonitasoft.engine.transaction.TransactionService; import org.springframework.util.CollectionUtils; /** * @author Matthieu Chaffotte * @author Zhang Bole */ public class LoginAPIImpl implements LoginAPI { @Override @CustomTransactions @AvailableInMaintenanceMode public APISession login(final String userName, final String password) throws LoginException { try { return loginInternal(userName, password); } catch (final LoginException e) { throw e; } catch (final Exception e) { throw new LoginException(e); } } @CustomTransactions @AvailableInMaintenanceMode protected APISession login(final String userName, final String password, final Long tenantId) throws LoginException { try { return loginInternal(userName, password); } catch (final LoginException e) { throw e; } catch (final Exception e) { throw new LoginException(e); } } @Override @CustomTransactions @AvailableInMaintenanceMode public APISession login(final Map credentials) throws LoginException { checkCredentialsAreNotNullOrEmpty(credentials); try { return loginInternal(credentials); } catch (final LoginException e) { throw e; } catch (final Exception e) { throw new LoginException(e); } } protected APISession loginInternal(final String userName, final String password) throws Exception { checkUsernameAndPassword(userName, password); final Map credentials = new HashMap<>(); credentials.put(AuthenticationConstants.BASIC_USERNAME, userName); credentials.put(AuthenticationConstants.BASIC_PASSWORD, password); return loginInternal(credentials); } protected APISession loginInternal(final Map credentials) throws Exception { final ServiceAccessor serviceAccessor = ServiceAccessorFactory.getInstance().createServiceAccessor(); final LoginService loginService = serviceAccessor.getLoginService(); final TransactionService transactionService = serviceAccessor.getTransactionService(); final SSession sSession = transactionService .executeInTransaction(() -> loginService.login(credentials)); return ModelConvertor.toAPISession(sSession); } private SPlatform getPlatform(final ServiceAccessor serviceAccessor) throws SBonitaException { final PlatformService platformService = serviceAccessor.getPlatformService(); try { return serviceAccessor.getTransactionService().executeInTransaction(platformService::getPlatform); } catch (SBonitaException | RuntimeException e) { throw e; } catch (Exception e) { throw new SBonitaRuntimeException(e); } } protected void checkUsernameAndPassword(final String userName, final String password) throws LoginException { if (userName == null || userName.isEmpty()) { throw new LoginException("User name is null or empty !! "); } if (password == null || password.isEmpty()) { throw new LoginException("Password is null or empty !!"); } } protected void checkCredentialsAreNotNullOrEmpty(final Map credentials) throws LoginException { if (CollectionUtils.isEmpty(credentials)) { throw new LoginException("Credentials are null or empty !!"); } } protected ServiceAccessor getServiceAccessor() { try { return ServiceAccessorFactory.getInstance().createServiceAccessor(); } catch (BonitaHomeConfigurationException | IOException | ReflectiveOperationException e) { throw new RuntimeException(e); } } @Override @CustomTransactions public void logout(final APISession session) throws LogoutException, SessionNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); try { serviceAccessor.getLoginService().logout(session.getId()); } catch (final SSessionNotFoundException sbe) { throw new SessionNotFoundException(sbe); } catch (final SLoginException sbe) { throw new LogoutException(sbe); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/MaintenanceAPIImpl.java ================================================ /** * Copyright (C) 2023 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.api.impl; import org.bonitasoft.engine.api.MaintenanceAPI; import org.bonitasoft.engine.api.impl.transaction.CustomTransactions; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.maintenance.MaintenanceDetails; import org.bonitasoft.engine.maintenance.impl.MaintenanceDetailsImpl; import org.bonitasoft.engine.platform.PlatformNotFoundException; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.platform.exception.SPlatformNotFoundException; import org.bonitasoft.engine.platform.exception.SPlatformUpdateException; import org.bonitasoft.engine.platform.model.SPlatform; import org.bonitasoft.engine.platform.model.builder.SPlatformUpdateBuilder; import org.bonitasoft.engine.platform.model.builder.impl.SPlatformUpdateBuilderImpl; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.tenant.TenantStateManager; /** * This API gives access to maintenance administration tasks. */ @AvailableInMaintenanceMode public class MaintenanceAPIImpl implements MaintenanceAPI { public MaintenanceAPIImpl() { } protected ServiceAccessor getServiceAccessor() { try { return ServiceAccessorFactory.getInstance().createServiceAccessor(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } @Override public MaintenanceDetails getMaintenanceDetails() throws PlatformNotFoundException { try { PlatformService platformService = getServiceAccessor().getPlatformService(); MaintenanceDetails.State state = platformService.getPlatform().isMaintenanceEnabled() ? MaintenanceDetails.State.ENABLED : MaintenanceDetails.State.DISABLED; SPlatform platform = platformService.getPlatform(); return MaintenanceDetailsImpl.builder() .maintenanceMessage(platform.getMaintenanceMessage()) .maintenanceMessageActive(platform.isMaintenanceMessageActive()) .maintenanceState(state) .build(); } catch (SPlatformNotFoundException e) { throw new PlatformNotFoundException(e.getMessage(), e); } } @Override @CustomTransactions public void enableMaintenanceMode() throws UpdateException { try { TenantStateManager tenantStateManager = getServiceAccessor().getTenantStateManager(); tenantStateManager.pause(); } catch (Exception e) { throw new UpdateException(e); } } @Override @CustomTransactions public void disableMaintenanceMode() throws UpdateException { try { TenantStateManager tenantStateManager = getServiceAccessor().getTenantStateManager(); tenantStateManager.resume(); getServiceAccessor().getTransactionService().executeInTransaction(() -> { disableMaintenanceMessage(); return null; }); } catch (Exception e) { throw new UpdateException(e); } } @Override public void updateMaintenanceMessage(String message) throws UpdateException { try { PlatformService platformService = getServiceAccessor().getPlatformService(); platformService.updatePlatform(getPlatformUpdateBuilder() .setMaintenanceMessage(message).done()); } catch (SPlatformUpdateException e) { throw new UpdateException(e); } } @Override public void enableMaintenanceMessage() throws UpdateException { try { PlatformService platformService = getServiceAccessor().getPlatformService(); platformService.updatePlatform(getPlatformUpdateBuilder() .setMaintenanceMessageActive(true).done()); } catch (SPlatformUpdateException e) { throw new UpdateException(e); } } @Override public void disableMaintenanceMessage() throws UpdateException { try { PlatformService platformService = getServiceAccessor().getPlatformService(); platformService.updatePlatform(getPlatformUpdateBuilder() .setMaintenanceMessageActive(false).done()); } catch (SPlatformUpdateException e) { throw new UpdateException(e); } } protected SPlatformUpdateBuilder getPlatformUpdateBuilder() { return SPlatformUpdateBuilderImpl.builder() .descriptor(new EntityUpdateDescriptor()) .build(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/OrderAndFields.java ================================================ /** * 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.api.impl; import org.bonitasoft.engine.bpm.connector.ConnectorCriterion; import org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor; import org.bonitasoft.engine.bpm.document.DocumentCriterion; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceCriterion; import org.bonitasoft.engine.bpm.flownode.EventCriterion; import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.document.model.SDocument; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilderFactory; import org.bonitasoft.engine.persistence.OrderAndField; import org.bonitasoft.engine.persistence.OrderByType; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class OrderAndFields { static OrderAndField getOrderAndFieldForConnectorImplementation(final ConnectorCriterion pagingCriterion) { String filed = null; OrderByType orderBy = null; ConnectorCriterion criterion = pagingCriterion; if (criterion == null) { criterion = ConnectorCriterion.DEFAULT; } switch (criterion) { case DEFINITION_ID_ASC: filed = ConnectorImplementationDescriptor.DEFINITION_ID; orderBy = OrderByType.ASC; break; case DEFINITION_ID_DESC: filed = ConnectorImplementationDescriptor.DEFINITION_ID; orderBy = OrderByType.DESC; break; case DEFINITION_VERSION_ASC: filed = ConnectorImplementationDescriptor.DEFINITION_VERSION; orderBy = OrderByType.ASC; break; case DEFINITION_VERSION_DESC: filed = ConnectorImplementationDescriptor.DEFINITION_VERSION; orderBy = OrderByType.DESC; break; case IMPLEMENTATION_VERSION_ASC: filed = ConnectorImplementationDescriptor.VERSIOIN; orderBy = OrderByType.ASC; break; case IMPLEMENTATIONN_VERSION_DESC: filed = ConnectorImplementationDescriptor.VERSIOIN; orderBy = OrderByType.DESC; break; case IMPLEMENTATIONN_CLASS_NAME_ACS: filed = ConnectorImplementationDescriptor.IMPLEMENTATION_CLASS_NAME; orderBy = OrderByType.ASC; break; case IMPLEMENTATIONN_CLASS_NAME_DESC: filed = ConnectorImplementationDescriptor.IMPLEMENTATION_CLASS_NAME; orderBy = OrderByType.DESC; break; case IMPLEMENTATION_ID_DESC: filed = ConnectorImplementationDescriptor.ID; orderBy = OrderByType.DESC; break; case IMPLEMENTATION_ID_ASC: case DEFAULT: default: filed = ConnectorImplementationDescriptor.ID; orderBy = OrderByType.ASC; break; } return new OrderAndField(orderBy, filed); } static OrderAndField getOrderAndFieldForProcessInstance(final ProcessInstanceCriterion criterion) { final SAProcessInstanceBuilderFactory keyProvider = BuilderFactory.get(SAProcessInstanceBuilderFactory.class); String field = null; OrderByType order = null; switch (criterion) { case STATE_ASC: field = keyProvider.getStateIdKey(); order = OrderByType.ASC; break; case STATE_DESC: field = keyProvider.getStateIdKey(); order = OrderByType.DESC; break; case ARCHIVE_DATE_ASC: field = keyProvider.getArchiveDateKey(); order = OrderByType.ASC; break; case ARCHIVE_DATE_DESC: field = keyProvider.getArchiveDateKey(); order = OrderByType.DESC; break; case LAST_UPDATE_DESC: field = keyProvider.getLastUpdateKey(); order = OrderByType.DESC; break; case LAST_UPDATE_ASC: field = keyProvider.getLastUpdateKey(); order = OrderByType.ASC; break; case CREATION_DATE_ASC: field = keyProvider.getStartDateKey(); order = OrderByType.ASC; break; case CREATION_DATE_DESC: case DEFAULT: field = keyProvider.getStartDateKey(); order = OrderByType.DESC; break; case NAME_ASC: field = keyProvider.getNameKey(); order = OrderByType.ASC; break; case NAME_DESC: field = keyProvider.getNameKey(); order = OrderByType.DESC; break; default: break; } return new OrderAndField(order, field); } static OrderAndField getOrderAndFieldForEvent(final EventCriterion sortingType) { final SEndEventInstanceBuilderFactory keyProvider = BuilderFactory.get(SEndEventInstanceBuilderFactory.class); OrderByType orderByType = null; String fieldName = null; switch (sortingType) { case NAME_DESC: orderByType = OrderByType.DESC; fieldName = keyProvider.getNameKey(); break; default: orderByType = OrderByType.ASC; fieldName = keyProvider.getNameKey(); break; } return new OrderAndField(orderByType, fieldName); } static OrderAndField getOrderAndFieldForActivityInstance(ActivityInstanceCriterion pagingCriterion) { final SUserTaskInstanceBuilderFactory keyProvider = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class); String field = null; OrderByType order = null; if (pagingCriterion == null) { pagingCriterion = ActivityInstanceCriterion.DEFAULT; } switch (pagingCriterion) { case DEFAULT: field = keyProvider.getPriorityKey(); order = OrderByType.DESC; break; case NAME_DESC: field = keyProvider.getNameKey(); order = OrderByType.DESC; break; case NAME_ASC: field = keyProvider.getNameKey(); order = OrderByType.ASC; break; case LAST_UPDATE_ASC: field = keyProvider.getLastUpdateDateKey(); order = OrderByType.ASC; break; case LAST_UPDATE_DESC: field = keyProvider.getLastUpdateDateKey(); order = OrderByType.DESC; break; case PRIORITY_ASC: field = keyProvider.getPriorityKey(); order = OrderByType.ASC; break; case PRIORITY_DESC: field = keyProvider.getPriorityKey(); order = OrderByType.DESC; break; case REACHED_STATE_DATE_ASC: field = keyProvider.getReachStateDateKey(); order = OrderByType.ASC; break; case REACHED_STATE_DATE_DESC: field = keyProvider.getReachStateDateKey(); order = OrderByType.DESC; break; case EXPECTED_END_DATE_ASC: field = keyProvider.getExpectedEndDateKey(); order = OrderByType.ASC; break; case EXPECTED_END_DATE_DESC: field = keyProvider.getExpectedEndDateKey(); order = OrderByType.DESC; break; default: break; } return new OrderAndField(order, field); } /** * @param pagingCriterion * @param builder * @return */ public static OrderAndField getOrderAndFieldForDocument(DocumentCriterion pagingCriterion) { String field; OrderByType order; if (pagingCriterion == null) { pagingCriterion = DocumentCriterion.DEFAULT; } switch (pagingCriterion) { case DEFAULT: field = "document." + SDocument.CREATION_DATE; order = OrderByType.DESC; break; case AUTHOR_ASC: field = "document." + SDocument.AUTHOR; order = OrderByType.ASC; break; case AUTHOR_DESC: field = "document." + SDocument.AUTHOR; order = OrderByType.DESC; break; case FILENAME_ASC: field = "document." + SDocument.FILENAME; order = OrderByType.ASC; break; case FILENAME_DESC: field = "document." + SDocument.FILENAME; order = OrderByType.DESC; break; case MIMETYPE_ASC: field = "document." + SDocument.MIMETYPE; order = OrderByType.ASC; break; case MIMETYPE_DESC: field = "document." + SDocument.MIMETYPE; order = OrderByType.DESC; break; case CREATION_DATE_ASC: field = "document." + SDocument.CREATION_DATE; order = OrderByType.ASC; break; case CREATION_DATE_DESC: field = "document." + SDocument.CREATION_DATE; order = OrderByType.DESC; break; case NAME_ASC: field = SDocument.NAME; order = OrderByType.ASC; break; case NAME_DESC: field = SDocument.NAME; order = OrderByType.DESC; break; case URL_ASC: field = "document." + SDocument.URL; order = OrderByType.ASC; break; case URL_DESC: field = "document." + SDocument.URL; order = OrderByType.DESC; break; default: throw new IllegalStateException(); } return new OrderAndField(order, field); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/OrganizationAPIImpl.java ================================================ /** * 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.api.impl; import java.util.List; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.api.impl.resolver.ActorBusinessArchiveArtifactManager; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SIdentityException; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.profile.ProfileService; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService; /** * @author Elias Ricken de Medeiros */ @AvailableInMaintenanceMode public class OrganizationAPIImpl { private final ServiceAccessor serviceAccessor; private final int pageSize; public OrganizationAPIImpl(ServiceAccessor serviceAccessor, int pageSize) { this.serviceAccessor = serviceAccessor; this.pageSize = pageSize; } public void deleteOrganization() throws DeletionException { final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SCommentService commentService = serviceAccessor.getCommentService(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); try { final QueryOptions queryOptions = new QueryOptions(0, 1); boolean canDeleteOrganization = processInstanceService.getNumberOfProcessInstances(queryOptions) == 0 && activityInstanceService.getNumberOfHumanTasks(queryOptions) == 0 && commentService.getNumberOfComments(queryOptions) == 0; if (canDeleteOrganization) { deleteOrganizationElements(activityInstanceService); updateActorProcessDependenciesForAllActors(serviceAccessor); } else { throw new DeletionException( "Can't delete a organization when a process, a human tasks, or a comment is active !!."); } } catch (final SBonitaException e) { throw new DeletionException(e); } } private void deleteOrganizationElements(final ActivityInstanceService activityInstanceService) throws SBonitaException { final IdentityService identityService = serviceAccessor.getIdentityService(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final ProfileService profileService = serviceAccessor.getProfileService(); final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService(); deleteCustomUserInfo(identityService); actorMappingService.deleteAllActorMembers(); profileService.deleteAllProfileMembers(); activityInstanceService.deleteAllPendingMappings(); supervisorService.deleteAllProcessSupervisors(); identityService.deleteAllUserMemberships(); identityService.deleteAllGroups(); identityService.deleteAllRoles(); identityService.deleteAllUsers(); } private void deleteCustomUserInfo(IdentityService identityService) throws SIdentityException { // only definitions will be deleted because values are deleted on cascade from DB List customUserInfoDefinitions; do { // the start index is always zero because the current page will be deleted customUserInfoDefinitions = identityService.getCustomUserInfoDefinitions(0, pageSize); deleteCustomUserInfo(customUserInfoDefinitions, identityService); } while (customUserInfoDefinitions.size() == pageSize); } private void deleteCustomUserInfo(List customUserInfoDefinitions, IdentityService identityService) throws SIdentityException { for (SCustomUserInfoDefinition definition : customUserInfoDefinitions) { identityService.deleteCustomUserInfoDefinition(definition.getId()); } } /** * Check / update process resolution information, for all processes in a list of actor IDs. */ private void updateActorProcessDependenciesForAllActors(final ServiceAccessor serviceAccessor) throws SBonitaException { final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); List processDefinitionIds; final ActorBusinessArchiveArtifactManager dependencyResolver = new ActorBusinessArchiveArtifactManager( serviceAccessor.getActorMappingService(), serviceAccessor.getIdentityService()); do { processDefinitionIds = processDefinitionService.getProcessDefinitionIds(0, 100); for (final Long processDefinitionId : processDefinitionIds) { serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(processDefinitionId, serviceAccessor, dependencyResolver); } } while (processDefinitionIds.size() == 100); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/PageAPIImpl.java ================================================ /** * 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.api.impl; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.Properties; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.impl.page.PageAPIDelegate; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.exception.*; import org.bonitasoft.engine.page.*; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; @AvailableInMaintenanceMode public class PageAPIImpl implements PageAPI { protected ServiceAccessor getServiceAccessor() { try { return ServiceAccessorSingleton.getInstance(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } protected PageAPIDelegate getPageAPIDelegate() { return PageAPIDelegate.getInstance(); } @Override public Page getPage(final long pageId) throws PageNotFoundException { return getPageAPIDelegate().getPage(pageId); } @Override public byte[] getPageContent(final long pageId) throws PageNotFoundException { return getPageAPIDelegate().getPageContent(pageId); } @Override public SearchResult searchPages(final SearchOptions searchOptions) throws SearchException { return getPageAPIDelegate().searchPages(searchOptions); } @Override public Page createPage(final PageCreator pageCreator, final byte[] content) throws CreationException { return getPageAPIDelegate().createPage(pageCreator, content, SessionInfos.getUserIdFromSession()); } @Override public Page createPage(final String contentName, final byte[] content) throws CreationException { return getPageAPIDelegate().createPage(contentName, content, SessionInfos.getUserIdFromSession()); } @Override public void deletePage(final long pageId) throws DeletionException { getPageAPIDelegate().deletePage(pageId); } @Override public void deletePages(final List pageIds) throws DeletionException { getPageAPIDelegate().deletePages(pageIds); } @Override public Page getPageByName(final String name) throws PageNotFoundException { return getPageAPIDelegate().getPageByName(name); } @Override public Page getPageByNameAndProcessDefinitionId(String name, long processDefinitionId) throws PageNotFoundException { return getPageAPIDelegate().getPageByNameAndProcessDefinition(name, processDefinitionId); } @Override public Page updatePage(final long pageId, final PageUpdater pageUpdater) throws UpdateException, AlreadyExistsException { return getPageAPIDelegate().updatePage(pageId, pageUpdater, SessionInfos.getUserIdFromSession()); } @Override public void updatePageContent(final long pageId, final byte[] content) throws UpdateException { getPageAPIDelegate().updatePageContent(pageId, content, SessionInfos.getUserIdFromSession()); } @Override public Properties getPageProperties(final byte[] content, final boolean checkIfItAlreadyExists) throws InvalidPageTokenException, AlreadyExistsException, InvalidPageZipMissingPropertiesException, InvalidPageZipMissingIndexException, InvalidPageZipInconsistentException, InvalidPageZipMissingAPropertyException { return getPageAPIDelegate().getPageProperties(content, checkIfItAlreadyExists); } @Override public PageURL resolvePageOrURL(String key, Map context, boolean executeAuthorizationRules) throws NotFoundException, ExecutionException, UnauthorizedAccessException { final PageMappingService pageMappingService = retrievePageMappingService(); try { return ModelConvertor.toPageURL( pageMappingService.resolvePageURL(pageMappingService.get(key), context, executeAuthorizationRules)); } catch (final SObjectNotFoundException e) { throw new NotFoundException(e); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } catch (final SExecutionException e) { throw new ExecutionException(e); } catch (final SAuthorizationException e) { throw new UnauthorizedAccessException(e); } } PageMappingService retrievePageMappingService() { return getServiceAccessor().getPageMappingService(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/PermissionAPIImpl.java ================================================ /** * 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.api.impl; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.PermissionAPI; import org.bonitasoft.engine.api.permission.APICallContext; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.exception.ExecutionException; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; /** * @author Baptiste Mesta */ @Slf4j @AvailableInMaintenanceMode public class PermissionAPIImpl implements PermissionAPI { @Override public boolean isAuthorized(APICallContext apiCallContext) throws ExecutionException { ServiceAccessor serviceAccessor = getServiceAccessor(); try { return serviceAccessor.getPermissionService().isAuthorized(apiCallContext); } catch (SExecutionException e) { throw new ExecutionException(e); } } @Override public Set getResourcePermissions(String resourceKey) { ServiceAccessor serviceAccessor = getServiceAccessor(); return serviceAccessor.getPermissionService().getResourcePermissions(resourceKey); } ServiceAccessor getServiceAccessor() { return ServiceAccessorSingleton.getInstance(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/PlatformAPIImpl.java ================================================ /** * 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.api.impl; import java.io.IOException; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.PlatformAPI; import org.bonitasoft.engine.api.impl.transaction.CustomTransactions; import org.bonitasoft.engine.api.impl.transaction.platform.GetPlatformContent; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.exception.BonitaHomeConfigurationException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.home.BonitaHomeServer; import org.bonitasoft.engine.platform.Platform; import org.bonitasoft.engine.platform.PlatformManager; import org.bonitasoft.engine.platform.PlatformNotFoundException; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.platform.PlatformState; import org.bonitasoft.engine.platform.StartNodeException; import org.bonitasoft.engine.platform.StopNodeException; import org.bonitasoft.engine.platform.model.SPlatform; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; /** * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Lu Kai * @author Zhang Bole * @author Yanyan Liu * @author Emmanuel Duchastenier * @author Celine Souchet */ @Slf4j public class PlatformAPIImpl implements PlatformAPI { public PlatformAPIImpl() { super(); } @Override @CustomTransactions @AvailableOnStoppedNode public void initializePlatform() { //nothing to do } protected ServiceAccessor getServiceAccessor() throws BonitaHomeNotSetException, IOException, BonitaHomeConfigurationException, ReflectiveOperationException { return ServiceAccessorFactory.getInstance().createServiceAccessor(); } @Override @CustomTransactions @AvailableOnStoppedNode public void startNode() throws StartNodeException { PlatformManager platformManager; try { platformManager = getServiceAccessor().getPlatformManager(); } catch (final Exception e) { throw new StartNodeException(e); } boolean isStarted; try { isStarted = platformManager.start(); } catch (final Exception e) { throw new StartNodeException("Platform starting failed.", e); } if (!isStarted) { throw new StartNodeException( "Platform is in state " + platformManager.getState() + " and cannot be started"); } } @Override @CustomTransactions @AvailableOnStoppedNode public void stopNode() throws StopNodeException { try { getServiceAccessor().getPlatformManager().stop(); } catch (final StopNodeException e) { throw e; } catch (final Exception e) { throw new StopNodeException(e); } } @Override @AvailableOnStoppedNode public Platform getPlatform() throws PlatformNotFoundException { ServiceAccessor platformAccessor; try { platformAccessor = getServiceAccessor(); } catch (final Exception e) { throw new PlatformNotFoundException(e); } final PlatformService platformService = platformAccessor.getPlatformService(); final GetPlatformContent transactionContent = new GetPlatformContent(platformService); try { transactionContent.execute(); } catch (final SBonitaException e) { throw new PlatformNotFoundException(e); } final SPlatform sPlatform = transactionContent.getResult(); return ModelConvertor.toPlatform(sPlatform, platformAccessor.getPlatformService().getSPlatformProperties()); } @Override @CustomTransactions @AvailableOnStoppedNode public boolean isPlatformCreated() { try { final ServiceAccessor serviceAccessor = getServiceAccessor(); final PlatformService platformService = serviceAccessor.getPlatformService(); return serviceAccessor.getTransactionService().executeInTransaction(platformService::isPlatformCreated); } catch (Exception e) { throw new BonitaRuntimeException(e); } } @Override @CustomTransactions @AvailableOnStoppedNode public PlatformState getPlatformState() { try { return getServiceAccessor().getPlatformManager().getState(); } catch (Exception e) { throw new IllegalStateException(e); } } /** * @return true if the current node is started, false otherwise */ @Override @AvailableOnStoppedNode public boolean isNodeStarted() { return getPlatformState() == PlatformState.STARTED; } @Override public void rescheduleErroneousTriggers() throws UpdateException { try { getServiceAccessor().getSchedulerService().rescheduleErroneousTriggers(); } catch (final Exception e) { throw new UpdateException(e); } } @Override public Map getClientPlatformConfigurations() { return getBonitaHomeServer().getClientPlatformConfigurations(); } @Override public Map getClientTenantConfigurations() { return getBonitaHomeServer().getTenantPortalConfigurations(); } @Override public byte[] getClientTenantConfiguration(String file) { return getBonitaHomeServer().getTenantPortalConfiguration(file); } protected BonitaHomeServer getBonitaHomeServer() { return BonitaHomeServer.getInstance(); } @Override public void updateClientTenantConfigurationFile(String file, byte[] content) throws UpdateException { getBonitaHomeServer().updateTenantPortalConfigurationFile(file, content); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/PlatformCommandAPIImpl.java ================================================ /** * 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.api.impl; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.*; import java.io.Serializable; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.PlatformCommandAPI; import org.bonitasoft.engine.api.impl.transaction.platform.DeleteSPlatformCommand; import org.bonitasoft.engine.api.impl.transaction.platform.GetSPlatformCommands; import org.bonitasoft.engine.api.impl.transaction.platform.UpdateSPlatformCommand; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.classloader.SClassLoaderException; import org.bonitasoft.engine.command.CommandCriterion; import org.bonitasoft.engine.command.CommandDescriptor; import org.bonitasoft.engine.command.CommandExecutionException; import org.bonitasoft.engine.command.CommandNotFoundException; import org.bonitasoft.engine.command.CommandParameterizationException; import org.bonitasoft.engine.command.CommandUpdater; import org.bonitasoft.engine.command.DependencyNotFoundException; import org.bonitasoft.engine.command.RuntimeCommand; import org.bonitasoft.engine.command.SCommandNotFoundException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.dependency.DependencyService; import org.bonitasoft.engine.dependency.SDependencyException; import org.bonitasoft.engine.dependency.SDependencyNotFoundException; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.platform.command.PlatformCommandService; import org.bonitasoft.engine.platform.command.SPlatformCommandGettingException; import org.bonitasoft.engine.platform.command.SPlatformCommandNotFoundException; import org.bonitasoft.engine.platform.command.model.SPlatformCommand; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; /** * @author Matthieu Chaffotte * @author Zhang Bole * @author Emmanuel Duchastenier * @author Celine Souchet */ public class PlatformCommandAPIImpl implements PlatformCommandAPI { private static ServiceAccessor getServiceAccessor() throws RetrieveException { try { return ServiceAccessorFactory.getInstance().createServiceAccessor(); } catch (final Exception e) { throw new RetrieveException(e); } } @Override public void addDependency(final String name, final byte[] jar) throws CreationException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DependencyService dependencyService = serviceAccessor.getPlatformDependencyService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); try { dependencyService.createMappedDependency(name, jar, name, GLOBAL_ID, GLOBAL_TYPE); classLoaderService.refreshClassLoaderAfterUpdate(GLOBAL); } catch (SDependencyException | SClassLoaderException e) { throw new CreationException(e); } } @Override public void removeDependency(final String name) throws DependencyNotFoundException, DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DependencyService dependencyService = serviceAccessor.getPlatformDependencyService(); ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); try { dependencyService.deleteDependency(name); classLoaderService.refreshClassLoaderAfterUpdate(GLOBAL); } catch (final SDependencyNotFoundException e) { throw new DependencyNotFoundException(e); } catch (final SBonitaException e) { throw new DeletionException(e); } } @Override public CommandDescriptor register(final String name, final String description, final String implementation) throws CreationException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final PlatformCommandService platformCommandService = serviceAccessor.getPlatformCommandService(); try { platformCommandService.getPlatformCommand(name); throw new AlreadyExistsException("A command with name \"" + name + "\" already exists"); } catch (SPlatformCommandNotFoundException ignored) { } catch (SPlatformCommandGettingException e) { throw new CreationException("Unable to create the platform command", e); } final SPlatformCommand sPlatformCommand = new SPlatformCommand(name, description, implementation); try { platformCommandService.create(sPlatformCommand); return ModelConvertor.toCommandDescriptor(sPlatformCommand); } catch (final SBonitaException e) { throw new CreationException(e); } } @Override public Serializable execute(final String platformCommandName, final Map parameters) throws CommandNotFoundException, CommandParameterizationException, CommandExecutionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final PlatformCommandService platformCommandService = serviceAccessor.getPlatformCommandService(); try { SPlatformCommand sPlatformCommand = platformCommandService.getPlatformCommand(platformCommandName); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); RuntimeCommand command = (RuntimeCommand) contextClassLoader .loadClass(sPlatformCommand.getImplementation()).newInstance(); return command.execute(parameters, serviceAccessor); } catch (final SPlatformCommandNotFoundException e) { throw new CommandNotFoundException(e); } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException e) { throw new CommandParameterizationException(e); } catch (final SBonitaException e) { throw new CommandExecutionException(e); } } @Override public void unregister(final String platformCommandName) throws CommandNotFoundException, DeletionException { if (platformCommandName == null) { throw new DeletionException("Command name can not be null!"); } try { final ServiceAccessor serviceAccessor = getServiceAccessor(); final PlatformCommandService platformCommandService = serviceAccessor.getPlatformCommandService(); final DeleteSPlatformCommand deletePlatformCommand = new DeleteSPlatformCommand(platformCommandService, platformCommandName); deletePlatformCommand.execute(); } catch (final SCommandNotFoundException scnfe) { throw new CommandNotFoundException(scnfe); } catch (final SBonitaException sbe) { throw new DeletionException(sbe); } } @Override public CommandDescriptor getCommand(final String platformCommandName) throws CommandNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final PlatformCommandService platformCommandService = serviceAccessor.getPlatformCommandService(); try { return ModelConvertor.toCommandDescriptor(platformCommandService.getPlatformCommand(platformCommandName)); } catch (final SBonitaException e) { throw new CommandNotFoundException(e); } } @Override public List getCommands(final int startIndex, final int maxResults, final CommandCriterion sort) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final PlatformCommandService platformCommandService = serviceAccessor.getPlatformCommandService(); try { final GetSPlatformCommands getPlatformCommands = new GetSPlatformCommands(platformCommandService, startIndex, maxResults, sort); getPlatformCommands.execute(); return ModelConvertor.toPlatformCommandDescriptors(getPlatformCommands.getResult()); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public void update(final String platformCommandName, final CommandUpdater updater) throws UpdateException { if (updater == null || updater.getFields().isEmpty()) { throw new UpdateException("The update descriptor does not contain field updates"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final PlatformCommandService platformCommandService = serviceAccessor.getPlatformCommandService(); try { final UpdateSPlatformCommand updatePlatformCommand = new UpdateSPlatformCommand(platformCommandService, platformCommandName, updater); updatePlatformCommand.execute(); } catch (final SCommandNotFoundException scnfe) { throw new UpdateException(scnfe); } catch (final SBonitaException e) { throw new UpdateException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/PlatformLoginAPIImpl.java ================================================ /** * 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.api.impl; import java.util.Date; import org.bonitasoft.engine.api.PlatformLoginAPI; import org.bonitasoft.engine.api.impl.transaction.CustomTransactions; import org.bonitasoft.engine.core.platform.login.PlatformLoginService; import org.bonitasoft.engine.core.platform.login.SInvalidPlatformCredentialsException; import org.bonitasoft.engine.core.platform.login.SPlatformLoginException; import org.bonitasoft.engine.platform.InvalidPlatformCredentialsException; import org.bonitasoft.engine.platform.PlatformLoginException; import org.bonitasoft.engine.platform.PlatformLogoutException; import org.bonitasoft.engine.platform.session.SSessionNotFoundException; import org.bonitasoft.engine.platform.session.model.SPlatformSession; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.engine.session.SessionNotFoundException; import org.bonitasoft.engine.session.impl.PlatformSessionImpl; /** * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros */ public class PlatformLoginAPIImpl implements PlatformLoginAPI { @Override @CustomTransactions @AvailableOnStoppedNode public PlatformSession login(final String userName, final String password) throws PlatformLoginException { ServiceAccessor serviceAccessor; try { serviceAccessor = ServiceAccessorFactory.getInstance().createServiceAccessor(); } catch (final Exception e) { throw new PlatformLoginException(e); } final PlatformLoginService platformLoginService = serviceAccessor.getPlatformLoginService(); final SPlatformSession platformSession; try { platformSession = platformLoginService.login(userName, password); } catch (SPlatformLoginException e) { throw new PlatformLoginException(e); } catch (SInvalidPlatformCredentialsException ignored) { throw new InvalidPlatformCredentialsException("Wrong username of password"); } final long id = platformSession.getId(); final Date creationDate = platformSession.getCreationDate(); final long duration = platformSession.getDuration(); final long userId = platformSession.getUserId(); return new PlatformSessionImpl(id, creationDate, duration, userName, userId); } @Override @CustomTransactions @AvailableOnStoppedNode public void logout(final PlatformSession session) throws PlatformLogoutException, SessionNotFoundException { ServiceAccessor serviceAccessor; try { serviceAccessor = ServiceAccessorFactory.getInstance().createServiceAccessor(); } catch (final Exception e) { throw new PlatformLogoutException(e); } final PlatformLoginService platformLoginService = serviceAccessor.getPlatformLoginService(); try { platformLoginService.logout(session.getId()); } catch (final SSessionNotFoundException e) { throw new SessionNotFoundException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ProcessAPIImpl.java ================================================ /** * 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.api.impl; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.singletonMap; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import static org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance.EXECUTION_DATE; import static org.bonitasoft.engine.search.AbstractSearchEntity.search; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.Callable; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.SActorNotFoundException; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilder; import org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilderFactory; import org.bonitasoft.engine.api.DocumentAPI; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.impl.connector.ConnectorReseter; import org.bonitasoft.engine.api.impl.connector.ResetAllFailedConnectorStrategy; import org.bonitasoft.engine.api.impl.flownode.FlowNodeRetrier; import org.bonitasoft.engine.api.impl.transaction.CustomTransactions; import org.bonitasoft.engine.api.impl.transaction.activity.GetArchivedActivityInstance; import org.bonitasoft.engine.api.impl.transaction.activity.GetArchivedActivityInstances; import org.bonitasoft.engine.api.impl.transaction.activity.GetNumberOfActivityInstance; import org.bonitasoft.engine.api.impl.transaction.actor.*; import org.bonitasoft.engine.api.impl.transaction.category.*; import org.bonitasoft.engine.api.impl.transaction.connector.GetConnectorImplementation; import org.bonitasoft.engine.api.impl.transaction.event.GetEventInstances; import org.bonitasoft.engine.api.impl.transaction.expression.EvaluateExpressionsDefinitionLevel; import org.bonitasoft.engine.api.impl.transaction.expression.EvaluateExpressionsInstanceLevel; import org.bonitasoft.engine.api.impl.transaction.expression.EvaluateExpressionsInstanceLevelAndArchived; import org.bonitasoft.engine.api.impl.transaction.flownode.SetExpectedEndDate; import org.bonitasoft.engine.api.impl.transaction.identity.GetSUser; import org.bonitasoft.engine.api.impl.transaction.process.*; import org.bonitasoft.engine.api.impl.transaction.task.*; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.bpm.actor.*; import org.bonitasoft.engine.bpm.actor.ActorUpdater.ActorField; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory; import org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.engine.bpm.category.CategoryCriterion; import org.bonitasoft.engine.bpm.category.CategoryNotFoundException; import org.bonitasoft.engine.bpm.category.CategoryUpdater; import org.bonitasoft.engine.bpm.category.CategoryUpdater.CategoryField; import org.bonitasoft.engine.bpm.comment.ArchivedComment; import org.bonitasoft.engine.bpm.comment.Comment; import org.bonitasoft.engine.bpm.connector.*; import org.bonitasoft.engine.bpm.contract.ContractDefinition; import org.bonitasoft.engine.bpm.contract.ContractViolationException; import org.bonitasoft.engine.bpm.contract.validation.ContractValidator; import org.bonitasoft.engine.bpm.contract.validation.ContractValidatorFactory; import org.bonitasoft.engine.bpm.data.*; import org.bonitasoft.engine.bpm.document.*; import org.bonitasoft.engine.bpm.flownode.*; import org.bonitasoft.engine.bpm.parameter.ParameterCriterion; import org.bonitasoft.engine.bpm.parameter.ParameterInstance; import org.bonitasoft.engine.bpm.process.*; import org.bonitasoft.engine.bpm.process.impl.ProcessInstanceUpdater; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.bpm.supervisor.SupervisorNotFoundException; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.classloader.SClassLoaderException; import org.bonitasoft.engine.commons.ExceptionUtils; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.category.CategoryService; import org.bonitasoft.engine.core.category.exception.SCategoryAlreadyExistsException; import org.bonitasoft.engine.core.category.exception.SCategoryInProcessAlreadyExistsException; import org.bonitasoft.engine.core.category.exception.SCategoryNotFoundException; import org.bonitasoft.engine.core.category.model.SCategory; import org.bonitasoft.engine.core.category.model.SProcessCategoryMapping; import org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilder; import org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilderFactory; import org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilderFactory; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.connector.ConnectorResult; import org.bonitasoft.engine.core.connector.ConnectorService; import org.bonitasoft.engine.core.connector.exception.SConnectorException; import org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor; import org.bonitasoft.engine.core.contract.data.ContractDataService; import org.bonitasoft.engine.core.contract.data.SContractDataNotFoundException; import org.bonitasoft.engine.core.data.instance.TransientDataService; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.filter.FilterResult; import org.bonitasoft.engine.core.filter.UserFilterService; import org.bonitasoft.engine.core.filter.exception.SUserFilterExecutionException; import org.bonitasoft.engine.core.operation.OperationService; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.process.comment.api.SCommentNotFoundException; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.comment.model.SComment; import org.bonitasoft.engine.core.process.comment.model.archive.SAComment; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; 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.builder.event.trigger.SThrowMessageEventTriggerDefinitionBuilder; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowMessageEventTriggerDefinitionBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowSignalEventTriggerDefinitionBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SCatchEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SIntermediateCatchEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowSignalEventTriggerDefinition; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.*; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceDeletionException; 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.archive.SAFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.data.instance.api.ParentContainerResolver; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SADataInstance; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.exception.*; import org.bonitasoft.engine.execution.ProcessInstanceInterruptor; import org.bonitasoft.engine.execution.SUnreleasableTaskException; import org.bonitasoft.engine.execution.event.EventsHandler; import org.bonitasoft.engine.execution.job.JobNameBuilder; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.execution.work.BPMWorkFactory; import org.bonitasoft.engine.expression.*; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.form.FormMapping; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserNotFoundException; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.job.FailedJob; import org.bonitasoft.engine.lock.BonitaLock; import org.bonitasoft.engine.lock.LockService; import org.bonitasoft.engine.lock.SLockException; import org.bonitasoft.engine.lock.SLockTimeoutException; import org.bonitasoft.engine.log.LogMessageBuilder; import org.bonitasoft.engine.mdc.FlowNodeInstanceMDC; import org.bonitasoft.engine.message.MessagesHandlingService; import org.bonitasoft.engine.operation.LeftOperand; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.operation.OperationBuilder; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderAndField; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.resources.BARResourceType; import org.bonitasoft.engine.resources.SBARResource; import org.bonitasoft.engine.scheduler.JobService; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; import org.bonitasoft.engine.scheduler.model.SFailedJob; import org.bonitasoft.engine.scheduler.model.SJobParameter; import org.bonitasoft.engine.search.*; import org.bonitasoft.engine.search.activity.SearchActivityInstances; import org.bonitasoft.engine.search.activity.SearchArchivedActivityInstances; import org.bonitasoft.engine.search.comment.SearchArchivedComments; import org.bonitasoft.engine.search.comment.SearchComments; import org.bonitasoft.engine.search.comment.SearchCommentsInvolvingUser; import org.bonitasoft.engine.search.comment.SearchCommentsManagedBy; import org.bonitasoft.engine.search.connector.SearchArchivedConnectorInstance; import org.bonitasoft.engine.search.descriptor.*; import org.bonitasoft.engine.search.events.trigger.SearchTimerEventTriggerInstances; import org.bonitasoft.engine.search.flownode.SearchArchivedFlowNodeInstances; import org.bonitasoft.engine.search.flownode.SearchFlowNodeInstances; import org.bonitasoft.engine.search.identity.SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo; import org.bonitasoft.engine.search.identity.SearchUsersWhoCanStartProcessDeploymentInfo; import org.bonitasoft.engine.search.impl.SearchFilter; import org.bonitasoft.engine.search.impl.SearchResultImpl; import org.bonitasoft.engine.search.process.*; import org.bonitasoft.engine.search.supervisor.SearchArchivedHumanTasksSupervisedBy; import org.bonitasoft.engine.search.supervisor.SearchProcessDeploymentInfosSupervised; import org.bonitasoft.engine.search.supervisor.SearchSupervisors; import org.bonitasoft.engine.search.task.SearchArchivedTasks; import org.bonitasoft.engine.search.task.SearchArchivedTasksManagedBy; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.engine.session.model.SSession; import org.bonitasoft.engine.supervisor.mapping.SSupervisorDeletionException; import org.bonitasoft.engine.supervisor.mapping.SSupervisorNotFoundException; import org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; import org.bonitasoft.engine.transaction.UserTransactionService; import org.bonitasoft.engine.work.WorkDescriptor; import org.bonitasoft.engine.work.WorkService; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Zhao Na * @author Zhang Bole * @author Emmanuel Duchastenier * @author Celine Souchet * @author Arthur Freycon * @author Haroun EL ALAMI */ @Slf4j public class ProcessAPIImpl implements ProcessAPI { private static final int BATCH_SIZE = 500; private static final String CONTAINER_TYPE_PROCESS_INSTANCE = "PROCESS_INSTANCE"; private static final String CONTAINER_TYPE_ACTIVITY_INSTANCE = "ACTIVITY_INSTANCE"; private static final String PENDING_OR_ASSIGNED = "PendingOrAssigned"; private static final String PENDING_OR_ASSIGNED_OR_ASSIGNED_TO_OTHERS = "PendingOrAssignedOrAssignedToOthers"; protected final ProcessConfigurationAPIImpl processConfigurationAPI; private final ProcessManagementAPIImplDelegate processManagementAPIImplDelegate; private final DocumentAPI documentAPI; private final TaskInvolvementDelegate taskInvolvementDelegate; private final ProcessInvolvementDelegate processInvolvementDelegate; private final ProcessDeploymentAPIDelegate processDeploymentAPIDelegate; public ProcessAPIImpl() { this(new ProcessManagementAPIImplDelegate(), new DocumentAPIImpl(), new ProcessConfigurationAPIImpl(), new TaskInvolvementDelegate(), new ProcessInvolvementDelegate()); } public ProcessAPIImpl(final ProcessManagementAPIImplDelegate processManagementAPIDelegate, final DocumentAPI documentAPI, ProcessConfigurationAPIImpl processConfigurationAPI, TaskInvolvementDelegate taskInvolvementDelegate, ProcessInvolvementDelegate processInvolvementDelegate) { this.processManagementAPIImplDelegate = processManagementAPIDelegate; this.documentAPI = documentAPI; this.processConfigurationAPI = processConfigurationAPI; this.taskInvolvementDelegate = taskInvolvementDelegate; this.processInvolvementDelegate = processInvolvementDelegate; this.processDeploymentAPIDelegate = ProcessDeploymentAPIDelegate.getInstance(); } @Override public SearchResult searchHumanTaskInstances(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); return AbstractHumanTaskInstanceSearchEntity .searchHumanTaskInstance(searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(), searchOptions, flowNodeStateManager, activityInstanceService::getNumberOfHumanTasks, activityInstanceService::searchHumanTasks) .search(); } @Override public void deleteProcessDefinition(final long processDefinitionId) throws DeletionException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 1); builder.filter(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID, processDefinitionId); final SearchOptions searchOptions = builder.done(); try { final boolean hasOpenProcessInstances = searchProcessInstances(getServiceAccessor(), searchOptions) .getCount() > 0; checkIfItIsPossibleToDeleteProcessInstance(processDefinitionId, hasOpenProcessInstances); final boolean hasArchivedProcessInstances = searchArchivedProcessInstancesInAllStates(searchOptions) .getCount() > 0; checkIfItIsPossibleToDeleteProcessInstance(processDefinitionId, hasArchivedProcessInstances); removeAllCategoriesFromProcessDefinition(processDefinitionId); deleteAllSupervisorsOfProcess(processDefinitionId); processManagementAPIImplDelegate.deleteProcessDefinition(processDefinitionId); } catch (final SProcessDefinitionNotFoundException spdnfe) { throw new DeletionException(new ProcessDefinitionNotFoundException(spdnfe)); } catch (final Exception e) { throw new DeletionException(e); } } private void checkIfItIsPossibleToDeleteProcessInstance(final long processDefinitionId, final boolean canThrowException) throws DeletionException { if (canThrowException) { throw new DeletionException("Some active process instances are still found, process #" + processDefinitionId + " can't be deleted."); } } @Override public void deleteProcessDefinitions(final List processDefinitionIds) throws DeletionException { for (final Long processDefinitionId : processDefinitionIds) { deleteProcessDefinition(processDefinitionId); } } private void releaseLocks(final LockService lockService, final List locks) { if (locks == null) { return; } for (final BonitaLock lock : locks) { try { lockService.unlock(lock); } catch (final SLockException e) { logError(e); } } } @Override public ProcessDefinition deployAndEnableProcess(final DesignProcessDefinition designProcessDefinition) throws ProcessDeployException, ProcessEnablementException, AlreadyExistsException, InvalidProcessDefinitionException { BusinessArchive businessArchive; try { businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition).done(); } catch (final InvalidBusinessArchiveFormatException e) { throw new InvalidProcessDefinitionException(e.getMessage()); } return deployAndEnableProcess(businessArchive); } @Override public ProcessDefinition deployAndEnableProcess(final BusinessArchive businessArchive) throws ProcessDeployException, ProcessEnablementException, AlreadyExistsException { return processDeploymentAPIDelegate.deployAndEnableProcess(businessArchive); } @Override public ProcessDefinition deploy(final DesignProcessDefinition designProcessDefinition) throws AlreadyExistsException, ProcessDeployException { try { final BusinessArchive businessArchive = new BusinessArchiveBuilder().createNewBusinessArchive() .setProcessDefinition(designProcessDefinition) .done(); return deploy(businessArchive); } catch (final InvalidBusinessArchiveFormatException e) { throw new ProcessDeployException(e); } } @Override public ProcessDefinition deploy(final BusinessArchive businessArchive) throws ProcessDeployException, AlreadyExistsException { return processDeploymentAPIDelegate.deploy(businessArchive); } @Override public void importActorMapping(final long pDefinitionId, final byte[] actorMappingXML) throws ActorMappingImportException { if (actorMappingXML != null) { importActorMapping(pDefinitionId, new String(actorMappingXML, UTF_8)); } } @Override public byte[] exportBarProcessContentUnderHome(final long processDefinitionId) throws ProcessExportException { File barExport = null; try { barExport = File.createTempFile("barExport", ".bar"); barExport.delete(); final BusinessArchive export = getServiceAccessor().getBusinessArchiveService().export(processDefinitionId); BusinessArchiveFactory.writeBusinessArchiveToFile(export, barExport); return FileUtils.readFileToByteArray(barExport); } catch (IOException | InvalidBusinessArchiveFormatException | SBonitaException e) { throw new ProcessExportException(e); } finally { if (barExport != null && barExport.exists()) { barExport.delete(); } } } @Override public void disableAndDeleteProcessDefinition(final long processDefinitionId) throws ProcessDefinitionNotFoundException, ProcessActivationException, DeletionException { disableProcess(processDefinitionId); deleteProcessDefinition(processDefinitionId); } @Override public void disableProcess(final long processDefinitionId) throws ProcessDefinitionNotFoundException, ProcessActivationException { try { processManagementAPIImplDelegate.disableProcess(processDefinitionId); } catch (final SProcessDefinitionNotFoundException e) { throw new ProcessDefinitionNotFoundException(e); } catch (final SBonitaException e) { throw new ProcessActivationException(e); } } @Override public void enableProcess(final long processDefinitionId) throws ProcessDefinitionNotFoundException, ProcessEnablementException { processDeploymentAPIDelegate.enableProcess(processDefinitionId); } SSession getSession() { return SessionInfos.getSession(); } @Override public void executeFlowNode(final long flownodeInstanceId) throws FlowNodeExecutionException { executeFlowNode(0, flownodeInstanceId); } @Override public void executeFlowNode(final long userId, final long flownodeInstanceId) throws FlowNodeExecutionException { try { ActivityInstanceService activityInstanceService = getServiceAccessor().getActivityInstanceService(); SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(flownodeInstanceId); log.warn(String.format( "executeFlowNode was called: <%s> is forcing the execution of the flow node with name = <%s> id = <%d> and type <%s> in current state <%s: %s>. " + "Executing a flow node directly through that API method is not recommended and can affect the normal behavior of the platform. " + "If that flow node was already scheduled for execution, there is no guarantee on how that flow node will behave anymore. " + "If you are trying to execute a user task, please use executeUserTask method instead.", getLoggedUsername(), flowNodeInstance.getName(), flowNodeInstance.getId(), flowNodeInstance.getType().name(), flowNodeInstance.getStateId(), flowNodeInstance.getStateName())); executeFlowNode(userId, flownodeInstanceId, new HashMap<>(), false); } catch (final ContractViolationException | SBonitaException e) { throw new FlowNodeExecutionException(e); } } @Override public List getActivities(final long processInstanceId, final int startIndex, final int maxResults) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); try { return ModelConvertor.toActivityInstances( activityInstanceService.getActivityInstances(processInstanceId, startIndex, maxResults), flowNodeStateManager); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public long getNumberOfProcessDeploymentInfos() { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final TransactionContentWithResult transactionContentWithResult = new GetNumberOfProcessDeploymentInfos( processDefinitionService); try { transactionContentWithResult.execute(); return transactionContentWithResult.getResult(); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public ProcessDefinition getProcessDefinition(final long processDefinitionId) throws ProcessDefinitionNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final SProcessDefinition sProcessDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); return ModelConvertor.toProcessDefinition(sProcessDefinition); } catch (final SProcessDefinitionNotFoundException e) { throw new ProcessDefinitionNotFoundException(e); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } } @Override public DesignProcessDefinition getDesignProcessDefinition(final long processDefinitionId) throws ProcessDefinitionNotFoundException { try { return getServiceAccessor().getProcessDefinitionService().getDesignProcessDefinition(processDefinitionId); } catch (SBonitaReadException | SProcessDefinitionNotFoundException e) { throw new ProcessDefinitionNotFoundException(processDefinitionId, e); } } @Override public ProcessDeploymentInfo getProcessDeploymentInfo(final long processDefinitionId) throws ProcessDefinitionNotFoundException { return processDeploymentAPIDelegate.getProcessDeploymentInfo(processDefinitionId); } private void logError(final Exception e) { if (log.isErrorEnabled()) { log.error("", e); } } private void logInstanceNotFound(final SBonitaException e) { if (log.isDebugEnabled()) { log.debug(e.getMessage() + ". It may have been completed."); } } @Override public ProcessInstance getProcessInstance(final long processInstanceId) throws ProcessInstanceNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final SProcessInstance sProcessInstance = getSProcessInstance(processInstanceId); return ModelConvertor .toProcessInstances(Collections.singletonList(sProcessInstance), processDefinitionService).get(0); } catch (final SProcessInstanceNotFoundException notFound) { throw new ProcessInstanceNotFoundException(notFound); } catch (final SBonitaException e) { throw new RetrieveException(e); } } protected SProcessInstance getSProcessInstance(final long processInstanceId) throws SProcessInstanceNotFoundException, SProcessInstanceReadException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); return processInstanceService.getProcessInstance(processInstanceId); } @Override public List getArchivedProcessInstances(final long processInstanceId, final int startIndex, final int maxResults) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final GetArchivedProcessInstanceList getProcessInstanceList = new GetArchivedProcessInstanceList( processInstanceService, serviceAccessor.getProcessDefinitionService(), searchEntitiesDescriptor, processInstanceId, startIndex, maxResults); try { getProcessInstanceList.execute(); } catch (final SBonitaException e) { logError(e); throw new RetrieveException(e); } return getProcessInstanceList.getResult(); } @Override public ArchivedProcessInstance getArchivedProcessInstance(final long id) throws ArchivedProcessInstanceNotFoundException, RetrieveException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final SAProcessInstance archivedProcessInstance = processInstanceService.getArchivedProcessInstance(id); if (archivedProcessInstance == null) { throw new ArchivedProcessInstanceNotFoundException(id); } final SProcessDefinition sProcessDefinition = processDefinitionService .getProcessDefinition(archivedProcessInstance .getProcessDefinitionId()); return toArchivedProcessInstance(archivedProcessInstance, sProcessDefinition); } catch (final SBonitaException e) { throw new RetrieveException(e); } } /** * internal use for mocking purpose */ protected ArchivedProcessInstance toArchivedProcessInstance(final SAProcessInstance archivedProcessInstance, final SProcessDefinition sProcessDefinition) { return ModelConvertor.toArchivedProcessInstance(archivedProcessInstance, sProcessDefinition); } @Override public ArchivedProcessInstance getFinalArchivedProcessInstance(final long sourceProcessInstanceId) throws ArchivedProcessInstanceNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final GetLastArchivedProcessInstance getProcessInstance = new GetLastArchivedProcessInstance( processInstanceService, serviceAccessor.getProcessDefinitionService(), sourceProcessInstanceId, serviceAccessor.getSearchEntitiesDescriptor()); try { getProcessInstance.execute(); } catch (final SProcessInstanceNotFoundException e) { logInstanceNotFound(e); throw new ArchivedProcessInstanceNotFoundException(e); } catch (final SBonitaException e) { logError(e); throw new RetrieveException(e); } return getProcessInstance.getResult(); } @Override public ProcessInstance startProcess(final long processDefinitionId) throws ProcessActivationException, ProcessExecutionException { try { return startProcess(getUserId(), processDefinitionId); } catch (final ProcessDefinitionNotFoundException e) { throw new ProcessExecutionException(e); } } @Override public ProcessInstance startProcess(final long userId, final long processDefinitionId) throws ProcessDefinitionNotFoundException, ProcessExecutionException, ProcessActivationException { return startProcess(userId, processDefinitionId, null, null); } @Override public int getNumberOfActors(final long processDefinitionId) throws ProcessDefinitionNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final GetNumberOfActors getNumberofActors = new GetNumberOfActors(processDefinitionService, processDefinitionId); try { getNumberofActors.execute(); } catch (final SBonitaException e) { throw new ProcessDefinitionNotFoundException(e); } return getNumberofActors.getResult(); } @Override public List getActors(final long processDefinitionId, final int startIndex, final int maxResults, final ActorCriterion sort) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); try { final QueryOptions queryOptions = getQueryOptions(startIndex, maxResults, sort); final List actors = actorMappingService.getActors(processDefinitionId, queryOptions); return ModelConvertor.toActors(actors); } catch (final SBonitaException e) { throw new RetrieveException(e); } } private static QueryOptions getQueryOptions(int startIndex, int maxResults, ActorCriterion sort) { OrderByType order; if (sort == null) { order = OrderByType.ASC; } else { switch (sort) { case NAME_ASC: order = OrderByType.ASC; break; default: order = OrderByType.DESC; break; } } return new QueryOptions(startIndex, maxResults, SActor.class, "name", order); } @Override public List getActorMembers(final long actorId, final int startIndex, final int maxResults) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); try { final List actorMembers = actorMappingService.getActorMembers(actorId, startIndex, maxResults); return ModelConvertor.toActorMembers(actorMembers); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public long getNumberOfActorMembers(final long actorId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final GetNumberOfActorMembers numberOfActorMembers = new GetNumberOfActorMembers(actorMappingService, actorId); try { numberOfActorMembers.execute(); return numberOfActorMembers.getResult(); } catch (final SBonitaException sbe) { return 0; // FIXME throw retrieve exception } } @Override public long getNumberOfUsersOfActor(final long actorId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final GetNumberOfUsersOfActor numberOfUsersOfActor = new GetNumberOfUsersOfActor(actorMappingService, actorId); numberOfUsersOfActor.execute(); return numberOfUsersOfActor.getResult(); } @Override public long getNumberOfRolesOfActor(final long actorId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final GetNumberOfRolesOfActor numberOfRolesOfActor = new GetNumberOfRolesOfActor(actorMappingService, actorId); numberOfRolesOfActor.execute(); return numberOfRolesOfActor.getResult(); } @Override public long getNumberOfGroupsOfActor(final long actorId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final GetNumberOfGroupsOfActor numberOfGroupsOfActor = new GetNumberOfGroupsOfActor(actorMappingService, actorId); numberOfGroupsOfActor.execute(); return numberOfGroupsOfActor.getResult(); } @Override public long getNumberOfMembershipsOfActor(final long actorId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final GetNumberOfMembershipsOfActor getNumber = new GetNumberOfMembershipsOfActor(actorMappingService, actorId); getNumber.execute(); return getNumber.getResult(); } /** * {@inheritDoc} */ @Override @Deprecated(since = "9.0.0") public ActorInstance updateActor(final long actorId, final ActorUpdater descriptor) throws ActorNotFoundException, UpdateException { if (descriptor == null || descriptor.getFields().isEmpty()) { throw new UpdateException("The update descriptor does not contain field updates"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final SActorUpdateBuilder actorUpdateBuilder = BuilderFactory.get(SActorUpdateBuilderFactory.class) .createNewInstance(); final Map fields = descriptor.getFields(); for (final Entry field : fields.entrySet()) { switch (field.getKey()) { case DISPLAY_NAME: actorUpdateBuilder.updateDisplayName((String) field.getValue()); break; case DESCRIPTION: actorUpdateBuilder.updateDescription((String) field.getValue()); break; default: break; } } final EntityUpdateDescriptor updateDescriptor = actorUpdateBuilder.done(); SActor updateActor; try { updateActor = actorMappingService.updateActor(actorId, updateDescriptor); return ModelConvertor.toActorInstance(updateActor); } catch (final SActorNotFoundException e) { throw new ActorNotFoundException(e); } catch (final SBonitaException e) { throw new UpdateException(e); } } @Override public ActorMember addUserToActor(final long actorId, final long userId) throws CreationException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); try { // Check if the mapping already to throw a specific exception checkIfActorMappingForUserAlreadyExists(actorId, userId); final SActorMember actorMember = actorMappingService.addUserToActor(actorId, userId); final long processDefinitionId = actorMappingService.getActor(actorId).getScopeId(); final ActorMember clientActorMember = ModelConvertor.toActorMember(actorMember); serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(processDefinitionId, serviceAccessor); return clientActorMember; } catch (final SBonitaException sbe) { throw new CreationException(sbe); } } private void checkIfActorMappingForUserAlreadyExists(final long actorId, final long userId) throws AlreadyExistsException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); try { final SActorMember sActorMember = actorMappingService.getActorMember(actorId, userId, -1, -1); if (sActorMember != null) { throw new AlreadyExistsException("The mapping already exists for the actor id = <" + actorId + ">, the user id = <" + userId + ">"); } } catch (final SBonitaReadException e) { // Do nothing } } @Override public ActorMember addUserToActor(final String actorName, final ProcessDefinition processDefinition, final long userId) throws CreationException, ActorNotFoundException { final List actors = getActors(processDefinition.getId(), 0, Integer.MAX_VALUE, ActorCriterion.NAME_ASC); for (final ActorInstance ai : actors) { if (actorName.equals(ai.getName())) { return addUserToActor(ai.getId(), userId); } } throw new ActorNotFoundException( "Actor " + actorName + " not found in process definition " + processDefinition.getName()); } @Override public ActorMember addGroupToActor(final long actorId, final long groupId) throws CreationException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); try { // Check if the mapping already to throw a specific exception checkIfActorMappingForGroupAlreadyExists(actorId, groupId); final SActorMember actorMember = actorMappingService.addGroupToActor(actorId, groupId); final long processDefinitionId = actorMappingService.getActor(actorId).getScopeId(); final ActorMember clientActorMember = ModelConvertor.toActorMember(actorMember); serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(processDefinitionId, serviceAccessor); return clientActorMember; } catch (final SBonitaException e) { throw new CreationException(e); } } private void checkIfActorMappingForGroupAlreadyExists(final long actorId, final long groupId) throws AlreadyExistsException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); try { final SActorMember sActorMember = actorMappingService.getActorMember(actorId, -1, groupId, -1); if (sActorMember != null) { throw new AlreadyExistsException("The mapping already exists for the actor id = <" + actorId + ">, the group id = <" + groupId + ">"); } } catch (final SBonitaReadException e) { // Do nothing } } @Override public ActorMember addGroupToActor(final String actorName, final long groupId, final ProcessDefinition processDefinition) throws CreationException, ActorNotFoundException { final List actors = getActors(processDefinition.getId(), 0, Integer.MAX_VALUE, ActorCriterion.NAME_ASC); for (final ActorInstance actorInstance : actors) { if (actorName.equals(actorInstance.getName())) { return addGroupToActor(actorInstance.getId(), groupId); } } throw new ActorNotFoundException( "Actor " + actorName + " not found in process definition " + processDefinition.getName()); } @Override public ActorMember addRoleToActor(final long actorId, final long roleId) throws CreationException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); try { // Check if the mapping already to throw a specific exception checkIfActorMappingForRoleAlreadyExists(actorId, roleId); final SActorMember actorMember = actorMappingService.addRoleToActor(actorId, roleId); final long processDefinitionId = actorMappingService.getActor(actorId).getScopeId(); final ActorMember clientActorMember = ModelConvertor.toActorMember(actorMember); serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(processDefinitionId, serviceAccessor); return clientActorMember; } catch (final SBonitaException sbe) { throw new CreationException(sbe); } } private void checkIfActorMappingForRoleAlreadyExists(final long actorId, final long roleId) throws AlreadyExistsException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); try { final SActorMember sActorMember = actorMappingService.getActorMember(actorId, -1, -1, roleId); if (sActorMember != null) { throw new AlreadyExistsException("The mapping already exists for the actor id = <" + actorId + ">, the role id = <" + roleId + ">"); } } catch (final SBonitaReadException e) { // Do nothing } } @Override public ActorMember addRoleToActor(final String actorName, final ProcessDefinition processDefinition, final long roleId) throws ActorNotFoundException, CreationException { final List actors = getActors(processDefinition.getId(), 0, Integer.MAX_VALUE, ActorCriterion.NAME_ASC); for (final ActorInstance ai : actors) { if (actorName.equals(ai.getName())) { return addRoleToActor(ai.getId(), roleId); } } throw new ActorNotFoundException( "Actor " + actorName + " not found in process definition " + processDefinition.getName()); } @Override public ActorMember addRoleAndGroupToActor(final String actorName, final ProcessDefinition processDefinition, final long roleId, final long groupId) throws ActorNotFoundException, CreationException { final List actors = getActors(processDefinition.getId(), 0, Integer.MAX_VALUE, ActorCriterion.NAME_ASC); for (final ActorInstance ai : actors) { if (actorName.equals(ai.getName())) { return addRoleAndGroupToActor(ai.getId(), roleId, groupId); } } throw new ActorNotFoundException( "Actor " + actorName + " not found in process definition " + processDefinition.getName()); } @Override public ActorMember addRoleAndGroupToActor(final long actorId, final long roleId, final long groupId) throws CreationException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); try { checkIfActorMappingForMembershipAlreadyExists(actorId, roleId, groupId); final SActorMember actorMember = actorMappingService.addRoleAndGroupToActor(actorId, roleId, groupId); final long processDefinitionId = actorMappingService.getActor(actorId).getScopeId(); final ActorMember clientActorMember = ModelConvertor.toActorMember(actorMember); serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(processDefinitionId, serviceAccessor); return clientActorMember; } catch (final SBonitaException sbe) { throw new CreationException(sbe); } } private void checkIfActorMappingForMembershipAlreadyExists(final long actorId, final long roleId, final long groupId) throws AlreadyExistsException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); try { final SActorMember sActorMember = actorMappingService.getActorMember(actorId, -1, groupId, roleId); if (sActorMember != null) { throw new AlreadyExistsException( "The mapping already exists for the actor id = <" + actorId + ">, the role id = <" + roleId + ">, the group id = <" + groupId + ">"); } } catch (final SBonitaReadException e) { // Do nothing } } @Override public void removeActorMember(final long actorMemberId) throws DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final RemoveActorMember removeActorMember = new RemoveActorMember(actorMappingService, actorMemberId); // FIXME remove an actor member when process is running! try { removeActorMember.execute(); final SActorMember actorMember = removeActorMember.getResult(); final long processDefinitionId = getActor(actorMember.getActorId()).getProcessDefinitionId(); serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(processDefinitionId, serviceAccessor); } catch (final SBonitaException | ActorNotFoundException sbe) { throw new DeletionException(sbe); } } @Override public ActorInstance getActor(final long actorId) throws ActorNotFoundException { try { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final GetActor getActor = new GetActor(actorMappingService, actorId); getActor.execute(); return ModelConvertor.toActorInstance(getActor.getResult()); } catch (final SBonitaException e) { throw new ActorNotFoundException(e); } } @Override public ActivityInstance getActivityInstance(final long activityInstanceId) throws ActivityInstanceNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final SActivityInstance sActivityInstance; try { sActivityInstance = getSActivityInstance(activityInstanceId); } catch (final SActivityInstanceNotFoundException e) { throw new ActivityInstanceNotFoundException(activityInstanceId); } catch (final SBonitaException e) { throw new RetrieveException(e); } return ModelConvertor.toActivityInstance(sActivityInstance, flowNodeStateManager); } protected SActivityInstance getSActivityInstance(final long activityInstanceId) throws SActivityInstanceNotFoundException, SActivityReadException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); return activityInstanceService.getActivityInstance(activityInstanceId); } @Override public FlowNodeInstance getFlowNodeInstance(final long flowNodeInstanceId) throws FlowNodeInstanceNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); try { final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId); return ModelConvertor.toFlowNodeInstance(flowNodeInstance, flowNodeStateManager); } catch (final SFlowNodeNotFoundException e) { throw new FlowNodeInstanceNotFoundException(e); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public List getAssignedHumanTaskInstances(final long userId, final int startIndex, final int maxResults, final ActivityInstanceCriterion pagingCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForActivityInstance(pagingCriterion); final ActivityInstanceService instanceService = serviceAccessor.getActivityInstanceService(); try { final GetAssignedTasks getAssignedTasks = new GetAssignedTasks(instanceService, userId, startIndex, maxResults, orderAndField.getField(), orderAndField.getOrder()); getAssignedTasks.execute(); final List assignedTasks = getAssignedTasks.getResult(); return ModelConvertor.toHumanTaskInstances(assignedTasks, flowNodeStateManager); } catch (final SBonitaException e) { return Collections.emptyList(); } } @Override public long getNumberOfPendingHumanTaskInstances(final long userId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final ProcessDefinitionService processDefService = serviceAccessor.getProcessDefinitionService(); try { final Set actorIds = getActorsForUser(userId, actorMappingService, processDefService); if (actorIds.isEmpty()) { return 0L; } return activityInstanceService.getNumberOfPendingTasksForUser(userId, QueryOptions.countQueryOptions()); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public List getPendingHumanTaskInstances(final long userId, final int startIndex, final int maxResults, final ActivityInstanceCriterion pagingCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForActivityInstance(pagingCriterion); final ProcessDefinitionService definitionService = serviceAccessor.getProcessDefinitionService(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); try { final Set actorIds = getActorsForUser(userId, actorMappingService, definitionService); final List pendingTasks = activityInstanceService.getPendingTasks(userId, actorIds, startIndex, maxResults, orderAndField.getField(), orderAndField.getOrder()); return ModelConvertor.toHumanTaskInstances(pendingTasks, flowNodeStateManager); } catch (final SBonitaException e) { return Collections.emptyList(); } } private Set getActorsForUser(final long userId, final ActorMappingService actorMappingService, final ProcessDefinitionService definitionService) throws SBonitaReadException { final Set actorIds = new HashSet<>(); final List processDefIds = definitionService.getProcessDefinitionIds(0, Integer.MAX_VALUE); if (!processDefIds.isEmpty()) { final Set processDefinitionIds = new HashSet<>(processDefIds); final List actors = actorMappingService.getActors(processDefinitionIds, userId); for (final SActor sActor : actors) { actorIds.add(sActor.getId()); } } return actorIds; } @Override public ArchivedActivityInstance getArchivedActivityInstance(final long sourceActivityInstanceId) throws ActivityInstanceNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final GetArchivedActivityInstance getActivityInstance = new GetArchivedActivityInstance(activityInstanceService, sourceActivityInstanceId); try { getActivityInstance.execute(); } catch (final SActivityInstanceNotFoundException e) { throw new ActivityInstanceNotFoundException(sourceActivityInstanceId, e); } catch (final SBonitaException e) { throw new RetrieveException(e); } return ModelConvertor.toArchivedActivityInstance(getActivityInstance.getResult(), flowNodeStateManager); } @Override public ArchivedFlowNodeInstance getArchivedFlowNodeInstance(final long archivedFlowNodeInstanceId) throws ArchivedFlowNodeInstanceNotFoundException, RetrieveException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); try { final SAFlowNodeInstance archivedFlowNodeInstance = activityInstanceService .getArchivedFlowNodeInstance(archivedFlowNodeInstanceId); return ModelConvertor.toArchivedFlowNodeInstance(archivedFlowNodeInstance, flowNodeStateManager); } catch (final SFlowNodeNotFoundException e) { throw new ArchivedFlowNodeInstanceNotFoundException(archivedFlowNodeInstanceId); } catch (final SFlowNodeReadException e) { throw new RetrieveException(e); } } @Override public List getProcessInstances(final int startIndex, final int maxResults, final ProcessInstanceCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForProcessInstance(criterion); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(startIndex, maxResults); searchOptionsBuilder.sort(orderAndField.getField(), Order.valueOf(orderAndField.getOrder().name())); List result; try { result = searchProcessInstances(serviceAccessor, searchOptionsBuilder.done()).getResult(); } catch (final SBonitaException e) { result = Collections.emptyList(); } return result; } @Override public long getNumberOfProcessInstances() { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); try { final TransactionContentWithResult transactionContent = new GetNumberOfProcessInstance( processInstanceService, processDefinitionService, searchEntitiesDescriptor); transactionContent.execute(); return transactionContent.getResult(); } catch (final SBonitaException e) { throw new RetrieveException(e); } } protected SearchResult searchProcessInstances(final ServiceAccessor serviceAccessor, final SearchOptions searchOptions) throws SBonitaException { final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchProcessInstances searchProcessInstances = new SearchProcessInstances(processInstanceService, searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), searchOptions, processDefinitionService); searchProcessInstances.execute(); return searchProcessInstances.getResult(); } @Deprecated @Override public List getArchivedProcessInstances(final int startIndex, final int maxResults, final ProcessInstanceCriterion criterion) { return getCompletedProcessInstances(startIndex, maxResults, criterion); } @Override public List getCompletedProcessInstances(final int startIndex, final int maxResults, final ProcessInstanceCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForProcessInstance(criterion); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(startIndex, maxResults); searchOptionsBuilder.sort(orderAndField.getField(), Order.valueOf(orderAndField.getOrder().name())); searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.STATE_ID, ProcessInstanceState.COMPLETED.getId()); searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.CALLER_ID, -1); final SearchArchivedProcessInstances searchArchivedProcessInstances = searchArchivedProcessInstances( serviceAccessor, searchOptionsBuilder.done()); try { searchArchivedProcessInstances.execute(); } catch (final SBonitaException e) { throw new RetrieveException(e); } return searchArchivedProcessInstances.getResult().getResult(); } private SearchArchivedProcessInstances searchArchivedProcessInstances(final ServiceAccessor serviceAccessor, final SearchOptions searchOptions) throws RetrieveException { final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); return new SearchArchivedProcessInstances(processInstanceService, serviceAccessor.getProcessDefinitionService(), searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptions); } @Deprecated @Override public long getNumberOfArchivedProcessInstances() { return getNumberOfCompletedProcessInstances(); } @Override public long getNumberOfCompletedProcessInstances() { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); try { final SAProcessInstanceBuilderFactory saProcessInstanceBuilder = BuilderFactory .get(SAProcessInstanceBuilderFactory.class); final List filterOptions = new ArrayList<>(2); filterOptions.add(new FilterOption(SAProcessInstance.class, saProcessInstanceBuilder.getStateIdKey(), ProcessInstanceState.COMPLETED.getId())); filterOptions.add(new FilterOption(SAProcessInstance.class, saProcessInstanceBuilder.getCallerIdKey(), -1)); final QueryOptions queryOptions = new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS, Collections.emptyList(), filterOptions, null); return processInstanceService.getNumberOfArchivedProcessInstances(queryOptions); } catch (final SBonitaException e) { logError(e); throw new RetrieveException(e); } } @Override public SearchResult searchArchivedProcessInstances(final SearchOptions searchOptions) throws RetrieveException, SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchArchivedProcessInstancesWithoutSubProcess searchArchivedProcessInstances = new SearchArchivedProcessInstancesWithoutSubProcess( processInstanceService, serviceAccessor.getProcessDefinitionService(), searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptions); try { searchArchivedProcessInstances.execute(); } catch (final SBonitaException e) { throw new SearchException(e); } return searchArchivedProcessInstances.getResult(); } @Override public SearchResult searchArchivedProcessInstancesInAllStates( final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchArchivedProcessInstances searchArchivedProcessInstances = new SearchArchivedProcessInstances( processInstanceService, serviceAccessor.getProcessDefinitionService(), searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptions); try { searchArchivedProcessInstances.execute(); } catch (final SBonitaException e) { throw new SearchException(e); } return searchArchivedProcessInstances.getResult(); } @Override public List getOpenActivityInstances(final long processInstanceId, final int startIndex, final int maxResults, final ActivityInstanceCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); try { final int totalNumber = activityInstanceService.getNumberOfOpenActivityInstances(processInstanceId); // If there are no instances, return an empty list: if (totalNumber == 0) { return Collections.emptyList(); } final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForActivityInstance(criterion); return ModelConvertor.toActivityInstances( activityInstanceService.getOpenActivityInstances(processInstanceId, startIndex, maxResults, orderAndField.getField(), orderAndField.getOrder()), flowNodeStateManager); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public List getArchivedActivityInstances(final long processInstanceId, final int startIndex, final int maxResults, final ActivityInstanceCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); return getArchivedActivityInstances(processInstanceId, startIndex, maxResults, criterion, serviceAccessor); } private List getArchivedActivityInstances(final long processInstanceId, final int pageIndex, final int numberPerPage, final ActivityInstanceCriterion pagingCriterion, final ServiceAccessor serviceAccessor) throws RetrieveException { final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForActivityInstance(pagingCriterion); final GetArchivedActivityInstances getActivityInstances = new GetArchivedActivityInstances( activityInstanceService, processInstanceId, pageIndex, numberPerPage, orderAndField.getField(), orderAndField.getOrder()); try { getActivityInstances.execute(); } catch (final SBonitaException e) { logError(e); throw new RetrieveException(e); } return ModelConvertor.toArchivedActivityInstances(getActivityInstances.getResult(), flowNodeStateManager); } @Override public int getNumberOfOpenedActivityInstances(final long processInstanceId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final TransactionContentWithResult transactionContentWithResult = new GetNumberOfActivityInstance( processInstanceId, activityInstanceService); try { transactionContentWithResult.execute(); return transactionContentWithResult.getResult(); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public Category createCategory(final String name, final String description) throws CreationException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final CategoryService categoryService = serviceAccessor.getCategoryService(); try { final CreateCategory createCategory = new CreateCategory(name, description, categoryService); createCategory.execute(); return ModelConvertor.toCategory(createCategory.getResult()); } catch (final SCategoryAlreadyExistsException scaee) { throw new AlreadyExistsException(scaee); } catch (final SBonitaException e) { throw new CreationException("Category create exception!", e); } } @Override public Category getCategory(final long categoryId) throws CategoryNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final CategoryService categoryService = serviceAccessor.getCategoryService(); try { final GetCategory getCategory = new GetCategory(categoryService, categoryId); getCategory.execute(); final SCategory sCategory = getCategory.getResult(); return ModelConvertor.toCategory(sCategory); } catch (final SBonitaException sbe) { throw new CategoryNotFoundException(sbe); } } @Override public long getNumberOfCategories() { final CategoryService categoryService = getServiceAccessor().getCategoryService(); try { final GetNumberOfCategories getNumberOfCategories = new GetNumberOfCategories(categoryService); getNumberOfCategories.execute(); return getNumberOfCategories.getResult(); } catch (final SBonitaException e) { return 0; } } @Override public List getCategories(final int startIndex, final int maxResults, final CategoryCriterion sortCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final CategoryService categoryService = serviceAccessor.getCategoryService(); String field; OrderByType order; switch (sortCriterion) { case NAME_ASC: field = SCategory.NAME; order = OrderByType.ASC; break; case NAME_DESC: field = SCategory.NAME; order = OrderByType.DESC; break; default: throw new IllegalStateException(); } try { final GetCategories getCategories = new GetCategories(startIndex, maxResults, field, categoryService, order); getCategories.execute(); return ModelConvertor.toCategories(getCategories.getResult()); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public void addCategoriesToProcess(final long processDefinitionId, final List categoryIds) throws CreationException { try { final CategoryService categoryService = getServiceAccessor().getCategoryService(); final ProcessDefinitionService processDefinitionService = getServiceAccessor() .getProcessDefinitionService(); for (final Long categoryId : categoryIds) { new AddProcessDefinitionToCategory(categoryId, processDefinitionId, categoryService, processDefinitionService).execute(); } } catch (final SCategoryInProcessAlreadyExistsException scipaee) { throw new AlreadyExistsException(scipaee); } catch (final SBonitaException sbe) { throw new CreationException(sbe); } } @Override public void removeCategoriesFromProcess(final long processDefinitionId, final List categoryIds) throws DeletionException { try { final CategoryService categoryService = getServiceAccessor().getCategoryService(); final TransactionContent transactionContent = new RemoveCategoriesFromProcessDefinition(processDefinitionId, categoryIds, categoryService); transactionContent.execute(); } catch (final SBonitaException sbe) { throw new DeletionException(sbe); } } @Override public void addProcessDefinitionToCategory(final long categoryId, final long processDefinitionId) throws CreationException { try { final CategoryService categoryService = getServiceAccessor().getCategoryService(); final ProcessDefinitionService processDefinitionService = getServiceAccessor() .getProcessDefinitionService(); final TransactionContent transactionContent = new AddProcessDefinitionToCategory(categoryId, processDefinitionId, categoryService, processDefinitionService); transactionContent.execute(); } catch (final SCategoryInProcessAlreadyExistsException cipaee) { throw new AlreadyExistsException(cipaee); } catch (final SBonitaException sbe) { throw new CreationException(sbe); } } @Override public void addProcessDefinitionsToCategory(final long categoryId, final List processDefinitionIds) throws CreationException { try { final CategoryService categoryService = getServiceAccessor().getCategoryService(); final ProcessDefinitionService processDefinitionService = getServiceAccessor() .getProcessDefinitionService(); for (final Long processDefinitionId : processDefinitionIds) { new AddProcessDefinitionToCategory(categoryId, processDefinitionId, categoryService, processDefinitionService).execute(); } } catch (final SCategoryInProcessAlreadyExistsException cipaee) { throw new AlreadyExistsException(cipaee); } catch (final SBonitaException sbe) { throw new CreationException(sbe); } } @Override public long getNumberOfCategories(final long processDefinitionId) { try { final ServiceAccessor serviceAccessor = getServiceAccessor(); final CategoryService categoryService = serviceAccessor.getCategoryService(); final GetNumberOfCategoriesOfProcess getNumberOfCategoriesOfProcess = new GetNumberOfCategoriesOfProcess( categoryService, processDefinitionId); getNumberOfCategoriesOfProcess.execute(); return getNumberOfCategoriesOfProcess.getResult(); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public long getNumberOfProcessDefinitionsOfCategory(final long categoryId) { try { final CategoryService categoryService = getServiceAccessor().getCategoryService(); return categoryService.getNumberOfProcessDeploymentInfosOfCategory(categoryId); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public List getProcessDeploymentInfosOfCategory(final long categoryId, final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortCriterion) { if (sortCriterion == null) { throw new IllegalArgumentException("You must to have a criterion to sort your result."); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final OrderByType order = buildOrderByType(sortCriterion.getOrder()); final String field = sortCriterion.getField(); try { final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults, SProcessDefinitionDeployInfo.class, field, order); final List sProcessDefinitionDeployInfos = processDefinitionService .searchProcessDeploymentInfosOfCategory(categoryId, queryOptions); return ModelConvertor.toProcessDeploymentInfo(sProcessDefinitionDeployInfos); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public List getCategoriesOfProcessDefinition(final long processDefinitionId, final int startIndex, final int maxResults, final CategoryCriterion sortingCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final CategoryService categoryService = serviceAccessor.getCategoryService(); try { final OrderByType order = buildOrderByType(sortingCriterion.getOrder()); return ModelConvertor.toCategories(categoryService.getCategoriesOfProcessDefinition(processDefinitionId, startIndex, maxResults, order)); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } private OrderByType buildOrderByType(final Order order) { return OrderByType.valueOf(order.name()); } @Override public List getCategoriesUnrelatedToProcessDefinition(final long processDefinitionId, final int startIndex, final int maxResults, final CategoryCriterion sortingCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final CategoryService categoryService = serviceAccessor.getCategoryService(); try { final OrderByType order = buildOrderByType(sortingCriterion.getOrder()); return ModelConvertor.toCategories(categoryService .getCategoriesUnrelatedToProcessDefinition(processDefinitionId, startIndex, maxResults, order)); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public void updateCategory(final long categoryId, final CategoryUpdater updater) throws CategoryNotFoundException, UpdateException { if (updater == null || updater.getFields().isEmpty()) { throw new UpdateException("The update descriptor does not contain field updates"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final CategoryService categoryService = serviceAccessor.getCategoryService(); try { final SCategoryUpdateBuilderFactory fact = BuilderFactory.get(SCategoryUpdateBuilderFactory.class); final EntityUpdateDescriptor updateDescriptor = getCategoryUpdateDescriptor(fact.createNewInstance(), updater); final UpdateCategory updateCategory = new UpdateCategory(categoryService, categoryId, updateDescriptor); updateCategory.execute(); } catch (final SCategoryNotFoundException scnfe) { throw new CategoryNotFoundException(scnfe); } catch (final SBonitaException sbe) { throw new UpdateException(sbe); } } private EntityUpdateDescriptor getCategoryUpdateDescriptor(final SCategoryUpdateBuilder descriptorBuilder, final CategoryUpdater updater) { final Map fields = updater.getFields(); final String name = (String) fields.get(CategoryField.NAME); if (name != null) { descriptorBuilder.updateName(name); } final String description = (String) fields.get(CategoryField.DESCRIPTION); if (description != null) { descriptorBuilder.updateDescription(description); } return descriptorBuilder.done(); } @Override public void deleteCategory(final long categoryId) throws DeletionException { if (categoryId <= 0) { throw new DeletionException("Category id can not be less than 0!"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final CategoryService categoryService = serviceAccessor.getCategoryService(); final DeleteSCategory deleteSCategory = new DeleteSCategory(categoryService, categoryId); try { deleteSCategory.execute(); } catch (final SCategoryNotFoundException scnfe) { throw new DeletionException(new CategoryNotFoundException(scnfe)); } catch (final SBonitaException sbe) { throw new DeletionException(sbe); } } @Override public long getNumberOfUncategorizedProcessDefinitions() { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final CategoryService categoryService = serviceAccessor.getCategoryService(); try { final List processDefinitionIds = processDefinitionService.getProcessDefinitionIds(0, Integer.MAX_VALUE); long number; if (processDefinitionIds.isEmpty()) { number = 0; } else { number = processDefinitionIds.size() - categoryService.getNumberOfCategorizedProcessIds(processDefinitionIds); } return number; } catch (final SBonitaException e) { throw new BonitaRuntimeException(e);// TODO refactor exceptions } } @Override public List getUncategorizedProcessDeploymentInfos(final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults, SProcessDefinitionDeployInfo.class, sortCriterion.getField(), buildOrderByType(sortCriterion.getOrder())); final List processDefinitionDeployInfos = processDefinitionService .searchUncategorizedProcessDeploymentInfos(queryOptions); return ModelConvertor.toProcessDeploymentInfo(processDefinitionDeployInfos); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public long getNumberOfProcessDeploymentInfosUnrelatedToCategory(final long categoryId) { try { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final GetNumberOfProcessDeploymentInfosUnrelatedToCategory transactionContentWithResult = new GetNumberOfProcessDeploymentInfosUnrelatedToCategory( categoryId, processDefinitionService); transactionContentWithResult.execute(); return transactionContentWithResult.getResult(); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public List getProcessDeploymentInfosUnrelatedToCategory(final long categoryId, final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { return ModelConvertor.toProcessDeploymentInfo( processDefinitionService.getProcessDeploymentInfosUnrelatedToCategory(categoryId, startIndex, maxResults, sortingCriterion)); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public long removeCategoriesFromProcessDefinition(final long processDefinitionId, final int startIndex, final int maxResults) throws DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final CategoryService categoryService = serviceAccessor.getCategoryService(); final SProcessCategoryMappingBuilderFactory fact = BuilderFactory .get(SProcessCategoryMappingBuilderFactory.class); try { final FilterOption filterOption = new FilterOption(SProcessCategoryMapping.class, fact.getProcessIdKey(), processDefinitionId); final OrderByOption order = new OrderByOption(SProcessCategoryMapping.class, fact.getIdKey(), OrderByType.ASC); final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults, Collections.singletonList(order), Collections.singletonList(filterOption), null); final List processCategoryMappings = categoryService .searchProcessCategoryMappings(queryOptions); return categoryService.deleteProcessCategoryMappings(processCategoryMappings); } catch (final SBonitaException e) { throw new DeletionException(e); } } private void removeAllCategoriesFromProcessDefinition(final long processDefinitionId) throws DeletionException { removeCategoriesFromProcessDefinition(processDefinitionId, 0, Integer.MAX_VALUE); } @Override public long removeProcessDefinitionsFromCategory(final long categoryId, final int startIndex, final int maxResults) throws DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final CategoryService categoryService = serviceAccessor.getCategoryService(); final SProcessCategoryMappingBuilderFactory fact = BuilderFactory .get(SProcessCategoryMappingBuilderFactory.class); try { final FilterOption filterOption = new FilterOption(SProcessCategoryMapping.class, fact.getCategoryIdKey(), categoryId); final OrderByOption order = new OrderByOption(SProcessCategoryMapping.class, fact.getIdKey(), OrderByType.ASC); final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults, Collections.singletonList(order), Collections.singletonList(filterOption), null); final List processCategoryMappings = categoryService .searchProcessCategoryMappings(queryOptions); return categoryService.deleteProcessCategoryMappings(processCategoryMappings); } catch (final SBonitaException e) { throw new DeletionException(e); } } @Override public List getEventInstances(final long rootContainerId, final int startIndex, final int maxResults, final EventCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final EventInstanceService eventInstanceService = serviceAccessor.getEventInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForEvent(criterion); final GetEventInstances getEventInstances = new GetEventInstances(eventInstanceService, rootContainerId, startIndex, maxResults, orderAndField.getField(), orderAndField.getOrder()); try { getEventInstances.execute(); final List result = getEventInstances.getResult(); return ModelConvertor.toEventInstances(result, flowNodeStateManager); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public void assignUserTask(final long userTaskId, final long userId) throws UpdateException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); try { final AssignOrUnassignUserTask assignUserTask = new AssignOrUnassignUserTask(userId, userTaskId, activityInstanceService, serviceAccessor.getFlowNodeStateManager().getStateBehaviors()); assignUserTask.execute(); } catch (final SBonitaException sbe) { throw new UpdateException(sbe); } } @Override public void assignUserTaskIfNotAssigned(final long userTaskId, final long userId) throws UpdateException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); try { final AssignUserTaskIfNotAssigned assignUserTask = new AssignUserTaskIfNotAssigned(userId, userTaskId, activityInstanceService, serviceAccessor.getFlowNodeStateManager().getStateBehaviors()); assignUserTask.execute(); } catch (final SBonitaException sbe) { throw new UpdateException("Unable to assign user task (id: " + userTaskId + ")" + " to user (id: " + userId + ") | " + sbe.getMessage(), sbe); } } @CustomTransactions @Override public void assignAndExecuteUserTask(long userId, long userTaskInstanceId, Map inputs) throws UserTaskNotFoundException, ContractViolationException, FlowNodeExecutionException { try { inTx(() -> { assignUserTask(userTaskInstanceId, userId); executeFlowNode(userId, userTaskInstanceId, inputs, true); return null; }); } catch (final ContractViolationException e) { throw e; } catch (final SFlowNodeNotFoundException e) { throw new UserTaskNotFoundException( String.format("User task %s is not found, it might already be executed", userTaskInstanceId)); } catch (final Exception e) { verifyIfTheActivityWasInTheCorrectStateAndThrowException(userTaskInstanceId, e); } } @Override public void updateActorsOfUserTask(final long userTaskId) throws UpdateException { final ServiceAccessor serviceAccessor = getServiceAccessor(); try { final SHumanTaskInstance humanTaskInstance = getSHumanTaskInstance(userTaskId); if (humanTaskInstance.getStateId() != 4 || humanTaskInstance.isStateExecuting()) { throw new UpdateException( "Unable to update actors of the task " + userTaskId + " because it is not in ready state"); } final long processDefinitionId = humanTaskInstance.getLogicalGroup(0); final SProcessDefinition processDefinition = serviceAccessor.getProcessDefinitionService() .getProcessDefinition(processDefinitionId); final SHumanTaskDefinition humanTaskDefinition = (SHumanTaskDefinition) processDefinition .getProcessContainer().getFlowNode( humanTaskInstance.getFlowNodeDefinitionId()); final long humanTaskInstanceId = humanTaskInstance.getId(); if (humanTaskDefinition != null) { final SUserFilterDefinition sUserFilterDefinition = humanTaskDefinition.getSUserFilterDefinition(); if (sUserFilterDefinition != null) { cleanPendingMappingsAndUnassignHumanTask(userTaskId, humanTaskInstance); final FilterResult result = executeFilter(processDefinitionId, humanTaskInstanceId, humanTaskDefinition.getActorName(), sUserFilterDefinition); final List userIds = result.getResult(); if (userIds == null || userIds.isEmpty() || userIds.contains(0L) || userIds.contains(-1L)) { throw new UpdateException( "no user id returned by the user filter " + sUserFilterDefinition + " on activity " + humanTaskDefinition.getName()); } createPendingMappingsAndAssignHumanTask(humanTaskInstanceId, result); } } log.info("User '" + getUserNameFromSession() + "' has re-executed assignation on activity " + humanTaskInstanceId + " of process instance " + humanTaskInstance.getLogicalGroup(1) + " of process named '" + processDefinition.getName() + "' in version " + processDefinition.getVersion()); } catch (final SBonitaException sbe) { throw new UpdateException(sbe); } } String getUserNameFromSession() { return SessionInfos.getUserNameFromSession(); } private void createPendingMappingsAndAssignHumanTask(final long humanTaskInstanceId, final FilterResult result) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final List userIds = result.getResult(); for (final Long userId : userIds) { createPendingMappingForUser(humanTaskInstanceId, userId); } if (userIds.size() == 1 && result.shouldAutoAssignTaskIfSingleResult()) { serviceAccessor.getActivityInstanceService().assignHumanTask(humanTaskInstanceId, userIds.get(0)); } } private FilterResult executeFilter(final long processDefinitionId, final long humanTaskInstanceId, final String actorName, final SUserFilterDefinition sUserFilterDefinition) throws SUserFilterExecutionException, SClassLoaderException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final UserFilterService userFilterService = serviceAccessor.getUserFilterService(); final ClassLoader processClassloader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, processDefinitionId)); final SExpressionContext expressionContext = new SExpressionContext(humanTaskInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinitionId); return userFilterService.executeFilter(processDefinitionId, sUserFilterDefinition, sUserFilterDefinition.getInputs(), processClassloader, expressionContext, actorName); } private void cleanPendingMappingsAndUnassignHumanTask(final long userTaskId, final SHumanTaskInstance humanTaskInstance) throws SFlowNodeNotFoundException, SFlowNodeReadException, SActivityModificationException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); // release if (humanTaskInstance.getAssigneeId() > 0) { activityInstanceService.assignHumanTask(userTaskId, 0); } activityInstanceService.deletePendingMappings(humanTaskInstance.getId()); } private SHumanTaskInstance getSHumanTaskInstance(final long userTaskId) throws SFlowNodeNotFoundException, SFlowNodeReadException, UpdateException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SFlowNodeInstance flowNodeInstance = serviceAccessor.getActivityInstanceService() .getFlowNodeInstance(userTaskId); if (!(flowNodeInstance instanceof SHumanTaskInstance)) { throw new UpdateException("The identifier does not refer to a human task"); } return (SHumanTaskInstance) flowNodeInstance; } private void createPendingMappingForUser(final long humanTaskInstanceId, final Long userId) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final SPendingActivityMapping mapping = SPendingActivityMapping.builder().activityId(humanTaskInstanceId) .userId(userId) .build(); activityInstanceService.addPendingActivityMappings(mapping); } @Override public List getActivityDataDefinitions(final long processDefinitionId, final String activityName, final int startIndex, final int maxResults) throws ActivityDefinitionNotFoundException, ProcessDefinitionNotFoundException { List subDataDefinitionList; List sdataDefinitionList = Collections.emptyList(); final ServiceAccessor serviceAccessor; serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final SProcessDefinition sProcessDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); boolean activityFound = false; final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer(); final Set activityDefList = processContainer.getActivities(); for (final SActivityDefinition sActivityDefinition : activityDefList) { if (activityName.equals(sActivityDefinition.getName())) { sdataDefinitionList = sActivityDefinition.getSDataDefinitions(); activityFound = true; break; } } if (!activityFound) { throw new ActivityDefinitionNotFoundException(activityName); } final List dataDefinitionList = ModelConvertor.toDataDefinitions(sdataDefinitionList); if (startIndex >= dataDefinitionList.size()) { return Collections.emptyList(); } final int toIndex = Math.min(dataDefinitionList.size(), startIndex + maxResults); subDataDefinitionList = new ArrayList<>(dataDefinitionList.subList(startIndex, toIndex)); } catch (final SProcessDefinitionNotFoundException e) { throw new ProcessDefinitionNotFoundException(e); } catch (final SBonitaException e) { throw new RetrieveException(e); } return subDataDefinitionList; } @Override public List getProcessDataDefinitions(final long processDefinitionId, final int startIndex, final int maxResults) throws ProcessDefinitionNotFoundException { List subDataDefinitionList; final ServiceAccessor serviceAccessor; serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final SProcessDefinition sProcessDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer(); final List sdataDefinitionList = processContainer.getDataDefinitions(); final List dataDefinitionList = ModelConvertor.toDataDefinitions(sdataDefinitionList); if (startIndex >= dataDefinitionList.size()) { return Collections.emptyList(); } final int toIndex = Math.min(dataDefinitionList.size(), startIndex + maxResults); subDataDefinitionList = new ArrayList<>(dataDefinitionList.subList(startIndex, toIndex)); return subDataDefinitionList; } catch (final SProcessDefinitionNotFoundException e) { throw new ProcessDefinitionNotFoundException(e); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public HumanTaskInstance getHumanTaskInstance(final long activityInstanceId) throws ActivityInstanceNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final GetHumanTaskInstance getHumanTaskInstance = new GetHumanTaskInstance(activityInstanceService, activityInstanceId); try { getHumanTaskInstance.execute(); } catch (final SActivityInstanceNotFoundException e) { throw new ActivityInstanceNotFoundException(activityInstanceId, e); } catch (final SBonitaException e) { throw new RetrieveException(e); } return ModelConvertor.toHumanTaskInstance(getHumanTaskInstance.getResult(), flowNodeStateManager); } @Override public long getNumberOfAssignedHumanTaskInstances(final long userId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); try { return activityInstanceService.getNumberOfAssignedHumanTaskInstances(userId); } catch (final SActivityReadException e) { throw new RetrieveException(e); } } @Override public Map getNumberOfOpenTasks(final List userIds) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); try { final GetNumberOfOpenTasksForUsers transactionContent = new GetNumberOfOpenTasksForUsers(userIds, activityInstanceService); transactionContent.execute(); return transactionContent.getResult(); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public Map getProcessResources(final long processDefinitionId, final String filenamesPattern) throws RetrieveException { try { BusinessArchive businessArchive = getServiceAccessor().getBusinessArchiveService() .export(processDefinitionId); return businessArchive.getResources(filenamesPattern); } catch (SBonitaException | InvalidBusinessArchiveFormatException e) { throw new RetrieveException(e); } } @Override public byte[] getExternalProcessResource(final long processDefinitionId, final String fileName) throws RetrieveException, FileNotFoundException { SBARResource resource; try { resource = getServiceAccessor().getProcessResourcesService().get(processDefinitionId, BARResourceType.EXTERNAL, fileName); } catch (SBonitaException e) { throw new RetrieveException(e); } if (resource == null) { throw new FileNotFoundException("No resource named " + fileName + " in process " + processDefinitionId); } return resource.getContent(); } @Override public byte[] getDocumentProcessResource(final long processDefinitionId, final String fileName) throws RetrieveException, ProcessResourceNotFoundException { SBARResource resource; try { resource = getServiceAccessor().getProcessResourcesService().get(processDefinitionId, BARResourceType.DOCUMENT, fileName); } catch (SBonitaException e) { throw new RetrieveException(e); } if (resource == null) { throw new ProcessResourceNotFoundException( "No resource named " + fileName + " in the documents of process " + processDefinitionId); } return resource.getContent(); } @Override public long getLatestProcessDefinitionId(final String processName) throws ProcessDefinitionNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final TransactionContentWithResult transactionContent = new GetLatestProcessDefinitionId( processDefinitionService, processName); try { transactionContent.execute(); } catch (final SBonitaException e) { throw new ProcessDefinitionNotFoundException(e); } return transactionContent.getResult(); } @Override public List getProcessDataInstances(final long processInstanceId, final int startIndex, final int maxResults) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final long processDefinitionId = processInstanceService.getProcessInstance(processInstanceId) .getProcessDefinitionId(); final ClassLoader processClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, processDefinitionId)); Thread.currentThread().setContextClassLoader(processClassLoader); final List dataInstances = dataInstanceService.getDataInstances(processInstanceId, DataInstanceContainer.PROCESS_INSTANCE.name(), parentContainerResolver, startIndex, maxResults); return convertModelToDataInstances(dataInstances); } catch (final SBonitaException e) { throw new RetrieveException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } @Override public DataInstance getProcessDataInstance(final String dataName, final long processInstanceId) throws DataNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final long processDefinitionId = processInstanceService.getProcessInstance(processInstanceId) .getProcessDefinitionId(); final ClassLoader processClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, processDefinitionId)); Thread.currentThread().setContextClassLoader(processClassLoader); final SDataInstance sDataInstance = dataInstanceService.getDataInstance(dataName, processInstanceId, DataInstanceContainer.PROCESS_INSTANCE.toString(), parentContainerResolver); return convertModeltoDataInstance(sDataInstance); } catch (final SBonitaException e) { throw new DataNotFoundException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } @Override public void updateProcessDataInstance(final String dataName, final long processInstanceId, final Serializable dataValue) throws UpdateException { updateProcessDataInstances(processInstanceId, singletonMap(dataName, dataValue)); } @Override public void updateProcessDataInstances(final long processInstanceId, final Map dataNameValues) throws UpdateException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService(); final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final ClassLoader processClassLoader = getProcessInstanceClassloader(serviceAccessor, processInstanceId); Thread.currentThread().setContextClassLoader(processClassLoader); final List dataNames = new ArrayList<>(dataNameValues.keySet()); final List sDataInstances = dataInstanceService.getDataInstances(dataNames, processInstanceId, DataInstanceContainer.PROCESS_INSTANCE.toString(), parentContainerResolver); updateDataInstances(sDataInstances, dataNameValues, processClassLoader); } catch (final SBonitaException | ClassNotFoundException e) { throw new UpdateException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } protected void updateDataInstances(final List sDataInstances, final Map dataNameValues, ClassLoader classLoader) throws ClassNotFoundException, UpdateException, SDataInstanceException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService(); for (final SDataInstance sDataInstance : sDataInstances) { final Serializable dataValue = dataNameValues.get(sDataInstance.getName()); updateDataInstance(dataInstanceService, sDataInstance, dataValue, classLoader); } } protected void updateDataInstance(final DataInstanceService dataInstanceService, final SDataInstance sDataInstance, final Serializable dataNewValue, ClassLoader classLoader) throws UpdateException, SDataInstanceException { verifyTypeOfNewDataValue(sDataInstance, dataNewValue, classLoader); final EntityUpdateDescriptor entityUpdateDescriptor = buildEntityUpdateDescriptorForData(dataNewValue); dataInstanceService.updateDataInstance(sDataInstance, entityUpdateDescriptor); } protected void verifyTypeOfNewDataValue(final SDataInstance sDataInstance, final Serializable dataValue, ClassLoader classLoader) throws UpdateException { final String dataClassName = sDataInstance.getClassName(); Class dataClass; try { dataClass = classLoader.loadClass(dataClassName); } catch (final ClassNotFoundException e) { throw new UpdateException(e); } if (!dataClass.isInstance(dataValue)) { final UpdateException e = new UpdateException("The type of new value [" + dataValue.getClass().getName() + "] is not compatible with the type of the data."); e.setDataName(sDataInstance.getName()); e.setDataClassName(dataClassName); throw e; } } private EntityUpdateDescriptor buildEntityUpdateDescriptorForData(final Serializable dataValue) { final EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor(); entityUpdateDescriptor.addField("value", dataValue); return entityUpdateDescriptor; } protected ClassLoader getProcessInstanceClassloader(final ServiceAccessor serviceAccessor, final long processInstanceId) throws SProcessInstanceNotFoundException, SProcessInstanceReadException, SClassLoaderException { final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final long processDefinitionId = processInstanceService.getProcessInstance(processInstanceId) .getProcessDefinitionId(); return classLoaderService.getClassLoader(identifier(ScopeType.PROCESS, processDefinitionId)); } @Override public List getActivityDataInstances(final long activityInstanceId, final int startIndex, final int maxResults) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class) .getProcessDefinitionIndex(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final long parentProcessInstanceId = activityInstanceService.getFlowNodeInstance(activityInstanceId) .getLogicalGroup(processDefinitionIndex); final ClassLoader processClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, parentProcessInstanceId)); Thread.currentThread().setContextClassLoader(processClassLoader); final List dataInstances = dataInstanceService.getDataInstances(activityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.name(), parentContainerResolver, startIndex, maxResults); return convertModelToDataInstances(dataInstances); } catch (final SBonitaException e) { throw new RetrieveException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } @Override public DataInstance getActivityDataInstance(final String dataName, final long activityInstanceId) throws DataNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class) .getProcessDefinitionIndex(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(activityInstanceId); SDataInstance data; final long parentProcessInstanceId = flowNodeInstance.getLogicalGroup(processDefinitionIndex); final ClassLoader processClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, parentProcessInstanceId)); Thread.currentThread().setContextClassLoader(processClassLoader); data = dataInstanceService.getDataInstance(dataName, activityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.toString(), parentContainerResolver); return convertModeltoDataInstance(data); } catch (final SBonitaException e) { throw new DataNotFoundException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } @Override public void updateActivityDataInstance(final String dataName, final long activityInstanceId, final Serializable dataValue) throws UpdateException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class) .getProcessDefinitionIndex(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(activityInstanceId); final long parentProcessInstanceId = flowNodeInstance.getLogicalGroup(processDefinitionIndex); final ClassLoader processClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, parentProcessInstanceId)); Thread.currentThread().setContextClassLoader(processClassLoader); final SDataInstance sDataInstance = dataInstanceService.getDataInstance(dataName, activityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.toString(), parentContainerResolver); updateDataInstance(dataInstanceService, sDataInstance, dataValue, processClassLoader); } catch (final SBonitaException e) { throw new UpdateException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } @Override public DataInstance getActivityTransientDataInstance(final String dataName, final long activityInstanceId) throws DataNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final TransientDataService transientDataService = serviceAccessor.getTransientDataService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class) .getProcessDefinitionIndex(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(activityInstanceId); SDataInstance data; final long parentProcessInstanceId = flowNodeInstance.getLogicalGroup(processDefinitionIndex); final ClassLoader processClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, parentProcessInstanceId)); Thread.currentThread().setContextClassLoader(processClassLoader); data = transientDataService.getDataInstance(dataName, activityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.toString()); return convertModeltoDataInstance(data); } catch (final SBonitaException e) { throw new DataNotFoundException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } /** * isolate static call for mocking reasons */ protected DataInstance convertModeltoDataInstance(final SDataInstance data) { return ModelConvertor.toDataInstance(data); } @Override public List getActivityTransientDataInstances(final long activityInstanceId, final int startIndex, final int maxResults) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final TransientDataService transientDataService = serviceAccessor.getTransientDataService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class) .getProcessDefinitionIndex(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final long parentProcessInstanceId = activityInstanceService.getFlowNodeInstance(activityInstanceId) .getLogicalGroup(processDefinitionIndex); final ClassLoader processClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, parentProcessInstanceId)); Thread.currentThread().setContextClassLoader(processClassLoader); final List dataInstances = transientDataService.getDataInstances(activityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.name(), startIndex, maxResults); return convertModelToDataInstances(dataInstances); } catch (final SBonitaException e) { throw new RetrieveException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } /** * isolate static call for mocking reasons */ protected List convertModelToDataInstances(final List dataInstances) { return ModelConvertor.toDataInstances(dataInstances); } @Override public void updateActivityTransientDataInstance(final String dataName, final long activityInstanceId, final Serializable dataValue) throws UpdateException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final TransientDataService transientDataService = serviceAccessor.getTransientDataService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class) .getProcessDefinitionIndex(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(activityInstanceId); final long parentProcessInstanceId = flowNodeInstance.getLogicalGroup(processDefinitionIndex); final ClassLoader processClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, parentProcessInstanceId)); Thread.currentThread().setContextClassLoader(processClassLoader); updateTransientData(dataName, activityInstanceId, dataValue, transientDataService, processClassLoader); } catch (final SBonitaException e) { throw new UpdateException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } protected void updateTransientData(final String dataName, final long activityInstanceId, final Serializable dataValue, final TransientDataService transientDataInstanceService, ClassLoader classLoader) throws SDataInstanceException, UpdateException { final SDataInstance sDataInstance = transientDataInstanceService.getDataInstance(dataName, activityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.toString()); verifyTypeOfNewDataValue(sDataInstance, dataValue, classLoader); final EntityUpdateDescriptor entityUpdateDescriptor = buildEntityUpdateDescriptorForData(dataValue); transientDataInstanceService.updateDataInstance(sDataInstance, entityUpdateDescriptor); } @Override public void importActorMapping(final long processDefinitionId, final String xmlContent) throws ActorMappingImportException { if (xmlContent != null) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { new ImportActorMapping(actorMappingService, identityService).importActorMappingFromXml(xmlContent, processDefinitionId); serviceAccessor.getBusinessArchiveArtifactsManager().resolveDependencies(processDefinitionId, serviceAccessor); } catch (final SBonitaException sbe) { throw new ActorMappingImportException(sbe); } } } @Override public String exportActorMapping(final long processDefinitionId) throws ActorMappingExportException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final IdentityService identityService = serviceAccessor.getIdentityService(); try { final ExportActorMapping exportActorMapping = new ExportActorMapping(actorMappingService, identityService, processDefinitionId); exportActorMapping.execute(); return exportActorMapping.getResult(); } catch (final SBonitaException sbe) { throw new ActorMappingExportException(sbe); } } @Override public boolean isInvolvedInProcessInstance(final long userId, final long processInstanceId) throws ProcessInstanceNotFoundException { return processInvolvementDelegate.isInvolvedInProcessInstance(userId, processInstanceId); } public boolean isInvolvedInHumanTaskInstance(long userId, long humanTaskInstanceId) throws ActivityInstanceNotFoundException { return taskInvolvementDelegate.isInvolvedInHumanTaskInstance(userId, humanTaskInstanceId); } @Override public boolean isManagerOfUserInvolvedInProcessInstance(final long managerUserId, final long processInstanceId) throws BonitaException { return processInvolvementDelegate.isManagerOfUserInvolvedInProcessInstance(managerUserId, processInstanceId); } @Override public long getProcessInstanceIdFromActivityInstanceId(final long activityInstanceId) throws ProcessInstanceNotFoundException { try { final SActivityInstance sActivityInstance = getSActivityInstance(activityInstanceId); return sActivityInstance.getRootContainerId(); } catch (final SActivityInstanceNotFoundException e) { logInstanceNotFound(e); throw new ProcessInstanceNotFoundException(e); } catch (final SBonitaException e) { logError(e); throw new ProcessInstanceNotFoundException(e); } } @Override public long getProcessDefinitionIdFromActivityInstanceId(final long activityInstanceId) throws ProcessDefinitionNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); try { final SActivityInstance sActivityInstance = getSActivityInstance(activityInstanceId); return processInstanceService.getProcessInstance(sActivityInstance.getParentProcessInstanceId()) .getProcessDefinitionId(); } catch (final SBonitaException e) { throw new ProcessDefinitionNotFoundException(e); } } @Override public long getProcessDefinitionIdFromProcessInstanceId(final long processInstanceId) throws ProcessDefinitionNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final SProcessInstance sProcessInstance = getSProcessInstance(processInstanceId); final ProcessInstance processInstance = ModelConvertor .toProcessInstances(Collections.singletonList(sProcessInstance), processDefinitionService) .get(0); return processInstance.getProcessDefinitionId(); } catch (final SProcessInstanceNotFoundException e) { logInstanceNotFound(e); throw new ProcessDefinitionNotFoundException(e); } catch (final SBonitaException e) { logError(e); throw new ProcessDefinitionNotFoundException(e); } } @Override public Date getActivityReachedStateDate(final long activityInstanceId, final String stateName) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final int stateId = ModelConvertor.getServerActivityStateId(stateName); final GetArchivedActivityInstance getArchivedActivityInstance = new GetArchivedActivityInstance( activityInstanceId, stateId, activityInstanceService); try { getArchivedActivityInstance.execute(); final long reachedDate = getArchivedActivityInstance.getResult().getReachedStateDate(); return new Date(reachedDate); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public Set getSupportedStates(final FlowNodeType nodeType) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); return flowNodeStateManager.getSupportedState(SFlowNodeType.valueOf(nodeType.toString())); } @Override public void updateActivityInstanceVariables(final long activityInstanceId, final Map variables) throws UpdateException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class) .getProcessDefinitionIndex(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final long parentProcessInstanceId = activityInstanceService.getFlowNodeInstance(activityInstanceId) .getLogicalGroup(processDefinitionIndex); final ClassLoader processClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, parentProcessInstanceId)); Thread.currentThread().setContextClassLoader(processClassLoader); final List dataInstances = dataInstanceService.getDataInstances( new ArrayList<>(variables.keySet()), activityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.toString(), parentContainerResolver); if (dataInstances.size() < variables.size()) { throw new UpdateException("Some data does not exists, wanted to update " + variables.keySet() + " but there is only " + dataInstances); } for (final SDataInstance dataInstance : dataInstances) { final Serializable newValue = variables.get(dataInstance.getName()); final EntityUpdateDescriptor entityUpdateDescriptor = buildEntityUpdateDescriptorForData(newValue); dataInstanceService.updateDataInstance(dataInstance, entityUpdateDescriptor); } } catch (final SBonitaException e) { throw new UpdateException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } @Override public void updateActivityInstanceVariables(final List operations, final long activityInstanceId, final Map expressionContexts) throws UpdateException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final OperationService operationService = serviceAccessor.getOperationService(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final SActivityInstance activityInstance = activityInstanceService.getActivityInstance(activityInstanceId); final ClassLoader processClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, activityInstance.getProcessDefinitionId())); Thread.currentThread().setContextClassLoader(processClassLoader); final List sOperations = convertOperations(operations); final SExpressionContext sExpressionContext = new SExpressionContext(activityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.toString(), activityInstance.getProcessDefinitionId()); sExpressionContext.setSerializableInputValues(expressionContexts); operationService.execute(sOperations, sExpressionContext); } catch (final SBonitaException e) { throw new UpdateException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } protected List convertOperations(final List operations) { return ModelConvertor.convertOperations(operations); } @Override public long getOneAssignedUserTaskInstanceOfProcessInstance(final long processInstanceId, final long userId) throws RetrieveException { // FIXME: write specific query that should be more efficient: final int assignedUserTaskInstanceNumber = (int) getNumberOfAssignedHumanTaskInstances(userId); final List userTaskInstances = getAssignedHumanTaskInstances(userId, 0, assignedUserTaskInstanceNumber, ActivityInstanceCriterion.DEFAULT); for (final HumanTaskInstance userTaskInstance : userTaskInstances) { final String stateName = userTaskInstance.getState(); final long userTaskInstanceId = userTaskInstance.getId(); if (stateName.equals(ActivityStates.READY_STATE) && userTaskInstance.getParentContainerId() == processInstanceId) { return userTaskInstanceId; } } return -1; } @Override public long getOneAssignedUserTaskInstanceOfProcessDefinition(final long processDefinitionId, final long userId) { final int assignedUserTaskInstanceNumber = (int) getNumberOfAssignedHumanTaskInstances(userId); final List userTaskInstances = getAssignedHumanTaskInstances(userId, 0, assignedUserTaskInstanceNumber, ActivityInstanceCriterion.DEFAULT); if (!userTaskInstances.isEmpty()) { for (final HumanTaskInstance userTaskInstance : userTaskInstances) { final String stateName = userTaskInstance.getState(); try { final SProcessInstance sProcessInstance = getSProcessInstance( userTaskInstance.getRootContainerId()); if (stateName.equals(ActivityStates.READY_STATE) && sProcessInstance.getProcessDefinitionId() == processDefinitionId) { return userTaskInstance.getId(); } } catch (final SBonitaException e) { throw new RetrieveException(e); } } } return -1; } @Override public String getActivityInstanceState(final long activityInstanceId) throws ActivityInstanceNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); try { final SActivityInstance sActivityInstance = getSActivityInstance(activityInstanceId); final ActivityInstance activityInstance = ModelConvertor.toActivityInstance(sActivityInstance, flowNodeStateManager); return activityInstance.getState(); } catch (final SActivityInstanceNotFoundException e) { logInstanceNotFound(e); throw new ActivityInstanceNotFoundException(activityInstanceId); } catch (final SBonitaException e) { logError(e); throw new ActivityInstanceNotFoundException(activityInstanceId, e); } } @Override public boolean canExecuteTask(final long activityInstanceId, final long userId) throws ActivityInstanceNotFoundException, RetrieveException { final HumanTaskInstance userTaskInstance = getHumanTaskInstance(activityInstanceId); return userTaskInstance.getState().equalsIgnoreCase(ActivityStates.READY_STATE) && userTaskInstance.getAssigneeId() == userId; } @Override public long getProcessDefinitionId(final String name, final String version) throws ProcessDefinitionNotFoundException { return processDeploymentAPIDelegate.getProcessDefinitionId(name, version); } @Override public void releaseUserTask(final long userTaskId) throws ActivityInstanceNotFoundException, UpdateException { final ServiceAccessor serviceAccessor; serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); try { final AssignOrUnassignUserTask assignUserTask = new AssignOrUnassignUserTask(0, userTaskId, activityInstanceService, serviceAccessor .getFlowNodeStateManager().getStateBehaviors()); assignUserTask.execute(); } catch (final SUnreleasableTaskException e) { throw new UpdateException(e); } catch (final SActivityInstanceNotFoundException e) { throw new ActivityInstanceNotFoundException(userTaskId, e); } catch (final SBonitaException e) { logError(e); throw new UpdateException(e); } } /** * {@inheritDoc} */ @Override @Deprecated(since = "9.0.0") public void updateProcessDeploymentInfo(final long processDefinitionId, final ProcessDeploymentInfoUpdater processDeploymentInfoUpdater) throws ProcessDefinitionNotFoundException, UpdateException { if (processDeploymentInfoUpdater == null || processDeploymentInfoUpdater.getFields().isEmpty()) { throw new UpdateException("The update descriptor does not contain field updates"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final UpdateProcessDeploymentInfo updateProcessDeploymentInfo = new UpdateProcessDeploymentInfo( processDefinitionService, processDefinitionId, processDeploymentInfoUpdater); try { updateProcessDeploymentInfo.execute(); } catch (final SProcessDefinitionNotFoundException e) { throw new ProcessDefinitionNotFoundException(e); } catch (final SBonitaException sbe) { throw new UpdateException(sbe); } } @Override public List getStartableProcessDeploymentInfosForActors(final Set actorIds, final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final Set processDefIds = getIdOfStartableProcessDeploymentInfosForActors(actorIds); String field = null; OrderByType order = null; switch (sortingCriterion) { case DEFAULT: break; case NAME_ASC: field = SProcessDefinitionDeployInfo.NAME_KEY; order = OrderByType.ASC; break; case NAME_DESC: field = SProcessDefinitionDeployInfo.NAME_KEY; order = OrderByType.DESC; break; case ACTIVATION_STATE_ASC: field = SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY; order = OrderByType.ASC; break; case ACTIVATION_STATE_DESC: field = SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY; order = OrderByType.DESC; break; case CONFIGURATION_STATE_ASC: field = SProcessDefinitionDeployInfo.CONFIGURATION_STATE_KEY; order = OrderByType.ASC; break; case CONFIGURATION_STATE_DESC: field = SProcessDefinitionDeployInfo.CONFIGURATION_STATE_KEY; order = OrderByType.DESC; break; case VERSION_ASC: field = SProcessDefinitionDeployInfo.VERSION_KEY; order = OrderByType.ASC; break; case VERSION_DESC: field = SProcessDefinitionDeployInfo.VERSION_KEY; order = OrderByType.DESC; break; default: break; } final List processDefinitionDeployInfos = processDefinitionService .getProcessDeploymentInfos(new ArrayList<>( processDefIds), startIndex, maxResults, field, order); return ModelConvertor.toProcessDeploymentInfo(processDefinitionDeployInfos); } catch (final SBonitaException e) { throw new RetrieveException(e); } } private Set getIdOfStartableProcessDeploymentInfosForActors(final Set actorIds) throws SActorNotFoundException, SBonitaReadException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final List actors = actorMappingService.getActors(new ArrayList<>(actorIds)); final Set processDefIds = new HashSet<>(actors.size()); for (final SActor sActor : actors) { if (sActor.isInitiator()) { processDefIds.add(sActor.getScopeId()); } } return processDefIds; } @Override public boolean isAllowedToStartProcess(final long processDefinitionId, final Set actorIds) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final GetActorsByActorIds getActorsByActorIds = new GetActorsByActorIds(actorMappingService, new ArrayList<>(actorIds)); try { getActorsByActorIds.execute(); final List actors = getActorsByActorIds.getResult(); boolean isAllowedToStartProcess = true; final Iterator iterator = actors.iterator(); while (isAllowedToStartProcess && iterator.hasNext()) { final SActor actor = iterator.next(); if (actor.getScopeId() != processDefinitionId || !actor.isInitiator()) { isAllowedToStartProcess = false; } } return isAllowedToStartProcess; } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public ActorInstance getActorInitiator(final long processDefinitionId) throws ActorNotFoundException, ProcessDefinitionNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); ActorInstance actorInstance; try { final SProcessDefinition definition = processDefinitionService.getProcessDefinition(processDefinitionId); final SActorDefinition sActorDefinition = definition.getActorInitiator(); if (sActorDefinition == null) { throw new ActorNotFoundException("No actor initiator defined on the process"); } final String name = sActorDefinition.getName(); final SActor sActor = actorMappingService.getActor(name, processDefinitionId); actorInstance = ModelConvertor.toActorInstance(sActor); } catch (final SProcessDefinitionNotFoundException e) { // no rollback need, we only read throw new ProcessDefinitionNotFoundException(e); } catch (final SBonitaException e) { throw new RetrieveException(e); } return actorInstance; } @Override public int getNumberOfActivityDataDefinitions(final long processDefinitionId, final String activityName) throws ProcessDefinitionNotFoundException, ActivityDefinitionNotFoundException { List sdataDefinitionList = Collections.emptyList(); final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final SProcessDefinition sProcessDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); boolean found = false; final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer(); final Set activityDefList = processContainer.getActivities(); for (final SActivityDefinition sActivityDefinition : activityDefList) { if (activityName.equals(sActivityDefinition.getName())) { sdataDefinitionList = sActivityDefinition.getSDataDefinitions(); found = true; break; } } if (!found) { throw new ActivityDefinitionNotFoundException(activityName); } return sdataDefinitionList.size(); } catch (final SProcessDefinitionNotFoundException e) { throw new ProcessDefinitionNotFoundException(e); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public int getNumberOfProcessDataDefinitions(final long processDefinitionId) throws ProcessDefinitionNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final SProcessDefinition sProcessDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer(); return processContainer.getDataDefinitions().size(); } catch (final SProcessDefinitionNotFoundException e) { throw new ProcessDefinitionNotFoundException(e); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public ProcessInstance startProcess(final long processDefinitionId, final Map initialVariables) throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException { final List operations = createSetDataOperation(processDefinitionId, initialVariables); return startProcess(processDefinitionId, operations, initialVariables); } @Override public ProcessInstance startProcessWithInputs(final long processDefinitionId, final Map instantiationInputs) throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException, ContractViolationException { return startProcessWithInputs(0, processDefinitionId, instantiationInputs); } @Override public ProcessInstance startProcessWithInputs(final long userId, final long processDefinitionId, final Map instantiationInputs) throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException, ContractViolationException { return new ProcessStarter(userId, processDefinitionId, instantiationInputs).start(); } @Override public ProcessInstance startProcess(final long userId, final long processDefinitionId, final Map initialVariables) throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException { final List operations = createSetDataOperation(processDefinitionId, initialVariables); return startProcess(userId, processDefinitionId, operations, initialVariables); } protected List createSetDataOperation(final long processDefinitionId, final Map initialVariables) throws ProcessExecutionException { final ClassLoaderService classLoaderService = getServiceAccessor().getClassLoaderService(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); final List operations = new ArrayList<>(); try { final ClassLoader localClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, processDefinitionId)); Thread.currentThread().setContextClassLoader(localClassLoader); if (initialVariables != null) { for (final Entry initialVariable : initialVariables.entrySet()) { final String name = initialVariable.getKey(); final Serializable value = initialVariable.getValue(); final Expression expression = new ExpressionBuilder().createExpression(name, name, value.getClass().getName(), ExpressionType.TYPE_INPUT); final Operation operation = new OperationBuilder().createSetDataOperation(name, expression); operations.add(operation); } } } catch (final SClassLoaderException | InvalidExpressionException cle) { throw new ProcessExecutionException(cle); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } return operations; } @Override public ProcessInstance startProcess(final long processDefinitionId, final List operations, final Map context) throws ProcessExecutionException, ProcessDefinitionNotFoundException, ProcessActivationException { try { return startProcess(0, processDefinitionId, operations, context); } catch (final RetrieveException e) { throw new ProcessExecutionException(e); } } @Override public ProcessInstance startProcess(final long userId, final long processDefinitionId, final List operations, final Map context) throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException { final ProcessStarter starter = new ProcessStarter(userId, processDefinitionId, operations, context); try { return starter.start(); } catch (ContractViolationException e) { // To not have an API break, we need to wrapped this new Exception: throw new ProcessExecutionException(e); } } @Override public long getNumberOfActivityDataInstances(final long activityInstanceId) throws ActivityInstanceNotFoundException { try { return getNumberOfDataInstancesOfContainer(activityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE); } catch (final SBonitaException e) { throw new ActivityInstanceNotFoundException(activityInstanceId); } } private long getNumberOfDataInstancesOfContainer(final long instanceId, final DataInstanceContainer containerType) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver(); final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService(); return dataInstanceService.getNumberOfDataInstances(instanceId, containerType.name(), parentContainerResolver); } @Override public long getNumberOfProcessDataInstances(final long processInstanceId) throws ProcessInstanceNotFoundException { try { return getNumberOfDataInstancesOfContainer(processInstanceId, DataInstanceContainer.PROCESS_INSTANCE); } catch (final SBonitaException e) { throw new ProcessInstanceNotFoundException(e); } } protected Map executeOperations(final ConnectorResult connectorResult, final List operations, final Map operationInputValues, final SExpressionContext expressionContext, final ClassLoader classLoader, final ServiceAccessor serviceAccessor) throws SBonitaException { final ConnectorService connectorService = serviceAccessor.getConnectorService(); final OperationService operationService = serviceAccessor.getOperationService(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(classLoader); final Map externalDataValue = new HashMap<>(operations.size()); // convert the client operation to server operation final List sOperations = convertOperations(operations); // set input values of expression with connector result + provided input for this operation final HashMap inputValues = new HashMap<>(operationInputValues); inputValues.putAll(connectorResult.getResult()); expressionContext.setInputValues(inputValues); // execute final Long containerId = expressionContext.getContainerId(); operationService.execute(sOperations, containerId == null ? -1 : containerId, expressionContext.getContainerType(), expressionContext); // return the value of the data if it's an external data for (final Operation operation : operations) { final LeftOperand leftOperand = operation.getLeftOperand(); if (LeftOperand.TYPE_EXTERNAL_DATA.equals(leftOperand.getType())) { externalDataValue.put(leftOperand.getName(), (Serializable) expressionContext.getInputValues().get(leftOperand.getName())); } } return externalDataValue; } finally { // we finally disconnect the connector connectorService.disconnect(connectorResult); Thread.currentThread().setContextClassLoader(contextClassLoader); } } private Map toSerializableMap(final Map map, final String connectorDefinitionId, final String connectorDefinitionVersion) throws NotSerializableException { final HashMap resMap = new HashMap<>(map.size()); for (final Entry entry : map.entrySet()) { try { resMap.put(entry.getKey(), (Serializable) entry.getValue()); } catch (final ClassCastException e) { throw new NotSerializableException(connectorDefinitionId, connectorDefinitionVersion, entry.getKey(), entry.getValue()); } } return resMap; } @Override public Map executeConnectorOnProcessDefinition(final String connectorDefinitionId, final String connectorDefinitionVersion, final Map connectorInputParameters, final Map> inputValues, final long processDefinitionId) throws ConnectorExecutionException { return executeConnectorOnProcessDefinitionWithOrWithoutOperations(connectorDefinitionId, connectorDefinitionVersion, connectorInputParameters, inputValues, null, null, processDefinitionId); } @Override public Map executeConnectorOnProcessDefinition(final String connectorDefinitionId, final String connectorDefinitionVersion, final Map connectorInputParameters, final Map> inputValues, final List operations, final Map operationInputValues, final long processDefinitionId) throws ConnectorExecutionException { return executeConnectorOnProcessDefinitionWithOrWithoutOperations(connectorDefinitionId, connectorDefinitionVersion, connectorInputParameters, inputValues, operations, operationInputValues, processDefinitionId); } /** * execute the connector and return connector output if there is no operation or operation output if there is * operation */ private Map executeConnectorOnProcessDefinitionWithOrWithoutOperations( final String connectorDefinitionId, final String connectorDefinitionVersion, final Map connectorInputParameters, final Map> inputValues, final List operations, final Map operationInputValues, final long processDefinitionId) throws ConnectorExecutionException { checkConnectorParameters(connectorInputParameters, inputValues); final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final ConnectorService connectorService = serviceAccessor.getConnectorService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); try { final ClassLoader classLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, processDefinitionId)); final Map connectorsExps = ModelConvertor .constructExpressions(connectorInputParameters); final SExpressionContext expcontext = new SExpressionContext(); expcontext.setProcessDefinitionId(processDefinitionId); expcontext.setContainerState(ContainerState.ACTIVE); final SProcessDefinition processDef = processDefinitionService.getProcessDefinition(processDefinitionId); if (processDef != null) { expcontext.setProcessDefinition(processDef); } final ConnectorResult connectorResult = connectorService.executeMultipleEvaluation(processDefinitionId, connectorDefinitionId, connectorDefinitionVersion, connectorsExps, inputValues, classLoader, expcontext); if (operations != null) { // execute operations return executeOperations(connectorResult, operations, operationInputValues, expcontext, classLoader, serviceAccessor); } return getSerializableResultOfConnector(connectorDefinitionVersion, connectorResult, connectorService); } catch (final SBonitaException | NotSerializableException e) { throw new ConnectorExecutionException(e); } } protected Map getSerializableResultOfConnector(final String connectorDefinitionVersion, final ConnectorResult connectorResult, final ConnectorService connectorService) throws NotSerializableException, SConnectorException { connectorService.disconnect(connectorResult); return toSerializableMap(connectorResult.getResult(), connectorDefinitionVersion, connectorDefinitionVersion); } protected void checkConnectorParameters(final Map connectorInputParameters, final Map> inputValues) throws ConnectorExecutionException { if (connectorInputParameters.size() != inputValues.size()) { throw new ConnectorExecutionException( "The number of input parameters is not consistent with the number of input values. Input parameters: " + connectorInputParameters.size() + ", number of input values: " + inputValues.size()); } } @Override public void setActivityStateByName(final long activityInstanceId, final String state) throws UpdateException { setActivityStateById(activityInstanceId, ModelConvertor.getServerActivityStateId(state)); } @Override public void setActivityStateById(final long activityInstanceId, final int stateId) throws UpdateException { try { ActivityInstanceService activityInstanceService = getServiceAccessor().getActivityInstanceService(); SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(activityInstanceId); FlowNodeState state = getServiceAccessor().getFlowNodeStateManager().getState(stateId); log.warn(String.format( "setActivityStateById was called: <%s> is forcing state of the flow node with name = <%s> id = <%d> and type <%s> in current state <%s: %s> to the state <%s: %s>. " + "Setting the state of a flow node through the API is not recommended and can affect the normal behavior of the platform. " + "If that flow node was already scheduled for execution, there is no guarantee on how that flow node will behave anymore.", getLoggedUsername(), flowNodeInstance.getName(), flowNodeInstance.getId(), flowNodeInstance.getType().name(), flowNodeInstance.getStateId(), flowNodeInstance.getStateName(), stateId, state.getName())); getServiceAccessor().getFlowNodeExecutor().setStateByStateId(activityInstanceId, stateId); } catch (final SBonitaException e) { throw new UpdateException(e); } } private String getLoggedUsername() { SSession session = getSession(); if (session == null || session.getUserId() <= 0) { return "SYSTEM"; } long userId = session.getUserId(); try { return getServiceAccessor().getIdentityService().getUser(userId).getUserName(); } catch (Exception e) { log.warn("Unable to retrieve user with id " + userId + " exception: " + e.getClass().getName() + " " + e.getMessage()); } return String.valueOf(userId); } @Override public void setTaskPriority(final long humanTaskInstanceId, final TaskPriority priority) throws UpdateException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); try { final SetTaskPriority transactionContent = new SetTaskPriority(activityInstanceService, humanTaskInstanceId, STaskPriority.valueOf(priority.name())); transactionContent.execute(); } catch (final SBonitaException e) { throw new UpdateException(e); } } private void deleteProcessInstanceInTransaction(final ServiceAccessor serviceAccessor, final long processInstanceId) throws SBonitaException { final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final DocumentService documentService = serviceAccessor.getDocumentService(); try { userTransactionService.executeInTransaction((Callable) () -> { final SProcessInstance sProcessInstance = processInstanceService.getProcessInstance(processInstanceId); deleteJobsOnProcessInstance(sProcessInstance); // Documents are only deleted when deleting the unfinished process instance from the API, // and not when the finished process instance is archived: documentService.deleteDocumentContentsForProcessInstance(sProcessInstance.getId()); processInstanceService.deleteParentProcessInstanceAndElements(sProcessInstance); return null; }); } catch (final SBonitaException e) { throw e; } catch (final Exception e) { throw new SBonitaRuntimeException("Error while deleting the parent process instance and elements.", e); } } @Override @CustomTransactions public long deleteProcessInstances(final long processDefinitionId, final int startIndex, final int maxResults) throws DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final UserTransactionService userTxService = serviceAccessor.getUserTransactionService(); try { final Map> processInstancesWithChildrenIds = userTxService .executeInTransaction(() -> { final List sProcessInstances1 = searchProcessInstancesFromProcessDefinition( processInstanceService, processDefinitionId, startIndex, maxResults); final Map> sProcessInstanceListHashMap = new LinkedHashMap<>( sProcessInstances1.size()); for (final SProcessInstance rootProcessInstance : sProcessInstances1) { List tmpList; final List childrenProcessInstanceIds = new ArrayList<>(); int fromIndex = 0; do { // from index always will be zero because elements will be deleted tmpList = processInstanceService .getArchivedChildrenSourceObjectIdsFromRootProcessInstance( rootProcessInstance.getId(), fromIndex, BATCH_SIZE, OrderByType.ASC); childrenProcessInstanceIds.addAll(tmpList); fromIndex += BATCH_SIZE; } while (tmpList.size() == BATCH_SIZE); sProcessInstanceListHashMap.put(rootProcessInstance, childrenProcessInstanceIds); } return sProcessInstanceListHashMap; }); if (processInstancesWithChildrenIds.isEmpty()) { return 0; } final LockService lockService = serviceAccessor.getLockService(); final DocumentService documentService = serviceAccessor.getDocumentService(); final String objectType = SFlowElementsContainerType.PROCESS.name(); List locks = null; try { locks = createLockProcessInstances(lockService, objectType, processInstancesWithChildrenIds); return userTxService.executeInTransaction(() -> { final List sProcessInstances = new ArrayList<>( processInstancesWithChildrenIds.keySet()); deleteJobsOnProcessInstance(processDefinitionId, sProcessInstances); for (SProcessInstance spi : sProcessInstances) { // Documents are only deleted when deleting the unfinished process instance from the API, // and not when the finished process instance is archived: documentService.deleteDocumentContentsForProcessInstance(spi.getId()); } return processInstanceService.deleteParentProcessInstanceAndElements(sProcessInstances); }); } finally { releaseLocks(lockService, locks); } } catch (final SProcessInstanceHierarchicalDeletionException e) { throw new ProcessInstanceHierarchicalDeletionException(e.getMessage(), e.getProcessInstanceId()); } catch (final Exception e) { throw new DeletionException(e); } } private void deleteJobsOnEventSubProcess(final SProcessDefinition processDefinition, final SProcessInstance sProcessInstance) { final Set sSubProcessDefinitions = processDefinition.getProcessContainer() .getSubProcessDefinitions(); for (final SSubProcessDefinition sSubProcessDefinition : sSubProcessDefinitions) { final List startEventsOfSubProcess = sSubProcessDefinition.getSubProcessContainer() .getStartEvents(); deleteJobsOnEventSubProcess(processDefinition, sProcessInstance, sSubProcessDefinition, startEventsOfSubProcess); final List intermediateCatchEvents = sSubProcessDefinition .getSubProcessContainer().getIntermediateCatchEvents(); deleteJobsOnEventSubProcess(processDefinition, sProcessInstance, sSubProcessDefinition, intermediateCatchEvents); final List sBoundaryEventDefinitions = sSubProcessDefinition .getSubProcessContainer().getBoundaryEvents(); deleteJobsOnEventSubProcess(processDefinition, sProcessInstance, sSubProcessDefinition, sBoundaryEventDefinitions); } } private void deleteJobsOnEventSubProcess(final SProcessDefinition processDefinition, final SProcessInstance sProcessInstance, final SSubProcessDefinition sSubProcessDefinition, final List sCatchEventDefinitions) { final SchedulerService schedulerService = getServiceAccessor().getSchedulerService(); for (final SCatchEventDefinition sCatchEventDefinition : sCatchEventDefinitions) { try { if (!sCatchEventDefinition.getTimerEventTriggerDefinitions().isEmpty()) { final String jobName = JobNameBuilder.getTimerEventJobName(processDefinition.getId(), sCatchEventDefinition, sProcessInstance.getId(), sSubProcessDefinition.getId()); final boolean delete = schedulerService.delete(jobName); if (!delete && schedulerService.isExistingJob(jobName)) { if (log.isWarnEnabled()) { log.warn("No job found with name '" + jobName + "' when interrupting timer catch event named '" + sCatchEventDefinition.getName() + "' on event sub process with the id '" + sSubProcessDefinition.getId() + "'. It was probably already triggered."); } } } } catch (final Exception e) { log.warn(ExceptionUtils.printLightWeightStacktrace(e)); } } } void deleteJobsOnProcessInstance(final SProcessInstance sProcessInstance) throws SBonitaException { deleteJobsOnProcessInstance(sProcessInstance.getProcessDefinitionId(), Collections.singletonList(sProcessInstance)); } private void deleteJobsOnProcessInstance(final long processDefinitionId, final List sProcessInstances) throws SBonitaException { final SProcessDefinition processDefinition = getServiceAccessor().getProcessDefinitionService() .getProcessDefinition(processDefinitionId); for (final SProcessInstance sProcessInstance : sProcessInstances) { deleteJobsOnProcessInstance(processDefinition, sProcessInstance); } } private void deleteJobsOnProcessInstance(final SProcessDefinition processDefinition, final SProcessInstance sProcessInstance) throws SBonitaReadException { deleteJobsOnCallActivitiesOfProcessInstance(sProcessInstance.getId()); final List startEventsOfSubProcess = processDefinition.getProcessContainer() .getStartEvents(); deleteJobsOnProcessInstance(processDefinition, sProcessInstance, startEventsOfSubProcess); final List intermediateCatchEvents = processDefinition.getProcessContainer() .getIntermediateCatchEvents(); deleteJobsOnProcessInstance(processDefinition, sProcessInstance, intermediateCatchEvents); final List sBoundaryEventDefinitions = processDefinition.getProcessContainer() .getBoundaryEvents(); deleteJobsOnProcessInstance(processDefinition, sProcessInstance, sBoundaryEventDefinitions); deleteJobsOnEventSubProcess(processDefinition, sProcessInstance); } private void deleteJobsOnCallActivitiesOfProcessInstance(final long processInstanceId) throws SBonitaReadException { List flowNodeInstances; int index = 0; final ProcessInstanceService processInstanceService = getServiceAccessor().getProcessInstanceService(); do { try { flowNodeInstances = searchActivities(new SearchOptionsBuilder(index, index + BATCH_SIZE) .filter(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstanceId) .filter(ActivityInstanceSearchDescriptor.ACTIVITY_TYPE, FlowNodeType.CALL_ACTIVITY) .done()).getResult(); } catch (SearchException e) { throw new SBonitaReadException( "Unable to delete jobs on call activities of process instance id " + processInstanceId, e); } for (ActivityInstance callActivityInstance : flowNodeInstances) { try { deleteJobsOnProcessInstance( processInstanceService.getChildOfActivity(callActivityInstance.getId())); } catch (SBonitaException e) { log.info( "Can't find the process instance called by the activity. This process may be already finished. {}", ExceptionUtils.printLightWeightStacktrace(e)); } } index = index + BATCH_SIZE; } while (flowNodeInstances.size() == BATCH_SIZE); } private void deleteJobsOnProcessInstance(final SProcessDefinition processDefinition, final SProcessInstance sProcessInstance, final List sCatchEventDefinitions) throws SBonitaReadException { for (final SCatchEventDefinition sCatchEventDefinition : sCatchEventDefinitions) { deleteJobsOnFlowNodeInstances(processDefinition, sCatchEventDefinition, sProcessInstance); } } private void deleteJobsOnFlowNodeInstances(final SProcessDefinition processDefinition, final SCatchEventDefinition sCatchEventDefinition, final SProcessInstance sProcessInstance) throws SBonitaReadException { final ActivityInstanceService activityInstanceService = getServiceAccessor().getActivityInstanceService(); final List orderByOptions = Collections .singletonList(new OrderByOption(SCatchEventInstance.class, "id", OrderByType.ASC)); final FilterOption filterOption1 = new FilterOption(SCatchEventInstance.class, "flowNodeDefinitionId", sCatchEventDefinition.getId()); final FilterOption filterOption2 = new FilterOption(SCatchEventInstance.class, "logicalGroup4", sProcessInstance.getId()); QueryOptions queryOptions = new QueryOptions(0, 100, orderByOptions, Arrays.asList(filterOption1, filterOption2), null); List sCatchEventInstances = activityInstanceService .searchFlowNodeInstances(SCatchEventInstance.class, queryOptions); while (!sCatchEventInstances.isEmpty()) { for (final SCatchEventInstance sCatchEventInstance : sCatchEventInstances) { deleteJobsOnFlowNodeInstance(processDefinition, sCatchEventDefinition, sCatchEventInstance); } queryOptions = QueryOptions.getNextPage(queryOptions); sCatchEventInstances = activityInstanceService.searchFlowNodeInstances(SCatchEventInstance.class, queryOptions); } } private void deleteJobsOnFlowNodeInstance(final SProcessDefinition processDefinition, final SCatchEventDefinition sCatchEventDefinition, final SCatchEventInstance sCatchEventInstance) { final SchedulerService schedulerService = getServiceAccessor().getSchedulerService(); EventInstanceService eventInstanceService = getServiceAccessor().getEventInstanceService(); try { if (!sCatchEventDefinition.getTimerEventTriggerDefinitions().isEmpty()) { final String jobName = JobNameBuilder.getTimerEventJobName(processDefinition.getId(), sCatchEventDefinition, sCatchEventInstance); final boolean delete = schedulerService.delete(jobName); try { eventInstanceService.deleteEventTriggerInstanceOfFlowNode(sCatchEventInstance.getId()); } catch (SEventTriggerInstanceDeletionException e) { log.warn( "Unable to delete event trigger of flow node instance " + sCatchEventInstance + " because: " + e.getMessage()); } if (!delete && schedulerService.isExistingJob(jobName)) { if (log.isWarnEnabled()) { log.warn("No job found with name '" + jobName + "' when interrupting timer catch event named '" + sCatchEventDefinition.getName() + "' and id '" + sCatchEventInstance.getId() + "'. It was probably already triggered."); } } } } catch (final Exception e) { log.warn(ExceptionUtils.printLightWeightStacktrace(e)); } } private List searchProcessInstancesFromProcessDefinition( final ProcessInstanceService processInstanceService, final long processDefinitionId, final int startIndex, final int maxResults) throws SBonitaReadException { final FilterOption filterOption = new FilterOption(SProcessInstance.class, SProcessInstance.PROCESSDEF_ID_KEY, processDefinitionId); final OrderByOption order2 = new OrderByOption(SProcessInstance.class, SProcessInstance.ID_KEY, OrderByType.ASC); // Order by caller id ASC because we need to have parent process deleted before their sub processes final OrderByOption order = new OrderByOption(SProcessInstance.class, SProcessInstance.CALLER_ID, OrderByType.ASC); final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults, Arrays.asList(order, order2), Collections.singletonList(filterOption), null); return processInstanceService.searchProcessInstances(queryOptions); } @Override public long deleteArchivedProcessInstances(final long processDefinitionId, final int startIndex, final int maxResults) throws DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); try { List sourceProcessInstanceIds = processInstanceService .getSourceProcessInstanceIdsOfArchProcessInstancesFromDefinition(processDefinitionId, startIndex, maxResults, null); if (sourceProcessInstanceIds.isEmpty()) { return 0; } return deleteArchivedProcessInstancesInAllStates(sourceProcessInstanceIds); } catch (final SBonitaException e) { throw new DeletionException(e); } } @Override public long deleteArchivedProcessInstancesInAllStates(final List sourceProcessInstanceIds) throws DeletionException { if (sourceProcessInstanceIds == null || sourceProcessInstanceIds.isEmpty()) { throw new IllegalArgumentException( "The identifier of the archived process instances to deleted are missing !!"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); try { return processInstanceService.deleteArchivedProcessInstances(sourceProcessInstanceIds); } catch (final SProcessInstanceHierarchicalDeletionException e) { throw new ProcessInstanceHierarchicalDeletionException(e.getMessage(), e.getProcessInstanceId()); } catch (final SBonitaException e) { throw new DeletionException(e); } } @Override public long deleteArchivedProcessInstancesInAllStates(final long sourceProcessInstanceId) throws DeletionException { return deleteArchivedProcessInstancesInAllStates(Collections.singletonList(sourceProcessInstanceId)); } private List createLockProcessInstances(final LockService lockService, final String objectType, final Map> sProcessInstances) throws SLockException, SLockTimeoutException { final List locks = new ArrayList<>(); final HashSet uniqueIds = new HashSet<>(); for (final Entry> processInstanceWithChildrenIds : sProcessInstances.entrySet()) { uniqueIds.add(processInstanceWithChildrenIds.getKey().getId()); uniqueIds.addAll(processInstanceWithChildrenIds.getValue()); } for (final Long id : uniqueIds) { final BonitaLock childLock = lockService.lock(id, objectType); locks.add(childLock); } return locks; } @Override @CustomTransactions public void deleteProcessInstance(final long processInstanceId) throws DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final LockService lockService = serviceAccessor.getLockService(); final String objectType = SFlowElementsContainerType.PROCESS.name(); BonitaLock lock = null; try { lock = lockService.lock(processInstanceId, objectType); deleteProcessInstanceInTransaction(serviceAccessor, processInstanceId); } catch (final SProcessInstanceHierarchicalDeletionException e) { throw new ProcessInstanceHierarchicalDeletionException(e.getMessage(), e.getProcessInstanceId()); } catch (final SProcessInstanceNotFoundException spinfe) { throw new DeletionException(new ProcessInstanceNotFoundException(spinfe)); } catch (final SBonitaException e) { throw new DeletionException(e); } finally { if (lock != null) { try { lockService.unlock(lock); } catch (final SLockException e) { throw new DeletionException( "Lock was not released. Object type: " + objectType + ", id: " + processInstanceId, e); } } } } @Override public SearchResult searchOpenProcessInstances(final SearchOptions searchOptions) throws SearchException { // To select all finished process instances, without subprocess final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(searchOptions); searchOptionsBuilder.differentFrom(ProcessInstanceSearchDescriptor.STATE_ID, ProcessInstanceState.COMPLETED.getId()) .differentFrom(ProcessInstanceSearchDescriptor.STATE_ID, ProcessInstanceState.ABORTED.getId()) .differentFrom(ProcessInstanceSearchDescriptor.STATE_ID, ProcessInstanceState.CANCELLED.getId()); searchOptionsBuilder.filter(ProcessInstanceSearchDescriptor.CALLER_ID, -1); try { return searchProcessInstances(getServiceAccessor(), searchOptionsBuilder.done()); } catch (final SBonitaException e) { throw new SearchException(e); } } @Override public SearchResult searchProcessInstances(final SearchOptions searchOptions) throws SearchException { try { return searchProcessInstances(getServiceAccessor(), searchOptions); } catch (final SBonitaException e) { throw new SearchException(e); } } @Override public SearchResult searchFailedProcessInstances(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); try { final SearchFailedProcessInstances searchProcessInstances = new SearchFailedProcessInstances( processInstanceService, searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), searchOptions, processDefinitionService); searchProcessInstances.execute(); return searchProcessInstances.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } /* * (non-Javadoc) * @see org.bonitasoft.engine.api.ProcessRuntimeAPI#searchFailedProcessInstancesSupervisedBy(long, * org.bonitasoft.engine.search.SearchOptions) */ @Override public SearchResult searchFailedProcessInstancesSupervisedBy(final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final GetSUser getUser = createTxUserGetter(userId, identityService); try { getUser.execute(); } catch (final SBonitaException e) { return new SearchResultImpl<>(0, Collections.emptyList()); } final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchFailedProcessInstancesSupervisedBy searchFailedProcessInstances = createSearchFailedProcessInstancesSupervisedBy( userId, searchOptions, processInstanceService, searchEntitiesDescriptor, processDefinitionService); try { searchFailedProcessInstances.execute(); return searchFailedProcessInstances.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } protected SearchFailedProcessInstancesSupervisedBy createSearchFailedProcessInstancesSupervisedBy(final long userId, final SearchOptions searchOptions, final ProcessInstanceService processInstanceService, final SearchEntitiesDescriptor searchEntitiesDescriptor, final ProcessDefinitionService processDefinitionService) { return new SearchFailedProcessInstancesSupervisedBy(processInstanceService, searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), userId, searchOptions, processDefinitionService); } protected GetSUser createTxUserGetter(final long userId, final IdentityService identityService) { return new GetSUser(identityService, userId); } @Override public SearchResult searchOpenProcessInstancesSupervisedBy(final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final IdentityService identityService = serviceAccessor.getIdentityService(); final GetSUser getUser = createTxUserGetter(userId, identityService); try { getUser.execute(); } catch (final SBonitaException e) { return new SearchResultImpl<>(0, Collections.emptyList()); } final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchOpenProcessInstancesSupervisedBy searchOpenProcessInstances = new SearchOpenProcessInstancesSupervisedBy( processInstanceService, searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), userId, searchOptions, processDefinitionService); try { searchOpenProcessInstances.execute(); return searchOpenProcessInstances.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } @Override public SearchResult searchProcessDeploymentInfosStartedBy(final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor .getSearchProcessDefinitionsDescriptor(); final SearchProcessDeploymentInfosStartedBy searcher = new SearchProcessDeploymentInfosStartedBy( processDefinitionService, searchDescriptor, userId, searchOptions); try { searcher.execute(); } catch (final SBonitaException e) { throw new SearchException("Can't get ProcessDeploymentInfo startedBy userid " + userId, e); } return searcher.getResult(); } @Override public SearchResult searchProcessDeploymentInfos(final SearchOptions searchOptions) throws SearchException { return processDeploymentAPIDelegate.searchProcessDeploymentInfos(searchOptions); } @Override public SearchResult searchProcessDeploymentInfosCanBeStartedBy(final long userId, final SearchOptions searchOptions) throws RetrieveException, SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor .getSearchProcessDefinitionsDescriptor(); final SearchProcessDeploymentInfosCanBeStartedBy transactionSearch = new SearchProcessDeploymentInfosCanBeStartedBy( processDefinitionService, searchDescriptor, searchOptions, userId); try { transactionSearch.execute(); } catch (final SBonitaException e) { throw new SearchException("Error while retrieving process definitions: " + e.getMessage(), e); } return transactionSearch.getResult(); } @Override public SearchResult searchProcessDeploymentInfosCanBeStartedByUsersManagedBy( final long managerUserId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor .getSearchProcessDefinitionsDescriptor(); final SearchProcessDeploymentInfosCanBeStartedByUsersManagedBy transactionSearch = new SearchProcessDeploymentInfosCanBeStartedByUsersManagedBy( processDefinitionService, searchDescriptor, searchOptions, managerUserId); try { transactionSearch.execute(); } catch (final SBonitaException e) { throw new SearchException(e); } return transactionSearch.getResult(); } @Override public SearchResult searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor( final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor .getSearchProcessDefinitionsDescriptor(); final SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor searcher = new SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor( processDefinitionService, searchDescriptor, searchOptions, userId); try { searcher.execute(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } return searcher.getResult(); } @Override public SearchResult searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy( final long supervisorId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor .getSearchProcessDefinitionsDescriptor(); final SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy searcher = new SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy( processDefinitionService, searchDescriptor, searchOptions, supervisorId); try { searcher.execute(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } return searcher.getResult(); } @Override public SearchResult searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks( final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor .getSearchProcessDefinitionsDescriptor(); final SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasks searcher = new SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasks( processDefinitionService, searchDescriptor, searchOptions); try { searcher.execute(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } return searcher.getResult(); } @Override public Map> getActiveFlownodeStateCountersForProcessDefinition( final long processDefinitionId) throws ProcessDefinitionNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final HashMap> countersForProcessDefinition = new HashMap<>(); try { //make sure a process definition with this Id exists serviceAccessor.getProcessDefinitionService().getProcessDeploymentInfo(processDefinitionId); // Active flownodes: final List flownodes = serviceAccessor.getActivityInstanceService() .getNumberOfFlownodesOfProcessDefinitionInAllStates( processDefinitionId); for (final SFlowNodeInstanceStateCounter nodeCounter : flownodes) { final String flownodeName = nodeCounter.getFlownodeName(); final Map flownodeCounters = getFlowNodeCounters(countersForProcessDefinition, flownodeName); flownodeCounters.put(nodeCounter.getStateName(), nodeCounter.getNumberOf()); countersForProcessDefinition.put(flownodeName, flownodeCounters); } } catch (final SBonitaReadException e) { throw new RetrieveException(e); } catch (SProcessDefinitionNotFoundException e) { throw new ProcessDefinitionNotFoundException(processDefinitionId, e); } return countersForProcessDefinition; } @Override public Map> getFlownodeStateCounters(final long processInstanceId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final HashMap> countersForProcessInstance = new HashMap<>(); try { //make sure a process instance with this Id exists // -- BEWARE: when a process instance is started by a Start Timer Event, // -- there is NO 'stateid=0' archived in the ARCH_PROCESS_INSTANCE table // -- Therefore, if such a process instance is still running, there won't be any record // -- in the ARCH_PROCESS_INSTANCE table. // -- Hence the need to also check in the PROCESS_INSTANCE table. if (serviceAccessor.getProcessInstanceService().getLastArchivedProcessInstance(processInstanceId) == null && serviceAccessor.getProcessInstanceService().getProcessInstance(processInstanceId) == null) { throw new SProcessInstanceNotFoundException(processInstanceId); } // Active flownodes: final List flownodes = serviceAccessor.getActivityInstanceService() .getNumberOfFlownodesInAllStates( processInstanceId); for (final SFlowNodeInstanceStateCounter nodeCounter : flownodes) { if (nodeCounter.getStateName() != null) { final String flownodeName = nodeCounter.getFlownodeName(); final Map flownodeCounters = getFlowNodeCounters(countersForProcessInstance, flownodeName); flownodeCounters.put(nodeCounter.getStateName(), nodeCounter.getNumberOf()); countersForProcessInstance.put(flownodeName, flownodeCounters); } } // Archived flownodes: final List archivedFlownodes = serviceAccessor.getActivityInstanceService() .getNumberOfArchivedFlownodesInAllStates( processInstanceId); for (final SFlowNodeInstanceStateCounter nodeCounter : archivedFlownodes) { if (nodeCounter.getStateName() != null) { final String flownodeName = nodeCounter.getFlownodeName(); final Map flownodeCounters = getFlowNodeCounters(countersForProcessInstance, flownodeName); flownodeCounters.put(nodeCounter.getStateName(), nodeCounter.getNumberOf()); countersForProcessInstance.put(flownodeName, flownodeCounters); } } } catch (final SProcessInstanceNotFoundException | SProcessInstanceReadException e) { //cannot throw directly a ProcessInstanceNotFoundException to avoid API break throw new RetrieveException(new ProcessInstanceNotFoundException(e, processInstanceId)); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } return countersForProcessInstance; } private Map getFlowNodeCounters(final HashMap> counters, final String flowNodeName) { return counters.computeIfAbsent(flowNodeName, k -> new HashMap<>()); } @Override public SearchResult searchProcessDeploymentInfosSupervisedBy(final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor .getSearchProcessDefinitionsDescriptor(); final SearchProcessDeploymentInfosSupervised searcher = new SearchProcessDeploymentInfosSupervised( processDefinitionService, searchDescriptor, searchOptions, userId); try { searcher.execute(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } return searcher.getResult(); } @Override public SearchResult searchAssignedTasksSupervisedBy(final long supervisorId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance( searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(), searchOptions, flowNodeStateManager, (queryOptions) -> activityInstanceService.getNumberOfAssignedTasksSupervisedBy(supervisorId, queryOptions), (queryOptions) -> activityInstanceService.searchAssignedTasksSupervisedBy(supervisorId, queryOptions)) .search(); } @Override public SearchResult searchArchivedHumanTasksSupervisedBy(final long supervisorId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchArchivedHumanTasksSupervisedBy searchedTasksTransaction = new SearchArchivedHumanTasksSupervisedBy( supervisorId, activityInstanceService, flowNodeStateManager, searchEntitiesDescriptor.getSearchArchivedHumanTaskInstanceDescriptor(), searchOptions); try { searchedTasksTransaction.execute(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } return searchedTasksTransaction.getResult(); } @Override public SearchResult searchProcessSupervisors(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService(); final SearchProcessSupervisorDescriptor searchDescriptor = new SearchProcessSupervisorDescriptor(); final SearchSupervisors searchSupervisorsTransaction = new SearchSupervisors(supervisorService, searchDescriptor, searchOptions); try { searchSupervisorsTransaction.execute(); return searchSupervisorsTransaction.getResult(); } catch (final SBonitaException e) { throw new SearchException(e); } } @Override public boolean isUserProcessSupervisor(final long processDefinitionId, final long userId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService(); try { return supervisorService.isProcessSupervisor(processDefinitionId, userId); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } } @Override public void deleteSupervisor(final long supervisorId) throws DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService(); try { supervisorService.deleteProcessSupervisor(supervisorId); log.info("The process manager has been deleted with id = <" + supervisorId + ">."); } catch (final SSupervisorNotFoundException spsnfe) { throw new DeletionException(new SupervisorNotFoundException( "The process manager was not found with id = <" + supervisorId + ">")); } catch (final SSupervisorDeletionException e) { throw new DeletionException(e); } } @Override public void deleteSupervisor(final Long processDefinitionId, final Long userId, final Long roleId, final Long groupId) throws DeletionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService(); try { final List sProcessSupervisors = searchSProcessSupervisors(processDefinitionId, userId, groupId, roleId); if (!sProcessSupervisors.isEmpty()) { // Then, delete it supervisorService.deleteProcessSupervisor(sProcessSupervisors.get(0)); log.info("The process manager has been deleted with process definition id = <" + processDefinitionId + ">, user id = <" + userId + ">, group id = <" + groupId + ">, and role id = <" + roleId + ">."); } else { throw new DeletionException(new SupervisorNotFoundException( "The process manager was not found with process definition id = <" + processDefinitionId + ">, user id = <" + userId + ">, group id = <" + groupId + ">, and role id = <" + roleId + ">.")); } } catch (final SBonitaException e) { throw new DeletionException(e); } } private void deleteAllSupervisorsOfProcess(final long processDefinitionId) throws DeletionException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, Integer.MAX_VALUE); builder.filter(ProcessSupervisorSearchDescriptor.PROCESS_DEFINITION_ID, processDefinitionId); try { SearchResult processSupervisors = searchProcessSupervisors(builder.done()); List supervisorIds = processSupervisors.getResult().stream().map(ProcessSupervisor::getSupervisorId) .collect(Collectors.toList()); for (Long id : supervisorIds) { deleteSupervisor(id); } } catch (SearchException e) { throw new DeletionException(e); } } @Override public ProcessSupervisor createProcessSupervisorForUser(final long processDefinitionId, final long userId) throws CreationException { return createSupervisor(SProcessSupervisor.builder().processDefId(processDefinitionId).userId(userId).build()); } @Override public ProcessSupervisor createProcessSupervisorForRole(final long processDefinitionId, final long roleId) throws CreationException { return createSupervisor(SProcessSupervisor.builder().processDefId(processDefinitionId).roleId(roleId).build()); } @Override public ProcessSupervisor createProcessSupervisorForGroup(final long processDefinitionId, final long groupId) throws CreationException { return createSupervisor( SProcessSupervisor.builder().processDefId(processDefinitionId).groupId(groupId).build()); } @Override public ProcessSupervisor createProcessSupervisorForMembership(final long processDefinitionId, final long groupId, final long roleId) throws CreationException { return createSupervisor( SProcessSupervisor.builder().processDefId(processDefinitionId).groupId(groupId).roleId(roleId).build()); } private ProcessSupervisor createSupervisor(final SProcessSupervisor sProcessSupervisor) throws CreationException { final ServiceAccessor ServiceAccessor = getServiceAccessor(); final SupervisorMappingService supervisorService = ServiceAccessor.getSupervisorService(); try { checkIfProcessSupervisorAlreadyExists(sProcessSupervisor.getProcessDefId(), sProcessSupervisor.getUserId(), sProcessSupervisor.getGroupId(), sProcessSupervisor.getRoleId()); final SProcessSupervisor supervisor = supervisorService.createProcessSupervisor(sProcessSupervisor); log.info("The process manager has been created with process definition id = <" + sProcessSupervisor.getProcessDefId() + ">, user id = <" + sProcessSupervisor.getUserId() + ">, group id = <" + sProcessSupervisor.getGroupId() + ">, and role id = <" + sProcessSupervisor.getRoleId() + ">"); return ModelConvertor.toProcessSupervisor(supervisor); } catch (final SBonitaException e) { throw new CreationException(e); } } private void checkIfProcessSupervisorAlreadyExists(final long processDefinitionId, final Long userId, final Long groupId, final Long roleId) throws AlreadyExistsException { try { final List processSupervisors = searchSProcessSupervisors(processDefinitionId, userId, groupId, roleId); if (!processSupervisors.isEmpty()) { throw new AlreadyExistsException("This supervisor already exists for process definition id = <" + processDefinitionId + ">, user id = <" + userId + ">, group id = <" + groupId + ">, role id = <" + roleId + ">"); } } catch (final SBonitaReadException e1) { // Nothing to do } } protected List searchSProcessSupervisors(final Long processDefinitionId, final Long userId, final Long groupId, final Long roleId) throws SBonitaReadException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SupervisorMappingService supervisorService = serviceAccessor.getSupervisorService(); final List oderByOptions = Collections.singletonList( new OrderByOption(SProcessSupervisor.class, SProcessSupervisor.USER_ID_KEY, OrderByType.DESC)); final List filterOptions = new ArrayList<>(); filterOptions.add(new FilterOption(SProcessSupervisor.class, SProcessSupervisor.PROCESS_DEF_ID_KEY, processDefinitionId == null ? -1 : processDefinitionId)); filterOptions.add(new FilterOption(SProcessSupervisor.class, SProcessSupervisor.USER_ID_KEY, userId == null ? -1 : userId)); filterOptions.add(new FilterOption(SProcessSupervisor.class, SProcessSupervisor.GROUP_ID_KEY, groupId == null ? -1 : groupId)); filterOptions.add(new FilterOption(SProcessSupervisor.class, SProcessSupervisor.ROLE_ID_KEY, roleId == null ? -1 : roleId)); return supervisorService.searchProcessSupervisors(new QueryOptions(0, 1, oderByOptions, filterOptions, null)); } @Override public SearchResult searchUncategorizedProcessDeploymentInfosCanBeStartedBy( final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor .getSearchProcessDefinitionsDescriptor(); final SearchUncategorizedProcessDeploymentInfosCanBeStartedBy transactionSearch = new SearchUncategorizedProcessDeploymentInfosCanBeStartedBy( processDefinitionService, searchDescriptor, searchOptions, userId); try { transactionSearch.execute(); } catch (final SBonitaException e) { throw new SearchException("Error while retrieving process definitions", e); } return transactionSearch.getResult(); } @Override public SearchResult searchArchivedHumanTasksManagedBy(final long managerUserId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchArchivedTasksManagedBy searchTransaction = new SearchArchivedTasksManagedBy(managerUserId, searchOptions, activityInstanceService, flowNodeStateManager, searchEntitiesDescriptor.getSearchArchivedHumanTaskInstanceDescriptor()); try { searchTransaction.execute(); } catch (final SBonitaException e) { throw new SearchException(e); } return searchTransaction.getResult(); } @Override public SearchResult searchOpenProcessInstancesInvolvingUser(final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchOpenProcessInstancesInvolvingUser searchOpenProcessInstances = new SearchOpenProcessInstancesInvolvingUser( processInstanceService, searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), userId, searchOptions, processDefinitionService); try { searchOpenProcessInstances.execute(); return searchOpenProcessInstances.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } @Override public SearchResult searchOpenProcessInstancesInvolvingUsersManagedBy(final long managerUserId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchOpenProcessInstancesInvolvingUsersManagedBy searchOpenProcessInstances = new SearchOpenProcessInstancesInvolvingUsersManagedBy( processInstanceService, searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), managerUserId, searchOptions, processDefinitionService); try { searchOpenProcessInstances.execute(); return searchOpenProcessInstances.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } @Override public SearchResult searchArchivedHumanTasks(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchArchivedTasks searchArchivedTasks = new SearchArchivedTasks(activityInstanceService, flowNodeStateManager, searchEntitiesDescriptor.getSearchArchivedHumanTaskInstanceDescriptor(), searchOptions); try { searchArchivedTasks.execute(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } return searchArchivedTasks.getResult(); } @Override public SearchResult searchAssignedTasksManagedBy(final long managerUserId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance( searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(), searchOptions, flowNodeStateManager, (queryOptions) -> activityInstanceService.getNumberOfAssignedTasksManagedBy(managerUserId, queryOptions), (queryOptions) -> activityInstanceService.searchAssignedTasksManagedBy(managerUserId, queryOptions)) .search(); } @Override public SearchResult searchArchivedProcessInstancesSupervisedBy(final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchArchivedProcessInstancesSupervisedBy searchArchivedProcessInstances = new SearchArchivedProcessInstancesSupervisedBy( userId, processInstanceService, serviceAccessor.getProcessDefinitionService(), searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptions); try { searchArchivedProcessInstances.execute(); return searchArchivedProcessInstances.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } @Override public SearchResult searchArchivedProcessInstancesInvolvingUser(final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchArchivedProcessInstancesInvolvingUser searchArchivedProcessInstances = new SearchArchivedProcessInstancesInvolvingUser( userId, processInstanceService, serviceAccessor.getProcessDefinitionService(), searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptions); try { searchArchivedProcessInstances.execute(); return searchArchivedProcessInstances.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } @Override public SearchResult searchPendingTasksForUser(final long userId, final SearchOptions searchOptions) throws SearchException { return searchTasksForUser(userId, searchOptions, ""); } /** * @param queryType can be : * PendingOrAssignedOrAssignedToOthers if we also want to retrieve tasks directly assigned to this user or * tasks assigned * to other users. * PendingOrAssigned if we also want to retrieve tasks directly assigned to this user. * empty for the default behaviour; retrieve pending tasks for user. */ private SearchResult searchTasksForUser(final long userId, final SearchOptions searchOptions, final String queryType) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); switch (queryType) { case PENDING_OR_ASSIGNED_OR_ASSIGNED_TO_OTHERS: return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance( searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(), searchOptions, flowNodeStateManager, (queryOptions) -> activityInstanceService.getNumberOfPendingOrAssignedOrAssignedToOthersTasks( userId, queryOptions), (queryOptions) -> activityInstanceService.searchPendingOrAssignedOrAssignedToOthersTasks(userId, queryOptions)) .search(); case PENDING_OR_ASSIGNED: return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance( searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(), searchOptions, flowNodeStateManager, (queryOptions) -> activityInstanceService.getNumberOfPendingOrAssignedTasks(userId, queryOptions), (queryOptions) -> activityInstanceService.searchPendingOrAssignedTasks(userId, queryOptions)) .search(); default: return AbstractHumanTaskInstanceSearchEntity .searchHumanTaskInstance(searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(), searchOptions, flowNodeStateManager, (queryOptions) -> activityInstanceService.getNumberOfPendingTasksForUser(userId, queryOptions), (queryOptions) -> activityInstanceService.searchPendingTasksForUser(userId, queryOptions)) .search(); } } @Override public SearchResult searchPendingTasksAssignedToUser(final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); return AbstractHumanTaskInstanceSearchEntity .searchHumanTaskInstance(searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(), searchOptions, flowNodeStateManager, (queryOptions) -> activityInstanceService.getNumberOfPendingTasksAssignedTo(userId, queryOptions), (queryOptions) -> activityInstanceService.searchPendingTasksAssignedTo(userId, queryOptions)) .search(); } @Override public SearchResult searchMyAvailableHumanTasks(final long userId, final SearchOptions searchOptions) throws SearchException { return searchTasksForUser(userId, searchOptions, PENDING_OR_ASSIGNED); } @Override public SearchResult searchPendingOrAssignedToUserOrAssignedToOthersTasks(final long userId, final SearchOptions searchOptions) throws SearchException { return searchTasksForUser(userId, searchOptions, PENDING_OR_ASSIGNED_OR_ASSIGNED_TO_OTHERS); } @Override public SearchResult searchPendingTasksSupervisedBy(final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); return AbstractHumanTaskInstanceSearchEntity .searchHumanTaskInstance(searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(), searchOptions, flowNodeStateManager, (queryOptions) -> activityInstanceService.getNumberOfPendingTasksSupervisedBy(userId, queryOptions), (queryOptions) -> activityInstanceService.searchPendingTasksSupervisedBy(userId, queryOptions)) .search(); } @Override public SearchResult searchComments(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SCommentService commentService = serviceAccessor.getCommentService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchComments searchComments = new SearchComments(searchEntitiesDescriptor.getSearchCommentDescriptor(), searchOptions, commentService); try { searchComments.execute(); return searchComments.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } @Override public Comment addProcessComment(final long processInstanceId, final String comment) throws CreationException { return addProcessCommentOnBehalfOfUser(processInstanceId, comment, getUserId()); } @Override public Comment addProcessCommentOnBehalfOfUser(final long processInstanceId, final String comment, long userId) throws CreationException { final ServiceAccessor serviceAccessor = getServiceAccessor(); try { serviceAccessor.getProcessInstanceService().getProcessInstance(processInstanceId); } catch (final SProcessInstanceReadException | SProcessInstanceNotFoundException e) { throw new RetrieveException(buildCantAddCommentOnProcessInstance(), e); // FIXME: should be another exception } final SCommentService commentService = serviceAccessor.getCommentService(); try { SComment sComment = commentService.addComment(processInstanceId, comment, userId); return ModelConvertor.toComment(sComment); } catch (final SBonitaException e) { throw new CreationException(buildCantAddCommentOnProcessInstance(), e.getCause()); } } private String buildCantAddCommentOnProcessInstance() { return "Cannot add a comment on a finished or inexistant process instance"; } @Override public Document attachDocument(final long processInstanceId, final String documentName, final String fileName, final String mimeType, final String url) throws DocumentAttachmentException, ProcessInstanceNotFoundException { return documentAPI.attachDocument(processInstanceId, documentName, fileName, mimeType, url); } @Override public Document attachDocument(final long processInstanceId, final String documentName, final String fileName, final String mimeType, final byte[] documentContent) throws DocumentAttachmentException, ProcessInstanceNotFoundException { return documentAPI.attachDocument(processInstanceId, documentName, fileName, mimeType, documentContent); } @Override public Document attachNewDocumentVersion(final long processInstanceId, final String documentName, final String fileName, final String mimeType, final String url) throws DocumentAttachmentException { return documentAPI.attachNewDocumentVersion(processInstanceId, documentName, fileName, mimeType, url); } @Override public Document attachNewDocumentVersion(final long processInstanceId, final String documentName, final String contentFileName, final String contentMimeType, final byte[] documentContent) throws DocumentAttachmentException { return documentAPI.attachNewDocumentVersion(processInstanceId, documentName, contentFileName, contentMimeType, documentContent); } @Override public Document getDocument(final long documentId) throws DocumentNotFoundException { return documentAPI.getDocument(documentId); } @Override public List getLastVersionOfDocuments(final long processInstanceId, final int pageIndex, final int numberPerPage, final DocumentCriterion pagingCriterion) throws DocumentException, ProcessInstanceNotFoundException { return documentAPI.getLastVersionOfDocuments(processInstanceId, pageIndex, numberPerPage, pagingCriterion); } @Override public byte[] getDocumentContent(final String documentStorageId) throws DocumentNotFoundException { return documentAPI.getDocumentContent(documentStorageId); } @Override public Document getLastDocument(final long processInstanceId, final String documentName) throws DocumentNotFoundException { return documentAPI.getLastDocument(processInstanceId, documentName); } @Override public long getNumberOfDocuments(final long processInstanceId) throws DocumentException { return documentAPI.getNumberOfDocuments(processInstanceId); } @Override public Document getDocumentAtProcessInstantiation(final long processInstanceId, final String documentName) throws DocumentNotFoundException { return documentAPI.getDocumentAtProcessInstantiation(processInstanceId, documentName); } @Override public Document getDocumentAtActivityInstanceCompletion(final long activityInstanceId, final String documentName) throws DocumentNotFoundException { return documentAPI.getDocumentAtActivityInstanceCompletion(activityInstanceId, documentName); } @Override public SearchResult searchPendingTasksManagedBy(final long managerUserId, final SearchOptions searchOptions) throws SearchException { return taskInvolvementDelegate.searchPendingTasksManagedBy(managerUserId, searchOptions); } @Override public Map getNumberOfOverdueOpenTasks(final List userIds) throws RetrieveException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); try { return activityInstanceService.getNumberOfOverdueOpenTasksForUsers(userIds); } catch (final SBonitaException e) { logError(e); throw new RetrieveException(e.getMessage()); } } @Override public SearchResult searchUncategorizedProcessDeploymentInfos( final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor .getSearchProcessDefinitionsDescriptor(); final SearchUncategorizedProcessDeploymentInfos transactionSearch = new SearchUncategorizedProcessDeploymentInfos( processDefinitionService, searchDescriptor, searchOptions); try { transactionSearch.execute(); } catch (final SBonitaException e) { throw new SearchException("Problem encountered while searching for Uncategorized Process Definitions", e); } return transactionSearch.getResult(); } @Override public SearchResult searchCommentsManagedBy(final long managerUserId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SCommentService commentService = serviceAccessor.getCommentService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchCommentsManagedBy searchComments = new SearchCommentsManagedBy( searchEntitiesDescriptor.getSearchCommentDescriptor(), searchOptions, commentService, managerUserId); try { searchComments.execute(); return searchComments.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } @Override public SearchResult searchCommentsInvolvingUser(final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SCommentService commentService = serviceAccessor.getCommentService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchCommentsInvolvingUser searchComments = new SearchCommentsInvolvingUser( searchEntitiesDescriptor.getSearchCommentDescriptor(), searchOptions, commentService, userId); try { searchComments.execute(); return searchComments.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } @Override public List getChildrenInstanceIdsOfProcessInstance(final long processInstanceId, final int startIndex, final int maxResults, final ProcessInstanceCriterion criterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); long totalNumber; try { totalNumber = processInstanceService.getNumberOfChildInstancesOfProcessInstance(processInstanceId); if (totalNumber == 0) { return Collections.emptyList(); } final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForProcessInstance(criterion); return processInstanceService.getChildInstanceIdsOfProcessInstance(processInstanceId, startIndex, maxResults, orderAndField.getField(), orderAndField.getOrder()); } catch (final SProcessInstanceReadException e) { throw new RetrieveException(e); } } @Override public SearchResult searchUncategorizedProcessDeploymentInfosSupervisedBy(final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor .getSearchProcessDefinitionsDescriptor(); final SearchUncategorizedProcessDeploymentInfosSupervisedBy transactionSearch = new SearchUncategorizedProcessDeploymentInfosSupervisedBy( processDefinitionService, searchDescriptor, searchOptions, userId); try { transactionSearch.execute(); } catch (final SBonitaException e) { throw new SearchException( "Problem encountered while searching for Uncategorized Process Definitions for a supervisor", e); } return transactionSearch.getResult(); } @Override public Map getProcessDeploymentInfosFromIds(final List processDefinitionIds) { return ProcessDeploymentAPIDelegate.getInstance().getProcessDeploymentInfosFromIds(processDefinitionIds); } @Override public List getConnectorImplementations(final long processDefinitionId, final int startIndex, final int maxsResults, final ConnectorCriterion sortingCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ConnectorService connectorService = serviceAccessor.getConnectorService(); final OrderAndField orderAndField = OrderAndFields.getOrderAndFieldForConnectorImplementation(sortingCriterion); try { final List sConnectorImplementationDescriptors = connectorService .getConnectorImplementations(processDefinitionId, startIndex, maxsResults, orderAndField.getField(), orderAndField.getOrder()); return ModelConvertor.toConnectorImplementationDescriptors(sConnectorImplementationDescriptors); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public long getNumberOfConnectorImplementations(final long processDefinitionId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ConnectorService connectorService = serviceAccessor.getConnectorService(); try { return connectorService.getNumberOfConnectorImplementations(processDefinitionId); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public SearchResult searchActivities(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final SearchActivityInstances searchActivityInstancesTransaction; try { searchActivityInstancesTransaction = new SearchActivityInstances(activityInstanceService, flowNodeStateManager, searchEntitiesDescriptor.getSearchActivityInstanceDescriptor(), searchOptions); searchActivityInstancesTransaction.execute(); } catch (final SBonitaException e) { throw new SearchException(e); } return searchActivityInstancesTransaction.getResult(); } @Override public SearchResult searchArchivedFlowNodeInstances(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final SearchArchivedFlowNodeInstances searchTransaction = new SearchArchivedFlowNodeInstances( activityInstanceService, flowNodeStateManager, searchEntitiesDescriptor.getSearchArchivedFlowNodeInstanceDescriptor(), searchOptions); try { searchTransaction.execute(); } catch (final SBonitaException e) { throw new SearchException(e); } return searchTransaction.getResult(); } @Override public SearchResult searchFlowNodeInstances(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final SearchFlowNodeInstances searchFlowNodeInstancesTransaction = new SearchFlowNodeInstances( activityInstanceService, flowNodeStateManager, searchEntitiesDescriptor.getSearchFlowNodeInstanceDescriptor(), searchOptions); try { searchFlowNodeInstancesTransaction.execute(); } catch (final SBonitaException e) { throw new SearchException(e); } return searchFlowNodeInstancesTransaction.getResult(); } @Override public SearchResult searchTimerEventTriggerInstances(final long processInstanceId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final EventInstanceService eventInstanceService = serviceAccessor.getEventInstanceService(); final SearchTimerEventTriggerInstances transaction = new SearchTimerEventTriggerInstances(eventInstanceService, searchEntitiesDescriptor.getSearchEventTriggerInstanceDescriptor(), processInstanceId, searchOptions); try { transaction.execute(); } catch (final SBonitaException e) { throw new SearchException(e); } return transaction.getResult(); } @Override public Date updateExecutionDateOfTimerEventTriggerInstance(final long timerEventTriggerInstanceId, final Date executionDate) throws TimerEventTriggerInstanceNotFoundException, UpdateException { if (executionDate == null) { throw new UpdateException("The date must be not null !!"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final EventInstanceService eventInstanceService = serviceAccessor.getEventInstanceService(); final SchedulerService schedulerService = serviceAccessor.getSchedulerService(); final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(EXECUTION_DATE, executionDate.getTime()); try { final STimerEventTriggerInstance sTimerEventTriggerInstance = eventInstanceService.getEventTriggerInstance( STimerEventTriggerInstance.class, timerEventTriggerInstanceId); if (sTimerEventTriggerInstance == null) { throw new TimerEventTriggerInstanceNotFoundException(timerEventTriggerInstanceId); } eventInstanceService.updateEventTriggerInstance(sTimerEventTriggerInstance, descriptor); return schedulerService.rescheduleJob(sTimerEventTriggerInstance.getJobTriggerName(), executionDate); } catch (final SBonitaException sbe) { throw new UpdateException(sbe); } } @Override public SearchResult searchArchivedActivities(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final SearchArchivedActivityInstances searchActivityInstancesTransaction = new SearchArchivedActivityInstances( activityInstanceService, flowNodeStateManager, searchEntitiesDescriptor.getSearchArchivedActivityInstanceDescriptor(), searchOptions); try { searchActivityInstancesTransaction.execute(); } catch (final SBonitaException e) { throw new SearchException(e); } return searchActivityInstancesTransaction.getResult(); } @Override public ConnectorImplementationDescriptor getConnectorImplementation(final long processDefinitionId, final String connectorId, final String connectorVersion) throws ConnectorNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ConnectorService connectorService = serviceAccessor.getConnectorService(); final GetConnectorImplementation transactionContent = new GetConnectorImplementation(connectorService, processDefinitionId, connectorId, connectorVersion); try { transactionContent.execute(); final SConnectorImplementationDescriptor sConnectorImplementationDescriptor = transactionContent .getResult(); return ModelConvertor.toConnectorImplementationDescriptor(sConnectorImplementationDescriptor); } catch (final SBonitaException e) { throw new ConnectorNotFoundException(e); } } @Override @CustomTransactions public void cancelProcessInstance(final long processInstanceId) throws ProcessInstanceNotFoundException, UpdateException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final LockService lockService = serviceAccessor.getLockService(); final ProcessInstanceInterruptor processInstanceInterruptor = serviceAccessor.getProcessInstanceInterruptor(); BonitaLock lock = null; try { // lock process execution lock = lockService.lock(processInstanceId, SFlowElementsContainerType.PROCESS.name()); inTx(() -> { try { return processInstanceInterruptor.interruptProcessInstance(processInstanceId, SStateCategory.CANCELLING); } catch (final SProcessInstanceNotFoundException spinfe) { throw new ProcessInstanceNotFoundException(processInstanceId); } catch (final SBonitaException e) { throw new UpdateException(e); } }); } catch (ProcessInstanceNotFoundException | UpdateException e) { throw e; } catch (Exception e) { throw new BonitaRuntimeException(String.format("Failed to cancel process instance %s", processInstanceId), e); } finally { // unlock process execution try { lockService.unlock(lock); } catch (final SLockException e) { // ignore it } } } @Override public void setProcessInstanceState(final ProcessInstance processInstance, final String state) throws UpdateException { // NOW, is only available for COMPLETED, ABORTED, CANCELLED, STARTED final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); try { final ProcessInstanceState processInstanceState = ModelConvertor.getProcessInstanceState(state); final SetProcessInstanceState transactionContent = new SetProcessInstanceState(processInstanceService, processInstance.getId(), processInstanceState); transactionContent.execute(); } catch (final IllegalArgumentException | SBonitaException e) { throw new UpdateException(e.getMessage()); } } @Override public Map getProcessDeploymentInfosFromProcessInstanceIds( final List processInstanceIds) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final Map sProcessDeploymentInfos = processDefinitionService .getProcessDeploymentInfosFromProcessInstanceIds(processInstanceIds); return ModelConvertor.toProcessDeploymentInfos(sProcessDeploymentInfos); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public Map getProcessDeploymentInfosFromArchivedProcessInstanceIds( final List archivedProcessInstantsIds) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final Map sProcessDeploymentInfos = processDefinitionService .getProcessDeploymentInfosFromArchivedProcessInstanceIds(archivedProcessInstantsIds); return ModelConvertor.toProcessDeploymentInfos(sProcessDeploymentInfos); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public SearchResult searchDocuments(final SearchOptions searchOptions) throws SearchException { return documentAPI.searchDocuments(searchOptions); } @Override public SearchResult searchDocumentsSupervisedBy(final long userId, final SearchOptions searchOptions) throws SearchException, UserNotFoundException { return documentAPI.searchDocumentsSupervisedBy(userId, searchOptions); } @Override public SearchResult searchArchivedDocuments(final SearchOptions searchOptions) throws SearchException { return documentAPI.searchArchivedDocuments(searchOptions); } @Override public SearchResult searchArchivedDocumentsSupervisedBy(final long userId, final SearchOptions searchOptions) throws SearchException, UserNotFoundException { return documentAPI.searchArchivedDocumentsSupervisedBy(userId, searchOptions); } @Override public void retryTask(final long activityInstanceId) throws ActivityExecutionException, ActivityInstanceNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); ConnectorInstanceService connectorInstanceService = serviceAccessor.getConnectorInstanceService(); ResetAllFailedConnectorStrategy strategy = new ResetAllFailedConnectorStrategy(connectorInstanceService, new ConnectorReseter(connectorInstanceService), BATCH_SIZE); FlowNodeRetrier flowNodeRetrier = new FlowNodeRetrier(serviceAccessor.getContainerRegistry(), serviceAccessor.getFlowNodeExecutor(), serviceAccessor.getActivityInstanceService(), serviceAccessor.getFlowNodeStateManager(), strategy); flowNodeRetrier.retry(activityInstanceId); } @Override public void executeMessageCouple(final long messageInstanceId, final long waitingMessageId) throws ExecutionException { MessagesHandlingService messagesHandlingService = getServiceAccessor().getMessagesHandlingService(); try { messagesHandlingService.resetMessageCouple(messageInstanceId, waitingMessageId); messagesHandlingService.triggerMatchingOfMessages(); } catch (SBonitaException e) { throw new ExecutionException( "Failed to execute Event Message couple: messageInstanceId=" + messageInstanceId + ", waitingMessageId=" + waitingMessageId, e); } } @Override public ArchivedDocument getArchivedVersionOfProcessDocument(final long sourceObjectId) throws ArchivedDocumentNotFoundException { return documentAPI.getArchivedVersionOfProcessDocument(sourceObjectId); } @Override public ArchivedDocument getArchivedProcessDocument(final long archivedProcessDocumentId) throws ArchivedDocumentNotFoundException { return documentAPI.getArchivedProcessDocument(archivedProcessDocumentId); } @Override public SearchResult searchArchivedComments(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SCommentService sCommentService = serviceAccessor.getCommentService(); final SearchArchivedComments searchArchivedComments = new SearchArchivedComments(sCommentService, searchEntitiesDescriptor.getSearchArchivedCommentsDescriptor(), searchOptions); try { searchArchivedComments.execute(); } catch (final SBonitaException e) { throw new SearchException(e); } return searchArchivedComments.getResult(); } @Override public ArchivedComment getArchivedComment(final long archivedCommentId) throws RetrieveException, NotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SCommentService sCommentService = serviceAccessor.getCommentService(); try { final SAComment archivedComment = sCommentService.getArchivedComment(archivedCommentId); return ModelConvertor.toArchivedComment(archivedComment); } catch (final SCommentNotFoundException e) { throw new NotFoundException(e); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public Map getActorsFromActorIds(final List actorIds) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final Map res = new HashMap<>(); final ActorMappingService actormappingService = serviceAccessor.getActorMappingService(); final GetActorsByActorIds getActorsByActorIds = new GetActorsByActorIds(actormappingService, actorIds); try { getActorsByActorIds.execute(); } catch (final SBonitaException e1) { throw new RetrieveException(e1); } final List actors = getActorsByActorIds.getResult(); for (final SActor actor : actors) { res.put(actor.getId(), ModelConvertor.toActorInstance(actor)); } return res; } @Override public Serializable evaluateExpressionOnProcessDefinition(final Expression expression, final Map context, final long processDefinitionId) throws ExpressionEvaluationException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ExpressionResolverService expressionResolverService = serviceAccessor.getExpressionResolverService(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SExpression sExpression = ModelConvertor.constructSExpression(expression); final SExpressionContext expcontext = new SExpressionContext(); expcontext.setProcessDefinitionId(processDefinitionId); SProcessDefinition processDef; try { processDef = processDefinitionService.getProcessDefinition(processDefinitionId); if (processDef != null) { expcontext.setProcessDefinition(processDef); } final HashMap hashMap = new HashMap<>(context); expcontext.setInputValues(hashMap); return (Serializable) expressionResolverService.evaluate(sExpression, expcontext); } catch (final SExpressionEvaluationException e) { throw new ExpressionEvaluationException(e, e.getExpressionName()); } catch (final SInvalidExpressionException e) { throw new ExpressionEvaluationException(e, e.getExpressionName()); } catch (final SBonitaException e) { throw new ExpressionEvaluationException(e, null); } } @Override public void updateDueDateOfTask(final long userTaskId, final Date dueDate) throws UpdateException { final ActivityInstanceService activityInstanceService = getServiceAccessor().getActivityInstanceService(); try { final SetExpectedEndDate updateProcessInstance = new SetExpectedEndDate(activityInstanceService, userTaskId, dueDate); updateProcessInstance.execute(); } catch (final SBonitaException e) { throw new UpdateException(e); } } @Override public long countComments(final SearchOptions searchOptions) throws SearchException { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 0) .setFilters(searchOptions.getFilters()).searchTerm( searchOptions.getSearchTerm()); final SearchResult searchResult = searchComments(searchOptionsBuilder.done()); return searchResult.getCount(); } @Override public long countAttachments(final SearchOptions searchOptions) throws SearchException { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 0) .setFilters(searchOptions.getFilters()).searchTerm( searchOptions.getSearchTerm()); final SearchResult searchResult = documentAPI.searchDocuments(searchOptionsBuilder.done()); return searchResult.getCount(); } @Override public void sendSignal(final String signalName) throws SendEventException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final EventsHandler eventsHandler = serviceAccessor.getEventsHandler(); final SThrowSignalEventTriggerDefinition signalEventTriggerDefinition = BuilderFactory .get(SThrowSignalEventTriggerDefinitionBuilderFactory.class) .createNewInstance(signalName).done(); try { eventsHandler.handleThrowEvent(signalEventTriggerDefinition); } catch (final SBonitaException e) { throw new SendEventException(e); } } @Override public void sendMessage(final String messageName, final Expression targetProcess, final Expression targetFlowNode, final Map messageContent) throws SendEventException { sendMessage(messageName, targetProcess, targetFlowNode, messageContent, null); } @Override public void sendMessage(final String messageName, final Expression targetProcess, final Expression targetFlowNode, final Map messageContent, final Map correlations) throws SendEventException { if (correlations != null && correlations.size() > 5) { throw new SendEventException("Too many correlations: a message can not have more than 5 correlations."); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final EventsHandler eventsHandler = serviceAccessor.getEventsHandler(); final ExpressionResolverService expressionResolverService = serviceAccessor.getExpressionResolverService(); final SExpression targetProcessNameExp = ModelConvertor.constructSExpression(targetProcess); SExpression targetFlowNodeNameExp = null; if (targetFlowNode != null) { targetFlowNodeNameExp = ModelConvertor.constructSExpression(targetFlowNode); } final SThrowMessageEventTriggerDefinitionBuilder builder = BuilderFactory .get(SThrowMessageEventTriggerDefinitionBuilderFactory.class) .createNewInstance(messageName, targetProcessNameExp, targetFlowNodeNameExp); if (correlations != null && !correlations.isEmpty()) { addMessageCorrelations(builder, correlations); } try { if (messageContent != null && !messageContent.isEmpty()) { addMessageContent(builder, expressionResolverService, messageContent); } final SThrowMessageEventTriggerDefinition messageEventTriggerDefinition = builder.done(); eventsHandler.handleThrowEvent(messageEventTriggerDefinition); } catch (final SBonitaException e) { throw new SendEventException(e); } } @Override public int deleteMessageByCreationDate(final long creationDate, SearchOptions searchOptions) throws ExecutionException { try { return getServiceAccessor().getEventInstanceService() .deleteMessageAndDataInstanceOlderThanCreationDate(creationDate, buildQueryOptions(searchOptions, getServiceAccessor().getSearchEntitiesDescriptor().getSearchMessageInstanceDescriptor())); } catch (SBonitaException e) { throw new ExecutionException(e); } } /** * Utility method to convert a SearchOptions into a QueryOptions */ private QueryOptions buildQueryOptions(SearchOptions searchOptions, SearchEntityDescriptor searchEntityDescriptor) { if (searchOptions != null) { final List filters = searchOptions.getFilters(); final List filterOptions = new ArrayList<>(filters.size()); for (final SearchFilter filter : filters) { final FilterOption option = searchEntityDescriptor.getEntityFilter(filter); if (option != null) {// in case of a unknown filter on state filterOptions.add(option); } } return new QueryOptions(searchOptions.getStartIndex(), searchOptions.getMaxResults(), Collections.emptyList(), filterOptions, null); } else { return QueryOptions.ALL_RESULTS; } } private void addMessageContent( final SThrowMessageEventTriggerDefinitionBuilder messageEventTriggerDefinitionBuilder, final ExpressionResolverService expressionResolverService, final Map messageContent) throws SBonitaException { for (final Entry entry : messageContent.entrySet()) { expressionResolverService.evaluate(ModelConvertor.constructSExpression(entry.getKey())); final SDataDefinitionBuilder dataDefinitionBuilder = BuilderFactory.get(SDataDefinitionBuilderFactory.class) .createNewInstance( entry.getKey().getContent(), entry.getValue().getReturnType()); dataDefinitionBuilder.setDefaultValue(ModelConvertor.constructSExpression(entry.getValue())); messageEventTriggerDefinitionBuilder.addData(dataDefinitionBuilder.done()); } } private void addMessageCorrelations( final SThrowMessageEventTriggerDefinitionBuilder messageEventTriggerDefinitionBuilder, final Map messageCorrelations) { for (final Entry entry : messageCorrelations.entrySet()) { messageEventTriggerDefinitionBuilder.addCorrelation(ModelConvertor.constructSExpression(entry.getKey()), ModelConvertor.constructSExpression(entry.getValue())); } } @Override public List getProcessResolutionProblems(final long processDefinitionId) throws ProcessDefinitionNotFoundException { return processDeploymentAPIDelegate.getProcessResolutionProblems(processDefinitionId); } @Override public List getProcessDeploymentInfos(final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion pagingCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = serviceAccessor .getSearchEntitiesDescriptor() .getSearchProcessDefinitionsDescriptor(); final GetProcessDefinitionDeployInfos transactionContentWithResult = new GetProcessDefinitionDeployInfos( processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, pagingCriterion); try { transactionContentWithResult.execute(); return transactionContentWithResult.getResult(); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public List getProcessDeploymentInfosWithActorOnlyForGroup(final long groupId, final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = serviceAccessor .getSearchEntitiesDescriptor() .getSearchProcessDefinitionsDescriptor(); final GetProcessDefinitionDeployInfosWithActorOnlyForGroup transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForGroup( processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion, groupId); try { transactionContentWithResult.execute(); return transactionContentWithResult.getResult(); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public List getProcessDeploymentInfosWithActorOnlyForGroups(final List groupIds, final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = serviceAccessor .getSearchEntitiesDescriptor() .getSearchProcessDefinitionsDescriptor(); final GetProcessDefinitionDeployInfosWithActorOnlyForGroups transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForGroups( processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion, groupIds); try { transactionContentWithResult.execute(); return transactionContentWithResult.getResult(); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public List getProcessDeploymentInfosWithActorOnlyForRole(final long roleId, final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = serviceAccessor .getSearchEntitiesDescriptor() .getSearchProcessDefinitionsDescriptor(); final GetProcessDefinitionDeployInfosWithActorOnlyForRole transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForRole( processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion, roleId); try { transactionContentWithResult.execute(); return transactionContentWithResult.getResult(); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public List getProcessDeploymentInfosWithActorOnlyForRoles(final List roleIds, final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = serviceAccessor .getSearchEntitiesDescriptor() .getSearchProcessDefinitionsDescriptor(); final GetProcessDefinitionDeployInfosWithActorOnlyForRoles transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForRoles( processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion, roleIds); try { transactionContentWithResult.execute(); return transactionContentWithResult.getResult(); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public List getProcessDeploymentInfosWithActorOnlyForUser(final long userId, final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = serviceAccessor .getSearchEntitiesDescriptor() .getSearchProcessDefinitionsDescriptor(); final GetProcessDefinitionDeployInfosWithActorOnlyForUser transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForUser( processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion, userId); try { transactionContentWithResult.execute(); return transactionContentWithResult.getResult(); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public List getProcessDeploymentInfosWithActorOnlyForUsers(final List userIds, final int startIndex, final int maxResults, final ProcessDeploymentInfoCriterion sortingCriterion) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchProcessDefinitionsDescriptor processDefinitionsDescriptor = serviceAccessor .getSearchEntitiesDescriptor() .getSearchProcessDefinitionsDescriptor(); final GetProcessDefinitionDeployInfosWithActorOnlyForUsers transactionContentWithResult = new GetProcessDefinitionDeployInfosWithActorOnlyForUsers( processDefinitionService, processDefinitionsDescriptor, startIndex, maxResults, sortingCriterion, userIds); try { transactionContentWithResult.execute(); return transactionContentWithResult.getResult(); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public SearchResult searchConnectorInstances(final SearchOptions searchOptions) throws RetrieveException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ConnectorInstanceService connectorInstanceService = serviceAccessor.getConnectorInstanceService(); if (searchOptions.getSorts().isEmpty()) { searchOptions.getSorts().add(new Sort(Order.ASC, ConnectorInstancesSearchDescriptor.EXECUTION_ORDER)); } try { return search(searchEntitiesDescriptor.getSearchConnectorInstanceDescriptor(), searchOptions, ModelConvertor::toConnectorInstances, connectorInstanceService::getNumberOfConnectorInstances, connectorInstanceService::searchConnectorInstances); } catch (SearchException e) { throw new RetrieveException(e); } } @Override public SearchResult searchArchivedConnectorInstances(final SearchOptions searchOptions) throws RetrieveException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ConnectorInstanceService connectorInstanceService = serviceAccessor.getConnectorInstanceService(); final ArchiveService archiveService = serviceAccessor.getArchiveService(); final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); final SearchArchivedConnectorInstance searchArchivedConnectorInstance = new SearchArchivedConnectorInstance( connectorInstanceService, searchEntitiesDescriptor.getSearchArchivedConnectorInstanceDescriptor(), searchOptions, persistenceService); try { searchArchivedConnectorInstance.execute(); } catch (final SBonitaException e) { throw new RetrieveException(e); } return searchArchivedConnectorInstance.getResult(); } @Override public List getHumanTaskInstances(final long rootProcessInstanceId, final String taskName, final int startIndex, final int maxResults) { try { final List humanTaskInstances = getHumanTaskInstances(rootProcessInstanceId, taskName, startIndex, maxResults, HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, Order.ASC); return Objects.requireNonNullElse(humanTaskInstances, Collections.emptyList()); } catch (final SearchException e) { throw new RetrieveException(e); } } @Override public HumanTaskInstance getLastStateHumanTaskInstance(final long rootProcessInstanceId, final String taskName) throws NotFoundException { try { final List humanTaskInstances = getHumanTaskInstances(rootProcessInstanceId, taskName, 0, 1, HumanTaskInstanceSearchDescriptor.REACHED_STATE_DATE, Order.DESC); if (humanTaskInstances == null || humanTaskInstances.isEmpty()) { throw new NotFoundException("Task '" + taskName + "' not found"); } return humanTaskInstances.get(0); } catch (final SearchException e) { throw new RetrieveException(e); } } private List getHumanTaskInstances(final long processInstanceId, final String taskName, final int startIndex, final int maxResults, final String field, final Order order) throws SearchException { final SearchOptionsBuilder builder = new SearchOptionsBuilder(startIndex, maxResults); builder.filter(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstanceId) .filter(HumanTaskInstanceSearchDescriptor.NAME, taskName); builder.sort(field, order); final SearchResult searchHumanTasks = searchHumanTaskInstances(builder.done()); return searchHumanTasks.getResult(); } @Override public SearchResult searchUsersWhoCanStartProcessDefinition(final long processDefinitionId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchUserDescriptor searchDescriptor = searchEntitiesDescriptor.getSearchUserDescriptor(); final SearchUsersWhoCanStartProcessDeploymentInfo transactionSearch = new SearchUsersWhoCanStartProcessDeploymentInfo( processDefinitionService, searchDescriptor, processDefinitionId, searchOptions); try { transactionSearch.execute(); } catch (final SBonitaException e) { throw new SearchException(e); } return transactionSearch.getResult(); } @Override public Map evaluateExpressionsAtProcessInstanciation(final long processInstanceId, final Map> expressions) throws ExpressionEvaluationException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); try { try { final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); // if it exists and is initializing or started final int stateId = processInstance.getStateId(); if (stateId == 0/* initializing */ || stateId == 1/* started */) { // the evaluation date is either now (initializing) or the start date if available final long evaluationDate = stateId == 0 ? System.currentTimeMillis() : processInstance.getStartDate(); return evaluateExpressionsInstanceLevelAndArchived(expressions, processInstanceId, CONTAINER_TYPE_PROCESS_INSTANCE, processInstance.getProcessDefinitionId(), evaluationDate); } } catch (final SProcessInstanceNotFoundException spinfe) { // get it in the archive } final ArchivedProcessInstance archiveProcessInstance = getStartedArchivedProcessInstance(processInstanceId); return evaluateExpressionsInstanceLevelAndArchived(expressions, processInstanceId, CONTAINER_TYPE_PROCESS_INSTANCE, archiveProcessInstance.getProcessDefinitionId(), archiveProcessInstance.getStartDate().getTime()); } catch (final SExpressionEvaluationException e) { throw new ExpressionEvaluationException(e, e.getExpressionName()); } catch (final SInvalidExpressionException e) { throw new ExpressionEvaluationException(e, e.getExpressionName()); } catch (final SBonitaException e) { throw new ExpressionEvaluationException(e, null); } } @Override public Map evaluateExpressionOnCompletedProcessInstance(final long processInstanceId, final Map> expressions) throws ExpressionEvaluationException { try { final ArchivedProcessInstance lastArchivedProcessInstance = getLastArchivedProcessInstance( processInstanceId); return evaluateExpressionsInstanceLevelAndArchived(expressions, processInstanceId, CONTAINER_TYPE_PROCESS_INSTANCE, lastArchivedProcessInstance.getProcessDefinitionId(), lastArchivedProcessInstance.getArchiveDate().getTime()); } catch (final SExpressionEvaluationException e) { throw new ExpressionEvaluationException(e, e.getExpressionName()); } catch (final SInvalidExpressionException e) { throw new ExpressionEvaluationException(e, e.getExpressionName()); } catch (final SBonitaException e) { throw new ExpressionEvaluationException(e, null); } } @Override public Map evaluateExpressionsOnProcessInstance(final long processInstanceId, final Map> expressions) throws ExpressionEvaluationException { try { final SProcessInstance sProcessInstance = getSProcessInstance(processInstanceId); return evaluateExpressionsInstanceLevel(expressions, processInstanceId, CONTAINER_TYPE_PROCESS_INSTANCE, sProcessInstance.getProcessDefinitionId()); } catch (final SExpressionEvaluationException e) { throw new ExpressionEvaluationException(e, e.getExpressionName()); } catch (final SInvalidExpressionException e) { throw new ExpressionEvaluationException(e, e.getExpressionName()); } catch (final SBonitaException e) { throw new ExpressionEvaluationException(e, null); } } @Override public Map evaluateExpressionsOnProcessDefinition(final long processDefinitionId, final Map> expressions) throws ExpressionEvaluationException { try { return evaluateExpressionsDefinitionLevel(expressions, processDefinitionId); } catch (final SExpressionEvaluationException e) { throw new ExpressionEvaluationException(e, e.getExpressionName()); } catch (final SInvalidExpressionException e) { throw new ExpressionEvaluationException(e, e.getExpressionName()); } catch (final SBonitaException e) { throw new ExpressionEvaluationException(e, null); } } @Override public Map evaluateExpressionsOnActivityInstance(final long activityInstanceId, final Map> expressions) throws ExpressionEvaluationException { try { final SActivityInstance sActivityInstance = getSActivityInstance(activityInstanceId); final SProcessInstance sProcessInstance = getSProcessInstance( sActivityInstance.getParentProcessInstanceId()); return evaluateExpressionsInstanceLevel(expressions, activityInstanceId, CONTAINER_TYPE_ACTIVITY_INSTANCE, sProcessInstance.getProcessDefinitionId()); } catch (final SExpressionEvaluationException e) { throw new ExpressionEvaluationException(e, e.getExpressionName()); } catch (final SInvalidExpressionException e) { throw new ExpressionEvaluationException(e, e.getExpressionName()); } catch (final SBonitaException e) { throw new ExpressionEvaluationException(e, null); } } @Override public Map evaluateExpressionsOnCompletedActivityInstance(final long activityInstanceId, final Map> expressions) throws ExpressionEvaluationException { try { final ArchivedActivityInstance activityInstance = getArchivedActivityInstance(activityInstanceId); // same archive time to process even if there're many activities in the process final ArchivedProcessInstance lastArchivedProcessInstance = getLastArchivedProcessInstance( activityInstance.getProcessInstanceId()); return evaluateExpressionsInstanceLevelAndArchived(expressions, activityInstanceId, CONTAINER_TYPE_ACTIVITY_INSTANCE, lastArchivedProcessInstance.getProcessDefinitionId(), activityInstance.getArchiveDate().getTime()); } catch (final SExpressionEvaluationException e) { throw new ExpressionEvaluationException(e, e.getExpressionName()); } catch (final SInvalidExpressionException e) { throw new ExpressionEvaluationException(e, e.getExpressionName()); } catch (final ActivityInstanceNotFoundException | SBonitaException e) { throw new ExpressionEvaluationException(e, null); } } private Map evaluateExpressionsDefinitionLevel( final Map> expressionsAndTheirPartialContext, final long processDefinitionId) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ExpressionResolverService expressionResolverService = serviceAccessor.getExpressionResolverService(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final EvaluateExpressionsDefinitionLevel evaluations = createDefinitionLevelExpressionEvaluator( expressionsAndTheirPartialContext, processDefinitionId, expressionResolverService, processDefinitionService); evaluations.execute(); return evaluations.getResult(); } protected EvaluateExpressionsDefinitionLevel createDefinitionLevelExpressionEvaluator( final Map> expressionsAndTheirPartialContext, final long processDefinitionId, final ExpressionResolverService expressionResolverService, final ProcessDefinitionService processDefinitionService) { return new EvaluateExpressionsDefinitionLevel(expressionsAndTheirPartialContext, processDefinitionId, expressionResolverService, processDefinitionService, getServiceAccessor().getBusinessDataRepository()); } private Map evaluateExpressionsInstanceLevel( final Map> expressions, final long containerId, final String containerType, final long processDefinitionId) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ExpressionResolverService expressionService = serviceAccessor.getExpressionResolverService(); final EvaluateExpressionsInstanceLevel evaluations = createInstanceLevelExpressionEvaluator(expressions, containerId, containerType, processDefinitionId, expressionService); evaluations.execute(); return evaluations.getResult(); } protected EvaluateExpressionsInstanceLevel createInstanceLevelExpressionEvaluator( final Map> expressions, final long containerId, final String containerType, final long processDefinitionId, final ExpressionResolverService expressionService) { return new EvaluateExpressionsInstanceLevel(expressions, containerId, containerType, processDefinitionId, expressionService, getServiceAccessor().getBusinessDataRepository()); } private Map evaluateExpressionsInstanceLevelAndArchived( final Map> expressions, final long containerId, final String containerType, final long processDefinitionId, final long time) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ExpressionResolverService expressionService = serviceAccessor.getExpressionResolverService(); final EvaluateExpressionsInstanceLevelAndArchived evaluations = createInstanceAndArchivedLevelExpressionEvaluator( expressions, containerId, containerType, processDefinitionId, time, expressionService); evaluations.execute(); return evaluations.getResult(); } protected EvaluateExpressionsInstanceLevelAndArchived createInstanceAndArchivedLevelExpressionEvaluator( final Map> expressions, final long containerId, final String containerType, final long processDefinitionId, final long time, final ExpressionResolverService expressionService) { return new EvaluateExpressionsInstanceLevelAndArchived(expressions, containerId, containerType, processDefinitionId, time, expressionService, getServiceAccessor().getBusinessDataRepository()); } private ArchivedProcessInstance getStartedArchivedProcessInstance(final long processInstanceId) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 2); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.ARCHIVE_DATE, Order.ASC); searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, processInstanceId); searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.STATE_ID, ProcessInstanceState.STARTED.getId()); final SearchArchivedProcessInstances searchArchivedProcessInstances = new SearchArchivedProcessInstances( processInstanceService, serviceAccessor.getProcessDefinitionService(), searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptionsBuilder.done()); searchArchivedProcessInstances.execute(); try { return searchArchivedProcessInstances.getResult().getResult().get(0); } catch (final IndexOutOfBoundsException e) { throw new SAProcessInstanceNotFoundException(processInstanceId, ProcessInstanceState.STARTED.name()); } } protected ArchivedProcessInstance getLastArchivedProcessInstance(final long processInstanceId) throws SBonitaException { return processInvolvementDelegate.getLastArchivedProcessInstance(processInstanceId); } @Override public List getFailedJobs(final int startIndex, final int maxResults) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final JobService jobService = serviceAccessor.getJobService(); try { final List failedJobs = jobService.getFailedJobs(startIndex, maxResults); return ModelConvertor.toFailedJobs(failedJobs); } catch (final SSchedulerException sse) { throw new RetrieveException(sse); } } @Override public void replayFailedJob(final long jobDescriptorId) throws ExecutionException { replayFailedJob(jobDescriptorId, null); } @Override public void replayFailedJob(final long jobDescriptorId, final Map parameters) throws ExecutionException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SchedulerService schedulerService = serviceAccessor.getSchedulerService(); try { if (parameters == null || parameters.isEmpty()) { schedulerService.retryJobThatFailed(jobDescriptorId); } else { final List jobParameters = buildJobParametersFromMap(parameters); schedulerService.retryJobThatFailed(jobDescriptorId, jobParameters); } } catch (final SSchedulerException sse) { throw new ExecutionException(sse); } } protected List buildJobParametersFromMap(final Map parameters) { final List jobParameters = new ArrayList<>(); for (final Entry parameter : parameters.entrySet()) { jobParameters.add(buildSJobParameter(parameter.getKey(), parameter.getValue())); } return jobParameters; } protected SJobParameter buildSJobParameter(final String parameterKey, final Serializable parameterValue) { return SJobParameter.builder() .key(parameterKey) .value(parameterValue).build(); } @Override public ArchivedDataInstance getArchivedProcessDataInstance(final String dataName, final long sourceProcessInstanceId) throws ArchivedDataNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver(); final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final SAProcessInstance lastArchivedProcessInstance = processInstanceService .getLastArchivedProcessInstance(sourceProcessInstanceId); if (lastArchivedProcessInstance == null) { throw new ArchivedDataNotFoundException( "Archived process instance not found: " + sourceProcessInstanceId); } final long processDefinitionId = lastArchivedProcessInstance.getProcessDefinitionId(); final ClassLoader processClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, processDefinitionId)); Thread.currentThread().setContextClassLoader(processClassLoader); final SADataInstance dataInstance = dataInstanceService.getLastSADataInstance(dataName, sourceProcessInstanceId, DataInstanceContainer.PROCESS_INSTANCE.toString(), parentContainerResolver); return ModelConvertor.toArchivedDataInstance(dataInstance); } catch (final SDataInstanceException e) { throw new ArchivedDataNotFoundException(e); } catch (final SBonitaException e) { throw new RetrieveException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } @Override public ArchivedDataInstance getArchivedActivityDataInstance(final String dataName, final long sourceActivityInstanceId) throws ArchivedDataNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final ParentContainerResolver parentContainerResolver = serviceAccessor.getParentContainerResolver(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class) .getProcessDefinitionIndex(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final SAFlowNodeInstance lastArchivedFlowNodeInstance = activityInstanceService .getLastArchivedFlowNodeInstance(SAFlowNodeInstance.class, sourceActivityInstanceId); if (lastArchivedFlowNodeInstance == null) { throw new ArchivedDataNotFoundException( new ArchivedActivityInstanceNotFoundException(sourceActivityInstanceId)); } final long parentProcessInstanceId = lastArchivedFlowNodeInstance .getLogicalGroup(processDefinitionIndex); final ClassLoader processClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, parentProcessInstanceId)); Thread.currentThread().setContextClassLoader(processClassLoader); final SADataInstance dataInstance = dataInstanceService.getLastSADataInstance(dataName, sourceActivityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.toString(), parentContainerResolver); return ModelConvertor.toArchivedDataInstance(dataInstance); } catch (final SDataInstanceException e) { throw new ArchivedDataNotFoundException(e); } catch (final SBonitaException e) { throw new RetrieveException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } @Override public List getArchivedProcessDataInstances(final long sourceProcessInstanceId, final int startIndex, final int maxResults) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final SAProcessInstance lastArchivedProcessInstance = processInstanceService .getLastArchivedProcessInstance(sourceProcessInstanceId); if (lastArchivedProcessInstance == null) { throw new RetrieveException("Archived process instance not found: " + sourceProcessInstanceId); } final long processDefinitionId = lastArchivedProcessInstance.getProcessDefinitionId(); final ClassLoader processClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, processDefinitionId)); Thread.currentThread().setContextClassLoader(processClassLoader); final List dataInstances = dataInstanceService.getLastLocalSADataInstances( sourceProcessInstanceId, DataInstanceContainer.PROCESS_INSTANCE.toString(), startIndex, maxResults); return ModelConvertor.toArchivedDataInstances(dataInstances); } catch (final SBonitaException e) { throw new RetrieveException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } @Override public List getArchivedActivityDataInstances(final long sourceActivityInstanceId, final int startIndex, final int maxResults) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService(); final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final int processDefinitionIndex = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class) .getProcessDefinitionIndex(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final long parentProcessInstanceId = activityInstanceService .getLastArchivedFlowNodeInstance(SAFlowNodeInstance.class, sourceActivityInstanceId) .getLogicalGroup(processDefinitionIndex); final ClassLoader processClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, parentProcessInstanceId)); Thread.currentThread().setContextClassLoader(processClassLoader); final List dataInstances = dataInstanceService.getLastLocalSADataInstances( sourceActivityInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.toString(), startIndex, maxResults); return ModelConvertor.toArchivedDataInstances(dataInstances); } catch (final SBonitaException e) { throw new RetrieveException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } @Override public List getPossibleUsersOfPendingHumanTask(final long humanTaskInstanceId, final int startIndex, final int maxResults) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); try { // pagination of this method is based on order by username: final List userIds = activityInstanceService.getPossibleUserIdsOfPendingTasks(humanTaskInstanceId, startIndex, maxResults); final IdentityService identityService = getServiceAccessor().getIdentityService(); // This method below is also ordered by username, so the order is preserved: final List sUsers = identityService.getUsers(userIds); return ModelConvertor.toUsers(sUsers); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } @Override public List getPossibleUsersOfHumanTask(final long processDefinitionId, final String humanTaskName, final int startIndex, final int maxResults) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final SProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); final SFlowNodeDefinition flowNode = processDefinition.getProcessContainer().getFlowNode(humanTaskName); if (!(flowNode instanceof SHumanTaskDefinition humanTask)) { return Collections.emptyList(); } final String actorName = humanTask.getActorName(); final List userIds = getUserIdsForActor(serviceAccessor, processDefinitionId, actorName, startIndex, maxResults); final List users = serviceAccessor.getIdentityService().getUsers(userIds); return ModelConvertor.toUsers(users); } catch (final SProcessDefinitionNotFoundException spdnfe) { return Collections.emptyList(); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } private List getUserIdsForActor(final ServiceAccessor serviceAccessor, final long processDefinitionId, final String actorName, final int startIndex, final int maxResults) throws SActorNotFoundException, SBonitaReadException { final ActorMappingService actorMappingService = serviceAccessor.getActorMappingService(); final SActor actor = actorMappingService.getActor(actorName, processDefinitionId); return actorMappingService.getPossibleUserIdsOfActorId(actor.getId(), startIndex, maxResults); } @Override public List getUserIdsForActor(final long processDefinitionId, final String actorName, final int startIndex, final int maxResults) { try { return getUserIdsForActor(getServiceAccessor(), processDefinitionId, actorName, startIndex, maxResults); } catch (final SBonitaException e) { throw new RetrieveException(e); } } @Override public SearchResult searchUsersWhoCanExecutePendingHumanTask(final long humanTaskInstanceId, final SearchOptions searchOptions) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchUserDescriptor searchDescriptor = searchEntitiesDescriptor.getSearchUserDescriptor(); final SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo searcher = new SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo( humanTaskInstanceId, activityInstanceService, searchDescriptor, searchOptions); try { searcher.execute(); } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } return searcher.getResult(); } @Override public SearchResult searchAssignedAndPendingHumanTasksFor(final long rootProcessDefinitionId, final long userId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final SearchHumanTaskInstanceDescriptor searchDescriptor = searchEntitiesDescriptor .getSearchHumanTaskInstanceDescriptor(); return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance(searchDescriptor, searchOptions, flowNodeStateManager, (queryOptions) -> activityInstanceService .getNumberOfAssignedAndPendingHumanTasksFor(rootProcessDefinitionId, userId, queryOptions), (queryOptions) -> activityInstanceService.searchAssignedAndPendingHumanTasksFor(rootProcessDefinitionId, userId, queryOptions)) .search(); } @Override public SearchResult searchAssignedAndPendingHumanTasks(final long rootProcessDefinitionId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final SearchHumanTaskInstanceDescriptor searchDescriptor = searchEntitiesDescriptor .getSearchHumanTaskInstanceDescriptor(); return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance(searchDescriptor, searchOptions, flowNodeStateManager, (queryOptions) -> activityInstanceService .getNumberOfAssignedAndPendingHumanTasks(rootProcessDefinitionId, queryOptions), (queryOptions) -> activityInstanceService.searchAssignedAndPendingHumanTasks(rootProcessDefinitionId, queryOptions)) .search(); } @Override public SearchResult searchAssignedAndPendingHumanTasks(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); final SearchHumanTaskInstanceDescriptor searchDescriptor = searchEntitiesDescriptor .getSearchHumanTaskInstanceDescriptor(); return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance(searchDescriptor, searchOptions, flowNodeStateManager, activityInstanceService::getNumberOfAssignedAndPendingHumanTasks, activityInstanceService::searchAssignedAndPendingHumanTasks).search(); } @Override public ContractDefinition getUserTaskContract(final long userTaskId) throws UserTaskNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); try { final SHumanTaskInstance taskInstance = activityInstanceService.getHumanTaskInstance(userTaskId); if (!(taskInstance instanceof SUserTaskInstance)) { throw new UserTaskNotFoundException("Impossible to find a user task with id: " + userTaskId); } final SProcessDefinition processDefinition = getServiceAccessor().getProcessDefinitionService() .getProcessDefinition( taskInstance.getProcessDefinitionId()); final SUserTaskDefinition userTask = (SUserTaskDefinition) processDefinition.getProcessContainer() .getFlowNode( taskInstance.getFlowNodeDefinitionId()); return ModelConvertor.toContract(userTask.getContract()); } catch (final SActivityInstanceNotFoundException | SProcessDefinitionNotFoundException | SActivityReadException | SBonitaReadException e) { throw new UserTaskNotFoundException(e.getMessage()); } } @Override public ContractDefinition getProcessContract(final long processDefinitionId) throws ProcessDefinitionNotFoundException { try { final SProcessDefinition processDefinition = getServiceAccessor().getProcessDefinitionService() .getProcessDefinition(processDefinitionId); return ModelConvertor.toContract(processDefinition.getContract()); } catch (final SProcessDefinitionNotFoundException | SBonitaReadException e) { throw new ProcessDefinitionNotFoundException(e.getMessage()); } } @Override @CustomTransactions public void executeUserTask(final long flownodeInstanceId, final Map inputs) throws FlowNodeExecutionException, ContractViolationException, UserTaskNotFoundException { executeUserTask(0, flownodeInstanceId, inputs); } @Override @CustomTransactions public void executeUserTask(final long userId, final long flownodeInstanceId, final Map inputs) throws FlowNodeExecutionException, ContractViolationException, UserTaskNotFoundException { try { inTx(() -> { executeFlowNode(userId, flownodeInstanceId, inputs, true); return null; }); } catch (final ContractViolationException e) { throw e; } catch (final SFlowNodeNotFoundException e) { throw new UserTaskNotFoundException( String.format("User task %s is not found, it might already be executed", flownodeInstanceId)); } catch (final Exception e) { verifyIfTheActivityWasInTheCorrectStateAndThrowException(flownodeInstanceId, e); } } private void verifyIfTheActivityWasInTheCorrectStateAndThrowException(long flownodeInstanceId, Exception e) throws UserTaskNotFoundException, FlowNodeExecutionException { SFlowNodeInstance flowNodeInstance; try { flowNodeInstance = inTx( () -> getServiceAccessor().getActivityInstanceService().getFlowNodeInstance(flownodeInstanceId)); } catch (SActivityInstanceNotFoundException e1) { throw new UserTaskNotFoundException( String.format("User task %s is not found, it might already be executed", flownodeInstanceId)); } catch (Exception e1) { throw new FlowNodeExecutionException(e); } if (flowNodeInstance.getStateId() != FlowNodeState.ID_ACTIVITY_READY || flowNodeInstance.isStateExecuting()) { //this in a not found because that task was not visible anymore throw new UserTaskNotFoundException( String.format( "User task is not executable (currently in state '%s'), this might be because someone else already executed it.", (flowNodeInstance.isStateExecuting() ? "executing " : "") + flowNodeInstance.getStateName())); } throw new FlowNodeExecutionException(e); } private T inTx(Callable booleanCallable) throws Exception { return getServiceAccessor().getUserTransactionService().executeInTransaction(booleanCallable); } private void checkIsHumanTaskInReadyState(SFlowNodeInstance flowNodeInstance) throws SFlowNodeExecutionException { if (!(flowNodeInstance instanceof SHumanTaskInstance)) { throw new SFlowNodeExecutionException( "Unable to execute flownode " + flowNodeInstance.getId() + " because is not a user task"); } if (flowNodeInstance.getStateId() != FlowNodeState.ID_ACTIVITY_READY || flowNodeInstance.isStateExecuting()) { throw new SFlowNodeExecutionException( "Unable to execute flow node " + flowNodeInstance.getId() + " because it is in an incompatible state (" + (flowNodeInstance.isStateExecuting() ? "transitioning from state " : "on state ") + flowNodeInstance.getStateName() + "). Someone probably already called execute on it."); } } /** * Execute a flow node. All methods that executes flow nodes and human tasks uses this one. * * @param userId the id of the user executing the task * @param shouldBeReadyTask if true the method will only accept to execute human task in ready state */ protected void executeFlowNode(final long userId, final long flowNodeInstanceId, final Map inputs, boolean shouldBeReadyTask) throws ContractViolationException, SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(); ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); ContractDataService contractDataService = serviceAccessor.getContractDataService(); IdentityService identityService = serviceAccessor.getIdentityService(); SCommentService commentService = serviceAccessor.getCommentService(); WorkService workService = serviceAccessor.getWorkService(); BPMWorkFactory workFactory = serviceAccessor.getBPMWorkFactory(); SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId); final SSession session = getSession(); final Optional executerSubstituteUserId = Optional.ofNullable(session).map(SSession::getUserId); final Optional executerUserId = session == null ? Optional.empty() : userId == 0L ? executerSubstituteUserId : Optional.of(userId); try (var flowNodeInstanceMDC = new FlowNodeInstanceMDC(flowNodeInstanceId, executerUserId, executerSubstituteUserId, flowNodeInstance.getProcessDefinitionId(), flowNodeInstance.getParentProcessInstanceId(), flowNodeInstance.getRootProcessInstanceId())) { if (shouldBeReadyTask) { /* * this is to protect from concurrent execution of the task when 2 users call execute user task at the * same * time * it still might have concurrency issue but: * - if the second client call execute with contract inputs, on commit there will be a constraint * violation * + rollback * - if there is no contract input, the work will check that the activity is in ready state before * calling * execute. * The only left issue is that on this last case the executor will change to the last one. */ checkIsHumanTaskInReadyState(flowNodeInstance); } if (flowNodeInstance instanceof SUserTaskInstance) { try { throwContractViolationExceptionIfContractIsInvalid(inputs, serviceAccessor, flowNodeInstance); } catch (SContractViolationException e) { throw new ContractViolationException(e.getSimpleMessage(), e.getMessage(), e.getExplanations(), e.getCause()); } } if (flowNodeInstance instanceof SHumanTaskInstance && ((SHumanTaskInstance) flowNodeInstance).getAssigneeId() <= 0) { throw new SFlowNodeExecutionException("The user task " + flowNodeInstanceId + " is not assigned"); } if (session != null) { final boolean isFirstState = flowNodeInstance.getStateId() == 0; if (flowNodeInstance instanceof SUserTaskInstance) { contractDataService.addUserTaskData(flowNodeInstance.getId(), inputs); } // TODO: the following 4 instructions seem to be redundant with stepForward: // Cannot we factorize this? serviceAccessor.getBPMArchiverService().archiveFlowNodeInstance(flowNodeInstance); // flag as executing activityInstanceService.setExecuting(flowNodeInstance); activityInstanceService.setExecutedBy(flowNodeInstance, executerUserId.orElse(0L)); activityInstanceService.setExecutedBySubstitute(flowNodeInstance, executerSubstituteUserId.orElse(0L)); WorkDescriptor work = workFactory.createExecuteFlowNodeWorkDescriptor(flowNodeInstance); workService.registerWork(work); if (log.isInfoEnabled() && !isFirstState /* * don't log when create * subtask */) { final String message = LogMessageBuilder.buildExecuteTaskContextMessage(flowNodeInstance, session.getUserName(), executerUserId.orElse(0L), executerSubstituteUserId.orElse(0L), inputs); log.info(message); } else if (log.isDebugEnabled()) { log.debug("Executing state " + flowNodeInstance.getStateName() + " (" + flowNodeInstance.getStateId() + ") for flownode " + LogMessageBuilder.buildFlowNodeContextMessage(flowNodeInstance)); } if (!executerUserId.equals(executerSubstituteUserId)) { try { final SUser executorUser = identityService.getUser(executerUserId.orElse(0L)); String stb = "The user " + session.getUserName() + " " + "acting as delegate of the user " + executorUser.getUserName() + " " + "has done the task \"" + flowNodeInstance.getDisplayName() + "\"."; commentService.addSystemComment(flowNodeInstance.getParentProcessInstanceId(), stb); } catch (final SBonitaException e) { log.error( "Error when adding a comment on the process instance.", e); } } } } } private void throwContractViolationExceptionIfContractIsInvalid(final Map inputs, final ServiceAccessor serviceAccessor, final SFlowNodeInstance flowNodeInstance) throws SBonitaReadException, SProcessDefinitionNotFoundException, SContractViolationException { final SProcessDefinition processDefinition = serviceAccessor.getProcessDefinitionService() .getProcessDefinition(flowNodeInstance.getProcessDefinitionId()); final SUserTaskDefinition userTaskDefinition = (SUserTaskDefinition) processDefinition.getProcessContainer() .getFlowNode( flowNodeInstance.getFlowNodeDefinitionId()); final SContractDefinition contractDefinition = userTaskDefinition.getContract(); final ContractValidator validator = new ContractValidatorFactory() .createContractValidator(serviceAccessor.getExpressionService()); validator.validate(flowNodeInstance.getProcessDefinitionId(), contractDefinition, inputs); } @Override public Document removeDocument(final long documentId) throws DocumentNotFoundException, DeletionException { return documentAPI.removeDocument(documentId); } @Override public List getDocumentList(final long processInstanceId, final String name, final int from, final int numberOfResult) throws DocumentNotFoundException { return documentAPI.getDocumentList(processInstanceId, name, from, numberOfResult); } @Override public void setDocumentList(final long processInstanceId, final String name, final List documentsValues) throws DocumentException, DocumentNotFoundException { documentAPI.setDocumentList(processInstanceId, name, documentsValues); } @Override public void deleteContentOfArchivedDocument(final long archivedDocumentId) throws DocumentException, DocumentNotFoundException { documentAPI.deleteContentOfArchivedDocument(archivedDocumentId); } protected ServiceAccessor getServiceAccessor() { return ServiceAccessorSingleton.getInstance(); } long getUserId() { return APIUtils.getUserId(); } @Override public Document addDocument(final long processInstanceId, final String documentName, final String description, final DocumentValue documentValue) throws ProcessInstanceNotFoundException, DocumentAttachmentException, AlreadyExistsException { return documentAPI.addDocument(processInstanceId, documentName, description, documentValue); } @Override public Document updateDocument(final long documentId, final DocumentValue documentValue) throws ProcessInstanceNotFoundException, DocumentAttachmentException, AlreadyExistsException { return documentAPI.updateDocument(documentId, documentValue); } @Override public void purgeClassLoader(final long processDefinitionId) throws ProcessDefinitionNotFoundException, UpdateException { processManagementAPIImplDelegate.purgeClassLoader(processDefinitionId); } @Override public Serializable getUserTaskContractVariableValue(final long userTaskInstanceId, final String name) throws ContractDataNotFoundException { final ContractDataService contractDataService = getServiceAccessor().getContractDataService(); try { return contractDataService.getArchivedUserTaskDataValue(userTaskInstanceId, name); } catch (final SContractDataNotFoundException scdnfe) { throw new ContractDataNotFoundException(scdnfe); } catch (final SBonitaReadException sbe) { throw new RetrieveException(sbe); } } @Override public Serializable getProcessInputValueDuringInitialization(long processInstanceId, String name) throws ContractDataNotFoundException { try { return getServiceAccessor().getContractDataService().getProcessDataValue(processInstanceId, name); } catch (SContractDataNotFoundException | SBonitaReadException e) { throw new ContractDataNotFoundException(e); } } @Override public Serializable getProcessInputValueAfterInitialization(long processInstanceId, String name) throws ContractDataNotFoundException { try { return getServiceAccessor().getContractDataService().getArchivedProcessDataValue(processInstanceId, name); } catch (SContractDataNotFoundException | SBonitaReadException e) { throw new ContractDataNotFoundException(e); } } @Override public int getNumberOfParameterInstances(final long processDefinitionId) { return processManagementAPIImplDelegate.getNumberOfParameterInstances(processDefinitionId); } @Override public ParameterInstance getParameterInstance(final long processDefinitionId, final String parameterName) throws NotFoundException { return processManagementAPIImplDelegate.getParameterInstance(processDefinitionId, parameterName); } @Override public List getParameterInstances(final long processDefinitionId, final int startIndex, final int maxResults, final ParameterCriterion sort) { return processManagementAPIImplDelegate.getParameterInstances(processDefinitionId, startIndex, maxResults, sort); } @Override public Map getUserTaskExecutionContext(long userTaskInstanceId) throws UserTaskNotFoundException, ExpressionEvaluationException { ServiceAccessor serviceAccessor = getServiceAccessor(); try { SFlowNodeInstance activityInstance = serviceAccessor.getActivityInstanceService() .getFlowNodeInstance(userTaskInstanceId); SProcessDefinition processDefinition = serviceAccessor.getProcessDefinitionService() .getProcessDefinition(activityInstance.getProcessDefinitionId()); final SExpressionContext expressionContext = createExpressionContext(userTaskInstanceId, processDefinition, CONTAINER_TYPE_ACTIVITY_INSTANCE, null); SFlowNodeDefinition flowNode = processDefinition.getProcessContainer() .getFlowNode(activityInstance.getFlowNodeDefinitionId()); return evaluateContext(serviceAccessor.getExpressionResolverService(), expressionContext, ((SUserTaskDefinition) flowNode).getContext()); } catch (SFlowNodeNotFoundException | SBonitaReadException | SFlowNodeReadException | SProcessDefinitionNotFoundException e) { throw new UserTaskNotFoundException(e); } catch (SInvalidExpressionException | SExpressionEvaluationException | SExpressionDependencyMissingException | SExpressionTypeUnknownException e) { throw new ExpressionEvaluationException(e); } } @Override public Map getArchivedUserTaskExecutionContext(long archivedUserTaskInstanceId) throws UserTaskNotFoundException, ExpressionEvaluationException { ServiceAccessor serviceAccessor = getServiceAccessor(); try { SAFlowNodeInstance archivedActivityInstance = serviceAccessor.getActivityInstanceService() .getArchivedFlowNodeInstance(archivedUserTaskInstanceId); SProcessDefinition processDefinition = serviceAccessor.getProcessDefinitionService() .getProcessDefinition(archivedActivityInstance.getProcessDefinitionId()); final SExpressionContext expressionContext = createExpressionContext( archivedActivityInstance.getSourceObjectId(), processDefinition, CONTAINER_TYPE_ACTIVITY_INSTANCE, archivedActivityInstance.getArchiveDate()); SFlowNodeDefinition flowNode = processDefinition.getProcessContainer() .getFlowNode(archivedActivityInstance.getFlowNodeDefinitionId()); return evaluateContext(serviceAccessor.getExpressionResolverService(), expressionContext, ((SUserTaskDefinition) flowNode).getContext()); } catch (SFlowNodeNotFoundException | SBonitaReadException | SFlowNodeReadException | SProcessDefinitionNotFoundException e) { throw new UserTaskNotFoundException(e); } catch (SInvalidExpressionException | SExpressionEvaluationException | SExpressionDependencyMissingException | SExpressionTypeUnknownException e) { throw new ExpressionEvaluationException(e); } } @Override public Map getProcessInstanceExecutionContext(long processInstanceId) throws ProcessInstanceNotFoundException, ExpressionEvaluationException { ServiceAccessor serviceAccessor = getServiceAccessor(); try { SProcessInstance processInstance = getProcessInstanceService(serviceAccessor) .getProcessInstance(processInstanceId); if (processInstance == null) { throw new ProcessInstanceNotFoundException("Process Instance not found " + processInstanceId); } SProcessDefinition processDefinition = serviceAccessor.getProcessDefinitionService() .getProcessDefinition(processInstance.getProcessDefinitionId()); final SExpressionContext expressionContext = createExpressionContext(processInstanceId, processDefinition, CONTAINER_TYPE_PROCESS_INSTANCE, null); return evaluateContext(serviceAccessor.getExpressionResolverService(), expressionContext, processDefinition.getContext()); } catch (SProcessInstanceNotFoundException | SBonitaReadException | SProcessInstanceReadException | SProcessDefinitionNotFoundException e) { throw new ProcessInstanceNotFoundException(e); } catch (SInvalidExpressionException | SExpressionEvaluationException | SExpressionDependencyMissingException | SExpressionTypeUnknownException e) { throw new ExpressionEvaluationException(e); } } @Override public Map getArchivedProcessInstanceExecutionContext(long archivedProcessInstanceId) throws ProcessInstanceNotFoundException, ExpressionEvaluationException { ServiceAccessor serviceAccessor = getServiceAccessor(); try { SAProcessInstance processInstance = getProcessInstanceService(serviceAccessor) .getArchivedProcessInstance(archivedProcessInstanceId); if (processInstance == null) { throw new ProcessInstanceNotFoundException( "Archived Process Instance not found " + archivedProcessInstanceId); } SProcessDefinition processDefinition = serviceAccessor.getProcessDefinitionService() .getProcessDefinition(processInstance.getProcessDefinitionId()); final SExpressionContext expressionContext = createExpressionContext(processInstance.getSourceObjectId(), processDefinition, CONTAINER_TYPE_PROCESS_INSTANCE, processInstance.getArchiveDate()); return evaluateContext(serviceAccessor.getExpressionResolverService(), expressionContext, processDefinition.getContext()); } catch (SBonitaReadException | SProcessInstanceReadException | SProcessDefinitionNotFoundException e) { throw new ProcessInstanceNotFoundException(e); } catch (SInvalidExpressionException | SExpressionEvaluationException | SExpressionDependencyMissingException | SExpressionTypeUnknownException e) { throw new ExpressionEvaluationException(e); } } Map evaluateContext(ExpressionResolverService expressionResolverService, SExpressionContext expressionContext, List context) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { if (context.isEmpty()) { return Collections.emptyMap(); } List expressions = toExpressionList(context); List evaluate = expressionResolverService.evaluate(expressions, expressionContext); return toResultMap(context, evaluate); } List toExpressionList(List context) { List expressions = new ArrayList<>(); for (SContextEntry sContextEntry : context) { expressions.add(sContextEntry.getExpression()); } return expressions; } HashMap toResultMap(List context, List evaluate) { HashMap result = new HashMap<>(evaluate.size()); for (int i = 0; i < evaluate.size(); i++) { result.put(context.get(i).getKey(), (Serializable) evaluate.get(i)); } return result; } ProcessInstanceService getProcessInstanceService(ServiceAccessor serviceAccessor) { return serviceAccessor.getProcessInstanceService(); } SExpressionContext createExpressionContext(long processInstanceId, SProcessDefinition processDefinition, String type, Long time) { final SExpressionContext expressionContext = new SExpressionContext(); expressionContext.setContainerId(processInstanceId); expressionContext.setContainerType(type); expressionContext.setProcessDefinitionId(processDefinition.getId()); if (time != null) { expressionContext.setTime(time); } return expressionContext; } @Override public SearchResult searchFormMappings(SearchOptions searchOptions) throws SearchException { return processConfigurationAPI.searchFormMappings(searchOptions); } @Override public FormMapping getFormMapping(long formMappingId) throws FormMappingNotFoundException { return processConfigurationAPI.getFormMapping(formMappingId); } @Override public ProcessInstance updateProcessInstance(final long processInstanceId, final ProcessInstanceUpdater updater) throws ProcessInstanceNotFoundException, UpdateException { if (updater == null || updater.getFields().isEmpty()) { throw new UpdateException("The update descriptor does not contain field updates"); } final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); try { final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor(); final Map fields = updater .getFields(); for (final Entry field : fields .entrySet()) { entityUpdateDescriptor.addField(SProcessInstance.STRING_INDEX_KEY + (field.getKey().ordinal() + 1), field.getValue()); } processInstanceService.updateProcess(processInstance, entityUpdateDescriptor); return getProcessInstance(processInstanceId); } catch (final SProcessInstanceNotFoundException spinfe) { throw new ProcessInstanceNotFoundException(spinfe); } catch (final SBonitaException | RetrieveException sbe) { throw new UpdateException(sbe); } } @Override public ProcessInstance updateProcessInstanceIndex(final long processInstanceId, final Index index, final String value) throws ProcessInstanceNotFoundException, UpdateException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); try { final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor(); entityUpdateDescriptor.addField(SProcessInstance.STRING_INDEX_KEY + (index.ordinal() + 1), value); processInstanceService.updateProcess(processInstance, entityUpdateDescriptor); return getProcessInstance(processInstanceId); } catch (final SProcessInstanceNotFoundException notFound) { throw new ProcessInstanceNotFoundException(notFound); } catch (final SBonitaException | RetrieveException sbe) { throw new UpdateException(sbe); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ProcessConfigurationAPIImpl.java ================================================ /** * 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.api.impl; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.core.form.FormMappingService; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.exception.FormMappingNotFoundException; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.form.FormMapping; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; import org.bonitasoft.engine.search.form.SearchFormMappings; import org.bonitasoft.engine.service.*; /** * @author Baptiste Mesta */ public class ProcessConfigurationAPIImpl { protected ServiceAccessor getServiceAccessor() { try { return ServiceAccessorSingleton.getInstance(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } public SearchResult searchFormMappings(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); FormMappingService formMappingService = serviceAccessor.getFormMappingService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchFormMappings searchFormMappings = new SearchFormMappings(formMappingService, getServiceAccessor().getProcessDefinitionService(), searchEntitiesDescriptor.getSearchFormMappingDescriptor(), searchOptions); try { searchFormMappings.execute(); return searchFormMappings.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } public FormMapping getFormMapping(long formMappingId) throws FormMappingNotFoundException { final FormMappingService formMappingService = getServiceAccessor().getFormMappingService(); try { return ModelConvertor.toFormMapping(formMappingService.get(formMappingId), new FormRequiredAnalyzer(getServiceAccessor() .getProcessDefinitionService())); } catch (SBonitaReadException e) { throw new RetrieveException(e); } catch (SObjectNotFoundException e) { throw new FormMappingNotFoundException("no form mapping found with id" + formMappingId); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ProcessDeploymentAPIDelegate.java ================================================ /** * Copyright (C) 2023 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.api.impl; import java.util.HashMap; import java.util.List; import java.util.Map; import lombok.Getter; import org.bonitasoft.engine.api.impl.transaction.process.EnableProcess; import org.bonitasoft.engine.bar.BusinessArchiveService; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.process.*; import org.bonitasoft.engine.commons.exceptions.SAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SV6FormsDeployException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.execution.event.EventsHandler; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; import org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor; import org.bonitasoft.engine.search.process.SearchProcessDeploymentInfos; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; /** * @author Emmanuel Duchastenier */ public class ProcessDeploymentAPIDelegate { @Getter private static final ProcessDeploymentAPIDelegate instance = new ProcessDeploymentAPIDelegate(); private ProcessDeploymentAPIDelegate() { } protected ServiceAccessor getServiceAccessor() { return ServiceAccessorSingleton.getInstance(); } public ProcessDefinition deploy(final BusinessArchive businessArchive) throws ProcessDeployException, AlreadyExistsException { validateBusinessArchive(businessArchive); final BusinessArchiveService businessArchiveService = getServiceAccessor().getBusinessArchiveService(); try { return ModelConvertor.toProcessDefinition(businessArchiveService.deploy(businessArchive)); } catch (SV6FormsDeployException e) { throw new V6FormDeployException(e); } catch (SObjectCreationException e) { throw new ProcessDeployException(e); } catch (SAlreadyExistsException e) { throw new AlreadyExistsException(e.getMessage()); } } public ProcessDefinition deployAndEnableProcess(final BusinessArchive businessArchive) throws ProcessDeployException, ProcessEnablementException, AlreadyExistsException { final ProcessDefinition processDefinition = deploy(businessArchive); try { enableProcess(processDefinition.getId()); } catch (final ProcessDefinitionNotFoundException e) { throw new ProcessEnablementException(e.getMessage()); } return processDefinition; } void validateBusinessArchive(BusinessArchive businessArchive) throws ProcessDeployException { for (Map.Entry resource : businessArchive.getResources().entrySet()) { final byte[] resourceContent = resource.getValue(); if (resourceContent == null || resourceContent.length == 0) { throw new ProcessDeployException( "The BAR file you are trying to deploy contains an empty file: " + resource.getKey() + ". The process cannot be deployed. Fix it or remove it from the BAR."); } } } public void enableProcess(final long processDefinitionId) throws ProcessDefinitionNotFoundException, ProcessEnablementException { final ProcessDefinitionService processDefinitionService = getServiceAccessor() .getProcessDefinitionService(); final EventsHandler eventsHandler = getServiceAccessor().getEventsHandler(); try { new EnableProcess(processDefinitionService, processDefinitionId, eventsHandler, SessionInfos.getUserNameFromSession()).execute(); } catch (final SProcessDefinitionNotFoundException e) { throw new ProcessDefinitionNotFoundException(e); } catch (final Exception e) { throw new ProcessEnablementException(e); } } public long getProcessDefinitionId(final String name, final String version) throws ProcessDefinitionNotFoundException { try { return getServiceAccessor().getProcessDefinitionService().getProcessDefinitionId(name, version); } catch (final SProcessDefinitionNotFoundException e) { throw new ProcessDefinitionNotFoundException(e); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } } public List getProcessResolutionProblems(final long processDefinitionId) throws ProcessDefinitionNotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId); return serviceAccessor.getBusinessArchiveArtifactsManager().getProcessResolutionProblems(processDefinition); } catch (final SProcessDefinitionNotFoundException | SBonitaReadException e) { throw new ProcessDefinitionNotFoundException(e); } } public Map getProcessDeploymentInfosFromIds(final List processDefinitionIds) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final List processDefinitionDeployInfos = processDefinitionService .getProcessDeploymentInfos(processDefinitionIds); final List processDeploymentInfos = ModelConvertor .toProcessDeploymentInfo(processDefinitionDeployInfos); final Map mProcessDefinitions = new HashMap<>(); for (final ProcessDeploymentInfo p : processDeploymentInfos) { mProcessDefinitions.put(p.getProcessId(), p); } return mProcessDefinitions; } catch (final SBonitaException e) { throw new RetrieveException(e); } } public ProcessDeploymentInfo getProcessDeploymentInfo(final long processDefinitionId) throws ProcessDefinitionNotFoundException { final ProcessDefinitionService processDefinitionService = getServiceAccessor() .getProcessDefinitionService(); try { return ModelConvertor .toProcessDeploymentInfo(processDefinitionService.getProcessDeploymentInfo(processDefinitionId)); } catch (final SProcessDefinitionNotFoundException e) { throw new ProcessDefinitionNotFoundException(e); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } } public SearchResult searchProcessDeploymentInfos(final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SearchProcessDefinitionsDescriptor searchDescriptor = searchEntitiesDescriptor .getSearchProcessDefinitionsDescriptor(); final SearchProcessDeploymentInfos transactionSearch = new SearchProcessDeploymentInfos( processDefinitionService, searchDescriptor, searchOptions); try { transactionSearch.execute(); } catch (final SBonitaException e) { throw new SearchException("Can't get processDefinition's executing searchProcessDefinitions()", e); } return transactionSearch.getResult(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ProcessInvolvementDelegate.java ================================================ /** * 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.api.impl; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.api.impl.transaction.process.GetLastArchivedProcessInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SUserNotFoundException; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.*; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; /** * @author Emmanuel Duchastenier */ public class ProcessInvolvementDelegate { private static final int BATCH_SIZE = 100; protected ServiceAccessor getServiceAccessor() { return ServiceAccessorSingleton.getInstance(); } private static QueryOptions buildArchivedTasksQueryOptions(final long processInstanceId) { final SAUserTaskInstanceBuilderFactory archUserTaskKeyFactory = BuilderFactory .get(SAUserTaskInstanceBuilderFactory.class); final String humanTaskIdKey = archUserTaskKeyFactory.getIdKey(); final String parentProcessInstanceKey = archUserTaskKeyFactory.getParentProcessInstanceKey(); final List archivedOrderByOptions = Collections .singletonList(new OrderByOption(SAHumanTaskInstance.class, humanTaskIdKey, OrderByType.ASC)); final List archivedFilterOptions = Collections .singletonList( new FilterOption(SAHumanTaskInstance.class, parentProcessInstanceKey, processInstanceId)); return new QueryOptions(0, BATCH_SIZE, archivedOrderByOptions, archivedFilterOptions, null); } public boolean isInvolvedInProcessInstance(final long userId, final long processInstanceId) throws ProcessInstanceNotFoundException { final TaskInvolvementDelegate taskInvolvementDelegate = new TaskInvolvementDelegate(); // IS_PROCESS_INITIATOR rule if (isProcessOrArchivedProcessInitiator(userId, processInstanceId)) { return true; } try { // IS_TASK_PERFORMER rule if (taskInvolvementDelegate.isExecutorOfArchivedTaskOfProcess(userId, processInstanceId)) { return true; } } catch (SBonitaReadException e) { throw new RetrieveException(e); } try { // IS_INVOLVED_IN_PROCESS_INSTANCE rule if (taskInvolvementDelegate.hasUserPendingOrAssignedTasks(userId, processInstanceId)) { return true; } } catch (SExecutionException e) { throw new RetrieveException(e); } return false; } public boolean isProcessOrArchivedProcessInitiator(long userId, long processInstanceId) throws ProcessInstanceNotFoundException { try { return isProcessInitiator(userId, processInstanceId); } catch (SProcessInstanceNotFoundException e) { return isArchivedProcessInitiator(userId, processInstanceId); } catch (SProcessInstanceReadException e) { throw new RetrieveException(e); } } private boolean isProcessInitiator(long userId, Long processInstanceId) throws SProcessInstanceNotFoundException, SProcessInstanceReadException { final ProcessInstanceService processInstanceService = getServiceAccessor().getProcessInstanceService(); final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); return userId == processInstance.getStartedBy(); } boolean isArchivedProcessInitiator(long userId, long processInstanceId) throws ProcessInstanceNotFoundException { final ProcessInstanceService processInstanceService = getServiceAccessor().getProcessInstanceService(); final List orderByOptions = Arrays.asList( new OrderByOption(SAProcessInstance.class, ArchivedProcessInstancesSearchDescriptor.ARCHIVE_DATE, OrderByType.DESC), new OrderByOption(SAProcessInstance.class, ArchivedProcessInstancesSearchDescriptor.END_DATE, OrderByType.DESC)); final List filterOptions = Collections.singletonList(new FilterOption(SAProcessInstance.class, ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, processInstanceId)); final QueryOptions queryOptions = new QueryOptions(0, 1, orderByOptions, filterOptions, null); final List saProcessInstances; try { saProcessInstances = processInstanceService.searchArchivedProcessInstances(queryOptions); } catch (SBonitaReadException e) { throw new RetrieveException(e); } if (saProcessInstances.isEmpty()) { throw new ProcessInstanceNotFoundException(processInstanceId); } return userId == (saProcessInstances.get(0).getStartedBy()); } public boolean isManagerOfUserInvolvedInProcessInstance(final long managerUserId, final long processInstanceId) throws BonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final IdentityService identityService = serviceAccessor.getIdentityService(); final TaskInvolvementDelegate taskInvolvementDelegate = new TaskInvolvementDelegate(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final List subordinates = getSubordinates(managerUserId, identityService); try { try { // Part specific to active process instances: final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); if (isUserManagerOfProcessInstanceInitiator(managerUserId, processInstance.getStartedBy())) { return true; } // Has the manager at least one subordinates with at least one pending task in this process instance: if (taskInvolvementDelegate.searchPendingTasksManagedBy(managerUserId, new SearchOptionsBuilder(0, 1) .filter(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, processInstanceId) .done()) .getCount() > 0) { return true; } QueryOptions queryOptions = buildActiveTasksQueryOptions(processInstanceId); List sHumanTaskInstances = activityInstanceService.searchHumanTasks(queryOptions); while (!sHumanTaskInstances.isEmpty()) { for (final SHumanTaskInstance sHumanTaskInstance : sHumanTaskInstances) { if (isTaskAssignedToAUserInTheList(sHumanTaskInstance, subordinates)) { return true; } } queryOptions = QueryOptions.getNextPage(queryOptions); sHumanTaskInstances = activityInstanceService.searchHumanTasks(queryOptions); } } catch (final SProcessInstanceNotFoundException exc) { // process instance may be completed already: // Part specific to archived process instances: try { final ArchivedProcessInstance archProcessInstance = getLastArchivedProcessInstance( processInstanceId); if (isUserManagerOfProcessInstanceInitiator(managerUserId, archProcessInstance.getStartedBy())) { return true; } } catch (final SBonitaException e) { throw new ProcessInstanceNotFoundException(processInstanceId); } } // Part common to active and archived process instances: return isArchivedTaskDoneByOneOfTheSubordinates(processInstanceId, activityInstanceService, subordinates); } catch (final SBonitaException e) { throw new BonitaException( "Problem while searching for users involved in process instance through their manager", e); } } private QueryOptions buildActiveTasksQueryOptions(final long processInstanceId) { final SUserTaskInstanceBuilderFactory userTaskKeyFactory = BuilderFactory .get(SUserTaskInstanceBuilderFactory.class); final String humanTaskIdKey = userTaskKeyFactory.getIdKey(); final String parentProcessInstanceKey = userTaskKeyFactory.getParentProcessInstanceKey(); final List orderByOptions = Collections .singletonList(new OrderByOption(SHumanTaskInstance.class, humanTaskIdKey, OrderByType.ASC)); final List filterOptions = Collections .singletonList(new FilterOption(SHumanTaskInstance.class, parentProcessInstanceKey, processInstanceId)); return new QueryOptions(0, BATCH_SIZE, orderByOptions, filterOptions, null); } private List getSubordinates(final long managerUserId, final IdentityService identityService) { final List userOrderBys = Collections .singletonList(new OrderByOption(SUser.class, SUser.ID, OrderByType.ASC)); final List userFilters = Collections .singletonList(new FilterOption(SUser.class, SUser.MANAGER_USER_ID, managerUserId)); try { return identityService.searchUsers(new QueryOptions(0, Integer.MAX_VALUE, userOrderBys, userFilters, null)); } catch (final SBonitaReadException e) { return Collections.emptyList(); } } private boolean isArchivedTaskDoneByOneOfTheSubordinates(final long processInstanceId, final ActivityInstanceService activityInstanceService, final List subordinates) throws SBonitaReadException { QueryOptions archivedQueryOptions = buildArchivedTasksQueryOptions(processInstanceId); List sArchivedHumanTasks = activityInstanceService .searchArchivedTasks(archivedQueryOptions); while (!sArchivedHumanTasks.isEmpty()) { for (final SAHumanTaskInstance sArchivedHumanTask : sArchivedHumanTasks) { if (isTaskDoneByAUserInTheList(sArchivedHumanTask, subordinates)) { return true; } } archivedQueryOptions = QueryOptions.getNextPage(archivedQueryOptions); sArchivedHumanTasks = activityInstanceService.searchArchivedTasks(archivedQueryOptions); } return false; } private boolean isTaskDoneByAUserInTheList(final SAHumanTaskInstance sArchivedHumanTask, final List users) { for (final SUser user : users) { if (user.getId() == sArchivedHumanTask.getExecutedBy()) { return true; } } return false; } private boolean isTaskAssignedToAUserInTheList(final SHumanTaskInstance humanTask, final List users) { for (final SUser user : users) { if (user.getId() == humanTask.getAssigneeId()) { return true; } } return false; } private boolean isUserManagerOfProcessInstanceInitiator(final long userId, final long startedByUserId) { final IdentityService identityService = getServiceAccessor().getIdentityService(); SUser sUser; try { sUser = identityService.getUser(startedByUserId); } catch (final SUserNotFoundException e) { return false; } return userId == sUser.getManagerUserId(); } public ArchivedProcessInstance getLastArchivedProcessInstance(final long processInstanceId) throws SBonitaException { final ProcessInstanceService processInstanceService = getServiceAccessor().getProcessInstanceService(); final ProcessDefinitionService processDefinitionService = getServiceAccessor() .getProcessDefinitionService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = getServiceAccessor() .getSearchEntitiesDescriptor(); final GetLastArchivedProcessInstance searchArchivedProcessInstances = new GetLastArchivedProcessInstance( processInstanceService, processDefinitionService, processInstanceId, searchEntitiesDescriptor); searchArchivedProcessInstances.execute(); return searchArchivedProcessInstances.getResult(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ProcessManagementAPIImplDelegate.java ================================================ /** * 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.api.impl; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.impl.transaction.process.DisableProcess; import org.bonitasoft.engine.bpm.parameter.ParameterCriterion; import org.bonitasoft.engine.bpm.parameter.ParameterInstance; import org.bonitasoft.engine.bpm.parameter.impl.ParameterImpl; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.classloader.SClassLoaderException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; import org.bonitasoft.engine.core.process.definition.model.SParameterDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.parameter.OrderBy; import org.bonitasoft.engine.parameter.ParameterService; import org.bonitasoft.engine.parameter.SParameter; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; /** * @author Matthieu Chaffotte */ // Uncomment the "implements" when this delegate implements all the methods. @Slf4j public class ProcessManagementAPIImplDelegate /* implements ProcessManagementAPI */ { private static final ProcessManagementAPIImplDelegate instance = new ProcessManagementAPIImplDelegate(); public static ProcessManagementAPIImplDelegate getInstance() { return instance; } protected ServiceAccessor getServiceAccessor() { try { return ServiceAccessorFactory.getInstance().createServiceAccessor(); } catch (final Exception e) { throw new RuntimeException(e); } } public static SProcessDefinition getServerProcessDefinition(final long processDefinitionId, final ProcessDefinitionService processDefinitionService) throws SProcessDefinitionNotFoundException, SBonitaReadException { return processDefinitionService.getProcessDefinition(processDefinitionId); } public void deleteProcessDefinition(final long processDefinitionId) throws SBonitaException, BonitaHomeNotSetException, IOException { final ServiceAccessor serviceAccessor = getServiceAccessor(); serviceAccessor.getBusinessArchiveService().delete(processDefinitionId); log.info("The user <" + SessionInfos.getUserNameFromSession() + "> has deleted process with id = <" + processDefinitionId + ">"); } public void disableProcess(final long processId) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final EventInstanceService eventInstanceService = serviceAccessor.getEventInstanceService(); final SchedulerService schedulerService = serviceAccessor.getSchedulerService(); final DisableProcess disableProcess = new DisableProcess(processDefinitionService, processId, eventInstanceService, schedulerService, SessionInfos.getUserNameFromSession()); disableProcess.execute(); } public void purgeClassLoader(final long processDefinitionId) throws ProcessDefinitionNotFoundException, UpdateException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final SProcessDefinitionDeployInfo processDeploymentInfo = processDefinitionService .getProcessDeploymentInfo(processDefinitionId); if (!ActivationState.DISABLED.name().equals(processDeploymentInfo.getActivationState())) { throw new UpdateException("Purge can only be done on a disabled process"); } final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final long numberOfProcessInstances = processInstanceService .getNumberOfProcessInstances(processDefinitionId); if (numberOfProcessInstances != 0) { throw new UpdateException("Purge can only be done on a disabled process with no running instances"); } serviceAccessor.getClassLoaderService().removeLocalClassloader(identifier(PROCESS, processDefinitionId)); } catch (final SProcessDefinitionNotFoundException e) { throw new ProcessDefinitionNotFoundException(e); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } catch (SClassLoaderException e) { throw new UpdateException(e); } } public List getParameterInstances(final long processDefinitionId, final int startIndex, final int maxResults, final ParameterCriterion sort) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ParameterService parameterService = serviceAccessor.getParameterService(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { OrderBy order; switch (sort) { case NAME_DESC: order = OrderBy.NAME_DESC; break; default: order = OrderBy.NAME_ASC; break; } final SProcessDefinition sProcessDefinition = getServerProcessDefinition(processDefinitionId, processDefinitionService); if (sProcessDefinition.getParameters().isEmpty()) { return Collections.emptyList(); } final List parameters = parameterService.get(processDefinitionId, startIndex, maxResults, order); final List parameterInstances = new ArrayList<>(); for (final SParameter parameter : parameters) { final String name = parameter.getName(); final String value = parameter.getValue(); final SParameterDefinition parameterDefinition = sProcessDefinition.getParameter(name); final String description = parameterDefinition.getDescription(); final String type = parameterDefinition.getType(); parameterInstances.add(new ParameterImpl(name, description, value, type)); } return parameterInstances; } catch (final SBonitaException e) { throw new RetrieveException(e); } } public int getNumberOfParameterInstances(final long processDefinitionId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final SProcessDefinition sProcessDefinition = getServerProcessDefinition(processDefinitionId, processDefinitionService); return sProcessDefinition.getParameters().size(); } catch (final SBonitaException e) { throw new RetrieveException(e); } } public ParameterInstance getParameterInstance(final long processDefinitionId, final String parameterName) throws NotFoundException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ParameterService parameterService = serviceAccessor.getParameterService(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { final SProcessDefinition sProcessDefinition = getServerProcessDefinition(processDefinitionId, processDefinitionService); final SParameter parameter = parameterService.get(processDefinitionId, parameterName); if (parameter == null) { throw new NotFoundException("the parameter with name " + parameterName + " and process with id " + processDefinitionId + " was not found."); } final String name = parameter.getName(); final String value = parameter.getValue(); final SParameterDefinition parameterDefinition = sProcessDefinition.getParameter(name); final String description = parameterDefinition.getDescription(); final String type = parameterDefinition.getType(); return new ParameterImpl(name, description, value, type); } catch (final SBonitaException e) { throw new RetrieveException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ProcessStarter.java ================================================ /** * 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.api.impl; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Supplier; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.bpm.connector.ConnectorDefinitionWithInputValues; import org.bonitasoft.engine.bpm.contract.ContractViolationException; import org.bonitasoft.engine.bpm.process.ProcessActivationException; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessExecutionException; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.process.comment.api.SCommentService; 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.SFlowNodeDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.execution.Filter; import org.bonitasoft.engine.execution.FlowNodeNameFilter; import org.bonitasoft.engine.execution.FlowNodeSelector; import org.bonitasoft.engine.execution.ProcessExecutor; import org.bonitasoft.engine.execution.StartFlowNodeFilter; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.mdc.MDCHelper; import org.bonitasoft.engine.mdc.MDCHelper.CheckedCallable4; import org.bonitasoft.engine.mdc.ProcessInstanceMDC; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; /** * @author Elias Ricken de Medeiros * @author Vincent Elcrin * @author Matthieu Chaffotte */ @Slf4j public class ProcessStarter { private final long userId; private final long processDefinitionId; private final List operations; private final Map context; private final Filter filter; private final Map processContractInputs; private ProcessStarter(final long userId, final long processDefinitionId, final List operations, final Map context, final Filter filter, final Map processContractInputs) { this.userId = userId; this.processDefinitionId = processDefinitionId; this.operations = operations; this.context = context; this.filter = filter; this.processContractInputs = processContractInputs; } public ProcessStarter(final long userId, final long processDefinitionId, final List operations, final Map context) { this(userId, processDefinitionId, operations, context, new StartFlowNodeFilter(), null); } public ProcessStarter(final long userId, final long processDefinitionId, final List operations, final Map context, final List activityNames, Map processContractInputs) { this(userId, processDefinitionId, operations, context, new FlowNodeNameFilter(activityNames), processContractInputs); } public ProcessStarter(final long userId, final long processDefinitionId, final Map processContractInputs) { this(userId, processDefinitionId, null, null, new StartFlowNodeFilter(), processContractInputs); } public ProcessInstance start() throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException, ContractViolationException { try { return start(null); } catch (final SContractViolationException e) { throw new ContractViolationException(e.getSimpleMessage(), e.getMessage(), e.getExplanations(), e.getCause()); } catch (final SProcessDefinitionNotFoundException e) { throw new ProcessDefinitionNotFoundException(e); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } catch (final SProcessDefinitionException e) { throw new ProcessActivationException(e); } catch (final SProcessInstanceCreationException e) { if (e.getRetryAfter() != -1L) { throw new ProcessExecutionException(e, e.getRetryAfter()); } else { throw new ProcessExecutionException(e); } } } // For commands public ProcessInstance start(final List connectorsWithInput) throws SProcessInstanceCreationException, SBonitaReadException, SProcessDefinitionException, SContractViolationException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ProcessExecutor processExecutor = serviceAccessor.getProcessExecutor(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SProcessDefinition sProcessDefinition = processDefinitionService .getProcessDefinitionIfIsEnabled(processDefinitionId); final Map operationContext = getContext(); final long starterSubstituteUserId = SessionInfos.getUserIdFromSession(); final long starterUserId = getStarterUserId(starterSubstituteUserId); Supplier processInstanceMDC = () -> new ProcessInstanceMDC(0, Optional.of(starterUserId), Optional.of(starterSubstituteUserId), sProcessDefinition.getId(), 0); CheckedCallable4 call = () -> { try { final List sOperations = ModelConvertor.convertOperations(operations); final SProcessInstance startedSProcessInstance = processExecutor.start(starterUserId, starterSubstituteUserId, sOperations, operationContext, connectorsWithInput, new FlowNodeSelector(sProcessDefinition, filter), processContractInputs); logProcessInstanceStartedAndAddComment(sProcessDefinition, starterUserId, starterSubstituteUserId, startedSProcessInstance); return ModelConvertor.toProcessInstance(sProcessDefinition, startedSProcessInstance); } catch (final SProcessInstanceCreationException e) { e.setProcessDefinitionIdOnContext(sProcessDefinition.getId()); e.setProcessDefinitionNameOnContext(sProcessDefinition.getName()); e.setProcessDefinitionVersionOnContext(sProcessDefinition.getVersion()); throw e; } }; return MDCHelper.tryWithMDC(processInstanceMDC, call); } protected long getStarterUserId(final long starterSubstituteUserId) { if (userId == 0) { return starterSubstituteUserId; } return userId; } protected Map getContext() { if (context != null) { return new HashMap<>(context); } return Collections.emptyMap(); } private void logProcessInstanceStartedAndAddComment(final SProcessDefinition sProcessDefinition, final long starterId, final long starterSubstituteId, final SProcessInstance sProcessInstance) { final StringBuilder stb = new StringBuilder(); stb.append("The user <"); stb.append(SessionInfos.getUserNameFromSession()); if (starterId != starterSubstituteId) { stb.append("> acting as delegate of user with id <"); stb.append(starterId); } stb.append("> has started the process instance <"); stb.append(sProcessInstance.getId()); stb.append("> of process <"); stb.append(sProcessDefinition.getName()); stb.append("> in version <"); stb.append(sProcessDefinition.getVersion()); stb.append("> and id <"); stb.append(sProcessDefinition.getId()); stb.append(">"); log.info(stb.toString()); addSystemCommentOnProcessInstanceWhenStartingProcessFor(sProcessInstance, starterId, starterSubstituteId); } protected void addSystemCommentOnProcessInstanceWhenStartingProcessFor(final SProcessInstance sProcessInstance, final long starterId, final long starterSubstituteId) { final ServiceAccessor serviceAccessor = getServiceAccessor(); final SCommentService commentService = serviceAccessor.getCommentService(); if (starterId != starterSubstituteId) { final IdentityService identityService = serviceAccessor.getIdentityService(); try { final SUser starter = identityService.getUser(starterId); commentService.addSystemComment(sProcessInstance.getId(), "The user " + SessionInfos.getUserNameFromSession() + " acting as delegate of the user " + starter.getUserName() + " has started the case."); } catch (final SBonitaException e) { log.error("Error when adding a comment on the process instance.", e); } } } protected ServiceAccessor getServiceAccessor() { try { return ServiceAccessorSingleton.getInstance(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ProfileAPIImpl.java ================================================ /** * 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.api.impl; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ProfileAPI; import org.bonitasoft.engine.api.impl.profile.ProfileAPIDelegate; import org.bonitasoft.engine.api.utils.VisibleForTesting; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.identity.MemberType; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.profile.ProfileCriterion; import org.bonitasoft.engine.profile.ProfileMember; import org.bonitasoft.engine.profile.ProfileMemberCreator; import org.bonitasoft.engine.profile.ProfileNotFoundException; import org.bonitasoft.engine.profile.model.SProfileMember; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Celine Souchet * @author Matthieu Chaffotte */ @AvailableInMaintenanceMode public class ProfileAPIImpl implements ProfileAPI { @VisibleForTesting ProfileAPIDelegate getProfileAPIDelegate() { return ProfileAPIDelegate.getInstance(); } @Override public Profile getProfile(final long id) throws ProfileNotFoundException { return getProfileAPIDelegate().getProfile(id); } @Override public List getProfilesForUser(final long userId, final int startIndex, final int maxResults, final ProfileCriterion criterion) { return getProfileAPIDelegate().getProfilesForUser(userId, startIndex, maxResults, criterion); } @Override public SearchResult searchProfiles(final SearchOptions options) throws SearchException { return getProfileAPIDelegate().searchProfiles(options); } @Override public Map getNumberOfProfileMembers(final List profileIds) { return getProfileAPIDelegate().getNumberOfProfileMembers(profileIds); } @Override public SearchResult searchProfileMembers(final String memberType, final SearchOptions options) throws SearchException { return getProfileAPIDelegate().searchProfileMembers(memberType, options); } @Override public ProfileMember createProfileMember(final Long profileId, final Long userId, final Long groupId, final Long roleId) throws CreationException { return getProfileAPIDelegate().createProfileMember(profileId, userId, groupId, roleId); } @Override public ProfileMember createProfileMember(final ProfileMemberCreator creator) throws CreationException, AlreadyExistsException { if (creator == null) { throw new CreationException("Unable to create a profile member with a null creator!"); } final SProfileMember sProfileMember = ModelConvertor.constructSProfileMember(creator); return createProfileMember(sProfileMember.getProfileId(), sProfileMember.getUserId(), sProfileMember.getGroupId(), sProfileMember.getRoleId()); } public MemberType getMemberType(final Long userId, final Long groupId, final Long roleId) throws CreationException { return getProfileAPIDelegate().getMemberType(userId, groupId, roleId); } @Override public void deleteProfileMember(final Long profileMemberId) throws DeletionException { getProfileAPIDelegate().deleteProfileMember(profileMemberId); } @VisibleForTesting public long getUserIdFromSession() { return SessionInfos.getUserIdFromSession(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/SCustomUserInfoValueAPI.java ================================================ /** * 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.api.impl; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.identity.CustomUserInfoValue; import org.bonitasoft.engine.identity.CustomUserInfoValueUpdater; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SIdentityException; import org.bonitasoft.engine.identity.model.SCustomUserInfoValue; import org.bonitasoft.engine.identity.model.builder.SCustomUserInfoValueUpdateBuilderFactory; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.search.identity.SearchCustomUserInfoValues; /** * @author Vincent Elcrin */ public class SCustomUserInfoValueAPI { private final SCustomUserInfoValueUpdateBuilderFactory updaterFactory; private final IdentityService service; public SCustomUserInfoValueAPI(IdentityService service, SCustomUserInfoValueUpdateBuilderFactory updaterFactory) { this.updaterFactory = updaterFactory; this.service = service; } public SearchResult search(SearchEntityDescriptor descriptor, final SearchOptions options) throws SBonitaException { SearchCustomUserInfoValues search = new SearchCustomUserInfoValues(service, descriptor, options); search.execute(); return search.getResult(); } public SCustomUserInfoValue update(SCustomUserInfoValue value, CustomUserInfoValueUpdater updater) throws SIdentityException { assertNoNull("Cannot update a value based on null parameters", value, updater); service.updateCustomUserInfoValue(value, updaterFactory.createNewInstance() .updateValue(updater.getValue()) .done()); return service.getCustomUserInfoValue(value.getId()); } public SCustomUserInfoValue set(long definitionId, long userId, String value) throws SIdentityException, SBonitaReadException { SCustomUserInfoValue customUserInfoValue = searchValue(definitionId, userId); if (value == null || value.isEmpty()) { delete(customUserInfoValue); return createValue(definitionId, userId, value); } if (customUserInfoValue != null) { return update(customUserInfoValue, new CustomUserInfoValueUpdater(value)); } return create(definitionId, userId, value); } public SCustomUserInfoValue create(long definitionId, long userId, String value) throws SIdentityException { return service.createCustomUserInfoValue(createValue(definitionId, userId, value)); } public void delete(SCustomUserInfoValue value) throws SIdentityException { if (value != null) { service.deleteCustomUserInfoValue(value); } } private SCustomUserInfoValue createValue(long definitionId, long userId, String value) { return SCustomUserInfoValue.builder() .definitionId(definitionId) .userId(userId) .value(value).build(); } private SCustomUserInfoValue searchValue(long definitionId, long userId) throws SBonitaReadException { List result = service.getCustomUserInfoValueOfUserAndDefinitions(userId, Arrays.asList(definitionId)); if (result.size() == 0) { return null; } return result.get(0); } private void assertNoNull(String message, Object... objects) { for (Object object : objects) { if (object == null) { throw new IllegalArgumentException(message); } } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ServerAPIFactory.java ================================================ /** * 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.api.impl; import org.bonitasoft.engine.api.internal.ServerAPI; import org.bonitasoft.engine.home.BonitaHomeServer; /** * This class load the server api implementation configured using the property `serverApi`. * All server-side code goes through this class to retrieve the server api implementation. */ public class ServerAPIFactory { private static final String SERVER_API_CLASS_NOT_FOUND = "Cannot load class %s. Platform property 'serverApi' may not be set."; private static ServerAPIFactory INSTANCE = new ServerAPIFactory(BonitaHomeServer.getInstance()); private final BonitaHomeServer bonitaHomeServer; private Class serverApiClass; ServerAPIFactory(final BonitaHomeServer bonitaHomeServer) { this.bonitaHomeServer = bonitaHomeServer; } public static ServerAPI getServerAPI() { return ServerAPIFactory.getInstance().getServerAPIImplementation(); } public static ServerAPI getServerAPI(final boolean cleanSession) { return ServerAPIFactory.getInstance().getServerAPIImplementation(cleanSession); } ServerAPI getServerAPIImplementation() { Class aClass = getServerAPIClass(); try { return (ServerAPI) aClass.newInstance(); } catch (Exception e) { throw new ExceptionInInitializerError(String.format(SERVER_API_CLASS_NOT_FOUND, aClass.getName())); } } private Class getServerAPIClass() { if (serverApiClass == null) { String serverAPIClassName = bonitaHomeServer.getServerAPIImplementation(); try { serverApiClass = Class.forName(serverAPIClassName); } catch (Exception e) { throw new ExceptionInInitializerError(String.format(SERVER_API_CLASS_NOT_FOUND, serverAPIClassName)); } } return serverApiClass; } private ServerAPI getServerAPIImplementation(final boolean cleanSession) { Class serverApiClass = getServerAPIClass(); try { return (ServerAPI) serverApiClass.getConstructor(boolean.class).newInstance(cleanSession); } catch (Exception e) { throw new ExceptionInInitializerError(String.format(SERVER_API_CLASS_NOT_FOUND, serverApiClass.getName())); } } public static ServerAPIFactory getInstance() { return INSTANCE; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ServerAPIImpl.java ================================================ /** * 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.api.impl; import java.io.IOException; import java.io.Serial; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; import java.text.MessageFormat; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import org.bonitasoft.engine.api.MaintenanceAPI; import org.bonitasoft.engine.api.NoSessionRequired; import org.bonitasoft.engine.api.PlatformAPI; import org.bonitasoft.engine.api.impl.transaction.CustomTransactions; import org.bonitasoft.engine.api.internal.ServerAPI; import org.bonitasoft.engine.api.internal.ServerWrappedException; import org.bonitasoft.engine.classloader.BonitaClassLoader; import org.bonitasoft.engine.classloader.ClassLoaderIdentifier; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.classloader.SClassLoaderException; import org.bonitasoft.engine.commons.ClassReflector; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.login.LoginService; import org.bonitasoft.engine.core.platform.login.PlatformLoginService; import org.bonitasoft.engine.exception.BonitaContextException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeConfigurationException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.exception.TenantStatusException; import org.bonitasoft.engine.exception.UnavailableLockException; import org.bonitasoft.engine.lock.BonitaLock; import org.bonitasoft.engine.lock.LockService; import org.bonitasoft.engine.maintenance.MaintenanceDetails; import org.bonitasoft.engine.mdc.MDCHelper; import org.bonitasoft.engine.mdc.UserIdMDC; import org.bonitasoft.engine.platform.NodeNotStartedException; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.platform.PlatformState; import org.bonitasoft.engine.platform.session.PlatformSessionService; import org.bonitasoft.engine.platform.session.SSessionException; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; import org.bonitasoft.engine.service.APIAccessResolver; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.engine.session.Session; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.transaction.UserTransactionService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class is in charge of calling APIs while adding behavior: *
    *
  • It sets the classloader to the one from platform or tenant
  • *
  • When the method is NOT annotated with {@link NoSessionRequired}, it verifies that the given session is * valid, is on the right scope (tenant or platform), and renew it
  • *
  • When the method is NOT annotated with {@link CustomTransactions}, it opens a transaction
  • *
  • When the method is deprecated, it print a warning
  • *
  • When the method or class is NOT annotated with {@link AvailableInMaintenanceMode}, it verifies the * maintenance mode * is disabled
  • *
  • When the method or class is annotated with {@link AvailableInMaintenanceMode} and onlyAvailableInMaintenanceMode * is set * to true, it verifies the maintenance mode is enabled
  • *
  • When the method is NOT annotated with {@link AvailableOnStoppedNode}, it verifies the platform is * running
  • *
*/ public class ServerAPIImpl implements ServerAPI { private static final Logger logger = LoggerFactory.getLogger(ServerAPIImpl.class); private static final String SESSION = "session"; @Serial private static final long serialVersionUID = -161775388604256321L; private final APIAccessResolver accessResolver; private final boolean cleanSession; public ServerAPIImpl(boolean cleanSession) { try { this.cleanSession = cleanSession; accessResolver = getServiceAccessorFactoryInstance().createAPIAccessResolver(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } public ServerAPIImpl() { this(true); } /** * For Test Mock usage */ public ServerAPIImpl(APIAccessResolver accessResolver) { this.cleanSession = true; this.accessResolver = accessResolver; } ServiceAccessorFactory getServiceAccessorFactoryInstance() { return ServiceAccessorFactory.getInstance(); } @Override public Object invokeMethod(final Map options, final String apiInterfaceName, final String methodName, final List classNameParameters, final Object[] parametersValues) throws ServerWrappedException { logger.trace("Starting Server API call {} {}", apiInterfaceName, methodName); final ClassLoader baseClassLoader = Thread.currentThread().getContextClassLoader(); SessionAccessor sessionAccessor = null; Session session = null; try { final Object api = accessResolver.getAPIImplementation(Class.forName(apiInterfaceName)); try { session = (Session) options.get(SESSION); sessionAccessor = beforeInvokeMethod(session, api); return invokeAPI(api, apiInterfaceName, methodName, classNameParameters, parametersValues, session); } catch (final ServerAPIRuntimeException e) { throw e.getCause(); } } catch (final BonitaRuntimeException | BonitaException bre) { fillGlobalContextForException(session, bre); throw createServerWrappedException(bre); } catch (final UndeclaredThrowableException ute) { throw createServerWrappedException(ute); } catch (final Throwable cause) { // Note: at this point the transaction outcome is uncertain — it may have // committed before the failure occurred. The client will receive an error // regardless. logger.error("Unexpected failure during API call {}.{}() (session={})", apiInterfaceName, methodName, session, cause); final BonitaRuntimeException throwableToWrap = wrapThrowable(cause); fillGlobalContextForException(session, throwableToWrap); throw createServerWrappedException(throwableToWrap); } finally { cleanSessionIfNeeded(sessionAccessor); // Reset the original classloader when not an equivalent BonitaClassLoader // We do not reset the the classloader if the original classloader and the current classloader are BonitaClassloader having the same name // e.g. for the cleanAndUninstallBusinessDataModel API method that reset the current classloader if (shouldResetClassloader(baseClassLoader, Thread.currentThread().getContextClassLoader())) { Thread.currentThread().setContextClassLoader(baseClassLoader); } logger.trace("End Server API call {} {}", apiInterfaceName, methodName); } } private boolean shouldResetClassloader(ClassLoader baseClassLoader, ClassLoader currentClassloader) { if (currentClassloader instanceof BonitaClassLoader bonitaClassLoader && baseClassLoader instanceof BonitaClassLoader bonitaBaseClassLoader) { // Classloader name is different, reset it return !Objects.equals(bonitaClassLoader.getName(), bonitaBaseClassLoader.getName()); } else { return true; } } protected BonitaRuntimeException wrapThrowable(final Throwable cause) { return new BonitaRuntimeException(cause); } private ServerWrappedException createServerWrappedException(final Throwable throwableToWrap) { return new ServerWrappedException(throwableToWrap); } private void fillGlobalContextForException(final Session session, final BonitaContextException be) { fillUserNameContextForException(session, be); } private void fillUserNameContextForException(final Session session, final BonitaContextException be) { if (session != null) { final String userName = session.getUserName(); if (userName != null) { be.setUserName(userName); } } } private void cleanSessionIfNeeded(SessionAccessor sessionAccessor) { if (cleanSession) { // clean session id if (sessionAccessor != null) { sessionAccessor.deleteSessionId(); } } } private SessionAccessor beforeInvokeMethod(final Session session, final Object api) throws BonitaHomeNotSetException, BonitaHomeConfigurationException, IOException, SBonitaException, ReflectiveOperationException { SessionAccessor sessionAccessor = null; final ServiceAccessorFactory serviceAccessorFactory = getServiceAccessorFactoryInstance(); final ServiceAccessor serviceAccessor = serviceAccessorFactory.createServiceAccessor(); ClassLoader serverClassLoader = null; if (session != null) { final SessionType sessionType = getSessionType(session); sessionAccessor = serviceAccessorFactory.createSessionAccessor(); serverClassLoader = switch (sessionType) { case PLATFORM -> beforeInvokeMethodForPlatformSession(sessionAccessor, serviceAccessor, session); case API -> beforeInvokeMethodForAPISession(sessionAccessor, serviceAccessor, session); }; } else if (needSession(api)) { throw new InvalidSessionException("Session is null!"); } if (serverClassLoader != null) { Thread.currentThread().setContextClassLoader(serverClassLoader); } return sessionAccessor; } private boolean needSession(Object api) { //require a session if "NoSessionRequired" is not present Class[] interfaces = api.getClass().getInterfaces(); for (Class anInterface : interfaces) { if (anInterface.isAnnotationPresent(NoSessionRequired.class)) { return false; } } return true; } private ClassLoader beforeInvokeMethodForAPISession(SessionAccessor sessionAccessor, ServiceAccessor serviceAccessor, Session session) throws SBonitaException { checkTenantSession(serviceAccessor, session); SessionService sessionService = serviceAccessor.getSessionService(); sessionService.renewSession(session.getId()); sessionAccessor.setSessionId(session.getId()); return getTenantClassLoader(serviceAccessor, session); } private ClassLoader beforeInvokeMethodForPlatformSession(final SessionAccessor sessionAccessor, final ServiceAccessor serviceAccessor, final Session session) throws SSessionException, SClassLoaderException { final PlatformSessionService platformSessionService = serviceAccessor.getPlatformSessionService(); final PlatformLoginService loginService = serviceAccessor.getPlatformLoginService(); if (!loginService.isValid(session.getId())) { throw new InvalidSessionException("Invalid session"); } platformSessionService.renewSession(session.getId()); sessionAccessor.setSessionId(session.getId()); return getPlatformClassLoader(serviceAccessor); } private SessionType getSessionType(final Session session) { SessionType sessionType = null; if (session instanceof PlatformSession) { sessionType = SessionType.PLATFORM; } else if (session instanceof APISession) { sessionType = SessionType.API; } return sessionType; } private Object invokeAPI(Object api, String apiInterfaceName, final String methodName, final List classNameParameters, final Object[] parametersValues, final Session session) throws Throwable { var userId = Optional.ofNullable(session).map(Session::getUserId).filter(l -> l >= 0L); Supplier withAuthenticatedUser = UserIdMDC.supplierFromOptionalId(userId); return MDCHelper.tryWithMDC(withAuthenticatedUser, () -> { final Class[] parameterTypes = getParameterTypes(classNameParameters); final Method method = ClassReflector.getMethod(api.getClass(), methodName, parameterTypes); // first, check if a lock is needed before opening any transaction // and get the key which defines the functional scope Optional lockKey = Optional.ofNullable(method.getAnnotation(WithLock.class)).map(WithLock::key); // try and acquire a lock with this scope if necessary Supplier failureMessage = () -> MessageFormat.format( "Operation ''{0}.{1}'' requires exclusive access. Another operation is already launched with the same ''{2}'' access scope. You may try again after the other operation has finished.", apiInterfaceName, methodName, lockKey.orElse("")); try (AutoCloseable ignored = withEventualLock(lockKey, failureMessage)) { // No session required means that there is no transaction if (method.isAnnotationPresent(CustomTransactions.class) || Class.forName(apiInterfaceName).isAnnotationPresent(NoSessionRequired.class)) { return invokeAPIOutsideTransaction(parametersValues, api, method, apiInterfaceName, session); } else { return invokeAPIInTransaction(parametersValues, api, method, session, apiInterfaceName); } } }); } /** * Eventually get a functional lock auto-closeable resource. * * @param lockKey the functional key for the lock scope or an empty * optional when no lock is necessary * @param failureMessage builds the failure message when lock is already taken * @return the auto-closeable resource or a stub ineffective resource when * lockKey is empty * @throws UnavailableLockException error with built message when * lock is already taken. */ private AutoCloseable withEventualLock(final Optional lockKey, Supplier failureMessage) throws Throwable { if (lockKey.isPresent()) { // try and acquire a lock with this scope LockService lockService = getServiceAccessorFactoryInstance().createServiceAccessor().getLockService(); BonitaLock lock = lockService.tryLock(1L, lockKey.get(), 1L, TimeUnit.MILLISECONDS); if (lock == null) { // timeout expired, we should not pursue this way throw new UnavailableLockException(failureMessage.get()); } return () -> lockService.unlock(lock); } else { // ineffective resource return () -> { }; } } private Object invokeAPIOutsideTransaction(Object[] parametersValues, Object apiImpl, Method method, String apiInterfaceName, Session session) throws Throwable { checkMethodAccessibility(apiImpl, apiInterfaceName, method, session, /* Not in transaction */false); return invokeAPI(method, apiImpl, parametersValues); } protected void checkMethodAccessibility(final Object apiImpl, final String apiInterfaceName, final Method method, final Session session, boolean isAlreadyInTransaction) { final MethodAvailability methodAvailability = getMethodAvailability(apiImpl, method); if (methodAvailability.isDeprecated) { logger.warn("The API method {}.{} is deprecated. It will be deleted in a future release. " + "Please plan to update your code to use the replacement method instead. Check the Javadoc for more details.", apiInterfaceName, method.getName()); } if (!methodAvailability.isAvailableWhenPlatformIsStopped && !isNodeStarted()) { logger.error( "Node not started. Method '{}. {}' cannot be called until node has been started (PlatformAPI.startNode()). Exact class: {}", apiInterfaceName, method.getName(), method.getDeclaringClass().getName()); throw new NodeNotStartedException(); } // we don't check maintenance mode at platform level and when there is no session // when there is no session means that we are trying to log in, in this case it is the LoginApiExt that check if the user is the technical user // For tenant level method call: if (!(session instanceof APISession)) { return; } if (methodAvailability.isAvailableInMaintenanceMode && methodAvailability.isAvailableWhenMaintenanceModeIsDisabled) { //method can be called when maintenance is enabled or disabled. return; } boolean isMaintenanceModeEnabled = isMaintenanceModeEnabled(session, isAlreadyInTransaction); if (isMaintenanceModeEnabled && !methodAvailability.isAvailableInMaintenanceMode) { throw new TenantStatusException( MessageFormat.format("Unable to call API method {0}.{1}, Maintenance mode is enabled.", apiInterfaceName, method.getName())); } if (!isMaintenanceModeEnabled && !methodAvailability.isAvailableWhenMaintenanceModeIsDisabled) { throw new TenantStatusException(MessageFormat.format( "Unable to call API method {0}.{1}, Maintenance mode is disabled and this method can only be called when Maintenance mode is enabled.", apiInterfaceName, method.getName())); } } private static class MethodAvailability { boolean isDeprecated; boolean isAvailableWhenMaintenanceModeIsDisabled = true; boolean isAvailableInMaintenanceMode = true; boolean isAvailableWhenPlatformIsStopped; } private MethodAvailability getMethodAvailability(Object apiInstance, Method method) { AvailableInMaintenanceMode availableInMaintenanceMode = Optional .ofNullable(method.getAnnotation(AvailableInMaintenanceMode.class)) .orElseGet(() -> apiInstance.getClass().getAnnotation(AvailableInMaintenanceMode.class)); AvailableOnStoppedNode availableOnStoppedNode = method.getAnnotation(AvailableOnStoppedNode.class); MethodAvailability methodAvailability = new MethodAvailability(); // Deprecation methodAvailability.isDeprecated = method.isAnnotationPresent(Deprecated.class); // Maintenance mode if (availableInMaintenanceMode == null) { methodAvailability.isAvailableInMaintenanceMode = false; } else if (availableInMaintenanceMode.onlyAvailableInMaintenanceMode()) { methodAvailability.isAvailableWhenMaintenanceModeIsDisabled = false; } // Platform status if (availableOnStoppedNode != null) { methodAvailability.isAvailableWhenPlatformIsStopped = true; } return methodAvailability; } /** * @param session the session to user * @param isAlreadyInTransaction if the request is made in a transaction * @return true if the maintenance mode is enabled, false otherwise */ protected boolean isMaintenanceModeEnabled(final Session session, boolean isAlreadyInTransaction) { try { MaintenanceAPI maintenanceAPI = accessResolver .getAPIImplementation(MaintenanceAPI.class); if (isAlreadyInTransaction) { return MaintenanceDetails.State.ENABLED .equals(maintenanceAPI.getMaintenanceDetails().getMaintenanceState()); } else { return selectUserTransactionService(session, getSessionType(session)) .executeInTransaction(() -> MaintenanceDetails.State.ENABLED .equals(maintenanceAPI.getMaintenanceDetails().getMaintenanceState())); } } catch (final Throwable e) { throw new BonitaRuntimeException("Cannot determine if the Maintenance mode is enabled", e); } } /** * @return true if the node is started, false otherwise. */ private boolean isNodeStarted() { try { return accessResolver.getAPIImplementation(PlatformAPI.class).isNodeStarted(); } catch (final Throwable e) { return false; } } protected Object invokeAPIInTransaction(final Object[] parametersValues, final Object apiImpl, final Method method, final Session session, final String apiInterfaceName) throws Throwable { if (session == null) { throw new BonitaRuntimeException("session is null"); } final UserTransactionService userTransactionService = selectUserTransactionService(session, getSessionType(session)); return userTransactionService.executeInTransaction(() -> { try { checkMethodAccessibility(apiImpl, apiInterfaceName, method, session, /* Already in a transaction */true); return invokeAPI(method, apiImpl, parametersValues); } catch (final Throwable cause) { throw new ServerAPIRuntimeException(cause); } }); } UserTransactionService selectUserTransactionService(final Session session, final SessionType sessionType) throws BonitaHomeNotSetException, IOException, BonitaHomeConfigurationException, ReflectiveOperationException { UserTransactionService transactionService; final ServiceAccessorFactory serviceAccessorFactory = getServiceAccessorFactoryInstance(); final ServiceAccessor serviceAccessor = serviceAccessorFactory.createServiceAccessor(); transactionService = switch (sessionType) { case PLATFORM -> serviceAccessor.getTransactionService(); case API -> serviceAccessor.getUserTransactionService(); }; return transactionService; } protected Object invokeAPI(final Method method, final Object apiImpl, final Object... parametersValues) throws Throwable { try { return method.invoke(apiImpl, parametersValues); } catch (final InvocationTargetException e) { throw e.getCause(); } } private Class[] getParameterTypes(final List classNameParameters) throws ClassNotFoundException { Class[] parameterTypes = null; if (classNameParameters != null && !classNameParameters.isEmpty()) { parameterTypes = new Class[classNameParameters.size()]; for (int i = 0; i < parameterTypes.length; i++) { final String className = classNameParameters.get(i); Class classType; if ("int".equals(className)) { classType = int.class; } else if ("long".equals(className)) { classType = long.class; } else if ("boolean".equals(className)) { classType = boolean.class; } else { classType = Class.forName(className); } parameterTypes[i] = classType; } } return parameterTypes; } private void checkTenantSession(final ServiceAccessor serviceAccessor, final Session session) throws SSchedulerException { final SchedulerService schedulerService = serviceAccessor.getSchedulerService(); if (!schedulerService.isStarted()) { logger.debug("The scheduler is not started!"); } final APISession apiSession = (APISession) session; final LoginService tenantLoginService = serviceAccessor.getLoginService(); if (!tenantLoginService.isValid(apiSession.getId())) { throw new InvalidSessionException("Invalid session"); } } private ClassLoader getTenantClassLoader(final ServiceAccessor serviceAccessor, final Session session) throws SClassLoaderException { final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); return classLoaderService.getClassLoader(ClassLoaderIdentifier.TENANT); } private ClassLoader getPlatformClassLoader(final ServiceAccessor serviceAccessor) throws SClassLoaderException { ClassLoader classLoader = null; PlatformState state = serviceAccessor.getPlatformManager().getState(); if (state != PlatformState.STARTED) { // We do not retrieve the platform classloader when the platform is not yet started // It needs to have services to be started to retrieve it // Returning null will cause the context classloader to be left untouched logger.debug("Tried to retrieve platform classloader on a not started platform, state = {}", state); return null; } final PlatformService platformService = serviceAccessor.getPlatformService(); // get the platform to put it in cache if needed if (!platformService.isPlatformCreated()) { try { serviceAccessor.getTransactionService().executeInTransaction((Callable) () -> { platformService.getPlatform(); return null; }); } catch (final Exception ignored) { // do not throw exceptions: it's just in case the platform was not in cache } } if (platformService.isPlatformCreated()) { final ClassLoaderService classLoaderService = serviceAccessor.getClassLoaderService(); classLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.GLOBAL); } return classLoader; } protected enum SessionType { PLATFORM, API } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ServerAPIRuntimeException.java ================================================ /** * 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.api.impl; /** * @author Baptiste Mesta */ public class ServerAPIRuntimeException extends RuntimeException { private static final long serialVersionUID = -5675131531953146131L; public ServerAPIRuntimeException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/SessionInfos.java ================================================ /** * 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.api.impl; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.session.SSessionNotFoundException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.session.model.SSession; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException; /** * @author Baptiste Mesta */ public class SessionInfos { private final String username; private final long userId; public SessionInfos(final String username, final long userId) { this.username = username; this.userId = userId; } public long getUserId() { return userId; } public String getUsername() { return username; } public static SessionInfos getSessionInfos() { SSession session = getSession(); if (session != null) { return new SessionInfos(session.getUserName(), session.getUserId()); } return new SessionInfos(SessionService.SYSTEM, -1); } public static SSession getSession() { SSession session; try { final long sessionId = getSessionAccessor().getSessionId(); session = getSessionService().getSession(sessionId); } catch (final SessionIdNotSetException | SSessionNotFoundException e) { return null; } return session; } private static SessionService getSessionService() { try { return ServiceAccessorFactory.getInstance().createServiceAccessor().getSessionService(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } private static SessionAccessor getSessionAccessor() { try { return ServiceAccessorFactory.getInstance().createSessionAccessor(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } public static long getUserIdFromSession() { return getSessionService().getLoggedUserFromSession(getSessionAccessor()); } public static String getUserNameFromSession() { SSession session = getSession(); if (session == null) { // system return SessionService.SYSTEM; } return session.getUserName(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/StarterThread.java ================================================ /** * 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.api.impl; import java.util.List; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.platform.model.SPlatform; import org.bonitasoft.engine.tenant.restart.TenantRestartHandler; import org.bonitasoft.engine.transaction.UserTransactionService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Thread start when the engine is ready. * Its purpose is to start elements to be recovered from the previous run of the engine. * * @author Baptiste Mesta */ public class StarterThread extends Thread { private static final Logger logger = LoggerFactory.getLogger(StarterThread.class); private final List tenantRestartHandlers; private final UserTransactionService transactionService; private final PlatformService platformService; public StarterThread(UserTransactionService transactionService, PlatformService platformService, List tenantRestartHandlers) { super("Starter Thread created"); this.tenantRestartHandlers = tenantRestartHandlers; this.transactionService = transactionService; this.platformService = platformService; } @Override public void run() { SPlatform platform = getPlatform(); logger.info("Restarting elements of platform that were not finished at the last shutdown"); if (platform.isMaintenanceEnabled()) { logger.warn("Unable to restart elements of platform because platform is {}", platform.getPausedStatus()); return; } executeHandlers(); } private void executeHandlers() { for (final TenantRestartHandler restartHandler : tenantRestartHandlers) { try { logger.info("Executing Restart Handler {}", restartHandler.getClass().getName()); restartHandler.afterServicesStart(); } catch (Exception e) { logger.error("The Restart Handler {} failed", restartHandler.getClass().getName(), e); } } } SPlatform getPlatform() { try { return transactionService.executeInTransaction(platformService::getPlatform); } catch (Exception e) { throw new RuntimeException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/TaskInvolvementDelegate.java ================================================ /** * 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.api.impl; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; 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.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.persistence.*; import org.bonitasoft.engine.search.AbstractHumanTaskInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; /** * @author Emmanuel Duchastenier */ public class TaskInvolvementDelegate { private static final int BATCH_SIZE = 100; protected ServiceAccessor getServiceAccessor() { return ServiceAccessorSingleton.getInstance(); } public boolean isExecutorOfArchivedTaskOfProcess(long userId, Long rootProcessInstanceId) throws SBonitaReadException { final ActivityInstanceService activityInstanceService = getServiceAccessor().getActivityInstanceService(); QueryOptions archivedQueryOptions = buildArchivedTasksQueryOptions(rootProcessInstanceId); List sArchivedHumanTasks = activityInstanceService .searchArchivedTasks(archivedQueryOptions); while (!sArchivedHumanTasks.isEmpty()) { for (final SAHumanTaskInstance sArchivedHumanTask : sArchivedHumanTasks) { if (userId == sArchivedHumanTask.getExecutedBy()) { return true; } } archivedQueryOptions = QueryOptions.getNextPage(archivedQueryOptions); sArchivedHumanTasks = activityInstanceService.searchArchivedTasks(archivedQueryOptions); } return false; } private static QueryOptions buildArchivedTasksQueryOptions(final long rootProcessInstanceId) { final SAUserTaskInstanceBuilderFactory archUserTaskKeyFactory = BuilderFactory .get(SAUserTaskInstanceBuilderFactory.class); final String humanTaskIdKey = archUserTaskKeyFactory.getIdKey(); final String parentProcessInstanceKey = archUserTaskKeyFactory.getRootProcessInstanceKey(); final List archivedOrderByOptions = Collections .singletonList(new OrderByOption(SAHumanTaskInstance.class, humanTaskIdKey, OrderByType.ASC)); final List archivedFilterOptions = Collections .singletonList( new FilterOption(SAHumanTaskInstance.class, parentProcessInstanceKey, rootProcessInstanceId)); return new QueryOptions(0, BATCH_SIZE, archivedOrderByOptions, archivedFilterOptions, null); } public boolean isInvolvedInHumanTaskInstance(long userId, long humanTaskInstanceId) throws ActivityInstanceNotFoundException { final ActivityInstanceService activityInstanceService = getServiceAccessor().getActivityInstanceService(); try { long assigneeId; final SHumanTaskInstance humanTaskInstance = activityInstanceService .getHumanTaskInstance(humanTaskInstanceId); assigneeId = humanTaskInstance.getAssigneeId(); if (assigneeId > 0) { //check if the user is the assigned user return userId == assigneeId; } else { //if the task is not assigned check if the user is mapped to the actor of the task return activityInstanceService.isTaskPendingForUser(humanTaskInstanceId, userId); } } catch (SActivityInstanceNotFoundException e) { throw new ActivityInstanceNotFoundException(humanTaskInstanceId); } catch (SBonitaReadException | SActivityReadException e) { throw new RetrieveException(e); } } public boolean hasUserPendingOrAssignedTasks(long userId, Long processInstanceId) throws SExecutionException { final ActivityInstanceService activityInstanceService = getServiceAccessor().getActivityInstanceService(); // is user assigned or has pending tasks on this process instance: final QueryOptions queryOptions = new QueryOptions(0, 1, Collections.EMPTY_LIST, List.of(new FilterOption(SHumanTaskInstance.class, "logicalGroup2", processInstanceId)), null); try { return activityInstanceService.getNumberOfPendingOrAssignedTasks(userId, queryOptions) > 0; } catch (SBonitaReadException e) { throw new SExecutionException(e); } } public SearchResult searchPendingTasksManagedBy(final long managerUserId, final SearchOptions searchOptions) throws SearchException { final ServiceAccessor serviceAccessor = getServiceAccessor(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final FlowNodeStateManager flowNodeStateManager = serviceAccessor.getFlowNodeStateManager(); return AbstractHumanTaskInstanceSearchEntity.searchHumanTaskInstance( searchEntitiesDescriptor.getSearchHumanTaskInstanceDescriptor(), searchOptions, flowNodeStateManager, (queryOptions) -> activityInstanceService.searchNumberOfPendingTasksManagedBy(managerUserId, queryOptions), (queryOptions) -> activityInstanceService.searchPendingTasksManagedBy(managerUserId, queryOptions)) .search(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/TenantAdministrationAPIImpl.java ================================================ /** * 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.api.impl; import java.io.IOException; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.TenantAdministrationAPI; import org.bonitasoft.engine.api.impl.transaction.CustomTransactions; import org.bonitasoft.engine.business.data.BusinessDataModelRepository; import org.bonitasoft.engine.business.data.BusinessDataRepositoryDeploymentException; import org.bonitasoft.engine.business.data.BusinessDataRepositoryException; import org.bonitasoft.engine.business.data.InvalidBusinessDataModelException; import org.bonitasoft.engine.business.data.SBusinessDataRepositoryDeploymentException; import org.bonitasoft.engine.business.data.SBusinessDataRepositoryException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.exception.BonitaHomeConfigurationException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.resources.STenantResourceLight; import org.bonitasoft.engine.resources.TenantResourcesService; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.tenant.TenantResource; import org.bonitasoft.engine.tenant.TenantResourceType; import org.bonitasoft.engine.tenant.TenantStateManager; /** * @author Matthieu Chaffotte * @author Baptiste Mesta */ @Slf4j public class TenantAdministrationAPIImpl implements TenantAdministrationAPI { protected ServiceAccessor getServiceAccessorNoException() { try { return ServiceAccessorFactory.getInstance().createServiceAccessor(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } @Override @AvailableInMaintenanceMode public boolean isPaused() { try { return getServiceAccessorNoException().getPlatformService().getPlatform().isMaintenanceEnabled(); } catch (final SBonitaException e) { throw new RetrieveException("Unable to retrieve the tenant status", e); } } @Override @AvailableInMaintenanceMode @CustomTransactions public void pause() throws UpdateException { ServiceAccessor serviceAccessor = getServiceAccessorNoException(); try { serviceAccessor.getTenantStateManager().pause(); } catch (Exception e) { throw new UpdateException(e); } } @Override @AvailableInMaintenanceMode @CustomTransactions public void resume() throws UpdateException { ServiceAccessor serviceAccessor = getServiceAccessorNoException(); try { serviceAccessor.getTenantStateManager().resume(); serviceAccessor.getUserTransactionService().executeInTransaction(() -> { resolveDependenciesForAllProcesses(); return null; }); } catch (Exception e) { throw new UpdateException(e); } } private void resolveDependenciesForAllProcesses() { getServiceAccessor().getBusinessArchiveArtifactsManager() .resolveDependenciesForAllProcesses(getServiceAccessor()); } @Override @AvailableInMaintenanceMode public TenantResource getBusinessDataModelResource() { return getTenantResource(TenantResourceType.BDM); } protected TenantResource getTenantResource(TenantResourceType type) { TenantResourcesService tenantResourcesService = getServiceAccessor().getTenantResourcesService(); org.bonitasoft.engine.resources.TenantResourceType resourceType = org.bonitasoft.engine.resources.TenantResourceType .valueOf(type.name()); try { STenantResourceLight tenantResource = tenantResourcesService.getSingleLightResource(resourceType); return ModelConvertor.toTenantResource(tenantResource); } catch (SBonitaReadException e) { return TenantResource.NONE; } } //visible for testing @Override @AvailableInMaintenanceMode public String getBusinessDataModelVersion() throws BusinessDataRepositoryException { try { final BusinessDataModelRepository modelRepository = getServiceAccessor().getBusinessDataModelRepository(); return modelRepository.getInstalledBDMVersion(); } catch (final SBusinessDataRepositoryException e) { throw new BusinessDataRepositoryException(e); } } private String installBusinessDataModel(final byte[] zip) throws InvalidBusinessDataModelException, BusinessDataRepositoryDeploymentException { log.info("Starting the installation of the BDM."); final ServiceAccessor serviceAccessor = getServiceAccessor(); final long userId; try { userId = getUserId(); } catch (IllegalStateException e) { throw new BusinessDataRepositoryDeploymentException("Unable to determine user ID"); } try { final BusinessDataModelRepository bdmRepository = serviceAccessor.getBusinessDataModelRepository(); TenantStateManager tenantStateManager = serviceAccessor.getTenantStateManager(); String bdm_version = tenantStateManager.executeManagementOperation("BDM Installation", () -> bdmRepository.install(zip, userId)); log.info("Installation of the BDM completed."); return bdm_version; } catch (final SBusinessDataRepositoryDeploymentException e) { throw new BusinessDataRepositoryDeploymentException(e); } catch (final InvalidBusinessDataModelException e) { throw e; } catch (final Exception e) { throw new BonitaRuntimeException(e); } } @Override @AvailableInMaintenanceMode(onlyAvailableInMaintenanceMode = true) @WithLock(key = UPDATE_BDM) public void uninstallBusinessDataModel() throws BusinessDataRepositoryDeploymentException { log.info("Uninstalling the currently deployed BDM"); final ServiceAccessor serviceAccessor = getServiceAccessor(); try { final BusinessDataModelRepository bdmRepository = serviceAccessor.getBusinessDataModelRepository(); TenantStateManager tenantStateManager = serviceAccessor.getTenantStateManager(); tenantStateManager.executeManagementOperation("BDM Uninstallation", () -> { bdmRepository.uninstall(); return null; }); log.info("BDM successfully uninstalled"); } catch (final SBusinessDataRepositoryException sbdre) { throw new BusinessDataRepositoryDeploymentException(sbdre); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } @Override @AvailableInMaintenanceMode(onlyAvailableInMaintenanceMode = true) @WithLock(key = UPDATE_BDM) @Deprecated(since = "9.0.0") public String updateBusinessDataModel(final byte[] zip) throws BusinessDataRepositoryDeploymentException, InvalidBusinessDataModelException { String bdmVersion; try { uninstallBusinessDataModel(); bdmVersion = installBusinessDataModel(zip); } catch (Exception e) { log.warn( "Caught an error when installing/updating the BDM, the transaction will be reverted and the previous BDM restored."); throw e; } log.info("Update operation completed, the BDM was successfully updated"); return bdmVersion; } @Override @AvailableInMaintenanceMode(onlyAvailableInMaintenanceMode = true) @WithLock(key = UPDATE_BDM) public void cleanAndUninstallBusinessDataModel() throws BusinessDataRepositoryDeploymentException { final ServiceAccessor serviceAccessor = getServiceAccessor(); try { final BusinessDataModelRepository bdmRepository = serviceAccessor.getBusinessDataModelRepository(); TenantStateManager tenantStateManager = serviceAccessor.getTenantStateManager(); tenantStateManager.executeManagementOperation("BDM Cleanup and uninstallation", () -> { bdmRepository.dropAndUninstall(); return null; }); } catch (final SBusinessDataRepositoryException sbdre) { throw new BusinessDataRepositoryDeploymentException(sbdre); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } @Override @AvailableInMaintenanceMode public byte[] getClientBDMZip() throws BusinessDataRepositoryException { final BusinessDataModelRepository bdmRepository = getServiceAccessor().getBusinessDataModelRepository(); try { return bdmRepository.getClientBDMZip(); } catch (final SBusinessDataRepositoryException e) { throw new BusinessDataRepositoryException(e); } } protected ServiceAccessor getServiceAccessor() { try { return ServiceAccessorFactory.getInstance().createServiceAccessor(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } private SessionAccessor getSessionAccessor() throws IOException, BonitaHomeConfigurationException, BonitaHomeNotSetException, ReflectiveOperationException { return ServiceAccessorFactory.getInstance().createSessionAccessor(); } protected long getUserId() throws IllegalStateException { try { return getServiceAccessor().getSessionService().getSession(getSessionAccessor().getSessionId()).getUserId(); } catch (final Exception e) { throw new BonitaRuntimeException(e.getMessage()); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/WithLock.java ================================================ /** * Copyright (C) 2023 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.api.impl; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Used to identify tenant-level API methods that require taking a functional * lock when they are called. When the lock with this key is already taken by * another call, the API method will fail immediately. * Used by the Bonita Engine server interceptor. */ @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface WithLock { /** A unique key to identify the lock (non-null) */ String key(); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/ApplicationArchive.java ================================================ /** * 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.api.impl.application.installer; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.ArrayList; import java.util.List; import java.util.Optional; import com.vdurmont.semver4j.Semver; import lombok.*; import lombok.extern.slf4j.Slf4j; @Slf4j @Data @NoArgsConstructor @AllArgsConstructor public class ApplicationArchive implements AutoCloseable { private String fileName; private File organization; private File bdm; private List processes = new ArrayList<>(); private List restAPIExtensions = new ArrayList<>(); private List pages = new ArrayList<>(); private List layouts = new ArrayList<>(); private List themes = new ArrayList<>(); private List applications = new ArrayList<>(); private List applicationIcons = new ArrayList<>(); private List ignoredFiles = new ArrayList<>(); private File configurationFile; private String version; @Setter(AccessLevel.NONE) @Getter(AccessLevel.NONE) private Semver semver; public ApplicationArchive addPage(File page) { pages.add(page); return this; } public ApplicationArchive addLayout(File layout) { layouts.add(layout); return this; } public ApplicationArchive addTheme(File theme) { themes.add(theme); return this; } public ApplicationArchive addRestAPIExtension(File extension) { restAPIExtensions.add(extension); return this; } public ApplicationArchive addApplication(File livingApplication) { applications.add(livingApplication); return this; } public ApplicationArchive addApplicationIcon(File icon) { applicationIcons.add(icon); return this; } public ApplicationArchive addProcess(File process) { processes.add(process); return this; } public ApplicationArchive addIgnoredFile(File file) { ignoredFiles.add(file); return this; } public ApplicationArchive setBdm(File bdm) { this.bdm = bdm; return this; } public Optional getConfigurationFile() { return Optional.ofNullable(configurationFile); } public void setVersion(String version) { this.version = version; if (version != null) { this.semver = new Semver(version, Semver.SemverType.LOOSE); } } public boolean hasVersionGreaterThan(String version) { if (semver == null) { return false; } return semver.isGreaterThan(version); } public boolean hasVersionEquivalentTo(String version) { if (semver == null) { return false; } return semver.isEquivalentTo(version); } public boolean hasVersion() { return semver != null; } /** * @return true if the application archive has no artifact */ public boolean isEmpty() { return organization == null && bdm == null && processes.isEmpty() && restAPIExtensions.isEmpty() && pages.isEmpty() && layouts.isEmpty() && themes.isEmpty() && applications.isEmpty(); } @Override public void close() throws Exception { cleanPhysicalArtifacts(); } protected void cleanPhysicalArtifacts() throws IOException { if (organization != null) { Files.deleteIfExists(organization.toPath()); } if (bdm != null) { Files.deleteIfExists(bdm.toPath()); } deletePhysicalFilesFromList(processes); deletePhysicalFilesFromList(restAPIExtensions); deletePhysicalFilesFromList(pages); deletePhysicalFilesFromList(layouts); deletePhysicalFilesFromList(themes); deletePhysicalFilesFromList(applications); deletePhysicalFilesFromList(applicationIcons); deletePhysicalFilesFromList(ignoredFiles); } protected void deletePhysicalFilesFromList(List list) throws IOException { for (File f : list) { Files.deleteIfExists(f.toPath()); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/ApplicationArchiveReader.java ================================================ /** * 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.api.impl.application.installer; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.impl.application.installer.detector.ArtifactTypeDetector; import org.bonitasoft.engine.io.IOUtil; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta. */ @Slf4j @Component @ConditionalOnSingleCandidate(ApplicationArchiveReader.class) public class ApplicationArchiveReader { private final ArtifactTypeDetector artifactTypeDetector; public ApplicationArchiveReader(ArtifactTypeDetector artifactTypeDetector) { this.artifactTypeDetector = artifactTypeDetector; } public ApplicationArchive read(byte[] applicationArchiveFile) throws IOException { try (InputStream inputStream = new ByteArrayInputStream(applicationArchiveFile)) { return read(inputStream); } } public ApplicationArchive read(InputStream inputStream) throws IOException { final ApplicationArchive applicationArchive = createNewApplicationArchive(); File destDir = IOUtil.createTempDirectory(Files.createTempDirectory("temp-custom-application").toUri()); IOUtil.unzipToFolder(inputStream, destDir); try (Stream walker = Files.walk(destDir.toPath())) { walker.filter(p -> p.toFile().isFile()).forEach(path -> { try { artifactTypeDetector.checkFileAndAddToArchive(path.toFile(), applicationArchive); } catch (IOException e) { throw new RuntimeException(e); } }); } return applicationArchive; } protected ApplicationArchive createNewApplicationArchive() { return new ApplicationArchive(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/ApplicationInstaller.java ================================================ /** * 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.api.impl.application.installer; import java.io.File; import org.bonitasoft.engine.api.result.ExecutionResult; import org.bonitasoft.engine.exception.ApplicationInstallationException; public interface ApplicationInstaller { void install(ApplicationArchive applicationArchive) throws ApplicationInstallationException; void update(ApplicationArchive applicationArchive) throws ApplicationInstallationException; void updateConfiguration(File configurationFileArchive, ExecutionResult executionResult) throws Exception; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/ApplicationInstallerImpl.java ================================================ /** * 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.api.impl.application.installer; import static java.lang.String.format; import static java.lang.System.lineSeparator; import static java.util.stream.Collectors.joining; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.bonitasoft.engine.api.result.Status.*; import static org.bonitasoft.engine.api.result.StatusCode.*; import static org.bonitasoft.engine.api.result.StatusContext.*; import static org.bonitasoft.engine.bpm.process.ActivationState.DISABLED; import static org.bonitasoft.engine.bpm.process.ConfigurationState.RESOLVED; import static org.bonitasoft.engine.business.application.ApplicationImportPolicy.FAIL_ON_DUPLICATES; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.Serializable; import java.net.URLConnection; import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.concurrent.Callable; import java.util.stream.Collectors; import javax.xml.bind.JAXBException; import lombok.Builder; import lombok.Data; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.ImportError; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.api.impl.ProcessDeploymentAPIDelegate; import org.bonitasoft.engine.api.impl.ProcessManagementAPIImplDelegate; import org.bonitasoft.engine.api.impl.organization.OrganizationAPIDelegate; import org.bonitasoft.engine.api.impl.page.PageAPIDelegate; import org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager; import org.bonitasoft.engine.api.result.ExecutionResult; import org.bonitasoft.engine.api.result.Status; import org.bonitasoft.engine.api.result.StatusCode; import org.bonitasoft.engine.api.utils.VisibleForTesting; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory; import org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessDeployException; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessEnablementException; import org.bonitasoft.engine.business.application.ApplicationImportPolicy; import org.bonitasoft.engine.business.application.exporter.ApplicationNodeContainerConverter; import org.bonitasoft.engine.business.application.importer.ApplicationImporter; import org.bonitasoft.engine.business.application.importer.StrategySelector; import org.bonitasoft.engine.business.application.xml.AbstractApplicationNode; import org.bonitasoft.engine.business.data.BusinessDataModelRepository; import org.bonitasoft.engine.business.data.BusinessDataRepositoryDeploymentException; import org.bonitasoft.engine.business.data.InvalidBusinessDataModelException; import org.bonitasoft.engine.business.data.SBusinessDataRepositoryDeploymentException; import org.bonitasoft.engine.business.data.SBusinessDataRepositoryException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.ApplicationInstallationException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.ImportException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.ImportPolicy; import org.bonitasoft.engine.identity.OrganizationImportException; import org.bonitasoft.engine.io.FileOperations; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.page.PageCreator; import org.bonitasoft.engine.page.PageSearchDescriptor; import org.bonitasoft.engine.page.PageUpdater; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.platform.exception.SPlatformUpdateException; import org.bonitasoft.engine.platform.model.builder.SPlatformUpdateBuilder; import org.bonitasoft.engine.platform.model.builder.impl.SPlatformUpdateBuilderImpl; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.service.InstallationFailedException; import org.bonitasoft.engine.service.InstallationService; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.session.model.SSession; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.tenant.TenantStateManager; import org.bonitasoft.engine.transaction.UserTransactionService; import org.bonitasoft.platform.exception.PlatformException; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; import org.xml.sax.SAXException; /** * Main entry point to deploy an {@link ApplicationArchive}. * * @author Baptiste Mesta. * @author Danila Mazour * @author Haroun El Alami */ @Slf4j @Component @ConditionalOnSingleCandidate(ApplicationInstaller.class) public class ApplicationInstallerImpl implements ApplicationInstaller { private static final String PAGE_TOKEN_PROPERTY = "name"; private static final String PAGE_DISPLAY_NAME_PROPERTY = "displayName"; private static final String PAGE_DESCRIPTION_PROPERTY = "description"; private static final String PAGE_CONTENT_TYPE_PROPERTY = "contentType"; private final InstallationService installationService; private final BusinessDataModelRepository bdmRepository; private final UserTransactionService transactionService; private final TenantStateManager tenantStateManager; private final SessionAccessor sessionAccessor; private final SessionService sessionService; private final BusinessArchiveArtifactsManager businessArchiveArtifactsManager; private final ApplicationImporter applicationImporter; private final ApplicationNodeContainerConverter appXmlConverter = new ApplicationNodeContainerConverter(); public ApplicationInstallerImpl(InstallationService installationService, @Qualifier("businessDataModelRepository") BusinessDataModelRepository bdmRepository, UserTransactionService transactionService, SessionAccessor sessionAccessor, SessionService sessionService, TenantStateManager tenantStateManager, @Qualifier("dependencyResolver") BusinessArchiveArtifactsManager businessArchiveArtifactsManager, ApplicationImporter applicationImporter) { this.installationService = installationService; this.bdmRepository = bdmRepository; this.transactionService = transactionService; this.sessionAccessor = sessionAccessor; this.sessionService = sessionService; this.tenantStateManager = tenantStateManager; this.businessArchiveArtifactsManager = businessArchiveArtifactsManager; this.applicationImporter = applicationImporter; } private PageAPIDelegate getPageAPIDelegate() { return PageAPIDelegate.getInstance(); } private OrganizationAPIDelegate getOrganizationImporter() { return OrganizationAPIDelegate.getInstance(); } @Override public void install(ApplicationArchive applicationArchive) throws ApplicationInstallationException { if (applicationArchive.isEmpty()) { throw new ApplicationInstallationException("The Application Archive contains no valid artifact to install"); } final ExecutionResult executionResult = new ExecutionResult(); try { final long startPoint = System.currentTimeMillis(); log.info("Starting Application Archive installation..."); installBusinessDataModel(applicationArchive); inSession(() -> inTransaction(() -> { var newlyInstalledProcessIds = installArtifacts(applicationArchive, executionResult); enableResolvedProcesses(newlyInstalledProcessIds, executionResult); updateApplicationVersion(applicationArchive.getVersion()); return null; })); log.info("The Application Archive (version {}) has been installed successfully in {} ms.", applicationArchive.getVersion(), (System.currentTimeMillis() - startPoint)); } catch (Exception e) { throw new ApplicationInstallationException("The Application Archive install operation has been aborted", e); } finally { logInstallationResult(executionResult); } if (executionResult.hasErrors()) { throw new ApplicationInstallationException("The Application Archive install operation has been aborted"); } } protected List installArtifacts(ApplicationArchive applicationArchive, ExecutionResult executionResult) throws Exception { installOrganization(applicationArchive, executionResult); installOrUpdateRestApiExtensions(applicationArchive, executionResult); installOrUpdatePages(applicationArchive, executionResult); installOrUpdateLayouts(applicationArchive, executionResult); installOrUpdateThemes(applicationArchive, executionResult); installLivingApplications(applicationArchive, executionResult, FAIL_ON_DUPLICATES); var installedProcessIds = installProcesses(applicationArchive, executionResult); applicationArchive.getConfigurationFile().ifPresent(configFile -> installConfiguration(configFile, executionResult)); return installedProcessIds; } @Override public void update(ApplicationArchive applicationArchive) throws ApplicationInstallationException { if (applicationArchive.isEmpty()) { throw new ApplicationInstallationException("The Application Archive contains no valid artifact to install"); } final ExecutionResult executionResult = new ExecutionResult(); try { final long startPoint = System.currentTimeMillis(); log.info("Starting Application Archive installation..."); installBusinessDataModel(applicationArchive); inSession(() -> inTransaction(() -> { List newlyInstalledProcessIds = updateArtifacts(applicationArchive, executionResult); disableOldProcesses(newlyInstalledProcessIds, executionResult); enableResolvedProcesses(newlyInstalledProcessIds, executionResult); updateApplicationVersion(applicationArchive.getVersion()); return null; })); log.info("The Application Archive has been installed successfully in {} ms.", (System.currentTimeMillis() - startPoint)); } catch (Exception e) { throw new ApplicationInstallationException("The Application Archive update operation has been aborted", e); } finally { logInstallationResult(executionResult); } if (executionResult.hasErrors()) { throw new ApplicationInstallationException("The Application Archive update operation has been aborted"); } } @VisibleForTesting public void resumeTenantInSession() throws Exception { inSession(() -> { try { if (tenantStateManager.isPaused()) { tenantStateManager.resume(); transactionService.executeInTransaction(() -> { businessArchiveArtifactsManager.resolveDependenciesForAllProcesses(getServiceAccessor()); return null; }); } } catch (Exception e) { throw new UpdateException(e); } return null; }); } @VisibleForTesting public void pauseTenantInSession() throws Exception { inSession(() -> { try { if (!tenantStateManager.isPaused()) { tenantStateManager.pause(); } } catch (Exception e) { throw new UpdateException(e); } return null; }); } protected List updateArtifacts(ApplicationArchive applicationArchive, ExecutionResult executionResult) throws Exception { updateOrganization(applicationArchive, executionResult); installOrUpdateRestApiExtensions(applicationArchive, executionResult); installOrUpdatePages(applicationArchive, executionResult); installOrUpdateLayouts(applicationArchive, executionResult); installOrUpdateThemes(applicationArchive, executionResult); installLivingApplications(applicationArchive, executionResult, ApplicationImportPolicy.REPLACE_DUPLICATES); List newlyInstalledProcessIds = installProcesses(applicationArchive, executionResult); applicationArchive.getConfigurationFile().ifPresent(configFile -> installConfiguration(configFile, executionResult)); return newlyInstalledProcessIds; } @VisibleForTesting public void updateApplicationVersion(String version) throws PlatformException { try { PlatformService platformService = getServiceAccessor().getPlatformService(); platformService.updatePlatform(getPlatformUpdateBuilder() .setApplicationVersion(version).done()); } catch (SPlatformUpdateException e) { throw new PlatformException("Error when updating Platform application version", e); } } protected SPlatformUpdateBuilder getPlatformUpdateBuilder() { return SPlatformUpdateBuilderImpl.builder() .descriptor(new EntityUpdateDescriptor()) .build(); } @VisibleForTesting void disableProcess(long processDefinitionId) throws SBonitaException { getProcessManagementAPIDelegate().disableProcess(processDefinitionId); } @VisibleForTesting List getDeployedProcessIds() throws SearchException { SearchOptionsBuilder optsBuilder = new SearchOptionsBuilder(0, Integer.MAX_VALUE); return getProcessDeploymentAPIDelegate() .searchProcessDeploymentInfos(optsBuilder.done()).getResult() .stream().map(ProcessDeploymentInfo::getProcessId).collect(Collectors.toList()); } @VisibleForTesting Optional getDeployedProcessId(String name, String version) { try { return Optional.of(getProcessDeploymentAPIDelegate().getProcessDefinitionId(name, version)); } catch (ProcessDefinitionNotFoundException e) { return Optional.empty(); } } public void disableOldProcesses(List installedProcessIds, ExecutionResult executionResult) throws SearchException, SBonitaException, ProcessDefinitionNotFoundException { List deployedProcessIds = getDeployedProcessIds(); // remove installed process ids deployedProcessIds.removeAll(installedProcessIds); // disable all processes for (Long processId : deployedProcessIds) { // get process Info ProcessDeploymentInfo info = getProcessDeploymentInfo(processId); if (info.getActivationState() == ActivationState.ENABLED) { disableProcess(processId); executionResult.addStatus(infoStatus(PROCESS_DEPLOYMENT_DISABLEMENT_OK, format("Process %s (%s) has been disabled successfully", info.getDisplayName(), info.getVersion()))); } } } @VisibleForTesting ProcessDeploymentInfo getProcessDeploymentInfo(Long processId) throws ProcessDefinitionNotFoundException { return getProcessDeploymentAPIDelegate().getProcessDeploymentInfo(processId); } public void enableResolvedProcesses(List processDefinitionIds, ExecutionResult executionResult) throws ProcessDeployException { Collection processDeploymentInfos = getProcessDeploymentAPIDelegate() .getProcessDeploymentInfosFromIds(processDefinitionIds) .values(); boolean atLeastOneBlockingProblem = false; // for all deployed process // if resolved and not already enabled, // enable it => if exception, add error status // if enablement ok, add info status Ok // if not resolved, add error status and list resolution problems // At the end, if at least one process disabled, throw Exception to cancel deployment and startup for (ProcessDeploymentInfo info : processDeploymentInfos) { if (info.getConfigurationState() == RESOLVED) { if (info.getActivationState() == DISABLED) { try { getProcessDeploymentAPIDelegate().enableProcess(info.getProcessId()); } catch (ProcessDefinitionNotFoundException | ProcessEnablementException e) { final Map context = new HashMap<>(); context.put(PROCESS_NAME_KEY, info.getName()); context.put(PROCESS_VERSION_KEY, info.getVersion()); executionResult.addStatus(errorStatus(PROCESS_DEPLOYMENT_ENABLEMENT_KO, format("Process %s (%s) could not be enabled", info.getName(), info.getVersion()), context)); atLeastOneBlockingProblem = true; continue; } } executionResult.addStatus(infoStatus(PROCESS_DEPLOYMENT_ENABLEMENT_OK, format("Process %s (%s) has been enabled successfully", info.getDisplayName(), info.getVersion()))); } else { try { atLeastOneBlockingProblem = true; List problems = getProcessDeploymentAPIDelegate() .getProcessResolutionProblems(info.getProcessId()); String message = format( "Process '%s' (%s) is unresolved. It cannot be enabled for now.", info.getDisplayName(), info.getVersion()); String description = message + lineSeparator() + problems.stream().map(Problem::getDescription) .collect(joining(lineSeparator())); executionResult.addStatus(errorStatus(PROCESS_DEPLOYMENT_ENABLEMENT_KO, description)); } catch (ProcessDefinitionNotFoundException e) { executionResult .addStatus(errorStatus(PROCESS_DEPLOYMENT_ENABLEMENT_KO, "Process definition not found")); } } } if (atLeastOneBlockingProblem) { throw new ProcessDeployException("At least one process failed to deploy / enable. Canceling installation."); } } @VisibleForTesting List importOrganization(File organization, ImportPolicy failOnDuplicates) throws OrganizationImportException, IOException { return getOrganizationImporter().importOrganizationWithWarnings( Files.readString(organization.toPath()), failOnDuplicates); } protected void installOrganization(ApplicationArchive applicationArchive, ExecutionResult executionResult) throws Exception { final List warnings; if (applicationArchive.getOrganization() == null) { executionResult.addStatus(Status.infoStatus(ORGANIZATION_IMPORT_WARNING, "No organization found. Use the technical user to configure the organization.")); return; } try { warnings = importOrganization(applicationArchive.getOrganization(), ImportPolicy.FAIL_ON_DUPLICATES); } catch (IOException e) { throw new OrganizationImportException(e); } for (String warning : warnings) { executionResult.addStatus(warningStatus(ORGANIZATION_IMPORT_WARNING, warning)); } } protected void updateOrganization(ApplicationArchive applicationArchive, ExecutionResult executionResult) throws Exception { final List warnings; if (applicationArchive.getOrganization() == null) { log.info("There is no organization file in the archive. Ignoring the organization update step."); return; } try { warnings = importOrganization(applicationArchive.getOrganization(), ImportPolicy.IGNORE_DUPLICATES); } catch (IOException e) { throw new OrganizationImportException(e); } for (String warning : warnings) { executionResult.addStatus(warningStatus(ORGANIZATION_IMPORT_WARNING, warning)); } } protected void installBusinessDataModel(ApplicationArchive applicationArchive) throws Exception { if (applicationArchive.getBdm() != null) { var alreadyDeployed = sameBdmContentDeployed(applicationArchive.getBdm()); if (alreadyDeployed) { log.info("Installed and current BDM are equivalent. No BDM update required."); return; } log.info("BDM must be installed or updated..."); pauseTenantInSession(); final String bdmVersion = inSession( () -> inTransaction(() -> updateBusinessDataModel(applicationArchive))); log.info("BDM successfully installed (version({})", bdmVersion); resumeTenantInSession(); } } boolean sameBdmContentDeployed(File bdmArchive) throws Exception { return inSession(() -> inTransaction(() -> { log.info("Comparing BDM to install with current BDM..."); return bdmRepository .isDeployed(Files.readAllBytes(bdmArchive.toPath())); })); } protected String updateBusinessDataModel(ApplicationArchive applicationArchive) throws InvalidBusinessDataModelException, BusinessDataRepositoryDeploymentException { String bdmVersion; try { uninstallBusinessDataModel(); bdmVersion = installBusinessDataModel(Files.readAllBytes(applicationArchive.getBdm().toPath())); } catch (IOException e) { log.warn("Cannot read the BDM file on disk"); log.warn( "Caught an error when installing/updating the BDM, the transaction will be reverted and the previous BDM restored."); throw new BusinessDataRepositoryDeploymentException(e); } catch (Exception e) { log.warn( "Caught an error when installing/updating the BDM, the transaction will be reverted and the previous BDM restored."); throw e; } log.info("Update operation completed, the BDM was successfully updated"); return bdmVersion; } protected void uninstallBusinessDataModel() throws BusinessDataRepositoryDeploymentException { log.info("Uninstalling the currently deployed BDM"); try { tenantStateManager.executeManagementOperation("BDM Uninstallation", () -> { bdmRepository.uninstall(); return null; }); log.info("BDM successfully uninstalled"); } catch (final SBusinessDataRepositoryException sbdre) { throw new BusinessDataRepositoryDeploymentException(sbdre); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } protected String installBusinessDataModel(final byte[] zip) throws InvalidBusinessDataModelException, BusinessDataRepositoryDeploymentException { log.info("Starting the installation of the BDM."); try { String bdmVersion = tenantStateManager.executeManagementOperation("BDM Installation", () -> bdmRepository.install(zip, SessionService.SYSTEM_ID)); log.info("Installation of the BDM completed."); return bdmVersion; } catch (final SBusinessDataRepositoryDeploymentException e) { throw new BusinessDataRepositoryDeploymentException(e); } catch (final InvalidBusinessDataModelException e) { throw e; } catch (final Exception e) { throw new BonitaRuntimeException(e); } } protected void installLivingApplications(ApplicationArchive applicationArchive, ExecutionResult executionResult, ApplicationImportPolicy policy) throws AlreadyExistsException, ImportException, ApplicationInstallationException { try { boolean atLeastOneBlockingProblem = false; for (File livingApplicationFile : applicationArchive.getApplications()) { log.info("Installing Living Application from file '{}'", livingApplicationFile.getName()); var appContainer = appXmlConverter .unmarshallFromXML(Files.readAllBytes(livingApplicationFile.toPath())); for (var application : appContainer.getAllApplications()) { var status = importApplication(application, getIconContent(application, applicationArchive), policy); final Map context = new HashMap<>(); context.put(LIVING_APPLICATION_TOKEN_KEY, status.getName()); context.put(LIVING_APPLICATION_IMPORT_STATUS_KEY, status.getStatus()); final List errors = status.getErrors(); if (errors != null && !errors.isEmpty()) { errors.forEach(error -> { Status errorStatus = buildErrorStatus(error, livingApplicationFile.getName()); executionResult.addStatus(errorStatus); }); atLeastOneBlockingProblem = true; continue; } executionResult.addStatus( infoStatus(LIVING_APP_DEPLOYMENT, format("Application '%s' has been %s", status.getName(), status.getStatus().name().toLowerCase()), context)); } } if (atLeastOneBlockingProblem) { throw new ApplicationInstallationException( "At least one application failed to be installed. Canceling installation."); } } catch (IOException | JAXBException | SAXException e) { throw new ImportException(e); } } private IconContent getIconContent(AbstractApplicationNode application, ApplicationArchive applicationArchive) { var iconPath = application.getIconPath(); if (iconPath != null && !iconPath.isBlank()) { var icon = applicationArchive.getApplicationIcons().stream() .filter(iconFile -> Objects.equals(iconPath, iconFile.getName())) .findFirst() .map(File::toPath) .orElse(null); try { if (icon != null) { log.info("Application icon {} found for {}", icon.getFileName().toString(), application.getDisplayName()); var bytes = Files.readAllBytes(icon); return IconContent.builder() .bytes(bytes) .mimeType(URLConnection.guessContentTypeFromName(icon.getFileName().toString())) .build(); } } catch (IOException e) { log.warn("Failed to read icon {}", icon, e); } } return IconContent.builder().build(); } private Status buildErrorStatus(ImportError importError, @NonNull String applicationName) { StatusCode code = null; switch (importError.getType()) { case PAGE: code = LIVING_APP_REFERENCES_UNKNOWN_PAGE; break; case PROFILE: code = LIVING_APP_REFERENCES_UNKNOWN_PROFILE; break; case APPLICATION_PAGE: code = LIVING_APP_REFERENCES_UNKNOWN_APPLICATION_PAGE; break; case LAYOUT: code = LIVING_APP_REFERENCES_UNKNOWN_LAYOUT; break; case THEME: code = LIVING_APP_REFERENCES_UNKNOWN_THEME; break; default: break; } final Map context = new HashMap<>(); context.put(LIVING_APPLICATION_TOKEN_KEY, applicationName); context.put(LIVING_APPLICATION_INVALID_ELEMENT_NAME, importError.getName()); context.put(LIVING_APPLICATION_INVALID_ELEMENT_TYPE, importError.getType()); return errorStatus( code, String.format("Unknown %s named '%s'", importError.getType().name(), importError.getName()), context); } ImportStatus importApplication(final AbstractApplicationNode application, IconContent iconContent, ApplicationImportPolicy policy) throws ImportException, AlreadyExistsException { return applicationImporter.importApplication(application, true, SessionService.SYSTEM_ID, iconContent.getBytes(), iconContent.getMimeType(), true, new StrategySelector().selectStrategy(policy)); } protected void installOrUpdatePages(ApplicationArchive applicationArchive, ExecutionResult executionResult) throws IOException, BonitaException { for (File pageFile : applicationArchive.getPages()) { installUnitPage(pageFile, "page", executionResult); } } protected void installOrUpdateLayouts(ApplicationArchive applicationArchive, ExecutionResult executionResult) throws IOException, BonitaException { for (File layoutFile : applicationArchive.getLayouts()) { installUnitPage(layoutFile, "layout", executionResult); } } protected void installOrUpdateThemes(ApplicationArchive applicationArchive, ExecutionResult executionResult) throws IOException, BonitaException { for (File pageFile : applicationArchive.getThemes()) { installUnitPage(pageFile, "theme", executionResult); } } protected void installOrUpdateRestApiExtensions(ApplicationArchive applicationArchive, ExecutionResult executionResult) throws IOException, BonitaException { for (File pageFile : applicationArchive.getRestAPIExtensions()) { installUnitPage(pageFile, "REST API extension", executionResult); } } /** * From the Engine perspective, all custom pages, layouts, themes, custom Rest APIs are of type Page */ protected void installUnitPage(File pageFile, String precisePageType, ExecutionResult executionResult) throws IOException, BonitaException { var pageProperties = loadPageProperties(pageFile); var pageToken = pageProperties.getProperty(PAGE_TOKEN_PROPERTY); final Map context = new HashMap<>(); context.put(PAGE_NAME_KEY, pageToken); Page existingPage = getPageIfExist(pageToken); if (existingPage == null) { final Page page = createPage(pageFile, pageProperties); log.info("Creating new {} '{}'", precisePageType, getPageName(page)); executionResult.addStatus(infoStatus(PAGE_DEPLOYMENT_CREATE_NEW, format("New %s '%s' has been installed", precisePageType, getPageName(page)), context)); } else { updatePageContent(pageFile, existingPage.getId()); } } @VisibleForTesting void updatePageContent(File pageFile, long pageId) throws UpdateException, IOException, AlreadyExistsException { getPageAPIDelegate().updatePageContent(pageId, Files.readAllBytes(pageFile.toPath()), SessionService.SYSTEM_ID); // update content name final PageUpdater pageUpdater = new PageUpdater(); pageUpdater.setContentName(pageFile.getName()); getPageAPIDelegate().updatePage(pageId, pageUpdater, SessionService.SYSTEM_ID); } @VisibleForTesting Page getPageIfExist(String pageToken) throws SearchException { List pageResearch = getPageAPIDelegate().searchPages(new SearchOptionsBuilder(0, 1) .filter(PageSearchDescriptor.NAME, pageToken).done()).getResult(); if (!pageResearch.isEmpty()) { return pageResearch.get(0); } return null; } Page createPage(File pageFile, Properties pageProperties) throws CreationException { try { var pageCreator = new PageCreator(pageProperties.getProperty(PAGE_TOKEN_PROPERTY), pageFile.getName()) .setContentType(pageProperties.getProperty(PAGE_CONTENT_TYPE_PROPERTY)) .setDisplayName(pageProperties.getProperty(PAGE_DISPLAY_NAME_PROPERTY)) .setDescription(pageProperties.getProperty(PAGE_DESCRIPTION_PROPERTY)); return getPageAPIDelegate().createPage(pageCreator, Files.readAllBytes(pageFile.toPath()), SessionService.SYSTEM_ID); } catch (IOException e) { throw new CreationException("Failed to read custom page content", e); } } private String getPageName(Page page) { return isNotBlank(page.getDisplayName()) ? page.getDisplayName() : page.getName(); } private Properties loadPageProperties(File zipFile) throws IOException { var properties = new Properties(); try (var pagePropertiesIs = new ByteArrayInputStream( FileOperations.getFileFromZip(zipFile, "page.properties"))) { properties.load(pagePropertiesIs); return validatePageProperties(zipFile, properties); } } private Properties validatePageProperties(File file, Properties properties) { String name = properties.getProperty(PAGE_TOKEN_PROPERTY); if (name == null || name.isEmpty()) { throw new IllegalArgumentException( format("Invalid page %s, page.properties file do not contain mandatory '%s' attribute", file.getName(), PAGE_TOKEN_PROPERTY)); } String type = properties.getProperty(PAGE_CONTENT_TYPE_PROPERTY); if (type == null || type.isEmpty()) { throw new IllegalArgumentException( format("Invalid page %s, page.properties file do not contain mandatory '%s' attribute", file.getName(), PAGE_CONTENT_TYPE_PROPERTY)); } return properties; } protected List installProcesses(ApplicationArchive applicationArchive, ExecutionResult executionResult) throws InvalidBusinessArchiveFormatException, IOException, ProcessDeployException { List processDefinitionIds = new ArrayList<>(); for (File processFile : applicationArchive.getProcesses()) { try (var is = new FileInputStream(processFile)) { final BusinessArchive businessArchive = BusinessArchiveFactory .readBusinessArchive(is); String name = businessArchive.getProcessDefinition().getName(); String version = businessArchive.getProcessDefinition().getVersion(); final Map context = new HashMap<>(); context.put(PROCESS_NAME_KEY, name); context.put(PROCESS_VERSION_KEY, version); Optional deployedProcessId = getDeployedProcessId(name, version); if (deployedProcessId.isPresent()) { // skip install processDefinitionIds.add(deployedProcessId.get()); executionResult.addStatus(infoStatus(PROCESS_DEPLOYMENT_SKIP_INSTALL, format("Process %s (%s) already exists in the database. Skipping installation.", name, version), context)); } else { processDefinitionIds.add(deployProcess(businessArchive, executionResult)); } } } return processDefinitionIds; } /** * Must be called in a transaction with active session * * @param configurationFileArchive * @param executionResult * @throws ApplicationInstallationException */ void installConfiguration(File configurationFileArchive, ExecutionResult executionResult) throws ApplicationInstallationException { try (var is = Files.newInputStream(configurationFileArchive.toPath())) { log.info("Installing application configuration from file"); installationService.install(null, is.readAllBytes()); executionResult.addStatus(Status.infoStatus(Status.okStatus().getCode(), "Configuration file has been imported")); } catch (IOException | InstallationFailedException e) { throw new ApplicationInstallationException("The Application Archive install operation has been aborted", e); } } /** * Update configuration with the given bconf file * * @param configurationFileArchive A bconf file * @param executionResult * @throws Exception */ @Override public void updateConfiguration(File configurationFileArchive, ExecutionResult executionResult) throws Exception { inSession(() -> inTransaction(() -> { installConfiguration(configurationFileArchive, executionResult); return null; })); } protected Long deployProcess(BusinessArchive businessArchive, ExecutionResult executionResult) throws ProcessDeployException { final String processName = businessArchive.getProcessDefinition().getName(); final String processVersion = businessArchive.getProcessDefinition().getVersion(); long processDefinitionId; final Map context = new HashMap<>(); context.put(PROCESS_NAME_KEY, processName); context.put(PROCESS_VERSION_KEY, processVersion); try { // Let's try to deploy the process, even if it already exists: processDefinitionId = getProcessDeploymentAPIDelegate().deploy(businessArchive).getId(); executionResult.addStatus(infoStatus(PROCESS_DEPLOYMENT_CREATE_NEW, format("New process %s (%s) has been installed successfully", processName, processVersion), context)); } catch (AlreadyExistsException e) { final String message = format("Process %s - %s already exists. Abandoning.", processName, processVersion); log.error(message); throw new ProcessDeployException(message); } return processDefinitionId; } @VisibleForTesting ProcessDeploymentAPIDelegate getProcessDeploymentAPIDelegate() { return ProcessDeploymentAPIDelegate.getInstance(); } @VisibleForTesting ProcessManagementAPIImplDelegate getProcessManagementAPIDelegate() { return ProcessManagementAPIImplDelegate.getInstance(); } @VisibleForTesting public T inSession(Callable callable) throws Exception { final SSession session = sessionService.createSession(SessionService.SYSTEM); final long sessionId = session.getId(); log.trace("New session created with id {}", sessionId); try { sessionAccessor.setSessionId(sessionId); return callable.call(); } finally { sessionAccessor.deleteSessionId(); } } protected T inTransaction(Callable callable) throws ApplicationInstallationException { try { return transactionService.executeInTransaction(callable); } catch (Exception e) { throw new ApplicationInstallationException("Problem installing application", e); } } private ServiceAccessor getServiceAccessor() { return ServiceAccessorSingleton.getInstance(); } void logInstallationResult(ExecutionResult result) { log.info("Result of the installation of the application:"); for (Status s : result.getAllStatus()) { var message = s.getContext() != null && !s.getContext().isEmpty() ? String.format("%s - %s - %s", s.getCode(), s.getMessage(), s.getContext().toString()) : String.format("%s - %s", s.getCode(), s.getMessage()); switch (s.getLevel()) { case ERROR: log.error(message); break; case WARNING: log.warn(message); break; case INFO: case OK: default: log.info(message); break; } } } @Builder @Data static class IconContent { private byte[] bytes; private String mimeType; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/CustomOrDefaultApplicationInstaller.java ================================================ /** * Copyright (C) 2023 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.api.impl.application.installer; import static java.lang.String.format; import java.io.IOException; import java.io.UncheckedIOException; import java.util.Optional; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.result.ExecutionResult; import org.bonitasoft.engine.api.utils.VisibleForTesting; import org.bonitasoft.engine.business.application.importer.DefaultLivingApplicationImporter; import org.bonitasoft.engine.business.application.importer.MandatoryLivingApplicationImporter; import org.bonitasoft.engine.event.PlatformStartedEvent; import org.bonitasoft.engine.exception.ApplicationInstallationException; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.tenant.TenantServicesManager; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.event.EventListener; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.stereotype.Component; /** * Install custom application if one exists under a specific folder. If none, install default provided applications. *
* This installer listens to the event {@link PlatformStartedEvent} to ensure the platform is started before launching * any application installation. */ @Component @Slf4j @RequiredArgsConstructor public class CustomOrDefaultApplicationInstaller { public static final String CUSTOM_APPLICATION_DEFAULT_FOLDER = "my-application"; @Value("${bonita.runtime.custom-application.install-folder:" + CUSTOM_APPLICATION_DEFAULT_FOLDER + "}") @Getter protected String applicationInstallFolder; protected final ApplicationInstallerImpl applicationInstaller; private final DefaultLivingApplicationImporter defaultLivingApplicationImporter; private final MandatoryLivingApplicationImporter mandatoryLivingApplicationImporter; private final TenantServicesManager tenantServicesManager; protected final ResourcePatternResolver cpResourceResolver = new PathMatchingResourcePatternResolver( CustomOrDefaultApplicationInstaller.class.getClassLoader()); private final ApplicationArchiveReader applicationArchiveReader; private final PlatformService platformService; @EventListener public void autoDeployDetectedCustomApplication(PlatformStartedEvent event) throws Exception { final Resource customApplication = detectCustomApplication(); if (customApplication == null) { if (isPlatformFirstInitialization()) { // install default provided applications if custom application does not exist log.info("No custom application detected under folder {}. Continuing with default Bonita startup.", applicationInstallFolder); installDefaultProvidedApplications(); } return; } try (var applicationArchive = createApplicationArchive(customApplication)) { if (!applicationArchive.hasVersion()) { throw new ApplicationInstallationException( "Application version not found. Abort installation."); } log.info("Custom application detected with name '{}' under folder '{}'", customApplication.getFilename(), applicationInstallFolder); if (isPlatformFirstInitialization()) { // install application if it exists and if it is the first init of the platform log.info("Bonita now tries to install it automatically..."); applicationInstaller.install(applicationArchive); } else { var currentVersion = getInstalledApplicationVersion(); log.info("Detected application version: '{}'; Current deployed version: '{}'", applicationArchive.getVersion(), currentVersion); if (applicationArchive.hasVersionGreaterThan(currentVersion)) { log.info("Updating the application..."); applicationInstaller.update(applicationArchive); } else if (applicationArchive.hasVersionEquivalentTo(currentVersion)) { log.info("Updating process configuration only..."); findAndUpdateConfiguration(); } else { throw new ApplicationInstallationException("An application has been detected, but its newVersion " + applicationArchive.getVersion() + " is inferior to the one deployed: " + currentVersion + ". Nothing will be updated, and the Bonita engine startup has been aborted."); } } } } String getInstalledApplicationVersion() throws Exception { return applicationInstaller.inSession( () -> applicationInstaller.inTransaction(() -> platformService.getPlatform().getApplicationVersion())); } boolean isPlatformFirstInitialization() { return mandatoryLivingApplicationImporter.isFirstRun(); } /** * @return custom application resource if one is found, null if none * @throws IOException if a resource cannot be resolved * @throws ApplicationInstallationException if more than one application is detected */ @VisibleForTesting Resource detectCustomApplication() throws IOException, ApplicationInstallationException { log.debug("Trying to detect custom application (.zip file from folder {})", applicationInstallFolder); return getResourceFromClasspath(getCustomAppResourcesFromClasspath(), "application zip"); } protected static Resource getResourceFromClasspath(Resource[] resources, String type) throws IOException, ApplicationInstallationException { // loop over resources to find an existing, readable and not empty resource Resource customRsource = null; var nbZipApplication = 0; if (resources != null) { for (Resource resource : resources) { if (resource.exists() && resource.isReadable() && resource.contentLength() > 0) { nbZipApplication++; customRsource = resource; } else { log.warn("A custom resource file '{}' is found but it cannot be read. It will be ignored.", resource.getFilename()); } } // if more than one application detected, stop execution by raising an exception if (nbZipApplication > 1) { throw new ApplicationInstallationException( format("More than one resource of type %s detected. Abort startup.", type)); } } return customRsource; } @VisibleForTesting Resource[] getCustomAppResourcesFromClasspath() throws IOException { return cpResourceResolver .getResources( ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "/" + applicationInstallFolder + "/*.zip"); } private void setConfigurationFile(ApplicationArchive applicationArchive) throws IOException { detectConfigurationFile().ifPresent(resource -> { try { log.debug("Found application configuration file " + resource.getFilename()); applicationArchive.setConfigurationFile(resource.getFile()); } catch (IOException e) { throw new UncheckedIOException(e); } }); } protected ApplicationArchive createApplicationArchive(Resource customApplication) { try (var applicationZipFileStream = customApplication.getInputStream()) { var applicationArchive = applicationArchiveReader.read(applicationZipFileStream); setConfigurationFile(applicationArchive); return applicationArchive; } catch (IOException e) { throw new ApplicationInstallationException( String.format("Unable to read the %s application archive.", customApplication.getFilename()), e); } } @VisibleForTesting void installDefaultProvidedApplications() throws ApplicationInstallationException { try { // default app importer requires a tenant session and to be executed inside a transaction tenantServicesManager.inSessionTransaction(() -> { defaultLivingApplicationImporter.execute(); return null; }); } catch (Exception e) { throw new ApplicationInstallationException("Unable to import default living applications", e); } } protected Optional detectConfigurationFile() throws IOException { log.debug("Trying to detect configuration file (.bconf file from folder {})", applicationInstallFolder); return Optional.ofNullable( getResourceFromClasspath(getConfigurationFileResourcesFromClasspath(), "configuration file .bconf")); } @VisibleForTesting Resource[] getConfigurationFileResourcesFromClasspath() throws IOException { return cpResourceResolver .getResources( ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "/" + applicationInstallFolder + "/*.bconf"); } protected void findAndUpdateConfiguration() throws ApplicationInstallationException, IOException { final ExecutionResult executionResult = new ExecutionResult(); detectConfigurationFile().ifPresent(resource -> { try { log.debug("Found application configuration file " + resource.getFilename()); applicationInstaller.updateConfiguration(resource.getFile(), executionResult); } catch (Exception e) { throw new ApplicationInstallationException("Failed to update configuration.", e); } }); applicationInstaller.logInstallationResult(executionResult); if (executionResult.hasErrors()) { throw new ApplicationInstallationException("The Application Archive install operation has been aborted"); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/ArtifactDetector.java ================================================ /** * Copyright (C) 2023 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.api.impl.application.installer.detector; import java.io.File; import java.io.IOException; public interface ArtifactDetector { boolean isCompliant(File file) throws IOException; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/ArtifactTypeDetector.java ================================================ /** * 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.api.impl.application.installer.detector; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.Objects; import java.util.Properties; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.impl.application.installer.ApplicationArchive; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; @Component @ConditionalOnSingleCandidate(ArtifactTypeDetector.class) @Slf4j public class ArtifactTypeDetector { private static final String VERSION_PROPERTY = "version"; private static final String APPLICATION_PROPERTIES_FILENAME = "application.properties"; private static final String REST_API_EXTENSION_CONTENT_TYPE = "apiExtension"; private final BdmDetector bdmDetector; private final LivingApplicationDetector livingApplicationDetector; private final OrganizationDetector organizationDetector; private final CustomPageDetector customPageDetector; private final ProcessDetector processDetector; private final ThemeDetector themeDetector; private final PageAndFormDetector pageAndFormDetector; private final LayoutDetector layoutDetector; private final IconDetector iconDetector; public ArtifactTypeDetector(BdmDetector bdmDetector, LivingApplicationDetector livingApplicationDetector, OrganizationDetector organizationDetector, CustomPageDetector customPageDetector, ProcessDetector processDetector, ThemeDetector themeDetector, PageAndFormDetector pageAndFormDetector, LayoutDetector layoutDetector, IconDetector iconDetector) { this.bdmDetector = bdmDetector; this.livingApplicationDetector = livingApplicationDetector; this.organizationDetector = organizationDetector; this.customPageDetector = customPageDetector; this.processDetector = processDetector; this.themeDetector = themeDetector; this.pageAndFormDetector = pageAndFormDetector; this.layoutDetector = layoutDetector; this.iconDetector = iconDetector; } public boolean isApplication(File file) throws IOException { return livingApplicationDetector.isCompliant(file); } public boolean isApplicationIcon(File file) throws IOException { return iconDetector.isCompliant(file); } public boolean isOrganization(File file) throws IOException { return organizationDetector.isCompliant(file); } public boolean isRestApiExtension(File file) throws IOException { return customPageDetector.isCompliant(file, REST_API_EXTENSION_CONTENT_TYPE); } public boolean isPage(File file) throws IOException { return pageAndFormDetector.isCompliant(file); } public boolean isLayout(File file) throws IOException { return layoutDetector.isCompliant(file); } public boolean isTheme(File file) throws IOException { return themeDetector.isCompliant(file); } public boolean isBdm(File file) { return bdmDetector.isCompliant(file); } public boolean isProcess(File file) throws IOException { return processDetector.isCompliant(file); } private boolean isApplicationProperties(File file) { return file.isFile() && Objects.equals(APPLICATION_PROPERTIES_FILENAME, file.getName()); } public void checkFileAndAddToArchive(File file, ApplicationArchive applicationArchive) throws IOException { if (isApplication(file)) { log.debug("Found application file: '{}'. ", file.getName()); applicationArchive.addApplication(file); } else if (isApplicationIcon(file)) { log.debug("Found icon file: '{}'. ", file.getName()); applicationArchive.addApplicationIcon(file); } else if (isProcess(file)) { log.debug("Found process file: '{}'. ", file.getName()); applicationArchive.addProcess(file); } else if (isOrganization(file)) { log.debug("Found organization file: '{}'. ", file.getName()); if (applicationArchive.getOrganization() != null) { log.warn("An organization file has already been set. Using {}", applicationArchive.getOrganization()); ignoreFile(file, applicationArchive); } else { applicationArchive.setOrganization(file); } } else if (isPage(file)) { log.debug("Found page file: '{}'. ", file.getName()); applicationArchive.addPage(file); } else if (isLayout(file)) { log.debug("Found layout file: '{}'. ", file.getName()); applicationArchive.addLayout(file); } else if (isTheme(file)) { log.debug("Found theme file: '{}'. ", file.getName()); applicationArchive.addTheme(file); } else if (isRestApiExtension(file)) { log.debug("Found rest api extension file: '{}'. ", file.getName()); applicationArchive.addRestAPIExtension(file); } else if (isBdm(file)) { log.debug("Found business data model file: '{}'. ", file.getName()); applicationArchive.setBdm(file); } else if (isApplicationProperties(file)) { applicationArchive.setVersion(readVersion(file)); } else { ignoreFile(file, applicationArchive); } } String readVersion(File applicationPropertiesFile) throws IOException { try (var is = Files.newInputStream(applicationPropertiesFile.toPath())) { var properties = new Properties(); properties.load(is); return properties.getProperty(VERSION_PROPERTY, null); } } private void ignoreFile(File file, ApplicationArchive applicationArchive) { log.debug("Ignoring file '{}'.", file.getName()); applicationArchive.addIgnoredFile(file); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/BdmDetector.java ================================================ /** * Copyright (C) 2018 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.api.impl.application.installer.detector; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.File; import java.io.IOException; import org.bonitasoft.engine.io.FileOperations; import org.springframework.stereotype.Component; @Component public class BdmDetector implements ArtifactDetector { @Override public boolean isCompliant(File file) { byte[] bdm; try { bdm = FileOperations.getFileFromZip(file, "bom.xml"); } catch (IOException e) { return false; } // The bdm doesn't contain its namespace, so we can't perform the usual xml validation. // So we look for the tag 'businessObjectModel', better than nothing: return new String(bdm, UTF_8).contains(" properties.getProperty(CONTENT_TYPE_PROPERTY)) .filter(Objects::nonNull) .filter(type -> Arrays.asList(contentTypes).contains(type)) .isPresent(); } private Optional getPageProperties(File file) { try { byte[] fileFromZip = FileOperations.getFileFromZip(file, PAGE_PROPERTIES_FILE); Properties properties = new Properties(); properties.load(new ByteArrayInputStream(fileFromZip)); return Optional.of(properties); } catch (IOException e) { return Optional.empty(); } } public boolean isFilePresent(File file, String filename) { try { FileOperations.getFileFromZip(file, filename); return true; } catch (IOException e) { return false; } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/IconDetector.java ================================================ /** * 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.api.impl.application.installer.detector; import java.io.File; import java.io.IOException; import java.net.URLConnection; import org.springframework.stereotype.Component; @Component public class IconDetector implements ArtifactDetector { /** * Determine the compliance using the guessed content-type of a given file name. */ @Override public boolean isCompliant(File file) throws IOException { if (file.isFile()) { var contentType = URLConnection.guessContentTypeFromName(file.getName()); return contentType != null && contentType.startsWith("image/"); } return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/LayoutDetector.java ================================================ /** * 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.api.impl.application.installer.detector; import java.io.File; import java.io.IOException; import org.springframework.stereotype.Component; /** * @author Emmanuel Duchastenier */ @Component public class LayoutDetector extends CustomPageDetector { private static final String INDEX_GROOVY = "resources/Index.groovy"; private static final String INDEX_HTML = "resources/index.html"; private static final String LAYOUT_CONTENT_TYPE = "layout"; public boolean isCompliant(File file) throws IOException { return super.isCompliant(file, LAYOUT_CONTENT_TYPE) && (isFilePresent(file, INDEX_HTML) || isFilePresent(file, INDEX_GROOVY)); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/LivingApplicationDetector.java ================================================ /** * 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.api.impl.application.installer.detector; import org.springframework.stereotype.Component; @Component public class LivingApplicationDetector extends XmlDetector implements ArtifactDetector { private static final String APPLICATION_NAMESPACE = "http://documentation.bonitasoft.com/application-xml-schema/1.1"; public LivingApplicationDetector() { super(APPLICATION_NAMESPACE); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/OrganizationDetector.java ================================================ /** * Copyright (C) 2023 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.api.impl.application.installer.detector; import org.springframework.stereotype.Component; @Component public class OrganizationDetector extends XmlDetector implements ArtifactDetector { private static final String ORGANIZATION_NAMESPACE = "http://documentation.bonitasoft.com/organization-xml-schema"; public OrganizationDetector() { super(ORGANIZATION_NAMESPACE); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/PageAndFormDetector.java ================================================ /** * 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.api.impl.application.installer.detector; import java.io.File; import java.io.IOException; import org.springframework.stereotype.Component; /** * @author Emmanuel Duchastenier */ @Component public class PageAndFormDetector extends CustomPageDetector { private static final String PAGE_CONTENT_TYPE = "page"; private static final String FORM_CONTENT_TYPE = "form"; private static final String INDEX_GROOVY = "resources/Index.groovy"; private static final String INDEX_HTML = "resources/index.html"; public boolean isCompliant(File file) throws IOException { return super.isCompliant(file, PAGE_CONTENT_TYPE, FORM_CONTENT_TYPE) && (isFilePresent(file, INDEX_HTML) || isFilePresent(file, INDEX_GROOVY)); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/ProcessDetector.java ================================================ /** * 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.api.impl.application.installer.detector; import static org.bonitasoft.engine.io.FileOperations.isBarFile; import java.io.File; import java.io.IOException; import org.bonitasoft.engine.io.FileOperations; import org.springframework.stereotype.Component; @Component public class ProcessDetector extends XmlDetector { private static final String PROCESS_DEFINITION_NAMESPACE = "http://www.bonitasoft.org/ns/process/client/"; private static final String PROCESS_DESIGN_DEFINITION = "process-design.xml"; public ProcessDetector() { super(PROCESS_DEFINITION_NAMESPACE); } public boolean isCompliant(File file) { if (isBarFile(file.getName())) { try { return super.isCompliant(FileOperations.getFileFromZip(file, PROCESS_DESIGN_DEFINITION)); } catch (IOException ignored) { return false; } } return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/ThemeDetector.java ================================================ /** * 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.api.impl.application.installer.detector; import java.io.File; import java.io.IOException; import org.springframework.stereotype.Component; /** * @author Emmanuel Duchastenier */ @Component public class ThemeDetector extends CustomPageDetector { private static final String THEME_CSS = "resources/theme.css"; private static final String THEME_CONTENT_TYPE = "theme"; public boolean isCompliant(File file) throws IOException { return super.isCompliant(file, THEME_CONTENT_TYPE) && isFilePresent(file, THEME_CSS); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/application/installer/detector/XmlDetector.java ================================================ /** * 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.api.impl.application.installer.detector; import static org.bonitasoft.engine.io.FileOperations.isXmlFile; import java.io.*; import java.nio.file.Files; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Element; import org.xml.sax.SAXException; public abstract class XmlDetector implements ArtifactDetector { protected DocumentBuilder documentBuilder; private final String namespace; protected XmlDetector(String namespace) { this.namespace = namespace; initDocumentBuilder(); } private void initDocumentBuilder() { try { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); try { documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // security-compliant documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // security-compliant } catch (IllegalArgumentException e) { //ignored, if not supported by the implementation } documentBuilderFactory.setNamespaceAware(true); this.documentBuilder = documentBuilderFactory.newDocumentBuilder(); } catch (ParserConfigurationException e) { // should never occur as we use a simple configuration which can always be honored throw new IllegalStateException("Unable to create a document builder during XmlValidator creation", e); } } @Override public boolean isCompliant(File file) throws IOException { if (isXmlFile(file.getName())) { return isCompliant(Files.readAllBytes(file.toPath())); } return false; } protected boolean isCompliant(byte[] fileContentBytes) { try (InputStream is = new ByteArrayInputStream(fileContentBytes)) { final Element documentElement = documentBuilder.parse(is).getDocumentElement(); // it should be an equals, // but it seems that some organization files have a /1.1 at the end and some other no ... // same for process definition as version depends on Bonita version return documentElement.getNamespaceURI() != null && documentElement.getNamespaceURI().contains(namespace); } catch (SAXException | IOException e) { return false; } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/connector/ConnectorResetStrategy.java ================================================ /** * 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.api.impl.connector; import org.bonitasoft.engine.bpm.flownode.ActivityExecutionException; /** * @author Elias Ricken de Medeiros */ public interface ConnectorResetStrategy { public void resetConnectorsOf(long flowNodeInstanceId) throws ActivityExecutionException; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/connector/ConnectorReseter.java ================================================ /** * 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.api.impl.connector; import org.bonitasoft.engine.bpm.connector.ConnectorStateReset; import org.bonitasoft.engine.bpm.flownode.ActivityExecutionException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo; /** * @author Elias Ricken de Medeiros */ public class ConnectorReseter { private ConnectorInstanceService connectorInstanceService; public ConnectorReseter(final ConnectorInstanceService connectorInstanceService) { this.connectorInstanceService = connectorInstanceService; } /** * Reset the {@link org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo} to the * given state * * @param connectorInstanceWithFailure {@code SConnectorInstance} identifier * @param state the new {@code SConnectorInstance} state * @throws ActivityExecutionException */ public void resetState(SConnectorInstanceWithFailureInfo connectorInstanceWithFailure, ConnectorStateReset state) throws ActivityExecutionException { try { // set state connectorInstanceService.setState(connectorInstanceWithFailure, state.name()); // clean stack trace if necessary if (connectorInstanceWithFailure.getStackTrace() != null) { connectorInstanceService.setConnectorInstanceFailureException(connectorInstanceWithFailure, null); } } catch (SBonitaException e) { throw new ActivityExecutionException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/connector/ResetAllFailedConnectorStrategy.java ================================================ /** * 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.api.impl.connector; import java.util.List; import org.bonitasoft.engine.bpm.connector.ConnectorState; import org.bonitasoft.engine.bpm.connector.ConnectorStateReset; import org.bonitasoft.engine.bpm.flownode.ActivityExecutionException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo; /** * @author Elias Ricken de Medeiros */ public class ResetAllFailedConnectorStrategy implements ConnectorResetStrategy { private final ConnectorInstanceService connectorInstanceService; private final ConnectorReseter connectorReseter; private final int maxResults; public ResetAllFailedConnectorStrategy(ConnectorInstanceService connectorInstanceService, ConnectorReseter connectorReseter, int maxResults) { this.connectorInstanceService = connectorInstanceService; this.connectorReseter = connectorReseter; this.maxResults = maxResults; } /** * Reset all {@link org.bonitasoft.engine.core.process.instance.model.SConnectorInstance}s related to the given * activity that are in the failed state * to the state {@link org.bonitasoft.engine.bpm.connector.ConnectorStateReset#TO_RE_EXECUTE} * * @param flowNodeInstanceId the identifier of the * {@link org.bonitasoft.engine.core.process.instance.model.SActivityInstance} where the connectors are * attached to. */ @Override public void resetConnectorsOf(final long flowNodeInstanceId) throws ActivityExecutionException { try { int startIndex = 0; List failedConnectors; do { failedConnectors = connectorInstanceService.getConnectorInstancesWithFailureInfo(flowNodeInstanceId, SConnectorInstance.FLOWNODE_TYPE, ConnectorState.FAILED.name(), startIndex, maxResults); resetCurrentPage(failedConnectors); startIndex += maxResults; } while (failedConnectors.size() == maxResults); } catch (SBonitaException e) { throw new ActivityExecutionException(e); } } private void resetCurrentPage(final List failedConnectors) throws ActivityExecutionException { for (SConnectorInstanceWithFailureInfo failedConnector : failedConnectors) { connectorReseter.resetState(failedConnector, ConnectorStateReset.TO_RE_EXECUTE); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/converter/ApplicationMenuModelConverter.java ================================================ /** * 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.api.impl.converter; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.business.application.ApplicationMenu; import org.bonitasoft.engine.business.application.ApplicationMenuCreator; import org.bonitasoft.engine.business.application.ApplicationMenuField; import org.bonitasoft.engine.business.application.ApplicationMenuUpdater; import org.bonitasoft.engine.business.application.impl.ApplicationMenuImpl; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.business.application.model.builder.SApplicationMenuUpdateBuilder; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Elias Ricken de Medeiros */ public class ApplicationMenuModelConverter { public SApplicationMenu buildSApplicationMenu(final ApplicationMenuCreator creator, int menuIndex) { final Map fields = creator.getFields(); final String displayName = (String) fields.get(ApplicationMenuField.DISPLAY_NAME); final Long applicationId = (Long) fields.get(ApplicationMenuField.APPLICATION_ID); final Long applicationPageId = (Long) fields.get(ApplicationMenuField.APPLICATION_PAGE_ID); final Long parentId = (Long) fields.get(ApplicationMenuField.PARENT_ID); final SApplicationMenu.SApplicationMenuBuilder builder = SApplicationMenu.builder().displayName(displayName) .applicationId(applicationId).applicationPageId(applicationPageId).index(menuIndex); if (parentId != null) { builder.parentId(parentId); } return builder.build(); } public ApplicationMenu toApplicationMenu(final SApplicationMenu sApplicationMenu) { final ApplicationMenuImpl menu = new ApplicationMenuImpl(sApplicationMenu.getDisplayName(), sApplicationMenu.getApplicationId(), sApplicationMenu.getApplicationPageId(), sApplicationMenu.getIndex()); menu.setId(sApplicationMenu.getId()); menu.setParentId(sApplicationMenu.getParentId()); return menu; } public List toApplicationMenu(final List sApplicationMenus) throws SBonitaException { final List menus = new ArrayList(sApplicationMenus.size()); for (final SApplicationMenu sMenu : sApplicationMenus) { menus.add(toApplicationMenu(sMenu)); } return menus; } public EntityUpdateDescriptor toApplicationMenuUpdateDescriptor(final ApplicationMenuUpdater updater) { SApplicationMenuUpdateBuilder builder = new SApplicationMenuUpdateBuilder(); for (Map.Entry entry : updater.getFields().entrySet()) { switch (entry.getKey()) { case APPLICATION_PAGE_ID: builder.updateApplicationPageId((Long) entry.getValue()); break; case DISPLAY_NAME: builder.updateDisplayName((String) entry.getValue()); break; case INDEX: builder.updateIndex((Integer) entry.getValue()); break; case PARENT_ID: builder.updateParentId((Long) entry.getValue()); break; } } return builder.done(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/converter/ApplicationModelConverter.java ================================================ /** * 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.api.impl.converter; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Map.Entry; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.business.application.*; import org.bonitasoft.engine.business.application.impl.*; import org.bonitasoft.engine.business.application.model.AbstractSApplication; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationState; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; import org.bonitasoft.engine.business.application.model.builder.SApplicationUpdateBuilder; import org.bonitasoft.engine.commons.io.IOUtil; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Elias Ricken de Medeiros */ @Slf4j public class ApplicationModelConverter { private final PageService pageService; public ApplicationModelConverter(PageService pageService) { this.pageService = pageService; } public SApplicationWithIcon buildSApplication(final AbstractApplicationCreator creator, final long creatorUserId) throws CreationException { final Map fields = creator.getFields(); final String description = (String) fields.get(ApplicationField.DESCRIPTION); final String iconPath = (String) fields.get(ApplicationField.ICON_PATH); final Long profileId = (Long) fields.get(ApplicationField.PROFILE_ID); final long now = System.currentTimeMillis(); final boolean isLink = creator.isLink(); SApplicationWithIcon application = new SApplicationWithIcon(); application.setLink(isLink); application.setToken((String) fields.get(ApplicationField.TOKEN)); application.setDisplayName((String) fields.get(ApplicationField.DISPLAY_NAME)); application.setVersion((String) fields.get(ApplicationField.VERSION)); application.setCreationDate(now); application.setLastUpdateDate(now); application.setCreatedBy(creatorUserId); application.setState(SApplicationState.ACTIVATED.name()); if (creator instanceof ApplicationCreator) { application.setLayoutId(getLayoutId((ApplicationCreator) creator)); application.setThemeId(getThemeId((ApplicationCreator) creator)); } application.setUpdatedBy(creatorUserId); byte[] iconContent = (byte[]) fields.get(ApplicationField.ICON_CONTENT); String iconFileName = (String) fields.get(ApplicationField.ICON_FILE_NAME); if (iconContent != null && iconContent.length > 0) { if (iconFileName != null && !iconFileName.isBlank()) { application.setIconContent(iconContent); application.setIconMimeType(IOUtil.getContentTypeForIcon(iconFileName)); } else { log.warn("Constructing application {} with {} set but without {} set. Icon is ignored.", application.getToken(), ApplicationField.ICON_CONTENT, ApplicationField.ICON_FILE_NAME); } } application.setDescription(description); application.setIconPath(iconPath); application.setProfileId(profileId); return application; } protected Long getLayoutId(final ApplicationCreator creator) throws CreationException { Long layoutId = (Long) creator.getFields().get(ApplicationField.LAYOUT_ID); if (layoutId == null) { layoutId = getPageId((String) creator.getFields().get(ApplicationField.TOKEN), ApplicationService.DEFAULT_LAYOUT_NAME); } return layoutId; } protected Long getThemeId(final ApplicationCreator creator) throws CreationException { Long themeId = (Long) creator.getFields().get(ApplicationField.THEME_ID); if (themeId == null) { themeId = getPageId((String) creator.getFields().get(ApplicationField.TOKEN), ApplicationService.DEFAULT_THEME_NAME); } return themeId; } private Long getPageId(final String applicationToken, final String pageName) throws CreationException { try { SPage defaultLayout = pageService.getPageByName(pageName); if (defaultLayout == null) { throw new CreationException(String.format( "Unable to created application with token '%s' because the page '%s' was not found.", applicationToken, pageName)); } return defaultLayout.getId(); } catch (SBonitaReadException e) { throw new CreationException(e); } } public IApplication toApplication(final AbstractSApplication abstractSApplication) { final AbstractApplicationImpl application; if (abstractSApplication.isLink()) { final ApplicationLinkImpl applicationLink = new ApplicationLinkImpl( abstractSApplication.getToken(), abstractSApplication.getVersion(), abstractSApplication.getDescription()); application = applicationLink; } else { ApplicationImpl legacyApplication = new ApplicationImpl(abstractSApplication.getToken(), abstractSApplication.getVersion(), abstractSApplication.getDescription(), abstractSApplication.getLayoutId(), abstractSApplication.getThemeId()); legacyApplication.setHomePageId(abstractSApplication.getHomePageId()); application = legacyApplication; } application.setId(abstractSApplication.getId()); application.setDisplayName(abstractSApplication.getDisplayName()); application.setCreatedBy(abstractSApplication.getCreatedBy()); application.setCreationDate(new Date(abstractSApplication.getCreationDate())); application.setUpdatedBy(abstractSApplication.getUpdatedBy()); application.setLastUpdateDate(new Date(abstractSApplication.getLastUpdateDate())); application.setState(abstractSApplication.getState()); application.setIconPath(abstractSApplication.getIconPath()); application.setProfileId(abstractSApplication.getProfileId()); application.setHasIcon(abstractSApplication.hasIcon()); application.setEditable(abstractSApplication.isEditable()); application.setVisibility( InternalProfiles.getApplicationVisibilityByProfileName(abstractSApplication.getInternalProfile())); return application; } public List toApplication(final List sApplications) { final List applications = new ArrayList<>(sApplications.size()); for (final SApplication sApplication : sApplications) { applications.add(toApplication(sApplication)); } return applications; } public EntityUpdateDescriptor toApplicationUpdateDescriptor(final AbstractApplicationUpdater updater, final long updaterUserId) { final SApplicationUpdateBuilder builder = new SApplicationUpdateBuilder(updaterUserId); updateFields(updater, builder); return builder.done(); } protected void updateFields(final AbstractApplicationUpdater updater, final SApplicationUpdateBuilder builder) { for (final Entry entry : updater.getFields().entrySet()) { switch (entry.getKey()) { case TOKEN: builder.updateToken((String) entry.getValue()); break; case DESCRIPTION: builder.updateDescription((String) entry.getValue()); break; case DISPLAY_NAME: builder.updateDisplayName((String) entry.getValue()); break; case ICON_PATH: builder.updateIconPath((String) entry.getValue()); break; case ICON_FILE_NAME: builder.updateIconMimeType( entry.getValue() == null || ((String) entry.getValue()).isBlank() ? null : IOUtil.getContentTypeForIcon((String) entry.getValue())); break; case ICON_CONTENT: builder.updateIconContent((byte[]) entry.getValue()); break; case PROFILE_ID: builder.updateProfileId((Long) entry.getValue()); break; case STATE: builder.updateState((String) entry.getValue()); break; case VERSION: builder.updateVersion((String) entry.getValue()); break; case HOME_PAGE_ID: builder.updateHomePageId((Long) entry.getValue()); break; case THEME_ID: case LAYOUT_ID: break; default: throw new IllegalArgumentException("Unknown application field " + entry.getKey()); } } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/converter/ApplicationPageModelConverter.java ================================================ /** * 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.api.impl.converter; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.business.application.ApplicationPage; import org.bonitasoft.engine.business.application.impl.ApplicationPageImpl; import org.bonitasoft.engine.business.application.model.SApplicationPage; /** * @author Elias Ricken de Medeiros */ public class ApplicationPageModelConverter { public ApplicationPage toApplicationPage(final SApplicationPage sApplicationPage) { final ApplicationPageImpl appPage = new ApplicationPageImpl(sApplicationPage.getApplicationId(), sApplicationPage.getPageId(), sApplicationPage.getToken()); appPage.setId(sApplicationPage.getId()); return appPage; } public List toApplicationPage(final List sApplicationPages) { final List appPages = new ArrayList(sApplicationPages.size()); for (final SApplicationPage sApplicationPage : sApplicationPages) { appPages.add(toApplicationPage(sApplicationPage)); } return appPages; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/converter/PageModelConverter.java ================================================ /** * 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.api.impl.converter; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.page.PageCreator; import org.bonitasoft.engine.page.PageUpdater; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.page.impl.PageImpl; /** * @author Elias Ricken de Medeiros */ public class PageModelConverter { public SPage constructSPage(final PageCreator pageCreator, final long creatorUserId) { final Map fields = pageCreator.getFields(); final String name = (String) fields.get(PageCreator.PageField.NAME); final String description = (String) fields.get(PageCreator.PageField.DESCRIPTION); final String displayName = (String) fields.get(PageCreator.PageField.DISPLAY_NAME); final String contentName = (String) fields.get(PageCreator.PageField.CONTENT_NAME); final String contentType = (String) fields.get(PageCreator.PageField.CONTENT_TYPE); final Long processDefinitionId = (Long) fields.get(PageCreator.PageField.PROCESS_DEFINITION_ID); return buildSPage(creatorUserId, name, description, displayName, contentName, contentType, processDefinitionId); } public SPage constructSPage(final PageUpdater pageUpdater, final long creatorUserId) { final Map fields = pageUpdater.getFields(); final String name = (String) fields.get(PageUpdater.PageUpdateField.NAME); final String description = (String) fields.get(PageUpdater.PageUpdateField.DESCRIPTION); final String displayName = (String) fields.get(PageUpdater.PageUpdateField.DISPLAY_NAME); final String contentName = (String) fields.get(PageUpdater.PageUpdateField.CONTENT_NAME); final String contentType = (String) fields.get(PageUpdater.PageUpdateField.CONTENT_TYPE); final Long processDefinitionId = (Long) fields.get(PageUpdater.PageUpdateField.PROCESS_DEFINITION_ID); return buildSPage(creatorUserId, name, description, displayName, contentName, contentType, processDefinitionId); } private SPage buildSPage(long creatorUserId, String name, String description, String displayName, String contentName, String contentType, Long processDefinitionId) { return SPage.builder().name(name).description(description).displayName(displayName) .installationDate(System.currentTimeMillis()).lastModificationDate(System.currentTimeMillis()) .installedBy(creatorUserId).lastUpdatedBy(creatorUserId) .provided(false) .contentName(contentName) .contentType(contentType) .editable(true) .removable(true) .processDefinitionId(processDefinitionId != null ? processDefinitionId : 0) .build(); } public Page toPage(final SPage sPage) { Long processDefinitionId = sPage.getProcessDefinitionId() > 0 ? sPage.getProcessDefinitionId() : null; return new PageImpl(sPage.getId(), sPage.getName(), sPage.getDisplayName(), sPage.isProvided(), sPage.isEditable(), sPage.isRemovable(), sPage.getDescription(), sPage.getInstallationDate(), sPage.getInstalledBy(), sPage.getLastModificationDate(), sPage.getLastUpdatedBy(), sPage.getContentName(), sPage.getContentType(), processDefinitionId); } public List toPages(final List sPages) { final List pages = new ArrayList<>(sPages.size()); for (final SPage sPage : sPages) { pages.add(toPage(sPage)); } return pages; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/flownode/FlowNodeRetrier.java ================================================ /** * 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.api.impl.flownode; import org.bonitasoft.engine.api.impl.connector.ConnectorResetStrategy; import org.bonitasoft.engine.bpm.flownode.ActivityExecutionException; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException; import org.bonitasoft.engine.bpm.flownode.ActivityStates; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.execution.ContainerRegistry; import org.bonitasoft.engine.execution.FlowNodeExecutor; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; /** * @author Elias Ricken de Medeiros */ public class FlowNodeRetrier { private final ContainerRegistry containerRegistry; private final FlowNodeExecutor flowNodeExecutor; private final ActivityInstanceService activityInstanceService; private final FlowNodeStateManager stateManager; private final ConnectorResetStrategy strategy; public FlowNodeRetrier(ContainerRegistry containerRegistry, FlowNodeExecutor flowNodeExecutor, ActivityInstanceService activityInstanceService, FlowNodeStateManager stateManager, final ConnectorResetStrategy strategy) { this.containerRegistry = containerRegistry; this.flowNodeExecutor = flowNodeExecutor; this.activityInstanceService = activityInstanceService; this.stateManager = stateManager; this.strategy = strategy; } public void retry(long flowNodeInstanceId) throws ActivityExecutionException, ActivityInstanceNotFoundException { try { final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId); FlowNodeState previousState = stateManager.getState(flowNodeInstance.getPreviousStateId()); validateCurrentState(flowNodeInstance); strategy.resetConnectorsOf(flowNodeInstanceId); flowNodeExecutor.setStateByStateId(flowNodeInstanceId, flowNodeInstance.getPreviousStateId()); if (!previousState.isTerminal()) { containerRegistry.executeFlowNode(flowNodeInstance); } } catch (final SFlowNodeNotFoundException e) { throw new ActivityInstanceNotFoundException(flowNodeInstanceId, e); } catch (final SBonitaException e) { throw new ActivityExecutionException(e); } } private void validateCurrentState(final SFlowNodeInstance flowNodeInstance) throws ActivityExecutionException { FlowNodeState currentState = stateManager.getState(flowNodeInstance.getStateId()); if (!ActivityStates.FAILED_STATE.equals(currentState.getName())) { throw new ActivityExecutionException( "Unable to retry the flow node instance [name=" + flowNodeInstance.getName() + ", id=" + flowNodeInstance.getId() + "] because it is not in failed state. The current state for this flow node instance is '" + currentState.getName() + "'"); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationAPIDelegate.java ================================================ /** * 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.api.impl.livingapplication; import java.util.Optional; import java.util.function.Predicate; import org.bonitasoft.engine.api.impl.converter.ApplicationModelConverter; import org.bonitasoft.engine.business.application.*; import org.bonitasoft.engine.business.application.impl.IconImpl; import org.bonitasoft.engine.business.application.importer.validator.ApplicationTokenValidator; import org.bonitasoft.engine.business.application.importer.validator.ValidationStatus; import org.bonitasoft.engine.business.application.model.AbstractSApplication; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.exception.*; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Elias Ricken de Medeiros */ public class LivingApplicationAPIDelegate { private final ApplicationModelConverter converter; private final long loggedUserId; private final ApplicationService applicationService; private final ApplicationTokenValidator tokenValidator; public LivingApplicationAPIDelegate(final ServiceAccessor accessor, final ApplicationModelConverter converter, final long loggedUserId, final ApplicationTokenValidator tokenValidator) { this.tokenValidator = tokenValidator; applicationService = accessor.getApplicationService(); this.converter = converter; this.loggedUserId = loggedUserId; } /** * @deprecated as of 9.0.0, Applications should be created at startup. */ @Deprecated(since = "9.0.0") public Application createApplication(final ApplicationCreator applicationCreator) throws CreationException { try { validateCreator(applicationCreator); final SApplicationWithIcon sApplicationWithIcon = applicationService .createApplication(converter.buildSApplication(applicationCreator, loggedUserId)); var converted = converter.toApplication(sApplicationWithIcon); if (converted instanceof Application res) { return res; } else { // should not occur anyway throw new CreationException("Use dedicated API for application links."); } } catch (final SObjectAlreadyExistsException e) { throw new AlreadyExistsException(e.getMessage()); } catch (final SBonitaException e) { throw new CreationException(e); } } /** * @deprecated as of 9.0.0, Applications should be created at startup. This also concerns application links * introduced in 10.2.0. */ @Deprecated(since = "10.2.0") public ApplicationLink createApplicationLink(final ApplicationLinkCreator applicationLinkCreator) throws CreationException { try { validateCreator(applicationLinkCreator); final SApplicationWithIcon sApplicationWithIcon = applicationService .createApplication(converter.buildSApplication(applicationLinkCreator, loggedUserId)); var converted = converter.toApplication(sApplicationWithIcon); if (converted instanceof ApplicationLink res) { return res; } else { // should not occur anyway throw new CreationException("Use dedicated API for legacy applications."); } } catch (final SObjectAlreadyExistsException e) { throw new AlreadyExistsException(e.getMessage()); } catch (final SBonitaException e) { throw new CreationException(e); } } private void validateCreator(final AbstractApplicationCreator applicationCreator) throws CreationException { ValidationStatus validationStatus = tokenValidator.validate(applicationCreator.getToken()); if (!validationStatus.isValid()) { throw new CreationException(validationStatus.getMessage()); } final String displayName = (String) applicationCreator.getFields().get(ApplicationField.DISPLAY_NAME); if (displayName == null || displayName.trim().isEmpty()) { throw new CreationException("The application display name can not be null or empty"); } Class appClass = applicationCreator.isLink() ? ApplicationLink.class : Application.class; if (!applicationCreator.getFields().keySet().stream().allMatch(k -> k.isForClass(appClass))) { throw new CreationException("The application fields used must be valid for the application type"); } } public IApplication getIApplication(final long applicationId) throws ApplicationNotFoundException { try { return converter.toApplication(applicationService.getApplication(applicationId)); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } catch (final SObjectNotFoundException e) { throw new ApplicationNotFoundException(applicationId); } } public IApplication getIApplicationByToken(final String applicationToken) throws ApplicationNotFoundException { try { SApplication applicationByToken = applicationService.getApplicationByToken(applicationToken); if (applicationByToken == null) { throw new ApplicationNotFoundException(applicationToken); } return converter.toApplication(applicationByToken); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } } public Icon getIconOfApplication(final long applicationId) throws ApplicationNotFoundException { try { SApplicationWithIcon application = applicationService.getApplicationWithIcon(applicationId); if (application.hasIcon()) { return new IconImpl(application.getIconMimeType(), application.getIconContent()); } return null; } catch (final SBonitaReadException e) { throw new RetrieveException(e); } catch (final SObjectNotFoundException e) { throw new ApplicationNotFoundException(applicationId); } } public void deleteApplication(final long applicationId) throws DeletionException { try { applicationService.deleteApplication(applicationId); } catch (final SObjectNotFoundException sonfe) { throw new DeletionException(new ApplicationNotFoundException(applicationId)); } catch (final SBonitaException e) { throw new DeletionException(e); } } public SearchResult searchIApplications( final AbstractSearchEntity searchApplications) throws SearchException { try { searchApplications.execute(); return searchApplications.getResult(); } catch (final SBonitaException e) { throw new SearchException(e); } } /** * @deprecated as of 9.0.0, Applications should be updated at startup. */ @Deprecated(since = "9.0.0") public Application updateApplication(final long applicationId, final ApplicationUpdater updater) throws UpdateException, AlreadyExistsException, ApplicationNotFoundException { try { validateUpdater(updater); AbstractSApplication application; if (!updater.getFields().isEmpty()) { /* * This API may be called within our without a transaction. * So we must check first whether the application is a link to have a consistent behavior * and never update the application link. */ if (Optional.ofNullable(applicationService.getApplicationWithIcon(applicationId)) .filter(AbstractSApplication::isLink).isPresent()) { throw new UpdateException("Use dedicated API for application links."); } application = applicationService.updateApplication(applicationId, converter.toApplicationUpdateDescriptor(updater, loggedUserId)); } else { application = applicationService.getApplication(applicationId); } var converted = converter.toApplication(application); if (converted instanceof Application res) { return res; } else { throw new UpdateException("Use dedicated API for application links."); } } catch (final SObjectAlreadyExistsException e) { throw new AlreadyExistsException(e.getMessage()); } catch (final SObjectNotFoundException e) { throw new ApplicationNotFoundException(applicationId); } catch (final SBonitaException e) { throw new UpdateException(e); } } /** * @deprecated as of 9.0.0, Applications should be updated at startup. This also concerns application links * introduced in 10.2.0. */ @Deprecated(since = "10.2.0") public ApplicationLink updateApplicationLink(final long applicationId, final ApplicationLinkUpdater updater) throws UpdateException, AlreadyExistsException, ApplicationNotFoundException { try { validateUpdater(updater); AbstractSApplication application; if (!updater.getFields().isEmpty()) { /* * This API may be called within our without a transaction. * So we must check first whether the application is a link to have a consistent behavior * and never update the legacy application. */ Predicate isLink = AbstractSApplication::isLink; if (Optional.ofNullable(applicationService.getApplicationWithIcon(applicationId)) .filter(isLink.negate()).isPresent()) { throw new UpdateException("Use dedicated API for legacy applications."); } application = applicationService.updateApplication(applicationId, converter.toApplicationUpdateDescriptor(updater, loggedUserId)); } else { application = applicationService.getApplication(applicationId); } var converted = converter.toApplication(application); if (converted instanceof ApplicationLink res) { return res; } else { throw new UpdateException("Use dedicated API for legacy applications."); } } catch (final SObjectAlreadyExistsException e) { throw new AlreadyExistsException(e.getMessage()); } catch (final SObjectNotFoundException e) { throw new ApplicationNotFoundException(applicationId); } catch (final SBonitaException e) { throw new UpdateException(e); } } private void validateUpdater(final AbstractApplicationUpdater updater) throws UpdateException { validateToken(updater); validateDisplayName(updater); Class appClass = updater instanceof ApplicationLinkUpdater ? ApplicationLink.class : Application.class; if (!updater.getFields().keySet().stream().allMatch(k -> k.isForClass(appClass))) { throw new UpdateException("The application fields used must be valid for the application type"); } } private void validateDisplayName(final AbstractApplicationUpdater updater) throws UpdateException { if (updater.getFields().keySet().contains(ApplicationField.DISPLAY_NAME)) { final String displayName = (String) updater.getFields().get(ApplicationField.DISPLAY_NAME); if (displayName == null || displayName.trim().isEmpty()) { throw new UpdateException("The application display name can not be null or empty"); } } } private void validateToken(final AbstractApplicationUpdater updater) throws UpdateException { if (updater.getFields().keySet().contains(ApplicationField.TOKEN)) { final String token = (String) updater.getFields().get(ApplicationField.TOKEN); ValidationStatus validationStatus = tokenValidator.validate(token); if (!validationStatus.isValid()) { throw new UpdateException(validationStatus.getMessage()); } } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationExporterDelegate.java ================================================ /** * 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.api.impl.livingapplication; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.exporter.ApplicationExporter; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.exception.ExportException; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Elias Ricken de Medeiros */ public class LivingApplicationExporterDelegate { private final ApplicationService applicationService; private final ApplicationExporter exporter; public LivingApplicationExporterDelegate(ApplicationService applicationService, ApplicationExporter exporter) { this.applicationService = applicationService; this.exporter = exporter; } public byte[] exportApplications(long... applicationIds) throws ExportException { try { List applications = new ArrayList<>(); for (long applicationId : applicationIds) { applications.add(applicationService.getApplication(applicationId)); } return exporter.export(applications); } catch (SObjectNotFoundException | SBonitaReadException e) { throw new ExportException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationMenuAPIDelegate.java ================================================ /** * 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.api.impl.livingapplication; import java.util.List; import org.bonitasoft.engine.api.impl.converter.ApplicationMenuModelConverter; import org.bonitasoft.engine.api.impl.transaction.application.SearchApplicationMenus; import org.bonitasoft.engine.business.application.*; import org.bonitasoft.engine.business.application.importer.validator.ApplicationMenuCreatorValidator; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.business.application.model.builder.SApplicationUpdateBuilder; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.exception.*; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Elias Ricken de Medeiros */ public class LivingApplicationMenuAPIDelegate { private final ApplicationMenuModelConverter converter; private final ApplicationService applicationService; private final ApplicationMenuCreatorValidator creatorValidator; private final long loggedUserId; public LivingApplicationMenuAPIDelegate(final ServiceAccessor accessor, final ApplicationMenuModelConverter converter, final ApplicationMenuCreatorValidator creatorValidator, final long loggedUserId) { this.creatorValidator = creatorValidator; this.loggedUserId = loggedUserId; applicationService = accessor.getApplicationService(); this.converter = converter; } /** * @deprecated as of 9.0.0, Application menu should be created at startup. */ @Deprecated(since = "9.0.0") public ApplicationMenu createApplicationMenu(final ApplicationMenuCreator applicationMenuCreator) throws CreationException { try { List errors = creatorValidator.isValid(applicationMenuCreator); if (!errors.isEmpty()) { throw new CreationException( "The ApplicationMenuCreator is invalid. Problems: " + errors); } final int index = applicationService.getNextAvailableIndex(applicationMenuCreator.getParentId()); final SApplicationMenu sApplicationMenu = applicationService .createApplicationMenu(converter.buildSApplicationMenu(applicationMenuCreator, index)); applicationService.updateApplication(sApplicationMenu.getApplicationId(), new SApplicationUpdateBuilder(loggedUserId).done()); return converter.toApplicationMenu(sApplicationMenu); } catch (final SBonitaException e) { throw new CreationException(e); } } /** * @deprecated as of 9.0.0, Application menu should be updated at startup. */ @Deprecated(since = "9.0.0") public ApplicationMenu updateApplicationMenu(final long applicationMenuId, final ApplicationMenuUpdater updater) throws ApplicationMenuNotFoundException, UpdateException { final EntityUpdateDescriptor updateDescriptor = converter.toApplicationMenuUpdateDescriptor(updater); try { final SApplicationMenu sApplicationMenu = applicationService.updateApplicationMenu(applicationMenuId, updateDescriptor); applicationService.updateApplication(sApplicationMenu.getApplicationId(), new SApplicationUpdateBuilder(loggedUserId).done()); return converter.toApplicationMenu(sApplicationMenu); } catch (final SObjectNotFoundException e) { throw new ApplicationMenuNotFoundException(e.getMessage()); } catch (final SBonitaException e) { throw new UpdateException(e); } } public ApplicationMenu getApplicationMenu(final long applicationMenuId) throws ApplicationMenuNotFoundException { try { final SApplicationMenu sApplicationMenu = applicationService.getApplicationMenu(applicationMenuId); return converter.toApplicationMenu(sApplicationMenu); } catch (final SObjectNotFoundException e) { throw new ApplicationMenuNotFoundException(e.getMessage()); } catch (final SBonitaException e) { throw new RetrieveException(e); } } public void deleteApplicationMenu(final long applicationMenuId) throws DeletionException { try { final SApplicationMenu deletedApplicationMenu = applicationService.deleteApplicationMenu(applicationMenuId); applicationService.updateApplication(deletedApplicationMenu.getApplicationId(), new SApplicationUpdateBuilder(loggedUserId).done()); } catch (final SObjectNotFoundException sonfe) { throw new DeletionException(new ApplicationMenuNotFoundException(sonfe.getMessage())); } catch (final SBonitaException e) { throw new DeletionException(e); } } public SearchResult searchApplicationMenus(final SearchApplicationMenus searchApplicationMenus) throws SearchException { try { searchApplicationMenus.execute(); return searchApplicationMenus.getResult(); } catch (final SBonitaException e) { throw new SearchException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationPageAPIDelegate.java ================================================ /** * 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.api.impl.livingapplication; import java.util.List; import org.bonitasoft.engine.api.impl.converter.ApplicationPageModelConverter; import org.bonitasoft.engine.api.impl.transaction.application.SearchApplicationPages; import org.bonitasoft.engine.business.application.ApplicationNotFoundException; import org.bonitasoft.engine.business.application.ApplicationPage; import org.bonitasoft.engine.business.application.ApplicationPageNotFoundException; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.importer.validator.ApplicationTokenValidator; import org.bonitasoft.engine.business.application.importer.validator.ValidationStatus; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.bonitasoft.engine.business.application.model.builder.SApplicationUpdateBuilder; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Elias Ricken de Medeiros */ public class LivingApplicationPageAPIDelegate { private final ApplicationPageModelConverter converter; private final ApplicationService applicationService; private final long loggedUserId; private final ApplicationTokenValidator tokenValidator; public LivingApplicationPageAPIDelegate(final ServiceAccessor accessor, final ApplicationPageModelConverter converter, final long loggedUserId, final ApplicationTokenValidator tokenValidator) { this.tokenValidator = tokenValidator; applicationService = accessor.getApplicationService(); this.converter = converter; this.loggedUserId = loggedUserId; } /** * @deprecated as of 9.0.0, Application home page should be defined at startup. */ @Deprecated(since = "9.0.0") public void setApplicationHomePage(final long applicationId, final long applicationPageId) throws UpdateException, ApplicationNotFoundException { final SApplicationUpdateBuilder sApplicationUpdateBuilder = new SApplicationUpdateBuilder(loggedUserId) .updateHomePageId(applicationPageId); try { applicationService.updateApplication(applicationId, sApplicationUpdateBuilder.done()); } catch (final SObjectNotFoundException e) { throw new ApplicationNotFoundException(applicationId); } catch (final SBonitaException e) { throw new UpdateException(e); } } /** * @deprecated as of 9.0.0, Application page should be created at startup. */ @Deprecated(since = "9.0.0") public ApplicationPage createApplicationPage(final long applicationId, final long pageId, final String token) throws CreationException { validateToken(token); final SApplicationPage.SApplicationPageBuilder pageBuilder = SApplicationPage.builder() .applicationId(applicationId).pageId(pageId).token(token); SApplicationPage sAppPage; try { sAppPage = applicationService.createApplicationPage(pageBuilder.build()); applicationService.updateApplication(applicationId, new SApplicationUpdateBuilder(loggedUserId) .done()); return converter.toApplicationPage(sAppPage); } catch (final SObjectAlreadyExistsException e) { throw new AlreadyExistsException(e.getMessage()); } catch (final SBonitaException e) { throw new CreationException(e); } } private void validateToken(final String token) throws CreationException { ValidationStatus validationStatus = tokenValidator.validate(token); if (!validationStatus.isValid()) { throw new CreationException(validationStatus.getMessage()); } } public ApplicationPage getApplicationPage(final String applicationName, final String applicationPageToken) throws ApplicationPageNotFoundException { try { final SApplicationPage sAppPage = applicationService.getApplicationPage(applicationName, applicationPageToken); return converter.toApplicationPage(sAppPage); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } catch (final SObjectNotFoundException e) { throw new ApplicationPageNotFoundException(e.getMessage()); } } public ApplicationPage getApplicationPage(final long applicationPageId) throws ApplicationPageNotFoundException { try { final SApplicationPage sApplicationPage = applicationService.getApplicationPage(applicationPageId); return converter.toApplicationPage(sApplicationPage); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } catch (final SObjectNotFoundException e) { throw new ApplicationPageNotFoundException(e.getMessage()); } } public void deleteApplicationPage(final long applicationPageId) throws DeletionException { try { final SApplicationPage deletedApplicationPage = applicationService.deleteApplicationPage(applicationPageId); final SApplicationUpdateBuilder appBuilder = new SApplicationUpdateBuilder(loggedUserId); applicationService.updateApplication(deletedApplicationPage.getApplicationId(), appBuilder.done()); } catch (final SObjectNotFoundException sonfe) { throw new DeletionException(new ApplicationPageNotFoundException(sonfe.getMessage())); } catch (final SBonitaException e) { throw new DeletionException(e); } } public ApplicationPage getApplicationHomePage(final long applicationId) throws ApplicationPageNotFoundException { try { SApplicationPage sHomePage = applicationService.getApplicationHomePage(applicationId); return converter.toApplicationPage(sHomePage); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } catch (final SObjectNotFoundException e) { throw new ApplicationPageNotFoundException(e.getMessage()); } } public SearchResult searchApplicationPages(final SearchApplicationPages searchApplicationPages) throws SearchException { try { searchApplicationPages.execute(); return searchApplicationPages.getResult(); } catch (final SBonitaException e) { throw new SearchException(e); } } public List getAllPagesForProfile(final long profileId) { try { return applicationService.getAllPagesForProfile(profileId); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } } public List getAllPagesForProfile(String profile) { try { return applicationService.getAllPagesForProfile(profile); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/organization/OrganizationAPIDelegate.java ================================================ /** * Copyright (C) 2023 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.api.impl.organization; import java.util.List; import javax.xml.bind.JAXBException; import org.bonitasoft.engine.api.impl.SCustomUserInfoValueAPI; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.identity.*; import org.bonitasoft.engine.identity.model.builder.SCustomUserInfoValueUpdateBuilderFactory; import org.bonitasoft.engine.service.ServiceAccessorSingleton; /** * @author Emmanuel Duchastenier */ public class OrganizationAPIDelegate { private static OrganizationAPIDelegate organizationAPIDelegate = null; private final IdentityService identityService; private OrganizationAPIDelegate(IdentityService identityService) { this.identityService = identityService; } public static OrganizationAPIDelegate getInstance() { if (organizationAPIDelegate == null) { organizationAPIDelegate = new OrganizationAPIDelegate(ServiceAccessorSingleton.getInstance() .getIdentityService()); } return organizationAPIDelegate; } public List importOrganizationWithWarnings(String organizationContent, ImportPolicy policy) throws OrganizationImportException { try { final SCustomUserInfoValueUpdateBuilderFactory updaterFactor = BuilderFactory .get(SCustomUserInfoValueUpdateBuilderFactory.class); final SCustomUserInfoValueAPI customUserInfoValueAPI = new SCustomUserInfoValueAPI( identityService, updaterFactor); ImportOrganization importedOrganization = new ImportOrganization(identityService, organizationContent, policy, customUserInfoValueAPI); return importedOrganization.execute(); } catch (JAXBException e) { throw new InvalidOrganizationFileFormatException(e); } catch (final SBonitaException e) { throw new OrganizationImportException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/page/PageAPIDelegate.java ================================================ /** * 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.api.impl.page; import java.io.Serializable; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.impl.converter.PageModelConverter; import org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager; import org.bonitasoft.engine.api.impl.transaction.page.SearchPages; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.core.form.FormMappingService; import org.bonitasoft.engine.core.form.SFormMapping; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.InvalidPageTokenException; import org.bonitasoft.engine.exception.InvalidPageZipContentException; import org.bonitasoft.engine.exception.InvalidPageZipInconsistentException; import org.bonitasoft.engine.exception.InvalidPageZipMissingAPropertyException; import org.bonitasoft.engine.exception.InvalidPageZipMissingIndexException; import org.bonitasoft.engine.exception.InvalidPageZipMissingPropertiesException; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.exception.UpdatingWithInvalidPageTokenException; import org.bonitasoft.engine.exception.UpdatingWithInvalidPageZipContentException; import org.bonitasoft.engine.form.FormMappingSearchDescriptor; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.page.PageCreator; import org.bonitasoft.engine.page.PageMappingService; import org.bonitasoft.engine.page.PageNotFoundException; import org.bonitasoft.engine.page.PageNotFoundException.PageAttribute; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.page.PageUpdater; import org.bonitasoft.engine.page.SInvalidPageTokenException; import org.bonitasoft.engine.page.SInvalidPageZipException; import org.bonitasoft.engine.page.SInvalidPageZipInconsistentException; import org.bonitasoft.engine.page.SInvalidPageZipMissingAPropertyException; import org.bonitasoft.engine.page.SInvalidPageZipMissingIndexException; import org.bonitasoft.engine.page.SInvalidPageZipMissingPropertiesException; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.page.SPageMapping; import org.bonitasoft.engine.page.SPageUpdateBuilder; import org.bonitasoft.engine.page.SPageUpdateBuilderFactory; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; /** * @author Emmanuel Duchastenier */ @Slf4j public class PageAPIDelegate { private static PageAPIDelegate pageAPIDelegate = null; private final PageService pageService; private final SearchEntitiesDescriptor searchEntitiesDescriptor; private final ServiceAccessor serviceAccessor; private final PageMappingService pageMappingService; private final FormMappingService formMappingService; private final BusinessArchiveArtifactsManager businessArchiveArtifactsManager; private PageAPIDelegate(ServiceAccessor serviceAccessor, BusinessArchiveArtifactsManager businessArchiveArtifactsManager, PageService pageService, PageMappingService pageMappingService, FormMappingService formMappingService, SearchEntitiesDescriptor searchEntitiesDescriptor) { this.serviceAccessor = serviceAccessor; this.businessArchiveArtifactsManager = businessArchiveArtifactsManager; this.pageService = pageService; this.pageMappingService = pageMappingService; this.formMappingService = formMappingService; this.searchEntitiesDescriptor = searchEntitiesDescriptor; } public static PageAPIDelegate getInstance() { if (pageAPIDelegate == null) { ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance(); pageAPIDelegate = new PageAPIDelegate(serviceAccessor, serviceAccessor.getBusinessArchiveArtifactsManager(), serviceAccessor.getPageService(), serviceAccessor.getPageMappingService(), serviceAccessor.getFormMappingService(), serviceAccessor.getSearchEntitiesDescriptor()); } return pageAPIDelegate; } public Page getPage(final long pageId) throws PageNotFoundException { try { return convertToPage(pageService.getPage(pageId)); } catch (final SBonitaReadException e) { throw new PageNotFoundException(e); } catch (final SObjectNotFoundException e) { throw new PageNotFoundException(e); } } public byte[] getPageContent(final long pageId) throws PageNotFoundException { try { return pageService.getPageContent(pageId); } catch (final SBonitaReadException e) { throw new PageNotFoundException(e); } catch (final SObjectNotFoundException e) { throw new PageNotFoundException(e); } } public SearchResult searchPages(final SearchOptions searchOptions) throws SearchException { final SearchPages searchPages = getSearchPages(searchOptions); try { searchPages.execute(); return searchPages.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } protected SearchPages getSearchPages(final SearchOptions searchOptions) { return new SearchPages(pageService, searchEntitiesDescriptor.getSearchPageDescriptor(), searchOptions); } public Page createPage(final PageCreator pageCreator, final byte[] content, long userIdFromSession) throws AlreadyExistsException, CreationException, InvalidPageTokenException, InvalidPageZipContentException { final SPage sPage = constructPage(pageCreator, userIdFromSession); try { final SPage addPage = pageService.addPage(sPage, content); return convertToPage(addPage); } catch (final SObjectAlreadyExistsException e) { throw new AlreadyExistsException("A page already exists with the name " + pageCreator.getName()); } catch (final SInvalidPageTokenException e) { throw new InvalidPageTokenException(e.getMessage(), e); } catch (final SInvalidPageZipException e) { throw convertException(e); } catch (final SBonitaException e) { throw new CreationException(e); } } public Page createPage(final String contentName, final byte[] content, long userIdFromSession) throws AlreadyExistsException, CreationException, InvalidPageTokenException, InvalidPageZipContentException { try { return convertToPage(pageService.addPage(content, contentName, userIdFromSession)); } catch (final SObjectAlreadyExistsException e) { throw new AlreadyExistsException("A page already exists with the name defined in page zip content"); } catch (final SInvalidPageTokenException e) { throw new InvalidPageTokenException(e.getMessage(), e); } catch (final SInvalidPageZipException e) { throw convertException(e); } catch (final SBonitaException e) { throw new CreationException(e); } } public void deletePage(final long pageId) throws DeletionException { try { pageService.deletePage(pageId); final Set processDefinitionIds = updatePageMappings(pageId); for (final Long processDefinitionId : processDefinitionIds) { updateProcessResolution(processDefinitionId); } } catch (final SObjectNotFoundException sonfe) { throw new DeletionException(new PageNotFoundException(sonfe)); } catch (final SBonitaException e) { throw new DeletionException(e); } } Set updatePageMappings(long pageId) throws SBonitaReadException, SObjectModificationException, SObjectNotFoundException { List formMappings; QueryOptions queryOptions = new QueryOptions(0, 20, Collections.singletonList(new OrderByOption(SFormMapping.class, FormMappingSearchDescriptor.ID, OrderByType.ASC)), Collections.singletonList(new FilterOption(SPageMapping.class, FormMappingSearchDescriptor.PAGE_ID, pageId)), null); final Set processDefinitionIds = new HashSet<>(); do { formMappings = formMappingService.searchFormMappings(queryOptions); for (final SFormMapping formMapping : formMappings) { pageMappingService.update(formMapping.getPageMapping(), null); processDefinitionIds.add(formMapping.getProcessDefinitionId()); } queryOptions = QueryOptions.getNextPage(queryOptions); } while (formMappings.size() == 20); return processDefinitionIds; } private void updateProcessResolution(Long processDefinitionId) { businessArchiveArtifactsManager.resolveDependencies(processDefinitionId, serviceAccessor); } public void deletePages(final List pageIds) throws DeletionException { for (final Long pageId : pageIds) { deletePage(pageId); } } public Page getPageByName(final String name) throws PageNotFoundException { try { final SPage sPage = pageService.getPageByName(name); if (sPage == null) { throw new PageNotFoundException(name, PageAttribute.NAME); } return convertToPage(sPage); } catch (final SBonitaReadException e) { throw new PageNotFoundException(e); } } public Page updatePage(final long pageId, final PageUpdater pageUpdater, long userIdFromSession) throws UpdateException, AlreadyExistsException { if (pageUpdater == null || pageUpdater.getFields().isEmpty()) { throw new UpdateException("The pageUpdater descriptor does not contain field updates"); } final SPage sPage = constructPage(pageUpdater, userIdFromSession); final SPageUpdateBuilder pageUpdateBuilder = getPageUpdateBuilder(); final Map fields = pageUpdater.getFields(); for (final Entry field : fields.entrySet()) { switch (field.getKey()) { case NAME: log.warn("Since 7.13 updating the name of a page is no more possible, the update will ignore it."); break; case DISPLAY_NAME: pageUpdateBuilder.updateDisplayName(sPage.getDisplayName()); break; case DESCRIPTION: pageUpdateBuilder.updateDescription(sPage.getDescription()); break; case CONTENT_NAME: pageUpdateBuilder.updateContentName(sPage.getContentName()); break; case CONTENT_TYPE: pageUpdateBuilder.updateContentType(sPage.getContentType()); break; case PROCESS_DEFINITION_ID: pageUpdateBuilder.updateProcessDefinitionId(sPage.getProcessDefinitionId()); break; default: break; } } pageUpdateBuilder.updateLastModificationDate(System.currentTimeMillis()); pageUpdateBuilder.updateLastUpdatedBy(userIdFromSession); pageUpdateBuilder.markNonProvided(); SPage updatedPage; try { updatedPage = pageService.updatePage(pageId, pageUpdateBuilder.done()); return convertToPage(updatedPage); } catch (final SObjectModificationException e) { throw new UpdateException(e); } catch (final SObjectAlreadyExistsException e) { throw new AlreadyExistsException(e); } catch (final SInvalidPageTokenException e) { throw new UpdatingWithInvalidPageTokenException(e.getMessage(), e); } } public void updatePageContent(final long pageId, final byte[] content, long userIdFromSession) throws UpdateException, UpdatingWithInvalidPageTokenException, UpdatingWithInvalidPageZipContentException { final SPageUpdateBuilder pageUpdateBuilder = getPageUpdateBuilder(); pageUpdateBuilder.updateLastModificationDate(System.currentTimeMillis()); pageUpdateBuilder.updateLastUpdatedBy(userIdFromSession); pageUpdateBuilder.markNonProvided(); try { pageService.updatePageContent(pageId, content, null, pageUpdateBuilder); } catch (final SInvalidPageTokenException e) { throw new UpdatingWithInvalidPageTokenException(e.getMessage(), e); } catch (final SInvalidPageZipException e) { throw new UpdatingWithInvalidPageZipContentException(e.getMessage(), e); } catch (final SBonitaException sBonitaException) { throw new UpdateException(sBonitaException); } } public Properties getPageProperties(final byte[] content, final boolean checkIfItAlreadyExists) throws InvalidPageTokenException, AlreadyExistsException, InvalidPageZipMissingPropertiesException, InvalidPageZipMissingIndexException, InvalidPageZipInconsistentException, InvalidPageZipMissingAPropertyException { try { return getProperties(content, checkIfItAlreadyExists, pageService); } catch (final SInvalidPageTokenException e) { throw new InvalidPageTokenException(e.getMessage()); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } catch (final SInvalidPageZipMissingAPropertyException e) { throw new InvalidPageZipMissingAPropertyException(e.getFields()); } catch (final SInvalidPageZipInconsistentException e) { throw new InvalidPageZipInconsistentException(e.getMessage(), e); } catch (final SInvalidPageZipMissingIndexException e) { throw new InvalidPageZipMissingIndexException(); } catch (final SInvalidPageZipMissingPropertiesException e) { throw new InvalidPageZipMissingPropertiesException(); } } protected SPageUpdateBuilder getPageUpdateBuilder() { return BuilderFactory.get(SPageUpdateBuilderFactory.class).createNewInstance(new EntityUpdateDescriptor()); } protected Page convertToPage(final SPage addPage) { return new PageModelConverter().toPage(addPage); } protected SPage constructPage(final PageCreator pageCreator, final long userId) { return new PageModelConverter().constructSPage(pageCreator, userId); } protected SPage constructPage(final PageUpdater pageUpdater, final long userId) { return new PageModelConverter().constructSPage(pageUpdater, userId); } private Properties getProperties(final byte[] content, final boolean checkIfItAlreadyExists, final PageService pageService) throws SInvalidPageZipMissingIndexException, SInvalidPageZipMissingAPropertyException, SInvalidPageZipInconsistentException, SInvalidPageZipMissingPropertiesException, SInvalidPageTokenException, SBonitaReadException, AlreadyExistsException { final Properties properties = pageService.readPageZip(content); if (checkIfItAlreadyExists) { final String name = properties.getProperty(PageService.PROPERTIES_NAME); final SPage pageByName = pageService.getPageByName(name); if (pageByName != null) { throw new AlreadyExistsException("A page with name " + name + " already exists"); } } return properties; } private InvalidPageZipContentException convertException(final SInvalidPageZipException e) { if (e instanceof SInvalidPageZipMissingPropertiesException) { return new InvalidPageZipMissingPropertiesException(); } if (e instanceof SInvalidPageZipMissingAPropertyException) { return new InvalidPageZipMissingAPropertyException( ((SInvalidPageZipMissingAPropertyException) e).getFields()); } if (e instanceof SInvalidPageZipInconsistentException) { return new InvalidPageZipInconsistentException(e.getMessage(), e); } if (e instanceof SInvalidPageZipMissingIndexException) { return new InvalidPageZipMissingIndexException(); } return new InvalidPageZipContentException(e.getMessage(), e); } public Page getPageByNameAndProcessDefinition(String name, long processDefinitionId) throws PageNotFoundException { try { final SPage sPage = pageService.getPageByNameAndProcessDefinitionId(name, processDefinitionId); if (sPage == null) { throw new PageNotFoundException(name, PageAttribute.NAME); } return convertToPage(sPage); } catch (final SBonitaReadException e) { throw new PageNotFoundException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/platform/PlatformInformationAPIImpl.java ================================================ /** * 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.api.impl.platform; import static java.lang.String.valueOf; import static org.bonitasoft.engine.execution.ProcessStarterVerifierImpl.LIMIT; import java.util.Map; import org.bonitasoft.engine.api.impl.AvailableInMaintenanceMode; import org.bonitasoft.engine.api.platform.PlatformInformationAPI; import org.bonitasoft.engine.execution.ProcessStarterVerifier; import org.bonitasoft.engine.service.ServiceAccessorSingleton; /** * Provides runtime information about the platform. * Information returned is dependent on the edition (Community or Subscription) * and the platform configuration / license. * * @author Emmanuel Duchastenier */ @AvailableInMaintenanceMode public class PlatformInformationAPIImpl implements PlatformInformationAPI { private ProcessStarterVerifier processStarterVerifier; public PlatformInformationAPIImpl() { // Keep this empty constructor for compatibility with the APIAccessResolverImpl } PlatformInformationAPIImpl(ProcessStarterVerifier processStarterVerifier) { this.processStarterVerifier = processStarterVerifier; } @Override public Map getPlatformInformation() { if (processStarterVerifier == null) { this.processStarterVerifier = ServiceAccessorSingleton.getInstance().getProcessStarterVerifier(); } return Map.of( "edition", "Community", "caseCounter", valueOf(processStarterVerifier.getCurrentNumberOfStartedProcessInstances()), "caseCounterLimit", valueOf(LIMIT)); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/profile/ProfileAPIDelegate.java ================================================ /** * Copyright (C) 2023 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.api.impl.profile; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.impl.transaction.profile.CreateProfileMember; import org.bonitasoft.engine.api.impl.transaction.profile.ProfileMemberUtils; import org.bonitasoft.engine.api.utils.VisibleForTesting; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.command.SGroupProfileMemberAlreadyExistsException; import org.bonitasoft.engine.command.SRoleProfileMemberAlreadyExistsException; import org.bonitasoft.engine.command.SUserMembershipProfileMemberAlreadyExistsException; import org.bonitasoft.engine.command.SUserProfileMemberAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.exception.*; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.MemberType; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.profile.*; import org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException; import org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberNotFoundException; import org.bonitasoft.engine.profile.model.SProfileMember; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.search.profile.SearchProfileMembersForProfile; import org.bonitasoft.engine.search.profile.SearchProfiles; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.engine.session.SessionService; public class ProfileAPIDelegate { private static ProfileAPIDelegate profileAPIDelegate; protected ProfileService profileService; protected ApplicationService applicationService; private SearchEntitiesDescriptor searchEntitiesDescriptor; private IdentityService identityService; @VisibleForTesting protected ProfileAPIDelegate(ProfileService profileService, IdentityService identityService, SearchEntitiesDescriptor searchEntitiesDescriptor, ApplicationService applicationService) { this.profileService = profileService; this.identityService = identityService; this.searchEntitiesDescriptor = searchEntitiesDescriptor; this.applicationService = applicationService; } public static ServiceAccessor getServiceAccessor() { return ServiceAccessorSingleton.getInstance(); } public static ProfileAPIDelegate getInstance() { if (profileAPIDelegate == null) { ServiceAccessor serviceAccessor = getServiceAccessor(); profileAPIDelegate = new ProfileAPIDelegate(serviceAccessor.getProfileService(), serviceAccessor.getIdentityService(), serviceAccessor.getSearchEntitiesDescriptor(), serviceAccessor.getApplicationService()); } return profileAPIDelegate; } public SearchResult searchProfiles(final SearchOptions options) throws SearchException { final SearchProfiles searchProfileTransaction = new SearchProfiles(profileService, searchEntitiesDescriptor.getSearchProfileDescriptor(), options); try { searchProfileTransaction.execute(); return searchProfileTransaction.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } public Map getNumberOfProfileMembers(final List profileIds) { try { final List listOfProfileMembers = profileService.getProfileMembers(profileIds); final Map profileMembers = new HashMap<>(); final Map result = new HashMap<>(); for (final SProfileMember p : listOfProfileMembers) { profileMembers.put(p.getProfileId(), p); } for (final Long obj : profileMembers.keySet()) { long number = 0; for (final SProfileMember listOfProfileMember : listOfProfileMembers) { final long tempProfileId = listOfProfileMember.getProfileId(); if (obj == tempProfileId) { number++; } } result.put(obj, number); } return result; } catch (final SBonitaException sbe) { throw new RetrieveException(sbe); } } public Profile getProfile(final long id) throws ProfileNotFoundException { try { return ModelConvertor.toProfile(profileService.getProfile(id)); } catch (final SProfileNotFoundException e) { throw new ProfileNotFoundException(e); } } public List getProfilesForUser(final long userId, final int startIndex, final int maxResults, final ProfileCriterion criterion) { if (SessionService.SYSTEM_ID == userId) { // It's the tenant admin user return Collections.emptyList(); } try { return ModelConvertor.toProfiles( profileService.searchProfilesOfUser(userId, startIndex, maxResults, criterion.getField(), OrderByType.valueOf(criterion.getOrder().name()))); } catch (final SBonitaReadException e) { throw new RetrieveException(e); } } public SearchResult searchProfileMembers(final String memberType, final SearchOptions options) throws SearchException { try { final SearchProfileMembersForProfile searchProfileMembersForProfile = new SearchProfileMembersForProfile( getProfileMemberQuerySuffix(memberType), profileService, getProfileMemberDescriptor(searchEntitiesDescriptor, memberType), options); searchProfileMembersForProfile.execute(); return searchProfileMembersForProfile.getResult(); } catch (final SBonitaException sbe) { throw new SearchException(sbe); } } public ProfileMember createProfileMember(final Long profileId, final Long userId, final Long groupId, final Long roleId) throws CreationException { try { final MemberType memberType = getMemberType(userId, groupId, roleId); final CreateProfileMember createProfileMember = new CreateProfileMember(profileService, identityService, profileId, userId, groupId, roleId, memberType); try { checkIfProfileMemberExists(profileId, userId, groupId, roleId, memberType); } catch (final SBonitaException e1) { throw new AlreadyExistsException(e1); } createProfileMember.execute(); return convertToProfileMember(createProfileMember); } catch (final SBonitaException e) { throw new CreationException(e); } } public void deleteProfileMember(final Long profileMemberId) throws DeletionException { try { // add a lock because the update profile call getProfile then update profile -> deadlock... final SProfileMember profileMember = profileService.getProfileMemberWithoutDisplayName(profileMemberId); profileService.updateProfileMetaData(profileMember.getProfileId()); profileService.deleteProfileMember(profileMember.getId()); } catch (final SProfileMemberNotFoundException spmnfe) { throw new DeletionException(new ProfileMemberNotFoundException(spmnfe)); } catch (final SBonitaException e) { throw new DeletionException(e); } } protected ProfileMember convertToProfileMember(final CreateProfileMember createProfileMember) { return ModelConvertor.toProfileMember(createProfileMember.getResult()); } protected void checkIfProfileMemberExists(final Long profileId, final Long userId, final Long groupId, final Long roleId, final MemberType memberType) throws SBonitaException { final SearchEntityDescriptor searchDescriptor = searchEntitiesDescriptor.getSearchProfileMemberUserDescriptor(); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 1); searchOptionsBuilder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, profileId); SearchProfileMembersForProfile searchProfileMembersForProfile; switch (memberType) { case USER: searchProfileMembersForProfile = new SearchProfileMembersForProfile("ForUser", profileService, searchDescriptor, searchOptionsBuilder.done()); searchOptionsBuilder.filter(ProfileMemberSearchDescriptor.USER_ID, userId); searchProfileMembersForProfile.execute(); if (searchProfileMembersForProfile.getResult().getCount() != 0) { throw new SUserProfileMemberAlreadyExistsException( "A profileMember with userId \"" + userId + "\" already exists"); } break; case GROUP: searchProfileMembersForProfile = new SearchProfileMembersForProfile("ForGroup", profileService, searchDescriptor, searchOptionsBuilder.done()); searchOptionsBuilder.filter(ProfileMemberSearchDescriptor.GROUP_ID, groupId); searchProfileMembersForProfile.execute(); if (searchProfileMembersForProfile.getResult().getCount() != 0) { throw new SGroupProfileMemberAlreadyExistsException( "A profileMember with groupId \"" + groupId + "\" already exists"); } break; case ROLE: searchProfileMembersForProfile = new SearchProfileMembersForProfile("ForRole", profileService, searchDescriptor, searchOptionsBuilder.done()); searchOptionsBuilder.filter(ProfileMemberSearchDescriptor.ROLE_ID, roleId); searchProfileMembersForProfile.execute(); if (searchProfileMembersForProfile.getResult().getCount() != 0) { throw new SRoleProfileMemberAlreadyExistsException( "A profileMember with roleId \"" + roleId + "\" already exists"); } break; default: searchProfileMembersForProfile = new SearchProfileMembersForProfile("ForRoleAndGroup", profileService, searchDescriptor, searchOptionsBuilder.done()); searchOptionsBuilder.filter(ProfileMemberSearchDescriptor.ROLE_ID, roleId); searchOptionsBuilder.filter(ProfileMemberSearchDescriptor.GROUP_ID, groupId); searchProfileMembersForProfile.execute(); if (searchProfileMembersForProfile.getResult().getCount() != 0) { throw new SUserMembershipProfileMemberAlreadyExistsException( "A profileMember with groupId \"" + groupId + "\" and roleId \"" + roleId + "\" already exists"); } break; } } private boolean isPositiveLong(final Long value) { return value != null && value > 0; } public MemberType getMemberType(final Long userId, final Long groupId, final Long roleId) throws CreationException { MemberType memberType; if (isPositiveLong(userId)) { memberType = MemberType.USER; } else if (isPositiveLong(groupId) && !isPositiveLong(roleId)) { memberType = MemberType.GROUP; } else if (isPositiveLong(roleId) && !isPositiveLong(groupId)) { memberType = MemberType.ROLE; } else if (isPositiveLong(roleId) && isPositiveLong(groupId)) { memberType = MemberType.MEMBERSHIP; } else { throw new CreationException(String.format("Parameters map must contain at least one of entries: %s, %s, %s", ProfileMemberUtils.USER_ID, ProfileMemberUtils.GROUP_ID, ProfileMemberUtils.ROLE_ID)); } return memberType; } private SearchEntityDescriptor getProfileMemberDescriptor(final SearchEntitiesDescriptor searchEntitiesDescriptor, final String memberType) throws SBonitaReadException { if (ProfileMemberUtils.USER_TYPE.equals(memberType)) { return searchEntitiesDescriptor.getSearchProfileMemberUserDescriptor(); } else if (ProfileMemberUtils.GROUP_TYPE.equals(memberType)) { return searchEntitiesDescriptor.getSearchProfileMemberGroupDescriptor(); } else if (ProfileMemberUtils.ROLE_TYPE.equals(memberType)) { return searchEntitiesDescriptor.getSearchProfileMemberRoleDescriptor(); } else if (ProfileMemberUtils.ROLE_AND_GROUP_TYPE.equals(memberType)) { return searchEntitiesDescriptor.getSearchProfileMemberRoleAndGroupDescriptor(); } else { throw new SBonitaReadException("Member type must be equalse to user,group,role or roleAndGroup."); } } private String getProfileMemberQuerySuffix(final String memberType) throws SBonitaReadException { if (ProfileMemberUtils.USER_TYPE.equals(memberType)) { return ProfileMemberUtils.USER_SUFFIX; } else if (ProfileMemberUtils.GROUP_TYPE.equals(memberType)) { return ProfileMemberUtils.GROUP_SUFFIX; } else if (ProfileMemberUtils.ROLE_TYPE.equals(memberType)) { return ProfileMemberUtils.ROLE_SUFFIX; } else if (ProfileMemberUtils.ROLE_AND_GROUP_TYPE.equals(memberType)) { return ProfileMemberUtils.ROLE_AND_GROUP_SUFFIX; } else { throw new SBonitaReadException("Member type must be equalse to user,group,role or roleAndGroup."); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/ActorBusinessArchiveArtifactManager.java ================================================ /** * Copyright (C) 2015 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.api.impl.resolver; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.SActorDeletionException; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.api.impl.transaction.actor.ExportActorMapping; import org.bonitasoft.engine.api.impl.transaction.actor.ImportActorMapping; import org.bonitasoft.engine.bpm.actor.ActorMappingImportException; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.bpm.process.Problem.Level; import org.bonitasoft.engine.bpm.process.impl.internal.ProblemImpl; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.core.process.definition.model.SActorDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ @Slf4j public class ActorBusinessArchiveArtifactManager implements BusinessArchiveArtifactManager { private final ActorMappingService actorMappingService; private final IdentityService identityService; public ActorBusinessArchiveArtifactManager(ActorMappingService actorMappingService, IdentityService identityService) { this.actorMappingService = actorMappingService; this.identityService = identityService; } @Override public boolean deploy(final BusinessArchive businessArchive, final SProcessDefinition processDefinition) throws ActorMappingImportException { final Set actors = processDefinition.getActors(); final Set sActors = new HashSet<>(actors.size() + 1); final SActorDefinition actorInitiator = processDefinition.getActorInitiator(); String initiatorName = null; if (actorInitiator != null) { initiatorName = actorInitiator.getName(); sActors.add(SActor.builder().name(initiatorName).scopeId(processDefinition.getId()).initiator(true) .description(actorInitiator.getDescription()).build()); } for (final SActorDefinition actor : actors) { if (initiatorName == null || !initiatorName.equals(actor.getName())) { sActors.add((SActor.builder().name(actor.getName()).scopeId(processDefinition.getId()).initiator(false) .description(actor.getDescription())).build()); } } try { actorMappingService.addActors(sActors); ActorMapping actorMapping = businessArchive.getActorMapping(); if (actorMapping != null) { final ImportActorMapping importActorMapping = new ImportActorMapping(actorMappingService, identityService); importActorMapping.execute(actorMapping, processDefinition.getId()); } // ignored } catch (SBonitaException e) { log.warn("Error in importing the actor-mapping: {}", e.getMessage()); } return checkResolution(actorMappingService, processDefinition.getId()).isEmpty(); } @Override public List checkResolution(final SProcessDefinition processDefinition) { final long processDefinitionId = processDefinition.getId(); return checkResolution(actorMappingService, processDefinitionId); } @Override public void delete(SProcessDefinition processDefinition) throws SObjectModificationException { try { actorMappingService.deleteActors(processDefinition.getId()); } catch (SActorDeletionException e) { throw new SObjectModificationException( "Unable to delete actors of the process definition <" + processDefinition.getName() + ">", e); } } @Override public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder) throws SBonitaException { final ExportActorMapping exportActorMapping = new ExportActorMapping(actorMappingService, identityService, processDefinitionId); businessArchiveBuilder.setActorMapping(exportActorMapping.getActorMapping()); } public List checkResolution(final ActorMappingService actorMappingService, final long processDefinitionId) { try { final List problems = new ArrayList(); QueryOptions queryOptions = new QueryOptions(0, 100, SActor.class, "id", OrderByType.ASC); List actors = actorMappingService.getActors(processDefinitionId, queryOptions); while (!actors.isEmpty()) { for (final SActor sActor : actors) { checkIfAActorMemberExists(actorMappingService, problems, sActor); } queryOptions = QueryOptions.getNextPage(queryOptions); actors = actorMappingService.getActors(processDefinitionId, queryOptions); } return problems; } catch (final SBonitaReadException e) { return Collections.singletonList( (Problem) new ProblemImpl(Level.ERROR, processDefinitionId, "process", "Unable to read actors")); } } private void checkIfAActorMemberExists(final ActorMappingService actorMappingService, final List problems, final SActor sActor) throws SBonitaReadException { final List actorMembers = actorMappingService.getActorMembers(sActor.getId(), 0, 1); if (actorMembers.isEmpty()) { final Problem problem = new ProblemImpl(Level.ERROR, sActor.getId(), "actor", "Actor '" + sActor.getName() + "' does not contain any members"); problems.add(problem); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/BARResourceArtifactManager.java ================================================ /** * 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.api.impl.resolver; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.resources.BARResourceType; import org.bonitasoft.engine.resources.ProcessResourcesService; import org.bonitasoft.engine.resources.SBARResource; /** * @author Baptiste Mesta */ public abstract class BARResourceArtifactManager implements BusinessArchiveArtifactManager { protected final ProcessResourcesService processResourcesService; public BARResourceArtifactManager(ProcessResourcesService processResourcesService) { this.processResourcesService = processResourcesService; } void saveResources(BusinessArchive businessArchive, SProcessDefinition processDefinition, String folder, BARResourceType type) throws SRecorderException { final Map resources = businessArchive.getResources("^" + folder + "/.*$"); for (Map.Entry entry : resources.entrySet()) { processResourcesService.add(processDefinition.getId(), entry.getKey().substring((folder + "/").length()), type, entry.getValue()); } } void exportResourcesToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder, BARResourceType type) throws SBonitaReadException { List resources; int from = 0; final int pageSize = 10; do { resources = processResourcesService.get(processDefinitionId, type, from, pageSize); addToBusinessArchive(businessArchiveBuilder, resources); from += pageSize; } while (resources.size() == pageSize); } void addToBusinessArchive(BusinessArchiveBuilder businessArchiveBuilder, List resources) { for (SBARResource resource : resources) { addToBusinessArchive(businessArchiveBuilder, new BarResource(resource.getName(), resource.getContent())); } } abstract void addToBusinessArchive(BusinessArchiveBuilder businessArchiveBuilder, BarResource resource); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/BusinessArchiveArtifactManager.java ================================================ /** * 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.api.impl.resolver; import java.util.List; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public interface BusinessArchiveArtifactManager { /** * deploy a dedicated part of the process * e.g. load connectors * Must throw an exception is something is not resolved in the process * * @param businessArchive * the business archive containing the dependency * @param processDefinition * the process definition * @return true if the process is resolved for this deployer, false otherwise * @throws BonitaException */ boolean deploy(BusinessArchive businessArchive, SProcessDefinition processDefinition) throws BonitaException, SBonitaException; /** * @param processDefinition * the process definition * @return * a list of resolution problems or an empty list is there is no issue for this artifact */ List checkResolution(final SProcessDefinition processDefinition); void delete(final SProcessDefinition processDefinition) throws SObjectModificationException, SBonitaReadException, SRecorderException; void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder) throws SBonitaException; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/BusinessArchiveArtifactsManager.java ================================================ /** * 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.api.impl.resolver; import java.util.List; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; 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.builder.SProcessDefinitionDeployInfoUpdateBuilderFactory; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.service.ServiceAccessor; /** * Handles the resolution of Process Dependencies. A process can have a list of ProcessDependencyResolvers * which validates different aspects of the * process to validate (or "resolve") * * @author Emmanuel Duchastenier * @author Matthieu Chaffotte * @author Celine Souchet */ @Slf4j public class BusinessArchiveArtifactsManager { private final List dependencyResolvers; public BusinessArchiveArtifactsManager(final List dependencyResolvers) { this.dependencyResolvers = dependencyResolvers; } public boolean resolveDependencies(final BusinessArchive businessArchive, final SProcessDefinition sDefinition) { final List artifactManagers = getArtifactManagers(); boolean resolved = true; for (final BusinessArchiveArtifactManager artifactManager : artifactManagers) { try { resolved &= artifactManager.deploy(businessArchive, sDefinition); if (!resolved) { for (Problem problem : artifactManager.checkResolution(sDefinition)) { log.info(problem.getDescription()); } } } catch (BonitaException | SBonitaException e) { // not logged, we will check later why the process is not resolved log.error("Unable to deploy process", e); resolved = false; } } return resolved; } public void resolveDependenciesForAllProcesses(ServiceAccessor serviceAccessor) { try { List processDefinitionIds = serviceAccessor.getProcessDefinitionService().getProcessDefinitionIds(0, Integer.MAX_VALUE); resolveDependencies(processDefinitionIds, serviceAccessor); } catch (SBonitaReadException e) { log.error("Unable to retrieve tenant process definitions, dependency resolution aborted"); } } private void resolveDependencies(final List processDefinitionIds, final ServiceAccessor serviceAccessor) { for (Long id : processDefinitionIds) { resolveDependencies(id, serviceAccessor); } } public void deleteDependencies(final SProcessDefinition processDefinition) throws SObjectModificationException, SBonitaReadException, SRecorderException { final List resolvers = getArtifactManagers(); for (BusinessArchiveArtifactManager resolver : resolvers) { resolver.delete(processDefinition); } } /* * Done in a separated transaction * We try here to check if now the process is resolved so it must not be done in the same transaction that did the * modification * this does not throw exception, it only log because it can be retried after. */ public void resolveDependencies(final long processDefinitionId, final ServiceAccessor serviceAccessor) { resolveDependencies(processDefinitionId, serviceAccessor, getArtifactManagers().toArray(new BusinessArchiveArtifactManager[0])); } public void resolveDependencies(final long processDefinitionId, final ServiceAccessor serviceAccessor, final BusinessArchiveArtifactManager... resolvers) { final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); try { boolean resolved = true; for (final BusinessArchiveArtifactManager dependencyResolver : resolvers) { final SProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); resolved &= dependencyResolver.checkResolution(processDefinition).isEmpty(); } changeResolutionStatus(processDefinitionId, processDefinitionService, resolved); } catch (final SBonitaException e) { if (log.isDebugEnabled()) { log.debug(e.toString()); } log.warn("Unable to resolve dependencies after they were modified because of " + e.getMessage() + ". Please retry it manually"); } } public void changeResolutionStatus(final long processDefinitionId, final ProcessDefinitionService processDefinitionService, final boolean resolved) throws SBonitaException { final SProcessDefinitionDeployInfo processDefinitionDeployInfo = processDefinitionService .getProcessDeploymentInfo(processDefinitionId); if (resolved) { if (ConfigurationState.UNRESOLVED.name().equals(processDefinitionDeployInfo.getConfigurationState())) { processDefinitionService.resolveProcess(processDefinitionId); } } else { if (ConfigurationState.RESOLVED.name().equals(processDefinitionDeployInfo.getConfigurationState())) { final EntityUpdateDescriptor updateDescriptor = BuilderFactory .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class).createNewInstance() .updateConfigurationState(ConfigurationState.UNRESOLVED).done(); processDefinitionService.updateProcessDefinitionDeployInfo(processDefinitionId, updateDescriptor); } } } public List getArtifactManagers() { return dependencyResolvers; } public BusinessArchive exportBusinessArchive(long processDefinitionId, DesignProcessDefinition designProcessDefinition) throws InvalidBusinessArchiveFormatException, SBonitaException { final BusinessArchiveBuilder businessArchiveBuilder = new BusinessArchiveBuilder().createNewBusinessArchive(); businessArchiveBuilder.setProcessDefinition(designProcessDefinition); for (BusinessArchiveArtifactManager businessArchiveArtifactManager : getArtifactManagers()) { businessArchiveArtifactManager.exportToBusinessArchive(processDefinitionId, businessArchiveBuilder); } return businessArchiveBuilder.done(); } public List getProcessResolutionProblems(SProcessDefinition processDefinition) { return getArtifactManagers().stream().flatMap(resolver -> resolver.checkResolution(processDefinition).stream()) .collect(Collectors.toList()); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/BusinessDataBusinessArchiveArtifactManager.java ================================================ /** * 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.api.impl.resolver; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.bpm.process.Problem.Level; import org.bonitasoft.engine.bpm.process.impl.internal.ProblemImpl; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; /** * @author Matthieu Chaffotte */ public class BusinessDataBusinessArchiveArtifactManager implements BusinessArchiveArtifactManager { private final BusinessDataRepository businessDataRepository; public BusinessDataBusinessArchiveArtifactManager(BusinessDataRepository businessDataRepository) { this.businessDataRepository = businessDataRepository; } @Override public boolean deploy(final BusinessArchive businessArchive, final SProcessDefinition processDefinition) { return checkResolution(processDefinition).isEmpty(); } @Override public List checkResolution(final SProcessDefinition processDefinition) { final List businessDataDefinitions = processDefinition.getProcessContainer() .getBusinessDataDefinitions(); if (businessDataDefinitions.isEmpty()) { return Collections.emptyList(); } final List problems = new ArrayList<>(); final Set entityClassNames = businessDataRepository.getEntityClassNames(); for (final SBusinessDataDefinition sBusinessDataDefinition : businessDataDefinitions) { final String className = sBusinessDataDefinition.getClassName(); if (!entityClassNames.contains(className)) { final Problem problem = new ProblemImpl(Level.ERROR, sBusinessDataDefinition.getName(), "business data", String.format( "Unknown Business Object type '%s'. Deploy a Business Data Model defining this type first.", className)); problems.add(problem); } } return problems; } @Override public void delete(SProcessDefinition processDefinition) throws SObjectModificationException { } @Override public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder) throws SBonitaException { } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/ClasspathArtifactManager.java ================================================ /** * Copyright (C) 2015 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.api.impl.resolver; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.connector.ConnectorException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.dependency.DependencyService; import org.bonitasoft.engine.dependency.SDependencyException; import org.bonitasoft.engine.dependency.model.AbstractSDependency; import org.bonitasoft.engine.dependency.model.SDependencyMapping; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.QueryOptions; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class ClasspathArtifactManager implements BusinessArchiveArtifactManager { private final DependencyService dependencyService; public ClasspathArtifactManager(DependencyService dependencyService) { this.dependencyService = dependencyService; } @Override public boolean deploy(final BusinessArchive businessArchive, final SProcessDefinition processDefinition) throws ConnectorException, SBonitaException { final Map resources = businessArchive.getResources("^classpath/.*$"); // remove the classpath/ on path of dependencies final Map resourcesWithRealName = new HashMap<>(resources.size()); for (final Map.Entry resource : resources.entrySet()) { final String name = resource.getKey().substring(10); final byte[] jarContent = resource.getValue(); resourcesWithRealName.put(name, jarContent); } addDependencies(resourcesWithRealName, dependencyService, processDefinition.getId()); return true; } @Override public List checkResolution(final SProcessDefinition processDefinition) { return Collections.emptyList(); } @Override public void delete(SProcessDefinition processDefinition) throws SObjectModificationException { try { dependencyService.deleteDependencies(processDefinition.getId(), ScopeType.PROCESS); } catch (SDependencyException e) { throw new SObjectModificationException( "Unable to delete dependencies of process " + processDefinition.getId(), e); } } @Override public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder) throws SBonitaException { final ArrayList filters = new ArrayList<>(); filters.add(new FilterOption(SDependencyMapping.class, "artifactId", processDefinitionId)); filters.add(new FilterOption(SDependencyMapping.class, "artifactType", ScopeType.PROCESS.name())); final List dependencyMappings = dependencyService .getDependencyMappings(new QueryOptions(0, Integer.MAX_VALUE, null, filters, null)); for (SDependencyMapping dependencyMapping : dependencyMappings) { final AbstractSDependency dependency = dependencyService.getDependency(dependencyMapping.getDependencyId()); businessArchiveBuilder .addClasspathResource(new BarResource(dependency.getFileName(), dependency.getValue())); } } private void addDependencies(final Map resources, final DependencyService dependencyService, final long processDefinitionId) throws SBonitaException { final List dependencyIds = getDependencyMappingsOfProcess(dependencyService, processDefinitionId); final List dependencies = getDependenciesOfProcess(dependencyService, dependencyIds); for (Map.Entry entry : resources.entrySet()) { if (!dependencies.contains(getDependencyName(processDefinitionId, entry.getKey()))) { final String name = entry.getKey(); dependencyService.createMappedDependency(name, entry.getValue(), name /* it is the real filename */, processDefinitionId, ScopeType.PROCESS); } } } private String getDependencyName(final long processDefinitionId, final String name) { return processDefinitionId + "_" + name; } private List getDependenciesOfProcess(final DependencyService dependencyService, final List dependencyIds) throws SBonitaException { if (dependencyIds.isEmpty()) { return Collections.emptyList(); } final List dependencies = dependencyService.getDependencies(dependencyIds); final ArrayList dependencyNames = new ArrayList<>(dependencies.size()); for (final AbstractSDependency sDependency : dependencies) { dependencyNames.add(sDependency.getName()); } return dependencyNames; } private List getDependencyMappingsOfProcess(final DependencyService dependencyService, final long processDefinitionId) throws SDependencyException { final List dependencyIds = new ArrayList<>(); int fromIndex = 0; final int pageSize = 100; List currentPage; do { currentPage = dependencyService.getDependencyIds(processDefinitionId, ScopeType.PROCESS, fromIndex, pageSize); dependencyIds.addAll(currentPage); fromIndex += pageSize; } while (currentPage.size() == pageSize); return dependencyIds; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/ConnectorBusinessArchiveArtifactManager.java ================================================ /** * 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.api.impl.resolver; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.bpm.process.Problem.Level; import org.bonitasoft.engine.bpm.process.impl.internal.ProblemImpl; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.connector.ConnectorException; import org.bonitasoft.engine.core.connector.ConnectorService; import org.bonitasoft.engine.core.connector.exception.SConnectorException; import org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.resources.SBARResource; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class ConnectorBusinessArchiveArtifactManager implements BusinessArchiveArtifactManager { public static final String CONNECTOR = "connector"; public static final int BATCH_SIZE = 10; private final ConnectorService connectorService; public ConnectorBusinessArchiveArtifactManager(ConnectorService connectorService) { this.connectorService = connectorService; } void addToBusinessArchive(BusinessArchiveBuilder businessArchiveBuilder, List resources) { for (SBARResource resource : resources) { businessArchiveBuilder .addConnectorImplementation(new BarResource(resource.getName(), resource.getContent())); } } @Override public boolean deploy(final BusinessArchive businessArchive, final SProcessDefinition processDefinition) throws ConnectorException, SRecorderException { try { final Map resources = businessArchive.getResources("^" + CONNECTOR + "/.*$"); for (Map.Entry entry : resources.entrySet()) { connectorService.addConnectorImplementation(processDefinition.getId(), entry.getKey().substring((CONNECTOR + "/").length()), entry.getValue()); } return connectorService.loadConnectors(processDefinition) && checkAllConnectorsHaveImplementation(connectorService, processDefinition).isEmpty(); } catch (final SConnectorException e) { throw new ConnectorException(e); } } private List checkAllConnectorsHaveImplementation(final ConnectorService connectorService, final SProcessDefinition processDefinition) { final List processConnectors = processDefinition.getProcessContainer().getConnectors(); final List problems = new ArrayList(); if (processConnectors != null) { for (final SConnectorDefinition sConnectorDefinition : processConnectors) { try { connectorService.getConnectorImplementation(processDefinition.getId(), sConnectorDefinition.getConnectorId(), sConnectorDefinition.getVersion()); } catch (final SConnectorException e) { final Problem problem = new ProblemImpl(Level.ERROR, sConnectorDefinition.getName(), "connector", "The process connector '" + sConnectorDefinition.getName() + "' has no implementation"); problems.add(problem); } } } final Set flowNodes = processDefinition.getProcessContainer().getFlowNodes(); if (flowNodes != null) { for (final SFlowNodeDefinition sFlowNodeDefinition : flowNodes) { final List flowNodeConnectors = sFlowNodeDefinition.getConnectors(); if (flowNodeConnectors != null) { for (final SConnectorDefinition sConnectorDefinition : flowNodeConnectors) { try { connectorService.getConnectorImplementation(processDefinition.getId(), sConnectorDefinition.getConnectorId(), sConnectorDefinition.getVersion()); } catch (final SConnectorException e) { final Problem problem = new ProblemImpl(Level.ERROR, sConnectorDefinition.getName(), "connector", "The connector '" + sConnectorDefinition.getName() + "' of flow node '" + sFlowNodeDefinition.getName() + "' has no implementation"); problems.add(problem); } } } } } return problems; } @Override public List checkResolution(final SProcessDefinition processDefinition) { return checkAllConnectorsHaveImplementation(connectorService, processDefinition); } @Override public void delete(SProcessDefinition processDefinition) throws SObjectModificationException, SBonitaReadException, SRecorderException { connectorService.removeConnectorImplementations(processDefinition.getId()); } @Override public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder) throws SBonitaException { List resources = getConnectorImplementations(processDefinitionId); addToBusinessArchive(businessArchiveBuilder, resources); } List getConnectorImplementations(long processDefinitionId) throws SBonitaReadException { List allResources = new ArrayList<>(); List resources; int from = 0; do { resources = connectorService.getConnectorImplementations(processDefinitionId, from, BATCH_SIZE); from += BATCH_SIZE; allResources.addAll(resources); } while (resources.size() == BATCH_SIZE); return allResources; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/DocumentInitialValueArtifactManager.java ================================================ /** * 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.api.impl.resolver; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.resources.BARResourceType; import org.bonitasoft.engine.resources.ProcessResourcesService; /** * @author Baptiste Mesta */ public class DocumentInitialValueArtifactManager extends BARResourceArtifactManager { public static final String FOLDER = "documents"; public DocumentInitialValueArtifactManager(ProcessResourcesService processResourcesService) { super(processResourcesService); } @Override public boolean deploy(BusinessArchive businessArchive, SProcessDefinition processDefinition) throws BonitaException, SBonitaException { saveResources(businessArchive, processDefinition, FOLDER, BARResourceType.DOCUMENT); return true; } @Override public List checkResolution(SProcessDefinition processDefinition) { return Collections.emptyList(); } @Override public void delete(SProcessDefinition processDefinition) throws SObjectModificationException, SBonitaReadException, SRecorderException { processResourcesService.removeAll(processDefinition.getId(), BARResourceType.DOCUMENT); } @Override public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder) throws SBonitaException { exportResourcesToBusinessArchive(processDefinitionId, businessArchiveBuilder, BARResourceType.DOCUMENT); } @Override void addToBusinessArchive(BusinessArchiveBuilder businessArchiveBuilder, BarResource resource) { businessArchiveBuilder.addDocumentResource(resource); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/ExternalResourceArtifactManager.java ================================================ /** * 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.api.impl.resolver; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.resources.BARResourceType; import org.bonitasoft.engine.resources.ProcessResourcesService; /** * @author Baptiste Mesta */ public class ExternalResourceArtifactManager extends BARResourceArtifactManager { public static final String RESOURCES = "resources"; public ExternalResourceArtifactManager(ProcessResourcesService processResourcesService) { super(processResourcesService); } @Override void addToBusinessArchive(BusinessArchiveBuilder businessArchiveBuilder, BarResource resource) { businessArchiveBuilder.addExternalResource(resource); } @Override public boolean deploy(BusinessArchive businessArchive, SProcessDefinition processDefinition) throws BonitaException, SBonitaException { saveResources(businessArchive, processDefinition, RESOURCES, BARResourceType.EXTERNAL); return true; } @Override public List checkResolution(SProcessDefinition processDefinition) { return Collections.emptyList(); } @Override public void delete(SProcessDefinition processDefinition) throws SObjectModificationException, SBonitaReadException, SRecorderException { processResourcesService.removeAll(processDefinition.getId(), BARResourceType.EXTERNAL); } @Override public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder) throws SBonitaException { exportResourcesToBusinessArchive(processDefinitionId, businessArchiveBuilder, BARResourceType.EXTERNAL); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/FormMappingAndPageArtifactManager.java ================================================ /** * 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.api.impl.resolver; import static org.bonitasoft.engine.form.FormMappingType.PROCESS_OVERVIEW; import static org.bonitasoft.engine.form.FormMappingType.PROCESS_START; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.impl.converter.PageModelConverter; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.bar.form.model.FormMappingDefinition; import org.bonitasoft.engine.bpm.bar.form.model.FormMappingModel; import org.bonitasoft.engine.bpm.flownode.ActivityDefinition; import org.bonitasoft.engine.bpm.flownode.FlowElementContainerDefinition; import org.bonitasoft.engine.bpm.flownode.HumanTaskDefinition; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.bpm.process.ProcessDeployException; import org.bonitasoft.engine.bpm.process.SubProcessDefinition; import org.bonitasoft.engine.bpm.process.impl.internal.ProblemImpl; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.core.form.FormMappingService; import org.bonitasoft.engine.core.form.SFormMapping; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.form.FormMapping; import org.bonitasoft.engine.form.FormMappingTarget; import org.bonitasoft.engine.form.FormMappingType; import org.bonitasoft.engine.page.*; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.service.FormRequiredAnalyzer; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; /** * @author Laurent Leseigneur */ @Slf4j public class FormMappingAndPageArtifactManager implements BusinessArchiveArtifactManager { public static final String ERROR_MESSAGE_FORM_NOT_SET = "Error while resolving form mapping for processDefinitionId=%s and task=%s. The target bonita form is not defined"; public static final String ERROR_MESSAGE_FORM_NOT_FOUND = "Error while resolving form mapping %s. The target bonita form with ID %s does not exist."; public static final String ERROR_MESSAGE_FORM_UNDEFINED = "Error while resolving form mapping processDefinitionId=%s and task=%s. The target bonita form is explicitly not yet defined but IS necessary for the process to be resolved"; private static final String REGEX = "^resources/customPages/(custompage_.*)\\.(zip)$"; private final SessionService sessionService; private final SessionAccessor sessionAccessor; private final PageService pageService; private final FormMappingService formMappingService; private final ProcessDefinitionService processDefinitionService; public static final int NUMBER_OF_RESULTS = 100; public FormMappingAndPageArtifactManager(SessionService sessionService, SessionAccessor sessionAccessor, PageService pageService, FormMappingService formMappingService, ProcessDefinitionService processDefinitionService) { this.sessionService = sessionService; this.sessionAccessor = sessionAccessor; this.pageService = pageService; this.formMappingService = formMappingService; this.processDefinitionService = processDefinitionService; } @Override public boolean deploy(final BusinessArchive businessArchive, final SProcessDefinition processDefinition) throws ProcessDeployException { deployProcessPages(businessArchive, processDefinition.getId(), sessionService.getLoggedUserFromSession(sessionAccessor)); deployFormMappings(businessArchive, processDefinition.getId()); return checkResolution(processDefinition).isEmpty(); } public void deployProcessPages(BusinessArchive businessArchive, Long processDefinitionId, long userId) { final Map pageResources = getPageResources(businessArchive); for (final Map.Entry resource : pageResources.entrySet()) { try { // TODO: pages are stored twice in Database: once as as page and once as an external resource (in ExternalResourceArtifactManager). // Remove this notion of external resource for custom pages. deployPage(resource.getKey(), resource.getValue(), processDefinitionId, userId, pageService); } catch (SBonitaException e) { log.error("Unable to deploy all pages", e); } } } protected Map getPageResources(BusinessArchive businessArchive) { return businessArchive.getResources(REGEX); } private void deployPage(String resourcePath, byte[] pageContent, Long processDefinitionId, long userId, PageService pageService) throws SBonitaException { final Matcher pathMatcher = getPathMatcher(resourcePath); if (pathMatcher.matches()) { final String pageName = pathMatcher.group(1); final String extension = pathMatcher.group(2); String contentName = pageName + "." + extension; final SPage sPage = pageService.getPageByNameAndProcessDefinitionId(pageName, processDefinitionId); if (sPage != null) { pageService.updatePageContent(sPage.getId(), pageContent, contentName); } else { final Properties pageProperties = pageService.readPageZip(pageContent); final PageCreator pageCreator = new PageCreator(pageName, contentName, ContentType.FORM, processDefinitionId) .setDisplayName(pageProperties.getProperty(PageService.PROPERTIES_DISPLAY_NAME)) .setDescription(pageProperties.getProperty(PageService.PROPERTIES_DESCRIPTION)); final SPage newPage = new PageModelConverter().constructSPage(pageCreator, userId); pageService.addPage(newPage, pageContent); } } } private Matcher getPathMatcher(String resourcePath) { final Pattern pattern = Pattern.compile(REGEX); return pattern.matcher(resourcePath); } @Override public List checkResolution(final SProcessDefinition processDefinition) { List problems = new ArrayList<>(); try { problems = checkPageProcessResolution(processDefinition); } catch (SBonitaReadException | SObjectNotFoundException e) { problems.add( new ProblemImpl(Problem.Level.ERROR, null, null, "unable to resolve form mapping dependencies")); } return problems; } @Override public void delete(SProcessDefinition processDefinition) throws SObjectModificationException { try { deleteFormMapping(processDefinition.getId()); deleteProcessPages(processDefinition.getId()); } catch (SBonitaReadException | SObjectNotFoundException e) { throw new SObjectModificationException( "Unable to delete forms and pages of the process definition <" + processDefinition.getName() + ">", e); } } @Override public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder) throws SBonitaException { // TODO: when custom pages stop being external resources, add them here: final FormMappingModel formMappingModel = new FormMappingModel(); final List formMappings = formMappingService.list(processDefinitionId, 0, Integer.MAX_VALUE); for (SFormMapping sFormMapping : formMappings) { final FormMapping formMapping = ModelConvertor.toFormMapping(sFormMapping, new FormRequiredAnalyzer(processDefinitionService)); String form = null; switch (formMapping.getTarget()) { case INTERNAL: if (formMapping.getPageId() != null) { final SPage page = pageService.getPage(formMapping.getPageId()); form = page.getName(); } break; case URL: form = formMapping.getURL(); break; } final FormMappingDefinition mapping = new FormMappingDefinition(form, formMapping.getType(), formMapping.getTarget(), formMapping.getTask()); formMappingModel.addFormMapping(mapping); } businessArchiveBuilder.setFormMappings(formMappingModel); } protected void deleteFormMapping(Long processDefinitionId) throws SBonitaReadException, SObjectModificationException { List formMappings; do { formMappings = formMappingService.list(processDefinitionId, 0, NUMBER_OF_RESULTS); for (SFormMapping formMapping : formMappings) { formMappingService.delete(formMapping); } } while (formMappings.size() == NUMBER_OF_RESULTS); } private void deleteProcessPages(Long processDefinitionId) throws SBonitaReadException, SObjectModificationException, SObjectNotFoundException { List sPages; do { sPages = pageService.getPageByProcessDefinitionId(processDefinitionId, 0, NUMBER_OF_RESULTS); for (SPage sPage : sPages) { pageService.deletePage(sPage.getId()); } } while (sPages.size() == NUMBER_OF_RESULTS); } protected List checkPageProcessResolution(SProcessDefinition sProcessDefinition) throws SBonitaReadException, SObjectNotFoundException { final List problems = new ArrayList<>(); List formMappings; do { formMappings = formMappingService.list(sProcessDefinition.getId(), 0, NUMBER_OF_RESULTS); for (SFormMapping formMapping : formMappings) { checkFormMappingResolution(formMapping, problems); } } while (formMappings.size() == NUMBER_OF_RESULTS); return problems; } protected void checkFormMappingResolution(SFormMapping formMapping, List problems) throws SBonitaReadException, SObjectNotFoundException { String errorMessage; if (isMappingRelatedToCustomPage(formMapping)) { SPageMapping pageMapping = formMapping.getPageMapping(); if (pageMapping == null) { errorMessage = String.format(ERROR_MESSAGE_FORM_NOT_SET, formMapping.getProcessDefinitionId(), formMapping.getTask()); addProblem(formMapping, problems, errorMessage); return; } final Long pageId = pageMapping.getPageId(); if (pageId == null || pageService.getPage(pageId) == null) { errorMessage = String.format(ERROR_MESSAGE_FORM_NOT_FOUND, pageMapping.getKey(), pageId); addProblem(formMapping, problems, errorMessage); } } else if (isUndefined(formMapping)) { errorMessage = String.format(ERROR_MESSAGE_FORM_UNDEFINED, formMapping.getProcessDefinitionId(), formMapping.getTask()); addProblem(formMapping, problems, errorMessage); } } private void addProblem(SFormMapping formMapping, List problems, String errorMessage) { problems.add(new ProblemImpl(Problem.Level.ERROR, formMapping.getProcessElementName(), "form mapping", errorMessage)); } private boolean isMappingRelatedToCustomPage(SFormMapping formMapping) { return FormMappingTarget.INTERNAL.name().equals(formMapping.getTarget()); } private boolean isUndefined(SFormMapping formMapping) { return FormMappingTarget.UNDEFINED.name().equals(formMapping.getTarget()); } public void deployFormMappings(final BusinessArchive businessArchive, final long processDefinitionId) throws ProcessDeployException { final List formMappings = businessArchive.getFormMappingModel().getFormMappings(); final FlowElementContainerDefinition flowElementContainer = businessArchive.getProcessDefinition() .getFlowElementContainer(); final List activities = flowElementContainer.getActivities(); try { // Deals with human tasks declared in process definition: for (final ActivityDefinition activity : activities) { createFormMapping(processDefinitionId, formMappingService, formMappings, activity); } // Deals with the process start / process overview forms: createFormMapping(formMappingService, processDefinitionId, getFormMappingForType(formMappings, PROCESS_START), PROCESS_START.getId(), null); createFormMapping(formMappingService, processDefinitionId, getFormMappingForType(formMappings, PROCESS_OVERVIEW), PROCESS_OVERVIEW.getId(), null); } catch (final SObjectCreationException | SBonitaReadException e) { throw new ProcessDeployException(e); } } void createFormMapping(long processDefinitionId, FormMappingService formMappingService, List formMappings, ActivityDefinition activity) throws SObjectCreationException, SBonitaReadException { if (isHumanTask(activity)) { // create mapping as declared in form mapping: createFormMapping(formMappingService, processDefinitionId, getFormMappingForHumanTask(activity.getName(), formMappings), FormMappingType.TASK.getId(), activity.getName()); } else if (activity instanceof SubProcessDefinition) { final org.bonitasoft.engine.bpm.flownode.FlowElementContainerDefinition subProcessContainer = ((SubProcessDefinition) activity) .getSubProcessContainer(); for (ActivityDefinition activityDefinition : subProcessContainer.getActivities()) { createFormMapping(processDefinitionId, formMappingService, formMappings, activityDefinition); } } } private void createFormMapping(FormMappingService formMappingService, long processDefinitionId, FormMappingDefinition formMappingDefinition, Integer type, String taskName) throws SObjectCreationException, SBonitaReadException { if (formMappingDefinition != null) { createSFormMapping(formMappingService, processDefinitionId, formMappingDefinition); } else { formMappingService.create(processDefinitionId, taskName, type, FormMappingTarget.NONE.name(), null); } } private SFormMapping createSFormMapping(FormMappingService formMappingService, long processDefinitionId, FormMappingDefinition formMappingDefinition) throws SObjectCreationException, SBonitaReadException { return formMappingService.create(processDefinitionId, formMappingDefinition.getTaskname(), formMappingDefinition.getType().getId(), formMappingDefinition.getTarget().name(), formMappingDefinition.getForm()); } private boolean isHumanTask(final ActivityDefinition activity) { return activity instanceof HumanTaskDefinition; } /** * @return the found mapping for the given human task, or null is not found */ private FormMappingDefinition getFormMappingForHumanTask(final String name, final List formMappings) { for (final FormMappingDefinition formMapping : formMappings) { if (name.equals(formMapping.getTaskname())) { return formMapping; } } return null; } private FormMappingDefinition getFormMappingForType(final List formMappings, final FormMappingType type) { for (final FormMappingDefinition formMapping : formMappings) { if (type == formMapping.getType()) { return formMapping; } } return null; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/ParameterBusinessArchiveArtifactManager.java ================================================ /** * 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.api.impl.resolver; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.bpm.process.Problem.Level; import org.bonitasoft.engine.bpm.process.impl.internal.ProblemImpl; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.core.process.definition.model.SParameterDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.parameter.OrderBy; import org.bonitasoft.engine.parameter.ParameterService; import org.bonitasoft.engine.parameter.SParameter; import org.bonitasoft.engine.parameter.SParameterProcessNotFoundException; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class ParameterBusinessArchiveArtifactManager implements BusinessArchiveArtifactManager { private final ParameterService parameterService; public ParameterBusinessArchiveArtifactManager(ParameterService parameterService) { this.parameterService = parameterService; } @Override public boolean deploy(final BusinessArchive businessArchive, final SProcessDefinition processDefinition) throws CreationException { final Set declaredParameters = processDefinition.getParameters(); boolean resolved = true; if (declaredParameters.isEmpty()) { return true; } final Map defaultParameterValues = businessArchive.getParameters(); final Map storedParameters = new HashMap<>(); for (final SParameterDefinition sParameterDefinition : declaredParameters) { final String name = sParameterDefinition.getName(); final String value = defaultParameterValues.get(sParameterDefinition.getName()); if (value == null) { resolved = false; } storedParameters.put(name, value); } try { parameterService.addAll(processDefinition.getId(), storedParameters); } catch (SBonitaException e) { throw new CreationException(e); } return resolved; } @Override public List checkResolution(final SProcessDefinition processDefinition) { if (processDefinition.getParameters().isEmpty()) { return Collections.emptyList(); } int NUMBER_OF_RESULT = 100; List parameters; final ArrayList problems = new ArrayList<>(); int i = 0; do { try { parameters = parameterService.getNullValues(processDefinition.getId(), i, NUMBER_OF_RESULT, OrderBy.NAME_ASC); } catch (final SBonitaException e) { return Collections .singletonList(new ProblemImpl(Level.ERROR, null, "parameter", "Unable to get parameter !!")); } i += NUMBER_OF_RESULT; for (final SParameter parameter : parameters) { if (parameter.getValue() == null) { final Problem problem = new ProblemImpl(Level.ERROR, null, "parameter", "Parameter '" + parameter.getName() + "' is not set."); problems.add(problem); } } } while (parameters.size() == NUMBER_OF_RESULT); return problems; } @Override public void delete(SProcessDefinition processDefinition) throws SObjectModificationException { try { parameterService.deleteAll(processDefinition.getId()); } catch (SParameterProcessNotFoundException | SBonitaReadException e) { throw new SObjectModificationException( "Unable to delete parameters of the process definition <" + processDefinition.getName() + ">", e); } } @Override public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder) throws SBonitaException { final List sParameter = parameterService.get(processDefinitionId, 0, Integer.MAX_VALUE, null); Map map = new HashMap<>(); for (SParameter parameter : sParameter) { map.put(parameter.getName(), parameter.getValue()); } businessArchiveBuilder.setParameters(map); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/resolver/UserFilterBusinessArchiveArtifactManager.java ================================================ /** * 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.api.impl.resolver; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveBuilder; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.core.filter.UserFilterService; import org.bonitasoft.engine.core.filter.exception.SUserFilterLoadingException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.filter.UserFilterException; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.resources.BARResourceType; import org.bonitasoft.engine.resources.ProcessResourcesService; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class UserFilterBusinessArchiveArtifactManager extends BARResourceArtifactManager { private final UserFilterService userFilterService; public UserFilterBusinessArchiveArtifactManager(UserFilterService userFilterService, ProcessResourcesService processResourcesService) { super(processResourcesService); this.userFilterService = userFilterService; } @Override public boolean deploy(final BusinessArchive businessArchive, final SProcessDefinition processDefinition) throws UserFilterException, SRecorderException { try { saveResources(businessArchive, processDefinition, "userFilters", BARResourceType.USER_FILTER); return userFilterService.loadUserFilters(processDefinition.getId()); } catch (final SUserFilterLoadingException e) { throw new UserFilterException(e); } } @Override public List checkResolution(final SProcessDefinition processDefinition) { return Collections.emptyList(); } @Override public void delete(SProcessDefinition processDefinition) throws SObjectModificationException, SBonitaReadException, SRecorderException { userFilterService.removeUserFilters(processDefinition.getId()); } @Override public void exportToBusinessArchive(long processDefinitionId, BusinessArchiveBuilder businessArchiveBuilder) throws SBonitaException { exportResourcesToBusinessArchive(processDefinitionId, businessArchiveBuilder, BARResourceType.USER_FILTER); } @Override void addToBusinessArchive(BusinessArchiveBuilder businessArchiveBuilder, BarResource resource) { businessArchiveBuilder.addUserFilters(resource); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/AbstractGetEntity.java ================================================ /** * 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.api.impl.transaction; import java.io.Serializable; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.Sort; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * Abstract class to allow to search server object and convert them to client object * * @author Celine Souchet * @param * The client object * @param * The server object */ public abstract class AbstractGetEntity implements TransactionContentWithResult> { private final SearchEntityDescriptor searchDescriptor; private final int numberOfResults; private final int fromIndex; private final Sort sort; private List clientObjects; /** * @param searchDescriptor * The descriptor corresponding to the searched object * @param fromIndex * The specified position to begin the search * @param numberOfResults * The number of elements to return * @param sort * How to sort the result */ public AbstractGetEntity(final SearchEntityDescriptor searchDescriptor, final int fromIndex, final int numberOfResults, final Sort sort) { this.searchDescriptor = searchDescriptor; this.numberOfResults = numberOfResults; this.fromIndex = fromIndex; this.sort = sort; } @Override public void execute() throws SBonitaException { if (searchDescriptor == null || sort == null) { throw new SBonitaReadException("Sort and SearchDescriptor cannot be null"); } final OrderByOption orderByOption = searchDescriptor.getEntityOrder(sort); final QueryOptions searchOptions = new QueryOptions(fromIndex, numberOfResults, Collections.singletonList(orderByOption)); List serverObjects = executeGet(searchOptions); clientObjects = convertToClientObjects(serverObjects); } /** * Execute the search here * * @param queryOptions * The query options to execute the search with * @return The searched result * @throws SBonitaException */ public abstract List executeGet(final QueryOptions queryOptions) throws SBonitaException; /** * Must convert server objects in client objects here * * @param serverObjects * The server object to convert * @return The list of the client objects */ public abstract List convertToClientObjects(final List serverObjects); @Override public List getResult() { return clientObjects; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/CustomTransactions.java ================================================ /** * 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.api.impl.transaction; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Indicates that a Bonita API method handles transactions itself, and thus does not need to use the generic transaction * mechanism. * * @author Emmanuel Duchastenier */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface CustomTransactions { } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/activity/GetActivityInstances.java ================================================ /** * 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.api.impl.transaction.activity; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.persistence.OrderByType; /** * @author Baptiste Mesta */ public class GetActivityInstances implements TransactionContentWithResult> { private List activities; private final ActivityInstanceService activityInstanceService; private final long processInstanceUUID; private final int pageIndex; private final int numberPerPage; private final String field; private final OrderByType order; public GetActivityInstances(final ActivityInstanceService activityInstanceService, final long processInstanceUUID, final int pageIndex, final int numberPerPage, final String field, final OrderByType order) { this.activityInstanceService = activityInstanceService; this.processInstanceUUID = processInstanceUUID; this.pageIndex = pageIndex; this.numberPerPage = numberPerPage; this.field = field; this.order = order; } @Override public void execute() throws SBonitaException { activities = activityInstanceService.getOpenActivityInstances(processInstanceUUID, pageIndex, numberPerPage, field, order); } @Override public List getResult() { return activities; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/activity/GetArchivedActivityInstance.java ================================================ /** * 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.api.impl.transaction.activity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class GetArchivedActivityInstance implements TransactionContentWithResult { private final ActivityInstanceService activityInstanceService; private final int stateId; private final long activityInstanceId; private SAActivityInstance activity; public GetArchivedActivityInstance(final ActivityInstanceService activityInstanceService, final long activityInstanceId) { this.activityInstanceService = activityInstanceService; this.activityInstanceId = activityInstanceId; stateId = -1; } public GetArchivedActivityInstance(final long activityInstanceId, final int stateId, final ActivityInstanceService activityInstanceService) { this.activityInstanceId = activityInstanceId; this.stateId = stateId; this.activityInstanceService = activityInstanceService; } @Override public void execute() throws SBonitaException { if (stateId > -1) { activity = activityInstanceService.getArchivedActivityInstance(activityInstanceId, stateId); } else { activity = activityInstanceService.getMostRecentArchivedActivityInstance(activityInstanceId); } } @Override public SAActivityInstance getResult() { return activity; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/activity/GetArchivedActivityInstances.java ================================================ /** * 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.api.impl.transaction.activity; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; /** * @author Baptiste Mesta * @author Celine Souchet */ public class GetArchivedActivityInstances implements TransactionContentWithResult> { private final ActivityInstanceService activityInstanceService; private final long processInstanceId; private final int pageIndex; private final int numberPerPage; private final String field; private final OrderByType order; private List archivedActivityInstances; public GetArchivedActivityInstances(final ActivityInstanceService activityInstanceService, final long processInstanceId, final int pageIndex, final int numberPerPage, final String field, final OrderByType order) { this.activityInstanceService = activityInstanceService; this.processInstanceId = processInstanceId; this.pageIndex = pageIndex; this.numberPerPage = numberPerPage; this.field = field; this.order = order; } @Override public void execute() throws SBonitaException { final QueryOptions queryOptions = new QueryOptions(pageIndex * numberPerPage, numberPerPage, SAActivityInstance.class, field, order); archivedActivityInstances = activityInstanceService.getArchivedActivityInstances(processInstanceId, queryOptions); } @Override public List getResult() { return archivedActivityInstances; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/activity/GetContractOfUserTaskInstance.java ================================================ /** * 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.api.impl.transaction.activity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SContractDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SUserTaskDefinition; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; /** * @author Matthieu Chaffotte */ public class GetContractOfUserTaskInstance implements TransactionContentWithResult { private final ProcessDefinitionService definitionService; private final SUserTaskInstance userTaskInstance; private SContractDefinition contract; public GetContractOfUserTaskInstance(final ProcessDefinitionService definitionService, final SUserTaskInstance userTaskInstance) { this.definitionService = definitionService; this.userTaskInstance = userTaskInstance; } @Override public void execute() throws SBonitaException { final SProcessDefinition processDefinition = definitionService .getProcessDefinition(userTaskInstance.getProcessDefinitionId()); final SUserTaskDefinition userTaskDefinition = (SUserTaskDefinition) processDefinition.getProcessContainer() .getFlowNode( userTaskInstance.getFlowNodeDefinitionId()); contract = userTaskDefinition.getContract(); } @Override public SContractDefinition getResult() { return contract; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/activity/GetNumberOfActivityInstance.java ================================================ /** * 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.api.impl.transaction.activity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; /** * @author Emmanuel Duchastenier */ public class GetNumberOfActivityInstance implements TransactionContentWithResult { private final ActivityInstanceService activityInstanceService; private int number; private final long processInstanceId; public GetNumberOfActivityInstance(final long processInstanceId, final ActivityInstanceService activityInstanceService) { this.processInstanceId = processInstanceId; this.activityInstanceService = activityInstanceService; } @Override public void execute() throws SBonitaException { number = activityInstanceService.getNumberOfOpenActivityInstances(processInstanceId); } @Override public Integer getResult() { return number; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/AddActor.java ================================================ /** * 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.api.impl.transaction.actor; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; /** * @author Zhao Na */ public class AddActor implements TransactionContent { private final ActorMappingService actorMappingService; private final SActor actor; public AddActor(final ActorMappingService actorMappingService, final SActor actor) { super(); this.actorMappingService = actorMappingService; this.actor = actor; } @Override public void execute() throws SBonitaException { actorMappingService.addActor(actor); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/AddActorMember.java ================================================ /** * 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.api.impl.transaction.actor; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.identity.MemberType; /** * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros */ public class AddActorMember implements TransactionContent { private final ActorMappingService actorMappingService; private final long actorId; private final long userId; private final long groupId; private final long roleId; private SActorMember actorMember; private final MemberType memberType; public AddActorMember(final ActorMappingService actorMappingService, final long actorId, final long userId, final long groupId, final long roleId, final MemberType memberType) { super(); this.actorMappingService = actorMappingService; this.actorId = actorId; this.userId = userId; this.groupId = groupId; this.roleId = roleId; this.memberType = memberType; } @Override public void execute() throws SBonitaException { switch (memberType) { case USER: actorMember = actorMappingService.addUserToActor(actorId, userId); break; case GROUP: actorMember = actorMappingService.addGroupToActor(actorId, groupId); break; case ROLE: actorMember = actorMappingService.addRoleToActor(actorId, roleId); break; case MEMBERSHIP: actorMember = actorMappingService.addRoleAndGroupToActor(actorId, roleId, groupId); break; default: throw new IllegalStateException(); } } public SActorMember getActorMember() { return actorMember; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/ExportActorMapping.java ================================================ /** * 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.api.impl.transaction.actor; import java.util.List; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.bpm.bar.ActorMappingMarshaller; import org.bonitasoft.engine.bpm.bar.XmlMarshallException; import org.bonitasoft.engine.bpm.bar.actorMapping.Actor; import org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SGroupNotFoundException; import org.bonitasoft.engine.identity.SRoleNotFoundException; import org.bonitasoft.engine.identity.SUserNotFoundException; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Matthieu Chaffotte */ public class ExportActorMapping implements TransactionContentWithResult { private final ActorMappingService actorMappingService; private final IdentityService identityService; private final long processDefinitionId; private String xmlContent; public ExportActorMapping(final ActorMappingService actorMappingService, final IdentityService identityService, final long processDefinitionId) { super(); this.actorMappingService = actorMappingService; this.identityService = identityService; this.processDefinitionId = processDefinitionId; } @Override public void execute() throws SBonitaException { final ActorMapping mapping = getActorMapping(); ActorMappingMarshaller marshaller = new ActorMappingMarshaller(); try { xmlContent = new String(marshaller.serializeToXML(mapping)); } catch (XmlMarshallException e) { throw new SBonitaReadException("Failed to generate xml from actorMapping", e); } } public ActorMapping getActorMapping() throws SBonitaException { final ActorMapping actorMapping = new ActorMapping(); QueryOptions queryOptions = new QueryOptions(0, 100, SActor.class, "id", OrderByType.ASC); List actors = actorMappingService.getActors(processDefinitionId, queryOptions); while (!actors.isEmpty()) { for (final SActor sActor : actors) { final Actor actor = new Actor(sActor.getName()); final List actorMembers = actorMappingService.getActorMembers(sActor.getId(), 0, Integer.MAX_VALUE); for (final SActorMember sActorMember : actorMembers) { addUser(actor, sActorMember); addGroup(actor, sActorMember); addRole(actor, sActorMember); addMembership(actor, sActorMember); } actorMapping.addActor(actor); } queryOptions = QueryOptions.getNextPage(queryOptions); actors = actorMappingService.getActors(processDefinitionId, queryOptions); } return actorMapping; } private void addUser(final Actor actor, final SActorMember sActorMember) throws SUserNotFoundException { if (sActorMember.getUserId() > 0) { final SUser user = identityService.getUser(sActorMember.getUserId()); actor.addUser(user.getUserName()); } } private void addGroup(final Actor actor, final SActorMember sActorMember) throws SGroupNotFoundException { if (sActorMember.getGroupId() > 0 && sActorMember.getRoleId() <= 0) { final SGroup group = identityService.getGroup(sActorMember.getGroupId()); actor.addGroup(group.getPath()); } } private void addRole(final Actor actor, final SActorMember sActorMember) throws SRoleNotFoundException { if (sActorMember.getRoleId() > 0 && sActorMember.getGroupId() <= 0) { final SRole role = identityService.getRole(sActorMember.getRoleId()); actor.addRole(role.getName()); } } private void addMembership(final Actor actor, final SActorMember sActorMember) throws SRoleNotFoundException, SGroupNotFoundException { if (sActorMember.getRoleId() > 0 && sActorMember.getGroupId() > 0) { final SRole role = identityService.getRole(sActorMember.getRoleId()); final SGroup group = identityService.getGroup(sActorMember.getGroupId()); actor.addMembership(group.getPath(), role.getName()); } } @Override public String getResult() { return xmlContent; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetActor.java ================================================ /** * 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.api.impl.transaction.actor; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; /** * @author Matthieu Chaffotte */ public class GetActor implements TransactionContentWithResult { private final ActorMappingService actorMappingService; private final long actorId; private final String actorName; private final long scopeId; private SActor actor; public GetActor(final ActorMappingService actorMappingService, final long actorId) { super(); this.actorMappingService = actorMappingService; this.actorId = actorId; actorName = null; scopeId = -1; } public GetActor(final ActorMappingService actorMappingService, final String actorName, final long scopeId) { super(); this.actorMappingService = actorMappingService; this.actorName = actorName; this.scopeId = scopeId; actorId = -1; } @Override public void execute() throws SBonitaException { if (actorId > 0) { actor = actorMappingService.getActor(actorId); } else { actor = actorMappingService.getActor(actorName, scopeId); } } @Override public SActor getResult() { return actor; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetActorInitiators.java ================================================ /** * 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.api.impl.transaction.actor; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; /** * @author Feng Hui */ public class GetActorInitiators implements TransactionContentWithResult> { private final ActorMappingService actorMappingService; private final Set actorIds; private final List sActors = new ArrayList(); public GetActorInitiators(final ActorMappingService actorMappingService, final Set actorIds) { this.actorMappingService = actorMappingService; this.actorIds = actorIds; } @Override public void execute() throws SBonitaException { for (final Iterator iterator = actorIds.iterator(); iterator.hasNext();) { final SActor sActor = actorMappingService.getActor(iterator.next()); if (sActor.isInitiator()) { sActors.add(sActor); } } } @Override public List getResult() { return sActors; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetActorsByActorIds.java ================================================ /** * 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.api.impl.transaction.actor; import java.util.List; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; /** * @author Zhao Na */ public class GetActorsByActorIds implements TransactionContentWithResult> { private final ActorMappingService actorMappingService; private final List actorIds; private List actors; public GetActorsByActorIds(final ActorMappingService actorMappingService, final List actorIds) { this.actorMappingService = actorMappingService; this.actorIds = actorIds; } @Override public void execute() throws SBonitaException { actors = actorMappingService.getActors(actorIds); } @Override public List getResult() { return actors; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetNumberOfActorMembers.java ================================================ /** * 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.api.impl.transaction.actor; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; /** * @author Matthieu Chaffotte */ public class GetNumberOfActorMembers implements TransactionContentWithResult { private final ActorMappingService actorMappingService; private final long actorId; private long numberOfActorMembers; public GetNumberOfActorMembers(final ActorMappingService actorMappingService, final long actorId) { this.actorMappingService = actorMappingService; this.actorId = actorId; } @Override public void execute() throws SBonitaException { numberOfActorMembers = actorMappingService.getNumberOfActorMembers(actorId); } @Override public Long getResult() { return numberOfActorMembers; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetNumberOfActors.java ================================================ /** * 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.api.impl.transaction.actor; import java.util.Set; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SActorDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; /** * @author Yanyan Liu * @author Emmanuel Duchastenier */ public class GetNumberOfActors implements TransactionContentWithResult { private final ProcessDefinitionService processDefinitionService; private final long processDefinitionId; private int numberOfActors; public GetNumberOfActors(final ProcessDefinitionService processDefinitionService, final long processDefinitionId) { this.processDefinitionService = processDefinitionService; this.processDefinitionId = processDefinitionId; } @Override public void execute() throws SBonitaException { final SProcessDefinition definition = processDefinitionService.getProcessDefinition(processDefinitionId); SActorDefinition actorInitiator = definition.getActorInitiator(); Set actors = definition.getActors(); numberOfActors = actors.size(); if ((actorInitiator != null) && !actors.contains(actorInitiator)) { numberOfActors++; } } @Override public Integer getResult() { return numberOfActors; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetNumberOfGroupsOfActor.java ================================================ /** * 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.api.impl.transaction.actor; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; /** * @author Celine Souchet */ public class GetNumberOfGroupsOfActor implements TransactionContentWithResult { private final ActorMappingService actorMappingService; private final long actorId; private long numberOfGroups; public GetNumberOfGroupsOfActor(final ActorMappingService actorMappingService, final long actorId) { this.actorMappingService = actorMappingService; this.actorId = actorId; } @Override public void execute() { numberOfGroups = actorMappingService.getNumberOfGroupsOfActor(actorId); } @Override public Long getResult() { return numberOfGroups; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetNumberOfMembershipsOfActor.java ================================================ /** * 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.api.impl.transaction.actor; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; /** * @author Celine Souchet * @author Matthieu Chaffotte */ public class GetNumberOfMembershipsOfActor implements TransactionContentWithResult { private final ActorMappingService actorMappingService; private final long actorId; private long numberOfUserMemberships; public GetNumberOfMembershipsOfActor(final ActorMappingService actorMappingService, final long actorId) { this.actorMappingService = actorMappingService; this.actorId = actorId; } @Override public void execute() { numberOfUserMemberships = actorMappingService.getNumberOfMembershipsOfActor(actorId); } @Override public Long getResult() { return numberOfUserMemberships; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetNumberOfRolesOfActor.java ================================================ /** * 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.api.impl.transaction.actor; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; /** * @author Celine Souchet */ public class GetNumberOfRolesOfActor implements TransactionContentWithResult { private final ActorMappingService actorMappingService; private final long actorId; private long numberOfRoles; public GetNumberOfRolesOfActor(final ActorMappingService actorMappingService, final long actorId) { this.actorMappingService = actorMappingService; this.actorId = actorId; } @Override public void execute() { numberOfRoles = actorMappingService.getNumberOfRolesOfActor(actorId); } @Override public Long getResult() { return numberOfRoles; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/GetNumberOfUsersOfActor.java ================================================ /** * 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.api.impl.transaction.actor; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; /** * @author Celine Souchet */ public class GetNumberOfUsersOfActor implements TransactionContentWithResult { private final ActorMappingService actorMappingService; private final long actorId; private long numberOfUsers; public GetNumberOfUsersOfActor(final ActorMappingService actorMappingService, final long actorId) { this.actorMappingService = actorMappingService; this.actorId = actorId; } @Override public void execute() { numberOfUsers = actorMappingService.getNumberOfUsersOfActor(actorId); } @Override public Long getResult() { return numberOfUsers; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/ImportActorMapping.java ================================================ /** * 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.api.impl.transaction.actor; import java.util.List; import java.util.Set; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.SActorMemberAlreadyExistsException; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.bpm.bar.ActorMappingMarshaller; import org.bonitasoft.engine.bpm.bar.XmlMarshallException; import org.bonitasoft.engine.bpm.bar.actorMapping.Actor; import org.bonitasoft.engine.bpm.bar.actorMapping.ActorMapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Matthieu Chaffotte */ public class ImportActorMapping { private final ActorMappingService actorMappingService; private final IdentityService identityService; public ImportActorMapping(final ActorMappingService actorMappingService, IdentityService identityService) { this.actorMappingService = actorMappingService; this.identityService = identityService; } public void importActorMappingFromXml(String xmlContent, long processDefinitionId) throws SBonitaException { ActorMapping actorMapping = getActorMappingFromXML(xmlContent); execute(actorMapping, processDefinitionId); } public void execute(ActorMapping actorMapping, long processDefinitionId) throws SBonitaException { final List actors = actorMapping.getActors(); for (final Actor actor : actors) { final SActor sActor = actorMappingService.getActor(actor.getName(), processDefinitionId); final long actorId = sActor.getId(); final Set userNames = actor.getUsers(); for (final String userName : userNames) { final SUser user = identityService.getUserByUserName(userName); checkAlreadyExistingUserMapping(actorId, user.getId()); actorMappingService.addUserToActor(actorId, user.getId()); } final Set roleNames = actor.getRoles(); for (final String roleName : roleNames) { final SRole role = identityService.getRoleByName(roleName); checkAlreadyExistingRoleMapping(actorId, role.getId()); actorMappingService.addRoleToActor(actorId, role.getId()); } final Set groupPaths = actor.getGroups(); for (final String groupPath : groupPaths) { final SGroup group = identityService.getGroupByPath(groupPath); checkAlreadyExistingGroupMapping(actorId, group.getId()); actorMappingService.addGroupToActor(actorId, group.getId()); } final Set memberships = actor.getMemberships(); for (final Actor.Membership membership : memberships) { final SGroup group = identityService.getGroupByPath(membership.getGroup()); final SRole role = identityService.getRoleByName(membership.getRole()); checkAlreadyExistingMembershipMapping(actorId, group.getId(), role.getId()); actorMappingService.addRoleAndGroupToActor(actorId, role.getId(), group.getId()); } } } private void checkAlreadyExistingUserMapping(final long actorId, final long userId) throws SActorMemberAlreadyExistsException, SBonitaReadException { List actorMembersOfUser; int startIndex = 0; do { actorMembersOfUser = actorMappingService.getActorMembers(actorId, startIndex, 50); for (final SActorMember sActorMember : actorMembersOfUser) { if (sActorMember.getUserId() == userId && sActorMember.getRoleId() == -1 && sActorMember.getGroupId() == -1) { throw new SActorMemberAlreadyExistsException( "This user / actor mapping already exists: actorId=" + actorId + ", userId=" + userId); } } startIndex += 50; } while (actorMembersOfUser.size() > 0); } private void checkAlreadyExistingGroupMapping(final long actorId, final long groupId) throws SActorMemberAlreadyExistsException, SBonitaReadException { List actorMembersOfGroup; int startIndex = 0; do { actorMembersOfGroup = actorMappingService.getActorMembers(actorId, startIndex, 50); for (final SActorMember sActorMember : actorMembersOfGroup) { if (sActorMember.getGroupId() == groupId && sActorMember.getRoleId() == -1 && sActorMember.getUserId() == -1) { throw new SActorMemberAlreadyExistsException( "This group / actor mapping already exists: actorId=" + actorId + ", groupId=" + groupId); } } startIndex += 50; } while (actorMembersOfGroup.size() > 0); } private void checkAlreadyExistingRoleMapping(final long actorId, final long roleId) throws SActorMemberAlreadyExistsException, SBonitaReadException { List actorMembersOfRole; int startIndex = 0; do { actorMembersOfRole = actorMappingService.getActorMembers(actorId, startIndex, 50); for (final SActorMember sActorMember : actorMembersOfRole) { if (sActorMember.getRoleId() == roleId && sActorMember.getGroupId() == -1 && sActorMember.getUserId() == -1) { throw new SActorMemberAlreadyExistsException( "This role / actor mapping already exists: actorId=" + actorId + ", roleId=" + roleId); } } startIndex += 50; } while (actorMembersOfRole.size() > 0); } private void checkAlreadyExistingMembershipMapping(final long actorId, final long groupId, final long roleId) throws SActorMemberAlreadyExistsException, SBonitaReadException { List actorMembersOfMembership; int startIndex = 0; do { actorMembersOfMembership = actorMappingService.getActorMembers(actorId, startIndex, 50); for (final SActorMember sActorMember : actorMembersOfMembership) { if (sActorMember.getRoleId() == roleId && sActorMember.getGroupId() == groupId) { throw new SActorMemberAlreadyExistsException( "This membership / actor mapping already exists: actorId=" + actorId + ", groupId=" + groupId + ", roleId=" + roleId); } } startIndex += 50; } while (actorMembersOfMembership.size() > 0); } private ActorMapping getActorMappingFromXML(String xmlContent) throws SBonitaException { byte[] b = xmlContent.getBytes(); try { return new ActorMappingMarshaller().deserializeFromXML(b); } catch (XmlMarshallException e) { throw new SBonitaReadException("Unable to read the actor mapping xml", e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/actor/RemoveActorMember.java ================================================ /** * 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.api.impl.transaction.actor; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; /** * @author Matthieu Chaffotte * @author Emmanuel Duchastenier */ public class RemoveActorMember implements TransactionContentWithResult { private final ActorMappingService actorMappingService; private final long actorMemberId; private SActorMember removedMember; public RemoveActorMember(final ActorMappingService actorMappingService, final long actorMemberId) { this.actorMappingService = actorMappingService; this.actorMemberId = actorMemberId; } @Override public void execute() throws SBonitaException { removedMember = actorMappingService.deleteActorMember(actorMemberId); } @Override public SActorMember getResult() { return removedMember; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/application/SearchApplicationMenus.java ================================================ /** * 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.api.impl.transaction.application; import java.util.List; import org.bonitasoft.engine.api.impl.converter.ApplicationMenuModelConverter; import org.bonitasoft.engine.business.application.ApplicationMenu; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Elias Ricken de Medeiros */ public class SearchApplicationMenus extends AbstractSearchEntity { private final ApplicationService applicationService; private final ApplicationMenuModelConverter convertor; public SearchApplicationMenus(final ApplicationService applicationService, final ApplicationMenuModelConverter convertor, final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); this.applicationService = applicationService; this.convertor = convertor; } @Override public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException { return applicationService.getNumberOfApplicationMenus(queryOptions); } @Override public List executeSearch(final QueryOptions queryOptions) throws SBonitaReadException { return applicationService.searchApplicationMenus(queryOptions); } @Override public List convertToClientObjects(final List serverObjects) throws SBonitaException { return convertor.toApplicationMenu(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/application/SearchApplicationPages.java ================================================ /** * 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.api.impl.transaction.application; import java.util.List; import org.bonitasoft.engine.api.impl.converter.ApplicationPageModelConverter; import org.bonitasoft.engine.business.application.ApplicationPage; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Elias Ricken de Medeiros */ public class SearchApplicationPages extends AbstractSearchEntity { private final ApplicationService applicationService; private final ApplicationPageModelConverter convertor; public SearchApplicationPages(final ApplicationService applicationService, final ApplicationPageModelConverter convertor, final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); this.applicationService = applicationService; this.convertor = convertor; } @Override public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException { return applicationService.getNumberOfApplicationPages(queryOptions); } @Override public List executeSearch(final QueryOptions queryOptions) throws SBonitaReadException { return applicationService.searchApplicationPages(queryOptions); } @Override public List convertToClientObjects(final List serverObjects) { return convertor.toApplicationPage(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/application/SearchApplications.java ================================================ /** * 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.api.impl.transaction.application; import java.util.List; import org.bonitasoft.engine.api.impl.converter.ApplicationModelConverter; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.IApplication; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Elias Ricken de Medeiros */ public class SearchApplications extends AbstractSearchEntity { private final Class applicationClass; private final ApplicationService applicationService; private final ApplicationModelConverter convertor; public static SearchApplications defaultSearchApplications( final ApplicationService applicationService, final SearchEntityDescriptor searchDescriptor, final SearchOptions options, final ApplicationModelConverter convertor) { return new SearchApplications(IApplication.class, applicationService, searchDescriptor, options, convertor); } public SearchApplications(final Class applicationClass, final ApplicationService applicationService, final SearchEntityDescriptor searchDescriptor, final SearchOptions options, final ApplicationModelConverter convertor) { super(searchDescriptor, options); this.applicationClass = applicationClass; this.applicationService = applicationService; this.convertor = convertor; } @Override public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException { return applicationService.getNumberOfApplications(queryOptions); } @Override public List executeSearch(final QueryOptions queryOptions) throws SBonitaReadException { return applicationService.searchApplications(queryOptions); } @Override public List convertToClientObjects(final List serverObjects) { if (IApplication.class.equals(applicationClass)) { return (List) convertor.toApplication(serverObjects); } else { return convertor.toApplication(serverObjects).stream().filter(applicationClass::isInstance) .map(applicationClass::cast).toList(); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/application/SearchApplicationsOfUser.java ================================================ /** * 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.api.impl.transaction.application; import java.util.List; import org.bonitasoft.engine.api.impl.converter.ApplicationModelConverter; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.IApplication; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; public class SearchApplicationsOfUser extends AbstractSearchEntity { private final Class applicationClass; private long userId; private final ApplicationService applicationService; private final ApplicationModelConverter convertor; public static SearchApplicationsOfUser defaultSearchApplicationsOfUser( final long userId, final ApplicationService applicationService, final SearchEntityDescriptor searchDescriptor, final SearchOptions options, final ApplicationModelConverter convertor) { return new SearchApplicationsOfUser(IApplication.class, userId, applicationService, searchDescriptor, options, convertor); } public SearchApplicationsOfUser(final Class applicationClass, final long userId, final ApplicationService applicationService, final SearchEntityDescriptor searchDescriptor, final SearchOptions options, final ApplicationModelConverter convertor) { super(searchDescriptor, options); this.applicationClass = applicationClass; this.userId = userId; this.applicationService = applicationService; this.convertor = convertor; } @Override public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException { return applicationService.getNumberOfApplicationsOfUser(userId, queryOptions); } @Override public List executeSearch(final QueryOptions queryOptions) throws SBonitaReadException { return applicationService.searchApplicationsOfUser(userId, queryOptions); } @Override public List convertToClientObjects(final List serverObjects) { if (IApplication.class.equals(applicationClass)) { return (List) convertor.toApplication(serverObjects); } else { return convertor.toApplication(serverObjects).stream().filter(applicationClass::isInstance) .map(applicationClass::cast).toList(); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/CreateCategory.java ================================================ /** * 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.api.impl.transaction.category; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.category.CategoryService; import org.bonitasoft.engine.core.category.exception.SCategoryAlreadyExistsException; import org.bonitasoft.engine.core.category.exception.SCategoryCreationException; import org.bonitasoft.engine.core.category.model.SCategory; /** * @author Yanyan Liu */ public class CreateCategory implements TransactionContentWithResult { private final CategoryService categoryService; private final String name; private final String description; private SCategory sCategory; public CreateCategory(final String name, final String description, final CategoryService categoryService) { this.name = name; this.description = description; this.categoryService = categoryService; } @Override public void execute() throws SCategoryAlreadyExistsException, SCategoryCreationException { sCategory = categoryService.createCategory(name, description); } @Override public SCategory getResult() { return sCategory; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/DeleteSCategory.java ================================================ /** * 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.api.impl.transaction.category; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.core.category.CategoryService; /** * @author Yanyan Liu */ public class DeleteSCategory implements TransactionContent { private final CategoryService categoryService; private final long categoryId; public DeleteSCategory(final CategoryService categoryService, final long processId) { super(); this.categoryService = categoryService; categoryId = processId; } @Override public void execute() throws SBonitaException { categoryService.deleteCategory(categoryId); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/GetCategories.java ================================================ /** * 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.api.impl.transaction.category; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.category.CategoryService; import org.bonitasoft.engine.core.category.model.SCategory; import org.bonitasoft.engine.persistence.OrderByType; /** * @author Yanyan Liu */ public class GetCategories implements TransactionContentWithResult> { private final int startIndex; private final String fieldExecutor; private final CategoryService categoryService; private final int maxResults; private final OrderByType orderExecutor; private List categoryList; public GetCategories(final int startIndex, final int maxResults, final String fieldExecutor, final CategoryService categoryService, final OrderByType orderExecutor) { super(); this.startIndex = startIndex; this.fieldExecutor = fieldExecutor; this.categoryService = categoryService; this.maxResults = maxResults; this.orderExecutor = orderExecutor; } @Override public void execute() throws SBonitaException { if (fieldExecutor == null) { categoryList = categoryService.getCategories(startIndex, maxResults, "name", OrderByType.ASC); } else { categoryList = categoryService.getCategories(startIndex, maxResults, fieldExecutor, orderExecutor); } } @Override public List getResult() { return categoryList; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/GetCategory.java ================================================ /** * 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.api.impl.transaction.category; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.category.CategoryService; import org.bonitasoft.engine.core.category.model.SCategory; /** * @author Yanyan Liu */ public class GetCategory implements TransactionContentWithResult { private final CategoryService categoryService; private final String categoryName; private final long categoryId; private SCategory sCategory; public GetCategory(final CategoryService categoryService, final String categoryName) { this.categoryService = categoryService; this.categoryName = categoryName; categoryId = -1; } public GetCategory(final CategoryService categoryService, final long categoryId) { this.categoryService = categoryService; this.categoryId = categoryId; categoryName = null; } @Override public void execute() throws SBonitaException { if (categoryName != null) { sCategory = categoryService.getCategoryByName(categoryName); } else { sCategory = categoryService.getCategory(categoryId); } } @Override public SCategory getResult() { return sCategory; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/GetNumberOfCategories.java ================================================ /** * 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.api.impl.transaction.category; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.category.CategoryService; /** * @author Yanyan Liu */ public class GetNumberOfCategories implements TransactionContentWithResult { private final CategoryService categoryService; private long categoryCount; public GetNumberOfCategories(final CategoryService categoryService) { super(); this.categoryService = categoryService; } @Override public void execute() throws SBonitaException { categoryCount = categoryService.getNumberOfCategories(); } @Override public Long getResult() { return categoryCount; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/GetNumberOfCategoriesOfProcess.java ================================================ /** * 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.api.impl.transaction.category; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.category.CategoryService; /** * @author Yanyan Liu */ public class GetNumberOfCategoriesOfProcess implements TransactionContentWithResult { private final CategoryService categoryService; private final long processDefinitionId; private long numberOfCategories; public GetNumberOfCategoriesOfProcess(final CategoryService categoryService, final long processDefinitionId) { super(); this.categoryService = categoryService; this.processDefinitionId = processDefinitionId; } @Override public void execute() throws SBonitaException { numberOfCategories = categoryService.getNumberOfCategoriesOfProcess(processDefinitionId); } @Override public Long getResult() { return numberOfCategories; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/GetNumberOfCategoriesUnrelatedToProcess.java ================================================ /** * 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.api.impl.transaction.category; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.category.CategoryService; /** * @author Celine Souchet */ public class GetNumberOfCategoriesUnrelatedToProcess implements TransactionContentWithResult { private final CategoryService categoryService; private final long processDefinitionId; private long numberOfCategories; public GetNumberOfCategoriesUnrelatedToProcess(final CategoryService categoryService, final long processDefinitionId) { super(); this.categoryService = categoryService; this.processDefinitionId = processDefinitionId; } @Override public void execute() throws SBonitaException { numberOfCategories = categoryService.getNumberOfCategoriesUnrelatedToProcess(processDefinitionId); } @Override public Long getResult() { return numberOfCategories; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/RemoveCategoriesFromProcessDefinition.java ================================================ /** * 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.api.impl.transaction.category; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.core.category.CategoryService; import org.bonitasoft.engine.core.category.exception.SCategoryException; /** * Transaction content to remove a list of categories from a Process definition. * * @author Emmanuel Duchastenier */ public class RemoveCategoriesFromProcessDefinition implements TransactionContent { private final long processDefinitionId; private final CategoryService categoryService; private final List categoryIds; public RemoveCategoriesFromProcessDefinition(final long processDefinitionId, final List categoryIds, final CategoryService categoryService) { this.processDefinitionId = processDefinitionId; this.categoryIds = categoryIds; this.categoryService = categoryService; } @Override public void execute() throws SCategoryException, SBonitaException { categoryService.removeCategoriesFromProcessDefinition(processDefinitionId, categoryIds); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/RemoveProcessDefinitionsOfCategory.java ================================================ /** * 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.api.impl.transaction.category; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.core.category.CategoryService; import org.bonitasoft.engine.core.category.model.SProcessCategoryMapping; import org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilderFactory; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; /** * @author Yanyan Liu * @author Celine Souchet */ public class RemoveProcessDefinitionsOfCategory implements TransactionContent { private final CategoryService categoryService; private final long categoryId; private final long processDefinitionId; public RemoveProcessDefinitionsOfCategory(final CategoryService categoryService, final long categoryId) { this.categoryService = categoryService; this.categoryId = categoryId; processDefinitionId = -1; } public RemoveProcessDefinitionsOfCategory(final long processDefinitionId, final CategoryService categoryService) { this.processDefinitionId = processDefinitionId; this.categoryService = categoryService; categoryId = -1; } @Override public void execute() throws SBonitaException { final FilterOption filterOption; if (categoryId != -1) { filterOption = new FilterOption(SProcessCategoryMapping.class, BuilderFactory.get(SProcessCategoryMappingBuilderFactory.class).getCategoryIdKey(), categoryId); } else { filterOption = new FilterOption(SProcessCategoryMapping.class, BuilderFactory.get(SProcessCategoryMappingBuilderFactory.class).getProcessIdKey(), processDefinitionId); } final OrderByOption order = new OrderByOption(SProcessCategoryMapping.class, BuilderFactory.get(SProcessCategoryMappingBuilderFactory.class).getIdKey(), OrderByType.ASC); final QueryOptions queryOptions = new QueryOptions(0, 100, Collections.singletonList(order), Collections.singletonList(filterOption), null); long deletedProcessCategoryMappings; do { final List processCategoryMappings = categoryService .searchProcessCategoryMappings(queryOptions); deletedProcessCategoryMappings = categoryService.deleteProcessCategoryMappings(processCategoryMappings); } while (deletedProcessCategoryMappings > 0); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/category/UpdateCategory.java ================================================ /** * 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.api.impl.transaction.category; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.core.category.CategoryService; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Yanyan Liu * @author Matthieu Chaffotte */ public class UpdateCategory implements TransactionContent { private final CategoryService categoryService; private final long categoryId; private final EntityUpdateDescriptor updateDescriptor; public UpdateCategory(final CategoryService categoryService, final long categoryId, final EntityUpdateDescriptor updateDescriptor) { super(); this.categoryService = categoryService; this.categoryId = categoryId; this.updateDescriptor = updateDescriptor; } @Override public void execute() throws SBonitaException { categoryService.updateCategory(categoryId, updateDescriptor); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/command/DeleteSCommand.java ================================================ /** * 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.api.impl.transaction.command; import org.bonitasoft.engine.command.CommandService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; /** * @author Matthieu Chaffotte * @author Yanyan Liu */ public class DeleteSCommand implements TransactionContent { private final String name; private final CommandService commandService; private long commandId; public DeleteSCommand(final CommandService commandService, final String name) { this.commandService = commandService; this.name = name; } public DeleteSCommand(final CommandService commandService, final long commandId) { this.commandService = commandService; this.commandId = commandId; name = null; } @Override public void execute() throws SBonitaException { if (name == null) { commandService.delete(commandId); } else { commandService.delete(name); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/command/GetCommands.java ================================================ /** * 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.api.impl.transaction.command; import java.util.List; import org.bonitasoft.engine.command.CommandCriterion; import org.bonitasoft.engine.command.CommandService; import org.bonitasoft.engine.command.model.SCommand; import org.bonitasoft.engine.command.model.SCommandCriterion; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; /** * @author Hongwen Zang * @since 6.0 */ public class GetCommands implements TransactionContentWithResult> { private final CommandService commandService; private final int startIndex; private final int maxResults; private final CommandCriterion sort; private List commands; public GetCommands(final CommandService commandService, final int startIndex, final int maxResults, final CommandCriterion sort) { super(); this.commandService = commandService; this.startIndex = startIndex; this.maxResults = maxResults; this.sort = sort; } @Override public void execute() throws SBonitaException { SCommandCriterion sCommandCriterion = null; if (CommandCriterion.NAME_ASC.equals(sort)) { sCommandCriterion = SCommandCriterion.NAME_ASC; } else { sCommandCriterion = SCommandCriterion.NAME_DESC; } commands = commandService.getUserCommands(startIndex, maxResults, sCommandCriterion); } @Override public List getResult() { return commands; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/connector/GetConnectorImplementation.java ================================================ /** * 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.api.impl.transaction.connector; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.connector.ConnectorService; import org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor; /** * @author Yanyan Liu */ public class GetConnectorImplementation implements TransactionContentWithResult { private final ConnectorService connectorService; private final long processDefinitionId; private final String connectorId; private final String connectorVersion; private SConnectorImplementationDescriptor connectorImplementation; public GetConnectorImplementation(final ConnectorService connectorService, final long processDefinitionId, final String connectorId, final String connectorVersion) { this.connectorService = connectorService; this.processDefinitionId = processDefinitionId; this.connectorId = connectorId; this.connectorVersion = connectorVersion; } @Override public void execute() throws SBonitaException { connectorImplementation = connectorService.getConnectorImplementation(processDefinitionId, connectorId, connectorVersion); } @Override public SConnectorImplementationDescriptor getResult() { return connectorImplementation; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/document/GetDocumentByNameAtProcessInstantiation.java ================================================ /** * 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.api.impl.transaction.document; import java.util.Date; import org.bonitasoft.engine.api.impl.transaction.process.GetArchivedProcessInstanceList; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.model.AbstractSMappedDocument; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; /** * @author Baptiste Mesta * @author Celine Souchet */ public class GetDocumentByNameAtProcessInstantiation implements TransactionContentWithResult { private final DocumentService documentService; private final ProcessInstanceService processInstanceService; private final SearchEntitiesDescriptor searchEntitiesDescriptor; private final long processInstanceId; private AbstractSMappedDocument result; private final String documentName; private final ProcessDefinitionService processDefinitionService; public GetDocumentByNameAtProcessInstantiation(final DocumentService documentService, final ProcessInstanceService processInstanceService, final ProcessDefinitionService processDefinitionService, final SearchEntitiesDescriptor searchEntitiesDescriptor, final long processInstanceId, final String documentName) { this.documentService = documentService; this.processDefinitionService = processDefinitionService; this.processInstanceId = processInstanceId; this.documentName = documentName; this.processInstanceService = processInstanceService; this.searchEntitiesDescriptor = searchEntitiesDescriptor; } @Override public void execute() throws SBonitaException { final GetArchivedProcessInstanceList getArchivedProcessInstanceList = new GetArchivedProcessInstanceList( processInstanceService, processDefinitionService, searchEntitiesDescriptor, processInstanceId, 0, 1, BuilderFactory.get(SAProcessInstanceBuilderFactory.class) .getIdKey(), OrderByType.ASC); getArchivedProcessInstanceList.execute(); final ArchivedProcessInstance saProcessInstance = getArchivedProcessInstanceList.getResult().get(0); final Date startDate = saProcessInstance.getStartDate(); final long startTime = startDate != null ? startDate.getTime() : 0; result = documentService.getMappedDocument(processInstanceId, documentName, startTime); } @Override public AbstractSMappedDocument getResult() { return result; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/event/GetEventInstances.java ================================================ /** * 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.api.impl.transaction.event; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.persistence.OrderByType; /** * @author Elias Ricken de Medeiros */ public final class GetEventInstances implements TransactionContentWithResult> { private final long rootContainerId; private final EventInstanceService eventInstanceService; private final int fromIndex; private final int maxResults; private final String fieldName; private final OrderByType orderByType; private List eventInstances; public GetEventInstances(final EventInstanceService eventInstanceService, final long rootContainerId, final int fromIndex, final int maxResults, final String fieldName, final OrderByType orderByType) { this.rootContainerId = rootContainerId; this.eventInstanceService = eventInstanceService; this.fromIndex = fromIndex; this.maxResults = maxResults; this.fieldName = fieldName; this.orderByType = orderByType; } @Override public void execute() throws SBonitaException { this.eventInstances = this.eventInstanceService.getEventInstances(rootContainerId, fromIndex, maxResults, fieldName, orderByType); } @Override public List getResult() { return this.eventInstances; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/expression/AbstractEvaluateExpressionsInstance.java ================================================ /** * 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.api.impl.transaction.expression; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.expression.Expression; /** * @author Emmanuel Duchastenier */ public abstract class AbstractEvaluateExpressionsInstance { private final EntityMerger entityMerger; public AbstractEvaluateExpressionsInstance(final BusinessDataRepository bdrService) { entityMerger = new EntityMerger(bdrService); } protected String buildName(final Expression exp) { String value = null; if (exp != null) { value = exp.getName() != null ? exp.getName() : exp.getContent(); } return value; } protected Map getPartialContext(final Map> expressions, final Expression exp) { Map partialContext = expressions.get(exp); if (partialContext == null || partialContext.isEmpty()) { return partialContext; } final Map result = new HashMap(); for (final Map.Entry entry : partialContext.entrySet()) { result.put(entry.getKey(), entityMerger.merge(entry.getValue())); } return result; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/expression/EntityMerger.java ================================================ /** * 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.api.impl.transaction.expression; import java.io.Serializable; import java.util.Collection; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.proxy.ServerLazyLoader; import org.bonitasoft.engine.business.data.proxy.ServerProxyfier; /** * @author Romain Bioteau */ public class EntityMerger { private final BusinessDataRepository bdrService; public EntityMerger(final BusinessDataRepository bdrService) { this.bdrService = bdrService; } public Serializable merge(final Serializable value) { if (isACollectionOfEntities(value)) { final Collection collection = (Collection) value; try { @SuppressWarnings("unchecked") final Collection newCollection = collection.getClass().newInstance(); ServerProxyfier proxyfier = new ServerProxyfier(new ServerLazyLoader(bdrService)); for (final Object item : collection) { newCollection.add(proxyfier.proxify((Entity) item)); } return (Serializable) newCollection; } catch (final InstantiationException | IllegalAccessException e) { throw new IllegalStateException(e); } } else if (isAnEntity(value)) { ServerProxyfier proxyfier = new ServerProxyfier(new ServerLazyLoader(bdrService)); return proxyfier.proxify((Entity) value); } else { return value; } } private boolean isAnEntity(final Serializable value) { return value instanceof Entity; } protected boolean isACollectionOfEntities(final Serializable value) { return value instanceof Collection && !((Collection) value).isEmpty() && ((Collection) value).iterator().next() instanceof Entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/expression/EvaluateExpression.java ================================================ /** * 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.api.impl.transaction.expression; import java.io.Serializable; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros */ public class EvaluateExpression implements TransactionContentWithResult { private final ExpressionResolverService expressionResolverService; private final SExpressionContext expressionContext; private final SExpression expression; private Serializable result; public EvaluateExpression(final ExpressionResolverService expressionResolverService, final SExpressionContext expressionContext, final SExpression expression) { this.expressionResolverService = expressionResolverService; this.expressionContext = expressionContext; this.expression = expression; } @Override public void execute() throws SBonitaException { result = (Serializable) expressionResolverService.evaluate(expression, expressionContext); } @Override public Serializable getResult() { return result; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/expression/EvaluateExpressionsDefinitionLevel.java ================================================ /** * 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.api.impl.transaction.expression; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Zhao Na * @author Matthieu Chaffotte */ public class EvaluateExpressionsDefinitionLevel extends AbstractEvaluateExpressionsInstance implements TransactionContentWithResult> { private final Map> expressionsAndTheirPartialContext; private final long processDefinitionId; private final ExpressionResolverService expressionResolver; private final ProcessDefinitionService processDefinitionService; private final Map results = new HashMap(0); public EvaluateExpressionsDefinitionLevel(final Map> expressions, final long processDefinitionId, final ExpressionResolverService expressionResolverService, final ProcessDefinitionService processDefinitionService, final BusinessDataRepository bdrService) { super(bdrService); expressionsAndTheirPartialContext = expressions; this.processDefinitionId = processDefinitionId; expressionResolver = expressionResolverService; this.processDefinitionService = processDefinitionService; } @Override public void execute() throws SBonitaException { // FIXME: call the appropriate method(s) from the right service(s): if (expressionsAndTheirPartialContext != null && !expressionsAndTheirPartialContext.isEmpty()) { // how to deal with containerType and containerId final SExpressionContext context = new SExpressionContext(); if (processDefinitionId != 0) { final SProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); final Set exps = expressionsAndTheirPartialContext.keySet(); for (final Expression exp : exps) { Map inputValues = getPartialContext(expressionsAndTheirPartialContext, exp); if (inputValues == null) { inputValues = new HashMap(); } inputValues.put(SExpressionContext.PROCESS_DEFINITION_KEY, processDefinition); context.setProcessDefinitionId(processDefinitionId); context.setSerializableInputValues(inputValues); final SExpression sexp = ModelConvertor.constructSExpression(exp); final Serializable res = evaluateExpression(context, sexp, processDefinition); results.put(buildName(exp), res); } } } } protected Serializable evaluateExpression(final SExpressionContext context, final SExpression sexp, final SProcessDefinition processDefinition) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { try { return (Serializable) expressionResolver.evaluate(sexp, context); } catch (final SExpressionTypeUnknownException e) { throw enrichExceptionContext(e, processDefinition); } catch (final SExpressionEvaluationException e) { throw enrichExceptionContext(e, processDefinition); } catch (final SExpressionDependencyMissingException e) { throw enrichExceptionContext(e, processDefinition); } catch (final SInvalidExpressionException e) { throw enrichExceptionContext(e, processDefinition); } } private T enrichExceptionContext(final T e, final SProcessDefinition processDefinition) { e.setProcessDefinitionIdOnContext(processDefinition.getId()); e.setProcessDefinitionNameOnContext(processDefinition.getName()); e.setProcessDefinitionVersionOnContext(processDefinition.getVersion()); return e; } @Override public Map getResult() { return results; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/expression/EvaluateExpressionsInstanceLevel.java ================================================ /** * 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.api.impl.transaction.expression; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Zhao Na * @author Matthieu Chaffotte */ public class EvaluateExpressionsInstanceLevel extends AbstractEvaluateExpressionsInstance implements TransactionContentWithResult> { private final Map> expressions; private final long containerId; private final long processDefinitionId; private final String containerType; private final ExpressionResolverService expressionResolver; private final Map results = new HashMap(0); public EvaluateExpressionsInstanceLevel(final Map> expressions, final long containerId, final String containerType, final long processDefinitionId, final ExpressionResolverService expressionService, final BusinessDataRepository bdrService) { super(bdrService); this.expressions = expressions; this.containerId = containerId; expressionResolver = expressionService; this.processDefinitionId = processDefinitionId; this.containerType = containerType; } @Override public void execute() throws SBonitaException { // FIXME: call the appropriate method(s) from the right service(s): if (expressions != null && !expressions.isEmpty()) { final SExpressionContext context = new SExpressionContext(); context.setContainerId(containerId); context.setContainerType(containerType); context.setProcessDefinitionId(processDefinitionId); final Set exps = expressions.keySet(); for (final Expression exp : exps) { final Map partialContext = getPartialContext(expressions, exp); context.setSerializableInputValues(partialContext); final SExpression sexp = ModelConvertor.constructSExpression(exp); final Serializable res = (Serializable) expressionResolver.evaluate(sexp, context); results.put(buildName(exp), res);// MAYBE instead of exp.getNAME } } } @Override public Map getResult() { return results; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/expression/EvaluateExpressionsInstanceLevelAndArchived.java ================================================ /** * 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.api.impl.transaction.expression; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Zhao Na * @author Matthieu Chaffotte */ public class EvaluateExpressionsInstanceLevelAndArchived extends AbstractEvaluateExpressionsInstance implements TransactionContentWithResult> { private final Map> expressions; private final long containerId; private final long processDefinitionId; private final String containerType; private final long time; private final ExpressionResolverService expressionResolver; private final Map results = new HashMap(0); public EvaluateExpressionsInstanceLevelAndArchived(final Map> expressions, final long containerId, final String containerType, final long processDefinitionId, final long time, final ExpressionResolverService expressionService, final BusinessDataRepository bdrService) { super(bdrService); this.expressions = expressions; this.containerId = containerId; expressionResolver = expressionService; this.processDefinitionId = processDefinitionId; this.containerType = containerType; this.time = time; } @Override public void execute() throws SBonitaException { // FIXME: call the appropriate method(s) from the right service(s): if (expressions != null && !expressions.isEmpty()) { // how to deal with containerType and containerId final SExpressionContext context = new SExpressionContext(); context.setContainerId(containerId); context.setContainerType(containerType); context.setProcessDefinitionId(processDefinitionId); context.setTime(time); final Set exps = expressions.keySet(); for (final Expression exp : exps) { final Map partialContext = getPartialContext(expressions, exp); context.setSerializableInputValues(partialContext); final SExpression sexp = ModelConvertor.constructSExpression(exp); final Serializable res = (Serializable) expressionResolver.evaluate(sexp, context); results.put(buildName(exp), res);// MAYBE instead of exp.getNAME } } } @Override public Map getResult() { return results; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/flownode/GetFlowNodeInstance.java ================================================ /** * 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.api.impl.transaction.flownode; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; /** * @author Emmanuel Duchastenier */ public class GetFlowNodeInstance implements TransactionContentWithResult { private SFlowNodeInstance flowNodeInstance = null; private final long flowNodeInstanceId; private final ActivityInstanceService activityInstanceService; public GetFlowNodeInstance(final ActivityInstanceService activityInstanceService, final long flowNodeInstanceId) { this.activityInstanceService = activityInstanceService; this.flowNodeInstanceId = flowNodeInstanceId; } @Override public void execute() throws SBonitaException { flowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId); } @Override public SFlowNodeInstance getResult() { return flowNodeInstance; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/flownode/SearchFlowNodeInstances.java ================================================ /** * 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.api.impl.transaction.flownode; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.QueryOptions; /** * @author Elias Ricken de Medeiros */ public class SearchFlowNodeInstances implements TransactionContentWithResult> { private final FlowNodeInstanceService flowNodeInstanceService; private final QueryOptions queryOptions; private final Class entityClass; private List result; private long count; public SearchFlowNodeInstances(final FlowNodeInstanceService flowNodeInstanceService, final QueryOptions queryOptions, final Class entityClass) { super(); this.flowNodeInstanceService = flowNodeInstanceService; this.queryOptions = queryOptions; this.entityClass = entityClass; } @Override public void execute() throws SBonitaException { result = flowNodeInstanceService.searchFlowNodeInstances(entityClass, queryOptions); final QueryOptions countOptions = new QueryOptions(0, 1, Collections. emptyList(), queryOptions.getFilters(), queryOptions.getMultipleFilter()); count = flowNodeInstanceService.getNumberOfFlowNodeInstances(entityClass, countOptions); } @Override public List getResult() { return result; } public long getCount() { return count; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/flownode/SetExpectedEndDate.java ================================================ /** * 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.api.impl.transaction.flownode; import java.util.Date; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; /** * @author Baptiste Mesta */ public class SetExpectedEndDate implements TransactionContent { private final ActivityInstanceService activityInstanceService; private final long userTaskId; private final Date dueDate; public SetExpectedEndDate(final ActivityInstanceService activityInstanceService, final long userTaskId, final Date dueDate) { this.activityInstanceService = activityInstanceService; this.userTaskId = userTaskId; this.dueDate = dueDate; } @Override public void execute() throws SBonitaException { final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(userTaskId); activityInstanceService.setExpectedEndDate(flowNodeInstance, convertToLong()); } public Long convertToLong() { if (dueDate == null) { return null; } return dueDate.getTime(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/AddUserMembership.java ================================================ /** * 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.api.impl.transaction.identity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserMembership; /** * @author Bole Zhang * @author Matthieu Chaffotte */ public class AddUserMembership implements TransactionContentWithResult { private final IdentityService identityService; private final long userId; private final long groupId; private final long roleId; private final long assignedBy; private SUserMembership userMembership; public AddUserMembership(final long userId, final long groupId, final long roleId, final long assignedBy, final IdentityService identityService) { this.assignedBy = assignedBy; this.identityService = identityService; this.userId = userId; this.groupId = groupId; this.roleId = roleId; } @Override public void execute() throws SBonitaException { // FIXME: if RDBMS has foreign keys, getUser, getRole, getGroup can be omitted: final SUser user = identityService.getUser(userId); final SRole role = identityService.getRole(roleId); final SGroup group = identityService.getGroup(groupId); userMembership = SUserMembership.builder().userId(user.getId()).groupId(group.getId()).roleId(role.getId()) .assignedBy(assignedBy) .assignedDate(System.currentTimeMillis()).build(); identityService.createUserMembership(userMembership); } @Override public SUserMembership getResult() { return userMembership; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/AddUserMemberships.java ================================================ /** * 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.api.impl.transaction.identity; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SUserMembership; /** * @author Lu Kai * @author Matthieu Chaffotte */ public class AddUserMemberships implements TransactionContent { private final List userIds; private final IdentityService identityService; private final long groupId; private final long roleId; private final long currentUserId; public AddUserMemberships(final long groupId, final long roleId, final List userIds, final IdentityService identityService, final long currentUserId) { this.groupId = groupId; this.roleId = roleId; this.userIds = userIds; this.identityService = identityService; this.currentUserId = currentUserId; } @Override public void execute() throws SBonitaException { for (final long userId : userIds) { final SUserMembership userMembership = SUserMembership.builder().userId(userId).groupId(groupId) .roleId(roleId) .assignedBy(currentUserId).build(); identityService.createUserMembership(userMembership); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/DeleteGroup.java ================================================ /** * 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.api.impl.transaction.identity; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.profile.ProfileService; /** * @author Lu Kai * @author Elias Ricken de Medeiros * @author Celine Souchet * @author Emmanuel Duchastenier */ public class DeleteGroup extends DeleteWithActorMembers implements TransactionContent { private final long groupId; public DeleteGroup(final IdentityService identityService, final ActorMappingService actorMappingService, final ProfileService profileService, final long groupId) { super(actorMappingService, profileService, identityService); this.groupId = groupId; } @Override public void execute() throws SBonitaException { deleteMembershipsByGroup(groupId); deleteActorMembersOfGroup(groupId); deleteProfileMembersOfGroup(groupId); getIdentityService().deleteChildrenGroup(groupId); getIdentityService().deleteGroup(groupId); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/DeleteGroups.java ================================================ /** * 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.api.impl.transaction.identity; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.profile.ProfileService; /** * @author Lu Kai * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class DeleteGroups extends DeleteWithActorMembers implements TransactionContent { private final List groupIds; public DeleteGroups(final IdentityService identityService, final ActorMappingService actorMappingService, final ProfileService profileService, final List groupIds) { super(actorMappingService, profileService, identityService); this.groupIds = groupIds; } @Override public void execute() throws SBonitaException { final ArrayList list = new ArrayList(groupIds); while (!list.isEmpty()) { final Long groupId = list.get(0); deleteMembershipsByGroup(groupId); deleteActorMembersOfGroup(groupId); deleteProfileMembersOfGroup(groupId); final List deleteChildrenGroup = getIdentityService().deleteChildrenGroup(groupId); list.removeAll(deleteChildrenGroup); getIdentityService().deleteGroup(groupId); list.remove(0); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/DeleteRole.java ================================================ /** * 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.api.impl.transaction.identity; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.profile.ProfileService; /** * @author Lu Kai * @author Matthieu Chaffotte * @author Emmanuel Duchastenier * @author Celine Souchet */ public class DeleteRole extends DeleteWithActorMembers implements TransactionContent { private final long roleId; public DeleteRole(final IdentityService identityService, final ActorMappingService actorMappingService, final ProfileService profileService, final long roleId) { super(actorMappingService, profileService, identityService); this.roleId = roleId; } @Override public void execute() throws SBonitaException { deleteMembershipsByRole(roleId); deleteActorMembersOfRole(roleId); deleteProfileMembersOfRole(roleId); getIdentityService().deleteRole(roleId); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/DeleteRoles.java ================================================ /** * 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.api.impl.transaction.identity; import java.util.List; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.profile.ProfileService; /** * @author Lu Kai * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class DeleteRoles extends DeleteWithActorMembers implements TransactionContent { private final List roleIds; public DeleteRoles(final IdentityService identityService, final ActorMappingService actorMappingService, final ProfileService profileService, final List roleIds) { super(actorMappingService, profileService, identityService); this.roleIds = roleIds; } @Override public void execute() throws SBonitaException { for (final Long roleId : roleIds) { // FIXME: refactor to use the same code as DeleteRole, same thing for DeleteGroups and DeleteUsers deleteMembershipsByRole(roleId); deleteActorMembersOfRole(roleId); deleteProfileMembersOfRole(roleId); getIdentityService().deleteRole(roleId); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/DeleteUser.java ================================================ /** * 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.api.impl.transaction.identity; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SUserNotFoundException; import org.bonitasoft.engine.profile.ProfileService; /** * @author Lu Kai * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class DeleteUser extends DeleteWithActorMembers implements TransactionContent { private final long userId; private final String userName; public DeleteUser(final IdentityService identityService, final ActorMappingService actorMappingService, final ProfileService profileService, final long userId) { super(actorMappingService, profileService, identityService); this.userId = userId; userName = null; } public DeleteUser(final IdentityService identityService, final ActorMappingService actorMappingService, final ProfileService profileService, final String userName) { super(actorMappingService, profileService, identityService); userId = -1; this.userName = userName; } @Override public void execute() throws SBonitaException { try { long id = userId; if (id == -1) { id = getIdentityService().getUserByUserName(userName).getId(); } deleteUserMembershipsByUser(id); deleteActorMembersOfUser(id); deleteProfileMembersOfUser(id); getIdentityService().deleteUser(id); } catch (SUserNotFoundException notFound) { // not found, don't do anything specific } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/DeleteUsers.java ================================================ /** * 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.api.impl.transaction.identity; import java.util.List; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.profile.ProfileService; /** * @author Lu Kai * @author Matthieu Chaffotte * @author Celine Souchet * @author Emmanuel Duchastenier */ public class DeleteUsers extends DeleteWithActorMembers implements TransactionContent { private final List userIds; public DeleteUsers(final IdentityService identityService, final ActorMappingService actorMappingService, final ProfileService profileService, final List userIds) { super(actorMappingService, profileService, identityService); this.userIds = userIds; } @Override public void execute() throws SBonitaException { for (final Long userId : userIds) { deleteUserMembershipsByUser(userId); deleteActorMembersOfUser(userId); deleteProfileMembersOfUser(userId); getIdentityService().deleteUser(userId); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/DeleteWithActorMembers.java ================================================ /** * 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.api.impl.transaction.identity; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.SActorMemberDeletionException; import org.bonitasoft.engine.actor.mapping.SActorMemberNotFoundException; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SIdentityException; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.profile.ProfileService; import org.bonitasoft.engine.profile.model.SProfileMember; /** * @author Emmanuel Duchastenier * @author Celine Souchet */ public class DeleteWithActorMembers { protected static final int BATCH_SIZE = 100; private final ActorMappingService actorMappingService; private final ProfileService profileService; private final IdentityService identityService; private final Set removedActorIds = new HashSet(); public DeleteWithActorMembers(final ActorMappingService actorMappingService, final ProfileService profileService, final IdentityService identityService) { super(); this.actorMappingService = actorMappingService; this.profileService = profileService; this.identityService = identityService; } public Set getRemovedActorIds() { return removedActorIds; } protected void setActorIdsOfRemovedElements(final SActorMember removedActorMember) { removedActorIds.add(removedActorMember.getActorId()); } protected void deleteActorMembersOfUser(final long userId) throws SActorMemberNotFoundException, SActorMemberDeletionException, SBonitaReadException { List actorMembers = actorMappingService.getActorMembersOfUser(userId, 0, BATCH_SIZE); while (!actorMembers.isEmpty()) { for (final SActorMember sActorMember : actorMembers) { setActorIdsOfRemovedElements(actorMappingService.deleteActorMember(sActorMember.getId())); } actorMembers = actorMappingService.getActorMembersOfUser(userId, 0, BATCH_SIZE); } } protected void deleteProfileMembersOfUser(final long id) throws SBonitaException { List profileMembersOfUser; do { profileMembersOfUser = profileService.getProfileMembersOfUser(id, 0, BATCH_SIZE, SProfileMember.ID, OrderByType.ASC); for (final SProfileMember sProfileMember : profileMembersOfUser) { profileService.deleteProfileMember(sProfileMember); } } while (profileMembersOfUser.size() == BATCH_SIZE); } protected void deleteUserMembershipsByUser(final long id) throws SIdentityException { List sUserMemberships = identityService.getUserMembershipsOfUser(id, 0, BATCH_SIZE); while (!sUserMemberships.isEmpty()) { for (final SUserMembership sUserMembership : sUserMemberships) { identityService.deleteUserMembership(sUserMembership.getId()); } sUserMemberships = identityService.getUserMembershipsOfUser(id, 0, BATCH_SIZE); } } protected void deleteActorMembersOfGroup(final long groupId) throws SActorMemberNotFoundException, SActorMemberDeletionException, SBonitaReadException, SIdentityException { List actorMembers; do { actorMembers = actorMappingService.getActorMembersOfGroup(groupId, 0, BATCH_SIZE); for (final SActorMember sActorMember : actorMembers) { setActorIdsOfRemovedElements(actorMappingService.deleteActorMember(sActorMember.getId())); } } while (actorMembers.size() == BATCH_SIZE); deleteActorMembersOfGroupChildren(groupId); } private void deleteActorMembersOfGroupChildren(final long groupId) throws SIdentityException, SActorMemberNotFoundException, SActorMemberDeletionException, SBonitaReadException { int i = 0; List childrenGroup; do { childrenGroup = identityService.getGroupChildren(groupId, i, BATCH_SIZE); i += BATCH_SIZE; for (final SGroup sGroup : childrenGroup) { deleteActorMembersOfGroup(sGroup.getId()); } } while (childrenGroup.size() == BATCH_SIZE); } protected void deleteProfileMembersOfGroup(final long groupId) throws SBonitaException { List profileMembers; do { profileMembers = profileService.getProfileMembersOfGroup(groupId, 0, BATCH_SIZE, SProfileMember.ID, OrderByType.ASC); for (final SProfileMember sProfileMember : profileMembers) { profileService.deleteProfileMember(sProfileMember); } } while (profileMembers.size() == BATCH_SIZE); deleteProfileMembersOfGroupChildren(groupId); } private void deleteProfileMembersOfGroupChildren(final long groupId) throws SIdentityException, SBonitaException { int i = 0; List childrenGroup; do { childrenGroup = identityService.getGroupChildren(groupId, i, BATCH_SIZE); i += BATCH_SIZE; for (final SGroup sGroup : childrenGroup) { deleteProfileMembersOfGroup(sGroup.getId()); } } while (childrenGroup.size() == BATCH_SIZE); } protected void deleteMembershipsByGroup(final long groupId) throws SBonitaException { List memberships; do { memberships = identityService.getUserMembershipsOfGroup(groupId, 0, BATCH_SIZE); for (final SUserMembership sUserMembership : memberships) { identityService.deleteUserMembership(sUserMembership.getId()); } } while (memberships.size() == BATCH_SIZE); } protected void deleteActorMembersOfRole(final long roleId) throws SActorMemberNotFoundException, SActorMemberDeletionException, SBonitaReadException { List actorMembers; do { actorMembers = actorMappingService.getActorMembersOfRole(roleId, 0, BATCH_SIZE); for (final SActorMember sActorMember : actorMembers) { setActorIdsOfRemovedElements(actorMappingService.deleteActorMember(sActorMember.getId())); } } while (actorMembers.size() == BATCH_SIZE); } protected void deleteProfileMembersOfRole(final long roleId) throws SBonitaException { List profileMembers; do { profileMembers = profileService.getProfileMembersOfRole(roleId, 0, BATCH_SIZE, SProfileMember.ID, OrderByType.ASC); for (final SProfileMember sProfileMember : profileMembers) { profileService.deleteProfileMember(sProfileMember); } } while (profileMembers.size() == BATCH_SIZE); } protected void deleteMembershipsByRole(final long roleId) throws SBonitaException { List memberships; do { memberships = identityService.getUserMembershipsOfRole(roleId, 0, BATCH_SIZE); for (final SUserMembership sUserMembership : memberships) { identityService.deleteUserMembership(sUserMembership.getId()); } } while (memberships.size() == BATCH_SIZE); } public ActorMappingService getActorMappingService() { return actorMappingService; } public ProfileService getProfileService() { return profileService; } public IdentityService getIdentityService() { return identityService; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetGroups.java ================================================ /** * 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.api.impl.transaction.identity; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.persistence.OrderByType; /** * @author Lu Kai * @author Matthieu Chaffotte */ public class GetGroups implements TransactionContentWithResult> { private final IdentityService identityService; private final int startIndex; private final int maxResults; private final OrderByType orderExecutor; private final String fieldExecutor; private List groups; public GetGroups(final IdentityService identityService, final int startIndex, final int maxResults, final OrderByType orderExecutor, final String fieldExecutor) { this.identityService = identityService; this.startIndex = startIndex; this.maxResults = maxResults; this.orderExecutor = orderExecutor; this.fieldExecutor = fieldExecutor; } @Override public void execute() throws SBonitaException { if (fieldExecutor == null) { groups = identityService.getGroups(startIndex, maxResults); } else { groups = identityService.getGroups(startIndex, maxResults, fieldExecutor, orderExecutor); } } @Override public List getResult() { return groups; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetNumberOfInstance.java ================================================ /** * 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.api.impl.transaction.identity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.identity.IdentityService; /** * @author Lu Kai */ public class GetNumberOfInstance implements TransactionContentWithResult { private final IdentityService identityService; private long number; private final String instanceName; public GetNumberOfInstance(final String instanceName, final IdentityService identityService) { this.instanceName = instanceName; this.identityService = identityService; } @Override public void execute() throws SBonitaException { if (instanceName.equals("getNumberOfGroups")) { number = identityService.getNumberOfGroups(); } if (instanceName.equals("getNumberOfRoles")) { number = identityService.getNumberOfRoles(); } if (instanceName.equals("getNumberOfUsers")) { number = identityService.getNumberOfUsers(); } } @Override public Long getResult() { return number; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetNumberOfUserMemberships.java ================================================ /** * 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.api.impl.transaction.identity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.identity.IdentityService; /** * @author Bole Zhang * @author Baptiste Mesta */ public class GetNumberOfUserMemberships implements TransactionContentWithResult { private final IdentityService identityService; private long number; private final long userId; public GetNumberOfUserMemberships(final long userId, final IdentityService identityService) { this.userId = userId; this.identityService = identityService; } public GetNumberOfUserMemberships(final IdentityService identityService) { userId = -1; this.identityService = identityService; } @Override public void execute() throws SBonitaException { if (userId == -1) { number = identityService.getNumberOfUserMemberships(); } else { number = identityService.getNumberOfUserMembershipsOfUser(userId); } } @Override public Long getResult() { return number; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetNumberOfUsersInType.java ================================================ /** * 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.api.impl.transaction.identity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.identity.IdentityService; /** * @author Lu Kai */ public class GetNumberOfUsersInType implements TransactionContentWithResult { private final long id; private final IdentityService identityService; private long userNumber; private final String instanceName; public GetNumberOfUsersInType(final long id, final String instanceName, final IdentityService identityService) { this.id = id; this.instanceName = instanceName; this.identityService = identityService; } @Override public void execute() throws SBonitaException { if (instanceName.equals("getNumberOfUsersInGroup")) { userNumber = identityService.getNumberOfUsersByGroup(id); } if (instanceName.equals("getNumberOfUsersInRole")) { userNumber = identityService.getNumberOfUsersByRole(id); } } @Override public Long getResult() { return userNumber; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetRoles.java ================================================ /** * 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.api.impl.transaction.identity; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.persistence.OrderByType; /** * @author Lu Kai * @author Matthieu Chaffotte */ public class GetRoles implements TransactionContentWithResult> { private final int startIndex; private final String fieldExecutor; private final IdentityService identityService; private final int maxResults; private final OrderByType orderExecutor; private List roleList; public GetRoles(final IdentityService identityService, final int startIndex, final int maxResults, final String fieldExecutor, final OrderByType orderExecutor) { this.startIndex = startIndex; this.fieldExecutor = fieldExecutor; this.identityService = identityService; this.maxResults = maxResults; this.orderExecutor = orderExecutor; } @Override public void execute() throws SBonitaException { if (fieldExecutor == null) { roleList = identityService.getRoles(startIndex, maxResults); } else { roleList = identityService.getRoles(startIndex, maxResults, fieldExecutor, orderExecutor); } } @Override public List getResult() { return roleList; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetSContactInfo.java ================================================ /** * 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.api.impl.transaction.identity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SContactInfo; /** * @author Emmanuel Duchastenier */ public class GetSContactInfo implements TransactionContentWithResult { private final IdentityService identityService; private final long userId; private final boolean personal; private SContactInfo sContactInfo; public GetSContactInfo(final long userId, final IdentityService identityService, final boolean personal) { super(); this.userId = userId; this.identityService = identityService; this.personal = personal; } @Override public void execute() throws SBonitaException { sContactInfo = identityService.getUserContactInfo(userId, personal); } @Override public SContactInfo getResult() { return sContactInfo; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetSUser.java ================================================ /** * 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.api.impl.transaction.identity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SUser; /** * @author Lu Kai * @author Matthieu Chaffotte */ public class GetSUser implements TransactionContentWithResult { private final IdentityService identityService; private final String userName; private final long userId; private SUser sUser; public GetSUser(final IdentityService identityService, final String userName) { this.identityService = identityService; this.userName = userName; userId = -1; } public GetSUser(final IdentityService identityService, final long userId) { this.identityService = identityService; this.userId = userId; userName = null; } @Override public void execute() throws SBonitaException { if (userName != null) { sUser = identityService.getUserByUserName(userName); } else { sUser = identityService.getUser(userId); } } @Override public SUser getResult() { return sUser; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetUserMembership.java ================================================ /** * 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.api.impl.transaction.identity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SUserMembership; /** * @author Bole Zhang */ public class GetUserMembership implements TransactionContentWithResult { private final IdentityService identityService; private final long userId; private SUserMembership sUserMembership; private final long groupId; private final long roleId; private final long userMembershipId; public GetUserMembership(final long userId, final long groupId, final long roleId, final IdentityService identityService) { this.userId = userId; this.groupId = groupId; this.roleId = roleId; this.identityService = identityService; userMembershipId = -1; } public GetUserMembership(final long userMembershipId, final IdentityService identityService) { this.userMembershipId = userMembershipId; this.identityService = identityService; userId = -1; groupId = -1; roleId = -1; } @Override public void execute() throws SBonitaException { if (userMembershipId == -1) { sUserMembership = identityService.getUserMembership(userId, groupId, roleId); } else { sUserMembership = identityService.getUserMembership(userMembershipId); } } @Override public SUserMembership getResult() { return sUserMembership; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetUserMembershipsOfGroup.java ================================================ /** * 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.api.impl.transaction.identity; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SUserMembership; /** * @author Bole Zhang * @author Baptiste Mesta */ public class GetUserMembershipsOfGroup implements TransactionContentWithResult> { private final long groupId; private final IdentityService identityService; private List sMembership; private final int startIndex; private final int maxResults; public GetUserMembershipsOfGroup(final long groupId, final IdentityService identityService, final int startIndex, final int maxResults) { this.groupId = groupId; this.identityService = identityService; this.startIndex = startIndex; this.maxResults = maxResults; } @Override public void execute() throws SBonitaException { sMembership = identityService.getUserMembershipsOfGroup(groupId, startIndex, maxResults); } @Override public List getResult() { return sMembership; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/GetUserMembershipsOfRole.java ================================================ /** * 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.api.impl.transaction.identity; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SUserMembership; /** * @author Bole Zhang * @author Baptiste Mesta */ public class GetUserMembershipsOfRole implements TransactionContentWithResult> { private final long roleId; private final IdentityService identityService; private List sMembership; private final int startIndex; private final int maxResults; public GetUserMembershipsOfRole(final long roleId, final IdentityService identityService, final int startIndex, final int maxResults) { this.roleId = roleId; this.identityService = identityService; this.startIndex = startIndex; this.maxResults = maxResults; } @Override public void execute() throws SBonitaException { sMembership = identityService.getUserMembershipsOfRole(roleId, startIndex, maxResults); } @Override public List getResult() { return sMembership; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/UpdateGroup.java ================================================ /** * 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.api.impl.transaction.identity; import java.util.List; import java.util.Map; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SIdentityException; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Lu Kai * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros */ public class UpdateGroup { private static final int BATCH_SIZE = 100; private final long groupId; private final EntityUpdateDescriptor changeDescriptor; private final IdentityService identityService; private EntityUpdateDescriptor iconUpdater; public UpdateGroup(final long groupId, final EntityUpdateDescriptor changeDescriptor, final IdentityService identityService, EntityUpdateDescriptor iconUpdater) { this.groupId = groupId; this.changeDescriptor = changeDescriptor; this.identityService = identityService; this.iconUpdater = iconUpdater; } public SGroup update() throws SIdentityException { SGroup sGroup = identityService.getGroup(groupId); // if the parent path changes it's also necessary to change the children's parent path final String parentPathKey = SGroup.PARENT_PATH; final String nameKey = SGroup.NAME; final Map fields = changeDescriptor.getFields(); if (fields.containsKey(parentPathKey) || fields.containsKey(nameKey)) { final String parentPath = fields.containsKey(parentPathKey) ? (String) fields.get(parentPathKey) : sGroup.getParentPath(); final String groupName = fields.containsKey(nameKey) ? (String) fields.get(nameKey) : sGroup.getName(); updateChildren(sGroup, parentPath, SGroup.ID, parentPathKey, groupName); } identityService.updateGroup(sGroup, changeDescriptor, iconUpdater); return sGroup; } private void updateChildren(final SGroup group, final String parentPath, final String idKey, final String parentPathKey, final String groupName) throws SIdentityException { List groupChildren; int i = 0; do { groupChildren = identityService.getGroupChildren(group.getId(), i * BATCH_SIZE, BATCH_SIZE, idKey, OrderByType.ASC); i++; for (final SGroup child : groupChildren) { if (parentPath != null) { updateChildren(child, parentPath + '/' + groupName, idKey, parentPathKey, child.getName()); } else { updateChildren(child, '/' + groupName, idKey, parentPathKey, child.getName()); } } } while (!groupChildren.isEmpty()); final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor(); updateDescriptor.addField(parentPathKey, parentPath); identityService.updateGroup(group, updateDescriptor, null); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/identity/UpdateMembershipByRoleIdAndGroupId.java ================================================ /** * 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.api.impl.transaction.identity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Bole Zhang */ public class UpdateMembershipByRoleIdAndGroupId implements TransactionContent { private final IdentityService identityService; private final EntityUpdateDescriptor changeDescriptor; private final long userMemebershipId; public UpdateMembershipByRoleIdAndGroupId(final long userMemebershipId, final IdentityService identityService, final EntityUpdateDescriptor changeDescriptor) { this.userMemebershipId = userMemebershipId; this.identityService = identityService; this.changeDescriptor = changeDescriptor; } @Override public void execute() throws SBonitaException { identityService.updateUserMembership(identityService.getLightUserMembership(userMemebershipId), changeDescriptor); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/page/SearchPages.java ================================================ /** * 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.api.impl.transaction.page; import java.util.List; import org.bonitasoft.engine.api.impl.converter.PageModelConverter; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Baptiste Mesta */ public class SearchPages extends AbstractSearchEntity { private final PageService pageService; public SearchPages(final PageService pageService, final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); this.pageService = pageService; } @Override public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException { try { return pageService.getNumberOfPages(queryOptions); } catch (final SBonitaReadException e) { throw new SBonitaReadException(e); } } @Override public List executeSearch(final QueryOptions queryOptions) throws SBonitaReadException { return pageService.searchPages(queryOptions); } @Override public List convertToClientObjects(final List pages) { return new PageModelConverter().toPages(pages); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/platform/DeleteSPlatformCommand.java ================================================ /** * 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.api.impl.transaction.platform; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.platform.command.PlatformCommandService; /** * @author Zhang Bole */ public class DeleteSPlatformCommand implements TransactionContent { private final String name; private final PlatformCommandService platformCommandService; public DeleteSPlatformCommand(final PlatformCommandService platformCommandService, final String name) { this.platformCommandService = platformCommandService; this.name = name; } @Override public void execute() throws SBonitaException { platformCommandService.delete(name); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/platform/GetPlatformContent.java ================================================ /** * 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.api.impl.transaction.platform; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.platform.model.SPlatform; /** * @author Lu Kai */ public class GetPlatformContent implements TransactionContentWithResult { private final PlatformService platformService; private SPlatform sPlatform; public GetPlatformContent(final PlatformService platformService) { this.platformService = platformService; } @Override public void execute() throws SBonitaException { sPlatform = platformService.getPlatform(); } @Override public SPlatform getResult() { return sPlatform; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/platform/GetSPlatformCommands.java ================================================ /** * 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.api.impl.transaction.platform; import java.util.List; import org.bonitasoft.engine.command.CommandCriterion; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.platform.command.PlatformCommandService; import org.bonitasoft.engine.platform.command.model.SPlatformCommand; /** * @author Zhang Bole */ public class GetSPlatformCommands implements TransactionContentWithResult> { private final PlatformCommandService platformCommandService; private final int startIndex; private final int maxResults; private final CommandCriterion sort; private List platformCommands; public GetSPlatformCommands(final PlatformCommandService platformCommandService, final int startIndex, final int maxResults, final CommandCriterion sort) { super(); this.platformCommandService = platformCommandService; this.startIndex = startIndex; this.maxResults = maxResults; this.sort = sort; } @Override public void execute() throws SBonitaException { final QueryOptions queryOptions; switch (sort) { case NAME_ASC: queryOptions = new QueryOptions(startIndex, maxResults, SPlatformCommand.class, "name", OrderByType.ASC); break; case NAME_DESC: default: queryOptions = new QueryOptions(startIndex, maxResults, SPlatformCommand.class, "name", OrderByType.DESC); break; } platformCommands = platformCommandService.getPlatformCommands(queryOptions); } @Override public List getResult() { return platformCommands; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/platform/UpdateSPlatformCommand.java ================================================ /** * 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.api.impl.transaction.platform; import java.io.Serializable; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.command.CommandUpdater; import org.bonitasoft.engine.command.CommandUpdater.CommandField; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.platform.command.PlatformCommandService; import org.bonitasoft.engine.platform.command.model.SPlatformCommand; import org.bonitasoft.engine.platform.command.model.SPlatformCommandUpdateBuilder; import org.bonitasoft.engine.platform.command.model.SPlatformCommandUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Zhang Bole */ public class UpdateSPlatformCommand implements TransactionContent { private final PlatformCommandService platformCommandService; private final String name; private final CommandUpdater updateDescriptor; public UpdateSPlatformCommand(final PlatformCommandService platformCommandService, final String name, final CommandUpdater updateDescriptor) { this.platformCommandService = platformCommandService; this.name = name; this.updateDescriptor = updateDescriptor; } @Override public void execute() throws SBonitaException { final EntityUpdateDescriptor changeDescriptor = getCommandUpdateDescriptor(); final SPlatformCommand sPlatformCommand = platformCommandService.getPlatformCommand(name); platformCommandService.update(sPlatformCommand, changeDescriptor); } private EntityUpdateDescriptor getCommandUpdateDescriptor() { final SPlatformCommandUpdateBuilder platformCommandUpdateBuilder = BuilderFactory .get(SPlatformCommandUpdateBuilderFactory.class).createNewInstance(); final Map fields = updateDescriptor.getFields(); for (final Entry field : fields.entrySet()) { switch (field.getKey()) { case NAME: platformCommandUpdateBuilder.updateName((String) field.getValue()); break; case DESCRIPTION: platformCommandUpdateBuilder.updateDescription((String) field.getValue()); break; default: throw new IllegalStateException(); } } return platformCommandUpdateBuilder.done(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/AbstractGetProcessDeploymentInfo.java ================================================ /** * 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.api.impl.transaction.process; import java.util.List; import org.bonitasoft.engine.api.impl.transaction.AbstractGetEntity; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Celine Souchet */ public abstract class AbstractGetProcessDeploymentInfo extends AbstractGetEntity { public AbstractGetProcessDeploymentInfo(final SearchEntityDescriptor searchDescriptor, final int fromIndex, final int numberOfResults, final ProcessDeploymentInfoCriterion criterion) { super(searchDescriptor, fromIndex, numberOfResults, criterion.getSort()); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toProcessDeploymentInfo(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/AddProcessDefinitionToCategory.java ================================================ /** * 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.api.impl.transaction.process; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.core.category.CategoryService; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; /** * @author Yanyan Liu */ public class AddProcessDefinitionToCategory implements TransactionContent { private final long categoryId; private final long processDefinitionId; private final CategoryService categoryService; private final ProcessDefinitionService processDefinitionService; public AddProcessDefinitionToCategory(final long categoryId, final long processDefinitionId, final CategoryService categoryService, final ProcessDefinitionService processDefinitionService) { this.categoryId = categoryId; this.processDefinitionId = processDefinitionId; this.categoryService = categoryService; this.processDefinitionService = processDefinitionService; } @Override public void execute() throws SBonitaException { processDefinitionService.getProcessDefinition(processDefinitionId); categoryService.addProcessDefinitionToCategory(categoryId, processDefinitionId); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/DisableProcess.java ================================================ /** * 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.api.impl.transaction.process; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.execution.job.JobNameBuilder; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ @Slf4j public final class DisableProcess implements TransactionContent { private final ProcessDefinitionService processDefinitionService; private final EventInstanceService eventInstanceService; private final long processDefinitionId; private final SchedulerService scheduler; private final String username; public DisableProcess(final ProcessDefinitionService processDefinitionService, final long processId, final EventInstanceService eventInstanceService, final SchedulerService scheduler, final String username) { this.processDefinitionService = processDefinitionService; this.eventInstanceService = eventInstanceService; this.processDefinitionId = processId; this.scheduler = scheduler; this.username = username; } @Override public void execute() throws SBonitaException { processDefinitionService.disableProcessDeploymentInfo(processDefinitionId); final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId); disableStartEvents(processDefinition); log.info("The user <" + username + "> has disabled process <" + processDefinition.getName() + "> in version <" + processDefinition.getVersion() + "> with id <" + processDefinition.getId() + ">"); } private void disableStartEvents(final SProcessDefinition processDefinition) throws SBonitaException { deleteWaitingEvents(); deleteJobs(processDefinition); } private void deleteJobs(final SProcessDefinition processDefinition) throws SSchedulerException { final List startEvents = processDefinition.getProcessContainer().getStartEvents(); for (final SStartEventDefinition startEvent : startEvents) { if (!startEvent.getTimerEventTriggerDefinitions().isEmpty()) { scheduler.delete(JobNameBuilder.getTimerEventJobName(processDefinitionId, startEvent, null)); } } } private void deleteWaitingEvents() throws SWaitingEventModificationException, SBonitaReadException { List waitingEvents = eventInstanceService .getStartWaitingEventsOfProcessDefinition(processDefinitionId); for (final SWaitingEvent startEvent : waitingEvents) { eventInstanceService.deleteWaitingEvent(startEvent); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/EnableProcess.java ================================================ /** * 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.api.impl.transaction.process; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition; import org.bonitasoft.engine.execution.event.EventsHandler; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ @Slf4j public final class EnableProcess implements TransactionContent { private final ProcessDefinitionService processDefinitionService; private final long processId; private final EventsHandler eventsHandler; private final String userName; public EnableProcess(final ProcessDefinitionService processDefinitionService, final long processId, final EventsHandler eventsHandler, final String userName) { this.processDefinitionService = processDefinitionService; this.processId = processId; this.eventsHandler = eventsHandler; this.userName = userName; } @Override public void execute() throws SBonitaException { final SProcessDefinition sProcessDefinition = processDefinitionService.getProcessDefinition(processId); handleStartEvents(sProcessDefinition); processDefinitionService.enableProcessDeploymentInfo(processId); log.info("The user <" + userName + "> has enabled process <" + sProcessDefinition.getName() + "> in version <" + sProcessDefinition.getVersion() + "> with id <" + sProcessDefinition.getId() + ">"); } private void handleStartEvents(final SProcessDefinition sProcessDefinition) throws SBonitaException { final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer(); for (final SStartEventDefinition sStartEventDefinition : processContainer.getStartEvents()) { eventsHandler.handleCatchEvent(sProcessDefinition, sStartEventDefinition, null); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetArchivedProcessInstanceList.java ================================================ /** * 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.api.impl.transaction.process; import java.util.List; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; import org.bonitasoft.engine.search.process.SearchArchivedProcessInstances; /** * @author Emmanuel Duchastenier * @author Celine Souchet * @author Matthieu Chaffotte */ public class GetArchivedProcessInstanceList implements TransactionContentWithResult> { private final ProcessInstanceService processInstanceService; private final SearchEntitiesDescriptor searchEntitiesDescriptor; private final SearchOptionsBuilder searchOptionsBuilder; private List processInstanceList; private final ProcessDefinitionService processDefinitionService; public GetArchivedProcessInstanceList(final ProcessInstanceService processInstanceService, final ProcessDefinitionService processDefinitionService, final SearchEntitiesDescriptor searchEntitiesDescriptor, final long processInstanceId, final int startIndex, final int maxResults) { this.processInstanceService = processInstanceService; this.processDefinitionService = processDefinitionService; this.searchEntitiesDescriptor = searchEntitiesDescriptor; searchOptionsBuilder = new SearchOptionsBuilder(startIndex, maxResults); searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, processInstanceId); } public GetArchivedProcessInstanceList(final ProcessInstanceService processInstanceService, final ProcessDefinitionService processDefinitionService, final SearchEntitiesDescriptor searchEntitiesDescriptor, final long processInstanceId, final int startIndex, final int maxResults, final String field, final OrderByType order) { this(processInstanceService, processDefinitionService, searchEntitiesDescriptor, processInstanceId, startIndex, maxResults); searchOptionsBuilder.sort(field, Order.valueOf(order.name())); } public GetArchivedProcessInstanceList(final ProcessInstanceService processInstanceService, final ProcessDefinitionService processDefinitionService, final SearchEntitiesDescriptor searchEntitiesDescriptor, final SearchOptions searchOptions) { this.processInstanceService = processInstanceService; this.processDefinitionService = processDefinitionService; this.searchEntitiesDescriptor = searchEntitiesDescriptor; searchOptionsBuilder = new SearchOptionsBuilder(searchOptions); } @Override public void execute() throws SBonitaException { final SearchArchivedProcessInstances searchArchivedProcessInstances = new SearchArchivedProcessInstances( processInstanceService, processDefinitionService, searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptionsBuilder.done()); searchArchivedProcessInstances.execute(); processInstanceList = searchArchivedProcessInstances.getResult().getResult(); } @Override public List getResult() { return processInstanceList; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetChildInstanceIdsOfProcessInstance.java ================================================ /** * 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.api.impl.transaction.process; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.persistence.OrderByType; /** * @author Yanyan Liu */ public class GetChildInstanceIdsOfProcessInstance implements TransactionContentWithResult> { private final ProcessInstanceService processInstanceService; private final long processInstanceId; private final int pageIndex; private final int numberPerPage; private final String sortingField; private final OrderByType sortingOrder; private List childInstanceIds; public GetChildInstanceIdsOfProcessInstance(final ProcessInstanceService processInstanceService, final long processInstanceId, final int pageIndex, final int numberPerPage, final String field, final OrderByType order) { this.processInstanceService = processInstanceService; this.processInstanceId = processInstanceId; this.pageIndex = pageIndex; this.numberPerPage = numberPerPage; this.sortingField = field; this.sortingOrder = order; } @Override public void execute() throws SBonitaException { this.childInstanceIds = this.processInstanceService.getChildInstanceIdsOfProcessInstance(this.processInstanceId, this.pageIndex * this.numberPerPage, this.numberPerPage, this.sortingField, this.sortingOrder); } @Override public List getResult() { return this.childInstanceIds; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetLastArchivedProcessInstance.java ================================================ /** * 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.api.impl.transaction.process; import java.util.List; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; import org.bonitasoft.engine.search.process.SearchArchivedProcessInstances; /** * Returns the most recent archived process instance from the archives. * * @author Emmanuel Duchastenier * @author Celine Souchet * @author Matthieu Chaffotte */ public class GetLastArchivedProcessInstance implements TransactionContentWithResult { private ArchivedProcessInstance processInstance; private final ProcessInstanceService processInstanceService; private final long processInstanceId; private final SearchEntitiesDescriptor searchEntitiesDescriptor; private final ProcessDefinitionService processDefinitionService; public GetLastArchivedProcessInstance(final ProcessInstanceService processInstanceService, final ProcessDefinitionService processDefinitionService, final long processInstanceId, final SearchEntitiesDescriptor searchEntitiesDescriptor) { this.processInstanceService = processInstanceService; this.processDefinitionService = processDefinitionService; this.processInstanceId = processInstanceId; this.searchEntitiesDescriptor = searchEntitiesDescriptor; } @Override public void execute() throws SBonitaException { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 2); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.ARCHIVE_DATE, Order.DESC); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.END_DATE, Order.DESC); searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, processInstanceId); final SearchArchivedProcessInstances searchArchivedProcessInstances = new SearchArchivedProcessInstances( processInstanceService, processDefinitionService, searchEntitiesDescriptor.getSearchArchivedProcessInstanceDescriptor(), searchOptionsBuilder.done()); searchArchivedProcessInstances.execute(); final List processInstances = searchArchivedProcessInstances.getResult().getResult(); if (processInstances.isEmpty()) { throw new SProcessInstanceNotFoundException(processInstanceId); } processInstance = processInstances.get(0); } @Override public ArchivedProcessInstance getResult() { return processInstance; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetLatestProcessDefinitionId.java ================================================ /** * 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.api.impl.transaction.process; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; /** * @author Yanyan Liu */ public class GetLatestProcessDefinitionId implements TransactionContentWithResult { private final ProcessDefinitionService processDefinitionService; private final String processName; private long processDefId; public GetLatestProcessDefinitionId(final ProcessDefinitionService processDefinitionService, final String processName) { this.processDefinitionService = processDefinitionService; this.processName = processName; } @Override public void execute() throws SBonitaException { this.processDefId = this.processDefinitionService.getLatestProcessDefinitionId(this.processName); } @Override public Long getResult() { return this.processDefId; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetNumberOfProcessDeploymentInfos.java ================================================ /** * 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.api.impl.transaction.process; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; /** * @author Baptiste Mesta */ public final class GetNumberOfProcessDeploymentInfos implements TransactionContentWithResult { private final ProcessDefinitionService processDefinitionService; private long numberOfProcesses; public GetNumberOfProcessDeploymentInfos(final ProcessDefinitionService processDefinitionService) { this.processDefinitionService = processDefinitionService; } @Override public void execute() throws SBonitaException { numberOfProcesses = processDefinitionService.getNumberOfProcessDeploymentInfos(); } @Override public Long getResult() { return numberOfProcesses; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetNumberOfProcessDeploymentInfosUnrelatedToCategory.java ================================================ /** * 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.api.impl.transaction.process; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; /** * @author Celine Souchet * @author Matthieu Chaffotte */ public class GetNumberOfProcessDeploymentInfosUnrelatedToCategory implements TransactionContentWithResult { private final long categoryId; private final ProcessDefinitionService processDefinitionService; private Long count; public GetNumberOfProcessDeploymentInfosUnrelatedToCategory(final long categoryId, final ProcessDefinitionService processDefinitionService) { this.categoryId = categoryId; this.processDefinitionService = processDefinitionService; } @Override public void execute() throws SBonitaException { count = processDefinitionService.getNumberOfProcessDeploymentInfosUnrelatedToCategory(categoryId); } @Override public Long getResult() { return count; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetNumberOfProcessInstance.java ================================================ /** * 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.api.impl.transaction.process; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; import org.bonitasoft.engine.search.process.SearchProcessInstances; /** * @author Emmanuel Duchastenier * @author Celine Souchet */ public class GetNumberOfProcessInstance implements TransactionContentWithResult { private final ProcessInstanceService processInstanceService; private final ProcessDefinitionService processDefinitionService; private final SearchEntitiesDescriptor searchEntitiesDescriptor; private long number; public GetNumberOfProcessInstance(final ProcessInstanceService processInstanceService, final ProcessDefinitionService processDefinitionService, final SearchEntitiesDescriptor searchEntitiesDescriptor) { this.processInstanceService = processInstanceService; this.processDefinitionService = processDefinitionService; this.searchEntitiesDescriptor = searchEntitiesDescriptor; } @Override public void execute() throws SBonitaException { final SearchProcessInstances searchProcessInstances = new SearchProcessInstances(processInstanceService, searchEntitiesDescriptor.getSearchProcessInstanceDescriptor(), null, processDefinitionService); final QueryOptions queryOptions = new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS); number = searchProcessInstances.executeCount(queryOptions); } @Override public Long getResult() { return number; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetProcessDefinitionDeployInfos.java ================================================ /** * 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.api.impl.transaction.process; import java.util.List; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Baptiste Mesta * @author Celine Souchet */ public final class GetProcessDefinitionDeployInfos extends AbstractGetProcessDeploymentInfo { private final ProcessDefinitionService processDefinitionService; public GetProcessDefinitionDeployInfos(final ProcessDefinitionService processDefinitionService, final SearchEntityDescriptor searchDescriptor, final int fromIndex, final int numberOfResults, final ProcessDeploymentInfoCriterion criterion) { super(searchDescriptor, fromIndex, numberOfResults, criterion); this.processDefinitionService = processDefinitionService; } @Override public List executeGet(final QueryOptions queryOptions) throws SBonitaException { return processDefinitionService.getProcessDeploymentInfos(queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetProcessDefinitionDeployInfosWithActorOnlyForGroup.java ================================================ /** * 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.api.impl.transaction.process; import java.util.List; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Arthur Freycon * @author Celine Souchet */ public class GetProcessDefinitionDeployInfosWithActorOnlyForGroup extends AbstractGetProcessDeploymentInfo { private final long groupId; private final ProcessDefinitionService processDefinitionService; public GetProcessDefinitionDeployInfosWithActorOnlyForGroup(final ProcessDefinitionService processDefinitionService, final SearchEntityDescriptor searchDescriptor, final int fromIndex, final int numberOfResults, final ProcessDeploymentInfoCriterion criterion, final long groupId) { super(searchDescriptor, fromIndex, numberOfResults, criterion); this.groupId = groupId; this.processDefinitionService = processDefinitionService; } @Override public List executeGet(final QueryOptions queryOptions) throws SBonitaException { return processDefinitionService.getProcessDeploymentInfosWithActorOnlyForGroup(groupId, queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetProcessDefinitionDeployInfosWithActorOnlyForGroups.java ================================================ /** * 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.api.impl.transaction.process; import java.util.List; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Arthur Freycon * @author Celine Souchet */ public class GetProcessDefinitionDeployInfosWithActorOnlyForGroups extends AbstractGetProcessDeploymentInfo { private final List groupIds; private final ProcessDefinitionService processDefinitionService; public GetProcessDefinitionDeployInfosWithActorOnlyForGroups( final ProcessDefinitionService processDefinitionService, final SearchEntityDescriptor searchDescriptor, final int fromIndex, final int numberOfResults, final ProcessDeploymentInfoCriterion criterion, final List groupIds) { super(searchDescriptor, fromIndex, numberOfResults, criterion); this.groupIds = groupIds; this.processDefinitionService = processDefinitionService; } @Override public List executeGet(final QueryOptions queryOptions) throws SBonitaException { return processDefinitionService.getProcessDeploymentInfosWithActorOnlyForGroups(groupIds, queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetProcessDefinitionDeployInfosWithActorOnlyForRole.java ================================================ /** * 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.api.impl.transaction.process; import java.util.List; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Arthur Freycon * @author Celine Souchet */ public class GetProcessDefinitionDeployInfosWithActorOnlyForRole extends AbstractGetProcessDeploymentInfo { private final long roleId; private final ProcessDefinitionService processDefinitionService; public GetProcessDefinitionDeployInfosWithActorOnlyForRole(final ProcessDefinitionService processDefinitionService, final SearchEntityDescriptor searchDescriptor, final int fromIndex, final int numberOfResults, final ProcessDeploymentInfoCriterion criterion, final long roleId) { super(searchDescriptor, fromIndex, numberOfResults, criterion); this.roleId = roleId; this.processDefinitionService = processDefinitionService; } @Override public List executeGet(final QueryOptions queryOptions) throws SBonitaException { return processDefinitionService.getProcessDeploymentInfosWithActorOnlyForRole(roleId, queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetProcessDefinitionDeployInfosWithActorOnlyForRoles.java ================================================ /** * 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.api.impl.transaction.process; import java.util.List; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Arthur Freycon * @author Celine Souchet */ public class GetProcessDefinitionDeployInfosWithActorOnlyForRoles extends AbstractGetProcessDeploymentInfo { private final List roleIds; private final ProcessDefinitionService processDefinitionService; public GetProcessDefinitionDeployInfosWithActorOnlyForRoles(final ProcessDefinitionService processDefinitionService, final SearchEntityDescriptor searchDescriptor, final int fromIndex, final int numberOfResults, final ProcessDeploymentInfoCriterion criterion, final List roleIds) { super(searchDescriptor, fromIndex, numberOfResults, criterion); this.roleIds = roleIds; this.processDefinitionService = processDefinitionService; } @Override public List executeGet(final QueryOptions queryOptions) throws SBonitaException { return processDefinitionService.getProcessDeploymentInfosWithActorOnlyForRoles(roleIds, queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetProcessDefinitionDeployInfosWithActorOnlyForUser.java ================================================ /** * 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.api.impl.transaction.process; import java.util.List; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Emmanuel Duchastenier * @author Celine Souchet */ public final class GetProcessDefinitionDeployInfosWithActorOnlyForUser extends AbstractGetProcessDeploymentInfo { private final ProcessDefinitionService processDefinitionService; private final long userId; public GetProcessDefinitionDeployInfosWithActorOnlyForUser(final ProcessDefinitionService processDefinitionService, final SearchEntityDescriptor searchDescriptor, final int fromIndex, final int numberOfResults, final ProcessDeploymentInfoCriterion criterion, final long userId) { super(searchDescriptor, fromIndex, numberOfResults, criterion); this.userId = userId; this.processDefinitionService = processDefinitionService; } @Override public List executeGet(final QueryOptions queryOptions) throws SBonitaException { return processDefinitionService.getProcessDeploymentInfosWithActorOnlyForUser(userId, queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/GetProcessDefinitionDeployInfosWithActorOnlyForUsers.java ================================================ /** * 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.api.impl.transaction.process; import java.util.List; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoCriterion; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Arthur Freycon * @author Celine Souchet */ public class GetProcessDefinitionDeployInfosWithActorOnlyForUsers extends AbstractGetProcessDeploymentInfo { private final List userIds; private final ProcessDefinitionService processDefinitionService; public GetProcessDefinitionDeployInfosWithActorOnlyForUsers(final ProcessDefinitionService processDefinitionService, final SearchEntityDescriptor searchDescriptor, final int fromIndex, final int numberOfResults, final ProcessDeploymentInfoCriterion criterion, final List userIds) { super(searchDescriptor, fromIndex, numberOfResults, criterion); this.userIds = userIds; this.processDefinitionService = processDefinitionService; } @Override public List executeGet(final QueryOptions queryOptions) throws SBonitaException { return processDefinitionService.getProcessDeploymentInfosWithActorOnlyForUsers(userIds, queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/SetProcessInstanceState.java ================================================ /** * 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.api.impl.transaction.process; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; /** * @author Baptiste Mesta */ public final class SetProcessInstanceState implements TransactionContentWithResult { private final long processInstanceId; private final ProcessInstanceState state; private final ProcessInstanceService processInstanceService; private SProcessInstance result; public SetProcessInstanceState(final ProcessInstanceService processInstanceService, final long processInstanceId, final ProcessInstanceState state) { this.processInstanceService = processInstanceService; this.processInstanceId = processInstanceId; this.state = state; } @Override public void execute() throws SBonitaException { result = processInstanceService.getProcessInstance(processInstanceId); processInstanceService.setState(result, state); } @Override public SProcessInstance getResult() { return result; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/process/UpdateProcessDeploymentInfo.java ================================================ /** * 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.api.impl.transaction.process; import java.io.Serializable; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoUpdater; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoUpdater.ProcessDeploymentInfoField; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilder; import org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Yanyan Liu * @author Zhang Bole * @author Celine Souchet */ public class UpdateProcessDeploymentInfo implements TransactionContent { private final ProcessDefinitionService processDefinitionService; private final long processId; private final ProcessDeploymentInfoUpdater processDeploymentInfoUpdateDescriptor; public UpdateProcessDeploymentInfo(final ProcessDefinitionService processDefinitionService, final long processId, final ProcessDeploymentInfoUpdater processDeploymentInfoUpdateDescriptor) { this.processDefinitionService = processDefinitionService; this.processId = processId; this.processDeploymentInfoUpdateDescriptor = processDeploymentInfoUpdateDescriptor; } @Override public void execute() throws SBonitaException { processDefinitionService.updateProcessDefinitionDeployInfo(processId, buildDescriptor()); } private EntityUpdateDescriptor buildDescriptor() { final SProcessDefinitionDeployInfoUpdateBuilder processDeploymentInfoUpdateBuilder = BuilderFactory .get(SProcessDefinitionDeployInfoUpdateBuilderFactory.class).createNewInstance(); final Map updatedFieldsMap = processDeploymentInfoUpdateDescriptor .getFields(); for (final Entry field : updatedFieldsMap.entrySet()) { switch (field.getKey()) { case DISPLAY_NAME: processDeploymentInfoUpdateBuilder.updateDisplayName((String) field.getValue()); break; case DISPLAY_DESCRIPTION: processDeploymentInfoUpdateBuilder.updateDisplayDescription((String) field.getValue()); break; case ICONPATH: processDeploymentInfoUpdateBuilder.updateIconPath((String) field.getValue()); break; default: break; } } return processDeploymentInfoUpdateBuilder.done(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/profile/CreateProfileMember.java ================================================ /** * 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.api.impl.transaction.profile; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.MemberType; import org.bonitasoft.engine.identity.SGroupNotFoundException; import org.bonitasoft.engine.identity.SRoleNotFoundException; import org.bonitasoft.engine.identity.SUserNotFoundException; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.profile.ProfileService; import org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberCreationException; import org.bonitasoft.engine.profile.model.SProfileMember; /** * @author Julien Mege * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public class CreateProfileMember implements TransactionContentWithResult { private final ProfileService profileService; private final IdentityService identityService; private final long profileId; private final Long userId; private final Long groupId; private final Long roleId; private final MemberType memberType; private SProfileMember sProfileMember; public CreateProfileMember(final ProfileService profileService, final IdentityService identityService, final long profileId, final Long userId, final Long groupId, final Long roleId, final MemberType memberType) { super(); this.profileService = profileService; this.identityService = identityService; this.profileId = profileId; this.userId = userId; this.groupId = groupId; this.roleId = roleId; this.memberType = memberType; } @Override public void execute() throws SBonitaException { profileService.updateProfileMetaData(profileId); switch (memberType) { case USER: if (isNotNullOrEmpty(userId)) { addUserToProfile(); } break; case GROUP: if (isNotNullOrEmpty(groupId)) { addGroupToProfile(); } break; case ROLE: if (isNotNullOrEmpty(roleId)) { addRoleToProfile(); } break; default: if (isNotNullOrEmpty(groupId) && isNotNullOrEmpty(roleId)) { addRoleAndGroupToProfile(); } break; } } private void addRoleAndGroupToProfile() throws SGroupNotFoundException, SRoleNotFoundException, SProfileMemberCreationException { final SGroup group = identityService.getGroup(groupId); final SRole role = identityService.getRole(roleId); if (group != null && role != null) { sProfileMember = profileService .addRoleAndGroupToProfile(profileId, roleId, groupId, role.getName(), group.getName(), group.getParentPath()); } } private void addRoleToProfile() throws SRoleNotFoundException, SProfileMemberCreationException { final SRole role = identityService.getRole(roleId); if (role != null) { sProfileMember = profileService.addRoleToProfile(profileId, roleId, role.getName()); } } private void addGroupToProfile() throws SGroupNotFoundException, SProfileMemberCreationException { final SGroup group = identityService.getGroup(groupId); if (group != null) { sProfileMember = profileService.addGroupToProfile(profileId, groupId, group.getName(), group.getParentPath()); } } private void addUserToProfile() throws SUserNotFoundException, SProfileMemberCreationException { final SUser user = identityService.getUser(userId); if (user != null) { sProfileMember = profileService.addUserToProfile(profileId, userId, user.getUserName(), user.getLastName(), user.getUserName()); } } private boolean isNotNullOrEmpty(final Long id) { return id != null && id > 0; } @Override public SProfileMember getResult() { return sProfileMember; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/profile/ProfileMemberUtils.java ================================================ /** * 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.api.impl.transaction.profile; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.profile.model.SProfileMember; /** * @author Julien Mege * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class ProfileMemberUtils { public static final String PROFILE_ID = "profileId"; public static final String USER_ID = "userId"; public static final String GROUP_ID = "groupId"; public static final String ROLE_ID = "roleId"; public static final String PROFILE_MEMBER_ID = "profileMemberId"; public static final String DISPLAY_NAME_PART1 = "displayNamePart1"; public static final String DISPLAY_NAME_PART2 = "displayNamePart2"; public static final String DISPLAY_NAME_PART3 = "displayNamePart3"; public static final String ROLE_AND_GROUP_TYPE = "roleAndGroup"; public static final String ROLE_TYPE = "role"; public static final String GROUP_TYPE = "group"; public static final String USER_TYPE = "user"; /** * Search command parameters */ public static final String PROFILE_MEMBER_SEARCH_INDEX = "fromIndex"; public static final String PROFILE_MEMBER_SEARCH_NUMBER = "numberOfProfiles"; public static final String PROFILE_MEMBER_SEARCH_FIELD = "field"; public static final String PROFILE_MEMBER_SEARCH_ORDER = "order"; public static final String PROFILE_MEMBER_SEARCH_OPTIONS_KEY = "searchOptions"; public static final String PROFILE_MEMBER_TYPE = "memberType"; /** * Query Suffix */ public static final String USER_SUFFIX = "ForUser"; public static final String GROUP_SUFFIX = "ForGroup"; public static final String ROLE_SUFFIX = "ForRole"; public static final String ROLE_AND_GROUP_SUFFIX = "ForRoleAndGroup"; private ProfileMemberUtils() { // For Sonar } public static Map memberAsProfileMembersMap(final SProfileMember profileMember) { final Map profileMemeber = new HashMap(); profileMemeber.put(PROFILE_ID, profileMember.getProfileId()); profileMemeber.put(USER_ID, profileMember.getUserId()); profileMemeber.put(GROUP_ID, profileMember.getGroupId()); profileMemeber.put(ROLE_ID, profileMember.getRoleId()); profileMemeber.put(PROFILE_MEMBER_ID, profileMember.getId()); profileMemeber.put(DISPLAY_NAME_PART1, profileMember.getDisplayNamePart1()); profileMemeber.put(DISPLAY_NAME_PART2, profileMember.getDisplayNamePart2()); profileMemeber.put(DISPLAY_NAME_PART3, profileMember.getDisplayNamePart3()); return profileMemeber; } public static List> membersAsProfileMembersMapList( final List serverObjects) { final List> profileMemberMaps = new ArrayList>(); for (final SProfileMember profileMember : serverObjects) { profileMemberMaps.add(memberAsProfileMembersMap(profileMember)); } return profileMemberMaps; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/task/AssignOrUnassignUserTask.java ================================================ /** * 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.api.impl.transaction.task; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.execution.SUnreleasableTaskException; import org.bonitasoft.engine.execution.StateBehaviors; /** * @author Baptiste Mesta */ public final class AssignOrUnassignUserTask implements TransactionContent { private final long userId; private final long userTaskId; private final ActivityInstanceService activityInstanceService; private final StateBehaviors stateBehaviors; public AssignOrUnassignUserTask(final long userId, final long userTaskId, final ActivityInstanceService activityInstanceService, StateBehaviors stateBehaviors) { this.userId = userId; this.userTaskId = userTaskId; this.activityInstanceService = activityInstanceService; this.stateBehaviors = stateBehaviors; } @Override public void execute() throws SBonitaException { final SActivityInstance activityInstance = activityInstanceService.getActivityInstance(userTaskId); if (userId == 0 && SFlowNodeType.MANUAL_TASK.equals(activityInstance.getType())) { throw new SUnreleasableTaskException("The activity with id " + activityInstance.getId() + " can't be assigned because it is a manual sub task"); } activityInstanceService.assignHumanTask(userTaskId, userId); if (userId > 0) { stateBehaviors.addAssignmentSystemComment(activityInstance, userId); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/task/AssignUserTaskIfNotAssigned.java ================================================ /** * 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.api.impl.transaction.task; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.execution.SUnreleasableTaskException; import org.bonitasoft.engine.execution.StateBehaviors; /** * @author Pablo Alonso de Linaje García */ public class AssignUserTaskIfNotAssigned implements TransactionContent { private final long userId; private final long userTaskId; private final ActivityInstanceService activityInstanceService; private final StateBehaviors stateBehaviors; public AssignUserTaskIfNotAssigned(final long userId, final long userTaskId, final ActivityInstanceService activityInstanceService, StateBehaviors stateBehaviors) { this.userId = userId; this.userTaskId = userTaskId; this.activityInstanceService = activityInstanceService; this.stateBehaviors = stateBehaviors; } @Override public void execute() throws SBonitaException { final SActivityInstance activityInstance = activityInstanceService.getActivityInstance(userTaskId); if (userId == 0 && SFlowNodeType.MANUAL_TASK.equals(activityInstance.getType())) { throw new SUnreleasableTaskException("The activity with id " + activityInstance.getId() + " can't be unassigned because it is a manual sub task"); } activityInstanceService.assignHumanTaskIfNotAssigned(userTaskId, userId); if (userId > 0) { stateBehaviors.addAssignmentSystemComment(activityInstance, userId); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/task/GetAssignedTasks.java ================================================ /** * 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.api.impl.transaction.task; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.persistence.OrderByType; /** * @author Matthieu Chaffotte * @author Emmanuel Duchastenier */ public class GetAssignedTasks implements TransactionContentWithResult> { private final ActivityInstanceService instanceService; private final long userId; private final int fromIndex; private final int maxResults; private final String sortFieldName; private final OrderByType order; private List userTasks; public GetAssignedTasks(final ActivityInstanceService instanceService, final long userId, final int fromIndex, final int maxResults, final String sortFieldName, final OrderByType order) { super(); this.instanceService = instanceService; this.userId = userId; this.fromIndex = fromIndex; this.maxResults = maxResults; this.sortFieldName = sortFieldName; this.order = order; } @Override public void execute() throws SBonitaException { userTasks = instanceService.getAssignedUserTasks(userId, fromIndex, maxResults, sortFieldName, order); } @Override public List getResult() { return userTasks; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/task/GetHumanTaskInstance.java ================================================ /** * 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.api.impl.transaction.task; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; /** * @author Zhang Bole */ public class GetHumanTaskInstance implements TransactionContentWithResult { private SHumanTaskInstance humanTaskInstance; private final ActivityInstanceService activityInstanceService; private final long activityInstanceId; public GetHumanTaskInstance(final ActivityInstanceService activityInstanceService, final long activityInstanceId) { this.activityInstanceService = activityInstanceService; this.activityInstanceId = activityInstanceId; } @Override public void execute() throws SBonitaException { humanTaskInstance = activityInstanceService.getHumanTaskInstance(activityInstanceId); } @Override public SHumanTaskInstance getResult() { return humanTaskInstance; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/task/GetNumberOfOpenTasksForUsers.java ================================================ /** * 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.api.impl.transaction.task; import java.util.List; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; /** * @author Yanyan Liu */ public class GetNumberOfOpenTasksForUsers implements TransactionContentWithResult> { private final List userIds; private final ActivityInstanceService activityInstanceService; private Map result; public GetNumberOfOpenTasksForUsers(final List userIds, final ActivityInstanceService activityInstanceService) { this.userIds = userIds; this.activityInstanceService = activityInstanceService; } @Override public void execute() throws SBonitaException { result = activityInstanceService.getNumberOfOpenTasksForUsers(userIds); } @Override public Map getResult() { return result; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/transaction/task/SetTaskPriority.java ================================================ /** * 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.api.impl.transaction.task; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.STaskPriority; /** * @author Zhang Bole */ public final class SetTaskPriority implements TransactionContent { private final long activityInstanceId; private final STaskPriority priority; private final ActivityInstanceService activityInstanceService; public SetTaskPriority(final ActivityInstanceService activityInstanceService, final long activityInstanceId, final STaskPriority sTaskPriority) { this.activityInstanceService = activityInstanceService; this.activityInstanceId = activityInstanceId; priority = sTaskPriority; } @Override public void execute() throws SBonitaException { final SActivityInstance activity = activityInstanceService.getActivityInstance(activityInstanceId); activityInstanceService.setTaskPriority(activity, priority); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/utils/VisibleForTesting.java ================================================ /** * 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.api.utils; /** * @author Emmanuel Duchastenier */ public @interface VisibleForTesting { } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/authorization/PermissionServiceImpl.java ================================================ /** * 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.authorization; import static java.lang.String.format; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.stream.Collectors; import groovy.lang.GroovyClassLoader; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.impl.APIAccessorImpl; import org.bonitasoft.engine.api.permission.APICallContext; import org.bonitasoft.engine.api.permission.PermissionRule; import org.bonitasoft.engine.authorization.properties.CompoundPermissionsMapping; import org.bonitasoft.engine.authorization.properties.CustomPermissionsMapping; import org.bonitasoft.engine.authorization.properties.DynamicPermissionsChecks; import org.bonitasoft.engine.authorization.properties.PropertiesWithSet; import org.bonitasoft.engine.authorization.properties.ResourcesPermissionsMapping; import org.bonitasoft.engine.classloader.ClassLoaderIdentifier; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.page.ContentType; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.properties.BooleanProperty; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.service.impl.ServerLoggerWrapper; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.SSessionNotFoundException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.session.model.SSession; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; /** * Permission service implementation * * @author Baptiste Mesta */ @Component @Slf4j @ConditionalOnSingleCandidate(PermissionService.class) public class PermissionServiceImpl implements PermissionService { public static final String PROPERTY_TO_ENABLE_DYNAMIC_PERMISSIONS = "bonita.runtime.authorization.dynamic-check.enabled"; public static final String RESOURCES_PROPERTY = "resources"; public static final String PROPERTY_CONTENT_TYPE = "contentType"; public static final String PROPERTY_API_EXTENSIONS = "apiExtensions"; public static final String PROPERTY_METHOD_MASK = "%s.method"; public static final String PROPERTY_PATH_TEMPLATE_MASK = "%s.pathTemplate"; public static final String PROPERTY_PERMISSIONS_MASK = "%s.permissions"; public static final String RESOURCE_PERMISSION_KEY_MASK = "%s|extension/%s"; public static final String RESOURCE_PERMISSION_VALUE = "[%s]"; public static final String EXTENSION_SEPARATOR = ","; private final ClassLoaderService classLoaderService; private final SessionAccessor sessionAccessor; private final SessionService sessionService; protected GroovyClassLoader groovyClassLoader; private final CompoundPermissionsMapping compoundPermissionsMapping; private final ResourcesPermissionsMapping resourcesPermissionsMapping; private final CustomPermissionsMapping customPermissionsMapping; protected final DynamicPermissionsChecks dynamicPermissionsChecks; protected final BooleanProperty dynamicPermissionCheck; public PermissionServiceImpl(final ClassLoaderService classLoaderService, final SessionAccessor sessionAccessor, final SessionService sessionService, CompoundPermissionsMapping compoundPermissionsMapping, ResourcesPermissionsMapping resourcesPermissionsMapping, CustomPermissionsMapping customPermissionsMapping, DynamicPermissionsChecks dynamicPermissionsChecks, @Value("${bonita.runtime.authorization.dynamic-check.enabled:true}") boolean dynamicPermissionCheck) { this.classLoaderService = classLoaderService; this.sessionAccessor = sessionAccessor; this.sessionService = sessionService; this.compoundPermissionsMapping = compoundPermissionsMapping; this.resourcesPermissionsMapping = resourcesPermissionsMapping; this.customPermissionsMapping = customPermissionsMapping; this.dynamicPermissionsChecks = dynamicPermissionsChecks; this.dynamicPermissionCheck = initDynamicPermissionsEnabledProperty(dynamicPermissionCheck); } BooleanProperty initDynamicPermissionsEnabledProperty(boolean dynamicPermissionsEnabled) { return new BooleanProperty("Dynamic permissions", PROPERTY_TO_ENABLE_DYNAMIC_PERMISSIONS, dynamicPermissionsEnabled); } protected boolean checkAPICallWithScript(final String className, final APICallContext context) throws SExecutionException, ClassNotFoundException { checkStarted(); //groovy class loader load class from files and cache then when loaded, no need to do some lazy loading or load all class on start Class aClass = getRuleClass(className); if (!PermissionRule.class.isAssignableFrom(aClass)) { throw new SExecutionException("The class " + aClass.getName() + " does not implements org.bonitasoft.engine.api.permission.PermissionRule"); } SSession session = getSession(); try { final APISession apiSession = ModelConvertor.toAPISession(session); final PermissionRule permissionRule = (PermissionRule) aClass.getDeclaredConstructor().newInstance(); return permissionRule.isAllowed(apiSession, context, createAPIAccessorImpl(), new ServerLoggerWrapper(permissionRule.getClass(), log)); } catch (final Throwable e) { throw new SExecutionException("The permission rule " + aClass.getName() + " threw an exception", e); } } protected Class getRuleClass(String className) throws SExecutionException, ClassNotFoundException { return Class.forName(className, true, groovyClassLoader); } public SSession getSession() throws SExecutionException { try { return sessionService.getSession(sessionAccessor.getSessionId()); } catch (SSessionNotFoundException | SessionIdNotSetException e) { throw new SExecutionException("The session is not set.", e); } } public void reload() throws SExecutionException { stop(); try { start(); } catch (SBonitaException e) { throw new SExecutionException("The permission rule service could not be reloaded", e); } } protected APIAccessorImpl createAPIAccessorImpl() { return new APIAccessorImpl(); } private void checkStarted() throws SExecutionException { if (groovyClassLoader == null) { throw new SExecutionException("The permission rule service is not started"); } } @Override public void start() throws SBonitaException { groovyClassLoader = new GroovyClassLoader( classLoaderService.getClassLoader(ClassLoaderIdentifier.TENANT)); groovyClassLoader.setShouldRecompile(true); } @Override public void stop() { if (groovyClassLoader != null) { groovyClassLoader.clearCache(); groovyClassLoader = null; } } @Override public boolean isAuthorized(APICallContext apiCallContext) throws SExecutionException { if (dynamicPermissionCheck.isEnabled()) { // Check if there is an active dynamic permission for this resource: final Set resourceDynamicPermissions = getDeclaredPermissions(apiCallContext.getApiName(), apiCallContext.getResourceName(), apiCallContext.getMethod(), apiCallContext.getResourceId(), dynamicPermissionsChecks); if (!resourceDynamicPermissions.isEmpty()) { // if there is a dynamic rule, use it to check the permissions if (log.isTraceEnabled()) { log.trace("Dynamic REST API permissions check"); } return isAuthorizedByDynamicPermissions(apiCallContext, getSession().getUserPermissions(), resourceDynamicPermissions); } } // if there is no dynamic rule, use the static permissions return isAuthorizedByStaticPermissions(apiCallContext); } protected boolean isAuthorizedByStaticPermissions(APICallContext apiCallContext) throws SExecutionException { if (log.isTraceEnabled()) { log.trace("Static REST API permissions check"); } final Set resourcePermissions = getDeclaredPermissions(apiCallContext.getApiName(), apiCallContext.getResourceName(), apiCallContext.getMethod(), apiCallContext.getResourceId(), resourcesPermissionsMapping); final Set userPermissions = getSession().getUserPermissions(); for (final String resourcePermission : resourcePermissions) { if (userPermissions.contains(resourcePermission)) { return true; } } log.debug( "Unauthorized access to " + apiCallContext.getMethod() + " " + apiCallContext.getApiName() + "/" + apiCallContext.getResourceName() + (apiCallContext.getResourceId() != null ? "/" + apiCallContext.getResourceId() : "") + " attempted by " + getSession().getUserName() + ", required permissions: " + resourcePermissions); return false; } protected boolean isAuthorizedByDynamicPermissions(APICallContext apiCallContext, Set userPermissions, Set resourceDynamicPermissions) throws SExecutionException { checkResourceAuthorizationsSyntax(resourceDynamicPermissions); if (checkDynamicPermissionsWithProfilesOrUsername(resourceDynamicPermissions, userPermissions)) { return true; } final String resourceClassName = getResourceClassName(resourceDynamicPermissions); if (resourceClassName != null) { try { return checkDynamicPermissionsWithScript(apiCallContext, resourceClassName); } catch (ClassNotFoundException e) { throw new SExecutionException( "Unable to execute the security rule " + resourceClassName + " for the api call " + apiCallContext + " because the class " + resourceClassName + " is not found", e); } } return false; } protected void checkResourceAuthorizationsSyntax(final Set resourceAuthorizations) { for (final String resourceAuthorization : resourceAuthorizations) { if (!resourceAuthorization.matches("(" + USER_TYPE_AUTHORIZATION_PREFIX + "|" + PROFILE_TYPE_AUTHORIZATION_PREFIX + "|" + SCRIPT_TYPE_AUTHORIZATION_PREFIX + ")\\|.+")) { if (log.isWarnEnabled()) { log.warn("Error while getting dynamic authorizations. Unknown syntax: " + resourceAuthorization + " defined in dynamic-permissions-checks.properties"); } throw new IllegalArgumentException( format("Dynamic permission rule %s is not valid", resourceAuthorization)); } } } protected Set getResourceAuthorizationsForProfileOrUser(final Set resourcePermissions) { return resourcePermissions.stream() .filter(item -> item.startsWith(PROFILE_TYPE_AUTHORIZATION_PREFIX + "|") || item.startsWith(USER_TYPE_AUTHORIZATION_PREFIX + "|")) .collect(Collectors.toSet()); } protected boolean checkDynamicPermissionsWithProfilesOrUsername(final Set resourceAuthorizations, final Set userPermissions) { return getResourceAuthorizationsForProfileOrUser(resourceAuthorizations).stream() .anyMatch(userPermissions::contains); } protected boolean checkDynamicPermissionsWithScript(final APICallContext apiCallContext, final String resourceClassName) throws SExecutionException, ClassNotFoundException { final boolean authorized = checkAPICallWithScript(resourceClassName, apiCallContext); if (!authorized) { if (log.isDebugEnabled()) { StringBuilder msg = new StringBuilder().append("Unauthorized access to ") .append(apiCallContext.getMethod()).append(" ").append(apiCallContext.getApiName()) .append("/").append(apiCallContext.getResourceName()) .append(apiCallContext.getResourceId() != null ? "/" + apiCallContext.getResourceId() : "") .append(", Permission script: ").append(resourceClassName); msg.append(", attempted by ").append(getSession().getUserName()); log.debug(msg.toString()); } } return authorized; } protected String getResourceClassName(final Set resourcePermissions) { String className = null; for (final String resourcePermission : resourcePermissions) { if (resourcePermission.startsWith(SCRIPT_TYPE_AUTHORIZATION_PREFIX + "|")) { className = resourcePermission.substring((SCRIPT_TYPE_AUTHORIZATION_PREFIX + "|").length()); } } return className; } public Set getResourceDynamicPermissions(final String resourceKey) { return dynamicPermissionsChecks.getPropertyAsSet(resourceKey); } protected Set getDeclaredPermissions(final String apiName, final String resourceName, final String method, final String resourceQualifiers, final ResourcesPermissionsMapping resourcesPermissionsMapping) { List resourceQualifiersIds = null; if (resourceQualifiers != null) { resourceQualifiersIds = Arrays .asList(resourceQualifiers.split(ResourcesPermissionsMapping.RESOURCE_IDS_SEPARATOR)); } Set resourcePermissions = resourcesPermissionsMapping.getResourcePermissions(method, apiName, resourceName, resourceQualifiersIds); if (resourcePermissions.isEmpty()) { resourcePermissions = resourcesPermissionsMapping.getResourcePermissionsWithWildCard(method, apiName, resourceName, resourceQualifiersIds); } if (resourcePermissions.isEmpty()) { resourcePermissions = resourcesPermissionsMapping.getResourcePermissions(method, apiName, resourceName); } return resourcePermissions; } @Override public void addPermissions(final String pageName, final Properties pageProperties) { Set customPagePermissions = getCustomPagePermissions( pageProperties.getProperty(RESOURCES_PROPERTY), resourcesPermissionsMapping); addRestApiExtensionPermissions(resourcesPermissionsMapping, pageProperties); addPagePermissions(pageName, pageProperties, customPagePermissions); } private void addPagePermissions(String pageName, Properties pageProperties, Set customPagePermissions) { if (ContentType.PAGE.equals(pageProperties.getProperty(PROPERTY_CONTENT_TYPE)) || ContentType.LAYOUT.equals(pageProperties.getProperty(PROPERTY_CONTENT_TYPE))) { compoundPermissionsMapping.setInternalPropertyAsSet(pageName, customPagePermissions); } } @Override public void removePermissions(Properties pageProperties) { for (String key : getApiExtensionResourcesPermissionsMapping(pageProperties).keySet()) { resourcesPermissionsMapping.removeInternalProperty(key); } compoundPermissionsMapping.removeInternalProperty(pageProperties.getProperty(PageService.PROPERTIES_NAME)); } public Set getCustomPagePermissions(final String declaredPageResources, final ResourcesPermissionsMapping resourcesPermissionsMapping) { final Set pageRestResources = PropertiesWithSet.stringToSet(declaredPageResources); final Set permissions = new HashSet<>(); for (final String pageRestResource : pageRestResources) { final Set resourcePermissions = resourcesPermissionsMapping.getPropertyAsSet(pageRestResource); if (resourcePermissions.isEmpty()) { log.warn("Error while getting resources permissions. Unknown resource: {} defined in page.properties", pageRestResource); } permissions.addAll(resourcePermissions); } return permissions; } void addRestApiExtensionPermissions(final ResourcesPermissionsMapping resourcesPermissionsMapping, final Properties pageProperties) { final Map permissionsMapping = getApiExtensionResourcesPermissionsMapping( pageProperties); permissionsMapping.keySet() .forEach(key -> resourcesPermissionsMapping.setInternalProperty(key, permissionsMapping.get(key))); } private Map getApiExtensionResourcesPermissionsMapping(Properties pageProperties) { final Properties propertiesWithSet = new PropertiesWithSet(pageProperties); final Map permissionsMap = new HashMap<>(); if (ContentType.API_EXTENSION.equals(propertiesWithSet.getProperty(PROPERTY_CONTENT_TYPE))) { final String apiExtensionList = propertiesWithSet.getProperty(PROPERTY_API_EXTENSIONS); final String[] apiExtensions = apiExtensionList.split(EXTENSION_SEPARATOR); for (final String apiExtension : apiExtensions) { final String method = propertiesWithSet .getProperty(String.format(PROPERTY_METHOD_MASK, apiExtension.trim())); String pathTemplate = propertiesWithSet .getProperty(String.format(PROPERTY_PATH_TEMPLATE_MASK, apiExtension.trim())); // Remove '/' prefix if declared in page.properties if (pathTemplate != null && pathTemplate.startsWith("/")) { pathTemplate = pathTemplate.substring(1); } final String permissions = propertiesWithSet .getProperty(String.format(PROPERTY_PERMISSIONS_MASK, apiExtension.trim())); permissionsMap.put(String.format(RESOURCE_PERMISSION_KEY_MASK, method, pathTemplate), String.format(RESOURCE_PERMISSION_VALUE, permissions)); } } return permissionsMap; } @Override public Set getResourcePermissions(final String resourceKey) { return resourcesPermissionsMapping.getPropertyAsSet(resourceKey); } public void addCustomEntityPermissions(final String entity, final Set resourcePermissions) { customPermissionsMapping.setPropertyAsSet(entity, resourcePermissions); } public void removeCustomEntityPermissions(String entity) { customPermissionsMapping.removeProperty(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bar/BusinessArchiveService.java ================================================ /** * 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.bar; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException; import org.bonitasoft.engine.commons.exceptions.SAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; /** * @author Baptiste Mesta */ public interface BusinessArchiveService { SProcessDefinition deploy(BusinessArchive businessArchive) throws SObjectCreationException, SAlreadyExistsException; BusinessArchive export(long processDefinitionId) throws SBonitaException, InvalidBusinessArchiveFormatException; void delete(long processDefinitionId) throws SProcessDefinitionNotFoundException, SObjectModificationException; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bar/BusinessArchiveServiceImpl.java ================================================ /** * 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.bar; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import static org.bonitasoft.engine.form.FormMappingTarget.LEGACY; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.impl.SessionInfos; import org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException; import org.bonitasoft.engine.bpm.bar.form.model.FormMappingDefinition; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.classloader.SClassLoaderException; import org.bonitasoft.engine.commons.exceptions.SAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SV6FormsDeployException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.exception.SDeletingEnabledProcessException; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; import org.bonitasoft.engine.core.process.definition.exception.SProcessDeletionException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; /** * @author Baptiste Mesta */ @Slf4j public class BusinessArchiveServiceImpl implements BusinessArchiveService { private final ProcessDefinitionService processDefinitionService; private final BusinessArchiveArtifactsManager businessArchiveArtifactsManager; private final ClassLoaderService classLoaderService; public BusinessArchiveServiceImpl(ProcessDefinitionService processDefinitionService, BusinessArchiveArtifactsManager businessArchiveArtifactsManager, ClassLoaderService classLoaderService) { this.processDefinitionService = processDefinitionService; this.businessArchiveArtifactsManager = businessArchiveArtifactsManager; this.classLoaderService = classLoaderService; } @Override public SProcessDefinition deploy(BusinessArchive businessArchive) throws SObjectCreationException, SAlreadyExistsException { final DesignProcessDefinition designProcessDefinition = businessArchive.getProcessDefinition(); SProcessDefinition sProcessDefinition; try { checkIfExists(designProcessDefinition); checkNoV6Forms(businessArchive); sProcessDefinition = processDefinitionService.store(designProcessDefinition); final boolean isResolved = businessArchiveArtifactsManager.resolveDependencies(businessArchive, sProcessDefinition); if (isResolved) { processDefinitionService.resolveProcess(sProcessDefinition.getId()); } classLoaderService.refreshClassLoaderAfterUpdate(identifier(ScopeType.PROCESS, sProcessDefinition.getId())); } catch (SV6FormsDeployException | SAlreadyExistsException e) { throw e; } catch (final SBonitaException e) { throw new SObjectCreationException(e); } info(sProcessDefinition); return sProcessDefinition; } private void checkNoV6Forms(BusinessArchive businessArchive) throws SV6FormsDeployException { List formMappings = businessArchive.getFormMappingModel().getFormMappings(); for (FormMappingDefinition formMapping : formMappings) { if (formMapping.getTarget() == LEGACY) { throw new SV6FormsDeployException("The process contains v6 forms"); } } } void checkIfExists(DesignProcessDefinition designProcessDefinition) throws SBonitaReadException, SAlreadyExistsException { try { processDefinitionService.getProcessDefinitionId(designProcessDefinition.getName(), designProcessDefinition.getVersion()); throw new SAlreadyExistsException("The process " + designProcessDefinition.getName() + " in version " + designProcessDefinition.getVersion() + " already exists."); } catch (final SProcessDefinitionNotFoundException e) { // ok } } void info(SProcessDefinition sProcessDefinition) { log.info("The user <{}> has installed process <{}> in version <{}> with id <{}>", SessionInfos.getUserNameFromSession(), sProcessDefinition.getName(), sProcessDefinition.getVersion(), sProcessDefinition.getId()); } @Override public BusinessArchive export(long processDefinitionId) throws SBonitaException, InvalidBusinessArchiveFormatException { final DesignProcessDefinition designProcessDefinition = processDefinitionService .getDesignProcessDefinition(processDefinitionId); return businessArchiveArtifactsManager.exportBusinessArchive(processDefinitionId, designProcessDefinition); } @Override public void delete(long processDefinitionId) throws SProcessDefinitionNotFoundException, SObjectModificationException { try { final SProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); businessArchiveArtifactsManager.deleteDependencies(processDefinition); processDefinitionService.delete(processDefinition.getId()); classLoaderService.removeLocalClassloader(identifier(ScopeType.PROCESS, processDefinition.getId())); } catch (SBonitaReadException | SProcessDeletionException | SDeletingEnabledProcessException | SRecorderException | SClassLoaderException e) { throw new SObjectModificationException( "Unable to delete the process definition <" + processDefinitionId + ">", e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ConstraintsDefinitionHelper.java ================================================ /** * 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.bpm.contract.validation; import java.util.List; import org.bonitasoft.engine.core.process.definition.model.SContractDefinition; import org.bonitasoft.engine.core.process.definition.model.SInputDefinition; public class ConstraintsDefinitionHelper { protected SInputDefinition getInputDefinition(final SContractDefinition contract, final String inputName) { final List inputs = contract.getInputDefinitions(); return getInputDefinition(inputName, inputs); } private SInputDefinition getInputDefinition(final String inputName, final List inputs) { SInputDefinition inputDefinition = getInputDefinitionInSimple(inputName, inputs); if (inputDefinition == null) { inputDefinition = getInputDefinitionInComplex(inputName, inputs); } return inputDefinition; } private SInputDefinition getInputDefinitionInSimple(final String inputName, final List simpleInputs) { for (final SInputDefinition sInputDefinition : simpleInputs) { if (sInputDefinition.getName().equals(inputName)) { return sInputDefinition; } } return null; } private SInputDefinition getInputDefinitionInComplex(final String inputName, final List complexInputs) { for (final SInputDefinition sInputDefinition : complexInputs) { if (sInputDefinition.getName().equals(inputName)) { return sInputDefinition; } final SInputDefinition inputDefinition = getInputDefinition(inputName, sInputDefinition.getInputDefinitions()); if (inputDefinition != null) { return inputDefinition; } } return null; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ContractConstraintsValidator.java ================================================ /** * 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.bpm.contract.validation; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.process.definition.model.SConstraintDefinition; import org.bonitasoft.engine.core.process.definition.model.SContractDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.ExpressionExecutorStrategy; import org.bonitasoft.engine.expression.ExpressionService; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory; @Slf4j public class ContractConstraintsValidator { private final ExpressionService expressionService; public ContractConstraintsValidator(ExpressionService expressionService) { this.expressionService = expressionService; } public void validate(long processDefinitionId, final SContractDefinition contract, final Map variables) throws SContractViolationException { final Map vars = (variables == null ? Collections. emptyMap() : variables); final List comments = new ArrayList<>(); final Map context = new HashMap<>(vars.size()); context.putAll(vars); context.put(ExpressionExecutorStrategy.DEFINITION_ID, processDefinitionId); for (final SConstraintDefinition constraint : contract.getConstraints()) { log.debug("Evaluating constraint [{}] on input(s) {}", constraint.getName(), constraint.getInputNames()); validateConstraint(comments, constraint, context); } if (!comments.isEmpty()) { throw new SContractViolationException("Error while validating constraints", comments); } } private void validateConstraint(final List comments, final SConstraintDefinition constraint, final Map variables) throws SContractViolationException { Boolean valid; try { valid = (Boolean) expressionService.evaluate(createGroovyExpression(constraint), variables, Collections. emptyMap(), ContainerState.ACTIVE); } catch (SExpressionTypeUnknownException | SExpressionEvaluationException | SExpressionDependencyMissingException | SInvalidExpressionException e) { log.error("Constraint [{}] on input(s) {} evaluation failed.", constraint.getName(), constraint.getInputNames(), e); throw new SContractViolationException("Exception while validating constraints", e); } if (!valid) { log.warn("Constraint [{}] on input(s) {} is not valid", constraint.getName(), constraint.getInputNames()); comments.add(constraint.getExplanation()); } } private SExpression createGroovyExpression(SConstraintDefinition constraint) throws SInvalidExpressionException { return BuilderFactory.get(SExpressionBuilderFactory.class).createNewInstance().setName(constraint.getName()) .setContent(constraint.getExpression()) .setExpressionType(ExpressionExecutorStrategy.TYPE_READ_ONLY_CONDITION_SCRIPT) .setInterpreter(ExpressionExecutorStrategy.INTERPRETER_GROOVY) .setReturnType(Boolean.class.getName()).done(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ContractStructureValidator.java ================================================ /** * 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.bpm.contract.validation; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.core.process.definition.model.SContractDefinition; import org.bonitasoft.engine.core.process.definition.model.SInputContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SInputDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException; @Slf4j public class ContractStructureValidator { private final ContractTypeValidator typeValidator; public ContractStructureValidator(final ContractTypeValidator typeValidator) { this.typeValidator = typeValidator; } public void validate(final SContractDefinition contract, final Map inputs) throws SContractViolationException { final ErrorReporter errorReporter = new ErrorReporter(); validateInputContainer(contract, inputs, errorReporter); if (errorReporter.hasError()) { throw new SContractViolationException("Error while validating expected inputs", errorReporter.getErrors()); } } void validateInputContainer(SInputContainerDefinition inputContainer, Map inputs, ErrorReporter errorReporter) { logInputsWhichAreNotInContract(inputContainer.getInputDefinitions(), inputs); for (final SInputDefinition inputDefinition : inputContainer.getInputDefinitions()) { // For each input, fills the errorReporter if some rule is not valid: validateInput(inputs != null ? inputs : Collections. emptyMap(), errorReporter, inputDefinition); } } private void validateInput(Map inputs, ErrorReporter errorReporter, SInputDefinition inputDefinition) { final String inputName = inputDefinition.getName(); if (expectedInputIsProvided(inputs, errorReporter, inputName)) { final Serializable input = inputs.get(inputName); if (input != null && typeValidator.validate(inputDefinition, input, errorReporter)) { validateChildren(inputs, errorReporter, inputDefinition); } } } private void validateChildren(Map inputs, ErrorReporter errorReporter, SInputDefinition inputDefinition) { if (inputDefinition.hasChildren() && inputDefinition.getType() == null) { if (inputDefinition.isMultiple()) { for (final Map complexItem : (List>) inputs .get(inputDefinition.getName())) { // we tolerate (and ignore) null values in lists: if (complexItem != null) { validateInputContainer(inputDefinition, complexItem, errorReporter); } } } else { validateInputContainer(inputDefinition, (Map) inputs.get(inputDefinition.getName()), errorReporter); } } } private boolean expectedInputIsProvided(Map inputs, ErrorReporter errorReporter, String inputName) { if (!inputs.containsKey(inputName)) { errorReporter.addError("Expected input [" + inputName + "] is missing"); return false; } return true; } // Log when some inputs were not expected but provided: private void logInputsWhichAreNotInContract(final List simpleInputs, final Map inputs) { if (log.isDebugEnabled()) { for (final String input : getInputsWhichAreNotInContract(simpleInputs, inputs)) { log.debug("Unexpected input [" + input + "] provided"); } } } private List getInputsWhichAreNotInContract(final List simpleInputs, final Map inputs) { if (inputs == null || inputs.isEmpty()) { return Collections.emptyList(); } final List keySet = new ArrayList<>(inputs.keySet()); for (final SInputDefinition def : simpleInputs) { keySet.remove(def.getName()); } return keySet; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ContractTypeValidator.java ================================================ /** * 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.bpm.contract.validation; import java.util.List; import java.util.Map; import org.bonitasoft.engine.core.process.definition.model.SInputDefinition; import org.bonitasoft.engine.core.process.definition.model.SType; /** * Validate that a value is assignable to a given contract type * * @author Colin Puy */ public class ContractTypeValidator { public boolean validate(final SInputDefinition definition, final Object object, ErrorReporter errorReporter) { if (definition.hasChildren() && definition.getType() == null) { if (!isValidForComplexType(definition, object, errorReporter)) { errorReporter.addError(object + " cannot be assigned to " + (definition.isMultiple() ? "multiple " : "") + "COMPLEX type"); return false; } } else { if (!isValidForSimpleType(definition, object)) { errorReporter.addError(object + " cannot be assigned to " + (definition.isMultiple() ? "multiple " : "") + definition.getType()); return false; } } return true; } private boolean isValidForSimpleType(final SInputDefinition definition, final Object object) { if (definition.isMultiple()) { return isValidForMultipleSimpleType(definition, object); } else { SType type = definition.getType(); return type != null && type.validate(object); } } @SuppressWarnings("unchecked") private boolean isValidForMultipleSimpleType(final SInputDefinition definition, final Object object) { if (!(object instanceof List)) { return false; } for (final Object item : (List) object) { if (!definition.getType().validate(item)) { return false; } } return true; } private boolean isValidForComplexType(final SInputDefinition definition, final Object object, ErrorReporter errorReporter) { if (definition.isMultiple()) { return isValidForMultipleComplexType(definition, object, errorReporter); } else { return isValidForSimpleComplexType(definition, object, errorReporter); } } @SuppressWarnings("unchecked") private boolean isValidForMultipleComplexType(final SInputDefinition definition, final Object object, ErrorReporter errorReporter) { if (!(object instanceof List)) { return false; } for (final Object item : (List) object) { //throws exception if invalid isValidForSimpleComplexType(definition, item, errorReporter); } return true; } private boolean isValidForSimpleComplexType(final SInputDefinition definition, final Object object, ErrorReporter errorReporter) { try { @SuppressWarnings("unchecked") final Map map = (Map) object; for (final SInputDefinition sInputDefinition : definition.getInputDefinitions()) { Object value = (map == null) ? null : map.get(sInputDefinition.getName()); if (value != null) { validate(sInputDefinition, value, errorReporter); } } return !errorReporter.hasError(); } catch (final ClassCastException e) { return false; } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ContractValidator.java ================================================ /** * 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.bpm.contract.validation; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.core.process.definition.model.SContractDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException; /** * Validate tasks inputs according to given contract * * @author Matthieu Chaffotte * @author Colin Puy */ public class ContractValidator { private final ContractStructureValidator structureValidator; private final ContractConstraintsValidator rulesValidator; public ContractValidator(final ContractStructureValidator contractStructureValidator, final ContractConstraintsValidator contractRulesValidator) { structureValidator = contractStructureValidator; rulesValidator = contractRulesValidator; } public void validate(long processDefinitionId, final SContractDefinition contract, final Map variables) throws SContractViolationException { structureValidator.validate(contract, variables); rulesValidator.validate(processDefinitionId, contract, variables); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ContractValidatorFactory.java ================================================ /** * 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.bpm.contract.validation; import org.bonitasoft.engine.expression.ExpressionService; /** * Contract validator factory - might be replaced by a spring configuration * * @author Colin Puy */ public class ContractValidatorFactory { public ContractValidator createContractValidator(ExpressionService expressionService) { return new ContractValidator(new ContractStructureValidator(new ContractTypeValidator()), new ContractConstraintsValidator(expressionService)); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ContractVariableHelper.java ================================================ /** * 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.bpm.contract.validation; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.bonitasoft.engine.core.process.definition.model.SConstraintDefinition; public class ContractVariableHelper { /** * @param constraint * @param contractVariables * @return * list of elements that must be checked by this mandatory constraint */ public List> buildMandatoryMultipleInputVariables(final SConstraintDefinition constraint, final Map contractVariables) { final List> constraintVariablesList = new ArrayList>(); for (final String inputName : constraint.getInputNames()) { buildRecursiveVariableList(contractVariables, constraintVariablesList, inputName); } return constraintVariablesList; } private List> buildRecursiveVariableList(final Map currentVariables, final List> constraintVariablesList, final String inputName) { if (currentVariables.containsKey(inputName)) { final Map value = new HashMap(); value.put(inputName, currentVariables.get(inputName)); constraintVariablesList.add(value); } else { buildRecursiveComplexVariableList(currentVariables, constraintVariablesList, inputName); } return constraintVariablesList; } @SuppressWarnings("unchecked") private Collection> buildRecursiveComplexVariableList( final Map currentVariables, final List> constraintVariablesList, final String inputName) { for (final Entry variableEntry : currentVariables.entrySet()) { final Object variableValue = variableEntry.getValue(); if (variableValue instanceof Map) { //complex case buildRecursiveVariableList((Map) variableValue, constraintVariablesList, inputName); } if (variableValue instanceof List) { //multiple case for (final Serializable variableValueItem : (List) variableValue) { if (variableValueItem instanceof Map) { //complex case buildRecursiveVariableList((Map) variableValueItem, constraintVariablesList, inputName); } } } } return constraintVariablesList; } @SuppressWarnings("unchecked") public List> convertMultipleToList(final Map multipleVariable) { final List> multipleVariables = new ArrayList>(); final Set> entrySet = multipleVariable.entrySet(); for (final Entry entry : entrySet) { if (entry.getValue() instanceof List) { for (final Serializable value : (List) entry.getValue()) { final Map item = new HashMap(); item.put(entry.getKey(), value); multipleVariables.add(item); } } } return multipleVariables; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/ErrorReporter.java ================================================ /** * 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.bpm.contract.validation; import java.util.ArrayList; import java.util.List; /** * @author Baptiste Mesta */ public class ErrorReporter { private final List errors = new ArrayList<>(); public void addError(String error) { errors.add(error); } public List getErrors() { return errors; } public boolean hasError() { return !errors.isEmpty(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/contract/validation/InputValidationException.java ================================================ /** * 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.bpm.contract.validation; @SuppressWarnings("serial") public class InputValidationException extends Exception { public InputValidationException(String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/model/impl/BPMInstancesCreator.java ================================================ /** * 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.bpm.model.impl; import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.SActorNotFoundException; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.api.impl.transaction.actor.GetActor; import org.bonitasoft.engine.bpm.connector.ConnectorState; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.business.data.RefBusinessDataRetriever; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.data.instance.TransientDataService; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.SOperatorType; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SBusinessDataDefinition; import org.bonitasoft.engine.core.process.definition.model.SCallActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition; 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.SHumanTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.SLoopCharacteristics; import org.bonitasoft.engine.core.process.definition.model.SMultiInstanceLoopCharacteristics; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SStandardLoopCharacteristics; import org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SIntermediateCatchEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SIntermediateThrowEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition; 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.RefBusinessDataService; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.*; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException; 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.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance; 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.STaskPriority; import org.bonitasoft.engine.core.process.instance.model.builder.SActivityInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SHumanTaskInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SManualTaskInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SManualTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SReceiveTaskInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SReceiveTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SSendTaskInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SSendTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SSubProcessActivityInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SSubProcessActivityInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.data.instance.api.ParentContainerResolver; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.data.instance.exception.SDataInstanceReadException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder; import org.bonitasoft.engine.data.instance.model.exceptions.SDataInstanceNotWellFormedException; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.log.LogMessageBuilder; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ @Slf4j public class BPMInstancesCreator { private final ActivityInstanceService activityInstanceService; private final ActorMappingService actorMappingService; private final GatewayInstanceService gatewayInstanceService; private final EventInstanceService eventInstanceService; private final ConnectorInstanceService connectorInstanceService; private final ExpressionResolverService expressionResolverService; private final DataInstanceService dataInstanceService; private final TransientDataService transientDataService; private final ParentContainerResolver parentContainerResolver; private final RefBusinessDataService refBusinessDataService; private final RefBusinessDataRetriever refBusinessDataRetriever; private FlowNodeStateManager stateManager; public BPMInstancesCreator(final ActivityInstanceService activityInstanceService, final ActorMappingService actorMappingService, final GatewayInstanceService gatewayInstanceService, final EventInstanceService eventInstanceService, final ConnectorInstanceService connectorInstanceService, final ExpressionResolverService expressionResolverService, final DataInstanceService dataInstanceService, final TransientDataService transientDataService, final ParentContainerResolver parentContainerResolver, RefBusinessDataService refBusinessDataService, final RefBusinessDataRetriever refBusinessDataRetriever) { super(); this.activityInstanceService = activityInstanceService; this.actorMappingService = actorMappingService; this.gatewayInstanceService = gatewayInstanceService; this.eventInstanceService = eventInstanceService; this.connectorInstanceService = connectorInstanceService; this.expressionResolverService = expressionResolverService; this.dataInstanceService = dataInstanceService; this.transientDataService = transientDataService; this.parentContainerResolver = parentContainerResolver; this.refBusinessDataService = refBusinessDataService; this.refBusinessDataRetriever = refBusinessDataRetriever; } public void setStateManager(final FlowNodeStateManager stateManager) { this.stateManager = stateManager; } public List createFlowNodeInstances(final Long processDefinitionId, final long rootContainerId, final long parentContainerId, final List flowNodeDefinitions, final long rootProcessInstanceId, final long parentProcessInstanceId, final SStateCategory stateCategory) throws SBonitaException { final List flownNodeInstances = new ArrayList<>(flowNodeDefinitions.size()); for (final SFlowNodeDefinition sFlowNodeDefinition : flowNodeDefinitions) { flownNodeInstances.add(createFlowNodeInstance(processDefinitionId, rootContainerId, parentContainerId, SFlowElementsContainerType.PROCESS, sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId, false, -1, stateCategory, -1)); } return flownNodeInstances; } public SFlowNodeInstance createFlowNodeInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition, final long rootProcessInstanceId, final long parentProcessInstanceId, final boolean createInnerActivity, final int loopCounter, final SStateCategory stateCategory, final long relatedActivityInstanceId) throws SBonitaException { final SFlowNodeInstance flownNodeInstance = toFlowNodeInstance(processDefinitionId, rootContainerId, parentContainerId, parentContainerType, sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId, createInnerActivity, loopCounter, stateCategory, relatedActivityInstanceId); if (SFlowNodeType.GATEWAY.equals(flownNodeInstance.getType())) { gatewayInstanceService.createGatewayInstance((SGatewayInstance) flownNodeInstance); } else if (flownNodeInstance instanceof SActivityInstance) { activityInstanceService.createActivityInstance((SActivityInstance) flownNodeInstance); } else { eventInstanceService.createEventInstance((SEventInstance) flownNodeInstance); } createConnectorInstances(flownNodeInstance, sFlowNodeDefinition.getConnectors(), SConnectorInstance.FLOWNODE_TYPE); return flownNodeInstance; } public SFlowNodeInstance toFlowNodeInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition, final long rootProcessInstanceId, final long parentProcessInstanceId, final boolean createInnerActivity, final int loopCounter, final SStateCategory stateCategory, final long relatedActivityInstanceId) throws SActorNotFoundException, SActivityReadException { if (!createInnerActivity && sFlowNodeDefinition instanceof SActivityDefinition activityDefinition) { final SLoopCharacteristics loopCharacteristics = activityDefinition.getLoopCharacteristics(); if (loopCharacteristics != null) { SFlowNodeInstanceBuilder builder; if (loopCharacteristics instanceof SStandardLoopCharacteristics) { builder = createLoopActivityInstance(processDefinitionId, rootContainerId, parentContainerId, rootProcessInstanceId, parentProcessInstanceId, activityDefinition); } else { builder = createMultiInstanceActivityInstance(processDefinitionId, rootContainerId, parentContainerId, rootProcessInstanceId, parentProcessInstanceId, activityDefinition, (SMultiInstanceLoopCharacteristics) loopCharacteristics); } builder.setState(stateManager.getFirstState(builder.getFlowNodeType())); builder.setStateCategory(stateCategory); return builder.done(); } } SFlowNodeInstanceBuilder builder; switch (sFlowNodeDefinition.getType()) { case AUTOMATIC_TASK: builder = createAutomaticTaskInstance(processDefinitionId, rootContainerId, parentContainerId, parentContainerType, sFlowNodeDefinition, parentProcessInstanceId); break; case END_EVENT: builder = createEndEventInstance(processDefinitionId, rootContainerId, parentContainerId, sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId); break; case GATEWAY: builder = createGatewayInstance(processDefinitionId, rootContainerId, parentContainerId, parentContainerType, sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId); break; case INTERMEDIATE_CATCH_EVENT: builder = createIntermediateCatchEventInstance(processDefinitionId, rootContainerId, parentContainerId, parentContainerType, sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId); break; case INTERMEDIATE_THROW_EVENT: builder = createIntermediateThrowEventInstance(processDefinitionId, rootContainerId, parentContainerId, parentContainerType, sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId); break; case MANUAL_TASK: builder = createManualTaskInstance(processDefinitionId, rootContainerId, parentContainerId, parentContainerType, sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId); break; case START_EVENT: builder = createStartEventInstance(processDefinitionId, rootContainerId, parentContainerId, parentContainerType, sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId); break; case USER_TASK: builder = createUserTaskInstance(processDefinitionId, rootContainerId, parentContainerId, parentContainerType, sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId); break; case RECEIVE_TASK: builder = createReceiveTaskInstance(processDefinitionId, rootContainerId, parentContainerId, parentContainerType, sFlowNodeDefinition, parentProcessInstanceId); break; case SEND_TASK: builder = createSendTaskInstance(processDefinitionId, rootContainerId, parentContainerId, parentContainerType, sFlowNodeDefinition, parentProcessInstanceId); break; case CALL_ACTIVITY: builder = createCallActivityInstance(processDefinitionId, rootContainerId, parentContainerId, parentContainerType, sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId); break; case SUB_PROCESS: builder = createSubProcessActivityInstance(processDefinitionId, rootContainerId, parentContainerId, parentContainerType, sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId); break; case BOUNDARY_EVENT: builder = createBoundaryEventInstance(processDefinitionId, rootContainerId, parentContainerId, parentContainerType, (SBoundaryEventDefinition) sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId, relatedActivityInstanceId); break; default: throw new SActivityReadException("Activity type not found : " + sFlowNodeDefinition.getType()); } builder.setLoopCounter(loopCounter); builder.setState(stateManager.getFirstState(builder.getFlowNodeType())); builder.setStateCategory(stateCategory); return builder.done(); } private SCallActivityInstanceBuilder createCallActivityInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SCallActivityDefinition callActivityDef = (SCallActivityDefinition) sFlowNodeDefinition; final SCallActivityInstanceBuilderFactory builderFact = BuilderFactory .get(SCallActivityInstanceBuilderFactory.class); final SCallActivityInstanceBuilder builder = builderFact.createNewCallActivityInstance( callActivityDef.getName(), callActivityDef.getId(), rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId, parentProcessInstanceId); updateActivityInstance(parentContainerId, parentContainerType, sFlowNodeDefinition, builder); return builder; } private SSubProcessActivityInstanceBuilder createSubProcessActivityInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SSubProcessDefinition subProcessActivityDef = (SSubProcessDefinition) sFlowNodeDefinition; final SSubProcessActivityInstanceBuilderFactory builderFact = BuilderFactory .get(SSubProcessActivityInstanceBuilderFactory.class); final SSubProcessActivityInstanceBuilder builder = builderFact.createNewSubProcessActivityInstance( subProcessActivityDef.getName(), subProcessActivityDef.getId(), rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId, parentProcessInstanceId, subProcessActivityDef.isTriggeredByEvent()); updateActivityInstance(parentContainerId, parentContainerType, sFlowNodeDefinition, builder); return builder; } private SHumanTaskInstanceBuilder createUserTaskInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition, final long rootProcessInstanceId, final long parentProcessInstanceId) throws SActorNotFoundException { final SHumanTaskInstanceBuilder builder = createHumanTaskInstance(processDefinitionId, rootContainerId, parentContainerId, sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId); updateActivityInstance(parentContainerId, parentContainerType, sFlowNodeDefinition, builder); return builder; } private SStartEventInstanceBuilder createStartEventInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SStartEventDefinition startEventDef = (SStartEventDefinition) sFlowNodeDefinition; final SStartEventInstanceBuilder builder = BuilderFactory.get(SStartEventInstanceBuilderFactory.class) .createNewStartEventInstance( startEventDef.getName(), startEventDef.getId(), rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId, parentProcessInstanceId); updateFlowNodeInstance(parentContainerId, parentContainerType, builder); return builder; } private SHumanTaskInstanceBuilder createManualTaskInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition, final long rootProcessInstanceId, final long parentProcessInstanceId) throws SActorNotFoundException { final SHumanTaskDefinition humanTaskDefinition = (SHumanTaskDefinition) sFlowNodeDefinition; final String actorName = humanTaskDefinition.getActorName(); final SActor actor = getActor(processDefinitionId, actorName); final SHumanTaskInstanceBuilder builder = BuilderFactory.get(SManualTaskInstanceBuilderFactory.class) .createNewManualTaskInstance( humanTaskDefinition.getName(), humanTaskDefinition.getId(), rootContainerId, parentContainerId, actor.getId(), processDefinitionId, rootProcessInstanceId, parentProcessInstanceId); fillHumanTask(humanTaskDefinition, builder); updateActivityInstance(parentContainerId, parentContainerType, sFlowNodeDefinition, builder); return builder; } public SManualTaskInstance createManualTaskInstance(final long parentUserTaskId, final String name, final long flowNodeDefinitionId, final String displayName, final long userId, final String description, final long dueDate, final STaskPriority priority) throws SFlowNodeNotFoundException, SFlowNodeReadException { final SHumanTaskInstance parentUserTask = (SHumanTaskInstance) activityInstanceService .getFlowNodeInstance(parentUserTaskId); final SManualTaskInstanceBuilderFactory manualTaskInstanceBuilderFact = BuilderFactory .get(SManualTaskInstanceBuilderFactory.class); final long processDefinitionId = parentUserTask .getLogicalGroup(manualTaskInstanceBuilderFact.getProcessDefinitionIndex()); final long rootProcessInstanceId = parentUserTask .getLogicalGroup(manualTaskInstanceBuilderFact.getRootProcessInstanceIndex()); final long parentProcessInstanceId = parentUserTask .getLogicalGroup(manualTaskInstanceBuilderFact.getParentProcessInstanceIndex()); final SManualTaskInstanceBuilder builder = manualTaskInstanceBuilderFact.createNewManualTaskInstance(name, flowNodeDefinitionId, parentUserTask.getRootContainerId(), parentUserTaskId, parentUserTask.getActorId(), processDefinitionId, rootProcessInstanceId, parentProcessInstanceId); builder.setParentContainerId(parentUserTaskId); builder.setParentActivityInstanceId(parentUserTaskId); builder.setAssigneeId(userId); builder.setExpectedEndDate(dueDate); builder.setDescription(description); builder.setDisplayDescription(description); builder.setDisplayName(displayName); builder.setPriority(priority); builder.setState(stateManager.getFirstState(builder.getFlowNodeType())); return builder.done(); } private SActor getActor(final long processDefinitionId, final String actorName) throws SActorNotFoundException { final GetActor getSActor = new GetActor(actorMappingService, actorName, processDefinitionId); try { getSActor.execute(); } catch (final SBonitaException sbe) { throw new SActorNotFoundException(sbe); } return getSActor.getResult(); } private SIntermediateThrowEventInstanceBuilder createIntermediateThrowEventInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SIntermediateThrowEventDefinition intermediateThrowEvent = (SIntermediateThrowEventDefinition) sFlowNodeDefinition; final SIntermediateThrowEventInstanceBuilder builder = BuilderFactory .get(SIntermediateThrowEventInstanceBuilderFactory.class) .createNewIntermediateThrowEventInstance( intermediateThrowEvent.getName(), intermediateThrowEvent.getId(), rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId, parentProcessInstanceId); updateFlowNodeInstance(parentContainerId, parentContainerType, builder); return builder; } private SIntermediateCatchEventInstanceBuilder createIntermediateCatchEventInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SIntermediateCatchEventDefinition intermediateCatchEvent = (SIntermediateCatchEventDefinition) sFlowNodeDefinition; final SIntermediateCatchEventInstanceBuilder builder = BuilderFactory .get(SIntermediateCatchEventInstanceBuilderFactory.class) .createNewIntermediateCatchEventInstance(intermediateCatchEvent.getName(), intermediateCatchEvent.getId(), rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId, parentProcessInstanceId); updateFlowNodeInstance(parentContainerId, parentContainerType, builder); return builder; } private SBoundaryEventInstanceBuilder createBoundaryEventInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SBoundaryEventDefinition boundaryEvent, final long rootProcessInstanceId, final long parentProcessInstanceId, final long activityInstanceId) { final SBoundaryEventInstanceBuilder builder = BuilderFactory.get(SBoundaryEventInstanceBuilderFactory.class) .createNewBoundaryEventInstance( boundaryEvent.getName(), boundaryEvent.isInterrupting(), boundaryEvent.getId(), rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId, parentProcessInstanceId, activityInstanceId); updateFlowNodeInstance(parentContainerId, parentContainerType, builder); return builder; } private SGatewayInstanceBuilder createGatewayInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SGatewayInstanceBuilder builder = BuilderFactory.get(SGatewayInstanceBuilderFactory.class) .createNewInstance(sFlowNodeDefinition.getName(), sFlowNodeDefinition.getId(), rootContainerId, parentContainerId, ((SGatewayDefinition) sFlowNodeDefinition).getGatewayType(), processDefinitionId, rootProcessInstanceId, parentProcessInstanceId); updateFlowNodeInstance(parentContainerId, parentContainerType, builder); return builder; } protected SEndEventInstanceBuilder createEndEventInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowNodeDefinition sFlowNodeDefinition, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SEndEventDefinition endEventDef = (SEndEventDefinition) sFlowNodeDefinition; return BuilderFactory.get(SEndEventInstanceBuilderFactory.class).createNewEndEventInstance( endEventDef.getName(), endEventDef.getId(), rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId, parentProcessInstanceId); } private SAutomaticTaskInstanceBuilder createAutomaticTaskInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition, final long parentProcessInstanceId) { final SAutomaticTaskInstanceBuilder builder = BuilderFactory.get(SAutomaticTaskInstanceBuilderFactory.class) .createNewAutomaticTaskInstance( sFlowNodeDefinition.getName(), sFlowNodeDefinition.getId(), rootContainerId, parentContainerId, processDefinitionId, rootContainerId, parentProcessInstanceId); updateActivityInstance(parentContainerId, parentContainerType, sFlowNodeDefinition, builder); return builder; } private SFlowNodeInstanceBuilder createReceiveTaskInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition, final long parentProcessInstanceId) { final SReceiveTaskInstanceBuilder builder = BuilderFactory.get(SReceiveTaskInstanceBuilderFactory.class) .createNewReceiveTaskInstance( sFlowNodeDefinition.getName(), sFlowNodeDefinition.getId(), rootContainerId, parentContainerId, processDefinitionId, rootContainerId, parentProcessInstanceId); updateActivityInstance(parentContainerId, parentContainerType, sFlowNodeDefinition, builder); return builder; } private SFlowNodeInstanceBuilder createSendTaskInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition, final long parentProcessInstanceId) { final SSendTaskInstanceBuilder builder = BuilderFactory.get(SSendTaskInstanceBuilderFactory.class) .createNewSendTaskInstance( sFlowNodeDefinition.getName(), sFlowNodeDefinition.getId(), rootContainerId, parentContainerId, processDefinitionId, rootContainerId, parentProcessInstanceId); updateActivityInstance(parentContainerId, parentContainerType, sFlowNodeDefinition, builder); return builder; } private void updateActivityInstance(final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SFlowNodeDefinition sFlowNodeDefinition, final SActivityInstanceBuilder builder) { updateFlowNodeInstance(parentContainerId, parentContainerType, builder); builder.setDescription(sFlowNodeDefinition.getDescription()); } private void updateFlowNodeInstance(final long parentContainerId, final SFlowElementsContainerType parentContainerType, final SFlowNodeInstanceBuilder builder) { long logicalGroup3; if (SFlowElementsContainerType.FLOWNODE.equals(parentContainerType)) { logicalGroup3 = parentContainerId; } else { logicalGroup3 = 0; } builder.setParentActivityInstanceId(logicalGroup3); } private SMultiInstanceActivityInstanceBuilder createMultiInstanceActivityInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final long rootProcessInstanceId, final long parentProcessInstanceId, final SActivityDefinition activityDefinition, final SMultiInstanceLoopCharacteristics loopCharacteristics) { final SMultiInstanceActivityInstanceBuilder builder = BuilderFactory .get(SMultiInstanceActivityInstanceBuilderFactory.class) .createNewOuterTaskInstance( activityDefinition.getName(), activityDefinition.getId(), rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId, parentProcessInstanceId, loopCharacteristics.isSequential()); builder.setLoopDataInputRef(loopCharacteristics.getLoopDataInputRef()); builder.setLoopDataOutputRef(loopCharacteristics.getLoopDataOutputRef()); builder.setDataInputItemRef(loopCharacteristics.getDataInputItemRef()); builder.setDataOutputItemRef(loopCharacteristics.getDataOutputItemRef()); return builder; } public SLoopActivityInstanceBuilder createLoopActivityInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final long rootProcessInstanceId, final long parentProcessInstanceId, final SActivityDefinition activityDefinition) { return BuilderFactory.get(SLoopActivityInstanceBuilderFactory.class).createNewOuterTaskInstance( activityDefinition.getName(), activityDefinition.getId(), rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId, parentProcessInstanceId); } private SHumanTaskInstanceBuilder createHumanTaskInstance(final long processDefinitionId, final long rootContainerId, final long parentContainerId, final SFlowNodeDefinition sFlowNodeDefinition, final long rootProcessInstanceId, final long parentProcessInstanceId) throws SActorNotFoundException { final SHumanTaskDefinition humanTaskDefinition = (SHumanTaskDefinition) sFlowNodeDefinition; final String actorName = humanTaskDefinition.getActorName(); final SActor actor = getActor(processDefinitionId, actorName); final SHumanTaskInstanceBuilder builder = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class) .createNewUserTaskInstance( humanTaskDefinition.getName(), humanTaskDefinition.getId(), rootContainerId, parentContainerId, actor.getId(), processDefinitionId, rootProcessInstanceId, parentProcessInstanceId); fillHumanTask(humanTaskDefinition, builder); return builder; } private void fillHumanTask(final SHumanTaskDefinition humanTaskDefinition, final SHumanTaskInstanceBuilder builder) { // Creation date: builder.setReachedStateDate(System.currentTimeMillis()); final String priority = humanTaskDefinition.getPriority(); if (priority != null) { // FIXME: use enum STaskPriority in client definition model: final STaskPriority sPriority = STaskPriority.valueOf(priority); builder.setPriority(sPriority); } } public void createConnectorInstances(final PersistentObject container, final List connectors, final String containerType) throws SBonitaException { final List connectorInstances = new ArrayList<>(connectors.size()); int executionOrder = 0; for (final SConnectorDefinition sConnectorDefinition : connectors) { final SConnectorInstance connectorInstance = createConnectorInstanceObject(container, containerType, sConnectorDefinition, executionOrder++); connectorInstances.add(connectorInstance); } for (final SConnectorInstance connectorInstance : connectorInstances) { connectorInstanceService.createConnectorInstance(connectorInstance); } } SConnectorInstance createConnectorInstanceObject(PersistentObject container, String containerType, SConnectorDefinition sConnectorDefinition, int executionOrder) { return SConnectorInstance.builder().name(sConnectorDefinition.getName()) .containerId(container.getId()) .containerType(containerType) .connectorId(sConnectorDefinition.getConnectorId()) .version(sConnectorDefinition.getVersion()) .activationEvent(sConnectorDefinition.getActivationEvent()) .state(ConnectorState.TO_BE_EXECUTED.name()) .executionOrder(executionOrder) .build(); } public void createDataInstances(final SProcessInstance processInstance, final SFlowElementContainerDefinition processContainer, final SProcessDefinition processDefinition, final SExpressionContext expressionContext, final List operations, final Map context, SExpressionContext expressionContextToEvaluateOperations) throws SDataInstanceNotWellFormedException, SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException, SDataInstanceException, SFlowNodeNotFoundException, SFlowNodeReadException { final List sDataDefinitions = processContainer.getDataDefinitions(); final List sDataInstances = new ArrayList<>(sDataDefinitions.size()); for (final SDataDefinition sDataDefinition : sDataDefinitions) { sDataInstances .add(createDataInstance(processInstance, expressionContext, operations, context, expressionContextToEvaluateOperations, sDataDefinition)); } if (hasLocalOrInheritedData(processDefinition, processContainer)) { // we create here only normal data, not transient because there is no transient on process createDataForProcess(sDataInstances); } debugLogVariableInitialized(processInstance, processDefinition); } SDataInstance createDataInstance(SProcessInstance processInstance, SExpressionContext expressionContext, List operations, Map context, SExpressionContext expressionContextToEvaluateOperations, SDataDefinition sDataDefinition) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException, SDataInstanceNotWellFormedException { SExpression expression; SExpressionContext currentExpressionContext; final SOperation operation; if ((operation = getOperationToSetData(sDataDefinition.getName(), operations)) != null) { expression = operation.getRightOperand(); currentExpressionContext = expressionContextToEvaluateOperations != null ? expressionContextToEvaluateOperations : expressionContext; currentExpressionContext.setInputValues(context); operations.remove(operation); } else { expression = sDataDefinition.getDefaultValueExpression(); currentExpressionContext = expressionContext; } return createDataInstanceObject(processInstance, sDataDefinition, evaluateExpression(sDataDefinition, expression, currentExpressionContext)); } private Serializable evaluateExpression(SDataDefinition sDataDefinition, SExpression expression, SExpressionContext currentExpressionContext) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { if (expression != null) { return (Serializable) expressionResolverService.evaluate(expression, currentExpressionContext); } else if (sDataDefinition.isTransientData()) { warningWhenTransientDataWithNullValue(); } return null; } void debugLogVariableInitialized(SProcessInstance processInstance, SProcessDefinition processDefinition) { if (log.isDebugEnabled()) { final StringBuilder stb = new StringBuilder(); stb.append("Initialized variables for process instance [name: <"); stb.append(processInstance.getName()); stb.append(">, version: <"); stb.append(processDefinition.getVersion()); stb.append(">, id: <"); stb.append(processInstance.getId()); stb.append(">, root process instance: <"); stb.append(processInstance.getRootProcessInstanceId()); stb.append(">, process definition: <"); stb.append(processInstance.getProcessDefinitionId()); if (processInstance.getCallerId() > 0) { stb.append(">, caller id: <"); stb.append(processInstance.getCallerId()); stb.append(">, caller type: <"); stb.append(processInstance.getCallerType()); } stb.append(">]"); log.debug(stb.toString()); } } SDataInstance createDataInstanceObject(SProcessInstance processInstance, SDataDefinition sDataDefinition, Serializable dataValue) throws SDataInstanceNotWellFormedException { try { return SDataInstanceBuilder.createNewInstance(sDataDefinition, processInstance.getId(), DataInstanceContainer.PROCESS_INSTANCE.name(), dataValue); } catch (final ClassCastException e) { throw new SDataInstanceNotWellFormedException( "Trying to set variable \"" + sDataDefinition.getName() + "\" with incompatible type: " + e.getMessage()); } } void warningWhenTransientDataWithNullValue() { log.warn("Creating a transient data instance with a null expression is not a good practice."); } private void createDataForProcess(final List sDataInstances) throws SDataInstanceException { if (!sDataInstances.isEmpty()) { for (final SDataInstance sDataInstance : sDataInstances) { dataInstanceService.createDataInstance(sDataInstance); } } } private boolean hasLocalOrInheritedData(final SProcessDefinition processDefinition, final SFlowElementContainerDefinition processContainer) { // processContainer is different of processDefinition.getProcessContainer() if it's a sub-process return !processContainer.getDataDefinitions().isEmpty() || !processDefinition.getProcessContainer().getDataDefinitions().isEmpty(); } SOperation getOperationToSetData(final String dataName, final List operations) { SOperation dataOperation = null; final Iterator iterator = operations.iterator(); boolean found = false; while (iterator.hasNext() && !found) { final SOperation operation = iterator.next(); if (SOperatorType.ASSIGNMENT.equals(operation.getType()) && SLeftOperand.TYPE_DATA.equals(operation.getLeftOperand().getType()) && dataName.equals(operation.getLeftOperand().getName())) { found = true; dataOperation = operation; } } return dataOperation; } private void createDataInstances(final List dataDefinitions, final long containerId, final DataInstanceContainer containerType, final SExpressionContext expressionContext, final String loopDataInputRef, final int index, final String dataInputRef, final long parentContainerId) throws SDataInstanceException, SExpressionException { for (final SDataDefinition dataDefinition : dataDefinitions) { Serializable dataValue = null; if (dataDefinition.getName().equals(dataInputRef)) { final SDataInstance dataInstance = dataInstanceService.getDataInstance(loopDataInputRef, parentContainerId, DataInstanceContainer.ACTIVITY_INSTANCE.name(), parentContainerResolver);// in a multi instance if (dataInstance != null) { try { final List list = (List) dataInstance.getValue(); if (!list.isEmpty()) { dataValue = list.get(index); } } catch (final ClassCastException e) { throw new SDataInstanceReadException("LoopDataInput ref named " + loopDataInputRef + " in " + containerId + " " + containerType + " is not a list or the value is not serializable."); } } else { throw new SDataInstanceReadException( "LoopDataInput ref named " + loopDataInputRef + " is not visible for " + containerId + " " + containerType); } } else { final SExpression defaultValueExpression = dataDefinition.getDefaultValueExpression(); if (defaultValueExpression != null) { dataValue = (Serializable) expressionResolverService .evaluate(dataDefinition.getDefaultValueExpression(), expressionContext); } else if (dataDefinition.isTransientData()) { warningWhenTransientDataWithNullValue(); } } final SDataInstance dataInstance; try { dataInstance = buildDataInstance(dataDefinition, containerId, containerType, dataValue); } catch (final SDataInstanceNotWellFormedException e) { throw new SDataInstanceReadException(e); } if (dataInstance.isTransientData()) { transientDataService.createDataInstance(dataInstance); } else { dataInstanceService.createDataInstance(dataInstance); } } } public void createDataInstances(final List dataDefinitions, final long containerId, final DataInstanceContainer containerType, final SExpressionContext expressionContext) throws SDataInstanceException, SExpressionException { createDataInstances(dataDefinitions, containerId, containerType, expressionContext, null, -1, null, -1); } private SDataInstance buildDataInstance(final SDataDefinition dataDefinition, final long dataContainerId, final DataInstanceContainer dataContainerType, final Serializable dataValue) throws SDataInstanceNotWellFormedException { return SDataInstanceBuilder.createNewInstance(dataDefinition, dataContainerId, dataContainerType.name(), dataValue); } public boolean createDataInstances(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SActivityDefinition activityDefinition = (SActivityDefinition) processContainer .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); if (activityDefinition != null) {// can be null if the activity was added in runtime return createDataInstances(activityDefinition, flowNodeInstance, new SExpressionContext(flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinition.getId())); } return false; } private boolean createDataInstances(final SActivityDefinition activityDefinition, final SFlowNodeInstance flowNodeInstance, final SExpressionContext expressionContext) throws SActivityStateExecutionException { final List sDataDefinitions = activityDefinition.getSDataDefinitions(); final SLoopCharacteristics loopCharacteristics = activityDefinition.getLoopCharacteristics(); if (loopCharacteristics instanceof SMultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics && (multiInstanceLoopCharacteristics.getDataInputItemRef() != null || multiInstanceLoopCharacteristics.getDataOutputItemRef() != null)) { try { createDataInstancesForMultiInstance(activityDefinition, flowNodeInstance, expressionContext); } catch (final SBonitaException e) { throw new SActivityStateExecutionException( "Failed to initialize multi instance variables of " + flowNodeInstance, ScopedException.ITERATION, e); } } else { try { createDataInstances(sDataDefinitions, flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE, expressionContext); } catch (final SBonitaException e) { throw new SActivityStateExecutionException("Failed to initialize variables of " + flowNodeInstance, ScopedException.DATA, e); } } if (!sDataDefinitions.isEmpty() && log.isDebugEnabled()) { final String message = "Initialized variables for flow node" + LogMessageBuilder.buildFlowNodeContextMessage(flowNodeInstance); log.debug(message); } return !sDataDefinitions.isEmpty(); } protected void createDataInstancesForMultiInstance(final SActivityDefinition activityDefinition, final SFlowNodeInstance flowNodeInstance, final SExpressionContext expressionContext) throws SDataInstanceException, SExpressionException { final SLoopCharacteristics loopCharacteristics = activityDefinition.getLoopCharacteristics(); final SMultiInstanceLoopCharacteristics miLoop = (SMultiInstanceLoopCharacteristics) loopCharacteristics; createBusinessDataInstancesForMultiInstance(activityDefinition, flowNodeInstance, miLoop); createDataInstances(activityDefinition.getSDataDefinitions(), flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE, expressionContext, miLoop.getLoopDataInputRef(), flowNodeInstance.getLoopCounter(), miLoop.getDataInputItemRef(), flowNodeInstance.getParentContainerId()); } private void createBusinessDataInstancesForMultiInstance(SActivityDefinition activityDefinition, SFlowNodeInstance flowNodeInstance, SMultiInstanceLoopCharacteristics miLoop) throws SDataInstanceException { final SBusinessDataDefinition outputBusinessData = activityDefinition .getBusinessDataDefinition(miLoop.getDataOutputItemRef()); final SRefBusinessDataInstanceBuilderFactory instanceFactory = BuilderFactory .get(SRefBusinessDataInstanceBuilderFactory.class); if (outputBusinessData != null) { final SRefBusinessDataInstance outputRefInstance = instanceFactory .createNewInstanceForFlowNode(outputBusinessData.getName(), flowNodeInstance.getId(), null, outputBusinessData.getClassName()) .done(); addRefBusinessData(outputRefInstance); } final SBusinessDataDefinition inputBusinessData = activityDefinition .getBusinessDataDefinition(miLoop.getDataInputItemRef()); if (inputBusinessData != null) { try { final SProcessMultiRefBusinessDataInstance loopDataRefInstance = (SProcessMultiRefBusinessDataInstance) refBusinessDataService .getRefBusinessDataInstance( miLoop.getLoopDataInputRef(), refBusinessDataRetriever.getProcessInstanceIdThatCanContainBusinessData( flowNodeInstance.getParentProcessInstanceId())); final List dataIds = loopDataRefInstance.getDataIds(); final SRefBusinessDataInstance inputRefInstance = instanceFactory .createNewInstanceForFlowNode(inputBusinessData.getName(), flowNodeInstance.getId(), dataIds.get(flowNodeInstance.getLoopCounter()), inputBusinessData.getClassName()) .done(); addRefBusinessData(inputRefInstance); } catch (final SRefBusinessDataInstanceNotFoundException | SBonitaReadException | SProcessInstanceReadException | SProcessInstanceNotFoundException | SFlowNodeReadException | SFlowNodeNotFoundException e) { throw new SDataInstanceException(e); } } } private void addRefBusinessData(final SRefBusinessDataInstance instance) throws SDataInstanceException { try { refBusinessDataService.addRefBusinessDataInstance(instance); } catch (final SRefBusinessDataInstanceCreationException e) { throw new SDataInstanceException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/bpm/process/impl/ProcessInstanceBuilder.java ================================================ /** * 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.bpm.process.impl; import java.util.Date; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.impl.internal.ProcessInstanceImpl; /** * @author Emmanuel Duchastenier * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class ProcessInstanceBuilder { protected final ProcessInstanceImpl processInstance; private ProcessInstanceBuilder() { this.processInstance = null; } private ProcessInstanceBuilder(final ProcessInstanceImpl processInstance) { super(); this.processInstance = processInstance; } public ProcessInstance done() { return processInstance; } public ProcessInstanceBuilder createNewInstance(final String name) { final ProcessInstanceImpl processInstance = new ProcessInstanceImpl(name); return new ProcessInstanceBuilder(processInstance); } public ProcessInstanceBuilder setState(final String state) { processInstance.setState(state); return this; } public ProcessInstanceBuilder setStartDate(final long startDate) { processInstance.setStartDate(new Date(startDate)); return this; } public ProcessInstanceBuilder setStartedBy(final long startedBy) { processInstance.setStartedBy(startedBy); return this; } public ProcessInstanceBuilder setStartedBySubstitute(final long startedBySubstitute) { processInstance.setStartedBySubstitute(startedBySubstitute); return this; } public ProcessInstanceBuilder setEndDate(final long endDate) { processInstance.setEndDate(new Date(endDate)); return this; } public ProcessInstanceBuilder setLastUpdate(final long lastUpdate) { processInstance.setLastUpdate(new Date(lastUpdate)); return this; } public ProcessInstanceBuilder setProcessDefinitionId(final long processDefinitionId) { processInstance.setProcessDefinitionId(processDefinitionId); return this; } public ProcessInstanceBuilder setDescription(final String description) { processInstance.setDescription(description); return this; } public ProcessInstanceBuilder setId(final long id) { processInstance.setId(id); return this; } public ProcessInstanceBuilder setRootProcessInstanceId(final long rootProcessInstanceId) { processInstance.setRootProcessInstanceId(rootProcessInstanceId); return this; } public ProcessInstanceBuilder setCallerId(final long callerId) { processInstance.setCallerId(callerId); return this; } public ProcessInstanceBuilder setStringIndex1(final String stringIndex1) { processInstance.setStringIndex1(stringIndex1); return this; } public ProcessInstanceBuilder setStringIndex2(final String stringIndex2) { processInstance.setStringIndex2(stringIndex2); return this; } public ProcessInstanceBuilder setStringIndex3(final String stringIndex3) { processInstance.setStringIndex3(stringIndex3); return this; } public ProcessInstanceBuilder setStringIndex4(final String stringIndex4) { processInstance.setStringIndex4(stringIndex4); return this; } public ProcessInstanceBuilder setStringIndex5(final String stringIndex5) { processInstance.setStringIndex5(stringIndex5); return this; } public void setStringIndexLabel(final int index, final String stringIndexLabel) { processInstance.setStringIndexLabel(index, stringIndexLabel); } public static ProcessInstanceBuilder getInstance() { return new ProcessInstanceBuilder(null); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/ApplicationMenuToNodeConverter.java ================================================ /** * 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.business.application.converter; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.business.application.xml.ApplicationMenuNode; import org.bonitasoft.engine.business.application.xml.ApplicationNode; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Emmanuel Duchastenier */ public class ApplicationMenuToNodeConverter { private final ApplicationService applicationService; public ApplicationMenuToNodeConverter(final ApplicationService applicationService) { this.applicationService = applicationService; } /** * RECURSIVELY convert menu (and sub-menus) to xml node. * * @param menu the menu to convert * @return the converted menu. * @throws SObjectNotFoundException if the referenced menu does not exist. * @throws SBonitaReadException if the referenced menu cannot be retrieved. */ protected ApplicationMenuNode toMenu(final SApplicationMenu menu) throws SBonitaReadException, SObjectNotFoundException { if (menu == null) { throw new IllegalArgumentException("Application menu to convert cannot be null"); } final ApplicationMenuNode menuNode = new ApplicationMenuNode(); // applicationPage attribute in the menu is the token in the referenced application page: final Long applicationPageId = menu.getApplicationPageId(); if (applicationPageId != null) { menuNode.setApplicationPage(applicationService.getApplicationPage(applicationPageId).getToken()); } menuNode.setDisplayName(menu.getDisplayName()); return menuNode; } /** * @param applicationId application ID. * @param parentMenuId Id of the parent menu, use null for explicit no parent. * @param startIndex pagination start index. * @param maxResults pagination max results to retrieve. * @return the newly built {@link QueryOptions} */ protected QueryOptions buildApplicationMenusQueryOptions(final long applicationId, final Long parentMenuId, final int startIndex, final int maxResults) { final List orderByOptions = Collections .singletonList(new OrderByOption(SApplicationMenu.class, SApplicationMenu.INDEX, OrderByType.ASC)); final List filters = Arrays.asList( new FilterOption(SApplicationMenu.class, SApplicationMenu.APPLICAITON_ID, applicationId), new FilterOption(SApplicationMenu.class, SApplicationMenu.PARENT_ID, parentMenuId)); return new QueryOptions(startIndex, maxResults, orderByOptions, filters, null); } /** * RECURSIVELY add menu elements (and sub-menus) to xml node, from menu identified by parentMenuId. * * @param applicationId ID of the application. * @param parentMenuId Id of the parent menu, use null for explicit no parent. * @param applicationNode * @param menuNode the menu node to add new menu elements to. Pass null if new menu node must be added to root * application node. * @throws SBonitaReadException * @throws SObjectNotFoundException */ public void addMenusToApplicationNode(final long applicationId, final Long parentMenuId, final ApplicationNode applicationNode, final ApplicationMenuNode menuNode) throws SBonitaReadException, SObjectNotFoundException { int startIndex = 0; final int maxResults = 50; List menus; do { menus = applicationService.searchApplicationMenus( buildApplicationMenusQueryOptions(applicationId, parentMenuId, startIndex, maxResults)); for (final SApplicationMenu menu : menus) { // Add converted current menu... final ApplicationMenuNode menuNode2 = toMenu(menu); if (menuNode == null) { applicationNode.addApplicationMenu(menuNode2); } else { menuNode.addApplicationMenu(menuNode2); } // ... and recursively add sub-menu: addMenusToApplicationNode(applicationId, menu.getId(), applicationNode, menuNode2); } startIndex += maxResults; } while (menus.size() == maxResults); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/ApplicationPageToNodeConverter.java ================================================ /** * 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.business.application.converter; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.bonitasoft.engine.business.application.xml.ApplicationPageNode; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Emmanuel Duchastenier */ public class ApplicationPageToNodeConverter { private final PageService pageService; public ApplicationPageToNodeConverter(final PageService pageService) { this.pageService = pageService; } /** * @param page the application page to convert to xml node. * @return the converted page. * @throws SObjectNotFoundException if the referenced page does not exist. * @throws SBonitaReadException if the referenced page cannot be retrieved. */ public ApplicationPageNode toPage(final SApplicationPage page) throws SBonitaReadException, SObjectNotFoundException { if (page == null) { throw new IllegalArgumentException("Application page to convert cannot be null"); } final ApplicationPageNode pageNode = new ApplicationPageNode(); pageNode.setToken(page.getToken()); pageNode.setCustomPage(pageService.getPage(page.getPageId()).getName()); return pageNode; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/ApplicationToNodeConverter.java ================================================ /** * 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.business.application.converter; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.bonitasoft.engine.business.application.xml.AbstractApplicationNode; import org.bonitasoft.engine.business.application.xml.ApplicationLinkNode; import org.bonitasoft.engine.business.application.xml.ApplicationNode; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.exception.ExportException; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.profile.ProfileService; import org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException; import org.bonitasoft.engine.profile.model.SProfile; /** * @author Elias Ricken de Medeiros */ public class ApplicationToNodeConverter { private final ProfileService profileService; private final ApplicationService applicationService; private final ApplicationPageToNodeConverter applicationPageToNodeConverter; private final ApplicationMenuToNodeConverter applicationMenuToNodeConverter; private final PageService pageService; public ApplicationToNodeConverter(final ProfileService profileService, final ApplicationService applicationService, final ApplicationPageToNodeConverter applicationPageToNodeConverter, final ApplicationMenuToNodeConverter applicationMenuToNodeConverter, final PageService pageService) { this.profileService = profileService; this.applicationService = applicationService; this.applicationPageToNodeConverter = applicationPageToNodeConverter; this.applicationMenuToNodeConverter = applicationMenuToNodeConverter; this.pageService = pageService; } public AbstractApplicationNode toNode(final SApplication application) throws ExportException { try { final AbstractApplicationNode applicationNode; if (application.isLink()) { applicationNode = new ApplicationLinkNode(); } else { ApplicationNode legacyApplicationNode = new ApplicationNode(); setLayout(application, legacyApplicationNode); setTheme(application, legacyApplicationNode); setHomePage(application, legacyApplicationNode); setPages(application.getId(), legacyApplicationNode); applicationMenuToNodeConverter.addMenusToApplicationNode(application.getId(), null, legacyApplicationNode, null); applicationNode = legacyApplicationNode; } applicationNode.setToken(application.getToken()); applicationNode.setDisplayName(application.getDisplayName()); applicationNode.setVersion(application.getVersion()); applicationNode.setDescription(application.getDescription()); applicationNode.setState(application.getState()); applicationNode.setIconPath(application.getIconPath()); setProfile(application, applicationNode); return applicationNode; } catch (SBonitaException e) { throw new ExportException(e); } } private void setTheme(final SApplication application, final ApplicationNode applicationNode) throws SBonitaReadException, SObjectNotFoundException { if (application.getThemeId() != null) { SPage page = pageService.getPage(application.getThemeId()); applicationNode.setTheme(page.getName()); } } private void setLayout(final SApplication application, final ApplicationNode applicationNode) throws SBonitaReadException, SObjectNotFoundException { if (application.getLayoutId() != null) { SPage page = pageService.getPage(application.getLayoutId()); applicationNode.setLayout(page.getName()); } } private void setPages(final long applicationId, final ApplicationNode applicationNode) throws SBonitaReadException, SObjectNotFoundException { int startIndex = 0; final int maxResults = 50; List pages; do { pages = applicationService .searchApplicationPages(buildApplicationPagesQueryOptions(applicationId, startIndex, maxResults)); for (final SApplicationPage page : pages) { applicationNode.addApplicationPage(applicationPageToNodeConverter.toPage(page)); } startIndex += maxResults; } while (pages.size() == maxResults); } public QueryOptions buildApplicationPagesQueryOptions(final long applicationId, final int startIndex, final int pageSize) { final List orderByOptions = Collections .singletonList(new OrderByOption(SApplicationPage.class, SApplicationPage.ID, OrderByType.ASC)); final List filters = Collections.singletonList( new FilterOption(SApplicationPage.class, SApplicationPage.APPLICATION_ID, applicationId)); return new QueryOptions(startIndex, pageSize, orderByOptions, filters, null); } private void setHomePage(final SApplication application, final ApplicationNode applicationNode) throws SBonitaReadException, SObjectNotFoundException { if (application.getHomePageId() != null) { final SApplicationPage homePage = applicationService.getApplicationPage(application.getHomePageId()); applicationNode.setHomePage(homePage.getToken()); } } private void setProfile(final SApplication application, final AbstractApplicationNode applicationNode) throws SProfileNotFoundException { if (application.getProfileId() != null) { final SProfile profile = profileService.getProfile(application.getProfileId()); applicationNode.setProfile(profile.getName()); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/ApplicationsToNodeContainerConverter.java ================================================ /** * 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.business.application.converter; import java.util.List; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer; import org.bonitasoft.engine.exception.ExportException; /** * @author Elias Ricken de Medeiros */ public class ApplicationsToNodeContainerConverter { private final ApplicationToNodeConverter applicationToNodeConverter; public ApplicationsToNodeContainerConverter(final ApplicationToNodeConverter applicationToNodeConverter) { this.applicationToNodeConverter = applicationToNodeConverter; } public ApplicationNodeContainer toNode(final List applications) throws ExportException { final ApplicationNodeContainer container = new ApplicationNodeContainer(); for (final SApplication application : applications) { container.addApplication(applicationToNodeConverter.toNode(application)); } return container; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/NodeToApplicationConverter.java ================================================ /** * 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.business.application.converter; import org.bonitasoft.engine.api.ImportError; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.InternalProfiles; import org.bonitasoft.engine.business.application.importer.ImportResult; import org.bonitasoft.engine.business.application.importer.validator.ApplicationImportValidator; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; import org.bonitasoft.engine.business.application.xml.AbstractApplicationNode; import org.bonitasoft.engine.business.application.xml.ApplicationLinkNode; import org.bonitasoft.engine.business.application.xml.ApplicationNode; import org.bonitasoft.engine.exception.ImportException; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.profile.ProfileService; import org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException; import org.bonitasoft.engine.profile.model.SProfile; import org.springframework.stereotype.Component; /** * @author Elias Ricken de Medeiros */ @Component public class NodeToApplicationConverter { private final ProfileService profileService; private final PageService pageService; private final ApplicationImportValidator validator; public NodeToApplicationConverter(final ProfileService profileService, final PageService pageService, final ApplicationImportValidator validator) { this.profileService = profileService; this.pageService = pageService; this.validator = validator; } public ImportResult toSApplication(final AbstractApplicationNode applicationNode, byte[] iconContent, String iconMimeType, final long createdBy, final boolean editable) throws SBonitaReadException, ImportException { String token = applicationNode.getToken(); validator.validate(token); final ImportStatus importStatus = new ImportStatus(token); final long currentDate = System.currentTimeMillis(); SApplicationWithIcon application = new SApplicationWithIcon(); application.setToken(token); application.setDisplayName(applicationNode.getDisplayName()); application.setVersion(applicationNode.getVersion()); application.setCreationDate(currentDate); application.setLastUpdateDate(currentDate); application.setCreatedBy(createdBy); application.setUpdatedBy(createdBy); application.setIconPath(applicationNode.getIconPath()); application.setDescription(applicationNode.getDescription()); application.setState(applicationNode.getState()); application.setEditable(editable); application.setLink(applicationNode instanceof ApplicationLinkNode); if (applicationNode instanceof ApplicationNode legacy) { application.setLayoutId(getLayoutId(getLayoutName(legacy), token, importStatus)); application.setThemeId(getThemeId(getThemeName(legacy), token, importStatus)); } application.setIconContent(iconContent); application.setIconMimeType(iconMimeType); if (applicationNode.getProfile() != null) { setProfile(applicationNode.getProfile(), application, importStatus); } return new ImportResult(application, importStatus); } private Long getLayoutId(final String layoutName, final String applicationToken, final ImportStatus importStatus) throws SBonitaReadException, ImportException { SPage layout = pageService.getPageByName(layoutName); if (layout == null) { return handleMissingLayout(layoutName, applicationToken, importStatus); } return layout.getId(); } private Long getThemeId(final String themeName, final String applicationToken, final ImportStatus importStatus) throws SBonitaReadException, ImportException { SPage theme = pageService.getPageByName(themeName); if (theme == null) { return handleMissingTheme(themeName, applicationToken, importStatus); } return theme.getId(); } protected Long handleMissingLayout(final String layoutName, final String applicationToken, final ImportStatus importStatus) throws ImportException, SBonitaReadException { throw new ImportException( String.format("Unable to import application with token '%s' because the layout '%s' was not found.", applicationToken, layoutName)); } protected Long handleMissingTheme(final String themeName, final String applicationToken, final ImportStatus importStatus) throws ImportException, SBonitaReadException { throw new ImportException( String.format("Unable to import application with token '%s' because the theme '%s' was not found.", applicationToken, themeName)); } private String getLayoutName(final ApplicationNode applicationNode) { return applicationNode.getLayout() != null ? applicationNode.getLayout() : ApplicationService.DEFAULT_LAYOUT_NAME; } private String getThemeName(final ApplicationNode applicationNode) { return applicationNode.getTheme() != null ? applicationNode.getTheme() : ApplicationService.DEFAULT_THEME_NAME; } private void setProfile(String profileName, SApplicationWithIcon application, ImportStatus importStatus) { InternalProfiles internalProfileByApplicationNodeProfile = InternalProfiles .getInternalProfileByProfileName(profileName); if (internalProfileByApplicationNodeProfile == null) { try { final SProfile profile = profileService.getProfileByName(profileName); application.setProfileId(profile.getId()); } catch (final SProfileNotFoundException | SBonitaReadException e) { importStatus.addError(new ImportError(profileName, ImportError.Type.PROFILE)); } } else { application.setInternalProfile(internalProfileByApplicationNodeProfile.getProfileName()); application.setProfileId(null); } } protected PageService getPageService() { return pageService; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/NodeToApplicationMenuConverter.java ================================================ /** * 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.business.application.converter; import org.bonitasoft.engine.api.ImportError; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.importer.ApplicationMenuImportResult; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; import org.bonitasoft.engine.business.application.xml.ApplicationMenuNode; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.springframework.stereotype.Component; /** * @author Emmanuel Duchastenier */ @Component public class NodeToApplicationMenuConverter { private final ApplicationService applicationService; public NodeToApplicationMenuConverter(final ApplicationService applicationService) { this.applicationService = applicationService; } /** * Convert an {@link ApplicationMenuNode} to * {@link SApplicationMenu} * * @param applicationMenuNode the XML node to convert * @param application the application where the menu will be attached to * @param parentMenu the parent menu. Null if no parent * @return the application where the menu will be attached to * @throws SBonitaReadException */ public ApplicationMenuImportResult toSApplicationMenu(ApplicationMenuNode applicationMenuNode, SApplicationWithIcon application, SApplicationMenu parentMenu) throws SBonitaReadException { Long appPageId = null; ImportError error = null; if (applicationMenuNode.getApplicationPage() != null) { try { SApplicationPage applicationPage = applicationService.getApplicationPage(application.getToken(), applicationMenuNode.getApplicationPage()); appPageId = applicationPage.getId(); } catch (SObjectNotFoundException e) { error = new ImportError(applicationMenuNode.getApplicationPage(), ImportError.Type.APPLICATION_PAGE); } } int index = getIndex(parentMenu); SApplicationMenu applicationMenu = buildApplicationMenu(applicationMenuNode, application, parentMenu, appPageId, index); return new ApplicationMenuImportResult(error, applicationMenu); } private int getIndex(final SApplicationMenu parentMenu) throws SBonitaReadException { int index; if (parentMenu == null) { index = applicationService.getNextAvailableIndex(null); } else { index = applicationService.getNextAvailableIndex(parentMenu.getId()); } return index; } private SApplicationMenu buildApplicationMenu(final ApplicationMenuNode applicationMenuNode, final SApplicationWithIcon application, final SApplicationMenu parentMenu, final Long appPageId, final int index) { SApplicationMenu.SApplicationMenuBuilder builder = SApplicationMenu.builder() .displayName(applicationMenuNode.getDisplayName()).applicationId(application.getId()) .applicationPageId(appPageId).index(index); if (parentMenu != null) { builder.parentId(parentMenu.getId()); } return builder.build(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/NodeToApplicationPageConverter.java ================================================ /** * 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.business.application.converter; import org.bonitasoft.engine.api.ImportError; import org.bonitasoft.engine.business.application.importer.ApplicationPageImportResult; import org.bonitasoft.engine.business.application.importer.validator.ApplicationImportValidator; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; import org.bonitasoft.engine.business.application.xml.ApplicationPageNode; import org.bonitasoft.engine.exception.ImportException; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.springframework.stereotype.Component; /** * @author Emmanuel Duchastenier */ @Component public class NodeToApplicationPageConverter { private final PageService pageService; private final ApplicationImportValidator importValidator; public NodeToApplicationPageConverter(final PageService pageService, final ApplicationImportValidator importValidator) { this.pageService = pageService; this.importValidator = importValidator; } /** * @param applicationPageNode the XML node to convert to {@link SApplicationPage} * @param application the {@link SApplicationWithIcon} where the {@code SApplicationPage} will be attached * @return an ApplicationPageImportResult containing the converted {@code SApplicationPage} and an error (if any) */ public ApplicationPageImportResult toSApplicationPage(ApplicationPageNode applicationPageNode, SApplicationWithIcon application) throws SBonitaReadException, ImportException { String token = applicationPageNode.getToken(); importValidator.validate(token); long pageId = 0; ImportError importError = null; SPage page = pageService.getPageByName(applicationPageNode.getCustomPage()); if (page != null) { pageId = page.getId(); } else { importError = new ImportError(applicationPageNode.getCustomPage(), ImportError.Type.PAGE); } return new ApplicationPageImportResult( SApplicationPage.builder().applicationId(application.getId()).pageId(pageId).token(token).build(), importError); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/exporter/ApplicationContainerExporter.java ================================================ /** * 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.business.application.exporter; import org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer; import org.bonitasoft.engine.exception.ExportException; /** * @author Elias Ricken de Medeiros */ public class ApplicationContainerExporter { public byte[] export(final ApplicationNodeContainer applicationNodeContainer) throws ExportException { try { return new ApplicationNodeContainerConverter().marshallToXML(applicationNodeContainer); } catch (final Exception e) { throw new ExportException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/exporter/ApplicationExporter.java ================================================ /** * 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.business.application.exporter; import java.util.List; import org.bonitasoft.engine.business.application.converter.ApplicationsToNodeContainerConverter; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer; import org.bonitasoft.engine.exception.ExportException; /** * @author Elias Ricken de Medeiros */ public class ApplicationExporter { private final ApplicationsToNodeContainerConverter converter; private final ApplicationContainerExporter exporter; public ApplicationExporter(ApplicationsToNodeContainerConverter converter, ApplicationContainerExporter exporter) { this.converter = converter; this.exporter = exporter; } public byte[] export(List applications) throws ExportException { ApplicationNodeContainer container = converter.toNode(applications); return exporter.export(container); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ApplicationImportStrategy.java ================================================ /** * 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.business.application.importer; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; /** * @author Elias Ricken de Medeiros */ public interface ApplicationImportStrategy { enum ImportStrategy { FAIL, SKIP, REPLACE } /** * Determine what to do when the application already exists. */ ImportStrategy whenApplicationExists(SApplication existing, SApplicationWithIcon toBeImported); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ApplicationImporter.java ================================================ /** * 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.business.application.importer; import java.util.ArrayList; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.ImportError; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.converter.NodeToApplicationConverter; import org.bonitasoft.engine.business.application.exporter.ApplicationNodeContainerConverter; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; import org.bonitasoft.engine.business.application.model.builder.SApplicationUpdateBuilder; import org.bonitasoft.engine.business.application.xml.AbstractApplicationNode; import org.bonitasoft.engine.business.application.xml.ApplicationNode; import org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.ImportException; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.springframework.stereotype.Component; /** * @author Elias Ricken de Medeiros */ @Component @Slf4j public class ApplicationImporter { private final ApplicationService applicationService; private final NodeToApplicationConverter nodeToApplicationConverter; private final ApplicationPageImporter applicationPageImporter; private final ApplicationMenuImporter applicationMenuImporter; public ApplicationImporter(ApplicationService applicationService, NodeToApplicationConverter nodeToApplicationConverter, ApplicationPageImporter applicationPageImporter, ApplicationMenuImporter applicationMenuImporter) { this.applicationService = applicationService; this.nodeToApplicationConverter = nodeToApplicationConverter; this.applicationPageImporter = applicationPageImporter; this.applicationMenuImporter = applicationMenuImporter; } private void updateHomePage(final SApplicationWithIcon application, final ApplicationNode applicationNode, final long createdBy, final ImportResult importResult) throws SBonitaException { if (applicationNode.getHomePage() != null) { try { SApplicationPage homePage = applicationService.getApplicationPage(applicationNode.getToken(), applicationNode.getHomePage()); SApplicationUpdateBuilder updateBuilder = new SApplicationUpdateBuilder(createdBy); updateBuilder.updateHomePageId(homePage.getId()); applicationService.updateApplication(application, updateBuilder.done()); } catch (SObjectNotFoundException e) { importResult.getImportStatus() .addErrorsIfNotExists(List.of( new ImportError(applicationNode.getHomePage(), ImportError.Type.APPLICATION_PAGE))); } } } public ImportStatus importApplication(AbstractApplicationNode applicationNode, boolean editable, long createdBy, byte[] iconContent, String iconMimeType, boolean addIfMissing, ApplicationImportStrategy strategy) throws ImportException, AlreadyExistsException { try { ImportResult importResult = nodeToApplicationConverter.toSApplication(applicationNode, iconContent, iconMimeType, createdBy, editable); SApplicationWithIcon applicationToBeImported = importResult.getApplication(); ImportStatus importStatus = importResult.getImportStatus(); importStatus.setStatus(ImportStatus.Status.ADDED); // import status will change depending on the chosen strategy, or will throw exception applyStrategyWhenApplicationExists(strategy, applicationToBeImported, importStatus); if (!addIfMissing) { importStatus.setStatus(ImportStatus.Status.SKIPPED); } if (importStatus.getStatus() != ImportStatus.Status.SKIPPED) { applicationService.createApplication(applicationToBeImported); // import more elements for applications built with legacy UID if (applicationNode instanceof ApplicationNode legacy) { importStatus.addErrorsIfNotExists( applicationPageImporter .importApplicationPages(legacy.getApplicationPages(), applicationToBeImported)); importStatus.addErrorsIfNotExists( applicationMenuImporter .importApplicationMenus(legacy.getApplicationMenus(), applicationToBeImported)); updateHomePage(applicationToBeImported, legacy, createdBy, importResult); } } return importStatus; } catch (SBonitaException e) { throw new ImportException(e); } } private void applyStrategyWhenApplicationExists(ApplicationImportStrategy strategy, SApplicationWithIcon applicationToBeImported, ImportStatus importStatus) throws SBonitaReadException, AlreadyExistsException, SObjectModificationException { SApplication conflictingApplication = applicationService .getApplicationByToken(applicationToBeImported.getToken()); if (conflictingApplication != null) { switch (strategy.whenApplicationExists(conflictingApplication, applicationToBeImported)) { case FAIL: throw new AlreadyExistsException( "An application with token '" + conflictingApplication.getToken() + "' already exists", conflictingApplication.getToken()); case REPLACE: importStatus.setStatus(ImportStatus.Status.REPLACED); applicationService.forceDeleteApplication(conflictingApplication); break; case SKIP: importStatus.setStatus(ImportStatus.Status.SKIPPED); break; } } } public List importApplications(final byte[] xmlContent, byte[] iconContent, String iconMimeType, long createdBy, ApplicationImportStrategy strategy) throws ImportException, AlreadyExistsException { ApplicationNodeContainer applicationNodeContainer = getApplicationNodeContainer(xmlContent); List importStatus = new ArrayList<>(); for (AbstractApplicationNode applicationNode : applicationNodeContainer.getAllApplications()) { importStatus.add( importApplication(applicationNode, true, createdBy, iconContent, iconMimeType, true, strategy)); } return importStatus; } public ApplicationNodeContainer getApplicationNodeContainer(byte[] xmlContent) throws ImportException { try { return new ApplicationNodeContainerConverter().unmarshallFromXML(xmlContent); } catch (final Exception e) { throw new ImportException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ApplicationMenuImportResult.java ================================================ /** * 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.business.application.importer; import org.bonitasoft.engine.api.ImportError; import org.bonitasoft.engine.business.application.model.SApplicationMenu; /** * @author Elias Ricken de Medeiros */ public class ApplicationMenuImportResult { private final ImportError error; private final SApplicationMenu applicationMenu; public ApplicationMenuImportResult(ImportError error, SApplicationMenu applicationMenu) { this.error = error; this.applicationMenu = applicationMenu; } public ImportError getError() { return error; } public SApplicationMenu getApplicationMenu() { return applicationMenu; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ApplicationMenuImporter.java ================================================ /** * 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.business.application.importer; import java.util.*; import org.bonitasoft.engine.api.ImportError; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.converter.NodeToApplicationMenuConverter; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; import org.bonitasoft.engine.business.application.xml.ApplicationMenuNode; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.exception.ImportException; import org.springframework.stereotype.Component; /** * @author Elias Ricken de Medeiros */ @Component public class ApplicationMenuImporter { private ApplicationService applicationService; private NodeToApplicationMenuConverter converter; public ApplicationMenuImporter(ApplicationService applicationService, NodeToApplicationMenuConverter converter) { this.applicationService = applicationService; this.converter = converter; } public List importApplicationMenu(ApplicationMenuNode applicationMenuNode, SApplicationWithIcon application, SApplicationMenu parentMenu) throws ImportException { List errors = new ArrayList(); try { ApplicationMenuImportResult importResult = converter.toSApplicationMenu(applicationMenuNode, application, parentMenu); if (importResult.getError() == null) { SApplicationMenu applicationMenu = applicationService .createApplicationMenu(importResult.getApplicationMenu()); for (ApplicationMenuNode subMenuNode : applicationMenuNode.getApplicationMenus()) { errors.addAll(importApplicationMenu(subMenuNode, application, applicationMenu)); } } else { errors.add(importResult.getError()); } return errors; } catch (SBonitaException e) { throw new ImportException(e); } } public List importApplicationMenus(List applicationMenus, SApplicationWithIcon application) throws ImportException { List importErrors = new ArrayList<>(); for (ApplicationMenuNode applicationMenuNode : applicationMenus) { importErrors.addAll(importApplicationMenu(applicationMenuNode, application, null)); } return importErrors; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ApplicationPageImportResult.java ================================================ /** * 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.business.application.importer; import org.bonitasoft.engine.api.ImportError; import org.bonitasoft.engine.business.application.model.SApplicationPage; /** * @author Elias Ricken de Medeiros */ public class ApplicationPageImportResult { private final SApplicationPage applicationPage; private final ImportError error; public ApplicationPageImportResult(SApplicationPage applicationPage, ImportError error) { this.applicationPage = applicationPage; this.error = error; } public SApplicationPage getApplicationPage() { return applicationPage; } public ImportError getError() { return error; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ApplicationPageImporter.java ================================================ /** * 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.business.application.importer; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.api.ImportError; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.converter.NodeToApplicationPageConverter; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; import org.bonitasoft.engine.business.application.xml.ApplicationPageNode; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.exception.ImportException; import org.springframework.stereotype.Component; /** * @author Elias Ricken de Medeiros */ @Component public class ApplicationPageImporter { private final ApplicationService applicationService; private final NodeToApplicationPageConverter nodeToApplicationPageConverter; public ApplicationPageImporter(ApplicationService applicationService, NodeToApplicationPageConverter nodeToApplicationPageConverter) { this.applicationService = applicationService; this.nodeToApplicationPageConverter = nodeToApplicationPageConverter; } public ImportError importApplicationPage(ApplicationPageNode applicationPageNode, SApplicationWithIcon application) throws ImportException { try { ApplicationPageImportResult importResult = nodeToApplicationPageConverter .toSApplicationPage(applicationPageNode, application); if (importResult.getError() == null) { applicationService.createApplicationPage(importResult.getApplicationPage()); } return importResult.getError(); } catch (SBonitaException e) { throw new ImportException(e); } } public List importApplicationPages(final List applicationPageNodes, final SApplicationWithIcon application) throws ImportException { List importErrors = new ArrayList<>(); for (ApplicationPageNode applicationPageNode : applicationPageNodes) { ImportError importError = importApplicationPage(applicationPageNode, application); if (importError != null) { importErrors.add(importError); } } return importErrors; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ApplicationZipContent.java ================================================ /** * Copyright (C) 2023 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.business.application.importer; import static org.bonitasoft.engine.commons.io.IOUtil.unzip; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import lombok.Data; import org.bonitasoft.engine.exception.ImportException; @Data class ApplicationZipContent { private final byte[] iconRaw; private final byte[] xmlRaw; private final String pngName; static ApplicationZipContent getApplicationZipContent(String resourceName, InputStream resourceAsStream) throws IOException, ImportException { final byte[] content = org.apache.commons.io.IOUtils.toByteArray(resourceAsStream); Map zipContent = unzip(content); List pngFileNamesList = zipContent.keySet().stream().filter(l -> l.endsWith(".png")) .collect(Collectors.toList()); List xmlFileNamesList = zipContent.keySet().stream().filter(l -> l.endsWith(".xml")) .collect(Collectors.toList()); if (xmlFileNamesList.size() > 1) { throw new ImportException("The application zip " + resourceName + " contains more than one xml descriptor, and therefore has an invalid format"); } else if (pngFileNamesList.size() > 1) { throw new ImportException("The application zip " + resourceName + " contains more than one icon file, and therefore has an invalid format"); } String pngName = pngFileNamesList.get(0); String xmlName = xmlFileNamesList.get(0); return new ApplicationZipContent(zipContent.get(pngName), zipContent.get(xmlName), pngName); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/DefaultLivingApplicationImporter.java ================================================ /** * Copyright (C) 2023 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.business.application.importer; import java.io.IOException; import java.util.List; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.commons.ExceptionUtils; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.page.PageService; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.stereotype.Component; /** * Service used to import default provided living applications at startup, such as: *
    *
  • Bonita Admin Application
  • *
  • Bonita User Application
  • *
*/ @Component @Slf4j public class DefaultLivingApplicationImporter extends LivingApplicationImporter { private static final String EDITABLE_REMOVABLE_PAGES_PATH = "org/bonitasoft/web/page"; private static final String PROVIDED_REMOVABLE_APPLICATIONS_PATH = "org/bonitasoft/web/application"; /** Boolean used to import provided removable pages if there are missing (mostly when it is a first install) */ @Setter private boolean addRemovablePagesIfMissing; /** * Boolean used to import provided editable applications if there are missing (mostly when it is a first install) */ @Setter private boolean addEditableApplicationsIfMissing; public DefaultLivingApplicationImporter(final PageService pageService, final ApplicationImporter applicationImporter) { super(pageService, applicationImporter); } /** * Main function to import default living applications with their associated pages. */ public void execute() { // Step 1: import default pages log.info("Importing Bonita default pages"); importDefaultPages(); log.info("Import of Bonita default pages completed"); // Step 2: import default living apps log.info("Importing Bonita default applications"); importDefaultApplications(); log.info("Import of Bonita default applications completed"); } private void importDefaultPages() { try { if (addRemovablePagesIfMissing) { log.info("Detected a first run (a tenant creation or an installation from scratch), " + "importing provided removable pages"); } else { log.info("Updating provided removable pages if they exist and are outdated"); } // import the provided pages as removable and editable List importStatuses = importProvidedPagesFromClasspath( ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "/" + EDITABLE_REMOVABLE_PAGES_PATH + "/*.zip", true, true, addRemovablePagesIfMissing); List createdOrReplaced = getNonSkippedImportedResources(importStatuses); if (createdOrReplaced.isEmpty()) { log.info("No default pages updated"); } else { log.info("Default pages updated or created: {}", createdOrReplaced); } } catch (BonitaException | IOException e) { log.error(ExceptionUtils.printLightWeightStacktrace(e)); log.debug("Stacktrace of the import issue is:", e); } } private void importDefaultApplications() { try { if (addEditableApplicationsIfMissing) { log.info("Detected a first run since a Bonita update, a Bonita upgrade, " + "a tenant creation or an installation from scratch. Importing default applications"); } else { log.info("Updating provided default applications if they exist and are outdated"); } // import the provided applications as editable List importStatuses = importProvidedApplicationsFromClasspath( ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "/" + PROVIDED_REMOVABLE_APPLICATIONS_PATH + "/*.zip", true, addEditableApplicationsIfMissing); List createdOrReplaced = getNonSkippedImportedResources(importStatuses); if (createdOrReplaced.isEmpty()) { log.info("No default applications updated"); } else { log.info("Default applications updated or created: {}", createdOrReplaced); } } catch (Exception e) { log.error("Cannot load provided default applications at startup. Root cause: {}", ExceptionUtils.printRootCauseOnly(e)); log.debug("Full stack:", e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/FailOnDuplicateApplicationImportStrategy.java ================================================ /** * 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.business.application.importer; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; /** * @author Elias Ricken de Medeiros */ public class FailOnDuplicateApplicationImportStrategy implements ApplicationImportStrategy { @Override public ImportStrategy whenApplicationExists(SApplication existing, SApplicationWithIcon toBeImported) { return ImportStrategy.FAIL; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ImportResult.java ================================================ /** * 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.business.application.importer; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; /** * @author Elias Ricken de Medeiros */ public class ImportResult { private final SApplicationWithIcon application; private final ImportStatus importStatus; public ImportResult(SApplicationWithIcon application, ImportStatus importStatus) { this.application = application; this.importStatus = importStatus; } public SApplicationWithIcon getApplication() { return application; } public ImportStatus getImportStatus() { return importStatus; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/LivingApplicationImporter.java ================================================ /** * Copyright (C) 2023 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.business.application.importer; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.business.application.xml.AbstractApplicationNode; import org.bonitasoft.engine.business.application.xml.ApplicationNodeContainer; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.ImportException; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.session.SessionService; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.util.DigestUtils; /** * Abstract class to regroup common code used by subclasses to import living applications. */ @RequiredArgsConstructor @Slf4j public abstract class LivingApplicationImporter { protected final PageService pageService; protected final ApplicationImporter applicationImporter; private final ResourcePatternResolver cpResourceResolver = new PathMatchingResourcePatternResolver( LivingApplicationImporter.class.getClassLoader()); protected List importProvidedPagesFromClasspath(final String locationPattern, final boolean removable, final boolean editable, final boolean addIfMissing) throws IOException, BonitaException { List importStatuses = new ArrayList<>(); Resource[] resources = cpResourceResolver.getResources(locationPattern); for (Resource resource : resources) { if (resource.exists() && resource.isReadable() && resource.contentLength() > 0) { String resourceName = resource.getFilename(); try (InputStream resourceAsStream = resource.getInputStream()) { log.debug("Found provided page '{}' in classpath", resourceName); final byte[] content = IOUtils.toByteArray(resourceAsStream); importStatuses.add( importProvidedPage(resourceName, content, removable, editable, addIfMissing)); } catch (IOException | SBonitaException e) { throw new BonitaException("Unable to import the page " + resourceName, e); } } else { throw new BonitaException( "A resource " + resource.getDescription() + " could not be read when loading default pages"); } } return importStatuses; } protected ImportStatus importProvidedPage(String pageZipName, final byte[] providedPageContent, boolean removable, boolean editable, boolean addIfMissing) throws SBonitaException { SPage page = pageService.buildPage(providedPageContent, pageZipName, SessionService.SYSTEM_ID, true, removable, editable); ImportStatus importStatus = new ImportStatus(page.getName()); SPage sPageInDb = pageService.checkIfPageAlreadyExists(page); if (sPageInDb == null && addIfMissing) { log.debug("Provided page {} does not exist yet, importing it.", page.getName()); page.setPageHash(DigestUtils.md5DigestAsHex(providedPageContent)); pageService.insertPage(page, providedPageContent); } else if (sPageInDb == null) { log.debug("Provided page {} has been deleted by the user, and will not be imported", page.getName()); importStatus.setStatus(ImportStatus.Status.SKIPPED); } else if (sPageInDb.isProvided()) { String md5Sum = DigestUtils.md5DigestAsHex(providedPageContent); if (Objects.equals(sPageInDb.getPageHash(), md5Sum)) { log.debug("Provided page exists and is up to date, nothing to do"); importStatus.setStatus(ImportStatus.Status.SKIPPED); } else { log.info("Provided page {} exists but the content is not up to date, updating it.", page.getName()); pageService.updatePageContent(sPageInDb.getId(), providedPageContent, pageZipName); importStatus.setStatus(ImportStatus.Status.REPLACED); } } else { log.debug("Page {} was updated by the user, and will not be updated", page.getName()); importStatus.setStatus(ImportStatus.Status.SKIPPED); } return importStatus; } protected List importProvidedApplicationsFromClasspath(final String locationPattern, final boolean editable, final boolean addIfMissing) throws IOException, ImportException { List importStatuses = new ArrayList<>(); Resource[] resources = cpResourceResolver.getResources(locationPattern); for (Resource resource : resources) { if (resource.exists() && resource.isReadable() && resource.contentLength() > 0) { String resourceName = resource.getFilename(); log.debug("Found provided applications '{}' in classpath", resourceName); try (InputStream resourceAsStream = resource.getInputStream()) { ApplicationZipContent zipContent = ApplicationZipContent.getApplicationZipContent(resourceName, resourceAsStream); importStatuses.addAll( importProvidedApplications(zipContent.getXmlRaw(), zipContent.getIconRaw(), zipContent.getPngName(), editable, addIfMissing)); } catch (IOException | ImportException | AlreadyExistsException e) { throw new ImportException(e); } } else { throw new ImportException( "A resource " + resource + " could not be read when loading default applications"); } } return importStatuses; } protected List importProvidedApplications(final byte[] xmlContent, final byte[] iconContent, final String iconMimeType, final boolean editable, final boolean addIfMissing) throws ImportException, AlreadyExistsException { List importStatuses = new ArrayList<>(); ApplicationNodeContainer applicationNodeContainer = applicationImporter.getApplicationNodeContainer(xmlContent); for (AbstractApplicationNode applicationNode : applicationNodeContainer.getAllApplications()) { // set the strategy to skip it if a version already exists importStatuses.add( applicationImporter.importApplication(applicationNode, editable, SessionService.SYSTEM_ID, iconContent, iconMimeType, addIfMissing, new UpdateNewerNonEditableApplicationStrategy())); } return importStatuses; } /** * Parses the given list of import statuses of living applications resources to filter those that are marked as * SKIPPED and returns the result in a readable format. * * @param importStatuses list of statuses that result from the import of living applications resources * @return a list of non-skipped imported resources in a format that concatenates their name and status */ protected List getNonSkippedImportedResources(final List importStatuses) { return importStatuses.stream() .filter(importStatus -> importStatus.getStatus() != ImportStatus.Status.SKIPPED) .map(importStatus -> importStatus.getName() + " " + importStatus.getStatus()) .collect(Collectors.toList()); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/MandatoryLivingApplicationImporter.java ================================================ /** * Copyright (C) 2023 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.business.application.importer; import java.io.IOException; import java.util.List; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.commons.ExceptionUtils; import org.bonitasoft.engine.commons.TenantLifecycleService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.page.PageService; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.stereotype.Component; /** * Service used to import provided living applications that are mandatory for the platform to start, such as: *
    *
  • Bonita Super Admin Application
  • *
  • Bonita Application Directory
  • *
*/ @Component @Slf4j public class MandatoryLivingApplicationImporter extends LivingApplicationImporter implements TenantLifecycleService { private static final String NON_EDITABLE_NON_REMOVABLE_PAGES_PATH = "org/bonitasoft/web/page/final"; private static final String EDITABLE_NON_REMOVABLE_PAGES_PATH = "org/bonitasoft/web/page/editonly"; private static final String PROVIDED_FINAL_APPLICATIONS_PATH = "org/bonitasoft/web/application/final"; private final DefaultLivingApplicationImporter defaultLivingApplicationImporter; @Getter private boolean firstRun; public MandatoryLivingApplicationImporter(final PageService pageService, final ApplicationImporter applicationImporter, final DefaultLivingApplicationImporter defaultLivingApplicationImporter) { super(pageService, applicationImporter); this.defaultLivingApplicationImporter = defaultLivingApplicationImporter; } @Override public void init() throws SBonitaException { // Step 1: import mandatory pages log.info("Importing Bonita mandatory pages"); importMandatoryPages(); log.info("Import of Bonita mandatory pages completed"); // Step 2: import mandatory living apps log.info("Importing Bonita mandatory applications"); importMandatoryApplications(); log.info("Import of Bonita mandatory applications completed"); } private void importMandatoryPages() { try { List importStatuses = importProvidedNonRemovableNonEditablePagesFromClasspath(); // If all mandatory pages have been added OR replaced, then it is considered a first startup this.firstRun = importStatuses.stream().map(ImportStatus::getStatus) .noneMatch(importStatus -> importStatus == ImportStatus.Status.SKIPPED); defaultLivingApplicationImporter.setAddRemovablePagesIfMissing(firstRun); importStatuses.addAll(importProvidedNonRemovableEditablePagesFromClasspath()); List createdOrReplaced = getNonSkippedImportedResources(importStatuses); if (createdOrReplaced.isEmpty()) { log.debug("No mandatory pages updated"); } else { log.debug("Mandatory pages updated or created: {}", createdOrReplaced); } } catch (BonitaException | IOException e) { log.error(ExceptionUtils.printLightWeightStacktrace(e)); log.debug("Stacktrace of the import issue is:", e); } } private List importProvidedNonRemovableNonEditablePagesFromClasspath() throws BonitaException, IOException { // import the provided pages as non-removable, non-editable and add them if they are missing return importProvidedPagesFromClasspath( ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "/" + NON_EDITABLE_NON_REMOVABLE_PAGES_PATH + "/*.zip", false, false, true); } private List importProvidedNonRemovableEditablePagesFromClasspath() throws IOException, BonitaException { // import the provided pages as non-removable, editable and add them if they are missing return importProvidedPagesFromClasspath( ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "/" + EDITABLE_NON_REMOVABLE_PAGES_PATH + "/*.zip", false, true, true); } private void importMandatoryApplications() { try { // import the provided applications as non-editable and add them if they are missing List importStatuses = importProvidedApplicationsFromClasspath( ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "/" + PROVIDED_FINAL_APPLICATIONS_PATH + "/*.zip", false, true); defaultLivingApplicationImporter.setAddEditableApplicationsIfMissing(importStatuses.stream() .map(ImportStatus::getStatus) .noneMatch(status -> status == ImportStatus.Status.SKIPPED)); List createdOrReplaced = getNonSkippedImportedResources(importStatuses); if (createdOrReplaced.isEmpty()) { log.info("No mandatory applications updated"); } else { log.info("Mandatory applications updated or created: {}", createdOrReplaced); } } catch (Exception e) { log.error("Cannot load provided mandatory applications at startup. Root cause: {}", ExceptionUtils.printRootCauseOnly(e)); log.debug("Full stack:", e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/ReplaceDuplicateApplicationImportStrategy.java ================================================ /** * 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.business.application.importer; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; /** * @author Pascal Garcia * @author Emmanuel Duchastenier */ public class ReplaceDuplicateApplicationImportStrategy implements ApplicationImportStrategy { ReplaceDuplicateApplicationImportStrategy() { } @Override public ImportStrategy whenApplicationExists(SApplication existing, SApplicationWithIcon toBeImported) { return ImportStrategy.REPLACE; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/StrategySelector.java ================================================ /** * 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.business.application.importer; import org.bonitasoft.engine.business.application.ApplicationImportPolicy; /** * @author Elias Ricken de Medeiros */ public class StrategySelector { public StrategySelector() { } public ApplicationImportStrategy selectStrategy(ApplicationImportPolicy policy) { switch (policy) { case REPLACE_DUPLICATES: return new ReplaceDuplicateApplicationImportStrategy(); case FAIL_ON_DUPLICATES: default: return new FailOnDuplicateApplicationImportStrategy(); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/UpdateNewerNonEditableApplicationStrategy.java ================================================ /** * Copyright (C) 2021 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.business.application.importer; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; public class UpdateNewerNonEditableApplicationStrategy implements ApplicationImportStrategy { UpdateNewerNonEditableApplicationStrategy() { } @Override public ImportStrategy whenApplicationExists(SApplication existing, SApplicationWithIcon toBeImported) { if (existing != null) { if (!existing.isEditable() && !existing.getVersion().equals(toBeImported.getVersion())) { return ImportStrategy.REPLACE; } } return ImportStrategy.SKIP; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/validator/ApplicationImportValidator.java ================================================ /** * 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.business.application.importer.validator; import org.bonitasoft.engine.exception.ImportException; import org.springframework.stereotype.Component; /** * @author Elias Ricken de Medeiros */ @Component public class ApplicationImportValidator { private final ApplicationTokenValidator tokenValidator; public ApplicationImportValidator(ApplicationTokenValidator tokenValidator) { this.tokenValidator = tokenValidator; } public void validate(String token) throws ImportException { ValidationStatus validationStatus = tokenValidator.validate(token); if (!validationStatus.isValid()) { throw new ImportException(validationStatus.getMessage()); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/validator/ApplicationMenuCreatorValidator.java ================================================ /** * 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.business.application.importer.validator; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.business.application.ApplicationMenuCreator; import org.bonitasoft.engine.business.application.ApplicationMenuField; import org.springframework.stereotype.Component; /** * @author Elias Ricken de Medeiros */ @Component public class ApplicationMenuCreatorValidator { public List isValid(final ApplicationMenuCreator creator) { List problems = new ArrayList<>(); final Map fields = creator.getFields(); if (fields.get(ApplicationMenuField.APPLICATION_ID) == null) { problems.add("The applicationId cannot be null"); } return problems; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/validator/ApplicationTokenValidator.java ================================================ /** * 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.business.application.importer.validator; import java.util.Arrays; import java.util.List; import org.springframework.stereotype.Component; /** * @author Elias Ricken de Medeiros */ @Component public class ApplicationTokenValidator { public ValidationStatus validate(String token) { List keywords = Arrays.asList("content", "api", "theme"); if (token == null || !token.matches("((\\p{Alnum})|-|\\.|_|~)+") || keywords.contains(token.toLowerCase())) { StringBuilder stb = new StringBuilder("The token '"); stb.append(token); stb.append( "' is invalid: the token can not be null or empty and should contain only alpha numeric characters and the following "); stb.append( "special characters '-', '.', '_' or '~'. In addition, the following words are reserved keywords and cannot be used as token: 'api', 'content', 'theme'."); return new ValidationStatus(false, stb.toString()); } return new ValidationStatus(true); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/importer/validator/ValidationStatus.java ================================================ /** * 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.business.application.importer.validator; /** * @author Elias Ricken de Medeiros */ public class ValidationStatus { boolean valid; private String message; public ValidationStatus(final boolean valid) { this.valid = valid; } public ValidationStatus(boolean valid, final String message) { this.valid = valid; this.message = message; } public String getMessage() { return message; } public boolean isValid() { return valid; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/data/BusinessDataRetriever.java ================================================ /** * 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.business.data; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.business.data.proxy.ServerProxyfier; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; /** * @author Elias Ricken de Medeiros */ public class BusinessDataRetriever { private final BusinessDataRepository businessDataRepository; private final ServerProxyfier proxyfier; public BusinessDataRetriever(BusinessDataRepository businessDataRepository, ServerProxyfier proxyfier) { this.businessDataRepository = businessDataRepository; this.proxyfier = proxyfier; } /** * Retrieves the Business Data related to the given {@link SSimpleRefBusinessDataInstance}. If the * {@code SSimpleRefBusinessDataInstance} does not * references any Business Data the result will be null. * * @param dataRef the business data reference * @param bizClass the business data class * @return the Business Data related to the given {@code SSimpleRefBusinessDataInstance} or null if no Business Data * is referenced. * @throws SBusinessDataNotFoundException when no Business Data is found for the given id */ public Entity getSimpleBusinessData(SSimpleRefBusinessDataInstance dataRef, Class bizClass) throws SBusinessDataNotFoundException { if (dataRef.getDataId() == null) { return null; } final Entity entity = businessDataRepository.findById(bizClass, dataRef.getDataId()); return proxyfier.proxify(entity); } /** * Retrieves the list of Business Data related to the given {@link SProcessMultiRefBusinessDataInstance}. If the * {@code SMultiRefBusinessDataInstance} does not * references any Business Data the result will em empty list. * * @param dataRef the multi business data reference * @param bizClass the business data class * @return the list of Business Data related to the given {@code SMultiRefBusinessDataInstance} or empty list if no * Business Data is referenced. */ public List getMultiBusinessData(SProcessMultiRefBusinessDataInstance dataRef, Class bizClass) { if (dataRef.getDataIds() == null || dataRef.getDataIds().isEmpty()) { return new ArrayList<>(); } final List entities = businessDataRepository.findByIds(bizClass, dataRef.getDataIds()); final List e = new ArrayList<>(); for (final Entity entity : entities) { e.add(proxyfier.proxify(entity)); } return e; } /** * Retrieves a Business Data or a List of Business Data related to the given {@link SRefBusinessDataInstance} * depending on its type (single {@link Entity} * if it's a {@link SSimpleRefBusinessDataInstance} or a List if it's a * {@link SProcessMultiRefBusinessDataInstance}. * This method will use {@link #getSimpleBusinessData(SSimpleRefBusinessDataInstance, Class)} or * {@link #getMultiBusinessData(SProcessMultiRefBusinessDataInstance, Class)} based on the data reference type * * @param refBusinessDataInstance the business data reference * @return The {@code Entity} or {@code List} if the business data reference is a * {@code SSimpleRefBusinessDataInstance} or a * {@code SMultiRefBusinessDataInstance} respectively * @throws SBusinessDataNotFoundException * @throws SExpressionEvaluationException */ public Object getBusinessData(final SRefBusinessDataInstance refBusinessDataInstance) throws SBusinessDataNotFoundException, SExpressionEvaluationException { try { final Class bizClass = (Class) Thread.currentThread().getContextClassLoader() .loadClass(refBusinessDataInstance.getDataClassName()); if (refBusinessDataInstance instanceof SSimpleRefBusinessDataInstance) { return getSimpleBusinessData((SSimpleRefBusinessDataInstance) refBusinessDataInstance, bizClass); } final SProcessMultiRefBusinessDataInstance reference = (SProcessMultiRefBusinessDataInstance) refBusinessDataInstance; return getMultiBusinessData(reference, bizClass); } catch (final ClassNotFoundException e) { throw new SExpressionEvaluationException( "Unable to load class for the business data having reference '" + refBusinessDataInstance.getName() + "'", e, refBusinessDataInstance.getName()); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/data/RefBusinessDataRetriever.java ================================================ /** * 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.business.data; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; 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.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.operation.BusinessDataContext; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Elias Ricken de Medeiros * @author Emmanuel Duchastenier */ public class RefBusinessDataRetriever { private RefBusinessDataService refBusinessDataService; private final FlowNodeInstanceService flowNodeInstanceService; private ProcessInstanceService processInstanceService; public RefBusinessDataRetriever(final RefBusinessDataService refBusinessDataService, FlowNodeInstanceService flowNodeInstanceService, ProcessInstanceService processInstanceService) { this.refBusinessDataService = refBusinessDataService; this.flowNodeInstanceService = flowNodeInstanceService; this.processInstanceService = processInstanceService; } public SRefBusinessDataInstance getRefBusinessDataInstance(BusinessDataContext context) throws SBonitaException { if (isProcessContext(context)) { return getRefBusinessDataUsingProcessContext(context); } return getRefBusinessDataUsingFlowNodeContext(context); } private SRefBusinessDataInstance getRefBusinessDataUsingFlowNodeContext(BusinessDataContext context) throws SBonitaReadException, SFlowNodeReadException, SRefBusinessDataInstanceNotFoundException, SProcessInstanceReadException { SRefBusinessDataInstance refBusinessDataInstance = getRefBusinessDataInFlowNode(context); if (refBusinessDataInstance != null) { return refBusinessDataInstance; } return getRefBusinessDataInProcess(context, getProcessInstanceIdFromFlowNode(context)); } private SRefBusinessDataInstance getRefBusinessDataInProcess(BusinessDataContext context, long rootProcessInstanceId) throws SBonitaReadException, SRefBusinessDataInstanceNotFoundException { try { return refBusinessDataService.getRefBusinessDataInstance(context.getName(), rootProcessInstanceId); } catch (SRefBusinessDataInstanceNotFoundException e) { SARefBusinessDataInstance saRefBusinessDataInstance = refBusinessDataService .getSARefBusinessDataInstance(context.getName(), rootProcessInstanceId); if (saRefBusinessDataInstance == null) { return null; } return saRefBusinessDataInstance.toSRefBusinessDataInstance(); } } private boolean isProcessContext(BusinessDataContext context) { return DataInstanceContainer.PROCESS_INSTANCE.name().equals(context.getContainer().getType()); } private long getProcessInstanceIdFromFlowNode(BusinessDataContext context) throws SFlowNodeReadException, SBonitaReadException, SProcessInstanceReadException { try { SFlowNodeInstance flowNodeInstance = flowNodeInstanceService .getFlowNodeInstance(context.getContainer().getId()); SProcessInstance processInstance = processInstanceService .getProcessInstance(flowNodeInstance.getParentProcessInstanceId()); if (isSubProcess(processInstance)) { return getParentOfSubProcess(processInstance); } return flowNodeInstance.getParentProcessInstanceId(); } catch (SFlowNodeNotFoundException | SProcessInstanceNotFoundException e) { SAFlowNodeInstance lastArchivedFlowNodeInstance = flowNodeInstanceService .getLastArchivedFlowNodeInstance(SAFlowNodeInstance.class, context.getContainer().getId()); //No caller type in archived process instance, get archive business data not supported from event subprocess return lastArchivedFlowNodeInstance.getParentProcessInstanceId(); } } private SRefBusinessDataInstance getRefBusinessDataInFlowNode(BusinessDataContext context) throws SBonitaReadException { try { return refBusinessDataService.getFlowNodeRefBusinessDataInstance(context.getName(), context.getContainer().getId()); } catch (final SRefBusinessDataInstanceNotFoundException sbe) { try { SARefBusinessDataInstance saFlowNodeRefBusinessDataInstance = refBusinessDataService .getSAFlowNodeRefBusinessDataInstance(context.getName(), context.getContainer().getId()); if (saFlowNodeRefBusinessDataInstance == null) { return null; } return saFlowNodeRefBusinessDataInstance.toSRefBusinessDataInstance(); } catch (SRefBusinessDataInstanceNotFoundException e) { return null; } } } private SRefBusinessDataInstance getRefBusinessDataUsingProcessContext(BusinessDataContext context) throws SBonitaReadException, SRefBusinessDataInstanceNotFoundException, SProcessInstanceReadException, SProcessInstanceNotFoundException, SFlowNodeReadException, SFlowNodeNotFoundException { return getRefBusinessDataInProcess(context, getProcessInstanceIdThatCanContainBusinessData(context.getContainer().getId())); } /* * get the first process in hierarchy that can contains business data, i.e. the process instance itself or its * parent if it is an event subprocess */ public long getProcessInstanceIdThatCanContainBusinessData(long processInstanceId) throws SProcessInstanceReadException, SBonitaReadException, SProcessInstanceNotFoundException, SFlowNodeReadException, SFlowNodeNotFoundException { try { SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); if (isSubProcess(processInstance)) { return getParentOfSubProcess(processInstance); } return processInstanceId; } catch (SProcessInstanceNotFoundException e) { //No caller type in archived process instance, get archive business data not supported from event subprocess return processInstanceId; } } private boolean isSubProcess(SProcessInstance processInstance) { return SFlowNodeType.SUB_PROCESS.equals(processInstance.getCallerType()); } private long getParentOfSubProcess(SProcessInstance processInstance) throws SFlowNodeNotFoundException, SFlowNodeReadException { return flowNodeInstanceService.getFlowNodeInstance(processInstance.getCallerId()).getParentProcessInstanceId(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/data/converter/BusinessDataModelConverter.java ================================================ /** * 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.business.data.converter; import org.bonitasoft.engine.business.data.MultipleBusinessDataReference; import org.bonitasoft.engine.business.data.SimpleBusinessDataReference; import org.bonitasoft.engine.business.data.impl.MultipleBusinessDataReferenceImpl; import org.bonitasoft.engine.business.data.impl.SimpleBusinessDataReferenceImpl; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance; /** * @author Elias Ricken de Medeiros */ public class BusinessDataModelConverter { public static SimpleBusinessDataReference toSimpleBusinessDataReference( final SSimpleRefBusinessDataInstance sReference) { return new SimpleBusinessDataReferenceImpl(sReference.getName(), sReference.getDataClassName(), sReference.getDataId()); } public static MultipleBusinessDataReference toMultipleBusinessDataReference( final SProcessMultiRefBusinessDataInstance sReference) { return new MultipleBusinessDataReferenceImpl(sReference.getName(), sReference.getDataClassName(), sReference.getDataIds()); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/AbstractStartProcessCommand.java ================================================ /** * 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.command; import java.io.Serializable; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.impl.ProcessStarter; import org.bonitasoft.engine.bpm.contract.ContractViolationException; import org.bonitasoft.engine.bpm.process.ProcessActivationException; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessExecutionException; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.execution.AdvancedStartProcessValidator; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Elias Ricken de Medeiros */ public abstract class AbstractStartProcessCommand extends RuntimeCommand { public static final String STARTED_BY = "started_by"; public static final String PROCESS_DEFINITION_ID = "process_definition_id"; public static final String OPERATIONS = "operations"; public static final String CONTEXT = "context"; public static final String PROCESS_CONTRACT_INPUTS = "process_contract_inputs"; @Override public Serializable execute(final Map parameters, final ServiceAccessor serviceAccessor) throws SCommandParameterizationException, SCommandExecutionException { // get parameters final long processDefinitionId = getProcessDefinitionId(parameters); final List activityNames = getActivityNames(parameters); final long startedBy = getStartedBy(parameters); final Map context = getContext(parameters); final List operations = getOperations(parameters); Map processContractInputs = getProcessContractInputs(parameters); try { validateInputs(serviceAccessor, processDefinitionId, activityNames, processContractInputs); return startProcess(processDefinitionId, activityNames, startedBy, context, operations, processContractInputs); } catch (final SCommandExecutionException e) { throw e; } catch (final Exception e) { throw new SCommandExecutionException(e); } } private ProcessInstance startProcess(final long processDefinitionId, final List activityNames, final long startedBy, final Map context, final List operations, Map processContractInputs) throws ProcessDefinitionNotFoundException, ProcessActivationException, ProcessExecutionException, ContractViolationException { final ProcessStarter starter = new ProcessStarter(startedBy, processDefinitionId, operations, context, activityNames, processContractInputs); return starter.start(); } private void validateInputs(final ServiceAccessor serviceAccessor, final long processDefinitionId, final List activityNames, Map processContractInputs) throws SBonitaException { final AdvancedStartProcessValidator validator = new AdvancedStartProcessValidator( serviceAccessor.getProcessDefinitionService(), processDefinitionId, serviceAccessor.getExpressionService()); final List problems = validator.validate(activityNames, processContractInputs); handleProblems(problems); } private void handleProblems(final List problems) throws SCommandExecutionException { if (!problems.isEmpty()) { final StringBuilder stb = new StringBuilder(); for (final String problem : problems) { stb.append(problem); stb.append("\n"); } throw new SCommandExecutionException(stb.toString()); } } private Long getStartedBy(final Map parameters) throws SCommandParameterizationException { return getLongMandatoryParameter(parameters, STARTED_BY); } private Long getProcessDefinitionId(final Map parameters) throws SCommandParameterizationException { return getLongMandatoryParameter(parameters, PROCESS_DEFINITION_ID); } private List getOperations(final Map parameters) throws SCommandParameterizationException { return getParameter(parameters, OPERATIONS); } private Map getContext(final Map parameters) throws SCommandParameterizationException { return getParameter(parameters, CONTEXT); } private Map getProcessContractInputs(final Map parameters) throws SCommandParameterizationException { return getParameter(parameters, PROCESS_CONTRACT_INPUTS); } protected abstract List getActivityNames(Map parameters) throws SCommandParameterizationException; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/AdvancedStartProcessCommand.java ================================================ /** * 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.command; import java.io.Serializable; import java.util.Collections; import java.util.List; import java.util.Map; /** * This command starts the process in the specified activity (if you need to specify several activities as start points, * please, use * {@link MultipleStartPointsProcessCommand}). Connectors on process start will be executed. *

It can be executed using the {@link org.bonitasoft.engine.api.CommandAPI#execute(String, java.util.Map)}. * Example: {@code commandAPI.execute("advancedStartProcessCommand", parameters)}

* Parameters: *
    *
  • started_by: the user id (long) is used as the process starter. It's a mandatory parameter.
  • *
  • process_definition_id: the process definition id (long) identifies the process to start. It's a mandatory * parameter.
  • *
  • activity_name: the name of the activity (String) where the process will start the execution. It's a mandatory * parameter.
  • *
  • operations: the operations (ArrayList) are executed when the process starts (set variables and * documents). It's an optional parameter.
  • *
  • context: the context (HashMap) is used during operations execution. It's an optional * parameter.
  • *
* Limitations: *
    *
  • It is not possible to start the execution of a process from a gateway, a boundary event or an event * sub-process
  • *
  • The process must be started when there is only one active branch. Otherwise use * {@code MultipleStartPointsProcessCommand}
  • *
* Example: * start -> step1 -> gateway1 -> (step2 || step3) -> gateway2 -> step4 -> end *
    *
  • Ok: start from "start" or "step1" or "step4" or "end"
  • *
  • All other start points are invalid.
  • *
* * @author Vincent Elcrin * @see org.bonitasoft.engine.command.MultipleStartPointsProcessCommand */ public class AdvancedStartProcessCommand extends AbstractStartProcessCommand { public static final String ACTIVITY_NAME = "activity_name"; protected List getActivityNames(final Map parameters) throws SCommandParameterizationException { return Collections.singletonList(getStringMandatoryParameter(parameters, ACTIVITY_NAME)); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/BusinessDataCommandField.java ================================================ /** * 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.command; /** * @author Laurent Leseigneur */ public interface BusinessDataCommandField { String BUSINESS_DATA_URI_PATTERN = "businessDataURIPattern"; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/Command.java ================================================ /** * 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.command; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.api.CommandAPI; import org.bonitasoft.engine.service.ServiceAccessor; /** * A command is a class that is called from the API and executed on the server side.
* It is used to extend the engine behavior. See {@link org.bonitasoft.engine.api.CommandAPI} for explanations of how to * deploy, undeploy and execute a command. *
* This class should not be directly subclassed by implementors: use {@link RuntimeCommand} instead * * @see org.bonitasoft.engine.api.CommandAPI * @see org.bonitasoft.engine.command.RuntimeCommand * @author Matthieu Chaffotte */ public interface Command { /** * Method that is called by the engine on the server side when the client calls * {@link CommandAPI#execute(String, Map)} with the name or id of this * command. * Implementors of commands must put here the code to be executed on the server side * * @param parameters * a map of parameters that can be used by the command and that is given by the client when executing the * command * @param serviceAccessor * the ServiceAccessor that provides access to the engine's server-side services * @return * a result that will be returned to the client * @throws SCommandParameterizationException * can be thrown if insufficient or wrong parameters are given by the client * @throws SCommandExecutionException * can be thrown when something unexpected happens while executing the command */ Serializable execute(Map parameters, T serviceAccessor) throws SCommandParameterizationException, SCommandExecutionException; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/DeletePlatformSessionCommand.java ================================================ /** * 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.command; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.platform.session.PlatformSessionService; import org.bonitasoft.engine.platform.session.SSessionNotFoundException; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Charles Souillard */ public class DeletePlatformSessionCommand extends RuntimeCommand { public Serializable execute(Map parameters, ServiceAccessor serviceAccessor) throws SCommandParameterizationException, SCommandExecutionException { Long sessionId = (Long) parameters.get("sessionId"); PlatformSessionService sessionService = serviceAccessor.getPlatformSessionService(); try { sessionService.deleteSession(sessionId.longValue()); } catch (SSessionNotFoundException e) { throw new SCommandExecutionException(e); } return null; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/ExecuteBDMQueryCommand.java ================================================ /** * 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.command; import static org.apache.commons.lang3.BooleanUtils.toBoolean; import java.io.IOException; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.bdm.serialization.BusinessDataObjectMapper; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.NonUniqueResultException; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Romain Bioteau * @author Matthieu Chaffotte */ public class ExecuteBDMQueryCommand extends RuntimeCommand { public static final String RETURNS_LIST = "returnsList"; public static final String QUERY_PARAMETERS = "queryParameters"; public static final String RETURN_TYPE = "returnType"; public static final String QUERY_NAME = "queryName"; public static final String START_INDEX = "startIndex"; public static final String MAX_RESULTS = "maxResults"; // Avoid multiple instantiations for better performance, see BS-16794 private static final BusinessDataObjectMapper businessDataObjectMapper = new BusinessDataObjectMapper(); @Override public Serializable execute(final Map parameters, final ServiceAccessor serviceAccessor) throws SCommandParameterizationException, SCommandExecutionException { final String queryName = getStringMandatoryParameter(parameters, QUERY_NAME); @SuppressWarnings("unchecked") final Map queryParameters = (Map) parameters.get(QUERY_PARAMETERS); final String returnType = getStringMandatoryParameter(parameters, RETURN_TYPE); Class resultClass = loadClass(returnType); final BusinessDataRepository businessDataRepository = serviceAccessor.getBusinessDataRepository(); final Boolean returnsList = (Boolean) parameters.get(RETURNS_LIST); final Serializable result; if (toBoolean(returnsList)) { final Integer startIndex = getIntegerMandatoryParameter(parameters, START_INDEX); final Integer maxResults = getIntegerMandatoryParameter(parameters, MAX_RESULTS); result = (Serializable) businessDataRepository.findListByNamedQuery(queryName, resultClass, queryParameters, startIndex, maxResults); } else { try { result = businessDataRepository.findByNamedQuery(queryName, resultClass, queryParameters); } catch (final NonUniqueResultException e) { throw new SCommandExecutionException(e); } } return serializeResult(result); } private static byte[] serializeResult(final Serializable result) throws SCommandExecutionException { try { return businessDataObjectMapper.writeValueAsBytes(result); } catch (final IOException jpe) { throw new SCommandExecutionException(jpe); } } @SuppressWarnings("unchecked") private Class loadClass(final String returnType) throws SCommandParameterizationException { try { return (Class) Thread.currentThread().getContextClassLoader().loadClass(returnType); } catch (final ClassNotFoundException e) { throw new SCommandParameterizationException("Unable to load class for type " + returnType, e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/GetBusinessDataByIdCommand.java ================================================ /** * 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.command; import java.io.Serializable; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.engine.business.data.BusinessDataService; import org.bonitasoft.engine.business.data.SBusinessDataNotFoundException; import org.bonitasoft.engine.business.data.SBusinessDataRepositoryException; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Matthieu Chaffotte * @author Laurent Leseigneur */ public class GetBusinessDataByIdCommand extends RuntimeCommand { public static final String ENTITY_CLASS_NAME = "entityClassName"; public static final String BUSINESS_DATA_ID = "businessDataId"; public static final String BUSINESS_DATA_CHILD_NAME = "businessDataChildName"; @Override public Serializable execute(final Map parameters, final ServiceAccessor serviceAccessor) throws SCommandParameterizationException, SCommandExecutionException { final BusinessDataService businessDataService = serviceAccessor.getBusinessDataService(); final Long identifier = getLongMandatoryParameter(parameters, BUSINESS_DATA_ID); final String entityClassName = getStringMandatoryParameter(parameters, ENTITY_CLASS_NAME); final String businessDataURIPattern = getStringMandatoryParameter(parameters, BusinessDataCommandField.BUSINESS_DATA_URI_PATTERN); final String childName = getParameter(parameters, BUSINESS_DATA_CHILD_NAME); try { if (StringUtils.isNotEmpty(childName)) { return businessDataService.getJsonChildEntity(entityClassName, identifier, childName, businessDataURIPattern); } else { return businessDataService.getJsonEntity(entityClassName, identifier, businessDataURIPattern); } } catch (final SBusinessDataNotFoundException e) { throw new SCommandExecutionException(e.convertToClientException()); } catch (final SBusinessDataRepositoryException e) { throw new SCommandExecutionException(e.convertToClientException()); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/GetBusinessDataByIdsCommand.java ================================================ /** * 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.command; import java.io.Serializable; import java.util.List; import java.util.Map; import org.bonitasoft.engine.business.data.BusinessDataService; import org.bonitasoft.engine.business.data.SBusinessDataRepositoryException; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Matthieu Chaffotte */ public class GetBusinessDataByIdsCommand extends RuntimeCommand { public static final String ENTITY_CLASS_NAME = "entityClassName"; public static final String BUSINESS_DATA_IDS = "businessDataIds"; @Override public Serializable execute(final Map parameters, final ServiceAccessor serviceAccessor) throws SCommandParameterizationException, SCommandExecutionException { final BusinessDataService businessDataService = serviceAccessor.getBusinessDataService(); final List identifiers = getMandatoryParameter(parameters, BUSINESS_DATA_IDS, "Parameters map must contain an entry " + BUSINESS_DATA_IDS + " with a Long List value."); final String entityClassName = getStringMandatoryParameter(parameters, ENTITY_CLASS_NAME); final String businessDataURIPattern = getStringMandatoryParameter(parameters, BusinessDataCommandField.BUSINESS_DATA_URI_PATTERN); try { return businessDataService.getJsonEntities(entityClassName, identifiers, businessDataURIPattern); } catch (final SBusinessDataRepositoryException e) { throw new SCommandExecutionException(e.convertToClientException()); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/GetBusinessDataByQueryCommand.java ================================================ /** * 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.command; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.business.data.SBusinessDataRepositoryException; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Matthieu Chaffotte * @author Laurent Leseigneur */ public class GetBusinessDataByQueryCommand extends RuntimeCommand { public static final String QUERY_PARAMETERS = "queryParameters"; public static final String ENTITY_CLASS_NAME = "entityClassName"; public static final String QUERY_NAME = "queryName"; public static final String START_INDEX = "startIndex"; public static final String MAX_RESULTS = "maxResults"; @Override public Serializable execute(final Map parameters, final ServiceAccessor serviceAccessor) throws SCommandParameterizationException, SCommandExecutionException { final String queryName = getStringMandatoryParameter(parameters, QUERY_NAME); @SuppressWarnings("unchecked") final Map queryParameters = (Map) parameters.get(QUERY_PARAMETERS); final String entityClassName = getStringMandatoryParameter(parameters, ENTITY_CLASS_NAME); final Integer startIndex = getIntegerMandatoryParameter(parameters, START_INDEX); final Integer maxResults = super.getIntegerMandatoryParameter(parameters, MAX_RESULTS); String businessDataURIPattern = getStringMandatoryParameter(parameters, BusinessDataCommandField.BUSINESS_DATA_URI_PATTERN); try { return serviceAccessor.getBusinessDataService().getJsonQueryEntities(entityClassName, queryName, queryParameters, startIndex, maxResults, businessDataURIPattern); } catch (SBusinessDataRepositoryException e) { throw new SCommandExecutionException(e.convertToClientException()); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/MultipleStartPointsProcessCommand.java ================================================ /** * 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.command; import java.io.Serializable; import java.util.List; import java.util.Map; /** * This command starts the process in the specified activity(ies). Connectors on process start will be executed. *

It can be executed using the {@link org.bonitasoft.engine.api.CommandAPI#execute(String, java.util.Map)}. * Example: {@code commandAPI.execute("multipleStartPointsProcessCommand", parameters)}

* Parameters: *
    *
  • started_by: the user id (long) is used as the process starter. It's a mandatory parameter.
  • *
  • process_definition_id: the process definition id (long) identifies the process to start. It's a mandatory * parameter.
  • *
  • activity_names: list of activity names (ArrayList) defining where the process will start the execution. * It's a mandatory parameter.
  • *
  • operations: the operations (ArrayList) are executed when the process starts (set variables and * documents). It's an optional parameter.
  • *
  • context: the context (HashMap) is used during operations execution. It's an optional * parameter.
  • *
* Limitations: It is not possible to start the execution of a process from a gateway, a boundary event or an event * sub-process *

Use this command carefully: note that no validation will be done concerning the start points coherence.

* * @author Elias Ricken de Medeiros * @since 6.5.0 */ public class MultipleStartPointsProcessCommand extends AbstractStartProcessCommand { public static final String ACTIVITY_NAMES = "activity_names"; @Override protected List getActivityNames(final Map parameters) throws SCommandParameterizationException { return getMandatoryParameter(parameters, ACTIVITY_NAMES, "Missing mandatory field: " + ACTIVITY_NAMES); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/RuntimeCommand.java ================================================ /** * Copyright (C) 2023 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.command; import java.io.Serializable; import java.util.Map; import java.util.function.Supplier; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Danila Mazour */ public abstract class RuntimeCommand implements Command { @SuppressWarnings("unchecked") protected T getParameter(final Map parameters, final String parameterName, final String message) throws SCommandParameterizationException { try { return (T) parameters.get(parameterName); } catch (final Exception e) { throw new SCommandParameterizationException(message); } } @SuppressWarnings("unchecked") protected T getParameter(final Map parameters, final String parameterName, final Supplier messageSupplier) throws SCommandParameterizationException { try { return (T) parameters.get(parameterName); } catch (final Exception e) { throw new SCommandParameterizationException(messageSupplier.get()); } } protected T getParameter(final Map parameters, final String parameterName) throws SCommandParameterizationException { return getParameter(parameters, parameterName, "An error occurred while parsing " + parameterName); } protected Long getLongMandatoryParameter(final Map parameters, final String field) throws SCommandParameterizationException { final String message = "Parameters map must contain an entry " + field + " with a long value."; final Long mandatoryParameter = getMandatoryParameter(parameters, field, message); if (mandatoryParameter == 0L) { throw new SCommandParameterizationException(message); } return mandatoryParameter; } protected Integer getIntegerMandatoryParameter(final Map parameters, final String field) throws SCommandParameterizationException { final String message = "Parameters map must contain an entry " + field + " with a int value."; return getMandatoryParameter(parameters, field, message); } protected String getStringMandatoryParameter(final Map parameters, final String field) throws SCommandParameterizationException { final String message = "Parameters map must contain an entry " + field + " with a String value."; return getMandatoryParameter(parameters, field, message); } protected Map getMapMandatoryParameter(final Map parameters, final String field) throws SCommandParameterizationException { return getMandatoryParameter(parameters, field, () -> "Parameters map must contain an entry " + field + " with a value of type Map."); } protected T getMandatoryParameter(final Map parameters, final String field, final String message) throws SCommandParameterizationException { final T value = getParameter(parameters, field, message); if (value == null) { throw new SCommandParameterizationException(message); } return value; } protected T getMandatoryParameter(final Map parameters, final String field, final Supplier messageSupplier) throws SCommandParameterizationException { final T value = getParameter(parameters, field, messageSupplier); if (value == null) { throw new SCommandParameterizationException(messageSupplier.get()); } return value; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/SCommandExecutionException.java ================================================ /** * 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.command; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Thrown by a {@link Command} when something unexpected happens during execution. * * @author Matthieu Chaffotte */ public class SCommandExecutionException extends SBonitaException { private static final long serialVersionUID = -6359883557268565892L; public SCommandExecutionException(final String message) { super(message); } public SCommandExecutionException(final Throwable cause) { super(cause); } public SCommandExecutionException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/SCommandParameterizationException.java ================================================ /** * 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.command; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Thrown by a {@link Command} that is called using insufficient or wrong parameters. * * @author Matthieu Chaffotte */ public class SCommandParameterizationException extends SBonitaException { private static final long serialVersionUID = -9049241280889323348L; public SCommandParameterizationException(final String message) { super(message); } public SCommandParameterizationException(final Throwable cause) { super(cause); } public SCommandParameterizationException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/SGroupProfileMemberAlreadyExistsException.java ================================================ /** * 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.command; /** * @author Celine Souchet */ public class SGroupProfileMemberAlreadyExistsException extends SCommandExecutionException { private static final long serialVersionUID = -6359883557268565892L; public SGroupProfileMemberAlreadyExistsException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/SRoleProfileMemberAlreadyExistsException.java ================================================ /** * 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.command; /** * @author Celine Souchet */ public class SRoleProfileMemberAlreadyExistsException extends SCommandExecutionException { private static final long serialVersionUID = -6359883557268565892L; public SRoleProfileMemberAlreadyExistsException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/SUserMembershipProfileMemberAlreadyExistsException.java ================================================ /** * 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.command; /** * @author Celine Souchet */ public class SUserMembershipProfileMemberAlreadyExistsException extends SCommandExecutionException { private static final long serialVersionUID = -6359883557268565892L; public SUserMembershipProfileMemberAlreadyExistsException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/SUserProfileMemberAlreadyExistsException.java ================================================ /** * 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.command; /** * @author Celine Souchet */ public class SUserProfileMemberAlreadyExistsException extends SCommandExecutionException { private static final long serialVersionUID = -6359883557268565892L; public SUserProfileMemberAlreadyExistsException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/command/system/SearchWaitingEventsCommand.java ================================================ /** * 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.command.system; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.command.RuntimeCommand; import org.bonitasoft.engine.command.SCommandExecutionException; import org.bonitasoft.engine.command.SCommandParameterizationException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchWaitingEventSerchDescriptor; import org.bonitasoft.engine.search.events.trigger.SearchWaitingEvents; import org.bonitasoft.engine.service.ServiceAccessor; /** * Search Waiting events * Parameters -> * searchOptions: the searchOptions * * @author Elias Ricken de Medeiros */ public class SearchWaitingEventsCommand extends RuntimeCommand { private static final String SEARCH_OPTIONS_KEY = "searchOptions"; /** * @param parameters * searchOptions: the searchOptions */ @Override public Serializable execute(final Map parameters, final ServiceAccessor serviceAccessor) throws SCommandParameterizationException, SCommandExecutionException { final EventInstanceService eventInstanceService = serviceAccessor.getEventInstanceService(); final SearchOptions searchOptions = getMandatoryParameter(parameters, SEARCH_OPTIONS_KEY, "Missing mandatory field: " + SEARCH_OPTIONS_KEY); final SearchWaitingEvents searchWaitingEvents = new SearchWaitingEvents(new SearchWaitingEventSerchDescriptor(), searchOptions, eventInstanceService); try { searchWaitingEvents.execute(); } catch (final SBonitaException e) { throw new SCommandExecutionException(e); } return searchWaitingEvents.getResult(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/connector/ConnectorAPIAccessorImpl.java ================================================ /** * 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.connector; import java.io.Serial; import java.lang.reflect.Proxy; import org.bonitasoft.engine.api.APIAccessor; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.BusinessDataAPI; import org.bonitasoft.engine.api.CommandAPI; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.PermissionAPI; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.ProfileAPI; import org.bonitasoft.engine.api.impl.ClientInterceptor; import org.bonitasoft.engine.api.impl.ServerAPIFactory; import org.bonitasoft.engine.api.internal.ServerAPI; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.session.model.SSession; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; /** * @author Baptiste Mesta * @author Celine Souchet */ public class ConnectorAPIAccessorImpl implements APIAccessor { @Serial private static final long serialVersionUID = 3365911149008207537L; private APISession apiSession; public ConnectorAPIAccessorImpl() { super(); } protected APISession getAPISession() { if (apiSession == null) { final ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance(); final SessionAccessor sessionAccessor = serviceAccessor.getSessionAccessor(); final SessionService sessionService = serviceAccessor.getSessionService(); try { final SSession session = sessionService.createSession(ConnectorAPIAccessorImpl.class.getSimpleName());// FIXME get the sessionAccessor.setSessionId(session.getId()); apiSession = ModelConvertor.toAPISession(session); } catch (final BonitaRuntimeException e) { throw e; } catch (final Exception e) { throw new BonitaRuntimeException(e); } } return apiSession; } @Override public IdentityAPI getIdentityAPI() { return getAPI(IdentityAPI.class, getAPISession()); } @Override public ProcessAPI getProcessAPI() { return getAPI(ProcessAPI.class, getAPISession()); } @Override public CommandAPI getCommandAPI() { return getAPI(CommandAPI.class, getAPISession()); } @Override public ProfileAPI getProfileAPI() { return getAPI(ProfileAPI.class, getAPISession()); } @Override public PermissionAPI getPermissionAPI() { return getAPI(PermissionAPI.class, getAPISession()); } @Override public PageAPI getCustomPageAPI() { return getAPI(PageAPI.class, getAPISession()); } @Override public ApplicationAPI getLivingApplicationAPI() { return getAPI(ApplicationAPI.class, getAPISession()); } @Override public BusinessDataAPI getBusinessDataAPI() { return getAPI(BusinessDataAPI.class, getAPISession()); } private static ServerAPI getServerAPI() { return ServerAPIFactory.getServerAPI(false); } private static T getAPI(final Class clazz, final APISession session) { final ServerAPI serverAPI = getServerAPI(); final ClientInterceptor sessionInterceptor = new ClientInterceptor(clazz.getName(), serverAPI, session); return (T) Proxy.newProxyInstance(APIAccessor.class.getClassLoader(), new Class[] { clazz }, sessionInterceptor); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/connector/ConnectorServiceDecorator.java ================================================ /** * 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.connector; import java.io.Serializable; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.bonitasoft.engine.core.connector.ConnectorResult; import org.bonitasoft.engine.core.connector.ConnectorService; import org.bonitasoft.engine.core.connector.exception.SConnectorException; import org.bonitasoft.engine.core.connector.exception.SInvalidConnectorImplementationException; import org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.expression.EngineConstantExpressionBuilder; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.resources.SBARResource; /** * This service wraps the connector service and add engine variables like apiAccessor, engineExecutionContext. * * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class ConnectorServiceDecorator implements ConnectorService { private final ConnectorService connectorService; public ConnectorServiceDecorator(final ConnectorService connectorService) { super(); this.connectorService = connectorService; } @Override public ConnectorResult executeMultipleEvaluation(final long processDefinitionId, final String connectorDefinitionId, final String connectorDefinitionVersion, final Map connectorInputParameters, final Map> inputValues, final ClassLoader classLoader, final SExpressionContext sexpContext) throws SConnectorException { final Map parameters = new HashMap(connectorInputParameters); parameters.put("connectorApiAccessor", EngineConstantExpressionBuilder.getConnectorAPIAccessorExpression()); parameters.put("engineExecutionContext", EngineConstantExpressionBuilder.getEngineExecutionContext()); return connectorService.executeMultipleEvaluation(processDefinitionId, connectorDefinitionId, connectorDefinitionVersion, parameters, inputValues, classLoader, sexpContext); } @Override public boolean loadConnectors(final SProcessDefinition sDefinition) throws SConnectorException { return connectorService.loadConnectors(sDefinition); } @Override public void setConnectorImplementation(final SProcessDefinition sProcessDefinition, final String connectorId, final String connectorVersion, final byte[] connectorImplementationArchive) throws SConnectorException, SInvalidConnectorImplementationException { connectorService.setConnectorImplementation(sProcessDefinition, connectorId, connectorVersion, connectorImplementationArchive); } @Override public List getConnectorImplementations(final long processDefinitionId, final int fromIndex, final int numberPerPage, final String field, final OrderByType order) throws SConnectorException { return connectorService.getConnectorImplementations(processDefinitionId, fromIndex, numberPerPage, field, order); } @Override public SConnectorImplementationDescriptor getConnectorImplementation(final long processDefinitionId, final String connectorId, final String connectorVersion) throws SConnectorException { return connectorService.getConnectorImplementation(processDefinitionId, connectorId, connectorVersion); } @Override public Map evaluateInputParameters(final String connectorId, final Map parameters, final SExpressionContext sExpressionContext, final Map> inputValues) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { SExpression apiAccessorExpression = EngineConstantExpressionBuilder.getConnectorAPIAccessorExpression(); SExpression engineExecutionContext = EngineConstantExpressionBuilder.getEngineExecutionContext(); final Map newParameters = new HashMap(parameters); newParameters.put("connectorApiAccessor", apiAccessorExpression); newParameters.put("engineExecutionContext", engineExecutionContext); return connectorService.evaluateInputParameters(connectorId, newParameters, sExpressionContext, inputValues); } @Override public void executeOutputOperation(final List outputs, final SExpressionContext expressionContext, final ConnectorResult connectorOutput) throws SConnectorException { connectorService.executeOutputOperation(outputs, expressionContext, connectorOutput); } @Override public CompletableFuture executeConnector(final long processDefinitionId, final SConnectorInstance sConnectorInstance, SConnectorImplementationDescriptor connectorImplementationDescriptor, final ClassLoader classLoader, final Map inputParameters) throws SConnectorException { return connectorService.executeConnector(processDefinitionId, sConnectorInstance, connectorImplementationDescriptor, classLoader, inputParameters); } @Override public void disconnect(final ConnectorResult result) throws SConnectorException { connectorService.disconnect(result); } @Override public Long getNumberOfConnectorImplementations(final long processDefinitionId) throws SConnectorException { return connectorService.getNumberOfConnectorImplementations(processDefinitionId); } @Override public List getConnectorImplementations(long processDefinitionId, int from, int numberOfElements) throws SBonitaReadException { return connectorService.getConnectorImplementations(processDefinitionId, from, numberOfElements); } @Override public void addConnectorImplementation(Long processDefinitionId, String name, byte[] content) throws SRecorderException { connectorService.addConnectorImplementation(processDefinitionId, name, content); } @Override public void removeConnectorImplementations(long processDefinitionId) throws SBonitaReadException, SRecorderException { connectorService.removeConnectorImplementations(processDefinitionId); } @Override public SConnectorImplementationDescriptor getConnectorImplementationDescriptor(long processDefinitionId, String connectorId, String version) throws SConnectorException { return connectorService.getConnectorImplementation(processDefinitionId, connectorId, version); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/core/document/api/impl/DocumentHelper.java ================================================ /** * 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.core.document.api.impl; import java.io.File; import java.nio.file.Files; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.bonitasoft.engine.bpm.contract.FileInputValue; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.document.DocumentValue; import org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.commons.io.IOUtil; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.model.AbstractSMappedDocument; import org.bonitasoft.engine.core.document.model.SDocument; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.core.document.model.builder.SDocumentBuilder; import org.bonitasoft.engine.core.document.model.builder.SDocumentBuilderFactory; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; import org.bonitasoft.engine.core.process.definition.model.SDocumentListDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * Helper class that set/get/update document on API level (it uses the process definition) * * @author Baptiste Mesta */ public class DocumentHelper { private final DocumentService documentService; private final ProcessDefinitionService processDefinitionService; private final ProcessInstanceService processInstanceService; public DocumentHelper(final DocumentService documentService, final ProcessDefinitionService processDefinitionService, final ProcessInstanceService processInstanceService) { this.documentService = documentService; this.processDefinitionService = processDefinitionService; this.processInstanceService = processInstanceService; } public List getAllDocumentOfTheList(final long processInstanceId, final String name) throws SBonitaReadException { QueryOptions queryOptions = new QueryOptions(0, 100); List mappedDocuments; final List result = new ArrayList<>(); do { mappedDocuments = documentService.getDocumentList(name, processInstanceId, queryOptions.getFromIndex(), queryOptions.getNumberOfResults()); result.addAll(mappedDocuments); queryOptions = QueryOptions.getNextPage(queryOptions); } while (mappedDocuments.size() == 100); return result; } public boolean isListDefinedInDefinition(final String documentName, final long processInstanceId) throws org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException, SBonitaReadException { try { final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); final SProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); final List documentDefinitions = processDefinition.getProcessContainer() .getDocumentListDefinitions(); for (final SDocumentListDefinition documentDefinition : documentDefinitions) { if (documentName.equals(documentDefinition.getName())) { return true; } } } catch (final SProcessInstanceNotFoundException e) { throw new SObjectNotFoundException( "Unable to find the list " + documentName + ", nothing in database and the process instance " + processInstanceId + " is not found", e); } catch (final SProcessInstanceReadException e) { throw new SBonitaReadException(e); } catch (final SProcessDefinitionNotFoundException e) { throw new SObjectNotFoundException( "Unable to find the list " + documentName + " on process instance " + processInstanceId + ", nothing in database and the process definition is not found", e); } return false; } public SDocument createDocumentObject(final DocumentValue documentValue, final long authorId) { final SDocumentBuilder processDocumentBuilder = new SDocumentBuilderFactory().createNewInstance( documentValue.getFileName(), getMimeTypeOrGuessIt(documentValue), authorId); processDocumentBuilder.setHasContent(documentValue.hasContent()); processDocumentBuilder.setURL(documentValue.getUrl()); processDocumentBuilder.setContent(documentValue.getContent()); return processDocumentBuilder.done(); } String getMimeTypeOrGuessIt(DocumentValue documentValue) { final String mimeType = documentValue.getMimeType(); final byte[] content = documentValue.getContent(); final String fileName = documentValue.getFileName(); if (mimeType != null && !mimeType.isEmpty() || content == null || fileName == null || fileName.isEmpty()) { return mimeType; } try { final File tempFile = File.createTempFile("tmp", fileName); IOUtil.write(tempFile, content); final String s = Files.probeContentType(tempFile.toPath()); tempFile.delete(); return s; } catch (Throwable e) { return mimeType; } } public void deleteDocument(final String documentName, final long processInstanceId) throws SObjectModificationException { try { documentService.removeCurrentVersion(processInstanceId, documentName); } catch (final SObjectNotFoundException e) { // nothing to do } } /** * @param newValue the new value * @param documentName the name of the document * @param processInstanceId the id of the process instance * @param authorId the author id * @param description used only when creating a document * @throws SBonitaReadException * @throws SObjectCreationException * @throws SObjectModificationException */ public void createOrUpdateDocument(final DocumentValue newValue, final String documentName, final long processInstanceId, final long authorId, String description) throws SBonitaReadException, SObjectCreationException, SObjectModificationException { final SDocument document = createDocumentObject(newValue, authorId); try { // Let's check if the document already exists: final SMappedDocument mappedDocument = documentService.getMappedDocument(processInstanceId, documentName); // a document exist, update it with the new values documentService.updateDocument(mappedDocument, document); } catch (final SObjectNotFoundException e) { documentService.attachDocumentToProcessInstance(document, processInstanceId, documentName, description); } } public void setDocumentList(final List documentList, final String documentName, final long processInstanceId, final long authorId) throws SBonitaReadException, SObjectCreationException, SObjectNotFoundException, SObjectModificationException, SObjectAlreadyExistsException { // get the list having the name final List currentList = getExistingDocumentList(documentName, processInstanceId); // iterate on elements int index; for (index = 0; index < documentList.size(); index++) { processDocumentOnIndex(documentList.get(index), documentName, processInstanceId, currentList, index, authorId); } // when no more elements in documentList remove elements above removeOthersDocuments(currentList); } void updateExistingDocument(final AbstractSMappedDocument documentToUpdate, final int index, final DocumentValue documentValue, final long authorId) throws SObjectModificationException { if (documentValue.hasChanged()) { documentService.updateDocumentOfList(documentToUpdate, createDocumentObject(documentValue, authorId), index); } else { // update the index if needed if (documentToUpdate.getIndex() != index) { documentService.updateDocumentIndex(documentToUpdate, index); } } } AbstractSMappedDocument getDocumentHavingDocumentIdAndRemoveFromList( final List currentList, final Long documentId, final String documentName, final Long processInstanceId) throws SObjectNotFoundException { final Iterator iterator = currentList.iterator(); while (iterator.hasNext()) { final AbstractSMappedDocument next = iterator.next(); if (next.getId() == documentId) { iterator.remove(); return next; } } throw new SObjectNotFoundException( "The document with id " + documentId + " was not in the list " + documentName + " of process instance " + processInstanceId); } List getExistingDocumentList(final String documentName, final long processInstanceId) throws SBonitaReadException, SObjectNotFoundException { List currentList; currentList = getAllDocumentOfTheList(processInstanceId, documentName); // if it's not a list it throws an exception if (currentList.isEmpty() && !isListDefinedInDefinition(documentName, processInstanceId)) { throw new SObjectNotFoundException( "Unable to find the list " + documentName + " on process instance " + processInstanceId + ", nothing in database and nothing declared in the definition"); } return currentList; } void removeOthersDocuments(final List currentList) throws SObjectModificationException { for (final AbstractSMappedDocument mappedDocument : currentList) { documentService.removeCurrentVersion(mappedDocument); } } void processDocumentOnIndex(final DocumentValue documentValue, final String documentName, final long processInstanceId, final List currentList, final int index, final long authorId) throws SObjectCreationException, SObjectAlreadyExistsException, SObjectNotFoundException, SObjectModificationException { if (documentValue.getDocumentId() != null) { // if hasChanged update final AbstractSMappedDocument documentToUpdate = getDocumentHavingDocumentIdAndRemoveFromList(currentList, documentValue.getDocumentId(), documentName, processInstanceId); updateExistingDocument(documentToUpdate, index, documentValue, authorId); } else { // create new element documentService.attachDocumentToProcessInstance(createDocumentObject(documentValue, authorId), processInstanceId, documentName, null, index); } } public DocumentValue toCheckedDocumentValue(final Object newValue) throws SOperationExecutionException { if (newValue != null) { final boolean isFileInput = newValue instanceof FileInputValue; if (isFileInput) { FileInputValue fileInput = ((FileInputValue) newValue); return toDocumentValue(fileInput); } final boolean isDocumentWithContent = newValue instanceof DocumentValue; if (!isDocumentWithContent) { throw new SOperationExecutionException( "Document operation only accepts an expression returning a DocumentValue and not " + newValue.getClass().getName()); } } return (DocumentValue) newValue; } public DocumentValue toDocumentValue(FileInputValue fileInput) { if (fileInput.getId() == null) { return new DocumentValue(fileInput.getContent(), fileInput.getContentType(), fileInput.getFileName()); } else if (fileInput.getContent() != null) { return new DocumentValue(Long.valueOf(fileInput.getId()), fileInput.getContent(), fileInput.getContentType(), fileInput.getFileName()); } else { return new DocumentValue(Long.valueOf(fileInput.getId())); } } public DocumentValue toDocumentValue(Document document) throws SOperationExecutionException { DocumentValue documentValue; if (document.hasContent()) { try { byte[] documentContent = documentService.getDocumentContent(document.getContentStorageId()); documentValue = new DocumentValue(documentContent, document.getContentMimeType(), document.getContentFileName()); } catch (SObjectNotFoundException e) { throw new SOperationExecutionException( "Unable to execute set document operation because the content of the document to use is not found", e); } } else { documentValue = new DocumentValue(document.getUrl()); } return documentValue; } public List toCheckedList(final Object newValue) throws SOperationExecutionException { if (!(newValue instanceof List)) { throw new SOperationExecutionException( "Document operation only accepts an expression returning a list of DocumentValue"); } List newList = new ArrayList<>(((List) newValue).size()); for (final Object item : (List) newValue) { if (item instanceof FileInputValue) { newList.add(toDocumentValue((FileInputValue) item)); continue; } if (item instanceof Document) { newList.add(toDocumentValue((Document) item)); continue; } if (item instanceof DocumentValue) { newList.add((DocumentValue) item); continue; } if (item == null) { //ignore the item continue; } throw new SOperationExecutionException( "Document operation only accepts an expression returning a list of DocumentValue"); } return newList; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/core/form/ExternalURLAdapter.java ================================================ /** * 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.core.form; import java.io.Serializable; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.page.URLAdapter; import org.bonitasoft.engine.page.URLAdapterConstants; /** * @author Baptiste Mesta, Anthony Birembaut */ public class ExternalURLAdapter implements URLAdapter { private static final String PARAM_TAG = "?"; private static final String HASH_TAG = "#"; @Override public String adapt(String url, String key, Map context) throws SExecutionException { @SuppressWarnings("unchecked") final Map queryParameters = (Map) context .get(URLAdapterConstants.QUERY_PARAMETERS); final String[] hash = url.split(HASH_TAG); StringBuffer newURL = new StringBuffer(hash[0]); appendParametersToURL(newURL, queryParameters); if (url.contains(HASH_TAG)) { newURL.append(HASH_TAG); if (hash.length > 1) { newURL.append(hash[1]); } } return newURL.toString(); } protected void appendParametersToURL(StringBuffer url, final Map parameters) throws SExecutionException { if (parameters != null) { for (Entry parameterEntry : parameters.entrySet()) { appendParameterToURL(url, parameterEntry.getKey(), parameterEntry.getValue()); } } } protected void appendParameterToURL(final StringBuffer newURL, final String key, final String[] value) { appendSeparator(newURL, PARAM_TAG); newURL.append(key).append("="); for (int i = 0; i < value.length; i++) { if (i > 0) { newURL.append(","); } newURL.append(value[i]); } } protected void appendSeparator(final StringBuffer buffer, final String initialSeparator) { if (buffer.indexOf(initialSeparator) != -1) { buffer.append("&"); } else { buffer.append(initialSeparator); } } @Override public String getId() { return URLAdapterConstants.EXTERNAL_URL_ADAPTER; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/core/form/LegacyURLAdapter.java ================================================ /** * 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.core.form; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.form.FormMappingType; import org.bonitasoft.engine.page.URLAdapter; import org.bonitasoft.engine.page.URLAdapterConstants; /** * @author Baptiste Mesta, Anthony Birembaut */ public class LegacyURLAdapter implements URLAdapter { private static final String UUID_SEPERATOR = "--"; private static final String DEFAULT_FORM_MODE = "form"; ProcessDefinitionService processDefinitionService; FormMappingService formMappingService; public LegacyURLAdapter(final ProcessDefinitionService processDefinitionService, final FormMappingService formMappingService) { this.processDefinitionService = processDefinitionService; this.formMappingService = formMappingService; } @Override public String adapt(final String url, final String key, final Map context) throws SExecutionException { @SuppressWarnings("unchecked") final Map queryParameters = (Map) context .get(URLAdapterConstants.QUERY_PARAMETERS); String[] idParamValue = new String[0]; if (queryParameters != null) { idParamValue = queryParameters.get(URLAdapterConstants.ID_QUERY_PARAM); } String bpmId; if (idParamValue == null || idParamValue.length == 0) { throw new IllegalArgumentException("The parameter \"id\" is missing from the original URL"); } else { bpmId = idParamValue[0]; try { final SFormMapping formMapping = formMappingService.get(key); final SProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(formMapping.getProcessDefinitionId()); final String locale = (String) context.get(URLAdapterConstants.LOCALE); final String contextPath = (String) context.get(URLAdapterConstants.CONTEXT_PATH); boolean assignTask = false; final String[] assignTaskValue = queryParameters.get(URLAdapterConstants.ASSIGN_TASK_QUERY_PARAM); if (assignTaskValue != null && assignTaskValue.length > 0 && "true".equals(assignTaskValue[0])) { assignTask = true; } String user = null; final String[] userParamValue = queryParameters.get(URLAdapterConstants.USER_QUERY_PARAM); if (userParamValue != null && userParamValue.length > 0) { user = userParamValue[0]; } String mode = DEFAULT_FORM_MODE; final String[] modeParamValue = queryParameters.get(URLAdapterConstants.MODE_QUERY_PARAM); if (modeParamValue != null && modeParamValue.length > 0) { mode = modeParamValue[0]; } boolean autoInstantiate = true; final String[] autoInstantiateValue = queryParameters .get(URLAdapterConstants.AUTO_INSTANTIATE_QUERY_PARAM); if (autoInstantiateValue != null && autoInstantiateValue.length > 0 && "false".equals(autoInstantiateValue[0])) { autoInstantiate = false; } return generateLegacyURL(contextPath, locale, bpmId, formMapping, processDefinition, user, assignTask, mode, autoInstantiate); } catch (final SBonitaException e) { throw new SExecutionException( "Unable to generate the legacy form URL for key " + key + "(id: " + bpmId + ")", e); } } } protected String generateLegacyURL(final String contextPath, final String locale, final String bpmId, final SFormMapping formMapping, final SProcessDefinition processDefinition, final String user, final boolean assignTask, final String mode, boolean autoInstantiate) { final StringBuilder legacyFormURL = new StringBuilder(contextPath); legacyFormURL.append("/portal/homepage?ui=form&locale=") .append(locale) .append("&theme=") .append(formMapping.getProcessDefinitionId()) .append("#mode=") .append(mode) .append("&form=") .append(urlEncode(processDefinition.getName())) .append(UUID_SEPERATOR) .append(urlEncode(processDefinition.getVersion())); if (FormMappingType.TASK.getId().equals(formMapping.getType())) { legacyFormURL.append(UUID_SEPERATOR).append(urlEncode(formMapping.getTask() + "$")) .append("entry&task=") .append(bpmId); if (assignTask) { legacyFormURL.append("&assignTask=true"); } } else if (FormMappingType.PROCESS_OVERVIEW.getId().equals(formMapping.getType())) { legacyFormURL.append(urlEncode("$")) .append("recap&instance=").append(bpmId) .append("&recap=true"); } else { legacyFormURL.append(urlEncode("$")) .append("entry&process=").append(bpmId); if (!autoInstantiate) { legacyFormURL.append("&autoInstantiate=false"); } } if (user != null) { legacyFormURL.append("&userId=").append(user); } return legacyFormURL.toString(); } protected String urlEncode(final String stringToEncode) { try { return URLEncoder.encode(stringToEncode, "UTF-8"); } catch (final UnsupportedEncodingException e) { throw new BonitaRuntimeException(e); } } @Override public String getId() { return URLAdapterConstants.LEGACY_URL_ADAPTER; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/data/ParentContainerResolverImpl.java ================================================ /** * 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.data; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.commons.exceptions.SObjectReadException; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.data.instance.api.DataContainer; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.data.instance.api.ParentContainerResolver; import org.bonitasoft.engine.persistence.SBonitaReadException; public class ParentContainerResolverImpl implements ParentContainerResolver { private final FlowNodeInstanceService flowNodeInstanceService; private final ProcessInstanceService processInstanceService; private boolean allowUnknownContainer; public ParentContainerResolverImpl(final FlowNodeInstanceService flowNodeInstanceService, final ProcessInstanceService processInstanceService) { super(); this.flowNodeInstanceService = flowNodeInstanceService; this.processInstanceService = processInstanceService; } public boolean getAllowUnknownContainer() { return allowUnknownContainer; } public void setAllowUnknownContainer(boolean allowUnknownContainer) { this.allowUnknownContainer = allowUnknownContainer; } @Override public List getContainerHierarchy(final DataContainer currentContainer) throws SObjectNotFoundException, SObjectReadException { return getContainerHierarchy(currentContainer, false); } @Override public List getArchivedContainerHierarchy(final DataContainer currentContainer) throws SObjectNotFoundException, SObjectReadException { try { return getContainerHierarchy(currentContainer, true); } catch (SObjectNotFoundException e) { return Collections.singletonList(currentContainer); } } private List getContainerHierarchy(DataContainer currentContainer, boolean isArchived) throws SObjectNotFoundException, SObjectReadException { DataContainer container = new DataContainer(currentContainer.getId(), currentContainer.getType()); final List containerHierarchy = new ArrayList<>(); containerHierarchy.add(container); try { do { container = getNextContainer(isArchived, container, containerHierarchy); } while (container != null); return containerHierarchy; } catch (SProcessInstanceNotFoundException | SFlowNodeNotFoundException e) { throw new SObjectNotFoundException(e); } catch (SProcessInstanceReadException | SBonitaReadException | SFlowNodeReadException e) { throw new SObjectReadException(e); } } private DataContainer getNextContainer(boolean isArchived, DataContainer container, List containerHierarchy) throws SFlowNodeReadException, SFlowNodeNotFoundException, SBonitaReadException, SProcessInstanceNotFoundException, SProcessInstanceReadException, SObjectNotFoundException { if (DataInstanceContainer.ACTIVITY_INSTANCE.name().equals(container.getType())) { container = handleActivityContainer(containerHierarchy, getsFlowNodeInstance(container.getId(), isArchived)); } else if (DataInstanceContainer.MESSAGE_INSTANCE.name().equals(container.getType())) { container = null; } else if (DataInstanceContainer.PROCESS_INSTANCE.name().equals(container.getType())) { container = handleProcessContainer(container.getId(), containerHierarchy, isArchived); } else { if (allowUnknownContainer) { return null; } else { throw new SObjectNotFoundException("Unknown container type: " + container.getType()); } } return container; } private DataContainer handleActivityContainer(List containerHierarchy, ActivityContainer flowNodeInstance) { String containerType; if (flowNodeInstance.parentActivityInstanceId > 0) { containerType = DataInstanceContainer.ACTIVITY_INSTANCE.name(); } else { containerType = DataInstanceContainer.PROCESS_INSTANCE.name(); } DataContainer container = new DataContainer(flowNodeInstance.parentContainerId, containerType); containerHierarchy.add(container); if (flowNodeInstance.parentActivityInstanceId <= 0 && flowNodeInstance.parentContainerId == flowNodeInstance.rootContainerId) { container = null; } return container; } private DataContainer handleProcessContainer(long id, List containerHierarchy, boolean isArchived) throws SProcessInstanceNotFoundException, SProcessInstanceReadException, SFlowNodeNotFoundException, SBonitaReadException, SFlowNodeReadException { final long callerId = getCallerId(id, isArchived); if (callerId >= 0) { ActivityContainer callerFlowNodeInstance = getsFlowNodeInstance(callerId, isArchived); final SFlowNodeType callerType = callerFlowNodeInstance.type; if (callerType != null && callerType.equals(SFlowNodeType.SUB_PROCESS)) { final long callerProcessInstanceId = callerFlowNodeInstance.parentProcessInstanceId; DataContainer container = new DataContainer(callerProcessInstanceId, DataInstanceContainer.PROCESS_INSTANCE.name()); containerHierarchy.add(container); return container; } else { return null; } } else { return null; } } private ActivityContainer getSaFlowNodeInstance(long id) throws SBonitaReadException, SFlowNodeNotFoundException { SAFlowNodeInstance flowNodeInstance; flowNodeInstance = flowNodeInstanceService.getLastArchivedFlowNodeInstance(SAFlowNodeInstance.class, id); if (flowNodeInstance == null) { throw new SFlowNodeNotFoundException(id); } return new ActivityContainer(flowNodeInstance); } private ActivityContainer getsFlowNodeInstance(long id) throws SFlowNodeReadException, SFlowNodeNotFoundException { SFlowNodeInstance flowNodeInstance; flowNodeInstance = flowNodeInstanceService.getFlowNodeInstance(id); return new ActivityContainer(flowNodeInstance); } private long getCallerId(long id, boolean isArchived) throws SProcessInstanceNotFoundException, SProcessInstanceReadException, SBonitaReadException { if (isArchived) { return getArchivedCallerId(id); } else { return getCallerId(id); } } private ActivityContainer getsFlowNodeInstance(long callerId, boolean isArchived) throws SFlowNodeReadException, SFlowNodeNotFoundException, SBonitaReadException { if (isArchived) { return getSaFlowNodeInstance(callerId); } else { return getsFlowNodeInstance(callerId); } } private long getCallerId(Long processInstanceId) throws SProcessInstanceNotFoundException, SProcessInstanceReadException { SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); return processInstance.getCallerId(); } private long getArchivedCallerId(Long processInstanceId) throws SProcessInstanceNotFoundException, SProcessInstanceReadException, SBonitaReadException { SAProcessInstance processInstance = processInstanceService.getLastArchivedProcessInstance(processInstanceId); if (processInstance == null) { throw new SProcessInstanceNotFoundException(processInstanceId); } return processInstance.getCallerId(); } private class ActivityContainer { private long parentContainerId; private long parentActivityInstanceId; private long rootContainerId; public SFlowNodeType type; public long parentProcessInstanceId; public ActivityContainer(SFlowNodeInstance flowNodeInstance) { parentContainerId = flowNodeInstance.getParentContainerId(); parentActivityInstanceId = flowNodeInstance.getParentActivityInstanceId(); rootContainerId = flowNodeInstance.getRootContainerId(); type = flowNodeInstance.getType(); parentProcessInstanceId = flowNodeInstance.getParentProcessInstanceId(); } public ActivityContainer(SAFlowNodeInstance flowNodeInstance) { parentContainerId = flowNodeInstance.getParentContainerId(); parentActivityInstanceId = flowNodeInstance.getParentActivityInstanceId(); rootContainerId = flowNodeInstance.getRootContainerId(); type = flowNodeInstance.getType(); parentProcessInstanceId = flowNodeInstance.getParentProcessInstanceId(); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/event/PlatformStartedEvent.java ================================================ /** * Copyright (C) 2022 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.event; import lombok.Data; /** * Spring event published when the Bonita Platform has finished to start. *

This event can be listened using Spring standards, for instance: * *

 * @EventListener
 * public void handlePlatformStarted(PlatformStartedEvent event) { ... }
 * 
*/ @Data public class PlatformStartedEvent { } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/AdvancedStartProcessValidator.java ================================================ /** * 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.execution; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.contract.validation.ContractValidator; import org.bonitasoft.engine.bpm.contract.validation.ContractValidatorFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SContractDefinition; 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.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SContractViolationException; import org.bonitasoft.engine.expression.ExpressionService; /** * @author Elias Ricken de Medeiros */ public class AdvancedStartProcessValidator { private final ProcessDefinitionService processDefinitionService; private final long processDefinitionId; private ExpressionService expressionService; public AdvancedStartProcessValidator(ProcessDefinitionService processDefinitionService, long processDefinitionId, ExpressionService expressionService) { this.processDefinitionService = processDefinitionService; this.processDefinitionId = processDefinitionId; this.expressionService = expressionService; } public List validate(List flowNodeNames, Map processContractInputs) throws SBonitaException { List problems = new ArrayList<>(); if (flowNodeNames.isEmpty()) { problems.add("The list of activity names to start cannot be empty!"); } List foundFlowNodes = new ArrayList<>(flowNodeNames.size()); SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId); problems.addAll(checkFlowNodesAreSupported(flowNodeNames, foundFlowNodes, processDefinition)); problems.addAll(checkForNotFoundFlowNodes(flowNodeNames, foundFlowNodes, processDefinition)); if (!problems.isEmpty()) { //check contract only if flow nodes are ok return problems; } problems.addAll(checkProcessContract(processContractInputs, processDefinition)); return problems; } private List checkProcessContract(Map processContractInputs, SProcessDefinition processDefinition) { return validateContract(processContractInputs, processDefinition.getContract(), processDefinition.getName()); } private List validateContract(Map inputs, SContractDefinition contract, String element) { if (contract == null) { return Collections.emptyList(); } final ContractValidator validator = new ContractValidatorFactory().createContractValidator(expressionService); try { validator.validate(processDefinitionId, contract, inputs); } catch (SContractViolationException e) { return e.getExplanations().isEmpty() ? Collections.singletonList(e.getSimpleMessage() + " on " + element) : appendElement(e, element); } return Collections.emptyList(); } private List appendElement(SContractViolationException e, String element) { ArrayList strings = new ArrayList<>(); for (String explanation : e.getExplanations()) { strings.add(explanation + " on " + element); } return strings; } private List checkFlowNodesAreSupported(List flowNodeNames, List foundFlowNodes, SProcessDefinition processDefinition) { List problems = new ArrayList<>(); for (SFlowNodeDefinition flowNode : processDefinition.getProcessContainer().getFlowNodes()) { boolean invalidType = SFlowNodeType.BOUNDARY_EVENT.equals(flowNode.getType()) || SFlowNodeType.SUB_PROCESS.equals(flowNode.getType()) || SFlowNodeType.GATEWAY.equals(flowNode.getType()); if (flowNodeNames.contains(flowNode.getName())) { foundFlowNodes.add(flowNode.getName()); if (invalidType) { problems.add(buildInvalidTypeErrorMessage(processDefinition, flowNode)); } } } return problems; } private List checkForNotFoundFlowNodes(List flowNodeNames, List foundFlowNodes, SProcessDefinition processDefinition) { List problems = new ArrayList<>(); for (String flowNodeName : flowNodeNames) { if (!foundFlowNodes.contains(flowNodeName)) { problems.add(buildFlowNodeNotFoundErroMessage(processDefinition, flowNodeName)); } } return problems; } private String buildInvalidTypeErrorMessage(SProcessDefinition processDefinition, SFlowNodeDefinition flowNode) { return "'" + flowNode.getName() + "' is not a valid start point for the process " + buildProcessContext(processDefinition) + " You cannot start a process from a gateway, a boundary event or an event sub-process"; } private String buildFlowNodeNotFoundErroMessage(SProcessDefinition processDefinition, String flowNodeName) { return "No flownode named '" + flowNodeName + "' was found in the process" + buildProcessContext(processDefinition); } private String buildProcessContext(SProcessDefinition processDefinition) { return "."; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ContainerExecutor.java ================================================ /** * 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.execution; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeExecutionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; /** * @author Baptiste Mesta * @author Celine Souchet */ public interface ContainerExecutor { /** * Method called to notify this container executor that a child reached the given state * * @param processDefinitionId * The identifier of the process definition * @param parentId * The identifier of the parent of the flow node * @param childFlowNode * @throws SBonitaException */ void childFinished(long processDefinitionId, long parentId, SFlowNodeInstance childFlowNode) throws SBonitaException; /** * Execute a flow node in the context of this container executor * * @param flowNodeInstance * The flow node instance * @param executerId * The identifier of the user which execute the flow node * @param executerSubstituteId * The identifier of the delegated user which execute the flow node * @return The new state of the flow node after execution * @throws SFlowNodeReadException * @throws SFlowNodeExecutionException * Throw if there is an error when execute the flow node */ FlowNodeState executeFlowNode(SFlowNodeInstance flowNodeInstance, final Long executerId, final Long executerSubstituteId) throws SFlowNodeReadException, SFlowNodeExecutionException; /** * @return The handled type */ String getHandledType(); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ContainerRegistry.java ================================================ /** * 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.execution; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeExecutionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.execution.work.BPMWorkFactory; import org.bonitasoft.engine.work.SWorkRegisterException; import org.bonitasoft.engine.work.WorkService; /** * @author Baptiste Mesta * @author Celine Souchet * @author Matthieu Chaffotte */ public class ContainerRegistry { private final Map executors = new HashMap(2); private final WorkService workService; private final BPMWorkFactory workFactory; public ContainerRegistry(final WorkService workService, BPMWorkFactory workFactory) { super(); this.workService = workService; this.workFactory = workFactory; } public void addContainerExecutor(final ContainerExecutor containerExecutor) { executors.put(containerExecutor.getHandledType(), containerExecutor); } public void notifyChildFinished(SFlowNodeInstance flowNodeInstance) throws SWorkRegisterException { workService.registerWork(workFactory.createNotifyChildFinishedWorkDescriptor(flowNodeInstance)); } public void nodeReachedState(SFlowNodeInstance flowNodeInstance) throws SBonitaException { final ContainerExecutor containerExecutor = executors.get(flowNodeInstance.getParentContainerType().name()); if (containerExecutor != null) { containerExecutor.childFinished(flowNodeInstance.getProcessDefinitionId(), flowNodeInstance.getParentContainerId(), flowNodeInstance); } else { throw new SActivityExecutionException( "There is no container executor for the container " + flowNodeInstance.getParentContainerId() + " having the type " + flowNodeInstance.getParentContainerType()); } } private ContainerExecutor getContainerExecutor(final String containerType) { return executors.get(containerType); } public void executeFlowNode(SFlowNodeInstance flowNodeInstance) throws SWorkRegisterException { workService.registerWork(workFactory.createExecuteFlowNodeWorkDescriptor(flowNodeInstance)); } // FIXME, we should never execute a flow node directly, all call to this method should be replaced by a work @Deprecated public void executeFlowNodeInSameThread(final SFlowNodeInstance flowNodeInstance, final String containerType) throws SFlowNodeReadException, SFlowNodeExecutionException { final ContainerExecutor containerExecutor = getContainerExecutor(containerType); containerExecutor.executeFlowNode(flowNodeInstance, null, null); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/EvaluateExpression.java ================================================ /** * 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.execution; import java.io.Serializable; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Baptiste Mesta */ final class EvaluateExpression implements TransactionContentWithResult { private final SExpressionContext contextDependency; private final SExpression expression; private Serializable result; private final ExpressionResolverService expressionResolverService; public EvaluateExpression(final ExpressionResolverService expressionResolverService, final SExpressionContext contextDependency, final SExpression expression) { this.expressionResolverService = expressionResolverService; this.contextDependency = contextDependency; this.expression = expression; } @Override public void execute() throws SBonitaException { result = (Serializable) expressionResolverService.evaluate(expression, contextDependency); } @Override public Serializable getResult() { return result; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/Filter.java ================================================ /** * 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.execution; import java.io.Serializable; /** * @author Elias Ricken de Medeiros */ public interface Filter extends Serializable { boolean mustSelect(E element); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/FlowElementExecutor.java ================================================ /** * 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.execution; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; /** * @author Baptiste Mesta */ public interface FlowElementExecutor { void executeActivity(SProcessDefinition sDefinition, SFlowNodeInstance flowNodeInstance); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/FlowNodeExecutor.java ================================================ /** * 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.execution; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; /** * @author Baptiste Mesta * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface FlowNodeExecutor extends ContainerExecutor { /** * @param flowNodeInstance * @param executerId * @param executerSubstituteId * @return * @throws SFlowNodeExecutionException */ FlowNodeState stepForward(SFlowNodeInstance flowNodeInstance, Long executerId, Long executerSubstituteId) throws SFlowNodeExecutionException; /** * force the state of a flow node toa particular state * * @param flowNodeInstanceId * @param stateId */ void setStateByStateId(long flowNodeInstanceId, int stateId) throws SActivityStateExecutionException; /** * @param childProcInst * @param childState * @param hasActionsToExecute * @throws SBonitaException * @since 6.1 */ void childReachedState(SProcessInstance childProcInst, ProcessInstanceState childState, boolean hasActionsToExecute) throws SBonitaException; /** * @param processDefinition * the process definition of the SFlowNodeInstance on which to execute the state. * @param flowNodeInstance * the SFlowNodeInstance whose state has to be executed * @param state * the FlowNodeState to execute * @return the next FlowNodeState, after executing current state * @throws SActivityStateExecutionException * if an exception occurs when executing current state * @throws SActivityExecutionException * if an exception occurs when retrieving next state * @since 6.0 */ StateCode executeState(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance, FlowNodeState state) throws SActivityStateExecutionException, SActivityExecutionException; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/FlowNodeExecutorImpl.java ================================================ /** * 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.execution; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import static org.bonitasoft.engine.core.process.instance.model.SStateCategory.ABORTING; import java.util.Optional; import java.util.function.Supplier; import org.bonitasoft.engine.SArchivingException; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.comment.api.SCommentAddException; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.comment.api.SystemCommentType; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeExecutionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; 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.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.execution.archive.BPMArchiverService; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.execution.work.BPMWorkFactory; import org.bonitasoft.engine.mdc.FlowNodeInstanceMDC; import org.bonitasoft.engine.mdc.MDCHelper; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.work.SWorkRegisterException; import org.bonitasoft.engine.work.WorkService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Emmanuel Duchastenier * @author Celine Souchet * @author Matthieu Chaffotte */ @Component("flowNodeExecutor") public class FlowNodeExecutorImpl implements FlowNodeExecutor { private static final Logger LOG = LoggerFactory.getLogger(FlowNodeExecutorImpl.class); private final FlowNodeStateManager flowNodeStateManager; private final ActivityInstanceService activityInstanceService; private final ContainerRegistry containerRegistry; private final ProcessDefinitionService processDefinitionService; private final SCommentService commentService; private final ClassLoaderService classLoaderService; private final WorkService workService; private final BPMWorkFactory workFactory; private final ProcessInstanceInterruptor processInstanceInterruptor; private final BPMArchiverService bpmArchiverService; public FlowNodeExecutorImpl(final FlowNodeStateManager flowNodeStateManager, final ActivityInstanceService activityInstanceManager, final ContainerRegistry containerRegistry, final ProcessDefinitionService processDefinitionService, final SCommentService commentService, final ClassLoaderService classLoaderService, final WorkService workService, BPMWorkFactory workFactory, final ProcessInstanceInterruptor processInstanceInterruptor, final BPMArchiverService bpmArchiverService) { this.flowNodeStateManager = flowNodeStateManager; activityInstanceService = activityInstanceManager; this.containerRegistry = containerRegistry; this.classLoaderService = classLoaderService; this.workService = workService; this.workFactory = workFactory; this.processInstanceInterruptor = processInstanceInterruptor; containerRegistry.addContainerExecutor(this); this.processDefinitionService = processDefinitionService; this.commentService = commentService; this.bpmArchiverService = bpmArchiverService; } @Override public StateCode executeState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance, final FlowNodeState state) throws SActivityStateExecutionException, SActivityExecutionException { try { // if state is part of normal state and the flowNode state category is aborting or canceling it's not necessary to execute the state StateCode stateCode = StateCode.DONE; if (state.getStateCategory().equals(flowNodeInstance.getStateCategory())) { stateCode = state.execute(processDefinition, flowNodeInstance); // Add a system comment for Human task only addSystemComment(flowNodeInstance, state); } return stateCode; } catch (final SCommentAddException e) { throw new SActivityExecutionException(e); } } private void addSystemComment(final SFlowNodeInstance flowNodeInstance, final FlowNodeState state) throws SCommentAddException { if (commentService.isCommentEnabled(SystemCommentType.STATE_CHANGE) && state.mustAddSystemComment(flowNodeInstance)) { commentService.addSystemComment(flowNodeInstance.getRootContainerId(), state.getSystemComment(flowNodeInstance)); } } @Override public FlowNodeState stepForward(SFlowNodeInstance flowNodeInstance, Long executerId, Long executerSubstituteId) throws SFlowNodeExecutionException { final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); Supplier mdc = () -> new FlowNodeInstanceMDC(flowNodeInstance.getId(), Optional.ofNullable(executerId), Optional.ofNullable(executerSubstituteId), flowNodeInstance.getProcessDefinitionId(), flowNodeInstance.getParentProcessInstanceId(), flowNodeInstance.getRootProcessInstanceId()); return MDCHelper.tryWithMDC(mdc, () -> { try { final long processDefinitionId = flowNodeInstance .getLogicalGroup(BuilderFactory.get(SUserTaskInstanceBuilderFactory.class) .getProcessDefinitionIndex()); final ClassLoader localClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, processDefinitionId)); Thread.currentThread().setContextClassLoader(localClassLoader); if (!flowNodeInstance.isStateExecuting()) { bpmArchiverService.archiveFlowNodeInstance(flowNodeInstance); setExecutedBy(executerId, flowNodeInstance); setExecutedBySubstitute(executerSubstituteId, flowNodeInstance); } final SProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); final FlowNodeState nextState = executeStateAndReturnNextState(flowNodeInstance, processDefinition); registerWorkIfUnstableOrTerminal(nextState, flowNodeInstance); return nextState; } catch (final SFlowNodeExecutionException e) { throw e; } catch (final SBonitaException e) { throw new SFlowNodeExecutionException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } }); } /** * Executes the current state, and returns the next step, if applicable. * In a normal case, the normal next state is returned. * If execution of the current state returns StateCode.EXECUTING (meaning the execution of the state is not * finished) * then null is returned (meaning we stay on the same state, some background work will trigger the next state * later). */ private FlowNodeState executeStateAndReturnNextState(final SFlowNodeInstance sFlowNodeInstance, final SProcessDefinition processDefinition) throws SActivityStateExecutionException, SActivityExecutionException, SFlowNodeModificationException { final StateCode stateCode = executeState(processDefinition, sFlowNodeInstance, flowNodeStateManager.getState(sFlowNodeInstance.getStateId())); switch (stateCode) { case DONE: return getNextNormalState(sFlowNodeInstance, processDefinition); case EXECUTING: default: // the state is still executing set the executing flag activityInstanceService.setExecuting(sFlowNodeInstance); return null; } } private FlowNodeState getNextNormalState(final SFlowNodeInstance sFlowNodeInstance, final SProcessDefinition processDefinition) throws SActivityExecutionException, SFlowNodeModificationException { final FlowNodeState state = flowNodeStateManager.getNextState(processDefinition, sFlowNodeInstance, sFlowNodeInstance.getStateId()); if (sFlowNodeInstance.getStateId() != state.getId()) { // this also unset the executing flag activityInstanceService.setState(sFlowNodeInstance, state); } return state; } private void registerWorkIfUnstableOrTerminal(FlowNodeState nextState, SFlowNodeInstance flowNodeInstance) throws SWorkRegisterException { if (flowNodeInstance.isStateExecuting() || nextState == null) { LOG.debug("No work to register on state {} for flowNode {}", nextState, flowNodeInstance); return; } if (!nextState.isStable()) { registerExecuteFlowNodeWork(flowNodeInstance); return; } if (nextState.isTerminal()) { registerNotifyFinishWork(flowNodeInstance); return; } // State is stable, nothing to do. LOG.debug("No work to register on state {} for flowNode {}", nextState, flowNodeInstance); } private void registerExecuteFlowNodeWork(SFlowNodeInstance sFlowNodeInstance) throws SWorkRegisterException { workService.registerWork(workFactory.createExecuteFlowNodeWorkDescriptor(sFlowNodeInstance)); } private void setExecutedBySubstitute(final Long executerSubstituteId, final SFlowNodeInstance sFlowNodeInstance) throws SFlowNodeModificationException { if (isNotNullOrEmptyAndDifferentOf(sFlowNodeInstance.getExecutedBySubstitute(), executerSubstituteId)) { activityInstanceService.setExecutedBySubstitute(sFlowNodeInstance, executerSubstituteId); } } private void setExecutedBy(final Long executerId, final SFlowNodeInstance sFlowNodeInstance) throws SFlowNodeModificationException { if (isNotNullOrEmptyAndDifferentOf(sFlowNodeInstance.getExecutedBy(), executerId)) { activityInstanceService.setExecutedBy(sFlowNodeInstance, executerId); } } private boolean isNotNullOrEmptyAndDifferentOf(final long executerId, final Long newExecuterId) { return newExecuterId != null && newExecuterId > 0 && executerId != newExecuterId; } @Override public void setStateByStateId(long flowNodeInstanceId, int stateId) throws SActivityStateExecutionException { final FlowNodeState state = flowNodeStateManager.getState(stateId); try { final SFlowNodeInstance sFlowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId); bpmArchiverService.archiveFlowNodeInstance(sFlowNodeInstance); activityInstanceService.setState(sFlowNodeInstance, state); if (state.isTerminal()) { if (hasChildren(sFlowNodeInstance)) { processInstanceInterruptor.interruptChildrenOfFlowNodeInstance(sFlowNodeInstance, ABORTING); } else { registerNotifyFinishWork(sFlowNodeInstance); } if (sFlowNodeInstance instanceof SActivityInstance) { flowNodeStateManager.getStateBehaviors().interruptAttachedBoundaryEvent( processDefinitionService.getProcessDefinition(sFlowNodeInstance.getProcessDefinitionId()), ((SActivityInstance) sFlowNodeInstance), ABORTING); } } } catch (final SBonitaException e) { throw new SActivityStateExecutionException(e); } } private boolean hasChildren(SFlowNodeInstance sFlowNodeInstance) { return sFlowNodeInstance.getTokenCount() > 0; } private void registerNotifyFinishWork(SFlowNodeInstance sFlowNodeInstance) throws SWorkRegisterException { workService.registerWork( workFactory.createNotifyChildFinishedWorkDescriptor(sFlowNodeInstance)); } @Override public void childFinished(final long processDefinitionId, final long parentId, SFlowNodeInstance childFlowNode) throws SFlowNodeNotFoundException, SFlowNodeReadException, SProcessDefinitionNotFoundException, SBonitaReadException, SArchivingException, SFlowNodeModificationException, SFlowNodeExecutionException, SWorkRegisterException { bpmArchiverService.archiveAndDeleteFlowNodeInstance(childFlowNode, processDefinitionId); final SActivityInstance activityInstanceParent = (SActivityInstance) activityInstanceService .getFlowNodeInstance(parentId); decrementToken(activityInstanceParent); final SProcessDefinition sProcessDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); final FlowNodeState state = flowNodeStateManager.getState(activityInstanceParent.getStateId()); final boolean shouldContinueParent = state.notifyChildFlowNodeHasFinished(sProcessDefinition, activityInstanceParent, childFlowNode); if (shouldContinueParent) { // it should never happen, because the terminal state never waits children to finish if (activityInstanceParent.isTerminal()) { LOG.warn("Parent '{}' (id={}, processInstance={}, processDefinition={}) is already terminal " + "when child '{}' (id={}) finished — this should never happen", activityInstanceParent.getName(), activityInstanceParent.getId(), childFlowNode.getRootProcessInstanceId(), processDefinitionId, childFlowNode.getName(), childFlowNode.getId()); registerNotifyFinishWork(activityInstanceParent); } else { stepForward(activityInstanceParent, null, null); } } else { LOG.debug("the child flownode {} of parent flownode {} finished, but there are other children remaining", childFlowNode, activityInstanceParent); } } private void decrementToken(final SActivityInstance sActivityInstance) throws SFlowNodeModificationException { final int tokenCount = sActivityInstance.getTokenCount() - 1; activityInstanceService.setTokenCount(sActivityInstance, tokenCount); } @Override public void childReachedState(final SProcessInstance childProcInst, final ProcessInstanceState childState, final boolean hasActionsToExecute) throws SBonitaException { if (isTerminalState(childState) && childProcInst.getCallerId() > 0) { final SActivityInstance callActivityInstance = activityInstanceService .getActivityInstance(childProcInst.getCallerId()); decrementToken(callActivityInstance); executeFlowNodeIfHasActionsToExecute(hasActionsToExecute, callActivityInstance); } } private void executeFlowNodeIfHasActionsToExecute(final boolean hasActionsToExecute, final SActivityInstance callActivityInstance) throws SWorkRegisterException { if (!hasActionsToExecute) { containerRegistry.executeFlowNode(callActivityInstance); } } private boolean isTerminalState(final ProcessInstanceState childState) { return ProcessInstanceState.COMPLETED.equals(childState) || ProcessInstanceState.CANCELLED.equals(childState) || ProcessInstanceState.ABORTED.equals(childState); } @Override public String getHandledType() { return SFlowElementsContainerType.FLOWNODE.name(); } @Override public FlowNodeState executeFlowNode(SFlowNodeInstance flowNodeInstance, Long executerId, Long executerSubstituteId) throws SFlowNodeExecutionException { return stepForward(flowNodeInstance, executerId, executerSubstituteId); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/FlowNodeIdFilter.java ================================================ /** * 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.execution; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition; /** * Created by Vincent Elcrin * Date: 17/12/13 * Time: 17:34 */ public class FlowNodeIdFilter implements Filter { private final List ids; public FlowNodeIdFilter(long id) { this.ids = Collections.singletonList(id); } public FlowNodeIdFilter(List ids) { this.ids = ids; } @Override public boolean mustSelect(SFlowNodeDefinition flowNode) { return ids.contains(flowNode.getId()); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/FlowNodeNameFilter.java ================================================ /** * 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.execution; import java.util.List; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition; /** * @author Elias Ricken de Medeiros */ public class FlowNodeNameFilter implements Filter { private final List flowNodeNames; public FlowNodeNameFilter(List flowNodeNames) { this.flowNodeNames = flowNodeNames; } @Override public boolean mustSelect(SFlowNodeDefinition element) { return flowNodeNames.contains(element.getName()); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/FlowNodeSelector.java ================================================ /** * 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.execution; import java.io.Serializable; import java.util.ArrayList; import java.util.List; 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.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition; /** * @author Elias Ricken de Medeiros */ public class FlowNodeSelector implements Serializable { private final Filter selector; private final SProcessDefinition definition; private long subProcessDefinitionId = -1; public boolean isEventSubProcess() { return subProcessDefinitionId > 0; } public FlowNodeSelector(SProcessDefinition definition, Filter filter) { this.definition = definition; this.selector = filter; } public FlowNodeSelector(SProcessDefinition definition, Filter filter, final long subProcessDefinitionId) { this(definition, filter); this.subProcessDefinitionId = subProcessDefinitionId; } public List getFilteredElements() { SFlowElementContainerDefinition container = getContainer(); ArrayList selectedFlowNodes = new ArrayList<>(); for (SFlowNodeDefinition flowNodeDefinition : container.getFlowNodes()) { if (selector.mustSelect(flowNodeDefinition)) { selectedFlowNodes.add(flowNodeDefinition); } } return selectedFlowNodes; } public SFlowElementContainerDefinition getContainer() { if (subProcessDefinitionId == -1) { return definition.getProcessContainer(); } final SSubProcessDefinition subProcDef = (SSubProcessDefinition) definition.getProcessContainer() .getFlowNode(subProcessDefinitionId); return subProcDef.getSubProcessContainer(); } public SProcessDefinition getProcessDefinition() { return definition; } public long getSubProcessDefinitionId() { return subProcessDefinitionId; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/FlowNodeStateManagerImpl.java ================================================ /** * 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.execution; import static org.bonitasoft.engine.core.process.instance.model.SStateCategory.NORMAL; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.execution.transition.FlowNodeStateSequences; import org.springframework.stereotype.Component; /** * Default implementation of the activity state manager. * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Yanyan Liu * @author Zhang Bole * @author Celine Souchet */ @Component("flowNodeStateManager") public class FlowNodeStateManagerImpl implements FlowNodeStateManager { protected final Map allStates; protected StateBehaviors stateBehaviors; private final Map flowNodeStateSequences; public FlowNodeStateManagerImpl( BPMInstancesCreator bpmInstancesCreator, StateBehaviors stateBehaviors, List flowNodeStateSequences, List allStates) { this.stateBehaviors = stateBehaviors; this.flowNodeStateSequences = flowNodeStateSequences.stream() .collect(Collectors.toMap(FlowNodeStateSequences::getFlowNodeType, e -> e)); this.allStates = allStates.stream().collect(Collectors.toMap(FlowNodeState::getId, s -> s)); bpmInstancesCreator.setStateManager(this); } @Override public StateBehaviors getStateBehaviors() { return stateBehaviors; } @Override public FlowNodeState getNextState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance, final int currentStateId) throws SActivityExecutionException { FlowNodeState currentState = getState(currentStateId); do { currentState = getNextStateToHandle(flowNodeInstance, currentState); } while (!currentState.shouldExecuteState(processDefinition, flowNodeInstance)); return currentState; } private FlowNodeState getNextStateToHandle(final SFlowNodeInstance flowNodeInstance, final FlowNodeState currentState) throws SActivityExecutionException { FlowNodeStateSequences stateSequence = this.flowNodeStateSequences.get(flowNodeInstance.getType()); SStateCategory stateCategory = flowNodeInstance.getStateCategory(); if (currentState.getStateCategory() != stateCategory) { // the state category changed (flow node was aborted or cancelled), get the first state of the corresponding state category return stateSequence.getFirstState(stateCategory); } else { FlowNodeState nextState = stateSequence.getStateAfter(stateCategory, currentState.getId()); if (nextState == null) { throw new SActivityExecutionException( "no state found after " + allStates.get(currentState.getId()).getClass().getSimpleName() + " for " + flowNodeInstance.getClass().getSimpleName() + " in state category " + flowNodeInstance.getStateCategory() + " activity id=" + flowNodeInstance.getId()); } return nextState; } } @Override public FlowNodeState getState(final int stateId) { return allStates.get(stateId); } @Override public Set getSupportedState(final SFlowNodeType nodeType) { return flowNodeStateSequences.get(nodeType).getSupportedStates(); } public FlowNodeState getFirstState(SFlowNodeType nodeType) { return flowNodeStateSequences.get(nodeType).getFirstState(NORMAL); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessExecutor.java ================================================ /** * 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.execution; import java.io.Serializable; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.connector.ConnectorDefinitionWithInputValues; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; 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.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; /** * @author Baptiste Mesta * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Hongwen Zang * @author Celine Souchet * @author Matthieu Chaffotte */ public interface ProcessExecutor extends ContainerExecutor { SProcessInstance start(long processDefinitionId, long targetSFlowNodeDefinitionId, long starterId, long starterSubstituteId, SExpressionContext expressionContext, List operations, long callerId, long subProcessDefinitionId, Map processInputs) throws SProcessInstanceCreationException, SContractViolationException; SProcessInstance start(long starterId, long starterSubstituteId, List operations, Map context, List connectorsWithInput, FlowNodeSelector selector, Map processInputs) throws SProcessInstanceCreationException, SContractViolationException; boolean registerConnectorsToExecute(SProcessDefinition processDefinition, SProcessInstance sInstance, ConnectorEvent activationEvent, FlowNodeSelector selector) throws SBonitaException; SProcessInstance startElements(final SProcessInstance sProcessInstance, FlowNodeSelector selector) throws SProcessInstanceCreationException, SFlowNodeExecutionException, SFlowNodeReadException; void handleProcessCompletion(final SProcessDefinition sProcessDefinition, final SProcessInstance sProcessInstance, final boolean hasActionsToExecute) throws SBonitaException; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessExecutorImpl.java ================================================ /** * 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.execution; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import java.io.Serializable; 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; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.bpm.connector.ConnectorDefinition; import org.bonitasoft.engine.bpm.connector.ConnectorDefinitionWithInputValues; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.contract.validation.ContractValidator; import org.bonitasoft.engine.bpm.contract.validation.ContractValidatorFactory; import org.bonitasoft.engine.bpm.document.DocumentValue; import org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.proxy.ServerProxyfier; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.classloader.SClassLoaderException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.connector.ConnectorResult; import org.bonitasoft.engine.core.connector.ConnectorService; import org.bonitasoft.engine.core.connector.exception.SConnectorException; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException; import org.bonitasoft.engine.core.contract.data.ContractDataService; import org.bonitasoft.engine.core.contract.data.SContractDataCreationException; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.api.impl.DocumentHelper; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.OperationService; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SOperation; 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.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.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.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; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.events.model.HandlerRegistrationException; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.execution.archive.BPMArchiverService; 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.work.BPMWorkFactory; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionService; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.mdc.MDCConstants; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.resources.BARResourceType; import org.bonitasoft.engine.resources.ProcessResourcesService; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.work.SWorkRegisterException; import org.bonitasoft.engine.work.WorkService; import org.slf4j.MDC; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Hongwen Zang * @author Celine Souchet */ @Slf4j public class ProcessExecutorImpl implements ProcessExecutor { protected final ActivityInstanceService activityInstanceService; protected final ProcessInstanceService processInstanceService; protected final ClassLoaderService classLoaderService; protected final ExpressionResolverService expressionResolverService; protected final ExpressionService expressionService; protected final ConnectorService connectorService; protected final BPMInstancesCreator bpmInstancesCreator; protected final EventsHandler eventsHandler; private final FlowNodeExecutor flowNodeExecutor; private final WorkService workService; private final ProcessDefinitionService processDefinitionService; private final GatewayInstanceService gatewayInstanceService; private final OperationService operationService; private final ProcessResourcesService processResourcesService; private final ConnectorInstanceService connectorInstanceService; private final TransitionEvaluator transitionEvaluator; private final ContractDataService contractDataService; private final BusinessDataRepository businessDataRepository; private final RefBusinessDataService refBusinessDataService; 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, final WorkService workService, final ProcessDefinitionService processDefinitionService, final GatewayInstanceService gatewayInstanceService, final ProcessResourcesService processResourcesService, final ConnectorService connectorService, final ConnectorInstanceService connectorInstanceService, final ClassLoaderService classLoaderService, final OperationService operationService, final ExpressionResolverService expressionResolverService, final ExpressionService expressionService, final EventService eventService, final Map> handlers, final DocumentService documentService, final ContainerRegistry containerRegistry, final BPMInstancesCreator bpmInstancesCreator, final EventsHandler eventsHandler, final BusinessDataRepository businessDataRepository, final RefBusinessDataService refBusinessDataService, final TransitionEvaluator transitionEvaluator, final ContractDataService contractDataService, BPMWorkFactory workFactory, BPMArchiverService bpmArchiverService, final ProcessStarterVerifier processStarterVerifier) { super(); this.activityInstanceService = activityInstanceService; this.processInstanceService = processInstanceService; this.processResourcesService = processResourcesService; this.connectorInstanceService = connectorInstanceService; this.flowNodeExecutor = flowNodeExecutor; this.workService = workService; this.processDefinitionService = processDefinitionService; this.gatewayInstanceService = gatewayInstanceService; this.connectorService = connectorService; this.classLoaderService = classLoaderService; this.operationService = operationService; this.expressionResolverService = expressionResolverService; this.expressionService = expressionService; this.bpmInstancesCreator = bpmInstancesCreator; this.eventsHandler = eventsHandler; this.transitionEvaluator = transitionEvaluator; this.businessDataRepository = businessDataRepository; this.refBusinessDataService = refBusinessDataService; 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); for (final Entry> handler : handlers.entrySet()) { try { eventService.addHandler(handler.getKey(), handler.getValue()); } catch (final HandlerRegistrationException e) { log.warn(e.getMessage()); log.debug("", e); } } containerRegistry.addContainerExecutor(this); } @Override public FlowNodeState executeFlowNode(SFlowNodeInstance flowNodeInstance, Long executerId, Long executerSubstituteId) throws SFlowNodeExecutionException { return flowNodeExecutor.stepForward(flowNodeInstance, executerId, executerSubstituteId); } private SConnectorInstance getNextConnectorInstance(final SProcessInstance processInstance, final ConnectorEvent event) throws SConnectorInstanceReadException { final List connectorInstances = connectorInstanceService.getConnectorInstances( processInstance.getId(), SConnectorInstance.PROCESS_TYPE, event, 0, 1, ConnectorService.TO_BE_EXECUTED); return connectorInstances.size() == 1 ? connectorInstances.get(0) : null; } @Override public boolean registerConnectorsToExecute(final SProcessDefinition processDefinition, final SProcessInstance sProcessInstance, final ConnectorEvent activationEvent, final FlowNodeSelector selectorForConnectorOnEnter) throws SBonitaException { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final long processDefinitionId = processDefinition.getId(); final List connectors = processContainer.getConnectors(activationEvent); if (connectors.size() > 0) { SConnectorInstance nextConnectorInstance; nextConnectorInstance = getNextConnectorInstance(sProcessInstance, activationEvent); if (nextConnectorInstance != null) { // TODO: extract this search algorithm in a dedicated method: for (final SConnectorDefinition sConnectorDefinition : connectors) { if (sConnectorDefinition.getName().equals(nextConnectorInstance.getName())) { workService.registerWork(workFactory.createExecuteConnectorOfProcessDescriptor( processDefinitionId, sProcessInstance.getId(), sProcessInstance.getRootProcessInstanceId(), nextConnectorInstance.getId(), sConnectorDefinition.getConnectorId(), sConnectorDefinition.getName(), activationEvent, selectorForConnectorOnEnter)); return true; } } } } return false; } private List initializeFirstExecutableElements(final SProcessInstance sProcessInstance, final FlowNodeSelector selector) { try { final List flownNodeDefinitions = selector.getFilteredElements(); long rootProcessInstanceId = sProcessInstance.getRootProcessInstanceId(); if (rootProcessInstanceId <= 0) { rootProcessInstanceId = sProcessInstance.getId(); } return bpmInstancesCreator.createFlowNodeInstances(selector.getProcessDefinition().getId(), rootProcessInstanceId, sProcessInstance.getId(), flownNodeDefinitions, rootProcessInstanceId, sProcessInstance.getId(), SStateCategory.NORMAL); } catch (final SBonitaException e) { setExceptionContext(selector.getProcessDefinition(), sProcessInstance, e); log.error("", e); throw new BonitaRuntimeException(e); } } private SProcessInstance createProcessInstance(final SProcessDefinition sDefinition, final long starterId, final long starterSubstituteId, final long callerId, final SFlowNodeType callerType, final long rootProcessInstanceId) throws SProcessInstanceCreationException { SProcessInstance sProcessInstance = SProcessInstance.builder().name(sDefinition.getName()) .processDefinitionId(sDefinition.getId()).description(sDefinition.getDescription()) .startedBy(starterId).startedBySubstitute(starterSubstituteId).callerId(callerId).callerType(callerType) .rootProcessInstanceId(rootProcessInstanceId).build(); processInstanceService.createProcessInstance(sProcessInstance); // Context will be auto clear by parent method (call hierarchy is always using ProcessInstanceMDC) MDC.put(MDCConstants.PROCESS_INSTANCE_ID, String.valueOf(sProcessInstance.getId())); MDC.put(MDCConstants.ROOT_PROCESS_INSTANCE_ID, String.valueOf(sProcessInstance.getRootProcessInstanceId())); return sProcessInstance; } protected SProcessInstance createProcessInstance(final SProcessDefinition processDefinition, final long starterId, final long starterSubstituteId, final long callerId) throws SProcessInstanceCreationException { SActivityInstance callerInstance; try { callerInstance = getCaller(callerId); } catch (final SBonitaException e) { throw new SProcessInstanceCreationException("Unable to get caller.", e); } if (callerInstance != null) { return createProcessInstance(processDefinition, starterId, starterSubstituteId, callerId, callerInstance.getType(), callerInstance.getRootContainerId()); } return createProcessInstance(processDefinition, starterId, starterSubstituteId, callerId, null, -1); } private SActivityInstance getCaller(final long callerId) throws SActivityReadException, SActivityInstanceNotFoundException { if (callerId > 0) { return activityInstanceService.getActivityInstance(callerId); } return null; } /* * this method is called when a flow node having a transition that goes to a gateway is finished * it get the active gateway pointed by this transition, update the tokens of this gateway and execute it if merged */ private void executeGateway(final SProcessDefinition sProcessDefinition, final STransitionDefinition sTransitionDefinition, final SFlowNodeInstance flowNodeThatTriggeredTheTransition) throws SBonitaException { final long parentProcessInstanceId = flowNodeThatTriggeredTheTransition.getParentProcessInstanceId(); final long rootProcessInstanceId = flowNodeThatTriggeredTheTransition.getRootProcessInstanceId(); final SFlowNodeDefinition sFlowNodeDefinition = processDefinitionService.getNextFlowNode(sProcessDefinition, String.valueOf(sTransitionDefinition.getId())); try { List gatewaysToExecute = new ArrayList<>(1); final SProcessInstance parentProcessInstance = processInstanceService .getProcessInstance(parentProcessInstanceId); final SStateCategory stateCategory = parentProcessInstance.getStateCategory(); final SGatewayInstance gatewayInstance = getActiveGatewayOrCreateIt(sProcessDefinition, sFlowNodeDefinition, stateCategory, parentProcessInstanceId, rootProcessInstanceId); gatewayInstanceService.hitTransition(gatewayInstance, sFlowNodeDefinition.getTransitionIndex(sTransitionDefinition.getId())); if (gatewayInstanceService.checkMergingCondition(sProcessDefinition, gatewayInstance)) { gatewaysToExecute.add(gatewayInstance); gatewaysToExecute.addAll(gatewayInstanceService .setFinishAndCreateNewGatewayForRemainingToken(sProcessDefinition, gatewayInstance)); } for (final SGatewayInstance gatewayToExecute : gatewaysToExecute) { registerExecuteFlowNodeWork(gatewayToExecute); } } catch (final SBonitaException e) { setExceptionContext(sProcessDefinition, flowNodeThatTriggeredTheTransition, e); log.error("", e); throw e; } } /* * try to gate active gateway. * if the gateway is already hit by this transition or by the same token, we create a new gateway */ SGatewayInstance getActiveGatewayOrCreateIt(final SProcessDefinition sProcessDefinition, final SFlowNodeDefinition flowNodeDefinition, final SStateCategory stateCategory, final long parentProcessInstanceId, final long rootProcessInstanceId) throws SBonitaException { SGatewayInstance gatewayInstance = gatewayInstanceService .getActiveGatewayInstanceOfTheProcess(parentProcessInstanceId, flowNodeDefinition.getName()); if (gatewayInstance == null) { // no gateway found we create one gatewayInstance = createGateway(sProcessDefinition.getId(), flowNodeDefinition, stateCategory, parentProcessInstanceId, rootProcessInstanceId); } return gatewayInstance; } private SGatewayInstance createGateway(final Long processDefinitionId, final SFlowNodeDefinition flowNodeDefinition, final SStateCategory stateCategory, final long parentProcessInstanceId, final long rootProcessInstanceId) throws SBonitaException { return (SGatewayInstance) bpmInstancesCreator .createFlowNodeInstance(processDefinitionId, rootProcessInstanceId, parentProcessInstanceId, SFlowElementsContainerType.PROCESS, flowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId, false, 0, stateCategory, -1); } protected void executeOperations(final List operations, final Map context, SExpressionContext expressionContext, final SExpressionContext expressionContextToEvaluateOperations, final SProcessInstance sProcessInstance) throws SBonitaException { if (operations != null && !operations.isEmpty()) { SExpressionContext currentExpressionContext = expressionContextToEvaluateOperations != null ? expressionContextToEvaluateOperations : expressionContext; currentExpressionContext.setInputValues(context); if (currentExpressionContext.getContainerId() == null) { currentExpressionContext.setContainerId(sProcessInstance.getId()); currentExpressionContext.setContainerType(DataInstanceContainer.PROCESS_INSTANCE.name()); } operationService.execute(new ArrayList<>(operations), sProcessInstance.getId(), DataInstanceContainer.PROCESS_INSTANCE.name(), currentExpressionContext); } } protected boolean initialize(final long userId, final SProcessDefinition sProcessDefinition, final SProcessInstance sProcessInstance, SExpressionContext expressionContextToEvaluateOperations, List operations, final Map context, final SFlowElementContainerDefinition processContainer, final List connectors, final FlowNodeSelector selectorForConnectorOnEnter, final Map processInputs) throws SBonitaException { SExpressionContext expressionContext = createExpressionsContextForProcessInstance(sProcessDefinition, sProcessInstance); operations = operations != null ? new ArrayList<>(operations) : Collections.emptyList(); try { storeProcessInstantiationInputs(sProcessInstance.getId(), processInputs); // Create SDataInstances bpmInstancesCreator.createDataInstances(sProcessInstance, processContainer, sProcessDefinition, expressionContext, operations, context, expressionContextToEvaluateOperations); initializeBusinessData(processContainer, sProcessInstance, expressionContext); initializeStringIndexes(sProcessInstance, sProcessDefinition, processContainer); createDocuments(sProcessDefinition, processContainer, sProcessInstance, userId, expressionContext, context); createDocumentLists(processContainer, sProcessInstance, userId, expressionContext, context); } catch (SBonitaException e) { e.setScope(ScopedException.DATA); throw e; } if (connectors != null) { //these are set only when start process through the command ExecuteActionsAndStartInstanceExt try { executeConnectors(sProcessDefinition, sProcessInstance, connectors); } catch (SBonitaException e) { e.setScope(ScopedException.CONNECTOR); throw e; } } // operations given to the startProcess method of the API or by command, not operations of the process definition try { executeOperations(operations, context, expressionContext, expressionContextToEvaluateOperations, sProcessInstance); } catch (SBonitaException e) { // Data mapping of call activity e.setScope(ScopedException.DATA); throw e; } // Create connectors bpmInstancesCreator.createConnectorInstances(sProcessInstance, processContainer.getConnectors(), SConnectorInstance.PROCESS_TYPE); return registerConnectorsToExecute(sProcessDefinition, sProcessInstance, ConnectorEvent.ON_ENTER, selectorForConnectorOnEnter); } private SExpressionContext createExpressionsContextForProcessInstance(SProcessDefinition sProcessDefinition, SProcessInstance sProcessInstance) { SExpressionContext expressionContext = new SExpressionContext(); expressionContext.setProcessDefinitionId(sProcessDefinition.getId()); expressionContext.setContainerId(sProcessInstance.getId()); expressionContext.setContainerType(DataInstanceContainer.PROCESS_INSTANCE.name()); return expressionContext; } private void storeProcessInstantiationInputs(final long processInstanceId, final Map processInputs) throws SContractDataCreationException { contractDataService.addProcessData(processInstanceId, processInputs); } protected void initializeBusinessData(SFlowElementContainerDefinition processContainer, SProcessInstance sInstance, SExpressionContext expressionContext) throws SBonitaException { final List businessDataDefinitions = processContainer.getBusinessDataDefinitions(); for (final SBusinessDataDefinition bdd : businessDataDefinitions) { final SExpression expression = bdd.getDefaultValueExpression(); if (bdd.isMultiple()) { final List dataIds = initializeMultipleBusinessDataIds(expressionContext, expression); final SRefBusinessDataInstanceBuilderFactory instanceFactory = BuilderFactory .get(SRefBusinessDataInstanceBuilderFactory.class); final SRefBusinessDataInstance instance = instanceFactory .createNewInstance(bdd.getName(), sInstance.getId(), dataIds, bdd.getClassName()) .done(); refBusinessDataService.addRefBusinessDataInstance(instance); } else { final Long primaryKey = initializeSingleBusinessData(expressionContext, expression); final SRefBusinessDataInstanceBuilderFactory instanceFactory = BuilderFactory .get(SRefBusinessDataInstanceBuilderFactory.class); final SRefBusinessDataInstance instance = instanceFactory .createNewInstance(bdd.getName(), sInstance.getId(), primaryKey, bdd.getClassName()) .done(); refBusinessDataService.addRefBusinessDataInstance(instance); } } } private Long initializeSingleBusinessData(final SExpressionContext expressionContext, final SExpression expression) throws SBonitaException { Long primaryKey = null; if (expression != null) { final Entity businessData = (Entity) expressionResolverService.evaluate(expression, expressionContext); primaryKey = saveBusinessData(businessData); } return primaryKey; } private List initializeMultipleBusinessDataIds(final SExpressionContext expressionContext, final SExpression expression) throws SBonitaException { final List dataIds = new ArrayList<>(); if (expression != null) { final List businessData = (List) expressionResolverService.evaluate(expression, expressionContext); if (businessData != null) { for (final Entity entity : businessData) { dataIds.add(saveBusinessData(entity)); } } } return dataIds; } private Long saveBusinessData(final Entity entity) throws SObjectCreationException { try { final Entity mergedBusinessData = businessDataRepository.merge(ServerProxyfier.unProxifyIfNeeded(entity)); if (mergedBusinessData == null) { return null; } return mergedBusinessData.getPersistenceId(); } catch (IllegalArgumentException e) { throw new SObjectCreationException("Unable to save the business data", e); } } private void createDocuments(final SProcessDefinition sDefinition, SFlowElementContainerDefinition processContainer, final SProcessInstance sProcessInstance, final long authorId, final SExpressionContext expressionContext, final Map context) throws SObjectCreationException, SBonitaReadException, SObjectModificationException, SExpressionTypeUnknownException, SExpressionDependencyMissingException, SExpressionEvaluationException, SInvalidExpressionException, SOperationExecutionException { final List documentDefinitions = processContainer.getDocumentDefinitions(); final Map evaluatedDocumentValues = evaluateInitialExpressionsOfDocument( sProcessInstance, expressionContext, context, documentDefinitions); if (!documentDefinitions.isEmpty()) { for (final SDocumentDefinition document : documentDefinitions) { final DocumentValue documentValue = getInitialDocumentValue(sDefinition, evaluatedDocumentValues, document); if (documentValue != null) { documentHelper.createOrUpdateDocument(documentValue, document.getName(), sProcessInstance.getId(), authorId, document.getDescription()); } } } } protected DocumentValue getInitialDocumentValue(final SProcessDefinition sDefinition, final Map evaluatedDocumentValues, final SDocumentDefinition document) throws SBonitaReadException { DocumentValue documentValue = null; if (document.getInitialValue() != null) { documentValue = evaluatedDocumentValues.get(document.getInitialValue()); } else if (document.getFile() != null) { final byte[] content = getProcessDocumentContent(sDefinition, document); documentValue = new DocumentValue(content, document.getMimeType(), document.getFileName()); } else if (document.getUrl() != null) { documentValue = new DocumentValue(document.getUrl()); documentValue.setFileName(document.getFileName()); documentValue.setMimeType(document.getMimeType()); } return documentValue; } byte[] getProcessDocumentContent(final SProcessDefinition sDefinition, final SDocumentDefinition document) throws SBonitaReadException { final String file = document.getFile();// should always exists...validation on BusinessArchive return processResourcesService.get(sDefinition.getId(), BARResourceType.DOCUMENT, file).getContent(); } private Map evaluateInitialExpressionsOfDocument(final SProcessInstance processInstance, final SExpressionContext expressionContext, final Map context, final List documentDefinitions) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException, SOperationExecutionException { final List initialValuesExpressions = new ArrayList<>(documentDefinitions.size()); final Map evaluatedDocumentValue = new HashMap<>(); for (final SDocumentDefinition documentDefinition : documentDefinitions) { if (documentDefinition.getInitialValue() != null) { initialValuesExpressions.add(documentDefinition.getInitialValue()); } } final List evaluate = expressionResolverService.evaluate(initialValuesExpressions, getsExpressionContext(processInstance, expressionContext, context)); for (int i = 0; i < initialValuesExpressions.size(); i++) { evaluatedDocumentValue.put(initialValuesExpressions.get(i), documentHelper.toCheckedDocumentValue(evaluate.get(i))); } return evaluatedDocumentValue; } private void createDocumentLists(SFlowElementContainerDefinition processContainer, final SProcessInstance processInstance, final long authorId, final SExpressionContext expressionContext, final Map context) throws SBonitaException { final List documentListDefinitions = processContainer.getDocumentListDefinitions(); if (!documentListDefinitions.isEmpty()) { final List initialValues = evaluateInitialExpressionsOfDocumentLists(processInstance, expressionContext, context, documentListDefinitions); for (int i = 0; i < documentListDefinitions.size(); i++) { final Object newValue = initialValues.get(i); if (newValue == null) { continue; } documentHelper.setDocumentList( documentHelper.toCheckedList(newValue), documentListDefinitions.get(i).getName(), processInstance.getId(), authorId); } } } private List evaluateInitialExpressionsOfDocumentLists(final SProcessInstance processInstance, final SExpressionContext expressionContext, final Map context, final List documentListDefinitions) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { final List initialValuesExpressions = new ArrayList<>(documentListDefinitions.size()); for (final SDocumentListDefinition documentList : documentListDefinitions) { initialValuesExpressions.add(documentList.getExpression()); } final SExpressionContext currentExpressionContext = getsExpressionContext(processInstance, expressionContext, context); return expressionResolverService.evaluate(initialValuesExpressions, currentExpressionContext); } private SExpressionContext getsExpressionContext(final SProcessInstance processInstance, final SExpressionContext expressionContext, final Map context) { SExpressionContext currentExpressionContext; if (expressionContext != null) { expressionContext.setInputValues(context); currentExpressionContext = expressionContext; } else { currentExpressionContext = new SExpressionContext(processInstance.getId(), DataInstanceContainer.PROCESS_INSTANCE.name(), processInstance.getProcessDefinitionId()); currentExpressionContext.setInputValues(context); } return currentExpressionContext; } @Override public void childFinished(long processDefinitionId, long parentId, SFlowNodeInstance childFlowNode) throws SBonitaException { final SProcessDefinition sProcessDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); final long processInstanceId = childFlowNode.getParentProcessInstanceId(); SProcessInstance sProcessInstance = processInstanceService.getProcessInstance(processInstanceId); // this also deletes the event (unless the process was interrupted by event) final boolean wasTheLastFlowNodeToExecute = executeValidOutgoingTransitionsAndUpdateTokens(sProcessDefinition, childFlowNode, sProcessInstance); log.debug("The flow node <{}> with id<{}> of process instance <{}> finished", childFlowNode.getName(), childFlowNode.getId(), processInstanceId); if (wasTheLastFlowNodeToExecute) { // flow node has finished, now we want to log only process information in the context MDC.remove(MDCConstants.FLOW_NODE_INSTANCE_ID); int numberOfFlowNode = activityInstanceService.getNumberOfFlowNodes(sProcessInstance.getId()); if (sProcessInstance.getInterruptingEventId() > 0) { //if it's interrupted by an event (error event), the flow node is kept to be executed last and deleted in triggerErrorEvents() numberOfFlowNode -= 1; } if (numberOfFlowNode > 0) { if (log.isDebugEnabled()) { log.debug("The process instance <{}> from definition <{}:{}> executed a branch " + "that is finished but there is still <{}> to execute", processInstanceId, sProcessDefinition.getName(), sProcessDefinition.getVersion(), numberOfFlowNode); log.debug(activityInstanceService.getDirectChildrenOfProcessInstance(processInstanceId, 0, numberOfFlowNode).toString()); } return; } log.debug("The process instance <{}> from definition <{}:{}> finished", processInstanceId, sProcessDefinition.getName(), sProcessDefinition.getVersion()); boolean hasTriggeredErrorEvents = false; // in case of interruption by error event: // * the first time the last element (except the error event itself) goes here, it put the process in aborting // * the error event in thrown // * the catching flow node is executed IN THE SAME THREAD (I don't know why...) // * OR the catching event sub process is executed IN THE SAME THREAD and the process having this event sub process is interrupted (not locked!) // * the throw error event is deleted // * the waiting error event is deleted // * the process is put in: // * ABORTING: if the process was in state category ABORTING (I don't know why...) // I'm not sure this case really happens because this would require that a last flow node trigger this method again and it's never the case // * COMPLETED: in 'normal' case // In that case if the process is called by a call activity, the calling activity have its token count decremented but is not executed (hasTriggeredErrorEvents==true) // if (ProcessInstanceState.ABORTING.getId() != sProcessInstance.getStateId()) { if (sProcessInstance.getStateCategory() != SStateCategory.CANCELLING && sProcessInstance.hasBeenInterruptedByEvent()) { // trigger error events only if process instance has been aborted by an event // and no-one cancelled the process instance in the meantime: hasTriggeredErrorEvents = triggerErrorEvents(sProcessDefinition, sProcessInstance, childFlowNode); } // the process instance has maybe changed log.debug("has action to execute"); if (hasTriggeredErrorEvents) { sProcessInstance = processInstanceService.getProcessInstance(processInstanceId); } eventsHandler.unregisterEventSubProcess(sProcessDefinition, sProcessInstance); } handleProcessCompletion(sProcessDefinition, sProcessInstance, hasTriggeredErrorEvents); } } @Override public void handleProcessCompletion(final SProcessDefinition sProcessDefinition, final SProcessInstance sProcessInstance, final boolean hasActionsToExecute) throws SBonitaException { ProcessInstanceState processInstanceState; switch (sProcessInstance.getStateCategory()) { case ABORTING: if (ProcessInstanceState.ABORTING.getId() == sProcessInstance.getStateId()) { processInstanceState = ProcessInstanceState.ABORTED; } else { if (hasActionsToExecute) { processInstanceState = ProcessInstanceState.ABORTING; } else { processInstanceState = ProcessInstanceState.ABORTED; } } break; case CANCELLING: processInstanceState = ProcessInstanceState.CANCELLED; break; default: if (ProcessInstanceState.COMPLETING.getId() == sProcessInstance.getStateId()) { processInstanceState = ProcessInstanceState.COMPLETED; } else { if (registerConnectorsToExecute(sProcessDefinition, sProcessInstance, ConnectorEvent.ON_FINISH, null)) { // some connectors were trigger processInstanceState = ProcessInstanceState.COMPLETING; } else { processInstanceState = ProcessInstanceState.COMPLETED; } } break; } processInstanceService.setState(sProcessInstance, processInstanceState); flowNodeExecutor.childReachedState(sProcessInstance, processInstanceState, hasActionsToExecute); } private boolean triggerErrorEvents(final SProcessDefinition sProcessDefinition, final SProcessInstance sProcessInstance, final SFlowNodeInstance child) throws SBonitaException { final SFlowNodeInstance endEventInstance = activityInstanceService .getFlowNodeInstance(sProcessInstance.getInterruptingEventId()); final SEndEventDefinition endEventDefinition = (SEndEventDefinition) sProcessDefinition .getProcessContainer().getFlowNode( endEventInstance.getFlowNodeDefinitionId()); boolean hasTriggeredErrorEvents = eventsHandler.handlePostThrowEvent(sProcessDefinition, endEventDefinition, (SThrowEventInstance) endEventInstance, child); bpmArchiverService.archiveAndDeleteFlowNodeInstance(endEventInstance, sProcessDefinition.getId()); return hasTriggeredErrorEvents; } /** * Evaluate the split of the element * The element contains the current token it received * * @return number of token of the process */ private boolean executeValidOutgoingTransitionsAndUpdateTokens(final SProcessDefinition processDefinition, final SFlowNodeInstance child, final SProcessInstance sProcessInstance) throws SBonitaException { // token we merged final SFlowNodeDefinition sFlowNodeDefinition = processDefinition.getProcessContainer() .getFlowNode(child.getFlowNodeDefinitionId()); final FlowNodeTransitionsWrapper transitionsDescriptor = transitionEvaluator .buildTransitionsWrapper(sFlowNodeDefinition, processDefinition, child); List chosenGatewaysTransitions = new ArrayList<>(transitionsDescriptor .getValidOutgoingTransitionDefinitions().size()); final List chosenFlowNode = new ArrayList<>( transitionsDescriptor.getValidOutgoingTransitionDefinitions().size()); for (final STransitionDefinition sTransitionDefinition : transitionsDescriptor .getValidOutgoingTransitionDefinitions()) { final SFlowNodeDefinition flowNodeDefinition = processDefinitionService.getNextFlowNode(processDefinition, String.valueOf(sTransitionDefinition.getId())); if (flowNodeDefinition instanceof SGatewayDefinition) { chosenGatewaysTransitions.add(sTransitionDefinition); } else { // Shortcut: event or activity, we execute them directly chosenFlowNode.add(flowNodeDefinition); } } archiveFlowNodeInstance(processDefinition, child, sProcessInstance); final long processInstanceId = sProcessInstance.getId(); createAndExecuteActivities(processDefinition.getId(), child, processInstanceId, chosenFlowNode, child.getRootProcessInstanceId()); //test to check the particular case of an inclusive gateway receiving transitions from the same flownode. //if that's the case only one should be executed. removeDuplicatedInclusiveGatewayTransitions(processDefinition, chosenGatewaysTransitions); // execute transition/activities for (final STransitionDefinition sTransitionDefinition : chosenGatewaysTransitions) { executeGateway(processDefinition, sTransitionDefinition, child); } if (processDefinition.getProcessContainer().containsInclusiveGateway() && needToReevaluateInclusiveGateways(transitionsDescriptor)) { reevaluateGateways(processDefinition, processInstanceId); } return transitionsDescriptor.isLastFlowNode(); } private void reevaluateGateways(SProcessDefinition processDefinition, long processInstanceId) throws SBonitaException { log.debug("some branches died, will check again all inclusive gateways"); final List inclusiveGatewaysOfProcessInstance = gatewayInstanceService .getInclusiveGatewaysOfProcessInstanceThatShouldFire( processDefinition, processInstanceId); List gatewaysToExecute = new ArrayList<>(inclusiveGatewaysOfProcessInstance); for (final SGatewayInstance gatewayInstance : inclusiveGatewaysOfProcessInstance) { gatewaysToExecute .addAll(gatewayInstanceService.setFinishAndCreateNewGatewayForRemainingToken(processDefinition, gatewayInstance)); } for (final SGatewayInstance gatewayToExecute : gatewaysToExecute) { registerExecuteFlowNodeWork(gatewayToExecute); } } protected void removeDuplicatedInclusiveGatewayTransitions(SProcessDefinition processDefinition, List chosenGatewaysTransitions) { List transitionToRemove = new ArrayList<>(); Set gateways = new HashSet<>(); for (STransitionDefinition gatewaysTransition : chosenGatewaysTransitions) { SGatewayDefinition gateway = getGateway(gatewaysTransition, processDefinition); if (isInclusiveGateway(gateway)) { boolean alreadyExists = !gateways.add(gateway); if (alreadyExists) { transitionToRemove.add(gatewaysTransition); } } } chosenGatewaysTransitions.removeAll(transitionToRemove); } private boolean isInclusiveGateway(SGatewayDefinition gateway) { return gateway.getGatewayType() == SGatewayType.INCLUSIVE; } private SGatewayDefinition getGateway(STransitionDefinition gatewaysTransition, SProcessDefinition processDefinition) { return (SGatewayDefinition) processDefinition.getProcessContainer().getFlowNode(gatewaysTransition.getTarget()); } private void archiveFlowNodeInstance(final SProcessDefinition sProcessDefinition, final SFlowNodeInstance child, final SProcessInstance sProcessInstance) throws SArchivingException { //FIXME we archive the flow node instance here because it was not archived before because the flow node was interrupting the parent.. we should change that because it's not very easy to see how it works // * the flow node is archived only if its not the error event that triggered the interruption (unless if its in a sub process????) if (child.getId() != sProcessInstance.getInterruptingEventId() || SFlowNodeType.SUB_PROCESS.equals(sProcessInstance.getCallerType())) { // Let's archive the final state of the child: bpmArchiverService.archiveAndDeleteFlowNodeInstance(child, sProcessDefinition.getId()); } } private boolean needToReevaluateInclusiveGateways(final FlowNodeTransitionsWrapper transitionsDescriptor) { final int allOutgoingTransitions = transitionsDescriptor.getNonDefaultOutgoingTransitionDefinitions().size() + (transitionsDescriptor.getDefaultTransition() != null ? 1 : 0); final int takenTransition = transitionsDescriptor.getValidOutgoingTransitionDefinitions().size(); /* * Why this condition? * If a gateway was blocked because it was waiting for a token to come it will not be unblock when all * transitions * are taken but only if some transitions are not. * In conclusion if all declared transition are not taken it means that a 'branch died' and that some inclusive * gateways might be triggered so the * reevaluation is needed */ return takenTransition < allOutgoingTransitions; } @Override public SProcessInstance start(final long starterId, final long starterSubstituteId, final List operations, final Map context, final List connectorsWithInput, final FlowNodeSelector selector, final Map processInputs) throws SProcessInstanceCreationException, SContractViolationException { return start(starterId, starterSubstituteId, null, operations, context, connectorsWithInput, -1, selector, processInputs); } @Override /* Started by call activity and events, operations must be evaluated using the given context */ public SProcessInstance start(final long processDefinitionId, final long targetSFlowNodeDefinitionId, final long starterId, final long starterSubstituteId, final SExpressionContext expressionContextToEvaluateOperations, final List operations, final long callerId, final long subProcessDefinitionId, final Map processInputs) throws SProcessInstanceCreationException, SContractViolationException { try { final SProcessDefinition sProcessDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); final FlowNodeSelector selector = new FlowNodeSelector(sProcessDefinition, getFilter(targetSFlowNodeDefinitionId), subProcessDefinitionId); return start(starterId, starterSubstituteId, expressionContextToEvaluateOperations, operations, null, null, callerId, selector, processInputs); } catch (final SProcessDefinitionNotFoundException | SBonitaReadException e) { throw new SProcessInstanceCreationException(e); } } private Filter getFilter(final long targetSFlowNodeDefinitionId) { if (targetSFlowNodeDefinitionId == -1) { return new StartFlowNodeFilter(); } return new FlowNodeIdFilter(targetSFlowNodeDefinitionId); } protected void initializeStringIndexes(final SProcessInstance sInstance, SProcessDefinition sProcessDefinition, final SFlowElementContainerDefinition processContainer) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException, SProcessInstanceModificationException { if (!sProcessDefinition.getProcessContainer().equals(processContainer)) { //we are not instantiating the process, we are starting an event subprocess return; } final SExpressionContext contextDependency = new SExpressionContext(sInstance.getId(), DataInstanceContainer.PROCESS_INSTANCE.name(), sProcessDefinition.getId()); boolean update = false; EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor(); for (int i = 1; i <= 5; i++) { final SExpression value = sProcessDefinition.getStringIndexValue(i); if (value != null) { update = true; entityUpdateDescriptor.addField(SProcessInstance.STRING_INDEX_KEY + i, String.valueOf(expressionResolverService.evaluate(value, contextDependency))); } } if (update) { processInstanceService.updateProcess(sInstance, entityUpdateDescriptor); } } protected SProcessInstance start(final long starterId, final long starterSubstituteId, final SExpressionContext expressionContextToEvaluateOperations, final List operations, final Map context, final List connectors, final long callerId, final FlowNodeSelector selector, final Map processInputs) throws SProcessInstanceCreationException, SContractViolationException { final SProcessDefinition sProcessDefinition = selector.getProcessDefinition(); // Validate start process contract inputs: if (!selector.isEventSubProcess()) { validateContractInputs(processInputs, sProcessDefinition); } final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { // so that event sub-process can trigger even if containing process definition is disabled: if (!selector.isEventSubProcess()) { ensureProcessIsEnabled(sProcessDefinition); } setProcessClassloader(sProcessDefinition); final SProcessInstance sProcessInstance = createProcessInstance(sProcessDefinition, starterId, starterSubstituteId, callerId); processStarterVerifier.verify(sProcessInstance); final boolean isInitializing = initialize(starterId, sProcessDefinition, sProcessInstance, expressionContextToEvaluateOperations, operations, context, selector.getContainer(), connectors, selector, processInputs); handleEventSubProcess(sProcessDefinition, sProcessInstance, selector.getSubProcessDefinitionId()); if (isInitializing) { // some connectors were trigger processInstanceService.setState(sProcessInstance, ProcessInstanceState.INITIALIZING); // we stop execution here return sProcessInstance; } return startElements(sProcessInstance, selector); } catch (final SProcessInstanceCreationException e) { throw e; } catch (final SBonitaException e) { throw new SProcessInstanceCreationException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } private void ensureProcessIsEnabled(final SProcessDefinition sProcessDefinition) throws SBonitaReadException, SProcessDefinitionException { final SProcessDefinitionDeployInfo deployInfo = processDefinitionService .getProcessDeploymentInfo(sProcessDefinition.getId()); if (ActivationState.DISABLED.name().equals(deployInfo.getActivationState())) { throw new SProcessDefinitionException( "The process " + deployInfo.getName() + " " + deployInfo.getVersion() + " is not enabled.", deployInfo.getProcessId(), deployInfo.getName(), deployInfo.getVersion()); } } private void setProcessClassloader(SProcessDefinition sProcessDefinition) throws SClassLoaderException { final ClassLoader localClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, sProcessDefinition.getId())); Thread.currentThread().setContextClassLoader(localClassLoader); // initialize the process classloader by getting it one time try { localClassLoader.loadClass(this.getClass().getName()); } catch (final ClassNotFoundException e) { // ignore, just to load } } protected void validateContractInputs(final Map processInputs, final SProcessDefinition sProcessDefinition) throws SContractViolationException { final SContractDefinition contractDefinition = sProcessDefinition.getContract(); if (contractDefinition != null) { final ContractValidator validator = new ContractValidatorFactory() .createContractValidator(expressionService); validator.validate(sProcessDefinition.getId(), contractDefinition, processInputs); } } /* * Execute connectors then execute output operation and disconnect connectors */ protected void executeConnectors(final SProcessDefinition processDefinition, final SProcessInstance sProcessInstance, final List connectorsList) throws SConnectorException { final SExpressionContext expcontext = new SExpressionContext(); expcontext.setProcessDefinitionId(processDefinition.getId()); expcontext.setProcessDefinition(processDefinition); expcontext.setContainerId(sProcessInstance.getId()); expcontext.setContainerType(DataInstanceContainer.PROCESS_INSTANCE.name()); for (final ConnectorDefinitionWithInputValues connectorWithInput : connectorsList) { final ConnectorDefinition connectorDefinition = connectorWithInput.getConnectorDefinition(); final Map> contextInputValues = connectorWithInput.getInputValues(); final String connectorId = connectorDefinition.getConnectorId(); final String version = connectorDefinition.getVersion(); final Map inputs = connectorDefinition.getInputs(); if (contextInputValues.size() != inputs.size()) { throw new SConnectorException("Invalid number of input parameters (expected " + inputs.size() + ", got " + contextInputValues.size() + ")"); } final Map connectorsExps = ModelConvertor.constructExpressions(inputs); // we use the context classloader because the process classloader is already set final ConnectorResult result = connectorService.executeMultipleEvaluation(processDefinition.getId(), connectorId, version, connectorsExps, contextInputValues, Thread.currentThread().getContextClassLoader(), expcontext); final List outputs = connectorDefinition.getOutputs(); connectorService.executeOutputOperation(ModelConvertor.convertOperations(outputs), expcontext, result); } } protected void handleEventSubProcess(final SProcessDefinition sProcessDefinition, final SProcessInstance sProcessInstance, final long subProcessDefinitionId) throws SBonitaException { if (subProcessDefinitionId == -1) { // modify that to support event sub-processes within sub-processes try { eventsHandler.handleEventSubProcess(sProcessDefinition, sProcessInstance); } catch (final SProcessInstanceCreationException e) { throw e; } catch (final SBonitaException e) { setExceptionContext(sProcessDefinition, sProcessInstance, e); throw new SProcessInstanceCreationException( "Unable to register events for event sub process in process.", e); } } } @Override public SProcessInstance startElements(final SProcessInstance sProcessInstance, final FlowNodeSelector selector) throws SProcessInstanceCreationException, SFlowNodeExecutionException { try { contractDataService.archiveAndDeleteProcessData(sProcessInstance.getId(), System.currentTimeMillis()); } catch (final SObjectModificationException e) { throw new SProcessInstanceCreationException(e); } final List flowNodeInstances = initializeFirstExecutableElements(sProcessInstance, selector); // process is initialized and now the engine trigger jobs to execute other activities, give the hand back ProcessInstanceState state; final int size = flowNodeInstances.size(); if (size == 0) { state = ProcessInstanceState.COMPLETED; } else { state = ProcessInstanceState.STARTED; } try { processInstanceService.setState(sProcessInstance, state); } catch (final SBonitaException e) { throw new SProcessInstanceCreationException("Unable to set the state on the process.", e); } for (final SFlowNodeInstance sFlowNodeInstance : flowNodeInstances) { try { registerExecuteFlowNodeWork(sFlowNodeInstance); } catch (final SWorkRegisterException e) { setExceptionContext(sProcessInstance, sFlowNodeInstance, e); throw new SFlowNodeExecutionException("Unable to trigger execution of the flow node.", e); } } return sProcessInstance; } @Override public String getHandledType() { return SFlowElementsContainerType.PROCESS.name(); } private void createAndExecuteActivities(final Long processDefinitionId, final SFlowNodeInstance flowNodeInstance, final long parentProcessInstanceId, final List choosenActivityDefinitions, final long rootProcessInstanceId) throws SBonitaException { final SProcessInstance parentProcessInstance = processInstanceService .getProcessInstance(parentProcessInstanceId); final SStateCategory stateCategory = parentProcessInstance.getStateCategory(); // Create Activities final List sFlowNodeInstances = bpmInstancesCreator.createFlowNodeInstances( processDefinitionId, flowNodeInstance.getRootContainerId(), flowNodeInstance.getParentContainerId(), choosenActivityDefinitions, rootProcessInstanceId, parentProcessInstanceId, stateCategory); // Execute Activities for (final SFlowNodeInstance sFlowNodeInstance : sFlowNodeInstances) { registerExecuteFlowNodeWork(sFlowNodeInstance); } } private void registerExecuteFlowNodeWork(SFlowNodeInstance sFlowNodeInstance) throws SWorkRegisterException { workService .registerWork(workFactory.createExecuteFlowNodeWorkDescriptor(sFlowNodeInstance)); } private void setExceptionContext(final SProcessDefinition sProcessDefinition, final SFlowNodeInstance sFlowNodeInstance, final SBonitaException e) { setExceptionContext(sProcessDefinition, e); e.setProcessInstanceIdOnContext(sFlowNodeInstance.getParentProcessInstanceId()); e.setRootProcessInstanceIdOnContext(sFlowNodeInstance.getRootProcessInstanceId()); setExceptionContext(sFlowNodeInstance, e); } private void setExceptionContext(final SProcessDefinition sProcessDefinition, final SProcessInstance sProcessInstance, final SBonitaException e) { setExceptionContext(sProcessDefinition, e); setExceptionContext(sProcessInstance, e); } private void setExceptionContext(final SProcessInstance sProcessInstance, final SFlowNodeInstance sFlowNodeInstance, final SBonitaException e) { e.setProcessDefinitionIdOnContext(sProcessInstance.getProcessDefinitionId()); e.setProcessDefinitionNameOnContext(sProcessInstance.getName()); setExceptionContext(sProcessInstance, e); setExceptionContext(sFlowNodeInstance, e); } private void setExceptionContext(final SProcessDefinition sProcessDefinition, final SBonitaException e) { e.setProcessDefinitionIdOnContext(sProcessDefinition.getId()); e.setProcessDefinitionNameOnContext(sProcessDefinition.getName()); e.setProcessDefinitionVersionOnContext(sProcessDefinition.getVersion()); } private void setExceptionContext(final SProcessInstance sProcessInstance, final SBonitaException e) { e.setProcessInstanceIdOnContext(sProcessInstance.getId()); e.setRootProcessInstanceIdOnContext(sProcessInstance.getRootProcessInstanceId()); } private void setExceptionContext(final SFlowNodeInstance sFlowNodeInstance, final SBonitaException e) { e.setFlowNodeDefinitionIdOnContext(sFlowNodeInstance.getFlowNodeDefinitionId()); e.setFlowNodeInstanceIdOnContext(sFlowNodeInstance.getId()); e.setFlowNodeNameOnContext(sFlowNodeInstance.getName()); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessInstanceInterruptor.java ================================================ /** * 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.execution; import java.util.List; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.work.SWorkRegisterException; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ @Slf4j public class ProcessInstanceInterruptor { private final ProcessInstanceService processInstanceService; private final FlowNodeInstanceService flowNodeInstanceService; private final ContainerRegistry containerRegistry; public ProcessInstanceInterruptor(ProcessInstanceService processInstanceService, FlowNodeInstanceService flowNodeInstanceService, ContainerRegistry containerRegistry) { this.processInstanceService = processInstanceService; this.flowNodeInstanceService = flowNodeInstanceService; this.containerRegistry = containerRegistry; } /** * Interrupt the given process instance AND its children * * @param processInstanceId the process instance * @param stateCategory the state category * @return true if some children were interrupted */ public boolean interruptProcessInstance(final long processInstanceId, final SStateCategory stateCategory) throws SBonitaException { processInstanceService.setStateCategory(processInstanceService.getProcessInstance(processInstanceId), stateCategory); List flowNodeInstances = flowNodeInstanceService .getDirectChildrenOfProcessInstance(processInstanceId, 0, Integer.MAX_VALUE); if (flowNodeInstances.isEmpty()) { log.info("Process instance {} with no children was {}", processInstanceId, getInterruptionType(stateCategory)); return false; } interruptFlowNodeInstances(flowNodeInstances, stateCategory); log.info("Process instance {} and its children were {}", processInstanceId, getInterruptionType(stateCategory)); return true; } private String getInterruptionType(SStateCategory stateCategory) { return stateCategory.equals(SStateCategory.ABORTING) ? "aborted" : "cancelled"; } private void executeFlowNode(SFlowNodeInstance child) throws SWorkRegisterException { if (child.isTerminal()) { containerRegistry.notifyChildFinished(child); } else { //should not try to execute these because its the children that should be aborted if (child.getType() != SFlowNodeType.MULTI_INSTANCE_ACTIVITY || child.getType() != SFlowNodeType.LOOP_ACTIVITY) { containerRegistry.executeFlowNode(child); } } } /** * Interrupt the given process instant AND its children, excluding the exceptionChildId * * @param processInstanceId the process instance * @param stateCategory the state category * @param exceptionChildId the element to exclude */ public void interruptProcessInstance(final long processInstanceId, final SStateCategory stateCategory, final long exceptionChildId) throws SBonitaException { processInstanceService.setStateCategory(processInstanceService.getProcessInstance(processInstanceId), stateCategory); interruptChildrenOfProcessInstance(processInstanceId, stateCategory, exceptionChildId); } /** * Interrupt children of given process instance excluding notToInterruptFlownodeId * * @param processInstanceId the process instance * @param stateCategory the state category in which children must be set * @param notToInterruptFlownodeId the element to exclude */ public void interruptChildrenOfProcessInstance(final long processInstanceId, final SStateCategory stateCategory, final long notToInterruptFlownodeId) throws SBonitaException { List flowNodeInstances = flowNodeInstanceService .getDirectChildrenOfProcessInstance(processInstanceId, 0, Integer.MAX_VALUE).stream() .filter(f -> f.getId() != notToInterruptFlownodeId) .collect(Collectors.toList()); if (flowNodeInstances.isEmpty()) { log.warn("Process instance {} with no children was {} by flownode {}", processInstanceId, getInterruptionType(stateCategory), notToInterruptFlownodeId); return; } interruptFlowNodeInstances(flowNodeInstances, stateCategory); log.info("Process instance {} and its children were {} by flownode {}", processInstanceId, getInterruptionType(stateCategory), notToInterruptFlownodeId); } /** * Interrupt children of given flow node instance * * @param flowNodeInstance the flow node instance * @param stateCategory the state category in which children must be set */ public void interruptChildrenOfFlowNodeInstance(SFlowNodeInstance flowNodeInstance, SStateCategory stateCategory) throws SBonitaException { List flowNodeInstances = flowNodeInstanceService .getDirectChildrenOfActivityInstance(flowNodeInstance.getId(), 0, Integer.MAX_VALUE); if (flowNodeInstances.isEmpty()) { log.warn("No children of flownode {} to {} found", flowNodeInstance.getId(), getInterruptionType(stateCategory)); return; } interruptFlowNodeInstances(flowNodeInstances, stateCategory); } private void interruptFlowNodeInstances(final List children, final SStateCategory stateCategory) throws SBonitaException { for (final SFlowNodeInstance child : children) { log.debug("Put element in {}, element: {}, {}, {}", stateCategory, child.getId(), child.getStateName(), child.getType()); flowNodeInstanceService.setStateCategory(child, stateCategory); log.debug("Resume child in stateCategory {}: {}, {}, {}", stateCategory, child.getId(), child.getStateName(), child.getStateCategory()); executeFlowNode(child); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessStarterVerifier.java ================================================ /** * 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; /** * Get the current number of started process instances. * * @return -1 if non relevant in this context */ default long getCurrentNumberOfStartedProcessInstances() { return -1; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/ProcessStarterVerifierImpl.java ================================================ /** * 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 static java.lang.System.currentTimeMillis; 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.ProcessInstanceService; 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 TransactionService transactionService; private final ProcessInstanceService processInstanceService; private final List counters = Collections.synchronizedList(new ArrayList<>()); @Autowired public ProcessStarterVerifierImpl(PlatformRetriever platformRetriever, PlatformInformationService platformInformationService, TransactionService transactionService, ProcessInstanceService processInstanceService) throws Exception { this.platformRetriever = platformRetriever; this.platformInformationService = platformInformationService; this.transactionService = transactionService; this.processInstanceService = processInstanceService; counters.addAll(setupCounters(transactionService)); // clean up old values: final long oldestValidDate = currentTimeMillis() - PERIOD_IN_MILLIS; cleanupOldValues(oldestValidDate); // then check database integrity: verifyCountersCoherence(counters, oldestValidDate); } List setupCounters(TransactionService transactionService) throws Exception { return 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 the possibility to create process instance {}", processInstance.getId()); final long processStartDate = processInstance.getStartDate(); cleanupOldValues(processStartDate - PERIOD_IN_MILLIS); log.debug("Found {} cases already started in the last {} days", counters.size(), PERIOD_IN_DAYS); if (counters.size() >= LIMIT) { var nextResetTimestamp = getNextResetTimestamp(counters); final String nextValidTime = getStringRepresentation(nextResetTimestamp); 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), nextResetTimestamp); } try { synchronized (counters) { counters.add(processStartDate); } 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()), e); } } void cleanupOldValues(long olderThanInMilliseconds) { log.trace("Cleaning up old values for the last {} days", PERIOD_IN_DAYS); synchronized (counters) { counters.removeIf(timestamp -> timestamp < olderThanInMilliseconds); } } 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); } } @Override public long getCurrentNumberOfStartedProcessInstances() { return counters.size(); } 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); } } List fetchLastArchivedProcessInstanceStartDates(Long oldestValidDate) throws Exception { final List startDates = transactionService.executeInTransaction( () -> processInstanceService.getLastArchivedProcessInstanceStartDates(oldestValidDate)); // print the start dates to the log: log.debug("Last archived process instance start dates: {}", startDates); return startDates; } public void verifyCountersCoherence(List counters, Long oldestValidDate) throws Exception { final List lastArchivedProcessInstanceStartDates = fetchLastArchivedProcessInstanceStartDates( oldestValidDate); for (Long startDate : lastArchivedProcessInstanceStartDates) { if (!counters.contains(startDate)) { throw new IllegalStateException("Invalid database. Please reset it and restart."); } } } 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; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/SIllegalStateTransition.java ================================================ /** * 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.execution; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException; /** * @author Elias Ricken de Medeiros */ public class SIllegalStateTransition extends SActivityExecutionException { private static final long serialVersionUID = 6940283544247417112L; public SIllegalStateTransition(String message, Throwable cause) { super(message, cause); } public SIllegalStateTransition(String message) { super(message); } public SIllegalStateTransition(Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/SNotSerializableException.java ================================================ /** * 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.execution; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SNotSerializableException extends SBonitaException { private static final long serialVersionUID = -229226043369898514L; public SNotSerializableException(final String connectorDefinitionId, final String connectorDefinitionVersion, final String key, final Object value) { super(createMessage(connectorDefinitionId, connectorDefinitionVersion, key, value)); } private static String createMessage(final String connectorDefinitionId, final String connectorDefinitionVersion, final String key, final Object value) { final StringBuilder stringBuilder = new StringBuilder("the connector "); stringBuilder.append(connectorDefinitionId); stringBuilder.append(' '); stringBuilder.append(connectorDefinitionVersion); stringBuilder.append(" have an unserializable output and was called directly from the api. name="); stringBuilder.append(key); stringBuilder.append(" value="); stringBuilder.append(value.toString()); return stringBuilder.toString(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/SUnreleasableTaskException.java ================================================ /** * 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.execution; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SUnreleasableTaskException extends SBonitaException { private static final long serialVersionUID = 2845399803318977578L; public SUnreleasableTaskException(final String string) { super(string); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/StartFlowNodeFilter.java ================================================ /** * 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.execution; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition; /** * @author Elias Ricken de Medeiros * @author Vincent Elcrin */ public class StartFlowNodeFilter implements Filter { @Override public boolean mustSelect(final SFlowNodeDefinition flowNode) { return flowNode.isStartable(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/StateBehaviors.java ================================================ /** * 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.execution; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.TreeSet; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.actor.mapping.SActorNotFoundException; import org.bonitasoft.engine.actor.mapping.model.SActor; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.connector.ConnectorState; import org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.business.data.RefBusinessDataRetriever; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.classloader.SClassLoaderException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceModificationException; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.filter.FilterResult; import org.bonitasoft.engine.core.filter.UserFilterService; import org.bonitasoft.engine.core.filter.exception.SUserFilterExecutionException; import org.bonitasoft.engine.core.operation.OperationService; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.process.comment.api.SCommentAddException; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.comment.api.SystemCommentType; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; 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.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.SHumanTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.SLoopCharacteristics; import org.bonitasoft.engine.core.process.definition.model.SMultiInstanceLoopCharacteristics; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SReceiveTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.SSendTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.SUserFilterDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SCatchEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SIntermediateCatchEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SThrowEventDefinition; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.*; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException; 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.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping; import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.data.instance.api.ParentContainerResolver; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.execution.event.EventsHandler; import org.bonitasoft.engine.execution.work.BPMWorkFactory; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SUserNotFoundException; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.work.SWorkRegisterException; import org.bonitasoft.engine.work.WorkService; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ @Component public class StateBehaviors { private static final int BATCH_SIZE = 20; protected final ParentContainerResolver parentContainerResolver; private final BPMInstancesCreator bpmInstancesCreator; private final EventsHandler eventsHandler; private final ActivityInstanceService activityInstanceService; private final UserFilterService userFilterService; private final ClassLoaderService classLoaderService; private final ActorMappingService actorMappingService; private final ExpressionResolverService expressionResolverService; private final DataInstanceService dataInstanceService; private final OperationService operationService; private final WorkService workService; private final ContainerRegistry containerRegistry; private final EventInstanceService eventInstanceService; private final ConnectorInstanceService connectorInstanceService; private final SCommentService commentService; private final IdentityService identityService; private final WaitingEventsInterrupter waitingEventsInterrupter; private final RefBusinessDataService refBusinessDataService; private final RefBusinessDataRetriever refBusinessDataRetriever; private final BPMWorkFactory workFactory; private ProcessInstanceInterruptor processInstanceInterruptor; public StateBehaviors(final BPMInstancesCreator bpmInstancesCreator, final EventsHandler eventsHandler, final ActivityInstanceService activityInstanceService, final UserFilterService userFilterService, final ClassLoaderService classLoaderService, final ActorMappingService actorMappingService, final ConnectorInstanceService connectorInstanceService, final ExpressionResolverService expressionResolverService, final DataInstanceService dataInstanceService, final OperationService operationService, final WorkService workService, final ContainerRegistry containerRegistry, final EventInstanceService eventInstanceService, final SCommentService commentService, final IdentityService identityService, final ParentContainerResolver parentContainerResolver, final WaitingEventsInterrupter waitingEventsInterrupter, final RefBusinessDataService refBusinessDataService, final RefBusinessDataRetriever refBusinessDataRetriever, final BPMWorkFactory workFactory, final ProcessInstanceInterruptor processInstanceInterruptor) { super(); this.bpmInstancesCreator = bpmInstancesCreator; this.eventsHandler = eventsHandler; this.activityInstanceService = activityInstanceService; this.userFilterService = userFilterService; this.classLoaderService = classLoaderService; this.actorMappingService = actorMappingService; this.connectorInstanceService = connectorInstanceService; this.expressionResolverService = expressionResolverService; this.dataInstanceService = dataInstanceService; this.operationService = operationService; this.workService = workService; this.containerRegistry = containerRegistry; this.eventInstanceService = eventInstanceService; this.commentService = commentService; this.identityService = identityService; this.parentContainerResolver = parentContainerResolver; this.refBusinessDataService = refBusinessDataService; this.refBusinessDataRetriever = refBusinessDataRetriever; this.waitingEventsInterrupter = waitingEventsInterrupter; this.workFactory = workFactory; this.processInstanceInterruptor = processInstanceInterruptor; } public DataInstanceContainer getParentContainerType(final SFlowNodeInstance flowNodeInstance) { DataInstanceContainer parentContainerType; if (flowNodeInstance.getLogicalGroup(2) <= 0) { parentContainerType = DataInstanceContainer.PROCESS_INSTANCE; } else { parentContainerType = DataInstanceContainer.ACTIVITY_INSTANCE; } return parentContainerType; } public DataInstanceService getDataInstanceService() { return dataInstanceService; } public void mapDataOutputOfMultiInstance(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { if (flowNodeInstance instanceof SActivityInstance && !SFlowNodeType.MULTI_INSTANCE_ACTIVITY.equals(flowNodeInstance.getType())) { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SActivityDefinition activityDefinition = (SActivityDefinition) processContainer .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); if (activityDefinition != null) {// can be null if the activity was added in runtime try { final SLoopCharacteristics loopCharacteristics = activityDefinition.getLoopCharacteristics(); if (loopCharacteristics instanceof SMultiInstanceLoopCharacteristics miLoop && ((SMultiInstanceLoopCharacteristics) loopCharacteristics) .getDataOutputItemRef() != null) { final SBusinessDataDefinition businessData = processContainer .getBusinessDataDefinition(miLoop.getLoopDataOutputRef()); if (businessData == null) { mapDataOutputOfMultiInstance(flowNodeInstance, miLoop); } else { mapMultiInstanceBusinessDataOutput(flowNodeInstance, miLoop); } } } catch (final SBonitaException sbe) { throw new SActivityStateExecutionException( "Error while mapping multi instance output of " + flowNodeInstance, ScopedException.ITERATION, sbe); } } } } private void mapMultiInstanceBusinessDataOutput(final SFlowNodeInstance flowNodeInstance, final SMultiInstanceLoopCharacteristics miLoop) throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException, SRefBusinessDataInstanceModificationException { final SRefBusinessDataInstance outputMIRef = refBusinessDataService.getFlowNodeRefBusinessDataInstance( miLoop.getDataOutputItemRef(), flowNodeInstance.getId()); final SRefBusinessDataInstance outputMILoopRef = refBusinessDataService.getRefBusinessDataInstance( miLoop.getLoopDataOutputRef(), flowNodeInstance.getParentProcessInstanceId()); final SProcessMultiRefBusinessDataInstance multiRefBusinessDataInstance = (SProcessMultiRefBusinessDataInstance) outputMILoopRef; List dataIds = multiRefBusinessDataInstance.getDataIds(); if (dataIds == null) { dataIds = new ArrayList<>(); } final Long dataId = ((SFlowNodeSimpleRefBusinessDataInstance) outputMIRef).getDataId(); dataIds.add(dataId); refBusinessDataService.updateRefBusinessDataInstance(multiRefBusinessDataInstance, dataIds); } @SuppressWarnings("unchecked") public void mapDataOutputOfMultiInstance(final SFlowNodeInstance flowNodeInstance, final SMultiInstanceLoopCharacteristics miLoop) throws SBonitaException { final SDataInstance outputData = dataInstanceService.getDataInstance(miLoop.getDataOutputItemRef(), flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), parentContainerResolver); final SDataInstance loopData = dataInstanceService.getDataInstance(miLoop.getLoopDataOutputRef(), flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), parentContainerResolver); if (outputData != null && loopData != null) { final Serializable value = loopData.getValue(); final int index = flowNodeInstance.getLoopCounter(); if (value instanceof List) { ((List) value).set(index, outputData.getValue()); } else { throw new SActivityExecutionException("unable to map the ouput of the multi instanciated activity " + flowNodeInstance.getName() + " the output loop data named " + loopData.getName() + " is not a list but " + loopData.getClassName()); } final EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor(); entityUpdateDescriptor.addField("value", value); dataInstanceService.updateDataInstance(loopData, entityUpdateDescriptor); } } public void mapActors(final SFlowNodeInstance flowNodeInstance, final SFlowElementContainerDefinition processContainer) throws SActivityStateExecutionException { if (SFlowNodeType.USER_TASK.equals(flowNodeInstance.getType()) || SFlowNodeType.MANUAL_TASK.equals(flowNodeInstance.getType())) { final SHumanTaskDefinition humanTaskDefinition = (SHumanTaskDefinition) processContainer .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); if (humanTaskDefinition != null) { final String actorName = humanTaskDefinition.getActorName(); final long processDefinitionId = flowNodeInstance.getLogicalGroup(0); final SUserFilterDefinition sUserFilterDefinition = humanTaskDefinition.getSUserFilterDefinition(); if (sUserFilterDefinition != null) { try { mapUsingUserFilters(flowNodeInstance, humanTaskDefinition, actorName, processDefinitionId, sUserFilterDefinition); } catch (SActivityStateExecutionException e) { throw e; } catch (SBonitaException e) { throw new SActivityStateExecutionException( "Error while mapping actor '" + actorName + "' with filter " + sUserFilterDefinition.getUserFilterId() + " for " + flowNodeInstance, ScopedException.ACTOR_MAPPING, e); } } else { try { mapUsingActors(flowNodeInstance, actorName, processDefinitionId); } catch (SBonitaException e) { throw new SActivityStateExecutionException("Error while mapping actors for " + flowNodeInstance, ScopedException.ACTOR_MAPPING, e); } } } } } private void mapUsingActors(final SFlowNodeInstance flowNodeInstance, final String actorName, final long processDefinitionId) throws SActorNotFoundException, SActivityCreationException { final SActor actor = actorMappingService.getActor(actorName, processDefinitionId); final SPendingActivityMapping mapping = SPendingActivityMapping.builder().activityId(flowNodeInstance.getId()) .actorId(actor.getId()).build(); activityInstanceService.addPendingActivityMappings(mapping); } void mapUsingUserFilters(final SFlowNodeInstance flowNodeInstance, final SHumanTaskDefinition humanTaskDefinition, final String actorName, final long processDefinitionId, final SUserFilterDefinition sUserFilterDefinition) throws SClassLoaderException, SUserFilterExecutionException, SActivityStateExecutionException, SActivityCreationException, SFlowNodeNotFoundException, SFlowNodeReadException, SActivityModificationException { final ClassLoader processClassloader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, processDefinitionId)); final SExpressionContext expressionContext = new SExpressionContext(flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), flowNodeInstance.getLogicalGroup(0)); final FilterResult result = userFilterService.executeFilter(processDefinitionId, sUserFilterDefinition, sUserFilterDefinition.getInputs(), processClassloader, expressionContext, actorName); final List userIds = result.getResult(); if (userIds == null || userIds.isEmpty() || userIds.contains(0L) || userIds.contains(-1L)) { throw new SActivityStateExecutionException( "No user id returned by the user filter " + sUserFilterDefinition + " on activity " + humanTaskDefinition.getName(), ScopedException.ACTOR_MAPPING); } for (final Long userId : new TreeSet<>(userIds)) { final SPendingActivityMapping mapping = SPendingActivityMapping.builder() .activityId(flowNodeInstance.getId()).userId(userId).build(); activityInstanceService.addPendingActivityMappings(mapping); } if (userIds.size() == 1 && result.shouldAutoAssignTaskIfSingleResult()) { final Long userId = userIds.get(0); activityInstanceService.assignHumanTask(flowNodeInstance.getId(), userId); //system comment is added after the evaluation of the display name } } public void registerWaitingEvent(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { // handle catch event if (flowNodeInstance instanceof SIntermediateCatchEventInstance intermediateCatchEventInstance) { // handleEventTriggerInstances(processDefinition, intermediateCatchEventInstance); final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SIntermediateCatchEventDefinition intermediateCatchEventDefinition = (SIntermediateCatchEventDefinition) processContainer .getFlowNode(intermediateCatchEventInstance.getFlowNodeDefinitionId()); try { eventsHandler.handleCatchEvent(processDefinition, intermediateCatchEventDefinition, intermediateCatchEventInstance); } catch (final SBonitaException e) { throw new SActivityStateExecutionException("unable to handle catch event " + flowNodeInstance, ScopedException.EVENT, e); } } else if (flowNodeInstance instanceof SReceiveTaskInstance receiveTaskInstance) { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SReceiveTaskDefinition receiveTaskIDefinition = (SReceiveTaskDefinition) processContainer .getFlowNode(receiveTaskInstance .getFlowNodeDefinitionId()); try { eventsHandler.handleCatchMessage(processDefinition, receiveTaskIDefinition, receiveTaskInstance); } catch (final SBonitaException e) { throw new SActivityStateExecutionException("unable to handle catch event " + flowNodeInstance, ScopedException.EVENT, e); } } } public void handleBoundaryEvent(final SProcessDefinition processDefinition, final SBoundaryEventInstance boundaryInstance) throws SActivityStateExecutionException { final long activityInstanceId = boundaryInstance.getActivityInstanceId(); // FIXME: add activity name in SBoundaryEventInstance to avoid the getActivityInstance below try { final SActivityInstance activityInstance = activityInstanceService.getActivityInstance(activityInstanceId); final SActivityDefinition activityDefinition = (SActivityDefinition) processDefinition.getProcessContainer() .getFlowNode( activityInstance.getFlowNodeDefinitionId()); final SBoundaryEventDefinition boundaryEventDefinition = activityDefinition .getBoundaryEventDefinition(boundaryInstance.getName()); eventsHandler.handleCatchEvent(processDefinition, boundaryEventDefinition, boundaryInstance); } catch (final SBonitaException e) { throw new SActivityStateExecutionException("Unable to handle catch event " + boundaryInstance, ScopedException.EVENT, e); } } public void createData(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { if (flowNodeInstance instanceof SActivityInstance) { bpmInstancesCreator.createDataInstances(processDefinition, flowNodeInstance); } } public void updateDisplayNameAndDescription(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SFlowNodeDefinition flowNode = processContainer .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); if (flowNode != null) { final SExpression displayNameExpression = flowNode.getDisplayName(); final SExpression displayDescriptionExpression = flowNode.getDisplayDescription(); final SExpressionContext sExpressionContext = new SExpressionContext(flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinition.getId()); try { String displayName = flowNode.getName(); if (displayNameExpression != null) { displayName = (String) expressionResolverService.evaluate(displayNameExpression, sExpressionContext); } activityInstanceService.updateDisplayName(flowNodeInstance, displayName); } catch (SBonitaException e) { throw new SActivityStateExecutionException("Error while updating display name", ScopedException.GENERAL_INFORMATION, e); } try { String displayDescription = flowNode.getDescription(); if (displayDescriptionExpression != null) { displayDescription = (String) expressionResolverService.evaluate(displayDescriptionExpression, sExpressionContext); } activityInstanceService.updateDisplayDescription(flowNodeInstance, displayDescription); } catch (SBonitaException e) { throw new SActivityStateExecutionException("Error while updating display description", ScopedException.GENERAL_INFORMATION, e); } } } public void updateExpectedDuration(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SFlowNodeDefinition flowNodeDefinition = processContainer .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); if (!(flowNodeDefinition instanceof SHumanTaskDefinition humanTaskDefinition)) { return; } final SExpression expectedDurationExpression = humanTaskDefinition.getExpectedDuration(); if (expectedDurationExpression == null) { return; } final SExpressionContext sExpressionContext = new SExpressionContext(flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinition.getId()); try { final Long duration = (Long) expressionResolverService.evaluate(expectedDurationExpression, sExpressionContext); if (duration == null) { activityInstanceService.setExpectedEndDate(flowNodeInstance, null); } else { activityInstanceService.setExpectedEndDate(flowNodeInstance, System.currentTimeMillis() + duration); } } catch (final SBonitaException e) { throw new SActivityStateExecutionException("Error while updating expected end date", ScopedException.GENERAL_INFORMATION, e); } } public void updateDisplayDescriptionAfterCompletion(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SFlowNodeDefinition flowNode = processContainer .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); if (flowNode != null) { final SExpression displayDescriptionAfterCompletionExpression = flowNode .getDisplayDescriptionAfterCompletion(); final SExpressionContext sExpressionContext = new SExpressionContext(flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinition.getId()); try { final String displayDescriptionAfterCompletion; if (displayDescriptionAfterCompletionExpression != null) { displayDescriptionAfterCompletion = (String) expressionResolverService.evaluate( displayDescriptionAfterCompletionExpression, sExpressionContext); activityInstanceService.updateDisplayDescription(flowNodeInstance, displayDescriptionAfterCompletion); } } catch (final SBonitaException e) { throw new SActivityStateExecutionException("Error while updating display description", ScopedException.GENERAL_INFORMATION, e); } } } public void executeOperations(final SProcessDefinition processDefinition, final SActivityInstance activityInstance) throws SActivityStateExecutionException { try { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SFlowNodeDefinition flowNode = processContainer .getFlowNode(activityInstance.getFlowNodeDefinitionId()); if (flowNode instanceof SActivityDefinition activityDefinition) { final List sOperations = activityDefinition.getSOperations(); final SExpressionContext sExpressionContext = new SExpressionContext(activityInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinition.getId()); operationService.execute(sOperations, sExpressionContext); } } catch (final SOperationExecutionException e) { throw new SActivityStateExecutionException("Error while executing operations", ScopedException.OPERATION, e); } } public void handleThrowEvent(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { if (flowNodeInstance instanceof SThrowEventInstance throwEventInstance) { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SThrowEventDefinition eventDefinition = (SThrowEventDefinition) processContainer .getFlowNode(throwEventInstance.getFlowNodeDefinitionId()); try { eventsHandler.handleThrowEvent(processDefinition, eventDefinition, throwEventInstance); } catch (final SBonitaException e) { throw new SActivityStateExecutionException("Unable to handle throw event " + flowNodeInstance, ScopedException.EVENT, e); } } else if (SFlowNodeType.SEND_TASK.equals(flowNodeInstance.getType())) { final SSendTaskInstance sendTaskInstance = (SSendTaskInstance) flowNodeInstance; final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SSendTaskDefinition sendTaskDefinition = (SSendTaskDefinition) processContainer .getFlowNode(sendTaskInstance.getFlowNodeDefinitionId()); try { eventsHandler.handleThrowMessage(processDefinition, sendTaskDefinition, sendTaskInstance); } catch (final SBonitaException e) { throw new SActivityStateExecutionException("Unable to handle throw message " + flowNodeInstance, ScopedException.EVENT, e); } } } public void executeChildrenActivities(final SFlowNodeInstance flowNodeInstance) throws SActivityExecutionException { try { int i = 0; List childrenOfAnActivity; do { childrenOfAnActivity = activityInstanceService.getChildrenOfAnActivity(flowNodeInstance.getId(), i, BATCH_SIZE); for (final SActivityInstance sActivityInstance : childrenOfAnActivity) { containerRegistry.executeFlowNode(sActivityInstance); } i += BATCH_SIZE; } while (childrenOfAnActivity.size() == BATCH_SIZE); } catch (final SBonitaException e) { throw new SActivityExecutionException(e); } } public void interruptSubActivities(SFlowNodeInstance flowNodeInstance, final SStateCategory stateCategory) throws SBonitaException { processInstanceInterruptor.interruptChildrenOfFlowNodeInstance(flowNodeInstance, stateCategory); } public void executeConnectorInWork(final Long processDefinitionId, final long processInstanceId, final long rootProcessInstanceId, final long flowNodeDefinitionId, final long flowNodeInstanceId, final SConnectorInstance connector, final SConnectorDefinition sConnectorDefinition) throws SActivityStateExecutionException { final long connectorInstanceId = connector.getId(); // final Long connectorDefinitionId = sConnectorDefinition.getId();// FIXME: Uncomment when generate id final String connectorDefinitionName = sConnectorDefinition.getName(); try { connectorInstanceService.setState(connector, ConnectorState.EXECUTING.name()); workService.registerWork(workFactory.createExecuteConnectorOfActivityDescriptor(processDefinitionId, processInstanceId, rootProcessInstanceId, flowNodeDefinitionId, flowNodeInstanceId, connectorInstanceId, sConnectorDefinition.getConnectorId(), connectorDefinitionName, sConnectorDefinition.getActivationEvent().name())); } catch (final SConnectorInstanceModificationException e) { throw new SActivityStateExecutionException("Unable to set ConnectorState to EXECUTING", ScopedException.CONNECTOR, e); } catch (final SWorkRegisterException e) { throw new SActivityStateExecutionException( "Unable to register the work that execute the connector " + connector + " on " + flowNodeInstanceId, ScopedException.CONNECTOR, e); } } public void createAttachedBoundaryEvents(final SProcessDefinition processDefinition, final SActivityInstance activityInstance) throws SActivityStateExecutionException { final SActivityDefinition activityDefinition = (SActivityDefinition) processDefinition.getProcessContainer() .getFlowNode( activityInstance.getFlowNodeDefinitionId()); if (mustAddBoundaryEvents(activityInstance, activityDefinition)) { createAttachedBoundaryEvents(processDefinition, activityInstance, activityDefinition); } } private void createAttachedBoundaryEvents(final SProcessDefinition processDefinition, final SActivityInstance activityInstance, final SActivityDefinition activityDefinition) throws SActivityStateExecutionException { final List boundaryEventDefinitions = activityDefinition .getBoundaryEventDefinitions(); try { final SBoundaryEventInstanceBuilderFactory boundaryEventInstanceBuilder = BuilderFactory .get(SBoundaryEventInstanceBuilderFactory.class); final long rootProcessInstanceId = activityInstance .getLogicalGroup(boundaryEventInstanceBuilder.getRootProcessInstanceIndex()); final long parentProcessInstanceId = activityInstance .getLogicalGroup(boundaryEventInstanceBuilder.getParentProcessInstanceIndex()); final SFlowElementsContainerType containerType = getContainerType(activityInstance, boundaryEventInstanceBuilder); for (final SBoundaryEventDefinition boundaryEventDefinition : boundaryEventDefinitions) { createBoundaryEvent(processDefinition, activityInstance, rootProcessInstanceId, parentProcessInstanceId, containerType, boundaryEventDefinition); } } catch (final SBonitaException e) { throw new SActivityStateExecutionException( "Unable to create boundary events attached to activity " + activityInstance.getName(), ScopedException.EVENT, e); } } private void createBoundaryEvent(final SProcessDefinition processDefinition, final SActivityInstance activityInstance, final long rootProcessInstanceId, final long parentProcessInstanceId, final SFlowElementsContainerType containerType, final SBoundaryEventDefinition boundaryEventDefinition) throws SBonitaException { final SBoundaryEventInstance boundaryEventInstance = (SBoundaryEventInstance) bpmInstancesCreator .createFlowNodeInstance(processDefinition.getId(), rootProcessInstanceId, activityInstance.getParentContainerId(), containerType, boundaryEventDefinition, rootProcessInstanceId, parentProcessInstanceId, false, -1, SStateCategory.NORMAL, activityInstance.getId()); // no need to handle failed state, creation is in the same tx containerRegistry.executeFlowNodeInSameThread(boundaryEventInstance, containerType.name()); } private SFlowElementsContainerType getContainerType(final SActivityInstance activityInstance, final SBoundaryEventInstanceBuilderFactory boundaryEventInstanceBuilder) { SFlowElementsContainerType containerType = SFlowElementsContainerType.PROCESS; final long parentActivityInstanceId = activityInstance .getLogicalGroup(boundaryEventInstanceBuilder.getParentActivityInstanceIndex()); if (parentActivityInstanceId > 0) { containerType = SFlowElementsContainerType.FLOWNODE; } return containerType; } private boolean mustAddBoundaryEvents(final SActivityInstance activityInstance, final SActivityDefinition activityDefinition) { // avoid to add boundary events in children of multi instance return activityDefinition != null && !activityDefinition.getBoundaryEventDefinitions().isEmpty() && !isChildOfLoopOrMultiInstance(activityInstance, activityDefinition); } private boolean isChildOfLoopOrMultiInstance(final SActivityInstance activityInstance, final SActivityDefinition activityDefinition) { return activityDefinition.getLoopCharacteristics() != null && !(SFlowNodeType.MULTI_INSTANCE_ACTIVITY.equals(activityInstance.getType()) || SFlowNodeType.LOOP_ACTIVITY.equals(activityInstance.getType())); } public void interruptAttachedBoundaryEvent(final SProcessDefinition processDefinition, final SActivityInstance activityInstance, final SStateCategory categoryState) throws SActivityStateExecutionException { try { final List boundaryEventInstances = eventInstanceService .getActivityBoundaryEventInstances(activityInstance.getId(), 0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS); for (final SBoundaryEventInstance boundaryEventInstance : boundaryEventInstances) { // don't abort boundary event that put this activity in aborting state if (activityInstance.getAbortedByBoundary() != boundaryEventInstance.getId()) { final SCatchEventDefinition catchEventDef = processDefinition.getProcessContainer() .getBoundaryEvent(boundaryEventInstance.getName()); waitingEventsInterrupter.interruptWaitingEvents(processDefinition, boundaryEventInstance, catchEventDef); activityInstanceService.setStateCategory(boundaryEventInstance, categoryState); containerRegistry.executeFlowNode(boundaryEventInstance); } } } catch (final SBonitaException e) { throw new SActivityStateExecutionException( "Unable to cancel boundary events attached to activity " + activityInstance.getName(), ScopedException.EVENT, e); } } public void addAssignmentSystemCommentIfTaskWasAutoAssign(final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { if (SFlowNodeType.USER_TASK.equals(flowNodeInstance.getType()) || SFlowNodeType.MANUAL_TASK.equals(flowNodeInstance.getType())) { final long userId = ((SHumanTaskInstance) flowNodeInstance).getAssigneeId(); if (userId > 0) { try { addAssignmentSystemComment(flowNodeInstance, userId); } catch (final SBonitaException e) { throw new SActivityStateExecutionException( "Error while adding a comment on task " + flowNodeInstance, ScopedException.GENERAL_INFORMATION, e); } } } } public void addAssignmentSystemComment(final SFlowNodeInstance flowNodeInstance, final long userId) throws SUserNotFoundException, SCommentAddException { final SUser user = identityService.getUser(userId); if (commentService.isCommentEnabled(SystemCommentType.STATE_CHANGE)) { commentService.addSystemComment(flowNodeInstance.getRootContainerId(), "The task \"" + flowNodeInstance.getDisplayName() + "\" is now assigned to " + user.getUserName()); } } public List createInnerInstances(final long processDefinitionId, final SActivityDefinition activity, final SMultiInstanceActivityInstance flowNodeInstance, final int numberOfInstanceToCreate) throws SBonitaException { final SMultiInstanceActivityInstanceBuilderFactory keyProvider = BuilderFactory .get(SMultiInstanceActivityInstanceBuilderFactory.class); final long rootProcessInstanceId = flowNodeInstance.getLogicalGroup(keyProvider.getRootProcessInstanceIndex()); final long parentProcessInstanceId = flowNodeInstance .getLogicalGroup(keyProvider.getParentProcessInstanceIndex()); int nbOfcreatedInstances = 0; final int nbOfInstances = flowNodeInstance.getNumberOfInstances(); final List createdInstances = new ArrayList<>(); for (int i = nbOfInstances; i < nbOfInstances + numberOfInstanceToCreate; i++) { createdInstances.add(bpmInstancesCreator.createFlowNodeInstance(processDefinitionId, flowNodeInstance.getRootContainerId(), flowNodeInstance.getId(), SFlowElementsContainerType.FLOWNODE, activity, rootProcessInstanceId, parentProcessInstanceId, true, i, SStateCategory.NORMAL, -1)); nbOfcreatedInstances++; } activityInstanceService.addMultiInstanceNumberOfActiveActivities(flowNodeInstance, nbOfcreatedInstances); final int tokenCount = flowNodeInstance.getTokenCount() + nbOfcreatedInstances; activityInstanceService.setTokenCount(flowNodeInstance, tokenCount); return createdInstances; } public int getNumberOfInstancesToCreateFromInputRef(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance, final SMultiInstanceLoopCharacteristics miLoop, final int numberOfInstanceMax) throws SDataInstanceException, SActivityStateExecutionException { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SBusinessDataDefinition businessData = processContainer .getBusinessDataDefinition(miLoop.getLoopDataInputRef()); if (businessData == null) { return getNumberOfInstanceToCreateFromSimpleData(processDefinition, flowNodeInstance, miLoop, numberOfInstanceMax); } try { return refBusinessDataService.getNumberOfDataOfMultiRefBusinessData(businessData.getName(), refBusinessDataRetriever.getProcessInstanceIdThatCanContainBusinessData( flowNodeInstance.getParentProcessInstanceId())); } catch (final SBonitaReadException | SProcessInstanceReadException | SProcessInstanceNotFoundException | SFlowNodeReadException | SFlowNodeNotFoundException sbre) { throw new SActivityStateExecutionException("Error while counting number of multi instances to create", ScopedException.ITERATION, sbre); } } private int getNumberOfInstanceToCreateFromSimpleData(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance, final SMultiInstanceLoopCharacteristics miLoop, final int numberOfInstanceMax) throws SDataInstanceException, SActivityStateExecutionException { final SDataInstance loopDataInput = dataInstanceService.getDataInstance(miLoop.getLoopDataInputRef(), flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), parentContainerResolver); if (loopDataInput != null) { final Serializable value = loopDataInput.getValue(); if (value instanceof List loopDataInputCollection) { return loopDataInputCollection.size(); } throw new SActivityStateExecutionException( "The multi instance on activity " + flowNodeInstance.getName() + " of process " + processDefinition.getName() + " " + processDefinition.getVersion() + " have a loop data input which is not a java.util.List", ScopedException.ITERATION); } return numberOfInstanceMax; } public boolean shouldCreateANewInstance(final SMultiInstanceLoopCharacteristics loopCharacteristics, final int numberOfInstances, final SMultiInstanceActivityInstance miActivityInstance) throws SDataInstanceException { if (loopCharacteristics.getLoopCardinality() != null) { return miActivityInstance.getLoopCardinality() > numberOfInstances; } List possibleValues; try { //FIXME find if a business data is used if instead of try catch final SProcessMultiRefBusinessDataInstance multiRef = (SProcessMultiRefBusinessDataInstance) refBusinessDataService .getRefBusinessDataInstance( loopCharacteristics.getLoopDataInputRef(), miActivityInstance.getParentProcessInstanceId()); possibleValues = multiRef.getDataIds(); } catch (final SBonitaException sbe) { final SDataInstance dataInstance = getDataInstanceService().getDataInstance( loopCharacteristics.getLoopDataInputRef(), miActivityInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), parentContainerResolver); possibleValues = (List) dataInstance.getValue(); } return possibleValues != null && numberOfInstances < possibleValues.size(); } public void updateOutputData(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance, final SMultiInstanceLoopCharacteristics miLoop, final int numberOfInstanceMax) throws SDataInstanceException, SActivityStateExecutionException { if (!isBusinessData(processDefinition, miLoop)) { final String loopDataOutputRef = miLoop.getLoopDataOutputRef(); if (loopDataOutputRef != null) { final SDataInstance loopDataOutput = dataInstanceService.getDataInstance(loopDataOutputRef, flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), parentContainerResolver); if (loopDataOutput != null) { final Serializable outValue = loopDataOutput.getValue(); if (outValue instanceof List) { updateLoopDataOutputWithListContent((List) outValue, loopDataOutput, numberOfInstanceMax); } else if (outValue == null) { updateLoopDataOutputWithNull(loopDataOutput, numberOfInstanceMax); } else { throw new SActivityStateExecutionException("The multi instance on activity " + flowNodeInstance.getName() + " of process " + processDefinition.getName() + " " + processDefinition.getVersion() + " have a loop data output which is not a java.util.List", ScopedException.ITERATION); } } } } } boolean isBusinessData(final SProcessDefinition processDefinition, final SMultiInstanceLoopCharacteristics miLoop) { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SBusinessDataDefinition businessData = processContainer .getBusinessDataDefinition(miLoop.getLoopDataOutputRef()); return businessData != null; } private void updateLoopDataOutputWithNull(final SDataInstance loopDataOutput, final int numberOfInstanceMax) throws SDataInstanceException { final ArrayList newOutputList = new ArrayList<>(numberOfInstanceMax); for (int i = 0; i < numberOfInstanceMax; i++) { newOutputList.add(null); } updateLoopDataOutputDataInstance(loopDataOutput, newOutputList); } private void updateLoopDataOutputWithListContent(final List outValue, final SDataInstance loopDataOutput, final int numberOfInstanceMax) throws SDataInstanceException { if (outValue.size() < numberOfInstanceMax) { // output data is too small final ArrayList newOutputList = new ArrayList<>(numberOfInstanceMax); newOutputList.addAll(outValue); for (int i = outValue.size(); i < numberOfInstanceMax; i++) { newOutputList.add(null); } updateLoopDataOutputDataInstance(loopDataOutput, newOutputList); } } private void updateLoopDataOutputDataInstance(final SDataInstance loopDataOutput, final ArrayList newOutputList) throws SDataInstanceException { final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor(); updateDescriptor.addField(SDataInstance.VALUE, newOutputList); dataInstanceService.updateDataInstance(loopDataOutput, updateDescriptor); } public List getConnectorDefinitions(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance, ConnectorEvent connectorEvent) { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SFlowNodeDefinition flowNodeDefinition = processContainer .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); if (flowNodeDefinition == null) { return Collections.emptyList(); } return flowNodeDefinition.getConnectors(connectorEvent); } public SConnectorInstance getNextConnectorInstance(List connectorDefinitions, SFlowNodeInstance flowNodeInstance, ConnectorEvent connectorEvent) throws SConnectorInstanceReadException { if (connectorDefinitions.isEmpty()) { return null; } return connectorInstanceService.getNextExecutableConnectorInstance(flowNodeInstance.getId(), SConnectorInstance.FLOWNODE_TYPE, connectorEvent); } public boolean isFirst(List connectorsOnEnter, SConnectorInstance nextConnectorInstanceToExecute) { return connectorsOnEnter.get(0).getName().equals(nextConnectorInstanceToExecute.getName()); } public boolean isNotExecutedYet(SConnectorInstance nextConnectorInstanceToExecute) { return ConnectorState.TO_BE_EXECUTED.name().equals(nextConnectorInstanceToExecute.getState()); } public SConnectorDefinition getConnectorDefinition(SConnectorInstance connectorInstance, List connectorDefinitions) { for (final SConnectorDefinition sConnectorDefinition : connectorDefinitions) { if (sConnectorDefinition.getName().equals(connectorInstance.getName())) { return sConnectorDefinition; } } throw new IllegalStateException("No connector definition found for connector instance " + connectorInstance); } public void executeConnector(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance, List connectorsOnEnter, SConnectorInstance connectorInstance) throws SActivityStateExecutionException { executeConnectorInWork(processDefinition.getId(), flowNodeInstance.getParentProcessInstanceId(), flowNodeInstance.getRootProcessInstanceId(), flowNodeInstance.getFlowNodeDefinitionId(), flowNodeInstance.getId(), connectorInstance, getConnectorDefinition(connectorInstance, connectorsOnEnter)); } public boolean noConnectorHasStartedInCurrentList(List connectorDefinitions, SConnectorInstance connectorInstance) throws SBonitaReadException { return /* there is no connector defined, it means that no connector could have been started here */ connectorDefinitions.isEmpty() || /* * if there is some connector defined the only way to have not started the phase is that there is a connector to * execute and it is the first * connector of the list and it was never executed */ connectorInstance != null && isFirst(connectorDefinitions, connectorInstance) && isNotExecutedYet(connectorInstance); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/TransitionEvaluator.java ================================================ /** * 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.execution; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; 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.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; 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.SStateCategory; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper; import org.bonitasoft.engine.execution.transition.ImplicitGatewayTransitionEvaluator; import org.bonitasoft.engine.execution.transition.InclusiveExclusiveTransitionEvaluator; import org.bonitasoft.engine.execution.transition.ParallelGatewayTransitionEvaluator; public class TransitionEvaluator { private final ImplicitGatewayTransitionEvaluator implicitGatewayTransitionEvaluator; private final ParallelGatewayTransitionEvaluator parallelGatewayTransitionEvaluator; private final InclusiveExclusiveTransitionEvaluator inclusiveTransitionEvaluator; private final InclusiveExclusiveTransitionEvaluator exclusiveTransitionEvaluator; public TransitionEvaluator(ImplicitGatewayTransitionEvaluator implicitGatewayTransitionEvaluator, ParallelGatewayTransitionEvaluator parallelGatewayTransitionEvaluator, InclusiveExclusiveTransitionEvaluator inclusiveTransitionEvaluator, InclusiveExclusiveTransitionEvaluator exclusiveTransitionEvaluator) { this.implicitGatewayTransitionEvaluator = implicitGatewayTransitionEvaluator; this.parallelGatewayTransitionEvaluator = parallelGatewayTransitionEvaluator; this.inclusiveTransitionEvaluator = inclusiveTransitionEvaluator; this.exclusiveTransitionEvaluator = exclusiveTransitionEvaluator; } protected List evaluateOutgoingTransitions(FlowNodeTransitionsWrapper transitions, final SProcessDefinition sDefinition, final SFlowNodeInstance flowNodeInstance) throws SBonitaException { // int nbOfTokenToMerge = 1;// may be > 1 in case of gateway // if is not a normal state don't create new elements if (!SStateCategory.NORMAL.equals(flowNodeInstance.getStateCategory())) { return Collections.emptyList(); } final SExpressionContext sExpressionContext = new SExpressionContext(flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), sDefinition.getId()); sExpressionContext.setProcessDefinition(sDefinition); if (SFlowNodeType.GATEWAY.equals(flowNodeInstance.getType())) { return evaluateOutgoingTransitionsForGateways(transitions, sDefinition, flowNodeInstance, sExpressionContext); } else if (SFlowNodeType.BOUNDARY_EVENT.equals(flowNodeInstance.getType())) { return new ArrayList<>(transitions.getNonDefaultOutgoingTransitionDefinitions()); } else { return evaluateOutgoingTransitionsForActivity(transitions, sDefinition, flowNodeInstance, sExpressionContext); } } List evaluateOutgoingTransitionsForActivity(final FlowNodeTransitionsWrapper transitions, final SProcessDefinition sDefinition, final SFlowNodeInstance flowNodeInstance, final SExpressionContext sExpressionContext) throws SBonitaException { if (transitions.getNonDefaultOutgoingTransitionDefinitions().isEmpty()) { STransitionDefinition defaultTransition; if ((defaultTransition = getDefaultTransition(sDefinition, flowNodeInstance)) == null) { return Collections.emptyList(); } return Collections.singletonList(defaultTransition); } return implicitGatewayTransitionEvaluator.evaluateTransitions(sDefinition, flowNodeInstance, transitions, sExpressionContext); } List evaluateOutgoingTransitionsForGateways(final FlowNodeTransitionsWrapper transitions, final SProcessDefinition sDefinition, final SFlowNodeInstance flowNodeInstance, final SExpressionContext sExpressionContext) throws SBonitaException { List chosenTransitionDefinitions; final SGatewayInstance gatewayInstance = (SGatewayInstance) flowNodeInstance; switch (gatewayInstance.getGatewayType()) { case EXCLUSIVE: chosenTransitionDefinitions = exclusiveTransitionEvaluator.evaluateTransitions(sDefinition, flowNodeInstance, transitions, sExpressionContext); break; case INCLUSIVE: chosenTransitionDefinitions = inclusiveTransitionEvaluator.evaluateTransitions(sDefinition, flowNodeInstance, transitions, sExpressionContext); break; case PARALLEL: chosenTransitionDefinitions = parallelGatewayTransitionEvaluator.evaluateTransitions(transitions); break; default: throw new UnsupportedOperationException( "Unsupported gateway type: " + gatewayInstance.getGatewayType()); } return chosenTransitionDefinitions; } protected STransitionDefinition getDefaultTransition(final SProcessDefinition sDefinition, final SFlowNodeInstance flowNodeInstance) { final SFlowElementContainerDefinition processContainer = sDefinition.getProcessContainer(); final SFlowNodeDefinition flowNode = processContainer.getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); return flowNode.getDefaultTransition(); } FlowNodeTransitionsWrapper buildTransitionsWrapper(final SFlowNodeDefinition flowNode, final SProcessDefinition sProcessDefinition, final SFlowNodeInstance child) throws SBonitaException { final FlowNodeTransitionsWrapper transitionsDescriptor = new FlowNodeTransitionsWrapper(); // Retrieve all outgoing transitions if (flowNode == null) { // not in definition transitionsDescriptor.setInputTransitionsSize(0); transitionsDescriptor.setAllOutgoingTransitionDefinitions(Collections. emptyList()); } else { transitionsDescriptor.setInputTransitionsSize(flowNode.getIncomingTransitions().size()); transitionsDescriptor .setAllOutgoingTransitionDefinitions(new ArrayList<>(flowNode.getOutgoingTransitions())); transitionsDescriptor.setDefaultTransition(flowNode.getDefaultTransition()); } // Evaluate all outgoing transitions, and retrieve valid outgoing transitions transitionsDescriptor.setValidOutgoingTransitionDefinitions(evaluateOutgoingTransitions(transitionsDescriptor, sProcessDefinition, child)); return transitionsDescriptor; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/WaitingEventsInterrupter.java ================================================ /** * 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.execution; import java.util.ArrayList; import java.util.Collections; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SCatchEventDefinition; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceDeletionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingEventKeyProviderBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.execution.job.JobNameBuilder; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Elias Ricken de Medeiros */ @Slf4j public class WaitingEventsInterrupter { private final EventInstanceService eventInstanceService; private final SchedulerService schedulerService; private static final int MAX_NUMBER_OF_RESULTS = 100; public WaitingEventsInterrupter(final EventInstanceService eventInstanceService, final SchedulerService schedulerService) { this.eventInstanceService = eventInstanceService; this.schedulerService = schedulerService; } public void interruptWaitingEvents(final SProcessDefinition processDefinition, final SCatchEventInstance catchEventInstance, final SCatchEventDefinition catchEventDef) throws SBonitaException { interruptTimerEvent(processDefinition, catchEventInstance, catchEventDef); // message, signal and error interruptWaitingEvents(catchEventInstance.getId(), catchEventDef); } private void interruptWaitingEvents(final long instanceId, final SCatchEventDefinition catchEventDef) throws SBonitaReadException, SWaitingEventModificationException { if (!catchEventDef.getEventTriggers().isEmpty()) { interruptWaitingEvents(instanceId, SWaitingEvent.class); } } public void interruptWaitingEvents(final SFlowNodeInstance flowNodeInstance) throws SBonitaException { if (flowNodeInstance instanceof SReceiveTaskInstance || flowNodeInstance instanceof SIntermediateCatchEventInstance || flowNodeInstance instanceof SBoundaryEventInstance) { interruptWaitingEvents(flowNodeInstance.getId(), SWaitingEvent.class); } } private void interruptWaitingEvents(final long instanceId, final Class waitingEventClass) throws SBonitaReadException, SWaitingEventModificationException { final QueryOptions queryOptions = getWaitingEventsQueryOptions(instanceId, waitingEventClass); final QueryOptions countOptions = getWaitingEventsCountOptions(instanceId, waitingEventClass); long count = 0; List waitingEvents; do { waitingEvents = eventInstanceService.searchWaitingEvents(waitingEventClass, queryOptions); count = eventInstanceService.getNumberOfWaitingEvents(waitingEventClass, countOptions); deleteWaitingEvents(waitingEvents); } while (count > waitingEvents.size()); } private void deleteWaitingEvents(final List waitingEvents) throws SWaitingEventModificationException { for (final SWaitingEvent sWaitingEvent : waitingEvents) { eventInstanceService.deleteWaitingEvent(sWaitingEvent); } } private QueryOptions getWaitingEventsCountOptions(final long instanceId, final Class waitingEventClass) { final List filters = getFilterForWaitingEventsToInterrupt(instanceId, waitingEventClass); return new QueryOptions(filters, null); } private QueryOptions getWaitingEventsQueryOptions(final long instanceId, final Class waitingEventClass) { final OrderByOption orderByOption = new OrderByOption(waitingEventClass, BuilderFactory.get(SWaitingEventKeyProviderBuilderFactory.class).getIdKey(), OrderByType.ASC); final List filters = getFilterForWaitingEventsToInterrupt(instanceId, waitingEventClass); return new QueryOptions(0, MAX_NUMBER_OF_RESULTS, Collections.singletonList(orderByOption), filters, null); } private List getFilterForWaitingEventsToInterrupt(final long instanceId, final Class waitingEventClass) { final SWaitingEventKeyProviderBuilderFactory waitingEventKeyProvider = BuilderFactory .get(SWaitingEventKeyProviderBuilderFactory.class); final List filters = new ArrayList(2); filters.add( new FilterOption(waitingEventClass, waitingEventKeyProvider.getFlowNodeInstanceIdKey(), instanceId)); filters.add(new FilterOption(waitingEventClass, waitingEventKeyProvider.getActiveKey(), true)); return filters; } private void interruptTimerEvent(final SProcessDefinition processDefinition, final SCatchEventInstance catchEventInstance, final SCatchEventDefinition catchEventDef) throws SSchedulerException, SBonitaReadException { // FIXME to support multiple events change this code if (!catchEventDef.getTimerEventTriggerDefinitions().isEmpty()) { final String jobName = JobNameBuilder.getTimerEventJobName(processDefinition.getId(), catchEventDef, catchEventInstance); final boolean delete = schedulerService.delete(jobName); try { eventInstanceService.deleteEventTriggerInstanceOfFlowNode(catchEventInstance.getId()); } catch (SEventTriggerInstanceDeletionException e) { log.warn("Unable to delete event trigger of flow node instance {} because: {}", catchEventInstance, e.getMessage()); } if (!delete) { log.warn("No job found with name '{}' when interrupting timer catch event named '{}' and id '{}'." + " It was probably already triggered.", jobName, catchEventDef.getName(), catchEventDef.getName()); } } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/archive/BPMArchiverService.java ================================================ /** * 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.execution.archive; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.SArchivingException; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService; import org.bonitasoft.engine.business.data.SDataRetentionBdmTrackingException; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.classloader.SClassLoaderException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.contract.data.ContractDataService; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.comment.model.SComment; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; 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.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceModificationException; import org.bonitasoft.engine.core.process.instance.model.SAbstractConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; 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.SLoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAAutomaticTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SACallActivityInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAGatewayInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SALoopActivityInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAManualTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAMultiInstanceActivityInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAReceiveTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SASendTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SASubProcessActivityInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.recorder.SRecorderException; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta * @author Celine Souchet */ @Slf4j public class BPMArchiverService { private final ArchiveService archiveService; private final ProcessInstanceService processInstanceService; private final DocumentService documentService; private final SCommentService commentService; private final ProcessDefinitionService processDefinitionService; private final ConnectorInstanceService connectorInstanceService; private final ClassLoaderService classLoaderService; private final RefBusinessDataService refBusinessDataService; private final ContractDataService contractDataService; private final DataInstanceService dataInstanceService; private final ActivityInstanceService activityInstanceService; private final BPMFailureService bpmFailureService; private final DataRetentionBdmTrackingService dataRetentionBdmTrackingService; private static final int BATCH_SIZE = 100; public BPMArchiverService(ArchiveService archiveService, ProcessInstanceService processInstanceService, DocumentService documentService, SCommentService commentService, ProcessDefinitionService processDefinitionService, ConnectorInstanceService connectorInstanceService, ClassLoaderService classLoaderService, RefBusinessDataService refBusinessDataService, ContractDataService contractDataService, DataInstanceService dataInstanceService, ActivityInstanceService activityInstanceService, BPMFailureService bpmFailureService, DataRetentionBdmTrackingService dataRetentionBdmTrackingService) { this.archiveService = archiveService; this.processInstanceService = processInstanceService; this.documentService = documentService; this.commentService = commentService; this.processDefinitionService = processDefinitionService; this.connectorInstanceService = connectorInstanceService; this.classLoaderService = classLoaderService; this.refBusinessDataService = refBusinessDataService; this.contractDataService = contractDataService; this.dataInstanceService = dataInstanceService; this.activityInstanceService = activityInstanceService; this.bpmFailureService = bpmFailureService; this.dataRetentionBdmTrackingService = dataRetentionBdmTrackingService; } public void archiveAndDeleteProcessInstance(final SProcessInstance processInstance) throws SArchivingException { //set the classloader to this process because we need it e.g. to archive data instance ClassLoader processClassLoader; try { processClassLoader = classLoaderService.getClassLoader( identifier(ScopeType.PROCESS, processInstance.getProcessDefinitionId())); } catch (SClassLoaderException e) { throw new SArchivingException(e); } ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(processClassLoader); try { final SAProcessInstance saProcessInstance = buildArchiveProcessInstance(processInstance); SProcessDefinition processDefinition; try { processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); } catch (final SBonitaException e) { throw new SArchivingException(e); } final long archiveDate = saProcessInstance.getEndDate(); // The archive of data instance is not done because it is done on creation + when updating. // Archive SComment archiveComments(processDefinition, processInstance, archiveDate); // archive document mappings archiveDocumentMappings(processDefinition, processInstance, archiveDate); archiveConnectorInstancesIfAny(processInstance, processDefinition, archiveDate); archiveRefBusinessDataInstances(processInstance.getId()); //process instance failures archiveProcessInstanceFailures(processInstance, archiveDate); // Archive archiveAndDeleteProcessInstanceObject(processDefinition, processInstance, saProcessInstance, archiveDate); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } protected void archiveConnectorInstancesIfAny(SProcessInstance processInstance, SProcessDefinition processDefinition, long archiveDate) throws SArchivingException { if (!processDefinition.getProcessContainer().getConnectors().isEmpty()) { archiveConnectors(archiveDate, processInstance.getId(), SConnectorInstance.PROCESS_TYPE); } } protected SAProcessInstance buildArchiveProcessInstance(SProcessInstance processInstance) { return BuilderFactory.get(SAProcessInstanceBuilderFactory.class).createNewInstance(processInstance).done(); } private void archiveRefBusinessDataInstances(long processInstanceId) throws SArchivingException { try { List refBusinessDataInstances; int i = 0; do { refBusinessDataInstances = refBusinessDataService.getRefBusinessDataInstances(processInstanceId, i, i + BATCH_SIZE); i += BATCH_SIZE; for (final SRefBusinessDataInstance sRefBusinessDataInstance : refBusinessDataInstances) { refBusinessDataService.archiveRefBusinessDataInstance(sRefBusinessDataInstance); upsertTrackingForRefBusinessData(sRefBusinessDataInstance); } } while (refBusinessDataInstances.size() == BATCH_SIZE); } catch (final SBonitaException e) { throw new SArchivingException("Unable to archive RefBusinessDataInstance", e); } } /** * Upserts the {@code last_modified_at} timestamp in the {@code data_retention_bdm_tracking} * table for every BDM entity referenced by the given business data instance. *

Handles both single-valued references ({@link SSimpleRefBusinessDataInstance}) * and multivalued references ({@link SProcessMultiRefBusinessDataInstance}). * References with a {@code null} data ID (unassigned BDM variable) are skipped. */ private void upsertTrackingForRefBusinessData(SRefBusinessDataInstance refBusinessDataInstance) throws SDataRetentionBdmTrackingException { String dataClassname = refBusinessDataInstance.getDataClassName(); if (refBusinessDataInstance instanceof SSimpleRefBusinessDataInstance simpleRef) { Long dataId = simpleRef.getDataId(); if (dataId != null) { dataRetentionBdmTrackingService.upsert(dataId, dataClassname); } else { log.warn("Data id is null for SSimpleRefBusinessDataInstance with name {}, " + "cannot update tracking for business data with class name {}", simpleRef.getName(), dataClassname); } } else if (refBusinessDataInstance instanceof SProcessMultiRefBusinessDataInstance multiRef) { for (Long dataId : multiRef.getDataIds()) { if (dataId != null) { dataRetentionBdmTrackingService.upsert(dataId, dataClassname); } else { log.warn("Data id is null for SProcessMultiRefBusinessDataInstance with name {}, " + "cannot update tracking for business data with class name {}", multiRef.getName(), dataClassname); } } } else { // Should never happen log.warn("Unknown type of SRefBusinessDataInstance {}, " + "cannot update tracking for business data with id {} and class name {}", refBusinessDataInstance.getClass().getName(), refBusinessDataInstance.getId(), dataClassname); } } private void archiveConnectors(final long archiveDate, final long containerId, final String containerType) throws SArchivingException { try { List connectorInstances; int i = 0; do { connectorInstances = connectorInstanceService.getConnectorInstances(containerId, containerType, i, i + BATCH_SIZE, "id", OrderByType.ASC); i += BATCH_SIZE; for (final SConnectorInstance sConnectorInstance : connectorInstances) { connectorInstanceService.archiveConnectorInstance(sConnectorInstance, archiveDate); } } while (connectorInstances.size() == BATCH_SIZE); } catch (final SBonitaException e) { throw new SArchivingException("Unable to archive the container instance with id " + containerId, e); } } private void archiveAndDeleteProcessInstanceObject(final SProcessDefinition processDefinition, final SProcessInstance processInstance, final SAProcessInstance saProcessInstance, final long archiveDate) throws SArchivingException { try { final ArchiveInsertRecord insertRecord = new ArchiveInsertRecord(saProcessInstance); archiveService.recordInsert(archiveDate, insertRecord); if (log.isDebugEnabled()) { log.debug("Archiving process instance with id = <{}> and state {}", processInstance.getId(), processInstance.getStateId()); } try { processInstanceService.deleteProcessInstance(processInstance.getId()); } catch (final SBonitaException e) { throw new SArchivingException("Unable to delete the process instance during the archiving.", e); } } catch (final SRecorderException e) { setExceptionContext(processDefinition, processInstance, e); throw new SArchivingException("Unable to archive the process instance.", e); } } private void archiveDocumentMappings(final SProcessDefinition processDefinition, final SProcessInstance processInstance, final long archiveDate) throws SArchivingException { try { List mappedDocuments; int startIndex = 0; do { mappedDocuments = documentService.getDocumentsOfProcessInstance(processInstance.getId(), startIndex, BATCH_SIZE, null, null); for (final SMappedDocument mappedDocument : mappedDocuments) { documentService.archive(mappedDocument, archiveDate); } startIndex += BATCH_SIZE; } while (mappedDocuments.size() == BATCH_SIZE); } catch (final SBonitaException e) { setExceptionContext(processDefinition, processInstance, e); throw new SArchivingException("Unable to archive the process instance.", e); } } private void archiveComments(final SProcessDefinition processDefinition, final SProcessInstance processInstance, final long archiveDate) throws SArchivingException { try { List sComments; int startIndex = 0; do { sComments = commentService .getComments(processInstance.getId(), new QueryOptions(startIndex, BATCH_SIZE, SComment.class, "id", OrderByType.ASC)); for (final SComment sComment : sComments) { commentService.archive(archiveDate, sComment); } startIndex += BATCH_SIZE; } while (!sComments.isEmpty()); } catch (final SBonitaException e) { setExceptionContext(processDefinition, processInstance, e); throw new SArchivingException("Unable to archive the process instance comments.", e); } } public void archiveAndDeleteFlowNodeInstance(final SFlowNodeInstance flowNodeInstance, final long processDefinitionId) throws SArchivingException { try { final SProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); // Remove data instance + data visibility mapping archiveAndDeleteFlownodeInstance(flowNodeInstance, processDefinition, System.currentTimeMillis()); } catch (final SArchivingException e) { throw e; } catch (final SBonitaException e) { throw new SArchivingException(e); } } public void archiveFlowNodeInstance(final SFlowNodeInstance flowNodeInstance) throws SArchivingException { archiveFlowNodeInstance(flowNodeInstance, System.currentTimeMillis()); } private void archiveAndDeleteFlownodeInstance(SFlowNodeInstance flowNodeInstance, SProcessDefinition processDefinition, long archiveDate) throws SDataInstanceException, SArchivingException, SFlowNodeNotFoundException, SFlowNodeReadException, SProcessInstanceModificationException { if (flowNodeInstance instanceof SActivityInstance) { final SActivityDefinition activityDef = (SActivityDefinition) processDefinition .getProcessContainer().getFlowNode( flowNodeInstance.getFlowNodeDefinitionId()); // only do search for data instances with there are data definitions. Can be null if it's a manual data add at runtime if (activityDef != null && !activityDef.getSDataDefinitions().isEmpty()) { /* * Delete data instances defined at activity level: * We do not archive because it's done after update not before update */ deleteLocalDataInstancesFromActivityInstance(flowNodeInstance); } if (activityDef != null && !activityDef.getConnectors().isEmpty()) { archiveConnectors(archiveDate, flowNodeInstance.getId(), SAbstractConnectorInstance.FLOWNODE_TYPE); } } if (flowNodeInstance instanceof SUserTaskInstance) { archiveContractData(archiveDate, flowNodeInstance.getId()); } archiveFlowNodeFailures(flowNodeInstance, archiveDate); // then archive the flow node instance: archiveFlowNodeInstance(flowNodeInstance, archiveDate); // Reconnect the persisted object before deleting it: final SFlowNodeInstance flowNodeInstance2 = activityInstanceService .getFlowNodeInstance(flowNodeInstance.getId()); processInstanceService.deleteFlowNodeInstance(flowNodeInstance2, processDefinition); } private void archiveProcessInstanceFailures(SProcessInstance processInstance, long archiveDate) throws SArchivingException { try { bpmFailureService.archiveProcessInstanceFailures(processInstance.getId(), archiveDate); } catch (SBonitaException e) { throw new SArchivingException(e); } } private void archiveFlowNodeFailures(SFlowNodeInstance flowNodeInstance, long archiveDate) throws SArchivingException { try { bpmFailureService.archiveFlowNodeFailures(flowNodeInstance.getId(), archiveDate); } catch (SBonitaException e) { throw new SArchivingException(e); } } private void setExceptionContext(final SProcessDefinition processDefinition, final SProcessInstance processInstance, final SBonitaException e) { e.setProcessInstanceIdOnContext(processInstance.getId()); e.setRootProcessInstanceIdOnContext(processInstance.getRootProcessInstanceId()); e.setProcessDefinitionIdOnContext(processInstance.getProcessDefinitionId()); e.setProcessDefinitionNameOnContext(processDefinition.getName()); e.setProcessDefinitionVersionOnContext(processDefinition.getVersion()); } private void archiveFlowNodeInstance(final SFlowNodeInstance flowNodeInstance, final long archiveDate) throws SArchivingException { try { final SAFlowNodeInstance saFlowNodeInstance = getArchivedObject(flowNodeInstance); if (saFlowNodeInstance != null) { final ArchiveInsertRecord insertRecord = new ArchiveInsertRecord(saFlowNodeInstance); archiveService.recordInsert(archiveDate, insertRecord); } } catch (final SBonitaException e) { throw new SArchivingException(e); } } private SAFlowNodeInstance getArchivedObject(final SFlowNodeInstance flowNodeInstance) { SAFlowNodeInstance saFlowNodeInstance = null; switch (flowNodeInstance.getType()) {// TODO archive other flow node case AUTOMATIC_TASK: saFlowNodeInstance = BuilderFactory.get(SAAutomaticTaskInstanceBuilderFactory.class) .createNewAutomaticTaskInstance((SAutomaticTaskInstance) flowNodeInstance).done(); break; case GATEWAY: saFlowNodeInstance = BuilderFactory.get(SAGatewayInstanceBuilderFactory.class) .createNewGatewayInstance((SGatewayInstance) flowNodeInstance).done(); break; case MANUAL_TASK: saFlowNodeInstance = BuilderFactory.get(SAManualTaskInstanceBuilderFactory.class) .createNewManualTaskInstance((SManualTaskInstance) flowNodeInstance).done(); break; case USER_TASK: saFlowNodeInstance = BuilderFactory.get(SAUserTaskInstanceBuilderFactory.class) .createNewUserTaskInstance((SUserTaskInstance) flowNodeInstance) .done(); break; case RECEIVE_TASK: saFlowNodeInstance = BuilderFactory.get(SAReceiveTaskInstanceBuilderFactory.class) .createNewReceiveTaskInstance((SReceiveTaskInstance) flowNodeInstance).done(); break; case SEND_TASK: saFlowNodeInstance = BuilderFactory.get(SASendTaskInstanceBuilderFactory.class) .createNewSendTaskInstance((SSendTaskInstance) flowNodeInstance) .done(); break; case LOOP_ACTIVITY: saFlowNodeInstance = BuilderFactory.get(SALoopActivityInstanceBuilderFactory.class) .createNewLoopActivityInstance((SLoopActivityInstance) flowNodeInstance).done(); break; case CALL_ACTIVITY: saFlowNodeInstance = BuilderFactory.get(SACallActivityInstanceBuilderFactory.class) .createNewArchivedCallActivityInstance((SCallActivityInstance) flowNodeInstance).done(); break; case MULTI_INSTANCE_ACTIVITY: saFlowNodeInstance = BuilderFactory.get(SAMultiInstanceActivityInstanceBuilderFactory.class) .createNewMultiInstanceActivityInstance((SMultiInstanceActivityInstance) flowNodeInstance) .done(); break; case SUB_PROCESS: saFlowNodeInstance = BuilderFactory.get(SASubProcessActivityInstanceBuilderFactory.class) .createNewArchivedSubProcessActivityInstance((SSubProcessActivityInstance) flowNodeInstance) .done(); break; case END_EVENT: case START_EVENT: case BOUNDARY_EVENT: case INTERMEDIATE_CATCH_EVENT: case INTERMEDIATE_THROW_EVENT: default: break; } return saFlowNodeInstance; } private void deleteLocalDataInstancesFromActivityInstance(final SFlowNodeInstance flowNodeInstance) throws SDataInstanceException { List dataInstances; do { dataInstances = dataInstanceService.getLocalDataInstances(flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.toString(), 0, 100); for (final SDataInstance sDataInstance : dataInstances) { dataInstanceService.deleteDataInstance(sDataInstance); } } while (dataInstances.size() > 0); } private void archiveContractData(final long archiveDate, final long userTaskId) throws SArchivingException { try { contractDataService.archiveAndDeleteUserTaskData(userTaskId, archiveDate); } catch (final SBonitaException e) { throw new SArchivingException("Unable to archive contract data of container instance with id " + userTaskId, e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/CoupleEventHandlerStrategy.java ================================================ /** * 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.execution.event; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingEventKeyProviderBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Elias Ricken de Medeiros * @author Celine Souchet * @author Matthieu Chaffotte */ public abstract class CoupleEventHandlerStrategy extends EventHandlerStrategy { private final EventInstanceService eventInstanceService; private static final int MAX_NUMBER_OF_RESULTS = 100; public CoupleEventHandlerStrategy(final EventInstanceService eventInstanceService) { this.eventInstanceService = eventInstanceService; } @Override public void unregisterCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId, final SProcessInstance parentProcessInstance) throws SBonitaException { if (!eventDefinition.getEventTriggers().isEmpty()) { unregisterWaitingEvents(SWaitingEvent.class, subProcessId, parentProcessInstance); } } private QueryOptions getWaitingEventsQueryOptions(final Class waitingEventClass, final long subProcessId, final SProcessInstance parentProcessInstance) { final OrderByOption orderByOption = new OrderByOption(waitingEventClass, BuilderFactory.get(SWaitingEventKeyProviderBuilderFactory.class).getIdKey(), OrderByType.ASC); final List filters = getFilterForWaitingEventsToUnregister(waitingEventClass, subProcessId, parentProcessInstance); return new QueryOptions(0, MAX_NUMBER_OF_RESULTS, Collections.singletonList(orderByOption), filters, null); } private List getFilterForWaitingEventsToUnregister( final Class waitingEventClass, final long subProcessId, final SProcessInstance parentProcessInstance) { final SWaitingEventKeyProviderBuilderFactory waitingEventKeyProvider = BuilderFactory .get(SWaitingEventKeyProviderBuilderFactory.class); final List filters = new ArrayList(3); filters.add(new FilterOption(waitingEventClass, waitingEventKeyProvider.getSubProcessIdKey(), subProcessId)); filters.add(new FilterOption(waitingEventClass, waitingEventKeyProvider.getParentProcessInstanceIdKey(), parentProcessInstance.getId())); filters.add(new FilterOption(waitingEventClass, waitingEventKeyProvider.getActiveKey(), true)); return filters; } private void unregisterWaitingEvents(final Class waitingEventClass, final long subProcessId, final SProcessInstance parentProcessInstance) throws SBonitaReadException, SWaitingEventModificationException { final QueryOptions queryOptions = getWaitingEventsQueryOptions(waitingEventClass, subProcessId, parentProcessInstance); List waitingEvents; do { waitingEvents = eventInstanceService.searchWaitingEvents(waitingEventClass, queryOptions); deleteWaitingEvents(waitingEvents); } while (waitingEvents.size() > 0); } private void deleteWaitingEvents(final List waitingEvents) throws SWaitingEventModificationException { for (final SWaitingEvent sWaitingEvent : waitingEvents) { eventInstanceService.deleteWaitingEvent(sWaitingEvent); } } protected EventInstanceService getEventInstanceService() { return eventInstanceService; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/ErrorEventHandlerStrategy.java ================================================ /** * 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.execution.event; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SCallActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchErrorEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SErrorEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventReadException; import org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; 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.SCallActivityInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.execution.ProcessInstanceInterruptor; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Elias Ricken de Medeiros * @author Celine Souchet * @author Matthieu Chaffotte */ public class ErrorEventHandlerStrategy extends CoupleEventHandlerStrategy { private static final OperationsWithContext EMPTY = new OperationsWithContext(null, null); private final ProcessInstanceService processInstanceService; private final FlowNodeInstanceService flowNodeInstanceService; private final ProcessDefinitionService processDefinitionService; private final EventsHandler eventsHandler; private static final Logger LOGGER = LoggerFactory.getLogger(ErrorEventHandlerStrategy.class); private ProcessInstanceInterruptor processInstanceInterruptor; public ErrorEventHandlerStrategy(final EventInstanceService eventInstanceService, final ProcessInstanceService processInstanceService, final FlowNodeInstanceService flowNodeInstanceService, final ProcessDefinitionService processDefinitionService, final EventsHandler eventsHandler, ProcessInstanceInterruptor processInstanceInterruptor) { super(eventInstanceService); this.processInstanceService = processInstanceService; this.flowNodeInstanceService = flowNodeInstanceService; this.processDefinitionService = processDefinitionService; this.eventsHandler = eventsHandler; this.processInstanceInterruptor = processInstanceInterruptor; } @Override public void handleThrowEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SThrowEventInstance eventInstance, final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException { LOGGER.debug("Error event is thrown, error code = {} process instance = {}", ((SErrorEventTriggerDefinition) sEventTriggerDefinition).getErrorCode(), eventInstance.getRootContainerId()); processInstanceService.setInterruptingEventId(eventInstance.getParentProcessInstanceId(), eventInstance.getId()); processInstanceInterruptor.interruptChildrenOfProcessInstance(eventInstance.getParentContainerId(), SStateCategory.ABORTING, eventInstance.getId()); } @Override public boolean handlePostThrowEvent(final SProcessDefinition processDefinition, final SEndEventDefinition sEventDefinition, final SThrowEventInstance sThrowEventInstance, final SEventTriggerDefinition sEventTriggerDefinition, final SFlowNodeInstance sFlowNodeInstance) throws SBonitaException { boolean hasActionToExecute = false; final SFlowNodeInstanceBuilderFactory flowNodeKeyProvider = BuilderFactory .get(SIntermediateThrowEventInstanceBuilderFactory.class); final long parentProcessInstanceId = sThrowEventInstance .getLogicalGroup(flowNodeKeyProvider.getParentProcessInstanceIndex()); final SErrorEventTriggerDefinition errorTrigger = (SErrorEventTriggerDefinition) sEventTriggerDefinition; final SWaitingErrorEvent waitingErrorEvent = getWaitingErrorEvent(processDefinition.getProcessContainer(), parentProcessInstanceId, errorTrigger, sThrowEventInstance, sFlowNodeInstance); if (waitingErrorEvent != null) { eventsHandler.triggerCatchEvent(waitingErrorEvent, sThrowEventInstance.getId()); hasActionToExecute = true; } else { LOGGER.warn( "No catch error event was defined to handle the error code {} defined in the process [name: {}, version: {}], throw event: {}. This throw error event will act as a Terminate Event.", errorTrigger.getErrorCode(), processDefinition.getName(), processDefinition.getVersion(), sEventDefinition == null ? null : sEventDefinition.getName()); } return hasActionToExecute; } private SWaitingErrorEvent getWaitingErrorEvent(final SFlowElementContainerDefinition container, final long parentProcessInstanceId, final SErrorEventTriggerDefinition errorTrigger, final SThrowEventInstance eventInstance, final SFlowNodeInstance flowNodeInstance) throws SBonitaException { final SProcessInstance processInstance = processInstanceService.getProcessInstance(parentProcessInstanceId); final String errorCode = errorTrigger.getErrorCode(); SWaitingErrorEvent waitingErrorEvent; // check on direct boundary waitingErrorEvent = getWaitingErrorEventFromBoundary(eventInstance, errorCode, flowNodeInstance); // check on event sub-process if (waitingErrorEvent == null) { waitingErrorEvent = getWaitingErrorEventSubProcess(container, parentProcessInstanceId, errorCode); } // check on call activities (recursive) if (waitingErrorEvent == null && processInstance.getCallerId() != -1 && SFlowNodeType.CALL_ACTIVITY.equals(processInstance.getCallerType())) { // check on call activities waitingErrorEvent = getWaitingErrorEventFromCallActivity(errorTrigger, processInstance, eventInstance, errorCode, flowNodeInstance); } return waitingErrorEvent; } protected SWaitingErrorEvent getWaitingErrorEventFromBoundary(final SThrowEventInstance eventInstance, final String errorCode, final SFlowNodeInstance flowNodeInstance) throws SBonitaException { final SFlowNodeInstanceBuilderFactory flowNodeKeyProvider = BuilderFactory .get(SBoundaryEventInstanceBuilderFactory.class); // get the parent activity of the boundary final long logicalGroup = eventInstance.getLogicalGroup(flowNodeKeyProvider.getParentActivityInstanceIndex()); if (logicalGroup <= 0) { // not in an activity = no boundary return null; } final long processDefinitionId = flowNodeInstance .getLogicalGroup(flowNodeKeyProvider.getProcessDefinitionIndex()); final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId); final SActivityDefinition flowNode = (SActivityDefinition) processDefinition.getProcessContainer().getFlowNode( flowNodeInstance.getFlowNodeDefinitionId()); final List boundaryEventDefinitions = flowNode.getBoundaryEventDefinitions(); SWaitingErrorEvent waitingErrorEvent; if (flowNode.getLoopCharacteristics() == null) { waitingErrorEvent = getWaitingErrorEventFromBoundary(errorCode, flowNodeInstance, boundaryEventDefinitions); } else { final long multipleInstanceActivityId = flowNodeInstance .getLogicalGroup(flowNodeKeyProvider.getParentActivityInstanceIndex()); final SFlowNodeInstance miActivityInstance = flowNodeInstanceService .getFlowNodeInstance(multipleInstanceActivityId); waitingErrorEvent = getWaitingErrorEventFromBoundary(errorCode, miActivityInstance, boundaryEventDefinitions); } return waitingErrorEvent; } private SWaitingErrorEvent getWaitingErrorEventFromCallActivity(final SErrorEventTriggerDefinition errorTrigger, final SProcessInstance processInstance, final SThrowEventInstance eventInstance, final String errorCode, final SFlowNodeInstance flowNodeInstance) throws SBonitaException { final SFlowNodeInstanceBuilderFactory flowNodeKeyProvider = BuilderFactory .get(SCallActivityInstanceBuilderFactory.class); final SCallActivityInstance callActivityInstance = (SCallActivityInstance) flowNodeInstanceService .getFlowNodeInstance(processInstance.getCallerId()); final long processDefinitionId = callActivityInstance .getLogicalGroup(flowNodeKeyProvider.getProcessDefinitionIndex()); final SProcessDefinition callActivityContainer = processDefinitionService .getProcessDefinition(processDefinitionId); final SCallActivityDefinition callActivityDef = (SCallActivityDefinition) callActivityContainer .getProcessContainer().getFlowNode( callActivityInstance.getFlowNodeDefinitionId()); final List boundaryEventDefinitions = callActivityDef.getBoundaryEventDefinitions(); SWaitingErrorEvent waitingErrorEvent; if (callActivityDef.getLoopCharacteristics() != null) { final long multipleInstanceActivityId = callActivityInstance .getLogicalGroup(flowNodeKeyProvider.getParentActivityInstanceIndex()); final SFlowNodeInstance miActivityInstance = flowNodeInstanceService .getFlowNodeInstance(multipleInstanceActivityId); waitingErrorEvent = getWaitingErrorEventFromBoundary(errorCode, miActivityInstance, boundaryEventDefinitions); } else { waitingErrorEvent = getWaitingErrorEventFromBoundary(errorCode, callActivityInstance, boundaryEventDefinitions); } if (waitingErrorEvent == null) { final long callActivityParentProcInstId = callActivityInstance .getLogicalGroup(flowNodeKeyProvider.getParentProcessInstanceIndex()); waitingErrorEvent = getWaitingErrorEvent(callActivityContainer.getProcessContainer(), callActivityParentProcInstId, errorTrigger, eventInstance, flowNodeInstance); } return waitingErrorEvent; } protected SWaitingErrorEvent getWaitingErrorEventFromBoundary(final String errorCode, final SFlowNodeInstance flowNodeInstance, final List boundaryEventDefinitions) throws SWaitingEventReadException { boolean canHandleError; String catchingErrorCode = errorCode; canHandleError = containsHandler(boundaryEventDefinitions, catchingErrorCode); if (!canHandleError) { catchingErrorCode = null; // catch all errors canHandleError = containsHandler(boundaryEventDefinitions, catchingErrorCode); // check for a handler that is able to catch all error codes } if (canHandleError) { return getEventInstanceService().getBoundaryWaitingErrorEvent(flowNodeInstance.getId(), catchingErrorCode); } return null; } private SWaitingErrorEvent getWaitingErrorEventSubProcess(final SFlowElementContainerDefinition container, final long parentProcessInstanceId, final String errorCode) throws SBonitaReadException, SBPMEventHandlerException { String catchingErrorCode = errorCode; boolean canHandleError = hasEventSubProcessCatchingError(container, catchingErrorCode); if (!canHandleError) { // if there is no event sub-process catching that particular error, we search for an event sub process that catch all kind of error catchingErrorCode = null; canHandleError = hasEventSubProcessCatchingError(container, catchingErrorCode); } SWaitingErrorEvent waitingErrorEvent = null; if (canHandleError) { final SWaitingErrorEventBuilderFactory waitingErrorEventKeyProvider = BuilderFactory .get(SWaitingErrorEventBuilderFactory.class); final OrderByOption orderByOption = new OrderByOption(SWaitingEvent.class, waitingErrorEventKeyProvider.getFlowNodeNameKey(), OrderByType.ASC); final List filters = new ArrayList<>(3); filters.add(new FilterOption(SWaitingErrorEvent.class, waitingErrorEventKeyProvider.getErrorCodeKey(), catchingErrorCode)); filters.add(new FilterOption(SWaitingErrorEvent.class, waitingErrorEventKeyProvider.getEventTypeKey(), SBPMEventType.EVENT_SUB_PROCESS.name())); filters.add(new FilterOption(SWaitingErrorEvent.class, waitingErrorEventKeyProvider.getParentProcessInstanceIdKey(), parentProcessInstanceId)); final QueryOptions queryOptions = new QueryOptions(0, 2, Collections.singletonList(orderByOption), filters, null); final List waitingEvents = getEventInstanceService() .searchWaitingEvents(SWaitingErrorEvent.class, queryOptions); if (waitingEvents.size() != 1) { final StringBuilder stb = new StringBuilder(); stb.append("One and only one error start event sub-process was expected for the process instance "); stb.append(parentProcessInstanceId); stb.append(" and error code "); stb.append(catchingErrorCode); stb.append(", but "); stb.append(waitingEvents.size()); stb.append(" was found."); throw new SBPMEventHandlerException(stb.toString()); } waitingErrorEvent = waitingEvents.get(0); } return waitingErrorEvent; } private boolean containsHandler(final List boundaryEventDefinitions, final String errorCode) { boolean found = false; final Iterator iterator = boundaryEventDefinitions.iterator(); while (iterator.hasNext() && !found) { final SBoundaryEventDefinition boundaryEventDefinition = iterator.next(); final SCatchErrorEventTriggerDefinition currentErrorTrigger = boundaryEventDefinition .getErrorEventTriggerDefinition(errorCode); if (currentErrorTrigger != null) { found = true; } } return found; } private boolean hasEventSubProcessCatchingError(final SFlowElementContainerDefinition container, final String errorCode) { boolean found = false; final Iterator iterator = container.getActivities().iterator(); while (iterator.hasNext() && !found) { final SActivityDefinition activity = iterator.next(); if (SFlowNodeType.SUB_PROCESS.equals(activity.getType()) && ((SSubProcessDefinition) activity).isTriggeredByEvent()) { final SSubProcessDefinition eventSubProcess = (SSubProcessDefinition) activity; final SStartEventDefinition startEventDefinition = eventSubProcess.getSubProcessContainer() .getStartEvents().get(0); if (startEventDefinition.getErrorEventTriggerDefinition(errorCode) != null) { found = true; } } } return found; } @Override public void handleCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SCatchEventInstance eventInstance, final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException { final SWaitingErrorEventBuilderFactory builderFact = BuilderFactory.get(SWaitingErrorEventBuilderFactory.class); final SErrorEventTriggerDefinition errorEventTriggerDefinition = (SErrorEventTriggerDefinition) sEventTriggerDefinition; final SEventInstanceBuilderFactory eventInstanceKeyProvider = BuilderFactory .get(SIntermediateCatchEventInstanceBuilderFactory.class); switch (eventDefinition.getType()) { case BOUNDARY_EVENT: final SBoundaryEventInstance boundary = (SBoundaryEventInstance) eventInstance; final long rootProcessInstanceId = eventInstance .getLogicalGroup(eventInstanceKeyProvider.getRootProcessInstanceIndex()); final long parentProcessInstanceId = eventInstance .getLogicalGroup(eventInstanceKeyProvider.getParentProcessInstanceIndex()); final SWaitingErrorEventBuilder builder = builderFact.createNewWaitingErrorBoundaryEventInstance( processDefinition.getId(), rootProcessInstanceId, parentProcessInstanceId, eventInstance.getId(), errorEventTriggerDefinition.getErrorCode(), processDefinition.getName(), eventInstance.getFlowNodeDefinitionId(), eventInstance.getName(), boundary.getActivityInstanceId()); final SWaitingErrorEvent errorEvent = builder.done(); getEventInstanceService().createWaitingEvent(errorEvent); break; case INTERMEDIATE_CATCH_EVENT: case START_EVENT: throw new SWaitingEventCreationException( "Catch error event cannot be put in " + eventDefinition.getType() + ". They must be used as boundary events or start event subprocess."); default: throw new SWaitingEventCreationException(eventDefinition.getType() + " is not a catch event."); } } @Override public OperationsWithContext getOperations(final SWaitingEvent waitingEvent, final Long triggeringElementID) { return EMPTY; } @Override public void handleEventSubProcess(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId, final SProcessInstance parentProcessInstance) throws SBonitaException { final SWaitingErrorEventBuilderFactory builderFact = BuilderFactory.get(SWaitingErrorEventBuilderFactory.class); final SErrorEventTriggerDefinition trigger = (SErrorEventTriggerDefinition) sEventTriggerDefinition; final SWaitingErrorEventBuilder builder = builderFact.createNewWaitingErrorEventSubProcInstance( processDefinition.getId(), parentProcessInstance.getId(), parentProcessInstance.getRootProcessInstanceId(), trigger.getErrorCode(), processDefinition.getName(), eventDefinition.getId(), eventDefinition.getName(), subProcessId); final SWaitingErrorEvent event = builder.done(); getEventInstanceService().createWaitingEvent(event); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/EventHandlerStrategy.java ================================================ /** * 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.execution.event; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition; 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.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; /** * Strategy to handle one kind of event: TIMER, ERROR, SIGNAL or MESSAGE * * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Celine Souchet */ public abstract class EventHandlerStrategy { public EventHandlerStrategy() { super(); } public abstract void handleThrowEvent(SProcessDefinition processDefinition, SEventDefinition eventDefinition, SThrowEventInstance eventInstance, SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException; public abstract void handleCatchEvent(SProcessDefinition processDefinition, SEventDefinition eventDefinition, SCatchEventInstance eventInstance, SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException; public abstract void handleEventSubProcess(SProcessDefinition processDefinition, SEventDefinition eventDefinition, SEventTriggerDefinition sEventTriggerDefinition, long subProcessId, SProcessInstance parentProcessInstance) throws SBonitaException; public abstract void unregisterCatchEvent(SProcessDefinition processDefinition, SEventDefinition eventDefinition, SEventTriggerDefinition sEventTriggerDefinition, long subProcessId, SProcessInstance parentProcessIsnstance) throws SBonitaException; public abstract OperationsWithContext getOperations(SWaitingEvent waitingEvent, Long triggeringElementID) throws SBonitaException; public void handleThrowEvent(final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException { } public boolean handlePostThrowEvent(final SProcessDefinition processDefinition, final SEndEventDefinition sEventDefinition, final SThrowEventInstance sThrowEventInstance, final SEventTriggerDefinition sEventTriggerDefinition, final SFlowNodeInstance sFlowNodeInstance) throws SBonitaException { return false; } protected DataInstanceContainer getParentContainerType(final SFlowNodeInstance flowNodeInstance) { DataInstanceContainer parentContainerType; if (SFlowElementsContainerType.PROCESS.equals(flowNodeInstance.getParentContainerType())) { parentContainerType = DataInstanceContainer.PROCESS_INSTANCE; } else { parentContainerType = DataInstanceContainer.ACTIVITY_INSTANCE; } return parentContainerType; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/EventsHandler.java ================================================ /** * 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.execution.event; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Supplier; import org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContent; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.operation.OperationService; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; 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.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SReceiveTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.SSendTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; 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.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException; 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.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.execution.ContainerRegistry; import org.bonitasoft.engine.execution.ProcessExecutor; import org.bonitasoft.engine.execution.ProcessInstanceInterruptor; import org.bonitasoft.engine.execution.work.BPMWorkFactory; import org.bonitasoft.engine.expression.exception.SExpressionException; import org.bonitasoft.engine.mdc.MDCHelper; import org.bonitasoft.engine.mdc.MDCHelper.CheckedRunnable2; import org.bonitasoft.engine.mdc.ProcessInstanceMDC; import org.bonitasoft.engine.message.MessagesHandlingService; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.transaction.STransactionNotFoundException; import org.bonitasoft.engine.work.WorkService; /** * Handle event depending on its type * TODO * * Move all code that instantiate process/execute flow node after a event was triggered here + make it call * reachedCatchEvent * * For event sub process: the instantiate process must cancel all activities then instantiate the process * * the instantiate event subprocess method is like the start of the process executor but with less things: factorise * it * * check that there is no execution issues with event sub process (add more test) * * add test for each kind of start event in event sub process * * try to trigger event subprocess with multiple events * * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class EventsHandler { private final Map handlers; private final ContainerRegistry containerRegistry; private final ProcessDefinitionService processDefinitionService; private final EventInstanceService eventInstanceService; private final BPMInstancesCreator bpmInstancesCreator; private final ProcessInstanceService processInstanceService; private final OperationService operationService; private ProcessExecutor processExecutor; private ProcessInstanceInterruptor processInstanceInterruptor; private FlowNodeInstanceService flowNodeInstanceService; public EventsHandler(final SchedulerService schedulerService, final ExpressionResolverService expressionResolverService, final EventInstanceService eventInstanceService, final BPMInstancesCreator bpmInstancesCreator, final ProcessDefinitionService processDefinitionService, final ContainerRegistry containerRegistry, final ProcessInstanceService processInstanceService, final FlowNodeInstanceService flowNodeInstanceService, OperationService operationService, MessagesHandlingService messagesHandlingService, WorkService workService, BPMWorkFactory workFactory, ProcessInstanceInterruptor processInstanceInterruptor) { this.eventInstanceService = eventInstanceService; this.processDefinitionService = processDefinitionService; this.containerRegistry = containerRegistry; this.bpmInstancesCreator = bpmInstancesCreator; this.processInstanceService = processInstanceService; this.operationService = operationService; this.flowNodeInstanceService = flowNodeInstanceService; this.processInstanceInterruptor = processInstanceInterruptor; handlers = new HashMap<>(4); handlers.put(SEventTriggerType.TIMER, new TimerEventHandlerStrategy(expressionResolverService, schedulerService, eventInstanceService)); handlers.put(SEventTriggerType.MESSAGE, new MessageEventHandlerStrategy(expressionResolverService, eventInstanceService, bpmInstancesCreator, processDefinitionService, messagesHandlingService)); handlers.put(SEventTriggerType.SIGNAL, new SignalEventHandlerStrategy(eventInstanceService, workService, workFactory)); handlers.put(SEventTriggerType.TERMINATE, new TerminateEventHandlerStrategy(processInstanceInterruptor)); handlers.put(SEventTriggerType.ERROR, new ErrorEventHandlerStrategy(eventInstanceService, processInstanceService, flowNodeInstanceService, processDefinitionService, this, processInstanceInterruptor)); } public void setProcessExecutor(final ProcessExecutor processExecutor) { this.processExecutor = processExecutor; } /** * called when a catchEvent is reached * e.g. we are going on a catch event in the flow of a process * This is different of trigger catch event: * e.g. for a message handleCatchEvent will create the waiting event and triggerCatchEvent is called when the * message is received * * @param processDefinition * @param eventDefinition * @param eventInstance * @throws SBonitaException */ public void handleCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SEventInstance eventInstance) throws SBonitaException { final List eventTriggers = eventDefinition.getEventTriggers(); for (final SEventTriggerDefinition sEventTriggerDefinition : eventTriggers) { final EventHandlerStrategy eventHandlerStrategy = handlers .get(sEventTriggerDefinition.getEventTriggerType()); eventHandlerStrategy.handleCatchEvent(processDefinition, eventDefinition, (SCatchEventInstance) eventInstance, sEventTriggerDefinition); } } public void handleCatchMessage(final SProcessDefinition processDefinition, final SReceiveTaskDefinition receiveTaskDefinition, final SReceiveTaskInstance receiveTaskInstance) throws SBonitaException { final SEventTriggerDefinition eventTrigger = receiveTaskDefinition.getTrigger(); final MessageEventHandlerStrategy messageEventHandlerStrategy = (MessageEventHandlerStrategy) handlers .get(SEventTriggerType.MESSAGE); messageEventHandlerStrategy.handleCatchEvent(processDefinition, receiveTaskInstance, eventTrigger); } /** * called when a star subprocess is reached * e.g. we are going on a catch event in the flow of a process * This is different of trigger catch event: * e.g. for a message handleCatchEvent will create the waiting event and triggerCatchEvent is called when the * message is received * * @param processDefinition * @param eventDefinition * @param parentProcessInstance * @throws SBonitaException */ private void handleEventSubProcess(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final long subProcessId, final SProcessInstance parentProcessInstance) throws SBonitaException { final List eventTriggers = eventDefinition.getEventTriggers(); for (final SEventTriggerDefinition sEventTriggerDefinition : eventTriggers) { final EventHandlerStrategy eventHandlerStrategy = handlers .get(sEventTriggerDefinition.getEventTriggerType()); eventHandlerStrategy.handleEventSubProcess(processDefinition, eventDefinition, sEventTriggerDefinition, subProcessId, parentProcessInstance); } } public void handleEventSubProcess(final SProcessDefinition sDefinition, final SProcessInstance parentProcessInstance) throws SBonitaException { final Set activities = sDefinition.getProcessContainer().getActivities(); for (final SActivityDefinition activity : activities) { if (SFlowNodeType.SUB_PROCESS.equals(activity.getType()) && ((SSubProcessDefinition) activity).isTriggeredByEvent()) { final SSubProcessDefinition eventSubProcess = (SSubProcessDefinition) activity; final SStartEventDefinition sStartEventDefinition = eventSubProcess.getSubProcessContainer() .getStartEvents().get(0); handleEventSubProcess(sDefinition, sStartEventDefinition, activity.getId(), parentProcessInstance); } } } private void unregisterEventSubProcess(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final long subProcessId, final SProcessInstance parentProcessInstance) throws SBonitaException { final List eventTriggers = eventDefinition.getEventTriggers(); for (final SEventTriggerDefinition sEventTriggerDefinition : eventTriggers) { final EventHandlerStrategy eventHandlerStrategy = handlers .get(sEventTriggerDefinition.getEventTriggerType()); eventHandlerStrategy.unregisterCatchEvent(processDefinition, eventDefinition, sEventTriggerDefinition, subProcessId, parentProcessInstance); } } public void unregisterEventSubProcess(final SProcessDefinition sDefinition, final SProcessInstance parentProcessInstance) throws SBonitaException { final Set activities = sDefinition.getProcessContainer().getActivities(); for (final SActivityDefinition activity : activities) { if (SFlowNodeType.SUB_PROCESS.equals(activity.getType()) && ((SSubProcessDefinition) activity).isTriggeredByEvent()) { final SSubProcessDefinition eventSubProcess = (SSubProcessDefinition) activity; final SStartEventDefinition sStartEventDefinition = eventSubProcess.getSubProcessContainer() .getStartEvents().get(0); unregisterEventSubProcess(sDefinition, sStartEventDefinition, activity.getId(), parentProcessInstance); } } } /** * called when we reach a throw event in the flow of a process * * @param processDefinition * @param eventDefinition * @param eventInstance * @throws SBonitaException */ public void handleThrowEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SEventInstance eventInstance) throws SBonitaException { final List eventTriggers = eventDefinition.getEventTriggers(); for (final SEventTriggerDefinition sEventTriggerDefinition : eventTriggers) { final EventHandlerStrategy eventHandlerStrategy = handlers .get(sEventTriggerDefinition.getEventTriggerType()); if (eventHandlerStrategy != null) { eventHandlerStrategy.handleThrowEvent(processDefinition, eventDefinition, (SThrowEventInstance) eventInstance, sEventTriggerDefinition); } } } public void handleThrowMessage(final SProcessDefinition processDefinition, final SSendTaskDefinition sendTaskDefinition, final SSendTaskInstance sendTaskInstance) throws SMessageInstanceCreationException, SDataInstanceException, SExpressionException, STransactionNotFoundException { final SThrowMessageEventTriggerDefinition eventTrigger = sendTaskDefinition.getMessageTrigger(); final MessageEventHandlerStrategy messageEventHandlerStrategy = (MessageEventHandlerStrategy) handlers .get(SEventTriggerType.MESSAGE); messageEventHandlerStrategy.handleThrowEvent(processDefinition, sendTaskInstance, eventTrigger); } public boolean handlePostThrowEvent(final SProcessDefinition sProcessDefinition, final SEndEventDefinition sEndEventDefinition, final SThrowEventInstance sThrowEventInstance, final SFlowNodeInstance sFlowNodeInstance) throws SBonitaException { boolean hasActionsToExecute = false; final List eventTriggers = sEndEventDefinition.getEventTriggers(); for (final SEventTriggerDefinition sEventTriggerDefinition : eventTriggers) { final EventHandlerStrategy eventHandlerStrategy = handlers .get(sEventTriggerDefinition.getEventTriggerType()); if (eventHandlerStrategy != null) { hasActionsToExecute = hasActionsToExecute || eventHandlerStrategy.handlePostThrowEvent(sProcessDefinition, sEndEventDefinition, sThrowEventInstance, sEventTriggerDefinition, sFlowNodeInstance); } } return hasActionsToExecute; } /** * called when a BPM event is triggered by the API * * @param sEventTriggerDefinition * @throws SBonitaException */ public void handleThrowEvent(final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException { final EventHandlerStrategy eventHandlerStrategy = handlers.get(sEventTriggerDefinition.getEventTriggerType()); if (eventHandlerStrategy != null) { eventHandlerStrategy.handleThrowEvent(sEventTriggerDefinition); } } /** * When a trigger is 'launched' the catch event is reached and is waken up/created using its waiting event * Depending on the type it will execute the catch event of instantiate the process/subprocess * * @param waitingEvent * @param triggeringElementID * @throws SBonitaException */ public void triggerCatchEvent(final SWaitingEvent waitingEvent, final Long triggeringElementID) throws SBonitaException { final SBPMEventType eventType = waitingEvent.getEventType(); final long processDefinitionId = waitingEvent.getProcessDefinitionId(); final long targetSFlowNodeDefinitionId = waitingEvent.getFlowNodeDefinitionId(); final long flowNodeInstanceId = waitingEvent.getFlowNodeInstanceId(); final OperationsWithContext operations = handlers.get(waitingEvent.getEventTriggerType()) .getOperations(waitingEvent, triggeringElementID); if (SBPMEventType.EVENT_SUB_PROCESS.equals(waitingEvent.getEventType())) { final SProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); final SStartEventDefinition startEvent = (SStartEventDefinition) processDefinition.getProcessContainer() .getFlowNode( waitingEvent.getFlowNodeDefinitionId()); triggerCatchStartEventSubProcess(waitingEvent.getEventTriggerType(), processDefinitionId, targetSFlowNodeDefinitionId, operations, waitingEvent.getSubProcessId(), waitingEvent.getParentProcessInstanceId(), waitingEvent.getRootProcessInstanceId(), startEvent.isInterrupting()); } else { triggerCatchEvent(eventType, processDefinitionId, targetSFlowNodeDefinitionId, waitingEvent, flowNodeInstanceId, operations); } } private void triggerInTransaction(final SBPMEventType eventType, final Long processDefinitionId, final Long targetSFlowNodeDefinitionId, final SWaitingEvent waitingEvent, final Long flowNodeInstanceId, final OperationsWithContext operations) throws SBonitaException { final TransactionContent transactionContent = new TransactionContent() { @Override public void execute() throws SBonitaException { triggerCatchEvent(eventType, processDefinitionId, targetSFlowNodeDefinitionId, waitingEvent, flowNodeInstanceId, operations); } }; transactionContent.execute(); } private void triggerInTransaction(final SEventTriggerType eventTriggerType, final Long processDefinitionId, final Long targetSFlowNodeDefinitionId, final OperationsWithContext operations, final long subProcessId, final Long parentProcessInstanceId, final Long rootProcessInstanceId, final Boolean isInterrupting) throws SBonitaException { final TransactionContent transactionContent = new TransactionContent() { @Override public void execute() throws SBonitaException { triggerCatchStartEventSubProcess(eventTriggerType, processDefinitionId, targetSFlowNodeDefinitionId, operations, subProcessId, parentProcessInstanceId, rootProcessInstanceId, isInterrupting); } }; transactionContent.execute(); } private void triggerCatchEvent(final SBPMEventType eventType, final Long processDefinitionId, final Long targetSFlowNodeDefinitionId, final SWaitingEvent waitingEvent, final Long flowNodeInstanceId, final OperationsWithContext operations) throws SBonitaException { switch (eventType) { case START_EVENT: instantiateProcess(processDefinitionId, targetSFlowNodeDefinitionId, operations); break; default: if (waitingEvent != null) { // is null if it's a timer eventInstanceService.deleteWaitingEvent(waitingEvent); executeFlowNode(flowNodeInstanceId, operations); } else { executeFlowNode(flowNodeInstanceId, operations); } break; } } private void triggerCatchStartEventSubProcess(final SEventTriggerType triggerType, final Long processDefinitionId, final Long targetSFlowNodeDefinitionId, final OperationsWithContext operations, final long subProcessId, final long parentProcessInstanceId, final Long rootProcessInstanceId, final Boolean isInterrupting) throws SBonitaException { final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId); final SFlowNodeDefinition sFlowNodeDefinition = processDefinition.getProcessContainer() .getFlowNode(subProcessId); final SFlowNodeInstance subProcflowNodeInstance = bpmInstancesCreator.createFlowNodeInstance( processDefinitionId, rootProcessInstanceId, parentProcessInstanceId, SFlowElementsContainerType.PROCESS, sFlowNodeDefinition, rootProcessInstanceId, parentProcessInstanceId, false, 0, SStateCategory.NORMAL, -1); final SProcessInstance parentProcessInstance = processInstanceService .getProcessInstance(parentProcessInstanceId); if (triggerType.equals(SEventTriggerType.ERROR) || isInterrupting) { processInstanceInterruptor.interruptProcessInstance(parentProcessInstanceId, SStateCategory.ABORTING, subProcflowNodeInstance.getId()); } Supplier processInstanceMDC = () -> new ProcessInstanceMDC(0, Optional.empty(), Optional.empty(), processDefinitionId, rootProcessInstanceId); MDCHelper.tryWithMDC(processInstanceMDC, () -> { processExecutor.start(processDefinitionId, targetSFlowNodeDefinitionId, 0, 0, operations.getContext(), operations.getOperations(), subProcflowNodeInstance.getId(), subProcessId, null); // Process contract inputs on EventSubProcess are not supported. }); unregisterEventSubProcess(processDefinition, parentProcessInstance); } public void triggerCatchEvent(final String eventType, final Long processDefinitionId, final Long targetSFlowNodeDefinitionId, final Long flowNodeInstanceId, final String containerType) throws SBonitaException { final SBPMEventType type = SBPMEventType.valueOf(eventType); triggerInTransaction(type, processDefinitionId, targetSFlowNodeDefinitionId, null, flowNodeInstanceId, new OperationsWithContext(null, null, containerType)); } public void triggerCatchEvent(final SEventTriggerType eventTriggerType, final Long processDefinitionId, final Long targetSFlowNodeDefinitionId, final String containerType, final long subProcessId, final Long parentProcessInstanceId, final Long rootProcessInstanceId, final Boolean isInterrupting) throws SBonitaException { triggerInTransaction(eventTriggerType, processDefinitionId, targetSFlowNodeDefinitionId, new OperationsWithContext(null, null, containerType), subProcessId, parentProcessInstanceId, rootProcessInstanceId, isInterrupting); } private void executeFlowNode(final long flowNodeInstanceId, final OperationsWithContext operations) throws SFlowNodeReadException, SFlowNodeExecutionException, SFlowNodeNotFoundException { // in same thread because we delete the message instance after triggering the catch event. The data is of the message // is deleted so we will be unable to execute the flow node instance if (operations.getOperations() != null && !operations.getOperations().isEmpty()) { try { operationService.execute(operations.getOperations(), flowNodeInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.name(), operations.getContext()); } catch (SOperationExecutionException e) { throw new SFlowNodeExecutionException( "Unable to execute operation before executing flow node " + flowNodeInstanceId, e); } } containerRegistry.executeFlowNodeInSameThread(flowNodeInstanceService.getFlowNodeInstance(flowNodeInstanceId), operations.getContainerType()); } private void instantiateProcess(final long processDefinitionId, final long targetSFlowNodeDefinitionId, final OperationsWithContext operations) throws SProcessInstanceCreationException, SContractViolationException { Supplier processInstanceMDC = () -> new ProcessInstanceMDC(0, Optional.empty(), Optional.empty(), processDefinitionId, 0); CheckedRunnable2 run = () -> { processExecutor.start(processDefinitionId, targetSFlowNodeDefinitionId, 0, 0, operations.getContext(), operations.getOperations(), -1, -1, null); }; MDCHelper.tryWithMDC(processInstanceMDC, run); } public EventHandlerStrategy getHandler(final SEventTriggerType triggerType) { return handlers.get(triggerType); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/MessageEventHandlerStrategy.java ================================================ /** * 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.execution.event; import java.util.ArrayList; import java.util.Collections; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; 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.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SReceiveTaskDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SCatchEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchMessageEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCorrelationDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SMessageEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowMessageEventTriggerDefinition; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventCreationException; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SCorrelationContainerBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SMessageInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.message.MessagesHandlingService; import org.bonitasoft.engine.transaction.STransactionNotFoundException; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Celine Souchet */ @Slf4j public class MessageEventHandlerStrategy extends CoupleEventHandlerStrategy { private static final String CORRELATION_NO_VALUE = "NONE"; private final ExpressionResolverService expressionResolverService; private final BPMInstancesCreator bpmInstancesCreator; private final ProcessDefinitionService processDefinitionService; private final MessagesHandlingService messagesHandlingService; public MessageEventHandlerStrategy(final ExpressionResolverService expressionResolverService, final EventInstanceService eventInstanceService, final BPMInstancesCreator bpmInstancesCreator, final ProcessDefinitionService processDefinitionService, MessagesHandlingService messagesHandlingService) { super(eventInstanceService); this.expressionResolverService = expressionResolverService; this.bpmInstancesCreator = bpmInstancesCreator; this.processDefinitionService = processDefinitionService; this.messagesHandlingService = messagesHandlingService; } @Override public void handleCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SCatchEventInstance eventInstance, final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException { final SCatchMessageEventTriggerDefinition messageTrigger = (SCatchMessageEventTriggerDefinition) sEventTriggerDefinition; final String messageName = messageTrigger.getMessageName(); final String processName = processDefinition.getName(); final SWaitingMessageEventBuilder builder; SExpressionContext expressionContext; switch (eventDefinition.getType()) { case BOUNDARY_EVENT: builder = BuilderFactory.get(SWaitingMessageEventBuilderFactory.class) .createNewWaitingMessageBoundaryEventInstance(processDefinition.getId(), eventInstance.getRootContainerId(), eventInstance.getParentProcessInstanceId(), eventInstance.getId(), messageName, processName, eventInstance.getFlowNodeDefinitionId(), eventInstance.getName()); expressionContext = new SExpressionContext(eventInstance.getParentContainerId(), getParentContainerType(eventInstance).name(), processDefinition.getId()); break; case INTERMEDIATE_CATCH_EVENT: builder = BuilderFactory.get(SWaitingMessageEventBuilderFactory.class) .createNewWaitingMessageIntermediateEventInstance( processDefinition.getId(), eventInstance.getRootContainerId(), eventInstance.getParentProcessInstanceId(), eventInstance.getId(), messageName, processName, eventInstance.getFlowNodeDefinitionId(), eventInstance.getName()); expressionContext = new SExpressionContext(eventInstance.getParentContainerId(), getParentContainerType(eventInstance).name(), processDefinition.getId()); break; case START_EVENT: builder = BuilderFactory.get(SWaitingMessageEventBuilderFactory.class) .createNewWaitingMessageStartEventInstance(processDefinition.getId(), messageTrigger.getMessageName(), processDefinition.getName(), eventDefinition.getId(), eventDefinition.getName()); expressionContext = new SExpressionContext(); expressionContext.setProcessDefinitionId(processDefinition.getId()); break; default: throw new SWaitingEventCreationException(eventDefinition.getType() + " is not a catch event."); } fillCorrelation(builder, messageTrigger.getCorrelations(), expressionContext); getEventInstanceService().createWaitingEvent(builder.done()); messagesHandlingService.triggerMatchingOfMessages(); } public void handleCatchEvent(final SProcessDefinition processDefinition, final SReceiveTaskInstance receiveTaskInstance, final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException { final SCatchMessageEventTriggerDefinition messageTrigger = (SCatchMessageEventTriggerDefinition) sEventTriggerDefinition; final String messageName = messageTrigger.getMessageName(); final String processName = processDefinition.getName(); final SWaitingMessageEventBuilder builder; SExpressionContext expressionContext; builder = BuilderFactory.get(SWaitingMessageEventBuilderFactory.class) .createNewWaitingMessageIntermediateEventInstance(processDefinition.getId(), receiveTaskInstance.getRootContainerId(), receiveTaskInstance.getParentProcessInstanceId(), receiveTaskInstance.getId(), messageName, processName, receiveTaskInstance.getFlowNodeDefinitionId(), receiveTaskInstance.getName()); expressionContext = new SExpressionContext(receiveTaskInstance.getParentContainerId(), getParentContainerType(receiveTaskInstance).name(), processDefinition.getId()); try { fillCorrelation(builder, messageTrigger.getCorrelations(), expressionContext); getEventInstanceService().createWaitingEvent(builder.done()); messagesHandlingService.triggerMatchingOfMessages(); } catch (SBonitaException e) { e.setMessageInstanceNameOnContext(messageTrigger.getMessageName()); throw e; } } @Override public void handleThrowEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SThrowEventInstance eventInstance, final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException { final Long processDefinitionId = processDefinition.getId(); final SExpressionContext expressionContext = new SExpressionContext(eventInstance.getParentContainerId(), getParentContainerType(eventInstance).name(), processDefinitionId); try { handleThrowMessage(sEventTriggerDefinition, eventInstance.getName(), processDefinitionId, expressionContext); } catch (SBonitaException e) { e.setMessageInstanceNameOnContext( ((SThrowMessageEventTriggerDefinition) sEventTriggerDefinition).getMessageName()); throw e; } } public void handleThrowEvent(final SProcessDefinition processDefinition, final SSendTaskInstance sendTaskInstance, final SThrowMessageEventTriggerDefinition messageTrigger) throws SMessageInstanceCreationException, SDataInstanceException, SExpressionException, STransactionNotFoundException { final SExpressionContext expressionContext = new SExpressionContext(sendTaskInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinition.getId()); try { handleThrowMessage(messageTrigger, sendTaskInstance.getName(), processDefinition.getId(), expressionContext); } catch (SBonitaException e) { e.setMessageInstanceNameOnContext(messageTrigger.getMessageName()); throw e; } } private void handleThrowMessage(final SEventTriggerDefinition sEventTriggerDefinition, final String eventInstanceName, final Long processDefinitionId, final SExpressionContext expressionContext) throws SMessageInstanceCreationException, SDataInstanceException, SExpressionException, STransactionNotFoundException { final SThrowMessageEventTriggerDefinition messageTrigger = (SThrowMessageEventTriggerDefinition) sEventTriggerDefinition; final String messageName = messageTrigger.getMessageName(); final SExpression targetProcess = messageTrigger.getTargetProcess(); final SExpression targetFlowNode = messageTrigger.getTargetFlowNode(); // evaluate expression final String stringTargetProcess = (String) expressionResolverService.evaluate(targetProcess, expressionContext); String stringTargetFlowNode = null; if (targetFlowNode != null) { stringTargetFlowNode = (String) expressionResolverService.evaluate(targetFlowNode, expressionContext); } final SMessageInstanceBuilder builder = SMessageInstanceBuilder.create(messageName, stringTargetProcess, stringTargetFlowNode, processDefinitionId, eventInstanceName); final List correlations = messageTrigger.getCorrelations(); fillCorrelation(builder, correlations, expressionContext); final SMessageInstance messageInstance = builder.done(); // evaluate and add correlations getEventInstanceService().createMessageInstance(messageInstance); messagesHandlingService.triggerMatchingOfMessages(); // create data if (!messageTrigger.getDataDefinitions().isEmpty()) { bpmInstancesCreator.createDataInstances(messageTrigger.getDataDefinitions(), messageInstance.getId(), DataInstanceContainer.MESSAGE_INSTANCE, expressionContext); if (log.isDebugEnabled()) { log.debug("Initialized variables for message instance [name: <{}>, id: <{}>, flow node: <{}>," + " target flow node: <{}>, target process: <{}>, process definition: <{}>]", messageInstance.getMessageName(), messageInstance.getId(), messageInstance.getFlowNodeName(), messageInstance.getTargetFlowNode(), messageInstance.getTargetProcess(), messageInstance.getProcessDefinitionId()); } } } private void fillCorrelation(final SCorrelationContainerBuilder builder, final List correlations, final SExpressionContext expressionContext) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { final int size = Math.min(5, correlations.size()); final List toEval = new ArrayList<>(size * 2); if (size > 0) { for (int i = 0; i < size; i++) { final SCorrelationDefinition sCorrelationDefinition = correlations.get(i); toEval.add(sCorrelationDefinition.getKey()); toEval.add(sCorrelationDefinition.getValue()); } final List res = expressionResolverService.evaluate(toEval, expressionContext); final List keys = new ArrayList<>(size); final List values = new ArrayList<>(size); for (int i = 0; i < size; i++) { keys.add(String.valueOf(res.get(i * 2))); values.add(String.valueOf(res.get(i * 2 + 1))); } final List sortedKeys = new ArrayList<>(keys); Collections.sort(sortedKeys); for (int i = 0; i < size; i++) { final String key = sortedKeys.get(i); builder.setCorrelation(i + 1, key + "-$-" + values.get(keys.indexOf(key))); } } for (int i = size; i < 5; i++) { builder.setCorrelation(i + 1, CORRELATION_NO_VALUE); } } @Override public OperationsWithContext getOperations(final SWaitingEvent waitingEvent, final Long triggeringElementID) throws SBonitaException { final SMessageInstance messageInstance = getEventInstanceService().getMessageInstance(triggeringElementID); final SProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(waitingEvent.getProcessDefinitionId()); final SExpressionContext context = new SExpressionContext(messageInstance.getId(), DataInstanceContainer.MESSAGE_INSTANCE.name(), processDefinition.getId()); final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SFlowNodeDefinition targetFlowNode = processContainer.getFlowNode(waitingEvent.getFlowNodeName()); SCatchMessageEventTriggerDefinition messageEventTrigger; if (targetFlowNode instanceof SReceiveTaskDefinition) { messageEventTrigger = ((SReceiveTaskDefinition) targetFlowNode).getTrigger(); } else { SCatchEventDefinition catchEvent; if (SBPMEventType.BOUNDARY_EVENT.equals(waitingEvent.getEventType())) { catchEvent = processContainer.getBoundaryEvent(messageInstance.getTargetFlowNode()); } else { catchEvent = (SCatchEventDefinition) targetFlowNode; } messageEventTrigger = catchEvent.getMessageEventTriggerDefinition(messageInstance.getMessageName()); } final List operations = messageEventTrigger.getOperations(); return new OperationsWithContext(context, operations); } @Override public void handleThrowEvent(final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException { handleThrowMessage(sEventTriggerDefinition, "", -1L, null); } @Override public void handleEventSubProcess(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId, final SProcessInstance parentProcessInstance) throws SBonitaException { final SWaitingMessageEventBuilderFactory builderFact = BuilderFactory .get(SWaitingMessageEventBuilderFactory.class); final SMessageEventTriggerDefinition messageEventTriggerDefinition = (SMessageEventTriggerDefinition) sEventTriggerDefinition; final SWaitingMessageEventBuilder builder = builderFact.createNewWaitingMessageEventSubProcInstance( processDefinition.getId(), parentProcessInstance.getId(), parentProcessInstance.getRootProcessInstanceId(), messageEventTriggerDefinition.getMessageName(), processDefinition.getName(), eventDefinition.getId(), eventDefinition.getName(), subProcessId); final SExpressionContext expressionContext = new SExpressionContext(parentProcessInstance.getId(), DataInstanceContainer.PROCESS_INSTANCE.name(), processDefinition.getId()); fillCorrelation(builder, messageEventTriggerDefinition.getCorrelations(), expressionContext); getEventInstanceService().createWaitingEvent(builder.done()); messagesHandlingService.triggerMatchingOfMessages(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/OperationsWithContext.java ================================================ /** * 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.execution.event; import java.io.Serializable; import java.util.List; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType; /** * @author Baptiste Mesta * @author Celine Souchet */ public class OperationsWithContext implements Serializable { private static final long serialVersionUID = 6034976719148086546L; private final SExpressionContext context; private final List operations; private final String containerType; /** * @param context the list of operations * @param operations */ public OperationsWithContext(final SExpressionContext context, final List operations) { this.context = context; this.operations = operations; containerType = SFlowElementsContainerType.PROCESS.name(); } /** * @param context * @param operations the list of operations * @param containerType the type of container on which to execute the operations */ public OperationsWithContext(final SExpressionContext context, final List operations, final String containerType) { this.context = context; this.operations = operations; this.containerType = containerType; } /** * @return the context */ public SExpressionContext getContext() { return context; } /** * @return the operations */ public List getOperations() { return operations; } /** * @return */ public String getContainerType() { return containerType; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/SBPMEventHandlerException.java ================================================ /** * 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.execution.event; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SBPMEventHandlerException extends SBonitaException { private static final long serialVersionUID = 1509958636645119957L; public SBPMEventHandlerException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/SignalEventHandlerStrategy.java ================================================ /** * 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.execution.event; import java.util.List; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SSignalEventTriggerDefinition; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventCreationException; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingSignalEventBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingSignalEventBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent; import org.bonitasoft.engine.execution.work.BPMWorkFactory; import org.bonitasoft.engine.work.WorkService; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SignalEventHandlerStrategy extends CoupleEventHandlerStrategy { private static final OperationsWithContext EMPTY = new OperationsWithContext(null, null); private final WorkService workService; private final BPMWorkFactory workFactory; public SignalEventHandlerStrategy(final EventInstanceService eventInstanceService, WorkService workService, BPMWorkFactory workFactory) { super(eventInstanceService); this.workService = workService; this.workFactory = workFactory; } @Override public void handleCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SCatchEventInstance eventInstance, final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException { final SWaitingSignalEventBuilderFactory builderFact = BuilderFactory .get(SWaitingSignalEventBuilderFactory.class); final SSignalEventTriggerDefinition sSignalEventTriggerDefinition = (SSignalEventTriggerDefinition) sEventTriggerDefinition; SWaitingSignalEventBuilder builder = null; switch (eventDefinition.getType()) { case BOUNDARY_EVENT: builder = builderFact.createNewWaitingSignalBoundaryEventInstance(processDefinition.getId(), eventInstance.getRootProcessInstanceId(), eventInstance.getParentContainerId(), eventInstance.getId(), sSignalEventTriggerDefinition.getSignalName(), processDefinition.getName(), eventDefinition.getId(), eventInstance.getName()); break; case INTERMEDIATE_CATCH_EVENT: builder = builderFact.createNewWaitingSignalIntermediateEventInstance(processDefinition.getId(), eventInstance.getRootProcessInstanceId(), eventInstance.getParentContainerId(), eventInstance.getId(), sSignalEventTriggerDefinition.getSignalName(), processDefinition.getName(), eventDefinition.getId(), eventInstance.getName()); break; case START_EVENT: builder = builderFact.createNewWaitingSignalStartEventInstance(processDefinition.getId(), sSignalEventTriggerDefinition.getSignalName(), processDefinition.getName(), eventDefinition.getId(), eventDefinition.getName()); break; default: throw new SWaitingEventCreationException(eventDefinition.getType() + " is not a catch event."); } final SWaitingSignalEvent signalEvent = builder.done(); getEventInstanceService().createWaitingEvent(signalEvent); } @Override public void handleThrowEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SThrowEventInstance eventInstance, final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException { handleThrowSignal(sEventTriggerDefinition); } private void handleThrowSignal(final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException { final SSignalEventTriggerDefinition signalTrigger = (SSignalEventTriggerDefinition) sEventTriggerDefinition; int index = 0; List listeningSignals = get100WaitingSignalEvents(signalTrigger, index); while (!listeningSignals.isEmpty()) { for (final SWaitingSignalEvent listeningSignal : listeningSignals) { workService.registerWork(workFactory.createTriggerSignalWorkDescriptor(listeningSignal)); } index++; listeningSignals = get100WaitingSignalEvents(signalTrigger, index); } } private List get100WaitingSignalEvents(final SSignalEventTriggerDefinition signalTrigger, int index) throws SEventTriggerInstanceReadException { return getEventInstanceService().getWaitingSignalEvents(signalTrigger.getSignalName(), 100 * index, 100); } @Override public OperationsWithContext getOperations(final SWaitingEvent waitingEvent, final Long triggeringElementID) { return EMPTY; } @Override public void handleThrowEvent(final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException { handleThrowSignal(sEventTriggerDefinition); } @Override public void handleEventSubProcess(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId, final SProcessInstance parentProcessInstance) throws SBonitaException { final SWaitingSignalEventBuilderFactory builderFact = BuilderFactory .get(SWaitingSignalEventBuilderFactory.class); final SSignalEventTriggerDefinition sSignalEventTriggerDefinition = (SSignalEventTriggerDefinition) sEventTriggerDefinition; final SWaitingSignalEventBuilder builder = builderFact.createNewWaitingSignalEventSubProcInstance( processDefinition.getId(), parentProcessInstance.getId(), parentProcessInstance.getRootProcessInstanceId(), sSignalEventTriggerDefinition.getSignalName(), processDefinition.getName(), eventDefinition.getId(), eventDefinition.getName(), subProcessId); final SWaitingSignalEvent signalEvent = builder.done(); getEventInstanceService().createWaitingEvent(signalEvent); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/TerminateEventHandlerStrategy.java ================================================ /** * 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.execution.event; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition; 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.event.SCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.execution.ProcessInstanceInterruptor; /** * @author Baptiste Mesta * @author Celine Souchet */ public class TerminateEventHandlerStrategy extends EventHandlerStrategy { private static final OperationsWithContext EMPTY = new OperationsWithContext(null, null); private ProcessInstanceInterruptor processInstanceInterruptor; public TerminateEventHandlerStrategy(ProcessInstanceInterruptor processInstanceInterruptor) { super(); this.processInstanceInterruptor = processInstanceInterruptor; } @Override public void handleThrowEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SThrowEventInstance eventInstance, final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException { processInstanceInterruptor.interruptChildrenOfProcessInstance(eventInstance.getParentContainerId(), SStateCategory.ABORTING, eventInstance.getId()); // Parent should always be process for event (but must change that id it's not the case anymore } @Override public void handleCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SCatchEventInstance eventInstance, final SEventTriggerDefinition sEventTriggerDefinition) { // No catch of terminate } @Override public OperationsWithContext getOperations(final SWaitingEvent waitingEvent, final Long triggeringElementID) { return EMPTY; } @Override public void handleEventSubProcess(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId, final SProcessInstance parentProcessInstance) { // TODO Auto-generated method stub } @Override public void unregisterCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId, final SProcessInstance parentProcessIsnstance) { // TODO Auto-generated method stub } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/event/TimerEventHandlerStrategy.java ================================================ /** * 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.execution.event; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.STimerEventTriggerDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.STimerType; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.execution.job.JobNameBuilder; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.jobs.TriggerTimerEventJob; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.scheduler.model.SJobDescriptor; import org.bonitasoft.engine.scheduler.model.SJobParameter; import org.bonitasoft.engine.scheduler.trigger.OneShotTrigger; import org.bonitasoft.engine.scheduler.trigger.Trigger; import org.bonitasoft.engine.scheduler.trigger.Trigger.MisfireRestartPolicy; import org.bonitasoft.engine.scheduler.trigger.UnixCronTrigger; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ @Slf4j public class TimerEventHandlerStrategy extends EventHandlerStrategy { private static final OperationsWithContext EMPTY = new OperationsWithContext(null, null); private final SchedulerService schedulerService; private final ExpressionResolverService expressionResolverService; private final EventInstanceService eventInstanceService; public TimerEventHandlerStrategy(final ExpressionResolverService expressionResolverService, final SchedulerService schedulerService, final EventInstanceService eventInstanceService) { this.schedulerService = schedulerService; this.expressionResolverService = expressionResolverService; this.eventInstanceService = eventInstanceService; } @Override public void handleCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SCatchEventInstance eventInstance, final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException { final String jobName = JobNameBuilder.getTimerEventJobName(processDefinition.getId(), eventDefinition, eventInstance); final SJobDescriptor jobDescriptor = getJobDescriptor(jobName); final List jobParameters = getJobParameters(processDefinition, eventDefinition, eventInstance); STimerEventTriggerDefinition timerEventTriggerDefinition = (STimerEventTriggerDefinition) sEventTriggerDefinition; final Object timerCondition = evaluateTimerCondition(timerEventTriggerDefinition, processDefinition.getId(), eventInstance != null ? eventInstance.getParentProcessInstanceId() : null); Trigger trigger = scheduleJob(timerEventTriggerDefinition, jobDescriptor, jobParameters, timerCondition); if (timerEventTriggerDefinition.getTimerType() != STimerType.CYCLE && eventInstance != null) { final STimerEventTriggerInstance sEventTriggerInstance = new STimerEventTriggerInstance( eventInstance.getId(), eventInstance.getName(), trigger.getStartDate().getTime(), trigger.getName()); eventInstanceService.createTimerEventTriggerInstance(sEventTriggerInstance); } } protected Trigger getTrigger(final STimerEventTriggerDefinition timerTrigger, Object timerCondition) throws SBonitaException { Date startDate; Trigger trigger; switch (timerTrigger.getTimerType()) { case DURATION: startDate = new Date(System.currentTimeMillis() + (Long) timerCondition); trigger = new OneShotTrigger("OneShotTrigger" + UUID.randomUUID().getLeastSignificantBits(), startDate); break; case DATE: startDate = (Date) timerCondition; trigger = new OneShotTrigger("OneShotTrigger" + UUID.randomUUID().getLeastSignificantBits(), startDate); break; case CYCLE: startDate = new Date(); trigger = new UnixCronTrigger("UnixCronTrigger" + UUID.randomUUID().getLeastSignificantBits(), startDate, (String) timerCondition, MisfireRestartPolicy.ALL); break; default: throw new IllegalStateException(); } return trigger; } private Object evaluateTimerCondition(STimerEventTriggerDefinition timerTrigger, long processDefinitionId, Long processInstanceId) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { final SExpressionContext expressionContext = getEvaluationContext(processDefinitionId, processInstanceId); final SExpression timerExpression = timerTrigger.getTimerExpression(); final Object result = expressionResolverService.evaluate(timerExpression, expressionContext); if (result == null) { throw new SInvalidExpressionException("The duration cannot be null.", timerExpression.getName()); } return result; } private SExpressionContext getEvaluationContext(long processDefinitionId, Long processInstanceId) { final SExpressionContext expressionContext; if (processInstanceId != null) { expressionContext = new SExpressionContext(processInstanceId, DataInstanceContainer.PROCESS_INSTANCE.name(), processDefinitionId); } else { expressionContext = new SExpressionContext(); expressionContext.setProcessDefinitionId(processDefinitionId); } return expressionContext; } @Override public void handleThrowEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SThrowEventInstance eventInstance, final SEventTriggerDefinition sEventTriggerDefinition) { } @Override public OperationsWithContext getOperations(final SWaitingEvent waitingEvent, final Long triggeringElementID) { return EMPTY; } @Override public void handleEventSubProcess(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId, final SProcessInstance parentProcessInstance) throws SBonitaException { final String jobName = JobNameBuilder.getTimerEventJobName(processDefinition.getId(), eventDefinition, parentProcessInstance.getId(), subProcessId); final SJobDescriptor jobDescriptor = getJobDescriptor(jobName); final List jobParameters = getJobParameters(processDefinition, eventDefinition, null, subProcessId, parentProcessInstance); STimerEventTriggerDefinition timerEventTriggerDefinition = (STimerEventTriggerDefinition) sEventTriggerDefinition; final Object timerCondition = evaluateTimerCondition(timerEventTriggerDefinition, processDefinition.getId(), parentProcessInstance.getId()); scheduleJob(timerEventTriggerDefinition, jobDescriptor, jobParameters, timerCondition); } private Trigger scheduleJob(final STimerEventTriggerDefinition sEventTriggerDefinition, final SJobDescriptor jobDescriptor, final List jobParameters, Object timerCondition) throws SBonitaException { final Trigger trigger = getTrigger(sEventTriggerDefinition, timerCondition); schedulerService.schedule(jobDescriptor, jobParameters, trigger); return trigger; } private List getJobParameters(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SCatchEventInstance eventInstance) { final List jobParameters = new ArrayList(); jobParameters.add(SJobParameter.builder() .key("processDefinitionId") .value(processDefinition.getId()).build()); jobParameters.add(SJobParameter.builder() .key("containerType") .value(SFlowElementsContainerType.PROCESS.name()) .build()); jobParameters.add(SJobParameter.builder() .key("eventType") .value(eventDefinition.getType().name()).build()); jobParameters.add(SJobParameter.builder() .key("targetSFlowNodeDefinitionId") .value(eventDefinition.getId()) .build()); if (SFlowNodeType.START_EVENT.equals(eventDefinition.getType())) { final SStartEventDefinition startEvent = (SStartEventDefinition) eventDefinition; jobParameters.add(SJobParameter.builder() .key("isInterrupting") .value(startEvent.isInterrupting()).build()); } if (eventInstance != null) { jobParameters.add(SJobParameter.builder() .key("flowNodeInstanceId") .value(eventInstance.getId()).build()); } return jobParameters; } private List getJobParameters(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SCatchEventInstance eventInstance, final long subProcessId, final SProcessInstance parentProcessInstance) { final List jobParameters = new ArrayList(); jobParameters.addAll(getJobParameters(processDefinition, eventDefinition, eventInstance)); jobParameters.add(SJobParameter.builder() .key("subProcessId") .value(subProcessId).build()); jobParameters.add(SJobParameter.builder() .key("processInstanceId") .value(parentProcessInstance.getId()).build()); jobParameters.add(SJobParameter.builder() .key("rootProcessInstanceId") .value(parentProcessInstance.getRootProcessInstanceId()).build()); return jobParameters; } private SJobDescriptor getJobDescriptor(final String jobName) { return SJobDescriptor.builder() .jobClassName(TriggerTimerEventJob.class.getName()) .jobName(jobName) .build(); } @Override public void unregisterCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition, final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId, final SProcessInstance parentProcessInstance) throws SBonitaException { final String jobName = JobNameBuilder.getTimerEventJobName(processDefinition.getId(), eventDefinition, parentProcessInstance.getId(), subProcessId); final boolean delete = schedulerService.delete(jobName); if (!delete) { if (log.isDebugEnabled()) { final StringBuilder stb = new StringBuilder(); stb.append("No job found with name '"); stb.append(jobName); stb.append("' when interrupting timer catch event named '"); stb.append(eventDefinition.getName()); stb.append(". In process definition [name = <"); stb.append(processDefinition.getName()); stb.append(">, version = <"); stb.append(processDefinition.getVersion()); stb.append(">]"); stb.append("'. It was probably already triggered."); final String message = stb.toString(); log.debug(message); } } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/flowmerger/FlowNodeTransitionsWrapper.java ================================================ /** * 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.execution.flowmerger; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; /** * @author Elias Ricken de Medeiros */ public class FlowNodeTransitionsWrapper { //TODO: calculate the value of this three attributes in this class private int inputTransitionsSize; private List allOutgoingTransitionDefinitions = new ArrayList<>(); private List validOutgoingTransitionDefinitions = new ArrayList<>(); private STransitionDefinition defaultTransition; public int getInputTransitionsSize() { return inputTransitionsSize; } public void setInputTransitionsSize(final int inputTransitionsSize) { this.inputTransitionsSize = inputTransitionsSize; } public List getNonDefaultOutgoingTransitionDefinitions() { return Collections.unmodifiableList(allOutgoingTransitionDefinitions); } public void setAllOutgoingTransitionDefinitions( final List allOutgoingTransitionDefinitions) { if (allOutgoingTransitionDefinitions != null) { this.allOutgoingTransitionDefinitions = allOutgoingTransitionDefinitions; } else { this.allOutgoingTransitionDefinitions = new ArrayList<>(); } } public List getValidOutgoingTransitionDefinitions() { return Collections.unmodifiableList(validOutgoingTransitionDefinitions); } public void setValidOutgoingTransitionDefinitions( final List validOutgoingTransitionDefinitions) { if (validOutgoingTransitionDefinitions != null) { this.validOutgoingTransitionDefinitions = validOutgoingTransitionDefinitions; } else { this.validOutgoingTransitionDefinitions = new ArrayList<>(); } } public boolean isLastFlowNode() { return validOutgoingTransitionDefinitions == null || validOutgoingTransitionDefinitions.isEmpty(); } public boolean hasMultipleOutgoingTransitions() { return hasMultipleElements(allOutgoingTransitionDefinitions); } private boolean hasMultipleElements(final Collection collection) { return collection != null && collection.size() > 1; } public boolean hasMultipleIncomingTransitions() { return inputTransitionsSize > 1; } public boolean isManyToMany() { return hasMultipleIncomingTransitions() && hasMultipleOutgoingTransitions(); } /** * from 0 or 1 input transition to one outgoing transition * * @return true for flow node with 0 or 1 input transition and one outgoing transitions * @since 6.2 */ public boolean isSimpleMerge() { return !hasMultipleIncomingTransitions() && hasOneElement(); } private boolean hasOneElement() { return allOutgoingTransitionDefinitions.size() == 1 || allOutgoingTransitionDefinitions.isEmpty() && validOutgoingTransitionDefinitions.size() == 1; } /** * from 0 or 1 input transition to more than one outgoing transitions * * @return true for flow node with 0 or 1 input transition and more than one outgoing transitions * @since 6.2 */ public boolean isSimpleToMany() { return !hasMultipleIncomingTransitions() && hasMultipleOutgoingTransitions(); } /** * from more than 1 input transition to one outgoing transition * * @return true for flow node with more than 1 input transition and one outgoing transitions * @since 6.2 */ public boolean isManyToOne() { return hasMultipleIncomingTransitions() && hasOneElement(); } public void setDefaultTransition(final STransitionDefinition defaultTransition) { this.defaultTransition = defaultTransition; } public STransitionDefinition getDefaultTransition() { return defaultTransition; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/handler/ArchiveProcessInstancesHandler.java ================================================ /** * 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.execution.handler; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.events.model.SHandlerExecutionException; import org.bonitasoft.engine.events.model.SUpdateEvent; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class ArchiveProcessInstancesHandler implements SProcessInstanceHandler { private static final long serialVersionUID = 1L; private final String identifier; public ArchiveProcessInstancesHandler() { this.identifier = ArchiveProcessInstancesHandler.class.getName(); } @Override public void execute(final SUpdateEvent event) throws SHandlerExecutionException { final SProcessInstance processInstance = (SProcessInstance) event.getObject(); try { getServiceAccessor().getBPMArchiverService().archiveAndDeleteProcessInstance(processInstance); } catch (SBonitaException e) { throw new SHandlerExecutionException(e); } } /** * @return ServiceAccessor */ private ServiceAccessor getServiceAccessor() throws SHandlerExecutionException { try { return ServiceAccessorFactory.getInstance().createServiceAccessor(); } catch (Exception e) { throw new SHandlerExecutionException(e.getMessage(), null); } } @Override public boolean isInterested(final SUpdateEvent event) { boolean isInterested = ProcessInstanceService.PROCESSINSTANCE_STATE_UPDATED.equals(event.getType()) && event.getObject() instanceof SProcessInstance; if (isInterested) { final SProcessInstance processInstance = (SProcessInstance) event.getObject(); // TODO add a method isInTerminalState in SProcessInstance final boolean isTerminal = ProcessInstanceState.COMPLETED.getId() == processInstance.getStateId() || ProcessInstanceState.ABORTED.getId() == processInstance.getStateId() || ProcessInstanceState.CANCELLED.getId() == processInstance.getStateId(); // process instances called by an call activity are archive in the state CompletingCallActivity (wait data transfer from called process to caller). // Sub-process can be archived directly isInterested = isTerminal && (processInstance.getCallerId() <= 0 || SFlowNodeType.SUB_PROCESS.equals(processInstance.getCallerType())); } return isInterested; } @Override public String getIdentifier() { return identifier; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/handler/SProcessInstanceHandler.java ================================================ /** * 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.execution.handler; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.events.model.SHandler; /** * @author Baptiste Mesta */ public interface SProcessInstanceHandler extends SHandler { } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/job/JobNameBuilder.java ================================================ /** * 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.execution.job; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition; import org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance; /** * @author Elias Ricken de Medeiros * @author Celine Souchet * @author Matthieu Chaffotte */ public class JobNameBuilder { private static final String PREFIX = "Timer_Ev_"; public static String getTimerEventJobName(final Long processDefinitionId, final SEventDefinition eventDefinition, final SCatchEventInstance flowNodeInstance) { if (SFlowNodeType.START_EVENT.equals(eventDefinition.getType())) { return PREFIX + processDefinitionId + eventDefinition.getName(); } return PREFIX + flowNodeInstance.getId(); } public static String getTimerEventJobName(final Long processDefinitionId, final SEventDefinition eventDefinition, final long parentProcessInstanceId, final long subProcessId) { return PREFIX + processDefinitionId + eventDefinition.getName() + parentProcessInstanceId + subProcessId; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortedFlowNodeState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.springframework.stereotype.Component; @Component public class AbortedFlowNodeState implements FlowNodeState { @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) { return StateCode.DONE; } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) { return true; } @Override public int getId() { return 16; } @Override public String getName() { return "aborted"; } @Override public boolean isStable() { return true; } @Override public boolean isTerminal() { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.ABORTING; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingActivityWithBoundaryState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.springframework.stereotype.Component; @Component public class AbortingActivityWithBoundaryState implements FlowNodeState { public AbortingActivityWithBoundaryState() { } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } @Override public int getId() { return 35; } @Override public String getName() { return "aborting activity with boundary"; } @Override public SStateCategory getStateCategory() { return SStateCategory.ABORTING; } public SStateCategory getBoundaryCategoryState() { return SStateCategory.ABORTING; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return false; } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) throws SActivityStateExecutionException { return StateCode.DONE; } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) { return true; } @Override public boolean isStable() { return false; } @Override public boolean isTerminal() { return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingBoundaryAndIntermediateCatchEventState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.WaitingEventsInterrupter; import org.springframework.stereotype.Component; @Component public class AbortingBoundaryAndIntermediateCatchEventState extends InterruptingBoundaryAndIntermediateCatchEventState { public AbortingBoundaryAndIntermediateCatchEventState(WaitingEventsInterrupter waitingEventsInterrupter) { super(waitingEventsInterrupter); } @Override public int getId() { return 60; } @Override public String getName() { return "aborting"; } @Override public SStateCategory getStateCategory() { return SStateCategory.ABORTING; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return "Aborting boundary or intermediate catch event"; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingBoundaryEventsOnCompletingActivityState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; @Component public class AbortingBoundaryEventsOnCompletingActivityState implements FlowNodeState { private final StateBehaviors stateBehaviors; public AbortingBoundaryEventsOnCompletingActivityState(final StateBehaviors stateBehaviors) { this.stateBehaviors = stateBehaviors; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } @Override public int getId() { return 34; } @Override public String getName() { // TODO: should be changed but has impacts client-side, as it is exposed client-side. return "completing activity with boundary"; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { final SActivityDefinition activityDef = (SActivityDefinition) processDefinition.getProcessContainer() .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); return !activityDef.getBoundaryEventDefinitions().isEmpty(); } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) throws SActivityStateExecutionException { final SActivityDefinition activityDef = (SActivityDefinition) processDefinition.getProcessContainer() .getFlowNode(instance.getFlowNodeDefinitionId()); if (!activityDef.getBoundaryEventDefinitions().isEmpty()) { final SActivityInstance activityInstance = (SActivityInstance) instance; stateBehaviors.interruptAttachedBoundaryEvent(processDefinition, activityInstance, SStateCategory.ABORTING); } return StateCode.DONE; } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) { return true; } @Override public boolean isStable() { return false; } @Override public boolean isTerminal() { return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingCallActivityState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.ProcessInstanceInterruptor; import org.bonitasoft.engine.execution.archive.BPMArchiverService; import org.springframework.stereotype.Component; @Component public class AbortingCallActivityState extends EndingCallActivityExceptionState { public AbortingCallActivityState(ProcessInstanceService processInstanceService, ProcessInstanceInterruptor processInstanceInterruptor, BPMArchiverService bpmArchiverService) { super(processInstanceService, processInstanceInterruptor, bpmArchiverService); } @Override public int getId() { return 20; } @Override public String getName() { return "aborting"; } @Override public SStateCategory getStateCategory() { return SStateCategory.ABORTING; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return "aborting call activity"; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingFlowNodeContainerState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; @Component public class AbortingFlowNodeContainerState extends EndingFlowNodeContainerExceptionState { public AbortingFlowNodeContainerState(final StateBehaviors stateBehaviors) { super(stateBehaviors); } @Override public int getId() { return 15; } @Override public String getName() { return "aborting"; } @Override public SStateCategory getStateCategory() { return SStateCategory.ABORTING; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingFlowNodeState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.springframework.stereotype.Component; @Component public class AbortingFlowNodeState implements FlowNodeState { @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return false; } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) { return StateCode.DONE; } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) { return true; } @Override public int getId() { return 21; } @Override public String getName() { return "aborting"; } @Override public boolean isStable() { return true; } @Override public boolean isTerminal() { return false; } @Override public SStateCategory getStateCategory() { return SStateCategory.ABORTING; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return "aborting flow node"; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingReceiveTaskState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance; import org.bonitasoft.engine.execution.StateBehaviors; import org.bonitasoft.engine.execution.WaitingEventsInterrupter; import org.springframework.stereotype.Component; @Component public class AbortingReceiveTaskState extends AbortingFlowNodeContainerState { private final WaitingEventsInterrupter waitingEventsInterrupter; public AbortingReceiveTaskState(final StateBehaviors stateBehaviors, WaitingEventsInterrupter waitingEventsInterrupter) { super(stateBehaviors); this.waitingEventsInterrupter = waitingEventsInterrupter; } @Override public int getId() { return 38; } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) throws SActivityStateExecutionException { try { final SReceiveTaskInstance receiveTaskInstance = (SReceiveTaskInstance) instance; waitingEventsInterrupter.interruptWaitingEvents(receiveTaskInstance); } catch (final SBonitaException e) { throw new SActivityStateExecutionException(e); } return super.execute(processDefinition, instance); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/AbortingSubTaskState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; @Component public class AbortingSubTaskState implements FlowNodeState { private final StateBehaviors stateBehaviors; public AbortingSubTaskState(final StateBehaviors stateBehaviors) { super(); this.stateBehaviors = stateBehaviors; } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return StateCode.DONE; } @Override public int getId() { return 13; } @Override public boolean isStable() { return true; } @Override public boolean isTerminal() { return false; } @Override public String getName() { // TODO: should be changed but has impacts client-side, as it is exposed in // org.bonitasoft.engine.bpm.flownode.ActivityStates.CANCELLING_SUBTASKS_STATE return "cancelling subtasks"; } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) { final SHumanTaskInstance sHumanTaskInstance = (SHumanTaskInstance) parentInstance; return sHumanTaskInstance.getTokenCount() == 0; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityExecutionException { final SHumanTaskInstance sHumanTaskInstance = (SHumanTaskInstance) flowNodeInstance; final boolean hasTokens = sHumanTaskInstance.getTokenCount() > 0; if (hasTokens) { try { stateBehaviors.interruptSubActivities(flowNodeInstance, SStateCategory.ABORTING); } catch (final SBonitaException e) { throw new SActivityExecutionException(e); } } return hasTokens; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CancelledFlowNodeState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.springframework.stereotype.Component; @Component public class CancelledFlowNodeState implements FlowNodeState { public CancelledFlowNodeState() { } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) { return StateCode.DONE; } @Override public int getId() { return 14; } @Override public String getName() { return "cancelled"; } @Override public boolean isStable() { return true; } @Override public boolean isTerminal() { return true; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.CANCELLING; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CancellingActivityWithBoundaryState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.springframework.stereotype.Component; @Component public class CancellingActivityWithBoundaryState implements FlowNodeState { public CancellingActivityWithBoundaryState() { } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } @Override public int getId() { return 36; } @Override public String getName() { return "completing activity with boundary"; } @Override public SStateCategory getStateCategory() { return SStateCategory.CANCELLING; } public SStateCategory getBoundaryCategoryState() { return SStateCategory.CANCELLING; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return false; } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) throws SActivityStateExecutionException { return StateCode.DONE; } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) { return true; } @Override public boolean isStable() { return false; } @Override public boolean isTerminal() { return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CancellingBoundaryAndIntermediateCatchEventState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.WaitingEventsInterrupter; import org.springframework.stereotype.Component; @Component public class CancellingBoundaryAndIntermediateCatchEventState extends InterruptingBoundaryAndIntermediateCatchEventState { public CancellingBoundaryAndIntermediateCatchEventState(WaitingEventsInterrupter waitingEventsInterrupter) { super(waitingEventsInterrupter); } @Override public int getId() { return 22; } @Override public String getName() { return "cancelling"; } @Override public SStateCategory getStateCategory() { return SStateCategory.CANCELLING; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return "Canceling boundary or intermediate catch event"; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CancellingCallActivityState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.ProcessInstanceInterruptor; import org.bonitasoft.engine.execution.archive.BPMArchiverService; import org.springframework.stereotype.Component; @Component public class CancellingCallActivityState extends EndingCallActivityExceptionState { public CancellingCallActivityState(ProcessInstanceService processInstanceService, ProcessInstanceInterruptor processInstanceInterruptor, BPMArchiverService bpmArchiverService) { super(processInstanceService, processInstanceInterruptor, bpmArchiverService); } @Override public int getId() { return 19; } @Override public String getName() { return "cancelling"; } @Override public SStateCategory getStateCategory() { return SStateCategory.CANCELLING; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return "canceling call activity"; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CancellingFlowNodeContainerChildrenState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; @Component public class CancellingFlowNodeContainerChildrenState extends EndingFlowNodeContainerExceptionState { public CancellingFlowNodeContainerChildrenState(final StateBehaviors stateBehaviors) { super(stateBehaviors); } @Override public int getId() { return 17; } @Override public String getName() { return "cancelling"; } @Override public SStateCategory getStateCategory() { return SStateCategory.CANCELLING; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CancellingFlowNodeState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.springframework.stereotype.Component; @Component public class CancellingFlowNodeState implements FlowNodeState { public CancellingFlowNodeState() { } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return false; } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) { return StateCode.DONE; } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) { return true; } @Override public int getId() { return 18; } @Override public String getName() { return "cancelling"; } @Override public boolean isStable() { return true; } @Override public boolean isTerminal() { return false; } @Override public SStateCategory getStateCategory() { return SStateCategory.CANCELLING; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CancellingReceiveTaskState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance; import org.bonitasoft.engine.execution.WaitingEventsInterrupter; import org.springframework.stereotype.Component; @Component public class CancellingReceiveTaskState extends CancellingActivityWithBoundaryState { private final WaitingEventsInterrupter waitingEventsInterrupter; public CancellingReceiveTaskState(WaitingEventsInterrupter waitingEventsInterrupter) { this.waitingEventsInterrupter = waitingEventsInterrupter; } @Override public int getId() { return 39; } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) throws SActivityStateExecutionException { try { final SReceiveTaskInstance receiveTaskInstance = (SReceiveTaskInstance) instance; waitingEventsInterrupter.interruptWaitingEvents(receiveTaskInstance); } catch (final SBonitaException e) { throw new SActivityStateExecutionException(e); } return super.execute(processDefinition, instance); } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CompletedActivityState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.springframework.stereotype.Component; @Component public class CompletedActivityState implements FlowNodeState { public static final int ID = 2; @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance activityInstance) { return StateCode.DONE; // this method is never called, use "completing" state } @Override public int getId() { return ID; } @Override public boolean isStable() { return true; } @Override public boolean isTerminal() { return true; } @Override public String getName() { return "completed"; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return flowNodeInstance instanceof SHumanTaskInstance; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return " User " + flowNodeInstance.getExecutedBy() + " has " + getName() + " task " + flowNodeInstance.getName(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CompletingActivityState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; @Component public class CompletingActivityState extends OnFinishConnectorState { protected final StateBehaviors stateBehaviors; public CompletingActivityState(final StateBehaviors stateBehaviors) { super(stateBehaviors); this.stateBehaviors = stateBehaviors; } @Override public int getId() { return 9; } @Override public String getName() { return "completing"; } @Override public boolean isStable() { return false; } @Override public boolean isTerminal() { return false; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } @Override protected void afterConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.mapDataOutputOfMultiInstance(processDefinition, flowNodeInstance); stateBehaviors.updateDisplayDescriptionAfterCompletion(processDefinition, flowNodeInstance); } @Override protected void beforeConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/CompletingCallActivityState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.OperationService; import org.bonitasoft.engine.core.process.definition.model.SCallActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.execution.StateBehaviors; import org.bonitasoft.engine.execution.archive.BPMArchiverService; import org.springframework.stereotype.Component; @Component public class CompletingCallActivityState extends CompletingActivityState { private final OperationService operationService; private final ProcessInstanceService processInstanceService; private final BPMArchiverService bpmArchiverService; public CompletingCallActivityState(StateBehaviors stateBehaviors, OperationService operationService, ProcessInstanceService processInstanceService, BPMArchiverService bpmArchiverService) { super(stateBehaviors); this.operationService = operationService; this.processInstanceService = processInstanceService; this.bpmArchiverService = bpmArchiverService; } @Override protected void beforeConnectors(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { executeDataOutputOperations(processDefinition, flowNodeInstance); stateBehaviors.executeOperations(processDefinition, (SActivityInstance) flowNodeInstance); } private void executeDataOutputOperations(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) throws SActivityStateExecutionException { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SCallActivityDefinition callActivityDef = (SCallActivityDefinition) processContainer .getFlowNode(instance.getFlowNodeDefinitionId()); try { final SProcessInstance childProcInst = processInstanceService.getChildOfActivity(instance.getId()); final SExpressionContext expressionContext = new SExpressionContext(childProcInst.getId(), DataInstanceContainer.PROCESS_INSTANCE.name(), childProcInst.getProcessDefinitionId()); expressionContext.setParentProcessDefinitionId(instance.getProcessDefinitionId()); operationService.execute(callActivityDef.getDataOutputOperations(), instance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), expressionContext); // archive child process instance bpmArchiverService.archiveAndDeleteProcessInstance(childProcInst); } catch (final SBonitaException e) { throw new SActivityStateExecutionException("Unable to map call activity output data", ScopedException.OPERATION, e); } } @Override public int getId() { return 30; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/EndingCallActivityExceptionState.java ================================================ /** * 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.execution.state; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.SArchivingException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.execution.ProcessInstanceInterruptor; import org.bonitasoft.engine.execution.archive.BPMArchiverService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.springframework.stereotype.Component; @Slf4j @Component public abstract class EndingCallActivityExceptionState implements FlowNodeState { private final ProcessInstanceService processInstanceService; private final ProcessInstanceInterruptor processInstanceInterruptor; private final BPMArchiverService bpmArchiverService; public EndingCallActivityExceptionState(ProcessInstanceService processInstanceService, ProcessInstanceInterruptor processInstanceInterruptor, BPMArchiverService bpmArchiverService) { this.processInstanceService = processInstanceService; this.processInstanceInterruptor = processInstanceInterruptor; this.bpmArchiverService = bpmArchiverService; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityExecutionException { try { final SProcessInstance targetProcessInstance = processInstanceService .getChildOfActivity(flowNodeInstance.getId()); final boolean hasActiveChild = processInstanceInterruptor .interruptProcessInstance(targetProcessInstance.getId(), getStateCategory()); log.debug("{} activity id {}, name {} with active process : {} ", getStateCategory(), flowNodeInstance.getId(), flowNodeInstance.getName(), hasActiveChild); if (!hasActiveChild) { archiveChildProcessInstance(flowNodeInstance); } return hasActiveChild; } catch (SProcessInstanceNotFoundException e) { return false; } catch (final SBonitaException e) { throw new SActivityExecutionException(e); } } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) throws SActivityStateExecutionException { // archive process target process instance try { archiveChildProcessInstance(instance); } catch (final SBonitaException e) { throw new SActivityStateExecutionException( "Unable to found the process instance called by call activity with id " + instance.getId(), e); } return StateCode.DONE; } private void archiveChildProcessInstance(final SFlowNodeInstance instance) throws SArchivingException, SBonitaReadException { try { final SProcessInstance childProcInst = processInstanceService.getChildOfActivity(instance.getId()); bpmArchiverService.archiveAndDeleteProcessInstance(childProcInst); } catch (SProcessInstanceNotFoundException ignored) { log.warn("No target process instance found when archiving the call activity {}, in state {}", instance.getId(), getName()); } } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) { return true; } @Override public boolean isStable() { return true; } @Override public boolean isTerminal() { return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/EndingFlowNodeContainerExceptionState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.execution.StateBehaviors; public abstract class EndingFlowNodeContainerExceptionState implements FlowNodeState { private final StateBehaviors stateBehaviors; public EndingFlowNodeContainerExceptionState(final StateBehaviors stateBehaviors) { super(); this.stateBehaviors = stateBehaviors; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityExecutionException { if (flowNodeInstance.getTokenCount() > 0) { try { stateBehaviors.interruptSubActivities(flowNodeInstance, getStateCategory()); } catch (final SBonitaException e) { throw new SActivityExecutionException(e); } } return flowNodeInstance.getTokenCount() > 0; } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) throws SActivityStateExecutionException { return StateCode.DONE; } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) { return parentInstance.getTokenCount() == 0; } @Override public boolean isStable() { return true; } @Override public boolean isTerminal() { return false; } public StateBehaviors getStateBehaviors() { return stateBehaviors; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingAutomaticActivityState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; @Component public class ExecutingAutomaticActivityState extends OnEnterAndFinishConnectorState { private final StateBehaviors stateBehaviors; public ExecutingAutomaticActivityState(final StateBehaviors stateBehaviors) { super(stateBehaviors); this.stateBehaviors = stateBehaviors; } @Override protected void afterOnFinish(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.mapDataOutputOfMultiInstance(processDefinition, flowNodeInstance); stateBehaviors.updateDisplayDescriptionAfterCompletion(processDefinition, flowNodeInstance); } @Override protected void onEnterToOnFinish(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { final SActivityInstance activityInstance = (SActivityInstance) flowNodeInstance; stateBehaviors.updateDisplayNameAndDescription(processDefinition, flowNodeInstance); stateBehaviors.executeOperations(processDefinition, activityInstance); stateBehaviors.handleThrowEvent(processDefinition, flowNodeInstance); } @Override protected void beforeOnEnter(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.createData(processDefinition, flowNodeInstance); stateBehaviors.createAttachedBoundaryEvents(processDefinition, (SActivityInstance) flowNodeInstance); } @Override public int getId() { return 37; } @Override public boolean isStable() { return false; } @Override public boolean isTerminal() { return false; } @Override public String getName() { return "executing"; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingBoundaryEventState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; 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.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.execution.ContainerRegistry; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; @Component public class ExecutingBoundaryEventState implements FlowNodeState { private final ActivityInstanceService activityInstanceService; private final ContainerRegistry containerRegistry; private final StateBehaviors stateBehaviors; public ExecutingBoundaryEventState(ActivityInstanceService activityInstanceService, ContainerRegistry containerRegistry, StateBehaviors stateBehaviors) { this.activityInstanceService = activityInstanceService; this.containerRegistry = containerRegistry; this.stateBehaviors = stateBehaviors; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return "executing boundary event"; } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance boundary) throws SActivityStateExecutionException { final SBoundaryEventInstance boundaryEventInstance = (SBoundaryEventInstance) boundary; if (boundaryEventInstance.isInterrupting()) { abortRelatedActivity(boundaryEventInstance); // Also abort the other boundary events on the same flow node instance: try { stateBehaviors.interruptAttachedBoundaryEvent(processDefinition, activityInstanceService.getActivityInstance(boundaryEventInstance.getActivityInstanceId()), SStateCategory.ABORTING); } catch (SActivityInstanceNotFoundException | SActivityReadException e) { throw new SActivityStateExecutionException(e); } } return StateCode.DONE; } private void abortRelatedActivity(final SBoundaryEventInstance boundaryEventInstance) throws SActivityStateExecutionException { if (SStateCategory.NORMAL.equals(boundaryEventInstance.getStateCategory())) { try { final SActivityInstance relatedActivityInst = activityInstanceService .getActivityInstance(boundaryEventInstance.getActivityInstanceId()); activityInstanceService.setStateCategory(relatedActivityInst, SStateCategory.ABORTING); activityInstanceService.setAbortedByBoundaryEvent(relatedActivityInst, boundaryEventInstance.getId()); containerRegistry .executeFlowNode(relatedActivityInst); } catch (final SBonitaException e) { throw new SActivityStateExecutionException(e); } } } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) { return true; } @Override public int getId() { return 65; } @Override public String getName() { return "executing"; } @Override public boolean isStable() { return false; } @Override public boolean isTerminal() { return false; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingCallActivityState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; @Component public class ExecutingCallActivityState implements FlowNodeState { private final StateBehaviors stateBehaviors; public ExecutingCallActivityState(final StateBehaviors stateBehaviors) { this.stateBehaviors = stateBehaviors; } @Override public int getId() { return 31; } @Override public boolean isStable() { return true; } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) throws SActivityStateExecutionException { stateBehaviors.mapDataOutputOfMultiInstance(processDefinition, instance); stateBehaviors.updateDisplayDescriptionAfterCompletion(processDefinition, instance); return StateCode.DONE; } @Override public boolean isTerminal() { return false; } @Override public String getName() { return "executing"; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingFlowNodeState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; @Component public class ExecutingFlowNodeState extends OnFinishConnectorState { private StateBehaviors stateBehaviors; public ExecutingFlowNodeState(final StateBehaviors stateBehaviors) { super(stateBehaviors); this.stateBehaviors = stateBehaviors; } @Override public int getId() { return ID_ACTIVITY_EXECUTING; } @Override public boolean isStable() { return false; } @Override public boolean isTerminal() { return false; } @Override public String getName() { return "executing"; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } @Override protected void beforeConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { } @Override protected void afterConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.updateDisplayDescriptionAfterCompletion(processDefinition, flowNodeInstance); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingLoopActivityState.java ================================================ /** * 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.execution.state; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SStandardLoopCharacteristics; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; 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.SLoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilderFactory; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.execution.ContainerRegistry; import org.bonitasoft.engine.expression.ExpressionConstants; import org.springframework.stereotype.Component; @Component public class ExecutingLoopActivityState implements FlowNodeState { private final ExpressionResolverService expressionResolverService; private final BPMInstancesCreator bpmInstancesCreator; private final ContainerRegistry containerRegistry; private final ActivityInstanceService activityInstanceService; public ExecutingLoopActivityState(final ExpressionResolverService expressionResolverService, final BPMInstancesCreator bpmInstancesCreator, final ContainerRegistry containerRegistry, final ActivityInstanceService activityInstanceService) { this.expressionResolverService = expressionResolverService; this.bpmInstancesCreator = bpmInstancesCreator; this.containerRegistry = containerRegistry; this.activityInstanceService = activityInstanceService; } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return StateCode.DONE; } @Override public int getId() { return 24; } @Override public boolean isStable() { return true; } @Override public boolean isTerminal() { return false; } @Override public String getName() { return "executing"; } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) throws SActivityStateExecutionException { try { final SLoopActivityInstance loopActivity = (SLoopActivityInstance) activityInstanceService .getFlowNodeInstance(parentInstance.getId());// get it if (loopActivity.getStateCategory() != SStateCategory.NORMAL) { // if is not a normal state (aborting / canceling), return true to change state from executing to aborting / cancelling (ChildReadstate), // without create a new child task return true; } final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SActivityDefinition activity = (SActivityDefinition) processContainer .getFlowNode(parentInstance.getFlowNodeDefinitionId()); final SStandardLoopCharacteristics loopCharacteristics = (SStandardLoopCharacteristics) activity .getLoopCharacteristics(); boolean loop = false; final int loopCounter = loopActivity.getLoopCounter(); if (loopActivity.getLoopMax() > 0 && loopCounter >= loopActivity.getLoopMax()) { return true; } final SStandardLoopCharacteristics standardLoop = loopCharacteristics; final Map input = new HashMap<>(1); input.put(ExpressionConstants.LOOP_COUNTER.getEngineConstantName(), loopActivity.getLoopCounter()); final SExpressionContext sExpressionContext = new SExpressionContext(loopActivity.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), loopActivity.getProcessDefinitionId(), input); loop = (Boolean) expressionResolverService.evaluate(standardLoop.getLoopCondition(), sExpressionContext); if (loop) { final SLoopActivityInstanceBuilderFactory keyProvider = BuilderFactory .get(SLoopActivityInstanceBuilderFactory.class); final long rootProcessInstanceId = parentInstance .getLogicalGroup(keyProvider.getRootProcessInstanceIndex()); final long parentProcessInstanceId = parentInstance .getLogicalGroup(keyProvider.getParentProcessInstanceIndex()); final SFlowNodeInstance child = bpmInstancesCreator.createFlowNodeInstance(processDefinition.getId(), parentInstance.getRootContainerId(), parentInstance.getId(), SFlowElementsContainerType.FLOWNODE, activity, rootProcessInstanceId, parentProcessInstanceId, true, loopCounter + 1, SStateCategory.NORMAL, -1); activityInstanceService.incrementLoopCounter(loopActivity); activityInstanceService.setTokenCount(loopActivity, loopActivity.getTokenCount() + 1); containerRegistry.executeFlowNode(child); } return !loop; } catch (final SBonitaException e) { throw new SActivityStateExecutionException("Unable to handle loop activity", ScopedException.ITERATION, e); } } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityExecutionException { List childrenOfAnActivity; try { childrenOfAnActivity = activityInstanceService.getChildrenOfAnActivity(flowNodeInstance.getId(), 0, 1); if (!childrenOfAnActivity.isEmpty()) { containerRegistry.executeFlowNode(childrenOfAnActivity.get(0)); } return !childrenOfAnActivity.isEmpty(); } catch (final SBonitaException e) { throw new SActivityExecutionException(e); } } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingMultiInstanceActivityState.java ================================================ /** * 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.execution.state; import static org.bonitasoft.engine.core.process.instance.model.SStateCategory.ABORTING; import java.util.HashMap; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SMultiInstanceLoopCharacteristics; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.execution.ContainerRegistry; import org.bonitasoft.engine.execution.StateBehaviors; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.expression.model.SExpression; import org.springframework.stereotype.Component; @Slf4j @Component public class ExecutingMultiInstanceActivityState implements FlowNodeState { private final ExpressionResolverService expressionResolverService; private final ContainerRegistry containerRegistry; private final ActivityInstanceService activityInstanceService; private final StateBehaviors stateBehaviors; public ExecutingMultiInstanceActivityState(final ExpressionResolverService expressionResolverService, final ContainerRegistry containerRegistry, final ActivityInstanceService activityInstanceService, final StateBehaviors stateBehaviors) { this.expressionResolverService = expressionResolverService; this.containerRegistry = containerRegistry; this.activityInstanceService = activityInstanceService; this.stateBehaviors = stateBehaviors; } @Override public int getId() { return 28; } @Override public boolean isStable() { return true; } @Override public boolean isTerminal() { return false; } @Override public String getName() { return "executing"; } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) throws SActivityStateExecutionException { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SActivityDefinition activityDefinition = (SActivityDefinition) processContainer .getFlowNode(parentInstance.getFlowNodeDefinitionId()); final SMultiInstanceLoopCharacteristics loopCharacteristics = (SMultiInstanceLoopCharacteristics) activityDefinition .getLoopCharacteristics(); try { if (parentInstance.getStateCategory() != SStateCategory.NORMAL) { // if is not a normal state (aborting / canceling), return true to change state from executing to aborting / cancelling (ChildReachstate), // without create a new child task return true; } final SMultiInstanceActivityInstance parentMultiInstance = (SMultiInstanceActivityInstance) parentInstance; final String stateCategory; if (childInstance.isAborting() || childInstance.isCanceling()) { activityInstanceService.addMultiInstanceNumberOfTerminatedActivities(parentMultiInstance, 1); stateCategory = "terminated"; } else { activityInstanceService.addMultiInstanceNumberOfCompletedActivities(parentMultiInstance, 1); stateCategory = "completed"; // check the completionCondition final SExpression completionCondition = loopCharacteristics.getCompletionCondition(); final Map input = new HashMap<>(1); input.put(ExpressionConstants.NUMBER_OF_ACTIVE_INSTANCES.getEngineConstantName(), parentMultiInstance.getNumberOfActiveInstances()); input.put(ExpressionConstants.NUMBER_OF_TERMINATED_INSTANCES.getEngineConstantName(), parentMultiInstance.getNumberOfTerminatedInstances()); input.put(ExpressionConstants.NUMBER_OF_COMPLETED_INSTANCES.getEngineConstantName(), parentMultiInstance.getNumberOfCompletedInstances()); final int numberOfInstances = parentMultiInstance.getNumberOfInstances(); input.put(ExpressionConstants.NUMBER_OF_INSTANCES.getEngineConstantName(), numberOfInstances); final SExpressionContext sExpressionContext = new SExpressionContext(parentMultiInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinition.getId(), input); sExpressionContext.setProcessDefinitionId(parentMultiInstance.getProcessDefinitionId()); if (completionCondition != null) { final boolean complete = (Boolean) expressionResolverService.evaluate(completionCondition, sExpressionContext); if (complete) { stateBehaviors.interruptSubActivities(parentMultiInstance, ABORTING); if (parentMultiInstance.isSequential()) { return true; } } } } final int numberOfActiveInstances = parentMultiInstance.getNumberOfActiveInstances(); final int numberOfCompletedInstances = parentMultiInstance.getNumberOfCompletedInstances(); final int numberOfTerminatedInstances = parentMultiInstance.getNumberOfTerminatedInstances(); final int numberOfInstances = parentMultiInstance.getNumberOfInstances(); log.debug("Multi-instance {} after child '{}' (id={}) notification ({}): " + "active={}, completed+terminated={}/{}", parentMultiInstance, childInstance.getName(), childInstance.getId(), stateCategory, numberOfActiveInstances, numberOfCompletedInstances + numberOfTerminatedInstances, numberOfInstances); if (parentMultiInstance.isSequential()) { // only instantiate when we are in sequence List createInnerInstances = null; if (stateBehaviors.shouldCreateANewInstance(loopCharacteristics, numberOfInstances, parentMultiInstance)) { createInnerInstances = stateBehaviors.createInnerInstances(processDefinition.getId(), activityDefinition, parentMultiInstance, 1); for (final SFlowNodeInstance sFlowNodeInstance : createInnerInstances) { containerRegistry.executeFlowNode(sFlowNodeInstance); } } return numberOfActiveInstances == 0 && (createInnerInstances == null || createInnerInstances.isEmpty()); } return numberOfActiveInstances == 0 || numberOfInstances == numberOfCompletedInstances + numberOfTerminatedInstances; } catch (final SBonitaException e) { throw new SActivityStateExecutionException(e); } } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityExecutionException { final int numberOfActiveInstances = ((SMultiInstanceActivityInstance) flowNodeInstance) .getNumberOfActiveInstances(); if (numberOfActiveInstances > 0) { stateBehaviors.executeChildrenActivities(flowNodeInstance); } return numberOfActiveInstances > 0; } @Override public final StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return StateCode.DONE; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ExecutingThrowEventState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; @Component public class ExecutingThrowEventState extends OnEnterAndFinishConnectorState { private final StateBehaviors stateBehaviors; public ExecutingThrowEventState(final StateBehaviors stateBehaviors) { super(stateBehaviors); this.stateBehaviors = stateBehaviors; } @Override public int getId() { return 26; } @Override public boolean isStable() { return false; } @Override public boolean isTerminal() { return false; } @Override public String getName() { return "executing"; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } @Override protected void beforeOnEnter(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.createData(processDefinition, flowNodeInstance); } @Override protected void onEnterToOnFinish(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); stateBehaviors.updateDisplayNameAndDescription(processDefinition, flowNodeInstance); stateBehaviors.mapActors(flowNodeInstance, processContainer); stateBehaviors.handleThrowEvent(processDefinition, flowNodeInstance); } @Override protected void afterOnFinish(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.updateDisplayDescriptionAfterCompletion(processDefinition, flowNodeInstance); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/FailedActivityState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.springframework.stereotype.Component; @Component public class FailedActivityState implements FlowNodeState { @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) throws SActivityStateExecutionException { return StateCode.DONE; } @Override public int getId() { return ID_ACTIVITY_FAILED; } @Override public boolean isStable() { return true; } @Override public boolean isTerminal() { return false; } @Override public String getName() { return "failed"; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { // FIXME: create failing category? return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/FlowNodeStateManager.java ================================================ /** * 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.execution.state; import java.util.Set; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.execution.StateBehaviors; /** * @author Baptiste Mesta * @author Yanyan Liu * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface FlowNodeStateManager { FlowNodeState getNextState(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance, int currentStateId) throws SActivityExecutionException; FlowNodeState getState(int stateId); Set getSupportedState(SFlowNodeType nodeType); StateBehaviors getStateBehaviors(); public FlowNodeState getFirstState(SFlowNodeType nodeType); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingActivityState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; @Component public class InitializingActivityState extends OnEnterConnectorState { private final StateBehaviors stateBehaviors; public InitializingActivityState(final StateBehaviors stateBehaviors) { super(stateBehaviors); this.stateBehaviors = stateBehaviors; } @Override public int getId() { return 0; } @Override public boolean isStable() { return false; } @Override public boolean isTerminal() { return false; } @Override public String getName() { return "initializing"; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } @Override protected void beforeConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.createData(processDefinition, flowNodeInstance); stateBehaviors.mapActors(flowNodeInstance, processDefinition.getProcessContainer()); } @Override protected void afterConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.updateDisplayNameAndDescription(processDefinition, flowNodeInstance); stateBehaviors.updateExpectedDuration(processDefinition, flowNodeInstance); stateBehaviors.registerWaitingEvent(processDefinition, flowNodeInstance); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingActivityWithBoundaryEventsState.java ================================================ /** * 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.execution.state; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SCallActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.execution.ProcessExecutor; import org.bonitasoft.engine.execution.StateBehaviors; import org.bonitasoft.engine.execution.work.BPMWorkFactory; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.mdc.MDCConstants; import org.bonitasoft.engine.mdc.MDCHelper; import org.bonitasoft.engine.mdc.ProcessInstanceMDC; import org.bonitasoft.engine.transaction.BonitaTransactionSynchronization; import org.bonitasoft.engine.transaction.UserTransactionService; import org.bonitasoft.engine.work.SWorkRegisterException; import org.bonitasoft.engine.work.WorkService; import org.slf4j.MDC; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @Component @Slf4j public class InitializingActivityWithBoundaryEventsState extends OnEnterConnectorState { private final StateBehaviors stateBehaviors; private final ExpressionResolverService expressionResolverService; private ProcessExecutor processExecutor; private final ActivityInstanceService activityInstanceService; private final ProcessDefinitionService processDefinitionService; private final UserTransactionService userTransactionService; private final WorkService workService; private final BPMWorkFactory workFactory; public InitializingActivityWithBoundaryEventsState(final StateBehaviors stateBehaviors, ExpressionResolverService expressionResolverService, ActivityInstanceService activityInstanceService, ProcessDefinitionService processDefinitionService, UserTransactionService userTransactionService, WorkService workService, BPMWorkFactory workFactory) { super(stateBehaviors); this.stateBehaviors = stateBehaviors; this.expressionResolverService = expressionResolverService; this.activityInstanceService = activityInstanceService; this.processDefinitionService = processDefinitionService; this.userTransactionService = userTransactionService; this.workService = workService; this.workFactory = workFactory; } //FIXME There is responsibility issue the circular dependencies must be fixed next time. @Autowired public void setProcessExecutor(@Lazy @Qualifier("processExecutor") ProcessExecutor processExecutor) { this.processExecutor = processExecutor; } public ProcessExecutor getProcessExecutor() { return processExecutor; } @Override public int getId() { return 32; } @Override public boolean isStable() { return false; } @Override public boolean isTerminal() { return false; } @Override public String getName() { return "initializing"; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } @Override protected void beforeConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.createData(processDefinition, flowNodeInstance); stateBehaviors.createAttachedBoundaryEvents(processDefinition, (SActivityInstance) flowNodeInstance); stateBehaviors.mapActors(flowNodeInstance, processDefinition.getProcessContainer()); } @Override protected void afterConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.updateDisplayNameAndDescription(processDefinition, flowNodeInstance); stateBehaviors.updateExpectedDuration(processDefinition, flowNodeInstance); stateBehaviors.addAssignmentSystemCommentIfTaskWasAutoAssign(flowNodeInstance); handleCallActivity(processDefinition, flowNodeInstance); stateBehaviors.registerWaitingEvent(processDefinition, flowNodeInstance); } private void handleCallActivity(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { if (SFlowNodeType.CALL_ACTIVITY.equals(flowNodeInstance.getType())) { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); try { final SCallActivityDefinition callActivity = (SCallActivityDefinition) processContainer .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); if (callActivity == null) { throw new SActivityStateExecutionException("Unable to find call activity definition with name " + flowNodeInstance.getName() + " in process definition " + processDefinition.getId()); } final SExpressionContext expressionContext = new SExpressionContext(flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinition.getId()); final String callableElement = (String) expressionResolverService .evaluate(callActivity.getCallableElement(), expressionContext); String callableElementVersion = null; if (callActivity.getCallableElementVersion() != null) { callableElementVersion = (String) expressionResolverService .evaluate(callActivity.getCallableElementVersion(), expressionContext); } final long targetProcessDefinitionId = getTargetProcessDefinitionId(callableElement, callableElementVersion); SProcessInstance sProcessInstance = instantiateProcess(processDefinition, callActivity, flowNodeInstance, targetProcessDefinitionId); final SCallActivityInstance callActivityInstance = (SCallActivityInstance) flowNodeInstance; // update token count if (sProcessInstance.getStateId() != ProcessInstanceState.COMPLETED.getId()) { activityInstanceService.setTokenCount(callActivityInstance, callActivityInstance.getTokenCount() + 1); } else { log.warn("Call activity {} with id {} started the process {} that is already completed. " + "This might due to an empty process or a process without 'NONE' start event.", callActivityInstance.getName(), callActivityInstance.getId(), sProcessInstance.getId()); // This need to be done "later" we register a work to execute this call activity because it is in fact finish // we can't do it right now because its state is not yet changed and the work would fail. // FIXME: it 's not normal to handle this here , it was added to fix the bug https://bonitasoft.atlassian.net/browse/BS-11654. // It's happens because the start process do too much things, we need to provide a new way that handle start process with work, like flow node handling // this is covered by integration CallActivityIT should_complete_call_activity_when_target_is_empty // this situation happens when the user do an error of designing process and we should almost to log a warning message to inform the user. userTransactionService.registerBonitaSynchronization(new BonitaTransactionSynchronization() { @Override public void beforeCompletion() { //the called process is finished, next step is stable so we trigger execution of this flownode try { workService.registerWork( workFactory.createExecuteFlowNodeWorkDescriptor(flowNodeInstance)); } catch (SWorkRegisterException e) { throw new BonitaRuntimeException(e); } } @Override public void afterCompletion(final int txState) { } }); } } catch (final SBonitaException e) { throw new SActivityStateExecutionException("Unable to handle call activity", ScopedException.DATA, e); } } } protected SProcessInstance instantiateProcess(final SProcessDefinition callerProcessDefinition, final SCallActivityDefinition callActivityDefinition, final SFlowNodeInstance callActivityInstance, final long targetProcessDefinitionId) throws SBonitaException { final long callerProcessDefinitionId = callerProcessDefinition.getId(); final long callerId = callActivityInstance.getId(); final SExpressionContext context = new SExpressionContext(callerId, DataInstanceContainer.ACTIVITY_INSTANCE.name(), callerProcessDefinitionId); /* * We are already in the context of the call activity, so the new MDC will conflict with current one. */ var parentContext = MDC.getCopyOfContextMap(); long rootProcessInstanceId = Optional.ofNullable(parentContext) .map(c -> c.get(MDCConstants.ROOT_PROCESS_INSTANCE_ID)).filter(Objects::nonNull).map(Long::valueOf) .orElse(0L); // AbstractMDC will temporarily erase conflicting values Supplier processInstanceMDC = () -> new ProcessInstanceMDC(0, Optional.empty(), Optional.empty(), targetProcessDefinitionId, Long.valueOf(rootProcessInstanceId)); return MDCHelper.tryWithMDC(processInstanceMDC, () -> { final Map processInputs = evaluateContractInputExpression( callActivityDefinition.getProcessStartContractInputs(), context); return processExecutor .start(targetProcessDefinitionId, -1, 0, 0, context, callActivityDefinition.getDataInputOperations(), callerId, -1, processInputs); }); } protected Map evaluateContractInputExpression(Map contractInputs, SExpressionContext context) throws SBonitaException { final Map evaluationResults = evaluateExpressions(contractInputs, context); final Map inputExpressionsMap = new HashMap<>(contractInputs.size()); for (Map.Entry entry : contractInputs.entrySet()) { inputExpressionsMap.put(entry.getKey(), evaluationResults.get(entry.getValue())); } return inputExpressionsMap; } private Map evaluateExpressions(Map contractInputs, SExpressionContext context) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { List expressionList = new ArrayList<>(contractInputs.values()); final List exprResults = expressionResolverService.evaluate(expressionList, context); final Map evaluationResults = new HashMap<>(contractInputs.size()); for (int i = 0; i < expressionList.size(); i++) { evaluationResults.put(expressionList.get(i), (Serializable) exprResults.get(i)); } return evaluationResults; } private long getTargetProcessDefinitionId(final String callableElement, final String callableElementVersion) throws SBonitaException { if (callableElementVersion != null) { return processDefinitionService.getProcessDefinitionId(callableElement, callableElementVersion); } return processDefinitionService.getLatestProcessDefinitionId(callableElement); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingAndExecutingFlowNodeState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; @Component public class InitializingAndExecutingFlowNodeState extends OnEnterAndFinishConnectorState { public static final int ID = 61; private final StateBehaviors stateBehaviors; public InitializingAndExecutingFlowNodeState(final StateBehaviors stateBehaviors) { super(stateBehaviors); this.stateBehaviors = stateBehaviors; } @Override public int getId() { return ID; } @Override public boolean isStable() { return false; } @Override public boolean isTerminal() { return false; } @Override public String getName() { return "executing"; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } @Override protected void beforeOnEnter(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.createData(processDefinition, flowNodeInstance); stateBehaviors.mapActors(flowNodeInstance, processDefinition.getProcessContainer()); } @Override protected void onEnterToOnFinish(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.updateDisplayNameAndDescription(processDefinition, flowNodeInstance); } @Override protected void afterOnFinish(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.updateDisplayDescriptionAfterCompletion(processDefinition, flowNodeInstance); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingBoundaryEventState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; @Component public class InitializingBoundaryEventState extends OnEnterConnectorState { private final StateBehaviors stateBehaviors; public InitializingBoundaryEventState(final StateBehaviors stateBehaviors) { super(stateBehaviors); this.stateBehaviors = stateBehaviors; } @Override public int getId() { return 33; } @Override public boolean isStable() { return false; } @Override public boolean isTerminal() { return false; } @Override public String getName() { return "initializing"; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } @Override protected void beforeConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.handleBoundaryEvent(processDefinition, (SBoundaryEventInstance) flowNodeInstance); } @Override protected void afterConnectors(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.updateDisplayNameAndDescription(processDefinition, flowNodeInstance); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingLoopActivityState.java ================================================ /** * 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.execution.state; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SLoopCharacteristics; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SStandardLoopCharacteristics; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; 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.SLoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilderFactory; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.execution.StateBehaviors; import org.bonitasoft.engine.expression.ExpressionConstants; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.springframework.stereotype.Component; @Component public class InitializingLoopActivityState implements FlowNodeState { private final ExpressionResolverService expressionResolverService; private final BPMInstancesCreator bpmInstancesCreator; private final ActivityInstanceService activityInstanceService; private final StateBehaviors stateBehaviors; public InitializingLoopActivityState(final ExpressionResolverService expressionResolverService, final BPMInstancesCreator bpmInstancesCreator, final ActivityInstanceService activityInstanceService, final StateBehaviors stateBehaviors) { this.expressionResolverService = expressionResolverService; this.bpmInstancesCreator = bpmInstancesCreator; this.activityInstanceService = activityInstanceService; this.stateBehaviors = stateBehaviors; } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.createAttachedBoundaryEvents(processDefinition, (SActivityInstance) flowNodeInstance); final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final Long processDefinitionId = processDefinition.getId(); final SActivityDefinition activity = (SActivityDefinition) processContainer .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); try { final SLoopActivityInstance loopActivity = (SLoopActivityInstance) activityInstanceService .getFlowNodeInstance(flowNodeInstance.getId()); final SLoopCharacteristics loopCharacteristics = activity.getLoopCharacteristics(); if (loopCharacteristics instanceof SStandardLoopCharacteristics standardLoop) { final SExpression loopMax = ((SStandardLoopCharacteristics) loopCharacteristics).getLoopMax(); Integer intLoopMax; if (loopMax != null) { intLoopMax = (Integer) expressionResolverService.evaluate(loopMax, new SExpressionContext(loopActivity.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinitionId)); activityInstanceService.setLoopMax(loopActivity, intLoopMax); } final boolean loop = !standardLoop.isTestBefore() || evaluateLoop(standardLoop, loopActivity); if (loop) { final SLoopActivityInstanceBuilderFactory keyProvider = BuilderFactory .get(SLoopActivityInstanceBuilderFactory.class); final long rootProcessInstanceId = flowNodeInstance .getLogicalGroup(keyProvider.getRootProcessInstanceIndex()); final long parentProcessInstanceId = flowNodeInstance .getLogicalGroup(keyProvider.getParentProcessInstanceIndex()); bpmInstancesCreator.createFlowNodeInstance(processDefinitionId, flowNodeInstance.getRootContainerId(), flowNodeInstance.getId(), SFlowElementsContainerType.FLOWNODE, activity, rootProcessInstanceId, parentProcessInstanceId, true, 1, SStateCategory.NORMAL, -1); activityInstanceService.incrementLoopCounter(loopActivity); activityInstanceService.setTokenCount(loopActivity, loopActivity.getTokenCount() + 1); } } } catch (final SBonitaException e) { throw new SActivityStateExecutionException(e); } return StateCode.DONE; } private boolean evaluateLoop(final SStandardLoopCharacteristics standardLoop, final SLoopActivityInstance loopActivity) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { final Map input = new HashMap<>(1); input.put(ExpressionConstants.LOOP_COUNTER.getEngineConstantName(), loopActivity.getLoopCounter()); final SExpressionContext sExpressionContext = new SExpressionContext(loopActivity.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), loopActivity.getProcessDefinitionId(), input); return ((Boolean) expressionResolverService.evaluate(standardLoop.getLoopCondition(), sExpressionContext)) .booleanValue(); } @Override public int getId() { return 23; } @Override public boolean isStable() { return false; } @Override public boolean isTerminal() { return false; } @Override public String getName() { return "initializing"; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InitializingMultiInstanceActivityState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SLoopCharacteristics; import org.bonitasoft.engine.core.process.definition.model.SMultiInstanceLoopCharacteristics; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.execution.StateBehaviors; import org.bonitasoft.engine.expression.model.SExpression; import org.springframework.stereotype.Component; @Component public class InitializingMultiInstanceActivityState implements FlowNodeState { private final ExpressionResolverService expressionResolverService; private final ActivityInstanceService activityInstanceService; private final StateBehaviors stateBehaviors; public InitializingMultiInstanceActivityState(final ExpressionResolverService expressionResolverService, final ActivityInstanceService activityInstanceService, final StateBehaviors stateBehaviors) { this.expressionResolverService = expressionResolverService; this.activityInstanceService = activityInstanceService; this.stateBehaviors = stateBehaviors; } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { try { final SMultiInstanceActivityInstance multiInstanceActivityInstance = (SMultiInstanceActivityInstance) flowNodeInstance; stateBehaviors.createAttachedBoundaryEvents(processDefinition, multiInstanceActivityInstance); final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SActivityDefinition activity = (SActivityDefinition) processContainer .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); final SLoopCharacteristics loopCharacteristics = activity.getLoopCharacteristics(); if (loopCharacteristics instanceof SMultiInstanceLoopCharacteristics miLoop) { final SExpression loopCardinality = miLoop.getLoopCardinality(); int numberOfInstanceMax = -1; if (loopCardinality != null) { numberOfInstanceMax = (Integer) expressionResolverService.evaluate(loopCardinality, new SExpressionContext(multiInstanceActivityInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinition .getId())); activityInstanceService.setLoopCardinality(multiInstanceActivityInstance, numberOfInstanceMax); } else if (miLoop.getLoopDataInputRef() != null) { numberOfInstanceMax = stateBehaviors.getNumberOfInstancesToCreateFromInputRef(processDefinition, multiInstanceActivityInstance, miLoop, numberOfInstanceMax); } if (numberOfInstanceMax < 0) { throw new SActivityStateExecutionException( "The multi instance on activity " + flowNodeInstance.getName() + " of process " + processDefinition.getName() + " " + processDefinition.getVersion() + " did not have loop cardinality nor loop data input ref set", ScopedException.ITERATION); } stateBehaviors.updateOutputData(processDefinition, multiInstanceActivityInstance, miLoop, numberOfInstanceMax); if (numberOfInstanceMax > 0) { stateBehaviors.createInnerInstances(processDefinition.getId(), activity, multiInstanceActivityInstance, miLoop.isSequential() ? 1 : numberOfInstanceMax); } } } catch (final SActivityStateExecutionException e) { throw e; } catch (final SBonitaException e) { throw new SActivityStateExecutionException("Failed to execute multi instance activity " + flowNodeInstance, ScopedException.ITERATION, e); } return StateCode.DONE; } @Override public int getId() { return 27; } @Override public boolean isStable() { return false; } @Override public boolean isTerminal() { return false; } @Override public String getName() { return "initializing"; } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) { return true; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/InterruptingBoundaryAndIntermediateCatchEventState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SCatchEventDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance; import org.bonitasoft.engine.execution.WaitingEventsInterrupter; public abstract class InterruptingBoundaryAndIntermediateCatchEventState implements FlowNodeState { private final WaitingEventsInterrupter waitingEventsInterrupter; public InterruptingBoundaryAndIntermediateCatchEventState(WaitingEventsInterrupter waitingEventsInterrupter) { super(); this.waitingEventsInterrupter = waitingEventsInterrupter; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) throws SActivityStateExecutionException { final SCatchEventDefinition catchEventDef = (SCatchEventDefinition) processDefinition.getProcessContainer() .getFlowNode( instance.getFlowNodeDefinitionId()); try { final SCatchEventInstance catchEventInstance = (SCatchEventInstance) instance; waitingEventsInterrupter.interruptWaitingEvents(processDefinition, catchEventInstance, catchEventDef); } catch (final SBonitaException e) { throw new SActivityStateExecutionException(e); } return StateCode.DONE; } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) { return true; } @Override public boolean isStable() { return false; } @Override public boolean isTerminal() { return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/OnEnterAndFinishConnectorState.java ================================================ /** * 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.execution.state; import java.util.List; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException; import org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.execution.StateBehaviors; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Baptiste Mesta */ public abstract class OnEnterAndFinishConnectorState implements FlowNodeState { private final StateBehaviors stateBehaviors; public OnEnterAndFinishConnectorState(StateBehaviors stateBehaviors) { this.stateBehaviors = stateBehaviors; } public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { // Retrieve the phase to execute depending on which connectors to execute and when to execute them: try { //get all element to check where we are in the state execution List onEnterConnectors = stateBehaviors.getConnectorDefinitions(processDefinition, flowNodeInstance, ConnectorEvent.ON_ENTER); final SConnectorInstance onEnterConnectorToExecute = stateBehaviors.getNextConnectorInstance( onEnterConnectors, flowNodeInstance, ConnectorEvent.ON_ENTER); boolean noConnectorStartedOnEnter = stateBehaviors.noConnectorHasStartedInCurrentList(onEnterConnectors, onEnterConnectorToExecute); List onFinishConnectors = stateBehaviors.getConnectorDefinitions(processDefinition, flowNodeInstance, ConnectorEvent.ON_FINISH); final SConnectorInstance onFinishConnectorToExecute = stateBehaviors.getNextConnectorInstance( onFinishConnectors, flowNodeInstance, ConnectorEvent.ON_FINISH); boolean noConnectorStartedOnFinish = stateBehaviors.noConnectorHasStartedInCurrentList(onFinishConnectors, onFinishConnectorToExecute); //if no connector has started neither on enter or on finish we can do the beforeOnEnter phase if (noConnectorStartedOnEnter && noConnectorStartedOnFinish) { beforeOnEnter(processDefinition, flowNodeInstance); } if (onEnterConnectorToExecute != null) { stateBehaviors.executeConnector(processDefinition, flowNodeInstance, onEnterConnectors, onEnterConnectorToExecute); return StateCode.EXECUTING; } if (noConnectorStartedOnFinish) { onEnterToOnFinish(processDefinition, flowNodeInstance); } if (onFinishConnectorToExecute != null) { stateBehaviors.executeConnector(processDefinition, flowNodeInstance, onFinishConnectors, onFinishConnectorToExecute); return StateCode.EXECUTING; } afterOnFinish(processDefinition, flowNodeInstance); return StateCode.DONE; } catch (SBonitaReadException | SConnectorInstanceReadException e) { throw new SActivityStateExecutionException(e); } } protected abstract void beforeOnEnter(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException; protected abstract void onEnterToOnFinish(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException; protected abstract void afterOnFinish(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/OnEnterConnectorState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta */ @Component public abstract class OnEnterConnectorState extends OnEnterOrOnFinishConnectorState { public OnEnterConnectorState(StateBehaviors stateBehaviors) { super(stateBehaviors); } @Override protected ConnectorEvent getConnectorEvent() { return ConnectorEvent.ON_ENTER; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/OnEnterOrOnFinishConnectorState.java ================================================ /** * 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.execution.state; import java.util.List; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException; import org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.execution.StateBehaviors; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta */ @Component public abstract class OnEnterOrOnFinishConnectorState implements FlowNodeState { protected final StateBehaviors stateBehaviors; public OnEnterOrOnFinishConnectorState(StateBehaviors stateBehaviors) { this.stateBehaviors = stateBehaviors; } protected abstract void beforeConnectors(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException; protected abstract void afterConnectors(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException; public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { // Retrieve the phase to execute depending on which connectors to execute and when to execute them: try { ConnectorEvent connectorEvent = getConnectorEvent(); //get all element to check where we are in the state execution List connectorDefinitions = stateBehaviors.getConnectorDefinitions(processDefinition, flowNodeInstance, connectorEvent); final SConnectorInstance connectorInstance = stateBehaviors.getNextConnectorInstance(connectorDefinitions, flowNodeInstance, connectorEvent); boolean noConnectorStartedOnEnter = stateBehaviors.noConnectorHasStartedInCurrentList(connectorDefinitions, connectorInstance); if (noConnectorStartedOnEnter) { //TODO unit test condition on this beforeConnectors(processDefinition, flowNodeInstance); } if (connectorInstance != null) { stateBehaviors.executeConnector(processDefinition, flowNodeInstance, connectorDefinitions, connectorInstance); return StateCode.EXECUTING; } afterConnectors(processDefinition, flowNodeInstance); return StateCode.DONE; } catch (SBonitaReadException | SConnectorInstanceReadException e) { throw new SActivityStateExecutionException(e); } } protected abstract ConnectorEvent getConnectorEvent(); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/OnFinishConnectorState.java ================================================ /** * 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.execution.state; /** * @author Baptiste Mesta */ import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; @Component public abstract class OnFinishConnectorState extends OnEnterOrOnFinishConnectorState { public OnFinishConnectorState(StateBehaviors stateBehaviors) { super(stateBehaviors); } @Override protected ConnectorEvent getConnectorEvent() { return ConnectorEvent.ON_FINISH; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/ReadyActivityState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.execution.StateBehaviors; import org.springframework.stereotype.Component; @Component public class ReadyActivityState extends OnEnterAndFinishConnectorState { public static final int ID = 4; private final StateBehaviors stateBehaviors; public ReadyActivityState(final StateBehaviors stateBehaviors) { super(stateBehaviors); this.stateBehaviors = stateBehaviors; } @Override protected void beforeOnEnter(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { if (((SHumanTaskInstance) flowNodeInstance).getAssigneeId() <= 0) { throw new SActivityStateExecutionException("The activity is not yet assigned, unable to execute it"); } } @Override protected void onEnterToOnFinish(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.executeOperations(processDefinition, (SActivityInstance) flowNodeInstance); } @Override protected void afterOnFinish(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityStateExecutionException { stateBehaviors.mapDataOutputOfMultiInstance(processDefinition, flowNodeInstance); stateBehaviors.updateDisplayDescriptionAfterCompletion(processDefinition, flowNodeInstance); } @Override public int getId() { return ID; } @Override public boolean isStable() { return true; } @Override public boolean isTerminal() { return false; } @Override public String getName() { return "ready"; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/SkippedFlowNodeState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.springframework.stereotype.Component; @Component public class SkippedFlowNodeState implements FlowNodeState { public static final int ID = 12; public SkippedFlowNodeState() { } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) { return StateCode.DONE; } @Override public int getId() { return ID; } @Override public String getName() { return "skipped"; } @Override public boolean isStable() { return true; } @Override public boolean isTerminal() { return true; } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) { return parentInstance.getTokenCount() == 0; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return flowNodeInstance instanceof SHumanTaskInstance; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return " User " + flowNodeInstance.getExecutedBy() + " has " + getName() + " task " + flowNodeInstance.getName(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/state/WaitingFlowNodeState.java ================================================ /** * 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.execution.state; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.api.states.StateCode; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.springframework.stereotype.Component; @Component public class WaitingFlowNodeState implements FlowNodeState { public WaitingFlowNodeState() { } @Override public StateCode execute(final SProcessDefinition processDefinition, final SFlowNodeInstance instance) { return StateCode.DONE; } @Override public int getId() { return 10; } @Override public String getName() { return "waiting"; } @Override public boolean isStable() { return true; } @Override public boolean isTerminal() { return false; } @Override public boolean notifyChildFlowNodeHasFinished(final SProcessDefinition processDefinition, final SFlowNodeInstance parentInstance, final SFlowNodeInstance childInstance) { return true; } @Override public boolean shouldExecuteState(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance) { return true; } @Override public SStateCategory getStateCategory() { return SStateCategory.NORMAL; } @Override public boolean mustAddSystemComment(final SFlowNodeInstance flowNodeInstance) { return false; } @Override public String getSystemComment(final SFlowNodeInstance flowNodeInstance) { return ""; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/AutomaticTaskStates.java ================================================ /** * Copyright (C) 2020 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.transition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.execution.state.AbortedFlowNodeState; import org.bonitasoft.engine.execution.state.AbortingActivityWithBoundaryState; import org.bonitasoft.engine.execution.state.AbortingBoundaryEventsOnCompletingActivityState; import org.bonitasoft.engine.execution.state.AbortingFlowNodeContainerState; import org.bonitasoft.engine.execution.state.CancelledFlowNodeState; import org.bonitasoft.engine.execution.state.CancellingActivityWithBoundaryState; import org.bonitasoft.engine.execution.state.CancellingFlowNodeContainerChildrenState; import org.bonitasoft.engine.execution.state.CompletedActivityState; import org.bonitasoft.engine.execution.state.ExecutingAutomaticActivityState; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class AutomaticTaskStates extends FlowNodeStateSequences { public SFlowNodeType getFlowNodeType() { return SFlowNodeType.AUTOMATIC_TASK; } public AutomaticTaskStates( AbortingBoundaryEventsOnCompletingActivityState abortingBoundaryEventsOnCompletingActivityState, CompletedActivityState completed, AbortingActivityWithBoundaryState abortingActivityWithBoundary, AbortedFlowNodeState aborted, @Qualifier("cancellingActivityWithBoundaryState") CancellingActivityWithBoundaryState cancellingActivityWithBoundary, CancelledFlowNodeState cancelled, ExecutingAutomaticActivityState executingAutomaticActivity, CancellingFlowNodeContainerChildrenState cancellingContainer, @Qualifier("abortingFlowNodeContainerState") AbortingFlowNodeContainerState abortingContainer) { defineNormalSequence(executingAutomaticActivity, abortingBoundaryEventsOnCompletingActivityState, completed); defineAbortSequence(abortingActivityWithBoundary, abortingContainer, aborted); defineCancelSequence(cancellingActivityWithBoundary, cancellingContainer, cancelled); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/BoundaryEventStates.java ================================================ /** * Copyright (C) 2020 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.transition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.execution.state.*; import org.springframework.stereotype.Component; @Component public class BoundaryEventStates extends FlowNodeStateSequences { public SFlowNodeType getFlowNodeType() { return SFlowNodeType.BOUNDARY_EVENT; } public BoundaryEventStates(CompletedActivityState completed, AbortingBoundaryAndIntermediateCatchEventState aborting, AbortedFlowNodeState aborted, CancellingBoundaryAndIntermediateCatchEventState cancelling, CancelledFlowNodeState cancelled, WaitingFlowNodeState waiting, InitializingBoundaryEventState initializingBoundaryEvent, ExecutingBoundaryEventState executingBoundaryEvent) { defineNormalSequence(initializingBoundaryEvent, waiting, executingBoundaryEvent, completed); defineAbortSequence(aborting, aborted); defineCancelSequence(cancelling, cancelled); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/CallActivityTaskStates.java ================================================ /** * Copyright (C) 2020 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.transition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.execution.state.AbortedFlowNodeState; import org.bonitasoft.engine.execution.state.AbortingActivityWithBoundaryState; import org.bonitasoft.engine.execution.state.AbortingBoundaryEventsOnCompletingActivityState; import org.bonitasoft.engine.execution.state.AbortingCallActivityState; import org.bonitasoft.engine.execution.state.CancelledFlowNodeState; import org.bonitasoft.engine.execution.state.CancellingActivityWithBoundaryState; import org.bonitasoft.engine.execution.state.CancellingCallActivityState; import org.bonitasoft.engine.execution.state.CompletedActivityState; import org.bonitasoft.engine.execution.state.CompletingCallActivityState; import org.bonitasoft.engine.execution.state.ExecutingCallActivityState; import org.bonitasoft.engine.execution.state.InitializingActivityWithBoundaryEventsState; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class CallActivityTaskStates extends FlowNodeStateSequences { public SFlowNodeType getFlowNodeType() { return SFlowNodeType.CALL_ACTIVITY; } public CallActivityTaskStates(InitializingActivityWithBoundaryEventsState initializingActivityWithBoundary, AbortingBoundaryEventsOnCompletingActivityState abortingBoundaryEventsOnCompletingActivityState, CompletedActivityState completed, AbortingActivityWithBoundaryState abortingActivityWithBoundary, AbortedFlowNodeState aborted, @Qualifier("cancellingActivityWithBoundaryState") CancellingActivityWithBoundaryState cancellingActivityWithBoundary, CancelledFlowNodeState cancelled, ExecutingCallActivityState executingCallActivity, CompletingCallActivityState completingCallActivity, AbortingCallActivityState abortingCallActivity, CancellingCallActivityState cancellingCallActivity) { defineNormalSequence(initializingActivityWithBoundary, executingCallActivity, abortingBoundaryEventsOnCompletingActivityState, completingCallActivity, completed); defineAbortSequence(abortingActivityWithBoundary, abortingCallActivity, aborted); defineCancelSequence(cancellingActivityWithBoundary, cancellingCallActivity, cancelled); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/DefaultTransitionGetter.java ================================================ /** * 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.execution.transition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper; /** * @author Elias Ricken de Medeiros */ public class DefaultTransitionGetter { STransitionDefinition getDefaultTransition(FlowNodeTransitionsWrapper transitions, SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance) throws SActivityExecutionException { STransitionDefinition defaultTransition = transitions.getDefaultTransition(); if (defaultTransition == null) { final SActivityExecutionException exception = new SActivityExecutionException( "There is no default transition on " + flowNodeInstance.getName() + ", but no outgoing transition had a valid condition."); exception.setProcessDefinitionNameOnContext(processDefinition.getName()); exception.setProcessDefinitionVersionOnContext(processDefinition.getVersion()); exception.setProcessInstanceIdOnContext(flowNodeInstance.getParentProcessInstanceId()); throw exception; } return defaultTransition; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/EndEventStates.java ================================================ /** * Copyright (C) 2020 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.transition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.execution.state.AbortedFlowNodeState; import org.bonitasoft.engine.execution.state.AbortingFlowNodeState; import org.bonitasoft.engine.execution.state.CancelledFlowNodeState; import org.bonitasoft.engine.execution.state.CancellingFlowNodeState; import org.bonitasoft.engine.execution.state.CompletedActivityState; import org.bonitasoft.engine.execution.state.ExecutingThrowEventState; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class EndEventStates extends FlowNodeStateSequences { public SFlowNodeType getFlowNodeType() { return SFlowNodeType.END_EVENT; } @Autowired public EndEventStates(CompletedActivityState completed, AbortedFlowNodeState aborted, CancelledFlowNodeState cancelled, AbortingFlowNodeState abortingFlowNode, CancellingFlowNodeState cancellingFlowNode, ExecutingThrowEventState executingThrowEvent) { defineNormalSequence(executingThrowEvent, completed); defineAbortSequence(abortingFlowNode, aborted); defineCancelSequence(cancellingFlowNode, cancelled); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/EvaluatedTransitions.java ================================================ /** * 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.execution.transition; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; /** * @author Elias Ricken de Medeiros */ public class EvaluatedTransitions { private List unconditionalTransitions = new ArrayList(); private List trueTransitions = new ArrayList(); private List falseTransitions = new ArrayList(); /** * Add a transition to unconditional transitions * * @param transitionDefinition unconditional transition */ void addUnconditionalTransition(STransitionDefinition transitionDefinition) { unconditionalTransitions.add(transitionDefinition); } /** * Add a transition to transitions having conditions evaluated to true * * @param transitionDefinition the transition which condition was evaluated to true */ void addTrueTransition(STransitionDefinition transitionDefinition) { trueTransitions.add(transitionDefinition); } /** * Add a transition to transitions having conditions evaluated to false * * @param transitionDefinition the transition which condition was evaluated to false */ void addFalseTransition(STransitionDefinition transitionDefinition) { falseTransitions.add(transitionDefinition); } /** * @return list of unconditional transitions */ public List getUnconditionalTransitions() { return unconditionalTransitions; } /** * @return list of transitions which conditions were evaluated to true */ public List getTrueTransitions() { return trueTransitions; } /** * @return true if there is at least one unconditional transition; false otherwise */ public boolean hasUnconditionalTransitions() { return !unconditionalTransitions.isEmpty(); } /** * @return true if there is at least one transition which condition was evaluated to true; false otherwise */ public boolean hasTrueTransitions() { return !trueTransitions.isEmpty(); } /** * @return true if there is at least one transition which condition was evaluated to false; false otherwise */ public boolean hasFalseTransitons() { return !falseTransitions.isEmpty(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/ExclusiveGatewayTransitionEvaluationStrategy.java ================================================ /** * 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.execution.transition; /** * @author Elias Ricken de Medeiros */ public class ExclusiveGatewayTransitionEvaluationStrategy implements TransitionEvaluationStrategy { @Override public boolean shouldContinue(final boolean alreadyFound) { return !alreadyFound; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/FlowNodeStateSequences.java ================================================ /** * Copyright (C) 2020 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.transition; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.springframework.stereotype.Component; /** * For a given flow node type, this class maintains the supported flow node states and the sequence * and transitions from one state to another. * Each FlowNodeStatesAndTransitions class defines 3 state sequences: *
    *
  • one for the normal flow
  • *
  • one for the aborting flow
  • *
  • one for the cancelling flow
  • *
*/ @Component public abstract class FlowNodeStateSequences { /* * store all states of a given state category, and their order. */ private static class StateSequence { Map mapOfCurrentStateIdToNextState = new HashMap<>(); FlowNodeState firstState; public StateSequence(FlowNodeState... flowNodeStates) { firstState = flowNodeStates[0]; for (int i = 0; i < flowNodeStates.length - 1; i++) { // key = id of the current state // value = next state mapOfCurrentStateIdToNextState.put(flowNodeStates[i].getId(), flowNodeStates[i + 1]); } } public FlowNodeState getFirstState() { return firstState; } public FlowNodeState getStateAfter(int previousStateId) { return mapOfCurrentStateIdToNextState.get(previousStateId); } } private StateSequence normalSequence; private StateSequence cancelSequence; private StateSequence abortSequence; protected void defineNormalSequence(FlowNodeState... flowNodeStates) { normalSequence = new StateSequence(flowNodeStates); } protected void defineCancelSequence(FlowNodeState... flowNodeStates) { cancelSequence = new StateSequence(flowNodeStates); } protected void defineAbortSequence(FlowNodeState... flowNodeStates) { abortSequence = new StateSequence(flowNodeStates); } public abstract SFlowNodeType getFlowNodeType(); public FlowNodeState getFirstState(SStateCategory category) { return getSequence(category).getFirstState(); } public FlowNodeState getStateAfter(SStateCategory category, int currentStateId) { return getSequence(category).getStateAfter(currentStateId); } private StateSequence getSequence(SStateCategory category) { switch (category) { case NORMAL: return normalSequence; case ABORTING: return abortSequence; case CANCELLING: return cancelSequence; default: throw new IllegalStateException("Unexpected value: " + category); } } public Set getSupportedStates() { Set names = new HashSet<>(); names.add(normalSequence.firstState.getName()); normalSequence.mapOfCurrentStateIdToNextState.values().forEach(s -> names.add(s.getName())); return names; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/GatewaysStates.java ================================================ /** * Copyright (C) 2020 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.transition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.execution.state.AbortedFlowNodeState; import org.bonitasoft.engine.execution.state.AbortingFlowNodeState; import org.bonitasoft.engine.execution.state.CancelledFlowNodeState; import org.bonitasoft.engine.execution.state.CancellingFlowNodeState; import org.bonitasoft.engine.execution.state.CompletedActivityState; import org.bonitasoft.engine.execution.state.InitializingAndExecutingFlowNodeState; import org.springframework.stereotype.Component; @Component public class GatewaysStates extends FlowNodeStateSequences { public SFlowNodeType getFlowNodeType() { return SFlowNodeType.GATEWAY; } public GatewaysStates(CompletedActivityState completed, AbortedFlowNodeState aborted, CancelledFlowNodeState cancelled, AbortingFlowNodeState abortingFlowNode, CancellingFlowNodeState cancellingFlowNode, InitializingAndExecutingFlowNodeState initializingAndExecuting) { defineNormalSequence(initializingAndExecuting, completed); defineAbortSequence(abortingFlowNode, aborted); defineCancelSequence(cancellingFlowNode, cancelled); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/ImplicitGatewayTransitionEvaluator.java ================================================ /** * 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.execution.transition; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper; /** * @author Elias Ricken de Medeiros */ public class ImplicitGatewayTransitionEvaluator { private final TransitionConditionEvaluator conditionEvaluator; private final DefaultTransitionGetter defaultTransitionGetter; public ImplicitGatewayTransitionEvaluator(TransitionConditionEvaluator conditionEvaluator, DefaultTransitionGetter defaultTransitionGetter) { this.conditionEvaluator = conditionEvaluator; this.defaultTransitionGetter = defaultTransitionGetter; } public List evaluateTransitions(final SProcessDefinition sDefinition, final SFlowNodeInstance flowNodeInstance, FlowNodeTransitionsWrapper transitions, final SExpressionContext sExpressionContext) throws SBonitaException { EvaluatedTransitions evaluatedTransitions = evaluatedTransitions(sExpressionContext, transitions.getNonDefaultOutgoingTransitionDefinitions()); return buildChosenTransitions(evaluatedTransitions, transitions, sDefinition, flowNodeInstance); } private List buildChosenTransitions(final EvaluatedTransitions evaluatedTransitions, final FlowNodeTransitionsWrapper transitions, final SProcessDefinition sDefinition, final SFlowNodeInstance flowNodeInstance) throws SActivityExecutionException { final List chosenTransitions = new ArrayList<>( evaluatedTransitions.getUnconditionalTransitions().size() + evaluatedTransitions.getTrueTransitions().size()); if (evaluatedTransitions.hasUnconditionalTransitions()) { chosenTransitions.addAll(evaluatedTransitions.getUnconditionalTransitions()); } if (evaluatedTransitions.hasTrueTransitions()) { chosenTransitions.addAll(evaluatedTransitions.getTrueTransitions()); } else if (evaluatedTransitions.hasFalseTransitons()) { final STransitionDefinition defaultTransition = defaultTransitionGetter.getDefaultTransition(transitions, sDefinition, flowNodeInstance); chosenTransitions.add(defaultTransition); } return chosenTransitions; } private EvaluatedTransitions evaluatedTransitions(final SExpressionContext sExpressionContext, final List outgoingTransitionDefinitions) throws SBonitaException { EvaluatedTransitions evaluatedTransitions = new EvaluatedTransitions(); for (final STransitionDefinition sTransitionDefinition : outgoingTransitionDefinitions) { evaluateTransition(evaluatedTransitions, sTransitionDefinition, sExpressionContext); } return evaluatedTransitions; } private void evaluateTransition(final EvaluatedTransitions evaluatedTransitions, final STransitionDefinition sTransitionDefinition, final SExpressionContext sExpressionContext) throws SBonitaException { final Boolean condition = conditionEvaluator.evaluateCondition(sTransitionDefinition, sExpressionContext); if (!sTransitionDefinition.hasCondition()) { evaluatedTransitions.addUnconditionalTransition(sTransitionDefinition); } else { if (condition != null && condition) { evaluatedTransitions.addTrueTransition(sTransitionDefinition); } else { evaluatedTransitions.addFalseTransition(sTransitionDefinition); } } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/InclusiveExclusiveTransitionEvaluator.java ================================================ /** * 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.execution.transition; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper; /** * @author Elias Ricken de Medeiros */ public class InclusiveExclusiveTransitionEvaluator { private final TransitionEvaluationStrategy strategy; private final TransitionConditionEvaluator evaluator; private final DefaultTransitionGetter defaultTransitionGetter; public InclusiveExclusiveTransitionEvaluator(TransitionEvaluationStrategy strategy, TransitionConditionEvaluator evaluator, DefaultTransitionGetter defaultTransitionGetter) { this.strategy = strategy; this.evaluator = evaluator; this.defaultTransitionGetter = defaultTransitionGetter; } public List evaluateTransitions(final SProcessDefinition sDefinition, final SFlowNodeInstance flowNodeInstance, FlowNodeTransitionsWrapper transitions, final SExpressionContext sExpressionContext) throws SBonitaException { List outgoingTransitionDefinitions = transitions .getNonDefaultOutgoingTransitionDefinitions(); final List chosenTransitions = evaluateNonDefaultTransitions(sExpressionContext, outgoingTransitionDefinitions); if (chosenTransitions.isEmpty()) { STransitionDefinition defaultTransition = defaultTransitionGetter.getDefaultTransition(transitions, sDefinition, flowNodeInstance); chosenTransitions.add(defaultTransition); } return chosenTransitions; } private List evaluateNonDefaultTransitions(final SExpressionContext sExpressionContext, final List outgoingTransitionDefinitions) throws SBonitaException { final List chosenTransitions = new ArrayList<>(outgoingTransitionDefinitions.size()); boolean found = false; Iterator iterator = outgoingTransitionDefinitions.iterator(); while (iterator.hasNext() && strategy.shouldContinue(found)) { STransitionDefinition transitionDefinition = iterator.next(); Boolean shouldTakeTransition = evaluator.evaluateCondition(transitionDefinition, sExpressionContext); if (!transitionDefinition.hasCondition() || (shouldTakeTransition != null && shouldTakeTransition)) { chosenTransitions.add(transitionDefinition); found = true; } } return chosenTransitions; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/InclusiveGatewayTransitionEvaluationStrategy.java ================================================ /** * 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.execution.transition; /** * @author Elias Ricken de Medeiros */ public class InclusiveGatewayTransitionEvaluationStrategy implements TransitionEvaluationStrategy { @Override public boolean shouldContinue(final boolean alreadyFound) { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/IntermediateCatchEventStates.java ================================================ /** * Copyright (C) 2020 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.transition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.execution.state.AbortedFlowNodeState; import org.bonitasoft.engine.execution.state.AbortingBoundaryAndIntermediateCatchEventState; import org.bonitasoft.engine.execution.state.CancelledFlowNodeState; import org.bonitasoft.engine.execution.state.CancellingBoundaryAndIntermediateCatchEventState; import org.bonitasoft.engine.execution.state.CompletedActivityState; import org.bonitasoft.engine.execution.state.ExecutingFlowNodeState; import org.bonitasoft.engine.execution.state.InitializingActivityState; import org.bonitasoft.engine.execution.state.WaitingFlowNodeState; import org.springframework.stereotype.Component; @Component public class IntermediateCatchEventStates extends FlowNodeStateSequences { public SFlowNodeType getFlowNodeType() { return SFlowNodeType.INTERMEDIATE_CATCH_EVENT; } public IntermediateCatchEventStates(CompletedActivityState completed, AbortedFlowNodeState aborted, CancelledFlowNodeState cancelled, CancellingBoundaryAndIntermediateCatchEventState cancelingBoundaryAndIntermediateCatchEvent, ExecutingFlowNodeState executing, InitializingActivityState initializing, WaitingFlowNodeState waiting, AbortingBoundaryAndIntermediateCatchEventState abortingBoundaryAndIntermediateCatchEvent) { defineNormalSequence(initializing, waiting, executing, completed); defineAbortSequence(abortingBoundaryAndIntermediateCatchEvent, aborted); defineCancelSequence(cancelingBoundaryAndIntermediateCatchEvent, cancelled); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/IntermediateThrowEventStates.java ================================================ /** * Copyright (C) 2020 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.transition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.execution.state.AbortedFlowNodeState; import org.bonitasoft.engine.execution.state.AbortingFlowNodeState; import org.bonitasoft.engine.execution.state.CancelledFlowNodeState; import org.bonitasoft.engine.execution.state.CancellingFlowNodeState; import org.bonitasoft.engine.execution.state.CompletedActivityState; import org.bonitasoft.engine.execution.state.ExecutingThrowEventState; import org.springframework.stereotype.Component; @Component public class IntermediateThrowEventStates extends FlowNodeStateSequences { public SFlowNodeType getFlowNodeType() { return SFlowNodeType.INTERMEDIATE_THROW_EVENT; } public IntermediateThrowEventStates( CompletedActivityState completed, AbortedFlowNodeState aborted, CancelledFlowNodeState cancelled, AbortingFlowNodeState abortingFlowNode, CancellingFlowNodeState cancellingFlowNode, ExecutingThrowEventState executingThrowEvent) { defineNormalSequence(executingThrowEvent, completed); defineAbortSequence(abortingFlowNode, aborted); defineCancelSequence(cancellingFlowNode, cancelled); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/LoopActivityStates.java ================================================ /** * Copyright (C) 2020 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.transition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.execution.state.AbortedFlowNodeState; import org.bonitasoft.engine.execution.state.AbortingActivityWithBoundaryState; import org.bonitasoft.engine.execution.state.AbortingBoundaryEventsOnCompletingActivityState; import org.bonitasoft.engine.execution.state.AbortingFlowNodeContainerState; import org.bonitasoft.engine.execution.state.CancelledFlowNodeState; import org.bonitasoft.engine.execution.state.CancellingActivityWithBoundaryState; import org.bonitasoft.engine.execution.state.CancellingFlowNodeContainerChildrenState; import org.bonitasoft.engine.execution.state.CompletedActivityState; import org.bonitasoft.engine.execution.state.ExecutingLoopActivityState; import org.bonitasoft.engine.execution.state.InitializingLoopActivityState; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class LoopActivityStates extends FlowNodeStateSequences { public SFlowNodeType getFlowNodeType() { return SFlowNodeType.LOOP_ACTIVITY; } public LoopActivityStates( AbortingBoundaryEventsOnCompletingActivityState abortingBoundaryEventsOnCompletingActivityState, CompletedActivityState completed, AbortingActivityWithBoundaryState abortingActivityWithBoundary, AbortedFlowNodeState aborted, @Qualifier("cancellingActivityWithBoundaryState") CancellingActivityWithBoundaryState cancellingActivityWithBoundary, CancelledFlowNodeState cancelled, CancellingFlowNodeContainerChildrenState cancellingContainer, @Qualifier("abortingFlowNodeContainerState") AbortingFlowNodeContainerState abortingContainer, InitializingLoopActivityState initializingLoop, ExecutingLoopActivityState executingLoop) { defineNormalSequence(initializingLoop, executingLoop, abortingBoundaryEventsOnCompletingActivityState, completed); defineAbortSequence(abortingActivityWithBoundary, abortingContainer, aborted); defineCancelSequence(cancellingActivityWithBoundary, cancellingContainer, cancelled); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/ManualTaskStates.java ================================================ /** * Copyright (C) 2020 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.transition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.execution.state.AbortedFlowNodeState; import org.bonitasoft.engine.execution.state.AbortingFlowNodeContainerState; import org.bonitasoft.engine.execution.state.AbortingSubTaskState; import org.bonitasoft.engine.execution.state.CancelledFlowNodeState; import org.bonitasoft.engine.execution.state.CancellingFlowNodeContainerChildrenState; import org.bonitasoft.engine.execution.state.CompletedActivityState; import org.bonitasoft.engine.execution.state.InitializingActivityState; import org.bonitasoft.engine.execution.state.ReadyActivityState; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class ManualTaskStates extends FlowNodeStateSequences { public SFlowNodeType getFlowNodeType() { return SFlowNodeType.MANUAL_TASK; } public ManualTaskStates(InitializingActivityState initializing, ReadyActivityState ready, AbortingSubTaskState abortingSubTaskState, CompletedActivityState completed, @Qualifier("abortingFlowNodeContainerState") AbortingFlowNodeContainerState abortingContainer, AbortedFlowNodeState aborted, CancellingFlowNodeContainerChildrenState cancellingContainer, CancelledFlowNodeState cancelled) { defineNormalSequence(initializing, ready, abortingSubTaskState, completed); defineAbortSequence(abortingContainer, aborted); defineCancelSequence(cancellingContainer, cancelled); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/MultiInstanceActivityStates.java ================================================ /** * Copyright (C) 2020 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.transition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.execution.state.AbortedFlowNodeState; import org.bonitasoft.engine.execution.state.AbortingActivityWithBoundaryState; import org.bonitasoft.engine.execution.state.AbortingBoundaryEventsOnCompletingActivityState; import org.bonitasoft.engine.execution.state.AbortingFlowNodeContainerState; import org.bonitasoft.engine.execution.state.CancelledFlowNodeState; import org.bonitasoft.engine.execution.state.CancellingActivityWithBoundaryState; import org.bonitasoft.engine.execution.state.CancellingFlowNodeContainerChildrenState; import org.bonitasoft.engine.execution.state.CompletedActivityState; import org.bonitasoft.engine.execution.state.ExecutingMultiInstanceActivityState; import org.bonitasoft.engine.execution.state.InitializingMultiInstanceActivityState; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class MultiInstanceActivityStates extends FlowNodeStateSequences { public SFlowNodeType getFlowNodeType() { return SFlowNodeType.MULTI_INSTANCE_ACTIVITY; } public MultiInstanceActivityStates( AbortingBoundaryEventsOnCompletingActivityState abortingBoundaryEventsOnCompletingActivityState, CompletedActivityState completed, AbortingActivityWithBoundaryState abortingActivityWithBoundary, AbortedFlowNodeState aborted, @Qualifier("cancellingActivityWithBoundaryState") CancellingActivityWithBoundaryState cancellingActivityWithBoundary, CancelledFlowNodeState cancelled, CancellingFlowNodeContainerChildrenState cancellingContainer, @Qualifier("abortingFlowNodeContainerState") AbortingFlowNodeContainerState abortingContainer, InitializingMultiInstanceActivityState initializingMultiInstance, ExecutingMultiInstanceActivityState executingMultiInstance) { defineNormalSequence(initializingMultiInstance, executingMultiInstance, abortingBoundaryEventsOnCompletingActivityState, completed); defineAbortSequence(abortingActivityWithBoundary, abortingContainer, aborted); defineCancelSequence(cancellingActivityWithBoundary, cancellingContainer, cancelled); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/ParallelGatewayTransitionEvaluator.java ================================================ /** * 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.execution.transition; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.execution.flowmerger.FlowNodeTransitionsWrapper; /** * @author Elias Ricken de Medeiros */ public class ParallelGatewayTransitionEvaluator { public List evaluateTransitions(FlowNodeTransitionsWrapper transitions) { return new ArrayList<>(transitions.getNonDefaultOutgoingTransitionDefinitions()); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/ReceiveTaskStates.java ================================================ /** * Copyright (C) 2020 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.transition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.execution.state.AbortedFlowNodeState; import org.bonitasoft.engine.execution.state.AbortingActivityWithBoundaryState; import org.bonitasoft.engine.execution.state.AbortingBoundaryEventsOnCompletingActivityState; import org.bonitasoft.engine.execution.state.AbortingReceiveTaskState; import org.bonitasoft.engine.execution.state.CancelledFlowNodeState; import org.bonitasoft.engine.execution.state.CancellingActivityWithBoundaryState; import org.bonitasoft.engine.execution.state.CancellingReceiveTaskState; import org.bonitasoft.engine.execution.state.CompletedActivityState; import org.bonitasoft.engine.execution.state.ExecutingFlowNodeState; import org.bonitasoft.engine.execution.state.InitializingActivityWithBoundaryEventsState; import org.bonitasoft.engine.execution.state.WaitingFlowNodeState; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class ReceiveTaskStates extends FlowNodeStateSequences { public SFlowNodeType getFlowNodeType() { return SFlowNodeType.RECEIVE_TASK; } public ReceiveTaskStates(InitializingActivityWithBoundaryEventsState initializingActivityWithBoundary, AbortingBoundaryEventsOnCompletingActivityState abortingBoundaryEventsOnCompletingActivityState, CompletedActivityState completed, AbortingActivityWithBoundaryState abortingActivityWithBoundary, AbortedFlowNodeState aborted, @Qualifier("cancellingActivityWithBoundaryState") CancellingActivityWithBoundaryState cancellingActivityWithBoundary, ExecutingFlowNodeState executing, AbortingReceiveTaskState abortingReceiveTask, @Qualifier("cancellingReceiveTaskState") CancellingReceiveTaskState cancellingReceiveTask, CancelledFlowNodeState cancelled, WaitingFlowNodeState waiting) { defineNormalSequence(initializingActivityWithBoundary, waiting, executing, abortingBoundaryEventsOnCompletingActivityState, completed); defineAbortSequence(abortingActivityWithBoundary, abortingReceiveTask, aborted); defineCancelSequence(cancellingActivityWithBoundary, cancellingReceiveTask, cancelled); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/STransitionConditionEvaluationException.java ================================================ /** * 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.transition; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SExceptionContext; import org.bonitasoft.engine.commons.exceptions.ScopedException; public class STransitionConditionEvaluationException extends SBonitaException { public STransitionConditionEvaluationException(String message, String transitionName, String targetFlowNode, Throwable cause) { super(message, ScopedException.OUTGOING_TRANSITION, cause); if (transitionName != null) { getContext().put(SExceptionContext.TRANSITION_NAME, transitionName); } if (targetFlowNode != null) { getContext().put(SExceptionContext.TRANSITION_TARGET_FLOWNODE_NAME, targetFlowNode); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/SendTaskStates.java ================================================ /** * Copyright (C) 2020 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.transition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.execution.state.AbortedFlowNodeState; import org.bonitasoft.engine.execution.state.AbortingActivityWithBoundaryState; import org.bonitasoft.engine.execution.state.AbortingBoundaryEventsOnCompletingActivityState; import org.bonitasoft.engine.execution.state.AbortingReceiveTaskState; import org.bonitasoft.engine.execution.state.CancelledFlowNodeState; import org.bonitasoft.engine.execution.state.CancellingActivityWithBoundaryState; import org.bonitasoft.engine.execution.state.CancellingReceiveTaskState; import org.bonitasoft.engine.execution.state.CompletedActivityState; import org.bonitasoft.engine.execution.state.ExecutingAutomaticActivityState; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class SendTaskStates extends FlowNodeStateSequences { public SFlowNodeType getFlowNodeType() { return SFlowNodeType.SEND_TASK; } public SendTaskStates( AbortingBoundaryEventsOnCompletingActivityState abortingBoundaryEventsOnCompletingActivityState, CompletedActivityState completed, AbortingActivityWithBoundaryState abortingActivityWithBoundary, AbortedFlowNodeState aborted, @Qualifier("cancellingActivityWithBoundaryState") CancellingActivityWithBoundaryState cancellingActivityWithBoundary, CancelledFlowNodeState cancelled, ExecutingAutomaticActivityState executingAutomaticActivity, @Qualifier("cancellingReceiveTaskState") CancellingReceiveTaskState cancellingReceiveTask, AbortingReceiveTaskState abortingReceiveTask) { defineNormalSequence(executingAutomaticActivity, abortingBoundaryEventsOnCompletingActivityState, completed); defineAbortSequence(abortingActivityWithBoundary, abortingReceiveTask, aborted); defineCancelSequence(cancellingActivityWithBoundary, cancellingReceiveTask, cancelled); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/StartEventStates.java ================================================ /** * Copyright (C) 2020 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.transition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.execution.state.AbortedFlowNodeState; import org.bonitasoft.engine.execution.state.AbortingFlowNodeState; import org.bonitasoft.engine.execution.state.CancelledFlowNodeState; import org.bonitasoft.engine.execution.state.CancellingFlowNodeState; import org.bonitasoft.engine.execution.state.CompletedActivityState; import org.bonitasoft.engine.execution.state.InitializingAndExecutingFlowNodeState; import org.springframework.stereotype.Component; @Component public class StartEventStates extends FlowNodeStateSequences { public SFlowNodeType getFlowNodeType() { return SFlowNodeType.START_EVENT; } public StartEventStates(CompletedActivityState completed, AbortedFlowNodeState aborted, CancelledFlowNodeState cancelled, AbortingFlowNodeState abortingFlowNode, CancellingFlowNodeState cancellingFlowNode, InitializingAndExecutingFlowNodeState initializingAndExecuting) { defineNormalSequence(initializingAndExecuting, completed); defineAbortSequence(abortingFlowNode, aborted); defineCancelSequence(cancellingFlowNode, cancelled); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/SubProcessActivityTaskStates.java ================================================ /** * Copyright (C) 2020 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.transition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.execution.state.AbortedFlowNodeState; import org.bonitasoft.engine.execution.state.AbortingCallActivityState; import org.bonitasoft.engine.execution.state.CancelledFlowNodeState; import org.bonitasoft.engine.execution.state.CancellingCallActivityState; import org.bonitasoft.engine.execution.state.CompletedActivityState; import org.bonitasoft.engine.execution.state.ExecutingCallActivityState; import org.springframework.stereotype.Component; @Component public class SubProcessActivityTaskStates extends FlowNodeStateSequences { public SFlowNodeType getFlowNodeType() { return SFlowNodeType.SUB_PROCESS; } public SubProcessActivityTaskStates(CompletedActivityState completed, AbortedFlowNodeState aborted, CancelledFlowNodeState cancelled, ExecutingCallActivityState executingCallActivity, AbortingCallActivityState abortingCallActivity, CancellingCallActivityState cancellingCallActivity) { defineNormalSequence(executingCallActivity, completed); defineAbortSequence(abortingCallActivity, aborted); defineCancelSequence(cancellingCallActivity, cancelled); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/TransitionConditionEvaluator.java ================================================ /** * 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.execution.transition; import java.util.Objects; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros */ public class TransitionConditionEvaluator { private final ExpressionResolverService resolverService; public TransitionConditionEvaluator(ExpressionResolverService resolverService) { this.resolverService = resolverService; } public Boolean evaluateCondition(final STransitionDefinition sTransitionDefinition, final SExpressionContext contextDependency) throws SBonitaException { SExpression condition = sTransitionDefinition.getCondition(); if (condition == null) {// no condition == true but return null to say it was a transition without condition -- return null; } if (!Boolean.class.getName().equals(condition.getReturnType())) { throw new STransitionConditionEvaluationException( "Condition expression must return a boolean", getTransitionName(sTransitionDefinition), getTargetFlowNode(sTransitionDefinition, contextDependency), new SExpressionEvaluationException("Invalid expression return type", condition.getName())); } try { return (Boolean) resolverService.evaluate(condition, contextDependency); } catch (SBonitaException e) { throw new STransitionConditionEvaluationException( "Unable to evaluate transition condition", getTransitionName(sTransitionDefinition), getTargetFlowNode(sTransitionDefinition, contextDependency), e); } } private static String getTransitionName(STransitionDefinition transition) { if (Objects.equals(transition.getName(), transition.getSource() + "_->_" + transition.getTarget())) { return null; } return transition.getName(); } private static String getTargetFlowNode(STransitionDefinition sTransitionDefinition, SExpressionContext contextDependency) { if (contextDependency.getProcessDefinition() != null && contextDependency.getProcessDefinition().getProcessContainer() != null) { var targetFlowNode = contextDependency.getProcessDefinition().getProcessContainer() .getFlowNode(sTransitionDefinition.getTarget()); if (targetFlowNode == null) { return null; } return targetFlowNode.getType().name().toLowerCase() + "::" + targetFlowNode.getName(); } return null; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/TransitionEvaluationStrategy.java ================================================ /** * 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.execution.transition; /** * @author Elias Ricken de Medeiros */ public interface TransitionEvaluationStrategy { boolean shouldContinue(boolean alreadyFound); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/transition/UserTaskStates.java ================================================ /** * Copyright (C) 2020 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.transition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.execution.state.AbortedFlowNodeState; import org.bonitasoft.engine.execution.state.AbortingActivityWithBoundaryState; import org.bonitasoft.engine.execution.state.AbortingBoundaryEventsOnCompletingActivityState; import org.bonitasoft.engine.execution.state.AbortingFlowNodeContainerState; import org.bonitasoft.engine.execution.state.AbortingSubTaskState; import org.bonitasoft.engine.execution.state.CancelledFlowNodeState; import org.bonitasoft.engine.execution.state.CancellingActivityWithBoundaryState; import org.bonitasoft.engine.execution.state.CancellingFlowNodeContainerChildrenState; import org.bonitasoft.engine.execution.state.CompletedActivityState; import org.bonitasoft.engine.execution.state.InitializingActivityWithBoundaryEventsState; import org.bonitasoft.engine.execution.state.ReadyActivityState; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component public class UserTaskStates extends FlowNodeStateSequences { public SFlowNodeType getFlowNodeType() { return SFlowNodeType.USER_TASK; } public UserTaskStates(InitializingActivityWithBoundaryEventsState initializingActivityWithBoundary, ReadyActivityState ready, AbortingBoundaryEventsOnCompletingActivityState abortingBoundaryEventsOnCompletingActivityState, AbortingSubTaskState abortingSubTaskState, CompletedActivityState completed, AbortingActivityWithBoundaryState abortingActivityWithBoundary, @Qualifier("abortingFlowNodeContainerState") AbortingFlowNodeContainerState abortingContainer, AbortedFlowNodeState aborted, @Qualifier("cancellingActivityWithBoundaryState") CancellingActivityWithBoundaryState cancellingActivityWithBoundary, CancellingFlowNodeContainerChildrenState cancellingContainer, CancelledFlowNodeState cancelled) { defineNormalSequence(initializingActivityWithBoundary, ready, abortingBoundaryEventsOnCompletingActivityState, abortingSubTaskState, completed); defineAbortSequence(abortingActivityWithBoundary, abortingContainer, aborted); defineCancelSequence(cancellingActivityWithBoundary, cancellingContainer, cancelled); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/BPMWorkFactory.java ================================================ /** * 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.execution.work; import static java.util.Arrays.stream; import static java.util.Collections.emptyList; import static java.util.stream.Collectors.toList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent; import org.bonitasoft.engine.execution.FlowNodeSelector; import org.bonitasoft.engine.execution.work.failurewrapping.ConnectorDefinitionAndInstanceContextWork; import org.bonitasoft.engine.execution.work.failurewrapping.FlowNodeDefinitionAndInstanceContextWork; import org.bonitasoft.engine.execution.work.failurewrapping.MessageInstanceContextWork; import org.bonitasoft.engine.execution.work.failurewrapping.ProcessDefinitionContextWork; import org.bonitasoft.engine.execution.work.failurewrapping.ProcessInstanceContextWork; import org.bonitasoft.engine.execution.work.failurewrapping.TriggerSignalWork; import org.bonitasoft.engine.work.BonitaWork; import org.bonitasoft.engine.work.WorkDescriptor; import org.bonitasoft.engine.work.WorkFactory; /** * Factory to construct works * * @author Baptiste Mesta * @author Celine Souchet * @author Matthieu Chaffotte */ public class BPMWorkFactory implements WorkFactory { private static final String EXECUTE_ACTIVITY_CONNECTOR = "EXECUTE_ACTIVITY_CONNECTOR"; private static final String EXECUTE_PROCESS_CONNECTOR = "EXECUTE_PROCESS_CONNECTOR"; private static final String EXECUTE_FLOWNODE = "EXECUTE_FLOWNODE"; private static final String FINISH_FLOWNODE = "FINISH_FLOWNODE"; private static final String EXECUTE_MESSAGE = "EXECUTE_MESSAGE"; private static final String TRIGGER_SIGNAL = "TRIGGER_SIGNAL"; private static final String PROCESS_DEFINITION_ID = "processDefinitionId"; private static final String PROCESS_INSTANCE_ID = "processInstanceId"; private static final String FLOW_NODE_DEFINITION_ID = "flowNodeDefinitionId"; private static final String FLOW_NODE_INSTANCE_ID = "flowNodeInstanceId"; private static final String CONNECTOR_INSTANCE_ID = "connectorInstanceId"; private static final String CONNECTOR_DEFINITION_NAME = "connectorDefinitionName"; private static final String CONNECTOR_DEFINITION_ID = "connectorDefinitionId"; private static final String ROOT_PROCESS_INSTANCE_ID = "rootProcessInstanceId"; public static final String STATE_ID = "stateId"; private static final String STATE_EXECUTING = "stateExecuting"; private static final String STATE_ABORTING = "stateAborting"; private static final String STATE_CANCELING = "stateCanceling"; private static final String ACTIVATION_EVENT = "activationEvent"; private static final String SUB_PROCESS_DEFINITION_ID = "subProcessDefinitionId"; private static final String FLOW_NODE_DEFINITIONS_FILTER = "flowNodeDefinitionsFilter"; private static final String MESSAGE_INSTANCE_ID = "messageInstanceId"; private static final String MESSAGE_INSTANCE_NAME = "messageInstanceName"; private static final String MESSAGE_INSTANCE_TARGET_PROCESS = "messageInstanceTargetProcess"; private static final String MESSAGE_INSTANCE_TARGET_FLOW_NODE = "messageInstanceTargetFlowNode"; private static final String WAITING_MESSAGE_ID = "waitingMessageId"; private static final String WAITING_MESSAGE_EVENT_TYPE = "waitingMessageEventType"; private static final String PARENT_TYPE = "parentType"; private static final String PARENT_ID = "parentId"; private static final String LISTENING_SIGNAL_ID = "listeningSignalId"; private static final String LISTENING_SIGNAL_NAME = "listeningSignalName"; private Map> extensions = new HashMap<>(); private BonitaWork createExecuteConnectorOfActivity(WorkDescriptor workDescriptor) { final long processDefinitionId = workDescriptor.getLong(PROCESS_DEFINITION_ID); final long processInstanceId = workDescriptor.getLong(PROCESS_INSTANCE_ID); final long rootProcessInstanceId = workDescriptor.getLong(ROOT_PROCESS_INSTANCE_ID); final long flowNodeInstanceId = workDescriptor.getLong(FLOW_NODE_INSTANCE_ID); final long connectorInstanceId = workDescriptor.getLong(CONNECTOR_INSTANCE_ID); final String connectorDefinitionId = workDescriptor.getString(CONNECTOR_DEFINITION_ID); final String connectorDefinitionName = workDescriptor.getString(CONNECTOR_DEFINITION_NAME); final ConnectorEvent activationEvent = ConnectorEvent.valueOf(workDescriptor.getString(ACTIVATION_EVENT)); BonitaWork wrappedWork = new ExecuteConnectorOfActivity(processDefinitionId, processInstanceId, workDescriptor.getLong(FLOW_NODE_DEFINITION_ID), flowNodeInstanceId, connectorInstanceId, connectorDefinitionName); wrappedWork = new ConnectorDefinitionAndInstanceContextWork(wrappedWork, connectorDefinitionId, connectorDefinitionName, connectorInstanceId, activationEvent); wrappedWork = withFlowNodeContext(processDefinitionId, processInstanceId, rootProcessInstanceId, flowNodeInstanceId, wrappedWork); return withSession(wrappedWork); } public WorkDescriptor createExecuteConnectorOfActivityDescriptor(final long processDefinitionId, final long processInstanceId, final long rootProcessInstanceId, final long flowNodeDefinitionId, final long flowNodeInstanceId, final long connectorInstanceId, final String connectorDefinitionId, final String connectorDefinitionName, final String activationEvent) { return WorkDescriptor.create(EXECUTE_ACTIVITY_CONNECTOR) .withParameter(PROCESS_DEFINITION_ID, processDefinitionId) .withParameter(PROCESS_INSTANCE_ID, processInstanceId) .withParameter(ROOT_PROCESS_INSTANCE_ID, rootProcessInstanceId) .withParameter(FLOW_NODE_DEFINITION_ID, flowNodeDefinitionId) .withParameter(FLOW_NODE_INSTANCE_ID, flowNodeInstanceId) .withParameter(CONNECTOR_INSTANCE_ID, connectorInstanceId) .withParameter(CONNECTOR_DEFINITION_ID, connectorDefinitionId) .withParameter(CONNECTOR_DEFINITION_NAME, connectorDefinitionName) .withParameter(ACTIVATION_EVENT, activationEvent); } private BonitaWork createExecuteConnectorOfProcess(WorkDescriptor workDescriptor) { long processDefinitionId = workDescriptor.getLong(PROCESS_DEFINITION_ID); long processInstanceId = workDescriptor.getLong(PROCESS_INSTANCE_ID); long rootProcessInstanceId = workDescriptor.getLong(ROOT_PROCESS_INSTANCE_ID); long connectorInstanceId = workDescriptor.getLong(CONNECTOR_INSTANCE_ID); String connectorDefinitionId = workDescriptor.getString(CONNECTOR_DEFINITION_ID); String connectorDefinitionName = workDescriptor.getString(CONNECTOR_DEFINITION_NAME); ConnectorEvent activationEvent = (ConnectorEvent.valueOf(workDescriptor.getString(ACTIVATION_EVENT))); String flowNodeIds = workDescriptor.getString(FLOW_NODE_DEFINITIONS_FILTER); List flowNodeDefinitionsFilter = getListOfFlowNodeDefinitionsToStart(flowNodeIds); Long subProcessDefinitionId = workDescriptor.getLong(SUB_PROCESS_DEFINITION_ID); BonitaWork wrappedWork = withConnectorContext(connectorInstanceId, connectorDefinitionId, connectorDefinitionName, activationEvent, withProcessContext(processDefinitionId, processInstanceId, rootProcessInstanceId, new ExecuteConnectorOfProcess(processDefinitionId, connectorInstanceId, connectorDefinitionName, processInstanceId, rootProcessInstanceId, activationEvent, flowNodeDefinitionsFilter, subProcessDefinitionId))); return withSession(wrappedWork); } private List getListOfFlowNodeDefinitionsToStart(String flowNodeIds) { if (flowNodeIds == null) { //no flow node selector return null; } if (flowNodeIds.isEmpty()) { //a flow node selector that selects no elements return emptyList(); } return stream(flowNodeIds.split(",")).map(Long::valueOf).collect(toList()); } public WorkDescriptor createExecuteConnectorOfProcessDescriptor(final long processDefinitionId, final long processInstanceId, final long rootProcessInstanceId, final long connectorInstanceId, final String connectorDefinitionId, final String connectorDefinitionName, final ConnectorEvent activationEvent, final FlowNodeSelector flowNodeSelector) { return WorkDescriptor.create(EXECUTE_PROCESS_CONNECTOR) .withParameter(PROCESS_DEFINITION_ID, processDefinitionId) .withParameter(PROCESS_INSTANCE_ID, processInstanceId) .withParameter(ROOT_PROCESS_INSTANCE_ID, rootProcessInstanceId) .withParameter(CONNECTOR_INSTANCE_ID, connectorInstanceId) .withParameter(CONNECTOR_DEFINITION_ID, connectorDefinitionId) .withParameter(CONNECTOR_DEFINITION_NAME, connectorDefinitionName) .withParameter(ACTIVATION_EVENT, activationEvent.name()) .withParameter(FLOW_NODE_DEFINITIONS_FILTER, flowNodeSelector != null ? flowNodeSelector.getFilteredElements().stream() .map(f -> f.getId().toString()).collect(Collectors.joining(",")) : null) .withParameter(SUB_PROCESS_DEFINITION_ID, flowNodeSelector != null ? flowNodeSelector.getSubProcessDefinitionId() : null); } private InSessionBonitaWork withSession(BonitaWork wrappedWork) { return new InSessionBonitaWork(wrappedWork); } private BonitaWork withConnectorContext(long connectorInstanceId, String connectorDefinitionId, String connectorDefinitionName, ConnectorEvent activationEvent, ProcessInstanceContextWork processInstanceContextWork) { return new ConnectorDefinitionAndInstanceContextWork(processInstanceContextWork, connectorDefinitionId, connectorDefinitionName, connectorInstanceId, activationEvent); } public WorkDescriptor createExecuteFlowNodeWorkDescriptor(SFlowNodeInstance flowNodeInstance) { return WorkDescriptor.create(EXECUTE_FLOWNODE) .withParameter(PROCESS_DEFINITION_ID, flowNodeInstance.getProcessDefinitionId()) .withParameter(PROCESS_INSTANCE_ID, flowNodeInstance.getParentProcessInstanceId()) .withParameter(ROOT_PROCESS_INSTANCE_ID, flowNodeInstance.getRootProcessInstanceId()) .withParameter(FLOW_NODE_INSTANCE_ID, flowNodeInstance.getId()) .withParameter(STATE_ID, flowNodeInstance.getStateId()) .withParameter(STATE_EXECUTING, flowNodeInstance.isStateExecuting()) .withParameter(STATE_ABORTING, flowNodeInstance.isAborting()) .withParameter(STATE_CANCELING, flowNodeInstance.isCanceling()); } private BonitaWork createExecuteFlowNodeWork(WorkDescriptor workDescriptor) { final long processInstanceId = workDescriptor.getLong(PROCESS_INSTANCE_ID); final long rootProcessInstanceId = workDescriptor.getLong(ROOT_PROCESS_INSTANCE_ID); final long flowNodeInstanceId = workDescriptor.getLong(FLOW_NODE_INSTANCE_ID); if (processInstanceId <= 0) { throw new RuntimeException( "It is forbidden to create a ExecuteFlowNodeWork with a processInstanceId equals to " + processInstanceId); } BonitaWork wrappedWork = new ExecuteFlowNodeWork(flowNodeInstanceId, workDescriptor.getInteger(STATE_ID), workDescriptor.getBoolean(STATE_EXECUTING), workDescriptor.getBoolean(STATE_ABORTING), workDescriptor.getBoolean(STATE_CANCELING)); wrappedWork = withLock(processInstanceId, withTx(wrappedWork)); wrappedWork = withFlowNodeContext(workDescriptor.getLong(PROCESS_DEFINITION_ID), processInstanceId, rootProcessInstanceId, flowNodeInstanceId, wrappedWork); return withSession(wrappedWork); } public WorkDescriptor createExecuteMessageCoupleWorkDescriptor(final SMessageInstance messageInstance, final SWaitingMessageEvent waitingMessage) { return WorkDescriptor.create(EXECUTE_MESSAGE) .withParameter(MESSAGE_INSTANCE_ID, messageInstance.getId()) .withParameter(MESSAGE_INSTANCE_NAME, messageInstance.getMessageName()) .withParameter(MESSAGE_INSTANCE_TARGET_PROCESS, messageInstance.getTargetProcess()) .withParameter(MESSAGE_INSTANCE_TARGET_FLOW_NODE, messageInstance.getTargetFlowNode()) .withParameter(WAITING_MESSAGE_ID, waitingMessage.getId()) .withParameter(WAITING_MESSAGE_EVENT_TYPE, waitingMessage.getEventType().name()) .withParameter(PROCESS_INSTANCE_ID, waitingMessage.getParentProcessInstanceId()) .withParameter(PROCESS_DEFINITION_ID, waitingMessage.getProcessDefinitionId()) .withParameter(FLOW_NODE_INSTANCE_ID, waitingMessage.getFlowNodeInstanceId()) .withParameter(ROOT_PROCESS_INSTANCE_ID, waitingMessage.getRootProcessInstanceId()); } private BonitaWork createExecuteMessageCoupleWork(WorkDescriptor workDescriptor) { // no target process: we do not wrap in a LockProcessInstanceWork long messageId = workDescriptor.getLong(MESSAGE_INSTANCE_ID); String messageName = workDescriptor.getString(MESSAGE_INSTANCE_NAME); String targetProcess = workDescriptor.getString(MESSAGE_INSTANCE_TARGET_PROCESS); String targetFlowNode = workDescriptor.getString(MESSAGE_INSTANCE_TARGET_FLOW_NODE); long waitingMessageId = workDescriptor.getLong(WAITING_MESSAGE_ID); long parentProcessInstanceId = workDescriptor.getLong(PROCESS_INSTANCE_ID); long processDefinitionId = workDescriptor.getLong(PROCESS_DEFINITION_ID); long flowNodeInstanceId = workDescriptor.getLong(FLOW_NODE_INSTANCE_ID); long rootProcessInstanceId = workDescriptor.getLong(ROOT_PROCESS_INSTANCE_ID); String eventType = workDescriptor.getString(WAITING_MESSAGE_EVENT_TYPE); BonitaWork wrappedWork = withTx(new ExecuteMessageCoupleWork(messageId, waitingMessageId)); if (parentProcessInstanceId > 0) { wrappedWork = withLock(parentProcessInstanceId, wrappedWork); } wrappedWork = new MessageInstanceContextWork(wrappedWork, messageName, targetProcess, targetFlowNode, eventType); wrappedWork = withProcessContext(processDefinitionId, parentProcessInstanceId, rootProcessInstanceId, flowNodeInstanceId, wrappedWork); return withSession(wrappedWork); } private BonitaWork createNotifyChildFinishedWork(WorkDescriptor workDescriptor) { final long processDefinitionId = workDescriptor.getLong(PROCESS_DEFINITION_ID); final long processInstanceId = workDescriptor.getLong(PROCESS_INSTANCE_ID); final long rootProcessInstanceId = workDescriptor.getLong(ROOT_PROCESS_INSTANCE_ID); final long flowNodeInstanceId = workDescriptor.getLong(FLOW_NODE_INSTANCE_ID); BonitaWork wrappedWork = new NotifyChildFinishedWork(processDefinitionId, flowNodeInstanceId, workDescriptor.getInteger(STATE_ID), workDescriptor.getBoolean(STATE_EXECUTING), workDescriptor.getBoolean(STATE_ABORTING), workDescriptor.getBoolean(STATE_CANCELING)); wrappedWork = withLock(processInstanceId, withTx(wrappedWork)); wrappedWork = withFlowNodeContext(processDefinitionId, processInstanceId, rootProcessInstanceId, flowNodeInstanceId, wrappedWork); return withSession(wrappedWork); } public WorkDescriptor createNotifyChildFinishedWorkDescriptor(SFlowNodeInstance sFlowNodeInstance) { return WorkDescriptor.create(FINISH_FLOWNODE) .withParameter(PROCESS_DEFINITION_ID, sFlowNodeInstance.getProcessDefinitionId()) .withParameter(PROCESS_INSTANCE_ID, sFlowNodeInstance.getParentProcessInstanceId()) .withParameter(ROOT_PROCESS_INSTANCE_ID, sFlowNodeInstance.getRootProcessInstanceId()) .withParameter(FLOW_NODE_INSTANCE_ID, sFlowNodeInstance.getId()) .withParameter(STATE_ID, sFlowNodeInstance.getStateId()) .withParameter(STATE_EXECUTING, sFlowNodeInstance.isStateExecuting()) .withParameter(STATE_ABORTING, sFlowNodeInstance.isAborting()) .withParameter(STATE_CANCELING, sFlowNodeInstance.isCanceling()); } private BonitaWork withLock(long processInstanceId, BonitaWork work) { return new LockProcessInstanceWork(work, processInstanceId); } private BonitaWork withTx(BonitaWork wrappedWork) { return new TxBonitaWork(wrappedWork); } private BonitaWork withFlowNodeContext(final long processDefinitionId, final long processInstanceId, final long rootProcessInstanceId, final long flowNodeInstanceId, final BonitaWork wrappedWork) { final ProcessDefinitionContextWork processDefinitionContextWork = new ProcessDefinitionContextWork(wrappedWork, processDefinitionId); final ProcessInstanceContextWork processInstanceContextWork = new ProcessInstanceContextWork( processDefinitionContextWork, processInstanceId, rootProcessInstanceId); return new FlowNodeDefinitionAndInstanceContextWork(processInstanceContextWork, flowNodeInstanceId); } private BonitaWork withProcessContext(final long processDefinitionId, final long processInstanceId, final long rootProcessInstanceId, final long flowNodeInstanceId, final BonitaWork wrappedWork) { final ProcessInstanceContextWork processInstanceContextWork = withProcessContext(processDefinitionId, processInstanceId, rootProcessInstanceId, wrappedWork); return new FlowNodeDefinitionAndInstanceContextWork(processInstanceContextWork, flowNodeInstanceId); } private ProcessInstanceContextWork withProcessContext(final long processDefinitionId, final long processInstanceId, final long rootProcessInstanceId, final BonitaWork wrappedWork) { final ProcessDefinitionContextWork processDefinitionContextWork = new ProcessDefinitionContextWork(wrappedWork, processDefinitionId); return new ProcessInstanceContextWork(processDefinitionContextWork, processInstanceId, rootProcessInstanceId); } public WorkDescriptor createTriggerSignalWorkDescriptor(SWaitingSignalEvent listeningSignal) { return WorkDescriptor.create(TRIGGER_SIGNAL) .withParameter(LISTENING_SIGNAL_ID, listeningSignal.getId()) .withParameter(LISTENING_SIGNAL_NAME, listeningSignal.getSignalName()) .withParameter(PROCESS_INSTANCE_ID, listeningSignal.getParentProcessInstanceId()); } private BonitaWork createTriggerSignalWork(WorkDescriptor workDescriptor) { BonitaWork triggerSignalWork = new TriggerSignalWork(workDescriptor.getLong(LISTENING_SIGNAL_ID), workDescriptor.getString(LISTENING_SIGNAL_NAME)); triggerSignalWork = withTx(triggerSignalWork); long parentProcessInstanceId = workDescriptor.getLong(PROCESS_INSTANCE_ID); if (parentProcessInstanceId > 0) { triggerSignalWork = withLock(parentProcessInstanceId, triggerSignalWork); } return withSession(triggerSignalWork); } @Override public BonitaWork create(WorkDescriptor workDescriptor) { return switch (workDescriptor.getType()) { case EXECUTE_ACTIVITY_CONNECTOR -> createExecuteConnectorOfActivity(workDescriptor); case EXECUTE_PROCESS_CONNECTOR -> createExecuteConnectorOfProcess(workDescriptor); case EXECUTE_FLOWNODE -> createExecuteFlowNodeWork(workDescriptor); case FINISH_FLOWNODE -> createNotifyChildFinishedWork(workDescriptor); case TRIGGER_SIGNAL -> createTriggerSignalWork(workDescriptor); case EXECUTE_MESSAGE -> createExecuteMessageCoupleWork(workDescriptor); default -> createFromExtension(workDescriptor); }; } private BonitaWork createFromExtension(WorkDescriptor workDescriptor) { if (!extensions.containsKey(workDescriptor.getType())) { throw new IllegalArgumentException("Unknown type of work:" + workDescriptor.getType()); } return extensions.get(workDescriptor.getType()).apply(workDescriptor); } public void addExtension(String workType, Function workFactoryOfType) { extensions.put(workType, workFactoryOfType); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorOfActivity.java ================================================ /** * 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.execution.work; import java.util.Map; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.connector.ConnectorResult; import org.bonitasoft.engine.core.connector.exception.SConnectorDefinitionNotFoundException; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SEndEventDefinitionBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowErrorEventTriggerDefinitionBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.execution.WaitingEventsInterrupter; import org.bonitasoft.engine.execution.event.EventsHandler; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.work.WorkDescriptor; import org.bonitasoft.engine.work.WorkService; /** * @author Baptiste Mesta * @author Celine Souchet * @author Matthieu Chaffotte */ public class ExecuteConnectorOfActivity extends ExecuteConnectorWork { private final long flowNodeInstanceId; private final long flowNodeDefinitionId; ExecuteConnectorOfActivity(final long processDefinitionId, final long processInstanceId, final long flowNodeDefinitionId, final long flowNodeInstanceId, final long connectorInstanceId, final String connectorDefinitionName) { super(processDefinitionId, connectorInstanceId, connectorDefinitionName, new SExpressionContext(flowNodeInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.name(), processDefinitionId), processInstanceId); this.flowNodeDefinitionId = flowNodeDefinitionId; this.flowNodeInstanceId = flowNodeInstanceId; } @Override protected void evaluateOutput(final Map context, final ConnectorResult result, final SConnectorDefinition sConnectorDefinition) throws SBonitaException { evaluateOutput(context, result, sConnectorDefinition, flowNodeInstanceId, DataInstanceContainer.ACTIVITY_INSTANCE.name()); } @Override protected void continueFlow(final Map context) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(context); final FlowNodeInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final WorkService workService = serviceAccessor.getWorkService(); final SFlowNodeInstance sFlowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId); final WorkDescriptor executeFlowNodeWork = serviceAccessor.getBPMWorkFactory() .createExecuteFlowNodeWorkDescriptor(sFlowNodeInstance); workService.registerWork(executeFlowNodeWork); } @Override protected void setContainerInFail(final Map context, Throwable t) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(context); WaitingEventsInterrupter waitingEventsInterrupter = new WaitingEventsInterrupter( serviceAccessor.getEventInstanceService(), serviceAccessor.getSchedulerService()); var activityInstanceService = serviceAccessor.getActivityInstanceService(); FailedStateSetter failedStateSetter = new FailedStateSetter(waitingEventsInterrupter, activityInstanceService, serviceAccessor.getFlowNodeStateManager()); failedStateSetter.setAsFailed(flowNodeInstanceId); var failureService = serviceAccessor.getBpmFailureService(); failureService.createFlowNodeFailure(activityInstanceService.getFlowNodeInstance(flowNodeInstanceId), new BPMFailureService.Failure(ScopedException.CONNECTOR, t)); } @Override protected SThrowEventInstance createThrowErrorEventInstance(final Map context, final SEndEventDefinition eventDefinition) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(context); final EventInstanceService eventInstanceService = serviceAccessor.getEventInstanceService(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final SFlowNodeInstance sFlowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId); final SEndEventInstanceBuilder builder = BuilderFactory.get(SEndEventInstanceBuilderFactory.class) .createNewEndEventInstance(eventDefinition.getName(), eventDefinition.getId(), sFlowNodeInstance.getRootContainerId(), sFlowNodeInstance.getParentContainerId(), processDefinitionId, sFlowNodeInstance.getRootContainerId(), sFlowNodeInstance.getParentProcessInstanceId()); builder.setParentActivityInstanceId(flowNodeInstanceId); final SThrowEventInstance done = (SThrowEventInstance) builder.done(); eventInstanceService.createEventInstance(done); return done; } @Override protected void errorEventOnFail(final Map context, final SConnectorDefinition sConnectorDefinition, final Throwable exception) throws SBonitaException { setConnectorAndContainerToFailed(context, exception); final ServiceAccessor serviceAccessor = getServiceAccessor(context); final EventsHandler eventsHandler = serviceAccessor.getEventsHandler(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final SFlowNodeInstance sFlowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId); // create a fake definition final String errorCode = sConnectorDefinition.getErrorCode(); final SThrowErrorEventTriggerDefinition errorEventTriggerDefinition = BuilderFactory .get(SThrowErrorEventTriggerDefinitionBuilderFactory.class) .createNewInstance(errorCode).done(); // event definition as the error code as name, this way we don't need to find the connector that throw this error final SEndEventDefinition eventDefinition = BuilderFactory.get(SEndEventDefinitionBuilderFactory.class) .createNewInstance(errorCode) .addErrorEventTriggerDefinition(errorEventTriggerDefinition).done(); // create an instance using this definition final SThrowEventInstance throwEventInstance = createThrowErrorEventInstance(context, eventDefinition); final SProcessDefinition sProcessDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); eventsHandler.getHandler(SEventTriggerType.ERROR).handlePostThrowEvent(sProcessDefinition, eventDefinition, throwEventInstance, errorEventTriggerDefinition, sFlowNodeInstance); serviceAccessor.getBPMArchiverService().archiveAndDeleteFlowNodeInstance(throwEventInstance, sProcessDefinition.getId()); } @Override public String getDescription() { return getClass().getSimpleName() + ": flowNodeInstanceId = " + flowNodeInstanceId + ", connectorDefinitionName = " + connectorDefinitionName; } @Override protected SConnectorDefinition getSConnectorDefinition(final ProcessDefinitionService processDefinitionService) throws SBonitaException { final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId); final SFlowNodeDefinition flowNodeDefinition = processDefinition.getProcessContainer() .getFlowNode(flowNodeDefinitionId); final SConnectorDefinition sConnectorDefinition = flowNodeDefinition .getConnectorDefinition(connectorDefinitionName); if (sConnectorDefinition == null) { throw new SConnectorDefinitionNotFoundException( "Couldn't find the connector definition [" + connectorDefinitionName + "]"); } return sConnectorDefinition; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorOfProcess.java ================================================ /** * 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.execution.work; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.connector.ConnectorResult; import org.bonitasoft.engine.core.connector.exception.SConnectorDefinitionNotFoundException; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; import org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition; 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.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SEndEventDefinitionBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowErrorEventTriggerDefinitionBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SThrowErrorEventTriggerDefinition; import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; 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.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.execution.Filter; import org.bonitasoft.engine.execution.FlowNodeIdFilter; import org.bonitasoft.engine.execution.FlowNodeSelector; import org.bonitasoft.engine.execution.ProcessExecutor; import org.bonitasoft.engine.execution.StartFlowNodeFilter; import org.bonitasoft.engine.execution.event.EventsHandler; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class ExecuteConnectorOfProcess extends ExecuteConnectorWork { private final long processInstanceId; private final long rootProcessInstanceId; private final ConnectorEvent activationEvent; final Filter filterFlowNodeDefinitions; private final long subProcessDefinitionId; ExecuteConnectorOfProcess(final long processDefinitionId, final long connectorInstanceId, final String connectorDefinitionName, final long processInstanceId, final long rootProcessInstanceId, final ConnectorEvent activationEvent, List flowNodeDefinitionsFilter, Long subProcessDefinitionId) { super(processDefinitionId, connectorInstanceId, connectorDefinitionName, new SExpressionContext(processInstanceId, DataInstanceContainer.PROCESS_INSTANCE.name(), processDefinitionId), processInstanceId); this.processInstanceId = processInstanceId; this.rootProcessInstanceId = rootProcessInstanceId; this.activationEvent = activationEvent; this.filterFlowNodeDefinitions = flowNodeDefinitionsFilter != null ? new FlowNodeIdFilter(flowNodeDefinitionsFilter) : new StartFlowNodeFilter(); this.subProcessDefinitionId = subProcessDefinitionId != null ? subProcessDefinitionId : -1; } @Override protected void evaluateOutput(final Map context, final ConnectorResult result, final SConnectorDefinition sConnectorDefinition) throws SBonitaException { evaluateOutput(context, result, sConnectorDefinition, processInstanceId, DataInstanceContainer.PROCESS_INSTANCE.name()); } @Override protected void continueFlow(final Map context) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(context); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final ProcessExecutor processExecutor = serviceAccessor.getProcessExecutor(); final SProcessDefinition sProcessDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); final SProcessInstance intTxProcessInstance = processInstanceService.getProcessInstance(processInstanceId); FlowNodeSelector flowNodeSelector = new FlowNodeSelector(sProcessDefinition, filterFlowNodeDefinitions, subProcessDefinitionId); final boolean connectorTriggered = processExecutor.registerConnectorsToExecute(sProcessDefinition, intTxProcessInstance, activationEvent, flowNodeSelector); if (!connectorTriggered) { if (activationEvent == ConnectorEvent.ON_ENTER) { processExecutor.startElements(intTxProcessInstance, flowNodeSelector); } else { processExecutor.handleProcessCompletion(sProcessDefinition, intTxProcessInstance, false); } } } @Override protected void setContainerInFail(final Map context, Throwable t) throws SBonitaException { final ProcessInstanceService processInstanceService = getServiceAccessor(context).getProcessInstanceService(); final SProcessInstance intTxProcessInstance = processInstanceService.getProcessInstance(processInstanceId); processInstanceService.setState(intTxProcessInstance, ProcessInstanceState.ERROR); var failureService = getServiceAccessor(context).getBpmFailureService(); failureService.createProcessInstanceFailure(intTxProcessInstance, new BPMFailureService.Failure(ScopedException.CONNECTOR, t)); } @Override protected SThrowEventInstance createThrowErrorEventInstance(final Map context, final SEndEventDefinition eventDefinition) throws SBonitaException { final BPMInstancesCreator bpmInstancesCreator = getServiceAccessor(context).getBPMInstancesCreator(); final SFlowNodeInstance createFlowNodeInstance = bpmInstancesCreator.createFlowNodeInstance(processDefinitionId, rootProcessInstanceId, processInstanceId, SFlowElementsContainerType.PROCESS, eventDefinition, rootProcessInstanceId, processInstanceId, false, -1, SStateCategory.NORMAL, -1); return (SThrowEventInstance) createFlowNodeInstance; } @Override protected void errorEventOnFail(final Map context, final SConnectorDefinition sConnectorDefinition, final Throwable exception) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(context); final EventsHandler eventsHandler = serviceAccessor.getEventsHandler(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); setConnectorOnlyToFailed(context, exception); // create a fake definition final String errorCode = sConnectorDefinition.getErrorCode(); final SThrowErrorEventTriggerDefinition errorEventTriggerDefinition = BuilderFactory .get(SThrowErrorEventTriggerDefinitionBuilderFactory.class) .createNewInstance(errorCode).done(); // event definition as the error code as name, this way we don't need to find the connector that throw this error final SEndEventDefinition eventDefinition = BuilderFactory.get(SEndEventDefinitionBuilderFactory.class) .createNewInstance(errorCode) .addErrorEventTriggerDefinition(errorEventTriggerDefinition).done(); // create an instance using this definition final SThrowEventInstance throwEventInstance = createThrowErrorEventInstance(context, eventDefinition); final SProcessDefinition sProcessDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); final boolean hasActionToExecute = eventsHandler.getHandler(SEventTriggerType.ERROR).handlePostThrowEvent( sProcessDefinition, eventDefinition, throwEventInstance, errorEventTriggerDefinition, throwEventInstance); serviceAccessor.getBPMArchiverService().archiveAndDeleteFlowNodeInstance(throwEventInstance, sProcessDefinition.getId()); if (!hasActionToExecute) { setConnectorAndContainerToFailed(context, exception); } } @Override public String getDescription() { return getClass().getSimpleName() + ": processInstanceId = " + processInstanceId + ", connectorDefinitionName = " + connectorDefinitionName; } @Override protected SConnectorDefinition getSConnectorDefinition(final ProcessDefinitionService processDefinitionService) throws SProcessDefinitionNotFoundException, SBonitaReadException, SConnectorDefinitionNotFoundException { final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId); final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); // final SConnectorDefinition sConnectorDefinition = processContainer.getConnectorDefinition(connectorDefinitionId);// FIXME: Uncomment when generate id final SConnectorDefinition sConnectorDefinition = processContainer .getConnectorDefinition(connectorDefinitionName); if (sConnectorDefinition == null) { throw new SConnectorDefinitionNotFoundException(connectorDefinitionName); } return sConnectorDefinition; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteConnectorWork.java ================================================ /** * 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.execution.work; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import org.bonitasoft.engine.bpm.connector.FailAction; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.connector.exception.SConnectorException; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.connector.ConnectorResult; import org.bonitasoft.engine.core.connector.ConnectorService; import org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SConnectorDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo; import org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType; import org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.lock.BonitaLock; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.tracking.TimeTracker; import org.bonitasoft.engine.tracking.TimeTrackerRecords; import org.bonitasoft.engine.transaction.UserTransactionService; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ @lombok.extern.slf4j.Slf4j public abstract class ExecuteConnectorWork extends TenantAwareBonitaWork { protected final long processDefinitionId; protected final long connectorInstanceId; protected final String connectorDefinitionName; private final SExpressionContext inputParametersContext; private final long processInstanceId; protected ExecuteConnectorWork(final long processDefinitionId, final long connectorInstanceId, final String connectorDefinitionName, final SExpressionContext inputParametersContext, long processInstanceId) { this(processDefinitionId, connectorInstanceId, connectorDefinitionName, inputParametersContext, null, processInstanceId); } protected ExecuteConnectorWork(final long processDefinitionId, final long connectorInstanceId, final String connectorDefinitionName, final SExpressionContext inputParametersContext, final Map inputs, long processInstanceId) { super(); this.processDefinitionId = processDefinitionId; this.connectorInstanceId = connectorInstanceId; this.connectorDefinitionName = connectorDefinitionName; this.inputParametersContext = inputParametersContext; this.processInstanceId = processInstanceId; this.inputParametersContext.setInputValues(inputs); } protected abstract void errorEventOnFail(Map context, SConnectorDefinition sConnectorDefinition, Throwable t) throws SBonitaException; protected abstract SThrowEventInstance createThrowErrorEventInstance(Map context, final SEndEventDefinition eventDefinition) throws SBonitaException; protected abstract SConnectorDefinition getSConnectorDefinition( final ProcessDefinitionService processDefinitionService) throws SBonitaException; protected abstract void setContainerInFail(Map context, Throwable t) throws SBonitaException; protected abstract void continueFlow(Map context) throws SBonitaException; protected abstract void evaluateOutput(Map context, final ConnectorResult result, SConnectorDefinition sConnectorDefinition) throws SBonitaException; protected ClassLoader getClassLoader(final Map context) throws SBonitaException { return getServiceAccessor(context).getClassLoaderService().getClassLoader( identifier(ScopeType.PROCESS, processDefinitionId)); } protected void setConnectorAndContainerToFailed(final Map context, final Throwable t) throws SBonitaException { setConnectorOnlyToFailed(context, t); setContainerInFail(context, t); } protected void setConnectorOnlyToFailed(final Map context, final Throwable t) throws SBonitaException { final ConnectorInstanceService connectorInstanceService = getServiceAccessor(context) .getConnectorInstanceService(); final SConnectorInstanceWithFailureInfo connectorInstanceWithFailure = connectorInstanceService .getConnectorInstanceWithFailureInfo(connectorInstanceId); connectorInstanceService.setState(connectorInstanceWithFailure, ConnectorService.FAILED); connectorInstanceService.setConnectorInstanceFailureException(connectorInstanceWithFailure, t); } protected void evaluateOutput(final Map context, final ConnectorResult result, final SConnectorDefinition sConnectorDefinition, final Long id, final String containerType) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(context); final ConnectorInstanceService connectorInstanceService = serviceAccessor.getConnectorInstanceService(); final ConnectorService connectorService = serviceAccessor.getConnectorService(); final List outputs = sConnectorDefinition.getOutputs(); final SExpressionContext sExpressionContext = new SExpressionContext(id, containerType, processDefinitionId); connectorService.executeOutputOperation(outputs, sExpressionContext, result); connectorInstanceService.setState(connectorInstanceService.getConnectorInstance(connectorInstanceId), ConnectorService.DONE); } @Override public CompletableFuture work(final Map context) throws Exception { final long startTime = System.currentTimeMillis(); final ServiceAccessor serviceAccessor = getServiceAccessor(context); final ConnectorService connectorService = serviceAccessor.getConnectorService(); final ConnectorInstanceService connectorInstanceService = serviceAccessor.getConnectorInstanceService(); final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService(); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final TimeTracker timeTracker = serviceAccessor.getTimeTracker(); final ClassLoader processClassloader = getClassLoader(context); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(processClassloader); final EvaluateParameterAndGetConnectorInstance callable = new EvaluateParameterAndGetConnectorInstance( connectorService, processDefinitionService, connectorInstanceService); userTransactionService.executeInTransaction(callable); final SConnectorDefinition sConnectorDefinition = callable.getsConnectorDefinition(); final SConnectorInstance connectorInstance = callable.getConnectorInstance(); SConnectorImplementationDescriptor connectorImplementationDescriptor = callable .getConnectorImplementationDescriptor(); return connectorService.executeConnector(processDefinitionId, connectorInstance, connectorImplementationDescriptor, processClassloader, callable.getInputParameters()) .thenAccept(r -> { try { executeOutputOperationsAndContinue(context, serviceAccessor, userTransactionService, sConnectorDefinition, r); } catch (Exception e) { log.error("Unable to evaluate output operations and continue flow" + " for connector '{}' (connectorInstanceId={}," + " processDefinitionId={}, processInstanceId={})", connectorDefinitionName, connectorInstanceId, processDefinitionId, processInstanceId, e); throw new CompletionException( new SConnectorException( "Unable to evaluate output operations and continue flow for connector '" + connectorDefinitionName + "'", e)); } }); } finally { if (timeTracker.isTrackable(TimeTrackerRecords.EXECUTE_CONNECTOR_WORK)) { final long endTime = System.currentTimeMillis(); final StringBuilder desc = new StringBuilder(); desc.append("processDefinitionId: "); desc.append(processDefinitionId); desc.append(" - "); desc.append("connectorDefinitionName: "); desc.append(connectorDefinitionName); desc.append(" - "); desc.append("connectorInstanceId: "); desc.append(connectorInstanceId); timeTracker.track(TimeTrackerRecords.EXECUTE_CONNECTOR_WORK, desc.toString(), endTime - startTime); } Thread.currentThread().setContextClassLoader(contextClassLoader); } } private void executeOutputOperationsAndContinue(Map context, ServiceAccessor serviceAccessor, UserTransactionService userTransactionService, SConnectorDefinition sConnectorDefinition, ConnectorResult r) throws Exception { // evaluate output and trigger the execution of the flow node BonitaLock lock = serviceAccessor.getLockService().lock(processInstanceId, SFlowElementsContainerType.PROCESS.name()); try { userTransactionService.executeInTransaction(() -> { evaluateOutput(context, r, sConnectorDefinition); continueFlow(context); return null; }); } finally { serviceAccessor.getLockService().unlock(lock); } } @Override public void handleFailure(final Throwable e, final Map context) throws Exception { final UserTransactionService userTransactionService = getServiceAccessor(context).getUserTransactionService(); final ProcessDefinitionService processDefinitionService = getServiceAccessor(context) .getProcessDefinitionService(); final HandleConnectorOnFailEventTxContent callable = new HandleConnectorOnFailEventTxContent(e, processDefinitionService, context); final SConnectorDefinition sConnectorDefinition = userTransactionService.executeInTransaction(callable); if (shouldContinueFlow(sConnectorDefinition)) { userTransactionService.executeInTransaction(new ContinueFlowTxContent(context)); } } private boolean shouldContinueFlow(SConnectorDefinition sConnectorDefinition) { return sConnectorDefinition.getFailAction() == FailAction.IGNORE; } private final class EvaluateParameterAndGetConnectorInstance implements Callable { private final ConnectorService connectorService; private final ConnectorInstanceService connectorInstanceService; private Map inputParameters; private SConnectorInstance connectorInstance; private final ProcessDefinitionService processDefinitionService; private SConnectorDefinition sConnectorDefinition; private SConnectorImplementationDescriptor connectorImplementationDescriptor; private EvaluateParameterAndGetConnectorInstance(final ConnectorService connectorService, final ProcessDefinitionService processDefinitionService, final ConnectorInstanceService connectorInstanceService) { this.connectorService = connectorService; this.processDefinitionService = processDefinitionService; this.connectorInstanceService = connectorInstanceService; } public Map getInputParameters() { return inputParameters; } public SConnectorInstance getConnectorInstance() { return connectorInstance; } public SConnectorDefinition getsConnectorDefinition() { return sConnectorDefinition; } public SConnectorImplementationDescriptor getConnectorImplementationDescriptor() { return connectorImplementationDescriptor; } @Override public Void call() throws Exception { sConnectorDefinition = getSConnectorDefinition(processDefinitionService); inputParameters = connectorService.evaluateInputParameters(sConnectorDefinition.getConnectorId(), sConnectorDefinition.getInputs(), inputParametersContext, null); connectorInstance = connectorInstanceService.getConnectorInstance(connectorInstanceId); connectorImplementationDescriptor = connectorService.getConnectorImplementationDescriptor( processDefinitionId, sConnectorDefinition.getConnectorId(), sConnectorDefinition.getVersion()); return null; } } /** * Handle the error according to failure policy. * * @author Emmanuel Duchastenier */ private final class HandleConnectorOnFailEventTxContent implements Callable { private final Throwable e; private final ProcessDefinitionService processDefinitionService; private final Map context; private HandleConnectorOnFailEventTxContent(final Throwable e, final ProcessDefinitionService processDefinitionService, final Map context) { this.e = e; this.processDefinitionService = processDefinitionService; this.context = context; } @Override public SConnectorDefinition call() throws Exception { final SConnectorDefinition sConnectorDefinition = getSConnectorDefinition(processDefinitionService); switch (sConnectorDefinition.getFailAction()) { case ERROR_EVENT: errorEventOnFail(context, sConnectorDefinition, e); break; case FAIL: setConnectorAndContainerToFailed(context, e); break; case IGNORE: setConnectorOnlyToFailed(context, e); break; default: throw new Exception("No action defined for " + sConnectorDefinition.getFailAction()); } return sConnectorDefinition; } } /** * @author Emmanuel Duchastenier */ final class ContinueFlowTxContent implements Callable { private final Map context; public ContinueFlowTxContent(final Map context) { this.context = context; } @Override public Void call() throws Exception { continueFlow(context); return null; } } @Override public boolean canBeRecoveredByTheRecoveryMechanism() { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteFlowNodeWork.java ================================================ /** * 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.execution.work; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.execution.WaitingEventsInterrupter; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.transaction.UserTransactionService; import org.bonitasoft.engine.work.SWorkPreconditionException; /** * Work that is responsible of executing a flow node. * If the execution fails it will put the flow node in failed state * * @author Baptiste Mesta * @author Celine Souchet * @author Matthieu Chaffotte */ public class ExecuteFlowNodeWork extends TenantAwareBonitaWork { private final long flowNodeInstanceId; private final Integer stateId; private final Boolean executing; private final Boolean aborting; private final Boolean canceling; ExecuteFlowNodeWork(final long flowNodeInstanceId, Integer stateId, Boolean executing, Boolean aborting, Boolean canceling) { this.flowNodeInstanceId = flowNodeInstanceId; this.stateId = stateId; this.executing = executing; this.aborting = aborting; this.canceling = canceling; } @Override public String getDescription() { return getClass().getSimpleName() + ": flowNodeInstanceId: " + flowNodeInstanceId + " (" + stateId + ", " + executing + ", " + aborting + ", " + canceling + ")"; } @Override public CompletableFuture work(final Map context) throws Exception { final ServiceAccessor serviceAccessor = getServiceAccessor(context); SFlowNodeInstance flowNodeInstance = retrieveAndVerifyFlowNodeInstance(serviceAccessor); context.put("flowNodeInstance", flowNodeInstance); serviceAccessor.getFlowNodeExecutor().executeFlowNode(flowNodeInstance, null, null); return CompletableFuture.completedFuture(null); } private SFlowNodeInstance retrieveAndVerifyFlowNodeInstance(ServiceAccessor serviceAccessor) throws SFlowNodeReadException, SWorkPreconditionException { SFlowNodeInstance flowNodeInstance; try { flowNodeInstance = serviceAccessor.getActivityInstanceService().getFlowNodeInstance(flowNodeInstanceId); } catch (SFlowNodeNotFoundException e) { throw new SWorkPreconditionException(String.format("Flow node %d does not exists", flowNodeInstanceId), e); } if (stateId != flowNodeInstance.getStateId() || executing != flowNodeInstance.isStateExecuting() || aborting != flowNodeInstance.isAborting() || canceling != flowNodeInstance.isCanceling()) { throw new SWorkPreconditionException( String.format("Unable to execute flow node %d because it is not in the expected state " + "( expected state: %d, transitioning: %s, aborting: %s, canceling: %s, but got state: %d, transitioning: %s, aborting: %s, canceling: %s)." + " Someone probably already called execute on it.", flowNodeInstance.getId(), stateId, executing, aborting, canceling, flowNodeInstance.getStateId(), flowNodeInstance.isStateExecuting(), flowNodeInstance.isAborting(), flowNodeInstance.isCanceling())); } return flowNodeInstance; } @Override public void handleFailure(final Throwable e, final Map context) throws Exception { ServiceAccessor serviceAccessor = getServiceAccessor(context); final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService(); WaitingEventsInterrupter waitingEventsInterrupter = new WaitingEventsInterrupter( serviceAccessor.getEventInstanceService(), serviceAccessor.getSchedulerService()); FailedStateSetter failedStateSetter = new FailedStateSetter(waitingEventsInterrupter, serviceAccessor.getActivityInstanceService(), serviceAccessor.getFlowNodeStateManager()); var flowNodeInstance = (SFlowNodeInstance) context.get("flowNodeInstance"); userTransactionService.executeInTransaction(new SetInFailCallable(failedStateSetter, flowNodeInstance, serviceAccessor.getBpmFailureService(), createFailure(e))); } private BPMFailureService.Failure createFailure(Throwable e) { if (e instanceof ScopedException scopedException) { return new BPMFailureService.Failure(scopedException.getScope(), e); } return new BPMFailureService.Failure(ScopedException.UNKNOWN_SCOPE, e); } @Override public String toString() { return "Work[" + getDescription() + "]"; } @Override public boolean canBeRecoveredByTheRecoveryMechanism() { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/ExecuteMessageCoupleWork.java ================================================ /** * 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.execution.work; import java.util.Map; import java.util.concurrent.CompletableFuture; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventReadException; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ @Slf4j public class ExecuteMessageCoupleWork extends TenantAwareBonitaWork { private final long messageInstanceId; private final long waitingMessageId; ExecuteMessageCoupleWork(final long messageInstanceId, final long waitingMessageId) { this.messageInstanceId = messageInstanceId; this.waitingMessageId = waitingMessageId; } @Override public String getDescription() { return getClass().getSimpleName() + ": messageInstanceId: " + messageInstanceId + ", waitingMessageId: " + waitingMessageId; } private void resetWaitingMessage(final long waitingMessageId, final EventInstanceService eventInstanceService) throws SWaitingEventModificationException, SWaitingEventReadException { final SWaitingMessageEvent waitingMsg = eventInstanceService.getWaitingMessage(waitingMessageId); final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor .addField(BuilderFactory.get(SWaitingMessageEventBuilderFactory.class).getProgressKey(), SWaitingMessageEventBuilderFactory.PROGRESS_FREE_KEY); eventInstanceService.updateWaitingMessage(waitingMsg, descriptor); } @Override public String getRecoveryProcedure() { return "call processApi.executeMessageCouple(" + messageInstanceId + ", " + waitingMessageId + "); to re-launch the execution of the message."; } @Override public CompletableFuture work(final Map context) throws Exception { final ServiceAccessor serviceAccessor = getServiceAccessor(context); final EventInstanceService eventInstanceService = serviceAccessor.getEventInstanceService(); final DataInstanceService dataInstanceService = serviceAccessor.getDataInstanceService(); final SWaitingMessageEvent waitingMessage = eventInstanceService.getWaitingMessage(waitingMessageId); final SMessageInstance messageInstance = eventInstanceService.getMessageInstance(messageInstanceId); if (waitingMessage != null) { serviceAccessor.getEventsHandler().triggerCatchEvent(waitingMessage, messageInstanceId); eventInstanceService.deleteMessageInstance(messageInstance); dataInstanceService.deleteLocalDataInstances(messageInstanceId, DataInstanceContainer.MESSAGE_INSTANCE.name(), true); } return CompletableFuture.completedFuture(null); } @Override public void handleFailure(final Throwable e, final Map context) throws Exception { final ServiceAccessor serviceAccessor = getServiceAccessor(context); serviceAccessor.getUserTransactionService().executeInTransaction(() -> { resetWaitingMessage(waitingMessageId, serviceAccessor.getEventInstanceService()); return null; }); log.warn( String.format( "Unable to execute message couple with sent message %s and waiting message %s, the waiting message was reset" + " to allow other message to trigger it. This failure might come from a design issue, cause is: %s", messageInstanceId, waitingMessageId, getRootCause(e))); log.debug("Cause of the issue while executing message couple: sent message {} and waiting message {} error {}", messageInstanceId, waitingMessageId, e); } private String getRootCause(Throwable e) { String message = null; while (e != null) { message = e.getMessage(); e = e.getCause(); } return message; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/FailedStateSetter.java ================================================ /** * 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.execution.work; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.execution.WaitingEventsInterrupter; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; /** * @author Elias Ricken de Medeiros */ @Slf4j public class FailedStateSetter { private final WaitingEventsInterrupter waitingEventsInterrupter; private final ActivityInstanceService activityInstanceService; private final FlowNodeStateManager flowNodeStateManager; public FailedStateSetter(WaitingEventsInterrupter waitingEventsInterrupter, final ActivityInstanceService activityInstanceService, final FlowNodeStateManager flowNodeStateManager) { this.waitingEventsInterrupter = waitingEventsInterrupter; this.activityInstanceService = activityInstanceService; this.flowNodeStateManager = flowNodeStateManager; } public void setAsFailed(long flowNodeInstanceId) throws SBonitaException { final SFlowNodeInstance flowNodeInstance; try { flowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId); //nothing to do if the flownode is already in failed state if (flowNodeInstance.getStateId() != FlowNodeState.ID_ACTIVITY_FAILED) { activityInstanceService.setState(flowNodeInstance, flowNodeStateManager.getState(FlowNodeState.ID_ACTIVITY_FAILED)); waitingEventsInterrupter.interruptWaitingEvents(flowNodeInstance); } } catch (SFlowNodeNotFoundException e) { log.debug( "Impossible to put flow node instance in failed state: flow node instance with id '{}' not found.", flowNodeInstanceId); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/InSessionBonitaWork.java ================================================ /** * 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.execution.work; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.engine.work.BonitaWork; /** * @author Emmanuel Duchastenier * @author Celine Souchet */ public class InSessionBonitaWork extends WrappingBonitaWork { public InSessionBonitaWork(final BonitaWork work) { super(work); } ServiceAccessor getServiceAccessor() { try { return ServiceAccessorSingleton.getInstance(); } catch (final Exception e) { throw new RuntimeException(e); } } @Override public CompletableFuture work(final Map context) throws Exception { final ServiceAccessor serviceAccessor = getServiceAccessor(); context.put(SERVICE_ACCESSOR, serviceAccessor); return getWrappedWork().work(context); } @Override public void handleFailure(final Throwable e, final Map context) throws Exception { getWrappedWork().handleFailure(e, context); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/LockProcessInstanceWork.java ================================================ /** * 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.execution.work; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.core.process.instance.model.SFlowElementsContainerType; import org.bonitasoft.engine.lock.BonitaLock; import org.bonitasoft.engine.lock.LockService; import org.bonitasoft.engine.lock.SLockException; import org.bonitasoft.engine.work.BonitaWork; import org.bonitasoft.engine.work.LockException; import org.bonitasoft.engine.work.LockTimeoutException; /** * Transactional work that lock the process instance * This work try to lock a process instance, if it can't be locked before the end of the TIMEOUT, we reschedule the fill * stack of work on the work service. * * @author Charles Souillard * @author Baptiste Mesta */ @Slf4j public class LockProcessInstanceWork extends WrappingBonitaWork { protected final long processInstanceId; private static final long TIMEOUT = 20; private final TimeUnit timeUnit = TimeUnit.MILLISECONDS; public LockProcessInstanceWork(final BonitaWork wrappedWork, final long processInstanceId) { super(wrappedWork); this.processInstanceId = processInstanceId; } @Override public CompletableFuture work(final Map context) throws Exception { final LockService lockService = getServiceAccessor(context).getLockService(); final String objectType = SFlowElementsContainerType.PROCESS.name(); BonitaLock lock = null; try { log.debug("{} trying to get lock for instance {}: {}", Thread.currentThread().getName(), processInstanceId, getDescription()); lock = lockService.tryLock(processInstanceId, objectType, TIMEOUT, timeUnit); if (lock == null) { throw new LockTimeoutException( "Unable to lock process instance " + processInstanceId + ": " + getDescription()); } log.debug("{} obtained lock for instance {}: {}", Thread.currentThread().getName(), processInstanceId, getDescription()); return getWrappedWork().work(context); } catch (SLockException e) { throw new LockException("Unexpected exception happened while trying to lock process instance " + processInstanceId + ": " + getDescription(), e); } finally { if (lock != null) { lockService.unlock(lock); log.debug("{} has unlocked lock for instance {}: {}", Thread.currentThread().getName(), processInstanceId, getDescription()); } } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/NotifyChildFinishedWork.java ================================================ /** * 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.execution.work; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import java.util.Map; import java.util.concurrent.CompletableFuture; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.ScopedException; import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.execution.ContainerRegistry; import org.bonitasoft.engine.execution.WaitingEventsInterrupter; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.transaction.UserTransactionService; import org.bonitasoft.engine.work.SWorkPreconditionException; /** * Work that notify a container that a flow node is in completed state * e.g. when a flow node of a process finish we evaluate the outgoing transitions of this flow node. * * @author Baptiste Mesta * @author Celine Souchet */ @Slf4j public class NotifyChildFinishedWork extends TenantAwareBonitaWork { private final long processDefinitionId; private final long flowNodeInstanceId; private final Integer stateId; private final Boolean executing; private final Boolean aborting; private final Boolean canceling; NotifyChildFinishedWork(long processDefinitionId, long flowNodeInstanceId, Integer stateId, Boolean executing, Boolean aborting, Boolean canceling) { this.processDefinitionId = processDefinitionId; this.flowNodeInstanceId = flowNodeInstanceId; this.stateId = stateId; this.executing = executing; this.aborting = aborting; this.canceling = canceling; } protected ClassLoader getClassLoader(final Map context) throws SBonitaException { return getServiceAccessor(context).getClassLoaderService().getClassLoader( identifier(ScopeType.PROCESS, processDefinitionId)); } @Override public CompletableFuture work(final Map context) throws Exception { final ClassLoader processClassloader = getClassLoader(context); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(processClassloader); ServiceAccessor serviceAccessor = getServiceAccessor(context); SFlowNodeInstance flowNodeInstance = retrieveAndVerifyFlowNodeInstance(serviceAccessor); context.put("flowNodeInstance", flowNodeInstance); log.debug("Processing completion of flowNode '{}' (id={}, state='{}', " + "processInstance={}, processDefinition={})", flowNodeInstance.getName(), flowNodeInstance.getId(), flowNodeInstance.getStateName(), flowNodeInstance.getRootProcessInstanceId(), processDefinitionId); final ContainerRegistry containerRegistry = serviceAccessor.getContainerRegistry(); containerRegistry.nodeReachedState(flowNodeInstance); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } return CompletableFuture.completedFuture(null); } private SFlowNodeInstance retrieveAndVerifyFlowNodeInstance(ServiceAccessor serviceAccessor) throws SWorkPreconditionException, SFlowNodeReadException { SFlowNodeInstance flowNodeInstance; try { flowNodeInstance = serviceAccessor.getActivityInstanceService().getFlowNodeInstance(flowNodeInstanceId); } catch (SFlowNodeNotFoundException e) { throw new SWorkPreconditionException( "Flow node " + flowNodeInstanceId + " is already completed ( not found )"); } if (stateId != flowNodeInstance.getStateId() || executing != flowNodeInstance.isStateExecuting() || aborting != flowNodeInstance.isAborting() || canceling != flowNodeInstance.isCanceling()) { throw new SWorkPreconditionException( String.format("Unable to execute flow node %d because it is not in the expected state " + "( expected state: %d, transitioning: %s, aborting: %s, canceling: %s, but got state: %d, transitioning: %s, aborting: %s, canceling: %s)." + " Someone probably already called execute on it.", flowNodeInstanceId, stateId, executing, aborting, canceling, flowNodeInstance.getStateId(), flowNodeInstance.isStateExecuting(), flowNodeInstance.isAborting(), flowNodeInstance.isCanceling())); } if (!flowNodeInstance.isTerminal()) { throw new SWorkPreconditionException("Flow node " + flowNodeInstanceId + " is not yet completed"); } return flowNodeInstance; } @Override public String getDescription() { return getClass().getSimpleName() + " flowNodeInstanceId: " + flowNodeInstanceId; } @Override public void handleFailure(final Throwable e, final Map context) throws Exception { ServiceAccessor serviceAccessor = getServiceAccessor(context); final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService(); WaitingEventsInterrupter waitingEventsInterrupter = new WaitingEventsInterrupter( serviceAccessor.getEventInstanceService(), serviceAccessor.getSchedulerService()); FailedStateSetter failedStateSetter = new FailedStateSetter(waitingEventsInterrupter, serviceAccessor.getActivityInstanceService(), serviceAccessor.getFlowNodeStateManager()); SFlowNodeInstance flowNodeInstance = (SFlowNodeInstance) context.get("flowNodeInstance"); userTransactionService.executeInTransaction(new SetInFailCallable(failedStateSetter, flowNodeInstance, serviceAccessor.getBpmFailureService(), createFailure(e))); } private BPMFailureService.Failure createFailure(Throwable e) { if (e instanceof ScopedException scopedException) { return new BPMFailureService.Failure(scopedException.getScope(), e); } return new BPMFailureService.Failure(ScopedException.UNKNOWN_SCOPE, e); } @Override public String toString() { return "Work[" + getDescription() + "]"; } @Override public boolean canBeRecoveredByTheRecoveryMechanism() { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/RestartException.java ================================================ /** * 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.execution.work; import org.bonitasoft.engine.exception.BonitaException; /** * @author Baptiste Mesta */ public class RestartException extends BonitaException { private static final long serialVersionUID = 6724312254693245291L; public RestartException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/SetInFailCallable.java ================================================ /** * 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.execution.work; import java.util.concurrent.Callable; import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; /** * @author Baptiste Mesta */ public class SetInFailCallable implements Callable { private final FailedStateSetter failedStateSetter; private final SFlowNodeInstance flowNodeInstance; private final BPMFailureService bpmFailureService; private final BPMFailureService.Failure failure; SetInFailCallable(FailedStateSetter failedStateSetter, final SFlowNodeInstance flowNodeInstance, BPMFailureService bpmFailureService, BPMFailureService.Failure failure) { this.failedStateSetter = failedStateSetter; this.flowNodeInstance = flowNodeInstance; this.bpmFailureService = bpmFailureService; this.failure = failure; } @Override public Void call() throws Exception { failedStateSetter.setAsFailed(flowNodeInstance.getId()); bpmFailureService.createFlowNodeFailure(flowNodeInstance, failure); return null; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/TenantAwareBonitaWork.java ================================================ /** * 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.execution.work; import java.util.Map; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.work.BonitaWork; /** * @author Baptiste Mesta */ public abstract class TenantAwareBonitaWork extends BonitaWork { public static final String SERVICE_ACCESSOR = "serviceAccessor"; public TenantAwareBonitaWork() { super(); } protected ServiceAccessor getServiceAccessor(final Map context) { return (ServiceAccessor) context.get(TenantAwareBonitaWork.SERVICE_ACCESSOR); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/TxBonitaWork.java ================================================ /** * 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.execution.work; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.transaction.UserTransactionService; import org.bonitasoft.engine.work.BonitaWork; /** * A work that wrap an other work in a transaction * * @author Baptiste Mesta * @author Emmanuel Duchastenier * @author Charles Souillard * @author Celine Souchet */ public class TxBonitaWork extends WrappingBonitaWork { public TxBonitaWork(final BonitaWork wrappedWork) { super(wrappedWork); } @Override public CompletableFuture work(final Map context) throws Exception { final ServiceAccessor serviceAccessor = getServiceAccessor(context); final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService(); return userTransactionService.executeInTransaction(() -> getWrappedWork().work(context)); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/WrappingBonitaWork.java ================================================ /** * 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.execution.work; import java.util.Map; import org.bonitasoft.engine.work.BonitaWork; /** * @author Baptiste Mesta */ public abstract class WrappingBonitaWork extends TenantAwareBonitaWork { private static final long serialVersionUID = 1L; private final BonitaWork wrappedWork; public WrappingBonitaWork(final BonitaWork wrappedWork) { this.wrappedWork = wrappedWork; wrappedWork.setParent(this); } @Override public String getDescription() { return wrappedWork.getDescription(); } @Override public String getRecoveryProcedure() { return wrappedWork.getRecoveryProcedure(); } public BonitaWork getWrappedWork() { return wrappedWork; } @Override public String toString() { return wrappedWork.toString(); } @Override public void handleFailure(final Throwable e, final Map context) throws Exception { wrappedWork.handleFailure(e, context); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ConnectorDefinitionAndInstanceContextWork.java ================================================ /** * 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.execution.work.failurewrapping; import java.util.Map; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.commons.exceptions.ExceptionContext; import org.bonitasoft.engine.work.BonitaWork; /** * Adding context information about Connector definition and instance to exception for better logging * * @author Celine Souchet */ public class ConnectorDefinitionAndInstanceContextWork extends TxInHandleFailureWrappingWork { private static final long serialVersionUID = 6958842321501639910L; private final String connectorDefinitionId; private final String connectorName; private final ConnectorEvent activationEvent; private final long connectorInstanceId; /** * @param wrappedWork * The work to wrap * @param connectorDefinitionId * The name of the connector definition * @param connectorName * The name of the connector * @param connectorInstanceId * The identifier of the connector instance * @param activationEvent * The event to activate the connector */ public ConnectorDefinitionAndInstanceContextWork(final BonitaWork wrappedWork, final String connectorDefinitionId, final String connectorName, long connectorInstanceId, final ConnectorEvent activationEvent) { super(wrappedWork); this.connectorDefinitionId = connectorDefinitionId; this.connectorName = connectorName; this.activationEvent = activationEvent; this.connectorInstanceId = connectorInstanceId; } @Override protected void setExceptionContext(final ExceptionContext e, final Map context) { e.setConnectorImplementationClassNameOnContext(connectorName); e.setConnectorNameOnContext(connectorName); e.setConnectorDefinitionIdOnContext(connectorDefinitionId); e.setConnectorInstanceIdOnContext(connectorInstanceId); if (activationEvent != null) { e.setConnectorActivationEventOnContext(activationEvent.name()); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/FlowNodeDefinitionAndInstanceContextWork.java ================================================ /** * 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.execution.work.failurewrapping; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; import org.bonitasoft.engine.commons.exceptions.ExceptionContext; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.execution.work.WrappingBonitaWork; import org.bonitasoft.engine.mdc.AbstractMDC; import org.bonitasoft.engine.mdc.MDCConstants; import org.bonitasoft.engine.mdc.MDCHelper; import org.bonitasoft.engine.service.ServiceAccessor; /** * Adding context information about Flow Node & Process definition and instance to exception for better logging * * @author Aurelien Pupier * @author Celine Souchet */ public class FlowNodeDefinitionAndInstanceContextWork extends TxInHandleFailureWrappingWork { private static final long serialVersionUID = -8192129441020811731L; private final long flowNodeInstanceId; /** * @param wrappedWork * The work to wrap * @param flowNodeInstanceId * The identifier of the flow node instance */ public FlowNodeDefinitionAndInstanceContextWork(final WrappingBonitaWork wrappedWork, final long flowNodeInstanceId) { super(wrappedWork); this.flowNodeInstanceId = flowNodeInstanceId; } @Override public CompletableFuture work(Map context) throws Exception { // the corresponding wrapping work will take care of adding other information... Supplier mdc = () -> new AbstractMDC( Map.of(MDCConstants.FLOW_NODE_INSTANCE_ID, Long.toString(flowNodeInstanceId))) { }; return MDCHelper.tryWithMDC(mdc, () -> super.work(context)); } @Override protected void setExceptionContext(final ExceptionContext sBonitaException, final Map context) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(context); final ActivityInstanceService activityInstanceService = serviceAccessor.getActivityInstanceService(); final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId); sBonitaException.setFlowNodeDefinitionIdOnContext(flowNodeInstance.getFlowNodeDefinitionId()); sBonitaException.setFlowNodeInstanceIdOnContext(flowNodeInstanceId); sBonitaException.setFlowNodeNameOnContext(flowNodeInstance.getName()); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/MessageInstanceContextWork.java ================================================ /** * 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.execution.work.failurewrapping; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.ExceptionContext; import org.bonitasoft.engine.work.BonitaWork; /** * @author Aurelien Pupier * @author Celine Souchet */ public class MessageInstanceContextWork extends TxInHandleFailureWrappingWork { private String messageName; private String targetProcess; private String targetFlowNode; private String eventType; public MessageInstanceContextWork(BonitaWork work, String messageName, String targetProcess, String targetFlowNode, String eventType) { super(work); this.messageName = messageName; this.targetProcess = targetProcess; this.targetFlowNode = targetFlowNode; this.eventType = eventType; } @Override protected void setExceptionContext(final ExceptionContext sBonitaException, final Map context) { sBonitaException.setMessageInstanceNameOnContext(messageName); sBonitaException.setMessageInstanceTargetProcessOnContext(targetProcess); sBonitaException.setMessageInstanceTargetFlowNodeOnContext(targetFlowNode); sBonitaException.setWaitingMessageEventTypeOnContext(eventType); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ProcessDefinitionContextWork.java ================================================ /** * 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.execution.work.failurewrapping; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; import org.bonitasoft.engine.commons.exceptions.ExceptionContext; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.mdc.AbstractMDC; import org.bonitasoft.engine.mdc.MDCConstants; import org.bonitasoft.engine.mdc.MDCHelper; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.work.BonitaWork; /** * Adding context information about Process definition to exception for better logging * * @author Celine Souchet */ public class ProcessDefinitionContextWork extends TxInHandleFailureWrappingWork { private static final long serialVersionUID = 6958842321501639910L; private long processDefinitionId; /** * @param wrappedWork * The work to wrap * @param processDefinitionId * The identifier of the process definition */ public ProcessDefinitionContextWork(final BonitaWork wrappedWork, final long processDefinitionId) { super(wrappedWork); this.processDefinitionId = processDefinitionId; } @Override public CompletableFuture work(Map context) throws Exception { // the corresponding wrapping work will take care of adding other information... Supplier mdc = () -> new AbstractMDC(Map.of( MDCConstants.PROCESS_DEFINITION_ID, Long.toString(processDefinitionId))) { }; return MDCHelper.tryWithMDC(mdc, () -> super.work(context)); } @Override protected void setExceptionContext(final ExceptionContext sBonitaException, final Map context) throws SBonitaException { final ServiceAccessor serviceAccessor = getServiceAccessor(context); final ProcessDefinitionService processDefinitionService = serviceAccessor.getProcessDefinitionService(); final SProcessDefinitionDeployInfo processDeploymentInfo = processDefinitionService .getProcessDeploymentInfo(processDefinitionId); sBonitaException.setProcessDefinitionIdOnContext(processDefinitionId); sBonitaException.setProcessDefinitionNameOnContext(processDeploymentInfo.getName()); sBonitaException.setProcessDefinitionVersionOnContext(processDeploymentInfo.getVersion()); } /** * @return The identifier of the process definition * @since 6.3 */ public long getProcessDefinitionId() { return processDefinitionId; } /** * @param processDefinitionId * The identifier of the process definition * @since 6.3 */ protected void setProcessDefinitionId(long processDefinitionId) { this.processDefinitionId = processDefinitionId; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/ProcessInstanceContextWork.java ================================================ /** * 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.execution.work.failurewrapping; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; import org.bonitasoft.engine.commons.exceptions.ExceptionContext; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.execution.work.WrappingBonitaWork; import org.bonitasoft.engine.mdc.AbstractMDC; import org.bonitasoft.engine.mdc.MDCConstants; import org.bonitasoft.engine.mdc.MDCHelper; import org.bonitasoft.engine.service.ServiceAccessor; /** * Adding context information about Process definition and instance to exception for better logging * * @author Celine Souchet */ public class ProcessInstanceContextWork extends TxInHandleFailureWrappingWork { private static final long serialVersionUID = 6958842321501639910L; private long processInstanceId; private long rootProcessInstanceId; /** * @param wrappedWork * The work to wrap * @param processInstanceId * The identifier of the process instance */ public ProcessInstanceContextWork(final WrappingBonitaWork wrappedWork, final long processInstanceId) { this(wrappedWork, processInstanceId, -1); } /** * @param wrappedWork * The work to wrap * @param processDefinitionId * The identifier of the process definition * @param processInstanceId * The identifier of the process instance * @param rootProcessInstanceId * The identifier of the root process instance */ public ProcessInstanceContextWork(final WrappingBonitaWork wrappedWork, final long processInstanceId, final long rootProcessInstanceId) { super(wrappedWork); this.processInstanceId = processInstanceId; this.rootProcessInstanceId = rootProcessInstanceId; } @Override public CompletableFuture work(Map context) throws Exception { // the corresponding wrapping work will take care of adding other information... Supplier mdc = () -> new AbstractMDC(Map.of( MDCConstants.PROCESS_INSTANCE_ID, Long.toString(processInstanceId), MDCConstants.ROOT_PROCESS_INSTANCE_ID, Long.toString(rootProcessInstanceId == -1 ? processInstanceId : rootProcessInstanceId))) { }; return MDCHelper.tryWithMDC(mdc, () -> super.work(context)); } protected void setProcessInstanceId(final long id) { processInstanceId = id; } protected void setRootProcessInstanceId(final long id) { rootProcessInstanceId = id; } @Override protected void setExceptionContext(final ExceptionContext sBonitaException, final Map context) throws SBonitaException { if (rootProcessInstanceId < 0) { final ServiceAccessor serviceAccessor = getServiceAccessor(context); final ProcessInstanceService processInstanceService = serviceAccessor.getProcessInstanceService(); final SProcessInstance sProcessInstance = processInstanceService.getProcessInstance(processInstanceId); rootProcessInstanceId = sProcessInstance.getRootProcessInstanceId(); } sBonitaException.setProcessInstanceIdOnContext(processInstanceId); sBonitaException.setRootProcessInstanceIdOnContext(rootProcessInstanceId); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/TriggerSignalWork.java ================================================ /** * 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.execution.work.failurewrapping; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent; import org.bonitasoft.engine.execution.work.TenantAwareBonitaWork; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Baptiste Mesta */ public class TriggerSignalWork extends TenantAwareBonitaWork { private long signalId; private String signalName; public TriggerSignalWork(long signalId, String signalName) { this.signalId = signalId; this.signalName = signalName; } @Override public String getDescription() { return getClass().getSimpleName() + " waitingSignalEvent: " + signalId; } @Override public CompletableFuture work(Map context) throws Exception { ServiceAccessor serviceAccessor = getServiceAccessor(context); SWaitingSignalEvent listeningSignal = serviceAccessor.getEventInstanceService() .getWaitingSignalEvent(signalId); serviceAccessor.getEventsHandler().triggerCatchEvent(listeningSignal, null); return CompletableFuture.completedFuture(null); } @Override public void handleFailure(Throwable e, Map context) throws Exception { throw new UnsupportedOperationException("No automatic failure handling for signals. See recovery procedure."); } @Override public String getRecoveryProcedure() { return "send the signal " + signalName + " again"; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/execution/work/failurewrapping/TxInHandleFailureWrappingWork.java ================================================ /** * 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.execution.work.failurewrapping; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import org.bonitasoft.engine.commons.exceptions.ExceptionContext; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.execution.work.WrappingBonitaWork; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.transaction.UserTransactionService; import org.bonitasoft.engine.work.BonitaWork; /** * This work manages the transaction in its handleFailure method. * Don't use this work with {@link org.bonitasoft.engine.execution.work.TxBonitaWork} * * @author Celine Souchet */ public abstract class TxInHandleFailureWrappingWork extends WrappingBonitaWork { protected TxInHandleFailureWrappingWork(final BonitaWork work) { super(work); } @Override public CompletableFuture work(final Map context) throws Exception { return getWrappedWork().work(context); } @Override public void handleFailure(final Throwable e, final Map context) throws Exception { // Enrich the exception before log it. if (e instanceof ExceptionContext exceptionContext) { final ServiceAccessor serviceAccessor = getServiceAccessor(context); final UserTransactionService transactionService = serviceAccessor.getUserTransactionService(); transactionService.executeInTransaction((Callable) () -> { setExceptionContext(exceptionContext, context); return null; }); } getWrappedWork().handleFailure(e, context); } protected abstract void setExceptionContext(final ExceptionContext exceptionContext, final Map context) throws SBonitaException; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/BusinessDataExpressionExecutorStrategy.java ================================================ /** * 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.expression; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.business.data.BusinessDataRetriever; import org.bonitasoft.engine.business.data.RefBusinessDataRetriever; import org.bonitasoft.engine.commons.Container; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.operation.BusinessDataContext; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Colin Puy * @author Emmanuel Duchastenier * @author Celine Souchet * @author Matthieu Chaffotte */ public class BusinessDataExpressionExecutorStrategy extends CommonBusinessDataExpressionExecutorStrategy { private final RefBusinessDataRetriever refBusinessDataRetriever; private final BusinessDataRetriever businessDataRetriever; public BusinessDataExpressionExecutorStrategy(final RefBusinessDataRetriever refBusinessDataRetriever, BusinessDataRetriever businessDataRetriever) { super(); this.refBusinessDataRetriever = refBusinessDataRetriever; this.businessDataRetriever = businessDataRetriever; } @Override public ExpressionKind getExpressionKind() { return KIND_BUSINESS_DATA; } @Override public Object evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final String businessDataName = expression.getContent(); if (context.containsKey(businessDataName)) { return context.get(businessDataName); } final Long containerId = (Long) context.get(SExpressionContext.CONTAINER_ID_KEY); final String containerType = (String) context.get(SExpressionContext.CONTAINER_TYPE_KEY); try { final SRefBusinessDataInstance refBusinessDataInstance = refBusinessDataRetriever .getRefBusinessDataInstance(new BusinessDataContext( businessDataName, new Container(containerId, containerType))); return businessDataRetriever.getBusinessData(refBusinessDataInstance); } catch (final SBonitaReadException | SFlowNodeReadException e) { throw new SExpressionEvaluationException( "Unable to retrieve business data instance with name " + businessDataName, e, businessDataName); } catch (final SBonitaException e) { setProcessInstanceId(containerId, containerType, e); throw new SExpressionEvaluationException(e, expression.getName()); } } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final List bizData = new ArrayList<>(expressions.size()); final List alreadyEvaluatedExpressionContent = new ArrayList<>(); for (final SExpression expression : expressions) { if (!alreadyEvaluatedExpressionContent.contains(expression.getContent())) { bizData.add(evaluate(expression, context, resolvedExpressions, containerState)); alreadyEvaluatedExpressionContent.add(expression.getContent()); } } return bizData; } @Override public boolean mustPutEvaluatedExpressionInContext() { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/BusinessDataReferenceExpressionExecutorStrategy.java ================================================ /** * 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.expression; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.business.data.RefBusinessDataRetriever; import org.bonitasoft.engine.commons.Container; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.operation.BusinessDataContext; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Colin Puy * @author Emmanuel Duchastenier * @author Celine Souchet * @author Matthieu Chaffotte */ public class BusinessDataReferenceExpressionExecutorStrategy extends CommonBusinessDataExpressionExecutorStrategy { private final RefBusinessDataRetriever refBusinessDataRetriever; public BusinessDataReferenceExpressionExecutorStrategy(final RefBusinessDataRetriever refBusinessDataRetriever) { this.refBusinessDataRetriever = refBusinessDataRetriever; } @Override public ExpressionKind getExpressionKind() { return KIND_BUSINESS_DATA_REFERENCE; } @Override public Object evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final String businessDataName = expression.getContent(); final Long containerId = (Long) context.get(SExpressionContext.CONTAINER_ID_KEY); final String containerType = (String) context.get(SExpressionContext.CONTAINER_TYPE_KEY); try { final SRefBusinessDataInstance refBusinessDataInstance = refBusinessDataRetriever .getRefBusinessDataInstance(new BusinessDataContext( businessDataName, new Container(containerId, containerType))); return ModelConvertor.toBusinessDataReference(refBusinessDataInstance); } catch (final SBonitaReadException e) { throw new SExpressionEvaluationException(e, "Unable to retrieve business data instance with name " + businessDataName); } catch (final SBonitaException e) { setProcessInstanceId(containerId, containerType, e); throw new SExpressionEvaluationException(e, expression.getName()); } } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final List bizData = new ArrayList<>(expressions.size()); for (final SExpression expression : expressions) { bizData.add(evaluate(expression, context, resolvedExpressions, containerState)); } return bizData; } @Override public boolean mustPutEvaluatedExpressionInContext() { return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/BusinessObjectDAOExpressionStrategy.java ================================================ /** * 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.expression; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.SBusinessDataRepositoryException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Emmanuel Duchastenier */ public class BusinessObjectDAOExpressionStrategy extends NonEmptyContentExpressionExecutorStrategy { private final BusinessDataRepository businessDataRepository; public BusinessObjectDAOExpressionStrategy(final BusinessDataRepository businessDataRepository) { this.businessDataRepository = businessDataRepository; } @Override public ExpressionKind getExpressionKind() { return KIND_BUSINESS_OBJECT_DAO; } @Override public Object evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final String daoName = expression.getContent(); if (context.containsKey(daoName)) { return context.get(daoName); } final String daoServerImplementation = getDAOServerImplementationFromInterface(expression.getReturnType()); try { return instantiateDAO(daoServerImplementation); } catch (Exception e) { throw new SExpressionEvaluationException( "Unable to instantiate Business Object Server DAO implementation" + daoServerImplementation, e, expression.getName()); } } protected Object instantiateDAO(final String daoClassName) throws Exception { Class daoImplClass = Thread.currentThread().getContextClassLoader().loadClass(daoClassName); if (daoImplClass != null) { Constructor constructor = daoImplClass.getConstructor(BusinessDataRepository.class); return constructor.newInstance(businessDataRepository); } throw new SBusinessDataRepositoryException("Cannot load class " + daoClassName); } protected String getDAOServerImplementationFromInterface(final String daoClassName) { int pointIdx = daoClassName.lastIndexOf('.'); return daoClassName.substring(0, pointIdx + 1) + "server." + daoClassName.substring(pointIdx + 1) + "Impl"; } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { List daos = new ArrayList<>(expressions.size()); for (SExpression expression : expressions) { daos.add(evaluate(expression, context, resolvedExpressions, containerState)); } return daos; } @Override public boolean mustPutEvaluatedExpressionInContext() { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/CommonBusinessDataExpressionExecutorStrategy.java ================================================ /** * 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.expression; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public abstract class CommonBusinessDataExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy { void setProcessInstanceId(final Long containerId, final String containerType, final SBonitaException e) { if ("PROCESS_INSTANCE".equals(containerType)) { e.setProcessInstanceIdOnContext(containerId); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/ContractInputExpressionExecutorStrategy.java ================================================ /** * 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.expression; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.core.contract.data.ContractDataService; import org.bonitasoft.engine.core.contract.data.SContractDataNotFoundException; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Matthieu Chaffotte */ public class ContractInputExpressionExecutorStrategy implements ExpressionExecutorStrategy { private final ContractDataService contractDataService; public ContractInputExpressionExecutorStrategy(final ContractDataService contractDataService) { super(); this.contractDataService = contractDataService; } @Override public Object evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException, SExpressionDependencyMissingException { final Long containerId = (Long) context.get(CONTAINER_ID_KEY); final String containerType = (String) context.get(CONTAINER_TYPE_KEY); if (containerId == null) { throw new SExpressionEvaluationException("Unable to evaluate contract input without container id", expression.getName()); } try { if ("PROCESS_INSTANCE".equals(containerType)) { return contractDataService.getProcessDataValue(containerId, expression.getContent()); } else { return contractDataService.getUserTaskDataValue(containerId, expression.getContent()); } } catch (final SContractDataNotFoundException | SBonitaReadException e) { throw new SExpressionEvaluationException(e, expression.getName()); } } @Override public void validate(final SExpression expression) throws SInvalidExpressionException { // No validation because the type validation can only be done after evaluation } @Override public ExpressionKind getExpressionKind() { return KIND_CONTRACT_INPUT; } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException, SExpressionDependencyMissingException { final List results = new ArrayList(); for (final SExpression expression : expressions) { results.add(evaluate(expression, context, resolvedExpressions, containerState)); } return results; } @Override public boolean mustPutEvaluatedExpressionInContext() { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/DataExpressionExecutorStrategy.java ================================================ /** * 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.expression; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.lang.model.SourceVersion; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.data.instance.api.ParentContainerResolver; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SADataInstance; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Zhao Na * @author Celine Souchet */ public class DataExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy { public static final String TIME = "time"; private final DataInstanceService dataService; private ParentContainerResolver parentContainerResolver; public DataExpressionExecutorStrategy(final DataInstanceService dataService, final ParentContainerResolver parentContainerResolver) { this.dataService = dataService; this.parentContainerResolver = parentContainerResolver; } @Override public Object evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionDependencyMissingException, SExpressionEvaluationException { return evaluate(Collections.singletonList(expression), context, resolvedExpressions, containerState).get(0); } @Override public void validate(final SExpression expression) throws SInvalidExpressionException { // $ can be part of variable name super.validate(expression); if (!SourceVersion.isIdentifier(expression.getContent()) || SourceVersion.isKeyword(expression.getContent())) { throw new SInvalidExpressionException( expression.getContent() + " is not a valid data name in expression : " + expression, expression.getName()); } } @Override public ExpressionKind getExpressionKind() { return KIND_VARIABLE; } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionDependencyMissingException, SExpressionEvaluationException { final int maxExpressionSize = expressions.size(); final List dataNames = new ArrayList<>(maxExpressionSize); final Map results = new HashMap<>(maxExpressionSize); for (final SExpression sExpression : expressions) { final String dataName = sExpression.getContent(); if (context.containsKey(dataName)) { final Serializable value = (Serializable) context.get(dataName); results.put(dataName, value); } else { dataNames.add(dataName); } } if (dataNames.isEmpty()) { return buildExpressionResultSameOrderAsInputList(expressions, results); } if (context == null || !context.containsKey(CONTAINER_ID_KEY) || !context.containsKey(CONTAINER_TYPE_KEY)) { throw new SExpressionDependencyMissingException( "The context to evaluate the data '" + dataNames + "' was not set"); } try { final long containerId = (Long) context.get(CONTAINER_ID_KEY); final String containerType = (String) context.get(CONTAINER_TYPE_KEY); final Long time = (Long) context.get(TIME); if (time != null) { final List dataInstances = dataService.getSADataInstances(containerId, containerType, parentContainerResolver, dataNames, time); for (final SADataInstance dataInstance : dataInstances) { dataNames.remove(dataInstance.getName()); results.put(dataInstance.getName(), dataInstance.getValue()); } } final List dataInstances = dataService.getDataInstances(dataNames, containerId, containerType, parentContainerResolver); for (final SDataInstance dataInstance : dataInstances) { dataNames.remove(dataInstance.getName()); results.put(dataInstance.getName(), dataInstance.getValue()); } if (!dataNames.isEmpty()) { throw new SExpressionEvaluationException("Some data were not found " + dataNames, dataNames.get(0)); } return buildExpressionResultSameOrderAsInputList(expressions, results); } catch (final SDataInstanceException e) { throw new SExpressionEvaluationException(e, null); } } private List buildExpressionResultSameOrderAsInputList(final List expressions, final Map results) { final List list = new ArrayList<>(expressions.size()); for (final SExpression expression : expressions) { list.add(results.get(expression.getContent())); } return list; } @Override public boolean mustPutEvaluatedExpressionInContext() { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/DocumentListReferenceExpressionExecutorStrategy.java ================================================ /** * 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.expression; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.api.impl.DocumentHelper; import org.bonitasoft.engine.core.document.model.AbstractSMappedDocument; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class DocumentListReferenceExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy { private final DocumentService documentService; private final ActivityInstanceService activityInstanceService; private final DocumentHelper documentHelper; public DocumentListReferenceExpressionExecutorStrategy(final DocumentService documentService, final ActivityInstanceService activityInstanceService, final ProcessDefinitionService processDefinitionService, final ProcessInstanceService processInstanceService) { this.documentService = documentService; this.activityInstanceService = activityInstanceService; documentHelper = new DocumentHelper(documentService, processDefinitionService, processInstanceService); } @Override public Object evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException, SExpressionDependencyMissingException { return evaluate(Collections.singletonList(expression), context, resolvedExpressions, containerState).get(0); } @Override public ExpressionKind getExpressionKind() { return KIND_DOCUMENT_LIST; } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException, SExpressionDependencyMissingException { final Long containerId = (Long) context.get(CONTAINER_ID_KEY); final String containerType = (String) context.get(CONTAINER_TYPE_KEY); try { final Long time = (Long) context.get("time"); final long processInstanceId = getProcessInstance(containerId, containerType, time != null); final List results = new ArrayList<>(expressions.size()); for (final SExpression expression : expressions) { final String docListName = expression.getContent(); if (context.containsKey(docListName)) { results.add(context.get(docListName)); } else { results.add(getDocumentList(processInstanceId, docListName, time)); } } return results; } catch (final SExpressionDependencyMissingException e) { throw e; } catch (final SBonitaException e) { throw new SExpressionEvaluationException(e, null); } } // Visible for Testing List getDocumentList(final long processInstanceId, final String name, final Long time) throws SBonitaReadException { final List documentList = getAllDocumentOfTheList(processInstanceId, name, time); try { if (documentList.isEmpty() && !documentHelper.isListDefinedInDefinition(name, processInstanceId)) { return null; } } catch (final SObjectNotFoundException e) { return null; } return ModelConvertor.toDocuments(documentList, documentService); } private List getAllDocumentOfTheList(final long processInstanceId, final String name, final Long time) throws SBonitaReadException { if (time != null) { return documentService.getDocumentList(name, processInstanceId, time); } QueryOptions queryOptions = new QueryOptions(0, 100); List mappedDocuments; final List result = new ArrayList<>(); do { mappedDocuments = documentService.getDocumentList(name, processInstanceId, queryOptions.getFromIndex(), queryOptions.getNumberOfResults()); result.addAll(mappedDocuments); queryOptions = QueryOptions.getNextPage(queryOptions); } while (mappedDocuments.size() == 100); return result; } private long getProcessInstance(final Long containerId, final String containerType, final boolean flowNodeIsArchived) throws SBonitaException { if (containerId == null || containerType == null) { throw new SExpressionDependencyMissingException("The context to retrieve the document is not set."); } if (DataInstanceContainer.PROCESS_INSTANCE.name().equals(containerType)) { return containerId; } if (flowNodeIsArchived) { return activityInstanceService.getMostRecentArchivedActivityInstance(containerId) .getParentProcessInstanceId(); } return activityInstanceService.getFlowNodeInstance(containerId).getParentProcessInstanceId(); } @Override public boolean mustPutEvaluatedExpressionInContext() { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/DocumentReferenceExpressionExecutorStrategy.java ================================================ /** * 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.expression; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.model.AbstractSMappedDocument; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class DocumentReferenceExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy { private final DocumentService documentService; private final ActivityInstanceService activityInstanceService; public DocumentReferenceExpressionExecutorStrategy(final DocumentService documentService, final ActivityInstanceService activityInstanceService) { this.documentService = documentService; this.activityInstanceService = activityInstanceService; } @Override public Object evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException, SExpressionDependencyMissingException { return evaluate(Collections.singletonList(expression), context, resolvedExpressions, containerState).get(0); } @Override public ExpressionKind getExpressionKind() { return KIND_DOCUMENT; } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException, SExpressionDependencyMissingException { final Long containerId = (Long) context.get(CONTAINER_ID_KEY); final String containerType = (String) context.get(CONTAINER_TYPE_KEY); final Long time = (Long) context.get("time"); try { final long processInstanceId = getProcessInstance(containerId, containerType, time); final List results = new ArrayList<>(expressions.size()); for (final SExpression expression : expressions) { final String docName = expression.getContent(); if (context.containsKey(docName)) { results.add(context.get(docName)); } else { results.add(getDocument(processInstanceId, expression, time)); } } return results; } catch (final SExpressionDependencyMissingException e) { throw e; } catch (final SBonitaException e) { throw new SExpressionEvaluationException(e, null); } } private Document getDocument(final long processInstanceId, final SExpression expression, final Long time) throws SBonitaReadException { try { AbstractSMappedDocument document; if (time != null) { document = documentService.getMappedDocument(processInstanceId, expression.getContent(), time); } else { document = documentService.getMappedDocument(processInstanceId, expression.getContent()); } return ModelConvertor.toDocument(document, documentService); } catch (final SObjectNotFoundException e) { return null; } } // visible for testing long getProcessInstance(final Long containerId, final String containerType, Long time) throws SFlowNodeNotFoundException, SFlowNodeReadException, SExpressionDependencyMissingException, SActivityInstanceNotFoundException { if (containerId == null || containerType == null) { throw new SExpressionDependencyMissingException("The context to retrieve the document is not set."); } if (DataInstanceContainer.PROCESS_INSTANCE.name().equals(containerType)) { return containerId; } // on a flownode instance, containerId is always the original id: if (time != null) { return activityInstanceService.getMostRecentArchivedActivityInstance(containerId) .getParentProcessInstanceId(); } else { return activityInstanceService.getFlowNodeInstance(containerId).getParentProcessInstanceId(); } } @Override public boolean mustPutEvaluatedExpressionInContext() { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/EngineConstantExpressionBuilder.java ================================================ /** * 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.expression; import org.bonitasoft.engine.api.APIAccessor; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.connector.EngineExecutionContext; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilder; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public final class EngineConstantExpressionBuilder { public static SExpression getConnectorAPIAccessorExpression() { final SExpressionBuilder builder = BuilderFactory.get(SExpressionBuilderFactory.class).createNewInstance(); builder.setContent("connectorApiAccessor").setExpressionType(ExpressionType.TYPE_ENGINE_CONSTANT.name()) .setReturnType(APIAccessor.class.getName()); try { return builder.done(); } catch (final SInvalidExpressionException e) { // Never happens !! throw new SBonitaRuntimeException(e); } } public static SExpression getEngineExecutionContext() { final SExpressionBuilder builder = BuilderFactory.get(SExpressionBuilderFactory.class).createNewInstance(); builder.setContent("engineExecutionContext").setExpressionType(ExpressionType.TYPE_ENGINE_CONSTANT.name()) .setReturnType(EngineExecutionContext.class.getName()); try { return builder.done(); } catch (final SInvalidExpressionException e) { // Never happens !! throw new SBonitaRuntimeException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/EngineConstantExpressionExecutorStrategy.java ================================================ /** * 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.expression; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.APIAccessor; import org.bonitasoft.engine.api.impl.APIAccessorImpl; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.connector.ConnectorAPIAccessorImpl; import org.bonitasoft.engine.connector.EngineExecutionContext; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; 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.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; /** * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class EngineConstantExpressionExecutorStrategy implements ExpressionExecutorStrategy { private final ActivityInstanceService activityInstanceService; private final ProcessInstanceService processInstanceService; private final SessionAccessor sessionAccessor; private final SessionService sessionService; public EngineConstantExpressionExecutorStrategy(final ActivityInstanceService activityInstanceService, final ProcessInstanceService processInstanceService, final SessionService sessionService, final SessionAccessor sessionAccessor) { this.activityInstanceService = activityInstanceService; this.processInstanceService = processInstanceService; this.sessionService = sessionService; this.sessionAccessor = sessionAccessor; } @Override public Serializable evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { ExpressionConstants expressionConstant = ExpressionConstantsResolver .getExpressionConstantsFromName(expression.getContent()); if (expressionConstant == null) { expressionConstant = ExpressionConstants.API_ACCESSOR;// just to make the expressionConstantsResolver load constants expressionConstant = ExpressionConstantsResolver.getExpressionConstantsFromName(expression.getContent()); } final String expressionName = expression.getName(); if (expressionConstant == null) { throw new SExpressionEvaluationException( expression.getContent() + " is not a valid Engine-provided variable", expressionName); } return evaluate(context, containerState, expressionConstant, expressionName); } public Serializable evaluate(Map context, ContainerState containerState, ExpressionConstants expressionConstant, String expressionName) throws SExpressionEvaluationException { try { switch (expressionConstant) { case API_ACCESSOR: return getApiAccessor(); case CONNECTOR_API_ACCESSOR: return getConnectorApiAccessor(); case ENGINE_EXECUTION_CONTEXT: case ACTIVITY_INSTANCE_ID: case PROCESS_INSTANCE_ID: case ROOT_PROCESS_INSTANCE_ID: case PROCESS_DEFINITION_ID: case TASK_ASSIGNEE_ID: return getFromContextOrEngineExecutionContext(expressionConstant, context, containerState); case LOGGED_USER_ID: return getLoggedUserFromSession(context, containerState); case LOOP_COUNTER: return getLoopCounter(context); case NUMBER_OF_COMPLETED_INSTANCES: case NUMBER_OF_INSTANCES: case NUMBER_OF_ACTIVE_INSTANCES: case NUMBER_OF_TERMINATED_INSTANCES: return getInstanceNumberForTheEngineConstant(context, expressionConstant); default: final Object object = context.get(expressionConstant.getEngineConstantName()); if (object == null) { throw new SExpressionEvaluationException( "EngineConstantExpression not supported for: " + expressionConstant.getEngineConstantName(), expressionName); } return (Serializable) object; } } catch (final SProcessInstanceNotFoundException | SProcessInstanceReadException e) { throw new SExpressionEvaluationException( "Error retrieving process instance while building EngineExecutionContext as EngineConstantExpression", e, expressionName); } catch (final SFlowNodeReadException | SFlowNodeNotFoundException e) { throw new SExpressionEvaluationException( "Error retrieving flow node instance while building EngineExecutionContext as EngineConstantExpression", e, expressionName); } catch (final SActivityInstanceNotFoundException e) { throw new SExpressionEvaluationException( "Error retrieving activity instance while building EngineExecutionContext as EngineConstantExpression", e, expressionName); } catch (final SBonitaReadException e) { throw new SExpressionEvaluationException( "Error while building EngineExecutionContext as EngineConstantExpression", e, expressionName); } catch (final SBonitaException e) { throw new SExpressionEvaluationException(e, expressionName); } } private Serializable getInstanceNumberForTheEngineConstant(Map context, ExpressionConstants expressionConstants) throws SFlowNodeReadException, SFlowNodeNotFoundException, SExpressionEvaluationException { final String containerType = (String) context.get(SExpressionContext.CONTAINER_TYPE_KEY); final long containerId = (Long) context.get(SExpressionContext.CONTAINER_ID_KEY); String engineConstantName = expressionConstants.getEngineConstantName(); if (DataInstanceContainer.ACTIVITY_INSTANCE.toString().equals(containerType)) { SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(containerId); if (flowNodeInstance instanceof SMultiInstanceActivityInstance) { switch (expressionConstants) { case NUMBER_OF_TERMINATED_INSTANCES: return ((SMultiInstanceActivityInstance) flowNodeInstance).getNumberOfTerminatedInstances(); case NUMBER_OF_ACTIVE_INSTANCES: return ((SMultiInstanceActivityInstance) flowNodeInstance).getNumberOfActiveInstances(); case NUMBER_OF_INSTANCES: return ((SMultiInstanceActivityInstance) flowNodeInstance).getNumberOfInstances(); case NUMBER_OF_COMPLETED_INSTANCES: return ((SMultiInstanceActivityInstance) flowNodeInstance).getNumberOfCompletedInstances(); default: throw new SExpressionEvaluationException( engineConstantName + " is not available in this context", engineConstantName); } } SMultiInstanceActivityInstance multiInstanceActivityInstance = (SMultiInstanceActivityInstance) activityInstanceService .getFlowNodeInstance(flowNodeInstance.getParentActivityInstanceId()); switch (expressionConstants) { case NUMBER_OF_TERMINATED_INSTANCES: return multiInstanceActivityInstance.getNumberOfTerminatedInstances(); case NUMBER_OF_ACTIVE_INSTANCES: return multiInstanceActivityInstance.getNumberOfActiveInstances(); case NUMBER_OF_INSTANCES: return multiInstanceActivityInstance.getNumberOfInstances(); case NUMBER_OF_COMPLETED_INSTANCES: return multiInstanceActivityInstance.getNumberOfCompletedInstances(); default: throw new SExpressionEvaluationException(engineConstantName + " is not available in this context", engineConstantName); } } throw new SExpressionEvaluationException(engineConstantName + " is not available in this context", engineConstantName); } private Serializable getLoopCounter(Map context) throws SExpressionEvaluationException, SFlowNodeReadException, SFlowNodeNotFoundException { final String containerType = (String) context.get(SExpressionContext.CONTAINER_TYPE_KEY); final long containerId = (Long) context.get(SExpressionContext.CONTAINER_ID_KEY); if (DataInstanceContainer.ACTIVITY_INSTANCE.toString().equals(containerType)) { SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(containerId); if (flowNodeInstance instanceof SLoopActivityInstance) { return flowNodeInstance.getLoopCounter(); } SLoopActivityInstance loopActivityInstance = (SLoopActivityInstance) activityInstanceService .getFlowNodeInstance(flowNodeInstance.getParentActivityInstanceId()); return loopActivityInstance.getLoopCounter(); } throw new SExpressionEvaluationException("loopCounter is not available in this context", "loopCounter"); } protected APIAccessor getApiAccessor() { return new APIAccessorImpl(); } protected APIAccessor getConnectorApiAccessor() { return new ConnectorAPIAccessorImpl(); } long getLoggedUserFromSession(Map context, ContainerState containerState) { long loggedUserFromSession = sessionService.getLoggedUserFromSession(sessionAccessor); if (loggedUserFromSession <= 0) { //try to get it from the user task assignee if applicable try { return (long) getFromContextOrEngineExecutionContext(ExpressionConstants.TASK_ASSIGNEE_ID, context, containerState); } catch (SBonitaException e) { return -1L; } } return loggedUserFromSession; } public Serializable getFromContextOrEngineExecutionContext(final ExpressionConstants expressionConstant, final Map context, final ContainerState containerState) throws SBonitaException { final Object object = context.get(expressionConstant.getEngineConstantName()); if (object == null) { // try to get it from an already evaluated context final EngineExecutionContext engineContext = (EngineExecutionContext) context .get(ExpressionConstants.ENGINE_EXECUTION_CONTEXT .getEngineConstantName()); if (engineContext != null) { return engineContext.getExpressionConstant(expressionConstant); } return evaluate(expressionConstant, context, containerState); } // we have it already evaluated return (Serializable) object; } private Serializable evaluate(final ExpressionConstants expressionConstant, final Map context, final ContainerState containerState) throws SBonitaException { // guess it if (ExpressionConstants.ENGINE_EXECUTION_CONTEXT.equals(expressionConstant)) { return createContext(context, containerState); } else if (context.containsKey(SExpressionContext.CONTAINER_TYPE_KEY) && context.containsKey(SExpressionContext.CONTAINER_ID_KEY)) { final String containerType = (String) context.get(SExpressionContext.CONTAINER_TYPE_KEY); final long containerId = (Long) context.get(SExpressionContext.CONTAINER_ID_KEY); if (ExpressionConstants.PROCESS_DEFINITION_ID.equals(expressionConstant)) { return (Serializable) context.get(SExpressionContext.PROCESS_DEFINITION_ID_KEY); } else if (DataInstanceContainer.ACTIVITY_INSTANCE.toString().equals(containerType)) { return evaluateUsingActivityInstanceContainer(expressionConstant, context, containerId); } else { return evaluateUsingProcessInstanceContainer(expressionConstant, context, containerId); } } else { return -1L;// no container id and not processDefinition } } private Serializable evaluateUsingProcessInstanceContainer(final ExpressionConstants expressionConstant, final Map context, final long containerId) throws SProcessInstanceNotFoundException, SProcessInstanceReadException { if (ExpressionConstants.PROCESS_INSTANCE_ID.equals(expressionConstant)) { return containerId; } else if (ExpressionConstants.TASK_ASSIGNEE_ID.equals(expressionConstant)) { return -1L; // the assignee is related to an user task } else { // get the process and fill the others elements fillDependenciesFromProcessInstance(context, containerId); return getNonNullLong(expressionConstant, context); } } private Serializable evaluateUsingActivityInstanceContainer(final ExpressionConstants expressionConstant, final Map context, final long containerId) throws SBonitaException { if (ExpressionConstants.ACTIVITY_INSTANCE_ID.equals(expressionConstant)) { return containerId; } // get the activity and fill the others elements fillDependenciesFromFlowNodeInstance(context, containerId); return getNonNullLong(expressionConstant, context); } private Serializable getNonNullLong(final ExpressionConstants expressionConstant, final Map context) { final Serializable serializable = (Serializable) context.get(expressionConstant.getEngineConstantName()); return serializable == null ? -1L : serializable; } private void fillDependenciesFromProcessInstance(final Map context, final long processInstanceId) throws SProcessInstanceNotFoundException, SProcessInstanceReadException { final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); context.put(ExpressionConstants.PROCESS_INSTANCE_ID.getEngineConstantName(), processInstance.getId()); context.put(ExpressionConstants.ROOT_PROCESS_INSTANCE_ID.getEngineConstantName(), processInstance.getRootProcessInstanceId()); } void fillDependenciesFromFlowNodeInstance(final Map context, final long flowNodeInstanceId) throws SBonitaException { if (context.get("time") != null) { final SAActivityInstance aActivityInstance = activityInstanceService .getMostRecentArchivedActivityInstance(flowNodeInstanceId); context.put(ExpressionConstants.PROCESS_INSTANCE_ID.getEngineConstantName(), aActivityInstance.getLogicalGroup(3)); context.put(ExpressionConstants.ROOT_PROCESS_INSTANCE_ID.getEngineConstantName(), aActivityInstance.getLogicalGroup(1)); if (isHumanTask(aActivityInstance)) { final SAHumanTaskInstance saHumanTask = (SAHumanTaskInstance) aActivityInstance; context.put(ExpressionConstants.TASK_ASSIGNEE_ID.getEngineConstantName(), saHumanTask.getAssigneeId()); } } else { final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(flowNodeInstanceId); context.put(ExpressionConstants.PROCESS_INSTANCE_ID.getEngineConstantName(), flowNodeInstance.getLogicalGroup(3)); context.put(ExpressionConstants.ROOT_PROCESS_INSTANCE_ID.getEngineConstantName(), flowNodeInstance.getLogicalGroup(1)); if (isHumanTask(flowNodeInstance)) { final SHumanTaskInstance taskInstance = (SHumanTaskInstance) flowNodeInstance; context.put(ExpressionConstants.TASK_ASSIGNEE_ID.getEngineConstantName(), taskInstance.getAssigneeId()); } } } private boolean isHumanTask(final SAFlowNodeInstance aFlowNodeInstance) { return SFlowNodeType.USER_TASK.equals(aFlowNodeInstance.getType()) || SFlowNodeType.MANUAL_TASK.equals(aFlowNodeInstance.getType()); } private boolean isHumanTask(final SFlowNodeInstance flowNodeInstance) { return SFlowNodeType.USER_TASK.equals(flowNodeInstance.getType()) || SFlowNodeType.MANUAL_TASK.equals(flowNodeInstance.getType()); } private Serializable createContext(final Map context, final ContainerState containerState) throws SProcessInstanceNotFoundException, SProcessInstanceReadException, SActivityInstanceNotFoundException, SFlowNodeReadException, SBonitaReadException { final EngineExecutionContext ctx = new EngineExecutionContext(); if (context.containsKey(SExpressionContext.CONTAINER_TYPE_KEY) && context.containsKey(SExpressionContext.CONTAINER_ID_KEY)) { final String containerType = (String) context.get(SExpressionContext.CONTAINER_TYPE_KEY); final long containerId = (Long) context.get(SExpressionContext.CONTAINER_ID_KEY); if (ContainerState.ARCHIVED.equals(containerState)) { if (DataInstanceContainer.ACTIVITY_INSTANCE.toString().equals(containerType)) { updateContextFromArchivedActivityInstance(ctx, containerId); } else if (DataInstanceContainer.PROCESS_INSTANCE.toString().equals(containerType)) { updateContextFromArchivedProcessInstance(ctx, containerId); } } else { if (DataInstanceContainer.ACTIVITY_INSTANCE.toString().equals(containerType)) { updateContextFromActivityInstance(ctx, containerId); } else if (DataInstanceContainer.PROCESS_INSTANCE.toString().equals(containerType)) { updateContextFromProcessInstance(ctx, containerId); } } } if (context.containsKey(SExpressionContext.PROCESS_DEFINITION_ID_KEY)) { ctx.setProcessDefinitionId((Long) context.get(SExpressionContext.PROCESS_DEFINITION_ID_KEY)); } return ctx; } private void updateContextFromArchivedProcessInstance(final EngineExecutionContext ctx, final long processInstanceId) throws SBonitaReadException { final SAProcessInstance processInstance = processInstanceService .getLastArchivedProcessInstance(processInstanceId); if (processInstance != null) { ctx.setProcessInstanceId(processInstance.getSourceObjectId()); ctx.setRootProcessInstanceId(processInstance.getRootProcessInstanceId()); } } private void updateContextFromArchivedActivityInstance(final EngineExecutionContext ctx, final long activityInstanceId) throws SBonitaReadException { final SAActivityInstance activityInstance = activityInstanceService .getLastArchivedFlowNodeInstance(SAActivityInstance.class, activityInstanceId); if (activityInstance != null) { ctx.setActivityInstanceId(activityInstance.getSourceObjectId()); ctx.setProcessInstanceId(activityInstance.getParentProcessInstanceId()); ctx.setRootProcessInstanceId(activityInstance.getRootProcessInstanceId()); if (isHumanTask(activityInstance)) { ctx.setTaskAssigneeId(((SAHumanTaskInstance) activityInstance).getAssigneeId()); } } } private void updateContextFromProcessInstance(final EngineExecutionContext ctx, final long processInstanceId) throws SProcessInstanceNotFoundException, SProcessInstanceReadException { final SProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); ctx.setProcessInstanceId(processInstance.getId()); ctx.setRootProcessInstanceId(processInstance.getRootProcessInstanceId()); } private void updateContextFromActivityInstance(final EngineExecutionContext ctx, final long activityInstanceId) throws SActivityReadException, SActivityInstanceNotFoundException { ctx.setActivityInstanceId(activityInstanceId); final SActivityInstance activityInstance = activityInstanceService.getActivityInstance(activityInstanceId); ctx.setProcessInstanceId(activityInstance.getParentProcessInstanceId()); ctx.setRootProcessInstanceId(activityInstance.getRootProcessInstanceId()); if (isHumanTask(activityInstance)) { ctx.setTaskAssigneeId(((SHumanTaskInstance) activityInstance).getAssigneeId()); } } @Override public void validate(final SExpression expression) throws SInvalidExpressionException { if (ExpressionConstantsResolver.getExpressionConstantsFromName(expression.getContent()) == null) { throw new SInvalidExpressionException( "Unable to get Engine Constant '" + expression.getContent() + "' in expression: " + expression, expression.getName()); } } @Override public ExpressionKind getExpressionKind() { return KIND_ENGINE_CONSTANT; } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final List results = new ArrayList(); for (final SExpression sExpression : expressions) { results.add(evaluate(sExpression, context, resolvedExpressions, containerState)); } return results; } @Override public boolean mustPutEvaluatedExpressionInContext() { return true; } public SessionAccessor getSessionAccessor() { return sessionAccessor; } public SessionService getSessionService() { return sessionService; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/ParameterExpressionExecutorStrategy.java ================================================ /** * 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.expression; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.lang.model.SourceVersion; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.parameter.ParameterService; import org.bonitasoft.engine.parameter.SParameter; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * Retrieve a String parameter from the ParameterService. The content of the expression must be the parameter name, as a * String. * The dependency map must contain a value for 'processDefinitionId' key to identify the process definition context to * evaluate the parameter. * * @see {@link ParameterService} * @author Zhao Na * @author Celine Souchet */ public class ParameterExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy { public static final String PROCESS_DEFINITION_ID = "processDefinitionId"; private final ParameterService parameterService; public ParameterExpressionExecutorStrategy(final ParameterService parameterService) { this.parameterService = parameterService; } @Override public Object evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionDependencyMissingException, SExpressionEvaluationException { final String expressionContent = expression.getContent(); try { if (context != null && !context.isEmpty()) { if (context.containsKey(PROCESS_DEFINITION_ID)) { final long processDefinitionId = (Long) context.get(PROCESS_DEFINITION_ID); final SParameter parameter = parameterService.get(processDefinitionId, expressionContent); if (parameter == null) { throw new SExpressionEvaluationException( "Referenced parameter '" + expressionContent + "' does not exist", expression.getName()); } try { final String returnType = expression.getReturnType(); if (Boolean.class.getName().equals(returnType)) { return Boolean.parseBoolean(parameter.getValue()); } else if (Double.class.getName().equals(returnType)) { return Double.parseDouble(parameter.getValue()); } else if (Integer.class.getName().equals(returnType)) { return Integer.parseInt(parameter.getValue()); } else if (String.class.getName().equals(returnType)) { return parameter.getValue(); } } catch (final NumberFormatException e) { throw new SExpressionEvaluationException( "Can't convert value = " + parameter.getValue() + " in type = returnType", e, expression.getName()); } } else { throw new SExpressionDependencyMissingException( "Mandatory dependency processDefinitionId is missing."); } } } catch (SBonitaReadException e) { throw new SExpressionEvaluationException("Unable to read references parameter '" + expressionContent + "' ", e, expression.getName()); } return null; } @Override public void validate(final SExpression expression) throws SInvalidExpressionException { super.validate(expression); if (!SourceVersion.isIdentifier(expression.getContent()) || SourceVersion.isKeyword(expression.getContent())) { throw new SInvalidExpressionException( expression.getContent() + " is not a valid parameter name in expression : " + expression, expression.getName()); } } @Override public ExpressionKind getExpressionKind() { return KIND_PARAMETER; } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException, SExpressionDependencyMissingException { final List list = new ArrayList(expressions.size()); for (final SExpression expression : expressions) { list.add(evaluate(expression, context, resolvedExpressions, containerState)); } return list; } @Override public boolean mustPutEvaluatedExpressionInContext() { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/expression/QueryBusinessDataExpressionExecutorStrategy.java ================================================ /** * 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.expression; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.NonUniqueResultException; import org.bonitasoft.engine.business.data.proxy.ServerLazyLoader; import org.bonitasoft.engine.business.data.proxy.ServerProxyfier; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class QueryBusinessDataExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy { private final BusinessDataRepository businessDataRepository; public QueryBusinessDataExpressionExecutorStrategy(final BusinessDataRepository businessDataRepository) { this.businessDataRepository = businessDataRepository; } @Override public Object evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final String queryName = expression.getContent(); final String returnType = expression.getReturnType(); final Map parameters = new HashMap<>(); for (final SExpression dependency : expression.getDependencies()) { parameters.put(dependency.getName(), (Serializable) resolvedExpressions.get(dependency.getDiscriminant())); } try { if (isNumber(returnType)) { Class numberClass; try { numberClass = (Class) Class.forName(returnType); } catch (ClassNotFoundException e) { throw new SExpressionEvaluationException(e, expression.getName()); } return businessDataRepository.findByNamedQuery(queryName, numberClass, parameters); } else if (List.class.getName().equals(returnType)) { List entities = businessDataRepository.findListByNamedQuery(queryName, Entity.class, parameters, getStartIndexParameter(expression.getDependencies(), resolvedExpressions, expression.getName(), parameters), getMaxResultParameter(expression.getDependencies(), resolvedExpressions, expression.getName(), parameters)); List e = new ArrayList<>(); for (Entity entity : entities) { ServerProxyfier proxyfier = new ServerProxyfier(new ServerLazyLoader(businessDataRepository)); Entity proxify = proxyfier.proxify(entity); e.add(proxify); } return e; } else { Entity findByNamedQuery = businessDataRepository.findByNamedQuery(queryName, Entity.class, parameters); ServerProxyfier proxyfier = new ServerProxyfier(new ServerLazyLoader(businessDataRepository)); return proxyfier.proxify(findByNamedQuery); } } catch (final NonUniqueResultException nure) { throw new SExpressionEvaluationException(nure, expression.getName()); } } private int getStartIndexParameter(final List dependencies, final Map resolvedExpressions, final String expressionName, final Map parameters) throws SExpressionEvaluationException { for (SExpression dependency : dependencies) { if ("startIndex".equals(dependency.getName())) { parameters.remove(dependency.getName()); return (Integer) resolvedExpressions.get(dependency.getDiscriminant()); } } throw new SExpressionEvaluationException( "Pagination parameter 'startIndex' is mandatory when calling 'find*()' methods returning a List of Business Data", expressionName); } private int getMaxResultParameter(final List dependencies, final Map resolvedExpressions, final String expressionName, final Map parameters) throws SExpressionEvaluationException { for (SExpression dependency : dependencies) { if ("maxResults".equals(dependency.getName())) { parameters.remove(dependency.getName()); return (Integer) resolvedExpressions.get(dependency.getDiscriminant()); } } throw new SExpressionEvaluationException( "Pagination parameter 'maxResults' is mandatory when calling 'find*()' methods returning a List of Business Data", expressionName); } private boolean isNumber(final String returnType) { return Long.class.getName().equals(returnType) || Integer.class.getName().equals(returnType) || Double.class.getName().equals(returnType) || Float.class.getName().equals(returnType); } @Override public ExpressionKind getExpressionKind() { return KIND_QUERY_BUSINESS_DATA; } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final List list = new ArrayList<>(expressions.size()); for (final SExpression expression : expressions) { list.add(evaluate(expression, context, resolvedExpressions, containerState)); } return list; } @Override public boolean mustPutEvaluatedExpressionInContext() { return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/handler/SchedulerServiceRestartHandler.java ================================================ /** * 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.handler; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.PlatformRestartHandler; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.transaction.UserTransactionService; /** * @author Matthieu Chaffotte */ @Slf4j public class SchedulerServiceRestartHandler implements PlatformRestartHandler { private final SchedulerService schedulerService; private UserTransactionService userTransactionService; public SchedulerServiceRestartHandler(SchedulerService schedulerService, UserTransactionService userTransactionService) { super(); this.schedulerService = schedulerService; this.userTransactionService = userTransactionService; } @Override public void execute() { log.info("Rescheduling all scheduler Triggers in ERROR state"); try { userTransactionService.executeInTransaction(() -> { schedulerService.rescheduleErroneousTriggers(); return null; }); } catch (Exception e) { log.warn( "Unable to reschedule all erroneous triggers, call PlatformAPI.rescheduleErroneousTriggers to retry. Cause is {}", e.getMessage()); log.debug("Cause: ", e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/CustomUserInfoDefinitionImporter.java ================================================ /** * Copyright (C) 2016 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.identity; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition; /** * @author Elias Ricken de Medeiros */ public class CustomUserInfoDefinitionImporter { private final IdentityService identityService; private final ImportOrganizationStrategy strategy; public CustomUserInfoDefinitionImporter(IdentityService identityService, final ImportOrganizationStrategy strategy) { this.strategy = strategy; this.identityService = identityService; } public Map importCustomUserInfoDefinitions( List userInfoDefinitionCreators) throws SIdentityException, ImportDuplicateInOrganizationException { Map nameToDefinition = new HashMap<>(userInfoDefinitionCreators.size()); for (ExportedCustomUserInfoDefinition creator : userInfoDefinitionCreators) { String name = creator.getName(); SCustomUserInfoDefinition userInfoDef; if (identityService.hasCustomUserInfoDefinition(name)) { userInfoDef = identityService.getCustomUserInfoDefinitionByName(name); strategy.foundExistingCustomUserInfoDefinition(userInfoDef, creator); } else { userInfoDef = addCustomUserInfoDefinition(creator); } nameToDefinition.put(name, userInfoDef); } return nameToDefinition; } private SCustomUserInfoDefinition addCustomUserInfoDefinition(ExportedCustomUserInfoDefinition creator) throws SCustomUserInfoDefinitionAlreadyExistsException, SCustomUserInfoDefinitionCreationException { final SCustomUserInfoDefinition.SCustomUserInfoDefinitionBuilder userInfoBuilder = SCustomUserInfoDefinition .builder(); userInfoBuilder.name(creator.getName()); userInfoBuilder.description(creator.getDescription()); return identityService.createCustomUserInfoDefinition(userInfoBuilder.build()); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/CustomUserInfoValueImporter.java ================================================ /** * Copyright (C) 2016 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.identity; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.impl.SCustomUserInfoValueAPI; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoValue; /** * @author Elias Ricken de Medeiros */ public class CustomUserInfoValueImporter { private final SCustomUserInfoValueAPI userInfoAPI; private final Map customUserInfoDefinitions; public CustomUserInfoValueImporter(SCustomUserInfoValueAPI userInfoAPI, Map customUserInfoDefinitions) { this.userInfoAPI = userInfoAPI; this.customUserInfoDefinitions = customUserInfoDefinitions; } public void imporCustomUserInfoValues(List customUserInfoValues, long persistedUserId) throws SBonitaException { for (ExportedCustomUserInfoValue infoValue : customUserInfoValues) { SCustomUserInfoDefinition infoDefinition = customUserInfoDefinitions.get(infoValue.getName()); if (infoDefinition == null) { String message = "The XML file is inconsistent. A custom user info value is refenced with name '" + infoValue.getName() + "', but no custom user info definition is defined with this name."; throw new SImportOrganizationException(message); } userInfoAPI.set(infoDefinition.getId(), persistedUserId, infoValue.getValue()); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/ExportOrganization.java ================================================ /** * Copyright (C) 2016 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.identity; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.bind.JAXBException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.identity.model.SContactInfo; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.model.SCustomUserInfoValue; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition; import org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoValue; import org.bonitasoft.engine.identity.xml.ExportedGroup; import org.bonitasoft.engine.identity.xml.ExportedRole; import org.bonitasoft.engine.identity.xml.ExportedUser; import org.bonitasoft.engine.identity.xml.ExportedUserMembership; import org.bonitasoft.engine.identity.xml.Organization; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros */ public class ExportOrganization implements TransactionContentWithResult { private final IdentityService identityService; private Map userNames; private String xmlOrganization; private final int maxResults; public ExportOrganization(final IdentityService identityService, final int maxResults) { this.identityService = identityService; this.maxResults = maxResults; } @Override public void execute() throws SBonitaException { userNames = new HashMap<>(20); final List customUserInfoDefinitions = getAllCustomUserInfoDefinitions(); final List users = getAllUsers(getUserInfoDefinitionNames(customUserInfoDefinitions)); // improvement: user server object to avoid useless conversion; final List roles = getAllRoles(); final List groups = getAllGroups(); final Map groupIdParentPath = new HashMap<>(groups.size()); for (final SGroup group : groups) { groupIdParentPath.put(group.getId(), group.getParentPath()); } final List userMemberships = getAllUserMemberships(); // improvement: user server object to avoid useless conversion; final List clientGroups = ModelConvertor.toExportedGroups(groups); // improvement: user server object to avoid useless conversion; final List clientUserMemberships = ModelConvertor .toExportedUserMembership(userMemberships, userNames, groupIdParentPath); List customUserInfoDefinitionCreators = ModelConvertor .toExportedCustomUserInfoDefinition(customUserInfoDefinitions); final Organization organization = new Organization(users, roles, clientGroups, clientUserMemberships, customUserInfoDefinitionCreators); try { xmlOrganization = new OrganizationParser().convert(organization); } catch (JAXBException e) { throw new SBonitaReadException(e); } } private static Map getUserInfoDefinitionNames( final List userInfoDefinitions) { final Map names = new HashMap<>(userInfoDefinitions.size()); for (final SCustomUserInfoDefinition userInfoDefinition : userInfoDefinitions) { names.put(userInfoDefinition.getId(), userInfoDefinition.getName()); } return names; } @Override public String getResult() { return xmlOrganization; } protected List getAllCustomUserInfoDefinitions() throws SIdentityException { final List allCustomUserInfoDefinitions = new ArrayList<>(5); List currentPage = null; int startIndex = 0; do { currentPage = identityService.getCustomUserInfoDefinitions(startIndex, maxResults); allCustomUserInfoDefinitions.addAll(currentPage); startIndex += maxResults; } while (currentPage.size() == maxResults); return allCustomUserInfoDefinitions; } List getAllUserMemberships() throws SIdentityException { final long numberOfUserMemberships = identityService.getNumberOfUserMemberships(); final List sUserMemberships = new ArrayList<>(); for (int startIndex = 0; startIndex < numberOfUserMemberships; startIndex = startIndex + maxResults) { OrderByOption orderByOption = new OrderByOption(SUserMembership.class, SUserMembership.ID, OrderByType.ASC); sUserMemberships.addAll(identityService.getUserMemberships(startIndex, maxResults, orderByOption)); } return sUserMemberships; } List getAllGroups() throws SIdentityException { List groups; final long groupNumber = identityService.getNumberOfGroups(); groups = new ArrayList<>(); for (int startIndex = 0; startIndex < groupNumber; startIndex = startIndex + maxResults) { groups.addAll(identityService.getGroups(startIndex, maxResults, "id", OrderByType.ASC)); } return groups; } List getAllRoles() throws SIdentityException { final long roleNumber = identityService.getNumberOfRoles(); final List roles = new ArrayList<>(); for (int startIndex = 0; startIndex < roleNumber; startIndex = startIndex + maxResults) { final List sRoles = identityService.getRoles(startIndex, maxResults, "id", OrderByType.ASC); roles.addAll(ModelConvertor.toExportedRoles(sRoles)); } return roles; } private List getAllUsers(final Map userInfoDefinitionNames) throws SBonitaException { final long userNumber = identityService.getNumberOfUsers(); final List users = new ArrayList<>(); for (int startIndex = 0; startIndex <= userNumber; startIndex = startIndex + maxResults) { users.addAll(getNextUsersPage(startIndex, maxResults, userInfoDefinitionNames)); } return users; } List getNextUsersPage(final int startIndex, final int numberPerPage, final Map userInfoDefinitionNames) throws SBonitaException { final List currentUsersPage = new ArrayList<>(numberPerPage); final List sUsers = identityService.getUsers(startIndex, numberPerPage, "id", OrderByType.ASC); for (final SUser sUser : sUsers) { userNames.put(sUser.getId(), sUser.getUserName()); currentUsersPage.add(toExportedUser(sUser, userInfoDefinitionNames)); } return currentUsersPage; } protected ExportedUser toExportedUser(final SUser sUser, final Map userInfoDefinitionNames) throws SBonitaException { final String managerUserName = getManagerUsername(sUser); final ExportedUserBuilder clientUserbuilder = ExportedUserBuilderFactory.createNewInstance(sUser.getUserName(), sUser.getPassword()); // Do not export dates and id clientUserbuilder.setPasswordEncrypted(true); clientUserbuilder.setFirstName(sUser.getFirstName()); clientUserbuilder.setLastName(sUser.getLastName()); clientUserbuilder.setTitle(sUser.getTitle()); clientUserbuilder.setJobTitle(sUser.getJobTitle()); clientUserbuilder.setEnabled(sUser.isEnabled()); setManagerInfo(managerUserName, clientUserbuilder); setPersonalContactInfo(sUser, clientUserbuilder); setProfessionalContactInfo(sUser, clientUserbuilder); addCustomUserInfoValues(sUser.getId(), clientUserbuilder, userInfoDefinitionNames); return clientUserbuilder.done(); } protected void addCustomUserInfoValues(final long userId, final ExportedUserBuilder clientUserbuilder, final Map userInfoDefinitionNames) throws SBonitaReadException { final List userInfoValues = getAllCustomUserInfoForUser(userId); for (final SCustomUserInfoValue infoValue : userInfoValues) { final String definitionName = userInfoDefinitionNames.get(infoValue.getDefinitionId()); clientUserbuilder .addCustomUserInfoValue(new ExportedCustomUserInfoValue(definitionName, infoValue.getValue())); } } protected List getAllCustomUserInfoForUser(final long userId) throws SBonitaReadException { final List allValues = new ArrayList<>(5); int fromIndex = 0; List currentPage; do { final QueryOptions options = getQueryOptions(userId, fromIndex); currentPage = identityService.searchCustomUserInfoValue(options); allValues.addAll(currentPage); fromIndex += maxResults; } while (currentPage.size() == maxResults); return allValues; } private QueryOptions getQueryOptions(final long userId, final int fromIndex) { final OrderByOption orderByOption = new OrderByOption(SCustomUserInfoValue.class, SCustomUserInfoValue.ID, OrderByType.ASC); final FilterOption filterOption = new FilterOption(SCustomUserInfoValue.class, SCustomUserInfoValue.USER_ID, userId); return new QueryOptions(fromIndex, maxResults, Collections.singletonList(orderByOption), Collections.singletonList(filterOption), null); } private void setPersonalContactInfo(final SUser sUser, final ExportedUserBuilder clientUserbuilder) throws SIdentityException { final SContactInfo persoInfo = identityService.getUserContactInfo(sUser.getId(), true); if (persoInfo != null) { clientUserbuilder.setPersonalData(ModelConvertor.toUserContactData(persoInfo)); } } private void setProfessionalContactInfo(final SUser sUser, final ExportedUserBuilder clientUserbuilder) throws SIdentityException { final SContactInfo proInfo = identityService.getUserContactInfo(sUser.getId(), false); if (proInfo != null) { clientUserbuilder.setProfessionalData(ModelConvertor.toUserContactData(proInfo)); } } private void setManagerInfo(final String managerUserName, final ExportedUserBuilder clientUserbuilder) { clientUserbuilder.setManagerUserName(managerUserName); } private String getManagerUsername(final SUser sUser) throws SUserNotFoundException { final long managerUserId = sUser.getManagerUserId(); String managerUserName = null; if (managerUserId > 0) { managerUserName = userNames.get(managerUserId); if (managerUserName == null) { final SUser manager = identityService.getUser(sUser.getManagerUserId()); userNames.put(manager.getId(), manager.getUserName()); managerUserName = manager.getUserName(); } } return managerUserName; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/ImportDuplicateInOrganizationException.java ================================================ /** * Copyright (C) 2016 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.identity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public class ImportDuplicateInOrganizationException extends SBonitaException { private static final long serialVersionUID = -2325217966573197811L; public ImportDuplicateInOrganizationException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/ImportOrganization.java ================================================ /** * Copyright (C) 2016 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.identity; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.bind.JAXBException; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.engine.api.impl.SCustomUserInfoValueAPI; import org.bonitasoft.engine.api.impl.SessionInfos; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.identity.model.builder.SUserUpdateBuilderFactory; import org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition; import org.bonitasoft.engine.identity.xml.ExportedGroup; import org.bonitasoft.engine.identity.xml.ExportedRole; import org.bonitasoft.engine.identity.xml.ExportedUser; import org.bonitasoft.engine.identity.xml.ExportedUserMembership; import org.bonitasoft.engine.identity.xml.Organization; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Matthieu Chaffotte */ @Slf4j public class ImportOrganization { private static final String LEGACY_NS = "xmlns:organization=\"http://documentation.bonitasoft.com/organization-xml-schema\""; private static final String VERSIONED_NS = "xmlns:organization=\"http://documentation.bonitasoft.com/organization-xml-schema/1.1\""; final IdentityService identityService; private final String organizationContent; private final List warnings; private final ImportOrganizationStrategy strategy; private final SCustomUserInfoValueAPI userInfoValueAPI; public ImportOrganization(IdentityService identityService, final String organizationContent, final ImportPolicy policy, final SCustomUserInfoValueAPI userInfoValueAPI) throws OrganizationImportException { this.userInfoValueAPI = userInfoValueAPI; this.identityService = identityService; this.organizationContent = updateNamespace(organizationContent); warnings = new ArrayList<>(); switch (policy) { case FAIL_ON_DUPLICATES: strategy = new ImportOrganizationFailOnDuplicatesStrategy(); break; case IGNORE_DUPLICATES: strategy = new ImportOrganizationIgnoreDuplicatesStrategy(); break; case MERGE_DUPLICATES: strategy = new ImportOrganizationMergeDuplicatesStrategy(identityService, userInfoValueAPI); break; default: throw new OrganizationImportException("No import strategy found for " + policy); } } private void excludeGroupsWithInvalidCharactersInName(List groups) throws OrganizationImportException { for (ExportedGroup group : groups) { String groupName = group.getName(); if (groupName.contains("/")) { groups.remove(group); warnings.add("The group name " + groupName + " contains the character '/' which is not supported. The group has not been imported"); } } } private String updateNamespace(String content) { if (content != null) { if (content.contains(LEGACY_NS)) { content = content.replace(LEGACY_NS, VERSIONED_NS); } } return content; } public List execute() throws SBonitaException, JAXBException { try { final Organization organization = new OrganizationParser().convert(organizationContent); final List users = organization.getUsers(); final List roles = organization.getRoles(); final List groups = organization.getGroups(); //checking if the Group names contain illegal characters excludeGroupsWithInvalidCharactersInName(groups); final List memberships = organization.getMemberships(); long importDate = System.currentTimeMillis(); // custom user info definitions final Map customUserInfoDefinitions = importCustomUserInfoDefinitions( organization.getCustomUserInfoDefinition()); // Users final Map userNameToSUsers = importUsers(users, customUserInfoDefinitions); updateManagerId(users, userNameToSUsers, importDate); // Roles final Map roleNameToIdMap = importRoles(roles); // Groups final Map groupPathToIdMap = importGroups(groups); // UserMemberships importMemberships(memberships, userNameToSUsers, roleNameToIdMap, groupPathToIdMap); return warnings; } catch (final SBonitaException | JAXBException e) { throw e; } catch (final Exception e) { throw new SImportOrganizationException(e); } } private Map importCustomUserInfoDefinitions( final List customUserInfoDefinitionCreators) throws SIdentityException, ImportDuplicateInOrganizationException { final CustomUserInfoDefinitionImporter importer = new CustomUserInfoDefinitionImporter(identityService, strategy); return importer.importCustomUserInfoDefinitions(customUserInfoDefinitionCreators); } private void importMemberships(final List memberships, final Map userNameToSUsers, final Map roleNameToIdMap, final Map groupPathToIdMap) throws SIdentityException, ImportDuplicateInOrganizationException { for (final ExportedUserMembership newMembership : memberships) { final Long userId = getUserId(userNameToSUsers, newMembership); final Long groupId = getGroupId(groupPathToIdMap, newMembership); final Long roleId = getRoleId(roleNameToIdMap, newMembership); if (userId != null && groupId != null && roleId != null) { try { final SUserMembership sUserMembership = identityService.getUserMembership(userId, groupId, roleId); strategy.foundExistingMembership(sUserMembership); } catch (final SIdentityException e) { final Long assignedBy = getAssignedBy(userNameToSUsers, newMembership); addMembership(newMembership, userId, groupId, roleId, assignedBy); } } else { log.warn("The membership " + newMembership + " could not be imported because the user, group or role can't be found\n userId=" + userId + " groupId=" + groupId + " roleId=" + roleId); } } } private Long getUserId(final Map userNameToSUsers, final ExportedUserMembership newMembership) { final String username = newMembership.getUserName(); if (username == null || username.isEmpty()) { return -1L; } final SUser sUser = userNameToSUsers.get(username); if (sUser != null) { return sUser.getId(); } return null; } private Long getGroupId(final Map groupPathToIdMap, final ExportedUserMembership newMembership) { final String groupParentPath = newMembership.getGroupParentPath(); final String groupFullPath = (groupParentPath == null ? '/' : groupParentPath + '/') + newMembership.getGroupName(); return groupPathToIdMap.get(groupFullPath); } private Long getRoleId(final Map roleNameToIdMap, final ExportedUserMembership newMembership) { final String roleName = newMembership.getRoleName(); if (roleName == null || roleName.isEmpty()) { return -1L; } final Long roleId = roleNameToIdMap.get(roleName); if (roleId != null) { return roleId; } return null; } private Long getAssignedBy(final Map userNameToSUsers, final ExportedUserMembership newMembership) { final String assignedByName = newMembership.getAssignedBy(); if (assignedByName == null || assignedByName.isEmpty()) { return -1L; } final SUser sUserAssigned = userNameToSUsers.get(assignedByName); if (sUserAssigned != null) { return sUserAssigned.getId(); } return -1L; } private Map importGroups(final List groupCreators) throws ImportDuplicateInOrganizationException, SIdentityException { final Map groupPathToIdMap = new HashMap<>(groupCreators.size()); for (final ExportedGroup groupCreator : groupCreators) { SGroup sGroup; try { final String groupPath = getGroupPath(groupCreator); sGroup = identityService.getGroupByPath(groupPath); strategy.foundExistingGroup(sGroup, groupCreator); } catch (final SGroupNotFoundException e) { sGroup = addGroup(groupCreator); } groupPathToIdMap.put(sGroup.getPath(), sGroup.getId()); } return groupPathToIdMap; } private String getGroupPath(final ExportedGroup exportedGroup) { final String name = exportedGroup.getName(); final String parentPath = exportedGroup.getParentPath(); if (parentPath == null) { return "/" + name; } return parentPath + "/" + name; } private Map importRoles(final List roleCreators) throws ImportDuplicateInOrganizationException, SIdentityException { final Map roleNameToIdMap = new HashMap<>(roleCreators.size()); for (final ExportedRole roleCreator : roleCreators) { SRole sRole; try { sRole = identityService.getRoleByName(roleCreator.getName()); strategy.foundExistingRole(sRole, roleCreator); } catch (final SRoleNotFoundException e) { sRole = addRole(roleCreator); } roleNameToIdMap.put(sRole.getName(), sRole.getId()); } return roleNameToIdMap; } private void updateManagerId(final List users, final Map userNameToSUsers, long importDate) throws SUserUpdateException { for (final ExportedUser user : users) { //Skip the update of the manager if user was already present before the import // and the strategy says to skip it in that case if (strategy.shouldSkipUpdateManagerOfExistingUser() && wasUserAlreadyPresent(userNameToSUsers, importDate, user)) { continue; } if (StringUtils.isNotBlank(user.getManagerUserName())) { final String managerUserName = user.getManagerUserName().trim(); SUser manager = userNameToSUsers.get(managerUserName); if (manager != null) { identityService.updateUser(userNameToSUsers.get(user.getUserName()), BuilderFactory.get(SUserUpdateBuilderFactory.class).createNewInstance() .updateManagerUserId(manager.getId()).done()); } else { log.warn("The user " + user.getUserName() + " has a manager with username " + managerUserName + ", but this one does not exist. Please set it manually."); } } } } private boolean wasUserAlreadyPresent(Map userNameToSUsers, long importDate, ExportedUser user) { return userNameToSUsers.get(user.getUserName()).getCreationDate() < importDate; } private Map importUsers(final List users, final Map customUserInfoDefinitions) throws SBonitaException { final CustomUserInfoValueImporter userInfoValueImporter = new CustomUserInfoValueImporter(userInfoValueAPI, customUserInfoDefinitions); final UserImporter userImporter = new UserImporter(identityService, strategy, SessionInfos.getUserIdFromSession(), userInfoValueImporter); return userImporter.importUsers(users); } private void addMembership(final ExportedUserMembership newMembership, final Long userId, final Long groupId, final Long roleId, final Long assignedBy) throws SUserMembershipCreationException { final long assignedDateAsLong = getAssignedDate(newMembership); final SUserMembership sUserMembership = SUserMembership.builder().userId(userId).groupId(groupId).roleId(roleId) .assignedBy(assignedBy).assignedDate(assignedDateAsLong).build(); identityService.createUserMembership(sUserMembership); } private long getAssignedDate(final ExportedUserMembership newMembership) { final Long assignedDate = newMembership.getAssignedDate(); if (assignedDate != null) { return assignedDate; } return 0; } private SGroup addGroup(final ExportedGroup exportedGroup) throws SGroupCreationException { final SGroup sGroup = ModelConvertor.constructSGroup(exportedGroup); identityService.createGroup(sGroup, null, null); return sGroup; } private SRole addRole(final ExportedRole exportedRole) throws SIdentityException { final SRole sRole = ModelConvertor.constructSRole(exportedRole); identityService.createRole(sRole, null, null); return sRole; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/ImportOrganizationFailOnDuplicatesStrategy.java ================================================ /** * Copyright (C) 2016 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.identity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition; import org.bonitasoft.engine.identity.xml.ExportedGroup; import org.bonitasoft.engine.identity.xml.ExportedRole; import org.bonitasoft.engine.identity.xml.ExportedUser; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class ImportOrganizationFailOnDuplicatesStrategy implements ImportOrganizationStrategy { public ImportOrganizationFailOnDuplicatesStrategy() { } @Override public void foundExistingGroup(final SGroup existingGroup, final ExportedGroup newGroup) throws ImportDuplicateInOrganizationException { throw new ImportDuplicateInOrganizationException( "There's already a group with the path : " + existingGroup.getPath()); } @Override public void foundExistingUser(final SUser existingUser, final ExportedUser user) throws SBonitaException { throw new ImportDuplicateInOrganizationException( "There's already a user with the name : " + existingUser.getUserName()); } @Override public void foundExistingRole(final SRole existingRole, final ExportedRole newRole) throws ImportDuplicateInOrganizationException { throw new ImportDuplicateInOrganizationException( "There's already a role with the name : " + existingRole.getName()); } @Override public void foundExistingMembership(final SUserMembership existingMembership) throws ImportDuplicateInOrganizationException { throw new ImportDuplicateInOrganizationException( "There's already a user membership with the name : " + existingMembership.getUsername() + ", the role : " + existingMembership.getRoleName() + "and the group : " + existingMembership.getGroupName()); } @Override public void foundExistingCustomUserInfoDefinition(SCustomUserInfoDefinition existingUserInfoDefinition, ExportedCustomUserInfoDefinition newUserInfoDefinition) throws ImportDuplicateInOrganizationException { throw new ImportDuplicateInOrganizationException( "There's already a custom user info definition with the name : '" + newUserInfoDefinition.getName() + "'"); } @Override public boolean shouldSkipUpdateManagerOfExistingUser() { return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/ImportOrganizationIgnoreDuplicatesStrategy.java ================================================ /** * Copyright (C) 2016 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.identity; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition; import org.bonitasoft.engine.identity.xml.ExportedGroup; import org.bonitasoft.engine.identity.xml.ExportedRole; import org.bonitasoft.engine.identity.xml.ExportedUser; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class ImportOrganizationIgnoreDuplicatesStrategy implements ImportOrganizationStrategy { public ImportOrganizationIgnoreDuplicatesStrategy() { } @Override public void foundExistingGroup(final SGroup existingGroup, final ExportedGroup newGroup) { } @Override public void foundExistingUser(final SUser existingUser, final ExportedUser user) { } @Override public void foundExistingRole(final SRole existingRole, final ExportedRole newRole) { } @Override public void foundExistingMembership(final SUserMembership existingMembership) { } @Override public void foundExistingCustomUserInfoDefinition(final SCustomUserInfoDefinition existingUserInfoDefinition, final ExportedCustomUserInfoDefinition newUserInfoDefinition) { } @Override public boolean shouldSkipUpdateManagerOfExistingUser() { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/ImportOrganizationMergeDuplicatesStrategy.java ================================================ /** * Copyright (C) 2016 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.identity; import org.bonitasoft.engine.api.impl.SCustomUserInfoValueAPI; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.identity.model.SContactInfo; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.identity.model.builder.SContactInfoUpdateBuilder; import org.bonitasoft.engine.identity.model.builder.SContactInfoUpdateBuilderFactory; import org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilder; import org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilderFactory; import org.bonitasoft.engine.identity.model.builder.SGroupUpdateBuilder; import org.bonitasoft.engine.identity.model.builder.SGroupUpdateBuilderFactory; import org.bonitasoft.engine.identity.model.builder.SRoleUpdateBuilder; import org.bonitasoft.engine.identity.model.builder.SRoleUpdateBuilderFactory; import org.bonitasoft.engine.identity.model.builder.SUserUpdateBuilder; import org.bonitasoft.engine.identity.model.builder.SUserUpdateBuilderFactory; import org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition; import org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoValue; import org.bonitasoft.engine.identity.xml.ExportedGroup; import org.bonitasoft.engine.identity.xml.ExportedRole; import org.bonitasoft.engine.identity.xml.ExportedUser; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class ImportOrganizationMergeDuplicatesStrategy implements ImportOrganizationStrategy { private final IdentityService identityService; private final SCustomUserInfoValueAPI userInfoValueAPI; public ImportOrganizationMergeDuplicatesStrategy(final IdentityService identityService, final SCustomUserInfoValueAPI userInfoValueAPI) { this.identityService = identityService; this.userInfoValueAPI = userInfoValueAPI; } @Override public void foundExistingGroup(final SGroup existingGroup, final ExportedGroup newGroup) throws SIdentityException { final EntityUpdateDescriptor descriptor = getGroupDescriptor(existingGroup, newGroup); if (!descriptor.getFields().isEmpty()) { identityService.updateGroup(existingGroup, descriptor, null); } } @Override public void foundExistingUser(final SUser existingUser, final ExportedUser userToImport) throws SBonitaException { final long existingUserId = existingUser.getId(); final EntityUpdateDescriptor descriptor = getUserDescriptor(userToImport); identityService.updateUser(existingUser, descriptor, userToImport.isPasswordEncrypted()); createOrUpdatePersonalContactInfo(userToImport, existingUserId); createOrUpdateProfessionalContactInfo(userToImport, existingUserId); updateCustomUserInfoValues(userToImport, existingUserId); } private void updateCustomUserInfoValues(final ExportedUser userToImport, final long existingUserId) throws SBonitaException { for (final ExportedCustomUserInfoValue infoValue : userToImport.getCustomUserInfoValues()) { final SCustomUserInfoDefinition customUserInfoDefinition = identityService .getCustomUserInfoDefinitionByName(infoValue.getName()); userInfoValueAPI.set(customUserInfoDefinition.getId(), existingUserId, infoValue.getValue()); } } private void createOrUpdateProfessionalContactInfo(final ExportedUser user, final long userId) throws SIdentityException, SUserCreationException { SContactInfo professContactInfo = identityService.getUserContactInfo(userId, false); if (professContactInfo == null) { professContactInfo = SContactInfo.builder().userId(userId).personal(false).build(); identityService.createUserContactInfo(professContactInfo); } final EntityUpdateDescriptor professionalDataDesc = getUserContactInfoDescriptor(user, false); identityService.updateUserContactInfo(professContactInfo, professionalDataDesc); } private void createOrUpdatePersonalContactInfo(final ExportedUser user, final long userId) throws SIdentityException, SUserCreationException { SContactInfo persoContactInfo = identityService.getUserContactInfo(userId, true); if (persoContactInfo == null) { persoContactInfo = SContactInfo.builder().userId(userId).personal(true).build(); identityService.createUserContactInfo(persoContactInfo); } final EntityUpdateDescriptor personalDataDesc = getUserContactInfoDescriptor(user, true); identityService.updateUserContactInfo(persoContactInfo, personalDataDesc); } @Override public void foundExistingRole(final SRole existingRole, final ExportedRole newRole) throws SIdentityException { final EntityUpdateDescriptor descriptor = getRoleDescriptor(existingRole, newRole); if (!descriptor.getFields().isEmpty()) { identityService.updateRole(existingRole, descriptor, null); } } @Override public void foundExistingMembership(final SUserMembership existingMembership) { } protected EntityUpdateDescriptor getRoleDescriptor(final SRole existingRole, final ExportedRole exportedRole) { final SRoleUpdateBuilder roleUpdateBuilder = BuilderFactory.get(SRoleUpdateBuilderFactory.class) .createNewInstance(); final String name = exportedRole.getName(); if (name != null && !name.equals(existingRole.getName())) { roleUpdateBuilder.updateName(name); } final String description = exportedRole.getDescription(); if (description != null && !description.equals(existingRole.getDescription())) { roleUpdateBuilder.updateDescription(description); } final String displayName = exportedRole.getDisplayName(); if (displayName != null && !displayName.equals(existingRole.getDisplayName())) { roleUpdateBuilder.updateDisplayName(displayName); } roleUpdateBuilder.updateLastUpdate(System.currentTimeMillis()); return roleUpdateBuilder.done(); } protected EntityUpdateDescriptor getGroupDescriptor(final SGroup existingGroup, final ExportedGroup exportedGroup) { final SGroupUpdateBuilder groupUpdateBuilder = BuilderFactory.get(SGroupUpdateBuilderFactory.class) .createNewInstance(); final String name = exportedGroup.getName(); if (name != null && !name.equals(existingGroup.getName())) { groupUpdateBuilder.updateName(name); } final String parentPath = exportedGroup.getParentPath(); if (parentPath != null && !parentPath.equals(existingGroup.getParentPath())) { groupUpdateBuilder.updateName(parentPath); } final String description = exportedGroup.getDescription(); if (description != null && !description.equals(existingGroup.getDescription())) { groupUpdateBuilder.updateDescription(description); } final String displayName = exportedGroup.getDisplayName(); if (displayName != null && !displayName.equals(existingGroup.getDisplayName())) { groupUpdateBuilder.updateDisplayName(displayName); } groupUpdateBuilder.updateLastUpdate(System.currentTimeMillis()); return groupUpdateBuilder.done(); } protected EntityUpdateDescriptor getUserContactInfoDescriptor(final ExportedUser user, final boolean isPersonal) { final SContactInfoUpdateBuilder updateBuilder = BuilderFactory.get(SContactInfoUpdateBuilderFactory.class) .createNewInstance(); if (isPersonal) { updateBuilder.updateAddress(user.getPersonalAddress()); updateBuilder.updateBuilding(user.getPersonalBuilding()); updateBuilder.updateCity(user.getPersonalCity()); updateBuilder.updateCountry(user.getPersonalCountry()); updateBuilder.updateEmail(user.getPersonalEmail()); updateBuilder.updateFaxNumber(user.getPersonalFaxNumber()); updateBuilder.updateMobileNumber(user.getPersonalMobileNumber()); updateBuilder.updatePhoneNumber(user.getPersonalPhoneNumber()); updateBuilder.updateRoom(user.getPersonalRoom()); updateBuilder.updateState(user.getPersonalState()); updateBuilder.updateWebsite(user.getPersonalWebsite()); updateBuilder.updateZipCode(user.getPersonalZipCode()); } else { updateBuilder.updateAddress(user.getProfessionalAddress()); updateBuilder.updateBuilding(user.getProfessionalBuilding()); updateBuilder.updateCity(user.getProfessionalCity()); updateBuilder.updateCountry(user.getProfessionalCountry()); updateBuilder.updateEmail(user.getProfessionalEmail()); updateBuilder.updateFaxNumber(user.getProfessionalFaxNumber()); updateBuilder.updateMobileNumber(user.getProfessionalMobileNumber()); updateBuilder.updatePhoneNumber(user.getProfessionalPhoneNumber()); updateBuilder.updateRoom(user.getProfessionalRoom()); updateBuilder.updateState(user.getProfessionalState()); updateBuilder.updateWebsite(user.getProfessionalWebsite()); updateBuilder.updateZipCode(user.getProfessionalZipCode()); } return updateBuilder.done(); } protected EntityUpdateDescriptor getUserDescriptor(final ExportedUser user) { final SUserUpdateBuilder userUpdateBuilder = BuilderFactory.get(SUserUpdateBuilderFactory.class) .createNewInstance(); userUpdateBuilder.updateFirstName(user.getFirstName()); userUpdateBuilder.updateJobTitle(user.getJobTitle()); userUpdateBuilder.updateLastName(user.getLastName()); userUpdateBuilder.updatePassword(user.getPassword()); userUpdateBuilder.updateTitle(user.getTitle()); userUpdateBuilder.updateUserName(user.getUserName()); userUpdateBuilder.updateEnabled(user.isEnabled()); userUpdateBuilder.updateLastUpdate(System.currentTimeMillis()); return userUpdateBuilder.done(); } @Override public void foundExistingCustomUserInfoDefinition(final SCustomUserInfoDefinition existingUserInfoDefinition, final ExportedCustomUserInfoDefinition newUserInfoDefinition) throws SIdentityException { // only description is updated as it only matches if they have the same name final EntityUpdateDescriptor updateDescriptor = getUpdateDescriptor(newUserInfoDefinition.getDescription()); identityService.updateCustomUserInfoDefinition(existingUserInfoDefinition, updateDescriptor); } @Override public boolean shouldSkipUpdateManagerOfExistingUser() { return false; } private EntityUpdateDescriptor getUpdateDescriptor(final String newDescription) { final SCustomUserInfoDefinitionUpdateBuilder builder = BuilderFactory .get(SCustomUserInfoDefinitionUpdateBuilderFactory.class).createNewInstance(); builder.updateDescription(newDescription); return builder.done(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/ImportOrganizationStrategy.java ================================================ /** * Copyright (C) 2016 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.identity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition; import org.bonitasoft.engine.identity.xml.ExportedGroup; import org.bonitasoft.engine.identity.xml.ExportedRole; import org.bonitasoft.engine.identity.xml.ExportedUser; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros */ public interface ImportOrganizationStrategy { void foundExistingMembership(final SUserMembership existingMembership) throws ImportDuplicateInOrganizationException; void foundExistingRole(final SRole existingRole, final ExportedRole newRole) throws ImportDuplicateInOrganizationException, SIdentityException; void foundExistingUser(final SUser existingUser, final ExportedUser user) throws SBonitaException; void foundExistingGroup(final SGroup existingGroup, final ExportedGroup newGroup) throws ImportDuplicateInOrganizationException, SIdentityException; void foundExistingCustomUserInfoDefinition(SCustomUserInfoDefinition existingUserInfoDefinition, ExportedCustomUserInfoDefinition newUserInfoDefinition) throws ImportDuplicateInOrganizationException, SIdentityException; /** * @return true if users that were already existing should have their manager set */ boolean shouldSkipUpdateManagerOfExistingUser(); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/SImportOrganizationException.java ================================================ /** * Copyright (C) 2016 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.identity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SImportOrganizationException extends SBonitaException { private static final long serialVersionUID = 3972373903843407422L; public SImportOrganizationException(final String message) { super(message); } public SImportOrganizationException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/identity/UserImporter.java ================================================ /** * Copyright (C) 2016 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.identity; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.identity.model.SContactInfo; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.xml.ExportedUser; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Elias Ricken de Medeiros */ public class UserImporter { private final IdentityService identityService; private final ImportOrganizationStrategy strategy; private final long userIdFromSession; private final CustomUserInfoValueImporter infoValueImporter; public UserImporter(IdentityService identityService, final ImportOrganizationStrategy strategy, long userIdFromSession, CustomUserInfoValueImporter infoValueImporter) { this.strategy = strategy; this.userIdFromSession = userIdFromSession; this.infoValueImporter = infoValueImporter; this.identityService = identityService; } public Map importUsers(final List usersToImport) throws SBonitaException { final Map userNameToSUsers = new HashMap<>( (int) Math.min(Integer.MAX_VALUE, identityService.getNumberOfUsers())); for (final ExportedUser userToImport : usersToImport) { SUser sUser; if (hasUserWithUserName(userToImport.getUserName())) { sUser = identityService.getUserByUserName(userToImport.getUserName()); strategy.foundExistingUser(sUser, userToImport); } else { sUser = addAllUserInfo(userToImport); } userNameToSUsers.put(sUser.getUserName(), sUser); } return userNameToSUsers; } private boolean hasUserWithUserName(String userName) throws SBonitaReadException { final FilterOption filter = new FilterOption(SUser.class, SUser.USER_NAME, userName); final QueryOptions queryOptions = new QueryOptions(Collections.singletonList(filter), null); final long numberOfUsers = identityService.getNumberOfUsers(queryOptions); return numberOfUsers > 0; } private SUser addAllUserInfo(final ExportedUser userToImport) throws SBonitaException { final SUser persistedUser = addUser(userToImport); addContactInfo(userToImport, persistedUser); infoValueImporter.imporCustomUserInfoValues(userToImport.getCustomUserInfoValues(), persistedUser.getId()); return persistedUser; } private void addContactInfo(final ExportedUser userToImport, SUser persistedUser) throws SUserCreationException { final SContactInfo persoSContactInfo = ModelConvertor.constructSUserContactInfo(userToImport, true, persistedUser.getId()); identityService.createUserContactInfo(persoSContactInfo); final SContactInfo professSContactInfo = ModelConvertor.constructSUserContactInfo(userToImport, false, persistedUser.getId()); identityService.createUserContactInfo(professSContactInfo); } private SUser addUser(final ExportedUser user) throws SUserCreationException { SUser sUser; if (user.isPasswordEncrypted()) { sUser = identityService.createUserWithoutEncryptingPassword(constructSUser(user)); } else { sUser = identityService.createUser(constructSUser(user)); } return sUser; } private SUser constructSUser(final ExportedUser exportedUser) { final SUser.SUserBuilder userBuilder = SUser.builder(); final long now = System.currentTimeMillis(); userBuilder.creationDate(now); userBuilder.lastUpdate(now); userBuilder.userName(exportedUser.getUserName()); userBuilder.password(exportedUser.getPassword()); userBuilder.firstName(exportedUser.getFirstName()); userBuilder.lastName(exportedUser.getLastName()); userBuilder.jobTitle(exportedUser.getJobTitle()); userBuilder.title(exportedUser.getTitle()); userBuilder.createdBy(userIdFromSession); userBuilder.enabled(exportedUser.isEnabled()); return userBuilder.build(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/jobs/InternalJob.java ================================================ /** * 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.jobs; import org.bonitasoft.engine.scheduler.StatelessJob; import org.bonitasoft.engine.scheduler.exception.SJobConfigurationException; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; /** * @author Baptiste Mesta */ public abstract class InternalJob implements StatelessJob { private static final long serialVersionUID = 5627886991070497312L; protected ServiceAccessor getServiceAccessor() throws SJobConfigurationException { try { return ServiceAccessorSingleton.getInstance(); } catch (final Exception e) { throw new SJobConfigurationException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/jobs/TriggerTimerEventJob.java ================================================ /** * 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.jobs; import java.io.Serializable; import java.util.Map; import java.util.Optional; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SRetryableException; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance; import org.bonitasoft.engine.execution.event.EventsHandler; import org.bonitasoft.engine.scheduler.exception.SJobConfigurationException; import org.bonitasoft.engine.scheduler.exception.SJobExecutionException; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.tenant.TenantServicesManager; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class TriggerTimerEventJob extends InternalJob { private static final long serialVersionUID = 8727861254645155327L; private transient EventsHandler eventsHandler; private Long processDefinitionId; private long targetSFlowNodeDefinitionId; private Long flowNodeInstanceId; private Long parentProcessInstanceId; private Long rootProcessInstanceId; private String containerType; private String eventType; private Boolean isInterrupting; private Long subProcessId; private transient EventInstanceService eventInstanceService; private Long jobDescriptorId; private TenantServicesManager tenantServicesManager; @Override public String getName() { return null; } @Override public String getDescription() { return null; } @Override public void execute() throws SJobExecutionException, SRetryableException { try { if (!tenantServicesManager.isStarted()) { throw new SRetryableException("Unable to execute timer linked with the job " + jobDescriptorId + " because the tenant is not started"); } if (subProcessId == null) { eventsHandler.triggerCatchEvent(eventType, processDefinitionId, targetSFlowNodeDefinitionId, flowNodeInstanceId, containerType); } else { eventsHandler.triggerCatchEvent(SEventTriggerType.TIMER, processDefinitionId, targetSFlowNodeDefinitionId, containerType, subProcessId, parentProcessInstanceId, rootProcessInstanceId, isInterrupting); } if (flowNodeInstanceId != null) { Optional triggerInstance = eventInstanceService .getTimerEventTriggerInstanceOfFlowNode(flowNodeInstanceId); if (triggerInstance.isPresent()) { eventInstanceService.deleteEventTriggerInstance(triggerInstance.get()); } } } catch (final SBonitaException e) { throw new SJobExecutionException(e); } } @Override public void setAttributes(final Map attributes) throws SJobConfigurationException { processDefinitionId = (Long) attributes.get("processDefinitionId"); targetSFlowNodeDefinitionId = (Long) attributes.get("targetSFlowNodeDefinitionId"); flowNodeInstanceId = (Long) attributes.get("flowNodeInstanceId"); containerType = (String) attributes.get("containerType"); eventType = (String) attributes.get("eventType"); subProcessId = (Long) attributes.get("subProcessId"); parentProcessInstanceId = (Long) attributes.get("processInstanceId"); rootProcessInstanceId = (Long) attributes.get("rootProcessInstanceId"); isInterrupting = (Boolean) attributes.get("isInterrupting"); final ServiceAccessor serviceAccessor = getServiceAccessor(); eventsHandler = serviceAccessor.getEventsHandler(); tenantServicesManager = serviceAccessor.getTenantServicesManager(); eventInstanceService = serviceAccessor.getEventInstanceService(); jobDescriptorId = (Long) attributes.get(JOB_DESCRIPTOR_ID); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/log/LogMessageBuilder.java ================================================ /** * 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.log; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.session.model.SSession; /** * @author Elias Ricken de Medeiros */ public class LogMessageBuilder { /** * Build the log message using the flow node's context (display name, id, parent activity id, parent process * instance id, root process instance id, and * process definition id) * * @param flowNodeInstance * @return the message log built using the flow node's context. */ public static String buildFlowNodeContextMessage(final SFlowNodeInstance flowNodeInstance) { StringBuilder stb = new StringBuilder(); stb.append(" [name = <"); stb.append(flowNodeInstance.getName()); stb.append(">, display name = <"); stb.append(flowNodeInstance.getDisplayName()); stb.append(">, id = <"); stb.append(flowNodeInstance.getId()); if (flowNodeInstance.getParentActivityInstanceId() > 0) { stb.append(">, parent activity instance = <"); stb.append(flowNodeInstance.getParentActivityInstanceId()); } stb.append(">, parent process instance = <"); stb.append(flowNodeInstance.getParentProcessInstanceId()); stb.append(">, root process instance = <"); stb.append(flowNodeInstance.getRootProcessInstanceId()); stb.append(">, process definition = <"); stb.append(flowNodeInstance.getProcessDefinitionId()); stb.append(">]"); return stb.toString(); } public static String buildExecuteTaskContextMessage(final SFlowNodeInstance flowNodeInstance, final String username, final long executerUserId, final long executerSubstituteId, Map inputs) { final StringBuilder stb = new StringBuilder(); stb.append("The user <" + username + "> "); if (executerUserId != executerSubstituteId) { stb.append("acting as delegate of the user with id = <" + executerUserId + "> "); } stb.append("has executed the task"); stb.append(LogMessageBuilder.buildFlowNodeContextMessage(flowNodeInstance)); if (inputs != null && !inputs.isEmpty()) { stb.append(" with task inputs: " + inputs); } return stb.toString(); } /** * Build message "The user (acting as delegate of user with id )" * * @param session * @param starterId * @return */ public static String builUserActionPrefix(final SSession session, final long starterId) { final StringBuilder stb = new StringBuilder(); stb.append("The user <"); stb.append(session.getUserName()); stb.append("> "); if (starterId != session.getUserId()) { stb.append("acting as delegate of the user with id = <"); stb.append(starterId); stb.append("> "); } return stb.toString(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/message/MessagesHandlingService.java ================================================ /** * 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.message; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.utils.VisibleForTesting; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.TenantLifecycleService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventReadException; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SMessageInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent; import org.bonitasoft.engine.execution.work.BPMWorkFactory; import org.bonitasoft.engine.lock.BonitaLock; import org.bonitasoft.engine.lock.LockService; import org.bonitasoft.engine.mdc.MDCTransmitingThreadPoolExecutor; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.transaction.BonitaTransactionSynchronization; import org.bonitasoft.engine.transaction.STransactionNotFoundException; import org.bonitasoft.engine.transaction.UserTransactionService; import org.bonitasoft.engine.work.SWorkRegisterException; import org.bonitasoft.engine.work.WorkService; /** * @author Baptiste Mesta */ @Slf4j public class MessagesHandlingService implements TenantLifecycleService { private static final int MAX_COUPLES = 100; private static final String LOCK_TYPE = "EVENTS"; public static final String NUMBER_OF_MESSAGES_EXECUTED = "bonita.bpmengine.message.executed"; public static final String NUMBER_OF_MESSAGES_POTENTIAL_MATCHED = "bonita.bpmengine.message.potential"; public static final String NUMBER_OF_MESSAGES_MATCHING_RETRIGGERED_TASKS = "bonita.bpmengine.message.retriggeredtasks"; private ThreadPoolExecutor threadPoolExecutor; private final EventInstanceService eventInstanceService; private final WorkService workService; private final LockService lockService; private final UserTransactionService userTransactionService; private final BPMWorkFactory workFactory; private final Counter executedMessagesCounter; private final Counter matchedPotentialMessagesCounter; private final Counter retriggeredMatchingTasksCounter; public MessagesHandlingService(EventInstanceService eventInstanceService, WorkService workService, LockService lockService, UserTransactionService userTransactionService, BPMWorkFactory workFactory, MeterRegistry meterRegistry) { this.eventInstanceService = eventInstanceService; this.workService = workService; this.lockService = lockService; this.userTransactionService = userTransactionService; this.workFactory = workFactory; executedMessagesCounter = Counter.builder(NUMBER_OF_MESSAGES_EXECUTED) .baseUnit("messages") .description("BPMN message couples executed") .register(meterRegistry); matchedPotentialMessagesCounter = Counter.builder(NUMBER_OF_MESSAGES_POTENTIAL_MATCHED) .baseUnit("messages") .description("BPMN message couples potentially matched") .register(meterRegistry); retriggeredMatchingTasksCounter = Counter.builder(NUMBER_OF_MESSAGES_MATCHING_RETRIGGERED_TASKS) .baseUnit("messages matching tasks") .description("BPMN message matching tasks retriggered") .register(meterRegistry); } @Override public void start() { log.info("Starting BPMN messages matcher thread"); threadPoolExecutor = new MDCTransmitingThreadPoolExecutor(1, 1, 1L, TimeUnit.HOURS, new ArrayBlockingQueue<>(5), r -> new Thread(r, "Bonita-Message-Matching"), (r, executor) -> log.debug("Message matching queue capacity reached")); log.debug("Thread pool that handle messages matching successfully started"); } @Override public void stop() { log.info("Stopping BPMN messages matcher thread"); if (threadPoolExecutor == null) { log.info("BPMN messages matcher thread is already stopped"); return; } threadPoolExecutor.shutdown(); try { boolean termination = threadPoolExecutor.awaitTermination(5000, TimeUnit.MILLISECONDS); if (!termination) { log.warn("Failed to terminate the BPMN messages matcher thread." + " This will not have functional impacts but it might produce warnings on server shutdown"); } } catch (InterruptedException ignored) { } threadPoolExecutor = null; log.info("BPMN messages matcher thread successfully stopped"); } @Override public void pause() { stop(); } @Override public void resume() { start(); } public void triggerMatchingOfMessages() throws STransactionNotFoundException { if (threadPoolExecutor == null) { log.warn("Cannot match messages when service is stopped. Maybe the engine is not yet started"); return; } userTransactionService.registerBonitaSynchronization(new RegisterMessagesMatchingSynchronization()); } @VisibleForTesting void matchEventCoupleAndTriggerExecution() throws Exception { userTransactionService.executeInTransaction(() -> { final List potentialMessageCouples = eventInstanceService.getMessageEventCouples(0, MAX_COUPLES); final int potentialMessagesCount = potentialMessageCouples.size(); log.info("Found {} potential message/event couples", potentialMessagesCount); matchedPotentialMessagesCounter.increment(potentialMessagesCount); final List uniqueCouples = getMessageUniqueCouples(potentialMessageCouples); if (!uniqueCouples.isEmpty()) { log.info("Triggering execution of unique {} message/event couples", uniqueCouples.size()); executeUniqueMessageCouplesWork(uniqueCouples); log.info("Execution of message/event couples triggered"); } else { log.debug("No message/event couples to be executed"); } if (potentialMessagesCount == MAX_COUPLES) { log.debug("There are more than {} message/event couples to match. " + "Will trigger the execution again now, to match more couples", MAX_COUPLES); triggerMatchingOfMessages(); retriggeredMatchingTasksCounter.increment(); } return null; }); } private void executeUniqueMessageCouplesWork(final List uniqueCouples) throws SBonitaException { for (final SMessageEventCouple couple : uniqueCouples) { executeMessageCouple(couple.getMessageInstanceId(), couple.getWaitingMessageId()); } } @VisibleForTesting void executeMessageCouple(long messageInstanceId, long waitingMessageId) throws SWaitingEventReadException, SMessageInstanceReadException, SMessageModificationException, SWaitingEventModificationException, SWorkRegisterException { log.debug("Registering message/event couple execution: message {} / event {}", messageInstanceId, waitingMessageId); // Mark messages that will be treated as "treatment in progress": final SWaitingMessageEvent waitingMsg = eventInstanceService.getWaitingMessage(waitingMessageId); final SMessageInstance messageInstance = eventInstanceService.getMessageInstance(messageInstanceId); markMessageAsInProgress(messageInstance); // EVENT_SUB_PROCESS of type non-interrupted should be considered as well, as soon as we support them if (!SBPMEventType.START_EVENT.equals(waitingMsg.getEventType())) { markWaitingMessageAsInProgress(waitingMsg); } executedMessagesCounter.increment(); workService.registerWork(workFactory.createExecuteMessageCoupleWorkDescriptor(messageInstance, waitingMsg)); } /** * From a list of couples that may contain duplicate waiting message candidates, select only one waiting message for * each message instance: the first * matching waiting message is arbitrary chosen. * In the case of SWaitingMessageEvent of types {@link SBPMEventType#START_EVENT} or * {@link SBPMEventType#EVENT_SUB_PROCESS}, it can be * selected several times to trigger multiple instances. * * @param potentialMessageCouples all the possible couples that match the potential correlation. * @return the reduced list of couple, where we insure that a unique message instance is associated with a unique * waiting message. */ List getMessageUniqueCouples(List potentialMessageCouples) { final List takenMessages = new ArrayList<>(); final List takenWaitings = new ArrayList<>(); final List uniqueMessageCouples = new ArrayList<>(); for (final SMessageEventCouple couple : potentialMessageCouples) { final long messageInstanceId = couple.getMessageInstanceId(); final long waitingMessageId = couple.getWaitingMessageId(); final SBPMEventType waitingMessageEventType = couple.getWaitingMessageEventType(); final boolean isMessageAlreadyTaken = takenMessages.contains(messageInstanceId); if (!isMessageAlreadyTaken && !takenWaitings.contains(waitingMessageId)) { takenMessages.add(messageInstanceId); // Starting events and Starting event sub-processes must not be considered as taken if they appear several times // EVENT_SUB_PROCESS of type non-interrupted should be considered as well, as soon as we support them if (!SBPMEventType.START_EVENT.equals(waitingMessageEventType)) { takenWaitings.add(waitingMessageId); } uniqueMessageCouples.add(couple); } else if (log.isTraceEnabled()) { log.trace("Ignoring couple: message {} / event {}." + " Duplication cause: message? {} / event? {}", couple.getMessageInstanceId(), couple.getWaitingMessageId(), isMessageAlreadyTaken, takenWaitings.contains(waitingMessageId)); } } return uniqueMessageCouples; } private void markMessageAsInProgress(final SMessageInstance messageInstance) throws SMessageModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(SMessageInstanceBuilder.HANDLED, true); eventInstanceService.updateMessageInstance(messageInstance, descriptor); } private void markWaitingMessageAsInProgress(final SWaitingMessageEvent waitingMsg) throws SWaitingEventModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(BuilderFactory.get(SWaitingMessageEventBuilderFactory.class).getProgressKey(), SWaitingMessageEventBuilderFactory.PROGRESS_IN_TREATMENT_KEY); eventInstanceService.updateWaitingMessage(waitingMsg, descriptor); } public void resetMessageCouple(long messageInstanceId, long waitingMessageId) throws SWaitingEventReadException, SWaitingEventModificationException, SMessageModificationException, SMessageInstanceReadException { resetWaitingMessage(waitingMessageId); resetMessageInstance(messageInstanceId); } private void resetMessageInstance(final long messageInstanceId) throws SMessageModificationException, SMessageInstanceReadException { final SMessageInstance messageInstance = eventInstanceService.getMessageInstance(messageInstanceId); if (messageInstance == null) { log.warn("Unable to reset message instance {} because it is not found", messageInstanceId); return; } final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(SMessageInstanceBuilder.HANDLED, false); eventInstanceService.updateMessageInstance(messageInstance, descriptor); } private void resetWaitingMessage(final long waitingMessageId) throws SWaitingEventModificationException, SWaitingEventReadException { final SWaitingMessageEvent waitingMsg = eventInstanceService.getWaitingMessage(waitingMessageId); if (waitingMsg == null) { log.warn("Unable to reset waiting event because it is not found: {}", waitingMessageId); return; } final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(BuilderFactory.get(SWaitingMessageEventBuilderFactory.class).getProgressKey(), SWaitingMessageEventBuilderFactory.PROGRESS_FREE_KEY); eventInstanceService.updateWaitingMessage(waitingMsg, descriptor); } private class MessagesMatchingTask implements Callable { @Override public Void call() throws Exception { try { log.debug("Starting messages matching"); // we use a lock in order to have only one execution at a time even in cluster BonitaLock eventLock = lockService.tryLock(1L, LOCK_TYPE, 1L, TimeUnit.MILLISECONDS); if (eventLock == null) { // It could happen that some messages were still not triggered because the work that is currently // executing was started after the last message execution log.debug( "The task that matches BPMN messages is already running, this execution will be ignored"); return null; } try { matchEventCoupleAndTriggerExecution(); } finally { lockService.unlock(eventLock); } log.debug("Messages matching completed"); } catch (Exception e) { log.error("Error while matching messages", e); throw e; } return null; } } private class RegisterMessagesMatchingSynchronization implements BonitaTransactionSynchronization { @Override public void afterCompletion(final int txState) { threadPoolExecutor.submit(new MessagesMatchingTask()); log.debug("Messages matching task registered"); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/AbstractDocumentLeftOperandHandler.java ================================================ /** * 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.operation; import java.util.List; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.LeftOperandHandler; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; 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.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; /** * @author Baptiste Mesta */ public abstract class AbstractDocumentLeftOperandHandler implements LeftOperandHandler { private final ActivityInstanceService activityInstanceService; private final SessionAccessor sessionAccessor; private final SessionService sessionService; public AbstractDocumentLeftOperandHandler(final ActivityInstanceService activityInstanceService, final SessionAccessor sessionAccessor, final SessionService sessionService) { this.activityInstanceService = activityInstanceService; this.sessionAccessor = sessionAccessor; this.sessionService = sessionService; } protected long getProcessInstanceId(final long containerId, final String containerType) throws SFlowNodeNotFoundException, SFlowNodeReadException { long processInstanceId; if (DataInstanceContainer.PROCESS_INSTANCE.name().equals(containerType)) { processInstanceId = containerId; } else { final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(containerId); processInstanceId = flowNodeInstance.getParentProcessInstanceId(); } return processInstanceId; } protected long getAuthorId(long containerId, String containerType) { long loggedUserFromSession = sessionService.getLoggedUserFromSession(sessionAccessor); try { if (loggedUserFromSession <= 0 && DataInstanceContainer.ACTIVITY_INSTANCE.name().equals(containerType)) { SActivityInstance activityInstance = activityInstanceService.getActivityInstance(containerId); if (activityInstance instanceof SHumanTaskInstance instance) { return instance.getAssigneeId(); } } } catch (SActivityInstanceNotFoundException | SActivityReadException ignored) { } return loggedUserFromSession; } @Override public void loadLeftOperandInContext(final SLeftOperand sLeftOperand, final long leftOperandContainerId, final String leftOperandContainerType, final SExpressionContext expressionContext) { //do nothing } @Override public void loadLeftOperandInContext(final List sLeftOperand, final long leftOperandContainerId, final String leftOperandContainerType, final SExpressionContext expressionContext) throws SBonitaReadException { //do nothing } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/BusinessDataAssignmentStrategy.java ================================================ /** * 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.operation; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.OperationExecutorStrategy; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.SOperatorType; import org.springframework.stereotype.Component; /** * @author Elias Ricken de Medeiros */ @Component public class BusinessDataAssignmentStrategy implements OperationExecutorStrategy { private final EntitiesActionsExecutor actionsExecutor; private final MergeEntityAction mergeEntityAction; public BusinessDataAssignmentStrategy(EntitiesActionsExecutor actionsExecutor, MergeEntityAction mergeEntityAction) { this.actionsExecutor = actionsExecutor; this.mergeEntityAction = mergeEntityAction; } @Override public Object computeNewValueForLeftOperand(final SOperation operation, final Object value, final SExpressionContext expressionContext, final boolean shouldPersistValue) throws SOperationExecutionException { if (!shouldPersistValue) { return value; } try { return actionsExecutor.executeAction(value, null, mergeEntityAction); } catch (SEntityActionExecutionException e) { throw new SOperationExecutionException(e); } } @Override public String getOperationType() { return SOperatorType.ASSIGNMENT.name() + "_" + SLeftOperand.TYPE_BUSINESS_DATA; } @Override public boolean shouldPersistOnNullValue() { return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/BusinessDataContext.java ================================================ /** * 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.operation; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.bonitasoft.engine.commons.Container; /** * Represents a Evaluation Context to reference / retrieve Business Data. * * @author Elias Ricken de Medeiros */ public class BusinessDataContext { /** * name of the business data to retrieve */ private String name; /** * Container on which to look for the business data (PROCESS of FLOWNODE) */ private Container container; public BusinessDataContext(final String name, final Container container) { this.name = name; this.container = container; } public String getName() { return name; } public Container getContainer() { return container; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BusinessDataContext that = (BusinessDataContext) o; return new EqualsBuilder() .append(name, that.name) .append(container, that.container) .isEquals(); } @Override public int hashCode() { return new HashCodeBuilder(17, 37) .append(name) .append(container) .toHashCode(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/BusinessDataJavaMethodOperationExecutorStrategy.java ================================================ /** * 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.operation; import org.bonitasoft.engine.business.data.BusinessDataService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.impl.JavaMethodOperationExecutorStrategy; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.springframework.stereotype.Component; /** * @author Laurent Leseigneur * @author Matthieu Chaffotte */ @Component public class BusinessDataJavaMethodOperationExecutorStrategy extends JavaMethodOperationExecutorStrategy { private final BusinessDataService businessDataService; private final EntitiesActionsExecutor entitiesActionsExecutor; private final MergeEntityAction mergeEntityAction; public BusinessDataJavaMethodOperationExecutorStrategy(final BusinessDataService businessDataService, final EntitiesActionsExecutor entitiesActionsExecutor, final MergeEntityAction mergeEntityAction) { this.businessDataService = businessDataService; this.entitiesActionsExecutor = entitiesActionsExecutor; this.mergeEntityAction = mergeEntityAction; } @Override public Object computeNewValueForLeftOperand(final SOperation operation, final Object valueToSetObjectWith, final SExpressionContext expressionContext, final boolean shouldPersistValue) throws SOperationExecutionException { if (isBusinessData(operation)) { return delegateBusinessValueForLeftOperand(operation, valueToSetObjectWith, expressionContext, shouldPersistValue); } return computeJavaOperation(operation, valueToSetObjectWith, expressionContext, shouldPersistValue); } protected Object computeJavaOperation(final SOperation operation, final Object valueToSetObjectWith, final SExpressionContext expressionContext, final boolean shouldPersistValue) throws SOperationExecutionException { return super.computeNewValueForLeftOperand(operation, valueToSetObjectWith, expressionContext, shouldPersistValue); } private Object delegateBusinessValueForLeftOperand(final SOperation operation, final Object valueToSetObjectWith, final SExpressionContext expressionContext, final boolean shouldPersistValue) throws SOperationExecutionException { final Object businessObject = extractObjectToInvokeFromContext(operation, expressionContext); final String methodName = extractMethodName(operation); final String parameterType = extractParameterType(operation); try { Object newValue = businessDataService.callJavaOperation(businessObject, valueToSetObjectWith, methodName, parameterType); if (shouldPersistValue) { newValue = entitiesActionsExecutor.executeAction(newValue, null, mergeEntityAction); } return newValue; } catch (final SBonitaException e) { throw new SOperationExecutionException(e); } } private boolean isBusinessData(final SOperation operation) { return SLeftOperand.TYPE_BUSINESS_DATA.equals(operation.getLeftOperand().getType()); } @Override public boolean shouldPersistOnNullValue() { // should persist because the null value will be used to set an object field, but the object itself is not null return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/BusinessDataLeftOperandHandler.java ================================================ /** * 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.operation; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.RefBusinessDataRetriever; import org.bonitasoft.engine.business.data.SBusinessDataNotFoundException; import org.bonitasoft.engine.commons.Container; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.LeftOperandHandler; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceModificationException; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Component public class BusinessDataLeftOperandHandler implements LeftOperandHandler { protected final RefBusinessDataService refBusinessDataService; private final EntitiesActionsExecutor entitiesActionsExecutor; private final UpdateDataRefAction updateDataRefAction; private final RefBusinessDataRetriever refBusinessDataRetriever; private final BusinessDataRepository businessDataRepository; protected BusinessDataLeftOperandHandler(final BusinessDataRepository businessDataRepository, final RefBusinessDataService refBusinessDataService, RefBusinessDataRetriever refBusinessDataRetriever, EntitiesActionsExecutor entitiesActionsExecutor, UpdateDataRefAction updateDataRefAction) { super(); this.businessDataRepository = businessDataRepository; this.refBusinessDataService = refBusinessDataService; this.entitiesActionsExecutor = entitiesActionsExecutor; this.updateDataRefAction = updateDataRefAction; this.refBusinessDataRetriever = refBusinessDataRetriever; } @Override public String getType() { return SLeftOperand.TYPE_BUSINESS_DATA; } @Override public Object update(final SLeftOperand sLeftOperand, Map inputValues, final Object newValue, final long containerId, final String containerType) throws SOperationExecutionException { try { return entitiesActionsExecutor.executeAction(newValue, new BusinessDataContext(sLeftOperand.getName(), new Container(containerId, containerType)), updateDataRefAction); } catch (SEntityActionExecutionException e) { throw new SOperationExecutionException(e); } } @SuppressWarnings("unchecked") protected Object getBusinessData(final String businessDataName, final long containerId, final String containerType) throws SBonitaReadException { try { final SRefBusinessDataInstance reference = refBusinessDataRetriever .getRefBusinessDataInstance(new BusinessDataContext(businessDataName, new Container(containerId, containerType))); final Class dataClass = (Class) Thread.currentThread().getContextClassLoader() .loadClass(reference.getDataClassName()); if (reference instanceof SSimpleRefBusinessDataInstance simpleRef) { final Long dataId = simpleRef.getDataId(); if (dataId != null) { return businessDataRepository.findById(dataClass, dataId); } return dataClass.newInstance(); } final SProcessMultiRefBusinessDataInstance multiRef = (SProcessMultiRefBusinessDataInstance) reference; final List dataIds = multiRef.getDataIds(); if (!dataIds.isEmpty()) { return businessDataRepository.findByIds(dataClass, dataIds); } return new ArrayList(); } catch (final Exception e) { throw new SBonitaReadException(e); } } @Override public void delete(final SLeftOperand sLeftOperand, final long containerId, final String containerType) throws SOperationExecutionException { try { final SRefBusinessDataInstance refBusinessDataInstance = refBusinessDataRetriever .getRefBusinessDataInstance(new BusinessDataContext(sLeftOperand .getName(), new Container(containerId, containerType))); removeBusinessData(refBusinessDataInstance); dereferenceBusinessData(refBusinessDataInstance); } catch (final Exception e) { throw new SOperationExecutionException(e); } } @SuppressWarnings("unchecked") protected void removeBusinessData(final SRefBusinessDataInstance reference) throws ClassNotFoundException, SBusinessDataNotFoundException { final Class dataClass = (Class) Thread.currentThread().getContextClassLoader() .loadClass(reference.getDataClassName()); if (reference instanceof SSimpleRefBusinessDataInstance simpleRef) { final Entity entity = businessDataRepository.findById(dataClass, simpleRef.getDataId()); businessDataRepository.remove(entity); } else { final SProcessMultiRefBusinessDataInstance multiRef = (SProcessMultiRefBusinessDataInstance) reference; for (final Long dataId : multiRef.getDataIds()) { final Entity entity = businessDataRepository.findById(dataClass, dataId); businessDataRepository.remove(entity); } } } protected void dereferenceBusinessData(final SRefBusinessDataInstance reference) throws SRefBusinessDataInstanceModificationException { if (reference instanceof SSimpleRefBusinessDataInstance) { refBusinessDataService.updateRefBusinessDataInstance((SSimpleRefBusinessDataInstance) reference, null); } else { refBusinessDataService.updateRefBusinessDataInstance((SProcessMultiRefBusinessDataInstance) reference, new ArrayList<>()); } } @Override public void loadLeftOperandInContext(final SLeftOperand sLeftOperand, final long leftOperandContainerId, final String leftOperandContainerType, final SExpressionContext expressionContext) throws SBonitaReadException { final Map inputValues = expressionContext.getInputValues(); final String businessDataName = sLeftOperand.getName(); if (!inputValues.containsKey(businessDataName)) { inputValues.put(businessDataName, getBusinessData(businessDataName, leftOperandContainerId, leftOperandContainerType)); } } @Override public void loadLeftOperandInContext(final List sLeftOperand, final long leftOperandContainerId, final String leftOperandContainerType, final SExpressionContext expressionContext) throws SBonitaReadException { for (SLeftOperand leftOperand : sLeftOperand) { loadLeftOperandInContext(leftOperand, leftOperandContainerId, leftOperandContainerType, expressionContext); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/DataLeftOperandHandler.java ================================================ /** * 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.operation; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.LeftOperandHandler; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.data.instance.api.ParentContainerResolver; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Component public class DataLeftOperandHandler implements LeftOperandHandler { private static final String DATA_INSTANCE = "%DATA_INSTANCE%_"; private final DataInstanceService dataInstanceService; private final ParentContainerResolver parentContainerResolver; public DataLeftOperandHandler(final DataInstanceService dataInstanceService, final ParentContainerResolver parentContainerResolver) { this.dataInstanceService = dataInstanceService; this.parentContainerResolver = parentContainerResolver; } @Override public String getType() { return "DATA"; } @Override public Object update(final SLeftOperand leftOperand, Map inputValues, final Object newValue, final long containerId, final String containerType) throws SOperationExecutionException { updateDataInstance(leftOperand, containerId, containerType, inputValues, newValue); return newValue; } protected void update(final SDataInstance sDataInstance, final Object content) throws SDataInstanceException { final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor(); updateDescriptor.addField(SDataInstance.VALUE, content); dataInstanceService.updateDataInstance(sDataInstance, updateDescriptor); } private void checkReturnType(final Object value, final SDataInstance sDataInstance) throws SOperationExecutionException { if (value != null) { final Object dataValue = sDataInstance.getValue(); /* * if the object is null (data is not initialized) the return type is not checked * but the data instance service should throw an exception */ if (dataValue != null) { final Class dataEffectiveType = dataValue.getClass(); final Class evaluatedReturnedType = value.getClass(); if (!(dataEffectiveType.isAssignableFrom(evaluatedReturnedType) || dataEffectiveType.equals(evaluatedReturnedType))) { throw new SOperationExecutionException( "Incompatible assignment operation type: Left operand " + dataEffectiveType + " is not compatible with right operand " + evaluatedReturnedType + " for expression with name '" + sDataInstance.getName() + "'"); } } } } private void updateDataInstance(final SLeftOperand leftOperand, final long containerId, final String containerType, Map inputValues, final Object expressionResult) throws SOperationExecutionException { final String dataInstanceName = leftOperand.getName(); SDataInstance dataInstance; try { dataInstance = (SDataInstance) inputValues.get(DATA_INSTANCE + dataInstanceName); if (dataInstance == null) { dataInstance = getDataInstance(dataInstanceName, containerId, containerType); } // Specific return type check for Data: checkReturnType(expressionResult, dataInstance); update(dataInstance, expressionResult); } catch (final SDataInstanceException e) { throw new SOperationExecutionException(e); } } @Override public void delete(final SLeftOperand leftOperand, final long containerId, final String containerType) throws SOperationExecutionException { throw new SOperationExecutionException("Deleting a data is not supported"); } @Override public void loadLeftOperandInContext(final SLeftOperand sLeftOperand, final long leftOperandContainerId, final String leftOperandContainerType, final SExpressionContext expressionContext) throws SBonitaReadException { try { putInContext(sLeftOperand, leftOperandContainerId, leftOperandContainerType, expressionContext); } catch (final SDataInstanceException e) { throw new SBonitaReadException("Unable to retrieve the data", e); } } private void putInContext(SLeftOperand sLeftOperand, final long leftOperandContainerId, final String leftOperandContainerType, SExpressionContext expressionContext) throws SDataInstanceException { SDataInstance dataInstance = getDataInstance(sLeftOperand.getName(), leftOperandContainerId, leftOperandContainerType); putDataInContext(expressionContext.getInputValues(), dataInstance); } private void putDataInContext(Map contextToSet, SDataInstance dataInstance) { String name = dataInstance.getName(); contextToSet.put(DATA_INSTANCE + name, dataInstance); if (!contextToSet.containsKey(name)) { contextToSet.put(name, dataInstance.getValue()); } } @Override public void loadLeftOperandInContext(final List sLeftOperand, final long leftOperandContainerId, final String leftOperandContainerType, final SExpressionContext expressionContext) throws SBonitaReadException { try { ArrayList names = new ArrayList<>(sLeftOperand.size()); for (SLeftOperand leftOperand : sLeftOperand) { names.add(leftOperand.getName()); } List dataInstances = dataInstanceService.getDataInstances(names, leftOperandContainerId, leftOperandContainerType, parentContainerResolver); for (SDataInstance dataInstance : dataInstances) { putDataInContext(expressionContext.getInputValues(), dataInstance); } } catch (final SDataInstanceException e) { throw new SBonitaReadException("Unable to retrieve the data", e); } } protected SDataInstance getDataInstance(final String dataInstanceName, final long containerId, final String containerType) throws SDataInstanceException { return dataInstanceService.getDataInstance(dataInstanceName, containerId, containerType, parentContainerResolver); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/DocumentLeftOperandHandler.java ================================================ /** * 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.operation; import java.util.Map; import org.bonitasoft.engine.bpm.document.DocumentValue; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.api.impl.DocumentHelper; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.springframework.stereotype.Component; /** * Updates of creates a document of the process. * this operation accepts only {@link DocumentValue} {@link DocumentValue} provides filename, mimetype and content * The document that will be update/created have the name given to the leftOperand (leftOperand.getName()) * If the document already exists on the process instance (document with same name), it is update. * If there is no document with this name, it is created. * * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Component public class DocumentLeftOperandHandler extends AbstractDocumentLeftOperandHandler { private final DocumentHelper documentHelper; final DocumentService documentService; public DocumentLeftOperandHandler(final DocumentService documentService, final ActivityInstanceService activityInstanceService, final SessionAccessor sessionAccessor, final SessionService sessionService) { super(activityInstanceService, sessionAccessor, sessionService); this.documentService = documentService; documentHelper = new DocumentHelper(documentService, null, null); } @Override public Object update(final SLeftOperand sLeftOperand, Map inputValues, final Object newValue, final long containerId, final String containerType) throws SOperationExecutionException { final DocumentValue documentValue = documentHelper.toCheckedDocumentValue(newValue); final String documentName = sLeftOperand.getName(); long processInstanceId; try { processInstanceId = getProcessInstanceId(containerId, containerType); if (newValue == null) { // we just delete the current version documentHelper.deleteDocument(documentName, processInstanceId); } else { if (documentValue.getDocumentId() != null && !documentValue.hasChanged()) { //do not update if the document value say it did not changed return newValue; } documentHelper.createOrUpdateDocument(documentValue, documentName, processInstanceId, getAuthorId(containerId, containerType), null); } return newValue; } catch (final SBonitaException e) { throw new SOperationExecutionException(e); } } @Override public String getType() { return LeftOperand.TYPE_DOCUMENT; } @Override public void delete(final SLeftOperand leftOperand, final long containerId, final String containerType) throws SOperationExecutionException { throw new SOperationExecutionException("Deleting a document is not supported"); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/DocumentListLeftOperandHandler.java ================================================ /** * 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.operation; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.document.DocumentValue; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.document.api.impl.DocumentHelper; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.springframework.stereotype.Component; /** * Handles document lists * this operation accepts only a List of {@link org.bonitasoft.engine.bpm.document.DocumentValue} * * @author Baptiste Mesta */ @Component public class DocumentListLeftOperandHandler extends AbstractDocumentLeftOperandHandler { public final DocumentHelper documentHelper; public DocumentListLeftOperandHandler(final ActivityInstanceService activityInstanceService, final SessionAccessor sessionAccessor, final SessionService sessionService, final DocumentHelper documentHelper) { super(activityInstanceService, sessionAccessor, sessionService); this.documentHelper = documentHelper; } @Override public Object update(final SLeftOperand sLeftOperand, Map inputValues, final Object newValue, final long containerId, final String containerType) throws SOperationExecutionException { final List documentList = documentHelper.toCheckedList(newValue); final String documentName = sLeftOperand.getName(); try { final long processInstanceId = getProcessInstanceId(containerId, containerType); documentHelper.setDocumentList(documentList, documentName, processInstanceId, getAuthorId(containerId, containerType)); return documentList; } catch (final SBonitaException e) { throw new SOperationExecutionException(e.getMessage(), e); } } @Override public String getType() { return LeftOperand.TYPE_DOCUMENT_LIST; } @Override public void delete(final SLeftOperand leftOperand, final long containerId, final String containerType) throws SOperationExecutionException { throw new SOperationExecutionException("Deleting a document is not supported"); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/EntitiesActionsExecutor.java ================================================ /** * 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.operation; import java.util.List; import org.bonitasoft.engine.bdm.Entity; /** * Allows the execution of an action against an Object that is instance of Entity or List * * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public class EntitiesActionsExecutor { /** * Executes of an action against an Object that is instance of Entity or List. * * @param value an Entity or List * @param businessDataContext the business data context * @param action the action to be executed * @return the initial object after executing the action. */ public Object executeAction(final Object value, final BusinessDataContext businessDataContext, final EntityAction action) throws SEntityActionExecutionException { if (value == null) { action.handleNull(businessDataContext); return null; } if (value instanceof Entity) { return action.execute((Entity) value, businessDataContext); } if (value instanceof List) { return action.execute((List) value, businessDataContext); } throw new SEntityActionExecutionException( value.getClass().getName() + " is not a valid type. Expected an Entity or a List"); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/EntityAction.java ================================================ /** * 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.operation; import java.util.List; import org.bonitasoft.engine.bdm.Entity; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface EntityAction { /** * Executes an action against a null entity. * * @param businessDataContext the business data context * @throws SEntityActionExecutionException */ void handleNull(final BusinessDataContext businessDataContext) throws SEntityActionExecutionException; /** * Executes an action against an entity. * * @param entity the entity * @param businessDataContext the business data context * @return the entity after the action execution. */ Entity execute(Entity entity, final BusinessDataContext businessDataContext) throws SEntityActionExecutionException; /** * Executes an action against a a list of entities. * * @param entities the list of entities * @param businessDataContext the business data context * @return the list of entities after the action execution. */ List execute(List entities, final BusinessDataContext businessDataContext) throws SEntityActionExecutionException; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/MergeEntityAction.java ================================================ /** * 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.operation; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.proxy.ServerProxyfier; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public class MergeEntityAction implements EntityAction { BusinessDataRepository repository; public MergeEntityAction(final BusinessDataRepository repository) { this.repository = repository; } @Override public Entity execute(final Entity entity, final BusinessDataContext businessDataContext) throws SEntityActionExecutionException { if (entity == null) { throw new SEntityActionExecutionException("Unable to insert/update a null business object instance"); } try { return repository.merge(ServerProxyfier.unProxifyIfNeeded(entity)); } catch (final IllegalArgumentException iae) { throw new SEntityActionExecutionException(iae); } } @Override public List execute(final List entities, final BusinessDataContext businessDataContext) throws SEntityActionExecutionException { final List mergedEntities = new ArrayList<>(); for (final Entity entity : entities) { if (entity != null) { mergedEntities.add(execute(entity, businessDataContext)); } } return mergedEntities; } @Override public void handleNull(final BusinessDataContext businessDataContext) throws SEntityActionExecutionException { throw new SEntityActionExecutionException("Cannot save a null entity"); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/SEntityActionExecutionException.java ================================================ /** * 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.operation; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SEntityActionExecutionException extends SBonitaException { public SEntityActionExecutionException(final String message) { super(message); } public SEntityActionExecutionException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/StringIndexLeftOperandHandler.java ================================================ /** * Copyright (C) 2020 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.operation; import java.util.List; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.LeftOperandHandler; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Component public class StringIndexLeftOperandHandler implements LeftOperandHandler { private final ProcessInstanceService processInstanceService; private final ActivityInstanceService activityInstanceService; public StringIndexLeftOperandHandler(final ProcessInstanceService processInstanceService, final ActivityInstanceService activityInstanceService) { this.processInstanceService = processInstanceService; this.activityInstanceService = activityInstanceService; } @Override public Object update(final SLeftOperand sLeftOperand, Map inputValues, final Object newValue, final long containerId, final String containerType) throws SOperationExecutionException { final String name = sLeftOperand.getName(); int index; try { index = Integer.parseInt(name); } catch (final NumberFormatException e) { throw new SOperationExecutionException( "name of left operand for search key operation must be 1,2,3,4 or 5 but was " + name); } if (index < 1 || index > 5) { throw new SOperationExecutionException( "name of left operand for search key operation must be 1,2,3,4 or 5 but was " + name); } if (newValue != null && !(newValue instanceof String)) { throw new SOperationExecutionException( "expression of search key operation must return a string, was:" + newValue.getClass().getName()); } try { SProcessInstance processInstance; if (DataInstanceContainer.PROCESS_INSTANCE.name().equals(containerType)) { processInstance = processInstanceService.getProcessInstance(containerId); } else { final SFlowNodeInstance flowNodeInstance = activityInstanceService.getFlowNodeInstance(containerId); processInstance = processInstanceService .getProcessInstance(flowNodeInstance.getParentProcessInstanceId()); } if (processInstance.getCallerType() == SFlowNodeType.SUB_PROCESS) { SFlowNodeInstance subProcessActivity = activityInstanceService .getFlowNodeInstance(processInstance.getCallerId()); processInstance = processInstanceService .getProcessInstance(subProcessActivity.getParentProcessInstanceId()); } processInstanceService.updateProcess(processInstance, new EntityUpdateDescriptor().addField(SProcessInstance.STRING_INDEX_KEY + index, newValue)); return newValue; } catch (final SBonitaException e) { throw new SOperationExecutionException(e); } } @Override public String getType() { return SLeftOperand.TYPE_SEARCH_INDEX; } @Override public void delete(final SLeftOperand leftOperand, final long containerId, final String containerType) throws SOperationExecutionException { throw new SOperationExecutionException("Deleting a search key is not supported"); } @Override public void loadLeftOperandInContext(SLeftOperand sLeftOperand, long leftOperandContainerId, String leftOperandContainerType, SExpressionContext contextToSet) throws SBonitaReadException { //do nothing } @Override public void loadLeftOperandInContext(List sLeftOperandList, long leftOperandContainerId, String leftOperandContainerType, SExpressionContext contextToSet) throws SBonitaReadException { //do nothing } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/TransientDataLeftOperandHandler.java ================================================ /** * 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.operation; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.core.data.instance.TransientDataService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.LeftOperandHandler; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Slf4j @Component public class TransientDataLeftOperandHandler implements LeftOperandHandler { private static final String TRANSIENT_DATA = "%TRANSIENT_DATA%_"; private final TransientDataService transientDataService; public TransientDataLeftOperandHandler(TransientDataService transientDataService) { this.transientDataService = transientDataService; } @Override public String getType() { return LeftOperand.TYPE_TRANSIENT_DATA; } @Override public Object update(final SLeftOperand sLeftOperand, Map inputValues, final Object newValue, final long containerId, final String containerType) throws SOperationExecutionException { SDataInstance dataInstance; try { dataInstance = (SDataInstance) inputValues.get(TRANSIENT_DATA + sLeftOperand.getName()); if (dataInstance == null) { dataInstance = retrieve(sLeftOperand, containerId, containerType); } final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField("value", newValue); log.warn( "The value of the transient data " + sLeftOperand.getName() + " of " + containerId + " " + containerType + " is being updated, be carefull if the application server is restarted this new value will be lost and the data will be reset to its initial value. " + "We advise you to change the design of your process. If you understand the risks and want to hide this warning, change the logging level of this class to error."); transientDataService.updateDataInstance(dataInstance, descriptor); return newValue; } catch (final SDataInstanceException | SBonitaReadException e) { throw new SOperationExecutionException("Unable to update the transient data", e); } } @Override public void delete(final SLeftOperand leftOperand, final long containerId, final String containerType) throws SOperationExecutionException { throw new SOperationExecutionException("Deleting a transient data is not supported"); } @Override public void loadLeftOperandInContext(final SLeftOperand sLeftOperand, final long leftOperandContainerId, final String leftOperandContainerType, final SExpressionContext expressionContext) throws SBonitaReadException { String name = sLeftOperand.getName(); SDataInstance dataInstance = retrieve(sLeftOperand, leftOperandContainerId, leftOperandContainerType); expressionContext.getInputValues().put(TRANSIENT_DATA + name, dataInstance); if (!expressionContext.getInputValues().containsKey(name)) { expressionContext.getInputValues().put(name, dataInstance.getValue()); } } private SDataInstance retrieve(final SLeftOperand sLeftOperand, final Long containerId, final String containerType) throws SBonitaReadException { try { return transientDataService.getDataInstance(sLeftOperand.getName(), containerId, containerType); } catch (final SDataInstanceException e) { throw new SBonitaReadException("Unable to read the transient data", e); } } @Override public void loadLeftOperandInContext(final List sLeftOperand, final long leftOperandContainerId, final String leftOperandContainerType, final SExpressionContext expressionContext) throws SBonitaReadException { for (SLeftOperand leftOperand : sLeftOperand) { loadLeftOperandInContext(leftOperand, leftOperandContainerId, leftOperandContainerType, expressionContext); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/operation/UpdateDataRefAction.java ================================================ /** * 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.operation; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.business.data.RefBusinessDataRetriever; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public class UpdateDataRefAction implements EntityAction { private final RefBusinessDataService refBusinessDataService; private final RefBusinessDataRetriever refBusinessDataRetriever; public UpdateDataRefAction(final RefBusinessDataService refBusinessDataService, final RefBusinessDataRetriever refBusinessDataRetriever) { this.refBusinessDataService = refBusinessDataService; this.refBusinessDataRetriever = refBusinessDataRetriever; } @Override public Entity execute(final Entity entity, final BusinessDataContext businessDataContext) throws SEntityActionExecutionException { try { final SRefBusinessDataInstance reference = refBusinessDataRetriever .getRefBusinessDataInstance(businessDataContext); checkThatIsSimpleRef(reference); final SSimpleRefBusinessDataInstance simpleRef = (SSimpleRefBusinessDataInstance) reference; if (!entity.getPersistenceId().equals(simpleRef.getDataId())) { refBusinessDataService.updateRefBusinessDataInstance(simpleRef, entity.getPersistenceId()); } } catch (final SBonitaException e) { throw new SEntityActionExecutionException(e); } return entity; } private void checkThatIsSimpleRef(final SRefBusinessDataInstance reference) throws SEntityActionExecutionException { if (!(reference instanceof SSimpleRefBusinessDataInstance)) { throw new SEntityActionExecutionException("Incompatible types: the business data '" + reference.getName() + "' is marked as multiple, but its new value is not a list. Either mark the business data as simple or use a list as new value.'"); } } @Override public List execute(final List entities, final BusinessDataContext businessDataContext) throws SEntityActionExecutionException { try { final SRefBusinessDataInstance reference = refBusinessDataRetriever .getRefBusinessDataInstance(businessDataContext); checkThatIsMultiRef(reference); final SProcessMultiRefBusinessDataInstance multiRef = (SProcessMultiRefBusinessDataInstance) reference; final ArrayList dataIds = buildDataIdsList(entities); if (!multiRef.getDataIds().containsAll(dataIds) || multiRef.getDataIds().size() != dataIds.size()) { refBusinessDataService.updateRefBusinessDataInstance(multiRef, dataIds); } } catch (final SBonitaException e) { throw new SEntityActionExecutionException(e); } return entities; } private void checkThatIsMultiRef(final SRefBusinessDataInstance reference) throws SEntityActionExecutionException { if (!(reference instanceof SProcessMultiRefBusinessDataInstance)) { throw new SEntityActionExecutionException( "Incompatible types: the business data '" + reference.getName() + "' is not marked as multiple, but its new value is a list. Either mark the business data as multiple or use a single Entity as new value.'"); } } private ArrayList buildDataIdsList(final List entities) throws SEntityActionExecutionException { final ArrayList businessDataIds = new ArrayList<>(entities.size()); for (final Entity entity : entities) { checkNotNull(entity); businessDataIds.add(entity.getPersistenceId()); } return businessDataIds; } private void checkNotNull(final Entity entity) throws SEntityActionExecutionException { if (entity == null) { throw new SEntityActionExecutionException( "The list of entities contains some null elements. Unable to execute action against null entity."); } } @Override public void handleNull(final BusinessDataContext businessDataContext) throws SEntityActionExecutionException { try { final SRefBusinessDataInstance reference = refBusinessDataRetriever .getRefBusinessDataInstance(businessDataContext); if (reference instanceof SSimpleRefBusinessDataInstance simpleReference) { refBusinessDataService.updateRefBusinessDataInstance(simpleReference, null); } else { final SProcessMultiRefBusinessDataInstance multiReference = (SProcessMultiRefBusinessDataInstance) reference; refBusinessDataService.updateRefBusinessDataInstance(multiReference, new ArrayList()); } } catch (final SBonitaException sbe) { throw new SEntityActionExecutionException(sbe); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/AuthorizationRuleWithParameters.java ================================================ /** * 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.page; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.session.SSessionNotFoundException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException; /** * @author Emmanuel Duchastenier */ public abstract class AuthorizationRuleWithParameters { protected Long getLongParameter(Map context, String parameterKey) { final Map queryParameters = (Map) context .get(URLAdapterConstants.QUERY_PARAMETERS); if (queryParameters != null) { String[] idParamValue = queryParameters.get(parameterKey); if (idParamValue != null && idParamValue.length > 0) { return Long.parseLong(idParamValue[0]); } } return null; } protected long getLoggedUserId(SessionAccessor sessionAccessor, SessionService sessionService) throws SExecutionException { try { return sessionService.getSession(sessionAccessor.getSessionId()).getUserId(); } catch (SSessionNotFoundException e) { throw new SExecutionException(e); } catch (SessionIdNotSetException e) { throw new SExecutionException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsActorInitiatorRule.java ================================================ /** * 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.page; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.core.form.FormMappingService; import org.bonitasoft.engine.core.form.SFormMapping; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; /** * @author Anthony Birembaut */ public class IsActorInitiatorRule implements AuthorizationRule { ActorMappingService actorMappingService; SessionAccessor sessionAccessor; SessionService sessionService; FormMappingService formMappingService; public IsActorInitiatorRule(ActorMappingService actorMappingService, SessionAccessor sessionAccessor, SessionService sessionService, FormMappingService formMappingService) { this.actorMappingService = actorMappingService; this.sessionAccessor = sessionAccessor; this.sessionService = sessionService; this.formMappingService = formMappingService; } @Override public boolean isAllowed(final String key, final Map context) throws SExecutionException { try { SFormMapping formMapping = formMappingService.get(key); long processDefinitionId = formMapping.getProcessDefinitionId(); final long userId = sessionService.getSession(sessionAccessor.getSessionId()).getUserId(); return actorMappingService.canUserStartProcessDefinition(userId, processDefinitionId); } catch (final SBonitaException e) { throw new SExecutionException( "Unable to figure out if the logged user is an actor initiator for the process.", e); } } @Override public String getId() { return AuthorizationRuleConstants.IS_ACTOR_INITIATOR; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsAdminRule.java ================================================ /** * 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.page; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.session.SSessionNotFoundException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.session.model.SSession; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException; /** * @author Emmanuel Duchastenier, Anthony Birembaut */ public class IsAdminRule implements AuthorizationRule { SessionAccessor sessionAccessor; SessionService sessionService; protected static final String PROCESS_DEPLOY_PERMISSION = "process_deploy"; public IsAdminRule(SessionAccessor sessionAccessor, SessionService sessionService) { this.sessionAccessor = sessionAccessor; this.sessionService = sessionService; } @Override public boolean isAllowed(final String key, final Map context) throws SExecutionException { try { return getSession().getUserPermissions().contains(PROCESS_DEPLOY_PERMISSION); } catch (SSessionNotFoundException | SessionIdNotSetException e) { throw new SExecutionException(e); } } protected SSession getSession() throws SSessionNotFoundException, SessionIdNotSetException { return sessionService.getSession(sessionAccessor.getSessionId()); } @Override public String getId() { return AuthorizationRuleConstants.IS_ADMIN; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsInvolvedInProcessInstanceRule.java ================================================ /** * 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.page; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.api.impl.TaskInvolvementDelegate; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; /** * @author Emmanuel Duchastenier */ public class IsInvolvedInProcessInstanceRule extends AuthorizationRuleWithParameters implements AuthorizationRule { private SessionAccessor sessionAccessor; private SessionService sessionService; private final TaskInvolvementDelegate taskInvolvementDelegate; public IsInvolvedInProcessInstanceRule(SessionService sessionService, SessionAccessor sessionAccessor, TaskInvolvementDelegate taskInvolvementDelegate) { this.sessionAccessor = sessionAccessor; this.sessionService = sessionService; this.taskInvolvementDelegate = taskInvolvementDelegate; } @Override public boolean isAllowed(final String key, final Map context) throws SExecutionException { long userId = getLoggedUserId(sessionAccessor, sessionService); Long processInstanceId = getLongParameter(context, URLAdapterConstants.ID_QUERY_PARAM); if (processInstanceId == null) { throw new IllegalArgumentException( "Parameter 'id' is mandatory to execute Page Authorization rule 'IsInvolvedInProcessInstanceRule'"); } return hasUserPendingOrAssignedTasks(userId, processInstanceId); } private boolean hasUserPendingOrAssignedTasks(long userId, Long processInstanceId) throws SExecutionException { return taskInvolvementDelegate.hasUserPendingOrAssignedTasks(userId, processInstanceId); } @Override public String getId() { return AuthorizationRuleConstants.IS_INVOLVED_IN_PROCESS_INSTANCE; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsManagerOfUserInvolvedInProcessInstanceRule.java ================================================ /** * Copyright (C) 2017 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.page; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.api.ProcessRuntimeAPI; import org.bonitasoft.engine.api.impl.ProcessInvolvementDelegate; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; /** * This Rule authorizes a user, if this user is the manager of another user involved in the given process instance. * It has the same behavior as {@link ProcessRuntimeAPI#isManagerOfUserInvolvedInProcessInstance(long, long)} * * @author Emmanuel Duchastenier */ public class IsManagerOfUserInvolvedInProcessInstanceRule extends AuthorizationRuleWithParameters implements AuthorizationRule { private SessionAccessor sessionAccessor; private SessionService sessionService; private final ProcessInvolvementDelegate processInvolvementDelegate; public IsManagerOfUserInvolvedInProcessInstanceRule(SessionService sessionService, SessionAccessor sessionAccessor, ProcessInvolvementDelegate processInvolvementDelegate) { this.sessionAccessor = sessionAccessor; this.sessionService = sessionService; this.processInvolvementDelegate = processInvolvementDelegate; } @Override public boolean isAllowed(final String key, final Map context) throws SExecutionException { Long processInstanceId = getLongParameter(context, URLAdapterConstants.ID_QUERY_PARAM); if (processInstanceId == null) { throw new IllegalArgumentException( "Parameter 'id' is mandatory to execute Page Authorization rule 'isManagerOfUserInvolvedInProcessInstance'"); } try { long userId = getLoggedUserId(sessionAccessor, sessionService); return processInvolvementDelegate.isManagerOfUserInvolvedInProcessInstance(userId, processInstanceId); } catch (BonitaException e) { throw new SExecutionException(e); } } @Override public String getId() { return AuthorizationRuleConstants.IS_MANAGER_OF_USER_INVOLVED_IN_PROCESS_INSTANCE; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsProcessInitiatorRule.java ================================================ /** * 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.page; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.api.impl.ProcessInvolvementDelegate; import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; /** * @author Anthony Birembaut */ public class IsProcessInitiatorRule extends AuthorizationRuleWithParameters implements AuthorizationRule { private SessionService sessionService; private SessionAccessor sessionAccessor; private final ProcessInvolvementDelegate processInvolvementDelegate; public IsProcessInitiatorRule(SessionService sessionService, SessionAccessor sessionAccessor, ProcessInvolvementDelegate processInvolvementDelegate) { this.sessionAccessor = sessionAccessor; this.sessionService = sessionService; this.processInvolvementDelegate = processInvolvementDelegate; } @Override public boolean isAllowed(final String key, final Map context) throws SExecutionException { long userId = getLoggedUserId(sessionAccessor, sessionService); Long processInstanceId = getLongParameter(context, URLAdapterConstants.ID_QUERY_PARAM); if (processInstanceId == null) { throw new IllegalArgumentException( "Parameter 'id' is mandatory to execute Page Authorization rule 'IsProcessInitiatorRule'"); } try { return processInvolvementDelegate.isProcessOrArchivedProcessInitiator(userId, processInstanceId); } catch (ProcessInstanceNotFoundException e) { throw new SExecutionException(e); } } @Override public String getId() { return AuthorizationRuleConstants.IS_PROCESS_INITIATOR; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsProcessOwnerRule.java ================================================ /** * 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.page; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.core.form.FormMappingService; import org.bonitasoft.engine.core.form.SFormMapping; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService; /** * @author Emmanuel Duchastenier, Anthony Birembaut */ public class IsProcessOwnerRule implements AuthorizationRule { SupervisorMappingService supervisorMappingService; SessionAccessor sessionAccessor; SessionService sessionService; FormMappingService formMappingService; public IsProcessOwnerRule(SupervisorMappingService supervisorMappingService, SessionAccessor sessionAccessor, SessionService sessionService, FormMappingService formMappingService) { this.supervisorMappingService = supervisorMappingService; this.sessionAccessor = sessionAccessor; this.sessionService = sessionService; this.formMappingService = formMappingService; } @Override public boolean isAllowed(final String key, final Map context) throws SExecutionException { try { SFormMapping formMapping = formMappingService.get(key); long processDefinitionId = formMapping.getProcessDefinitionId(); final long userId = sessionService.getSession(sessionAccessor.getSessionId()).getUserId(); return supervisorMappingService.isProcessSupervisor(processDefinitionId, userId); } catch (final SBonitaException e) { throw new SExecutionException("Unable to figure out if the logged user is a process owner.", e); } } @Override public String getId() { return AuthorizationRuleConstants.IS_PROCESS_OWNER; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsTaskAvailableForUserRule.java ================================================ /** * 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.page; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; 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.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; /** * @author Emmanuel Duchastenier, Anthony Birembaut */ public class IsTaskAvailableForUserRule implements AuthorizationRule { ActivityInstanceService activityInstanceService; SessionService sessionService; SessionAccessor sessionAccessor; public IsTaskAvailableForUserRule(ActivityInstanceService activityInstanceService, SessionService sessionService, SessionAccessor sessionAccessor) { this.activityInstanceService = activityInstanceService; this.sessionService = sessionService; this.sessionAccessor = sessionAccessor; } @Override public boolean isAllowed(final String key, final Map context) throws SExecutionException { @SuppressWarnings("unchecked") final Map queryParameters = (Map) context .get(URLAdapterConstants.QUERY_PARAMETERS); String[] idParamValue = new String[0]; if (queryParameters != null) { idParamValue = queryParameters.get(URLAdapterConstants.ID_QUERY_PARAM); } long taskInstanceId; if (idParamValue == null || idParamValue.length == 0) { throw new IllegalArgumentException("The parameter \"id\" is missing from the original URL"); } else { taskInstanceId = Long.parseLong(idParamValue[0]); try { long userId = sessionService.getSession(sessionAccessor.getSessionId()).getUserId(); return isTaskAvailableForOrExecutedByUser(taskInstanceId, userId); } catch (final SBonitaException e) { throw new SExecutionException( "Unable to figure out if the task " + taskInstanceId + " is available for the user.", e); } } } protected boolean isTaskAvailableForOrExecutedByUser(long taskInstanceId, long userId) throws SActivityReadException, SBonitaReadException, SActivityInstanceNotFoundException { try { final SHumanTaskInstance humanTaskInstance = activityInstanceService.getHumanTaskInstance(taskInstanceId); long assigneeId = humanTaskInstance.getAssigneeId(); if (assigneeId > 0) { return userId == assigneeId; } else { return activityInstanceService.isTaskPendingForUser(taskInstanceId, userId); } } catch (SActivityInstanceNotFoundException e) { final SAHumanTaskInstance archivedHumanTaskInstance = activityInstanceService .getLastArchivedFlowNodeInstance(SAHumanTaskInstance.class, taskInstanceId); if (archivedHumanTaskInstance != null) { return userId == archivedHumanTaskInstance.getExecutedBy(); } else { throw e; } } } @Override public String getId() { return AuthorizationRuleConstants.IS_TASK_AVAILABLE_FOR_USER; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/page/IsTaskPerformerRule.java ================================================ /** * 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.page; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.api.impl.TaskInvolvementDelegate; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; /** * @author Emmanuel Duchastenier */ public class IsTaskPerformerRule extends AuthorizationRuleWithParameters implements AuthorizationRule { private final SessionService sessionService; private final SessionAccessor sessionAccessor; private final TaskInvolvementDelegate taskInvolvementDelegate; public IsTaskPerformerRule(SessionService sessionService, SessionAccessor sessionAccessor, TaskInvolvementDelegate taskInvolvementDelegate) { this.sessionAccessor = sessionAccessor; this.sessionService = sessionService; this.taskInvolvementDelegate = taskInvolvementDelegate; } @Override public boolean isAllowed(String key, Map context) throws SExecutionException { try { long userId = getLoggedUserId(sessionAccessor, sessionService); Long processInstanceId = getLongParameter(context, URLAdapterConstants.ID_QUERY_PARAM); if (processInstanceId == null) { throw new IllegalArgumentException( "Parameter 'id' is mandatory to execute Page Authorization rule 'IsProcessInitiatorRule'"); } return taskInvolvementDelegate.isExecutorOfArchivedTaskOfProcess(userId, processInstanceId); } catch (final SBonitaException e) { throw new SExecutionException(e); } } @Override public String getId() { return AuthorizationRuleConstants.IS_TASK_PERFORMER; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/BroadcastServiceLocal.java ================================================ /** * 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.platform; import java.util.Collections; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import org.bonitasoft.engine.service.BroadcastService; import org.bonitasoft.engine.service.TaskResult; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; /** * This implementation does nothing, to be used in a single node environment. * When cluster feature is enabled, an other implementation of the BroadcastService dispatch calls to other nodes. * * @author Baptiste Mesta */ @Component @ConditionalOnSingleCandidate(BroadcastService.class) public class BroadcastServiceLocal implements BroadcastService { @Override public Future>> executeOnOthers(Callable callable) { return CompletableFuture.completedFuture(Collections.emptyMap()); } @Override public Map> executeOnOthersAndWait(Callable callable) { return Collections.emptyMap(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/PlatformManager.java ================================================ /** * 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.platform; import java.util.List; import org.bonitasoft.engine.commons.PlatformLifecycleService; import org.bonitasoft.engine.commons.PlatformRestartHandler; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.platform.configuration.NodeConfiguration; import org.bonitasoft.engine.service.BonitaTaskExecutor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; import org.bonitasoft.engine.tenant.TenantStateManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * Manages the lifecycle of the Bonita Platform node, coordinating the start and stop of all platform services. *

* The PlatformManager is responsible for orchestrating the startup and shutdown sequences of the Bonita Engine. * It manages the state transitions of the platform node (the current JVM instance) and coordinates all * {@link PlatformLifecycleService} implementations to ensure proper initialization and cleanup. *

* Key Responsibilities: *

    *
  • Platform State Management: Tracks and manages platform state (STARTED, STOPPED, STARTING, STOPPING) * via {@link PlatformStateProvider}
  • *
  • Service Lifecycle Coordination: Starts and stops all {@link PlatformLifecycleService} instances * in the correct order (SchedulerService, WorkService, ConnectorExecutor, ClassLoaderService, etc.)
  • *
  • Tenant Management: Coordinates with {@link TenantStateManager} to start/stop tenant services
  • *
  • Version Validation: Verifies platform binaries version matches database schema via * {@link PlatformVersionChecker}
  • *
  • Restart Handlers: Executes {@link PlatformRestartHandler} instances after successful startup * (e.g., resume interrupted work, reschedule jobs)
  • *
*

* Startup Sequence: *

    *
  1. Validates platform state allows starting (via {@link PlatformStateProvider#initializeStart()})
  2. *
  3. Checks platform version compatibility via {@link PlatformVersionChecker}
  4. *
  5. Starts all {@link PlatformLifecycleService} instances in order
  6. *
  7. Updates state to STARTED
  8. *
  9. Starts tenant services via {@link TenantStateManager}
  10. *
  11. Executes platform restart handlers asynchronously
  12. *
*

* Shutdown Sequence: *

    *
  1. Validates platform state allows stopping (via {@link PlatformStateProvider#initializeStop()})
  2. *
  3. Stops tenant services via {@link TenantStateManager}
  4. *
  5. Stops all {@link PlatformLifecycleService} instances in order
  6. *
  7. Updates state to STOPPED
  8. *
*

* Thread Safety: All start/stop methods are synchronized to prevent concurrent state modifications. *

* Typical Platform Services Managed: *

    *
  • SchedulerService - Quartz-based job scheduling
  • *
  • WorkService - Asynchronous work execution via thread pools
  • *
  • ConnectorExecutorService - Connector execution management
  • *
  • ClassLoaderService - Process-specific classloader management
  • *
  • EventService - Event publishing and subscription
  • *
  • CacheService - Data caching infrastructure
  • *
* * @see PlatformLifecycleService * @see PlatformStateProvider * @see TenantStateManager * @see PlatformVersionChecker * @see PlatformRestartHandler */ @Component public class PlatformManager { private static final Logger logger = LoggerFactory.getLogger(PlatformManager.class); private final BonitaTaskExecutor bonitaTaskExecutor; private final NodeConfiguration nodeConfiguration; private final List platformServices; private final PlatformStateProvider platformStateProvider; private final PlatformVersionChecker platformVersionChecker; public PlatformManager(NodeConfiguration nodeConfiguration, List platformServices, PlatformStateProvider platformStateProvider, BonitaTaskExecutor bonitaTaskExecutor, PlatformVersionChecker platformVersionChecker) { this.nodeConfiguration = nodeConfiguration; this.platformServices = platformServices; this.platformStateProvider = platformStateProvider; this.bonitaTaskExecutor = bonitaTaskExecutor; this.platformVersionChecker = platformVersionChecker; } /** * @return the current state of the platform */ public PlatformState getState() { return platformStateProvider.getState(); } /** * Stop the platform * * @return true if the node was stopped, false if it was not stoppable (already stopped, starting or stopping) */ public synchronized boolean stop() throws Exception { logger.info("Stopping platform:"); if (!platformStateProvider.initializeStop()) { return false; } getTenantStateManager().stop(); for (final PlatformLifecycleService platformService : platformServices) { logger.info("Stop service of platform: {}", platformService); platformService.stop(); } platformStateProvider.setStopped(); logger.info("Platform stopped."); return true; } /** * Start the platform and default tenant * * @return true if the node was started, false if it was not startable (already started, starting or stopping) */ public synchronized boolean start() throws Exception { logger.info("Starting platform:"); if (!platformStateProvider.initializeStart()) { logger.info("Platform cannot be started, it is: {}", platformStateProvider.getState()); return false; } checkPlatformVersion(); startPlatformServices(); platformStateProvider.setStarted(); getTenantStateManager().start(); restartHandlersOfPlatform(); logger.info("Platform started."); return true; } TenantStateManager getTenantStateManager() { return ServiceAccessorSingleton.getInstance().getTenantStateManager(); } private void restartHandlersOfPlatform() { for (final PlatformRestartHandler platformRestartHandler : nodeConfiguration.getPlatformRestartHandlers()) { bonitaTaskExecutor.execute(platformRestartHandler::execute); } } private void checkPlatformVersion() throws Exception { if (!platformVersionChecker.verifyPlatformVersion()) { throw new StartNodeException(platformVersionChecker.getErrorMessage()); } } private void startPlatformServices() throws SBonitaException { for (final PlatformLifecycleService platformService : platformServices) { logger.info("Start service of platform : {}", platformService); platformService.start(); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/PlatformStateProvider.java ================================================ /** * 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.platform; import lombok.Getter; import org.springframework.stereotype.Component; @Getter @Component public class PlatformStateProvider { private PlatformState state = PlatformState.STOPPED; public PlatformStateProvider() { } PlatformStateProvider(PlatformState state) { this.state = state; } /** * Transition the Platform state to STARTED ( state put in STARTING ) * * @return true only if the state was changed */ boolean initializeStart() { if (state != PlatformState.STOPPED) { return false; } state = PlatformState.STARTING; return true; } void setStarted() { state = PlatformState.STARTED; } /** * Transition the Platform state to STOPPED ( state put in STOPPING ) * * @return true only if the state was changed */ boolean initializeStop() { if (state != PlatformState.STARTED) { return false; } state = PlatformState.STOPPING; return true; } void setStopped() { state = PlatformState.STOPPED; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/PlatformVersionChecker.java ================================================ /** * Copyright (C) 2020 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.platform; import java.io.Serializable; import java.text.MessageFormat; import java.util.Map; import java.util.Optional; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.bonitasoft.engine.EngineInitializer; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.service.BroadcastService; import org.bonitasoft.engine.service.TaskResult; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.transaction.TransactionService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * @author Matthieu Chaffotte * @author Baptiste Mesta * @author Emmanuel Duchastenier */ @Component public class PlatformVersionChecker { private static final Logger LOGGER = LoggerFactory.getLogger(EngineInitializer.class); private final PlatformService platformService; private final BroadcastService broadcastService; private final TransactionService transactionService; private String errorMessage; public PlatformVersionChecker(final PlatformService platformService, BroadcastService broadcastService, TransactionService transactionService) { this.platformService = platformService; this.broadcastService = broadcastService; this.transactionService = transactionService; } public boolean verifyPlatformVersion() throws Exception { return transactionService.executeInTransaction(this::execute); } boolean execute() throws SBonitaException { // the database schema version: String databaseSchemaVersion = platformService.getPlatform().getDbSchemaVersion(); // the version in jars final String platformVersionFromBinaries = platformService.getSPlatformProperties().getPlatformVersion(); String supportedDatabaseSchemaVersion = extractMinorVersion(platformVersionFromBinaries); LOGGER.info("Bonita platform version (binaries): {}", platformVersionFromBinaries); LOGGER.info("Bonita database schema version: {}", databaseSchemaVersion); boolean isDatabaseSchemaSupported = databaseSchemaVersion.equals(supportedDatabaseSchemaVersion); if (!isDatabaseSchemaSupported) { errorMessage = MessageFormat.format("The version of the platform in database is not the same as expected:" + " Supported database schema version is <{0}> and current database schema version is <{1}>", supportedDatabaseSchemaVersion, databaseSchemaVersion); return false; } final Optional versionFromOtherNodes = getVersionFromOtherNodes(); if (versionFromOtherNodes.isPresent() && !platformVersionFromBinaries.equals(versionFromOtherNodes.get())) { errorMessage = MessageFormat.format( "Node cannot be started as it is in version {0} whereas other nodes are in version {1}\n" + "All nodes in the same cluster must execute the same Bonita runtime version", platformVersionFromBinaries, versionFromOtherNodes.get()); LOGGER.error(errorMessage); return false; } return true; } /** * Search for the first version found amongst other nodes. * * @return the first version encountered, if there are other nodes, * or empty optional if there are no other nodes in the cluster, * or throw exception if no version can be retrieved. */ protected Optional getVersionFromOtherNodes() { try { final Map> nodeToVersionMap = broadcastService .executeOnOthers(new GetPlatformVersionFromNode()).get(); if (nodeToVersionMap.isEmpty()) { // There are no other nodes in the cluster, so ok: return Optional.empty(); } for (Map.Entry> nodeToVersion : nodeToVersionMap.entrySet()) { final TaskResult result = nodeToVersion.getValue(); if (result.isOk()) { LOGGER.info("Found that Bonita node '{}' is in version {}", nodeToVersion.getKey(), result.getResult()); return Optional.of(result.getResult()); } // otherwise we check the next node, until finding a valid version } } catch (InterruptedException | ExecutionException ignored) { } final String msg = "Cannot access other node version in the cluster"; LOGGER.error(msg); throw new RuntimeException(msg); } /** * This method is duplicate in class VersionServiceImpl. * This is accepted to limit over-engineering just to extract an util method. */ private String extractMinorVersion(String version) { final Matcher matcher = Pattern.compile("(\\d+\\.\\d+).*").matcher(version); if (matcher.matches()) { return matcher.group(1); } else { throw new IllegalArgumentException(version + " does not respect Semantic Versioning"); } } public String getErrorMessage() { return errorMessage; } static class GetPlatformVersionFromNode implements Callable, Serializable { @Override public String call() throws Exception { return ServiceAccessorFactory.getInstance().createServiceAccessor() .getPlatformService().getSPlatformProperties().getPlatformVersion(); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/configuration/NodeConfiguration.java ================================================ /** * 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.platform.configuration; import java.util.List; import org.bonitasoft.engine.commons.PlatformRestartHandler; /** * This class allow to provide a configuration for the current node * We should be able to have one different per node * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public interface NodeConfiguration { /** * Handlers called on restart of the platform */ List getPlatformRestartHandlers(); /** * @return * true if the sessions should be cleaned when the node is stopped */ boolean shouldClearSessions(); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/configuration/NodeConfigurationImpl.java ================================================ /** * 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.platform.configuration; import java.util.List; import org.bonitasoft.engine.commons.CollectionUtil; import org.bonitasoft.engine.commons.PlatformRestartHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Laurent Vaills * @author Celine Souchet */ @Component @ConditionalOnSingleCandidate(NodeConfiguration.class) public class NodeConfigurationImpl implements NodeConfiguration { private List platformRestartHandlers; @Override public List getPlatformRestartHandlers() { return CollectionUtil.emptyOrUnmodifiable(platformRestartHandlers); } @Autowired(required = false) public void setPlatformRestartHandlers(final List platformRestartHandlers) { this.platformRestartHandlers = platformRestartHandlers; } @Override public boolean shouldClearSessions() { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/configuration/datasource/QuartzConnectionProvider.java ================================================ /** * 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.platform.configuration.datasource; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import org.quartz.utils.ConnectionProvider; /** * This is a hack to let Quartz access datasource beans from SprigContext * Quartz support custom connection providers but not non primitive parameters for them */ public class QuartzConnectionProvider implements ConnectionProvider { private Boolean isXaDataSource; private DataSource dataSource; /** * this is called by quartz (given by the configuration) * * @param isXaDataSource true if the datasource returned by this configuration provider should be the xa datasource */ public void setXaDataSource(boolean isXaDataSource) { this.isXaDataSource = isXaDataSource; } @Override public Connection getConnection() throws SQLException { return dataSource.getConnection(); } @Override public void shutdown() throws SQLException { } @Override public void initialize() throws SQLException { if (isXaDataSource == null) { throw new IllegalStateException("Quartz datasource is not set"); } if (isXaDataSource) { dataSource = QuartzDataSourceAccessorProvider.getInstance().getBonitaDataSource(); } else { dataSource = QuartzDataSourceAccessorProvider.getInstance().getBonitaNonXaDataSource(); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/configuration/datasource/QuartzDataSourceAccessor.java ================================================ /** * 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.platform.configuration.datasource; import javax.sql.DataSource; /** * This is a hack to let Quartz access datasource beans from SprigContext * Quartz support custom connection providers but not non primitive parameters for them */ public class QuartzDataSourceAccessor { private DataSource bonitaDataSource; private DataSource bonitaNonXaDataSource; public QuartzDataSourceAccessor(DataSource bonitaDataSource, DataSource bonitaNonXaDataSource) { this.bonitaDataSource = bonitaDataSource; this.bonitaNonXaDataSource = bonitaNonXaDataSource; } public DataSource getBonitaDataSource() { return bonitaDataSource; } public DataSource getBonitaNonXaDataSource() { return bonitaNonXaDataSource; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/platform/configuration/datasource/QuartzDataSourceAccessorProvider.java ================================================ /** * 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.platform.configuration.datasource; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; /** * This is a hack to let Quartz access datasource beans from SprigContext * Quartz support custom connection providers but not non primitive parameters for them */ @Component public class QuartzDataSourceAccessorProvider { private static QuartzDataSourceAccessor INSTANCE; public QuartzDataSourceAccessorProvider(@Qualifier("bonitaDataSource") DataSource bonitaDataSource, @Qualifier("bonitaNonXaDataSource") DataSource bonitaNonXaDataSource) { INSTANCE = new QuartzDataSourceAccessor(bonitaDataSource, bonitaNonXaDataSource); } public static QuartzDataSourceAccessor getInstance() { return INSTANCE; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/DefaultProfilesUpdater.java ================================================ /** * 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.profile; import java.io.File; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.commons.TenantLifecycleService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.io.IOUtil; import org.bonitasoft.engine.exception.ExecutionException; import org.bonitasoft.engine.profile.xml.ProfilesNode; import org.bonitasoft.engine.session.SessionService; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * Update provided profiles from profiles.xml from classpath */ @Slf4j @Component //must be initialized before page and application import to ensure applications are mapped to profile @Order(3) public class DefaultProfilesUpdater implements TenantLifecycleService { private final ProfilesImporter profilesImporter; public DefaultProfilesUpdater(ProfilesImporter profilesImporter) { this.profilesImporter = profilesImporter; } @Override public void init() throws SBonitaException { execute(); } /** * Executes a default profile update * * @return whether the default profiles where updated * @throws RuntimeException if execution fails */ public boolean execute() { try { final File md5File = getProfilesMD5File(); final String defaultProfilesXml = getDefaultProfilesXml(); if (shouldUpdateProfiles(md5File, defaultProfilesXml)) { // Default profiles do not exist or are different log.info( "Default profiles not up to date, updating them..."); final ProfilesNode defaultProfiles = getProfilesFromXML(defaultProfilesXml); doUpdateProfiles(defaultProfiles, md5File, defaultProfilesXml); return true; } else { // No update required log.info( "Default profiles are up to date"); return false; } } catch (IOException e) { log.error( "Unable to read the read the default profile file to update them", e); } catch (Exception e) { log.error( "Unable to update default profiles", e); } return false; } void doUpdateProfiles(final ProfilesNode defaultProfiles, final File md5File, final String defaultProfilesXml) throws NoSuchAlgorithmException, IOException { try { final List importStatuses = profilesImporter.importProfiles(defaultProfiles, ImportPolicy.UPDATE_DEFAULTS, SessionService.SYSTEM_ID); log.info("Updated default profiles {}", importStatuses); if (md5File != null) { // but may not exist IOUtil.writeMD5(md5File, defaultProfilesXml.getBytes()); } } catch (ExecutionException e) { log.error("Unable to update default profiles", e); } } File getProfilesMD5File() throws IOException { return ProfilesImporter.getFileContainingMD5(); } /** * Checks if profiles should be updated: if MD5 file differs from MD5 of XML file * * @return true if profiles should be updated */ boolean shouldUpdateProfiles(final File md5File, final String defaultProfilesXml) throws NoSuchAlgorithmException { return md5File == null || !IOUtil.checkMD5(md5File, defaultProfilesXml.getBytes()); } /** * @return content of the XML file that contains default profiles * @throws IOException in case of problem reading profiles file */ String getDefaultProfilesXml() throws IOException { String profiles = IOUtil.readResource("profiles-sp.xml"); if (profiles != null) { log.info("Loading profiles from file profiles-sp.xml"); } else { profiles = IOUtil.readResource("profiles.xml"); log.info("Loading profiles from file profiles.xml"); } return profiles; } ProfilesNode getProfilesFromXML(final String defaultProfilesXml) throws IOException { return profilesImporter.convertFromXml(defaultProfilesXml); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/DeleteExistingImportStrategy.java ================================================ /** * 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.profile; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.exception.ExecutionException; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.profile.xml.ProfileNode; /** * @author Baptiste Mesta */ public class DeleteExistingImportStrategy extends ProfileImportStrategy { public DeleteExistingImportStrategy(final ProfileService profileService) { super(profileService); } @Override public void beforeImport() throws ExecutionException { final QueryOptions queryOptions = new QueryOptions(0, 100, Collections.singletonList(new OrderByOption(SProfile.class, SProfile.NAME, OrderByType.ASC)), Collections. emptyList(), null); // delete all profiles try { List profiles; do { profiles = getProfileService().searchProfiles(queryOptions); for (final SProfile sProfile : profiles) { getProfileService().deleteProfile(sProfile); } } while (!profiles.isEmpty()); } catch (final SBonitaException e) { throw new ExecutionException(e); } } @Override public SProfile whenProfileExists(final long importerId, final ProfileNode profile, final SProfile existingProfile) { // nothing to do because we deleted all profiles return null; } @Override public boolean canCreateProfileIfNotExists(final ProfileNode profile) { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/FailOnDuplicateImportStrategy.java ================================================ /** * 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.profile; import org.bonitasoft.engine.exception.ExecutionException; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.profile.xml.ProfileNode; /** * @author Baptiste Mesta */ public class FailOnDuplicateImportStrategy extends ProfileImportStrategy { public FailOnDuplicateImportStrategy(final ProfileService profileService) { super(profileService); } @Override public void beforeImport() { } @Override public SProfile whenProfileExists(final long importerId, final ProfileNode profile, final SProfile existingProfile) throws ExecutionException { throw new ExecutionException("There's a same name profile when import a profile named " + profile.getName()); } @Override public boolean canCreateProfileIfNotExists(final ProfileNode profile) { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/IgnoreDuplicateImportStrategy.java ================================================ /** * 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.profile; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.profile.xml.ProfileNode; /** * @author Baptiste Mesta */ public class IgnoreDuplicateImportStrategy extends ProfileImportStrategy { public IgnoreDuplicateImportStrategy(final ProfileService profileService) { super(profileService); } @Override public void beforeImport() { } @Override public SProfile whenProfileExists(final long importerId, final ProfileNode profile, final SProfile existingProfile) { // will be skipped return null; } @Override public boolean canCreateProfileIfNotExists(final ProfileNode profile) { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/ImportPolicy.java ================================================ /** * 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.profile; /** * @author Zhao Na * @author Matthieu Chaffotte * @author Emmanuel Duchastenier */ public enum ImportPolicy { DELETE_EXISTING, REPLACE_DUPLICATES, FAIL_ON_DUPLICATES, IGNORE_DUPLICATES, UPDATE_DEFAULTS, UPDATE_DEFAULTS_AND_CREATE_NEW // , MERGE_DUPLICATES (Merge should retain existing IDs) } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/ProfileImportStrategy.java ================================================ /** * 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.profile; import org.bonitasoft.engine.exception.ExecutionException; import org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException; import org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberDeletionException; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.profile.xml.ProfileNode; /** * @author Baptiste Mesta */ public abstract class ProfileImportStrategy { private final ProfileService profileService; public ProfileImportStrategy(final ProfileService profileService) { this.profileService = profileService; } /** * what to do before the import */ public abstract void beforeImport() throws ExecutionException; /** * return the imported version of the profile */ public abstract SProfile whenProfileExists(final long importerId, final ProfileNode profile, final SProfile existingProfile) throws ExecutionException, SProfileMemberDeletionException, SProfileUpdateException; /** * return whether the profile can be created if it does not exist */ public abstract boolean canCreateProfileIfNotExists(ProfileNode profile); /** * convert {@link ProfileNode} to {@link SProfile} * * @return the profile service */ protected ProfileService getProfileService() { return profileService; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/ProfilesExporter.java ================================================ /** * 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.profile; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.xml.bind.JAXBException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.exception.ExecutionException; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SGroupNotFoundException; import org.bonitasoft.engine.identity.SRoleNotFoundException; import org.bonitasoft.engine.identity.SUserNotFoundException; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.profile.model.SProfileMember; import org.bonitasoft.engine.profile.xml.MembershipNode; import org.bonitasoft.engine.profile.xml.ProfileMappingNode; import org.bonitasoft.engine.profile.xml.ProfileNode; import org.bonitasoft.engine.profile.xml.ProfilesNode; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta */ @Component public class ProfilesExporter { private final static String USER_SUFFIX = "ForUser"; private final static String ROLE_SUFFIX = "ForRole"; private final static String GROUP_SUFFIX = "ForGroup"; private final static String ROLE_AND_GROUP_SUFFIX = "ForRoleAndGroup"; private static final int NUMBER_OF_RESULTS = 100; private IdentityService identityService; private ProfileService profileService; private ProfilesParser profilesParser; public ProfilesExporter(IdentityService identityService, ProfileService profileService, ProfilesParser profilesParser) { this.identityService = identityService; this.profileService = profileService; this.profilesParser = profilesParser; } public String exportAllProfiles() throws ExecutionException { try { ArrayList sProfiles = getAllProfiles(); ProfilesNode profiles = toProfiles(sProfiles); return profilesParser.convert(profiles); } catch (SBonitaException | JAXBException e) { throw new ExecutionException(e); } } private ArrayList getAllProfiles() throws SBonitaReadException { ArrayList profiles = new ArrayList<>(); int from = 0; List sProfiles; do { final QueryOptions queryOptions = new QueryOptions(from, 100, Collections.singletonList(new OrderByOption( SProfile.class, SProfile.NAME, OrderByType.ASC)), Collections. emptyList(), null); sProfiles = profileService.searchProfiles(queryOptions); from += 100; profiles.addAll(sProfiles); } while (sProfiles.size() == 100); return profiles; } public String exportProfiles(List longs) throws ExecutionException { try { List sProfile = profileService.getProfiles(longs); ProfilesNode profiles = toProfiles(sProfile); return profilesParser.convert(profiles); } catch (SBonitaException | JAXBException e) { throw new ExecutionException(e); } } ProfilesNode toProfiles(List sProfile) throws SBonitaReadException, SUserNotFoundException, SGroupNotFoundException, SRoleNotFoundException { ArrayList profiles = new ArrayList<>(); for (SProfile profile : sProfile) { profiles.add(toProfile(profile)); } return new ProfilesNode(profiles); } private ProfileNode toProfile(SProfile sProfile) throws SBonitaReadException, SUserNotFoundException, SGroupNotFoundException, SRoleNotFoundException { ProfileNode profile = new ProfileNode(sProfile.getName(), sProfile.isDefault()); profile.setDescription(sProfile.getDescription()); profile.setProfileMapping(getProfileMapping(sProfile)); return profile; } private ProfileMappingNode getProfileMapping(SProfile sProfile) throws SUserNotFoundException, SBonitaReadException, SGroupNotFoundException, SRoleNotFoundException { ProfileMappingNode profileMapping = new ProfileMappingNode(); profileMapping.setUsers(getUsers(sProfile)); profileMapping.setGroups(getGroups(sProfile)); profileMapping.setRoles(getRoles(sProfile)); profileMapping.setMemberships(getMemberships(sProfile)); return profileMapping; } private List getUsers(SProfile profile) throws SBonitaReadException, SUserNotFoundException { ArrayList users = new ArrayList<>(); int pageIndex = 0; List sProfileMembers; do { sProfileMembers = searchProfileMembers(pageIndex, profile.getId(), USER_SUFFIX); for (final SProfileMember sProfileMember : sProfileMembers) { users.add(identityService.getUser(sProfileMember.getUserId()).getUserName()); } pageIndex++; } while (sProfileMembers.size() > 0); return users; } private List getGroups(SProfile profile) throws SBonitaReadException, SGroupNotFoundException { ArrayList groups = new ArrayList<>(); int pageIndex = 0; List sProfileMembers; do { sProfileMembers = searchProfileMembers(pageIndex, profile.getId(), GROUP_SUFFIX); for (final SProfileMember sProfileMember : sProfileMembers) { groups.add(identityService.getGroup(sProfileMember.getGroupId()).getPath()); } pageIndex++; } while (sProfileMembers.size() > 0); return groups; } private List getRoles(SProfile profile) throws SBonitaReadException, SRoleNotFoundException { ArrayList roles = new ArrayList<>(); int pageIndex = 0; List sProfileMembers; do { sProfileMembers = searchProfileMembers(pageIndex, profile.getId(), ROLE_SUFFIX); for (final SProfileMember sProfileMember : sProfileMembers) { roles.add(identityService.getRole(sProfileMember.getRoleId()).getName()); } pageIndex++; } while (sProfileMembers.size() > 0); return roles; } private List getMemberships(SProfile profile) throws SBonitaReadException, SRoleNotFoundException, SGroupNotFoundException { ArrayList memberships = new ArrayList<>(); int pageIndex = 0; List sProfileMembers; do { sProfileMembers = searchProfileMembers(pageIndex, profile.getId(), ROLE_AND_GROUP_SUFFIX); for (final SProfileMember sProfileMember : sProfileMembers) { memberships.add(new MembershipNode(identityService.getGroup(sProfileMember.getGroupId()).getPath(), identityService.getRole(sProfileMember.getRoleId()).getName())); } pageIndex++; } while (sProfileMembers.size() > 0); return memberships; } private List searchProfileMembers(final int fromIndex, final long profileId, final String querySuffix) throws SBonitaReadException { final QueryOptions queryOptions = new QueryOptions(fromIndex * NUMBER_OF_RESULTS, NUMBER_OF_RESULTS, Collections.singletonList(new OrderByOption( SProfileMember.class, SProfileMember.ID, OrderByType.ASC)), Collections.singletonList(new FilterOption(SProfileMember.class, SProfileMember.PROFILE_ID, profileId)), null); return profileService.searchProfileMembers(querySuffix, queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/ProfilesImporter.java ================================================ /** * 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.profile; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.xml.bind.JAXBException; import org.bonitasoft.engine.api.ImportError; import org.bonitasoft.engine.api.ImportError.Type; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.api.ImportStatus.Status; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.exception.ExecutionException; import org.bonitasoft.engine.home.BonitaHomeServer; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SGroupNotFoundException; import org.bonitasoft.engine.identity.SRoleNotFoundException; import org.bonitasoft.engine.identity.SUserNotFoundException; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.profile.exception.profile.SProfileCreationException; import org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException; import org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException; import org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberCreationException; import org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberDeletionException; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.profile.xml.MembershipNode; import org.bonitasoft.engine.profile.xml.ProfileMappingNode; import org.bonitasoft.engine.profile.xml.ProfileNode; import org.bonitasoft.engine.profile.xml.ProfilesNode; import org.springframework.stereotype.Component; /** * Import profiles with mapping using Policy * * @author Baptiste Mesta */ @Component public class ProfilesImporter { private final ProfileService profileService; private final IdentityService identityService; private final ProfilesParser profilesParser; public ProfilesImporter(final ProfileService profileService, final IdentityService identityService, ProfilesParser profilesParser) { this.profileService = profileService; this.identityService = identityService; this.profilesParser = profilesParser; } private static ProfileImportStrategy getStrategy(final ProfileService profileService, final ImportPolicy policy) { return switch (policy) { case DELETE_EXISTING -> new DeleteExistingImportStrategy(profileService); case FAIL_ON_DUPLICATES -> new FailOnDuplicateImportStrategy(profileService); case IGNORE_DUPLICATES -> new IgnoreDuplicateImportStrategy(profileService); case REPLACE_DUPLICATES -> new ReplaceDuplicateImportStrategy(profileService); case UPDATE_DEFAULTS -> new UpdateDefaultsImportStrategy(profileService); case UPDATE_DEFAULTS_AND_CREATE_NEW -> new UpdateDefaultsAndCreateNewImportStrategy(profileService); }; } public List importProfiles(ProfilesNode profiles, ImportPolicy policy, final long importerId) throws ExecutionException { ProfileImportStrategy importStrategy = getStrategy(profileService, policy); importStrategy.beforeImport(); try { final List importStatus = new ArrayList<>(profiles.getProfiles().size()); for (final ProfileNode profile : profiles.getProfiles()) { if (profile.getName() == null || profile.getName().isEmpty()) { continue; } final ImportStatus currentStatus = new ImportStatus(profile.getName()); importStatus.add(currentStatus); SProfile existingProfile = null; try { existingProfile = profileService.getProfileByName(profile.getName()); currentStatus.setStatus(Status.REPLACED); } catch (final SProfileNotFoundException e1) { // profile does not exists } final SProfile newProfile = importTheProfile(importerId, profile, existingProfile, importStrategy); if (newProfile == null) { // in case of skip currentStatus.setStatus(Status.SKIPPED); continue; } final long profileId = newProfile.getId(); /* * Import mapping with organization */ ProfileMappingNode profileMapping = profile.getProfileMapping(); if (profileMapping != null) { currentStatus.getErrors() .addAll(importProfileMapping(profileService, identityService, profileId, profileMapping)); } } return importStatus; } catch (final SBonitaException e) { throw new ExecutionException(e); } } List importProfileMapping(final ProfileService profileService, final IdentityService identityService, final long profileId, final ProfileMappingNode profileMapping) throws SProfileMemberCreationException { final ArrayList errors = new ArrayList<>(); for (final String userName : profileMapping.getUsers()) { SUser user = null; try { user = identityService.getUserByUserName(userName); } catch (final SUserNotFoundException e) { errors.add(new ImportError(userName, Type.USER)); continue; } profileService.addUserToProfile(profileId, user.getId(), user.getFirstName(), user.getLastName(), user.getUserName()); } for (final String groupPath : profileMapping.getGroups()) { SGroup group = null; try { group = identityService.getGroupByPath(groupPath); } catch (final SGroupNotFoundException e) { errors.add(new ImportError(groupPath, Type.GROUP)); continue; } profileService.addGroupToProfile(profileId, group.getId(), group.getName(), group.getParentPath()); } for (final String roleName : profileMapping.getRoles()) { SRole role = null; try { role = identityService.getRoleByName(roleName); } catch (final SRoleNotFoundException e) { errors.add(new ImportError(roleName, Type.ROLE)); continue; } profileService.addRoleToProfile(profileId, role.getId(), role.getName()); } for (final MembershipNode membership : profileMapping.getMemberships()) { SGroup group = null; try { group = identityService.getGroupByPath(membership.getGroup()); } catch (final SGroupNotFoundException e) { errors.add(new ImportError(membership.getGroup(), Type.GROUP)); } SRole role = null; try { role = identityService.getRoleByName(membership.getRole()); } catch (final SRoleNotFoundException e) { errors.add(new ImportError(membership.getRole(), Type.ROLE)); } if (group == null || role == null) { continue; } profileService.addRoleAndGroupToProfile(profileId, role.getId(), group.getId(), role.getName(), group.getName(), group.getParentPath()); } return errors; } protected SProfile importTheProfile(final long importerId, final ProfileNode profile, final SProfile existingProfile, ProfileImportStrategy importStrategy) throws ExecutionException, SProfileMemberDeletionException, SProfileUpdateException, SProfileCreationException { final SProfile newProfile; if (existingProfile != null) { newProfile = importStrategy.whenProfileExists(importerId, profile, existingProfile); } else if (importStrategy.canCreateProfileIfNotExists(profile)) { newProfile = profileService.createProfile(createSProfile(profile, importerId)); } else { newProfile = null; } return newProfile; } SProfile createSProfile(final ProfileNode profileNode, final long importerId) { final boolean isDefault = profileNode.isDefault(); final long creationDate = System.currentTimeMillis(); return SProfile.builder() .name(profileNode.getName()) .isDefault(isDefault) .creationDate(creationDate) .createdBy(importerId) .lastUpdateDate(creationDate) .lastUpdatedBy(importerId).description(profileNode.getDescription()).build(); } public ProfilesNode convertFromXml(final String xmlContent) throws IOException { try { return profilesParser.convert(xmlContent); } catch (JAXBException e) { throw new IOException(e); } } static File getFileContainingMD5() throws IOException { return BonitaHomeServer.getInstance().getProfileStorage().getProfileMD5(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/ReplaceDuplicateImportStrategy.java ================================================ /** * 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.profile; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.profile.builder.SProfileUpdateBuilder; import org.bonitasoft.engine.profile.builder.SProfileUpdateBuilderFactory; import org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException; import org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberDeletionException; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.profile.xml.ProfileNode; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta */ public class ReplaceDuplicateImportStrategy extends ProfileImportStrategy { public ReplaceDuplicateImportStrategy(final ProfileService profileService) { super(profileService); } @Override public void beforeImport() { } @Override public SProfile whenProfileExists(final long importerId, final ProfileNode profile, final SProfile existingProfile) throws SProfileMemberDeletionException, SProfileUpdateException { getProfileService().deleteAllProfileMembersOfProfile(existingProfile); // update profile if (profile.isDefault() || existingProfile.isDefault()) { // only update LastUpdatedBy and LastUpdateDate return getProfileService().updateProfile(existingProfile, getProfileUpdateDescriptor(profile, importerId, false)); } else { return getProfileService().updateProfile(existingProfile, getProfileUpdateDescriptor(profile, importerId, true)); } } @Override public boolean canCreateProfileIfNotExists(final ProfileNode profile) { return !profile.isDefault(); } EntityUpdateDescriptor getProfileUpdateDescriptor(final ProfileNode profile, final long importerId, final boolean updateAllProfile) { final SProfileUpdateBuilder updateBuilder = BuilderFactory.get(SProfileUpdateBuilderFactory.class) .createNewInstance(); updateBuilder.setLastUpdateDate(System.currentTimeMillis()).setLastUpdatedBy(importerId); if (updateAllProfile) { updateBuilder.setDescription(profile.getDescription()); } return updateBuilder.done(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/UpdateDefaultsAndCreateNewImportStrategy.java ================================================ /** * Copyright (C) 2023 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.profile; import org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.profile.xml.ProfileNode; public class UpdateDefaultsAndCreateNewImportStrategy extends UpdateDefaultsImportStrategy { public UpdateDefaultsAndCreateNewImportStrategy(final ProfileService profileService) { super(profileService); } @Override public SProfile whenProfileExists(final long importerId, final ProfileNode profile, final SProfile existingProfile) throws SProfileUpdateException { if (profile.isDefault() || existingProfile.isDefault()) { // only update LastUpdatedBy and LastUpdateDate return getProfileService().updateProfile(existingProfile, getProfileUpdateDescriptor(profile, importerId, true)); } else { throw new SProfileUpdateException("A profile already exists with name: " + profile.getName()); } } @Override public boolean canCreateProfileIfNotExists(final ProfileNode profile) { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/profile/UpdateDefaultsImportStrategy.java ================================================ /** * 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.profile; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.profile.builder.SProfileUpdateBuilder; import org.bonitasoft.engine.profile.builder.SProfileUpdateBuilderFactory; import org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.profile.xml.ProfileNode; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta */ public class UpdateDefaultsImportStrategy extends ProfileImportStrategy { public UpdateDefaultsImportStrategy(final ProfileService profileService) { super(profileService); } @Override public void beforeImport() { } @Override public SProfile whenProfileExists(final long importerId, final ProfileNode profile, final SProfile existingProfile) throws SProfileUpdateException { // update profile if (profile.isDefault() || existingProfile.isDefault()) { // only update LastUpdatedBy and LastUpdateDate return getProfileService().updateProfile(existingProfile, getProfileUpdateDescriptor(profile, importerId, true)); } else { return null; } } @Override public boolean canCreateProfileIfNotExists(final ProfileNode profile) { return profile.isDefault(); } EntityUpdateDescriptor getProfileUpdateDescriptor(final ProfileNode profile, final long importerId, final boolean updateAllProfile) { final SProfileUpdateBuilder updateBuilder = BuilderFactory.get(SProfileUpdateBuilderFactory.class) .createNewInstance(); updateBuilder.setLastUpdateDate(System.currentTimeMillis()).setLastUpdatedBy(importerId); if (updateAllProfile) { updateBuilder.setDescription(profile.getDescription()); } return updateBuilder.done(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractActivityInstanceSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.search.impl.SearchFilter; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Yanyan Liu * @author Celine Souchet */ public abstract class AbstractActivityInstanceSearchEntity extends AbstractSearchEntity { private final FlowNodeStateManager flowNodeStateManager; private final Class entityClass; public AbstractActivityInstanceSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options, final FlowNodeStateManager flowNodeStateManager) throws SBonitaReadException { super(searchDescriptor, options); this.flowNodeStateManager = flowNodeStateManager; entityClass = getEntityClass(options); } @Override public List convertToClientObjects(final List serverObjects) { // invoke this method to get to different typed client object according to the server type return ModelConvertor.toActivityInstances(serverObjects, flowNodeStateManager); } private Class getEntityClass(final SearchOptions searchOptions) throws SBonitaReadException { Class entityClass = SActivityInstance.class; final SearchFilter searchFilter = getSearchFilter(searchOptions, ActivityInstanceSearchDescriptor.ACTIVITY_TYPE); if (searchFilter != null) { final FlowNodeType activityType = (FlowNodeType) searchFilter.getValue(); if (activityType != null) { switch (activityType) { case AUTOMATIC_TASK: entityClass = SAutomaticTaskInstance.class; break; case MANUAL_TASK: entityClass = SManualTaskInstance.class; break; case USER_TASK: entityClass = SUserTaskInstance.class; break; case HUMAN_TASK: entityClass = SHumanTaskInstance.class; break; case RECEIVE_TASK: entityClass = SReceiveTaskInstance.class; break; case SEND_TASK: entityClass = SSendTaskInstance.class; break; case CALL_ACTIVITY: entityClass = SCallActivityInstance.class; break; case MULTI_INSTANCE_ACTIVITY: entityClass = SMultiInstanceActivityInstance.class; break; case SUB_PROCESS: entityClass = SSubProcessActivityInstance.class; break; case LOOP_ACTIVITY: entityClass = SLoopActivityInstance.class; break; default: throw new SBonitaReadException("You're searching for a " + activityType.name() + " which is not an activity type, using the ActivityInstanceSearch feature. Ensure you are using the correct search method for your needs."); } searchOptions.getFilters().remove(searchFilter); } } return entityClass; } @Override protected void validateQuery(final SearchOptions searchOptions) throws SBonitaReadException { validateActivityTypeFilterUnicity(searchOptions); } private void validateActivityTypeFilterUnicity(SearchOptions searchOptions) throws SBonitaReadException { for (final SearchFilter searchFilter : searchOptions.getFilters()) { if (ActivityInstanceSearchDescriptor.ACTIVITY_TYPE.equals(searchFilter.getField())) { throw new SBonitaReadException( "Invalid query, filtering several times on 'ActivityInstanceSearchDescriptor.ACTIVITY_TYPE' is not supported."); } } } protected Class getEntityClass() { return entityClass; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractArchiveActivityInstanceSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.search.descriptor.SearchArchivedActivityInstanceDescriptor; import org.bonitasoft.engine.search.impl.SearchFilter; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Yanyan Liu * @author Celine Souchet */ public abstract class AbstractArchiveActivityInstanceSearchEntity extends AbstractSearchEntity { private final FlowNodeStateManager flowNodeStateManager; private final Class entityClass; public AbstractArchiveActivityInstanceSearchEntity(final SearchArchivedActivityInstanceDescriptor searchDescriptor, final SearchOptions searchOptions, final FlowNodeStateManager flowNodeStateManager) { super(searchDescriptor, searchOptions); this.flowNodeStateManager = flowNodeStateManager; entityClass = getEntityClass(searchOptions); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toArchivedActivityInstances(serverObjects, flowNodeStateManager); } protected Class getEntityClass(final SearchOptions searchOptions) { Class entityClass = SAActivityInstance.class; final SearchFilter searchFilter = getSearchFilter(searchOptions, ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE); if (searchFilter != null) { final FlowNodeType activityType = (FlowNodeType) searchFilter.getValue(); if (activityType != null) { switch (activityType) { case AUTOMATIC_TASK: entityClass = SAAutomaticTaskInstance.class; break; case MANUAL_TASK: entityClass = SAManualTaskInstance.class; break; case USER_TASK: entityClass = SAUserTaskInstance.class; break; case HUMAN_TASK: entityClass = SAHumanTaskInstance.class; break; case RECEIVE_TASK: entityClass = SAReceiveTaskInstance.class; break; case SEND_TASK: entityClass = SASendTaskInstance.class; break; case CALL_ACTIVITY: entityClass = SACallActivityInstance.class; break; case LOOP_ACTIVITY: entityClass = SALoopActivityInstance.class; break; case MULTI_INSTANCE_ACTIVITY: entityClass = SAMultiInstanceActivityInstance.class; break; case SUB_PROCESS: entityClass = SASubProcessActivityInstance.class; break; default: entityClass = SAActivityInstance.class; break; } searchOptions.getFilters().remove(searchFilter); } } return entityClass; } protected Class getEntityClass() { return entityClass; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractArchivedCommentsSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.bpm.comment.ArchivedComment; import org.bonitasoft.engine.core.process.comment.model.archive.SAComment; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Hongwen Zang */ public abstract class AbstractArchivedCommentsSearchEntity extends AbstractSearchEntity { public AbstractArchivedCommentsSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toArchivedComments(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractArchivedConnectorInstanceSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Baptiste Mesta */ public abstract class AbstractArchivedConnectorInstanceSearchEntity extends AbstractSearchEntity { public AbstractArchivedConnectorInstanceSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toArchivedConnectorInstances(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractArchivedDocumentSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.bpm.document.ArchivedDocument; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.model.archive.SAMappedDocument; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Zhang Bole * @author Baptiste Mesta */ public abstract class AbstractArchivedDocumentSearchEntity extends AbstractSearchEntity { private final DocumentService documentService; public AbstractArchivedDocumentSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options, DocumentService documentService) { super(searchDescriptor, options); this.documentService = documentService; } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toArchivedDocuments(serverObjects, documentService); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractArchivedHumanTaskInstanceSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Baptiste Mesta */ public abstract class AbstractArchivedHumanTaskInstanceSearchEntity extends AbstractSearchEntity { private final FlowNodeStateManager flowNodeStateManager; public AbstractArchivedHumanTaskInstanceSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options, final FlowNodeStateManager flowNodeStateManager) { super(searchDescriptor, options); this.flowNodeStateManager = flowNodeStateManager; } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toArchivedHumanTaskInstances(serverObjects, flowNodeStateManager); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractArchivedProcessInstanceSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Baptiste Mesta * @author Celine Souchet */ public abstract class AbstractArchivedProcessInstanceSearchEntity extends AbstractSearchEntity { private SProcessDefinition sProcessDefinition; private ProcessDefinitionService processDefinitionService; public AbstractArchivedProcessInstanceSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options, final SProcessDefinition sProcessDefinition) { super(searchDescriptor, options); this.sProcessDefinition = sProcessDefinition; } public AbstractArchivedProcessInstanceSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options, final ProcessDefinitionService processDefinitionService) { super(searchDescriptor, options); this.processDefinitionService = processDefinitionService; } @Override public List convertToClientObjects(final List serverObjects) { if (sProcessDefinition != null) { return ModelConvertor.toArchivedProcessInstances(serverObjects, sProcessDefinition); } return ModelConvertor.toArchivedProcessInstances(serverObjects, processDefinitionService); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractCommandSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.command.CommandDescriptor; import org.bonitasoft.engine.command.model.SCommand; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Yanyan Liu */ public abstract class AbstractCommandSearchEntity extends AbstractSearchEntity { public AbstractCommandSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toCommandDescriptors(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractCommentSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.bpm.comment.Comment; import org.bonitasoft.engine.core.process.comment.model.SComment; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Hongwen Zang */ public abstract class AbstractCommentSearchEntity extends AbstractSearchEntity { public AbstractCommentSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toComments(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractDocumentSearchEntity.java ================================================ /** * 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.search; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Zhang Bole */ public abstract class AbstractDocumentSearchEntity extends AbstractSearchEntity { private final DocumentService documentService; public AbstractDocumentSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options, DocumentService documentService) { super(searchDescriptor, options); this.documentService = documentService; } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toDocuments(new ArrayList<>(serverObjects), documentService); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractGroupSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Baptiste Mesta */ public abstract class AbstractGroupSearchEntity extends AbstractSearchEntity { public AbstractGroupSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toGroups(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractHumanTaskInstanceSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Baptiste Mesta */ public abstract class AbstractHumanTaskInstanceSearchEntity extends AbstractSearchEntity { private final FlowNodeStateManager flowNodeStateManager; public AbstractHumanTaskInstanceSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options, final FlowNodeStateManager flowNodeStateManager) { super(searchDescriptor, options); this.flowNodeStateManager = flowNodeStateManager; } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toHumanTaskInstances(serverObjects, flowNodeStateManager); } /** * factory to create a search human task descriptor */ public static AbstractHumanTaskInstanceSearchEntity searchHumanTaskInstance(SearchEntityDescriptor searchDescriptor, SearchOptions options, FlowNodeStateManager flowNodeStateManager, BonitaReadFunction count, BonitaReadFunction> search) { return new HumanTaskInstanceSearchEntity(searchDescriptor, options, flowNodeStateManager, count, search); } private static class HumanTaskInstanceSearchEntity extends AbstractHumanTaskInstanceSearchEntity { private final BonitaReadFunction count; private final BonitaReadFunction> search; HumanTaskInstanceSearchEntity(SearchEntityDescriptor searchDescriptor, SearchOptions options, FlowNodeStateManager flowNodeStateManager, BonitaReadFunction count, BonitaReadFunction> search) { super(searchDescriptor, options, flowNodeStateManager); this.count = count; this.search = search; } @Override public long executeCount(QueryOptions queryOptions) throws SBonitaReadException { return count.apply(queryOptions); } @Override public List executeSearch(QueryOptions queryOptions) throws SBonitaReadException { return search.apply(queryOptions); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractProcessDeploymentInfoSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Baptiste Mesta */ public abstract class AbstractProcessDeploymentInfoSearchEntity extends AbstractSearchEntity { public AbstractProcessDeploymentInfoSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toProcessDeploymentInfo(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractProcessInstanceSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Baptiste Mesta */ public abstract class AbstractProcessInstanceSearchEntity extends AbstractSearchEntity { private final ProcessDefinitionService processDefinitionService; public AbstractProcessInstanceSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options, final ProcessDefinitionService processDefinitionService) { super(searchDescriptor, options); this.processDefinitionService = processDefinitionService; } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toProcessInstances(serverObjects, processDefinitionService); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractProfileSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Celine Souchet */ public abstract class AbstractProfileSearchEntity extends AbstractSearchEntity { public AbstractProfileSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toProfiles(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractRoleSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Baptiste Mesta */ public abstract class AbstractRoleSearchEntity extends AbstractSearchEntity { public AbstractRoleSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toRoles(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractSearchEntity.java ================================================ /** * 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.search; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.transaction.TransactionContentWithResult; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SearchFields; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.search.impl.SearchFilter; import org.bonitasoft.engine.search.impl.SearchResultImpl; /** * Abstract class to allow to search server object and convert them to client object * * @author Matthieu Chaffotte * @author Baptiste Mesta * @author Celine Souchet * @param * The client object * @param * The server object */ @Slf4j public abstract class AbstractSearchEntity implements TransactionContentWithResult> { private final SearchOptions options; private final SearchEntityDescriptor searchDescriptor; private long count; private List clientObjects; /** * @param searchDescriptor * The search descriptor of the searched entity * @param options * The options of the search */ public AbstractSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { this.searchDescriptor = searchDescriptor; this.options = options; } protected void validateQuery(SearchOptions options) throws SBonitaException { /* * Used to validate the query before execution, * in order to throw exceptions with meaningful message. */ } @Override public void execute() throws SBonitaException { validateQuery(options); List serverObjects; if (options == null) { throw new SBonitaReadException("SearchOptions cannot be null"); } final int numberOfResults = options.getMaxResults(); final int fromIndex = options.getStartIndex(); final List filters = options.getFilters(); final List filterOptions = new ArrayList<>(filters.size()); for (final SearchFilter filter : filters) { final FilterOption option = searchDescriptor.getEntityFilter(filter); if (option != null) {// in case of a unknown filter on state filterOptions.add(option); } } final String searchTerm = options.getSearchTerm(); SearchFields userSearchTerm = null; if (searchTerm != null) { userSearchTerm = searchDescriptor.getEntitySearchTerm(searchTerm); } final List orderOptions = new ArrayList<>(); final List sorts = options.getSorts(); for (final Sort sort : sorts) { final OrderByOption order = searchDescriptor.getEntityOrder(sort); orderOptions.add(order); } final QueryOptions countOptions = new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS, null, filterOptions, userSearchTerm); count = executeCount(countOptions); if (count > 0 && numberOfResults != 0) { final QueryOptions searchOptions = new QueryOptions(fromIndex, numberOfResults, orderOptions, filterOptions, userSearchTerm); serverObjects = executeSearch(searchOptions); detectPotentialTransactionIsolationIssue(serverObjects, numberOfResults, countOptions); } else { serverObjects = Collections.emptyList(); } clientObjects = convertToClientObjects(serverObjects); } private void detectPotentialTransactionIsolationIssue(List serverObjects, int numberOfResults, QueryOptions countOptions) throws SBonitaReadException { // If there are at most 1 page of result AND the count does not detect as many objects as the search: if (count <= numberOfResults && count != serverObjects.size()) { long doubleCheck = executeCount(countOptions); if (count != doubleCheck) { log.error("Double checking the same query within the same transaction did NOT bring the same" + " result. You DO have a database transaction isolation problem. Please fix it ASAP. See" + " https://documentation.ofelia.com/bonita/latest/runtime/database-configuration#customize-rdbms" + " for details."); } else { log.warn("Within the same transaction, the Search count & page results are not consistent." + " Please see https://documentation.ofelia.com/bonita/latest/runtime/performance-troubleshooting#monitor-transaction-isolation"); } } } /** * execute this search and return the result * * @return the result of the search */ public SearchResult search() throws SearchException { try { execute(); } catch (SBonitaException e) { throw new SearchException(e); } return getResult(); } /** * Execute the count here * * @param queryOptions * The query options to execute the count with * @return The number of result on the server * @throws SBonitaReadException when the search failed to retrieve the count number */ public abstract long executeCount(QueryOptions queryOptions) throws SBonitaReadException; /** * Execute the search here * * @param queryOptions * The query options to execute the search with * @return The list of searched server objects * @throws SBonitaReadException when the search failed to retrieve the results */ public abstract List executeSearch(QueryOptions queryOptions) throws SBonitaReadException; /** * Must convert server objects in client objects here * * @param serverObjects * The server object to convert * @return The list of the client objects corresponding to the server objects */ public abstract List convertToClientObjects(List serverObjects) throws SBonitaException; @Override public SearchResult getResult() { return new SearchResultImpl<>(count, clientObjects); } protected SearchFilter getSearchFilter(final SearchOptions searchOptions, final String searchedKey) { return searchOptions.getFilters().stream().filter(searchFilter -> searchedKey.equals(searchFilter.getField())) .findFirst().orElse(null); } public static SearchResult search( SearchEntityDescriptor searchDescriptor, SearchOptions options, BonitaReadFunction, List> converter, BonitaReadFunction count, BonitaReadFunction> search) throws SearchException { return new AbstractSearchEntity(searchDescriptor, options) { @Override public long executeCount(QueryOptions queryOptions) throws SBonitaReadException { return count.apply(queryOptions); } @Override public List executeSearch(QueryOptions queryOptions) throws SBonitaReadException { return search.apply(queryOptions); } @Override public List convertToClientObjects(List serverObjects) throws SBonitaException { return converter.apply(serverObjects); } }.search(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractSupervisorSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; /** * @author Baptiste Mesta */ public abstract class AbstractSupervisorSearchEntity extends AbstractSearchEntity { public AbstractSupervisorSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toProcessSupervisors(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/AbstractUserSearchEntity.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Baptiste Mesta */ public abstract class AbstractUserSearchEntity extends AbstractSearchEntity { public AbstractUserSearchEntity(final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toUsers(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/BonitaReadFunction.java ================================================ /** * 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.search; import org.bonitasoft.engine.persistence.SBonitaReadException; @FunctionalInterface public interface BonitaReadFunction { R apply(T t) throws SBonitaReadException; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/SPageOutOfRangeException.java ================================================ /** * 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.search; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Yanyan Liu */ public class SPageOutOfRangeException extends SBonitaException { private static final long serialVersionUID = 7565283595822464464L; public SPageOutOfRangeException(final String message, final Throwable cause) { super(message, cause); } public SPageOutOfRangeException(final String message) { super(message); } public SPageOutOfRangeException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/SSearchException.java ================================================ /** * 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.search; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Emmanuel Duchastenier */ public class SSearchException extends SBonitaException { private static final long serialVersionUID = -938225343187657385L; public SSearchException(final Throwable cause) { super(cause); } public SSearchException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/SearchCommands.java ================================================ /** * 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.search; import java.util.List; import org.bonitasoft.engine.command.CommandService; import org.bonitasoft.engine.command.model.SCommand; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.descriptor.SearchCommandDescriptor; /** * @author Yanyan Liu */ public class SearchCommands extends AbstractCommandSearchEntity { private final CommandService commandService; public SearchCommands(final CommandService commandService, final SearchCommandDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); this.commandService = commandService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return commandService.getNumberOfCommands(searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return commandService.searchCommands(searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/activity/SearchActivityInstances.java ================================================ /** * 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.search.activity; import java.util.List; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractActivityInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchActivityInstanceDescriptor; /** * @author Yanyan Liu * @author Celine Souchet */ public class SearchActivityInstances extends AbstractActivityInstanceSearchEntity { private final ActivityInstanceService activityInstanceService; public SearchActivityInstances(final ActivityInstanceService activityInstanceService, final FlowNodeStateManager flowNodeStateManager, final SearchActivityInstanceDescriptor searchDescriptor, final SearchOptions searchOptions) throws SBonitaReadException { super(searchDescriptor, searchOptions, flowNodeStateManager); this.activityInstanceService = activityInstanceService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return activityInstanceService.getNumberOfActivityInstances(getEntityClass(), searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return activityInstanceService.searchActivityInstances(getEntityClass(), searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/activity/SearchArchivedActivityInstances.java ================================================ /** * 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.search.activity; import java.util.List; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractArchiveActivityInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchArchivedActivityInstanceDescriptor; /** * @author Yanyan Liu * @author Celine Souchet */ public class SearchArchivedActivityInstances extends AbstractArchiveActivityInstanceSearchEntity { private final ActivityInstanceService activityInstanceService; public SearchArchivedActivityInstances(final ActivityInstanceService activityInstanceService, final FlowNodeStateManager flowNodeStateManager, final SearchArchivedActivityInstanceDescriptor searchDescriptor, final SearchOptions searchOptions) { super(searchDescriptor, searchOptions, flowNodeStateManager); this.activityInstanceService = activityInstanceService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return activityInstanceService.getNumberOfArchivedActivityInstances(getEntityClass(), searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return activityInstanceService.searchArchivedActivityInstances(getEntityClass(), searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/comment/SearchArchivedComments.java ================================================ /** * 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.search.comment; import java.util.List; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.comment.model.archive.SAComment; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractArchivedCommentsSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Hongwen Zang * @author Celine Souchet */ public class SearchArchivedComments extends AbstractArchivedCommentsSearchEntity { private final SCommentService sCommentService; public SearchArchivedComments(final SCommentService sCommentService, final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); this.sCommentService = sCommentService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return sCommentService.getNumberOfArchivedComments(searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return sCommentService.searchArchivedComments(searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/comment/SearchComments.java ================================================ /** * 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.search.comment; import java.util.List; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.comment.model.SComment; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractCommentSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchCommentDescriptor; /** * @author Hongwen Zang */ public class SearchComments extends AbstractCommentSearchEntity { private final SCommentService commentService; public SearchComments(SearchCommentDescriptor searchDescriptor, SearchOptions options, SCommentService commentService) { super(searchDescriptor, options); this.commentService = commentService; } @Override public long executeCount(QueryOptions searchOptions) throws SBonitaReadException { return commentService.getNumberOfComments(searchOptions); } @Override public List executeSearch(QueryOptions searchOptions) throws SBonitaReadException { return commentService.searchComments(searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/comment/SearchCommentsInvolvingUser.java ================================================ /** * 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.search.comment; import java.util.List; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.comment.model.SComment; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractCommentSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Hongwen Zang */ public class SearchCommentsInvolvingUser extends AbstractCommentSearchEntity { private final SCommentService commentService; private final long userId; public SearchCommentsInvolvingUser(SearchEntityDescriptor searchDescriptor, SearchOptions options, SCommentService commentService, long userId) { super(searchDescriptor, options); this.commentService = commentService; this.userId = userId; } @Override public long executeCount(QueryOptions searchOptions) throws SBonitaReadException { return commentService.getNumberOfCommentsInvolvingUser(userId, searchOptions); } @Override public List executeSearch(QueryOptions searchOptions) throws SBonitaReadException { return commentService.searchCommentsInvolvingUser(userId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/comment/SearchCommentsManagedBy.java ================================================ /** * 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.search.comment; import java.util.List; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.comment.model.SComment; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractCommentSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Hongwen Zang */ public class SearchCommentsManagedBy extends AbstractCommentSearchEntity { private final SCommentService commentService; private final long managerUserId; public SearchCommentsManagedBy(SearchEntityDescriptor searchDescriptor, SearchOptions options, SCommentService commentService, long managerUserId) { super(searchDescriptor, options); this.commentService = commentService; this.managerUserId = managerUserId; } @Override public long executeCount(QueryOptions searchOptions) throws SBonitaReadException { return commentService.getNumberOfCommentsManagedBy(managerUserId, searchOptions); } @Override public List executeSearch(QueryOptions searchOptions) throws SBonitaReadException { return commentService.searchCommentsManagedBy(managerUserId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/connector/SearchArchivedConnectorInstance.java ================================================ /** * 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.search.connector; import java.util.List; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractArchivedConnectorInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Baptiste Mesta */ public class SearchArchivedConnectorInstance extends AbstractArchivedConnectorInstanceSearchEntity { private final ReadPersistenceService persistenceService; private final ConnectorInstanceService connectorInstanceService; public SearchArchivedConnectorInstance(final ConnectorInstanceService connectorInstanceService, final SearchEntityDescriptor searchDescriptor, final SearchOptions options, final ReadPersistenceService persistenceService) { super(searchDescriptor, options); this.connectorInstanceService = connectorInstanceService; this.persistenceService = persistenceService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return connectorInstanceService.getNumberArchivedConnectorInstance(searchOptions, persistenceService); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return connectorInstanceService.searchArchivedConnectorInstance(searchOptions, persistenceService); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/FieldDescriptor.java ================================================ /** * 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.search.descriptor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Matthieu Chaffotte */ public class FieldDescriptor { private final Class persitentClass; private final String value; public FieldDescriptor(final Class persitentClass, final String value) { super(); this.persitentClass = persitentClass; this.value = value; } public Class getPersistentClass() { return persitentClass; } public String getValue() { return value; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchActivityInstanceDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; /** * @author Yanyan Liu * @author Elias Ricken de Medeiros */ public class SearchActivityInstanceDescriptor extends SearchEntityDescriptor { private final Map activityInstanceDescriptorKeys; private final Map, Set> activityInstanceDescriptorAllFields; public SearchActivityInstanceDescriptor() { final SUserTaskInstanceBuilderFactory keyProvider = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class); activityInstanceDescriptorKeys = new HashMap<>(); activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.NAME, new FieldDescriptor(SActivityInstance.class, keyProvider.getNameKey())); activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.STATE_NAME, new FieldDescriptor(SActivityInstance.class, keyProvider.getStateNameKey())); activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, new FieldDescriptor(SActivityInstance.class, keyProvider.getProcessDefinitionKey())); activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, new FieldDescriptor(SActivityInstance.class, keyProvider.getRootProcessInstanceKey())); activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID, new FieldDescriptor(SActivityInstance.class, keyProvider.getParentActivityInstanceKey())); activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, new FieldDescriptor(SActivityInstance.class, keyProvider.getParentProcessInstanceKey())); activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.PARENT_CONTAINER_ID, new FieldDescriptor(SActivityInstance.class, keyProvider.getParentContainerIdKey())); activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.LAST_MODIFICATION_DATE, new FieldDescriptor(SActivityInstance.class, keyProvider.getLastUpdateDateKey())); activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.DISPLAY_NAME, new FieldDescriptor(SActivityInstance.class, keyProvider.getDisplayNameKey())); activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.USER_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.USER_ID_KEY)); activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.GROUP_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.GROUP_ID_KEY)); activityInstanceDescriptorKeys.put(ActivityInstanceSearchDescriptor.ROLE_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.ROLE_ID_KEY)); activityInstanceDescriptorAllFields = new HashMap<>(); final Set tasksInstanceFields = new HashSet<>(); tasksInstanceFields.add(keyProvider.getNameKey()); tasksInstanceFields.add(keyProvider.getDisplayNameKey()); activityInstanceDescriptorAllFields.put(SActivityInstance.class, tasksInstanceFields); } @Override protected Map getEntityKeys() { return activityInstanceDescriptorKeys; } @Override protected Map, Set> getAllFields() { return activityInstanceDescriptorAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchApplicationDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.business.application.ApplicationSearchDescriptor; import org.bonitasoft.engine.business.application.model.AbstractSApplication; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Elias Ricken de Medeiros */ public class SearchApplicationDescriptor extends SearchEntityDescriptor { public static final String APPLICATION_VISIBILITY = "visibility"; private final Map keys; private final Map, Set> allFields; protected SearchApplicationDescriptor() { keys = new HashMap<>(13); keys.put(ApplicationSearchDescriptor.ID, new FieldDescriptor(SApplication.class, AbstractSApplication.ID)); keys.put(ApplicationSearchDescriptor.TOKEN, new FieldDescriptor(SApplication.class, AbstractSApplication.TOKEN)); keys.put(ApplicationSearchDescriptor.DISPLAY_NAME, new FieldDescriptor(SApplication.class, AbstractSApplication.DISPLAY_NAME)); keys.put(ApplicationSearchDescriptor.VERSION, new FieldDescriptor(SApplication.class, AbstractSApplication.VERSION)); keys.put(ApplicationSearchDescriptor.ICON_PATH, new FieldDescriptor(SApplication.class, AbstractSApplication.ICON_PATH)); keys.put(ApplicationSearchDescriptor.CREATION_DATE, new FieldDescriptor(SApplication.class, AbstractSApplication.CREATION_DATE)); keys.put(ApplicationSearchDescriptor.CREATED_BY, new FieldDescriptor(SApplication.class, AbstractSApplication.CREATED_BY)); keys.put(ApplicationSearchDescriptor.LAST_UPDATE_DATE, new FieldDescriptor(SApplication.class, AbstractSApplication.LAST_UPDATE_DATE)); keys.put(ApplicationSearchDescriptor.UPDATED_BY, new FieldDescriptor(SApplication.class, AbstractSApplication.UPDATED_BY)); keys.put(ApplicationSearchDescriptor.STATE, new FieldDescriptor(SApplication.class, AbstractSApplication.STATE)); keys.put(ApplicationSearchDescriptor.PROFILE_ID, new FieldDescriptor(SApplication.class, AbstractSApplication.PROFILE_ID)); keys.put(ApplicationSearchDescriptor.LAYOUT_ID, new FieldDescriptor(SApplication.class, AbstractSApplication.LAYOUT_ID)); keys.put(ApplicationSearchDescriptor.THEME_ID, new FieldDescriptor(SApplication.class, AbstractSApplication.THEME_ID)); // internal usage only for now (as it would require a conversion of the Visibility enum): keys.put(APPLICATION_VISIBILITY, new FieldDescriptor(SApplication.class, AbstractSApplication.INTERNAL_PROFILE)); allFields = new HashMap<>(1); final Set pageFields = new HashSet<>(5); pageFields.add(AbstractSApplication.TOKEN); pageFields.add(AbstractSApplication.DISPLAY_NAME); pageFields.add(AbstractSApplication.VERSION); pageFields.add(AbstractSApplication.ICON_PATH); pageFields.add(AbstractSApplication.STATE); allFields.put(SApplication.class, pageFields); } @Override protected Map getEntityKeys() { return keys; } @Override protected Map, Set> getAllFields() { return allFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchApplicationMenuDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.business.application.ApplicationMenuSearchDescriptor; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Elias Ricken de Medeiros */ public class SearchApplicationMenuDescriptor extends SearchEntityDescriptor { private final Map keys; private final Map, Set> allFields; SearchApplicationMenuDescriptor() { keys = new HashMap<>(6); keys.put(ApplicationMenuSearchDescriptor.ID, new FieldDescriptor(SApplicationMenu.class, SApplicationMenu.ID)); keys.put(ApplicationMenuSearchDescriptor.APPLICATION_PAGE_ID, new FieldDescriptor(SApplicationMenu.class, SApplicationMenu.APPLICATION_PAGE_ID)); keys.put(ApplicationMenuSearchDescriptor.DISPLAY_NAME, new FieldDescriptor(SApplicationMenu.class, SApplicationMenu.DISPLAY_NAME)); keys.put(ApplicationMenuSearchDescriptor.INDEX, new FieldDescriptor(SApplicationMenu.class, SApplicationMenu.INDEX)); keys.put(ApplicationMenuSearchDescriptor.APPLICATION_ID, new FieldDescriptor(SApplicationMenu.class, SApplicationMenu.APPLICAITON_ID)); keys.put(ApplicationMenuSearchDescriptor.PARENT_ID, new FieldDescriptor(SApplicationMenu.class, SApplicationMenu.PARENT_ID)); allFields = new HashMap<>(1); final Set pageFields = new HashSet<>(1); pageFields.add(SApplicationMenu.DISPLAY_NAME); allFields.put(SApplicationMenu.class, pageFields); } @Override protected Map getEntityKeys() { return keys; } @Override protected Map, Set> getAllFields() { return allFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchApplicationPageDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.business.application.ApplicationPageSearchDescriptor; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Elias Ricken de Medeiros */ public class SearchApplicationPageDescriptor extends SearchEntityDescriptor { private final Map keys; private final Map, Set> allFields; SearchApplicationPageDescriptor() { keys = new HashMap<>(4); keys.put(ApplicationPageSearchDescriptor.ID, new FieldDescriptor(SApplicationPage.class, SApplicationPage.ID)); keys.put(ApplicationPageSearchDescriptor.TOKEN, new FieldDescriptor(SApplicationPage.class, SApplicationPage.TOKEN)); keys.put(ApplicationPageSearchDescriptor.APPLICATION_ID, new FieldDescriptor(SApplicationPage.class, SApplicationPage.APPLICATION_ID)); keys.put(ApplicationPageSearchDescriptor.PAGE_ID, new FieldDescriptor(SApplicationPage.class, SApplicationPage.PAGE_ID)); allFields = new HashMap<>(1); final Set pageFields = new HashSet<>(1); pageFields.add(SApplicationPage.TOKEN); allFields.put(SApplicationPage.class, pageFields); } @Override protected Map getEntityKeys() { return keys; } @Override protected Map, Set> getAllFields() { return allFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchArchivedActivityInstanceDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Yanyan Liu * @author Celine Souchet */ public class SearchArchivedActivityInstanceDescriptor extends SearchEntityDescriptor { private final Map archivedActivityInstanceDescriptorKeys; private final Map, Set> archivedActivityInstanceDescriptorAllFields; public SearchArchivedActivityInstanceDescriptor() { final SAUserTaskInstanceBuilderFactory keyProvider = BuilderFactory.get(SAUserTaskInstanceBuilderFactory.class); archivedActivityInstanceDescriptorKeys = new HashMap<>(10); archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.NAME, new FieldDescriptor(SAActivityInstance.class, keyProvider.getNameKey())); archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.PRIORITY, new FieldDescriptor(SAActivityInstance.class, keyProvider.getPriorityKey())); archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID, new FieldDescriptor( SAActivityInstance.class, keyProvider.getProcessDefinitionKey())); archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, new FieldDescriptor( SAActivityInstance.class, keyProvider.getRootProcessInstanceKey())); archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, new FieldDescriptor( SAActivityInstance.class, keyProvider.getParentProcessInstanceKey())); archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID, new FieldDescriptor( SAActivityInstance.class, keyProvider.getParentActivityInstanceKey())); archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.STATE_NAME, new FieldDescriptor(SAActivityInstance.class, keyProvider.getStateNameKey())); archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.ASSIGNEE_ID, new FieldDescriptor(SAActivityInstance.class, keyProvider.getAssigneeIdKey())); archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.DISPLAY_NAME, new FieldDescriptor(SAActivityInstance.class, keyProvider.getDisplayNameKey())); archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.REACHED_STATE_DATE, new FieldDescriptor(SAActivityInstance.class, keyProvider.getReachedStateDateKey())); archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.SOURCE_OBJECT_ID, new FieldDescriptor(SAActivityInstance.class, keyProvider.getSourceObjectIdKey())); archivedActivityInstanceDescriptorKeys.put(ArchivedActivityInstanceSearchDescriptor.ARCHIVE_DATE, new FieldDescriptor(SAActivityInstance.class, keyProvider.getArchivedDateKey())); archivedActivityInstanceDescriptorAllFields = new HashMap<>(1); final Set humanFields = new HashSet(2); humanFields.add(keyProvider.getNameKey()); humanFields.add(keyProvider.getDisplayNameKey()); archivedActivityInstanceDescriptorAllFields.put(SAActivityInstance.class, humanFields); } @Override protected Map getEntityKeys() { return archivedActivityInstanceDescriptorKeys; } @Override protected Map, Set> getAllFields() { return archivedActivityInstanceDescriptorAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchArchivedCommentsDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.comment.ArchivedCommentsSearchDescriptor; import org.bonitasoft.engine.core.process.comment.model.archive.SAComment; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Hongwen Zang * @author Celine Souchet */ public class SearchArchivedCommentsDescriptor extends SearchEntityDescriptor { private final Map searchEntityKeys; private final Map, Set> archivedCommentsAllFields; SearchArchivedCommentsDescriptor() { searchEntityKeys = new HashMap<>(7); searchEntityKeys.put(ArchivedCommentsSearchDescriptor.PROCESS_INSTANCE_ID, new FieldDescriptor(SAComment.class, SAComment.PROCESSINSTANCEID_KEY)); searchEntityKeys.put(ArchivedCommentsSearchDescriptor.POSTED_BY_ID, new FieldDescriptor(SAComment.class, SAComment.USERID_KEY)); searchEntityKeys.put(ArchivedCommentsSearchDescriptor.ID, new FieldDescriptor(SAComment.class, SAComment.ID_KEY)); searchEntityKeys.put(ArchivedCommentsSearchDescriptor.POSTDATE, new FieldDescriptor(SAComment.class, SAComment.POSTDATE_KEY)); searchEntityKeys.put(ArchivedCommentsSearchDescriptor.SOURCE_OBJECT_ID, new FieldDescriptor(SAComment.class, SAComment.SOURCEOBJECTID_KEY)); searchEntityKeys.put(ArchivedCommentsSearchDescriptor.CONTENT, new FieldDescriptor(SAComment.class, SAComment.CONTENT_KEY)); searchEntityKeys.put(ArchivedCommentsSearchDescriptor.USER_NAME, new FieldDescriptor(SUser.class, SUser.USER_NAME)); archivedCommentsAllFields = new HashMap<>(1); final Set archivedCommentFields = new HashSet<>(1); archivedCommentFields.add(SAComment.CONTENT_KEY); archivedCommentsAllFields.put(SAComment.class, archivedCommentFields); } @Override protected Map getEntityKeys() { return searchEntityKeys; } @Override protected Map, Set> getAllFields() { return archivedCommentsAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchArchivedConnectorInstanceDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.connector.ArchiveConnectorInstancesSearchDescriptor; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAConnectorInstanceBuilderFactory; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Baptiste Mesta */ public class SearchArchivedConnectorInstanceDescriptor extends SearchEntityDescriptor { private final Map searchEntityKeys; private final Map, Set> archivedConnectorssAllFields; SearchArchivedConnectorInstanceDescriptor() { final SAConnectorInstanceBuilderFactory keyProvider = BuilderFactory .get(SAConnectorInstanceBuilderFactory.class); searchEntityKeys = new HashMap(7); searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.NAME, new FieldDescriptor(SAConnectorInstance.class, keyProvider.getNameKey())); searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.ACTIVATION_EVENT, new FieldDescriptor(SAConnectorInstance.class, keyProvider.getActivationEventKey())); searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.CONNECTOR_DEFINITION_ID, new FieldDescriptor(SAConnectorInstance.class, keyProvider.getConnectorIdKey())); searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.CONNECTOR_DEFINITION_VERSION, new FieldDescriptor(SAConnectorInstance.class, keyProvider.getVersionKey())); searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.CONTAINER_ID, new FieldDescriptor(SAConnectorInstance.class, keyProvider.getContainerIdKey())); searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.CONTAINER_TYPE, new FieldDescriptor(SAConnectorInstance.class, keyProvider.getContainerTypeKey())); searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.STATE, new FieldDescriptor(SAConnectorInstance.class, keyProvider.getStateKey())); searchEntityKeys.put(ArchiveConnectorInstancesSearchDescriptor.SOURCE_OBJECT_ID, new FieldDescriptor(SAConnectorInstance.class, keyProvider.getSourceObjectIdKey())); archivedConnectorssAllFields = new HashMap, Set>(1); final Set connectorFields = new HashSet(2); connectorFields.add(keyProvider.getNameKey()); connectorFields.add(keyProvider.getConnectorIdKey()); archivedConnectorssAllFields.put(SAConnectorInstance.class, connectorFields); } @Override protected Map getEntityKeys() { return searchEntityKeys; } @Override protected Map, Set> getAllFields() { return archivedConnectorssAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchArchivedDocumentDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.document.ArchivedDocumentsSearchDescriptor; import org.bonitasoft.engine.core.document.model.archive.SADocumentMapping; import org.bonitasoft.engine.core.document.model.archive.SAMappedDocument; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Zhang Bole * @author Matthieu Chaffotte */ public class SearchArchivedDocumentDescriptor extends SearchEntityDescriptor { private final Map searchEntityKeys; private final Map, Set> documentAllFields; SearchArchivedDocumentDescriptor() { searchEntityKeys = new HashMap<>(12); searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.ARCHIVE_DATE, new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.ARCHIVE_DATE)); searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_AUTHOR, new FieldDescriptor(SAMappedDocument.class, "document." + SADocumentMapping.AUTHOR)); searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_CONTENT_FILENAME, new FieldDescriptor(SAMappedDocument.class, "document." + SADocumentMapping.FILE_NAME)); searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_CONTENT_MIMETYPE, new FieldDescriptor(SAMappedDocument.class, "document." + SADocumentMapping.MIME_TYPE)); searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_CREATIONDATE, new FieldDescriptor(SAMappedDocument.class, "document." + SADocumentMapping.CREATION_DATE)); searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_HAS_CONTENT, new FieldDescriptor(SAMappedDocument.class, "document." + SADocumentMapping.HAS_CONTENT)); searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME, new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.NAME)); searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_DESCRIPTION, new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.DESCRIPTION)); searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_VERSION, new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.VERSION)); searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.LIST_INDEX, new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.INDEX)); searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.DOCUMENT_URL, new FieldDescriptor(SAMappedDocument.class, "document." + SADocumentMapping.URL)); searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.PROCESS_INSTANCE_ID)); searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.SOURCEOBJECT_ID, new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.SOURCE_OBJECT_ID)); searchEntityKeys.put(ArchivedDocumentsSearchDescriptor.CONTENT_STORAGE_ID, new FieldDescriptor(SAMappedDocument.class, SADocumentMapping.DOCUMENT_ID)); documentAllFields = new HashMap<>(1); final Set documentFields = new HashSet(7); documentFields.add(SADocumentMapping.NAME); documentFields.add(SADocumentMapping.DESCRIPTION); documentFields.add("document." + SADocumentMapping.FILE_NAME); documentFields.add("document." + SADocumentMapping.MIME_TYPE); documentFields.add("document." + SADocumentMapping.URL); documentAllFields.put(SAMappedDocument.class, documentFields); } @Override protected Map getEntityKeys() { return searchEntityKeys; } @Override protected Map, Set> getAllFields() { return documentAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchArchivedFlowNodeInstanceDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowNodeInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Emmanuel Duchastenier * @author Celine Souchet */ public class SearchArchivedFlowNodeInstanceDescriptor extends SearchEntityDescriptor { private final Map archFlowNodeDescriptorKeys; private final Map, Set> flowNodeInstanceDescriptorAllFields; public SearchArchivedFlowNodeInstanceDescriptor() { final SAFlowNodeInstanceBuilderFactory keyProvider = BuilderFactory.get(SAUserTaskInstanceBuilderFactory.class); archFlowNodeDescriptorKeys = new HashMap(13); archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.NAME, new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getNameKey())); archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME, new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getStateNameKey())); archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.PROCESS_DEFINITION_ID, new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getProcessDefinitionKey())); archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getParentProcessInstanceKey())); archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID, new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getParentActivityInstanceKey())); archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getRootProcessInstanceKey())); archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.DISPLAY_NAME, new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getDisplayNameKey())); archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.FLOW_NODE_TYPE, new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getKindKey())); archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.ORIGINAL_FLOW_NODE_ID, new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getSourceObjectIdKey())); archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL, new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getTerminalKey())); archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.REACHED_STATE_DATE, new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getReachedStateDateKey())); archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.ARCHIVE_DATE, new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getArchivedDateKey())); archFlowNodeDescriptorKeys.put(ArchivedFlowNodeInstanceSearchDescriptor.STATE_ID, new FieldDescriptor(SAFlowNodeInstance.class, keyProvider.getStateIdKey())); final Set tasksInstanceFields = new HashSet(2); tasksInstanceFields.add(keyProvider.getNameKey()); tasksInstanceFields.add(keyProvider.getDisplayNameKey()); flowNodeInstanceDescriptorAllFields = new HashMap, Set>(1); flowNodeInstanceDescriptorAllFields.put(SAFlowNodeInstance.class, tasksInstanceFields); } @Override protected Map getEntityKeys() { return archFlowNodeDescriptorKeys; } @Override protected Map, Set> getAllFields() { return flowNodeInstanceDescriptorAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchArchivedHumanTaskInstanceDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Julien Mege * @author Matthieu Chaffotte */ public class SearchArchivedHumanTaskInstanceDescriptor extends SearchEntityDescriptor { private final Map entityKeys; private final Map, Set> humanTaskInstanceAllFields; public SearchArchivedHumanTaskInstanceDescriptor() { final SAUserTaskInstanceBuilderFactory keyProvider = BuilderFactory.get(SAUserTaskInstanceBuilderFactory.class); entityKeys = new HashMap(10); entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.NAME, new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getNameKey())); entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.PRIORITY, new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getPriorityKey())); entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getProcessDefinitionKey())); entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getRootProcessInstanceKey())); entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getParentProcessInstanceKey())); entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.ORIGINAL_HUMAN_TASK_ID, new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getSourceObjectIdKey())); entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID, new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getParentActivityInstanceKey())); entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.STATE_NAME, new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getStateNameKey())); entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getAssigneeIdKey())); entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.DISPLAY_NAME, new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getDisplayNameKey())); entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.REACHED_STATE_DATE, new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getReachedStateDateKey())); entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.TERMINAL, new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getTerminalKey())); entityKeys.put(ArchivedHumanTaskInstanceSearchDescriptor.ARCHIVE_DATE, new FieldDescriptor(SAHumanTaskInstance.class, keyProvider.getArchivedDateKey())); humanTaskInstanceAllFields = new HashMap, Set>(1); final Set humanFields = new HashSet(2); humanFields.add(keyProvider.getNameKey()); humanFields.add(keyProvider.getDisplayNameKey()); humanTaskInstanceAllFields.put(SAHumanTaskInstance.class, humanFields); } @Override protected Map getEntityKeys() { return entityKeys; } @Override protected Map, Set> getAllFields() { return humanTaskInstanceAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchArchivedProcessInstancesDescriptor.java ================================================ /** * 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.search.descriptor; import static org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor.*; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; /** * @author Yanyan Liu * @author Matthieu Chaffotte * @author Celine Souchet */ public class SearchArchivedProcessInstancesDescriptor extends SearchEntityDescriptor { protected final Map searchEntityKeys; protected final Map, Set> archivedProcessInstanceAllFields; protected final Set processInstanceFields; public SearchArchivedProcessInstancesDescriptor() { final SAProcessInstanceBuilderFactory instanceBuilder = BuilderFactory .get(SAProcessInstanceBuilderFactory.class); final SUserTaskInstanceBuilderFactory sUserTaskInstanceBuilder = BuilderFactory .get(SUserTaskInstanceBuilderFactory.class); searchEntityKeys = new HashMap<>(); searchEntityKeys.put(NAME, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getNameKey())); searchEntityKeys.put(PROCESS_DEFINITION_ID, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getProcessDefinitionIdKey())); searchEntityKeys.put(ID, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getIdKey())); searchEntityKeys.put(ROOT_PROCESS_INSTANCE_ID, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getRootProcessInstanceIdKey())); searchEntityKeys.put(STARTED_BY, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getStartedByKey())); searchEntityKeys.put(STARTED_BY_SUBSTITUTE, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getStartedBySubstituteKey())); searchEntityKeys.put(START_DATE, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getStartDateKey())); searchEntityKeys.put(END_DATE, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getEndDateKey())); searchEntityKeys.put(STATE_ID, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getStateIdKey())); searchEntityKeys.put(SOURCE_OBJECT_ID, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getSourceObjectIdKey())); searchEntityKeys.put(LAST_UPDATE, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getLastUpdateKey())); searchEntityKeys.put(ARCHIVE_DATE, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getArchiveDateKey())); searchEntityKeys.put(CALLER_ID, new FieldDescriptor(SAProcessInstance.class, instanceBuilder.getCallerIdKey())); searchEntityKeys .put(USER_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.USER_ID_KEY)); searchEntityKeys.put(GROUP_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.GROUP_ID_KEY)); searchEntityKeys .put(ROLE_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.ROLE_ID_KEY)); searchEntityKeys.put(ASSIGNEE_ID, new FieldDescriptor(SUserTaskInstance.class, sUserTaskInstanceBuilder.getAssigneeIdKey())); searchEntityKeys.put(STRING_INDEX_1, new FieldDescriptor(SAProcessInstance.class, STRING_INDEX_1)); searchEntityKeys.put(STRING_INDEX_2, new FieldDescriptor(SAProcessInstance.class, STRING_INDEX_2)); searchEntityKeys.put(STRING_INDEX_3, new FieldDescriptor(SAProcessInstance.class, STRING_INDEX_3)); searchEntityKeys.put(STRING_INDEX_4, new FieldDescriptor(SAProcessInstance.class, STRING_INDEX_4)); searchEntityKeys.put(STRING_INDEX_5, new FieldDescriptor(SAProcessInstance.class, STRING_INDEX_5)); archivedProcessInstanceAllFields = new HashMap<>(); processInstanceFields = new HashSet<>(); processInstanceFields.add(instanceBuilder.getNameKey()); processInstanceFields.add(STRING_INDEX_1); processInstanceFields.add(STRING_INDEX_2); processInstanceFields.add(STRING_INDEX_3); processInstanceFields.add(STRING_INDEX_4); processInstanceFields.add(STRING_INDEX_5); archivedProcessInstanceAllFields.put(SAProcessInstance.class, processInstanceFields); } @Override protected Map getEntityKeys() { return searchEntityKeys; } @Override protected Map, Set> getAllFields() { return archivedProcessInstanceAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchCommandDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.command.CommandSearchDescriptor; import org.bonitasoft.engine.command.model.SCommand; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Yanyan Liu * @author Matthieu Chaffotte */ public class SearchCommandDescriptor extends SearchEntityDescriptor { private final Map commandKeys; private final Map, Set> commandAllFields; SearchCommandDescriptor() { commandKeys = new HashMap(5); commandKeys.put(CommandSearchDescriptor.ID, new FieldDescriptor(SCommand.class, SCommand.ID)); commandKeys.put(CommandSearchDescriptor.NAME, new FieldDescriptor(SCommand.class, SCommand.NAME)); commandKeys.put(CommandSearchDescriptor.DESCRIPTION, new FieldDescriptor(SCommand.class, SCommand.DESCRIPTION)); commandKeys.put(CommandSearchDescriptor.IMPLEMENTATION, new FieldDescriptor(SCommand.class, SCommand.IMPLEMENTATION)); commandKeys.put(CommandSearchDescriptor.SYSTEM, new FieldDescriptor(SCommand.class, SCommand.SYSTEM)); commandAllFields = new HashMap, Set>(1); final Set commandFields = new HashSet(5); commandFields.add(SCommand.NAME); commandFields.add(SCommand.DESCRIPTION); commandFields.add(SCommand.IMPLEMENTATION); commandAllFields.put(SCommand.class, commandFields); } @Override protected Map getEntityKeys() { return commandKeys; } @Override protected Map, Set> getAllFields() { return commandAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchCommentDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor; import org.bonitasoft.engine.core.process.comment.model.SComment; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Hongwen Zang * @author Matthieu Chaffotte */ public class SearchCommentDescriptor extends SearchEntityDescriptor { private final Map commentKeys; private final Map, Set> commentAllFields; public SearchCommentDescriptor() { commentKeys = new HashMap<>(); commentKeys.put(SearchCommentsDescriptor.PROCESS_INSTANCE_ID, new FieldDescriptor(SComment.class, SComment.PROCESSINSTANCEID_KEY)); commentKeys.put(SearchCommentsDescriptor.POSTED_BY_ID, new FieldDescriptor(SComment.class, SComment.USERID_KEY)); commentKeys.put(SearchCommentsDescriptor.ID, new FieldDescriptor(SComment.class, SComment.ID_KEY)); commentKeys.put(SearchCommentsDescriptor.POSTDATE, new FieldDescriptor(SComment.class, SComment.POSTDATE_KEY)); commentKeys.put(SearchCommentsDescriptor.CONTENT, new FieldDescriptor(SComment.class, SComment.CONTENT_KEY)); commentKeys.put(SearchCommentsDescriptor.USER_NAME, new FieldDescriptor(SUser.class, SUser.USER_NAME)); commentAllFields = new HashMap<>(); final Set commentFields = new HashSet<>(); commentFields.add(SComment.CONTENT_KEY); commentAllFields.put(SComment.class, commentFields); } @Override protected Map getEntityKeys() { return commentKeys; } @Override protected Map, Set> getAllFields() { return commentAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchConnectorInstanceDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.connector.ConnectorInstancesSearchDescriptor; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Baptiste Mesta */ public class SearchConnectorInstanceDescriptor extends SearchEntityDescriptor { private final Map searchEntityKeys; private final Map, Set> connectorInstanceAllFields; SearchConnectorInstanceDescriptor() { searchEntityKeys = new HashMap<>(7); searchEntityKeys.put(ConnectorInstancesSearchDescriptor.NAME, new FieldDescriptor(SConnectorInstance.class, SConnectorInstance.NAME_KEY)); searchEntityKeys.put(ConnectorInstancesSearchDescriptor.ACTIVATION_EVENT, new FieldDescriptor(SConnectorInstance.class, SConnectorInstance.ACTIVATION_EVENT_KEY)); searchEntityKeys.put(ConnectorInstancesSearchDescriptor.CONNECTOR_DEFINITION_ID, new FieldDescriptor(SConnectorInstance.class, SConnectorInstance.CONNECTOR_ID_KEY)); searchEntityKeys.put(ConnectorInstancesSearchDescriptor.CONNECTOR_DEFINITION_VERSION, new FieldDescriptor(SConnectorInstance.class, SConnectorInstance.VERSION_KEY)); searchEntityKeys.put(ConnectorInstancesSearchDescriptor.CONTAINER_ID, new FieldDescriptor(SConnectorInstance.class, SConnectorInstance.CONTAINER_ID_KEY)); searchEntityKeys.put(ConnectorInstancesSearchDescriptor.CONTAINER_TYPE, new FieldDescriptor(SConnectorInstance.class, SConnectorInstance.CONTAINER_TYPE_KEY)); searchEntityKeys.put(ConnectorInstancesSearchDescriptor.STATE, new FieldDescriptor(SConnectorInstance.class, SConnectorInstance.STATE_KEY)); searchEntityKeys.put(ConnectorInstancesSearchDescriptor.EXECUTION_ORDER, new FieldDescriptor(SConnectorInstance.class, SConnectorInstance.EXECUTION_ORDER)); connectorInstanceAllFields = new HashMap<>(1); final Set connectorFields = new HashSet(2); connectorFields.add(SConnectorInstance.NAME_KEY); connectorFields.add(SConnectorInstance.CONNECTOR_ID_KEY); connectorInstanceAllFields.put(SConnectorInstance.class, connectorFields); } @Override protected Map getEntityKeys() { return searchEntityKeys; } @Override protected Map, Set> getAllFields() { return connectorInstanceAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchCustomUserInfoValueDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.identity.CustomUserInfoValueSearchDescriptor; import org.bonitasoft.engine.identity.model.SCustomUserInfoValue; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Vincent Elcrin */ public class SearchCustomUserInfoValueDescriptor extends SearchEntityDescriptor { private final Map searchableKeys; private final Map, Set> allFields; public SearchCustomUserInfoValueDescriptor() { searchableKeys = new HashMap(3); searchableKeys.put(CustomUserInfoValueSearchDescriptor.DEFINITION_ID, new FieldDescriptor(SCustomUserInfoValue.class, SCustomUserInfoValue.DEFINITION_ID)); searchableKeys.put(CustomUserInfoValueSearchDescriptor.USER_ID, new FieldDescriptor(SCustomUserInfoValue.class, SCustomUserInfoValue.USER_ID)); searchableKeys.put(CustomUserInfoValueSearchDescriptor.VALUE, new FieldDescriptor(SCustomUserInfoValue.class, SCustomUserInfoValue.VALUE)); allFields = new HashMap, Set>(1); final Set fields = new HashSet(2); fields.add(SCustomUserInfoValue.DEFINITION_ID); fields.add(SCustomUserInfoValue.USER_ID); fields.add(SCustomUserInfoValue.VALUE); allFields.put(SConnectorInstance.class, fields); } @Override protected Map getEntityKeys() { return searchableKeys; } @Override protected Map, Set> getAllFields() { return allFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchDocumentDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.document.DocumentsSearchDescriptor; import org.bonitasoft.engine.core.document.model.SDocument; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Zhang Bole * @author Baptiste Mesta */ public class SearchDocumentDescriptor extends SearchEntityDescriptor { public static final String DOCUMENT_PREFIX = "document."; private final Map searchEntityKeys; private final Map, Set> documentAllFields; SearchDocumentDescriptor() { searchEntityKeys = new HashMap<>(9); searchEntityKeys.put(DocumentsSearchDescriptor.CONTENT_STORAGE_ID, new FieldDescriptor(SMappedDocument.class, "documentId")); searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_AUTHOR, new FieldDescriptor(SMappedDocument.class, DOCUMENT_PREFIX + SDocument.AUTHOR)); searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_CONTENT_FILENAME, new FieldDescriptor(SMappedDocument.class, DOCUMENT_PREFIX + SDocument.FILENAME)); searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_CONTENT_MIMETYPE, new FieldDescriptor(SMappedDocument.class, DOCUMENT_PREFIX + SDocument.MIMETYPE)); searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_CREATIONDATE, new FieldDescriptor(SMappedDocument.class, DOCUMENT_PREFIX + SDocument.CREATION_DATE)); searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_HAS_CONTENT, new FieldDescriptor(SMappedDocument.class, DOCUMENT_PREFIX + SDocument.HAS_CONTENT)); searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_NAME, new FieldDescriptor(SMappedDocument.class, SDocument.NAME)); searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_DESCRIPTION, new FieldDescriptor(SMappedDocument.class, SDocument.DESCRIPTION)); searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_VERSION, new FieldDescriptor(SMappedDocument.class, SDocument.VERSION)); searchEntityKeys.put(DocumentsSearchDescriptor.LIST_INDEX, new FieldDescriptor(SMappedDocument.class, SDocument.INDEX)); searchEntityKeys.put(DocumentsSearchDescriptor.DOCUMENT_URL, new FieldDescriptor(SMappedDocument.class, DOCUMENT_PREFIX + SDocument.URL)); searchEntityKeys.put(DocumentsSearchDescriptor.PROCESSINSTANCE_ID, new FieldDescriptor(SMappedDocument.class, "processInstanceId")); documentAllFields = new HashMap<>(1); final Set documentFields = new HashSet<>(8); documentFields.add(DOCUMENT_PREFIX + SDocument.FILENAME); documentFields.add(DOCUMENT_PREFIX + SDocument.MIMETYPE); documentFields.add(SDocument.NAME); documentFields.add(SDocument.DESCRIPTION); documentFields.add(DOCUMENT_PREFIX + SDocument.URL); documentAllFields.put(SMappedDocument.class, documentFields); } @Override protected Map getEntityKeys() { return searchEntityKeys; } @Override protected Map, Set> getAllFields() { return documentAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchEntitiesDescriptor.java ================================================ /** * 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.search.descriptor; /** * @author Matthieu Chaffotte * @author Zhang Bole * @author Yanyan Liu * @author Celine Souchet */ public class SearchEntitiesDescriptor { private final SearchUserDescriptor searchUserDescriptor; private final SearchRoleDescriptor searchRoleDescriptor; private final SearchGroupDescriptor searchGroupDescriptor; private final SearchCustomUserInfoValueDescriptor searchCustomUserInfoValueDescriptor; private final SearchProcessInstanceDescriptor searchProcessInstanceDescriptor; private final SearchArchivedProcessInstancesDescriptor searchArchivedProcessInstanceDescriptor; private final SearchHumanTaskInstanceDescriptor searchHumanTaskInstanceDescriptor; private final SearchArchivedHumanTaskInstanceDescriptor searchArchivedHumanTaskInstanceDescriptor; private final SearchProcessDefinitionsDescriptor searchProcessDefinitionsDescriptor; private final SearchCommentDescriptor searchCommentDescriptor; private final SearchDocumentDescriptor searchDocumentDescriptor; private final SearchArchivedDocumentDescriptor searchArchivedDocumentDescriptor; private final SearchActivityInstanceDescriptor searchActivityInstanceDescriptor; private final SearchFlowNodeInstanceDescriptor searchFlowNodeInstanceDescriptor; private final SearchArchivedActivityInstanceDescriptor searchArchivedActivityInstanceDescriptor; private final SearchArchivedCommentsDescriptor searchArchivedCommentsDescriptor; private final SearchArchivedConnectorInstanceDescriptor searchArchivedConnectorInstanceDescriptor; private final SearchCommandDescriptor searchCommandDescriptor; private final SearchArchivedFlowNodeInstanceDescriptor searchArchivedFlowNodeInstanceDescriptor; private final SearchConnectorInstanceDescriptor searchConnectorInstanceDescriptor; private final SearchProfileDescriptor searchProfileDescriptor; private final SearchFormMappingDescriptor searchFormMappingDescriptor; private final SearchProfileMemberUserDescriptor searchProfileMemberUserDescriptor; private final SearchProfileMemberGroupDescriptor searchProfileMemberGroupDescriptor; private final SearchProfileMemberRoleDescriptor searchProfileMemberRoleDescriptor; private final SearchProfileMemberRoleAndGroupDescriptor searchProfileMemberRoleAndGroupDescriptor; private final SearchEventTriggerInstanceDescriptor searchEventTriggerInstanceDescriptor; private final SearchPageDescriptor searchPageDescriptor; private final SearchApplicationDescriptor searchApplicationDescriptor; private final SearchApplicationMenuDescriptor searchApplicationMenuDescriptor; private final SearchApplicationPageDescriptor searchApplicationPageDescriptor; private final SearchMessageInstanceDescriptor searchMessageInstanceDescriptor; public SearchEntitiesDescriptor() { searchUserDescriptor = new SearchUserDescriptor(); searchRoleDescriptor = new SearchRoleDescriptor(); searchGroupDescriptor = new SearchGroupDescriptor(); searchCustomUserInfoValueDescriptor = new SearchCustomUserInfoValueDescriptor(); searchProcessInstanceDescriptor = new SearchProcessInstanceDescriptor(); searchArchivedProcessInstanceDescriptor = new SearchArchivedProcessInstancesDescriptor(); searchHumanTaskInstanceDescriptor = new SearchHumanTaskInstanceDescriptor(); searchArchivedHumanTaskInstanceDescriptor = new SearchArchivedHumanTaskInstanceDescriptor(); searchProcessDefinitionsDescriptor = new SearchProcessDefinitionsDescriptor(); searchCommentDescriptor = new SearchCommentDescriptor(); searchConnectorInstanceDescriptor = new SearchConnectorInstanceDescriptor(); searchDocumentDescriptor = new SearchDocumentDescriptor(); searchArchivedDocumentDescriptor = new SearchArchivedDocumentDescriptor(); searchActivityInstanceDescriptor = new SearchActivityInstanceDescriptor(); searchArchivedActivityInstanceDescriptor = new SearchArchivedActivityInstanceDescriptor(); searchArchivedCommentsDescriptor = new SearchArchivedCommentsDescriptor(); searchArchivedConnectorInstanceDescriptor = new SearchArchivedConnectorInstanceDescriptor(); searchFlowNodeInstanceDescriptor = new SearchFlowNodeInstanceDescriptor(); searchCommandDescriptor = new SearchCommandDescriptor(); searchArchivedFlowNodeInstanceDescriptor = new SearchArchivedFlowNodeInstanceDescriptor(); searchProfileDescriptor = new SearchProfileDescriptor(); searchProfileMemberUserDescriptor = new SearchProfileMemberUserDescriptor(); searchProfileMemberGroupDescriptor = new SearchProfileMemberGroupDescriptor(); searchProfileMemberRoleDescriptor = new SearchProfileMemberRoleDescriptor(); searchProfileMemberRoleAndGroupDescriptor = new SearchProfileMemberRoleAndGroupDescriptor(); searchEventTriggerInstanceDescriptor = new SearchEventTriggerInstanceDescriptor(); searchPageDescriptor = new SearchPageDescriptor(); searchApplicationDescriptor = new SearchApplicationDescriptor(); searchApplicationMenuDescriptor = new SearchApplicationMenuDescriptor(); searchApplicationPageDescriptor = new SearchApplicationPageDescriptor(); searchFormMappingDescriptor = new SearchFormMappingDescriptor(); searchMessageInstanceDescriptor = new SearchMessageInstanceDescriptor(); } public SearchUserDescriptor getSearchUserDescriptor() { return searchUserDescriptor; } public SearchRoleDescriptor getSearchRoleDescriptor() { return searchRoleDescriptor; } public SearchGroupDescriptor getSearchGroupDescriptor() { return searchGroupDescriptor; } public SearchCustomUserInfoValueDescriptor getSearchCustomUserInfoValueDescriptor() { return searchCustomUserInfoValueDescriptor; } public SearchProcessInstanceDescriptor getSearchProcessInstanceDescriptor() { return searchProcessInstanceDescriptor; } public SearchArchivedProcessInstancesDescriptor getSearchArchivedProcessInstanceDescriptor() { return searchArchivedProcessInstanceDescriptor; } public SearchHumanTaskInstanceDescriptor getSearchHumanTaskInstanceDescriptor() { return searchHumanTaskInstanceDescriptor; } public SearchArchivedHumanTaskInstanceDescriptor getSearchArchivedHumanTaskInstanceDescriptor() { return searchArchivedHumanTaskInstanceDescriptor; } public SearchProcessDefinitionsDescriptor getSearchProcessDefinitionsDescriptor() { return searchProcessDefinitionsDescriptor; } public SearchCommentDescriptor getSearchCommentDescriptor() { return searchCommentDescriptor; } public SearchDocumentDescriptor getSearchDocumentDescriptor() { return searchDocumentDescriptor; } public SearchArchivedDocumentDescriptor getSearchArchivedDocumentDescriptor() { return searchArchivedDocumentDescriptor; } public SearchActivityInstanceDescriptor getSearchActivityInstanceDescriptor() { return searchActivityInstanceDescriptor; } public SearchFlowNodeInstanceDescriptor getSearchFlowNodeInstanceDescriptor() { return searchFlowNodeInstanceDescriptor; } public SearchArchivedActivityInstanceDescriptor getSearchArchivedActivityInstanceDescriptor() { return searchArchivedActivityInstanceDescriptor; } public SearchArchivedCommentsDescriptor getSearchArchivedCommentsDescriptor() { return searchArchivedCommentsDescriptor; } public SearchArchivedConnectorInstanceDescriptor getSearchArchivedConnectorInstanceDescriptor() { return searchArchivedConnectorInstanceDescriptor; } public SearchCommandDescriptor getSearchCommandDescriptor() { return searchCommandDescriptor; } public SearchArchivedFlowNodeInstanceDescriptor getSearchArchivedFlowNodeInstanceDescriptor() { return searchArchivedFlowNodeInstanceDescriptor; } public SearchConnectorInstanceDescriptor getSearchConnectorInstanceDescriptor() { return searchConnectorInstanceDescriptor; } public SearchProfileDescriptor getSearchProfileDescriptor() { return searchProfileDescriptor; } public SearchFormMappingDescriptor getSearchFormMappingDescriptor() { return searchFormMappingDescriptor; } public SearchProfileMemberUserDescriptor getSearchProfileMemberUserDescriptor() { return searchProfileMemberUserDescriptor; } public SearchProfileMemberGroupDescriptor getSearchProfileMemberGroupDescriptor() { return searchProfileMemberGroupDescriptor; } public SearchProfileMemberRoleDescriptor getSearchProfileMemberRoleDescriptor() { return searchProfileMemberRoleDescriptor; } public SearchProfileMemberRoleAndGroupDescriptor getSearchProfileMemberRoleAndGroupDescriptor() { return searchProfileMemberRoleAndGroupDescriptor; } public SearchEventTriggerInstanceDescriptor getSearchEventTriggerInstanceDescriptor() { return searchEventTriggerInstanceDescriptor; } public SearchPageDescriptor getSearchPageDescriptor() { return searchPageDescriptor; } public SearchApplicationDescriptor getSearchApplicationDescriptor() { return searchApplicationDescriptor; } public SearchApplicationMenuDescriptor getSearchApplicationMenuDescriptor() { return searchApplicationMenuDescriptor; } public SearchApplicationPageDescriptor getSearchApplicationPageDescriptor() { return searchApplicationPageDescriptor; } public SearchMessageInstanceDescriptor getSearchMessageInstanceDescriptor() { return searchMessageInstanceDescriptor; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchEntityDescriptor.java ================================================ /** * 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.search.descriptor; import java.io.Serializable; import java.util.ArrayList; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SearchFields; import org.bonitasoft.engine.persistence.search.FilterOperationType; import org.bonitasoft.engine.search.Sort; import org.bonitasoft.engine.search.impl.SearchFilter; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public abstract class SearchEntityDescriptor { public FilterOption getEntityFilter(final SearchFilter filter) { final String key = filter.getField(); final FieldDescriptor fieldDescriptor = getEntityKeys().get(key); if (fieldDescriptor == null && !filter.isUndefinedFieldNameAuthorized()) { throw new IllegalArgumentException("the field '" + key + "' is unknown for the entity searched using " + this.getClass().getSimpleName()); } return constructFilterOption(filter, fieldDescriptor); } public OrderByOption getEntityOrder(final Sort sort) throws SBonitaReadException { final FieldDescriptor fieldDescriptor = getEntityKeys().get(sort.getField()); if (fieldDescriptor == null) { throw new SBonitaReadException("Invalid sort key: " + sort.getField()); } final OrderByType type = OrderByType.valueOf(sort.getOrder().name()); return new OrderByOption(fieldDescriptor.getPersistentClass(), fieldDescriptor.getValue(), type); } public SearchFields getEntitySearchTerm(final String searchString) { final StringTokenizer tokens = new StringTokenizer(searchString, " "); final ArrayList terms = new ArrayList<>(tokens.countTokens()); while (tokens.hasMoreTokens()) { final String term = tokens.nextToken(); terms.add(term); } return new SearchFields(terms, getAllFields()); } protected abstract Map getEntityKeys(); protected abstract Map, Set> getAllFields(); /** * Override this method to have specific conversion behavior from client filter value to server filter value . * * @param filterField * The field to filter * @param filterValue * The initial value * @return the converted filter value * @since 6.4.0 */ protected Serializable convertFilterValue(final String filterField, final Serializable filterValue) { return filterValue; } public FilterOption constructFilterOption(final SearchFilter filter, final FieldDescriptor fieldDescriptor) { final Class clazz = fieldDescriptor != null ? fieldDescriptor.getPersistentClass() : null; final String fieldName = fieldDescriptor != null ? fieldDescriptor.getValue() : null; final Serializable value = convertFilterValue(filter.getField(), filter.getValue()); switch (filter.getOperation()) { case BETWEEN: return new FilterOption(clazz, fieldName, convertFilterValue(filter.getField(), filter.getFrom()), convertFilterValue(filter.getField(), filter.getTo())); case DIFFERENT: return new FilterOption(clazz, fieldName, value, FilterOperationType.DIFFERENT); case EQUALS: return new FilterOption(clazz, fieldName, value, FilterOperationType.EQUALS); case GREATER_OR_EQUAL: return new FilterOption(clazz, fieldName, value, FilterOperationType.GREATER_OR_EQUALS); case GREATER_THAN: return new FilterOption(clazz, fieldName, value, FilterOperationType.GREATER); case LESS_OR_EQUAL: return new FilterOption(clazz, fieldName, value, FilterOperationType.LESS_OR_EQUALS); case LESS_THAN: return new FilterOption(clazz, fieldName, value, FilterOperationType.LESS); case AND: return new FilterOption(FilterOperationType.AND); case OR: return new FilterOption(FilterOperationType.OR); case L_PARENTHESIS: return new FilterOption(FilterOperationType.L_PARENTHESIS); case R_PARENTHESIS: return new FilterOption(FilterOperationType.R_PARENTHESIS); default: return null; } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchEventTriggerInstanceDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.flownode.EventTriggerInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstanceSearchDescriptor; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ public class SearchEventTriggerInstanceDescriptor extends SearchEntityDescriptor { private final Map eventTriggerInstanceDescriptorKeys; private final Map, Set> eventTriggerInstanceDescriptorAllFields; public SearchEventTriggerInstanceDescriptor() { eventTriggerInstanceDescriptorKeys = new HashMap(6); eventTriggerInstanceDescriptorKeys.put(EventTriggerInstanceSearchDescriptor.EVENT_INSTANCE_ID, new FieldDescriptor(STimerEventTriggerInstance.class, "eventInstanceId")); eventTriggerInstanceDescriptorKeys.put(TimerEventTriggerInstanceSearchDescriptor.EVENT_INSTANCE_NAME, new FieldDescriptor(SEventInstance.class, "name")); eventTriggerInstanceDescriptorKeys.put(TimerEventTriggerInstanceSearchDescriptor.EXECUTION_DATE, new FieldDescriptor(STimerEventTriggerInstance.class, "executionDate")); eventTriggerInstanceDescriptorAllFields = new HashMap, Set>( 1); eventTriggerInstanceDescriptorAllFields.put(SEventInstance.class, Collections.singleton("name")); } @Override protected Map getEntityKeys() { return eventTriggerInstanceDescriptorKeys; } @Override protected Map, Set> getAllFields() { return eventTriggerInstanceDescriptorAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchFlowNodeInstanceDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Elias Ricken de Medeiros */ public class SearchFlowNodeInstanceDescriptor extends SearchEntityDescriptor { private final Map flowNodeInstanceDescriptorKeys; private final Map, Set> flowNodeInstanceDescriptorAllFields; public SearchFlowNodeInstanceDescriptor() { final SFlowNodeInstanceBuilderFactory keyProvider = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class); flowNodeInstanceDescriptorKeys = new HashMap(8); flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.NAME, new FieldDescriptor(SFlowNodeInstance.class, keyProvider.getNameKey())); flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.STATE_NAME, new FieldDescriptor(SFlowNodeInstance.class, keyProvider.getStateNameKey())); flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.PROCESS_DEFINITION_ID, new FieldDescriptor(SFlowNodeInstance.class, keyProvider.getProcessDefinitionKey())); flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, new FieldDescriptor(SFlowNodeInstance.class, keyProvider.getParentProcessInstanceKey())); flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID, new FieldDescriptor(SFlowNodeInstance.class, keyProvider.getParentActivityInstanceKey())); flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, new FieldDescriptor(SFlowNodeInstance.class, keyProvider.getRootProcessInstanceKey())); flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.DISPLAY_NAME, new FieldDescriptor(SFlowNodeInstance.class, keyProvider.getDisplayNameKey())); flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.STATE_CATEGORY, new FieldDescriptor(SFlowNodeInstance.class, keyProvider.getStateCategoryKey())); flowNodeInstanceDescriptorKeys.put(FlowNodeInstanceSearchDescriptor.LAST_UPDATE_DATE, new FieldDescriptor(SFlowNodeInstance.class, keyProvider.getLastUpdateDateKey())); final Set tasksInstanceFields = new HashSet(2); tasksInstanceFields.add(keyProvider.getNameKey()); tasksInstanceFields.add(keyProvider.getDisplayNameKey()); flowNodeInstanceDescriptorAllFields = new HashMap, Set>(1); flowNodeInstanceDescriptorAllFields.put(SFlowNodeInstance.class, tasksInstanceFields); } @Override protected Map getEntityKeys() { return flowNodeInstanceDescriptorKeys; } @Override protected Map, Set> getAllFields() { return flowNodeInstanceDescriptorAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchFormMappingDescriptor.java ================================================ /** * 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.search.descriptor; import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.core.form.SFormMapping; import org.bonitasoft.engine.form.FormMappingSearchDescriptor; import org.bonitasoft.engine.form.FormMappingType; import org.bonitasoft.engine.page.SPageMapping; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.profile.model.SProfile; /** * @author Baptiste Mesta */ public class SearchFormMappingDescriptor extends SearchEntityDescriptor { private final Map searchEntityKeys; private final Map, Set> profileAllFields; public SearchFormMappingDescriptor() { searchEntityKeys = new HashMap<>(6); searchEntityKeys.put(FormMappingSearchDescriptor.ID, new FieldDescriptor(SFormMapping.class, "id")); searchEntityKeys.put(FormMappingSearchDescriptor.PROCESS_DEFINITION_ID, new FieldDescriptor(SFormMapping.class, "processDefinitionId")); searchEntityKeys.put(FormMappingSearchDescriptor.TYPE, new FieldDescriptor(SFormMapping.class, "type")); searchEntityKeys.put(FormMappingSearchDescriptor.TASK, new FieldDescriptor(SFormMapping.class, "task")); searchEntityKeys.put(FormMappingSearchDescriptor.PAGE_ID, new FieldDescriptor(SPageMapping.class, "pageId")); profileAllFields = new HashMap<>(1); profileAllFields.put(SProfile.class, new HashSet(0)); } @Override protected Map getEntityKeys() { return searchEntityKeys; } @Override protected Map, Set> getAllFields() { return profileAllFields; } @Override protected Serializable convertFilterValue(String filterField, Serializable filterValue) { if (filterValue instanceof FormMappingType) { return ((FormMappingType) filterValue).getId(); } return super.convertFilterValue(filterField, filterValue); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchGroupDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.identity.GroupSearchDescriptor; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Matthieu Chaffotte */ public class SearchGroupDescriptor extends SearchEntityDescriptor { private final Map groupKeys; private final Map, Set> groupAllFields; SearchGroupDescriptor() { groupKeys = new HashMap<>(4); groupKeys.put(GroupSearchDescriptor.ID, new FieldDescriptor(SGroup.class, SGroup.ID)); groupKeys.put(GroupSearchDescriptor.NAME, new FieldDescriptor(SGroup.class, SGroup.NAME)); groupKeys.put(GroupSearchDescriptor.PARENT_PATH, new FieldDescriptor(SGroup.class, SGroup.PARENT_PATH)); groupKeys.put(GroupSearchDescriptor.DISPLAY_NAME, new FieldDescriptor(SGroup.class, SGroup.DISPLAY_NAME)); groupAllFields = new HashMap<>(1); final Set groupFields = new HashSet<>(3); groupFields.add(SGroup.NAME); groupFields.add(SGroup.DISPLAY_NAME); groupFields.add(SGroup.DESCRIPTION); groupAllFields.put(SGroup.class, groupFields); } @Override protected Map getEntityKeys() { return groupKeys; } @Override protected Map, Set> getAllFields() { return groupAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchHumanTaskInstanceDescriptor.java ================================================ /** * 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.search.descriptor; import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.TaskPriority; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.STaskPriority; import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; /** * @author Julien Mege * @author Zhang Bole * @author Matthieu Chaffotte */ public class SearchHumanTaskInstanceDescriptor extends SearchEntityDescriptor { private final Map humanTaskInstanceDescriptorKeys; private final Map, Set> humanTaskInstanceDescriptorAllFields; public SearchHumanTaskInstanceDescriptor() { final SUserTaskInstanceBuilderFactory keyProvider = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class); humanTaskInstanceDescriptorKeys = new HashMap<>(13); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.NAME, new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getNameKey())); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.PRIORITY, new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getPriorityKey())); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.DUE_DATE, new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getExpectedEndDateKey())); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.STATE_NAME, new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getStateNameKey())); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getAssigneeIdKey())); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID, new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getProcessDefinitionKey())); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID, new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getRootProcessInstanceKey())); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getRootProcessInstanceKey())); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getParentProcessInstanceKey())); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID, new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getParentActivityInstanceKey())); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.PARENT_CONTAINER_ID, new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getParentContainerIdKey())); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.DISPLAY_NAME, new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getDisplayNameKey())); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.REACHED_STATE_DATE, new FieldDescriptor(SHumanTaskInstance.class, keyProvider.getReachStateDateKey())); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.USER_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.USER_ID_KEY)); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.GROUP_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.GROUP_ID_KEY)); humanTaskInstanceDescriptorKeys.put(HumanTaskInstanceSearchDescriptor.ROLE_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.ROLE_ID_KEY)); humanTaskInstanceDescriptorAllFields = new HashMap<>(1); final Set tasksInstanceFields = new HashSet<>(3); tasksInstanceFields.add(keyProvider.getNameKey()); tasksInstanceFields.add(keyProvider.getDisplayNameKey()); humanTaskInstanceDescriptorAllFields.put(SHumanTaskInstance.class, tasksInstanceFields); } @Override protected Map getEntityKeys() { return humanTaskInstanceDescriptorKeys; } @Override protected Map, Set> getAllFields() { return humanTaskInstanceDescriptorAllFields; } @Override protected Serializable convertFilterValue(final String filterField, final Serializable filterValue) { // Convert value of the filter "priority" to the server STaskPriority, hiberate will handle that as a normal enum if (HumanTaskInstanceSearchDescriptor.PRIORITY.equals(filterField)) { if (filterValue instanceof Integer) { return STaskPriority.fromOrdinal((Integer) filterValue); } else if (filterValue instanceof String) { return STaskPriority.valueOf((String) filterValue); } else if (filterValue instanceof TaskPriority) { return STaskPriority.valueOf(((TaskPriority) filterValue).name()); } else { throw new IllegalArgumentException( "Invalid value '" + filterValue + "' for filter on field 'priority'"); } } return filterValue; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchMessageInstanceDescriptor.java ================================================ /** * 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.search.descriptor; import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance; import org.bonitasoft.engine.form.FormMappingType; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.profile.model.SProfile; /** * @author Baptiste Mesta */ public class SearchMessageInstanceDescriptor extends SearchEntityDescriptor { private final Map searchEntityKeys; private final Map, Set> profileAllFields; public SearchMessageInstanceDescriptor() { searchEntityKeys = new HashMap<>(1); searchEntityKeys.put("messageName", new FieldDescriptor(SMessageInstance.class, "messageName")); profileAllFields = new HashMap<>(1); profileAllFields.put(SProfile.class, new HashSet(0)); } @Override protected Map getEntityKeys() { return searchEntityKeys; } @Override protected Map, Set> getAllFields() { return profileAllFields; } @Override protected Serializable convertFilterValue(String filterField, Serializable filterValue) { if (filterValue instanceof FormMappingType) { return ((FormMappingType) filterValue).getId(); } return super.convertFilterValue(filterField, filterValue); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchPageDescriptor.java ================================================ /** * 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.search.descriptor; import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.page.PageSearchDescriptor; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Baptiste Mesta */ public class SearchPageDescriptor extends SearchEntityDescriptor { private final Map pageKeys; private final Map, Set> pageAllFields; SearchPageDescriptor() { pageKeys = new HashMap<>(); pageKeys.put(PageSearchDescriptor.ID, new FieldDescriptor(SPage.class, SPage.ID)); pageKeys.put(PageSearchDescriptor.NAME, new FieldDescriptor(SPage.class, SPage.NAME)); pageKeys.put(PageSearchDescriptor.PROVIDED, new FieldDescriptor(SPage.class, SPage.PROVIDED)); pageKeys.put(PageSearchDescriptor.INSTALLATION_DATE, new FieldDescriptor(SPage.class, SPage.INSTALLATION_DATE)); pageKeys.put(PageSearchDescriptor.LAST_MODIFICATION_DATE, new FieldDescriptor(SPage.class, SPage.LAST_MODIFICATION_DATE)); pageKeys.put(PageSearchDescriptor.INSTALLED_BY, new FieldDescriptor(SPage.class, SPage.INSTALLED_BY)); pageKeys.put(PageSearchDescriptor.DISPLAY_NAME, new FieldDescriptor(SPage.class, SPage.DISPLAY_NAME)); pageKeys.put(PageSearchDescriptor.CONTENT_TYPE, new FieldDescriptor(SPage.class, SPage.CONTENT_TYPE)); pageKeys.put(PageSearchDescriptor.PROCESS_DEFINITION_ID, new FieldDescriptor(SPage.class, SPage.PROCESS_DEFINITION_ID)); pageAllFields = new HashMap<>(); final Set pageFields = new HashSet<>(); pageFields.add(SPage.NAME); pageFields.add(SPage.DISPLAY_NAME); pageAllFields.put(SPage.class, pageFields); } @Override protected Serializable convertFilterValue(String filterField, Serializable filterValue) { if (PageSearchDescriptor.PROCESS_DEFINITION_ID.equals(filterField) && filterValue == null) { return 0; } return super.convertFilterValue(filterField, filterValue); } @Override protected Map getEntityKeys() { return pageKeys; } @Override protected Map, Set> getAllFields() { return pageAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProcessDefinitionsDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.category.model.SProcessCategoryMapping; import org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilderFactory; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Zhao Na * @author Matthieu Chaffotte * @author Celine Souchet */ public class SearchProcessDefinitionsDescriptor extends SearchEntityDescriptor { private final Map searchEntityKeys; private final Map, Set> processDefDeployInfos; public SearchProcessDefinitionsDescriptor() { searchEntityKeys = new HashMap(12); searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.ACTIVATION_STATE, new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.ACTIVATION_STATE_KEY)); searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.CONFIGURATION_STATE, new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.CONFIGURATION_STATE_KEY)); searchEntityKeys .put(ProcessDeploymentInfoSearchDescriptor.ID, new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.ID_KEY)); searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.NAME, new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.NAME_KEY)); searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.VERSION, new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.VERSION_KEY)); searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.DEPLOYMENT_DATE, new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.DEPLOYMENT_DATE_KEY)); searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.DEPLOYED_BY, new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.DEPLOYED_BY_KEY)); searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.PROCESS_ID, new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.PROCESS_ID_KEY)); searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.DISPLAY_NAME, new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.DISPLAY_NAME_KEY)); searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.LAST_UPDATE_DATE, new FieldDescriptor(SProcessDefinitionDeployInfo.class, SProcessDefinitionDeployInfo.LAST_UPDATE_DATE_KEY)); final SProcessCategoryMappingBuilderFactory processCategoryMappingBuilderFactory = BuilderFactory .get(SProcessCategoryMappingBuilderFactory.class); searchEntityKeys.put(ProcessDeploymentInfoSearchDescriptor.CATEGORY_ID, new FieldDescriptor(SProcessCategoryMapping.class, processCategoryMappingBuilderFactory.getCategoryIdKey())); processDefDeployInfos = new HashMap, Set>(1); final Set processFields = new HashSet(3); processFields.add(SProcessDefinitionDeployInfo.NAME_KEY); processFields.add(SProcessDefinitionDeployInfo.DISPLAY_NAME_KEY); processFields.add(SProcessDefinitionDeployInfo.VERSION_KEY); processDefDeployInfos.put(SProcessDefinitionDeployInfo.class, processFields); } @Override protected Map getEntityKeys() { return searchEntityKeys; } @Override protected Map, Set> getAllFields() { return processDefDeployInfos; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProcessInstanceDescriptor.java ================================================ /** * 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.search.descriptor; import static org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor.*; import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; /** * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public class SearchProcessInstanceDescriptor extends SearchEntityDescriptor { protected final Map searchEntityKeys; private final Map, Set> processInstanceAllFields; protected final Set processFields; public SearchProcessInstanceDescriptor() { final SUserTaskInstanceBuilderFactory sUserTaskInstanceBuilder = BuilderFactory .get(SUserTaskInstanceBuilderFactory.class); searchEntityKeys = new HashMap<>(); searchEntityKeys.put(NAME, new FieldDescriptor(SProcessInstance.class, SProcessInstance.NAME_KEY)); searchEntityKeys.put(PROCESS_DEFINITION_ID, new FieldDescriptor(SProcessInstance.class, SProcessInstance.PROCESSDEF_ID_KEY)); searchEntityKeys.put(LAST_UPDATE, new FieldDescriptor(SProcessInstance.class, SProcessInstance.LAST_UPDATE_KEY)); searchEntityKeys.put(START_DATE, new FieldDescriptor(SProcessInstance.class, SProcessInstance.START_DATE_KEY)); searchEntityKeys.put(END_DATE, new FieldDescriptor(SProcessInstance.class, SProcessInstance.END_DATE_KEY)); searchEntityKeys.put(STATE_ID, new FieldDescriptor(SProcessInstance.class, SProcessInstance.STATE_ID_KEY)); searchEntityKeys.put(STATE_NAME, new FieldDescriptor(SProcessInstance.class, SProcessInstance.STATE_ID_KEY)); searchEntityKeys.put(ID, new FieldDescriptor(SProcessInstance.class, SProcessInstance.ID_KEY)); searchEntityKeys.put(ROOT_PROCESS_INSTANCE_ID, new FieldDescriptor(SProcessInstance.class, SProcessInstance.ROOT_PROCESS_INSTANCE_ID_KEY)); searchEntityKeys.put(STARTED_BY, new FieldDescriptor(SProcessInstance.class, SProcessInstance.STARTED_BY_KEY)); searchEntityKeys.put(CALLER_ID, new FieldDescriptor(SProcessInstance.class, SProcessInstance.CALLER_ID)); searchEntityKeys.put(PROCESS_SUPERVISOR_USER_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.USER_ID_KEY)); searchEntityKeys.put(PROCESS_SUPERVISOR_GROUP_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.GROUP_ID_KEY)); searchEntityKeys.put(PROCESS_SUPERVISOR_ROLE_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.ROLE_ID_KEY)); searchEntityKeys.put(ASSIGNEE_ID, new FieldDescriptor(SUserTaskInstance.class, sUserTaskInstanceBuilder.getAssigneeIdKey())); processInstanceAllFields = new HashMap<>(); processFields = new HashSet<>(); processFields.add(SProcessInstance.NAME_KEY); processInstanceAllFields.put(SProcessInstance.class, processFields); searchEntityKeys.put(ProcessInstanceSearchDescriptor.STRING_INDEX_1, new FieldDescriptor(SProcessInstance.class, SProcessInstance.STRING_INDEX_1_KEY)); searchEntityKeys.put(ProcessInstanceSearchDescriptor.STRING_INDEX_2, new FieldDescriptor(SProcessInstance.class, SProcessInstance.STRING_INDEX_2_KEY)); searchEntityKeys.put(ProcessInstanceSearchDescriptor.STRING_INDEX_3, new FieldDescriptor(SProcessInstance.class, SProcessInstance.STRING_INDEX_3_KEY)); searchEntityKeys.put(ProcessInstanceSearchDescriptor.STRING_INDEX_4, new FieldDescriptor(SProcessInstance.class, SProcessInstance.STRING_INDEX_4_KEY)); searchEntityKeys.put(ProcessInstanceSearchDescriptor.STRING_INDEX_5, new FieldDescriptor(SProcessInstance.class, SProcessInstance.STRING_INDEX_5_KEY)); processFields.add(SProcessInstance.STRING_INDEX_1_KEY); processFields.add(SProcessInstance.STRING_INDEX_2_KEY); processFields.add(SProcessInstance.STRING_INDEX_3_KEY); processFields.add(SProcessInstance.STRING_INDEX_4_KEY); processFields.add(SProcessInstance.STRING_INDEX_5_KEY); } @Override protected Map getEntityKeys() { return searchEntityKeys; } @Override protected Map, Set> getAllFields() { return processInstanceAllFields; } @Override protected Serializable convertFilterValue(final String filterField, final Serializable filterValue) { if (STATE_NAME.equals(filterField)) { if (filterValue instanceof String) { return ProcessInstanceState.valueOf((String) filterValue).getId(); } else if (filterValue instanceof ProcessInstanceState) { return ((ProcessInstanceState) filterValue).getId(); } else { throw new IllegalArgumentException("The state name must be a String or a ProcessInstanceState !!"); } } return filterValue; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProcessSupervisorDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisorSearchDescriptor; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; /** * @author Emmanuel Duchastenier * @author Celine Souchet */ public class SearchProcessSupervisorDescriptor extends SearchEntityDescriptor { private final Map fieldDescriptorMap; private final Map, Set> supervisorAllFields; public SearchProcessSupervisorDescriptor() { // final SUserBuilder userBuilder = identityModelBuilder.getUserBuilder(); // final GroupBuilder groupBuilder = identityModelBuilder.getGroupBuilder(); // final RoleBuilder roleBuilder = identityModelBuilder.getRoleBuilder(); fieldDescriptorMap = new HashMap(5); fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.ID_KEY)); fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.PROCESS_DEFINITION_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.PROCESS_DEF_ID_KEY)); fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.USER_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.USER_ID_KEY)); fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.GROUP_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.GROUP_ID_KEY)); fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.ROLE_ID, new FieldDescriptor(SProcessSupervisor.class, SProcessSupervisor.ROLE_ID_KEY)); // fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.USER_FISRT_NAME, new FieldDescriptor(SUser.class, userBuilder.getFirstNameKey())); // fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.USER_LAST_NAME, new FieldDescriptor(SUser.class, userBuilder.getLastNameKey())); // fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.USERNAME, new FieldDescriptor(SUser.class, userBuilder.getUserNameKey())); // fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.GROUP_NAME, new FieldDescriptor(SGroup.class, groupBuilder.getNameKey())); // fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.GROUP_PARENT_PATH, new FieldDescriptor(SGroup.class, groupBuilder.getParentPathKey())); // fieldDescriptorMap.put(ProcessSupervisorSearchDescriptor.ROLE_NAME, new FieldDescriptor(SRole.class, roleBuilder.getNameKey())); // supervisorAllFields = new HashMap, Set>(3); // final Set userFields = new HashSet(3); // userFields.add(userBuilder.getFirstNameKey()); // userFields.add(userBuilder.getLastNameKey()); // userFields.add(userBuilder.getUserNameKey()); // supervisorAllFields.put(SUser.class, userFields); // // final Set groupFields = new HashSet(2); // groupFields.add(groupBuilder.getNameKey()); // groupFields.add(groupBuilder.getParentPathKey()); // supervisorAllFields.put(SGroup.class, groupFields); // // final Set roleFields = new HashSet(1); // roleFields.add(roleBuilder.getNameKey()); // supervisorAllFields.put(SRole.class, roleFields); } @Override protected Map getEntityKeys() { return fieldDescriptorMap; } @Override protected Map, Set> getAllFields() { return supervisorAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProfileDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.profile.ProfileSearchDescriptor; import org.bonitasoft.engine.profile.model.SProfile; /** * @author Zhang Bole * @author Matthieu Chaffotte * @author Celine Souchet */ public class SearchProfileDescriptor extends SearchEntityDescriptor { private final Map searchEntityKeys = new HashMap<>(); private final Map, Set> profileAllFields = new HashMap<>(); public SearchProfileDescriptor() { searchEntityKeys.put(ProfileSearchDescriptor.ID, new FieldDescriptor(SProfile.class, SProfile.ID)); searchEntityKeys.put(ProfileSearchDescriptor.NAME, new FieldDescriptor(SProfile.class, SProfile.NAME)); final Set fields = new HashSet<>(); fields.add(SProfile.NAME); profileAllFields.put(SProfile.class, fields); } @Override protected Map getEntityKeys() { return searchEntityKeys; } @Override protected Map, Set> getAllFields() { return profileAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProfileMemberGroupDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor; import org.bonitasoft.engine.profile.model.SProfileMember; /** * @author Zhang Bole * @author Matthieu Chaffotte * @author Celine Souchet */ public class SearchProfileMemberGroupDescriptor extends SearchEntityDescriptor { private final Map searchEntityKeys; private final Map, Set> profileMemberAllFields; public SearchProfileMemberGroupDescriptor() { searchEntityKeys = new HashMap(7); searchEntityKeys.put(ProfileMemberSearchDescriptor.ID, new FieldDescriptor(SProfileMember.class, SProfileMember.ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.PROFILE_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.PROFILE_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.ROLE_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.ROLE_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.USER_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.USER_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.GROUP_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.GROUP_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, new FieldDescriptor(SGroup.class, SGroup.NAME)); searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART2, new FieldDescriptor(SGroup.class, SGroup.PARENT_PATH)); profileMemberAllFields = new HashMap, Set>(1); final Set groupFields = new HashSet(2); groupFields.add(SGroup.NAME); groupFields.add(SGroup.PARENT_PATH); profileMemberAllFields.put(SGroup.class, groupFields); } @Override protected Map getEntityKeys() { return searchEntityKeys; } @Override protected Map, Set> getAllFields() { return profileMemberAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProfileMemberRoleAndGroupDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor; import org.bonitasoft.engine.profile.model.SProfileMember; /** * @author Zhang Bole * @author Celine Souchet */ public class SearchProfileMemberRoleAndGroupDescriptor extends SearchEntityDescriptor { private final Map searchEntityKeys; private final Map, Set> profileMemberAllFields; public SearchProfileMemberRoleAndGroupDescriptor() { searchEntityKeys = new HashMap(7); searchEntityKeys.put(ProfileMemberSearchDescriptor.ID, new FieldDescriptor(SProfileMember.class, SProfileMember.ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.PROFILE_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.PROFILE_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.ROLE_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.ROLE_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.USER_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.USER_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.GROUP_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.GROUP_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, new FieldDescriptor(SRole.class, SRole.NAME)); searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART2, new FieldDescriptor(SGroup.class, SGroup.NAME)); searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART3, new FieldDescriptor(SGroup.class, SGroup.PARENT_PATH)); profileMemberAllFields = new HashMap, Set>(2); final Set roleFields = new HashSet(1); roleFields.add(SRole.NAME); profileMemberAllFields.put(SRole.class, roleFields); final Set groupFields = new HashSet(2); groupFields.add(SGroup.NAME); groupFields.add(SGroup.PARENT_PATH); profileMemberAllFields.put(SGroup.class, groupFields); } @Override protected Map getEntityKeys() { return searchEntityKeys; } @Override protected Map, Set> getAllFields() { return profileMemberAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProfileMemberRoleDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor; import org.bonitasoft.engine.profile.model.SProfileMember; /** * @author Zhang Bole * @author Matthieu Chaffotte * @author Celine Souchet */ public class SearchProfileMemberRoleDescriptor extends SearchEntityDescriptor { private final Map searchEntityKeys; private final Map, Set> profileMemberAllFields; public SearchProfileMemberRoleDescriptor() { searchEntityKeys = new HashMap(6); searchEntityKeys.put(ProfileMemberSearchDescriptor.ID, new FieldDescriptor(SProfileMember.class, SProfileMember.ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.PROFILE_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.PROFILE_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.ROLE_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.ROLE_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.USER_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.USER_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.GROUP_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.GROUP_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, new FieldDescriptor(SRole.class, SRole.NAME)); profileMemberAllFields = new HashMap, Set>(1); final Set roleFields = new HashSet(1); roleFields.add(SRole.NAME); profileMemberAllFields.put(SRole.class, roleFields); } @Override protected Map getEntityKeys() { return searchEntityKeys; } @Override protected Map, Set> getAllFields() { return profileMemberAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchProfileMemberUserDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor; import org.bonitasoft.engine.profile.model.SProfileMember; /** * @author Zhang Bole * @author Matthieu Chaffotte * @author Celine Souchet */ public class SearchProfileMemberUserDescriptor extends SearchEntityDescriptor { private final Map searchEntityKeys; private final Map, Set> profileMemberAllFields; public SearchProfileMemberUserDescriptor() { searchEntityKeys = new HashMap(5); searchEntityKeys.put(ProfileMemberSearchDescriptor.ID, new FieldDescriptor(SProfileMember.class, SProfileMember.ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.PROFILE_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.PROFILE_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.ROLE_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.ROLE_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.USER_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.USER_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.GROUP_ID, new FieldDescriptor(SProfileMember.class, SProfileMember.GROUP_ID)); searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART1, new FieldDescriptor(SUser.class, SUser.FIRST_NAME)); searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART2, new FieldDescriptor(SUser.class, SUser.LAST_NAME)); searchEntityKeys.put(ProfileMemberSearchDescriptor.DISPLAY_NAME_PART3, new FieldDescriptor(SUser.class, SUser.USER_NAME)); profileMemberAllFields = new HashMap, Set>(1); final Set userFields = new HashSet(3); userFields.add(SUser.FIRST_NAME); userFields.add(SUser.LAST_NAME); userFields.add(SUser.USER_NAME); profileMemberAllFields.put(SUser.class, userFields); } @Override protected Map getEntityKeys() { return searchEntityKeys; } @Override protected Map, Set> getAllFields() { return profileMemberAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchRoleDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.identity.RoleSearchDescriptor; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Matthieu Chaffotte */ public class SearchRoleDescriptor extends SearchEntityDescriptor { private final Map roleKeys; private final Map, Set> roleAllFields; SearchRoleDescriptor() { roleKeys = new HashMap<>(5); roleKeys.put(RoleSearchDescriptor.ID, new FieldDescriptor(SRole.class, SRole.ID)); roleKeys.put(RoleSearchDescriptor.NAME, new FieldDescriptor(SRole.class, SRole.NAME)); roleKeys.put(RoleSearchDescriptor.DISPLAY_NAME, new FieldDescriptor(SRole.class, SRole.DISPLAY_NAME)); roleAllFields = new HashMap<>(1); final Set roleFields = new HashSet<>(3); roleFields.add(SRole.NAME); roleFields.add(SRole.DISPLAY_NAME); roleFields.add(SRole.DESCRIPTION); roleAllFields.put(SRole.class, roleFields); } @Override protected Map getEntityKeys() { return roleKeys; } @Override protected Map, Set> getAllFields() { return roleAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchUserDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.identity.UserSearchDescriptor; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Matthieu Chaffotte */ public class SearchUserDescriptor extends SearchEntityDescriptor { private final Map userKeys; private final Map, Set> userAllFields; SearchUserDescriptor() { userKeys = new HashMap(8); userKeys.put(UserSearchDescriptor.ID, new FieldDescriptor(SUser.class, SUser.ID)); userKeys.put(UserSearchDescriptor.USER_NAME, new FieldDescriptor(SUser.class, SUser.USER_NAME)); userKeys.put(UserSearchDescriptor.FIRST_NAME, new FieldDescriptor(SUser.class, SUser.FIRST_NAME)); userKeys.put(UserSearchDescriptor.LAST_NAME, new FieldDescriptor(SUser.class, SUser.LAST_NAME)); userKeys.put(UserSearchDescriptor.ENABLED, new FieldDescriptor(SUser.class, SUser.ENABLED)); userKeys.put(UserSearchDescriptor.LAST_CONNECTION, new FieldDescriptor(SUser.class, SUser.LAST_CONNECTION)); userKeys.put(UserSearchDescriptor.MANAGER_USER_ID, new FieldDescriptor(SUser.class, SUser.MANAGER_USER_ID)); userKeys.put(UserSearchDescriptor.ROLE_ID, new FieldDescriptor(SUserMembership.class, SUserMembership.ROLE_ID)); userKeys.put(UserSearchDescriptor.GROUP_ID, new FieldDescriptor(SUserMembership.class, SUserMembership.GROUP_ID)); userAllFields = new HashMap, Set>(1); final Set userFields = new HashSet(4); userFields.add(SUser.USER_NAME); userFields.add(SUser.FIRST_NAME); userFields.add(SUser.LAST_NAME); userFields.add(SUser.JOB_TITLE); userAllFields.put(SUser.class, userFields); } @Override protected Map getEntityKeys() { return userKeys; } @Override protected Map, Set> getAllFields() { return userAllFields; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/descriptor/SearchWaitingEventSerchDescriptor.java ================================================ /** * 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.search.descriptor; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bpm.flownode.WaitingEventSearchDescriptor; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingEventKeyProviderBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Elias Ricken de Medeiros */ public class SearchWaitingEventSerchDescriptor extends SearchEntityDescriptor { private final Map activityInstanceDescriptorKeys; public SearchWaitingEventSerchDescriptor() { final SWaitingEventKeyProviderBuilderFactory keyProvider = BuilderFactory .get(SWaitingEventKeyProviderBuilderFactory.class); activityInstanceDescriptorKeys = new HashMap(6); activityInstanceDescriptorKeys.put(WaitingEventSearchDescriptor.BPM_EVENT_TYPE, new FieldDescriptor(SWaitingEvent.class, keyProvider.getEventTypeKey())); activityInstanceDescriptorKeys.put(WaitingEventSearchDescriptor.FLOW_NODE_NAME, new FieldDescriptor(SWaitingEvent.class, keyProvider.getFlowNodeNameKey())); activityInstanceDescriptorKeys.put(WaitingEventSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, new FieldDescriptor(SWaitingEvent.class, keyProvider.getParentProcessInstanceIdKey())); activityInstanceDescriptorKeys.put(WaitingEventSearchDescriptor.PROCESS_DEFINITION_ID, new FieldDescriptor(SWaitingEvent.class, keyProvider.getProcessDefinitionIdKey())); activityInstanceDescriptorKeys.put(WaitingEventSearchDescriptor.PROCESS_NAME, new FieldDescriptor(SWaitingEvent.class, keyProvider.getProcessNameKey())); activityInstanceDescriptorKeys.put(WaitingEventSearchDescriptor.ROOT_PROCESS_INSTANCE_ID, new FieldDescriptor(SWaitingEvent.class, keyProvider.getRootProcessInstanceIdKey())); } @Override protected Map getEntityKeys() { return activityInstanceDescriptorKeys; } @Override protected Map, Set> getAllFields() { return Collections.emptyMap(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/document/SearchArchivedDocuments.java ================================================ /** * 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.search.document; import java.util.List; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.model.archive.SAMappedDocument; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractArchivedDocumentSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchArchivedDocumentDescriptor; /** * @author Zhang Bole * @author Celine Souchet */ public class SearchArchivedDocuments extends AbstractArchivedDocumentSearchEntity { private final DocumentService documentService; public SearchArchivedDocuments(final DocumentService documentService, final SearchArchivedDocumentDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options, documentService); this.documentService = documentService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return documentService.getNumberOfArchivedDocuments(searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return documentService.searchArchivedDocuments(searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/document/SearchArchivedDocumentsSupervisedBy.java ================================================ /** * 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.search.document; import java.util.List; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.model.archive.SAMappedDocument; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractArchivedDocumentSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchArchivedDocumentDescriptor; /** * @author Zhang Bole * @author Celine Souchet */ public class SearchArchivedDocumentsSupervisedBy extends AbstractArchivedDocumentSearchEntity { private final long userId; private final DocumentService documentService; public SearchArchivedDocumentsSupervisedBy(final long userId, final DocumentService documentService, final SearchArchivedDocumentDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options, documentService); this.userId = userId; this.documentService = documentService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return documentService.getNumberOfArchivedDocumentsSupervisedBy(userId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return documentService.searchArchivedDocumentsSupervisedBy(userId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/document/SearchDocuments.java ================================================ /** * 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.search.document; import java.util.List; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractDocumentSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchDocumentDescriptor; /** * @author Zhang Bole */ public class SearchDocuments extends AbstractDocumentSearchEntity { private final DocumentService documentService; public SearchDocuments(final DocumentService documentService, final SearchDocumentDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options, documentService); this.documentService = documentService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return documentService.getNumberOfDocuments(searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return documentService.searchDocuments(searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/document/SearchDocumentsSupervisedBy.java ================================================ /** * 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.search.document; import java.util.List; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractDocumentSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchDocumentDescriptor; /** * @author Zhang Bole */ public class SearchDocumentsSupervisedBy extends AbstractDocumentSearchEntity { private final DocumentService documentService; private final long userId; public SearchDocumentsSupervisedBy(final DocumentService documentService, final SearchDocumentDescriptor searchDescriptor, final SearchOptions options, final long userId) { super(searchDescriptor, options, documentService); this.documentService = documentService; this.userId = userId; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return documentService.getNumberOfDocumentsSupervisedBy(userId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return documentService.searchDocumentsSupervisedBy(userId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/events/trigger/SearchTimerEventTriggerInstances.java ================================================ /** * 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.search.events.trigger; import java.util.List; import org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstance; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEventTriggerInstanceDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ public class SearchTimerEventTriggerInstances extends AbstractSearchEntity { private final EventInstanceService eventInstanceService; private final long processInstanceId; public SearchTimerEventTriggerInstances(final EventInstanceService eventInstanceService, final SearchEventTriggerInstanceDescriptor searchEventTriggerInstanceDescriptor, final long processInstanceId, final SearchOptions searchOptions) { super(searchEventTriggerInstanceDescriptor, searchOptions); this.eventInstanceService = eventInstanceService; this.processInstanceId = processInstanceId; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return eventInstanceService.getNumberOfTimerEventTriggerInstances(processInstanceId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return eventInstanceService.searchTimerEventTriggerInstances(processInstanceId, searchOptions); } @Override public List convertToClientObjects( final List serverObjects) { return ModelConvertor.toTimerEventTriggerInstances(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/events/trigger/SearchWaitingEvents.java ================================================ /** * 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.search.events.trigger; import java.util.List; import org.bonitasoft.engine.bpm.flownode.WaitingEvent; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public class SearchWaitingEvents extends AbstractSearchEntity { private final EventInstanceService eventInstanceService; public SearchWaitingEvents(final SearchEntityDescriptor searchDescriptor, final SearchOptions options, final EventInstanceService eventInstanceService) { super(searchDescriptor, options); this.eventInstanceService = eventInstanceService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return eventInstanceService.getNumberOfWaitingEvents(SWaitingEvent.class, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return eventInstanceService.searchWaitingEvents(SWaitingEvent.class, searchOptions); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toWaitingEvents(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/flownode/SearchArchivedFlowNodeInstances.java ================================================ /** * 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.search.flownode; import java.util.List; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchArchivedFlowNodeInstanceDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Emmanuel Duchastenier */ public class SearchArchivedFlowNodeInstances extends AbstractSearchEntity { private final FlowNodeInstanceService flowNodeInstanceService; private final FlowNodeStateManager flowNodeStateManager; public SearchArchivedFlowNodeInstances(final FlowNodeInstanceService flowNodeInstanceService, final FlowNodeStateManager flowNodeStateManager, final SearchArchivedFlowNodeInstanceDescriptor searchDescriptor, final SearchOptions searchOptions) { super(searchDescriptor, searchOptions); this.flowNodeInstanceService = flowNodeInstanceService; this.flowNodeStateManager = flowNodeStateManager; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return flowNodeInstanceService.getNumberOfArchivedFlowNodeInstances(SAFlowNodeInstance.class, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return flowNodeInstanceService.searchArchivedFlowNodeInstances(SAFlowNodeInstance.class, searchOptions); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toArchivedFlowNodeInstances(serverObjects, flowNodeStateManager); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/flownode/SearchFlowNodeInstances.java ================================================ /** * 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.search.flownode; import java.util.List; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchFlowNodeInstanceDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Elias Ricken de Medeiros */ public class SearchFlowNodeInstances extends AbstractSearchEntity { private final FlowNodeInstanceService flowNodeInstanceService; private final FlowNodeStateManager flowNodeStateManager; public SearchFlowNodeInstances(final FlowNodeInstanceService flowNodeInstanceService, final FlowNodeStateManager flowNodeStateManager, final SearchFlowNodeInstanceDescriptor searchDescriptor, final SearchOptions searchOptions) { super(searchDescriptor, searchOptions); this.flowNodeInstanceService = flowNodeInstanceService; this.flowNodeStateManager = flowNodeStateManager; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return flowNodeInstanceService.getNumberOfFlowNodeInstances(SFlowNodeInstance.class, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return flowNodeInstanceService.searchFlowNodeInstances(SFlowNodeInstance.class, searchOptions); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toFlowNodeInstances(serverObjects, flowNodeStateManager); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/form/SearchFormMappings.java ================================================ /** * 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.search.form; import java.util.List; import org.bonitasoft.engine.core.form.FormMappingService; import org.bonitasoft.engine.core.form.SFormMapping; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.form.FormMapping; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchFormMappingDescriptor; import org.bonitasoft.engine.service.FormRequiredAnalyzer; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Baptiste Mesta */ public class SearchFormMappings extends AbstractSearchEntity { private final FormMappingService formMappingService; private final ProcessDefinitionService processDefinitionService; public SearchFormMappings(final FormMappingService formMappingService, final ProcessDefinitionService processDefinitionService, final SearchFormMappingDescriptor searchProfileDescriptor, final SearchOptions options) { super(searchProfileDescriptor, options); this.formMappingService = formMappingService; this.processDefinitionService = processDefinitionService; } @Override public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException { return formMappingService.getNumberOfFormMappings(queryOptions); } @Override public List executeSearch(final QueryOptions queryOptions) throws SBonitaReadException { return formMappingService.searchFormMappings(queryOptions); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toFormMappings(serverObjects, new FormRequiredAnalyzer(processDefinitionService)); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/identity/SearchCustomUserInfoValues.java ================================================ /** * 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.search.identity; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.identity.CustomUserInfoValue; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SCustomUserInfoValue; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Vincent Elcrin */ public class SearchCustomUserInfoValues extends AbstractSearchEntity { private final IdentityService service; public SearchCustomUserInfoValues(IdentityService service, SearchEntityDescriptor searchDescriptor, SearchOptions options) { super(searchDescriptor, options); this.service = service; } @Override public long executeCount(QueryOptions options) throws SBonitaReadException { return service.getNumberOfCustomUserInfoValue(options); } @Override public List executeSearch(QueryOptions options) throws SBonitaReadException { return service.searchCustomUserInfoValue(options); } @Override public List convertToClientObjects(List sValues) { List values = new ArrayList(sValues.size()); for (SCustomUserInfoValue value : sValues) { values.add(ModelConvertor.convert(value)); } return values; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/identity/SearchGroups.java ================================================ /** * 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.search.identity; import java.util.List; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractGroupSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchGroupDescriptor; /** * @author Matthieu Chaffotte */ public class SearchGroups extends AbstractGroupSearchEntity { private final IdentityService identityService; public SearchGroups(final IdentityService identityService, final SearchGroupDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); this.identityService = identityService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return identityService.getNumberOfGroups(searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return identityService.searchGroups(searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/identity/SearchRoles.java ================================================ /** * 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.search.identity; import java.util.List; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractRoleSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchRoleDescriptor; /** * @author Matthieu Chaffotte */ public class SearchRoles extends AbstractRoleSearchEntity { private final IdentityService identityService; public SearchRoles(final IdentityService identityService, final SearchRoleDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); this.identityService = identityService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return identityService.getNumberOfRoles(searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return identityService.searchRoles(searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/identity/SearchUsers.java ================================================ /** * 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.search.identity; import java.util.List; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractUserSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchUserDescriptor; /** * @author Matthieu Chaffotte * @author Baptiste Mesta */ public class SearchUsers extends AbstractUserSearchEntity { private final IdentityService identityService; public SearchUsers(final IdentityService identityService, final SearchUserDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); this.identityService = identityService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return identityService.getNumberOfUsers(searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return identityService.searchUsers(searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/identity/SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo.java ================================================ /** * 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.search.identity; import java.util.List; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractUserSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchUserDescriptor; /** * @author Julien Reboul */ public class SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo extends AbstractUserSearchEntity { private final ActivityInstanceService activityInstanceService; private final long humanTaskInstanceId; public SearchUsersWhoCanExecutePendingHumanTaskDeploymentInfo(long humanTaskInstanceId, final ActivityInstanceService activityInstanceService, final SearchUserDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); this.activityInstanceService = activityInstanceService; this.humanTaskInstanceId = humanTaskInstanceId; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return activityInstanceService.getNumberOfUsersWhoCanExecutePendingHumanTaskDeploymentInfo(humanTaskInstanceId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return activityInstanceService.searchUsersWhoCanExecutePendingHumanTaskDeploymentInfo(humanTaskInstanceId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/identity/SearchUsersWhoCanStartProcessDeploymentInfo.java ================================================ /** * 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.search.identity; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractUserSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchUserDescriptor; /** * @author Celine Souchet */ public class SearchUsersWhoCanStartProcessDeploymentInfo extends AbstractUserSearchEntity { private final ProcessDefinitionService processDefinitionService; private final long processDefinitionId; public SearchUsersWhoCanStartProcessDeploymentInfo(final ProcessDefinitionService processDefinitionService, final SearchUserDescriptor searchDescriptor, final long processDefinitionId, final SearchOptions options) { super(searchDescriptor, options); this.processDefinitionService = processDefinitionService; this.processDefinitionId = processDefinitionId; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.getNumberOfUsersWhoCanStartProcessDeploymentInfo(processDefinitionId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.searchUsersWhoCanStartProcessDeploymentInfo(processDefinitionId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchArchivedProcessInstances.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractArchivedProcessInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchArchivedProcessInstancesDescriptor; /** * @author Celine Souchet */ public class SearchArchivedProcessInstances extends AbstractArchivedProcessInstanceSearchEntity { private final ProcessInstanceService processInstanceService; public SearchArchivedProcessInstances(final ProcessInstanceService processInstanceService, final ProcessDefinitionService processDefinitionService, final SearchArchivedProcessInstancesDescriptor archivedProcessInstancesDescriptor, final SearchOptions options) { super(archivedProcessInstancesDescriptor, options, processDefinitionService); this.processInstanceService = processInstanceService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.getNumberOfArchivedProcessInstances(searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.searchArchivedProcessInstances(searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchArchivedProcessInstancesInvolvingUser.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractArchivedProcessInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Yanyan Liu * @author Emmanuel Duchastenier * @author Celine Souchet */ public class SearchArchivedProcessInstancesInvolvingUser extends AbstractArchivedProcessInstanceSearchEntity { private final ProcessInstanceService processInstanceService; private final long userId; public SearchArchivedProcessInstancesInvolvingUser(final long userId, final ProcessInstanceService processInstanceService, final ProcessDefinitionService processDefinitionService, final SearchEntityDescriptor searchDescriptor, final SearchOptions searchOptions) { super(searchDescriptor, searchOptions, processDefinitionService); this.userId = userId; this.processInstanceService = processInstanceService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.getNumberOfArchivedProcessInstancesInvolvingUser(userId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.searchArchivedProcessInstancesInvolvingUser(userId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchArchivedProcessInstancesSupervisedBy.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractArchivedProcessInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Yanyan Liu * @author Celine Souchet */ public class SearchArchivedProcessInstancesSupervisedBy extends AbstractArchivedProcessInstanceSearchEntity { private final long userId; private final ProcessInstanceService processInstanceService; public SearchArchivedProcessInstancesSupervisedBy(final long userId, final ProcessInstanceService processInstanceService, final ProcessDefinitionService processDefinitionService, final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options, processDefinitionService); this.userId = userId; this.processInstanceService = processInstanceService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.getNumberOfArchivedProcessInstancesSupervisedBy(userId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.searchArchivedProcessInstancesSupervisedBy(userId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchArchivedProcessInstancesWithoutSubProcess.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractArchivedProcessInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Yanyan Liu * @author Baptiste Mesta * @author Celine Souchet */ public class SearchArchivedProcessInstancesWithoutSubProcess extends AbstractArchivedProcessInstanceSearchEntity { private final ProcessInstanceService processInstanceService; public SearchArchivedProcessInstancesWithoutSubProcess(final ProcessInstanceService processInstanceService, final ProcessDefinitionService processDefinitionService, final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options, processDefinitionService); this.processInstanceService = processInstanceService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.getNumberOfArchivedProcessInstancesWithoutSubProcess(searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.searchArchivedProcessInstancesWithoutSubProcess(searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchFailedProcessInstances.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ public class SearchFailedProcessInstances extends AbstractProcessInstanceSearchEntity { private final ProcessInstanceService processInstanceService; public SearchFailedProcessInstances(final ProcessInstanceService processInstanceService, final SearchProcessInstanceDescriptor searchEntitiesDescriptor, final SearchOptions options, final ProcessDefinitionService processDefinitionService) { super(searchEntitiesDescriptor, options, processDefinitionService); this.processInstanceService = processInstanceService; } @Override public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException { return processInstanceService.getNumberOfFailedProcessInstances(queryOptions); } @Override public List executeSearch(final QueryOptions queryOptions) throws SBonitaReadException { return processInstanceService.searchFailedProcessInstances(queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchFailedProcessInstancesSupervisedBy.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor; /** * @author Yanyan Liu * @author Baptiste Mesta */ public class SearchFailedProcessInstancesSupervisedBy extends AbstractProcessInstanceSearchEntity { private final ProcessInstanceService processInstanceService; private final long userId; public SearchFailedProcessInstancesSupervisedBy(final ProcessInstanceService processInstanceService, final SearchProcessInstanceDescriptor searchEntitiesDescriptor, final long userId, final SearchOptions options, final ProcessDefinitionService processDefinitionService) { super(searchEntitiesDescriptor, options, processDefinitionService); this.processInstanceService = processInstanceService; this.userId = userId; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.getNumberOfFailedProcessInstancesSupervisedBy(userId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.searchFailedProcessInstancesSupervisedBy(userId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchOpenProcessInstancesInvolvingUser.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor; /** * @author Yanyan Liu */ public class SearchOpenProcessInstancesInvolvingUser extends AbstractProcessInstanceSearchEntity { private final ProcessInstanceService processInstanceService; private final long userId; public SearchOpenProcessInstancesInvolvingUser(final ProcessInstanceService processInstanceService, final SearchProcessInstanceDescriptor searchEntitiesDescriptor, final long userId, final SearchOptions options, final ProcessDefinitionService processDefinitionService) { super(searchEntitiesDescriptor, options, processDefinitionService); this.processInstanceService = processInstanceService; this.userId = userId; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.getNumberOfOpenProcessInstancesInvolvingUser(userId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.searchOpenProcessInstancesInvolvingUser(userId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchOpenProcessInstancesInvolvingUsersManagedBy.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor; /** * @author Celine Souchet */ public class SearchOpenProcessInstancesInvolvingUsersManagedBy extends AbstractProcessInstanceSearchEntity { private final ProcessInstanceService processInstanceService; private final long managerUserId; public SearchOpenProcessInstancesInvolvingUsersManagedBy(final ProcessInstanceService processInstanceService, final SearchProcessInstanceDescriptor searchEntitiesDescriptor, final long managerUserId, final SearchOptions options, final ProcessDefinitionService processDefinitionService) { super(searchEntitiesDescriptor, options, processDefinitionService); this.processInstanceService = processInstanceService; this.managerUserId = managerUserId; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.getNumberOfOpenProcessInstancesInvolvingUsersManagedBy(managerUserId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.searchOpenProcessInstancesInvolvingUsersManagedBy(managerUserId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchOpenProcessInstancesSupervisedBy.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor; /** * @author Yanyan Liu * @author Baptiste Mesta */ public class SearchOpenProcessInstancesSupervisedBy extends AbstractProcessInstanceSearchEntity { private final ProcessInstanceService processInstanceService; private final long userId; public SearchOpenProcessInstancesSupervisedBy(final ProcessInstanceService processInstanceService, final SearchProcessInstanceDescriptor searchEntitiesDescriptor, final long userId, final SearchOptions options, final ProcessDefinitionService processDefinitionService) { super(searchEntitiesDescriptor, options, processDefinitionService); this.processInstanceService = processInstanceService; this.userId = userId; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.getNumberOfOpenProcessInstancesSupervisedBy(userId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processInstanceService.searchOpenProcessInstancesSupervisedBy(userId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessDeploymentInfos.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor; /** * @author Zhao Na */ public class SearchProcessDeploymentInfos extends AbstractProcessDeploymentInfoSearchEntity { private final ProcessDefinitionService processDefinitionService; public SearchProcessDeploymentInfos(final ProcessDefinitionService processDefinitionService, final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options) { super(searchEntitiesDescriptor, options); this.processDefinitionService = processDefinitionService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.getNumberOfProcessDeploymentInfos(searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.searchProcessDeploymentInfos(searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessDeploymentInfosCanBeStartedBy.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor; /** * @author Elias Ricken de Medeiros */ public class SearchProcessDeploymentInfosCanBeStartedBy extends AbstractProcessDeploymentInfoSearchEntity { private final ProcessDefinitionService processDefinitionService; private final long userId; public SearchProcessDeploymentInfosCanBeStartedBy(final ProcessDefinitionService processDefinitionService, final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options, final long userId) { super(searchEntitiesDescriptor, options); this.processDefinitionService = processDefinitionService; this.userId = userId; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.getNumberOfProcessDeploymentInfosCanBeStartedBy(userId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.searchProcessDeploymentInfosCanBeStartedBy(userId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessDeploymentInfosCanBeStartedByUsersManagedBy.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor; /** * @author Celine Souchet */ public class SearchProcessDeploymentInfosCanBeStartedByUsersManagedBy extends AbstractProcessDeploymentInfoSearchEntity { private final ProcessDefinitionService processDefinitionService; private final long managerUserId; public SearchProcessDeploymentInfosCanBeStartedByUsersManagedBy( final ProcessDefinitionService processDefinitionService, final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options, final long managerUserId) { super(searchEntitiesDescriptor, options); this.processDefinitionService = processDefinitionService; this.managerUserId = managerUserId; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.getNumberOfProcessDeploymentInfosCanBeStartedByUsersManagedBy(managerUserId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.searchProcessDeploymentInfosCanBeStartedByUsersManagedBy(managerUserId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessDeploymentInfosStartedBy.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor; /** * @author Zhao Na */ public class SearchProcessDeploymentInfosStartedBy extends AbstractProcessDeploymentInfoSearchEntity { private final ProcessDefinitionService processDefinitionService; private final long userId; public SearchProcessDeploymentInfosStartedBy(final ProcessDefinitionService processDefinitionService, final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final long userId, final SearchOptions options) { super(searchEntitiesDescriptor, options); this.processDefinitionService = processDefinitionService; this.userId = userId; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.getNumberOfProcessDeploymentInfosStartedBy(userId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.searchProcessDeploymentInfosStartedBy(userId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasks.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor; /** * @author Celine Souchet */ public class SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasks extends AbstractProcessDeploymentInfoSearchEntity { private final ProcessDefinitionService processDefinitionService; public SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasks( final ProcessDefinitionService processDefinitionService, final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options) { super(searchEntitiesDescriptor, options); this.processDefinitionService = processDefinitionService; } @Override public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException { return processDefinitionService.getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasks(queryOptions); } @Override public List executeSearch(final QueryOptions queryOptions) throws SBonitaReadException { return processDefinitionService.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor; /** * @author Celine Souchet */ public class SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor extends AbstractProcessDeploymentInfoSearchEntity { private final ProcessDefinitionService processDefinitionService; private final long userId; public SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor( final ProcessDefinitionService processDefinitionService, final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options, final long userId) { super(searchEntitiesDescriptor, options); this.processDefinitionService = processDefinitionService; this.userId = userId; } @Override public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException { return processDefinitionService.getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(userId, queryOptions); } @Override public List executeSearch(final QueryOptions queryOptions) throws SBonitaReadException { return processDefinitionService.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(userId, queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor; /** * @author Celine Souchet */ public class SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy extends AbstractProcessDeploymentInfoSearchEntity { private final ProcessDefinitionService processDefinitionService; private final long userId; public SearchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy( final ProcessDefinitionService processDefinitionService, final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options, final long userId) { super(searchEntitiesDescriptor, options); this.processDefinitionService = processDefinitionService; this.userId = userId; } @Override public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException { return processDefinitionService .getNumberOfProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(userId, queryOptions); } @Override public List executeSearch(final QueryOptions queryOptions) throws SBonitaReadException { return processDefinitionService.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(userId, queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchProcessInstances.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessInstanceDescriptor; /** * @author Celine Souchet */ public class SearchProcessInstances extends AbstractProcessInstanceSearchEntity { private final ProcessInstanceService processInstanceService; public SearchProcessInstances(final ProcessInstanceService processInstanceService, final SearchProcessInstanceDescriptor searchEntitiesDescriptor, final SearchOptions options, final ProcessDefinitionService processDefinitionService) { super(searchEntitiesDescriptor, options, processDefinitionService); this.processInstanceService = processInstanceService; } @Override public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException { return processInstanceService.getNumberOfProcessInstances(queryOptions); } @Override public List executeSearch(final QueryOptions queryOptions) throws SBonitaReadException { return processInstanceService.searchProcessInstances(queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchUncategorizedProcessDeploymentInfos.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor; /** * @author Zhang Bole */ public class SearchUncategorizedProcessDeploymentInfos extends AbstractProcessDeploymentInfoSearchEntity { private final ProcessDefinitionService processDefinitionService; public SearchUncategorizedProcessDeploymentInfos(final ProcessDefinitionService processDefinitionService, final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options) { super(searchEntitiesDescriptor, options); this.processDefinitionService = processDefinitionService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.getNumberOfUncategorizedProcessDeploymentInfos(searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.searchUncategorizedProcessDeploymentInfos(searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchUncategorizedProcessDeploymentInfosCanBeStartedBy.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor; /** * @author Zhang Bole */ public class SearchUncategorizedProcessDeploymentInfosCanBeStartedBy extends AbstractProcessDeploymentInfoSearchEntity { private final ProcessDefinitionService processDefinitionService; private final long userId; public SearchUncategorizedProcessDeploymentInfosCanBeStartedBy( final ProcessDefinitionService processDefinitionService, final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options, final long userId) { super(searchEntitiesDescriptor, options); this.processDefinitionService = processDefinitionService; this.userId = userId; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.getNumberOfUncategorizedProcessDeploymentInfosCanBeStartedBy(userId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.searchUncategorizedProcessDeploymentInfosCanBeStartedBy(userId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/process/SearchUncategorizedProcessDeploymentInfosSupervisedBy.java ================================================ /** * 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.search.process; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor; /** * @author Zhang Bole */ public class SearchUncategorizedProcessDeploymentInfosSupervisedBy extends AbstractProcessDeploymentInfoSearchEntity { private final ProcessDefinitionService processDefinitionService; private final long userId; public SearchUncategorizedProcessDeploymentInfosSupervisedBy( final ProcessDefinitionService processDefinitionService, final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options, final long userId) { super(searchEntitiesDescriptor, options); this.processDefinitionService = processDefinitionService; this.userId = userId; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.getNumberOfUncategorizedProcessDeploymentInfosSupervisedBy(userId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.searchUncategorizedProcessDeploymentInfosSupervisedBy(userId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/profile/SearchProfileMembersForProfile.java ================================================ /** * 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.search.profile; import java.util.List; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.profile.ProfileMember; import org.bonitasoft.engine.profile.ProfileService; import org.bonitasoft.engine.profile.model.SProfileMember; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Zhao Na * @author Celine Souchet */ public class SearchProfileMembersForProfile extends AbstractSearchEntity { private final ProfileService profileService; private final String querySuffix; public SearchProfileMembersForProfile(final String querySuffix, final ProfileService profileService, final SearchEntityDescriptor searchDescriptor, final SearchOptions searchOptions) { super(searchDescriptor, searchOptions); this.querySuffix = querySuffix; this.profileService = profileService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return profileService.getNumberOfProfileMembers(querySuffix, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return profileService.searchProfileMembers(querySuffix, searchOptions); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toProfileMembers(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/profile/SearchProfiles.java ================================================ /** * 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.search.profile; import java.util.List; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.profile.ProfileService; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProfileDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Celine Souchet */ public class SearchProfiles extends AbstractSearchEntity { private final ProfileService profileService; public SearchProfiles(final ProfileService profileService, final SearchProfileDescriptor searchProfileDescriptor, final SearchOptions options) { super(searchProfileDescriptor, options); this.profileService = profileService; } @Override public long executeCount(final QueryOptions queryOptions) throws SBonitaReadException { return profileService.getNumberOfProfiles(queryOptions); } @Override public List executeSearch(final QueryOptions queryOptions) throws SBonitaReadException { return profileService.searchProfiles(queryOptions); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toProfiles(serverObjects); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/supervisor/SearchArchivedActivityInstanceSupervisedBy.java ================================================ /** * 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.search.supervisor; import java.util.List; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Celine Souchet */ public class SearchArchivedActivityInstanceSupervisedBy extends AbstractSearchEntity { private final ActivityInstanceService activityInstanceService; private final FlowNodeStateManager flowNodeStateManager; private final Long supervisorId; public SearchArchivedActivityInstanceSupervisedBy(final Long supervisorId, final ActivityInstanceService activityInstanceService, final FlowNodeStateManager flowNodeStateManager, final SearchEntityDescriptor searchDescriptor, final SearchOptions searchOptions) { super(searchDescriptor, searchOptions); this.supervisorId = supervisorId; this.activityInstanceService = activityInstanceService; this.flowNodeStateManager = flowNodeStateManager; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return activityInstanceService.getNumberOfArchivedFlowNodeInstancesSupervisedBy(supervisorId, SAActivityInstance.class, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return activityInstanceService.searchArchivedFlowNodeInstancesSupervisedBy(supervisorId, SAActivityInstance.class, searchOptions); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toArchivedActivityInstances(serverObjects, flowNodeStateManager); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/supervisor/SearchArchivedFlowNodeInstanceSupervisedBy.java ================================================ /** * 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.search.supervisor; import java.util.List; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Celine Souchet */ public class SearchArchivedFlowNodeInstanceSupervisedBy extends AbstractSearchEntity { private final FlowNodeInstanceService flowNodeInstanceService; private final FlowNodeStateManager flowNodeStateManager; private final Long supervisorId; public SearchArchivedFlowNodeInstanceSupervisedBy(final Long supervisorId, final FlowNodeInstanceService flowNodeInstanceService, final FlowNodeStateManager flowNodeStateManager, final SearchEntityDescriptor searchDescriptor, final SearchOptions searchOptions) { super(searchDescriptor, searchOptions); this.supervisorId = supervisorId; this.flowNodeInstanceService = flowNodeInstanceService; this.flowNodeStateManager = flowNodeStateManager; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return flowNodeInstanceService.getNumberOfArchivedFlowNodeInstancesSupervisedBy(supervisorId, SAFlowNodeInstance.class, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return flowNodeInstanceService.searchArchivedFlowNodeInstancesSupervisedBy(supervisorId, SAFlowNodeInstance.class, searchOptions); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toArchivedFlowNodeInstances(serverObjects, flowNodeStateManager); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/supervisor/SearchArchivedHumanTasksSupervisedBy.java ================================================ /** * 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.search.supervisor; import java.util.List; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractArchivedHumanTaskInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; public class SearchArchivedHumanTasksSupervisedBy extends AbstractArchivedHumanTaskInstanceSearchEntity { private final ActivityInstanceService activityInstanceService; private final Long supervisorId; public SearchArchivedHumanTasksSupervisedBy(final Long supervisorId, final ActivityInstanceService activityInstanceService, final FlowNodeStateManager flowNodeStateManager, final SearchEntityDescriptor searchDescriptor, final SearchOptions searchOptions) { super(searchDescriptor, searchOptions, flowNodeStateManager); this.supervisorId = supervisorId; this.activityInstanceService = activityInstanceService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return activityInstanceService.getNumberOfArchivedHumanTasksSupervisedBy(supervisorId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return activityInstanceService.searchArchivedHumanTasksSupervisedBy(supervisorId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/supervisor/SearchFlowNodeInstanceSupervisedBy.java ================================================ /** * 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.search.supervisor; import java.util.List; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.service.ModelConvertor; /** * @author Celine Souchet */ public class SearchFlowNodeInstanceSupervisedBy extends AbstractSearchEntity { private final FlowNodeInstanceService flowNodeInstanceService; private final FlowNodeStateManager flowNodeStateManager; private final Long supervisorId; public SearchFlowNodeInstanceSupervisedBy(final Long supervisorId, final FlowNodeInstanceService flowNodeInstanceService, final FlowNodeStateManager flowNodeStateManager, final SearchEntityDescriptor searchDescriptor, final SearchOptions searchOptions) { super(searchDescriptor, searchOptions); this.supervisorId = supervisorId; this.flowNodeInstanceService = flowNodeInstanceService; this.flowNodeStateManager = flowNodeStateManager; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return flowNodeInstanceService.getNumberOfFlowNodeInstancesSupervisedBy(supervisorId, SFlowNodeInstance.class, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return flowNodeInstanceService.searchFlowNodeInstancesSupervisedBy(supervisorId, SFlowNodeInstance.class, searchOptions); } @Override public List convertToClientObjects(final List serverObjects) { return ModelConvertor.toFlowNodeInstances(serverObjects, flowNodeStateManager); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/supervisor/SearchProcessDeploymentInfosSupervised.java ================================================ /** * 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.search.supervisor; import java.util.List; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractProcessDeploymentInfoSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchProcessDefinitionsDescriptor; public class SearchProcessDeploymentInfosSupervised extends AbstractProcessDeploymentInfoSearchEntity { private final ProcessDefinitionService processDefinitionService; private final long userId; public SearchProcessDeploymentInfosSupervised(final ProcessDefinitionService processDefinitionService, final SearchProcessDefinitionsDescriptor searchEntitiesDescriptor, final SearchOptions options, final long userId) { super(searchEntitiesDescriptor, options); this.processDefinitionService = processDefinitionService; this.userId = userId; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.getNumberOfProcessDeploymentInfos(userId, searchOptions, "UserSupervised"); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return processDefinitionService.searchProcessDeploymentInfos(userId, searchOptions, "UserSupervised"); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/supervisor/SearchSupervisors.java ================================================ /** * 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.search.supervisor; import java.util.List; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractSupervisorSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; import org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; /** * @author Baptiste Mesta * @author Celine Souchet */ public class SearchSupervisors extends AbstractSupervisorSearchEntity { private final SupervisorMappingService supervisorService; public SearchSupervisors(final SupervisorMappingService supervisorService, final SearchEntityDescriptor searchDescriptor, final SearchOptions options) { super(searchDescriptor, options); this.supervisorService = supervisorService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return supervisorService.getNumberOfProcessSupervisors(searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return supervisorService.searchProcessSupervisors(searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/task/SearchArchivedTasks.java ================================================ /** * 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.search.task; import java.util.List; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractArchivedHumanTaskInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchArchivedHumanTaskInstanceDescriptor; /** * @author Zhang Bole * @author Celine Souchet */ public class SearchArchivedTasks extends AbstractArchivedHumanTaskInstanceSearchEntity { private final ActivityInstanceService activityInstanceService; public SearchArchivedTasks(final ActivityInstanceService activityInstanceService, final FlowNodeStateManager flowNodeStateManager, final SearchArchivedHumanTaskInstanceDescriptor searchArchivedTasksDescriptor, final SearchOptions options) { super(searchArchivedTasksDescriptor, options, flowNodeStateManager); this.activityInstanceService = activityInstanceService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return activityInstanceService.getNumberOfArchivedTasks(searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return activityInstanceService.searchArchivedTasks(searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/search/task/SearchArchivedTasksManagedBy.java ================================================ /** * 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.search.task; import java.util.List; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractArchivedHumanTaskInstanceSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchArchivedHumanTaskInstanceDescriptor; /** * @author Emmanuel Duchastenier * @author Celine Souchet */ public class SearchArchivedTasksManagedBy extends AbstractArchivedHumanTaskInstanceSearchEntity { private final ActivityInstanceService activityInstanceService; private final long managerUserId; public SearchArchivedTasksManagedBy(final long managerUserId, final SearchOptions options, final ActivityInstanceService activityInstanceService, final FlowNodeStateManager flowNodeStateManager, final SearchArchivedHumanTaskInstanceDescriptor searchArchivedHumanTaskInstanceDescriptor) { super(searchArchivedHumanTaskInstanceDescriptor, options, flowNodeStateManager); this.managerUserId = managerUserId; this.activityInstanceService = activityInstanceService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return activityInstanceService.getNumberOfArchivedTasksManagedBy(managerUserId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return activityInstanceService.searchArchivedTasksManagedBy(managerUserId, searchOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/APIAccessResolver.java ================================================ /** * 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; import org.bonitasoft.engine.exception.APIImplementationNotFoundException; /** * @author Matthieu Chaffotte */ public interface APIAccessResolver { T getAPIImplementation(Class apiInterface) throws APIImplementationNotFoundException; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/FormRequiredAnalyzer.java ================================================ /** * 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; import java.util.List; import org.bonitasoft.engine.bpm.flownode.ActivityDefinition; import org.bonitasoft.engine.bpm.flownode.UserTaskDefinition; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.form.SFormMapping; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; public class FormRequiredAnalyzer { private ProcessDefinitionService processDefinitionService; public FormRequiredAnalyzer(ProcessDefinitionService processDefinitionService) { this.processDefinitionService = processDefinitionService; } /** * Is a form required to start case / execute task: if there is a contract, a form is required (by the web part * only, the Engine never needs a form mapping * to start a case / execute a task) * * @param sFormMapping * @return true if a form is required for this form mapping, false otherwise. */ public boolean isFormRequired(SFormMapping sFormMapping) { if (sFormMapping.getType() == SFormMapping.TYPE_PROCESS_OVERVIEW) { return false; } try { final DesignProcessDefinition designProcessDefinition = processDefinitionService .getDesignProcessDefinition(sFormMapping.getProcessDefinitionId()); if (designProcessDefinition != null) { if (sFormMapping.getType() == SFormMapping.TYPE_PROCESS_START) { return designProcessDefinition.getContract() != null && designProcessDefinition.getContract().getInputs() != null && !designProcessDefinition.getContract().getInputs().isEmpty(); } else // if (sFormMapping.getType() == TYPE_TASK) { final List activities = designProcessDefinition.getFlowElementContainer() .getActivities(); final UserTaskDefinition userTaskDefinition = findActivityWithName(activities, sFormMapping.getTask()); return userTaskDefinition != null && userTaskDefinition.getContract() != null && userTaskDefinition.getContract().getInputs() != null && !userTaskDefinition.getContract().getInputs().isEmpty(); } } } catch (SBonitaException e) { } return false; } protected UserTaskDefinition findActivityWithName(List activities, String task) { if (task != null && activities != null) { for (ActivityDefinition activity : activities) { if (task.equals(activity.getName()) && activity instanceof UserTaskDefinition) { return (UserTaskDefinition) activity; } } } return null; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/InstallationFailedException.java ================================================ /** * 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; public class InstallationFailedException extends Exception { private static final long serialVersionUID = 1L; public InstallationFailedException(String message, Exception e) { super(message, e); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/InstallationService.java ================================================ /** * 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; public interface InstallationService { /** * Install binaries archive content * If a configuration archive is provided, it also update the configuration */ void install(byte[] binaries, byte[] configuration) throws InstallationFailedException; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/ModelConvertor.java ================================================ /** * 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; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; 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.actor.mapping.model.SActor; import org.bonitasoft.engine.actor.mapping.model.SActorMember; import org.bonitasoft.engine.api.impl.SessionInfos; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.actor.ActorMember; import org.bonitasoft.engine.bpm.actor.impl.ActorInstanceImpl; import org.bonitasoft.engine.bpm.actor.impl.ActorMemberImpl; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.engine.bpm.category.impl.CategoryImpl; import org.bonitasoft.engine.bpm.comment.ArchivedComment; import org.bonitasoft.engine.bpm.comment.Comment; import org.bonitasoft.engine.bpm.comment.impl.ArchivedCommentImpl; import org.bonitasoft.engine.bpm.comment.impl.CommentImpl; import org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance; import org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor; import org.bonitasoft.engine.bpm.connector.ConnectorInstance; import org.bonitasoft.engine.bpm.connector.ConnectorInstanceWithFailureInfo; import org.bonitasoft.engine.bpm.connector.ConnectorState; import org.bonitasoft.engine.bpm.connector.impl.ArchivedConnectorInstanceImpl; import org.bonitasoft.engine.bpm.connector.impl.ConnectorInstanceImpl; import org.bonitasoft.engine.bpm.connector.impl.ConnectorInstanceWithFailureInfoImpl; import org.bonitasoft.engine.bpm.contract.ContractDefinition; import org.bonitasoft.engine.bpm.contract.InputDefinition; import org.bonitasoft.engine.bpm.contract.Type; import org.bonitasoft.engine.bpm.contract.impl.ConstraintDefinitionImpl; import org.bonitasoft.engine.bpm.contract.impl.ContractDefinitionImpl; import org.bonitasoft.engine.bpm.contract.impl.InputDefinitionImpl; import org.bonitasoft.engine.bpm.data.ArchivedDataInstance; import org.bonitasoft.engine.bpm.data.DataDefinition; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.data.impl.ArchivedDataInstanceImpl; import org.bonitasoft.engine.bpm.data.impl.BlobDataInstanceImpl; import org.bonitasoft.engine.bpm.data.impl.BooleanDataInstanceImpl; import org.bonitasoft.engine.bpm.data.impl.DataDefinitionImpl; import org.bonitasoft.engine.bpm.data.impl.DataInstanceImpl; import org.bonitasoft.engine.bpm.data.impl.DateDataInstanceImpl; import org.bonitasoft.engine.bpm.data.impl.DoubleDataInstanceImpl; import org.bonitasoft.engine.bpm.data.impl.FloatDataInstanceImpl; import org.bonitasoft.engine.bpm.data.impl.IntegerDataInstanceImpl; import org.bonitasoft.engine.bpm.data.impl.LongDataInstanceImpl; import org.bonitasoft.engine.bpm.data.impl.ShortTextDataInstanceImpl; import org.bonitasoft.engine.bpm.document.ArchivedDocument; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.document.impl.ArchivedDocumentImpl; import org.bonitasoft.engine.bpm.document.impl.DocumentImpl; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ActivityStates; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedAutomaticTaskInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedCallActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedGatewayInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedLoopActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedManualTaskInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedReceiveTaskInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedSendTaskInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedSubProcessActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedUserTaskInstance; import org.bonitasoft.engine.bpm.flownode.BPMEventType; import org.bonitasoft.engine.bpm.flownode.EventInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.GatewayInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.ManualTaskInstance; import org.bonitasoft.engine.bpm.flownode.StateCategory; import org.bonitasoft.engine.bpm.flownode.TaskPriority; import org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstance; import org.bonitasoft.engine.bpm.flownode.UserTaskInstance; import org.bonitasoft.engine.bpm.flownode.WaitingEvent; import org.bonitasoft.engine.bpm.flownode.impl.internal.ActivityInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedAutomaticTaskInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedCallActivityInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedFlowNodeInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedGatewayInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedHumanTaskInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedLoopActivityInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedManualTaskInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedMultiInstanceActivityInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedReceiveTaskInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedSendTaskInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedSubProcessActivityInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedUserTaskInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.AutomaticTaskInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.BoundaryEventInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.CallActivityInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.EndEventInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.EventInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.FlowNodeInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.GatewayInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.HumanTaskInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.IntermediateCatchEventInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.IntermediateThrowEventInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.LoopActivityInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ManualTaskInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.MultiInstanceActivityInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ReceiveTaskInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.SendTaskInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.StartEventInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.SubProcessActivityInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.TimerEventTriggerInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.UserTaskInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.WaitingErrorEventImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.WaitingEventImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.WaitingMessageEventImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.WaitingSignalEventImpl; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.ProcessInstanceBuilder; import org.bonitasoft.engine.bpm.process.impl.internal.ArchivedProcessInstanceImpl; import org.bonitasoft.engine.bpm.process.impl.internal.ProcessDefinitionImpl; import org.bonitasoft.engine.bpm.process.impl.internal.ProcessDeploymentInfoImpl; import org.bonitasoft.engine.bpm.supervisor.ProcessSupervisor; import org.bonitasoft.engine.bpm.supervisor.impl.ProcessSupervisorImpl; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.business.data.BusinessDataReference; import org.bonitasoft.engine.business.data.impl.MultipleBusinessDataReferenceImpl; import org.bonitasoft.engine.business.data.impl.SimpleBusinessDataReferenceImpl; import org.bonitasoft.engine.command.CommandDescriptor; import org.bonitasoft.engine.command.CommandDescriptorImpl; import org.bonitasoft.engine.command.model.SCommand; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.core.category.model.SCategory; import org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.model.AbstractSMappedDocument; import org.bonitasoft.engine.core.document.model.archive.SAMappedDocument; import org.bonitasoft.engine.core.form.SFormMapping; import org.bonitasoft.engine.core.operation.model.SLeftOperand; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.operation.model.SOperatorType; import org.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilderFactory; import org.bonitasoft.engine.core.operation.model.builder.SOperationBuilderFactory; import org.bonitasoft.engine.core.process.comment.model.SComment; import org.bonitasoft.engine.core.process.comment.model.archive.SAComment; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; import org.bonitasoft.engine.core.process.definition.model.SConstraintDefinition; import org.bonitasoft.engine.core.process.definition.model.SContractDefinition; import org.bonitasoft.engine.core.process.definition.model.SInputDefinition; 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.SType; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo; 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.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent; import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SADataInstance; import org.bonitasoft.engine.exception.UnknownElementType; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.impl.ExpressionImpl; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilder; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory; import org.bonitasoft.engine.form.FormMapping; import org.bonitasoft.engine.form.FormMappingTarget; import org.bonitasoft.engine.form.FormMappingType; import org.bonitasoft.engine.identity.ContactData; import org.bonitasoft.engine.identity.ContactDataCreator.ContactDataField; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.GroupCreator; import org.bonitasoft.engine.identity.GroupCreator.GroupField; import org.bonitasoft.engine.identity.Icon; import org.bonitasoft.engine.identity.Role; import org.bonitasoft.engine.identity.RoleCreator; import org.bonitasoft.engine.identity.RoleCreator.RoleField; import org.bonitasoft.engine.identity.SIcon; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserCreator; import org.bonitasoft.engine.identity.UserCreator.UserField; import org.bonitasoft.engine.identity.UserMembership; import org.bonitasoft.engine.identity.impl.ContactDataImpl; import org.bonitasoft.engine.identity.impl.CustomUserInfoDefinitionImpl; import org.bonitasoft.engine.identity.impl.CustomUserInfoValueImpl; import org.bonitasoft.engine.identity.impl.GroupImpl; import org.bonitasoft.engine.identity.impl.IconImpl; import org.bonitasoft.engine.identity.impl.RoleImpl; import org.bonitasoft.engine.identity.impl.UserImpl; import org.bonitasoft.engine.identity.impl.UserMembershipImpl; import org.bonitasoft.engine.identity.model.SContactInfo; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.model.SCustomUserInfoValue; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserLogin; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.identity.xml.ExportedCustomUserInfoDefinition; import org.bonitasoft.engine.identity.xml.ExportedGroup; import org.bonitasoft.engine.identity.xml.ExportedRole; import org.bonitasoft.engine.identity.xml.ExportedUser; import org.bonitasoft.engine.identity.xml.ExportedUserMembership; import org.bonitasoft.engine.job.FailedJob; import org.bonitasoft.engine.job.impl.FailedJobImpl; import org.bonitasoft.engine.operation.Operation; import org.bonitasoft.engine.operation.OperatorType; import org.bonitasoft.engine.operation.impl.LeftOperandImpl; import org.bonitasoft.engine.operation.impl.OperationImpl; import org.bonitasoft.engine.page.PageURL; import org.bonitasoft.engine.page.SPageMapping; import org.bonitasoft.engine.page.SPageURL; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.platform.Platform; import org.bonitasoft.engine.platform.command.model.SPlatformCommand; import org.bonitasoft.engine.platform.impl.PlatformImpl; import org.bonitasoft.engine.platform.model.SPlatform; import org.bonitasoft.engine.platform.model.SPlatformProperties; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.profile.ProfileMember; import org.bonitasoft.engine.profile.ProfileMemberCreator; import org.bonitasoft.engine.profile.ProfileMemberCreator.ProfileMemberField; import org.bonitasoft.engine.profile.impl.ProfileImpl; import org.bonitasoft.engine.profile.impl.ProfileMemberImpl; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.profile.model.SProfileMember; import org.bonitasoft.engine.resources.STenantResourceLight; import org.bonitasoft.engine.scheduler.model.SFailedJob; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.impl.APISessionImpl; import org.bonitasoft.engine.session.model.SSession; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; import org.bonitasoft.engine.tenant.TenantResource; import org.bonitasoft.engine.tenant.TenantResourceState; /** * @author Matthieu Chaffotte * @author Yanyan Liu * @author Celine Souchet */ @Slf4j public class ModelConvertor { protected ModelConvertor() { // private constructor } public static APISession toAPISession(final SSession session) { final long id = session.getId(); final long userId = session.getUserId(); final String userName = session.getUserName(); final Date creationDate = session.getCreationDate(); final long duration = session.getDuration(); final boolean technicalUser = session.isTechnicalUser(); final APISessionImpl apiSession = new APISessionImpl(id, creationDate, duration, userName, userId); apiSession.setTechnicalUser(technicalUser); apiSession.setProfiles(session.getProfiles()); return apiSession; } public static Platform toPlatform(final SPlatform sPlatform, SPlatformProperties sPlatformProperties) { return new PlatformImpl(sPlatformProperties.getPlatformVersion(), sPlatform.getInitialBonitaVersion(), sPlatform.getCreatedBy(), sPlatform.getCreated(), sPlatform.isMaintenanceEnabled()); } public static List toActivityInstances(final List sActivities, final FlowNodeStateManager flowNodeStateManager) { final List activityInstances = new ArrayList<>(); for (final SActivityInstance sActivity : sActivities) { final ActivityInstance activityInstance = toActivityInstance(sActivity, flowNodeStateManager); activityInstances.add(activityInstance); } return activityInstances; } public static List toFlowNodeInstances(final List sFlowNodes, final FlowNodeStateManager flowNodeStateManager) { final List flowNodeInstances = new ArrayList<>(); for (final SFlowNodeInstance sFlowNode : sFlowNodes) { final FlowNodeInstance flowNodeInstance = toFlowNodeInstance(sFlowNode, flowNodeStateManager); flowNodeInstances.add(flowNodeInstance); } return flowNodeInstances; } private static void updateFlowNode(final FlowNodeInstanceImpl flowNode, final SFlowNodeInstance sflowNode, final String state) { flowNode.setId(sflowNode.getId()); flowNode.setState(state); flowNode.setParentContainerId(sflowNode.getParentContainerId()); flowNode.setRootContainerId(sflowNode.getRootContainerId()); flowNode.setProcessDefinitionId(sflowNode.getLogicalGroup(0)); flowNode.setParentProcessInstanceId(sflowNode.getLogicalGroup(3)); flowNode.setDisplayName(sflowNode.getDisplayName()); flowNode.setDisplayDescription(sflowNode.getDisplayDescription()); flowNode.setDescription(sflowNode.getDescription()); flowNode.setExecutedBy(sflowNode.getExecutedBy()); flowNode.setExecutedBySubstitute(sflowNode.getExecutedBySubstitute()); flowNode.setStateCategory(StateCategory.valueOf(sflowNode.getStateCategory().name())); flowNode.setReachedSateDate(new Date(sflowNode.getReachedStateDate())); flowNode.setLastUpdateDate(new Date(sflowNode.getLastUpdateDate())); } public static ActivityInstance toActivityInstance(final SActivityInstance sActivity, final FlowNodeStateManager flowNodeStateManager) { switch (sActivity.getType()) { case AUTOMATIC_TASK: return toAutomaticTask((SAutomaticTaskInstance) sActivity, flowNodeStateManager); case MANUAL_TASK: return toManualTask((SManualTaskInstance) sActivity, flowNodeStateManager); case USER_TASK: return toUserTaskInstance((SUserTaskInstance) sActivity, flowNodeStateManager); case RECEIVE_TASK: return toReceiveTaskInstance((SReceiveTaskInstance) sActivity, flowNodeStateManager); case SEND_TASK: return toSendTaskInstance((SSendTaskInstance) sActivity, flowNodeStateManager); case CALL_ACTIVITY: return toCallActivityInstance((SCallActivityInstance) sActivity, flowNodeStateManager); case SUB_PROCESS: return toSubProcessActivityInstance((SSubProcessActivityInstance) sActivity, flowNodeStateManager); case LOOP_ACTIVITY: return toLoopActivityInstance((SLoopActivityInstance) sActivity, flowNodeStateManager); case MULTI_INSTANCE_ACTIVITY: return toMultiInstanceActivityInstance((SMultiInstanceActivityInstance) sActivity, flowNodeStateManager); default: throw new UnknownElementType(sActivity.getType().name()); } } public static FlowNodeInstance toFlowNodeInstance(final SFlowNodeInstance sFlowNode, final FlowNodeStateManager flowNodeStateManager) { return switch (sFlowNode.getType()) { case START_EVENT, INTERMEDIATE_CATCH_EVENT, BOUNDARY_EVENT, INTERMEDIATE_THROW_EVENT, END_EVENT -> toEventInstance((SEventInstance) sFlowNode, flowNodeStateManager); case GATEWAY -> toGatewayInstance((SGatewayInstance) sFlowNode, flowNodeStateManager); default -> { if (sFlowNode instanceof SActivityInstance) { yield toActivityInstance((SActivityInstance) sFlowNode, flowNodeStateManager); } throw new UnknownElementType(sFlowNode.getType().name()); } }; } public static ActivityInstance toAutomaticTask(final SAutomaticTaskInstance sActivity, final FlowNodeStateManager flowNodeStateManager) { final AutomaticTaskInstanceImpl automaticTaskInstance = new AutomaticTaskInstanceImpl(sActivity.getName(), sActivity.getFlowNodeDefinitionId()); updateActivityInstance(sActivity, flowNodeStateManager, automaticTaskInstance); return automaticTaskInstance; } public static ActivityInstance toCallActivityInstance(final SCallActivityInstance sActivity, final FlowNodeStateManager flowNodeStateManager) { final CallActivityInstanceImpl callActivityInstance = new CallActivityInstanceImpl(sActivity.getName(), sActivity.getFlowNodeDefinitionId()); updateActivityInstance(sActivity, flowNodeStateManager, callActivityInstance); return callActivityInstance; } public static ActivityInstance toCallActivityInstance(final SSubProcessActivityInstance sActivity, final FlowNodeStateManager flowNodeStateManager) { final SubProcessActivityInstanceImpl subProcActivityInstance = new SubProcessActivityInstanceImpl( sActivity.getName(), sActivity.getFlowNodeDefinitionId(), sActivity.isTriggeredByEvent()); updateActivityInstance(sActivity, flowNodeStateManager, subProcActivityInstance); return subProcActivityInstance; } public static ActivityInstance toSubProcessActivityInstance(final SSubProcessActivityInstance sActivity, final FlowNodeStateManager flowNodeStateManager) { final SubProcessActivityInstanceImpl subProcessActivityInstance = new SubProcessActivityInstanceImpl( sActivity.getName(), sActivity.getFlowNodeDefinitionId(), sActivity.isTriggeredByEvent()); updateActivityInstance(sActivity, flowNodeStateManager, subProcessActivityInstance); return subProcessActivityInstance; } public static ActivityInstance toLoopActivityInstance(final SLoopActivityInstance sActivity, final FlowNodeStateManager flowNodeStateManager) { final LoopActivityInstanceImpl loopActivityInstance = new LoopActivityInstanceImpl(sActivity.getName(), sActivity.getFlowNodeDefinitionId(), sActivity.getLoopCounter()); updateActivityInstance(sActivity, flowNodeStateManager, loopActivityInstance); return loopActivityInstance; } public static ActivityInstance toMultiInstanceActivityInstance(final SMultiInstanceActivityInstance sActivity, final FlowNodeStateManager flowNodeStateManager) { final MultiInstanceActivityInstanceImpl loopActivityInstance = new MultiInstanceActivityInstanceImpl( sActivity.getName(), sActivity.getFlowNodeDefinitionId(), sActivity.isSequential(), sActivity.getLoopDataInputRef(), sActivity.getLoopDataOutputRef(), sActivity.getDataInputItemRef(), sActivity.getDataOutputItemRef(), sActivity.getNumberOfActiveInstances(), sActivity.getNumberOfCompletedInstances(), sActivity.getNumberOfTerminatedInstances(), sActivity.getLoopCardinality()); updateActivityInstance(sActivity, flowNodeStateManager, loopActivityInstance); return loopActivityInstance; } public static GatewayInstance toGatewayInstance(final SGatewayInstance sGatewayInstance, final FlowNodeStateManager flowNodeStateManager) { final GatewayInstanceImpl gatewayInstance = new GatewayInstanceImpl(sGatewayInstance.getName(), sGatewayInstance.getFlowNodeDefinitionId()); final String state = flowNodeStateManager.getState(sGatewayInstance.getStateId()).getName(); updateFlowNode(gatewayInstance, sGatewayInstance, state); return gatewayInstance; } public static ArchivedGatewayInstance toArchivedGatewayInstance(final SAGatewayInstance saGatewayInstance, final FlowNodeStateManager flowNodeStateManager) { final String name = saGatewayInstance.getName(); final ArchivedGatewayInstanceImpl aGatewayInstance = new ArchivedGatewayInstanceImpl(name); updateArchivedFlowNodeInstance(aGatewayInstance, saGatewayInstance, flowNodeStateManager.getState(saGatewayInstance.getStateId()).getName()); return aGatewayInstance; } private static void updateActivityInstance(final SActivityInstance sActivity, final FlowNodeStateManager flowNodeStateManager, final ActivityInstanceImpl activity) { final String state = flowNodeStateManager.getState(sActivity.getStateId()).getName(); updateFlowNode(activity, sActivity, state); } public static List toUserTaskInstances(final List sUserTasks, final FlowNodeStateManager flowNodeStateManager) { final List userTaskInstances = new ArrayList<>(); for (final SUserTaskInstance sUserTask : sUserTasks) { final UserTaskInstance userTask = toUserTaskInstance(sUserTask, flowNodeStateManager); userTaskInstances.add(userTask); } return userTaskInstances; } public static UserTaskInstance toUserTaskInstance(final SUserTaskInstance sUserTask, final FlowNodeStateManager flowNodeStateManager) { final UserTaskInstanceImpl userTaskInstance = new UserTaskInstanceImpl(sUserTask.getName(), sUserTask.getFlowNodeDefinitionId(), sUserTask.getActorId()); updateHumanTaskInstance(sUserTask, flowNodeStateManager, userTaskInstance); return userTaskInstance; } public static ActivityInstance toReceiveTaskInstance(final SReceiveTaskInstance sReceiveTask, final FlowNodeStateManager flowNodeStateManager) { final ReceiveTaskInstanceImpl receiveTaskInstance = new ReceiveTaskInstanceImpl(sReceiveTask.getName(), sReceiveTask.getFlowNodeDefinitionId()); updateActivityInstance(sReceiveTask, flowNodeStateManager, receiveTaskInstance); return receiveTaskInstance; } public static ActivityInstance toSendTaskInstance(final SSendTaskInstance sSendTask, final FlowNodeStateManager flowNodeStateManager) { final SendTaskInstanceImpl sendTask = new SendTaskInstanceImpl(sSendTask.getName(), sSendTask.getFlowNodeDefinitionId()); updateActivityInstance(sSendTask, flowNodeStateManager, sendTask); return sendTask; } private static void updateHumanTaskInstance(final SHumanTaskInstance sHumanTask, final FlowNodeStateManager flowNodeStateManager, final HumanTaskInstanceImpl humanTaskInstance) { updateActivityInstance(sHumanTask, flowNodeStateManager, humanTaskInstance); humanTaskInstance.setAssigneeId(sHumanTask.getAssigneeId()); final long claimedDate = sHumanTask.getClaimedDate(); if (claimedDate > 0) { humanTaskInstance.setClaimedDate(new Date(claimedDate)); } humanTaskInstance.setPriority(TaskPriority.valueOf(sHumanTask.getPriority().name())); final Long expectedEndDate = sHumanTask.getExpectedEndDate(); if (expectedEndDate != null) { humanTaskInstance.setExpectedEndDate(new Date(expectedEndDate)); } } public static List toHumanTaskInstances(final List sHumanTasks, final FlowNodeStateManager flowNodeStateManager) { final List humanTaskInstances = new ArrayList<>(sHumanTasks.size()); for (final SHumanTaskInstance sUserTask : sHumanTasks) { final HumanTaskInstance userTask = toHumanTaskInstance(sUserTask, flowNodeStateManager); humanTaskInstances.add(userTask); } return humanTaskInstances; } public static HumanTaskInstance toHumanTaskInstance(final SHumanTaskInstance sHumanTask, final FlowNodeStateManager flowNodeStateManager) { switch (sHumanTask.getType()) { case USER_TASK: return toUserTaskInstance((SUserTaskInstance) sHumanTask, flowNodeStateManager); case MANUAL_TASK: return toManualTask((SManualTaskInstance) sHumanTask, flowNodeStateManager); default: throw new UnknownElementType(sHumanTask.getType().name()); } } public static ManualTaskInstance toManualTask(final SManualTaskInstance sHumanTask, final FlowNodeStateManager flowNodeStateManager) { final ManualTaskInstanceImpl manualTaskInstance = new ManualTaskInstanceImpl(sHumanTask.getName(), sHumanTask.getFlowNodeDefinitionId(), sHumanTask.getActorId()); updateHumanTaskInstance(sHumanTask, flowNodeStateManager, manualTaskInstance); return manualTaskInstance; } public static ProcessDefinition toProcessDefinition(final SProcessDefinition sDefinition) { final ProcessDefinitionImpl processDefinitionImpl = new ProcessDefinitionImpl(sDefinition.getName(), sDefinition.getVersion()); processDefinitionImpl.setId(sDefinition.getId()); processDefinitionImpl.setDescription(sDefinition.getDescription()); return processDefinitionImpl; } public static List toProcessInstances(final List sProcessInstances, final ProcessDefinitionService processDefinitionService) { final List clientProcessInstances = new ArrayList<>(); if (sProcessInstances != null) { final Map processDefinitions = new HashMap<>(); for (final SProcessInstance sProcessInstance : sProcessInstances) { SProcessDefinition sProcessDefinition = processDefinitions.computeIfAbsent( sProcessInstance.getProcessDefinitionId(), processDefinitionId -> getProcessDefinition(processDefinitionService, processDefinitionId)); clientProcessInstances.add(toProcessInstance(sProcessDefinition, sProcessInstance)); } } return Collections.unmodifiableList(clientProcessInstances); } /** * @param processDefinitionService the {@link ProcessDefinitionService} * @param processDefinitionId the process definition id * @return the {@link SProcessDefinition} for the processDefinitionId or null if no {@link SProcessDefinition} is * found. */ private static SProcessDefinition getProcessDefinition(final ProcessDefinitionService processDefinitionService, final long processDefinitionId) { try { return processDefinitionService.getProcessDefinition(processDefinitionId); } catch (SProcessDefinitionNotFoundException pdnfe) { // Due to RUNTIME-1828 sProcessDefinition can be null. Do not rethrow exception log.warn("No process definition found for process definition id {}.", processDefinitionId); return null; } catch (SBonitaReadException e) { throw new SBonitaRuntimeException(e); } } public static ProcessInstance toProcessInstance(final SProcessDefinition definition, final SProcessInstance sInstance) { final ProcessInstanceBuilder clientProcessInstanceBuilder = ProcessInstanceBuilder.getInstance() .createNewInstance(sInstance.getName()); clientProcessInstanceBuilder.setId(sInstance.getId()); clientProcessInstanceBuilder .setState(ProcessInstanceState.getFromId(sInstance.getStateId()).name().toLowerCase()); if (sInstance.getStartDate() > 0) { clientProcessInstanceBuilder.setStartDate(sInstance.getStartDate()); } clientProcessInstanceBuilder.setStartedBy(sInstance.getStartedBy()); clientProcessInstanceBuilder.setStartedBySubstitute(sInstance.getStartedBySubstitute()); if (sInstance.getEndDate() > 0) { clientProcessInstanceBuilder.setEndDate(sInstance.getEndDate()); } clientProcessInstanceBuilder.setLastUpdate(sInstance.getLastUpdate()); clientProcessInstanceBuilder.setProcessDefinitionId(sInstance.getProcessDefinitionId()); clientProcessInstanceBuilder.setDescription(sInstance.getDescription()); clientProcessInstanceBuilder.setRootProcessInstanceId(sInstance.getRootProcessInstanceId()); clientProcessInstanceBuilder.setCallerId(sInstance.getCallerId()); if (definition != null) { for (int i = 1; i <= 5; i++) { clientProcessInstanceBuilder.setStringIndexLabel(i, definition.getStringIndexLabel(i)); } } clientProcessInstanceBuilder.setStringIndex1(sInstance.getStringIndex1()); clientProcessInstanceBuilder.setStringIndex2(sInstance.getStringIndex2()); clientProcessInstanceBuilder.setStringIndex3(sInstance.getStringIndex3()); clientProcessInstanceBuilder.setStringIndex4(sInstance.getStringIndex4()); clientProcessInstanceBuilder.setStringIndex5(sInstance.getStringIndex5()); return clientProcessInstanceBuilder.done(); } public static List toProcessDeploymentInfo( final List processDefinitionDIs) { final List deploymentInfos = new ArrayList<>(); for (final SProcessDefinitionDeployInfo processDefinitionDI : processDefinitionDIs) { final ProcessDeploymentInfo deploymentInfo = toProcessDeploymentInfo(processDefinitionDI); deploymentInfos.add(deploymentInfo); } return deploymentInfos; } public static ProcessDeploymentInfo toProcessDeploymentInfo( final SProcessDefinitionDeployInfo processDefinitionDI) { return new ProcessDeploymentInfoImpl(processDefinitionDI.getId(), processDefinitionDI.getProcessId(), processDefinitionDI.getName(), processDefinitionDI.getVersion(), processDefinitionDI.getDescription(), new Date(processDefinitionDI.getDeploymentDate()), processDefinitionDI.getDeployedBy(), ActivationState.valueOf(processDefinitionDI.getActivationState()), ConfigurationState.valueOf(processDefinitionDI.getConfigurationState()), processDefinitionDI.getDisplayName(), new Date( processDefinitionDI.getLastUpdateDate()), processDefinitionDI.getIconPath(), processDefinitionDI.getDisplayDescription()); } public static Map toProcessDeploymentInfos( final Map sProcessDeploymentInfos) { if (sProcessDeploymentInfos != null && !sProcessDeploymentInfos.isEmpty()) { final Map processDeploymentInfos = new HashMap<>(); final Set> entries = sProcessDeploymentInfos.entrySet(); for (final Entry entry : entries) { processDeploymentInfos.put(entry.getKey(), toProcessDeploymentInfo(entry.getValue())); } return processDeploymentInfos; } return Collections.emptyMap(); } public static ArchivedUserTaskInstance toArchivedUserTaskInstance(final SAUserTaskInstance sInstance, final FlowNodeStateManager flowNodeStateManager) { final ArchivedUserTaskInstanceImpl archivedUserTaskInstanceImpl = new ArchivedUserTaskInstanceImpl( sInstance.getName()); updateArchivedFlowNodeInstance(archivedUserTaskInstanceImpl, sInstance, flowNodeStateManager.getState(sInstance.getStateId()).getName()); updateArchivedHumanTaskInstance(archivedUserTaskInstanceImpl, sInstance); return archivedUserTaskInstanceImpl; } public static ArchivedReceiveTaskInstance toArchivedReceiveTaskInstance(final SAReceiveTaskInstance sInstance, final FlowNodeStateManager flowNodeStateManager) { final ArchivedReceiveTaskInstanceImpl archivedReceiveTaskInstanceImpl = new ArchivedReceiveTaskInstanceImpl( sInstance.getName()); updateArchivedFlowNodeInstance(archivedReceiveTaskInstanceImpl, sInstance, flowNodeStateManager.getState(sInstance.getStateId()).getName()); return archivedReceiveTaskInstanceImpl; } public static ArchivedSendTaskInstance toArchivedSendTaskInstance(final SASendTaskInstance sInstance, final FlowNodeStateManager flowNodeStateManager) { final ArchivedSendTaskInstanceImpl archivedSendTaskInstanceImpl = new ArchivedSendTaskInstanceImpl( sInstance.getName()); updateArchivedFlowNodeInstance(archivedSendTaskInstanceImpl, sInstance, flowNodeStateManager.getState(sInstance.getStateId()).getName()); return archivedSendTaskInstanceImpl; } /** * Update the fields of ArchivedHumanTaskInstance from a SAHumanTaskInstance */ private static void updateArchivedHumanTaskInstance(final ArchivedHumanTaskInstanceImpl activity, final SAHumanTaskInstance saHumanTask) { activity.setAssigneeId(saHumanTask.getAssigneeId()); activity.setPriority(TaskPriority.valueOf(saHumanTask.getPriority().name())); activity.setActorId(saHumanTask.getActorId()); if (saHumanTask.getExpectedEndDate() != null) { activity.setExpectedEndDate(new Date(saHumanTask.getExpectedEndDate())); } if (saHumanTask.getClaimedDate() > 0) { activity.setClaimedDate(new Date(saHumanTask.getClaimedDate())); } } private static void updateArchivedFlowNodeInstance(final ArchivedFlowNodeInstanceImpl aFlowNode, final SAFlowNodeInstance saFlowNode, final String state) { aFlowNode.setId(saFlowNode.getId()); aFlowNode.setState(state); aFlowNode.setParentContainerId(saFlowNode.getParentContainerId()); aFlowNode.setRootContainerId(saFlowNode.getRootContainerId()); aFlowNode.setSourceObjectId(saFlowNode.getSourceObjectId()); aFlowNode.setProcessDefinitionId(saFlowNode.getProcessDefinitionId()); aFlowNode.setProcessInstanceId(saFlowNode.getParentProcessInstanceId()); aFlowNode.setParentActivityInstanceId(saFlowNode.getParentActivityInstanceId()); aFlowNode.setDescription(saFlowNode.getDescription()); aFlowNode.setDisplayName(saFlowNode.getDisplayName()); aFlowNode.setDisplayDescription(saFlowNode.getDisplayDescription()); if (saFlowNode.getArchiveDate() > 0) { aFlowNode.setArchiveDate(new Date(saFlowNode.getArchiveDate())); } aFlowNode.setExecutedBy(saFlowNode.getExecutedBy()); aFlowNode.setExecutedBySubstitute(saFlowNode.getExecutedBySubstitute()); aFlowNode.setFlownodeDefinitionId(saFlowNode.getFlowNodeDefinitionId()); aFlowNode.setTerminal(saFlowNode.isTerminal()); aFlowNode.setReachedStateDate(new Date(saFlowNode.getReachedStateDate())); aFlowNode.setLastUpdateDate(new Date(saFlowNode.getLastUpdateDate())); } public static List toArchivedUserTaskInstances(final List sInstances, final FlowNodeStateManager flowNodeStateManager) { final List archivedUserTaskInstances = new ArrayList<>(); for (final SAUserTaskInstance sAUserTaskInstance : sInstances) { final ArchivedUserTaskInstance archivedUserTaskInstance = toArchivedUserTaskInstance(sAUserTaskInstance, flowNodeStateManager); archivedUserTaskInstances.add(archivedUserTaskInstance); } return archivedUserTaskInstances; } public static List toArchivedReceiveTaskInstances( final List sInstances, final FlowNodeStateManager flowNodeStateManager) { final List archivedReceiveTaskInstances = new ArrayList<>(); for (final SAReceiveTaskInstance sAReceiveTaskInstance : sInstances) { final ArchivedReceiveTaskInstance archivedReceiveTaskInstance = toArchivedReceiveTaskInstance( sAReceiveTaskInstance, flowNodeStateManager); archivedReceiveTaskInstances.add(archivedReceiveTaskInstance); } return archivedReceiveTaskInstances; } public static List toArchivedHumanTaskInstances( final List sInstances, final FlowNodeStateManager flowNodeStateManager) { final List archivedUserTaskInstances = new ArrayList<>(); for (final SAHumanTaskInstance sInstance : sInstances) { final ArchivedHumanTaskInstance archivedUserTaskInstance = toArchivedHumanTaskInstance(sInstance, flowNodeStateManager); archivedUserTaskInstances.add(archivedUserTaskInstance); } return archivedUserTaskInstances; } public static ArchivedHumanTaskInstance toArchivedHumanTaskInstance(final SAHumanTaskInstance sInstance, final FlowNodeStateManager flowNodeStateManager) { switch (sInstance.getType()) { case MANUAL_TASK: return toArchivedManualTaskInstance((SAManualTaskInstance) sInstance, flowNodeStateManager); case USER_TASK: return toArchivedUserTaskInstance((SAUserTaskInstance) sInstance, flowNodeStateManager); default: throw new UnknownElementType(sInstance.getType().name()); } } public static ArchivedActivityInstance toArchivedActivityInstance(final SAActivityInstance sInstance, final FlowNodeStateManager flowNodeStateManager) { switch (sInstance.getType()) { case AUTOMATIC_TASK: return toArchivedAutomaticTaskInstance(sInstance, flowNodeStateManager); case MANUAL_TASK: return toArchivedManualTaskInstance((SAManualTaskInstance) sInstance, flowNodeStateManager); case USER_TASK: return toArchivedUserTaskInstance((SAUserTaskInstance) sInstance, flowNodeStateManager); case RECEIVE_TASK: return toArchivedReceiveTaskInstance((SAReceiveTaskInstance) sInstance, flowNodeStateManager); case SEND_TASK: return toArchivedSendTaskInstance((SASendTaskInstance) sInstance, flowNodeStateManager); case LOOP_ACTIVITY: return toArchivedLoopActivityInstance((SALoopActivityInstance) sInstance, flowNodeStateManager); case CALL_ACTIVITY: return toArchivedCallActivityInstance((SACallActivityInstance) sInstance, flowNodeStateManager); case SUB_PROCESS: return toArchivedSubProcessActivityInstance((SASubProcessActivityInstance) sInstance, flowNodeStateManager); case MULTI_INSTANCE_ACTIVITY: return toArchivedMultiInstanceActivityInstance((SAMultiInstanceActivityInstance) sInstance, flowNodeStateManager); case BOUNDARY_EVENT: case END_EVENT: case GATEWAY: case INTERMEDIATE_CATCH_EVENT: case INTERMEDIATE_THROW_EVENT: case START_EVENT: throw new UnknownElementType("Events are not yet archived"); default: throw new UnknownElementType(sInstance.getType().name()); } } private static ArchivedLoopActivityInstance toArchivedLoopActivityInstance(final SALoopActivityInstance sInstance, final FlowNodeStateManager flowNodeStateManager) { final ArchivedLoopActivityInstanceImpl archivedloopActivityInstanceImpl = new ArchivedLoopActivityInstanceImpl( sInstance.getName()); updateArchivedFlowNodeInstance(archivedloopActivityInstanceImpl, sInstance, flowNodeStateManager.getState(sInstance.getStateId()).getName()); archivedloopActivityInstanceImpl.setLoopCounter(sInstance.getLoopCounter()); archivedloopActivityInstanceImpl.setLoopMax(sInstance.getLoopMax()); return archivedloopActivityInstanceImpl; } private static ArchivedMultiInstanceActivityInstanceImpl toArchivedMultiInstanceActivityInstance( final SAMultiInstanceActivityInstance sInstance, final FlowNodeStateManager flowNodeStateManager) { final ArchivedMultiInstanceActivityInstanceImpl archivedMultiInstanceActivityInstanceImpl = new ArchivedMultiInstanceActivityInstanceImpl( sInstance.getName(), sInstance.getFlowNodeDefinitionId(), sInstance.isSequential(), sInstance.getLoopDataInputRef(), sInstance.getLoopDataOutputRef(), sInstance.getDataInputItemRef(), sInstance.getDataOutputItemRef(), sInstance.getNumberOfActiveInstances(), sInstance.getNumberOfCompletedInstances(), sInstance.getNumberOfTerminatedInstances(), sInstance.getLoopCardinality()); updateArchivedFlowNodeInstance(archivedMultiInstanceActivityInstanceImpl, sInstance, flowNodeStateManager.getState(sInstance.getStateId()).getName()); return archivedMultiInstanceActivityInstanceImpl; } public static ArchivedManualTaskInstance toArchivedManualTaskInstance(final SAManualTaskInstance sInstance, final FlowNodeStateManager flowNodeStateManager) { final ArchivedManualTaskInstanceImpl archivedManualTaskInstance = new ArchivedManualTaskInstanceImpl( sInstance.getName()); updateArchivedFlowNodeInstance(archivedManualTaskInstance, sInstance, flowNodeStateManager.getState(sInstance.getStateId()).getName()); updateArchivedHumanTaskInstance(archivedManualTaskInstance, sInstance); return archivedManualTaskInstance; } public static ArchivedCallActivityInstance toArchivedCallActivityInstance(final SACallActivityInstance sInstance, final FlowNodeStateManager flowNodeStateManager) { final ArchivedCallActivityInstanceImpl archivedCallActivityInstanceImpl = new ArchivedCallActivityInstanceImpl( sInstance.getName()); updateArchivedFlowNodeInstance(archivedCallActivityInstanceImpl, sInstance, flowNodeStateManager.getState(sInstance.getStateId()).getName()); return archivedCallActivityInstanceImpl; } public static ArchivedSubProcessActivityInstance toArchivedSubProcessActivityInstance( final SASubProcessActivityInstance sInstance, final FlowNodeStateManager flowNodeStateManager) { final ArchivedSubProcessActivityInstanceImpl archivedSubProcActivityInstanceImpl = new ArchivedSubProcessActivityInstanceImpl( sInstance.getName(), sInstance.isTriggeredByEvent()); updateArchivedFlowNodeInstance(archivedSubProcActivityInstanceImpl, sInstance, flowNodeStateManager.getState(sInstance.getStateId()).getName()); return archivedSubProcActivityInstanceImpl; } public static ArchivedAutomaticTaskInstance toArchivedAutomaticTaskInstance(final SAActivityInstance sInstance, final FlowNodeStateManager flowNodeStateManager) { final ArchivedAutomaticTaskInstanceImpl archivedUserTaskInstanceImpl = new ArchivedAutomaticTaskInstanceImpl( sInstance.getName()); updateArchivedFlowNodeInstance(archivedUserTaskInstanceImpl, sInstance, flowNodeStateManager.getState(sInstance.getStateId()).getName()); return archivedUserTaskInstanceImpl; } public static ArchivedGatewayInstance toArchivedGatewayInstance(final SAActivityInstance sInstance, final FlowNodeStateManager flowNodeStateManager) { final ArchivedGatewayInstanceImpl archGatewayInstanceImpl = new ArchivedGatewayInstanceImpl( sInstance.getName()); updateArchivedFlowNodeInstance(archGatewayInstanceImpl, sInstance, flowNodeStateManager.getState(sInstance.getStateId()).getName()); return archGatewayInstanceImpl; } public static List toArchivedActivityInstances( final List saActivityInstances, final FlowNodeStateManager flowNodeStateManager) { final List archivedActivityInstances = new ArrayList<>(); for (final SAActivityInstance saActivityInstance : saActivityInstances) { final ArchivedActivityInstance archivedActivityInstance = toArchivedActivityInstance(saActivityInstance, flowNodeStateManager); archivedActivityInstances.add(archivedActivityInstance); } return archivedActivityInstances; } public static List toArchivedProcessInstances( final List saProcessInstances, final ProcessDefinitionService processDefinitionService) { if (saProcessInstances != null) { final List clientProcessInstances = new ArrayList<>(saProcessInstances.size()); final Map processDefinitions = new HashMap<>(saProcessInstances.size()); for (final SAProcessInstance saProcessInstance : saProcessInstances) { SProcessDefinition sProcessDefinition = processDefinitions.computeIfAbsent( saProcessInstance.getProcessDefinitionId(), processDefinitionId -> getProcessDefinition(processDefinitionService, processDefinitionId)); clientProcessInstances.add(toArchivedProcessInstance(saProcessInstance, sProcessDefinition)); } return Collections.unmodifiableList(clientProcessInstances); } return Collections.unmodifiableList(new ArrayList(1)); } public static List toArchivedProcessInstances( final List sProcessInstances, final SProcessDefinition sProcessDefinition) { final List clientProcessInstances = new ArrayList<>(sProcessInstances.size()); for (final SAProcessInstance sProcessInstance : sProcessInstances) { clientProcessInstances.add(toArchivedProcessInstance(sProcessInstance, sProcessDefinition)); } return Collections.unmodifiableList(clientProcessInstances); } public static ArchivedProcessInstance toArchivedProcessInstance(final SAProcessInstance sInstance, final SProcessDefinition sProcessDefinition) { final ArchivedProcessInstanceImpl archivedInstance = new ArchivedProcessInstanceImpl(sInstance.getName()); archivedInstance.setId(sInstance.getId()); final int stateId = sInstance.getStateId(); archivedInstance.setStateId(stateId); archivedInstance.setState(ProcessInstanceState.getFromId(stateId).name().toLowerCase()); if (sInstance.getStartDate() > 0) { archivedInstance.setStartDate(new Date(sInstance.getStartDate())); } archivedInstance.setStartedBy(sInstance.getStartedBy()); archivedInstance.setStartedBySubstitute(sInstance.getStartedBySubstitute()); if (sInstance.getEndDate() > 0) { archivedInstance.setEndDate(new Date(sInstance.getEndDate())); } if (sInstance.getArchiveDate() > 0) { archivedInstance.setArchiveDate(new Date(sInstance.getArchiveDate())); } if (sInstance.getLastUpdate() > 0) { archivedInstance.setLastUpdate(new Date(sInstance.getLastUpdate())); } archivedInstance.setProcessDefinitionId(sInstance.getProcessDefinitionId()); archivedInstance.setDescription(sInstance.getDescription()); archivedInstance.setSourceObjectId(sInstance.getSourceObjectId()); archivedInstance.setRootProcessInstanceId(sInstance.getRootProcessInstanceId()); archivedInstance.setCallerId(sInstance.getCallerId()); if (sProcessDefinition != null) { for (int i = 1; i <= 5; i++) { archivedInstance.setStringIndexLabel(i, sProcessDefinition.getStringIndexLabel(i)); } } archivedInstance.setStringIndexValue(1, sInstance.getStringIndex1()); archivedInstance.setStringIndexValue(2, sInstance.getStringIndex2()); archivedInstance.setStringIndexValue(3, sInstance.getStringIndex3()); archivedInstance.setStringIndexValue(4, sInstance.getStringIndex4()); archivedInstance.setStringIndexValue(5, sInstance.getStringIndex5()); return archivedInstance; } public static List toGroups(final List sGroups) { final List clientGroups = new ArrayList<>(); if (sGroups != null) { for (final SGroup sGroup : sGroups) { clientGroups.add(toGroup(sGroup)); } } return Collections.unmodifiableList(clientGroups); } public static List toExportedGroups(final List sGroups) { final List clientGroups = new ArrayList<>(); if (sGroups != null) { for (final SGroup sGroup : sGroups) { clientGroups.add(toExportedGroup(sGroup)); } } return Collections.unmodifiableList(clientGroups); } public static Group toGroup(final SGroup sGroup) { final GroupImpl group = new GroupImpl(sGroup.getId(), sGroup.getName()); group.setParentPath(sGroup.getParentPath()); group.setCreatedBy(sGroup.getCreatedBy()); group.setCreationDate(new Date(sGroup.getCreationDate())); group.setDescription(sGroup.getDescription()); group.setDisplayName(sGroup.getDisplayName()); group.setIconId(sGroup.getIconId()); group.setLastUpdate(new Date(sGroup.getLastUpdate())); return group; } private static ExportedGroup toExportedGroup(final SGroup sGroup) { final ExportedGroup group = new ExportedGroup(); group.setName(sGroup.getName()); group.setParentPath(sGroup.getParentPath()); group.setDescription(sGroup.getDescription()); group.setDisplayName(sGroup.getDisplayName()); return group; } public static User toUser(final SUser sUser) { final UserImpl user = new UserImpl(sUser.getId(), sUser.getUserName()); user.setFirstName(sUser.getFirstName()); user.setLastName(sUser.getLastName()); user.setTitle(sUser.getTitle()); user.setJobTitle(sUser.getJobTitle()); user.setCreatedBy(sUser.getCreatedBy()); user.setCreationDate(new Date(sUser.getCreationDate())); user.setIconId(sUser.getIconId()); user.setLastUpdate(new Date(sUser.getLastUpdate())); user.setEnabled(sUser.isEnabled()); final long managerUserId = sUser.getManagerUserId(); user.setManagerUserId(managerUserId); final SUserLogin sUserLogin = sUser.getSUserLogin(); if (sUserLogin != null && sUserLogin.getLastConnection() != null) { user.setLastConnection(new Date(sUserLogin.getLastConnection())); } return user; } public static ContactData toUserContactData(final SContactInfo sContactData) { final ContactDataImpl contactData = new ContactDataImpl(sContactData.getUserId()); contactData.setAddress(sContactData.getAddress()); contactData.setBuilding(sContactData.getBuilding()); contactData.setCity(sContactData.getCity()); contactData.setCountry(sContactData.getCountry()); contactData.setEmail(sContactData.getEmail()); contactData.setFaxNumber(sContactData.getFaxNumber()); contactData.setMobileNumber(sContactData.getMobileNumber()); contactData.setPersonal(sContactData.isPersonal()); contactData.setPhoneNumber(sContactData.getPhoneNumber()); contactData.setRoom(sContactData.getRoom()); contactData.setState(sContactData.getState()); contactData.setWebsite(sContactData.getWebsite()); contactData.setZipCode(sContactData.getZipCode()); return contactData; } public static List toUsers(final List sUsers) { final List users = new ArrayList<>(); if (sUsers != null) { for (final SUser sUser : sUsers) { final User user = ModelConvertor.toUser(sUser); users.add(user); } } return Collections.unmodifiableList(users); } public static Role toRole(final SRole sRole) { final RoleImpl role = new RoleImpl(sRole.getId(), sRole.getName()); role.setDisplayName(sRole.getDisplayName()); role.setDescription(sRole.getDescription()); role.setIconId(sRole.getIconId()); role.setCreatedBy(sRole.getCreatedBy()); role.setCreationDate(new Date(sRole.getCreationDate())); role.setLastUpdate(new Date(sRole.getLastUpdate())); return role; } private static ExportedRole toExportedRole(final SRole sRole) { final ExportedRole role = new ExportedRole(sRole.getName()); role.setDisplayName(sRole.getDisplayName()); role.setDescription(sRole.getDescription()); return role; } public static List toRoles(final List sRoles) { final List lightRoles = new ArrayList<>(); if (sRoles != null) { for (final SRole sRole : sRoles) { final Role role = toRole(sRole); lightRoles.add(role); } } return Collections.unmodifiableList(lightRoles); } public static List toExportedRoles(final List sRoles) { final List lightRoles = new ArrayList<>(); if (sRoles != null) { for (final SRole sRole : sRoles) { final ExportedRole role = toExportedRole(sRole); lightRoles.add(role); } } return Collections.unmodifiableList(lightRoles); } public static UserMembership toUserMembership(final SUserMembership sUserMembership) { final UserMembershipImpl userMembership = new UserMembershipImpl(sUserMembership.getId(), sUserMembership.getUserId(), sUserMembership.getGroupId(), sUserMembership.getRoleId()); userMembership.setAssignedBy(sUserMembership.getAssignedBy()); userMembership.setAssignedDate(new Date(sUserMembership.getAssignedDate())); userMembership.setGroupName(sUserMembership.getGroupName()); userMembership.setRoleName(sUserMembership.getRoleName()); userMembership.setUsername(sUserMembership.getUsername()); userMembership.setGroupParentPath(sUserMembership.getGroupParentPath()); return userMembership; } public static List toUserMembership(final List sUserMemberships) { final List userMemberships = new ArrayList<>(); if (sUserMemberships != null) { for (final SUserMembership sMembership : sUserMemberships) { final UserMembership userMembership = toUserMembership(sMembership); userMemberships.add(userMembership); } } return Collections.unmodifiableList(userMemberships); } public static List toUserMembership(final List sUserMemberships, final Map userNames, final Map groupIdToGroup) { final List userMemberships = new ArrayList<>(); if (sUserMemberships != null) { for (final SUserMembership sMembership : sUserMemberships) { final UserMembership userMembership = toUserMembership(sMembership, userNames, groupIdToGroup); userMemberships.add(userMembership); } } return Collections.unmodifiableList(userMemberships); } public static List toExportedUserMembership(final List sUserMemberships, final Map userNames, final Map groupIdToGroup) { final List userMemberships = new ArrayList<>(); if (sUserMemberships != null) { for (final SUserMembership sMembership : sUserMemberships) { final ExportedUserMembership userMembership = toExportedUserMembership(sMembership, userNames, groupIdToGroup); userMemberships.add(userMembership); } } return Collections.unmodifiableList(userMemberships); } private static UserMembership toUserMembership(final SUserMembership sUserMembership, final Map userNames, final Map groupIdToGroup) { final UserMembershipImpl userMembership = new UserMembershipImpl(sUserMembership.getId(), sUserMembership.getUserId(), sUserMembership.getGroupId(), sUserMembership.getRoleId()); userMembership.setGroupName(sUserMembership.getGroupName()); userMembership.setGroupParentPath(groupIdToGroup.get(sUserMembership.getGroupId())); userMembership.setRoleName(sUserMembership.getRoleName()); userMembership.setUsername(sUserMembership.getUsername()); final long assignedBy = sUserMembership.getAssignedBy(); userMembership.setAssignedBy(assignedBy); if (assignedBy > 0) { userMembership.setAssignedByName(userNames.get(assignedBy)); } userMembership.setAssignedDate(new Date(sUserMembership.getAssignedDate())); return userMembership; } private static ExportedUserMembership toExportedUserMembership(final SUserMembership sUserMembership, final Map userNames, final Map groupIdToGroup) { final ExportedUserMembership userMembership = new ExportedUserMembership(); userMembership.setGroupName(sUserMembership.getGroupName()); userMembership.setGroupParentPath(groupIdToGroup.get(sUserMembership.getGroupId())); userMembership.setRoleName(sUserMembership.getRoleName()); userMembership.setUserName(sUserMembership.getUsername()); final long assignedBy = sUserMembership.getAssignedBy(); userMembership.setAssignedBy(userNames.get(assignedBy)); userMembership.setAssignedDate(sUserMembership.getAssignedDate()); return userMembership; } public static Category toCategory(final SCategory sCategory) { final CategoryImpl category = new CategoryImpl(sCategory.getId(), sCategory.getName()); category.setDescription(sCategory.getDescription()); category.setCreator(sCategory.getCreator()); category.setCreationDate(new Date(sCategory.getCreationDate())); category.setLastUpdate(new Date(sCategory.getLastUpdateDate())); return category; } public static CommandDescriptor toCommandDescriptor(final SCommand command) { final CommandDescriptorImpl commandDescriptor = new CommandDescriptorImpl(command.getName(), command.getDescription(), command.getImplementation()); commandDescriptor.setId(command.getId()); commandDescriptor.setSystem(command.isSystem()); return commandDescriptor; } public static CommandDescriptor toCommandDescriptor(final SPlatformCommand platformCommand) { final CommandDescriptorImpl commandDescriptor = new CommandDescriptorImpl(platformCommand.getName(), platformCommand.getDescription(), platformCommand.getImplementation()); commandDescriptor.setId(platformCommand.getId()); return commandDescriptor; } public static List toCommandDescriptors(final List sCommands) { if (sCommands != null) { final List commandList = new ArrayList<>(); for (final SCommand sCommand : sCommands) { commandList.add(toCommandDescriptor(sCommand)); } return Collections.unmodifiableList(commandList); } return Collections.emptyList(); } public static List toPlatformCommandDescriptors(final List sPlatformCommands) { if (sPlatformCommands != null) { final List platformCommandList = new ArrayList<>(); for (final SPlatformCommand sCommand : sPlatformCommands) { platformCommandList.add(toCommandDescriptor(sCommand)); } return Collections.unmodifiableList(platformCommandList); } return Collections.emptyList(); } public static List toCategories(final List sCategories) { if (sCategories != null) { final List categoryList = new ArrayList<>(); for (final SCategory sCategory : sCategories) { categoryList.add(toCategory(sCategory)); } return Collections.unmodifiableList(categoryList); } return Collections.emptyList(); } public static List toEventInstances(final Collection sEvents, final FlowNodeStateManager flowNodeStateManager) { final List eventInstances = new ArrayList<>(); for (final SEventInstance sEvent : sEvents) { final EventInstance eventInstance = toEventInstance(sEvent, flowNodeStateManager); eventInstances.add(eventInstance); } return eventInstances; } public static EventInstance toEventInstance(final SEventInstance sEvent, final FlowNodeStateManager flowNodeStateManager) { final EventInstanceImpl eventInstance = getEventInstance(sEvent); updateFlowNode(eventInstance, sEvent, flowNodeStateManager.getState(sEvent.getStateId()).getName()); return eventInstance; } public static List toTimerEventTriggerInstances( final List sEventTriggerInstances) { final List eventTriggerInstances = new ArrayList<>(); for (final STimerEventTriggerInstance sEventTriggerInstance : sEventTriggerInstances) { final TimerEventTriggerInstance eventTriggerInstance = toTimerEventTriggerInstance(sEventTriggerInstance); eventTriggerInstances.add(eventTriggerInstance); } return eventTriggerInstances; } public static TimerEventTriggerInstance toTimerEventTriggerInstance( final STimerEventTriggerInstance sTimerEventTriggerInstance) { return new TimerEventTriggerInstanceImpl(sTimerEventTriggerInstance.getId(), sTimerEventTriggerInstance.getEventInstanceId(), sTimerEventTriggerInstance.getEventInstanceName(), new Date(sTimerEventTriggerInstance.getExecutionDate())); } public static WaitingEvent toWaitingEvent(final SWaitingEvent sWaitingEvent) { WaitingEventImpl waitingEvent; final BPMEventType bpmEventType = BPMEventType.valueOf(sWaitingEvent.getEventType().name()); final long processDefinitionId = sWaitingEvent.getProcessDefinitionId(); final String processName = sWaitingEvent.getProcessName(); final long flowNodeDefinitionId = sWaitingEvent.getFlowNodeDefinitionId(); switch (sWaitingEvent.getEventTriggerType()) { case ERROR: final SWaitingErrorEvent sWaitingErrorEvent = (SWaitingErrorEvent) sWaitingEvent; waitingEvent = new WaitingErrorEventImpl(bpmEventType, processDefinitionId, processName, flowNodeDefinitionId, sWaitingErrorEvent.getErrorCode()); break; case MESSAGE: final SWaitingMessageEvent sWaitingMessageEvent = (SWaitingMessageEvent) sWaitingEvent; waitingEvent = new WaitingMessageEventImpl(bpmEventType, processDefinitionId, processName, flowNodeDefinitionId, sWaitingMessageEvent.getMessageName()); break; case SIGNAL: final SWaitingSignalEvent sWaitingSignalEvent = (SWaitingSignalEvent) sWaitingEvent; waitingEvent = new WaitingSignalEventImpl(bpmEventType, processDefinitionId, processName, flowNodeDefinitionId, sWaitingSignalEvent.getSignalName()); break; default: throw new UnknownElementType(sWaitingEvent.getClass().getName()); } waitingEvent.setRootProcessInstanceId(sWaitingEvent.getRootProcessInstanceId()); waitingEvent.setParentProcessInstanceId(sWaitingEvent.getParentProcessInstanceId()); return waitingEvent; } public static List toWaitingEvents(final List sWaitingEvents) { final List waitingEvents = new ArrayList<>(sWaitingEvents.size()); for (final SWaitingEvent sWaitingEvent : sWaitingEvents) { waitingEvents.add(toWaitingEvent(sWaitingEvent)); } return Collections.unmodifiableList(waitingEvents); } private static EventInstanceImpl getEventInstance(final SEventInstance sEvent) { switch (sEvent.getType()) { case END_EVENT: return new EndEventInstanceImpl(sEvent.getName(), sEvent.getFlowNodeDefinitionId()); case INTERMEDIATE_CATCH_EVENT: return new IntermediateCatchEventInstanceImpl(sEvent.getName(), sEvent.getFlowNodeDefinitionId()); case INTERMEDIATE_THROW_EVENT: return new IntermediateThrowEventInstanceImpl(sEvent.getName(), sEvent.getFlowNodeDefinitionId()); case BOUNDARY_EVENT: return new BoundaryEventInstanceImpl(sEvent.getName(), sEvent.getFlowNodeDefinitionId(), ((SBoundaryEventInstance) sEvent).getActivityInstanceId()); case START_EVENT: return new StartEventInstanceImpl(sEvent.getName(), sEvent.getFlowNodeDefinitionId()); default: throw new UnknownElementType(sEvent.getType().name()); } } public static List toDataInstances(final List sDataInstances) { if (sDataInstances != null) { final List dataInstanceList = new ArrayList<>(); for (final SDataInstance sDataInstance : sDataInstances) { dataInstanceList.add(toDataInstance(sDataInstance)); } return Collections.unmodifiableList(dataInstanceList); } return Collections.emptyList(); } public static List toDataDefinitions(final List sDataDefinitions) { if (sDataDefinitions != null) { final List dataDefinitionList = new ArrayList<>(); for (final SDataDefinition sDataDefinition : sDataDefinitions) { dataDefinitionList.add(toDataDefinition(sDataDefinition)); } return Collections.unmodifiableList(dataDefinitionList); } return Collections.emptyList(); } public static DataDefinition toDataDefinition(final SDataDefinition sDataDefinition) { DataDefinitionImpl dataDefinitionImpl = null; if (sDataDefinition != null) { dataDefinitionImpl = new DataDefinitionImpl(sDataDefinition.getName(), toExpression(sDataDefinition.getDefaultValueExpression())); dataDefinitionImpl.setClassName(sDataDefinition.getClassName()); dataDefinitionImpl.setDescription(sDataDefinition.getDescription()); dataDefinitionImpl.setTransientData(sDataDefinition.isTransientData()); } return dataDefinitionImpl; } public static List toExpressions(final List sExpressions) { if (sExpressions != null && !sExpressions.isEmpty()) { final List expList = new ArrayList<>(sExpressions.size()); for (final SExpression sexp : sExpressions) { expList.add(toExpression(sexp)); } return expList; } return Collections.emptyList(); } public static Expression toExpression(final SExpression sexp) { final ExpressionImpl exp = new ExpressionImpl(); if (sexp != null) { exp.setContent(sexp.getContent()); exp.setExpressionType(sexp.getExpressionType()); exp.setInterpreter(sexp.getInterpreter()); exp.setName(sexp.getName()); exp.setReturnType(sexp.getReturnType()); exp.setDependencies(toExpressions(sexp.getDependencies())); } return exp; } public static DataInstance toDataInstance(final SDataInstance sDataInstance) { DataInstanceImpl dataInstance; if (sDataInstance.getClassName().equals(Integer.class.getName())) { dataInstance = new IntegerDataInstanceImpl(); } else if (sDataInstance.getClassName().equals(Long.class.getName())) { dataInstance = new LongDataInstanceImpl(); } else if (sDataInstance.getClassName().equals(Boolean.class.getName())) { dataInstance = new BooleanDataInstanceImpl(); } else if (sDataInstance.getClassName().equals(Date.class.getName())) { dataInstance = new DateDataInstanceImpl(); } else if (sDataInstance.getClassName().equals(Double.class.getName())) { dataInstance = new DoubleDataInstanceImpl(); } else if (sDataInstance.getClassName().equals(Float.class.getName())) { dataInstance = new FloatDataInstanceImpl(); } else if (sDataInstance.getClassName().equals(String.class.getName())) { dataInstance = new ShortTextDataInstanceImpl(); } else { dataInstance = new BlobDataInstanceImpl(); } dataInstance.setTransientData(sDataInstance.isTransientData()); dataInstance.setClassName(sDataInstance.getClassName()); dataInstance.setContainerId(sDataInstance.getContainerId()); dataInstance.setContainerType(sDataInstance.getContainerType()); dataInstance.setDataTypeClassName(sDataInstance.getClassName()); dataInstance.setDescription(sDataInstance.getDescription()); dataInstance.setId(sDataInstance.getId()); dataInstance.setName(sDataInstance.getName()); dataInstance.setValue(sDataInstance.getValue()); return dataInstance; } public static List toArchivedDataInstances(final List sADataInstances) { final List dataInstances = new ArrayList<>(); for (final SADataInstance sADataInstance : sADataInstances) { final ArchivedDataInstance dataInstance = toArchivedDataInstance(sADataInstance); dataInstances.add(dataInstance); } return dataInstances; } public static ArchivedDataInstance toArchivedDataInstance(final SADataInstance sDataInstance) { final ArchivedDataInstanceImpl dataInstance = new ArchivedDataInstanceImpl(); dataInstance.setClassName(sDataInstance.getClassName()); dataInstance.setContainerId(sDataInstance.getContainerId()); dataInstance.setContainerType(sDataInstance.getContainerType()); dataInstance.setDataTypeClassName(sDataInstance.getClassName()); dataInstance.setDescription(sDataInstance.getDescription()); dataInstance.setId(sDataInstance.getId()); dataInstance.setName(sDataInstance.getName()); dataInstance.setValue(sDataInstance.getValue()); dataInstance.setArchiveDate(new Date(sDataInstance.getArchiveDate())); dataInstance.setSourceObjectId(sDataInstance.getSourceObjectId()); return dataInstance; } public static ActorMember toActorMember(final SActorMember sActorMember) { return new ActorMemberImpl(sActorMember.getId(), sActorMember.getUserId(), sActorMember.getGroupId(), sActorMember.getRoleId()); } public static List toActorMembers(final List sActorMembers) { final List actorMembers = new ArrayList<>(); for (final SActorMember sActorMember : sActorMembers) { final ActorMember actorMember = toActorMember(sActorMember); actorMembers.add(actorMember); } return actorMembers; } public static ActorInstance toActorInstance(final SActor actor) { final String name = actor.getName(); final String description = actor.getDescription(); final long scopeId = actor.getScopeId(); final String displayName = actor.getDisplayName(); final boolean initiator = actor.isInitiator(); final ActorInstanceImpl actorInstance = new ActorInstanceImpl(name, description, displayName, scopeId, initiator); actorInstance.setId(actor.getId()); return actorInstance; } public static SUser constructSUser(final UserCreator creator) { final long now = System.currentTimeMillis(); final SUser.SUserBuilder userBuilder = SUser.builder(); final Map fields = creator.getFields(); userBuilder.userName((String) fields.get(UserField.NAME)); userBuilder.password((String) fields.get(UserField.PASSWORD)); final String firstName = (String) fields.get(UserField.FIRST_NAME); if (firstName != null) { userBuilder.firstName(firstName); } final String lastName = (String) fields.get(UserField.LAST_NAME); if (lastName != null) { userBuilder.lastName(lastName); } final String jobTitle = (String) fields.get(UserField.JOB_TITLE); if (jobTitle != null) { userBuilder.jobTitle(jobTitle); } final String title = (String) fields.get(UserField.TITLE); if (title != null) { userBuilder.title(title); } userBuilder.createdBy(SessionInfos.getUserIdFromSession()); final Long managerUserId = (Long) fields.get(UserField.MANAGER_ID); if (managerUserId != null) { userBuilder.managerUserId(managerUserId); } final Boolean enabled = (Boolean) fields.get(UserField.ENABLED); if (enabled != null) { userBuilder.enabled(enabled); } else { userBuilder.enabled(Boolean.TRUE); } userBuilder.creationDate(now); userBuilder.lastUpdate(now); return userBuilder.build(); } public static SContactInfo constructSUserContactInfo(final UserCreator creator, final long userId, final boolean personal) { Map fields; if (personal) { fields = creator.getPersoFields(); } else { fields = creator.getProFields(); } if (fields != null && !fields.isEmpty()) { final SContactInfo.SContactInfoBuilder contactInfoBuilder = SContactInfo.builder().userId(userId) .personal(personal); final String address = (String) fields.get(ContactDataField.ADDRESS); if (address != null) { contactInfoBuilder.address(address); } final String email = (String) fields.get(ContactDataField.EMAIL); if (email != null) { contactInfoBuilder.email(email); } final String building = (String) fields.get(ContactDataField.BUILDING); if (building != null) { contactInfoBuilder.building(building); } final String city = (String) fields.get(ContactDataField.CITY); if (city != null) { contactInfoBuilder.city(city); } final String country = (String) fields.get(ContactDataField.COUNTRY); if (country != null) { contactInfoBuilder.country(country); } final String fax = (String) fields.get(ContactDataField.FAX); if (fax != null) { contactInfoBuilder.faxNumber(fax); } final String mobile = (String) fields.get(ContactDataField.MOBILE); if (mobile != null) { contactInfoBuilder.mobileNumber(mobile); } final String phone = (String) fields.get(ContactDataField.PHONE); if (phone != null) { contactInfoBuilder.phoneNumber(phone); } final String room = (String) fields.get(ContactDataField.ROOM); if (room != null) { contactInfoBuilder.room(room); } final String state = (String) fields.get(ContactDataField.STATE); if (state != null) { contactInfoBuilder.state(state); } final String website = (String) fields.get(ContactDataField.WEBSITE); if (website != null) { contactInfoBuilder.website(website); } final String zipCode = (String) fields.get(ContactDataField.ZIP_CODE); if (zipCode != null) { contactInfoBuilder.zipCode(zipCode); } return contactInfoBuilder.build(); } return null; } public static SContactInfo constructSUserContactInfo(final ExportedUser user, final boolean isPersonal, final long userId) { final SContactInfo.SContactInfoBuilder contactInfoBuilder = SContactInfo.builder().userId(userId) .personal(isPersonal); if (isPersonal) { contactInfoBuilder.address(user.getPersonalAddress()); contactInfoBuilder.building(user.getPersonalBuilding()); contactInfoBuilder.city(user.getPersonalCity()); contactInfoBuilder.country(user.getPersonalCountry()); contactInfoBuilder.email(user.getPersonalEmail()); contactInfoBuilder.faxNumber(user.getPersonalFaxNumber()); contactInfoBuilder.mobileNumber(user.getPersonalMobileNumber()); contactInfoBuilder.phoneNumber(user.getPersonalPhoneNumber()); contactInfoBuilder.room(user.getPersonalRoom()); contactInfoBuilder.state(user.getPersonalState()); contactInfoBuilder.website(user.getPersonalWebsite()); contactInfoBuilder.zipCode(user.getPersonalZipCode()); } else { contactInfoBuilder.address(user.getProfessionalAddress()); contactInfoBuilder.building(user.getProfessionalBuilding()); contactInfoBuilder.city(user.getProfessionalCity()); contactInfoBuilder.country(user.getProfessionalCountry()); contactInfoBuilder.email(user.getProfessionalEmail()); contactInfoBuilder.faxNumber(user.getProfessionalFaxNumber()); contactInfoBuilder.mobileNumber(user.getProfessionalMobileNumber()); contactInfoBuilder.phoneNumber(user.getProfessionalPhoneNumber()); contactInfoBuilder.room(user.getProfessionalRoom()); contactInfoBuilder.state(user.getProfessionalState()); contactInfoBuilder.website(user.getProfessionalWebsite()); contactInfoBuilder.zipCode(user.getProfessionalZipCode()); } return contactInfoBuilder.build(); } public static SRole constructSRole(final RoleCreator creator) { final long now = System.currentTimeMillis(); final SRole.SRoleBuilder roleBuilder = SRole.builder(); roleBuilder.createdBy(SessionInfos.getUserIdFromSession()); roleBuilder.creationDate(now).lastUpdate(now); final Map fields = creator.getFields(); roleBuilder.name((String) fields.get(RoleField.NAME)); final String displayName = (String) fields.get(RoleField.DISPLAY_NAME); if (displayName != null) { roleBuilder.displayName(displayName); } final String description = (String) fields.get(RoleField.DESCRIPTION); if (description != null) { roleBuilder.description(description); } return roleBuilder.build(); } public static SRole constructSRole(final ExportedRole exportedRole) { final long now = System.currentTimeMillis(); final SRole.SRoleBuilder roleBuilder = SRole.builder(); roleBuilder.createdBy(SessionInfos.getUserIdFromSession()); roleBuilder.creationDate(now).lastUpdate(now); roleBuilder.name(exportedRole.getName()); roleBuilder.displayName(exportedRole.getDisplayName()); roleBuilder.description(exportedRole.getDescription()); return roleBuilder.build(); } public static SGroup constructSGroup(final GroupCreator creator) { final long now = System.currentTimeMillis(); final SGroup.SGroupBuilder groupBuilder = SGroup.builder(); groupBuilder.createdBy(SessionInfos.getUserIdFromSession()); groupBuilder.creationDate(now).lastUpdate(now); final Map fields = creator.getFields(); groupBuilder.name((String) fields.get(GroupField.NAME)); final String parentPath = (String) fields.get(GroupField.PARENT_PATH); if (parentPath != null && !parentPath.isEmpty()) { groupBuilder.parentPath(parentPath); } final String displayName = (String) fields.get(GroupField.DISPLAY_NAME); if (displayName != null) { groupBuilder.displayName(displayName); } final String description = (String) fields.get(GroupField.DESCRIPTION); if (description != null) { groupBuilder.description(description); } return groupBuilder.build(); } public static SGroup constructSGroup(final ExportedGroup exportedGroup) { final long now = System.currentTimeMillis(); final SGroup.SGroupBuilder groupBuilder = SGroup.builder(); groupBuilder.createdBy(SessionInfos.getUserIdFromSession()); groupBuilder.creationDate(now).lastUpdate(now); groupBuilder.name(exportedGroup.getName()); groupBuilder.parentPath(exportedGroup.getParentPath()); groupBuilder.displayName(exportedGroup.getDisplayName()); groupBuilder.description(exportedGroup.getDescription()); return groupBuilder.build(); } public static List toProcessSupervisors(final List sSupervisors) { final List processSupervisors = new ArrayList<>(); if (sSupervisors != null) { for (final SProcessSupervisor sSupervisor : sSupervisors) { processSupervisors.add(toProcessSupervisor(sSupervisor)); } } return processSupervisors; } public static ProcessSupervisor toProcessSupervisor(final SProcessSupervisor sSupervisor) { final ProcessSupervisorImpl supervisor = new ProcessSupervisorImpl(); supervisor.setId(sSupervisor.getId()); supervisor.setProcessDefinitionId(sSupervisor.getProcessDefId()); supervisor.setUserId(sSupervisor.getUserId()); supervisor.setGroupId(sSupervisor.getGroupId()); supervisor.setRoleId(sSupervisor.getRoleId()); return supervisor; } public static List toDocuments(final Collection mappedDocuments, final DocumentService documentService) { final List documents = new ArrayList<>(); for (final AbstractSMappedDocument mappedDocument : mappedDocuments) { final Document document = toDocument(mappedDocument, documentService); documents.add(document); } return documents; } public static Document toDocument(final AbstractSMappedDocument mappedDocument, final DocumentService documentService) { final DocumentImpl documentImpl = new DocumentImpl(); if (mappedDocument instanceof SAMappedDocument) { documentImpl.setId(((SAMappedDocument) mappedDocument).getSourceObjectId()); } else { documentImpl.setId(mappedDocument.getId()); } setDocumentFields(mappedDocument, documentService, documentImpl); return documentImpl; } private static void setDocumentFields(final AbstractSMappedDocument mappedDocument, final DocumentService documentService, final DocumentImpl documentImpl) { documentImpl.setProcessInstanceId(mappedDocument.getProcessInstanceId()); documentImpl.setName(mappedDocument.getName()); documentImpl.setDescription(mappedDocument.getDescription()); documentImpl.setVersion(mappedDocument.getVersion()); documentImpl.setAuthor(mappedDocument.getAuthor()); documentImpl.setCreationDate(new Date(mappedDocument.getCreationDate())); documentImpl.setHasContent(mappedDocument.hasContent()); documentImpl.setContentMimeType(mappedDocument.getMimeType()); documentImpl.setFileName(mappedDocument.getFileName()); documentImpl.setContentStorageId(String.valueOf(mappedDocument.getDocumentId())); documentImpl.setIndex(mappedDocument.getIndex()); if (mappedDocument.hasContent()) { documentImpl.setUrl(documentService.generateDocumentURL(mappedDocument.getFileName(), String.valueOf(mappedDocument.getDocumentId()))); } else { documentImpl.setUrl(mappedDocument.getUrl()); } } public static List toArchivedDocuments(final Collection mappedDocuments, final DocumentService documentService) { final List documents = new ArrayList<>(); for (final SAMappedDocument mappedDocument : mappedDocuments) { final ArchivedDocument document = toArchivedDocument(mappedDocument, documentService); documents.add(document); } return documents; } public static ArchivedDocument toArchivedDocument(final SAMappedDocument mappedDocument, final DocumentService documentService) { final ArchivedDocumentImpl documentImpl = new ArchivedDocumentImpl(mappedDocument.getName()); documentImpl.setId(mappedDocument.getId()); setDocumentFields(mappedDocument, documentService, documentImpl); documentImpl.setArchiveDate(new Date(mappedDocument.getArchiveDate())); documentImpl.setSourceObjectId(mappedDocument.getSourceObjectId()); return documentImpl; } public static int getServerActivityStateId(final String state) { int stateId; if (state.equalsIgnoreCase(ActivityStates.READY_STATE)) { stateId = 4; } else if (state.equalsIgnoreCase(ActivityStates.COMPLETING_STATE)) { stateId = 9; } else if (state.equalsIgnoreCase(ActivityStates.COMPLETED_STATE)) { stateId = 2; } else if (state.equalsIgnoreCase(ActivityStates.EXECUTING_STATE)) { stateId = 1; } else if (state.equalsIgnoreCase(ActivityStates.INITIALIZING_STATE)) { stateId = 0; } else if (state.equalsIgnoreCase(ActivityStates.SKIPPED_STATE)) { stateId = 12; } else if (state.equalsIgnoreCase(ActivityStates.CANCELLING_SUBTASKS_STATE)) { stateId = 13; } else if (state.equalsIgnoreCase(ActivityStates.CANCELLED_STATE)) { stateId = 14; } else if (state.equalsIgnoreCase(ActivityStates.FAILED_STATE)) { stateId = 3; } else { throw new IllegalArgumentException("Unknown activity state " + state); } return stateId; } public static ProcessInstanceState getProcessInstanceState(final String state) { if (state != null) { if (state.equalsIgnoreCase(ProcessInstanceState.ABORTED.toString())) { return ProcessInstanceState.ABORTED; } else if (state.equalsIgnoreCase(ProcessInstanceState.CANCELLED.toString())) { return ProcessInstanceState.CANCELLED; } else if (state.equalsIgnoreCase(ProcessInstanceState.COMPLETED.toString())) { return ProcessInstanceState.COMPLETED; } else if (state.equalsIgnoreCase(ProcessInstanceState.COMPLETING.toString())) { return ProcessInstanceState.COMPLETING; } else if (state.equalsIgnoreCase(ProcessInstanceState.ERROR.toString())) { return ProcessInstanceState.ERROR; } else if (state.equalsIgnoreCase(ProcessInstanceState.INITIALIZING.toString())) { return ProcessInstanceState.INITIALIZING; } else if (state.equalsIgnoreCase(ProcessInstanceState.STARTED.toString())) { return ProcessInstanceState.STARTED; } else if (state.equalsIgnoreCase(ProcessInstanceState.SUSPENDED.toString())) { return ProcessInstanceState.SUSPENDED; } } throw new IllegalArgumentException("Invalid process instance state: " + state); } public static Comment toComment(final SComment sComment) { final CommentImpl commentImpl = new CommentImpl(); commentImpl.setId(sComment.getId()); commentImpl.setUserId(sComment.getUserId()); commentImpl.setProcessInstanceId(sComment.getProcessInstanceId()); commentImpl.setPostDate(sComment.getPostDate()); commentImpl.setContent(sComment.getContent()); return commentImpl; } public static List toComments(final List sComments) { final List comments = new ArrayList<>(); for (final SComment sComment : sComments) { comments.add(toComment(sComment)); } return comments; } public static Map constructExpressions(final Map inputs) { final Map result = new HashMap<>(inputs.size()); for (final Entry expression : inputs.entrySet()) { result.put(expression.getKey(), constructSExpression(expression.getValue())); } return result; } public static SExpression constructSExpression(final Expression model) { final ArrayList dependencies = new ArrayList<>(); for (final Expression dep : model.getDependencies()) { dependencies.add(constructSExpression(dep)); } final SExpressionBuilder expressionBuilder = BuilderFactory.get(SExpressionBuilderFactory.class) .createNewInstance(); expressionBuilder.setName(model.getName()); expressionBuilder.setContent(model.getContent()); expressionBuilder.setExpressionType(model.getExpressionType()); expressionBuilder.setInterpreter(model.getInterpreter()); expressionBuilder.setReturnType(model.getReturnType()); expressionBuilder.setDependencies(dependencies); try { return expressionBuilder.done(); } catch (final SInvalidExpressionException e) { throw new IllegalArgumentException("Error constructing SExpression"); } } public static SOperation convertOperation(final Operation operation) { if (operation == null) { return null; } return BuilderFactory .get(SOperationBuilderFactory.class) .createNewInstance() .setOperator(operation.getOperator()) .setType(SOperatorType.valueOf(operation.getType().name())) .setRightOperand(ModelConvertor.constructSExpression(operation.getRightOperand())) .setLeftOperand( BuilderFactory.get(SLeftOperandBuilderFactory.class).createNewInstance() .setName(operation.getLeftOperand().getName()) .setType(operation.getLeftOperand().getType()).done()) .done(); } public static List convertOperations(final List operations) { if (operations == null) { return Collections.emptyList(); } final List sOperations = new ArrayList<>(operations.size()); for (final Operation operation : operations) { sOperations.add(convertOperation(operation)); } return sOperations; } public static List toConnectorImplementationDescriptors( final List sConnectorImplementationDescriptors) { if (sConnectorImplementationDescriptors != null) { final List connectorImplementationDescriptors = new ArrayList<>( sConnectorImplementationDescriptors.size()); for (final SConnectorImplementationDescriptor sConnectorImplementationDescriptor : sConnectorImplementationDescriptors) { connectorImplementationDescriptors .add(toConnectorImplementationDescriptor(sConnectorImplementationDescriptor)); } return connectorImplementationDescriptors; } return Collections.emptyList(); } public static ConnectorImplementationDescriptor toConnectorImplementationDescriptor( final SConnectorImplementationDescriptor sConnectorImplementationDescriptor) { return new ConnectorImplementationDescriptor(sConnectorImplementationDescriptor.getImplementationClassName(), sConnectorImplementationDescriptor.getId(), sConnectorImplementationDescriptor.getVersion(), sConnectorImplementationDescriptor.getDefinitionId(), sConnectorImplementationDescriptor.getDefinitionVersion(), sConnectorImplementationDescriptor.getJarDependencies()); } public static List toArchivedComments(final List serverObjects) { final List comments = new ArrayList<>(); for (final SAComment saComment : serverObjects) { final ArchivedComment comment = toArchivedComment(saComment); comments.add(comment); } return comments; } public static ArchivedComment toArchivedComment(final SAComment saComment) { final ArchivedCommentImpl commentImpl = new ArchivedCommentImpl(saComment.getContent()); commentImpl.setId(saComment.getId()); commentImpl.setProcessInstanceId(saComment.getProcessInstanceId()); commentImpl.setArchiveDate(new Date(saComment.getArchiveDate())); commentImpl.setContent(saComment.getContent()); commentImpl.setSourceObjectId(saComment.getSourceObjectId()); commentImpl.setUserId(saComment.getUserId()); commentImpl.setPostDate(new Date(saComment.getPostDate())); return commentImpl; } public static Operation toOperation(final SOperation operation) { final OperationImpl operationImpl = new OperationImpl(); operationImpl.setRightOperand(toExpression(operation.getRightOperand())); operationImpl.setOperator(operation.getOperator()); operationImpl.setType(toOperatorType(operation.getType())); final LeftOperandImpl leftOperand = new LeftOperandImpl(); final SLeftOperand sLeftOperand = operation.getLeftOperand(); leftOperand.setName(sLeftOperand.getName()); leftOperand.setType(sLeftOperand.getType()); operationImpl.setLeftOperand(leftOperand); return operationImpl; } private static OperatorType toOperatorType(final SOperatorType type) { OperatorType operatorType = null; if (SOperatorType.ASSIGNMENT.equals(type)) { operatorType = OperatorType.ASSIGNMENT; } else if (SOperatorType.JAVA_METHOD.equals(type)) { operatorType = OperatorType.JAVA_METHOD; } else if (SOperatorType.XPATH_UPDATE_QUERY.equals(type)) { operatorType = OperatorType.XPATH_UPDATE_QUERY; } return operatorType; } public static List toActors(final List sActors) { final List actors = new ArrayList<>(); for (final SActor sActor : sActors) { final ActorInstance actor = toActorInstance(sActor); actors.add(actor); } return actors; } public static List toArchivedFlowNodeInstances(final List saFlowNodes, final FlowNodeStateManager flowNodeStateManager) { final List flowNodeInstances = new ArrayList<>(); for (final SAFlowNodeInstance saFlowNode : saFlowNodes) { final ArchivedFlowNodeInstance flowNodeInstance = toArchivedFlowNodeInstance(saFlowNode, flowNodeStateManager); flowNodeInstances.add(flowNodeInstance); } return flowNodeInstances; } public static ArchivedFlowNodeInstance toArchivedFlowNodeInstance(final SAFlowNodeInstance saFlowNode, final FlowNodeStateManager flowNodeStateManager) { ArchivedFlowNodeInstance archiveFlowNodeInstance = null; switch (saFlowNode.getType()) { case AUTOMATIC_TASK: archiveFlowNodeInstance = toArchivedAutomaticTaskInstance((SAAutomaticTaskInstance) saFlowNode, flowNodeStateManager); break; case MANUAL_TASK: archiveFlowNodeInstance = toArchivedManualTaskInstance((SAManualTaskInstance) saFlowNode, flowNodeStateManager); break; case USER_TASK: archiveFlowNodeInstance = toArchivedUserTaskInstance((SAUserTaskInstance) saFlowNode, flowNodeStateManager); break; case RECEIVE_TASK: archiveFlowNodeInstance = toArchivedReceiveTaskInstance((SAReceiveTaskInstance) saFlowNode, flowNodeStateManager); break; case SEND_TASK: archiveFlowNodeInstance = toArchivedSendTaskInstance((SASendTaskInstance) saFlowNode, flowNodeStateManager); break; case CALL_ACTIVITY: archiveFlowNodeInstance = toArchivedCallActivityInstance((SACallActivityInstance) saFlowNode, flowNodeStateManager); break; case LOOP_ACTIVITY: archiveFlowNodeInstance = toArchivedLoopActivityInstance((SALoopActivityInstance) saFlowNode, flowNodeStateManager); break; case SUB_PROCESS: archiveFlowNodeInstance = toArchivedSubProcessActivityInstance( (SASubProcessActivityInstance) saFlowNode, flowNodeStateManager); break; case GATEWAY: archiveFlowNodeInstance = toArchivedGatewayInstance((SAGatewayInstance) saFlowNode, flowNodeStateManager); break; case MULTI_INSTANCE_ACTIVITY: archiveFlowNodeInstance = toArchivedMultiInstanceActivityInstance( (SAMultiInstanceActivityInstance) saFlowNode, flowNodeStateManager); break; case BOUNDARY_EVENT: case START_EVENT: case INTERMEDIATE_CATCH_EVENT: case INTERMEDIATE_THROW_EVENT: case END_EVENT: // archiveFlowNodeInstance = toArchivedEventInstance((SAEventInstance) saFlowNode, flowNodeStateManager); break; default: throw new UnknownElementType(saFlowNode.getType().name()); } return archiveFlowNodeInstance; } public static List toConnectorInstances(final List sConnectorInstances) { final ArrayList connectorInstances = new ArrayList<>(sConnectorInstances.size()); for (final SConnectorInstance sConnectorInstance : sConnectorInstances) { connectorInstances.add(toConnectorInstance(sConnectorInstance)); } return connectorInstances; } private static ConnectorInstance toConnectorInstance(final SConnectorInstance sConnectorInstance) { final ConnectorInstanceImpl connectorInstanceImpl = new ConnectorInstanceImpl(sConnectorInstance.getName(), sConnectorInstance.getContainerId(), sConnectorInstance.getContainerType(), sConnectorInstance.getConnectorId(), sConnectorInstance.getVersion(), ConnectorState.valueOf(sConnectorInstance.getState()), sConnectorInstance.getActivationEvent()); connectorInstanceImpl.setId(sConnectorInstance.getId()); return connectorInstanceImpl; } public static ConnectorInstanceWithFailureInfo toConnectorInstanceWithFailureInfo( final SConnectorInstanceWithFailureInfo sConnectorInstanceWithFailureInfo) { final ConnectorInstanceWithFailureInfoImpl connectorInstanceImpl = new ConnectorInstanceWithFailureInfoImpl( sConnectorInstanceWithFailureInfo.getName(), sConnectorInstanceWithFailureInfo.getContainerId(), sConnectorInstanceWithFailureInfo.getContainerType(), sConnectorInstanceWithFailureInfo.getConnectorId(), sConnectorInstanceWithFailureInfo.getVersion(), ConnectorState.valueOf(sConnectorInstanceWithFailureInfo.getState()), sConnectorInstanceWithFailureInfo.getActivationEvent(), sConnectorInstanceWithFailureInfo.getExceptionMessage(), sConnectorInstanceWithFailureInfo.getStackTrace()); connectorInstanceImpl.setId(sConnectorInstanceWithFailureInfo.getId()); return connectorInstanceImpl; } public static ArchivedConnectorInstance toArchivedConnectorInstance(final SAConnectorInstance sAConnectorInstance) { final ArchivedConnectorInstanceImpl connectorInstanceImpl = new ArchivedConnectorInstanceImpl( sAConnectorInstance.getName(), new Date( sAConnectorInstance.getArchiveDate()), sAConnectorInstance.getContainerId(), sAConnectorInstance.getContainerType(), sAConnectorInstance.getConnectorId(), sAConnectorInstance.getVersion(), sAConnectorInstance.getActivationEvent(), ConnectorState.valueOf(sAConnectorInstance.getState()), sAConnectorInstance.getSourceObjectId()); connectorInstanceImpl.setId(sAConnectorInstance.getId()); return connectorInstanceImpl; } public static List toArchivedConnectorInstances( final List serverObjects) { final List commments = new ArrayList<>(); for (final SAConnectorInstance saConnectorInstance : serverObjects) { final ArchivedConnectorInstance archivedConnectorInstance = toArchivedConnectorInstance( saConnectorInstance); commments.add(archivedConnectorInstance); } return commments; } public static List toProfiles(final List sProfiles) { final List profiles = new ArrayList<>(sProfiles.size()); for (final SProfile sProfile : sProfiles) { final Profile profile = toProfile(sProfile); profiles.add(profile); } return profiles; } public static Profile toProfile(final SProfile sProfile) { final ProfileImpl profileImpl = new ProfileImpl(sProfile.getName()); profileImpl.setId(sProfile.getId()); profileImpl.setDefault(sProfile.isDefault()); profileImpl.setDescription(sProfile.getDescription()); profileImpl.setCreationDate(new Date(sProfile.getCreationDate())); profileImpl.setCreatedBy(sProfile.getCreatedBy()); profileImpl.setLastUpdateDate(new Date(sProfile.getLastUpdateDate())); profileImpl.setLastUpdatedBy(sProfile.getLastUpdatedBy()); return profileImpl; } public static List toProfileMembers(final List sProfileMembers) { final List profiles = new ArrayList<>(sProfileMembers.size()); for (final SProfileMember sProfileMember : sProfileMembers) { final ProfileMember profile = toProfileMember(sProfileMember); profiles.add(profile); } return profiles; } public static ProfileMember toProfileMember(final SProfileMember sProfileMember) { final ProfileMemberImpl profileMemberImpl = new ProfileMemberImpl(); profileMemberImpl.setId(sProfileMember.getId()); profileMemberImpl.setDisplayNamePart1(sProfileMember.getDisplayNamePart1()); profileMemberImpl.setDisplayNamePart2(sProfileMember.getDisplayNamePart2()); profileMemberImpl.setDisplayNamePart3(sProfileMember.getDisplayNamePart3()); profileMemberImpl.setGroupId(sProfileMember.getGroupId()); profileMemberImpl.setProfileId(sProfileMember.getProfileId()); profileMemberImpl.setRoleId(sProfileMember.getRoleId()); profileMemberImpl.setUserId(sProfileMember.getUserId()); return profileMemberImpl; } public static SProfileMember constructSProfileMember(final ProfileMemberCreator creator) { final Map fields = creator.getFields(); final SProfileMember.SProfileMemberBuilder newSProfileMemberBuilder = SProfileMember.builder() .profileId((Long) fields.get(ProfileMemberField.PROFILE_ID)); final Long groupeId = (Long) fields.get(ProfileMemberField.GROUP_ID); if (groupeId != null) { newSProfileMemberBuilder.groupId(groupeId); } final Long roleId = (Long) fields.get(ProfileMemberField.ROLE_ID); if (roleId != null) { newSProfileMemberBuilder.roleId(roleId); } final Long userId = (Long) fields.get(ProfileMemberField.USER_ID); if (userId != null) { newSProfileMemberBuilder.userId(userId); } return newSProfileMemberBuilder.build(); } public static List toFailedJobs(final List sFailedJobs) { final List failedJobs = new ArrayList<>(sFailedJobs.size()); for (final SFailedJob sFailedJob : sFailedJobs) { failedJobs.add(toFailedJob(sFailedJob)); } return failedJobs; } public static FailedJob toFailedJob(final SFailedJob sFailedJob) { final FailedJobImpl failedJob = new FailedJobImpl(sFailedJob.getJobDescriptorId(), sFailedJob.getJobName()); failedJob.setDescription(sFailedJob.getDescription()); failedJob.setLastMessage(sFailedJob.getLastMessage()); failedJob.setNumberOfFailures(sFailedJob.getNumberOfFailures()); failedJob.setLastUpdateDate(new Date(sFailedJob.getLastUpdateDate())); return failedJob; } public static CustomUserInfoDefinitionImpl convert(final SCustomUserInfoDefinition sDefinition) { final CustomUserInfoDefinitionImpl definition = new CustomUserInfoDefinitionImpl(); definition.setId(sDefinition.getId()); definition.setName(sDefinition.getName()); definition.setDescription(sDefinition.getDescription()); return definition; } public static CustomUserInfoValueImpl convert(final SCustomUserInfoValue sValue) { if (sValue == null) { return null; } final CustomUserInfoValueImpl value = new CustomUserInfoValueImpl(); value.setDefinitionId(sValue.getDefinitionId()); value.setUserId(sValue.getUserId()); value.setValue(sValue.getValue()); return value; } public static FormMapping toFormMapping(final SFormMapping sFormMapping, final FormRequiredAnalyzer formRequiredAnalyzer) { if (sFormMapping == null) { return null; } final FormMapping formMapping = new FormMapping(); formMapping.setId(sFormMapping.getId()); formMapping.setTask(sFormMapping.getTask()); final SPageMapping pageMapping = sFormMapping.getPageMapping(); if (pageMapping != null) { formMapping.setPageMappingKey(pageMapping.getKey()); formMapping.setPageId(pageMapping.getPageId()); formMapping.setPageURL(pageMapping.getUrl()); } formMapping.setType(FormMappingType.getTypeFromId(sFormMapping.getType())); formMapping.setTarget( sFormMapping.getTarget() == null ? null : FormMappingTarget.valueOf(sFormMapping.getTarget())); formMapping.setProcessDefinitionId(sFormMapping.getProcessDefinitionId()); final long lastUpdateDate = sFormMapping.getLastUpdateDate(); formMapping.setLastUpdateDate(lastUpdateDate > 0 ? new Date(lastUpdateDate) : null); formMapping.setLastUpdatedBy(sFormMapping.getLastUpdatedBy()); formMapping.setFormRequired(formRequiredAnalyzer.isFormRequired(sFormMapping)); return formMapping; } public static List toFormMappings(final List serverObjects, final FormRequiredAnalyzer formRequiredAnalyzer) { final List clientObjects = new ArrayList<>(serverObjects.size()); for (final SFormMapping serverObject : serverObjects) { clientObjects.add(toFormMapping(serverObject, formRequiredAnalyzer)); } return clientObjects; } public static BusinessDataReference toBusinessDataReference( final SRefBusinessDataInstance sRefBusinessDataInstance) { if (sRefBusinessDataInstance == null) { return null; } if (sRefBusinessDataInstance instanceof SProcessMultiRefBusinessDataInstance multi) { return new MultipleBusinessDataReferenceImpl(multi.getName(), multi.getDataClassName(), multi.getDataIds()); } final SSimpleRefBusinessDataInstance simple = (SSimpleRefBusinessDataInstance) sRefBusinessDataInstance; return new SimpleBusinessDataReferenceImpl(simple.getName(), simple.getDataClassName(), simple.getDataId()); } public static ContractDefinition toContract(final SContractDefinition sContract) { if (sContract == null) { return null; } final ContractDefinitionImpl contract = new ContractDefinitionImpl(); for (final SInputDefinition input : sContract.getInputDefinitions()) { contract.addInput(toInput(input)); } for (final SConstraintDefinition sConstraintDefinition : sContract.getConstraints()) { final ConstraintDefinitionImpl constraint = new ConstraintDefinitionImpl(sConstraintDefinition.getName(), sConstraintDefinition.getExpression(), sConstraintDefinition.getExplanation()); for (final String inputName : sConstraintDefinition.getInputNames()) { constraint.addInputName(inputName); } contract.addConstraint(constraint); } return contract; } private static InputDefinition toInput(final SInputDefinition input) { final List inputDefinitions = new ArrayList<>(); for (final SInputDefinition sInputDefinition : input.getInputDefinitions()) { inputDefinitions.add(toInput(sInputDefinition)); } final SType type = input.getType(); final InputDefinitionImpl inputDefinition = new InputDefinitionImpl(input.getName(), type == null ? null : Type.valueOf(type.toString()), input.getDescription(), input.isMultiple()); inputDefinition.getInputs().addAll(inputDefinitions); return inputDefinition; } public static PageURL toPageURL(final SPageURL sPageURL) { return new PageURL(sPageURL.getUrl(), sPageURL.getPageId()); } public static List toExportedCustomUserInfoDefinition( List customUserInfoDefinitions) { ArrayList customUserInfoDefinitionCreators = new ArrayList<>( customUserInfoDefinitions.size()); for (SCustomUserInfoDefinition customUserInfoDefinition : customUserInfoDefinitions) { customUserInfoDefinitionCreators .add(new ExportedCustomUserInfoDefinition(customUserInfoDefinition.getName(), customUserInfoDefinition.getDescription())); } return customUserInfoDefinitionCreators; } public static Icon toIcon(SIcon icon) { return new IconImpl(icon.getId(), icon.getMimeType(), icon.getContent()); } public static TenantResource toTenantResource(STenantResourceLight r) { if (r == null) { return TenantResource.NONE; } return new TenantResource(r.getId(), r.getName(), org.bonitasoft.engine.tenant.TenantResourceType.valueOf(r.getType().name()), r.getLastUpdateDate(), r.getLastUpdatedBy(), TenantResourceState.valueOf(r.getState().name())); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/ProcessEngineServicesResolver.java ================================================ /** * 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; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; /** * @author Baptiste Mesta */ public class ProcessEngineServicesResolver implements ServicesLookup { @Override public T lookupService(String serviceName) { try { return ServiceAccessorFactory.getInstance().createServiceAccessor().lookup(serviceName); } catch (Exception e) { throw new IllegalStateException("Unable to find the service " + serviceName, e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/ServiceAccessor.java ================================================ /** * 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; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.authentication.GenericAuthenticationService; import org.bonitasoft.engine.authorization.PermissionService; import org.bonitasoft.engine.bar.BusinessArchiveService; import org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.importer.ApplicationImporter; import org.bonitasoft.engine.business.data.BusinessDataModelRepository; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.BusinessDataService; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.command.CommandService; import org.bonitasoft.engine.connector.ConnectorExecutor; import org.bonitasoft.engine.core.category.CategoryService; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.connector.ConnectorService; import org.bonitasoft.engine.core.contract.data.ContractDataService; import org.bonitasoft.engine.core.data.instance.TransientDataService; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.filter.UserFilterService; import org.bonitasoft.engine.core.form.FormMappingService; import org.bonitasoft.engine.core.login.LoginService; import org.bonitasoft.engine.core.login.TechnicalUser; import org.bonitasoft.engine.core.operation.OperationService; import org.bonitasoft.engine.core.platform.login.PlatformLoginService; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; 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.event.EventInstanceRepository; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.data.instance.api.ParentContainerResolver; import org.bonitasoft.engine.dependency.DependencyService; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.execution.ContainerRegistry; import org.bonitasoft.engine.execution.FlowNodeExecutor; import org.bonitasoft.engine.execution.ProcessExecutor; import org.bonitasoft.engine.execution.ProcessInstanceInterruptor; import org.bonitasoft.engine.execution.ProcessStarterVerifier; import org.bonitasoft.engine.execution.archive.BPMArchiverService; import org.bonitasoft.engine.execution.event.EventsHandler; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.execution.work.BPMWorkFactory; import org.bonitasoft.engine.expression.ExpressionService; import org.bonitasoft.engine.identity.IconService; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.incident.IncidentService; import org.bonitasoft.engine.lock.LockService; import org.bonitasoft.engine.message.MessagesHandlingService; import org.bonitasoft.engine.page.PageMappingService; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.parameter.ParameterService; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.platform.PlatformManager; import org.bonitasoft.engine.platform.PlatformRetriever; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.platform.authentication.PlatformAuthenticationService; import org.bonitasoft.engine.platform.command.PlatformCommandService; import org.bonitasoft.engine.platform.configuration.NodeConfiguration; import org.bonitasoft.engine.platform.session.PlatformSessionService; import org.bonitasoft.engine.profile.ProfileService; import org.bonitasoft.engine.profile.ProfilesExporter; import org.bonitasoft.engine.profile.ProfilesImporter; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.resources.ProcessResourcesService; import org.bonitasoft.engine.resources.TenantResourcesService; import org.bonitasoft.engine.scheduler.JobService; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; import org.bonitasoft.engine.services.QueriableLoggerService; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService; import org.bonitasoft.engine.synchro.SynchroService; import org.bonitasoft.engine.temporary.content.TemporaryContentService; import org.bonitasoft.engine.tenant.TenantServicesManager; import org.bonitasoft.engine.tenant.TenantStateManager; import org.bonitasoft.engine.tracking.TimeTracker; import org.bonitasoft.engine.transaction.TransactionService; import org.bonitasoft.engine.transaction.UserTransactionService; import org.bonitasoft.engine.work.WorkExecutorService; import org.bonitasoft.engine.work.WorkService; import org.springframework.context.ApplicationContext; /** * @author Matthieu Chaffotte */ public interface ServiceAccessor { ParentContainerResolver getParentContainerResolver(); SessionService getSessionService(); IdentityService getIdentityService(); IconService getIconService(); LoginService getLoginService(); QueriableLoggerService getQueriableLoggerService(); UserTransactionService getUserTransactionService(); ProcessDefinitionService getProcessDefinitionService(); ProcessInstanceService getProcessInstanceService(); ActivityInstanceService getActivityInstanceService(); BPMFailureService getBpmFailureService(); BPMInstancesCreator getBPMInstancesCreator(); FlowNodeExecutor getFlowNodeExecutor(); ProcessExecutor getProcessExecutor(); FlowNodeStateManager getFlowNodeStateManager(); ActorMappingService getActorMappingService(); ArchiveService getArchiveService(); CategoryService getCategoryService(); ExpressionService getExpressionService(); CommandService getCommandService(); ClassLoaderService getClassLoaderService(); DependencyService getDependencyService(); DependencyService getPlatformDependencyService(); EventInstanceService getEventInstanceService(); EventInstanceRepository getEventInstanceRepository(); ConnectorService getConnectorService(); ConnectorInstanceService getConnectorInstanceService(); DocumentService getDocumentService(); ProfileService getProfileService(); ProfilesImporter getProfilesImporter(); ProfilesExporter getProfilesExporter(); DataInstanceService getDataInstanceService(); TransientDataService getTransientDataService(); ExpressionResolverService getExpressionResolverService(); OperationService getOperationService(); SupervisorMappingService getSupervisorService(); UserFilterService getUserFilterService(); SearchEntitiesDescriptor getSearchEntitiesDescriptor(); SCommentService getCommentService(); ContainerRegistry getContainerRegistry(); LockService getLockService(); EventsHandler getEventsHandler(); EventService getEventService(); ConnectorExecutor getConnectorExecutor(); CacheService getCacheService(); BusinessArchiveArtifactsManager getBusinessArchiveArtifactsManager(); WorkService getWorkService(); WorkExecutorService getWorkExecutorService(); SessionAccessor getSessionAccessor(); SynchroService getSynchroService(); IncidentService getIncidentService(); SchedulerService getSchedulerService(); JobService getJobService(); T lookup(String serviceName) throws NotFoundException; T lookup(Class beanClass) throws NotFoundException; GatewayInstanceService getGatewayInstanceService(); void destroy(); TimeTracker getTimeTracker(); PermissionService getPermissionService(); ContractDataService getContractDataService(); ParameterService getParameterService(); PageService getPageService(); ApplicationService getApplicationService(); FormMappingService getFormMappingService(); BusinessDataRepository getBusinessDataRepository(); BusinessDataService getBusinessDataService(); BusinessDataModelRepository getBusinessDataModelRepository(); RefBusinessDataService getRefBusinessDataService(); PageMappingService getPageMappingService(); GenericAuthenticationService getAuthenticationService(); ReadPersistenceService getReadPersistenceService(); Recorder getRecorder(); BusinessArchiveService getBusinessArchiveService(); ProcessResourcesService getProcessResourcesService(); TenantResourcesService getTenantResourcesService(); MessagesHandlingService getMessagesHandlingService(); ProcessInstanceInterruptor getProcessInstanceInterruptor(); BPMWorkFactory getBPMWorkFactory(); TechnicalUser getTechnicalUser(); TenantStateManager getTenantStateManager(); TenantServicesManager getTenantServicesManager(); BPMArchiverService getBPMArchiverService(); ApplicationImporter getApplicationImporter(); PlatformService getPlatformService(); PlatformLoginService getPlatformLoginService(); TransactionService getTransactionService(); PlatformSessionService getPlatformSessionService(); PlatformCommandService getPlatformCommandService(); NodeConfiguration getPlatformConfiguration(); PlatformManager getPlatformManager(); CacheService getPlatformCacheService(); TemporaryContentService getTemporaryContentService(); BroadcastService getBroadcastService(); PlatformAuthenticationService getPlatformAuthenticationService(); ServicesResolver getServicesResolver(); void publishEvent(Object event); ApplicationContext getContext(); PlatformRetriever getPlatformRetriever(); InstallationService getInstallationService(); ProcessStarterVerifier getProcessStarterVerifier(); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/ServiceAccessorSingleton.java ================================================ /** * Copyright (C) 2019-2023 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; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; /** * @author Matthieu Chaffotte */ public final class ServiceAccessorSingleton { private static ServiceAccessor instance = null; private ServiceAccessorSingleton() { super(); } private static synchronized ServiceAccessor createNewInstance() { try { return ServiceAccessorFactory.getInstance().createServiceAccessor(); } catch (final Exception e) { throw new BonitaRuntimeException(e); } } public static ServiceAccessor getInstance() { if (instance == null) { instance = createNewInstance(); } return instance; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/APIAccessResolverImpl.java ================================================ /** * 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.impl; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.api.*; import org.bonitasoft.engine.api.impl.*; import org.bonitasoft.engine.api.impl.platform.PlatformInformationAPIImpl; import org.bonitasoft.engine.api.platform.PlatformInformationAPI; import org.bonitasoft.engine.exception.APIImplementationNotFoundException; import org.bonitasoft.engine.service.APIAccessResolver; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class APIAccessResolverImpl implements APIAccessResolver { private static final Map apis = new HashMap<>(); static { apis.put(PlatformAPI.class.getName(), new PlatformAPIImpl()); apis.put(PlatformLoginAPI.class.getName(), new PlatformLoginAPIImpl()); apis.put(PlatformCommandAPI.class.getName(), new PlatformCommandAPIImpl()); apis.put(LoginAPI.class.getName(), new LoginAPIImpl()); apis.put(IdentityAPI.class.getName(), new IdentityAPIImpl()); apis.put(ProcessAPI.class.getName(), new ProcessAPIImpl()); apis.put(CommandAPI.class.getName(), new CommandAPIImpl()); apis.put(ProfileAPI.class.getName(), new ProfileAPIImpl()); apis.put(PermissionAPI.class.getName(), new PermissionAPIImpl()); apis.put(PageAPI.class.getName(), new PageAPIImpl()); apis.put(ApplicationAPI.class.getName(), new ApplicationAPIImpl()); apis.put(TenantAdministrationAPI.class.getName(), new TenantAdministrationAPIImpl()); apis.put(BusinessDataAPI.class.getName(), new BusinessDataAPIImpl()); apis.put(TemporaryContentAPI.class.getName(), new TemporaryContentAPIImpl()); apis.put(MaintenanceAPI.class.getName(), new MaintenanceAPIImpl()); apis.put(PlatformInformationAPI.class.getName(), new PlatformInformationAPIImpl()); } @Override public T getAPIImplementation(Class apiInterface) throws APIImplementationNotFoundException { final Object api = getApiImplementation(apiInterface); if (api == null) { throw new APIImplementationNotFoundException( "No API implementation was found for: " + apiInterface.getName()); } return apiInterface.cast(api); } protected Object getApiImplementation(Class apiInterface) { return apis.get(apiInterface.getName()); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/BonitaSpringContext.java ================================================ /** * 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.impl; import java.util.ArrayList; import java.util.List; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractXmlApplicationContext; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; /** * @author Matthieu Chaffotte */ public class BonitaSpringContext extends AbstractXmlApplicationContext { private String name; private List resources = new ArrayList<>(); /** * Create a new XmlApplicationContext with the given parent, * loading the definitions from the given XML files and automatically * refreshing the context. * * @param parent the parent context * @throws BeansException if context creation failed */ public BonitaSpringContext(ApplicationContext parent, String name) throws BeansException { super(parent); this.name = name; } public String getName() { return name; } @Override protected Resource[] getConfigResources() { return resources.toArray(new Resource[resources.size()]); } public void addClassPathResource(String location) { ClassPathResource classPathResource = new ClassPathResource(location); if (classPathResource.exists()) { resources.add(classPathResource); } } public void addByteArrayResource(BonitaConfiguration configuration) { resources.add(new ByteArrayResource(configuration.getResourceContent(), configuration.getResourceName())); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/CustomPropertySource.java ================================================ /** * 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.impl; import java.util.Properties; import org.springframework.core.env.PropertySource; /** * @author Charles Souillard */ public class CustomPropertySource extends PropertySource { private final Properties properties; public CustomPropertySource(final String name, final Properties properties) { super(name); this.properties = properties; //System.err.println("----- CustomPropertySource(" + name + ") Thread: " + Thread.currentThread().getId() + "-----"); //Thread.dumpStack(); //System.err.println("Loading properties: " + properties); //System.err.println("----- END CustomPropertySource(" + name + ") Thread: " + Thread.currentThread().getId() + "-----"); } @Override public Object getProperty(String key) { final Object value = properties.get(key); System.err.println("--- (" + name + " --- Retrieving " + key + "=" + value); return value; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/MapToPropertiesFactoryBean.java ================================================ /** * 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.impl; import java.util.Map; import java.util.Properties; import org.springframework.beans.factory.config.AbstractFactoryBean; /** * @author Baptiste Mesta */ public class MapToPropertiesFactoryBean extends AbstractFactoryBean { private Map map; @Override public Class getObjectType() { return Properties.class; } @Override protected Properties createInstance() throws Exception { Properties properties = new Properties(); properties.putAll(map); return properties; } public void setMap(final Map map) { this.map = map; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/PlatformAuthenticationChecker.java ================================================ /** * 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.impl; import java.io.IOException; import java.util.Properties; import org.bonitasoft.engine.home.BonitaHomeServer; import org.bonitasoft.engine.platform.authentication.PlatformAuthenticationService; import org.bonitasoft.engine.platform.authentication.SInvalidPasswordException; import org.bonitasoft.engine.platform.authentication.SInvalidUserException; /** * @author Lu Kai * @author Matthieu Chaffotte * @author Frederic Bouquet */ public class PlatformAuthenticationChecker implements PlatformAuthenticationService { @Override public void checkUserCredentials(final String userName, final String password) throws SInvalidUserException, SInvalidPasswordException { try { final Properties properties = BonitaHomeServer.getInstance().getPlatformProperties(); final String userProperty = properties.getProperty("platformAdminUsername"); if (userProperty == null || !userProperty.equals(userName)) { throw new SInvalidUserException("Invalid user: " + userName); } final String passProperty = properties.getProperty("platformAdminPassword"); if (passProperty == null || !passProperty.equals(password)) { throw new SInvalidPasswordException("Invalid password"); } } catch (final IOException ioe) { throw new SInvalidUserException(ioe); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/ServerLoggerWrapper.java ================================================ /** * 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.impl; import org.bonitasoft.engine.api.Logger; /** * Wrap the technical logger service to be available to client extensions * * @author Baptiste Mesta */ public class ServerLoggerWrapper implements Logger { private Class clazz; private org.slf4j.Logger logger; public ServerLoggerWrapper(Class clazz, org.slf4j.Logger logger) { this.clazz = clazz; this.logger = logger; } @Override public void trace(String message, Throwable t) { logger.trace(message, t); } @Override public void trace(String message) { logger.trace(message); } @Override public void debug(String message, Throwable t) { logger.debug(message, t); } @Override public void debug(String message) { logger.debug(message); } @Override public void info(String message, Throwable t) { logger.info(message, t); } @Override public void info(String message) { logger.info(message); } @Override public void warning(String message, Throwable t) { logger.warn(message, t); } @Override public void warning(String message) { logger.warn(message); } @Override public void error(String message, Throwable t) { logger.error(message, t); } @Override public void error(String message) { logger.error(message); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/ServiceAccessorFactory.java ================================================ /** * 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.impl; import java.io.IOException; import org.bonitasoft.engine.exception.BonitaHomeConfigurationException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.home.BonitaHomeServer; import org.bonitasoft.engine.service.APIAccessResolver; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; /** * Singleton factory that creates and manages the main {@link ServiceAccessor} instance for the Bonita Engine. *

* This factory is the main entry point to access all engine services and API implementations. It manages * the lifecycle of the Spring application context that contains all engine services (100+ beans) and * provides access to core infrastructure components. *

* Key Responsibilities: *

    *
  • Service Accessor Creation: Creates and caches the main {@link ServiceAccessor} instance, * which wraps the engine's Spring application context
  • *
  • Session Accessor Creation: Provides {@link SessionAccessor} for thread-local session management
  • *
  • API Access Resolver: Creates {@link APIAccessResolver} for API implementation resolution
  • *
  • Lifecycle Management: Handles cleanup via {@link #destroyAccessors()} during engine shutdown
  • *
  • Configuration Loading: Loads class names from bonita-platform-private-community.properties
  • *
*

* Configuration Properties: * The factory reads the following properties from {@code bonita-platform-private-community.properties}: *

    *
  • {@code serviceAccessors}: Class name of {@link ServiceAccessors} implementation (default: * ServiceAccessorsImpl)
  • *
  • {@code apiAccessResolver}: Class name of {@link APIAccessResolver} implementation
  • *
*

* Usage Pattern: * *

 *
 * ServiceAccessor accessor = ServiceAccessorFactory.getInstance().createServiceAccessor();
 * PlatformService platformService = accessor.getPlatformService();
 * 
* *

* Spring Context Hierarchy: * The {@link ServiceAccessor} created by this factory contains the root Spring context for the engine, * which serves as the parent context for the web application context. *

* Thread Safety: All factory methods are synchronized to ensure thread-safe singleton creation. * * @see ServiceAccessor * @see SessionAccessor * @see APIAccessResolver */ public class ServiceAccessorFactory { private static final ServiceAccessorFactory INSTANCE = new ServiceAccessorFactory(); private static final String API_ACCESS_RESOLVER_CLASS_NAME = "apiAccessResolver"; private static final String SERVICE_ACCESSORS = "serviceAccessors"; private APIAccessResolver apiAccessResolver; private ServiceAccessors serviceAccessors; protected ServiceAccessorFactory() { super(); } public static ServiceAccessorFactory getInstance() { return INSTANCE; } public synchronized ServiceAccessor createServiceAccessor() throws BonitaHomeConfigurationException, IOException, ReflectiveOperationException { return getServiceAccessors().getServiceAccessor(); } private synchronized ServiceAccessors getServiceAccessors() throws BonitaHomeConfigurationException, IOException, ReflectiveOperationException { if (serviceAccessors == null) { serviceAccessors = (ServiceAccessors) loadClassFromPropertyName(SERVICE_ACCESSORS).getDeclaredConstructor() .newInstance(); } return serviceAccessors; } public SessionAccessor createSessionAccessor() throws BonitaHomeNotSetException, IOException, BonitaHomeConfigurationException, ReflectiveOperationException { return createServiceAccessor().getSessionAccessor(); } public synchronized APIAccessResolver createAPIAccessResolver() throws IOException, BonitaHomeConfigurationException, ReflectiveOperationException { if (apiAccessResolver == null) { apiAccessResolver = (APIAccessResolver) loadClassFromPropertyName(API_ACCESS_RESOLVER_CLASS_NAME) .getDeclaredConstructor().newInstance(); } return apiAccessResolver; } private Class loadClassFromPropertyName(String propertyName) throws IOException, BonitaHomeConfigurationException, ClassNotFoundException { final String sessionAccessorStr = BonitaHomeServer.getInstance().getPlatformProperties() .getProperty(propertyName); if (sessionAccessorStr == null) { throw new BonitaHomeConfigurationException( propertyName + " not set in bonita-platform-private-community.properties"); } return Class.forName(sessionAccessorStr); } public synchronized void destroyAccessors() { serviceAccessors.destroy(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/ServiceAccessors.java ================================================ /** * 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.impl; import org.bonitasoft.engine.service.ServiceAccessor; /** * This is the main entry point to the engine services * * @author Baptiste Mesta. */ public interface ServiceAccessors { ServiceAccessor getServiceAccessor(); void destroy(); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/SpringBeanAccessor.java ================================================ /** * 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.impl; import static org.bonitasoft.engine.Profiles.CLUSTER; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Properties; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.home.BonitaHomeServer; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertiesPropertySource; import org.springframework.core.env.StandardEnvironment; /** * Spring bean accessor that get its configuration from configuration file in classpath and in database * * @author Charles Souillard */ @Slf4j public class SpringBeanAccessor { private static final String HAZELCAST_CONFIG_FILENAME = "hazelcast.xml"; static final BonitaHomeServer BONITA_HOME_SERVER = BonitaHomeServer.getInstance(); private static final String WORK_CORE_POOL_SIZE = "bonita.tenant.work.corePoolSize"; private static final String WORK_MAX_POOL_SIZE = "bonita.tenant.work.maximumPoolSize"; private static final String WORK_KEEP_ALIVE_IN_SECONDS = "bonita.tenant.work.keepAliveTimeSeconds"; private static final String WORK_SQLSERVER_DELAY_ON_MULTIPLE_XA_RESOURCE = "bonita.tenant.work.sqlserver.delayOnMultipleXAResource"; private static final String WORK_MYSQL_DELAY_ON_MULTIPLE_XA_RESOURCE = "bonita.tenant.work.mysql.delayOnMultipleXAResource"; private static final String WORK_ORACLE_DELAY_ON_MULTIPLE_XA_RESOURCE = "bonita.tenant.work.oracle.delayOnMultipleXAResource"; private static final String CONNECTOR_CORE_POOL_SIZE = "bonita.tenant.connector.corePoolSize"; private static final String CONNECTOR_MAX_POOL_SIZE = "bonita.tenant.connector.maximumPoolSize"; private static final String CONNECTOR_KEEP_ALIVE_IN_SECONDS = "bonita.tenant.connector.keepAliveTimeSeconds"; private static final String PROMOTION_MESSAGES_ENABLED = "bonita.runtime.promotion.messages.enabled"; private BonitaSpringContext context; private boolean contextFinishedInitialized = false; public T getService(final Class serviceClass) { return getContext().getBean(serviceClass); } T getService(String name, Class clazz) { return getContext().getBean(name, clazz); } T getService(String serviceName) { return (T) getContext().getBean(serviceName); } public ApplicationContext getContext() { if (!contextFinishedInitialized) { initializeContext(); } return context; } private synchronized void initializeContext() { if (contextFinishedInitialized) { return; } try { context = createContext(); configureContext(context); context.refresh(); contextFinishedInitialized = true; } catch (IOException e) { throw new BonitaRuntimeException(e); } } private void configureContext(BonitaSpringContext context) throws IOException { boolean isCluster = isCluster(); for (String classPathResource : getSpringFileFromClassPath(isCluster)) { context.addClassPathResource(classPathResource); } if (isCluster) { context.getEnvironment().setActiveProfiles(CLUSTER); } for (BonitaConfiguration bonitaConfiguration : getConfigurationFromDatabase()) { context.addByteArrayResource(bonitaConfiguration); } MutablePropertySources propertySources = context.getEnvironment().getPropertySources(); final boolean legacyMode = context.getEnvironment().getProperty("bonita.runtime.properties.order.legacy-mode", boolean.class, false); if (legacyMode) { // continue to have properties files from database with the higher priority order. propertySources.addFirst(new PropertiesPropertySource("contextProperties", getProperties())); } else { // Make values from database be easily overridable with default Spring mechanism. // This is achieved by adding Bonita properties from database with a priority just AFTER // OS environment variables and Java System properties. // For default Spring property order, see // https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config propertySources.addAfter(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, new PropertiesPropertySource("contextProperties", getProperties())); } warnDeprecatedProperties(propertySources); } protected void warnDeprecatedProperties(MutablePropertySources propertySources) { warnIfPropertyIsDeprecated(propertySources, WORK_CORE_POOL_SIZE); warnIfPropertyIsDeprecated(propertySources, WORK_MAX_POOL_SIZE); warnIfPropertyIsDeprecated(propertySources, WORK_KEEP_ALIVE_IN_SECONDS); warnIfPropertyIsDeprecated(propertySources, WORK_SQLSERVER_DELAY_ON_MULTIPLE_XA_RESOURCE); warnIfPropertyIsDeprecated(propertySources, WORK_MYSQL_DELAY_ON_MULTIPLE_XA_RESOURCE); warnIfPropertyIsDeprecated(propertySources, WORK_ORACLE_DELAY_ON_MULTIPLE_XA_RESOURCE); warnIfPropertyIsDeprecated(propertySources, CONNECTOR_CORE_POOL_SIZE); warnIfPropertyIsDeprecated(propertySources, CONNECTOR_MAX_POOL_SIZE); warnIfPropertyIsDeprecated(propertySources, CONNECTOR_KEEP_ALIVE_IN_SECONDS); warnIfPropertyIsDeprecated(propertySources, PROMOTION_MESSAGES_ENABLED); } private void warnIfPropertyIsDeprecated(MutablePropertySources propertySources, String property) { propertySources.stream() .filter(ps -> ps.containsProperty(property)) .map(ps -> ps.getProperty(property)) .filter(Objects::nonNull) .findFirst() .ifPresent(value -> log.warn( "{} property is not supported in community edition anymore. It will be ignored.", property)); } protected BonitaSpringContext createContext() { return new BonitaSpringContext(null, "Platform"); } public void destroy() { if (context != null) { context.close(); context = null; } contextFinishedInitialized = false; } protected Properties getProperties() throws IOException { Properties platformProperties = BONITA_HOME_SERVER.getPlatformProperties(); platformProperties.putAll(BONITA_HOME_SERVER.getTenantProperties()); return platformProperties; } protected List getConfigurationFromDatabase() throws IOException { List bonitaConfigurations = new ArrayList<>(); List platformConfiguration = BONITA_HOME_SERVER.getPlatformConfiguration(); extractHazelcastConfigurationFile(platformConfiguration); bonitaConfigurations.addAll(platformConfiguration); bonitaConfigurations.addAll(BONITA_HOME_SERVER.getTenantConfiguration()); return bonitaConfigurations; } private static void extractHazelcastConfigurationFile(List platformConfiguration) throws IOException { // handle special case for Hazelcast configuration file: Iterator iterator = platformConfiguration.iterator(); while (iterator.hasNext()) { BonitaConfiguration bonitaConfiguration = iterator.next(); if (HAZELCAST_CONFIG_FILENAME.equals(bonitaConfiguration.getResourceName())) { iterator.remove(); final File hzConfigFile = new File(IOUtil.TMP_DIRECTORY, HAZELCAST_CONFIG_FILENAME); if (!hzConfigFile.exists()) { Files.write(hzConfigFile.toPath(), bonitaConfiguration.getResourceContent()); hzConfigFile.deleteOnExit(); } String hazelcastConfigFile = hzConfigFile.getAbsolutePath(); // Allow to preserve "hazelcast.config" if already passed as System property: if (!System.getProperties().containsKey("hazelcast.config")) { log.info("Setting sysprop 'hazelcast.config' to {}", hazelcastConfigFile); System.setProperty("hazelcast.config", hazelcastConfigFile); System.setProperty("hibernate.javax.cache.uri", new File(hazelcastConfigFile).toURI().toString()); } else { log.info("Sysprop 'hazelcast.config' already set to '{}'. Preserving this value.", System.getProperty("hazelcast.config")); } return; // found, no need to go further } } } protected List getSpringFileFromClassPath(boolean cluster) { return List.of("bonita-community.xml", "bonita-subscription.xml"); } String getPropertyWithPlaceholder(Properties properties, String key, String defaultValue) { String property = properties.getProperty(key, defaultValue); if (property.startsWith("${") && property.endsWith("}")) { property = property.substring(2, property.length() - 1); String sysPropertyKey = property.substring(0, property.indexOf(':')); String sysPropertyDefaultValue = property.substring(property.indexOf(':') + 1); return System.getProperty(sysPropertyKey, sysPropertyDefaultValue); } return property; } protected boolean isCluster() throws IOException { return Boolean.parseBoolean( getPropertyWithPlaceholder(BONITA_HOME_SERVER.getPlatformProperties(), "bonita.cluster", "false")); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/SpringServiceAccessor.java ================================================ /** * Copyright (C) 2023 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.impl; import org.bonitasoft.engine.actor.mapping.ActorMappingService; import org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.authentication.GenericAuthenticationService; import org.bonitasoft.engine.authentication.GenericAuthenticationServiceAccessor; import org.bonitasoft.engine.authorization.PermissionService; import org.bonitasoft.engine.bar.BusinessArchiveService; import org.bonitasoft.engine.bpm.model.impl.BPMInstancesCreator; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.importer.ApplicationImporter; import org.bonitasoft.engine.business.data.BusinessDataModelRepository; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.BusinessDataService; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.command.CommandService; import org.bonitasoft.engine.connector.ConnectorExecutor; import org.bonitasoft.engine.core.category.CategoryService; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.connector.ConnectorService; import org.bonitasoft.engine.core.contract.data.ContractDataService; import org.bonitasoft.engine.core.data.instance.TransientDataService; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.filter.UserFilterService; import org.bonitasoft.engine.core.form.FormMappingService; import org.bonitasoft.engine.core.login.LoginService; import org.bonitasoft.engine.core.login.TechnicalUser; import org.bonitasoft.engine.core.operation.OperationService; import org.bonitasoft.engine.core.platform.login.PlatformLoginService; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; 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.event.EventInstanceRepository; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.data.instance.api.ParentContainerResolver; import org.bonitasoft.engine.dependency.DependencyService; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.execution.ContainerRegistry; import org.bonitasoft.engine.execution.FlowNodeExecutor; import org.bonitasoft.engine.execution.ProcessExecutor; import org.bonitasoft.engine.execution.ProcessInstanceInterruptor; import org.bonitasoft.engine.execution.ProcessStarterVerifier; import org.bonitasoft.engine.execution.archive.BPMArchiverService; import org.bonitasoft.engine.execution.event.EventsHandler; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.execution.work.BPMWorkFactory; import org.bonitasoft.engine.expression.ExpressionService; import org.bonitasoft.engine.identity.IconService; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.incident.IncidentService; import org.bonitasoft.engine.lock.LockService; import org.bonitasoft.engine.message.MessagesHandlingService; import org.bonitasoft.engine.page.PageMappingService; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.parameter.ParameterService; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.platform.PlatformManager; import org.bonitasoft.engine.platform.PlatformRetriever; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.platform.authentication.PlatformAuthenticationService; import org.bonitasoft.engine.platform.command.PlatformCommandService; import org.bonitasoft.engine.platform.configuration.NodeConfiguration; import org.bonitasoft.engine.platform.session.PlatformSessionService; import org.bonitasoft.engine.profile.ProfileService; import org.bonitasoft.engine.profile.ProfilesExporter; import org.bonitasoft.engine.profile.ProfilesImporter; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.resources.ProcessResourcesService; import org.bonitasoft.engine.resources.TenantResourcesService; import org.bonitasoft.engine.scheduler.JobService; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; import org.bonitasoft.engine.service.BroadcastService; import org.bonitasoft.engine.service.InstallationService; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServicesResolver; import org.bonitasoft.engine.services.QueriableLoggerService; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService; import org.bonitasoft.engine.synchro.SynchroService; import org.bonitasoft.engine.temporary.content.TemporaryContentService; import org.bonitasoft.engine.tenant.TenantServicesManager; import org.bonitasoft.engine.tenant.TenantStateManager; import org.bonitasoft.engine.tracking.TimeTracker; import org.bonitasoft.engine.transaction.TransactionService; import org.bonitasoft.engine.transaction.UserTransactionService; import org.bonitasoft.engine.work.WorkExecutorService; import org.bonitasoft.engine.work.WorkService; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; public class SpringServiceAccessor implements ServiceAccessor { protected final SpringBeanAccessor beanAccessor; public SpringServiceAccessor(final SpringBeanAccessor beanAccessor) { this.beanAccessor = beanAccessor; } @Override public ParentContainerResolver getParentContainerResolver() { return beanAccessor.getService(ParentContainerResolver.class); } @Override public TimeTracker getTimeTracker() { return beanAccessor.getService(TimeTracker.class); } @Override public SessionAccessor getSessionAccessor() { return beanAccessor.getService(SessionAccessor.class); } @Override public SessionService getSessionService() { return beanAccessor.getService(SessionService.class); } @Override public IdentityService getIdentityService() { return beanAccessor.getService(IdentityService.class); } @Override public IconService getIconService() { return beanAccessor.getService(IconService.class); } @Override public LoginService getLoginService() { return beanAccessor.getService(LoginService.class); } @Override public QueriableLoggerService getQueriableLoggerService() { return beanAccessor.getService("queriableLoggerService", QueriableLoggerService.class); } @Override public UserTransactionService getUserTransactionService() { return getTransactionService(); } @Override public ProcessDefinitionService getProcessDefinitionService() { return beanAccessor.getService(ProcessDefinitionService.class); } @Override public ProcessInstanceService getProcessInstanceService() { return beanAccessor.getService(ProcessInstanceService.class); } @Override public ActivityInstanceService getActivityInstanceService() { return beanAccessor.getService(ActivityInstanceService.class); } @Override public BPMFailureService getBpmFailureService() { return beanAccessor.getService(BPMFailureService.class); } @Override public BPMInstancesCreator getBPMInstancesCreator() { return beanAccessor.getService(BPMInstancesCreator.class); } @Override public FlowNodeExecutor getFlowNodeExecutor() { return beanAccessor.getService(FlowNodeExecutor.class); } @Override public ProcessExecutor getProcessExecutor() { return beanAccessor.getService(ProcessExecutor.class); } @Override public FlowNodeStateManager getFlowNodeStateManager() { return beanAccessor.getService(FlowNodeStateManager.class); } @Override public ActorMappingService getActorMappingService() { return beanAccessor.getService(ActorMappingService.class); } @Override public ArchiveService getArchiveService() { return beanAccessor.getService(ArchiveService.class); } @Override public CategoryService getCategoryService() { return beanAccessor.getService(CategoryService.class); } @Override public CommandService getCommandService() { return beanAccessor.getService(CommandService.class); } @Override public ClassLoaderService getClassLoaderService() { return beanAccessor.getService(ClassLoaderService.class); } @Override public DependencyService getDependencyService() { // default implementation taken from SpringTenantServiceAccessor return beanAccessor.getService("dependencyService", DependencyService.class); } @Override public DependencyService getPlatformDependencyService() { return beanAccessor.getService("platformDependencyService", DependencyService.class); } @Override public EventInstanceService getEventInstanceService() { return beanAccessor.getService(EventInstanceService.class); } @Override public EventInstanceRepository getEventInstanceRepository() { return beanAccessor.getService(EventInstanceRepository.class); } @Override public ConnectorService getConnectorService() { return beanAccessor.getService("connectorService", ConnectorService.class); } @Override public ConnectorInstanceService getConnectorInstanceService() { return beanAccessor.getService(ConnectorInstanceService.class); } @Override public ConnectorExecutor getConnectorExecutor() { return beanAccessor.getService(ConnectorExecutor.class); } @Override public ExpressionService getExpressionService() { return beanAccessor.getService(ExpressionService.class); } @Override public DocumentService getDocumentService() { return beanAccessor.getService(DocumentService.class); } @Override public ProfileService getProfileService() { return beanAccessor.getService(ProfileService.class); } @Override public ProfilesImporter getProfilesImporter() { return beanAccessor.getService(ProfilesImporter.class); } @Override public ProfilesExporter getProfilesExporter() { return beanAccessor.getService(ProfilesExporter.class); } @Override public DataInstanceService getDataInstanceService() { return beanAccessor.getService(DataInstanceService.class); } @Override public OperationService getOperationService() { return beanAccessor.getService(OperationService.class); } @Override public ExpressionResolverService getExpressionResolverService() { return beanAccessor.getService(ExpressionResolverService.class); } @Override public SupervisorMappingService getSupervisorService() { return beanAccessor.getService(SupervisorMappingService.class); } @Override public UserFilterService getUserFilterService() { return beanAccessor.getService("userFilterService", UserFilterService.class); } @Override public SearchEntitiesDescriptor getSearchEntitiesDescriptor() { return beanAccessor.getService(SearchEntitiesDescriptor.class); } @Override public SCommentService getCommentService() { return beanAccessor.getService(SCommentService.class); } @Override public ContainerRegistry getContainerRegistry() { return beanAccessor.getService(ContainerRegistry.class); } @Override public LockService getLockService() { return beanAccessor.getService(LockService.class); } @Override public EventsHandler getEventsHandler() { return beanAccessor.getService(EventsHandler.class); } @Override public EventService getEventService() { return beanAccessor.getService("platformEventService", EventService.class); } public SpringBeanAccessor getBeanAccessor() { return beanAccessor; } @Override public CacheService getCacheService() { return beanAccessor.getService(CacheService.class); } @Override public BusinessArchiveArtifactsManager getBusinessArchiveArtifactsManager() { return beanAccessor.getService(BusinessArchiveArtifactsManager.class); } @Override public WorkService getWorkService() { return beanAccessor.getService(WorkService.class); } @Override public WorkExecutorService getWorkExecutorService() { return beanAccessor.getService(WorkExecutorService.class); } @Override public SynchroService getSynchroService() { return beanAccessor.getService(SynchroService.class); } @Override public IncidentService getIncidentService() { return beanAccessor.getService(IncidentService.class); } @Override public SchedulerService getSchedulerService() { return beanAccessor.getService(SchedulerService.class); } @Override public JobService getJobService() { return beanAccessor.getService(JobService.class); } @Override public TransientDataService getTransientDataService() { return beanAccessor.getService(TransientDataService.class); } @Override public GatewayInstanceService getGatewayInstanceService() { return beanAccessor.getService(GatewayInstanceService.class); } @Override public void destroy() { beanAccessor.destroy(); } @Override public T lookup(final String serviceName) throws NotFoundException { try { return beanAccessor.getService(serviceName); } catch (NoSuchBeanDefinitionException e) { throw new NotFoundException(e); } } @Override public T lookup(final Class beanClass) throws NotFoundException { try { return beanAccessor.getService(beanClass); } catch (NoSuchBeanDefinitionException e) { throw new NotFoundException(e); } } @Override public PermissionService getPermissionService() { return beanAccessor.getService(PermissionService.class); } @Override public ContractDataService getContractDataService() { return beanAccessor.getService(ContractDataService.class); } @Override public ParameterService getParameterService() { return beanAccessor.getService(ParameterService.class); } /** * might not be an available service */ @Override public PageService getPageService() { return beanAccessor.getService(PageService.class); } @Override public ApplicationService getApplicationService() { return beanAccessor.getService(ApplicationService.class); } @Override public BusinessDataRepository getBusinessDataRepository() { return beanAccessor.getService(BusinessDataRepository.class); } @Override public BusinessDataModelRepository getBusinessDataModelRepository() { return beanAccessor.getService(BusinessDataModelRepository.class); } @Override public RefBusinessDataService getRefBusinessDataService() { return beanAccessor.getService(RefBusinessDataService.class); } @Override public GenericAuthenticationService getAuthenticationService() { return beanAccessor.getService(GenericAuthenticationServiceAccessor.class).getAuthenticationService(); } @Override public ReadPersistenceService getReadPersistenceService() { return beanAccessor.getService("persistenceService"); } @Override public Recorder getRecorder() { return beanAccessor.getService(Recorder.class); } @Override public BusinessArchiveService getBusinessArchiveService() { return beanAccessor.getService(BusinessArchiveService.class); } @Override public BusinessDataService getBusinessDataService() { return beanAccessor.getService(BusinessDataService.class); } @Override public FormMappingService getFormMappingService() { return beanAccessor.getService(FormMappingService.class); } @Override public PageMappingService getPageMappingService() { return beanAccessor.getService(PageMappingService.class); } public ProcessResourcesService getProcessResourcesService() { return beanAccessor.getService(ProcessResourcesService.class); } public TenantResourcesService getTenantResourcesService() { return beanAccessor.getService(TenantResourcesService.class); } public MessagesHandlingService getMessagesHandlingService() { return beanAccessor.getService(MessagesHandlingService.class); } @Override public ProcessInstanceInterruptor getProcessInstanceInterruptor() { return beanAccessor.getService(ProcessInstanceInterruptor.class); } public BPMWorkFactory getBPMWorkFactory() { return beanAccessor.getService(BPMWorkFactory.class); } public TechnicalUser getTechnicalUser() { return beanAccessor.getService(TechnicalUser.class); } @Override public TenantStateManager getTenantStateManager() { return beanAccessor.getService(TenantStateManager.class); } @Override public TenantServicesManager getTenantServicesManager() { return beanAccessor.getService(TenantServicesManager.class); } @Override public BPMArchiverService getBPMArchiverService() { return beanAccessor.getService(BPMArchiverService.class); } @Override public ApplicationImporter getApplicationImporter() { return beanAccessor.getService(ApplicationImporter.class); } @Override public TransactionService getTransactionService() { return beanAccessor.getService(TransactionService.class); } @Override public PlatformLoginService getPlatformLoginService() { return beanAccessor.getService(PlatformLoginService.class); } @Override public PlatformService getPlatformService() { return beanAccessor.getService(PlatformService.class); } @Override public PlatformCommandService getPlatformCommandService() { return beanAccessor.getService("platformCommandService", PlatformCommandService.class); } @Override public PlatformSessionService getPlatformSessionService() { return beanAccessor.getService(PlatformSessionService.class); } @Override public NodeConfiguration getPlatformConfiguration() { return beanAccessor.getService(NodeConfiguration.class); } @Override public PlatformManager getPlatformManager() { return beanAccessor.getService(PlatformManager.class); } @Override public CacheService getPlatformCacheService() { return beanAccessor.getService(CacheService.class); } @Override public TemporaryContentService getTemporaryContentService() { return beanAccessor.getService(TemporaryContentService.class); } @Override public BroadcastService getBroadcastService() { return beanAccessor.getService(BroadcastService.class); } @Override public PlatformAuthenticationService getPlatformAuthenticationService() { return beanAccessor.getService(PlatformAuthenticationService.class); } @Override public ServicesResolver getServicesResolver() { return beanAccessor.getService(ServicesResolver.class); } @Override public void publishEvent(final Object event) { beanAccessor.getContext().publishEvent(event); } @Override public ApplicationContext getContext() { return beanAccessor.getContext(); } @Override public PlatformRetriever getPlatformRetriever() { return beanAccessor.getService(PlatformRetriever.class); } @Override public InstallationService getInstallationService() { return beanAccessor.getService(InstallationService.class); } @Override public ProcessStarterVerifier getProcessStarterVerifier() { return beanAccessor.getService(ProcessStarterVerifier.class); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/SpringServiceAccessors.java ================================================ /** * 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.impl; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Baptiste Mesta. */ public class SpringServiceAccessors implements ServiceAccessors { private SpringBeanAccessor springBeanAccessor; //---- Initialize spring contexts protected synchronized SpringBeanAccessor getBeanAccessor() { if (springBeanAccessor == null) { springBeanAccessor = createBeanAccessor(); } return springBeanAccessor; } protected SpringBeanAccessor createBeanAccessor() { return new SpringBeanAccessor(); } @Override public ServiceAccessor getServiceAccessor() { return new SpringServiceAccessor(getBeanAccessor()); } @Override public void destroy() { if (springBeanAccessor != null) { springBeanAccessor.destroy(); springBeanAccessor = null; } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/installation/ConfigurationArchive.java ================================================ /** * 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.impl.installation; import static java.util.stream.Collectors.toMap; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import lombok.extern.slf4j.Slf4j; @Slf4j public class ConfigurationArchive implements AutoCloseable { private static final String PARAMETERS_FILENAME = "parameters.properties"; private final File tmpFile; private String builderVersion; private String targetEnvironment; private final List processConfigurations = new ArrayList<>(); public ConfigurationArchive(byte[] content) throws IOException { tmpFile = File.createTempFile("configuration", ".zip"); try (ByteArrayInputStream is = new ByteArrayInputStream(content)) { Files.copy(is, tmpFile.toPath(), StandardCopyOption.REPLACE_EXISTING); } try (ZipFile zipFile = new ZipFile(tmpFile)) { readManifest(zipFile); loadProcessConfigurations(zipFile); loadProcessConfigurationsParameters(zipFile); } } private void loadProcessConfigurationsParameters(ZipFile zipFile) throws IOException { for (ProcessConfiguration processConf : processConfigurations) { ZipEntry parametersEntry = zipFile .getEntry(String.format("%s/%s/%s", processConf.getName(), processConf.getVersion(), PARAMETERS_FILENAME)); if (parametersEntry != null) { try (InputStream is = zipFile.getInputStream(parametersEntry)) { Properties parameters = new Properties(); parameters.load(is); processConf.setParameters(asMap(parameters.entrySet())); } } } } private Map asMap(Set> entrySet) { return entrySet.stream() .collect(toMap(entry -> (String) entry.getKey(), entry -> (String) entry.getValue(), (a, b) -> b)); } private void loadProcessConfigurations(ZipFile zipFile) { zipFile.stream().forEach(entry -> { String name = entry.getName(); String[] path = name.split("/"); if (path.length > 2) { String processName = path[0]; String processVersion = path[1]; ProcessConfiguration processConfiguration = new ProcessConfiguration(processName, processVersion); if (!processConfigurations.contains(processConfiguration)) { processConfigurations.add(processConfiguration); } } }); } private void readManifest(ZipFile zipFile) throws IOException { ZipEntry manifestEntry = zipFile.getEntry("MANIFEST"); if (manifestEntry == null) { throw new IOException("Invalid archive format (missing MANIFEST)."); } try (InputStream is = zipFile.getInputStream(manifestEntry)) { Properties manifest = new Properties(); manifest.load(is); builderVersion = manifest.getProperty("builder.version"); targetEnvironment = manifest.getProperty("target.environment"); } } public String getBuilderVersion() { return builderVersion; } public String getTargetEnvironment() { return targetEnvironment; } public List getProcessConfigurations() { return processConfigurations; } @Override public void close() throws Exception { if (tmpFile != null) { log.debug("deleting temp file for application configuration file read"); final boolean done = Files.deleteIfExists(tmpFile.toPath()); log.debug("deleting temp file {}", done ? "successful" : "unsuccessful!"); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/installation/InstallationServiceImpl.java ================================================ /** * 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.impl.installation; import static org.bonitasoft.engine.commons.ExceptionUtils.printLightWeightStacktrace; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.impl.resolver.BusinessArchiveArtifactsManager; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; import org.bonitasoft.engine.core.process.definition.exception.SProcessDisablementException; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; 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.event.SStartEventDefinition; import org.bonitasoft.engine.execution.event.EventsHandler; import org.bonitasoft.engine.parameter.ParameterService; import org.bonitasoft.engine.service.InstallationFailedException; import org.bonitasoft.engine.service.InstallationService; import org.springframework.stereotype.Service; @Slf4j @Service public class InstallationServiceImpl implements InstallationService { private final ProcessDefinitionService processDefinitionService; private final ParameterService parameterService; private final BusinessArchiveArtifactsManager businessArchiveArtifactsManager; private final EventsHandler eventsHandler; public InstallationServiceImpl(ProcessDefinitionService processDefinitionService, ParameterService parameterService, BusinessArchiveArtifactsManager dependencyResolver, EventsHandler eventsHandler) { this.processDefinitionService = processDefinitionService; this.parameterService = parameterService; this.businessArchiveArtifactsManager = dependencyResolver; this.eventsHandler = eventsHandler; } @Override public void install(byte[] binaries, byte[] configuration) throws InstallationFailedException { if (binaries != null) { throw new IllegalStateException("binaries archive is not yet implemented"); } if (configuration != null) { installConfiguration(configuration); } } private void installConfiguration(byte[] configuration) throws InstallationFailedException { try (ConfigurationArchive confArchive = new ConfigurationArchive(configuration)) { for (ProcessConfiguration processConfiguration : confArchive.getProcessConfigurations()) { String processName = processConfiguration.getName(); String processVersion = processConfiguration.getVersion(); try { long pDefId = processDefinitionService.getProcessDefinitionId(processName, processVersion); parameterService.merge(pDefId, processConfiguration.getParameters()); final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(pDefId); final List problems = businessArchiveArtifactsManager .getProcessResolutionProblems(processDefinition); final SProcessDefinitionDeployInfo pInfo = processDefinitionService .getProcessDeploymentInfo(pDefId); if (problems.isEmpty()) { businessArchiveArtifactsManager.changeResolutionStatus(pDefId, processDefinitionService, true); if (ActivationState.DISABLED.name().equals(pInfo.getActivationState())) { log.info("Configuration of process {}-{} is now complete. Enabling it.", processName, processVersion); enableProcess(pDefId, processName, processVersion); } } else { businessArchiveArtifactsManager.changeResolutionStatus(pDefId, processDefinitionService, false); if (ActivationState.ENABLED.name().equals(pInfo.getActivationState())) { disableProcess(pDefId, processName, processVersion); } } } catch (SProcessDefinitionNotFoundException e) { // Process configuration may be present in bconf file for non deployed processes filtered by a deploy.json file at deploy time (see la-deployer) // don't rethrow ex to avoid breaking ongoing configuration deployment. log.warn("Configuration parameter found in BCONF file for non existing process " + processName + "-" + processVersion + ". Skipping those parameters."); } } } catch (Exception e) { log.error("Failed to apply configuration.", e); throw new InstallationFailedException("Failed to apply configuration.", e); } } private void enableProcess(long processDefinitionId, String processName, String processVersion) { try { processDefinitionService.enableProcess(processDefinitionId, false); handleStartEvents(processDefinitionId); } catch (SBonitaException e) { // It's not mandatory to enable the process here, simply log the action log.warn("Failed to enable the process " + processName + " in version " + processVersion + " after deploying the configuration", printLightWeightStacktrace(e)); } } private void handleStartEvents(long processDefinitionId) throws SBonitaException { final SProcessDefinition sProcessDefinition = processDefinitionService .getProcessDefinition(processDefinitionId); final SFlowElementContainerDefinition processContainer = sProcessDefinition.getProcessContainer(); for (final SStartEventDefinition sStartEventDefinition : processContainer.getStartEvents()) { eventsHandler.handleCatchEvent(sProcessDefinition, sStartEventDefinition, null); } } private void disableProcess(long processDefinitionId, String processName, String processVersion) { try { processDefinitionService.disableProcess(processDefinitionId, false); } catch (SProcessDefinitionNotFoundException | SProcessDisablementException e) { // It's not mandatory to disable the process here, simply log the action log.warn("Failed to disable the process " + processName + " in version " + processVersion + " after deploying the configuration", printLightWeightStacktrace(e)); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/impl/installation/ProcessConfiguration.java ================================================ /** * 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.impl.installation; import java.util.Map; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; @RequiredArgsConstructor @EqualsAndHashCode(exclude = "parameters") public class ProcessConfiguration { @Getter private final String name; @Getter private final String version; @Getter @Setter private Map parameters; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/platform/PlatformInformationService.java ================================================ /** * 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; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/service/platform/PlatformInformationServiceImpl.java ================================================ /** * 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); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/ChangesServicesStateCallable.java ================================================ /** * 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.tenant; import java.io.Serializable; import java.util.concurrent.Callable; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.ServiceAccessorSingleton; /** * @author Emmanuel Duchastenier */ class ChangesServicesStateCallable implements Callable, Serializable { private final TenantServicesManager.ServiceAction action; public ChangesServicesStateCallable(TenantServicesManager.ServiceAction action) { this.action = action; } @Override public Void call() throws Exception { ServiceAccessor serviceAccessor = ServiceAccessorSingleton.getInstance(); TenantServicesManager tenantServicesManager = serviceAccessor.getTenantServicesManager(); TenantStateManager tenantStateManager = serviceAccessor.getTenantStateManager(); return tenantStateManager.executeManagementOperation( "Executing received " + action.name().toLowerCase() + " operation", () -> { switch (action) { case START: tenantServicesManager.start(); break; case STOP: tenantServicesManager.stop(); break; case PAUSE: tenantServicesManager.pause(); break; case RESUME: tenantServicesManager.resume(); break; } return null; }); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/SingleNodeTaskCoordinator.java ================================================ /** * Copyright (C) 2026 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.tenant; /** * Determines whether the current node is responsible for executing a given named task. *

* In single-node deployments, always returns {@code true}. * In cluster deployments, uses Hazelcast partition ownership to ensure * exactly one node is responsible for each task at any given time. *

* Each task name maps to a distinct Hazelcast partition, so different tasks * may be owned by different cluster nodes, distributing the load. * * @see SingleNodeTaskCoordinatorLocal */ public interface SingleNodeTaskCoordinator { /** Task name used by the {@link org.bonitasoft.engine.tenant.restart.RecoveryScheduler}. */ String TASK_RECOVERY = "RECOVER_NODE"; /** Task name used by the (Subscription-specific) {@link com.bonitasoft.engine.retention.DataRetentionScheduler}. */ String TASK_CLEANUP_OBSOLETE_DATA = "CLEANUP_OBSOLETE_DATA"; /** * Check whether the current node is responsible for executing the given task. * * @param taskName a stable identifier for the task (used as Hazelcast partition key in cluster mode) * @return {@code true} if this node should execute the task, {@code false} otherwise */ boolean isResponsibleForTask(String taskName); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/SingleNodeTaskCoordinatorLocal.java ================================================ /** * Copyright (C) 2026 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.tenant; import static org.bonitasoft.engine.Profiles.NOT_IN_CLUSTER; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; /** * Local (single-node) implementation of {@link SingleNodeTaskCoordinator}. * Always returns {@code true} since there is only one node to run any task. */ @Component @Profile(NOT_IN_CLUSTER) public class SingleNodeTaskCoordinatorLocal implements SingleNodeTaskCoordinator { @Override public boolean isResponsibleForTask(String taskName) { return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/TenantElementsRestartSupervisor.java ================================================ /** * 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.tenant; public interface TenantElementsRestartSupervisor { boolean shouldRestartElements(); boolean willRestartElements(); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/TenantElementsRestartSupervisorLocal.java ================================================ /** * 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.tenant; import org.bonitasoft.engine.commons.TenantLifecycleService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; @Component @ConditionalOnSingleCandidate(TenantElementsRestartSupervisor.class) public class TenantElementsRestartSupervisorLocal implements TenantLifecycleService, TenantElementsRestartSupervisor { private boolean areTenantsElementsAlreadyRestarted; @Override public void start() throws SBonitaException { } @Override public void stop() { areTenantsElementsAlreadyRestarted = false; } @Override public void pause() { areTenantsElementsAlreadyRestarted = false; } @Override public boolean shouldRestartElements() { return !areTenantsElementsAlreadyRestarted; } @Override public boolean willRestartElements() { if (!areTenantsElementsAlreadyRestarted) { areTenantsElementsAlreadyRestarted = true; return true; } else { return false; } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/TenantElementsRestarter.java ================================================ /** * 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.tenant; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** * Handles the restart of elements when the tenant is started (strategy is different in cluster) */ @Component @Slf4j public class TenantElementsRestarter { private final TenantRestarter tenantRestarter; private final TenantElementsRestartSupervisor tenantElementsRestartSupervisor; public TenantElementsRestarter(TenantRestarter tenantRestarter, TenantElementsRestartSupervisor tenantElementsRestartSupervisor) { this.tenantRestarter = tenantRestarter; this.tenantElementsRestartSupervisor = tenantElementsRestartSupervisor; } void prepareRestartOfElements() throws Exception { if (tenantElementsRestartSupervisor.shouldRestartElements()) { // Here get all elements that are not "finished" // * FlowNodes that have flag: stateExecuting to true: call execute on them (connectors were executing) // * Process instances with token count == 0 (either not started again or finishing) -> same thing connectors were executing // * transitions that are in state created: call execute on them // * flow node that are completed and not deleted : call execute to make it create transitions and so on // * all element that are in not stable state log.info("Preparing restart of elements"); tenantRestarter.executeBeforeServicesStart(); } else { log.info("Not preparing restart of elements, as another node already did it"); } } // Here get all elements that are not "finished" // * FlowNodes that have flag: stateExecuting to true: call execute on them (connectors were executing) // * Process instances with token count == 0 (either not started again or finishing) -> same thing connectors were executing // * transitions that are in state created: call execute on them // * flow node that are completed and not deleted : call execute to make it create transitions and so on // * all element that are in not stable state void restartElements() throws Exception { if (tenantElementsRestartSupervisor.willRestartElements()) { log.info("Restarting unfinished elements"); tenantRestarter.executeAfterServicesStart(); } else { log.info("Not restarting elements of tenant, as another node already did it"); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/TenantRestarter.java ================================================ /** * 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.tenant; import java.util.List; import org.bonitasoft.engine.api.impl.StarterThread; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.tenant.restart.TenantRestartHandler; import org.bonitasoft.engine.transaction.UserTransactionService; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta */ @Component public class TenantRestarter { private final UserTransactionService transactionService; private final List tenantRestartHandlers; private final PlatformService platformService; public TenantRestarter(UserTransactionService transactionService, PlatformService platformService, List tenantRestartHandlers) { this.transactionService = transactionService; this.platformService = platformService; this.tenantRestartHandlers = tenantRestartHandlers; } public void executeBeforeServicesStart() throws Exception { transactionService.executeInTransaction(() -> { for (TenantRestartHandler tenantRestartHandler : tenantRestartHandlers) { tenantRestartHandler.beforeServicesStart(); } return null; }); } public void executeAfterServicesStart() { new StarterThread(transactionService, platformService, tenantRestartHandlers).start(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/TenantServicesManager.java ================================================ /** * Copyright (C) 2020 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.tenant; import static java.text.MessageFormat.format; import static org.bonitasoft.engine.tenant.TenantServicesManager.ServiceAction.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.concurrent.Callable; import org.bonitasoft.engine.classloader.ClassLoaderIdentifier; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.commons.TenantLifecycleService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SLifecycleException; import org.bonitasoft.engine.service.RunnableWithException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException; import org.bonitasoft.engine.transaction.TransactionService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * Handles the lifecycle of tenant services: start, stop, (pause, resume -> will be removed) * Does not handle state of the tenant in database */ @Component public class TenantServicesManager { private static final Logger LOGGER = LoggerFactory.getLogger(TenantServicesManager.class); public enum ServiceAction { START, STOP, PAUSE, RESUME } public enum TenantServiceState { STOPPED, STARTING, STARTED, STOPPING, ABORTING_START } private final SessionAccessor sessionAccessor; private final SessionService sessionService; private final TransactionService transactionService; private final ClassLoaderService classLoaderService; private final List services; private final TenantElementsRestarter tenantElementsRestarter; private TenantServiceState tenantServiceState = TenantServiceState.STOPPED; public TenantServicesManager(SessionAccessor sessionAccessor, SessionService sessionService, TransactionService transactionService, ClassLoaderService classLoaderService, List services, TenantElementsRestarter tenantElementsRestarter) { this.sessionAccessor = sessionAccessor; this.sessionService = sessionService; this.transactionService = transactionService; this.classLoaderService = classLoaderService; this.services = services; this.tenantElementsRestarter = tenantElementsRestarter; } public boolean isStarted() { return tenantServiceState == TenantServiceState.STARTED; } private void updateState(TenantServiceState tenantServiceState) { LOGGER.debug("Services state updated to {}", tenantServiceState); this.tenantServiceState = tenantServiceState; } public void start() throws Exception { doStart(START); } public void resume() throws Exception { doStart(RESUME); } public void stop() throws Exception { // stop the services: doStop(STOP); } public void pause() throws Exception { doStop(PAUSE); } public void initServices() throws Exception { inSessionTransaction(() -> { for (TenantLifecycleService tenantService : services) { LOGGER.info("Initializing service {}", tenantService.getClass().getName()); tenantService.init(); } return null; }); } private void doStart(ServiceAction startAction) throws Exception { LOGGER.debug("Starting services"); if (tenantServiceState != TenantServiceState.STOPPED) { LOGGER.debug("Services cannot be started, they are {}", tenantServiceState); return; } updateState(TenantServiceState.STARTING); try { inSession(() -> { tenantElementsRestarter.prepareRestartOfElements(); transactionService.executeInTransaction((Callable) () -> { executeInClassloader(() -> startServices(startAction)); return null; }); }); } catch (Exception e) { abortStart(startAction, e); throw new SLifecycleException( "Unable to " + startAction + " a service. All services are kept STOPPED. Error: " + e.getMessage(), e); } updateState(TenantServiceState.STARTED); inSession(tenantElementsRestarter::restartElements); LOGGER.debug("Services are started."); } private void startServices(ServiceAction startAction) throws SLifecycleException { for (TenantLifecycleService tenantService : services) { try { LOGGER.info("{} service {}", startAction, tenantService.getClass().getName()); if (startAction == RESUME) { tenantService.resume(); } else { tenantService.start(); } } catch (Exception e) { LOGGER.error("Error while executing the {} of the service {}", startAction, tenantService.getClass().getName()); throw new SLifecycleException( format("Error while executing the {0} of the service {1}: {2}", startAction, transactionService.getClass().getName(), e.getMessage()), e); } } } private void abortStart(ServiceAction startAction, Exception e) { updateState(TenantServiceState.ABORTING_START); ServiceAction stopAction = startAction == START ? STOP : PAUSE; try { LOGGER.info("Stopping services after a failed {}...", startAction); doStop(stopAction); } catch (Exception exceptionOnStop) { LOGGER.warn("Unable to {} services to recover from exception when executing {} because {}: {}", stopAction, startAction, e.getClass().getName(), e.getMessage()); LOGGER.debug("Caused by: ", exceptionOnStop); } } private void doStop(ServiceAction stopAction) throws Exception { LOGGER.debug("Stopping services"); if (tenantServiceState != TenantServiceState.STARTED && tenantServiceState != TenantServiceState.ABORTING_START) { LOGGER.debug("Services cannot be stopped, they are {}", tenantServiceState); return; } updateState(TenantServiceState.STOPPING); List list = new ArrayList<>(services); Collections.reverse(list); Optional firstIssue = transactionService .executeInTransaction( () -> list.stream() .map(tenantService -> { LOGGER.info("{} service {}", stopAction, tenantService.getClass().getName()); try { if (stopAction == PAUSE) { tenantService.pause(); } else { tenantService.stop(); } } catch (final Exception e) { LOGGER.error("Error executing the {} of the service {} because: {} {}", stopAction, tenantService.getClass().getName(), e.getClass().getName(), e.getMessage()); LOGGER.debug("Cause", e); return e; } return null; }).filter(Objects::nonNull).findFirst()); updateState(TenantServiceState.STOPPED); LOGGER.debug("Services are stopped."); if (firstIssue.isPresent()) { throw new SLifecycleException("Unable to stop some services", firstIssue.get()); } } private void executeInClassloader(RunnableWithException runnable) throws Exception { final ClassLoader baseClassLoader = Thread.currentThread().getContextClassLoader(); try { // Set the right classloader only on start and resume because we destroy it on stop and pause anyway final ClassLoader serverClassLoader = classLoaderService.getClassLoader( ClassLoaderIdentifier.TENANT); Thread.currentThread().setContextClassLoader(serverClassLoader); runnable.run(); } finally { // reset previous class loader: Thread.currentThread().setContextClassLoader(baseClassLoader); } } protected Long createSession(final SessionService sessionService) throws SBonitaException { return sessionService.createSession(SessionService.SYSTEM).getId(); } private void inSession(RunnableWithException runnable) throws Exception { long currentSessionId; try { currentSessionId = sessionAccessor.getSessionId(); } catch (SessionIdNotSetException e) { runnable.run(); return; } try { final long sessionId = createSession(sessionService); sessionAccessor.deleteSessionId(); sessionAccessor.setSessionId(sessionId); runnable.run(); sessionService.deleteSession(sessionId); } finally { sessionAccessor.setSessionId(currentSessionId); } } public void inSessionTransaction(final Callable callable) throws Exception { inSession(() -> transactionService.executeInTransaction(callable)); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/TenantStateManager.java ================================================ /** * 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.tenant; import static org.bonitasoft.engine.tenant.TenantServicesManager.ServiceAction.PAUSE; import static org.bonitasoft.engine.tenant.TenantServicesManager.ServiceAction.RESUME; import java.io.IOException; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import org.bonitasoft.engine.exception.BonitaHomeConfigurationException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.platform.configuration.NodeConfiguration; import org.bonitasoft.engine.platform.model.SPlatform; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.service.BroadcastService; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.TaskResult; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.tenant.TenantServicesManager.ServiceAction; import org.bonitasoft.engine.transaction.UserTransactionService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class TenantStateManager { private static final Logger LOGGER = LoggerFactory.getLogger(TenantStateManager.class); private final UserTransactionService transactionService; private final PlatformService platformService; private final NodeConfiguration nodeConfiguration; private final SessionService sessionService; private final SchedulerService schedulerService; private final BroadcastService broadcastService; private final TenantServicesManager tenantServicesManager; public TenantStateManager(UserTransactionService transactionService, PlatformService platformService, NodeConfiguration nodeConfiguration, SessionService sessionService, SchedulerService schedulerService, BroadcastService broadcastService, TenantServicesManager tenantServicesManager) { this.transactionService = transactionService; this.platformService = platformService; this.nodeConfiguration = nodeConfiguration; this.sessionService = sessionService; this.schedulerService = schedulerService; this.broadcastService = broadcastService; this.tenantServicesManager = tenantServicesManager; } /** * Stop the platform: * - stop services * - delete session if its the only node * **Called outside of a transaction in a platform-level session** */ public synchronized void stop() throws Exception { if (nodeConfiguration.shouldClearSessions()) { sessionService.deleteSessions(); } tenantServicesManager.stop(); } /** * Start the platform: * - start services * - resume elements if its the only node * **Called outside of a transaction in a platform-level session** */ public synchronized void start() throws Exception { tenantServicesManager.initServices(); final SPlatform platform = getPlatformInTransaction(); if (platform.isMaintenanceEnabled()) { LOGGER.debug("Not starting platform. It is {}", platform.getPausedStatus()); return; } tenantServicesManager.start(); } protected ServiceAccessor getServiceAccessor() throws BonitaHomeNotSetException, IOException, BonitaHomeConfigurationException, ReflectiveOperationException { return ServiceAccessorFactory.getInstance().createServiceAccessor(); } private SPlatform getPlatformInTransaction() throws Exception { return transactionService.executeInTransaction(platformService::getPlatform); } /** * Pause the platform: * - pause services * - platform has the status PAUSED in database * - other nodes pause the services * **Called outside a transaction with a platform-level session** */ public synchronized void pause() throws Exception { LOGGER.info("Pausing platform"); SPlatform platform = getPlatformInTransaction(); if (platform.isMaintenanceEnabled()) { throw new UpdateException("Can't pause platform in state " + platform.getPausedStatus()); } pauseServicesInTransaction(); pauseSchedulerJobsInTransaction(); tenantServicesManager.pause(); pauseServicesOnOtherNodes(); LOGGER.info("Paused platform"); } /** * Resume the platform: * - resume services * - platform has the status ACTIVATED in database * - other nodes resume the services * **Called outside a transaction with a platform-level session** */ public synchronized void resume() throws Exception { LOGGER.info("Resuming platform"); SPlatform platform = getPlatformInTransaction(); if (!platform.isMaintenanceEnabled()) { throw new UpdateException("Can't resume platform in state " + platform.getPausedStatus()); } resumeServicesInTransaction(); try { tenantServicesManager.resume(); } catch (Exception e) { pauseServicesInTransaction(); throw e; } resumeServicesOnOtherNodes(); resumeSchedulerJobsInTransaction(); LOGGER.info("Resumed platform"); } private void resumeSchedulerJobsInTransaction() throws Exception { transactionService.executeInTransaction(() -> { schedulerService.resumeJobs(); return null; }); } private void pauseSchedulerJobsInTransaction() throws Exception { transactionService.executeInTransaction(() -> { schedulerService.pauseJobs(); return null; }); } private void pauseServicesInTransaction() throws Exception { transactionService.executeInTransaction(() -> { platformService.pauseServices(); return null; }); } private void resumeServicesInTransaction() throws Exception { transactionService.executeInTransaction(() -> { platformService.resumeServices(); return null; }); } private void pauseServicesOnOtherNodes() { executeOnOtherNodes(PAUSE); } private void resumeServicesOnOtherNodes() { executeOnOtherNodes(RESUME); } private void executeOnOtherNodes(ServiceAction action) { Map> execute; try { execute = broadcastService.executeOnOthersAndWait(new ChangesServicesStateCallable(action)); } catch (InterruptedException | ExecutionException | TimeoutException e) { throw new IllegalStateException("Unable to update services on other nodes", e); } for (Map.Entry> resultEntry : execute.entrySet()) { if (resultEntry.getValue().isError()) { throw new IllegalStateException(resultEntry.getValue().getThrowable()); } } } public synchronized T executeManagementOperation(String operationName, Callable operation) throws Exception { LOGGER.info("Executing synchronized maintenance operation {}", operationName); T operationReturn = operation.call(); LOGGER.info("Successful synchronized maintenance operation {}", operationName); return operationReturn; } public synchronized boolean isPaused() throws Exception { return getPlatformInTransaction().isMaintenanceEnabled(); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/ElementToRecover.java ================================================ /** * Copyright (C) 2020 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.tenant.restart; import lombok.Builder; import lombok.Data; @Data @Builder public class ElementToRecover { enum Type { PROCESS, FLOWNODE } private Type type; private Long id; } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/FlowNodesRecover.java ================================================ /** * 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.tenant.restart; import java.util.ArrayList; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.utils.VisibleForTesting; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; 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.SStateCategory; import org.bonitasoft.engine.execution.state.FlowNodeStateManager; import org.bonitasoft.engine.execution.work.BPMWorkFactory; import org.bonitasoft.engine.work.WorkService; import org.springframework.stereotype.Component; /** * Recover flow nodes *

* This class is called after the start of the engine or periodically and will recover elements given their ids. */ @Slf4j @Component public class FlowNodesRecover { private final WorkService workService; private final BPMWorkFactory workFactory; private final ActivityInstanceService activityInstanceService; private final FlowNodeStateManager flowNodeStateManager; public FlowNodesRecover(WorkService workService, ActivityInstanceService activityInstanceService, FlowNodeStateManager flowNodeStateManager, BPMWorkFactory workFactory) { this.workService = workService; this.workFactory = workFactory; this.activityInstanceService = activityInstanceService; this.flowNodeStateManager = flowNodeStateManager; } void execute(RecoveryMonitor recoveryMonitor, List flowNodeIds) throws SBonitaException { List unprocessed = new ArrayList<>(flowNodeIds); List flowNodeInstances = activityInstanceService.getFlowNodeInstancesByIds(flowNodeIds); for (SFlowNodeInstance flowNodeInstance : flowNodeInstances) { unprocessed.remove(flowNodeInstance.getId()); if (flowNodeInstance.isTerminal()) { recoveryMonitor.incrementFinishing(); log.debug("Restarting flow node (Notify finished...) with name = <" + flowNodeInstance.getName() + ">, and id = <" + flowNodeInstance.getId() + " in state = <" + flowNodeInstance.getStateName() + ">"); workService.registerWork(workFactory.createNotifyChildFinishedWorkDescriptor(flowNodeInstance)); } else { if (shouldBeRecovered(flowNodeInstance)) { recoveryMonitor.incrementExecuting(); log.debug("Recovering flow node (Execute ...) with name = <" + flowNodeInstance.getName() + ">, and id = <" + flowNodeInstance.getId() + "> in state = <" + flowNodeInstance.getStateName() + ">"); workService.registerWork(workFactory.createExecuteFlowNodeWorkDescriptor(flowNodeInstance)); } else { recoveryMonitor.incrementNotExecutable(); log.debug( "Flownode with name = <{}>, and id = <{}> in state = <{}> does not fulfill the recovered conditions.", flowNodeInstance.getName(), flowNodeInstance.getId(), flowNodeInstance.getStateName()); } } } recoveryMonitor.incrementNotFound(unprocessed.size()); } @VisibleForTesting boolean shouldBeRecovered(final SFlowNodeInstance sFlowNodeInstance) { //when state category is cancelling but the state is 'stable' (e.g. boundary event in waiting but that has been cancelled) if ((sFlowNodeInstance.getStateCategory().equals(SStateCategory.CANCELLING) || sFlowNodeInstance.getStateCategory().equals(SStateCategory.ABORTING)) && !sFlowNodeInstance.isTerminal() && sFlowNodeInstance.isStable()) { FlowNodeState state = flowNodeStateManager.getState(sFlowNodeInstance.getStateId()); //in this case we restart it only if the state is not in this cancelling of aborting category //this can happen when we abort a process with a call activity: //the call activity is put in aborting state and wait for its children to finish, in that case we do not call execute on it return !state.getStateCategory().equals(sFlowNodeInstance.getStateCategory()); } // Gateway is a special case : // Initial state is not stable but it should probably be stable because it waits for other flowNode to be completed. if (SFlowNodeType.GATEWAY.equals(sFlowNodeInstance.getType())) { return sFlowNodeInstance.isAborting() || sFlowNodeInstance.isCanceling() || ((SGatewayInstance) sFlowNodeInstance).isFinished(); } return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/MessagesRestartHandler.java ================================================ /** * 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.tenant.restart; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository; import org.bonitasoft.engine.execution.work.ExecuteMessageCoupleWork; import org.bonitasoft.engine.execution.work.RestartException; import org.bonitasoft.engine.message.MessagesHandlingService; import org.bonitasoft.engine.transaction.UserTransactionService; import org.springframework.stereotype.Component; /** * Resets all "In Progress" BPMN Message couples so that they can be triggered again on next cron. * Restart work {@link ExecuteMessageCoupleWork} * * @author Emmanuel Duchastenier */ @Slf4j @Component public class MessagesRestartHandler implements TenantRestartHandler { private EventInstanceRepository eventInstanceRepository; private UserTransactionService userTransactionService; private MessagesHandlingService messagesHandlingService; public MessagesRestartHandler( EventInstanceRepository eventInstanceRepository, UserTransactionService userTransactionService, MessagesHandlingService messagesHandlingService) { this.eventInstanceRepository = eventInstanceRepository; this.userTransactionService = userTransactionService; this.messagesHandlingService = messagesHandlingService; } @Override public void beforeServicesStart() throws RestartException { try { // Reset of all SMessageInstance: log.info( "Reinitializing message instances in non-stable state to make them reworked by MessagesHandlingService"); final int nbMessagesReset = eventInstanceRepository.resetProgressMessageInstances(); log.info(nbMessagesReset + " message instances found and reset."); // Reset of all SWaitingMessageEvent: log.info( "Reinitializing waiting message events in non-stable state to make them reworked by MessagesHandlingService"); final int nbWaitingEventsReset = eventInstanceRepository.resetInProgressWaitingEvents(); log.info(nbWaitingEventsReset + " waiting message events found and reset."); } catch (final SBonitaException e) { throw new RestartException( "Unable to reset MessageInstances / WaitingMessageEvents that were 'In Progress' when the node stopped", e); } } @Override public void afterServicesStart() { try { userTransactionService.executeInTransaction(() -> { messagesHandlingService.triggerMatchingOfMessages(); return null; }); } catch (Exception e) { log.error( "Unable to register work to handle message events on startup, work will be triggered on next message event update", e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/ProcessesRecover.java ================================================ /** * Copyright (C) 2020 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.tenant.restart; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.execution.ProcessExecutor; import org.bonitasoft.engine.execution.work.BPMWorkFactory; import org.bonitasoft.engine.work.WorkService; import org.springframework.stereotype.Component; /** * This class handles the continuation of unfinished process instances * passed as parameter in {@link #execute(RecoveryMonitor, List)} method. * The logic ensures that if an instance fails to continue its execution, * the error is logged and other instances are continued anyways. */ @Slf4j @Component public class ProcessesRecover { private final WorkService workService; private final ActivityInstanceService activityInstanceService; private final ProcessDefinitionService processDefinitionService; private final ProcessInstanceService processInstanceService; private final ProcessExecutor processExecutor; private final BPMWorkFactory workFactory; public ProcessesRecover(WorkService workService, ActivityInstanceService activityInstanceService, ProcessDefinitionService processDefinitionService, ProcessInstanceService processInstanceService, ProcessExecutor processExecutor, BPMWorkFactory workFactory) { this.workService = workService; this.activityInstanceService = activityInstanceService; this.processDefinitionService = processDefinitionService; this.processInstanceService = processInstanceService; this.processExecutor = processExecutor; this.workFactory = workFactory; } void execute(RecoveryMonitor recoveryMonitor, List ids) { for (Long processId : ids) { try { final SProcessInstance processInstance = processInstanceService.getProcessInstance(processId); final SProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); switch (ProcessInstanceState.getFromId(processInstance.getStateId())) { case ABORTED: case CANCELLED: case COMPLETED: recoveryMonitor.incrementFinishing(); handleCompletion(processInstance); break; case COMPLETING: recoveryMonitor.incrementFinishing(); processExecutor.registerConnectorsToExecute(processDefinition, processInstance, ConnectorEvent.ON_FINISH, null); break; case INITIALIZING: recoveryMonitor.incrementExecuting(); processExecutor.registerConnectorsToExecute(processDefinition, processInstance, ConnectorEvent.ON_ENTER, null); break; default: recoveryMonitor.incrementNotExecutable(); break; } } catch (final SProcessInstanceNotFoundException e) { recoveryMonitor.incrementNotFound(); log.debug("Unable to recover the process instance {}, it is not found (probably already completed).", processId); } catch (final Exception e) { recoveryMonitor.incrementInError(); log.warn( "Unable to recover the process instance {}, it will be retry in next recovery. Because : {} ", processId, e.getMessage()); log.debug("Cause", e); } } } private void handleCompletion(final SProcessInstance processInstance) throws SBonitaException { // Only Error events set interruptedByEvent on SProcessInstance: if (!processInstance.hasBeenInterruptedByEvent()) { final long callerId = processInstance.getCallerId(); // Should always be in a CallActivity: if (callerId > 0) { final SActivityInstance callActivityInstance = activityInstanceService .getActivityInstance(processInstance.getCallerId()); if (callActivityInstance.getStateId() != FlowNodeState.ID_ACTIVITY_FAILED) { workService.registerWork(workFactory.createExecuteFlowNodeWorkDescriptor(callActivityInstance)); log.info("Restarting notification of finished process '{}' with id {} in state {}", processInstance.getName(), processInstance.getId(), ProcessInstanceState.getFromId(processInstance.getStateId())); } } // No need to handle completion of process instance in state COMPLETED here, // as it can never happen, because when a process instance goes into COMPLETED state, it is archived // directly in the same transaction (in ArchiveProcessInstanceHandler) } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/RecoveryHandler.java ================================================ /** * 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.tenant.restart; import java.time.Duration; import java.util.List; import org.springframework.stereotype.Component; /** * The Recovery handler is responsible for recovering all candidate elements * at Engine startup. * It is called only once in a cluster startup (handled by TenantElementsRestartSupervisor) */ @Component public class RecoveryHandler implements TenantRestartHandler { private final RecoveryService recoveryService; private List allElementsToRecover; public RecoveryHandler(RecoveryService recoveryService) { this.recoveryService = recoveryService; } @Override public void beforeServicesStart() { allElementsToRecover = recoveryService.getAllElementsToRecover(Duration.ZERO); } @Override public void afterServicesStart() { recoveryService.recover(allElementsToRecover); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/RecoveryMonitor.java ================================================ /** * Copyright (C) 2020 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.tenant.restart; import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE; import java.time.Duration; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; /** * Responsible for monitoring the recovery mechanism. * It measures some recovery metrics and prints them in standard logger when the recovery triggers. */ @Slf4j @Component @Scope(SCOPE_PROTOTYPE) class RecoveryMonitor { private long finishing; private long executing; private long notExecutable; private long notFound; private long inError; private long startTime; private int numberOfElementsToProcess; public void startNow(int numberOfElementsToProcess) { if (startTime > 0) { throw new UnsupportedOperationException("Can't start the Recovery Monitor, it is already started"); } this.numberOfElementsToProcess = numberOfElementsToProcess; startTime = System.currentTimeMillis(); } public long getFinishing() { return finishing; } public long getExecuting() { return executing; } public long getNumberOfElementRecovered() { return executing + finishing; } public long getNotExecutable() { return notExecutable; } public long getNotFound() { return notFound; } public long getInError() { return inError; } public void incrementFinishing() { this.finishing++; } public void incrementExecuting() { this.executing++; } public void incrementNotExecutable() { this.notExecutable++; } public void incrementInError() { this.inError++; } public void incrementNotFound() { this.notFound++; } public void incrementNotFound(int add) { this.notFound += add; } public void printProgress() { //This will be called only when more than one "page" of element to restart are present log.info("Restarting elements...Handled " + (getFinishing() + getExecuting() + getNotExecutable() + getNotFound() + getInError()) + " of " + numberOfElementsToProcess + " elements candidates to be recovered in " + Duration.ofMillis(System.currentTimeMillis() - startTime)); } public void printSummary() { // only print a single status line for that long numberOfElementRecovered = getNumberOfElementRecovered(); if (numberOfElementRecovered == 0) { log.info("Recovery of elements executed. Nothing detected that needs recovery."); } else { log.info("Recovery of elements executed, {} elements recovered.", numberOfElementRecovered); } // details in debug log.debug("Handled {} elements candidates to be recovered in {}", (getFinishing() + getExecuting() + getNotExecutable() + getNotFound() + getInError()), Duration.ofMillis(System.currentTimeMillis() - startTime)); log.debug("Found {} elements recovered (Executing)", getExecuting()); log.debug("Found {} elements recovered (Finishing)", getFinishing()); log.debug("Found {} elements that were not executable (e.g. unmerged gateway)", getNotExecutable()); log.debug(getNotFound() + " elements were not found (might have been manually executed)"); log.debug("Found {} elements in error (see stacktrace for reason)", getInError()); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/RecoveryScheduler.java ================================================ /** * Copyright (C) 2020 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.tenant.restart; import static org.bonitasoft.engine.tenant.SingleNodeTaskCoordinator.TASK_RECOVERY; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.tenant.SingleNodeTaskCoordinator; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; /** * The RecoveryScheduler is responsible to trigger the recovery mechanism periodically. * The scheduling values can be changed using engine properties files. */ @Component @Slf4j public class RecoveryScheduler { private final SingleNodeTaskCoordinator singleNodeTaskCoordinator; private final RecoveryService recoveryService; RecoveryScheduler(SingleNodeTaskCoordinator singleNodeTaskCoordinator, RecoveryService recoveryService) { this.singleNodeTaskCoordinator = singleNodeTaskCoordinator; this.recoveryService = recoveryService; } @Scheduled(fixedDelayString = "${bonita.tenant.recover.delay_between_recovery:PT2H}", initialDelayString = "${bonita.tenant.recover.delay_between_recovery:PT2H}") public void triggerRecoveryOfAllElements() { try { if (singleNodeTaskCoordinator.isResponsibleForTask(TASK_RECOVERY)) { log.debug("Starting periodic recovery of elements..."); recoveryService.recoverAllElements(); log.debug("Completed periodic recovery of elements."); } else { log.debug("Periodic recovery of elements not executed, an other node is responsible for it."); } } catch (Exception e) { log.warn("Recovery of elements failed because of {} - {}, it will be re-executed soon", e.getClass().getName(), e.getMessage()); log.debug("Cause by ", e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/RecoveryService.java ================================================ /** * Copyright (C) 2020 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.tenant.restart; import static org.bonitasoft.engine.commons.CollectionUtil.split; import static org.bonitasoft.engine.tenant.restart.ElementToRecover.Type.FLOWNODE; import static org.bonitasoft.engine.tenant.restart.ElementToRecover.Type.PROCESS; import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import javax.annotation.PostConstruct; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.LongTaskTimer; import io.micrometer.core.instrument.MeterRegistry; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.utils.VisibleForTesting; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.transaction.UserTransactionService; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * Responsible to recover from incidents like database or network outage. * It scans the database (on-demand) and reschedules the elements to recover. * It will recover these elements using multiple transaction using a batch size configured by the property * `bonita.tenant.work.batch_restart_size` */ @Component @Slf4j public class RecoveryService { public static final String DURATION_OF_RECOVERY_TASK = "bonita.bpmengine.recovery.duration"; public static final String NUMBER_OF_RECOVERY = "bonita.bpmengine.recovery.execution"; public static final String NUMBER_OF_ELEMENTS_RECOVERED_LAST_RECOVERY = "bonita.bpmengine.recovery.recovered.last"; public static final String NUMBER_OF_ELEMENTS_RECOVERED_TOTAL = "bonita.bpmengine.recovery.recovered.total"; private final FlowNodeInstanceService flowNodeInstanceService; private final ProcessInstanceService processInstanceService; private final UserTransactionService userTransactionService; private final FlowNodesRecover flowNodesRecover; private final ProcessesRecover processesRecover; private final ObjectFactory recoveryMonitorProvider; private final MeterRegistry meterRegistry; private int readBatchSize; private int batchRestartSize; private Duration considerElementsOlderThan; private LongTaskTimer longTaskTimer; private Counter numberOfElementsRecoveredTotal; private Counter numberOfRecoverExecuted; private final AtomicLong numberOfElementsRecoveredDuringTheLastRecover = new AtomicLong(); public RecoveryService(FlowNodeInstanceService flowNodeInstanceService, ProcessInstanceService processInstanceService, UserTransactionService userTransactionService, FlowNodesRecover flowNodesRecover, ProcessesRecover processesRecover, ObjectFactory recoveryMonitorProvider, MeterRegistry meterRegistry) { this.flowNodeInstanceService = flowNodeInstanceService; this.processInstanceService = processInstanceService; this.userTransactionService = userTransactionService; this.flowNodesRecover = flowNodesRecover; this.processesRecover = processesRecover; this.recoveryMonitorProvider = recoveryMonitorProvider; this.meterRegistry = meterRegistry; } @PostConstruct protected void initMetrics() { this.longTaskTimer = LongTaskTimer .builder(DURATION_OF_RECOVERY_TASK) .description("duration of recovery task") .register(meterRegistry); Gauge.builder(NUMBER_OF_ELEMENTS_RECOVERED_LAST_RECOVERY, numberOfElementsRecoveredDuringTheLastRecover, AtomicLong::doubleValue) .description("number of elements recovered").baseUnit("elements") .register(meterRegistry); numberOfElementsRecoveredTotal = Counter.builder(NUMBER_OF_ELEMENTS_RECOVERED_TOTAL) .baseUnit("elements").description("Total number of elements recovered") .register(meterRegistry); numberOfRecoverExecuted = Counter.builder(NUMBER_OF_RECOVERY) .baseUnit("executions").description("Number of recovery executed") .register(meterRegistry); } @Value("${bonita.tenant.recover.read_batch_size:5000}") public void setReadBatchSize(int readBatchSize) { this.readBatchSize = readBatchSize; } @Value("${bonita.tenant.recover.consider_elements_older_than:PT1H}") public void setConsiderElementsOlderThan(String considerElementsOlderThan) { setConsiderElementsOlderThan(Duration.parse(considerElementsOlderThan)); } @Value("${bonita.tenant.work.batch_restart_size:1000}") public void setBatchRestartSize(int batchRestartSize) { this.batchRestartSize = batchRestartSize; } @VisibleForTesting void setConsiderElementsOlderThan(Duration considerElementsOlderThan) { this.considerElementsOlderThan = considerElementsOlderThan; } /** * Retrieve elements ( ProcessInstance and Flow Nodes ) that needs to be recovered and that are older than the given * duration. * * @param considerElementsOlderThan consider elements older than that duration * @return elements to be recovered */ public List getAllElementsToRecover(Duration considerElementsOlderThan) { List elementsToRecover = new ArrayList<>(); try { elementsToRecover.addAll(getAllElementsToRecover(PROCESS, (q) -> processInstanceService.getProcessInstanceIdsToRecover(considerElementsOlderThan, q))); elementsToRecover.addAll(getAllElementsToRecover(FLOWNODE, (q) -> flowNodeInstanceService.getFlowNodeInstanceIdsToRecover(considerElementsOlderThan, q))); elementsToRecover.addAll(getAllElementsToRecover(FLOWNODE, (q) -> flowNodeInstanceService.getGatewayInstanceIdsToRecover(considerElementsOlderThan, q))); return elementsToRecover; } catch (SBonitaException e) { throw new RuntimeException(e); } } /** * Trigger works to execute elements ( ProcessInstance and Flow Nodes ) that needs to be recovered * * @param elementsToRecover elements needs to be recovered */ public void recover(List elementsToRecover) { RecoveryMonitor recoveryMonitor = recoveryMonitorProvider.getObject(); recoveryMonitor.startNow(elementsToRecover.size()); executeInBatch(recoveryMonitor, elementsToRecover.stream() .filter(e1 -> e1.getType() == FLOWNODE) .collect(Collectors.toList()), ids -> flowNodesRecover.execute(recoveryMonitor, ids)); executeInBatch(recoveryMonitor, elementsToRecover.stream() .filter(e -> e.getType() == PROCESS) .collect(Collectors.toList()), ids -> processesRecover.execute(recoveryMonitor, ids)); recoveryMonitor.printSummary(); long numberOfElementRecovered = recoveryMonitor.getNumberOfElementRecovered(); numberOfElementsRecoveredTotal.increment(numberOfElementRecovered); numberOfElementsRecoveredDuringTheLastRecover.set(numberOfElementRecovered); numberOfRecoverExecuted.increment(); } protected void executeInBatch(RecoveryMonitor recoveryMonitor, List elements, BatchExecution execution) { for (List batchElementsIds : split(elements, batchRestartSize)) { try { userTransactionService.executeInTransaction(() -> { execution.execute( batchElementsIds.stream().map(ElementToRecover::getId).collect(Collectors.toList())); return null; }); } catch (Exception e) { log.warn( "Error processing batch of elements to recover, they will be recovered next time: {}, Cause: {}: {}", batchElementsIds, e.getClass().getName(), e.getMessage()); log.debug("Cause", e); } if (batchElementsIds.size() == batchRestartSize) { // only print progress when there is more than one page recoveryMonitor.printProgress(); } } } /** * Recover all elements considered as "stuck". * Only recover elements older than a duration configured with {@link #setConsiderElementsOlderThan(String)}. */ public void recoverAllElements() { longTaskTimer.record(() -> { try { List allElementsToRecover = userTransactionService.executeInTransaction( () -> RecoveryService.this.getAllElementsToRecover(considerElementsOlderThan)); log.debug("Found {} that can potentially be recovered", allElementsToRecover.size()); recover(allElementsToRecover); } catch (Exception e) { throw new RuntimeException(e); } }); } private List getAllElementsToRecover(ElementToRecover.Type type, IdsRetriever idsRetriever) throws SBonitaException { // using a too low page size (100) causes too many access to the database and causes timeout exception if there are lot of elements. // As we retrieve only the id we can use a greater page size QueryOptions queryOptions = new QueryOptions(0, readBatchSize); final List ids = new ArrayList<>(); List elementsIds; log.debug("Start detecting {} to recover...", type); do { elementsIds = idsRetriever.getIds(queryOptions); queryOptions = QueryOptions.getNextPage(queryOptions); ids.addAll(elementsIds); } while (elementsIds.size() == queryOptions.getNumberOfResults()); log.debug("Found {} {} to recover", elementsIds.size(), type); return ids .stream().map(id -> ElementToRecover.builder().id(id).type(type).build()) .collect(Collectors.toList()); } private interface BatchExecution { void execute(List ids) throws Exception; } private interface IdsRetriever { List getIds(QueryOptions queryOptions) throws SBonitaException; } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/tenant/restart/TenantRestartHandler.java ================================================ /** * 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.tenant.restart; import org.bonitasoft.engine.api.PlatformAPI; import org.bonitasoft.engine.execution.work.RestartException; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface TenantRestartHandler { /** * Called in a transaction during {@link PlatformAPI#startNode()}. */ void beforeServicesStart() throws RestartException; /** * Called outside a transaction after {@link PlatformAPI#startNode()} in a separate thread from the api call. */ void afterServicesStart(); } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/userfilter/UserFilterServiceDecorator.java ================================================ /** * 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.userfilter; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.api.APIAccessor; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.filter.FilterResult; import org.bonitasoft.engine.core.filter.UserFilterService; import org.bonitasoft.engine.core.filter.exception.SUserFilterExecutionException; import org.bonitasoft.engine.core.filter.exception.SUserFilterLoadingException; import org.bonitasoft.engine.core.process.definition.model.SUserFilterDefinition; import org.bonitasoft.engine.expression.EngineConstantExpressionBuilder; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; /** * This {@link UserFilterService} implementation injects, in method * {@link #executeFilter(long, SUserFilterDefinition, Map, ClassLoader)} a new expression to * access the {@link APIAccessor} for User filters. * This new expression is referenced under the name 'apiAccessor'. * * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public class UserFilterServiceDecorator implements UserFilterService { private final UserFilterService userFilterService; /** * @param userFilterService * the UserFilterService class that this class is decorating. */ public UserFilterServiceDecorator(final UserFilterService userFilterService) { super(); this.userFilterService = userFilterService; } /** * {@inheritDoc}. This implementation injects a new expression to access the {@link APIAccessor} for User filters. * This new expression is referenced under the name 'apiAccessor'. */ @Override public FilterResult executeFilter(final long processDefinitionId, final SUserFilterDefinition sUserFilterDefinition, final Map inputs, final ClassLoader classLoader, final SExpressionContext expressionContext, final String actorName) throws SUserFilterExecutionException { SExpression apiAccessorExpression; SExpression engineExecutionContext; apiAccessorExpression = EngineConstantExpressionBuilder.getConnectorAPIAccessorExpression(); engineExecutionContext = EngineConstantExpressionBuilder.getEngineExecutionContext(); final Map enrichedInputs = new HashMap(inputs); enrichedInputs.put("connectorApiAccessor", apiAccessorExpression); enrichedInputs.put("engineExecutionContext", engineExecutionContext); return userFilterService.executeFilter(processDefinitionId, sUserFilterDefinition, enrichedInputs, classLoader, expressionContext, actorName); } @Override public boolean loadUserFilters(final long processDefinitionId) throws SUserFilterLoadingException { return userFilterService.loadUserFilters(processDefinitionId); } @Override public void removeUserFilters(long processDefinitionId) throws SBonitaReadException, SRecorderException { userFilterService.removeUserFilters(processDefinitionId); } } ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/resources/actorMapping.xsd ================================================ Actor Mapping Schema 1.0 for Bonita Open Solution. Copyright (C) 2011 BonitaSoft S.A. ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-community.xml ================================================ ${org.quartz.scheduler.skipUpdateCheck:true} org.quartz.simpl.SimpleThreadPool ${bonita.platform.scheduler.quartz.threadpool.size} org.bonitasoft.engine.scheduler.impl.BonitaJobStoreCMT ${${db.vendor}.quartz.connection.jobstoredriver} managedDS true notManagedDS org.bonitasoft.engine.platform.configuration.datasource.QuartzConnectionProvider true org.bonitasoft.engine.platform.configuration.datasource.QuartzConnectionProvider false true true ${userTransaction} org.quartz.plugins.management.ShutdownHookPlugin false ${org.quartz.jobStore.txIsolationLevelReadCommitted} ${org.quartz.jobStore.misfireThreshold} ${org.quartz.jobStore.maxMisfiresToHandleAtATime} ${org.quartz.jobStore.acquireTriggersWithinLock} ${org.quartz.scheduler.batchTriggerAcquisitionMaxCount} ${org.quartz.scheduler.batchTriggerAcquisitionFireAheadTimeWindow} org.bonitasoft.engine.core.process.instance.model.SActivityInstance org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance org.bonitasoft.engine.core.process.instance.model.SGatewayInstance org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance org.bonitasoft.engine.core.process.instance.model.SConnectorInstance org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance org.bonitasoft.engine.core.process.instance.model.event.trigger.impl.SThrowMessageEventTriggerInstanceImpl org.bonitasoft.engine.core.process.instance.model.event.trigger.impl.SThrowSignalEventTriggerInstanceImpl org.bonitasoft.engine.core.process.instance.model.event.trigger.impl.SThrowErrorEventTriggerInstanceImpl org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent org.bonitasoft.engine.data.instance.model.SDataInstance org.bonitasoft.engine.data.instance.model.SBooleanDataInstance org.bonitasoft.engine.data.instance.model.SIntegerDataInstance org.bonitasoft.engine.data.instance.model.SDoubleDataInstance org.bonitasoft.engine.data.instance.model.SFloatDataInstance org.bonitasoft.engine.data.instance.model.SShortTextDataInstance org.bonitasoft.engine.data.instance.model.SLongDataInstance org.bonitasoft.engine.data.instance.model.SDateDataInstance org.bonitasoft.engine.data.instance.model.SLongTextDataInstance org.bonitasoft.engine.data.instance.model.SXMLDataInstance org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance org.bonitasoft.engine.data.instance.model.SBlobDataInstance org.bonitasoft.engine.core.process.comment.model.SHumanComment org.bonitasoft.engine.core.process.comment.model.SSystemComment org.bonitasoft.engine.core.document.model.SDocument org.bonitasoft.engine.core.document.model.SLightDocument org.bonitasoft.engine.page.SPageWithContent org.bonitasoft.engine.page.SPage org.bonitasoft.engine.parameter.SParameter org.bonitasoft.engine.resources.SBARResource org.bonitasoft.engine.resources.STenantResource org.bonitasoft.engine.business.application.model.SApplication org.bonitasoft.engine.business.application.model.SApplicationWithIcon org.bonitasoft.engine.core.contract.data.STaskContractData org.bonitasoft.engine.core.contract.data.SProcessContractData org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessMultiRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessSimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAFlowNodeSimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SAStartEventInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SAEndEventInstance org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance org.bonitasoft.engine.data.instance.model.archive.SADataInstance org.bonitasoft.engine.data.instance.model.archive.SABooleanDataInstance org.bonitasoft.engine.data.instance.model.archive.SAIntegerDataInstance org.bonitasoft.engine.data.instance.model.archive.SADoubleDataInstance org.bonitasoft.engine.data.instance.model.archive.SAFloatDataInstance org.bonitasoft.engine.data.instance.model.archive.SAShortTextDataInstance org.bonitasoft.engine.data.instance.model.archive.SALongDataInstance org.bonitasoft.engine.data.instance.model.archive.SADateDataInstance org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance org.bonitasoft.engine.data.instance.model.archive.SAXMLObjectDataInstance org.bonitasoft.engine.data.instance.model.archive.SABlobDataInstance org.bonitasoft.engine.data.instance.model.archive.SAXMLDataInstance org.bonitasoft.engine.core.contract.data.SATaskContractData org.bonitasoft.engine.core.contract.data.SAProcessContractData org.bonitasoft.engine.temporary.content.STemporaryContent org.bonitasoft.engine.core.process.instance.model.SBPMFailure org.bonitasoft.engine.core.process.instance.model.SABPMFailure org/bonitasoft/engine/actor/mapping/model/impl/hibernate/actor.queries.hbm.xml org/bonitasoft/engine/resources/hibernate/resources.queries.hbm.xml org/bonitasoft/engine/temporary/content/hibernate/temporary.content.queries.hbm.xml org/bonitasoft/engine/supervisor/mapping/model/impl/hibernate/supervisor.queries.hbm.xml org/bonitasoft/engine/scheduler/impl/hibernate/schedulerimpl.queries.hbm.xml org/bonitasoft/engine/profile/model/impl/hibernate/profile.queries.hbm.xml org/bonitasoft/engine/core/process/instance/model/impl/hibernate/process.instance.queries.hbm.xml org/bonitasoft/engine/core/process/instance/model/impl/hibernate/archived.process.instance.queries.hbm.xml org/bonitasoft/engine/core/process/definition/model/impl/hibernate/process.definition.queries.hbm.xml org/bonitasoft/engine/core/process/comment/model/impl/hibernate/comment.queries.hbm.xml org/bonitasoft/engine/core/process/comment/model/impl/hibernate/archive.comment.queries.hbm.xml org/bonitasoft/engine/platform/model/impl/hibernate/platform.queries.hbm.xml org/bonitasoft/engine/platform/command/model/impl/hibernate/platformCommand.queries.hbm.xml org/bonitasoft/engine/page/impl/hibernate/page.queries.hbm.xml org/bonitasoft/engine/queriablelogger/model/impl/hibernate/queriablelogger.queries.hbm.xml org/bonitasoft/engine/identity/model/impl/hibernate/identity.queries.hbm.xml org/bonitasoft/engine/core/document/model/impl/hibernate/document.queries.hbm.xml org/bonitasoft/engine/core/document/model/impl/hibernate/archive.document.queries.hbm.xml org/bonitasoft/engine/dependency/model/impl/hibernate/platform-dependency.queries.hbm.xml org/bonitasoft/engine/dependency/model/impl/hibernate/dependency.queries.hbm.xml org/bonitasoft/engine/data/instance/model/impl/hibernate/data.instance.queries.hbm.xml org/bonitasoft/engine/data/instance/model/impl/hibernate/archived.data.instance.queries.hbm.xml org/bonitasoft/engine/command/model/impl/hibernate/command.queries.hbm.xml org/bonitasoft/engine/core/category/model/impl/hibernate/category.queries.hbm.xml org/bonitasoft/engine/business/application/impl/hibernate/application.queries.hbm.xml org/bonitasoft/engine/core/form/impl/form-mapping.queries.hbm.xml org/bonitasoft/engine/core/contract/data/model/impl/hibernate/contract.queries.hbm.xml org/bonitasoft/engine/parameter/parameter.queries.hbm.xml org/bonitasoft/engine/core/failure/model/impl/hibernate/failure.queries.hbm.xml org/bonitasoft/engine/business/data/model/impl/hibernate/data-retention-bdm-tracking.queries.hbm.xml org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDesignContent org.bonitasoft.engine.identity.model.SContactInfo org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition org.bonitasoft.engine.identity.model.SCustomUserInfoValue org.bonitasoft.engine.identity.model.SGroup org.bonitasoft.engine.identity.model.SHavingIcon org.bonitasoft.engine.identity.SIcon org.bonitasoft.engine.identity.model.SRole org.bonitasoft.engine.identity.model.SUser org.bonitasoft.engine.identity.model.SUserLogin org.bonitasoft.engine.identity.model.SUserMembership org.bonitasoft.engine.actor.mapping.model.SActor org.bonitasoft.engine.actor.mapping.model.SActorMember org.bonitasoft.engine.business.application.model.SApplicationWithIcon org.bonitasoft.engine.business.application.model.SApplication org.bonitasoft.engine.business.application.model.SApplicationPage org.bonitasoft.engine.business.application.model.SApplicationMenu org.bonitasoft.engine.platform.model.SPlatform org.bonitasoft.engine.business.data.model.SDataRetentionConfig org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking org.bonitasoft.engine.core.delegation.model.SDelegationRule org.bonitasoft.engine.core.delegation.model.SDelegationRuleProcess org.bonitasoft.engine.platform.command.model.SPlatformCommand org.bonitasoft.engine.core.category.model.SCategory org.bonitasoft.engine.core.category.model.SProcessCategoryMapping org.bonitasoft.engine.scheduler.model.SJobDescriptor org.bonitasoft.engine.scheduler.model.SJobParameter org.bonitasoft.engine.scheduler.model.SJobLog org.bonitasoft.engine.command.model.SCommand org.bonitasoft.engine.core.process.comment.model.SComment org.bonitasoft.engine.core.process.comment.model.SHumanComment org.bonitasoft.engine.core.process.comment.model.SSystemComment org.bonitasoft.engine.core.process.comment.model.archive.SAComment org.bonitasoft.engine.dependency.model.SDependency org.bonitasoft.engine.dependency.model.SDependencyMapping org.bonitasoft.engine.dependency.model.SPlatformDependency org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping org.bonitasoft.engine.core.contract.data.SContractData org.bonitasoft.engine.core.contract.data.SProcessContractData org.bonitasoft.engine.core.contract.data.STaskContractData org.bonitasoft.engine.core.contract.data.SAContractData org.bonitasoft.engine.core.contract.data.SAProcessContractData org.bonitasoft.engine.core.contract.data.SATaskContractData org.bonitasoft.engine.core.document.model.SLightDocument org.bonitasoft.engine.core.document.model.SDocument org.bonitasoft.engine.core.document.model.SDocumentMapping org.bonitasoft.engine.core.document.model.SMappedDocument org.bonitasoft.engine.core.document.model.archive.SADocumentMapping org.bonitasoft.engine.core.document.model.archive.SAMappedDocument org.bonitasoft.engine.data.instance.model.archive.SABlobDataInstance org.bonitasoft.engine.data.instance.model.archive.SABooleanDataInstance org.bonitasoft.engine.data.instance.model.archive.SADataInstance org.bonitasoft.engine.data.instance.model.archive.SADateDataInstance org.bonitasoft.engine.data.instance.model.archive.SADoubleDataInstance org.bonitasoft.engine.data.instance.model.archive.SAFloatDataInstance org.bonitasoft.engine.data.instance.model.archive.SAIntegerDataInstance org.bonitasoft.engine.data.instance.model.archive.SALongDataInstance org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance org.bonitasoft.engine.data.instance.model.archive.SAShortTextDataInstance org.bonitasoft.engine.data.instance.model.archive.SAXMLDataInstance org.bonitasoft.engine.data.instance.model.archive.SAXMLObjectDataInstance org.bonitasoft.engine.data.instance.model.SBlobDataInstance org.bonitasoft.engine.data.instance.model.SBooleanDataInstance org.bonitasoft.engine.data.instance.model.SDataInstance org.bonitasoft.engine.data.instance.model.SDateDataInstance org.bonitasoft.engine.data.instance.model.SDoubleDataInstance org.bonitasoft.engine.data.instance.model.SFloatDataInstance org.bonitasoft.engine.data.instance.model.SIntegerDataInstance org.bonitasoft.engine.data.instance.model.SLongDataInstance org.bonitasoft.engine.data.instance.model.SLongTextDataInstance org.bonitasoft.engine.data.instance.model.SShortTextDataInstance org.bonitasoft.engine.data.instance.model.SXMLDataInstance org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance org.bonitasoft.engine.page.SPage org.bonitasoft.engine.page.SPageWithContent org.bonitasoft.engine.page.SPageMapping org.bonitasoft.engine.profile.model.SProfile org.bonitasoft.engine.profile.model.SProfileMember org.bonitasoft.engine.parameter.SParameter org.bonitasoft.engine.resources.SBARResourceLight org.bonitasoft.engine.resources.SBARResource org.bonitasoft.engine.resources.STenantResource org.bonitasoft.engine.resources.STenantResourceLight org.bonitasoft.engine.temporary.content.STemporaryContent org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor org.bonitasoft.engine.core.form.SFormMapping org.bonitasoft.engine.core.process.instance.model.SProcessInstance org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance org.bonitasoft.engine.core.process.instance.model.SActivityInstance org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance org.bonitasoft.engine.core.process.instance.model.SGatewayInstance org.bonitasoft.engine.core.process.instance.model.SConnectorInstance org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo org.bonitasoft.engine.core.process.instance.model.event.SEventInstance org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SAEventInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SACatchEventInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SAStartEventInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SAIntermediateCatchEventInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SABoundaryEventInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SAThrowEventInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SAIntermediateThrowEventInstance org.bonitasoft.engine.core.process.instance.model.archive.event.SAEndEventInstance org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.archive.business.data.SASimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessSimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAFlowNodeSimpleRefBusinessDataInstance org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessMultiRefBusinessDataInstance org.bonitasoft.engine.queriablelogger.model.SQueriableLog org.bonitasoft.engine.core.process.instance.model.SBPMFailure org.bonitasoft.engine.core.process.instance.model.SABPMFailure org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple executeBDMQuery Execute a named query in the BDM. Use parameter keys : "queryName" the name of the query in the bdm, "returnType" the query expected return type, "returnsList" if result is a List or a single value, "queryParameters" a Map to value query parameters org.bonitasoft.engine.command.ExecuteBDMQueryCommand searchSCommentSupervisedBy Returns the list of comments for processes supervised by a user. One mandatory parameter: supervisorId: ID of the user supervisor of the processes on which comments are. org.bonitasoft.engine.external.comment.SearchCommentsSupervisedBy searchWaitingEventsCommand Search waiting events. Parameter keys: searchOptions. org.bonitasoft.engine.command.system.SearchWaitingEventsCommand advancedStartProcessCommand Advanced start process. org.bonitasoft.engine.command.AdvancedStartProcessCommand getBusinessDataById Get the business data via its identifier and class name, and returns its Json representation. org.bonitasoft.engine.command.GetBusinessDataByIdCommand getBusinessDataByIds Get the business data via its identifiers and class name, and returns its Json representation. org.bonitasoft.engine.command.GetBusinessDataByIdsCommand getBusinessDataByQueryCommand Execute a named query in the BDM, and returns its Json representation. Use parameter keys : "queryName" the name of the query in the bdm, "returnType" the query expected return type, "returnsList" if result is a List or a single value, "queryParameters" a Map to value query parameters org.bonitasoft.engine.command.GetBusinessDataByQueryCommand multipleStartPointsProcessCommand Advanced start process using multiple start points. org.bonitasoft.engine.command.MultipleStartPointsProcessCommand org.bonitasoft.engine.commons.exceptions.SRetryableException org.hibernate.StaleStateException org.hibernate.exception.LockAcquisitionException javax.persistence.LockTimeoutException javax.persistence.PessimisticLockException javax.persistence.OptimisticLockException org.hibernate.dialect.lock.LockingStrategyException java.sql.SQLRecoverableException java.sql.SQLTransientException org.springframework.dao.TransientDataAccessException org.springframework.dao.RecoverableDataAccessException org.hibernate.exception.JDBCConnectionException org.bonitasoft.engine.connector.exception.SConnectorException EXECUTE_CONNECTOR_INCLUDING_POOL_SUBMIT EXECUTE_CONNECTOR_CALLABLE EXECUTE_CONNECTOR_OUTPUT_OPERATIONS EXECUTE_CONNECTOR_INPUT_EXPRESSIONS EXECUTE_CONNECTOR_DISCONNECT EXECUTE_CONNECTOR_WORK EVALUATE_EXPRESSION_INCLUDING_CONTEXT EVALUATE_EXPRESSION EVALUATE_EXPRESSIONS yyyy-MM-dd HH:mm:ss yyyy-MM-dd HH:mm:ss yyyy-MM-dd'T'HH:mm:ss yyyy-MM-dd'T'HH:mm:ss.SSS ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-platform-community.properties ================================================ #Bonita platform core configuration # Platform administrator platformAdminUsername=platformAdmin platformAdminPassword=platform # this datasource name is used by the engine to get connected to the database database.journal.datasource.name=${sysprop.bonita.database.journal.datasource.name:java:comp/env/bonitaDS} database.sequence.manager.datasource.name=${sysprop.bonita.database.sequence.manager.datasource.name:java:comp/env/bonitaSequenceManagerDS} # By default, get DB vendor type from JVM System Property. If not set, fallback on value after semi-colon (e.g. h2) db.vendor=${sysprop.bonita.db.vendor:h2} # Hibernate specific configurations hibernate.journal.show_sql=false hibernate.journal.format_sql=false hibernate.journal.use_sql_comments=false hibernate.transaction.jta_platform=${sysprop.bonita.hibernate.transaction.jta_platform:org.bonitasoft.engine.persistence.Narayana5HibernateJtaPlatform} # Transaction Service properties transaction.manager=${sysprop.bonita.transaction.manager:java:comp/env/TransactionManager} userTransaction=${sysprop.bonita.userTransaction:java:comp/UserTransaction} # Synchro service # Initial capacity of the waiters map bonita.platform.synchro.initialcapacity=50 # Scheduler # Number of threads in Quartz scheduler Thread Pool bonita.platform.scheduler.quartz.threadpool.size=5 # Lock Service # Number of seconds to wait for a lock bonita.platform.lock.memory.timeout=60 # Default platform cache: used if no specific cache is defined bonita.platform.cache.default.maxElementsInMemory=1000 bonita.platform.cache.default.eternal=true bonita.platform.cache.default.evictionPolicy=LRU bonita.platform.cache.default.timeToLiveSeconds=3600 bonita.platform.cache.default.readIntensive=false bonita.platform.cache.default.offHeapSizeMB=0 # Synchro service cache configuration bonita.platform.cache.synchro.maxElementsInMemory=10000 bonita.platform.cache.synchro.eternal=false bonita.platform.cache.synchro.evictionPolicy=LRU bonita.platform.cache.synchro.timeToLiveSeconds=120 bonita.platform.cache.synchro.readIntensive=false bonita.platform.cache.synchro.offHeapSizeMB=0 # Configuration files cache configuration bonita.platform.cache.configfiles.maxElementsInMemory=10000 bonita.platform.cache.configfiles.eternal=true bonita.platform.cache.configfiles.evictionPolicy=LRU bonita.platform.cache.configfiles.timeToLiveSeconds=3600 bonita.platform.cache.configfiles.readIntensive=true bonita.platform.cache.configfiles.offHeapSizeMB=0 # Sequence manager configuration bonita.platform.sequence.retries=9 bonita.platform.sequence.delay=10 bonita.platform.sequence.delayFactor=3 # if the sequence range size is not overridden, this value will be taken bonita.platform.sequence.defaultRangeSize=100 # you can override the range size of any sequenceId following the pattern bonita.platform.sequence.= # Most used objects: range size depends on process design bonita.platform.sequence.70=2000 # Job description bonita.platform.sequence.30=10000 # queriable log bonita.platform.sequence.10010=1000 # ProcessInstance bonita.platform.sequence.10011=20000 # ActivityInstance |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: bpm/bonita-core/bonita-process-engine/src/test/resources/testThatShouldFail.xml ================================================ william.jobs RD ================================================ FILE: bpm/bonita-core/bonita-process-instance/build.gradle ================================================ dependencies { api platform(libs.bonitaArtifactsModelBom) api project(':bpm:bonita-common') api project(':bpm:bonita-core:bonita-process-definition') api project(':services:bonita-identity') api project(':services:bonita-persistence') api project(':services:bonita-commons') api project(':services:bonita-log') api project(':services:bonita-events') api project(':services:bonita-archive') api project(':services:bonita-data-instance') api project(':services:bonita-classloader') api project(':bpm:bonita-core:bonita-process-comment') api project(':services:bonita-builder') api project(':bpm:bonita-core:bonita-home-server') api project(':services:bonita-session') api project(':services:bonita-connector-executor') api project(':services:bonita-resources') api project(':services:bonita-time-tracker') api project(':services:bonita-expression') api project(':services:bonita-cache') api project(':bpm:bonita-core:bonita-contract-data') api 'org.bonitasoft.engine:bonita-connector-model' testImplementation libs.assertj testImplementation libs.systemRules testRuntimeOnly libs.logback annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/ConnectorInstanceService.java ================================================ /** * 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.core.connector; import java.util.List; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceCreationException; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceDeletionException; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceModificationException; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceNotFoundException; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException; import org.bonitasoft.engine.core.process.instance.model.SAbstractConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo; import org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface ConnectorInstanceService { String CONNECTOR_INSTANCE = "CONNECTOR_INSTANCE"; String CONNECTOR_INSTANCE_STATE = "CONNECTOR_INSTANCE_STATE"; String CONNECTOR_INSTANCE_STATE_UPDATED = "CONNECTOR_INSTANCE_STATE_UPDATED"; /** * Get a list of connectorInstances for specified container * * @param containerId * Identifier of container * @param containerType * Type of container * @param activationEvent * The event to indicate when the connector will be activated * @return list of connectorInstance objects * @throws SConnectorInstanceReadException * Error thrown if has exceptions during the connector retrieve */ List getConnectorInstances(long containerId, String containerType, ConnectorEvent activationEvent, int from, int numberOfResult, String state) throws SConnectorInstanceReadException; /** * Create connector instance by give connector instance, the connector instance will be stored in database * * @param connectorInstance * Connector instance * @throws SConnectorInstanceCreationException * Error thrown if has exceptions during the connector instance creation */ void createConnectorInstance(SConnectorInstance connectorInstance) throws SConnectorInstanceCreationException; /** * Delete the given connector instance from the database * * @param connectorInstance * the connector instance * @throws SConnectorInstanceDeletionException * if has exceptions during the connector instance deletion */ void deleteConnectorInstance(SConnectorInstance connectorInstance) throws SConnectorInstanceDeletionException; /** * @param sConnectorInstance * @param state * @throws SConnectorInstanceModificationException */ void setState(SAbstractConnectorInstance sConnectorInstance, String state) throws SConnectorInstanceModificationException; /** * Defines the exception associated to the connector failure * * @param connectorInstanceWithFailure failed connector instance * @param throwable exception responsible for connector failure * @throws SConnectorInstanceModificationException * @since 6.1 */ void setConnectorInstanceFailureException(SConnectorInstanceWithFailureInfo connectorInstanceWithFailure, Throwable throwable) throws SConnectorInstanceModificationException; /** * @param connectorInstanceId * @return * @throws SConnectorInstanceReadException * @throws SConnectorInstanceNotFoundException */ SConnectorInstance getConnectorInstance(long connectorInstanceId) throws SConnectorInstanceReadException, SConnectorInstanceNotFoundException; /** * Retrieves the connector instance with failure information for the given connector instance id * * @param connectorInstanceId * @return the connector instance with failure information for the given connector instance id * @throws SConnectorInstanceReadException * @throws SConnectorInstanceNotFoundException * @since 6.1 */ SConnectorInstanceWithFailureInfo getConnectorInstanceWithFailureInfo(long connectorInstanceId) throws SConnectorInstanceReadException, SConnectorInstanceNotFoundException; /** * Retrieves the connector instance with failure information for the given container * * @param containerId * @param containerType * @param state * @param from * @param maxResults * @return the connector instance with failure information for the given connector instance id * @throws SConnectorInstanceReadException * @throws SConnectorInstanceNotFoundException * @since 6.1 */ List getConnectorInstancesWithFailureInfo(long containerId, String containerType, String state, int from, int maxResults) throws SConnectorInstanceReadException; /** * @param containerId * @param containerType * @return * @throws SConnectorInstanceReadException */ long getNumberOfConnectorInstances(long containerId, String containerType) throws SConnectorInstanceReadException; /** * @param containerId * @param containerType * @param from * @param numberOfResult * @throws SConnectorInstanceReadException */ List getConnectorInstances(long containerId, String containerType, int from, int numberOfResult, String fieldName, OrderByType orderByType) throws SConnectorInstanceReadException; /** * @param containerId * @param containerType * @return * @throws SConnectorInstanceReadException */ SConnectorInstance getNextExecutableConnectorInstance(long containerId, String containerType, ConnectorEvent activationEvent) throws SConnectorInstanceReadException; /** * @param searchOptions * @return */ long getNumberOfConnectorInstances(QueryOptions searchOptions) throws SBonitaReadException; /** * @param searchOptions * @return */ List searchConnectorInstances(QueryOptions searchOptions) throws SBonitaReadException; /** * @param connectorInstance * @param archiveDate * @throws SConnectorInstanceCreationException */ void archiveConnectorInstance(SConnectorInstance connectorInstance, long archiveDate) throws SConnectorInstanceCreationException; /** * @param searchOptions * @param persistenceService * @return * @throws SBonitaReadException */ long getNumberArchivedConnectorInstance(QueryOptions searchOptions, ReadPersistenceService persistenceService) throws SBonitaReadException; /** * @param searchOptions * @param persistenceService * @return * @throws SBonitaReadException */ List searchArchivedConnectorInstance(QueryOptions searchOptions, ReadPersistenceService persistenceService) throws SBonitaReadException; /** * @param containerId * @param containerType * @throws SConnectorInstanceReadException * @throws SConnectorInstanceDeletionException * @since 6.1 */ void deleteConnectors(long containerId, String containerType) throws SConnectorInstanceReadException, SConnectorInstanceDeletionException; /** * Delete archived connector instances using a list of container ids and a container type * * @param containerIds ids on the container (source process instance id or source task id) * @param containerType * @throws SBonitaException */ void deleteArchivedConnectorInstances(List containerIds, String containerType) throws SBonitaException; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/ConnectorResult.java ================================================ /** * 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.core.connector; import java.util.Collections; import java.util.Map; import lombok.AllArgsConstructor; import lombok.Data; import org.bonitasoft.engine.connector.Connector; /** * Contains the instantiated connector and its result * It is used to give make the connector service return both the instantiated connector and its result * in order to be able to call disconnect on it in the execute operation method * * @author Baptiste Mesta */ @Data @AllArgsConstructor public class ConnectorResult { private Connector connector; private Map result; private long executionTimeMillis; public Map getResult() { if (result == null) { return Collections.emptyMap(); } return Collections.unmodifiableMap(result); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/ConnectorService.java ================================================ /** * 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.core.connector; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.bonitasoft.engine.core.connector.exception.SConnectorException; import org.bonitasoft.engine.core.connector.exception.SInvalidConnectorImplementationException; import org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.resources.SBARResource; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @since 6.0 */ public interface ConnectorService { String TO_BE_EXECUTED = "TO_BE_EXECUTED"; String DONE = "DONE"; String FAILED = "FAILED"; String SKIPPED = "SKIPPED"; String TO_RE_EXECUTE = "TO_RE_EXECUTE"; /** * Execute a connector instance by given connectorDefinitionId and connectorDefinitionVersion * * @param processDefinitionId * The identifier of process definition * @param connectorDefinitionId * The identifier of connector definition * @param connectorDefinitionVersion * The version of connector definition * @param connectorInputParameters * The input of connector * @param inputValues * The input values of connector * @param classLoader * The class loader used to load and run connector * @param sexpContext * The expression context * @return The output after connector executing * @throws SConnectorException * Error thrown if has exceptions during the connector executing */ ConnectorResult executeMultipleEvaluation(long processDefinitionId, String connectorDefinitionId, String connectorDefinitionVersion, Map connectorInputParameters, Map> inputValues, ClassLoader classLoader, SExpressionContext sexpContext) throws SConnectorException; /** * Load connectors for given process definition and tenant, connectors will be stored in cache after loading * * @param sDefinition * The process definition * @return true if all connectors found have all them dependencies resolved and are correctly loaded * @throws SConnectorException * Error thrown if has exceptions during the connector loading */ boolean loadConnectors(SProcessDefinition sDefinition) throws SConnectorException; /** * Set connector implementation for id and version specified connector. * Store all connector related files if they are not existed and replace the old implementation with the new one in * file system. * Delete former and load current connectors in cache. * * @param sProcessDefinition * The process definition which the connector belongs to * @param connectorId * Id of connector definition. * @param connectorVersion * Version of connector definition * @param connectorImplementationArchive * zip byte array containing the connector implementation information * @throws SConnectorException * Error thrown if has exceptions during the connector implementation setting * @throws SInvalidConnectorImplementationException */ void setConnectorImplementation(SProcessDefinition sProcessDefinition, String connectorId, String connectorVersion, byte[] connectorImplementationArchive) throws SConnectorException, SInvalidConnectorImplementationException; /** * Get a list of connector implementation descriptors for id specified process definition, the returned list is * paginated * * @param processDefinitionId * Identifier of process definition * @param fromIndex * Start index of connector record * @param numberPerPage * Number of connectors we want to get. Maximum number of connectors returned. * @param field * The field that the result ordered by * @param order * The order, ACS or DESC * @return A list of all satisfied connector implementation descriptor objects * @throws SConnectorException * Error thrown if has exceptions during the connector implementations retrieve */ List getConnectorImplementations(long processDefinitionId, int fromIndex, int numberPerPage, String field, OrderByType order) throws SConnectorException; /** * Get connector implementation descriptor for specified connector in a process definition. * * @param processDefinitionId * Identifier of process definition * @param connectorId * id of connector definition * @param connectorVersion * version of connector definition * @return connector implementation descriptor object * @throws SConnectorException * Error thrown if has exceptions during the connector implementation get */ SConnectorImplementationDescriptor getConnectorImplementation(long processDefinitionId, String connectorId, String connectorVersion) throws SConnectorException; /** * @param parameters * @param sExpressionContext * @param inputValues * @return * @throws SExpressionTypeUnknownException * @throws SExpressionEvaluationException * @throws SExpressionDependencyMissingException * @throws SInvalidExpressionException */ Map evaluateInputParameters(String connectorId, Map parameters, SExpressionContext sExpressionContext, Map> inputValues) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException; SConnectorImplementationDescriptor getConnectorImplementationDescriptor(long processDefinitionId, String connectorId, String version) throws SConnectorException; /** * @param outputs * @param expressionContext * @param result * @throws SConnectorException */ void executeOutputOperation(List outputs, SExpressionContext expressionContext, ConnectorResult result) throws SConnectorException; /** * @param processDefinitionId * @param sConnectorInstance * @param connectorImplementationDescriptor * @param classLoader * @param inputParameters * @return the result of the connector execution * @throws SConnectorException */ CompletableFuture executeConnector(long processDefinitionId, SConnectorInstance sConnectorInstance, SConnectorImplementationDescriptor connectorImplementationDescriptor, ClassLoader classLoader, Map inputParameters) throws SConnectorException; /** * @param result * @throws SConnectorException */ void disconnect(ConnectorResult result) throws SConnectorException; /** * @param processDefinitionId * the id of the process definition * @return * the number of connector implementation for this process definition * @throws SConnectorException */ Long getNumberOfConnectorImplementations(long processDefinitionId) throws SConnectorException; List getConnectorImplementations(long processDefinitionId, int from, int numberOfElements) throws SBonitaReadException; void addConnectorImplementation(Long processDefinitionId, String name, byte[] content) throws SRecorderException; void removeConnectorImplementations(long processDefinitionId) throws SBonitaReadException, SRecorderException; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorDefinitionNotFoundException.java ================================================ /** * 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.core.connector.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Celine Souchet */ public class SConnectorDefinitionNotFoundException extends SBonitaException { private static final long serialVersionUID = -4558945817033278955L; public SConnectorDefinitionNotFoundException(final long id) { super("Unable to find connector definition with id \"" + id + "\"."); } public SConnectorDefinitionNotFoundException(final String message) { super(message); } public SConnectorDefinitionNotFoundException(final String message, final Throwable cause) { super(message, cause); } public SConnectorDefinitionNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorException.java ================================================ /** * 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.core.connector.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Feng Hui */ public class SConnectorException extends SBonitaException { private static final long serialVersionUID = -3113075377405323282L; public SConnectorException(String message) { super(message); } public SConnectorException(Throwable t) { super(t); } public SConnectorException(String message, Exception e) { super(message, e); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorInstanceCreationException.java ================================================ /** * 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.core.connector.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SConnectorInstanceCreationException extends SBonitaException { public SConnectorInstanceCreationException(final Exception e) { super(e); } /** * @param string * @param e */ public SConnectorInstanceCreationException(final String message, final Exception e) { super(e); } private static final long serialVersionUID = -1002690534909571624L; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorInstanceDeletionException.java ================================================ /** * 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.core.connector.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SConnectorInstanceDeletionException extends SBonitaException { private static final long serialVersionUID = -6148115450304632856L; public SConnectorInstanceDeletionException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorInstanceModificationException.java ================================================ /** * 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.core.connector.exception; /** * @author Baptiste Mesta */ public class SConnectorInstanceModificationException extends SConnectorException { private static final long serialVersionUID = -7431560273445001133L; public SConnectorInstanceModificationException(final Throwable t) { super(t); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorInstanceNotFoundException.java ================================================ /** * 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.core.connector.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SConnectorInstanceNotFoundException extends SBonitaException { private static final long serialVersionUID = -4558945817033278955L; public SConnectorInstanceNotFoundException(final long connectorInstanceId) { super("Unable to find connector instance with id " + connectorInstanceId); } public SConnectorInstanceNotFoundException(final String message) { super(message); } public SConnectorInstanceNotFoundException(final String message, final Throwable cause) { super(message, cause); } public SConnectorInstanceNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorInstanceReadException.java ================================================ /** * 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.core.connector.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SConnectorInstanceReadException extends SBonitaException { private static final long serialVersionUID = -8274379054928302790L; public SConnectorInstanceReadException(final Exception e) { super(e); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SConnectorValidationException.java ================================================ /** * 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.core.connector.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Feng Hui */ public class SConnectorValidationException extends SBonitaException { private static final long serialVersionUID = -7025831546419799447L; public SConnectorValidationException(final String message) { super(message); } public SConnectorValidationException(final Throwable t) { super(t); } public SConnectorValidationException(final String message, final Exception e) { super(message, e); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/exception/SInvalidConnectorImplementationException.java ================================================ /** * 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.core.connector.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor; /** * @author Feng Hui * @author Celine Souchet */ public class SInvalidConnectorImplementationException extends SBonitaException { private static final long serialVersionUID = -3113075377405323282L; public SInvalidConnectorImplementationException(final String message, final SConnectorImplementationDescriptor connectorImplementationDescriptor) { this(message); setConnectorImplementationClassNameOnContext(connectorImplementationDescriptor.getImplementationClassName()); setConnectorDefinitionIdOnContext(connectorImplementationDescriptor.getDefinitionId()); setConnectorDefinitionVersionOnContext(connectorImplementationDescriptor.getDefinitionVersion()); } public SInvalidConnectorImplementationException(final String message) { super(message); } public SInvalidConnectorImplementationException(final Throwable cause) { super(cause); } public SInvalidConnectorImplementationException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/impl/ConnectorArchive.java ================================================ /** * 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.core.connector.impl; import java.util.HashMap; import java.util.Map; /** * @author Baptiste Mesta */ class ConnectorArchive { private Map dependencies = new HashMap<>(); private String connectorImplName; private byte[] connectorImplContent; public void addDependency(String entryName, byte[] fileContent) { dependencies.put(entryName, fileContent); } public Map getDependencies() { return dependencies; } public void setConnectorImpl(String connectorImplName, byte[] connectorImplContent) { this.connectorImplName = connectorImplName; this.connectorImplContent = connectorImplContent; } public byte[] getConnectorImplContent() { return connectorImplContent; } public String getConnectorImplName() { return connectorImplName; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/impl/ConnectorExecutionTimeLogger.java ================================================ /** * Copyright (C) 2020 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.core.connector.impl; import java.util.Map; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.connector.Connector; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; /** * Log connector execution time when the connector took more than the configured threshold */ @Slf4j public class ConnectorExecutionTimeLogger { private Long warnWhenLongerThanMillis; /** * @param warnWhenLongerThanMillis the duration in milli seconds above which the time taken will be logged as a * warning */ public ConnectorExecutionTimeLogger(Long warnWhenLongerThanMillis) { this.warnWhenLongerThanMillis = warnWhenLongerThanMillis; } public void log(long processDefinitionId, SConnectorInstance sConnectorInstance, Connector connector, Map inputParameters, long executionTimeMillis) { if (executionTimeMillis < warnWhenLongerThanMillis) { return; } log.warn( "Connector {} with id {} with class {} of process definition {} on element {} took {} ms.", sConnectorInstance.getName(), sConnectorInstance.getId(), connector.getClass().getName(), processDefinitionId, printContext(sConnectorInstance), executionTimeMillis); if (log.isDebugEnabled()) { log.debug("Input parameters of the connector with id {}: {}", sConnectorInstance.getId(), print(inputParameters)); } } private String print(Map inputParameters) { return inputParameters.entrySet().stream() .map((e -> e.getKey() + ": [" + e.getValue().toString().replaceAll("([\\r\\n])", " ") + "]")) .collect(Collectors.joining(", ", "{", "}")); } private String printContext(SConnectorInstance sConnectorInstance) { return sConnectorInstance.getContainerType() + " with id " + sConnectorInstance.getContainerId(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/impl/ConnectorInstanceServiceImpl.java ================================================ /** * 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.core.connector.impl; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceCreationException; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceDeletionException; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceModificationException; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceNotFoundException; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException; import org.bonitasoft.engine.core.process.instance.model.SAbstractConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo; import org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAConnectorInstanceBuilderFactory; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; /** * @author Baptiste Mesta * @author Yanyan Liu * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class ConnectorInstanceServiceImpl implements ConnectorInstanceService { /** * Length maximum of the message of the exception */ private static final int MAX_MESSAGE_LENGTH = 255; private final Recorder recorder; private final ReadPersistenceService persistenceService; private final ArchiveService archiveService; public ConnectorInstanceServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder, final ArchiveService archiveService) { this.persistenceService = persistenceService; this.recorder = recorder; this.archiveService = archiveService; } @Override public void setState(final SAbstractConnectorInstance sConnectorInstance, final String state) throws SConnectorInstanceModificationException { final EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor(); entityUpdateDescriptor.addField(SConnectorInstance.STATE_KEY, state); try { recorder.recordUpdate(UpdateRecord.buildSetFields(sConnectorInstance, entityUpdateDescriptor), CONNECTOR_INSTANCE_STATE); } catch (final SRecorderException e) { throw new SConnectorInstanceModificationException(e); } } @Override public void setConnectorInstanceFailureException( final SConnectorInstanceWithFailureInfo connectorInstanceWithFailure, final Throwable throwable) throws SConnectorInstanceModificationException { final EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor(); entityUpdateDescriptor.addField(SConnectorInstanceWithFailureInfo.EXCEPTION_MESSAGE, getExceptionMessage(throwable)); entityUpdateDescriptor.addField(SConnectorInstanceWithFailureInfo.STACK_TRACE, getStringStackTrace(throwable)); try { recorder.recordUpdate(UpdateRecord.buildSetFields(connectorInstanceWithFailure, entityUpdateDescriptor), CONNECTOR_INSTANCE); } catch (final SRecorderException e) { throw new SConnectorInstanceModificationException(e); } } private String getExceptionMessage(final Throwable throwable) { if (throwable == null) { return null; } Throwable current = throwable; while (current.getCause() != null) { current = current.getCause(); } String message = current.getMessage(); if (message != null && message.length() > MAX_MESSAGE_LENGTH) { message = message.substring(0, MAX_MESSAGE_LENGTH); } return message; } private static String getStringStackTrace(final Throwable throwable) { if (throwable == null) { return null; } try (StringWriter writer = new StringWriter(); PrintWriter printer = new PrintWriter(writer)) { throwable.printStackTrace(printer); return writer.toString(); } catch (Throwable e) { //Unable to print exception to the Writer, simply retrieve all exceptions as stacktrace try { Throwable current = throwable; StringBuilder stacktrace = new StringBuilder(); stacktrace.append("Unable to retrieve stacktrace, exceptions were:\n"); do { stacktrace.append(" * ").append(current.getClass().getName()).append(": ") .append(current.getMessage()).append("\n"); current = current.getCause(); } while (current != null); return stacktrace.toString(); } catch (Throwable e1) { return "Unable to retrieve exception stacktrace"; } } } @Override public void createConnectorInstance(final SConnectorInstance connectorInstance) throws SConnectorInstanceCreationException { try { recorder.recordInsert(new InsertRecord(connectorInstance), CONNECTOR_INSTANCE); } catch (final SRecorderException e) { throw new SConnectorInstanceCreationException(e); } } @Override public List getConnectorInstances(final long containerId, final String containerType, final ConnectorEvent activationEvent, final int from, final int numberOfResult, final String state) throws SConnectorInstanceReadException { final Map inputParameters = new HashMap<>(4); inputParameters.put("containerId", containerId); inputParameters.put("containerType", containerType); inputParameters.put("activationEvent", activationEvent); inputParameters.put("state", state); final SelectListDescriptor selectListDescriptor = new SelectListDescriptor<>( "getConnectorInstancesWithState", inputParameters, SConnectorInstance.class, new QueryOptions(from, numberOfResult, SConnectorInstance.class, "id", OrderByType.ASC)); try { return persistenceService.selectList(selectListDescriptor); } catch (final SBonitaReadException e) { throw new SConnectorInstanceReadException(e); } } @Override public List getConnectorInstances(final long containerId, final String containerType, final int from, final int numberOfResult, final String fieldName, final OrderByType orderByType) throws SConnectorInstanceReadException { final Map inputParameters = new HashMap<>(2); inputParameters.put("containerId", containerId); inputParameters.put("containerType", containerType); final SelectListDescriptor selectListDescriptor = new SelectListDescriptor<>( "getConnectorInstances", inputParameters, SConnectorInstance.class, new QueryOptions(from, numberOfResult, SConnectorInstance.class, fieldName, orderByType)); try { return persistenceService.selectList(selectListDescriptor); } catch (final SBonitaReadException e) { throw new SConnectorInstanceReadException(e); } } private List getConnectorInstancesOrderedById(final long containerId, final String containerType, final int from, final int numberOfResult) throws SConnectorInstanceReadException { final Map inputParameters = new HashMap<>(2); inputParameters.put("containerId", containerId); inputParameters.put("containerType", containerType); final SelectListDescriptor selectListDescriptor = new SelectListDescriptor<>( "getConnectorInstancesOrderedById", inputParameters, SConnectorInstance.class, new QueryOptions(from, numberOfResult)); try { return persistenceService.selectList(selectListDescriptor); } catch (final SBonitaReadException e) { throw new SConnectorInstanceReadException(e); } } @Override public SConnectorInstance getNextExecutableConnectorInstance(final long containerId, final String containerType, final ConnectorEvent activationEvent) throws SConnectorInstanceReadException { final Map inputParameters = new HashMap<>(3); inputParameters.put("containerId", containerId); inputParameters.put("containerType", containerType); inputParameters.put("activationEvent", activationEvent); final SelectListDescriptor selectOneDescriptor = new SelectListDescriptor<>( "getNextExecutableConnectorInstance", inputParameters, SConnectorInstance.class, new QueryOptions(0, 1)); try { final List selectList = persistenceService.selectList(selectOneDescriptor); if (selectList.size() == 1) { return selectList.get(0); } return null; } catch (final SBonitaReadException e) { throw new SConnectorInstanceReadException(e); } } @Override public long getNumberOfConnectorInstances(final long containerId, final String containerType) throws SConnectorInstanceReadException { final Map inputParameters = new HashMap<>(2); inputParameters.put("containerId", containerId); inputParameters.put("containerType", containerType); final SelectOneDescriptor selectListDescriptor = new SelectOneDescriptor<>( "getNumberOfConnectorInstances", inputParameters, SConnectorInstance.class); try { return persistenceService.selectOne(selectListDescriptor); } catch (final SBonitaReadException e) { throw new SConnectorInstanceReadException(e); } } @Override public SConnectorInstance getConnectorInstance(final long connectorInstanceId) throws SConnectorInstanceReadException, SConnectorInstanceNotFoundException { final SelectByIdDescriptor selectByIdDescriptor = new SelectByIdDescriptor<>( SConnectorInstance.class, connectorInstanceId); try { final SConnectorInstance connectorInstance = persistenceService.selectById(selectByIdDescriptor); if (connectorInstance == null) { throw new SConnectorInstanceNotFoundException(connectorInstanceId); } return connectorInstance; } catch (final SBonitaReadException e) { throw new SConnectorInstanceReadException(e); } } @Override public SConnectorInstanceWithFailureInfo getConnectorInstanceWithFailureInfo(final long connectorInstanceId) throws SConnectorInstanceReadException, SConnectorInstanceNotFoundException { final SelectByIdDescriptor selectByIdDescriptor = new SelectByIdDescriptor<>( SConnectorInstanceWithFailureInfo.class, connectorInstanceId); try { final SConnectorInstanceWithFailureInfo connectorInstance = persistenceService .selectById(selectByIdDescriptor); if (connectorInstance == null) { throw new SConnectorInstanceNotFoundException(connectorInstanceId); } return connectorInstance; } catch (final SBonitaReadException e) { throw new SConnectorInstanceReadException(e); } } @Override public List getConnectorInstancesWithFailureInfo(final long containerId, final String containerType, final String state, final int from, final int maxResults) throws SConnectorInstanceReadException { final Map inputParameters = new HashMap<>(3); inputParameters.put("containerId", containerId); inputParameters.put("containerType", containerType); inputParameters.put("state", state); final SelectListDescriptor selectListDescriptor = new SelectListDescriptor<>( "getConnectorInstancesWithFailureInfoInState", inputParameters, SConnectorInstanceWithFailureInfo.class, new QueryOptions(from, maxResults, SConnectorInstanceWithFailureInfo.class, "id", OrderByType.ASC)); try { return persistenceService.selectList(selectListDescriptor); } catch (final SBonitaReadException e) { throw new SConnectorInstanceReadException(e); } } @Override public long getNumberOfConnectorInstances(final QueryOptions searchOptions) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SConnectorInstance.class, searchOptions, null); } // charles @Override public List searchConnectorInstances(final QueryOptions searchOptions) throws SBonitaReadException { return persistenceService.searchEntity(SConnectorInstance.class, searchOptions, null); } @Override public void archiveConnectorInstance(final SConnectorInstance connectorInstance, final long archiveDate) throws SConnectorInstanceCreationException { if (connectorInstance != null) { final SAConnectorInstance saConnectorInstance = BuilderFactory.get(SAConnectorInstanceBuilderFactory.class) .createNewArchivedConnectorInstance(connectorInstance).done(); final ArchiveInsertRecord insertRecord = new ArchiveInsertRecord(saConnectorInstance); try { archiveService.recordInsert(archiveDate, insertRecord); } catch (final SBonitaException e) { throw new SConnectorInstanceCreationException( "Unable to archive the connectorInstance instance with id " + connectorInstance.getId(), e); } } } @Override public void deleteConnectorInstance(final SConnectorInstance connectorInstance) throws SConnectorInstanceDeletionException { try { recorder.recordDelete(new DeleteRecord(connectorInstance), CONNECTOR_INSTANCE); } catch (final SRecorderException e) { throw new SConnectorInstanceDeletionException(e); } } @Override public long getNumberArchivedConnectorInstance(final QueryOptions searchOptions, final ReadPersistenceService persistenceService) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SAConnectorInstance.class, searchOptions, null); } @Override public List searchArchivedConnectorInstance(final QueryOptions searchOptions, final ReadPersistenceService persistenceService) throws SBonitaReadException { return persistenceService.searchEntity(SAConnectorInstance.class, searchOptions, null); } @Override public void deleteArchivedConnectorInstances(List containerIds, String containerType) throws SBonitaException { HashMap map = new HashMap<>(); map.put("containerIds", containerIds); map.put("containerType", containerType); archiveService.deleteFromQuery("deleteArchivedConnectorInstances", map); } @Override public void deleteConnectors(final long containerId, final String containerType) throws SConnectorInstanceReadException, SConnectorInstanceDeletionException { List connetorInstances; do { // the QueryOptions always will use 0 as start index because the retrieved results will be deleted connetorInstances = getConnectorInstancesOrderedById(containerId, containerType, 0, 100); for (final SConnectorInstance sConnectorInstance : connetorInstances) { deleteConnectorInstance(sConnectorInstance); } } while (!connetorInstances.isEmpty()); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/impl/ConnectorServiceImpl.java ================================================ /** * 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.core.connector.impl; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.Serializable; 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 java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import javax.xml.bind.JAXBException; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.cache.SCacheException; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.classloader.SClassLoaderException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.io.IOUtil; import org.bonitasoft.engine.connector.Connector; import org.bonitasoft.engine.connector.ConnectorExecutor; import org.bonitasoft.engine.core.connector.ConnectorResult; import org.bonitasoft.engine.core.connector.ConnectorService; import org.bonitasoft.engine.core.connector.exception.SConnectorException; import org.bonitasoft.engine.core.connector.exception.SInvalidConnectorImplementationException; import org.bonitasoft.engine.core.connector.parser.ConnectorImplementationFieldComparator; import org.bonitasoft.engine.core.connector.parser.ConnectorImplementationParser; import org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.OperationService; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.operation.model.SOperation; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.dependency.DependencyService; import org.bonitasoft.engine.dependency.SDependencyException; import org.bonitasoft.engine.dependency.model.AbstractSDependency; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.resources.BARResourceType; import org.bonitasoft.engine.resources.ProcessResourcesService; import org.bonitasoft.engine.resources.SBARResource; import org.bonitasoft.engine.tracking.TimeTracker; import org.bonitasoft.engine.tracking.TimeTrackerRecords; /** * @author Baptiste Mesta * @author Yanyan Liu * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ @Slf4j public class ConnectorServiceImpl implements ConnectorService { protected static final String CONNECTOR_CACHE_NAME = "CONNECTOR"; private static final String LINE_SEPARATOR = System.getProperty("line.separator"); private static final String IMPLEMENTATION_EXT = ".impl"; private final CacheService cacheService; private final ConnectorExecutor connectorExecutor; private final ExpressionResolverService expressionResolverService; private final OperationService operationService; private final DependencyService dependencyService; private final ClassLoaderService classLoaderService; private final TimeTracker timeTracker; private final ConnectorExecutionTimeLogger connectorExecutionTimeLogger; private final ProcessResourcesService processResourcesService; private final ConnectorImplementationParser connectorImplementationParser = new ConnectorImplementationParser(); public ConnectorServiceImpl(final CacheService cacheService, final ConnectorExecutor connectorExecutor, final ExpressionResolverService expressionResolverService, final OperationService operationService, final DependencyService dependencyService, ClassLoaderService classLoaderService, final TimeTracker timeTracker, ProcessResourcesService processResourcesService, ConnectorExecutionTimeLogger connectorExecutionTimeLogger) { this.cacheService = cacheService; this.connectorExecutor = connectorExecutor; this.expressionResolverService = expressionResolverService; this.classLoaderService = classLoaderService; this.processResourcesService = processResourcesService; this.operationService = operationService; this.dependencyService = dependencyService; this.timeTracker = timeTracker; this.connectorExecutionTimeLogger = connectorExecutionTimeLogger; } /** * Build the log message using the connector instance's context (name, version, connector id, connector instance id, * container type, container id) * * @param connectorInstance * @return the log message built using the connector instance's context */ private static String buildConnectorContextMessage(final SConnectorInstance connectorInstance) { return " [name: <" + connectorInstance.getName() + ">, version: <" + connectorInstance.getVersion() + ">, connector id: <" + connectorInstance.getConnectorId() + ">, connector instance id: <" + connectorInstance.getId() + ">, container type: <" + connectorInstance.getContainerType() + ">, container id: <" + connectorInstance.getContainerId() + ">, activation event: <" + connectorInstance.getActivationEvent() + ">]"; } private static String buildConnectorInputMessage(final Map inputParameters) { final StringBuilder stb = new StringBuilder(); if (!inputParameters.isEmpty()) { stb.append(LINE_SEPARATOR); stb.append("Inputs: "); stb.append(LINE_SEPARATOR); stb.append(inputParameters.entrySet().stream() .map(e -> " <" + e.getKey() + "> : <" + e.getValue() + ">") .collect(Collectors.joining(LINE_SEPARATOR))); } return stb.toString(); } @Override public CompletableFuture executeConnector(final long processDefinitionId, final SConnectorInstance sConnectorInstance, SConnectorImplementationDescriptor connectorImplementationDescriptor, final ClassLoader classLoader, final Map inputParameters) throws SConnectorException { final String implementationClassName = connectorImplementationDescriptor.getImplementationClassName(); if (log.isDebugEnabled()) { log.debug("Executing connector {} {}", buildConnectorContextMessage(sConnectorInstance), buildConnectorInputMessage(inputParameters)); } return executeConnectorInClassloader(implementationClassName, classLoader, inputParameters) .thenApply(result -> { connectorExecutionTimeLogger.log(processDefinitionId, sConnectorInstance, result.getConnector(), inputParameters, result.getExecutionTimeMillis()); return result; }); } @Override public SConnectorImplementationDescriptor getConnectorImplementationDescriptor(long processDefinitionId, String connectorId, String version) throws SConnectorException { try { SConnectorImplementationDescriptor descriptor = getImplementation(processDefinitionId, connectorId, version); if (descriptor == null) { throw new SConnectorException("There is no implementation found for the connector " + connectorId + " with version " + version); } return descriptor; } catch (final SCacheException e) { throw new SConnectorException(e); } } @Override public void executeOutputOperation(final List outputs, final SExpressionContext expressionContext, final ConnectorResult result) throws SConnectorException { final long startTime = System.currentTimeMillis(); try { expressionContext.putAllInputValues(result.getResult()); operationService.execute(outputs, expressionContext.getContainerId(), expressionContext.getContainerType(), expressionContext);// data is in } catch (final SOperationExecutionException e) { throw new SConnectorException(e); } finally { if (timeTracker.isTrackable(TimeTrackerRecords.EXECUTE_CONNECTOR_OUTPUT_OPERATIONS)) { final long endTime = System.currentTimeMillis(); String desc = "ConnectorResult: " + result; timeTracker.track(TimeTrackerRecords.EXECUTE_CONNECTOR_OUTPUT_OPERATIONS, desc, endTime - startTime); } disconnect(result); } } @Override public void disconnect(final ConnectorResult result) throws SConnectorException { final long startTime = System.currentTimeMillis(); try { connectorExecutor.disconnect(new SConnectorAdapter(result.getConnector())); } catch (final org.bonitasoft.engine.connector.exception.SConnectorException e) { throw new SConnectorException(e); } finally { if (timeTracker.isTrackable(TimeTrackerRecords.EXECUTE_CONNECTOR_DISCONNECT)) { final long endTime = System.currentTimeMillis(); final StringBuilder desc = new StringBuilder(); desc.append("ConnectorResult: "); desc.append(result); timeTracker.track(TimeTrackerRecords.EXECUTE_CONNECTOR_DISCONNECT, desc.toString(), endTime - startTime); } } } private SConnectorImplementationDescriptor getImplementation(final long rootDefinitionId, final String connectorId, final String version) throws SConnectorException, SCacheException { SConnectorImplementationDescriptor descriptor; try { final String key = buildConnectorImplementationKey(rootDefinitionId, connectorId, version); descriptor = (SConnectorImplementationDescriptor) cacheService.get(CONNECTOR_CACHE_NAME, key); if (descriptor == null) { // No value in cache : reload connector to ensure the cache stores all connectors for the current process loadConnectors(rootDefinitionId); descriptor = (SConnectorImplementationDescriptor) cacheService.get(CONNECTOR_CACHE_NAME, key); } } catch (final NumberFormatException e) { throw new SConnectorException(e); } catch (final SCacheException e) { throw e; } return descriptor; } private void cache(final long processDefinitionId, final SConnectorImplementationDescriptor connectorImplementation) throws SCacheException { final String key = buildConnectorImplementationKey(processDefinitionId, connectorImplementation.getDefinitionId(), connectorImplementation.getDefinitionVersion()); cacheService.store(CONNECTOR_CACHE_NAME, key, connectorImplementation); } protected String buildConnectorImplementationKey(final long rootDefinitionId, final String connectorId, final String version) { return rootDefinitionId + ":" + connectorId + "-" + version; } @Override public ConnectorResult executeMultipleEvaluation(final long processDefinitionId, final String connectorDefinitionId, final String connectorDefinitionVersion, final Map connectorInputParameters, final Map> inputValues, final ClassLoader classLoader, final SExpressionContext expressionContext) throws SConnectorException { final String implementationClassName = getConnectorImplementationDescriptor(processDefinitionId, connectorDefinitionId, connectorDefinitionVersion) .getImplementationClassName(); final Map inputParameters; try { inputParameters = evaluateInputParameters(connectorDefinitionId, connectorInputParameters, expressionContext, inputValues); } catch (final SBonitaException e) { throw new SConnectorException(e); } final ConnectorResult connectorResult; try { connectorResult = executeConnectorInClassloader(implementationClassName, classLoader, inputParameters) .get(); } catch (InterruptedException | ExecutionException e) { throw new SConnectorException(e); } if (log.isDebugEnabled()) { log.debug("Executed connector <{}> with definition id <{}>, version <{}>, {}", implementationClassName, connectorDefinitionId, connectorDefinitionVersion, buildConnectorInputMessage(inputParameters)); } return connectorResult; } private CompletableFuture executeConnectorInClassloader(final String implementationClassName, final ClassLoader classLoader, final Map inputParameters) throws SConnectorException { final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(classLoader); Connector connector = (Connector) classLoader.loadClass(implementationClassName).newInstance(); final SConnectorAdapter sConnectorAdapter = new SConnectorAdapter(connector); return connectorExecutor.execute(sConnectorAdapter, inputParameters, classLoader) .thenApply(result -> new ConnectorResult(connector, result.getOutputs(), result.getExecutionTimeMillis())); } catch (final ClassNotFoundException e) { throw new SConnectorException(implementationClassName + " can not be found.", e); } catch (final InstantiationException e) { throw new SConnectorException(implementationClassName + " can not be instantiated.", e); } catch (Throwable e) { throw new SConnectorException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } @Override public Map evaluateInputParameters(final String connectorId, final Map parameters, final SExpressionContext sExpressionContext, final Map> inputValues) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { final long startTime = System.currentTimeMillis(); final Map inputParameters = new HashMap<>(parameters.size()); try { for (final Entry input : parameters.entrySet()) { try { if (sExpressionContext != null) { final String key = input.getKey(); if (inputValues != null && !inputValues.isEmpty() && inputValues.containsKey(key)) { sExpressionContext.setSerializableInputValues(inputValues.get(key)); } inputParameters.put(input.getKey(), expressionResolverService.evaluate(input.getValue(), sExpressionContext)); } else { inputParameters.put(input.getKey(), expressionResolverService.evaluate(input.getValue())); } } catch (SBonitaException e) { e.setConnectorInputOnContext(input.getKey()); throw e; } } } finally { if (timeTracker.isTrackable(TimeTrackerRecords.EXECUTE_CONNECTOR_INPUT_EXPRESSIONS)) { final long endTime = System.currentTimeMillis(); String desc = "Connector ID: " + connectorId + " - input parameters: " + inputParameters; timeTracker.track(TimeTrackerRecords.EXECUTE_CONNECTOR_INPUT_EXPRESSIONS, desc, endTime - startTime); } } return inputParameters; } @Override public boolean loadConnectors(final SProcessDefinition sDefinition) throws SConnectorException { return loadConnectors(sDefinition.getId()); } protected boolean loadConnectors(final long processDefinitionId) throws SConnectorException { String name = null; try { final List connectorImplementations = getConnectorImplementations(processDefinitionId, 0, Integer.MAX_VALUE); for (SBARResource connectorImplementationFile : connectorImplementations) { name = connectorImplementationFile.getName(); cache(processDefinitionId, connectorImplementationParser.convert(new String(connectorImplementationFile.getContent()))); } } catch (final JAXBException e) { throw new SConnectorException("Can not load ConnectorImplementation XML. The file name is <" + name + ">.", e); } catch (final SCacheException e) { throw new SConnectorException("Unable to cache the connector implementation " + name + ".", e); } catch (SBonitaReadException e) { throw new SConnectorException("Unable to list the connector implementations", e); } return true; } @Override public void setConnectorImplementation(final SProcessDefinition sProcessDefinition, final String connectorId, final String connectorVersion, final byte[] connectorImplementationArchive) throws SConnectorException, SInvalidConnectorImplementationException { ConnectorArchive connectorArchive = extractConnectorImplementation(connectorImplementationArchive); replaceConnectorImpl(sProcessDefinition, connectorId, connectorVersion, connectorArchive); reLoadConnectors(sProcessDefinition, connectorId, connectorVersion); } private void replaceConnectorImpl(final SProcessDefinition sDefinition, final String connectorId, final String connectorVersion, ConnectorArchive connectorArchive) throws SConnectorException, SInvalidConnectorImplementationException { try { checkConnectorImplementationIsValid( parseConnectorImplementation(connectorArchive.getConnectorImplContent()), connectorId, connectorVersion); SBARResource connectorImplementationFile = getConnectorImplementationResource(sDefinition.getId(), connectorId, connectorVersion); SConnectorImplementationDescriptor connectorImplementationDescriptorToReplace = null; if (connectorImplementationFile != null) { connectorImplementationDescriptorToReplace = parseConnectorImplementation( connectorImplementationFile.getContent()); } updateJarDependencies(sDefinition, connectorArchive, connectorImplementationDescriptorToReplace); updateConnectorImplementationFile(sDefinition, connectorArchive, connectorImplementationFile); classLoaderService.refreshClassLoaderAfterUpdate(identifier(ScopeType.PROCESS, sDefinition.getId())); } catch (final SRecorderException | SDependencyException | SBonitaReadException | SClassLoaderException e) { throw new SConnectorException("Problem replacing connector implementation of connector " + connectorId + " of process " + sDefinition.getId(), e); } } private void updateConnectorImplementationFile(SProcessDefinition sDefinition, ConnectorArchive connectorArchive, SBARResource connectorResourceToReplace) throws SRecorderException { if (connectorResourceToReplace != null && connectorResourceToReplace.getName().equals(connectorArchive.getConnectorImplName())) { processResourcesService.update(connectorResourceToReplace, connectorArchive.getConnectorImplContent()); } else { if (connectorResourceToReplace != null) { processResourcesService.remove(connectorResourceToReplace); } processResourcesService.add(sDefinition.getId(), connectorArchive.getConnectorImplName(), BARResourceType.CONNECTOR, connectorArchive.getConnectorImplContent()); } } private void updateJarDependencies(SProcessDefinition sDefinition, ConnectorArchive connectorArchive, SConnectorImplementationDescriptor connectorImplementationDescriptorToReplace) throws SBonitaReadException, SDependencyException { List jarFileNames = connectorImplementationDescriptorToReplace == null ? Collections.emptyList() : connectorImplementationDescriptorToReplace.getJarDependencies(); Set dependenciesToUpdate = new HashSet<>(); if (jarFileNames != null) { for (String jarFileName : jarFileNames) { AbstractSDependency dependencyOfArtifact = dependencyService .getDependencyOfArtifact(sDefinition.getId(), ScopeType.PROCESS, jarFileName); if (dependencyOfArtifact == null) { // For compatibility with older versions that may still have the wrong name in database: dependencyOfArtifact = dependencyService.getDependencyOfArtifact(sDefinition.getId(), ScopeType.PROCESS, sDefinition.getId() + "_" + jarFileName); } if (dependencyOfArtifact != null) { if (connectorArchive.getDependencies().keySet().contains(jarFileName)) { dependenciesToUpdate.add(jarFileName); } else { dependencyService.deleteDependency(dependencyOfArtifact); } } } } final long processDefinitionId = sDefinition.getId(); for (final Entry file : connectorArchive.getDependencies().entrySet()) { if (dependenciesToUpdate.contains(file.getKey())) { dependencyService.updateDependencyOfArtifact(file.getKey(), file.getValue(), file.getKey(), processDefinitionId, ScopeType.PROCESS); } else { final AbstractSDependency existingDependency = dependencyService .getDependencyOfArtifact(sDefinition.getId(), ScopeType.PROCESS, file.getKey()); if (existingDependency != null) { //a dependency with this name did exists event if it was not declared as a dependency of the connector inside the connector impl file if (connectorImplementationDescriptorToReplace != null) { log.warn( "Updating a dependency of the connector {} in version {} of process definition {}. " + "The jar file {} was not declared in the previous connector implementation but is in the dependencies of the process." + " The jar is still updated but this can lead to inconsistencies.", connectorImplementationDescriptorToReplace.getDefinitionId(), connectorImplementationDescriptorToReplace.getDefinitionVersion(), processDefinitionId, file.getKey()); } dependencyService.updateDependencyOfArtifact(file.getKey(), file.getValue(), file.getKey(), processDefinitionId, ScopeType.PROCESS); } else { dependencyService.createMappedDependency(file.getKey(), file.getValue(), file.getKey(), processDefinitionId, ScopeType.PROCESS); } } } } protected void checkConnectorImplementationIsValid( final SConnectorImplementationDescriptor connectorImplementationDescriptor, final String connectorId, final String connectorVersion) throws SConnectorException, SInvalidConnectorImplementationException { if (!connectorImplementationDescriptor.getDefinitionId().equals(connectorId) || !connectorImplementationDescriptor.getDefinitionVersion().equals(connectorVersion)) { throw new SInvalidConnectorImplementationException( "The connector must implement the connectorDefinition with id = <" + connectorId + "> and version = <" + connectorVersion + ">.", connectorImplementationDescriptor); } } ConnectorArchive extractConnectorImplementation(final byte[] connectorImplementationArchive) throws SInvalidConnectorImplementationException { ConnectorArchive connectorArchive = new ConnectorArchive(); try (ZipInputStream zipInputstream = new ZipInputStream( new ByteArrayInputStream(connectorImplementationArchive))) { ZipEntry zipEntry; while ((zipEntry = zipInputstream.getNextEntry()) != null) { String fileName = getFileName(zipEntry.getName()); if (!fileName.endsWith(".jar") && !fileName.endsWith(".impl")) { continue; } final byte[] fileContent = IOUtil.getBytes(zipInputstream); if (fileName.endsWith(".jar")) { connectorArchive.addDependency(fileName, fileContent); } else { connectorArchive.setConnectorImpl(fileName, fileContent); } zipInputstream.closeEntry(); } } catch (final IOException e) { throw new SInvalidConnectorImplementationException(e); } if (connectorArchive.getConnectorImplContent() == null) { throw new SInvalidConnectorImplementationException( "The connector archive do not contains a connector implementation"); } return connectorArchive; } private String getFileName(String name) { return name.substring(name.lastIndexOf('/') + 1); } private SConnectorImplementationDescriptor parseConnectorImplementation(final byte[] bytes) throws SInvalidConnectorImplementationException { try { return connectorImplementationParser.convert(new String(bytes)); } catch (final JAXBException e) { throw new SInvalidConnectorImplementationException("Can not load ConnectorImplementation XML.", e); } } private SBARResource getConnectorImplementationResource(long processId, String connectorId, String connectorVersion) throws SBonitaReadException, SInvalidConnectorImplementationException { final List listFiles = processResourcesService.get(processId, BARResourceType.CONNECTOR, 0, 1000); final Pattern pattern = Pattern.compile("^.*\\" + IMPLEMENTATION_EXT + "$"); SBARResource connectorToReplace = null; for (final SBARResource resource : listFiles) { final String name = resource.getName(); if (pattern.matcher(name).matches()) { final SConnectorImplementationDescriptor connectorImplementation = parseConnectorImplementation( resource.getContent()); if (connectorId.equals(connectorImplementation.getDefinitionId()) && connectorVersion.equals(connectorImplementation.getDefinitionVersion())) { connectorToReplace = resource; break; } } } return connectorToReplace; } private void reLoadConnectors(final SProcessDefinition sProcessDefinition, final String connectorId, final String connectorVersion) throws SConnectorException { final String connectorKey = buildConnectorImplementationKey(sProcessDefinition.getId(), connectorId, connectorVersion); try { cacheService.remove(CONNECTOR_CACHE_NAME, connectorKey); // re_load connectors loadConnectors(sProcessDefinition); } catch (final SCacheException e) { throw new SConnectorException(e); } } @Override public Long getNumberOfConnectorImplementations(final long processDefinitionId) throws SConnectorException { try { return processResourcesService.count(processDefinitionId, BARResourceType.CONNECTOR); } catch (SBonitaReadException e) { throw new SConnectorException(e); } } @Override public List getConnectorImplementations(final long processDefinitionId, final int fromIndex, final int numberPerPage, final String field, final OrderByType order) throws SConnectorException { final List sConnectorImplementationDescriptors = getAllConnectorImplementations( processDefinitionId); if (sConnectorImplementationDescriptors != null && !sConnectorImplementationDescriptors.isEmpty()) { // pagination if (sConnectorImplementationDescriptors.size() <= fromIndex) { throw new SConnectorException( "page out of range exception. Total size is <" + sConnectorImplementationDescriptors.size() + ">, but from index is <" + fromIndex + ">"); } // set the comparison field var connectorComparator = new ConnectorImplementationFieldComparator(field); if (order == OrderByType.DESC) { sConnectorImplementationDescriptors.sort(connectorComparator.reversed()); } else { // sort with ASC by default sConnectorImplementationDescriptors.sort(connectorComparator); } // sub list int endIndex = fromIndex + numberPerPage; if (endIndex >= sConnectorImplementationDescriptors.size()) { endIndex = sConnectorImplementationDescriptors.size(); } return sConnectorImplementationDescriptors.subList(fromIndex, endIndex); } return Collections.emptyList(); } /** * @param processDefinitionId * @return * @throws SConnectorException */ private List getAllConnectorImplementations(final long processDefinitionId) throws SConnectorException { // get all connector implementations for processDefinitionId List sConnectorImplementationDescriptors = null; try { final int size = cacheService.getCacheSize(CONNECTOR_CACHE_NAME); // reload connectors if connector cache size is 0; if (size == 0) { this.loadConnectors(processDefinitionId); } sConnectorImplementationDescriptors = getConnectorImplementationsFromCacheService(processDefinitionId); if (sConnectorImplementationDescriptors.isEmpty()) { // reload connectors if cache is not filed, e.g. server restart this.loadConnectors(processDefinitionId); sConnectorImplementationDescriptors = getConnectorImplementationsFromCacheService(processDefinitionId); } } catch (final SCacheException e) { // If cache name not found, ignore it. } return sConnectorImplementationDescriptors; } private List getConnectorImplementationsFromCacheService( final long processDefinitionId) throws SCacheException, SConnectorException { List sConnectorImplementationDescriptors; sConnectorImplementationDescriptors = new ArrayList<>(); final List cacheKeys = cacheService.getKeys(CONNECTOR_CACHE_NAME); if (cacheKeys.size() > 0) { for (final Object cacheKey : cacheKeys) { if (String.valueOf(cacheKey).startsWith(String.valueOf(processDefinitionId))) { // Is it needed? SConnectorImplementationDescriptor connectorImplementationDescriptor = (SConnectorImplementationDescriptor) cacheService .get( CONNECTOR_CACHE_NAME, cacheKey); if (!isGoodImplementation(connectorImplementationDescriptor)) { this.loadConnectors(processDefinitionId); connectorImplementationDescriptor = (SConnectorImplementationDescriptor) cacheService .get(CONNECTOR_CACHE_NAME, cacheKey); } sConnectorImplementationDescriptors.add(connectorImplementationDescriptor); } } } return sConnectorImplementationDescriptors; } /** * @param connectorImplementationDescriptor check the implementation has all required properties or not * @return */ private boolean isGoodImplementation(final SConnectorImplementationDescriptor connectorImplementationDescriptor) { return connectorImplementationDescriptor != null && connectorImplementationDescriptor.getImplementationClassName() != null && connectorImplementationDescriptor.getId() != null && connectorImplementationDescriptor.getVersion() != null && connectorImplementationDescriptor.getDefinitionId() != null && connectorImplementationDescriptor.getDefinitionVersion() != null; } @Override public SConnectorImplementationDescriptor getConnectorImplementation(final long processDefinitionId, final String connectorId, final String connectorVersion) throws SConnectorException { SConnectorImplementationDescriptor connectorImplementationDescriptor; try { final String connectorImplementationNameInCache = buildConnectorImplementationKey(processDefinitionId, connectorId, connectorVersion); connectorImplementationDescriptor = (SConnectorImplementationDescriptor) cacheService .get(CONNECTOR_CACHE_NAME, connectorImplementationNameInCache); if (connectorImplementationDescriptor == null) { /* * Maybe connector was out of cache * We try to reload connector before throwing an exception */ loadConnectors(processDefinitionId); connectorImplementationDescriptor = (SConnectorImplementationDescriptor) cacheService.get( CONNECTOR_CACHE_NAME, connectorImplementationNameInCache); if (connectorImplementationDescriptor == null) { throw new SConnectorException("Connector implementation not found with id = " + connectorId + " and version = " + connectorVersion + " in process + " + processDefinitionId); } } } catch (final SCacheException e) { throw new SConnectorException(e); } return connectorImplementationDescriptor; } @Override public List getConnectorImplementations(long processDefinitionId, int from, int numberOfElements) throws SBonitaReadException { return processResourcesService.get(processDefinitionId, BARResourceType.CONNECTOR, from, numberOfElements); } @Override public void addConnectorImplementation(Long processDefinitionId, String name, byte[] content) throws SRecorderException { processResourcesService.add(processDefinitionId, name, BARResourceType.CONNECTOR, content); } @Override public void removeConnectorImplementations(long processDefinitionId) throws SBonitaReadException, SRecorderException { processResourcesService.removeAll(processDefinitionId, BARResourceType.CONNECTOR); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/connector/impl/SConnectorAdapter.java ================================================ /** * 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.core.connector.impl; import java.util.Map; import org.bonitasoft.engine.api.APIAccessor; import org.bonitasoft.engine.commons.NullCheckingUtil; import org.bonitasoft.engine.connector.AbstractConnector; import org.bonitasoft.engine.connector.Connector; import org.bonitasoft.engine.connector.ConnectorException; import org.bonitasoft.engine.connector.ConnectorValidationException; import org.bonitasoft.engine.connector.EngineExecutionContext; import org.bonitasoft.engine.connector.SConnector; import org.bonitasoft.engine.connector.exception.SConnectorException; import org.bonitasoft.engine.connector.exception.SConnectorValidationException; /** * Adapter to execute client connector objects in the server side * * @author Baptiste Mesta * @author Emmanuel Duchastenier */ public class SConnectorAdapter implements SConnector { private final Connector connector; public SConnectorAdapter(final Connector connector) { NullCheckingUtil.checkArgsNotNull(connector); this.connector = connector; } public Connector getConnector() { return connector; } @Override public void setInputParameters(final Map parameters) { final APIAccessor apiAccessor = (APIAccessor) parameters.remove("connectorApiAccessor"); final EngineExecutionContext executionContext = (EngineExecutionContext) parameters .remove("engineExecutionContext"); if (connector instanceof AbstractConnector) { ((AbstractConnector) connector).setAPIAccessor(apiAccessor); if (executionContext != null) { ((AbstractConnector) connector).setExecutionContext(executionContext); } } connector.setInputParameters(parameters); } @Override public void validate() throws SConnectorValidationException { try { connector.validateInputParameters(); } catch (final ConnectorValidationException e) { throw new SConnectorValidationException(e); } } @Override public Map execute() throws SConnectorException { try { return connector.execute(); } catch (final ConnectorException e) { throw new SConnectorException(e); } } @Override public void connect() throws SConnectorException { try { connector.connect(); } catch (final ConnectorException e) { throw new SConnectorException(e); } } @Override public void disconnect() throws SConnectorException { try { connector.disconnect(); } catch (final ConnectorException e) { throw new SConnectorException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/delegation/model/SDelegationRule.java ================================================ /** * Copyright (C) 2026 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.core.delegation.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * Task delegation rule defined by a user (the delegator) granting another user (the delegate) * visibility and execution rights on the delegator's human tasks during a bounded absence period. *

* Delegation is not reassignment: tasks remain assigned to the delegator, and the delegate simply gains the * ability to see and execute them through dedicated views. Original ownership and full audit trail are preserved * via the existing {@code executedBy} / {@code executedBySubstitute} fields and {@code queryable_log}. *

* A rule applies only to the processes declared in its associated {@link SDelegationRuleProcess} whitelist (matched * by process name, covering every deployed version). The rule's lifecycle status — {@code scheduled}, * {@code active}, or {@code expired} — is derived at query time from {@code startDate} and {@code endDate}; there is * no boolean activation flag, and deactivation is performed by deleting the row. *

* A user can hold at most one delegation rule at a time, enforced by a {@code UNIQUE} constraint on * {@code delegator_id}. {@code lastUpdatedBy} / {@code lastUpdatedAt} record the last administrative or self-service * modification for audit purposes. */ @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "delegation_rule") public class SDelegationRule implements PersistentObject { public static final String ID_KEY = "id"; public static final String DELEGATOR_ID_KEY = "delegatorId"; public static final String DELEGATE_ID_KEY = "delegateId"; public static final String START_DATE_KEY = "startDate"; public static final String END_DATE_KEY = "endDate"; public static final String LAST_UPDATED_BY_KEY = "lastUpdatedBy"; public static final String LAST_UPDATED_AT_KEY = "lastUpdatedAt"; @Id private long id; @Column(name = "delegator_id", nullable = false) private long delegatorId; @Column(name = "delegate_id", nullable = false) private long delegateId; @Column(name = "start_date", nullable = false) private long startDate; @Column(name = "end_date", nullable = false) private long endDate; @Column(name = "last_updated_by", nullable = false) private long lastUpdatedBy; @Column(name = "last_updated_at", nullable = false) private long lastUpdatedAt; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/delegation/model/SDelegationRuleProcess.java ================================================ /** * Copyright (C) 2026 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.core.delegation.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * Process whitelist entry attached to a {@link SDelegationRule}: declares one process (identified by its name) for * which the parent rule applies. Each rule must carry at least one entry — a delegation has no effect on a process * that is not listed. *

* Entries are unique per rule ({@code UNIQUE (delegation_rule_id, process_name)}) and are removed together with * their parent rule ({@code ON DELETE CASCADE}). *

* Process matching is performed by name rather than by definition id so that a single whitelist entry covers every * deployed version of the process and remains stable across redeployments. */ @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "delegation_rule_process") public class SDelegationRuleProcess implements PersistentObject { public static final String ID_KEY = "id"; public static final String DELEGATION_RULE_ID_KEY = "delegationRuleId"; public static final String PROCESS_NAME_KEY = "processName"; @Id private long id; @Column(name = "delegation_rule_id", nullable = false) private long delegationRuleId; @Column(name = "process_name", nullable = false) private String processName; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/DocumentCriterion.java ================================================ /** * 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.core.document; import java.io.Serializable; import java.util.Collection; /** * @author Baptiste Mesta */ public class DocumentCriterion implements Serializable { private static final long serialVersionUID = 8636952840023531275L; private final DocumentQueryBuilder builder; private final DocumentField fieldName; private Object value; private Object to; private Object from; private Collection in; /** * @param index * @param builder */ public DocumentCriterion(final DocumentField index, final DocumentQueryBuilder builder) { this.fieldName = index; this.builder = builder; } /** * @return the fieldName */ public DocumentField getField() { return fieldName; } /** * @return the value */ public Object getValue() { return value; } /** * @return the to */ public Object getTo() { return to; } /** * @return the from */ public Object getFrom() { return from; } public DocumentCriterion equalsTo(final Object value) { this.value = value; return this; } public DocumentCriterion between(final Object from, final Object to) { this.from = from; this.to = to; return this; } public DocumentCriterion in(final Collection values) { this.in = values; return this; } public DocumentQueryBuilder rightParenthesis() { builder.rightParenthesis(); return builder; } public DocumentQueryBuilder or() { builder.or(); return builder; } public DocumentQueryBuilder and() { builder.and(); return builder; } public DocumentQueryBuilder allVersion() { builder.allVersion(); return builder; } public DocumentQueryBuilder latestVersion() { builder.latestVersion(); return builder; } /** * @return */ public Collection getValues() { return in; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/DocumentField.java ================================================ /** * 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.core.document; /** * @author Baptiste Mesta, Emmanuel Duchastenier */ public enum DocumentField { ID, SERIES_ID, FILENAME, AUTHOR, CREATION_DATE, IS_EMPTY } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/DocumentQueryBuilder.java ================================================ /** * 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.core.document; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * @author Baptiste Mesta, Emmanuel Duchastenier */ public class DocumentQueryBuilder implements Serializable { private static final long serialVersionUID = 5788988731741295555L; private final List query; private boolean searchAllVersions = false; public DocumentQueryBuilder() { query = new ArrayList(); } public DocumentCriterion criterion(final DocumentField index) { DocumentCriterion criterion = new DocumentCriterion(index, this); query.add(criterion); return criterion; } public DocumentQueryBuilder leftParenthesis() { query.add("("); return this; } public DocumentQueryBuilder rightParenthesis() { query.add(")"); return this; } public DocumentQueryBuilder or() { query.add("OR"); return this; } public DocumentQueryBuilder and() { query.add("AND"); return this; } public List getQuery() { return query; } public DocumentQueryBuilder allVersion() { this.searchAllVersions = true; return this; } public DocumentQueryBuilder latestVersion() { this.searchAllVersions = false; return this; } /** * @return */ public boolean isSearchAllVersions() { return searchAllVersions; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/api/DocumentService.java ================================================ /** * 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.core.document.api; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.core.document.model.AbstractSDocumentMapping; import org.bonitasoft.engine.core.document.model.AbstractSMappedDocument; import org.bonitasoft.engine.core.document.model.SDocument; import org.bonitasoft.engine.core.document.model.SLightDocument; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.core.document.model.archive.SAMappedDocument; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; /** * @author Nicolas Chabanoles * @author Matthieu Chaffotte * @author Baptiste Mesta * @since 6.0 */ public interface DocumentService { String DOCUMENT = "DOCUMENT"; String DOCUMENTMAPPING = "DOCUMENTMAPPING"; String SUPERVISED_BY = "SupervisedBy"; /** * Save a document * * @param document the document to store * @param processInstanceId the process instance id to attach the document to * @return The document image from database * @throws SObjectCreationException when the storage has failed */ SMappedDocument attachDocumentToProcessInstance(SDocument document, long processInstanceId, String name, String description) throws SObjectCreationException; /** * Save a document * * @param document the document to store * @param processInstanceId the process instance id to attach the document to * @param index the index in the list of document * @return The document image from database * @throws SObjectCreationException when the storage has failed */ SMappedDocument attachDocumentToProcessInstance(SDocument document, long processInstanceId, String name, String description, int index) throws SObjectCreationException, SObjectAlreadyExistsException; /** * Remove this document. *

* This archives and deletes mapping to the process, i.e. the content of the document itself will be kept in * database, use {@link #deleteContentOfArchivedDocument(long)} to delete the content. *

* * @param document the document mapping to remove */ void removeCurrentVersion(AbstractSMappedDocument document) throws SObjectModificationException; /** * Remove the document with the specified process instance and name. *

* This archives and deletes mapping to the process, i.e. the content of the document itself will be kept in * database, use {@link #deleteContentOfArchivedDocument(long)} to delete the content. *

* * @param processInstanceId id of the process having the document * @param documentName name of the document */ void removeCurrentVersion(long processInstanceId, String documentName) throws SObjectNotFoundException, SObjectModificationException; /** * Get document content by document id * * @param documentId identifier of the document * @return document content */ byte[] getDocumentContent(String documentId) throws SObjectNotFoundException; /** * Get document with mapping by its mapping id * * @param mappingId identifier of the mapping of the document * @return an SDocumentMapping object with id corresponding to the parameter */ SMappedDocument getMappedDocument(long mappingId) throws SObjectNotFoundException, SBonitaReadException; /** * Get document by its id * * @param documentId identifier of document * @return an SDocumentMapping object with id corresponding to the parameter */ SLightDocument getDocument(long documentId) throws SObjectNotFoundException, SBonitaReadException; /** * Get document with mapping by its name in the specific process instance * * @param processInstanceId identifier of process instance * @param documentName name of process document * @return the corresponding SDocumentMapping object */ SMappedDocument getMappedDocument(long processInstanceId, String documentName) throws SObjectNotFoundException, SBonitaReadException; /** * Get a list of documents for specific process instance, this can be used for pagination * * @param processInstanceId identifier of process instance * @param fromIndex Index of the record to be retrieved from. First record has index 0 * @param numberPerPage Number of result we want to get. Maximum number of result returned * @return a list of SDocumentMapping objects */ List getDocumentsOfProcessInstance(long processInstanceId, int fromIndex, int numberPerPage, String field, OrderByType order) throws SBonitaReadException; /** * Get total number of documents in the specific process instance * * @param processInstanceId identifier of process instance * @return number of documents in the process instance */ long getNumberOfDocumentsOfProcessInstance(long processInstanceId) throws SBonitaReadException; /** * Get name specified document archived in a certain time in the process instance * * @param processInstanceId identifier of process instance * @param documentName name of document * @param time the archived time of document * @return an SDocumentMapping object archived in the specific time or not archived */ AbstractSMappedDocument getMappedDocument(long processInstanceId, String documentName, long time) throws SObjectNotFoundException, SBonitaReadException; /** * Get total number of document according to the query criteria * * @param queryOptions a QueryOptions object containing some query conditions * @return number of document satisfied to the query criteria */ long getNumberOfDocuments(QueryOptions queryOptions) throws SBonitaReadException; /** * Search all documents according to the query criteria * * @param queryOptions a QueryOptions object containing some query conditions * @return a list of SDocumentMapping objects */ List searchDocuments(QueryOptions queryOptions) throws SBonitaReadException; /** * Get total number of documents for the specific supervisor * * @param userId identifier of supervisor user * @param queryOptions a QueryOptions object containing some query conditions * @return number of documents for the specific supervisor */ long getNumberOfDocumentsSupervisedBy(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search all documents for the specific supervisor * * @param userId identifier of supervisor user * @param queryOptions a QueryOptions object containing some query conditions * @return a list of SDocumentMapping objects */ List searchDocumentsSupervisedBy(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Get total number of archived documents according to the query criteria * * @param queryOptions a QueryOptions object containing some query conditions * @return number of archived documents */ long getNumberOfArchivedDocuments(QueryOptions queryOptions) throws SBonitaReadException; /** * Delete the given document mapping without removing the document content. * * @param mappedDocument the document mapping to delete * @throws SObjectModificationException if an error occurred during the deletion */ void deleteMappedDocument(AbstractSMappedDocument mappedDocument) throws SObjectModificationException; /** * Search all archived documents according to the query criteria. * * @param queryOptions a QueryOptions object containing some query conditions * @return a list of SADocumentMapping objects */ List searchArchivedDocuments(QueryOptions queryOptions) throws SBonitaReadException; /** * Get total number of archived documents for the specific supervisor * * @param userId identifier of supervisor user * @param queryOptions a QueryOptions object containing some query conditions * @return number of archived documents for the specific supervisor */ long getNumberOfArchivedDocumentsSupervisedBy(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search all archived documents for the specific supervisor * * @param userId identifier of supervisor user * @param queryOptions a QueryOptions object containing some query conditions * @return a list of SADocumentMapping objects */ List searchArchivedDocumentsSupervisedBy(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Get the archived version corresponding to a document * * @param documentId identifier of process document * @return the archive of the corresponding document * @throws SObjectNotFoundException when the document does not exist */ SAMappedDocument getArchivedVersionOfProcessDocument(long documentId) throws SObjectNotFoundException; String generateDocumentURL(String name, String contentStorageId); /** * Retrieve an archived document * * @param archivedProcessDocumentId the id of the archived document * @return the corresponding archive * @throws SObjectNotFoundException when the archive does not exist */ SAMappedDocument getArchivedDocument(long archivedProcessDocumentId) throws SObjectNotFoundException; /** * Delete a document by its id. It does not delete potential mappings, which might throw an error if they are not * deleted beforehand. * * @param documentId the id of the document to delete * @throws SObjectNotFoundException if the document to delete does not exist * @throws SBonitaReadException if an error occurred while getting the document * @throws SObjectModificationException if an error occurred during the deletion */ void deleteDocument(long documentId) throws SObjectNotFoundException, SBonitaReadException, SObjectModificationException; /** * Delete the given document. It does not delete potential mappings, which might throw an error if they are not * deleted beforehand. * * @param document the document to delete * @throws SObjectModificationException if an error occurred during the deletion */ void deleteDocument(SLightDocument document) throws SObjectModificationException; /** * Delete documents and their associated mappings to a specified process instance. * * @param processInstanceId the id of the process instance to delete documents from * @throws SBonitaReadException if an error occurred while getting the documents or their mappings * @throws SObjectModificationException if an error occurred during the deletion * @throws SObjectNotFoundException if an element does not exist * @since 6.1 */ void deleteDocumentsFromProcessInstance(final Long processInstanceId) throws SBonitaReadException, SObjectModificationException, SObjectNotFoundException; /** * Delete the document content of a process instance. It only deletes the content, not the document mapping. * Note that to reach the document content, the document mapping must still exist at the time of call: indeed, this * is the only way to reach the content from the process instance. * So if you need to call this method and also delete the document mappings, ensure this method is called first. * * @param processInstanceId the process instance id to delete the document contents from */ void deleteDocumentContentsForProcessInstance(Long processInstanceId) throws SBonitaReadException, SObjectModificationException, SObjectNotFoundException; /** * delete archived documents mapping and documents links to a set of processes * * @param processInstanceId ids of the source process instances */ void deleteArchivedDocuments(List processInstanceId) throws SBonitaReadException, SRecorderException; /** * archive the specific document mapping in the archive date * * @param documentMapping document mapping will be archived * @param archiveDate the archive time * @since 6.4.0 */ void archive(AbstractSDocumentMapping documentMapping, long archiveDate) throws SObjectModificationException; /** * @param mappedDocument the document to update * @param document the new content * @param index the new index * @since 6.4.0 */ void updateDocumentOfList(final AbstractSDocumentMapping mappedDocument, final SDocument document, int index) throws SObjectModificationException; /** * update the index of a document inside the list * * @param mappedDocument the document to update * @param index the new index * @since 6.4.0 */ void updateDocumentIndex(final AbstractSDocumentMapping mappedDocument, int index) throws SObjectModificationException; /** * Get a list of document. if there is no document in the list returns an empty list * * @param documentName the name of the document list * @param processInstanceId the id of the process instance that contains the list * @param fromIndex pagination parameter * @param numberOfResult pagination parameter * @return the list of document * @since 6.4.0 */ List getDocumentList(String documentName, long processInstanceId, int fromIndex, int numberOfResult) throws SBonitaReadException; /** * @param documentToUpdate the document mapping to udpate * @param sDocument the value to set th emapping with * @return the updated document mapping */ SMappedDocument updateDocument(AbstractSDocumentMapping documentToUpdate, SDocument sDocument) throws SObjectModificationException; /** * Get a list of document at a given time. if there is no document in the list returns an empty list. *

* elements are taken from archive and from non archived mapping if the process is still running *

* * @param documentName the name of the document list * @param processInstanceId the id of the process instance that contains the list * @param time time when the list was like that * @return the list of document * @since 6.4.0 */ List getDocumentList(String documentName, long processInstanceId, long time) throws SBonitaReadException; /** * Remove the content of an archived document while keeping it's metadata. *

* After calling this method you will not be able to retrieve the content of the document since it will be erased * from the database. * This method can be useful for keeping history of a document without overloading the database. *

* * @param archivedDocumentId the id of the archived document to remove content on * @throws SObjectNotFoundException if the document to delete does not exist * @since 6.4.0 */ void deleteContentOfArchivedDocument(long archivedDocumentId) throws SObjectNotFoundException, SBonitaReadException, SRecorderException; /** * update the document having the documentId with this new version * * @param documentId the id of the document to update * @param sDocument the new version of the document @return */ SMappedDocument updateDocument(long documentId, SDocument sDocument) throws SObjectNotFoundException, SObjectModificationException, SBonitaReadException; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/api/SDocumentMappingCriterion.java ================================================ /** * 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.core.document.api; /** * @author Emmanuel Duchastenier */ public enum SDocumentMappingCriterion { NAME_DESC, NAME_ASC; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/api/impl/DocumentServiceImpl.java ================================================ /** * 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.core.document.api.impl; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.document.model.AbstractSDocumentMapping; import org.bonitasoft.engine.core.document.model.AbstractSMappedDocument; import org.bonitasoft.engine.core.document.model.SDocument; import org.bonitasoft.engine.core.document.model.SDocumentMapping; import org.bonitasoft.engine.core.document.model.SLightDocument; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.core.document.model.archive.SADocumentMapping; import org.bonitasoft.engine.core.document.model.archive.SAMappedDocument; import org.bonitasoft.engine.core.document.model.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; /** * @author Nicolas Chabanoles * @author Matthieu Chaffotte * @author Celine Souchet * @author Baptiste Mesta */ public class DocumentServiceImpl implements DocumentService { private final SDocumentDownloadURLProvider urlProvider; private final ArchiveService archiveService; private final Recorder recorder; private final ReadPersistenceService persistenceService; private final ReadPersistenceService definitiveArchiveReadPersistenceService; public DocumentServiceImpl(final Recorder recorder, final ReadPersistenceService persistenceService, final SDocumentDownloadURLProvider urlProvider, final ArchiveService archiveService) { this.recorder = recorder; this.persistenceService = persistenceService; this.urlProvider = urlProvider; this.archiveService = archiveService; definitiveArchiveReadPersistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); } @Override public SMappedDocument attachDocumentToProcessInstance(final SDocument document, final long processInstanceId, final String name, final String description) throws SObjectCreationException { try { insertDocument(document); final SDocumentMapping documentMapping = create(document.getId(), processInstanceId, name, description, -1); return new SMappedDocument(documentMapping, document); } catch (final SBonitaException e) { throw new SObjectCreationException(e.getMessage(), e); } } @Override public SMappedDocument attachDocumentToProcessInstance(final SDocument document, final long processInstanceId, final String name, final String description, final int index) throws SObjectCreationException, SObjectAlreadyExistsException { try { if (index == -1) { final SMappedDocument mappedDocumentInternal = getMappedDocumentInternal(processInstanceId, name); if (mappedDocumentInternal != null) { throw new SObjectAlreadyExistsException("A document already exists with name " + name + " and process instance id " + processInstanceId); } } insertDocument(document); final SDocumentMapping documentMapping = create(document.getId(), processInstanceId, name, description, index); return new SMappedDocument(documentMapping, document); } catch (final SObjectAlreadyExistsException e) { throw new SObjectAlreadyExistsException(e); } catch (final SBonitaException e) { throw new SObjectCreationException(e); } } @Override public void updateDocumentOfList(final AbstractSDocumentMapping mappedDocument, final SDocument document, final int index) throws SObjectModificationException { updateDocument(mappedDocument, document, index); } @Override public void updateDocumentIndex(final AbstractSDocumentMapping mappedDocument, final int index) throws SObjectModificationException { final Map params = new HashMap<>(2); params.put("index", index); updateFields(mappedDocument, params); } private void updateFields(final AbstractSDocumentMapping mappedDocument, final Map params) throws SObjectModificationException { try { recorder.recordUpdate(UpdateRecord.buildSetFields(mappedDocument, params), DOCUMENTMAPPING); } catch (final SRecorderException e) { throw new SObjectModificationException(e); } } private void updateMapping(final long documentId, final AbstractSDocumentMapping sDocumentMapping, final String description, final int index) throws SObjectModificationException { final Map params = new HashMap<>(2); params.put("documentId", documentId); params.put("description", description); params.put("version", incrementVersion(sDocumentMapping.getVersion())); params.put("index", index); updateFields(sDocumentMapping, params); } private String incrementVersion(final String version) { return String.valueOf(Integer.parseInt(version) + 1); } private void insertDocument(final SDocument document) throws SRecorderException { recorder.recordInsert(new InsertRecord(document), DOCUMENT); } @Override public void deleteDocumentsFromProcessInstance(final Long processInstanceId) throws SBonitaReadException, SObjectModificationException, SObjectNotFoundException { List mappedDocuments; do { mappedDocuments = persistenceService .selectList(SelectDescriptorBuilder.getDocumentMappingsForProcessInstance( processInstanceId, 0, 100, null, null)); for (final SMappedDocument mappedDocument : mappedDocuments) { deleteMappedDocument(mappedDocument); } } while (!mappedDocuments.isEmpty()); } @Override public void deleteDocumentContentsForProcessInstance(final Long processInstanceId) throws SBonitaReadException, SObjectModificationException, SObjectNotFoundException { List mappedDocuments; do { mappedDocuments = getDocumentsOfProcessInstance(processInstanceId, 0, 100, null, null); for (final SMappedDocument mappedDocument : mappedDocuments) { // remove the document itself deleteDocument(mappedDocument.getDocumentId()); } } while (!mappedDocuments.isEmpty()); } @Override public String generateDocumentURL(final String name, final String contentStorageId) { return urlProvider.generateURL(name, contentStorageId); } @Override public SAMappedDocument getArchivedDocument(final long archivedProcessDocumentId) throws SObjectNotFoundException { try { final SAMappedDocument docMapping = definitiveArchiveReadPersistenceService .selectById(SelectDescriptorBuilder .getArchivedDocumentById(archivedProcessDocumentId)); if (docMapping == null) { throw new SObjectNotFoundException("Document not found with identifier: " + archivedProcessDocumentId); } return docMapping; } catch (final SBonitaReadException e) { throw new SObjectNotFoundException(e); } } @Override public SAMappedDocument getArchivedVersionOfProcessDocument(final long documentId) throws SObjectNotFoundException { try { final SAMappedDocument aDocMapping = definitiveArchiveReadPersistenceService .selectOne(SelectDescriptorBuilder .getArchivedVersionOfDocument(documentId)); if (aDocMapping == null) { throw new SObjectNotFoundException(documentId); } return aDocMapping; } catch (final SBonitaReadException e) { throw new SObjectNotFoundException("Document not found with identifier: " + documentId, e); } } @Override public SLightDocument getDocument(final long documentId) throws SObjectNotFoundException, SBonitaReadException { final SLightDocument document = persistenceService .selectById(new SelectByIdDescriptor<>(SLightDocument.class, documentId)); if (document == null) { throw new SObjectNotFoundException("Document with id " + documentId + " not found"); } return document; } @Override public SMappedDocument getMappedDocument(final long processInstanceId, final String documentName) throws SObjectNotFoundException, SBonitaReadException { final SMappedDocument document = getMappedDocumentInternal(processInstanceId, documentName); if (document == null) { throw new SObjectNotFoundException( "Document not found: " + documentName + " for process instance: " + processInstanceId); } return document; } private SMappedDocument getMappedDocumentInternal(final long processInstanceId, final String documentName) throws SBonitaReadException { final Map parameters = new HashMap<>(2); parameters.put("processInstanceId", processInstanceId); parameters.put("name", documentName); final SelectOneDescriptor selectOneDescriptor = new SelectOneDescriptor<>( "getSMappedDocumentOfProcessWithName", parameters, SDocument.class); return persistenceService.selectOne(selectOneDescriptor); } @Override public AbstractSMappedDocument getMappedDocument(final long processInstanceId, final String documentName, final long time) throws SObjectNotFoundException, SBonitaReadException { final List docMapping = definitiveArchiveReadPersistenceService .selectList(SelectDescriptorBuilder .getSAMappedDocumentOfProcessWithName( processInstanceId, documentName, time)); if (docMapping.isEmpty()) { return getMappedDocument(processInstanceId, documentName); } return docMapping.get(0); } @Override public byte[] getDocumentContent(final String documentId) throws SObjectNotFoundException { try { final Long id = Long .valueOf(documentId); return getDocumentWithContent(id).getContent(); } catch (final NumberFormatException e) { throw new SObjectNotFoundException("Identifier " + documentId + " is not valid, it must be a long"); } catch (final SBonitaReadException e) { throw new SObjectNotFoundException(e); } } private SDocument getDocumentWithContent(final Long id) throws SBonitaReadException, SObjectNotFoundException { final SDocument document = persistenceService .selectById(new SelectByIdDescriptor<>(SDocument.class, id)); if (document == null) { throw new SObjectNotFoundException("Document with id " + id + " not found"); } return document; } @Override public SMappedDocument getMappedDocument(final long mappingId) throws SObjectNotFoundException, SBonitaReadException { final SMappedDocument document = persistenceService.selectById(new SelectByIdDescriptor<>( SMappedDocument.class, mappingId)); if (document == null) { throw new SObjectNotFoundException("SMappedDocument with id " + mappingId + " not found"); } return document; } @Override public List getDocumentsOfProcessInstance(final long processInstanceId, final int fromIndex, final int numberPerPage, final String field, final OrderByType order) throws SBonitaReadException { return persistenceService.selectList(SelectDescriptorBuilder.getDocumentMappingsForProcessInstance( processInstanceId, fromIndex, numberPerPage, field, order)); } @Override public long getNumberOfArchivedDocuments(final QueryOptions queryOptions) throws SBonitaReadException { return definitiveArchiveReadPersistenceService.getNumberOfEntities(SAMappedDocument.class, queryOptions, null); } @Override public long getNumberOfArchivedDocumentsSupervisedBy(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userId", userId); return definitiveArchiveReadPersistenceService.getNumberOfEntities(SAMappedDocument.class, SUPERVISED_BY, queryOptions, parameters); } @Override public long getNumberOfDocuments(final QueryOptions queryOptions) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SMappedDocument.class, queryOptions, null); } @Override public long getNumberOfDocumentsOfProcessInstance(final long processInstanceId) throws SBonitaReadException { return persistenceService .selectOne(SelectDescriptorBuilder.getNumberOfSMappedDocumentOfProcess(processInstanceId)); } @Override public long getNumberOfDocumentsSupervisedBy(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userId", userId); return persistenceService.getNumberOfEntities(SMappedDocument.class, SUPERVISED_BY, queryOptions, parameters); } @Override public void removeCurrentVersion(final AbstractSMappedDocument document) throws SObjectModificationException { archive(document, System.currentTimeMillis()); deleteMappedDocument(document); } @Override public void removeCurrentVersion(final long processInstanceId, final String documentName) throws SObjectNotFoundException, SObjectModificationException { try { removeCurrentVersion(getMappedDocument(processInstanceId, documentName)); } catch (final SBonitaReadException e) { throw new SObjectModificationException(e); } } @Override public void deleteDocument(long documentId) throws SObjectNotFoundException, SBonitaReadException, SObjectModificationException { deleteDocument(getDocument(documentId)); } @Override public void deleteDocument(final SLightDocument document) throws SObjectModificationException { try { recorder.recordDelete(new DeleteRecord(document), "SDocument"); } catch (final SRecorderException e) { throw new SObjectModificationException(e); } } @Override public void deleteMappedDocument(final AbstractSMappedDocument mappedDocument) throws SObjectModificationException { try { recorder.recordDelete(new DeleteRecord(mappedDocument), "SDocumentMapping"); } catch (final SBonitaException e) { throw new SObjectModificationException(e.getMessage(), e); } } @Override public List searchArchivedDocuments(final QueryOptions queryOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService1 = archiveService.getDefinitiveArchiveReadPersistenceService(); return persistenceService1.searchEntity(SAMappedDocument.class, queryOptions, null); } @Override public List searchArchivedDocumentsSupervisedBy(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService1 = archiveService.getDefinitiveArchiveReadPersistenceService(); final Map parameters = Collections.singletonMap("userId", userId); return persistenceService1.searchEntity(SAMappedDocument.class, SUPERVISED_BY, queryOptions, parameters); } @Override public List searchDocuments(final QueryOptions queryOptions) throws SBonitaReadException { try { return persistenceService.searchEntity(SMappedDocument.class, queryOptions, null); } catch (final SBonitaReadException e) { throw new SBonitaReadException(e); } } @Override public List searchDocumentsSupervisedBy(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { try { final Map parameters = Collections.singletonMap("userId", userId); return persistenceService.searchEntity(SMappedDocument.class, SUPERVISED_BY, queryOptions, parameters); } catch (final SBonitaReadException e) { throw new SBonitaReadException(e); } } @Override public void deleteArchivedDocuments(List processInstanceIds) throws SBonitaReadException, SRecorderException { List archivedMappedDocuments = persistenceService .selectList(new SelectListDescriptor<>("getArchiveMappingsOfProcessInstances", Collections.singletonMap("processInstanceIds", processInstanceIds), SAMappedDocument.class, QueryOptions.countQueryOptions())); if (archivedMappedDocuments.isEmpty()) { //no documents to delete return; } List documentIds = new ArrayList<>(); List documentMappingIds = new ArrayList<>(); for (SAMappedDocument mappedDocument : archivedMappedDocuments) { documentIds.add(mappedDocument.getDocumentId()); documentMappingIds.add(mappedDocument.getId()); } archiveService.deleteFromQuery("deleteArchiveDocumentsByIds", Collections.singletonMap("ids", documentIds)); archiveService.deleteFromQuery("deleteArchiveMappingsByIds", Collections.singletonMap("ids", documentMappingIds)); } private SDocumentMapping create(final long documentId, final long processInstanceId, final String name, final String description, final int index) throws SRecorderException { final SDocumentMapping documentMapping = new SDocumentMapping(documentId, processInstanceId, name); documentMapping.setDescription(description); documentMapping.setVersion("1"); documentMapping.setIndex(index); recorder.recordInsert(new InsertRecord(documentMapping), DOCUMENTMAPPING); return documentMapping; } @Override public void archive(final AbstractSDocumentMapping docMapping, final long archiveDate) throws SObjectModificationException { if (archiveService.isArchivable(SDocumentMapping.class)) { final SADocumentMapping saDocumentMapping = new SADocumentMapping(docMapping.getDocumentId(), docMapping.getProcessInstanceId(), archiveDate, docMapping.getId(), docMapping.getName(), docMapping.getDescription(), docMapping.getVersion()); saDocumentMapping.setIndex(docMapping.getIndex()); final ArchiveInsertRecord insertRecord = new ArchiveInsertRecord(saDocumentMapping); try { archiveService.recordInsert(archiveDate, insertRecord); } catch (final SBonitaException e) { throw new SObjectModificationException( "Unable to archive the document with id = <" + docMapping.getId() + ">", e); } } } @Override public List getDocumentList(final String documentName, final long processInstanceId, final int fromIndex, final int numberOfResult) throws SBonitaReadException { return persistenceService.selectList( SelectDescriptorBuilder.getDocumentList(documentName, processInstanceId, new QueryOptions(fromIndex, numberOfResult))); } @Override public void deleteContentOfArchivedDocument(final long archivedDocumentId) throws SObjectNotFoundException, SBonitaReadException, SRecorderException { final SAMappedDocument archivedDocument = getArchivedDocument(archivedDocumentId); final SDocument document = getDocumentWithContent(archivedDocument.getDocumentId()); final HashMap updateFields = new HashMap<>(); updateFields.put("content", null); updateFields.put("hasContent", false); recorder.recordUpdate(UpdateRecord.buildSetFields(document, updateFields), DOCUMENT); } @Override public SMappedDocument updateDocument(final long documentId, final SDocument sDocument) throws SBonitaReadException, SObjectNotFoundException, SObjectModificationException { final AbstractSDocumentMapping sDocumentMapping = getMappedDocument(documentId); return updateDocument(sDocumentMapping, sDocument); } @Override public SMappedDocument updateDocument(final AbstractSDocumentMapping documentToUpdate, final SDocument sDocument) throws SObjectModificationException { return updateDocument(documentToUpdate, sDocument, documentToUpdate.getIndex()); } private SMappedDocument updateDocument(final AbstractSDocumentMapping documentToUpdate, final SDocument sDocument, final int index) throws SObjectModificationException { //insert new document try { insertDocument(sDocument); } catch (final SRecorderException e) { throw new SObjectModificationException(e); } //update mapping archive(documentToUpdate, System.currentTimeMillis()); updateMapping(sDocument.getId(), documentToUpdate, documentToUpdate.getDescription(), index); return new SMappedDocument(documentToUpdate, sDocument); } @Override public List getDocumentList(final String documentName, final long processInstanceId, final long time) throws SBonitaReadException { final List archivedList = persistenceService .selectList(SelectDescriptorBuilder.getArchivedDocumentList(documentName, processInstanceId, new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS), time)); final List elementsInJournal = persistenceService .selectList(SelectDescriptorBuilder.getDocumentListCreatedBefore(documentName, processInstanceId, new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS), time)); final List result = new ArrayList<>( archivedList.size() + elementsInJournal.size()); result.addAll(archivedList); result.addAll(elementsInJournal); return result; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/api/impl/SDocumentDownloadURLProvider.java ================================================ /** * 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.core.document.api.impl; /** * @author Nicolas Chabanoles * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SDocumentDownloadURLProvider { String generateURL(String documentName, String contentStorageId); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/api/impl/SDocumentDownloadURLProviderImpl.java ================================================ /** * 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.core.document.api.impl; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; /** * @author Nicolas Chabanoles */ public class SDocumentDownloadURLProviderImpl implements SDocumentDownloadURLProvider { private final String servletUrl; public SDocumentDownloadURLProviderImpl(final String servletUrl) { this.servletUrl = servletUrl; } @Override public String generateURL(String documentName, String contentStorageId) { final StringBuffer buffer = new StringBuffer(servletUrl); buffer.append("?fileName=").append(getUrlEncodedValue(documentName)); buffer.append("&contentStorageId=").append(contentStorageId); return buffer.toString(); } private String getUrlEncodedValue(String documentName) { String encoding = "UTF-8"; try { return URLEncoder.encode(documentName, encoding); } catch (UnsupportedEncodingException e) { // This exception should never occur, // as UnsupportedEncodingException is thrown only if the named encoding is not supported throw new IllegalStateException("the named encoding " + encoding + " is not supported.", e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/AbstractSDocument.java ================================================ /** * 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.core.document.model; import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Emmanuel Duchastenier */ @NoArgsConstructor @AllArgsConstructor @Data @SuperBuilder @MappedSuperclass public class AbstractSDocument implements PersistentObject { @Id private long id; private long author; private String url; @Column(name = "creationdate") private long creationDate; @Column(name = "hascontent") private boolean hasContent; @Column(name = "filename") private String fileName; @Column(name = "mimeType") private String mimeType; public boolean hasContent() { return hasContent; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/AbstractSDocumentMapping.java ================================================ /** * 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.core.document.model; import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.persistence.PersistentObject; /** * Mapping for a document *

* can be part of a list of document, in that case all documents have the same name and an index * If it's standalone documents index = -1 * * @author Baptiste Mesta */ @Data @NoArgsConstructor @AllArgsConstructor @SuperBuilder @MappedSuperclass public class AbstractSDocumentMapping implements PersistentObject { @Id private long id; @Column(name = "processinstanceid") private long processInstanceId; @Column(name = "documentid") private long documentId; private String name; private String description; private String version; @Column(name = "index_") private int index; protected AbstractSDocumentMapping(long documentId, long processInstanceId, String name) { this.documentId = documentId; this.processInstanceId = processInstanceId; this.name = name; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/AbstractSMappedDocument.java ================================================ /** * 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.core.document.model; import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.MappedSuperclass; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @AllArgsConstructor @SuperBuilder @EqualsAndHashCode(callSuper = true) @MappedSuperclass public abstract class AbstractSMappedDocument extends AbstractSDocumentMapping { @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "documentid", referencedColumnName = "id", insertable = false, updatable = false) protected SLightDocument document; public AbstractSMappedDocument(AbstractSDocumentMapping documentMapping, SDocument document) { this.setId(documentMapping.getId()); this.setName(documentMapping.getName()); this.setDescription(documentMapping.getDescription()); this.setVersion(documentMapping.getVersion()); this.setDocumentId(documentMapping.getDocumentId()); this.setProcessInstanceId(documentMapping.getProcessInstanceId()); this.setIndex(documentMapping.getIndex()); this.document = SLightDocument.builder() .id(document.getId()) .fileName(document.getFileName()) .hasContent(document.hasContent()) .mimeType(document.getMimeType()) .author(document.getAuthor()) .url(document.getUrl()) .creationDate(document.getCreationDate()) .build(); } public AbstractSDocument getDocument() { return document; } public void setDocument(SLightDocument document) { this.document = document; } public long getAuthor() { return document.getAuthor(); } public long getCreationDate() { return document.getCreationDate(); } public String getMimeType() { return document.getMimeType(); } public String getFileName() { return document.getFileName(); } public boolean hasContent() { return document.hasContent(); } public String getUrl() { return document.getUrl(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/SDocument.java ================================================ /** * 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.core.document.model; import javax.persistence.Cacheable; import javax.persistence.Entity; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import lombok.experimental.SuperBuilder; import org.hibernate.annotations.Type; @Data @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = true, exclude = "content") @ToString(exclude = { "content" }) @SuperBuilder @Entity @Table(name = "document") @Cacheable(false) public class SDocument extends AbstractSDocument { public static final String ID = "id"; public static final String NAME = "name"; public static final String AUTHOR = "author"; public static final String CREATION_DATE = "creationDate"; public static final String HAS_CONTENT = "hasContent"; public static final String FILENAME = "fileName"; public static final String MIMETYPE = "mimeType"; public static final String URL = "url"; public static final String VERSION = "version"; public static final String DESCRIPTION = "description"; public static final String INDEX = "index"; @Type(type = "materialized_blob") private byte[] content; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/SDocumentMapping.java ================================================ /** * 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.core.document.model; import javax.persistence.Cacheable; import javax.persistence.Entity; import javax.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; /** * Mapping for a document *

* can be part of a list of document, in that case all documents have the same name and an index * If it's standalone documents index = -1 * * @author Baptiste Mesta */ @Data @NoArgsConstructor @SuperBuilder @EqualsAndHashCode(callSuper = true) @Entity @Table(name = "document_mapping") @Cacheable(false) public class SDocumentMapping extends AbstractSDocumentMapping { public SDocumentMapping(long documentId, long processInstanceId, String name) { super(documentId, processInstanceId, name); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/SLightDocument.java ================================================ /** * 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.core.document.model; import javax.persistence.Cacheable; import javax.persistence.Entity; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import lombok.experimental.SuperBuilder; /** * @author Emmanuel Duchastenier */ @AllArgsConstructor @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @SuperBuilder @Entity @Table(name = "document") @Cacheable(false) public class SLightDocument extends AbstractSDocument { } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/SMappedDocument.java ================================================ /** * 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.core.document.model; import javax.persistence.Cacheable; import javax.persistence.Entity; import javax.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @SuperBuilder @EqualsAndHashCode(callSuper = true) @Entity @Table(name = "document_mapping") @Cacheable(false) public class SMappedDocument extends AbstractSMappedDocument { public SMappedDocument(AbstractSDocumentMapping documentMapping, SDocument document) { this.setId(documentMapping.getId()); this.setName(documentMapping.getName()); this.setDescription(documentMapping.getDescription()); this.setVersion(documentMapping.getVersion()); this.setDocumentId(documentMapping.getDocumentId()); this.setProcessInstanceId(documentMapping.getProcessInstanceId()); this.setIndex(documentMapping.getIndex()); this.document = SLightDocument.builder() .id(document.getId()) .fileName(document.getFileName()) .hasContent(document.hasContent()) .mimeType(document.getMimeType()) .author(document.getAuthor()) .url(document.getUrl()) .creationDate(document.getCreationDate()) .build(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/archive/SADocumentMapping.java ================================================ /** * 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.core.document.model.archive; import javax.persistence.Cacheable; import javax.persistence.Entity; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.core.document.model.AbstractSDocumentMapping; import org.bonitasoft.engine.core.document.model.SDocumentMapping; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @AllArgsConstructor @SuperBuilder @EqualsAndHashCode(callSuper = true) @Entity @Table(name = "arch_document_mapping") @Cacheable(false) public class SADocumentMapping extends AbstractSDocumentMapping implements ArchivedPersistentObject { public static final String ID = "id"; public static final String PROCESS_INSTANCE_ID = "processInstanceId"; public static final String ARCHIVE_DATE = "archiveDate"; public static final String SOURCE_OBJECT_ID = "sourceObjectId"; public static final String DOCUMENT_ID = "documentId"; public static final String URL = "url"; public static final String NAME = "name"; public static final String HAS_CONTENT = "hasContent"; public static final String AUTHOR = "author"; public static final String FILE_NAME = "fileName"; public static final String MIME_TYPE = "mimeType"; public static final String CREATION_DATE = "creationDate"; public static final String DESCRIPTION = "description"; public static final String VERSION = "version"; public static final String INDEX = "index"; private long archiveDate; private long sourceObjectId; public SADocumentMapping(final long documentId, final long processInstanceId, final long archiveDate, final long sourceObjectId, final String name, final String description, final String version) { super(documentId, processInstanceId, name); setDescription(description); setVersion(version); this.archiveDate = archiveDate; this.sourceObjectId = sourceObjectId; } @Override public Class getPersistentObjectInterface() { return SDocumentMapping.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/archive/SAMappedDocument.java ================================================ /** * 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.core.document.model.archive; import javax.persistence.Cacheable; import javax.persistence.Entity; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.core.document.model.AbstractSMappedDocument; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @AllArgsConstructor @SuperBuilder @EqualsAndHashCode(callSuper = true) @Entity @Table(name = "arch_document_mapping") @Cacheable(false) public class SAMappedDocument extends AbstractSMappedDocument implements ArchivedPersistentObject { private long archiveDate; private long sourceObjectId; @Override public Class getPersistentObjectInterface() { return SMappedDocument.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/builder/SDocumentBuilder.java ================================================ /** * 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.core.document.model.builder; import org.bonitasoft.engine.core.document.model.SDocument; /** * @author Baptiste Mesta */ public class SDocumentBuilder { private final SDocument entity; public SDocumentBuilder() { entity = new SDocument(); } public SDocumentBuilder setAuthor(final long author) { entity.setAuthor(author); return this; } public SDocumentBuilder setCreationDate(final long creationDate) { entity.setCreationDate(creationDate); return this; } public SDocumentBuilder setHasContent(final boolean hasContent) { entity.setHasContent(hasContent); return this; } public SDocumentBuilder setFileName(final String fileName) { entity.setFileName(fileName); return this; } public SDocumentBuilder setMimeType(final String mimeType) { entity.setMimeType(mimeType); return this; } public SDocumentBuilder setContent(final byte[] content) { entity.setContent(content); return this; } public SDocumentBuilder setURL(final String url) { entity.setUrl(url); return this; } public SDocument done() { return entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/builder/SDocumentBuilderFactory.java ================================================ /** * 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.core.document.model.builder; /** * @author Nicolas Chabanoles * @author Zhang Bole * @author Baptiste Mesta * @author Celine Souchet */ public class SDocumentBuilderFactory { public SDocumentBuilder createNewInstance(final String fileName, final String mimetype, final long authorId) { final SDocumentBuilder sDocumentBuilder = new SDocumentBuilder(); sDocumentBuilder.setFileName(fileName); sDocumentBuilder.setMimeType(mimetype); sDocumentBuilder.setAuthor(authorId); sDocumentBuilder.setCreationDate(System.currentTimeMillis()); return sDocumentBuilder; } public SDocumentBuilder createNewProcessDocument(final String fileName, final String mimetype, final long authorId, final byte[] content) { if (fileName == null || fileName.isEmpty()) { throw new IllegalArgumentException("The fileName must be filled for a document with content"); } final SDocumentBuilder sDocumentBuilder = createNewInstance(fileName, mimetype, authorId); sDocumentBuilder.setContent(content); sDocumentBuilder.setHasContent(true); return sDocumentBuilder; } public SDocumentBuilder createNewExternalProcessDocumentReference(final String fileName, final String mimetype, final long authorId, final String url) { final SDocumentBuilder sDocumentBuilder = createNewInstance(fileName, mimetype, authorId); sDocumentBuilder.setURL(url); sDocumentBuilder.setHasContent(false); return sDocumentBuilder; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/builder/SDocumentMappingBuilder.java ================================================ /** * 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.core.document.model.builder; import org.bonitasoft.engine.core.document.model.SDocumentMapping; /** * @author Baptiste Mesta */ public interface SDocumentMappingBuilder { SDocumentBuilder setProcessInstanceId(final long processInstanceId); SDocumentMapping done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/builder/SDocumentMappingBuilderFactory.java ================================================ /** * 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.core.document.model.builder; /** * @author Zhao Na * @author Baptiste Mesta */ public interface SDocumentMappingBuilderFactory { SDocumentMappingBuilder createNewInstance(); String getIdKey(); String getDocumentIdKey(); String getProcessInstanceIdKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/builder/SDocumentUpdateBuilder.java ================================================ /** * 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.core.document.model.builder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Nicolas Chabanoles */ public interface SDocumentUpdateBuilder { SDocumentUpdateBuilder setDocumentName(String documentName); SDocumentUpdateBuilder setDocumentAuthor(long author); SDocumentUpdateBuilder setDocumentCreationDate(long creationDate); SDocumentUpdateBuilder setHasContent(boolean hasContent); SDocumentUpdateBuilder setDocumentContentFileName(String contentFileName); SDocumentUpdateBuilder setDocumentContentMimeType(String contentMimeType); SDocumentUpdateBuilder setDocumentStorageId(final String documentId); SDocumentUpdateBuilder setDocumentURL(String generateURL); EntityUpdateDescriptor done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/builder/SDocumentUpdateBuilderFactory.java ================================================ /** * 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.core.document.model.builder; /** * @author Nicolas Chabanoles * @author Baptiste Mesta */ public interface SDocumentUpdateBuilderFactory { SDocumentUpdateBuilder createNewInstance(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/document/model/recorder/SelectDescriptorBuilder.java ================================================ /** * 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.core.document.model.recorder; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.core.document.model.SDocument; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.core.document.model.archive.SAMappedDocument; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; /** * @author Emmanuel Duchastenier * @author Nicolas Chabanoles * @author Baptiste Mesta */ public class SelectDescriptorBuilder { public static SelectListDescriptor getDocumentMappingsForProcessInstance( final long processInstanceId, final int fromIndex, final int maxResults, final String sortFieldOrder, final OrderByType orderBy) { QueryOptions queryOptions; String queryName = "getSMappedDocumentOfProcess"; if (sortFieldOrder == null) { queryOptions = new QueryOptions(fromIndex, maxResults); queryName = "getSMappedDocumentOfProcessOrderedById"; } else { queryOptions = new QueryOptions(fromIndex, maxResults, SMappedDocument.class, sortFieldOrder, orderBy); } final Map parameters = new HashMap<>(1); parameters.put("processInstanceId", processInstanceId); return new SelectListDescriptor<>(queryName, parameters, SMappedDocument.class, queryOptions); } public static SelectOneDescriptor getSMappedDocumentOfProcessWithName(final long processInstanceId, final String documentName) { final Map parameters = new HashMap<>(2); parameters.put("processInstanceId", processInstanceId); parameters.put("name", documentName); return new SelectOneDescriptor<>("getSMappedDocumentOfProcessWithName", parameters, SMappedDocument.class); } public static SelectOneDescriptor getNumberOfSMappedDocumentOfProcess(final long processInstanceId) { final Map parameters = new HashMap<>(1); parameters.put("processInstanceId", processInstanceId); return new SelectOneDescriptor<>("getNumberOfSMappedDocumentOfProcess", parameters, SDocument.class); } public static SelectListDescriptor getSAMappedDocumentOfProcessWithName( final long processInstanceId, final String documentName, final long time) { final Map parameters = new HashMap<>(3); parameters.put("processInstanceId", processInstanceId); parameters.put("name", documentName); parameters.put("time", time); return new SelectListDescriptor<>("getSAMappedDocumentOfProcessWithName", parameters, SAMappedDocument.class, new QueryOptions(0, 1)); } public static SelectByIdDescriptor getArchivedDocumentById(final long documentId) { return new SelectByIdDescriptor<>(SAMappedDocument.class, documentId); } public static SelectOneDescriptor getArchivedVersionOfDocument(final long documentId) { final Map parameters = new HashMap<>(1); parameters.put("sourceObjectId", documentId); return new SelectOneDescriptor<>("getArchivedVersionOfDocument", parameters, SAMappedDocument.class); } public static SelectListDescriptor getDocumentList(String name, long processInstanceId, QueryOptions queryOptions) { final Map parameters = new HashMap<>(3); parameters.put("processInstanceId", processInstanceId); parameters.put("name", name); return new SelectListDescriptor<>("getDocumentList", parameters, SMappedDocument.class, queryOptions); } public static SelectListDescriptor getArchivedDocumentList(String name, long processInstanceId, QueryOptions queryOptions, long time) { final Map parameters = new HashMap<>(3); parameters.put("processInstanceId", processInstanceId); parameters.put("name", name); parameters.put("time", time); return new SelectListDescriptor<>("getArchivedDocumentList", parameters, SAMappedDocument.class, queryOptions); } public static SelectListDescriptor getDocumentListCreatedBefore(String name, long processInstanceId, QueryOptions queryOptions, long time) { final Map parameters = new HashMap<>(3); parameters.put("processInstanceId", processInstanceId); parameters.put("name", name); parameters.put("time", time); return new SelectListDescriptor<>("getDocumentCreatedBeforeList", parameters, SMappedDocument.class, queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/ActivityInstanceService.java ================================================ /** * 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.core.process.instance.api; import java.util.List; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.SHumanTaskAlreadyAssignedException; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping; import org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Hongwen Zang * @author Yanyan Liu * @author Baptiste Mesta * @author Celine Souchet * @since 6.0 */ public interface ActivityInstanceService extends FlowNodeInstanceService { String ACTIVITYINSTANCE = "ACTIVITYINSTANCE"; String PENDINGACTIVITYMAPPING = "PENDINGACTIVITYMAPPING"; /** * Create activityInstance in DB according to the given activityInstance object * * @param activityInstance * an SActivityInstance object * @throws SActivityCreationException */ void createActivityInstance(SActivityInstance activityInstance) throws SActivityCreationException; /** * Create a new pending activity mapping in DB * * @param mapping * pending activity mapping object * @throws SActivityCreationException */ void addPendingActivityMappings(SPendingActivityMapping mapping) throws SActivityCreationException; /** * deletePendingMappings * * @param mapping * pending activity mapping object * @throws SActivityModificationException */ void deletePendingMappings(long humanTaskInstanceId) throws SActivityModificationException; /** * Delete all pending mappings for the connected tenant * * @throws SActivityModificationException * @since 6.1 */ void deleteAllPendingMappings() throws SActivityModificationException; /** * Get activityInstance by its id * * @param activityInstanceId * identifier of activityInstance * @return an SActivityInstance object with id corresponding to the parameter * @throws SActivityInstanceNotFoundException * if no activityInstance found * @throws SActivityReadException */ SActivityInstance getActivityInstance(long activityInstanceId) throws SActivityInstanceNotFoundException, SActivityReadException; /** * Get humanTaskInstance by its id * * @param activityInstanceId * identifier of humanTaskInstance * @return an SHumanTaskInstance object with id corresponding to the parameter * @throws SActivityInstanceNotFoundException * @throws SActivityReadException */ SHumanTaskInstance getHumanTaskInstance(long activityInstanceId) throws SActivityInstanceNotFoundException, SActivityReadException; /** * Get activities with specific states in the root container in specific order, this is used for pagination * * @param rootContainerId * identifier of root container, it always is process definition id * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param maxResults * Number of result we want to get. Maximum number of result returned * @param sortingField * the field used to do order * @param sortingOrder * ASC or DESC * @param stateIds * Identifiers of states * @return a list of SActivityInstance objects * @throws SActivityReadException */ List getActivitiesWithStates(long rootContainerId, Set stateIds, int fromIndex, int maxResults, String sortingField, OrderByType sortingOrder) throws SActivityReadException; /** * Get the most recent archived version of a specified activity instance * * @param activityInstanceId * identifier of activity instance * @return an SAActivityInstance object * @throws SActivityReadException * if a Read error occurs * @throws SActivityInstanceNotFoundException * it the provided activityInstanceId does not refer to an existing Activity Instance */ SAActivityInstance getMostRecentArchivedActivityInstance(long activityInstanceId) throws SActivityReadException, SActivityInstanceNotFoundException; /** * Get pending tasks for the user in specific actors. This is used for pagination * * @param userId * identifier of user * @param actorIds * identifiers of actor * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param maxResults * Number of result we want to get. Maximum number of result returned * @param sortFieldName * the field used to do order * @param order * ASC or DESC * @return a list of SActivityInstance objects * @throws SActivityReadException */ List getPendingTasks(long userId, Set actorIds, int fromIndex, int maxResults, String sortFieldName, OrderByType order) throws SActivityReadException; /** * Get tasks assigned to the user. This is used for pagination * * @param userId * identifier of user * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param maxResults * Number of result we want to get. Maximum number of result returned * @param sortFieldName * the field used to do order * @param order * ASC or DESC * @return a list of SHumanTaskInstance objects * @throws SActivityReadException */ List getAssignedUserTasks(long userId, int fromIndex, int maxResults, String sortFieldName, OrderByType order) throws SActivityReadException; /** * Get archived activity instances in the specific root container. * * @param rootContainerId * identifier of root container, the root container can be process instance * @param queryOptions * a map of specific parameters of a query * @return a list of SAActivityInstance objects * @throws SActivityReadException */ List getArchivedActivityInstances(long rootContainerId, QueryOptions queryOptions) throws SActivityReadException; /** * Get total number of open activity instances for the specific process instance * * @param processInstanceId * identifier of process instance * @return the number of opened activity instances in the specific process instance * @throws SActivityReadException */ int getNumberOfOpenActivityInstances(long processInstanceId) throws SActivityReadException; /** * Get all open activity instances in the specific process instance. This is used for pagination * * @param rootContainerId * identifier of root container, the root container can be process instance * @param pageIndex * the page index to indicate which page will be retrieved. First page has index 0 * @param maxResults * Number of result we want to get. Maximum number of result returned * @param sortingField * the field used to do order * @param orderbyType * ASC or DESC * @return a list of SActivityInstance objects * @throws SActivityReadException */ List getOpenActivityInstances(long rootContainerId, int pageIndex, int maxResults, String sortingField, OrderByType orderbyType) throws SActivityReadException; /** * Get all activity instances for the specific process instance * * @param rootContainerId * identifier of root container, the root container can be process instance * @return a list of SActivityInstance objects * @throws SActivityReadException */ List getActivityInstances(long rootContainerId, int fromIndex, int numberOfResults) throws SActivityReadException; /** * Get all child instances for the specific parent activity instance, order by id ascending. * * @param parentActivityInstanceId * identifier of parent activity instance * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfResults * TODO * @return a list of SActivityInstance objects * @throws SActivityReadException */ List getChildrenOfAnActivity(long parentActivityInstanceId, int fromIndex, int numberOfResults) throws SActivityReadException; /** * Assign the specific human task to the user * * @param userTaskId * identifier of human task instance * @param userId * identifier of user * @throws SFlowNodeNotFoundException * @throws SFlowNodeReadException * @throws SActivityModificationException */ void assignHumanTask(long userTaskId, long userId) throws SFlowNodeNotFoundException, SFlowNodeReadException, SActivityModificationException; /** * Assign the specific human task to the user if it is not currently assigned *

Use a more restrictive mechanism than assignHumanTask method to assign * human task: *

    *
  • exception when task is already assign to a different user
  • *
  • only update claimed date when assign to same user
  • *
  • remove claimed date when assign to user with id 0 (un-assign)
  • *
*

*

*

under high load, getting pending tasks could return tasks that are being assign in a previous transaction in a * separate thread, and thus assignee is override

* * @since 7.6 * @param userTaskId * identifier of human task instance * @param userId * identifier of user * @throws SFlowNodeNotFoundException * @throws SFlowNodeReadException * @throws SActivityModificationException * @throws SHumanTaskAlreadyAssignedException */ void assignHumanTaskIfNotAssigned(long userTaskId, long userId) throws SFlowNodeNotFoundException, SFlowNodeReadException, SActivityModificationException, SHumanTaskAlreadyAssignedException; /** * Get the number of UserTask instances assigned to a specific user * * @param userId * the id of the user concerned * @return the number of UserTask instances assigned to this specific user * @throws SActivityReadException * if a Read exception occurs */ long getNumberOfAssignedHumanTaskInstances(long userId) throws SActivityReadException; /** * Search UserTask instances assigned for a specific supervisor * * @param parameters * a map of specific parameters of a query * @param parameters * a map of specific parameters of a query * @return the number of UserTask assigned to this specific supervisor * @throws SActivityReadException * if a Read exception occurs */ long getNumberOfAssignedTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions) throws SBonitaReadException; /** * Search AUserTask instances archived for a specific supervisor * * @param queryOptions * the object used to manage all the search parameters of a query * @param parameters * a map of specific parameters of a query * @return the number of UserTask archived to this specific supervisor * @throws SActivityReadException * if a Read exception occurs */ long getNumberOfArchivedHumanTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions) throws SBonitaReadException; /** * Search UserTask instances assigned for a specific supervisor * * @param queryOptions * the object used to manage all the search parameters of a query * @param parameters * a map of specific parameters of a query * @return the UserTask instances list assigned to this specific supervisor * @throws SActivityReadException * if a Read exception occurs */ List searchAssignedTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions) throws SBonitaReadException; /** * Search AUserTask instances archived for a specific supervisor * * @param queryOptions * the object used to manage all the search parameters of a query * @param parameters * a map of specific parameters of a query * @return the UserTask instances list archived to this specific supervisor * @throws SActivityReadException * if a Read exception occurs */ List searchArchivedHumanTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions) throws SBonitaReadException; /** * Gets the archive instance of the activity according to its identifier at a given state. * * @param activityId * the activity identifier * @param stateId * the state identifier * @param persistenceService * @return * @throws SActivityReadException * if a Read exception occurs * @throws SActivityInstanceNotFoundException */ SAActivityInstance getArchivedActivityInstance(long activityInstanceId, int stateId) throws SActivityReadException, SActivityInstanceNotFoundException; /** * Search archived human tasks according to specific search criteria * * @param searchOptions * the object used to manage all the search parameters of a query * @param persistenceService * used to retrieve the archived tasks * @return a list of SAHumanTaskInstance objects * @throws SBonitaReadException */ List searchArchivedTasks(QueryOptions searchOptions) throws SBonitaReadException; /** * Get total number of archived tasks according to specific search criteria * * @param searchOptions * the object used to manage all the search parameters of a query * @param persistenceService * used to retrieve the archived tasks * @return * @throws SBonitaReadException */ long getNumberOfArchivedTasks(QueryOptions searchOptions) throws SBonitaReadException; /** * Get total number of assigned tasks managed by the specific manager * * @param managerUserId * identifier of manager user * @param searchOptions * the object used to manage all the search parameters of a query * @return number of assigned tasks managed by the specific manager * @throws SBonitaReadException */ long getNumberOfAssignedTasksManagedBy(long managerUserId, QueryOptions searchOptions) throws SBonitaReadException; /** * Get all assigned tasks managed by the specific manager * * @param managerUserId * identifier of manager user * @param searchOptions * the object used to manage all the search parameters of a query * @return a list of SHumanTaskInstance objects */ List searchAssignedTasksManagedBy(long managerUserId, QueryOptions searchOptions) throws SBonitaReadException; /** * get the total number of archived tasks assigned to subordinates of specified manager. * * @param managerUserId * the userId of the manager * @param searchOptions * the search options to paginate, filter, ... * @return the number of elements encountered * @throws SBonitaReadException * in case a search error occurs */ long getNumberOfArchivedTasksManagedBy(long managerUserId, QueryOptions searchOptions) throws SBonitaReadException; /** * get the archived tasks assigned to subordinates of specified manager, limited to, sorted, paginated with the * specifies QueryOptions * * @param managerUserId * the userId of the manager * @param searchOptions * the search options to paginate, filter, sort ... * @return the elements encountered matching the specified options * @throws SBonitaReadException * in case a search error occurs */ List searchArchivedTasksManagedBy(long managerUserId, QueryOptions searchOptions) throws SBonitaReadException; /** * Search all pending human task instances for the specific supervisor * * @param userId * identifier of supervisor user * @param searchOptions * the search options to paginate, filter, sort ... * @return a list of SHumanTaskInstance objects * @throws SBonitaReadException */ List searchPendingTasksSupervisedBy(long userId, QueryOptions searchOptions) throws SBonitaReadException; /** * Get total number of pending human task instances for the specific supervisor * * @param userId * identifier of supervisor user * @param queryOptions * the search options to paginate, filter, sort ... * @return number of pending human task instances for the specific supervisor * @throws SBonitaReadException */ long getNumberOfPendingTasksSupervisedBy(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Get number of human task instances according to the criteria * * @param queryOptions * the search options to paginate, filter, sort ... * @return number of human task instances satisfied to the criteria * @throws SBonitaReadException */ long getNumberOfHumanTasks(QueryOptions queryOptions) throws SBonitaReadException; /** * Search all human task instances according to the criteria * * @param queryOptions * the search options to paginate, filter, sort ... * @return a list of SHumanTaskInstance objects * @throws SBonitaReadException */ List searchHumanTasks(QueryOptions queryOptions) throws SBonitaReadException; /** * Get number of open tasks for each user * * @param userIds * identifiers of users * @return a map containing user id and corresponding task number * @throws SBonitaReadException */ Map getNumberOfOpenTasksForUsers(List userIds) throws SBonitaReadException; /** * Search total number of pending tasks for the specific manager * * @param managerUserId * identifier of manager user * @param searchOptions * the search options to paginate, filter, sort ... * @return number of pending tasks * @throws SBonitaReadException */ long searchNumberOfPendingTasksManagedBy(long managerUserId, QueryOptions searchOptions) throws SBonitaReadException; /** * Search all pending tasks for the specific manager * * @param managerUserId * identifier of manager user * @param searchOptions * the search options to paginate, filter, sort ... * @return a list of SHumanTaskInstance objects * @throws SBonitaReadException */ List searchPendingTasksManagedBy(long managerUserId, QueryOptions searchOptions) throws SBonitaReadException; /** * Increase loopCounter(loopCount+1) for the specific loop instance * * @param loopInstance * the loopCounter in which will be increased * @throws SActivityModificationException */ void incrementLoopCounter(final SLoopActivityInstance loopInstance) throws SActivityModificationException; /** * Get number of overdue open tasks for each user * * @param userIds * identifiers of users * @return a map containing userId and corresponding number of tasks * @throws SBonitaReadException */ Map getNumberOfOverdueOpenTasksForUsers(List userIds) throws SBonitaReadException; /** * Set max loop for the specific loopActvity * * @param loopActivity * the loopActivity * @param result * value for max loop * @throws SActivityModificationException */ void setLoopMax(SLoopActivityInstance loopActivity, Integer result) throws SActivityModificationException; /** * Set LoopCardinality for the specific loopActvity * * @param flowNodeInstance * the loopActvity * @param intLoopCardinality * value of loop cardinality * @throws SActivityModificationException */ void setLoopCardinality(SFlowNodeInstance flowNodeInstance, int intLoopCardinality) throws SActivityModificationException; /** * Add number of activeInstances for the specific SMultiInstanceActivityInstance object * * @param flowNodeInstance * an SMultiInstanceActivityInstance object * @param number * the number will be added * @throws SActivityModificationException */ void addMultiInstanceNumberOfActiveActivities(SMultiInstanceActivityInstance flowNodeInstance, int number) throws SActivityModificationException; /** * Add number of terminated activeInstances for the specific SMultiInstanceActivityInstance object * * @param flowNodeInstance * an SMultiInstanceActivityInstance object * @param number * will be added to terminated instances of flowNodeInstance * the number will be added * @throws SActivityModificationException */ void addMultiInstanceNumberOfTerminatedActivities(SMultiInstanceActivityInstance flowNodeInstance, int number) throws SActivityModificationException; /** * Add number of completed activeInstances for the specific SMultiInstanceActivityInstance object * * @param flowNodeInstance * an SMultiInstanceActivityInstance object whose completed activity number will be updated * @param number * the number will be added * @throws SActivityModificationException */ void addMultiInstanceNumberOfCompletedActivities(SMultiInstanceActivityInstance flowNodeInstance, int number) throws SActivityModificationException; /** * Get total number of activity instances for the specific entity class * * @param entityClass * to indicate which type of class will be retrieved * @param searchOptions * the search options to paginate, filter, sort ... * @return number of activity instances for the specific entity class * @throws SBonitaReadException */ long getNumberOfActivityInstances(Class entityClass, QueryOptions searchOptions) throws SBonitaReadException; /** * Search all activity instances for the specific entity class * * @param entityClass * to indicate which type of class will be retrieved * @param searchOptions * the search options to paginate, filter, sort ... * @return a list of SActivityInstance objects * @throws SBonitaReadException */ List searchActivityInstances(Class entityClass, QueryOptions searchOptions) throws SBonitaReadException; /** * Get total number of archived activity instances for the specific entity class * * @param entityClass * to indicate which type of class will be retrieved * @param searchOptions * the search options to paginate, filter, sort ... * @return number of archived activity instances for the specific entity class * @throws SBonitaReadException */ long getNumberOfArchivedActivityInstances(Class entityClass, QueryOptions searchOptions) throws SBonitaReadException; /*** * Search all archived activity instances for the specific entity class * * @param entityClass * to indicate which type of class will be retrieved * @param searchOptions * the search options to paginate, filter, sort ... * @return a list of SAActivityInstance objects * @throws SBonitaReadException */ List searchArchivedActivityInstances(Class entityClass, QueryOptions searchOptions) throws SBonitaReadException; /** * Set tokenCount for the specific activity instance * * @param activityInstance * the activityInstance will be updated * @param tokenCount * value of tokenCount will be set to the activity * @throws SFlowNodeModificationException */ void setTokenCount(final SActivityInstance activityInstance, int tokenCount) throws SFlowNodeModificationException; /** * @param userId * @param searchOptions * @return * @since 6.0 */ long getNumberOfPendingTasksForUser(long userId, QueryOptions searchOptions) throws SBonitaReadException; /** * @param userId * @param searchOptions * @return * @since 6.0 */ List searchPendingTasksForUser(long userId, QueryOptions searchOptions) throws SBonitaReadException; /** * @since 7.5.5 */ long getNumberOfPendingTasksAssignedTo(long userId, QueryOptions searchOptions) throws SBonitaReadException; /** * @since 7.5.5 */ List searchPendingTasksAssignedTo(long userId, QueryOptions searchOptions) throws SBonitaReadException; /** * @param humanTaskInstanceId * @param queryOptions * @return * @throws SBonitaReadException */ List getPendingMappings(long humanTaskInstanceId, QueryOptions queryOptions) throws SBonitaReadException; /** * @param userId * @param searchOptions * @return * @throws SBonitaReadException * @since 6.0 */ List searchPendingOrAssignedTasks(long userId, QueryOptions searchOptions) throws SBonitaReadException; /** * @param userId * @param searchOptions * @return * @throws SBonitaReadException * @since 6.0 */ long getNumberOfPendingOrAssignedTasks(long userId, QueryOptions searchOptions) throws SBonitaReadException; /** * @param userId * @param searchOptions * @return * @throws SBonitaReadException * @since 7.15 */ List searchPendingOrAssignedOrAssignedToOthersTasks(long userId, QueryOptions searchOptions) throws SBonitaReadException; /** * @param userId * @param searchOptions * @return * @throws SBonitaReadException * @since 7.15 */ long getNumberOfPendingOrAssignedOrAssignedToOthersTasks(long userId, QueryOptions searchOptions) throws SBonitaReadException; /** * @param activityInstance * @param boundaryEventId * @throws SActivityModificationException * @since 6.0 */ void setAbortedByBoundaryEvent(SActivityInstance activityInstance, long boundaryEventId) throws SActivityModificationException; List getPossibleUserIdsOfPendingTasks(long humanTaskInstanceId, int startIndex, int maxResults) throws SActivityReadException; boolean isTaskPendingForUser(long humanTaskInstanceId, long userId) throws SBonitaReadException; /** * Get total number of users according to specific query options, and who can start the task filtered with the * search option * of the given process definition * * @param searchOptions * The QueryOptions object containing some query conditions * @return */ long getNumberOfUsersWhoCanExecutePendingHumanTaskDeploymentInfo(long humanTaskInstanceId, QueryOptions searchOptions) throws SBonitaReadException; /** * Search the users according to specific query options, and who can start the task filtered with the search option * of the given process definition * * @param searchOptions * The QueryOptions object containing some query conditions * @return */ List searchUsersWhoCanExecutePendingHumanTaskDeploymentInfo(long humanTaskInstanceId, QueryOptions searchOptions) throws SBonitaReadException; /** * Get the total number of the assigned and pending human tasks for the specified user, on the specified root * process definition, corresponding to the * options. * * @param rootProcessDefinitionId * The identifier of the root process definition * @param userId * The identifier of the user * @param queryOptions * The search conditions and the options for sorting and paging the results. * @return The assigned and pending human tasks * @since 6.3.3 */ long getNumberOfAssignedAndPendingHumanTasksFor(long rootProcessDefinitionId, long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search the assigned and pending human tasks for the specified user, on the specified root process definition, * corresponding to the options. * * @param rootProcessDefinitionId * The identifier of the root process definition * @param userId * The identifier of the user * @param queryOptions * The search conditions and the options for sorting and paging the results. * @return The assigned and pending human tasks * @since 6.3.3 */ List searchAssignedAndPendingHumanTasksFor(long rootProcessDefinitionId, long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Get the total number of the assigned and pending human tasks for any user, on the specified root process * definition, corresponding to the * options. * * @param rootProcessDefinitionId * The identifier of the root process definition * @param queryOptions * The search conditions and the options for sorting and paging the results. * @return The assigned and pending human tasks * @since 6.3.3 */ long getNumberOfAssignedAndPendingHumanTasks(long rootProcessDefinitionId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search the assigned and pending human tasks for any user, on the specified root process definition, corresponding * to the options. * * @param rootProcessDefinitionId * The identifier of the root process definition * @param queryOptions * The search conditions and the options for sorting and paging the results. * @return The assigned and pending human tasks * @since 6.3.3 */ List searchAssignedAndPendingHumanTasks(long rootProcessDefinitionId, QueryOptions queryOptions) throws SBonitaReadException; /** * Get the total number of the assigned and pending human tasks for any user corresponding to the * options. * * @param queryOptions * The search conditions and the options for sorting and paging the results. * @return The assigned and pending human tasks * @since 7.6.1 */ long getNumberOfAssignedAndPendingHumanTasks(QueryOptions queryOptions) throws SBonitaReadException; /** * Search the assigned and pending human tasks for any user corresponding to the options. * * @param queryOptions * The search conditions and the options for sorting and paging the results. * @return The assigned and pending human tasks * @since 7.6.1 */ List searchAssignedAndPendingHumanTasks(QueryOptions queryOptions) throws SBonitaReadException; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/BPMFailureService.java ================================================ /** * 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.core.process.instance.api; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.model.SABPMFailure; import org.bonitasoft.engine.core.process.instance.model.SBPMFailure; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.services.SPersistenceException; public interface BPMFailureService { SBPMFailure createFlowNodeFailure(SFlowNodeInstance flowNodeInstance, Failure failure) throws SPersistenceException; List getFlowNodeFailures(long flowNodeInstanceId, int maxResults) throws SBonitaReadException; void archiveFlowNodeFailures(long flowNodeInstanceId, long archiveDate) throws SBonitaException; void deleteFlowNodeFailures(long flowNodeInstanceId) throws SBonitaException; void deleteArchivedFlowNodeFailures(List flowNodeInstanceIds) throws SBonitaException; List getArchivedFlowNodeFailures(long flowNodeInstanceId, int maxResults) throws SBonitaReadException; SBPMFailure createProcessInstanceFailure(SProcessInstance processInstance, Failure failure) throws SPersistenceException; List getProcessInstanceFailures(long processInstanceId, int maxResults) throws SBonitaReadException; void archiveProcessInstanceFailures(long processInstanceId, long archiveDate) throws SBonitaException; void deleteProcessInstanceFailures(long processInstanceId) throws SBonitaException; void deleteArchivedProcessInstanceFailures(List processInstanceIds) throws SBonitaException; List getArchivedProcessInstanceFailures(long processInstanceId, int maxResults) throws SBonitaReadException; List getChildProcessInstancesFailures(long rootProcessInstanceId, int maxResults) throws SBonitaReadException; List getArchivedChildProcessInstancesFailures(long rootProcessInstanceId, int maxResults) throws SBonitaReadException; record Failure(String scope, Throwable throwable) {} } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/FlowNodeInstanceService.java ================================================ /** * 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.core.process.instance.api; import java.time.Duration; import java.util.List; import java.util.Set; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeDeletionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.core.process.instance.model.STaskPriority; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public interface FlowNodeInstanceService { String FLOWNODE_INSTANCE = "FLOWNODE_INSTANCE"; String ACTIVITYINSTANCE_STATE = "ACTIVITYINSTANCE_STATE"; String ACTIVITY_INSTANCE_TOKEN_COUNT = "ACTIVITY_INSTANCE_TOKEN_COUNT"; String ACTIVITYINSTANCE_DISPLAY_DESCRIPTION = "ACTIVITYINSTANCE_DISPLAY_DESCRIPTION"; String LOOPINSTANCE_LOOPMAX_MODIFIED = "LOOPINSTANCE_LOOPMAX_MODIFIED"; String MULTIINSTANCE_LOOPCARDINALITY_MODIFIED = "MULTIINSTANCE_LOOPMAX_MODIFIED"; String MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED = "MULTIINSTANCE_LOOPMAX_MODIFIED"; String ACTIVITYINSTANCE_DISPLAY_NAME = "ACTIVITYINSTANCE_DISPLAY_NAME"; String STATE_CATEGORY = "STATE_CATEGORY"; String EXECUTED_BY_MODIFIED = "EXECUTED_BY_MODIFIED"; String EXECUTED_BY_SUBSTITUTE_MODIFIED = "EXECUTED_BY_SUBSTITUTE_MODIFIED"; String EXPECTED_END_DATE_MODIFIED = "EXPECTED_END_DATE_MODIFIED"; /** * @param flowNodeInstanceId * @return * @throws SFlowNodeNotFoundException * @throws SFlowNodeReadException * @since 6.0 */ SFlowNodeInstance getFlowNodeInstance(long flowNodeInstanceId) throws SFlowNodeNotFoundException, SFlowNodeReadException; /** * get the flow node instances directly contained in the given process instance * * @param processInstanceId the parent process instance */ List getAllChildrenOfProcessInstance(long processInstanceId, int fromIndex, int maxResults) throws SFlowNodeReadException, SBonitaReadException; /** * get the flow node instances directly contained in the given process instance * * @param processInstanceId the parent process instance */ List getDirectChildrenOfProcessInstance(long processInstanceId, int fromIndex, int maxResults) throws SFlowNodeReadException, SBonitaReadException; /** * get the flow node instances directly contained in the given activity instance * * @param parentActivityInstanceId the parent process instance */ List getDirectChildrenOfActivityInstance(long parentActivityInstanceId, int fromIndex, int maxResults) throws SFlowNodeReadException, SBonitaReadException; /** * @param flowNodeInstance * @param state * @throws SFlowNodeModificationException * @since 6.0 */ void setState(SFlowNodeInstance flowNodeInstance, FlowNodeState state) throws SFlowNodeModificationException; /** * @param flowNodeInstance * @param priority * @throws SFlowNodeModificationException * @since 6.0 */ void setTaskPriority(SFlowNodeInstance flowNodeInstance, STaskPriority priority) throws SFlowNodeModificationException; /** * @param flowNodeInstance * @param displayDescription * @throws SFlowNodeModificationException * @since 6.0 */ void updateDisplayDescription(SFlowNodeInstance flowNodeInstance, String displayDescription) throws SFlowNodeModificationException; /** * @param flowNodeInstance * @param displayName * @throws SFlowNodeModificationException * @since 6.0 */ void updateDisplayName(SFlowNodeInstance flowNodeInstance, String displayName) throws SFlowNodeModificationException; /** * @param flowElementInstance * @param stateCategory * @throws SFlowNodeModificationException * @since 6.0 */ void setStateCategory(SFlowNodeInstance flowElementInstance, SStateCategory stateCategory) throws SFlowNodeModificationException; /** * @param entityClass * @param countOptions * @return * @throws SBonitaReadException * @since 6.0 */ long getNumberOfFlowNodeInstances(Class entityClass, QueryOptions countOptions) throws SBonitaReadException; /** * @param entityClass * @param countOptions * @return * @throws SBonitaReadException * @since 6.0 */ long getNumberOfFlowNodeInstancesSupervisedBy(Long supervisorId, Class entityClass, QueryOptions countOptions) throws SBonitaReadException; /** * @param entityClass * @param searchOptions * @return * @throws SBonitaReadException * @since 6.0 */ List searchFlowNodeInstances(Class entityClass, QueryOptions searchOptions) throws SBonitaReadException; /** * @param entityClass * @param searchOptions * @return * @throws SBonitaReadException * @since 6.0 */ List searchFlowNodeInstancesSupervisedBy(Long supervisorId, Class entityClass, QueryOptions searchOptions) throws SBonitaReadException; /** * Counts the number of flownode instances in all states. Only considers flownodes direcly contained in given * process definition. Results are counted per flownode name and per state. * * @param processDefinitionId the ID of the process definition to search flownodes for. * @return a list of FlowNodeInstanceStateCounter. If no results, returns an empty list. * @throws SBonitaReadException if a read exception occurs. */ List getNumberOfFlownodesOfProcessDefinitionInAllStates( final long processDefinitionId) throws SBonitaReadException; /** * Counts the number of flownode instances in all states. Only considers flownodes direcly contained in given * process instance, not flownodes in * sub-process instances. Results are counted per flownode name and per state. * * @param processInstanceId the ID of the process instance to search flownodes for. * @return a list of FlowNodeInstanceStateCounter. If no results, returns an empty list. * @throws SBonitaReadException if a read exception occurs. */ List getNumberOfFlownodesInAllStates(final long processInstanceId) throws SBonitaReadException; /** * Counts the number of archived flownode instances in a specific state. Only considers archived flownodes direcly * contained in given process instance, not * flownodes in sub-process instances. Results are counted per flownode name and per state. * * @param processInstanceId the ID of the process instance to search flownodes for. This is the ID of the process * instance before it was archived * (corresponding to the sourceObjectId in the archives) * @return a list of FlowNodeInstanceStateCounter. If no results, returns an empty list. * @throws SBonitaReadException if a read exception occurs. */ public List getNumberOfArchivedFlownodesInAllStates(final long processInstanceId) throws SBonitaReadException; /** * Set execute by for the specific flow node instance * * @param sFlowNodeInstance * the flowNodeInstance will be updated * @param userId * value for executedBy * @throws SFlowNodeModificationException * @since 6.0 */ void setExecutedBy(SFlowNodeInstance sFlowNodeInstance, long userId) throws SFlowNodeModificationException; /** * Set execute by delegate for the specific flow node instance * * @param sFlowNodeInstance * the flowNodeInstance will be updated * @param executerSubstituteId * value for executedBySubstitute * @throws SFlowNodeModificationException * @since 6.0.1 */ void setExecutedBySubstitute(SFlowNodeInstance sFlowNodeInstance, long executerSubstituteId) throws SFlowNodeModificationException; /** * Retrieve the total number of the archived flow nodes matching the given search criteria. * * @param entityClass * The type of the archived flow node to search for * @param queryOptions * The search options to filter the results * @return The number found, 0 if none matching search criteria * @since 6.0 */ long getNumberOfArchivedFlowNodeInstances(Class entityClass, QueryOptions queryOptions) throws SBonitaReadException; /** * Retrieve the total number of the archived flow nodes matching the given search criteria, for a specific * supervisor. * * @param supervisorId * The identifier of the supervisor * @param entityClass * The type of the archived flow node to search for * @param queryOptions * The search options to filter the results * @return The number found, 0 if no matching search criteria * @since 6.3 */ long getNumberOfArchivedFlowNodeInstancesSupervisedBy(long supervisorId, Class entityClass, QueryOptions queryOptions) throws SBonitaReadException; /** * Retrieve the total number of the archived flow nodes matching the given search criteria. * * @param entityClass * The type of the archived flow node to search for * @param queryOptions * The search options to filter the results * @return The list of paginated results, according to the QueryOptions search criteria * @since 6.0 */ List searchArchivedFlowNodeInstances(Class entityClass, QueryOptions queryOptions) throws SBonitaReadException; /** * Retrieve the total number of the archived flow nodes matching the given search criteria, for a specific * supervisor. * * @param supervisorId * The identifier of the supervisor * @param entityClass * The type of the archived flow node to search for * @param queryOptions * The search options to filter the results * @return The list of paginated results, according to the QueryOptions search criteria * @since 6.3 */ List searchArchivedFlowNodeInstancesSupervisedBy(long supervisorId, Class entityClass, QueryOptions queryOptions) throws SBonitaReadException; /** * @param flowNodeInstance * @param dueDate * @throws SFlowNodeModificationException */ void setExpectedEndDate(SFlowNodeInstance flowNodeInstance, Long dueDate) throws SFlowNodeModificationException; /** * @param rootContainerId * @param fromIndex * @param maxResults * @return * @throws SFlowNodeReadException */ List getArchivedFlowNodeInstances(long rootContainerId, int fromIndex, int maxResults) throws SFlowNodeReadException; /** * @param archivedFlowNodeInstanceId * @return * @throws SFlowNodeReadException * @throws SFlowNodeNotFoundException * @since 6.0 */ SAFlowNodeInstance getArchivedFlowNodeInstance(long archivedFlowNodeInstanceId) throws SFlowNodeReadException, SFlowNodeNotFoundException; /** * @param sourceObjectFlowNodeInstanceId * The source identifier of the flow node instance * @return The last archived flow node * @since 6.3 */ T getLastArchivedFlowNodeInstance(final Class entityClass, final long sourceObjectFlowNodeInstanceId) throws SBonitaReadException; /** * @param flowNodeInstance * @throws SFlowNodeModificationException */ void setExecuting(SFlowNodeInstance flowNodeInstance) throws SFlowNodeModificationException; /** * @param sFlowNodeInstance * @throws SFlowNodeReadException * @throws SFlowNodeDeletionException * @since 6.1 */ void deleteFlowNodeInstance(SFlowNodeInstance sFlowNodeInstance) throws SFlowNodeReadException, SFlowNodeDeletionException; /** * retrieve ids of elements that need to be recovered * Called on start node to set the flag to tell the engine to restart these flow nodes * Should not be called when the engine is started! * This does not retrieve SGatewayInstances * * @param considerElementsOlderThan consider elements older than that duration * @param queryOptions used for pagination */ List getFlowNodeInstanceIdsToRecover(Duration considerElementsOlderThan, QueryOptions queryOptions) throws SBonitaReadException; List getFlowNodeInstancesByIds(List ids) throws SBonitaReadException; /** * Retrieve ids of SGatewayInstances that need to be recovered * * @param queryOptions * @return * @throws SBonitaReadException */ List getGatewayInstanceIdsToRecover(Duration considerElementsOlderThan, QueryOptions queryOptions) throws SBonitaReadException; /** * get the number of flow node is this root container * * @param rootContainerId * @return the number of flow node * @since 6.5 */ int getNumberOfFlowNodes(long rootContainerId) throws SBonitaReadException; /** * get all flow nodes contained in the list of root source process instance ids * * @param sourceProcessInstanceIds root source process instance ids * @return list of root source process instance ids * @throws SBonitaReadException */ Set getSourceObjectIdsOfArchivedFlowNodeInstances(List sourceProcessInstanceIds) throws SBonitaReadException; void deleteArchivedFlowNodeInstances(List sourceObjectIds) throws SBonitaException; List getFlowNodeInstancesByNameAndParentContainerId(String name, Long parentContainerId) throws SBonitaReadException; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/GatewayInstanceService.java ================================================ /** * 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.core.process.instance.api; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayReadException; import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Feng Hui * @author Baptiste Mesta * @author Matthieu Chaffotte * @since 6.0 */ public interface GatewayInstanceService { String FINISH = "FINISH:"; String GATEWAYINSTANCE = "GATEWAYINSTANCE"; String GATEWAYINSTANCE_STATE = "GATEWAYINSTANCE_STATE"; String GATEWAYINSTANCE_HITBYS = "GATEWAYINSTANCE_HITBYS"; /** * Create gatewayInstance in DB according to the given gateway instance object * * @param gatewayInstance * the gatewayInsttance object * @throws SGatewayCreationException */ void createGatewayInstance(SGatewayInstance gatewayInstance) throws SGatewayCreationException; /** * Get gateway instance by its id * * @param gatewayInstanceId * identifier of gateway instance * @return an SGatewayInstance object * @throws SGatewayNotFoundException * @throws SGatewayReadException */ SGatewayInstance getGatewayInstance(long gatewayInstanceId) throws SGatewayNotFoundException, SGatewayReadException; /** * Change state of the specific gateway * * @param gatewayInstance * the gateway instance will be updated * @param stateId * identifier of gateway state * @throws SGatewayModificationException */ void setState(SGatewayInstance gatewayInstance, int stateId) throws SGatewayModificationException; /** * @param sDefinition * @param gatewayInstance * @return * @throws SBonitaException */ boolean checkMergingCondition(SProcessDefinition sDefinition, SGatewayInstance gatewayInstance) throws SBonitaException; /** * Add transitionDefinitionName to hitBy of specific gatewayInstance * * @param gatewayInstance * the gateway instance will be updated * @param transitionIndex * value will be added to hitBy of gatewayInstance * @throws SGatewayModificationException * @throws SGatewayCreationException */ void hitTransition(SGatewayInstance gatewayInstance, long transitionIndex) throws SGatewayModificationException, SGatewayCreationException; /** * Get active gatewayInstance in the specific process instance * * @param parentProcessInstanceId * identifier of parent process instance * @param name * name of gateway instance * @return * @throws SGatewayNotFoundException * @throws SGatewayReadException */ SGatewayInstance getActiveGatewayInstanceOfTheProcess(long parentProcessInstanceId, String name) throws SGatewayNotFoundException, SGatewayReadException; List setFinishAndCreateNewGatewayForRemainingToken(SProcessDefinition processDefinition, SGatewayInstance gatewayInstance) throws SBonitaException; List getInclusiveGatewaysOfProcessInstanceThatShouldFire(SProcessDefinition processDefinition, long processInstanceId) throws SBonitaReadException; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/ProcessInstanceService.java ================================================ /** * 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.core.process.instance.api; import java.time.Duration; import java.util.List; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceHierarchicalDeletionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; 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.archive.SAProcessInstance; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Elias Ricken de Medeiros * @author Yanyan Liu * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet * @since 6.0 */ public interface ProcessInstanceService { String PROCESSINSTANCE = "PROCESSINSTANCE"; String PROCESSINSTANCE_STATE = "PROCESSINSTANCE_STATE"; String PROCESS_INSTANCE_CATEGORY_STATE = "PROCESS_INSTANCE_CATEGORY_STATE"; String PROCESSINSTANCE_STATE_UPDATED = "PROCESSINSTANCE_STATE_UPDATED"; String PROCESSINSTANCE_TOKEN_COUNT = "ACTIVITY_INSTANCE_TOKEN_COUNT"; String EVENT_TRIGGER_INSTANCE = "EVENT_TRIGGER_INSTANCE"; /** * Create process instance in DB according to the given process instance object * * @param processInstance * the processInstance * @throws SProcessInstanceCreationException */ void createProcessInstance(SProcessInstance processInstance) throws SProcessInstanceCreationException; /** * Delete the id specified process instance * * @param processInstanceId * identifier of process instance * @throws SProcessInstanceNotFoundException * @throws SProcessInstanceReadException * @throws SFlowNodeReadException * @throws SProcessInstanceModificationException * @throws SProcessInstanceHierarchicalDeletionException */ void deleteProcessInstance(long processInstanceId) throws SProcessInstanceNotFoundException, SProcessInstanceReadException, SFlowNodeReadException, SProcessInstanceModificationException, SProcessInstanceHierarchicalDeletionException; /** * Delete the specified process instance * * @param processInstance * the process instance * @throws SFlowNodeReadException * @throws SProcessInstanceModificationException * @throws SProcessInstanceHierarchicalDeletionException * @since 6.0 */ void deleteProcessInstance(SProcessInstance processInstance) throws SFlowNodeReadException, SProcessInstanceModificationException, SProcessInstanceHierarchicalDeletionException; /** * Delete the specified process instances with id, and their elements archived and not, if are not a subProcess * * @param sProcessInstances * list of process instances to deleted * @return Number of deleted process instances * @since 6.1 */ long deleteParentProcessInstanceAndElements(List sProcessInstances) throws SBonitaException; /** * Delete the specified process instance, and its elements archived and not, if are not a subProcess * * @param processInstance * The {@link SProcessInstance} to delete * @since 6.4.0 */ void deleteParentProcessInstanceAndElements(SProcessInstance processInstance) throws SBonitaException; /** * Get process instance by its id * * @param processInstanceId * identifier of process instance * @return the process instance object * @throws SProcessInstanceNotFoundException * @throws SProcessInstanceReadException */ SProcessInstance getProcessInstance(long processInstanceId) throws SProcessInstanceNotFoundException, SProcessInstanceReadException; /** * Set state for the processInstance * * @param processInstance * the process instance will be updated * @param state * the state will be set to the process instance * @throws SProcessInstanceNotFoundException * @throws SProcessInstanceModificationException */ void setState(SProcessInstance processInstance, ProcessInstanceState state) throws SProcessInstanceNotFoundException, SProcessInstanceModificationException; /** * Set process state category for the given process instance * * @param processInstance * process instance to update * @param stateCatetory * new category state for the process instance * @throws SProcessInstanceNotFoundException * @throws SProcessInstanceModificationException * @since 6.0 */ void setStateCategory(SProcessInstance processInstance, SStateCategory stateCatetory) throws SProcessInstanceNotFoundException, SProcessInstanceModificationException; /** * Set which event interrupted the process instance */ void setInterruptingEventId(long parentProcessInstanceId, long eventInstanceId) throws SProcessInstanceNotFoundException, SProcessInstanceReadException, SProcessInstanceModificationException; /** * Delete specified archived process instance * * @param archivedProcessInstance * the archived process instance * @throws SProcessInstanceModificationException * @throws SFlowNodeReadException * @since 6.0 */ void deleteArchivedProcessInstance(SAProcessInstance archivedProcessInstance) throws SProcessInstanceModificationException, SFlowNodeReadException; /** * delete archived process instances using a list of root source process instance ids * If the given id is not a root process instance (but a called process) it will not be deleted along with its * elements. * * @param sourceProcessInstanceIds list of root source process instance ids * @return the number of archived elements deleted (might be greater than the number of ids given) * @throws SBonitaException */ int deleteArchivedProcessInstances(List sourceProcessInstanceIds) throws SBonitaException; /** * Get child instance identifiers for specific process instance, this can be used for pagination * * @param processInstanceId * identifier of process instance * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param maxResults * Number of result we want to get. Maximum number of result returned * @param sortingField * the field used to do order * @param sortingOrder * ASC or DESC * @return a list of identifiers * @throws SProcessInstanceReadException */ List getChildInstanceIdsOfProcessInstance(long processInstanceId, int fromIndex, int maxResults, String sortingField, OrderByType sortingOrder) throws SProcessInstanceReadException; /** * Get child process instance for the specific call activity or subprocess activity * * @param activityInstId * identifier of call activity or subprocess activity * @return an SProcessInstance object * @throws SProcessInstanceNotFoundException * @throws SBonitaReadException */ SProcessInstance getChildOfActivity(long activityInstId) throws SProcessInstanceNotFoundException, SBonitaReadException; /** * Get total number of child instance for specific process instance * * @param processInstanceId * identifier of process instance * @return number of child instance for the process instance * @throws SProcessInstanceReadException */ long getNumberOfChildInstancesOfProcessInstance(long processInstanceId) throws SProcessInstanceReadException; /** * Get the archived process instances corresponding to the identifiers * * @param archivedProcessInstanceIds * Identifier of the {@link SAProcessInstance}s * @return The list of {@link SAProcessInstance} * @throws SProcessInstanceReadException * @since 6.4.0 */ List getArchivedProcessInstancesInAllStates(List processInstanceIds) throws SProcessInstanceReadException; /** * Get total number of archived process instances according to specific criteria * * @param queryOptions * the search criteria containing a map of specific parameters of a query * @return number of archived process instances * @throws SBonitaReadException */ long getNumberOfArchivedProcessInstances(QueryOptions queryOptions) throws SBonitaReadException; /** * Search all archived process instance according to specific criteria * * @param queryOptions * the search criteria containing a map of specific parameters of a query * @return A list of all archived process instance according to specific criteria * @throws SBonitaReadException */ List searchArchivedProcessInstances(QueryOptions queryOptions) throws SBonitaReadException; /** * Get the latest archived process instance object for the specific process instance * * @param archivedProcessInstanceId * identifier of the archived process instance (not the process instance) * @param persistenceService * @return an SAProcessInstance object * @throws SProcessInstanceReadException */ SAProcessInstance getArchivedProcessInstance(long archivedProcessInstanceId) throws SProcessInstanceReadException; /** * Get total number of process instances * * @param queryOptions * a map of specific parameters of a query * @return total number of process instances * @throws SBonitaReadException */ long getNumberOfProcessInstances(QueryOptions queryOptions) throws SBonitaReadException; /** * Search all process instance according to specific criteria * * @param queryOptions * a map of specific parameters of a query * @return a list of SProcessInstance objects * @throws SBonitaReadException */ List searchProcessInstances(QueryOptions queryOptions) throws SBonitaReadException; /** * Get total number of open process instances for the specific supervisor * * @param userId * identifier of supervisor user * @param queryOptions * a map of specific parameters of a query * @return number of open process instance for the specific supervisor * @throws SBonitaReadException */ long getNumberOfOpenProcessInstancesSupervisedBy(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Get total number of {@link SProcessInstance} with at least one failed task or the * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} * state for the specific supervisor * * @param userId * identifier of supervisor user * @param queryOptions * a map of specific parameters of a query * @return The number of the {@link SProcessInstance} with at least one failed task or the * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} state for the specific supervisor * @throws SBonitaReadException * @since 7.0 */ long getNumberOfFailedProcessInstancesSupervisedBy(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search all {@link SProcessInstance} with at least one failed task or the * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} state * for the specific supervisor * * @param userId * identifier of supervisor user * @param queryOptions * a map of specific parameters of a query * @return The list of {@link SProcessInstance} with at least one failed task or the * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} * state for the specific supervisor * @throws SBonitaReadException * @since 7.0 */ List searchFailedProcessInstancesSupervisedBy(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search all open process instances for the specific supervisor * * @param userId * identifier of supervisor user * @param queryOptions * a map of specific parameters of a query * @return a list of SProcessInstance objects * @throws SBonitaReadException */ List searchOpenProcessInstancesSupervisedBy(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Get total number of open process instance involving the specific user * * @param userId * identifier of user who can perform or be assigned to tasks in process instance. * @param queryOptions * a map of specific parameters of a query * @return number of open process instance for the specific user * @throws SBonitaReadException */ long getNumberOfOpenProcessInstancesInvolvingUser(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search all open process instance involving the specific user * * @param userId * identifier of user who can perform or be assigned to tasks in process instance. * @param queryOptions * @return a list of SProcessInstance objects * @throws SBonitaReadException */ List searchOpenProcessInstancesInvolvingUser(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Get total number of open process instance involving all users of the specific manager * * @param managerUserId * @param queryOptions * @return * @throws SBonitaReadException */ long getNumberOfOpenProcessInstancesInvolvingUsersManagedBy(long managerUserId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search all open process instance involving all users of the specific manager * * @param managerUserId * @param queryOptions * @return * @throws SBonitaReadException */ List searchOpenProcessInstancesInvolvingUsersManagedBy(long managerUserId, QueryOptions queryOptions) throws SBonitaReadException; /** * Get the list of all archived process instance start dates since the given date * * @param sinceDateInMillis the date since when we want to get the list of start dates (in milliseconds) * @return the list of all archived process instance start dates, expressed in millis since EPOCH. * @throws SProcessInstanceReadException if an exception occurs while reading the process instances */ List getLastArchivedProcessInstanceStartDates(long sinceDateInMillis) throws SProcessInstanceReadException; /** * Get the list of sourceObjectIds for archived process instances children of process instance identified by * rootProcessIntanceId * * @param rootProcessIntanceId * the root process instance id * @param fromIndex * index of first result to be retried * @param maxResults * max number of results to be retrieved * @param sortingOrder * the searching order (ASC or DESC) * @return the list of sourceObjectIds for archived process instances children of process instance identified by * rootProcessIntanceId * @throws SBonitaReadException * @since 6.0 */ List getArchivedChildrenSourceObjectIdsFromRootProcessInstance(long rootProcessIntanceId, int fromIndex, int maxResults, OrderByType sortingOrder) throws SBonitaReadException; /** * Get total number of archived process instance according to the search criteria * * @param queryOptions * the search criteria containing a map of specific parameters of a query * @return number of archived process instance satisfied to the search criteria * @throws SBonitaReadException */ long getNumberOfArchivedProcessInstancesWithoutSubProcess(QueryOptions queryOptions) throws SBonitaReadException; /** * Search all archived process instance according to the search criteria * * @param queryOptions * the search criteria containing a map of specific parameters of a query * @return a list of SAProcessInstance objects * @throws SBonitaReadException */ List searchArchivedProcessInstancesWithoutSubProcess(QueryOptions queryOptions) throws SBonitaReadException; /** * Get total number of archived process instance for the specific supervisor * * @param userId * identifier of user who is the supervisor of archived process instance. * @param countOptions * the search criteria containing a map of specific parameters of a query * @return number of archived process instance for the specific supervisor * @throws SBonitaReadException */ long getNumberOfArchivedProcessInstancesSupervisedBy(long userId, QueryOptions countOptions) throws SBonitaReadException; /** * Search all archived process instance for the specific supervisor * * @param userId * identifier of user who is the supervisor of archived process instance. * @param queryOptions * the search criteria containing a map of specific parameters of a query * @return a list of SAProcessInstance objects * @throws SBonitaReadException */ List searchArchivedProcessInstancesSupervisedBy(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Get total number of archived process instance involving the specific user * * @param userId * the identifier of user who is assignee of tasks of process instance * @param countOptions * the search criteria containing a map of specific parameters of a query * @return number of archived process instance involving the specific user * @throws SBonitaReadException */ long getNumberOfArchivedProcessInstancesInvolvingUser(long userId, QueryOptions countOptions) throws SBonitaReadException; /** * Search all archived process instance involving the specific user * * @param userId * the identifier of user who is assignee of tasks of process instance * @param queryOptions * the search criteria containing a map of specific parameters of a query * @return a list of SAProcessInstance objects * @throws SBonitaReadException */ List searchArchivedProcessInstancesInvolvingUser(long userId, QueryOptions queryOptions) throws SBonitaReadException; /** * Update the specific process instance * * @param processInstance * the processInstance will be updated * @param descriptor * update description * @throws SProcessInstanceModificationException */ void updateProcess(SProcessInstance processInstance, EntityUpdateDescriptor descriptor) throws SProcessInstanceModificationException; /** * @param flowNodeInstance * @param processDefinition * @throws SFlowNodeReadException * @throws SProcessInstanceModificationException */ void deleteFlowNodeInstance(SFlowNodeInstance flowNodeInstance, SProcessDefinition processDefinition) throws SFlowNodeReadException, SProcessInstanceModificationException; /** * @param processDefinitionId * @param fromIndex * @param maxResults * @param sortingOrder * @return * @throws SProcessInstanceReadException */ List getSourceProcessInstanceIdsOfArchProcessInstancesFromDefinition(long processDefinitionId, int fromIndex, int maxResults, OrderByType sortingOrder) throws SProcessInstanceReadException; /** * @param sourceObjectProcessInstanceId * The source identifier of the process instance * @return The last archived process instance * @since 6.3 */ SAProcessInstance getLastArchivedProcessInstance(long sourceObjectProcessInstanceId) throws SBonitaReadException; /** * Get the number of the {@link SProcessInstance} with at least one failed task or the * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} * state. * * @param queryOptions * the search criteria containing a map of specific parameters of a query * @return The number of the {@link SProcessInstance} with at least one failed task or the * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} state. * @throws SBonitaException * @since 6.4.0 */ long getNumberOfFailedProcessInstances(QueryOptions queryOptions) throws SBonitaReadException; /** * List all {@link SProcessInstance} with at least one failed task or the * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} state. * * @param queryOptions * the search criteria containing a map of specific parameters of a query * @return The list of {@link SProcessInstance} with at least one failed task or the * {@link org.bonitasoft.engine.bpm.process.ProcessInstanceState#ERROR} * state. * @throws SBonitaException * @since 6.4.0 */ List searchFailedProcessInstances(QueryOptions queryOptions) throws SBonitaReadException; long getNumberOfProcessInstances(long processDefinitionId) throws SBonitaReadException; /** * Retrieve ids of process instances nodes that needs to be recovered. * This is used by recover mechanism ProcessInstanceRecoveryService * * @param considerElementsOlderThan consider elements older than that duration * @param queryOptions used for pagination */ List getProcessInstanceIdsToRecover(Duration considerElementsOlderThan, QueryOptions queryOptions) throws SBonitaReadException; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/RefBusinessDataService.java ================================================ /** * 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.core.process.instance.api; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Matthieu Chaffotte * @author Emmanuel Duchastenier */ public interface RefBusinessDataService { String NEW_REF_BUSINESS_DATA_INSTANCE_ADDED = "New reference to a business data added"; String REF_BUSINESS_DATA_INSTANCE = "REF_BUSINESS_DATA_INSTANCE"; SRefBusinessDataInstance getRefBusinessDataInstance(String name, long processInstanceId) throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException; List getRefBusinessDataInstances(long processInstanceId, int startIndex, int maxResults) throws SBonitaReadException; SRefBusinessDataInstance getFlowNodeRefBusinessDataInstance(String name, long flowNodeInstanceId) throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException; SRefBusinessDataInstance addRefBusinessDataInstance(SRefBusinessDataInstance instance) throws SRefBusinessDataInstanceCreationException; void updateRefBusinessDataInstance(SSimpleRefBusinessDataInstance refBusinessDataInstance, Long dataId) throws SRefBusinessDataInstanceModificationException; void updateRefBusinessDataInstance(SProcessMultiRefBusinessDataInstance refBusinessDataInstance, List dataIds) throws SRefBusinessDataInstanceModificationException; int getNumberOfDataOfMultiRefBusinessData(String name, long processInstanceId) throws SBonitaReadException; SARefBusinessDataInstance getSARefBusinessDataInstance(String name, long processInstanceId) throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException; SARefBusinessDataInstance getSAFlowNodeRefBusinessDataInstance(String name, long flowNodeInstanceId) throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException; void archiveRefBusinessDataInstance(SRefBusinessDataInstance businessDataInstance) throws SObjectModificationException; // TODO; specific exception void deleteArchivedRefBusinessDataInstance(long processInstanceId) throws SObjectModificationException; void deleteArchivedRefBusinessDataInstance(List processInstanceIds) throws SObjectModificationException; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/event/EventInstanceRepository.java ================================================ /** * 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.core.process.instance.api.event; import java.util.List; import java.util.Optional; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceDeletionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventReadException; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent; import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.search.SearchOptions; /** * @author Pascal GARCIA */ public interface EventInstanceRepository { String EVENT_INSTANCE = "EVENT_INSTANCE"; String EVENT_TRIGGER_INSTANCE = "EVENT_TRIGGER_INSTANCE"; String MESSAGE_INSTANCE = "MESSAGE_INSTANCE"; int IN_REQUEST_SIZE = 100; void createEventInstance(SEventInstance eventInstance) throws SEventInstanceCreationException; /** * STimerEventTriggerInstance is used to keep track of currently running timers * using {@link org.bonitasoft.engine.api.ProcessAPI#searchTimerEventTriggerInstances(long, SearchOptions)} */ void createTimerEventTriggerInstance(STimerEventTriggerInstance sEventTriggerInstance) throws SEventTriggerInstanceCreationException; void createMessageInstance(SMessageInstance messageInstance) throws SMessageInstanceCreationException; void createWaitingEvent(SWaitingEvent waitingEvent) throws SWaitingEventCreationException; SWaitingErrorEvent getBoundaryWaitingErrorEvent(long relatedActivityInstanceId, String errorCode) throws SWaitingEventReadException; List getEventInstances(long rootContainerId, int fromIndex, int maxResults, String fieldName, OrderByType orderByType) throws SEventInstanceReadException; /** * @param activityInstanceId * @param fromIndex * @param maxResults * @return List of SBoundaryEventInstance, ordered by identifier ascending * @throws SEventInstanceReadException * @since 6.2 */ List getActivityBoundaryEventInstances(long activityInstanceId, int fromIndex, int maxResults) throws SEventInstanceReadException; /** * @param entityClass * @param eventTriggerInstanceId * @return * @throws SEventTriggerInstanceReadException * @since 6.4.0 */ T getEventTriggerInstance(Class entityClass, long eventTriggerInstanceId) throws SEventTriggerInstanceReadException; void deleteMessageInstance(SMessageInstance messageInstance) throws SMessageModificationException; void deleteWaitingEvent(SWaitingEvent waitingEvent) throws SWaitingEventModificationException; /** * @param signalName * @param fromIndex * @param maxResults * @return * @throws SEventTriggerInstanceReadException * @since 6.3 */ List getWaitingSignalEvents(String signalName, int fromIndex, int maxResults) throws SEventTriggerInstanceReadException; /** * search start waiting events related to a process definition (not its event sub processes) * * @param processDefinitionId * @return * @throws SBonitaReadException * @since 6.3 */ List getStartWaitingEventsOfProcessDefinition(long processDefinitionId) throws SBonitaReadException; List getMessageEventCouples(int fromIndex, int maxResults) throws SEventTriggerInstanceReadException; SWaitingMessageEvent getWaitingMessage(long waitingMessageId) throws SWaitingEventReadException; SMessageInstance getMessageInstance(long messageInstanceId) throws SMessageInstanceReadException; void deleteMessageInstanceByIds(List ids) throws SMessageModificationException; List getMessageInstanceIdOlderThanCreationDate(final long creationDate, QueryOptions queryOptions) throws SEventTriggerInstanceReadException, SMessageInstanceReadException; void updateWaitingMessage(SWaitingMessageEvent waitingMessageEvent, EntityUpdateDescriptor descriptor) throws SWaitingEventModificationException; void updateMessageInstance(SMessageInstance messageInstance, EntityUpdateDescriptor descriptor) throws SMessageModificationException; List searchWaitingEvents(Class entityClass, QueryOptions searchOptions) throws SBonitaReadException; long getNumberOfWaitingEvents(Class entityClass, QueryOptions countOptions) throws SBonitaReadException; /** * @param flowNodeInstanceId the flow node instance id * @return the timer event trigger instance of this flow node if there is one */ Optional getTimerEventTriggerInstanceOfFlowNode(long flowNodeInstanceId) throws SBonitaReadException; SWaitingSignalEvent getWaitingSignalEvent(long id) throws SEventTriggerInstanceReadException, SEventTriggerInstanceNotFoundException; /** * @param eventTriggerInstance * @throws SEventTriggerInstanceDeletionException * @since 6.1 */ void deleteEventTriggerInstance(STimerEventTriggerInstance eventTriggerInstance) throws SEventTriggerInstanceDeletionException; /** * Resets all Message Instances marked as handled, so that they are eligible to match Waiting Events again. * * @throws SMessageModificationException * if an error occurs when resetting the 'handled' flag. */ int resetProgressMessageInstances() throws SMessageModificationException; /** * Resets all Waiting Message Events marked as 'in progress", so that they are eligible to match Message Instances * again. * * @return the number of waiting events reset. * @throws SWaitingEventModificationException * if an error occurs when resetting the 'progress' flag. */ int resetInProgressWaitingEvents() throws SWaitingEventModificationException; /** * Get the number of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria * * @param processInstanceId * The identifier of the process instance * @param queryOptions * Criteria of the search * @return The number of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria * @since 6.4.0 */ long getNumberOfTimerEventTriggerInstances(long processInstanceId, QueryOptions queryOptions) throws SBonitaReadException; /** * Search the list of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria * * @param processInstanceId * The identifier of the process instance * @param queryOptions * Criteria of the search * @return The list of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria * @since 6.4.0 */ List searchTimerEventTriggerInstances(long processInstanceId, QueryOptions queryOptions) throws SBonitaReadException; /** * Update an event trigger instance. * * @param sTimerEventTriggerInstance * The event trigger instance to update * @param descriptor * The fields to update * @throws SEventTriggerInstanceModificationException * @since 6.4.0 */ void updateEventTriggerInstance(STimerEventTriggerInstance sTimerEventTriggerInstance, EntityUpdateDescriptor descriptor) throws SEventTriggerInstanceModificationException; List getWaitingEventsForFlowNodeId(long flowNodeInstanceId) throws SEventTriggerInstanceReadException; /** * Return waiting events related to the process instance passed as parameter, including * the ones at flow node level. */ List getAllWaitingEventsForProcessInstance(long processInstanceId, final int fromIndex, final int maxResults) throws SEventTriggerInstanceReadException; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/event/EventInstanceService.java ================================================ /** * 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.core.process.instance.api.event; import java.util.List; import java.util.Optional; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceDeletionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventReadException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent; import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.search.SearchOptions; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public interface EventInstanceService { void createEventInstance(SEventInstance eventInstance) throws SEventInstanceCreationException; /** * STimerEventTriggerInstance is used to keep track of currently running timers * using {@link org.bonitasoft.engine.api.ProcessAPI#searchTimerEventTriggerInstances(long, SearchOptions)} */ void createTimerEventTriggerInstance(STimerEventTriggerInstance sEventTriggerInstance) throws SEventTriggerInstanceCreationException; void createMessageInstance(SMessageInstance messageInstance) throws SMessageInstanceCreationException; void createWaitingEvent(SWaitingEvent waitingEvent) throws SWaitingEventCreationException; SWaitingErrorEvent getBoundaryWaitingErrorEvent(long relatedActivityInstanceId, String errorCode) throws SWaitingEventReadException; List getEventInstances(long rootContainerId, int fromIndex, int maxResults, String fieldName, OrderByType orderByType) throws SEventInstanceReadException; void deleteWaitingEvents(SProcessInstance processInstance) throws SWaitingEventModificationException, SEventTriggerInstanceReadException; /** * @param activityInstanceId * @param fromIndex * @param maxResults * @return List of SBoundaryEventInstance, ordered by identifier ascending * @throws SEventInstanceReadException * @since 6.2 */ List getActivityBoundaryEventInstances(long activityInstanceId, int fromIndex, int maxResults) throws SEventInstanceReadException; /** * @param entityClass * @param eventTriggerInstanceId * @return * @throws SEventTriggerInstanceReadException * @since 6.4.0 */ T getEventTriggerInstance(Class entityClass, long eventTriggerInstanceId) throws SEventTriggerInstanceReadException; void deleteMessageInstance(SMessageInstance messageInstance) throws SMessageModificationException; void deleteWaitingEvent(SWaitingEvent waitingEvent) throws SWaitingEventModificationException; /** * @param signalName * @param fromIndex * @param maxResults * @return * @throws SEventTriggerInstanceReadException * @since 6.3 */ List getWaitingSignalEvents(String signalName, int fromIndex, int maxResults) throws SEventTriggerInstanceReadException; /** * @param processDefinitionId * @return * @throws SBonitaReadException * @since 6.3 */ List getStartWaitingEventsOfProcessDefinition(long processDefinitionId) throws SBonitaReadException; List getMessageEventCouples(int fromIndex, int maxResults) throws SEventTriggerInstanceReadException; SWaitingMessageEvent getWaitingMessage(long waitingMessageId) throws SWaitingEventReadException; SMessageInstance getMessageInstance(long messageInstanceId) throws SMessageInstanceReadException; void updateWaitingMessage(SWaitingMessageEvent waitingMessageEvent, EntityUpdateDescriptor descriptor) throws SWaitingEventModificationException; void updateMessageInstance(SMessageInstance messageInstance, EntityUpdateDescriptor descriptor) throws SMessageModificationException; List searchWaitingEvents(Class entityClass, QueryOptions searchOptions) throws SBonitaReadException; long getNumberOfWaitingEvents(Class entityClass, QueryOptions countOptions) throws SBonitaReadException; /** * @param flowNodeInstanceId the flow node instance id * @return the timer event trigger instance of this flow node if there is one */ Optional getTimerEventTriggerInstanceOfFlowNode(long flowNodeInstanceId) throws SBonitaReadException; SWaitingSignalEvent getWaitingSignalEvent(long id) throws SEventTriggerInstanceReadException, SEventTriggerInstanceNotFoundException; /** * @param eventTriggerInstance * @throws SEventTriggerInstanceDeletionException * @since 6.1 */ void deleteEventTriggerInstance(STimerEventTriggerInstance eventTriggerInstance) throws SEventTriggerInstanceDeletionException; void deleteEventTriggerInstanceOfFlowNode(long flowNodeInstanceId) throws SBonitaReadException, SEventTriggerInstanceDeletionException; /** * @param flowNodeInstance * @throws SWaitingEventModificationException * @throws SEventTriggerInstanceReadException * @since 6.1 */ void deleteWaitingEvents(SFlowNodeInstance flowNodeInstance) throws SWaitingEventModificationException, SEventTriggerInstanceReadException; /** * Resets all Message Instances marked as handled, so that they are eligible to match Waiting Events again. * * @throws SMessageModificationException * if an error occurs when resetting the 'handled' flag. */ int resetProgressMessageInstances() throws SMessageModificationException; /** * Resets all Waiting Message Events marked as 'in progress", so that they are eligible to match Message Instances * again. * * @return the number of waiting events reset. * @throws SWaitingEventModificationException * if an error occurs when resetting the 'progress' flag. */ int resetInProgressWaitingEvents() throws SWaitingEventModificationException; /** * Get the number of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria * * @param processInstanceId * The identifier of the process instance * @param searchOptions * Criteria of the search * @return The number of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria * @since 6.4.0 */ long getNumberOfTimerEventTriggerInstances(long processInstanceId, QueryOptions searchOptions) throws SBonitaReadException; /** * Search the list of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria * * @param processInstanceId * The identifier of the process instance * @param searchOptions * Criteria of the search * @return The list of STimerEventTriggerInstance on the specific process instance & corresponding to the criteria * @since 6.4.0 */ List searchTimerEventTriggerInstances(long processInstanceId, QueryOptions searchOptions) throws SBonitaReadException; Integer deleteMessageAndDataInstanceOlderThanCreationDate(long creationDate, QueryOptions queryOptions) throws SMessageModificationException; List getMessageInstanceIdOlderThanCreationDate(long creationDate, QueryOptions queryOptions) throws SEventTriggerInstanceReadException, SMessageInstanceReadException; /** * Update an event trigger instance. * * @param sTimerEventTriggerInstance * The event trigger instance to update * @param descriptor * The fields to update * @throws SEventTriggerInstanceModificationException * @since 6.4.0 */ void updateEventTriggerInstance(STimerEventTriggerInstance sTimerEventTriggerInstance, EntityUpdateDescriptor descriptor) throws SEventTriggerInstanceModificationException; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SAProcessInstanceNotFoundException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Celine Souchet */ public class SAProcessInstanceNotFoundException extends SBonitaException { private static final long serialVersionUID = 1956687311066803177L; public SAProcessInstanceNotFoundException(final long processInstanceId) { super("Archived process instance with id <" + processInstanceId + "> not found"); } public SAProcessInstanceNotFoundException(final long processInstanceId, final String state) { super("Archived process instance with id <" + processInstanceId + "> and state <" + state + "> not found"); } public SAProcessInstanceNotFoundException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityCreationException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SActivityCreationException extends SBonitaException { private static final long serialVersionUID = 831038382346357330L; public SActivityCreationException(final Throwable cause) { super(cause); } public SActivityCreationException(final String message, final Throwable cause) { super(message, cause); } public SActivityCreationException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityExecutionException.java ================================================ /** * 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.core.process.instance.api.exceptions; /** * @author Baptiste Mesta * @author Nicolas Chabanoles * @author Celine Souchet * An unexpected error happened during the execution of the activity * The activity might be in an inconsistent state */ public class SActivityExecutionException extends SFlowNodeExecutionException { private static final long serialVersionUID = -1134776910207667894L; public SActivityExecutionException(final String message, final Throwable cause) { super(message, cause); } public SActivityExecutionException(final String message) { super(message); } public SActivityExecutionException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityExecutionFailedException.java ================================================ /** * 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.core.process.instance.api.exceptions; /** * @author Baptiste Mesta * @author Celine Souchet * An error happened during the execution of the activity * The activity is now in a failed state */ public class SActivityExecutionFailedException extends SFlowNodeExecutionException { private static final long serialVersionUID = 8873662434571064042L; public SActivityExecutionFailedException(final Throwable e) { super(e); } /** * @param message * @param cause */ public SActivityExecutionFailedException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityInstanceNotFoundException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SActivityInstanceNotFoundException extends SBonitaException { private static final long serialVersionUID = 4163590480725860398L; public SActivityInstanceNotFoundException(final long activityInstanceId) { super("Activity instance with id " + activityInstanceId + " not found"); } public SActivityInstanceNotFoundException(final long activityInstanceId, final int stateId) { super("Activity instance with id " + activityInstanceId + " and stateId " + stateId + " not found"); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityModificationException.java ================================================ /** * 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.core.process.instance.api.exceptions; import java.io.Serial; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta * @author Celine Souchet */ public class SActivityModificationException extends SBonitaException { @Serial private static final long serialVersionUID = 976164513418178786L; public SActivityModificationException(final Throwable cause) { super(cause); } public SActivityModificationException(final String message) { super(message); } public SActivityModificationException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityReadException.java ================================================ /** * 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.core.process.instance.api.exceptions; /** * @author Baptiste Mesta */ public class SActivityReadException extends SFlowNodeReadException { private static final long serialVersionUID = 8517963344655147902L; public SActivityReadException(final Throwable e) { super(e); } public SActivityReadException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SActivityStateExecutionException.java ================================================ /** * 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.core.process.instance.api.exceptions; /** * @author Baptiste Mesta * @author Celine Souchet */ public class SActivityStateExecutionException extends SFlowNodeExecutionException { private static final long serialVersionUID = -6135671543460179364L; public SActivityStateExecutionException(final String message) { super(message); } public SActivityStateExecutionException(final Throwable cause) { super(cause); } public SActivityStateExecutionException(final String message, final Throwable cause) { super(message, cause); } public SActivityStateExecutionException(final String message, String scope, final Throwable cause) { super(message, scope, cause); } public SActivityStateExecutionException(final String message, String scope) { super(message, scope); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SContractViolationException.java ================================================ /** * 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.core.process.instance.api.exceptions; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Thrown when the {@link org.bonitasoft.engine.core.process.definition.model.SContractDefinition} is not fulfilled. * * @author Emmanuel Duchastenier * @since 7.0 */ public class SContractViolationException extends SBonitaException { private final List explanations; private final String simpleMessage; /** * Constructs an SContractViolationException with the specified detail message and the explanations. * * @param message the specified detail message * @param explanations the explanations */ public SContractViolationException(final String message, final List explanations) { super(message + ": " + ((explanations == null) || (explanations.isEmpty()) ? "no details" : String.join(", ", explanations))); this.simpleMessage = message; if (explanations == null) { this.explanations = Collections.emptyList(); } else { this.explanations = new ArrayList<>(explanations); } } public SContractViolationException(String message, Exception e) { super(message, e); this.simpleMessage = message; this.explanations = Collections.emptyList(); } /** * Returns the explanations of why the contract is not fulfilled. * * @return the explanations */ public List getExplanations() { return explanations; } public String getSimpleMessage() { return simpleMessage; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowElementModificationException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SFlowElementModificationException extends SBonitaException { private static final long serialVersionUID = 976164513418178786L; public SFlowElementModificationException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowNodeDeletionException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Celine Souchet */ public class SFlowNodeDeletionException extends SBonitaException { private static final long serialVersionUID = 976164513418178786L; public SFlowNodeDeletionException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowNodeExecutionException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * An unexpected error happened during the execution of the flow node * The flow node might be in an inconsistent state * * @author Celine Souchet */ public class SFlowNodeExecutionException extends SBonitaException { private static final long serialVersionUID = 5549874111741638842L; public SFlowNodeExecutionException(final String message, final String scope, final Throwable cause) { super(message, scope, cause); } public SFlowNodeExecutionException(final String message, final String scope) { super(message, scope); } public SFlowNodeExecutionException(final String message, final Throwable cause) { super(message, cause); } public SFlowNodeExecutionException(final String message) { super(message); } public SFlowNodeExecutionException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowNodeModificationException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SFlowNodeModificationException extends SBonitaException { private static final long serialVersionUID = 976164513418178786L; public SFlowNodeModificationException(final Throwable cause) { super(cause); } public SFlowNodeModificationException(String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowNodeNotFoundException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SFlowNodeNotFoundException extends SBonitaException { private static final long serialVersionUID = -3838993791524556098L; public SFlowNodeNotFoundException(final long flowNodeId) { super("Flow node instance with id " + flowNodeId + " not found"); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SFlowNodeReadException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SFlowNodeReadException extends SBonitaException { private static final long serialVersionUID = 7025419917545646389L; public SFlowNodeReadException(final String message, final Throwable cause) { super(message, cause); } public SFlowNodeReadException(final Throwable cause) { super(cause); } public SFlowNodeReadException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SGatewayCreationException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Feng Hui */ public class SGatewayCreationException extends SBonitaException { private static final long serialVersionUID = 831038382346357330L; public SGatewayCreationException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SGatewayModificationException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Feng Hui */ public class SGatewayModificationException extends SBonitaException { private static final long serialVersionUID = 4889011534447314330L; public SGatewayModificationException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SGatewayNotFoundException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Feng Hui * @author Matthieu Chaffotte */ public class SGatewayNotFoundException extends SBonitaException { private static final long serialVersionUID = -7022009042275018689L; public SGatewayNotFoundException(final long gatewayInstanceId) { super("Gateway instance with id " + gatewayInstanceId + " not found"); } public SGatewayNotFoundException(final long processInstanceId, final String name) { super("Gateway instance with process id '" + processInstanceId + "' and name '" + name + "' not found"); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SGatewayReadException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SObjectReadException; /** * @author Feng Hui */ public class SGatewayReadException extends SObjectReadException { private static final long serialVersionUID = 8517963344655147902L; public SGatewayReadException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SProcessInstanceCreationException.java ================================================ /** * 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.core.process.instance.api.exceptions; import lombok.Getter; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; /** * @author Baptiste Mesta */ public class SProcessInstanceCreationException extends SBonitaException { private static final long serialVersionUID = 7581906795549409593L; @Getter private long retryAfter = -1L; public SProcessInstanceCreationException(final Throwable cause) { super(cause); } public SProcessInstanceCreationException(final String message, final Throwable cause) { super(message, cause); } public SProcessInstanceCreationException(final String message) { super(message); } public SProcessInstanceCreationException(final String message, final long retryAfter) { super(message); this.retryAfter = retryAfter; } /** * @param sDefinition * The process definition to add on context * @since 6.3 */ public void setProcessDefinitionOnContext(final SProcessDefinition sDefinition) { setProcessDefinitionIdOnContext(sDefinition.getId()); setProcessDefinitionNameOnContext(sDefinition.getName()); setProcessDefinitionVersionOnContext(sDefinition.getVersion()); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SProcessInstanceDeletionException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SProcessInstanceDeletionException extends SBonitaException { private static final long serialVersionUID = 1704316503564609224L; public SProcessInstanceDeletionException(final Throwable cause) { super(cause); } public SProcessInstanceDeletionException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SProcessInstanceHierarchicalDeletionException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SProcessInstanceHierarchicalDeletionException extends SBonitaException { private static final long serialVersionUID = 1704316503564609224L; private final long processInstanceId; public SProcessInstanceHierarchicalDeletionException(final String string, final long processInstanceId) { super(string); this.processInstanceId = processInstanceId; } public long getProcessInstanceId() { return processInstanceId; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SProcessInstanceModificationException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SProcessInstanceModificationException extends SBonitaException { private static final long serialVersionUID = 4889011534447314330L; public SProcessInstanceModificationException(final Throwable cause) { super(cause); } /** * @param string * @param e */ public SProcessInstanceModificationException(final String string, final Exception e) { super(string, e); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SProcessInstanceNotFoundException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SProcessInstanceNotFoundException extends SBonitaException { private static final long serialVersionUID = 1956687311066803177L; public SProcessInstanceNotFoundException(final long processInstanceId) { super("Process instance with id <" + processInstanceId + "> not found"); } public SProcessInstanceNotFoundException(final long processInstanceId, final String state) { super("Process instance with id <" + processInstanceId + "> and state <" + state + "> not found"); } public SProcessInstanceNotFoundException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SProcessInstanceReadException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.persistence.AbstractSelectDescriptor; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Emmanuel Duchastenier */ public class SProcessInstanceReadException extends SBonitaException { private static final long serialVersionUID = -5549277707760652364L; public SProcessInstanceReadException(final Throwable cause) { super(formatMessage(cause), cause); } private static final String formatMessage(final Throwable cause) { String message = null; if (cause instanceof SBonitaReadException) { final AbstractSelectDescriptor descriptor = ((SBonitaReadException) cause).getSelectDescriptor(); if (descriptor != null) { message = descriptor.toString(); } } return message; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/STaskVisibilityException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Emmanuel Duchastenier * @author Celine Souchet */ public class STaskVisibilityException extends SBonitaException { private static final long serialVersionUID = 7406562594163981383L; public STaskVisibilityException(final String message) { super(message); } public STaskVisibilityException(final String message, final Throwable cause) { super(message, cause); } public STaskVisibilityException(final String message, final long activityInstanceId, final long userId) { super(message); setFlowNodeDefinitionIdOnContext(activityInstanceId); setUserIdOnContext(userId); } /** * @param taskInstanceId * the ID of the task whose visibility is being * @param userId * the ID of the user whose Task visibility is associated to */ public STaskVisibilityException(final String message, final long activityInstanceId, final long userId, final Throwable cause) { super(message, cause); setFlowNodeDefinitionIdOnContext(activityInstanceId); setUserIdOnContext(userId); } public STaskVisibilityException(final String message, final long activityInstanceId) { super(message); setFlowNodeDefinitionIdOnContext(activityInstanceId); } public STaskVisibilityException(final String message, final long activityInstanceId, final Throwable cause) { super(message, cause); setFlowNodeDefinitionIdOnContext(activityInstanceId); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SUnknowStateCategory.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SUnknowStateCategory extends SBonitaException { private static final long serialVersionUID = 6420290986048627139L; public SUnknowStateCategory(final String categoryState) { super("Unknown state category: " + categoryState); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SuserTaskSkipException.java ================================================ /** * 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.core.process.instance.api.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Zhang Bole */ public class SuserTaskSkipException extends SBonitaException { /** * */ private static final long serialVersionUID = -7556717384908033L; public SuserTaskSkipException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/business/SHumanTaskAlreadyAssignedException.java ================================================ /** * 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.core.process.instance.api.exceptions.business; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Pablo Alonso de Linaje */ public class SHumanTaskAlreadyAssignedException extends SBonitaException { public SHumanTaskAlreadyAssignedException(final Throwable cause) { super(cause); } public SHumanTaskAlreadyAssignedException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/business/data/SRefBusinessDataInstanceCreationException.java ================================================ /** * 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.core.process.instance.api.exceptions.business.data; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SRefBusinessDataInstanceCreationException extends SBonitaException { private static final long serialVersionUID = 4703430668508021915L; public SRefBusinessDataInstanceCreationException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/business/data/SRefBusinessDataInstanceModificationException.java ================================================ /** * 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.core.process.instance.api.exceptions.business.data; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SRefBusinessDataInstanceModificationException extends SBonitaException { private static final long serialVersionUID = 3527430173631664746L; public SRefBusinessDataInstanceModificationException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/business/data/SRefBusinessDataInstanceNotFoundException.java ================================================ /** * 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.core.process.instance.api.exceptions.business.data; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SRefBusinessDataInstanceNotFoundException extends SBonitaException { private static final long serialVersionUID = -5163481117317685640L; public SRefBusinessDataInstanceNotFoundException(final long processInstanceId, final String name) { super("Unable to find a reference to a business data named '" + name + "' of process instance " + processInstanceId); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/SEventInstanceCreationException.java ================================================ /** * 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.core.process.instance.api.exceptions.event; /** * @author Elias Ricken de Medeiros */ public class SEventInstanceCreationException extends SEventInstanceException { private static final long serialVersionUID = -2743737085797834497L; public SEventInstanceCreationException(final String message, final Throwable cause) { super(message, cause); } public SEventInstanceCreationException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/SEventInstanceException.java ================================================ /** * 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.core.process.instance.api.exceptions.event; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SEventInstanceException extends SBonitaException { private static final long serialVersionUID = -6849370511498116125L; public SEventInstanceException(final String message) { super(message); } public SEventInstanceException(final String message, final Throwable cause) { super(message, cause); } public SEventInstanceException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/SEventInstanceNotFoundException.java ================================================ /** * 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.core.process.instance.api.exceptions.event; /** * @author Elias Ricken de Medeiros */ public class SEventInstanceNotFoundException extends SEventInstanceException { private static final long serialVersionUID = 6394599118486821409L; public SEventInstanceNotFoundException(final long eventId) { super("Unable to find event instance with id " + eventId); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/SEventInstanceReadException.java ================================================ /** * 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.core.process.instance.api.exceptions.event; /** * @author Elias Ricken de Medeiros */ public class SEventInstanceReadException extends SEventInstanceException { private static final long serialVersionUID = 3246685428772443332L; public SEventInstanceReadException(final String message, final Throwable cause) { super(message, cause); } public SEventInstanceReadException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SEventTriggerInstanceCreationException.java ================================================ /** * 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.core.process.instance.api.exceptions.event.trigger; /** * @author Elias Ricken de Medeiros */ public class SEventTriggerInstanceCreationException extends SEventTriggerInstanceException { private static final long serialVersionUID = -3184254325049610898L; public SEventTriggerInstanceCreationException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SEventTriggerInstanceDeletionException.java ================================================ /** * 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.core.process.instance.api.exceptions.event.trigger; /** * @author Elias Ricken de Medeiros */ public class SEventTriggerInstanceDeletionException extends SEventTriggerInstanceException { private static final long serialVersionUID = -3184254325049610898L; public SEventTriggerInstanceDeletionException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SEventTriggerInstanceException.java ================================================ /** * 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.core.process.instance.api.exceptions.event.trigger; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SEventTriggerInstanceException extends SBonitaException { private static final long serialVersionUID = 5400072957387312123L; public SEventTriggerInstanceException(final String message, final Throwable cause) { super(message, cause); } public SEventTriggerInstanceException(final String message) { super(message); } public SEventTriggerInstanceException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SEventTriggerInstanceModificationException.java ================================================ /** * 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.core.process.instance.api.exceptions.event.trigger; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ public class SEventTriggerInstanceModificationException extends SEventTriggerInstanceException { private static final long serialVersionUID = -2488805930392940258L; public SEventTriggerInstanceModificationException(String message) { super(message); } public SEventTriggerInstanceModificationException(String message, Throwable cause) { super(message, cause); } public SEventTriggerInstanceModificationException(Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SEventTriggerInstanceNotFoundException.java ================================================ /** * 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.core.process.instance.api.exceptions.event.trigger; /** * @author Elias Ricken de Medeiros */ public class SEventTriggerInstanceNotFoundException extends SEventTriggerInstanceException { private static final long serialVersionUID = 4533632552469947077L; public SEventTriggerInstanceNotFoundException(final long eventTriggerId) { super("Unable to find event trigger instance with id " + eventTriggerId); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SEventTriggerInstanceReadException.java ================================================ /** * 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.core.process.instance.api.exceptions.event.trigger; /** * @author Elias Ricken de Medeiros */ public class SEventTriggerInstanceReadException extends SEventTriggerInstanceException { private static final long serialVersionUID = -6798342718027113516L; public SEventTriggerInstanceReadException(final String message, final Throwable cause) { super(message, cause); } public SEventTriggerInstanceReadException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SMessageInstanceCreationException.java ================================================ /** * 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.core.process.instance.api.exceptions.event.trigger; /** * @author Elias Ricken de Medeiros */ public class SMessageInstanceCreationException extends SEventTriggerInstanceException { private static final long serialVersionUID = 8177794618577658085L; public SMessageInstanceCreationException(final String message, final Throwable cause) { super(message, cause); } public SMessageInstanceCreationException(final String message) { super(message); } public SMessageInstanceCreationException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SMessageInstanceNotFoundException.java ================================================ /** * 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.core.process.instance.api.exceptions.event.trigger; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceException; /** * @author Elias Ricken de Medeiros */ public class SMessageInstanceNotFoundException extends SEventInstanceException { private static final long serialVersionUID = 6394599118486821409L; public SMessageInstanceNotFoundException(final long messageInstanceId) { super("Unable to find message instance with id " + messageInstanceId); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SMessageInstanceReadException.java ================================================ /** * 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.core.process.instance.api.exceptions.event.trigger; /** * @author Elias Ricken de Medeiros */ public class SMessageInstanceReadException extends SEventTriggerInstanceException { private static final long serialVersionUID = -9015190020329584232L; public SMessageInstanceReadException(final String message, final Throwable cause) { super(message, cause); } public SMessageInstanceReadException(final String message) { super(message); } public SMessageInstanceReadException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SMessageModificationException.java ================================================ /** * 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.core.process.instance.api.exceptions.event.trigger; /** * @author Elias Ricken de Medeiros */ public class SMessageModificationException extends SEventTriggerInstanceException { private static final long serialVersionUID = -2488805930392940258L; public SMessageModificationException(String message) { super(message); } public SMessageModificationException(String message, Throwable cause) { super(message, cause); } public SMessageModificationException(Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SWaitingEventCreationException.java ================================================ /** * 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.core.process.instance.api.exceptions.event.trigger; /** * @author Elias Ricken de Medeiros */ public class SWaitingEventCreationException extends SEventTriggerInstanceException { private static final long serialVersionUID = 8177794618577658085L; public SWaitingEventCreationException(String message, Throwable cause) { super(message, cause); } public SWaitingEventCreationException(String message) { super(message); } public SWaitingEventCreationException(Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SWaitingEventModificationException.java ================================================ /** * 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.core.process.instance.api.exceptions.event.trigger; /** * @author Elias Ricken de Medeiros */ public class SWaitingEventModificationException extends SEventTriggerInstanceException { private static final long serialVersionUID = -2488805930392940258L; public SWaitingEventModificationException(String message) { super(message); } public SWaitingEventModificationException(String message, Throwable cause) { super(message, cause); } public SWaitingEventModificationException(Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SWaitingEventNotFoundException.java ================================================ /** * 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.core.process.instance.api.exceptions.event.trigger; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceException; /** * @author Elias Ricken de Medeiros */ public class SWaitingEventNotFoundException extends SEventInstanceException { private static final long serialVersionUID = 6394599118486821409L; public SWaitingEventNotFoundException(final long waitingMessageId) { super("Unable to find waiting message event with id " + waitingMessageId); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/exceptions/event/trigger/SWaitingEventReadException.java ================================================ /** * 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.core.process.instance.api.exceptions.event.trigger; /** * @author Elias Ricken de Medeiros */ public class SWaitingEventReadException extends SEventTriggerInstanceException { private static final long serialVersionUID = 3000875111256588497L; public SWaitingEventReadException(final String message, final Throwable cause) { super(message, cause); } public SWaitingEventReadException(final String message) { super(message); } public SWaitingEventReadException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/states/FlowNodeState.java ================================================ /** * 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.core.process.instance.api.states; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityExecutionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityStateExecutionException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface FlowNodeState { int ID_ACTIVITY_EXECUTING = 1; int ID_ACTIVITY_READY = 4; int ID_ACTIVITY_FAILED = 3; int getId(); /** * @param processDefinition * @param flowNodeInstance * @return true the state must be executed, false if the execution must skip this state and go directly to the next * one * @throws SActivityExecutionException */ boolean shouldExecuteState(SProcessDefinition processDefinition, SFlowNodeInstance flowNodeInstance) throws SActivityExecutionException; /** * Return true if flowNodeInstance instance of SHumanTaskInstance * * @param flowNodeInstance * @return true or false * @since 6.0 */ boolean mustAddSystemComment(SFlowNodeInstance flowNodeInstance); /** * Add a system comment "User XYZ has XYZ(state change) task XYZ(task name)" * * @param flowNodeInstance * @return system comment "User XYZ has XYZ(state change) task XYZ(task name)" * @since 6.0 */ String getSystemComment(SFlowNodeInstance flowNodeInstance); StateCode execute(SProcessDefinition processDefinition, SFlowNodeInstance instance) throws SActivityStateExecutionException; /** * Called when a child of the flow node parentInstance finishes. * Triggers what's next, if applicable. * returns if all children activity is finished / triggered. * * @return true if the state is finished (the flow node will continue its flow), * false if there are still some children to be triggered / to wait for. */ default boolean notifyChildFlowNodeHasFinished(SProcessDefinition processDefinition, SFlowNodeInstance parentInstance, SFlowNodeInstance childInstance) throws SActivityStateExecutionException { return false; } String getName(); /** * @return true if the state is stable * a final state is stable */ boolean isStable(); /** * Checks whether the state is a terminal one. * * @return true is the state is a terminal one; false otherwise */ boolean isTerminal(); /** * Get the state's category * * @return the state's category */ SStateCategory getStateCategory(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/api/states/StateCode.java ================================================ /** * 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.core.process.instance.api.states; /** * This is code that are returned by state when they are execute * DONE is when the state is finished and executed correctly * EXECUTING when there is still work to be done on the state (so the state must not change) * * @author Baptiste Mesta */ public enum StateCode { DONE, EXECUTING; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceRepositoryImpl.java ================================================ /** * 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.core.process.instance.event.impl; import static java.util.Collections.singletonMap; import java.util.*; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.ListUtils; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.*; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.*; import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance; import org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.*; import org.bonitasoft.engine.persistence.search.FilterOperationType; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.SPersistenceException; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Frederic Bouquet * @author Celine Souchet */ @Slf4j public class EventInstanceRepositoryImpl implements EventInstanceRepository { public static final String QUERY_RESET_IN_PROGRESS_WAITING_EVENTS = "resetInProgressWaitingEvents"; private static final String QUERY_RESET_PROGRESS_MESSAGE_INSTANCES = "resetProgressMessageInstances"; private final EventService eventService; private final Recorder recorder; private final PersistenceService persistenceService; public EventInstanceRepositoryImpl(final Recorder recorder, final PersistenceService persistenceService, final EventService eventService, final ArchiveService archiveService) { this.recorder = recorder; this.eventService = eventService; this.persistenceService = persistenceService; } @Override public void createEventInstance(final SEventInstance eventInstance) throws SEventInstanceCreationException { try { recorder.recordInsert(new InsertRecord(eventInstance), EVENT_INSTANCE); } catch (final SRecorderException e) { throw new SEventInstanceCreationException(e); } if (log.isDebugEnabled()) { final StringBuilder stb = new StringBuilder(); stb.append("Created "); stb.append(eventInstance.getType().getValue()); stb.append(" <"); stb.append(eventInstance.getName()); stb.append("> with id = <"); stb.append(eventInstance.getId()); stb.append(">, parent process instance id = <"); stb.append(eventInstance.getParentProcessInstanceId()); stb.append(">, root process instance id = <"); stb.append(eventInstance.getRootProcessInstanceId()); stb.append(">, process definition id = <"); stb.append(eventInstance.getProcessDefinitionId()); stb.append(">"); final String message = stb.toString(); log.debug(message); } } @Override public void createTimerEventTriggerInstance(final STimerEventTriggerInstance eventTriggerInstance) throws SEventTriggerInstanceCreationException { try { recorder.recordInsert(new InsertRecord(eventTriggerInstance), EVENT_TRIGGER_INSTANCE); } catch (final SRecorderException e) { throw new SEventTriggerInstanceCreationException(e); } } @Override public void createMessageInstance(SMessageInstance messageInstance) throws SMessageInstanceCreationException { try { recorder.recordInsert(new InsertRecord(messageInstance), MESSAGE_INSTANCE); } catch (final SRecorderException e) { throw new SMessageInstanceCreationException(e); } } @Override public void createWaitingEvent(final SWaitingEvent waitingEvent) throws SWaitingEventCreationException { try { recorder.recordInsert(new InsertRecord(waitingEvent), EVENT_TRIGGER_INSTANCE); } catch (final SRecorderException e) { throw new SWaitingEventCreationException(e); } } @Override public void deleteEventTriggerInstance(final STimerEventTriggerInstance eventTriggerInstance) throws SEventTriggerInstanceDeletionException { try { recorder.recordDelete(new DeleteRecord(eventTriggerInstance), EVENT_TRIGGER_INSTANCE); } catch (final SRecorderException e) { throw new SEventTriggerInstanceDeletionException(e); } } @Override public void deleteMessageInstance(final SMessageInstance messageInstance) throws SMessageModificationException { try { recorder.recordDelete(new DeleteRecord(messageInstance), MESSAGE_INSTANCE); } catch (final SRecorderException e) { throw new SMessageModificationException(e); } } @Override public List getActivityBoundaryEventInstances(final long activityInstanceId, final int fromIndex, final int maxResults) throws SEventInstanceReadException { final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getActivityBoundaryEvents(activityInstanceId, fromIndex, maxResults); try { return persistenceService.selectList(selectDescriptor); } catch (final SBonitaReadException e) { throw new SEventInstanceReadException(e); } } @Override public SWaitingErrorEvent getBoundaryWaitingErrorEvent(final long relatedActivityInstanceId, final String errorCode) throws SWaitingEventReadException { final QueryOptions queryOptions = new QueryOptions(0, 2, SWaitingErrorEvent.class, "id", OrderByType.ASC); SelectListDescriptor selectDescriptor; if (errorCode == null) { selectDescriptor = SelectDescriptorBuilder.getCaughtError(relatedActivityInstanceId, queryOptions); } else { selectDescriptor = SelectDescriptorBuilder.getCaughtError(relatedActivityInstanceId, errorCode, queryOptions); } SWaitingErrorEvent waitingError = null; try { final List selectList = persistenceService.selectList(selectDescriptor); if (selectList != null && !selectList.isEmpty()) { if (selectList.size() == 1) { waitingError = selectList.get(0); } else { final StringBuilder stb = new StringBuilder(); stb.append("Only one catch error event was expected to handle the error code "); stb.append(errorCode); stb.append(" in the activity instance with id "); stb.append(relatedActivityInstanceId + "."); throw new SWaitingEventReadException(stb.toString()); } } } catch (final SBonitaReadException e) { throw new SWaitingEventReadException(e); } return waitingError; } @Override public List getEventInstances(final long rootContainerId, final int fromIndex, final int maxResults, final String fieldName, final OrderByType orderByType) throws SEventInstanceReadException { final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getEventsFromRootContainer(rootContainerId, fromIndex, maxResults, fieldName, orderByType); try { return persistenceService.selectList(selectDescriptor); } catch (final SBonitaReadException e) { throw new SEventInstanceReadException(e); } } @Override public T getEventTriggerInstance(final Class entityClass, final long eventTriggerInstanceId) throws SEventTriggerInstanceReadException { try { return persistenceService.selectById( SelectDescriptorBuilder.getElementById(entityClass, entityClass.getSimpleName(), eventTriggerInstanceId)); } catch (final SBonitaReadException e) { throw new SEventTriggerInstanceReadException(e); } } @Override public int resetProgressMessageInstances() throws SMessageModificationException { try { return persistenceService.update(QUERY_RESET_PROGRESS_MESSAGE_INSTANCES); } catch (final SPersistenceException e) { throw new SMessageModificationException(e); } } @Override public int resetInProgressWaitingEvents() throws SWaitingEventModificationException { try { return persistenceService.update(QUERY_RESET_IN_PROGRESS_WAITING_EVENTS); } catch (final SPersistenceException e) { throw new SWaitingEventModificationException(e); } } @Override public List getMessageEventCouples(final int fromIndex, final int maxResults) throws SEventTriggerInstanceReadException { final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getMessageEventCouples(fromIndex, maxResults); try { return persistenceService.selectList(selectDescriptor); } catch (final SBonitaReadException e) { throw new SEventTriggerInstanceReadException(e); } } @Override public SMessageInstance getMessageInstance(final long messageInstanceId) throws SMessageInstanceReadException { try { return persistenceService .selectById(SelectDescriptorBuilder.getElementById(SMessageInstance.class, "MessageInstance", messageInstanceId)); } catch (final SBonitaReadException e) { throw new SMessageInstanceReadException(e); } } @Override public List getMessageInstanceIdOlderThanCreationDate(final long creationDate, QueryOptions queryOptions) throws SMessageInstanceReadException { try { if (queryOptions != null) { validateFiltersForGetMessage(queryOptions); } else { queryOptions = QueryOptions.ALL_RESULTS; } final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getMessageInstanceIdOlderThanCreationDate(creationDate, queryOptions); return persistenceService.selectList(selectDescriptor); } catch (final SBonitaReadException e) { throw new SMessageInstanceReadException(e); } } private void validateFiltersForGetMessage(QueryOptions queryOptions) { List notOkField = queryOptions.getFilters().stream() .filter(filterOption -> filterOption.getFilterOperationType() != FilterOperationType.EQUALS || !filterOption.getFieldName().equals("messageName") || filterOption.getPersistentClass() != SMessageInstance.class) .collect(Collectors.toList()); if (!notOkField.isEmpty()) { throw new IllegalArgumentException( "Unsupported filters " + notOkField + ", can only filter on messageName"); } } @Override public void deleteMessageInstanceByIds(List ids) throws SMessageModificationException { List> listAsFragment = ListUtils.partition(ids, IN_REQUEST_SIZE); for (List fragmentIds : listAsFragment) { try { Map parameters = new HashMap<>(); parameters.put("ids", fragmentIds); persistenceService.update("deleteMessageInstanceByIds", parameters); } catch (SPersistenceException e) { throw new SMessageModificationException(e); } } } @Override public long getNumberOfWaitingEvents(final Class entityClass, final QueryOptions countOptions) throws SBonitaReadException { return persistenceService.getNumberOfEntities(entityClass, countOptions, null); } @Override public List getStartWaitingEventsOfProcessDefinition(final long processDefinitionId) throws SBonitaReadException { // event sub-processes are not returned: return persistenceService.selectList(new SelectListDescriptor<>("getStartWaitingEvents", singletonMap("processDefinitionId", processDefinitionId), SWaitingEvent.class, QueryOptions.ALL_RESULTS)); } @Override public SWaitingMessageEvent getWaitingMessage(final long waitingMessageId) throws SWaitingEventReadException { try { return persistenceService.selectById( SelectDescriptorBuilder.getElementById(SWaitingMessageEvent.class, "WaitingMessageEvent", waitingMessageId)); } catch (final SBonitaReadException e) { throw new SWaitingEventReadException(e); } } @Override public void deleteWaitingEvent(final SWaitingEvent waitingEvent) throws SWaitingEventModificationException { try { recorder.recordDelete(new DeleteRecord(waitingEvent), EVENT_TRIGGER_INSTANCE); } catch (final SRecorderException e) { throw new SWaitingEventModificationException(e); } } @Override public List getWaitingEventsForFlowNodeId(long flowNodeInstanceId) throws SEventTriggerInstanceReadException { try { return persistenceService.selectList(new SelectListDescriptor<>("getWaitingEventsOfFlowNode", Collections.singletonMap("flowNodeInstanceId", flowNodeInstanceId), SWaitingEvent.class, new QueryOptions(0, 100))); } catch (final SBonitaReadException e) { throw new SEventTriggerInstanceReadException(e); } } @Override public List getAllWaitingEventsForProcessInstance(long processInstanceId, final int fromIndex, final int maxResults) throws SEventTriggerInstanceReadException { try { return persistenceService .selectList(new SelectListDescriptor<>("getWaitingEventsForProcessInstance", Collections.singletonMap("processInstanceId", processInstanceId), SWaitingEvent.class, new QueryOptions(fromIndex, maxResults))); } catch (final SBonitaReadException e) { throw new SEventTriggerInstanceReadException(e); } } @Override public List getWaitingSignalEvents(final String signalName, final int fromIndex, final int maxResults) throws SEventTriggerInstanceReadException { final SelectListDescriptor descriptor = SelectDescriptorBuilder .getListeningSignals(signalName, fromIndex, maxResults); try { return persistenceService.selectList(descriptor); } catch (final SBonitaReadException e) { throw new SEventTriggerInstanceReadException(e); } } @Override public SWaitingSignalEvent getWaitingSignalEvent(final long id) throws SEventTriggerInstanceReadException, SEventTriggerInstanceNotFoundException { final SelectByIdDescriptor descriptor = new SelectByIdDescriptor<>( SWaitingSignalEvent.class, id); try { SWaitingSignalEvent sWaitingSignalEvent = persistenceService.selectById(descriptor); if (sWaitingSignalEvent == null) { throw new SEventTriggerInstanceNotFoundException(id); } return sWaitingSignalEvent; } catch (final SBonitaReadException e) { throw new SEventTriggerInstanceReadException(e); } } @Override public Optional getTimerEventTriggerInstanceOfFlowNode(long flowNodeInstanceId) throws SBonitaReadException { return Optional.ofNullable(persistenceService.selectOne(new SelectOneDescriptor<>( "getEventTriggerInstances", singletonMap("eventInstanceId", flowNodeInstanceId), STimerEventTriggerInstance.class))); } @Override public long getNumberOfTimerEventTriggerInstances(final long processInstanceId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = singletonMap("processInstanceId", processInstanceId); return persistenceService.getNumberOfEntities(STimerEventTriggerInstance.class, "ByProcessInstance", queryOptions, parameters); } @Override public List searchTimerEventTriggerInstances(final long processInstanceId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = singletonMap("processInstanceId", processInstanceId); return persistenceService.searchEntity(STimerEventTriggerInstance.class, "ByProcessInstance", queryOptions, parameters); } @Override public List searchWaitingEvents(final Class entityClass, final QueryOptions searchOptions) throws SBonitaReadException { return persistenceService.searchEntity(entityClass, searchOptions, null); } @Override public void updateMessageInstance(final SMessageInstance messageInstance, final EntityUpdateDescriptor descriptor) throws SMessageModificationException { try { recorder.recordUpdate(UpdateRecord.buildSetFields(messageInstance, descriptor), MESSAGE_INSTANCE); } catch (final SRecorderException re) { throw new SMessageModificationException(re); } } @Override public void updateWaitingMessage(final SWaitingMessageEvent waitingMessageEvent, final EntityUpdateDescriptor descriptor) throws SWaitingEventModificationException { try { recorder.recordUpdate(UpdateRecord.buildSetFields(waitingMessageEvent, descriptor), MESSAGE_INSTANCE); } catch (final SRecorderException e) { throw new SWaitingEventModificationException(e); } } @Override public void updateEventTriggerInstance(final STimerEventTriggerInstance sTimerEventTriggerInstance, final EntityUpdateDescriptor descriptor) throws SEventTriggerInstanceModificationException { try { recorder.recordUpdate(UpdateRecord.buildSetFields(sTimerEventTriggerInstance, descriptor), EVENT_TRIGGER_INSTANCE); } catch (final SRecorderException e) { throw new SEventTriggerInstanceModificationException(e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceServiceImpl.java ================================================ /** * 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.core.process.instance.event.impl; import java.util.List; import java.util.Optional; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceDeletionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventReadException; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent; import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; public class EventInstanceServiceImpl implements EventInstanceService { private final DataInstanceService dataInstanceService; public static final String BONITA_BPMENGINE_MESSAGE_SENT = "bonita.bpmengine.message.sent"; private final Counter messageSentCounter; private final EventInstanceRepository eventInstanceRepository; public EventInstanceServiceImpl(EventInstanceRepository eventInstanceRepository, DataInstanceService dataInstanceService, MeterRegistry meterRegistry) { this.eventInstanceRepository = eventInstanceRepository; this.dataInstanceService = dataInstanceService; messageSentCounter = meterRegistry.counter(BONITA_BPMENGINE_MESSAGE_SENT); } @Override public void createEventInstance(SEventInstance eventInstance) throws SEventInstanceCreationException { this.eventInstanceRepository.createEventInstance(eventInstance); } @Override public void createTimerEventTriggerInstance(STimerEventTriggerInstance sEventTriggerInstance) throws SEventTriggerInstanceCreationException { this.eventInstanceRepository.createTimerEventTriggerInstance(sEventTriggerInstance); } @Override public void createMessageInstance(SMessageInstance messageInstance) throws SMessageInstanceCreationException { messageSentCounter.increment(); this.eventInstanceRepository.createMessageInstance(messageInstance); } @Override public void createWaitingEvent(SWaitingEvent sWaitingEvent) throws SWaitingEventCreationException { this.eventInstanceRepository.createWaitingEvent(sWaitingEvent); } @Override public void deleteEventTriggerInstance(STimerEventTriggerInstance sTimerEventTriggerInstance) throws SEventTriggerInstanceDeletionException { this.eventInstanceRepository.deleteEventTriggerInstance(sTimerEventTriggerInstance); } @Override public void deleteEventTriggerInstanceOfFlowNode(long flowNodeInstanceId) throws SBonitaReadException, SEventTriggerInstanceDeletionException { Optional timerEventTriggerInstanceOfFlowNode = getTimerEventTriggerInstanceOfFlowNode( flowNodeInstanceId); if (!timerEventTriggerInstanceOfFlowNode.isPresent()) { return; } deleteEventTriggerInstance(timerEventTriggerInstanceOfFlowNode.get()); } @Override public void deleteMessageInstance(SMessageInstance messageInstance) throws SMessageModificationException { this.eventInstanceRepository.deleteMessageInstance(messageInstance); } @Override public void deleteWaitingEvent(SWaitingEvent sWaitingEvent) throws SWaitingEventModificationException { this.eventInstanceRepository.deleteWaitingEvent(sWaitingEvent); } @Override public void deleteWaitingEvents(SFlowNodeInstance flowNodeInstance) throws SWaitingEventModificationException, SEventTriggerInstanceReadException { List waitingEvents; do { waitingEvents = this.eventInstanceRepository.getWaitingEventsForFlowNodeId(flowNodeInstance.getId()); for (final SWaitingEvent sWaitingEvent : waitingEvents) { deleteWaitingEvent(sWaitingEvent); } } while (waitingEvents.size() == 100); } @Override public void deleteWaitingEvents(SProcessInstance processInstance) throws SWaitingEventModificationException, SEventTriggerInstanceReadException { List waitingEvents; final int pageCount = 100; do { waitingEvents = this.eventInstanceRepository.getAllWaitingEventsForProcessInstance(processInstance.getId(), 0, pageCount); for (final SWaitingEvent sWaitingEvent : waitingEvents) { deleteWaitingEvent(sWaitingEvent); } } while (waitingEvents.size() == pageCount); } @Override public List getActivityBoundaryEventInstances(long id, int fromIndex, int maxResults) throws SEventInstanceReadException { return this.eventInstanceRepository.getActivityBoundaryEventInstances(id, fromIndex, maxResults); } @Override public SWaitingErrorEvent getBoundaryWaitingErrorEvent(long id, String catchingErrorCode) throws SWaitingEventReadException { return this.eventInstanceRepository.getBoundaryWaitingErrorEvent(id, catchingErrorCode); } @Override public List getEventInstances(long rootContainerId, int fromIndex, int maxResults, String fieldName, OrderByType orderByType) throws SEventInstanceReadException { return this.eventInstanceRepository.getEventInstances(rootContainerId, fromIndex, maxResults, fieldName, orderByType); } @Override public T getEventTriggerInstance(Class entityClass, long eventTriggerInstanceId) throws SEventTriggerInstanceReadException { return this.eventInstanceRepository.getEventTriggerInstance(entityClass, eventTriggerInstanceId); } @Override public int resetProgressMessageInstances() throws SMessageModificationException { return this.eventInstanceRepository.resetProgressMessageInstances(); } @Override public int resetInProgressWaitingEvents() throws SWaitingEventModificationException { return this.eventInstanceRepository.resetInProgressWaitingEvents(); } @Override public List getMessageEventCouples(int i, int maxCouples) throws SEventTriggerInstanceReadException { return this.eventInstanceRepository.getMessageEventCouples(i, maxCouples); } @Override public SMessageInstance getMessageInstance(long messageInstanceId) throws SMessageInstanceReadException { return this.eventInstanceRepository.getMessageInstance(messageInstanceId); } @Override public long getNumberOfWaitingEvents(final Class sWaitingEventClass, QueryOptions searchOptions) throws SBonitaReadException { return this.eventInstanceRepository.getNumberOfWaitingEvents(sWaitingEventClass, searchOptions); } @Override public List getStartWaitingEventsOfProcessDefinition(long processDefinitionId) throws SBonitaReadException { return this.eventInstanceRepository.getStartWaitingEventsOfProcessDefinition(processDefinitionId); } @Override public SWaitingMessageEvent getWaitingMessage(long waitingMessageId) throws SWaitingEventReadException { return this.eventInstanceRepository.getWaitingMessage(waitingMessageId); } @Override public List getWaitingSignalEvents(String signalName, int fromIndex, int maxResults) throws SEventTriggerInstanceReadException { return this.eventInstanceRepository.getWaitingSignalEvents(signalName, fromIndex, maxResults); } @Override public SWaitingSignalEvent getWaitingSignalEvent(long signalId) throws SEventTriggerInstanceReadException, SEventTriggerInstanceNotFoundException { return this.eventInstanceRepository.getWaitingSignalEvent(signalId); } @Override public Optional getTimerEventTriggerInstanceOfFlowNode(long flowNodeInstanceId) throws SBonitaReadException { return this.eventInstanceRepository.getTimerEventTriggerInstanceOfFlowNode(flowNodeInstanceId); } @Override public long getNumberOfTimerEventTriggerInstances(long processInstanceId, QueryOptions searchOptions) throws SBonitaReadException { return this.eventInstanceRepository.getNumberOfTimerEventTriggerInstances(processInstanceId, searchOptions); } @Override public List searchTimerEventTriggerInstances(long processInstanceId, QueryOptions searchOptions) throws SBonitaReadException { return this.eventInstanceRepository.searchTimerEventTriggerInstances(processInstanceId, searchOptions); } @Override public List searchWaitingEvents(Class waitingEventClass, QueryOptions queryOptions) throws SBonitaReadException { return this.eventInstanceRepository.searchWaitingEvents(waitingEventClass, queryOptions); } @Override public void updateMessageInstance(SMessageInstance messageInstance, EntityUpdateDescriptor descriptor) throws SMessageModificationException { this.eventInstanceRepository.updateMessageInstance(messageInstance, descriptor); } @Override public void updateWaitingMessage(SWaitingMessageEvent waitingMsg, EntityUpdateDescriptor descriptor) throws SWaitingEventModificationException { this.eventInstanceRepository.updateWaitingMessage(waitingMsg, descriptor); } @Override public Integer deleteMessageAndDataInstanceOlderThanCreationDate(long creationDate, QueryOptions queryOptions) throws SMessageModificationException { try { List messageInstanceIdOlderThanCreationDate = eventInstanceRepository .getMessageInstanceIdOlderThanCreationDate(creationDate, queryOptions); if (messageInstanceIdOlderThanCreationDate.size() > 0) { eventInstanceRepository.deleteMessageInstanceByIds(messageInstanceIdOlderThanCreationDate); for (Long messageId : messageInstanceIdOlderThanCreationDate) { dataInstanceService.deleteLocalDataInstances(messageId, DataInstanceContainer.MESSAGE_INSTANCE.name(), true); dataInstanceService.deleteLocalArchivedDataInstances(messageId, DataInstanceContainer.MESSAGE_INSTANCE.name()); } } return messageInstanceIdOlderThanCreationDate.size(); } catch (SDataInstanceException | SEventTriggerInstanceReadException | SMessageInstanceReadException e) { throw new SMessageModificationException(e); } } @Override public List getMessageInstanceIdOlderThanCreationDate(long creationDate, QueryOptions queryOptions) throws SEventTriggerInstanceReadException, SMessageInstanceReadException { return this.eventInstanceRepository.getMessageInstanceIdOlderThanCreationDate(creationDate, queryOptions); } @Override public void updateEventTriggerInstance(STimerEventTriggerInstance sTimerEventTriggerInstance, EntityUpdateDescriptor descriptor) throws SEventTriggerInstanceModificationException { this.eventInstanceRepository.updateEventTriggerInstance(sTimerEventTriggerInstance, descriptor); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/ActivityInstanceServiceImpl.java ================================================ /** * 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.core.process.instance.impl; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.CollectionUtil; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.SHumanTaskAlreadyAssignedException; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping; import org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.persistence.search.FilterOperationType; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteAllRecord; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.SPersistenceException; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Hongwen Zang * @author Emmanuel Duchastenier * @author Yanyan Liu * @author Baptiste Mesta * @author Celine Souchet */ @Slf4j public class ActivityInstanceServiceImpl extends FlowNodeInstancesServiceImpl implements ActivityInstanceService { private static final String ASSIGNED_AND_PENDING_BY_ROOT_PROCESS = "AssignedAndPendingByRootProcess"; private static final String ASSIGNED_AND_PENDING_BY_ROOT_PROCESS_FOR = "AssignedAndPendingByRootProcessFor"; private static final String ASSIGNED_AND_PENDING = "AssignedAndPending"; private static final String SUPERVISED_BY = "SupervisedBy"; private static final String MANAGED_BY = "ManagedBy"; private static final String PENDING_MANAGED_BY = "PendingManagedBy"; private static final String PENDING_SUPERVISED_BY = "PendingSupervisedBy"; private static final String PENDING_FOR_USER = "PendingForUser"; private static final String PENDING_OR_ASSIGNED = "PendingOrAssigned"; private static final String PENDING_OR_ASSIGNED_OR_ASSIGNED_TO_OTHERS = "PendingOrAssignedOrAssignedToOthers"; private static final String PENDING_ASSIGNED_TO = "PendingAssignedTo"; private static final String HUMAN_TASK_INSTANCE_ASSIGNEE = "HUMAN_TASK_INSTANCE_ASSIGNEE"; private static final String QUERY_HUMAN_TASK_INSTANCE_ASSIGNEE = "updateStrictHuman"; private static final String WHOCANSTART_PENDING_TASK_SUFFIX = "WhoCanStartPendingTask"; private static final int BATCH_SIZE = 100; private final SUserTaskInstanceBuilderFactory sUserTaskInstanceBuilder; private final SMultiInstanceActivityInstanceBuilderFactory sMultiInstanceActivityInstanceBuilder; public ActivityInstanceServiceImpl(final Recorder recorder, final PersistenceService persistenceService, final ArchiveService archiveService) { super(recorder, persistenceService, archiveService); sUserTaskInstanceBuilder = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class); sMultiInstanceActivityInstanceBuilder = BuilderFactory.get(SMultiInstanceActivityInstanceBuilderFactory.class); } @Override public void createActivityInstance(final SActivityInstance activityInstance) throws SActivityCreationException { try { getRecorder().recordInsert(new InsertRecord(activityInstance), ACTIVITYINSTANCE); } catch (final SRecorderException e) { throw new SActivityCreationException(e); } if (log.isDebugEnabled()) { final StringBuilder stb = new StringBuilder(); stb.append("Created "); stb.append(activityInstance.getType().getValue()); stb.append(" <"); stb.append(activityInstance.getName()); stb.append("> with id = <"); stb.append(activityInstance.getId()); if (activityInstance.getParentActivityInstanceId() > 0) { stb.append(">, parent activity instance id = <"); stb.append(activityInstance.getParentActivityInstanceId()); } stb.append(">, parent process instance id = <"); stb.append(activityInstance.getParentProcessInstanceId()); stb.append(">, root process instance id = <"); stb.append(activityInstance.getRootProcessInstanceId()); stb.append(">, process definition id = <"); stb.append(activityInstance.getProcessDefinitionId()); stb.append(">"); log.debug(stb.toString()); } } @Override public void addPendingActivityMappings(final SPendingActivityMapping mapping) throws SActivityCreationException { try { getRecorder().recordInsert(new InsertRecord(mapping), PENDINGACTIVITYMAPPING); } catch (final SRecorderException e) { throw new SActivityCreationException(e); } } @Override public void deletePendingMappings(final long humanTaskInstanceId) throws SActivityModificationException { try { List mappings = null; final QueryOptions queryOptions = new QueryOptions(0, BATCH_SIZE, SPendingActivityMapping.class, "id", OrderByType.ASC); while (!(mappings = getPendingMappings(humanTaskInstanceId, queryOptions)).isEmpty()) { deletePendingMappings(mappings); } } catch (final SBonitaException e) { throw new SActivityModificationException(e); } } private void deletePendingMappings(final List mappings) throws SRecorderException { for (final SPendingActivityMapping mapping : mappings) { getRecorder().recordDelete(new DeleteRecord(mapping), PENDINGACTIVITYMAPPING); } } @Override public void deleteAllPendingMappings() throws SActivityModificationException { try { final FilterOption filterOption = new FilterOption(SPendingActivityMapping.class, SPendingActivityMapping.ACTOR_ID, -1L); final DeleteAllRecord record = new DeleteAllRecord(SPendingActivityMapping.class, Collections.singletonList(filterOption)); getRecorder().recordDeleteAll(record); } catch (final SRecorderException e) { throw new SActivityModificationException("Can't delete all pending mappings not attached to an actor.", e); } } /** * @param humanTaskInstanceId * @param queryOptions * @return */ @Override public List getPendingMappings(final long humanTaskInstanceId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = CollectionUtil.buildSimpleMap("activityId", humanTaskInstanceId); return getPersistenceService().selectList( new SelectListDescriptor("getPendingMappingsOfTask", parameters, SPendingActivityMapping.class, queryOptions)); } @Override public SActivityInstance getActivityInstance(final long activityInstanceId) throws SActivityInstanceNotFoundException, SActivityReadException { try { final SActivityInstance activity = getPersistenceService().selectById( SelectDescriptorBuilder.getElementById(SActivityInstance.class, "SActivityInstance", activityInstanceId)); if (activity == null) { throw new SActivityInstanceNotFoundException(activityInstanceId); } return activity; } catch (final SBonitaReadException e) { throw new SActivityReadException(e); } } @Override public SHumanTaskInstance getHumanTaskInstance(final long activityInstanceId) throws SActivityInstanceNotFoundException, SActivityReadException { final SelectByIdDescriptor descriptor = SelectDescriptorBuilder.getElementById( SHumanTaskInstance.class, "SHumanTaskInstance", activityInstanceId); SHumanTaskInstance humanTask; try { humanTask = getPersistenceService().selectById(descriptor); if (humanTask == null) { throw new SActivityInstanceNotFoundException(activityInstanceId); } return humanTask; } catch (final SBonitaReadException e) { throw new SActivityReadException(e.getMessage()); } } @Override public List getActivitiesWithStates(final long rootContainerId, final Set stateIds, final int fromIndex, final int maxResults, final String sortingField, final OrderByType sortingOrder) throws SActivityReadException { final HashMap parameters = new HashMap(); parameters.put("rootContainerId", rootContainerId); parameters.put("stateIds", stateIds); final SelectListDescriptor elements = SelectDescriptorBuilder.getSpecificQueryWithParameters( SActivityInstance.class, "getActivitiesWithStates", parameters, new QueryOptions(fromIndex, maxResults, SActivityInstance.class, sortingField, sortingOrder)); try { return getPersistenceService().selectList(elements); } catch (final SBonitaReadException e) { throw new SActivityReadException(e); } } @Override public List getOpenActivityInstances(final long rootContainerId, final int pageIndex, final int maxResults, final String sortingField, final OrderByType orderbyType) throws SActivityReadException { final Map parameters = Collections.singletonMap("rootContainerId", rootContainerId); final QueryOptions queryOptions = new QueryOptions(pageIndex * maxResults, maxResults, SActivityInstance.class, sortingField, orderbyType); final SelectListDescriptor elements = SelectDescriptorBuilder.getSpecificQueryWithParameters( SActivityInstance.class, "getOpenActivitiesFromProcessInstance", parameters, queryOptions); try { return getPersistenceService().selectList(elements); } catch (final SBonitaReadException e) { throw new SActivityReadException(e); } } @Override public SAActivityInstance getMostRecentArchivedActivityInstance(final long activityInstanceId) throws SActivityReadException, SActivityInstanceNotFoundException { final ReadPersistenceService persistenceService = getArchiveService() .getDefinitiveArchiveReadPersistenceService(); final SelectOneDescriptor descriptor = SelectDescriptorBuilder .getMostRecentArchivedActivityInstance(activityInstanceId); try { final SAActivityInstance activity = persistenceService.selectOne(descriptor); if (activity == null) { throw new SActivityInstanceNotFoundException(activityInstanceId); } return activity; } catch (final SBonitaReadException e) { throw new SActivityReadException(e); } } @Override public List getArchivedActivityInstances(final long rootContainerId, final QueryOptions queryOptions) throws SActivityReadException { final ReadPersistenceService persistenceService = getArchiveService() .getDefinitiveArchiveReadPersistenceService(); try { final List activities = persistenceService .selectList(SelectDescriptorBuilder.getArchivedActivitiesFromProcessInstance( rootContainerId, queryOptions)); return getUnmodifiableList(activities); } catch (final SBonitaReadException e) { throw new SActivityReadException(e); } } @Override public List getPendingTasks(final long userId, final Set actorIds, final int fromIndex, final int maxResults, final String sortFieldName, final OrderByType order) throws SActivityReadException { try { final SelectListDescriptor selectListDescriptor; if (actorIds.isEmpty()) { selectListDescriptor = SelectDescriptorBuilder.getPendingUserTasks(userId, fromIndex, maxResults, sortFieldName, order); } else { selectListDescriptor = SelectDescriptorBuilder.getPendingUserTasks(userId, actorIds, fromIndex, maxResults, sortFieldName, order); } return getPersistenceService().selectList(selectListDescriptor); } catch (final SBonitaReadException bre) { throw new SActivityReadException(bre); } } @Override public List getAssignedUserTasks(final long assigneeId, final int fromIndex, final int maxResults, final String sortFieldName, final OrderByType order) throws SActivityReadException { try { final SelectListDescriptor selectListDescriptor = SelectDescriptorBuilder .getAssignedUserTasks(assigneeId, fromIndex, maxResults, sortFieldName, order); return getPersistenceService().selectList(selectListDescriptor); } catch (final SBonitaReadException bre) { throw new SActivityReadException(bre); } } @Override public int getNumberOfOpenActivityInstances(final long rootContainerId) throws SActivityReadException { try { return getPersistenceService().selectOne(SelectDescriptorBuilder.getNumberOfOpenActivities(rootContainerId)) .intValue(); } catch (final SBonitaReadException e) { throw new SActivityReadException(e); } } @Override public List getActivityInstances(final long rootContainerId, final int fromIndex, final int numberOfResults) throws SActivityReadException { final SelectListDescriptor descriptor = SelectDescriptorBuilder .getActivitiesFromProcessInstance(rootContainerId, fromIndex, numberOfResults); try { final List selectList = getPersistenceService().selectList(descriptor); return getUnmodifiableList(selectList); } catch (final SBonitaReadException e) { throw new SActivityReadException(e); } } @Override public void assignHumanTask(final long userTaskId, final long userId) throws SFlowNodeNotFoundException, SFlowNodeReadException, SActivityModificationException { final SFlowNodeInstance flowNodeInstance = getFlowNodeInstance(userTaskId); if (flowNodeInstance instanceof SHumanTaskInstance) { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(sUserTaskInstanceBuilder.getAssigneeIdKey(), userId); if (userId > 0) { // if this action is a Assign action: descriptor.addField(sUserTaskInstanceBuilder.getClaimedDateKey(), System.currentTimeMillis()); } else { // if this action is a Release action: descriptor.addField(sUserTaskInstanceBuilder.getClaimedDateKey(), 0); } setLastUpdateDate(descriptor); try { getRecorder().recordUpdate(UpdateRecord.buildSetFields(flowNodeInstance, descriptor), HUMAN_TASK_INSTANCE_ASSIGNEE); } catch (final SRecorderException e) { throw new SActivityModificationException(e); } } else { throw new SActivityReadException("the activity with id " + userTaskId + " is not a user task"); } } @Override public void assignHumanTaskIfNotAssigned(final long userTaskId, final long userId) throws SFlowNodeNotFoundException, SFlowNodeReadException, SActivityModificationException, SHumanTaskAlreadyAssignedException { final SFlowNodeInstance flowNodeInstance = getFlowNodeInstance(userTaskId); if (flowNodeInstance instanceof SHumanTaskInstance) { long assigneeId = ((SHumanTaskInstance) flowNodeInstance).getAssigneeId(); if (assigneeId > 0 && assigneeId != userId && userId > 0) { throw new SHumanTaskAlreadyAssignedException( "The task with id " + userTaskId + " is currently assigned to" + " user with id " + assigneeId + ". Try to unassign before assigning it again.", null); } final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(sUserTaskInstanceBuilder.getIdKey(), userTaskId); descriptor.addField(sUserTaskInstanceBuilder.getAssigneeIdKey(), userId); if (userId > 0) { // if this action is a Assign action: descriptor.addField(sUserTaskInstanceBuilder.getClaimedDateKey(), System.currentTimeMillis()); } else { // if this action is a Release action: descriptor.addField(sUserTaskInstanceBuilder.getClaimedDateKey(), 0L); } setLastUpdateDate(descriptor); try { int updatedRows = getRecorder().recordUpdateWithQuery( UpdateRecord.buildSetFields(flowNodeInstance, descriptor), HUMAN_TASK_INSTANCE_ASSIGNEE, QUERY_HUMAN_TASK_INSTANCE_ASSIGNEE); if (updatedRows != 1) { throw new SHumanTaskAlreadyAssignedException( "The task with id " + userTaskId + " is currently assigned." + " Try to unassign before assigning it again.", null); } } catch (final SRecorderException e) { throw new SActivityModificationException(e); } } else { throw new SActivityReadException("the activity with id " + userTaskId + " is not a user task"); } } @Override public long getNumberOfAssignedHumanTaskInstances(final long userId) throws SActivityReadException { try { return getPersistenceService() .selectOne(SelectDescriptorBuilder.getNumberOfAssignedHumanTaskInstances(userId)); } catch (final SBonitaReadException e) { throw new SActivityReadException(e); } } @Override public SAActivityInstance getArchivedActivityInstance(final long activityInstanceId, final int stateId) throws SActivityReadException, SActivityInstanceNotFoundException { final ReadPersistenceService persistenceService = getArchiveService() .getDefinitiveArchiveReadPersistenceService(); SAActivityInstance selectOne; try { selectOne = persistenceService.selectOne(SelectDescriptorBuilder .getArchivedActivityInstanceWithActivityIdAndStateId(activityInstanceId, stateId)); } catch (final SBonitaReadException e) { throw new SActivityReadException(e); } if (selectOne == null) { throw new SActivityInstanceNotFoundException(activityInstanceId, stateId); } return selectOne; } @Override public long getNumberOfArchivedTasksManagedBy(final long managerUserId, final QueryOptions searchOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = getArchiveService() .getDefinitiveArchiveReadPersistenceService(); final Map parameters = Collections.singletonMap("managerUserId", managerUserId); return persistenceService.getNumberOfEntities(SAHumanTaskInstance.class, MANAGED_BY, searchOptions, parameters); } @Override public List searchArchivedTasksManagedBy(final long managerUserId, final QueryOptions searchOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = getArchiveService() .getDefinitiveArchiveReadPersistenceService(); final Map parameters = Collections.singletonMap("managerUserId", managerUserId); return persistenceService.searchEntity(SAHumanTaskInstance.class, MANAGED_BY, searchOptions, parameters); } @Override public long getNumberOfArchivedHumanTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("supervisorId", supervisorId); return getPersistenceService().getNumberOfEntities(SAHumanTaskInstance.class, SUPERVISED_BY, queryOptions, parameters); } @Override public long getNumberOfAssignedTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("supervisorId", supervisorId); queryOptions.getFilters() .add(new FilterOption(SHumanTaskInstance.class, "assigneeId", 0, FilterOperationType.GREATER)); return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, SUPERVISED_BY, queryOptions, parameters); } @Override public List searchAssignedTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("supervisorId", supervisorId); queryOptions.getFilters() .add(new FilterOption(SHumanTaskInstance.class, "assigneeId", 0, FilterOperationType.GREATER)); return getPersistenceService().searchEntity(SHumanTaskInstance.class, SUPERVISED_BY, queryOptions, parameters); } @Override public long getNumberOfHumanTasks(final QueryOptions queryOptions) throws SBonitaReadException { return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, queryOptions, null); } @Override public List searchHumanTasks(final QueryOptions queryOptions) throws SBonitaReadException { return getPersistenceService().searchEntity(SHumanTaskInstance.class, queryOptions, null); } @Override public List searchArchivedHumanTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("supervisorId", supervisorId); return getPersistenceService().searchEntity(SAHumanTaskInstance.class, SUPERVISED_BY, queryOptions, parameters); } @Override public List searchArchivedTasks(final QueryOptions searchOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = getArchiveService() .getDefinitiveArchiveReadPersistenceService(); return persistenceService.searchEntity(SAHumanTaskInstance.class, searchOptions, null); } @Override public long getNumberOfArchivedTasks(final QueryOptions searchOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = getArchiveService() .getDefinitiveArchiveReadPersistenceService(); return persistenceService.getNumberOfEntities(SAHumanTaskInstance.class, searchOptions, null); } @Override public long getNumberOfAssignedTasksManagedBy(final long managerUserId, final QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("managerUserId", managerUserId); return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, MANAGED_BY, searchOptions, parameters); } @Override public List searchAssignedTasksManagedBy(final long managerUserId, final QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("managerUserId", managerUserId); return getPersistenceService().searchEntity(SHumanTaskInstance.class, MANAGED_BY, searchOptions, parameters); } @Override public List searchPendingTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions) throws SBonitaReadException { try { final Map parameters = Collections.singletonMap("userId", supervisorId); return getPersistenceService().searchEntity(SHumanTaskInstance.class, PENDING_SUPERVISED_BY, queryOptions, parameters); } catch (final SBonitaReadException bre) { throw new SBonitaReadException(bre); } } @Override public long getNumberOfPendingTasksSupervisedBy(final long supervisorId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userId", supervisorId); return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, PENDING_SUPERVISED_BY, queryOptions, parameters); } @Override public Map getNumberOfOpenTasksForUsers(final List userIds) throws SBonitaReadException { if (userIds == null || userIds.isEmpty()) { return Collections.emptyMap(); } // get assigned tasks for each user final List> result = getPersistenceService() .selectList(SelectDescriptorBuilder.getNumbersOfAssignedOpenTasks(userIds)); final Map userTaskNumbermap = new HashMap(); for (final Map record : result) { userTaskNumbermap.put(record.get("userId"), record.get("numberOfTasks")); // "userId" and "numberOfTasks" are embed in mybatis/hibernate query // statements named "getNumbersOfOpenTasksForUsers" } // get number of pending tasks for each user for (final Long userId : userIds) { final long pendingCount = getNumberOfPendingTasksForUser(userId, new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS)); if (!userTaskNumbermap.containsKey(userId)) { userTaskNumbermap.put(userId, pendingCount); } else { userTaskNumbermap.put(userId, userTaskNumbermap.get(userId) + pendingCount); } } return userTaskNumbermap; } @Override public long searchNumberOfPendingTasksManagedBy(final long managerUserId, final QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("managerUserId", managerUserId); return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, PENDING_MANAGED_BY, searchOptions, parameters); } @Override public List searchPendingTasksManagedBy(final long managerUserId, final QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("managerUserId", managerUserId); return getPersistenceService().searchEntity(SHumanTaskInstance.class, PENDING_MANAGED_BY, searchOptions, parameters); } // FIXME synchronized on the object @Override public void incrementLoopCounter(final SLoopActivityInstance loopInstance) throws SActivityModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField("loopCounter", loopInstance.getLoopCounter() + 1); setLastUpdateDate(descriptor); try { getRecorder().recordUpdate(UpdateRecord.buildSetFields(loopInstance, descriptor), ACTIVITYINSTANCE_STATE); } catch (final SRecorderException sre) { throw new SActivityModificationException(sre); } } @Override public Map getNumberOfOverdueOpenTasksForUsers(final List userIds) throws SBonitaReadException { if (userIds == null || userIds.isEmpty()) { return Collections.emptyMap(); } // get assigned overdue open tasks for each user final List> result = getPersistenceService() .selectList(SelectDescriptorBuilder.getNumbersOfAssignedOverdueOpenTasks(userIds)); final Map userTaskNumbermap = new HashMap(); for (final Map record : result) { userTaskNumbermap.put(record.get("userId"), record.get("numberOfTasks")); // "userId" and "numberOfTasks" are embed in mybatis/hibernate query // statements named "getNumbersOfOpenTasksForUsers" } // get number of pending overdue open tasks for each user for (final Long userId : userIds) { final long pendingCount = getPersistenceService() .selectOne(SelectDescriptorBuilder.getNumberOfPendingOverdueOpenTasksForUser(userId)); if (!userTaskNumbermap.containsKey(userId)) { userTaskNumbermap.put(userId, pendingCount); } else { userTaskNumbermap.put(userId, userTaskNumbermap.get(userId) + pendingCount); } } return userTaskNumbermap; } @Override public List getChildrenOfAnActivity(final long parentActivityInstanceId, final int fromIndex, final int numberOfResults) throws SActivityReadException { final HashMap parameters = new HashMap(); parameters.put("parentActivityInstanceId", parentActivityInstanceId); final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfResults, SFlowNodeInstance.class, "id", OrderByType.ASC); final SelectListDescriptor descriptor = new SelectListDescriptor( "getChildrenOfAnActivity", parameters, SActivityInstance.class, queryOptions); try { return getPersistenceService().selectList(descriptor); } catch (final SBonitaReadException e) { throw new SActivityReadException(e); } } @Override public void setLoopMax(final SLoopActivityInstance loopActivity, final Integer loopMap) throws SActivityModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField("loopMax", loopMap); try { updateFlowNode(loopActivity, LOOPINSTANCE_LOOPMAX_MODIFIED, descriptor); } catch (final SFlowNodeModificationException e) { throw new SActivityModificationException(e); } } @Override public void setLoopCardinality(final SFlowNodeInstance flowNodeInstance, final int intLoopCardinality) throws SActivityModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(sMultiInstanceActivityInstanceBuilder.getLoopCardinalityKey(), intLoopCardinality); try { updateFlowNode(flowNodeInstance, MULTIINSTANCE_LOOPCARDINALITY_MODIFIED, descriptor); } catch (final SFlowNodeModificationException e) { throw new SActivityModificationException(e); } } @Override public void addMultiInstanceNumberOfActiveActivities(final SMultiInstanceActivityInstance flowNodeInstance, final int number) throws SActivityModificationException { updateMultiInstanceCounters(flowNodeInstance, number, "updateMultiInstanceActiveCounters", "active"); } @Override public void addMultiInstanceNumberOfTerminatedActivities(final SMultiInstanceActivityInstance flowNodeInstance, final int number) throws SActivityModificationException { updateMultiInstanceCounters(flowNodeInstance, number, "updateMultiInstanceTerminatedCounters", "terminated"); } @Override public void addMultiInstanceNumberOfCompletedActivities(final SMultiInstanceActivityInstance flowNodeInstance, final int number) throws SActivityModificationException { updateMultiInstanceCounters(flowNodeInstance, number, "updateMultiInstanceCompletedCounters", "completed"); } private void updateMultiInstanceCounters(final SMultiInstanceActivityInstance flowNodeInstance, final int number, final String queryName, final String counterDescription) throws SActivityModificationException { final long now = System.currentTimeMillis(); final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField("id", flowNodeInstance.getId()); descriptor.addField("number", number); descriptor.addField("lastUpdateDate", now); log.debug("Multi-instance '{}' (id={}, processInstance={}, processDefinition={}): " + "updating {} counters (atomic SQL, number={})", flowNodeInstance.getName(), flowNodeInstance.getId(), flowNodeInstance.getRootProcessInstanceId(), flowNodeInstance.getProcessDefinitionId(), counterDescription, number); try { final int updatedRows = getRecorder().recordUpdateWithQuery( UpdateRecord.buildSetFields(flowNodeInstance, descriptor), MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED, queryName); if (updatedRows != 1) { log.warn("Multi-instance '{}' (id={}, processInstance={}, processDefinition={}): " + "failed to update {} counters — entity not found or counter precondition failed " + "(expected 1 updated row but got {})", flowNodeInstance.getName(), flowNodeInstance.getId(), flowNodeInstance.getRootProcessInstanceId(), flowNodeInstance.getProcessDefinitionId(), counterDescription, updatedRows); throw new SActivityModificationException( String.format("Failed to update multi-instance %s counters for flow node %d: " + "entity not found or counter precondition failed (updated rows: %d)", counterDescription, flowNodeInstance.getId(), updatedRows)); } // Refresh entity from DB to sync in-memory state with the atomic SQL update. // This prevents Hibernate dirty-checking from overwriting the atomic values, // and keeps the entity managed in the session for downstream operations. getPersistenceService().refresh(flowNodeInstance); } catch (final SRecorderException | SPersistenceException e) { log.warn("Multi-instance '{}' (id={}, processInstance={}, processDefinition={}): " + "failed to update {} counters — {}", flowNodeInstance.getName(), flowNodeInstance.getId(), flowNodeInstance.getRootProcessInstanceId(), flowNodeInstance.getProcessDefinitionId(), counterDescription, e.getMessage(), e); throw new SActivityModificationException(e); } } @Override public long getNumberOfActivityInstances(final Class entityClass, final QueryOptions searchOptions) throws SBonitaReadException { return getPersistenceService().getNumberOfEntities(entityClass, searchOptions, null); } @SuppressWarnings("unchecked") @Override public List searchActivityInstances(final Class entityClass, final QueryOptions searchOptions) throws SBonitaReadException { return (List) getPersistenceService().searchEntity(entityClass, searchOptions, null); } @Override public long getNumberOfArchivedActivityInstances(final Class entityClass, final QueryOptions searchOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = getArchiveService() .getDefinitiveArchiveReadPersistenceService(); return persistenceService.getNumberOfEntities(entityClass, searchOptions, null); } @SuppressWarnings("unchecked") @Override public List searchArchivedActivityInstances( final Class entityClass, final QueryOptions searchOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = getArchiveService() .getDefinitiveArchiveReadPersistenceService(); return (List) persistenceService.searchEntity(entityClass, searchOptions, null); } @Override public void setTokenCount(final SActivityInstance activityInstance, final int tokenCount) throws SFlowNodeModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(sUserTaskInstanceBuilder.getTokenCountKey(), tokenCount); setLastUpdateDate(descriptor); try { getRecorder().recordUpdate(UpdateRecord.buildSetFields(activityInstance, descriptor), ACTIVITY_INSTANCE_TOKEN_COUNT); } catch (final SRecorderException e) { throw new SFlowNodeModificationException(e); } } @Override public long getNumberOfPendingTasksForUser(final long userId, final QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userId", userId); return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, PENDING_FOR_USER, searchOptions, parameters); } @Override public List searchPendingTasksForUser(final long userId, final QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userId", userId); return getPersistenceService().searchEntity(SHumanTaskInstance.class, PENDING_FOR_USER, searchOptions, parameters); } @Override public List searchPendingTasksAssignedTo(long userId, QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userId", userId); return getPersistenceService().searchEntity(SHumanTaskInstance.class, PENDING_ASSIGNED_TO, searchOptions, parameters); } @Override public long getNumberOfPendingOrAssignedTasks(final long userId, final QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userId", userId); return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, PENDING_OR_ASSIGNED, searchOptions, parameters); } @Override public long getNumberOfPendingOrAssignedOrAssignedToOthersTasks(final long userId, final QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userId", userId); return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, PENDING_OR_ASSIGNED_OR_ASSIGNED_TO_OTHERS, searchOptions, parameters); } @Override public long getNumberOfPendingTasksAssignedTo(long userId, QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userId", userId); return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, PENDING_ASSIGNED_TO, searchOptions, parameters); } @Override public List searchPendingOrAssignedTasks(final long userId, final QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userId", userId); return getPersistenceService().searchEntity(SHumanTaskInstance.class, PENDING_OR_ASSIGNED, searchOptions, parameters); } @Override public List searchPendingOrAssignedOrAssignedToOthersTasks(final long userId, final QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userId", userId); return getPersistenceService().searchEntity(SHumanTaskInstance.class, PENDING_OR_ASSIGNED_OR_ASSIGNED_TO_OTHERS, searchOptions, parameters); } @Override public void setAbortedByBoundaryEvent(final SActivityInstance activityInstance, final long boundaryEventId) throws SActivityModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(sUserTaskInstanceBuilder.getAbortedByBoundaryEventIdKey(), boundaryEventId); setLastUpdateDate(descriptor); try { getRecorder().recordUpdate(UpdateRecord.buildSetFields(activityInstance, descriptor), STATE_CATEGORY); } catch (final SRecorderException sre) { throw new SActivityModificationException(sre); } } @Override public List getPossibleUserIdsOfPendingTasks(final long humanTaskInstanceId, final int startIndex, final int maxResults) throws SActivityReadException { final Map parameters = new HashMap(); parameters.put("humanTaskInstanceId", humanTaskInstanceId); final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults); final SelectListDescriptor elements = new SelectListDescriptor("getPossibleUserIdsOfPendingTasks", parameters, SActivityInstance.class, queryOptions); try { return getPersistenceService().selectList(elements); } catch (final SBonitaReadException e) { throw new SActivityReadException(e); } } @Override public boolean isTaskPendingForUser(final long humanTaskInstanceId, final long userId) throws SBonitaReadException { final Map parameters = new HashMap(); parameters.put("humanTaskInstanceId", humanTaskInstanceId); parameters.put("userId", userId); final SelectOneDescriptor elements = new SelectOneDescriptor("isTaskPendingForUser", parameters, SActivityInstance.class); Long aLong = getPersistenceService().selectOne(elements); return aLong == 1; } @Override public long getNumberOfUsersWhoCanExecutePendingHumanTaskDeploymentInfo(final long humanTaskInstanceId, final QueryOptions searchOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("humanTaskInstanceId", humanTaskInstanceId); return getPersistenceService().getNumberOfEntities(SUser.class, WHOCANSTART_PENDING_TASK_SUFFIX, searchOptions, parameters); } @Override public List searchUsersWhoCanExecutePendingHumanTaskDeploymentInfo(final long humanTaskInstanceId, final QueryOptions searchOptions) throws SBonitaReadException { try { final Map parameters = Collections.singletonMap("humanTaskInstanceId", humanTaskInstanceId); return getPersistenceService().searchEntity(SUser.class, WHOCANSTART_PENDING_TASK_SUFFIX, searchOptions, parameters); } catch (final SBonitaReadException bre) { throw new SBonitaReadException(bre); } } @Override public long getNumberOfAssignedAndPendingHumanTasksFor(final long rootProcessDefinitionId, final long userId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = new HashMap(); parameters.put("userId", userId); parameters.put("rootProcessDefinitionId", rootProcessDefinitionId); return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, ASSIGNED_AND_PENDING_BY_ROOT_PROCESS_FOR, queryOptions, parameters); } @Override public List searchAssignedAndPendingHumanTasksFor(final long rootProcessDefinitionId, final long userId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = new HashMap(); parameters.put("userId", userId); parameters.put("rootProcessDefinitionId", rootProcessDefinitionId); return getPersistenceService().searchEntity(SHumanTaskInstance.class, ASSIGNED_AND_PENDING_BY_ROOT_PROCESS_FOR, queryOptions, parameters); } @Override public long getNumberOfAssignedAndPendingHumanTasks(final long rootProcessDefinitionId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("rootProcessDefinitionId", rootProcessDefinitionId); return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, ASSIGNED_AND_PENDING_BY_ROOT_PROCESS, queryOptions, parameters); } @Override public List searchAssignedAndPendingHumanTasks(final long rootProcessDefinitionId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.singletonMap("rootProcessDefinitionId", rootProcessDefinitionId); return getPersistenceService().searchEntity(SHumanTaskInstance.class, ASSIGNED_AND_PENDING_BY_ROOT_PROCESS, queryOptions, parameters); } @Override public long getNumberOfAssignedAndPendingHumanTasks(final QueryOptions queryOptions) throws SBonitaReadException { return getPersistenceService().getNumberOfEntities(SHumanTaskInstance.class, ASSIGNED_AND_PENDING, queryOptions, Collections.emptyMap()); } @Override public List searchAssignedAndPendingHumanTasks(final QueryOptions queryOptions) throws SBonitaReadException { return getPersistenceService().searchEntity(SHumanTaskInstance.class, ASSIGNED_AND_PENDING, queryOptions, Collections.emptyMap()); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/BPMFailureServiceImpl.java ================================================ /** * 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.core.process.instance.impl; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.TreeMap; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.exception.ExceptionUtils; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.commons.exceptions.ExceptionContext; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SExceptionContext; import org.bonitasoft.engine.connector.ConnectorValidationException; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; import org.bonitasoft.engine.core.process.instance.model.SABPMFailure; import org.bonitasoft.engine.core.process.instance.model.SBPMFailure; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.SPersistenceException; import org.springframework.stereotype.Service; @Slf4j @Service public class BPMFailureServiceImpl implements BPMFailureService { private static final String TYPE_SEPARATOR = "::"; private static final String CONTEXT_PATH_SEPARATOR = "//"; private final PersistenceService persistenceService; private final ArchiveService archiveService; public BPMFailureServiceImpl(PersistenceService persistenceService, ArchiveService archiveService) { this.persistenceService = persistenceService; this.archiveService = archiveService; } @Override public SBPMFailure createFlowNodeFailure(SFlowNodeInstance flowNodeInstance, Failure failure) throws SPersistenceException { log.debug("Adding failure for flow node instance {}", flowNodeInstance.getId()); var bpmFailure = SBPMFailure.builder() .flowNodeInstanceId(flowNodeInstance.getId()) .processInstanceId(flowNodeInstance.getParentProcessInstanceId()) .rootProcessInstanceId(flowNodeInstance.getRootProcessInstanceId()) .processDefinitionId(flowNodeInstance.getProcessDefinitionId()) .scope(failure.scope()) .context(buildContext(failure.throwable())) .errorMessage(ExceptionUtils.getRootCauseMessage(failure.throwable())) .stackTrace(ExceptionUtils.getStackTrace(failure.throwable())) .build(); return persistenceService.insert(bpmFailure); } @Override public List getFlowNodeFailures(long flowNodeInstanceId, int maxResults) throws SBonitaReadException { final QueryOptions queryOptions = new QueryOptions(0, maxResults); final Map parameters = Map.ofEntries(Map.entry("flowNodeInstanceId", flowNodeInstanceId)); return persistenceService.selectList(new SelectListDescriptor<>("getFlowNodeFailures", parameters, SBPMFailure.class, queryOptions)); } @Override public void archiveFlowNodeFailures(long flowNodeInstanceId, long archiveDate) throws SBonitaException { log.debug("Archiving failures of flow node instance {}", flowNodeInstanceId); archiveService.recordInserts(archiveDate, getFlowNodeFailures(flowNodeInstanceId, Integer.MAX_VALUE) .stream() .map(SABPMFailure::new) .map(ArchiveInsertRecord::new) .toArray(ArchiveInsertRecord[]::new)); } @Override public void deleteFlowNodeFailures(long flowNodeInstanceId) throws SBonitaException { log.debug("Deleting failures of flow node instance {}", flowNodeInstanceId); persistenceService.delete(getFlowNodeFailures(flowNodeInstanceId, Integer.MAX_VALUE) .stream() .map(SBPMFailure::getId) .toList(), SBPMFailure.class); } @Override public void deleteArchivedFlowNodeFailures(List flowNodeInstanceIds) throws SBonitaException { if (!flowNodeInstanceIds.isEmpty()) { log.debug("Deleting archived failures of flow node instances {}", flowNodeInstanceIds); archiveService.deleteFromQuery("deleteArchivedBPMFailuresByFlowNodeInstanceIds", Map.ofEntries(Map.entry("flowNodeInstanceIds", flowNodeInstanceIds))); } } @Override public List getArchivedFlowNodeFailures(long flowNodeInstanceId, int maxResults) throws SBonitaReadException { final QueryOptions queryOptions = new QueryOptions(0, maxResults); final Map parameters = Map.ofEntries(Map.entry("flowNodeInstanceId", flowNodeInstanceId)); return persistenceService.selectList(new SelectListDescriptor<>("getArchivedFlowNodeFailures", parameters, SABPMFailure.class, queryOptions)); } @Override public SBPMFailure createProcessInstanceFailure(SProcessInstance processInstance, Failure failure) throws SPersistenceException { log.debug("Registering failure for process instance {}", processInstance.getId()); var bpmFailure = SBPMFailure.builder() .processInstanceId(processInstance.getId()) .processDefinitionId(processInstance.getProcessDefinitionId()) .rootProcessInstanceId(processInstance.getRootProcessInstanceId()) .scope(failure.scope()) .context(buildContext(failure.throwable())) .errorMessage(ExceptionUtils.getRootCauseMessage(failure.throwable())) .stackTrace(ExceptionUtils.getStackTrace(failure.throwable())) .build(); return persistenceService.insert(bpmFailure); } @Override public List getProcessInstanceFailures(long processInstanceId, int maxResults) throws SBonitaReadException { final QueryOptions queryOptions = new QueryOptions(0, maxResults); final Map parameters = Map.ofEntries(Map.entry("processInstanceId", processInstanceId)); return persistenceService.selectList(new SelectListDescriptor<>("getProcessInstanceFailures", parameters, SBPMFailure.class, queryOptions)); } @Override public void archiveProcessInstanceFailures(long processInstanceId, long archiveDate) throws SBonitaException { log.debug("Archiving failures of process instance {}", processInstanceId); archiveService.recordInserts(archiveDate, getProcessInstanceFailures(processInstanceId, Integer.MAX_VALUE) .stream() .map(SABPMFailure::new) .map(ArchiveInsertRecord::new) .toArray(ArchiveInsertRecord[]::new)); } @Override public void deleteProcessInstanceFailures(long processInstanceId) throws SBonitaException { log.debug("Deleting failures of process instance {}", processInstanceId); persistenceService.delete(getProcessInstanceFailures(processInstanceId, Integer.MAX_VALUE) .stream() .map(SBPMFailure::getId) .toList(), SBPMFailure.class); } @Override public void deleteArchivedProcessInstanceFailures(List processInstanceIds) throws SBonitaException { if (!processInstanceIds.isEmpty()) { log.debug("Deleting archived failures of process instances {}", processInstanceIds); archiveService.deleteFromQuery("deleteArchivedBPMFailuresByProcessInstanceIds", Map.ofEntries(Map.entry("processInstanceIds", processInstanceIds))); } } @Override public List getArchivedProcessInstanceFailures(long processInstanceId, int maxResults) throws SBonitaReadException { final QueryOptions queryOptions = new QueryOptions(0, maxResults); final Map parameters = Map.ofEntries(Map.entry("processInstanceId", processInstanceId)); return persistenceService .selectList(new SelectListDescriptor<>("getArchivedProcessInstanceFailures", parameters, SABPMFailure.class, queryOptions)); } @Override public List getChildProcessInstancesFailures(long rootProcessInstanceId, int maxResults) throws SBonitaReadException { final QueryOptions queryOptions = new QueryOptions(0, maxResults); final Map parameters = Map.ofEntries(Map.entry("rootProcessInstanceId", rootProcessInstanceId)); return persistenceService.selectList(new SelectListDescriptor<>("getChildProcessInstancesFailures", parameters, SBPMFailure.class, queryOptions)); } @Override public List getArchivedChildProcessInstancesFailures(long rootProcessInstanceId, int maxResults) throws SBonitaReadException { final QueryOptions queryOptions = new QueryOptions(0, maxResults); final Map parameters = Map.ofEntries(Map.entry("rootProcessInstanceId", rootProcessInstanceId)); return persistenceService .selectList(new SelectListDescriptor<>("getArchivedChildProcessInstancesFailures", parameters, SABPMFailure.class, queryOptions)); } private static String buildContext(Throwable e) { Map ctx = new TreeMap<>(); if (e instanceof ExceptionContext exceptionContext) { ctx = exceptionContext.getContext(); } var contextBuilder = new StringBuilder(); if (ctx.containsKey(SExceptionContext.MESSAGE_INSTANCE_NAME)) { contextBuilder.append("message") .append(TYPE_SEPARATOR) .append(ctx.get(SExceptionContext.MESSAGE_INSTANCE_NAME)); } addConnectorContext(contextBuilder, e, ctx); addTransitionContext(contextBuilder, ctx); var evaluationException = ExceptionUtils.throwableOfType(e, SExpressionEvaluationException.class); if (evaluationException != null) { addContextPathSeparator(contextBuilder); contextBuilder.append("expression") .append(TYPE_SEPARATOR) .append(evaluationException.getExpressionName()); } return contextBuilder.toString(); } private static void addTransitionContext(StringBuilder contextBuilder, Map ctx) { if (ctx.containsKey(SExceptionContext.TRANSITION_TARGET_FLOWNODE_NAME)) { if (ctx.containsKey(SExceptionContext.TRANSITION_NAME)) { contextBuilder.append(ctx.get(SExceptionContext.TRANSITION_NAME)); addContextPathSeparator(contextBuilder); } contextBuilder.append("to") .append(TYPE_SEPARATOR) .append(ctx.get(SExceptionContext.TRANSITION_TARGET_FLOWNODE_NAME)); } } private static void addConnectorContext(StringBuilder contextBuilder, Throwable e, Map ctx) { if (ctx.containsKey(SExceptionContext.CONNECTOR_NAME)) { addContextPathSeparator(contextBuilder); contextBuilder.append(ctx.get(SExceptionContext.CONNECTOR_NAME)); if (ctx.containsKey(SExceptionContext.CONNECTOR_DEFINITION_ID)) { contextBuilder.append(TYPE_SEPARATOR); contextBuilder.append(ctx.get(SExceptionContext.CONNECTOR_DEFINITION_ID)); } if (ctx.containsKey(SExceptionContext.CONNECTOR_ACTIVATION_EVENT)) { contextBuilder.append(TYPE_SEPARATOR); contextBuilder.append(ctx.get(SExceptionContext.CONNECTOR_ACTIVATION_EVENT).toString().toLowerCase()); } var operationExecutionException = ExceptionUtils.throwableOfType(e, SOperationExecutionException.class); if (operationExecutionException != null) { addContextPathSeparator(contextBuilder); contextBuilder.append("output"); } } if (ctx.containsKey(SExceptionContext.CONNECTOR_INPUT_NAME)) { addContextPathSeparator(contextBuilder); contextBuilder.append("input") .append(TYPE_SEPARATOR) .append(ctx.get(SExceptionContext.CONNECTOR_INPUT_NAME)); } var connectorValidationEx = ExceptionUtils.throwableOfType(e, ConnectorValidationException.class); if (connectorValidationEx != null) { addContextPathSeparator(contextBuilder); contextBuilder.append("input-validation"); } } private static void addContextPathSeparator(StringBuilder contextBuilder) { if (!contextBuilder.isEmpty()) { contextBuilder.append(CONTEXT_PATH_SEPARATOR); } } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/FlowNodeInstancesServiceImpl.java ================================================ /** * 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.core.process.instance.impl; import static java.util.Collections.singletonMap; import java.text.MessageFormat; import java.time.Duration; 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.Set; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeDeletionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter; import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.core.process.instance.model.STaskPriority; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAManualTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.PersistenceService; /** * @author Elias Ricken de Medeiros * @author Frederic Bouquet * @author Celine Souchet */ @Slf4j public abstract class FlowNodeInstancesServiceImpl implements FlowNodeInstanceService { private static final String SUPERVISED_BY = "SupervisedBy"; private final SUserTaskInstanceBuilderFactory activityInstanceKeyProvider; private final Recorder recorder; private final PersistenceService persistenceService; private final ArchiveService archiveService; public FlowNodeInstancesServiceImpl(final Recorder recorder, final PersistenceService persistenceService, final ArchiveService archiveService) { this.recorder = recorder; this.persistenceService = persistenceService; activityInstanceKeyProvider = BuilderFactory.get(SUserTaskInstanceBuilderFactory.class); this.archiveService = archiveService; } public ArchiveService getArchiveService() { return archiveService; } @Override public void setState(final SFlowNodeInstance flowNodeInstance, final FlowNodeState state) throws SFlowNodeModificationException { final long now = System.currentTimeMillis(); final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(activityInstanceKeyProvider.getPreviousStateIdKey(), flowNodeInstance.getStateId()); descriptor.addField(activityInstanceKeyProvider.getStateIdKey(), state.getId()); descriptor.addField(activityInstanceKeyProvider.getStateNameKey(), state.getName()); descriptor.addField(activityInstanceKeyProvider.getStableKey(), state.isStable()); descriptor.addField(activityInstanceKeyProvider.getTerminalKey(), state.isTerminal()); descriptor.addField(activityInstanceKeyProvider.getReachStateDateKey(), now); descriptor.addField(activityInstanceKeyProvider.getLastUpdateDateKey(), now); descriptor.addField(activityInstanceKeyProvider.getStateExecutingKey(), false); log.debug(MessageFormat.format("[{0} with id {1}] changed state {2}->{3}(new={4})", flowNodeInstance.getClass().getSimpleName(), flowNodeInstance.getId(), flowNodeInstance.getStateId(), state.getId(), state.getClass().getSimpleName())); updateOneField(flowNodeInstance, ACTIVITYINSTANCE_STATE, descriptor); } @Override public void setExecuting(final SFlowNodeInstance flowNodeInstance) throws SFlowNodeModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(activityInstanceKeyProvider.getStateExecutingKey(), true); log.debug(MessageFormat.format("[{0} with id {1}] have executing flag set to true", flowNodeInstance.getClass().getSimpleName(), flowNodeInstance.getId())); updateOneField(flowNodeInstance, ACTIVITYINSTANCE_STATE, descriptor); } @Override public void updateDisplayName(final SFlowNodeInstance flowNodeInstance, final String displayName) throws SFlowNodeModificationException { if (displayName != null && !displayName.equals(flowNodeInstance.getDisplayName())) { final String key = activityInstanceKeyProvider.getDisplayNameKey(); final String event = ACTIVITYINSTANCE_DISPLAY_NAME; updateOneField(flowNodeInstance, key, displayName, 255, event); } } private String getTruncated(final String value, final int maxLengh, final SFlowNodeInstance flowNodeInstance, final String key) { if (value.length() > maxLengh) { final String truncatedValue = value.substring(0, maxLengh); logTruncationWarning(value, truncatedValue, maxLengh, flowNodeInstance, key); return truncatedValue; } return value; } private void logTruncationWarning(final String value, final String truncatedValue, final int maxLengh, final SFlowNodeInstance flowNodeInstance, final String key) { if (log.isWarnEnabled()) { final StringBuilder stb = new StringBuilder(); stb.append("The field "); stb.append(key); stb.append(" is too long in the flow node instance [id: "); stb.append(flowNodeInstance.getId()); stb.append(", name: "); stb.append(flowNodeInstance.getName()); stb.append(", process instance id: "); stb.append(flowNodeInstance.getParentProcessInstanceId()); stb.append(", root process instance id: "); stb.append(flowNodeInstance.getRootProcessInstanceId()); stb.append("] and will be truncated to the max lengh ("); stb.append(maxLengh); stb.append("). The truncated value is: '"); stb.append(truncatedValue); stb.append("'. The original value was: '"); stb.append(value); stb.append("'."); final String message = stb.toString(); log.warn(message); } } private void updateOneField(final SFlowNodeInstance flowNodeInstance, final String attributeKey, final String attributeValue, final int maxLength, final String event) throws SFlowNodeModificationException { final String truncatedValue = getTruncated(attributeValue, maxLength, flowNodeInstance, attributeKey); final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(attributeKey, truncatedValue); updateOneField(flowNodeInstance, event, descriptor); } private void updateOneField(final SFlowNodeInstance flowNodeInstance, final String attributeKey, final Long attributeValue, final String event) throws SFlowNodeModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(attributeKey, attributeValue); updateOneField(flowNodeInstance, event, descriptor); } private void updateOneField(SFlowNodeInstance flowNodeInstance, String event, EntityUpdateDescriptor descriptor) throws SFlowNodeModificationException { setLastUpdateDate(descriptor); try { recorder.recordUpdate(UpdateRecord.buildSetFields(flowNodeInstance, descriptor), event); } catch (final SRecorderException e) { throw new SFlowNodeModificationException(e); } } @Override public void updateDisplayDescription(final SFlowNodeInstance flowNodeInstance, final String displayDescription) throws SFlowNodeModificationException { if (displayDescription != null && !displayDescription.equals(flowNodeInstance.getDisplayDescription())) { final String event = ACTIVITYINSTANCE_DISPLAY_DESCRIPTION; final String key = activityInstanceKeyProvider.getDisplayDescriptionKey(); updateOneField(flowNodeInstance, key, displayDescription, 255, event); } } @Override public void setTaskPriority(final SFlowNodeInstance flowNodeInstance, final STaskPriority priority) throws SFlowNodeModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(activityInstanceKeyProvider.getPriorityKey(), priority); updateOneField(flowNodeInstance, ACTIVITYINSTANCE_STATE, descriptor); } @Override public SFlowNodeInstance getFlowNodeInstance(final long flowNodeInstanceId) throws SFlowNodeNotFoundException, SFlowNodeReadException { SFlowNodeInstance selectOne; try { selectOne = persistenceService.selectById(SelectDescriptorBuilder.getElementById(SFlowNodeInstance.class, "SFlowNodeInstance", flowNodeInstanceId)); } catch (final SBonitaReadException e) { throw new SFlowNodeReadException(e); } if (selectOne == null) { throw new SFlowNodeNotFoundException(flowNodeInstanceId); } return selectOne; } @Override public List getAllChildrenOfProcessInstance(final long parentProcessInstanceId, final int fromIndex, final int maxResults) throws SBonitaReadException { return getUnmodifiableList( getPersistenceService().selectList(new SelectListDescriptor<>("getAllChildrenOfProcessInstance", singletonMap("parentProcessInstanceId", parentProcessInstanceId), SFlowNodeInstance.class, new QueryOptions(fromIndex, maxResults)))); } @Override public List getDirectChildrenOfProcessInstance(final long parentProcessInstanceId, final int fromIndex, final int maxResults) throws SBonitaReadException { return getUnmodifiableList(getPersistenceService().selectList(new SelectListDescriptor<>( "getDirectChildrenOfProcessInstance", singletonMap("parentProcessInstanceId", parentProcessInstanceId), SFlowNodeInstance.class, new QueryOptions(fromIndex, maxResults)))); } @Override public List getDirectChildrenOfActivityInstance(long parentActivityInstanceId, int fromIndex, int maxResults) throws SBonitaReadException { List selectList; final Map parameters = singletonMap("parentActivityInstanceId", parentActivityInstanceId); final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults); selectList = getPersistenceService() .selectList(new SelectListDescriptor<>("getDirectChildrenOfActivityInstance", parameters, SFlowNodeInstance.class, queryOptions)); return getUnmodifiableList(selectList); } @Override public List getArchivedFlowNodeInstances(final long rootContainerId, final int fromIndex, final int maxResults) throws SFlowNodeReadException { List selectList; try { selectList = getPersistenceService().selectList( SelectDescriptorBuilder.getArchivedFlowNodesFromProcessInstance(rootContainerId, fromIndex, maxResults)); } catch (final SBonitaReadException e) { throw new SFlowNodeReadException(e); } return getUnmodifiableList(selectList); } @Override public Set getSourceObjectIdsOfArchivedFlowNodeInstances(List sourceProcessInstanceIds) throws SBonitaReadException { return new HashSet<>(getPersistenceService() .selectList(new SelectListDescriptor<>("getSourceObjectIdsOfArchivedFlowNodeInstances", singletonMap("sourceProcessInstanceIds", sourceProcessInstanceIds), SAFlowNodeInstance.class, QueryOptions.countQueryOptions()))); } @Override public void deleteArchivedFlowNodeInstances(List sourceObjectIds) throws SBonitaException { archiveService.deleteFromQuery("deleteArchivedFlowNodeInstances", singletonMap("sourceObjectIds", sourceObjectIds)); } @Override public SAFlowNodeInstance getArchivedFlowNodeInstance(final long archivedFlowNodeInstanceId) throws SFlowNodeReadException, SFlowNodeNotFoundException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); SAFlowNodeInstance selectOne; try { selectOne = persistenceService.selectById( SelectDescriptorBuilder.getElementById(SAFlowNodeInstance.class, "SArchivedFlowNodeInstance", archivedFlowNodeInstanceId)); } catch (final SBonitaReadException e) { throw new SFlowNodeReadException(e); } if (selectOne == null) { throw new SFlowNodeNotFoundException(archivedFlowNodeInstanceId); } return selectOne; } @Override public T getLastArchivedFlowNodeInstance(final Class entityClass, final long sourceObjectFlowNodeInstanceId) throws SBonitaReadException { final SAManualTaskInstanceBuilderFactory builderFactory = BuilderFactory .get(SAManualTaskInstanceBuilderFactory.class); final FilterOption filterOption = new FilterOption(entityClass, builderFactory.getSourceObjectIdKey(), sourceObjectFlowNodeInstanceId); final List orderByOptions = new ArrayList<>(); orderByOptions.add(new OrderByOption(entityClass, builderFactory.getArchivedDateKey(), OrderByType.DESC)); orderByOptions.add(new OrderByOption(entityClass, builderFactory.getLastUpdateKey(), OrderByType.DESC)); final QueryOptions queryOptions = new QueryOptions(0, 1, orderByOptions, Collections.singletonList(filterOption), null); final List saFlowNodeInstances = searchArchivedFlowNodeInstances(entityClass, queryOptions); if (!saFlowNodeInstances.isEmpty()) { return saFlowNodeInstances.get(0); } return null; } @Override public void setStateCategory(final SFlowNodeInstance flowElementInstance, final SStateCategory stateCategory) throws SFlowNodeModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(activityInstanceKeyProvider.getStateCategoryKey(), stateCategory); updateFlowNode(flowElementInstance, STATE_CATEGORY, descriptor); } @Override public void setExecutedBy(final SFlowNodeInstance flowNodeInstance, final long userId) throws SFlowNodeModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(activityInstanceKeyProvider.getExecutedBy(), userId); updateFlowNode(flowNodeInstance, EXECUTED_BY_MODIFIED, descriptor); } @Override public void setExecutedBySubstitute(final SFlowNodeInstance flowNodeInstance, final long executerSubstituteId) throws SFlowNodeModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(activityInstanceKeyProvider.getExecutedBySubstitute(), executerSubstituteId); updateFlowNode(flowNodeInstance, EXECUTED_BY_SUBSTITUTE_MODIFIED, descriptor); } @Override public void setExpectedEndDate(final SFlowNodeInstance flowNodeInstance, final Long dueDate) throws SFlowNodeModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(activityInstanceKeyProvider.getExpectedEndDateKey(), dueDate); updateFlowNode(flowNodeInstance, EXPECTED_END_DATE_MODIFIED, descriptor); } protected void updateFlowNode(final SFlowNodeInstance flowNodeInstance, final String eventName, final EntityUpdateDescriptor descriptor) throws SFlowNodeModificationException { setLastUpdateDate(descriptor); try { getRecorder().recordUpdate(UpdateRecord.buildSetFields(flowNodeInstance, descriptor), eventName); } catch (final SRecorderException sre) { throw new SFlowNodeModificationException(sre); } } /** * Adds the lastUpdateDate field to the descriptor if not already present. * This ensures that any update to a flow node instance also updates the lastUpdateDate. * If another date field (reachStateDate, claimedDate) is already in the descriptor with a * timestamp value, that value is reused to ensure consistency across date fields in the same update. */ protected void setLastUpdateDate(final EntityUpdateDescriptor descriptor) { if (!descriptor.getFields().containsKey(activityInstanceKeyProvider.getLastUpdateDateKey())) { Long timestamp = findExistingTimestamp(descriptor); if (timestamp == null) { timestamp = System.currentTimeMillis(); } descriptor.addField(activityInstanceKeyProvider.getLastUpdateDateKey(), timestamp); } } /** * Looks for an existing timestamp value from known date fields in the descriptor. * Returns the timestamp if found, or null if no date field is present. */ private Long findExistingTimestamp(final EntityUpdateDescriptor descriptor) { // Check reachStateDate (used in setState) Object reachStateDate = descriptor.getFields().get(activityInstanceKeyProvider.getReachStateDateKey()); if (reachStateDate instanceof Long && (Long) reachStateDate > 0) { return (Long) reachStateDate; } // Check claimedDate (used in assignHumanTask). // Only reuse if > 0: when a task is released (unassigned), claimedDate is set to 0, // which is not a valid timestamp. In that case, we return null to get a fresh timestamp. Object claimedDate = descriptor.getFields().get(activityInstanceKeyProvider.getClaimedDateKey()); if (claimedDate instanceof Long && (Long) claimedDate > 0) { return (Long) claimedDate; } return null; } protected List getUnmodifiableList(List selectList) { if (selectList == null) { selectList = new ArrayList<>(); } return Collections.unmodifiableList(selectList); } @Override public long getNumberOfFlowNodeInstances(final Class entityClass, final QueryOptions countOptions) throws SBonitaReadException { return getPersistenceService().getNumberOfEntities(entityClass, countOptions, null); } @Override public List searchFlowNodeInstances(final Class entityClass, final QueryOptions searchOptions) throws SBonitaReadException { return getPersistenceService().searchEntity(entityClass, searchOptions, null); } @Override public long getNumberOfFlowNodeInstancesSupervisedBy(final Long supervisorId, final Class entityClass, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = singletonMap("supervisorId", (Object) supervisorId); return getPersistenceService().getNumberOfEntities(entityClass, SUPERVISED_BY, queryOptions, parameters); } @Override public List searchFlowNodeInstancesSupervisedBy(final Long supervisorId, final Class entityClass, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = singletonMap("supervisorId", (Object) supervisorId); return getPersistenceService().searchEntity(entityClass, SUPERVISED_BY, queryOptions, parameters); } @Override public List getNumberOfFlownodesOfProcessDefinitionInAllStates( final long processDefinitionId) throws SBonitaReadException { final HashMap parameters = new HashMap<>(2); parameters.put("processDefinitionId", processDefinitionId); final List result = persistenceService.selectList(new SelectListDescriptor<>( "getNumberOfFlowNodesOfProcessDefinitionInAllStates", parameters, SFlowNodeInstance.class, new QueryOptions(0, Integer.MAX_VALUE))); if (result != null && result.size() > 0) { return result; } return Collections.emptyList(); } @Override public List getNumberOfFlownodesInAllStates(final long parentProcessInstanceId) throws SBonitaReadException { final HashMap parameters = new HashMap<>(2); parameters.put("parentProcessInstanceId", parentProcessInstanceId); final List result = persistenceService.selectList(new SelectListDescriptor<>( "getNumberOfFlowNodesInAllStates", parameters, SFlowNodeInstance.class, new QueryOptions(0, Integer.MAX_VALUE))); if (result != null && result.size() > 0) { return result; } return Collections.emptyList(); } @Override public List getNumberOfArchivedFlownodesInAllStates( final long parentProcessInstanceId) throws SBonitaReadException { final HashMap parameters = new HashMap<>(2); parameters.put("parentProcessInstanceId", parentProcessInstanceId); final List result = persistenceService.selectList(new SelectListDescriptor<>( "getNumberOfArchivedFlowNodesInAllStates", parameters, SAFlowNodeInstance.class, new QueryOptions(0, Integer.MAX_VALUE))); if (result != null && result.size() > 0) { return result; } return Collections.emptyList(); } @Override public long getNumberOfArchivedFlowNodeInstances(final Class entityClass, final QueryOptions countOptions) throws SBonitaReadException { return getPersistenceService().getNumberOfEntities(entityClass, countOptions, null); } @Override public List searchArchivedFlowNodeInstances(final Class entityClass, final QueryOptions searchOptions) throws SBonitaReadException { return getPersistenceService().searchEntity(entityClass, searchOptions, null); } @Override public long getNumberOfArchivedFlowNodeInstancesSupervisedBy(final long supervisorId, final Class entityClass, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = singletonMap("supervisorId", (Object) supervisorId); return getPersistenceService().getNumberOfEntities(entityClass, SUPERVISED_BY, queryOptions, parameters); } @Override public List searchArchivedFlowNodeInstancesSupervisedBy(final long supervisorId, final Class entityClass, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = singletonMap("supervisorId", (Object) supervisorId); return getPersistenceService().searchEntity(entityClass, SUPERVISED_BY, queryOptions, parameters); } protected Recorder getRecorder() { return recorder; } protected PersistenceService getPersistenceService() { return persistenceService; } @Override public void deleteFlowNodeInstance(final SFlowNodeInstance sFlowNodeInstance) throws SFlowNodeDeletionException { try { recorder.recordDelete(new DeleteRecord(sFlowNodeInstance), FLOWNODE_INSTANCE); } catch (final SBonitaException e) { throw new SFlowNodeDeletionException(e); } } @Override public List getFlowNodeInstanceIdsToRecover(Duration considerElementsOlderThan, QueryOptions queryOptions) throws SBonitaReadException { final List selectList = getPersistenceService().selectList( new SelectListDescriptor<>("getFlowNodeInstanceIdsToRecover", singletonMap("maxLastUpdate", System.currentTimeMillis() - considerElementsOlderThan.toMillis()), SFlowNodeInstance.class, queryOptions)); return getUnmodifiableList(selectList); } @Override public List getGatewayInstanceIdsToRecover(Duration considerElementsOlderThan, final QueryOptions queryOptions) throws SBonitaReadException { final List selectList = getPersistenceService().selectList( new SelectListDescriptor<>("getGatewayInstanceIdsToRecover", singletonMap("maxLastUpdate", System.currentTimeMillis() - considerElementsOlderThan.toMillis()), SGatewayInstance.class, queryOptions)); return getUnmodifiableList(selectList); } @Override public List getFlowNodeInstancesByIds(List ids) throws SBonitaReadException { return getUnmodifiableList(getPersistenceService().selectList( new SelectListDescriptor<>("getFlowNodeInstancesByIds", singletonMap("ids", ids), SFlowNodeInstance.class, QueryOptions.ALL_RESULTS))); } @Override public int getNumberOfFlowNodes(final long parentProcessInstanceId) throws SBonitaReadException { return getPersistenceService().selectOne(SelectDescriptorBuilder.getNumberOfFlowNode(parentProcessInstanceId)) .intValue(); } @Override public List getFlowNodeInstancesByNameAndParentContainerId(String name, Long parentContainerId) throws SBonitaReadException { Map parameters = new HashMap<>(); parameters.put("name", name); parameters.put("parentContainerId", parentContainerId); return getPersistenceService().selectList( new SelectListDescriptor<>("getFlowNodeInstancesByNameAndParentContainerId", parameters, SFlowNodeInstance.class, QueryOptions.ALL_RESULTS)); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/GatewayInstanceServiceImpl.java ================================================ /** * 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.core.process.instance.impl; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; 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.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.api.GatewayInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayReadException; 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.builder.SGatewayInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; /** * @author Feng Hui * @author Zhao Na * @author Matthieu Chaffotte */ @Slf4j public class GatewayInstanceServiceImpl implements GatewayInstanceService { private final Recorder recorder; private final ReadPersistenceService persistenceRead; private final SGatewayInstanceBuilderFactory sGatewayInstanceBuilderFactory; private final FlowNodeInstanceService flowNodeInstanceService; public GatewayInstanceServiceImpl(final Recorder recorder, final ReadPersistenceService persistenceRead, FlowNodeInstanceService flowNodeInstanceService) { this.recorder = recorder; this.persistenceRead = persistenceRead; this.flowNodeInstanceService = flowNodeInstanceService; sGatewayInstanceBuilderFactory = BuilderFactory.get(SGatewayInstanceBuilderFactory.class); } @Override public void createGatewayInstance(final SGatewayInstance gatewayInstance) throws SGatewayCreationException { try { recorder.recordInsert(new InsertRecord(gatewayInstance), GATEWAYINSTANCE); } catch (final SRecorderException e) { throw new SGatewayCreationException(e); } if (log.isDebugEnabled()) { String stb = "Created gateway instance [name = <" + gatewayInstance.getName() + ">, id = <" + gatewayInstance.getId() + ">, parent process instance id = <" + gatewayInstance.getParentProcessInstanceId() + ">, root process instance id = <" + gatewayInstance.getRootProcessInstanceId() + ">, process definition id = <" + gatewayInstance.getRootProcessInstanceId() + ">]"; log.debug(stb); } } @Override public SGatewayInstance getGatewayInstance(final long gatewayInstanceId) throws SGatewayNotFoundException, SGatewayReadException { SGatewayInstance selectOne; try { selectOne = persistenceRead.selectById(SelectDescriptorBuilder.getElementById(SGatewayInstance.class, "SGatewayInstance", gatewayInstanceId)); } catch (final SBonitaReadException e) { throw new SGatewayReadException(e); } if (selectOne == null) { throw new SGatewayNotFoundException(gatewayInstanceId); } return selectOne; } @Override public boolean checkMergingCondition(final SProcessDefinition sDefinition, final SGatewayInstance gatewayInstance) throws SBonitaException { switch (gatewayInstance.getGatewayType()) { case EXCLUSIVE: return true; case INCLUSIVE: return isInclusiveGatewayActivated(sDefinition, gatewayInstance); case PARALLEL: return isParallelGatewayActivated(sDefinition, gatewayInstance); default: return false; } } boolean isInclusiveGatewayActivated(final SProcessDefinition sDefinition, final SGatewayInstance gatewayInstance) throws SBonitaReadException { log.debug("Evaluate if gateway " + gatewayInstance.getName() + " of instance " + gatewayInstance.getRootProcessInstanceId() + " of definition " + sDefinition.getName() + " must be activated "); log.debug("HitBys = " + gatewayInstance.getHitBys()); if (gatewayInstance.isFinished()) { return false; } SFlowElementContainerDefinition processContainer = sDefinition.getProcessContainer(); SFlowNodeDefinition gatewayDefinition = processContainer.getFlowNode(gatewayInstance.getFlowNodeDefinitionId()); long processInstanceId = gatewayInstance.getParentContainerId(); List hitByTransitionList = getHitByTransitionList(gatewayInstance); List incomingTransitions = gatewayDefinition.getIncomingTransitions(); List incomingWithTokens = new ArrayList<>(); List incomingWithoutTokens = new ArrayList<>(); for (int i = 0; i < incomingTransitions.size(); i++) { STransitionDefinition currentTransition = incomingTransitions.get(i); if (hitByTransitionList.contains(String.valueOf(i + 1))) { incomingWithTokens.add(currentTransition); } else { incomingWithoutTokens.add(currentTransition); } } /* * We create two lists that will contain transition definitions that have a path to the gateway finishing with * a token and the list of transition that does not have a path to the gateway finishing with a token */ List finishWithAToken = new ArrayList<>(); List doesNotFinishWithAToken = new ArrayList<>(); /* * we calculate all transition definitions that can block the gateway */ addBackwardReachableTransitions(processContainer, gatewayDefinition, incomingWithTokens, finishWithAToken, Collections. emptyList()); addBackwardReachableTransitions(processContainer, gatewayDefinition, incomingWithoutTokens, doesNotFinishWithAToken, finishWithAToken); /* * we check if one of the transitions that are 'blocking' contains a token in this process instance */ return !transitionsContainsAToken(doesNotFinishWithAToken, gatewayDefinition, processInstanceId, processContainer); } boolean transitionsContainsAToken(List transitions, SFlowNodeDefinition gatewayDefinition, long processInstanceId, SFlowElementContainerDefinition processContainer) throws SBonitaReadException { List sourceElements = new ArrayList<>(); List targetElements = new ArrayList<>(); log.debug("Check if there is a token on " + transitions); for (STransitionDefinition sTransitionDefinition : transitions) { SFlowNodeDefinition source = processContainer.getFlowNode(sTransitionDefinition.getSource()); if (!source.equals(gatewayDefinition) && !sourceElements.contains(source)) { sourceElements.add(source); } SFlowNodeDefinition target = processContainer.getFlowNode(sTransitionDefinition.getTarget()); if (!target.equals(gatewayDefinition) && !targetElements.contains(target)) { targetElements.add(target); } } List sourceAndTarget = extractElementThatAreSourceAndTarget(sourceElements, targetElements); if (containsToken(processInstanceId, sourceAndTarget, null)) return true; if (containsToken(processInstanceId, sourceElements, true)) return true; if (containsToken(processInstanceId, targetElements, false)) return true; log.debug("No token to wait, gateway will fire"); return false; } List extractElementThatAreSourceAndTarget(List sourceElements, List targetElements) { List sourceAndTarget = new ArrayList<>(); Iterator iterator = sourceElements.iterator(); while (iterator.hasNext()) { SFlowNodeDefinition sourceElement = iterator.next(); if (targetElements.contains(sourceElement) || sourceElement.getIncomingTransitions().isEmpty() /* * if it's a * start * element * it's also * considered * as a * target */) { iterator.remove(); targetElements.remove(sourceElement); sourceAndTarget.add(sourceElement); } } return sourceAndTarget; } /** * @param shouldBeTerminal * is true is the element should be in state terminal * is false if it should not be in terminal * is null if it should be either in terminal or not */ boolean containsToken(long processInstanceId, List elements, Boolean shouldBeTerminal) throws SBonitaReadException { for (SFlowNodeDefinition element : elements) { List sFlowNodeInstances = flowNodeInstanceService .getFlowNodeInstancesByNameAndParentContainerId(element.getName(), processInstanceId); for (SFlowNodeInstance sFlowNodeInstance : sFlowNodeInstances) { if (shouldBeTerminal == null || (shouldBeTerminal ^ !sFlowNodeInstance.isTerminal())) { log.debug("flow node " + sFlowNodeInstance.getName() + " contain a token, gateway not merged"); return true; } } } return false; } void addBackwardReachableTransitions(SFlowElementContainerDefinition processContainer, SFlowNodeDefinition gatewayDefinition, List transitions, List backwardReachable, List notIn) { for (STransitionDefinition sTransitionDefinition : transitions) { if (!backwardReachable.contains(sTransitionDefinition) && !notIn.contains(sTransitionDefinition)) { backwardReachable.add(sTransitionDefinition); //if the source is the gateway we stop searching backward SFlowNodeDefinition flowNode = processContainer.getFlowNode(sTransitionDefinition.getSource()); if (!flowNode.equals(gatewayDefinition)) { addBackwardReachableTransitions(processContainer, gatewayDefinition, flowNode.getIncomingTransitions(), backwardReachable, notIn); } } } } boolean isParallelGatewayActivated(final SProcessDefinition sDefinition, final SGatewayInstance gatewayInstance) { final List hitsBy = getHitByTransitionList(gatewayInstance); final List trans = getTransitionDefinitions(gatewayInstance, sDefinition); boolean go = true; int i = 1; while (go && i <= trans.size()) { go = hitsBy.contains(String.valueOf(i)); i++; } return go; } /** * @return the list of transition indexes that hit the gateway */ private List getHitByTransitionList(final SGatewayInstance gatewayInstance) { return Arrays.asList(gatewayInstance.getHitBys().split(",")); } protected List getTransitionDefinitions(final SGatewayInstance gatewayInstance, final SProcessDefinition processDefinition) { final SFlowElementContainerDefinition processContainer = processDefinition.getProcessContainer(); final SFlowNodeDefinition gatewayDefinition = processContainer .getFlowNode(gatewayInstance.getFlowNodeDefinitionId()); return gatewayDefinition.getIncomingTransitions(); } @Override public void setState(final SGatewayInstance gatewayInstance, final int stateId) throws SGatewayModificationException { updateOneColumnAndMetaData(gatewayInstance, sGatewayInstanceBuilderFactory.getStateIdKey(), stateId, GATEWAYINSTANCE_STATE); } @Override public void hitTransition(final SGatewayInstance gatewayInstance, final long transitionIndex) throws SGatewayModificationException { log.debug("Hit gateway " + gatewayInstance.getName() + " (" + gatewayInstance.getId() + ")" + " of instance " + gatewayInstance.getRootProcessInstanceId() + " with transition index " + transitionIndex); final String hitBys = gatewayInstance.getHitBys(); String columnValue; if (hitBys == null || hitBys.isEmpty()) { columnValue = String.valueOf(transitionIndex); } else { columnValue = hitBys + "," + transitionIndex; } updateOneColumnAndMetaData(gatewayInstance, sGatewayInstanceBuilderFactory.getHitBysKey(), columnValue, GATEWAYINSTANCE_HITBYS); } private void updateOneColumnAndMetaData(final SGatewayInstance gatewayInstance, final String columnName, final Serializable columnValue, final String event) throws SGatewayModificationException { final long now = System.currentTimeMillis(); final EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor(); entityUpdateDescriptor.addField(columnName, columnValue); entityUpdateDescriptor.addField("lastUpdateDate", now); entityUpdateDescriptor.addField("reachedStateDate", now); try { recorder.recordUpdate(UpdateRecord.buildSetFields(gatewayInstance, entityUpdateDescriptor), event); } catch (final SRecorderException e) { throw new SGatewayModificationException(e); } } @Override public SGatewayInstance getActiveGatewayInstanceOfTheProcess(final long parentProcessInstanceId, final String name) throws SGatewayNotFoundException, SGatewayReadException { try { return persistenceRead.selectOne( SelectDescriptorBuilder.getActiveGatewayInstanceOfProcess(parentProcessInstanceId, name)); } catch (final SBonitaReadException e) { throw new SGatewayReadException(e); } } /* * set the gateway to finish, then if there is some transition that arrived at the gateway multiple times (e.g. a * loop with an inclusive gateway) * it is possible that some of the token are not merged. * in that case a new gateway is created with these remaining token and we check if this new gateway is already * merged * new gateway that are already merged are returned */ @Override public List setFinishAndCreateNewGatewayForRemainingToken(SProcessDefinition processDefinition, final SGatewayInstance gatewayInstance) throws SBonitaException { List hitBys = getHitByTransitionList(gatewayInstance); List merged = getMergedTokens(gatewayInstance, hitBys); setFinished(gatewayInstance, merged.size()); List remaining = getRemainingTokens(hitBys, merged); log.debug("There is {} remaining token to merge on gateway {} will create a new if there is", remaining, gatewayInstance.getName()); if (remaining.isEmpty()) { return Collections.emptyList(); } List toFire = new ArrayList<>(); SGatewayInstance newGatewayInstance = createGatewayWithRemainingTokens(gatewayInstance, remaining); if (checkMergingCondition(processDefinition, newGatewayInstance)) { toFire.add(newGatewayInstance); //recursively add newly created gateway that are already merged toFire.addAll(setFinishAndCreateNewGatewayForRemainingToken(processDefinition, newGatewayInstance)); } return toFire; } List getMergedTokens(SGatewayInstance gatewayInstance, List hitBys) { List merged = null; switch (gatewayInstance.getGatewayType()) { case PARALLEL: merged = unique(hitBys); break; case INCLUSIVE: merged = unique(hitBys); break; case EXCLUSIVE: merged = Collections.singletonList(hitBys.get(0)); break; } return merged; } private ArrayList unique(List hitBys) { ArrayList merged = new ArrayList<>(); for (String hitBy : hitBys) { if (!merged.contains(hitBy)) { merged.add(hitBy); } } return merged; } ArrayList getRemainingTokens(List hitBys, List merged) { ArrayList remaining = new ArrayList<>(hitBys); for (String mergedToken : merged) { remaining.remove(mergedToken); } return remaining; } /** * create a new gateway instance with the remaining token * * @param gatewayInstance * the original gatewayInstance * @param remaining * the token that already hit the gateway * @return * the new gateway */ private SGatewayInstance createGatewayWithRemainingTokens(SGatewayInstance gatewayInstance, List remaining) throws SGatewayCreationException { SGatewayInstance sGatewayInstance = new SGatewayInstance(gatewayInstance); String remainingTokenAsString = ""; for (String token : remaining) { remainingTokenAsString += token + ","; } sGatewayInstance.setHitBys(remainingTokenAsString.substring(0, remainingTokenAsString.length() - 1)); createGatewayInstance(sGatewayInstance); return sGatewayInstance; } void setFinished(SGatewayInstance gatewayInstance, int numberOfTokenToMerge) throws SGatewayModificationException { final String columnValue = FINISH + numberOfTokenToMerge; log.trace("set finish on gateway {} {}", gatewayInstance.getName(), columnValue); updateOneColumnAndMetaData(gatewayInstance, sGatewayInstanceBuilderFactory.getHitBysKey(), columnValue, GATEWAYINSTANCE_HITBYS); } @Override public List getInclusiveGatewaysOfProcessInstanceThatShouldFire( SProcessDefinition processDefinition, long processInstanceId) throws SBonitaReadException { List sGatewayInstances = getInclusiveGatewayInstanceOfProcessInstance(processInstanceId); ArrayList shouldFire = new ArrayList<>(); for (SGatewayInstance sGatewayInstance : sGatewayInstances) { if (isInclusiveGatewayActivated(processDefinition, sGatewayInstance)) { shouldFire.add(sGatewayInstance); } } return shouldFire; } private List getInclusiveGatewayInstanceOfProcessInstance(long processInstanceId) throws SBonitaReadException { final HashMap hashMap = new HashMap<>(2); hashMap.put("processInstanceId", processInstanceId); SelectListDescriptor getGatewayMergingToken = new SelectListDescriptor<>( "getInclusiveGatewayInstanceOfProcessInstance", hashMap, SGatewayInstance.class, new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS)); return persistenceRead.selectList(getGatewayMergingToken); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/ProcessInstanceServiceImpl.java ================================================ /** * 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.core.process.instance.impl; import static java.util.Collections.singletonMap; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import java.text.MessageFormat; import java.time.Duration; 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.Set; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.ListUtils; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.commons.ExceptionUtils; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceDeletionException; import org.bonitasoft.engine.core.connector.exception.SConnectorInstanceReadException; import org.bonitasoft.engine.core.contract.data.ContractDataService; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.comment.model.archive.SAComment; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.exception.SProcessDefinitionNotFoundException; import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition; 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.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; 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.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceHierarchicalDeletionException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException; 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.SFlowNodeInstance; 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.archive.SAProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.data.instance.api.DataInstanceContainer; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Yanyan Liu * @author Frederic Bouquet * @author Celine Souchet */ @Slf4j public class ProcessInstanceServiceImpl implements ProcessInstanceService { private static final String MANAGER_USER_ID = "managerUserId"; private static final String USER_ID = "userId"; private static final String SUPERVISED_BY = "SupervisedBy"; private static final String FAILED_AND_SUPERVISED_BY = "FailedAndSupervisedBy"; private static final String INVOLVING_USER = "InvolvingUser"; private static final String FAILED = "Failed"; private static final String MANAGED_BY = "ManagedBy"; private static final int BATCH_SIZE = 100; static final int IN_REQUEST_SIZE = 100; private final Recorder recorder; private final ReadPersistenceService persistenceRead; private final ActivityInstanceService activityService; private final EventInstanceService bpmEventInstanceService; private final DataInstanceService dataInstanceService; private final ArchiveService archiveService; private final ProcessDefinitionService processDefinitionService; private final ConnectorInstanceService connectorInstanceService; private final ClassLoaderService classLoaderService; private final DocumentService documentService; private final SCommentService commentService; private final RefBusinessDataService refBusinessDataService; private final ContractDataService contractDataService; private final BPMFailureService bpmFailureService; public ProcessInstanceServiceImpl(final Recorder recorder, final ReadPersistenceService persistenceRead, final ActivityInstanceService activityService, final EventInstanceService bpmEventInstanceService, final DataInstanceService dataInstanceService, final ArchiveService archiveService, final ProcessDefinitionService processDefinitionService, final ConnectorInstanceService connectorInstanceService, final ClassLoaderService classLoaderService, final DocumentService documentService, final SCommentService commentService, final RefBusinessDataService refBusinessDataService, final ContractDataService contractDataService, final BPMFailureService bpmFailureService) { this.recorder = recorder; this.persistenceRead = persistenceRead; this.activityService = activityService; this.processDefinitionService = processDefinitionService; this.connectorInstanceService = connectorInstanceService; this.classLoaderService = classLoaderService; this.documentService = documentService; this.commentService = commentService; this.refBusinessDataService = refBusinessDataService; this.contractDataService = contractDataService; this.bpmEventInstanceService = bpmEventInstanceService; this.dataInstanceService = dataInstanceService; this.archiveService = archiveService; this.bpmFailureService = bpmFailureService; } @Override public void createProcessInstance(final SProcessInstance processInstance) throws SProcessInstanceCreationException { try { recorder.recordInsert(new InsertRecord(processInstance), PROCESSINSTANCE); setProcessState(processInstance, ProcessInstanceState.INITIALIZING); } catch (final SRecorderException | SProcessInstanceModificationException sre) { throw new SProcessInstanceCreationException(sre); } } @Override public SProcessInstance getProcessInstance(final long processInstanceId) throws SProcessInstanceReadException, SProcessInstanceNotFoundException { final SProcessInstance instance; try { instance = persistenceRead.selectById(SelectDescriptorBuilder.getElementById(SProcessInstance.class, "ProcessInstance", processInstanceId)); } catch (final SBonitaReadException e) { throw new SProcessInstanceReadException(e); } if (instance == null) { throw new SProcessInstanceNotFoundException(processInstanceId); } return instance; } @Override public void deleteProcessInstance(final long processInstanceId) throws SProcessInstanceModificationException, SProcessInstanceReadException, SProcessInstanceNotFoundException { final SProcessInstance processInstance = getProcessInstance(processInstanceId); deleteProcessInstance(processInstance); } @Override public long deleteParentProcessInstanceAndElements(final List sProcessInstances) throws SBonitaException { long nbDeleted = 0; for (final SProcessInstance sProcessInstance : sProcessInstances) { deleteParentProcessInstanceAndElements(sProcessInstance); nbDeleted++; } return nbDeleted; } @Override public void deleteParentProcessInstanceAndElements(final SProcessInstance sProcessInstance) throws SBonitaException { checkIfCallerIsNotActive(sProcessInstance.getCallerId()); try { deleteProcessInstance(sProcessInstance); deleteArchivedProcessInstances(Collections.singletonList(sProcessInstance.getId())); } catch (final SProcessInstanceModificationException e) { getProcessInstanceAndLogException(sProcessInstance, e); } } void getProcessInstanceAndLogException(final SProcessInstance sProcessInstance, final SProcessInstanceModificationException e) throws SProcessInstanceModificationException { try { getProcessInstance(sProcessInstance.getId()); // process is still here, that's not normal. The problem must be raised: throw e; } catch (final SProcessInstanceReadException | SProcessInstanceNotFoundException e1) { log.debug("{}. It has probably completed.", e.getMessage()); } } @Override public int deleteArchivedProcessInstances(List sourceProcessInstanceIds) throws SBonitaException { //delete all flow node having as root process instances these processes deleteArchivedFlowNodeInstancesAndElements(sourceProcessInstanceIds); Set archivedChildrenProcessInstances = getArchivedChildrenProcessInstances(sourceProcessInstanceIds); Set allSourceObjectIds = new HashSet<>(); allSourceObjectIds.addAll(sourceProcessInstanceIds); allSourceObjectIds.addAll(archivedChildrenProcessInstances); // Easier to partition than a set List allSourceObjectIdsList = new ArrayList<>(allSourceObjectIds); int numberOfDeletedInstances = 0; // Verify that the resulting IN statement in the request has a reasonable size, if not split in smaller requests // See BS-19316 Iterable> sourceObjectIdsPartitions = ListUtils.partition(allSourceObjectIdsList, IN_REQUEST_SIZE); for (List sourceObjectIds2k : sourceObjectIdsPartitions) { //delete all elements deleteElementsOfArchivedProcessInstances(new ArrayList<>(sourceObjectIds2k)); //delete all archived processes numberOfDeletedInstances = numberOfDeletedInstances + archiveService.deleteFromQuery("deleteArchiveProcessInstanceBySourceObjectId", Collections.singletonMap("sourceProcessInstanceIds", sourceObjectIds2k)); } return numberOfDeletedInstances; } private void deleteElementsOfArchivedProcessInstances(List sourceProcessInstanceIds) throws SBonitaException { documentService.deleteArchivedDocuments(sourceProcessInstanceIds); connectorInstanceService.deleteArchivedConnectorInstances(sourceProcessInstanceIds, SConnectorInstance.PROCESS_TYPE); dataInstanceService.deleteLocalArchivedDataInstances(sourceProcessInstanceIds, DataInstanceContainer.PROCESS_INSTANCE.toString()); commentService.deleteArchivedComments(sourceProcessInstanceIds); refBusinessDataService.deleteArchivedRefBusinessDataInstance(sourceProcessInstanceIds); contractDataService.deleteArchivedProcessData(sourceProcessInstanceIds); bpmFailureService.deleteArchivedProcessInstanceFailures(sourceProcessInstanceIds); } private void deleteArchivedFlowNodeInstancesAndElements(List sourceProcessInstanceIds) throws SBonitaException { List flowNodesSourceObjectIds = new ArrayList<>( activityService.getSourceObjectIdsOfArchivedFlowNodeInstances(sourceProcessInstanceIds)); if (!flowNodesSourceObjectIds.isEmpty()) { // Verify that the resulting IN statement in the request has a reasonable size, if not split it in smaller requests // See BS-19316 List> flowNodesSourceObjectIdsPartitions = ListUtils.partition(flowNodesSourceObjectIds, IN_REQUEST_SIZE); for (List flowNodesSourceObjectIds2k : flowNodesSourceObjectIdsPartitions) { connectorInstanceService.deleteArchivedConnectorInstances(flowNodesSourceObjectIds2k, SConnectorInstance.FLOWNODE_TYPE); dataInstanceService.deleteLocalArchivedDataInstances(flowNodesSourceObjectIds2k, DataInstanceContainer.ACTIVITY_INSTANCE.toString()); contractDataService.deleteArchivedUserTaskData(flowNodesSourceObjectIds2k); bpmFailureService.deleteArchivedFlowNodeFailures(flowNodesSourceObjectIds2k); activityService.deleteArchivedFlowNodeInstances(flowNodesSourceObjectIds2k); } } } private Set getArchivedChildrenProcessInstances(List sourceProcessInstanceIds) throws SBonitaException { final SelectListDescriptor selectListDescriptor = new SelectListDescriptor<>( "getArchivedChildrenProcessInstanceIds", Collections.singletonMap("sourceProcessInstanceIds", sourceProcessInstanceIds), SAProcessInstance.class, new QueryOptions(0, Integer.MAX_VALUE)); return new HashSet<>(persistenceRead.selectList(selectListDescriptor)); } @Override public void deleteArchivedProcessInstance(final SAProcessInstance archivedProcessInstance) throws SProcessInstanceModificationException { try { recorder.recordDelete(new DeleteRecord(archivedProcessInstance), PROCESSINSTANCE); } catch (final SRecorderException e) { throw new SProcessInstanceModificationException(e); } } @Override public List getSourceProcessInstanceIdsOfArchProcessInstancesFromDefinition(final long processDefinitionId, final int fromIndex, final int maxResults, final OrderByType sortingOrder) throws SProcessInstanceReadException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); final String saCommentSourceObjectId = SAComment.SOURCEOBJECTID_KEY; final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults, SAProcessInstance.class, saCommentSourceObjectId, sortingOrder); try { return persistenceService.selectList(SelectDescriptorBuilder .getSourceProcesInstanceIdsOfArchProcessInstancesFromDefinition(processDefinitionId, queryOptions)); } catch (final SBonitaReadException e) { throw new SProcessInstanceReadException(e); } } @Override public void deleteProcessInstance(final SProcessInstance sProcessInstance) throws SProcessInstanceModificationException { final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { final long processDefinitionId = sProcessInstance.getProcessDefinitionId(); final ClassLoader localClassLoader = classLoaderService .getClassLoader(identifier(ScopeType.PROCESS, processDefinitionId)); Thread.currentThread().setContextClassLoader(localClassLoader); deleteProcessInstanceElements(sProcessInstance); final DeleteRecord deleteRecord = new DeleteRecord(sProcessInstance); recorder.recordDelete(deleteRecord, PROCESSINSTANCE); } catch (final SBonitaException e) { throw new SProcessInstanceModificationException(e); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } protected void checkIfCallerIsNotActive(final long callerId) throws SFlowNodeReadException, SProcessInstanceHierarchicalDeletionException { try { if (callerId > 0) { final SFlowNodeInstance flowNodeInstance = activityService.getFlowNodeInstance(callerId); final SProcessInstanceHierarchicalDeletionException exception = new SProcessInstanceHierarchicalDeletionException( "Unable to delete the process instance, because the parent (call activity) is still active.", flowNodeInstance.getRootProcessInstanceId()); setExceptionContext(flowNodeInstance, exception); throw exception; } } catch (final SFlowNodeNotFoundException e) { // ok the activity that called this process do not exists anymore } } protected void deleteProcessInstanceElements(final SProcessInstance processInstance) throws SBonitaException { SProcessDefinition processDefinition = null; try { processDefinition = processDefinitionService.getProcessDefinition(processInstance.getProcessDefinitionId()); } catch (final SProcessDefinitionNotFoundException e) { // delete anyway } deleteFlowNodeInstances(processInstance.getId(), processDefinition); deleteDataInstancesIfNecessary(processInstance, processDefinition); documentService.deleteDocumentsFromProcessInstance(processInstance.getId()); deleteConnectorInstancesIfNecessary(processInstance, processDefinition); commentService.deleteComments(processInstance.getId()); deleteEventSubprocessWaitingEvents(processInstance, processDefinition); bpmFailureService.deleteProcessInstanceFailures(processInstance.getId()); } private void deleteEventSubprocessWaitingEvents(SProcessInstance processInstance, SProcessDefinition processDefinition) throws SWaitingEventModificationException, SEventTriggerInstanceReadException { if (processDefinition != null) { boolean containsEventSubProcess = processDefinition.getProcessContainer().getActivities().stream() .anyMatch(a -> a.getType().equals(SFlowNodeType.SUB_PROCESS) && ((SSubProcessDefinition) a).isTriggeredByEvent()); if (containsEventSubProcess) { bpmEventInstanceService.deleteWaitingEvents(processInstance); } } } private void deleteConnectorInstancesIfNecessary(final SProcessInstance processInstance, final SProcessDefinition processDefinition) throws SConnectorInstanceReadException, SConnectorInstanceDeletionException { if (processDefinition != null && processDefinition.hasConnectors()) { connectorInstanceService.deleteConnectors(processInstance.getId(), SConnectorInstance.PROCESS_TYPE); } } protected void deleteConnectorInstancesIfNecessary(final SFlowNodeInstance flowNodeInstance, final SProcessDefinition processDefinition) throws SConnectorInstanceReadException, SConnectorInstanceDeletionException { if (hasConnectors(flowNodeInstance, processDefinition)) { connectorInstanceService.deleteConnectors(flowNodeInstance.getId(), SConnectorInstance.FLOWNODE_TYPE); } } private boolean hasConnectors(final SFlowNodeInstance flowNodeInstance, final SProcessDefinition processDefinition) { if (processDefinition != null) { final SActivityDefinition activityDefinition = (SActivityDefinition) getFlowNode(flowNodeInstance, processDefinition); if (activityDefinition != null) { return activityDefinition.hasConnectors(); } } return false; } private void deleteDataInstancesIfNecessary(final SProcessInstance processInstance, final SProcessDefinition processDefinition) throws SDataInstanceException { boolean dataPresent = true; if (processDefinition != null) { dataPresent = processDefinition.getProcessContainer().getDataDefinitions().size() > 0; } dataInstanceService.deleteLocalDataInstances(processInstance.getId(), DataInstanceContainer.PROCESS_INSTANCE.toString(), dataPresent); } private void deleteFlowNodeInstances(final long processInstanceId, final SProcessDefinition processDefinition) throws SProcessInstanceModificationException, SFlowNodeReadException, SBonitaReadException { List activityInstances; do { activityInstances = activityService.getAllChildrenOfProcessInstance(processInstanceId, 0, BATCH_SIZE); for (final SFlowNodeInstance activityInstance : activityInstances) { deleteFlowNodeInstance(activityInstance, processDefinition); } } while (activityInstances.size() == BATCH_SIZE); } @Override public void deleteFlowNodeInstance(final SFlowNodeInstance flowNodeInstance, final SProcessDefinition processDefinition) throws SProcessInstanceModificationException { try { deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition); activityService.deleteFlowNodeInstance(flowNodeInstance); } catch (final SBonitaException e) { setExceptionContext(processDefinition, flowNodeInstance, e); throw new SProcessInstanceModificationException(e); } } void deleteFlowNodeInstanceElements(final SFlowNodeInstance flowNodeInstance, final SProcessDefinition processDefinition) throws SBonitaException { if (isReceiveTask(flowNodeInstance) || isEventWithTrigger(flowNodeInstance, processDefinition)) { bpmEventInstanceService.deleteWaitingEvents(flowNodeInstance); } if (flowNodeInstance instanceof SActivityInstance activityInstance) { deleteActivityInstanceElements(activityInstance, processDefinition); } bpmFailureService.deleteFlowNodeFailures(flowNodeInstance.getId()); } private boolean isEventWithTrigger(SFlowNodeInstance flowNodeInstance, SProcessDefinition processDefinition) { if (processDefinition != null) { SFlowNodeDefinition flowNodeDefinition = processDefinition.getProcessContainer() .getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); return flowNodeDefinition instanceof SEventDefinition && !((SEventDefinition) flowNodeDefinition).getEventTriggers().isEmpty(); } return false; } private boolean isReceiveTask(SFlowNodeInstance flowNodeInstance) { return flowNodeInstance.getType().equals(SFlowNodeType.RECEIVE_TASK); } private void deleteActivityInstanceElements(final SActivityInstance sActivityInstance, final SProcessDefinition processDefinition) throws SBonitaException { deleteDataInstancesIfNecessary(sActivityInstance, processDefinition); deleteConnectorInstancesIfNecessary(sActivityInstance, processDefinition); if (SFlowNodeType.USER_TASK.equals(sActivityInstance.getType()) || SFlowNodeType.MANUAL_TASK.equals(sActivityInstance.getType())) { activityService.deletePendingMappings(sActivityInstance.getId()); } else if (SFlowNodeType.CALL_ACTIVITY.equals(sActivityInstance.getType()) || SFlowNodeType.SUB_PROCESS.equals(sActivityInstance.getType())) { // in the case of a call activity or subprocess activity delete the child process instance deleteSubProcess(sActivityInstance, processDefinition); } } void deleteSubProcess(final SFlowNodeInstance flowNodeInstance, final SProcessDefinition processDefinition) throws SBonitaException { try { deleteProcessInstance(getChildOfActivity(flowNodeInstance.getId())); } catch (final SProcessInstanceNotFoundException e) { setExceptionContext(processDefinition, flowNodeInstance, e); // if the child process is not found, it's because it has already finished and archived or it was not created log.debug("Can't find the process instance called by the activity. This process may be already finished."); log.debug(ExceptionUtils.printLightWeightStacktrace(e)); } } private void setExceptionContext(final SProcessDefinition processDefinition, final SFlowNodeInstance flowNodeInstance, final SBonitaException e) { if (processDefinition != null) { e.setProcessDefinitionIdOnContext(processDefinition.getId()); e.setProcessDefinitionNameOnContext(processDefinition.getName()); e.setProcessDefinitionVersionOnContext(processDefinition.getVersion()); } setExceptionContext(flowNodeInstance, e); } private void setExceptionContext(final SFlowNodeInstance flowNodeInstance, final SBonitaException e) { e.setProcessInstanceIdOnContext(flowNodeInstance.getParentProcessInstanceId()); e.setRootProcessInstanceIdOnContext(flowNodeInstance.getRootProcessInstanceId()); e.setFlowNodeDefinitionIdOnContext(flowNodeInstance.getFlowNodeDefinitionId()); e.setFlowNodeInstanceIdOnContext(flowNodeInstance.getId()); e.setFlowNodeNameOnContext(flowNodeInstance.getName()); } void deleteDataInstancesIfNecessary(final SFlowNodeInstance flowNodeInstance, final SProcessDefinition processDefinition) throws SDataInstanceException { boolean hasData = true; if (processDefinition != null) { final SActivityDefinition activityDefinition = (SActivityDefinition) getFlowNode(flowNodeInstance, processDefinition); if (activityDefinition != null) { hasData = activityDefinition.getSDataDefinitions().size() > 0; } } dataInstanceService.deleteLocalDataInstances(flowNodeInstance.getId(), DataInstanceContainer.ACTIVITY_INSTANCE.toString(), hasData); } private SFlowNodeDefinition getFlowNode(final SFlowNodeInstance flowNodeInstance, final SProcessDefinition processDefinition) { if (processDefinition == null) { return null; } return processDefinition.getProcessContainer().getFlowNode(flowNodeInstance.getFlowNodeDefinitionId()); } @Override public void setState(final SProcessInstance processInstance, final ProcessInstanceState state) throws SProcessInstanceModificationException { // Let's archive the process instance before changing the state (to keep a track of state change): archiveProcessInstance(processInstance); final int previousStateId = processInstance.getStateId(); setProcessState(processInstance, state); log.debug(MessageFormat.format("[{0} with id {1}]{2}->{3}(new={4})", processInstance.getClass() .getSimpleName(), processInstance.getId(), previousStateId, state.getId(), state.name())); } private void setProcessState(final SProcessInstance processInstance, final ProcessInstanceState state) throws SProcessInstanceModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(SProcessInstance.STATE_ID_KEY, state.getId()); final long now = System.currentTimeMillis(); switch (state) { case COMPLETED: case ABORTED: case CANCELLED: descriptor.addField(SProcessInstance.END_DATE_KEY, now); break; case STARTED: descriptor.addField(SProcessInstance.START_DATE_KEY, now); break; default: break; } descriptor.addField(SProcessInstance.LAST_UPDATE_KEY, now); updateProcessInstance(processInstance, descriptor, PROCESSINSTANCE_STATE); } private void updateProcessInstance(final SProcessInstance processInstance, final EntityUpdateDescriptor descriptor, final String eventType) throws SProcessInstanceModificationException { try { recorder.recordUpdate(UpdateRecord.buildSetFields(processInstance, descriptor), eventType); } catch (final SRecorderException e) { throw new SProcessInstanceModificationException(e); } } private void archiveProcessInstance(final SProcessInstance processInstance) throws SProcessInstanceModificationException { final SAProcessInstance saProcessInstance = BuilderFactory.get(SAProcessInstanceBuilderFactory.class) .createNewInstance(processInstance).done(); final ArchiveInsertRecord insertRecord = new ArchiveInsertRecord(saProcessInstance); try { archiveService.recordInsert(System.currentTimeMillis(), insertRecord); } catch (final SRecorderException e) { throw new SProcessInstanceModificationException(e); } } @Override public void setStateCategory(final SProcessInstance processInstance, final SStateCategory stateCatetory) throws SProcessInstanceModificationException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField(SProcessInstance.STATE_CATEGORY_KEY, stateCatetory); updateProcessInstance(processInstance, descriptor, PROCESS_INSTANCE_CATEGORY_STATE); } @Override public List getChildInstanceIdsOfProcessInstance(final long processInstanceId, final int fromIndex, final int maxResults, final String sortingField, final OrderByType sortingOrder) throws SProcessInstanceReadException { try { final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults, SProcessInstance.class, sortingField, sortingOrder); final SelectListDescriptor elements = SelectDescriptorBuilder.getChildInstanceIdsOfProcessInstance( SProcessInstance.class, processInstanceId, queryOptions); return persistenceRead.selectList(elements); } catch (final SBonitaReadException e) { throw new SProcessInstanceReadException(e); } } @Override public SProcessInstance getChildOfActivity(final long activityInstId) throws SProcessInstanceNotFoundException, SBonitaReadException { final SProcessInstance sProcessInstance = persistenceRead .selectOne(new SelectOneDescriptor("getChildOfActivity", Collections . singletonMap("activityInstanceId", activityInstId), SProcessInstance.class)); if (sProcessInstance == null) { throw new SProcessInstanceNotFoundException( "No process instance was found as child of the activity with id = <" + activityInstId + ">"); } return sProcessInstance; } @Override public long getNumberOfChildInstancesOfProcessInstance(final long processInstanceId) throws SProcessInstanceReadException { try { return persistenceRead .selectOne(SelectDescriptorBuilder.getNumberOfChildInstancesOfProcessInstance(processInstanceId)); } catch (final SBonitaReadException e) { throw new SProcessInstanceReadException(e); } } @Override public long getNumberOfProcessInstances(final QueryOptions queryOptions) throws SBonitaReadException { return persistenceRead.getNumberOfEntities(SProcessInstance.class, queryOptions, Collections.emptyMap()); } @Override public List searchProcessInstances(final QueryOptions queryOptions) throws SBonitaReadException { return persistenceRead.searchEntity(SProcessInstance.class, queryOptions, Collections.emptyMap()); } @Override public long getNumberOfOpenProcessInstancesSupervisedBy(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { try { final Map parameters = Collections.singletonMap(USER_ID, (Object) userId); return persistenceRead.getNumberOfEntities(SProcessInstance.class, SUPERVISED_BY, queryOptions, parameters); } catch (final SBonitaReadException e) { throw new SBonitaReadException(e); } } @Override public List searchOpenProcessInstancesSupervisedBy(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { try { final Map parameters = Collections.singletonMap(USER_ID, (Object) userId); return persistenceRead.searchEntity(SProcessInstance.class, SUPERVISED_BY, queryOptions, parameters); } catch (final SBonitaReadException e) { throw new SBonitaReadException(e); } } @Override public long getNumberOfFailedProcessInstancesSupervisedBy(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { try { final Map parameters = Collections.singletonMap(USER_ID, (Object) userId); return persistenceRead.getNumberOfEntities(SProcessInstance.class, FAILED_AND_SUPERVISED_BY, queryOptions, parameters); } catch (final SBonitaReadException e) { throw new SBonitaReadException(e); } } @Override public List searchFailedProcessInstancesSupervisedBy(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { try { final Map parameters = Collections.singletonMap(USER_ID, (Object) userId); return persistenceRead.searchEntity(SProcessInstance.class, FAILED_AND_SUPERVISED_BY, queryOptions, parameters); } catch (final SBonitaReadException e) { throw new SBonitaReadException(e); } } @Override public long getNumberOfOpenProcessInstancesInvolvingUser(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { try { final Map parameters = new HashMap<>(1); parameters.put(USER_ID, userId); return persistenceRead.getNumberOfEntities(SProcessInstance.class, INVOLVING_USER, queryOptions, parameters); } catch (final SBonitaReadException e) { throw new SBonitaReadException(e); } } @Override public List searchOpenProcessInstancesInvolvingUser(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = new HashMap<>(1); parameters.put(USER_ID, userId); return persistenceRead.searchEntity(SProcessInstance.class, INVOLVING_USER, queryOptions, parameters); } @Override public long getNumberOfOpenProcessInstancesInvolvingUsersManagedBy(final long managerUserId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = new HashMap<>(1); parameters.put(MANAGER_USER_ID, managerUserId); return persistenceRead.getNumberOfEntities(SProcessInstance.class, INVOLVING_USER + "s" + MANAGED_BY, queryOptions, parameters); } @Override public List searchOpenProcessInstancesInvolvingUsersManagedBy(final long managerUserId, final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = new HashMap<>(1); parameters.put(MANAGER_USER_ID, managerUserId); return persistenceRead.searchEntity(SProcessInstance.class, INVOLVING_USER + "s" + MANAGED_BY, queryOptions, parameters); } @Override public long getNumberOfArchivedProcessInstancesWithoutSubProcess(final QueryOptions queryOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); return persistenceService.getNumberOfEntities(SAProcessInstance.class, "WithoutSubProcess", queryOptions, null); } @Override public List searchArchivedProcessInstancesWithoutSubProcess(final QueryOptions queryOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); return persistenceService.searchEntity(SAProcessInstance.class, "WithoutSubProcess", queryOptions, null); } @Override public List searchArchivedProcessInstancesSupervisedBy(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); final Map parameters = Collections.singletonMap(USER_ID, (Object) userId); return persistenceService.searchEntity(SAProcessInstance.class, SUPERVISED_BY, queryOptions, parameters); } @Override public long getNumberOfArchivedProcessInstancesSupervisedBy(final long userId, final QueryOptions countOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); final Map parameters = Collections.singletonMap(USER_ID, (Object) userId); return persistenceService.getNumberOfEntities(SAProcessInstance.class, SUPERVISED_BY, countOptions, parameters); } @Override public long getNumberOfArchivedProcessInstancesInvolvingUser(final long userId, final QueryOptions countOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); try { final Map parameters = new HashMap<>(2); parameters.put(USER_ID, userId); return persistenceService.getNumberOfEntities(SAProcessInstance.class, INVOLVING_USER, countOptions, parameters); } catch (final SBonitaReadException bre) { throw new SBonitaReadException(bre); } } @Override public long getNumberOfArchivedProcessInstances(final QueryOptions queryOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); return persistenceService.getNumberOfEntities(SAProcessInstance.class, queryOptions, null); } @Override public List searchArchivedProcessInstances(final QueryOptions queryOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); return persistenceService.searchEntity(SAProcessInstance.class, queryOptions, null); } @Override public List searchArchivedProcessInstancesInvolvingUser(final long userId, final QueryOptions queryOptions) throws SBonitaReadException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); try { final Map parameters = new HashMap<>(1); parameters.put(USER_ID, userId); return persistenceService.searchEntity(SAProcessInstance.class, INVOLVING_USER, queryOptions, parameters); } catch (final SBonitaReadException e) { throw new SBonitaReadException(e); } } @Override public void updateProcess(final SProcessInstance processInstance, final EntityUpdateDescriptor descriptor) throws SProcessInstanceModificationException { try { recorder.recordUpdate(UpdateRecord.buildSetFields(processInstance, descriptor.addField(SProcessInstance.LAST_UPDATE_KEY, System.currentTimeMillis())), PROCESSINSTANCE); } catch (final SRecorderException e) { throw new SProcessInstanceModificationException(e); } } @Override public SAProcessInstance getArchivedProcessInstance(final long archivedProcessInstanceId) throws SProcessInstanceReadException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); try { final Map parameters = Collections.singletonMap("id", (Object) archivedProcessInstanceId); return persistenceService.selectOne(new SelectOneDescriptor("getArchivedProcessInstance", parameters, SAProcessInstance.class)); } catch (final SBonitaReadException e) { throw new SProcessInstanceReadException(e); } } @Override public List getArchivedProcessInstancesInAllStates(final List processInstanceIds) throws SProcessInstanceReadException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); try { final Map parameters = Collections.singletonMap("sourceObjectIds", (Object) processInstanceIds); final SelectListDescriptor saProcessInstances = new SelectListDescriptor<>( "getArchivedProcessInstancesInAllStates", parameters, SAProcessInstance.class, QueryOptions.countQueryOptions()); return persistenceService.selectList(saProcessInstances); } catch (final SBonitaReadException e) { throw new SProcessInstanceReadException(e); } } @Override public List getLastArchivedProcessInstanceStartDates(final long sinceDateInMillis) throws SProcessInstanceReadException { final ReadPersistenceService persistenceService = archiveService.getDefinitiveArchiveReadPersistenceService(); try { final Map parameters = Collections.singletonMap("sinceDateInMillis", sinceDateInMillis); final SelectListDescriptor saProcessInstances = new SelectListDescriptor<>( "getLastArchivedProcessInstanceStartDates", parameters, SAProcessInstance.class, QueryOptions.countQueryOptions()); return persistenceService.selectList(saProcessInstances); } catch (final SBonitaReadException e) { throw new SProcessInstanceReadException(e); } } protected Set getStateIdsFromStates(final ProcessInstanceState... states) { if (states.length < 1) { throw new IllegalArgumentException( "ProcessInstanceServiceImpl.getProcessInstancesInStates() must have at least one state as parameter"); } final Set stateIds = new HashSet<>(states.length); for (ProcessInstanceState state : states) { stateIds.add(state.getId()); } return stateIds; } @Override public List getArchivedChildrenSourceObjectIdsFromRootProcessInstance(final long rootProcessInstanceId, final int fromIndex, final int maxResults, final OrderByType sortingOrder) throws SBonitaReadException { final Map inputParameters = new HashMap<>(1); inputParameters.put("rootProcessInstanceId", rootProcessInstanceId); final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults, SAProcessInstance.class, "sourceObjectId", sortingOrder); final SelectListDescriptor selectListDescriptor = new SelectListDescriptor<>( "getChildrenSourceProcessInstanceIdsFromRootProcessInstance", inputParameters, SAProcessInstance.class, queryOptions); return persistenceRead.selectList(selectListDescriptor); } @Override public SAProcessInstance getLastArchivedProcessInstance(final long processInstanceId) throws SBonitaReadException { final SAProcessInstanceBuilderFactory processInstanceBuilderFact = BuilderFactory .get(SAProcessInstanceBuilderFactory.class); final FilterOption filterOption = new FilterOption(SAProcessInstance.class, processInstanceBuilderFact.getSourceObjectIdKey(), processInstanceId); final List orderByOptions = new ArrayList<>(); orderByOptions.add(new OrderByOption(SAProcessInstance.class, processInstanceBuilderFact.getArchiveDateKey(), OrderByType.DESC)); orderByOptions.add(new OrderByOption(SAProcessInstance.class, processInstanceBuilderFact.getEndDateKey(), OrderByType.DESC)); final QueryOptions queryOptions = new QueryOptions(0, 1, orderByOptions, Collections.singletonList(filterOption), null); final List processInstances = searchArchivedProcessInstances(queryOptions); if (!processInstances.isEmpty()) { return processInstances.get(0); } return null; } @Override public long getNumberOfFailedProcessInstances(final QueryOptions queryOptions) throws SBonitaReadException { return persistenceRead.getNumberOfEntities(SProcessInstance.class, FAILED, queryOptions, null); } @Override public List searchFailedProcessInstances(final QueryOptions queryOptions) throws SBonitaReadException { return persistenceRead.searchEntity(SProcessInstance.class, FAILED, queryOptions, null); } @Override public long getNumberOfProcessInstances(final long processDefinitionId) throws SBonitaReadException { final Map inputParameters = new HashMap<>(); inputParameters.put("processDefinitionId", processDefinitionId); final SelectOneDescriptor countDescriptor = new SelectOneDescriptor<>( "countProcessInstancesOfProcessDefinition", inputParameters, SProcessInstance.class); return persistenceRead.selectOne(countDescriptor); } @Override public List getProcessInstanceIdsToRecover(Duration considerElementsOlderThan, QueryOptions queryOptions) throws SBonitaReadException { return persistenceRead.selectList(new SelectListDescriptor<>( "getProcessInstanceIdsToRecover", singletonMap("maxLastUpdate", System.currentTimeMillis() - considerElementsOlderThan.toMillis()), SProcessInstance.class, queryOptions)); } @Override public void setInterruptingEventId(long parentProcessInstanceId, long eventInstanceId) throws SProcessInstanceNotFoundException, SProcessInstanceReadException, SProcessInstanceModificationException { updateProcess(getProcessInstance(parentProcessInstanceId), new EntityUpdateDescriptor().addField(SProcessInstance.INTERRUPTING_EVENT_ID_KEY, eventInstanceId)); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/impl/RefBusinessDataServiceImpl.java ================================================ /** * 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.core.process.instance.impl; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.model.archive.builder.business.data.SARefBusinessDataInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceLogBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceLogBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.recorder.SelectBusinessDataDescriptorBuilder; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; /** * @author Matthieu Chaffotte */ public class RefBusinessDataServiceImpl implements RefBusinessDataService { private final EventService eventService; private final Recorder recorder; private final ReadPersistenceService persistenceRead; private final QueriableLoggerService queriableLoggerService; private final ArchiveService archiveService; public RefBusinessDataServiceImpl(final Recorder recorder, final ReadPersistenceService persistenceRead, final EventService eventService, final QueriableLoggerService queriableLoggerService, final ArchiveService archiveService) { this.recorder = recorder; this.persistenceRead = persistenceRead; this.eventService = eventService; this.queriableLoggerService = queriableLoggerService; this.archiveService = archiveService; } @Override public SRefBusinessDataInstance getRefBusinessDataInstance(final String name, final long processInstanceId) throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException { final SelectOneDescriptor descriptor = SelectBusinessDataDescriptorBuilder .getSRefBusinessDataInstance(name, processInstanceId); final SRefBusinessDataInstance ref = persistenceRead.selectOne(descriptor); if (ref == null) { throw new SRefBusinessDataInstanceNotFoundException(processInstanceId, name); } return ref; } @Override public SRefBusinessDataInstance getFlowNodeRefBusinessDataInstance(final String name, final long flowNodeInstanceId) throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException { final SelectOneDescriptor descriptor = SelectBusinessDataDescriptorBuilder .getSFlowNodeRefBusinessDataInstance(name, flowNodeInstanceId); final SRefBusinessDataInstance ref = persistenceRead.selectOne(descriptor); if (ref == null) { throw new SRefBusinessDataInstanceNotFoundException(flowNodeInstanceId, name); } return ref; } @Override public SRefBusinessDataInstance addRefBusinessDataInstance(final SRefBusinessDataInstance instance) throws SRefBusinessDataInstanceCreationException { final SRefBusinessDataInstanceLogBuilder logBuilder = getQueriableLog(ActionType.CREATED, NEW_REF_BUSINESS_DATA_INSTANCE_ADDED); try { recorder.recordInsert(new InsertRecord(instance), REF_BUSINESS_DATA_INSTANCE); initiateLogBuilder(instance.getId(), SQueriableLog.STATUS_OK, logBuilder, "addRefBusinessDataInstance"); } catch (final SBonitaException sbe) { initiateLogBuilder(instance.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "addRefBusinessDataInstance"); throw new SRefBusinessDataInstanceCreationException(sbe); } return instance; } protected SRefBusinessDataInstanceLogBuilder getQueriableLog(final ActionType actionType, final String message) { final SRefBusinessDataInstanceLogBuilder logBuilder = BuilderFactory .get(SRefBusinessDataInstanceLogBuilderFactory.class).createNewInstance(); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); return logBuilder; } protected void initializeLogBuilder(final T logBuilder, final String message) { logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message); } protected void updateLog(final ActionType actionType, final T logBuilder) { logBuilder.setActionType(actionType); } private void initiateLogBuilder(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder, final String callerMethodName) { logBuilder.actionScope(String.valueOf(objectId)); logBuilder.actionStatus(sQueriableLogStatus); logBuilder.objectId(objectId); final SQueriableLog log = logBuilder.build(); if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) { queriableLoggerService.log(this.getClass().getName(), callerMethodName, log); } } @Override public void updateRefBusinessDataInstance(final SSimpleRefBusinessDataInstance refBusinessDataInstance, final Long dataId) throws SRefBusinessDataInstanceModificationException { final Map fields = new HashMap<>(); fields.put("dataId", dataId); updateRefBusinessDataInstance(refBusinessDataInstance, fields); } @Override public void updateRefBusinessDataInstance(final SProcessMultiRefBusinessDataInstance refBusinessDataInstance, final List dataIds) throws SRefBusinessDataInstanceModificationException { final Map fields = new HashMap<>(); fields.put("dataIds", dataIds); updateRefBusinessDataInstance(refBusinessDataInstance, fields); } private void updateRefBusinessDataInstance(final SRefBusinessDataInstance refBusinessDataInstance, final Map fields) throws SRefBusinessDataInstanceModificationException { try { recorder.recordUpdate(UpdateRecord.buildSetFields(refBusinessDataInstance, fields), REF_BUSINESS_DATA_INSTANCE); } catch (final SRecorderException sre) { throw new SRefBusinessDataInstanceModificationException(sre); } } @Override public int getNumberOfDataOfMultiRefBusinessData(final String name, final long processInstanceId) throws SBonitaReadException { final SelectOneDescriptor descriptor = SelectBusinessDataDescriptorBuilder .getNumberOfDataOfMultiRefBusinessData(name, processInstanceId); return persistenceRead.selectOne(descriptor); } @Override public List getRefBusinessDataInstances(final long processInstanceId, final int startIndex, final int maxResults) throws SBonitaReadException { final SelectListDescriptor descriptor = SelectBusinessDataDescriptorBuilder .getSRefBusinessDataInstances(processInstanceId, startIndex, maxResults); return persistenceRead.selectList(descriptor); } @Override public SARefBusinessDataInstance getSARefBusinessDataInstance(final String name, final long processInstanceId) throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException { final SelectOneDescriptor descriptor = SelectBusinessDataDescriptorBuilder .getSARefBusinessDataInstance(name, processInstanceId); final SARefBusinessDataInstance ref = persistenceRead.selectOne(descriptor); if (ref == null) { throw new SRefBusinessDataInstanceNotFoundException(processInstanceId, name); } return ref; } @Override public SARefBusinessDataInstance getSAFlowNodeRefBusinessDataInstance(final String name, final long flowNodeInstanceId) throws SRefBusinessDataInstanceNotFoundException, SBonitaReadException { final SelectOneDescriptor descriptor = SelectBusinessDataDescriptorBuilder .getSAFlowNodeRefBusinessDataInstance(name, flowNodeInstanceId); final SARefBusinessDataInstance ref = persistenceRead.selectOne(descriptor); if (ref == null) { throw new SRefBusinessDataInstanceNotFoundException(flowNodeInstanceId, name); } return ref; } @Override public void archiveRefBusinessDataInstance(SRefBusinessDataInstance businessDataInstance) throws SObjectModificationException { try { final SARefBusinessDataInstance saRefBusinessDataInstance = BuilderFactory .get(SARefBusinessDataInstanceBuilderFactory.class) .createNewInstance(businessDataInstance).done(); final ArchiveInsertRecord archiveInsertRecord = new ArchiveInsertRecord(saRefBusinessDataInstance); archiveService.recordInsert(0L, archiveInsertRecord); // no archived date (nor sourceObjectId) in this table, as it is not necessary (archived only once at end of process execution) } catch (final SRecorderException e) { throw new SObjectModificationException("Unable to archive RefBusinessDataInstance", e); } } @Override public void deleteArchivedRefBusinessDataInstance(long processInstanceId) throws SObjectModificationException { try { archiveService.deleteFromQuery("deleteArchivedRefBizDataForProcessInstance", Collections.singletonMap("processInstanceId", processInstanceId)); // archiveService.deleteFromQuery("deleteArchivedMultiRefBizDataForProcessInstance", // Collections. singletonMap("processInstanceId", processInstanceId)); } catch (SRecorderException e) { throw new SObjectModificationException( "Unable to delete SARefBusinessDataInstance's for processInstanceId: " + processInstanceId, e); } } @Override public void deleteArchivedRefBusinessDataInstance(List processInstanceIds) throws SObjectModificationException { try { archiveService.deleteFromQuery("deleteArchivedRefBizDataForProcessInstances", Collections.singletonMap("processInstanceIds", processInstanceIds)); } catch (SRecorderException e) { throw new SObjectModificationException( "Unable to delete SARefBusinessDataInstance's for processInstanceIds: " + processInstanceIds, e); } } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SABPMFailure.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; import org.bonitasoft.engine.persistence.PersistentObject; import org.hibernate.annotations.Type; @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "arch_bpm_failure") public class SABPMFailure implements ArchivedPersistentObject { @Id private long id; private long processDefinitionId; private long processInstanceId; private long rootProcessInstanceId; private long flowNodeInstanceId; private String scope; private String context; private String errorMessage; @Type(type = "materialized_clob") private String stackTrace; private long failureDate; private long archiveDate; private long sourceObjectId; public SABPMFailure(final SBPMFailure bpmFailure) { this.sourceObjectId = bpmFailure.getId(); this.processDefinitionId = bpmFailure.getProcessDefinitionId(); this.processInstanceId = bpmFailure.getProcessInstanceId(); this.rootProcessInstanceId = bpmFailure.getRootProcessInstanceId(); this.flowNodeInstanceId = bpmFailure.getFlowNodeInstanceId(); this.scope = bpmFailure.getScope(); this.context = bpmFailure.getContext(); this.errorMessage = bpmFailure.getErrorMessage(); this.stackTrace = bpmFailure.getStackTrace(); this.failureDate = bpmFailure.getFailureDate(); } @Override public Class getPersistentObjectInterface() { return SBPMFailure.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SAbstractConnectorInstance.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @AllArgsConstructor @SuperBuilder @MappedSuperclass public abstract class SAbstractConnectorInstance implements PersistentObject { public static final String ID_KEY = "id"; public static final String NAME_KEY = "name"; public static final String CONTAINER_ID_KEY = "containerId"; public static final String CONTAINER_TYPE_KEY = "containerType"; public static final String CONNECTOR_ID_KEY = "connectorId"; public static final String VERSION_KEY = "version"; public static final String ACTIVATION_EVENT_KEY = "activationEvent"; public static final String STATE_KEY = "state"; public static final String EXECUTION_ORDER = "executionOrder"; public static final String FLOWNODE_TYPE = "flowNode"; public static final String PROCESS_TYPE = "process"; @Id private long id; private String name; private long containerId; private String connectorId; private String version; @Enumerated(EnumType.STRING) private ConnectorEvent activationEvent; private String state; private String containerType; private int executionOrder; public SAbstractConnectorInstance(final String name, final long containerId, final String containerType, final String connectorId, final String version, final ConnectorEvent activationEvent) { this.name = name; this.containerId = containerId; this.containerType = containerType; this.connectorId = connectorId; this.version = version; this.activationEvent = activationEvent; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SActivityInstance.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity public abstract class SActivityInstance extends SFlowNodeInstance { private long abortedByBoundary = 0; public SActivityInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SAutomaticTaskInstance.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("auto") public class SAutomaticTaskInstance extends SActivityInstance { public SAutomaticTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); } @Override public SFlowNodeType getType() { return SFlowNodeType.AUTOMATIC_TASK; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SBPMFailure.java ================================================ /** * 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.core.process.instance.model; import java.time.Instant; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; import org.hibernate.annotations.Type; @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "bpm_failure") public class SBPMFailure implements PersistentObject { @Id private long id; private long processDefinitionId; private long processInstanceId; private long rootProcessInstanceId; private long flowNodeInstanceId; private String scope; private String context; private String errorMessage; @Type(type = "materialized_clob") private String stackTrace; @Builder.Default private long failureDate = Instant.now().toEpochMilli(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SCallActivityInstance.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("call") public class SCallActivityInstance extends SActivityInstance { public SCallActivityInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); } @Override public SFlowNodeType getType() { return SFlowNodeType.CALL_ACTIVITY; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SConnectorInstance.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.*; import lombok.*; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Data @SuperBuilder @EqualsAndHashCode(callSuper = true) @Entity @Table(name = "connector_instance") public class SConnectorInstance extends SAbstractConnectorInstance { public SConnectorInstance(final String name, final long containerId, final String containerType, final String connectorId, final String version, final ConnectorEvent activationEvent) { super(name, containerId, containerType, connectorId, version, activationEvent); } public SConnectorInstance() { super(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SConnectorInstanceWithFailureInfo.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.Cacheable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.hibernate.annotations.Type; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @Table(name = "connector_instance") @Cacheable(false) public class SConnectorInstanceWithFailureInfo extends SAbstractConnectorInstance { public static final String EXCEPTION_MESSAGE = "exceptionMessage"; public static final String STACK_TRACE = "stackTrace"; @Column @Type(type = "materialized_clob") private String stackTrace; private String exceptionMessage; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SFlowElementsContainerType.java ================================================ /** * 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.core.process.instance.model; /** * @author Baptiste Mesta */ public enum SFlowElementsContainerType { PROCESS, FLOWNODE } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SFlowNodeInstance.java ================================================ /** * 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.core.process.instance.model; import java.util.Date; import javax.persistence.Column; import javax.persistence.DiscriminatorColumn; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Feng Hui * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Celine Souchet */ @Data @NoArgsConstructor @Entity @Table(name = "flownode_instance") @DiscriminatorColumn(name = "kind") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) public abstract class SFlowNodeInstance implements PersistentObject { @Id private long id; private long flowNodeDefinitionId; private long rootContainerId; private long parentContainerId; private String name; private String displayName; private String displayDescription; private int stateId; private String stateName; @Column(name = "prev_state_id") private int previousStateId; private boolean terminal; private boolean stable; @Enumerated(EnumType.STRING) private SStateCategory stateCategory = SStateCategory.NORMAL; private long reachedStateDate; private long lastUpdateDate; //process definition id private long logicalGroup1; //root process instance id private long logicalGroup2; //parent activity instance id private long logicalGroup3; //parent process instance id private long logicalGroup4; private int tokenCount = 0; private String description; @Column(name = "loop_counter") protected int loopCounter; /** * id of the user who originally executed the flow node */ @Column private long executedBy; /** * id of the user (delegate) who executed the flow node for the original executer */ @Column private long executedBySubstitute; @Column(name = "state_executing") private boolean stateExecuting; public SFlowNodeInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2) { this.name = name; this.rootContainerId = rootContainerId; this.parentContainerId = parentContainerId; this.logicalGroup1 = logicalGroup1; this.logicalGroup2 = logicalGroup2; this.flowNodeDefinitionId = flowNodeDefinitionId; long now = new Date().getTime(); lastUpdateDate = now; reachedStateDate = now; } public long getProcessDefinitionId() { return logicalGroup1; } /** * @return the root process instance is the top level process containing this element */ public long getRootProcessInstanceId() { return logicalGroup2; } /** * @return * the id of the activity instance containing this element or 0 if this element is not contained in an * activity */ public long getParentActivityInstanceId() { return logicalGroup3; } /** * @return * the id of the process instance containing this element */ public long getParentProcessInstanceId() { return logicalGroup4; } /** * @return * the type of the element that contains this element */ public SFlowElementsContainerType getParentContainerType() { return getParentActivityInstanceId() <= 0 ? SFlowElementsContainerType.PROCESS : SFlowElementsContainerType.FLOWNODE; } public long getLogicalGroup(final int index) { switch (index) { case 0: return logicalGroup1; case 1: return logicalGroup2; case 2: return logicalGroup3; case 3: return logicalGroup4; default: throw new IllegalArgumentException("Invalid index: the index must be 0, 1, 2 or 3"); } } public boolean isAborting() { return SStateCategory.ABORTING.equals(stateCategory); } public boolean isCanceling() { return SStateCategory.CANCELLING.equals(stateCategory); } public void setLogicalGroup(final int index, final long value) { switch (index) { case 0: logicalGroup1 = value; break; case 1: logicalGroup2 = value; break; case 2: logicalGroup3 = value; break; case 3: logicalGroup4 = value; break; default: throw new IllegalArgumentException("Invalid index: the index must be 0, 1, 2 or 3"); } } public SFlowElementsContainerType getContainerType() { return SFlowElementsContainerType.FLOWNODE; } /** * @return true if the execution must continues automatically on abort or cancel the parent process instance */ public boolean mustExecuteOnAbortOrCancelProcess() { return isStable() && !isTerminal(); } public abstract SFlowNodeType getType(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SFlowNodeInstanceStateCounter.java ================================================ /** * 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.core.process.instance.model; /*** * Represents flownode state counters for a given process instance. Only serves query purposes (like a view), not * persisted. * * @author Emmanuel Duchastenier */ public class SFlowNodeInstanceStateCounter { private String flownodeName; private String stateName; private Long numberOf; public SFlowNodeInstanceStateCounter(String flownodeName, String stateName, Long numberOf) { this.flownodeName = flownodeName; this.stateName = stateName; this.numberOf = numberOf; } public String getFlownodeName() { return flownodeName; } public String getStateName() { return stateName; } public Long getNumberOf() { return numberOf; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SGatewayInstance.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.*; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.SGatewayType; import org.bonitasoft.engine.core.process.instance.api.GatewayInstanceService; /** * @author Feng Hui */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("gate") public class SGatewayInstance extends SFlowNodeInstance { @Column @Enumerated(EnumType.STRING) private SGatewayType gatewayType; private String hitBys = ""; public SGatewayInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final SGatewayType gatewayType, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); this.gatewayType = gatewayType; } public SGatewayInstance(SGatewayInstance gatewayInstance) { super(gatewayInstance.getName(), gatewayInstance.getFlowNodeDefinitionId(), gatewayInstance.getRootContainerId(), gatewayInstance.getParentContainerId(), gatewayInstance.getLogicalGroup(0), gatewayInstance.getLogicalGroup(1)); setLogicalGroup(2, gatewayInstance.getLogicalGroup(2)); setLogicalGroup(3, gatewayInstance.getLogicalGroup(3)); this.gatewayType = gatewayInstance.getGatewayType(); setStateId(gatewayInstance.getStateId()); } public boolean isFinished() { return hitBys != null && hitBys.startsWith(GatewayInstanceService.FINISH); } public SFlowNodeType getType() { return SFlowNodeType.GATEWAY; } @Override public boolean mustExecuteOnAbortOrCancelProcess() { //always call execute when abort the gateway because the gateway that merge wait for the flow node in an unstable state // this fact is a little strange but a full cleanup of the flownode execution mechanism should be done in order to change that return true; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SHumanTaskInstance.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity public abstract class SHumanTaskInstance extends SActivityInstance { private long actorId; private long assigneeId; private Long expectedEndDate; private long claimedDate; private STaskPriority priority; public SHumanTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parenteContainerId, final long actorId, final STaskPriority priority, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parenteContainerId, logicalGroup1, logicalGroup2); this.actorId = actorId; this.priority = priority; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SLoopActivityInstance.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; /** * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("loop") public class SLoopActivityInstance extends SActivityInstance { @Column(name = "loop_max") private int loopMax; public SLoopActivityInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); loopMax = -1; } @Override public SFlowNodeType getType() { return SFlowNodeType.LOOP_ACTIVITY; } @Override public boolean mustExecuteOnAbortOrCancelProcess() { // it's not necessary to execute it because this will be done when the child reaches the aborted state return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SManualTaskInstance.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("manual") public class SManualTaskInstance extends SHumanTaskInstance { public SManualTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parenteContainerId, final long actorId, final STaskPriority priority, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parenteContainerId, actorId, priority, logicalGroup1, logicalGroup2); } @Override public SFlowNodeType getType() { return SFlowNodeType.MANUAL_TASK; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SMultiInstanceActivityInstance.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("multi") public class SMultiInstanceActivityInstance extends SActivityInstance { private boolean sequential; private String loopDataInputRef; private String loopDataOutputRef; private String dataInputItemRef; private String dataOutputItemRef; @Column(name = "nbActiveInst") private int numberOfActiveInstances; @Column(name = "nbCompletedInst") private int numberOfCompletedInstances; @Column(name = "nbTerminatedInst") private int numberOfTerminatedInstances; private int loopCardinality; public SMultiInstanceActivityInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId, final boolean isSequential) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId); sequential = isSequential; } @Override public SFlowNodeType getType() { return SFlowNodeType.MULTI_INSTANCE_ACTIVITY; } public int getNumberOfInstances() { return numberOfActiveInstances + numberOfCompletedInstances + numberOfTerminatedInstances; } @Override public boolean mustExecuteOnAbortOrCancelProcess() { // it's not necessary to execute it because this will be done when the last child reaches the aborted state return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SPendingActivityMapping.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * Used to get pending activities of a user. * Link activity to * - an actor if the activity is not filtered * - a user if the activity is filtered * When the activity is created we insert one or more of this object: * - if the activity is not filtered we insert one of them for each actor of the activity with inside the actorId * - if the activity is filtered we execute those filter and insert a one of them for each user that is filtered * * @author Baptiste Mesta */ @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "pending_mapping") public class SPendingActivityMapping implements PersistentObject { public static final String ACTOR_ID = "actorId"; public static final String ACTIVITY_ID = "activityId"; public static final String USER_ID = "userId"; @Id private long id; /** * the id of the activity */ private long activityId; /** * the id of the actor or -1 if the mapping is on user */ @Builder.Default private long actorId = -1; /** * the id of the user or -1 if the mapping is on actor */ @Builder.Default private long userId = -1; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SProcessInstance.java ================================================ /** * 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.core.process.instance.model; import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Elias Ricken de Medeiros * @author Yanyan Liu * @author Matthieu Chaffotte * @author Baptiste Mesta * @author Celine Souchet */ @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "process_instance") public class SProcessInstance implements PersistentObject { private static final long DEFAULT_INTERRUPTING_EVENT_ID = -1L; public static final String STRING_INDEX_KEY = "stringIndex"; public static final String STRING_INDEX_1_KEY = "stringIndex1"; public static final String STRING_INDEX_2_KEY = "stringIndex2"; public static final String STRING_INDEX_3_KEY = "stringIndex3"; public static final String STRING_INDEX_4_KEY = "stringIndex4"; public static final String STRING_INDEX_5_KEY = "stringIndex5"; public static final String LAST_UPDATE_KEY = "lastUpdate"; public static final String INTERRUPTING_EVENT_ID_KEY = "interruptingEventId"; public static final String ID_KEY = "id"; public static final String ROOT_PROCESS_INSTANCE_ID_KEY = "rootProcessInstanceId"; public static final String NAME_KEY = "name"; public static final String PROCESSDEF_ID_KEY = "processDefinitionId"; public static final String STATE_ID_KEY = "stateId"; public static final String STATE_CATEGORY_KEY = "stateCategory"; public static final String CONTAINER_ID_KEY = "containerId"; public static final String END_DATE_KEY = "endDate"; public static final String STARTED_BY_KEY = "startedBy"; public static final String STARTED_BY_SUBSTITUTE_KEY = "startedBySubstitute"; public static final String START_DATE_KEY = "startDate"; public static final String CALLER_ID = "callerId"; @Id private long id; private String name; private long processDefinitionId; private String description; @Builder.Default private long startDate = new Date().getTime(); /** * id of the user who originally started the process */ private long startedBy; /** * id of the user (delegate) who started the process for the original starter */ private long startedBySubstitute; private long endDate; private int stateId; @Builder.Default @Enumerated(EnumType.STRING) private SStateCategory stateCategory = SStateCategory.NORMAL; private long lastUpdate; private long containerId; @Builder.Default private long rootProcessInstanceId = -1; @Builder.Default private long callerId = -1; /** * The caller's SFlowNodeType if the it's called by a call activity or sub-process, null otherwise */ @Column @Enumerated(EnumType.STRING) private SFlowNodeType callerType; /** * Id of the end error event that interrupted the process instance or -1 if the process was not interrupted by a end * error event */ @Builder.Default @Column private long interruptingEventId = DEFAULT_INTERRUPTING_EVENT_ID; @Column private String stringIndex1; @Column private String stringIndex2; @Column private String stringIndex3; @Column private String stringIndex4; @Column private String stringIndex5; public SProcessInstance(final String name, final long processDefinitionId) { this.name = name; this.processDefinitionId = processDefinitionId; } public SProcessInstance(final SProcessDefinition definition) { name = definition.getName(); processDefinitionId = definition.getId(); description = definition.getDescription(); } @Override public void setId(final long id) { this.id = id; if (rootProcessInstanceId == -1) { rootProcessInstanceId = id; } } public SFlowElementsContainerType getContainerType() { return SFlowElementsContainerType.PROCESS; } public boolean hasBeenInterruptedByEvent() { return getInterruptingEventId() != -1; } /** * Determines if this instance is a root process instance. That is, it is neither a process called by a call * activity, neither a sub-process * * @return true if it's a root process instance; false otherwise. */ public boolean isRootInstance() { return callerId <= 0; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SReceiveTaskInstance.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; /** * @author Julien Molinaro */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("receive") public class SReceiveTaskInstance extends SActivityInstance { public SReceiveTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); } @Override public SFlowNodeType getType() { return SFlowNodeType.RECEIVE_TASK; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SSendTaskInstance.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("send") public class SSendTaskInstance extends SActivityInstance { public SSendTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); } @Override public SFlowNodeType getType() { return SFlowNodeType.SEND_TASK; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SStateCategory.java ================================================ /** * 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.core.process.instance.model; /** * @author Elias Ricken de Medeiros */ public enum SStateCategory { /** * The state is part of normal states */ NORMAL, /** * The state is part of aborting states */ ABORTING, /** * The state is part of canceling states */ CANCELLING } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SSubProcessActivityInstance.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("subProc") public class SSubProcessActivityInstance extends SActivityInstance { private boolean triggeredByEvent; public SSubProcessActivityInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2, final boolean triggeredByEvent) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); this.triggeredByEvent = triggeredByEvent; } @Override public SFlowNodeType getType() { return SFlowNodeType.SUB_PROCESS; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/STaskPriority.java ================================================ /** * 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.core.process.instance.model; import org.bonitasoft.engine.commons.EnumToObjectConvertible; /** * @author Emmanuel Duchastenier */ public enum STaskPriority implements EnumToObjectConvertible { LOWEST, UNDER_NORMAL, NORMAL, ABOVE_NORMAL, HIGHEST; @Override public int fromEnum() { return ordinal(); } public static STaskPriority fromOrdinal(int n) { for (STaskPriority enumValue : values()) { if (enumValue.ordinal() == n) { return enumValue; } } throw new IllegalArgumentException("Invalid ordinal value for STaskPriority"); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/SUserTaskInstance.java ================================================ /** * 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.core.process.instance.model; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("user") public class SUserTaskInstance extends SHumanTaskInstance { public SUserTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long actorId, final STaskPriority priority, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, actorId, priority, logicalGroup1, logicalGroup2); } @Override public SFlowNodeType getType() { return SFlowNodeType.USER_TASK; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAActivityInstance.java ================================================ /** * 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.core.process.instance.model.archive; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity public abstract class SAActivityInstance extends SAFlowNodeInstance { public SAActivityInstance(final SActivityInstance activityInstance) { super(activityInstance); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAAutomaticTaskInstance.java ================================================ /** * 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.core.process.instance.model.archive; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("auto") public class SAAutomaticTaskInstance extends SAActivityInstance { public SAAutomaticTaskInstance(final SAutomaticTaskInstance sAutomaticTaskInstance) { super(sAutomaticTaskInstance); } @Override public SFlowNodeType getType() { return SFlowNodeType.AUTOMATIC_TASK; } @Override public Class getPersistentObjectInterface() { return SAutomaticTaskInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SACallActivityInstance.java ================================================ /** * 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.core.process.instance.model.archive; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("call") public class SACallActivityInstance extends SAActivityInstance { public SACallActivityInstance(final SCallActivityInstance activityInstance) { super(activityInstance); } @Override public SFlowNodeType getType() { return SFlowNodeType.CALL_ACTIVITY; } @Override public Class getPersistentObjectInterface() { return SCallActivityInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAConnectorInstance.java ================================================ /** * 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.core.process.instance.model.archive; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @Entity @Table(name = "arch_connector_instance") public class SAConnectorInstance implements ArchivedPersistentObject { private static final String FLOWNODE_TYPE = "flowNode"; private static final String PROCESS_TYPE = "process"; @Id private long id; private long archiveDate; private long sourceObjectId; private String name; private long containerId; private String connectorId; private String version; @Enumerated(EnumType.STRING) private ConnectorEvent activationEvent; private String state; private String containerType; public SAConnectorInstance(final SConnectorInstance connectorInstance) { sourceObjectId = connectorInstance.getId(); name = connectorInstance.getName(); containerId = connectorInstance.getContainerId(); connectorId = connectorInstance.getConnectorId(); version = connectorInstance.getVersion(); activationEvent = connectorInstance.getActivationEvent(); state = connectorInstance.getState(); containerType = connectorInstance.getContainerType(); } @Override public Class getPersistentObjectInterface() { return SConnectorInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAFlowNodeInstance.java ================================================ /** * 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.core.process.instance.model.archive; import javax.persistence.Column; import javax.persistence.DiscriminatorColumn; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; @Data @NoArgsConstructor @Entity @Table(name = "arch_flownode_instance") @DiscriminatorColumn(name = "kind") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) public abstract class SAFlowNodeInstance implements ArchivedPersistentObject { @Id private long id; private long archiveDate; private long sourceObjectId; private String name; private long rootContainerId; private long parentContainerId; private boolean aborting; private long logicalGroup1; private long logicalGroup2; private long logicalGroup3; private long logicalGroup4; private int stateId; private String stateName; private boolean terminal; private boolean stable; private long reachedStateDate; private long lastUpdateDate; private String displayDescription; private String displayName; private String description; private long executedBy; private long executedBySubstitute; //The field is mapped on the object and is also used for the discriminator, that is why it is not insertable and updatable @Column(insertable = false, updatable = false) private String kind; @Column(name = "flownodeDefinitionId") private long flowNodeDefinitionId; public SAFlowNodeInstance(final SFlowNodeInstance flowNodeInstance) { sourceObjectId = flowNodeInstance.getId(); name = flowNodeInstance.getName(); rootContainerId = flowNodeInstance.getRootContainerId(); parentContainerId = flowNodeInstance.getParentContainerId(); logicalGroup1 = flowNodeInstance.getLogicalGroup(0); logicalGroup2 = flowNodeInstance.getLogicalGroup(1); logicalGroup3 = flowNodeInstance.getLogicalGroup(2); logicalGroup4 = flowNodeInstance.getLogicalGroup(3); stateId = flowNodeInstance.getStateId(); stateName = flowNodeInstance.getStateName(); reachedStateDate = flowNodeInstance.getReachedStateDate(); lastUpdateDate = flowNodeInstance.getLastUpdateDate(); terminal = flowNodeInstance.isTerminal(); stable = flowNodeInstance.isStable(); displayName = flowNodeInstance.getDisplayName(); displayDescription = flowNodeInstance.getDisplayDescription(); description = flowNodeInstance.getDescription(); executedBy = flowNodeInstance.getExecutedBy(); executedBySubstitute = flowNodeInstance.getExecutedBySubstitute(); flowNodeDefinitionId = flowNodeInstance.getFlowNodeDefinitionId(); } public long getProcessDefinitionId() { return logicalGroup1; } public long getRootProcessInstanceId() { return logicalGroup2; } public long getParentActivityInstanceId() { return logicalGroup3; } public long getParentProcessInstanceId() { return logicalGroup4; } public void setLogicalGroup(final int index, final long id) { switch (index) { case 0: logicalGroup1 = id; break; case 1: logicalGroup2 = id; break; case 2: logicalGroup3 = id; break; case 3: logicalGroup4 = id; break; default: throw new IndexOutOfBoundsException("Index out of range for setLogicalGroup: " + index); } } public long getLogicalGroup(final int index) { switch (index) { case 0: return logicalGroup1; case 1: return logicalGroup2; case 2: return logicalGroup3; case 3: return logicalGroup4; default: throw new IllegalArgumentException("Invalid index: must be 0, 1, 2 or 3"); } } abstract public SFlowNodeType getType(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAGatewayInstance.java ================================================ /** * 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.core.process.instance.model.archive; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.definition.model.SGatewayType; import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("gate") public class SAGatewayInstance extends SAFlowNodeInstance { @Column @Enumerated(EnumType.STRING) private SGatewayType gatewayType; private String hitBys = ""; public SAGatewayInstance(final SGatewayInstance sGatewayInstance) { super(sGatewayInstance); gatewayType = sGatewayInstance.getGatewayType(); hitBys = sGatewayInstance.getHitBys(); } @Override public SFlowNodeType getType() { return SFlowNodeType.GATEWAY; } @Override public Class getPersistentObjectInterface() { return SGatewayInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAHumanTaskInstance.java ================================================ /** * 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.core.process.instance.model.archive; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.STaskPriority; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity public abstract class SAHumanTaskInstance extends SAActivityInstance { private long actorId; private long assigneeId; private long claimedDate; private Long expectedEndDate; private STaskPriority priority; public SAHumanTaskInstance(final SHumanTaskInstance sHumanTaskInstance) { super(sHumanTaskInstance); actorId = sHumanTaskInstance.getActorId(); assigneeId = sHumanTaskInstance.getAssigneeId(); priority = sHumanTaskInstance.getPriority(); expectedEndDate = sHumanTaskInstance.getExpectedEndDate(); claimedDate = sHumanTaskInstance.getClaimedDate(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SALoopActivityInstance.java ================================================ /** * 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.core.process.instance.model.archive; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("loop") public class SALoopActivityInstance extends SAActivityInstance { @Column(name = "loop_counter") private int loopCounter; @Column(name = "loop_max") private int loopMax; public SALoopActivityInstance(final SLoopActivityInstance sLoopActivityInstance) { super(sLoopActivityInstance); loopMax = sLoopActivityInstance.getLoopMax(); loopCounter = sLoopActivityInstance.getLoopCounter(); } @Override public SFlowNodeType getType() { return SFlowNodeType.LOOP_ACTIVITY; } @Override public Class getPersistentObjectInterface() { return SLoopActivityInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAManualTaskInstance.java ================================================ /** * 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.core.process.instance.model.archive; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("manual") public class SAManualTaskInstance extends SAHumanTaskInstance { public SAManualTaskInstance(final SManualTaskInstance sManualTaskInstance) { super(sManualTaskInstance); } @Override public SFlowNodeType getType() { return SFlowNodeType.MANUAL_TASK; } @Override public Class getPersistentObjectInterface() { return SManualTaskInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAMultiInstanceActivityInstance.java ================================================ /** * 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.core.process.instance.model.archive; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("multi") public class SAMultiInstanceActivityInstance extends SAActivityInstance { private boolean sequential; private String loopDataInputRef; private String loopDataOutputRef; private String dataInputItemRef; private String dataOutputItemRef; @Column(name = "nbActiveInst") private int numberOfActiveInstances; @Column(name = "nbCompletedInst") private int numberOfCompletedInstances; @Column(name = "nbTerminatedInst") private int numberOfTerminatedInstances; private int loopCardinality; public SAMultiInstanceActivityInstance(final SMultiInstanceActivityInstance activityInstance) { super(activityInstance); sequential = activityInstance.isSequential(); loopDataInputRef = activityInstance.getLoopDataInputRef(); loopDataOutputRef = activityInstance.getLoopDataOutputRef(); dataInputItemRef = activityInstance.getDataInputItemRef(); dataOutputItemRef = activityInstance.getDataOutputItemRef(); numberOfActiveInstances = activityInstance.getNumberOfActiveInstances(); numberOfCompletedInstances = activityInstance.getNumberOfCompletedInstances(); numberOfTerminatedInstances = activityInstance.getNumberOfTerminatedInstances(); loopCardinality = activityInstance.getLoopCardinality(); } @Override public SFlowNodeType getType() { return SFlowNodeType.MULTI_INSTANCE_ACTIVITY; } public int getNumberOfInstances() { return numberOfActiveInstances + numberOfCompletedInstances + numberOfTerminatedInstances; } @Override public Class getPersistentObjectInterface() { return SMultiInstanceActivityInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAProcessInstance.java ================================================ /** * 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.core.process.instance.model.archive; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @Entity @Table(name = "arch_process_instance") public class SAProcessInstance implements ArchivedPersistentObject { @Id private long id; private long archiveDate; private long sourceObjectId; private String name; private long processDefinitionId; private String description; private int stateId; private long startDate; private long startedBy; private long startedBySubstitute; private long endDate; private long lastUpdate; private long rootProcessInstanceId = -1; private long callerId = -1; private String stringIndex1; private String stringIndex2; private String stringIndex3; private String stringIndex4; private String stringIndex5; public SAProcessInstance(final SProcessInstance processInstance) { sourceObjectId = processInstance.getId(); name = processInstance.getName(); processDefinitionId = processInstance.getProcessDefinitionId(); description = processInstance.getDescription(); startDate = processInstance.getStartDate(); endDate = processInstance.getEndDate(); startedBy = processInstance.getStartedBy(); startedBySubstitute = processInstance.getStartedBySubstitute(); lastUpdate = processInstance.getLastUpdate(); stateId = processInstance.getStateId(); rootProcessInstanceId = processInstance.getRootProcessInstanceId(); callerId = processInstance.getCallerId(); stringIndex1 = processInstance.getStringIndex1(); stringIndex2 = processInstance.getStringIndex2(); stringIndex3 = processInstance.getStringIndex3(); stringIndex4 = processInstance.getStringIndex4(); stringIndex5 = processInstance.getStringIndex5(); } @Override public Class getPersistentObjectInterface() { return SProcessInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAReceiveTaskInstance.java ================================================ /** * 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.core.process.instance.model.archive; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("receive") public class SAReceiveTaskInstance extends SAActivityInstance { public SAReceiveTaskInstance(final SReceiveTaskInstance sReceiveTaskInstance) { super(sReceiveTaskInstance); } @Override public SFlowNodeType getType() { return SFlowNodeType.RECEIVE_TASK; } @Override public Class getPersistentObjectInterface() { return SReceiveTaskInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SASendTaskInstance.java ================================================ /** * 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.core.process.instance.model.archive; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("send") public class SASendTaskInstance extends SAActivityInstance { public SASendTaskInstance(final SSendTaskInstance sSendTaskInstance) { super(sSendTaskInstance); } @Override public SFlowNodeType getType() { return SFlowNodeType.SEND_TASK; } @Override public Class getPersistentObjectInterface() { return SSendTaskInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SASubProcessActivityInstance.java ================================================ /** * 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.core.process.instance.model.archive; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("subProc") public class SASubProcessActivityInstance extends SAActivityInstance { private boolean triggeredByEvent; public SASubProcessActivityInstance(final SSubProcessActivityInstance activityInstance) { super(activityInstance); triggeredByEvent = activityInstance.isTriggeredByEvent(); } @Override public SFlowNodeType getType() { return SFlowNodeType.SUB_PROCESS; } @Override public Class getPersistentObjectInterface() { return SSubProcessActivityInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/SAUserTaskInstance.java ================================================ /** * 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.core.process.instance.model.archive; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("user") public class SAUserTaskInstance extends SAHumanTaskInstance { public SAUserTaskInstance(final SUserTaskInstance sUserTaskInstance) { super(sUserTaskInstance); } @Override public SFlowNodeType getType() { return SFlowNodeType.USER_TASK; } @Override public Class getPersistentObjectInterface() { return SUserTaskInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAActivityInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SAActivityInstanceBuilder { SAActivityInstanceBuilder setProcessDefinitionId(long processDefinitionId); SAActivityInstanceBuilder setRootProcessInstanceId(long processInstanceId); SAActivityInstanceBuilder setParentActivityInstanceId(long parentActivityInstanceId); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAActivityInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SAActivityInstanceBuilderFactory extends SAFlowNodeInstanceBuilderFactory { String getPriorityKey(); String getActivityInstanceIdKey(); String getAssigneeIdKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAAutomaticTaskInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SAAutomaticTaskInstanceBuilder extends SAActivityInstanceBuilder { SAAutomaticTaskInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAAutomaticTaskInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SAAutomaticTaskInstanceBuilderFactory extends SAActivityInstanceBuilderFactory { SAAutomaticTaskInstanceBuilder createNewAutomaticTaskInstance(SAutomaticTaskInstance sAutomaticTaskInstance); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SACallActivityInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SACallActivityInstanceBuilder extends SAActivityInstanceBuilder { SACallActivityInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SACallActivityInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SACallActivityInstanceBuilderFactory extends SAActivityInstanceBuilderFactory { SACallActivityInstanceBuilder createNewArchivedCallActivityInstance(SCallActivityInstance callActivityInstance); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAConnectorInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance; /** * @author Elias Ricken de Medeiros */ public interface SAConnectorInstanceBuilder { SAConnectorInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAConnectorInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; /** * @author Elias Ricken de Medeiros */ public interface SAConnectorInstanceBuilderFactory extends SANamedElementBuilderFactory { SAConnectorInstanceBuilder createNewArchivedConnectorInstance(SConnectorInstance connectorInstance); String getContainerIdKey(); String getConnectorIdKey(); String getContainerTypeKey(); String getVersionKey(); String getActivationEventKey(); String getStateKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAFlowElementInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder; public interface SAFlowElementInstanceBuilder { } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAFlowElementInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; /** * @author Emmanuel Duchastenier * @author Elias Ricken de Medeiros */ public interface SAFlowElementInstanceBuilderFactory extends SANamedElementBuilderFactory { String getDescriptionKey(); String getRootContainerIdKey(); String getParentContainerIdKey(); String getProcessDefinitionKey(); String getRootProcessInstanceKey(); String getParentProcessInstanceKey(); String getParentActivityInstanceKey(); String getKindKey(); int getProcessDefinitionIndex(); int getRootProcessInstanceIndex(); int getParentProcessInstanceIndex(); int getParentActivityInstanceIndex(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAFlowNodeInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder; public interface SAFlowNodeInstanceBuilder extends SAFlowElementInstanceBuilder { } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAFlowNodeInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; /** * @author Elias Ricken de Medeiros * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public interface SAFlowNodeInstanceBuilderFactory extends SAFlowElementInstanceBuilderFactory { String getStateIdKey(); String getStateNameKey(); String getReachedStateDateKey(); String getLastUpdateKey(); String getDisplayNameKey(); String getTerminalKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAGatewayInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance; /** * @author Hongwen Zang * @author Matthieu Chaffotte */ public interface SAGatewayInstanceBuilder extends SAFlowNodeInstanceBuilder { SAGatewayInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAGatewayInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance; /** * @author Hongwen Zang * @author Matthieu Chaffotte */ public interface SAGatewayInstanceBuilderFactory extends SAActivityInstanceBuilderFactory { SAGatewayInstanceBuilder createNewGatewayInstance(SGatewayInstance sGatewayInstance); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SALoopActivityInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SALoopActivityInstanceBuilder extends SAActivityInstanceBuilder { SALoopActivityInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SALoopActivityInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SALoopActivityInstanceBuilderFactory extends SAActivityInstanceBuilderFactory { SALoopActivityInstanceBuilder createNewLoopActivityInstance(SLoopActivityInstance sLoopActivityInstance); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAManualTaskInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SAManualTaskInstanceBuilder extends SAActivityInstanceBuilder { SAManualTaskInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAManualTaskInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SAManualTaskInstanceBuilderFactory extends SAActivityInstanceBuilderFactory { SAManualTaskInstanceBuilder createNewManualTaskInstance(SManualTaskInstance sManualTaskInstance); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAMultiInstanceActivityInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SAMultiInstanceActivityInstanceBuilder extends SAActivityInstanceBuilder { SAMultiInstanceActivityInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAMultiInstanceActivityInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SAMultiInstanceActivityInstanceBuilderFactory extends SAActivityInstanceBuilderFactory { SAMultiInstanceActivityInstanceBuilder createNewMultiInstanceActivityInstance( SMultiInstanceActivityInstance sMultiActivityInstance); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SANamedElementBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; /** * @author Elias Ricken de Medeiros */ public interface SANamedElementBuilderFactory { String getIdKey(); String getNameKey(); String getSourceObjectIdKey(); String getArchivedDateKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAProcessInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; /** * @author Baptiste Mesta * @author Yanyan Liu * @author Celine Souchet */ public interface SAProcessInstanceBuilder { SAProcessInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAProcessInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; /** * @author Baptiste Mesta * @author Yanyan Liu * @author Celine Souchet */ public interface SAProcessInstanceBuilderFactory { /** * create a new Server Archived Process Instance from a Server Process Instance * the id is kept * * @param processInstance * @return */ SAProcessInstanceBuilder createNewInstance(SProcessInstance processInstance); String getArchiveDateKey(); String getProcessDefinitionIdKey(); String getIdKey(); String getSourceObjectIdKey(); String getRootProcessInstanceIdKey(); String getEndDateKey(); String getStartDateKey(); String getLastUpdateKey(); String getStartedByKey(); String getStartedBySubstituteKey(); String getStateIdKey(); String getNameKey(); String getCallerIdKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAReceiveTaskInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance; /** * @author Julien Molinaro */ public interface SAReceiveTaskInstanceBuilder extends SAActivityInstanceBuilder { SAReceiveTaskInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAReceiveTaskInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance; /** * @author Julien Molinaro */ public interface SAReceiveTaskInstanceBuilderFactory extends SAActivityInstanceBuilderFactory { SAReceiveTaskInstanceBuilder createNewReceiveTaskInstance(SReceiveTaskInstance sReceiveTaskInstance); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SASendTaskInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance; /** * @author Baptiste Mesta */ public interface SASendTaskInstanceBuilder extends SAActivityInstanceBuilder { SASendTaskInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SASendTaskInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance; /** * @author Baptiste Mesta */ public interface SASendTaskInstanceBuilderFactory extends SAActivityInstanceBuilderFactory { SASendTaskInstanceBuilder createNewSendTaskInstance(SSendTaskInstance sSendTaskInstance); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SASubProcessActivityInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance; /** * @author Elias Ricken de Medeiros * @author Matthieu Caffotte */ public interface SASubProcessActivityInstanceBuilder extends SAActivityInstanceBuilder { SASubProcessActivityInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SASubProcessActivityInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance; /** * @author Elias Ricken de Medeiros * @author Matthieu Caffotte */ public interface SASubProcessActivityInstanceBuilderFactory extends SAActivityInstanceBuilderFactory { SASubProcessActivityInstanceBuilder createNewArchivedSubProcessActivityInstance( SSubProcessActivityInstance subProcActInst); String getTriggeredByEventKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAUserTaskInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SAUserTaskInstanceBuilder extends SAActivityInstanceBuilder { SAUserTaskInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/SAUserTaskInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SAUserTaskInstanceBuilderFactory extends SAActivityInstanceBuilderFactory { SAUserTaskInstanceBuilder createNewUserTaskInstance(SUserTaskInstance sUserTaskInstance); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/business/data/SARefBusinessDataInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder.business.data; import org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance; /** * @author Emmanuel Duchastenier */ public interface SARefBusinessDataInstanceBuilder { SARefBusinessDataInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/business/data/SARefBusinessDataInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder.business.data; import org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; /** * @author Emmanuel Duchastenier */ public interface SARefBusinessDataInstanceBuilderFactory { SARefBusinessDataInstanceBuilder createNewInstance(SProcessSimpleRefBusinessDataInstance businessDataInstance); SARefBusinessDataInstanceBuilder createNewInstance(SProcessMultiRefBusinessDataInstance businessDataInstance); SARefBusinessDataInstanceBuilder createNewInstanceForFlowNode( SFlowNodeSimpleRefBusinessDataInstance businessDataInstance); SARefBusinessDataInstanceBuilder createNewInstance(SRefBusinessDataInstance sRefBusinessDataInstance); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/SAEndEventInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder.event; import org.bonitasoft.engine.core.process.instance.model.archive.event.SAEndEventInstance; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SAEndEventInstanceBuilder { SAEndEventInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/SAEndEventInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder.event; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowNodeInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SAEndEventInstanceBuilderFactory extends SAFlowNodeInstanceBuilderFactory { SAEndEventInstanceBuilder createNewArchivedEndEventInstance(final SEndEventInstance endEvent); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/SAStartEventInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.archive.builder.event; import org.bonitasoft.engine.core.process.instance.model.archive.event.SAStartEventInstance; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SAStartEventInstanceBuilder { SAStartEventInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/SAStartEventInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.archive.builder.event; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowNodeInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SAStartEventInstanceBuilderFactory extends SAFlowNodeInstanceBuilderFactory { SAStartEventInstanceBuilder createNewArchivedStartEventInstance(final SStartEventInstance startEventInstance); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/impl/SAEndEventInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAEndEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAEndEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAFlowNodeInstanceBuilderFactoryImpl; import org.bonitasoft.engine.core.process.instance.model.archive.event.SAEndEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance; /** * @author Elias Ricken de Medeiros */ public class SAEndEventInstanceBuilderFactoryImpl extends SAFlowNodeInstanceBuilderFactoryImpl implements SAEndEventInstanceBuilderFactory { @Override public SAEndEventInstanceBuilder createNewArchivedEndEventInstance(final SEndEventInstance endEvent) { final SAEndEventInstance entity = new SAEndEventInstance(endEvent); return new SAEndEventInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/impl/SAEndEventInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAEndEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.event.SAEndEventInstance; /** * @author Elias Ricken de Medeiros */ public class SAEndEventInstanceBuilderImpl implements SAEndEventInstanceBuilder { private final SAEndEventInstance entity; public SAEndEventInstanceBuilderImpl(final SAEndEventInstance entity) { super(); this.entity = entity; } @Override public SAEndEventInstance done() { return entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/impl/SAStartEventInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAStartEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAStartEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAFlowNodeInstanceBuilderFactoryImpl; import org.bonitasoft.engine.core.process.instance.model.archive.event.SAStartEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance; /** * @author Elias Ricken de Medeiros */ public class SAStartEventInstanceBuilderFactoryImpl extends SAFlowNodeInstanceBuilderFactoryImpl implements SAStartEventInstanceBuilderFactory { @Override public SAStartEventInstanceBuilder createNewArchivedStartEventInstance( final SStartEventInstance startEventInstance) { final SAStartEventInstance entity = new SAStartEventInstance(startEventInstance); return new SAStartEventInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/event/impl/SAStartEventInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAStartEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.event.SAStartEventInstance; /** * @author Elias Ricken de Medeiros */ public class SAStartEventInstanceBuilderImpl implements SAStartEventInstanceBuilder { private final SAStartEventInstance entity; public SAStartEventInstanceBuilderImpl(final SAStartEventInstance entity) { super(); this.entity = entity; } @Override public SAStartEventInstance done() { return this.entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAActivityInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAActivityInstanceBuilderFactory; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public abstract class SAActivityInstanceBuilderFactoryImpl extends SAFlowNodeInstanceBuilderFactoryImpl implements SAActivityInstanceBuilderFactory { protected static final String PRIORITY_KEY = "priority"; protected static final String ASSIGNEE_ID_KEY = "assigneeId"; protected static final String ACTIVITY_INSTANCE_ID_KEY = "activityInstanceId"; @Override public String getPriorityKey() { return PRIORITY_KEY; } @Override public String getActivityInstanceIdKey() { return ACTIVITY_INSTANCE_ID_KEY; } @Override public String getAssigneeIdKey() { return ASSIGNEE_ID_KEY; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAActivityInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAActivityInstanceBuilder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public abstract class SAActivityInstanceBuilderImpl extends SAFlowNodeInstanceBuilderImpl implements SAActivityInstanceBuilder { protected SAActivityInstanceBuilderImpl(final SAActivityInstance entity) { super(entity); } @Override public SAActivityInstanceBuilder setParentActivityInstanceId(final long parentActivityInstanceId) { entity.setLogicalGroup(SAActivityInstanceBuilderFactoryImpl.PROCESS_DEFINITION_INDEX, parentActivityInstanceId); return this; } @Override public SAActivityInstanceBuilder setProcessDefinitionId(final long processDefinitionId) { entity.setLogicalGroup(SAActivityInstanceBuilderFactoryImpl.PROCESS_DEFINITION_INDEX, processDefinitionId); return this; } @Override public SAActivityInstanceBuilder setRootProcessInstanceId(final long processInstanceId) { entity.setLogicalGroup(SAActivityInstanceBuilderFactoryImpl.ROOT_PROCESS_INSTANCE_INDEX, processInstanceId); return this; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAAutomaticTaskInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAAutomaticTaskInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAAutomaticTaskInstanceBuilderFactory; /** * @author Baptiste Mesta */ public class SAAutomaticTaskInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl implements SAAutomaticTaskInstanceBuilderFactory { @Override public SAAutomaticTaskInstanceBuilder createNewAutomaticTaskInstance( final SAutomaticTaskInstance sAutomaticTaskInstance) { final SAAutomaticTaskInstance entity = new SAAutomaticTaskInstance(sAutomaticTaskInstance); return new SAAutomaticTaskInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAAutomaticTaskInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAAutomaticTaskInstanceBuilder; /** * @author Baptiste Mesta */ public class SAAutomaticTaskInstanceBuilderImpl extends SAActivityInstanceBuilderImpl implements SAAutomaticTaskInstanceBuilder { public SAAutomaticTaskInstanceBuilderImpl(final SAAutomaticTaskInstance entity) { super(entity); } @Override public SAAutomaticTaskInstance done() { return (SAAutomaticTaskInstance) entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SACallActivityInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SACallActivityInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SACallActivityInstanceBuilderFactory; /** * @author Elias Ricken de Medeiros */ public class SACallActivityInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl implements SACallActivityInstanceBuilderFactory { @Override public SACallActivityInstanceBuilder createNewArchivedCallActivityInstance( final SCallActivityInstance callActivityInstance) { final SACallActivityInstance entity = new SACallActivityInstance(callActivityInstance); return new SACallActivityInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SACallActivityInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.SACallActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SACallActivityInstanceBuilder; /** * @author Elias Ricken de Medeiros */ public class SACallActivityInstanceBuilderImpl extends SAActivityInstanceBuilderImpl implements SACallActivityInstanceBuilder { public SACallActivityInstanceBuilderImpl(final SACallActivityInstance entity) { super(entity); } @Override public SACallActivityInstance done() { return (SACallActivityInstance) this.entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAConnectorInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAConnectorInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAConnectorInstanceBuilderFactory; /** * @author Elias Ricken de Medeiros */ public class SAConnectorInstanceBuilderFactoryImpl extends SANamedElementBuilderFactoryImpl implements SAConnectorInstanceBuilderFactory { private static final String CONTAINER_ID_KEY = "containerId"; private static final String CONTAINER_TYPE_KEY = "containerType"; private static final String CONNECTOR_ID_KEY = "connectorId"; private static final String VERSION_KEY = "version"; private static final String ACTIVATION_EVENT_KEY = "activationEvent"; private static final String STATE_KEY = "state"; @Override public SAConnectorInstanceBuilder createNewArchivedConnectorInstance(final SConnectorInstance connectorInstance) { return new SAConnectorInstanceBuilderImpl(new SAConnectorInstance(connectorInstance)); } @Override public String getContainerIdKey() { return CONTAINER_ID_KEY; } @Override public String getContainerTypeKey() { return CONTAINER_TYPE_KEY; } @Override public String getConnectorIdKey() { return CONNECTOR_ID_KEY; } @Override public String getVersionKey() { return VERSION_KEY; } @Override public String getActivationEventKey() { return ACTIVATION_EVENT_KEY; } @Override public String getStateKey() { return STATE_KEY; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAConnectorInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAConnectorInstanceBuilder; /** * @author Elias Ricken de Medeiros */ public class SAConnectorInstanceBuilderImpl implements SAConnectorInstanceBuilder { private final SAConnectorInstance entity; public SAConnectorInstanceBuilderImpl(final SAConnectorInstance entity) { this.entity = entity; } @Override public SAConnectorInstance done() { return entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAFlowElementInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowElementInstanceBuilderFactory; /** * @author Emmanuel Duchastenier */ public abstract class SAFlowElementInstanceBuilderFactoryImpl extends SANamedElementBuilderFactoryImpl implements SAFlowElementInstanceBuilderFactory { protected static final String DESCRIPTION_KEY = "description"; protected static final String ROOT_CONTAINER_ID_KEY = "rootContainerId"; protected static final String PARENT_CONTAINER_ID_KEY = "parentContainerId"; protected static final String LOGICAL_GROUP1_KEY = "logicalGroup1"; protected static final String LOGICAL_GROUP2_KEY = "logicalGroup2"; protected static final String LOGICAL_GROUP3_KEY = "logicalGroup3"; protected static final String LOGICAL_GROUP4_KEY = "logicalGroup4"; protected static final String KIND_KEY = "kind"; protected static final int PROCESS_DEFINITION_INDEX = 0; protected static final int ROOT_PROCESS_INSTANCE_INDEX = 1; protected static final int PARENT_ACTIVITY_INSTANCE_INDEX = 2; protected static final int PARENT_PROCESS_INSTANCE_INDEX = 3; @Override public String getRootContainerIdKey() { return ROOT_CONTAINER_ID_KEY; } @Override public String getParentContainerIdKey() { return PARENT_CONTAINER_ID_KEY; } @Override public String getProcessDefinitionKey() { return LOGICAL_GROUP1_KEY; } @Override public String getRootProcessInstanceKey() { return LOGICAL_GROUP2_KEY; } @Override public String getParentProcessInstanceKey() { return LOGICAL_GROUP4_KEY; } @Override public String getParentActivityInstanceKey() { return LOGICAL_GROUP3_KEY; } @Override public int getProcessDefinitionIndex() { return PROCESS_DEFINITION_INDEX; } @Override public int getRootProcessInstanceIndex() { return ROOT_PROCESS_INSTANCE_INDEX; } @Override public int getParentProcessInstanceIndex() { return PARENT_PROCESS_INSTANCE_INDEX; } @Override public int getParentActivityInstanceIndex() { return PARENT_ACTIVITY_INSTANCE_INDEX; } @Override public String getDescriptionKey() { return DESCRIPTION_KEY; } @Override public String getKindKey() { return KIND_KEY; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAFlowNodeInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowNodeInstanceBuilderFactory; /** * @author Elias Ricken de Medeiros */ public abstract class SAFlowNodeInstanceBuilderFactoryImpl extends SAFlowElementInstanceBuilderFactoryImpl implements SAFlowNodeInstanceBuilderFactory { protected static final String STATE_ID_KEY = "stateId"; protected static final String STATE_NAME_KEY = "stateName"; protected static final String REACHED_STATE_DATE_KEY = "reachedStateDate"; protected static final String LAST_UPDATE_KEY = "lastUpdateDate"; protected static final String DISPLAY_NAME_KEY = "displayName"; protected static final String TERMINAL_KEY = "terminal"; @Override public String getStateIdKey() { return STATE_ID_KEY; } @Override public String getStateNameKey() { return STATE_NAME_KEY; } @Override public String getReachedStateDateKey() { return REACHED_STATE_DATE_KEY; } @Override public String getLastUpdateKey() { return LAST_UPDATE_KEY; } @Override public String getDisplayNameKey() { return DISPLAY_NAME_KEY; } @Override public String getTerminalKey() { return TERMINAL_KEY; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAFlowNodeInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowNodeInstanceBuilder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public abstract class SAFlowNodeInstanceBuilderImpl implements SAFlowNodeInstanceBuilder { protected final SAFlowNodeInstance entity; protected SAFlowNodeInstanceBuilderImpl(final SAFlowNodeInstance entity) { super(); this.entity = entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAGatewayInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAGatewayInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAGatewayInstanceBuilderFactory; /** * @author Hongwen Zang */ public class SAGatewayInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl implements SAGatewayInstanceBuilderFactory { @Override public SAGatewayInstanceBuilder createNewGatewayInstance(final SGatewayInstance sGatewayInstance) { final SAGatewayInstance entity = new SAGatewayInstance(sGatewayInstance); return new SAGatewayInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAGatewayInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.SAGatewayInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAGatewayInstanceBuilder; /** * @author Hongwen Zang */ public class SAGatewayInstanceBuilderImpl extends SAFlowNodeInstanceBuilderImpl implements SAGatewayInstanceBuilder { public SAGatewayInstanceBuilderImpl(final SAGatewayInstance entity) { super(entity); } @Override public SAGatewayInstance done() { return (SAGatewayInstance) entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SALoopActivityInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SALoopActivityInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SALoopActivityInstanceBuilderFactory; /** * @author Baptiste Mesta */ public class SALoopActivityInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl implements SALoopActivityInstanceBuilderFactory { @Override public SALoopActivityInstanceBuilder createNewLoopActivityInstance( final SLoopActivityInstance sLoopActivityInstance) { final SALoopActivityInstance entity = new SALoopActivityInstance(sLoopActivityInstance); return new SALoopActivityInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SALoopActivityInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.SALoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SALoopActivityInstanceBuilder; /** * @author Baptiste Mesta */ public class SALoopActivityInstanceBuilderImpl extends SAActivityInstanceBuilderImpl implements SALoopActivityInstanceBuilder { public SALoopActivityInstanceBuilderImpl(final SALoopActivityInstance entity) { super(entity); } @Override public SALoopActivityInstance done() { return (SALoopActivityInstance) entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAManualTaskInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAManualTaskInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAManualTaskInstanceBuilderFactory; /** * @author Baptiste Mesta */ public class SAManualTaskInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl implements SAManualTaskInstanceBuilderFactory { @Override public SAManualTaskInstanceBuilder createNewManualTaskInstance(final SManualTaskInstance sManualTaskInstance) { final SAManualTaskInstance entity = new SAManualTaskInstance(sManualTaskInstance); return new SAManualTaskInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAManualTaskInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAManualTaskInstanceBuilder; /** * @author Baptiste Mesta */ public class SAManualTaskInstanceBuilderImpl extends SAActivityInstanceBuilderImpl implements SAManualTaskInstanceBuilder { public SAManualTaskInstanceBuilderImpl(final SAManualTaskInstance entity) { super(entity); } @Override public SAManualTaskInstance done() { return (SAManualTaskInstance) entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAMultiInstanceActivityInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAMultiInstanceActivityInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAMultiInstanceActivityInstanceBuilderFactory; /** * @author Baptiste Mesta */ public class SAMultiInstanceActivityInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl implements SAMultiInstanceActivityInstanceBuilderFactory { @Override public SAMultiInstanceActivityInstanceBuilder createNewMultiInstanceActivityInstance( final SMultiInstanceActivityInstance sMultiActivityInstance) { final SAMultiInstanceActivityInstance entity = new SAMultiInstanceActivityInstance(sMultiActivityInstance); return new SAMultiInstanceActivityInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAMultiInstanceActivityInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.SAMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAMultiInstanceActivityInstanceBuilder; /** * @author Baptiste Mesta */ public class SAMultiInstanceActivityInstanceBuilderImpl extends SAActivityInstanceBuilderImpl implements SAMultiInstanceActivityInstanceBuilder { public SAMultiInstanceActivityInstanceBuilderImpl(final SAMultiInstanceActivityInstance entity) { super(entity); } @Override public SAMultiInstanceActivityInstance done() { return (SAMultiInstanceActivityInstance) entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SANamedElementBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SANamedElementBuilderFactory; /** * @author Elias Ricken de Medeiros */ public class SANamedElementBuilderFactoryImpl implements SANamedElementBuilderFactory { protected static final String ARCHIVED_DATE = "archiveDate"; protected static final String ID_KEY = "id"; protected static final String NAME_KEY = "name"; @Override public String getIdKey() { return ID_KEY; } @Override public String getNameKey() { return NAME_KEY; } @Override public String getSourceObjectIdKey() { return "sourceObjectId"; } @Override public String getArchivedDateKey() { return ARCHIVED_DATE; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAProcessInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory; /** * @author Baptiste Mesta * @author Yanyan Liu * @author Matthieu Chaffotte * @author Celine Souchet */ public class SAProcessInstanceBuilderFactoryImpl implements SAProcessInstanceBuilderFactory { private static final String ARCHIVE_DATE_KEY = "archiveDate"; private static final String ID_KEY = "id"; private static final String NAME_KEY = "name"; private static final String PROCESSDEF_ID_KEY = "processDefinitionId"; private static final String ROOT_PROCESS_INSTANCE_ID = "rootProcessInstanceId"; private static final String STATE_ID_KEY = "stateId"; private static final String SOURCE_OBJECT_ID_KEY = "sourceObjectId"; private static final String CALLER_ID = "callerId"; private static final String END_DATE_KEY = "endDate"; private static final String STARTED_BY_KEY = "startedBy"; private static final String STARTED_BY_SUBSTITUTE_KEY = "startedBySubstitute"; private static final String START_DATE_KEY = "startDate"; private static final String LAST_UPDATE_KEY = "lastUpdate"; @Override public SAProcessInstanceBuilder createNewInstance(final SProcessInstance processInstance) { final SAProcessInstance entity = new SAProcessInstance(processInstance); return new SAProcessInstanceBuilderImpl(entity); } @Override public String getArchiveDateKey() { return ARCHIVE_DATE_KEY; } @Override public String getProcessDefinitionIdKey() { return PROCESSDEF_ID_KEY; } @Override public String getIdKey() { return ID_KEY; } public String getRootProcessInstanceIdKey() { return ROOT_PROCESS_INSTANCE_ID; } @Override public String getSourceObjectIdKey() { return SOURCE_OBJECT_ID_KEY; } @Override public String getEndDateKey() { return END_DATE_KEY; } @Override public String getStartDateKey() { return START_DATE_KEY; } @Override public String getLastUpdateKey() { return LAST_UPDATE_KEY; } @Override public String getStartedByKey() { return STARTED_BY_KEY; } @Override public String getStartedBySubstituteKey() { return STARTED_BY_SUBSTITUTE_KEY; } @Override public String getStateIdKey() { return STATE_ID_KEY; } @Override public String getNameKey() { return NAME_KEY; } @Override public String getCallerIdKey() { return CALLER_ID; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAProcessInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilder; /** * @author Baptiste Mesta * @author Yanyan Liu * @author Matthieu Chaffotte * @author Celine Souchet */ public class SAProcessInstanceBuilderImpl implements SAProcessInstanceBuilder { private final SAProcessInstance entity; public SAProcessInstanceBuilderImpl(final SAProcessInstance entity) { super(); this.entity = entity; } @Override public SAProcessInstance done() { return entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAReceiveTaskInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAReceiveTaskInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAReceiveTaskInstanceBuilderFactory; /** * @author Julien Molinaro */ public class SAReceiveTaskInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl implements SAReceiveTaskInstanceBuilderFactory { @Override public SAReceiveTaskInstanceBuilder createNewReceiveTaskInstance(final SReceiveTaskInstance sReceiveTaskInstance) { final SAReceiveTaskInstance entity = new SAReceiveTaskInstance(sReceiveTaskInstance); return new SAReceiveTaskInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAReceiveTaskInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAReceiveTaskInstanceBuilder; /** * @author Julien Molinaro */ public class SAReceiveTaskInstanceBuilderImpl extends SAActivityInstanceBuilderImpl implements SAReceiveTaskInstanceBuilder { public SAReceiveTaskInstanceBuilderImpl(final SAReceiveTaskInstance entity) { super(entity); } @Override public SAReceiveTaskInstance done() { return (SAReceiveTaskInstance) this.entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SASendTaskInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SASendTaskInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SASendTaskInstanceBuilderFactory; /** * @author Baptiste Mesta */ public class SASendTaskInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl implements SASendTaskInstanceBuilderFactory { @Override public SASendTaskInstanceBuilder createNewSendTaskInstance(final SSendTaskInstance sSendTaskInstance) { final SASendTaskInstance entity = new SASendTaskInstance(sSendTaskInstance); return new SASendTaskInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SASendTaskInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.SASendTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SASendTaskInstanceBuilder; /** * @author Baptiste Mesta */ public class SASendTaskInstanceBuilderImpl extends SAActivityInstanceBuilderImpl implements SASendTaskInstanceBuilder { public SASendTaskInstanceBuilderImpl(final SASendTaskInstance entity) { super(entity); } @Override public SASendTaskInstance done() { return (SASendTaskInstance) entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SASubProcessActivityInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SASubProcessActivityInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SASubProcessActivityInstanceBuilderFactory; /** * @author Elias Ricken de Medeiros */ public class SASubProcessActivityInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl implements SASubProcessActivityInstanceBuilderFactory { @Override public SASubProcessActivityInstanceBuilder createNewArchivedSubProcessActivityInstance( final SSubProcessActivityInstance subProcActInst) { final SASubProcessActivityInstance entity = new SASubProcessActivityInstance(subProcActInst); return new SASubProcessActivityInstanceBuilderImpl(entity); } @Override public String getTriggeredByEventKey() { return "triggeredByEvent"; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SASubProcessActivityInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.SASubProcessActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SASubProcessActivityInstanceBuilder; /** * @author Elias Ricken de Medeiros */ public class SASubProcessActivityInstanceBuilderImpl extends SAActivityInstanceBuilderImpl implements SASubProcessActivityInstanceBuilder { public SASubProcessActivityInstanceBuilderImpl(final SASubProcessActivityInstance entity) { super(entity); } @Override public SASubProcessActivityInstance done() { return (SASubProcessActivityInstance) entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAUserTaskInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory; /** * @author Baptiste Mesta */ public class SAUserTaskInstanceBuilderFactoryImpl extends SAActivityInstanceBuilderFactoryImpl implements SAUserTaskInstanceBuilderFactory { @Override public SAUserTaskInstanceBuilder createNewUserTaskInstance(final SUserTaskInstance sUserTaskInstance) { final SAUserTaskInstance entity = new SAUserTaskInstance(sUserTaskInstance); return new SAUserTaskInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/SAUserTaskInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl; import org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilder; /** * @author Baptiste Mesta */ public class SAUserTaskInstanceBuilderImpl extends SAActivityInstanceBuilderImpl implements SAUserTaskInstanceBuilder { public SAUserTaskInstanceBuilderImpl(final SAUserTaskInstance entity) { super(entity); } @Override public SAUserTaskInstance done() { return (SAUserTaskInstance) entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/business/data/SARefBusinessDataInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl.business.data; import java.util.ArrayList; import org.bonitasoft.engine.core.process.instance.model.archive.builder.business.data.SARefBusinessDataInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.builder.business.data.SARefBusinessDataInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAFlowNodeSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.archive.business.data.SAProcessSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; /** * @author Emmanuel Duchastenier */ public class SARefBusinessDataInstanceBuilderFactoryImpl implements SARefBusinessDataInstanceBuilderFactory { @Override public SARefBusinessDataInstanceBuilder createNewInstance(SRefBusinessDataInstance sRefBusinessDataInstance) { if (sRefBusinessDataInstance instanceof SFlowNodeSimpleRefBusinessDataInstance) { return createNewInstanceForFlowNode((SFlowNodeSimpleRefBusinessDataInstance) sRefBusinessDataInstance); } else if (sRefBusinessDataInstance instanceof SProcessSimpleRefBusinessDataInstance) { return createNewInstance((SProcessSimpleRefBusinessDataInstance) sRefBusinessDataInstance); } else if (sRefBusinessDataInstance instanceof SProcessMultiRefBusinessDataInstance) { return createNewInstance((SProcessMultiRefBusinessDataInstance) sRefBusinessDataInstance); } else return null; } @Override public SARefBusinessDataInstanceBuilder createNewInstance( SProcessSimpleRefBusinessDataInstance businessDataInstance) { final SAProcessSimpleRefBusinessDataInstance entity = new SAProcessSimpleRefBusinessDataInstance(); setCommonAttributes(businessDataInstance, entity); entity.setProcessInstanceId(businessDataInstance.getProcessInstanceId()); entity.setDataId(businessDataInstance.getDataId()); return new SARefBusinessDataInstanceBuilderImpl(entity); } @Override public SARefBusinessDataInstanceBuilder createNewInstance( SProcessMultiRefBusinessDataInstance businessDataInstance) { final SAProcessMultiRefBusinessDataInstance entity = new SAProcessMultiRefBusinessDataInstance(); setCommonAttributes(businessDataInstance, entity); entity.setProcessInstanceId(businessDataInstance.getProcessInstanceId()); final ArrayList dataIds = new ArrayList<>(businessDataInstance.getDataIds().size()); for (Long dataId : businessDataInstance.getDataIds()) { dataIds.add(dataId); } entity.setDataIds(dataIds); return new SARefBusinessDataInstanceBuilderImpl(entity); } @Override public SARefBusinessDataInstanceBuilder createNewInstanceForFlowNode( SFlowNodeSimpleRefBusinessDataInstance businessDataInstance) { final SAFlowNodeSimpleRefBusinessDataInstance entity = new SAFlowNodeSimpleRefBusinessDataInstance(); setCommonAttributes(businessDataInstance, entity); entity.setFlowNodeInstanceId(businessDataInstance.getFlowNodeInstanceId()); entity.setDataId(businessDataInstance.getDataId()); return new SARefBusinessDataInstanceBuilderImpl(entity); } protected void setCommonAttributes(SRefBusinessDataInstance businessDataInstance, SARefBusinessDataInstance entity) { entity.setName(businessDataInstance.getName()); entity.setDataClassName(businessDataInstance.getDataClassName()); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/builder/impl/business/data/SARefBusinessDataInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.archive.builder.impl.business.data; import org.bonitasoft.engine.core.process.instance.model.archive.builder.business.data.SARefBusinessDataInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance; /** * @author Emmanuel Duchastenier */ public class SARefBusinessDataInstanceBuilderImpl implements SARefBusinessDataInstanceBuilder { private final SARefBusinessDataInstance entity; public SARefBusinessDataInstanceBuilderImpl(final SARefBusinessDataInstance entity) { super(); this.entity = entity; } @Override public SARefBusinessDataInstance done() { return entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/business/data/SAFlowNodeSimpleRefBusinessDataInstance.java ================================================ /** * 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.core.process.instance.model.archive.business.data; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; /** * @author Emmanuel Duchastenier */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("fn_simple_ref") public class SAFlowNodeSimpleRefBusinessDataInstance extends SASimpleRefBusinessDataInstance { @Column(name = "orig_fn_inst_id") private long flowNodeInstanceId; @Override public SRefBusinessDataInstance toSRefBusinessDataInstance() { SFlowNodeSimpleRefBusinessDataInstance refBusinessDataInstance = new SFlowNodeSimpleRefBusinessDataInstance(); refBusinessDataInstance.setId(getSourceObjectId()); refBusinessDataInstance.setName(getName()); refBusinessDataInstance.setDataClassName(getDataClassName()); refBusinessDataInstance.setDataId(getDataId()); refBusinessDataInstance.setFlowNodeInstanceId(flowNodeInstanceId); return refBusinessDataInstance; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/business/data/SAProcessMultiRefBusinessDataInstance.java ================================================ /** * 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.core.process.instance.model.archive.business.data; import java.util.List; import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.OrderColumn; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; /** * @author Emmanuel Duchastenier */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("proc_multi_ref") public class SAProcessMultiRefBusinessDataInstance extends SARefBusinessDataInstance { @Column(name = "orig_proc_inst_id") private long processInstanceId; @ElementCollection @CollectionTable(name = "arch_multi_biz_data", joinColumns = { @JoinColumn(name = "id", referencedColumnName = "id") }) @OrderColumn(name = "idx") @Column(name = "data_id") private List dataIds; @Override public SRefBusinessDataInstance toSRefBusinessDataInstance() { SProcessMultiRefBusinessDataInstance refBusinessDataInstance = new SProcessMultiRefBusinessDataInstance(); refBusinessDataInstance.setId(getSourceObjectId()); refBusinessDataInstance.setName(getName()); refBusinessDataInstance.setDataClassName(getDataClassName()); refBusinessDataInstance.setDataIds(getDataIds()); refBusinessDataInstance.setProcessInstanceId(processInstanceId); return refBusinessDataInstance; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/business/data/SAProcessSimpleRefBusinessDataInstance.java ================================================ /** * 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.core.process.instance.model.archive.business.data; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; /** * @author Emmanuel Duchastenier */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("proc_simple_ref") public class SAProcessSimpleRefBusinessDataInstance extends SASimpleRefBusinessDataInstance { @Column(name = "orig_proc_inst_id") private long processInstanceId; @Override public SRefBusinessDataInstance toSRefBusinessDataInstance() { SProcessSimpleRefBusinessDataInstance refBusinessDataInstance = new SProcessSimpleRefBusinessDataInstance(); refBusinessDataInstance.setId(getSourceObjectId()); refBusinessDataInstance.setName(getName()); refBusinessDataInstance.setDataClassName(getDataClassName()); refBusinessDataInstance.setDataId(getDataId()); refBusinessDataInstance.setProcessInstanceId(processInstanceId); return refBusinessDataInstance; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/business/data/SARefBusinessDataInstance.java ================================================ /** * 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.core.process.instance.model.archive.business.data; import javax.persistence.Column; import javax.persistence.DiscriminatorColumn; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Emmanuel Duchastenier */ @Data @NoArgsConstructor @Entity @Table(name = "arch_ref_biz_data_inst") @DiscriminatorColumn(name = "kind") public abstract class SARefBusinessDataInstance implements ArchivedPersistentObject { @Id protected long id; private String name; @Column(name = "data_classname") private String dataClassName; @Override public long getSourceObjectId() { // WARNING // There is no column sourceObjectId, return the id of the column // This is an error. we should have this column return getId(); } @Override public long getArchiveDate() { //There is no archiveDate column return 0; } public void setSourceObjectId(long id) { //Nothing to do, not persisted } public void setArchiveDate(long id) { //Nothing to do, not persisted } @Override public Class getPersistentObjectInterface() { return SRefBusinessDataInstance.class; } public abstract SRefBusinessDataInstance toSRefBusinessDataInstance(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/business/data/SASimpleRefBusinessDataInstance.java ================================================ /** * 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.core.process.instance.model.archive.business.data; import javax.persistence.Column; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * @author Emmanuel Duchastenier */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity public abstract class SASimpleRefBusinessDataInstance extends SARefBusinessDataInstance { @Column(name = "data_id") private Long dataId; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SABoundaryEventInstance.java ================================================ /** * 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.core.process.instance.model.archive.event; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("boundaryEvent") public class SABoundaryEventInstance extends SACatchEventInstance { private long activityInstanceId; public SABoundaryEventInstance(final SBoundaryEventInstance sBoundaryEventInstance) { super(sBoundaryEventInstance); } @Override public SFlowNodeType getType() { return SFlowNodeType.BOUNDARY_EVENT; } @Override public Class getPersistentObjectInterface() { return SBoundaryEventInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SACatchEventInstance.java ================================================ /** * 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.core.process.instance.model.archive.event; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity public abstract class SACatchEventInstance extends SAEventInstance { private boolean interrupting = true; public SACatchEventInstance(final SCatchEventInstance sCatchEventInstance) { super(sCatchEventInstance); interrupting = sCatchEventInstance.isInterrupting(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SAEndEventInstance.java ================================================ /** * 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.core.process.instance.model.archive.event; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("endEvent") public class SAEndEventInstance extends SAThrowEventInstance { public SAEndEventInstance(final SEndEventInstance endEvent) { super(endEvent); } @Override public SFlowNodeType getType() { return SFlowNodeType.END_EVENT; } @Override public Class getPersistentObjectInterface() { return SEndEventInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SAEventInstance.java ================================================ /** * 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.core.process.instance.model.archive.event; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity public abstract class SAEventInstance extends SAFlowNodeInstance { public SAEventInstance(final SEventInstance eventInstance) { super(eventInstance); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SAIntermediateCatchEventInstance.java ================================================ /** * 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.core.process.instance.model.archive.event; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("intermediateCatchEvent") public class SAIntermediateCatchEventInstance extends SACatchEventInstance { public SAIntermediateCatchEventInstance(final SIntermediateCatchEventInstance sIntermediateCatchEventInstance) { super(sIntermediateCatchEventInstance); } @Override public SFlowNodeType getType() { return SFlowNodeType.INTERMEDIATE_CATCH_EVENT; } @Override public Class getPersistentObjectInterface() { return SIntermediateCatchEventInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SAIntermediateThrowEventInstance.java ================================================ /** * 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.core.process.instance.model.archive.event; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("intermediateThrowEvent") public class SAIntermediateThrowEventInstance extends SAThrowEventInstance { public SAIntermediateThrowEventInstance(final SIntermediateThrowEventInstance sIntermediateThrowEventInstance) { super(sIntermediateThrowEventInstance); } @Override public SFlowNodeType getType() { return SFlowNodeType.INTERMEDIATE_THROW_EVENT; } @Override public Class getPersistentObjectInterface() { return SIntermediateThrowEventInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SAStartEventInstance.java ================================================ /** * 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.core.process.instance.model.archive.event; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("startEvent") public class SAStartEventInstance extends SACatchEventInstance { public SAStartEventInstance(final SStartEventInstance startEventInstance) { super(startEventInstance); } @Override public SFlowNodeType getType() { return SFlowNodeType.START_EVENT; } @Override public Class getPersistentObjectInterface() { return SStartEventInstance.class; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/archive/event/SAThrowEventInstance.java ================================================ /** * 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.core.process.instance.model.archive.event; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity public abstract class SAThrowEventInstance extends SAEventInstance { public SAThrowEventInstance(final SThrowEventInstance sThrowEventInstance) { super(sThrowEventInstance); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SActivityInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Yanyan Liu */ public interface SActivityInstanceBuilder extends SFlowNodeInstanceBuilder { SActivityInstance done(); SActivityInstanceBuilder setName(final String name); SActivityInstanceBuilder setDescription(String description); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SActivityInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Yanyan Liu */ public interface SActivityInstanceBuilderFactory extends SFlowNodeInstanceBuilderFactory { String getTokenCountKey(); String getAbortedByBoundaryEventIdKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SAutomaticTaskInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder; import org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance; /** * @author Baptiste Mesta * @author Celine Souchet */ public interface SAutomaticTaskInstanceBuilder extends SActivityInstanceBuilder { SAutomaticTaskInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SAutomaticTaskInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Baptiste Mesta * @author Celine Souchet */ public interface SAutomaticTaskInstanceBuilderFactory extends SActivityInstanceBuilderFactory { SAutomaticTaskInstanceBuilder createNewAutomaticTaskInstance(final String name, long flowNodeDefinitionId, final long rootContainerId, long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SCallActivityInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SCallActivityInstanceBuilder extends SActivityInstanceBuilder { } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SCallActivityInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SCallActivityInstanceBuilderFactory extends SActivityInstanceBuilderFactory { SCallActivityInstanceBuilder createNewCallActivityInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SFlowElementInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Elias Ricken de Medeiros * @author Yanyan Liu */ public interface SFlowElementInstanceBuilderFactory { String getIdKey(); String getNameKey(); String getRootContainerIdKey(); String getParentContainerIdKey(); String getProcessDefinitionKey(); String getRootProcessInstanceKey(); String getParentProcessInstanceKey(); String getParentActivityInstanceKey(); String getStateCategoryKey(); String getStableKey(); String getTerminalKey(); int getProcessDefinitionIndex(); int getRootProcessInstanceIndex(); int getParentActivityInstanceIndex(); int getParentProcessInstanceIndex(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SFlowNodeInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public interface SFlowNodeInstanceBuilder { SFlowNodeInstanceBuilder setState(FlowNodeState state); SFlowNodeInstanceBuilder setReachedStateDate(final long reachStateDate); SFlowNodeInstanceBuilder setLastUpdateDate(final long lastUpdateDate); SFlowNodeInstanceBuilder setRootContainerId(final long containerId); SFlowNodeInstanceBuilder setParentContainerId(final long containerId); SFlowNodeInstanceBuilder setProcessDefinitionId(final long processDefinitionId); SFlowNodeInstanceBuilder setRootProcessInstanceId(final long processInstanceId); SFlowNodeInstanceBuilder setParentProcessInstanceId(final long processInstanceId); SFlowNodeInstanceBuilder setParentActivityInstanceId(final long activityInstanceId); SFlowNodeInstanceBuilder setLoopCounter(int loopCounter); SFlowNodeInstanceBuilder setStateCategory(SStateCategory stateCategory); SFlowNodeInstance done(); SFlowNodeType getFlowNodeType(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SFlowNodeInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public interface SFlowNodeInstanceBuilderFactory extends SFlowElementInstanceBuilderFactory { String getStateIdKey(); String getStateNameKey(); String getPreviousStateIdKey(); String getReachStateDateKey(); String getLastUpdateDateKey(); String getDisplayNameKey(); String getDisplayDescriptionKey(); String getStateExecutingKey(); String getExecutedBy(); String getExecutedBySubstitute(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SGatewayInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder; import org.bonitasoft.engine.core.process.definition.model.SGatewayType; import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance; /** * @author Feng Hui * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SGatewayInstanceBuilder extends SFlowNodeInstanceBuilder { SGatewayInstanceBuilder setStateId(final int stateId); SGatewayInstanceBuilder setGatewayType(final SGatewayType gatewayType); SGatewayInstanceBuilder setHitBys(final String hitBys); SGatewayInstanceBuilder setProcessInstanceId(final long processInstanceId); @Override SGatewayInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SGatewayInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder; import org.bonitasoft.engine.core.process.definition.model.SGatewayType; /** * @author Feng Hui * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SGatewayInstanceBuilderFactory extends SFlowNodeInstanceBuilderFactory { SGatewayInstanceBuilder createNewInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, long parentContainerId, final SGatewayType gatewayType, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId); String getGatewayTypeKey(); String getHitBysKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SHumanTaskInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.STaskPriority; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SHumanTaskInstanceBuilder extends SActivityInstanceBuilder { SHumanTaskInstanceBuilder setAssigneeId(long assigneeId); SHumanTaskInstanceBuilder setPriority(STaskPriority priority); SHumanTaskInstanceBuilder setExpectedEndDate(Long expectedEndDate); SHumanTaskInstanceBuilder setDisplayDescription(String displayDescription); SHumanTaskInstanceBuilder setDisplayName(String displayName); @Override SHumanTaskInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SHumanTaskInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SHumanTaskInstanceBuilderFactory extends SActivityInstanceBuilderFactory { String getExpectedEndDateKey(); String getAssigneeIdKey(); String getClaimedDateKey(); String getPriorityKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SLoopActivityInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder; import org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public interface SLoopActivityInstanceBuilder extends SActivityInstanceBuilder { @Override SLoopActivityInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SLoopActivityInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public interface SLoopActivityInstanceBuilderFactory extends SActivityInstanceBuilderFactory { SLoopActivityInstanceBuilder createNewOuterTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SManualTaskInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder; import org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public interface SManualTaskInstanceBuilder extends SHumanTaskInstanceBuilder { @Override SManualTaskInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SManualTaskInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public interface SManualTaskInstanceBuilderFactory extends SHumanTaskInstanceBuilderFactory { SManualTaskInstanceBuilder createNewManualTaskInstance(String name, long flowNodeDefinitionId, long rootContainerId, long parentContainerId, long actorId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SMultiInstanceActivityInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; /** * @author Baptiste Mesta * @author Celine Souchet */ public interface SMultiInstanceActivityInstanceBuilder extends SActivityInstanceBuilder { @Override SMultiInstanceActivityInstance done(); SMultiInstanceActivityInstanceBuilder setLoopDataInputRef(String loopDataInputRef); SMultiInstanceActivityInstanceBuilder setLoopDataOutputRef(String loopDataOutputRef); SMultiInstanceActivityInstanceBuilder setDataInputItemRef(String dataInputItemRef); SMultiInstanceActivityInstanceBuilder setDataOutputItemRef(String dataOutputItemRef); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SMultiInstanceActivityInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Baptiste Mesta * @author Celine Souchet */ public interface SMultiInstanceActivityInstanceBuilderFactory extends SActivityInstanceBuilderFactory { SMultiInstanceActivityInstanceBuilder createNewOuterTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId, boolean isSequential); String getLoopCardinalityKey(); String getNumberOfActiveInstancesKey(); String getNumberOfCompletedInstancesKey(); String getNumberOfTerminatedInstancesKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SReceiveTaskInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Julien Molinaro */ public interface SReceiveTaskInstanceBuilder extends SActivityInstanceBuilder { } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SReceiveTaskInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Julien Molinaro */ public interface SReceiveTaskInstanceBuilderFactory extends SActivityInstanceBuilderFactory { SReceiveTaskInstanceBuilder createNewReceiveTaskInstance(final String name, long flowNodeDefinitionId, final long rootContainerId, long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SSendTaskInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Baptiste Mesta */ public interface SSendTaskInstanceBuilder extends SActivityInstanceBuilder { } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SSendTaskInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Baptiste Mesta */ public interface SSendTaskInstanceBuilderFactory extends SActivityInstanceBuilderFactory { SSendTaskInstanceBuilder createNewSendTaskInstance(final String name, long flowNodeDefinitionId, final long rootContainerId, long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SSubProcessActivityInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder; import org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SSubProcessActivityInstanceBuilder extends SActivityInstanceBuilder { @Override SSubProcessActivityInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SSubProcessActivityInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SSubProcessActivityInstanceBuilderFactory extends SActivityInstanceBuilderFactory { SSubProcessActivityInstanceBuilder createNewSubProcessActivityInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId, boolean isTriggeredByEvent); String getTriggeredByEventKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SUserTaskInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public interface SUserTaskInstanceBuilder extends SHumanTaskInstanceBuilder { @Override SUserTaskInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/SUserTaskInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public interface SUserTaskInstanceBuilderFactory extends SHumanTaskInstanceBuilderFactory { SUserTaskInstanceBuilder createNewUserTaskInstance(String name, long flowNodeDefinitionId, long rootContainerId, long parentContainerId, long actorId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/business/data/SRefBusinessDataInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder.business.data; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; /** * @author Matthieu Chaffotte */ public interface SRefBusinessDataInstanceBuilder { SRefBusinessDataInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/business/data/SRefBusinessDataInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder.business.data; import java.util.List; /** * @author Matthieu Chaffotte */ public interface SRefBusinessDataInstanceBuilderFactory { SRefBusinessDataInstanceBuilder createNewInstance(String name, long processInstanceId, Long dataId, String dataClassName); SRefBusinessDataInstanceBuilder createNewInstance(String name, long processInstanceId, List dataIds, String dataClassName); SRefBusinessDataInstanceBuilder createNewInstanceForFlowNode(String name, long flowNodeInstanceId, Long dataId, String dataClassName); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/business/data/SRefBusinessDataInstanceLogBuilder.java ================================================ /** * 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.core.process.instance.model.builder.business.data; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Matthieu Chaffotte */ public interface SRefBusinessDataInstanceLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/business/data/SRefBusinessDataInstanceLogBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder.business.data; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Matthieu Chaffotte */ public interface SRefBusinessDataInstanceLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { @Override SRefBusinessDataInstanceLogBuilder createNewInstance(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SBoundaryEventInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder.event; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SBoundaryEventInstanceBuilder extends SEventInstanceBuilder { } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SBoundaryEventInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder.event; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SBoundaryEventInstanceBuilderFactory extends SEventInstanceBuilderFactory { SBoundaryEventInstanceBuilder createNewBoundaryEventInstance(final String name, boolean isInterrupting, final long flowNodeDefinitionId, final long rootContainerId, long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId, long activityInstanceId); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SEndEventInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder.event; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SEndEventInstanceBuilder extends SEventInstanceBuilder { } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SEndEventInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder.event; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SEndEventInstanceBuilderFactory extends SEventInstanceBuilderFactory { SEndEventInstanceBuilder createNewEndEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SEventInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder.event; import org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; /** * @author Elias Ricken de Medeiros */ public interface SEventInstanceBuilder extends SFlowNodeInstanceBuilder { @Override SEventInstance done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SEventInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder.event; import org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilderFactory; /** * @author Elias Ricken de Medeiros */ public interface SEventInstanceBuilderFactory extends SFlowNodeInstanceBuilderFactory { } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SIntermediateCatchEventInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder.event; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SIntermediateCatchEventInstanceBuilder extends SEventInstanceBuilder { } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SIntermediateCatchEventInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder.event; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SIntermediateCatchEventInstanceBuilderFactory extends SEventInstanceBuilderFactory { SIntermediateCatchEventInstanceBuilder createNewIntermediateCatchEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SIntermediateThrowEventInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder.event; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public interface SIntermediateThrowEventInstanceBuilder extends SEventInstanceBuilder { } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SIntermediateThrowEventInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder.event; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public interface SIntermediateThrowEventInstanceBuilderFactory extends SEventInstanceBuilderFactory { SIntermediateThrowEventInstanceBuilder createNewIntermediateThrowEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SStartEventInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder.event; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SStartEventInstanceBuilder extends SEventInstanceBuilder { } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/SStartEventInstanceBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder.event; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SStartEventInstanceBuilderFactory extends SEventInstanceBuilderFactory { SStartEventInstanceBuilder createNewStartEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, long parentContainerId, long processDefinitionId, long rootProcessInstanceId, long parentProcessInstanceId); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SCorrelationContainerBuilder.java ================================================ /** * 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.core.process.instance.model.builder.event.handling; /** * @author Baptiste Mesta */ public interface SCorrelationContainerBuilder { SCorrelationContainerBuilder setCorrelation(int index, String correlation); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SMessageInstanceBuilder.java ================================================ /** * 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.core.process.instance.model.builder.event.handling; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance; /** * @author Elias Ricken de Medeiros */ public class SMessageInstanceBuilder implements SCorrelationContainerBuilder { public static final String HANDLED = "handled"; protected final SMessageInstance entity; public static SMessageInstanceBuilder create(String messageName, String targetProcess, String targetFlowNode, Long processDefinitionId, String flowNodeName) { return new SMessageInstanceBuilder( new SMessageInstance(messageName, targetProcess, targetFlowNode, processDefinitionId, flowNodeName)); } private SMessageInstanceBuilder(SMessageInstance entity) { this.entity = entity; } @Override public SMessageInstanceBuilder setCorrelation(final int index, final String correlation) { switch (index) { case 1: entity.setCorrelation1(correlation); break; case 2: entity.setCorrelation2(correlation); break; case 3: entity.setCorrelation3(correlation); break; case 4: entity.setCorrelation4(correlation); break; case 5: entity.setCorrelation5(correlation); break; default: break; } return this; } public SMessageInstance done() { return entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SMessageInstanceLogBuilder.java ================================================ /** * 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.core.process.instance.model.builder.event.handling; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Elias Ricken de Medeiros */ public interface SMessageInstanceLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { SMessageInstanceLogBuilder setMessageName(final String messageName); SMessageInstanceLogBuilder setTargetProcess(final String processName); SMessageInstanceLogBuilder setTargetFlowNode(final String flowNode); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SMessageInstanceLogBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder.event.handling; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Elias Ricken de Medeiros */ public interface SMessageInstanceLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { String getProcessNameKey(); String getStartEventNameKey(); String getMessageNameKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SWaitingErrorEventBuilder.java ================================================ /** * 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.core.process.instance.model.builder.event.handling; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SWaitingErrorEventBuilder { SWaitingErrorEvent done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SWaitingErrorEventBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder.event.handling; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SWaitingErrorEventBuilderFactory extends SWaitingEventKeyProviderBuilderFactory { SWaitingErrorEventBuilder createNewWaitingErrorBoundaryEventInstance(long processdefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId, final long flowNodeInstanceId, final String errorCode, final String processName, final long flowNodeDefinitionId, final String flowNodeName, long relatedActivityInstanceId); SWaitingErrorEventBuilder createNewWaitingErrorEventSubProcInstance(final long processdefinitionId, final long parentProcessInstanceId, final long rootProcessInstanceId, final String errorCode, final String processName, final long flowNodeDefinitionId, final String flowNodeName, final long subProcessId); String getErrorCodeKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SWaitingEventKeyProviderBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder.event.handling; /** * @author Elias Ricken de Medeiros */ public interface SWaitingEventKeyProviderBuilderFactory { String getIdKey(); String getProcessDefinitionIdKey(); String getRootProcessInstanceIdKey(); String getParentProcessInstanceIdKey(); String getFlowNodeInstanceIdKey(); String getSubProcessIdKey(); String getProcessNameKey(); String getFlowNodeNameKey(); String getEventTypeKey(); String getActiveKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SWaitingMessageEventBuilder.java ================================================ /** * 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.core.process.instance.model.builder.event.handling; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SWaitingMessageEventBuilder extends SCorrelationContainerBuilder { SWaitingMessageEvent done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SWaitingMessageEventBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder.event.handling; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SWaitingMessageEventBuilderFactory extends SWaitingEventKeyProviderBuilderFactory { int PROGRESS_FREE_KEY = 0; int PROGRESS_IN_TREATMENT_KEY = 1; SWaitingMessageEventBuilder createNewWaitingMessageStartEventInstance(long processdefinitionId, final String messageName, final String processName, final long flowNodeDefinitionId, final String flowNodeName); SWaitingMessageEventBuilder createNewWaitingMessageEventSubProcInstance(final long processdefinitionId, final long parentProcessInstanceId, final long rootProcessInstanceId, final String messageName, final String processName, final long flowNodeDefinitionId, final String flowNodeName, final long subProcessId); SWaitingMessageEventBuilder createNewWaitingMessageIntermediateEventInstance(long processdefinitionId, final long rootProcessInstanceId, final long processInstanceId, final long flowNodeInstanceId, final String messageName, final String processName, final long flowNodeDefinitionId, final String flowNodeName); SWaitingMessageEventBuilder createNewWaitingMessageBoundaryEventInstance(long processdefinitionId, final long rootProcessInstanceId, final long processInstanceId, final long flowNodeInstanceId, final String messageName, final String processName, final long flowNodeDefinitionId, final String flowNodeName); SWaitingMessageEventBuilder createNewInstance(SWaitingMessageEvent waitingMessage); String getMessageNameKey(); String getLockedKey(); String getProgressKey(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SWaitingSignalEventBuilder.java ================================================ /** * 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.core.process.instance.model.builder.event.handling; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent; /** * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SWaitingSignalEventBuilder { SWaitingSignalEvent done(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/SWaitingSignalEventBuilderFactory.java ================================================ /** * 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.core.process.instance.model.builder.event.handling; /** * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SWaitingSignalEventBuilderFactory extends SWaitingEventKeyProviderBuilderFactory { SWaitingSignalEventBuilder createNewWaitingSignalStartEventInstance(final long processdefinitionId, final String signalName, final String processName, final long flowNodeDefinitionId, final String flowNodeName); SWaitingSignalEventBuilder createNewWaitingSignalEventSubProcInstance(final long processdefinitionId, final long parentProcessInstanceId, final long rootProcessInstanceId, final String signalName, final String processName, final long flowNodeDefinitionId, final String flowNodeName, final long subProcessId); SWaitingSignalEventBuilder createNewWaitingSignalIntermediateEventInstance(long processdefinitionId, final long rootProcessInstanceId, final long processInstanceId, final long flowNodeInstanceId, final String signalName, final String processName, final long flowNodeDefinitionId, final String flowNodeName); SWaitingSignalEventBuilder createNewWaitingSignalBoundaryEventInstance(long processdefinitionId, final long rootProcessInstanceId, final long processInstanceId, final long flowNodeInstanceId, final String signalName, final String processName, final long flowNodeDefinitionId, final String flowNodeName); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/impl/SWaitingErrorEventBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.handling.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SWaitingErrorEventBuilderFactoryImpl extends SWaitingEventKeyProviderBuilderFactoryImpl implements SWaitingErrorEventBuilderFactory { @Override public SWaitingErrorEventBuilder createNewWaitingErrorBoundaryEventInstance(final long processdefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId, final long flowNodeInstanceId, final String errorCode, final String processName, final long flowNodeDefinitionId, final String flowNodeName, final long relatedActivityInstanceId) { final SWaitingErrorEvent entity = new SWaitingErrorEvent(SBPMEventType.BOUNDARY_EVENT, processdefinitionId, processName, flowNodeDefinitionId, flowNodeName, errorCode); entity.setFlowNodeInstanceId(flowNodeInstanceId); entity.setRootProcessInstanceId(rootProcessInstanceId); entity.setParentProcessInstanceId(parentProcessInstanceId); entity.setRelatedActivityInstanceId(relatedActivityInstanceId); return new SWaitingErrorEventBuilderImpl(entity); } @Override public SWaitingErrorEventBuilder createNewWaitingErrorEventSubProcInstance(final long processdefinitionId, final long parentProcessInstanceId, final long rootProcessInstanceId, final String errorCode, final String processName, final long flowNodeDefinitionId, final String flowNodeName, final long subProcessId) { final SWaitingErrorEvent entity = new SWaitingErrorEvent(SBPMEventType.EVENT_SUB_PROCESS, processdefinitionId, processName, flowNodeDefinitionId, flowNodeName, errorCode); entity.setRootProcessInstanceId(rootProcessInstanceId); entity.setParentProcessInstanceId(parentProcessInstanceId); entity.setSubProcessId(subProcessId); return new SWaitingErrorEventBuilderImpl(entity); } @Override public String getErrorCodeKey() { return "errorCode"; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/impl/SWaitingErrorEventBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.handling.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilder; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SWaitingErrorEventBuilderImpl implements SWaitingErrorEventBuilder { private final SWaitingErrorEvent entity; public SWaitingErrorEventBuilderImpl(final SWaitingErrorEvent entity) { super(); this.entity = entity; } @Override public SWaitingErrorEvent done() { return entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/impl/SWaitingEventKeyProviderBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.handling.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingEventKeyProviderBuilderFactory; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SWaitingEventKeyProviderBuilderFactoryImpl implements SWaitingEventKeyProviderBuilderFactory { @Override public String getIdKey() { return "id"; } @Override public String getProcessDefinitionIdKey() { return "processDefinitionId"; } @Override public String getRootProcessInstanceIdKey() { return "rootProcessInstanceId"; } @Override public String getParentProcessInstanceIdKey() { return "parentProcessInstanceId"; } @Override public String getFlowNodeInstanceIdKey() { return "flowNodeInstanceId"; } @Override public String getSubProcessIdKey() { return "subProcessId"; } @Override public String getProcessNameKey() { return "processName"; } @Override public String getFlowNodeNameKey() { return "flowNodeName"; } @Override public String getEventTypeKey() { return "eventType"; } @Override public String getActiveKey() { return "active"; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/impl/SWaitingMessageEventBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.handling.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SWaitingMessageEventBuilderFactoryImpl extends SWaitingEventKeyProviderBuilderFactoryImpl implements SWaitingMessageEventBuilderFactory { @Override public SWaitingMessageEventBuilder createNewWaitingMessageStartEventInstance(final long processdefinitionId, final String messageName, final String processName, final long flowNodeDefinitionId, final String flowNodeName) { final SWaitingMessageEvent entity = new SWaitingMessageEvent(SBPMEventType.START_EVENT, processdefinitionId, processName, flowNodeDefinitionId, flowNodeName, messageName); return new SWaitingMessageEventBuilderImpl(entity); } @Override public SWaitingMessageEventBuilder createNewWaitingMessageEventSubProcInstance(final long processdefinitionId, final long parentProcessInstanceId, final long rootProcessInstanceId, final String messageName, final String processName, final long flowNodeDefinitionId, final String flowNodeName, final long subProcessId) { final SWaitingMessageEvent entity = new SWaitingMessageEvent(SBPMEventType.EVENT_SUB_PROCESS, processdefinitionId, processName, flowNodeDefinitionId, flowNodeName, messageName); entity.setRootProcessInstanceId(rootProcessInstanceId); entity.setParentProcessInstanceId(parentProcessInstanceId); entity.setSubProcessId(subProcessId); return new SWaitingMessageEventBuilderImpl(entity); } @Override public SWaitingMessageEventBuilder createNewWaitingMessageIntermediateEventInstance(final long processdefinitionId, final long rootProcessInstanceId, final long processInstanceId, final long flowNodeInstanceId, final String messageName, final String processName, final long flowNodeDefinitionId, final String flowNodeName) { final SWaitingMessageEvent entity = new SWaitingMessageEvent(SBPMEventType.INTERMEDIATE_CATCH_EVENT, processdefinitionId, processName, flowNodeDefinitionId, flowNodeName, messageName); entity.setRootProcessInstanceId(rootProcessInstanceId); entity.setParentProcessInstanceId(processInstanceId); entity.setFlowNodeInstanceId(flowNodeInstanceId); return new SWaitingMessageEventBuilderImpl(entity); } @Override public SWaitingMessageEventBuilder createNewWaitingMessageBoundaryEventInstance(final long processdefinitionId, final long rootProcessInstanceId, final long processInstanceId, final long flowNodeInstanceId, final String messageName, final String processName, final long flowNodeDefinitionId, final String flowNodeName) { final SWaitingMessageEvent entity = new SWaitingMessageEvent(SBPMEventType.BOUNDARY_EVENT, processdefinitionId, processName, flowNodeDefinitionId, flowNodeName, messageName); entity.setRootProcessInstanceId(rootProcessInstanceId); entity.setParentProcessInstanceId(processInstanceId); entity.setFlowNodeInstanceId(flowNodeInstanceId); return new SWaitingMessageEventBuilderImpl(entity); } @Override public SWaitingMessageEventBuilder createNewInstance(final SWaitingMessageEvent waitingMessage) { final SWaitingMessageEvent entity = new SWaitingMessageEvent(waitingMessage.getEventType(), waitingMessage.getProcessDefinitionId(), waitingMessage.getProcessName(), waitingMessage.getFlowNodeDefinitionId(), waitingMessage.getFlowNodeName(), waitingMessage.getMessageName()); entity.setRootProcessInstanceId(waitingMessage.getRootProcessInstanceId()); entity.setParentProcessInstanceId(waitingMessage.getParentProcessInstanceId()); entity.setFlowNodeInstanceId(waitingMessage.getFlowNodeInstanceId()); return new SWaitingMessageEventBuilderImpl(entity); } @Override public String getMessageNameKey() { return "messageName"; } @Override public String getLockedKey() { return "locked"; } @Override public String getProgressKey() { return "progress"; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/impl/SWaitingMessageEventBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.handling.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilder; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SWaitingMessageEventBuilderImpl implements SWaitingMessageEventBuilder { private final SWaitingMessageEvent entity; public SWaitingMessageEventBuilderImpl(final SWaitingMessageEvent entity) { super(); this.entity = entity; } @Override public SWaitingMessageEvent done() { return entity; } @Override public SWaitingMessageEventBuilder setCorrelation(final int index, final String correlation) { switch (index) { case 1: entity.setCorrelation1(correlation); break; case 2: entity.setCorrelation2(correlation); break; case 3: entity.setCorrelation3(correlation); break; case 4: entity.setCorrelation4(correlation); break; case 5: entity.setCorrelation5(correlation); break; default: break; } return this; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/impl/SWaitingSignalEventBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.handling.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingSignalEventBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingSignalEventBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent; /** * @author Zhao Na * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SWaitingSignalEventBuilderFactoryImpl extends SWaitingEventKeyProviderBuilderFactoryImpl implements SWaitingSignalEventBuilderFactory { @Override public SWaitingSignalEventBuilder createNewWaitingSignalStartEventInstance(final long processdefinitionId, final String signalName, final String processName, final long flowNodeDefinitionId, final String flowNodeName) { final SWaitingSignalEvent entity = new SWaitingSignalEvent(SBPMEventType.START_EVENT, processdefinitionId, processName, flowNodeDefinitionId, flowNodeName, signalName); return new SWaitingSignalEventBuilderImpl(entity); } @Override public SWaitingSignalEventBuilder createNewWaitingSignalEventSubProcInstance(final long processdefinitionId, final long parentProcessInstanceId, final long rootProcessInstanceId, final String signalName, final String processName, final long flowNodeDefinitionId, final String flowNodeName, final long subProcessId) { final SWaitingSignalEvent entity = new SWaitingSignalEvent(SBPMEventType.EVENT_SUB_PROCESS, processdefinitionId, processName, flowNodeDefinitionId, flowNodeName, signalName); entity.setParentProcessInstanceId(parentProcessInstanceId); entity.setRootProcessInstanceId(rootProcessInstanceId); entity.setSubProcessId(subProcessId); return new SWaitingSignalEventBuilderImpl(entity); } @Override public SWaitingSignalEventBuilder createNewWaitingSignalIntermediateEventInstance(final long processdefinitionId, final long rootProcessInstanceId, final long processInstanceId, final long flowNodeInstanceId, final String signalName, final String processName, final long flowNodeDefinitionId, final String flowNodeName) { return createNonStartEvent(processdefinitionId, rootProcessInstanceId, processInstanceId, flowNodeInstanceId, signalName, processName, flowNodeDefinitionId, flowNodeName, SBPMEventType.INTERMEDIATE_CATCH_EVENT); } protected SWaitingSignalEventBuilder createNonStartEvent(final long processdefinitionId, final long rootProcessInstanceId, final long processInstanceId, final long flowNodeInstanceId, final String signalName, final String processName, final long flowNodeDefinitionId, final String flowNodeName, final SBPMEventType eventType) { final SWaitingSignalEvent entity = new SWaitingSignalEvent(eventType, processdefinitionId, processName, flowNodeDefinitionId, flowNodeName, signalName); entity.setFlowNodeInstanceId(flowNodeInstanceId); entity.setRootProcessInstanceId(rootProcessInstanceId); entity.setParentProcessInstanceId(processInstanceId); return new SWaitingSignalEventBuilderImpl(entity); } @Override public SWaitingSignalEventBuilder createNewWaitingSignalBoundaryEventInstance(final long processdefinitionId, final long rootProcessInstanceId, final long processInstanceId, final long flowNodeInstanceId, final String signalName, final String processName, final long flowNodeDefinitionId, final String flowNodeName) { return createNonStartEvent(processdefinitionId, rootProcessInstanceId, processInstanceId, flowNodeInstanceId, signalName, processName, flowNodeDefinitionId, flowNodeName, SBPMEventType.BOUNDARY_EVENT); } public static SWaitingSignalEventBuilder getInstance() { return new SWaitingSignalEventBuilderImpl(null); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/handling/impl/SWaitingSignalEventBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.handling.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingSignalEventBuilder; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent; /** * @author Zhao Na * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SWaitingSignalEventBuilderImpl implements SWaitingSignalEventBuilder { private final SWaitingSignalEvent entity; public SWaitingSignalEventBuilderImpl(final SWaitingSignalEvent entity) { super(); this.entity = entity; } @Override public SWaitingSignalEvent done() { return entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SBoundaryEventInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SBoundaryEventInstanceBuilderFactoryImpl extends SEventInstanceBuilderFactoryImpl implements SBoundaryEventInstanceBuilderFactory { @Override public SBoundaryEventInstanceBuilder createNewBoundaryEventInstance(final String name, final boolean isInterrupting, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId, final long activityInstanceId) { final SBoundaryEventInstance entity = new SBoundaryEventInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId); entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); entity.setActivityInstanceId(activityInstanceId); entity.setInterrupting(isInterrupting); return new SBoundaryEventInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SBoundaryEventInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SBoundaryEventInstanceBuilderImpl extends SEventInstanceBuilderImpl implements SBoundaryEventInstanceBuilder { protected SBoundaryEventInstanceBuilderImpl(final SBoundaryEventInstance entity) { super(entity); } @Override public SEventInstance done() { return (SEventInstance) entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SEndEventInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SEndEventInstanceBuilderFactoryImpl extends SEventInstanceBuilderFactoryImpl implements SEndEventInstanceBuilderFactory { @Override public SEndEventInstanceBuilder createNewEndEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SEndEventInstance entity = new SEndEventInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId); entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); return new SEndEventInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SEndEventInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.event.SEndEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SEndEventInstanceBuilderImpl extends SEventInstanceBuilderImpl implements SEndEventInstanceBuilder { public SEndEventInstanceBuilderImpl(final SEndEventInstance entity) { super(entity); } @Override public SEventInstance done() { return (SEventInstance) entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SEventInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.SEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.impl.SFlowNodeInstanceBuilderFactoryImpl; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta */ public abstract class SEventInstanceBuilderFactoryImpl extends SFlowNodeInstanceBuilderFactoryImpl implements SEventInstanceBuilderFactory { private static final String ID_KEY = "id"; private static final String NAME_KEY = "name"; @Override public String getIdKey() { return ID_KEY; } @Override public String getNameKey() { return NAME_KEY; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SEventInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.SEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.impl.SFlowNodeInstanceBuilderImpl; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta */ public abstract class SEventInstanceBuilderImpl extends SFlowNodeInstanceBuilderImpl implements SEventInstanceBuilder { protected SEventInstanceBuilderImpl(SEventInstance entity) { super(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SIntermediateCatchEventInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta */ public class SIntermediateCatchEventInstanceBuilderFactoryImpl extends SEventInstanceBuilderFactoryImpl implements SIntermediateCatchEventInstanceBuilderFactory { @Override public SIntermediateCatchEventInstanceBuilder createNewIntermediateCatchEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SIntermediateCatchEventInstance entity = new SIntermediateCatchEventInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId); entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); return new SIntermediateCatchEventInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SIntermediateCatchEventInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta */ public class SIntermediateCatchEventInstanceBuilderImpl extends SEventInstanceBuilderImpl implements SIntermediateCatchEventInstanceBuilder { public SIntermediateCatchEventInstanceBuilderImpl(final SIntermediateCatchEventInstance entity) { super(entity); } @Override public SEventInstance done() { return (SEventInstance) entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SIntermediateThrowEventInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance; /** * @author Matthieu Chaffotte * @author Baptiste Mesta * @author Celine Souchet */ public class SIntermediateThrowEventInstanceBuilderFactoryImpl extends SEventInstanceBuilderFactoryImpl implements SIntermediateThrowEventInstanceBuilderFactory { @Override public SIntermediateThrowEventInstanceBuilder createNewIntermediateThrowEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SIntermediateThrowEventInstance entity = new SIntermediateThrowEventInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId); entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); return new SIntermediateThrowEventInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SIntermediateThrowEventInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateThrowEventInstance; /** * @author Matthieu Chaffotte * @author Baptiste Mesta * @author Celine Souchet */ public class SIntermediateThrowEventInstanceBuilderImpl extends SEventInstanceBuilderImpl implements SIntermediateThrowEventInstanceBuilder { public SIntermediateThrowEventInstanceBuilderImpl(final SIntermediateThrowEventInstance entity) { super(entity); } @Override public SEventInstance done() { return (SEventInstance) entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SStartEventInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta * @author Celine Souchet */ public class SStartEventInstanceBuilderFactoryImpl extends SEventInstanceBuilderFactoryImpl implements SStartEventInstanceBuilderFactory { @Override public SStartEventInstanceBuilder createNewStartEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SStartEventInstance entity = new SStartEventInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId); entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); return new SStartEventInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/event/impl/SStartEventInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.event.impl; import org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta * @author Celine Souchet */ public class SStartEventInstanceBuilderImpl extends SEventInstanceBuilderImpl implements SStartEventInstanceBuilder { public SStartEventInstanceBuilderImpl(final SStartEventInstance entity) { super(entity); } @Override public SStartEventInstance done() { return (SStartEventInstance) entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SActivityInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.builder.SActivityInstanceBuilderFactory; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros */ public abstract class SActivityInstanceBuilderFactoryImpl extends SFlowNodeInstanceBuilderFactoryImpl implements SActivityInstanceBuilderFactory { private static final String ID_KEY = "id"; private static final String NAME_KEY = "name"; private static final String TOKEN_COUNT_KEY = "tokenCount"; private static final String ABORTED_BY_BOUNDARY_KEY = "abortedByBoundary"; @Override public String getIdKey() { return ID_KEY; } @Override public String getNameKey() { return NAME_KEY; } @Override public String getTokenCountKey() { return TOKEN_COUNT_KEY; } @Override public String getAbortedByBoundaryEventIdKey() { return ABORTED_BY_BOUNDARY_KEY; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SActivityInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SActivityInstanceBuilder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros */ public abstract class SActivityInstanceBuilderImpl extends SFlowNodeInstanceBuilderImpl implements SActivityInstanceBuilder { protected SActivityInstanceBuilderImpl(final SActivityInstance entity) { super(entity); } @Override public SActivityInstanceBuilder setName(final String name) { this.entity.setName(name); return this; } @Override public SActivityInstanceBuilder setDescription(final String description) { this.entity.setDescription(description); return this; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SAutomaticTaskInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilderFactory; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class SAutomaticTaskInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl implements SAutomaticTaskInstanceBuilderFactory { @Override public SAutomaticTaskInstanceBuilder createNewAutomaticTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SAutomaticTaskInstance activityInstanceImpl = new SAutomaticTaskInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId); activityInstanceImpl.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); return new SAutomaticTaskInstanceBuilderImpl(activityInstanceImpl); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SAutomaticTaskInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class SAutomaticTaskInstanceBuilderImpl extends SActivityInstanceBuilderImpl implements SAutomaticTaskInstanceBuilder { public SAutomaticTaskInstanceBuilderImpl(final SAutomaticTaskInstance activityInstanceImpl) { super(activityInstanceImpl); } @Override public SAutomaticTaskInstance done() { return (SAutomaticTaskInstance) this.entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SCallActivityInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilderFactory; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SCallActivityInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl implements SCallActivityInstanceBuilderFactory { @Override public SCallActivityInstanceBuilder createNewCallActivityInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SCallActivityInstance callActivityInstance = new SCallActivityInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId); callActivityInstance.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); return new SCallActivityInstanceBuilderImpl(callActivityInstance); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SCallActivityInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilder; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SCallActivityInstanceBuilderImpl extends SActivityInstanceBuilderImpl implements SCallActivityInstanceBuilder { public SCallActivityInstanceBuilderImpl(final SCallActivityInstance callActivityInstance) { super(callActivityInstance); } @Override public SCallActivityInstance done() { return (SCallActivityInstance) this.entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SFlowElementInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.builder.SFlowElementInstanceBuilderFactory; /** * @author Elias Ricken de Medeiros * @author Yanyan Liu */ public abstract class SFlowElementInstanceBuilderFactoryImpl implements SFlowElementInstanceBuilderFactory { protected static final String ROOT_CONTAINER_ID_KEY = "rootContainerId"; protected static final String PARENT_CONTAINER_ID_KEY = "parentContainerId"; protected static final String LOGICAL_GROUP1_KEY = "logicalGroup1"; protected static final String LOGICAL_GROUP2_KEY = "logicalGroup2"; protected static final String LOGICAL_GROUP3_KEY = "logicalGroup3"; protected static final String LOGICAL_GROUP4_KEY = "logicalGroup4"; protected static final String STABLE_KEY = "stable"; protected static final String TERMINAL_KEY = "terminal"; protected static final int PROCESS_DEFINITION_INDEX = 0; protected static final int ROOT_PROCESS_INSTANCE_INDEX = 1; protected static final int PARENT_ACTIVITY_INSTANCE_INDEX = 2; protected static final int PARENT_PROCESS_INSTANCE_INDEX = 3; @Override public String getRootContainerIdKey() { return ROOT_CONTAINER_ID_KEY; } @Override public String getParentContainerIdKey() { return PARENT_CONTAINER_ID_KEY; } @Override public String getProcessDefinitionKey() { return LOGICAL_GROUP1_KEY; } @Override public String getRootProcessInstanceKey() { return LOGICAL_GROUP2_KEY; } @Override public String getParentProcessInstanceKey() { return LOGICAL_GROUP4_KEY; } @Override public String getParentActivityInstanceKey() { return LOGICAL_GROUP3_KEY; } @Override public String getStateCategoryKey() { return "stateCategory"; } @Override public int getProcessDefinitionIndex() { return PROCESS_DEFINITION_INDEX; } @Override public int getRootProcessInstanceIndex() { return ROOT_PROCESS_INSTANCE_INDEX; } @Override public int getParentProcessInstanceIndex() { return PARENT_PROCESS_INSTANCE_INDEX; } @Override public int getParentActivityInstanceIndex() { return PARENT_ACTIVITY_INSTANCE_INDEX; } @Override public String getStableKey() { return STABLE_KEY; } @Override public String getTerminalKey() { return TERMINAL_KEY; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SFlowNodeInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilderFactory; /** * @author Baptiste Mesta */ public abstract class SFlowNodeInstanceBuilderFactoryImpl extends SFlowElementInstanceBuilderFactoryImpl implements SFlowNodeInstanceBuilderFactory { protected static final String DISPLAY_DESCRIPTION = "displayDescription"; protected static final String DISPLAY_NAME = "displayName"; protected static final String STATE_ID_KEY = "stateId"; protected static final String STATE_NAME_KEY = "stateName"; protected static final String PREVIOUS_STATE_ID_KEY = "previousStateId"; protected static final String LAST_UPDATE_KEY = "lastUpdateDate"; protected static final String REACHED_STATE_DATE_KEY = "reachedStateDate"; protected static final String EXECUTE_BY_KEY = "executedBy"; protected static final String EXECUTE_FOR_KEY = "executedBySubstitute"; protected static final String STATE_EXECUTING_KEY = "stateExecuting"; @Override public String getDisplayDescriptionKey() { return DISPLAY_DESCRIPTION; } @Override public String getDisplayNameKey() { return DISPLAY_NAME; } @Override public String getStateExecutingKey() { return STATE_EXECUTING_KEY; } @Override public String getExecutedBy() { return EXECUTE_BY_KEY; } @Override public String getExecutedBySubstitute() { return EXECUTE_FOR_KEY; } @Override public String getStateIdKey() { return STATE_ID_KEY; } @Override public String getStateNameKey() { return STATE_NAME_KEY; } @Override public String getPreviousStateIdKey() { return PREVIOUS_STATE_ID_KEY; } @Override public String getLastUpdateDateKey() { return LAST_UPDATE_KEY; } @Override public String getReachStateDateKey() { return REACHED_STATE_DATE_KEY; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SFlowNodeInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilder; /** * @author Baptiste Mesta */ public abstract class SFlowNodeInstanceBuilderImpl implements SFlowNodeInstanceBuilder { protected final SFlowNodeInstance entity; public SFlowNodeInstanceBuilderImpl(final SFlowNodeInstance entity) { super(); this.entity = entity; } @Override public SFlowNodeInstanceBuilder setState(final FlowNodeState state) { entity.setStateId(state.getId()); entity.setStateName(state.getName()); entity.setStable(state.isStable()); entity.setTerminal(state.isTerminal()); return this; } @Override public SFlowNodeInstanceBuilder setLastUpdateDate(final long lastUpdateDate) { this.entity.setLastUpdateDate(lastUpdateDate); return this; } @Override public SFlowNodeInstanceBuilder setReachedStateDate(final long reachedStateDate) { this.entity.setReachedStateDate(reachedStateDate); return this; } @Override public SFlowNodeInstanceBuilder setRootContainerId(final long containerId) { this.entity.setRootContainerId(containerId); return this; } @Override public SFlowNodeInstanceBuilder setParentContainerId(final long processInstanceId) { this.entity.setParentContainerId(processInstanceId); return this; } @Override public SFlowNodeInstanceBuilder setProcessDefinitionId(final long processDefinitionId) { this.entity.setLogicalGroup(SFlowNodeInstanceBuilderFactoryImpl.PROCESS_DEFINITION_INDEX, processDefinitionId); return this; } @Override public SFlowNodeInstanceBuilder setRootProcessInstanceId(final long processInstanceId) { this.entity.setLogicalGroup(SFlowNodeInstanceBuilderFactoryImpl.ROOT_PROCESS_INSTANCE_INDEX, processInstanceId); return this; } @Override public SFlowNodeInstanceBuilder setParentProcessInstanceId(final long parentProcessInstanceId) { this.entity.setLogicalGroup(SFlowNodeInstanceBuilderFactoryImpl.PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); return this; } @Override public SFlowNodeInstanceBuilder setParentActivityInstanceId(final long activityInstanceId) { this.entity.setLogicalGroup(SFlowNodeInstanceBuilderFactoryImpl.PARENT_ACTIVITY_INSTANCE_INDEX, activityInstanceId); return this; } @Override public SFlowNodeInstanceBuilder setLoopCounter(final int loopCounter) { this.entity.setLoopCounter(loopCounter); return this; } @Override public SFlowNodeInstanceBuilder setStateCategory(final SStateCategory stateCategory) { this.entity.setStateCategory(stateCategory); return this; } @Override public SFlowNodeType getFlowNodeType() { return this.entity.getType(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SGatewayInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.definition.model.SGatewayType; import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilderFactory; /** * @author Feng Hui * @author Celine Souchet */ public class SGatewayInstanceBuilderFactoryImpl extends SFlowNodeInstanceBuilderFactoryImpl implements SGatewayInstanceBuilderFactory { private static final String ID_KEY = "id"; private static final String NAME_KEY = "name"; private static final String STATE_ID_KEY = "stateId"; private static final String GATEWAY_TYPE_KEY = "gatewayType"; private static final String HITBYS = "hitBys"; @Override public SGatewayInstanceBuilder createNewInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final SGatewayType gatewayType, final long processDefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SGatewayInstance entity = new SGatewayInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, gatewayType, processDefinitionId, rootProcessInstanceId); entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); return new SGatewayInstanceBuilderImpl(entity); } @Override public String getIdKey() { return ID_KEY; } @Override public String getNameKey() { return NAME_KEY; } @Override public String getStateIdKey() { return STATE_ID_KEY; } @Override public String getGatewayTypeKey() { return GATEWAY_TYPE_KEY; } @Override public String getHitBysKey() { return HITBYS; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SGatewayInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.definition.model.SGatewayType; import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilder; /** * @author Feng Hui * @author Celine Souchet */ public class SGatewayInstanceBuilderImpl extends SFlowNodeInstanceBuilderImpl implements SGatewayInstanceBuilder { public SGatewayInstanceBuilderImpl(final SGatewayInstance entity) { super(entity); } @Override public SGatewayInstanceBuilder setStateId(final int stateId) { entity.setStateId(stateId); return this; } @Override public SGatewayInstanceBuilder setGatewayType(final SGatewayType gatewayType) { ((SGatewayInstance) entity).setGatewayType(gatewayType); return this; } @Override public SGatewayInstanceBuilder setHitBys(final String hitBys) { ((SGatewayInstance) entity).setHitBys(hitBys); return this; } @Override public SGatewayInstanceBuilder setParentContainerId(final long containerId) { entity.setParentContainerId(containerId); return this; } @Override public SGatewayInstanceBuilder setRootContainerId(final long containerId) { entity.setRootContainerId(containerId); return this; } @Override public SGatewayInstance done() { return (SGatewayInstance) entity; } @Override public SGatewayInstanceBuilder setParentActivityInstanceId(final long parentActivityInstanceId) { entity.setLogicalGroup(SGatewayInstanceBuilderFactoryImpl.PARENT_ACTIVITY_INSTANCE_INDEX, parentActivityInstanceId); return this; } @Override public SGatewayInstanceBuilder setProcessInstanceId(final long processInstanceId) { entity.setLogicalGroup(SGatewayInstanceBuilderFactoryImpl.ROOT_PROCESS_INSTANCE_INDEX, processInstanceId); return this; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SHumanTaskInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.builder.SHumanTaskInstanceBuilderFactory; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public abstract class SHumanTaskInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl implements SHumanTaskInstanceBuilderFactory { private static final String ASSIGNEE_ID_KEY = "assigneeId"; private static final String CLAIMED_DATE = "claimedDate"; private static final String EXPECTED_END_DATE_KEY = "expectedEndDate"; private static final String PRIORITY_KEY = "priority"; @Override public String getAssigneeIdKey() { return ASSIGNEE_ID_KEY; } @Override public String getClaimedDateKey() { return CLAIMED_DATE; } @Override public String getPriorityKey() { return PRIORITY_KEY; } @Override public String getExpectedEndDateKey() { return EXPECTED_END_DATE_KEY; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SHumanTaskInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.STaskPriority; import org.bonitasoft.engine.core.process.instance.model.builder.SHumanTaskInstanceBuilder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public abstract class SHumanTaskInstanceBuilderImpl extends SActivityInstanceBuilderImpl implements SHumanTaskInstanceBuilder { protected SHumanTaskInstanceBuilderImpl(final SHumanTaskInstance entity) { super(entity); } @Override public SHumanTaskInstanceBuilder setAssigneeId(final long assigneeId) { ((SHumanTaskInstance) this.entity).setAssigneeId(assigneeId); ((SHumanTaskInstance) this.entity).setClaimedDate(System.currentTimeMillis()); return this; } @Override public SHumanTaskInstanceBuilder setPriority(final STaskPriority priority) { ((SHumanTaskInstance) this.entity).setPriority(priority); return this; } @Override public SHumanTaskInstanceBuilder setExpectedEndDate(final Long expectedEndDate) { ((SHumanTaskInstance) this.entity).setExpectedEndDate(expectedEndDate); return this; } @Override public SHumanTaskInstanceBuilder setDisplayDescription(final String displayDescription) { this.entity.setDisplayDescription(displayDescription); return this; } @Override public SHumanTaskInstanceBuilder setDisplayName(final String displayName) { this.entity.setDisplayName(displayName); return this; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SLoopActivityInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilderFactory; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class SLoopActivityInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl implements SLoopActivityInstanceBuilderFactory { @Override public SLoopActivityInstanceBuilder createNewOuterTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SLoopActivityInstance activityInstanceImpl = new SLoopActivityInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId); activityInstanceImpl.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); return new SLoopActivityInstanceBuilderImpl(activityInstanceImpl); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SLoopActivityInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilder; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class SLoopActivityInstanceBuilderImpl extends SActivityInstanceBuilderImpl implements SLoopActivityInstanceBuilder { public SLoopActivityInstanceBuilderImpl(final SLoopActivityInstance activityInstanceImpl) { super(activityInstanceImpl); } @Override public SLoopActivityInstance done() { return (SLoopActivityInstance) this.entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SManualTaskInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance; import org.bonitasoft.engine.core.process.instance.model.STaskPriority; import org.bonitasoft.engine.core.process.instance.model.builder.SManualTaskInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SManualTaskInstanceBuilderFactory; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class SManualTaskInstanceBuilderFactoryImpl extends SHumanTaskInstanceBuilderFactoryImpl implements SManualTaskInstanceBuilderFactory { @Override public SManualTaskInstanceBuilder createNewManualTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long actorId, final long processDefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SManualTaskInstance activityInst = new SManualTaskInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, actorId, STaskPriority.NORMAL, processDefinitionId, rootProcessInstanceId); activityInst.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); return new SManualTaskInstanceBuilderImpl(activityInst); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SManualTaskInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SManualTaskInstanceBuilder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class SManualTaskInstanceBuilderImpl extends SHumanTaskInstanceBuilderImpl implements SManualTaskInstanceBuilder { public SManualTaskInstanceBuilderImpl(final SManualTaskInstance activityInst) { super(activityInst); } @Override public SManualTaskInstance done() { return (SManualTaskInstance) this.entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SMultiInstanceActivityInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilderFactory; /** * @author Baptiste Mesta * @author Celine Souchet */ public class SMultiInstanceActivityInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl implements SMultiInstanceActivityInstanceBuilderFactory { @Override public SMultiInstanceActivityInstanceBuilder createNewOuterTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId, final boolean isSequential) { final SMultiInstanceActivityInstance entity = new SMultiInstanceActivityInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId, isSequential); entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); return new SMultiInstanceActivityInstanceBuilderImpl(entity); } @Override public String getLoopCardinalityKey() { return "loopCardinality"; } @Override public String getNumberOfActiveInstancesKey() { return "numberOfActiveInstances"; } @Override public String getNumberOfCompletedInstancesKey() { return "numberOfCompletedInstances"; } @Override public String getNumberOfTerminatedInstancesKey() { return "numberOfTerminatedInstances"; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SMultiInstanceActivityInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilder; /** * @author Baptiste Mesta * @author Celine Souchet */ public class SMultiInstanceActivityInstanceBuilderImpl extends SActivityInstanceBuilderImpl implements SMultiInstanceActivityInstanceBuilder { public SMultiInstanceActivityInstanceBuilderImpl(final SMultiInstanceActivityInstance entity) { super(entity); } @Override public SMultiInstanceActivityInstance done() { return (SMultiInstanceActivityInstance) entity; } @Override public SMultiInstanceActivityInstanceBuilder setLoopDataInputRef(final String loopDataInputRef) { ((SMultiInstanceActivityInstance) entity).setLoopDataInputRef(loopDataInputRef); return this; } @Override public SMultiInstanceActivityInstanceBuilder setLoopDataOutputRef(final String loopDataOutputRef) { ((SMultiInstanceActivityInstance) entity).setLoopDataOutputRef(loopDataOutputRef); return this; } @Override public SMultiInstanceActivityInstanceBuilder setDataInputItemRef(final String dataInputItemRef) { ((SMultiInstanceActivityInstance) entity).setDataInputItemRef(dataInputItemRef); return this; } @Override public SMultiInstanceActivityInstanceBuilder setDataOutputItemRef(final String dataOutputItemRef) { ((SMultiInstanceActivityInstance) entity).setDataOutputItemRef(dataOutputItemRef); return this; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SProcessInstanceLogIndexesMapper.java ================================================ /** * 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.core.process.instance.model.builder.impl; /** * @author Elias Ricken de Medeiros * @author Feng Hui * @author Matthieu Chaffotte */ public class SProcessInstanceLogIndexesMapper { public static final int PROCESS_INSTANCE_INDEX = 0; public static final int ACTIVITY_INSTANCE_INDEX = 1; public static final int GATEWAY_INSTANCE_INDEX = 2; public static final int TRANSITION_INSTANCE_INDEX = 3; public static final String PROCESS_INSTANCE_NAME = "numericIndex1"; public static final String ACTIVITY_INSTANCE_NAME = "numericIndex2"; public static final String GATEWAY_INSTANCE_NAME = "numericIndex3"; public static final String TRANSITION_INSTANCE_NAME = "numericIndex4"; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SReceiveTaskInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SReceiveTaskInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SReceiveTaskInstanceBuilderFactory; /** * @author Julien Molinaro */ public class SReceiveTaskInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl implements SReceiveTaskInstanceBuilderFactory { @Override public SReceiveTaskInstanceBuilder createNewReceiveTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SReceiveTaskInstance activityInstanceImpl = new SReceiveTaskInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId); activityInstanceImpl.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); return new SReceiveTaskInstanceBuilderImpl(activityInstanceImpl); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SReceiveTaskInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SReceiveTaskInstanceBuilder; /** * @author Julien Molinaro */ public class SReceiveTaskInstanceBuilderImpl extends SActivityInstanceBuilderImpl implements SReceiveTaskInstanceBuilder { public SReceiveTaskInstanceBuilderImpl(final SReceiveTaskInstance activityInstanceImpl) { super(activityInstanceImpl); } @Override public SReceiveTaskInstance done() { return (SReceiveTaskInstance) this.entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SSendTaskInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SSendTaskInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SSendTaskInstanceBuilderFactory; /** * @author Baptiste Mesta */ public class SSendTaskInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl implements SSendTaskInstanceBuilderFactory { @Override public SSendTaskInstanceBuilder createNewSendTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SSendTaskInstance activityInstanceImpl = new SSendTaskInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId); activityInstanceImpl.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); return new SSendTaskInstanceBuilderImpl(activityInstanceImpl); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SSendTaskInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SSendTaskInstanceBuilder; /** * @author Baptiste Mesta */ public class SSendTaskInstanceBuilderImpl extends SActivityInstanceBuilderImpl implements SSendTaskInstanceBuilder { public SSendTaskInstanceBuilderImpl(final SSendTaskInstance activityInstanceImpl) { super(activityInstanceImpl); } @Override public SSendTaskInstance done() { return (SSendTaskInstance) this.entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SSubProcessActivityInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SSubProcessActivityInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SSubProcessActivityInstanceBuilderFactory; /** * @author Celine Souchet * @author Elias Ricken de Medeiros */ public class SSubProcessActivityInstanceBuilderFactoryImpl extends SActivityInstanceBuilderFactoryImpl implements SSubProcessActivityInstanceBuilderFactory { @Override public SSubProcessActivityInstanceBuilder createNewSubProcessActivityInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long processDefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId, final boolean isTriggeredByEvent) { final SSubProcessActivityInstance entity = new SSubProcessActivityInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, processDefinitionId, rootProcessInstanceId, isTriggeredByEvent); entity.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); entity.setTokenCount(1); return new SSubProcessActivityInstanceBuilderImpl(entity); } @Override public String getTriggeredByEventKey() { return "triggeredByEvent"; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SSubProcessActivityInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SSubProcessActivityInstanceBuilder; /** * @author Celine Souchet * @author Elias Ricken de Medeiros */ public class SSubProcessActivityInstanceBuilderImpl extends SActivityInstanceBuilderImpl implements SSubProcessActivityInstanceBuilder { public SSubProcessActivityInstanceBuilderImpl(final SSubProcessActivityInstance entity) { super(entity); } @Override public SSubProcessActivityInstance done() { return (SSubProcessActivityInstance) entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SUserTaskInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.STaskPriority; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class SUserTaskInstanceBuilderFactoryImpl extends SHumanTaskInstanceBuilderFactoryImpl implements SUserTaskInstanceBuilderFactory { @Override public SUserTaskInstanceBuilder createNewUserTaskInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long actorId, final long processDefinitionId, final long rootProcessInstanceId, final long parentProcessInstanceId) { final SUserTaskInstance activityInst = new SUserTaskInstance(name, flowNodeDefinitionId, rootContainerId, parentContainerId, actorId, STaskPriority.NORMAL, processDefinitionId, rootProcessInstanceId); activityInst.setLogicalGroup(PARENT_PROCESS_INSTANCE_INDEX, parentProcessInstanceId); return new SUserTaskInstanceBuilderImpl(activityInst); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SUserTaskInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class SUserTaskInstanceBuilderImpl extends SHumanTaskInstanceBuilderImpl implements SUserTaskInstanceBuilder { public SUserTaskInstanceBuilderImpl(final SUserTaskInstance activityInst) { super(activityInst); } @Override public SUserTaskInstance done() { return (SUserTaskInstance) this.entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/business/data/SRefBusinessDataInstanceBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl.business.data; import java.util.List; import org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.business.data.SFlowNodeSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance; /** * @author Matthieu Chaffotte */ public class SRefBusinessDataInstanceBuilderFactoryImpl implements SRefBusinessDataInstanceBuilderFactory { @Override public SRefBusinessDataInstanceBuilder createNewInstance(final String name, final long processInstanceId, final Long dataId, final String dataClassName) { final SProcessSimpleRefBusinessDataInstance entity = new SProcessSimpleRefBusinessDataInstance(); entity.setProcessInstanceId(processInstanceId); entity.setName(name); entity.setDataId(dataId); entity.setDataClassName(dataClassName); return new SRefBusinessDataInstanceBuilderImpl(entity); } @Override public SRefBusinessDataInstanceBuilder createNewInstanceForFlowNode(final String name, final long flowNodeInstanceId, final Long dataId, final String dataClassName) { final SFlowNodeSimpleRefBusinessDataInstance entity = new SFlowNodeSimpleRefBusinessDataInstance(); entity.setFlowNodeInstanceId(flowNodeInstanceId); entity.setName(name); entity.setDataId(dataId); entity.setDataClassName(dataClassName); return new SRefBusinessDataInstanceBuilderImpl(entity); } @Override public SRefBusinessDataInstanceBuilder createNewInstance(final String name, final long processInstanceId, final List dataIds, final String dataClassName) { final SProcessMultiRefBusinessDataInstance entity = new SProcessMultiRefBusinessDataInstance(); entity.setProcessInstanceId(processInstanceId); entity.setName(name); entity.setDataIds(dataIds); entity.setDataClassName(dataClassName); return new SRefBusinessDataInstanceBuilderImpl(entity); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/business/data/SRefBusinessDataInstanceBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl.business.data; import org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceBuilder; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; /** * @author Matthieu Chaffotte */ public class SRefBusinessDataInstanceBuilderImpl implements SRefBusinessDataInstanceBuilder { private final SRefBusinessDataInstance entity; public SRefBusinessDataInstanceBuilderImpl(final SRefBusinessDataInstance entity) { super(); this.entity = entity; } @Override public SRefBusinessDataInstance done() { return entity; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/business/data/SRefBusinessDataInstanceLogBuilderFactoryImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl.business.data; import org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceLogBuilder; import org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Matthieu Chaffotte */ public class SRefBusinessDataInstanceLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SRefBusinessDataInstanceLogBuilderFactory { @Override public SRefBusinessDataInstanceLogBuilder createNewInstance() { return new SRefBusinessDataInstanceLogBuilderImpl(); } @Override public String getObjectIdKey() { return "numericIndex2"; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/business/data/SRefBusinessDataInstanceLogBuilderImpl.java ================================================ /** * 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.core.process.instance.model.builder.impl.business.data; import org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Matthieu Chaffotte */ public class SRefBusinessDataInstanceLogBuilderImpl extends CRUDELogBuilder implements SRefBusinessDataInstanceLogBuilder { @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(1, objectId); return this; } @Override protected String getActionTypePrefix() { return "REF_BUSINESS_DATA_INSTANCE"; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(0) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + "Flow Node Instance Id"); } } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/business/data/SFlowNodeSimpleRefBusinessDataInstance.java ================================================ /** * 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.core.process.instance.model.business.data; import javax.persistence.Cacheable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("fn_simple_ref") @Cacheable(false) public class SFlowNodeSimpleRefBusinessDataInstance extends SSimpleRefBusinessDataInstance { @Column(name = "fn_inst_id") private long flowNodeInstanceId; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/business/data/SProcessMultiRefBusinessDataInstance.java ================================================ /** * 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.core.process.instance.model.business.data; import java.util.List; import javax.persistence.Cacheable; import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.OrderColumn; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("proc_multi_ref") @Cacheable(false) public class SProcessMultiRefBusinessDataInstance extends SRefBusinessDataInstance { @ElementCollection @CollectionTable(name = "multi_biz_data", joinColumns = { @JoinColumn(name = "id", referencedColumnName = "id") }) @OrderColumn(name = "idx") @Column(name = "data_id") private List dataIds; @Column(name = "proc_inst_id") private long processInstanceId; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/business/data/SProcessSimpleRefBusinessDataInstance.java ================================================ /** * 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.core.process.instance.model.business.data; import javax.persistence.Cacheable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("proc_simple_ref") @Cacheable(false) public class SProcessSimpleRefBusinessDataInstance extends SSimpleRefBusinessDataInstance { @Column(name = "proc_inst_id") private long processInstanceId; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/business/data/SRefBusinessDataInstance.java ================================================ /** * 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.core.process.instance.model.business.data; import javax.persistence.Column; import javax.persistence.DiscriminatorColumn; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @Entity @Table(name = "ref_biz_data_inst") @DiscriminatorColumn(name = "kind") public abstract class SRefBusinessDataInstance implements PersistentObject { @Id private long id; private String name; @Column(name = "data_classname") private String dataClassName; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/business/data/SSimpleRefBusinessDataInstance.java ================================================ /** * 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.core.process.instance.model.business.data; import javax.persistence.Column; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity public abstract class SSimpleRefBusinessDataInstance extends SRefBusinessDataInstance { @Column(name = "data_id") private Long dataId; } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SBoundaryEventInstance.java ================================================ /** * 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.core.process.instance.model.event; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("boundaryEvent") public class SBoundaryEventInstance extends SCatchEventInstance { private long activityInstanceId; public SBoundaryEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); } @Override public SFlowNodeType getType() { return SFlowNodeType.BOUNDARY_EVENT; } @Override public boolean mustExecuteOnAbortOrCancelProcess() { //a boundary event never must be executed when the process instance is aborted because it will be aborted by the attached activity return false; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SCatchEventInstance.java ================================================ /** * 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.core.process.instance.model.event; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity public abstract class SCatchEventInstance extends SEventInstance { private boolean interrupting = true; public SCatchEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SEndEventInstance.java ================================================ /** * 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.core.process.instance.model.event; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("endEvent") public class SEndEventInstance extends SThrowEventInstance { public SEndEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); } @Override public SFlowNodeType getType() { return SFlowNodeType.END_EVENT; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SEventInstance.java ================================================ /** * 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.core.process.instance.model.event; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity public abstract class SEventInstance extends SFlowNodeInstance { public SEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SIntermediateCatchEventInstance.java ================================================ /** * 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.core.process.instance.model.event; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("intermediateCatchEvent") public class SIntermediateCatchEventInstance extends SCatchEventInstance { public SIntermediateCatchEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); } @Override public SFlowNodeType getType() { return SFlowNodeType.INTERMEDIATE_CATCH_EVENT; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SIntermediateThrowEventInstance.java ================================================ /** * 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.core.process.instance.model.event; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; /** * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("intermediateThrowEvent") public class SIntermediateThrowEventInstance extends SThrowEventInstance { public SIntermediateThrowEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); } @Override public SFlowNodeType getType() { return SFlowNodeType.INTERMEDIATE_THROW_EVENT; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SStartEventInstance.java ================================================ /** * 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.core.process.instance.model.event; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("startEvent") public class SStartEventInstance extends SCatchEventInstance { public SStartEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); } @Override public SFlowNodeType getType() { return SFlowNodeType.START_EVENT; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/SThrowEventInstance.java ================================================ /** * 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.core.process.instance.model.event; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity public abstract class SThrowEventInstance extends SEventInstance { public SThrowEventInstance(final String name, final long flowNodeDefinitionId, final long rootContainerId, final long parentContainerId, final long logicalGroup1, final long logicalGroup2) { super(name, flowNodeDefinitionId, rootContainerId, parentContainerId, logicalGroup1, logicalGroup2); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SBPMEventType.java ================================================ /** * 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.core.process.instance.model.event.handling; /** * @author Zhao Na * @author Baptiste Mesta * @author Elias Ricken de Medeiros */ public enum SBPMEventType { START_EVENT, INTERMEDIATE_CATCH_EVENT, INTERMEDIATE_THROW_EVENT, END_EVENT, BOUNDARY_EVENT, EVENT_SUB_PROCESS } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SMessageEventCouple.java ================================================ /** * 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.core.process.instance.model.event.handling; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor public class SMessageEventCouple implements PersistentObject { private long waitingMessageId; private SBPMEventType waitingMessageEventType; private long messageInstanceId; public SMessageEventCouple(final long waitingMessageId, final SBPMEventType waitingMessageEventType, final long messageInstanceId) { this.waitingMessageId = waitingMessageId; this.waitingMessageEventType = waitingMessageEventType; this.messageInstanceId = messageInstanceId; } @Override public long getId() { return -1; } @Override public void setId(final long id) { throw new IllegalArgumentException(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SMessageInstance.java ================================================ /** * 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.core.process.instance.model.event.handling; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @Entity @Table(name = "message_instance") public class SMessageInstance implements PersistentObject { @Id private long id; private String messageName; private String targetProcess; private String targetFlowNode; private long processDefinitionId; private boolean locked = false; private boolean handled = false; private String flowNodeName; private String correlation1; private String correlation2; private String correlation3; private String correlation4; private String correlation5; private long creationDate; public SMessageInstance(final String messageName, final String targetProcess, final String targetFlowNode, final long processDefinitionId, final String flowNodeName) { this.messageName = messageName; this.targetProcess = targetProcess; this.targetFlowNode = targetFlowNode; this.processDefinitionId = processDefinitionId; this.flowNodeName = flowNodeName; this.creationDate = System.currentTimeMillis(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SWaitingErrorEvent.java ================================================ /** * 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.core.process.instance.model.event.handling; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("error") public class SWaitingErrorEvent extends SWaitingEvent { private String errorCode; /** * the id of activity where the boundary event is attached to. */ private long relatedActivityInstanceId; public SWaitingErrorEvent(final SBPMEventType eventType, final long processdefinitionId, final String processName, final long flowNodeDefinitionId, final String flowNodeName, final String errorCode) { super(eventType, processdefinitionId, processName, flowNodeDefinitionId, flowNodeName); this.errorCode = errorCode; } @Override public SEventTriggerType getEventTriggerType() { return SEventTriggerType.ERROR; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SWaitingEvent.java ================================================ /** * 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.core.process.instance.model.event.handling; import javax.persistence.DiscriminatorColumn; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Zhao Na * @author Elias Ricken de Medeiros * @author Celine Souchet */ @Data @NoArgsConstructor @AllArgsConstructor @Entity @Table(name = "waiting_event") @DiscriminatorColumn(name = "kind") public abstract class SWaitingEvent implements PersistentObject { @Id private long id; @Enumerated(EnumType.STRING) private SBPMEventType eventType; private String processName; private long processDefinitionId; private long flowNodeDefinitionId; private String flowNodeName; private long subProcessId = -1; private long parentProcessInstanceId = -1; private long rootProcessInstanceId = -1; private long flowNodeInstanceId = -1; private boolean active = true; public SWaitingEvent(final SBPMEventType eventType, final long processdefinitionId, final String processName, final long flowNodeDefinitionId, final String flowNodeName) { this.eventType = eventType; this.processName = processName; this.flowNodeDefinitionId = flowNodeDefinitionId; this.flowNodeName = flowNodeName; this.processDefinitionId = processdefinitionId; } public abstract SEventTriggerType getEventTriggerType(); } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SWaitingMessageEvent.java ================================================ /** * 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.core.process.instance.model.event.handling; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("message") public class SWaitingMessageEvent extends SWaitingEvent { private String messageName; private boolean locked = false; private int progress = 0; private String correlation1; private String correlation2; private String correlation3; private String correlation4; private String correlation5; public SWaitingMessageEvent(final SBPMEventType eventType, final long processdefinitionId, final String processName, final long flowNodeDefinitionId, final String flowNodeName, final String messageName) { super(eventType, processdefinitionId, processName, flowNodeDefinitionId, flowNodeName); this.messageName = messageName; } @Override public SEventTriggerType getEventTriggerType() { return SEventTriggerType.MESSAGE; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SWaitingSignalEvent.java ================================================ /** * 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.core.process.instance.model.event.handling; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerType; /** * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @DiscriminatorValue("signal") public class SWaitingSignalEvent extends SWaitingEvent { private String signalName; public SWaitingSignalEvent(final SBPMEventType eventType, final long processdefinitionId, final String processName, final long flowNodeDefinitionId, final String flowNodeName, final String signalName) { super(eventType, processdefinitionId, processName, flowNodeDefinitionId, flowNodeName); this.signalName = signalName; } @Override public SEventTriggerType getEventTriggerType() { return SEventTriggerType.SIGNAL; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/model/event/trigger/STimerEventTriggerInstance.java ================================================ /** * 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.core.process.instance.model.event.trigger; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @Entity @Table(name = "event_trigger_instance") public class STimerEventTriggerInstance implements PersistentObject { public static final String EXECUTION_DATE = "executionDate"; @Id private long id; private long eventInstanceId; /** * @return The date of the execution of the trigger * @since 6.4.0 */ private long executionDate; /** * @return The name of the trigger of the job * @since 6.4.0 */ private String jobTriggerName; /** * @return The name of the {@link org.bonitasoft.engine.core.process.instance.model.event.SEventInstance} * @since 6.4.0 */ private String eventInstanceName; public STimerEventTriggerInstance(final long eventInstanceId, final String eventInstanceName, final long executionDate, final String jobTriggerName) { this.eventInstanceId = eventInstanceId; this.eventInstanceName = eventInstanceName; this.executionDate = executionDate; this.jobTriggerName = jobTriggerName; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/recorder/ProcessInstanceRecordType.java ================================================ /** * 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.core.process.instance.recorder; /** * @author Elias Ricken de Medeiros */ public enum ProcessInstanceRecordType { createActivityInstance, deleteActivityInstance, createProcessInstance, deleteProcessInstance } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/recorder/SelectBusinessDataDescriptorBuilder.java ================================================ /** * 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.core.process.instance.recorder; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Hongwen Zang * @author Celine Souchet */ public class SelectBusinessDataDescriptorBuilder { public static SelectOneDescriptor getSRefBusinessDataInstance(final String name, final long processInstanceId) { final Map parameters = new HashMap<>(); parameters.put("name", name); parameters.put("processInstanceId", processInstanceId); return new SelectOneDescriptor<>("getSRefBusinessDataInstance", parameters, SRefBusinessDataInstance.class); } public static SelectOneDescriptor getSARefBusinessDataInstance(final String name, final long processInstanceId) { final Map parameters = new HashMap<>(); parameters.put("name", name); parameters.put("processInstanceId", processInstanceId); return new SelectOneDescriptor<>("getSARefBusinessDataInstance", parameters, SARefBusinessDataInstance.class); } public static SelectListDescriptor getSRefBusinessDataInstances( final long processInstanceId, final int startIndex, final int maxResults) { final Map parameters = new HashMap<>(); parameters.put("processInstanceId", processInstanceId); final QueryOptions options = new QueryOptions(startIndex, maxResults); return new SelectListDescriptor<>("getSRefBusinessDataInstancesOfProcess", parameters, SRefBusinessDataInstance.class, options); } public static SelectOneDescriptor getSFlowNodeRefBusinessDataInstance(final String name, final long flowNodeInstanceId) { final Map parameters = new HashMap<>(); parameters.put("name", name); parameters.put("flowNodeInstanceId", flowNodeInstanceId); return new SelectOneDescriptor<>("getSFlowNodeRefBusinessDataInstance", parameters, SRefBusinessDataInstance.class); } public static SelectOneDescriptor getSAFlowNodeRefBusinessDataInstance(final String name, final long flowNodeInstanceId) { final Map parameters = new HashMap<>(); parameters.put("name", name); parameters.put("flowNodeInstanceId", flowNodeInstanceId); return new SelectOneDescriptor<>("getSAFlowNodeRefBusinessDataInstance", parameters, SARefBusinessDataInstance.class); } public static SelectOneDescriptor getNumberOfDataOfMultiRefBusinessData(final String name, final long processInstanceId) { final Map parameters = new HashMap<>(); parameters.put("name", name); parameters.put("processInstanceId", processInstanceId); return new SelectOneDescriptor<>("getNumberOfDataOfMultiRefBusinessData", parameters, SProcessMultiRefBusinessDataInstance.class); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/java/org/bonitasoft/engine/core/process/instance/recorder/SelectDescriptorBuilder.java ================================================ /** * 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.core.process.instance.recorder; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; 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.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent; import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Hongwen Zang * @author Celine Souchet */ public class SelectDescriptorBuilder { // FIXME put in a common model public static SelectByIdDescriptor getElementById(final Class clazz, final String elementName, final long id) { return new SelectByIdDescriptor<>(clazz, id); } public static SelectListDescriptor getArchivedFlowNodesFromProcessInstance( final long rootContainerId, final int fromIndex, final int maxResults) { final Map parameters = Collections.singletonMap("rootContainerId", rootContainerId); final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults); return new SelectListDescriptor<>("getArchivedFlowNodesFromProcessInstance", parameters, SAFlowNodeInstance.class, queryOptions); } public static SelectListDescriptor getArchivedActivitiesFromProcessInstance( final long rootContainerId, final QueryOptions queryOptions) { final Map parameters = Collections.singletonMap("rootContainerId", rootContainerId); return new SelectListDescriptor<>("getAActivitiesFromProcessInstance", parameters, SAActivityInstance.class, queryOptions); } public static SelectListDescriptor getSourceProcesInstanceIdsOfArchProcessInstancesFromDefinition( final long processDefinitionId, final QueryOptions queryOptions) { final Map parameters = Collections.singletonMap("processDefinitionId", processDefinitionId); return new SelectListDescriptor<>("getSourceProcessInstanceIdsByProcessDefinitionId", parameters, SAProcessInstance.class, queryOptions); } public static SelectListDescriptor getSpecificQueryWithParameters( final Class clazz, final String queryName, final Map parameters, final QueryOptions queryOptions) { return new SelectListDescriptor<>(queryName, parameters, clazz, queryOptions); } public static SelectListDescriptor getAssignedUserTasks(final long userId, final int fromIndex, final int maxResults, final String sortFieldName, final OrderByType order) { final Map parameters = Collections.singletonMap("assigneeId", userId); final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults, SActivityInstance.class, sortFieldName, order); return new SelectListDescriptor<>("getAssignedUserTasks", parameters, SHumanTaskInstance.class, queryOptions); } public static SelectListDescriptor getPendingUserTasks(final long userId, final Set actorIds, final int fromIndex, final int maxResults, final String sortFieldName, final OrderByType order) { final Map parameters = new HashMap<>(3); parameters.put("actorIds", actorIds); parameters.put("userId", userId); final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults, SActivityInstance.class, sortFieldName, order); return new SelectListDescriptor<>("getPendingUserTasks", parameters, SHumanTaskInstance.class, queryOptions); } public static SelectListDescriptor getPendingUserTasks(final long userId, final int fromIndex, final int maxResults, final String sortFieldName, final OrderByType order) { final Map parameters = new HashMap<>(3); parameters.put("userId", userId); final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults, SActivityInstance.class, sortFieldName, order); return new SelectListDescriptor<>("getPendingUserTasksWithoutActorIds", parameters, SHumanTaskInstance.class, queryOptions); } public static SelectOneDescriptor getNumberOfProcessInstances() { final Map emptyMap = Collections.emptyMap(); return new SelectOneDescriptor<>("getNumberOfProcessInstances", emptyMap, SProcessInstance.class, Long.class); } public static SelectOneDescriptor getNumberOfArchivedProcessInstances() { final Map emptyMap = Collections.emptyMap(); return new SelectOneDescriptor<>("getNumberOfArchivedProcessInstances", emptyMap, SAProcessInstance.class, Long.class); } public static SelectOneDescriptor getNumberOfOpenActivities(final long rootContainerId) { final Map parameters = Collections.singletonMap("rootContainerId", rootContainerId); return new SelectOneDescriptor<>("getNumberOfOpenActivities", parameters, SActivityInstance.class, Long.class); } public static SelectOneDescriptor getNumberOfAssignedHumanTaskInstances(final long userId) { final Map parameters = Collections.singletonMap("assigneeId", userId); return new SelectOneDescriptor<>("getNumberOfAssignedUserTaskInstances", parameters, SHumanTaskInstance.class, Long.class); } public static SelectOneDescriptor getActiveGatewayInstanceOfProcess( final long parentProcessInstanceId, final String name) { final Map parameters = new HashMap<>(2); parameters.put("parentProcessInstanceId", parentProcessInstanceId); parameters.put("name", name); return new SelectOneDescriptor<>("getActiveGatewayInstanceOfProcess", parameters, SGatewayInstance.class); } public static SelectListDescriptor getActivitiesFromProcessInstance(final long rootContainerId, final int fromIndex, final int maxResults) { final Map parameters = Collections.singletonMap("rootContainerId", rootContainerId); final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults); return new SelectListDescriptor<>("getActivitiesFromProcessInstance", parameters, SActivityInstance.class, queryOptions); } public static SelectOneDescriptor getNumberOfActivitiesFromProcessInstance(final long rootContainerId) { final Map parameters = Collections.singletonMap("rootContainerId", rootContainerId); return new SelectOneDescriptor<>("getNumberOfActivitiesFromProcessInstance", parameters, SFlowNodeInstance.class, Long.class); } public static SelectOneDescriptor getNumberOfFlowNode(final long parentProcessInstanceId) { final Map parameters = Collections.singletonMap("parentProcessInstanceId", parentProcessInstanceId); return new SelectOneDescriptor<>("getNumberOfFlowNode", parameters, SFlowNodeInstance.class, Long.class); } public static SelectListDescriptor getEventsFromRootContainer(final long rootContainerId, final int fromIndex, final int maxResults, final String field, final OrderByType orderByType) { final Map parameters = Collections.singletonMap("rootContainerId", rootContainerId); final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults, SEventInstance.class, field, orderByType); return new SelectListDescriptor<>("getEventInstancesFromRootContainer", parameters, SEventInstance.class, queryOptions); } public static SelectListDescriptor getActivityBoundaryEvents(final long activityInstanceId, final int fromIndex, final int maxResults) { final Map parameters = Collections.singletonMap("activityInstanceId", activityInstanceId); final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults); return new SelectListDescriptor<>("getActivityBoundaryEventInstances", parameters, SBoundaryEventInstance.class, queryOptions); } public static SelectListDescriptor getEventTriggers(final long eventInstanceId, final QueryOptions queryOptions) { final Map parameters = Collections.singletonMap("eventInstanceId", eventInstanceId); return new SelectListDescriptor<>("getEventTriggerInstances", parameters, STimerEventTriggerInstance.class, queryOptions); } public static SelectListDescriptor getChildInstanceIdsOfProcessInstance(final Class class1, final long processInstanceId, final QueryOptions queryOptions) { final Map map = Collections.singletonMap("processInstanceId", processInstanceId); return new SelectListDescriptor<>("getChildInstanceIdsOfProcessInstance", map, class1, queryOptions); } public static SelectOneDescriptor getNumberOfChildInstancesOfProcessInstance(final long processInstanceId) { final Map parameters = Collections.singletonMap("processInstanceId", processInstanceId); return new SelectOneDescriptor<>("getNumberOfChildInstancesOfProcessInstance", parameters, SProcessInstance.class, Long.class); } public static SelectListDescriptor getCaughtError(final long relatedActivityInstanceId, final QueryOptions queryOptions) { final Map parameters = new HashMap<>(1); parameters.put("relatedActivityInstanceId", relatedActivityInstanceId); return new SelectListDescriptor<>("getCaughtErrorByRelatedActivityAndAnyErrorCode", parameters, SWaitingErrorEvent.class, queryOptions); } public static SelectListDescriptor getCaughtError(final long relatedActivityInstanceId, final String errorCode, final QueryOptions queryOptions) { final Map parameters = new HashMap<>(2); parameters.put("relatedActivityInstanceId", relatedActivityInstanceId); parameters.put("errorCode", errorCode); return new SelectListDescriptor<>("getCaughtErrorByRelatedActivityAndErrorCode", parameters, SWaitingErrorEvent.class, queryOptions); } public static SelectListDescriptor getListeningSignals(final String signalName, final int fromIndex, final int maxResults) { final Map parameters = Collections.singletonMap("signalName", signalName); final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults); return new SelectListDescriptor<>("getListeningSignals", parameters, SWaitingSignalEvent.class, queryOptions); } public static SelectListDescriptor getMessageEventCouples(final int fromIndex, final int maxResults) { final Map parameters = Collections.emptyMap(); final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults); return new SelectListDescriptor<>("getMessageEventCouples", parameters, SMessageEventCouple.class, queryOptions); } public static SelectOneDescriptor getArchivedActivityInstanceWithActivityIdAndStateId( final long activityInstanceId, final int stateId) { final Map parameters = new HashMap<>(2); parameters.put("activityInstanceId", activityInstanceId); parameters.put("stateId", stateId); return new SelectOneDescriptor<>("getAActivityInstanceByActivityInstanceIdAndStateId", parameters, SAActivityInstance.class); } public static SelectOneDescriptor getMostRecentArchivedActivityInstance( final long activityInstanceId) { final Map parameters = new HashMap<>(1); parameters.put("activityInstanceId", activityInstanceId); return new SelectOneDescriptor<>("getMostRecentArchivedActivityInstance", parameters, SAActivityInstance.class); } public static SelectListDescriptor searchAssignedTasksSupervisedBy(final long supervisorId, final int fromIndex, final int maxResults) { final Map parameters = Collections.singletonMap("supervisorId", supervisorId); final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults); return new SelectListDescriptor<>("searchAssignedTasksSupervisedBy", parameters, SHumanTaskInstance.class, queryOptions); } public static SelectListDescriptor> getNumbersOfAssignedOpenTasks(final List userIds) { final QueryOptions queryOptions = new QueryOptions(0, userIds.size()); final Map parameters = Collections.singletonMap("assigneeIds", userIds); return new SelectListDescriptor<>("getNumbersOfOpenTasksForUsers", parameters, SHumanTaskInstance.class, queryOptions); } public static SelectListDescriptor> getNumbersOfAssignedOverdueOpenTasks(final List userIds) { final QueryOptions queryOptions = new QueryOptions(0, userIds.size()); final Map parameters = new HashMap<>(2); parameters.put("assigneeIds", userIds); parameters.put("currentTime", System.currentTimeMillis()); return new SelectListDescriptor<>("getNumbersOfAssignedOverdueTasksForUsers", parameters, SHumanTaskInstance.class, queryOptions); } public static SelectOneDescriptor getNumberOfPendingOverdueOpenTasksForUser(final Long userId) { final Map parameters = new HashMap<>(2); parameters.put("userId", userId); parameters.put("currentTime", System.currentTimeMillis()); return new SelectOneDescriptor<>("getNumberOfPendingOverdueTasksForUser", parameters, SHumanTaskInstance.class, Long.class); } public static SelectListDescriptor deleteMessageInstanceByIds(List ids, final int fromIndex, final int maxResults) { final Map parameters = new HashMap<>(); parameters.put("ids", ids); final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults); return new SelectListDescriptor<>("deleteMessageInstanceByIds", parameters, SMessageEventCouple.class, queryOptions); } public static SelectListDescriptor getMessageInstanceIdOlderThanCreationDate(long creationDate, QueryOptions queryOptions) { final Map parameters = Collections.singletonMap("creationDate", creationDate); return new SelectListDescriptor<>("getMessageInstanceIdOlderThanCreationDate", parameters, SMessageInstance.class, queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/resources/org/bonitasoft/engine/core/document/model/impl/hibernate/archive.document.queries.hbm.xml ================================================ SELECT COUNT(mappeddoc.id) FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc SELECT mappeddoc FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc SELECT mappeddoc FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc WHERE mappeddoc.processInstanceId = :processInstanceId AND mappeddoc.name = :name AND mappeddoc.archiveDate >= :time ORDER BY mappeddoc.archiveDate ASC SELECT mappeddoc FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc WHERE mappeddoc.processInstanceId = :processInstanceId AND mappeddoc.name = :name AND mappeddoc.archiveDate >= :time ORDER BY mappeddoc.index ASC SELECT mappeddoc FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc WHERE mappeddoc.sourceObjectId = :sourceObjectId ORDER BY mappeddoc.archiveDate DESC, mappeddoc.id DESC SELECT COUNT(mappeddoc.id) FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc, org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS p WHERE mappeddoc.processInstanceId = p.sourceObjectId AND p.processDefinitionId IN ( SELECT supervisor.processDefId FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE (supervisor.userId = :userId) OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) ) ) ) ) SELECT mappeddoc FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc, org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS p WHERE mappeddoc.processInstanceId = p.sourceObjectId AND p.processDefinitionId IN ( SELECT supervisor.processDefId FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE (supervisor.userId = :userId) OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) ) ) ) ) SELECT mappeddoc FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc WHERE mappeddoc.processInstanceId IN (:processInstanceIds) DELETE FROM org.bonitasoft.engine.core.document.model.SLightDocument AS doc WHERE doc.id IN (:ids) DELETE FROM org.bonitasoft.engine.core.document.model.archive.SAMappedDocument AS mappeddoc WHERE mappeddoc.id IN (:ids) ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/resources/org/bonitasoft/engine/core/document/model/impl/hibernate/document.queries.hbm.xml ================================================ SELECT mappeddoc FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc WHERE mappeddoc.processInstanceId = :processInstanceId AND mappeddoc.name = :name AND mappeddoc.index = -1 SELECT mappeddoc FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc WHERE mappeddoc.processInstanceId = :processInstanceId AND mappeddoc.name = :name AND mappeddoc.document.creationDate <= :time ORDER BY mappeddoc.index ASC SELECT mappeddoc FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc WHERE mappeddoc.processInstanceId = :processInstanceId SELECT COUNT(mappeddoc.id) FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc WHERE mappeddoc.processInstanceId = :processInstanceId SELECT mappeddoc FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc WHERE mappeddoc.processInstanceId = :processInstanceId ORDER BY mappeddoc.id SELECT COUNT(mappeddoc.id) FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc SELECT mappeddoc FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc SELECT COUNT(mappeddoc.id) FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc, org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p WHERE mappeddoc.processInstanceId = p.id AND p.processDefinitionId IN ( SELECT supervisor.processDefId FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE (supervisor.userId = :userId) OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) ) ) ) ) SELECT mappeddoc FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc, org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p WHERE mappeddoc.processInstanceId = p.id AND p.processDefinitionId IN ( SELECT supervisor.processDefId FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE (supervisor.userId = :userId) OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) ) ) ) ) SELECT mappeddoc FROM org.bonitasoft.engine.core.document.model.SMappedDocument AS mappeddoc WHERE mappeddoc.processInstanceId = :processInstanceId AND mappeddoc.name = :name AND mappeddoc.index > -1 ORDER BY mappeddoc.index ASC ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/resources/org/bonitasoft/engine/core/failure/model/impl/hibernate/failure.queries.hbm.xml ================================================ SELECT f FROM org.bonitasoft.engine.core.process.instance.model.SBPMFailure AS f WHERE f.flowNodeInstanceId = :flowNodeInstanceId ORDER BY f.failureDate DESC DELETE FROM org.bonitasoft.engine.core.process.instance.model.SBPMFailure AS f WHERE f.id IN (:ids) DELETE FROM org.bonitasoft.engine.core.process.instance.model.SABPMFailure AS f WHERE f.flowNodeInstanceId IN (:flowNodeInstanceIds) SELECT f FROM org.bonitasoft.engine.core.process.instance.model.SABPMFailure AS f WHERE f.flowNodeInstanceId = :flowNodeInstanceId ORDER BY f.failureDate DESC SELECT f FROM org.bonitasoft.engine.core.process.instance.model.SBPMFailure AS f WHERE f.processInstanceId = :processInstanceId ORDER BY f.failureDate DESC DELETE FROM org.bonitasoft.engine.core.process.instance.model.SABPMFailure AS f WHERE f.processInstanceId IN (:processInstanceIds) SELECT f FROM org.bonitasoft.engine.core.process.instance.model.SABPMFailure AS f WHERE f.processInstanceId = :processInstanceId ORDER BY f.failureDate DESC SELECT f FROM org.bonitasoft.engine.core.process.instance.model.SBPMFailure AS f WHERE f.rootProcessInstanceId = :rootProcessInstanceId AND f.processInstanceId != f.rootProcessInstanceId ORDER BY f.failureDate DESC SELECT f FROM org.bonitasoft.engine.core.process.instance.model.SABPMFailure AS f WHERE f.rootProcessInstanceId = :rootProcessInstanceId AND f.processInstanceId != f.rootProcessInstanceId ORDER BY f.failureDate DESC ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/resources/org/bonitasoft/engine/core/process/instance/model/impl/hibernate/archived.process.instance.queries.hbm.xml ================================================ SELECT COUNT(aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS aa SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS aa SELECT COUNT(DISTINCT aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS aa, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE terminal = TRUE AND aa.logicalGroup1 = supervisor.processDefId AND (supervisor.userId = :supervisorId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :supervisorId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT DISTINCT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS aa, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE terminal = TRUE AND aa.logicalGroup1 = supervisor.processDefId AND (supervisor.userId = :supervisorId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :supervisorId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT DISTINCT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE terminal = TRUE AND aa.logicalGroup1 = supervisor.processDefId AND (supervisor.userId = :supervisorId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :supervisorId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT COUNT(DISTINCT aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE terminal = TRUE AND aa.logicalGroup1 = supervisor.processDefId AND (supervisor.userId = :supervisorId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :supervisorId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS aa WHERE aa.id = :id SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa WHERE aa.sourceObjectId = :activityInstanceId ORDER BY aa.archiveDate DESC, aa.id DESC SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa WHERE aa.sourceObjectId = :activityInstanceId AND aa.stateId = :stateId SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa WHERE aa.rootContainerId = :rootContainerId SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa WHERE aa.rootContainerId = :rootContainerId AND aa.stateId IN (:stateIds) SELECT COUNT(aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS aa WHERE terminal = TRUE AND assigneeId IN ( SELECT u.id FROM org.bonitasoft.engine.identity.model.SUser u WHERE u.managerUserId = :managerUserId ) SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS aa WHERE terminal = TRUE AND assigneeId IN ( SELECT u.id FROM org.bonitasoft.engine.identity.model.SUser u WHERE u.managerUserId = :managerUserId ) SELECT COUNT(DISTINCT aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE terminal = TRUE AND aa.logicalGroup1 = supervisor.processDefId AND(supervisor.userId = :supervisorId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :supervisorId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT DISTINCT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE terminal = TRUE AND aa.logicalGroup1 = supervisor.processDefId AND(supervisor.userId = :supervisorId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :supervisorId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS aa WHERE terminal = TRUE SELECT COUNT(aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS aa WHERE terminal = TRUE select COUNT(DISTINCT ap.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap, org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a WHERE ap.archiveDate = ( SELECT MAX(aa2.archiveDate) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa2 WHERE aa2.sourceObjectId = ap.sourceObjectId ) AND ap.stateId != 8 AND (ap.startedBy = :userId OR (a.rootContainerId = ap.sourceObjectId AND a.stable = TRUE AND a.assigneeId = :userId) ) select DISTINCT ap FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap, org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a WHERE ap.archiveDate = ( SELECT MAX(aa2.archiveDate) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa2 WHERE aa2.sourceObjectId = ap.sourceObjectId ) AND ap.stateId != 8 AND (ap.startedBy = :userId OR (a.rootContainerId = ap.sourceObjectId AND a.stable = TRUE AND a.assigneeId = :userId) ) SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS aa WHERE terminal = TRUE SELECT COUNT(aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS aa WHERE terminal = TRUE SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance AS aa WHERE terminal = TRUE SELECT COUNT(aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance AS aa WHERE terminal = TRUE SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance AS aa WHERE terminal = TRUE SELECT COUNT(aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAManualTaskInstance AS aa WHERE terminal = TRUE SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa WHERE terminal = TRUE SELECT COUNT(aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa WHERE terminal = TRUE SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa WHERE terminal = TRUE SELECT COUNT(aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance AS aa WHERE terminal = TRUE SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance AS aa WHERE terminal = TRUE SELECT COUNT(aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance AS aa WHERE terminal = TRUE SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance AS aa WHERE terminal = TRUE SELECT COUNT(aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance AS aa WHERE terminal = TRUE SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance AS aa WHERE terminal = TRUE SELECT COUNT(aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAAutomaticTaskInstance AS aa WHERE terminal = TRUE SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance AS aa WHERE terminal = TRUE SELECT COUNT(aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAReceiveTaskInstance AS aa WHERE terminal = TRUE SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa WHERE terminal = TRUE SELECT COUNT(aa.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAActivityInstance AS aa WHERE terminal = TRUE SELECT aa FROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS aa WHERE aa.rootContainerId = :rootContainerId ORDER BY aa.id SELECT new org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter(name AS name, stateName AS statename, count(aa) AS numberof) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS aa WHERE logicalGroup4 = :parentProcessInstanceId AND terminal = TRUE GROUP BY name, stateName ORDER BY name ASC, stateName ASC SELECT ac FROM org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance AS ac DELETE FROM org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance AS ac WHERE ac.containerType = :containerType AND ac.containerId IN (:containerIds) SELECT COUNT(ac.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAConnectorInstance AS ac SELECT DISTINCT ap.sourceObjectId FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap WHERE ap.processDefinitionId = :processDefinitionId SELECT ap FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap SELECT COUNT(ap.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap SELECT ap FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap WHERE ap.sourceObjectId IN (:sourceObjectIds) SELECT DISTINCT ap.startDate FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap WHERE ap.stateId = 0 AND ap.startDate >= :sinceDateInMillis SELECT ap FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap WHERE ap.id = :id SELECT COUNT(ap.sourceObjectId) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap WHERE ap.callerId = -1 AND ap.stateId IN (6,7,3,4) SELECT ap FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap WHERE ap.callerId = -1 AND ap.stateId IN (6,7,3,4) SELECT COUNT(ap.sourceObjectId) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap, org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a WHERE ap.callerId = -1 AND ap.stateId IN (6,7,3,4) AND a.rootContainerId = ap.sourceObjectId SELECT ap FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap, org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a WHERE ap.callerId = -1 AND ap.stateId IN (6,7,3,4) AND a.rootContainerId = ap.sourceObjectId SELECT COUNT(ap.sourceObjectId) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE ap.callerId = -1 AND ap.stateId IN (6,7,3,4) AND ap.processDefinitionId = processsupervisor.processDefId SELECT DISTINCT ap FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE ap.callerId = -1 AND ap.stateId IN (6,7,3,4) AND ap.processDefinitionId = processsupervisor.processDefId SELECT COUNT(ap.sourceObjectId) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap, org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE ap.callerId = -1 AND ap.stateId IN (6,7,3,4) AND a.rootContainerId = ap.sourceObjectId AND ap.processDefinitionId = processsupervisor.processDefId SELECT DISTINCT ap FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap, org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE ap.callerId = -1 AND ap.stateId IN (6,7,3,4) AND a.rootContainerId = ap.sourceObjectId AND ap.processDefinitionId = processsupervisor.processDefId SELECT DISTINCT ap.sourceObjectId FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap WHERE ap.rootProcessInstanceId = :rootProcessInstanceId AND ap.callerId != -1 SELECT ap.sourceObjectId FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap WHERE ap.rootProcessInstanceId IN (:sourceProcessInstanceIds) DELETE FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap WHERE ap.id IN (:ids) SELECT count(id) as count from ( SELECT ap.* FROM arch_process_instance ap JOIN ( select afi.logicalGroup2 from arch_flownode_instance afi WHERE afi.kind in ('user', 'manual') AND ( afi.executedBy = :userId OR afi.executedBySubstitute = :userId ) AND afi.stateId = 2 ) humanTask ON (humanTask.logicalGroup2 = ap.sourceObjectId) WHERE (ap.stateId in (6 , 7 , 3 , 4)) UNION SELECT ap.* FROM arch_process_instance ap WHERE (ap.stateId in (6 , 7 , 3 , 4)) AND ap.startedBy = :userId ) ap SELECT * from ( SELECT ap.* FROM arch_process_instance ap JOIN ( select afi.logicalGroup2 from arch_flownode_instance afi WHERE afi.kind in ('user', 'manual') AND ( afi.executedBy = :userId OR afi.executedBySubstitute = :userId ) AND afi.stateId = 2 ) humanTask ON (humanTask.logicalGroup2 = ap.sourceObjectId) WHERE (ap.stateId in (6 , 7 , 3 , 4)) UNION SELECT ap.* FROM arch_process_instance ap WHERE (ap.stateId in (6 , 7 , 3 , 4)) AND ap.startedBy = :userId ) ap SELECT COUNT(DISTINCT ap.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE ap.stateId IN (6,7,3,4) AND ap.processDefinitionId = supervisor.processDefId AND (supervisor.userId = :userId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT DISTINCT ap FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE ap.stateId IN (6,7,3,4) AND ap.processDefinitionId = supervisor.processDefId AND (supervisor.userId = :userId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT COUNT(DISTINCT ap.id) FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a WHERE ap.stateId IN (6,7,3,4) AND a.rootContainerId = ap.sourceObjectId AND ap.processDefinitionId = supervisor.processDefId AND (supervisor.userId = :userId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT DISTINCT ap FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance AS ap, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a WHERE ap.stateId IN (6,7,3,4) AND a.rootContainerId = ap.sourceObjectId AND ap.processDefinitionId = supervisor.processDefId AND (supervisor.userId = :userId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.groupId = um.groupId AND supervisor.roleId = um.roleId) ) ) ) ) SELECT ref FROM org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance AS ref WHERE ref.name = :name AND ref.processInstanceId = :processInstanceId SELECT ref FROM org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance AS ref WHERE ref.name = :name AND ref.flowNodeInstanceId = :flowNodeInstanceId DELETE FROM org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance WHERE processInstanceId = :processInstanceId DELETE FROM org.bonitasoft.engine.core.process.instance.model.archive.business.data.SARefBusinessDataInstance WHERE processInstanceId IN (:processInstanceIds) DELETE FROM org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance WHERE sourceObjectId IN (:sourceProcessInstanceIds) AND rootProcessInstanceId IN (:sourceProcessInstanceIds) SELECT f.sourceObjectId FROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS f WHERE f.logicalGroup2 IN (:sourceProcessInstanceIds) DELETE FROM org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance AS f WHERE f.sourceObjectId IN (:sourceObjectIds) ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/main/resources/org/bonitasoft/engine/core/process/instance/model/impl/hibernate/process.instance.queries.hbm.xml ================================================ SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a WHERE a.id = :id SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.id = :id SELECT flowNode FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS flowNode WHERE flowNode.id = :id SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a WHERE a.rootContainerId = :rootContainerId ORDER BY a.id ASC SELECT count(f.id) FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f WHERE f.logicalGroup4 = :parentProcessInstanceId SELECT count(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a WHERE a.rootContainerId = :rootContainerId SELECT f FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f WHERE f.logicalGroup4 = :parentProcessInstanceId ORDER BY f.id ASC SELECT f FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f WHERE f.logicalGroup4 = :parentProcessInstanceId AND f.logicalGroup3 = 0 ORDER BY f.id ASC SELECT f FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f WHERE f.logicalGroup3 = :parentActivityInstanceId ORDER BY f.id ASC SELECT new org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter(name AS name, stateName AS statename, count(f) AS numberof) FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f WHERE logicalGroup1 = :processDefinitionId GROUP BY name, stateName ORDER BY name ASC, stateName ASC SELECT new org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter(name AS name, stateName AS statename, count(f) AS numberof) FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f WHERE logicalGroup4 = :parentProcessInstanceId GROUP BY name, stateName ORDER BY name ASC, stateName ASC SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.rootContainerId = :rootContainerId AND a.stateExecuting = FALSE AND a.stable = TRUE AND a.terminal = FALSE SELECT f.id FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f WHERE f.stateId != 3 AND type(f) != org.bonitasoft.engine.core.process.instance.model.SGatewayInstance AND (f.stateExecuting = TRUE OR f.stable = FALSE OR f.terminal = TRUE OR f.stateCategory = 'ABORTING' OR f.stateCategory = 'CANCELLING') AND f.lastUpdateDate < :maxLastUpdate ORDER BY id SELECT f.id FROM org.bonitasoft.engine.core.process.instance.model.SGatewayInstance AS f WHERE f.stateId != 3 AND (f.stateId != 61 OR f.hitBys LIKE 'FINISH:%' OR f.stateCategory = 'ABORTING' OR f.stateCategory = 'CANCELLING') AND f.lastUpdateDate < :maxLastUpdate ORDER BY id SELECT f FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f WHERE f.name = :name AND f.parentContainerId = :parentContainerId SELECT f FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f WHERE f.id IN (:ids) SELECT g FROM org.bonitasoft.engine.core.process.instance.model.SGatewayInstance AS g WHERE g.logicalGroup4 = :processInstanceId AND g.terminal = FALSE AND g.gatewayType = 'INCLUSIVE' SELECT g FROM org.bonitasoft.engine.core.process.instance.model.SGatewayInstance AS g WHERE g.logicalGroup4 = :parentProcessInstanceId AND g.name = :name AND g.terminal = FALSE SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a WHERE a.rootContainerId = :rootContainerId AND a.stateId IN (:stateIds) SELECT f FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f WHERE f.logicalGroup3 = :parentActivityInstanceId AND f.terminal = FALSE UPDATE org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance SET assigneeId = :assigneeId, claimedDate = :claimedDate, lastUpdateDate = :lastUpdateDate WHERE id = :id AND ( :assigneeId = 0 OR assigneeId = 0 OR :assigneeId = assigneeId ) SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.assigneeId = :assigneeId AND a.stable = TRUE AND a.terminal = FALSE AND a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stable = TRUE AND a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE AND a.assigneeId = 0 AND EXISTS (SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId=a.id AND ( mapping.userId = :userId OR mapping.actorId in (:actorIds)) ) SELECT mapping FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId=:activityId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stable = TRUE AND a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE AND a.assigneeId = 0 AND EXISTS ( SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId=a.id AND mapping.userId = :userId ) SELECT g FROM org.bonitasoft.engine.core.process.instance.model.SGatewayInstance AS g WHERE g.id = :id SELECT e FROM org.bonitasoft.engine.core.process.instance.model.event.SEventInstance AS e WHERE e.id = :id SELECT e FROM org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance AS e WHERE e.activityInstanceId = :activityInstanceId ORDER BY e.id SELECT e FROM org.bonitasoft.engine.core.process.instance.model.event.SEventInstance AS e WHERE e.rootContainerId = :rootContainerId SELECT t FROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t WHERE t.eventInstanceId = :eventInstanceId SELECT t FROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t WHERE t.id = :id SELECT count(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.assigneeId = :assigneeId AND a.stable = TRUE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE SELECT new map(a.assigneeId as userId, count(a) as numberOfTasks) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.assigneeId IN (:assigneeIds) AND a.stable = TRUE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE GROUP BY a.assigneeId ORDER BY a.assigneeId ASC SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stable = TRUE AND a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE AND a.assigneeId = 0 AND EXISTS (SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId=a.id AND ( mapping.userId = :userId OR mapping.actorId in (SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId = :userId OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) ) SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stable = TRUE AND a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE AND a.assigneeId = 0 AND EXISTS (SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId=a.id AND ( mapping.userId = :userId OR mapping.actorId in (SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId = :userId OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) ) SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stable = TRUE AND a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE AND ( ( a.assigneeId = :userId ) OR ( a.assigneeId = 0 AND EXISTS (SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId=a.id AND ( mapping.userId = :userId OR mapping.actorId in (SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId = :userId OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) ) ) ) SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stable = TRUE AND a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE AND ( ( a.assigneeId = :userId ) OR ( a.assigneeId = 0 AND EXISTS (SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId=a.id AND ( mapping.userId = :userId OR mapping.actorId in (SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId = :userId OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) ) ) ) SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stable = TRUE AND a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE AND ( ( a.assigneeId = :userId ) OR ( EXISTS (SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId=a.id AND ( mapping.userId = :userId OR mapping.actorId in (SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId = :userId OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) ) ) ) SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stable = TRUE AND a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE AND ( ( a.assigneeId = :userId ) OR ( EXISTS (SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId=a.id AND ( mapping.userId = :userId OR mapping.actorId in (SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId = :userId OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) ) ) ) SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stable = TRUE AND a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE AND a.assigneeId = :userId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stable = TRUE AND a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE AND a.assigneeId = :userId SELECT count(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a WHERE a.rootContainerId = :rootContainerId AND a.stateExecuting = FALSE AND a.stable = TRUE AND a.terminal = FALSE SELECT c FROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c WHERE c.containerId = :containerId AND c.containerType = :containerType AND c.activationEvent = :activationEvent AND c.state = :state SELECT cfi FROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo AS cfi WHERE cfi.containerId = :containerId AND cfi.containerType = :containerType AND cfi.state = :state SELECT COUNT(c.id) FROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c SELECT c FROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c SELECT c FROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c WHERE c.containerId = :containerId AND c.containerType = :containerType AND c.activationEvent = :activationEvent AND (c.state = 'TO_BE_EXECUTED' OR c.state = 'TO_RE_EXECUTE' OR c.state = 'EXECUTING') ORDER BY c.executionOrder SELECT c FROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c WHERE c.containerId = :containerId AND c.containerType = :containerType SELECT c FROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c WHERE c.containerId = :containerId AND c.containerType = :containerType ORDER BY c.id SELECT count(c.id) FROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c WHERE c.containerId = :containerId AND c.containerType = :containerType SELECT c FROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstance AS c WHERE c.containerId = :id SELECT cfi FROM org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo AS cfi WHERE cfi.containerId = :id SELECT s FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent AS s WHERE s.relatedActivityInstanceId = :relatedActivityInstanceId AND s.errorCode IS NULL SELECT s FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent AS s WHERE s.relatedActivityInstanceId = :relatedActivityInstanceId AND s.errorCode = :errorCode SELECT new org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple(s.id, s.eventType, m.id) FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent AS s, org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance AS m WHERE m.messageName = s.messageName AND m.targetProcess = s.processName AND (m.targetFlowNode = null OR m.targetFlowNode = s.flowNodeName) AND m.locked = false AND s.locked = false AND m.handled = false AND s.active = true AND s.progress = 0 AND s.correlation1 = m.correlation1 AND s.correlation2 = m.correlation2 AND s.correlation3 = m.correlation3 AND s.correlation4 = m.correlation4 AND s.correlation5 = m.correlation5 SELECT m.id FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance AS m WHERE m.creationDate <= :creationDate DELETE FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance AS m WHERE m.id IN (:ids) SELECT s FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent AS s WHERE s.signalName = :signalName ORDER BY s.id ASC SELECT s FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent AS s WHERE s.processDefinitionId = :processDefinitionId AND (s.eventType = 'START_EVENT') SELECT s FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent AS s WHERE s.parentProcessInstanceId = :processInstanceId SELECT s FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent AS s WHERE s.flowNodeInstanceId = :flowNodeInstanceId UPDATE org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance AS m SET m.handled = false WHERE m.handled = true SELECT m FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance AS m WHERE m.handled = true UPDATE org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent AS s SET s.progress = 0 WHERE s.progress = 1 SELECT s FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent AS s WHERE s.progress = 1 SELECT COUNT(s.id) FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent AS s SELECT s FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent AS s SELECT COUNT(s.id) FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent AS s SELECT s FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent AS s SELECT COUNT(s.id) FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent AS s SELECT s FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent AS s SELECT COUNT(s.id) FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent AS s SELECT s FROM org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent AS s SELECT COUNT(t.id) FROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t SELECT COUNT(t.id) FROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t, org.bonitasoft.engine.core.process.instance.model.event.SEventInstance AS e WHERE t.eventInstanceId = e.id AND e.logicalGroup4 = :processInstanceId SELECT t FROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t, org.bonitasoft.engine.core.process.instance.model.event.SEventInstance AS e WHERE t.eventInstanceId = e.id AND e.logicalGroup4 = :processInstanceId SELECT COUNT(t.id) FROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t, org.bonitasoft.engine.core.process.instance.model.event.SEventInstance AS e WHERE t.eventInstanceId = e.id AND e.logicalGroup4 = :processInstanceId SELECT t FROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t, org.bonitasoft.engine.core.process.instance.model.event.SEventInstance AS e WHERE t.eventInstanceId = e.id AND e.logicalGroup4 = :processInstanceId SELECT COUNT(t.id) FROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t SELECT t FROM org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance AS t SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE a.logicalGroup1 = processsupervisor.processDefId SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE a.logicalGroup1 = supervisor.processDefId AND (supervisor.userId = :supervisorId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = :supervisorId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) ) ) ) ) SELECT DISTINCT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE a.logicalGroup1 = supervisor.processDefId AND (supervisor.userId = :supervisorId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = :supervisorId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) ) ) ) ) SELECT DISTINCT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.logicalGroup1 = supervisor.processDefId AND (supervisor.userId = :userId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) ) ) ) ) SELECT COUNT(DISTINCT a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.logicalGroup1 = supervisor.processDefId AND (supervisor.userId = :userId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) ) ) ) ) SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stable = TRUE AND a.terminal = FALSE AND a.stateCategory = 'NORMAL' AND a.assigneeId IN ( SELECT u.id FROM org.bonitasoft.engine.identity.model.SUser AS u WHERE u.managerUserId = :managerUserId ) SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stable = TRUE AND a.terminal = FALSE AND a.stateCategory = 'NORMAL' AND a.assigneeId IN ( SELECT u.id FROM org.bonitasoft.engine.identity.model.SUser AS u WHERE u.managerUserId = :managerUserId ) SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stateId = 4 AND a.stable = TRUE AND a.terminal = FALSE AND a.stateCategory = 'NORMAL' AND exists ( SELECT mapping.activityId FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId=a.id AND ( mapping.userId IN ( SELECT u.id FROM org.bonitasoft.engine.identity.model.SUser AS u WHERE u.managerUserId = :managerUserId ) OR mapping.actorId IN ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId IN ( SELECT u.id FROM org.bonitasoft.engine.identity.model.SUser AS u WHERE u.managerUserId = :managerUserId) OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId IN (SELECT u.id FROM org.bonitasoft.engine.identity.model.SUser AS u WHERE u.managerUserId = :managerUserId) AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)) ) ) ) ) ) SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stateId = 4 AND a.stable = TRUE AND a.terminal = FALSE AND a.stateCategory = 'NORMAL' AND exists ( SELECT mapping.activityId FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId=a.id AND ( mapping.userId IN ( SELECT u.id FROM org.bonitasoft.engine.identity.model.SUser AS u WHERE u.managerUserId = :managerUserId ) OR mapping.actorId IN ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId IN ( SELECT u.id FROM org.bonitasoft.engine.identity.model.SUser AS u WHERE u.managerUserId = :managerUserId) OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId IN (SELECT u.id FROM org.bonitasoft.engine.identity.model.SUser AS u WHERE u.managerUserId = :managerUserId) AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId)) ) ) ) ) ) SELECT new map(a.assigneeId as userId, count(a) as numberOfTasks) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.assigneeId IN (:assigneeIds) AND a.stable = TRUE AND a.terminal = FALSE AND a.stateCategory = 'NORMAL' AND a.expectedEndDate < :currentTime GROUP BY a.assigneeId ORDER BY a.assigneeId ASC SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stable = TRUE AND a.terminal = FALSE AND a.stateCategory = 'NORMAL' AND a.assigneeId = 0 AND a.expectedEndDate < :currentTime AND EXISTS (SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId=a.id AND ( mapping.userId = :userId OR mapping.actorId in (SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE actor.id = actormember.actorId AND ( actormember.userId = :userId OR actormember.id IN ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) ) SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a SELECT COUNT(f.id) FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f SELECT f FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f SELECT e FROM org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance AS e SELECT count(DISTINCT f.id) FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE f.logicalGroup1 = supervisor.processDefId AND (supervisor.userId = :supervisorId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = :supervisorId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) ) ) ) ) SELECT DISTINCT f FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE f.logicalGroup1 = supervisor.processDefId AND (supervisor.userId = :supervisorId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = :supervisorId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) ) ) ) ) SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance AS a SELECT COUNT(p.id) FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p SELECT p FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p SELECT COUNT(DISTINCT p.id) FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a WHERE p.id = a.logicalGroup4 SELECT DISTINCT p FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a WHERE p.id = a.logicalGroup4 SELECT COUNT(DISTINCT p.id) FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE p.processDefinitionId = processsupervisor.processDefId SELECT DISTINCT p FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE p.processDefinitionId = processsupervisor.processDefId SELECT COUNT(DISTINCT p.id) FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a WHERE p.processDefinitionId = processsupervisor.processDefId AND p.id = a.logicalGroup4 SELECT DISTINCT p FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance AS a WHERE p.processDefinitionId = processsupervisor.processDefId AND p.id = a.logicalGroup4 SELECT COUNT(p.id) FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p WHERE ( p.stateId = 7 OR EXISTS ( SELECT f.id FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f WHERE f.logicalGroup4 = p.id AND f.stateId = 3 ) ) SELECT DISTINCT p FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p WHERE ( p.stateId = 7 OR EXISTS ( SELECT f.id FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f WHERE f.logicalGroup4 = p.id AND f.stateId = 3 ) ) SELECT p.id FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p WHERE p.lastUpdate < :maxLastUpdate AND p.stateId IN ( 0, 3, 4, 5, 6) ORDER BY id SELECT COUNT(p.id) FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a WHERE p.callerId = a.id and a.logicalGroup4 = :processInstanceId SELECT p.id FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.core.process.instance.model.SActivityInstance AS a WHERE p.callerId = a.id and a.logicalGroup4 = :processInstanceId SELECT p FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p WHERE p.callerId = :activityInstanceId SELECT COUNT(DISTINCT p.id) FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE ( p.stateId = 7 OR EXISTS ( SELECT f.id FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f WHERE f.logicalGroup4 = p.id AND f.stateId = 3 ) ) AND p.processDefinitionId = supervisor.processDefId AND (supervisor.userId = :userId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) ) ) ) ) SELECT DISTINCT p FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE ( p.stateId = 7 OR EXISTS ( SELECT f.id FROM org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance AS f WHERE f.logicalGroup4 = p.id AND f.stateId = 3 ) ) AND p.processDefinitionId = supervisor.processDefId AND (supervisor.userId = :userId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) ) ) ) ) SELECT COUNT(DISTINCT p.id) FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE p.stateId != 6 AND p.processDefinitionId = supervisor.processDefId AND (supervisor.userId = :userId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) ) ) ) ) SELECT DISTINCT p FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor WHERE p.stateId != 6 AND p.processDefinitionId = supervisor.processDefId AND (supervisor.userId = :userId OR (supervisor.id IN ( SELECT supervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS supervisor, org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = :userId AND ( (supervisor.groupId = um.groupId AND supervisor.roleId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId <= 0) OR (supervisor.roleId = um.roleId AND supervisor.groupId = um.groupId) ) ) ) ) SELECT COUNT(1) as count FROM process_instance p WHERE ( ID IN ( SELECT DISTINCT afi.logicalGroup2 FROM arch_flownode_instance afi WHERE afi.kind IN ('user', 'manual') AND (afi.executedBy = :userId OR afi.executedBySubstitute = :userId) AND afi.stateId = 2) OR startedBy = :userId ) SELECT * FROM process_instance p WHERE ( ID IN ( SELECT DISTINCT afi.logicalGroup2 FROM arch_flownode_instance afi WHERE afi.kind IN ('user', 'manual') AND (afi.executedBy = :userId OR afi.executedBySubstitute = :userId) AND afi.stateId = 2) OR startedBy = :userId ) SELECT COUNT(DISTINCT p.id) FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.identity.model.SUser AS u WHERE (p.id IN (SELECT at.logicalGroup2 FROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS at WHERE (at.executedBy = u.id OR at.executedBySubstitute = u.id) AND u.managerUserId = :managerUserId ) OR (p.startedBy = u.id AND u.managerUserId = :managerUserId) ) SELECT DISTINCT p FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.identity.model.SUser AS u WHERE (p.id IN (SELECT at.logicalGroup2 FROM org.bonitasoft.engine.core.process.instance.model.archive.SAHumanTaskInstance AS at WHERE (at.executedBy = u.id OR at.executedBySubstitute = u.id) AND u.managerUserId = :managerUserId ) OR (p.startedBy = u.id AND u.managerUserId = :managerUserId) ) SELECT COUNT(mapping.id) FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId = :humanTaskInstanceId AND ( mapping.userId = :userId OR EXISTS ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE mapping.actorId = actormember.actorId AND actormember.userId = :userId ) OR EXISTS ( SELECT actormember.id FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE mapping.actorId = actormember.actorId AND ( (user_membership.userId = :userId AND actormember.roleId = -1 AND actormember.groupId = user_membership.groupId) OR (actormember.groupId = -1 AND actormember.roleId = user_membership.roleId) OR (actormember.roleId = user_membership.roleId AND actormember.groupId = user_membership.groupId) ) ) ) SELECT user.id FROM org.bonitasoft.engine.identity.model.SUser AS user WHERE user.id IN ( SELECT mapping.userId FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId = :humanTaskInstanceId ) OR user.id IN ( SELECT actormember.userId FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping, org.bonitasoft.engine.actor.mapping.model.SActor AS actor WHERE mapping.activityId = :humanTaskInstanceId AND mapping.actorId = actor.id AND actor.id = actormember.actorId ) OR user.id IN ( SELECT user_membership.userId FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership, org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping, org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE mapping.activityId = :humanTaskInstanceId AND mapping.actorId = actor.id AND actor.id = actormember.actorId AND ( (actormember.roleId = -1 AND actormember.groupId = user_membership.groupId) OR (actormember.groupId = -1 AND actormember.roleId = user_membership.roleId) OR (actormember.roleId = user_membership.roleId AND actormember.groupId = user_membership.groupId) ) ) GROUP BY user.id ORDER BY MIN(user.userName) SELECT count(DISTINCT user.id) FROM org.bonitasoft.engine.identity.model.SUser AS user, org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId = :humanTaskInstanceId AND ( user.id = mapping.userId OR user.id IN ( SELECT actormember.userId FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.actor.mapping.model.SActor AS actor WHERE mapping.actorId = actor.id AND actor.id = actormember.actorId ) OR user.id IN ( SELECT user_membership.userId FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership, org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE mapping.actorId = actor.id AND actor.id = actormember.actorId AND ( (actormember.roleId = -1 AND actormember.groupId = user_membership.groupId) OR (actormember.groupId = -1 AND actormember.roleId = user_membership.roleId) OR (actormember.roleId = user_membership.roleId AND actormember.groupId = user_membership.groupId) ) ) ) SELECT DISTINCT user FROM org.bonitasoft.engine.identity.model.SUser AS user, org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId = :humanTaskInstanceId AND ( user.id = mapping.userId OR user.id IN ( SELECT actormember.userId FROM org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember, org.bonitasoft.engine.actor.mapping.model.SActor AS actor WHERE mapping.actorId = actor.id AND actor.id = actormember.actorId ) OR user.id IN ( SELECT user_membership.userId FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership, org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE mapping.actorId = actor.id AND actor.id = actormember.actorId AND ( (actormember.roleId = -1 AND actormember.groupId = user_membership.groupId) OR (actormember.groupId = -1 AND actormember.roleId = user_membership.roleId) OR (actormember.roleId = user_membership.roleId AND actormember.groupId = user_membership.groupId) ) ) ) SELECT COUNT(DISTINCT a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a, org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p WHERE p.processDefinitionId = :rootProcessDefinitionId AND p.id = a.logicalGroup2 AND a.stable = TRUE AND a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE AND ( a.assigneeId = :userId OR ( a.assigneeId = 0 AND EXISTS ( SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId = a.id AND ( mapping.userId = :userId OR EXISTS ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE mapping.actorId = actor.id AND actor.id = actormember.actorId AND ( actormember.userId = :userId OR EXISTS ( SELECT um.id FROM org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) ) ) ) SELECT DISTINCT(a) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a, org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p WHERE p.processDefinitionId = :rootProcessDefinitionId AND p.id = a.logicalGroup2 AND a.stable = TRUE AND a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE AND ( a.assigneeId = :userId OR ( a.assigneeId = 0 AND EXISTS ( SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId = a.id AND ( mapping.userId = :userId OR EXISTS ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE mapping.actorId = actor.id AND actor.id = actormember.actorId AND ( actormember.userId = :userId OR EXISTS ( SELECT um.id FROM org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = :userId AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) ) ) ) SELECT COUNT(DISTINCT a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a, org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.identity.model.SUser as user WHERE p.processDefinitionId = :rootProcessDefinitionId AND p.id = a.logicalGroup2 AND a.stable = TRUE AND a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE AND ( a.assigneeId = user.id OR ( a.assigneeId = 0 AND EXISTS ( SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId = a.id AND ( mapping.userId = user.id OR EXISTS ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE mapping.actorId = actor.id AND actor.id = actormember.actorId AND ( actormember.userId = user.id OR EXISTS ( SELECT um.id FROM org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = user.id AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) ) ) ) SELECT DISTINCT(a) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a, org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p, org.bonitasoft.engine.identity.model.SUser as user WHERE p.processDefinitionId = :rootProcessDefinitionId AND p.id = a.logicalGroup2 AND a.stable = TRUE AND a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.terminal = FALSE AND ( a.assigneeId = user.id OR ( a.assigneeId = 0 AND EXISTS ( SELECT mapping.id FROM org.bonitasoft.engine.core.process.instance.model.SPendingActivityMapping AS mapping WHERE mapping.activityId = a.id AND ( mapping.userId = user.id OR EXISTS ( SELECT actor.id FROM org.bonitasoft.engine.actor.mapping.model.SActor AS actor, org.bonitasoft.engine.actor.mapping.model.SActorMember AS actormember WHERE mapping.actorId = actor.id AND actor.id = actormember.actorId AND ( actormember.userId = user.id OR EXISTS ( SELECT um.id FROM org.bonitasoft.engine.identity.model.SUserMembership as um WHERE um.userId = user.id AND ( (actormember.groupId = um.groupId AND actormember.roleId = -1) OR (actormember.roleId = um.roleId AND actormember.groupId = -1) OR (actormember.groupId = um.groupId AND actormember.roleId = um.roleId) ) ) ) ) ) ) ) ) SELECT COUNT(a.id) FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.stateId = 4 SELECT a FROM org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance AS a WHERE a.stateExecuting = FALSE AND a.stateCategory = 'NORMAL' AND a.stateId = 4 SELECT COUNT(p.id) FROM org.bonitasoft.engine.core.process.instance.model.SProcessInstance AS p WHERE p.processDefinitionId = :processDefinitionId SELECT ref FROM org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance AS ref WHERE ref.name = :name AND ref.processInstanceId = :processInstanceId SELECT ref FROM org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance AS ref WHERE ref.processInstanceId = :processInstanceId ORDER BY ref.id SELECT ref FROM org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance AS ref WHERE ref.name = :name AND ref.flowNodeInstanceId = :flowNodeInstanceId SELECT size(ref.dataIds) FROM org.bonitasoft.engine.core.process.instance.model.business.data.SProcessMultiRefBusinessDataInstance AS ref WHERE ref.name = :name AND ref.processInstanceId = :processInstanceId UPDATE org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance SET numberOfActiveInstances = numberOfActiveInstances - :number, numberOfCompletedInstances = numberOfCompletedInstances + :number, lastUpdateDate = :lastUpdateDate WHERE id = :id AND numberOfActiveInstances >= :number UPDATE org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance SET numberOfActiveInstances = numberOfActiveInstances - :number, numberOfTerminatedInstances = numberOfTerminatedInstances + :number, lastUpdateDate = :lastUpdateDate WHERE id = :id AND numberOfActiveInstances >= :number UPDATE org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance SET numberOfActiveInstances = numberOfActiveInstances + :number, lastUpdateDate = :lastUpdateDate WHERE id = :id ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/connector/ConnectorResultTest.java ================================================ /** * 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.core.connector; import static org.junit.Assert.assertTrue; import org.junit.Test; public class ConnectorResultTest { @Test public void test() { final ConnectorResult result = new ConnectorResult(null, null, 100); assertTrue(result.getResult().isEmpty()); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/connector/impl/ConnectorExecutionTimeLoggerTest.java ================================================ /** * Copyright (C) 2020 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.core.connector.impl; import static org.assertj.core.api.Assertions.assertThat; import java.util.Collections; import java.util.HashMap; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; public class ConnectorExecutionTimeLoggerTest { @Rule public SystemOutRule systemOutRule = new SystemOutRule().enableLog(); private static final long MAX_DURATION_IN_MILLIS_THRESHOLD = 1000L; private ConnectorExecutionTimeLogger connectorExecutionTimeLogger = new ConnectorExecutionTimeLogger( MAX_DURATION_IN_MILLIS_THRESHOLD); @Test public void should_log_a_warning_when_connector_takes_more_time_than_the_defined_threshold() { systemOutRule.clearLog(); HashMap inputParameters = new HashMap<>(); inputParameters.put("serviceUrl", "http://some.external.service/1234?call=true"); inputParameters.put("groovyScript", "import something\n" + "\n" + "Thread.sleep(10000)"); SConnectorInstance sConnectorInstance = new SConnectorInstance("myConnector", 5555L, SConnectorInstance.FLOWNODE_TYPE, "theConnectorId", "1.0.0", ConnectorEvent.ON_ENTER); sConnectorInstance.setId(333L); connectorExecutionTimeLogger.log(123L, sConnectorInstance, new ConnectorServiceImplTest.MyTestConnector(), inputParameters, 1500); assertThat(systemOutRule.getLog()).contains( "Connector myConnector with id 333 with class org.bonitasoft.engine.core.connector.impl.ConnectorServiceImplTest$MyTestConnector" + " of process definition 123 on element flowNode with id 5555 took 1500 ms."); assertThat(systemOutRule.getLog()).contains( "Input parameters of the connector with id 333: {groovyScript: [import something Thread.sleep(10000)], serviceUrl: [http://some.external.service/1234?call=true]}"); } @Test public void should_not_log_when_connector_takes_less_than_the_defined_threashold() { systemOutRule.clearLog(); SConnectorInstance sConnectorInstance = new SConnectorInstance("myConnector", 5555L, SConnectorInstance.FLOWNODE_TYPE, "theConnectorId", "1.0.0", ConnectorEvent.ON_ENTER); connectorExecutionTimeLogger.log(123L, sConnectorInstance, new ConnectorServiceImplTest.MyTestConnector(), Collections.emptyMap(), 100); assertThat(systemOutRule.getLog()).isEmpty(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/connector/impl/ConnectorInstanceServiceImplTest.java ================================================ /** * 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.core.connector.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstanceWithFailureInfo; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Elias Ricken de Medeiros */ @RunWith(MockitoJUnitRunner.class) public class ConnectorInstanceServiceImplTest { private static final String STACK_TRACE = "stackTrace"; private static final String EXCEPTION_MESSAGE = "exceptionMessage"; private final String message = "An exception occurred during execution."; @Mock private ReadPersistenceService readPersistenceService; @Mock private Recorder recorder; @Mock private EventService eventService; @Mock private QueriableLoggerService queriableLoggerService; @Mock private SConnectorInstanceWithFailureInfo connectorInstanceWithFailureMock; @InjectMocks private ConnectorInstanceServiceImpl connectorInstanceServiceImpl; @Test public void should_persist_stack_trace_on_failure() throws Exception { SConnectorInstanceWithFailureInfo connectorInstanceWithFailure = new SConnectorInstanceWithFailureInfo(); connectorInstanceServiceImpl.setConnectorInstanceFailureException(connectorInstanceWithFailure, new Exception("Root cause")); verify(recorder).recordUpdate(argThat(r -> r.getFields().get("exceptionMessage").equals("Root cause") && ((String) r.getFields().get("stackTrace")).startsWith("java.lang.Exception: Root cause" + System.lineSeparator() + "\tat org.bonitasoft.engine.core.connector.impl.ConnectorInstanceServiceImplTest.should_persist_stack_trace_on_failure(ConnectorInstanceServiceImplTest.java:")), any()); } @Test public void should_not_fail_when_persisting_stack_trace_on_failure() throws Exception { SConnectorInstanceWithFailureInfo connectorInstanceWithFailure = new SConnectorInstanceWithFailureInfo(); Exception mockedException = mock(Exception.class); when(mockedException.getMessage()).thenReturn("the message of the exception"); when(mockedException.toString()).thenThrow(new AbstractMethodError()); connectorInstanceServiceImpl.setConnectorInstanceFailureException(connectorInstanceWithFailure, new Exception("wrapping exception", mockedException)); String stacktraceStart = "Unable to retrieve stacktrace, exceptions were:\n" + " * java.lang.Exception: wrapping exception\n" + " * "; String stacktraceEnd = ": the message of the exception\n"; verify(recorder).recordUpdate( argThat(r -> r.getFields().get("exceptionMessage").equals("the message of the exception") && ((String) r.getFields().get("stackTrace")).startsWith(stacktraceStart) && ((String) r.getFields().get("stackTrace")).endsWith(stacktraceEnd)), any()); } @Test public void setConnectorInstanceFailureExceptionWithNullMessage() throws Exception { final Exception exception = new Exception(); //call method connectorInstanceServiceImpl.setConnectorInstanceFailureException(connectorInstanceWithFailureMock, exception); //verify final ArgumentCaptor updateRecordCaptor = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder, times(1)).recordUpdate(updateRecordCaptor.capture(), nullable(String.class)); final UpdateRecord updateRecord = updateRecordCaptor.getValue(); final String stackTrace = (String) updateRecord.getFields().get(STACK_TRACE); assertNull(updateRecord.getFields().get(EXCEPTION_MESSAGE)); assertTrue(stackTrace.startsWith(Exception.class.getName())); assertTrue(stackTrace.contains(getClass().getName() + ".setConnectorInstanceFailureExceptionWithNullMessage")); } @Test public void setConnectorInstanceFailureExceptionWithCausedBy() throws Exception { String causedByMessage = "This is the caused by message."; final Exception causedByException = new Exception(causedByMessage); final Exception exception = new Exception(message, causedByException); //call method connectorInstanceServiceImpl.setConnectorInstanceFailureException(connectorInstanceWithFailureMock, exception); //verify final ArgumentCaptor updateRecordCaptor = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder, times(1)).recordUpdate(updateRecordCaptor.capture(), nullable(String.class)); final UpdateRecord updateRecord = updateRecordCaptor.getValue(); final String stackTrace = (String) updateRecord.getFields().get(STACK_TRACE); assertEquals(causedByMessage, updateRecord.getFields().get(EXCEPTION_MESSAGE)); assertTrue(stackTrace.startsWith(Exception.class.getName() + ": " + message)); assertTrue(stackTrace.contains(getClass().getName() + ".setConnectorInstanceFailureExceptionWithCausedBy")); assertTrue(stackTrace.contains("Caused by: " + Exception.class.getName() + ": " + causedByMessage)); } @Test public void cleanConnectorInstanceFailureException() throws Exception { //call method connectorInstanceServiceImpl.setConnectorInstanceFailureException(connectorInstanceWithFailureMock, null); //verify final ArgumentCaptor updateRecordCaptor = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder, times(1)).recordUpdate(updateRecordCaptor.capture(), nullable(String.class)); final UpdateRecord updateRecord = updateRecordCaptor.getValue(); final String stackTrace = (String) updateRecord.getFields().get(STACK_TRACE); assertNull(updateRecord.getFields().get(EXCEPTION_MESSAGE)); assertNull(stackTrace); } @Test public void setConnectorInstanceFailureExceptionMessageGreaterThen255() throws Exception { final String messageToRepeat = "This is a message repeated many times. "; final StringBuilder stb = new StringBuilder(); int currentLength = 0; while (currentLength < 256) { stb.append(messageToRepeat); currentLength += messageToRepeat.length(); } final String longMessage = stb.toString(); final Exception exception = new Exception(longMessage); //call method connectorInstanceServiceImpl.setConnectorInstanceFailureException(connectorInstanceWithFailureMock, exception); //verify final ArgumentCaptor updateRecordCaptor = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder, times(1)).recordUpdate(updateRecordCaptor.capture(), nullable(String.class)); final UpdateRecord updateRecord = updateRecordCaptor.getValue(); final String stackTrace = (String) updateRecord.getFields().get(STACK_TRACE); final String retrievedMessage = (String) updateRecord.getFields().get(EXCEPTION_MESSAGE); assertEquals(longMessage.substring(0, 255), retrievedMessage); assertTrue(stackTrace.startsWith(Exception.class.getName() + ": " + longMessage)); assertTrue(stackTrace .contains(getClass().getName() + ".setConnectorInstanceFailureExceptionMessageGreaterThen255")); } @Test public void getConnectorInstanceWithFailureInfo_should_return_the_result_of_select_list() throws Exception { //given Map parameters = new HashMap<>(); parameters.put("containerId", 1L); parameters.put("containerType", "flowNode"); parameters.put("state", "failed"); List connectors = Arrays.asList( mock(SConnectorInstanceWithFailureInfo.class), mock(SConnectorInstanceWithFailureInfo.class)); given( readPersistenceService.selectList(new SelectListDescriptor( "getConnectorInstancesWithFailureInfoInState", parameters, SConnectorInstanceWithFailureInfo.class, new QueryOptions(0, 100, SConnectorInstanceWithFailureInfo.class, "id", OrderByType.ASC)))) .willReturn(connectors); //when List retrievedConnectors = connectorInstanceServiceImpl .getConnectorInstancesWithFailureInfo(1L, "flowNode", "failed", 0, 100); //then assertThat(retrievedConnectors).isEqualTo(connectors); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/connector/impl/ConnectorServiceImplTest.java ================================================ /** * 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.core.connector.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.Serializable; 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.concurrent.CompletableFuture; import org.bonitasoft.engine.bpm.bar.BarResource; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.connector.AbstractConnector; import org.bonitasoft.engine.connector.Connector; import org.bonitasoft.engine.connector.ConnectorException; import org.bonitasoft.engine.connector.ConnectorExecutionResult; import org.bonitasoft.engine.connector.ConnectorExecutor; import org.bonitasoft.engine.connector.ConnectorValidationException; import org.bonitasoft.engine.connector.SConnector; import org.bonitasoft.engine.core.connector.ConnectorResult; import org.bonitasoft.engine.core.connector.exception.SConnectorException; import org.bonitasoft.engine.core.connector.exception.SInvalidConnectorImplementationException; import org.bonitasoft.engine.core.connector.parser.SConnectorImplementationDescriptor; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.operation.OperationService; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl; import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance; import org.bonitasoft.engine.dependency.DependencyService; import org.bonitasoft.engine.dependency.model.SDependency; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.resources.BARResourceType; import org.bonitasoft.engine.resources.ProcessResourcesService; import org.bonitasoft.engine.resources.SBARResource; import org.bonitasoft.engine.tracking.TimeTracker; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @SuppressWarnings("javadoc") @RunWith(MockitoJUnitRunner.class) public class ConnectorServiceImplTest { private static final long PROCESS_DEFINITION_ID = 123153L; private SProcessDefinitionImpl processDefinition; @Rule public SystemOutRule systemOutRule = new SystemOutRule().enableLog(); @Mock private CacheService cacheService; @Mock private DependencyService dependencyService; @Mock private ProcessResourcesService processResourcesService; @Mock private ConnectorExecutionTimeLogger connectorExecutionTimeLogger; @Mock private ConnectorExecutor connectorExecutor; @Mock private ExpressionResolverService expressionResolverService; @Mock private OperationService operationService; @Mock private TimeTracker timeTracker; @Mock private ClassLoaderService classLoaderService; @Captor private ArgumentCaptor connectorArgumentCaptor; @Captor ArgumentCaptor dependencyArgumentCaptor; @Captor ArgumentCaptor sBarResourceArgumentCaptor; private ConnectorServiceImpl connectorService; @Rule public ExpectedException expectedException = ExpectedException.none(); @SuppressWarnings("unchecked") @Before public void setup() { connectorService = new ConnectorServiceImpl(cacheService, connectorExecutor, expressionResolverService, operationService, dependencyService, classLoaderService, timeTracker, processResourcesService, connectorExecutionTimeLogger); processDefinition = new SProcessDefinitionImpl("proc", "1"); processDefinition.setId(PROCESS_DEFINITION_ID); } @Test(expected = SInvalidConnectorImplementationException.class) public void setConnectorImplementationWithCorruptFile() throws Exception { connectorService.setConnectorImplementation(processDefinition, "myConnector", "1.0.0", new byte[] { 1, 5, 6, 87, 9, 9, 36, 1, 6, 6, 5, 3, 5, 5, 5, 64, 6, 5, 5 }); } @Test(expected = SInvalidConnectorImplementationException.class) public void setConnectorImplementationZipHavingNoImpl() throws Exception { final byte[] zip = IOUtil.zip(Collections.singletonMap("connector.notImpl", "mocked".getBytes())); connectorService.setConnectorImplementation(processDefinition, "myConnector", "1.0.0", zip); } @Test(expected = SInvalidConnectorImplementationException.class) public void setConnectorImplementationValidFileButWrongImpl() throws Exception { final byte[] zip = IOUtil.zip(Collections.singletonMap("connector.impl", "mocked".getBytes())); connectorService.setConnectorImplementation(processDefinition, "myConnector", "1.0.0", zip); } @Test public void setNewConnectorImplemCleansOldDependencies() throws Exception { final long processDefId = 17L; final SProcessDefinitionImpl sProcessDef = new SProcessDefinitionImpl("MyProcess", "1.0"); sProcessDef.setId(processDefId); final String connectorDefId = "org.bonitasoft.connector.BeerConnector"; final String connectorDefVersion = "1.0.0"; final String connectorImplId = "org.bonitasoft.connector.HoogardenConnector"; final String connectorImplVersion = "1.0"; final String implementationClassName = "org.bonitasoft.engine.connectors.HoogardenBeerConnector"; final SConnectorImplementationDescriptor hoogardenConnectorDescriptor = new SConnectorImplementationDescriptor( implementationClassName, connectorImplId, connectorImplVersion, connectorDefId, connectorDefVersion, new ArrayList<>(Arrays.asList("some1.jar", "HoogardenConnector.jar"))); final SConnectorImplementationDescriptor oldConnectorDescriptor = new SConnectorImplementationDescriptor( implementationClassName, connectorImplId, connectorImplVersion, connectorDefId, connectorDefVersion, new ArrayList<>(Collections.singletonList("file.jar"))); Map zipFileMap = new HashMap<>(3); byte[] connectorImplFile = createConnectorImplFile(hoogardenConnectorDescriptor); zipFileMap.put("HoogardenBeerConnector.impl", connectorImplFile); final byte[] dep1Bytes = { 12, 94, 14, 12 }; zipFileMap.put("some1.jar", dep1Bytes); final byte[] hoogardenConnectorBytes = { 12, 94, 14, 9, 54, 65, 98, 54, 21, 32, 65 }; zipFileMap.put("HoogardenConnector.jar", hoogardenConnectorBytes); final byte[] zip1 = IOUtil.zip(zipFileMap); final SBARResource originalConnector = new SBARResource("file.impl", BARResourceType.CONNECTOR, processDefId, createConnectorImplFile(oldConnectorDescriptor)); doReturn(Collections.singletonList(originalConnector)).when(processResourcesService) .get(eq(processDefId), eq(BARResourceType.CONNECTOR), eq(0), anyInt()); SDependency dependency = mock(SDependency.class); doReturn(dependency).when(dependencyService).getDependencyOfArtifact(processDefId, ScopeType.PROCESS, "file.jar"); connectorService.setConnectorImplementation(sProcessDef, connectorDefId, connectorDefVersion, zip1); verify(dependencyService).createMappedDependency("HoogardenConnector.jar", hoogardenConnectorBytes, "HoogardenConnector.jar", processDefId, ScopeType.PROCESS); verify(dependencyService).createMappedDependency("some1.jar", dep1Bytes, "some1.jar", processDefId, ScopeType.PROCESS); verify(processResourcesService).add(processDefId, "HoogardenBeerConnector.impl", BARResourceType.CONNECTOR, connectorImplFile); verify(dependencyService).deleteDependency(dependency); verify(processResourcesService).remove(originalConnector); } @Test public void setNewConnectorImplemShouldIgnoreSourceFiles() throws Exception { final long processDefId = 1324565477444L; Map zipFileMap = new HashMap<>(1); zipFileMap.put("src/net/company/MyImplem.java", "some Java source file content".getBytes()); zipFileMap.put("connector.impl", "thecontent".getBytes()); final byte[] zip = IOUtil.zip(zipFileMap); connectorService.extractConnectorImplementation(zip); verify(processResourcesService, times(0)).add(eq(processDefId), anyString(), any(BARResourceType.class), any(byte[].class)); } @Test public void getConnectorImplementationShouldReadFileWhenCacheIsVoid() throws Exception { checkGetConnectorImplementationUsesCache(0, 1, true); } @Test public void getConnectorImplementationShouldReadFileWhenCacheIsNotEmpty() throws Exception { checkGetConnectorImplementationUsesCache(1, 0, true); } @Test public void getConnectorImplementationShouldReadFileWhenCacheDoesNotContainsConnector() throws Exception { checkGetConnectorImplementationUsesCache(1, 1, false); } @Test public void getConnectorImplementationShouldNoteReadFileWhenCacheContainsConnector() throws Exception { checkGetConnectorImplementationUsesCache(1, 0, true); } @Test public void should_executeConnector_call_connector_executor() throws Exception { //given SConnectorImplementationDescriptor connectorImplementationDescriptor = new SConnectorImplementationDescriptor( MyTestConnector.class.getName(), "implId", "impplVersion", "defId", "defVersion", new ArrayList<>(Collections. emptyList())); SConnectorInstance connectorInstance = mock(SConnectorInstance.class); when(connectorExecutor.execute(any(), any(), any())) .thenReturn(CompletableFuture .completedFuture(ConnectorExecutionResult.result(Collections.emptyMap()).tookMillis(100))); //when Map inputParameters = Collections. singletonMap("key", "value"); ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); connectorService.executeConnector(PROCESS_DEFINITION_ID, connectorInstance, connectorImplementationDescriptor, contextClassLoader, inputParameters); //then verify(connectorExecutor).execute(connectorArgumentCaptor.capture(), eq(inputParameters), eq(contextClassLoader)); SConnector sConnector = connectorArgumentCaptor.getValue(); assertThat(sConnector).isInstanceOf(SConnectorAdapter.class); assertThat(((SConnectorAdapter) sConnector).getConnector()).isInstanceOf(MyTestConnector.class); } @Test public void should_log_execution_time_when_executing_connector() throws Exception { //given SConnectorImplementationDescriptor connectorImplementationDescriptor = new SConnectorImplementationDescriptor( MyTestConnector.class.getName(), "implId", "impplVersion", "defId", "defVersion", new ArrayList<>(Collections.emptyList())); SConnectorInstance connectorInstance = mock(SConnectorInstance.class); when(connectorExecutor.execute(any(), any(), any())).thenReturn(CompletableFuture .completedFuture(ConnectorExecutionResult.result(Collections.emptyMap()).tookMillis(100))); //when Map inputParameters = Collections.singletonMap("key", "value"); ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); connectorService.executeConnector(PROCESS_DEFINITION_ID, connectorInstance, connectorImplementationDescriptor, contextClassLoader, inputParameters); //then verify(connectorExecutionTimeLogger).log(eq(PROCESS_DEFINITION_ID), eq(connectorInstance), any(MyTestConnector.class), eq(inputParameters), eq(100L)); } private void checkGetConnectorImplementationUsesCache(final int givenCacheSizeToBeReturned, final int expectedNumberOfCacheStoreInvocations, final boolean shouldCacheContainsConnectorImplementation) throws Exception { final String connectorDefId = "org.bonitasoft.connector.BeerConnector"; final String connectorDefVersion = "1.0.0"; final String connectorImplId = "org.bonitasoft.connector.HoogardenConnector"; final String connectorImplVersion = "1.0"; final String implementationClassName = "org.bonitasoft.engine.connectors.HoogardenBeerConnector"; final SConnectorImplementationDescriptor connectorImplDescriptor = new SConnectorImplementationDescriptor( implementationClassName, connectorImplId, connectorImplVersion, connectorDefId, connectorDefVersion, new ArrayList<>(Arrays.asList("some1.jar", "HoogardenConnector.jar"))); byte[] connectorImplFile = createConnectorImplFile(connectorImplDescriptor); final Map zipFileMap = new HashMap<>(3); zipFileMap.put("HoogardenBeerConnector.impl", connectorImplFile); zipFileMap.put("some1.jar", new byte[] { 12, 94, 14, 12 }); zipFileMap.put("HoogardenConnector.jar", new byte[] { 12, 94, 14, 9, 54, 65, 98, 54, 21, 32, 65 }); final byte[] zip1 = IOUtil.zip(zipFileMap); doReturn(Collections .singletonList(new SBARResource("HoogardenBeerConnector.impl", BARResourceType.CONNECTOR, processDefinition.getId(), connectorImplFile))) .when(processResourcesService) .get(eq(processDefinition.getId()), eq(BARResourceType.CONNECTOR), eq(0), anyInt()); //setConnectorImplementation store to cache connectorService.setConnectorImplementation(processDefinition, connectorDefId, connectorDefVersion, zip1); //given doReturn(givenCacheSizeToBeReturned).when(cacheService).getCacheSize(ConnectorServiceImpl.CONNECTOR_CACHE_NAME); List cacheContentKeys = Collections.emptyList(); final String buildConnectorImplementationKey = connectorService .buildConnectorImplementationKey(processDefinition.getId(), connectorImplId, connectorImplVersion); if (shouldCacheContainsConnectorImplementation) { cacheContentKeys = Collections.singletonList(buildConnectorImplementationKey); } doReturn(cacheContentKeys).when(cacheService).getKeys(ConnectorServiceImpl.CONNECTOR_CACHE_NAME); doReturn(connectorImplDescriptor).when(cacheService).get(ConnectorServiceImpl.CONNECTOR_CACHE_NAME, buildConnectorImplementationKey); //when connectorService.getConnectorImplementations(processDefinition.getId(), 0, 10, "", OrderByType.ASC); //then verify(cacheService, times(expectedNumberOfCacheStoreInvocations + 1)).store(anyString(), any(Serializable.class), any(Object.class)); } public static class MyTestConnector extends AbstractConnector { @Override public void validateInputParameters() throws ConnectorValidationException { } @Override protected void executeBusinessLogic() throws ConnectorException { } } @Test public void getNumberOfConnectorImplementations_should_call_count_on_resource_service() throws Exception { final long processDefinitionId = 451L; final long expectedCount = 11L; doReturn(expectedCount).when(processResourcesService).count(processDefinitionId, BARResourceType.CONNECTOR); final Long numberOfConnectorImplementations = connectorService .getNumberOfConnectorImplementations(processDefinitionId); assertThat(numberOfConnectorImplementations).isEqualTo(expectedCount); } @Test public void should_read_connector_archive_correctly() throws Exception { //given String connectorImplFileName = "myConnector.impl"; HashMap files = new HashMap<>(); byte[] connectorImplFileContent = ("\n" + "\n" + "\torg.bonitasoft.connector.testConnectorWithOutput\n" + "\t1.0\n" + "\torg.bonitasoft.engine.connectors.TestConnectorWithModifiedOutput\n" + "\torg.bonitasoft.connector.testConnectorWithModifiedOutput\n" + "\t1.0\n" + "\n" + "\t\n" + "\t\tTestConnectorWithModifiedOutput.jar\n" + "\t\n" + "\n").getBytes(); files.put(connectorImplFileName, connectorImplFileContent); files.put("classpath/jar1.jar", new byte[] { 1, 2, 3 }); files.put("classpath/jar2.jar", new byte[] { 1, 2, 4 }); byte[] zip = IOUtil.zip(files); //when ConnectorArchive connectorArchive = connectorService.extractConnectorImplementation(zip); //then assertThat(connectorArchive.getConnectorImplName()).isEqualTo(connectorImplFileName); assertThat(connectorArchive.getConnectorImplContent()).isEqualTo(connectorImplFileContent); assertThat(connectorArchive.getDependencies()).containsKeys("jar1.jar", "jar2.jar").hasSize(2); assertThat(connectorArchive.getDependencies().get("jar1.jar")).isEqualTo(new byte[] { 1, 2, 3 }); assertThat(connectorArchive.getDependencies().get("jar2.jar")).isEqualTo(new byte[] { 1, 2, 4 }); } @Test public void should_setConnectorImplementation_delete_and_create_dependency() throws Exception { //given byte[] zip = createConnectorArchiveZip("myConnector2.impl", "connectorId", "connectorVersion", new BarResource("jar3.jar", new byte[] { 3 }), new BarResource("jar4.jar", new byte[] { 4 })); havingConnector(processDefinition, "myConnector1.impl", "connectorId", "connectorVersion", new BarResource("jar1.jar", new byte[] { 1 }), new BarResource("jar2.jar", new byte[] { 2 })); //when connectorService.setConnectorImplementation(processDefinition, "connectorId", "connectorVersion", zip); //then verify(dependencyService, times(2)).deleteDependency(dependencyArgumentCaptor.capture()); assertThat(dependencyArgumentCaptor.getAllValues()).extracting("fileName").containsOnly("jar1.jar", "jar2.jar"); verify(dependencyService).createMappedDependency("jar3.jar", new byte[] { 3 }, "jar3.jar", processDefinition.getId(), ScopeType.PROCESS); verify(dependencyService).createMappedDependency("jar4.jar", new byte[] { 4 }, "jar4.jar", processDefinition.getId(), ScopeType.PROCESS); } @Test public void should_setConnectorImplementation_replace_jar_even_if_jar_was_not_in_jarDependencies() throws Exception { //given byte[] zip = createConnectorArchiveZip("myConnector1.impl", "connectorId", "connectorVersion", new BarResource("jar2.jar", new byte[] { 3 })); //a process with an inconsistent connector implementation is deployed: connector have a dependency names jar2.jar and jar1.jar but they are not declared in jarDependencies doReturn(Collections.singletonList( new SBARResource("myConnector1.impl", BARResourceType.CONNECTOR, processDefinition.getId(), createConnectorImplFile("connectorId", "connectorVersion")))) .when(processResourcesService) .get(eq(processDefinition.getId()), eq(BARResourceType.CONNECTOR), anyInt(), anyInt()); doReturn(new SDependency("jar2.jar", "jar2.jar", new byte[] { 2 })).when(dependencyService) .getDependencyOfArtifact(processDefinition.getId(), ScopeType.PROCESS, "jar2.jar"); systemOutRule.clearLog(); //when connectorService.setConnectorImplementation(processDefinition, "connectorId", "connectorVersion", zip); //then verify(dependencyService, never()).createMappedDependency(anyString(), any(byte[].class), anyString(), anyLong(), any(ScopeType.class)); verify(dependencyService, never()).deleteDependency(any(SDependency.class)); verify(dependencyService).updateDependencyOfArtifact("jar2.jar", new byte[] { 3 }, "jar2.jar", processDefinition.getId(), ScopeType.PROCESS); assertThat(systemOutRule.getLog()).containsPattern( "WARN.*Updating a dependency of the connector connectorId in version connectorVersion of process definition 123153. " + "The jar file jar2.jar was not declared in the previous connector implementation but is in the dependencies of the process. " + "The jar is still updated but this can lead to inconsistencies."); } @Test public void should_setConnectorImplementation_update_existing_dependencies() throws Exception { //given byte[] zip = createConnectorArchiveZip("myConnector2.impl", "connectorId", "connectorVersion", new BarResource("jar2.jar", new byte[] { 3 }), new BarResource("jar4.jar", new byte[] { 4 })); havingConnector(processDefinition, "myConnector1.impl", "connectorId", "connectorVersion", new BarResource("jar1.jar", new byte[] { 1 }), new BarResource("jar2.jar", new byte[] { 2 })); //when connectorService.setConnectorImplementation(processDefinition, "connectorId", "connectorVersion", zip); //then verify(dependencyService, times(1)).deleteDependency(dependencyArgumentCaptor.capture()); assertThat(dependencyArgumentCaptor.getAllValues()).extracting("fileName").containsOnly("jar1.jar"); verify(dependencyService).createMappedDependency("jar4.jar", new byte[] { 4 }, "jar4.jar", processDefinition.getId(), ScopeType.PROCESS); verify(dependencyService).updateDependencyOfArtifact("jar2.jar", new byte[] { 3 }, "jar2.jar", processDefinition.getId(), ScopeType.PROCESS); } @Test public void should_setConnectorImplementation_delete_and_create_impl_file() throws Exception { //given byte[] zip = createConnectorArchiveZip("myConnector2.impl", "connectorId", "connectorVersion"); havingConnector(processDefinition, "myConnector1.impl", "connectorId", "connectorVersion"); //when connectorService.setConnectorImplementation(processDefinition, "connectorId", "connectorVersion", zip); //then verify(processResourcesService).remove(sBarResourceArgumentCaptor.capture()); assertThat(sBarResourceArgumentCaptor.getValue().getName()).isEqualTo("myConnector1.impl"); byte[] connectorImplFile = createConnectorImplFile("connectorId", "connectorVersion"); verify(processResourcesService).add(processDefinition.getId(), "myConnector2.impl", BARResourceType.CONNECTOR, connectorImplFile); } @Test public void should_setConnectorImplementation_update_connector_implementation_file() throws Exception { //given byte[] zip = createConnectorArchiveZip("myConnector1.impl", "connectorId", "connectorVersion", new BarResource("jar1.jar", new byte[] { 1 })); havingConnector(processDefinition, "myConnector1.impl", "connectorId", "connectorVersion"); //when connectorService.setConnectorImplementation(processDefinition, "connectorId", "connectorVersion", zip); //then verify(processResourcesService, never()).remove(any(SBARResource.class)); verify(processResourcesService, never()).add(anyLong(), anyString(), any(BARResourceType.class), any(byte[].class)); byte[] connectorImplFile = createConnectorImplFile("connectorId", "connectorVersion", new BarResource("jar1.jar", new byte[] { 1 })); verify(processResourcesService).update(sBarResourceArgumentCaptor.capture(), eq(connectorImplFile)); assertThat(sBarResourceArgumentCaptor.getValue().getName()).isEqualTo("myConnector1.impl"); } private void havingConnector(SProcessDefinitionImpl processDefinition, String implName, String connectorId, String connectorVersion, BarResource... jars) throws Exception { doReturn(Collections.singletonList( new SBARResource(implName, BARResourceType.CONNECTOR, processDefinition.getId(), createConnectorImplFile(connectorId, connectorVersion, jars)))) .when(processResourcesService) .get(eq(processDefinition.getId()), eq(BARResourceType.CONNECTOR), anyInt(), anyInt()); for (BarResource jar : jars) { doReturn(new SDependency(jar.getName(), jar.getName(), jar.getContent())).when(dependencyService) .getDependencyOfArtifact(processDefinition.getId(), ScopeType.PROCESS, jar.getName()); } } @Test public void setConnectorImplementation_should_detect_old_convention_fileName_in_db() throws Exception { //given byte[] zip = createConnectorArchiveZip("myConnector2.impl", "connectorId", "connectorVersion"); havingConnectorWithWrongJarName(processDefinition, "myConnector1.impl", "connectorId", "connectorVersion", new BarResource("myJar.jar", new byte[] { 1 })); //when connectorService.setConnectorImplementation(processDefinition, "connectorId", "connectorVersion", zip); //then verify(dependencyService).getDependencyOfArtifact(123153L, ScopeType.PROCESS, "123153_myJar.jar"); } private void havingConnectorWithWrongJarName(SProcessDefinitionImpl processDefinition, String implName, String connectorId, String connectorVersion, BarResource... jars) throws Exception { doReturn(Collections.singletonList( new SBARResource(implName, BARResourceType.CONNECTOR, processDefinition.getId(), createConnectorImplFile(connectorId, connectorVersion, jars)))) .when(processResourcesService) .get(eq(processDefinition.getId()), eq(BARResourceType.CONNECTOR), anyInt(), anyInt()); for (BarResource jar : jars) { doReturn(null).when(dependencyService).getDependencyOfArtifact(processDefinition.getId(), ScopeType.PROCESS, jar.getName()); doReturn(new SDependency(jar.getName(), jar.getName(), jar.getContent())).when(dependencyService) .getDependencyOfArtifact(processDefinition.getId(), ScopeType.PROCESS, processDefinition.getId() + "_" + jar.getName()); } } private byte[] createConnectorArchiveZip(String connectorImplFileName, String definitionId, final String definitionVersion, BarResource... jars) throws Exception { HashMap files = new HashMap<>(); for (BarResource jar : jars) { files.put("classpath/" + jar.getName(), jar.getContent()); } byte[] connectorImplFileContent = createConnectorImplFile(definitionId, definitionVersion, jars); files.put(connectorImplFileName, connectorImplFileContent); return IOUtil.zip(files); } private byte[] createConnectorImplFile(SConnectorImplementationDescriptor connector) { return createConnectorImplFile(connector.getDefinitionId(), connector.getDefinitionVersion(), connector.getId(), connector.getVersion(), connector.getImplementationClassName(), connector.getJarDependencies()); } private byte[] createConnectorImplFile(String definitionId, String definitionVersion, String id, String version, String className, List jars) { return ("\n" + "\n" + "\t" + definitionId + "\n" + "\t" + definitionVersion + "\n" + "\t" + className + "\n" + "\t" + id + "\n" + "\t" + version + "\n" + "\n" + "\t\n" + getJarsXml(jars) + "\t\n" + "\n").getBytes(); } private byte[] createConnectorImplFile(String definitionId, String definitionVersion, BarResource... jars) { List jarNames = new ArrayList<>(jars.length); for (BarResource jar : jars) { jarNames.add(jar.getName()); } return createConnectorImplFile(definitionId, definitionVersion, "implId", "1.0", "TheClass", jarNames); } private String getJarsXml(List jars) { StringBuilder result = new StringBuilder(); for (String jar : jars) { result.append("\t\t").append(jar).append("\n"); } return result.toString(); } @Test public void should_throw_a_SConnectorException_if_a_Throwable_is_thrown_when_executing_a_connector() throws Exception { //given SConnectorImplementationDescriptor connectorImplementationDescriptor = new SConnectorImplementationDescriptor( MyTestConnector.class.getName(), "implId", "impplVersion", "defId", "defVersion", new ArrayList<>(Collections. emptyList())); SConnectorInstance connectorInstance = mock(SConnectorInstance.class); Map inputParameters = Collections. singletonMap("key", "value"); ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); when(connectorExecutor.execute(notNull(SConnector.class), eq(inputParameters), eq(contextClassLoader))) .thenThrow(new NoClassDefFoundError()); expectedException.expect(SConnectorException.class); connectorService.executeConnector(PROCESS_DEFINITION_ID, connectorInstance, connectorImplementationDescriptor, contextClassLoader, inputParameters); } @Test public void should_log_connector_details_when_executing_it() throws Exception { //given SConnectorImplementationDescriptor connectorImplementationDescriptor = new SConnectorImplementationDescriptor( MyTestConnector.class.getName(), "implId", "impplVersion", "defId", "defVersion", new ArrayList<>(Collections. emptyList())); SConnectorInstance connectorInstance = new SConnectorInstance("myConnectorInstance", 123L, "containerType", "connectorId", "connectorVersion", ConnectorEvent.ON_ENTER); when(connectorExecutor.execute(any(), any(), any())) .thenReturn(CompletableFuture .completedFuture(ConnectorExecutionResult.result(Collections.emptyMap()).tookMillis(100))); systemOutRule.clearLog(); //when Map inputParameters = new HashMap<>(); inputParameters.put("param1", "value1"); inputParameters.put("param2", "value2"); ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); connectorService.executeConnector(PROCESS_DEFINITION_ID, connectorInstance, connectorImplementationDescriptor, contextClassLoader, inputParameters); //then assertThat(systemOutRule.getLog()).contains( "Executing connector [name: , version: , connector id: , " + "connector instance id: <0>, container type: , container id: <123>, activation event: "); } @Test public void should_disconnect_connector_when_executeOutputOperation_throw_a_SOperationExecutionException() throws Exception { // Given doThrow(SOperationExecutionException.class).when(operationService).execute(anyList(), anyLong(), nullable(String.class), nullable(SExpressionContext.class)); //When ConnectorResult connectorResult = new ConnectorResult(mock(Connector.class), new HashMap<>(), 1); assertThatThrownBy( () -> connectorService.executeOutputOperation(new ArrayList<>(), new SExpressionContext(1L, "", 1L), connectorResult)) .isInstanceOf(SConnectorException.class); // Then verify(connectorExecutor).disconnect(any(SConnectorAdapter.class)); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/document/api/impl/DocumentServiceImplTest.java ================================================ /** * 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.core.document.api.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; import java.util.*; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.core.document.model.AbstractSMappedDocument; import org.bonitasoft.engine.core.document.model.SDocument; import org.bonitasoft.engine.core.document.model.SMappedDocument; import org.bonitasoft.engine.core.document.model.archive.SAMappedDocument; import org.bonitasoft.engine.core.document.model.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.persistence.*; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Vincent Elcrin * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class DocumentServiceImplTest { @Mock private Recorder recorder; @Mock private ReadPersistenceService persistenceService; @Mock private ReadPersistenceService definitiveArchiveReadPersistenceService; @Mock private SDocumentDownloadURLProvider urlProvider; @Mock private ArchiveService archiveService; private DocumentServiceImpl documentService; @Before public void setUp() { when(archiveService.getDefinitiveArchiveReadPersistenceService()) .thenReturn(definitiveArchiveReadPersistenceService); documentService = spy(new DocumentServiceImpl(recorder, persistenceService, urlProvider, archiveService)); } @Test public void generateDocumentURL_should_call_urlProvider() { doReturn("generated").when(urlProvider).generateURL("name", "docId"); assertEquals("generated", documentService.generateDocumentURL("name", "docId")); } @Test public void should_getDocumentList_return_the_list() throws Exception { //given final List documentList = Arrays.asList(new SMappedDocument(), new SMappedDocument()); doReturn(documentList).when(persistenceService) .selectList(ArgumentMatchers.> any()); //when final List theList = documentService.getDocumentList("theList", 45L, 0, 100); //then assertThat(theList).isEqualTo(documentList); } @Test public void should_getDocumentList_return_the_list_with_more_than_100_elements() throws Exception { //given final List documentList1 = constructList(100); final List documentList2 = constructList(50); when(persistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(documentList1).thenReturn(documentList2); //when final List theList = documentService.getDocumentList("theList", 45L, 0, 100); //then documentList1.addAll(documentList2); assertThat(theList).isEqualTo(documentList1); } @Test public void should_getDocumentList_return_the_list_with_100_elements() throws Exception { //given final List documentList1 = constructList(100); final List documentList2 = constructList(0); when(persistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(documentList1).thenReturn(documentList2); //when final List theList = documentService.getDocumentList("theList", 45L, 0, 100); //then assertThat(theList).isEqualTo(documentList1); } @Test public void should_call_deletion_with_right_arguments() throws Exception { //given Long sourceObjectId = 5L; final SAMappedDocument mappedDocument = new SAMappedDocument(1L, sourceObjectId); byte[] docContent = "theContent".getBytes(); final SDocument document = new SDocument(docContent); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(UpdateRecord.class); when(definitiveArchiveReadPersistenceService.selectById(any())) .thenReturn(mappedDocument); when(persistenceService.selectById(any())) .thenReturn(document); //when documentService.deleteContentOfArchivedDocument(sourceObjectId); //then verify(recorder).recordUpdate(argumentCaptor.capture(), any()); UpdateRecord record = argumentCaptor.getValue(); assertThat(record.getFields()).containsOnlyKeys("content", "hasContent"); } private List constructList(final int size) { final ArrayList list = new ArrayList<>(); for (int i = 0; i < size; i++) { list.add(new SMappedDocument()); } return list; } @Test public void should_getDocumentList_return_empty_list() throws Exception { //given doReturn(Collections.emptyList()).when(persistenceService) .selectList(ArgumentMatchers.> any()); //when final List theList = documentService.getDocumentList("theList", 45L, 0, 100); //then assertThat(theList).isEmpty(); } @Test public void should_getDocumentList_at_give_time_get_archived() throws Exception { //given final List sMappedDocuments = Arrays.asList(new SMappedDocument(), new SMappedDocument()); final List saMappedDocuments = Arrays.asList(new SAMappedDocument(), new SAMappedDocument()); doReturn(saMappedDocuments).when(persistenceService).selectList( SelectDescriptorBuilder.getArchivedDocumentList("theList", 45L, new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS), 123456789L)); doReturn(sMappedDocuments).when(persistenceService) .selectList( SelectDescriptorBuilder.getDocumentListCreatedBefore("theList", 45L, new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS), 123456789L)); //when final List theList = documentService.getDocumentList("theList", 45L, 123456789L); //then final ArrayList expected = new ArrayList<>(saMappedDocuments); expected.addAll(sMappedDocuments); assertThat(theList).isEqualTo(expected); } @Test(expected = SObjectNotFoundException.class) public void should_get_throw_not_found_exceptions() throws Exception { //when documentService.getDocument(123456L); //then exception } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/document/api/impl/SDocumentDownloadURLProviderImplTest.java ================================================ /** * Copyright (C) 2020 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.core.document.api.impl; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; /** * @author Julien Mege */ @RunWith(MockitoJUnitRunner.class) public class SDocumentDownloadURLProviderImplTest { private static final String SERVLET_URL = "documentDownload"; private SDocumentDownloadURLProvider urlProvider = new SDocumentDownloadURLProviderImpl(SERVLET_URL); @Test public void generateURL_should_return_encoded_url() { assertEquals("documentDownload?fileName=%5BnameWithSpecialChar%5D&contentStorageId=docId", urlProvider.generateURL("[nameWithSpecialChar]", "docId")); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/document/model/builder/impl/SDocumentBuilderFactoryImplTest.java ================================================ /** * 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.core.document.model.builder.impl; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.core.document.model.builder.SDocumentBuilder; import org.bonitasoft.engine.core.document.model.builder.SDocumentBuilderFactory; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet * @version 6.4.2 * @since 6.4.2 */ @RunWith(MockitoJUnitRunner.class) public class SDocumentBuilderFactoryImplTest { @InjectMocks private SDocumentBuilderFactory sDocumentBuilderFactory; @Test(expected = IllegalArgumentException.class) public final void createNewProcessDocument_should_throw_exception_if_no_file_name() { // Given final String fileName = null; final String mimeType = "mimeType"; final long authorId = 3; final byte[] content = "content".getBytes(); // When sDocumentBuilderFactory.createNewProcessDocument(fileName, mimeType, authorId, content); } /** * Test method for * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(byte[], java.lang.String, java.lang.String)}. */ @Test(expected = IllegalArgumentException.class) public final void createNewProcessDocument_should_throw_exception_if_empty_file_name() { // Given final String fileName = ""; final String mimeType = "mimeType"; final long authorId = 3; final byte[] content = "content".getBytes(); // When sDocumentBuilderFactory.createNewProcessDocument(fileName, mimeType, authorId, content); } @Test public final void createNewProcessDocument_should_be_valid_if_no_content_but_with_filename() { // Given final String fileName = "filename"; final String mimeType = "mimeType"; final long authorId = 3; final byte[] content = null; // When final SDocumentBuilder newProcessDocument = sDocumentBuilderFactory.createNewProcessDocument(fileName, mimeType, authorId, content); // then assertThat(newProcessDocument).isNotNull(); } /** * Test method for * {@link org.bonitasoft.engine.bpm.document.DocumentValue#DocumentValue(byte[], java.lang.String, java.lang.String)}. */ @Test public final void createNewProcessDocument_should_be_valid_if_empty_content_but_with_filename() { // Given final String fileName = "filename"; final String mimeType = "mimeType"; final long authorId = 3; final byte[] content = "".getBytes(); // When final SDocumentBuilder newProcessDocument = sDocumentBuilderFactory.createNewProcessDocument(fileName, mimeType, authorId, content); // then assertThat(newProcessDocument).isNotNull(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SContractViolationExceptionTest.java ================================================ /** * 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.core.process.instance.api.exceptions; import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.junit.Test; /** * author Emmanuel Duchastenier */ public class SContractViolationExceptionTest { @Test public void should_return_explanation() { final SContractViolationException contractViolationException = new SContractViolationException("Bad contract", Arrays.asList("issue1", "issue2")); assertThat(contractViolationException.getExplanations()).containsExactly("issue1", "issue2"); } @Test public void should_print_stack_trace_show_explanations() { final SContractViolationException contractViolationException = new SContractViolationException("Bad contract", Arrays.asList("issue1", "issue2")); assertThat(contractViolationException.getMessage()).isEqualTo("Bad contract: issue1, issue2"); } @Test public void should_print_stack_trace_show_one_explanation() { final SContractViolationException contractViolationException = new SContractViolationException("Bad contract", Arrays.asList("issue1")); assertThat(contractViolationException.getMessage()).isEqualTo("Bad contract: issue1"); } @Test public void should_print_stack_trace_with_null_explanations() { List explanations = null; @SuppressWarnings("ConstantConditions") final SContractViolationException contractViolationException = new SContractViolationException("Bad contract", explanations); assertThat(contractViolationException.getMessage()).isEqualTo("Bad contract: no details"); } @Test public void should_print_stack_trace_with_empty_explanations() { List explanations = Collections.emptyList(); final SContractViolationException contractViolationException = new SContractViolationException("Bad contract", explanations); assertThat(contractViolationException.getMessage()).isEqualTo("Bad contract: no details"); } @Test public void should_getSimpleMessage_not_show_explanations() { final SContractViolationException contractViolationException = new SContractViolationException("Bad contract", Arrays.asList("issue1", "issue2")); assertThat(contractViolationException.getSimpleMessage()).isEqualTo("Bad contract"); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/api/exceptions/SProcessInstanceReadExceptionTest.java ================================================ /** * 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.core.process.instance.api.exceptions; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.junit.Test; /** * @author Celine Souchet */ public class SProcessInstanceReadExceptionTest { @Test public void constructTheExceptionWithoutADescriptor() { final SBonitaReadException cause = new SBonitaReadException("problem"); final SProcessInstanceReadException exception = new SProcessInstanceReadException(cause); assertTrue(exception.getMessage().isEmpty()); } @Test public void constructTheExceptionWithADescriptor() { final SelectOneDescriptor descriptor = new SelectOneDescriptor( "getPersistentObject", null, PersistentObject.class); final SBonitaReadException cause = new SBonitaReadException("problem", null, descriptor); final SProcessInstanceReadException exception = new SProcessInstanceReadException(cause); assertEquals(descriptor.toString(), exception.getMessage()); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceRepositoryImplForEventTest.java ================================================ /** * 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.core.process.instance.event.impl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.SEventInstanceReadException; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance; import org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.services.PersistenceService; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ @RunWith(MockitoJUnitRunner.class) public class EventInstanceRepositoryImplForEventTest { @Mock private ArchiveService archiveService; @Mock private EventService eventService; @Mock private PersistenceService persistenceService; @Mock private Recorder recorder; @InjectMocks private EventInstanceRepositoryImpl eventInstanceRepository; /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#createEventInstance(org.bonitasoft.engine.core.process.instance.model.event.SEventInstance)} * . */ @Test public final void createEventInstance_should_create_event_instance() throws Exception { // Given final SIntermediateCatchEventInstance eventInstanceImpl = new SIntermediateCatchEventInstance(); final InsertRecord insertRecord = new InsertRecord(eventInstanceImpl); // When eventInstanceRepository.createEventInstance(eventInstanceImpl); // Then verify(recorder).recordInsert(eq(insertRecord), nullable(String.class)); } @Test(expected = SEventInstanceCreationException.class) public final void createEventInstance_should_throw_exception_when_there_is_error() throws Exception { // Given final SStartEventInstance eventInstanceImpl = new SStartEventInstance(); doThrow(new SRecorderException("")).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); // When eventInstanceRepository.createEventInstance(eventInstanceImpl); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#getActivityBoundaryEventInstances(long, int, int)} * . */ @Test public final void getActivityBoundaryEventInstances_should_return_event_instances() throws Exception { // Given final long activityInstanceId = 56L; final int fromIndex = 1; final int maxResults = 6; final List triggerInstanceImpls = Arrays.asList(new SBoundaryEventInstance()); final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getActivityBoundaryEvents(activityInstanceId, fromIndex, maxResults); doReturn(triggerInstanceImpls).when(persistenceService).selectList(selectDescriptor); // When final List result = eventInstanceRepository .getActivityBoundaryEventInstances(activityInstanceId, fromIndex, maxResults); // Then assertEquals("Should return the result of the mock.", triggerInstanceImpls, result); } @Test public final void getActivityBoundaryEventInstances_should_return_empty_list_if_doesnt_exist() throws Exception { // Given final long activityInstanceId = 56L; final int fromIndex = 1; final int maxResults = 6; final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getActivityBoundaryEvents(activityInstanceId, fromIndex, maxResults); doReturn(Arrays.asList()).when(persistenceService).selectList(selectDescriptor); // When final List result = eventInstanceRepository .getActivityBoundaryEventInstances(activityInstanceId, fromIndex, maxResults); // Then assertTrue("The result must be empty.", result.isEmpty()); } @Test(expected = SEventInstanceReadException.class) public final void getActivityBoundaryEventInstances_should_throw_exception_when_there_is_error() throws Exception { // Given final long activityInstanceId = 56L; final int fromIndex = 1; final int maxResults = 6; final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getActivityBoundaryEvents(activityInstanceId, fromIndex, maxResults); doThrow(new SBonitaReadException("")).when(persistenceService).selectList(selectDescriptor); // When eventInstanceRepository.getActivityBoundaryEventInstances(activityInstanceId, fromIndex, maxResults); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#getEventInstances(long, int, int, java.lang.String, org.bonitasoft.engine.persistence.OrderByType)} * . */ @Test public final void getEventInstances_should_return_event_instances() throws Exception { // Given final long rootContainerId = 56L; final int fromIndex = 1; final int maxResults = 6; final String fieldName = "name"; final OrderByType orderByType = OrderByType.ASC; final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getEventsFromRootContainer(rootContainerId, fromIndex, maxResults, fieldName, orderByType); final List eventInstanceImpls = Arrays .asList(new SIntermediateCatchEventInstance()); doReturn(eventInstanceImpls).when(persistenceService).selectList(selectDescriptor); // When final List result = eventInstanceRepository.getEventInstances(rootContainerId, fromIndex, maxResults, fieldName, orderByType); // Then assertEquals("Should return the result of the mock.", eventInstanceImpls, result); } @Test public final void getEventInstances_should_return_empty_list_if_doesnt_exist() throws Exception { // Given final long rootContainerId = 56L; final int fromIndex = 1; final int maxResults = 6; final String fieldName = "name"; final OrderByType orderByType = OrderByType.ASC; final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getEventsFromRootContainer(rootContainerId, fromIndex, maxResults, fieldName, orderByType); doReturn(Arrays.asList()).when(persistenceService).selectList(selectDescriptor); // When final List result = eventInstanceRepository.getEventInstances(rootContainerId, fromIndex, maxResults, fieldName, orderByType); // Then assertTrue("The result must be empty.", result.isEmpty()); } @Test(expected = SEventInstanceReadException.class) public final void getEventInstances_should_throw_exception_when_there_is_error() throws Exception { // Given final long rootContainerId = 56L; final int fromIndex = 1; final int maxResults = 6; final String fieldName = "name"; final OrderByType orderByType = OrderByType.ASC; final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getEventsFromRootContainer(rootContainerId, fromIndex, maxResults, fieldName, orderByType); doThrow(new SBonitaReadException("")).when(persistenceService).selectList(selectDescriptor); // When eventInstanceRepository.getEventInstances(rootContainerId, fromIndex, maxResults, fieldName, orderByType); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceRepositoryImplForEventTriggerTest.java ================================================ /** * 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.core.process.instance.event.impl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.List; import java.util.Map; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceDeletionException; import org.bonitasoft.engine.core.process.instance.model.event.trigger.STimerEventTriggerInstance; import org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.PersistenceService; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ @RunWith(MockitoJUnitRunner.class) public class EventInstanceRepositoryImplForEventTriggerTest { @Mock private ArchiveService archiveService; @Mock private EventService eventService; @Mock private PersistenceService persistenceService; @Mock private Recorder recorder; @Spy @InjectMocks private EventInstanceRepositoryImpl eventInstanceRepository; /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#createTimerEventTriggerInstance(STimerEventTriggerInstance)} * . */ @Test public final void createEventTriggerInstance_should_create_event_trigger_instance() throws Exception { // Given final STimerEventTriggerInstance eventTriggerInstance = new STimerEventTriggerInstance(); final InsertRecord insertRecord = new InsertRecord(eventTriggerInstance); // When eventInstanceRepository.createTimerEventTriggerInstance(eventTriggerInstance); // Then verify(recorder).recordInsert(eq(insertRecord), nullable(String.class)); } @Test(expected = SEventTriggerInstanceCreationException.class) public final void createEventTriggerInstance_should_throw_exception_when_there_is_error() throws Exception { // Given final STimerEventTriggerInstance eventTriggerInstance = new STimerEventTriggerInstance(); doThrow(new SRecorderException("")).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); // When eventInstanceRepository.createTimerEventTriggerInstance(eventTriggerInstance); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#deleteEventTriggerInstance(STimerEventTriggerInstance)} * . */ @Test public final void deleteEventTriggerInstance_should_delete_event_trigger_instance() throws Exception { // Given final STimerEventTriggerInstance eventTriggerInstance = new STimerEventTriggerInstance(); final DeleteRecord insertRecord = new DeleteRecord(eventTriggerInstance); // When eventInstanceRepository.deleteEventTriggerInstance(eventTriggerInstance); // Then verify(recorder).recordDelete(eq(insertRecord), nullable(String.class)); } @Test(expected = SEventTriggerInstanceDeletionException.class) public final void deleteEventTriggerInstance_should_throw_exception_when_there_is_error() throws Exception { // Given final STimerEventTriggerInstance eventTriggerInstance = new STimerEventTriggerInstance(); doThrow(new SRecorderException("")).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); // When eventInstanceRepository.deleteEventTriggerInstance(eventTriggerInstance); } @Test public final void getEventTriggerInstance_should_return_event_trigger_instance() throws Exception { // Given final long eventTriggerInstanceId = 63L; final STimerEventTriggerInstance eventTriggerInstance = new STimerEventTriggerInstance(); final SelectByIdDescriptor selectByIdDescriptor = SelectDescriptorBuilder .getElementById(STimerEventTriggerInstance.class, STimerEventTriggerInstance.class.getSimpleName(), eventTriggerInstanceId); doReturn(eventTriggerInstance).when(persistenceService).selectById(selectByIdDescriptor); // When final STimerEventTriggerInstance result = eventInstanceRepository .getEventTriggerInstance(STimerEventTriggerInstance.class, eventTriggerInstanceId); // Then assertEquals("Should return the result of the mock.", eventTriggerInstance, result); } @Test public final void getEventTriggerInstance_should_return_null_if_doesnt_exist() throws Exception { // Given final long eventTriggerInstanceId = 63L; final SelectByIdDescriptor selectByIdDescriptor = SelectDescriptorBuilder .getElementById(STimerEventTriggerInstance.class, STimerEventTriggerInstance.class.getSimpleName(), eventTriggerInstanceId); doReturn(null).when(persistenceService).selectById(selectByIdDescriptor); // When final STimerEventTriggerInstance result = eventInstanceRepository .getEventTriggerInstance(STimerEventTriggerInstance.class, eventTriggerInstanceId); // Then assertNull("Should return the result of the mock.", result); } @Test public final void getNumberOfEventTriggerInstancesByProcessInstance_should_return_the_number() throws Exception { // Given final int processInstanceId = 2; final QueryOptions queryOptions = new QueryOptions(0, 100, STimerEventTriggerInstance.class, "id", OrderByType.ASC); doReturn(3L).when(persistenceService).getNumberOfEntities(eq(STimerEventTriggerInstance.class), eq("ByProcessInstance"), eq(queryOptions), ArgumentMatchers.> any()); // When final long result = eventInstanceRepository.getNumberOfTimerEventTriggerInstances(processInstanceId, queryOptions); // Then assertEquals("Should be equals to the result of the mock.", 3L, result); } @Test(expected = SBonitaReadException.class) public final void getNumberOfEventTriggerInstancesByProcessInstance_should_throw_exception_when_there_is_error() throws Exception { // Given final int processInstanceId = 2; final QueryOptions queryOptions = new QueryOptions(0, 100, STimerEventTriggerInstance.class, "id", OrderByType.ASC); doThrow(new SBonitaReadException("")).when(persistenceService).getNumberOfEntities( eq(STimerEventTriggerInstance.class), eq("ByProcessInstance"), eq(queryOptions), any()); // When eventInstanceRepository.getNumberOfTimerEventTriggerInstances(processInstanceId, queryOptions); } @Test public final void searchEventTriggerInstancesByProcessInstance_should_return_the_list() throws Exception { // Given final int processInstanceId = 2; final QueryOptions queryOptions = new QueryOptions(0, 100, STimerEventTriggerInstance.class, "id", OrderByType.ASC); final List triggerInstanceImpls = Arrays.asList(new STimerEventTriggerInstance()); doReturn(triggerInstanceImpls).when(persistenceService).searchEntity(eq(STimerEventTriggerInstance.class), eq("ByProcessInstance"), eq(queryOptions), any()); // When final List result = eventInstanceRepository .searchTimerEventTriggerInstances(processInstanceId, queryOptions); // Then assertEquals("Should be equals to the result of the mock.", triggerInstanceImpls, result); } @Test(expected = SBonitaReadException.class) public final void searchEventTriggerInstancesByProcessInstance_should_throw_exception_when_there_is_error() throws Exception { // Given final int processInstanceId = 2; final QueryOptions queryOptions = new QueryOptions(0, 100, STimerEventTriggerInstance.class, "id", OrderByType.ASC); doThrow(new SBonitaReadException("")).when(persistenceService).searchEntity( eq(STimerEventTriggerInstance.class), eq("ByProcessInstance"), eq(queryOptions), any()); // When eventInstanceRepository.searchTimerEventTriggerInstances(processInstanceId, queryOptions); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#updateEventTriggerInstance(STimerEventTriggerInstance, EntityUpdateDescriptor)} * . */ @Test public final void updateEventTriggerInstance_should_update_timer_event_trigger_instance() throws Exception { // Given final STimerEventTriggerInstance sTimerEventTriggerInstance = new STimerEventTriggerInstance(); final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); final UpdateRecord updateRecord = UpdateRecord.buildSetFields(sTimerEventTriggerInstance, descriptor); // When eventInstanceRepository.updateEventTriggerInstance(sTimerEventTriggerInstance, descriptor); // Then verify(recorder).recordUpdate(eq(updateRecord), nullable(String.class)); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceRepositoryImplForMessageTest.java ================================================ /** * 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.core.process.instance.event.impl; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Random; import java.util.stream.Collectors; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageModificationException; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageEventCouple; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance; import org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.*; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.SPersistenceException; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ @RunWith(MockitoJUnitRunner.class) public class EventInstanceRepositoryImplForMessageTest { @Mock private ArchiveService archiveService; @Mock private EventService eventService; @Mock private PersistenceService persistenceService; @Mock private Recorder recorder; @InjectMocks private EventInstanceRepositoryImpl eventInstanceRepository; /** * Test method for * {@link EventInstanceRepository#createMessageInstance(org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance)} * . */ @Test public final void createMessageInstance_should_create_message_instance() throws Exception { // Given final SMessageInstance sMessageInstance = new SMessageInstance(); final InsertRecord insertRecord = new InsertRecord(sMessageInstance); // When eventInstanceRepository.createMessageInstance(sMessageInstance); // Then verify(recorder).recordInsert(eq(insertRecord), nullable(String.class)); } @Test(expected = SMessageInstanceCreationException.class) public final void createMessageInstance_should_throw_exception_when_there_is_error() throws Exception { // Given final SMessageInstance sMessageInstance = new SMessageInstance(); doThrow(new SRecorderException("")).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); // When eventInstanceRepository.createMessageInstance(sMessageInstance); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#deleteMessageInstance(org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance)} * . */ @Test public final void deleteMessageInstance_should_delete_message_instance() throws Exception { // Given final SMessageInstance sMessageInstance = new SMessageInstance(); final DeleteRecord insertRecord = new DeleteRecord(sMessageInstance); // When eventInstanceRepository.deleteMessageInstance(sMessageInstance); // Then verify(recorder).recordDelete(eq(insertRecord), nullable(String.class)); } @Test(expected = SMessageModificationException.class) public final void deleteMessageInstance_should_throw_exception_when_there_is_error() throws Exception { // Given final SMessageInstance sMessageInstance = new SMessageInstance(); doThrow(new SRecorderException("")).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); // When eventInstanceRepository.deleteMessageInstance(sMessageInstance); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#resetProgressMessageInstances()}. */ @Test public final void resetProgressMessageInstances_should_update_message() throws Exception { // Given doReturn(3).when(persistenceService).update("resetProgressMessageInstances"); // When final int result = eventInstanceRepository.resetProgressMessageInstances(); // Then assertEquals("Should return the result of the mock.", 3, result); } @Test(expected = SMessageModificationException.class) public final void resetProgressMessageInstances_should_throw_exception_when_there_is_error() throws Exception { // Given doThrow(new SPersistenceException("")).when(persistenceService).update("resetProgressMessageInstances"); // When eventInstanceRepository.resetProgressMessageInstances(); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#getMessageEventCouples(int, int)}. */ @Test public final void getMessageEventCouples_should_return_message_event_couples() throws Exception { // Given final List sMessageEventCoupleImpls = Arrays.asList(new SMessageEventCouple()); final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getMessageEventCouples(0, 100); doReturn(sMessageEventCoupleImpls).when(persistenceService).selectList(selectDescriptor); // When final List result = eventInstanceRepository.getMessageEventCouples(0, 100); // Then assertEquals("Should return the result of the mock.", sMessageEventCoupleImpls, result); } @Test public final void getMessageEventCouples_should_return_empty_list_if_doesnt_exist() throws Exception { // Given final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getMessageEventCouples(0, 100); doReturn(Arrays.asList()).when(persistenceService).selectList(selectDescriptor); // When final List result = eventInstanceRepository.getMessageEventCouples(0, 100); // Then assertTrue("The result must be empty.", result.isEmpty()); } @Test(expected = SEventTriggerInstanceReadException.class) public final void getMessageEventCouples_should_throw_exception_when_there_is_error() throws Exception { // Given final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getMessageEventCouples(0, 100); doThrow(new SBonitaReadException("")).when(persistenceService).selectList(selectDescriptor); // When eventInstanceRepository.getMessageEventCouples(0, 100); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#getMessageInstance(long)}. */ @Test public final void getMessageInstance_should_return_message_instance() throws Exception { // Given final long messageInstanceId = 63L; final SMessageInstance sMessageInstance = new SMessageInstance(); final SelectByIdDescriptor selectByIdDescriptor = SelectDescriptorBuilder.getElementById( SMessageInstance.class, "MessageInstance", messageInstanceId); doReturn(sMessageInstance).when(persistenceService).selectById(selectByIdDescriptor); // When final SMessageInstance result = eventInstanceRepository.getMessageInstance(messageInstanceId); // Then assertEquals("Should return the result of the mock.", sMessageInstance, result); } @Test public final void deleteMessage_should_process_ids_in_batch() throws Exception { // Given List ids = new Random().longs(0, 350).limit(350).boxed().collect(Collectors.toList()); // When eventInstanceRepository.deleteMessageInstanceByIds(ids); // Then verify(persistenceService, times(4)).update(anyString(), anyMap()); } @Test public final void getMessageInstance_should_return_null_if_doesnt_exist() throws Exception { // Given final long messageInstanceId = 63L; final SelectByIdDescriptor selectByIdDescriptor = SelectDescriptorBuilder.getElementById( SMessageInstance.class, "MessageInstance", messageInstanceId); doReturn(null).when(persistenceService).selectById(selectByIdDescriptor); // When final SMessageInstance result = eventInstanceRepository.getMessageInstance(messageInstanceId); // Then assertNull("Should return the result of the mock.", result); } @Test(expected = SMessageInstanceReadException.class) public final void getMessageInstance_should_throw_exception_when_there_is_error() throws Exception { // Given final long messageInstanceId = 63L; final SelectByIdDescriptor selectByIdDescriptor = SelectDescriptorBuilder.getElementById( SMessageInstance.class, "MessageInstance", messageInstanceId); doThrow(new SBonitaReadException("")).when(persistenceService).selectById(selectByIdDescriptor); // When eventInstanceRepository.getMessageInstance(messageInstanceId); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#updateMessageInstance(org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance, org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor)} * . */ @Test public final void updateMessageInstance_should_update_message_instance() throws Exception { // Given final SMessageInstance sMessageInstance = new SMessageInstance(); final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); final UpdateRecord updateRecord = UpdateRecord.buildSetFields(sMessageInstance, descriptor); // When eventInstanceRepository.updateMessageInstance(sMessageInstance, descriptor); // Then verify(recorder).recordUpdate(eq(updateRecord), nullable(String.class)); } @Test(expected = SMessageModificationException.class) public final void updateMessageInstance_should_throw_exception_when_there_is_error() throws Exception { // Given final SMessageInstance sMessageInstance = new SMessageInstance(); final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); doThrow(new SRecorderException("")).when(recorder).recordUpdate(any(UpdateRecord.class), nullable(String.class)); // When eventInstanceRepository.updateMessageInstance(sMessageInstance, descriptor); } @Test(expected = SMessageModificationException.class) public final void deleteMessageInstanceByIds_throw_exception_when_there_is_error() throws Exception { // Given doThrow(new SPersistenceException("")).when(persistenceService).update(anyString(), anyMap()); // When eventInstanceRepository.deleteMessageInstanceByIds(Collections.singletonList(50L)); } @Test(expected = SMessageInstanceReadException.class) public final void getMessageInstanceIdOlderThanCreationDateInstanceByIds_throw_exception_when_there_is_error() throws Exception { // Given doThrow(new SBonitaReadException("")).when(persistenceService).selectList(any(SelectListDescriptor.class)); // When eventInstanceRepository.getMessageInstanceIdOlderThanCreationDate(14500L, new QueryOptions(0, 100)); } @Test(expected = IllegalArgumentException.class) public final void should_throw_exception_when_query_option_have_bad_filters() throws Exception { eventInstanceRepository.getMessageInstanceIdOlderThanCreationDate(14500L, new QueryOptions( Collections.singletonList(new FilterOption(SMessageInstance.class, "toto", "testt")), null)); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceRepositoryImplForWaitingTest.java ================================================ /** * 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.core.process.instance.event.impl; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventReadException; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent; import org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.*; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.SPersistenceException; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ @RunWith(MockitoJUnitRunner.class) public class EventInstanceRepositoryImplForWaitingTest { @Mock private ArchiveService archiveService; @Mock private EventService eventService; @Mock private PersistenceService persistenceService; @Mock private Recorder recorder; @Spy @InjectMocks private EventInstanceRepositoryImpl eventInstanceRepository; /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#createWaitingEvent(org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent)} * . */ @Test public final void createWaitingEvent_should_create_waiting_instance() throws Exception { // Given final SWaitingSignalEvent sWaitingEventImpl = new SWaitingSignalEvent(); final InsertRecord insertRecord = new InsertRecord(sWaitingEventImpl); // When eventInstanceRepository.createWaitingEvent(sWaitingEventImpl); // Then verify(recorder).recordInsert(eq(insertRecord), nullable(String.class)); } @Test(expected = SWaitingEventCreationException.class) public final void createWaitingEvent_should_throw_exception_when_there_is_error() throws Exception { // Given final SWaitingMessageEvent sWaitingEventImpl = new SWaitingMessageEvent(); doThrow(new SRecorderException("")).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); // When eventInstanceRepository.createWaitingEvent(sWaitingEventImpl); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#deleteWaitingEvent(org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent)} * . */ @Test public final void deleteWaitingEvent_should_delete_waiting_event() throws Exception { // Given final SWaitingMessageEvent sWaitingEventImpl = new SWaitingMessageEvent(); final DeleteRecord insertRecord = new DeleteRecord(sWaitingEventImpl); // When eventInstanceRepository.deleteWaitingEvent(sWaitingEventImpl); // Then verify(recorder).recordDelete(eq(insertRecord), nullable(String.class)); } @Test(expected = SWaitingEventModificationException.class) public final void deleteWaitingEvent_should_throw_exception_when_there_is_error() throws Exception { // Given final SWaitingSignalEvent sWaitingEventImpl = new SWaitingSignalEvent(); doThrow(new SRecorderException("")).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); // When eventInstanceRepository.deleteWaitingEvent(sWaitingEventImpl); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#resetInProgressWaitingEvents()}. */ @Test public final void resetInProgressWaitingEvents_should_update_waiting_events() throws Exception { // Given doReturn(3).when(persistenceService).update("resetInProgressWaitingEvents"); // When final int result = eventInstanceRepository.resetInProgressWaitingEvents(); // Then assertEquals("Should return the result of the mock.", 3, result); } @Test(expected = SWaitingEventModificationException.class) public final void resetInProgressWaitingEvents_should_throw_exception_when_there_is_error() throws Exception { // Given doThrow(new SPersistenceException("")).when(persistenceService).update("resetInProgressWaitingEvents"); // When eventInstanceRepository.resetInProgressWaitingEvents(); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#getNumberOfWaitingEvents(java.lang.Class, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public final void getNumberOfWaitingEvents_should_return_the_number() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 100, SWaitingEvent.class, "id", OrderByType.ASC); doReturn(2L).when(persistenceService).getNumberOfEntities(SWaitingEvent.class, queryOptions, null); // When final long result = eventInstanceRepository.getNumberOfWaitingEvents(SWaitingEvent.class, queryOptions); // Then assertEquals("Should be equals to the result of the mock.", 2L, result); } @Test(expected = SBonitaReadException.class) public final void getNumberOfWaitingEvents_should_throw_exception_when_there_is_error() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 100, SWaitingEvent.class, "id", OrderByType.ASC); doThrow(new SBonitaReadException("")).when(persistenceService).getNumberOfEntities(SWaitingEvent.class, queryOptions, null); // When eventInstanceRepository.getNumberOfWaitingEvents(SWaitingEvent.class, queryOptions); } /** * Test method for * {@link EventInstanceRepository#getStartWaitingEventsOfProcessDefinition(long)} * . */ @Test public final void searchStartWaitingEvents_should_return_the_list() throws Exception { // Given final List sWaitingSignalEvents = Arrays.asList(new SWaitingSignalEvent()); // event sub-processes are not returned: doReturn(sWaitingSignalEvents).when(persistenceService).selectList(any()); // When final List result = eventInstanceRepository .getStartWaitingEventsOfProcessDefinition(9L); // Then assertEquals("Should be equals to the result of the mock.", sWaitingSignalEvents, result); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#getWaitingMessage(long)}. */ @Test public final void getWaitingMessage_should_return_waiting_message() throws Exception { // Given final long waitingMessageId = 63L; final SWaitingMessageEvent sWaitingMessageEvent = new SWaitingMessageEvent(); final SelectByIdDescriptor selectByIdDescriptor = SelectDescriptorBuilder.getElementById( SWaitingMessageEvent.class, "WaitingMessageEvent", waitingMessageId); doReturn(sWaitingMessageEvent).when(persistenceService).selectById(selectByIdDescriptor); // When final SWaitingMessageEvent result = eventInstanceRepository.getWaitingMessage(waitingMessageId); // Then assertEquals("Should return the result of the mock.", sWaitingMessageEvent, result); } @Test public final void getWaitingMessage_should_return_null_if_doesnt_exist() throws Exception { // Given final long waitingMessageId = 63L; final SelectByIdDescriptor selectByIdDescriptor = SelectDescriptorBuilder.getElementById( SWaitingMessageEvent.class, "WaitingMessageEvent", waitingMessageId); doReturn(null).when(persistenceService).selectById(selectByIdDescriptor); // When final SWaitingMessageEvent result = eventInstanceRepository.getWaitingMessage(waitingMessageId); // Then assertNull("Should return the result of the mock.", result); } @Test(expected = SWaitingEventReadException.class) public final void getWaitingMessage_should_throw_exception_when_there_is_error() throws Exception { // Given final long waitingMessageId = 63L; final SelectByIdDescriptor selectByIdDescriptor = SelectDescriptorBuilder.getElementById( SWaitingMessageEvent.class, "WaitingMessageEvent", waitingMessageId); doThrow(new SBonitaReadException("")).when(persistenceService).selectById(selectByIdDescriptor); // When eventInstanceRepository.getWaitingMessage(waitingMessageId); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#getWaitingSignalEvents(java.lang.String, int, int)}. */ @Test public final void getWaitingSignalEvents_should_return_waiting_signal_event() throws Exception { // Given final String signalName = "name"; final List waitingSignalEventImpls = Arrays.asList(new SWaitingSignalEvent()); final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getListeningSignals(signalName, 0, 100); doReturn(waitingSignalEventImpls).when(persistenceService).selectList(selectDescriptor); // When final List result = eventInstanceRepository.getWaitingSignalEvents(signalName, 0, 100); // Then assertEquals("Should return the result of the mock.", waitingSignalEventImpls, result); } @Test public final void getWaitingSignalEvents_should_return_empty_list_if_doesnt_exist() throws Exception { // Given final String signalName = "name"; final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getListeningSignals(signalName, 0, 100); doReturn(Arrays.asList()).when(persistenceService).selectList(selectDescriptor); // When final List result = eventInstanceRepository.getWaitingSignalEvents(signalName, 0, 100); // Then assertTrue("The result must be empty.", result.isEmpty()); } @Test(expected = SEventTriggerInstanceReadException.class) public final void getEventTriggerInstances_should_throw_exception_when_there_is_error() throws Exception { // Given final String signalName = "name"; final SelectListDescriptor selectDescriptor = SelectDescriptorBuilder .getListeningSignals(signalName, 0, 100); doThrow(new SBonitaReadException("")).when(persistenceService).selectList(selectDescriptor); // When eventInstanceRepository.getWaitingSignalEvents(signalName, 0, 100); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#searchWaitingEvents(java.lang.Class, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public final void searchWaitingEvents_should_return_the_list() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 100, SWaitingEvent.class, "id", OrderByType.ASC); final List sWaitingSignalEvents = Arrays.asList(new SWaitingSignalEvent()); doReturn(sWaitingSignalEvents).when(persistenceService).searchEntity(SWaitingEvent.class, queryOptions, null); // When final List result = eventInstanceRepository.searchWaitingEvents(SWaitingEvent.class, queryOptions); // Then assertEquals("Should be equals to the result of the mock.", sWaitingSignalEvents, result); } @Test(expected = SBonitaReadException.class) public final void searchWaitingEvents_should_throw_exception_when_there_is_error() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 100, SWaitingEvent.class, "id", OrderByType.ASC); doThrow(new SBonitaReadException("")).when(persistenceService).searchEntity(SWaitingEvent.class, queryOptions, null); // When eventInstanceRepository.searchWaitingEvents(SWaitingEvent.class, queryOptions); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceRepositoryImpl#updateWaitingMessage(org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent, org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor)} * . */ @Test public final void updateWaitingMessage_should_update_waiting_message() throws Exception { // Given final SWaitingMessageEvent waitingMessageEventImpl = new SWaitingMessageEvent(); final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); final UpdateRecord updateRecord = UpdateRecord.buildSetFields(waitingMessageEventImpl, descriptor); // When eventInstanceRepository.updateWaitingMessage(waitingMessageEventImpl, descriptor); // Then verify(recorder).recordUpdate(eq(updateRecord), nullable(String.class)); } @Test(expected = SWaitingEventModificationException.class) public final void updateWaitingMessage_should_throw_exception_when_there_is_error() throws Exception { // Given final SWaitingMessageEvent waitingMessageEventImpl = new SWaitingMessageEvent(); final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); doThrow(new SRecorderException("")).when(recorder).recordUpdate(any(UpdateRecord.class), nullable(String.class)); // When eventInstanceRepository.updateWaitingMessage(waitingMessageEventImpl, descriptor); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceServiceImplForWaitingTest.java ================================================ /** * 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.core.process.instance.event.impl; import static org.mockito.Mockito.*; import java.util.Collections; import io.micrometer.core.instrument.MeterRegistry; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SEventTriggerInstanceReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventModificationException; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingMessageEvent; import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingSignalEvent; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class EventInstanceServiceImplForWaitingTest { @Mock private EventInstanceRepository instanceRepository; @Mock private MeterRegistry meterRegistry; @Mock private DataInstanceService dataInstanceService; private EventInstanceServiceImpl eventInstanceServiceImpl; @Before public void setUp() { eventInstanceServiceImpl = spy( new EventInstanceServiceImpl(instanceRepository, dataInstanceService, meterRegistry)); } @Test public final void deleteWaitingEvents_should_delete_waiting_events() throws Exception { // Given final SIntermediateCatchEventInstance sIntermediateCatchEventInstance = new SIntermediateCatchEventInstance(); final SWaitingMessageEvent waitingMessageEventImpl = new SWaitingMessageEvent(); doReturn(Collections.singletonList(waitingMessageEventImpl)).doReturn(Collections.emptyList()) .when(instanceRepository) .getWaitingEventsForFlowNodeId(anyLong()); doNothing().when(eventInstanceServiceImpl).deleteWaitingEvent(waitingMessageEventImpl); // When eventInstanceServiceImpl.deleteWaitingEvents(sIntermediateCatchEventInstance); // Then verify(eventInstanceServiceImpl).deleteWaitingEvent(waitingMessageEventImpl); } @Test(expected = SWaitingEventModificationException.class) public final void deleteWaitingEvents_should_throw_exception_when_cant_delete_waiting_event() throws Exception { // Given final SIntermediateCatchEventInstance sIntermediateCatchEventInstance = new SIntermediateCatchEventInstance(); final SWaitingSignalEvent waitingMessageEventImpl = new SWaitingSignalEvent(); doReturn(Collections.singletonList(waitingMessageEventImpl)).doReturn(Collections.emptyList()) .when(instanceRepository) .getWaitingEventsForFlowNodeId(anyLong()); doThrow(new SWaitingEventModificationException(new Exception(""))).when(instanceRepository) .deleteWaitingEvent(waitingMessageEventImpl); // When eventInstanceServiceImpl.deleteWaitingEvents(sIntermediateCatchEventInstance); } @Test(expected = SEventTriggerInstanceReadException.class) public final void deleteWaitingEvents_should_throw_exception_when_cant_search_waiting_event() throws Exception { // Given final SIntermediateCatchEventInstance sIntermediateCatchEventInstance = new SIntermediateCatchEventInstance(); doThrow(new SEventTriggerInstanceReadException(new Exception(""))).when(instanceRepository) .getWaitingEventsForFlowNodeId(anyLong()); // When eventInstanceServiceImpl.deleteWaitingEvents(sIntermediateCatchEventInstance); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/event/impl/EventInstanceServiceImplTest.java ================================================ /** * 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.core.process.instance.event.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.core.process.instance.event.impl.EventInstanceServiceImpl.BONITA_BPMENGINE_MESSAGE_SENT; import static org.bonitasoft.engine.data.instance.api.DataInstanceContainer.MESSAGE_INSTANCE; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import java.time.Duration; import java.time.Instant; import java.util.Arrays; import java.util.List; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceRepository; import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SMessageInstanceCreationException; import org.bonitasoft.engine.core.process.instance.model.event.handling.SMessageInstance; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.persistence.QueryOptions; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class EventInstanceServiceImplTest { @Mock private EventInstanceRepository instanceRepository; @Mock private DataInstanceService dataInstanceService; private EventInstanceServiceImpl eventInstanceServiceImpl; private final MeterRegistry meterRegistry = new SimpleMeterRegistry( // So that micrometer updates its counters every 1 ms: k -> k.equals("simple.step") ? Duration.ofMillis(1).toString() : null, Clock.SYSTEM); @Before public void setUp() { eventInstanceServiceImpl = new EventInstanceServiceImpl(instanceRepository, dataInstanceService, meterRegistry); } @Test public final void deleteMessageAndDataInstanceOlderCreationDate_should_call_expected_method_and_return_nbMessage_deleted() throws Exception { // Given long creationDate = Instant.now().toEpochMilli(); List idsToBeDeleted = Arrays.asList(1L, 2L); when(instanceRepository.getMessageInstanceIdOlderThanCreationDate(eq(creationDate), any())) .thenReturn(idsToBeDeleted); Integer totalRemoved = eventInstanceServiceImpl.deleteMessageAndDataInstanceOlderThanCreationDate(creationDate, QueryOptions.countQueryOptions()); assertThat(totalRemoved).isEqualTo(idsToBeDeleted.size()); verify(instanceRepository).deleteMessageInstanceByIds(idsToBeDeleted); verify(dataInstanceService).deleteLocalDataInstances(1L, MESSAGE_INSTANCE.name(), true); verify(dataInstanceService).deleteLocalDataInstances(2L, MESSAGE_INSTANCE.name(), true); verify(dataInstanceService).deleteLocalArchivedDataInstances(1L, MESSAGE_INSTANCE.name()); verify(dataInstanceService).deleteLocalArchivedDataInstances(2L, MESSAGE_INSTANCE.name()); } @Test public final void should_count_message_thrown() throws SMessageInstanceCreationException { eventInstanceServiceImpl.createMessageInstance(new SMessageInstance()); eventInstanceServiceImpl.createMessageInstance(new SMessageInstance()); assertThat(meterRegistry.find(BONITA_BPMENGINE_MESSAGE_SENT).counter().count()).isEqualTo(2); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/impl/ActivityInstanceServiceImplTest.java ================================================ /** * 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.core.process.instance.impl; import static org.assertj.core.api.Assertions.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SActivityReadException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeModificationException; import org.bonitasoft.engine.core.process.instance.model.SActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstanceStateCounter; import org.bonitasoft.engine.core.process.instance.model.SHumanTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; import org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilderFactory; import org.bonitasoft.engine.core.process.instance.model.builder.impl.SUserTaskInstanceBuilderFactoryImpl; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.PersistenceService; 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.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ActivityInstanceServiceImplTest { final static String DISPLAY_NAME_255 = "123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345123456789 123456789 123456789 123456789 123456789 123456789 123456789 12345000000000000000000000000000000"; @Rule public ExpectedException expectedException = ExpectedException.none(); @Mock private PersistenceService persistenceService; @Mock private Recorder recorder; @InjectMocks private ActivityInstanceServiceImpl activityInstanceServiceImpl; private final SFlowNodeInstanceBuilderFactory keyProvider = new SUserTaskInstanceBuilderFactoryImpl(); @Mock private SFlowNodeInstance sFlowNodeInstance; @Test public void getPossibleUserIdsOfPendingTasks() throws Exception { final List sUserIds = new ArrayList(); Collections.addAll(sUserIds, 78l, 2l, 5l, 486l); when(persistenceService.selectList(ArgumentMatchers.> any())).thenReturn(sUserIds); final List userIds = activityInstanceServiceImpl.getPossibleUserIdsOfPendingTasks(2, 0, 10); assertEquals(sUserIds, userIds); } @Test(expected = SActivityReadException.class) public void throwExceptionwhenGettingPossibleUserIdsOfPendingTasksDueToPersistenceException() throws Exception { when(persistenceService.selectList(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("database out")); activityInstanceServiceImpl.getPossibleUserIdsOfPendingTasks(2, 0, 10); } @Test public void getEmptyPossibleUserIdsOfPendingTasks() throws Exception { when(persistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(Collections. emptyList()); final List userIds = activityInstanceServiceImpl.getPossibleUserIdsOfPendingTasks(2, 0, 10); assertEquals(Collections.emptyList(), userIds); } @Test public void updateDisplayName_should_truncate_when_display_name_is_bigger_than_75_characters() throws Exception { // given final String displayName = DISPLAY_NAME_255 + "__"; final SFlowNodeInstance flowNode = mock(SFlowNodeInstance.class); // when activityInstanceServiceImpl.updateDisplayName(flowNode, displayName); // then // the value must be truncated to 75 characters checkFlowNodeUpdate(keyProvider.getDisplayNameKey(), DISPLAY_NAME_255); } private void checkFlowNodeUpdate(final String attributeKey, final String expectedValue) throws SRecorderException { final ArgumentCaptor recordCaptor = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder, times(1)).recordUpdate(recordCaptor.capture(), anyString()); final Object actualValue = recordCaptor.getValue().getFields().get(attributeKey); assertThat(actualValue).isEqualTo(expectedValue); } @Test public void updateDisplayName_should_not_change_value_when_display_name_is_lower_than_255_characters() throws Exception { // given final String displayName = "simple task"; final SFlowNodeInstance flowNode = mock(SFlowNodeInstance.class); // when activityInstanceServiceImpl.updateDisplayName(flowNode, displayName); // then // keep original value checkFlowNodeUpdate(keyProvider.getDisplayNameKey(), displayName); } @Test public void updateDisplayName_should_not_change_value_when_display_name_is_255_characters_long() throws Exception { // given final SFlowNodeInstance flowNode = mock(SFlowNodeInstance.class); // when activityInstanceServiceImpl.updateDisplayName(flowNode, DISPLAY_NAME_255); // then // keep original value checkFlowNodeUpdate(keyProvider.getDisplayNameKey(), DISPLAY_NAME_255); } @Test public void updateDisplayDescription_should_truncate_when_display_description_is_bigger_than_255_characters() throws Exception { // given final String displayDescr255 = "123456789 123456789 123456789 123456789 123456789 " + "123456789 123456789 123456789 123456789 123456789 " + "123456789 123456789 123456789 123456789 123456789 " + "123456789 123456789 123456789 123456789 123456789 " + "123456789 123456789 123456789 123456789 123456789 12345"; final String displayDescr = displayDescr255 + displayDescr255; final SFlowNodeInstance flowNode = mock(SFlowNodeInstance.class); // when activityInstanceServiceImpl.updateDisplayDescription(flowNode, displayDescr); // then // the value must be truncated to 255 characters checkFlowNodeUpdate(keyProvider.getDisplayDescriptionKey(), displayDescr255); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.impl.ActivityInstanceServiceImpl#searchAssignedAndPendingHumanTasksFor(long, long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void searchAssignedAndPendingHumanTasksFor() throws Exception { // Given final long rootProcessDefinitionId = 10; final long userId = 6; final QueryOptions options = new QueryOptions(0, 10); final Map parameters = new HashMap(); parameters.put("userId", userId); parameters.put("rootProcessDefinitionId", rootProcessDefinitionId); when(persistenceService.searchEntity(SHumanTaskInstance.class, "AssignedAndPendingByRootProcessFor", options, parameters)).thenReturn( new ArrayList()); // When final List result = activityInstanceServiceImpl .searchAssignedAndPendingHumanTasksFor(rootProcessDefinitionId, userId, options); // Then assertNotNull(result); } @Test(expected = SBonitaReadException.class) public void searchAssignedAndPendingHumanTasksForThrowException() throws Exception { // Given final long rootProcessDefinitionId = 10; final long userId = 6; final QueryOptions options = new QueryOptions(0, 10); final Map parameters = new HashMap(); parameters.put("userId", userId); parameters.put("rootProcessDefinitionId", rootProcessDefinitionId); when(persistenceService.searchEntity(SHumanTaskInstance.class, "AssignedAndPendingByRootProcessFor", options, parameters)).thenThrow( new SBonitaReadException("")); // When activityInstanceServiceImpl.searchAssignedAndPendingHumanTasksFor(rootProcessDefinitionId, userId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.impl.ActivityInstanceServiceImpl#getNumberOfAssignedAndPendingHumanTasksFor(long, long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getNumberOfAssignedAndPendingHumanTasksFor() throws Exception { // Given final long rootProcessDefinitionId = 10; final long userId = 6; final QueryOptions options = new QueryOptions(0, 10); final Map parameters = new HashMap(); parameters.put("userId", userId); parameters.put("rootProcessDefinitionId", rootProcessDefinitionId); when(persistenceService.getNumberOfEntities(SHumanTaskInstance.class, "AssignedAndPendingByRootProcessFor", options, parameters)).thenReturn(1L); // When final long result = activityInstanceServiceImpl .getNumberOfAssignedAndPendingHumanTasksFor(rootProcessDefinitionId, userId, options); // Then assertEquals(1L, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfAssignedAndPendingHumanTasksForThrowException() throws Exception { // Given final long rootProcessDefinitionId = 10; final long userId = 6; final QueryOptions options = new QueryOptions(0, 10); final Map parameters = new HashMap(); parameters.put("userId", userId); parameters.put("rootProcessDefinitionId", rootProcessDefinitionId); when(persistenceService.getNumberOfEntities(SHumanTaskInstance.class, "AssignedAndPendingByRootProcessFor", options, parameters)).thenThrow( new SBonitaReadException("")); // When activityInstanceServiceImpl.getNumberOfAssignedAndPendingHumanTasksFor(rootProcessDefinitionId, userId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.impl.ActivityInstanceServiceImpl#searchAssignedAndPendingHumanTasks(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void searchAssignedAndPendingHumanTasks() throws Exception { // Given final long rootProcessDefinitionId = 10; final QueryOptions options = new QueryOptions(0, 10); final Map parameters = Collections.singletonMap("rootProcessDefinitionId", (Object) rootProcessDefinitionId); when(persistenceService.searchEntity(SHumanTaskInstance.class, "AssignedAndPendingByRootProcess", options, parameters)).thenReturn( new ArrayList()); // When final List result = activityInstanceServiceImpl .searchAssignedAndPendingHumanTasks(rootProcessDefinitionId, options); // Then assertNotNull(result); } @Test(expected = SBonitaReadException.class) public void searchAssignedAndPendingHumanTasksThrowException() throws Exception { // Given final long rootProcessDefinitionId = 10; final QueryOptions options = new QueryOptions(0, 10); final Map parameters = Collections.singletonMap("rootProcessDefinitionId", (Object) rootProcessDefinitionId); when(persistenceService.searchEntity(SHumanTaskInstance.class, "AssignedAndPendingByRootProcess", options, parameters)).thenThrow( new SBonitaReadException("")); // When activityInstanceServiceImpl.searchAssignedAndPendingHumanTasks(rootProcessDefinitionId, options); } /** * Test method for * {@link org.bonitasoft.engine.core.process.instance.impl.ActivityInstanceServiceImpl#getNumberOfAssignedAndPendingHumanTasks(long, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public void getNumberOfAssignedAndPendingHumanTasks() throws Exception { // Given final long rootProcessDefinitionId = 10; final QueryOptions options = new QueryOptions(0, 10); final Map parameters = Collections.singletonMap("rootProcessDefinitionId", (Object) rootProcessDefinitionId); when(persistenceService.getNumberOfEntities(SHumanTaskInstance.class, "AssignedAndPendingByRootProcess", options, parameters)).thenReturn(1L); // When final long result = activityInstanceServiceImpl.getNumberOfAssignedAndPendingHumanTasks(rootProcessDefinitionId, options); // Then assertEquals(1L, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfAssignedAndPendingHumanTasksThrowException() throws Exception { // Given final long rootProcessDefinitionId = 10; final QueryOptions options = new QueryOptions(0, 10); final Map parameters = Collections.singletonMap("rootProcessDefinitionId", (Object) rootProcessDefinitionId); when(persistenceService.getNumberOfEntities(SHumanTaskInstance.class, "AssignedAndPendingByRootProcess", options, parameters)).thenThrow( new SBonitaReadException("")); // When activityInstanceServiceImpl.getNumberOfAssignedAndPendingHumanTasks(rootProcessDefinitionId, options); } @Test public void getNumberOfFlownodesOfProcessDefinitionInAllStates_should_return_empty_collections_if_no_results() throws SBonitaReadException { when(persistenceService.selectList(ArgumentMatchers.>> any())) .thenReturn( Collections.> emptyList()); final List numberOfFlownodesInState = activityInstanceServiceImpl .getNumberOfFlownodesOfProcessDefinitionInAllStates(1L); assertThat(numberOfFlownodesInState).isEmpty(); } @Test public void getNumberOfFlownodesInAllStates_should_return_empty_collections_if_no_results() throws SBonitaReadException { when(persistenceService.selectList(ArgumentMatchers.>> any())) .thenReturn( Collections.> emptyList()); final List numberOfFlownodesInState = activityInstanceServiceImpl .getNumberOfFlownodesInAllStates(2L); assertThat(numberOfFlownodesInState).isEmpty(); } @Test public void getNumberOfArchivedFlownodesInAllStates_should_return_empty_collections_if_no_results() throws SBonitaReadException { when(persistenceService.selectList(ArgumentMatchers.>> any())) .thenReturn( Collections.> emptyList()); final List numberOfFlownodesInState = activityInstanceServiceImpl .getNumberOfArchivedFlownodesInAllStates(2L); assertThat(numberOfFlownodesInState).isEmpty(); } @Test public void should_update_due_date_on_right_flownode_with_right_update_event_message() throws Exception { //when activityInstanceServiceImpl.setExpectedEndDate(sFlowNodeInstance, 123L); ArgumentCaptor updateRecordArgumentCaptor = ArgumentCaptor.forClass(UpdateRecord.class); //then verify(recorder).recordUpdate(updateRecordArgumentCaptor.capture(), eq(FlowNodeInstanceService.EXPECTED_END_DATE_MODIFIED)); assertThat(updateRecordArgumentCaptor.getValue().getEntity()).as("should update entity") .isEqualTo(sFlowNodeInstance); assertThat(updateRecordArgumentCaptor.getValue().getFields()) .as("should update expectedEndDate and lastUpdateDate fields") .containsKey("expectedEndDate") .containsKey("lastUpdateDate"); assertThat(updateRecordArgumentCaptor.getValue().getFields().get("expectedEndDate")) .as("expectedEndDate should have the new value") .isEqualTo(123L); } @Test public void should_updateExpectedEndDate_throw_SFlowNodeModificationException_when_recorder_fails() throws Exception { //given doThrow(SRecorderException.class).when(recorder).recordUpdate(any(UpdateRecord.class), anyString()); //expect expectedException.expect(SFlowNodeModificationException.class); //when activityInstanceServiceImpl.setExpectedEndDate(sFlowNodeInstance, 123L); } @Test public void should_search_pending_tasks_assigned_to_a_user() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); List expectedResult = new ArrayList<>(); when(persistenceService.searchEntity(SHumanTaskInstance.class, "PendingAssignedTo", options, Collections.singletonMap("userId", 61L))) .thenReturn(expectedResult); // When final List result = activityInstanceServiceImpl.searchPendingTasksAssignedTo(61L, options); // Then assertThat(result).describedAs("Human tasks list").isSameAs(expectedResult); } @Test public void should_search_pending_tasks_assigned_to_a_user_throw_exception_on_persitence_error() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.searchEntity(SHumanTaskInstance.class, "PendingAssignedTo", options, Collections.singletonMap("userId", 99L))) .thenThrow(new SBonitaReadException("Fake for test")); // When Throwable thrown = catchThrowable(() -> { activityInstanceServiceImpl.searchPendingTasksAssignedTo(99L, options); }); // Then assertThat(thrown) .isInstanceOf(SBonitaReadException.class) .hasMessageContaining("Fake for test"); } @Test public void should_count_number_of_pending_tasks_assigned_to_user() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.getNumberOfEntities(SHumanTaskInstance.class, "PendingAssignedTo", options, Collections.singletonMap("userId", 365L))) .thenReturn(14L); // When final long count = activityInstanceServiceImpl.getNumberOfPendingTasksAssignedTo(365L, options); // Then assertThat(count).describedAs("Number of human tasks").isEqualTo(14L); } @Test public void should_count_number_of_pending_tasks_assigned_to_user_throw_exception_on_persitence_error() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.getNumberOfEntities(SHumanTaskInstance.class, "PendingAssignedTo", options, Collections.singletonMap("userId", 3365L))) .thenThrow(new SBonitaReadException("Fake for test")); // When Throwable thrown = catchThrowable(() -> { activityInstanceServiceImpl.getNumberOfPendingTasksAssignedTo(3365L, options); }); // Then assertThat(thrown) .isInstanceOf(SBonitaReadException.class) .hasMessageContaining("Fake for test"); } // === Tests for lastUpdateDate on ActivityInstanceServiceImpl methods === @Test public void should_assignHumanTask_update_lastUpdateDate() throws Exception { // Given SHumanTaskInstance humanTask = mock(SHumanTaskInstance.class); when(persistenceService.selectById(any())).thenReturn(humanTask); // When activityInstanceServiceImpl.assignHumanTask(123L, 456L); // Then ArgumentCaptor captor = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder).recordUpdate(captor.capture(), anyString()); assertThat(captor.getValue().getFields().keySet()) .contains("assigneeId", "claimedDate", "lastUpdateDate"); } @Test public void should_incrementLoopCounter_update_lastUpdateDate() throws Exception { // Given SLoopActivityInstance loopInstance = mock(SLoopActivityInstance.class); when(loopInstance.getLoopCounter()).thenReturn(5); // When activityInstanceServiceImpl.incrementLoopCounter(loopInstance); // Then ArgumentCaptor captor = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder).recordUpdate(captor.capture(), anyString()); assertThat(captor.getValue().getFields().keySet()) .contains("loopCounter", "lastUpdateDate"); } @Test public void should_setLoopMax_update_lastUpdateDate() throws Exception { // Given SLoopActivityInstance loopActivity = mock(SLoopActivityInstance.class); // When activityInstanceServiceImpl.setLoopMax(loopActivity, 10); // Then ArgumentCaptor captor = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder).recordUpdate(captor.capture(), anyString()); assertThat(captor.getValue().getFields().keySet()) .contains("loopMax", "lastUpdateDate"); } @Test public void should_setLoopCardinality_update_lastUpdateDate() throws Exception { // Given SFlowNodeInstance flowNode = mock(SFlowNodeInstance.class); // When activityInstanceServiceImpl.setLoopCardinality(flowNode, 5); // Then ArgumentCaptor captor = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder).recordUpdate(captor.capture(), anyString()); assertThat(captor.getValue().getFields().keySet()) .contains("loopCardinality", "lastUpdateDate"); } @Test public void should_addMultiInstanceNumberOfActiveActivities_update_lastUpdateDate() throws Exception { // Given SMultiInstanceActivityInstance multiInstance = mock(SMultiInstanceActivityInstance.class); when(recorder.recordUpdateWithQuery(any(UpdateRecord.class), eq(FlowNodeInstanceService.MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED), eq("updateMultiInstanceActiveCounters"))).thenReturn(1); // When activityInstanceServiceImpl.addMultiInstanceNumberOfActiveActivities(multiInstance, 2); // Then ArgumentCaptor captor = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder).recordUpdateWithQuery(captor.capture(), eq(FlowNodeInstanceService.MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED), eq("updateMultiInstanceActiveCounters")); assertThat(captor.getValue().getFields().keySet()) .contains("id", "number", "lastUpdateDate"); } @Test public void should_addMultiInstanceNumberOfTerminatedActivities_update_lastUpdateDate() throws Exception { // Given SMultiInstanceActivityInstance multiInstance = mock(SMultiInstanceActivityInstance.class); when(recorder.recordUpdateWithQuery(any(UpdateRecord.class), eq(FlowNodeInstanceService.MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED), eq("updateMultiInstanceTerminatedCounters"))).thenReturn(1); // When activityInstanceServiceImpl.addMultiInstanceNumberOfTerminatedActivities(multiInstance, 1); // Then ArgumentCaptor captor = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder).recordUpdateWithQuery(captor.capture(), eq(FlowNodeInstanceService.MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED), eq("updateMultiInstanceTerminatedCounters")); assertThat(captor.getValue().getFields().keySet()) .contains("id", "number", "lastUpdateDate"); } @Test public void should_addMultiInstanceNumberOfCompletedActivities_update_lastUpdateDate() throws Exception { // Given SMultiInstanceActivityInstance multiInstance = mock(SMultiInstanceActivityInstance.class); when(recorder.recordUpdateWithQuery(any(UpdateRecord.class), eq(FlowNodeInstanceService.MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED), eq("updateMultiInstanceCompletedCounters"))).thenReturn(1); // When activityInstanceServiceImpl.addMultiInstanceNumberOfCompletedActivities(multiInstance, 1); // Then ArgumentCaptor captor = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder).recordUpdateWithQuery(captor.capture(), eq(FlowNodeInstanceService.MULTIINSTANCE_NUMBEROFINSTANCE_MODIFIED), eq("updateMultiInstanceCompletedCounters")); assertThat(captor.getValue().getFields().keySet()) .contains("id", "number", "lastUpdateDate"); } @Test public void should_setTokenCount_update_lastUpdateDate() throws Exception { // Given SActivityInstance activityInstance = mock(SActivityInstance.class); // When activityInstanceServiceImpl.setTokenCount(activityInstance, 5); // Then ArgumentCaptor captor = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder).recordUpdate(captor.capture(), anyString()); assertThat(captor.getValue().getFields().keySet()) .contains("tokenCount", "lastUpdateDate"); } @Test public void should_setAbortedByBoundaryEvent_update_lastUpdateDate() throws Exception { // Given SActivityInstance activityInstance = mock(SActivityInstance.class); // When activityInstanceServiceImpl.setAbortedByBoundaryEvent(activityInstance, 789L); // Then ArgumentCaptor captor = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder).recordUpdate(captor.capture(), anyString()); assertThat(captor.getValue().getFields().keySet()) .contains("abortedByBoundary", "lastUpdateDate"); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/impl/BPMFailureServiceImplTest.java ================================================ /** * 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.core.process.instance.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.within; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Map; import org.apache.commons.lang3.exception.ExceptionUtils; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.commons.exceptions.SExceptionContext; import org.bonitasoft.engine.connector.ConnectorValidationException; import org.bonitasoft.engine.core.operation.exception.SOperationExecutionException; import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; import org.bonitasoft.engine.core.process.instance.model.SABPMFailure; import org.bonitasoft.engine.core.process.instance.model.SBPMFailure; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.services.PersistenceService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.AdditionalAnswers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class BPMFailureServiceImplTest { @Mock private PersistenceService persistenceService; @Mock private ArchiveService archiveService; private BPMFailureServiceImpl service; @BeforeEach void setup() throws Exception { service = spy(new BPMFailureServiceImpl(persistenceService, archiveService)); when(persistenceService.insert(any(SBPMFailure.class))).thenAnswer(AdditionalAnswers.returnsFirstArg()); } @Test void should_createFlowNodeFailure_record_failure_with_proper_content() throws Exception { var now = Instant.now(); var flowNodeInstance = createFlowNodeInstance(); var failure = new BPMFailureService.Failure("scope", new Throwable("error message")); var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); verify(persistenceService).insert(bpmFailure); assertThat(bpmFailure.getFlowNodeInstanceId()).isEqualTo(flowNodeInstance.getId()); assertThat(bpmFailure.getProcessDefinitionId()).isEqualTo(flowNodeInstance.getProcessDefinitionId()); assertThat(bpmFailure.getProcessInstanceId()).isEqualTo(flowNodeInstance.getParentProcessInstanceId()); assertThat(bpmFailure.getRootProcessInstanceId()).isEqualTo(flowNodeInstance.getRootProcessInstanceId()); assertThat(bpmFailure.getScope()).isEqualTo("scope"); assertThat(bpmFailure.getErrorMessage()).isEqualTo("Throwable: error message"); assertThat(bpmFailure.getStackTrace()).isEqualTo(ExceptionUtils.getStackTrace(failure.throwable())); assertThat(Instant.ofEpochMilli(bpmFailure.getFailureDate())).isCloseTo(now, within(1000, ChronoUnit.MILLIS)); } @Test void should_createProcessInstanceFailure_record_failure_with_proper_content() throws Exception { var now = Instant.now(); var processInstance = createProcessInstance(); var failure = new BPMFailureService.Failure("scope", new Throwable("error message")); var bpmFailure = service.createProcessInstanceFailure(processInstance, failure); verify(persistenceService).insert(bpmFailure); assertThat(bpmFailure.getProcessInstanceId()).isEqualTo(processInstance.getId()); assertThat(bpmFailure.getProcessDefinitionId()).isEqualTo(processInstance.getProcessDefinitionId()); assertThat(bpmFailure.getRootProcessInstanceId()).isEqualTo(processInstance.getRootProcessInstanceId()); assertThat(bpmFailure.getScope()).isEqualTo("scope"); assertThat(bpmFailure.getErrorMessage()).isEqualTo("Throwable: error message"); assertThat(bpmFailure.getStackTrace()).isEqualTo(ExceptionUtils.getStackTrace(failure.throwable())); assertThat(Instant.ofEpochMilli(bpmFailure.getFailureDate())).isCloseTo(now, within(1000, ChronoUnit.MILLIS)); } private static SFlowNodeInstance createFlowNodeInstance() { SFlowNodeInstance flowNodeInstance = Mockito.mock(SFlowNodeInstance.class); when(flowNodeInstance.getId()).thenReturn(1L); when(flowNodeInstance.getParentProcessInstanceId()).thenReturn(2L); when(flowNodeInstance.getProcessDefinitionId()).thenReturn(3L); when(flowNodeInstance.getRootProcessInstanceId()).thenReturn(4L); return flowNodeInstance; } private static SProcessInstance createProcessInstance() { SProcessInstance processInstance = Mockito.mock(SProcessInstance.class); when(processInstance.getId()).thenReturn(10L); when(processInstance.getProcessDefinitionId()).thenReturn(11L); when(processInstance.getRootProcessInstanceId()).thenReturn(12L); return processInstance; } @Test void should_createFlowNodeFailure_record_failure_with_expression_context() throws Exception { var flowNodeInstance = createFlowNodeInstance(); var failure = new BPMFailureService.Failure("scope", new SExpressionEvaluationException(new RuntimeException("error in expression"), "expressionName")); var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); assertThat(bpmFailure.getContext()).isEqualTo("expression::expressionName"); } @Test void should_createFlowNodeFailure_record_failure_with_message_context() throws Exception { var flowNodeInstance = createFlowNodeInstance(); var errorInMessage = new SBonitaRuntimeException("error in message"); errorInMessage.setMessageInstanceNameOnContext("messageName"); var failure = new BPMFailureService.Failure("scope", errorInMessage); var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); assertThat(bpmFailure.getContext()).isEqualTo("message::messageName"); } @Test void should_createFlowNodeFailure_record_failure_with_connector_context() throws Exception { var flowNodeInstance = createFlowNodeInstance(); var errorInConnector = new SBonitaRuntimeException("error in connector"); errorInConnector.setConnectorDefinitionIdOnContext("rest-connector"); errorInConnector.setConnectorNameOnContext("get-info"); errorInConnector.setConnectorActivationEventOnContext(ConnectorEvent.ON_ENTER.name()); var failure = new BPMFailureService.Failure("scope", errorInConnector); var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); assertThat(bpmFailure.getContext()).isEqualTo("get-info::rest-connector::on_enter"); } @Test void should_createFlowNodeFailure_record_failure_with_connector_input_context() throws Exception { var flowNodeInstance = createFlowNodeInstance(); var errorInConnector = new SBonitaRuntimeException("error in connector"); errorInConnector.setConnectorDefinitionIdOnContext("rest-connector"); errorInConnector.setConnectorNameOnContext("get-info"); errorInConnector.setConnectorActivationEventOnContext(ConnectorEvent.ON_ENTER.name()); errorInConnector.setConnectorInputOnContext("username"); var failure = new BPMFailureService.Failure("scope", errorInConnector); var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); assertThat(bpmFailure.getContext()).isEqualTo("get-info::rest-connector::on_enter//input::username"); } @Test void should_createFlowNodeFailure_record_failure_with_connector_input_expression_context() throws Exception { var flowNodeInstance = createFlowNodeInstance(); var errorInConnector = new SBonitaRuntimeException(new SExpressionEvaluationException( new RuntimeException("error in input expression"), "expressionName")); errorInConnector.setConnectorDefinitionIdOnContext("rest-connector"); errorInConnector.setConnectorNameOnContext("get-info"); errorInConnector.setConnectorActivationEventOnContext(ConnectorEvent.ON_ENTER.name()); errorInConnector.setConnectorInputOnContext("username"); var failure = new BPMFailureService.Failure("scope", errorInConnector); var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); assertThat(bpmFailure.getContext()) .isEqualTo("get-info::rest-connector::on_enter//input::username//expression::expressionName"); } @Test void should_createFlowNodeFailure_record_failure_with_connector_output_expression_context() throws Exception { var flowNodeInstance = createFlowNodeInstance(); var errorInConnector = new SBonitaRuntimeException( new SOperationExecutionException(new SExpressionEvaluationException( new RuntimeException("error in output expression"), "expressionName"))); errorInConnector.setConnectorDefinitionIdOnContext("rest-connector"); errorInConnector.setConnectorNameOnContext("get-info"); errorInConnector.setConnectorActivationEventOnContext(ConnectorEvent.ON_ENTER.name()); var failure = new BPMFailureService.Failure("scope", errorInConnector); var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); assertThat(bpmFailure.getContext()) .isEqualTo("get-info::rest-connector::on_enter//output//expression::expressionName"); } @Test void should_createFlowNodeFailure_record_failure_with_connector_validation_context() throws Exception { var flowNodeInstance = createFlowNodeInstance(); var errorInConnector = new SBonitaRuntimeException(new ConnectorValidationException("error in validation")); errorInConnector.setConnectorDefinitionIdOnContext("rest-connector"); errorInConnector.setConnectorNameOnContext("get-info"); errorInConnector.setConnectorActivationEventOnContext(ConnectorEvent.ON_ENTER.name()); var failure = new BPMFailureService.Failure("scope", errorInConnector); var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); assertThat(bpmFailure.getContext()).isEqualTo("get-info::rest-connector::on_enter//input-validation"); } @Test void should_createFlowNodeFailure_record_failure_with_named_transition_context() throws Exception { var flowNodeInstance = createFlowNodeInstance(); var errorInTransition = new SBonitaRuntimeException(new SExpressionEvaluationException( new RuntimeException("error in consition expression"), "expressionName")); errorInTransition.getContext().put(SExceptionContext.TRANSITION_NAME, "transitionName"); errorInTransition.getContext().put(SExceptionContext.TRANSITION_TARGET_FLOWNODE_NAME, "gateway::gatewayName"); var failure = new BPMFailureService.Failure("scope", errorInTransition); var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); assertThat(bpmFailure.getContext()) .isEqualTo("transitionName//to::gateway::gatewayName//expression::expressionName"); } @Test void should_createFlowNodeFailure_record_failure_with_unnamed_transition_context() throws Exception { var flowNodeInstance = createFlowNodeInstance(); var errorInTransition = new SBonitaRuntimeException(new SExpressionEvaluationException( new RuntimeException("error in consition expression"), "expressionName")); errorInTransition.getContext().put(SExceptionContext.TRANSITION_TARGET_FLOWNODE_NAME, "gateway::gatewayName"); var failure = new BPMFailureService.Failure("scope", errorInTransition); var bpmFailure = service.createFlowNodeFailure(flowNodeInstance, failure); assertThat(bpmFailure.getContext()) .isEqualTo("to::gateway::gatewayName//expression::expressionName"); } @Test void should_getFlowNodeFailures_call_the_right_query_with_proper_parameters() throws Exception { service.getFlowNodeFailures(1, 10); ArgumentCaptor> captor = ArgumentCaptor.forClass(SelectListDescriptor.class); verify(persistenceService).selectList(captor.capture()); var descriptor = captor.getValue(); assertThat(descriptor.getQueryName()).isEqualTo("getFlowNodeFailures"); assertThat(descriptor.getInputParameter("flowNodeInstanceId")).isEqualTo(1L); assertThat(descriptor.getReturnType()).isEqualTo(SBPMFailure.class); assertThat(descriptor.getStartIndex()).isZero(); assertThat(descriptor.getPageSize()).isEqualTo(10); } @Test void should_getProcessInstanceFailures_call_the_right_query_with_proper_parameters() throws Exception { service.getProcessInstanceFailures(1, 10); ArgumentCaptor> captor = ArgumentCaptor.forClass(SelectListDescriptor.class); verify(persistenceService).selectList(captor.capture()); var descriptor = captor.getValue(); assertThat(descriptor.getQueryName()).isEqualTo("getProcessInstanceFailures"); assertThat(descriptor.getInputParameter("processInstanceId")).isEqualTo(1L); assertThat(descriptor.getReturnType()).isEqualTo(SBPMFailure.class); assertThat(descriptor.getStartIndex()).isZero(); assertThat(descriptor.getPageSize()).isEqualTo(10); } @Test void should_archiveFlowNodeFailures_call_archive_service_with_proper_parameters() throws Exception { var failureDate = Instant.now().toEpochMilli(); doReturn(List.of(SBPMFailure.builder() .failureDate(failureDate) .flowNodeInstanceId(1L) .rootProcessInstanceId(1L) .processDefinitionId(1L) .processInstanceId(1L) .scope("scope") .context("context") .errorMessage("errorMessage") .stackTrace("stackTrace") .build())).when(service).getFlowNodeFailures(1L, Integer.MAX_VALUE); long archiveDate = Instant.now().toEpochMilli(); service.archiveFlowNodeFailures(1L, archiveDate); var captor = ArgumentCaptor.forClass(ArchiveInsertRecord.class); verify(archiveService).recordInserts(eq(archiveDate), captor.capture()); var archiveInsertRecord = captor.getValue(); var entity = archiveInsertRecord.getEntity(); assertThat(entity).isInstanceOf(SABPMFailure.class); var archiveBPMFailure = (SABPMFailure) entity; assertThat(archiveBPMFailure.getFailureDate()).isEqualTo(failureDate); assertThat(archiveBPMFailure.getFlowNodeInstanceId()).isEqualTo(1L); assertThat(archiveBPMFailure.getProcessDefinitionId()).isEqualTo(1L); assertThat(archiveBPMFailure.getProcessInstanceId()).isEqualTo(1L); } @Test void should_archiveProcessInstanceFailures_call_archive_service_with_proper_parameters() throws Exception { var failureDate = Instant.now().toEpochMilli(); doReturn(List.of(SBPMFailure.builder() .failureDate(failureDate) .rootProcessInstanceId(1L) .processDefinitionId(1L) .processInstanceId(1L) .scope("scope") .context("context") .errorMessage("errorMessage") .stackTrace("stackTrace") .build())).when(service).getProcessInstanceFailures(1L, Integer.MAX_VALUE); long archiveDate = Instant.now().toEpochMilli(); service.archiveProcessInstanceFailures(1L, archiveDate); var captor = ArgumentCaptor.forClass(ArchiveInsertRecord.class); verify(archiveService).recordInserts(eq(archiveDate), captor.capture()); var archiveInsertRecord = captor.getValue(); var entity = archiveInsertRecord.getEntity(); assertThat(entity).isInstanceOf(SABPMFailure.class); var archiveBPMFailure = (SABPMFailure) entity; assertThat(archiveBPMFailure.getFailureDate()).isEqualTo(failureDate); assertThat(archiveBPMFailure.getRootProcessInstanceId()).isEqualTo(1L); assertThat(archiveBPMFailure.getProcessDefinitionId()).isEqualTo(1L); assertThat(archiveBPMFailure.getProcessInstanceId()).isEqualTo(1L); } @Test void should_deleteFlowNodeFailures_call_persistence_service_with_proper_parameters() throws Exception { doReturn(List.of(SBPMFailure.builder() .id(1L) .build(), SBPMFailure.builder() .id(2L) .build())) .when(service).getFlowNodeFailures(1L, Integer.MAX_VALUE); service.deleteFlowNodeFailures(1L); ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); verify(persistenceService).delete(captor.capture(), eq(SBPMFailure.class)); assertThat(captor.getValue()).contains(1L, 2L); } @Test void should_deleteProcessInstanceFailures_call_persistence_service_with_proper_parameters() throws Exception { doReturn(List.of(SBPMFailure.builder() .id(1L) .build(), SBPMFailure.builder() .id(2L) .build())) .when(service).getProcessInstanceFailures(1L, Integer.MAX_VALUE); service.deleteProcessInstanceFailures(1L); ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); verify(persistenceService).delete(captor.capture(), eq(SBPMFailure.class)); assertThat(captor.getValue()).contains(1L, 2L); } @Test void should_deleteArchivedFlowNodeFailures_call_the_right_query_with_proper_parameters() throws Exception { service.deleteArchivedFlowNodeFailures(List.of(1L, 2L)); verify(archiveService).deleteFromQuery("deleteArchivedBPMFailuresByFlowNodeInstanceIds", Map.ofEntries(Map.entry("flowNodeInstanceIds", List.of(1L, 2L)))); } @Test void should_getArchivedFlowNodeFailures_call_the_right_query_with_proper_parameters() throws Exception { service.getArchivedFlowNodeFailures(1L, 10); ArgumentCaptor> captor = ArgumentCaptor.forClass(SelectListDescriptor.class); verify(persistenceService).selectList(captor.capture()); var descriptor = captor.getValue(); assertThat(descriptor.getQueryName()).isEqualTo("getArchivedFlowNodeFailures"); assertThat(descriptor.getInputParameter("flowNodeInstanceId")).isEqualTo(1L); assertThat(descriptor.getReturnType()).isEqualTo(SABPMFailure.class); assertThat(descriptor.getStartIndex()).isZero(); assertThat(descriptor.getPageSize()).isEqualTo(10); } @Test void should_deleteArchivedProcessInstanceFailures_call_the_right_query_with_proper_parameters() throws Exception { service.deleteArchivedProcessInstanceFailures(List.of(1L, 2L)); verify(archiveService).deleteFromQuery("deleteArchivedBPMFailuresByProcessInstanceIds", Map.ofEntries(Map.entry("processInstanceIds", List.of(1L, 2L)))); } @Test void should_getArchivedProcessInstanceFailures_call_the_right_query_with_proper_parameters() throws Exception { service.getArchivedProcessInstanceFailures(1L, 10); ArgumentCaptor> captor = ArgumentCaptor.forClass(SelectListDescriptor.class); verify(persistenceService).selectList(captor.capture()); var descriptor = captor.getValue(); assertThat(descriptor.getQueryName()).isEqualTo("getArchivedProcessInstanceFailures"); assertThat(descriptor.getInputParameter("processInstanceId")).isEqualTo(1L); assertThat(descriptor.getReturnType()).isEqualTo(SABPMFailure.class); assertThat(descriptor.getStartIndex()).isZero(); assertThat(descriptor.getPageSize()).isEqualTo(10); } @Test void should_getChildProcessInstancesFailures_call_the_right_query_with_proper_parameters() throws Exception { service.getChildProcessInstancesFailures(1, 10); ArgumentCaptor> captor = ArgumentCaptor.forClass(SelectListDescriptor.class); verify(persistenceService).selectList(captor.capture()); var descriptor = captor.getValue(); assertThat(descriptor.getQueryName()).isEqualTo("getChildProcessInstancesFailures"); assertThat(descriptor.getInputParameter("rootProcessInstanceId")).isEqualTo(1L); assertThat(descriptor.getReturnType()).isEqualTo(SBPMFailure.class); assertThat(descriptor.getStartIndex()).isZero(); assertThat(descriptor.getPageSize()).isEqualTo(10); } @Test void should_getArchivedChildProcessInstancesFailures_call_the_right_query_with_proper_parameters() throws Exception { service.getArchivedChildProcessInstancesFailures(1L, 10); ArgumentCaptor> captor = ArgumentCaptor.forClass(SelectListDescriptor.class); verify(persistenceService).selectList(captor.capture()); var descriptor = captor.getValue(); assertThat(descriptor.getQueryName()).isEqualTo("getArchivedChildProcessInstancesFailures"); assertThat(descriptor.getInputParameter("rootProcessInstanceId")).isEqualTo(1L); assertThat(descriptor.getReturnType()).isEqualTo(SABPMFailure.class); assertThat(descriptor.getStartIndex()).isZero(); assertThat(descriptor.getPageSize()).isEqualTo(10); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/impl/FlowNodeInstancesServiceImplTest.java ================================================ /** * 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.core.process.instance.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Map; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SStateCategory; import org.bonitasoft.engine.core.process.instance.model.STaskPriority; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.PersistenceService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class FlowNodeInstancesServiceImplTest { @Mock private Recorder recorder; @Mock private PersistenceService persistenceService; @Mock private ArchiveService archiveService; @Mock private SFlowNodeInstance flowNodeInstance; @Mock private FlowNodeState flowNodeState; @Captor private ArgumentCaptor updateRecordCaptor; private ActivityInstanceServiceImpl service; @Before public void setUp() { service = new ActivityInstanceServiceImpl(recorder, persistenceService, archiveService); } // === Tests for setLastUpdateDate helper method behavior === @Test public void should_setState_update_both_reachStateDate_and_lastUpdateDate() throws Exception { // Given when(flowNodeState.getId()).thenReturn(1); when(flowNodeState.getName()).thenReturn("ready"); when(flowNodeState.isStable()).thenReturn(true); when(flowNodeState.isTerminal()).thenReturn(false); // When service.setState(flowNodeInstance, flowNodeState); // Then verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString()); assertThat(updateRecordCaptor.getValue().getFields().keySet()) .contains("stateId", "reachedStateDate", "lastUpdateDate"); } @Test public void should_setExecuting_update_lastUpdateDate() throws Exception { // When service.setExecuting(flowNodeInstance); // Then verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString()); assertThat(updateRecordCaptor.getValue().getFields().keySet()) .contains("stateExecuting", "lastUpdateDate"); } @Test public void should_updateDisplayName_update_lastUpdateDate() throws Exception { // Given when(flowNodeInstance.getDisplayName()).thenReturn("oldName"); // When service.updateDisplayName(flowNodeInstance, "newName"); // Then verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString()); assertThat(updateRecordCaptor.getValue().getFields().keySet()) .contains("displayName", "lastUpdateDate"); } @Test public void should_updateDisplayDescription_update_lastUpdateDate() throws Exception { // Given when(flowNodeInstance.getDisplayDescription()).thenReturn("oldDesc"); // When service.updateDisplayDescription(flowNodeInstance, "newDesc"); // Then verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString()); assertThat(updateRecordCaptor.getValue().getFields().keySet()) .contains("displayDescription", "lastUpdateDate"); } @Test public void should_setTaskPriority_update_lastUpdateDate() throws Exception { // When service.setTaskPriority(flowNodeInstance, STaskPriority.HIGHEST); // Then verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString()); assertThat(updateRecordCaptor.getValue().getFields().keySet()) .contains("priority", "lastUpdateDate"); } @Test public void should_setStateCategory_update_lastUpdateDate() throws Exception { // When service.setStateCategory(flowNodeInstance, SStateCategory.ABORTING); // Then verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString()); assertThat(updateRecordCaptor.getValue().getFields().keySet()) .contains("stateCategory", "lastUpdateDate"); } @Test public void should_setExecutedBy_update_lastUpdateDate() throws Exception { // When service.setExecutedBy(flowNodeInstance, 123L); // Then verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString()); assertThat(updateRecordCaptor.getValue().getFields().keySet()) .contains("executedBy", "lastUpdateDate"); } @Test public void should_setExecutedBySubstitute_update_lastUpdateDate() throws Exception { // When service.setExecutedBySubstitute(flowNodeInstance, 456L); // Then verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString()); assertThat(updateRecordCaptor.getValue().getFields().keySet()) .contains("executedBySubstitute", "lastUpdateDate"); } @Test public void should_setExpectedEndDate_update_lastUpdateDate() throws Exception { // When service.setExpectedEndDate(flowNodeInstance, 123456789L); // Then verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString()); assertThat(updateRecordCaptor.getValue().getFields().keySet()) .contains("expectedEndDate", "lastUpdateDate"); } // === Tests for timestamp reuse behavior === @Test public void should_setState_use_same_timestamp_for_reachStateDate_and_lastUpdateDate() throws Exception { // Given when(flowNodeState.getId()).thenReturn(1); when(flowNodeState.getName()).thenReturn("ready"); when(flowNodeState.isStable()).thenReturn(true); when(flowNodeState.isTerminal()).thenReturn(false); // When service.setState(flowNodeInstance, flowNodeState); // Then verify(recorder).recordUpdate(updateRecordCaptor.capture(), anyString()); Map fields = updateRecordCaptor.getValue().getFields(); Long reachedStateDate = (Long) fields.get("reachedStateDate"); Long lastUpdateDate = (Long) fields.get("lastUpdateDate"); assertThat(lastUpdateDate).isNotNull(); assertThat(lastUpdateDate).isEqualTo(reachedStateDate); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/impl/GatewayInstanceServiceImplTest.java ================================================ /** * 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.core.process.instance.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; 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.SGatewayType; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.STransitionDefinition; import org.bonitasoft.engine.core.process.definition.model.impl.SFlowNodeDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.impl.STransitionDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.impl.SUserTaskDefinitionImpl; import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SGatewayModificationException; import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; import org.bonitasoft.engine.core.process.instance.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class GatewayInstanceServiceImplTest { private static final long PROCESS_INSTANCE_ID = 12l; @Mock private Recorder recorder; @Mock private EventService eventService; @Mock private ReadPersistenceService persistenceRead; @Mock private FlowNodeInstanceService flowNodeInstanceService; @Mock private SFlowElementContainerDefinition processContainer; @Captor private ArgumentCaptor updateRecordCaptor; @InjectMocks @Spy private GatewayInstanceServiceImpl gatewayInstanceService; @Test public void should_extractElementThatAreSourceAndTarget_modify_the_lists() { SFlowNodeDefinition step1 = node(1, "step1"); SFlowNodeDefinition step2 = node(2, "step2"); SFlowNodeDefinition step3 = node(3, "step3"); List sourceElements = new ArrayList<>(Arrays.asList(step1, step2)); List targetElements = new ArrayList<>(Arrays.asList(step2, step3)); List sourceAndTarget = gatewayInstanceService .extractElementThatAreSourceAndTarget(sourceElements, targetElements); assertThat(sourceElements).isEmpty(); assertThat(targetElements).containsOnly(step3); assertThat(sourceAndTarget).containsOnly(step2, step1);//step1 because it's also a start element } SFlowNodeDefinition node(long id, String name) { SUserTaskDefinitionImpl definition = new SUserTaskDefinitionImpl(id, name, "actor"); doReturn(definition).when(processContainer).getFlowNode(id); return definition; } @Test public void should_containsToken_for_source_element_with_one_token_return_true() throws Exception { instanceInDatabase("step1", PROCESS_INSTANCE_ID, false); instanceInDatabase("step2", PROCESS_INSTANCE_ID, true); boolean containsToken = gatewayInstanceService.containsToken(PROCESS_INSTANCE_ID, flowNodeDefList("step0", "step1", "step2"), true); assertThat(containsToken).isTrue(); } @Test public void should_containsToken_for_source_element_with_no_token_return_false() throws Exception { instanceInDatabase("step1", PROCESS_INSTANCE_ID, false); instanceInDatabase("step2", PROCESS_INSTANCE_ID, false); boolean containsToken = gatewayInstanceService.containsToken(PROCESS_INSTANCE_ID, flowNodeDefList("step0", "step1", "step2"), true); assertThat(containsToken).isFalse(); } @Test public void should_containsToken_for_target_element_with_token_return_true() throws Exception { instanceInDatabase("step1", PROCESS_INSTANCE_ID, true); instanceInDatabase("step2", PROCESS_INSTANCE_ID, false); boolean containsToken = gatewayInstanceService.containsToken(PROCESS_INSTANCE_ID, flowNodeDefList("step0", "step1", "step2"), false); assertThat(containsToken).isTrue(); } @Test public void should_containsToken_for_target_element_with_no_token_return_false() throws Exception { instanceInDatabase("step1", PROCESS_INSTANCE_ID, true); instanceInDatabase("step2", PROCESS_INSTANCE_ID, true); boolean containsToken = gatewayInstanceService.containsToken(PROCESS_INSTANCE_ID, flowNodeDefList("step0", "step1", "step2"), false); assertThat(containsToken).isFalse(); } @Test public void should_containsToken_for_both_element_with_token_return_true1() throws Exception { instanceInDatabase("step2", PROCESS_INSTANCE_ID, true); boolean containsToken = gatewayInstanceService.containsToken(PROCESS_INSTANCE_ID, flowNodeDefList("step0", "step1", "step2"), null); assertThat(containsToken).isTrue(); } @Test public void should_containsToken_for_both_element_with_token_return_true2() throws Exception { instanceInDatabase("step2", PROCESS_INSTANCE_ID, false); boolean containsToken = gatewayInstanceService.containsToken(PROCESS_INSTANCE_ID, flowNodeDefList("step0", "step1", "step2"), null); assertThat(containsToken).isTrue(); } @Test public void should_containsToken_for_both_element_with_no_token_return_false() throws Exception { boolean containsToken = gatewayInstanceService.containsToken(PROCESS_INSTANCE_ID, flowNodeDefList("step0", "step1", "step2"), null); assertThat(containsToken).isFalse(); } List flowNodeDefList(String... names) { ArrayList list = new ArrayList<>(); for (String name : names) { list.add(node(1, name)); } return list; } private void instanceInDatabase(String name, long processInstanceId, boolean terminal) throws Exception { SUserTaskInstance sUserTaskInstance = new SUserTaskInstance(); sUserTaskInstance.setName(name); sUserTaskInstance.setTerminal(terminal); doReturn(Arrays.asList(sUserTaskInstance)).when(flowNodeInstanceService) .getFlowNodeInstancesByNameAndParentContainerId(name, processInstanceId); } @Test public void should_transitionsContainsAToken_calls_containsToken_with_middle_node_terminal() throws Exception { SFlowNodeDefinition gate = node(666, "gate"); node(1, "step1"); node(2, "step2"); node(3, "step3"); instanceInDatabase("step2", PROCESS_INSTANCE_ID, true); boolean containsAToken = gatewayInstanceService.transitionsContainsAToken( Arrays.asList(transition(1, 2), transition(2, 3)), gate, PROCESS_INSTANCE_ID, processContainer); assertThat(containsAToken).isTrue(); } @Test public void should_transitionsContainsAToken_calls_containsToken_with_middle_node_not_terminal() throws Exception { SFlowNodeDefinition gate = node(666, "gate"); node(1, "step1"); node(2, "step2"); node(3, "step3"); instanceInDatabase("step2", PROCESS_INSTANCE_ID, false); boolean containsAToken = gatewayInstanceService.transitionsContainsAToken( Arrays.asList(transition(1, 2), transition(2, 3)), gate, PROCESS_INSTANCE_ID, processContainer); assertThat(containsAToken).isTrue(); } @Test public void should_transitionsContainsAToken_calls_containsToken_with_source_node_true() throws Exception { SFlowNodeDefinition gate = node(666, "gate"); node(1, "step1"); node(2, "step2"); node(3, "step3"); instanceInDatabase("step1", PROCESS_INSTANCE_ID, true); boolean containsAToken = gatewayInstanceService.transitionsContainsAToken( Arrays.asList(transition(1, 2), transition(2, 3)), gate, PROCESS_INSTANCE_ID, processContainer); assertThat(containsAToken).isTrue(); } @Test public void should_transitionsContainsAToken_calls_containsToken_with_source_node_false() throws Exception { SFlowNodeDefinition gate = node(666, "gate"); node(0, "step0"); node(1, "step1"); node(2, "step2"); node(3, "step3"); transition(0, 1); transition(1, 2); transition(2, 3); instanceInDatabase("step1", PROCESS_INSTANCE_ID, false); boolean containsAToken = gatewayInstanceService.transitionsContainsAToken( Arrays.asList(transition(1, 2), transition(2, 3)), gate, PROCESS_INSTANCE_ID, processContainer); assertThat(containsAToken).isFalse(); } @Test public void should_transitionsContainsAToken_calls_containsToken_with_target_node_true() throws Exception { SFlowNodeDefinition gate = node(666, "gate"); node(1, "step1"); node(2, "step2"); node(3, "step3"); instanceInDatabase("step3", PROCESS_INSTANCE_ID, false); boolean containsAToken = gatewayInstanceService.transitionsContainsAToken( Arrays.asList(transition(1, 2), transition(2, 3)), gate, PROCESS_INSTANCE_ID, processContainer); assertThat(containsAToken).isTrue(); } @Test public void should_transitionsContainsAToken_calls_containsToken_with_target_node_false() throws Exception { SFlowNodeDefinition gate = node(666, "gate"); node(1, "step1"); node(2, "step2"); node(3, "step3"); instanceInDatabase("step3", PROCESS_INSTANCE_ID, true); boolean containsAToken = gatewayInstanceService.transitionsContainsAToken( Arrays.asList(transition(1, 2), transition(2, 3)), gate, PROCESS_INSTANCE_ID, processContainer); assertThat(containsAToken).isFalse(); } @Test public void should_transitionsContainsAToken_calls_containsToken_with_no_node() throws Exception { SFlowNodeDefinition gate = node(666, "gate"); node(1, "step1"); node(2, "step2"); node(3, "step3"); boolean containsAToken = gatewayInstanceService.transitionsContainsAToken( Arrays.asList(transition(1, 2), transition(2, 3)), gate, PROCESS_INSTANCE_ID, processContainer); assertThat(containsAToken).isFalse(); } @Test public void should_addBackwardReachableTransitions_complete_list() { SFlowNodeDefinition gate = node(666, "gate"); node(1, "stepa1"); node(2, "stepa2"); transition(1, 2); node(3, "stepa3"); transition(2, 3); node(4, "stepb4"); node(5, "stepb5"); node(6, "stepb6"); transition(5, 6); transition(4, 6); List startTransition = Arrays.asList(transition(6, 666), transition(3, 666)); List toComplete = new ArrayList<>(); gatewayInstanceService.addBackwardReachableTransitions(processContainer, gate, startTransition, toComplete, Collections. emptyList()); assertThat(toComplete).containsOnly(transition(1, 2), transition(2, 3), transition(4, 6), transition(5, 6), transition(3, 666), transition(6, 666)); } @Test public void should_addBackwardReachableTransitions_with_gateway_having_a_loop_on_itself() { SFlowNodeDefinition gate = node(666, "gate"); node(1, "step1"); node(2, "step2"); transition(1, 666); transition(666, 1); transition(2, 666); List startTransition = Arrays.asList(transition(1, 666)); List toComplete = new ArrayList<>(); gatewayInstanceService.addBackwardReachableTransitions(processContainer, gate, startTransition, toComplete, Collections. emptyList()); assertThat(toComplete).containsOnly(transition(1, 666), transition(666, 1)); } private STransitionDefinition transition(long source, long target) { STransitionDefinitionImpl transition = new STransitionDefinitionImpl("name", source, target); ((SFlowNodeDefinitionImpl) processContainer.getFlowNode(target)).addIncomingTransition(transition); ((SFlowNodeDefinitionImpl) processContainer.getFlowNode(source)).addOutgoingTransition(transition); return transition; } @Test public void should_checkMergingCondition_on_inclusive() throws Exception { doReturn(true).when(gatewayInstanceService).isInclusiveGatewayActivated(any(SProcessDefinition.class), any(SGatewayInstance.class)); SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl("P", "1.0"); SGatewayInstance gate = new SGatewayInstance(); gate.setGatewayType(SGatewayType.INCLUSIVE); boolean mergingCondition = gatewayInstanceService.checkMergingCondition(processDefinition, gate); verify(gatewayInstanceService).isInclusiveGatewayActivated(processDefinition, gate); assertThat(mergingCondition).isTrue(); } @Test public void should_checkMergingCondition_on_parallel() throws Exception { doReturn(true).when(gatewayInstanceService).isParallelGatewayActivated(any(SProcessDefinition.class), any(SGatewayInstance.class)); SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl("P", "1.0"); SGatewayInstance gate = new SGatewayInstance(); gate.setGatewayType(SGatewayType.PARALLEL); boolean mergingCondition = gatewayInstanceService.checkMergingCondition(processDefinition, gate); verify(gatewayInstanceService).isParallelGatewayActivated(processDefinition, gate); assertThat(mergingCondition).isTrue(); } @Test public void should_checkMergingCondition_on_exclusive() throws Exception { SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl("P", "1.0"); SGatewayInstance gate = new SGatewayInstance(); gate.setGatewayType(SGatewayType.EXCLUSIVE); boolean mergingCondition = gatewayInstanceService.checkMergingCondition(processDefinition, gate); assertThat(mergingCondition).isTrue(); } @Test public void should_getMergedTokens_on_exclusive_return_the_first_token() throws Exception { SGatewayInstance gate = new SGatewayInstance(); gate.setGatewayType(SGatewayType.EXCLUSIVE); List mergedTokens = gatewayInstanceService.getMergedTokens(gate, Arrays.asList("1", "2")); assertThat(mergedTokens).containsOnly("1"); } @Test public void should_getMergedTokens_on_parallel() throws Exception { SGatewayInstance gate = new SGatewayInstance(); gate.setGatewayType(SGatewayType.PARALLEL); List mergedTokens = gatewayInstanceService.getMergedTokens(gate, Arrays.asList("1", "2", "3", "2")); assertThat(mergedTokens).containsOnly("1", "2", "3"); } @Test public void should_getMergedTokens_on_inclusive() throws Exception { SGatewayInstance gate = new SGatewayInstance(); gate.setGatewayType(SGatewayType.INCLUSIVE); List mergedTokens = gatewayInstanceService.getMergedTokens(gate, Arrays.asList("1", "2", "3", "2")); assertThat(mergedTokens).containsOnly("1", "2", "3"); } @Test public void should_getRemainingToken_return_not_merged_tokens() { List remainingTokens = gatewayInstanceService.getRemainingTokens(Arrays.asList("1", "2", "3", "2", "1"), Arrays.asList("1", "2", "3")); assertThat(remainingTokens).containsOnly("2", "1"); } @Test public void should_parallelBehavior_merged() { node(666, "gate"); node(1, "step1"); node(2, "step2"); node(3, "step3"); SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl("P", "1.0"); processDefinition.setProcessContainer(processContainer); SGatewayInstance gate = new SGatewayInstance(); gate.setName("gate"); gate.setHitBys("1,2,3,2"); gate.setFlowNodeDefinitionId(666); node(666, "gate"); transition(1, 666); transition(2, 666); transition(3, 666); boolean isMerged = gatewayInstanceService.isParallelGatewayActivated(processDefinition, gate); assertThat(isMerged).isTrue(); } @Test public void should_parallelBehavior_not_merged() { node(666, "gate"); node(1, "step1"); node(2, "step2"); node(3, "step3"); SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl("P", "1.0"); processDefinition.setProcessContainer(processContainer); SGatewayInstance gate = new SGatewayInstance(); gate.setName("gate"); gate.setHitBys("2,3,2"); gate.setFlowNodeDefinitionId(666); node(666, "gate"); transition(1, 666); transition(2, 666); transition(3, 666); boolean isMerged = gatewayInstanceService.isParallelGatewayActivated(processDefinition, gate); assertThat(isMerged).isFalse(); } @Test public void should_inclusiveBehavior_merged() throws SBonitaReadException { node(666, "gate"); node(1, "step1"); node(2, "step2"); node(3, "step3"); SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl("P", "1.0"); processDefinition.setProcessContainer(processContainer); SGatewayInstance gate = new SGatewayInstance(); gate.setName("gate"); gate.setHitBys("1,2,3,2"); gate.setFlowNodeDefinitionId(666); gate.setParentContainerId(PROCESS_INSTANCE_ID); SFlowNodeDefinition nodeDefinition = node(666, "gate"); transition(1, 666); transition(2, 666); transition(3, 666); doNothing().when(gatewayInstanceService).addBackwardReachableTransitions( any(SFlowElementContainerDefinition.class), any(SFlowNodeDefinition.class), anyList(), anyList(), anyList()); doReturn(true).when(gatewayInstanceService).transitionsContainsAToken(anyList(), any(SFlowNodeDefinition.class), anyLong(), any(SFlowElementContainerDefinition.class)); boolean isMerged = gatewayInstanceService.isInclusiveGatewayActivated(processDefinition, gate); assertThat(isMerged).isFalse(); verify(gatewayInstanceService, times(2)).addBackwardReachableTransitions(eq(processContainer), eq(nodeDefinition), anyList(), anyList(), anyList()); verify(gatewayInstanceService).transitionsContainsAToken(anyList(), eq(nodeDefinition), eq(PROCESS_INSTANCE_ID), eq(processContainer)); } @Test public void should_isInclusiveGatewayActivated_return_false_when_finished() throws SBonitaReadException { SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl("P", "1.0"); processDefinition.setProcessContainer(processContainer); SGatewayInstance gate = new SGatewayInstance(); gate.setName("gate"); gate.setHitBys("FINISH:1"); gate.setFlowNodeDefinitionId(50L); gate.setParentContainerId(PROCESS_INSTANCE_ID); node(50L, "gate"); boolean activated = gatewayInstanceService.isInclusiveGatewayActivated(processDefinition, gate); assertThat(activated).isFalse(); } @Test public void should_setState_change_lastUpdate_and_reachStateDate() throws SGatewayModificationException, SRecorderException { SGatewayInstance gate = new SGatewayInstance(); gatewayInstanceService.setState(gate, 12); verify(recorder).recordUpdate(updateRecordCaptor.capture(), nullable(String.class)); assertThat(updateRecordCaptor.getValue().getFields().keySet()).contains("stateId", "reachedStateDate", "lastUpdateDate"); } @Test public void should_getActiveGatewayOfProcess_return_the_gateway() throws Exception { SGatewayInstance gate = new SGatewayInstance(); doReturn(gate).when(persistenceRead) .selectOne(SelectDescriptorBuilder.getActiveGatewayInstanceOfProcess(PROCESS_INSTANCE_ID, "myGate")); final SGatewayInstance myGate = gatewayInstanceService.getActiveGatewayInstanceOfTheProcess(PROCESS_INSTANCE_ID, "myGate"); assertThat(myGate).isEqualTo(gate); } @Test public void should_getActiveGatewayOfProcess_return_null_if_not_found() throws Exception { doReturn(null).when(persistenceRead) .selectOne(SelectDescriptorBuilder.getActiveGatewayInstanceOfProcess(PROCESS_INSTANCE_ID, "myGate")); final SGatewayInstance myGate = gatewayInstanceService.getActiveGatewayInstanceOfTheProcess(PROCESS_INSTANCE_ID, "myGate"); assertThat(myGate).isNull(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/impl/ProcessInstanceServiceImplTest.java ================================================ /** * 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.core.process.instance.impl; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import static org.bonitasoft.engine.core.process.instance.impl.ProcessInstanceServiceImpl.IN_REQUEST_SIZE; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.connector.ConnectorInstanceService; import org.bonitasoft.engine.core.contract.data.ContractDataService; import org.bonitasoft.engine.core.document.api.DocumentService; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService; import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition; import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition; import org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition; import org.bonitasoft.engine.core.process.definition.model.event.impl.SBoundaryEventDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.event.impl.SIntermediateThrowEventDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.event.impl.SStartEventDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SCatchSignalEventTriggerDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.SThrowMessageEventTriggerDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.event.trigger.impl.STimerEventTriggerDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.impl.SFlowElementContainerDefinitionImpl; import org.bonitasoft.engine.core.process.definition.model.impl.SProcessDefinitionImpl; import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService; import org.bonitasoft.engine.core.process.instance.api.BPMFailureService; import org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService; import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceReadException; import org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance; 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.SLoopActivityInstance; import org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance; import org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance; import org.bonitasoft.engine.core.process.instance.model.STaskPriority; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SIntermediateCatchEventInstance; import org.bonitasoft.engine.core.process.instance.model.event.SStartEventInstance; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Elias Ricken de Medeiros * @author Emmanuel Duchastenier * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class ProcessInstanceServiceImplTest { @Rule public SystemOutRule systemOutRule = new SystemOutRule().enableLog(); private final long processInstanceId = 574815189L; private final long archivedProcessInstanceId = 11223344L; @Mock private SProcessInstance processInstance; @Mock private SAProcessInstance aProcessInstance; @Mock private ClassLoaderService classLoaderService; @Mock private EventInstanceService eventInstanceService; @Mock private Recorder recorder; @Mock private ArchiveService archiveService; @Mock private ReadPersistenceService readPersistenceService; @Mock private ActivityInstanceService activityInstanceService; @Mock private DataInstanceService dataInstanceService; @Mock private ProcessDefinitionService processDefinitionService; @Mock private ConnectorInstanceService connectorInstanceService; @Mock private DocumentService documentService; @Mock private SCommentService sCommentService; @Mock private RefBusinessDataService refBusinessDataService; @Mock private ContractDataService contractDataService; @Mock private BPMFailureService bpmFailureService; @Spy @InjectMocks private ProcessInstanceServiceImpl processInstanceService; @Before public void setUp() throws SBonitaException { doCallRealMethod().when(processInstanceService).deleteParentProcessInstanceAndElements(anyList()); when(processInstance.getId()).thenReturn(processInstanceId); when(archiveService.getDefinitiveArchiveReadPersistenceService()).thenReturn(readPersistenceService); } @Test public void deleteParentProcessInstanceAndElementsOnAbsentProcessShouldBeIgnored() throws Exception { // given: doThrow(SProcessInstanceModificationException.class).when(processInstanceService) .deleteProcessInstance(processInstance); doThrow(SProcessInstanceNotFoundException.class).when(processInstanceService) .getProcessInstance(processInstanceId); // when: processInstanceService.deleteParentProcessInstanceAndElements(processInstance); // then: verify(processInstanceService).getProcessInstance(processInstanceId); } @Test(expected = SBonitaException.class) public void deleteParentProcessInstanceAndElements_should_throw_exception_when_deleteProcessInstance_failed() throws Exception { // given: doThrow(SProcessInstanceModificationException.class).when(processInstanceService) .deleteProcessInstance(processInstance); // getProcessInstance normally returns: doReturn(mock(SProcessInstance.class)).when(processInstanceService).getProcessInstance(processInstanceId); try { // when: processInstanceService.deleteParentProcessInstanceAndElements(processInstance); } finally { // then: verify(processInstanceService).getProcessInstance(processInstanceId); } } @Test public void deleteParentProcessInstanceAndElements_returns_0_when_no_elements_are_deleted() throws Exception { assertEquals(0, processInstanceService .deleteParentProcessInstanceAndElements(Collections. emptyList())); } @Test public void deleteParentProcessInstanceAndElements_returns_1_when_1_elements_are_deleted() throws Exception { final List processInstances = asList(mock(SProcessInstance.class)); assertEquals(1, processInstanceService.deleteParentProcessInstanceAndElements(processInstances)); } @Test public void deleteParentProcessInstanceAndElements_returns_n_when_n_elements_are_deleted() throws Exception { final List processInstances = asList(mock(SProcessInstance.class), mock(SProcessInstance.class), mock(SProcessInstance.class)); assertEquals(3, processInstanceService.deleteParentProcessInstanceAndElements(processInstances)); } @Test public void testDeleteProcessInstance_delete_archived_activity() throws Exception { final SProcessInstance sProcessInstance = mock(SProcessInstance.class); final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); when(classLoaderService.getClassLoader(identifier(ScopeType.PROCESS, sProcessInstance.getId()))) .thenReturn(classLoader); doReturn(new HashSet<>(asList(4L, 5L, 6L))).when(activityInstanceService) .getSourceObjectIdsOfArchivedFlowNodeInstances(any()); processInstanceService.deleteParentProcessInstanceAndElements(sProcessInstance); verify(processInstanceService, times(1)).deleteProcessInstanceElements(sProcessInstance); verify(processInstanceService, times(1)) .deleteArchivedProcessInstances(Collections.singletonList(sProcessInstance.getId())); verify(activityInstanceService, times(1)).deleteArchivedFlowNodeInstances(asList(4L, 5L, 6L)); verify(bpmFailureService).deleteArchivedFlowNodeFailures(asList(4L, 5L, 6L)); } @Test public void testDeleteProcessInstance_delete_failures() throws Exception { final SProcessInstance sProcessInstance = mock(SProcessInstance.class); final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); when(classLoaderService.getClassLoader(identifier(ScopeType.PROCESS, sProcessInstance.getId()))) .thenReturn(classLoader); doReturn(new HashSet<>()).when(activityInstanceService) .getSourceObjectIdsOfArchivedFlowNodeInstances(any()); processInstanceService.deleteParentProcessInstanceAndElements(sProcessInstance); verify(bpmFailureService).deleteProcessInstanceFailures(sProcessInstance.getId()); verify(bpmFailureService) .deleteArchivedProcessInstanceFailures(Collections.singletonList(sProcessInstance.getId())); } @Test public void getNumberOfFailedProcessInstances_should_return_number_of_failed_process_instance() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); final long number = 2L; doReturn(number).when(readPersistenceService).getNumberOfEntities(SProcessInstance.class, "Failed", queryOptions, null); // When final long result = processInstanceService.getNumberOfFailedProcessInstances(queryOptions); // Then assertEquals("The result should be equals to the number returned by the mock.", number, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfFailedProcessInstances_should_throw_exception_when_persistence_service_failed() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); doThrow(new SBonitaReadException("plop")).when(readPersistenceService) .getNumberOfEntities(SProcessInstance.class, "Failed", queryOptions, null); // When processInstanceService.getNumberOfFailedProcessInstances(queryOptions); } @Test public void searchFailedProcessInstances_should_return_list_of_failed_process_instance() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); final List list = asList(mock(ProcessInstance.class)); doReturn(list).when(readPersistenceService).searchEntity(SProcessInstance.class, "Failed", queryOptions, null); // When final List result = processInstanceService.searchFailedProcessInstances(queryOptions); // Then assertEquals("The result should be equals to the list returned by the mock.", list, result); } @Test(expected = SBonitaReadException.class) public void searchFailedProcessInstances_should_throw_exception_when_persistence_service_failed() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); doThrow(new SBonitaReadException("plop")).when(readPersistenceService).searchEntity(SProcessInstance.class, "Failed", queryOptions, null); // When processInstanceService.searchFailedProcessInstances(queryOptions); } @Test public void getNumberOfOpenProcessInstancesSupervisedBy_should_return_number_of_open_process_instance_supervised_by() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); final long userId = 198L; final long number = 2L; doReturn(number).when(readPersistenceService).getNumberOfEntities(eq(SProcessInstance.class), eq("SupervisedBy"), eq(queryOptions), anyMap()); // When final long result = processInstanceService.getNumberOfOpenProcessInstancesSupervisedBy(userId, queryOptions); // Then assertEquals("The result should be equals to the number returned by the mock.", number, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfOpenProcessInstancesSupervisedBy_should_throw_exception_when_persistence_service_failed() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); final long userId = 198L; doThrow(new SBonitaReadException("plop")).when(readPersistenceService).getNumberOfEntities( eq(SProcessInstance.class), eq("SupervisedBy"), eq(queryOptions), anyMap()); // When processInstanceService.getNumberOfOpenProcessInstancesSupervisedBy(userId, queryOptions); } @Test public void searchOpenProcessInstancesSupervisedBy_should_return_list_of_open_process_instance_supervised_by() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); final long userId = 198L; final List list = asList(mock(ProcessInstance.class)); doReturn(list).when(readPersistenceService).searchEntity(eq(SProcessInstance.class), eq("SupervisedBy"), eq(queryOptions), anyMap()); // When final List result = processInstanceService.searchOpenProcessInstancesSupervisedBy(userId, queryOptions); // Then assertEquals("The result should be equals to the list returned by the mock.", list, result); } @Test(expected = SBonitaReadException.class) public void searchOpenProcessInstancesSupervisedBy_should_throw_exception_when_persistence_service_failed() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); final long userId = 198L; doThrow(new SBonitaReadException("plop")).when(readPersistenceService).searchEntity(eq(SProcessInstance.class), eq("SupervisedBy"), eq(queryOptions), anyMap()); // When processInstanceService.searchOpenProcessInstancesSupervisedBy(userId, queryOptions); } @Test public void getNumberOfOpenProcessInstancesInvolvingUser_should_return_number_of_open_process_instance_involving_user() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); final long userId = 198L; final long number = 2L; doReturn(number).when(readPersistenceService).getNumberOfEntities(eq(SProcessInstance.class), eq("InvolvingUser"), eq(queryOptions), anyMap()); // When final long result = processInstanceService.getNumberOfOpenProcessInstancesInvolvingUser(userId, queryOptions); // Then assertEquals("The result should be equals to the number returned by the mock.", number, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfOpenProcessInstancesInvolvingUser_should_throw_exception_when_persistence_service_failed() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); final long userId = 198L; doThrow(new SBonitaReadException("plop")).when(readPersistenceService).getNumberOfEntities( eq(SProcessInstance.class), eq("InvolvingUser"), eq(queryOptions), anyMap()); // When processInstanceService.getNumberOfOpenProcessInstancesInvolvingUser(userId, queryOptions); } @Test public void searchOpenProcessInstancesInvolvingUser_should_return_list_of_open_process_instance_involving_user() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); final long userId = 198L; final List list = asList(mock(ProcessInstance.class)); doReturn(list).when(readPersistenceService).searchEntity(eq(SProcessInstance.class), eq("InvolvingUser"), eq(queryOptions), anyMap()); // When final List result = processInstanceService.searchOpenProcessInstancesInvolvingUser(userId, queryOptions); // Then assertEquals("The result should be equals to the list returned by the mock.", list, result); } @Test(expected = SBonitaReadException.class) public void searchOpenProcessInstancesInvolvingUser_should_throw_exception_when_persistence_service_failed() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); final long userId = 198L; doThrow(new SBonitaReadException("plop")).when(readPersistenceService).searchEntity(eq(SProcessInstance.class), eq("InvolvingUser"), eq(queryOptions), anyMap()); // When processInstanceService.searchOpenProcessInstancesInvolvingUser(userId, queryOptions); } @Test public void getNumberOfOpenProcessInstancesInvolvingUsersManagedBy_should_return_number_of_open_process_instance_involving_users_managed_by() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); final long userId = 198L; final long number = 2L; doReturn(number).when(readPersistenceService).getNumberOfEntities(eq(SProcessInstance.class), eq("InvolvingUsersManagedBy"), eq(queryOptions), anyMap()); // When final long result = processInstanceService.getNumberOfOpenProcessInstancesInvolvingUsersManagedBy(userId, queryOptions); // Then assertEquals("The result should be equals to the number returned by the mock.", number, result); } @Test(expected = SBonitaReadException.class) public void getNumberOfOpenProcessInstancesInvolvingUsersManagedBy_should_throw_exception_when_persistence_service_failed() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); final long userId = 198L; doThrow(new SBonitaReadException("plop")).when(readPersistenceService).getNumberOfEntities( eq(SProcessInstance.class), eq("InvolvingUsersManagedBy"), eq(queryOptions), anyMap()); // When processInstanceService.getNumberOfOpenProcessInstancesInvolvingUsersManagedBy(userId, queryOptions); } @Test public void searchOpenProcessInstancesInvolvingUsersManagedBy_should_return_list_of_open_process_instance_involving_users_managed_by() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); final long userId = 198L; final List list = asList(mock(ProcessInstance.class)); doReturn(list).when(readPersistenceService).searchEntity(eq(SProcessInstance.class), eq("InvolvingUsersManagedBy"), eq(queryOptions), anyMap()); // When final List result = processInstanceService .searchOpenProcessInstancesInvolvingUsersManagedBy(userId, queryOptions); // Then assertEquals("The result should be equals to the list returned by the mock.", list, result); } @Test(expected = SBonitaReadException.class) public void searchOpenProcessInstancesInvolvingUsersManagedBy_should_throw_exception_when_persistence_service_failed() throws Exception { // Given final QueryOptions queryOptions = new QueryOptions(0, 10); final long userId = 198L; doThrow(new SBonitaReadException("plop")).when(readPersistenceService).searchEntity(eq(SProcessInstance.class), eq("InvolvingUsersManagedBy"), eq(queryOptions), anyMap()); // When processInstanceService.searchOpenProcessInstancesInvolvingUsersManagedBy(userId, queryOptions); } @Test public void getArchivedProcessInstancesInAllStates_should_return_list_of_archived_process_instance() throws Exception { // Given final List archivedProcessInstanceIds = asList(41L); final Map parameters = Collections.singletonMap("sourceObjectIds", (Object) archivedProcessInstanceIds); final SelectListDescriptor selectListDescriptor = new SelectListDescriptor( "getArchivedProcessInstancesInAllStates", parameters, SAProcessInstance.class, new QueryOptions(0, archivedProcessInstanceIds.size())); final List saProcessInstances = asList(mock(SAProcessInstance.class)); doReturn(saProcessInstances).when(readPersistenceService).selectList(selectListDescriptor); // When final List archivedProcessInstances = processInstanceService .getArchivedProcessInstancesInAllStates(archivedProcessInstanceIds); // Then assertEquals("The result should be equals to the list returned by the mock.", saProcessInstances, archivedProcessInstances); verify(readPersistenceService).selectList(selectListDescriptor); } @Test(expected = SProcessInstanceReadException.class) public void getArchivedProcessInstancesInAllStates_should_throw_exception_when_there_is_problem() throws Exception { // Given final List archivedProcessInstanceIds = asList(41L); final Map parameters = Collections.singletonMap("sourceObjectIds", (Object) archivedProcessInstanceIds); final SelectListDescriptor selectListDescriptor = new SelectListDescriptor( "getArchivedProcessInstancesInAllStates", parameters, SAProcessInstance.class, new QueryOptions(0, archivedProcessInstanceIds.size())); doThrow(new SBonitaReadException("plop")).when(readPersistenceService).selectList(selectListDescriptor); // When processInstanceService.getArchivedProcessInstancesInAllStates(archivedProcessInstanceIds); } @Test public void getArchivedProcessInstance_should_return_archived_process_instance() throws Exception { // Given final long archivedProcessInstanceId = 41L; final Map parameters = Collections.singletonMap("id", (Object) archivedProcessInstanceId); final SelectOneDescriptor selectOneDescriptor = new SelectOneDescriptor( "getArchivedProcessInstance", parameters, SAProcessInstance.class); final SAProcessInstance saProcessInstance = mock(SAProcessInstance.class); doReturn(saProcessInstance).when(readPersistenceService).selectOne(selectOneDescriptor); // When final SAProcessInstance archivedProcessInstance = processInstanceService .getArchivedProcessInstance(archivedProcessInstanceId); // Then assertEquals("The result should be equals to the list returned by the mock.", saProcessInstance, archivedProcessInstance); verify(readPersistenceService).selectOne(selectOneDescriptor); } @Test(expected = SProcessInstanceReadException.class) public void getArchivedProcessInstance_should_throw_exception_when_there_is_problem() throws Exception { // Given final long archivedProcessInstanceId = 41L; final Map parameters = Collections.singletonMap("id", (Object) archivedProcessInstanceId); final SelectOneDescriptor selectOneDescriptor = new SelectOneDescriptor( "getArchivedProcessInstance", parameters, SAProcessInstance.class); doThrow(new SBonitaReadException("plop")).when(readPersistenceService).selectOne(selectOneDescriptor); // When processInstanceService.getArchivedProcessInstance(archivedProcessInstanceId); } @Test public void deleteFlowNodeInstanceElements_should_delete_child_process_instance_when_flownode_is_sub_process() throws Exception { final SFlowNodeInstance flowNodeInstance = new SSubProcessActivityInstance(); deleteFlowNodeInstanceElements_should_delete_child_process_instance(flowNodeInstance); } @Test public void deleteFlowNodeInstanceElements_should_delete_child_process_instance_when_flownode_is_call_activity() throws Exception { final SFlowNodeInstance flowNodeInstance = new SCallActivityInstance(); deleteFlowNodeInstanceElements_should_delete_child_process_instance(flowNodeInstance); } private void deleteFlowNodeInstanceElements_should_delete_child_process_instance( final SFlowNodeInstance flowNodeInstance) throws SBonitaException { // Given final SProcessDefinition processDefinition = mock(SProcessDefinition.class); doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer(); doNothing().when(processInstanceService).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition); doNothing().when(processInstanceService).deleteConnectorInstancesIfNecessary(flowNodeInstance, processDefinition); final SProcessInstance sProcessInstance = mock(SProcessInstance.class); doReturn(sProcessInstance).when(processInstanceService).getChildOfActivity(flowNodeInstance.getId()); doNothing().when(processInstanceService).deleteProcessInstance(sProcessInstance); // When processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition); // Then verify(processInstanceService).deleteProcessInstance(sProcessInstance); } @Test public void deleteFlowNodeInstanceElements_should_log_exception_when_getChildOfActivity_failed_and_log_is_active() throws Exception { // Given final SFlowNodeInstance flowNodeInstance = new SSubProcessActivityInstance(); final SProcessDefinition processDefinition = mock(SProcessDefinition.class); doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer(); doNothing().when(processInstanceService).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition); doNothing().when(processInstanceService).deleteConnectorInstancesIfNecessary(flowNodeInstance, processDefinition); final SProcessInstanceNotFoundException exception = new SProcessInstanceNotFoundException(6); doThrow(exception).when(processInstanceService).getChildOfActivity(flowNodeInstance.getId()); // When systemOutRule.clearLog(); processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition); // Then assertThat(systemOutRule.getLog()).contains("Process instance with id <6> not found"); } @Test public void deleteFlowNodeInstanceElements_should_only_log_in_debug_when_getChildOfActivity_failed() throws Exception { // Given final SFlowNodeInstance flowNodeInstance = new SCallActivityInstance(); final SProcessDefinition processDefinition = mock(SProcessDefinition.class); doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer(); doNothing().when(processInstanceService).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition); doNothing().when(processInstanceService).deleteConnectorInstancesIfNecessary(flowNodeInstance, processDefinition); final SProcessInstanceNotFoundException exception = new SProcessInstanceNotFoundException(6); doThrow(exception).when(processInstanceService).getChildOfActivity(flowNodeInstance.getId()); // When processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition); assertThat(systemOutRule.getLog()).containsPattern("DEBUG.*.Process instance with id <6> not found"); } @Test public void deleteFlowNodeInstanceElements_should_call_deleteWaitingEvents_when_flownode_is_type_INTERMEDIATE_CATCH_EVENT() throws Exception { // Given final SFlowNodeInstance flowNodeInstance = new SIntermediateCatchEventInstance(); flowNodeInstance.setFlowNodeDefinitionId(42); SIntermediateThrowEventDefinitionImpl definition = new SIntermediateThrowEventDefinitionImpl(42, "inter"); definition.addMessageEventTriggerDefinition(new SThrowMessageEventTriggerDefinitionImpl()); final SProcessDefinition processDefinition = aProcess().with(definition).done(); // When processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition); // Then verify(eventInstanceService).deleteWaitingEvents(flowNodeInstance); } @Test public void deleteFlowNodeInstanceElements_should_call_deleteWaitingEvents_when_flownode_is_type_RECEIVE_TASK() throws Exception { // Given final SFlowNodeInstance flowNodeInstance = new SReceiveTaskInstance(); final SProcessDefinition processDefinition = mock(SProcessDefinition.class); doNothing().when(processInstanceService).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition); doNothing().when(processInstanceService).deleteConnectorInstancesIfNecessary(flowNodeInstance, processDefinition); // When processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition); // Then verify(eventInstanceService).deleteWaitingEvents(flowNodeInstance); } @Test public void deleteFlowNodeInstanceElements_should_not_call_deleteWaitingEvents_when_flownode_is_type_START_EVENT_with_no_trigger() throws Exception { // Given final SFlowNodeInstance flowNodeInstance = new SStartEventInstance(); flowNodeInstance.setFlowNodeDefinitionId(42); final SProcessDefinition processDefinition = aProcess().with(new SStartEventDefinitionImpl(42, "start")).done(); // When processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition); // Then verify(eventInstanceService, never()).deleteWaitingEvents(flowNodeInstance); } @Test public void deleteFlowNodeInstanceElements_should_call_deleteWaitingEvents_when_flownode_is_type_START_EVENT() throws Exception { // Given final SFlowNodeInstance flowNodeInstance = new SStartEventInstance(); flowNodeInstance.setFlowNodeDefinitionId(42); SStartEventDefinitionImpl start = new SStartEventDefinitionImpl(42, "start"); start.addSignalEventTrigger(new SCatchSignalEventTriggerDefinitionImpl("signal")); final SProcessDefinition processDefinition = aProcess().with(start).done(); // When processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition); // Then verify(eventInstanceService).deleteWaitingEvents(flowNodeInstance); } @Test public void deleteFlowNodeInstanceElements_should_call_deleteWaitingEvents_when_flownode_is_type_BOUNDARY_EVENT() throws Exception { // Given final SFlowNodeInstance flowNodeInstance = new SBoundaryEventInstance(); flowNodeInstance.setFlowNodeDefinitionId(42); SBoundaryEventDefinitionImpl boundary = new SBoundaryEventDefinitionImpl(42, "boundary"); boundary.addTimerEventTrigger(new STimerEventTriggerDefinitionImpl(null, null)); final SProcessDefinition processDefinition = aProcess().with(boundary).done(); // When processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition); // Then verify(eventInstanceService).deleteWaitingEvents(flowNodeInstance); } @Test public void deleteFlowNodeInstanceElements_should_call_deletePendingMappings_when_flownode_is_type_USER_TASK() throws Exception { // Given final SFlowNodeInstance flowNodeInstance = new SUserTaskInstance("name", 3L, 6L, 9L, 12L, STaskPriority.ABOVE_NORMAL, 7L, 8L); final SProcessDefinition processDefinition = mock(SProcessDefinition.class); doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer(); doNothing().when(processInstanceService).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition); doNothing().when(processInstanceService).deleteConnectorInstancesIfNecessary(flowNodeInstance, processDefinition); // When processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition); // Then verify(activityInstanceService).deletePendingMappings(flowNodeInstance.getId()); } @Test public void deleteFlowNodeInstanceElements_should_call_deletePendingMappings_when_flownode_is_type_MANUAL_TASK() throws Exception { // Given final SFlowNodeInstance flowNodeInstance = new SManualTaskInstance("name", 1L, 2L, 3L, 4L, STaskPriority.ABOVE_NORMAL, 5L, 6L); final SProcessDefinition processDefinition = mock(SProcessDefinition.class); doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer(); doNothing().when(processInstanceService).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition); doNothing().when(processInstanceService).deleteConnectorInstancesIfNecessary(flowNodeInstance, processDefinition); // When processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition); // Then verify(activityInstanceService).deletePendingMappings(flowNodeInstance.getId()); } @Test public void deleteFlowNodeInstanceElements_should_just_deleteDataInstancesIfNecessary_and_deleteConnectorInstancesIfNecessary_when_flownode_is_loop() throws Exception { // Given final SFlowNodeInstance flowNodeInstance = new SLoopActivityInstance(); final SProcessDefinition processDefinition = mock(SProcessDefinition.class); doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer(); doNothing().when(processInstanceService).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition); doNothing().when(processInstanceService).deleteConnectorInstancesIfNecessary(flowNodeInstance, processDefinition); // When processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition); // Then verify(activityInstanceService, never()).deletePendingMappings(flowNodeInstance.getId()); verify(processInstanceService, never()).deleteSubProcess(flowNodeInstance, processDefinition); } @Test public void deleteFlowNodeInstanceElements_should_always_delete_bpm_failures() throws Exception { // Given final SFlowNodeInstance flowNodeInstance = new SGatewayInstance(); flowNodeInstance.setId(1L); final SProcessDefinition processDefinition = mock(SProcessDefinition.class); doReturn(mock(SFlowElementContainerDefinition.class)).when(processDefinition).getProcessContainer(); // When processInstanceService.deleteFlowNodeInstanceElements(flowNodeInstance, processDefinition); // Then verify(processInstanceService, never()).deleteDataInstancesIfNecessary(flowNodeInstance, processDefinition); verify(processInstanceService, never()).deleteConnectorInstancesIfNecessary(flowNodeInstance, processDefinition); verify(bpmFailureService).deleteFlowNodeFailures(1L); } @Test public void getNumberOfProcessInstances_should_call_getNumberOfEntities() throws Exception { final Map inputParameters = new HashMap(); inputParameters.put("processDefinitionId", 45L); final SelectOneDescriptor countDescriptor = new SelectOneDescriptor( "countProcessInstancesOfProcessDefinition", inputParameters, SProcessInstance.class); when(readPersistenceService.selectOne(countDescriptor)).thenReturn(4L); processInstanceService.getNumberOfProcessInstances(45L); verify(readPersistenceService).selectOne(eq(countDescriptor)); } @Test(expected = SBonitaReadException.class) public void getNumberOfProcessInstances_should_throw_a_read_exception_if_getNumberOfEntities_does_it() throws Exception { when(readPersistenceService.selectOne(ArgumentMatchers.> any())).thenThrow( new SBonitaReadException("error")); processInstanceService.getNumberOfProcessInstances(45L); } @Test public void _IN_REQUEST_SIZE_should_be_100_for_good_performance() { // In any case, it should be <= 1000, as this is the limit of the most restrictive RDBMS we support: Oracle. assertThat(IN_REQUEST_SIZE).isLessThanOrEqualTo(100); } private SProcessDefinitionBuilder aProcess() { return new SProcessDefinitionBuilder(); } private class SProcessDefinitionBuilder { private SProcessDefinitionImpl processDefinition = new SProcessDefinitionImpl("aProcess", "1.0"); SProcessDefinitionBuilder with(SEventDefinition eventDefinition) { ((SFlowElementContainerDefinitionImpl) processDefinition.getProcessContainer()).addEvent(eventDefinition); return this; } SProcessDefinition done() { return processDefinition; } } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/impl/RefBusinessDataServiceImplTest.java ================================================ /** * 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.core.process.instance.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceCreationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceModificationException; import org.bonitasoft.engine.core.process.instance.api.exceptions.business.data.SRefBusinessDataInstanceNotFoundException; import org.bonitasoft.engine.core.process.instance.model.business.data.SProcessSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.model.business.data.SSimpleRefBusinessDataInstance; import org.bonitasoft.engine.core.process.instance.recorder.SelectBusinessDataDescriptorBuilder; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class RefBusinessDataServiceImplTest { @Mock private ReadPersistenceService persistence; @Mock private EventService eventService; @Mock private Recorder recorder; @Mock QueriableLoggerService loggerService; @InjectMocks private RefBusinessDataServiceImpl service; private SSimpleRefBusinessDataInstance buildSRefBusinessDataInstance() { final SSimpleRefBusinessDataInstance instance = new SProcessSimpleRefBusinessDataInstance(); instance.setName("myLeaveRequest"); instance.setDataId(45l); instance.setDataClassName("org.bonitasoft.LeaveRequest"); return instance; } private SelectOneDescriptor buildGetDescriptor(final String name, final long dataId) { return SelectBusinessDataDescriptorBuilder.getSRefBusinessDataInstance(name, dataId); } @Test public void getRefBusinessDataInstanceReturnsTheRightObject() throws Exception { final String name = "myLeaveRequest"; final long dataId = 45; final SRefBusinessDataInstance expectedDataInstance = buildSRefBusinessDataInstance(); when(persistence.selectOne(buildGetDescriptor(name, dataId))).thenReturn(expectedDataInstance); final SRefBusinessDataInstance actualDataInstance = service.getRefBusinessDataInstance(name, dataId); assertThat(actualDataInstance).isEqualTo(expectedDataInstance); } @Test(expected = SRefBusinessDataInstanceNotFoundException.class) public void getRefBusinessDataInstanceThrowsAnExceptionWhenObjectDoesNotExist() throws Exception { final String name = "myLeaveRequest"; final long dataId = 45; when(persistence.selectOne(buildGetDescriptor(name, dataId))).thenReturn(null); service.getRefBusinessDataInstance(name, dataId); } @Test(expected = SBonitaReadException.class) public void getRefBusinessDataInstanceThrowsAnExceptionWhenPeristenceServiceIsDown() throws Exception { final String name = "myLeaveRequest"; final long dataId = 45; when(persistence.selectOne(buildGetDescriptor(name, dataId))).thenThrow(new SBonitaReadException("down")); service.getRefBusinessDataInstance(name, dataId); } @Test public void updateRefBusinessData() throws Exception { final SSimpleRefBusinessDataInstance refBusinessDataInstance = buildSRefBusinessDataInstance(); final long dataId = 564654654654654l; final Map fields = new HashMap(); fields.put("dataId", dataId); final UpdateRecord updateRecord = UpdateRecord.buildSetFields(refBusinessDataInstance, fields); service.updateRefBusinessDataInstance(refBusinessDataInstance, dataId); verify(recorder).recordUpdate(updateRecord, RefBusinessDataService.REF_BUSINESS_DATA_INSTANCE); } @Test(expected = SRefBusinessDataInstanceModificationException.class) public void updateRefBusinessDataThrowException() throws Exception { final SSimpleRefBusinessDataInstance refBusinessDataInstance = buildSRefBusinessDataInstance(); final long dataId = 564654654654654l; final Map fields = new HashMap(); fields.put("dataId", dataId); final UpdateRecord updateRecord = UpdateRecord.buildSetFields(refBusinessDataInstance, fields); doThrow(new SRecorderException("ouch!")).when(recorder).recordUpdate(updateRecord, RefBusinessDataService.REF_BUSINESS_DATA_INSTANCE); service.updateRefBusinessDataInstance(refBusinessDataInstance, dataId); } @Test public void addRefBusinessData() throws Exception { final SRefBusinessDataInstance refBusinessDataInstance = buildSRefBusinessDataInstance(); service.addRefBusinessDataInstance(refBusinessDataInstance); verify(recorder).recordInsert(new InsertRecord(refBusinessDataInstance), RefBusinessDataService.REF_BUSINESS_DATA_INSTANCE); } @Test(expected = SRefBusinessDataInstanceCreationException.class) public void addRefBusinessDataThrowException() throws Exception { final SRefBusinessDataInstance refBusinessDataInstance = buildSRefBusinessDataInstance(); doThrow(new SRecorderException("ouch!")).when(recorder).recordInsert(new InsertRecord(refBusinessDataInstance), RefBusinessDataService.REF_BUSINESS_DATA_INSTANCE); service.addRefBusinessDataInstance(refBusinessDataInstance); } @Test public void getRefBusinessDataInstancesShouldCallThePersistenceService() throws Exception { final long processIntanceId = 789798798L; final int startIndex = 0; final int maxResults = 100; service.getRefBusinessDataInstances(processIntanceId, startIndex, maxResults); verify(persistence) .selectList(SelectBusinessDataDescriptorBuilder.getSRefBusinessDataInstances(processIntanceId, startIndex, maxResults)); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/archive/impl/SAHumanTaskInstanceImplTest.java ================================================ /** * 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.core.process.instance.model.archive.impl; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.core.process.instance.model.STaskPriority; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; import org.bonitasoft.engine.core.process.instance.model.archive.SAUserTaskInstance; import org.junit.Test; /** * author Emmanuel Duchastenier */ public class SAHumanTaskInstanceImplTest { @Test public void creatingArchiveShouldCopyAllRelevantAttibute() { final SUserTaskInstance taskInstance = new SUserTaskInstance("taskName", 147L, 2565412L, 874555L, 7L, STaskPriority.UNDER_NORMAL, 7777L, 8888L); final long claimedDate = System.currentTimeMillis(); taskInstance.setClaimedDate(claimedDate); taskInstance.setStateId(3); taskInstance.setStateName("estado"); taskInstance.setAssigneeId(8412L); taskInstance.setDescription("tarea de usuario"); taskInstance.setDisplayDescription("tarea del usuario Pepo"); taskInstance.setDisplayName("task for everyone"); taskInstance.setExecutedBy(987987L); taskInstance.setExecutedBySubstitute(11111L); final long expectedEndDate = System.currentTimeMillis() + 10; taskInstance.setExpectedEndDate(expectedEndDate); final SAUserTaskInstance archivedTaskInstance = new SAUserTaskInstance(taskInstance); assertThat(archivedTaskInstance.getType()).isEqualTo(taskInstance.getType()); assertThat(archivedTaskInstance.getActorId()).isEqualTo(7L); assertThat(archivedTaskInstance.getAssigneeId()).isEqualTo(8412L); assertThat(archivedTaskInstance.getExpectedEndDate()).isEqualTo(expectedEndDate); assertThat(archivedTaskInstance.getPriority()).isEqualTo(STaskPriority.UNDER_NORMAL); assertThat(archivedTaskInstance.getClaimedDate()).isEqualTo(claimedDate); assertThat(archivedTaskInstance.getDescription()).isEqualTo("tarea de usuario"); assertThat(archivedTaskInstance.getDisplayDescription()).isEqualTo("tarea del usuario Pepo"); assertThat(archivedTaskInstance.getDisplayName()).isEqualTo("task for everyone"); assertThat(archivedTaskInstance.getExecutedBy()).isEqualTo(987987L); assertThat(archivedTaskInstance.getExecutedBySubstitute()).isEqualTo(11111L); assertThat(archivedTaskInstance.getName()).isEqualTo("taskName"); assertThat(archivedTaskInstance.getSourceObjectId()).isEqualTo(taskInstance.getId()); assertThat(archivedTaskInstance.getStateName()).isEqualTo("estado"); assertThat(archivedTaskInstance.getStateId()).isEqualTo(3); assertThat(archivedTaskInstance.getParentActivityInstanceId()) .isEqualTo(taskInstance.getParentActivityInstanceId()); assertThat(archivedTaskInstance.getParentContainerId()).isEqualTo(874555L); assertThat(archivedTaskInstance.getParentProcessInstanceId()) .isEqualTo(taskInstance.getParentProcessInstanceId()); assertThat(archivedTaskInstance.getRootContainerId()).isEqualTo(2565412L); assertThat(archivedTaskInstance.getFlowNodeDefinitionId()).isEqualTo(147L); assertThat(archivedTaskInstance.getLogicalGroup(0)).isEqualTo(7777L); assertThat(archivedTaskInstance.getLogicalGroup(1)).isEqualTo(8888L); } @Test public void should_allow_null_expected_date() { //given final SUserTaskInstance taskInstance = new SUserTaskInstance("taskName", 147L, 2565412L, 874555L, 7L, STaskPriority.UNDER_NORMAL, 7777L, 8888L); //when taskInstance.setExpectedEndDate(null); final SAUserTaskInstance archivedTaskInstance = new SAUserTaskInstance(taskInstance); //then assertThat(archivedTaskInstance.getExpectedEndDate()).isNull(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/builder/impl/SFlowNodeInstanceBuilderImplTest.java ================================================ /** * 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.core.process.instance.model.builder.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import org.bonitasoft.engine.core.process.instance.api.states.FlowNodeState; import org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance; import org.junit.Test; public class SFlowNodeInstanceBuilderImplTest { @Test public void setState_should_set_all_fields_related_to_state() throws Exception { //given SAutomaticTaskInstanceBuilderImpl builder = new SAutomaticTaskInstanceBuilderImpl(new SAutomaticTaskInstance()); int stateId = 100; String stateName = "mockState"; FlowNodeState state = mock(FlowNodeState.class); given(state.getId()).willReturn(stateId); given(state.getName()).willReturn(stateName); given(state.isTerminal()).willReturn(true); given(state.isStable()).willReturn(true); //when builder.setState(state); //then SAutomaticTaskInstance taskInstance = builder.done(); assertThat(taskInstance.getStateId()).isEqualTo(stateId); assertThat(taskInstance.getStateName()).isEqualTo(stateName); assertThat(taskInstance.isStable()).isTrue(); assertThat(taskInstance.isTerminal()).isTrue(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/event/handling/SMessageInstanceTest.java ================================================ /** * 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.core.process.instance.model.event.handling; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class SMessageInstanceTest { @Test public void should_have_a_creation_date() { SMessageInstance sMessageInstance = new SMessageInstance("myMessage", "target", "target", 1234L, "fn"); assertThat(sMessageInstance.getCreationDate()).isGreaterThan(0); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/event/impl/SBoundaryEventInstanceImplTest.java ================================================ /** * 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.core.process.instance.model.event.impl; import static org.junit.Assert.assertFalse; import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance; import org.junit.Before; import org.junit.Test; public class SBoundaryEventInstanceImplTest { private SBoundaryEventInstance boundary; @Before public void setUp() { boundary = new SBoundaryEventInstance(); } @Test public void mustExecutOnAbortOrCancelProcess_return_false_if_stable() { boundary.setStable(true); assertFalse(boundary.mustExecuteOnAbortOrCancelProcess()); } // for a boundary we never must execute the flow node on abort the process instance because it will be aborted by the related activity @Test public void mustExecutOnAbortOrCancelProcess_return_false_if_not_stable() { boundary.setStable(false); assertFalse(boundary.mustExecuteOnAbortOrCancelProcess()); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/impl/SGatewayInstanceImplTest.java ================================================ /** * 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.core.process.instance.model.impl; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance; import org.junit.Test; public class SGatewayInstanceImplTest { @Test public void isFinished_should_return_true_when_hitbys_starts_with_FINISH() throws Exception { //given SGatewayInstance gatewayInstance = buildGateWithHitBys("FINISH:1"); //when gatewayInstance.isFinished(); //then assertThat(gatewayInstance.isFinished()).isTrue(); } @Test public void isFinished_should_return_false_when_hitbys_doesnt_start_with_FINISH() throws Exception { //given SGatewayInstance gatewayInstance = buildGateWithHitBys("1,2"); //when gatewayInstance.isFinished(); //then assertThat(gatewayInstance.isFinished()).isFalse(); } @Test public void isFinished_should_return_false_when_hitbys_is_null() throws Exception { //given SGatewayInstance gatewayInstance = buildGateWithHitBys(null); //when gatewayInstance.isFinished(); //then assertThat(gatewayInstance.isFinished()).isFalse(); } private SGatewayInstance buildGateWithHitBys(final String hitBys) { SGatewayInstance gatewayInstance = new SGatewayInstance(); gatewayInstance.setHitBys(hitBys); return gatewayInstance; } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/impl/SLoopActivityInstanceImplTest.java ================================================ /** * 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.core.process.instance.model.impl; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance; import org.junit.Before; import org.junit.Test; public class SLoopActivityInstanceImplTest { private SLoopActivityInstance loop; @Before public void setUp() { loop = new SLoopActivityInstance(); } @Test public void mustExecuteOnAbortOrCancelProcess_return_false_when_is_stable() { // given loop.setStable(true); // when boolean executeOnAbortOrCancelProcess = loop.mustExecuteOnAbortOrCancelProcess(); // then assertThat(executeOnAbortOrCancelProcess).isFalse(); } @Test public void mustExecuteOnAbortOrCancelProcess_return_false_when_is_not_stable() { // given loop.setStable(false); // when boolean executeOnAbortOrCancelProcess = loop.mustExecuteOnAbortOrCancelProcess(); // then assertThat(executeOnAbortOrCancelProcess).isFalse(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/impl/SMultiInstanceActivityInstanceImplTest.java ================================================ /** * 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.core.process.instance.model.impl; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance; import org.junit.Before; import org.junit.Test; public class SMultiInstanceActivityInstanceImplTest { private SMultiInstanceActivityInstance multi; @Before public void setUp() { multi = new SMultiInstanceActivityInstance(); } @Test public void mustExecuteOnAbortOrCancelProcess_should_return_false_when_is_stable() { //given multi.setStable(true); //when final boolean executeOnAbortOrCancelProcess = multi.mustExecuteOnAbortOrCancelProcess(); //then assertThat(executeOnAbortOrCancelProcess).isFalse(); } @Test public void mustExecuteOnAbortOrCancelProcess_should_return_false_when_is_not_stable() { //given multi.setStable(false); //when final boolean executeOnAbortOrCancelProcess = multi.mustExecuteOnAbortOrCancelProcess(); //then assertThat(executeOnAbortOrCancelProcess).isFalse(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/impl/SProcessInstanceImplTest.java ================================================ /** * 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.core.process.instance.model.impl; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.junit.Test; public class SProcessInstanceImplTest { @Test public void defaultInterruptingEventIdShouldBeMinusOne() { assertThat(SProcessInstance.builder().build().getInterruptingEventId()).isEqualTo(-1L); } @Test public void should_be_root_instance_when_callerId_is_less_than_zero() throws Exception { //given SProcessInstance instance = new SProcessInstance(); instance.setCallerId(-1); //then assertThat(instance.isRootInstance()).isTrue(); } @Test public void should_not_be_root_instance_when_callerId_is_greater_than_zero() throws Exception { //given SProcessInstance instance = new SProcessInstance(); instance.setCallerId(1); //then assertThat(instance.isRootInstance()).isFalse(); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/java/org/bonitasoft/engine/core/process/instance/model/impl/SUserTaskInstanceImplTest.java ================================================ /** * 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.core.process.instance.model.impl; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance; import org.junit.Before; import org.junit.Test; public class SUserTaskInstanceImplTest { private SUserTaskInstance task; @Before public void setUp() { task = new SUserTaskInstance(); } @Test public void mustExecuteOnAbortOrCancelProcess_returns_true_if_stable_state() { task.setStable(true); assertTrue(task.mustExecuteOnAbortOrCancelProcess()); } @Test public void mustExecuteOnAbortOrCancelProcess_returns_false_if_stable_state() { task.setStable(false); assertFalse(task.mustExecuteOnAbortOrCancelProcess()); } } ================================================ FILE: bpm/bonita-core/bonita-process-instance/src/test/resources/logback-test.xml ================================================ [%-5level] %msg%n ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/build.gradle ================================================ dependencies { api project(':services:bonita-commons') api project(':services:bonita-builder') api project(':services:bonita-events') api project(':services:bonita-log') api project(':services:bonita-persistence') annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/SProcessDefinitionNotFoundException.java ================================================ /** * 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.supervisor.mapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Yanyan Liu */ public class SProcessDefinitionNotFoundException extends SBonitaException { private static final long serialVersionUID = -7591704670782032169L; public SProcessDefinitionNotFoundException(final String message) { super(message); } public SProcessDefinitionNotFoundException(final Throwable cause) { super(cause); } public SProcessDefinitionNotFoundException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/SSupervisorAlreadyExistsException.java ================================================ /** * 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.supervisor.mapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Yanyan Liu */ public class SSupervisorAlreadyExistsException extends SBonitaException { private static final long serialVersionUID = 5396310718485439404L; public SSupervisorAlreadyExistsException(final String message) { super(message); } public SSupervisorAlreadyExistsException(final Throwable cause) { super(cause); } public SSupervisorAlreadyExistsException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/SSupervisorCreationException.java ================================================ /** * 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.supervisor.mapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Yanyan Liu */ public class SSupervisorCreationException extends SBonitaException { private static final long serialVersionUID = 2848214220076867957L; public SSupervisorCreationException(final String message) { super(message); } public SSupervisorCreationException(final Throwable cause) { super(cause); } public SSupervisorCreationException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/SSupervisorDeletionException.java ================================================ /** * 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.supervisor.mapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Yanyan Liu */ public class SSupervisorDeletionException extends SBonitaException { private static final long serialVersionUID = 3748312313790371984L; public SSupervisorDeletionException(final String message) { super(message); } public SSupervisorDeletionException(final Throwable cause) { super(cause); } public SSupervisorDeletionException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/SSupervisorException.java ================================================ /** * 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.supervisor.mapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Yanyan Liu */ public class SSupervisorException extends SBonitaException { private static final long serialVersionUID = 5245682138584055122L; public SSupervisorException(final String message, final Throwable cause) { super(message, cause); } public SSupervisorException(final String message) { super(message); } public SSupervisorException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/SSupervisorNotFoundException.java ================================================ /** * 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.supervisor.mapping; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Yanyan Liu * @author Celine Souchet */ public class SSupervisorNotFoundException extends SBonitaException { private static final long serialVersionUID = 2848214220076867957L; public SSupervisorNotFoundException(final String message) { super(message); } public SSupervisorNotFoundException(final Throwable cause) { super(cause); } public SSupervisorNotFoundException(final String message, final Throwable cause) { super(message, cause); } public SSupervisorNotFoundException(final Long userId, final Long roleId, final Long groupId, final Long processDefinitionId) { super("No supervisor was found !!"); setUserIdOnContext(userId); setRoleIdOnContext(roleId); setGroupIdOnContext(groupId); setProcessDefinitionIdOnContext(processDefinitionId); } } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/SupervisorMappingService.java ================================================ /** * 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.supervisor.mapping; import java.util.List; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; /** * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet * @since 6.0 */ public interface SupervisorMappingService { String SUPERVISOR = "SUPERVISOR"; /** * Create supervisor in DB according to the given supervisor * * @param supervisor * a SSupervisor object * @return the new created supervisor * @throws SSupervisorCreationException */ SProcessSupervisor createProcessSupervisor(SProcessSupervisor supervisor) throws SSupervisorCreationException; /** * get supervisor without display name by its id * * @param supervisorId * identifier of supervisor * @return the supervisor with id equals the parameter * @throws SSupervisorNotFoundException */ SProcessSupervisor getProcessSupervisor(long supervisorId) throws SSupervisorNotFoundException; /** * Delete the id specified supervisor * * @param supervisorId * identifier of supervisor * @throws SSupervisorNotFoundException * @throws SSupervisorDeletionException */ void deleteProcessSupervisor(long supervisorId) throws SSupervisorNotFoundException, SSupervisorDeletionException; /** * Delete the specific supervisor * * @param supervisor * the supervisor will be deleted * @throws SSupervisorDeletionException */ void deleteProcessSupervisor(SProcessSupervisor supervisor) throws SSupervisorDeletionException; /** * Delete all supervisors for the connected tenant * * @throws SSupervisorDeletionException * @since 6.1 */ void deleteAllProcessSupervisors() throws SSupervisorDeletionException; /** * Verify if the id specified user is the supervisor of id specified process definition * * @param processDefinitionId * identifier of process definition * @param userId * identifier of user * @return true if user is supervisor of the process, false otherwise * @throws SBonitaReadException */ Boolean isProcessSupervisor(long processDefinitionId, long userId) throws SBonitaReadException; /** * Search all supervisors suit to the specific criteria * * @param queryOptions * The QueryOptions object containing some query conditions * @return a list of SSupervisor objects * @throws SBonitaReadException */ List searchProcessSupervisors(QueryOptions queryOptions) throws SBonitaReadException; /** * Get total number of supervisors suit to the specific criteria * * @param searchOptions * The QueryOptions object containing some query conditions * @return a list of SSupervisor objects * @throws SBonitaReadException */ long getNumberOfProcessSupervisors(QueryOptions searchOptions) throws SBonitaReadException; } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/impl/SelectDescriptorBuilder.java ================================================ /** * 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.supervisor.mapping.impl; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; /** * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SelectDescriptorBuilder { public static SelectByIdDescriptor getSupervisor(final long supervisorId) { return new SelectByIdDescriptor(SProcessSupervisor.class, supervisorId); } public static SelectOneDescriptor getNumberOfSupervisors(final long processDefId) { final Map parameters = Collections.singletonMap("processDefId", (Object) processDefId); return new SelectOneDescriptor("getNumberOfSupervisorsOfProcessDef", parameters, SProcessSupervisor.class); } public static SelectOneDescriptor getSupervisor(final long processDefId, final long userId) { final Map parameters = new HashMap(); parameters.put("processDefId", processDefId); parameters.put("userId", userId); return new SelectOneDescriptor("getSupervisor", parameters, SProcessSupervisor.class); } public static SelectListDescriptor getProcessDefIdsOfUser(final long userId, final int fromIndex, final int maxResult, final OrderByType orderByType) { final Map parameters = new HashMap(); parameters.put("userId", userId); final OrderByOption orderByOption = new OrderByOption(SProcessSupervisor.class, SProcessSupervisor.PROCESS_DEF_ID_KEY, orderByType); final QueryOptions queryOptions = new QueryOptions(fromIndex, maxResult, Collections.singletonList(orderByOption)); return new SelectListDescriptor("getProcessDefIdsOfUser", parameters, SProcessSupervisor.class, queryOptions); } } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/impl/SupervisorMappingServiceImpl.java ================================================ /** * 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.supervisor.mapping.impl; import java.util.List; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteAllRecord; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.services.QueriableLoggerService; import org.bonitasoft.engine.supervisor.mapping.SSupervisorCreationException; import org.bonitasoft.engine.supervisor.mapping.SSupervisorDeletionException; import org.bonitasoft.engine.supervisor.mapping.SSupervisorNotFoundException; import org.bonitasoft.engine.supervisor.mapping.SupervisorMappingService; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisorLogBuilder; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisorLogBuilderFactory; /** * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SupervisorMappingServiceImpl implements SupervisorMappingService { private final ReadPersistenceService persistenceService; private final Recorder recorder; private final EventService eventService; private final QueriableLoggerService queriableLoggerService; public SupervisorMappingServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder, final EventService eventService, final QueriableLoggerService queriableLoggerService) { this.persistenceService = persistenceService; this.recorder = recorder; this.eventService = eventService; this.queriableLoggerService = queriableLoggerService; } @Override public SProcessSupervisor createProcessSupervisor(final SProcessSupervisor supervisor) throws SSupervisorCreationException { final SProcessSupervisorLogBuilder logBuilder = getQueriableLog(ActionType.CREATED, "Adding a new supervisor"); try { recorder.recordInsert(new InsertRecord(supervisor), SUPERVISOR); log(supervisor.getId(), SQueriableLog.STATUS_OK, logBuilder, "createSupervisor"); return supervisor; } catch (final SRecorderException re) { log(supervisor.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "createSupervisor"); throw new SSupervisorCreationException(re); } } @Override public SProcessSupervisor getProcessSupervisor(final long supervisorId) throws SSupervisorNotFoundException { final SelectByIdDescriptor selectByIdDescriptor = SelectDescriptorBuilder .getSupervisor(supervisorId); try { final SProcessSupervisor supervisor = persistenceService.selectById(selectByIdDescriptor); if (supervisor == null) { throw new SSupervisorNotFoundException(supervisorId + " does not refer to any supervisor"); } return supervisor; } catch (final SBonitaReadException bre) { throw new SSupervisorNotFoundException(bre); } } @Override public void deleteProcessSupervisor(final long supervisorId) throws SSupervisorNotFoundException, SSupervisorDeletionException { final SProcessSupervisor sSupervisor = getProcessSupervisor(supervisorId); deleteProcessSupervisor(sSupervisor); } @Override public void deleteProcessSupervisor(final SProcessSupervisor supervisor) throws SSupervisorDeletionException { final SProcessSupervisorLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, "deleting supervisor"); try { recorder.recordDelete(new DeleteRecord(supervisor), SUPERVISOR); log(supervisor.getId(), SQueriableLog.STATUS_OK, logBuilder, "createSupervisor"); } catch (final SRecorderException e) { log(supervisor.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "createSupervisor"); throw new SSupervisorDeletionException("Can't delete process supervisor " + supervisor, e); } } @Override public void deleteAllProcessSupervisors() throws SSupervisorDeletionException { try { final DeleteAllRecord record = new DeleteAllRecord(SProcessSupervisor.class, null); recorder.recordDeleteAll(record); } catch (final SRecorderException e) { throw new SSupervisorDeletionException("Can't delete all process supervisors.", e); } } private SProcessSupervisorLogBuilder getQueriableLog(final ActionType actionType, final String message) { final SProcessSupervisorLogBuilder logBuilder = BuilderFactory.get(SProcessSupervisorLogBuilderFactory.class) .createNewInstance(); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); return logBuilder; } private void initializeLogBuilder(final T logBuilder, final String message) { logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message); } private void updateLog(final ActionType actionType, final T logBuilder) { logBuilder.setActionType(actionType); } @Override public Boolean isProcessSupervisor(final long processDefinitionId, final long userId) throws SBonitaReadException { final SelectOneDescriptor descriptor = SelectDescriptorBuilder .getSupervisor(processDefinitionId, userId); final SProcessSupervisor supervisor = persistenceService.selectOne(descriptor); return supervisor != null; } @Override public List searchProcessSupervisors(final QueryOptions queryOptions) throws SBonitaReadException { return persistenceService.searchEntity(SProcessSupervisor.class, null, queryOptions, null); } @Override public long getNumberOfProcessSupervisors(final QueryOptions searchOptions) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SProcessSupervisor.class, null, searchOptions, null); } private void log(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder, final String callerMethodName) { logBuilder.actionScope(String.valueOf(objectId)); logBuilder.actionStatus(sQueriableLogStatus); logBuilder.objectId(objectId); final SQueriableLog log = logBuilder.build(); if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) { queriableLoggerService.log(this.getClass().getName(), callerMethodName, log); } } } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/model/SMemberType.java ================================================ /** * 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.supervisor.mapping.model; /** * @author Yanyan Liu */ public enum SMemberType { USER, GROUP, ROLE, MEMBERSHIP; } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/model/SProcessSupervisor.java ================================================ /** * 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.supervisor.mapping.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "processsupervisor") public class SProcessSupervisor implements PersistentObject { public static final String ID_KEY = "id"; public static final String USER_ID_KEY = "userId"; public static final String GROUP_ID_KEY = "groupId"; public static final String ROLE_ID_KEY = "roleId"; public static final String PROCESS_DEF_ID_KEY = "processDefId"; @Id private long id; @Column private long processDefId; @Builder.Default @Column private long userId = -1; @Builder.Default @Column private long groupId = -1; @Builder.Default @Column private long roleId = -1; public SProcessSupervisor(final long processDefId) { this.processDefId = processDefId; } } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/model/SProcessSupervisorLogBuilder.java ================================================ /** * 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.supervisor.mapping.model; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Yanyan Liu */ public interface SProcessSupervisorLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/model/SProcessSupervisorLogBuilderFactory.java ================================================ /** * 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.supervisor.mapping.model; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Yanyan Liu */ public interface SProcessSupervisorLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { SProcessSupervisorLogBuilder createNewInstance(); } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/model/impl/SProcessSupervisorLogBuilderFactoryImpl.java ================================================ /** * 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.supervisor.mapping.model.impl; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisorLogBuilder; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisorLogBuilderFactory; /** * @author Yanyan Liu */ public class SProcessSupervisorLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SProcessSupervisorLogBuilderFactory { @Override public SProcessSupervisorLogBuilder createNewInstance() { return new SProcessSupervisorLogBuilderImpl(); } @Override public String getObjectIdKey() { return SProcessSupervisorLogIndexesMapper.SUPERVISOR_INDEX_NAME; } } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/model/impl/SProcessSupervisorLogBuilderImpl.java ================================================ /** * 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.supervisor.mapping.model.impl; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; import org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisorLogBuilder; /** * @author Yanyan Liu */ public class SProcessSupervisorLogBuilderImpl extends CRUDELogBuilder implements SProcessSupervisorLogBuilder { private static final String PREFIX = "SUPERVISOR"; @Override public SPersistenceLogBuilder objectId(final long objectId) { this.queriableLogBuilder.numericIndex(SProcessSupervisorLogIndexesMapper.SUPERVISOR_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return PREFIX; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL) { if (log.getNumericIndex(SProcessSupervisorLogIndexesMapper.SUPERVISOR_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + "supervisor Id"); } } } } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/java/org/bonitasoft/engine/supervisor/mapping/model/impl/SProcessSupervisorLogIndexesMapper.java ================================================ /** * 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.supervisor.mapping.model.impl; /** * @author Yanyan Liu */ public class SProcessSupervisorLogIndexesMapper { public static final int SUPERVISOR_INDEX = 0; public static final String SUPERVISOR_INDEX_NAME = "numericIndex1"; } ================================================ FILE: bpm/bonita-core/bonita-supervisor-mapping/src/main/resources/org/bonitasoft/engine/supervisor/mapping/model/impl/hibernate/supervisor.queries.hbm.xml ================================================ SELECT COUNT(DISTINCT processsupervisor.id) FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor SELECT DISTINCT processsupervisor FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor SELECT COUNT(DISTINCT processsupervisor.id) FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SUser as user WHERE processsupervisor.userId = user.id SELECT DISTINCT processsupervisor FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SUser as user WHERE processsupervisor.userId = user.id SELECT COUNT(DISTINCT processsupervisor.id) FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE processsupervisor.groupId = group_.id SELECT DISTINCT processsupervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE processsupervisor.groupId = group_.id SELECT COUNT(DISTINCT processsupervisor.id) FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SRole AS role WHERE processsupervisor.roleId = role.id SELECT DISTINCT processsupervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SRole AS role WHERE processsupervisor.roleId = role.id SELECT COUNT(DISTINCT processsupervisor.id) FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SGroup AS group_, org.bonitasoft.engine.identity.model.SRole AS role WHERE processsupervisor.roleId = role.id AND processsupervisor.groupId = group_.id SELECT DISTINCT processsupervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SGroup AS group_, org.bonitasoft.engine.identity.model.SRole AS role WHERE processsupervisor.roleId = role.id AND processsupervisor.groupId = group_.id SELECT COUNT(DISTINCT processsupervisor.id) FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SUser as user, org.bonitasoft.engine.identity.model.SRole AS role WHERE (processsupervisor.userId <= 0 AND processsupervisor.roleId = role.id) OR (processsupervisor.userId = user.id AND processsupervisor.roleId <= 0) SELECT DISTINCT processsupervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SUser as user, org.bonitasoft.engine.identity.model.SRole AS role WHERE (processsupervisor.userId <= 0 AND processsupervisor.roleId = role.id) OR (processsupervisor.userId = user.id AND processsupervisor.roleId <= 0) SELECT COUNT(DISTINCT processsupervisor.id) FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SUser as user, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE (processsupervisor.userId = user.id AND processsupervisor.groupId <= 0) OR (processsupervisor.userId <= 0 AND processsupervisor.groupId = group_.id) SELECT DISTINCT processsupervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SUser as user, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE (processsupervisor.userId = user.id AND processsupervisor.groupId <= 0) OR (processsupervisor.userId <= 0 AND processsupervisor.groupId = group_.id) SELECT COUNT(DISTINCT processsupervisor.id) FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SUser as user, org.bonitasoft.engine.identity.model.SGroup AS group_, org.bonitasoft.engine.identity.model.SRole AS role WHERE ((user.id = processsupervisor.userId AND processsupervisor.roleId <= 0 AND processsupervisor.groupId <= 0) OR (processsupervisor.userId <= 0 AND ((processsupervisor.roleId = role.id AND processsupervisor.groupId = group_.id) OR (processsupervisor.roleId = role.id AND processsupervisor.groupId <= 0) OR (processsupervisor.roleId <= 0 AND processsupervisor.groupId = group_.id)))) SELECT DISTINCT processsupervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SUser as user, org.bonitasoft.engine.identity.model.SGroup AS group_, org.bonitasoft.engine.identity.model.SRole AS role WHERE ((user.id = processsupervisor.userId AND processsupervisor.roleId <= 0 AND processsupervisor.groupId <= 0) OR (processsupervisor.userId <= 0 AND ((processsupervisor.roleId = role.id AND processsupervisor.groupId = group_.id) OR (processsupervisor.roleId = role.id AND processsupervisor.groupId <= 0) OR (processsupervisor.roleId <= 0 AND processsupervisor.groupId = group_.id)))) SELECT processsupervisor FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE processsupervisor.id = :id SELECT processsupervisor FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor WHERE processsupervisor.processDefId = :processDefId AND ( processsupervisor.userId = :userId OR processsupervisor.id IN ( SELECT processsupervisor.id FROM org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisor AS processsupervisor, org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user_membership.userId = :userId AND ( (processsupervisor.groupId = user_membership.groupId AND processsupervisor.roleId <= 0) OR (processsupervisor.roleId = user_membership.roleId AND processsupervisor.groupId <= 0) OR (processsupervisor.roleId = user_membership.roleId AND processsupervisor.groupId = user_membership.groupId) ) ) ) ================================================ FILE: bpm/bonita-core/bonita-user-filter/build.gradle ================================================ dependencies { api project(':services:bonita-session') api project(':services:bonita-expression') api project(':bpm:bonita-common') api project(':bpm:bonita-core:bonita-process-definition') api project(':services:bonita-cache') api project(':services:bonita-resources') api project(':services:bonita-connector-executor') api project(':bpm:bonita-core:bonita-process-instance') api project(':bpm:bonita-core:bonita-home-server') api project(':services:bonita-commons') testImplementation libs.assertj testImplementation libs.mockitoCore } ================================================ FILE: bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/FilterResult.java ================================================ /** * 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.core.filter; import java.io.Serializable; import java.util.List; /** * @author Baptiste Mesta */ public interface FilterResult extends Serializable { List getResult(); boolean shouldAutoAssignTaskIfSingleResult(); } ================================================ FILE: bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/UserFilterService.java ================================================ /** * 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.core.filter; import java.util.Map; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.filter.exception.SUserFilterExecutionException; import org.bonitasoft.engine.core.filter.exception.SUserFilterLoadingException; import org.bonitasoft.engine.core.process.definition.model.SUserFilterDefinition; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface UserFilterService { String IMPLEMENTATION_EXT = ".impl"; FilterResult executeFilter(long processDefinitionId, SUserFilterDefinition sUserFilterDefinition, Map inputs, ClassLoader classLoader, SExpressionContext expressionContext, final String actorName) throws SUserFilterExecutionException; void removeUserFilters(long processDefinitionId) throws SBonitaReadException, SRecorderException; boolean loadUserFilters(long processDefinitionId) throws SUserFilterLoadingException; } ================================================ FILE: bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/exception/SUserFilterExecutionException.java ================================================ /** * 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.core.filter.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SUserFilterExecutionException extends SBonitaException { private static final long serialVersionUID = -573004533756971602L; public SUserFilterExecutionException(final Throwable throwable) { super(throwable); } public SUserFilterExecutionException(final String message) { super(message); } public SUserFilterExecutionException(String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/exception/SUserFilterLoadingException.java ================================================ /** * 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.core.filter.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SUserFilterLoadingException extends SBonitaException { private static final long serialVersionUID = -746939454533910295L; /** * */ public SUserFilterLoadingException(final Throwable t) { super(t); } /** * @param message */ public SUserFilterLoadingException(final String message) { super(message); } /** * @param message * @param e1 */ public SUserFilterLoadingException(final String message, final Throwable t) { super(message, t); } } ================================================ FILE: bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/impl/FilterResultImpl.java ================================================ /** * 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.core.filter.impl; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import org.bonitasoft.engine.core.filter.FilterResult; /** * @author Baptiste Mesta */ public class FilterResultImpl implements FilterResult { private static final long serialVersionUID = -6952608023861384532L; private final List result; private final boolean shouldAutoAssignTaskIfSingleResult; public FilterResultImpl(final List userIds, final boolean shouldAutoAssignTaskIfSingleResult) { //To avoid errors due to duplicates see BS-16189 if (userIds != null) { result = new ArrayList<>(new LinkedHashSet<>(userIds)); } else { result = null; } this.shouldAutoAssignTaskIfSingleResult = shouldAutoAssignTaskIfSingleResult; } @Override public List getResult() { return result; } @Override public boolean shouldAutoAssignTaskIfSingleResult() { return shouldAutoAssignTaskIfSingleResult; } } ================================================ FILE: bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/impl/SConnectorUserFilterAdapter.java ================================================ /** * 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.core.filter.impl; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.APIAccessor; import org.bonitasoft.engine.connector.ConnectorValidationException; import org.bonitasoft.engine.connector.EngineExecutionContext; import org.bonitasoft.engine.connector.SConnector; import org.bonitasoft.engine.connector.exception.SConnectorException; import org.bonitasoft.engine.connector.exception.SConnectorValidationException; import org.bonitasoft.engine.filter.AbstractUserFilter; import org.bonitasoft.engine.filter.UserFilter; import org.bonitasoft.engine.filter.UserFilterException; /** * @author Baptiste Mesta */ public class SConnectorUserFilterAdapter implements SConnector { private final UserFilter filter; private List userIds; private boolean shouldAutoAssignTaskIfSingleResult; private final String actorName; public SConnectorUserFilterAdapter(final UserFilter filter, final String actorName) { this.filter = filter; this.actorName = actorName; } @Override public void setInputParameters(final Map parameters) { final APIAccessor apiAccessor = (APIAccessor) parameters.remove("connectorApiAccessor"); final EngineExecutionContext executionContext = (EngineExecutionContext) parameters .remove("engineExecutionContext"); if (filter instanceof AbstractUserFilter) { ((AbstractUserFilter) filter).setAPIAccessor(apiAccessor); if (executionContext != null) { ((AbstractUserFilter) filter).setExecutionContext(executionContext); } } filter.setInputParameters(parameters); } @Override public void validate() throws SConnectorValidationException { try { filter.validateInputParameters(); } catch (ConnectorValidationException e) { throw new SConnectorValidationException(e); } } @Override public Map execute() throws SConnectorException { try { userIds = filter.filter(actorName); shouldAutoAssignTaskIfSingleResult = filter.shouldAutoAssignTaskIfSingleResult(); } catch (final UserFilterException e) { throw new SConnectorException(e); } return null; } public List getUserIds() { return userIds; } public boolean shouldAutoAssignTaskIfSingleResult() { return shouldAutoAssignTaskIfSingleResult; } @Override public void connect() { // nothing for user filters } @Override public void disconnect() { // nothing for user filters } } ================================================ FILE: bpm/bonita-core/bonita-user-filter/src/main/java/org/bonitasoft/engine/core/filter/impl/UserFilterServiceImpl.java ================================================ /** * 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.core.filter.impl; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; import java.util.regex.Pattern; import javax.xml.bind.JAXBException; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.cache.SCacheException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.connector.ConnectorExecutor; import org.bonitasoft.engine.connector.exception.SConnectorException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.filter.FilterResult; import org.bonitasoft.engine.core.filter.UserFilterService; import org.bonitasoft.engine.core.filter.exception.SUserFilterExecutionException; import org.bonitasoft.engine.core.filter.exception.SUserFilterLoadingException; import org.bonitasoft.engine.core.filter.model.UserFilterImplementationDescriptor; import org.bonitasoft.engine.core.filter.model.UserFilterImplementationParser; import org.bonitasoft.engine.core.process.definition.model.SUserFilterDefinition; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.filter.UserFilter; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.resources.BARResourceType; import org.bonitasoft.engine.resources.ProcessResourcesService; import org.bonitasoft.engine.resources.SBARResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class UserFilterServiceImpl implements UserFilterService { private static final Logger logger = LoggerFactory.getLogger(UserFilterServiceImpl.class); public static final String FILTER_CACHE_NAME = "USER_FILTER"; private final ConnectorExecutor connectorExecutor; private final CacheService cacheService; private final ExpressionResolverService expressionResolverService; private final ProcessResourcesService processResourcesService; private final UserFilterImplementationParser userFilterImplementationParser = new UserFilterImplementationParser(); public UserFilterServiceImpl(final ConnectorExecutor connectorExecutor, final CacheService cacheService, final ExpressionResolverService expressionResolverService, ProcessResourcesService processResourcesService) { super(); this.connectorExecutor = connectorExecutor; this.cacheService = cacheService; this.expressionResolverService = expressionResolverService; this.processResourcesService = processResourcesService; } @Override public FilterResult executeFilter(final long processDefinitionId, final SUserFilterDefinition sUserFilterDefinition, final Map inputs, final ClassLoader classLoader, final SExpressionContext expressionContext, final String actorName) throws SUserFilterExecutionException { final FilterResult filterResult; String implementationClassName = ""; UserFilterImplementationDescriptor descriptor = null; if (logger.isDebugEnabled()) { logger.debug( Thread.currentThread().toString() + "-[" + Thread.currentThread().getId() + "," + Thread.currentThread().getState() + "]"); } try { descriptor = getDescriptor(processDefinitionId, sUserFilterDefinition); if (descriptor == null) { loadUserFilters(processDefinitionId); descriptor = getDescriptor(processDefinitionId, sUserFilterDefinition); if (descriptor == null) { throw new SUserFilterExecutionException( "unable to load descriptor for filter " + sUserFilterDefinition.getUserFilterId()); } } implementationClassName = descriptor.getImplementationClassName(); filterResult = executeFilterInClassloader(implementationClassName, inputs, classLoader, expressionContext, actorName); } catch (final SConnectorException e) { String dbgInfo = ""; if (logger.isDebugEnabled()) { dbgInfo = buildDebugMessage(processDefinitionId, sUserFilterDefinition, inputs, classLoader, expressionContext, actorName, implementationClassName, descriptor); } if (e.getCause() != null) { throw new SUserFilterExecutionException(dbgInfo, e.getCause()); } else { throw new SUserFilterExecutionException("SConnectorException: " + e.getMessage() + dbgInfo, e); } } catch (final SUserFilterExecutionException e) { throw e; } catch (final Throwable e) { // catch throwable because we might have NoClassDefFound... see ENGINE-1333 throw new SUserFilterExecutionException(e); } if (logger.isDebugEnabled()) { String stb = "Executed userFilter [name: <" + sUserFilterDefinition.getName() + ">, user filter id: <" + sUserFilterDefinition.getUserFilterId() + ">, version: <" + sUserFilterDefinition.getVersion() + ">] on flow node instance with id: <" + expressionContext.getContainerId() + ">"; logger.debug(stb); } return filterResult; } protected String buildDebugMessage(long processDefinitionId, SUserFilterDefinition sUserFilterDefinition, Map inputs, ClassLoader classLoader, SExpressionContext expressionContext, String actorName, String implementationClassName, UserFilterImplementationDescriptor descriptor) { return " Flow node instance id: <" + expressionContext.getContainerId() + ">" + "\n Current Thread ID : <" + Thread.currentThread().getId() + ">, Current Thread State : <" + Thread.currentThread().getState() + ">,\n ProcessDefinitionID : <" + processDefinitionId + ">, SUserFilterDefinition : <" + sUserFilterDefinition + ">, Inputs : <" + inputs.toString() + ">, ClassLoader : <" + classLoader.toString() + ">, ExpressionContext : <" + expressionContext + ">, ActorName : <" + actorName + ">, UserFilterImplementationDescriptor : <" + descriptor + ">, ImplementationClassName : <" + implementationClassName + ">"; } private UserFilterImplementationDescriptor getDescriptor(final long processDefinitionId, final SUserFilterDefinition sUserFilterDefinition) throws SCacheException { return (UserFilterImplementationDescriptor) cacheService.get(FILTER_CACHE_NAME, getUserFilterImplementationIdInCache(processDefinitionId, sUserFilterDefinition.getUserFilterId(), sUserFilterDefinition.getVersion())); } private String getUserFilterImplementationIdInCache(final long processDefinitionId, final String userFilterId, final String version) { return processDefinitionId + ":" + userFilterId + "-" + version; } protected FilterResult executeFilterInClassloader(final String implementationClassName, final Map parameters, final ClassLoader classLoader, final SExpressionContext expressionContext, final String actorName) throws InstantiationException, IllegalAccessException, ClassNotFoundException, SUserFilterExecutionException, SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException, SConnectorException, InterruptedException, ExecutionException { final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(classLoader); final UserFilter filter = (UserFilter) Class.forName(implementationClassName, true, classLoader) .newInstance(); final SConnectorUserFilterAdapter adapter = new SConnectorUserFilterAdapter(filter, actorName); final HashMap inputParameters = new HashMap<>(parameters.size()); for (final Entry input : parameters.entrySet()) { try { if (expressionContext != null) { inputParameters.put(input.getKey(), expressionResolverService.evaluate(input.getValue(), expressionContext)); } else { inputParameters.put(input.getKey(), expressionResolverService.evaluate(input.getValue())); } } catch (SBonitaException e) { e.setConnectorInputOnContext(input.getKey()); throw e; } } connectorExecutor.execute(adapter, inputParameters, classLoader).get(); return new FilterResultImpl(adapter.getUserIds(), adapter.shouldAutoAssignTaskIfSingleResult()); } finally { Thread.currentThread().setContextClassLoader(contextClassLoader); } } @Override public void removeUserFilters(final long processDefinitionId) throws SBonitaReadException, SRecorderException { processResourcesService.removeAll(processDefinitionId, BARResourceType.USER_FILTER); } @Override public boolean loadUserFilters(final long processDefinitionId) throws SUserFilterLoadingException { String name = null; try { final List listFiles = processResourcesService.get(processDefinitionId, BARResourceType.USER_FILTER, 0, 1000);//FIXME final Pattern pattern = Pattern.compile("^.*\\" + IMPLEMENTATION_EXT + "$"); for (final SBARResource file : listFiles) { name = file.getName(); if (pattern.matcher(name).matches()) { UserFilterImplementationDescriptor userFilterImplementationDescriptor; userFilterImplementationDescriptor = userFilterImplementationParser .convert(new String(file.getContent())); if (userFilterImplementationDescriptor == null) { throw new SUserFilterLoadingException( "Can not parse ConnectorImplementation XML. The file name is " + name); } cacheService.store( FILTER_CACHE_NAME, getUserFilterImplementationIdInCache(processDefinitionId, userFilterImplementationDescriptor.getDefinitionId(), userFilterImplementationDescriptor.getDefinitionVersion()), userFilterImplementationDescriptor); } } return true; } catch (final JAXBException e) { throw new SUserFilterLoadingException( "Cannot load userFilterImplementationDescriptor XML. The file name is " + name, e); } catch (final SCacheException e) { throw new SUserFilterLoadingException("Unable to cache the user filter implementation" + name, e); } catch (SBonitaReadException e) { throw new SUserFilterLoadingException("Unable to list the user filter implementations", e); } } } ================================================ FILE: bpm/bonita-core/bonita-user-filter/src/test/java/org/bonitasoft/engine/core/filter/impl/FilterResultImplTest.java ================================================ /** * 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.core.filter.impl; import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; import java.util.List; import org.junit.Test; /** * @author Danila Mazour */ public class FilterResultImplTest { @Test public void filterResult_should_not_return_duplicates() { //given List listWithDuplicates = Arrays.asList(15L, 15L, 15L, 28L, 32L, 28L, 39L); FilterResultImpl filterResult = new FilterResultImpl(listWithDuplicates, true); //when List result = filterResult.getResult(); //then assertThat(result).containsExactly(15L, 28L, 32L, 39L); } } ================================================ FILE: bpm/bonita-core/bonita-user-filter/src/test/java/org/bonitasoft/engine/core/filter/impl/SConnectorUserFilterAdapterTest.java ================================================ /** * Copyright (C) 2020 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.core.filter.impl; import static org.mockito.Mockito.verify; import org.bonitasoft.engine.filter.UserFilter; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Emmanuel Duchastenier */ @RunWith(MockitoJUnitRunner.class) public class SConnectorUserFilterAdapterTest { @Mock UserFilter userFilter; SConnectorUserFilterAdapter sConnectorUserFilterAdapter; @Before public void setUp() { sConnectorUserFilterAdapter = new SConnectorUserFilterAdapter(userFilter, "anActor"); } @Test public void validate_should_call_validateInputParameters() throws Exception { sConnectorUserFilterAdapter.validate(); verify(userFilter).validateInputParameters(); } } ================================================ FILE: bpm/bonita-core/bonita-user-filter/src/test/java/org/bonitasoft/engine/core/filter/impl/UserFilterServiceImplTest.java ================================================ /** * 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.core.filter.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; import java.net.URL; import java.net.URLClassLoader; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.concurrent.CompletableFuture; import org.bonitasoft.engine.bpm.userfilter.impl.UserFilterDefinitionImpl; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.connector.ConnectorExecutor; import org.bonitasoft.engine.connector.ConnectorValidationException; import org.bonitasoft.engine.connector.exception.SConnectorException; import org.bonitasoft.engine.core.expression.control.api.ExpressionResolverService; import org.bonitasoft.engine.core.expression.control.model.SExpressionContext; import org.bonitasoft.engine.core.filter.exception.SUserFilterExecutionException; import org.bonitasoft.engine.core.filter.model.JarDependencies; import org.bonitasoft.engine.core.filter.model.UserFilterImplementationDescriptor; import org.bonitasoft.engine.core.process.definition.model.impl.SUserFilterDefinitionImpl; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.filter.AbstractUserFilter; import org.bonitasoft.engine.filter.UserFilterException; import org.bonitasoft.engine.resources.BARResourceType; import org.bonitasoft.engine.resources.ProcessResourcesService; import org.bonitasoft.engine.resources.SBARResource; import org.junit.Before; 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.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class UserFilterServiceImplTest { public static final long PROCESS_DEFINITION_ID = 123456L; @InjectMocks private UserFilterServiceImpl userFilterService; @Mock private ConnectorExecutor connectorExecutor; @Mock private CacheService cacheService; @Mock private ExpressionResolverService expressionResolverService; @Mock private ProcessResourcesService resourceService; private SUserFilterDefinitionImpl sUserFilterDefinition; private UserFilterImplementationDescriptor userFilterImplementationDescriptor; @Captor private ArgumentCaptor userFilterImplementationDescriptorArgumentCaptor; @Rule public ExpectedException expectedException = ExpectedException.none(); @Before public void setup() { sUserFilterDefinition = new SUserFilterDefinitionImpl( new UserFilterDefinitionImpl("UserFiler", "filterId", "version")); userFilterImplementationDescriptor = new UserFilterImplementationDescriptor(MyUserFilter.class.getName(), "id", "version", "filterId", "version", new JarDependencies(Collections.singletonList("dep.jar"))); } @Test public void executeFilter_should_work_for_existing_descriptor() throws Exception { doReturn(CompletableFuture.completedFuture(Collections.emptyMap())).when(connectorExecutor).execute(any(), anyMap(), any()); doReturn(userFilterImplementationDescriptor).when(cacheService).get(eq("USER_FILTER"), eq("" + PROCESS_DEFINITION_ID + ":filterId-version")); userFilterService.executeFilter(PROCESS_DEFINITION_ID, sUserFilterDefinition, Collections.emptyMap(), new URLClassLoader(new URL[] {}, Thread.currentThread().getContextClassLoader()), new SExpressionContext(), "actorName"); } @Test(expected = SUserFilterExecutionException.class) public void executeFilter_should_fail_for_non_existing_descriptor() throws Exception { userFilterService.executeFilter(PROCESS_DEFINITION_ID, sUserFilterDefinition, Collections. emptyMap(), new URLClassLoader(new URL[] {}, Thread.currentThread().getContextClassLoader()), new SExpressionContext(), "actorName"); } @Test public void loadUserFilters_should_load_from_resourceService() throws Exception { userFilterService.loadUserFilters(PROCESS_DEFINITION_ID); verify(resourceService).get(eq(PROCESS_DEFINITION_ID), eq(BARResourceType.USER_FILTER), anyInt(), anyInt()); } @Test public void should_parse_user_filter_implementation_file_and_cache_it_when_loading_userfilters() throws Exception { //given byte[] userFilterImplContent = ("\n" + "\n" + "\tuser-filter-def\n" + "\t1.0\n" + "\torg.bonitasoft.user.filter.TestUserFilter\n" + "\tuser-filter-impl\n" + "\t1.0\n" + "\n" + "\t\n" + "\t\tUserFilterDependency.jar\n" + "\t\n" + "\n").getBytes(); doReturn(Collections.singletonList(new SBARResource("my-user-filter.impl", BARResourceType.USER_FILTER, PROCESS_DEFINITION_ID, userFilterImplContent))) .when(resourceService) .get(eq(PROCESS_DEFINITION_ID), eq(BARResourceType.USER_FILTER), anyInt(), anyInt()); //when userFilterService.loadUserFilters(PROCESS_DEFINITION_ID); //then verify(cacheService).store(eq("USER_FILTER"), eq(PROCESS_DEFINITION_ID + ":user-filter-def-1.0"), userFilterImplementationDescriptorArgumentCaptor.capture()); UserFilterImplementationDescriptor userFilterImplementationDescriptor = userFilterImplementationDescriptorArgumentCaptor .getValue(); assertThat(userFilterImplementationDescriptor.getDefinitionId()).isEqualTo("user-filter-def"); assertThat(userFilterImplementationDescriptor.getDefinitionVersion()).isEqualTo("1.0"); assertThat(userFilterImplementationDescriptor.getImplementationClassName()) .isEqualTo("org.bonitasoft.user.filter.TestUserFilter"); assertThat(userFilterImplementationDescriptor.getId()).isEqualTo("user-filter-impl"); assertThat(userFilterImplementationDescriptor.getVersion()).isEqualTo("1.0"); assertThat(userFilterImplementationDescriptor.getJarDependencies().getDependencies()) .containsOnly("UserFilterDependency.jar"); } @Test public void executeFilter_should_throw_a_SUserFilterExecutionException_when_receiving_SConnectorException_with_null_cause() throws Exception { //given UserFilterServiceImpl spyUserFilterService = spy( new UserFilterServiceImpl(connectorExecutor, cacheService, expressionResolverService, resourceService)); doReturn(userFilterImplementationDescriptor).when(cacheService).get(eq("USER_FILTER"), eq("" + PROCESS_DEFINITION_ID + ":filterId-version")); doThrow(new SConnectorException("Test exception")).when(spyUserFilterService).executeFilterInClassloader( anyString(), anyMap(), (URLClassLoader) any(), (SExpressionContext) any(), anyString()); //then expectedException.expect(SUserFilterExecutionException.class); expectedException.expectMessage("Test exception"); //when spyUserFilterService.executeFilter(PROCESS_DEFINITION_ID, sUserFilterDefinition, Collections. emptyMap(), new URLClassLoader(new URL[] {}, Thread.currentThread().getContextClassLoader()), new SExpressionContext(), "actorName"); } @Test public void executeFilter_should_throw_a_SUserFilterExecutionException_when_receiving_SConnectorException_with_nonNull_cause() throws Exception { //given SConnectorException theException = mock(SConnectorException.class); UserFilterServiceImpl spyUserFilterService = spy( new UserFilterServiceImpl(connectorExecutor, cacheService, expressionResolverService, resourceService)); doReturn(userFilterImplementationDescriptor).when(cacheService).get(eq("USER_FILTER"), eq("" + PROCESS_DEFINITION_ID + ":filterId-version")); when(theException.getCause()).thenReturn(new RuntimeException(" The root cause")); doThrow(theException).when(spyUserFilterService).executeFilterInClassloader(anyString(), anyMap(), (URLClassLoader) any(), (SExpressionContext) any(), anyString()); //then expectedException.expect(SUserFilterExecutionException.class); expectedException.expectMessage("Flow node instance id:"); expectedException.expectCause(instanceOf(RuntimeException.class)); //when spyUserFilterService.executeFilter(PROCESS_DEFINITION_ID, sUserFilterDefinition, Collections. emptyMap(), new URLClassLoader(new URL[] {}, Thread.currentThread().getContextClassLoader()), new SExpressionContext(), "actorName"); } @Test public void executeFilter_should_throw_a_SUserFilterExecutionException_when_receiving_SConnectorException_in_debug_mode() throws Exception { //given SConnectorException theException = mock(SConnectorException.class); UserFilterServiceImpl spyUserFilterService = spy( new UserFilterServiceImpl(connectorExecutor, cacheService, expressionResolverService, resourceService)); doReturn(userFilterImplementationDescriptor).when(cacheService).get(eq("USER_FILTER"), eq("" + PROCESS_DEFINITION_ID + ":filterId-version")); when(theException.getCause()).thenReturn(new RuntimeException(" The root cause")); doThrow(theException).when(spyUserFilterService).executeFilterInClassloader(anyString(), anyMap(), (URLClassLoader) any(), (SExpressionContext) any(), anyString()); //then expectedException.expect(SUserFilterExecutionException.class); expectedException.expectMessage("Current Thread ID : <"); //when spyUserFilterService.executeFilter(PROCESS_DEFINITION_ID, sUserFilterDefinition, Collections. emptyMap(), new URLClassLoader(new URL[] {}, Thread.currentThread().getContextClassLoader()), new SExpressionContext(), "actorName"); } @Test public void buildDebugMessage_should_contain_all_the_debug_info() { //when String result = userFilterService.buildDebugMessage(45L, sUserFilterDefinition, new HashMap(), new URLClassLoader(new URL[] {}, Thread.currentThread().getContextClassLoader()), new SExpressionContext(), "an actor", "an implementation class name", userFilterImplementationDescriptor); //then assertThat(result).contains("an actor"); assertThat(result).contains("an implementation class name"); assertThat(result).contains("45"); assertThat(result).contains(sUserFilterDefinition.toString()); assertThat(result).contains(userFilterImplementationDescriptor.toString()); assertThat(result).contains("RUNNABLE"); } public static class MyUserFilter extends AbstractUserFilter { @Override public void validateInputParameters() throws ConnectorValidationException { } @Override public List filter(String actorName) throws UserFilterException { return null; } } } ================================================ FILE: bpm/bonita-external/build.gradle ================================================ dependencies { api project(':bpm:bonita-core:bonita-process-engine') api project(':services:bonita-command') api project(':bpm:bonita-core:bonita-process-definition') testImplementation libs.mockitoCore } ================================================ FILE: bpm/bonita-external/src/main/java/org/bonitasoft/engine/external/comment/SearchCommentsSupervisedBy.java ================================================ /** * 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.external.comment; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.command.RuntimeCommand; import org.bonitasoft.engine.command.SCommandExecutionException; import org.bonitasoft.engine.command.SCommandParameterizationException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.external.comment.transaction.SearchCommentsSupervisedByTransaction; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntitiesDescriptor; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Hongwen Zang * @author Matthieu Chaffotte */ public class SearchCommentsSupervisedBy extends RuntimeCommand { private static final String SUPERVISOR_ID_KEY = "supervisorId"; @Override public Serializable execute(final Map parameters, final ServiceAccessor serviceAccessor) throws SCommandParameterizationException, SCommandExecutionException { final Long supervisorId = (Long) parameters.get(SUPERVISOR_ID_KEY); if (supervisorId == null) { throw new SCommandParameterizationException(SUPERVISOR_ID_KEY + " is missing"); } final SearchOptions searchOptions = (SearchOptions) parameters.get("SEARCH_OPTIONS_KEY"); if (searchOptions == null) { throw new SCommandParameterizationException("SEARCH_OPTIONS_KEY is missing"); } final SCommentService commentService = serviceAccessor.getCommentService(); final SearchEntitiesDescriptor searchEntitiesDescriptor = serviceAccessor.getSearchEntitiesDescriptor(); final SearchCommentsSupervisedByTransaction searchTransaction = new SearchCommentsSupervisedByTransaction( supervisorId, commentService, searchEntitiesDescriptor.getSearchCommentDescriptor(), searchOptions); try { searchTransaction.execute(); } catch (final SBonitaException sbe) { throw new SCommandExecutionException(sbe); } return searchTransaction.getResult(); } } ================================================ FILE: bpm/bonita-external/src/main/java/org/bonitasoft/engine/external/comment/transaction/SearchCommentsSupervisedByTransaction.java ================================================ /** * 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.external.comment.transaction; import java.util.List; import org.bonitasoft.engine.core.process.comment.api.SCommentService; import org.bonitasoft.engine.core.process.comment.model.SComment; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.search.AbstractCommentSearchEntity; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.descriptor.SearchEntityDescriptor; /** * @author Hongwen Zang */ public class SearchCommentsSupervisedByTransaction extends AbstractCommentSearchEntity { private final SCommentService commentService; private final Long supervisorId; public SearchCommentsSupervisedByTransaction(final Long supervisorId, final SCommentService commentService, final SearchEntityDescriptor searchDescriptor, final SearchOptions searchOptions) { super(searchDescriptor, searchOptions); this.supervisorId = supervisorId; this.commentService = commentService; } @Override public long executeCount(final QueryOptions searchOptions) throws SBonitaReadException { return commentService.getNumberOfCommentsSupervisedBy(supervisorId, searchOptions); } @Override public List executeSearch(final QueryOptions searchOptions) throws SBonitaReadException { return commentService.searchCommentsSupervisedBy(supervisorId, searchOptions); } } ================================================ FILE: bpm/bonita-sap-jco-connector-api/src/main/java/com/sap/conn/jco/ext/DestinationDataEventListener.java ================================================ /** * Copyright (C) 2020 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 com.sap.conn.jco.ext; public interface DestinationDataEventListener { void deleted(String destinationName); void updated(String destinationName); } ================================================ FILE: bpm/bonita-sap-jco-connector-api/src/main/java/com/sap/conn/jco/ext/DestinationDataProvider.java ================================================ /** * Copyright (C) 2020 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 com.sap.conn.jco.ext; import java.util.Properties; public interface DestinationDataProvider { Properties getDestinationProperties(final String destinationName); void setDestinationDataEventListener(final DestinationDataEventListener eventListener); boolean supportsEvents(); } ================================================ FILE: bpm/bonita-sap-jco-connector-api/src/main/java/com/sap/conn/jco/ext/Environment.java ================================================ /** * Copyright (C) 2020 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 com.sap.conn.jco.ext; public abstract class Environment { public static void registerDestinationDataProvider(DestinationDataProvider destinationDataProvider) { // Mock implementation to remove sap jco connector compile dependency // Concrete implementation should be provided at runtime } public static void unregisterDestinationDataProvider(DestinationDataProvider destinationDataProvider) { // Mock implementation to remove sap jco connector compile dependency // Concrete implementation should be provided at runtime } } ================================================ FILE: bpm/bonita-server/build.gradle ================================================ import org.bonitasoft.engine.gradle.ShadeDependency plugins { id 'bonita-shade' } dependencies { // import jackson bom to align jackson dependencies even when resolving shade dependencies // When create the shade version of bonita-server, the bonita-common project is excluded causing the jackson bom not to be imported // This cause issues to the code deposit because an different version of jackson, that was not downloaded to local cache, can be required during offline build. // Remove the shade to solve that issue implementation(platform(libs.jacksonBom)) api project(':bpm:bonita-core:bonita-process-engine') api project(':services:bonita-builder') api project(':bpm:bonita-core:bonita-actor-mapping') api project(':bpm:bonita-api:bonita-server-api-http') api project(':services:bonita-archive') api project(':services:bonita-authentication') api project(':services:bonita-business-application') api project(':services:bonita-business-data:bonita-business-data-impl') api project(':services:bonita-business-data:bonita-business-data-client-resources') api project(':services:bonita-business-data:bonita-business-data-generator') api project(':services:bonita-cache') api project(':bpm:bonita-core:bonita-category') api project(':services:bonita-classloader') api project(':services:bonita-command') api project(':bpm:bonita-core:bonita-contract-data') api project(':services:bonita-connector-executor') api project(':services:bonita-data-definition') api project(':services:bonita-data-instance') api project(':services:bonita-events') api project(':services:bonita-expression') api project(':bpm:bonita-core:bonita-form-mapping') api project(':services:bonita-incident') api project(':services:bonita-identity') api project(':services:bonita-lock') api project(':bpm:bonita-core:bonita-login') api project(':services:bonita-log') api project(':services:bonita-page') api project(':bpm:bonita-core:bonita-parameter') api project(':services:bonita-persistence') api project(':services:bonita-platform') api project(':services:bonita-platform-authentication') api project(':services:bonita-platform-command') api project(':bpm:bonita-core:bonita-platform-login') api project(':services:bonita-platform-session') api project(':bpm:bonita-core:bonita-process-comment') api project(':bpm:bonita-core:bonita-process-definition') api project(':bpm:bonita-core:bonita-process-instance') api project(':services:bonita-scheduler') api project(':services:bonita-session') api project(':bpm:bonita-core:bonita-supervisor-mapping') api project(':bpm:bonita-synchro-repository:bonita-synchro-service') api project(':bpm:bonita-synchro-repository:bonita-synchro-service-impl') api project(':bpm:bonita-synchro-repository:bonita-synchro-register') api project(':services:bonita-time-tracker') api project(':services:bonita-transaction') api project(':bpm:bonita-core:bonita-core-data') api project(':bpm:bonita-core:bonita-user-filter') api project(':services:bonita-work') api project(':bpm:bonita-external') api project(':services:bonita-profile') api project(':bpm:bonita-common') } shade { exclude project(':bpm:bonita-common') exclude project(':platform:platform-resources') excludeLibs('hibernate-core', new ShadeDependency(group: 'org.jboss.spec.javax.transaction', name: 'jboss-transaction-api_1.2_spec'), new ShadeDependency(group: 'javax.activation', name: 'javax.activation-api') ) excludeLibs('jaxb-api', new ShadeDependency(group: 'javax.activation', name: 'javax.activation-api')) excludeLibs('jaxb-runtime', new ShadeDependency(group: 'javax.activation', name: 'javax.activation-api')) excludeLibs('quartz', new ShadeDependency(group: 'com.mchange', name: '*'), new ShadeDependency(group: 'com.zaxxer', name: 'HikariCP-java7')) } afterEvaluate { publishing { publications { shadow(MavenPublication) { publication -> publication.getPom().with { name = "Bonita Server" description = "Bonita Server is the Server-side BPM Execution Engine" } } } } } ================================================ FILE: bpm/bonita-synchro-repository/bonita-synchro-register/build.gradle ================================================ 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) } ================================================ FILE: bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/AbstractUpdateHandler.java ================================================ /** * 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.synchro; import java.io.Serializable; import java.lang.reflect.Method; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.events.model.SHandler; import org.bonitasoft.engine.events.model.SHandlerExecutionException; import org.bonitasoft.engine.service.ServiceAccessor; import org.bonitasoft.engine.service.impl.ServiceAccessorFactory; import org.bonitasoft.engine.transaction.BonitaTransactionSynchronization; import org.bonitasoft.engine.transaction.STransactionNotFoundException; import org.bonitasoft.engine.transaction.UserTransactionService; /** * @author Baptiste Mesta */ public abstract class AbstractUpdateHandler implements SHandler { private static final long serialVersionUID = 1L; private final Map, Method> getIdMethods = Collections.synchronizedMap(new HashMap, Method>()); public AbstractUpdateHandler() { super(); } protected abstract Map getEvent(final SEvent sEvent); @Override public void execute(final SEvent sEvent) throws SHandlerExecutionException { try { final Map event = getEvent(sEvent); final Long id = getObjectId(sEvent); final ServiceAccessor serviceAccessor = getServiceAccessor(); final BonitaTransactionSynchronization synchronization = getSynchronization(event, id, serviceAccessor); final UserTransactionService userTransactionService = serviceAccessor.getUserTransactionService(); userTransactionService.registerBonitaSynchronization(synchronization); } catch (final STransactionNotFoundException e) { e.printStackTrace(); throw new SHandlerExecutionException(e); } } protected BonitaTransactionSynchronization getSynchronization(final Map event, final Long id, final ServiceAccessor serviceAccessor) { return new WaitForEventSynchronization(event, id, serviceAccessor.getSynchroService()); } /** * @param sEvent * @return */ private Long getObjectId(final SEvent sEvent) { Long id = null; Object object = null; try { object = sEvent.getObject(); final Class clazz = object.getClass(); Method method = null; if (getIdMethods.containsKey(clazz)) { method = getIdMethods.get(clazz); } else { method = clazz.getMethod("getId"); } final Object invoke = method.invoke(object); id = (Long) invoke; } catch (final Exception e) { System.err.println("AbstractUpdateHandler: No id on object " + object); } return id; } private ServiceAccessor getServiceAccessor() throws SHandlerExecutionException { try { return ServiceAccessorFactory.getInstance().createServiceAccessor(); } catch (final Exception e) { throw new SHandlerExecutionException(e.getMessage(), null); } } } ================================================ FILE: bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/AddHandlerCommand.java ================================================ /** * 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.synchro; import java.io.Serializable; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.command.RuntimeCommand; import org.bonitasoft.engine.command.SCommandExecutionException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.events.model.SHandler; import org.bonitasoft.engine.service.ServiceAccessor; public class AddHandlerCommand extends RuntimeCommand { private static final String PROCESSINSTANCE_STATE_UPDATED = "PROCESSINSTANCE_STATE_UPDATED"; private static final String ACTIVITYINSTANCE_CREATED = "ACTIVITYINSTANCE_CREATED"; private static final String ACTIVITYINSTANCE_STATE_UPDATED = "ACTIVITYINSTANCE_STATE_UPDATED"; private static final String EVENT_INSTANCE_CREATED = "EVENT_INSTANCE_CREATED"; private static final String GATEWAYINSTANCE_CREATED = "GATEWAYINSTANCE_CREATED"; private static final String GATEWAYINSTANCE_STATE_UPDATED = "GATEWAYINSTANCE_STATE_UPDATED"; @Override public Serializable execute(final Map parameters, final ServiceAccessor serviceAccessor) throws SCommandExecutionException { final EventService eventService = serviceAccessor.getEventService(); try { if (!containsHandler(eventService, PROCESSINSTANCE_STATE_UPDATED, ProcessInstanceHandler.class)) { eventService.addHandler(PROCESSINSTANCE_STATE_UPDATED, new ProcessInstanceHandler()); } if (!containsHandler(eventService, ACTIVITYINSTANCE_STATE_UPDATED, FlowNodeHandler.class)) { eventService.addHandler(ACTIVITYINSTANCE_STATE_UPDATED, new FlowNodeHandler()); eventService.addHandler(ACTIVITYINSTANCE_CREATED, new FlowNodeHandler()); eventService.addHandler(EVENT_INSTANCE_CREATED, new FlowNodeHandler()); } if (!containsHandler(eventService, GATEWAYINSTANCE_CREATED, GatewayHandler.class)) { eventService.addHandler(GATEWAYINSTANCE_CREATED, new GatewayHandler()); eventService.addHandler(GATEWAYINSTANCE_STATE_UPDATED, new GatewayHandler()); } } catch (final SBonitaException e) { throw new SCommandExecutionException(e); } return null; } private boolean containsHandler(final EventService eventService, final String eventType, final Class clazz) { final Set> handlers = eventService.getHandlers(eventType); if (handlers != null) { for (final SHandler sHandler : handlers) { if (clazz.isInstance(sHandler)) { return true; } } } return false; } } ================================================ FILE: bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/EventUtil.java ================================================ /** * 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.synchro; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; /** * 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 PROCESS_DEFINITION_ID = "processDefinitionId"; private static final String STATE_ID = "stateId"; private static final String ID = "id"; private static final String STATE = "state"; public static Map getEventForProcess(final SProcessInstance instance) { final Map map = new HashMap(4); map.put(TYPE, PROCESS); map.put(ID, instance.getId()); map.put(STATE_ID, instance.getStateId()); map.put(PROCESS_DEFINITION_ID, instance.getProcessDefinitionId()); map.put(NAME, instance.getName()); return map; } public static Map getEventForFlowNode(final SFlowNodeInstance flowNodeInstance) { final Map map = new HashMap(5); map.put(TYPE, FLOW_NODE); map.put(ID, flowNodeInstance.getId()); map.put(ROOT_CONTAINER_ID, flowNodeInstance.getRootContainerId()); map.put(PARENT_CONTAINER_ID, flowNodeInstance.getParentContainerId()); map.put(NAME, flowNodeInstance.getName()); map.put(STATE_ID, flowNodeInstance.getStateId()); map.put(STATE, flowNodeInstance.getStateName()); return map; } } ================================================ FILE: bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/FlowNodeHandler.java ================================================ /** * 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.synchro; import java.io.Serializable; import java.util.Map; import java.util.UUID; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.events.model.SEvent; /** * @author Baptiste Mesta */ public class FlowNodeHandler extends AbstractUpdateHandler { private static final long serialVersionUID = 1L; private final String identifier; public FlowNodeHandler() { super(); identifier = UUID.randomUUID().toString(); } @Override protected Map getEvent(final SEvent sEvent) { final SFlowNodeInstance flowNodeInstance = (SFlowNodeInstance) sEvent.getObject(); return EventUtil.getEventForFlowNode(flowNodeInstance); } @Override public boolean isInterested(final SEvent event) { // the !isStateExecuting avoids having 2 times the same event in case of execution of e.g. connectors return event.getObject() instanceof SFlowNodeInstance && !((SFlowNodeInstance) event.getObject()).isStateExecuting(); } @Override public String getIdentifier() { return identifier; } } ================================================ FILE: bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/GatewayHandler.java ================================================ /** * 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.synchro; import java.io.Serializable; import java.util.Map; import java.util.UUID; import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance; import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance; import org.bonitasoft.engine.events.model.SEvent; /** * @author Celine Souchet */ public class GatewayHandler extends AbstractUpdateHandler { private static final long serialVersionUID = 1L; private final String identifier; public GatewayHandler() { super(); this.identifier = UUID.randomUUID().toString(); } @Override protected Map getEvent(final SEvent sEvent) { final SFlowNodeInstance flowNodeInstance = (SFlowNodeInstance) sEvent.getObject(); return EventUtil.getEventForFlowNode(flowNodeInstance); } @Override public boolean isInterested(final SEvent event) { return event.getObject() instanceof SGatewayInstance; } @Override public String getIdentifier() { return identifier; } } ================================================ FILE: bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/PerfEventUtil.java ================================================ /** * 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.synchro; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * Utility methods to get maps that match process as wanted * * @author Baptiste Mesta */ public class PerfEventUtil { 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 STATE_ID = "stateId"; private static final String ID = "id"; public static Map getProcessInstanceFinishedEvent(final long processInstanceId) { final HashMap map = new HashMap(3); map.put(TYPE, PROCESS); map.put(ID, processInstanceId); map.put(STATE_ID, 6); return map; } public static Map getReadyTaskEvent(final long processInstanceId, final String taskName) { final HashMap map = new HashMap(4); map.put(TYPE, FLOW_NODE); map.put(ID, processInstanceId); map.put(NAME, taskName); map.put(STATE_ID, 4); return map; } public static Map getFlowNodeReachStateEvent(final long processInstanceId, final String taskName, final int stateId) { final HashMap map = new HashMap(4); map.put(TYPE, FLOW_NODE); map.put(ID, processInstanceId); map.put(NAME, taskName); map.put(STATE_ID, stateId); return map; } } ================================================ FILE: bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/ProcessInstanceHandler.java ================================================ /** * 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.synchro; import java.io.Serializable; import java.util.Map; import java.util.UUID; import org.bonitasoft.engine.core.process.instance.model.SProcessInstance; import org.bonitasoft.engine.events.model.SEvent; /** * @author Baptiste Mesta */ public class ProcessInstanceHandler extends AbstractUpdateHandler { private static final long serialVersionUID = 1L; private final String identifier; public ProcessInstanceHandler() { super(); identifier = UUID.randomUUID().toString(); } @Override protected Map getEvent(final SEvent sEvent) { final SProcessInstance instance = (SProcessInstance) sEvent.getObject(); return EventUtil.getEventForProcess(instance); } @Override public boolean isInterested(final SEvent event) { final Object object = event.getObject(); return object instanceof SProcessInstance; } @Override public String getIdentifier() { return identifier; } } ================================================ FILE: bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/WaitForEventSynchronization.java ================================================ /** * 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.synchro; import java.io.Serializable; import java.util.Map; import javax.transaction.Status; import org.bonitasoft.engine.transaction.BonitaTransactionSynchronization; /** * @author Baptiste Mesta */ public class WaitForEventSynchronization implements BonitaTransactionSynchronization { private final Map event; private final Long id; private final SynchroService synchroService; public WaitForEventSynchronization(final Map event, final Long id, final SynchroService synchroService) { this.event = event; this.id = id; this.synchroService = synchroService; } @Override public void afterCompletion(final int status) { if (status == Status.STATUS_COMMITTED) { synchroService.fireEvent(event, id); } } } ================================================ FILE: bpm/bonita-synchro-repository/bonita-synchro-register/src/main/java/org/bonitasoft/engine/synchro/WaitServerCommand.java ================================================ /** * 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.synchro; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.command.RuntimeCommand; import org.bonitasoft.engine.command.SCommandExecutionException; import org.bonitasoft.engine.service.ServiceAccessor; /** * @author Baptiste Mesta */ public class WaitServerCommand extends RuntimeCommand { @Override public Serializable execute(final Map parameters, final ServiceAccessor serviceAccessor) throws SCommandExecutionException { if (parameters.get("clear") != null) { serviceAccessor.getSynchroService().clearAllEvents(); return null; } @SuppressWarnings("unchecked") final Map event = (Map) parameters.get("event"); final int timeout = (Integer) parameters.get("timeout"); try { return serviceAccessor.getSynchroService().waitForEvent(event, timeout); } catch (final Exception e) { throw new SCommandExecutionException(e); } } } ================================================ FILE: bpm/bonita-synchro-repository/bonita-synchro-service/build.gradle ================================================ ================================================ FILE: bpm/bonita-synchro-repository/bonita-synchro-service/src/main/java/org/bonitasoft/engine/synchro/SynchroService.java ================================================ /** * 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.synchro; import java.io.Serializable; import java.util.Map; import java.util.concurrent.TimeoutException; /** * @author Emmanuel Duchastenier */ public interface SynchroService { void fireEvent(Map event, Serializable id); Serializable waitForEvent(Map event, long timeout) throws InterruptedException, TimeoutException; void clearAllEvents(); boolean hasWaiters(); } ================================================ FILE: bpm/bonita-synchro-repository/bonita-synchro-service-impl/build.gradle ================================================ dependencies { api project(':services:bonita-commons') api project(':services:bonita-cache') api project(':bpm:bonita-synchro-repository:bonita-synchro-service') } ================================================ FILE: bpm/bonita-synchro-repository/bonita-synchro-service-impl/src/main/java/org/bonitasoft/engine/synchro/AbstractSynchroService.java ================================================ /** * 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.synchro; import java.io.Serializable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.Lock; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.cache.SCacheException; import org.slf4j.Logger; /** * @author Emmanuel Duchastenier * @author Charles Souillard */ public abstract class AbstractSynchroService implements SynchroService { protected static final String SYNCHRO_SERVICE_CACHE = "SYNCHRO_SERVICE_CACHE"; /** * String value is an identifier of the sempaphore for the current event. */ protected abstract Map, String> getWaitersMap(); protected abstract Logger getLogger(); /** * Maitains a map of */ protected abstract Map getEventKeyAndIdMap(); protected abstract void releaseWaiter(String semaphoreKey); protected abstract Lock getServiceLock(); protected final CacheService cacheService; public AbstractSynchroService(final CacheService cacheService) { this.cacheService = cacheService; } @Override public void fireEvent(final Map event, final Serializable id) { getLogger().debug("Firing event " + event + " with id " + id); getServiceLock().lock(); try { final String semaphoreKey = getWaiterAndRemoveIt(event); if (semaphoreKey == null) { // No waiter found yet: getLogger().debug("No waiter found, storing event " + event); try { cacheService.store(SYNCHRO_SERVICE_CACHE, (Serializable) event, id); } catch (SCacheException e) { throw new RuntimeException(e); } } else { // Waiter already exists, let's release waiter: getEventKeyAndIdMap().put(semaphoreKey, id); getLogger().debug( "releasing waiter for event " + event + " and id " + id); releaseWaiter(semaphoreKey); } } finally { getServiceLock().unlock(); } } protected String getWaiterAndRemoveIt(final Map event) { for (final Iterator, String>> iterator = getWaitersMap().entrySet() .iterator(); iterator.hasNext();) { final Entry, String> waiter = iterator.next(); if (matchedAtLeastAllExpectedEntries(waiter.getKey(), event)) { iterator.remove(); return waiter.getValue(); } } return null; } protected boolean matchedAtLeastAllExpectedEntries(final Map expectedEventEntries, final Map actualEventEntries) { for (final Entry expectedEventEntry : expectedEventEntries.entrySet()) { final Serializable expectedValue = expectedEventEntry.getValue(); if (!expectedValue.equals(actualEventEntries.get(expectedEventEntry.getKey()))) { return false; } } return true; } @SuppressWarnings("unchecked") protected Serializable getFiredAndRemoveIt(final Map expectedEvent) { try { List firedEvents = cacheService.getKeys(SYNCHRO_SERVICE_CACHE); for (Map firedEvent : (List>) firedEvents) { if (matchedAtLeastAllExpectedEntries(expectedEvent, firedEvent)) { cacheService.remove(SYNCHRO_SERVICE_CACHE, firedEvent); return firedEvent.get("id"); } } } catch (SCacheException e) { throw new RuntimeException(e); } return null; } protected void throwTimeout(final Map event, final long timeout) throws TimeoutException { throw new TimeoutException( "Event '" + event + "' has not been received on time after waiting '" + timeout + " ms'"); } @Override public void clearAllEvents() { try { cacheService.clear(SYNCHRO_SERVICE_CACHE); } catch (SCacheException e) { throw new RuntimeException(e); } getWaitersMap().clear(); } @Override public boolean hasWaiters() { return !getWaitersMap().isEmpty(); } } ================================================ FILE: bpm/bonita-synchro-repository/bonita-synchro-service-impl/src/main/java/org/bonitasoft/engine/synchro/SynchroServiceImpl.java ================================================ /** * 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.synchro; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.bonitasoft.engine.cache.CacheService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Emmanuel Duchastenier * @author Baptiste Mesta * @author Charles Souillard */ public class SynchroServiceImpl extends AbstractSynchroService { private final Logger logger = LoggerFactory.getLogger(SynchroServiceImpl.class); private final Map, String> waiters; private final Map eventKeyAndIdMap; private final Map eventSemaphores; private final Lock lock = new SynchroServiceImplReentrantLock(); /** * @param initialCapacity * the initial capacity of the map of fired events / waiters (default 50) */ private SynchroServiceImpl(final int initialCapacity, final CacheService cacheService) { super(cacheService); waiters = new HashMap<>(initialCapacity); eventKeyAndIdMap = new HashMap<>(initialCapacity); eventSemaphores = new HashMap<>(); } private static final class SynchroServiceImplReentrantLock extends ReentrantLock { } @Override protected Map, String> getWaitersMap() { return waiters; } @Override protected Logger getLogger() { return logger; } @Override protected Map getEventKeyAndIdMap() { return eventKeyAndIdMap; } @Override protected Lock getServiceLock() { return lock; } @Override protected void releaseWaiter(final String semaphoreKey) { final Semaphore semaphore = eventSemaphores.get(semaphoreKey); if (semaphore != null) { semaphore.release(); } } @Override public Serializable waitForEvent(final Map event, final long timeout) throws InterruptedException, TimeoutException { Serializable id; String semaphoreKey = null; Semaphore semaphore = null; getServiceLock().lock(); try { id = getFiredAndRemoveIt(event); if (id == null) { semaphoreKey = getSemaphoreKey(event); semaphore = new Semaphore(1); eventSemaphores.put(semaphoreKey, semaphore); semaphore.acquire(1); getWaitersMap().put(event, semaphoreKey); } } finally { getServiceLock().unlock(); } if (semaphore != null) { try { if (!semaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS)) { throwTimeout(event, timeout); } } catch (final InterruptedException e) { throwTimeout(event, timeout); } finally { getWaitersMap().remove(event); } return getEventKeyAndIdMap().get(semaphoreKey); } return id; } private String getSemaphoreKey(final Map event) { final StringBuilder sb = new StringBuilder(); final TreeMap orderedMap = new TreeMap<>(event); boolean first = true; for (final Map.Entry entry : orderedMap.entrySet()) { if (!first) { sb.append(";;;;;"); } else { first = false; } sb.append(entry.getKey()); sb.append("="); sb.append(entry.getValue()); } return sb.toString(); } } ================================================ FILE: bpm/bonita-web-extensions/build.gradle ================================================ import org.bonitasoft.engine.gradle.PomUtils dependencies { api(libs.javaxServletApi) api(project(":bpm:bonita-client")) api(project(":bpm:bonita-common")) testImplementation libs.assertj } group = 'org.bonitasoft.web' java { withSourcesJar() withJavadocJar() } publishing { publications { mavenJava(MavenPublication) { from components.java pom { pom -> name = 'Bonita Web Extensions' description = 'Provide API to develop custom web extensions like pages and rest apis' PomUtils.pomCommunityPublication(pom) } } } } ================================================ FILE: bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/ResourceProvider.java ================================================ /** * Copyright (C) 2022 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.web.extension; import java.io.File; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.Locale; import java.util.ResourceBundle; /** * Access to the extension resources */ public interface ResourceProvider { /** * Retrieve a resource as an {@link InputStream} * * @param resourceName the name of the resource to retrieve. It can be a path. * @return a {@link InputStream} for this resource */ InputStream getResourceAsStream(final String resourceName) throws FileNotFoundException; /** * Retrieve a resource as a {@link File} * * @param resourceName the name of the resource to retrieve. It can be a path. * @return a {@link File} for this resource */ File getResourceAsFile(final String resourceName); /** * Retrieve a resource URL * * @param resourceName the name of the resource to retrieve. It can be a path. * @return the URL of where the resource is available. */ String getResourceURL(final String resourceName); /** * Retrieve a {@link ResourceBundle} that can be used for localization. * * @param name the name of the resource to retrieve (e.g. : messages for a resource named messages_fr.properties) * @param locale the {@link Locale} of the {@link ResourceBundle} to retrieve.(e.g. : Locale.FRENCH for a resource * named messages_fr.properties) * @return the {@link ResourceBundle} for given name and {@link Locale} */ ResourceBundle getResourceBundle(final String name, final Locale locale); } ================================================ FILE: bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/page/PageContext.java ================================================ /** * Copyright (C) 2022 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.web.extension.page; import java.util.Locale; import org.bonitasoft.engine.session.APISession; /** * This class provide access to the data relative to the context in which the custom page is displayed * * @since 7.2.0 */ public interface PageContext { /** * @return the engine {@link APISession} */ APISession getApiSession(); /** * @return the user locale */ Locale getLocale(); /** * @return the ID of the profile in which the page is currently displayed */ String getProfileID(); } ================================================ FILE: bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/page/PageController.java ================================================ /** * Copyright (C) 2022 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.web.extension.page; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * The interface to implement for a Custom Page in Bonita. */ public interface PageController { /** * Let the custom page parse request for specific attribute handling. * * @param request * the HTTP servlet request intended to be used as in a servlet * @param response * the HTTP servlet response intended to be used as in a servlet * @param pageResourceProvider * provide access to the resources contained in the custom page zip * @param pageContext * provide access to the data relative to the context in which the custom page is displayed */ void doGet(HttpServletRequest request, HttpServletResponse response, PageResourceProvider pageResourceProvider, PageContext pageContext); } ================================================ FILE: bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/page/PageResourceProvider.java ================================================ /** * Copyright (C) 2022 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.web.extension.page; import java.io.File; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.page.PageNotFoundException; import org.bonitasoft.web.extension.ResourceProvider; /** * This interface provide access to the resources contained in the extension archive * * @since 7.2.0 */ public interface PageResourceProvider extends ResourceProvider { /** * The URL of the CSS used as Bonita Theme. */ String getBonitaThemeCSSURL(); /** * The folder where the extension is deployed. */ File getPageDirectory(); /** * The page name. Set in the page.properties of the extension. */ String getPageName(); /** * The page name with the process definition ID as prefix (format = p%PROCESS_DEF_ID_%pageName). */ String getFullPageName(); /** * The deployed {@link Page} for this extension. */ Page getPage(final PageAPI pageAPI) throws PageNotFoundException; } ================================================ FILE: bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/rest/RestAPIContext.java ================================================ /** * Copyright (C) 2022 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.web.extension.rest; import java.util.Locale; import org.bonitasoft.engine.api.APIClient; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.extension.ResourceProvider; /** * This class provide access to the data relative to the context in which the Rest API extension is called * * @since 7.2.0 */ public interface RestAPIContext { /** * The {@link APIClient} is used to access business data and Bonita APIs such as: *
    *
  • {@link IdentityAPI},
  • *
  • {@link ProcessAPI},
  • *
  • ...
  • *
* * @return an engine {@link APIClient} logged to the current {@link APISession} */ APIClient getApiClient(); /** * @return Current engine {@link APISession} */ APISession getApiSession(); /** * @return Current selected {@link Locale} in BonitaBPM Portal */ Locale getLocale(); /** * @return a {@link ResourceProvider} to retrieve resources location */ ResourceProvider getResourceProvider(); } ================================================ FILE: bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/rest/RestApiController.java ================================================ /** * Copyright (C) 2022 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.web.extension.rest; import javax.servlet.http.HttpServletRequest; /** * The interface to implement for Rest API extension in Bonita. */ public interface RestApiController { /** * Let the Rest API Extension parse request for specific attribute handling. * * @param request the HTTP servlet request intended to be used as in a servlet * @param responseBuilder a builder for HTTP response * @param context to access the current execution context data like current session,locale... * @return a RestApiResponse with a body, an HTTP status and other optional http content * @since 7.2 */ RestApiResponse doHandle(HttpServletRequest request, RestApiResponseBuilder responseBuilder, RestAPIContext context); } ================================================ FILE: bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/rest/RestApiResponse.java ================================================ /** * Copyright (C) 2022 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.web.extension.rest; import java.io.Serializable; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import javax.servlet.http.Cookie; /** * A Wrapper of an HTTP Response describe by its: *
    *
  • Status
  • *
  • Headers
  • *
  • Body
  • *
  • Charset
  • *
  • Media type
  • *
  • Cookies
  • *
*/ public class RestApiResponse { /** * default http status code */ public static final int DEFAULT_STATUS = 200; public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; /** * default character set */ public static final String DEFAULT_CHARACTER_SET = DEFAULT_CHARSET.name(); /** * default media type */ public static final String DEFAULT_MEDIA_TYPE = "application/json"; private final Serializable response; private final int httpStatus; private final Map additionalHeaders; private final List additionalCookies; private final String mediaType; private final String characterSet; public RestApiResponse(Serializable response, int httpStatus, Map additionalHeaders, List additionalCookies, String mediaType, String characterSet) { this.response = response; this.httpStatus = httpStatus; this.additionalHeaders = additionalHeaders; this.additionalCookies = additionalCookies; this.mediaType = mediaType; this.characterSet = characterSet; } public Serializable getResponse() { return response; } public int getHttpStatus() { return httpStatus; } public Map getAdditionalHeaders() { return additionalHeaders; } public List getAdditionalCookies() { return additionalCookies; } public String getCharacterSet() { return characterSet; } public String getMediaType() { return mediaType; } } ================================================ FILE: bpm/bonita-web-extensions/src/main/java/org/bonitasoft/web/extension/rest/RestApiResponseBuilder.java ================================================ /** * Copyright (C) 2022 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.web.extension.rest; import static org.bonitasoft.web.extension.rest.RestApiResponse.*; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import org.apache.http.HttpHeaders; /** * Build a RestApiResponse specifying response body, status and other HTTP attributes. */ public class RestApiResponseBuilder { protected Serializable response; protected int httpStatus; protected final Map additionalHeaders; protected final List additionalCookies; protected String characterSet; protected String mediaType; private int pageIndex = -1; private int pageSize = -1; private long totalSize = -1; public RestApiResponseBuilder() { this.httpStatus = DEFAULT_STATUS; this.additionalHeaders = new HashMap<>(); this.additionalCookies = new ArrayList<>(); this.characterSet = DEFAULT_CHARACTER_SET; this.mediaType = DEFAULT_MEDIA_TYPE; } /** * Set the body of the response * * @param response the response body */ public RestApiResponseBuilder withResponse(Serializable response) { this.response = response; return this; } /** * Set the HTTP status of the response. By default, OK status (200) is set. * * @param httpStatus the HTTP status of the response * @see HttpServletResponse */ public RestApiResponseBuilder withResponseStatus(int httpStatus) { this.httpStatus = httpStatus; return this; } /** * Adds a header in the HTTP response * * @param name the name of the header to add in the response. * @param value the value for this header * @see org.apache.http.HttpHeaders */ public RestApiResponseBuilder withAdditionalHeader(String name, String value) { additionalHeaders.put(name, value); return this; } /** * Adds a cookie to the HTTP response * * @param cookie the {@link javax.servlet.http.Cookie} to add to the response */ public RestApiResponseBuilder withAdditionalCookie(Cookie cookie) { additionalCookies.add(cookie); return this; } /** * Set the character set of the HTTP response. By default UTF-8 is set. * * @param characterSet the name of the character set * @see java.nio.charset.Charset */ public RestApiResponseBuilder withCharacterSet(String characterSet) { this.characterSet = characterSet; return this; } /** * Set the media type of the HTTP response body. By default "application/json" is set. * * @param mediaType the media type to set. * @see Registered media types */ public RestApiResponseBuilder withMediaType(String mediaType) { this.mediaType = mediaType; return this; } /** * When returning a paged result, sets the start index and the page size. * Setting content range overrides the Content-Range header of the response. * * @param pageIndex the start index of the returned page. * @param pageSize the size of the returned page. * @return the {@link RestApiResponseBuilder} */ public RestApiResponseBuilder withContentRange(int pageIndex, int pageSize) { this.pageIndex = pageIndex; this.pageSize = pageSize; return this; } /** * When returning a paged result, sets the start index, the page size and the total size. * Setting content range overrides the Content-Range header of the response. * * @param pageIndex the start index of the returned page. * @param pageSize the size of the returned page. * @param totalSize the total size of the requested entity. * @return the {@link RestApiResponseBuilder} */ public RestApiResponseBuilder withContentRange(int pageIndex, int pageSize, long totalSize) { this.pageIndex = pageIndex; this.pageSize = pageSize; this.totalSize = totalSize; return this; } /** * @return the RestApiResponse response */ public RestApiResponse build() { if (pageIndex >= 0 && pageSize >= 0) { additionalHeaders.put(HttpHeaders.CONTENT_RANGE, String.format("%s-%s/%s", pageIndex, pageSize, totalSize >= 0 ? totalSize : "*")); } return new RestApiResponse(response, httpStatus, additionalHeaders, additionalCookies, mediaType, characterSet); } } ================================================ FILE: bpm/bonita-web-extensions/src/test/java/org/bonitasoft/web/extension/rest/RestApiResponseAssert.java ================================================ /** * Copyright (C) 2022 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.web.extension.rest; import static java.lang.String.format; import java.io.Serializable; import java.util.Map; import javax.servlet.http.Cookie; import org.assertj.core.api.AbstractAssert; import org.assertj.core.api.Assertions; /** * {@link RestApiResponse} specific assertions - Generated by CustomAssertionGenerator. */ public class RestApiResponseAssert extends AbstractAssert { /** * Creates a new {@link RestApiResponseAssert} to make assertions on actual RestApiResponse. * * @param actual the RestApiResponse we want to make assertions on. */ public RestApiResponseAssert(RestApiResponse actual) { super(actual, RestApiResponseAssert.class); } /** * An entry point for RestApiResponseAssert to follow AssertJ standard assertThat() statements.
* With a static import, one's can write directly : assertThat(myRestApiResponse) and get specific * assertion with code completion. * * @param actual the RestApiResponse we want to make assertions on. * @return a new {@link RestApiResponseAssert} */ public static RestApiResponseAssert assertThat(RestApiResponse actual) { return new RestApiResponseAssert(actual); } /** * Verifies that the actual RestApiResponse's additionalCookies contains the given Cookie elements. * * @param additionalCookies the given elements that should be contained in actual RestApiResponse's * additionalCookies. * @return this assertion object. * @throws AssertionError if the actual RestApiResponse's additionalCookies does not contain all given Cookie * elements. */ public RestApiResponseAssert hasAdditionalCookies(Cookie... additionalCookies) { // check that actual RestApiResponse we want to make assertions on is not null. isNotNull(); // check that given Cookie varargs is not null. if (additionalCookies == null) throw new AssertionError("Expecting additionalCookies parameter not to be null."); // check with standard error message (see commented below to set your own message). Assertions.assertThat(actual.getAdditionalCookies()).contains(additionalCookies); // uncomment the 4 lines below if you want to build your own error message : // WritableAssertionInfo assertionInfo = new WritableAssertionInfo(); // String errorMessage = "my error message"; // assertionInfo.overridingErrorMessage(errorMessage); // Iterables.instance().assertContains(assertionInfo, actual.getTeamMates(), teamMates); // return the current assertion for method chaining return this; } /** * Verifies that the actual RestApiResponse has no additionalCookies. * * @return this assertion object. * @throws AssertionError if the actual RestApiResponse's additionalCookies is not empty. */ public RestApiResponseAssert hasNoAdditionalCookies() { // check that actual RestApiResponse we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected :\n <%s>\nnot to have additionalCookies but had :\n <%s>", actual, actual.getAdditionalCookies()); // check if (!actual.getAdditionalCookies().isEmpty()) throw new AssertionError(errorMessage); // return the current assertion for method chaining return this; } /** * Verifies that the actual RestApiResponse's additionalHeaders is equal to the given one. * * @param additionalHeaders the given additionalHeaders to compare the actual RestApiResponse's additionalHeaders * to. * @return this assertion object. * @throws AssertionError - if the actual RestApiResponse's additionalHeaders is not equal to the given one. */ public RestApiResponseAssert hasAdditionalHeaders(Map additionalHeaders) { // check that actual RestApiResponse we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> additionalHeaders to be:\n <%s>\n but was:\n <%s>", actual, additionalHeaders, actual.getAdditionalHeaders()); // check if (!actual.getAdditionalHeaders().equals(additionalHeaders)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual RestApiResponse's characterSet is equal to the given one. * * @param characterSet the given characterSet to compare the actual RestApiResponse's characterSet to. * @return this assertion object. * @throws AssertionError - if the actual RestApiResponse's characterSet is not equal to the given one. */ public RestApiResponseAssert hasCharacterSet(String characterSet) { // check that actual RestApiResponse we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> characterSet to be:\n <%s>\n but was:\n <%s>", actual, characterSet, actual.getCharacterSet()); // check if (!actual.getCharacterSet().equals(characterSet)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual RestApiResponse's httpStatus is equal to the given one. * * @param httpStatus the given httpStatus to compare the actual RestApiResponse's httpStatus to. * @return this assertion object. * @throws AssertionError - if the actual RestApiResponse's httpStatus is not equal to the given one. */ public RestApiResponseAssert hasHttpStatus(int httpStatus) { // check that actual RestApiResponse we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> httpStatus to be:\n <%s>\n but was:\n <%s>", actual, httpStatus, actual.getHttpStatus()); // check if (actual.getHttpStatus() != httpStatus) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual RestApiResponse's mediaType is equal to the given one. * * @param mediaType the given mediaType to compare the actual RestApiResponse's mediaType to. * @return this assertion object. * @throws AssertionError - if the actual RestApiResponse's mediaType is not equal to the given one. */ public RestApiResponseAssert hasMediaType(String mediaType) { // check that actual RestApiResponse we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> mediaType to be:\n <%s>\n but was:\n <%s>", actual, mediaType, actual.getMediaType()); // check if (!actual.getMediaType().equals(mediaType)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual RestApiResponse's response is equal to the given one. * * @param response the given response to compare the actual RestApiResponse's response to. * @return this assertion object. * @throws AssertionError - if the actual RestApiResponse's response is not equal to the given one. */ public RestApiResponseAssert hasResponse(Serializable response) { // check that actual RestApiResponse we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> response to be:\n <%s>\n but was:\n <%s>", actual, response, actual.getResponse()); // check if (!actual.getResponse().equals(response)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } } ================================================ FILE: bpm/bonita-web-extensions/src/test/java/org/bonitasoft/web/extension/rest/RestApiResponseTest.java ================================================ /** * Copyright (C) 2022 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.web.extension.rest; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import javax.servlet.http.Cookie; import org.apache.http.HttpHeaders; import org.junit.Before; import org.junit.Test; public class RestApiResponseTest { RestApiResponseBuilder restApiResponseBuilder; @Before public void setUp() { restApiResponseBuilder = new RestApiResponseBuilder(); } @Test public void testGetResponse_with_default_values() { //when final RestApiResponse response = restApiResponseBuilder.withResponse("response").build(); //then RestApiResponseAssert.assertThat(response) .hasResponse("response") .hasMediaType("application/json") .hasHttpStatus(200) .hasCharacterSet("UTF-8") .hasNoAdditionalCookies(); assertThat(response.getAdditionalHeaders()).isEmpty(); } @Test public void testGetHttpStatus() { //when final RestApiResponse response = restApiResponseBuilder.withResponseStatus(404).build(); //then RestApiResponseAssert.assertThat(response).hasHttpStatus(404); } @Test public void testGetAdditionalHeaders() { //when final RestApiResponse response = restApiResponseBuilder.withAdditionalHeader("x-header1", "value1") .withAdditionalHeader("x-header2", "value2") .build(); //then assertThat(response.getAdditionalHeaders()).hasSize(2).contains(entry("x-header1", "value1"), entry("x-header1", "value1")); } @Test public void testGetAdditionalCookies() { //when final Cookie cookie1 = new Cookie("name1", "value1"); final Cookie cookie2 = new Cookie("name2", "value2"); final RestApiResponse response = restApiResponseBuilder.withAdditionalCookie(cookie1) .withAdditionalCookie(cookie2) .build(); //then RestApiResponseAssert.assertThat(response).hasAdditionalCookies(cookie1, cookie2); } @Test public void testGetCharacterSet() { //given final String characterSet = "utf-16"; //when final RestApiResponse response = restApiResponseBuilder.withCharacterSet(characterSet) .build(); //then RestApiResponseAssert.assertThat(response).hasCharacterSet(characterSet); } @Test public void testGetMediaType() { //when final String mediaType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; //when final RestApiResponse response = restApiResponseBuilder.withMediaType(mediaType) .build(); //then RestApiResponseAssert.assertThat(response).hasMediaType(mediaType); } @Test public void should_not_set_range_when_pageSize_not_set() { //when final RestApiResponse response = restApiResponseBuilder.withContentRange(0, -1) .build(); //then assertThat(response.getAdditionalHeaders()).doesNotContainKey(HttpHeaders.CONTENT_RANGE); } @Test public void should_not_set_range_when_pageIndex_not_set() { //when final RestApiResponse response = restApiResponseBuilder.withContentRange(-1, 10) .build(); //then assertThat(response.getAdditionalHeaders()).doesNotContainKey(HttpHeaders.CONTENT_RANGE); } @Test public void should_set_range_when_pageIndex_and_pageSize_is_set() { //when final RestApiResponse response = restApiResponseBuilder .withContentRange(0, 10) .build(); //then assertThat(response.getAdditionalHeaders()).contains(entry(HttpHeaders.CONTENT_RANGE, "0-10/*")); } @Test public void should_set_range_when_pageIndex_and_pageSize_and_totalSize_is_set() { //when final RestApiResponse response = restApiResponseBuilder .withContentRange(0, 10, 100) .build(); //then assertThat(response.getAdditionalHeaders()).contains(entry(HttpHeaders.CONTENT_RANGE, "0-10/100")); } } ================================================ FILE: bpm/bonita-web-server/build.gradle ================================================ import org.apache.tools.ant.filters.ReplaceTokens group = 'org.bonitasoft.console' description = 'Bonita Web Server' dependencies { implementation(project(":bpm:bonita-common")) implementation(project(":bpm:bonita-web-extensions")) implementation(project(":services:bonita-commons")) implementation libs.commonsIO implementation libs.slf4jApi implementation libs.ehCache implementation libs.commonsFileUpload implementation libs.commonsBeanUtils implementation libs.commonsCollections implementation libs.commonsLang implementation libs.jsonSimple implementation libs.urlrewritefilter implementation libs.jakartaJstl implementation libs.jakartaJstlApi implementation libs.woodstoxCore implementation libs.groovyCore implementation libs.xbeanClassloader implementation libs.springCore implementation libs.springContext implementation libs.springWeb implementation libs.springWebMvc implementation libs.jgettext implementation libs.owaspHtmlSanitizer compileOnly libs.lombok annotationProcessor libs.lombok testImplementation(project(":bpm:bonita-server")) testImplementation(libs.jsonUnit) { exclude(group: "org.slf4j", module: "slf4j-api") } testImplementation libs.assertj testImplementation libs.logback testImplementation libs.springTest testImplementation libs.systemRules testImplementation libs.mockitoCore testImplementation libs.hamcrest testImplementation libs.systemLambda testRuntimeOnly libs.jsonassert } configurations { configureEach { resolutionStrategy.force libs.woodstoxCore } tests } processResources { inputs.property("version", project.version) inputs.property("brandingVersion", brandingVersion) from('src/main/resources') { include '**/*' filter(ReplaceTokens, tokens: [projectVersion : project.version, brandingVersion: brandingVersion, buildYear : String.valueOf(LocalDate.now().getYear())]) } } tasks.register('buildZip', Zip) { from('src/main/resources') { include 'VERSION' filter(ReplaceTokens, tokens: [projectVersion : project.version, brandingVersion: brandingVersion, buildYear : String.valueOf(LocalDate.now().getYear())]) } from 'src/main/webapp' } tasks.named("build") { dependsOn tasks.named("buildZip") } tasks.register('testsJar', Jar) { dependsOn testClasses archiveClassifier = 'tests' from(sourceSets.test.output) } test { dependsOn tasks.named("testsJar") } artifacts { tests testsJar } publishing { publications { mavenJava(MavenPublication) { from components.java artifact testsJar artifact buildZip pom { pom -> name = 'Bonita Web Server' description = 'Web API implementation module' org.bonitasoft.engine.gradle.PomUtils.pomCommunityPublication(pom) } } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/api/CommandCaller.java ================================================ /** * Copyright (C) 2022 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.console.common.server.api; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; /** * @author Vincent Elcrin */ public final class CommandCaller { private final APISession session; private final String command; private final Map parameters; /** * Default Constructor. */ public CommandCaller(APISession session, final String command) { this.session = session; this.command = command; parameters = new HashMap<>(); } public CommandCaller addParameter(final String key, final Serializable value) { parameters.put(key, value); return this; } public Serializable run() { try { return TenantAPIAccessor.getCommandAPI(this.session).execute(this.command, this.parameters); } catch (final BonitaException e) { throw new APIException(e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/api/token/APIToken.java ================================================ /** * Copyright (C) 2022 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.console.common.server.api.token; import java.util.UUID; /** * @author Paul AMAR */ public class APIToken { private final String APIToken; public APIToken() { this.APIToken = UUID.randomUUID().toString(); } public String getToken() { return this.APIToken; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/AuthenticationFailedException.java ================================================ /** * Copyright (C) 2022 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.console.common.server.auth; /** * @author Ruiheng Fan */ public class AuthenticationFailedException extends Exception { /** * */ private static final long serialVersionUID = 1853195048371475012L; /** * Constructor */ public AuthenticationFailedException() { super(); } /** * @param message * message associated with the exception * @param cause * cause of the exception */ public AuthenticationFailedException(final String message, final Throwable cause) { super(message, cause); } /** * @param message * message associated with the exception */ public AuthenticationFailedException(final String message) { super(message); } /** * @param cause * cause of the exception */ public AuthenticationFailedException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/AuthenticationManager.java ================================================ /** * Copyright (C) 2022 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.console.common.server.auth; import java.io.Serializable; import java.util.Map; import javax.servlet.ServletException; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; import org.bonitasoft.console.common.server.login.credentials.Credentials; /** * Interface to implement in order to delegate the authentication to an external provider * * @author Ruiheng Fan, Anthony Birembaut */ public interface AuthenticationManager { /** * Redirection URL parameter name */ String REDIRECT_URL = "redirectUrl"; /** * the URL param for the login page after logout */ String LOGIN_URL_PARAM_NAME = "loginUrl"; /** * the URL param to indicate if we should redirect after login/logout (true by default) */ String REDIRECT_AFTER_LOGIN_PARAM_NAME = "redirect"; /** * the URL of the default login page. Only used when @BONITA_RUNTIME_AUTHENTICATION_LOGIN_URL_VAR is not set */ String LOGIN_PAGE = "/login.jsp"; /* * The system variable or property name for the login page URL */ String BONITA_RUNTIME_AUTHENTICATION_LOGIN_URL_VAR = "bonita.runtime.authentication.login.url"; /** * The default redirect URL. */ String DEFAULT_DIRECT_URL = "apps/appDirectoryBonita"; /** * indicate wheather the HTTP session should be invalidated and re created upon login */ String INVALIDATE_SESSION = "authentication.session.invalidate"; /** * Get Login Page URL * * @param redirectURL * redirect url * @return new redirect url */ String getLoginPageURL(final HttpServletRequestAccessor requestAccessor, final String redirectURL) throws ServletException; /** * Authenticate the user (If no exception is thrown, an engine login will then be performed with the credentials) * * @param credentials * credentials extracted from the request or from the auto-login config * @return a map of credentials which if not empty (and containing more than just the key * "authentication.session.invalidate") will be used to login on the engine. Otherwise, the username and * password contained in the * credentials will be used * @throws AuthenticationFailedException * @throws ServletException */ Map authenticate(final HttpServletRequestAccessor requestAccessor, final Credentials credentials) throws AuthenticationFailedException, ServletException; /** * Get Logout Page URL * If the LoginManager implementation of this method is to return null the default login page will be displayed * * @param redirectURL * redirect url * @return new redirect url */ String getLogoutPageURL(final HttpServletRequestAccessor requestAccessor, final String redirectURL) throws ServletException; } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/AuthenticationManagerFactory.java ================================================ /** * Copyright (C) 2022 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.console.common.server.auth; import org.bonitasoft.console.common.server.auth.impl.standard.StandardAuthenticationManagerImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Ruiheng Fan */ public class AuthenticationManagerFactory { private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationManagerFactory.class.getName()); static AuthenticationManager authenticationManager; public static AuthenticationManager getAuthenticationManager() throws AuthenticationManagerNotFoundException { String authenticationManagerName = null; if (authenticationManager == null) { try { authenticationManagerName = getManagerImplementationClassName(); authenticationManager = (AuthenticationManager) Class.forName(authenticationManagerName).newInstance(); } catch (final Exception e) { final String message = "The AuthenticationManager implementation " + authenticationManagerName + " does not exist!"; throw new AuthenticationManagerNotFoundException(message); } } return authenticationManager; } private static String getManagerImplementationClassName() { String authenticationManagerName = AuthenticationManagerProperties.getProperties() .getAuthenticationManagerImpl(); if (authenticationManagerName == null || authenticationManagerName.isEmpty()) { authenticationManagerName = StandardAuthenticationManagerImpl.class.getName(); LOGGER.trace("The login manager implementation is undefined. Using default implementation : " + authenticationManagerName); } return authenticationManagerName; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/AuthenticationManagerNotFoundException.java ================================================ /** * Copyright (C) 2022 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.console.common.server.auth; /** * @author Ruiheng Fan */ public class AuthenticationManagerNotFoundException extends Exception { /** * */ private static final long serialVersionUID = 1L; /** * @param message * message associated with the exception */ public AuthenticationManagerNotFoundException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/AuthenticationManagerProperties.java ================================================ /** * Copyright (C) 2022 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.console.common.server.auth; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang3.BooleanUtils; import org.bonitasoft.console.common.server.preferences.properties.ConfigurationFile; /** * Utility class for Session Manager access (read in a properties file) * * @author Ruiheng Fan */ public class AuthenticationManagerProperties extends ConfigurationFile { protected static final String AUTHENTICATION_CONFIG_FILE_NAME = "authenticationManager-config.properties"; /** * Logout Hidden constant */ public static final String LOGOUT_DISABLED = "logout.link.hidden"; /** * Configuration of authentication manager implementation */ protected static final String AUTHENTICATION_MANAGER = "auth.AuthenticationManager"; /** * Configuration of OAuth service provider name */ protected static final String OAUTH_SERVICE_PROVIDER = "OAuth.serviceProvider"; /** * Configuration of OAuth consumer key */ protected static final String OAUTH_CONSUMER_KEY = "OAuth.consumerKey"; /** * Configuration of OAuth consumer secret */ protected static final String OAUTH_CONSUMER_SECRET = "OAuth.consumerSecret"; /** * Configuration of OAuth callback URL */ protected static final String OAUTH_CALLBACK_URL = "OAuth.callbackURL"; /** * Configuration of CAS Server URL */ protected static final String CAS_SERVER_URL = "Cas.serverUrlPrefix"; /** * Configuration of CAS Bonita Service URL */ protected static final String CAS_BONITA_SERVICE_URL = "Cas.bonitaServiceURL"; private static final Map> authenticationProperties = new ConcurrentHashMap<>(); /** * properties */ public AuthenticationManagerProperties() { super(AUTHENTICATION_CONFIG_FILE_NAME); } public static AuthenticationManagerProperties getProperties() { return new AuthenticationManagerProperties(); } /** * @return get login manager implementation */ public String getAuthenticationManagerImpl() { return getTenantProperty(AUTHENTICATION_MANAGER); } /** * @return get OAuth service provider name */ public String getOAuthServiceProviderName() { return getTenantProperty(OAUTH_SERVICE_PROVIDER); } /** * @return get OAuth consumer key */ public String getOAuthConsumerKey() { return getTenantProperty(OAUTH_CONSUMER_KEY); } /** * @return get OAuth consumer secret */ public String getOAuthConsumerSecret() { return getTenantProperty(OAUTH_CONSUMER_SECRET); } /** * @return get OAuth callback URL */ public String getOAuthCallbackURL() { return getTenantProperty(OAUTH_CALLBACK_URL); } /** * @return get OAuth callback URL */ public String getCasServerURL() { return getTenantProperty(CAS_SERVER_URL); } /** * @return get OAuth callback URL */ public String getCasBonitaServiceUrl() { return getTenantProperty(CAS_BONITA_SERVICE_URL); } /** * @return if properties are set up to display the logout button */ public boolean isLogoutDisabled() { return BooleanUtils.toBoolean(getTenantProperty(LOGOUT_DISABLED)); } @Override public String getTenantProperty(String propertyName) { Optional propertyValue = authenticationProperties.get(propertyName); if (propertyValue == null) { propertyValue = Optional.ofNullable(super.getTenantProperty(propertyName)); authenticationProperties.put(propertyName, propertyValue); } return propertyValue.orElse(null); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/ConsumerNotFoundException.java ================================================ /** * Copyright (C) 2022 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.console.common.server.auth; import javax.servlet.ServletException; /** * This class cannot be removed without the risk of breaking * old {@link AuthenticationManager} implementations that may still use it * * @deprecated since 8.0.0 * @author Chong Zhao */ @Deprecated(since = "8.0.0") public class ConsumerNotFoundException extends ServletException { /** * serial UID */ private static final long serialVersionUID = -8891020375098355542L; /** * Constructor */ public ConsumerNotFoundException() { super(); } /** * @param message * @param cause */ public ConsumerNotFoundException(final String message, final Throwable cause) { super(message, cause); } /** * @param message */ public ConsumerNotFoundException(final String message) { super(message); } /** * @param cause */ public ConsumerNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/impl/jaas/ConsoleCallbackHandler.java ================================================ /** * Copyright (C) 2022 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.console.common.server.auth.impl.jaas; import java.io.IOException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; /** * Console call back handler * * @author Qixiang Zhang */ public class ConsoleCallbackHandler implements CallbackHandler { /** * User name */ private final String name; /** * User password */ private final String password; /** * Default Constructor. * * @param name * user name * @param password * user password */ public ConsoleCallbackHandler(final String name, final String password) { this.name = name; this.password = password; } /* * (non-Javadoc) * @see javax.security.auth.callback.CallbackHandler#handle(javax.security.auth.callback.Callback[]) */ @Override public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (final Callback callback : callbacks) { if (callback instanceof NameCallback nc) { nc.setName(this.name); } else if (callback instanceof PasswordCallback pc) { pc.setPassword(this.password.toCharArray()); } } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/impl/jaas/ConsoleIdentityLoginModule.java ================================================ /** * Copyright (C) 2022 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.console.common.server.auth.impl.jaas; import java.security.Principal; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.security.auth.Destroyable; import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.login.FailedLoginException; import javax.security.auth.login.LoginException; import javax.security.auth.spi.LoginModule; import org.bonitasoft.console.common.server.login.credentials.LoginDatastore; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.APISession; /** * This {@link LoginModule} is used to verify a user identity against Bonita authentication service. * * @author Qixiang Zhang */ public class ConsoleIdentityLoginModule implements LoginModule { /** * User name prompt */ private static final String NAME_PROMPT = "Name: "; /** * User password prompt */ private static final String PASSWORD_PROMPT = "Password: "; /** * javax security auth login password */ protected static final String JAVAX_SECURITY_AUTH_LOGIN_PASSWORD = "javax.security.auth.login.password"; /** * javax security auth login name */ protected static final String JAVAX_SECURITY_AUTH_LOGIN_NAME = "javax.security.auth.login.name"; /** * Property key for the debug flag. Defined to be "debug". * Property Value. If set, should be either "true" or "false". Default is * "false". */ public static final String DEBUG_OPTION_NAME = "debug"; /** * The subject to be authenticated */ private Subject subject = null; /** * A CallbackHandler for communicating with the end user (prompting * for usernames and passwords, for example) */ private CallbackHandler callbackHandler = null; /** * State shared with other configured LoginModules. */ private Map sharedState; /** * Debug flag */ private boolean debug = false; /** * Principal id */ private String id; /** * Initialize this LoginModule. This method is called by the LoginContext * after this LoginModule has been instantiated. The purpose of this method is * to initialize this LoginModule with the relevant information. If this * LoginModule does not understand any of the data stored in sharedState or * options parameters, they can be ignored. * * @param subject * the Subject to be authenticated. * @param callbackHandler * a CallbackHandler for communicating with the end user (prompting * for usernames and passwords, for example). * @param sharedState * state shared with other configured LoginModules. * @param options * options specified in the login Configuration for this particular * LoginModule. */ @Override @SuppressWarnings("unchecked") public void initialize(final Subject subject, final CallbackHandler callbackHandler, final Map sharedState, final Map options) { this.subject = subject; this.callbackHandler = callbackHandler; this.sharedState = (Map) sharedState; final String debugFlag = (String) options.get(DEBUG_OPTION_NAME); if (debugFlag != null) { this.debug = Boolean.valueOf(debugFlag); } } /** * Method to authenticate a Subject (phase 1). The implementation of this * method authenticates a Subject. For example, it may prompt for Subject * information such as a username and password and then attempt to verify the * password. This method saves the result of the authentication attempt as * private state within the LoginModule. * * @return true if the authentication succeeded, or false if this LoginModule * should be ignored. * @throws LoginException * if the authentication fails */ @Override public boolean login() throws LoginException { if (this.debug) { System.err.println("[" + ConsoleIdentityLoginModule.class.getName() + "] login() - preparing - step 1"); } try { final Map loggingsArgs = getSharedState(); final Map callbacks = getPromptCallbacks(loggingsArgs); if (!callbacks.isEmpty()) { if (this.debug) { System.err.println( "[" + ConsoleIdentityLoginModule.class.getName() + "] login() - callback - step 2"); } this.callbackHandler.handle(callbacks.values().toArray(new Callback[0])); adjustLoggingsArgs(callbacks, loggingsArgs); } if (isDebug()) { System.err.println( "[" + ConsoleIdentityLoginModule.class.getName() + "] login() - authenticating - step 3"); } final APISession aAPISession = (loggingsArgs.containsKey(JAVAX_SECURITY_AUTH_LOGIN_NAME)) ? doLogin(loggingsArgs) : null; if (isDebug()) { System.err.println( "[" + ConsoleIdentityLoginModule.class.getName() + "] login() - storing data - step 4"); } if (aAPISession != null) { this.id = (String) getSharedState().get(JAVAX_SECURITY_AUTH_LOGIN_NAME); } if (isDebug()) { System.err.println("[" + ConsoleIdentityLoginModule.class.getName() + "] login() - returning - step 5"); } if (this.id == null) { throw new FailedLoginException("id is null"); } return true; } catch (final Exception e) { e.printStackTrace(); final LoginException le = new LoginException(); le.initCause(e); throw le; } } /** * Log user in * * @param loggingsArgs * @return * @throws BonitaException */ protected APISession doLogin(final Map loggingsArgs) throws BonitaException { final LoginDatastore loginDatastore = new LoginDatastore(); return loginDatastore.login(String.valueOf(loggingsArgs.get(JAVAX_SECURITY_AUTH_LOGIN_NAME)), String.valueOf(loggingsArgs.get(JAVAX_SECURITY_AUTH_LOGIN_PASSWORD))); } /** * Get callback to prompt authentication to user * * @param loggingsArgs * @return */ protected Map getPromptCallbacks(final Map loggingsArgs) { final Map callbacks = new HashMap<>(); // login if (!loggingsArgs.containsKey(JAVAX_SECURITY_AUTH_LOGIN_NAME)) { callbacks.put(NAME_PROMPT, new NameCallback(NAME_PROMPT)); } // password if (!loggingsArgs.containsKey(JAVAX_SECURITY_AUTH_LOGIN_PASSWORD)) { callbacks.put(PASSWORD_PROMPT, new PasswordCallback(PASSWORD_PROMPT, false)); } return callbacks; } /** * Adjust loggings arguments depending on callbacks results * * @param callbacks * @param loggingsArgs */ protected void adjustLoggingsArgs(final Map callbacks, final Map loggingsArgs) { if (!loggingsArgs.containsKey(JAVAX_SECURITY_AUTH_LOGIN_NAME)) { // update name if (callbacks.get(NAME_PROMPT) instanceof NameCallback) { loggingsArgs.put(JAVAX_SECURITY_AUTH_LOGIN_NAME, ((NameCallback) callbacks.get(NAME_PROMPT)).getName()); } } if (!loggingsArgs.containsKey(JAVAX_SECURITY_AUTH_LOGIN_PASSWORD)) { // update password if (callbacks.get(PASSWORD_PROMPT) instanceof PasswordCallback pwdCallback) { loggingsArgs.put(JAVAX_SECURITY_AUTH_LOGIN_PASSWORD, String.valueOf(pwdCallback.getPassword())); pwdCallback.clearPassword(); } } } /** * Method to commit the authentication process (phase 2). This method is * called if the LoginContext's overall authentication succeeded (the relevant * REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules succeeded). If * this LoginModule's own authentication attempt succeeded (checked by * retrieving the private state saved by the login method), then this method * associates relevant Principals and Credentials with the Subject located in * the LoginModule. If this LoginModule's own authentication attempted failed, * then this method removes/destroys any state that was originally saved. * * @return true if this method succeeded, or false if this LoginModule should * be ignored. * @throws LoginException * if the commit fails */ @Override public boolean commit() throws LoginException { if (this.id == null) { throw new FailedLoginException("id is null"); } final Set principals = this.subject.getPrincipals(); principals.add(new ConsolePrincipal(this.id)); return true; } /** * Method to abort the authentication process (phase 2). This method is called * if the LoginContext's overall authentication failed. (the relevant * REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules did not succeed). * If this LoginModule's own authentication attempt succeeded (checked by * retrieving the private state saved by the login method), then this method * cleans up any state that was originally saved. * * @return true if this method succeeded, or false if this LoginModule should * be ignored. * @throws LoginException * if the abort fails */ @Override public boolean abort() throws LoginException { if (this.debug) { System.err.println("[" + ConsoleIdentityLoginModule.class.getName() + "] abort()"); } if (this.id == null) { return false; } this.subject = null; this.id = null; return true; } /** * Method which logs out a Subject. An implementation of this method might * remove/destroy a Subject's Principals and Credentials. * * @return true if this method succeeded, or false if this LoginModule should * be ignored. * @throws LoginException * if the logout fails */ @Override public boolean logout() throws LoginException { if (this.id != null) { if (this.debug) { System.err .println("[" + ConsoleIdentityLoginModule.class.getName() + "] logout() - removing principals"); } // Remove only principals added by our commit method final Set principals = new HashSet<>(this.subject.getPrincipals()); for (final Principal p : principals) { if (p instanceof ConsolePrincipal) { if (this.debug) { System.err.println("[" + ConsoleIdentityLoginModule.class.getName() + "] logout() - removing principal: " + p); } this.subject.getPrincipals().remove(p); } } if (this.debug) { System.err.println("[" + ConsoleIdentityLoginModule.class.getName() + "] logout() - destroying/removing credentials"); } // Remove/destroy only credentials added by our commit method final Set credentials = new HashSet<>(this.subject.getPublicCredentials()); for (final Object o : credentials) { if (o instanceof Destroyable) { if (this.debug) { System.err.println("[" + ConsoleIdentityLoginModule.class.getName() + "] logout() - destroying credential: " + o); } // Bug: only from this module !! // ((Destroyable) o).destroy(); } if (!this.subject.isReadOnly()) { if (this.debug) { System.err.println("[" + ConsoleIdentityLoginModule.class.getName() + "] logout() - removing credential: " + o); } this.subject.getPublicCredentials().remove(o); } } } return true; } protected Map getSharedState() { return sharedState; } protected boolean isDebug() { return debug; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/impl/jaas/ConsolePrincipal.java ================================================ /** * Copyright (C) 2022 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.console.common.server.auth.impl.jaas; import java.io.Serializable; import java.security.Principal; /** * Subject of principal * * @author Qixiang Zhang */ public class ConsolePrincipal implements Principal, Serializable { /** * Serial version uid */ private static final long serialVersionUID = 1120874595111127685L; /** * Subject identities */ private final String id; /** * Default Constructor. * * @param id * Subject identities */ public ConsolePrincipal(final String id) { this.id = id; } /* * (non-Javadoc) * @see java.security.Principal#getName() */ @Override public String getName() { return this.id; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/auth/impl/standard/StandardAuthenticationManagerImpl.java ================================================ /** * Copyright (C) 2022 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.console.common.server.auth.impl.standard; import java.io.Serializable; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Map; import javax.servlet.ServletException; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.console.common.server.auth.AuthenticationFailedException; import org.bonitasoft.console.common.server.auth.AuthenticationManager; import org.bonitasoft.console.common.server.auth.AuthenticationManagerProperties; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; import org.bonitasoft.console.common.server.login.credentials.Credentials; import org.bonitasoft.console.common.server.utils.LocaleUtils; import org.bonitasoft.console.common.server.utils.UrlBuilder; import org.bonitasoft.engine.properties.StringProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Chong Zhao */ public class StandardAuthenticationManagerImpl implements AuthenticationManager { /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(StandardAuthenticationManagerImpl.class.getName()); protected String loginPageURL = null; @Override public String getLoginPageURL(final HttpServletRequestAccessor request, final String redirectURL) throws ServletException { final UrlBuilder loginURL = new UrlBuilder(getLoginPage(request)); //adds the locale to the login URL if it is set in the requested URL String localeFromRequestedURL = LocaleUtils.getLocaleFromRequestURL(request.asHttpServletRequest()); if (localeFromRequestedURL != null) { loginURL.appendParameter(LocaleUtils.PORTAL_LOCALE_PARAM, localeFromRequestedURL); } if (StringUtils.isNotBlank(redirectURL)) { //Decodes the redirect URL if it is encoded because LoginUrl already encodes it (avoid double encoding) //since this method is part of a public interface, we cannot change the calling code to pass decoded redirectURL String decodedRedirectURL = URLDecoder.decode(redirectURL, StandardCharsets.UTF_8); loginURL.appendParameter(AuthenticationManager.REDIRECT_URL, decodedRedirectURL); } return loginURL.build(); } @Override public Map authenticate(final HttpServletRequestAccessor requestAccessor, final Credentials credentials) throws AuthenticationFailedException { if (LOGGER.isDebugEnabled()) { LOGGER.debug("#authenticate (this implementation of " + AuthenticationManager.class.getName() + " does nothing. The subsequent engine login is enough to authenticate the user.)"); } return Collections.emptyMap(); } @Override public String getLogoutPageURL(final HttpServletRequestAccessor request, final String redirectURL) throws ServletException { return null; } protected String getLoginPage(HttpServletRequestAccessor requestAccessor) { //the login page cannot be different from one request to another, so only compute it once if (loginPageURL == null) { StringProperty loginPage = new StringProperty("External Login URL", AuthenticationManager.BONITA_RUNTIME_AUTHENTICATION_LOGIN_URL_VAR, getAuthenticationProperty( AuthenticationManager.BONITA_RUNTIME_AUTHENTICATION_LOGIN_URL_VAR, getDefaultLoginPage(requestAccessor))); loginPageURL = loginPage.getValue(); } return loginPageURL; } protected String getDefaultLoginPage(HttpServletRequestAccessor requestAccessor) { final StringBuilder url = new StringBuilder(); String context = requestAccessor.asHttpServletRequest().getContextPath(); url.append(context).append(LOGIN_PAGE); return url.toString(); } protected String getAuthenticationProperty(String propertyName, String defaultValue) { String propertyValue = AuthenticationManagerProperties.getProperties().getTenantProperty(propertyName); return propertyValue != null ? propertyValue : defaultValue; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/CacheFilter.java ================================================ /** * Copyright (C) 2022 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.console.common.server.filter; import java.io.IOException; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author Haojie Yuan * @author Benjamin Parisel * @author Anthony Birembaut */ public class CacheFilter extends ExcludingPatternFilter { public static final String ALWAYS_CACHING = "alwaysCaching"; public static final String NO_CUSTOMPAGE_CACHE = "noCacheCustomPage"; protected static final String CACHE_FILTER_EXCLUDED_RESOURCES_PATTERN = "^/(bonita/)?(apps/.+/$)|(portal/resource/.+/content/$)|(portal/custom-page/.+/$)|(portal/custom-page/API/)"; protected Map paramMap = new HashMap<>(); private final String DURATION = "duration"; @Override public void init(final FilterConfig filterConfig) throws ServletException { super.init(filterConfig); final Enumeration names = filterConfig.getInitParameterNames(); while (names.hasMoreElements()) { final String name = (String) names.nextElement(); final String value = filterConfig.getInitParameter(name); paramMap.put(name, value); } } public String getDefaultExcludedPages() { return CACHE_FILTER_EXCLUDED_RESOURCES_PATTERN; } @Override public void proceedWithFiltering(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException { final HttpServletRequest req = (HttpServletRequest) request; final HttpServletResponse res = (HttpServletResponse) response; setResponseHeader(res); chain.doFilter(req, res); } @Override public void excludePatternFiltering(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { final HttpServletRequest req = (HttpServletRequest) request; final HttpServletResponse res = (HttpServletResponse) response; res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate"); chain.doFilter(req, res); } private void setResponseHeader(final HttpServletResponse response) { final Integer duration = Integer.valueOf(paramMap.get(DURATION)); final boolean alwaysCaching = Boolean.parseBoolean(paramMap.get(ALWAYS_CACHING)); final boolean noCustomPageCache = Boolean.parseBoolean(System.getProperty(NO_CUSTOMPAGE_CACHE)); if (!noCustomPageCache || alwaysCaching) { response.setHeader("Cache-Control", "max-age=" + duration.intValue()); } else { response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate"); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/ExcludingPatternFilter.java ================================================ /** * Copyright (C) 2022 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.console.common.server.filter; import java.io.IOException; import java.util.regex.Pattern; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; /** * Base filter that supports URL exclude patterns and ensures each filter instance * executes at most once per request (similar to Spring's {@code OncePerRequestFilter}). *

* The once-per-request guard uses a request attribute named {@code ".FILTERED"} * (where {@code } is the {@code } declared in {@code web.xml}). * This allows filter-mappings to include both REQUEST and FORWARD dispatchers for * defense-in-depth, without the filter logic running twice when a URL-rewritten request * is forwarded internally (e.g. {@code /APIToolkit/*} forwarded to {@code /API/*}). *

* Because the attribute key is derived from the filter name (not the class name), * two distinct {@code } declarations of the same class (e.g. {@code CacheFilter} * and {@code CustomPageCacheFilter}) are tracked independently. * * @author Anthony Birembaut */ public abstract class ExcludingPatternFilter implements Filter { protected URLExcludePattern urlExcludePattern; /** * Request attribute name used to mark that this filter instance has already * been applied to the current request. Derived from the filter-name in web.xml. */ private String alreadyFilteredAttrName; @Override public void init(FilterConfig filterConfig) throws ServletException { alreadyFilteredAttrName = filterConfig.getFilterName() + ".FILTERED"; urlExcludePattern = new URLExcludePattern(filterConfig, getDefaultExcludedPages()); } @Override public void destroy() { } @Override public final void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // Skip if this filter instance already executed for this request (e.g. on FORWARD // after the initial REQUEST dispatch). This prevents double-filtering while still // allowing the filter-mapping to cover both REQUEST and FORWARD dispatchers. if (request.getAttribute(alreadyFilteredAttrName) != null) { chain.doFilter(request, response); return; } request.setAttribute(alreadyFilteredAttrName, Boolean.TRUE); final String url = ((HttpServletRequest) request).getRequestURL().toString(); if (matchExcludePatterns(url)) { excludePatternFiltering(request, response, chain); } else { proceedWithFiltering(request, response, chain); } } public void excludePatternFiltering(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request, response); } public abstract void proceedWithFiltering(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException; public abstract String getDefaultExcludedPages(); public Pattern compilePattern(final String stringPattern) { return urlExcludePattern.compilePattern(stringPattern); } /** * check the given url against the local url exclude pattern * * @param url * the url to check * @return true if the url match the pattern */ public boolean matchExcludePatterns(final String url) { return urlExcludePattern.matchExcludePatterns(url); } public Pattern getExcludePattern() { return urlExcludePattern.getExcludePattern(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/MultiReadHttpServletRequest.java ================================================ /** * Copyright (C) 2022 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.console.common.server.filter; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.io.IOUtils; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; public class MultiReadHttpServletRequest extends HttpServletRequestWrapper { private File tempFile; private ByteArrayOutputStream readBytes; private boolean isMultipart; public MultiReadHttpServletRequest(final HttpServletRequest request) { super(request); isMultipart = ServletFileUpload.isMultipartContent(request); } @Override public ServletInputStream getInputStream() throws IOException { if (!isMultipart && readBytes == null || isMultipart && (tempFile == null || !tempFile.exists())) { readInputStream(); } return new CachedServletInputStream(); } @Override public BufferedReader getReader() throws IOException { String enc = getCharacterEncoding(); if (enc == null) { enc = "UTF-8"; } return new BufferedReader(new InputStreamReader(getInputStream(), enc)); } private void readInputStream() throws IOException { if (!isMultipart) { readBytes = new ByteArrayOutputStream(); IOUtils.copy(super.getInputStream(), readBytes); } else { File tempDir = WebBonitaConstantsUtils.getPlatformInstance().getTempFolder(); if (!tempDir.exists()) { tempDir.mkdirs(); } tempFile = File.createTempFile("tmp_", ".part", tempDir); tempFile.deleteOnExit(); try (FileOutputStream fileOutput = new FileOutputStream(tempFile)) { IOUtils.copy(super.getInputStream(), fileOutput); } } } public void cleanMultipartTempContent() { if (tempFile != null && tempFile.exists()) { tempFile.delete(); } // clean recursively if there are several layers of wrapped requests if (getRequest() instanceof MultiReadHttpServletRequest) { ((MultiReadHttpServletRequest) getRequest()).cleanMultipartTempContent(); } } class CachedServletInputStream extends ServletInputStream { private final InputStream input; private ReadListener readListener; public CachedServletInputStream() throws IOException { if (!isMultipart) { input = new ByteArrayInputStream(readBytes.toByteArray()); } else { input = new FileInputStream(tempFile); } readListener = null; } @Override public int read() throws IOException { int nextByte = input.read(); if (nextByte == -1) { onAllDataRead(); } return nextByte; } @Override public int read(final byte[] b) throws IOException { int numberOfBytesRead = input.read(b); if (numberOfBytesRead == -1) { onAllDataRead(); } return numberOfBytesRead; } @Override public int read(final byte[] b, final int off, final int len) throws IOException { int numberOfBytesRead = input.read(b, off, len); if (numberOfBytesRead == -1) { onAllDataRead(); } return numberOfBytesRead; } @Override public void close() throws IOException { input.close(); super.close(); } @Override public boolean isFinished() { try { return input.available() == 0; } catch (IOException e) { // stream has been closed return true; } } @Override public boolean isReady() { return !isFinished(); } @Override public void setReadListener(ReadListener readListener) { this.readListener = readListener; if (!isFinished()) { try { readListener.onDataAvailable(); } catch (IOException e) { readListener.onError(e); } } else { onAllDataRead(); } } private void onAllDataRead() { if (readListener != null) { try { readListener.onAllDataRead(); } catch (IOException e) { readListener.onError(e); } } } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/NoCacheFilter.java ================================================ /** * Copyright (C) 2022 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.console.common.server.filter; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; /** * This Filter adds headers to the response to prevent caching of the page/resource. * It extends ExcludingPatternFilter, so that it benefits from its once-per-request behavior. * * @author Paul AMAR */ public class NoCacheFilter extends ExcludingPatternFilter { @Override public void proceedWithFiltering(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { final HttpServletResponse res = (HttpServletResponse) response; res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate"); chain.doFilter(request, res); } @Override public String getDefaultExcludedPages() { return ""; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/PathSanitizer.java ================================================ /** * Copyright (C) 2026 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.console.common.server.filter; import java.util.regex.Pattern; import lombok.extern.slf4j.Slf4j; /** * Utility class to sanitize URL paths by stripping path parameters (semicolons). *

* Per RFC 3986, semicolons in path segments introduce matrix parameters. * Some servlet containers (e.g. Tomcat) strip semicolons when resolving * {@code getRequestDispatcher()} paths, while {@code java.net.URI.normalize()} * treats them as literal characters. This differential can be exploited for * path traversal attacks (e.g. {@code /API/..;/..;/serverAPI}). * Using this class prevents such attacks by normalizing and sanitizing * URLs before processing them (fixes Bonita CVE-233). *

*/ @Slf4j public final class PathSanitizer { private static final Pattern SEMICOLON_PARAMS = Pattern.compile(";[^/]*"); private PathSanitizer() { } /** * Strips path parameters (everything from ';' to the next '/' or end of string) * from each segment of the given path. *

* Example: {@code /API/..;/..;anything/serverAPI} becomes {@code /API/../../serverAPI} *

* * @param path the URL path to sanitize * @return the sanitized path with path parameters removed, or {@code null} if the input is {@code null} */ public static String stripPathParameters(String path) { if (path == null || path.indexOf(';') < 0) { return path; } String sanitized = SEMICOLON_PARAMS.matcher(path).replaceAll(""); log.debug("Path parameters stripped from URL path: [{}] -> [{}]", path, sanitized); return sanitized; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/RedirectFilter.java ================================================ /** * 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.console.common.server.filter; import java.io.IOException; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.console.common.server.auth.AuthenticationManager; /** * Filter that redirects to a URL specified as a parameter in the request. * The URL must be relative to the current domain. * * @author Haroun EL ALAMI */ @Slf4j public class RedirectFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; final HttpServletResponse httpResponse = (HttpServletResponse) servletResponse; String redirectUrl = httpRequest.getParameter(AuthenticationManager.REDIRECT_URL); if (StringUtils.isNoneBlank(redirectUrl)) { // avoid redirecting to a different domain if (!redirectUrl.contains("//")) { httpResponse.sendRedirect(redirectUrl); return; } // If the redirectUrl is not valid log.warn("Invalid redirect URL: {}", redirectUrl); } filterChain.doFilter(servletRequest, servletResponse); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/RequestIdFilter.java ================================================ /** * 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.console.common.server.filter; import static org.bonitasoft.engine.mdc.MDCConstants.CORRELATION_REQUEST_ID; import static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_ID; import static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_METHOD; import static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_QUERY_STRING; import static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_REMOTE_HOST_MDC_KEY; import static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_REQUEST_URI; import static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_REQUEST_URL; import static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_USER_AGENT_MDC_KEY; import static org.bonitasoft.engine.mdc.MDCConstants.REQUEST_X_FORWARDED_FOR; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.function.Predicate; import java.util.function.Supplier; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory; import org.bonitasoft.engine.mdc.AbstractMDC; import org.bonitasoft.engine.mdc.MDCHelper; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; public class RequestIdFilter implements Filter { private String requestIdAttributeName = PropertiesFactory.getConsoleProperties().getRequestIdAttributeName(); private String requestIdHeaderName = PropertiesFactory.getConsoleProperties().getRequestIdHeaderName(); private String correlationIdAttributeName = PropertiesFactory.getConsoleProperties() .getCorrelationIdAttributeName(); private String correlationIdHeaderName = PropertiesFactory.getConsoleProperties().getCorrelationIdHeaderName(); /** * Check if the current request has a requestId and a correlationId. * If no requestId is found, tries and finds in header or generates a new requestId and attach it to the request. */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // get request id already attached var attachedReqIdAtt = Optional.ofNullable((String) request.getAttribute(requestIdAttributeName)); var requestId = attachedReqIdAtt.orElseGet(() -> { // look in header String headerRequestId = null; if (request instanceof HttpServletRequest httpRequest) { headerRequestId = httpRequest.getHeader(requestIdHeaderName); } var id = Optional.ofNullable(headerRequestId).orElseGet(() -> { // generate a new request id return Long.toHexString(System.nanoTime()); }); // attach it request.setAttribute(requestIdAttributeName, id); return id; }); // find correlation id var attachedCorrelIdAtt = Optional.ofNullable((String) request.getAttribute(correlationIdAttributeName)); var correlationId = attachedCorrelIdAtt.or(() -> { // look in header String headerCorrelId = null; if (request instanceof HttpServletRequest httpRequest) { headerCorrelId = httpRequest.getHeader(correlationIdHeaderName); } var id = Optional.ofNullable(headerCorrelId); // attach it id.ifPresent(i -> request.setAttribute(correlationIdAttributeName, i)); return id; }); Supplier mdc = () -> new AbstractMDC(buildContextMap(requestId, correlationId, request)) { }; MDCHelper.CheckedRunnable2 call = () -> { chain.doFilter(request, response); }; MDCHelper.tryWithMDC(mdc, call); } private static Map buildContextMap(String requestId, Optional correlationId, ServletRequest request) { Map map = new HashMap(9); // insert request id and correlation id map.put(REQUEST_ID, Objects.requireNonNull(requestId)); Predicate isBlank = StringUtil::isBlank; correlationId.filter(isBlank.negate()) .ifPresent(id -> map.put(CORRELATION_REQUEST_ID, Objects.requireNonNull(id))); // insert extra information similar to ch.qos.logback.classic.helpers.MDCInsertingServletFilter map.put(REQUEST_REMOTE_HOST_MDC_KEY, request.getRemoteHost()); if (request instanceof HttpServletRequest http) { map.put(REQUEST_REQUEST_URI, http.getRequestURI()); Optional.ofNullable(http.getRequestURL()).ifPresent(url -> map.put(REQUEST_REQUEST_URL, url.toString())); map.put(REQUEST_METHOD, http.getMethod()); Optional.ofNullable(http.getQueryString()).ifPresent(str -> map.put(REQUEST_QUERY_STRING, str)); Optional.ofNullable(http.getHeader("User-Agent")) .ifPresent(agent -> map.put(REQUEST_USER_AGENT_MDC_KEY, agent)); Optional.ofNullable(http.getHeader("X-Forwarded-For")) .ifPresent(forwarded -> map.put(REQUEST_X_FORWARDED_FOR, forwarded)); } return map; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/SanitizerFilter.java ================================================ /** * 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.console.common.server.filter; import static java.lang.String.format; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.stream.Stream; import javax.servlet.FilterChain; import javax.servlet.ReadListener; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory; import org.owasp.html.HtmlChangeListener; import org.owasp.html.HtmlPolicyBuilder; import org.owasp.html.PolicyFactory; import org.owasp.html.Sanitizers; import org.springframework.util.StringUtils; import org.springframework.web.util.HtmlUtils; /** * This class is used to filter malicious payload (e.g. XSS injection) by * neutralizing the injected code. * * @author Vincent Hemery */ @Slf4j public class SanitizerFilter extends ExcludingPatternFilter { private static final HtmlPolicyBuilder CUSTOM_POLICY = new HtmlPolicyBuilder().allowElements("a", "pre") .allowStandardUrlProtocols() .allowAttributes("href").onElements("a").requireRelsOnLinks("noopener", "noreferrer", "nofollow"); /** * Sanitizer to apply to values. */ private static final PolicyFactory sanitizer = Sanitizers.BLOCKS.and(Sanitizers.FORMATTING).and(Sanitizers.STYLES) .and(Sanitizers.IMAGES).and(Sanitizers.TABLES).and(CUSTOM_POLICY.toFactory()); /** * The HTTP methods concerned by this filter. */ private static final String[] CONCERNED_METHODS = { "POST", "PUT", "PATCH" }; /** * Json object mapper */ private final ObjectMapper mapper = new ObjectMapper(); private final boolean isEnabled = PropertiesFactory.getSecurityProperties().isSanitizerProtectionEnabled(); private final List attributesExcluded = PropertiesFactory.getSecurityProperties() .getAttributeExcludedFromSanitizerProtection(); private final HtmlChangeListener changeListener = new HtmlChangeListener<>() { @Override public void discardedTag(Object context, String elementName) { if (log.isDebugEnabled()) { log.debug(format("Tag '%s' has been discarded", elementName)); } } @Override public void discardedAttributes(Object context, String elementName, String... attributeNames) { if (log.isDebugEnabled()) { log.debug(format("Tag '%s' has been cleaned from the following attributes: %s", elementName, String.join(", ", attributeNames))); } } }; @Override public void destroy() { } @Override public String getDefaultExcludedPages() { // excludes nothing for now return ""; } @Override public void proceedWithFiltering(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { final HttpServletRequest req = (HttpServletRequest) request; var method = req.getMethod(); if (!isSanitizerEnabled() || method == null || Stream.of(CONCERNED_METHODS).noneMatch(method::equalsIgnoreCase)) { // no body to sanitize, just skip this filter chain.doFilter(req, response); return; } // get body of request as Json var body = getJsonBody(req); if (body == null) { // request has no body chain.doFilter(req, response); return; } // sanitize body final var sanitized = sanitize(body); if (sanitized.isEmpty() && req instanceof MultiReadHttpServletRequest) { // Body was not altered and request is a MultiReadHttpServletRequest // (supports re-reading): pass the original request through unchanged chain.doFilter(req, response); return; } // Wrap the request with body bytes when either: // - the body was sanitized (must forward sanitized content, even if the request is // already a MultiReadHttpServletRequest from an earlier filter), or // - the request is a plain HttpServletRequest (input stream was consumed by // getJsonBody and cannot be re-read, so we must restore it). byte[] saneBodyBytes = mapper.writeValueAsBytes(sanitized.orElse(body)); final var wrapper = new HttpServletRequestWrapper(req) { private ServletInputStream inputStream = null; @Override public ServletInputStream getInputStream() throws IOException { if (inputStream == null) { final ByteArrayInputStream is = new ByteArrayInputStream(saneBodyBytes); inputStream = new ServletInputStream() { @Override public int read() throws IOException { return is.read(); } @Override public boolean isFinished() { return is.available() == 0; } @Override public boolean isReady() { return !isFinished(); } @Override public void setReadListener(ReadListener readListener) { throw new UnsupportedOperationException( "Unimplemented method 'setReadListener'"); } }; } return inputStream; } @Override public int getContentLength() { return saneBodyBytes.length; } @Override public long getContentLengthLong() { return saneBodyBytes.length; } @Override public BufferedReader getReader() throws IOException { String enc = Optional.ofNullable(getCharacterEncoding()).orElse("UTF-8"); return new BufferedReader(new InputStreamReader(getInputStream(), enc)); } }; chain.doFilter(wrapper, response); } /** * Sanitize the given JsonNode. * * @param node Json node to sanitize * @return the sanitized Json node if it has effectively changed */ protected Optional sanitize(JsonNode node) { if (node == null) { return Optional.empty(); } else if (node.isObject()) { AtomicBoolean changed = new AtomicBoolean(false); ObjectNode object = (ObjectNode) node; List operationsToPerformAfterIteration = new ArrayList<>(); object.fields().forEachRemaining(entry -> { var key = entry.getKey(); var newKey = sanitizeValueAndPerformAction(key, s -> { // can't remove key while iterating, or we get a ConcurrentModificationException operationsToPerformAfterIteration.add(() -> { object.remove(key); object.set(s, entry.getValue()); }); changed.set(true); }).orElse(key); if (getAttributesExcluded() == null || !getAttributesExcluded().contains(key)) { var value = entry.getValue(); sanitize(value).ifPresent(v -> { object.set(newKey, v); changed.set(true); }); } }); operationsToPerformAfterIteration.forEach(Runnable::run); return changed.get() ? Optional.of(object) : Optional.empty(); } else if (node.isArray()) { AtomicBoolean changed = new AtomicBoolean(false); ArrayNode array = (ArrayNode) node; for (int i = 0; i < array.size(); i++) { var value = array.get(i); final int index = i; sanitize(value).ifPresent(v -> { array.set(index, v); changed.set(true); }); } return changed.get() ? Optional.of(array) : Optional.empty(); } else if (node.isValueNode()) { if (node.isBoolean() || node.isNumber() || node.isPojo() || StringUtils.isEmpty(node.textValue())) { // that's safe return Optional.empty(); } var changedValue = sanitizeValueAndPerformAction(node.textValue(), v -> { }); return changedValue.map(TextNode::new); } return Optional.empty(); } private JsonNode getJsonBody(HttpServletRequest request) throws ServletException { if (request.getContentType() != null && request.getContentType().toLowerCase().startsWith("application/json")) { try (ServletInputStream inputStream = request.getInputStream()) { if (inputStream == null) { return null; } String characterEncoding = Optional.ofNullable(request.getCharacterEncoding()).orElse("UTF-8"); var stringBody = IOUtils.toString(inputStream, characterEncoding); if (!stringBody.isBlank()) { return mapper.readTree(stringBody); } } catch (IOException e) { throw new ServletException(e); } } return null; } /** * Sanitize the value and perform the action only when value has changed * * @param value String value to sanitize * @param action action to perform when value has changed * @return the sanitized value if it has changed */ private Optional sanitizeValueAndPerformAction(String value, Consumer action) { /* * Sanitize the value. * It's not just about applying the sanitizer... * We want the value to contain unescaped characters, but no script. * To avoid values with multiple escaping, we unescape until the value does not * change. * Then we apply the sanitizer. * And finally, we unescape once again to get values that the frontend can * easily handle. */ var previous = value; var unescaped = HtmlUtils.htmlUnescape(previous); while (!unescaped.equals(previous)) { previous = unescaped; unescaped = HtmlUtils.htmlUnescape(previous); } var sanitized = sanitizer.sanitize(unescaped, changeListener, null); sanitized = HtmlUtils.htmlUnescape(sanitized); // check whether value has effectively changed before doing anything if (!sanitized.equals(value)) { log.warn("Incoming HTML content has been altered. More details at debug level"); action.accept(sanitized); return Optional.of(sanitized); } return Optional.empty(); } public List getAttributesExcluded() { return attributesExcluded; } public boolean isSanitizerEnabled() { return isEnabled; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/filter/URLExcludePattern.java ================================================ /** * Copyright (C) 2022 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.console.common.server.filter; import java.net.URI; import java.util.regex.Pattern; import javax.servlet.FilterConfig; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.util.UriUtils; public class URLExcludePattern { /** * the Pattern of url not to filter */ public final Pattern excludePattern; private static final Logger LOGGER = LoggerFactory.getLogger(URLExcludePattern.class.getName()); public URLExcludePattern(final FilterConfig filterConfig, String defaultExcludePattern) { final String contextPath = filterConfig.getServletContext().getContextPath(); String processedDefaultExcludePattern; if (contextPath.length() > 0 && !contextPath.equals("/bonita")) { String webappName = contextPath.substring(1); processedDefaultExcludePattern = defaultExcludePattern.replace("bonita", webappName); } else { processedDefaultExcludePattern = defaultExcludePattern; } final String configExcludePattern = filterConfig.getInitParameter("excludePattern"); excludePattern = compilePattern( StringUtils.defaultString(configExcludePattern, processedDefaultExcludePattern)); } protected Pattern compilePattern(final String stringPattern) { if (StringUtils.isNotBlank(stringPattern)) { try { return Pattern.compile(stringPattern); } catch (final Exception e) { LOGGER.error("impossible to create pattern from [ {} ] : ", stringPattern, e); } } return null; } /** * check the given url against the local url exclude pattern * * @param url the url to check * @return true if the url match the pattern */ public boolean matchExcludePatterns(final String url) { if (getExcludePattern() == null) { return false; } try { // URL-decode then strip path parameters (semicolons) to handle both // literal ';' and percent-encoded '%3b' before normalization, preventing // ..;-based traversal from fooling the exclude pattern check. // URI handles both absolute (http://host/path) and relative (/path) references. URI uri = new URI(url); String rawPath = uri.getRawPath(); if (rawPath == null) { rawPath = url; } String decodedPath = java.net.URLDecoder.decode(rawPath, "UTF-8"); String sanitizedPath = PathSanitizer.stripPathParameters(decodedPath); // Re-encode the sanitized path before creating the URI for normalization, // because URL-decoding may have introduced literal spaces (from %20) that // are illegal in URI syntax per RFC 3986. String normalizedPath = new URI(UriUtils.encodePath(sanitizedPath, "UTF-8")) .normalize().getPath(); boolean isExcluded = getExcludePattern().matcher(sanitizedPath).find() && getExcludePattern().matcher(normalizedPath).find(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Exclude pattern {} with this url: {}", isExcluded ? "match" : "does not match", url); } return isExcluded; } catch (final Exception e) { LOGGER.warn("impossible to get URL from given input [{}]: {}", url, e); return false; } } public Pattern getExcludePattern() { return excludePattern; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/form/ProcessFormService.java ================================================ /** * Copyright (C) 2022 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.console.common.server.form; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ProcessFormService { private static final Logger LOGGER = LoggerFactory.getLogger(ProcessFormService.class.getName()); public String getProcessPath(final APISession apiSession, final long processDefinitionId) throws BonitaException, UnsupportedEncodingException { final ProcessDeploymentInfo processDeploymentInfo = getProcessAPI(apiSession) .getProcessDeploymentInfo(processDefinitionId); return encodePathSegment(processDeploymentInfo.getName()) + "/" + encodePathSegment(processDeploymentInfo.getVersion()); } public String encodePathSegment(final String stringToEncode) throws UnsupportedEncodingException { //URLEncoder#encode encodes spaces to '+' but we want '%20' instead in the path part of the URL // '/' gets encoded to %2F whereas is should be left as it is since it in perfectly safe in the path part of the URL // '+' gets encoded to %2B whereas is should be left as it is since it in perfectly safe in the path part of the URL return URLEncoder.encode(stringToEncode, StandardCharsets.UTF_8).replaceAll("\\+", "%20").replaceAll("%2F", "/") .replaceAll("%2B", "+"); } public long getProcessDefinitionId(final APISession apiSession, final String processName, final String processVersion) throws BonitaException { if (processName != null && processVersion != null) { try { return getProcessAPI(apiSession).getProcessDefinitionId(processName, processVersion); } catch (final ProcessDefinitionNotFoundException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Wrong parameters for process name and version", e); } } } return -1L; } public long getTaskInstanceId(final APISession apiSession, final long processInstanceId, final String taskName, final long userId) throws BonitaException { final ProcessAPI processAPI = getProcessAPI(apiSession); long ensuredUserId; if (userId != -1L) { ensuredUserId = userId; } else { ensuredUserId = apiSession.getUserId(); } final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 1); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.PARENT_CONTAINER_ID, processInstanceId); searchOptionsBuilder.filter(HumanTaskInstanceSearchDescriptor.NAME, taskName); final SearchResult searchMyAvailableHumanTasks = processAPI.searchMyAvailableHumanTasks( ensuredUserId, searchOptionsBuilder.done()); if (searchMyAvailableHumanTasks.getCount() > 0) { return searchMyAvailableHumanTasks.getResult().get(0).getId(); } else { final SearchOptionsBuilder archivedSearchOptionsBuilder = new SearchOptionsBuilder(0, 1); archivedSearchOptionsBuilder.filter(ArchivedHumanTaskInstanceSearchDescriptor.ASSIGNEE_ID, ensuredUserId); archivedSearchOptionsBuilder.filter(ArchivedHumanTaskInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID, processInstanceId); archivedSearchOptionsBuilder.filter(ArchivedHumanTaskInstanceSearchDescriptor.NAME, taskName); final SearchResult searchArchivedHumanTasks = processAPI .searchArchivedHumanTasks(archivedSearchOptionsBuilder .done()); if (searchArchivedHumanTasks.getCount() > 0) { return searchArchivedHumanTasks.getResult().get(0).getSourceObjectId(); } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Could not find task available with name " + taskName + " for process instance " + processInstanceId); } throw new ActivityInstanceNotFoundException(-1L); } } } public String getTaskName(final APISession apiSession, final long taskInstanceId) throws BonitaException { if (taskInstanceId != -1L) { final ProcessAPI processAPI = getProcessAPI(apiSession); try { final ActivityInstance activity = processAPI.getActivityInstance(taskInstanceId); return activity.getName(); } catch (final ActivityInstanceNotFoundException e) { final ArchivedActivityInstance activity = processAPI.getArchivedActivityInstance(taskInstanceId); return activity.getName(); } } return null; } public long ensureProcessDefinitionId(final APISession apiSession, final long processDefinitionId, final long processInstanceId, final long taskInstanceId) throws BonitaException { if (processDefinitionId != -1L) { return processDefinitionId; } else if (processInstanceId != -1L) { return getProcessDefinitionIdFromProcessInstanceId(apiSession, processInstanceId); } else { return getProcessDefinitionIdFromTaskId(apiSession, taskInstanceId); } } protected long getProcessDefinitionIdFromTaskId(final APISession apiSession, final long taskInstanceId) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, ActivityInstanceNotFoundException { final ProcessAPI processAPI = getProcessAPI(apiSession); try { final ActivityInstance activity = processAPI.getActivityInstance(taskInstanceId); return activity.getProcessDefinitionId(); } catch (final ActivityInstanceNotFoundException e) { final ArchivedActivityInstance activity = processAPI.getArchivedActivityInstance(taskInstanceId); return activity.getProcessDefinitionId(); } } protected long getProcessDefinitionIdFromProcessInstanceId(final APISession apiSession, final long processInstanceId) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, ArchivedProcessInstanceNotFoundException { final ProcessAPI processAPI = getProcessAPI(apiSession); try { final ProcessInstance processInstance = processAPI.getProcessInstance(processInstanceId); return processInstance.getProcessDefinitionId(); } catch (final ProcessInstanceNotFoundException e) { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 1); searchOptionsBuilder.filter(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, processInstanceId); searchOptionsBuilder.sort(ArchivedProcessInstancesSearchDescriptor.ARCHIVE_DATE, Order.ASC); SearchResult searchArchivedProcessInstances = null; try { searchArchivedProcessInstances = processAPI .searchArchivedProcessInstancesInAllStates(searchOptionsBuilder.done()); } catch (final SearchException se) { throw new ArchivedProcessInstanceNotFoundException(se); } if (searchArchivedProcessInstances != null && searchArchivedProcessInstances.getCount() > 0) { return searchArchivedProcessInstances.getResult().get(0).getProcessDefinitionId(); } else { throw new ArchivedProcessInstanceNotFoundException(processInstanceId); } } } protected ProcessAPI getProcessAPI(final APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getProcessAPI(apiSession); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/form/ProcessFormServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.form; import java.io.IOException; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.bonitasoft.console.common.server.page.PageServlet; import org.bonitasoft.console.common.server.page.ResourceRenderer; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.console.common.server.utils.UrlBuilder; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.APISession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Servlet allowing to display a form for a process or a task * The servlet only redirect to the generic Page servlet with the right URL * * @author Anthony Birembaut */ public class ProcessFormServlet extends HttpServlet { /** * UUID */ private static final long serialVersionUID = -6397856355139281873L; /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(ProcessFormServlet.class.getName()); private static final String PAGE_SERVLET_MAPPING = "/portal/resource/"; private static final String PROCESS_PATH_SEGMENT = "process"; private static final String PROCESS_INSTANCE_PATH_SEGMENT = "processInstance"; private static final String TASK_INSTANCE_PATH_SEGMENT = "taskInstance"; private static final String TASK_PATH_SEGMENT = "task"; private static final String USER_ID_PARAM = "user"; private static final String ID_PARAM = "id"; protected ProcessFormService processFormService = new ProcessFormService(); private final ResourceRenderer resourceRenderer = new ResourceRenderer(); @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { long processDefinitionId = -1L; long processInstanceId = -1L; long taskInstanceId = -1L; String taskName = null; final List pathSegments = resourceRenderer.getPathSegments(request.getPathInfo()); final String user = request.getParameter(USER_ID_PARAM); final long userId = convertToLong(USER_ID_PARAM, user); final HttpSession session = request.getSession(); final APISession apiSession = (APISession) session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY); try { if (pathSegments.size() > 1) { taskInstanceId = getTaskInstanceId(apiSession, pathSegments, userId); processInstanceId = getProcessInstanceId(pathSegments); processDefinitionId = getProcessDefinitionId(apiSession, pathSegments); } if (processDefinitionId == -1L && processInstanceId == -1L && taskInstanceId == -1L) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Either process name and version are required or process instance Id (with or without task name) or task instance Id."); return; } processDefinitionId = processFormService.ensureProcessDefinitionId(apiSession, processDefinitionId, processInstanceId, taskInstanceId); taskName = processFormService.getTaskName(apiSession, taskInstanceId); redirectToPageServlet(request, response, apiSession, processDefinitionId, processInstanceId, taskInstanceId, taskName); } catch (final Exception e) { handleException(response, processDefinitionId, taskName, processInstanceId != -1L, e); } } @SuppressWarnings("unchecked") protected void redirectToPageServlet(final HttpServletRequest request, final HttpServletResponse response, final APISession apiSession, final long processDefinitionId, final long processInstanceId, final long taskInstanceId, final String taskName) throws BonitaException, IOException { final String pageServletURL = buildPageServletURL(request, apiSession, processDefinitionId, processInstanceId, taskInstanceId, taskName); final UrlBuilder urlBuilder = new UrlBuilder(pageServletURL); urlBuilder.appendParameters(request.getParameterMap()); response.sendRedirect(response.encodeRedirectURL(urlBuilder.build())); } protected String buildPageServletURL(final HttpServletRequest request, final APISession apiSession, final long processDefinitionId, final long processInstanceId, final long taskInstanceId, final String taskName) throws BonitaException, IOException { final StringBuilder pageServletURL = new StringBuilder(request.getContextPath()); pageServletURL.append(PAGE_SERVLET_MAPPING); if (taskInstanceId != -1L) { pageServletURL.append(TASK_INSTANCE_PATH_SEGMENT) .append("/") .append(processFormService.getProcessPath(apiSession, processDefinitionId)) .append("/") .append(processFormService.encodePathSegment(taskName)) .append(PageServlet.RESOURCE_PATH_SEPARATOR) .append("/?") .append(ID_PARAM) .append("=") .append(taskInstanceId); } else if (processInstanceId != -1L) { pageServletURL.append(PROCESS_INSTANCE_PATH_SEGMENT) .append("/") .append(processFormService.getProcessPath(apiSession, processDefinitionId)) .append(PageServlet.RESOURCE_PATH_SEPARATOR) .append("/?") .append(ID_PARAM) .append("=") .append(processInstanceId); } else { pageServletURL.append(PROCESS_PATH_SEGMENT) .append("/") .append(processFormService.getProcessPath(apiSession, processDefinitionId)) .append(PageServlet.RESOURCE_PATH_SEPARATOR) .append("/?") .append(ID_PARAM) .append("=") .append(processDefinitionId); } return pageServletURL.toString(); } protected long getProcessInstanceId(final List pathSegments) { long processInstanceId = -1L; if (PROCESS_INSTANCE_PATH_SEGMENT.equals(pathSegments.get(0))) { final String processInstance = pathSegments.get(1); processInstanceId = convertToLong(PROCESS_INSTANCE_PATH_SEGMENT, processInstance); } return processInstanceId; } protected long getTaskInstanceId(final APISession apiSession, final List pathSegments, final long userId) throws BonitaException { if (TASK_INSTANCE_PATH_SEGMENT.equals(pathSegments.get(0))) { final String taskInstance = pathSegments.get(1); return convertToLong(TASK_INSTANCE_PATH_SEGMENT, taskInstance); } else if (PROCESS_INSTANCE_PATH_SEGMENT.equals(pathSegments.get(0))) { final String processInstance = pathSegments.get(1); final long processInstanceId = convertToLong(PROCESS_INSTANCE_PATH_SEGMENT, processInstance); if (pathSegments.size() > 2 && TASK_PATH_SEGMENT.equals(pathSegments.get(2))) { final String taskName = URLDecoder.decode(pathSegments.get(3), StandardCharsets.UTF_8); return processFormService.getTaskInstanceId(apiSession, processInstanceId, taskName, userId); } } return -1L; } protected long getProcessDefinitionId(final APISession apiSession, final List pathSegments) throws BonitaException { long processDefinitionId = -1L; if (PROCESS_PATH_SEGMENT.equals(pathSegments.get(0))) { if (pathSegments.size() > 2) { final String processName = pathSegments.get(1); final String processVersion = pathSegments.get(2); processDefinitionId = processFormService.getProcessDefinitionId(apiSession, processName, processVersion); } } return processDefinitionId; } protected long convertToLong(final String parameterName, final String idAsString) { if (idAsString != null) { try { return Long.parseLong(idAsString); } catch (final NumberFormatException e) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Wrong value for " + parameterName + " expecting a number (long value)"); } } } return -1; } protected void handleException(final HttpServletResponse response, final long processDefinitionId, final String taskName, final boolean hasProcessInstanceId, final Exception e) throws ServletException { try { if (e instanceof ProcessDefinitionNotFoundException) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "Cannot find the process"); } else if (e instanceof ArchivedProcessInstanceNotFoundException) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "Cannot find the process instance"); } else if (e instanceof ActivityInstanceNotFoundException) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "Cannot find the task instance"); } else { if (LOGGER.isWarnEnabled()) { String message = "Error while trying to display a form"; if (processDefinitionId != -1) { message = message + " for process " + processDefinitionId; } if (taskName != null) { message = message + " for task " + taskName; } else if (hasProcessInstanceId) { message = message + " ( instance overview)"; } LOGGER.warn(message, e); } response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } } catch (final IOException ioe) { throw new ServletException(ioe); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/i18n/FileUtils.java ================================================ /** * Copyright (C) 2022 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.console.common.server.i18n; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * @author Vincent Elcrin */ public class FileUtils { public static List getMatchingFiles(final String regex, List files) { List locales = new ArrayList<>(); for (File file : files) { if (isFileMatching(file, regex)) { locales.add(file); } } return locales; } private static boolean isFileMatching(File file, String regex) { return file.isFile() && file.getName().matches(regex); } public static List listDir(File dir) { File[] files = dir.listFiles(); if (files == null) { files = new File[0]; } return Arrays.asList(files); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/i18n/I18n.java ================================================ /** * Copyright (C) 2022 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.console.common.server.i18n; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.bonitasoft.web.toolkit.client.common.texttemplate.TextTemplate; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; /** * @author Paul AMAR, Vincent Elcrin */ public class I18n extends AbstractI18n { /** * Property that can be set to override the default i18n *.po files, or to provide translations for new languages. * Points to a folder that contains .po files */ public static final String I18N_CUSTOM_DIR_PROPERTY = "org.bonitasoft.i18n.folder"; private static File I18N_CUSTOM_DIR = getI18nCustomDirectory(); private I18n() { // Singleton } public static I18n getInstance() { if (I18N_instance == null) { I18N_instance = new I18n(); } return (I18n) I18N_instance; } // For test matters only: public static void setInstance(I18n instance) { I18N_instance = instance; } // For test matters only: public void refresh() { I18N_CUSTOM_DIR = getI18nCustomDirectory(); } private static File getI18nCustomDirectory() { String customI18nFolder = System.getProperty(I18N_CUSTOM_DIR_PROPERTY); return customI18nFolder != null ? new File(customI18nFolder) : null; } @Override public void loadLocale(final LOCALE locale) { Map results = loadLocale(getStreams(locale)); if (I18N_CUSTOM_DIR != null) { results.putAll(loadLocale(locale, FileUtils.listDir(I18N_CUSTOM_DIR))); } setLocale(locale, results); } private Map loadLocale(List streams) { TreeMap treeMap = new TreeMap<>(); for (InputStream stream : streams) { treeMap.putAll(parsePoResource(stream)); } return treeMap; } private TreeMap loadLocale(final LOCALE locale, List files) { TreeMap treeMap = new TreeMap<>(); for (File file : getLocaleFiles(locale, files)) { treeMap.putAll(parsePoFile(file)); } return treeMap; } public List getStreams(LOCALE locale) { ResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver(); try { Resource[] resources = patternResolver.getResources("classpath*:i18n/" + getLocaleRegexForResource(locale)); List streams = new ArrayList<>(resources.length); for (Resource resource : resources) { InputStream stream = resource.getInputStream(); streams.add(stream); } return streams; } catch (IOException e) { e.printStackTrace(); return Collections.emptyList(); } } private Map parsePoFile(final File file) { return POParser.parse(file); } private Map parsePoResource(final InputStream stream) { return POParser.parsePOFromStream(stream); } private List getLocaleFiles(final LOCALE locale, List files) { return FileUtils.getMatchingFiles(makeLocaleRegex(locale), files); } private String makeLocaleRegex(final LOCALE locale) { return "(.*)" + locale.toString().trim() + ".po"; } private String getLocaleRegexForResource(final LOCALE locale) { return "*" + locale.toString().trim() + ".po"; } public Map getAvailableLocalesFor(String application) { final Map results = new LinkedHashMap<>(); final Map locales = getLocales(); // Add English by default since _en.po files have been removed results.put("en", "English"); for (final String locale : locales.keySet()) { if (localeExists(locale, application)) { results.put(locale, locales.get(locale)); } } return results; } /** * @return available locale file for a specified application (i.e application_locale.po) */ private static boolean localeExists(String locale, String application) { final String fileName = application + "_" + locale + ".po"; final File file = new File(I18N_CUSTOM_DIR, fileName); return file.exists() || I18n.class.getResource("/i18n/" + fileName) != null; } @Override protected String getText(String string) { return string; } @Override protected String getText(String string, Arg... args) { return new TextTemplate(string).toString(args); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/i18n/POParser.java ================================================ /** * Copyright (C) 2022 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.console.common.server.i18n; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Map; import java.util.TreeMap; import org.fedorahosted.tennera.jgettext.Catalog; import org.fedorahosted.tennera.jgettext.Message; import org.fedorahosted.tennera.jgettext.PoParser; import org.fedorahosted.tennera.jgettext.catalog.parse.ParseException; /** * @author Séverin Moussel */ public class POParser { private final Map translations = new TreeMap<>(); private POParser() { } public static Map parse(final File file) { return new POParser().parseFile(file); } private Map parseFile(final File file) { try { parseCatalog(new PoParser().parseCatalog(file)); } catch (FileNotFoundException e) { throw new RuntimeException("I18N: File not found " + file.getPath()); } catch (ParseException e) { throw new RuntimeException("I18N: Could not parse po file " + file.getPath()); } catch (IOException e) { throw new RuntimeException("I18N: IO issues while parsing " + file.getPath()); } return this.translations; } public static Map parsePOFromStream(final InputStream stream) { return new POParser().parseFromStream(stream); } private Map parseFromStream(final InputStream stream) { try { parseCatalog(new PoParser().parseCatalog(stream, false)); } catch (IOException e) { throw new RuntimeException("I18N: IO issues while parsing translation resource " + stream.toString()); } return this.translations; } private void parseCatalog(Catalog catalog) { for (Message msg : catalog) { translations.put(msg.getMsgid(), msg.getMsgstr()); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/jsp/JSPI18n.java ================================================ /** * Copyright (C) 2022 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.console.common.server.jsp; import org.bonitasoft.console.common.server.i18n.I18n; import org.bonitasoft.console.common.server.utils.LocaleUtils; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE; /** * Used in JSP. Do not delete. */ public class JSPI18n { private final LOCALE locale; private final JSPUtils jspUtils; public JSPI18n(JSPUtils jspUtils) { this.jspUtils = jspUtils; this.locale = loadLocale(); // initialize I18n instance I18n.getInstance(); } public String t_(String message) { return I18n.t_(message, locale); } public LOCALE getLocale() { return locale; } private LOCALE loadLocale() { String localeAsString = LocaleUtils.getUserLocaleAsString(jspUtils.getRequest()); LOCALE locale = null; try { locale = LOCALE.valueOf(localeAsString); } catch (IllegalArgumentException e) { //this should not happen as the LOCALE enum is supposed to contain all locales LocaleUtils.logUnsupportedLocale(localeAsString, LocaleUtils.DEFAULT_LOCALE); } return locale; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/jsp/JSPUtils.java ================================================ /** * Copyright (C) 2022 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.console.common.server.jsp; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; /** * @author Séverin Moussel */ public class JSPUtils { private final HttpServletRequest request; private final HttpSession session; public JSPUtils(final HttpServletRequest request, final HttpSession session) { super(); this.request = request; this.session = session; } public String getSessionOrCookie(final String name, final String defaultValue) { // Try reading in session String result = (String) this.session.getAttribute(name); // else, try reading in cookies if (result == null) { final Cookie[] cookies = this.request.getCookies(); if (cookies != null) { for (int i = 0; i < cookies.length; i++) { if (cookies[i].getName().equals(name)) { result = cookies[i].getValue(); break; } } } } // else, set default value if (result == null) { result = defaultValue; } return result; } public String getParameter(final String name, final String defaultValue) { String result = this.request.getParameter(name); if (result == null) { result = defaultValue; } return result; } public String getParameter(final String name) { return this.getParameter(name, null); } public HttpServletRequest getRequest() { return this.request; } public boolean getParameter(final String name, final boolean defaultValue) { final String result = this.request.getParameter(name); if (result != null) { try { // If boolean is passed as an integer final int intValue = Integer.parseInt(result); return intValue > 0; } catch (final NumberFormatException e) { // If boolean is passed as a recognized string if ("false".equalsIgnoreCase(result) || "no".equals(result)) { return false; } if ("true".equalsIgnoreCase(result) || "yes".equals(result)) { return false; } } } return defaultValue; } public int getParameter(final String name, final int defaultValue) { final String result = this.request.getParameter(name); if (result != null) { try { // If boolean is passed as an integer return Integer.parseInt(result); } catch (final NumberFormatException e) { // DO nothing } } return defaultValue; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/AccountLockedException.java ================================================ /** * Copyright (C) 2026 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.console.common.server.login; import org.bonitasoft.web.server.login.LoginFailureTracker; /** * Thrown when a login attempt is rejected because the user account has been temporarily locked * due to too many consecutive failed authentication attempts. *

* This exception is raised by {@link LoginManager} when the {@link LoginFailureTracker} detects that the maximum * number of allowed failures has been exceeded for a given username. The account remains locked until the configured * lockout duration expires. * * @see LoginFailureTracker * @see LoginManager */ public class AccountLockedException extends LoginFailedException { private static final long serialVersionUID = 1L; public AccountLockedException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/HttpServletRequestAccessor.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.bonitasoft.console.common.server.auth.AuthenticationManager; import org.bonitasoft.console.common.server.utils.LocaleUtils; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.session.APISession; /** * @author Vincent Elcrin */ public class HttpServletRequestAccessor { protected static final String REDIRECT_URL = AuthenticationManager.REDIRECT_URL; public static final String USERNAME_PARAM = "username"; public static final String PASSWORD_PARAM = "password"; private final static String OAUTH_VERIFIER = "oauth_verifier"; private final static String OAUTH_TOKEN = "oauth_token"; private final HttpServletRequest httpServletRequest; public HttpServletRequestAccessor(final HttpServletRequest httpServletRequest) { this.httpServletRequest = httpServletRequest; } public String getUsername() { return httpServletRequest.getParameter(USERNAME_PARAM); } public String getPassword() { return httpServletRequest.getParameter(PASSWORD_PARAM); } public HttpSession getHttpSession() { return httpServletRequest.getSession(); } public HttpSession getHttpSession(boolean create) { return httpServletRequest.getSession(create); } public String getRedirectUrl() { return httpServletRequest.getParameter(REDIRECT_URL); } public String getOAuthToken() { return httpServletRequest.getParameter(OAUTH_TOKEN); } public String getOAuthVerifier() { return httpServletRequest.getParameter(OAUTH_VERIFIER); } public APISession getApiSession() { return (APISession) getHttpSession().getAttribute(SessionUtil.API_SESSION_PARAM_KEY); } @SuppressWarnings("unchecked") public Map getParameterMap() { return httpServletRequest.getParameterMap(); } public String getRequestedUrl() { return httpServletRequest.getRequestURL().toString(); } public String getRequestedUri() { return httpServletRequest.getRequestURI(); } public HttpServletRequest asHttpServletRequest() { return httpServletRequest; } public String getUserAgent() { return httpServletRequest.getHeader("User-Agent"); } public String getLocale() { return LocaleUtils.getUserLocaleAsString(httpServletRequest); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/LoginFailedException.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login; /** * @author Ruiheng Fan */ public class LoginFailedException extends Exception { /** * */ private static final long serialVersionUID = 1853195048371475012L; /** * Constructor */ public LoginFailedException() { super(); } /** * @param message * message associated with the exception * @param cause * cause of the exception */ public LoginFailedException(final String message, final Throwable cause) { super(message, cause); } /** * @param message * message associated with the exception */ public LoginFailedException(final String message) { super(message); } /** * @param cause * cause of the exception */ public LoginFailedException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/LoginManager.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login; import java.io.Serializable; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.console.common.server.auth.AuthenticationFailedException; import org.bonitasoft.console.common.server.auth.AuthenticationManager; import org.bonitasoft.console.common.server.auth.AuthenticationManagerFactory; import org.bonitasoft.console.common.server.auth.AuthenticationManagerNotFoundException; import org.bonitasoft.console.common.server.login.credentials.Credentials; import org.bonitasoft.console.common.server.login.credentials.StandardCredentials; import org.bonitasoft.console.common.server.login.credentials.UserLogger; import org.bonitasoft.console.common.server.login.filter.TokenGenerator; import org.bonitasoft.console.common.server.utils.LocaleUtils; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.exception.TenantStatusException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.user.User; import org.bonitasoft.web.server.login.LoginFailureTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class performs the authentication, the login and initializes the HTTP session * * @author Anthony Birembaut */ public class LoginManager { private static final Logger LOGGER = LoggerFactory.getLogger(LoginManager.class.getName()); protected TokenGenerator tokenGenerator = new TokenGenerator(); protected PortalCookies portalCookies = new PortalCookies(); protected LoginFailureTracker loginFailureTracker; /** * @deprecated Use {@link #LoginManager(LoginFailureTracker)} instead. * This constructor disables brute-force login protection. */ @Deprecated public LoginManager() { this(null); } public LoginManager(LoginFailureTracker loginFailureTracker) { this.loginFailureTracker = loginFailureTracker; } /** * Performs the login using the appropriate AuthenticationManager implementation for authentication, logging in on * the engine, initializing the HTTP session, and creating the cookies (tanat and CSRF) */ public void login(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticationManagerNotFoundException, LoginFailedException, TenantStatusException, AuthenticationFailedException, ServletException { final HttpServletRequestAccessor requestAccessor = new HttpServletRequestAccessor(request); final StandardCredentials userCredentials = createUserCredentials(requestAccessor); loginInternal(requestAccessor, response, getUserLogger(), userCredentials); } public void loginInternal(HttpServletRequestAccessor request, HttpServletResponse response, UserLogger userLoger, Credentials credentials) throws AuthenticationFailedException, ServletException, LoginFailedException { String username = credentials.getName(); boolean shouldTrackLoginFailures = loginFailureTracker != null && StringUtils.isNotEmpty(username); // Note: returning 429 (locked) vs 401 (bad credentials) reveals whether a username exists. // This is an intentional usability trade-off — the lockout message helps legitimate users // understand why they cannot log in, and username enumeration is low-risk given that // the login page itself already exposes whether an account is valid. if (shouldTrackLoginFailures && loginFailureTracker.isLockedOut(username)) { LOGGER.warn( "Login attempt rejected for user [{}]: account temporarily locked due to too many failed attempts", username); throw new AccountLockedException("Too many failed login attempts. Please try again later."); } AuthenticationManager authenticationManager = getAuthenticationManager(); APISession apiSession; boolean invalidateAndRecreateHTTPSession; try { Map credentialsMap = authenticationManager.authenticate(request, credentials); // In case of a login with the login service we invalidate the session and create a new one. // Otherwise, logging in with the credentials in the request (SSO) it is not mandatory it depends on the AuthenticationManager implementation used // some SSO mechanisms already handle it (SAML, OIDC). Boolean invalidateAndRecreateHTTPSessionIfSet = (Boolean) credentialsMap .remove(AuthenticationManager.INVALIDATE_SESSION); invalidateAndRecreateHTTPSession = invalidateAndRecreateHTTPSessionIfSet == null || invalidateAndRecreateHTTPSessionIfSet.booleanValue(); if (credentialsMap.isEmpty()) { if (credentials.getName() == null || credentials.getName().isEmpty()) { LOGGER.debug("There are no credentials in the request"); throw new AuthenticationFailedException("No credentials in request"); } } apiSession = loginWithAppropriateCredentials(userLoger, credentials, credentialsMap); } catch (LoginFailedException | AuthenticationFailedException e) { if (shouldTrackLoginFailures) { loginFailureTracker.recordFailure(username); } throw e; } if (shouldTrackLoginFailures) { loginFailureTracker.resetFailures(username); } storeCredentials(request, apiSession, invalidateAndRecreateHTTPSession); portalCookies.addCSRFTokenCookieToResponse(request.asHttpServletRequest(), response, tokenGenerator.createOrLoadToken(request.getHttpSession())); } protected StandardCredentials createUserCredentials(final HttpServletRequestAccessor requestAccessor) { return new StandardCredentials(requestAccessor.getUsername(), requestAccessor.getPassword()); } protected UserLogger getUserLogger() { return new UserLogger(); } public AuthenticationManager getAuthenticationManager() throws ServletException { try { final AuthenticationManager authenticationManager = AuthenticationManagerFactory.getAuthenticationManager(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Using the AuthenticationManager implementation: " + authenticationManager.getClass().getName()); } return authenticationManager; } catch (final AuthenticationManagerNotFoundException e) { throw new ServletException(e); } } private APISession loginWithAppropriateCredentials(UserLogger userLoger, Credentials credentials, Map credentialsMap) throws LoginFailedException { if (MapUtils.isEmpty(credentialsMap)) { LOGGER.debug("Engine login using the username and password"); return userLoger.doLogin(credentials); } else { LOGGER.debug("Engine login using the map of credentials retrieved from the request"); return userLoger.doLogin(credentialsMap); } } protected void storeCredentials(final HttpServletRequestAccessor request, final APISession session, boolean recreateHTTPSession) throws LoginFailedException { String local = LocaleUtils.getUserLocaleAsString(request.asHttpServletRequest()); // Use APISession username instead of request parameter for SSO compatibility (OIDC, SAML, etc.) final User user = new User(session.getUserName(), local); initSession(request, session, user, recreateHTTPSession); } protected void initSession(final HttpServletRequestAccessor request, final APISession session, final User user, boolean recreateHTTPSession) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("HTTP session initialization"); } if (recreateHTTPSession) { //invalidating session allows to fix session fixation security issue request.getHttpSession().invalidate(); } //calling request.getSession() creates a new Session if no any valid exists SessionUtil.sessionLogin(user, session, request.getHttpSession()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/PortalCookies.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.console.common.server.login.filter.TokenGenerator; import org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory; public class PortalCookies { /** * set the CSRF security token to the HTTP response as cookie. */ public void addCSRFTokenCookieToResponse(HttpServletRequest request, HttpServletResponse res, Object apiTokenFromClient) { // set the cookie path to / if the app is deployed at root context String defaultCookiePath = StringUtils.isEmpty(request.getContextPath()) ? "/" : request.getContextPath(); String path = System.getProperty("bonita.csrf.cookie.path", defaultCookiePath); invalidatePreviousCSRFTokenCookie(request, res, path); Cookie csrfCookie = new Cookie(TokenGenerator.X_BONITA_API_TOKEN, apiTokenFromClient.toString()); // cookie path can be set via system property. // Can be set to '/' when another app is deployed in same server than bonita and want to share csrf cookie csrfCookie.setPath(path); csrfCookie.setSecure(isCSRFTokenCookieSecure()); res.addCookie(csrfCookie); } // when a cookie already exists on a different path than the one expected, we need to invalidate it. // since there is no way of knowing the path as it is not sent server-side (getPath return null) we invalidate any cookie found // see https://bonitasoft.atlassian.net/browse/BS-15883 and BS-16241 private void invalidatePreviousCSRFTokenCookie(HttpServletRequest request, HttpServletResponse res, String path) { Cookie cookie = getCookie(request, TokenGenerator.X_BONITA_API_TOKEN); if (cookie != null) { cookie.setMaxAge(0); cookie.setValue(""); res.addCookie(cookie); invalidateRootCookie(res, cookie, path); } } //Studio sets the cookie to the path '/' so this is required when using a bunble after a studio in the same browser public void invalidateRootCookie(HttpServletResponse res, Cookie cookie, String path) { if (!"/".equals(path)) { Cookie rootCookie = (Cookie) cookie.clone(); rootCookie.setPath("/"); res.addCookie(rootCookie); } } // protected for testing public boolean isCSRFTokenCookieSecure() { return PropertiesFactory.getSecurityProperties().isCSRFTokenCookieSecure(); } public Cookie getCookie(HttpServletRequest request, String name) { Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if (cookie.getName().equals(name)) { return cookie; } } } return null; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/credentials/Credentials.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.credentials; /** * @author Vincent Elcrin */ public interface Credentials { String getName(); String getPassword(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/credentials/LoginDatastore.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.credentials; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.api.LoginAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.platform.LoginException; import org.bonitasoft.engine.platform.LogoutException; import org.bonitasoft.engine.platform.UnknownUserException; import org.bonitasoft.engine.session.APISession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Yongtao Guo */ public class LoginDatastore { /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(LoginDatastore.class.getName()); public APISession login(final String username, final String password) throws BonitaException { APISession apiSession; final String errorMessage = "Error while logging in the engine API."; try { if (username == null || password == null) { LOGGER.error(errorMessage); throw new LoginException(errorMessage); } apiSession = getLoginAPI().login(username, password); } catch (final UnknownUserException e) { LOGGER.error(e.getMessage()); throw e; } catch (final LoginException e) { LOGGER.error(errorMessage); throw e; } catch (final BonitaException e) { LOGGER.error(e.getMessage()); throw e; } return apiSession; } /** * login. * * @return APISession aAPISession * @throws BonitaException */ public APISession login(final Map credentials) throws BonitaException { APISession apiSession; try { if (credentials == null) { final String errorMessage = "Error while logging in on the engine API."; if (LOGGER.isErrorEnabled()) { LOGGER.error(errorMessage); } throw new LoginException(errorMessage); } apiSession = getLoginAPI().login(credentials); } catch (final LoginException e) { final String errorMessage = "Error while logging in on the engine API."; if (LOGGER.isErrorEnabled()) { LOGGER.error(errorMessage); } throw new BonitaException(e); } return apiSession; } /** * logout . * * @throws BonitaException */ public void logout(final APISession apiSession) throws BonitaException { if (apiSession != null) { try { getLoginAPI().logout(apiSession); } catch (final LogoutException e) { final String errorMessage = "Logout error while calling the engine API."; if (LOGGER.isErrorEnabled()) { LOGGER.error(errorMessage); } throw new BonitaException(errorMessage, e); } } } protected LoginAPI getLoginAPI() throws BonitaException { try { return TenantAPIAccessor.getLoginAPI(); } catch (final BonitaException e) { final String errorMessage = "Error while getting the loginAPI."; if (LOGGER.isErrorEnabled()) { LOGGER.error(errorMessage); } throw new BonitaException(errorMessage, e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/credentials/StandardCredentials.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.credentials; /** * @author Julien Reboul */ public class StandardCredentials implements Credentials { private final String name; private final String password; public StandardCredentials(final String name, final String password) { this.name = name; this.password = password; } /** * @see org.bonitasoft.console.common.server.login.credentials.Credentials#getName() */ @Override public String getName() { return name; } /** * @see org.bonitasoft.console.common.server.login.credentials.Credentials#getPassword() */ @Override public String getPassword() { return password; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/credentials/UserLogger.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.credentials; import java.io.Serializable; import java.util.Map; import org.bonitasoft.console.common.server.login.LoginFailedException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.APISession; /** * @author Vincent Elcrin */ public class UserLogger { /** * Overridden in SP */ public APISession doLogin(Credentials credentials) throws LoginFailedException { try { return getDatastore().login(credentials.getName(), credentials.getPassword()); } catch (final BonitaException e) { throw new LoginFailedException(e.getMessage(), e); } } public APISession doLogin(Map credentials) throws LoginFailedException { try { return getDatastore().login(credentials); } catch (final BonitaException e) { throw new LoginFailedException(e.getMessage(), e); } } public void doLogout(final APISession apiSession) throws LoginFailedException { try { getDatastore().logout(apiSession); } catch (final BonitaException e) { throw new LoginFailedException(e.getMessage(), e); } } /** * Overridden in SP */ private LoginDatastore getDatastore() { return new LoginDatastore(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/AccountLockedRuntimeException.java ================================================ /** * Copyright (C) 2026 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.console.common.server.login.filter; /** * Unchecked bridge exception used at the servlet-filter level to propagate account lockout * across filter-chain boundaries (e.g. from {@code AuthenticationRule} to {@code AuthenticationFilter}). *

* Not to be confused with {@link org.bonitasoft.console.common.server.login.AccountLockedException}, * which is the engine-level checked exception (extends {@code LoginFailedException}) thrown by * {@code LoginManager} when brute-force protection triggers a lockout. */ public class AccountLockedRuntimeException extends RuntimeException { private static final long serialVersionUID = 1L; public AccountLockedRuntimeException(final String message) { super(message); } public AccountLockedRuntimeException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/AlreadyLoggedInRule.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.filter; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; import org.bonitasoft.console.common.server.login.servlet.PlatformLoginServlet; import org.bonitasoft.console.common.server.utils.LocaleUtils; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.user.User; public class AlreadyLoggedInRule extends AuthenticationRule { @Override public boolean doAuthorize(final HttpServletRequestAccessor request, HttpServletResponse response) throws ServletException { if (isUserAlreadyLoggedIn(request)) { ensureUserSession(request.asHttpServletRequest(), request.getHttpSession(), request.getApiSession()); return true; } return false; } /** * Overridden is Subscription */ protected boolean isUserAlreadyLoggedIn(final HttpServletRequestAccessor request) throws ServletException { HttpServletRequest httpServletRequest = request.asHttpServletRequest(); if (httpServletRequest.getPathInfo() != null) { String requestPath = httpServletRequest.getServletPath() + httpServletRequest.getPathInfo(); if (requestPath.matches(RestAPIAuthorizationFilter.PLATFORM_API_URI_REGEXP)) { return request.getHttpSession().getAttribute(PlatformLoginServlet.PLATFORM_SESSION_PARAM_KEY) != null; } } return request.getApiSession() != null; } private void ensureUserSession(final HttpServletRequest request, final HttpSession session, final APISession apiSession) { if (apiSession != null && session.getAttribute(SessionUtil.USER_SESSION_PARAM_KEY) == null) { reCreateUser(request, session, apiSession); } } private void reCreateUser(final HttpServletRequest request, final HttpSession session, final APISession apiSession) { final String locale = getLocale(request); final User user = new User(apiSession.getUserName(), locale); session.setAttribute(SessionUtil.USER_SESSION_PARAM_KEY, user); } private String getLocale(final HttpServletRequest request) { return LocaleUtils.getUserLocaleAsString(request); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/AuthenticationFilter.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.filter; import java.io.IOException; import java.util.LinkedList; import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.console.common.server.auth.AuthenticationManager; import org.bonitasoft.console.common.server.auth.AuthenticationManagerFactory; import org.bonitasoft.console.common.server.auth.AuthenticationManagerNotFoundException; import org.bonitasoft.console.common.server.filter.ExcludingPatternFilter; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; import org.bonitasoft.console.common.server.login.utils.LoginUrl; import org.bonitasoft.console.common.server.login.utils.RedirectUrl; import org.bonitasoft.console.common.server.login.utils.RedirectUrlBuilder; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.api.TenantAdministrationAPI; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.mdc.MDCHelper; import org.bonitasoft.engine.mdc.MDCHelper.CheckedCallable4; import org.bonitasoft.engine.mdc.UserIdMDC; import org.bonitasoft.engine.session.Session; import org.bonitasoft.web.server.login.LoginFailureTracker; import org.bonitasoft.web.server.login.LoginFailureTrackerAccessor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; /** * @author Vincent Elcrin * @author Anthony Birembaut */ public class AuthenticationFilter extends ExcludingPatternFilter { protected static final String AUTHENTICATION_FILTER_EXCLUDED_PAGES_PATTERN = "^/(bonita/)?(?:(login\\.jsp$)|(apps/.+/API/)|(portal/resource/.+/API/))"; protected static final String REDIRECT_PARAM = "redirectWhenUnauthorized"; protected static final String USER_NOT_FOUND_JSP = "/usernotfound.jsp"; public static final String ERROR_PAGE_REQUEST_PATH_REGEX = "/portal/resource/app/appDirectoryBonita/error-\\d+/(content|theme)/.*"; protected boolean redirectWhenUnauthorized; private LoginFailureTracker loginFailureTracker; /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationFilter.class.getName()); private final LinkedList rules = new LinkedList<>(); public AuthenticationFilter() { addRules(); } protected void addRules() { addRule(new AlreadyLoggedInRule()); } protected void addRule(final AuthenticationRule rule) { rules.add(rule); } @Override public void init(FilterConfig filterConfig) throws ServletException { String redirectInitParam = filterConfig.getInitParameter(REDIRECT_PARAM); redirectWhenUnauthorized = redirectInitParam != null ? Boolean.parseBoolean(redirectInitParam) : true; super.init(filterConfig); loginFailureTracker = LoginFailureTrackerAccessor .getLoginFailureTracker(filterConfig.getServletContext()); for (AuthenticationRule rule : rules) { rule.setLoginFailureTracker(loginFailureTracker); } } @Override public String getDefaultExcludedPages() { return AUTHENTICATION_FILTER_EXCLUDED_PAGES_PATTERN; } @Override public void proceedWithFiltering(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws ServletException, IOException { final HttpServletRequestAccessor requestAccessor = new HttpServletRequestAccessor((HttpServletRequest) request); doAuthenticationFiltering(requestAccessor, (HttpServletResponse) response, chain); } protected void doAuthenticationFiltering(final HttpServletRequestAccessor requestAccessor, final HttpServletResponse response, final FilterChain chain) throws ServletException, IOException { try { if (!isAuthorized(requestAccessor, response, chain)) { if (!isAccessingPlatformAPIWithAPISession(requestAccessor)) { //do not invalidate the API session when trying to access platform API without a platform session //fix RUNTIME-1562 cleanHttpSession(requestAccessor.getHttpSession()); } if (redirectWhenUnauthorized && requestAccessor.asHttpServletRequest().getMethod().equals("GET")) { response.sendRedirect(createLoginPageUrl(requestAccessor).getLocation()); } else { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); } } } catch (AccountLockedRuntimeException e) { handleAccountLockoutException(response, e); } catch (PlatformUnderMaintenanceException e) { handlePlatformUnderMaintenanceException(requestAccessor, response, e); } catch (final EngineUserNotFoundOrInactive e) { handleUserNotFoundOrInactiveException(requestAccessor, response, e); } catch (BonitaException e) { handleBonitaException(requestAccessor, response, e); } } protected boolean isAccessingPlatformAPIWithAPISession(final HttpServletRequestAccessor requestAccessor) { String requestPath = requestAccessor.asHttpServletRequest().getServletPath() + requestAccessor.asHttpServletRequest().getPathInfo(); return requestPath.matches(RestAPIAuthorizationFilter.PLATFORM_API_URI_REGEXP) && requestAccessor.getApiSession() != null; } /** * @return true if one of the rules pass false otherwise */ protected boolean isAuthorized(final HttpServletRequestAccessor requestAccessor, final HttpServletResponse response, final FilterChain chain) throws ServletException, IOException, PlatformUnderMaintenanceException, BonitaException { for (final AuthenticationRule rule : getRules()) { if (rule.doAuthorize(requestAccessor, response)) { // only when proceeding, log the authenticated user id in context var userId = Optional.ofNullable(requestAccessor).map(HttpServletRequestAccessor::getApiSession) .filter(Objects::nonNull).map(Session::getUserId).filter(id -> id >= 0L); Supplier withAuthenticatedUser = UserIdMDC.supplierFromOptionalId(userId); CheckedCallable4 call = () -> { checkPlatformMaintenanceState(requestAccessor); rule.proceedWithRequest(chain, requestAccessor.asHttpServletRequest(), response); return true; }; return MDCHelper.tryWithMDC(withAuthenticatedUser, call); } } return false; } protected void checkPlatformMaintenanceState(final HttpServletRequestAccessor requestAccessor) throws PlatformUnderMaintenanceException, BonitaException { try { // If redirectWhenUnauthorized is set to false it means we are not trying to access a page // Maintenance state will be Handled at REST API Authorization filter in this case if (redirectWhenUnauthorized && !isLoggedInAsTechnicalUser(requestAccessor) && isPlaformInMaintenance(requestAccessor) && !isAccessingErrorPage(requestAccessor)) { throw new PlatformUnderMaintenanceException("Platform is under Maintenance"); } } catch (BonitaException e) { if (LOGGER.isWarnEnabled()) { LOGGER.warn("Error while checking platform maintenance state : " + e.getMessage(), e); } throw e; } } protected boolean isAccessingErrorPage(HttpServletRequestAccessor requestAccessor) { HttpServletRequest httpRequest = requestAccessor.asHttpServletRequest(); if (httpRequest.getPathInfo() != null) { String requestPath = httpRequest.getServletPath() + httpRequest.getPathInfo(); return requestPath.matches(ERROR_PAGE_REQUEST_PATH_REGEX); } return false; } protected boolean isLoggedInAsTechnicalUser(HttpServletRequestAccessor requestAccessor) { return requestAccessor.getApiSession() != null && requestAccessor.getApiSession().isTechnicalUser(); } protected boolean isPlaformInMaintenance(HttpServletRequestAccessor requestAccessor) throws BonitaException { TenantAdministrationAPI tenantAdministrationAPI = TenantAPIAccessor .getTenantAdministrationAPI(requestAccessor.getApiSession()); return tenantAdministrationAPI.isPaused(); } protected void handleAccountLockoutException(final HttpServletResponse response, final AccountLockedRuntimeException e) { LOGGER.debug("Account locked out: {}", e.getMessage()); response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value()); if (loginFailureTracker != null) { response.setHeader(HttpHeaders.RETRY_AFTER, String.valueOf(loginFailureTracker.getLockoutDurationSeconds())); } } protected void handleUserNotFoundOrInactiveException(final HttpServletRequestAccessor requestAccessor, final HttpServletResponse response, final EngineUserNotFoundOrInactive e) throws ServletException { if (LOGGER.isDebugEnabled()) { LOGGER.debug("redirection to user not found page : " + e.getMessage(), e); } if (redirectWhenUnauthorized && requestAccessor.asHttpServletRequest().getMethod().equals("GET")) { redirectTo(requestAccessor, response, USER_NOT_FOUND_JSP); } else { response.setStatus(HttpServletResponse.SC_FORBIDDEN); } } protected void handlePlatformUnderMaintenanceException(final HttpServletRequestAccessor requestAccessor, final HttpServletResponse response, final PlatformUnderMaintenanceException e) throws IOException { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Redirection to maintenance page : " + e.getMessage(), e); } if (redirectWhenUnauthorized && requestAccessor.asHttpServletRequest().getMethod().equals("GET")) { response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Platform is under maintenance"); } else { response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE); } } protected void handleBonitaException(final HttpServletRequestAccessor requestAccessor, final HttpServletResponse response, final BonitaException e) throws IOException { if (LOGGER.isDebugEnabled()) { LOGGER.debug("redirection to 500 error page : " + e.getMessage(), e); } if (redirectWhenUnauthorized && requestAccessor.asHttpServletRequest().getMethod().equals("GET")) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } else { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } /** * manage redirection to maintenance page * * @param request * @param response * @param pagePath */ protected void redirectTo(final HttpServletRequestAccessor request, final HttpServletResponse response, final String pagePath) throws ServletException { try { response.sendRedirect(request.asHttpServletRequest().getContextPath() + pagePath); } catch (final IOException e) { if (LOGGER.isInfoEnabled()) { LOGGER.info(e.getMessage()); } } } protected LinkedList getRules() { return rules; } @Override public void destroy() { } // protected for test stubbing protected AuthenticationManager getAuthenticationManager() throws ServletException { try { return AuthenticationManagerFactory.getAuthenticationManager(); } catch (final AuthenticationManagerNotFoundException e) { throw new ServletException(e); } } protected RedirectUrl makeRedirectUrl(final HttpServletRequestAccessor httpRequest) { // if the request has a redirection URL, we use it, otherwise we use the requested URI boolean hasRedirectionURL = StringUtils.isNotBlank(httpRequest.getRedirectUrl()); final RedirectUrlBuilder builder = new RedirectUrlBuilder( hasRedirectionURL ? httpRequest.getRedirectUrl() : httpRequest.getRequestedUri()); builder.appendParameters(httpRequest.getParameterMap()); return builder.build(); } protected LoginUrl createLoginPageUrl(final HttpServletRequestAccessor requestAccessor) throws ServletException { return new LoginUrl(getAuthenticationManager(), makeRedirectUrl(requestAccessor).getUrl(), requestAccessor); } protected void cleanHttpSession(final HttpSession session) { SessionUtil.sessionLogout(session); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/AuthenticationRule.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.filter; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; import org.bonitasoft.console.common.server.login.LoginManager; import org.bonitasoft.web.server.login.LoginFailureTracker; /** * Created by Vincent Elcrin * Date: 30/08/13 * Time: 10:28 */ public abstract class AuthenticationRule { private LoginManager loginManager = null; private LoginFailureTracker loginFailureTracker; /* * @return whether the process needs to be aborted or not */ public abstract boolean doAuthorize(HttpServletRequestAccessor request, HttpServletResponse response) throws ServletException; void setLoginFailureTracker(LoginFailureTracker loginFailureTracker) { this.loginFailureTracker = loginFailureTracker; // Reset so that getLoginManager() re-creates it with the new tracker this.loginManager = null; } protected LoginManager getLoginManager() { if (loginManager == null) { loginManager = new LoginManager(loginFailureTracker); } return loginManager; } public void proceedWithRequest(FilterChain chain, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { chain.doFilter(request, response); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/ContentTypeSecurityFilter.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.filter; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.console.common.server.filter.ExcludingPatternFilter; /** * Security filter setting the X-Content-Type-Options in the response headers * * @author Paul AMAR * @author Anthony Birembaut */ public class ContentTypeSecurityFilter extends ExcludingPatternFilter { protected static final String X_CONTENT_TYPE_HEADER = "X-Content-Type-Options"; protected String headerValue; @Override public String getDefaultExcludedPages() { return ""; } @Override public void init(FilterConfig filterConfig) throws ServletException { headerValue = StringUtils.defaultIfEmpty(filterConfig.getInitParameter(X_CONTENT_TYPE_HEADER), "nosniff"); super.init(filterConfig); } @Override public void proceedWithFiltering(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws ServletException, IOException { // casting to HTTPServlet(Request|Response) final HttpServletRequest req = (HttpServletRequest) request; final HttpServletResponse res = (HttpServletResponse) response; // X-Content-Type-Options (Drive-by download attacks) res.setHeader(X_CONTENT_TYPE_HEADER, headerValue); chain.doFilter(req, res); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/EngineUserNotFoundOrInactive.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.filter; public class EngineUserNotFoundOrInactive extends RuntimeException { private static final long serialVersionUID = 4735182063504328575L; public EngineUserNotFoundOrInactive(final String message) { super(message); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/FrameSecurityFilter.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.filter; import java.io.IOException; import java.util.Objects; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.console.common.server.filter.ExcludingPatternFilter; import org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory; import org.bonitasoft.console.common.server.preferences.properties.SecurityProperties; /** * Security filter setting the X-Frame-Options in the response headers * * @author Anthony Birembaut */ public class FrameSecurityFilter extends ExcludingPatternFilter { protected static final String X_FRAME_OPTIONS_HEADER = "X-Frame-Options"; protected static final String X_FRAME_OPTIONS_HEADER_DEFAULT = "SAMEORIGIN"; protected static final String CONTENT_SECURITY_POLICY_HEADER = "Content-Security-Policy"; protected static final String CONTENT_SECURITY_POLICY_HEADER_DEFAULT = "frame-ancestors 'self';"; protected String xFrameHeaderValue; protected String contentSecurityHeaderValue; @Override public String getDefaultExcludedPages() { return ""; } @Override public void init(FilterConfig filterConfig) throws ServletException { SecurityProperties securityProperties = getSecurityProperties(); String xFrameHeaderPropertyValue = securityProperties.getXFrameOptionsHeader(); xFrameHeaderValue = Objects.requireNonNullElse(xFrameHeaderPropertyValue, StringUtils.defaultIfEmpty(filterConfig.getInitParameter(X_FRAME_OPTIONS_HEADER), X_FRAME_OPTIONS_HEADER_DEFAULT)); String contentSecurityHeaderPropertyValue = securityProperties.getContentSecurityPolicyHeader(); contentSecurityHeaderValue = Objects.requireNonNullElse(contentSecurityHeaderPropertyValue, StringUtils.defaultIfEmpty(filterConfig.getInitParameter(CONTENT_SECURITY_POLICY_HEADER), CONTENT_SECURITY_POLICY_HEADER_DEFAULT)); super.init(filterConfig); } protected SecurityProperties getSecurityProperties() { return PropertiesFactory.getSecurityProperties(); } @Override public void proceedWithFiltering(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws ServletException, IOException { // casting to HTTPServlet(Request|Response) final HttpServletRequest req = (HttpServletRequest) request; final HttpServletResponse res = (HttpServletResponse) response; // X-frame-options (ClickJacking) if (!StringUtils.isBlank(xFrameHeaderValue)) { res.setHeader(X_FRAME_OPTIONS_HEADER, xFrameHeaderValue); } if (!StringUtils.isBlank(contentSecurityHeaderValue)) { res.setHeader(CONTENT_SECURITY_POLICY_HEADER, contentSecurityHeaderValue); } chain.doFilter(req, res); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/PlatformUnderMaintenanceException.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.filter; public class PlatformUnderMaintenanceException extends RuntimeException { private static final long serialVersionUID = 4836182063504328577L; public PlatformUnderMaintenanceException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/RestAPIAuthorizationFilter.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.filter; import java.io.IOException; import java.util.Optional; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.io.IOUtils; import org.bonitasoft.console.common.server.filter.ExcludingPatternFilter; import org.bonitasoft.console.common.server.filter.MultiReadHttpServletRequest; import org.bonitasoft.console.common.server.login.servlet.PlatformLoginServlet; import org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.api.permission.APICallContext; import org.bonitasoft.engine.exception.*; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.web.rest.server.framework.utils.ParsedRestRequestURI; import org.bonitasoft.web.rest.server.framework.utils.RestRequestURIParser; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIMalformedUrlException; import org.bonitasoft.web.toolkit.client.common.i18n.model.I18nLocaleDefinition; import org.bonitasoft.web.toolkit.client.common.session.SessionDefinition; import org.bonitasoft.web.toolkit.client.data.APIID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Zhiheng Yang, Chong Zhao * @author Baptiste Mesta * @author Anthony Birembaut */ public class RestAPIAuthorizationFilter extends ExcludingPatternFilter { public static final String PLATFORM_API_URI_REGEXP = "^/(API|APIToolkit)/platform/.*"; protected static final String AUTHORIZATION_FILTER_EXCLUDED_PAGES_PATTERN = "^/(bonita/)?((apps/.+/)|(portal/resource/.+/))?(API|APIToolkit)/system/(i18ntranslation|feature)"; /** * Logger */ protected static final Logger LOGGER = LoggerFactory.getLogger(RestAPIAuthorizationFilter.class.getName()); @Override public void proceedWithFiltering(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) request; try { // body of multi-parts requests is not needed when checking permissions if (!ServletFileUpload.isMultipartContent(httpServletRequest)) { //we need to use a MultiReadHttpServletRequest wrapper in order to be able to get the input stream twice (in the filter and in the API servlet) httpServletRequest = new MultiReadHttpServletRequest(httpServletRequest); } String pathInfo = Optional.ofNullable(httpServletRequest.getPathInfo()).orElse(""); String requestPath = httpServletRequest.getServletPath() + pathInfo; HttpServletResponse httpServletResponse = (HttpServletResponse) response; boolean isAuthorized; if (requestPath.matches(PLATFORM_API_URI_REGEXP)) { isAuthorized = platformAPIsCheck(httpServletRequest, httpServletResponse); } else { isAuthorized = tenantAPIsCheck(httpServletRequest, httpServletResponse); } if (isAuthorized) { chain.doFilter(httpServletRequest, response); } } catch (final InvalidSessionException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Invalid Bonita engine session: {}", e.getMessage()); } SessionUtil.sessionLogout(httpServletRequest.getSession()); ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_UNAUTHORIZED); } catch (final TenantStatusException e) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Platform is probably under Maintenance : {}", e.getMessage()); } ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE); } catch (final Exception e) { if (LOGGER.isErrorEnabled()) { LOGGER.error(e.getMessage(), e); } if (e instanceof APIException) { throw new ServletException(e); } else { //wrap exception in APIException to avoid disclose too much information throw new ServletException(new APIException(e)); } } } protected boolean tenantAPIsCheck(final HttpServletRequest httpRequest, final HttpServletResponse httpResponse) throws ServletException, IOException { final APISession apiSession = (APISession) httpRequest.getSession() .getAttribute(SessionUtil.API_SESSION_PARAM_KEY); try { if (apiSession == null) { httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); httpResponse.flushBuffer(); return false; } else if (!checkPermissions(httpRequest)) { httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); httpResponse.flushBuffer(); return false; } else { return true; } } catch (APIMalformedUrlException e) { LOGGER.info("Malformed API URL: {}", e.getMessage()); httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); httpResponse.flushBuffer(); return false; } catch (InvalidSessionException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Invalid Bonita engine session.", e); } httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); SessionUtil.sessionLogout(httpRequest.getSession()); httpResponse.flushBuffer(); return false; } } protected boolean platformAPIsCheck(final HttpServletRequest httpRequest, final HttpServletResponse httpResponse) { final PlatformSession platformSession = (PlatformSession) httpRequest.getSession() .getAttribute(PlatformLoginServlet.PLATFORM_SESSION_PARAM_KEY); if (platformSession != null) { return true; } else { httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return false; } } protected boolean checkPermissions(final HttpServletRequest request) throws ServletException { final ParsedRestRequestURI parsedURI = new RestRequestURIParser(request).parse(); return checkPermissions(request, parsedURI.getApiName(), parsedURI.getResourceName(), parsedURI.getResourceQualifiers()); } protected boolean checkPermissions(final HttpServletRequest request, final String apiName, final String resourceName, final APIID resourceQualifiers) throws ServletException { final String method = request.getMethod(); final HttpSession session = request.getSession(); // userPermissions are of type: "organization_visualization" // @SuppressWarnings("unchecked") final Set userPermissions = (Set) session.getAttribute(SessionUtil.PERMISSIONS_SESSION_PARAM_KEY); final APISession apiSession = (APISession) session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY); final boolean apiAuthorizationsCheckEnabled = isApiAuthorizationsCheckEnabled(); if (!apiAuthorizationsCheckEnabled || apiSession.isTechnicalUser()) { return true; } final String resourceQualifiersAsString = resourceQualifiers != null ? resourceQualifiers.toString() : null; final String body = getRequestBody(request); final APICallContext apiCallContext = new APICallContext(method, apiName, resourceName, resourceQualifiersAsString, request.getQueryString(), body); if (isAlwaysAuthorizedResource(apiCallContext)) { return true; } try { return enginePermissionsCheck(apiCallContext, apiSession); } catch (BonitaException e) { throw new ServletException(e); } } protected boolean isAlwaysAuthorizedResource(final APICallContext apiCallContext) { return apiCallContext.isGET() && (isSingleResourceCall(apiCallContext, SessionDefinition.TOKEN) || isSingleResourceCall(apiCallContext, I18nLocaleDefinition.TOKEN)); } private boolean isSingleResourceCall(final APICallContext apiCallContext, final String authorizedResourceName) { return authorizedResourceName.equals(apiCallContext.getResourceName()) && "system".equals(apiCallContext.getApiName()); } protected String getRequestBody(final HttpServletRequest request) throws ServletException { try { // body of multi-parts requests is not needed when checking permissions if (ServletFileUpload.isMultipartContent(request)) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("[Multi-Part Request] not adding body to APIContext"); } return null; } final ServletInputStream inputStream = request.getInputStream(); return IOUtils.toString(inputStream, request.getCharacterEncoding()); } catch (final IOException e) { throw new ServletException(e); } } protected boolean isApiAuthorizationsCheckEnabled() { return PropertiesFactory.getSecurityProperties().isAPIAuthorizationsCheckEnabled(); } protected boolean enginePermissionsCheck(final APICallContext apiCallContext, final APISession apiSession) throws ServerAPIException, BonitaHomeNotSetException, UnknownAPITypeException, ExecutionException { return TenantAPIAccessor.getPermissionAPI(apiSession).isAuthorized(apiCallContext); } @Override public String getDefaultExcludedPages() { return AUTHORIZATION_FILTER_EXCLUDED_PAGES_PATTERN; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/TokenGenerator.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.filter; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.bonitasoft.console.common.server.api.token.APIToken; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Julien Reboul */ public class TokenGenerator { protected static final Logger LOGGER = LoggerFactory.getLogger(TokenGenerator.class.getName()); public static final String API_TOKEN = "api_token"; public static final String X_BONITA_API_TOKEN = "X-Bonita-API-Token"; /** * generate and store the CSRF security inside HTTP session * or retrieve it token from the HTTP session * * @param session the HTTP session * @return the CSRF security token */ public String createOrLoadToken(final HttpSession session) { Object apiTokenFromClient = session.getAttribute(API_TOKEN); if (apiTokenFromClient == null) { apiTokenFromClient = new APIToken().getToken(); session.setAttribute(API_TOKEN, apiTokenFromClient); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Bonita API Token generated: " + apiTokenFromClient); } } return apiTokenFromClient.toString(); } /** * set the CSRF security token to the HTTP response as HTTP Header. * * @param res the http response * @param token the security token to set */ public void setTokenToResponseHeader(final HttpServletResponse res, final String token) { if (res.containsHeader(X_BONITA_API_TOKEN)) { res.setHeader(X_BONITA_API_TOKEN, token); } else { res.addHeader(X_BONITA_API_TOKEN, token); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/TokenGeneratorFilter.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.console.common.server.login.PortalCookies; /** * @author Paul AMAR */ public class TokenGeneratorFilter implements Filter { protected TokenGenerator tokenGenerator = new TokenGenerator(); protected PortalCookies portalCookies = new PortalCookies(); @Override public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException { final HttpServletRequest req = (HttpServletRequest) request; final HttpServletResponse res = (HttpServletResponse) response; // Create final String apiTokenFromClient = tokenGenerator.createOrLoadToken(req.getSession()); tokenGenerator.setTokenToResponseHeader(res, apiTokenFromClient); portalCookies.addCSRFTokenCookieToResponse(req, res, apiTokenFromClient); chain.doFilter(req, res); } @Override public void init(final FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/filter/TokenValidatorFilter.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.filter; import static org.apache.commons.lang3.StringUtils.isBlank; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.bonitasoft.console.common.server.filter.ExcludingPatternFilter; import org.bonitasoft.console.common.server.filter.MultiReadHttpServletRequest; import org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.commons.CommonsMultipartResolver; /** * @author Paul AMAR */ public class TokenValidatorFilter extends ExcludingPatternFilter { public static final String BEARER_HEADER_VERIFIED_ATTRIBUTE = "bearerVerified"; private static final String CSRF_TOKEN_PARAM = "CSRFToken"; private static final String CSRF_TOKEN_HEADER = "X-Bonita-API-Token"; protected static final String TOKEN_VALIDATOR_FILTER_EXCLUDED_PAGES_PATTERN = "^/(bonita/)?((apps/.+/)|(portal/resource/.+/))?(API|APIToolkit)/system/(i18ntranslation|feature|session)"; /** * Logger */ protected static final Logger LOGGER = LoggerFactory.getLogger(TokenValidatorFilter.class.getName()); @Override public void proceedWithFiltering(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpServletResponse httpServletResponse = (HttpServletResponse) response; if (isCsrfProtectionEnabled() && !isSafeMethod(httpServletRequest.getMethod()) && !isBearerVerifiedRequest(httpServletRequest.getSession())) { //we need to use a MultiReadHttpServletRequest wrapper in order to be able to get the inputstream twice (in the filter and in the API servlet) MultiReadHttpServletRequest multiReadHttpServletRequest = new MultiReadHttpServletRequest( httpServletRequest); String headerFromRequest = getCSRFToken(multiReadHttpServletRequest); String apiToken = (String) multiReadHttpServletRequest.getSession().getAttribute("api_token"); if (headerFromRequest == null || !headerFromRequest.equals(apiToken)) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Token Validation failed, expected: " + apiToken + ", received: " + headerFromRequest); } httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.flushBuffer(); } else { chain.doFilter(multiReadHttpServletRequest, response); } } else { chain.doFilter(request, response); } } protected boolean isBearerVerifiedRequest(HttpSession httpSession) { boolean isBearerVerified = false; Object bearerSessionAttribute = httpSession.getAttribute(BEARER_HEADER_VERIFIED_ATTRIBUTE); if (bearerSessionAttribute != null) { isBearerVerified = Boolean.parseBoolean((String) bearerSessionAttribute); httpSession.removeAttribute(BEARER_HEADER_VERIFIED_ATTRIBUTE); } return isBearerVerified; } // protected for testing protected boolean isCsrfProtectionEnabled() { return PropertiesFactory.getSecurityProperties().isCSRFProtectionEnabled(); } /** * Get CSRF token from request following order as below * - In 'X-Bonita-API-Token' header * - In 'CSRFToken' parameter * - In 'CSRFToken' multipart body parameter */ private String getCSRFToken(HttpServletRequest httpRequest) { String token = httpRequest.getHeader(CSRF_TOKEN_HEADER); if (isBlank(token)) { token = httpRequest.getParameter(CSRF_TOKEN_PARAM); } if (isBlank(token) && isFormData(httpRequest.getContentType())) { CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); multipartResolver.setResolveLazily(true); MultipartHttpServletRequest multiPartRequest = multipartResolver.resolveMultipart(httpRequest); if (multiPartRequest.getParameterMap().containsKey(CSRF_TOKEN_PARAM)) { token = multiPartRequest.getParameter(CSRF_TOKEN_PARAM); } } return token; } private boolean isFormData(String contentType) { return contentType != null && contentType.toLowerCase().contains("multipart/form-data"); } private boolean isSafeMethod(String method) { return "GET".equalsIgnoreCase(method) || "HEAD".equalsIgnoreCase(method) || "OPTIONS".equalsIgnoreCase(method); } @Override public String getDefaultExcludedPages() { return TOKEN_VALIDATOR_FILTER_EXCLUDED_PAGES_PATTERN; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/servlet/LoginServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.servlet; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.console.common.server.auth.AuthenticationFailedException; import org.bonitasoft.console.common.server.auth.AuthenticationManager; import org.bonitasoft.console.common.server.auth.AuthenticationManagerFactory; import org.bonitasoft.console.common.server.auth.AuthenticationManagerNotFoundException; import org.bonitasoft.console.common.server.filter.PathSanitizer; import org.bonitasoft.console.common.server.login.AccountLockedException; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; import org.bonitasoft.console.common.server.login.LoginFailedException; import org.bonitasoft.console.common.server.login.LoginManager; import org.bonitasoft.console.common.server.login.utils.RedirectUrlBuilder; import org.bonitasoft.console.common.server.login.utils.RedirectUrlHandler; import org.bonitasoft.console.common.server.utils.LocaleUtils; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.console.common.server.utils.TenantsManagementUtils; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.server.login.LoginFailureTracker; import org.bonitasoft.web.server.login.LoginFailureTrackerAccessor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; /** * @author Anthony Birembaut, Ruiheng Fan, Chong Zhao, Haojie Yuan */ public class LoginServlet extends HttpServlet { /** * serialVersionUID */ private static final long serialVersionUID = -5326931127638029215L; /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(LoginServlet.class.getName()); /** * login fail message */ protected static final String LOGIN_FAIL_MESSAGE = "loginFailMessage"; /** * account locked message — must match the string literal in login.jsp (line 58) */ protected static final String ACCOUNT_LOCKED_MESSAGE = "accountLockedMessage"; /* * System property to allow login with GET from the development suite */ public static final String ENABLE_DEV_SUITE_LOGIN = "org.bonitasoft.web.login.get.enabled"; private LoginFailureTracker loginFailureTracker; @Override public void init() throws ServletException { super.init(); loginFailureTracker = LoginFailureTrackerAccessor.getLoginFailureTracker(getServletContext()); } /** * Necessary studio integration (username and password are passed in the URL in development mode) * * @deprecated * use {@link #doPost(HttpServletRequest, HttpServletResponse)} instead */ @Override @Deprecated(since = "8.0", forRemoval = false) protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { if (!Boolean.parseBoolean(System.getProperty(ENABLE_DEV_SUITE_LOGIN))) { resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); return; } if (LOGGER.isTraceEnabled()) { LOGGER.trace("query string : " + dropPassword(req.getQueryString())); } doPost(req, resp); } @Override protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException { // force post request body to UTF-8 try { request.setCharacterEncoding(StandardCharsets.UTF_8.name()); } catch (final UnsupportedEncodingException e) { // should never appear throw new ServletException(e); } if (request.getContentType() != null && !MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith( MediaType.parseMediaType(request.getContentType()))) { if (LOGGER.isDebugEnabled()) { LOGGER.debug( "The only content type supported by this service is application/x-www-form-urlencoded. The content-type request header needs to be set accordingly."); } response.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); } else { handleLogin(request, response); } } protected void handleLogin(final HttpServletRequest request, final HttpServletResponse response) throws ServletException { final boolean redirectAfterLogin = RedirectUrlHandler.shouldRedirectAfterLogin(request); final String redirectURL = getRedirectUrl(request, redirectAfterLogin); String locale = LocaleUtils.getUserLocaleAsString(request); try { doLogin(request, response); final APISession apiSession = (APISession) request.getSession() .getAttribute(SessionUtil.API_SESSION_PARAM_KEY); // if there is no redirect=true or redirectURL parameter in the request do nothing (API login), otherwise, redirect (Portal login) if (redirectAfterLogin) { if (apiSession.isTechnicalUser() || hasProfile(apiSession)) { response.sendRedirect(createRedirectUrl(redirectURL, locale)); } else { String loginURL = getAuthenticationManager() .getLoginPageURL(new HttpServletRequestAccessor(request), redirectURL); if (loginURL.startsWith(request.getContextPath() + AuthenticationManager.LOGIN_PAGE)) { request.setAttribute(LOGIN_FAIL_MESSAGE, "noProfileForUser"); getServletContext().getRequestDispatcher(AuthenticationManager.LOGIN_PAGE).forward(request, response); } else { // if the login page is not the default jsp but an external URL the forward cannot work. // just respond with a 401 if (LOGGER.isDebugEnabled()) { LOGGER.debug("No profiles for user"); } response.setStatus(HttpServletResponse.SC_UNAUTHORIZED, "noProfileForUser"); } } } else { LocaleUtils.addOrReplaceLocaleCookieResponse(response, locale); response.setStatus(HttpServletResponse.SC_NO_CONTENT); } } catch (final AuthenticationManagerNotFoundException e) { final String message = "Can't get login manager"; if (LOGGER.isErrorEnabled()) { LOGGER.error(message, e); } throw new ServletException(e); } catch (final LoginFailedException e) { handleException(request, response, redirectAfterLogin, e, locale); } catch (final AuthenticationFailedException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Authentication failed : " + e.getMessage(), e); } handleException(request, response, redirectAfterLogin, e, locale); } catch (final Exception e) { LOGGER.error("Error while trying to log in", e); throw new ServletException(e); } } protected boolean hasProfile(final APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantsManagementUtils.hasProfileForUser(apiSession); } private void handleException(final HttpServletRequest request, final HttpServletResponse response, final boolean redirectAfterLogin, final Exception exception, final String locale) throws ServletException { if (redirectAfterLogin) { try { if (exception instanceof AccountLockedException) { request.setAttribute(LOGIN_FAIL_MESSAGE, ACCOUNT_LOCKED_MESSAGE); } else { request.setAttribute(LOGIN_FAIL_MESSAGE, LOGIN_FAIL_MESSAGE); } String loginURL = request.getParameter(AuthenticationManager.LOGIN_URL_PARAM_NAME); if (loginURL == null) { loginURL = createDefaultRedirectUrl(request, redirectAfterLogin); } else { // Defense-in-depth: sanitize user-supplied loginURL before passing // to getRequestDispatcher(). Strip path parameters (semicolons) to // prevent ..;-based traversal, then normalize to collapse any ../ String sanitizedLoginURL; try { sanitizedLoginURL = PathSanitizer.stripPathParameters(loginURL); String normalizedPath = new URI(sanitizedLoginURL).normalize().getPath(); sanitizedLoginURL = normalizedPath != null ? normalizedPath : AuthenticationManager.LOGIN_PAGE; loginURL = createRedirectUrl(sanitizedLoginURL, locale); } catch (URISyntaxException uriEx) { LOGGER.warn("Invalid loginURL [{}], falling back to default login page: {}", loginURL, uriEx.getMessage()); loginURL = createDefaultRedirectUrl(request, redirectAfterLogin); } } if (loginURL.startsWith(request.getContextPath() + AuthenticationManager.LOGIN_PAGE)) { getServletContext().getRequestDispatcher(AuthenticationManager.LOGIN_PAGE).forward(request, response); } else { // if the login page is not the default jsp but an external URL the forward cannot work. // just respond with a 401 if (LOGGER.isDebugEnabled()) { LOGGER.debug(exception.getMessage()); } response.setStatus(HttpServletResponse.SC_UNAUTHORIZED, LOGIN_FAIL_MESSAGE); } } catch (final Exception e) { LOGGER.error(e.getMessage()); throw new ServletException(e); } } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug(exception.getMessage()); } if (exception instanceof AccountLockedException) { response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value()); if (loginFailureTracker != null) { response.setHeader(HttpHeaders.RETRY_AFTER, String.valueOf(loginFailureTracker.getLockoutDurationSeconds())); } } else { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); } } } private String getRedirectUrl(final HttpServletRequest request, final boolean redirectAfterLogin) { String redirectURL = request.getParameter(AuthenticationManager.REDIRECT_URL); if (LOGGER.isDebugEnabled()) { LOGGER.debug("redirecting to : " + redirectURL); } if (redirectAfterLogin && (redirectURL == null || redirectURL.isEmpty())) { redirectURL = AuthenticationManager.DEFAULT_DIRECT_URL; } else { if (redirectURL != null) { redirectURL = new URLProtector().protectRedirectUrl(redirectURL); } } return redirectURL; } private String createRedirectUrl(final String redirectURL, final String locale) { RedirectUrlBuilder redirectUrlBuilder = new RedirectUrlBuilder(redirectURL); redirectUrlBuilder.appendParameter(LocaleUtils.PORTAL_LOCALE_PARAM, locale); return redirectUrlBuilder.build().getUrl(); } private String createDefaultRedirectUrl(HttpServletRequest request, boolean redirectAfterLogin) throws ServletException, AuthenticationManagerNotFoundException { final String redirectURL = getRedirectUrl(request, redirectAfterLogin); return getAuthenticationManager() .getLoginPageURL(new HttpServletRequestAccessor(request), redirectURL); } protected void doLogin(final HttpServletRequest request, final HttpServletResponse response) throws AuthenticationManagerNotFoundException, LoginFailedException, ServletException, AuthenticationFailedException { final LoginManager loginManager = getLoginManager(); loginManager.login(request, response); } protected AuthenticationManager getAuthenticationManager() throws AuthenticationManagerNotFoundException { return AuthenticationManagerFactory.getAuthenticationManager(); } protected LoginManager getLoginManager() { return new LoginManager(loginFailureTracker); } static String dropPassword(final String content) { String tmp = content; if (content != null && content.contains("password")) { tmp = tmp.replaceAll("[&]?password=([^&|#]*)?", ""); } return tmp; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/servlet/LogoutServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.bonitasoft.console.common.server.auth.AuthenticationManager; import org.bonitasoft.console.common.server.auth.AuthenticationManagerFactory; import org.bonitasoft.console.common.server.auth.AuthenticationManagerNotFoundException; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; import org.bonitasoft.console.common.server.login.LoginFailedException; import org.bonitasoft.console.common.server.login.credentials.UserLogger; import org.bonitasoft.console.common.server.login.utils.LoginUrl; import org.bonitasoft.console.common.server.login.utils.RedirectUrlBuilder; import org.bonitasoft.console.common.server.login.utils.RedirectUrlHandler; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.session.APISession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Servlet used to logout from the applications * * @author Zhiheng Yang, Chong Zhao */ public class LogoutServlet extends HttpServlet { /** * UID */ private static final long serialVersionUID = 739607235407639011L; private static final Logger LOGGER = LoggerFactory.getLogger(LogoutServlet.class.getName()); /** * {@inheritDoc} */ @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { logout(request, response); } /** * {@inheritDoc} */ @Override protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { logout(request, response); } /** * Console logout */ protected void logout(final HttpServletRequest request, final HttpServletResponse response) throws ServletException { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Logging out from Bonita"); } final HttpServletRequestAccessor requestAccessor = new HttpServletRequestAccessor(request); final HttpSession session = requestAccessor.getHttpSession(); final APISession apiSession = requestAccessor.getApiSession(); try { engineLogout(apiSession); SessionUtil.sessionLogout(session); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Logged out from Bonita"); } if (RedirectUrlHandler.shouldRedirectAfterLogout(request)) { final String loginPage = getURLToRedirectTo(requestAccessor); response.sendRedirect(loginPage); } } catch (final Exception e) { if (LOGGER.isErrorEnabled()) { LOGGER.error("Error while performing the logout", e); } throw new ServletException(e); } } // protected for test stubbing protected AuthenticationManager getAuthenticationManager() throws ServletException { try { return AuthenticationManagerFactory.getAuthenticationManager(); } catch (final AuthenticationManagerNotFoundException e) { throw new ServletException(e); } } protected String getURLToRedirectTo(final HttpServletRequestAccessor requestAccessor) throws ServletException { final AuthenticationManager authenticationManager = getAuthenticationManager(); final HttpServletRequest request = requestAccessor.asHttpServletRequest(); final String redirectURL = createRedirectUrl(requestAccessor); final String logoutPage = authenticationManager.getLogoutPageURL(requestAccessor, redirectURL); String redirectionPage; if (logoutPage != null) { redirectionPage = logoutPage; } else { final String loginPageURLFromRequest = request.getParameter(AuthenticationManager.LOGIN_URL_PARAM_NAME); if (loginPageURLFromRequest != null) { redirectionPage = sanitizeLoginPageUrl(loginPageURLFromRequest); } else { LoginUrl loginPageURL = new LoginUrl(authenticationManager, redirectURL, requestAccessor); redirectionPage = loginPageURL.getLocation(); } } return redirectionPage; } protected String createRedirectUrl(final HttpServletRequestAccessor requestAccessor) throws ServletException { return RedirectUrlHandler.retrieveRedirectUrl(requestAccessor); } protected String sanitizeLoginPageUrl(final String loginURL) { return new RedirectUrlBuilder(new URLProtector().protectRedirectUrl(loginURL)).build().getUrl(); } protected void engineLogout(final APISession apiSession) throws LoginFailedException { if (apiSession != null) { getUserLogger().doLogout(apiSession); } } protected UserLogger getUserLogger() { return new UserLogger(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/servlet/PlatformLoginServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.console.common.server.auth.AuthenticationManager; import org.bonitasoft.console.common.server.login.PortalCookies; import org.bonitasoft.console.common.server.login.filter.TokenGenerator; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.api.PlatformLoginAPI; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.platform.InvalidPlatformCredentialsException; import org.bonitasoft.engine.session.PlatformSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Ruiheng Fan, Haojie Yuan */ public class PlatformLoginServlet extends HttpServlet { /** * engine PlatformSession atribute name in HTTP session */ public static final String PLATFORM_SESSION_PARAM_KEY = "platformSession"; /** * the request param for the username */ protected static final String USERNAME_PARAM = "username"; /** * the request param for the password */ protected static final String PASSWORD_PARAM = "password"; /** * login fail message */ protected static final String LOGIN_FAIL_MESSAGE = "loginFailMessage"; /** * serialVersionUID */ private static final long serialVersionUID = -5326931127638029215L; /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(PlatformLoginServlet.class.getName()); /** * the URL of the login page */ // FIXME: page does not exist: protected static final String LOGIN_PAGE = "/platformLogin.jsp"; // FIXME: page does not exist: protected static final String PLATFORM_PAGE = "platform/BonitaPlatform.html#?_p=Platform"; public static final String ERROR_MESSAGE = "Error while logging in to the platform"; protected final TokenGenerator tokenGenerator = new TokenGenerator(); protected final PortalCookies portalCookies = new PortalCookies(); @Override protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { PlatformSession platformSession; PlatformLoginAPI platformLoginAPI; final String username = request.getParameter(USERNAME_PARAM); final String password = request.getParameter(PASSWORD_PARAM); String redirectStr = request.getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME); boolean redirectAfterLogin = Boolean.parseBoolean(redirectStr); try { platformLoginAPI = getPlatformLoginAPI(); platformSession = platformLoginAPI.login(username, password); request.getSession().setAttribute(PLATFORM_SESSION_PARAM_KEY, platformSession); String csrfToken = tokenGenerator.createOrLoadToken(request.getSession()); portalCookies.addCSRFTokenCookieToResponse(request, response, csrfToken); if (redirectAfterLogin) { response.sendRedirect(PLATFORM_PAGE); } } catch (final InvalidPlatformCredentialsException e) { LOGGER.trace("Wrong username or password", e); if (redirectAfterLogin) { response.sendError(HttpServletResponse.SC_FORBIDDEN, "Wrong username or password"); } else { response.setStatus(HttpServletResponse.SC_FORBIDDEN); } } catch (final Exception e) { LOGGER.error(ERROR_MESSAGE, e); if (redirectAfterLogin) { try { request.setAttribute(LOGIN_FAIL_MESSAGE, LOGIN_FAIL_MESSAGE); getServletContext().getRequestDispatcher(LOGIN_PAGE).forward(request, response); } catch (final IOException ioe) { LOGGER.error("Error while redirecting to login.jsp", ioe); throw new ServletException(ERROR_MESSAGE, e); } } else { throw new ServletException(ERROR_MESSAGE, e); } } } PlatformLoginAPI getPlatformLoginAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return PlatformAPIAccessor.getPlatformLoginAPI(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/servlet/PlatformLogoutServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.bonitasoft.console.common.server.auth.AuthenticationManager; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.api.PlatformLoginAPI; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.PlatformSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Servlet used to logout from the applications * * @author Ruiheng Fan */ public class PlatformLogoutServlet extends HttpServlet { /** * UID */ private static final long serialVersionUID = 739607235407639011L; /** * the URL of the login page */ // FIXME: page does not exist: protected final String PLATFORM_LOGIN_PAGE = "platformLogin.jsp"; /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(PlatformLogoutServlet.class.getName()); /** * {@inheritDoc} */ @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { logout(request, response); } /** * {@inheritDoc} */ @Override protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { logout(request, response); } /** * Console logout */ protected void logout(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { final HttpSession session = request.getSession(); final PlatformSession platformSession = (PlatformSession) session .getAttribute(PlatformLoginServlet.PLATFORM_SESSION_PARAM_KEY); if (platformSession != null) { try { final PlatformLoginAPI platformLoginAPI = PlatformAPIAccessor.getPlatformLoginAPI(); platformLoginAPI.logout(platformSession); } catch (final BonitaException e) { if (LOGGER.isErrorEnabled()) { LOGGER.error("Error while performing the logout", e); } } } session.removeAttribute(PlatformLoginServlet.PLATFORM_SESSION_PARAM_KEY); session.invalidate(); String redirectStr = request.getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME); if (Boolean.parseBoolean(redirectStr)) { response.sendRedirect(PLATFORM_LOGIN_PAGE); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/servlet/URLProtector.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.servlet; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * @author Paul AMAR */ public class URLProtector { protected final List tokens = Arrays.asList("https", "http", "www", "HTTPS", "HTTP", "WWW", "//"); public String protectRedirectUrl(String redirectUrl) { if (redirectUrl != null && !redirectUrl.startsWith("portal")) { return removeTokenFromUrl(redirectUrl, new ArrayList<>(tokens)); } return redirectUrl; } private String removeTokenFromUrl(String redirectUrl, List tokens) { if (tokens.size() > 0) { return removeTokenFromUrl(redirectUrl.replaceAll(tokens.remove(0), ""), tokens); } return redirectUrl; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/utils/LoginUrl.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.utils; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import javax.servlet.ServletException; import org.bonitasoft.console.common.server.auth.AuthenticationManager; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; /** * @author Vincent Elcrin */ public class LoginUrl { private final String location; /** * @throws ServletException */ public LoginUrl(final AuthenticationManager authenticationManager, final String redirectUrl, final HttpServletRequestAccessor request) throws ServletException { location = getLoginPageUrl(authenticationManager, redirectUrl, request); } public String getLocation() { return location; } private String getLoginPageUrl(final AuthenticationManager authenticationManager, final String redirectURL, final HttpServletRequestAccessor request) throws ServletException { return authenticationManager.getLoginPageURL(request, URLEncoder.encode(redirectURL, StandardCharsets.UTF_8)); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/utils/RedirectUrl.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.utils; /** * @author Vincent Elcrin */ public class RedirectUrl { private final String url; protected RedirectUrl(String url) { this.url = url; } public String getUrl() { return url; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/utils/RedirectUrlBuilder.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.utils; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.console.common.server.auth.AuthenticationManager; import org.bonitasoft.console.common.server.utils.UrlBuilder; /** * @author Vincent Elcrin */ public class RedirectUrlBuilder { private final UrlBuilder urlBuilder; private final List blackList = List.of( AuthenticationManager.REDIRECT_URL); public RedirectUrlBuilder(final String redirectUrl) { urlBuilder = new UrlBuilder(redirectUrl != null ? redirectUrl : ""); } public RedirectUrl build() { return new RedirectUrl(urlBuilder.build()); } public void appendParameters(final Map parameters) { for (final Entry next : parameters.entrySet()) { appendParameter(next.getKey(), next.getValue()); } } public void appendParameter(String name, String... values) { if (!isBlackListed(name)) { urlBuilder.appendParameter(name, values); } } public void removeParameter(String name) { urlBuilder.removeParameter(name); } private boolean isBlackListed(final String key) { return blackList.contains(key); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/login/utils/RedirectUrlHandler.java ================================================ /** * Copyright (C) 2022 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.console.common.server.login.utils; import javax.servlet.http.HttpServletRequest; import org.bonitasoft.console.common.server.auth.AuthenticationManager; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; public class RedirectUrlHandler { public static boolean shouldRedirectAfterLogin(final HttpServletRequest request) { final String redirectAfterLogin = request.getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME); final String redirectURL = request.getParameter(AuthenticationManager.REDIRECT_URL); // If there is a redirect param in the request use it otherwise check if there is a redirect URL return redirectAfterLogin != null ? Boolean.parseBoolean(redirectAfterLogin) : redirectURL != null; } public static boolean shouldRedirectAfterLogout(final HttpServletRequest request) { final String redirectAfterLogin = request.getParameter(AuthenticationManager.REDIRECT_AFTER_LOGIN_PARAM_NAME); final String redirectURL = request.getParameter(AuthenticationManager.REDIRECT_URL); final String loginPageURL = request.getParameter(AuthenticationManager.LOGIN_URL_PARAM_NAME); // If there is a redirect param in the request use it otherwise check if there is a redirect or login URL return redirectAfterLogin != null ? Boolean.parseBoolean(redirectAfterLogin) : (redirectURL != null || loginPageURL != null); } public static String retrieveRedirectUrl(final HttpServletRequestAccessor request, String... parametersToRemove) { final String redirectUrlFromRequest = request.getRedirectUrl(); String redirectUrl = redirectUrlFromRequest != null ? redirectUrlFromRequest : getDefaultRedirectUrl(); RedirectUrlBuilder redirectUrlBuilder = new RedirectUrlBuilder(redirectUrl); for (String parameterToRemove : parametersToRemove) { redirectUrlBuilder.removeParameter(parameterToRemove); } return redirectUrlBuilder.build().getUrl(); } protected static String getDefaultRedirectUrl() { return AuthenticationManager.DEFAULT_DIRECT_URL; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/ApplicationAuthorizationsHelper.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.livingapps.ApplicationModel; import org.bonitasoft.livingapps.ApplicationModelFactory; public class ApplicationAuthorizationsHelper { private final APISession apiSession; private final ApplicationModelFactory applicationFactory; public ApplicationAuthorizationsHelper(final APISession apiSession, final ApplicationModelFactory applicationModelFactory) { this.apiSession = apiSession; this.applicationFactory = applicationModelFactory; } public boolean isAuthorized(final String applicationToken) { try { final ApplicationModel application = applicationFactory.createApplicationModel(applicationToken); return application.authorize(apiSession); } catch (final Exception e) { return false; } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/BDMClientDependenciesResolver.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.io.FileUtils; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.api.TenantAdministrationAPI; import org.bonitasoft.engine.business.data.BusinessDataRepositoryException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.session.APISession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BDMClientDependenciesResolver { private final APISession session; private static final Logger LOGGER = LoggerFactory.getLogger(BDMClientDependenciesResolver.class.getName()); private final Set dependenciesNames = new HashSet<>(); public BDMClientDependenciesResolver(APISession session) { this.session = session; } public URL[] getBDMDependencies() throws IOException { if (!isBDMDeployed()) { return new URL[0]; } final File currentBDMFolder = getCurrentBDMFolder(); if (shouldUpdateBDMDependencies(currentBDMFolder)) { cleanBDMFolder(currentBDMFolder); createBDMClientFolder(currentBDMFolder); } return getBDMLibrariesURLs(currentBDMFolder); } private boolean isBDMDeployed() { return getBusinessDataModelVersion() != null; } private boolean shouldUpdateBDMDependencies(File currentBDMFolder) { return !currentBDMFolder.exists() || currentBDMFolder.listFiles().length == 0; } private File getCurrentBDMFolder() { File bdmWorkDir = null; final String businessDataModelVersion = getBusinessDataModelVersion(); if (businessDataModelVersion != null) { bdmWorkDir = new File(WebBonitaConstantsUtils.getTenantInstance().geBDMWorkFolder(), businessDataModelVersion); } return bdmWorkDir; } private void cleanBDMFolder(final File currentBDMFolder) { final File parentFile = currentBDMFolder.getParentFile(); if (parentFile != null && parentFile.exists()) { for (final File previousDeployedBDM : parentFile.listFiles()) { if (previousDeployedBDM.isDirectory()) { try { FileUtils.deleteDirectory(previousDeployedBDM); } catch (final IOException e) { if (LOGGER.isWarnEnabled()) { LOGGER.warn("Unable to delete obsolete BDM libraries", e); } } } } } } private void createBDMClientFolder(final File bdmWorkDir) { if (!bdmWorkDir.exists()) { bdmWorkDir.mkdirs(); } ByteArrayInputStream inpuStream = null; try { final TenantAdministrationAPI tenantAdministrationAPI = getTenantAdminstrationAPI(); inpuStream = new ByteArrayInputStream(tenantAdministrationAPI.getClientBDMZip()); IOUtil.unzipToFolder(inpuStream, bdmWorkDir); } catch (final BonitaHomeNotSetException | IOException | BusinessDataRepositoryException | ServerAPIException | UnknownAPITypeException e) { final String message = "Unable to create the class loader for the BDM libraries"; if (LOGGER.isErrorEnabled()) { LOGGER.error(message, e); } } } protected TenantAdministrationAPI getTenantAdminstrationAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getTenantAdministrationAPI(session); } public String getBusinessDataModelVersion() { try { final TenantAdministrationAPI tenantAdministrationAPI = getTenantAdminstrationAPI(); return tenantAdministrationAPI.getBusinessDataModelVersion(); } catch (final Exception e) { LOGGER.error("Unable to retrieve business data model version", e); return null; } } private URL[] getBDMLibrariesURLs(final File bdmFolder) throws IOException { final List urls = new ArrayList<>(); for (final File file : bdmFolder.listFiles()) { if (file.getName().endsWith(".jar")) { dependenciesNames.add(file.getName()); urls.add(file.toURI().toURL()); } } return urls.toArray(new URL[urls.size()]); } public boolean isABDMDependency(String resourceName) { return dependenciesNames.contains(resourceName); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/CustomPageChildFirstClassLoader.java ================================================ /** * Copyright (C) 2011-2015 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.console.common.server.page; import static org.apache.commons.io.FileUtils.deleteQuietly; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.Objects; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Elias Ricken de Medeiros * @author Charles Souillard * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Romain Bioteau * A Classloader adding given custom page resources and bdm resources in its classpath. * This classloader is versioned with a runtime bdm version and should be discard when bdm is updated */ public class CustomPageChildFirstClassLoader extends MonoParentJarFileClassLoader implements VersionedClassloader { protected final Map nonJarResources = new HashMap<>(); private boolean isActive = true; private static final Logger LOGGER = LoggerFactory.getLogger(CustomPageChildFirstClassLoader.class.getName()); private final CustomPageDependenciesResolver customPageDependenciesResolver; private final BDMClientDependenciesResolver bdmDependenciesResolver; private final String version; CustomPageChildFirstClassLoader(String pageName, CustomPageDependenciesResolver customPageDependenciesResolver, BDMClientDependenciesResolver bdmDependenciesResolver, ClassLoader parent) { super(pageName, new URL[] {}, parent); this.customPageDependenciesResolver = customPageDependenciesResolver; this.bdmDependenciesResolver = bdmDependenciesResolver; this.version = bdmDependenciesResolver.getBusinessDataModelVersion(); } public void addCustomPageResources() throws IOException { addBDMDependencies(); addOtherDependencies(); } private void addBDMDependencies() { try { addURLs(bdmDependenciesResolver.getBDMDependencies()); } catch (final IOException e) { LOGGER.error("Failed to add BDM dependencies in ClassLoader", e); } } private void addOtherDependencies() throws IOException { final Map customPageDependencies = customPageDependenciesResolver .resolveCustomPageDependencies(); for (final Map.Entry resource : customPageDependencies.entrySet()) { if (resource.getKey().matches(".*\\.jar") && !bdmDependenciesResolver.isABDMDependency(resource.getKey())) { final byte[] data = resource.getValue(); final File file = File.createTempFile(resource.getKey(), null, customPageDependenciesResolver.getTempFolder()); file.deleteOnExit(); FileUtils.writeByteArrayToFile(file, data); addURL(new File(file.getAbsolutePath()).toURI().toURL()); } else { nonJarResources.put(resource.getKey(), resource.getValue()); } } } @Override public InputStream getResourceAsStream(final String name) { /* * if (!isActive) { * throw new RuntimeException(this.toString() + " is not active anymore. Don't use it."); * } */ InputStream is = getResourceAsStreamInternal(name); if (is == null && name.length() > 0 && name.charAt(0) == '/') { is = getResourceAsStreamInternal(name.substring(1)); } return is; } protected InputStream getResourceAsStreamInternal(final String name) { final byte[] classData = loadProcessResource(name); if (classData != null) { return new ByteArrayInputStream(classData); } return getResourceAsStreamRegular(name); } protected InputStream getResourceAsStreamRegular(final String name) { return super.getResourceAsStream(name); } private byte[] loadProcessResource(final String resourceName) { return nonJarResources.containsKey(resourceName) ? nonJarResources.get(resourceName) : null; } @Override protected Class loadClass(final String name, final boolean resolve) throws ClassNotFoundException { /* * if (!isActive) { * throw new RuntimeException(this.toString() + " is not active anymore. Don't use it."); * } */ Class c = null; c = findLoadedClass(name); if (c == null) { try { c = findClass(name); } catch (final ClassNotFoundException e) { // ignore } catch (final LinkageError le) { // might be because of a duplicate loading (concurrency loading), retry to find it one time See BS-2483 c = findLoadedClass(name); if (c == null) { // was not because of duplicate loading: throw the exception throw le; } } } if (c == null) { c = getParent().loadClass(name); } if (resolve) { resolveClass(c); } return c; } // never called. Is it normal? public void release() { deleteQuietly(customPageDependenciesResolver.getTempFolder()); isActive = false; } @Override public String toString() { return super.toString() + ", name=" + getName() + ", isActive: " + isActive + ", parent= " + getParent(); } @Override public String getVersion() { return version; } @Override public boolean hasVersion(String version) { return Objects.equals(getVersion(), version); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/CustomPageDependenciesResolver.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; import static org.apache.commons.io.FileUtils.readFileToByteArray; import java.io.File; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.commons.io.FileUtils; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CustomPageDependenciesResolver { private static final String LIB_FOLDER_NAME = "lib"; static final Map PAGES_LIB_TMPDIR = new HashMap<>(); private static final Logger LOGGER = LoggerFactory.getLogger(CustomPageDependenciesResolver.class.getName()); private File libTempFolder; private final WebBonitaConstantsUtils webBonitaConstantsUtils; private final File pageDirectory; private final String pageName; public CustomPageDependenciesResolver(final String pageName, final File pageDirectory, final WebBonitaConstantsUtils webBonitaConstantsUtils) { this.pageName = pageName; this.pageDirectory = pageDirectory; this.webBonitaConstantsUtils = webBonitaConstantsUtils; } public Map resolveCustomPageDependencies() { final File customPageLibDirectory = new File(pageDirectory, LIB_FOLDER_NAME); if (customPageLibDirectory.exists()) { this.libTempFolder = new File(this.webBonitaConstantsUtils.getTempFolder(), pageName + new Date().getTime()); if (!this.libTempFolder.exists()) { this.libTempFolder.mkdirs(); } removePageLibTempFolder(pageName); PAGES_LIB_TMPDIR.put(pageName, this.libTempFolder); return loadLibraries(customPageLibDirectory); } return Collections.emptyMap(); } private Map loadLibraries(final File customPageLibDirectory) { final Map result = new HashMap<>(); try { Files.walkFileTree(customPageLibDirectory.toPath(), new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { final File currentFile = file.toFile(); result.put(currentFile.getName(), readFileToByteArray(currentFile)); return super.visitFile(file, attrs); } }); } catch (final IOException e) { LOGGER.error(e.getMessage(), e); } return result; } public static File removePageLibTempFolder(final String pageName) { final File libTempFolder = PAGES_LIB_TMPDIR.remove(pageName); if (libTempFolder != null) { try { FileUtils.deleteDirectory(libTempFolder); } catch (final IOException e) { if (LOGGER.isWarnEnabled()) { LOGGER.warn("The custom page temporary lib directory " + libTempFolder.getPath() + " cannot be deleted. This is likely to be due to a JDK bug on Windows. You can safely delete it after a server restart."); } } } return libTempFolder; } public File getTempFolder() { if (libTempFolder == null) { throw new IllegalStateException("Custom page dependencies must be resolved first."); } return libTempFolder; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/CustomPageRequestModifier.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.console.common.server.filter.PathSanitizer; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.util.UriUtils; /** * @author Julien Mege */ public class CustomPageRequestModifier { /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(CustomPageRequestModifier.class.getName()); public void redirectToValidPageUrl(final HttpServletRequest request, final HttpServletResponse response) throws IOException { final StringBuilder taskURLBuilder = new StringBuilder(request.getContextPath()); taskURLBuilder.append(request.getServletPath()); if (request.getPathInfo() != null) { taskURLBuilder.append(request.getPathInfo()); } taskURLBuilder.append("/"); if (!StringUtil.isBlank(request.getQueryString())) { taskURLBuilder.append("?").append(request.getQueryString()); } response.sendRedirect(response.encodeRedirectURL(taskURLBuilder.toString())); } public void forwardIfRequestIsAuthorized(final HttpServletRequest request, final HttpServletResponse response, final String apiPathShouldStartWith, final String apiPath) throws IOException, ServletException { try { // Strip path parameters (semicolons) to prevent parser differential // between URI.normalize() and Tomcat's getRequestDispatcher() String sanitizedPath = PathSanitizer.stripPathParameters(apiPath); String encodedAPIPath = UriUtils.encodePath(sanitizedPath, "UTF-8"); URI uri = new URI(encodedAPIPath); if (!uri.normalize().toString().startsWith(apiPathShouldStartWith)) { final String message = "attempt to access unauthorized path " + encodedAPIPath; if (LOGGER.isDebugEnabled()) { LOGGER.debug(message); } response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.flushBuffer(); } else { request.getRequestDispatcher(encodedAPIPath).forward(request, response); } } catch (URISyntaxException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(e.getMessage()); } response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.flushBuffer(); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/CustomPageService.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; import static java.util.Collections.emptySet; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import groovy.lang.GroovyClassLoader; import org.apache.commons.io.FileUtils; import org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory; import org.bonitasoft.console.common.server.preferences.properties.PropertiesWithSet; import org.bonitasoft.console.common.server.utils.UnzipUtil; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.PermissionAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.page.PageNotFoundException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.extension.page.PageController; import org.bonitasoft.web.extension.page.PageResourceProvider; import org.bonitasoft.web.extension.rest.RestApiController; import org.bonitasoft.web.rest.server.api.extension.ControllerClassName; import org.codehaus.groovy.control.CompilationFailedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Anthony Birembaut, Fabio Lombardi */ public class CustomPageService { public static final String PAGE_CONTROLLER_FILENAME = "Index.groovy"; public static final String PAGE_INDEX_NAME = "index"; public static final String PAGE_INDEX_FILENAME = "index.html"; private static final ConcurrentMap PAGES_CLASSLOADERS = new ConcurrentHashMap<>(); private static final ConcurrentMap PAGES_UPDATE_TIMESTAMPS = new ConcurrentHashMap<>(); private static final ConcurrentMap PAGES_LAST_UPDATE_DB_CHECK = new ConcurrentHashMap<>(); private static final ConcurrentMap PAGES_LOCKS = new ConcurrentHashMap<>(); public static final String RESOURCES_PROPERTY = "resources"; public static final String PROPERTY_CONTENT_TYPE = "contentType"; public static final String NAME_PROPERTY = "name"; private static final Logger LOGGER = LoggerFactory.getLogger(CustomPageService.class.getName()); public GroovyClassLoader getPageClassloader(final APISession apiSession, final PageResourceProvider pageResourceProvider) throws IOException, CompilationFailedException { return buildPageClassloader(apiSession, pageResourceProvider.getFullPageName(), pageResourceProvider.getPageDirectory()); } public void ensurePageFolderIsPresent(final APISession apiSession, final PageResourceProvider pageResourceProvider) throws BonitaException, IOException { final String fullPageName = pageResourceProvider.getFullPageName(); synchronized (getPageLock(fullPageName)) { File pageDirectory = pageResourceProvider.getPageDirectory(); if (!pageDirectory.exists() || pageDirectory.list().length == 0) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("No page folder for page: " + fullPageName); } retrievePageZipContent(apiSession, pageResourceProvider); } } } public void ensurePageFolderIsUpToDate(final APISession apiSession, final PageResourceProvider pageResourceProvider) throws BonitaException, IOException { final String fullPageName = pageResourceProvider.getFullPageName(); synchronized (getPageLock(fullPageName)) { final Long pageTimestampFromCache = getPageTimestampFromMemoryCache(fullPageName); if (pageTimestampFromCache != null) { if (shouldVerifyLastUpdateDateInDatabaseAndFolderHealthy(fullPageName)) { //if it has been less than a certain time since the last database check do not check again final long databaseLastUpdateTimestamp = getPageLastUpdateDateFromEngine(apiSession, pageResourceProvider); if (databaseLastUpdateTimestamp != pageTimestampFromCache) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Local update will be performed for page: " + fullPageName); } removePage(pageResourceProvider, true); retrievePageZipContent(apiSession, pageResourceProvider); } else { //make sure the page temp directory has not been altered ensurePageTempFolderIsHealthy(apiSession, pageResourceProvider); } } } else { //if the last update date is not in the cache, the page has not been retrieved yet if (LOGGER.isDebugEnabled()) { LOGGER.debug("Local retrieval of page: " + fullPageName); } retrievePageZipContent(apiSession, pageResourceProvider); } } } protected void ensurePageTempFolderIsHealthy(final APISession apiSession, final PageResourceProvider pageResourceProvider) throws IOException, BonitaException { final File pageFolder = pageResourceProvider.getPageDirectory(); if (!pageFolder.exists() || pageFolder.list().length == 0) { if (LOGGER.isDebugEnabled()) { LOGGER.debug( "Page folder does not seem to be healthy for page: " + pageResourceProvider.getFullPageName()); } removePage(pageResourceProvider, true); retrievePageZipContent(apiSession, pageResourceProvider); } } protected boolean shouldVerifyLastUpdateDateInDatabaseAndFolderHealthy(final String fullPageName) { Long lastTimePageUpdateWasCheckedInDB = getLastTimePageUpdateWasCheckedInDB(fullPageName); if (lastTimePageUpdateWasCheckedInDB != null) { return System.currentTimeMillis() - lastTimePageUpdateWasCheckedInDB > getPageLastUpdateCheckInterval(); } return true; } @SuppressWarnings("unchecked") public Class registerPage(final GroovyClassLoader pageClassLoader, final PageResourceProvider pageResourceProvider) throws CompilationFailedException, IOException { final File pageControllerFile = getGroovyPageFile(pageResourceProvider.getPageDirectory()); return pageClassLoader.parseClass(pageControllerFile); } public Class registerRestApiPage(final GroovyClassLoader pageClassLoader, PageResourceProviderImpl pageResourceProvider, ControllerClassName restApiControllerClassName, String mappingKey) throws BonitaException { try { if (restApiControllerClassName.isSource()) { File groovyFile = toFile(pageResourceProvider, restApiControllerClassName.getName()); if (groovyFile.exists()) { return pageClassLoader.parseClass(groovyFile); } LOGGER.error("resource does not exists:" + mappingKey); throw new BonitaException("unable to handle rest api call to " + mappingKey); } return pageClassLoader.loadClass(restApiControllerClassName.getName()); } catch (CompilationFailedException | IOException | ClassNotFoundException e) { throw new BonitaException(e.getMessage(), e); } } protected File toFile(PageResourceProviderImpl pageResourceProvider, String classFileName) { if (classFileName.startsWith("/")) { classFileName = classFileName.substring(1); } final String[] paths = classFileName.split("/"); final Path restApiControllerPath = paths.length == 1 ? Paths.get(paths[0]) : Paths.get(paths[0], Arrays.copyOfRange(paths, 1, paths.length)); return pageResourceProvider.getPageDirectory().toPath().resolve(restApiControllerPath).toFile(); } public void verifyPageClass(final File tempPageDirectory, APISession session) throws IOException { final File pageControllerFile = getPageFile(tempPageDirectory, PAGE_CONTROLLER_FILENAME); if (pageControllerFile.exists()) { final String classloaderName = String.valueOf(System.currentTimeMillis()); final GroovyClassLoader pageClassLoader = buildPageClassloader(session, classloaderName, tempPageDirectory); try { pageClassLoader.parseClass(pageControllerFile); } catch (final CompilationFailedException ex) { LOGGER.error("Failed to compile Index.groovy ", ex); } finally { final GroovyClassLoader classLoader = PAGES_CLASSLOADERS.remove(classloaderName); if (classLoader != null) { classLoader.close(); } } } } public PageController loadPage(final Class pageClass) throws InstantiationException, IllegalAccessException { return pageClass.newInstance(); } public RestApiController loadRestApiPage(final Class restApiControllerClass) throws InstantiationException, IllegalAccessException { return restApiControllerClass.newInstance(); } protected void removePage(final PageResourceProvider pageResourceProvider, final boolean ignoreErrorOnPageDirectoryDelete) throws IOException { String fullPageName = pageResourceProvider.getFullPageName(); closeClassloader(fullPageName); removePageZipContent(pageResourceProvider.getPageDirectory(), ignoreErrorOnPageDirectoryDelete); CustomPageDependenciesResolver.removePageLibTempFolder(fullPageName); removePageTimestampsFromMemoryCache(fullPageName); } public void removePageLocally(final Page page) throws IOException { final PageResourceProvider pageResourceProvider = new PageResourceProviderImpl(page); removePageLocally(pageResourceProvider); } public void removePageLocally(final PageResourceProvider pageResourceProvider) throws IOException { removePage(pageResourceProvider, false); removePageLock(pageResourceProvider.getFullPageName()); } protected Object getPageLock(final String fullPageName) { return PAGES_LOCKS.computeIfAbsent(fullPageName, k -> new Object()); } protected void removePageLock(final String fullPageName) { PAGES_LOCKS.remove(fullPageName); } protected void addPageTimestampToMemoryCache(final String fullPageName, long timestamp) { PAGES_UPDATE_TIMESTAMPS.put(fullPageName, timestamp); } protected void setTimePageUpdateWasCheckedInDB(final String fullPageName) { PAGES_LAST_UPDATE_DB_CHECK.put(fullPageName, System.currentTimeMillis()); } protected Long getPageTimestampFromMemoryCache(final String fullPageName) { return PAGES_UPDATE_TIMESTAMPS.get(fullPageName); } protected Long getLastTimePageUpdateWasCheckedInDB(final String fullPageName) { return PAGES_LAST_UPDATE_DB_CHECK.get(fullPageName); } protected void removePageTimestampsFromMemoryCache(final String fullPageName) { PAGES_LAST_UPDATE_DB_CHECK.remove(fullPageName); PAGES_UPDATE_TIMESTAMPS.remove(fullPageName); } protected void clearPageTimestampsMemoryCache() { PAGES_UPDATE_TIMESTAMPS.clear(); PAGES_LAST_UPDATE_DB_CHECK.clear(); } private static void closeClassloader(final String pageName) throws IOException { final GroovyClassLoader classloader = PAGES_CLASSLOADERS.remove(pageName); if (classloader != null) { classloader.clearCache(); classloader.close(); } } protected GroovyClassLoader buildPageClassloader(final APISession apiSession, final String pageName, final File pageDirectory) throws CompilationFailedException, IOException { final BDMClientDependenciesResolver bdmDependenciesResolver = new BDMClientDependenciesResolver(apiSession); if (isPageInDebugMode()) { synchronized (CustomPageService.class) {//Handle multiple queries to create several classloaders at the same time return createPageClassloader(pageName, pageDirectory, bdmDependenciesResolver); } } else { //not putting the get in the synchronized block to avoid performance cost when the classloader is already in the map GroovyClassLoader pageClassLoader = PAGES_CLASSLOADERS.get(pageName); if (pageClassLoader == null || isOutdated(pageClassLoader, bdmDependenciesResolver)) { synchronized (CustomPageService.class) {//Handle multiple queries to create several classloaders at the same time if (pageClassLoader == null) { //double check in synchronize block to avoid creating the classloader twice if a creation is already in progress in another thread pageClassLoader = PAGES_CLASSLOADERS.get(pageName); if (pageClassLoader == null || isOutdated(pageClassLoader, bdmDependenciesResolver)) { pageClassLoader = createPageClassloader(pageName, pageDirectory, bdmDependenciesResolver); PAGES_CLASSLOADERS.put(pageName, pageClassLoader); } } else { //classloader is outdated pageClassLoader.clearCache(); pageClassLoader.close(); pageClassLoader = createPageClassloader(pageName, pageDirectory, bdmDependenciesResolver); PAGES_CLASSLOADERS.put(pageName, pageClassLoader); } } } return pageClassLoader; } } private GroovyClassLoader createPageClassloader(final String pageName, final File pageDirectory, final BDMClientDependenciesResolver bdmDependenciesResolver) throws IOException { GroovyClassLoader pageClassLoader = new GroovyClassLoader(getParentClassloader(pageName, new CustomPageDependenciesResolver(pageName, pageDirectory, getWebBonitaConstantsUtils()), bdmDependenciesResolver)); if (pageDirectory.exists()) { pageClassLoader.addClasspath(pageDirectory.getPath()); } return pageClassLoader; } private boolean isOutdated(GroovyClassLoader pageClassLoader, BDMClientDependenciesResolver bdmDependenciesResolver) { final ClassLoader parent = pageClassLoader.getParent(); if (!(parent instanceof VersionedClassloader cachedClassloader)) { throw new IllegalStateException("Parent classloader should be versioned."); } return !cachedClassloader.hasVersion(bdmDependenciesResolver.getBusinessDataModelVersion()); } public boolean isPageInDebugMode() { return PropertiesFactory.getConsoleProperties().isPageInDebugMode(); } public long getPageLastUpdateCheckInterval() { return PropertiesFactory.getConsoleProperties().getPageLastUpdateCheckInterval(); } protected WebBonitaConstantsUtils getWebBonitaConstantsUtils() { return WebBonitaConstantsUtils.getTenantInstance(); } protected ClassLoader getParentClassloader(final String pageName, final CustomPageDependenciesResolver customPageDependenciesResolver, final BDMClientDependenciesResolver bdmDependenciesResolver) throws IOException { final CustomPageChildFirstClassLoader classLoader = new CustomPageChildFirstClassLoader(pageName, customPageDependenciesResolver, bdmDependenciesResolver, Thread.currentThread().getContextClassLoader()); classLoader.addCustomPageResources(); return classLoader; } protected void retrievePageZipContent(final APISession apiSession, final PageResourceProvider pageResourceProvider) throws BonitaException, IOException { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Retrieving page content for page: " + pageResourceProvider.getFullPageName()); } final PageAPI pageAPI = getPageAPI(apiSession); // retrieve page zip content from engine and cache it final Page page = pageResourceProvider.getPage(pageAPI); final byte[] pageContent = pageAPI.getPageContent(page.getId()); if (pageContent.length == 0) { throw new BonitaException("No content available for page: " + page.getName()); } final File tempPageFile = ((PageResourceProviderImpl) pageResourceProvider).getTempPageFile(); FileUtils.writeByteArrayToFile(tempPageFile, pageContent); UnzipUtil.unzip(tempPageFile, pageResourceProvider.getPageDirectory().getPath(), true); long lastUpdateTimestamp = 0L; if (page.getLastModificationDate() != null) { lastUpdateTimestamp = page.getLastModificationDate().getTime(); } String fullPageName = pageResourceProvider.getFullPageName(); addPageTimestampToMemoryCache(fullPageName, lastUpdateTimestamp); setTimePageUpdateWasCheckedInDB(fullPageName); } protected PageAPI getPageAPI(final APISession apiSession) throws BonitaException { return TenantAPIAccessor.getCustomPageAPI(apiSession); } protected PermissionAPI getPermissionAPI(final APISession apiSession) throws BonitaException { return TenantAPIAccessor.getPermissionAPI(apiSession); } protected void removePageZipContent(final File pageDirectory, final boolean ignoreErrorOnPageDirectoryDelete) throws IOException { try { FileUtils.deleteDirectory(pageDirectory); } catch (IOException e) { //in some circumstances with java 8 page directory cannot be removed. //this catch and the method ignoreErrorOnPageDirectoryDelete can probably be removed in 7.13.x if (LOGGER.isDebugEnabled()) { LOGGER.debug("Unable delete page folder. " + e.getMessage()); } if (!ignoreErrorOnPageDirectoryDelete) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Page folder will be removed when jvm shuts down."); } pageDirectory.deleteOnExit(); } } } public File getGroovyPageFile(final File pageDirectory) { return getPageFile(pageDirectory, PAGE_CONTROLLER_FILENAME); } public File getPageFile(final File pageDirectory, final String fileName) { return new File(pageDirectory, fileName); } protected long getPageLastUpdateDateFromEngine(final APISession apiSession, final PageResourceProvider pageResourceProvider) throws BonitaException { long lastUpdate = 0L; try { final PageAPI pageAPI = getPageAPI(apiSession); final Date lastUpdateDate = pageResourceProvider.getPage(pageAPI).getLastModificationDate(); if (lastUpdateDate != null) { lastUpdate = lastUpdateDate.getTime(); } } catch (final PageNotFoundException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Unable to find the page " + pageResourceProvider); } } setTimePageUpdateWasCheckedInDB(pageResourceProvider.getFullPageName()); return lastUpdate; } public Properties getPageProperties(final APISession apiSession, final byte[] zipContent, final boolean checkIfItAlreadyExists, final Long processDefinitionId) throws BonitaException { final PageAPI pageAPI = getPageAPI(apiSession); Properties properties; if (processDefinitionId == null) { properties = pageAPI.getPageProperties(zipContent, checkIfItAlreadyExists); } else { properties = pageAPI.getPageProperties(zipContent, false); if (checkIfItAlreadyExists) { final String pageName = properties.getProperty(NAME_PROPERTY); try { pageAPI.getPageByNameAndProcessDefinitionId(pageName, processDefinitionId); throw new AlreadyExistsException( "A page with name " + pageName + " already exists for the process " + processDefinitionId); } catch (final PageNotFoundException e) { try { pageAPI.getPageByName(pageName); throw new AlreadyExistsException( "A page with name " + pageName + " already exists for the tenant"); } catch (final PageNotFoundException e1) { //Do nothing (if the page was not found, it means a page with the same name doesn't already exist) } //Do nothing (if the page was not found, it means a page with the same name doesn't already exist) } } } return properties; } public Set getCustomPagePermissions(final Properties pageProperties, APISession apiSession) throws BonitaException { final PropertiesWithSet pagePropertiesWithSet = new PropertiesWithSet(pageProperties); final Set pageRestResources = new HashSet<>(pagePropertiesWithSet.getPropertyAsSet(RESOURCES_PROPERTY)); final Set permissions = new HashSet<>(); for (final String pageRestResource : pageRestResources) { final Set resourcePermissions = getPermissionAPI(apiSession) .getResourcePermissions(pageRestResource); if (emptySet().equals(resourcePermissions)) { permissions.add("<" + pageRestResource + ">"); } permissions.addAll(resourcePermissions); } return permissions; } public Page getPage(final APISession apiSession, final String pageName, final long processDefinitionId) throws BonitaException { return getPageAPI(apiSession).getPageByNameAndProcessDefinitionId(pageName, processDefinitionId); } public Page getPage(final APISession apiSession, final long pageId) throws BonitaException { return getPageAPI(apiSession).getPage(pageId); } public PageResourceProvider getPageResourceProvider(final Page page) { return new PageResourceProviderImpl(page); } public static void clearCachedClassloaders() throws IOException { for (final String page : PAGES_CLASSLOADERS.keySet()) { closeClassloader(page); } } public void writePageToPageDirectory(Page page, PageResourceProvider pageResourceProvider, File unzipPageTempFolder, APISession session) throws IOException { verifyPageClass(unzipPageTempFolder, session); File pageDirectory = pageResourceProvider.getPageDirectory(); FileUtils.copyDirectory(unzipPageTempFolder, pageDirectory); long lastUpdateTimestamp = 0L; if (page.getLastModificationDate() != null) { lastUpdateTimestamp = page.getLastModificationDate().getTime(); } String fullPageName = pageResourceProvider.getFullPageName(); addPageTimestampToMemoryCache(fullPageName, lastUpdateTimestamp); setTimePageUpdateWasCheckedInDB(fullPageName); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/CustomPageServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; import java.io.File; import java.io.IOException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnauthorizedAccessException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.livingapps.ApplicationModelFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CustomPageServlet extends HttpServlet { public static final String APP_TOKEN_PARAM = "appToken"; /** * uuid */ private static final long serialVersionUID = -5410859017103815654L; /** * Logger */ private static Logger LOGGER = LoggerFactory.getLogger(CustomPageServlet.class.getName()); protected ResourceRenderer resourceRenderer = new ResourceRenderer(); protected PageRenderer pageRenderer = new PageRenderer(resourceRenderer); protected BonitaHomeFolderAccessor bonitaHomeFolderAccessor = new BonitaHomeFolderAccessor(); protected CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier(); @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { /* * Check if requested URL is missing last slash, like "custom-page/page-name". * If missing, redirect to "custom-page/page-name/" */ if (isPageUrlWithoutFinalSlash(request)) { customPageRequestModifier.redirectToValidPageUrl(request, response); return; } final String appToken = request.getParameter(APP_TOKEN_PARAM); final HttpSession session = request.getSession(); final APISession apiSession = (APISession) session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY); final List pathSegments = resourceRenderer.getPathSegments(request.getPathInfo()); if (pathSegments.isEmpty()) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "The name of the page is required."); return; } final String pageName = pathSegments.get(0); try { if (isAuthorized(apiSession, appToken)) { if (isPageRequest(pathSegments)) { pageRenderer.displayCustomPage(request, response, apiSession, pageName); } else { final File resourceFile = getResourceFile(request.getPathInfo(), pageName); pageRenderer.ensurePageFolderIsPresent(apiSession, pageRenderer.getPageResourceProvider(pageName)); resourceRenderer.renderFile(request, response, resourceFile); } } else { response.sendError(HttpServletResponse.SC_FORBIDDEN, "User not Authorized"); } } catch (UnauthorizedAccessException e) { response.sendError(HttpServletResponse.SC_FORBIDDEN, "User not Authorized"); } catch (final Exception e) { handleException(pageName, e); } } private boolean isPageRequest(final List pathSegments) { if (pathSegments.size() == 1) { return true; } else if (pathSegments.size() == 2) { return isAnIndexSegment(pathSegments.get(1)); } return false; } private boolean isAnIndexSegment(final String segment) { return segment.equalsIgnoreCase(CustomPageService.PAGE_INDEX_FILENAME) || segment.equalsIgnoreCase(CustomPageService.PAGE_CONTROLLER_FILENAME) || segment.equalsIgnoreCase(CustomPageService.PAGE_INDEX_NAME); } private boolean isPageUrlWithoutFinalSlash(final HttpServletRequest request) { return request.getPathInfo() == null || request.getPathInfo().matches("/[^/]+"); } private File getResourceFile(final String resourcePath, final String pageName) throws IOException, BonitaException { final PageResourceProviderImpl pageResourceProvider = pageRenderer.getPageResourceProvider(pageName); final File resourceFile = new File(pageResourceProvider.getPageDirectory(), CustomPageService.RESOURCES_PROPERTY + File.separator + getResourcePathWithoutPageName(resourcePath, pageName)); if (!bonitaHomeFolderAccessor.isInFolder(resourceFile, pageResourceProvider.getPageDirectory())) { throw new UnauthorizedAccessException("Unauthorized access to the file " + resourcePath); } return resourceFile; } private String getResourcePathWithoutPageName(final String resourcePath, final String pageName) { //resource path match "/pagename/resourcefolder/filename" return resourcePath.substring(pageName.length() + 2); } private boolean isAuthorized(final APISession apiSession, final String appToken) throws BonitaException { //Technical user should be authorized in order for the custom pages to be displayed in his profile return apiSession.isTechnicalUser() || getCustomPageAuthorizationsHelper(apiSession).isAuthorized(appToken); } private void handleException(final String pageName, final Exception e) throws ServletException { if (LOGGER.isWarnEnabled()) { LOGGER.warn("Error while trying to render the custom page {}", pageName, e); } throw new ServletException(e.getMessage()); } protected ApplicationAuthorizationsHelper getCustomPageAuthorizationsHelper(final APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return new ApplicationAuthorizationsHelper(apiSession, new ApplicationModelFactory( TenantAPIAccessor.getLivingApplicationAPI(apiSession), TenantAPIAccessor.getCustomPageAPI(apiSession), TenantAPIAccessor.getProfileAPI(apiSession))); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/MonoParentJarFileClassLoader.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URL; import java.security.AccessControlContext; import java.security.AccessController; import java.security.CodeSource; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.security.cert.Certificate; import java.util.Enumeration; import java.util.jar.Attributes; import java.util.jar.Manifest; import org.apache.xbean.classloader.NamedClassLoader; import org.apache.xbean.classloader.ResourceHandle; import org.apache.xbean.classloader.UnionEnumeration; import org.apache.xbean.classloader.UrlResourceFinder; /** * This class is highly inspired form JarFileClassLoader from xbean. The main difference is that * it inherits from NamedClassLoader instead of MultiParentClassLoader. */ public class MonoParentJarFileClassLoader extends NamedClassLoader { private static final URL[] EMPTY_URLS = new URL[0]; private final UrlResourceFinder resourceFinder = new UrlResourceFinder(); private final AccessControlContext acc; /** * Creates a JarFileClassLoader that is a child of the specified class loader. * * @param name * the name of this class loader * @param urls * a list of URLs from which classes and resources should be loaded * @param parent * the parent of this class loader */ public MonoParentJarFileClassLoader(final String name, final URL[] urls, final ClassLoader parent) { super(name, EMPTY_URLS, parent); acc = AccessController.getContext(); addURLs(urls); } /** * {@inheritDoc} */ @Override public URL[] getURLs() { return resourceFinder.getUrls(); } /** * {@inheritDoc} */ @Override public void addURL(final URL url) { AccessController.doPrivileged((PrivilegedAction) () -> { resourceFinder.addUrl(url); return null; }, acc); } /** * Adds an array of urls to the end of this class loader. * * @param urls * the URLs to add */ protected void addURLs(final URL[] urls) { AccessController.doPrivileged((PrivilegedAction) () -> { if (urls != null && urls.length > 0) { for (final URL url : urls) { resourceFinder.addUrl(url); } } return null; }, acc); } /** * {@inheritDoc} */ @Override public void destroy() { resourceFinder.destroy(); super.destroy(); } /** * {@inheritDoc} */ @Override public URL findResource(final String resourceName) { return (URL) AccessController.doPrivileged((PrivilegedAction) () -> resourceFinder.findResource(resourceName), acc); } /** * {@inheritDoc} */ @Override public Enumeration findResources(final String resourceName) throws IOException { // TODO this is not right // first get the resources from the parent classloaders final Enumeration parentResources = super.findResources(resourceName); // get the classes from my urls final Enumeration myResources = (Enumeration) AccessController .doPrivileged((PrivilegedAction) () -> resourceFinder.findResources(resourceName), acc); // join the two together return new UnionEnumeration(parentResources, myResources); } /** * {@inheritDoc} */ @Override protected String findLibrary(final String libraryName) { // if the libraryName is actually a directory it is invalid final int pathEnd = libraryName.lastIndexOf('/'); if (pathEnd == libraryName.length() - 1) { throw new IllegalArgumentException("libraryName ends with a '/' character: " + libraryName); } // get the name if the library file final String resourceName; if (pathEnd < 0) { resourceName = System.mapLibraryName(libraryName); } else { final String path = libraryName.substring(0, pathEnd + 1); final String file = libraryName.substring(pathEnd + 1); resourceName = path + System.mapLibraryName(file); } // get a resource handle to the library final ResourceHandle resourceHandle = (ResourceHandle) AccessController .doPrivileged((PrivilegedAction) () -> resourceFinder.getResource(resourceName), acc); if (resourceHandle == null) { return null; } // the library must be accessable on the file system final URL url = resourceHandle.getUrl(); if (!"file".equals(url.getProtocol())) { return null; } return new File(URI.create(url.toString())).getPath(); } /** * {@inheritDoc} */ @Override protected Class findClass(final String className) throws ClassNotFoundException { try { return (Class) AccessController.doPrivileged(new PrivilegedExceptionAction<>() { @Override public Object run() throws ClassNotFoundException { // First think check if we are allowed to define the package checkPackageDefinition(className); final ResourceHandle resourceHandle = findClassFileResource(className); byte[] bytes; Manifest manifest; try { // get the bytes from the class file bytes = resourceHandle.getBytes(); // get the manifest for defining the packages manifest = resourceHandle.getManifest(); } catch (final IOException e) { throw new ClassNotFoundException(className, e); } // get the certificates for the code source final Certificate[] certificates = resourceHandle.getCertificates(); // the code source url is used to define the package and as the security context for the class final URL codeSourceUrl = resourceHandle.getCodeSourceUrl(); // define the package (required for security) definePackage(className, codeSourceUrl, manifest); // this is the security context of the class final CodeSource codeSource = new CodeSource(codeSourceUrl, certificates); // load the class into the vm return defineClass(className, bytes, 0, bytes.length, codeSource); } private ResourceHandle findClassFileResource(final String className) throws ClassNotFoundException { // convert the class name to a file name final String resourceName = className.replace('.', '/') + ".class"; // find the class file resource final ResourceHandle resourceHandle = resourceFinder.getResource(resourceName); if (resourceHandle == null) { throw new ClassNotFoundException(className); } return resourceHandle; } private void checkPackageDefinition(final String className) { final SecurityManager securityManager = System.getSecurityManager(); if (securityManager != null) { String packageName; final int packageEnd = className.lastIndexOf('.'); if (packageEnd >= 0) { packageName = className.substring(0, packageEnd); securityManager.checkPackageDefinition(packageName); } } } }, acc); } catch (final PrivilegedActionException e) { throw (ClassNotFoundException) e.getException(); } } private void definePackage(final String className, final URL jarUrl, final Manifest manifest) { final int packageEnd = className.lastIndexOf('.'); if (packageEnd < 0) { return; } final String packageName = className.substring(0, packageEnd); final String packagePath = packageName.replace('.', '/') + "/"; Attributes packageAttributes = null; Attributes mainAttributes = null; if (manifest != null) { packageAttributes = manifest.getAttributes(packagePath); mainAttributes = manifest.getMainAttributes(); } final Package pkg = getPackage(packageName); if (pkg != null) { if (pkg.isSealed()) { if (!pkg.isSealed(jarUrl)) { throw new SecurityException( "Package was already sealed with another URL: package=" + packageName + ", url=" + jarUrl); } } else { if (isSealed(packageAttributes, mainAttributes)) { throw new SecurityException("Package was already been loaded and not sealed: package=" + packageName + ", url=" + jarUrl); } } } else { final String specTitle = getAttribute(Attributes.Name.SPECIFICATION_TITLE, packageAttributes, mainAttributes); final String specVendor = getAttribute(Attributes.Name.SPECIFICATION_VENDOR, packageAttributes, mainAttributes); final String specVersion = getAttribute(Attributes.Name.SPECIFICATION_VERSION, packageAttributes, mainAttributes); final String implTitle = getAttribute(Attributes.Name.IMPLEMENTATION_TITLE, packageAttributes, mainAttributes); final String implVendor = getAttribute(Attributes.Name.IMPLEMENTATION_VENDOR, packageAttributes, mainAttributes); final String implVersion = getAttribute(Attributes.Name.IMPLEMENTATION_VERSION, packageAttributes, mainAttributes); URL sealBase = null; if (isSealed(packageAttributes, mainAttributes)) { sealBase = jarUrl; } definePackage(packageName, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase); } } private String getAttribute(final Attributes.Name name, final Attributes packageAttributes, final Attributes mainAttributes) { if (packageAttributes != null) { final String value = packageAttributes.getValue(name); if (value != null) { return value; } } if (mainAttributes != null) { return mainAttributes.getValue(name); } return null; } private boolean isSealed(final Attributes packageAttributes, final Attributes mainAttributes) { final String sealed = getAttribute(Attributes.Name.SEALED, packageAttributes, mainAttributes); if (sealed == null) { return false; } return "true".equalsIgnoreCase(sealed); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/PageContextHelper.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; import java.util.Locale; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.bonitasoft.console.common.server.utils.LocaleUtils; import org.bonitasoft.engine.session.APISession; public class PageContextHelper { public static final String PROFILE_PARAM = "profile"; public static final String ATTRIBUTE_API_SESSION = "apiSession"; private final HttpServletRequest request; public PageContextHelper(HttpServletRequest request) { this.request = request; } public String getCurrentProfile() { return request.getParameter(PROFILE_PARAM); } public Locale getCurrentLocale() { return LocaleUtils.getUserLocale(request); } public APISession getApiSession() { final HttpSession httpSession = request.getSession(); return (APISession) httpSession.getAttribute(ATTRIBUTE_API_SESSION); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/PageDownloadServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; import java.io.IOException; import java.io.OutputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Anthony Birembaut */ public class PageDownloadServlet extends HttpServlet { private static final String ID_PARAM = "id"; /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(PageDownloadServlet.class.getName()); /** * UID */ private static final long serialVersionUID = 7203686892997001991L; @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException { final String pageIDStr = request.getParameter(ID_PARAM); long pageId = 0L; if (pageIDStr != null) { pageId = Long.parseLong(pageIDStr); } else { throw new ServletException("The ID parameter is mandatory."); } final APISession apiSession = (APISession) request.getSession().getAttribute(SessionUtil.API_SESSION_PARAM_KEY); OutputStream out = null; try { final PageAPI pageAPI = getPageAPI(apiSession); final Page page = pageAPI.getPage(pageId); final byte[] pageContent = pageAPI.getPageContent(pageId); // Set response headers response.setCharacterEncoding("UTF-8"); response.setContentType("application/octet-stream"); final String encodedfileName = URLEncoder.encode(page.getContentName(), StandardCharsets.UTF_8); final String userAgent = request.getHeader("User-Agent"); if (userAgent != null && userAgent.contains("Firefox")) { response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedfileName); } else { response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedfileName.replaceAll("\\_", " ") + "\"; filename*=UTF-8''" + encodedfileName); } out = response.getOutputStream(); if (pageContent == null) { response.setContentLength(0); } else { response.setContentLength(pageContent.length); } out.write(pageContent); } catch (final InvalidSessionException e) { final String message = "Session expired. Please login again."; if (LOGGER.isInfoEnabled()) { LOGGER.info(message, e); } try { out.write(message.getBytes()); } catch (final IOException e1) { throw new ServletException(e1); } } catch (final Exception e) { if (LOGGER.isErrorEnabled()) { LOGGER.error(e.getMessage(), e); } try { out.write("An exception occurred. Please contact an administrator".getBytes()); } catch (final IOException e1) { throw new ServletException(e1); } } } private PageAPI getPageAPI(final APISession apiSession) throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getCustomPageAPI(apiSession); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/PageMappingService.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; import java.io.Serializable; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.page.PageURL; import org.bonitasoft.engine.page.URLAdapterConstants; import org.bonitasoft.engine.session.APISession; public class PageMappingService { public PageReference getPage(final HttpServletRequest request, final APISession apiSession, final String mappingKey, final Locale locale, final boolean executeAuthorizationRules) throws BonitaException { final Map context = new HashMap<>(); //clone the request parameters map to a HashMap to avoid deserialization exceptions when calling a remote engine //see BS-16992 (the parameters map implementation is specific to the servlet container). Map parametersMapCopy = request.getParameterMap().entrySet().stream() .collect(Collectors.toMap(Entry::getKey, e -> e.getValue().clone())); context.put(URLAdapterConstants.QUERY_PARAMETERS, (Serializable) parametersMapCopy); context.put(URLAdapterConstants.LOCALE, locale.toString()); context.put(URLAdapterConstants.CONTEXT_PATH, request.getContextPath()); final PageAPI pageAPI = getPageAPI(apiSession); final PageURL pageURL = pageAPI.resolvePageOrURL(mappingKey, context, executeAuthorizationRules); return new PageReference(pageURL.getPageId(), pageURL.getUrl()); } protected PageAPI getPageAPI(final APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getCustomPageAPI(apiSession); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/PageReference.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; import java.io.Serial; import java.io.Serializable; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class PageReference implements Serializable { @Serial private static final long serialVersionUID = -1692145871057019847L; private Long pageId; private String url; } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/PageRenderer.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; import java.io.File; import java.io.IOException; import java.util.Locale; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import groovy.lang.GroovyClassLoader; import org.bonitasoft.console.common.server.page.extension.PageContextImpl; import org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl; import org.bonitasoft.console.common.server.utils.LocaleUtils; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.session.APISession; import org.codehaus.groovy.control.CompilationFailedException; /** * Class used by servlets to display a custom page * Since each instance of the servlet carry an instance of this class, it should have absolutely no instance attribute * * @author Anthony Birembaut */ public class PageRenderer { public static final String PROFILE_PARAM = "profile"; protected CustomPageService customPageService = new CustomPageService(); protected ResourceRenderer resourceRenderer; public PageRenderer(final ResourceRenderer resourceRenderer) { this.resourceRenderer = resourceRenderer; } public void displayCustomPage(final HttpServletRequest request, final HttpServletResponse response, final APISession apiSession, final String pageName) throws CompilationFailedException, InstantiationException, IllegalAccessException, IOException, BonitaException { final PageResourceProviderImpl pageResourceProvider = getPageResourceProvider(pageName); displayCustomPage(request, response, apiSession, pageResourceProvider, getCurrentLocale(request)); } public void displayCustomPage(final HttpServletRequest request, final HttpServletResponse response, final APISession apiSession, final long pageId) throws CompilationFailedException, InstantiationException, IllegalAccessException, IOException, BonitaException { displayCustomPage(request, response, apiSession, getPageResourceProvider(pageId, apiSession), getCurrentLocale(request)); } public void displayCustomPage(final HttpServletRequest request, final HttpServletResponse response, final APISession apiSession, final long pageId, final Locale currentLocale) throws CompilationFailedException, InstantiationException, IllegalAccessException, IOException, BonitaException { displayCustomPage(request, response, apiSession, getPageResourceProvider(pageId, apiSession), currentLocale); } public void ensurePageFolderIsPresent(final APISession apiSession, final PageResourceProviderImpl pageResourceProvider) throws BonitaException, IOException { customPageService.ensurePageFolderIsPresent(apiSession, pageResourceProvider); } private void displayCustomPage(final HttpServletRequest request, final HttpServletResponse response, final APISession apiSession, final PageResourceProviderImpl pageResourceProvider, final Locale currentLocale) throws BonitaException, IOException, InstantiationException, IllegalAccessException { customPageService.ensurePageFolderIsUpToDate(apiSession, pageResourceProvider); enforceLocaleCookieIfPresentInURLOrBrowser(request, response, currentLocale); if (isGroovyPage(pageResourceProvider)) { displayGroovyPage(request, response, apiSession, pageResourceProvider); } else { displaySimpleHtmlPage(request, response, pageResourceProvider); } } private boolean isGroovyPage(final PageResourceProviderImpl pageResourceProvider) { final File pageFolder = pageResourceProvider.getPageDirectory(); final File indexGroovy = customPageService.getGroovyPageFile(pageFolder); return indexGroovy.exists(); } private void displaySimpleHtmlPage(final HttpServletRequest request, final HttpServletResponse response, final PageResourceProviderImpl pageResourceProvider) throws IOException, BonitaException, IllegalAccessException { final File resourceFile = getIndexFile(pageResourceProvider); resourceRenderer.renderFile(request, response, resourceFile, true); } private File getIndexFile(final PageResourceProviderImpl pageResourceProvider) { final File indexHtml = new File(getResourceFolder(pageResourceProvider), CustomPageService.PAGE_INDEX_FILENAME); if (indexHtml.exists()) { return indexHtml; } //fallback try to found index.html a the root of the zip to support custompage legacy. return new File(getResourceFolder(pageResourceProvider).getParent(), CustomPageService.PAGE_INDEX_FILENAME); } private File getResourceFolder(final PageResourceProviderImpl pageResourceProvider) { return new File(pageResourceProvider.getPageDirectory(), CustomPageService.RESOURCES_PROPERTY); } private void displayGroovyPage(final HttpServletRequest request, final HttpServletResponse response, final APISession apiSession, final PageResourceProviderImpl pageResourceProvider) throws CompilationFailedException, InstantiationException, IllegalAccessException, IOException { response.setContentType("text/html"); response.setCharacterEncoding("UTF-8"); final ClassLoader originalClassloader = Thread.currentThread().getContextClassLoader(); final GroovyClassLoader pageClassloader = customPageService.getPageClassloader(apiSession, pageResourceProvider); try { Thread.currentThread().setContextClassLoader(pageClassloader); pageResourceProvider.setResourceClassLoader(pageClassloader); final Class pageClass = customPageService.registerPage(pageClassloader, pageResourceProvider); final org.bonitasoft.web.extension.page.PageController pageController = ((Class) pageClass) .newInstance(); pageController.doGet(request, response, pageResourceProvider, new PageContextImpl(apiSession, getCurrentLocale(request), getCurrentProfile(request))); } finally { Thread.currentThread().setContextClassLoader(originalClassloader); } } public String getCurrentProfile(final HttpServletRequest request) { return request.getParameter(PROFILE_PARAM); } private void enforceLocaleCookieIfPresentInURLOrBrowser(final HttpServletRequest request, final HttpServletResponse response, final Locale currentLocale) { final String localeFromCookie = LocaleUtils.getStandardizedLocaleFromCookie(request); if (currentLocale != null && !currentLocale.toString().equals(localeFromCookie)) { //Set the cookie if the locale is in the URL and different from the existing cookie value or the cookie does not exist yet LocaleUtils.addOrReplaceLocaleCookieResponse(response, currentLocale.toString()); } } public Locale getCurrentLocale(final HttpServletRequest request) { return LocaleUtils.getUserLocale(request); } public PageResourceProviderImpl getPageResourceProvider(final String pageName) { return new PageResourceProviderImpl(pageName); } public PageResourceProviderImpl getPageResourceProvider(final long pageId, final APISession apiSession) throws BonitaException { final Page page = customPageService.getPage(apiSession, pageId); return new PageResourceProviderImpl(page); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/PageServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.Locale; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpHeaders; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; import org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.exception.UnauthorizedAccessException; import org.bonitasoft.engine.page.PageNotFoundException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.livingapps.ApplicationModelFactory; import org.bonitasoft.livingapps.exception.CreationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Servlet allowing to display a page or the resource of a page with an URL like * /portal/resource//content/ * (it can be a custom page or an external page) * This servlet is used to display process forms/overview pages * Note: whenever there is an InvalidSessionException from the engine it performs an HTTP session logout and send a 401 * error. since we are in a iframe, we cannot redirect to the login page. * However, the page should handle the 401 and refresh the top window. * * @author Anthony Birembaut */ public class PageServlet extends HttpServlet { /** * UID */ private static final long serialVersionUID = 2789496969243916444L; /** * Logger */ private static Logger LOGGER = LoggerFactory.getLogger(PageServlet.class.getName()); public static final String RESOURCE_PATH_SEPARATOR = "/content"; public static final String API_PATH_SEPARATOR = "/API"; public static final String THEME_PATH_SEPARATOR = "/theme"; public static final String APPLICATION_PARAM = "app"; protected CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier(); protected PageMappingService pageMappingService = new PageMappingService(); protected BonitaHomeFolderAccessor bonitaHomeFolderAccessor = new BonitaHomeFolderAccessor(); protected ResourceRenderer resourceRenderer = new ResourceRenderer(); protected PageRenderer pageRenderer = new PageRenderer(resourceRenderer); @Override protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { final String pathInfo = request.getPathInfo(); if (!pathInfo.contains(RESOURCE_PATH_SEPARATOR + "/") && pathInfo.indexOf(API_PATH_SEPARATOR + "/") > 0) { //Support relative calls to the REST API from the forms using ../API/ final String apiPath = pathInfo.substring(pathInfo.indexOf(API_PATH_SEPARATOR + "/")); //security check against directory traversal attack customPageRequestModifier.forwardIfRequestIsAuthorized(request, response, API_PATH_SEPARATOR, apiPath); } else { super.service(request, response); } } @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { final HttpSession session = request.getSession(); final APISession apiSession = (APISession) session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY); final String pathInfo = request.getPathInfo(); // Check if requested URL is missing final slash (necessary in order to be able to use relative URLs for resources) if (pathInfo.endsWith(RESOURCE_PATH_SEPARATOR)) { customPageRequestModifier.redirectToValidPageUrl(request, response); } else if (pathInfo.indexOf(RESOURCE_PATH_SEPARATOR + "/") > 0) { final String[] pathInfoSegments = pathInfo.split(RESOURCE_PATH_SEPARATOR + "/", 2); String resourcePath = getResourcePath(pathInfoSegments); final String mappingKey = pathInfoSegments[0].substring(1); try { resolveAndDisplayPage(request, response, apiSession, mappingKey, resourcePath); } catch (final Exception e) { handleException(request, response, mappingKey, isNotResourcePath(resourcePath), e); } } else if (pathInfo.indexOf(THEME_PATH_SEPARATOR + "/") > 0) { final String[] pathInfoSegments = pathInfo.split(THEME_PATH_SEPARATOR + "/", 2); String resourcePath = getResourcePath(pathInfoSegments); final String mappingKey = pathInfoSegments[0].substring(1); try { renderThemeResource(request, response, apiSession, resourcePath); } catch (final Exception e) { handleException(request, response, mappingKey, false, e); } } else { final String message = "/content or /theme is expected in the URL after the page mapping key"; if (LOGGER.isDebugEnabled()) { LOGGER.debug("Bad request: " + message); } response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); } } protected void renderThemeResource(final HttpServletRequest request, final HttpServletResponse response, final APISession apiSession, String resourcePath) throws BonitaException, CreationException, IllegalAccessException, IOException, ServletException { String appToken = request.getParameter(APPLICATION_PARAM); if (appToken != null) { renderThemeResource(request, response, apiSession, resourcePath, appToken); } else { String appTokenFromReferer = getAppFromReferer(request); if (appTokenFromReferer != null) { if (resourcePath.endsWith(".css")) { if (LOGGER.isDebugEnabled()) { LOGGER.debug( "App parameter retrieved from the referer. Redirecting the request to get it in the URL for resource " + resourcePath); } String queryString = StringUtils.isEmpty(request.getQueryString()) ? "" : (request.getQueryString() + "&"); response.sendRedirect("?" + queryString + APPLICATION_PARAM + "=" + appTokenFromReferer); } else { renderThemeResource(request, response, apiSession, resourcePath, appTokenFromReferer); } } else { // Try to get requested resource from portal theme if (LOGGER.isDebugEnabled()) { LOGGER.debug("Unable tor retrieve app parameter for resource " + resourcePath + ". Request referer is missing an an app parameter. Forwarding to the portal theme."); } String themePath = THEME_PATH_SEPARATOR + "/" + resourcePath; //security check against directory traversal attack customPageRequestModifier.forwardIfRequestIsAuthorized(request, response, THEME_PATH_SEPARATOR, themePath); } } } protected void renderThemeResource(final HttpServletRequest request, final HttpServletResponse response, final APISession apiSession, String resourcePath, String appToken) throws BonitaException, CreationException, IllegalAccessException, IOException { // Try to get requested resource from the current Living application theme Long themeId = getThemeId(apiSession, appToken); resourceRenderer.renderFile(request, response, getResourceFile(response, apiSession, themeId, resourcePath)); } protected String getResourcePath(final String[] pathInfoSegments) { String resourcePath = null; if (pathInfoSegments.length > 1 && !pathInfoSegments[1].isEmpty()) { resourcePath = pathInfoSegments[1]; } return resourcePath; } protected String getAppFromReferer(final HttpServletRequest request) { String referer = request.getHeader(HttpHeaders.REFERER); if (referer != null) { List paramList = null; try { paramList = URLEncodedUtils.parse(new URI(referer), "UTF-8"); } catch (URISyntaxException e) { if (LOGGER.isWarnEnabled()) { LOGGER.warn("Unable tor retrieve app parameter. Bad request referer: " + e.getMessage()); } } for (NameValuePair param : paramList) { if (APPLICATION_PARAM.equalsIgnoreCase(param.getName())) { return param.getValue(); } } } else if (LOGGER.isDebugEnabled()) { LOGGER.debug("Unable tor retrieve app parameter. Request referer is null."); } return null; } protected Long getThemeId(APISession apiSession, final String appToken) throws BonitaException, CreationException { ApplicationModelFactory applicationModelFactory = new ApplicationModelFactory( TenantAPIAccessor.getLivingApplicationAPI(apiSession), TenantAPIAccessor.getCustomPageAPI(apiSession), TenantAPIAccessor.getProfileAPI(apiSession)); return applicationModelFactory.createApplicationModel(appToken).getApplicationThemeId(); } protected void resolveAndDisplayPage(final HttpServletRequest request, final HttpServletResponse response, final APISession apiSession, final String mappingKey, final String resourcePath) throws BonitaException, IOException, InstantiationException, IllegalAccessException { boolean isNotResourcePath = isNotResourcePath(resourcePath); try { Locale currentLocale = pageRenderer.getCurrentLocale(request); final PageReference pageReference = pageMappingService.getPage(request, apiSession, mappingKey, currentLocale, isNotResourcePath); if (pageReference.getUrl() != null) { displayExternalPage(response, pageReference.getUrl()); } else if (pageReference.getPageId() != null) { displayPageOrResource(request, response, apiSession, pageReference.getPageId(), resourcePath, currentLocale); } else { if (LOGGER.isDebugEnabled()) { final String message = "Both URL and pageId are not set in the page mapping for " + mappingKey; LOGGER.debug(message); } } } catch (final UnauthorizedAccessException e) { final String message = "User not Authorized"; if (LOGGER.isDebugEnabled()) { LOGGER.debug("Forbidden: " + message, e); } if (isNotResourcePath) { response.sendError(HttpServletResponse.SC_FORBIDDEN, message); } else { response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.flushBuffer(); } } catch (final NotFoundException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Not found: Cannot find the form mapping for key " + mappingKey, e); } if (isNotResourcePath) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "Form mapping not found"); } else { response.setStatus(HttpServletResponse.SC_NOT_FOUND); response.flushBuffer(); } } } protected void displayPageOrResource(final HttpServletRequest request, final HttpServletResponse response, final APISession apiSession, final Long pageId, final String resourcePath, final Locale currentLocale) throws InstantiationException, IllegalAccessException, IOException, BonitaException { boolean isNotResourcePath = isNotResourcePath(resourcePath); try { if (isNotResourcePath) { pageRenderer.displayCustomPage(request, response, apiSession, pageId, currentLocale); } else { resourceRenderer.renderFile(request, response, getResourceFile(response, apiSession, pageId, resourcePath)); } } catch (final PageNotFoundException e) { if (LOGGER.isWarnEnabled()) { LOGGER.warn("Cannot find the page with ID " + pageId); } if (isNotResourcePath) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "Page not found"); } else { response.setStatus(HttpServletResponse.SC_NOT_FOUND); response.flushBuffer(); } } } private boolean isNotResourcePath(final String resourcePath) { return resourcePath == null || CustomPageService.PAGE_INDEX_FILENAME.equals(resourcePath) || CustomPageService.PAGE_CONTROLLER_FILENAME.equals(resourcePath) || CustomPageService.PAGE_INDEX_NAME.equals(resourcePath); } protected File getResourceFile(final HttpServletResponse response, final APISession apiSession, final Long pageId, final String resourcePath) throws IOException, BonitaException { final PageResourceProviderImpl pageResourceProvider = pageRenderer.getPageResourceProvider(pageId, apiSession); final File resourceFile = pageResourceProvider .getResourceAsFile(CustomPageService.RESOURCES_PROPERTY + File.separator + resourcePath); if (!bonitaHomeFolderAccessor.isInFolder(resourceFile, pageResourceProvider.getPageDirectory())) { final String message = "For security reasons, access to this file path is forbidden : " + resourcePath; if (LOGGER.isDebugEnabled()) { LOGGER.debug("Forbidden: " + message); } response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.flushBuffer(); } pageRenderer.ensurePageFolderIsPresent(apiSession, pageResourceProvider); return resourceFile; } protected void displayExternalPage(final HttpServletResponse response, final String url) throws IOException { response.sendRedirect(response.encodeRedirectURL(url)); } protected void handleException(final HttpServletRequest request, final HttpServletResponse response, final String mappingKey, final boolean isNotResourcePath, final Exception e) throws ServletException, IOException { if (e instanceof IllegalArgumentException) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("The parameters passed to the servlet are invalid.", e); } if (isNotResourcePath) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid Request."); } else { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.flushBuffer(); } } else if (e instanceof InvalidSessionException) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Invalid Bonita engine session.", e); } SessionUtil.sessionLogout(request.getSession()); if (isNotResourcePath) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid Bonita engine session."); } else { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.flushBuffer(); } } else { if (LOGGER.isWarnEnabled()) { final String message = "Error while trying to display a page or resource for key " + mappingKey; LOGGER.warn(message, e); } if (!response.isCommitted()) { if (isNotResourcePath) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } else { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); response.flushBuffer(); } } else { throw new IOException("The response is already commited.", e); } } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/ResourceRenderer.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.engine.exception.BonitaException; import org.codehaus.groovy.control.CompilationFailedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Class used by servlets to display serve a file * Since each instance of the servlet carry an instance of this class, it should have absolutely no instance attribute * * @author Julien Mege */ public class ResourceRenderer { /** * Logger */ private final static Logger LOGGER = LoggerFactory.getLogger(ResourceRenderer.class.getName()); public void renderFile(final HttpServletRequest request, final HttpServletResponse response, final File resourceFile) throws CompilationFailedException, IllegalAccessException, IOException, BonitaException { renderFile(request, response, resourceFile, false); } public void renderFile(final HttpServletRequest request, final HttpServletResponse response, final File resourceFile, final boolean isPage) throws CompilationFailedException, IllegalAccessException, IOException, BonitaException { byte[] content; response.setCharacterEncoding("UTF-8"); try { content = getFileContent(resourceFile); response.setContentType(request.getSession().getServletContext() .getMimeType(resourceFile.getName())); response.setContentLength(content.length); response.setBufferSize(content.length); OutputStream out = response.getOutputStream(); out.write(content, 0, content.length); } catch (final FileNotFoundException e) { if (isPage) { response.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage()); } else { response.setStatus(HttpServletResponse.SC_NOT_FOUND); } } catch (final IOException e) { if (LOGGER.isErrorEnabled()) { LOGGER.error("Error while generating the response.", e); } throw e; } } private byte[] getFileContent(final File resourceFile) throws IOException, BonitaException { if (resourceFile == null) { final String errorMessage = "Resource file must not be null."; if (LOGGER.isWarnEnabled()) { LOGGER.warn(errorMessage); } throw new BonitaException(errorMessage); } if (resourceFile.exists()) { return Files.readAllBytes(resourceFile.toPath()); } else { final String fileNotFoundMessage = "Cannot find the resource file "; if (LOGGER.isDebugEnabled()) { LOGGER.debug(fileNotFoundMessage + resourceFile.getCanonicalPath()); } throw new FileNotFoundException(fileNotFoundMessage + resourceFile.getName()); } } public List getPathSegments(final String pathInfo) throws UnsupportedEncodingException { final List segments = new ArrayList<>(); if (pathInfo != null) { for (final String segment : pathInfo.split("/")) { if (!segment.isEmpty()) { segments.add(URLDecoder.decode(segment, StandardCharsets.UTF_8)); } } } return segments; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/RestApiRenderer.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import groovy.lang.GroovyClassLoader; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl; import org.bonitasoft.console.common.server.page.extension.RestAPIContextImpl; import org.bonitasoft.engine.api.APIClient; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.extension.rest.RestApiController; import org.bonitasoft.web.extension.rest.RestApiResponse; import org.bonitasoft.web.extension.rest.RestApiResponseBuilder; import org.bonitasoft.web.rest.server.api.extension.ControllerClassName; import org.bonitasoft.web.rest.server.api.extension.ResourceExtensionResolver; import org.codehaus.groovy.control.CompilationFailedException; /** * Class used by servlets to display a custom rest api * Since each instance of the servlet carry an instance of this class, it should have absolutely no instance attribute * * @author Laurent Leseigneur */ @Slf4j public class RestApiRenderer { private final CustomPageService customPageService = new CustomPageService(); public RestApiResponse handleRestApiCall(final HttpServletRequest request, ResourceExtensionResolver resourceExtensionResolver) throws CompilationFailedException, InstantiationException, IllegalAccessException, IOException, BonitaException { final PageContextHelper pageContextHelper = new PageContextHelper(request); final APISession apiSession = pageContextHelper.getApiSession(); final Long pageId = resourceExtensionResolver.resolvePageId(apiSession); final Page page = customPageService.getPage(apiSession, pageId); final PageResourceProviderImpl pageResourceProvider = new PageResourceProviderImpl(page); customPageService.ensurePageFolderIsUpToDate(apiSession, pageResourceProvider); final ControllerClassName restApiControllerClassName = resourceExtensionResolver .resolveRestApiControllerClassName(pageResourceProvider); final String mappingKey = resourceExtensionResolver.generateMappingKey(); return renderResponse(request, apiSession, pageContextHelper, pageResourceProvider, restApiControllerClassName, mappingKey); } private RestApiResponse renderResponse(final HttpServletRequest request, final APISession apiSession, final PageContextHelper pageContextHelper, final PageResourceProviderImpl pageResourceProvider, ControllerClassName restApiControllerClassName, String mappingKey) throws CompilationFailedException, InstantiationException, IllegalAccessException, IOException, BonitaException { final ClassLoader originalClassloader = Thread.currentThread().getContextClassLoader(); final GroovyClassLoader pageClassloader = customPageService.getPageClassloader(apiSession, pageResourceProvider); try { Thread.currentThread().setContextClassLoader(pageClassloader); final Class restApiControllerClass = customPageService.registerRestApiPage(pageClassloader, pageResourceProvider, restApiControllerClassName, mappingKey); pageResourceProvider.setResourceClassLoader(pageClassloader); try { return doHandle(request, apiSession, pageContextHelper, pageResourceProvider, restApiControllerClass); } catch (final Throwable e) { log.error("Error when executing rest api extension call to {}", mappingKey, e); throw e; } } finally { Thread.currentThread().setContextClassLoader(originalClassloader); } } protected RestApiResponse doHandle(final HttpServletRequest request, final APISession apiSession, final PageContextHelper pageContextHelper, final PageResourceProviderImpl pageResourceProvider, final Class restApiControllerClass) throws InstantiationException, IllegalAccessException { final RestApiController restApiController = instantiate(restApiControllerClass); return restApiController.doHandle(request, new RestApiResponseBuilder(), new RestAPIContextImpl(apiSession, new APIClient(apiSession), pageContextHelper.getCurrentLocale(), pageResourceProvider)); } protected T instantiate(Class baseClass) throws InstantiationException, IllegalAccessException { return (T) baseClass.newInstance(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/VersionedClassloader.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page; public interface VersionedClassloader { String getVersion(); boolean hasVersion(String version); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/extension/PageContextImpl.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page.extension; import java.util.Locale; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.extension.page.PageContext; /** * This class provide access to the data relative to the context in which the custom page is displayed * * @author Anthony Birembaut */ public class PageContextImpl implements PageContext { protected final APISession apiSession; protected final Locale locale; protected final String profileID; public PageContextImpl(final APISession apiSession, final Locale locale, final String profileID) { super(); this.apiSession = apiSession; this.locale = locale; this.profileID = profileID; } /** * @return the engine {@link APISession} */ public APISession getApiSession() { return apiSession; } /** * @return the user locale */ public Locale getLocale() { return locale; } /** * @return the ID of the profile in which the page is currently displayed */ public String getProfileID() { return profileID; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/extension/PageResourceProviderImpl.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page.extension; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Locale; import java.util.ResourceBundle; import org.apache.commons.io.IOUtils; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.page.PageNotFoundException; import org.bonitasoft.web.extension.page.PageResourceProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class provide access to the resources contained in the custom page zip * * @author Anthony Birembaut */ public class PageResourceProviderImpl implements PageResourceProvider { /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(PageResourceProviderImpl.class.getName()); private static final String VERSION_FILENAME = "VERSION"; protected final static String THEME_RESOURCE_SERVLET_NAME = "themeResource"; protected final static String PORTAL_THEME_NAME = "portal"; protected final static String BONITA_THEME_CSS_FILENAME = "bonita.css"; protected static String productVersion; static { final InputStream versionStream = PageResourceProviderImpl.class.getClassLoader() .getResourceAsStream(VERSION_FILENAME); if (versionStream != null) { try { productVersion = IOUtils.toString(versionStream); } catch (final Exception e) { if (LOGGER.isWarnEnabled()) { LOGGER.warn("Unable to read the file " + VERSION_FILENAME, e); } productVersion = ""; } finally { try { versionStream.close(); } catch (final IOException e) { if (LOGGER.isWarnEnabled()) { LOGGER.warn("Unable to close the input stream for file " + VERSION_FILENAME, e); } } } } else { productVersion = ""; } } /** * product version param */ protected final static String VERSION_PARAM = "v"; /** * file name */ protected final static String LOCATION_PARAM = "location"; /** * theme name : the theme folder's name */ protected final static String THEME_PARAM = "theme"; private final String fullPageName; protected String pageName; protected File pageDirectory; private ClassLoader resourceClassLoader; private File pageTempFile = null; private final Long pageId; public PageResourceProviderImpl(final String pageName) { this(pageName, null, null, true); } public PageResourceProviderImpl(final Page page) { this(page.getName(), page.getId(), page.getProcessDefinitionId(), true); } private PageResourceProviderImpl(final String pageName, final Long pageId, final Long processDefinitionId, final boolean buildPageTempFile) { this.pageName = pageName; this.pageId = pageId; fullPageName = buildFullPageName(pageName, processDefinitionId); pageDirectory = buildPageDirectory(fullPageName); if (buildPageTempFile) { buildPageTempDirectory(fullPageName); pageTempFile = buildPageTempFile(fullPageName); } } private String buildFullPageName(final String pageName, final Long processDefinitionId) { final StringBuilder builder = new StringBuilder(); if (processDefinitionId != null) { builder.append("p").append(processDefinitionId).append("_"); } builder.append(pageName); return builder.toString(); } protected void buildPageTempDirectory(final String fullPageName) { new File(WebBonitaConstantsUtils.getTenantInstance().getTempFolder(), fullPageName); } protected File buildPageTempFile(final String fullPageName) { return new File(WebBonitaConstantsUtils.getTenantInstance().getTempFolder(), fullPageName + ".zip"); } protected File buildPageDirectory(final String fullPageName) { return new File(WebBonitaConstantsUtils.getTenantInstance().getPagesFolder(), fullPageName); } @Override public InputStream getResourceAsStream(final String resourceName) throws FileNotFoundException { return new FileInputStream(getResourceAsFile(resourceName)); } @Override public File getResourceAsFile(final String resourceName) { return new File(pageDirectory, resourceName); } @Override public String getResourceURL(final String resourceName) { return resourceName + "?" + VERSION_PARAM + "=" + productVersion; } @Override public String getBonitaThemeCSSURL() { return THEME_RESOURCE_SERVLET_NAME + "?" + THEME_PARAM + "=" + PORTAL_THEME_NAME + "&" + LOCATION_PARAM + "=" + BONITA_THEME_CSS_FILENAME + "&" + VERSION_PARAM + "=" + productVersion; } @Override public File getPageDirectory() { return pageDirectory; } public File getTempPageFile() { return pageTempFile; } public void setResourceClassLoader(final ClassLoader resourceClassLoader) { this.resourceClassLoader = resourceClassLoader; } @Override public ResourceBundle getResourceBundle(final String name, final Locale locale) { return ResourceBundle.getBundle(name, locale, resourceClassLoader); } @Override public String getPageName() { return pageName; } @Override public Page getPage(final PageAPI pageAPI) throws PageNotFoundException { if (pageId != null) { return pageAPI.getPage(pageId); } return pageAPI.getPageByName(getPageName()); } @Override public String getFullPageName() { return fullPageName; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/page/extension/RestAPIContextImpl.java ================================================ /** * Copyright (C) 2022 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.console.common.server.page.extension; import java.util.Locale; import org.bonitasoft.engine.api.APIClient; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.extension.page.PageResourceProvider; import org.bonitasoft.web.extension.rest.RestAPIContext; public class RestAPIContextImpl implements RestAPIContext { private final APISession apiSession; private final Locale locale; private final PageResourceProvider resourceProvider; private final APIClient apiClient; public RestAPIContextImpl(final APISession apiSession, final APIClient apiClient, final Locale locale, PageResourceProvider resourceProvider) { this.apiSession = apiSession; this.locale = locale; this.resourceProvider = resourceProvider; this.apiClient = apiClient; } /* * (non-Javadoc) * @see org.bonitasoft.console.common.server.page.RestAPIContext#getApiSession() */ @Override public APISession getApiSession() { return apiSession; } /* * (non-Javadoc) * @see org.bonitasoft.console.common.server.page.RestAPIContext#getLocale() */ @Override public Locale getLocale() { return locale; } /* * (non-Javadoc) * @see org.bonitasoft.console.common.server.page.RestAPIContext#getResourceProvider() */ @Override public PageResourceProvider getResourceProvider() { return resourceProvider; } @Override public APIClient getApiClient() { return apiClient; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/constants/WebBonitaConstants.java ================================================ /** * Copyright (C) 2022 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.console.common.server.preferences.constants; import java.io.File; import java.lang.management.ManagementFactory; /** * @author Nicolas Chabanoles */ public interface WebBonitaConstants { /** * tenants folder */ String tenantsFolderName = "tenant"; /** * Generics folders */ String clientFolderName = "client"; String tmpFolderName = "bonita_portal_"; // We use a tempFolder specific to the running JVM, so that 2 JVMs running on the same machine are isolated: String rootTempDir = System.getProperty("java.io.tmpdir") + File.separator + tmpFolderName + ManagementFactory.getRuntimeMXBean().getName(); String formsFolderName = "forms"; String bdmFolderName = "bdm"; /** * Client */ String clientFolderPath = clientFolderName + File.separator; /** * Get Tenants Folder Path * * @return path */ String getTenantsFolderPath(); /** * Get Tenant TempFolder Path * * @return path */ String getTempFolderPath(); /** * Get Tenant FormsTempFolder Path * * @return path */ String getFormsTempFolderPath(); /** * Get pagesConsoleTempFolder Path * * @return path */ String getPagesTempFolderPath(); /** * Get BDMTempFolderPath Path * * @return path */ String getBDMTempFolderPath(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/constants/WebBonitaConstantsImpl.java ================================================ /** * Copyright (C) 2022 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.console.common.server.preferences.constants; import java.io.File; import java.nio.file.Paths; /** * @author Ruiheng.Fan */ public class WebBonitaConstantsImpl implements WebBonitaConstants { /** * platform folder */ private static final String platformFolderName = "platform"; private String platformFolderPath = null; /** * tenants folder */ private String tenantsFolderPath = null; /** * tmp */ private String tempFolderPath = null; /** * conf */ private String confFolderPath = null; private String formsWorkFolderPath = null; /** * Default constructor. */ public WebBonitaConstantsImpl() { } private String getPlatformFolderPath() { if (platformFolderPath == null) { platformFolderPath = clientFolderPath + platformFolderName + File.separator; } return platformFolderPath; } /** * {@inheritDoc} */ @Override public String getTenantsFolderPath() { if (tenantsFolderPath == null) { tenantsFolderPath = Paths.get(getTempFolderPath()).resolveSibling(tenantsFolderName) + File.separator; } return tenantsFolderPath; } /** * {@inheritDoc} */ @Override public String getTempFolderPath() { if (tempFolderPath == null) { tempFolderPath = rootTempDir + File.separator + platformFolderName + File.separator; } return tempFolderPath; } /** * {@inheritDoc} */ @Override public String getPagesTempFolderPath() { return null; } /** * {@inheritDoc} */ @Override public String getFormsTempFolderPath() { if (formsWorkFolderPath == null) { formsWorkFolderPath = getTempFolderPath() + formsFolderName + File.separator; } return formsWorkFolderPath; } @Override public String getBDMTempFolderPath() { return null; // does not means anything at platform level } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/constants/WebBonitaConstantsTenancyImpl.java ================================================ /** * Copyright (C) 2022 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.console.common.server.preferences.constants; import java.io.File; /** * @author Ruiheng.Fan */ public class WebBonitaConstantsTenancyImpl implements WebBonitaConstants { private static final String PAGES_WORK_FOLDER_NAME = "pages"; private String tenantsFolderPath = null; private final String tempFolderPath; private String formsWorkFolderPath = null; private String pagesWorkFolderPath = null; private String bdmWorkFolderPath; /** * Default constructor. */ WebBonitaConstantsTenancyImpl() { tempFolderPath = rootTempDir + File.separator + tenantsFolderName + File.separator; } @Override public String getTenantsFolderPath() { if (tenantsFolderPath == null) { tenantsFolderPath = rootTempDir + File.separator + tenantsFolderName + File.separator; } return tenantsFolderPath; } /** * {@inheritDoc} */ @Override public String getTempFolderPath() { return tempFolderPath; } /** * {@inheritDoc} */ @Override public String getPagesTempFolderPath() { if (pagesWorkFolderPath == null) { pagesWorkFolderPath = getTempFolderPath() + PAGES_WORK_FOLDER_NAME + File.separator; } return pagesWorkFolderPath; } /** * {@inheritDoc} */ @Override public String getFormsTempFolderPath() { if (formsWorkFolderPath == null) { formsWorkFolderPath = getTempFolderPath() + formsFolderName + File.separator; } return formsWorkFolderPath; } @Override public String getBDMTempFolderPath() { if (bdmWorkFolderPath == null) { bdmWorkFolderPath = getTempFolderPath() + File.separator + bdmFolderName + File.separator; } return bdmWorkFolderPath; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/constants/WebBonitaConstantsUtils.java ================================================ /** * Copyright (C) 2022 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.console.common.server.preferences.constants; import static org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstants.rootTempDir; import static org.bonitasoft.engine.io.IOUtil.createTempDirectory; import java.io.File; /** * @author Anthony Birembaut */ public class WebBonitaConstantsUtils { private static WebBonitaConstantsUtils tenantConstantsUtils = new WebBonitaConstantsUtils( new WebBonitaConstantsTenancyImpl()); private static WebBonitaConstantsUtils platformConstantsUtils = new WebBonitaConstantsUtils( new WebBonitaConstantsImpl()); private WebBonitaConstants webBonitaConstants; public WebBonitaConstantsUtils(WebBonitaConstants constants) { webBonitaConstants = constants; } public static WebBonitaConstantsUtils getTenantInstance() { return tenantConstantsUtils; } public static WebBonitaConstantsUtils getPlatformInstance() { return platformConstantsUtils; } /** * Get the folder where to write Tenant temporary files commons to all web * applications. */ public File getTempFolder() { final File tempFolder = new File(rootTempDir); if (!tempFolder.exists()) { createTempDirectory(tempFolder.toURI()); } return getFolder(webBonitaConstants.getTempFolderPath()); } /** * Get the folder of Tenant pages files */ public File getPagesFolder() { return getFolder(webBonitaConstants.getPagesTempFolderPath()); } /** * Get the Tenant folder where to write Work files. */ public File getFormsWorkFolder() { return getFolder(webBonitaConstants.getFormsTempFolderPath()); } /** * Get the Tenant folder where to write BDM work files. */ public File geBDMWorkFolder() { return getFolder(webBonitaConstants.getBDMTempFolderPath()); } private File getFolder(final String folderPath) { final File folder = new File(folderPath); if (!folder.exists()) { folder.mkdirs(); } return folder; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/properties/ConfigurationFile.java ================================================ /** * Copyright (C) 2022 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.console.common.server.preferences.properties; import static org.bonitasoft.console.common.server.preferences.properties.PropertiesWithSet.stringToSet; import java.util.Properties; import java.util.Set; /** * @author Ruiheng Fan, Anthony Birembaut */ public class ConfigurationFile { private final String propertiesFilename; public ConfigurationFile(String propertiesFilename) { this.propertiesFilename = propertiesFilename; } public String getTenantProperty(final String propertyName) { final Properties properties = getTenantPropertiesOfScope(); final String propertyValue = properties.getProperty(propertyName); return propertyValue != null ? propertyValue.trim() : null; } public Properties getTenantPropertiesOfScope() { return ConfigurationFilesManager.getInstance().getTenantProperties(propertiesFilename); } public Set getPropertyAsSet(final String propertyName) { final String propertyAsString = getTenantProperty(propertyName); return stringToSet(propertyAsString); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/properties/ConfigurationFilesManager.java ================================================ /** * Copyright (C) 2022 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.console.common.server.preferences.properties; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.apache.commons.io.FileUtils; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.console.common.server.utils.PlatformManagementUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Baptiste Mesta, Emmanuel Duchastenier, Anthony Birembaut */ public class ConfigurationFilesManager { private static final ConfigurationFilesManager INSTANCE = new ConfigurationFilesManager(); public static ConfigurationFilesManager getInstance() { return INSTANCE; } private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationFilesManager.class.getName()); private final Map tenantConfigurations = new HashMap<>(); private final Map tenantConfigurationFiles = new HashMap<>(); private Map platformConfigurations = new HashMap<>(); private final Map platformConfigurationFiles = new HashMap<>(); public Properties getPlatformProperties(String propertiesFile) { Properties properties = platformConfigurations.get(propertiesFile); if (properties == null) { return new Properties(); } return properties; } Properties getAlsoCustomAndInternalPropertiesFromFilename(String propertiesFileName) { Properties properties = new Properties(); Properties tenantConfiguration = getTenantConfiguration(propertiesFileName); if (tenantConfiguration != null) { properties.putAll(tenantConfiguration); // if -internal properties also exists, merge key/value pairs: final String internalPropertyFilename = getSuffixedPropertyFilename(propertiesFileName, "-internal"); final Properties internalConfiguration = getTenantConfiguration(internalPropertyFilename); if (internalConfiguration != null) { properties.putAll(internalConfiguration); } // if -custom properties also exists, merge key/value pairs (and overwrite previous values if same key name): final String customPropertyFilename = getSuffixedPropertyFilename(propertiesFileName, "-custom"); final Properties customConfiguration = getTenantConfiguration(customPropertyFilename); if (customConfiguration != null) { properties.putAll(customConfiguration); } } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("File {} not found. Returning empty properties object.", propertiesFileName); } } return properties; } public Properties getTenantProperties(String propertiesFileName) { return getAlsoCustomAndInternalPropertiesFromFilename(propertiesFileName); } /** * Parses the content as a Properties object. * If content is null, return empty properties. */ public static Properties getProperties(byte[] content) { Properties properties = new Properties(); if (content != null) { try (ByteArrayInputStream inputStream = new ByteArrayInputStream(content)) { properties.load(inputStream); } catch (IOException ioe) { LOGGER.error("Cannot parse properties file content", ioe); } } return properties; } public void setPlatformConfigurations(Map configurationFiles) throws IOException { platformConfigurations = new HashMap<>(configurationFiles.size()); for (Map.Entry entry : configurationFiles.entrySet()) { if (entry.getKey().endsWith(".properties")) { platformConfigurations.put(entry.getKey(), getProperties(entry.getValue())); } else { File file = new File(WebBonitaConstantsUtils.getPlatformInstance().getTempFolder(), entry.getKey()); FileUtils.writeByteArrayToFile(file, entry.getValue()); platformConfigurationFiles.put(entry.getKey(), file); } } } public synchronized void setTenantConfigurationFiles(Map configurationFiles) throws IOException { for (Map.Entry entry : configurationFiles.entrySet()) { if (!entry.getKey().endsWith(".properties")) { File file = new File(WebBonitaConstantsUtils.getTenantInstance().getTempFolder(), entry.getKey()); FileUtils.writeByteArrayToFile(file, entry.getValue()); tenantConfigurationFiles.put(entry.getKey(), file); } tenantConfigurations.put(entry.getKey(), ConfigurationFilesManager.getProperties(entry.getValue())); } } private String getSuffixedPropertyFilename(String propertiesFilename, String suffix) { return propertiesFilename.replaceAll("\\.properties$", suffix + ".properties"); } PlatformManagementUtils getPlatformManagementUtils() { return new PlatformManagementUtils(); } public File getTenantConfigurationFile(String fileName) { if (tenantConfigurationFiles.isEmpty()) { try { setTenantConfigurationFiles(getPlatformManagementUtils().readTenantConfigurationsFromEngine()); } catch (IOException e) { LOGGER.error("Cannot retrieve tenant configuration files", e); throw new RuntimeException(e); } } return tenantConfigurationFiles.get(fileName); } Properties getTenantConfiguration(String propertiesFilename) { if (tenantConfigurations.isEmpty()) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Tenant configuration file {} not yet in cache. Adding it.", propertiesFilename); } try { setTenantConfigurationFiles(getPlatformManagementUtils().readTenantConfigurationsFromEngine()); } catch (IOException e) { LOGGER.error("Cannot retrieve tenant configuration", e); throw new RuntimeException(e); } } else if (LOGGER.isDebugEnabled()) { LOGGER.debug("Retrieving tenant configuration file {} from cache.", propertiesFilename); } return tenantConfigurations.get(propertiesFilename); } public File getPlatformConfigurationFile(String fileName) { return platformConfigurationFiles.get(fileName); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/properties/ConsoleProperties.java ================================================ /** * Copyright (C) 2022 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.console.common.server.preferences.properties; import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; /** * @author Yang zhiheng */ public class ConsoleProperties { /** * Document max size */ private static final String ATTACHMENT_MAX_SIZE = "form.attachment.max.size"; /** * Image upload max size */ private static final String IMAGE_UPLOAD_MAX_SIZE = "image.upload.max.size"; /** * Custom page and rest api ext debug mode */ private static final String CUSTOM_PAGE_DEBUG = "custom.page.debug"; /** * time between two database check of custom page and rest api last update date in milliseconds */ private static final String PAGE_LAST_UPDATE_CHECK_INTERVAL_MILLIS = "custom.page.lastupdate.database.check.interval.milliseconds"; //Default time between two database check of custom page and rest api last update date in milliseconds private static final int DEFAULT_PAGE_LAST_UPDATE_CHECK_INTERVAL_MILLIS = 3000; /** Name of attribute attached to the request to indicate the request id */ private static final String REQUEST_ID_ATTRIBUTE_NAME = "req.requestId.attributeName"; private static final String DEFAULT_REQUEST_ID_ATTRIBUTE_NAME = "track.requestId"; /** Name of request header containing the request id when not already attached */ private static final String REQUEST_ID_HEADER_NAME = "req.requestId.headerName"; private static final String DEFAULT_REQUEST_ID_HEADER_NAME = "X-Request-ID"; /** Name of attribute attached to the request to indicate the correlation id */ private static final String CORRELATION_ID_ATTRIBUTE_NAME = "req.correlationId.attributeName"; private static final String DEFAULT_CORRELATION_ID_ATTRIBUTE_NAME = "track.correlationId"; /** Name of request header containing the correlation id when not already attached */ private static final String CORRELATION_ID_HEADER_NAME = "req.correlationId.headerName"; private static final String DEFAULT_CORRELATION_ID_HEADER_NAME = "X-Correlation-ID"; private static final String PROPERTIES_FILE = "console-config.properties"; private static Map> consoleProperties; public Properties getProperties() { return ConfigurationFilesManager.getInstance().getTenantProperties(PROPERTIES_FILE); } public long getMaxSize() { final String maxSize = this.getProperty(ATTACHMENT_MAX_SIZE); if (maxSize != null) { return Long.valueOf(maxSize); } return 15; } public long getImageMaxSizeInKB() { final String maxSize = this.getProperty(IMAGE_UPLOAD_MAX_SIZE); if (maxSize != null) { return Long.valueOf(maxSize); } return 100; } public boolean isPageInDebugMode() { final String debugMode = this.getProperty(CUSTOM_PAGE_DEBUG); return Boolean.parseBoolean(debugMode); } public long getPageLastUpdateCheckInterval() { final String pageLastUpdateCheckInterval = this.getProperty(PAGE_LAST_UPDATE_CHECK_INTERVAL_MILLIS); if (pageLastUpdateCheckInterval != null) { return Long.valueOf(pageLastUpdateCheckInterval); } return DEFAULT_PAGE_LAST_UPDATE_CHECK_INTERVAL_MILLIS; } public String getRequestIdAttributeName() { return Optional.ofNullable(this.getProperty(REQUEST_ID_ATTRIBUTE_NAME)) .orElse(DEFAULT_REQUEST_ID_ATTRIBUTE_NAME); } public String getRequestIdHeaderName() { return Optional.ofNullable(this.getProperty(REQUEST_ID_HEADER_NAME)).orElse(DEFAULT_REQUEST_ID_HEADER_NAME); } public String getCorrelationIdAttributeName() { return Optional.ofNullable(this.getProperty(CORRELATION_ID_ATTRIBUTE_NAME)) .orElse(DEFAULT_CORRELATION_ID_ATTRIBUTE_NAME); } public String getCorrelationIdHeaderName() { return Optional.ofNullable(this.getProperty(CORRELATION_ID_HEADER_NAME)) .orElse(DEFAULT_CORRELATION_ID_HEADER_NAME); } public String getProperty(String propertyName) { if (consoleProperties == null) { consoleProperties = new ConcurrentHashMap<>(); } Optional propertyValue = consoleProperties.get(propertyName); if (propertyValue == null) { propertyValue = Optional.ofNullable(getProperties().getProperty(propertyName)); consoleProperties.put(propertyName, propertyValue); } return propertyValue.orElse(null); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/properties/PropertiesFactory.java ================================================ /** * Copyright (C) 2022 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.console.common.server.preferences.properties; /** * @author Anthony Birembaut */ public class PropertiesFactory { public static SecurityProperties getSecurityProperties() { return new SecurityProperties(); } public static ConsoleProperties getConsoleProperties() { return new ConsoleProperties(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/properties/PropertiesWithSet.java ================================================ /** * Copyright (C) 2016 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.console.common.server.preferences.properties; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Collections; import java.util.HashSet; import java.util.Properties; import java.util.Set; /** * @author Baptiste Mesta */ public class PropertiesWithSet extends Properties { public PropertiesWithSet(Properties properties) { super(properties); } public PropertiesWithSet(File file) { try (FileInputStream inStream = new FileInputStream(file)) { this.load(inStream); } catch (IOException e) { throw new IllegalStateException(e); } } public Set getPropertyAsSet(final String propertyName) { return stringToSet(getProperty(propertyName)); } public static Set stringToSet(final String propertyValueAsString) { if (propertyValueAsString != null) { final Set propertiesSet = new HashSet<>(); final String propertyValueAsStringTrimmed = propertyValueAsString.trim(); if (propertyValueAsStringTrimmed.startsWith("[") && propertyValueAsStringTrimmed.endsWith("]")) { String propertyCSV = propertyValueAsStringTrimmed.substring(1, propertyValueAsStringTrimmed.length() - 1); propertyCSV = propertyCSV.trim(); if (propertyCSV.isEmpty()) { return Collections.emptySet(); } final String[] propertyArray = propertyCSV.split(","); for (final String propertyValue : propertyArray) { propertiesSet.add(propertyValue.trim()); } } else { propertiesSet.add(propertyValueAsString); } return propertiesSet; } else { return Collections.emptySet(); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/preferences/properties/SecurityProperties.java ================================================ /** * Copyright (C) 2022 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.console.common.server.preferences.properties; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * Utility class for security properties access * * @author Anthony Birembaut */ public class SecurityProperties { /** * Default name of the form definition file */ public static final String SECURITY_DEFAULT_CONFIG_FILE_NAME = "security-config.properties"; /** * property for the robustness of the password */ public static final String PASSWORD_VALIDATOR_CLASSNAME = "security.password.validator"; /** * property for the CSRF protection activation */ public static final String CSRF_PROTECTION = "security.csrf.enabled"; /** * Property for the (OWASP) Sanitizer protection activation. * This sanitizer protects against multiple attacks such as XSS, but may restrict the use of some character * sequences. */ public static final String SANITIZER_PROTECTION = "security.sanitizer.enabled"; /** * Property for the (OWASP) Sanitizer protection. * The value of this property lists the json attributes that should be excluded when sanitizer protection is active. */ public static final String SANITIZER_PROTECTION_EXCLUSIONS = "security.sanitizer.exclude"; /** * default list of attributes excluded from sanitizer protection when the property is not set in * security-config.properties */ public static final List DEFAULT_SANITIZER_PROTECTION_EXCLUSIONS = List.of("email", "password", "password_confirm"); /** * property for the CSRF token cookie to have the secure flag (HTTPS only) */ public static final String SECURE_TOKEN_COOKIE = "security.csrf.cookie.secure"; /** * property allowing to set the X-Frame-Options header value in the response */ public static final String X_FRAME_OPTIONS_HEADER = "bonita.runtime.security.csrf.header.frame.options"; /** * property allowing to set the Content-Security-Policy header value in the response */ public static final String CONTENT_SECURITY_POLICY_HEADER = "bonita.runtime.security.csrf.header.content.security.policy"; /** * property for the REST API Authorization checks activation */ public static final String API_AUTHORIZATIONS_CHECK = "security.rest.api.authorizations.check.enabled"; private static final Map> securityProperties = new ConcurrentHashMap<>(); /** * @return the password validator property */ public String getPasswordValidator() { return getTenantProperty(PASSWORD_VALIDATOR_CLASSNAME); } /** * @return the value to allow or not API authorization checks */ public boolean isAPIAuthorizationsCheckEnabled() { final String res = getTenantProperty(API_AUTHORIZATIONS_CHECK); return res != null && res.equals("true"); } /** * @return the value to allow or not CSRF protection */ public boolean isCSRFProtectionEnabled() { final String res = getPlatformProperty(CSRF_PROTECTION); return res != null && res.equals("true"); } /** * @return the value to allow or not Sanitizer activation for protection */ public boolean isSanitizerProtectionEnabled() { final String res = getPlatformProperty(SANITIZER_PROTECTION); // keep true as default when not set correctly (empty string, null, or any non-"false" value) return !"false".equalsIgnoreCase(res); } /** * @return the attributes to exclude from Sanitizer protection, comma separated */ public List getAttributeExcludedFromSanitizerProtection() { String excludedAttributes = getPlatformProperty(SANITIZER_PROTECTION_EXCLUSIONS); if (excludedAttributes == null) { return DEFAULT_SANITIZER_PROTECTION_EXCLUSIONS; } else if (excludedAttributes.isBlank()) { return Collections.emptyList(); } return Arrays.asList(excludedAttributes.trim().split("\\s*,\\s*")); } /** * @return the value to add or not secure flag to the cookies for CSRF token */ public boolean isCSRFTokenCookieSecure() { final String res = getPlatformProperty(SECURE_TOKEN_COOKIE); return res != null && res.equals("true"); } public String getXFrameOptionsHeader() { return getPlatformProperty(X_FRAME_OPTIONS_HEADER); } public String getContentSecurityPolicyHeader() { return getPlatformProperty(CONTENT_SECURITY_POLICY_HEADER); } protected ConfigurationFilesManager getConfigurationFilesManager() { return ConfigurationFilesManager.getInstance(); } public String getTenantProperty(String propertyName) { Properties tenantProperties = getConfigurationFilesManager() .getTenantProperties(SECURITY_DEFAULT_CONFIG_FILE_NAME); Optional propertyValue = securityProperties.get(propertyName); if (propertyValue == null) { propertyValue = Optional.ofNullable(tenantProperties.getProperty(propertyName)); securityProperties.put(propertyName, propertyValue); } return propertyValue.orElse(null); } public String getPlatformProperty(String propertyName) { Properties platformProperties = getConfigurationFilesManager() .getPlatformProperties(SECURITY_DEFAULT_CONFIG_FILE_NAME); Optional propertyValue = securityProperties.get(propertyName); if (propertyValue == null) { propertyValue = Optional.ofNullable(platformProperties.getProperty(propertyName)); securityProperties.put(propertyName, propertyValue); } return propertyValue.orElse(null); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/ApplicationIconServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.servlet; import java.util.Optional; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.APIClient; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.business.application.Application; import org.bonitasoft.engine.business.application.ApplicationNotFoundException; import org.bonitasoft.engine.business.application.ApplicationUpdater; import org.bonitasoft.engine.business.application.Icon; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.common.exception.http.ServerException; import org.bonitasoft.web.toolkit.client.data.APIID; @Slf4j public class ApplicationIconServlet extends IconServlet { @Override protected Optional retrieveIcon(Long iconId, APISession apiSession) { ApplicationAPI applicationApi = getApplicationApi(apiSession); try { Icon icon = applicationApi.getIconOfApplication(iconId); if (icon != null) { return Optional.of(new IconContent(icon.getContent(), icon.getMimeType())); } else { return Optional.empty(); } } catch (NotFoundException e) { return Optional.empty(); } } /** * {@inheritDoc} * * @deprecated as of 9.0.0, Application icon should be deleted/updated at startup. */ @Override @Deprecated(since = "9.0.0") protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException { log.warn("DELETE request on Application Icon is deprecated! " + "An application icon should be deleted or updated at startup instead."); super.doDelete(request, response); } /** * {@inheritDoc} * * @deprecated as of 9.0.0, Application icon should be deleted/updated at startup. */ @Override @Deprecated(since = "9.0.0") protected void deleteIcon(Long entityId, APISession apiSession, HttpServletRequest request) throws ServerException { ApplicationAPI applicationApi = getApplicationApi(apiSession); ApplicationUpdater updater = new ApplicationUpdater(); updater.setIcon(null, null); try { applicationApi.updateApplication(entityId, updater); } catch (ApplicationNotFoundException e) { throw new APIItemNotFoundException(Application.class.getName(), APIID.makeAPIID(entityId)); } catch (UpdateException | AlreadyExistsException e) { throw new APIException(e); } } ApplicationAPI getApplicationApi(APISession apiSession) { return new APIClient(apiSession).getApplicationAPI(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/DocumentDownloadServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.servlet; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.FilenameUtils; import org.bonitasoft.console.common.server.utils.BPMEngineAPIUtil; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.document.ArchivedDocument; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.document.DocumentNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessResourceNotFoundException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.engine.session.APISession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Servlet allowing to download process instances attachments * * @author Anthony Birembaut */ public class DocumentDownloadServlet extends HttpServlet { /** * UID */ private static final long serialVersionUID = 5209516978177786895L; /** * process ID */ protected static final String PROCESS_ID_PARAM = "process"; /** * instance ID */ protected static final String INSTANCE_ID_PARAM = "instance"; /** * task ID */ protected static final String TASK_ID_PARAM = "task"; /** * document id of the document to download */ protected static final String DOCUMENT_ID_PARAM = "document"; /** * attachment : indicate the path of the process attachment */ protected static final String FILE_PATH_PARAM = "filePath"; /** * attachment : indicate the file name of the process attachment */ protected static final String FILE_NAME_PARAM = "fileName"; /** * resource : indicate the file name of the process resource */ protected static final String RESOURCE_FILE_NAME_PARAM = "resourceFileName"; /** * The engine API session param key name */ protected static final String API_SESSION_PARAM_KEY = "apiSession"; /** * content storage id of the document downloaded */ protected static final String CONTENT_STORAGE_ID_PARAM = "contentStorageId"; /** * Util class allowing to work with the BPM engine API */ protected final BPMEngineAPIUtil bpmEngineAPIUtil = new BPMEngineAPIUtil(); protected final BonitaHomeFolderAccessor bonitaHomeFolderAccessor = new BonitaHomeFolderAccessor(); /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(DocumentDownloadServlet.class.getName()); /** * {@inheritDoc} */ @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { final String filePath = request.getParameter(FILE_PATH_PARAM); String fileName = request.getParameter(FILE_NAME_PARAM); final String resourcePath = request.getParameter(RESOURCE_FILE_NAME_PARAM); final String documentId = request.getParameter(DOCUMENT_ID_PARAM); String contentStorageId = request.getParameter(CONTENT_STORAGE_ID_PARAM); final APISession apiSession = (APISession) request.getSession().getAttribute(API_SESSION_PARAM_KEY); byte[] content = null; if (filePath != null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("attachmentPath: " + filePath); } try { final FileContent fileContent = bonitaHomeFolderAccessor .retrieveUploadedTempContent(FilenameUtils.separatorsToSystem(filePath)); if (fileName == null) { fileName = fileContent.getFileName(); } try (InputStream inputStream = fileContent.getInputStream()) { content = getFileContent(inputStream, filePath, fileContent.getSize()); } } catch (final BonitaException e) { throw new ServletException(e.getMessage()); } catch (final IOException e) { throw new ServletException(e); } } else if (fileName != null && contentStorageId != null) { try { content = bpmEngineAPIUtil.getProcessAPI(apiSession).getDocumentContent(contentStorageId); } catch (final Exception e) { final String errorMessage = "Error while retrieving the document with content storage ID " + contentStorageId + " from the engine."; if (LOGGER.isErrorEnabled()) { LOGGER.error(errorMessage, e); } throw new ServletException(errorMessage, e); } } else if (documentId != null) { try { final ProcessAPI processAPI = bpmEngineAPIUtil.getProcessAPI(apiSession); try { final Document document = processAPI.getDocument(Long.valueOf(documentId)); fileName = document.getContentFileName(); contentStorageId = document.getContentStorageId(); } catch (final DocumentNotFoundException dnfe) { final ArchivedDocument archivedDocument = processAPI .getArchivedVersionOfProcessDocument(Long.valueOf(documentId)); fileName = archivedDocument.getContentFileName(); contentStorageId = archivedDocument.getContentStorageId(); } if (contentStorageId != null && !contentStorageId.isEmpty()) { content = processAPI.getDocumentContent(contentStorageId); } } catch (final Exception e) { final String errorMessage = "Error while retrieving the document with ID " + documentId + " from the engine."; if (LOGGER.isErrorEnabled()) { LOGGER.error(errorMessage, e); } throw new ServletException(errorMessage, e); } } else if (resourcePath != null) { final String processIDStr = request.getParameter(PROCESS_ID_PARAM); final String instanceIDStr = request.getParameter(INSTANCE_ID_PARAM); final String taskIdStr = request.getParameter(TASK_ID_PARAM); long processDefinitionID = -1; try { if (processIDStr != null) { processDefinitionID = Long.parseLong(processIDStr); } else if (taskIdStr != null) { processDefinitionID = getProcessDefinitionIDFromActivityInstanceID(apiSession, Long.parseLong(taskIdStr)); } else if (instanceIDStr != null) { processDefinitionID = getProcessDefinitionIDFromProcessInstanceID(apiSession, Long.parseLong(instanceIDStr)); } else { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Either a process, instance or task parameter is required in the URL"); return; } final ProcessAPI processAPI = bpmEngineAPIUtil.getProcessAPI(apiSession); content = processAPI.getDocumentProcessResource(processDefinitionID, resourcePath); fileName = resourcePath.contains("/") ? resourcePath.substring(resourcePath.lastIndexOf('/') + 1) : resourcePath; } catch (final ProcessResourceNotFoundException e) { if (LOGGER.isWarnEnabled()) { LOGGER.warn(e.getMessage()); } response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } catch (final Exception e) { final String errorMessage = "Error while retrieving the resource " + resourcePath; if (LOGGER.isErrorEnabled()) { LOGGER.error(errorMessage, e); } throw new ServletException(errorMessage, e); } } else { final String errorMessage = "Error while getting the file. either a document, a filePath or a resourcePath parameter is required."; if (LOGGER.isErrorEnabled()) { LOGGER.error(errorMessage); } throw new ServletException(errorMessage); } response.setContentType("application/octet-stream"); response.setCharacterEncoding(StandardCharsets.UTF_8.name()); try { final String encodedfileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8); final String userAgent = request.getHeader("User-Agent"); if (userAgent != null && userAgent.contains("Firefox")) { response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedfileName.replace("+", "%20")); } else { response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedfileName.replaceAll("\\+", " ") + "\"; filename*=UTF-8''" + encodedfileName.replace("+", "%20")); } final OutputStream out = response.getOutputStream(); if (content == null) { response.setContentLength(0); } else { response.setContentLength(content.length); out.write(content); } } catch (final IOException e) { if (LOGGER.isErrorEnabled()) { LOGGER.error("Error while generating the response.", e); } throw new ServletException(e); } } protected byte[] getFileContent(final InputStream inputStream, final String filePath, final long size) throws ServletException, IOException { int fileLength = 0; if (size > Integer.MAX_VALUE) { throw new ServletException("file " + filePath + " too big !"); } else { fileLength = (int) size; } byte[] content; try { final byte[] fileContent = new byte[fileLength]; try { int offset = 0; int length = fileLength; while (length > 0) { final int read = inputStream.read(fileContent, offset, length); if (read <= 0) { break; } length -= read; offset += read; } content = fileContent; } catch (final FileNotFoundException e) { final String errorMessage = "Error while getting the attachment. The file " + filePath + " does not exist."; if (LOGGER.isErrorEnabled()) { LOGGER.error(errorMessage, e); } throw new ServletException(errorMessage, e); } finally { inputStream.close(); } } catch (final IOException e) { final String errorMessage = "Error while reading attachment (file : " + filePath; if (LOGGER.isErrorEnabled()) { LOGGER.error(errorMessage, e); } throw new ServletException(errorMessage, e); } return content; } protected long getProcessDefinitionIDFromActivityInstanceID(final APISession session, final long activityInstanceID) throws BonitaException { final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(session); return processAPI.getProcessDefinitionIdFromActivityInstanceId(activityInstanceID); } protected long getProcessDefinitionIDFromProcessInstanceID(final APISession session, final long processInstanceID) throws BonitaException { final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(session); return processAPI.getProcessDefinitionIdFromProcessInstanceId(processInstanceID); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/DocumentImageServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.servlet; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URLConnection; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.FilenameUtils; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.document.ArchivedDocument; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.document.DocumentNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessResourceNotFoundException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.engine.session.APISession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Servlet allowing to view process instances attachments as images * TODO refactor to remove duplicate code with {@link DocumentDownloadServlet} * * @author Anthony Birembaut */ public class DocumentImageServlet extends DocumentDownloadServlet { /** * UID */ private static final long serialVersionUID = -2397573068771431608L; /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(DocumentImageServlet.class.getName()); /** * {@inheritDoc} */ @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { final String filePath = request.getParameter(FILE_PATH_PARAM); String fileName = request.getParameter(FILE_NAME_PARAM); final String resourcePath = request.getParameter(RESOURCE_FILE_NAME_PARAM); final String documentId = request.getParameter(DOCUMENT_ID_PARAM); final APISession apiSession = (APISession) request.getSession().getAttribute(API_SESSION_PARAM_KEY); byte[] content = null; if (filePath != null) { try { final FileContent fileContent = bonitaHomeFolderAccessor .retrieveUploadedTempContent(FilenameUtils.separatorsToSystem(filePath)); if (fileName == null) { fileName = fileContent.getFileName(); } try (InputStream inputStream = fileContent.getInputStream()) { content = getFileContent(inputStream, filePath, fileContent.getSize()); } } catch (final BonitaException e) { throw new ServletException(e.getMessage()); } catch (final IOException e) { throw new ServletException(e); } } else if (documentId != null) { try { final ProcessAPI processAPI = bpmEngineAPIUtil.getProcessAPI(apiSession); String contentStorageId; try { final Document document = processAPI.getDocument(Long.valueOf(documentId)); fileName = document.getContentFileName(); contentStorageId = document.getContentStorageId(); } catch (final DocumentNotFoundException dnfe) { final ArchivedDocument archivedDocument = processAPI .getArchivedVersionOfProcessDocument(Long.valueOf(documentId)); fileName = archivedDocument.getContentFileName(); contentStorageId = archivedDocument.getContentStorageId(); } if (contentStorageId != null && !contentStorageId.isEmpty()) { content = processAPI.getDocumentContent(contentStorageId); } } catch (final Exception e) { final String errorMessage = "Error while retrieving the document with ID " + documentId + " from the engine."; if (LOGGER.isErrorEnabled()) { LOGGER.error(errorMessage, e); } throw new ServletException(errorMessage, e); } } else if (resourcePath != null) { final String processIDStr = request.getParameter(PROCESS_ID_PARAM); final String instanceIDStr = request.getParameter(INSTANCE_ID_PARAM); final String taskIdStr = request.getParameter(TASK_ID_PARAM); long processDefinitionID = -1; try { if (processIDStr != null) { processDefinitionID = Long.parseLong(processIDStr); } else if (taskIdStr != null) { processDefinitionID = getProcessDefinitionIDFromActivityInstanceID(apiSession, Long.parseLong(taskIdStr)); } else if (instanceIDStr != null) { processDefinitionID = getProcessDefinitionIDFromProcessInstanceID(apiSession, Long.parseLong(instanceIDStr)); } else { response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Either a process, instance or task parameter is required in the URL"); return; } final ProcessAPI processAPI = bpmEngineAPIUtil.getProcessAPI(apiSession); content = processAPI.getDocumentProcessResource(processDefinitionID, resourcePath); fileName = resourcePath.contains("/") ? resourcePath.substring(resourcePath.lastIndexOf('/') + 1) : resourcePath; } catch (final ProcessResourceNotFoundException e) { if (LOGGER.isWarnEnabled()) { LOGGER.warn(e.getMessage()); } response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } catch (final Exception e) { final String errorMessage = "Error while retrieving the resource " + resourcePath; if (LOGGER.isErrorEnabled()) { LOGGER.error(errorMessage, e); } throw new ServletException(errorMessage, e); } } else { final String errorMessage = "Error while getting the file. either a document, a filePath or a resourcePath parameter is required."; if (LOGGER.isErrorEnabled()) { LOGGER.error(errorMessage); } throw new ServletException(errorMessage); } final String contentType = URLConnection.guessContentTypeFromName(fileName); if (contentType != null) { response.setContentType(contentType); } response.setCharacterEncoding(StandardCharsets.UTF_8.name()); if (fileName != null) { try { final String encodedfileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replaceAll("\\+", "%20"); final String userAgent = request.getHeader("User-Agent"); if (userAgent != null && userAgent.contains("Firefox")) { response.setHeader("Content-Disposition", "inline; filename*=UTF-8''" + encodedfileName); } else { response.setHeader("Content-Disposition", "inline; filename=\"" + encodedfileName + "\"; filename*=UTF-8''" + encodedfileName); } if (content != null) { response.setContentLength(content.length); OutputStream out = response.getOutputStream(); out.write(content); } } catch (final IOException e) { if (LOGGER.isErrorEnabled()) { LOGGER.error("Error while generating the response.", e); } throw new ServletException(e.getMessage(), e); } } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/ErrorPageServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.servlet; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.regex.Pattern; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.console.common.server.utils.PlatformManagementUtils; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.session.APISession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ErrorPageServlet extends HttpServlet { /** * UID */ private static final long serialVersionUID = -6981838056314293935L; /** * Path of the error page template */ protected static final String ERROR_TEMPLATE_PATH = "/WEB-INF/errors.html"; /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(ErrorPageServlet.class.getName()); private static final Pattern HTTP_STATUS_CODE = Pattern.compile("\\d{3}"); /** * Static variable to avoid reading the error HTML page every time the error page is displayed */ private static String errorPageString = null; /** * {@inheritDoc} * * @throws IOException */ @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { String pathInfo = request.getPathInfo(); response.setContentType("text/html"); response.setCharacterEncoding(StandardCharsets.UTF_8.name()); try (PrintWriter output = response.getWriter()) { if (!StringUtils.isEmpty(pathInfo)) { String errorCode = pathInfo.substring(1); // Defense-in-depth: only accept 3-digit HTTP status codes. // errorCode comes from user-controlled pathInfo and is concatenated // into a getRequestDispatcher() path — without validation, a crafted // pathInfo (e.g. "/../WEB-INF/web.xml%00") could reach arbitrary JSPs. if (!HTTP_STATUS_CODE.matcher(errorCode).matches()) { response.sendError(HttpServletResponse.SC_BAD_REQUEST); return; } if (LOGGER.isInfoEnabled()) { LOGGER.info("Displaying error page with code " + errorCode); } final APISession apiSession = (APISession) request.getSession() .getAttribute(SessionUtil.API_SESSION_PARAM_KEY); if (apiSession != null && isPlatformHealthy()) { String contextPath = request.getContextPath(); if (contextPath.equals("/")) { //avoid double / in URL if Bonita is deployed at the root contextPath = ""; } if (errorPageString == null) { ServletContext sc = getServletContext(); try (InputStream errorPageInputStream = sc.getResourceAsStream(ERROR_TEMPLATE_PATH)) { errorPageString = new String(errorPageInputStream.readAllBytes(), StandardCharsets.UTF_8); } catch (Exception e) { if (LOGGER.isErrorEnabled()) { LOGGER.error("Error while trying to get the error page.", e); } output.println("An Error occurred."); } } writeFormatedResponse(output, errorCode, contextPath); } else { //if there is no session or if the platform is not available, fallback on generic error pages getServletContext().getRequestDispatcher("/" + errorCode + ".jsp").forward(request, response); } } else { if (LOGGER.isWarnEnabled()) { LOGGER.warn("Status code missing from request."); } output.println("Status code missing from request."); } output.flush(); } } protected boolean isPlatformHealthy() { try { PlatformManagementUtils platformManagementUtils = new PlatformManagementUtils(); return platformManagementUtils.isPlatformAvailable(); } catch (Exception e) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Platform is not healthy."); } return false; } } protected void writeFormatedResponse(PrintWriter output, String errorCode, String contextPath) throws IOException { try { output.format(errorPageString, errorCode, contextPath, errorCode); } catch (Exception e) { if (LOGGER.isErrorEnabled()) { LOGGER.error("Error while trying to display the error page.", e); } output.println("An Error occurred."); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/FileUploadServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.servlet; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.Serializable; import java.net.HttpURLConnection; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException; import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.bonitasoft.console.common.server.filter.MultiReadHttpServletRequest; import org.bonitasoft.console.common.server.utils.DocumentUtil; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.api.TemporaryContentAPI; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.engine.session.SessionNotFoundException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Servlet allowing to upload a File. * * @author Julien Mege */ public abstract class FileUploadServlet extends HttpServlet { /** * UID */ protected static final long serialVersionUID = -948661031179067420L; protected static final Logger LOGGER = LoggerFactory.getLogger(FileUploadServlet.class.getName()); protected String uploadDirectoryPath = null; public static final String RESPONSE_SEPARATOR = "::"; protected static final String SUPPORTED_EXTENSIONS_PARAM = "SupportedExtensions"; protected static final String SUPPORTED_EXTENSIONS_SEPARATOR = ","; protected static final String RETURN_ORIGINAL_FILENAME_PARAM = "ReturnOriginalFilename"; protected static final String CHECK_UPLOADED_FILE_SIZE = "CheckUploadedFileSize"; protected static final String CHECK_UPLOADED_IMAGE_SIZE = "CheckUploadedImageSize"; protected static final String RESPONSE_CONTENT_TYPE_PARAM = "ContentType"; protected static final String TEXT_CONTENT_TYPE = "text"; protected static final String JSON_CONTENT_TYPE = "json"; protected static final String TEMP_PATH_RESPONSE_ATTRIBUTE = "tempPath"; protected static final String FILE_NAME_RESPONSE_ATTRIBUTE = "filename"; protected static final String CONTENT_TYPE_ATTRIBUTE = "contentType"; protected static final String JARLESS_BAR_ATTRIBUTE = "jarlessBar"; public static final int MEGABYTE = 1048576; public static final int KILOBYTE = 1024; protected String[] supportedExtensionsList = new String[0]; protected boolean alsoReturnOriginalFilename = false; protected boolean checkUploadedFileSize = false; protected boolean checkUploadedImageSize = false; protected String responseContentType = TEXT_CONTENT_TYPE; private ObjectMapper objectMapper = new ObjectMapper(); protected TemporaryContentAPI temporaryContentAPI; @Override public void init() throws ServletException { final String supportedExtensionsParam = getInitParameter(SUPPORTED_EXTENSIONS_PARAM); if (supportedExtensionsParam != null) { supportedExtensionsList = supportedExtensionsParam.split(SUPPORTED_EXTENSIONS_SEPARATOR); } alsoReturnOriginalFilename = Boolean.parseBoolean(getInitParameter(RETURN_ORIGINAL_FILENAME_PARAM)); final String responseContentTypeParam = getInitParameter(RESPONSE_CONTENT_TYPE_PARAM); if (responseContentTypeParam != null) { responseContentType = responseContentTypeParam; } checkUploadedFileSize = Boolean.parseBoolean(getInitParameter(CHECK_UPLOADED_FILE_SIZE)); checkUploadedImageSize = Boolean.parseBoolean(getInitParameter(CHECK_UPLOADED_IMAGE_SIZE)); temporaryContentAPI = getTemporaryContentAPI(); } protected TemporaryContentAPI getTemporaryContentAPI() { try { return PlatformAPIAccessor.getTemporaryContentAPI(); } catch (Exception e) { throw new RuntimeException(e); } } protected abstract void defineUploadDirectoryPath(final HttpServletRequest request) throws SessionNotFoundException; protected abstract void setUploadMaxSize(ServletFileUpload serviceFileUpload, final HttpServletRequest request); protected void setUploadDirectoryPath(final String uploadDirectoryPath) { this.uploadDirectoryPath = uploadDirectoryPath; } /** * Test whether file is a jar less bar (only when check is relevant). * * @param fileName the file name * @param item the file item * @return true when file is a bar file without jar dependency files and check is relevant, false otherwise */ private boolean isJarLessBar(String fileName, FileItem item) { if (Arrays.equals(supportedExtensionsList, new String[] { "bar" }) && fileName.endsWith(".bar")) { ZipEntry zipEntry = null; try (ZipInputStream zipInputstream = new ZipInputStream(item.getInputStream());) { while ((zipEntry = zipInputstream.getNextEntry()) != null) { if (".jarless".equals(zipEntry.getName())) { return true; } } } catch (IOException e) { LOGGER.error("Error while checking if file is a jar less bar", e); } } return false; } @Override public void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/plain;charset=UTF-8"); PrintWriter responsePW = null; try { defineUploadDirectoryPath(request); if (!ServletFileUpload.isMultipartContent(request)) { return; } final File targetDirectory = new File(uploadDirectoryPath); if (!targetDirectory.exists()) { targetDirectory.mkdirs(); } responsePW = response.getWriter(); final FileItemFactory fileItemFactory = new DiskFileItemFactory(); final ServletFileUpload serviceFileUpload = createServletFileUpload(fileItemFactory); setUploadMaxSize(serviceFileUpload, request); List items; try { items = serviceFileUpload.parseRequest(request); } catch (final OutOfMemoryError e) { throw new SizeLimitExceededException("The file exceeds its maximum permitted size.", 0L, 0); } for (final FileItem item : items) { if (item.isFormField()) { continue; } final String fileName = DocumentUtil.sanitizeFilename(item.getName()); // Check if extension is allowed if (!isSupportedExtension(fileName)) { outputMediaTypeError(response, responsePW); return; } // Make unique file name final String uploadedFileKey = storeTempFile(fileName, item); //Clean multiread wrapper temp file if it exists if (request instanceof MultiReadHttpServletRequest) { ((MultiReadHttpServletRequest) request).cleanMultipartTempContent(); } // Response final String responseString; if (JSON_CONTENT_TYPE.equals(responseContentType)) { // just check whether it's a jarless bar to display a warning boolean isJarlessBar = isJarLessBar(fileName, item); responseString = generateResponseJson(request, fileName, item.getContentType(), uploadedFileKey, isJarlessBar); } else if (TEXT_CONTENT_TYPE.equals(responseContentType)) { responseString = generateResponseString(request, fileName, uploadedFileKey); } else { throw new ServletException( "Unsupported content type in servlet configuration : " + responseContentType); } responsePW.print(responseString); responsePW.flush(); break; } } catch (SessionNotFoundException e) { final String message = "Session expired"; LOGGER.debug(message); response.sendError(HttpServletResponse.SC_UNAUTHORIZED, message); } catch (final SizeLimitExceededException e) { LOGGER.error("File is Too Big", e); generateFileTooBigError(response, responsePW, "Uploaded file is too large, server is unable to process it"); } catch (final FileSizeLimitExceededException e) { LOGGER.error("File is Too Big", e); generateFileTooBigError(response, responsePW, e.getFileName() + " is " + e.getActualSize() + " large, limit is set to " + e.getPermittedSize() / FileUploadServlet.MEGABYTE + "Mb"); } catch (final Exception e) { final String theErrorMessage = "Exception while uploading file."; if (LOGGER.isErrorEnabled()) { LOGGER.error(theErrorMessage, e); } throw new ServletException(theErrorMessage, e); } finally { if (responsePW != null) { responsePW.close(); } } } protected String storeTempFile(final String fileName, final FileItem item) throws Exception { temporaryContentAPI = PlatformAPIAccessor.getTemporaryContentAPI(); return temporaryContentAPI .storeTempFile(new FileContent(fileName, item.getInputStream(), item.getContentType())); } private void generateFileTooBigError(final HttpServletResponse response, final PrintWriter responsePW, final String message) throws JsonProcessingException { response.setStatus(HttpURLConnection.HTTP_ENTITY_TOO_LARGE); if (JSON_CONTENT_TYPE.equals(responseContentType)) { final Map errorResponse = new HashMap<>(); errorResponse.put("type", "EntityTooLarge"); errorResponse.put("message", message); errorResponse.put("statusCode", HttpURLConnection.HTTP_ENTITY_TOO_LARGE); responsePW.print(objectMapper.writeValueAsString(errorResponse)); responsePW.flush(); } } //for test purpose protected ServletFileUpload createServletFileUpload(final FileItemFactory fileItemFactory) { return new ServletFileUpload(fileItemFactory); } protected String generateResponseString(final HttpServletRequest request, final String fileName, final String uploadedFileName) throws Exception { String responseString = uploadedFileName; if (alsoReturnOriginalFilename) { responseString = responseString + RESPONSE_SEPARATOR + getFilenameLastSegment(fileName); } return responseString; } protected String generateResponseJson(final HttpServletRequest request, final String fileName, String contentType, final String uploadedFileName, final boolean isJarlessBar) throws Exception { final Map responseMap = new HashMap<>(); fillJsonResponseMap(request, responseMap, fileName, contentType, uploadedFileName, isJarlessBar); return objectMapper.writeValueAsString(responseMap); } protected void fillJsonResponseMap(HttpServletRequest request, final Map responseMap, final String fileName, final String contentType, final String uploadedFileName, final boolean isJarlessBar) { if (alsoReturnOriginalFilename) { responseMap.put(FILE_NAME_RESPONSE_ATTRIBUTE, getFilenameLastSegment(fileName)); } responseMap.put(TEMP_PATH_RESPONSE_ATTRIBUTE, uploadedFileName); responseMap.put(CONTENT_TYPE_ATTRIBUTE, contentType); if (isJarlessBar) { responseMap.put(JARLESS_BAR_ATTRIBUTE, Boolean.TRUE); } } protected String getFilenameLastSegment(final String fileName) { int slashPos = fileName.lastIndexOf("/"); if (slashPos == -1) { slashPos = fileName.lastIndexOf("\\"); } return fileName.substring(slashPos + 1); } protected void outputMediaTypeError(final HttpServletResponse response, final PrintWriter responsePW) { response.setStatus(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); responsePW.print("Extension not supported."); responsePW.flush(); } protected boolean isSupportedExtension(final String fileName) { if (fileName == null) { return false; } // if no extension specified, all extensions are allowed if (supportedExtensionsList.length < 1) { return true; } for (final String extension : supportedExtensionsList) { if (fileName.toLowerCase().endsWith("." + extension.toLowerCase())) { return true; } } return false; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/IconContent.java ================================================ /** * Copyright (C) 2022 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.console.common.server.servlet; class IconContent { private final byte[] content; private final String mimeType; public IconContent(byte[] content, String mimeType) { this.content = content; this.mimeType = mimeType; } public byte[] getContent() { return content; } public String getMimeType() { return mimeType; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/IconServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.servlet; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Optional; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIMalformedUrlException; import org.bonitasoft.web.toolkit.client.common.exception.http.ServerException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class IconServlet extends HttpServlet { private static final Logger LOGGER = LoggerFactory.getLogger(IconServlet.class.getName()); @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException { String iconIdPath = request.getPathInfo(); if (iconIdPath == null || iconIdPath.isEmpty()) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } Long iconId = parseLong(iconIdPath); if (iconId == null) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } Optional iconContent = retrieveIcon(iconId, (APISession) request.getSession().getAttribute("apiSession")); if (!iconContent.isPresent()) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); return; } response.setContentType(iconContent.get().getMimeType()); response.setCharacterEncoding("UTF-8"); try { setHeaders(request, response, iconId); } catch (UnsupportedEncodingException e) { logAndThrowException(e, "Error while generating the headers."); } try { OutputStream out = response.getOutputStream(); response.setContentLength(iconContent.get().getContent().length); out.write(iconContent.get().getContent()); } catch (final IOException e) { logAndThrowException(e, "Error while generating the response."); } } @Override protected void doDelete(final HttpServletRequest request, final HttpServletResponse response) throws ServletException { String pathInfo = request.getPathInfo(); if (pathInfo == null || pathInfo.isEmpty()) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } Long entityId = parseLong(pathInfo); if (entityId == null) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } try { deleteIcon(entityId, (APISession) request.getSession().getAttribute("apiSession"), request); } catch (APIItemNotFoundException e) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); return; } catch (APIMalformedUrlException e) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } catch (ServerException e) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } try { setHeaders(request, response, entityId); } catch (UnsupportedEncodingException e) { logAndThrowException(e, "Error while generating the headers."); } response.setStatus(HttpServletResponse.SC_NO_CONTENT); } protected abstract Optional retrieveIcon(Long iconId, APISession apiSession); protected abstract void deleteIcon(Long entityId, APISession apiSession, HttpServletRequest request) throws ServerException; private Long parseLong(String iconIdPath) { try { return Long.valueOf(iconIdPath.substring(1)); } catch (NumberFormatException e) { return null; } } private void logAndThrowException(IOException e, String msg) throws ServletException { LOGGER.error(msg, e); throw new ServletException(e.getMessage(), e); } private void setHeaders(HttpServletRequest request, HttpServletResponse response, Long iconId) throws UnsupportedEncodingException { final String encodedFileName = URLEncoder.encode(String.valueOf(iconId), StandardCharsets.UTF_8); final String userAgent = request.getHeader("User-Agent"); if (userAgent != null && userAgent.contains("Firefox")) { response.setHeader("Content-Disposition", "inline; filename*=UTF-8''" + encodedFileName); } else { response.setHeader("Content-Disposition", "inline; filename=\"" + encodedFileName.replaceAll("\\+", " ") + "\"; filename*=UTF-8''" + encodedFileName); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/OrganizationIconServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.servlet; import java.util.Optional; import javax.servlet.http.HttpServletRequest; import org.bonitasoft.engine.api.APIClient; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.Icon; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserNotFoundException; import org.bonitasoft.engine.identity.UserUpdater; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIMalformedUrlException; import org.bonitasoft.web.toolkit.client.common.exception.http.ServerException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Anthony Birembaut * @author Baptiste Mesta */ public class OrganizationIconServlet extends IconServlet { @Override protected Optional retrieveIcon(Long iconId, APISession apiSession) { IdentityAPI identityAPI = getIdentityApi(apiSession); Icon icon; try { icon = identityAPI.getIcon(iconId); } catch (NotFoundException e) { return Optional.empty(); } return Optional.of(new IconContent(icon.getContent(), icon.getMimeType())); } @Override protected void deleteIcon(Long entityId, APISession apiSession, HttpServletRequest request) throws ServerException { String entityType = request.getParameter("type"); if (entityType == null || !entityType.equals("user")) { throw new APIMalformedUrlException(request.getRequestURL().toString(), "Cannot delete a non-user icon. Provide type=user as a query parameter."); } IdentityAPI identityAPI = getIdentityApi(apiSession); UserUpdater updater = new UserUpdater(); updater.setIcon(null, null); try { identityAPI.updateUser(entityId, updater); } catch (UserNotFoundException e) { throw new APIItemNotFoundException(User.class.getName(), APIID.makeAPIID(entityId)); } catch (UpdateException e) { throw new APIException(e); } } IdentityAPI getIdentityApi(APISession apiSession) { return new APIClient(apiSession).getIdentityAPI(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/PageUploadServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.servlet; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.Serializable; import java.util.Map; import java.util.Properties; import java.util.Set; import javax.activation.FileTypeMap; import javax.activation.MimetypesFileTypeMap; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.commons.fileupload.FileItem; import org.apache.commons.io.FileUtils; import org.bonitasoft.console.common.server.page.CustomPageService; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.InvalidPageTokenException; import org.bonitasoft.engine.exception.InvalidPageZipContentException; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.engine.session.APISession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @deprecated as of 9.0.0, Application page should be created/updated at startup. */ @Slf4j @Deprecated(since = "9.0.0") public class PageUploadServlet extends TenantFileUploadServlet { /** * UID */ private static final long serialVersionUID = -5733037726905793432L; protected static final String ACTION_PARAM_NAME = "action"; private static final String PROCESS_PARAM_NAME = "process"; protected static final String ADD_ACTION = "add"; protected static final Logger LOGGER = LoggerFactory.getLogger(PageUploadServlet.class.getName()); protected static final String PERMISSIONS_RESPONSE_ATTRIBUTE = "permissions"; protected File pageTmp; /** * {@inheritDoc} * * @deprecated as of 9.0.0, Application page should be created/updated at startup. */ @Override @Deprecated(since = "9.0.0") public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { log.warn("Application Page upload is deprecated! " + "An application page should be created or updated at startup instead."); super.doPost(request, response); } @Override protected String generateResponseString(final HttpServletRequest request, final String fileName, final String uploadedFileName) throws Exception { final String responseString = super.generateResponseString(request, fileName, uploadedFileName); String permissionString; try { final String[] permissions = getPermissions(request); permissionString = "[" + String.join(",", permissions) + "]"; } catch (final Exception e) { permissionString = getPermissionsError(e); } return responseString + RESPONSE_SEPARATOR + permissionString; } @Override protected void fillJsonResponseMap(final HttpServletRequest request, final Map responseMap, final String fileName, final String contentType, final String uploadedFileKey, final boolean isJarlessBar) { super.fillJsonResponseMap(request, responseMap, fileName, contentType, uploadedFileKey, isJarlessBar); // also add the permissions to the map try { final String[] permissions = getPermissions(request); responseMap.put(PERMISSIONS_RESPONSE_ATTRIBUTE, permissions); } catch (final Exception e) { responseMap.put(PERMISSIONS_RESPONSE_ATTRIBUTE, getPermissionsError(e)); } } @Override protected String storeTempFile(final String fileName, final FileItem item) throws Exception { pageTmp = File.createTempFile("tmp_page", ".tmp"); FileUtils.copyToFile(item.getInputStream(), pageTmp); pageTmp.deleteOnExit(); final FileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap(); String mimeType = mimetypesFileTypeMap.getContentType(pageTmp); return temporaryContentAPI.storeTempFile(new FileContent(fileName, new FileInputStream(pageTmp), mimeType)); } protected String[] getPermissions(final HttpServletRequest request) throws InvalidPageZipContentException, InvalidPageTokenException, AlreadyExistsException, BonitaException, IOException { final String action = request.getParameter(ACTION_PARAM_NAME); final boolean checkIfItAlreadyExists = ADD_ACTION.equals(action); final Set permissionsSet = getPagePermissions(request, checkIfItAlreadyExists); return permissionsSet != null ? permissionsSet.toArray(new String[permissionsSet.size()]) : new String[0]; } protected String getPermissionsError(final Exception e) { if (LOGGER.isWarnEnabled()) { LOGGER.warn(e.getMessage()); } return e.getClass().getSimpleName(); } protected Set getPagePermissions(final HttpServletRequest request, final boolean checkIfItAlreadyExists) throws BonitaException, IOException { final APISession apiSession = getAPISession(request); final Long processDefinitionId = getProcessDefinitionId(request); final CustomPageService customPageService = new CustomPageService(); final Properties properties = customPageService.getPageProperties(apiSession, FileUtils.readFileToByteArray(pageTmp), checkIfItAlreadyExists, processDefinitionId); Set customPagePermissions = customPageService.getCustomPagePermissions(properties, apiSession); pageTmp.delete(); return customPagePermissions; } private Long getProcessDefinitionId(final HttpServletRequest request) { final String processStr = request.getParameter(PROCESS_PARAM_NAME); Long processDefinitionId = null; if (processStr != null) { processDefinitionId = Long.parseLong(processStr); } return processDefinitionId; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/PlatformFileUploadServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.servlet; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; /** * Servlet allowing to upload a file in the platform common temp folder * * @author Anthony Birembaut */ public class PlatformFileUploadServlet extends FileUploadServlet { /** * UID */ private static final long serialVersionUID = 58370675776169565L; @Override protected void defineUploadDirectoryPath(final HttpServletRequest request) { setUploadDirectoryPath(WebBonitaConstantsUtils.getPlatformInstance().getTempFolder().getPath()); } @Override protected void setUploadMaxSize(final ServletFileUpload serviceFileUpload, final HttpServletRequest request) { //currently no limit check on the platform } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/PlatformTenantListener.java ================================================ /** * Copyright (C) 2022 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.console.common.server.servlet; import java.io.IOException; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.console.common.server.utils.PlatformManagementUtils; import org.bonitasoft.engine.exception.BonitaException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Zhiheng Yang, Anthony Birembaut */ public class PlatformTenantListener implements ServletContextListener { /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(PlatformTenantListener.class.getName()); @Override public void contextInitialized(final ServletContextEvent sce) { PlatformManagementUtils platformManagementUtils = new PlatformManagementUtils(); try { platformManagementUtils.initializePlatformConfiguration(); // Create temporary folder specific to portal at startup: WebBonitaConstantsUtils.getPlatformInstance().getTempFolder(); } catch (BonitaException e) { LOGGER.error( "Error initializing platform configuration. Engine most likely failed to start. Check previous error logs for more details."); } catch (IOException e) { if (LOGGER.isErrorEnabled()) { LOGGER.error("Error while retrieving configuration", e); } } } @Override public void contextDestroyed(final ServletContextEvent sce) { } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/ResourceLocationReader.java ================================================ /** * Copyright (C) 2022 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.console.common.server.servlet; import javax.servlet.http.HttpServletRequest; public class ResourceLocationReader { /** * file location */ public final static String LOCATION_PARAM = "location"; public String getResourceLocationFromRequest(final HttpServletRequest request) { String fileName = request.getParameter(LOCATION_PARAM); if (fileName == null) { final String pathInfo = request.getPathInfo(); if (pathInfo != null && pathInfo.startsWith("/") && pathInfo.length() > 1) { //Support relative calls to the THEME from the homepage using ../theme fileName = pathInfo.substring(1); } } return fileName; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/ResourceServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.servlet; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import javax.activation.FileTypeMap; import javax.activation.MimetypesFileTypeMap; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.FileUtils; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Anthony Birembaut */ public abstract class ResourceServlet extends HttpServlet { /** * UID */ private static final long serialVersionUID = -2103038794535737881L; /** * Logger */ private final static Logger LOGGER = LoggerFactory.getLogger(ResourceServlet.class.getName()); protected abstract String getResourceParameterName(); protected abstract String getDefaultResourceName(); protected abstract File getResourcesParentFolder(); /** * Return null if there is no subfolder */ protected abstract String getSubFolderName(); protected ResourceLocationReader resourceLocationReader = new ResourceLocationReader(); /** * {@inheritDoc} */ @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { final String fileName = resourceLocationReader.getResourceLocationFromRequest(request); String resourceName = request.getParameter(getResourceParameterName()); if (resourceName == null) { resourceName = getDefaultResourceName(); } try { getResourceFile(response, resourceName, fileName); } catch (final UnsupportedEncodingException e) { final String errorMessage = "UnsupportedEncodingException :" + e; if (LOGGER.isErrorEnabled()) { LOGGER.error(errorMessage); } throw new ServletException(errorMessage); } } /** * Get resource file. * * @throws ServletException * @throws UnsupportedEncodingException */ protected void getResourceFile(final HttpServletResponse response, String resourceName, String fileName) throws ServletException, IOException { if (resourceName == null) { final String errorMessage = "Error while using the servlet to get a resource: the parameter " + getResourceParameterName() + " is null."; if (LOGGER.isWarnEnabled()) { LOGGER.warn(errorMessage); } throw new ServletException(errorMessage); } if (fileName == null) { final String errorMessage = "Error while using the servlet to get a resource: the parameter " + ResourceLocationReader.LOCATION_PARAM + " is null."; if (LOGGER.isWarnEnabled()) { LOGGER.warn(errorMessage); } throw new ServletException(errorMessage); } resourceName = URLDecoder.decode(resourceName, "UTF-8"); fileName = URLDecoder.decode(fileName, "UTF-8"); response.setCharacterEncoding("UTF-8"); final File resourcesParentFolder = getResourcesFolder(); final String subFolderName = getSubFolderName(); String subFolderSuffix; if (subFolderName != null) { subFolderSuffix = File.separator + subFolderName; } else { subFolderSuffix = ""; } try { final File resourceFolder = new File(resourcesParentFolder, resourceName + subFolderSuffix); final File file = new File(resourceFolder, fileName); final BonitaHomeFolderAccessor tenantFolder = new BonitaHomeFolderAccessor(); if (!tenantFolder.isInFolder(resourceFolder, resourcesParentFolder)) { throw new ServletException("For security reasons, access to this file path is restricted."); } if (!tenantFolder.isInFolder(file, resourceFolder)) { throw new ServletException("For security reasons, access to this file path is restricted."); } byte[] content; String contentType; final String lowerCaseFileName = fileName.toLowerCase(); if (lowerCaseFileName.endsWith(".jpg")) { contentType = "image/jpeg"; } else if (lowerCaseFileName.endsWith(".jpeg")) { contentType = "image/jpeg"; } else if (lowerCaseFileName.endsWith(".gif")) { contentType = "image/gif"; } else if (lowerCaseFileName.endsWith(".png")) { contentType = "image/png"; } else if (lowerCaseFileName.endsWith(".css") || lowerCaseFileName.endsWith(".less")) { contentType = "text/css"; } else if (lowerCaseFileName.endsWith(".js")) { contentType = "application/x-javascript"; } else if (lowerCaseFileName.endsWith(".html")) { contentType = "text/html; charset=UTF-8"; } else if (lowerCaseFileName.endsWith(".htc")) { contentType = "text/x-component"; } else if (lowerCaseFileName.endsWith(".svg")) { contentType = "image/svg+xml"; } else if (lowerCaseFileName.endsWith(".eot")) { contentType = "application/vnd.ms-fontobject"; } else if (lowerCaseFileName.endsWith(".woff")) { contentType = "application/x-font-woff"; } else if (lowerCaseFileName.endsWith(".ttf")) { contentType = "application/x-font-ttf"; } else if (lowerCaseFileName.endsWith(".otf")) { contentType = "application/x-font-opentype"; } else { final FileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap(); contentType = mimetypesFileTypeMap.getContentType(file); } if (contentType == null) { contentType = "application/octet-stream"; } content = FileUtils.readFileToByteArray(file); response.setContentType(contentType); response.setContentLength(content.length); response.setBufferSize(content.length); final OutputStream out = response.getOutputStream(); out.write(content, 0, content.length); } catch (FileNotFoundException e) { if (LOGGER.isWarnEnabled()) { LOGGER.warn(e.getMessage()); } response.sendError(404); } catch (final IOException e) { if (LOGGER.isErrorEnabled()) { LOGGER.error("Error while generating the response.", e); } throw new ServletException(e.getMessage(), e); } } protected File getResourcesFolder() throws ServletException { try { return getResourcesParentFolder(); } catch (final RuntimeException e) { final String errorMessage = "Error while using the servlet to get parent folder."; if (LOGGER.isWarnEnabled()) { LOGGER.warn(errorMessage); } throw new ServletException(errorMessage); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/servlet/TenantFileUploadServlet.java ================================================ /** * Copyright (C) 2022 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.console.common.server.servlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.console.common.server.preferences.properties.ConsoleProperties; import org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory; import org.bonitasoft.engine.session.APISession; /** * Servlet allowing to upload a file in the tenant common temp folder * * @author Anthony Birembaut */ public class TenantFileUploadServlet extends FileUploadServlet { /** * UID */ private static final long serialVersionUID = 58370675886169565L; @Override protected void defineUploadDirectoryPath(final HttpServletRequest request) { setUploadDirectoryPath(WebBonitaConstantsUtils.getTenantInstance().getTempFolder().getPath()); } protected APISession getAPISession(final HttpServletRequest request) { final HttpSession session = request.getSession(); return (APISession) session.getAttribute("apiSession"); } @Override protected void setUploadMaxSize(final ServletFileUpload serviceFileUpload, final HttpServletRequest request) { if (checkUploadedImageSize) { serviceFileUpload.setFileSizeMax(getConsoleProperties().getImageMaxSizeInKB() * KILOBYTE); } else if (checkUploadedFileSize) { serviceFileUpload.setFileSizeMax(getConsoleProperties().getMaxSize() * MEGABYTE); } else { //Some db vendor do not support more than 2GB Blobs serviceFileUpload.setFileSizeMax(Integer.MAX_VALUE); } } protected ConsoleProperties getConsoleProperties() { return PropertiesFactory.getConsoleProperties(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/BPMEngineAPIUtil.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Util class to work with the BPM engine API * * @author Anthony Birembaut */ public class BPMEngineAPIUtil { /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(BPMEngineAPIUtil.class.getName()); /** * Get the engine process API * * @param session * API session * @return an instance of {@link ProcessAPI} */ public ProcessAPI getProcessAPI(final APISession session) throws BPMEngineException, InvalidSessionException { try { return TenantAPIAccessor.getProcessAPI(session); } catch (final BonitaHomeNotSetException e) { final String message = "Bonita home system variable is not defined"; if (LOGGER.isErrorEnabled()) { LOGGER.error(message, e); } throw new BPMEngineException(message); } catch (final UnknownAPITypeException e) { final String message = "The engine API Implementation is unknown."; if (LOGGER.isErrorEnabled()) { LOGGER.error(message, e); } throw new BPMEngineException(message); } catch (final ServerAPIException e) { final String message = "The engine client was not able to communicate with the engine server."; if (LOGGER.isErrorEnabled()) { LOGGER.error(message, e); } throw new BPMEngineException(message); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/BPMEngineException.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; /** * Technical exception thrown when the BPM engine report a API communication error. * * @author Ruiheng Fan, Anthony Birembaut */ public class BPMEngineException extends Exception { /** * UID */ private static final long serialVersionUID = -4812682510242412305L; /** * @param message * message associated with the exception */ public BPMEngineException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/BonitaHomeFolderAccessor.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.api.TemporaryContentAPI; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.engine.io.TemporaryFileNotFoundException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BonitaHomeFolderAccessor { /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(BonitaHomeFolderAccessor.class.getName()); public BonitaHomeFolderAccessor() { } /** * @param tempFileKey * @return * @throws IOException * @deprecated use {@link #retrieveUploadedTempContent(String)} instead to avoid creating additional temp file */ @Deprecated(since = "9.0.0") public File getTempFile(final String tempFileKey) throws IOException { try { FileContent fileContent = retrieveUploadedTempContent(tempFileKey); File file = makeUniqueFilename(fileContent.getFileName()); try (InputStream inputStream = fileContent.getInputStream()) { Files.copy(inputStream, file.toPath(), StandardCopyOption.REPLACE_EXISTING); } return file; } catch (BonitaException e) { throw new IOException(e); } } public FileContent retrieveUploadedTempContent(final String tempFileKey) throws BonitaException { TemporaryContentAPI temporaryContentAPI = PlatformAPIAccessor.getTemporaryContentAPI(); try { return temporaryContentAPI.retrieveTempFile(tempFileKey); } catch (TemporaryFileNotFoundException e) { LOGGER.error("Unable to find temporary file with key {}", tempFileKey); throw e; } catch (BonitaRuntimeException e) { LOGGER.error("Unable to retrieve temporary file with key {}", tempFileKey); throw new BonitaException(e); } } public void removeUploadedTempContent(final String tempFileKey) { try { TemporaryContentAPI temporaryContentAPI = PlatformAPIAccessor.getTemporaryContentAPI(); temporaryContentAPI.removeTempFile(tempFileKey); } catch (BonitaException | BonitaRuntimeException e) { LOGGER.warn( "Unable to remove temporary file with key {}. If still present, it will be cleaned by the scheduler.", tempFileKey); } } protected File makeUniqueFilename(final String fileName) throws IOException { final File uploadedFile = File.createTempFile("tmp_", getExtension(fileName), getBonitaTenantConstantUtil().getTempFolder()); uploadedFile.deleteOnExit(); return uploadedFile; } protected String getExtension(final String fileName) { return FilenameUtils.getExtension(fileName); } public WebBonitaConstantsUtils getBonitaTenantConstantUtil() { return WebBonitaConstantsUtils.getTenantInstance(); } public boolean isInTempFolder(final File file, final WebBonitaConstantsUtils webBonitaConstantsUtils) throws IOException { return isInFolder(file, webBonitaConstantsUtils.getTempFolder()); } public boolean isInFolder(final File file, final File parentFolder) throws IOException { try { verifyFolderAuthorization(file, parentFolder); } catch (final UnauthorizedFolderException e) { return false; } return true; } private void verifyFolderAuthorization(final File file, final File parentFolder) throws IOException { try { if (!file.getCanonicalPath().startsWith(parentFolder.getCanonicalPath())) { throw new UnauthorizedFolderException("Unauthorized access to the file " + file.getPath()); } } catch (final UnauthorizedFolderException e) { LOGGER.error( "Unauthorized access to the file {}. For security reasons, access to paths other than {} is restricted.", file.getAbsolutePath(), parentFolder.getAbsolutePath(), e); throw e; } } public IconDescriptor getIconFromFileSystem(String iconKey) { try { TemporaryContentAPI temporaryContentAPI = PlatformAPIAccessor.getTemporaryContentAPI(); FileContent fileContent = temporaryContentAPI.retrieveTempFile(iconKey); return new IconDescriptor(fileContent.getFileName(), IOUtils.toByteArray(fileContent.getInputStream())); } catch (TemporaryFileNotFoundException e) { LOGGER.error("Unable to find the icon temporary file with key {}", iconKey); throw new RuntimeException(e); } catch (BonitaException | IOException e) { throw new RuntimeException(e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/CacheUtil.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder; import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; import lombok.extern.slf4j.Slf4j; import org.ehcache.Cache; import org.ehcache.CacheManager; import org.ehcache.config.units.EntryUnit; @Slf4j public class CacheUtil { private static final int DEFAULT_ON_HEAP_MAX_ENTRIES = 10_000; protected static CacheManager CACHE_MANAGER = null; protected static synchronized CacheManager getCacheManager() { if (CACHE_MANAGER == null) { if (log.isInfoEnabled()) { log.info( "Initializing Ehcache 3 CacheManager with programmatic configuration (heap-only, LRU eviction)"); } // Create CacheManager with programmatic configuration (no XML needed) // Default cache template: heap-only with 10,000 entry capacity and LRU eviction // Note: Ehcache 3 uses LRU (Least Recently Used) eviction by default when heap capacity is exceeded CACHE_MANAGER = newCacheManagerBuilder() .withCache("default", newCacheConfigurationBuilder(Object.class, Object.class, newResourcePoolsBuilder().heap(DEFAULT_ON_HEAP_MAX_ENTRIES, EntryUnit.ENTRIES).build()) .build()) .build(true); } return CACHE_MANAGER; } protected static synchronized Cache createCache(final CacheManager cacheManager, final String cacheName) { // Double-check Cache cache = cacheManager.getCache(cacheName, Object.class, Object.class); if (cache == null) { // In Ehcache 3, we need to provide a configuration when creating a cache // Using a simple heap-based cache with default settings (10,000 entries) // Eviction policy: LRU (Least Recently Used) when heap capacity is exceeded cache = cacheManager.createCache(cacheName, newCacheConfigurationBuilder(Object.class, Object.class, newResourcePoolsBuilder().heap(DEFAULT_ON_HEAP_MAX_ENTRIES, EntryUnit.ENTRIES).build()) .build()); } return cache; } public static void store(final String cacheName, final Object key, final Object value) { final CacheManager cacheManager = getCacheManager(); Cache cache = cacheManager.getCache(cacheName, Object.class, Object.class); if (cache == null) { cache = createCache(cacheManager, cacheName); } // In Ehcache 3, we directly put key-value without Element wrapper cache.put(key, value); if (log.isTraceEnabled()) { log.trace("####Element {} created in cache with name {}", key, cacheName); } } public static Object get(final String cacheName, final Object key) { Object value = null; final CacheManager cacheManager = getCacheManager(); final Cache cache = cacheManager.getCache(cacheName, Object.class, Object.class); if (cache != null) { // In Ehcache 3, get() returns the value directly (no Element wrapper) value = cache.get(key); if (value != null) { if (log.isTraceEnabled()) { log.trace("####Element {} found in cache with name {}", key, cacheName); } } else { if (log.isTraceEnabled()) { log.trace("####Element {} not found in cache with name {}", key, cacheName); } } } else { if (log.isTraceEnabled()) { log.trace("####Cache with name {} doesn't exists or wasn't created yet.", cacheName); } } return value; } public static void clear(final String cacheName) { final Cache cache = getCacheManager().getCache(cacheName, Object.class, Object.class); if (cache != null) { // In Ehcache 3, clear() is used instead of removeAll() cache.clear(); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/ContractTypeConverter.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; import static org.bonitasoft.engine.bpm.contract.InputDefinition.FILE_INPUT_ID; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZonedDateTime; import java.time.format.DateTimeParseException; import java.util.*; import java.util.Map.Entry; import org.apache.commons.beanutils.ConversionException; import org.apache.commons.beanutils.ConvertUtilsBean; import org.apache.commons.beanutils.converters.DateConverter; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.bpm.contract.*; import org.bonitasoft.engine.bpm.contract.impl.ContractDefinitionImpl; import org.bonitasoft.engine.bpm.contract.impl.InputDefinitionImpl; import org.bonitasoft.engine.bpm.document.DocumentException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.io.FileContent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Anthony Birembaut */ public class ContractTypeConverter { /** * Logger */ protected static final Logger LOGGER = LoggerFactory.getLogger(ContractTypeConverter.class.getName()); public static final String[] ISO_8601_DATE_PATTERNS = new String[] { "yyyy-MM-dd", "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ss'Z'", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" }; static final String CONTENT_TYPE = "contentType"; static final String FILE_TEMP_PATH = "tempPath"; static final String TEMP_PATH_DESCRIPTION = "file name in the temporary upload directory"; protected BonitaHomeFolderAccessor bonitaHomeFolderAccessor = new BonitaHomeFolderAccessor(); private final ConvertUtilsBean convertUtilsBean; private long maxSizeForTenant; public ContractTypeConverter(final String[] datePatterns) { convertUtilsBean = new ConvertUtilsBean(); convertUtilsBean.register(true, false, 0); final DateConverter dateConverter = new DateConverter(); dateConverter.setPatterns(datePatterns); dateConverter.setTimeZone(TimeZone.getTimeZone("GMT")); convertUtilsBean.register(dateConverter, Date.class); } Object convertToType(final Type type, final Serializable parameterValue) { final Class clazz = getClassFromType(type); Serializable preprocessedParameterValue = preprocessInputs(type, parameterValue); return convertToType(clazz, preprocessedParameterValue); } private Serializable preprocessInputs(final Type type, final Serializable parameterValue) { //Also support Integer as DATE contract input (as deserialization is handled by jackson it can be mapped to an integer instead of a long when it is a small number) if (Type.DATE.equals(type) && parameterValue instanceof Integer) { return Long.valueOf((Integer) parameterValue); } return parameterValue; } public Map getProcessedInput(final ContractDefinition processContract, final Map inputs, final long maxSizeForTenant) throws FileNotFoundException { this.maxSizeForTenant = maxSizeForTenant; final Map processedInputs = new HashMap<>(); final Map contractDefinitionMap = processContract == null ? Collections.emptyMap() : createContractInputMap(processContract.getInputs()); if (inputs != null) { for (final Entry inputEntry : inputs.entrySet()) { processedInputs.put(inputEntry.getKey(), convertInputToExpectedType(inputEntry.getValue(), contractDefinitionMap.get(inputEntry.getKey()))); } } return processedInputs; } public void deleteTemporaryFiles(Map inputs) { if (inputs != null) { deleteTemporaryFilesInternal((Serializable) inputs); } } private void deleteTemporaryFilesInternal(Serializable inputValue) { if (inputValue == null) { return; } if (inputValue instanceof List) { for (Object element : ((List) inputValue)) { deleteTemporaryFilesInternal(((Serializable) element)); } } else if (inputValue instanceof Map) { for (Map.Entry element : ((Map) inputValue).entrySet()) { if (element.getKey().equals(FILE_TEMP_PATH) && element.getValue() != null) { String path = (String) element.getValue(); bonitaHomeFolderAccessor.removeUploadedTempContent(path); } else { deleteTemporaryFilesInternal(element.getValue()); } } } } private Serializable convertInputToExpectedType(final Serializable inputValue, final Serializable inputDefinition) throws FileNotFoundException { if (inputValue == null) { return null; } else if (inputValue instanceof List) { return convertMultipleInputToExpectedType(inputValue, inputDefinition); } else { return convertSingleInputToExpectedType(inputValue, inputDefinition); } } private Serializable convertMultipleInputToExpectedType(final Serializable inputValue, final Serializable inputDefinition) throws FileNotFoundException { @SuppressWarnings("unchecked") final List listOfValues = (List) inputValue; final List convertedListOfValues = new ArrayList<>(); for (final Serializable value : listOfValues) { Serializable convertedValue = null; if (value != null) { convertedValue = convertSingleInputToExpectedType(value, inputDefinition); } convertedListOfValues.add(convertedValue); } return (Serializable) convertedListOfValues; } private Serializable convertSingleInputToExpectedType(final Serializable inputValue, final Serializable inputDefinition) throws FileNotFoundException { if (inputDefinition == null) { return inputValue; } else if (inputDefinition instanceof Map) { @SuppressWarnings("unchecked") final Map mapOfInputDefinition = (Map) inputDefinition; return convertComplexInputToExpectedType(inputValue, mapOfInputDefinition); } else { final InputDefinition simpleInputDefinition = (InputDefinition) inputDefinition; if (Type.FILE.equals(simpleInputDefinition.getType())) { return convertFileInputToExpectedType(inputValue); } else { return (Serializable) convertToType(simpleInputDefinition.getType(), inputValue); } } } private Serializable convertComplexInputToExpectedType(final Serializable inputValue, final Map mapOfInputDefinition) throws FileNotFoundException { if (inputValue instanceof Map) { @SuppressWarnings("unchecked") final Map mapOfValues = (Map) inputValue; final Map convertedMapOfValues = new HashMap<>(); for (final Entry valueEntry : mapOfValues.entrySet()) { final Serializable childInputDefinition = mapOfInputDefinition.get(valueEntry.getKey()); final Serializable convertedValue = convertInputToExpectedType(valueEntry.getValue(), childInputDefinition); convertedMapOfValues.put(valueEntry.getKey(), convertedValue); } return (Serializable) convertedMapOfValues; } else { return inputValue; } } private Serializable convertFileInputToExpectedType(final Serializable inputValue) throws FileNotFoundException { if (inputValue instanceof Map) { @SuppressWarnings("unchecked") final Map mapOfValues = (Map) inputValue; if (mapOfValues.containsKey(InputDefinition.FILE_INPUT_FILENAME) && mapOfValues.containsKey(FILE_TEMP_PATH)) { final String filename = (String) mapOfValues.get(InputDefinition.FILE_INPUT_FILENAME); return new FileInputValue( DocumentUtil.sanitizeFilename(filename), (String) mapOfValues.get(CONTENT_TYPE), retrieveFileAndGetContent((String) mapOfValues.get(FILE_TEMP_PATH)), (String) mapOfValues.get(FILE_INPUT_ID)); } } return inputValue; } private byte[] retrieveFileAndGetContent(final String fileTempPath) throws FileNotFoundException { byte[] fileContent = null; if (fileTempPath != null) { try { final FileContent sourceFile = bonitaHomeFolderAccessor.retrieveUploadedTempContent(fileTempPath); try (InputStream inputStream = sourceFile.getInputStream()) { fileContent = getFileContent(inputStream, sourceFile.getSize()); } } catch (final BonitaException e) { throw new FileNotFoundException("Cannot find " + fileTempPath + " temp file."); } catch (final IOException e) { throw new RuntimeException(e); } } return fileContent; } private byte[] getFileContent(final InputStream inputStream, long size) throws DocumentException, IOException { byte[] fileContent; if (size > maxSizeForTenant * 1048576 || size > Integer.MAX_VALUE) { // more than 2 GB final String errorMessage = "This document is exceeded configured max size (" + maxSizeForTenant + "Mb) or 2GB"; throw new DocumentException(errorMessage); } return IOUtils.toByteArray(inputStream); } private Map createContractInputMap(final List inputDefinitions) { final Map contractDefinitionMap = new HashMap<>(); for (final InputDefinition inputDefinition : inputDefinitions) { if (inputDefinition.hasChildren() && !Type.FILE.equals(inputDefinition.getType())) { contractDefinitionMap.put(inputDefinition.getName(), (Serializable) createContractInputMap(inputDefinition.getInputs())); } else { contractDefinitionMap.put(inputDefinition.getName(), inputDefinition); } } return contractDefinitionMap; } public ContractDefinition getAdaptedContractDefinition(final ContractDefinition contract) { if (contract == null) { return null; } final List constraints = contract.getConstraints(); final List inputDefinitions = adaptContractInputList(contract.getInputs()); return getContractDefinition(constraints, inputDefinitions); } private List adaptContractInputList(final List inputDefinitions) { final List contractDefinition = new ArrayList<>(); for (final InputDefinition inputDefinition : inputDefinitions) { List childInputDefinitions; if (Type.FILE.equals(inputDefinition.getType())) { childInputDefinitions = getFileChildInputDefinitions(inputDefinition); } else if (inputDefinition.hasChildren()) { childInputDefinitions = adaptContractInputList(inputDefinition.getInputs()); } else { childInputDefinitions = new ArrayList<>(); } final InputDefinition newInputDefinition = new InputDefinitionImpl(inputDefinition.getName(), inputDefinition.getDescription(), inputDefinition.isMultiple(), inputDefinition.getType(), childInputDefinitions); contractDefinition.add(newInputDefinition); } return contractDefinition; } private List getFileChildInputDefinitions(final InputDefinition inputDefinition) { List childInputDefinitions; childInputDefinitions = new ArrayList<>(); for (final InputDefinition childInputDefinition : inputDefinition.getInputs()) { if (Type.BYTE_ARRAY.equals(childInputDefinition.getType())) { childInputDefinitions.add(new InputDefinitionImpl(FILE_TEMP_PATH, TEMP_PATH_DESCRIPTION, false, Type.TEXT, new ArrayList<>())); } else { childInputDefinitions.add(childInputDefinition); } } return childInputDefinitions; } private ContractDefinitionImpl getContractDefinition(final List constraints, final List inputDefinitions) { final ContractDefinitionImpl contractDefinition = new ContractDefinitionImpl(); for (final ConstraintDefinition constraint : constraints) { contractDefinition.addConstraint(constraint); } for (final InputDefinition input : inputDefinitions) { contractDefinition.addInput(input); } return contractDefinition; } private Object convertToType(final Class clazz, final Serializable parameterValue) { if (parameterValue == null) { return null; } String paramValueString = parameterValue.toString(); try { if (clazz == LocalDate.class) { //We drop useless info received from the widget ex: 2010-12-04T18:42:10Z, we drop T18:42:10Z to allow conversion if (paramValueString.length() > 10) { LOGGER.debug("The string " + paramValueString + " contains information that will be dropped to convert it to a LocalDate (most likely time and timezone information which are not relevant)."); paramValueString = paramValueString.substring(0, 10); } return LocalDate.parse(paramValueString); } else if (clazz == LocalDateTime.class) { try { return LocalDateTime.parse(paramValueString); } catch (DateTimeParseException e) { LOGGER.debug("The string " + paramValueString + " contains information that will be dropped to convert it to a LocalDateTime (most likely time and timezone information which are not relevant)."); //We drop the timezone info from the String: return ZonedDateTime.parse(paramValueString).toLocalDateTime(); } } else if (clazz == OffsetDateTime.class) { ZonedDateTime zonedDateTime = ZonedDateTime.parse(paramValueString); return zonedDateTime.toOffsetDateTime(); } else { return convertUtilsBean.convert(parameterValue, clazz); } } catch (final ConversionException | DateTimeParseException e) { LOGGER.info("unable to parse '" + parameterValue + "' to type " + clazz.getName(), e); return parameterValue; } } private Class getClassFromType(final Type type) { switch (type) { case BOOLEAN: return Boolean.class; case DATE: return Date.class; case INTEGER: return Integer.class; case DECIMAL: return Double.class; case BYTE_ARRAY: return Byte[].class; case LONG: return Long.class; case LOCALDATE: return LocalDate.class; case LOCALDATETIME: return LocalDateTime.class; case OFFSETDATETIME: return OffsetDateTime.class; default: return String.class; } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/DefaultTenantIdException.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; /** * @author Vincent Elcrin */ public class DefaultTenantIdException extends RuntimeException { private static final long serialVersionUID = -6433100847740743825L; public DefaultTenantIdException(Exception e) { super("Can't retrieve default tenant id", e); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/DocumentUtil.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; /** * @author Yongtao Guo */ public class DocumentUtil { public static byte[] getArrayByte(final InputStream input, final int estimatedSize) throws IOException { final ByteArrayOutputStream output = new ByteArrayOutputStream(estimatedSize); try (output) { final byte[] buf = new byte[8192]; int len; while ((len = input.read(buf)) >= 0) { output.write(buf, 0, len); } } return output.toByteArray(); } public static byte[] getArrayByteFromFile(final File f) throws IOException { final long length = f.length(); if (length > Integer.MAX_VALUE) { // more than 2 GB throw new IOException("File too big"); } try (FileInputStream input = new FileInputStream(f)) { return getArrayByte(input, (int) length); } } public static String sanitizeFilename(String filename) { if (filename != null) { return filename.replaceAll("[<>\"]+", "_").trim(); } return null; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/IconDescriptor.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; import java.util.Arrays; import java.util.Objects; /** * @author Baptiste Mesta */ public class IconDescriptor { private final String filename; private final byte[] content; public IconDescriptor(String filename, byte[] content) { this.filename = filename; this.content = content; } public byte[] getContent() { return content; } public String getFilename() { return filename; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; IconDescriptor that = (IconDescriptor) o; return Objects.equals(filename, that.filename) && Arrays.equals(content, that.content); } @Override public int hashCode() { return Objects.hash(filename, content); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/ListUtil.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; import java.util.List; /** * @author Haojie Yuan */ public class ListUtil { public static List paginate(final List list, final int page, final int resultsByPage) { final int startIndex = page * resultsByPage; if (startIndex > list.size()) { final List result = list.subList(0, 0); result.clear(); return result; } final int endIndex = Math.min(startIndex + resultsByPage, list.size()); return list.subList(startIndex, endIndex); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/LocaleUtils.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; import java.util.*; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.console.common.server.i18n.I18n; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Anthony Birembaut */ public class LocaleUtils { public static final String LOCALE_PARAM = "locale"; /** * user's locale URL parameter used by the login JSP */ public static final String PORTAL_LOCALE_PARAM = "_l"; public static final String DEFAULT_LOCALE = "en"; public static final String LOCALE_COOKIE_NAME = "BOS_Locale"; private static final String DEFAULT_APPLICATION = "portal"; private static final Map AVAILABLE_LOCALES = I18n.getInstance() .getAvailableLocalesFor(DEFAULT_APPLICATION); /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(LocaleUtils.class.getName()); /** * Return the user locale as set in the the request. If the locale code is invalid, returns the default locale (en). * If the locale is not passed in the request try to get it from the BOS_Locale cookie. * If the cookie is not set returns the Browser locale * If the browser locale is not set returns the default locale (en). * This method should be used sparingly for performances reasons (gather in one call when possible) * * @param request * the HTTP servlet request * @return the user locale */ public static String getUserLocaleAsString(final HttpServletRequest request) { String locale = getLocaleFromRequestURL(request); if (locale == null) { locale = getStandardizedLocaleFromCookie(request); } if (locale == null) { String browserLocale = getLocaleFromBrowser(request); locale = browserLocale != null ? browserLocale : DEFAULT_LOCALE; } return locale; } public static void logUnsupportedLocale(String unsupportedLocale, String usedLocale) { if (!unsupportedLocale.equals(usedLocale) && LOGGER.isDebugEnabled()) { LOGGER.debug("Locale \"" + unsupportedLocale + "\" is not part of the locales available. Using locale \"" + usedLocale + "\""); } } public static boolean isLocaleSupportedInPortal(String locale) { return new ArrayList<>(AVAILABLE_LOCALES.keySet()).contains(locale); } public static boolean canLocaleBeReducedToSupportedLocale(String locale, String supportedLocale) { if (locale.indexOf("-") > 0) { return locale.substring(0, locale.indexOf("-")).equals(supportedLocale); } else if (locale.indexOf("_") > 0) { return locale.substring(0, locale.indexOf("_")).equals(supportedLocale); } return false; } private static String standardizeLocale(String locale) { if (isLocaleSupportedInPortal(locale)) { return locale; } else { List supportedLocales = new ArrayList<>(AVAILABLE_LOCALES.keySet()); for (String supportedLocale : supportedLocales) { if (canLocaleBeReducedToSupportedLocale(locale, supportedLocale)) { if (LOGGER.isTraceEnabled()) { LOGGER.trace( "Using available locale \"" + supportedLocale + "\" instead of \"" + locale + "\""); } return supportedLocale; } } } logUnsupportedLocale(locale, "en"); return DEFAULT_LOCALE; } public static String getStandardizedLocaleFromCookie(final HttpServletRequest request) { String locale = getLocaleFromCookies(request); return locale != null ? standardizeLocale(locale) : null; } public static String getLocaleFromCookies(HttpServletRequest request) { if (request.getCookies() != null) { for (final Cookie cookie : request.getCookies()) { if (cookie.getName().equals(LOCALE_COOKIE_NAME)) { return cookie.getValue(); } } } return null; } public static String getLocaleFromBrowser(final HttpServletRequest request) { Locale browserLocale = request.getLocale(); return browserLocale != null ? standardizeLocale(browserLocale.toString()) : null; } public static Locale getUserLocale(final HttpServletRequest request) { return org.apache.commons.lang3.LocaleUtils.toLocale(getUserLocaleAsString(request)); } public static String getLocaleFromRequestURL(final HttpServletRequest request) { String localeAsString = request.getParameter(LOCALE_PARAM); if (localeAsString == null) { localeAsString = request.getParameter(PORTAL_LOCALE_PARAM); } if (localeAsString != null && localeAsString.length() > 0) { try { org.apache.commons.lang3.LocaleUtils.toLocale(localeAsString); localeAsString = standardizeLocale(localeAsString); } catch (Exception e) { logUnsupportedLocale(localeAsString, "en"); localeAsString = DEFAULT_LOCALE; } return localeAsString; } return null; } public static void addOrReplaceLocaleCookieResponse(final HttpServletResponse response, final String locale) { String standardizedLocale = standardizeLocale(locale); Cookie localeCookie = new Cookie(LOCALE_COOKIE_NAME, standardizedLocale); localeCookie.setPath("/"); response.addCookie(localeCookie); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/PlatformManagementUtils.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.Map; import org.bonitasoft.console.common.server.preferences.properties.ConfigurationFilesManager; import org.bonitasoft.engine.api.ApiAccessType; import org.bonitasoft.engine.api.PlatformAPI; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.api.PlatformLoginAPI; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.platform.InvalidPlatformCredentialsException; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.engine.util.APITypeManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Baptiste Mesta */ public class PlatformManagementUtils { private static final Logger LOGGER = LoggerFactory.getLogger(PlatformManagementUtils.class.getName()); private final ConfigurationFilesManager configurationFilesManager = ConfigurationFilesManager.getInstance(); //package local for testing purpose boolean isLocal() throws UnknownAPITypeException, ServerAPIException, IOException { return ApiAccessType.LOCAL.equals(APITypeManager.getAPIType()); } //package local for testing purpose PlatformAPI getPlatformAPI(final PlatformSession platformSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return PlatformAPIAccessor.getPlatformAPI(platformSession); } //package local for testing purpose PlatformLoginAPI getPlatformLoginAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return PlatformAPIAccessor.getPlatformLoginAPI(); } public PlatformSession platformLogin() throws BonitaException, IOException { if (isLocal()) { try { return localPlatformLogin(); } catch (final Exception e) { throw new ServerAPIException("Unable to login locally", e); } } else { String username = System.getProperty("org.bonitasoft.platform.username"); String password = System.getProperty("org.bonitasoft.platform.password"); try { return getPlatformLoginAPI().login(username, password); } catch (InvalidPlatformCredentialsException e) { throw new InvalidPlatformCredentialsException("The portal is not able to login to the engine because " + "system properties org.bonitasoft.platform.username and org.bonitasoft.platform.password are " + "not set correctly to the platform administrator credentials.\n " + "These properties must be set when connecting to a Bonita engine that is not local. " + "If the engine is local, change the connection to LOCAL using the APITypeManager"); } } } PlatformSession localPlatformLogin() throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException { final Class api = Class.forName("org.bonitasoft.engine.LocalLoginMechanism"); return (PlatformSession) api.getDeclaredMethod("login").invoke(api.getDeclaredConstructor().newInstance()); } void platformLogout(final PlatformSession platformSession) throws BonitaException { final PlatformLoginAPI platformLoginAPI = getPlatformLoginAPI(); platformLoginAPI.logout(platformSession); } private void retrieveTenantsConfiguration(final PlatformAPI platformAPI) throws IOException { configurationFilesManager.setTenantConfigurationFiles(platformAPI.getClientTenantConfigurations()); } private void retrievePlatformConfiguration(final PlatformAPI platformAPI) throws IOException { final Map clientPlatformConfigurations = platformAPI.getClientPlatformConfigurations(); configurationFilesManager.setPlatformConfigurations(clientPlatformConfigurations); } public void initializePlatformConfiguration() throws BonitaException, IOException { final PlatformSession platformSession = platformLogin(); final PlatformAPI platformAPI = getPlatformAPI(platformSession); retrievePlatformConfiguration(platformAPI); retrieveTenantsConfiguration(platformAPI); platformLogout(platformSession); } public void updateConfigurationFile(final String file, final byte[] content) throws IOException, BonitaException { final PlatformSession platformSession = platformLogin(); final PlatformAPI platformAPI = getPlatformAPI(platformSession); platformAPI.updateClientTenantConfigurationFile(file, content); platformLogout(platformSession); } /** * String => configuration file name * byte[] => content of the configuration file */ public Map readTenantConfigurationsFromEngine() { try { final PlatformSession platformSession = platformLogin(); try { return getPlatformAPI(platformSession).getClientTenantConfigurations(); } finally { platformLogout(platformSession); } } catch (BonitaException | IOException e) { LOGGER.error("Cannot retrieve tenant configurations", e); return Collections.emptyMap(); } } public boolean isPlatformAvailable() { try { final PlatformSession platformSession = platformLogin(); try { PlatformAPI platformAPI = PlatformAPIAccessor.getPlatformAPI(platformSession); return platformAPI.isNodeStarted(); } finally { platformLogout(platformSession); } } catch (Exception e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Platform is not available.", e); } else if (LOGGER.isInfoEnabled()) { LOGGER.info("Platform is not available."); } return false; } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/SessionUtil.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; import javax.servlet.http.HttpSession; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.user.User; /** * @author Ruiheng.Fan * @author Baptiste Mesta */ public class SessionUtil { /** * the session param for the engine API session */ public static final String API_SESSION_PARAM_KEY = "apiSession"; /** * the session param for the user */ public static final String USER_SESSION_PARAM_KEY = "user"; /** * the session param for the username */ public static final String USERNAME_SESSION_PARAM = "username"; public static void sessionLogin(final User user, final APISession apiSession, final HttpSession session) { session.setAttribute(USERNAME_SESSION_PARAM, user.getUsername()); session.setAttribute(USER_SESSION_PARAM_KEY, user); session.setAttribute(API_SESSION_PARAM_KEY, apiSession); } public static void sessionLogout(final HttpSession session) { sessionLogout(session, true); } public static void sessionLogout(final HttpSession session, final boolean invalidateHTTPSession) { session.removeAttribute(API_SESSION_PARAM_KEY); session.removeAttribute(USERNAME_SESSION_PARAM); session.removeAttribute(USER_SESSION_PARAM_KEY); if (invalidateHTTPSession) { session.invalidate(); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/TenantCacheUtil.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; public class TenantCacheUtil { protected static final String PROCESS_ACTOR_INITIATOR_CACHE = "processActorInitiatorCache"; protected TenantCacheUtil() { } public Long getProcessActorInitiatorId(final Long processId) { return (Long) CacheUtil.get(PROCESS_ACTOR_INITIATOR_CACHE, processId); } public Long storeProcessActorInitiatorId(final Long processId, final Long actorInitiatorId) { CacheUtil.store(PROCESS_ACTOR_INITIATOR_CACHE, processId, actorInitiatorId); return actorInitiatorId; } public void clearProcessActorInitiators() { CacheUtil.clear(PROCESS_ACTOR_INITIATOR_CACHE); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/TenantCacheUtilFactory.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; /** * @author Julien Mege */ public class TenantCacheUtilFactory { private static TenantCacheUtil tenantCacheUtil; /** * Get FormCacheUtil of different Domain * * @return TenatCacheUtil */ public static TenantCacheUtil getTenantCacheUtil() { if (tenantCacheUtil == null) { tenantCacheUtil = new TenantCacheUtil(); } return tenantCacheUtil; } public static void clearTenantCacheUtil() { tenantCacheUtil.clearProcessActorInitiators(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/TenantsManagementUtils.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; import org.bonitasoft.engine.api.ProfileAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.profile.ProfileCriterion; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; /** * Tenant management utils class * * @author Anthony Birembaut */ public class TenantsManagementUtils { /** * Check for user's profile */ public static boolean hasProfileForUser(final APISession apiSession) throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return !getProfileApi(apiSession).getProfilesForUser(apiSession.getUserId(), 0, 1, ProfileCriterion.ID_ASC) .isEmpty(); } private static ProfileAPI getProfileApi(final APISession session) throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getProfileAPI(session); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/UnauthorizedFolderException.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; import java.io.IOException; /** * Technical exception thrown when we try to read/write a file out of the authorized file system path. * * @author Julien Mege */ public class UnauthorizedFolderException extends IOException { private static final long serialVersionUID = 1071342750973031637L; public UnauthorizedFolderException(final String message, final Throwable throwable) { super(message, throwable); } public UnauthorizedFolderException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/UnzipUtil.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import org.bonitasoft.engine.io.IOUtil; /** * Utility class extracting zip file * * @author Zhiheng Yang */ public class UnzipUtil { /** * Unzip a zip file from InputStream. * Client is responsible to close the input stream. */ public static synchronized void unzip(final InputStream sourceFile, final String targetPath) throws IOException { IOUtil.unzipToFolder(sourceFile, new File(targetPath)); } /** * Unzip a zip file from InputStream * * @param zipFile * of SourceFile * @param targetPath * @throws IOException * @throws FileNotFoundException */ public static synchronized void unzip(final File zipFile, final String targetPath) throws FileNotFoundException, IOException { try (final FileInputStream zipFileInputStream = new FileInputStream(zipFile)) { unzip(zipFileInputStream, targetPath); } } public static synchronized void unzip(final File zipFile, final String targetPath, final boolean deleteFileAfterZip) throws IOException { unzip(zipFile, targetPath); if (deleteFileAfterZip) { zipFile.delete(); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/UrlBuilder.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.http.Consts; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URIBuilder; import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.message.BasicNameValuePair; /** * @author Vincent Elcrin, Julien Reboul */ public class UrlBuilder { private final URIBuilder uriBuilder; private final List parameters = new LinkedList<>(); public UrlBuilder(final String urlString) { URI uri; try { uri = new URI(urlString); } catch (URISyntaxException e) { throw new RuntimeException(e); } uriBuilder = new URIBuilder(uri); uriBuilder.setCharset(Consts.UTF_8); if (uri.getRawQuery() != null) { //avoid NPE thrown by httpClient 4.5.2 regression parameters.addAll(URLEncodedUtils.parse(uri.getRawQuery(), StandardCharsets.UTF_8)); } } public void appendParameter(final String key, final String... values) { if (!isParameterAlreadyDefined(parameters, key)) { parameters.add(new BasicNameValuePair(key, new UrlValue(values).toString())); } } public void removeParameter(final String key) { parameters.removeIf(param -> param.getName().equals(key)); } private boolean isParameterAlreadyDefined(List params, String key) { for (NameValuePair param : params) { if (param.getName().equals(key)) { return true; } } return false; } public void appendParameters(final Map parameters) { for (Entry next : parameters.entrySet()) { appendParameter(next.getKey(), new UrlValue(next.getValue()).toString()); } } public String build() { if (!parameters.isEmpty()) { uriBuilder.setParameters(parameters); } else { //in case the parameters list is an empty list setParameters does not remove the question mark //removeQuery does remove the question mark in the URL uriBuilder.removeQuery(); } return uriBuilder.toString(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/common/server/utils/UrlValue.java ================================================ /** * Copyright (C) 2022 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.console.common.server.utils; /** * @author Vincent Elcrin */ public class UrlValue { private final String value; public UrlValue(String value) { this.value = value; } public UrlValue(String... values) { this(merge(values)); } private static String merge(String... values) { final StringBuilder value = new StringBuilder(); for (final String parameterValue : values) { if (value.length() > 0) { value.append(","); } value.append(parameterValue); } return value.toString(); } @Override public String toString() { return value; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/ConsoleServiceFactory.java ================================================ /** * Copyright (C) 2022 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.console.server; import org.bonitasoft.console.server.service.ApplicationsImportService; import org.bonitasoft.console.server.service.OrganizationImportService; import org.bonitasoft.console.server.service.ProcessActorImportService; import org.bonitasoft.web.toolkit.server.Service; import org.bonitasoft.web.toolkit.server.ServiceFactory; import org.bonitasoft.web.toolkit.server.ServiceNotFoundException; public class ConsoleServiceFactory implements ServiceFactory { @Override public Service getService(final String calledToolToken) { if (OrganizationImportService.TOKEN.equals(calledToolToken)) { return new OrganizationImportService(); } else if (ProcessActorImportService.TOKEN.equals(calledToolToken)) { return new ProcessActorImportService(); } else if (ApplicationsImportService.TOKEN.equals(calledToolToken)) { return new ApplicationsImportService(); } throw new ServiceNotFoundException(calledToolToken); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/ConsoleServiceServlet.java ================================================ /** * Copyright (C) 2022 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.console.server; import org.bonitasoft.web.toolkit.server.servlet.ServiceServlet; /** * @author Séverin Moussel */ @SuppressWarnings("serial") public class ConsoleServiceServlet extends ServiceServlet { public ConsoleServiceServlet() { super(new ConsoleServiceFactory()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/service/ApplicationsImportService.java ================================================ /** * Copyright (C) 2022 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.console.server.service; import static org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.t_; import java.util.List; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.business.application.ApplicationImportPolicy; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ExecutionException; import org.bonitasoft.engine.exception.ImportException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.web.common.model.ImportStatusMessages; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Julien Mege * @deprecated as of 9.0.0, Applications should be imported at startup. */ @Deprecated(since = "9.0.0") public class ApplicationsImportService extends BonitaImportService { public final static String TOKEN = "/application/import"; /** * organization data file */ public static final String FILE_UPLOAD = "applicationsDataUpload"; /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationsImportService.class.getName()); protected ApplicationAPI getApplicationAPI() throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getLivingApplicationAPI(getSession()); } @Override public String getToken() { return TOKEN; } @Override public String getFileUploadParamName() { return FILE_UPLOAD; } @Override public ImportStatusMessages importFileContent(final byte[] fileContent, final String importPolicyAsString) throws ExecutionException, ImportException, AlreadyExistsException, InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { final ApplicationImportPolicy importPolicy = ApplicationImportPolicy.valueOf(importPolicyAsString); final List ImportStatusList = getApplicationAPI().importApplications(fileContent, importPolicy); return new ImportStatusMessages(ImportStatusList); } @Override protected String getFileFormatExceptionMessage() { return AbstractI18n.t_("Can't import Applications.", getLocale()); } @Override protected String getAlreadyExistsExceptionMessage(final AlreadyExistsException e) { return t_("Can't import applications. An application '%token%' already exists", getLocale(), new Arg("token", e.getName())); } @Override protected String getFileReadingError() { return AbstractI18n.t_("Error during Application import file reading."); } @Override protected Logger getLogger() { return LOGGER; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/service/BonitaImportService.java ================================================ /** * Copyright (C) 2022 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.console.server.service; import java.io.InputStream; import org.apache.commons.io.IOUtils; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.exception.*; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.web.common.model.ImportStatusMessages; import org.bonitasoft.web.rest.server.framework.json.JacksonSerializer; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; import org.bonitasoft.web.toolkit.server.ServiceException; import org.slf4j.Logger; /** * Import REST Resources and return the Import Status Messages * * @author Julien Mege */ abstract class BonitaImportService extends ConsoleService { /** * import policy */ public static final String IMPORT_POLICY_PARAM_NAME = "importPolicy"; @Override public Object run() { FileContent xmlFile; try { xmlFile = getTenantFolder().retrieveUploadedTempContent(getFileUploadParamValue()); } catch (final BonitaException e) { throw new ServiceException(getToken(), e.getMessage(), e); } final String importPolicyAsString = getParameter(IMPORT_POLICY_PARAM_NAME); try { final JacksonSerializer serializer = new JacksonSerializer(); final ImportStatusMessages importStatusMessages = importFileContent( readImportFile(xmlFile.getInputStream()), importPolicyAsString); return serializer.serialize(importStatusMessages); } catch (final InvalidSessionException e) { if (getLogger().isInfoEnabled()) { getLogger().info(AbstractI18n.t_("Session expired. Please log in again."), e); } throw e; } catch (final ExecutionException e) { if (getLogger().isWarnEnabled()) { getLogger().warn(e.getMessage(), e); } throw new ServiceException(getToken(), getFileFormatExceptionMessage(), e); } catch (final ImportException e) { if (getLogger().isWarnEnabled()) { getLogger().warn(e.getMessage(), e); } throw new ServiceException(getToken(), getFileFormatExceptionMessage(), e); } catch (final AlreadyExistsException e) { if (getLogger().isWarnEnabled()) { getLogger().warn(e.getMessage(), e); } throw new ServiceException(getToken(), getAlreadyExistsExceptionMessage(e), e); } catch (final Exception e) { if (getLogger().isErrorEnabled()) { getLogger().error(e.getMessage(), e); } throw new ServiceException(getToken(), e); } finally { getTenantFolder().removeUploadedTempContent(getFileUploadParamValue()); } } protected byte[] readImportFile(final InputStream xmlInputStream) { try (xmlInputStream) { return IOUtils.toByteArray(xmlInputStream); } catch (final Exception e) { throw new ServiceException(getFileReadingError(), e); } } protected BonitaHomeFolderAccessor getTenantFolder() { return new BonitaHomeFolderAccessor(); } protected String getFileUploadParamValue() { return getParameter(getFileUploadParamName()); } protected abstract String getFileReadingError(); protected abstract String getToken(); protected abstract String getFileUploadParamName(); protected abstract ImportStatusMessages importFileContent(final byte[] fileContent, final String importPolicy) throws ExecutionException, ImportException, AlreadyExistsException, InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException; protected abstract String getFileFormatExceptionMessage(); protected abstract String getAlreadyExistsExceptionMessage(AlreadyExistsException e); protected abstract Logger getLogger(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/service/ConsoleService.java ================================================ /** * Copyright (C) 2022 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.console.server.service; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.toolkit.server.Service; /** * @author Séverin Moussel */ public abstract class ConsoleService extends Service { private APISession sessionSingleton = null; /** * Get the session */ protected final APISession getSession() { if (this.sessionSingleton == null) { this.sessionSingleton = (APISession) getHttpSession().getAttribute("apiSession"); } return this.sessionSingleton; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/service/OrganizationImportService.java ================================================ /** * Copyright (C) 2022 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.console.server.service; import java.io.IOException; import java.io.InputStream; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.identity.ImportPolicy; import org.bonitasoft.engine.identity.InvalidOrganizationFileFormatException; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; import org.bonitasoft.web.toolkit.server.ServiceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Séverin Moussel */ public class OrganizationImportService extends ConsoleService { public static final String TOKEN = "/organization/import"; /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(OrganizationImportService.class.getName()); /** * organization data file */ private static final String FILE_UPLOAD = "organizationDataUpload"; /** * import policy */ private static final String IMPORT_POLICY_PARAM_NAME = "importPolicy"; @Override public Object run() { final BonitaHomeFolderAccessor tenantFolder = new BonitaHomeFolderAccessor(); try { final byte[] organizationContent = getOrganizationContent(tenantFolder); getIdentityAPI().importOrganizationWithWarnings(new String(organizationContent), getImportPolicy()); } catch (final InvalidSessionException e) { getHttpResponse().setStatus(HttpServletResponse.SC_UNAUTHORIZED); String message = AbstractI18n.t_("Session expired. Please log in again."); if (LOGGER.isInfoEnabled()) { LOGGER.info(message, e.getMessage()); } throw new ServiceException(TOKEN, message, e); } catch (InvalidOrganizationFileFormatException | IllegalArgumentException e) { getHttpResponse().setStatus(HttpServletResponse.SC_BAD_REQUEST); String message = AbstractI18n.t_("Can't import organization. Please check that your file is well-formed."); if (LOGGER.isInfoEnabled()) { LOGGER.info(message, e.getMessage()); } throw new ServiceException(TOKEN, message, e); } catch (final Exception e) { if (LOGGER.isErrorEnabled()) { LOGGER.error(e.getMessage(), e); } throw new ServiceException(TOKEN, AbstractI18n.t_("Can't import organization"), e); } finally { cleanTempContent(tenantFolder); } return ""; } private ImportPolicy getImportPolicy() { final String importPolicyAsString = getParameter(IMPORT_POLICY_PARAM_NAME); ImportPolicy importPolicy = ImportPolicy.MERGE_DUPLICATES; if (importPolicyAsString != null) { importPolicy = ImportPolicy.valueOf(importPolicyAsString); } return importPolicy; } public byte[] getOrganizationContent(final BonitaHomeFolderAccessor tenantFolder) throws IOException, BonitaException { try (InputStream xmlStream = tenantFolder.retrieveUploadedTempContent(getFileUploadParameter()) .getInputStream()) { return IOUtils.toByteArray(xmlStream); } } public void cleanTempContent(final BonitaHomeFolderAccessor tenantFolder) { tenantFolder.removeUploadedTempContent(getFileUploadParameter()); } protected IdentityAPI getIdentityAPI() throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getIdentityAPI(getSession()); } protected String getFileUploadParameter() { return getParameter(FILE_UPLOAD); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/service/ProcessActorImportService.java ================================================ /** * Copyright (C) 2022 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.console.server.service; import java.io.IOException; import java.io.InputStream; import org.apache.commons.io.IOUtils; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.toolkit.server.ServiceException; /** * @author Paul AMAR */ public class ProcessActorImportService extends ConsoleService { public final static String TOKEN = "/bpm/process/importActors"; @Override public Object run() { final BonitaHomeFolderAccessor tenantFolder = new BonitaHomeFolderAccessor(); try { final FileContent xmlFile = tenantFolder.retrieveUploadedTempContent(getFileUploadParameter()); final APISession apiSession = getSession(); final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(apiSession); try (InputStream xmlStream = xmlFile.getInputStream()) { final byte[] actorsXmlContent = IOUtils.toByteArray(xmlStream); processAPI.importActorMapping(Long.valueOf(getParameter("process_id")), actorsXmlContent); } } catch (final BonitaException | IOException e) { throw new ServiceException(TOKEN, e.getMessage()); } finally { tenantFolder.removeUploadedTempContent(getFileUploadParameter()); } return ""; } protected String getFileUploadParameter() { return getParameter("file"); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/servlet/ApplicationsExportServlet.java ================================================ /** * Copyright (C) 2022 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.console.server.servlet; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ExecutionException; import org.bonitasoft.engine.exception.ExportException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.APISession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Julien MEGE */ public class ApplicationsExportServlet extends ExportByIdsServlet { /** * export file name */ private static final String EXPORT_FILE_NAME = "applicationDescriptorFile.xml"; /** * UID */ private static final long serialVersionUID = 1800666571090128789L; /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationsExportServlet.class.getName()); @Override protected String getFileExportName() { return EXPORT_FILE_NAME; } @Override protected byte[] exportResources(final long[] ids, final APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, ExecutionException, ExportException { final ApplicationAPI applicationAPI = getApplicationAPI(apiSession); return applicationAPI.exportApplications(ids); } protected ApplicationAPI getApplicationAPI(final APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getLivingApplicationAPI(apiSession); } @Override protected Logger getLogger() { return LOGGER; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/servlet/BonitaExportServlet.java ================================================ /** * Copyright (C) 2022 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.console.server.servlet; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ExecutionException; import org.bonitasoft.engine.exception.ExportException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.InvalidSessionException; import org.slf4j.Logger; /** * Export Resources as XML file * * @author Cuisha Gai, Anthony Birembaut */ abstract class BonitaExportServlet extends HttpServlet { /** * UID */ private static final long serialVersionUID = 1800666571090128789L; @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { OutputStream out = null; try { final byte[] resourceBytes = exportResources(request); // Set response headers setResponseHeaders(request, response); out = response.getOutputStream(); out.write(resourceBytes); } catch (final InvalidSessionException e) { String message = "Session expired. Please log in again."; if (getLogger().isDebugEnabled()) { getLogger().debug(message, e); } response.sendError(HttpServletResponse.SC_UNAUTHORIZED, message); } catch (final FileNotFoundException e) { String message = "There is no BDM Access control installed."; if (getLogger().isInfoEnabled()) { getLogger().info(message); } response.sendError(HttpServletResponse.SC_NOT_FOUND, message); } catch (final Exception e) { if (getLogger().isErrorEnabled()) { getLogger().error(e.getMessage(), e); } throw new ServletException(e.getMessage(), e); } } protected void setResponseHeaders(final HttpServletRequest request, final HttpServletResponse response) throws UnsupportedEncodingException { response.setCharacterEncoding("UTF-8"); response.setContentType("application/octet-stream"); final String encodedfileName = URLEncoder.encode(getFileExportName(), StandardCharsets.UTF_8); final String userAgent = request.getHeader("User-Agent"); if (userAgent != null && userAgent.contains("Firefox")) { response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedfileName); } else { response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedfileName.replaceAll("\\_", " ") + "\"; filename*=UTF-8''" + encodedfileName); } } protected abstract String getFileExportName(); protected abstract byte[] exportResources(final HttpServletRequest request) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, ExportException, FileNotFoundException, ExecutionException; protected abstract Logger getLogger(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/servlet/ExportByIdsServlet.java ================================================ /** * Copyright (C) 2022 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.console.server.servlet; import java.io.FileNotFoundException; import javax.servlet.http.HttpServletRequest; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ExecutionException; import org.bonitasoft.engine.exception.ExportException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * Export Resources with Ids as XML file * * @author Anthony Birembaut */ public abstract class ExportByIdsServlet extends BonitaExportServlet { private static final long serialVersionUID = -671004787066141408L; private static final String RESOURCES_PARAM_KEY = "id"; @Override protected byte[] exportResources(final HttpServletRequest request) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, ExportException, FileNotFoundException, ExecutionException { final APISession apiSession = (APISession) request.getSession().getAttribute(SessionUtil.API_SESSION_PARAM_KEY); long[] resourcesIds = getResourcesAsList(request); return exportResources(resourcesIds, apiSession); } protected final long[] getResourcesAsList(final HttpServletRequest request) { final String resourceIDParamValue = request.getParameter(RESOURCES_PARAM_KEY); final String[] resourcesIDAsStrings = parseIdsParamValue(resourceIDParamValue); final long[] resourceIDs = new long[resourcesIDAsStrings.length]; if (resourcesIDAsStrings != null) { for (int i = 0; i < resourcesIDAsStrings.length; i++) { resourceIDs[i] = Long.valueOf(resourcesIDAsStrings[i]); } } return resourceIDs; } private String[] parseIdsParamValue(final String resourceIDParamValue) { if (resourceIDParamValue != null) { return resourceIDParamValue.split(","); } else { throw new RuntimeException(AbstractI18n.t_("Request parameter \"id\" must be set.")); } } protected abstract byte[] exportResources(final long[] ids, final APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, ExportException, FileNotFoundException, ExecutionException; } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/servlet/OrganizationExportServlet.java ================================================ /** * Copyright (C) 2022 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.console.server.servlet; import java.io.OutputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Chong Zhao */ public class OrganizationExportServlet extends HttpServlet { private static final String EXPORT_FILE_NAME = "Organization_Data.xml"; /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(OrganizationExportServlet.class.getName()); /** * UID */ private static final long serialVersionUID = 7203686892997001991L; @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException { final APISession apiSession = (APISession) request.getSession().getAttribute(SessionUtil.API_SESSION_PARAM_KEY); OutputStream out = null; try { final String organizationContent = getIdentityAPI(apiSession).exportOrganization(); // Set response headers response.setCharacterEncoding("UTF-8"); response.setContentType("application/octet-stream"); final String encodedfileName = URLEncoder.encode(EXPORT_FILE_NAME, StandardCharsets.UTF_8); final String userAgent = request.getHeader("User-Agent"); if (userAgent != null && userAgent.contains("Firefox")) { response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedfileName); } else { response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedfileName.replaceAll("\\_", " ") + "\"; filename*=UTF-8''" + encodedfileName); } out = response.getOutputStream(); if (organizationContent == null) { response.setContentLength(0); } else { response.setContentLength(organizationContent.getBytes().length); } out.write(organizationContent.getBytes()); } catch (final InvalidSessionException e) { final String message = "Session expired. Please log in again."; if (LOGGER.isInfoEnabled()) { LOGGER.info(message, e); } throw new ServletException(e.getMessage(), e); } catch (final Exception e) { if (LOGGER.isErrorEnabled()) { LOGGER.error(e.getMessage(), e); } throw new ServletException(e.getMessage(), e); } } private IdentityAPI getIdentityAPI(final APISession apiSession) throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getIdentityAPI(apiSession); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/console/server/servlet/ProcessActorsExportServlet.java ================================================ /** * Copyright (C) 2022 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.console.server.servlet; import java.io.OutputStream; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Chong Zhao */ public class ProcessActorsExportServlet extends HttpServlet { private static final String EXPORT_FILE_SUFFIX = "_Process_Actors.xml"; private static final String PROCESS_ID = "processId"; /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(ProcessActorsExportServlet.class.getName()); /** * UID */ private static final long serialVersionUID = -1463938254928196006L; @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException { final String processId = request.getParameter(PROCESS_ID); final APISession apiSession = (APISession) request.getSession().getAttribute(SessionUtil.API_SESSION_PARAM_KEY); OutputStream out = null; try { final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(apiSession); final String actorContent = processAPI.exportActorMapping(Long.parseLong(processId)); // Set response headers response.setCharacterEncoding("UTF-8"); response.setContentType("application/octet-stream"); final String encodedfileName = URLEncoder.encode(processId + EXPORT_FILE_SUFFIX, StandardCharsets.UTF_8); final String userAgent = request.getHeader("User-Agent"); if (userAgent != null && userAgent.contains("Firefox")) { response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedfileName); } else { response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedfileName.replaceAll("\\_", " ") + "\"; filename*=UTF-8''" + encodedfileName); } out = response.getOutputStream(); if (actorContent == null) { response.setContentLength(0); } else { response.setContentLength(actorContent.length()); } out.write(actorContent.getBytes()); } catch (final InvalidSessionException e) { final String message = "Session expires. Please login again."; if (LOGGER.isErrorEnabled()) { LOGGER.error(message, e); } throw new ServletException(e.getMessage(), e); } catch (final Exception e) { if (LOGGER.isErrorEnabled()) { LOGGER.error(e.getMessage(), e); } throw new ServletException(e.getMessage(), e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/ApplicationModel.java ================================================ /** * Copyright (C) 2022 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.livingapps; import java.util.List; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.ProfileAPI; import org.bonitasoft.engine.business.application.Application; import org.bonitasoft.engine.business.application.ApplicationMenuSearchDescriptor; import org.bonitasoft.engine.business.application.ApplicationPageNotFoundException; import org.bonitasoft.engine.business.application.ApplicationVisibility; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.page.PageNotFoundException; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.profile.ProfileCriterion; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.livingapps.menu.Menu; import org.bonitasoft.livingapps.menu.MenuFactory; public class ApplicationModel { private final ProfileAPI profileApi; private final ApplicationAPI applicationApi; private final PageAPI pageApi; private final Application application; private final MenuFactory factory; public ApplicationModel( final ApplicationAPI applicationApi, final PageAPI pageApi, final ProfileAPI profileApi, final Application application, final MenuFactory factory) { this.applicationApi = applicationApi; this.pageApi = pageApi; this.profileApi = profileApi; this.application = application; this.factory = factory; } public long getId() { return application.getId(); } public String getApplicationLayoutName() throws PageNotFoundException { return pageApi.getPage(application.getLayoutId()).getName(); } public String getApplicationThemeName() throws PageNotFoundException { return pageApi.getPage(application.getThemeId()).getName(); } public Long getApplicationThemeId() throws PageNotFoundException { return pageApi.getPage(application.getThemeId()).getId(); } public String getApplicationHomePage() throws ApplicationPageNotFoundException { return applicationApi.getApplicationHomePage(application.getId()).getToken() + "/"; } public boolean hasPage(final String pageToken) { try { applicationApi.getApplicationPage(application.getToken(), pageToken); return true; } catch (final ApplicationPageNotFoundException e) { return false; } } public boolean authorize(final APISession session) { if (ApplicationVisibility.ALL.equals(application.getVisibility())) { return true; } else if (ApplicationVisibility.TECHNICAL_USER.equals(application.getVisibility())) { return session.isTechnicalUser(); } else { for (final Profile userProfile : getUserProfiles(session)) { if (userProfile.getId() == application.getProfileId()) { return true; } } } return false; } public boolean hasProfileMapped() { if (ApplicationVisibility.RESTRICTED.equals(application.getVisibility())) { return application.getProfileId() != null; } return true; } private List getUserProfiles(final APISession session) { return profileApi.getProfilesForUser( session.getUserId(), 0, Integer.MAX_VALUE, ProfileCriterion.ID_ASC); } public Page getCustomPage(final String pageToken) throws ApplicationPageNotFoundException, PageNotFoundException { return pageApi.getPage(applicationApi.getApplicationPage(application.getToken(), pageToken).getPageId()); } public List

getMenuList() throws SearchException, ApplicationPageNotFoundException { return factory.create(applicationApi.searchApplicationMenus(new SearchOptionsBuilder(0, Integer.MAX_VALUE) .filter(ApplicationMenuSearchDescriptor.APPLICATION_ID, application.getId()) .sort(ApplicationMenuSearchDescriptor.INDEX, Order.ASC).done()) .getResult()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/ApplicationModelFactory.java ================================================ /** * Copyright (C) 2022 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.livingapps; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.ProfileAPI; import org.bonitasoft.engine.business.application.Application; import org.bonitasoft.engine.business.application.ApplicationNotFoundException; import org.bonitasoft.livingapps.exception.CreationException; import org.bonitasoft.livingapps.menu.MenuFactory; public class ApplicationModelFactory { private final ApplicationAPI applicationApi; private final PageAPI customPageApi; private final ProfileAPI profileApi; public ApplicationModelFactory(final ApplicationAPI applicationApi, final PageAPI customPageApi, final ProfileAPI profileApi) { this.applicationApi = applicationApi; this.customPageApi = customPageApi; this.profileApi = profileApi; } public ApplicationModel createApplicationModel(final String applicationToken) throws CreationException { try { var application = applicationApi.getIApplicationByToken(applicationToken); if (!(application instanceof Application)) { throw new CreationException("Only application links were found with name " + applicationToken); } return new ApplicationModel( applicationApi, customPageApi, profileApi, (Application) application, new MenuFactory(applicationApi)); } catch (final ApplicationNotFoundException e) { throw new CreationException("Error while searching for the application " + applicationToken, e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/ApplicationRouter.java ================================================ /** * Copyright (C) 2022 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.livingapps; import java.io.File; import java.io.IOException; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.console.common.server.page.CustomPageRequestModifier; import org.bonitasoft.console.common.server.page.CustomPageService; import org.bonitasoft.console.common.server.page.PageRenderer; import org.bonitasoft.console.common.server.page.ResourceRenderer; import org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.page.PageNotFoundException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.livingapps.exception.CreationException; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; public class ApplicationRouter { private final ApplicationModelFactory applicationModelFactory; protected final CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier(); protected final String THEME_TOKEN = "theme"; public ApplicationRouter(final ApplicationModelFactory applicationModelFactory) { this.applicationModelFactory = applicationModelFactory; } public void route(final HttpServletRequest hsRequest, final HttpServletResponse hsResponse, final APISession session, final PageRenderer pageRenderer, final ResourceRenderer resourceRenderer, final BonitaHomeFolderAccessor bonitaHomeFolderAccessor) throws CreationException, BonitaException, IOException, ServletException, IllegalAccessException, InstantiationException { final ParsedRequest parsedRequest = parse(hsRequest.getContextPath(), hsRequest.getServletPath(), hsRequest.getPathInfo()); //Test if url contain at least application name final List pathSegments = resourceRenderer.getPathSegments(hsRequest.getPathInfo()); if (pathSegments.isEmpty()) { hsResponse.sendError(HttpServletResponse.SC_NOT_FOUND, "The name of the application is required."); return; } if ("API".equals(parsedRequest.getPageToken())) { //Support relative calls to the REST API from the application page using ../API/ String apiPath = "/" + getResourcePathWithoutApplicationToken(hsRequest.getPathInfo(), parsedRequest.getApplicationName()); //security check against directory traversal attack customPageRequestModifier.forwardIfRequestIsAuthorized(hsRequest, hsResponse, "/API", apiPath); } else if ("GET".equals(hsRequest.getMethod())) { displayPageOrResource(hsRequest, hsResponse, session, pageRenderer, resourceRenderer, bonitaHomeFolderAccessor, parsedRequest, pathSegments); } else { hsResponse.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); hsResponse.flushBuffer(); } } protected void displayPageOrResource(final HttpServletRequest hsRequest, final HttpServletResponse hsResponse, final APISession session, final PageRenderer pageRenderer, final ResourceRenderer resourceRenderer, final BonitaHomeFolderAccessor bonitaHomeFolderAccessor, final ParsedRequest parsedRequest, final List pathSegments) throws IOException, InstantiationException, IllegalAccessException, BonitaException, CreationException { final ApplicationModel application = applicationModelFactory .createApplicationModel(parsedRequest.getApplicationName()); if (!application.hasProfileMapped()) { hsResponse.sendError(HttpServletResponse.SC_NOT_FOUND, "No profile mapped to living application"); return; } //If no page name, redirect to Home page if (parsedRequest.getPageToken() == null) { hsResponse.sendRedirect(buildHomePageRouteWithParams(hsRequest, application.getApplicationHomePage())); return; } if (isApplicationPageRequest(pathSegments)) { //Application page request if (!application.hasPage(parsedRequest.getPageToken())) { hsResponse.sendError(HttpServletResponse.SC_NOT_FOUND, "There is no page " + parsedRequest.getPageToken() + " in the application " + parsedRequest.getApplicationName()); return; } if (!application.authorize(session)) { hsResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Unauthorized access for the page " + parsedRequest.getPageToken() + " of the application " + parsedRequest.getApplicationName()); return; } pageRenderer.displayCustomPage(hsRequest, hsResponse, session, application.getApplicationLayoutName()); } else { //Layout or theme resource file request final File resourceFile = getResourceFile(pageRenderer, hsRequest.getPathInfo(), pathSegments, application, bonitaHomeFolderAccessor); pageRenderer .ensurePageFolderIsPresent(session, pageRenderer.getPageResourceProvider(getPageName(pathSegments, application))); resourceRenderer.renderFile(hsRequest, hsResponse, resourceFile); } } private String buildHomePageRouteWithParams(final HttpServletRequest hsRequest, final String applicationHomePage) { final StringBuilder routeWithParamsBuilder = new StringBuilder(applicationHomePage); if (!StringUtil.isBlank(hsRequest.getQueryString())) { routeWithParamsBuilder.append("?").append(hsRequest.getQueryString()); } return routeWithParamsBuilder.toString(); } private boolean isApplicationPageRequest(final List pathSegments) { return pathSegments.size() == 2; } private File getResourceFile(final PageRenderer pageRenderer, final String resourcePath, final List pathSegments, final ApplicationModel application, final BonitaHomeFolderAccessor bonitaHomeFolderAccessor) throws IOException, BonitaException { final String pageName = getPageName(pathSegments, application); final PageResourceProviderImpl pageResourceProvider = pageRenderer.getPageResourceProvider(pageName); final File resourceFile = new File(pageResourceProvider.getPageDirectory(), CustomPageService.RESOURCES_PROPERTY + File.separator + getResourcePath(resourcePath, pathSegments.get(0), pathSegments.get(1))); if (!bonitaHomeFolderAccessor.isInFolder(resourceFile, pageResourceProvider.getPageDirectory())) { throw new BonitaException("Unauthorized access to the file " + resourcePath); } return resourceFile; } private String getPageName(final List pathSegments, final ApplicationModel application) throws PageNotFoundException { String pageName; if (THEME_TOKEN.equals(pathSegments.get(1))) { pageName = application.getApplicationThemeName(); } else { pageName = application.getApplicationLayoutName(); } return pageName; } private String getResourcePath(final String fullResourcePath, final String applicationName, final String pageToken) { //resource path match "/applicationName/pageName/{resourcePath}" // or "/applicationName/theme/{resourcePath}" String resourcePath = getResourcePathWithoutApplicationToken(fullResourcePath, applicationName); resourcePath = getResourcePathWithoutPageToken(resourcePath, pageToken); return resourcePath; } private String getResourcePathWithoutApplicationToken(final String resourcePath, final String applicationName) { //resource path match "/applicationName/{resourcePath}" return resourcePath.substring(applicationName.length() + 2); } private String getResourcePathWithoutPageToken(final String resourcePath, final String pageToken) { return resourcePath.substring(pageToken.length() + 1); } private ParsedRequest parse(final String context, final String servletPath, final String pathInfo) { final Pattern pattern = Pattern.compile("^" + context + "/apps/(.*)$"); final Matcher matcher = pattern.matcher(context + servletPath + pathInfo); if (!matcher.find()) { throw new RuntimeException("URI badly formed."); } final String[] fragments = matcher.group(1).split("/"); String pageToken = null; if (fragments.length > 1) { pageToken = fragments[1]; } return new ParsedRequest(fragments[0], pageToken); } private class ParsedRequest { private final String applicationToken; private final String pageToken; public ParsedRequest(final String applicationToken, final String pageToken) { this.applicationToken = applicationToken; this.pageToken = pageToken; } public String getApplicationName() { return applicationToken; } public String getPageToken() { return pageToken; } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/LivingApplicationPageServlet.java ================================================ /** * Copyright (C) 2022 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.livingapps; import java.io.File; import java.io.IOException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.console.common.server.page.ApplicationAuthorizationsHelper; import org.bonitasoft.console.common.server.page.CustomPageRequestModifier; import org.bonitasoft.console.common.server.page.CustomPageService; import org.bonitasoft.console.common.server.page.PageRenderer; import org.bonitasoft.console.common.server.page.ResourceRenderer; import org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Servlet which displays an application page (iframe below the layout menu) with an URL like * /portal/resource/app///content/ * Note: whenever there is an InvalidSessionException from the engine it performs an HTTP session logout and send a 401 * error. since we are in a iframe, we cannot redirect to the login page. * However, the page should handle the 401 and refresh the top window. */ public class LivingApplicationPageServlet extends HttpServlet { public static final String RESOURCE_PATH_SEPARATOR = "/content"; public static final String API_PATH_SEPARATOR = "/API"; public static final String THEME_PATH_SEPARATOR = "/theme"; /** * uuid */ private static final long serialVersionUID = -5410859017103815654L; /** * Logger */ private static Logger LOGGER = LoggerFactory.getLogger(LivingApplicationPageServlet.class.getName()); protected ResourceRenderer resourceRenderer = new ResourceRenderer(); protected PageRenderer pageRenderer = new PageRenderer(resourceRenderer); protected BonitaHomeFolderAccessor bonitaHomeFolderAccessor = new BonitaHomeFolderAccessor(); protected CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier(); @Override protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { final String pathInfo = request.getPathInfo(); final List pathSegments = resourceRenderer.getPathSegments(pathInfo); if (isValidPathForToken(API_PATH_SEPARATOR, pathSegments)) { //Support relative calls to the REST API from the forms using ../API/ final String apiPath = pathInfo.substring(pathInfo.indexOf(API_PATH_SEPARATOR + "/")); //security check against directory traversal attack customPageRequestModifier.forwardIfRequestIsAuthorized(request, response, API_PATH_SEPARATOR, apiPath); } else { super.service(request, response); } } @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { final String pathInfo = request.getPathInfo(); final HttpSession session = request.getSession(); final APISession apiSession = (APISession) session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY); // Check if requested URL is missing final slash (necessary in order to be able to use relative URLs for resources) if (pathInfo.endsWith(RESOURCE_PATH_SEPARATOR)) { customPageRequestModifier.redirectToValidPageUrl(request, response); return; } String appToken = null; String pageToken = null; String customPageName = null; String resourcePath = null; //Validate mapping key contain "AppToken/PageToken/" and at least one more segment final List pathSegments = resourceRenderer.getPathSegments(pathInfo); if (pathSegments.size() >= 3) { appToken = pathSegments.get(0); pageToken = pathSegments.get(1); if (isValidPathForToken(RESOURCE_PATH_SEPARATOR, pathSegments)) { final String pageMapping = "/" + appToken + "/" + pageToken + RESOURCE_PATH_SEPARATOR + "/"; if (pathInfo.length() > pageMapping.length()) { resourcePath = pathInfo.substring(pageMapping.length()); } boolean isNotResourcePath = isNotResourcePath(resourcePath); customPageName = getCustomPageName(appToken, pageToken, apiSession, request, response, isNotResourcePath); if (StringUtils.isBlank(customPageName)) { if (LOGGER.isWarnEnabled()) { LOGGER.warn("Error while trying to retrieve the application page"); } //the response status is set in the error handling of getCustomPageName return; } try { if (isAuthorized(apiSession, appToken, customPageName)) { if (isNotResourcePath) { pageRenderer.displayCustomPage(request, response, apiSession, customPageName); } else { final File resourceFile = getResourceFile(resourcePath, customPageName); pageRenderer.ensurePageFolderIsPresent(apiSession, pageRenderer.getPageResourceProvider(customPageName)); resourceRenderer.renderFile(request, response, resourceFile); } } else { if (isNotResourcePath) { response.sendError(HttpServletResponse.SC_FORBIDDEN, "User not Authorized"); } else { response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.flushBuffer(); } } } catch (final Exception e) { handleException(customPageName, e, request, response, isNotResourcePath); } } else if (isValidPathForToken(THEME_PATH_SEPARATOR, pathSegments)) { //Support relative calls to the THEME from the application page using ../theme/ final String themeResourcePath = pathInfo.substring(pathInfo.indexOf(THEME_PATH_SEPARATOR + "/")); String appThemeResourcePrefix = "/apps/" + appToken; customPageRequestModifier.forwardIfRequestIsAuthorized(request, response, appThemeResourcePrefix + THEME_PATH_SEPARATOR + "/", appThemeResourcePrefix + themeResourcePath); } else { if (LOGGER.isTraceEnabled()) { LOGGER.trace( "One of the separator '/content', '/theme' or '/API' is expected in the URL after the application token and the page token."); } response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.flushBuffer(); } } else { if (LOGGER.isTraceEnabled()) { LOGGER.trace( "The info path is suppose to contain the application token, the page token and one of the separator '/content', '/theme' or '/API'."); } response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.flushBuffer(); } } private String getCustomPageName(final String appToken, final String pageToken, final APISession apiSession, final HttpServletRequest request, final HttpServletResponse response, final boolean isNotResourcePath) throws ServletException, IOException { try { final Long customPageId = getApplicationApi(apiSession).getApplicationPage(appToken, pageToken).getPageId(); return getPageApi(apiSession).getPage(customPageId).getName(); } catch (final NotFoundException e) { if (LOGGER.isInfoEnabled()) { LOGGER.info("The application page " + appToken + "/" + pageToken + " was not found."); } if (isNotResourcePath) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "Cannot find the page" + pageToken + "for the application" + appToken + "."); } else { response.setStatus(HttpServletResponse.SC_NOT_FOUND); response.flushBuffer(); } } catch (final Exception e) { handleException(appToken + "/" + pageToken, e, request, response, isNotResourcePath); } return ""; } private boolean isValidPathForToken(final String TokenSeparator, final List pathSegments) { return pathSegments.size() > 2 && pathSegments.get(2).equals(TokenSeparator.substring(1)); } private boolean isNotResourcePath(final String resourcePath) { return resourcePath == null || CustomPageService.PAGE_INDEX_FILENAME.equals(resourcePath) || CustomPageService.PAGE_CONTROLLER_FILENAME.equals(resourcePath) || CustomPageService.PAGE_INDEX_NAME.equals(resourcePath); } private File getResourceFile(final String resourcePath, final String pageName) throws IOException, BonitaException { final PageResourceProviderImpl pageResourceProvider = pageRenderer.getPageResourceProvider(pageName); final File resourceFile = new File(pageResourceProvider.getPageDirectory(), CustomPageService.RESOURCES_PROPERTY + File.separator + resourcePath); if (!bonitaHomeFolderAccessor.isInFolder(resourceFile, pageResourceProvider.getPageDirectory())) { throw new BonitaException("Unauthorized access to the file " + resourcePath); } return resourceFile; } private boolean isAuthorized(final APISession apiSession, final String appToken, final String pageName) throws BonitaException { return getCustomPageAuthorizationsHelper(apiSession).isAuthorized(appToken); } private void handleException(final String pageName, final Exception e, final HttpServletRequest request, final HttpServletResponse response, final boolean isNotResourcePath) throws ServletException, IOException { if (e instanceof InvalidSessionException) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Invalid Bonita engine session.", e); } SessionUtil.sessionLogout(request.getSession()); if (isNotResourcePath) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid Bonita engine session."); } else { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.flushBuffer(); } } else { if (LOGGER.isWarnEnabled()) { LOGGER.warn("Error while trying to render the application page " + pageName, e); } throw new ServletException(e.getMessage()); } } protected ApplicationAPI getApplicationApi(final APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getLivingApplicationAPI(apiSession); } protected PageAPI getPageApi(final APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getCustomPageAPI(apiSession); } protected ApplicationAuthorizationsHelper getCustomPageAuthorizationsHelper(final APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return new ApplicationAuthorizationsHelper(apiSession, new ApplicationModelFactory( TenantAPIAccessor.getLivingApplicationAPI(apiSession), TenantAPIAccessor.getCustomPageAPI(apiSession), TenantAPIAccessor.getProfileAPI(apiSession))); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/LivingApplicationServlet.java ================================================ /** * Copyright (C) 2022 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.livingapps; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.console.common.server.auth.AuthenticationManager; import org.bonitasoft.console.common.server.auth.AuthenticationManagerFactory; import org.bonitasoft.console.common.server.auth.AuthenticationManagerNotFoundException; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; import org.bonitasoft.console.common.server.login.utils.LoginUrl; import org.bonitasoft.console.common.server.login.utils.RedirectUrl; import org.bonitasoft.console.common.server.login.utils.RedirectUrlBuilder; import org.bonitasoft.console.common.server.page.CustomPageRequestModifier; import org.bonitasoft.console.common.server.page.PageRenderer; import org.bonitasoft.console.common.server.page.ResourceRenderer; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.business.application.ApplicationPageNotFoundException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.page.PageNotFoundException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.livingapps.exception.CreationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Servlet which displays the application layout with an URL like /apps//* * Note: whenever there is an InvalidSessionException from the engine it performs an HTTP session logout and redirects * to the login page (this is possible because unlike in LivingApplicationPageServlet we are in the top frame). */ public class LivingApplicationServlet extends HttpServlet { private static final long serialVersionUID = -3911437607969651000L; /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(LivingApplicationServlet.class.getName()); protected CustomPageRequestModifier customPageRequestModifier = new CustomPageRequestModifier(); @Override protected void service(final HttpServletRequest hsRequest, final HttpServletResponse hsResponse) throws ServletException, IOException { final APISession session = getSession(hsRequest); // Check if requested URL is missing final slash (necessary in order to be able to use relative URLs for resources) if (isPageUrlWithoutFinalSlash(hsRequest)) { customPageRequestModifier.redirectToValidPageUrl(hsRequest, hsResponse); return; } try { createApplicationRouter(session).route(hsRequest, hsResponse, session, getPageRenderer(), getResourceRenderer(), new BonitaHomeFolderAccessor()); } catch (final ApplicationPageNotFoundException | PageNotFoundException | CreationException e) { hsResponse.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage()); } catch (final BonitaException | IllegalAccessException | InstantiationException e) { if (LOGGER.isWarnEnabled()) { final String message = "Error while trying to display application " + hsRequest.getPathInfo(); LOGGER.warn(message, e); } if (!hsResponse.isCommitted()) { hsResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } else { throw new ServletException(e); } } catch (final InvalidSessionException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Invalid Bonita engine session.", e); } SessionUtil.sessionLogout(hsRequest.getSession()); HttpServletRequestAccessor requestAccessor = new HttpServletRequestAccessor(hsRequest); LoginUrl loginURL = new LoginUrl(getAuthenticationManager(), makeRedirectUrl(requestAccessor).getUrl(), requestAccessor); hsResponse.sendRedirect(loginURL.getLocation()); } } ApplicationRouter createApplicationRouter(final APISession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return new ApplicationRouter(new ApplicationModelFactory( TenantAPIAccessor.getLivingApplicationAPI(session), TenantAPIAccessor.getCustomPageAPI(session), TenantAPIAccessor.getProfileAPI(session))); } private boolean isPageUrlWithoutFinalSlash(final HttpServletRequest request) { return request.getPathInfo() == null || request.getPathInfo().matches("/[^/]+/[^/]+") || request.getPathInfo().matches("/[^/]+"); } APISession getSession(final HttpServletRequest hsRequest) { return (APISession) hsRequest.getSession().getAttribute("apiSession"); } PageRenderer getPageRenderer() { return new PageRenderer(getResourceRenderer()); } ResourceRenderer getResourceRenderer() { return new ResourceRenderer(); } protected RedirectUrl makeRedirectUrl(final HttpServletRequestAccessor httpRequest) { final RedirectUrlBuilder builder = new RedirectUrlBuilder(httpRequest.getRequestedUri()); builder.appendParameters(httpRequest.getParameterMap()); return builder.build(); } // protected for test stubbing protected AuthenticationManager getAuthenticationManager() throws ServletException { try { return AuthenticationManagerFactory.getAuthenticationManager(); } catch (final AuthenticationManagerNotFoundException e) { throw new ServletException(e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/exception/CreationException.java ================================================ /** * Copyright (C) 2022 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.livingapps.exception; public class CreationException extends Exception { public CreationException(String message, Throwable throwable) { super(message, throwable); } public CreationException(String message) { super(message); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/menu/ChildrenMenuCollector.java ================================================ /** * Copyright (C) 2022 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.livingapps.menu; import org.bonitasoft.engine.business.application.ApplicationMenu; import org.bonitasoft.livingapps.menu.MenuFactory.Collector; class ChildrenMenuCollector implements Collector { private final Long parentId; public ChildrenMenuCollector(final Long parentId) { this.parentId = parentId; } @Override public boolean isCollectible(final ApplicationMenu menu) { return parentId.equals(menu.getParentId()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/menu/Menu.java ================================================ /** * Copyright (C) 2022 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.livingapps.menu; public interface Menu { String getHtml(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/menu/MenuContainer.java ================================================ /** * Copyright (C) 2022 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.livingapps.menu; import java.util.List; import org.bonitasoft.engine.business.application.ApplicationMenu; public class MenuContainer implements Menu { private final ApplicationMenu menu; private final List children; public MenuContainer(final ApplicationMenu menu, final List children) { this.menu = menu; this.children = children; } @Override public String getHtml() { final StringBuilder builder = new StringBuilder() .append("
  • ") .append("") .append(menu.getDisplayName()).append(" ") .append("
      "); for (final Menu child : children) { builder.append(child.getHtml()); } return builder.append("
  • ").toString(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/menu/MenuFactory.java ================================================ /** * Copyright (C) 2022 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.livingapps.menu; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.business.application.ApplicationMenu; import org.bonitasoft.engine.business.application.ApplicationPageNotFoundException; import org.bonitasoft.engine.exception.SearchException; public class MenuFactory { public interface Collector { boolean isCollectible(ApplicationMenu item); } private final ApplicationAPI applicationApi; public MenuFactory(final ApplicationAPI applicationApi) { this.applicationApi = applicationApi; } public List create(final List menuList) throws ApplicationPageNotFoundException, SearchException { return collect(menuList, new RootMenuCollector()); } private Menu create(final ApplicationMenu menu, final List menuList) throws ApplicationPageNotFoundException, SearchException { if (menu.getApplicationPageId() == null) { return new MenuContainer(menu, collect(menuList, new ChildrenMenuCollector(menu.getId()))); } return new MenuLink(menu, applicationApi.getApplicationPage(menu.getApplicationPageId()).getToken()); } private List collect(final List items, final Collector collector) throws ApplicationPageNotFoundException, SearchException { final List menuList = new ArrayList<>(); for (final ApplicationMenu item : items) { if (collector.isCollectible(item)) { menuList.add(create(item, items)); } } return menuList; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/menu/MenuLink.java ================================================ /** * Copyright (C) 2022 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.livingapps.menu; import org.bonitasoft.engine.business.application.ApplicationMenu; public class MenuLink implements Menu { private final ApplicationMenu menu; private final String pageToken; public MenuLink(final ApplicationMenu menu, final String pageToken) { this.menu = menu; this.pageToken = pageToken; } @Override public String getHtml() { return "
  • " + menu.getDisplayName() + "
  • "; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/livingapps/menu/RootMenuCollector.java ================================================ /** * Copyright (C) 2022 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.livingapps.menu; import org.bonitasoft.engine.business.application.ApplicationMenu; import org.bonitasoft.livingapps.menu.MenuFactory.Collector; class RootMenuCollector implements Collector { @Override public boolean isCollectible(final ApplicationMenu menu) { return menu.getParentId() == null; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/common/model/ImportStatusMessage.java ================================================ /** * Copyright (C) 2022 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.web.common.model; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author Fabio Lombardi */ public class ImportStatusMessage implements Serializable { private static final long serialVersionUID = 1L; private Map> errors; private String name; private final String statusType; public ImportStatusMessage(final String name, final String statusType) { errors = new HashMap<>(); this.name = name; this.statusType = statusType; } public void addError(final String elementType, final String errorMessage) { if (!errors.containsKey(elementType)) { errors.put(elementType, new ArrayList<>()); } errors.get(elementType).add(errorMessage); } public void setName(final String name) { this.name = name; } public Map> getErrors() { return errors; } public void setErrors(final Map> errors) { this.errors = errors; } public String getName() { return name; } public String getStatusType() { return statusType; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/common/model/ImportStatusMessages.java ================================================ /** * Copyright (C) 2022 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.web.common.model; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.api.ImportError; import org.bonitasoft.engine.api.ImportStatus; /** * @author Fabio Lombardi */ public class ImportStatusMessages implements Serializable { private static final long serialVersionUID = 1L; private final List errors = new ArrayList<>(); private final List imported = new ArrayList<>(); private final List skipped = new ArrayList<>(); public ImportStatusMessages(final List statusMessages) { setImportStatus(statusMessages); } public void addResourceImported(final ImportStatusMessage profileImportStatusMessage) { imported.add(profileImportStatusMessage); } public void addResourceSkipped(final ImportStatusMessage profileImportStatusMessage) { skipped.add(profileImportStatusMessage); } public void addResourceInError(final ImportStatusMessage profileImportStatusMessage) { errors.add(profileImportStatusMessage); } public List getErrors() { return errors; } public List getImported() { return imported; } public List getSkipped() { return skipped; } public void setImportStatus(final List statusMessages) { for (final ImportStatus statusMessage : statusMessages) { if (statusMessage.getErrors().size() > 0) { addResourceInError(convertImportStatus(statusMessage)); } else if (statusMessage.getStatus().equals(ImportStatus.Status.REPLACED) || statusMessage.getStatus().equals(ImportStatus.Status.ADDED)) { addResourceImported(convertImportStatus(statusMessage)); } else if (statusMessage.getStatus().equals(ImportStatus.Status.SKIPPED)) { addResourceSkipped(convertImportStatus(statusMessage)); } } } private ImportStatusMessage convertImportStatus(final ImportStatus importStatus) { final ImportStatusMessage importStatusMessage = new ImportStatusMessage(importStatus.getName(), importStatus.getStatus().toString()); final List errors = importStatus.getErrors(); for (final ImportError error : errors) { importStatusMessage.addError(error.getType().toString(), error.getName()); } return importStatusMessage; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/ModelFactory.java ================================================ /** * Copyright (C) 2022 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.web.rest.model; import org.bonitasoft.web.rest.model.application.AbstractApplicationDefinition; import org.bonitasoft.web.rest.model.application.AbstractApplicationItem; import org.bonitasoft.web.rest.model.application.ApplicationDefinition; import org.bonitasoft.web.rest.model.application.ApplicationLinkDefinition; import org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuDefinition; import org.bonitasoft.web.rest.model.applicationpage.ApplicationPageDefinition; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDefinition; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentDefinition; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCommentDefinition; import org.bonitasoft.web.rest.model.bpm.cases.CaseDefinition; import org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentDefinition; import org.bonitasoft.web.rest.model.bpm.cases.CaseVariableDefinition; import org.bonitasoft.web.rest.model.bpm.cases.CommentDefinition; import org.bonitasoft.web.rest.model.bpm.connector.ArchivedConnectorInstanceDefinition; import org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.ActivityDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedUserTaskDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.TaskDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.UserTaskDefinition; import org.bonitasoft.web.rest.model.bpm.process.ActorDefinition; import org.bonitasoft.web.rest.model.bpm.process.ActorMemberDefinition; import org.bonitasoft.web.rest.model.bpm.process.CategoryDefinition; import org.bonitasoft.web.rest.model.bpm.process.ProcessCategoryDefinition; import org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDefinition; import org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyDefinition; import org.bonitasoft.web.rest.model.bpm.process.ProcessDefinition; import org.bonitasoft.web.rest.model.bpm.process.ProcessParameterDefinition; import org.bonitasoft.web.rest.model.bpm.process.ProcessResolutionProblemDefinition; import org.bonitasoft.web.rest.model.document.ArchivedDocumentDefinition; import org.bonitasoft.web.rest.model.document.DocumentDefinition; import org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinition; import org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinitionDefinition; import org.bonitasoft.web.rest.model.identity.CustomUserInfoValueDefinition; import org.bonitasoft.web.rest.model.identity.GroupDefinition; import org.bonitasoft.web.rest.model.identity.MembershipDefinition; import org.bonitasoft.web.rest.model.identity.PersonalContactDataDefinition; import org.bonitasoft.web.rest.model.identity.ProfessionalContactDataDefinition; import org.bonitasoft.web.rest.model.identity.RoleDefinition; import org.bonitasoft.web.rest.model.identity.UserDefinition; import org.bonitasoft.web.rest.model.platform.PlatformDefinition; import org.bonitasoft.web.rest.model.portal.page.PageDefinition; import org.bonitasoft.web.rest.model.portal.profile.ProfileDefinition; import org.bonitasoft.web.rest.model.portal.profile.ProfileMemberDefinition; import org.bonitasoft.web.rest.model.tenant.BusinessDataModelDefinition; import org.bonitasoft.web.toolkit.client.ItemDefinitionFactory; import org.bonitasoft.web.toolkit.client.common.session.SessionDefinition; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Yongtao Guo */ public class ModelFactory extends ItemDefinitionFactory { @Override public ItemDefinition defineItemDefinitions(final String token) { // organization if (UserDefinition.TOKEN.equals(token)) { return new UserDefinition(); } else if (PersonalContactDataDefinition.TOKEN.equals(token)) { return new PersonalContactDataDefinition(); } else if (ProfessionalContactDataDefinition.TOKEN.equals(token)) { return new ProfessionalContactDataDefinition(); } else if (RoleDefinition.TOKEN.equals(token)) { return new RoleDefinition(); } else if (GroupDefinition.TOKEN.equals(token)) { return new GroupDefinition(); } else if (MembershipDefinition.TOKEN.equals(token)) { return new MembershipDefinition(); } else if (CustomUserInfoDefinition.TOKEN.equals(token)) { return new CustomUserInfoDefinition(); } else if (CustomUserInfoDefinitionDefinition.TOKEN.equals(token)) { return new CustomUserInfoDefinitionDefinition(); } else if (CustomUserInfoValueDefinition.TOKEN.equals(token)) { return new CustomUserInfoValueDefinition(); } // bpm.process else if (ProcessDefinition.TOKEN.equals(token)) { return new ProcessDefinition(); } else if (ProcessConnectorDefinition.TOKEN.equals(token)) { return new ProcessConnectorDefinition(); } else if (ProcessConnectorDependencyDefinition.TOKEN.equals(token)) { return new ProcessConnectorDependencyDefinition(); } else if (ProcessCategoryDefinition.TOKEN.equals(token)) { return new ProcessCategoryDefinition(); } else if (ActorDefinition.TOKEN.equals(token)) { return new ActorDefinition(); } else if (ActorMemberDefinition.TOKEN.equals(token)) { return new ActorMemberDefinition(); } else if (CategoryDefinition.TOKEN.equals(token)) { return new CategoryDefinition(); } else if (ProcessResolutionProblemDefinition.TOKEN.equals(token)) { return new ProcessResolutionProblemDefinition(); } else if (ProcessParameterDefinition.TOKEN.equals(token)) { return new ProcessParameterDefinition(); } // bpm.cases else if (CaseDefinition.TOKEN.equals(token)) { return new CaseDefinition(); } else if (CommentDefinition.TOKEN.equals(token)) { return new CommentDefinition(); } else if (ArchivedCommentDefinition.TOKEN.equals(token)) { return new ArchivedCommentDefinition(); } else if (ArchivedCaseDefinition.TOKEN.equals(token)) { return new ArchivedCaseDefinition(); } else if (CaseVariableDefinition.TOKEN.equals(token)) { return new CaseVariableDefinition(); } else if (CaseDocumentDefinition.TOKEN.equals(token)) { return new CaseDocumentDefinition(); } else if (ArchivedCaseDocumentDefinition.TOKEN.equals(token)) { return new CaseDocumentDefinition(); } // bpm.flownode else if (FlowNodeDefinition.TOKEN.equals(token)) { return new FlowNodeDefinition(); } else if (ActivityDefinition.TOKEN.equals(token)) { return new ActivityDefinition(); } else if (TaskDefinition.TOKEN.equals(token)) { return new TaskDefinition(); } else if (HumanTaskDefinition.TOKEN.equals(token)) { return new HumanTaskDefinition(); } else if (UserTaskDefinition.TOKEN.equals(token)) { return new UserTaskDefinition(); } else if (ConnectorInstanceDefinition.TOKEN.equals(token)) { return new ConnectorInstanceDefinition(); } // bpm.flownode.archive else if (ArchivedFlowNodeDefinition.TOKEN.equals(token)) { return new ArchivedFlowNodeDefinition(); } else if (ArchivedActivityDefinition.TOKEN.equals(token)) { return new ArchivedActivityDefinition(); } else if (ArchivedTaskDefinition.TOKEN.equals(token)) { return new ArchivedTaskDefinition(); } else if (ArchivedHumanTaskDefinition.TOKEN.equals(token)) { return new ArchivedHumanTaskDefinition(); } else if (ArchivedUserTaskDefinition.TOKEN.equals(token)) { return new ArchivedUserTaskDefinition(); } else if (ArchivedConnectorInstanceDefinition.TOKEN.equals(token)) { return new ArchivedConnectorInstanceDefinition(); } // system else if (ProfileDefinition.TOKEN.equals(token)) { return new ProfileDefinition(); } else if (ProfileMemberDefinition.TOKEN.equals(token)) { return new ProfileMemberDefinition(); } else if (SessionDefinition.TOKEN.equals(token)) { return new SessionDefinition(); } // platform else if (PlatformDefinition.TOKEN.equals(token)) { return new PlatformDefinition(); } // documents else if (DocumentDefinition.TOKEN.equals(token)) { return new DocumentDefinition(); } else if (ArchivedDocumentDefinition.TOKEN.equals(token)) { return new ArchivedDocumentDefinition(); } // Pages else if (PageDefinition.TOKEN.equals(token)) { return new PageDefinition(); } //Applications else if (AbstractApplicationDefinition.TOKEN.equals(token)) { return new AbstractApplicationDefinition(); } else if (ApplicationLinkDefinition.TOKEN.equals(token)) { return new ApplicationLinkDefinition(); } else if (ApplicationDefinition.TOKEN.equals(token)) { return new ApplicationDefinition(); } else if (ApplicationPageDefinition.TOKEN.equals(token)) { return new ApplicationPageDefinition(); } else if (ApplicationMenuDefinition.TOKEN.equals(token)) { return new ApplicationMenuDefinition(); } //tenant else if (BusinessDataModelDefinition.TOKEN.equals(token)) { return new BusinessDataModelDefinition(); } // default else { return null; } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/AbstractApplicationDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.application; import java.util.Collections; import java.util.Map; import java.util.Optional; import java.util.function.Supplier; import org.bonitasoft.web.toolkit.client.common.TreeIndexed; import org.bonitasoft.web.toolkit.client.common.TreeLeaf; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.DiscriminatedItemDefinitionHelper; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; import org.bonitasoft.web.toolkit.client.data.item.attribute.ValidationError; import org.bonitasoft.web.toolkit.client.data.item.attribute.ValidationException; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.FileIsImageOrServletPathValidator; /** * Item definition for a Bonita Living Application for the REST API (either legacy or link). */ public class AbstractApplicationDefinition extends ItemDefinition { public static final String TOKEN = "abstractApplication"; @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return "../API/living/application"; } @Override protected void defineAttributes() { createAttribute(AbstractApplicationItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(AbstractApplicationItem.ATTRIBUTE_LINK, ItemAttribute.TYPE.BOOLEAN); createAttribute(AbstractApplicationItem.ATTRIBUTE_TOKEN, ItemAttribute.TYPE.STRING); createAttribute(AbstractApplicationItem.ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.STRING); createAttribute(AbstractApplicationItem.ATTRIBUTE_PROFILE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(AbstractApplicationItem.ATTRIBUTE_VERSION, ItemAttribute.TYPE.STRING); createAttribute(AbstractApplicationItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT); createAttribute(AbstractApplicationItem.ATTRIBUTE_ICON, ItemAttribute.TYPE.STRING) .addValidator(new FileIsImageOrServletPathValidator(ApplicationItem.ICON_PATH_API_PREFIX)); createAttribute(AbstractApplicationItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.STRING); createAttribute(AbstractApplicationItem.ATTRIBUTE_CREATED_BY, ItemAttribute.TYPE.ITEM_ID); createAttribute(AbstractApplicationItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.STRING); createAttribute(AbstractApplicationItem.ATTRIBUTE_UPDATED_BY, ItemAttribute.TYPE.ITEM_ID); createAttribute(AbstractApplicationItem.ATTRIBUTE_STATE, ItemAttribute.TYPE.STRING); createAttribute(AbstractApplicationItem.ATTRIBUTE_VISIBILITY, ItemAttribute.TYPE.STRING); createAttribute(ApplicationItem.ATTRIBUTE_EDITABLE, ItemAttribute.TYPE.BOOLEAN); } @Override protected void definePrimaryKeys() { setPrimaryKeys(AbstractApplicationItem.ATTRIBUTE_ID); } public static AbstractApplicationDefinition get() { return (AbstractApplicationDefinition) Definitions.get(TOKEN); } @Override protected ITEM _createItem() { // this must not be called by deprecated PUT and POST methods which need to discriminate on "link". throw new ValidationException(Collections.singletonList( new ValidationError("link", "%attribute% is mandatory to discriminate the application type"))); } @Override public Optional> getDiscriminatedHelper() { if (AbstractApplicationDefinition.class.equals(getClass())) { return Optional.of(new DiscriminatedItemDefinitionHelper() { @Override @SuppressWarnings("unchecked") public Supplier findItemCreator(Map attributes) { // We need the "link" attribute to discriminate between legacy application and application link. boolean isLink = attributes != null && Boolean.parseBoolean(attributes.get(AbstractApplicationItem.ATTRIBUTE_LINK)); return isLink ? () -> (ITEM) ApplicationLinkDefinition.get()._createItem() : () -> (ITEM) ApplicationDefinition.get()._createItem(); } @Override public Supplier findItemCreator(TreeIndexed tree) { // We need the "link" attribute to discriminate between legacy application and application link. boolean isLink; if (tree != null && tree.get(AbstractApplicationItem.ATTRIBUTE_LINK) instanceof TreeLeaf v) { isLink = Optional.ofNullable(v.getValue()).map(Object::toString).map(Boolean::parseBoolean) .orElse(Boolean.FALSE); } else { isLink = false; } return isLink ? () -> (ITEM) ApplicationLinkDefinition.get()._createItem() : () -> (ITEM) ApplicationDefinition.get()._createItem(); } }); } else { // subclasses do not need the helper and use a concrete implementation return Optional.empty(); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/AbstractApplicationItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.application; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * Contains the meta information of a Bonita Living Application for the REST API. */ public abstract class AbstractApplicationItem extends Item implements ItemHasUniqueId, ItemHasIcon { /** * This attributes is used to distinguish application links from legacy * applications. It can be used for search, filter or ordering. */ public static final String ATTRIBUTE_LINK = "link"; public static final String ATTRIBUTE_TOKEN = "token"; public static final String ATTRIBUTE_DISPLAY_NAME = "displayName"; public static final String ATTRIBUTE_VERSION = "version"; public static final String ATTRIBUTE_DESCRIPTION = "description"; public static final String ATTRIBUTE_CREATION_DATE = "creationDate"; public static final String ATTRIBUTE_CREATED_BY = "createdBy"; public static final String ATTRIBUTE_LAST_UPDATE_DATE = "lastUpdateDate"; public static final String ATTRIBUTE_UPDATED_BY = "updatedBy"; public static final String ATTRIBUTE_STATE = "state"; public static final String ATTRIBUTE_PROFILE_ID = "profileId"; public static final String ATTRIBUTE_VISIBILITY = "visibility"; public static final String ATTRIBUTE_EDITABLE = "editable"; public static final String FILTER_USER_ID = "userId"; public static final String ICON_PATH_API_PREFIX = "../API/applicationIcon/"; public AbstractApplicationItem() { super(); setAttribute(ATTRIBUTE_LINK, isLink()); } public AbstractApplicationItem(final IItem item) { super(item); setAttribute(ATTRIBUTE_LINK, isLink()); } @Override public ApplicationDefinition getItemDefinition() { return ApplicationDefinition.get(); } @Override public void setId(final String id) { setId(APIID.makeAPIID(id)); } @Override public void setId(final Long id) { setId(APIID.makeAPIID(id)); } /* * #isLink should be implemented by subclasses with a static result depending on the application nature. */ public abstract boolean isLink(); public String getToken() { return getAttributeValue(ATTRIBUTE_TOKEN); } public void setToken(final String token) { setAttribute(ATTRIBUTE_TOKEN, token); } public String getDisplayName() { return getAttributeValue(ATTRIBUTE_DISPLAY_NAME); } public void setDisplayName(final String name) { setAttribute(ATTRIBUTE_DISPLAY_NAME, name); } public String getVersion() { return getAttributeValue(ATTRIBUTE_VERSION); } public void setVersion(final String version) { setAttribute(ATTRIBUTE_VERSION, version); } public String getDescription() { return getAttributeValue(ATTRIBUTE_DESCRIPTION); } public void setDescription(final String description) { setAttribute(ATTRIBUTE_DESCRIPTION, description); } @Override public String getIcon() { return getAttributeValue(ATTRIBUTE_ICON); } @Override public void setIcon(String icon) { setAttribute(ATTRIBUTE_ICON, icon); } public String getCreationDate() { return getAttributeValue(ATTRIBUTE_CREATION_DATE); } public void setCreationDate(final String creationDate) { setAttribute(ATTRIBUTE_CREATION_DATE, creationDate); } public long getCreatedBy() { return getAttributeValueAsLong(ATTRIBUTE_CREATED_BY); } public void setCreatedBy(final long createdBy) { setAttribute(ATTRIBUTE_CREATED_BY, createdBy); } public String getLastUpdateDate() { return getAttributeValue(ATTRIBUTE_LAST_UPDATE_DATE); } public void setLastUpdateDate(final String lastUpdateDate) { setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, lastUpdateDate); } public long getUpdatedBy() { return getAttributeValueAsLong(ATTRIBUTE_UPDATED_BY); } public void setUpdatedBy(final long updatedBy) { setAttribute(ATTRIBUTE_UPDATED_BY, updatedBy); } public String getState() { return getAttributeValue(ATTRIBUTE_STATE); } public void setState(final String state) { setAttribute(ATTRIBUTE_STATE, state); } public APIID getProfileId() { return getAttributeValueAsAPIID(ATTRIBUTE_PROFILE_ID); } public void setProfileId(final Long profileId) { setAttribute(ATTRIBUTE_PROFILE_ID, profileId); } public APIID getUserId() { return getAttributeValueAsAPIID(FILTER_USER_ID); } public void setUserId(final String userId) { setAttribute(FILTER_USER_ID, userId); } public String getVisibility() { return getAttributeValue(ATTRIBUTE_VISIBILITY); } /** FIXME Use Enum instead of String after removing GWT permutations */ public void setVisibility(final String visibility) { setAttribute(ATTRIBUTE_VISIBILITY, visibility); } public boolean isEditable() { return Boolean.parseBoolean(getAttributeValue(ATTRIBUTE_EDITABLE)); } public void setEditable(final boolean isEditable) { setAttribute(ATTRIBUTE_EDITABLE, String.valueOf(isEditable)); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/ApplicationDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.application; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * Item definition for a Legacy Bonita Living Application for the REST API. */ public class ApplicationDefinition extends AbstractApplicationDefinition { public static final String TOKEN = "application"; @Override protected String defineToken() { return TOKEN; } @Override protected void defineAttributes() { super.defineAttributes(); createAttribute(ApplicationItem.ATTRIBUTE_HOME_PAGE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ApplicationItem.ATTRIBUTE_LAYOUT_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ApplicationItem.ATTRIBUTE_THEME_ID, ItemAttribute.TYPE.ITEM_ID); } @Override protected ApplicationItem _createItem() { return new ApplicationItem(); } public static ApplicationDefinition get() { return (ApplicationDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/ApplicationItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.application; import org.bonitasoft.web.rest.model.portal.page.PageItem; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * Contains the meta information of a legacy Bonita Living Application for the REST API. */ public class ApplicationItem extends AbstractApplicationItem implements ItemHasUniqueId, ItemHasIcon { public static final String ATTRIBUTE_HOME_PAGE_ID = "homePageId"; public static final String ATTRIBUTE_LAYOUT_ID = "layoutId"; public static final String ATTRIBUTE_THEME_ID = "themeId"; public ApplicationItem() { super(); } public ApplicationItem(final IItem item) { super(item); } @Override public boolean isLink() { return false; } public APIID getHomePageId() { return getAttributeValueAsAPIID(ATTRIBUTE_HOME_PAGE_ID); } public void setHomePageId(final Long homePageId) { setAttribute(ATTRIBUTE_HOME_PAGE_ID, homePageId); } public APIID getLayoutId() { return getAttributeValueAsAPIID(ATTRIBUTE_LAYOUT_ID); } public void setLayoutId(final Long layoutId) { setAttribute(ATTRIBUTE_LAYOUT_ID, layoutId); } public APIID getThemeId() { return getAttributeValueAsAPIID(ATTRIBUTE_THEME_ID); } public void setThemeId(final Long themeId) { setAttribute(ATTRIBUTE_THEME_ID, themeId); } public PageItem getLayout() { return (PageItem) getDeploy(ATTRIBUTE_LAYOUT_ID); } public PageItem getTheme() { return (PageItem) getDeploy(ATTRIBUTE_THEME_ID); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/ApplicationLinkDefinition.java ================================================ /** * 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.web.rest.model.application; import org.bonitasoft.web.toolkit.client.data.item.Definitions; /** * Item definition for an Bonita Living Application Link for the REST API. */ public class ApplicationLinkDefinition extends AbstractApplicationDefinition { public static final String TOKEN = "applicationLink"; @Override protected String defineToken() { return TOKEN; } @Override protected ApplicationLinkItem _createItem() { return new ApplicationLinkItem(); } public static ApplicationLinkDefinition get() { return (ApplicationLinkDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/ApplicationLinkItem.java ================================================ /** * 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.web.rest.model.application; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * Contains the meta information of a Bonita Living Application Link for the REST API. */ public class ApplicationLinkItem extends AbstractApplicationItem implements ItemHasUniqueId, ItemHasIcon { public ApplicationLinkItem() { super(); } public ApplicationLinkItem(final IItem item) { super(item); } @Override public boolean isLink() { return true; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/applicationmenu/ApplicationMenuDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.applicationmenu; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Julien Mege */ public class ApplicationMenuDefinition extends ItemDefinition { public static final String TOKEN = "applicationmenu"; @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return "../API/living/application-menu"; } @Override protected void defineAttributes() { createAttribute(ApplicationMenuItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ApplicationMenuItem.ATTRIBUTE_APPLICATION_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ApplicationMenuItem.ATTRIBUTE_PARENT_MENU_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ApplicationMenuItem.ATTRIBUTE_MENU_INDEX, ItemAttribute.TYPE.INTEGER); createAttribute(ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.STRING); } @Override protected void definePrimaryKeys() { setPrimaryKeys(ApplicationMenuItem.ATTRIBUTE_ID); } @Override protected ApplicationMenuItem _createItem() { return new ApplicationMenuItem(); } public static ApplicationMenuDefinition get() { return (ApplicationMenuDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/applicationmenu/ApplicationMenuItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.applicationmenu; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Julien Mege */ public class ApplicationMenuItem extends Item implements ItemHasUniqueId { public static final String ATTRIBUTE_DISPLAY_NAME = "displayName"; public static final String ATTRIBUTE_APPLICATION_ID = "applicationId"; public static final String ATTRIBUTE_APPLICATION_PAGE_ID = "applicationPageId"; public static final String ATTRIBUTE_PARENT_MENU_ID = "parentMenuId"; public static final String ATTRIBUTE_MENU_INDEX = "menuIndex"; @Override public void setId(final String id) { this.setAttribute(ATTRIBUTE_ID, id); } @Override public void setId(final Long id) { this.setId(id.toString()); } public void setDisplayName(final String displayName) { setAttribute(ATTRIBUTE_DISPLAY_NAME, displayName); } public String getDisplayName() { return getAttributeValue(ATTRIBUTE_DISPLAY_NAME); } public void setApplicationPageId(final String id) { this.setAttribute(ATTRIBUTE_APPLICATION_PAGE_ID, id); } public void setApplicationPageId(final Long id) { this.setAttribute(ATTRIBUTE_APPLICATION_PAGE_ID, id.toString()); } public APIID getApplicationPageId() { return getAttributeValueAsAPIID(ATTRIBUTE_APPLICATION_PAGE_ID); } public void setApplicationId(final String id) { this.setAttribute(ATTRIBUTE_APPLICATION_ID, id); } public void setApplicationId(final Long id) { this.setAttribute(ATTRIBUTE_APPLICATION_ID, id.toString()); } public APIID getApplicationId() { return getAttributeValueAsAPIID(ATTRIBUTE_APPLICATION_ID); } public void setParentMenuId(final String id) { this.setAttribute(ATTRIBUTE_PARENT_MENU_ID, id); } public void setParentMenuId(final Long id) { this.setAttribute(ATTRIBUTE_PARENT_MENU_ID, id.toString()); } public APIID getParentMenuId() { return getAttributeValueAsAPIID(ATTRIBUTE_PARENT_MENU_ID); } public void setMenuIndex(final int id) { this.setAttribute(ATTRIBUTE_MENU_INDEX, Integer.toString(id)); } public Integer getMenuIndex() { return StringUtil.toInteger(getAttributeValue(ATTRIBUTE_MENU_INDEX)); } @Override public ApplicationMenuDefinition getItemDefinition() { return ApplicationMenuDefinition.get(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/applicationpage/ApplicationPageDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.applicationpage; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Julien Mege */ public class ApplicationPageDefinition extends ItemDefinition { public static final String TOKEN = "applicationpage"; @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return "../API/living/application-page"; } @Override protected void defineAttributes() { createAttribute(ApplicationPageItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ApplicationPageItem.ATTRIBUTE_TOKEN, ItemAttribute.TYPE.STRING); createAttribute(ApplicationPageItem.ATTRIBUTE_APPLICATION_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ApplicationPageItem.ATTRIBUTE_PAGE_ID, ItemAttribute.TYPE.ITEM_ID); } @Override protected void definePrimaryKeys() { setPrimaryKeys(ApplicationPageItem.ATTRIBUTE_ID); } @Override protected ApplicationPageItem _createItem() { return new ApplicationPageItem(); } public static ApplicationPageDefinition get() { return (ApplicationPageDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/applicationpage/ApplicationPageItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.applicationpage; import org.bonitasoft.web.rest.model.application.ApplicationItem; import org.bonitasoft.web.rest.model.portal.page.PageItem; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Julien Mege */ public class ApplicationPageItem extends Item implements ItemHasUniqueId { /* Token use to access the Page using URL : ../appName/pageToken/ */ public static final String ATTRIBUTE_TOKEN = "token"; public static final String ATTRIBUTE_APPLICATION_ID = "applicationId"; public static final String ATTRIBUTE_PAGE_ID = "pageId"; @Override public ApplicationPageDefinition getItemDefinition() { return ApplicationPageDefinition.get(); } @Override public void setId(final String id) { setId(APIID.makeAPIID(id)); } @Override public void setId(final Long id) { setId(APIID.makeAPIID(id)); } public String getToken() { return getAttributeValue(ATTRIBUTE_TOKEN); } public void setToken(final String name) { setAttribute(ATTRIBUTE_TOKEN, name); } public APIID getApplicationId() { return getAttributeValueAsAPIID(ATTRIBUTE_APPLICATION_ID); } public ApplicationItem getApplication() { return new ApplicationItem(getDeploy(ATTRIBUTE_APPLICATION_ID)); } public void setApplicationId(final Long appId) { setAttribute(ATTRIBUTE_APPLICATION_ID, appId); } public APIID getPageId() { return getAttributeValueAsAPIID(ATTRIBUTE_PAGE_ID); } public PageItem getPage() { return new PageItem(getDeploy(ATTRIBUTE_PAGE_ID)); } public void setPageId(final Long pageId) { setAttribute(ATTRIBUTE_PAGE_ID, pageId); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bdm/BusinessDataModelItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bdm; import java.io.Serial; import java.io.Serializable; import lombok.Getter; import lombok.Setter; @Setter @Getter public class BusinessDataModelItem implements Serializable { @Serial private static final long serialVersionUID = -6119736077951213751L; private String fileUpload; } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/AbstractDocumentDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Paul AMAR */ public abstract class AbstractDocumentDefinition extends ItemDefinition { /** * Default Constructor. */ public AbstractDocumentDefinition() { super(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/AbstractDocumentItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm; import java.util.Date; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Paul AMAR */ public abstract class AbstractDocumentItem extends Item implements ItemHasUniqueId { public static final String ATTRIBUTE_NAME = "name"; public static final String ATTRIBUTE_AUTHOR = "author"; public static final String ATTRIBUTE_CREATION_DATE = "creationDate"; public static final String ATTRIBUTE_HAS_CONTENT = "hasContent"; public static final String ATTRIBUTE_FILENAME = "filename"; public static final String ATTRIBUTE_CONTENT_MIME_TYPE = "contentMimetype"; public static final String ATTRIBUTE_FILE = "file"; public static final String ATTRIBUTE_URL = "url"; // ///////////////////////////////////////////////////////////////////////////////// // / FILTER // ///////////////////////////////////////////////////////////////////////////////// public static final String FILTER_SUPERVISOR_ID = "supervisor_id"; // ///////////////////////////////////////////////////////////////////////////////// // / GETTERS // ///////////////////////////////////////////////////////////////////////////////// public String getName() { return this.getAttributeValue(ATTRIBUTE_NAME); } public String getFileName() { return this.getAttributeValue(ATTRIBUTE_FILENAME); } public String getContentMimeType() { return this.getAttributeValue(ATTRIBUTE_CONTENT_MIME_TYPE); } public APIID getAuthor() { return this.getAttributeValueAsAPIID(ATTRIBUTE_AUTHOR); } public String getContentFileName() { return this.getAttributeValue(ATTRIBUTE_FILENAME); } public Date getCreationDate() { return this.getAttributeValueAsDate(ATTRIBUTE_CREATION_DATE); } public String getUrl() { return this.getAttributeValue(ATTRIBUTE_URL); } public String getFile() { return this.getAttributeValue(ATTRIBUTE_FILE); } public boolean hasContent() { return StringUtil.toBoolean(this.getAttributeValue(ATTRIBUTE_HAS_CONTENT)); } // ////////////////////////////////////////////////////////////////////////////////// // / SETTERS // ///////////////////////////////////////////////////////////////////////////////// @Override public void setId(final String id) { this.setAttribute(ATTRIBUTE_ID, id); } @Override public void setId(final Long id) { this.setAttribute(ATTRIBUTE_ID, id); } public void setHasContent(final boolean hasContent) { this.setAttribute(ATTRIBUTE_NAME, hasContent); } public void setContentMimeType(final String contentMimeType) { this.setAttribute(ATTRIBUTE_CONTENT_MIME_TYPE, contentMimeType); } public void setFileName(final String fileName) { this.setAttribute(ATTRIBUTE_FILENAME, fileName); } public void setUrl(final String url) { this.setAttribute(ATTRIBUTE_URL, url); } public void setAuthor(final long author) { this.setAttribute(ATTRIBUTE_AUTHOR, author); } public void setAuthor(final APIID author) { this.setAttribute(ATTRIBUTE_AUTHOR, author); } public void setAuthor(final String author) { this.setAttribute(ATTRIBUTE_AUTHOR, author); } public void setCreationDate(final Date creationDate) { this.setAttribute(ATTRIBUTE_CREATION_DATE, creationDate); } public void setFile(final String file) { this.setAttribute(ATTRIBUTE_FILE, file); } public void setName(final String name) { this.setAttribute(ATTRIBUTE_NAME, name); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedActivityVariable.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import java.io.Serializable; import org.bonitasoft.engine.bpm.data.ArchivedDataInstance; public class ArchivedActivityVariable extends ArchivedVariable { /** * ID of the container this variable belongs to */ private String containerId; /** * Type of the container this variable belongs to */ private String containerType; public String getContainerType() { return containerType; } public void setContainerType(String containerType) { this.containerType = containerType; } public String getContainerId() { return containerId; } public void setContainerId(String containerId) { this.containerId = containerId; } public static ArchivedActivityVariable create(ArchivedDataInstance archivedProcessDataInstance) { var instance = new ArchivedActivityVariable(); instance.setName(archivedProcessDataInstance.getName()); instance.setContainerId(String.valueOf(archivedProcessDataInstance.getContainerId())); instance.setDescription(archivedProcessDataInstance.getDescription()); instance.setType(archivedProcessDataInstance.getClassName()); instance.setContainerType(archivedProcessDataInstance.getContainerType()); Serializable value = archivedProcessDataInstance.getValue(); instance.setValue(value == null ? null : String.valueOf(value)); instance.setArchivedDate(archivedProcessDataInstance.getArchiveDate()); archivedProcessDataInstance.getContainerType(); instance.setSourceObjectId(String.valueOf(archivedProcessDataInstance.getSourceObjectId())); return instance; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedCaseDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * Archived process instance definition * * @author Séverin Moussel */ public class ArchivedCaseDefinition extends ItemDefinition { /** * Singleton */ public static ArchivedCaseDefinition get() { return (ArchivedCaseDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "archivedcases"; /** * the URL of user resource */ protected static final String API_URL = "../API/bpm/archivedCase"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { setPrimaryKeys(CaseItem.ATTRIBUTE_ID); } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(ArchivedCaseItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ArchivedCaseItem.ATTRIBUTE_STATE, ItemAttribute.TYPE.ENUM); createAttribute(ArchivedCaseItem.ATTRIBUTE_START_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(ArchivedCaseItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(ArchivedCaseItem.ATTRIBUTE_END_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(ArchivedCaseItem.ATTRIBUTE_PROCESS_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ArchivedCaseItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(ArchivedCaseItem.ATTRIBUTE_STARTED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ArchivedCaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_1_LABEL, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_2_LABEL, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_3_LABEL, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_4_LABEL, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_5_LABEL, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_1_VALUE, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_2_VALUE, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_3_VALUE, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_4_VALUE, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_5_VALUE, ItemAttribute.TYPE.STRING); } @Override public ArchivedCaseItem _createItem() { return new ArchivedCaseItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedCaseDocumentDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Fabio Lombardi */ public class ArchivedCaseDocumentDefinition extends ItemDefinition { /** * Singleton */ public static ArchivedCaseDocumentDefinition get() { return (ArchivedCaseDocumentDefinition) Definitions.get(TOKEN); } /** * token */ public static final String TOKEN = "archivedcasedocument"; /** * the URL of user resource */ private static final String API_URL = "../API/bpm/archviedCaseDocument"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_VERSION, ItemAttribute.TYPE.STRING); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_CASE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_SOURCE_OBJECT_ID, ItemAttribute.TYPE.STRING); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATE); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_SUBMITTED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.DATE); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_HAS_CONTENT, ItemAttribute.TYPE.BOOLEAN); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_CONTENT_FILENAME, ItemAttribute.TYPE.STRING); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_CONTENT_MIMETYPE, ItemAttribute.TYPE.STRING); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_CONTENT_STORAGE_ID, ItemAttribute.TYPE.STRING); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_URL, ItemAttribute.TYPE.URL); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, ItemAttribute.TYPE.STRING); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_INDEX, ItemAttribute.TYPE.STRING); createAttribute(ArchivedCaseDocumentItem.ATTRIBUTE_AUTHOR, ItemAttribute.TYPE.STRING); } @Override protected void definePrimaryKeys() { setPrimaryKeys(ArchivedCaseDocumentItem.ATTRIBUTE_ID); } @Override protected IItem _createItem() { return new ArchivedCaseDocumentItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedCaseDocumentItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import java.util.Date; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Fabio Lombardi */ public class ArchivedCaseDocumentItem extends CaseDocumentItem { public static final String ATTRIBUTE_SOURCE_OBJECT_ID = "sourceObjectId"; public static final String ATTRIBUTE_ARCHIVED_DATE = "archivedDate"; public static final String FILTER_ARCHIVED_CASE_ID = "archivedCaseId"; public ArchivedCaseDocumentItem() { super(); } public ArchivedCaseDocumentItem(final IItem item) { super(item); } // SETTER public void setArchivedDate(final Date date) { setAttribute(ATTRIBUTE_ARCHIVED_DATE, date); } public void setArchivedDate(final String date) { setAttribute(ATTRIBUTE_ARCHIVED_DATE, date); } public void setSourceObjectId(final Long id) { setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id); } public void setSourceObjectId(final APIID id) { setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id); } public void setSourceObjectId(final String id) { setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id); } // GETTER @Override public ItemDefinition getItemDefinition() { return new ArchivedCaseDefinition(); } public APIID getSourceObjectId() { return getAttributeValueAsAPIID(ATTRIBUTE_SOURCE_OBJECT_ID); } public Date getArchivedDate() { return getAttributeValueAsDate(ATTRIBUTE_ARCHIVED_DATE); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedCaseItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import java.util.Date; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * Archived process instance item * * @author Séverin Moussel */ public class ArchivedCaseItem extends CaseItem { public static final String ATTRIBUTE_SOURCE_OBJECT_ID = "sourceObjectId"; public ArchivedCaseItem() { super(); } public ArchivedCaseItem(final IItem item) { super(item); } public static final String ATTRIBUTE_ARCHIVED_DATE = "archivedDate"; // SETTER public void setArchivedDate(final Date date) { setAttribute(ATTRIBUTE_ARCHIVED_DATE, date); } public void setArchivedDate(final String date) { setAttribute(ATTRIBUTE_ARCHIVED_DATE, date); } public void setSourceObjectId(final Long id) { setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id); } public void setSourceObjectId(final APIID id) { setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id); } public void setSourceObjectId(final String id) { setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id); } // GETTER @Override public ItemDefinition getItemDefinition() { return new ArchivedCaseDefinition(); } public APIID getSourceObjectId() { return getAttributeValueAsAPIID(ATTRIBUTE_SOURCE_OBJECT_ID); } public Date getArchivedDate() { return getAttributeValueAsDate(ATTRIBUTE_ARCHIVED_DATE); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedCaseVariable.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import java.io.Serializable; import com.fasterxml.jackson.annotation.JsonProperty; import org.bonitasoft.engine.bpm.data.ArchivedDataInstance; public class ArchivedCaseVariable extends ArchivedVariable { /** * ID of the case this variable belongs to */ @JsonProperty(value = "case_id") private String caseId; public String getCaseId() { return caseId; } public void setCaseId(String caseId) { this.caseId = caseId; } public static ArchivedCaseVariable create(ArchivedDataInstance archivedProcessDataInstance) { var instance = new ArchivedCaseVariable(); instance.setName(archivedProcessDataInstance.getName()); instance.setCaseId(String.valueOf(archivedProcessDataInstance.getContainerId())); instance.setDescription(archivedProcessDataInstance.getDescription()); instance.setType(archivedProcessDataInstance.getClassName()); Serializable value = archivedProcessDataInstance.getValue(); instance.setValue(value == null ? null : String.valueOf(value)); instance.setArchivedDate(archivedProcessDataInstance.getArchiveDate()); instance.setSourceObjectId(String.valueOf(archivedProcessDataInstance.getSourceObjectId())); return instance; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedCommentDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Gai Cuisha */ public class ArchivedCommentDefinition extends ItemDefinition { /** * Singleton */ public static ArchivedCommentDefinition get() { return (ArchivedCommentDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "archivedComment"; /** * the URL of user resource */ private static final String API_URL = "../API/bpm/archivedComment"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { setPrimaryKeys(CommentItem.ATTRIBUTE_ID); } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(ArchivedCommentItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ArchivedCommentItem.ATTRIBUTE_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ArchivedCommentItem.ATTRIBUTE_PROCESS_INSTANCE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ArchivedCommentItem.ATTRIBUTE_POST_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(ArchivedCommentItem.ATTRIBUTE_CONTENT, ItemAttribute.TYPE.TEXT) .isMandatory(); createAttribute(ArchivedCommentItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME); } @Override public ArchivedCommentItem _createItem() { return new ArchivedCommentItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedCommentItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import java.util.Date; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Gai Cuisha */ public class ArchivedCommentItem extends CommentItem { public ArchivedCommentItem() { super(); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES NAMES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String ATTRIBUTE_ARCHIVED_DATE = "archivedDate"; // SETTERS public void setArchivedDate(final String date) { this.setAttribute(ATTRIBUTE_ARCHIVED_DATE, date); } public void setArchivedDate(final Date date) { this.setAttribute(ATTRIBUTE_ARCHIVED_DATE, date); } // GETTERS public Date getArchivedDate() { return this.getAttributeValueAsDate(ATTRIBUTE_ARCHIVED_DATE); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEPLOYS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return new ArchivedCommentDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/ArchivedVariable.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import java.util.Date; import com.fasterxml.jackson.annotation.JsonFormat; public abstract class ArchivedVariable { /** * Name of the variable in the case */ private String name; /** * Detailed description of the case variable, as set in the definition at * design-time */ private String description; /** * The value of the archived case variable */ private String value; /** * The Java type of the variable */ private String type; /** * The date and time when this variable was archived */ @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSS") private Date archivedDate; /** * ID of the variable before it was archived */ private String sourceObjectId; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String getType() { return type; } public void setType(String type) { this.type = type; } public Date getArchivedDate() { return archivedDate; } public void setArchivedDate(Date archivedDate) { this.archivedDate = archivedDate; } public String getSourceObjectId() { return sourceObjectId; } public void setSourceObjectId(String sourceObjectId) { this.sourceObjectId = sourceObjectId; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CaseDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * Process instance definition * * @author Séverin Moussel * */ public class CaseDefinition extends ItemDefinition { /** * Singleton */ public static CaseDefinition get() { return (CaseDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "cases"; /** * the URL of user resource */ protected static final String API_URL = "../API/bpm/case"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { setPrimaryKeys(CaseItem.ATTRIBUTE_ID); } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(CaseItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(CaseItem.ATTRIBUTE_STATE, ItemAttribute.TYPE.ENUM); createAttribute(CaseItem.ATTRIBUTE_PROCESS_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(CaseItem.ATTRIBUTE_STARTED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(CaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(CaseItem.ATTRIBUTE_START_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(CaseItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(CaseItem.ATTRIBUTE_END_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_1_LABEL, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_2_LABEL, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_3_LABEL, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_4_LABEL, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_5_LABEL, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_1_VALUE, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_2_VALUE, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_3_VALUE, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_4_VALUE, ItemAttribute.TYPE.STRING); createAttribute(CaseItem.ATTRIBUTE_SEARCH_INDEX_5_VALUE, ItemAttribute.TYPE.STRING); } @Override public IItem _createItem() { return new CaseItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CaseDocumentDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Paul AMAR */ public class CaseDocumentDefinition extends ItemDefinition { /** * Singleton */ public static CaseDocumentDefinition get() { return (CaseDocumentDefinition) Definitions.get(TOKEN); } /** * token */ public static final String TOKEN = "casedocument"; /** * the URL of user resource */ private static final String API_URL = "../API/bpm/caseDocument"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(CaseDocumentItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(CaseDocumentItem.ATTRIBUTE_VERSION, ItemAttribute.TYPE.STRING); createAttribute(CaseDocumentItem.ATTRIBUTE_CASE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(CaseDocumentItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING); createAttribute(CaseDocumentItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT); createAttribute(CaseDocumentItem.ATTRIBUTE_SUBMITTED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(CaseDocumentItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.DATE); createAttribute(CaseDocumentItem.ATTRIBUTE_HAS_CONTENT, ItemAttribute.TYPE.BOOLEAN); createAttribute(CaseDocumentItem.ATTRIBUTE_CONTENT_FILENAME, ItemAttribute.TYPE.STRING); createAttribute(CaseDocumentItem.ATTRIBUTE_CONTENT_MIMETYPE, ItemAttribute.TYPE.STRING); createAttribute(CaseDocumentItem.ATTRIBUTE_CONTENT_STORAGE_ID, ItemAttribute.TYPE.STRING); createAttribute(CaseDocumentItem.ATTRIBUTE_URL, ItemAttribute.TYPE.URL); createAttribute(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, ItemAttribute.TYPE.STRING); createAttribute(CaseDocumentItem.ATTRIBUTE_INDEX, ItemAttribute.TYPE.STRING); createAttribute(CaseDocumentItem.ATTRIBUTE_AUTHOR, ItemAttribute.TYPE.STRING); } @Override protected void definePrimaryKeys() { setPrimaryKeys(CaseDocumentItem.ATTRIBUTE_ID); } @Override protected IItem _createItem() { return new CaseDocumentItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CaseDocumentItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import java.util.Date; import org.bonitasoft.web.rest.model.document.DocumentDefinition; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Fabio Lombardi */ public class CaseDocumentItem extends Item { public CaseDocumentItem() { super(); } public CaseDocumentItem(final IItem item) { super(item); } public static final String ATTRIBUTE_ID = "id"; public static final String ATTRIBUTE_INDEX = "index"; public static final String ATTRIBUTE_VERSION = "version"; public static final String ATTRIBUTE_CASE_ID = "caseId"; public static final String ATTRIBUTE_NAME = "name"; public static final String ATTRIBUTE_DESCRIPTION = "description"; public static final String ATTRIBUTE_SUBMITTED_BY_USER_ID = "submittedBy"; public static final String ATTRIBUTE_AUTHOR = "author"; public static final String ATTRIBUTE_CREATION_DATE = "creationDate"; public static final String ATTRIBUTE_HAS_CONTENT = "isInternal"; public static final String ATTRIBUTE_CONTENT_FILENAME = "fileName"; public static final String ATTRIBUTE_CONTENT_MIMETYPE = "contentMimetype"; public static final String ATTRIBUTE_CONTENT_STORAGE_ID = "contentStorageId"; public static final String ATTRIBUTE_UPLOAD_PATH = "file"; public static final String ATTRIBUTE_URL = "url"; public static final String FILTER_SUPERVISOR_ID = "supervisor_id"; public void setId(final String id) { this.setAttribute(ATTRIBUTE_ID, id); } public void setVersion(final String version) { this.setAttribute(ATTRIBUTE_VERSION, version); } public void setCaseId(final String caseId) { this.setAttribute(ATTRIBUTE_CASE_ID, caseId); } public void setName(final String name) { this.setAttribute(ATTRIBUTE_NAME, name); } public void setDescription(final String description) { setAttribute(ATTRIBUTE_DESCRIPTION, description); } public void setSubmittedBy(final APIID userId) { this.setAttribute(ATTRIBUTE_SUBMITTED_BY_USER_ID, userId.toString()); this.setAttribute(ATTRIBUTE_AUTHOR, userId); } public void setSubmittedBy(final Long userId) { this.setAttribute(ATTRIBUTE_SUBMITTED_BY_USER_ID, userId.toString()); this.setAttribute(ATTRIBUTE_AUTHOR, userId.toString()); } public void setCreationDate(final String creationDate) { this.setAttribute(ATTRIBUTE_CREATION_DATE, creationDate); } public void setCreationDate(final Date creationDate) { this.setAttribute(ATTRIBUTE_CREATION_DATE, creationDate); } public void setHasContent(final String hasContent) { this.setAttribute(ATTRIBUTE_HAS_CONTENT, hasContent); } public void setFileName(final String fileName) { this.setAttribute(ATTRIBUTE_CONTENT_FILENAME, fileName); } public void setMIMEType(final String MIMEType) { this.setAttribute(ATTRIBUTE_CONTENT_MIMETYPE, MIMEType); } public void setStorageId(final String storageId) { this.setAttribute(ATTRIBUTE_CONTENT_STORAGE_ID, storageId); } public void setUploadPath(final String uploadPath) { this.setAttribute(ATTRIBUTE_UPLOAD_PATH, uploadPath); } public String getUploadPath() { return getAttributeValue(ATTRIBUTE_UPLOAD_PATH); } public void setURL(final String URL) { this.setAttribute(ATTRIBUTE_URL, URL); } public void setIndex(final String index) { this.setAttribute(ATTRIBUTE_INDEX, index); } public void setIndex(final int index) { this.setAttribute(ATTRIBUTE_INDEX, index); } public String getVersion() { return getAttributeValue(ATTRIBUTE_VERSION); } public APIID getCaseId() { return getAttributeValueAsAPIID(ATTRIBUTE_CASE_ID); } public String getName() { return getAttributeValue(ATTRIBUTE_NAME); } public String getDescription() { return getAttributeValue(ATTRIBUTE_DESCRIPTION); } public APIID getSubmittedBy() { return getAttributeValueAsAPIID(ATTRIBUTE_SUBMITTED_BY_USER_ID); } public Date getCreationDate() { return getAttributeValueAsDate(ATTRIBUTE_CREATION_DATE); } public boolean hasContent() { return Boolean.parseBoolean(getAttributeValue(ATTRIBUTE_HAS_CONTENT)); } public String getFileName() { return getAttributeValue(ATTRIBUTE_CONTENT_FILENAME); } public String getMIMEType() { return getAttributeValue(ATTRIBUTE_CONTENT_MIMETYPE); } public String getStorageId() { return getAttributeValue(ATTRIBUTE_CONTENT_STORAGE_ID); } public String getURL() { return getAttributeValue(ATTRIBUTE_URL); } public String getIndex() { return getAttributeValue(ATTRIBUTE_INDEX); } @Override public ItemDefinition getItemDefinition() { return new DocumentDefinition(); } public UserItem getSubmittedByUser() { return (UserItem) getDeploy(ATTRIBUTE_SUBMITTED_BY_USER_ID); } /* Methods kept here to avoid API break */ public void setDocumentAuthor(final Long userId) { this.setAttribute(ATTRIBUTE_AUTHOR, userId); } public UserItem getAuthorByUser() { return (UserItem) getDeploy(ATTRIBUTE_AUTHOR); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CaseItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import java.util.Date; import org.bonitasoft.web.rest.model.bpm.process.ProcessItem; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * process instance item * * @author Haojie Yuan * @author Celine Souchet */ public class CaseItem extends Item implements ItemHasLastUpdateDate, ItemHasUniqueId { public static final String ATTRIBUTE_VARIABLES = "variables"; public static final String ATTRIBUTE_STATE = "state"; public static final String ATTRIBUTE_PROCESS_ID = "processDefinitionId"; public static final String ATTRIBUTE_PROCESS_NAME = "name"; public static final String ATTRIBUTE_ROOT_CASE_ID = "rootCaseId"; public static final String ATTRIBUTE_STARTED_BY_USER_ID = "started_by"; public static final String ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID = "startedBySubstitute"; public static final String ATTRIBUTE_START_DATE = "start"; public static final String ATTRIBUTE_END_DATE = "end_date"; public static final String COUNTER_FAILED_FLOW_NODES = "failedFlowNodes"; public static final String COUNTER_ACTIVE_FLOW_NODES = "activeFlowNodes"; public static final String COUNTER_PENDING_FLOW_NODES = "pendingFlowNodes"; public static final String ATTRIBUTE_SEARCH_INDEX_1_LABEL = "searchIndex1Label"; public static final String ATTRIBUTE_SEARCH_INDEX_1_VALUE = "searchIndex1Value"; public static final String ATTRIBUTE_SEARCH_INDEX_2_LABEL = "searchIndex2Label"; public static final String ATTRIBUTE_SEARCH_INDEX_2_VALUE = "searchIndex2Value"; public static final String ATTRIBUTE_SEARCH_INDEX_3_LABEL = "searchIndex3Label"; public static final String ATTRIBUTE_SEARCH_INDEX_3_VALUE = "searchIndex3Value"; public static final String ATTRIBUTE_SEARCH_INDEX_4_LABEL = "searchIndex4Label"; public static final String ATTRIBUTE_SEARCH_INDEX_4_VALUE = "searchIndex4Value"; public static final String ATTRIBUTE_SEARCH_INDEX_5_LABEL = "searchIndex5Label"; public static final String ATTRIBUTE_SEARCH_INDEX_5_VALUE = "searchIndex5Value"; public static final String ATTRIBUTE_CALLER_ID = "callerId"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES VALUES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // see ProcessInstanceState enum public static final String VALUE_STATE_INITIALIZING = "0"; public static final String VALUE_STATE_STARTED = "1"; public static final String VALUE_STATE_SUSPENDED = "2"; public static final String VALUE_STATE_CANCELLED = "3"; public static final String VALUE_STATE_ABORTED = "4"; public static final String VALUE_STATE_COMPLETING = "5"; public static final String VALUE_STATE_COMPLETED = "6"; public static final String VALUE_STATE_ERROR = "7"; public static final String VALUE_STATE_TO_MIGRATE = "8"; public static final String VALUE_STATE_READY_FOR_MIGRATION = "9"; public static final String VALUE_STATE_MIGRATING = "10"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String FILTER_USER_ID = "user_id"; public static final String FILTER_SUPERVISOR_ID = "supervisor_id"; public static final String FILTER_TEAM_MANAGER_ID = "team_manager_id"; public static final String FILTER_CALLER = "caller"; public static final String FILTER_STATE = "state"; public CaseItem() { super(); } public CaseItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS AND SETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEPLOYS public UserItem getStartedByUser() { return new UserItem(getDeploy(ATTRIBUTE_STARTED_BY_USER_ID)); } public UserItem getStartedBySubstituteUser() { return new UserItem(getDeploy(ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID)); } public ProcessItem getProcess() { return new ProcessItem(getDeploy(ATTRIBUTE_PROCESS_ID)); } // GETTERS @Override public Date getLastUpdateDate() { return getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE); } public String getState() { return getAttributeValue(ATTRIBUTE_STATE); } public Date getStartDate() { return getAttributeValueAsDate(ATTRIBUTE_START_DATE); } public APIID getStartedByUserId() { return getAttributeValueAsAPIID(ATTRIBUTE_STARTED_BY_USER_ID); } public APIID getStartedBySubstituteUserId() { return getAttributeValueAsAPIID(ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID); } public Date getEndDate() { return getAttributeValueAsDate(ATTRIBUTE_END_DATE); } public APIID getProcessId() { return getAttributeValueAsAPIID(ATTRIBUTE_PROCESS_ID); } public APIID getRootCaseId() { return getAttributeValueAsAPIID(ATTRIBUTE_ROOT_CASE_ID); } public String getSearchIndex1Label() { return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_1_LABEL); } public String getSearchIndex1Value() { return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_1_VALUE); } public String getSearchIndex2Label() { return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_2_LABEL); } public String getSearchIndex2Value() { return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_2_VALUE); } public String getSearchIndex3Label() { return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_3_LABEL); } public String getSearchIndex3Value() { return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_3_VALUE); } public String getSearchIndex4Label() { return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_4_LABEL); } public String getSearchIndex4Value() { return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_4_VALUE); } public String getSearchIndex5Label() { return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_5_LABEL); } public String getSearchIndex5Value() { return getAttributeValue(ATTRIBUTE_SEARCH_INDEX_5_VALUE); } public APIID getCallerId() { return getAttributeValueAsAPIID(ATTRIBUTE_CALLER_ID); } // SETTERS @Override public void setId(final Long id) { setAttribute(ATTRIBUTE_ID, id); } @Override public void setId(final String id) { setAttribute(ATTRIBUTE_ID, id); } @Override public void setLastUpdateDate(final String date) { setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } @Override public void setLastUpdateDate(final Date date) { setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } public void setState(final String state) { setAttribute(ATTRIBUTE_STATE, state); } /** * @param date Must be SQL formated date */ public void setStartDate(final String date) { setAttribute(ATTRIBUTE_START_DATE, date); } public void setStartDate(final Date date) { setAttribute(ATTRIBUTE_START_DATE, date); } public void setStartedByUserId(final Long userId) { setAttribute(ATTRIBUTE_STARTED_BY_USER_ID, userId); } public void setStartedByUserId(final APIID userId) { setAttribute(ATTRIBUTE_STARTED_BY_USER_ID, userId); } public void setStartedByUserId(final String userId) { setAttribute(ATTRIBUTE_STARTED_BY_USER_ID, userId); } public void setStartedBySubstituteUserId(final Long userId) { setAttribute(ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, userId); } public void setStartedBySubstituteUserId(final APIID userId) { setAttribute(ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, userId); } public void setStartedBySubstituteUserId(final String userId) { setAttribute(ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, userId); } public void setEndDate(final String date) { setAttribute(ATTRIBUTE_END_DATE, date); } public void setEndDate(final Date date) { setAttribute(ATTRIBUTE_END_DATE, date); } public void setProcessId(final Long processId) { setProcessId(APIID.makeAPIID(processId)); } public void setProcessId(final String processId) { setAttribute(ATTRIBUTE_PROCESS_ID, processId); } public void setProcessId(final APIID processId) { setAttribute(ATTRIBUTE_PROCESS_ID, processId); } public void setRootCaseId(final long rootCaseId) { setAttribute(ATTRIBUTE_ROOT_CASE_ID, rootCaseId); } public void setSearchIndex1Label(final String attributeSearchIndex1Label) { setAttribute(ATTRIBUTE_SEARCH_INDEX_1_LABEL, attributeSearchIndex1Label); } public void setSearchIndex1Value(final String attributeSearchIndex1Value) { setAttribute(ATTRIBUTE_SEARCH_INDEX_1_VALUE, attributeSearchIndex1Value); } public void setSearchIndex2Label(final String attributeSearchIndex2Label) { setAttribute(ATTRIBUTE_SEARCH_INDEX_2_LABEL, attributeSearchIndex2Label); } public void setSearchIndex2Value(final String attributeSearchIndex2Value) { setAttribute(ATTRIBUTE_SEARCH_INDEX_2_VALUE, attributeSearchIndex2Value); } public void setSearchIndex3Label(final String attributeSearchIndex3Label) { setAttribute(ATTRIBUTE_SEARCH_INDEX_3_LABEL, attributeSearchIndex3Label); } public void setSearchIndex3Value(final String attributeSearchIndex3Value) { setAttribute(ATTRIBUTE_SEARCH_INDEX_3_VALUE, attributeSearchIndex3Value); } public void setSearchIndex4Label(final String attributeSearchIndex4Label) { setAttribute(ATTRIBUTE_SEARCH_INDEX_4_LABEL, attributeSearchIndex4Label); } public void setSearchIndex4Value(final String attributeSearchIndex4Value) { setAttribute(ATTRIBUTE_SEARCH_INDEX_4_VALUE, attributeSearchIndex4Value); } public void setSearchIndex5Label(final String attributeSearchIndex5Label) { setAttribute(ATTRIBUTE_SEARCH_INDEX_5_LABEL, attributeSearchIndex5Label); } public void setSearchIndex5Value(final String attributeSearchIndex5Value) { setAttribute(ATTRIBUTE_SEARCH_INDEX_5_VALUE, attributeSearchIndex5Value); } public void setCallerId(final long attributeCallerId) { setAttribute(ATTRIBUTE_CALLER_ID, attributeCallerId); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return new CaseDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CaseVariableDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import static org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem.ATTRIBUTE_CASE_ID; import static org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem.ATTRIBUTE_NAME; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringMaxLengthValidator; /** * @author Colin PUY */ public class CaseVariableDefinition extends ItemDefinition { private static final String API_URL = "../API/bpm/caseVariable"; public static final String TOKEN = "caseVariable"; public static CaseVariableDefinition get() { return (CaseVariableDefinition) Definitions.get(TOKEN); } @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(CaseVariableItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING); createAttribute(CaseVariableItem.ATTRIBUTE_TYPE, ItemAttribute.TYPE.STRING); createAttribute(CaseVariableItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT); createAttribute(CaseVariableItem.ATTRIBUTE_VALUE, ItemAttribute.TYPE.STRING) .removeValidator(StringMaxLengthValidator.class.getName()); } @Override protected void definePrimaryKeys() { setPrimaryKeys(ATTRIBUTE_CASE_ID, ATTRIBUTE_NAME); } @Override protected CaseVariableItem _createItem() { return new CaseVariableItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CaseVariableItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import java.io.Serializable; import java.util.Map; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Colin PUY */ public class CaseVariableItem extends Item { public static final String ATTRIBUTE_CASE_ID = "case_id"; public static final String ATTRIBUTE_NAME = "name"; public static final String ATTRIBUTE_TYPE = "type"; public static final String ATTRIBUTE_VALUE = "value"; public static final String ATTRIBUTE_DESCRIPTION = "description"; public CaseVariableItem() { } public CaseVariableItem(final long caseId, final String name, final Serializable value, final String type, final String description) { setAttribute(ATTRIBUTE_CASE_ID, String.valueOf(caseId)); setAttribute(ATTRIBUTE_NAME, name); setAttribute(ATTRIBUTE_VALUE, String.valueOf(value)); setAttribute(ATTRIBUTE_TYPE, type); setAttribute(ATTRIBUTE_DESCRIPTION, description); } public static CaseVariableItem fromIdAndAttributes(final APIID apiid, final Map attributes) { return new CaseVariableItem(apiid.getPartAsLong(ATTRIBUTE_CASE_ID), apiid.getPart(ATTRIBUTE_NAME), attributes.get(ATTRIBUTE_VALUE), attributes.get(ATTRIBUTE_TYPE), attributes.get(ATTRIBUTE_DESCRIPTION)); } @Override public ItemDefinition getItemDefinition() { return CaseVariableDefinition.get(); } public long getCaseId() { return getAttributeValueAsLong(ATTRIBUTE_CASE_ID); } public String getName() { return getAttributeValue(ATTRIBUTE_NAME); } public String getType() { return getAttributeValue(ATTRIBUTE_TYPE); } public String getValue() { return getAttributeValue(ATTRIBUTE_VALUE); } public String getDescription() { return getAttributeValue(ATTRIBUTE_DESCRIPTION); } public void setValue(final String value) { setAttribute(ATTRIBUTE_VALUE, value); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CommentDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Gai Cuisha */ public class CommentDefinition extends ItemDefinition { /** * Singleton */ public static CommentDefinition get() { return (CommentDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "comment"; /** * the URL of user resource */ private static final String API_URL = "../API/bpm/comment"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { setPrimaryKeys(CommentItem.ATTRIBUTE_ID); } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(CommentItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(CommentItem.ATTRIBUTE_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(CommentItem.ATTRIBUTE_PROCESS_INSTANCE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(CommentItem.ATTRIBUTE_POST_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(CommentItem.ATTRIBUTE_CONTENT, ItemAttribute.TYPE.TEXT) .isMandatory(); } @Override public CommentItem _createItem() { return new CommentItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/cases/CommentItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.cases; import java.util.Date; import org.bonitasoft.web.rest.model.bpm.flownode.TaskItem; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Vincent Elcrin */ public class CommentItem extends Item implements ItemHasUniqueId { public CommentItem() { super(); } public CommentItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES NAMES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String ATTRIBUTE_USER_ID = "userId"; public static final String ATTRIBUTE_PROCESS_INSTANCE_ID = "processInstanceId"; public static final String ATTRIBUTE_POST_DATE = "postDate"; public static final String ATTRIBUTE_CONTENT = "content"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES VALUES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String FILTER_TEAM_MANAGER_ID = "team_manager_id"; public static final String FILTER_SUPERVISOR_ID = "supervisor_id"; public static final String FILTER_USER_ID = "user_id"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // COUNTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS AND SETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS public APIID getUserId() { return APIID.makeAPIID(this.getAttributeValue(ATTRIBUTE_USER_ID)); } public APIID getProcessInstanceId() { return APIID.makeAPIID(this.getAttributeValue(ATTRIBUTE_PROCESS_INSTANCE_ID)); } public String getPostDate() { return this.getAttributeValue(ATTRIBUTE_POST_DATE); } public String getContent() { return this.getAttributeValue(ATTRIBUTE_CONTENT); } // SETTERS @Override public void setId(final String id) { this.setAttribute(ATTRIBUTE_ID, id); } @Override public void setId(final Long id) { this.setAttribute(ATTRIBUTE_ID, APIID.makeAPIID(id)); } public void setUserId(final String id) { this.setAttribute(ATTRIBUTE_USER_ID, id); } public void setUserId(final Long id) { this.setAttribute(ATTRIBUTE_USER_ID, APIID.makeAPIID(id)); } public void setUserId(final APIID id) { this.setAttribute(ATTRIBUTE_USER_ID, id); } public void setProcessInstanceId(final String id) { this.setAttribute(ATTRIBUTE_PROCESS_INSTANCE_ID, id); } public void setProcessInstanceId(final Long id) { this.setAttribute(ATTRIBUTE_PROCESS_INSTANCE_ID, APIID.makeAPIID(id)); } public void setProcessInstanceId(final APIID id) { this.setAttribute(ATTRIBUTE_PROCESS_INSTANCE_ID, id); } public void setPostDate(final String date) { this.setAttribute(ATTRIBUTE_POST_DATE, date); } public void setPostDate(final Long date) { setPostDate(new Date(date)); } public void setPostDate(final Date date) { this.setAttribute(ATTRIBUTE_POST_DATE, date); } public void setContent(final String content) { this.setAttribute(ATTRIBUTE_CONTENT, content); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEPLOYS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public UserItem getUser() { return new UserItem(getDeploy(ATTRIBUTE_USER_ID)); } public TaskItem getProcessInstance() { return new TaskItem(getDeploy(ATTRIBUTE_PROCESS_INSTANCE_ID)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return Definitions.get(CommentDefinition.TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/connector/ArchivedConnectorInstanceDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.connector; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Julien Mege */ public class ArchivedConnectorInstanceDefinition extends ConnectorInstanceDefinition { public static final String TOKEN = "archivedConnectorInstance"; protected static final String API_URL = "../API/bpm/archivedConnectorInstance"; @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(ArchivedConnectorInstanceItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(ArchivedConnectorInstanceItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.ITEM_ID); } @Override protected void definePrimaryKeys() { setPrimaryKeys(ArchivedConnectorInstanceItem.ATTRIBUTE_ID); } @Override protected Item _createItem() { return new ConnectorInstanceItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/connector/ArchivedConnectorInstanceItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.connector; import java.util.Date; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Julien Mege */ public class ArchivedConnectorInstanceItem extends ConnectorInstanceItem implements ItemHasUniqueId { // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String ATTRIBUTE_ARCHIVED_DATE = "archivedDate"; public static final String ATTRIBUTE_SOURCE_OBJECT_ID = "sourceObjectId"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS AND GETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS public void setArchivedDate(final Date date) { this.setAttribute(ATTRIBUTE_ARCHIVED_DATE, date); } public void setSourceObjectId(final Long id) { this.setAttribute(ATTRIBUTE_CONTAINER_ID, id); } public void setSourceObjectId(final String id) { this.setAttribute(ATTRIBUTE_CONTAINER_ID, id); } public void setSourceObjectId(final APIID id) { this.setAttribute(ATTRIBUTE_CONTAINER_ID, id); } // GETTERS public String getArchivedDate() { return this.getAttributeValue(ATTRIBUTE_ARCHIVED_DATE); } public APIID getSourceObjectId(final Long id) { return APIID.makeAPIID(this.getAttributeValue(ATTRIBUTE_SOURCE_OBJECT_ID)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEPLOYS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return Definitions.get(ArchivedConnectorInstanceDefinition.TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/connector/ConnectorInstanceDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.connector; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Vincent Elcrin */ public class ConnectorInstanceDefinition extends ItemDefinition { public static final String TOKEN = "connectorInstance"; protected static final String API_URL = "../API/bpm/connectorInstance"; @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(ConnectorInstanceItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ConnectorInstanceItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING); createAttribute(ConnectorInstanceItem.ATTRIBUTE_CONNECTOR_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ConnectorInstanceItem.ATTRIBUTE_VERSION, ItemAttribute.TYPE.STRING); createAttribute(ConnectorInstanceItem.ATTRIBUTE_STATE, ItemAttribute.TYPE.ENUM); } @Override protected void definePrimaryKeys() { setPrimaryKeys(ConnectorInstanceItem.ATTRIBUTE_ID); } @Override protected Item _createItem() { return new ConnectorInstanceItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/connector/ConnectorInstanceItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.connector; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Vincent Elcrin */ public class ConnectorInstanceItem extends Item implements ItemHasUniqueId { // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String ATTRIBUTE_CONNECTOR_ID = "connectorId"; public static final String ATTRIBUTE_NAME = "name"; public static final String ATTRIBUTE_VERSION = "version"; public static final String ATTRIBUTE_ACTIVATION_EVENT = "activationEvent"; public static final String ATTRIBUTE_STATE = "state"; public static final String ATTRIBUTE_CONTAINER_TYPE = "containerType"; public static final String ATTRIBUTE_CONTAINER_ID = "containerId"; public static final String ATTRIBUTE_RESET_STATE = "resetState"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES VALUES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String VALUE_STATE_TO_BE_EXECUTED = "TO_BE_EXECUTED"; public static final String VALUE_STATE_TO_RE_EXECUTE = "TO_RE_EXECUTE"; public static final String VALUE_STATE_DONE = "DONE"; public static final String VALUE_STATE_FAILED = "FAILED"; public static final String VALUE_STATE_SKIPPED = "SKIPPED"; public static final String VALUE_RESET_STATE_TO_RE_EXECUTE = "toReExecute"; public static final String VALUE_RESET_STATE_SKIPPED = "skipped"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS AND GETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS @Override public void setId(String id) { this.setAttribute(ATTRIBUTE_ID, id); } @Override public void setId(Long id) { this.setAttribute(ATTRIBUTE_ID, id); } public void setName(String name) { this.setAttribute(ATTRIBUTE_NAME, name); } public void setConnectorId(String id) { this.setAttribute(ATTRIBUTE_CONNECTOR_ID, id); } public void setVersion(String version) { this.setAttribute(ATTRIBUTE_VERSION, version); } public void setActivationEvent(String activationEvent) { this.setAttribute(ATTRIBUTE_ACTIVATION_EVENT, activationEvent); } public void setState(String state) { this.setAttribute(ATTRIBUTE_STATE, state); } public void setContainerType(String type) { this.setAttribute(ATTRIBUTE_CONTAINER_TYPE, type); } public void setContainerId(Long id) { this.setAttribute(ATTRIBUTE_CONTAINER_ID, id); } // GETTERS public String getName() { return this.getAttributeValue(ATTRIBUTE_NAME); } public APIID getConnectorId() { return this.getAttributeValueAsAPIID(ATTRIBUTE_CONNECTOR_ID); } public String getVersion() { return this.getAttributeValue(ATTRIBUTE_VERSION); } public String getState() { return this.getAttributeValue(ATTRIBUTE_STATE); } public APIID getContainerId() { return this.getAttributeValueAsAPIID(ATTRIBUTE_CONTAINER_ID); } public String getContainerType() { return this.getAttributeValue(ATTRIBUTE_CONTAINER_TYPE); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEPLOYS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return Definitions.get(ConnectorInstanceDefinition.TOKEN); } public boolean hasFailed() { return VALUE_STATE_FAILED.equals(getState()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ActivityDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.EnumValidator; /** * User definition * * @author Séverin Moussel */ public class ActivityDefinition extends FlowNodeDefinition { public static final String TOKEN = "activity"; protected static final String API_URL = "../API/bpm/activity"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { super.defineAttributes(); // Restrict type to activities getAttribute(TaskItem.ATTRIBUTE_TYPE) .removeValidator(EnumValidator.class.getName()) .addValidator(new EnumValidator( TaskItem.VALUE_TYPE_USER_TASK, TaskItem.VALUE_TYPE_AUTOMATIC_TASK, TaskItem.VALUE_TYPE_MANUAL_TASK, TaskItem.VALUE_TYPE_CALL_ACTIVITY, TaskItem.VALUE_TYPE_LOOP_ACTIVITY)); createAttribute(TaskItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(TaskItem.ATTRIBUTE_REACHED_STATE_DATE, ItemAttribute.TYPE.DATETIME); } @Override public IActivityItem _createItem() { return new ActivityItem(); } public static ActivityDefinition get() { return (ActivityDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ActivityItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import java.util.Date; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class ActivityItem extends FlowNodeItem implements IActivityItem { public static final String ATTRIBUTE_VARIABLES = "variables"; public ActivityItem() { super(); } public ActivityItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS AND GETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public final void setReachStateDate(final String reachedStateDate) { setAttribute(ATTRIBUTE_REACHED_STATE_DATE, reachedStateDate); } @Override public final void setReachStateDate(final Date reachedStateDate) { setAttribute(ATTRIBUTE_REACHED_STATE_DATE, reachedStateDate); } @Override public final Date getReachStateDate() { return this.getAttributeValueAsDate(ATTRIBUTE_REACHED_STATE_DATE); } @Override public final void setLastUpdateDate(final String date) { setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } @Override public final void setLastUpdateDate(final Date date) { setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } @Override public final Date getLastUpdateDate() { return this.getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return ActivityDefinition.get(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedActivityDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Séverin Moussel */ public class ArchivedActivityDefinition extends ActivityDefinition { public static final String TOKEN = "archivedActivity"; protected static final String API_URL = "../API/bpm/archivedActivity"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { super.defineAttributes(); createAttribute(ArchivedActivityItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME); } @Override public ArchivedActivityItem _createItem() { return new ArchivedActivityItem(); } public static ArchivedActivityDefinition get() { return (ArchivedActivityDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedActivityItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import java.util.Date; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class ArchivedActivityItem extends ArchivedFlowNodeItem implements IActivityItem { public ArchivedActivityItem() { super(); } public ArchivedActivityItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS AND GETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public final void setReachStateDate(final String reachedStateDate) { setAttribute(ATTRIBUTE_REACHED_STATE_DATE, reachedStateDate); } @Override public final void setReachStateDate(final Date reachedStateDate) { setAttribute(ATTRIBUTE_REACHED_STATE_DATE, reachedStateDate); } @Override public final Date getReachStateDate() { return this.getAttributeValueAsDate(ATTRIBUTE_REACHED_STATE_DATE); } @Override public final void setLastUpdateDate(final String date) { setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } @Override public final void setLastUpdateDate(final Date date) { setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } @Override public final Date getLastUpdateDate() { return this.getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return new ArchivedActivityDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedFlowNode.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import java.util.Date; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public interface ArchivedFlowNode { String ATTRIBUTE_SOURCE_OBJECT_ID = "sourceObjectId"; String FILTER_IS_TERMINAL = "isTerminal"; String ATTRIBUTE_ARCHIVED_DATE = "archivedDate"; void setArchivedDate(final String date); void setArchivedDate(final Date date); Date getArchivedDate(); boolean isArchived(); void setSourceObjectId(final APIID id); void setSourceObjectId(final String id); void setSourceObjectId(final Long id); APIID getSourceObjectId(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedFlowNodeDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Séverin Moussel */ public class ArchivedFlowNodeDefinition extends FlowNodeDefinition { public static final String TOKEN = "archivedflownode"; private static final String API_URL = "../API/bpm/archivedFlowNode"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { super.defineAttributes(); createAttribute(ArchivedFlowNodeItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME); } @Override public ArchivedFlowNodeItem _createItem() { return new ArchivedFlowNodeItem(); } public static ArchivedFlowNodeDefinition get() { return (ArchivedFlowNodeDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedFlowNodeItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import java.util.Date; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class ArchivedFlowNodeItem extends FlowNodeItem implements ArchivedFlowNode { public ArchivedFlowNodeItem() { super(); } public ArchivedFlowNodeItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS AND SETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public final void setArchivedDate(final String archivedDate) { this.setAttribute(ATTRIBUTE_ARCHIVED_DATE, archivedDate); } @Override public final void setArchivedDate(final Date date) { this.setAttribute(ATTRIBUTE_ARCHIVED_DATE, date); } @Override public Date getArchivedDate() { return getAttributeValueAsDate(ATTRIBUTE_ARCHIVED_DATE); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return new ArchivedFlowNodeDefinition(); } public final boolean isArchived() { return true; } /* * (non-Javadoc) * @see * org.bonitasoft.console.client.model.bpm.flownode.ArchivedFlowNode#setSourceObjectId(org.bonitasoft.web.toolkit. * client.data.APIID) */ @Override public void setSourceObjectId(APIID id) { this.setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id); } /* * (non-Javadoc) * @see org.bonitasoft.console.client.model.bpm.flownode.ArchivedFlowNode#setSourceObjectId(java.lang.String) */ @Override public void setSourceObjectId(String id) { this.setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id); } /* * (non-Javadoc) * @see org.bonitasoft.console.client.model.bpm.flownode.ArchivedFlowNode#setSourceObjectId(java.lang.Long) */ @Override public void setSourceObjectId(Long id) { this.setAttribute(ATTRIBUTE_SOURCE_OBJECT_ID, id); } /* * (non-Javadoc) * @see org.bonitasoft.console.client.model.bpm.flownode.ArchivedFlowNode#getSourceObjectId() */ @Override public APIID getSourceObjectId() { return this.getAttributeValueAsAPIID(ATTRIBUTE_SOURCE_OBJECT_ID); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedHumanTaskDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Séverin Moussel */ public class ArchivedHumanTaskDefinition extends HumanTaskDefinition { public static final String TOKEN = "archivedhumantask"; /** * the URL of users resource */ private static final String API_URL = "../API/bpm/archivedHumanTask"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { super.defineAttributes(); createAttribute(ArchivedHumanTaskItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME); } @Override public ArchivedHumanTaskItem _createItem() { return new ArchivedHumanTaskItem(); } public static ArchivedHumanTaskDefinition get() { return (ArchivedHumanTaskDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedHumanTaskItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import java.util.Date; import org.bonitasoft.web.rest.model.bpm.process.ActorItem; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class ArchivedHumanTaskItem extends ArchivedTaskItem implements IHumanTaskItem { public ArchivedHumanTaskItem() { super(); } public ArchivedHumanTaskItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS @Override public void setAssignedId(final String id) { setAttribute(ATTRIBUTE_ASSIGNED_USER_ID, id); } @Override public void setAssignedId(final APIID id) { setAttribute(ATTRIBUTE_ASSIGNED_USER_ID, id); } @Override public void setAssignedId(final Long id) { setAttribute(ATTRIBUTE_ASSIGNED_USER_ID, id); } @Override public void setAssignedDate(final String date) { setAttribute(ATTRIBUTE_ASSIGNED_DATE, date); } @Override public void setAssignedDate(final Date date) { setAttribute(ATTRIBUTE_ASSIGNED_DATE, date); } @Override public void setPriority(final String priority) { setAttribute(ATTRIBUTE_PRIORITY, priority); } @Override public void setDueDate(final String date) { setAttribute(ATTRIBUTE_DUE_DATE, date); } @Override public void setDueDate(final Date date) { setAttribute(ATTRIBUTE_DUE_DATE, date); } @Override public void setActorId(final APIID id) { setAttribute(ATTRIBUTE_ACTOR_ID, id); } @Override public void setActorId(final String actorId) { setAttribute(ATTRIBUTE_ACTOR_ID, actorId); } @Override public void setActorId(final Long actorId) { setAttribute(ATTRIBUTE_ACTOR_ID, actorId); } // Counters @Override public void setNbOfAttachment(final String count) { setAttribute(COUNT_ATTACHMENT_NUMBER, count); } @Override public void setNbOfAttachment(final int count) { setAttribute(COUNT_ATTACHMENT_NUMBER, count); } @Override public void setNbOfComment(final String count) { setAttribute(COUNT_COMMENT_NUMBER, count); } @Override public void setNbOfComment(final int count) { setAttribute(COUNT_COMMENT_NUMBER, count); } @Override public void setNbOfActorUser(final String count) { setAttribute(COUNT_ACTOR_USER_NUMBER, count); } @Override public void setNbOfActorUser(final int count) { setAttribute(COUNT_ACTOR_USER_NUMBER, count); } // GETTERS @Override public final APIID getActorId() { return getAttributeValueAsAPIID(ATTRIBUTE_ACTOR_ID); } @Override public String getPriority() { return this.getAttributeValue(ATTRIBUTE_PRIORITY); } @Override public String getDueDate() { return this.getAttributeValue(ATTRIBUTE_DUE_DATE); } @Override public APIID getAssignedId() { return this.getAttributeValueAsAPIID(ATTRIBUTE_ASSIGNED_USER_ID); } @Override public String getAssignedDate() { return this.getAttributeValue(ATTRIBUTE_ASSIGNED_DATE); } // Counters @Override public Integer getNbOfAttachment() { return Integer.parseInt(this.getAttributeValue(COUNT_ATTACHMENT_NUMBER)); } @Override public Integer getNbOfComment() { return Integer.parseInt(this.getAttributeValue(COUNT_COMMENT_NUMBER)); } @Override public Integer getNbOfActorUser() { return Integer.parseInt(this.getAttributeValue(COUNT_ACTOR_USER_NUMBER)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEPLOYS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public UserItem getAssignedUser() { return new UserItem(getDeploy(ATTRIBUTE_ASSIGNED_USER_ID)); } @Override public final ActorItem getActor() { return new ActorItem(getDeploy(ATTRIBUTE_ACTOR_ID)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return new HumanTaskDefinition(); } @Override public boolean isAssigned() { return getAssignedId() != null; } @Override public boolean isUnassigned() { return !isAssigned(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedTaskDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Séverin Moussel */ public class ArchivedTaskDefinition extends TaskDefinition { public static final String TOKEN = "archivedtask"; private static final String API_URL = "../API/bpm/archivedTask"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { super.defineAttributes(); createAttribute(ArchivedTaskItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME); } @Override public ArchivedTaskItem _createItem() { return new ArchivedTaskItem(); } public static ArchivedTaskDefinition get() { return (ArchivedTaskDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedTaskItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class ArchivedTaskItem extends ArchivedActivityItem implements ITaskItem { public ArchivedTaskItem() { super(); } public ArchivedTaskItem(final IItem item) { super(item); } @Override public ItemDefinition getItemDefinition() { return new ArchivedTaskDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedUserTaskDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Séverin Moussel */ public class ArchivedUserTaskDefinition extends UserTaskDefinition { public static final String TOKEN = "archivedusertask"; private static final String API_URL = "../API/bpm/archivedUserTask"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { super.defineAttributes(); createAttribute(ArchivedUserTaskItem.ATTRIBUTE_ARCHIVED_DATE, ItemAttribute.TYPE.DATETIME); } @Override public ArchivedUserTaskItem _createItem() { return new ArchivedUserTaskItem(); } public static ArchivedUserTaskDefinition get() { return (ArchivedUserTaskDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ArchivedUserTaskItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class ArchivedUserTaskItem extends ArchivedHumanTaskItem implements IUserTaskItem { public ArchivedUserTaskItem() { super(); } public ArchivedUserTaskItem(final IItem item) { super(item); } @Override public ItemDefinition getItemDefinition() { return new ArchivedUserTaskDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/FlowNodeDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.rest.model.bpm.cases.CaseDefinition; import org.bonitasoft.web.rest.model.bpm.process.ProcessDefinition; import org.bonitasoft.web.rest.model.identity.UserDefinition; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; import org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.ReplaceRegexpModifier; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.EnumValidator; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringRegexpValidator; /** * @author Séverin Moussel */ public class FlowNodeDefinition extends ItemDefinition { public static final String TOKEN = "flownode"; public static final String API_URL = "../API/bpm/flowNode"; private static final String ATTRIBUTE_NAME_FORBIDDEN_CHARACTERS = ":/\\?#\\[\\]@!\\$&'\\(\\)\\*\\+,;="; @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(FlowNodeItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(FlowNodeItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING) .isMandatory() .addValidator(new StringRegexpValidator(ATTRIBUTE_NAME_FORBIDDEN_CHARACTERS, true)) .addInputModifier(new ReplaceRegexpModifier(ATTRIBUTE_NAME_FORBIDDEN_CHARACTERS, "_")); createAttribute(FlowNodeItem.ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.STRING); createAttribute(FlowNodeItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT); createAttribute(FlowNodeItem.ATTRIBUTE_DISPLAY_DESCRIPTION, ItemAttribute.TYPE.TEXT); createAttribute(FlowNodeItem.ATTRIBUTE_STATE, ItemAttribute.TYPE.ENUM) .setDefaultValue(FlowNodeItem.VALUE_STATE_READY) .addValidator(new EnumValidator( FlowNodeItem.VALUE_STATE_READY, FlowNodeItem.VALUE_STATE_COMPLETED, FlowNodeItem.VALUE_STATE_FAILED, FlowNodeItem.VALUE_STATE_REPLAY, FlowNodeItem.VALUE_STATE_SKIPPED)); createAttribute(FlowNodeItem.ATTRIBUTE_CASE_ID, ItemAttribute.TYPE.ITEM_ID) .isMandatory(); createAttribute(FlowNodeItem.ATTRIBUTE_ROOT_CONTAINER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(FlowNodeItem.ATTRIBUTE_PROCESS_ID, ItemAttribute.TYPE.ITEM_ID) .isMandatory(); createAttribute(FlowNodeItem.ATTRIBUTE_TYPE, ItemAttribute.TYPE.ENUM) .addValidator(new EnumValidator( FlowNodeItem.VALUE_TYPE_USER_TASK, FlowNodeItem.VALUE_TYPE_AUTOMATIC_TASK, FlowNodeItem.VALUE_TYPE_MANUAL_TASK, FlowNodeItem.VALUE_TYPE_BOUNDARY_EVENT, FlowNodeItem.VALUE_TYPE_CALL_ACTIVITY, FlowNodeItem.VALUE_TYPE_END_EVENT, FlowNodeItem.VALUE_TYPE_GATEWAY, FlowNodeItem.VALUE_TYPE_INTERMEDIATE_CATCH_EVENT, FlowNodeItem.VALUE_TYPE_INTERMEDIATE_THROW_EVENT, FlowNodeItem.VALUE_TYPE_LOOP_ACTIVITY, FlowNodeItem.VALUE_TYPE_START_EVENT)); createAttribute(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID, ItemAttribute.TYPE.ITEM_ID); } @Override protected void defineDeploys() { super.defineDeploys(); declareDeployable(FlowNodeItem.ATTRIBUTE_PROCESS_ID, Definitions.get(ProcessDefinition.TOKEN)); declareDeployable(FlowNodeItem.ATTRIBUTE_CASE_ID, Definitions.get(CaseDefinition.TOKEN)); declareDeployable(FlowNodeItem.ATTRIBUTE_ROOT_CONTAINER_ID, Definitions.get(ProcessDefinition.TOKEN)); declareDeployable(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_USER_ID, UserDefinition.get()); declareDeployable(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID, UserDefinition.get()); } @Override protected void definePrimaryKeys() { setPrimaryKeys(FlowNodeItem.ATTRIBUTE_ID); } @Override protected IFlowNodeItem _createItem() { return new FlowNodeItem(); } public static FlowNodeDefinition get() { return (FlowNodeDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/FlowNodeItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.rest.model.bpm.cases.CaseItem; import org.bonitasoft.web.rest.model.bpm.process.ProcessItem; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class FlowNodeItem extends Item implements IFlowNodeItem { public FlowNodeItem() { super(); } public FlowNodeItem(final IItem item) { super(item); } @Override public final void setDescription(final String description) { this.setAttribute(ATTRIBUTE_DESCRIPTION, description); } @Override public final void setDisplayDescription(final String displayDescription) { this.setAttribute(ATTRIBUTE_DISPLAY_DESCRIPTION, displayDescription); } @Override public final String getDescription() { return getAttributeValue(ATTRIBUTE_DESCRIPTION); } @Override public final String getDisplayDescription() { return getAttributeValue(ATTRIBUTE_DISPLAY_DESCRIPTION); } @Override public final void setName(final String name) { this.setAttribute(ATTRIBUTE_NAME, name); } @Override public final void setDisplayName(final String displayName) { this.setAttribute(ATTRIBUTE_DISPLAY_NAME, displayName); } @Override public final String getName() { return getAttributeValue(ATTRIBUTE_NAME); } @Override public final String getDisplayName() { return getAttributeValue(ATTRIBUTE_DISPLAY_NAME); } @Override public final void setId(final String id) { this.setAttribute(ATTRIBUTE_ID, id); } @Override public final void setId(final Long id) { this.setAttribute(ATTRIBUTE_ID, id); } @Override public final void setProcessId(final APIID id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } @Override public final void setProcessId(final String id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } @Override public final void setProcessId(final Long id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } @Override public final APIID getProcessId() { return getAttributeValueAsAPIID(ATTRIBUTE_PROCESS_ID); } @Override @Deprecated public final void setCaseId(final APIID id) { setRootCaseId(id); } @Override @Deprecated public final void setCaseId(final String id) { setRootCaseId(id); } @Override @Deprecated public final void setCaseId(final Long id) { setRootCaseId(id); } @Override @Deprecated public final APIID getCaseId() { return getAttributeValueAsAPIID(ATTRIBUTE_CASE_ID); } @Override public void setRootCaseId(final APIID id) { setAttribute(ATTRIBUTE_ROOT_CASE_ID, id); setAttribute(ATTRIBUTE_CASE_ID, id); } @Override public void setRootCaseId(final String id) { setAttribute(ATTRIBUTE_ROOT_CASE_ID, id); setAttribute(ATTRIBUTE_CASE_ID, id); } @Override public void setRootCaseId(final Long id) { setAttribute(ATTRIBUTE_ROOT_CASE_ID, id); setAttribute(ATTRIBUTE_CASE_ID, id); } @Override public APIID getRootCaseId() { return getAttributeValueAsAPIID(ATTRIBUTE_ROOT_CASE_ID); } @Override public void setParentCaseId(final APIID id) { setAttribute(ATTRIBUTE_PARENT_CASE_ID, id); } @Override public void setParentCaseId(final String id) { setAttribute(ATTRIBUTE_PARENT_CASE_ID, id); } @Override public void setParentCaseId(final Long id) { setAttribute(ATTRIBUTE_PARENT_CASE_ID, id); } @Override public APIID getParentCaseId() { return getAttributeValueAsAPIID(ATTRIBUTE_PARENT_CASE_ID); } @Override public final void setRootContainerId(final APIID rootContainerId) { setAttribute(ATTRIBUTE_ROOT_CONTAINER_ID, rootContainerId); } @Override public final void setRootContainerId(final String rootContainerId) { setAttribute(ATTRIBUTE_ROOT_CONTAINER_ID, rootContainerId); } @Override public final void setRootContainerId(final Long rootContainerId) { setAttribute(ATTRIBUTE_ROOT_CONTAINER_ID, rootContainerId); } @Override public APIID getRootContainerId() { return this.getAttributeValueAsAPIID(ATTRIBUTE_ROOT_CONTAINER_ID); } @Override public final void setState(final String state) { setAttribute(ATTRIBUTE_STATE, state); } @Override public final String getState() { return getAttributeValue(ATTRIBUTE_STATE); } @Override public final void setType(final String type) { setAttribute(ATTRIBUTE_TYPE, type); } @Override public final String getType() { return getAttributeValue(ATTRIBUTE_TYPE); } @Override public final void setExecutedByUserId(final APIID id) { setAttribute(ATTRIBUTE_EXECUTED_BY_USER_ID, id); } @Override public final void setExecutedByUserId(final String id) { setAttribute(ATTRIBUTE_EXECUTED_BY_USER_ID, id); } @Override public final void setExecutedByUserId(final Long id) { setAttribute(ATTRIBUTE_EXECUTED_BY_USER_ID, id); } @Override public final APIID getExecutedByUserId() { return getAttributeValueAsAPIID(ATTRIBUTE_EXECUTED_BY_USER_ID); } @Override public final UserItem getExecutedByUser() { return new UserItem(getDeploy(ATTRIBUTE_EXECUTED_BY_USER_ID)); } @Override public void setExecutedBySubstituteUserId(final APIID id) { setAttribute(ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID, id); } @Override public void setExecutedBySubstituteUserId(final String id) { setAttribute(ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID, id); } @Override public void setExecutedBySubstituteUserId(final Long id) { setAttribute(ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID, id); } @Override public APIID getExecutedBySubstituteUserId() { return getAttributeValueAsAPIID(ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID); } @Override public UserItem getExecutedBySubstituteUser() { return new UserItem(getDeploy(ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID)); } @Override public final ProcessItem getProcess() { return new ProcessItem(getDeploy(ATTRIBUTE_PROCESS_ID)); } @Override @Deprecated public final CaseItem getCase() { return new CaseItem(getDeploy(ATTRIBUTE_CASE_ID)); } @Override public final CaseItem getRootCase() { return new CaseItem(getDeploy(ATTRIBUTE_ROOT_CASE_ID)); } @Override public final CaseItem getParentCase() { return new CaseItem(getDeploy(ATTRIBUTE_PARENT_CASE_ID)); } @Override public final ProcessItem getRootContainerProcess() { return new ProcessItem(getDeploy(ATTRIBUTE_ROOT_CONTAINER_ID)); } @Override public ItemDefinition getItemDefinition() { return FlowNodeDefinition.get(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/HumanTaskDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.rest.model.bpm.process.ActorDefinition; import org.bonitasoft.web.rest.model.identity.UserDefinition; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.EnumValidator; /** * @author Séverin Moussel */ public class HumanTaskDefinition extends TaskDefinition { public static final String TOKEN = "humantask"; private static final String API_URL = "../API/bpm/humanTask"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { super.defineAttributes(); // Restrict type to Humantasks getAttribute(TaskItem.ATTRIBUTE_TYPE) .removeValidator(EnumValidator.class.getName()) .addValidator(new EnumValidator( TaskItem.VALUE_TYPE_USER_TASK, TaskItem.VALUE_TYPE_MANUAL_TASK)); createAttribute(HumanTaskItem.ATTRIBUTE_ACTOR_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(HumanTaskItem.ATTRIBUTE_ASSIGNED_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(HumanTaskItem.ATTRIBUTE_PRIORITY, ItemAttribute.TYPE.ENUM) .setDefaultValue(HumanTaskItem.VALUE_PRIORITY_NORMAL) .addValidator(new EnumValidator( HumanTaskItem.VALUE_PRIORITY_LOWEST, HumanTaskItem.VALUE_PRIORITY_UNDER_NORMAL, HumanTaskItem.VALUE_PRIORITY_NORMAL, HumanTaskItem.VALUE_PRIORITY_ABOVE_NORMAL, HumanTaskItem.VALUE_PRIORITY_HIGHEST)); createAttribute(HumanTaskItem.ATTRIBUTE_DUE_DATE, ItemAttribute.TYPE.DATETIME); } @Override protected void defineDeploys() { super.defineDeploys(); declareDeployable(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID, Definitions.get(UserDefinition.TOKEN)); declareDeployable(HumanTaskItem.ATTRIBUTE_ACTOR_ID, Definitions.get(ActorDefinition.TOKEN)); } @Override public IHumanTaskItem _createItem() { return new HumanTaskItem(); } public static HumanTaskDefinition get() { return (HumanTaskDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/HumanTaskItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import java.util.Date; import org.bonitasoft.web.rest.model.bpm.process.ActorItem; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class HumanTaskItem extends TaskItem implements IHumanTaskItem { public HumanTaskItem() { super(); } public HumanTaskItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS @Override public final void setAssignedId(final String id) { setAttribute(ATTRIBUTE_ASSIGNED_USER_ID, id); } @Override public final void setAssignedId(final APIID id) { setAttribute(ATTRIBUTE_ASSIGNED_USER_ID, id); } @Override public final void setAssignedId(final Long id) { setAttribute(ATTRIBUTE_ASSIGNED_USER_ID, id); } @Override public final void setAssignedDate(final String date) { setAttribute(ATTRIBUTE_ASSIGNED_DATE, date); } @Override public final void setAssignedDate(final Date date) { setAttribute(ATTRIBUTE_ASSIGNED_DATE, date); } @Override public final void setPriority(final String priority) { setAttribute(ATTRIBUTE_PRIORITY, priority); } @Override public final void setDueDate(final String date) { setAttribute(ATTRIBUTE_DUE_DATE, date); } @Override public final void setDueDate(final Date date) { setAttribute(ATTRIBUTE_DUE_DATE, date); } @Override public final void setActorId(final APIID id) { setAttribute(ATTRIBUTE_ACTOR_ID, id); } @Override public final void setActorId(final String actorId) { setAttribute(ATTRIBUTE_ACTOR_ID, actorId); } @Override public final void setActorId(final Long actorId) { setAttribute(ATTRIBUTE_ACTOR_ID, actorId); } // Counters @Override public final void setNbOfAttachment(final String count) { setAttribute(COUNT_ATTACHMENT_NUMBER, count); } @Override public final void setNbOfAttachment(final int count) { setAttribute(COUNT_ATTACHMENT_NUMBER, count); } @Override public final void setNbOfComment(final String count) { setAttribute(COUNT_COMMENT_NUMBER, count); } @Override public final void setNbOfComment(final int count) { setAttribute(COUNT_COMMENT_NUMBER, count); } @Override public final void setNbOfActorUser(final String count) { setAttribute(COUNT_ACTOR_USER_NUMBER, count); } @Override public final void setNbOfActorUser(final int count) { setAttribute(COUNT_ACTOR_USER_NUMBER, count); } // GETTERS @Override public final APIID getActorId() { return getAttributeValueAsAPIID(ATTRIBUTE_ACTOR_ID); } @Override public final String getPriority() { return this.getAttributeValue(ATTRIBUTE_PRIORITY); } @Override public final String getDueDate() { return this.getAttributeValue(ATTRIBUTE_DUE_DATE); } @Override public final APIID getAssignedId() { return this.getAttributeValueAsAPIID(ATTRIBUTE_ASSIGNED_USER_ID); } @Override public final String getAssignedDate() { return this.getAttributeValue(ATTRIBUTE_ASSIGNED_DATE); } // Counters @Override public final Integer getNbOfAttachment() { return Integer.parseInt(this.getAttributeValue(COUNT_ATTACHMENT_NUMBER)); } @Override public final Integer getNbOfComment() { return Integer.parseInt(this.getAttributeValue(COUNT_COMMENT_NUMBER)); } @Override public final Integer getNbOfActorUser() { return Integer.parseInt(this.getAttributeValue(COUNT_ACTOR_USER_NUMBER)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEPLOYS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public final UserItem getAssignedUser() { IItem item = getDeploy(ATTRIBUTE_ASSIGNED_USER_ID); if (item == null) { return null; } return new UserItem(item); } @Override public final ActorItem getActor() { return new ActorItem(getDeploy(ATTRIBUTE_ACTOR_ID)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return new HumanTaskDefinition(); } @Override public final boolean isAssigned() { return getAssignedId() != null; } @Override public final boolean isUnassigned() { return !isAssigned(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/IActivityItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import java.util.Date; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate; /** * @author Séverin Moussel */ public interface IActivityItem extends ItemHasLastUpdateDate, IFlowNodeItem { // // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// String ATTRIBUTE_REACHED_STATE_DATE = "reached_state_date"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// String FILTER_SUPERVISOR_ID = "supervisor_id"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS AND GETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void setReachStateDate(final String reachedStateDate); void setReachStateDate(final Date reachedStateDate); Date getReachStateDate(); @Override void setLastUpdateDate(final String date); @Override void setLastUpdateDate(final Date date); @Override Date getLastUpdateDate(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/IFlowNodeItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.rest.model.bpm.cases.CaseItem; import org.bonitasoft.web.rest.model.bpm.process.ProcessItem; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualDescription; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Séverin Moussel */ public interface IFlowNodeItem extends IItem, ItemHasUniqueId, ItemHasDualName, ItemHasDualDescription { // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// String ATTRIBUTE_PROCESS_ID = "processId"; String ATTRIBUTE_CASE_ID = "caseId"; /** * @since 6.4.0 */ String ATTRIBUTE_ROOT_CASE_ID = "rootCaseId"; /** * @since 6.4.0 */ String ATTRIBUTE_PARENT_CASE_ID = "parentCaseId"; String ATTRIBUTE_PARENT_ACTIVITY_INSTANCE_ID = "parentActivityInstanceId"; String ATTRIBUTE_ROOT_CONTAINER_ID = "rootContainerId"; String ATTRIBUTE_STATE = "state"; String ATTRIBUTE_TYPE = "type"; String ATTRIBUTE_EXECUTED_BY_USER_ID = "executedBy"; String ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID = "executedBySubstitute"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES VALUES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// String VALUE_STATE_FAILED = "failed"; String VALUE_STATE_READY = "ready"; String VALUE_STATE_COMPLETED = "completed"; String VALUE_STATE_SKIPPED = "skipped"; String VALUE_STATE_REPLAY = "replay"; String VALUE_STATE_PENDING = "pending"; String VALUE_STATE_ONGOING = "ongoing"; String VALUE_TYPE_AUTOMATIC_TASK = "AUTOMATIC_TASK"; String VALUE_TYPE_USER_TASK = "USER_TASK"; String VALUE_TYPE_MANUAL_TASK = "MANUAL_TASK"; String VALUE_TYPE_CALL_ACTIVITY = "CALL_ACTIVITY"; String VALUE_TYPE_LOOP_ACTIVITY = "LOOP_ACTIVITY"; String VALUE_TYPE_MULTI_INSTANCE_ACTIVITY = "MULTI_INSTANCE_ACTIVITY"; String VALUE_TYPE_SUB_PROCESS_ACTIVITY = "SUB_PROCESS_ACTIVITY"; String VALUE_TYPE_GATEWAY = "GATEWAY"; String VALUE_TYPE_START_EVENT = "EVENT"; String VALUE_TYPE_INTERMEDIATE_CATCH_EVENT = "INTERMEDIATE_CATCH_EVENT"; String VALUE_TYPE_BOUNDARY_EVENT = "BOUNDARY_EVENT"; String VALUE_TYPE_INTERMEDIATE_THROW_EVENT = ""; String VALUE_TYPE_END_EVENT = "END_EVENT"; String FILTER_IS_FAILED = "isFailed"; void setProcessId(final APIID id); void setProcessId(final String id); void setProcessId(final Long id); APIID getProcessId(); /** * @param id * @see IFlowNodeItem#setRootCaseId(APIID) * @deprecated Since 6.4.0 */ @Deprecated void setCaseId(final APIID id); /** * @param id * @see IFlowNodeItem#setRootCaseId(String) * @deprecated Since 6.4.0 */ @Deprecated void setCaseId(final String id); /** * @param id * @see IFlowNodeItem#setRootCaseId(Long) * @deprecated Since 6.4.0 */ @Deprecated void setCaseId(final Long id); void setRootCaseId(final APIID id); void setRootCaseId(final String id); void setRootCaseId(final Long id); void setParentCaseId(final APIID id); void setParentCaseId(final String id); void setParentCaseId(final Long id); /** * @return * @Deprecated Since 6.4.0 * @see IFlowNodeItem#getRootCaseId() */ @Deprecated APIID getCaseId(); APIID getRootCaseId(); APIID getParentCaseId(); void setRootContainerId(final APIID rootContainerId); void setRootContainerId(final String rootContainerId); void setRootContainerId(final Long rootContainerId); APIID getRootContainerId(); void setState(final String state); String getState(); void setType(final String type); String getType(); void setExecutedByUserId(final APIID id); void setExecutedByUserId(final String id); void setExecutedByUserId(final Long id); APIID getExecutedByUserId(); UserItem getExecutedByUser(); void setExecutedBySubstituteUserId(final APIID id); void setExecutedBySubstituteUserId(final String id); void setExecutedBySubstituteUserId(final Long id); APIID getExecutedBySubstituteUserId(); UserItem getExecutedBySubstituteUser(); ProcessItem getProcess(); /** * @return * @see IFlowNodeItem#getCase() * @deprecated since 6.4.0 */ @Deprecated CaseItem getCase(); CaseItem getRootCase(); CaseItem getParentCase(); ProcessItem getRootContainerProcess(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/IHumanTaskItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import java.util.Date; import org.bonitasoft.web.rest.model.bpm.process.ActorItem; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public interface IHumanTaskItem extends ITaskItem { // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES NAMES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// String ATTRIBUTE_ASSIGNED_USER_ID = "assigned_id"; String ATTRIBUTE_ASSIGNED_DATE = "assigned_date"; String ATTRIBUTE_PRIORITY = "priority"; String ATTRIBUTE_DUE_DATE = "dueDate"; String ATTRIBUTE_ACTOR_ID = "actorId"; /* * Same as ATTRIBUTE_PARENT_CONTAINER_ID * Should be in manual task but lives there because of deploy restrictions */ String ATTRIBUTE_PARENT_TASK_ID = "parentTaskId"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES VALUES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// String VALUE_PRIORITY_HIGHEST = "highest"; String VALUE_PRIORITY_ABOVE_NORMAL = "above_normal"; String VALUE_PRIORITY_NORMAL = "normal"; String VALUE_PRIORITY_UNDER_NORMAL = "under_normal"; String VALUE_PRIORITY_LOWEST = "lowest"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // COUNTS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// String COUNT_ATTACHMENT_NUMBER = "nb_of_attachment"; String COUNT_ACTOR_USER_NUMBER = "nb_of_actor_user"; String COUNT_COMMENT_NUMBER = "nb_of_comment"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// String FILTER_TEAM_MANAGER_ID = "team_manager_id"; String FILTER_USER_ID = "user_id"; String FILTER_HIDDEN_TO_USER_ID = "hidden_user_id"; String FILTER_IS_ASSIGNED = "is_claimed"; String FILTER_SHOW_ASSIGNED_TO_OTHERS = "show_assigned_to_others"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS void setAssignedId(final String id); void setAssignedId(final APIID id); void setAssignedId(final Long id); void setAssignedDate(final String date); void setAssignedDate(final Date date); void setPriority(final String priority); void setDueDate(final String date); void setDueDate(final Date date); void setActorId(final APIID id); void setActorId(final String actorId); void setActorId(final Long actorId); // Counters void setNbOfAttachment(final String count); void setNbOfAttachment(final int count); void setNbOfComment(final String count); void setNbOfComment(final int count); void setNbOfActorUser(final String count); void setNbOfActorUser(final int count); // GETTERS APIID getActorId(); String getPriority(); String getDueDate(); APIID getAssignedId(); String getAssignedDate(); // Counters Integer getNbOfAttachment(); Integer getNbOfComment(); Integer getNbOfActorUser(); // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEPLOYS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// UserItem getAssignedUser(); ActorItem getActor(); // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// boolean isAssigned(); boolean isUnassigned(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/ITaskItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; /** * @author Séverin Moussel */ public interface ITaskItem extends IActivityItem { } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/IUserTaskItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; /** * @author Séverin Moussel */ public interface IUserTaskItem extends IHumanTaskItem { } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/TaskDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.EnumValidator; /** * @author Séverin Moussel */ public class TaskDefinition extends ActivityDefinition { public static final String TOKEN = "task"; public static final String API_URL = "../API/bpm/task"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { super.defineAttributes(); // Restrict type to tasks getAttribute(TaskItem.ATTRIBUTE_TYPE) .removeValidator(EnumValidator.class.getName()) .addValidator(new EnumValidator( TaskItem.VALUE_TYPE_USER_TASK, TaskItem.VALUE_TYPE_AUTOMATIC_TASK, TaskItem.VALUE_TYPE_MANUAL_TASK)); // add extra state ((EnumValidator) getAttribute(FlowNodeItem.ATTRIBUTE_STATE) .getValidator(EnumValidator.class.getName())) .addValue(TaskItem.VALUE_STATE_REPLAY); } @Override public ITaskItem _createItem() { return new TaskItem(); } public static TaskDefinition get() { return (TaskDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/TaskItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class TaskItem extends ActivityItem implements ITaskItem { public TaskItem() { super(); } public TaskItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return new TaskDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/UserTaskDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.EnumValidator; /** * @author Séverin Moussel */ public class UserTaskDefinition extends HumanTaskDefinition { public static final String TOKEN = "usertask"; private static final String API_URL = "../API/bpm/userTask"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { super.defineAttributes(); // Restrict type to manual tasks getAttribute(UserTaskItem.ATTRIBUTE_TYPE) .removeValidator(EnumValidator.class.getName()) .addValidator(new EnumValidator( UserTaskItem.VALUE_TYPE_MANUAL_TASK)); } @Override public IUserTaskItem _createItem() { return new UserTaskItem(); } public static UserTaskDefinition get() { return (UserTaskDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/flownode/UserTaskItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.flownode; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class UserTaskItem extends HumanTaskItem implements IUserTaskItem { public UserTaskItem() { super(); } public UserTaskItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return new UserTaskDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ActorDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import static org.bonitasoft.web.rest.model.bpm.process.ActorItem.ATTRIBUTE_DESCRIPTION; import static org.bonitasoft.web.rest.model.bpm.process.ActorItem.ATTRIBUTE_PROCESS_ID; import static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName.ATTRIBUTE_DISPLAY_NAME; import static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName.ATTRIBUTE_NAME; import static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId.ATTRIBUTE_ID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Haojie Yuan * @author Séverin Moussel */ public class ActorDefinition extends ItemDefinition { /** * Singleton */ public static ActorDefinition get() { return (ActorDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "actor"; @Override public String defineToken() { return TOKEN; } private static final String API_URL = "../API/bpm/actor"; @Override protected String defineAPIUrl() { return API_URL; } @Override protected void definePrimaryKeys() { setPrimaryKeys(ATTRIBUTE_ID); } /** * categoryList */ @Override protected void defineAttributes() { createAttribute(ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING).isMandatory(); createAttribute(ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.STRING); createAttribute(ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT); createAttribute(ATTRIBUTE_PROCESS_ID, ItemAttribute.TYPE.ITEM_ID).isMandatory(); } @Override public IItem _createItem() { return new ActorItem(); } @Override protected void defineDeploys() { declareDeployable(ATTRIBUTE_PROCESS_ID, ProcessDefinition.get()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ActorItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Haojie Yuan * @author Séverin Moussel */ public class ActorItem extends Item implements ItemHasUniqueId, ItemHasDualName { public ActorItem() { super(); } public ActorItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String ATTRIBUTE_DESCRIPTION = "description"; public static final String ATTRIBUTE_PROCESS_ID = "process_id"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // COUNTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String COUNTER_USERS = "users"; public static final String COUNTER_GROUPS = "groups"; public static final String COUNTER_ROLES = "roles"; public static final String COUNTER_MEMBERSHIPS = "memberships"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS AND GETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Setters @Override public void setId(final String id) { setAttribute(ATTRIBUTE_ID, id); } @Override public void setId(final Long id) { setAttribute(ATTRIBUTE_ID, id); } @Override public void setName(final String name) { setAttribute(ATTRIBUTE_NAME, name); } @Override public void setDisplayName(final String name) { setAttribute(ATTRIBUTE_DISPLAY_NAME, name); } public void setDescription(final String description) { setAttribute(ATTRIBUTE_DESCRIPTION, description); } public void setProcessId(final String id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } public void setProcessId(final Long id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } public void setProcessId(final APIID id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } // Getters @Override public String getName() { return getAttributeValue(ATTRIBUTE_NAME); } @Override public String getDisplayName() { return getAttributeValue(ATTRIBUTE_DISPLAY_NAME); } public String getDescription() { return getAttributeValue(ATTRIBUTE_DESCRIPTION); } public APIID getProcessId() { return getAttributeValueAsAPIID(ATTRIBUTE_PROCESS_ID); } // Counters public Long getNbSelectedUsers() { return getAttributeValueAsLong(COUNTER_USERS); } public Long getNbSelectedGroups() { return getAttributeValueAsLong(COUNTER_GROUPS); } public Long getNbSelectedRoles() { return getAttributeValueAsLong(COUNTER_ROLES); } public Long getNbSelectedMembershipss() { return getAttributeValueAsLong(COUNTER_MEMBERSHIPS); } // Deploys public ProcessItem getProcess() { return new ProcessItem(getDeploy(ATTRIBUTE_PROCESS_ID)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return ActorDefinition.get(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ActorMemberDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.rest.model.portal.profile.AbstractMemberDefinition; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Haojie Yuan * @author Séverin Moussel */ public class ActorMemberDefinition extends AbstractMemberDefinition { /** * Singleton */ public static ActorMemberDefinition get() { return (ActorMemberDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "actormember"; /** * the URL of users resource */ private static final String API_URL = "../API/bpm/actorMember"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { // FIXME : Remove after engine has removed the unique ID. setPrimaryKeys(ActorMemberItem.ATTRIBUTE_ID); // FIXME : Uncomment after engine has removed the unique ID. // setPrimaryKeys( // ATTRIBUTE_ACTOR_ID, // ATTRIBUTE_USER_ID, // ATTRIBUTE_ROLE_ID, // ATTRIBUTE_GROUP_ID); } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(ActorMemberItem.ATTRIBUTE_ACTOR_ID, ItemAttribute.TYPE.ITEM_ID); super.defineAttributes(); } @Override protected void defineDeploys() { declareDeployable(ActorMemberItem.ATTRIBUTE_ACTOR_ID, ActorDefinition.get()); super.defineDeploys(); } @Override public IItem _createItem() { return new ActorMemberItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ActorMemberItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Séverin Moussel */ public class ActorMemberItem extends AbstractMemberItem implements ItemHasUniqueId { public ActorMemberItem() { super(); } public ActorMemberItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String ATTRIBUTE_ACTOR_ID = "actor_id"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS AND GETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Setters @Override public void setId(final String id) { setAttribute(ATTRIBUTE_ID, id); } @Override public void setId(final Long id) { setAttribute(ATTRIBUTE_ID, id); } public void setActorId(final APIID id) { setAttribute(ATTRIBUTE_ACTOR_ID, id); } public void setActorId(final Long id) { setAttribute(ATTRIBUTE_ACTOR_ID, id); } public void setActorId(final String id) { setAttribute(ATTRIBUTE_ACTOR_ID, id); } // Getters public APIID getActorId() { return getAttributeValueAsAPIID(ATTRIBUTE_ACTOR_ID); } // Deploys public ActorItem getActor() { return new ActorItem(getDeploy(ATTRIBUTE_ACTOR_ID)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return ActorMemberDefinition.get(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/CategoryDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Zhiheng Yang */ public class CategoryDefinition extends ItemDefinition { /** * Singleton */ public static CategoryDefinition get() { return (CategoryDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "categories"; /** * the URL of categories resource */ private static final String API_URL = "../API/bpm/category"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(CategoryItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING).isMandatory(); createAttribute(CategoryItem.ATTRIBUTE_CREATED_BY_USER_ID, ItemAttribute.TYPE.STRING); createAttribute(CategoryItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT); } @Override protected void definePrimaryKeys() { setPrimaryKeys(CategoryItem.ATTRIBUTE_ID); } @Override protected IItem _createItem() { return new CategoryItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/CategoryItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import java.util.Date; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasCreator; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * category item * * @author Qixiang Zhang */ public class CategoryItem extends Item implements ItemHasUniqueId, ItemHasDualName, ItemHasCreator, ItemHasLastUpdateDate { public CategoryItem() { super(); } public CategoryItem(final IItem item) { super(item); } /** * category of id */ public static final String ATTRIBUTE_ID = "id"; /** * created by */ public static final String ATTRIBUTE_CREATED_BY_USER_ID = "createdBy"; /** * category name */ public static final String ATTRIBUTE_NAME = "name"; /** * description about category */ public static final String ATTRIBUTE_DESCRIPTION = "description"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES VALUES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String FILTER_PROCESS_ID = "processId"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // COUNTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS AND SETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS @Override public String getName() { return this.getAttributeValue(ATTRIBUTE_NAME); } public String getDescription() { return this.getAttributeValue(ATTRIBUTE_DESCRIPTION); } @Override public APIID getCreatedByUserId() { return this.getAttributeValueAsAPIID(ATTRIBUTE_CREATED_BY_USER_ID); } @Override public UserItem getCreatedByUser() { return new UserItem(getDeploy(ATTRIBUTE_CREATED_BY_USER_ID)); } @Override public Date getCreationDate() { return this.getAttributeValueAsDate(ATTRIBUTE_CREATION_DATE); } @Override public String getDisplayName() { return this.getAttributeValue(ATTRIBUTE_DISPLAY_NAME); } // SETTERS public void setCreatedby(final String attributeCreatedby) { this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, attributeCreatedby); } @Override public void setCreatedByUserId(final Long id) { this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id.toString()); } @Override public void setCreationDate(final String date) { this.setAttribute(ATTRIBUTE_CREATION_DATE, date); } @Override public void setCreationDate(final Date date) { this.setAttribute(ATTRIBUTE_CREATION_DATE, date); } @Override public void setCreatedByUserId(final APIID id) { this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id); } @Override public void setCreatedByUserId(final String id) { this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id); } @Override public void setId(final String id) { this.setAttribute("id", id); } @Override public void setId(final Long id) { this.setAttribute("id", id.toString()); } @Override public void setName(final String name) { this.setAttribute(ATTRIBUTE_NAME, name); } @Override public void setDisplayName(final String displayName) { this.setAttribute(ATTRIBUTE_DISPLAY_NAME, displayName); } public void setDescription(final String description) { this.setAttribute(ATTRIBUTE_DESCRIPTION, description); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return new CategoryDefinition(); } @Override public void setLastUpdateDate(final String date) { this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } @Override public void setLastUpdateDate(final Date date) { this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } @Override public Date getLastUpdateDate() { return this.getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessCategoryDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE; /** * @author Séverin Moussel */ public class ProcessCategoryDefinition extends ItemDefinition { /** * Singleton */ public static ProcessCategoryDefinition get() { return (ProcessCategoryDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "processcategory"; public static final String API_URL = "../API/bpm/processCategory"; @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(ProcessCategoryItem.ATTRIBUTE_PROCESS_ID, TYPE.ITEM_ID).isMandatory(); createAttribute(ProcessCategoryItem.ATTRIBUTE_CATEGORY_ID, TYPE.ITEM_ID).isMandatory(); } @Override protected void definePrimaryKeys() { setPrimaryKeys( ProcessCategoryItem.ATTRIBUTE_PROCESS_ID, ProcessCategoryItem.ATTRIBUTE_CATEGORY_ID); } @Override protected IItem _createItem() { return new ProcessCategoryItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessCategoryItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class ProcessCategoryItem extends Item { // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String ATTRIBUTE_PROCESS_ID = "process_id"; public static final String ATTRIBUTE_CATEGORY_ID = "category_id"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS AND GETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS public final void setProcessId(final APIID id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } public final void setProcessId(final String id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } public final void setProcessId(final Long id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } public final void setCategoryId(final APIID id) { setAttribute(ATTRIBUTE_CATEGORY_ID, id); } public final void setCategoryId(final String id) { setAttribute(ATTRIBUTE_CATEGORY_ID, id); } public final void setCategoryId(final Long id) { setAttribute(ATTRIBUTE_CATEGORY_ID, id); } // GETTERS public final APIID getProcessId() { return getAttributeValueAsAPIID(ATTRIBUTE_PROCESS_ID); } public final APIID getCategoryId() { return getAttributeValueAsAPIID(ATTRIBUTE_CATEGORY_ID); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEPLOYS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public final ProcessItem getProcess() { return new ProcessItem(getDeploy(ATTRIBUTE_PROCESS_ID)); } public final CategoryItem getCategory() { return new CategoryItem(getDeploy(ATTRIBUTE_CATEGORY_ID)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public final ItemDefinition getItemDefinition() { return ProcessCategoryDefinition.get(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessConnectorDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Séverin Moussel */ public class ProcessConnectorDefinition extends ItemDefinition { /** * Singleton */ public static ProcessConnectorDefinition get() { return (ProcessConnectorDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "processconnector"; private static final String API_URL = "../API/bpm/processConnector"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void definePrimaryKeys() { setPrimaryKeys( ProcessConnectorItem.ATTRIBUTE_PROCESS_ID, ProcessConnectorItem.ATTRIBUTE_NAME, ProcessConnectorItem.ATTRIBUTE_VERSION); } @Override protected void defineAttributes() { createAttribute(ProcessConnectorItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING) .isMandatory(); createAttribute(ProcessConnectorItem.ATTRIBUTE_VERSION, ItemAttribute.TYPE.STRING) .isMandatory(); createAttribute(ProcessConnectorItem.ATTRIBUTE_PROCESS_ID, ItemAttribute.TYPE.ITEM_ID) .isMandatory(); createAttribute(ProcessConnectorItem.ATTRIBUTE_IMPLEMENTATION_NAME, ItemAttribute.TYPE.STRING) .isMandatory(); createAttribute(ProcessConnectorItem.ATTRIBUTE_IMPLEMENTATION_VERSION, ItemAttribute.TYPE.STRING) .isMandatory(); createAttribute(ProcessConnectorItem.ATTRIBUTE_CLASSNAME, ItemAttribute.TYPE.STRING) .isMandatory(); } @Override protected void defineDeploys() { declareDeployable(ProcessConnectorItem.ATTRIBUTE_PROCESS_ID, ProcessDefinition.get()); } @Override protected IItem _createItem() { return new ProcessConnectorItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessConnectorDependencyDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Gai Cuisha */ public class ProcessConnectorDependencyDefinition extends ItemDefinition { /** * Singleton */ public static ProcessConnectorDependencyDefinition get() { return (ProcessConnectorDependencyDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "processconnectordependency"; /** * the URL of user resource */ private static final String API_URL = "../API/bpm/processConnectorDependency"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { setPrimaryKeys( ProcessConnectorDependencyItem.ATTRIBUTE_PROCESS_ID, ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_NAME, ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_VERSION, ProcessConnectorDependencyItem.ATTRIBUTE_FILENAME); } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(ProcessConnectorDependencyItem.ATTRIBUTE_PROCESS_ID, ItemAttribute.TYPE.ITEM_ID).isMandatory(); createAttribute(ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_NAME, ItemAttribute.TYPE.STRING) .isMandatory(); createAttribute(ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_VERSION, ItemAttribute.TYPE.STRING) .isMandatory(); createAttribute(ProcessConnectorDependencyItem.ATTRIBUTE_FILENAME, ItemAttribute.TYPE.STRING).isMandatory(); } @Override protected IItem _createItem() { return new ProcessConnectorDependencyItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessConnectorDependencyItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class ProcessConnectorDependencyItem extends Item { public ProcessConnectorDependencyItem() { super(); } public ProcessConnectorDependencyItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String ATTRIBUTE_PROCESS_ID = "connector_process_id"; public static final String ATTRIBUTE_CONNECTOR_NAME = "connector_name"; public static final String ATTRIBUTE_CONNECTOR_VERSION = "connector_version"; public static final String ATTRIBUTE_FILENAME = "filename"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS AND GETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Setters public void setConnectorId(final APIID id) { setProcessId(id.getPart(ATTRIBUTE_PROCESS_ID)); setConnectorName(id.getPart(ATTRIBUTE_CONNECTOR_NAME)); setConnectorVersion(id.getPart(ATTRIBUTE_CONNECTOR_VERSION)); } public void setProcessId(final String id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } public void setProcessId(final Long id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } public void setProcessId(final APIID id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } public void setConnectorName(final String name) { setAttribute(ATTRIBUTE_CONNECTOR_NAME, name); } public void setConnectorVersion(final String version) { setAttribute(ATTRIBUTE_CONNECTOR_VERSION, version); } public void setFilename(final String filename) { setAttribute(ATTRIBUTE_FILENAME, filename); } // Getters public APIID getProcessId() { return getAttributeValueAsAPIID(ATTRIBUTE_PROCESS_ID); } public String getConnectorName() { return getAttributeValue(ATTRIBUTE_CONNECTOR_NAME); } public String getConnectorVersion() { return getAttributeValue(ATTRIBUTE_CONNECTOR_VERSION); } public String getFilename() { return getAttributeValue(ATTRIBUTE_FILENAME); } // Deploys public ProcessItem getProcess() { return new ProcessItem(getDeploy(ATTRIBUTE_PROCESS_ID)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return ProcessConnectorDependencyDefinition.get(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessConnectorItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class ProcessConnectorItem extends Item { public ProcessConnectorItem() { super(); } public ProcessConnectorItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String ATTRIBUTE_NAME = "definition_id"; public static final String ATTRIBUTE_VERSION = "definition_version"; public static final String ATTRIBUTE_PROCESS_ID = "process_id"; public static final String ATTRIBUTE_IMPLEMENTATION_NAME = "impl_name"; public static final String ATTRIBUTE_IMPLEMENTATION_VERSION = "impl_version"; public static final String ATTRIBUTE_CLASSNAME = "classname"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS AND GETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Setters public void setName(final String name) { setAttribute(ATTRIBUTE_NAME, name); } public void setVersion(final String version) { setAttribute(ATTRIBUTE_VERSION, version); } public void setProcessId(final String id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } public void setProcessId(final APIID id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } public void setProcessId(final long id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } public void setImplementationName(final String name) { setAttribute(ATTRIBUTE_IMPLEMENTATION_NAME, name); } public void setImplementationVersion(final String version) { setAttribute(ATTRIBUTE_IMPLEMENTATION_VERSION, version); } public void setClassname(final String classname) { setAttribute(ATTRIBUTE_CLASSNAME, classname); } // Getters public String getName() { return getAttributeValue(ATTRIBUTE_NAME); } public String getVersion() { return getAttributeValue(ATTRIBUTE_VERSION); } public APIID getProcessId() { return getAttributeValueAsAPIID(ATTRIBUTE_PROCESS_ID); } public String getImplementationName() { return getAttributeValue(ATTRIBUTE_IMPLEMENTATION_NAME); } public String getImplementationVersion() { return getAttributeValue(ATTRIBUTE_IMPLEMENTATION_VERSION); } public String getClassname() { return getAttributeValue(ATTRIBUTE_CLASSNAME); } // Deploys public ProcessItem getProcess() { return new ProcessItem(getDeploy(ATTRIBUTE_PROCESS_ID)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return new ProcessConnectorDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.EnumValidator; /** * @author Vincent Elcrin */ public class ProcessDefinition extends ItemDefinition { /** * Singleton */ public static ProcessDefinition get() { return (ProcessDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "process"; /** * the URL of users resource */ private static final String API_URL = "../API/bpm/process"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { setPrimaryKeys(ProcessItem.ATTRIBUTE_ID); } @Override protected String defineAPIUrl() { return API_URL; } /** * categoryList */ @Override protected void defineAttributes() { createAttribute(ProcessItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ProcessItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING) .isMandatory(); createAttribute(ProcessItem.ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.STRING); createAttribute(ProcessItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT); createAttribute(ProcessItem.ATTRIBUTE_DISPLAY_DESCRIPTION, ItemAttribute.TYPE.TEXT); createAttribute(ProcessItem.ATTRIBUTE_VERSION, ItemAttribute.TYPE.STRING) .isMandatory(); createAttribute(ProcessItem.ATTRIBUTE_ACTIVATION_STATE, ItemAttribute.TYPE.ENUM) .addValidator(new EnumValidator(ProcessItem.VALUE_ACTIVATION_STATE_DISABLED, ProcessItem.VALUE_ACTIVATION_STATE_ENABLED)); createAttribute(ProcessItem.ATTRIBUTE_CONFIGURATION_STATE, ItemAttribute.TYPE.ENUM) .addValidator(new EnumValidator(ProcessItem.VALUE_CONFIGURATION_STATE_UNRESOLVED, ProcessItem.VALUE_CONFIGURATION_STATE_RESOLVED)); createAttribute(ProcessItem.ATTRIBUTE_DEPLOYED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ProcessItem.ATTRIBUTE_DEPLOYMENT_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(ProcessItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(ProcessItem.ATTRIBUTE_ACTOR_INITIATOR_ID, ItemAttribute.TYPE.ITEM_ID); } @Override public ProcessItem _createItem() { return new ProcessItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessDocumentDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.rest.model.bpm.AbstractDocumentDefinition; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Paul AMAR */ public class ProcessDocumentDefinition extends AbstractDocumentDefinition { public static final String TOKEN = "process"; /** * the URL of user resource */ private static final String API_URL = "../API/document/process"; @Override protected void definePrimaryKeys() { } @Override protected IItem _createItem() { return new ProcessDocumentItem(); } @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { // TODO Auto-generated method stub } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessDocumentItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.rest.model.bpm.AbstractDocumentItem; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Paul AMAR */ public class ProcessDocumentItem extends AbstractDocumentItem { private final String ATTRIBUTE_PROCESS_ID = "processId"; /** * Default Constructor. */ public ProcessDocumentItem() { super(); } @Override public ItemDefinition getItemDefinition() { return new ProcessDocumentDefinition(); } public String getProcessId() { return this.getAttributeValue(this.ATTRIBUTE_PROCESS_ID); } public IItem setProcessId(final String processId) { this.setAttribute(this.ATTRIBUTE_PROCESS_ID, processId); return this; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import java.util.Date; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Vincent Elcrin * @author Celine Souchet */ public class ProcessItem extends Item implements ItemHasUniqueId, ItemHasLastUpdateDate, ItemHasDualName { public ProcessItem() { super(); } public ProcessItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES NAMES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String ATTRIBUTE_VERSION = "version"; public static final String ATTRIBUTE_DESCRIPTION = "description"; public static final String ATTRIBUTE_DEPLOYMENT_DATE = "deploymentDate"; public static final String ATTRIBUTE_DEPLOYED_BY_USER_ID = "deployedBy"; public static final String ATTRIBUTE_ACTIVATION_STATE = "activationState"; public static final String ATTRIBUTE_CONFIGURATION_STATE = "configurationState"; public static final String ATTRIBUTE_DISPLAY_DESCRIPTION = "displayDescription"; public static final String ATTRIBUTE_ACTOR_INITIATOR_ID = "actorinitiatorid"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES VALUES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String VALUE_ACTIVATION_STATE_DISABLED = "DISABLED"; public static final String VALUE_ACTIVATION_STATE_ENABLED = "ENABLED"; public static final String VALUE_CONFIGURATION_STATE_UNRESOLVED = "UNRESOLVED"; public static final String VALUE_CONFIGURATION_STATE_RESOLVED = "RESOLVED"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String FILTER_TEAM_MANAGER_ID = "team_manager_id"; public static final String FILTER_SUPERVISOR_ID = "supervisor_id"; public static final String FILTER_USER_ID = "user_id"; public static final String FILTER_RECENT_PROCESSES = "recentProcesses"; public static final String FILTER_CATEGORY_ID = "categoryId"; public static final String FILTER_FOR_PENDING_OR_ASSIGNED_TASKS = "forPendingOrAssignedTask"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // COUNTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String COUNTER_FAILED_CASES = "failedCases"; public static final String COUNTER_OPEN_CASES = "openCases"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS AND SETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS @Override public String getName() { return this.getAttributeValue(ATTRIBUTE_NAME); } public String getVersion() { return this.getAttributeValue(ATTRIBUTE_VERSION); } public String ensureName() { if (StringUtil.isBlank(getDisplayName())) { return getName(); } return getDisplayName(); } public String getDescription() { return this.getAttributeValue(ATTRIBUTE_DESCRIPTION); } public String getDeploymentDate() { return this.getAttributeValue(ATTRIBUTE_DEPLOYMENT_DATE); } public APIID getDeployedByUserId() { return APIID.makeAPIID(this.getAttributeValue(ATTRIBUTE_DEPLOYED_BY_USER_ID)); } public String getActivationState() { return this.getAttributeValue(ATTRIBUTE_ACTIVATION_STATE); } public String getConfigurationState() { return this.getAttributeValue(ATTRIBUTE_CONFIGURATION_STATE); } @Override public String getDisplayName() { return this.getAttributeValue(ATTRIBUTE_DISPLAY_NAME); } public String getDisplayDescription() { return this.getAttributeValue(ATTRIBUTE_DISPLAY_DESCRIPTION); } @Override public Date getLastUpdateDate() { return this.getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE); } public String getActorInitiatorId() { return this.getAttributeValue(ATTRIBUTE_ACTOR_INITIATOR_ID); } // SETTERS @Override public void setId(final String id) { this.setAttribute(ATTRIBUTE_ID, id); } @Override public void setId(final Long id) { this.setId(id.toString()); } @Override public void setName(final String name) { this.setAttribute(ATTRIBUTE_NAME, name); } public void setVersion(final String version) { this.setAttribute(ATTRIBUTE_VERSION, version); } public void setDescription(final String description) { this.setAttribute(ATTRIBUTE_DESCRIPTION, description); } public void setDeployedByUserId(final String id) { this.setAttribute(ATTRIBUTE_DEPLOYED_BY_USER_ID, id); } public void setDeployedByUserId(final Long id) { setDeployedByUserId(id.toString()); } public void setDeployedByUserId(final APIID id) { this.setAttribute(ATTRIBUTE_DEPLOYED_BY_USER_ID, id); } public void setDeploymentDate(final String date) { this.setAttribute(ATTRIBUTE_DEPLOYMENT_DATE, date); } public void setDeploymentDate(final Date date) { this.setAttribute(ATTRIBUTE_DEPLOYMENT_DATE, date); } public void setActivationState(final String state) { this.setAttribute(ATTRIBUTE_ACTIVATION_STATE, state); } public void setConfigurationState(final String state) { this.setAttribute(ATTRIBUTE_CONFIGURATION_STATE, state); } @Override public void setDisplayName(final String displayName) { this.setAttribute(ATTRIBUTE_DISPLAY_NAME, displayName); } public void setDisplayDescription(final String displayDescription) { this.setAttribute(ATTRIBUTE_DISPLAY_DESCRIPTION, displayDescription); } @Override public void setLastUpdateDate(final String date) { this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } @Override public void setLastUpdateDate(final Date date) { this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } public void setActorInitiatorId(final Long id) { setActorInitiatorId(id.toString()); } public void setActorInitiatorId(final APIID id) { setActorInitiatorId(id.toString()); } public void setActorInitiatorId(final String id) { this.setAttribute(ATTRIBUTE_ACTOR_INITIATOR_ID, id); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEPLOYS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public UserItem getDeployedByUser() { return new UserItem(getDeploy(ATTRIBUTE_DEPLOYED_BY_USER_ID)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public boolean isEnabled() { return VALUE_ACTIVATION_STATE_ENABLED.equals(getActivationState()); } public boolean isResolved() { return VALUE_CONFIGURATION_STATE_RESOLVED.equals(getConfigurationState()); } public boolean isStartable() { return isEnabled() && isResolved(); } /* * (non-Javadoc) * @see org.bonitasoft.web.toolkit.client.data.item.Item#getItemDefinition() */ @Override public ItemDefinition getItemDefinition() { return ProcessDefinition.get(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessParameterDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringMaxLengthValidator; /** * @author Yongtao Guo, Haojie Yuan, Anthony Birembaut */ public class ProcessParameterDefinition extends ItemDefinition { /** * Singleton */ public static ProcessParameterDefinition get() { return (ProcessParameterDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "processParameter"; /** * the URL of users resource */ private static final String API_URL = "../API/bpm/processParameter"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { setPrimaryKeys(ProcessParameterItem.ATTRIBUTE_PROCESS_ID, ProcessParameterItem.ATTRIBUTE_NAME); } @Override protected String defineAPIUrl() { return API_URL; } /** * categoryList */ @Override protected void defineAttributes() { createAttribute(ProcessParameterItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING); createAttribute(ProcessParameterItem.ATTRIBUTE_TYPE, ItemAttribute.TYPE.STRING); createAttribute(ProcessParameterItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT); createAttribute(ProcessParameterItem.ATTRIBUTE_VALUE, ItemAttribute.TYPE.STRING) .removeValidator(StringMaxLengthValidator.class.getName()); } @Override public ProcessParameterItem _createItem() { return new ProcessParameterItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessParameterItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Ruiheng Fan */ public class ProcessParameterItem extends Item { public ProcessParameterItem() { super(); } public ProcessParameterItem(final IItem item) { super(item); } public static final String ATTRIBUTE_PROCESS_ID = "process_id"; public static final String ATTRIBUTE_NAME = "name"; public static final String ATTRIBUTE_TYPE = "type"; public static final String ATTRIBUTE_VALUE = "value"; public static final String ATTRIBUTE_DESCRIPTION = "description"; public static final String ATTRIBUTE_PROCESS_NAME = "process_name"; public static final String ATTRIBUTE_PROCESS_VERSION = "process_version"; public static final String FILTER_PROCESS_ID = "process_id"; /** * Default Constructor. * * @param ProcessParameter * name * @param ProcessParameter * type * @param ProcessParameter * value * @param ProcessParameter * description */ public ProcessParameterItem(final String processId, final String name, final String type, final String value, final String description, final String processName, final String processVersion) { this.setAttribute(ATTRIBUTE_PROCESS_ID, processId); this.setAttribute(ATTRIBUTE_NAME, name); this.setAttribute(ATTRIBUTE_TYPE, type); this.setAttribute(ATTRIBUTE_VALUE, value); this.setAttribute(ATTRIBUTE_DESCRIPTION, description); this.setAttribute(ATTRIBUTE_PROCESS_NAME, processName); this.setAttribute(ATTRIBUTE_PROCESS_VERSION, processVersion); } @Override public ItemDefinition getItemDefinition() { return new ProcessParameterDefinition(); } public String getName() { return getAttributeValue(ATTRIBUTE_NAME); } public String getType() { return getAttributeValue(ATTRIBUTE_TYPE); } public String getValue() { return getAttributeValue(ATTRIBUTE_VALUE); } public String getDescription() { return getAttributeValue(ATTRIBUTE_DESCRIPTION); } public void setValue(String value) { setAttribute(ATTRIBUTE_VALUE, value); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessResolutionProblemDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.EnumValidator; /** * @author Séverin Moussel */ public class ProcessResolutionProblemDefinition extends ItemDefinition { public static ProcessResolutionProblemDefinition get() { return (ProcessResolutionProblemDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "processresolutionerror"; private static final String API_URL = "../API/bpm/processResolutionProblem"; @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(ProcessResolutionProblemItem.FILTER_PROCESS_ID, TYPE.ITEM_ID) .isMandatory(); createAttribute(ProcessResolutionProblemItem.ATTRIBUTE_MESSAGE, TYPE.STRING) .isMandatory(); createAttribute(ProcessResolutionProblemItem.ATTRIBUTE_TARGET_TYPE, TYPE.ENUM) .isMandatory() .addValidator(new EnumValidator( ProcessResolutionProblemItem.VALUE_STATE_TARGET_TYPE_ACTOR, ProcessResolutionProblemItem.VALUE_STATE_TARGET_TYPE_CONNECTOR, ProcessResolutionProblemItem.VALUE_STATE_TARGET_TYPE_PARAMETER)); } @Override protected void definePrimaryKeys() { setPrimaryKeys(ProcessResolutionProblemItem.FILTER_PROCESS_ID); } @Override protected ProcessResolutionProblemItem _createItem() { return new ProcessResolutionProblemItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/bpm/process/ProcessResolutionProblemItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.bpm.process; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class ProcessResolutionProblemItem extends Item { public ProcessResolutionProblemItem() { super(); } public ProcessResolutionProblemItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String ATTRIBUTE_MESSAGE = "message"; public static final String ATTRIBUTE_TARGET_TYPE = "target_type"; public static final String ATTRIBUTE_RESSOURCE_ID = "ressource_id"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES VALUES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String VALUE_STATE_TARGET_TYPE_CONNECTOR = "connector"; public static final String VALUE_STATE_TARGET_TYPE_ACTOR = "actor"; public static final String VALUE_STATE_TARGET_TYPE_PARAMETER = "parameter"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String FILTER_PROCESS_ID = "process_id"; public String getMessage() { return getAttributeValue(ATTRIBUTE_MESSAGE); } public void setMessage(final String text) { setAttribute(ATTRIBUTE_MESSAGE, text); } public String getTargetType() { return getAttributeValue(ATTRIBUTE_TARGET_TYPE); } public void setTargetType(final String type) { setAttribute(ATTRIBUTE_TARGET_TYPE, type); } public String getRessourceId() { return getAttributeValue(ATTRIBUTE_RESSOURCE_ID); } public void setRessourceId(final String ressourceId) { setAttribute(ATTRIBUTE_RESSOURCE_ID, ressourceId); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return Definitions.get(ProcessResolutionProblemDefinition.TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/bpm/cases/CaseItemBuilder.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.builder.bpm.cases; import org.bonitasoft.web.rest.model.bpm.cases.CaseItem; public class CaseItemBuilder { private long processId; private String jsonVariables; private long userId; public static CaseItemBuilder aCaseItem() { return new CaseItemBuilder(); } public CaseItemBuilder withProcessId(final long processId) { this.processId = processId; return this; } public CaseItemBuilder withUserId(final long userId) { this.userId = userId; return this; } public CaseItemBuilder withVariables(final String jsonVariables) { this.jsonVariables = jsonVariables; return this; } public CaseItem build() { final CaseItem item = new CaseItem(); setAttributeIfNotNull(item, CaseItem.ATTRIBUTE_PROCESS_ID, processId); setAttributeIfNotNull(item, CaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, userId); setAttributeIfNotNull(item, CaseItem.ATTRIBUTE_VARIABLES, jsonVariables); return item; } private void setAttributeIfNotNull(final CaseItem caseItem, final String attributeName, final Object attributeValue) { if (attributeValue != null) { caseItem.setAttribute(attributeName, attributeValue); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/bpm/process/ActorItemBuilder.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.builder.bpm.process; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.web.rest.model.bpm.process.ActorItem; /** * @author Colin PUY */ public class ActorItemBuilder { private Long id; private Long processId; private String name; private String displayName; private String description; private ActorItemBuilder() { } public static ActorItemBuilder anActorItem() { return new ActorItemBuilder(); } public ActorItem build() { ActorItem actorItem = new ActorItem(); actorItem.setId(id); actorItem.setProcessId(processId); actorItem.setName(name); actorItem.setDisplayName(displayName); actorItem.setDescription(description); return actorItem; } public ActorItemBuilder fromActorInstance(ActorInstance engineItem) { id = engineItem.getId(); processId = engineItem.getProcessDefinitionId(); name = engineItem.getName(); displayName = engineItem.getDisplayName(); description = engineItem.getDescription(); return this; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/bpm/process/ActorMemberItemBuilder.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.builder.bpm.process; import org.bonitasoft.engine.bpm.actor.ActorMember; import org.bonitasoft.web.rest.model.bpm.process.ActorMemberItem; /** * @author Colin PUY */ public class ActorMemberItemBuilder { private Long id; private Long actorId; private Long userId; private Long roleId; private Long groupId; private ActorMemberItemBuilder() { } public static ActorMemberItemBuilder anActorMemberItem() { return new ActorMemberItemBuilder(); } public ActorMemberItem build() { ActorMemberItem item = new ActorMemberItem(); item.setId(id); item.setActorId(actorId); item.setUserId(userId); item.setRoleId(roleId); item.setGroupId(groupId); return item; } public ActorMemberItemBuilder fromActorMember(ActorMember actorMember, long actorId) { id = actorMember.getId(); this.actorId = actorId; userId = actorMember.getUserId(); roleId = actorMember.getRoleId(); groupId = actorMember.getGroupId(); return this; } public ActorMemberItemBuilder withActorId(long actorId) { this.actorId = actorId; return this; } public ActorMemberItemBuilder withuserId(long userId) { this.userId = userId; return this; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/bpm/process/CategoryItemBuilder.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.builder.bpm.process; import org.bonitasoft.web.rest.model.bpm.process.CategoryItem; /** * @author Colin PUY */ public class CategoryItemBuilder { private final String description = "aDescription"; private final String name = "aCategory"; public static CategoryItemBuilder aCategoryItem() { return new CategoryItemBuilder(); } public CategoryItem build() { CategoryItem category = new CategoryItem(); category.setName(name); category.setDescription(description); return category; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/bpm/process/ProcessCategoryItemBuilder.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.builder.bpm.process; import org.bonitasoft.web.rest.model.bpm.process.ProcessCategoryItem; /** * @author Colin PUY */ public class ProcessCategoryItemBuilder { private final long processId = 1L; private final long categoryId = 1L; public static ProcessCategoryItemBuilder aProcessCategory() { return new ProcessCategoryItemBuilder(); } public ProcessCategoryItem build() { ProcessCategoryItem processCategory = new ProcessCategoryItem(); processCategory.setCategoryId(processId); processCategory.setProcessId(categoryId); return processCategory; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/identity/ContactDataBuilder.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.builder.identity; import org.bonitasoft.engine.identity.ContactDataCreator; import org.bonitasoft.web.rest.model.identity.ProfessionalContactDataItem; /** * @author Colin PUY */ public class ContactDataBuilder { private String address = "anAddress"; private final String building = "aBuilding"; private final String city = "aCity"; private final String country = "aCountry"; private final String email = "anEmail"; private final String faxNumber = "aFaxNumber"; private final String mobileNumber = "aMobileNumber"; private final String phoneNumber = "aPhoneNumber"; private final String room = "aRoom"; private final String state = "aState"; private final String website = "aWebsite"; private final String zipCode = "aZipCode"; public static ContactDataBuilder aContactData() { return new ContactDataBuilder(); } public ContactDataBuilder withAddress(String address) { this.address = address; return this; } public ContactDataCreator toContactDataCreator() { return new ContactDataCreator() .setAddress(address) .setBuilding(building) .setCity(city) .setCountry(country) .setEmail(email) .setFaxNumber(faxNumber) .setMobileNumber(mobileNumber) .setPhoneNumber(phoneNumber) .setRoom(room) .setState(state) .setWebsite(website) .setZipCode(zipCode); } public ProfessionalContactDataItem toProfessionalContactDataItem() { ProfessionalContactDataItem item = new ProfessionalContactDataItem(); item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_EMAIL, email); item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_PHONE, phoneNumber); item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_MOBILE, mobileNumber); item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_FAX, faxNumber); item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_BUILDING, building); item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_ROOM, room); item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_ADDRESS, address); item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_ZIPCODE, zipCode); item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_CITY, city); item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_STATE, state); item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_COUNTRY, country); item.setAttribute(ProfessionalContactDataItem.ATTRIBUTE_WEBSITE, website); return item; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/organisation/GroupItemBuilder.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.builder.organisation; import org.bonitasoft.web.rest.model.identity.GroupItem; /** * @author Colin PUY */ public class GroupItemBuilder { private final String name = "Group1"; private final String displayName = "Group"; private final String description = "Text for describe the group"; private final String parentPath = ""; private final String parentGroupId = ""; public static GroupItemBuilder aGroup() { return new GroupItemBuilder(); } public GroupItem build() { GroupItem group = new GroupItem(); group.setName(name); group.setDisplayName(displayName); group.setDescription(description); group.setParentPath(parentPath); group.setParentGroupId(parentGroupId); return group; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/profile/member/AbstractProfileMemberBuilder.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.builder.profile.member; /** * @author Vincent Elcrin */ public abstract class AbstractProfileMemberBuilder { protected Long id = 1L; protected Long profileId = 2L; protected Long userId = 3L; protected Long groupId = 4L; protected Long roleId = 5L; public AbstractProfileMemberBuilder withId(Long id) { this.id = id; return this; } public AbstractProfileMemberBuilder withProfileId(Long id) { this.profileId = id; return this; } public AbstractProfileMemberBuilder withUserId(Long id) { this.userId = id; return this; } public AbstractProfileMemberBuilder withGroupId(Long id) { this.groupId = id; return this; } public AbstractProfileMemberBuilder withRoleId(Long id) { this.roleId = id; return this; } public abstract O build(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/profile/member/EngineProfileMemberBuilder.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.builder.profile.member; import org.bonitasoft.engine.profile.ProfileMember; import org.bonitasoft.engine.profile.impl.ProfileMemberImpl; /** * @author Vincent Elcrin */ public class EngineProfileMemberBuilder extends AbstractProfileMemberBuilder { public static EngineProfileMemberBuilder anEngineProfileMember() { return new EngineProfileMemberBuilder(); } public ProfileMember build() { ProfileMemberImpl profileMember = new ProfileMemberImpl(); profileMember.setProfileId(profileId); profileMember.setUserId(userId); profileMember.setGroupId(groupId); profileMember.setRoleId(roleId); return profileMember; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/builder/profile/member/ProfileMemberItemBuilder.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.builder.profile.member; import org.bonitasoft.engine.profile.ProfileMember; import org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem; /** * @author Vincent Elcrin */ public class ProfileMemberItemBuilder extends AbstractProfileMemberBuilder { public static ProfileMemberItemBuilder aProfileMemberItem() { return new ProfileMemberItemBuilder(); } public ProfileMemberItem build() { ProfileMemberItem item = new ProfileMemberItem(); item.setId(id); item.setProfileId(profileId); item.setUserId(userId); item.setGroupId(groupId); item.setRoleId(roleId); return item; } public ProfileMemberItemBuilder from(ProfileMember profileMember) { id = profileMember.getId(); profileId = profileMember.getProfileId(); userId = profileMember.getUserId(); groupId = profileMember.getGroupId(); roleId = profileMember.getRoleId(); return this; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/document/ArchivedDocumentDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.document; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Gai Cuisha */ public class ArchivedDocumentDefinition extends ItemDefinition { /** * Singleton */ public static ArchivedDocumentDefinition get() { return (ArchivedDocumentDefinition) Definitions.get(TOKEN); } /** * token */ public static final String TOKEN = "archivedDocument"; /** * the URL of document */ private static final String API_URL = "../API/bpm/archiveddocument"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(DocumentItem.DOCUMENT_ID, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_VERSION, ItemAttribute.TYPE.STRING); createAttribute(ArchivedDocumentItem.SOURCEOBJECT_ID, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.PROCESSINSTANCE_NAME, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.PROCESS_DISPLAY_NAME, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.PROCESSINSTANCE_ID, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.PROCESSINSTANCE_NAME, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_NAME, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_AUTHOR, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_CREATIONDATE, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_HAS_CONTENT, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_CONTENT_FILENAME, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_CONTENT_MIMETYPE, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.CONTENT_STORAGE_ID, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_URL, ItemAttribute.TYPE.STRING); createAttribute(ArchivedDocumentItem.ARCHIVED_DATE, ItemAttribute.TYPE.STRING); } @Override protected void definePrimaryKeys() { setPrimaryKeys(DocumentItem.DOCUMENT_ID); } @Override protected IItem _createItem() { return new ArchivedDocumentItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/document/ArchivedDocumentItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.document; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * Document item * * @author Gai Cuisha */ public class ArchivedDocumentItem extends DocumentItem { public static final String SOURCEOBJECT_ID = "sourceObjectId"; public static final String ARCHIVED_DATE = "archivedDate"; public ArchivedDocumentItem() { super(); } public ArchivedDocumentItem(final IItem item) { super(item); } public void setDocumentSourceObjectId(final String sourceObjectId) { this.setAttribute(SOURCEOBJECT_ID, sourceObjectId); } public void setArchivedDate(final String archivedDate) { this.setAttribute(ARCHIVED_DATE, archivedDate); } @Override public ItemDefinition getItemDefinition() { return new ArchivedDocumentDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/document/DocumentDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.document; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Gai Cuisha */ public class DocumentDefinition extends ItemDefinition { /** * Singleton */ public static DocumentDefinition get() { return (DocumentDefinition) Definitions.get(TOKEN); } /** * token */ public static final String TOKEN = "document"; /** * the URL of document */ private static final String API_URL = "../API/bpm/document"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(DocumentItem.DOCUMENT_ID, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_VERSION, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.PROCESSINSTANCE_ID, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.PROCESSINSTANCE_NAME, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.PROCESS_DISPLAY_NAME, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.PROCESS_VERSION, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_NAME, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_AUTHOR, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_CREATIONDATE, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_HAS_CONTENT, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_CONTENT_FILENAME, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_CONTENT_MIMETYPE, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.CONTENT_STORAGE_ID, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_URL, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_CREATION_TYPE, ItemAttribute.TYPE.STRING); createAttribute(DocumentItem.DOCUMENT_UPLOAD, ItemAttribute.TYPE.STRING); } @Override protected void definePrimaryKeys() { setPrimaryKeys(DocumentItem.DOCUMENT_ID); } @Override protected IItem _createItem() { return new DocumentItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/document/DocumentItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.document; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * Document item * * @author Gai Cuisha */ public class DocumentItem extends Item { public DocumentItem() { super(); } public DocumentItem(final IItem item) { super(item); } public static final String DOCUMENT_ID = "id"; public static final String DOCUMENT_VERSION = "version"; public static final String PROCESSINSTANCE_ID = "processinstanceId"; public static final String PROCESSINSTANCE_NAME = "processinstanceName"; public static final String PROCESS_DISPLAY_NAME = "processDisplayName"; public static final String PROCESS_VERSION = "processinstanceVersion"; public static final String DOCUMENT_NAME = "documentName"; public static final String DOCUMENT_AUTHOR = "documentAuthor"; public static final String DOCUMENT_CREATIONDATE = "documentCreationDate"; public static final String DOCUMENT_HAS_CONTENT = "documentHasContent"; public static final String DOCUMENT_CONTENT_FILENAME = "documentContentFileName"; public static final String DOCUMENT_CONTENT_MIMETYPE = "documentContentMimeType"; public static final String CONTENT_STORAGE_ID = "contentStorageId"; public static final String DOCUMENT_URL = "documentURL"; public static final String DOCUMENT_CREATION_TYPE = "DOCUMENT_CREATION_TYPE"; public static final String DOCUMENT_UPLOAD = "documentUpload"; /* * Filter are in uppercase due to a refactoring (delete FilterKey class), please put them in lowercase if there is * no side effect */ public static final String FILTER_CASE_ID = "CASE_ID"; public static final String FILTER_VIEW_TYPE = "VIEW"; public static final String FILTER_USER_ID = "USER_ID"; /* idem for values */ public static final String VALUE_VIEW_TYPE_ADMINISTRATOR = "ADMINISTRATOR"; public static final String VALUE_VIEW_TYPE_USER = "USER"; public static final String VALUE_VIEW_TYPE_TEAM_MANAGER = "TEAM_MANAGER"; public static final String VALUE_VIEW_TYPE_PROCESS_OWNER = "PROCESS_OWNER"; public void setDocumentId(final String documentId) { this.setAttribute(DOCUMENT_ID, documentId); } public void setDocumentVersion(final String documentVersion) { this.setAttribute(DOCUMENT_VERSION, documentVersion); } public void setCaseId(final String caseId) { this.setAttribute(PROCESSINSTANCE_ID, caseId); } public void setCaseName(final String caseName) { this.setAttribute(PROCESSINSTANCE_NAME, caseName); } public void setProcessDisplayName(final String caseName) { this.setAttribute(PROCESS_DISPLAY_NAME, caseName); } public void setProcessVersion(final String caseVersion) { this.setAttribute(PROCESS_VERSION, caseVersion); } public void setDocumentName(final String documentName) { this.setAttribute(DOCUMENT_NAME, documentName); } public void setDocumentAuthor(final APIID userId) { this.setAttribute(DOCUMENT_AUTHOR, userId.toString()); } public void setDocumentAuthor(final Long userId) { this.setAttribute(DOCUMENT_AUTHOR, userId.toString()); } public void setDocumentCreationDate(final String documentCreationDate) { this.setAttribute(DOCUMENT_CREATIONDATE, documentCreationDate); } public void setDocumentHasContent(final String documentHasContent) { this.setAttribute(DOCUMENT_HAS_CONTENT, documentHasContent); } public void setDocumentFileName(final String documentFileName) { this.setAttribute(DOCUMENT_CONTENT_FILENAME, documentFileName); } public void setDocumentMIMEType(final String documentMIMEType) { this.setAttribute(DOCUMENT_CONTENT_MIMETYPE, documentMIMEType); } public void setDocumentStorageId(final String documentStorageId) { this.setAttribute(CONTENT_STORAGE_ID, documentStorageId); } public void setDocumentURL(final String documentURL) { this.setAttribute(DOCUMENT_URL, documentURL); } public void setDocumentCreationType(final String documentCreationType) { this.setAttribute(DOCUMENT_CREATION_TYPE, documentCreationType); } public void setDocumentUpload(final String documentUpload) { this.setAttribute(DOCUMENT_UPLOAD, documentUpload); } @Override public ItemDefinition getItemDefinition() { return new DocumentDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/AbstractContactDataDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringRegexpValidator; /** * @author Paul AMAR */ public abstract class AbstractContactDataDefinition extends ItemDefinition { @Override protected void defineAttributes() { createAttribute(AbstractContactDataItem.ATTRIBUTE_EMAIL, ItemAttribute.TYPE.EMAIL); createAttribute(AbstractContactDataItem.ATTRIBUTE_PHONE, ItemAttribute.TYPE.STRING) .addValidator(new StringRegexpValidator("[\\d\\.\\(\\)#\\+]*")); createAttribute(AbstractContactDataItem.ATTRIBUTE_MOBILE, ItemAttribute.TYPE.STRING) .addValidator(new StringRegexpValidator("[\\d\\.\\(\\)#\\+]*")); createAttribute(AbstractContactDataItem.ATTRIBUTE_FAX, ItemAttribute.TYPE.STRING) .addValidator(new StringRegexpValidator("[\\d\\.\\(\\)#\\+]*")); createAttribute(AbstractContactDataItem.ATTRIBUTE_BUILDING, ItemAttribute.TYPE.STRING); createAttribute(AbstractContactDataItem.ATTRIBUTE_ROOM, ItemAttribute.TYPE.STRING); createAttribute(AbstractContactDataItem.ATTRIBUTE_ADDRESS, ItemAttribute.TYPE.STRING); createAttribute(AbstractContactDataItem.ATTRIBUTE_ZIPCODE, ItemAttribute.TYPE.STRING); createAttribute(AbstractContactDataItem.ATTRIBUTE_CITY, ItemAttribute.TYPE.STRING); createAttribute(AbstractContactDataItem.ATTRIBUTE_STATE, ItemAttribute.TYPE.STRING); createAttribute(AbstractContactDataItem.ATTRIBUTE_COUNTRY, ItemAttribute.TYPE.STRING); createAttribute(AbstractContactDataItem.ATTRIBUTE_WEBSITE, ItemAttribute.TYPE.URL); } @Override protected abstract IItem _createItem(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/AbstractContactDataItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Paul AMAR */ public abstract class AbstractContactDataItem extends Item implements ItemHasUniqueId { public static final String ATTRIBUTE_EMAIL = "email"; public static final String ATTRIBUTE_PHONE = "phone_number"; public static final String ATTRIBUTE_MOBILE = "mobile_number"; public static final String ATTRIBUTE_FAX = "fax_number"; public static final String ATTRIBUTE_BUILDING = "building"; public static final String ATTRIBUTE_ROOM = "room"; public static final String ATTRIBUTE_ADDRESS = "address"; public static final String ATTRIBUTE_ZIPCODE = "zipcode"; public static final String ATTRIBUTE_CITY = "city"; public static final String ATTRIBUTE_STATE = "state"; public static final String ATTRIBUTE_COUNTRY = "country"; public static final String ATTRIBUTE_WEBSITE = "website"; public AbstractContactDataItem() { super(); } public AbstractContactDataItem(final IItem item) { super(item); } // /////////////////////////////////////////////////////// // // GETTERS // ////////////////////////////////////////////////////// public String getEmail() { return this.getAttributeValue(ATTRIBUTE_EMAIL); } public String getPhoneNumber() { return this.getAttributeValue(ATTRIBUTE_PHONE); } public String getMobileNumber() { return this.getAttributeValue(ATTRIBUTE_MOBILE); } public String getFaxNumber() { return this.getAttributeValue(ATTRIBUTE_FAX); } public String getBuilding() { return this.getAttributeValue(ATTRIBUTE_BUILDING); } public String getRoom() { return this.getAttributeValue(ATTRIBUTE_ROOM); } public String getAddress() { return this.getAttributeValue(ATTRIBUTE_ADDRESS); } public String getZipCode() { return this.getAttributeValue(ATTRIBUTE_ZIPCODE); } public String getCity() { return this.getAttributeValue(ATTRIBUTE_CITY); } public String getState() { return this.getAttributeValue(ATTRIBUTE_STATE); } public String getCountry() { return this.getAttributeValue(ATTRIBUTE_COUNTRY); } public String getWebsite() { return this.getAttributeValue(ATTRIBUTE_WEBSITE); } // /////////////////////////////////////////////////////// // // SETTERS // ////////////////////////////////////////////////////// @Override public void setId(final String id) { setAttribute(ItemHasUniqueId.ATTRIBUTE_ID, id); } @Override public void setId(final Long id) { setAttribute(ItemHasUniqueId.ATTRIBUTE_ID, id); } public void setEmail(final String Email) { this.setAttribute(ATTRIBUTE_EMAIL, Email); } public void setPhoneNumber(final String PhoneNumber) { this.setAttribute(ATTRIBUTE_PHONE, PhoneNumber); } public void setMobileNumber(final String MobileNumber) { this.setAttribute(ATTRIBUTE_MOBILE, MobileNumber); } public void setFaxNumber(final String FaxNumber) { this.setAttribute(ATTRIBUTE_FAX, FaxNumber); } public void setBuilding(final String Building) { this.setAttribute(ATTRIBUTE_BUILDING, Building); } public void setRoom(final String Room) { this.setAttribute(ATTRIBUTE_ROOM, Room); } public void setAddress(final String Address) { this.setAttribute(ATTRIBUTE_ADDRESS, Address); } public void setZipCode(final String ZipCode) { this.setAttribute(ATTRIBUTE_ZIPCODE, ZipCode); } public void setCity(final String City) { this.setAttribute(ATTRIBUTE_CITY, City); } public void setState(final String State) { this.setAttribute(ATTRIBUTE_STATE, State); } public void setCountry(final String Country) { this.setAttribute(ATTRIBUTE_COUNTRY, Country); } public void setWebsite(final String Website) { this.setAttribute(ATTRIBUTE_WEBSITE, Website); } @Override public abstract ItemDefinition getItemDefinition(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/CustomUserInfoDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Vincent Elcrin */ public class CustomUserInfoDefinition extends ItemDefinition { public static final String TOKEN = "customuserinfo"; @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return "../API/customuserinfo/user"; } @Override protected void defineAttributes() { createAttribute(CustomUserInfoItem.ATTRIBUTE_DEFINITION_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(CustomUserInfoItem.ATTRIBUTE_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(CustomUserInfoItem.ATTRIBUTE_VALUE, ItemAttribute.TYPE.STRING); } @Override protected void defineDeploys() { declareDeployable(CustomUserInfoItem.ATTRIBUTE_DEFINITION_ID, CustomUserInfoDefinitionDefinition.get()); } @Override protected void definePrimaryKeys() { setPrimaryKeys(CustomUserInfoItem.ATTRIBUTE_USER_ID, CustomUserInfoItem.ATTRIBUTE_DEFINITION_ID); } @Override protected CustomUserInfoItem _createItem() { return new CustomUserInfoItem(); } public static CustomUserInfoDefinition get() { return (CustomUserInfoDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/CustomUserInfoDefinitionDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Vincent Elcrin */ public class CustomUserInfoDefinitionDefinition extends ItemDefinition { public static final String TOKEN = "customuserinfo/definitions"; @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return "../API/customuserinfo/definition"; } @Override protected void defineAttributes() { createAttribute(CustomUserInfoDefinitionItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(CustomUserInfoDefinitionItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING); createAttribute(CustomUserInfoDefinitionItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT); } @Override protected void definePrimaryKeys() { setPrimaryKeys(CustomUserInfoDefinitionItem.ATTRIBUTE_ID); } @Override protected CustomUserInfoDefinitionItem _createItem() { return new CustomUserInfoDefinitionItem(); } public static CustomUserInfoDefinitionDefinition get() { return (CustomUserInfoDefinitionDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/CustomUserInfoDefinitionItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Vincent Elcrin */ public class CustomUserInfoDefinitionItem extends Item implements ItemHasUniqueId { public static final String ATTRIBUTE_NAME = "name"; public static final String ATTRIBUTE_DESCRIPTION = "description"; @Override public CustomUserInfoDefinitionDefinition getItemDefinition() { return new CustomUserInfoDefinitionDefinition(); } @Override public void setId(String id) { setId(APIID.makeAPIID(id)); } @Override public void setId(Long id) { setId(APIID.makeAPIID(id)); } public String getName() { return getAttributeValue(ATTRIBUTE_NAME); } public void setName(String name) { setAttribute(ATTRIBUTE_NAME, name); } public String getDescription() { return getAttributeValue(ATTRIBUTE_DESCRIPTION); } public void setDescription(String description) { setAttribute(ATTRIBUTE_DESCRIPTION, description); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/CustomUserInfoItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Item; /** * @author Vincent Elcrin */ public class CustomUserInfoItem extends Item { public static final String FILTER_USER_ID = "userId"; public static final String ATTRIBUTE_DEFINITION_ID = "definitionId"; public static final String ATTRIBUTE_USER_ID = "userId"; public static final String ATTRIBUTE_VALUE = "value"; @Override public CustomUserInfoDefinition getItemDefinition() { return new CustomUserInfoDefinition(); } public void setUserId(long userId) { setAttribute(ATTRIBUTE_USER_ID, userId); } public void setDefinition(APIID id) { setAttribute(ATTRIBUTE_DEFINITION_ID, id); } public void setDefinition(CustomUserInfoDefinitionItem definition) { setDefinition(definition.getId()); setDeploy(ATTRIBUTE_DEFINITION_ID, definition); } public void setValue(String value) { setAttribute(ATTRIBUTE_VALUE, value); } public long getUserId() { return getAttributeValueAsLong(ATTRIBUTE_USER_ID); } public CustomUserInfoDefinitionItem getDefinition() { return (CustomUserInfoDefinitionItem) getDeploy(ATTRIBUTE_DEFINITION_ID); } public String getValue() { return getAttributeValue(ATTRIBUTE_VALUE); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/CustomUserInfoValueDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import org.bonitasoft.web.toolkit.client.data.item.Definitions; /** * @author Vincent Elcrin */ public class CustomUserInfoValueDefinition extends CustomUserInfoDefinition { public static final String TOKEN = "customuserinfo/value"; @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return "../API/customuserinfo/value"; } public static CustomUserInfoValueDefinition get() { return (CustomUserInfoValueDefinition) Definitions.get(TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/GroupDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import org.bonitasoft.web.rest.server.datastore.organization.Avatars; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.FileIsImageOrServletPathValidator; /** * @author Yongtao Guo */ public class GroupDefinition extends ItemDefinition { /** * Singleton */ public static GroupDefinition get() { return (GroupDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "group"; /** * the URL of user resource */ private static final String API_URL = "../API/identity/group"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { setPrimaryKeys(GroupItem.ATTRIBUTE_ID); } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(GroupItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(GroupItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING); createAttribute(GroupItem.ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.STRING); createAttribute(GroupItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT); createAttribute(GroupItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(GroupItem.ATTRIBUTE_CREATED_BY_USER_ID, ItemAttribute.TYPE.STRING); createAttribute(GroupItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(GroupItem.ATTRIBUTE_ICON, ItemAttribute.TYPE.STRING) .addValidator(new FileIsImageOrServletPathValidator(Avatars.PATH)); createAttribute(GroupItem.ATTRIBUTE_PARENT_PATH, ItemAttribute.TYPE.STRING); createAttribute(GroupItem.ATTRIBUTE_PARENT_GROUP_ID, ItemAttribute.TYPE.STRING); } @Override public GroupItem _createItem() { return new GroupItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/GroupItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import java.util.Date; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasCreator; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Yongtao Guo */ public class GroupItem extends Item implements ItemHasDualName, ItemHasUniqueId, ItemHasCreator, ItemHasLastUpdateDate, ItemHasIcon { public static final String ATTRIBUTE_DESCRIPTION = "description"; public static final String ATTRIBUTE_PATH = "path"; public static final String ATTRIBUTE_PARENT_PATH = "parent_path"; public static final String ATTRIBUTE_PARENT_GROUP_ID = "parent_group_id"; public GroupItem() { super(); } public GroupItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // COUNTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String COUNTER_NUMBER_OF_USERS = "number_of_users"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS AND SETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS @Override public ItemDefinition getItemDefinition() { return new GroupDefinition(); } @Override public String getName() { return this.getAttributeValue(ATTRIBUTE_NAME); } @Override public String getDisplayName() { if (!StringUtil.isBlank(this.getAttributeValue(ATTRIBUTE_DISPLAY_NAME))) { return this.getAttributeValue(ATTRIBUTE_DISPLAY_NAME); } else { return this.getAttributeValue(ATTRIBUTE_NAME); } } public String getDescription() { return this.getAttributeValue(ATTRIBUTE_DESCRIPTION); } @Override public APIID getCreatedByUserId() { return this.getAttributeValueAsAPIID(ATTRIBUTE_CREATED_BY_USER_ID); } @Override public UserItem getCreatedByUser() { return new UserItem(getDeploy(ATTRIBUTE_CREATED_BY_USER_ID)); } @Override public Date getCreationDate() { return this.getAttributeValueAsDate(ATTRIBUTE_CREATION_DATE); } @Override public Date getLastUpdateDate() { return this.getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE); } @Override public String getIcon() { return this.getAttributeValue(ATTRIBUTE_ICON); } public String getParentPath() { return getAttributeValue(ATTRIBUTE_PARENT_PATH); } public String getPath() { return getAttributeValue(ATTRIBUTE_PATH); } public String getParentGroupId() { return getAttributeValue(ATTRIBUTE_PARENT_GROUP_ID); } // SETTERS @Override public void setId(final String id) { this.setAttribute(ATTRIBUTE_ID, id); } @Override public void setId(final Long id) { this.setAttribute(ATTRIBUTE_ID, id.toString());// TODO delete the tostring call when severin will commit } @Override public void setName(final String name) { this.setAttribute(ATTRIBUTE_NAME, name); } @Override public void setCreatedByUserId(final APIID id) { this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id); } @Override public void setCreatedByUserId(final String id) { this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id); } @Override public void setCreatedByUserId(final Long id) { this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id.toString()); } @Override public void setCreationDate(final String date) { this.setAttribute(ATTRIBUTE_CREATION_DATE, date); } @Override public void setCreationDate(final Date date) { this.setAttribute(ATTRIBUTE_CREATION_DATE, date); } @Override public void setDisplayName(final String displayName) { this.setAttribute(ATTRIBUTE_DISPLAY_NAME, displayName); } public void setDescription(final String description) { this.setAttribute(ATTRIBUTE_DESCRIPTION, description); } @Override public void setLastUpdateDate(final String date) { this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } @Override public void setLastUpdateDate(final Date date) { this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } @Override public void setIcon(final String icon) { this.setAttribute(ATTRIBUTE_ICON, icon); } public void setParentPath(final String parentPath) { this.setAttribute(ATTRIBUTE_PARENT_PATH, parentPath); } public void setPath(final String parentPath) { this.setAttribute(ATTRIBUTE_PATH, parentPath); } public void setParentGroupId(final String parentGroupId) { this.setAttribute(ATTRIBUTE_PARENT_GROUP_ID, parentGroupId); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/MemberType.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; /** * @author Yongtao Guo */ public enum MemberType { USER, GROUP, ROLE, MEMBERSHIP; @Override public String toString() { return super.toString().toLowerCase(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/MembershipDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * User definition * * @author Séverin Moussel */ public class MembershipDefinition extends ItemDefinition { /** * Singleton */ public static MembershipDefinition get() { return (MembershipDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "membership"; /** * the URL of user resource */ private static final String API_URL = "../API/identity/membership"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { setPrimaryKeys(MembershipItem.ATTRIBUTE_USER_ID, MembershipItem.ATTRIBUTE_GROUP_ID, MembershipItem.ATTRIBUTE_ROLE_ID); } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(MembershipItem.ATTRIBUTE_USER_ID, ItemAttribute.TYPE.ITEM_ID) .isMandatory(); createAttribute(MembershipItem.ATTRIBUTE_GROUP_ID, ItemAttribute.TYPE.ITEM_ID) .isMandatory(); createAttribute(MembershipItem.ATTRIBUTE_ROLE_ID, ItemAttribute.TYPE.ITEM_ID) .isMandatory(); createAttribute(MembershipItem.ATTRIBUTE_ASSIGNED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(MembershipItem.ATTRIBUTE_ASSIGNED_DATE, ItemAttribute.TYPE.DATETIME); } @Override public IItem _createItem() { return new MembershipItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/MembershipItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import java.util.Date; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class MembershipItem extends Item { public MembershipItem() { super(); } public MembershipItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String ATTRIBUTE_USER_ID = "user_id"; public static final String ATTRIBUTE_GROUP_ID = "group_id"; public static final String ATTRIBUTE_ROLE_ID = "role_id"; public static final String ATTRIBUTE_ASSIGNED_BY_USER_ID = "assigned_by_user_id"; public static final String ATTRIBUTE_ASSIGNED_DATE = "assigned_date"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES VALUES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // COUNTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEPLOYS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public UserItem getUser() { return new UserItem(getDeploy(ATTRIBUTE_USER_ID)); } public RoleItem getRole() { return new RoleItem(getDeploy(ATTRIBUTE_ROLE_ID)); } public GroupItem getGroup() { return new GroupItem(getDeploy(ATTRIBUTE_GROUP_ID)); } public UserItem getAssignedByUser() { return new UserItem(getDeploy(ATTRIBUTE_ASSIGNED_BY_USER_ID)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS AND SETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS public APIID getUserId() { return getAttributeValueAsAPIID(ATTRIBUTE_USER_ID); } public APIID getGroupId() { return getAttributeValueAsAPIID(ATTRIBUTE_GROUP_ID); } public APIID getRoleId() { return getAttributeValueAsAPIID(ATTRIBUTE_ROLE_ID); } public APIID getAssignedByUserId() { return getAttributeValueAsAPIID(ATTRIBUTE_ASSIGNED_BY_USER_ID); } public Date getAssignedDate() { return getAttributeValueAsDate(ATTRIBUTE_ASSIGNED_DATE); } // SETTERS public void setUserId(final String id) { setAttribute(ATTRIBUTE_USER_ID, id); } public void setUserId(final Long id) { setAttribute(ATTRIBUTE_USER_ID, id); } public void setUserId(final APIID id) { setAttribute(ATTRIBUTE_USER_ID, id); } public void setRoleId(final String id) { setAttribute(ATTRIBUTE_ROLE_ID, id); } public void setRoleId(final Long id) { setAttribute(ATTRIBUTE_ROLE_ID, id); } public void setRoleId(final APIID id) { setAttribute(ATTRIBUTE_ROLE_ID, id); } public void setGroupId(final String id) { setAttribute(ATTRIBUTE_GROUP_ID, id); } public void setGroupId(final Long id) { setAttribute(ATTRIBUTE_GROUP_ID, id); } public void setGroupId(final APIID id) { setAttribute(ATTRIBUTE_GROUP_ID, id); } public void setAssignedByUserId(final String id) { setAttribute(ATTRIBUTE_ASSIGNED_BY_USER_ID, id); } public void setAssignedByUserId(final Long id) { setAttribute(ATTRIBUTE_ASSIGNED_BY_USER_ID, id); } public void setAssignedByUserId(final APIID id) { setAttribute(ATTRIBUTE_ASSIGNED_BY_USER_ID, id); } public void setAssignedDate(final Date date) { setAttribute(ATTRIBUTE_ASSIGNED_DATE, date); } public void setAssignedDate(final String date) { setAttribute(ATTRIBUTE_ASSIGNED_DATE, date); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return new MembershipDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/PersonalContactDataDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Paul AMAR */ public class PersonalContactDataDefinition extends AbstractContactDataDefinition { public static final String TOKEN = "personalcontactdata"; /** * the URL of user resource */ private static final String API_URL = "../API/identity/personalcontactdata"; @Override protected void definePrimaryKeys() { setPrimaryKeys(PersonalContactDataItem.ATTRIBUTE_ID); } @Override protected IItem _createItem() { return new PersonalContactDataItem(); } @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/PersonalContactDataItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Paul AMAR */ public class PersonalContactDataItem extends AbstractContactDataItem { /** * Default Constructor. */ public PersonalContactDataItem() { super(); } /** * Default Constructor. * * @param item */ public PersonalContactDataItem(final IItem item) { super(item); } @Override public ItemDefinition getItemDefinition() { return new PersonalContactDataDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/ProfessionalContactDataDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Paul AMAR */ public class ProfessionalContactDataDefinition extends AbstractContactDataDefinition { public static final String TOKEN = "professionalcontactdata"; /** * the URL of user resource */ private static final String API_URL = "../API/identity/professionalcontactdata"; @Override protected IItem _createItem() { return new ProfessionalContactDataItem(); } @Override protected void definePrimaryKeys() { setPrimaryKeys(ItemHasUniqueId.ATTRIBUTE_ID); } @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/ProfessionalContactDataItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Paul AMAR */ public class ProfessionalContactDataItem extends AbstractContactDataItem { /** * Default Constructor. */ public ProfessionalContactDataItem() { super(); } /** * Default Constructor. * * @param item */ public ProfessionalContactDataItem(final IItem item) { super(item); } @Override public ItemDefinition getItemDefinition() { return new ProfessionalContactDataDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/RoleDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import org.bonitasoft.web.rest.server.datastore.organization.Avatars; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.FileIsImageOrServletPathValidator; /** * @author Yongtao Guo */ public class RoleDefinition extends ItemDefinition { /** * Singleton */ public static RoleDefinition get() { return (RoleDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "role"; /** * the URL of user resource */ private static final String API_URL = "../API/identity/role"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { setPrimaryKeys(RoleItem.ATTRIBUTE_ID); } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(RoleItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(RoleItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING); createAttribute(GroupItem.ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.STRING); createAttribute(RoleItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT); createAttribute(RoleItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(RoleItem.ATTRIBUTE_CREATED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(RoleItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(RoleItem.ATTRIBUTE_ICON, ItemAttribute.TYPE.STRING) .addValidator(new FileIsImageOrServletPathValidator(Avatars.PATH)); } @Override public RoleItem _createItem() { return new RoleItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/RoleItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import java.util.Date; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasCreator; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Séverin Moussel */ public class RoleItem extends Item implements ItemHasDualName, ItemHasUniqueId, ItemHasCreator, ItemHasLastUpdateDate, ItemHasIcon { public RoleItem() { super(); } public RoleItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String ATTRIBUTE_DESCRIPTION = "description"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES VALUES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // COUNTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String COUNTER_NUMBER_OF_USERS = "number_of_users"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEPLOYS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public UserItem getCreatedByUser() { return new UserItem(getDeploy(ATTRIBUTE_CREATED_BY_USER_ID)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS AND SETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS @Override public String getName() { return getAttributeValue(ATTRIBUTE_NAME); } @Override public String getDisplayName() { return getAttributeValue(ATTRIBUTE_DISPLAY_NAME); } public String getDescription() { return getAttributeValue(ATTRIBUTE_DESCRIPTION); } @Override public String getIcon() { return getAttributeValue(ATTRIBUTE_ICON); } @Override public Date getLastUpdateDate() { return getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE); } @Override public Date getCreationDate() { return getAttributeValueAsDate(ATTRIBUTE_CREATION_DATE); } @Override public APIID getCreatedByUserId() { return getAttributeValueAsAPIID(ATTRIBUTE_CREATED_BY_USER_ID); } public Long getNumberOfUsers() { return getAttributeValueAsLong(COUNTER_NUMBER_OF_USERS); } // SETTERS public void setDescription(final String description) { setAttribute(ATTRIBUTE_DESCRIPTION, description); } @Override public void setIcon(final String icon) { setAttribute(ATTRIBUTE_ICON, icon); } @Override public void setLastUpdateDate(final String date) { setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } @Override public void setLastUpdateDate(final Date date) { setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } @Override public void setCreationDate(final String date) { setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } @Override public void setCreationDate(final Date date) { setAttribute(ATTRIBUTE_CREATION_DATE, date); } @Override public void setCreatedByUserId(final String id) { setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id); } @Override public void setCreatedByUserId(final Long id) { setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id); } @Override public void setCreatedByUserId(final APIID id) { setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id); } @Override public void setId(final String id) { setAttribute(ATTRIBUTE_ID, id); } @Override public void setId(final Long id) { setAttribute(ATTRIBUTE_ID, id); } @Override public void setName(final String name) { setAttribute(ATTRIBUTE_NAME, name); } @Override public void setDisplayName(final String displayName) { setAttribute(ATTRIBUTE_DISPLAY_NAME, displayName); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return Definitions.get(RoleDefinition.TOKEN); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/UserDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import org.bonitasoft.web.rest.server.datastore.organization.Avatars; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.FileIsImageOrServletPathValidator; /** * User definition * * @author Séverin Moussel */ public class UserDefinition extends ItemDefinition { /** * Singleton */ public static UserDefinition get() { return (UserDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "user"; /** * the URL of user resource */ private static final String API_URL = "../API/identity/user"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { setPrimaryKeys(UserItem.ATTRIBUTE_ID); } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(UserItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(UserItem.ATTRIBUTE_FIRSTNAME, ItemAttribute.TYPE.STRING) .isMandatory(); createAttribute(UserItem.ATTRIBUTE_LASTNAME, ItemAttribute.TYPE.STRING) .isMandatory(); createAttribute(UserItem.ATTRIBUTE_ICON, ItemAttribute.TYPE.STRING) .addValidator(new FileIsImageOrServletPathValidator(Avatars.PATH)); createAttribute(UserItem.ATTRIBUTE_USERNAME, ItemAttribute.TYPE.STRING) .isMandatory(); createAttribute(UserItem.ATTRIBUTE_PASSWORD, ItemAttribute.TYPE.PASSWORD); createAttribute(UserItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(UserItem.ATTRIBUTE_CREATED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(UserItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(UserItem.ATTRIBUTE_LAST_CONNECTION_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(UserItem.ATTRIBUTE_TITLE, ItemAttribute.TYPE.STRING); createAttribute(UserItem.ATTRIBUTE_JOB_TITLE, ItemAttribute.TYPE.STRING); createAttribute(UserItem.ATTRIBUTE_MANAGER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(UserItem.ATTRIBUTE_ENABLED, ItemAttribute.TYPE.BOOLEAN); } /* * (non-Javadoc) * @see org.bonitasoft.web.toolkit.client.data.item.ItemDefinition#defineDeploys() */ @Override protected void defineDeploys() { super.defineDeploys(); declareDeployable(UserItem.DEPLOY_PROFESSIONAL_DATA, Definitions.get(ProfessionalContactDataDefinition.TOKEN)); declareDeployable(UserItem.DEPLOY_PERSONAL_DATA, Definitions.get(PersonalContactDataDefinition.TOKEN)); } @Override public UserItem _createItem() { return new UserItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/identity/UserItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.identity; import java.util.Date; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasCreator; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasLastUpdateDate; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Séverin Moussel */ public class UserItem extends Item implements ItemHasUniqueId, ItemHasLastUpdateDate, ItemHasCreator, ItemHasIcon { public static final String DEFAULT_USER_ICON = "icons/default/icon_user.png"; public static final String ATTRIBUTE_FIRSTNAME = "firstname"; public static final String ATTRIBUTE_LASTNAME = "lastname"; public static final String ATTRIBUTE_PASSWORD = "password"; public static final String ATTRIBUTE_USERNAME = "userName"; public static final String ATTRIBUTE_MANAGER_ID = "manager_id"; public static final String ATTRIBUTE_LAST_CONNECTION_DATE = "last_connection"; public static final String ATTRIBUTE_TITLE = "title"; public static final String ATTRIBUTE_JOB_TITLE = "job_title"; public static final String ATTRIBUTE_STATE = "user_state"; public static final String ATTRIBUTE_ENABLED = "enabled"; public UserItem() { super(); } public UserItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES VALUES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String VALUE_ACTIVATION_STATE_DISABLED = "DISABLED"; public static final String VALUE_ACTIVATION_STATE_ENABLED = "ENABLED"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String FILTER_ROLE_ID = "role_id"; public static final String FILTER_GROUP_ID = "group_id"; public static final String FILTER_PROFILE_ID = "profile_id"; public static final String FILTER_INDIRECT_PROFILE_ID = "indirect_profile_id"; public static final String FILTER_PROCESS_ID = "process_id"; public static final String FILTER_HUMAN_TASK_ID = "task_id"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // COUNTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String COUNTER_OPEN_TASKS = "open_tasks"; public static final String COUNTER_OVERDUE_TASKS = "overdue_tasks"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS AND SETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS public String getFirstName() { return this.getAttributeValue(ATTRIBUTE_FIRSTNAME); } public String getLastName() { return this.getAttributeValue(ATTRIBUTE_LASTNAME); } public String getPassword() { return this.getAttributeValue(ATTRIBUTE_PASSWORD); } public String getUserName() { return this.getAttributeValue(ATTRIBUTE_USERNAME); } public APIID getManagerId() { return APIID.makeAPIID(this.getAttributeValue(ATTRIBUTE_MANAGER_ID)); } @Override public String getIcon() { return this.getAttributeValue(ATTRIBUTE_ICON); } @Override public Date getCreationDate() { return this.getAttributeValueAsDate(ATTRIBUTE_CREATION_DATE); } @Override public APIID getCreatedByUserId() { return APIID.makeAPIID(this.getAttributeValue(ATTRIBUTE_CREATED_BY_USER_ID)); } @Override public Date getLastUpdateDate() { return this.getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE); } public String getState() { return this.getAttributeValue(ATTRIBUTE_STATE); } public String getLastConnectionDate() { return this.getAttributeValue(ATTRIBUTE_LAST_CONNECTION_DATE); } public String getTitle() { return this.getAttributeValue(ATTRIBUTE_TITLE); } public String getJobTitle() { return this.getAttributeValue(ATTRIBUTE_JOB_TITLE); } public boolean isEnabled() { return "true".equals(getAttributeValue(ATTRIBUTE_ENABLED)); } public void setEnabled(boolean enabled) { setAttribute(ATTRIBUTE_ENABLED, String.valueOf(enabled)); } // SETTERS @Override public void setId(final String id) { this.setAttribute("id", id); } @Override public void setId(final Long id) { this.setAttribute("id", id.toString()); } public void setFirstName(final String firstName) { this.setAttribute(ATTRIBUTE_FIRSTNAME, firstName); } public void setLastName(final String lastName) { this.setAttribute(ATTRIBUTE_LASTNAME, lastName); } public void setPassword(final String password) { this.setAttribute(ATTRIBUTE_PASSWORD, password); } public void setUserName(final String userName) { this.setAttribute(ATTRIBUTE_USERNAME, userName); } public void setManagerId(final String id) { this.setAttribute(ATTRIBUTE_MANAGER_ID, id); } public void setManagerId(final Long id) { setManagerId(id.toString()); } public void setManagerId(final APIID id) { setAttribute(ATTRIBUTE_MANAGER_ID, id); } public void setState(final String state) { this.setAttribute(ATTRIBUTE_STATE, state); } @Override public void setIcon(final String iconPath) { this.setAttribute(ATTRIBUTE_ICON, iconPath); } @Override public void setCreationDate(final String date) { this.setAttribute(ATTRIBUTE_CREATION_DATE, date); } @Override public void setCreationDate(final Date date) { this.setAttribute(ATTRIBUTE_CREATION_DATE, date); } @Override public void setCreatedByUserId(final String id) { this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id); } @Override public void setCreatedByUserId(final Long id) { setCreatedByUserId(id.toString()); } @Override public void setCreatedByUserId(final APIID id) { this.setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id); } @Override public void setLastUpdateDate(final String date) { this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } @Override public void setLastUpdateDate(final Date date) { this.setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } public void setLastConnectionDate(final String date) { this.setAttribute(ATTRIBUTE_LAST_CONNECTION_DATE, date); } public void setLastConnectionDate(final Date date) { this.setAttribute(ATTRIBUTE_LAST_CONNECTION_DATE, date); } public void setTitle(final String title) { this.setAttribute(ATTRIBUTE_TITLE, title); } public void setJobTitle(final String jobTitle) { this.setAttribute(ATTRIBUTE_JOB_TITLE, jobTitle); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEPLOYS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String DEPLOY_PROFESSIONAL_DATA = "professional_data"; public static final String DEPLOY_PERSONAL_DATA = "personnal_data"; public ProfessionalContactDataItem getProfessionalData() { return new ProfessionalContactDataItem(getDeploy(DEPLOY_PROFESSIONAL_DATA)); } public PersonalContactDataItem getPersonalData() { return new PersonalContactDataItem(getDeploy(DEPLOY_PERSONAL_DATA)); } public UserItem getManager() { return new UserItem(getDeploy(ATTRIBUTE_MANAGER_ID)); } @Override public UserItem getCreatedByUser() { return new UserItem(getDeploy(ATTRIBUTE_CREATED_BY_USER_ID)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemDefinition getItemDefinition() { return new UserDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/platform/PlatformDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.platform; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Zhiheng Yang */ public class PlatformDefinition extends ItemDefinition { /** * Singleton */ public static PlatformDefinition get() { return (PlatformDefinition) Definitions.get(TOKEN); } public final static String TOKEN = "platformInfo"; private static final String API_URL = "../API/platform/platform"; @Override public String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(PlatformItem.ATTRIBUTE_CREATED_DATE, ItemAttribute.TYPE.STRING); createAttribute(PlatformItem.ATTRIBUTE_INIT_VERSION, ItemAttribute.TYPE.STRING); createAttribute(PlatformItem.ATTRIBUTE_VERSION, ItemAttribute.TYPE.STRING); createAttribute(PlatformItem.ATTRIBUTE_CREATEDBY, ItemAttribute.TYPE.STRING); createAttribute(PlatformItem.ATTRIBUTE_STATE, ItemAttribute.TYPE.STRING); } @Override protected void definePrimaryKeys() { setPrimaryKeys(PlatformItem.ATTRIBUTE_CREATED_DATE); } @Override protected PlatformItem _createItem() { return new PlatformItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/platform/PlatformItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.platform; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Zhiheng Yang */ public class PlatformItem extends Item { public PlatformItem() { super(); } public PlatformItem(final IItem item) { super(item); } public final static String ATTRIBUTE_VERSION = "version"; public final static String ATTRIBUTE_INIT_VERSION = "initialVersion"; public final static String ATTRIBUTE_CREATED_DATE = "created"; public final static String ATTRIBUTE_CREATEDBY = "createdBy"; public final static String ATTRIBUTE_STATE = "state"; public PlatformItem(final String version, final String initVersion, final String createdDate, final String createdBy, final String state) { this.setAttribute(ATTRIBUTE_VERSION, version); this.setAttribute(ATTRIBUTE_INIT_VERSION, initVersion); this.setAttribute(ATTRIBUTE_CREATED_DATE, createdDate); this.setAttribute(ATTRIBUTE_CREATEDBY, createdBy); this.setAttribute(ATTRIBUTE_STATE, state); } @Override public ItemDefinition getItemDefinition() { return new PlatformDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/page/PageDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.portal.page; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Fabio Lombardi */ public class PageDefinition extends ItemDefinition { public static final String TOKEN = "page"; protected static final String API_URL = "../API/portal/page"; public static PageDefinition get() { return (PageDefinition) Definitions.get(TOKEN); } @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(PageItem.ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(PageItem.ATTRIBUTE_URL_TOKEN, ItemAttribute.TYPE.STRING) .isMandatory(true); createAttribute(PageItem.ATTRIBUTE_DISPLAY_NAME, ItemAttribute.TYPE.TEXT) .isMandatory(true); createAttribute(PageItem.ATTRIBUTE_CONTENT_NAME, ItemAttribute.TYPE.STRING) .isMandatory(true); createAttribute(PageItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT); createAttribute(PageItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.DATE); createAttribute(PageItem.ATTRIBUTE_CREATED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(PageItem.ATTRIBUTE_IS_PROVIDED, ItemAttribute.TYPE.BOOLEAN); createAttribute(PageItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(PageItem.ATTRIBUTE_UPDATED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(PageItem.ATTRIBUTE_PROCESS_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(PageItem.ATTRIBUTE_CONTENT_TYPE, ItemAttribute.TYPE.STRING); createAttribute(PageItem.ATTRIBUTE_IS_EDITABLE, ItemAttribute.TYPE.BOOLEAN); createAttribute(PageItem.ATTRIBUTE_IS_REMOVABLE, ItemAttribute.TYPE.BOOLEAN); } @Override protected void definePrimaryKeys() { setPrimaryKeys(PageItem.ATTRIBUTE_ID); } @Override protected PageItem _createItem() { return new PageItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/page/PageItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.portal.page; import java.util.Date; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Fabio Lombardi */ public class PageItem extends Item implements ItemHasUniqueId { public static final String ATTRIBUTE_PROCESS_ID = "processDefinitionId"; public static final String ATTRIBUTE_URL_TOKEN = "urlToken"; public static final String ATTRIBUTE_DISPLAY_NAME = "displayName"; public static final String ATTRIBUTE_DESCRIPTION = "description"; public static final String ATTRIBUTE_IS_PROVIDED = "isProvided"; public static final String ATTRIBUTE_LAST_UPDATE_DATE = "lastUpdateDate"; public static final String ATTRIBUTE_CREATION_DATE = "creationDate"; public static final String ATTRIBUTE_CREATED_BY_USER_ID = "createdBy"; public static final String ATTRIBUTE_UPDATED_BY_USER_ID = "updatedBy"; public static final String ATTRIBUTE_CONTENT_NAME = "contentName"; public static final String ATTRIBUTE_CONTENT_TYPE = "contentType"; public static final String ATTRIBUTE_IS_EDITABLE = "isEditable"; public static final String ATTRIBUTE_IS_REMOVABLE = "isRemovable"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String FILTER_CONTENT_TYPE = "contentType"; public PageItem() { } public PageItem(final IItem item) { super(item); } @Override public void setId(final String id) { setAttribute(ATTRIBUTE_ID, id); } @Override public void setId(final Long id) { setAttribute(ATTRIBUTE_ID, id); } public void setProcessId(final String id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } public void setProcessId(final Long id) { setAttribute(ATTRIBUTE_PROCESS_ID, id); } public void setUrlToken(final String name) { setAttribute(ATTRIBUTE_URL_TOKEN, name); } public void setDisplayName(final String name) { setAttribute(ATTRIBUTE_DISPLAY_NAME, name); } public void setDescription(final String description) { setAttribute(ATTRIBUTE_DESCRIPTION, description); } public void setCreationDate(final String date) { setAttribute(ATTRIBUTE_CREATION_DATE, date); } public void setCreationDate(final Date date) { setAttribute(ATTRIBUTE_CREATION_DATE, date); } public void setCreatedByUserId(final String id) { setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id); } public void setCreatedByUserId(final Long id) { setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id); } public void setCreatedByUserId(final APIID id) { setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id); } public void setUpdatedByUserId(final String id) { setAttribute(ATTRIBUTE_UPDATED_BY_USER_ID, id); } public void setUpdatedByUserId(final Long id) { setAttribute(ATTRIBUTE_UPDATED_BY_USER_ID, id); } public void setUpdatedByUserId(final APIID id) { setAttribute(ATTRIBUTE_UPDATED_BY_USER_ID, id); } public void setLastUpdateDate(final String date) { setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } public void setLastUpdateDate(final Date date) { setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } public void setIsProvided(final boolean isProvided) { setAttribute(ATTRIBUTE_IS_PROVIDED, isProvided); } public void setContentName(final String name) { setAttribute(ATTRIBUTE_CONTENT_NAME, name); } public void setContentType(final String contentType) { setAttribute(ATTRIBUTE_CONTENT_TYPE, contentType); } public void setIsEditable(final boolean isEditable) { setAttribute(ATTRIBUTE_IS_EDITABLE, isEditable); } public void setIsRemovable(final boolean isRemovable) { setAttribute(ATTRIBUTE_IS_REMOVABLE, isRemovable); } public String getUrlToken() { return getAttributeValue(ATTRIBUTE_URL_TOKEN); } public String getDisplayName() { return getAttributeValue(ATTRIBUTE_DISPLAY_NAME); } public String getDescription() { return getAttributeValue(ATTRIBUTE_DESCRIPTION); } public boolean isProvided() { return Boolean.parseBoolean(getAttributeValue(ATTRIBUTE_IS_PROVIDED)); } public String getContentName() { return getAttributeValue(ATTRIBUTE_CONTENT_NAME); } public Date getLastUpdateDate() { return getAttributeValueAsDate(ATTRIBUTE_LAST_UPDATE_DATE); } @Override public ItemDefinition getItemDefinition() { return PageDefinition.get(); } public Date getCreationDate() { return getAttributeValueAsDate(ATTRIBUTE_CREATION_DATE); } public APIID getCreatedByUserId() { return getAttributeValueAsAPIID(ATTRIBUTE_CREATED_BY_USER_ID); } public UserItem getCreatedByUser() { return (UserItem) getDeploy(ATTRIBUTE_CREATED_BY_USER_ID); } public APIID getUpdatedByUserId() { return getAttributeValueAsAPIID(ATTRIBUTE_UPDATED_BY_USER_ID); } public APIID getProcessId() { return getAttributeValueAsAPIID(ATTRIBUTE_PROCESS_ID); } public String getContentType() { return getAttributeValue(ATTRIBUTE_CONTENT_TYPE); } public UserItem getUpdatedByUser() { return (UserItem) getDeploy(ATTRIBUTE_UPDATED_BY_USER_ID); } public boolean isEditable() { return Boolean.parseBoolean(getAttributeValue(ATTRIBUTE_IS_EDITABLE)); } public boolean isRemovable() { return Boolean.parseBoolean(getAttributeValue(ATTRIBUTE_IS_REMOVABLE)); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/profile/AbstractMemberDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.portal.profile; import static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.ATTRIBUTE_GROUP_ID; import static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.ATTRIBUTE_ROLE_ID; import static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.ATTRIBUTE_USER_ID; import static org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE.ITEM_ID; import org.bonitasoft.web.rest.model.identity.RoleDefinition; import org.bonitasoft.web.rest.model.identity.UserDefinition; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public abstract class AbstractMemberDefinition extends ItemDefinition { @Override protected void defineAttributes() { createAttribute(ATTRIBUTE_GROUP_ID, ITEM_ID); createAttribute(ATTRIBUTE_USER_ID, ITEM_ID); createAttribute(ATTRIBUTE_ROLE_ID, ITEM_ID); } @Override protected void defineDeploys() { declareDeployable(ATTRIBUTE_USER_ID, UserDefinition.get()); declareDeployable(ATTRIBUTE_GROUP_ID, UserDefinition.get()); declareDeployable(ATTRIBUTE_ROLE_ID, RoleDefinition.get()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/profile/AbstractMemberItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.portal.profile; import org.bonitasoft.web.rest.model.identity.GroupItem; import org.bonitasoft.web.rest.model.identity.RoleItem; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; /** * @author Séverin Moussel */ public abstract class AbstractMemberItem extends Item { public AbstractMemberItem() { super(); } public AbstractMemberItem(final IItem item) { super(item); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String ATTRIBUTE_USER_ID = "user_id"; public static final String ATTRIBUTE_ROLE_ID = "role_id"; public static final String ATTRIBUTE_GROUP_ID = "group_id"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String FILTER_MEMBER_TYPE = "member_type"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FILTERS VALUES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final String VALUE_MEMBER_TYPE_USER = "user"; public static final String VALUE_MEMBER_TYPE_GROUP = "group"; public static final String VALUE_MEMBER_TYPE_ROLE = "role"; public static final String VALUE_MEMBER_TYPE_MEMBERSHIP = "roleAndGroup"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS / GETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public APIID getUserId() { return getAttributeValueAsAPIID(ATTRIBUTE_USER_ID); } public void setUserId(final String userId) { setAttribute(ATTRIBUTE_USER_ID, userId); } public void setUserId(final APIID userId) { setAttribute(ATTRIBUTE_USER_ID, userId); } public void setUserId(final Long userId) { setAttribute(ATTRIBUTE_USER_ID, userId); } public APIID getRoleId() { return getAttributeValueAsAPIID(ATTRIBUTE_ROLE_ID); } public void setRoleId(final String roleId) { setAttribute(ATTRIBUTE_ROLE_ID, roleId); } public void setRoleId(final APIID roleId) { setAttribute(ATTRIBUTE_ROLE_ID, roleId); } public void setRoleId(final Long roleId) { setAttribute(ATTRIBUTE_ROLE_ID, roleId); } public APIID getGroupId() { return getAttributeValueAsAPIID(ATTRIBUTE_GROUP_ID); } public void setGroupId(final String groupId) { setAttribute(ATTRIBUTE_GROUP_ID, groupId); } public void setGroupId(final APIID groupId) { setAttribute(ATTRIBUTE_GROUP_ID, groupId); } public void setGroupId(final Long groupId) { setAttribute(ATTRIBUTE_GROUP_ID, groupId); } // Deploys public UserItem getUser() { return new UserItem(getDeploy(ATTRIBUTE_USER_ID)); } public RoleItem getRole() { return new RoleItem(getDeploy(ATTRIBUTE_ROLE_ID)); } public GroupItem getGroup() { return new GroupItem(getDeploy(ATTRIBUTE_GROUP_ID)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void setMembershipId(final APIID roleId, final APIID groupId) { setRoleId(roleId); setGroupId(groupId); } public boolean isGroup() { return getGroupId() != null && getUserId() == null && getRoleId() == null; } public boolean isRole() { return getGroupId() == null && getUserId() == null && getRoleId() != null; } public boolean isUser() { return getGroupId() == null && getUserId() != null && getRoleId() == null; } public boolean isMembership() { return getGroupId() != null && getUserId() == null && getRoleId() != null; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/profile/ProfileDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.portal.profile; import static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon.ATTRIBUTE_ICON; import static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId.ATTRIBUTE_ID; import org.bonitasoft.web.rest.server.datastore.organization.Avatars; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.FileIsImageOrServletPathValidator; /** * @author Julien Mege * @author Séverin Moussel */ public class ProfileDefinition extends ItemDefinition { /** * Singleton */ public static ProfileDefinition get() { return (ProfileDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "profile"; /** * the URL of profile resource */ protected static final String API_URL = "../API/portal/profile"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { setPrimaryKeys(ATTRIBUTE_ID); } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(ATTRIBUTE_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ProfileItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING) .isMandatory(true); createAttribute(ProfileItem.ATTRIBUTE_DESCRIPTION, ItemAttribute.TYPE.TEXT); createAttribute(ATTRIBUTE_ICON, ItemAttribute.TYPE.STRING) .addValidator(new FileIsImageOrServletPathValidator(Avatars.PATH)); createAttribute(ProfileItem.ATTRIBUTE_UPDATED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ProfileItem.ATTRIBUTE_LAST_UPDATE_DATE, ItemAttribute.TYPE.DATETIME); createAttribute(ProfileItem.ATTRIBUTE_CREATED_BY_USER_ID, ItemAttribute.TYPE.ITEM_ID); createAttribute(ProfileItem.ATTRIBUTE_CREATION_DATE, ItemAttribute.TYPE.DATETIME); } @Override public ProfileItem _createItem() { return new ProfileItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/profile/ProfileItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.portal.profile; import java.util.Date; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Julien Mege * @author Zhiheng Yang * @author Séverin Moussel * @autor Paul Amar */ public class ProfileItem extends Item implements ItemHasUniqueId, ItemHasIcon { public static final String ATTRIBUTE_NAME = "name"; public static final String ATTRIBUTE_DESCRIPTION = "description"; public static final String FILTER_USER_ID = "user_id"; public static final String ATTRIBUTE_IS_DEFAULT = "is_default"; public static final String ATTRIBUTE_LAST_UPDATE_DATE = "lastUpdateDate"; public static final String ATTRIBUTE_CREATION_DATE = "creationDate"; public static final String ATTRIBUTE_CREATED_BY_USER_ID = "createdBy"; public static final String ATTRIBUTE_UPDATED_BY_USER_ID = "updatedBy"; public ProfileItem() { super(); } public ProfileItem(final IItem item) { super(item); } @Override public void setId(final String id) { setAttribute(ATTRIBUTE_ID, id); } @Override public void setId(final Long id) { setAttribute(ATTRIBUTE_ID, id); } @Override public void setIcon(final String icon) { setAttribute(ATTRIBUTE_ICON, icon); } public void setName(final String name) { setAttribute(ATTRIBUTE_NAME, name); } public void setDescription(final String description) { setAttribute(ATTRIBUTE_DESCRIPTION, description); } public void setCreationDate(String date) { setAttribute(ATTRIBUTE_CREATION_DATE, date); } public void setCreationDate(Date date) { setAttribute(ATTRIBUTE_CREATION_DATE, date); } public void setCreatedByUserId(String id) { setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id); } public void setCreatedByUserId(Long id) { setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id); } public void setCreatedByUserId(APIID id) { setAttribute(ATTRIBUTE_CREATED_BY_USER_ID, id); } public UserItem getCreatedByUser() { return (UserItem) getDeploy(ATTRIBUTE_CREATED_BY_USER_ID); } public void setUpdatedByUserId(String id) { setAttribute(ATTRIBUTE_UPDATED_BY_USER_ID, id); } public void setUpdatedByUserId(Long id) { setAttribute(ATTRIBUTE_UPDATED_BY_USER_ID, id); } public void setUpdatedByUserId(APIID id) { setAttribute(ATTRIBUTE_UPDATED_BY_USER_ID, id); } public void setLastUpdateDate(final String date) { setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } public void setLastUpdateDate(final Date date) { setAttribute(ATTRIBUTE_LAST_UPDATE_DATE, date); } public UserItem getUpdatedByUser() { return (UserItem) getDeploy(ATTRIBUTE_UPDATED_BY_USER_ID); } public void setIsDefault(final Boolean isDefault) { setAttribute(ATTRIBUTE_IS_DEFAULT, isDefault); } @Override public String getIcon() { return getAttributeValue(ATTRIBUTE_ICON); } public String getName() { return getAttributeValue(ATTRIBUTE_NAME); } public String getDescription() { return getAttributeValue(ATTRIBUTE_DESCRIPTION); } public String isDefault() { return getAttributeValue(ATTRIBUTE_IS_DEFAULT); } @Override public ItemDefinition getItemDefinition() { return ProfileDefinition.get(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/profile/ProfileMemberDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.portal.profile; import static org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem.ATTRIBUTE_PROFILE_ID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Julien Mege * @author Séverin Moussel */ public class ProfileMemberDefinition extends AbstractMemberDefinition { /** * Singleton */ public static ProfileMemberDefinition get() { return (ProfileMemberDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "memberProfile"; /** * the URL of UserProfileAssociation resource */ protected static final String API_URL = "../API/portal/profileMember"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { // FIXME : delete when id in engine is deleted setPrimaryKeys(ProfileMemberItem.ATTRIBUTE_ID); // FIXME : uncomment when id in engine is deleted // setPrimaryKeys( // ATTRIBUTE_PROFILE_ID, // ATTRIBUTE_USER_ID, // ATTRIBUTE_ROLE_ID, // ATTRIBUTE_GROUP_ID); } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(ATTRIBUTE_PROFILE_ID, ItemAttribute.TYPE.ITEM_ID).isMandatory(); super.defineAttributes(); } @Override protected void defineDeploys() { declareDeployable(ATTRIBUTE_PROFILE_ID, ProfileDefinition.get()); super.defineDeploys(); } @Override public ProfileMemberItem _createItem() { return new ProfileMemberItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/portal/profile/ProfileMemberItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.portal.profile; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; /** * @author Julien Mege * @author Séverin Moussel */ public class ProfileMemberItem extends AbstractMemberItem implements ItemHasUniqueId { public ProfileMemberItem() { super(); } public ProfileMemberItem(final IItem item) { super(item); } public static final String ATTRIBUTE_PROFILE_ID = "profile_id"; // FIXME : delete when id in engine is deleted @Override public void setId(final String id) { this.setAttribute(ATTRIBUTE_ID, id); } // FIXME : delete when id in engine is deleted @Override public void setId(final Long id) { this.setAttribute(ATTRIBUTE_ID, id); } public APIID getProfileId() { return getAttributeValueAsAPIID(ATTRIBUTE_PROFILE_ID); } public void setProfileId(final String id) { setAttribute(ATTRIBUTE_PROFILE_ID, id); } public void setProfileId(final APIID id) { setAttribute(ATTRIBUTE_PROFILE_ID, id); } public void setProfileId(final Long id) { setAttribute(ATTRIBUTE_PROFILE_ID, id); } // Deploys public ProfileItem getProfile() { return new ProfileItem(getDeploy(ATTRIBUTE_PROFILE_ID)); } @Override public ItemDefinition getItemDefinition() { return ProfileMemberDefinition.get(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/system/MaintenanceDetailsClient.java ================================================ /** * Copyright (C) 2023 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.web.rest.model.system; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.maintenance.MaintenanceDetails; /** * Client implementation object required for Json deserialization */ @Data @NoArgsConstructor @AllArgsConstructor public class MaintenanceDetailsClient implements MaintenanceDetails { private State maintenanceState; private String maintenanceMessage; private Boolean maintenanceMessageActive; @Override public State getMaintenanceState() { return maintenanceState; } @Override public String getMaintenanceMessage() { return maintenanceMessage; } @Override public Boolean isMaintenanceMessageActive() { return maintenanceMessageActive; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/tenant/BusinessDataModelDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.tenant; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; public class BusinessDataModelDefinition extends ItemDefinition { public static final String TOKEN = "bdm"; private static final String API_URL = "../API/tenant/" + TOKEN; public static BusinessDataModelDefinition get() { return (BusinessDataModelDefinition) Definitions.get(TOKEN); } @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { } @Override protected void definePrimaryKeys() { } @Override protected BusinessDataModelItem _createItem() { return new BusinessDataModelItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/tenant/BusinessDataModelItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.tenant; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; public class BusinessDataModelItem extends Item { public static final String ATTRIBUTE_FILE_UPLOAD_PATH = "fileUpload"; @Override public ItemDefinition getItemDefinition() { return new BusinessDataModelDefinition(); } public String getFileUploadPath() { return getAttributeValue(ATTRIBUTE_FILE_UPLOAD_PATH); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/user/User.java ================================================ /** * Copyright (C) 2022 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.web.rest.model.user; import java.io.Serializable; /** * @author Guo Yongtao */ public class User implements Serializable { /** * ID used for serialization. */ private static final long serialVersionUID = 1940844173066923676L; /** * The username */ protected String username; /** * Indicates the locale to use to display the user interface */ protected String locale; public User() { super(); // Mandatory for serialization. } public User(final String username, final String locale) { this.username = username; this.locale = locale; } /** * @return the userUUID */ public String getUsername() { return username; } public String getLocale() { return locale; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/APIPaginationUtils.java ================================================ /** * Copyright (C) 2026 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.web.rest.server; import java.util.List; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.filter.FormMappingTypeCreator; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.datastore.utils.Sorts; public class APIPaginationUtils { /** * Build search options from query parameters. * * @param page the page number (0-indexed) * @param count the number of results per page * @param search the search term * @param order the sort order * @param filters the filters * @return the search options */ public static SearchOptions buildSearchOptions(int page, int count, String search, String order, List filters) { Filters parsedFilters = new Filters(QueryParameterUtils.parseFilters(filters), new FormMappingTypeCreator()); Sorts sorts = new Sorts(order); return new SearchOptionsCreator(page, count, search, sorts, parsedFilters).create(); } /** * Build Content-Range header value. * * @param page the page number * @param countOnCurrentPage the page size * @param total the total count * @return the Content-Range header value */ public static String buildContentRange(int page, int countOnCurrentPage, long total) { return page + "-" + countOnCurrentPage + "/" + total; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/BonitaRestAPIFactory.java ================================================ /** * Copyright (C) 2022 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.web.rest.server; import org.bonitasoft.web.rest.server.api.application.APIApplication; import org.bonitasoft.web.rest.server.api.applicationmenu.APIApplicationMenu; import org.bonitasoft.web.rest.server.api.applicationpage.APIApplicationDataStoreFactory; import org.bonitasoft.web.rest.server.api.applicationpage.APIApplicationPage; import org.bonitasoft.web.rest.server.api.bpm.cases.APIArchivedCase; import org.bonitasoft.web.rest.server.api.bpm.cases.APIArchivedCaseDocument; import org.bonitasoft.web.rest.server.api.bpm.cases.APIArchivedComment; import org.bonitasoft.web.rest.server.api.bpm.cases.APICase; import org.bonitasoft.web.rest.server.api.bpm.cases.APICaseDocument; import org.bonitasoft.web.rest.server.api.bpm.cases.APICaseVariable; import org.bonitasoft.web.rest.server.api.bpm.cases.APIComment; import org.bonitasoft.web.rest.server.api.bpm.connector.APIArchivedConnectorInstance; import org.bonitasoft.web.rest.server.api.bpm.connector.APIConnectorInstance; import org.bonitasoft.web.rest.server.api.bpm.flownode.APIActivity; import org.bonitasoft.web.rest.server.api.bpm.flownode.APIFlowNode; import org.bonitasoft.web.rest.server.api.bpm.flownode.APIHumanTask; import org.bonitasoft.web.rest.server.api.bpm.flownode.APITask; import org.bonitasoft.web.rest.server.api.bpm.flownode.APIUserTask; import org.bonitasoft.web.rest.server.api.bpm.flownode.archive.APIArchivedActivity; import org.bonitasoft.web.rest.server.api.bpm.flownode.archive.APIArchivedFlowNode; import org.bonitasoft.web.rest.server.api.bpm.flownode.archive.APIArchivedHumanTask; import org.bonitasoft.web.rest.server.api.bpm.flownode.archive.APIArchivedTask; import org.bonitasoft.web.rest.server.api.bpm.flownode.archive.APIArchivedUserTask; import org.bonitasoft.web.rest.server.api.bpm.process.APIActor; import org.bonitasoft.web.rest.server.api.bpm.process.APIActorMember; import org.bonitasoft.web.rest.server.api.bpm.process.APICategory; import org.bonitasoft.web.rest.server.api.bpm.process.APIProcess; import org.bonitasoft.web.rest.server.api.bpm.process.APIProcessCategory; import org.bonitasoft.web.rest.server.api.bpm.process.APIProcessConnector; import org.bonitasoft.web.rest.server.api.bpm.process.APIProcessConnectorDependency; import org.bonitasoft.web.rest.server.api.bpm.process.APIProcessParameter; import org.bonitasoft.web.rest.server.api.bpm.process.APIProcessResolutionProblem; import org.bonitasoft.web.rest.server.api.document.APIArchivedDocument; import org.bonitasoft.web.rest.server.api.document.APIDocument; import org.bonitasoft.web.rest.server.api.organization.APICustomUserInfoDefinition; import org.bonitasoft.web.rest.server.api.organization.APICustomUserInfoUser; import org.bonitasoft.web.rest.server.api.organization.APICustomUserInfoValue; import org.bonitasoft.web.rest.server.api.organization.APIGroup; import org.bonitasoft.web.rest.server.api.organization.APIMembership; import org.bonitasoft.web.rest.server.api.organization.APIPersonalContactData; import org.bonitasoft.web.rest.server.api.organization.APIProfessionalContactData; import org.bonitasoft.web.rest.server.api.organization.APIRole; import org.bonitasoft.web.rest.server.api.organization.APIUser; import org.bonitasoft.web.rest.server.api.page.APIPage; import org.bonitasoft.web.rest.server.api.platform.APIPlatform; import org.bonitasoft.web.rest.server.api.profile.APIProfile; import org.bonitasoft.web.rest.server.api.profile.APIProfileMember; import org.bonitasoft.web.rest.server.api.system.APII18nLocale; import org.bonitasoft.web.rest.server.api.system.APISession; import org.bonitasoft.web.rest.server.datastore.application.ApplicationDataStoreCreator; import org.bonitasoft.web.rest.server.datastore.applicationmenu.ApplicationMenuDataStoreCreator; import org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClientCreator; import org.bonitasoft.web.rest.server.framework.API; import org.bonitasoft.web.rest.server.framework.RestAPIFactory; import org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Séverin Moussel */ public class BonitaRestAPIFactory extends RestAPIFactory { @Override public API defineApis(final String apiToken, final String resourceToken) { if ("identity".equals(apiToken)) { if ("user".equals(resourceToken)) { return new APIUser(); } else if ("role".equals(resourceToken)) { return new APIRole(); } else if ("group".equals(resourceToken)) { return new APIGroup(); } else if ("membership".equals(resourceToken)) { return new APIMembership(); } else if ("professionalcontactdata".equals(resourceToken)) { return new APIProfessionalContactData(); } else if ("personalcontactdata".equals(resourceToken)) { return new APIPersonalContactData(); } } else if ("customuserinfo".equals(apiToken)) { if ("definition".equals(resourceToken)) { return new APICustomUserInfoDefinition(new CustomUserInfoEngineClientCreator()); } else if ("user".equals(resourceToken)) { return new APICustomUserInfoUser(new CustomUserInfoEngineClientCreator()); } else if ("value".equals(resourceToken)) { return new APICustomUserInfoValue(new CustomUserInfoEngineClientCreator()); } } else if ("system".equals(apiToken)) { if ("i18nlocale".equals(resourceToken)) { return new APII18nLocale(); } else if ("session".equals(resourceToken)) { return new APISession(); } } else if ("portal".equals(apiToken)) { if ("profile".equals(resourceToken)) { return new APIProfile(); } else if ("profileMember".equals(resourceToken)) { return new APIProfileMember(); } else if ("page".equals(resourceToken)) { return new APIPage(); } } else if ("bpm".equals(apiToken)) { if ("humanTask".equals(resourceToken)) { return new APIHumanTask(); } else if ("userTask".equals(resourceToken)) { return new APIUserTask(); } else if ("archivedHumanTask".equals(resourceToken)) { return new APIArchivedHumanTask(); } else if ("archivedUserTask".equals(resourceToken)) { return new APIArchivedUserTask(); } else if ("process".equals(resourceToken)) { return new APIProcess(); } else if ("category".equals(resourceToken)) { return new APICategory(); } else if ("processCategory".equals(resourceToken)) { return new APIProcessCategory(); } else if ("processConnector".equals(resourceToken)) { return new APIProcessConnector(); } else if ("case".equals(resourceToken)) { return new APICase(); } else if ("archivedCase".equals(resourceToken)) { return new APIArchivedCase(); } else if ("comment".equals(resourceToken)) { return new APIComment(); } else if ("archivedComment".equals(resourceToken)) { return new APIArchivedComment(); } else if ("document".equals(resourceToken)) { return new APIDocument(); } else if ("archiveddocument".equals(resourceToken)) { return new APIArchivedDocument(); } else if ("actor".equals(resourceToken)) { return new APIActor(); } else if ("actorMember".equals(resourceToken)) { return new APIActorMember(); } else if ("delegation".equals(resourceToken)) { return new APIActorMember(); } else if ("activity".equals(resourceToken)) { return new APIActivity(); } else if ("archivedActivity".equals(resourceToken)) { return new APIArchivedActivity(); } else if ("task".equals(resourceToken)) { return new APITask(); } else if ("archivedTask".equals(resourceToken)) { return new APIArchivedTask(); } else if ("flowNode".equals(resourceToken)) { return new APIFlowNode(); } else if ("archivedFlowNode".equals(resourceToken)) { return new APIArchivedFlowNode(); } else if ("processResolutionProblem".equals(resourceToken)) { return new APIProcessResolutionProblem(); } else if ("caseDocument".equals(resourceToken)) { return new APICaseDocument(); } else if ("archivedCaseDocument".equals(resourceToken)) { return new APIArchivedCaseDocument(); } else if ("connectorInstance".equals(resourceToken)) { return new APIConnectorInstance(); } else if ("archivedConnectorInstance".equals(resourceToken)) { return new APIArchivedConnectorInstance(); } else if ("processConnectorDependency".equals(resourceToken)) { return new APIProcessConnectorDependency(); } else if ("caseVariable".equals(resourceToken)) { return new APICaseVariable(); } else if ("processParameter".equals(resourceToken)) { return new APIProcessParameter(); } } else if ("living".equals(apiToken)) { if ("application".equals(resourceToken)) { return new APIApplication(new ApplicationDataStoreCreator(), new APIApplicationDataStoreFactory()); } else if ("application-page".equals(resourceToken)) { return new APIApplicationPage(new APIApplicationDataStoreFactory()); } else if ("application-menu".equals(resourceToken)) { return new APIApplicationMenu(new ApplicationMenuDataStoreCreator()); } } else if ("platform".equals(apiToken)) { if ("platform".equals(resourceToken)) { return new APIPlatform(); } } throw new APINotFoundException(apiToken, resourceToken); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/BonitaRestAPIServlet.java ================================================ /** * Copyright (C) 2022 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.web.rest.server; import java.io.UnsupportedEncodingException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.exception.TenantStatusException; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.web.rest.model.ModelFactory; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.FlowNodeConverter; import org.bonitasoft.web.rest.server.framework.RestAPIFactory; import org.bonitasoft.web.rest.server.framework.servlet.APIServlet; import org.bonitasoft.web.toolkit.client.ItemDefinitionFactory; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; /** * @author Séverin Moussel */ public class BonitaRestAPIServlet extends APIServlet { private static final long serialVersionUID = 525945083859596909L; public BonitaRestAPIServlet() { super(); FlowNodeConverter.setFlowNodeConverter(new FlowNodeConverter()); } @Override protected ItemDefinitionFactory defineApplicatioFactoryCommon() { return new ModelFactory(); } @Override protected RestAPIFactory defineApplicatioFactoryServer() { return new BonitaRestAPIFactory(); } @Override protected void catchAllExceptions(final Throwable exception, final HttpServletRequest req, final HttpServletResponse resp) { resp.setCharacterEncoding("UTF-8"); try { req.setCharacterEncoding("UTF-8"); } catch (final UnsupportedEncodingException e) { super.catchAllExceptions(e, req, resp); } if (exception instanceof InvalidSessionException || exception instanceof APIException && exception.getCause() != null && exception.getCause() instanceof InvalidSessionException) { final HttpServletRequestAccessor requestAccessor = new HttpServletRequestAccessor(req); if (LOGGER.isDebugEnabled()) { LOGGER.debug(exception.getMessage(), exception); } outputException(exception, req, resp, HttpServletResponse.SC_UNAUTHORIZED); SessionUtil.sessionLogout(requestAccessor.getHttpSession()); } else if (exception.getCause() instanceof NotFoundException) { outputException(null, req, resp, HttpServletResponse.SC_NOT_FOUND); } else if (exception instanceof TenantStatusException) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Platform is probably under Maintenance : " + exception.getMessage()); } outputException(null, req, resp, HttpServletResponse.SC_SERVICE_UNAVAILABLE); } else { super.catchAllExceptions(exception, req, resp); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/QueryParameterUtils.java ================================================ /** * Copyright (C) 2025 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.web.rest.server; import java.util.HashMap; import java.util.List; import java.util.Map; import org.springframework.lang.Nullable; /** * Utilities to handle query parameters. * Shared across Spring MVC controllers. */ public class QueryParameterUtils { /** * Builds a map where keys are Engine constants defining filter keys, and values are values corresponding to those * keys. * * @param parameters the filters passed as string according to the form * {@code ["key1=value1", "key2=value2"]} * @return a map of the form {@code {key1: value1, key2: value2}}, or {@code null} if * {@code parameters} is {@code null} */ public static Map parseFilters(final List parameters) { if (parameters == null) { return null; } final Map results = new HashMap<>(); for (final String parameter : parameters) { final String[] split = parameter.split("="); if (split.length == 0) { // "=".split("=") returns an empty array — skip filters with no key continue; } if (split.length == 1) { // filter with no value (e.g. "key1" or "key1="), put null as value in the map results.put(split[0], null); } else { // valid filter, put key and value in the map (e.g. "key1=value1") results.put(split[0], parameter.substring(split[0].length() + 1)); } } return results; } /** * Extracts a string filter value from the filter list. * * @param filters the list of filter strings in format {@code ["key1=value1", "key2=value2"]} * @param filterName the name of the filter to extract * @return the filter value, or {@code null} if the filter is not found, {@code filters} is {@code null}, * or the value is empty */ public static @Nullable String extractStringFilter(List filters, String filterName) { if (filters == null) { return null; } for (String filter : filters) { String filterKey = filterName + "="; if (filter.startsWith(filterKey)) { String value = filter.substring(filterKey.length()); return value.isEmpty() ? null : value; } } return null; } /** * Extracts a mandatory string filter value from the filter list. * * @param filters the list of filter strings in format {@code ["key1=value1", "key2=value2"]} * @param filterName the name of the filter to extract * @return the filter value * @throws IllegalArgumentException if the filter is not found or its value is empty */ public static String extractMandatoryStringFilter(List filters, String filterName) { String value = extractStringFilter(filters, filterName); if (value == null) { throw new IllegalArgumentException("filter " + filterName + " is mandatory"); } return value; } /** * Extracts an optional numeric {@link Long} filter value from the filter list. * * @param filters the list of filter strings in format {@code ["key1=value1", "key2=value2"]} * @param filterName the name of the filter to extract * @return the filter value as a {@link Long}, or {@code null} if the filter is not found or {@code filters} * is {@code null} * @throws IllegalArgumentException if the filter value is not a valid number */ public static @Nullable Long extractLongFilter(List filters, String filterName) { String value = extractStringFilter(filters, filterName); try { return value == null ? null : Long.parseLong(value); } catch (NumberFormatException e) { throw new IllegalArgumentException("filter " + filterName + " must be a number"); } } /** * Extracts a mandatory numeric {@code long} filter value from the filter list. * * @param filters the list of filter strings in format {@code ["key1=value1", "key2=value2"]} * @param filterName the name of the filter to extract * @return the filter value as a {@code long} * @throws IllegalArgumentException if the filter is not found, its value is empty, or not a valid number */ public static long extractMandatoryLongFilter(List filters, String filterName) { Long value = extractLongFilter(filters, filterName); if (value == null) { throw new IllegalArgumentException("filter " + filterName + " is mandatory"); } return value; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/SpringWebConfiguration.java ================================================ /** * Copyright (C) 2023 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.web.rest.server; import java.util.ArrayList; import java.util.List; import com.fasterxml.jackson.databind.ObjectMapper; import org.bonitasoft.web.rest.server.utils.BonitaJacksonModuleProvider; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.format.support.FormattingConversionService; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.servlet.resource.ResourceUrlProvider; @Configuration // Note: Do NOT use @EnableWebMvc when extending WebMvcConfigurationSupport directly @ComponentScan({ "org.bonitasoft.web.rest.server.api", "com.bonitasoft.web.rest.server.api" }) public class SpringWebConfiguration extends WebMvcConfigurationSupport { @Bean @Override public RequestMappingHandlerMapping requestMappingHandlerMapping( @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) { RequestMappingHandlerMapping handlerMapping = super.requestMappingHandlerMapping(contentNegotiationManager, conversionService, resourceUrlProvider); handlerMapping.setAlwaysUseFullPath(true); return handlerMapping; } /** * Creates the list of HTTP message converters configured for the Bonita REST API. *

    * Uses Spring's default converter ordering (where StringHttpMessageConverter comes * before MappingJackson2HttpMessageConverter), then replaces the default Jackson * converter with one configured with Bonita custom serializers (_string suffix fields * for numeric IDs, custom date/time formats). *

    * This ordering is important: controllers returning pre-serialized JSON strings * (e.g. BusinessDataController, ProcessDefinitionDesignController) must be handled * by StringHttpMessageConverter to avoid double-serialization by Jackson. * * @return the configured message converters */ public static List> createBonitaMessageConverters() { var converters = new ArrayList>(); // Use a temporary instance to access the protected addDefaultHttpMessageConverters new SpringWebConfiguration().addDefaultHttpMessageConverters(converters); // Replace the default Jackson converter with one configured with Bonita custom serializers converters.removeIf(c -> c instanceof MappingJackson2HttpMessageConverter); ObjectMapper objectMapper = new ObjectMapper(); BonitaJacksonModuleProvider.configureObjectMapper(objectMapper); converters.add(new MappingJackson2HttpMessageConverter(objectMapper)); return converters; } @Override protected void configureMessageConverters(List> converters) { converters.addAll(createBonitaMessageConverters()); } @Override protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) { // enforces JSON as the default content type for content negotiation for Spring MVC APIs // /!\ Make sure that RestControllerUtils uses the same configuration /!\ configurer.defaultContentType(MediaType.APPLICATION_JSON); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/APIPreconditions.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api; import java.util.HashMap; import java.util.Map; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; /** * @author Vincent Elcrin */ public class APIPreconditions { public static void check(boolean condition, T_ message) { if (!condition) { throw new APIException(message); } } public static boolean containsOnly(String key, Map map) { if (map == null) { return false; } HashMap clone = new HashMap<>(map); return clone.remove(key) != null && clone.isEmpty(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/AbstractRESTController.java ================================================ /** * Copyright (C) 2023 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.web.rest.server.api; import javax.servlet.http.HttpSession; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.api.BusinessDataAPI; import org.bonitasoft.engine.api.CommandAPI; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.api.TenantAdministrationAPI; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.APISession; import org.springframework.http.HttpStatus; import org.springframework.web.server.ResponseStatusException; /** * Parent class providing common methods for Bonita REST Controllers */ public abstract class AbstractRESTController { public static final String API_SPRING_INTERNAL = "APISpringInternal"; public APISession getApiSession(HttpSession session) { APISession apiSession = (APISession) session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY); if (apiSession == null) { throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Not authenticated"); } return apiSession; } // VisibleForTesting public CommandAPI getCommandAPI(APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getCommandAPI(apiSession); } protected CommandAPI getCommandAPI(HttpSession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getCommandAPI(getApiSession(session)); } // VisibleForTesting public ProcessAPI getProcessAPI(APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getProcessAPI(apiSession); } protected ProcessAPI getProcessAPI(HttpSession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getProcessAPI(getApiSession(session)); } public TenantAdministrationAPI getTenantAdministrationAPI(HttpSession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getTenantAdministrationAPI(getApiSession(session)); } // VisibleForTesting public BusinessDataAPI getBusinessDataAPI(APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getBusinessDataAPI(apiSession); } protected BusinessDataAPI getBusinessDataAPI(HttpSession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return getBusinessDataAPI(getApiSession(session)); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/ConsoleAPI.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.server.framework.API; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Séverin Moussel */ public abstract class ConsoleAPI extends API { private APISession sessionSingleton = null; /** * Get the session to access the engine SDK */ protected APISession getEngineSession() { if (this.sessionSingleton == null) { this.sessionSingleton = (APISession) getHttpSession().getAttribute("apiSession"); } return this.sessionSingleton; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/FilterParameterBindingAdvice.java ================================================ /** * Copyright (C) 2025 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.web.rest.server.api; import static org.bonitasoft.web.rest.server.framework.APIServletCall.PARAMETER_FILTER; import java.util.List; import org.springframework.beans.propertyeditors.CustomCollectionEditor; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.InitBinder; /** * Global binding configuration for REST controllers. *

    * Registers a {@link CustomCollectionEditor} for the {@code "f"} request parameter only, so that * comma-separated filter values are preserved as a single string instead of being split into a * list by Spring's default binding. *

    * For example, with {@code ?f=names=Harry,Anna}: *

      *
    • "f" parameter: bound as the single value {@code "names=Harry,Anna"} (desired behavior)
    • *
    * This editor is intentionally not applied to other list parameters (e.g. {@code ids}), where * the default comma-splitting is expected: *
      *
    • "ids" parameter: {@code ?ids=1,2,3} is bound as three separate values * {@code ["1", "2", "3"]}
    • *
    */ @ControllerAdvice public class FilterParameterBindingAdvice { @InitBinder protected void initBinder(WebDataBinder binder) { if (PARAMETER_FILTER.equals(binder.getObjectName())) { binder.registerCustomEditor(List.class, new CustomCollectionEditor(List.class)); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/PlatformAPI.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api; import org.bonitasoft.engine.session.PlatformSession; import org.bonitasoft.web.rest.server.framework.API; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Julien Mege */ public abstract class PlatformAPI extends API { private PlatformSession sessionSingleton = null; /** * Get the session */ protected final PlatformSession getPlatformSession() { if (this.sessionSingleton == null) { this.sessionSingleton = (PlatformSession) getHttpSession().getAttribute("platformSession"); } return this.sessionSingleton; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/SpringResponseEntityUtils.java ================================================ /** * Copyright (C) 2025 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.web.rest.server.api; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; /** * Utility class to generate standardized error responses for Spring REST APIs. * It encapsulates the logic to create a ResponseEntity with an error message and status. */ public class SpringResponseEntityUtils { public static ResponseEntity generateErrorResponse(String exceptionClassName, HttpStatus status, String message) { return ResponseEntity.status(status).body(new ResponseError("class " + exceptionClassName, message)); } public static ResponseEntity generateErrorResponse(Exception exception, HttpStatus status) { return generateErrorResponse(exception.getClass().getName(), status, exception.getMessage()); } public record ResponseError(String exception, String message) {} } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/SpringRestResponseEntityExceptionHandler.java ================================================ /** * Copyright (C) 2023 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.web.rest.server.api; import static org.bonitasoft.engine.commons.ExceptionUtils.printLightWeightStacktrace; import static org.bonitasoft.web.rest.server.api.SpringResponseEntityUtils.generateErrorResponse; import java.lang.reflect.UndeclaredThrowableException; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException; import org.bonitasoft.engine.business.data.BusinessDataCrudOperationException; import org.bonitasoft.engine.business.data.InvalidBusinessDataModelException; import org.bonitasoft.engine.command.CommandExecutionException; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.exception.TenantStatusException; import org.bonitasoft.engine.exception.UnavailableLockException; import org.bonitasoft.engine.session.InvalidSessionException; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.util.ClassUtils; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.context.request.WebRequest; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.multipart.MaxUploadSizeExceededException; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; @Slf4j @RestControllerAdvice public class SpringRestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(value = { TenantStatusException.class }) protected ResponseEntity handleMaintenanceMode(RuntimeException ex, WebRequest request) { return handleExceptionInternal(ex, "Platform under maintenance", new HttpHeaders(), HttpStatus.SERVICE_UNAVAILABLE, request); } @ExceptionHandler(value = { InvalidSessionException.class }) protected ResponseEntity handleInvalidSession(RuntimeException ex, WebRequest request, HttpSession httpSession) { SessionUtil.sessionLogout(httpSession); return handleExceptionInternal(ex, "Invalid session", new HttpHeaders(), HttpStatus.UNAUTHORIZED, request); } @ExceptionHandler(value = { Exception.class }) protected ResponseEntity defaultToInternalServerError(Exception exception) { // If no specific exception handler is found, log the error and return an internal server error: log.error("Generic server-side error:\n{}", printLightWeightStacktrace(exception)); log.debug(exception.getMessage(), exception); return bonitaHandleException(exception, HttpStatus.INTERNAL_SERVER_ERROR); } @ExceptionHandler(MaxUploadSizeExceededException.class) public ResponseEntity handleMaxSizeException(MaxUploadSizeExceededException ex) { return generateErrorResponse(ex.getClass().getName(), HttpStatus.BAD_REQUEST, getRootCause(ex).getMessage()); } @ExceptionHandler(IllegalArgumentException.class) public ResponseEntity handleIllegalArgumentException(IllegalArgumentException e) { return generateErrorResponse(e.getClass().getName(), HttpStatus.BAD_REQUEST, e.getMessage()); } private static final Map parameterErrorNames = Map.of("c", "count", "p", "page"); @ExceptionHandler(value = { MethodArgumentTypeMismatchException.class }) public ResponseEntity handleInvalidParameters(HttpServletRequest req, MethodArgumentTypeMismatchException ex) { String parameterName = ex.getName(); if (log.isDebugEnabled()) { log.debug("Invalid parameter [{}] {}: {}", req.getPathInfo(), parameterName, ex.getMessage()); } String mapping = parameterErrorNames.get(parameterName); if (mapping != null) { return bonitaHandleException( new IllegalArgumentException( "query parameter " + parameterName + " (" + mapping + ") should be a number"), HttpStatus.BAD_REQUEST); } Object value = ex.getValue(); Class requiredType = ex.getRequiredType(); // Check if the required type is a number (handle primitive types and primitive objects, e.g. int and Integer) boolean isNumber = requiredType != null && Number.class.isAssignableFrom(ClassUtils.resolvePrimitiveIfNecessary(requiredType)); if (isNumber) { return bonitaHandleException(new IllegalArgumentException("[ " + value + " ] must be a number"), HttpStatus.BAD_REQUEST); } return bonitaHandleException(new IllegalArgumentException("Bad parameter " + parameterName + "=" + value), HttpStatus.BAD_REQUEST); } @ExceptionHandler(value = { ActivityInstanceNotFoundException.class }) public ResponseEntity handleActivityInstanceNotFound(ActivityInstanceNotFoundException exception) { // The message contains the ID: return generateErrorResponse(exception.getClass().getName(), HttpStatus.NOT_FOUND, exception.getMessage()); } @ExceptionHandler(value = { NotFoundException.class }) public ResponseEntity handleNotFound(NotFoundException exception) { return bonitaHandleException(exception, HttpStatus.NOT_FOUND); } @ExceptionHandler(value = { AlreadyExistsException.class }) public ResponseEntity handleAlreadyExists(AlreadyExistsException exception) { return bonitaHandleException(exception, HttpStatus.CONFLICT); } @ExceptionHandler(value = { CommandExecutionException.class }) public ResponseEntity handleExecutionException(CommandExecutionException exception) { Throwable wrapped = exception.getCause(); final Throwable causedByIsDataNotFoundException = getFirstCauseOfType(wrapped, NotFoundException.class); if (causedByIsDataNotFoundException != null) { return bonitaHandleException(causedByIsDataNotFoundException, HttpStatus.NOT_FOUND); } final Throwable causedByBusinessDataCrudOperationException = getFirstCauseOfType(wrapped, BusinessDataCrudOperationException.class); if (causedByBusinessDataCrudOperationException != null) { return generateErrorResponse(causedByBusinessDataCrudOperationException.getClass().getName(), HttpStatus.BAD_REQUEST, causedByBusinessDataCrudOperationException.getMessage()); } return bonitaHandleException(exception, HttpStatus.INTERNAL_SERVER_ERROR); } @ExceptionHandler(value = { InvalidBusinessDataModelException.class }) public ResponseEntity handleInvalidBDM(InvalidBusinessDataModelException exception) { return bonitaHandleException(exception, HttpStatus.BAD_REQUEST); } @ExceptionHandler(value = { UndeclaredThrowableException.class }) protected ResponseEntity handleUnavailableLockException(UndeclaredThrowableException exception) { final UnavailableLockException unavailableLockException = (UnavailableLockException) getFirstCauseOfType( exception, UnavailableLockException.class); if (unavailableLockException != null) { // UnavailableLockExceptions are not declared in the API, as they are thrown by the engine interceptor (ServerAPIImpl), so it is automatically wrapped in an UndeclaredThrowableException by the HttpClient proxy: return generateErrorResponse(unavailableLockException.getClass().getName(), HttpStatus.NOT_ACCEPTABLE, unavailableLockException.getMessage()); } return bonitaHandleException(exception, HttpStatus.INTERNAL_SERVER_ERROR); } private Throwable getFirstCauseOfType(Throwable exception, Class exceptionTypeToSearch) { if (exception == null) { return null; } if (exceptionTypeToSearch.isAssignableFrom(exception.getClass())) { return exception; } return getFirstCauseOfType(exception.getCause(), exceptionTypeToSearch); } // get the root cause of the given exception: private Throwable getRootCause(Throwable exception) { if (exception == null) { return null; } Throwable cause = exception.getCause(); if (cause == null || cause == exception) { return exception; } return getRootCause(cause); } @Override protected ResponseEntity handleHttpMessageNotReadable(HttpMessageNotReadableException exception, HttpHeaders headers, HttpStatus status, WebRequest request) { return generateErrorResponse(exception.getClass().getName(), HttpStatus.BAD_REQUEST, "Unable to parse the JSON body"); } @Override protected ResponseEntity handleMissingServletRequestParameter( MissingServletRequestParameterException exception, HttpHeaders headers, HttpStatus status, WebRequest request) { String parameterName = exception.getParameterName(); String mapping = parameterErrorNames.get(parameterName); String message; if (mapping != null) { message = "query parameter " + parameterName + " (" + mapping + ") is mandatory"; } else { message = "query parameter " + parameterName + " is mandatory"; } return bonitaHandleException(new IllegalArgumentException(message), HttpStatus.BAD_REQUEST); } private static ResponseEntity bonitaHandleException(Throwable exception, HttpStatus status) { final Throwable cause = exception.getCause() != null ? exception.getCause() : exception; return generateErrorResponse(exception.getClass().getName(), status, cause.getMessage()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/application/APIApplication.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.application; import java.util.List; import java.util.Map; import org.bonitasoft.web.rest.model.application.AbstractApplicationDefinition; import org.bonitasoft.web.rest.model.application.AbstractApplicationItem; import org.bonitasoft.web.rest.model.application.ApplicationItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.api.applicationpage.APIApplicationDataStoreFactory; import org.bonitasoft.web.rest.server.api.deployer.DeployerFactory; import org.bonitasoft.web.rest.server.api.deployer.PageDeployer; import org.bonitasoft.web.rest.server.api.deployer.UserDeployer; import org.bonitasoft.web.rest.server.datastore.application.ApplicationDataStoreCreator; import org.bonitasoft.web.rest.server.datastore.organization.UserDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasAdd; import org.bonitasoft.web.rest.server.framework.api.APIHasDelete; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * Defines the REST API for applications. *

    Since 10.2.0 some types have changed in this interface. * Yet, these are not breaking changes as the json conversion does not change when using only legacy applications. * This class is not intended to be used as a Java API.

    * * @author Julien Mege */ public class APIApplication extends ConsoleAPI implements APIHasAdd, APIHasSearch, APIHasGet, APIHasUpdate, APIHasDelete { private final ApplicationDataStoreCreator creator; private final APIApplicationDataStoreFactory applicationDataStoreFactory; public APIApplication(final ApplicationDataStoreCreator creator, final APIApplicationDataStoreFactory applicationDataStoreFactory) { this.creator = creator; this.applicationDataStoreFactory = applicationDataStoreFactory; } /** * @deprecated as of 9.0.0, Applications should be created at startup. */ @Override @Deprecated(since = "9.0.0") public AbstractApplicationItem add(final AbstractApplicationItem item) { return creator.create(getEngineSession()).add(item); } /** * @deprecated as of 9.0.0, Applications should be updated at startup. */ @Override @Deprecated(since = "9.0.0") public AbstractApplicationItem update(final APIID id, final Map attributes) { return creator.create(getEngineSession()).update(id, attributes); } @Override public AbstractApplicationItem get(final APIID id) { return creator.create(getEngineSession()).get(id); } @Override public void delete(final List ids) { creator.create(getEngineSession()).delete(ids); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { return creator.create(getEngineSession()).search(page, resultsByPage, search, orders, filters); } @Override public String defineDefaultSearchOrder() { return AbstractApplicationItem.ATTRIBUTE_DISPLAY_NAME; } @SuppressWarnings("unchecked") @Override protected ItemDefinition defineItemDefinition() { return (ItemDefinition) AbstractApplicationDefinition.get(); } @Override protected void fillDeploys(final AbstractApplicationItem item, final List deploys) { addDeployer(new UserDeployer( new UserDatastore(getEngineSession()), AbstractApplicationItem.ATTRIBUTE_CREATED_BY)); addDeployer(new UserDeployer( new UserDatastore(getEngineSession()), AbstractApplicationItem.ATTRIBUTE_UPDATED_BY)); addDeployer(getDeployerFactory().createProfileDeployer(AbstractApplicationItem.ATTRIBUTE_PROFILE_ID)); if (item instanceof ApplicationItem) { addDeployer(new PageDeployer( applicationDataStoreFactory.createPageDataStore(getEngineSession()), ApplicationItem.ATTRIBUTE_LAYOUT_ID)); addDeployer(new PageDeployer( applicationDataStoreFactory.createPageDataStore(getEngineSession()), ApplicationItem.ATTRIBUTE_THEME_ID)); } super.fillDeploys(item, deploys); } protected DeployerFactory getDeployerFactory() { return new DeployerFactory(getEngineSession()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/applicationmenu/APIApplicationMenu.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.applicationmenu; import java.util.List; import java.util.Map; import org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuDefinition; import org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.api.deployer.ApplicationPageDeployer; import org.bonitasoft.web.rest.server.datastore.applicationmenu.ApplicationMenuDataStoreCreator; import org.bonitasoft.web.rest.server.datastore.applicationpage.ApplicationPageDataStoreCreator; import org.bonitasoft.web.rest.server.framework.api.APIHasAdd; import org.bonitasoft.web.rest.server.framework.api.APIHasDelete; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Julien Mege */ public class APIApplicationMenu extends ConsoleAPI implements APIHasAdd, APIHasSearch, APIHasGet, APIHasDelete { private final ApplicationMenuDataStoreCreator creator; public APIApplicationMenu(final ApplicationMenuDataStoreCreator creator) { this.creator = creator; } /** * @deprecated as of 9.0.0, Application menu should be created at startup. */ @Override @Deprecated(since = "9.0.0") public ApplicationMenuItem add(final ApplicationMenuItem item) { return creator.create(getEngineSession()).add(item); } @Override public ApplicationMenuItem get(final APIID id) { return creator.create(getEngineSession()).get(id); } /** * @deprecated as of 9.0.0, Application menu should be updated at startup. */ @Override @Deprecated(since = "9.0.0") public ApplicationMenuItem update(final APIID id, final Map attributes) { return creator.create(getEngineSession()).update(id, attributes); } @Override public void delete(final List ids) { creator.create(getEngineSession()).delete(ids); } @Override protected ItemDefinition defineItemDefinition() { return ApplicationMenuDefinition.get(); } @Override protected Datastore defineDefaultDatastore() { return creator.create(getEngineSession()); } @Override public String defineDefaultSearchOrder() { return ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME; } @Override protected void fillDeploys(final ApplicationMenuItem item, final List deploys) { addDeployer(new ApplicationPageDeployer( new ApplicationPageDataStoreCreator().create(getEngineSession()), ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID)); super.fillDeploys(item, deploys); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/applicationpage/APIApplicationDataStoreFactory.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.applicationpage; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.server.datastore.application.ApplicationDataStore; import org.bonitasoft.web.rest.server.datastore.application.ApplicationDataStoreCreator; import org.bonitasoft.web.rest.server.datastore.applicationpage.ApplicationPageDataStore; import org.bonitasoft.web.rest.server.datastore.applicationpage.ApplicationPageDataStoreCreator; import org.bonitasoft.web.rest.server.datastore.page.PageDatastore; import org.bonitasoft.web.rest.server.datastore.page.PageDatastoreFactory; import org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; public class APIApplicationDataStoreFactory { public ApplicationPageDataStore createApplicationPageDataStore(final APISession session) { return new ApplicationPageDataStoreCreator().create(session); } public PageDatastore createPageDataStore(final APISession session) { try { final PageDatastoreFactory pageDatastoreFactory = new PageDatastoreFactory(); return pageDatastoreFactory.create(session, WebBonitaConstantsUtils.getTenantInstance(), new EngineAPIAccessor(session).getPageAPI()); } catch (final Exception e) { throw new APIException(e); } } public ApplicationDataStore createApplicationDataStore(final APISession session) { return new ApplicationDataStoreCreator().create(session); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/applicationpage/APIApplicationPage.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.applicationpage; import java.util.List; import org.bonitasoft.web.rest.model.applicationpage.ApplicationPageDefinition; import org.bonitasoft.web.rest.model.applicationpage.ApplicationPageItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.api.deployer.ApplicationDeployer; import org.bonitasoft.web.rest.server.api.deployer.PageDeployer; import org.bonitasoft.web.rest.server.framework.api.APIHasAdd; import org.bonitasoft.web.rest.server.framework.api.APIHasDelete; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Julien Mege */ public class APIApplicationPage extends ConsoleAPI implements APIHasAdd, APIHasSearch, APIHasGet, APIHasDelete { private final APIApplicationDataStoreFactory factory; public APIApplicationPage(final APIApplicationDataStoreFactory factory) { this.factory = factory; } /** * @deprecated as of 9.0.0, Application page should be created at startup. */ @Override @Deprecated(since = "9.0.0") public ApplicationPageItem add(final ApplicationPageItem item) { return factory.createApplicationPageDataStore(getEngineSession()).add(item); } @Override protected ItemDefinition defineItemDefinition() { return ApplicationPageDefinition.get(); } @Override protected Datastore defineDefaultDatastore() { return factory.createApplicationPageDataStore(getEngineSession()); } @Override public String defineDefaultSearchOrder() { return ApplicationPageItem.ATTRIBUTE_TOKEN; } @Override protected void fillDeploys(final ApplicationPageItem item, final List deploys) { addDeployer(new PageDeployer( factory.createPageDataStore(getEngineSession()), ApplicationPageItem.ATTRIBUTE_PAGE_ID)); addDeployer(new ApplicationDeployer( factory.createApplicationDataStore(getEngineSession()), ApplicationPageItem.ATTRIBUTE_APPLICATION_ID)); super.fillDeploys(item, deploys); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/BusinessDataController.java ================================================ /** * Copyright (C) 2025 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.web.rest.server.api.bdm; import static org.bonitasoft.web.rest.server.QueryParameterUtils.parseFilters; import java.io.Serializable; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpSession; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryResult; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController @RequestMapping("/API/bdm/businessData/{className}") public class BusinessDataController extends AbstractRESTController { @GetMapping("/{id}") public String getBusinessData(@PathVariable String className, @PathVariable long id, HttpSession session) throws BonitaException { return getBusinessData(className, id, null, session); } @GetMapping("/{id}/{fieldName}") public String getBusinessData(@PathVariable String className, @PathVariable long id, @PathVariable String fieldName, HttpSession session) throws BonitaException { final Map parameters = new HashMap<>(); parameters.put("entityClassName", className); parameters.put("businessDataId", id); parameters.put("businessDataURIPattern", BusinessDataFieldValue.URI_PATTERN); if (fieldName != null) { parameters.put("businessDataChildName", fieldName); } return (String) getCommandAPI(session).execute("getBusinessDataById", parameters); } @GetMapping("/findByIds") public String getBusinessData(@PathVariable String className, @RequestParam("ids") List ids, HttpSession session) throws BonitaException { final Map parameters = new HashMap<>(); parameters.put("entityClassName", className); parameters.put("businessDataIds", (Serializable) ids); parameters.put("businessDataURIPattern", BusinessDataFieldValue.URI_PATTERN); return (String) getCommandAPI(session).execute("getBusinessDataByIds", parameters); } @GetMapping("") public ResponseEntity getBusinessDataByQuery(@PathVariable String className, @RequestParam("c") int searchPageSize, @RequestParam(value = "f", required = false) List filters, @RequestParam("p") int searchPageNumber, @RequestParam("q") String queryName, HttpSession session) throws BonitaException { final Map parameters = new HashMap<>(); parameters.put("queryName", queryName); parameters.put("queryParameters", (Serializable) parseFilters(filters)); parameters.put("entityClassName", className); parameters.put("startIndex", searchPageNumber * searchPageSize); parameters.put("maxResults", searchPageSize); parameters.put("businessDataURIPattern", BusinessDataFieldValue.URI_PATTERN); if (log.isDebugEnabled()) { log.debug("Executing business Data Query: {}", parameters.get("queryName")); log.debug("entityClassName: {}", parameters.get("entityClassName")); log.debug("queryParameters: {}", parameters.get("queryParameters").toString()); log.debug("startIndex: {}", parameters.get("startIndex")); log.debug("maxResults: {}", parameters.get("maxResults")); } BusinessDataQueryResult businessDataQueryResult = (BusinessDataQueryResult) getCommandAPI(session).execute( "getBusinessDataByQueryCommand", parameters); var responseBody = (String) businessDataQueryResult.getJsonResults(); // Build response with proper headers final var businessDataQueryMetadata = businessDataQueryResult.getBusinessDataQueryMetadata(); Long totalCount = null; if (businessDataQueryMetadata != null) { totalCount = businessDataQueryMetadata.getCount(); } HttpHeaders headers = buildHttpHeaders(searchPageSize, searchPageNumber, totalCount); return ResponseEntity.ok().headers(headers).body(responseBody); } private static HttpHeaders buildHttpHeaders(Integer searchPageSize, Integer searchPageNumber, Long totalCount) { HttpHeaders headers = new HttpHeaders(); if (totalCount != null) { // Format content range header similar to what is produced by APIServletCall.doGet and CommonResource.setContentRange // Our API is not conform to the Content-range header specs String contentRangeValue = searchPageNumber + "-" + searchPageSize + "/" + totalCount; headers.add(HttpHeaders.CONTENT_RANGE, contentRangeValue); } return headers; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/BusinessDataFieldValue.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bdm; /** * @author Laurent Leseigneur */ public interface BusinessDataFieldValue { // used by command. parameter names must not be changed String URI_PATTERN = "/API/bdm/businessData/{className}/{id}/{field}"; } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/BusinessDataModelController.java ================================================ /** * Copyright (C) 2022-2025 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.web.rest.server.api.bdm; import java.io.IOException; import java.io.InputStream; import javax.servlet.http.HttpSession; import org.apache.commons.io.IOUtils; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.api.TenantAdministrationAPI; import org.bonitasoft.engine.business.data.BusinessDataRepositoryDeploymentException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.TenantStatusException; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.web.rest.model.bdm.BusinessDataModelItem; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.bonitasoft.web.rest.server.api.SpringResponseEntityUtils; import org.bonitasoft.web.rest.server.api.tenant.TenantResourceItem; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author Anthony Birembaut * @author Emmanuel Duchastenier */ @RestController @RequestMapping("/API/tenant/bdm") public class BusinessDataModelController extends AbstractRESTController { /** * @deprecated as of 9.0.0. The BDM should only be updated at startup. */ @PostMapping @Deprecated(since = "9.0.0") public TenantResourceItem installOrUpdateBDM(@RequestBody final BusinessDataModelItem businessDataModelItem, HttpSession httpSession) throws BonitaException { final TenantAdministrationAPI tenantAdministrationAPI = getTenantAdministrationAPI(httpSession); if (!tenantAdministrationAPI.isPaused()) { // Exception handling is done in the method right below: throw new TenantStatusException( "Unable to install the Business Data Model. Please pause the BPM Services first. Go to Configuration > BPM Services."); } try { final FileContent businessDataModel = getBusinessDataModel(businessDataModelItem); final byte[] businessDataModelContent = getBusinessDataModelContent(businessDataModel.getInputStream()); tenantAdministrationAPI.updateBusinessDataModel(businessDataModelContent); return new TenantResourceItem(tenantAdministrationAPI.getBusinessDataModelResource(), businessDataModel.getFileName()); } catch (final BusinessDataRepositoryDeploymentException e) { throw new APIException("An error has occurred when deploying Business Data Model.", e); } finally { getBonitaHomeFolderAccessor().removeUploadedTempContent(businessDataModelItem.getFileUpload()); } } // In the particular case of this controller, we treat the TenantStatusException as a forbidden error. // In other controllers, it is treated in SpringRestResponseEntityExceptionHandler as a Service Unavailable error. @ExceptionHandler(value = { APIForbiddenException.class, TenantStatusException.class }) protected ResponseEntity handleAPIForbidden(Exception ex) { return SpringResponseEntityUtils.generateErrorResponse(ex, HttpStatus.FORBIDDEN); } @GetMapping public TenantResourceItem getBDM(HttpSession httpSession) { try { return new TenantResourceItem(getTenantAdministrationAPI(httpSession).getBusinessDataModelResource()); } catch (final TenantStatusException | InvalidSessionException e) { throw e; //handled by REST API Authorization filter } catch (final Exception e) { throw new APIException(e); } } protected FileContent getBusinessDataModel(final BusinessDataModelItem item) { try { return getBonitaHomeFolderAccessor().retrieveUploadedTempContent(item.getFileUpload()); } catch (final BonitaException e) { throw new APIException("Can't read business data model file", e); } } private byte[] getBusinessDataModelContent(InputStream inputStream) { try (inputStream) { return IOUtils.toByteArray(inputStream); } catch (IOException e) { throw new APIException("Can't read business data model file", e); } } protected BonitaHomeFolderAccessor getBonitaHomeFolderAccessor() { return new BonitaHomeFolderAccessor(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/BusinessDataReferenceClient.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bdm; import java.io.Serializable; /** * @author Baptiste Mesta */ public class BusinessDataReferenceClient implements Serializable { private String name; private String type; private String link; public BusinessDataReferenceClient(String name, String type, String link) { this.name = name; this.type = type; this.link = link; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/BusinessDataReferenceController.java ================================================ /** * Copyright (C) 2025 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.web.rest.server.api.bdm; import java.util.List; import javax.servlet.http.HttpSession; import org.bonitasoft.engine.bpm.data.DataNotFoundException; import org.bonitasoft.engine.business.data.BusinessDataReference; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.web.rest.server.QueryParameterUtils; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * Spring MVC controller for BusinessDataReference REST API. *

    * Provides endpoints to retrieve business data references for process instances. *

    * * @author Matthieu Chaffotte * @author Colin Puy */ @RestController @RequestMapping("/API/bdm/businessDataReference") public class BusinessDataReferenceController extends AbstractRESTController { /** * Gets a specific business data reference by case ID and data name. * * @param caseId the process instance ID * @param dataName the name of the business data variable * @param httpSession the HTTP session * @return the business data reference as a client object * @throws DataNotFoundException if the business data reference is not found * @throws BonitaException if an error occurs while accessing the API */ @GetMapping("/{caseId}/{dataName}") public BusinessDataReferenceClient getProcessBusinessDataReference( @PathVariable long caseId, @PathVariable String dataName, HttpSession httpSession) throws DataNotFoundException, BonitaException { BusinessDataReference reference = getBusinessDataAPI(httpSession) .getProcessBusinessDataReference(dataName, caseId); return BusinessDataReferenceConverter.toClient(reference); } /** * Gets all business data references for a process instance with pagination. *

    * Example: * *

         * f=caseId=123&p=0&c=10
         * 
    * *

    * * @param filters standard filter set that MUST contain a filter named caseId and containing the * process instance ID: f=caseId=123 * @param page the page number (0-based) * @param count the number of results per page * @param httpSession the HTTP session * @return a list of business data references as client objects * @throws BonitaException if an error occurs while accessing the API */ @GetMapping public List getProcessBusinessDataReferences( @RequestParam("f") List filters, @RequestParam("p") int page, @RequestParam("c") int count, HttpSession httpSession) throws BonitaException { long caseId = QueryParameterUtils.extractMandatoryLongFilter(filters, "caseId"); List references = getBusinessDataAPI(httpSession) .getProcessBusinessDataReferences(caseId, page * count, count); return references.stream() .map(BusinessDataReferenceConverter::toClient) .toList(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/BusinessDataReferenceConverter.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.bdm; import java.io.Serializable; import org.bonitasoft.engine.business.data.BusinessDataReference; import org.bonitasoft.engine.business.data.MultipleBusinessDataReference; import org.bonitasoft.engine.business.data.SimpleBusinessDataReference; /** * Converts {@link BusinessDataReference} objects to their client representations. * Used by context controllers to transform execution context values before returning them to the client. */ public final class BusinessDataReferenceConverter { private static final String BDM_BUSINESS_DATA_URL = "/bdm/businessData"; private BusinessDataReferenceConverter() { } /** * If the given object is a {@link BusinessDataReference}, converts it to a * {@link BusinessDataReferenceClient}. Otherwise, returns the object unchanged. */ public static Serializable convertIfApplicable(Serializable object) { if (object instanceof BusinessDataReference reference) { return toClient(reference); } return object; } /** * Converts a {@link BusinessDataReference} to its client representation. */ public static BusinessDataReferenceClient toClient(BusinessDataReference reference) { if (reference instanceof SimpleBusinessDataReference simpleReference) { return new SimpleBusinessDataReferenceClient( reference.getName(), reference.getType(), getUrl(reference.getType(), getStorageIdString(simpleReference)), simpleReference.getStorageId()); } else { MultipleBusinessDataReference multipleReference = (MultipleBusinessDataReference) reference; return new MultipleBusinessDataReferenceClient( reference.getName(), reference.getType(), getUrl(multipleReference.getType(), getStorageIdsValue(multipleReference)), multipleReference.getStorageIds()); } } private static String getStorageIdString(SimpleBusinessDataReference reference) { Long storageId = reference.getStorageId(); if (storageId != null) { return storageId.toString(); } return ""; } private static String getStorageIdsValue(MultipleBusinessDataReference reference) { return "findByIds?ids=" + reference.getStorageIds().toString().replaceAll("[\\[\\] ]", ""); } private static String getUrl(String type, String value) { return "API" + BDM_BUSINESS_DATA_URL + "/" + type + "/" + value; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/MultipleBusinessDataReferenceClient.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bdm; import java.util.ArrayList; import java.util.List; import com.fasterxml.jackson.annotation.JsonProperty; /** * @author Baptiste Mesta */ public class MultipleBusinessDataReferenceClient extends BusinessDataReferenceClient { private final List storageIds; @JsonProperty("storageIds_string") private final List storageIdsAsString; public MultipleBusinessDataReferenceClient(String name, String type, String link, List storageIds) { super(name, type, link); this.storageIds = storageIds; storageIdsAsString = new ArrayList<>(); for (Long storageId : storageIds) { storageIdsAsString.add(storageId.toString()); } } public List getStorageIds() { return storageIds; } public List getStorageIdsAsString() { return storageIdsAsString; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bdm/SimpleBusinessDataReferenceClient.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bdm; import com.fasterxml.jackson.annotation.JsonProperty; /** * @author Baptiste Mesta */ public class SimpleBusinessDataReferenceClient extends BusinessDataReferenceClient { /** * UID */ private static final long serialVersionUID = 4377973199157064562L; private Long storageId; @JsonProperty("storageId_string") private String storageIdAsString; public SimpleBusinessDataReferenceClient(final String name, final String type, final String link, final Long storageId) { super(name, type, link); this.storageId = storageId; if (storageId != null) { storageIdAsString = storageId.toString(); } } public Long getStorageId() { return storageId; } public void setStorageId(final Long storageId) { this.storageId = storageId; } public String getStorageIdAsString() { return storageIdAsString; } public void setStorageIdAsString(final String storageIdAsString) { this.storageIdAsString = storageIdAsString; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APIArchivedCase.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.cases; import java.util.List; import java.util.Map; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDefinition; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.bpm.cases.ArchivedCaseDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore; import org.bonitasoft.web.rest.server.datastore.organization.UserDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasDelete; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class APIArchivedCase extends ConsoleAPI implements APIHasGet, APIHasSearch, APIHasDelete { @Override public ItemDefinition defineItemDefinition() { return Definitions.get(ArchivedCaseDefinition.TOKEN); } @Override protected Datastore defineDefaultDatastore() { return new ArchivedCaseDatastore(getEngineSession()); } @Override public ArchivedCaseItem get(final APIID id) { return getArchivedCaseDatastore().get(id); } @Override public String defineDefaultSearchOrder() { return ""; } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { // Check that team manager and supervisor filters are not used together if (filters.containsKey(ArchivedCaseItem.FILTER_TEAM_MANAGER_ID) && filters.containsKey(ArchivedCaseItem.FILTER_SUPERVISOR_ID)) { throw new APIException( "Can't set those filters at the same time : " + ArchivedCaseItem.FILTER_TEAM_MANAGER_ID + " and " + ArchivedCaseItem.FILTER_SUPERVISOR_ID); } return getArchivedCaseDatastore().search(page, resultsByPage, search, orders, filters); } @Override protected void fillDeploys(final ArchivedCaseItem item, final List deploys) { if (isDeployable(ArchivedCaseItem.ATTRIBUTE_STARTED_BY_USER_ID, deploys, item)) { item.setDeploy( ArchivedCaseItem.ATTRIBUTE_STARTED_BY_USER_ID, getUserDatastore().get(item.getStartedByUserId())); } if (isDeployable(ArchivedCaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, deploys, item)) { item.setDeploy( ArchivedCaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, getUserDatastore().get(item.getStartedBySubstituteUserId())); } if (isDeployable(ArchivedCaseItem.ATTRIBUTE_PROCESS_ID, deploys, item)) { item.setDeploy( ArchivedCaseItem.ATTRIBUTE_PROCESS_ID, getProcessDatastore().get(item.getProcessId())); } } /** * @return */ protected ProcessDatastore getProcessDatastore() { return new ProcessDatastore(getEngineSession()); } /** * @return */ protected UserDatastore getUserDatastore() { return new UserDatastore(getEngineSession()); } @Override protected void fillCounters(final ArchivedCaseItem item, final List counters) { } protected ArchivedCaseDatastore getArchivedCaseDatastore() { return new ArchivedCaseDatastore(getEngineSession()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APIArchivedCaseDocument.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.cases; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentDefinition; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.api.deployer.DeployerFactory; import org.bonitasoft.web.rest.server.datastore.bpm.cases.ArchivedCaseDocumentDatastore; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Fabio Lombardi */ public class APIArchivedCaseDocument extends ConsoleAPI { @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(ArchivedCaseDocumentDefinition.TOKEN); } @Override public ArchivedCaseDocumentItem get(final APIID id) { return getArchivedCaseDocumentDatastore().get(id); } @Override public String defineDefaultSearchOrder() { return ""; } @Override protected void fillDeploys(final ArchivedCaseDocumentItem item, final List deploys) { addDeployer(getDeployerFactory().createUserDeployer(ArchivedCaseDocumentItem.ATTRIBUTE_SUBMITTED_BY_USER_ID)); addDeployer(getDeployerFactory().createUserDeployer(ArchivedCaseDocumentItem.ATTRIBUTE_AUTHOR)); super.fillDeploys(item, deploys); } protected DeployerFactory getDeployerFactory() { return new DeployerFactory(getEngineSession()); } @Override protected void fillCounters(final ArchivedCaseDocumentItem item, final List counters) { } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { return getArchivedCaseDocumentDatastore().search(page, resultsByPage, search, filters, orders); } @Override public void delete(final List ids) { getArchivedCaseDocumentDatastore().delete(ids); } protected ArchivedCaseDocumentDatastore getArchivedCaseDocumentDatastore() { ProcessAPI processAPI; try { processAPI = TenantAPIAccessor.getProcessAPI(getEngineSession()); } catch (final BonitaException e) { throw new APIException(e); } final WebBonitaConstantsUtils constants = WebBonitaConstantsUtils.getTenantInstance(); return new ArchivedCaseDocumentDatastore(getEngineSession(), processAPI); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APIArchivedComment.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.cases; import java.util.List; import java.util.Map; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCommentDefinition; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCommentItem; import org.bonitasoft.web.rest.model.bpm.cases.CommentItem; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.bpm.cases.ArchivedCommentDatastore; import org.bonitasoft.web.rest.server.datastore.organization.UserDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Nicolas Tith */ public class APIArchivedComment extends ConsoleAPI implements APIHasSearch { // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CONFIGURE // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(ArchivedCommentDefinition.TOKEN); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // C.R.U.D.S // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { return getDatastore().search(page, resultsByPage, search, orders, filters); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public ArchivedCommentDatastore getDatastore() { return new ArchivedCommentDatastore(getEngineSession()); } @Override public String defineDefaultSearchOrder() { return ""; } @Override protected void fillDeploys(final ArchivedCommentItem item, final List deploys) { if (isDeployable(ArchivedCommentItem.ATTRIBUTE_USER_ID, deploys, item)) { item.setDeploy(ArchivedCommentItem.ATTRIBUTE_USER_ID, new UserDatastore(getEngineSession()).get(item.getUserId())); } else { item.setDeploy(CommentItem.ATTRIBUTE_USER_ID, getSystemUser()); } // TODO: Deploy process instance } private UserItem getSystemUser() { final UserItem systemUser = new UserItem(); systemUser.setUserName("System"); systemUser.setIcon(UserItem.DEFAULT_USER_ICON); return systemUser; } @Override protected void fillCounters(final ArchivedCommentItem item, final List counters) { } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICase.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.cases; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.process.ProcessInstanceCriterion; import org.bonitasoft.web.rest.model.bpm.cases.CaseDefinition; import org.bonitasoft.web.rest.model.bpm.cases.CaseItem; import org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.FlowNodeDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore; import org.bonitasoft.web.rest.server.datastore.organization.UserDatastore; import org.bonitasoft.web.rest.server.framework.api.*; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel * @author Celine Souchet */ public class APICase extends ConsoleAPI implements APIHasUpdate, APIHasGet, APIHasAdd, APIHasSearch, APIHasDelete { @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(CaseDefinition.TOKEN); } @Override public CaseItem add(final CaseItem caseItem) { return getCaseDatastore().add(caseItem); } @Override public CaseItem get(final APIID id) { return getCaseDatastore().get(id); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { // Check that team manager and supervisor filters are not used together if (filters.containsKey(CaseItem.FILTER_TEAM_MANAGER_ID) && filters.containsKey(CaseItem.FILTER_SUPERVISOR_ID)) { throw new APIException( "Can't set those filters at the same time : " + CaseItem.FILTER_TEAM_MANAGER_ID + " and " + CaseItem.FILTER_SUPERVISOR_ID); } return getCaseDatastore().search(page, resultsByPage, search, orders, filters); } @Override public String defineDefaultSearchOrder() { return ProcessInstanceCriterion.CREATION_DATE_DESC.name(); } @Override protected void fillDeploys(final CaseItem item, final List deploys) { fillStartedBy(item, deploys); fillStartedBySubstitute(item, deploys); fillProcess(item, deploys); } private void fillStartedBy(final CaseItem item, final List deploys) { if (isDeployable(CaseItem.ATTRIBUTE_STARTED_BY_USER_ID, deploys, item)) { item.setDeploy( CaseItem.ATTRIBUTE_STARTED_BY_USER_ID, getUserDatastore().get(item.getStartedByUserId())); } } private void fillStartedBySubstitute(final CaseItem item, final List deploys) { if (isDeployable(CaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, deploys, item)) { item.setDeploy( CaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID, getUserDatastore().get(item.getStartedBySubstituteUserId())); } } private void fillProcess(final CaseItem item, final List deploys) { if (isDeployable(CaseItem.ATTRIBUTE_PROCESS_ID, deploys, item)) { item.setDeploy( CaseItem.ATTRIBUTE_PROCESS_ID, getProcessDatastore().get(item.getProcessId())); } } private void fillNumberOfFailedFlowNodesIfFailedCounterExists(final CaseItem item, final List counters, Map filters) { if (counters.contains(CaseItem.COUNTER_FAILED_FLOW_NODES)) { final FlowNodeDatastore flowNodeDatastore = getFlowNodeDatastore(); final Map flowNodeFilters = new HashMap<>(); if ("any".equalsIgnoreCase(filters.get(CaseItem.FILTER_CALLER))) { flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, String.valueOf(item.getId().toLong())); } else { flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, String.valueOf(item.getId().toLong())); } flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_STATE, FlowNodeItem.VALUE_STATE_FAILED); item.setAttribute(CaseItem.COUNTER_FAILED_FLOW_NODES, flowNodeDatastore.count(null, null, flowNodeFilters)); } } private void fillNumberOfActiveFlowNodesIfActiveCounterExists(final CaseItem item, final List counters, Map filters) { if (counters.contains(CaseItem.COUNTER_ACTIVE_FLOW_NODES)) { final FlowNodeDatastore flowNodeDatastore = getFlowNodeDatastore(); final Map flowNodeFilters = new HashMap<>(); if ("any".equalsIgnoreCase(filters.get(CaseItem.FILTER_CALLER))) { flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, String.valueOf(item.getId().toLong())); } else { flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, String.valueOf(item.getId().toLong())); } item.setAttribute(CaseItem.COUNTER_ACTIVE_FLOW_NODES, flowNodeDatastore.count(null, null, flowNodeFilters)); } } private void fillNumberOfPendingFlowNodesIfActiveCounterExists(final CaseItem item, final List counters, Map filters) { if (counters.contains(CaseItem.COUNTER_PENDING_FLOW_NODES)) { final FlowNodeDatastore flowNodeDatastore = getFlowNodeDatastore(); final Map flowNodeFilters = new HashMap<>(); if ("any".equalsIgnoreCase(filters.get(CaseItem.FILTER_CALLER))) { flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, String.valueOf(item.getId().toLong())); } else { flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, String.valueOf(item.getId().toLong())); } flowNodeFilters.put(FlowNodeItem.ATTRIBUTE_STATE, FlowNodeItem.VALUE_STATE_PENDING); item.setAttribute(CaseItem.COUNTER_PENDING_FLOW_NODES, flowNodeDatastore.count(null, null, flowNodeFilters)); } } @Override public void delete(final List ids) { getCaseDatastore().delete(ids); } @Override protected void fillCountersDependingOnFilters(final CaseItem item, final List counters, final Map filters) { fillNumberOfFailedFlowNodesIfFailedCounterExists(item, counters, filters); fillNumberOfActiveFlowNodesIfActiveCounterExists(item, counters, filters); fillNumberOfPendingFlowNodesIfActiveCounterExists(item, counters, filters); } @Override protected void fillCounters(final CaseItem item, final List counters) { fillNumberOfFailedFlowNodesIfFailedCounterExists(item, counters, Map.of(CaseItem.FILTER_CALLER, "any")); fillNumberOfActiveFlowNodesIfActiveCounterExists(item, counters, Map.of(CaseItem.FILTER_CALLER, "any")); fillNumberOfPendingFlowNodesIfActiveCounterExists(item, counters, Map.of(CaseItem.FILTER_CALLER, "any")); } UserDatastore getUserDatastore() { return new UserDatastore(getEngineSession()); } ProcessDatastore getProcessDatastore() { return new ProcessDatastore(getEngineSession()); } FlowNodeDatastore getFlowNodeDatastore() { return new FlowNodeDatastore(getEngineSession()); } protected CaseDatastore getCaseDatastore() { return new CaseDatastore(getEngineSession()); } @Override public CaseItem update(final APIID id, final Map attributes) { return getCaseDatastore().update(id, attributes); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseDocument.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.cases; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentDefinition; import org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.api.deployer.DeployerFactory; import org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDocumentDatastore; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Fabio Lombardi */ public class APICaseDocument extends ConsoleAPI { @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(CaseDocumentDefinition.TOKEN); } @Override public CaseDocumentItem get(final APIID id) { return getCaseDocumentDatastore().get(id); } @Override public CaseDocumentItem add(final CaseDocumentItem item) { return getCaseDocumentDatastore().add(item); } @Override public CaseDocumentItem update(final APIID id, final Map attributes) { return getCaseDocumentDatastore().update(id, attributes); } @Override public String defineDefaultSearchOrder() { return ""; } @Override protected void fillDeploys(final CaseDocumentItem item, final List deploys) { addDeployer(getDeployerFactory().createUserDeployer(CaseDocumentItem.ATTRIBUTE_SUBMITTED_BY_USER_ID)); addDeployer(getDeployerFactory().createUserDeployer(CaseDocumentItem.ATTRIBUTE_AUTHOR)); super.fillDeploys(item, deploys); } protected DeployerFactory getDeployerFactory() { return new DeployerFactory(getEngineSession()); } @Override protected void fillCounters(final CaseDocumentItem item, final List counters) { } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { return getCaseDocumentDatastore().search(page, resultsByPage, search, filters, orders); } @Override public void delete(final List ids) { getCaseDocumentDatastore().delete(ids); } protected CaseDocumentDatastore getCaseDocumentDatastore() { ProcessAPI processAPI; try { processAPI = TenantAPIAccessor.getProcessAPI(getEngineSession()); } catch (final BonitaException e) { throw new APIException(e); } final WebBonitaConstantsUtils constants = WebBonitaConstantsUtils.getTenantInstance(); return new CaseDocumentDatastore(getEngineSession(), processAPI, new BonitaHomeFolderAccessor()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseVariable.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.cases; import java.util.List; import java.util.Map; import org.bonitasoft.web.rest.model.bpm.cases.CaseVariableDefinition; import org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseVariableDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Colin PUY */ public class APICaseVariable extends ConsoleAPI implements APIHasSearch, APIHasUpdate, APIHasGet { private APICaseVariableAttributeChecker attributeChecker = new APICaseVariableAttributeChecker(); @Override public CaseVariableItem runUpdate(APIID id, Map attributes) { attributeChecker.checkUpdateAttributes(attributes); id.setItemDefinition(CaseVariableDefinition.get()); CaseVariableItem item = CaseVariableItem.fromIdAndAttributes(id, attributes); ((CaseVariableDatastore) getDefaultDatastore()).updateVariableValue(item.getCaseId(), item.getName(), item.getType(), item.getValue()); return item; } @Override public ItemSearchResult runSearch(int page, int resultsByPage, String search, String orders, Map filters, List deploys, List counters) { attributeChecker.checkSearchFilters(filters); long caseId = Long.valueOf(filters.get(CaseVariableItem.ATTRIBUTE_CASE_ID)); return ((CaseVariableDatastore) getDefaultDatastore()).findByCaseId(caseId, page, resultsByPage); } @Override public CaseVariableItem runGet(APIID id, List deploys, List counters) { id.setItemDefinition(CaseVariableDefinition.get()); long caseId = id.getPartAsLong(CaseVariableItem.ATTRIBUTE_CASE_ID); String variableName = id.getPart(CaseVariableItem.ATTRIBUTE_NAME); return ((CaseVariableDatastore) getDefaultDatastore()).findById(caseId, variableName); } @Override protected Datastore defineDefaultDatastore() { return new CaseVariableDatastore(getEngineSession()); } @Override protected ItemDefinition defineItemDefinition() { return CaseVariableDefinition.get(); } /* * Only used for tests because we cannot pass anything to constructor due to design restrictions */ public void setAttributeChecker(APICaseVariableAttributeChecker attributeChecker) { this.attributeChecker = attributeChecker; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APICaseVariableAttributeChecker.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.cases; import static java.lang.String.format; import java.util.Map; import org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; /** * @author Colin PUY */ public class APICaseVariableAttributeChecker { public void checkUpdateAttributes(Map attributes) { if (attributes == null || attributes.isEmpty()) { throw new APIException(format("Attributes '%s' and '%s' must be specified", CaseVariableItem.ATTRIBUTE_TYPE, CaseVariableItem.ATTRIBUTE_VALUE)); } if (!attributes.containsKey(CaseVariableItem.ATTRIBUTE_TYPE)) { throw new APIException(format("Attribute '%s' must be specified", CaseVariableItem.ATTRIBUTE_TYPE)); } if (!attributes.containsKey(CaseVariableItem.ATTRIBUTE_VALUE)) { throw new APIException(format("Attribute '%s' must be specified", CaseVariableItem.ATTRIBUTE_VALUE)); } } // filters could be null... public void checkSearchFilters(Map filters) { if (filters == null || !filters.containsKey(CaseVariableItem.ATTRIBUTE_CASE_ID)) { throw new APIException(format("Filter '%s' must be specified", CaseVariableItem.ATTRIBUTE_CASE_ID)); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/APIComment.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.cases; import java.util.List; import java.util.Map; import org.bonitasoft.web.rest.model.bpm.cases.CommentDefinition; import org.bonitasoft.web.rest.model.bpm.cases.CommentItem; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.bpm.cases.CommentDatastore; import org.bonitasoft.web.rest.server.datastore.organization.UserDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasAdd; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Vincent Elcrin */ public class APIComment extends ConsoleAPI implements APIHasAdd, APIHasSearch { // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CONFIGURE // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(CommentDefinition.TOKEN); } @Override public String defineDefaultSearchOrder() { // FIXME Define criterion return ""; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // C.R.U.D.S // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { return new CommentDatastore(getEngineSession()).search(page, resultsByPage, search, orders, filters); } @Override public CommentItem add(final CommentItem comment) { return new CommentDatastore(getEngineSession()).add(comment); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* * (non-Javadoc) * @see org.bonitasoft.web.toolkit.server.API#fillDeploys(org.bonitasoft.web.toolkit.client.data.item.Item, * java.util.List) */ @Override protected void fillDeploys(final CommentItem item, final List deploys) { if (isDeployable(CommentItem.ATTRIBUTE_USER_ID, deploys, item)) { item.setDeploy(CommentItem.ATTRIBUTE_USER_ID, new UserDatastore(getEngineSession()).get(item.getUserId())); } else { item.setDeploy(CommentItem.ATTRIBUTE_USER_ID, getSystemUser()); } // FIXME Deploy process instance } private UserItem getSystemUser() { final UserItem systemUser = new UserItem(); systemUser.setUserName("System"); systemUser.setIcon(UserItem.DEFAULT_USER_ICON); return systemUser; } /* * (non-Javadoc) * @see org.bonitasoft.web.toolkit.server.API#fillCounters(org.bonitasoft.web.toolkit.client.data.item.Item, * java.util.List) */ @Override protected void fillCounters(final CommentItem item, final List counters) { } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/ArchivedCaseContextController.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.bpm.cases; import static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpSession; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.bonitasoft.web.rest.server.api.bdm.BusinessDataReferenceConverter; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * REST API controller for retrieving archived case (process instance) execution context. * Provides access to process variables, business data, and other context elements from archived cases. */ @RestController @RequestMapping("/" + API_SPRING_INTERNAL + "/bpm/archivedCase/{archivedCaseId}/context") public class ArchivedCaseContextController extends AbstractRESTController { @GetMapping public Map getArchivedCaseContext(@PathVariable long archivedCaseId, HttpSession httpSession) throws Exception { Map caseExecutionContext = getProcessAPI(httpSession) .getArchivedProcessInstanceExecutionContext(archivedCaseId); Map resultMap = new HashMap<>(); caseExecutionContext .forEach((key, value) -> resultMap.put(key, BusinessDataReferenceConverter.convertIfApplicable(value))); return resultMap; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/ArchivedCaseVariableController.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.bpm.cases; import static org.bonitasoft.web.rest.server.APIPaginationUtils.buildContentRange; import java.util.List; import javax.servlet.http.HttpSession; import org.bonitasoft.engine.bpm.data.ArchivedDataInstance; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseVariable; import org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem; import org.bonitasoft.web.rest.server.QueryParameterUtils; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/API/bpm/archivedCaseVariable") public class ArchivedCaseVariableController extends AbstractRESTController { @GetMapping("/{caseId}/{variableName}") public ArchivedCaseVariable getArchivedCaseVariable( @PathVariable long caseId, @PathVariable String variableName, HttpSession session) throws Exception { var archivedProcessDataInstance = getProcessAPI(session) .getArchivedProcessDataInstance(variableName, caseId); return ArchivedCaseVariable.create(archivedProcessDataInstance); } @GetMapping public ResponseEntity> getArchivedCaseVariables( @RequestParam("p") int page, @RequestParam("c") int count, @RequestParam(value = "f", required = false) List filters, HttpSession session) throws Exception { long caseId = QueryParameterUtils.extractMandatoryLongFilter(filters, CaseVariableItem.ATTRIBUTE_CASE_ID); List allResults = getProcessAPI(session) .getArchivedProcessDataInstances(caseId, 0, Integer.MAX_VALUE); List pagedResults = allResults.stream() .skip((long) page * count) .limit(count) .map(ArchivedCaseVariable::create) .toList(); return ResponseEntity.ok().header(HttpHeaders.CONTENT_RANGE, buildContentRange(page, count, allResults.size())) .body(pagedResults); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/cases/CaseContextController.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.bpm.cases; import static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpSession; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.bonitasoft.web.rest.server.api.bdm.BusinessDataReferenceConverter; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * REST API controller for retrieving case (process instance) execution context. * Provides access to process variables, business data, and other context elements. */ @RestController @RequestMapping("/" + API_SPRING_INTERNAL + "/bpm/case/{caseId}/context") public class CaseContextController extends AbstractRESTController { @GetMapping public Map getCaseContext(@PathVariable long caseId, HttpSession httpSession) throws Exception { Map resultMap = new HashMap<>(); Map caseExecutionContext = getProcessAPI(httpSession) .getProcessInstanceExecutionContext(caseId); for (Map.Entry entry : caseExecutionContext.entrySet()) { resultMap.put(entry.getKey(), BusinessDataReferenceConverter.convertIfApplicable(entry.getValue())); } return resultMap; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/connector/APIArchivedConnectorInstance.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.connector; import org.bonitasoft.engine.bpm.connector.ConnectorInstanceCriterion; import org.bonitasoft.web.rest.model.bpm.connector.ArchivedConnectorInstanceDefinition; import org.bonitasoft.web.rest.model.bpm.connector.ArchivedConnectorInstanceItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.bpm.connector.ArchivedConnectorInstanceDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Julien Mege */ public class APIArchivedConnectorInstance extends ConsoleAPI implements APIHasSearch, APIHasUpdate { @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(ArchivedConnectorInstanceDefinition.TOKEN); } @Override public String defineDefaultSearchOrder() { return ConnectorInstanceCriterion.NAME_ASC.name(); } @Override protected Datastore defineDefaultDatastore() { return new ArchivedConnectorInstanceDatastore(getEngineSession()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/connector/APIConnectorInstance.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.connector; import org.bonitasoft.engine.bpm.connector.ConnectorInstanceCriterion; import org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceDefinition; import org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.bpm.connector.ConnectorInstanceDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Vincent Elcrin */ public class APIConnectorInstance extends ConsoleAPI implements APIHasSearch, APIHasUpdate { @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(ConnectorInstanceDefinition.TOKEN); } @Override public String defineDefaultSearchOrder() { return ConnectorInstanceCriterion.NAME_ASC.name(); } @Override protected Datastore defineDefaultDatastore() { return new ConnectorInstanceDatastore(getEngineSession()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APIActivity.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem; import org.bonitasoft.web.rest.server.framework.search.ISearchDirection; /** * @author Séverin Moussel */ public class APIActivity extends AbstractAPIActivity { @Override public String defineDefaultSearchOrder() { return ActivityInstanceSearchDescriptor.STATE_NAME + ISearchDirection.SORT_ORDER_ASCENDING; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APIFlowNode.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode; import org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem; /** * @author Séverin Moussel */ public class APIFlowNode extends AbstractAPIFlowNode { } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APIHumanTask.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem; /** * @author Séverin Moussel */ public class APIHumanTask extends AbstractAPIHumanTask { } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APITask.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode; import org.bonitasoft.web.rest.model.bpm.flownode.TaskItem; import org.bonitasoft.web.rest.server.framework.search.ISearchDirection; /** * @author Séverin Moussel */ public class APITask extends AbstractAPITask { @Override protected boolean isAllowedState(final String state) { return TaskItem.VALUE_STATE_REPLAY.equals(state) || super.isAllowedState(state); } @Override public String defineDefaultSearchOrder() { return TaskItem.ATTRIBUTE_LAST_UPDATE_DATE + ISearchDirection.SORT_ORDER_DESCENDING; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/APIUserTask.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode; import org.bonitasoft.web.rest.model.bpm.flownode.UserTaskItem; /** * @author Séverin Moussel */ public class APIUserTask extends AbstractAPIUserTask { } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/AbstractAPIActivity.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode; import java.util.List; import org.bonitasoft.web.rest.model.bpm.flownode.ActivityDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem; import org.bonitasoft.web.rest.model.bpm.flownode.IActivityItem; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.ActivityDatastore; import org.bonitasoft.web.rest.server.framework.api.Datastore; /** * @author Séverin Moussel */ public class AbstractAPIActivity extends AbstractAPIFlowNode { @Override protected ActivityDefinition defineItemDefinition() { return ActivityDefinition.get(); } @Override protected Datastore defineDefaultDatastore() { return new ActivityDatastore(getEngineSession()); } @Override protected List defineReadOnlyAttributes() { final List attributes = super.defineReadOnlyAttributes(); attributes.add(ActivityItem.ATTRIBUTE_REACHED_STATE_DATE); attributes.add(ActivityItem.ATTRIBUTE_LAST_UPDATE_DATE); return attributes; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/AbstractAPIFlowNode.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseItem; import org.bonitasoft.web.rest.model.bpm.cases.CaseItem; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem; import org.bonitasoft.web.rest.model.bpm.flownode.IFlowNodeItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.api.deployer.GenericDeployer; import org.bonitasoft.web.rest.server.datastore.bpm.cases.ArchivedCaseDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.FlowNodeDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.TaskDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.TaskFinder; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedTaskDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.process.ActorDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore; import org.bonitasoft.web.rest.server.datastore.organization.UserDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.rest.server.framework.search.ISearchDirection; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * Contains all the implementation for a APIFlowNode and inherited APIs * * @author Séverin Moussel */ public class AbstractAPIFlowNode extends ConsoleAPI implements APIHasUpdate, APIHasGet, APIHasSearch { @Override protected FlowNodeDefinition defineItemDefinition() { return FlowNodeDefinition.get(); } @Override public String defineDefaultSearchOrder() { return FlowNodeInstanceSearchDescriptor.DISPLAY_NAME + ISearchDirection.SORT_ORDER_ASCENDING; } @Override protected Datastore defineDefaultDatastore() { return new FlowNodeDatastore(getEngineSession()); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { // Check that team manager and supervisor filters are not used together if (filters.containsKey(HumanTaskItem.FILTER_TEAM_MANAGER_ID) && filters.containsKey(HumanTaskItem.FILTER_SUPERVISOR_ID)) { throw new APIException("Can't set those filters at the same time : " + HumanTaskItem.FILTER_TEAM_MANAGER_ID + " and " + HumanTaskItem.FILTER_SUPERVISOR_ID); } return super.search(page, resultsByPage, search, orders, filters); } @Override public ITEM update(final APIID id, final Map attributes) { final String state = attributes.get(FlowNodeItem.ATTRIBUTE_STATE); if (state != null && !isAllowedState(state)) { throw new APIException("Can't update a flow node state to \"" + state + "\""); } return super.update(id, attributes); } protected boolean isAllowedState(final String state) { return FlowNodeItem.VALUE_STATE_READY.equals(state) || FlowNodeItem.VALUE_STATE_SKIPPED.equals(state) || FlowNodeItem.VALUE_STATE_REPLAY.equals(state) || FlowNodeItem.VALUE_STATE_COMPLETED.equals(state); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected void fillDeploys(final ITEM item, final List deploys) { /** TODO Refactor to an oriented object (cf. WEB-1637 ) */ if (isDeployable(FlowNodeItem.ATTRIBUTE_PROCESS_ID, deploys, item)) { item.setDeploy(FlowNodeItem.ATTRIBUTE_PROCESS_ID, new ProcessDatastore(getEngineSession()).get(item.getProcessId())); } if (isDeployable(FlowNodeItem.ATTRIBUTE_CASE_ID, deploys, item) || isDeployable(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, deploys, item)) { final CaseItem openedCaseItem = getCaseDatastore().get(item.getCaseId()); if (openedCaseItem != null) { item.setDeploy(FlowNodeItem.ATTRIBUTE_CASE_ID, openedCaseItem); item.setDeploy(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, openedCaseItem); } else { final ArchivedCaseItem archivedCaseItem = getArchivedCaseDatastore() .getUsingSourceObjectId(item.getCaseId()); item.setDeploy(FlowNodeItem.ATTRIBUTE_CASE_ID, archivedCaseItem); item.setDeploy(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, archivedCaseItem); } } if (isDeployable(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, deploys, item)) { final CaseItem openedParentCaseItem = getCaseDatastore().get(item.getParentCaseId()); if (openedParentCaseItem != null) { item.setDeploy(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, openedParentCaseItem); } else { final ArchivedCaseItem archivedParentCaseItem = getArchivedCaseDatastore() .getUsingSourceObjectId(item.getParentCaseId()); item.setDeploy(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, archivedParentCaseItem); } } if (isDeployable(FlowNodeItem.ATTRIBUTE_ROOT_CONTAINER_ID, deploys, item)) { CaseItem rootContainerCase = getCaseDatastore().get(item .getAttributeValueAsAPIID(HumanTaskItem.ATTRIBUTE_ROOT_CONTAINER_ID)); if (rootContainerCase == null) { rootContainerCase = getArchivedCase(item.getAttributeValue(HumanTaskItem.ATTRIBUTE_ROOT_CONTAINER_ID)); } if (rootContainerCase != null) { item.setDeploy(FlowNodeItem.ATTRIBUTE_ROOT_CONTAINER_ID, new ProcessDatastore(getEngineSession()).get(rootContainerCase.getProcessId())); } } if (isDeployable(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_USER_ID, deploys, item)) { item.setDeploy(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_USER_ID, new UserDatastore(getEngineSession()).get(item.getExecutedByUserId())); } if (isDeployable(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID, deploys, item)) { item.setDeploy(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_SUBSTITUTE_USER_ID, new UserDatastore(getEngineSession()).get(item.getExecutedBySubstituteUserId())); } if (isDeployable(HumanTaskItem.ATTRIBUTE_ACTOR_ID, deploys, item)) { item.setDeploy(HumanTaskItem.ATTRIBUTE_ACTOR_ID, new ActorDatastore(getEngineSession()) .get(item.getAttributeValueAsAPIID(HumanTaskItem.ATTRIBUTE_ACTOR_ID))); } if (isDeployable(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID, deploys, item)) { item.setDeploy(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID, new UserDatastore(getEngineSession()) .get(item.getAttributeValueAsAPIID(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID))); } addDeployer(new GenericDeployer<>(id -> new TaskFinder( new TaskDatastore(getEngineSession()), new ArchivedTaskDatastore(getEngineSession(), ArchivedTaskDefinition.TOKEN)).find(id), HumanTaskItem.ATTRIBUTE_PARENT_TASK_ID)); super.fillDeploys(item, deploys); } protected CaseDatastore getCaseDatastore() { return new CaseDatastore(getEngineSession()); } private CaseItem getArchivedCase(final String id) { final List result = getArchivedCaseDatastore().search( 0, 1, null, null, Collections.singletonMap(ArchivedHumanTaskItem.ATTRIBUTE_SOURCE_OBJECT_ID, id)).getResults(); if (result.size() > 0) { return result.get(0); } return null; } protected ArchivedCaseDatastore getArchivedCaseDatastore() { return new ArchivedCaseDatastore(getEngineSession()); } @Override protected List defineReadOnlyAttributes() { final List attributes = new ArrayList<>(); attributes.add(FlowNodeItem.ATTRIBUTE_CASE_ID); attributes.add(FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID); attributes.add(FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID); attributes.add(FlowNodeItem.ATTRIBUTE_PROCESS_ID); attributes.add(FlowNodeItem.ATTRIBUTE_DESCRIPTION); attributes.add(FlowNodeItem.ATTRIBUTE_NAME); attributes.add(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_USER_ID); attributes.add(FlowNodeItem.ATTRIBUTE_TYPE); attributes.add(FlowNodeItem.ATTRIBUTE_ID); return attributes; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/AbstractAPIHumanTask.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode; import static org.bonitasoft.web.rest.model.bpm.flownode.IFlowNodeItem.VALUE_TYPE_MANUAL_TASK; import java.util.List; import java.util.Map; import org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem; import org.bonitasoft.web.rest.model.bpm.flownode.IHumanTaskItem; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.HumanTaskDatastore; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.rest.server.framework.search.ISearchDirection; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public class AbstractAPIHumanTask extends AbstractAPITask { @Override protected HumanTaskDefinition defineItemDefinition() { return HumanTaskDefinition.get(); } @Override protected Datastore defineDefaultDatastore() { return new HumanTaskDatastore(getEngineSession()); } @Override public String defineDefaultSearchOrder() { return HumanTaskItem.ATTRIBUTE_PRIORITY + ISearchDirection.SORT_ORDER_DESCENDING; } // // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CRUDS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ITEM update(final APIID id, final Map attributes) { // Cant unassigned a manual task final String assignedUserId = attributes.get(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID); if (assignedUserId != null && assignedUserId.length() > 0) { final ITEM humanTask = get(id); if (VALUE_TYPE_MANUAL_TASK.equals(humanTask.getType()) && StringUtil.isBlank(assignedUserId)) { throw new APIForbiddenException("Can't unassigned a manual task."); } if (humanTask.getAttributes().containsKey(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID)) { if (humanTask.getAttributeValue(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID) != null && !humanTask.getAttributeValue(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID) .equals(assignedUserId)) { throw new APIForbiddenException("Can't assign this task because it has already been assigned."); } } } return super.update(id, attributes); } // // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected List defineReadOnlyAttributes() { final List attributes = super.defineReadOnlyAttributes(); attributes.add(HumanTaskItem.ATTRIBUTE_ACTOR_ID); attributes.add(HumanTaskItem.ATTRIBUTE_ASSIGNED_DATE); attributes.remove(FlowNodeItem.ATTRIBUTE_EXECUTED_BY_USER_ID);// allow user execute instead someone else return attributes; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/AbstractAPITask.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode; import org.bonitasoft.web.rest.model.bpm.flownode.ITaskItem; import org.bonitasoft.web.rest.model.bpm.flownode.TaskDefinition; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.TaskDatastore; import org.bonitasoft.web.rest.server.framework.api.Datastore; /** * @author Séverin Moussel */ public class AbstractAPITask extends AbstractAPIActivity { @Override protected TaskDefinition defineItemDefinition() { return TaskDefinition.get(); } @Override protected Datastore defineDefaultDatastore() { return new TaskDatastore(getEngineSession()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/AbstractAPIUserTask.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode; import org.bonitasoft.web.rest.model.bpm.flownode.IUserTaskItem; import org.bonitasoft.web.rest.model.bpm.flownode.UserTaskDefinition; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.UserTaskDatastore; import org.bonitasoft.web.rest.server.framework.api.Datastore; /** * @author Séverin Moussel */ public class AbstractAPIUserTask extends AbstractAPIHumanTask { @Override protected UserTaskDefinition defineItemDefinition() { return UserTaskDefinition.get(); } @Override protected Datastore defineDefaultDatastore() { return new UserTaskDatastore(getEngineSession()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/ActivityVariableController.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.bpm.flownode; import javax.servlet.http.HttpSession; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/API/bpm/activityVariable/{activityId}/{dataName}") public class ActivityVariableController extends AbstractRESTController { @GetMapping public DataInstance getTaskVariable(@PathVariable long activityId, @PathVariable String dataName, HttpSession session) throws Exception { return getProcessAPI(session).getActivityDataInstance(dataName, activityId); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/TimerEventTrigger.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode; public record TimerEventTrigger(Long executionDate) {} ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/TimerEventTriggerController.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.bpm.flownode; import static org.bonitasoft.web.rest.server.APIPaginationUtils.buildContentRange; import java.util.Date; import java.util.List; import javax.servlet.http.HttpSession; import org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstance; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * Spring MVC Controller for Timer Event Triggers. * * @author Emmanuel Duchastenier */ @RestController @RequestMapping("/API/bpm/timerEventTrigger") public class TimerEventTriggerController extends AbstractRESTController { /** * Search timer event triggers for a given case. * * @param caseId the case ID to search timers for * @param page the page number (p parameter) * @param count the number of results per page (c parameter) * @param httpSession the HTTP session * @return the list of timer event triggers with Content-Range header * @throws BonitaException if an error occurs */ @GetMapping public ResponseEntity> searchTimerEventTriggers( @RequestParam(value = "caseId") long caseId, @RequestParam(value = "p", defaultValue = "0") int page, @RequestParam(value = "c", defaultValue = "10") int count, HttpSession httpSession) throws BonitaException { final SearchResult searchResult = getProcessAPI(httpSession) .searchTimerEventTriggerInstances(caseId, new SearchOptionsBuilder(page * count, count).done()); final List results = searchResult.getResult(); // Return 204 No Content for empty results if (results.isEmpty()) { return ResponseEntity.noContent() .header(HttpHeaders.CONTENT_RANGE, buildContentRange(0, 0, 0)) .build(); } return ResponseEntity.ok() .header(HttpHeaders.CONTENT_RANGE, buildContentRange(page, results.size(), searchResult.getCount())) .body(results); } /** * Update the execution date of a timer event trigger. * * @param id the timer event trigger instance ID * @param trigger the timer event trigger data containing the new execution date * @param httpSession the HTTP session * @return the updated timer event trigger * @throws BonitaException if an error occurs */ @PutMapping("/{id}") public TimerEventTrigger updateTimerEventTrigger(@PathVariable long id, @RequestBody TimerEventTrigger trigger, HttpSession httpSession) throws BonitaException { final Date executionDate = new Date(trigger.executionDate()); final Date updatedDate = getProcessAPI(httpSession).updateExecutionDateOfTimerEventTriggerInstance(id, executionDate); return new TimerEventTrigger(updatedDate.getTime()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/UserTaskContextController.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.bpm.flownode; import static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpSession; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.bonitasoft.web.rest.server.api.bdm.BusinessDataReferenceConverter; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/" + API_SPRING_INTERNAL + "/bpm/userTask/{taskId}/context") public class UserTaskContextController extends AbstractRESTController { @GetMapping public Map getUserTaskContext(@PathVariable long taskId, HttpSession session) throws Exception { final Map resultMap = new HashMap<>(); Map userTaskExecutionContext = getProcessAPI(session).getUserTaskExecutionContext(taskId); for (Map.Entry entry : userTaskExecutionContext.entrySet()) { resultMap.put(entry.getKey(), BusinessDataReferenceConverter.convertIfApplicable(entry.getValue())); } return resultMap; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/UserTaskContractController.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.bpm.flownode; import static org.bonitasoft.console.common.server.utils.ContractTypeConverter.ISO_8601_DATE_PATTERNS; import static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL; import javax.servlet.http.HttpSession; import org.bonitasoft.console.common.server.utils.ContractTypeConverter; import org.bonitasoft.engine.bpm.contract.ContractDefinition; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/" + API_SPRING_INTERNAL + "/bpm/userTask/{taskId}/contract") public class UserTaskContractController extends AbstractRESTController { protected final ContractTypeConverter typeConverterUtil = new ContractTypeConverter(ISO_8601_DATE_PATTERNS); @GetMapping public ResponseEntity getContract(@PathVariable final long taskId, HttpSession session) throws BonitaException { ContractDefinition contract = getProcessAPI(session).getUserTaskContract(taskId); ContractDefinition adaptedContract = typeConverterUtil.getAdaptedContractDefinition(contract); if (adaptedContract == null) { return ResponseEntity.noContent().build(); } return ResponseEntity.ok(adaptedContract); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/UserTaskExecutionController.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.bpm.flownode; import static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL; import java.io.Serializable; import java.util.Map; import javax.servlet.http.HttpSession; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory; import org.bonitasoft.console.common.server.utils.ContractTypeConverter; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.contract.ContractDefinition; import org.bonitasoft.engine.bpm.contract.ContractViolationException; import org.bonitasoft.engine.bpm.flownode.FlowNodeExecutionException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.bonitasoft.web.rest.server.api.resource.ErrorMessageWithExplanations; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; /** * REST controller for user task execution. * Public URL: /API/bpm/userTask/{taskId}/execution * Internal path (via URL rewrite): /APISpringInternal/bpm/userTask/{taskId}/execution * * @author Emmanuel Duchastenier * @author Fabio Lombardi */ @Slf4j @RestController @RequestMapping("/" + API_SPRING_INTERNAL + "/bpm/userTask/{taskId}/execution") public class UserTaskExecutionController extends AbstractRESTController { protected ContractTypeConverter typeConverterUtil = new ContractTypeConverter( ContractTypeConverter.ISO_8601_DATE_PATTERNS); /** * Executes a user task with contract inputs. * * @param taskId the ID of the user task to execute * @param userId optional user ID to execute the task for (defaults to session user) * @param assign whether to assign the task before execution * @param inputs contract inputs for the task * @param httpSession the HTTP session * @throws BonitaException if an error occurs during execution */ @PostMapping @ResponseStatus(HttpStatus.NO_CONTENT) public void executeTask( @PathVariable final long taskId, @RequestParam(name = "user", required = false) final Long userId, @RequestParam(name = "assign", defaultValue = "false") final boolean assign, @RequestBody(required = false) final Map inputs, final HttpSession httpSession) throws BonitaException, java.io.FileNotFoundException { final ProcessAPI processAPI = getProcessAPI(httpSession); final long effectiveUserId = userId != null ? userId : getApiSession(httpSession).getUserId(); try { final ContractDefinition taskContract = processAPI.getUserTaskContract(taskId); final Map processedInputs = typeConverterUtil.getProcessedInput(taskContract, inputs, getMaxSize()); if (assign) { processAPI.assignAndExecuteUserTask(effectiveUserId, taskId, processedInputs); } else { processAPI.executeUserTask(effectiveUserId, taskId, processedInputs); } typeConverterUtil.deleteTemporaryFiles(inputs); } catch (final FlowNodeExecutionException e) { String errorMessage = "Unable to execute the task with ID " + taskId; log.error("{}. Caused by: {}", errorMessage, e.getMessage()); // Avoid throwing original exception that may contain sensitive information unwanted in the HTTP response throw new FlowNodeExecutionException(errorMessage + " (consult the logs for more information)."); } catch (final ContractViolationException e) { logContractViolation(e); throw e; } } // Visible for testing protected long getMaxSize() { return PropertiesFactory.getConsoleProperties().getMaxSize(); } private void logContractViolation(final ContractViolationException e) { if (log.isInfoEnabled()) { final StringBuilder explanations = new StringBuilder(); for (final String explanation : e.getExplanations()) { explanations.append(explanation).append("\n"); } log.info("{}\nExplanations:\n{}", e.getSimpleMessage(), explanations); } } @ExceptionHandler(ContractViolationException.class) public ResponseEntity handleContractViolation(ContractViolationException e) { final ErrorMessageWithExplanations errorMessage = new ErrorMessageWithExplanations(e); errorMessage.setMessage(e.getSimpleMessage()); errorMessage.setExplanations(e.getExplanations()); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorMessage); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedActivity.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode.archive; import java.util.List; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem; import org.bonitasoft.web.rest.server.api.bpm.flownode.AbstractAPIActivity; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedActivityDatastore; import org.bonitasoft.web.rest.server.framework.search.ISearchDirection; /** * @author Séverin Moussel */ public class APIArchivedActivity extends AbstractAPIActivity { @Override protected ArchivedActivityDefinition defineItemDefinition() { return new ArchivedActivityDefinition(); } @Override protected ArchivedActivityDatastore defineDefaultDatastore() { return new ArchivedActivityDatastore(getEngineSession(), ArchivedActivityDefinition.TOKEN); } @Override protected List defineReadOnlyAttributes() { final List attributes = super.defineReadOnlyAttributes(); attributes.add(ArchivedActivityItem.ATTRIBUTE_ARCHIVED_DATE); return attributes; } @Override public String defineDefaultSearchOrder() { return ArchivedActivityItem.ATTRIBUTE_REACHED_STATE_DATE + ISearchDirection.SORT_ORDER_ASCENDING; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedFlowNode.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode.archive; import java.util.List; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem; import org.bonitasoft.web.rest.server.api.bpm.flownode.AbstractAPIFlowNode; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedFlowNodeDatastore; /** * @author Séverin Moussel */ public class APIArchivedFlowNode extends AbstractAPIFlowNode { @Override protected ArchivedFlowNodeDefinition defineItemDefinition() { return new ArchivedFlowNodeDefinition(); } @Override protected ArchivedFlowNodeDatastore defineDefaultDatastore() { return new ArchivedFlowNodeDatastore(getEngineSession(), ArchivedFlowNodeDefinition.TOKEN); } @Override protected List defineReadOnlyAttributes() { final List attributes = super.defineReadOnlyAttributes(); attributes.add(ArchivedFlowNodeItem.ATTRIBUTE_ARCHIVED_DATE); return attributes; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedHumanTask.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode.archive; import java.util.List; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem; import org.bonitasoft.web.rest.server.api.bpm.flownode.AbstractAPIHumanTask; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedHumanTaskDatastore; /** * @author Séverin Moussel */ public class APIArchivedHumanTask extends AbstractAPIHumanTask { @Override protected ArchivedHumanTaskDefinition defineItemDefinition() { return new ArchivedHumanTaskDefinition(); } @Override protected ArchivedHumanTaskDatastore defineDefaultDatastore() { return new ArchivedHumanTaskDatastore(getEngineSession(), ArchivedHumanTaskDefinition.TOKEN); } @Override protected List defineReadOnlyAttributes() { final List attributes = super.defineReadOnlyAttributes(); attributes.add(ArchivedHumanTaskItem.ATTRIBUTE_ARCHIVED_DATE); return attributes; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedTask.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode.archive; import java.util.List; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskItem; import org.bonitasoft.web.rest.server.api.bpm.flownode.AbstractAPITask; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedTaskDatastore; /** * @author Séverin Moussel */ public class APIArchivedTask extends AbstractAPITask { @Override protected ArchivedTaskDefinition defineItemDefinition() { return new ArchivedTaskDefinition(); } @Override protected ArchivedTaskDatastore defineDefaultDatastore() { return new ArchivedTaskDatastore(getEngineSession(), ArchivedTaskDefinition.TOKEN); } @Override protected List defineReadOnlyAttributes() { final List attributes = super.defineReadOnlyAttributes(); attributes.add(ArchivedTaskItem.ATTRIBUTE_ARCHIVED_DATE); return attributes; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/APIArchivedUserTask.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.flownode.archive; import java.util.List; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedUserTaskDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedUserTaskItem; import org.bonitasoft.web.rest.server.api.bpm.flownode.AbstractAPIUserTask; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedUserTaskDatastore; /** * @author Séverin Moussel */ public class APIArchivedUserTask extends AbstractAPIUserTask { @Override protected ArchivedUserTaskDefinition defineItemDefinition() { return new ArchivedUserTaskDefinition(); } @Override protected ArchivedUserTaskDatastore defineDefaultDatastore() { return new ArchivedUserTaskDatastore(getEngineSession(), ArchivedUserTaskDefinition.TOKEN); } @Override protected List defineReadOnlyAttributes() { final List attributes = super.defineReadOnlyAttributes(); attributes.add(ArchivedUserTaskItem.ATTRIBUTE_ARCHIVED_DATE); return attributes; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/ArchivedActivityVariableController.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.bpm.flownode.archive; import javax.servlet.http.HttpSession; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedActivityVariable; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/API/bpm/archivedActivityVariable/{activityId}/{variableName}") public class ArchivedActivityVariableController extends AbstractRESTController { @GetMapping public ArchivedActivityVariable getArchivedActivityVariable(@PathVariable long activityId, @PathVariable String variableName, HttpSession session) throws Exception { return ArchivedActivityVariable .create(getProcessAPI(session).getArchivedActivityDataInstance(variableName, activityId)); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/flownode/archive/ArchivedUserTaskContextController.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.bpm.flownode.archive; import static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL; import java.io.Serializable; import java.util.Map; import javax.servlet.http.HttpSession; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/" + API_SPRING_INTERNAL + "/bpm/archivedUserTask/{archivedTaskId}/context") public class ArchivedUserTaskContextController extends AbstractRESTController { @GetMapping public Map getArchivedUserTaskContext(@PathVariable long archivedTaskId, HttpSession session) throws Exception { return getProcessAPI(session).getArchivedUserTaskExecutionContext(archivedTaskId); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/message/BPMMessage.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.message; import java.util.Map; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public record BPMMessage( String messageName, String targetProcess, String targetFlowNode, Map messageContent, Map correlations) { } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/message/BPMMessageController.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.bpm.message; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.format.DateTimeParseException; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.servlet.http.HttpSession; import org.bonitasoft.engine.bpm.flownode.SendEventException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.expression.Expression; import org.bonitasoft.engine.expression.ExpressionBuilder; import org.bonitasoft.engine.expression.ExpressionType; import org.bonitasoft.engine.expression.InvalidExpressionException; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/API/bpm/message") public class BPMMessageController extends AbstractRESTController { private static final Set SUPPORTED_TYPES = new HashSet<>(); static { SUPPORTED_TYPES.add(String.class.getName()); SUPPORTED_TYPES.add(Integer.class.getName()); SUPPORTED_TYPES.add(Long.class.getName()); SUPPORTED_TYPES.add(Float.class.getName()); SUPPORTED_TYPES.add(Double.class.getName()); SUPPORTED_TYPES.add(Boolean.class.getName()); SUPPORTED_TYPES.add(Date.class.getName()); SUPPORTED_TYPES.add(LocalDate.class.getName()); SUPPORTED_TYPES.add(LocalDateTime.class.getName()); SUPPORTED_TYPES.add(OffsetDateTime.class.getName()); } @PostMapping @ResponseStatus(HttpStatus.NO_CONTENT) public void sendMessage(@RequestBody BPMMessage message, HttpSession httpSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, SendEventException, InvalidExpressionException { validateMandatoryAttributes(message); Map msgContent = new HashMap<>(); if (message.messageContent() != null) { for (Map.Entry entry : message.messageContent().entrySet()) { msgContent.put(new ExpressionBuilder().createConstantStringExpression(entry.getKey()), getExpressionFromObject(entry)); } } Map correlations = new HashMap<>(); if (message.correlations() != null) { int nbCorrelations = message.correlations().size(); if (nbCorrelations > 5) { throw new IllegalArgumentException( String.format("A maximum of 5 correlations is supported. %s found.", nbCorrelations)); } for (Map.Entry entry : message.correlations().entrySet()) { correlations.put(new ExpressionBuilder().createConstantStringExpression(entry.getKey()), getExpressionFromObject(entry)); } } Expression targetFlowNodeExpression = null; if (message.targetFlowNode() != null) { targetFlowNodeExpression = new ExpressionBuilder() .createConstantStringExpression(message.targetFlowNode()); } getProcessAPI(httpSession).sendMessage(message.messageName(), new ExpressionBuilder().createConstantStringExpression(message.targetProcess()), targetFlowNodeExpression, msgContent, correlations); } private Expression getExpressionFromObject(Entry entry) throws InvalidExpressionException { BPMMessageValue messageValue = entry.getValue(); if (messageValue == null) { throw new IllegalArgumentException(String.format("%s value cannot be null.", entry.getKey())); } Object value = messageValue.getValue(); if (value == null) { throw new IllegalArgumentException(String.format("%s value cannot be null.", entry.getKey())); } String type = valueType(messageValue.getType(), value); if (!isSupportedType(type)) { throw new InvalidExpressionException( String.format( "BPM send message: unsupported value type '%s' for key '%s'. Only primitive types are supported.", messageValue.getType(), entry.getKey())); } String stringValue = String.valueOf(value); String expressionName = stringValue.trim().isEmpty() ? "empty-value" : stringValue; return new ExpressionBuilder().createExpression(expressionName, stringValue, valueType(type, value), ExpressionType.TYPE_CONSTANT); } private String valueType(String type, Object value) { if (type != null) { return type; } return guessType(value); } private String guessType(Object value) { if (value instanceof String) { try { LocalDate.parse((String) value); return LocalDate.class.getName(); } catch (DateTimeParseException e) { //Ignore } try { LocalDateTime.parse((String) value); return LocalDateTime.class.getName(); } catch (DateTimeParseException e) { //Ignore } try { OffsetDateTime.parse((String) value); return OffsetDateTime.class.getName(); } catch (DateTimeParseException e) { //Ignore } return String.class.getName(); } else if (value instanceof Long) { return Long.class.getName(); } else if (value instanceof Double) { return Double.class.getName(); } else if (value instanceof Float) { return Float.class.getName(); } else if (value instanceof Integer) { return Integer.class.getName(); } else if (value instanceof Boolean) { return Boolean.class.getName(); } return null; } private boolean isSupportedType(String type) { return SUPPORTED_TYPES.contains(type); } private void validateMandatoryAttributes(BPMMessage message) { if (message.messageName() == null) { throw new IllegalArgumentException("'messageName' attribute is mandatory"); } if (message.targetProcess() == null) { throw new IllegalArgumentException("'targetProcess' attribute is mandatory"); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/message/BPMMessageValue.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.message; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; public class BPMMessageValue { @JsonInclude(Include.NON_EMPTY) private String type; private Object value; public String getType() { return type; } public void setType(String type) { this.type = type; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } public static BPMMessageValue create(Object value, String type) { BPMMessageValue res = new BPMMessageValue(); res.setValue(value); res.setType(type); return res; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIActor.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import static org.bonitasoft.web.rest.model.bpm.process.ActorItem.ATTRIBUTE_PROCESS_ID; import static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName.ATTRIBUTE_NAME; import java.util.Arrays; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.actor.ActorCriterion; import org.bonitasoft.web.rest.model.bpm.process.ActorDefinition; import org.bonitasoft.web.rest.model.bpm.process.ActorItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.bpm.process.ActorDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Julien Mege * @author Séverin Moussel */ public class APIActor extends ConsoleAPI implements APIHasGet, APIHasSearch, APIHasUpdate { @Override protected ItemDefinition defineItemDefinition() { return ActorDefinition.get(); } @Override protected Datastore defineDefaultDatastore() { return new ActorDatastore(getEngineSession()); } @Override protected List defineReadOnlyAttributes() { return Arrays.asList(ATTRIBUTE_PROCESS_ID, ATTRIBUTE_NAME); } @Override public String defineDefaultSearchOrder() { return ActorCriterion.NAME_ASC.name(); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { // Process id is a mandatory filter if (MapUtil.isBlank(filters, ActorItem.ATTRIBUTE_PROCESS_ID)) { throw new APIFilterMandatoryException(ActorItem.ATTRIBUTE_PROCESS_ID); } return super.search(page, resultsByPage, search, orders, filters); } @Override protected void fillDeploys(final ActorItem item, final List deploys) { if (isDeployable(ATTRIBUTE_PROCESS_ID, deploys, item)) { item.setDeploy(ATTRIBUTE_PROCESS_ID, new ProcessDatastore(getEngineSession()).get(item.getProcessId())); } } @Override protected void fillCounters(final ActorItem item, final List counters) { final ActorDatastore actorDatastore = (ActorDatastore) getDefaultDatastore(); if (counters.contains(ActorItem.COUNTER_USERS)) { item.setAttribute(ActorItem.COUNTER_USERS, actorDatastore.countUsers(item.getId())); } if (counters.contains(ActorItem.COUNTER_GROUPS)) { item.setAttribute(ActorItem.COUNTER_GROUPS, actorDatastore.countGroups(item.getId())); } if (counters.contains(ActorItem.COUNTER_ROLES)) { item.setAttribute(ActorItem.COUNTER_ROLES, actorDatastore.countRoles(item.getId())); } if (counters.contains(ActorItem.COUNTER_MEMBERSHIPS)) { item.setAttribute(ActorItem.COUNTER_MEMBERSHIPS, actorDatastore.countMemberships(item.getId())); } } /** * @deprecated as of 9.0.0, Actor should be updated at startup. */ @Override @Deprecated(since = "9.0.0") public ActorItem update(APIID id, Map attributes) { return super.update(id, attributes); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIActorMember.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import static org.bonitasoft.web.rest.model.bpm.process.ActorMemberItem.ATTRIBUTE_ACTOR_ID; import static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.FILTER_MEMBER_TYPE; import java.util.List; import java.util.Map; import org.bonitasoft.web.rest.model.bpm.process.ActorMemberDefinition; import org.bonitasoft.web.rest.model.bpm.process.ActorMemberItem; import org.bonitasoft.web.rest.server.api.profile.AbstractAPIMember; import org.bonitasoft.web.rest.server.datastore.bpm.process.ActorDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.process.ActorMemberDatastore; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.rest.server.framework.exception.APIFilterEmptyException; import org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Julien Mege * @author Séverin Moussel */ public class APIActorMember extends AbstractAPIMember { @Override protected ItemDefinition defineItemDefinition() { return ActorMemberDefinition.get(); } @Override public String defineDefaultSearchOrder() { return ""; } @Override protected Datastore defineDefaultDatastore() { return new ActorMemberDatastore(getEngineSession()); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { if (MapUtil.isBlank(filters, ATTRIBUTE_ACTOR_ID)) { throw new APIFilterMandatoryException(ATTRIBUTE_ACTOR_ID); } if (filters.containsKey(FILTER_MEMBER_TYPE) && MapUtil.isBlank(filters, FILTER_MEMBER_TYPE)) { throw new APIFilterEmptyException(FILTER_MEMBER_TYPE); } return super.search(page, resultsByPage, search, orders, filters); } @Override protected void fillDeploys(final ActorMemberItem item, final List deploys) { if (isDeployable(ActorMemberItem.ATTRIBUTE_ACTOR_ID, deploys, item)) { item.setDeploy(ActorMemberItem.ATTRIBUTE_ACTOR_ID, new ActorDatastore(getEngineSession()).get(item.getActorId())); } super.fillDeploys(item, deploys); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APICategory.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.category.CategoryCriterion; import org.bonitasoft.web.rest.model.bpm.process.CategoryDefinition; import org.bonitasoft.web.rest.model.bpm.process.CategoryItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.bpm.process.CategoryDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasAdd; import org.bonitasoft.web.rest.server.framework.api.APIHasDelete; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Nicolas Tith */ public class APICategory extends ConsoleAPI implements APIHasAdd, APIHasDelete, APIHasGet, APIHasSearch, APIHasUpdate { // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CONFIGURE // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(CategoryDefinition.TOKEN); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // C.R.U.D.S // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public CategoryItem add(final CategoryItem item) { return new CategoryDatastore(getEngineSession()).add(item); } @Override public CategoryItem update(final APIID id, final Map attributes) { return new CategoryDatastore(getEngineSession()).update(id, attributes); } @Override public CategoryItem get(final APIID id) { return new CategoryDatastore(getEngineSession()).get(id); } @Override public String defineDefaultSearchOrder() { return CategoryCriterion.NAME_ASC.toString(); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { return new CategoryDatastore(getEngineSession()).search(page, resultsByPage, search, orders, filters); } @Override public void delete(final List ids) { new CategoryDatastore(getEngineSession()).delete(ids); } @Override protected void fillDeploys(final CategoryItem item, final List deploys) { } @Override protected void fillCounters(final CategoryItem item, final List counters) { } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcess.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.web.rest.model.bpm.cases.CaseItem; import org.bonitasoft.web.rest.model.bpm.process.ProcessDefinition; import org.bonitasoft.web.rest.model.bpm.process.ProcessItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.api.deployer.UserDeployer; import org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore; import org.bonitasoft.web.rest.server.datastore.organization.UserDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasAdd; import org.bonitasoft.web.rest.server.framework.api.APIHasDelete; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Nicolas Tith * @author Celine Souchet */ public class APIProcess extends ConsoleAPI implements APIHasAdd, APIHasUpdate, APIHasGet, APIHasSearch, APIHasDelete { // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CONFIGURE // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected ItemDefinition defineItemDefinition() { return ProcessDefinition.get(); } @Override public String defineDefaultSearchOrder() { return ProcessItem.ATTRIBUTE_NAME + " ASC"; } @Override protected Datastore defineDefaultDatastore() { return new ProcessDatastore(getEngineSession()); } /** * @deprecated as of 9.0.0, Process should be created at startup. */ @Override @Deprecated(since = "9.0.0") public ProcessItem add(final ProcessItem item) { return getProcessDatastore().add(item); } /** * @deprecated as of 9.0.0, Process should be updated at startup. */ @Override @Deprecated(since = "9.0.0") public ProcessItem update(final APIID id, final Map attributes) { return getProcessDatastore().update(id, attributes); } @Override public ProcessItem get(final APIID id) { return getProcessDatastore().get(id); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { if (filters.containsKey(ProcessItem.FILTER_TEAM_MANAGER_ID) && filters.containsKey(ProcessItem.FILTER_SUPERVISOR_ID)) { throw new APIException( "Can't set those filters at the same time : " + ProcessItem.FILTER_TEAM_MANAGER_ID + " and " + ProcessItem.FILTER_SUPERVISOR_ID); } return getProcessDatastore().search(page, resultsByPage, search, orders, filters); } @Override public void delete(final List ids) { getProcessDatastore().delete(ids); } @Override protected void fillDeploys(final ProcessItem item, final List deploys) { addDeployer(new UserDeployer( new UserDatastore(getEngineSession()), ProcessItem.ATTRIBUTE_DEPLOYED_BY_USER_ID)); super.fillDeploys(item, deploys); } @Override protected void fillCounters(final ProcessItem item, final List counters) { fillNumberOfFailedCasesIfFailedCounterExists(item, counters); fillNumberOfOpenCasesIfOpenCounterExists(item, counters); } private void fillNumberOfFailedCasesIfFailedCounterExists(final ProcessItem item, final List counters) { if (counters.contains(ProcessItem.COUNTER_FAILED_CASES)) { final Map filters = new HashMap<>(); filters.put(CaseItem.FILTER_CALLER, "any"); filters.put(CaseItem.ATTRIBUTE_PROCESS_ID, item.getId().toString()); filters.put(CaseItem.FILTER_STATE, ProcessInstanceState.ERROR.name()); item.setAttribute(ProcessItem.COUNTER_FAILED_CASES, getCaseDatastore().count(null, null, filters)); } } private void fillNumberOfOpenCasesIfOpenCounterExists(final ProcessItem item, final List counters) { if (counters.contains(ProcessItem.COUNTER_OPEN_CASES)) { // Open is all states without the terminal states final Map filters = new HashMap<>(); filters.put(CaseItem.FILTER_CALLER, "any"); filters.put(CaseItem.ATTRIBUTE_PROCESS_ID, item.getId().toString()); item.setAttribute(ProcessItem.COUNTER_OPEN_CASES, getCaseDatastore().count(null, null, filters)); } } protected ProcessDatastore getProcessDatastore() { return new ProcessDatastore(getEngineSession()); } protected CaseDatastore getCaseDatastore() { return new CaseDatastore(getEngineSession()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessCategory.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import org.bonitasoft.web.rest.model.bpm.process.ProcessCategoryDefinition; import org.bonitasoft.web.rest.model.bpm.process.ProcessCategoryItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessCategoryDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasAdd; import org.bonitasoft.web.rest.server.framework.api.APIHasDelete; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class APIProcessCategory extends ConsoleAPI implements APIHasAdd, APIHasDelete { @Override protected ItemDefinition defineItemDefinition() { return ProcessCategoryDefinition.get(); } @Override protected Datastore defineDefaultDatastore() { return new ProcessCategoryDatastore(getEngineSession()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessConnector.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem.ATTRIBUTE_PROCESS_ID; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.connector.ConnectorCriterion; import org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDefinition; import org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessConnectorDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class APIProcessConnector extends ConsoleAPI implements APIHasGet, APIHasSearch { @Override protected ItemDefinition defineItemDefinition() { return ProcessConnectorDefinition.get(); } @Override protected Datastore defineDefaultDatastore() { return new ProcessConnectorDatastore(getEngineSession()); } @Override public String defineDefaultSearchOrder() { return ConnectorCriterion.DEFINITION_ID_ASC.name(); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { if (MapUtil.isBlank(filters, ATTRIBUTE_PROCESS_ID)) { throw new APIFilterMandatoryException(ATTRIBUTE_PROCESS_ID); } return super.search(page, resultsByPage, search, orders, filters); } @Override protected void fillDeploys(final ProcessConnectorItem item, final List deploys) { if (isDeployable(ATTRIBUTE_PROCESS_ID, deploys, item)) { item.setDeploy(ATTRIBUTE_PROCESS_ID, new ProcessDatastore(getEngineSession()).get(item.getProcessId())); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessConnectorDependency.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_NAME; import static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_VERSION; import static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_PROCESS_ID; import java.util.List; import java.util.Map; import org.bonitasoft.engine.bpm.connector.ConnectorCriterion; import org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyDefinition; import org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessConnectorDependencyDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; /** * @author Séverin Moussel */ public class APIProcessConnectorDependency extends ConsoleAPI implements APIHasSearch { @Override protected ProcessConnectorDependencyDefinition defineItemDefinition() { return ProcessConnectorDependencyDefinition.get(); } @Override public String defineDefaultSearchOrder() { return ConnectorCriterion.DEFINITION_ID_ASC.name(); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { checkMandatoryAttributes(filters); return super.search(page, resultsByPage, search, orders, filters); } protected void checkMandatoryAttributes(final Map filters) { if (MapUtil.isBlank(filters, ATTRIBUTE_PROCESS_ID)) { throw new APIFilterMandatoryException(ATTRIBUTE_PROCESS_ID); } if (MapUtil.isBlank(filters, ATTRIBUTE_CONNECTOR_NAME)) { throw new APIFilterMandatoryException(ATTRIBUTE_CONNECTOR_NAME); } if (MapUtil.isBlank(filters, ATTRIBUTE_CONNECTOR_VERSION)) { throw new APIFilterMandatoryException(ATTRIBUTE_CONNECTOR_VERSION); } } @Override protected ProcessConnectorDependencyDatastore defineDefaultDatastore() { return new ProcessConnectorDependencyDatastore(getEngineSession()); } @Override protected void fillDeploys(final ProcessConnectorDependencyItem item, final List deploys) { if (isDeployable(ATTRIBUTE_PROCESS_ID, deploys, item)) { item.setDeploy(ATTRIBUTE_PROCESS_ID, new ProcessDatastore(getEngineSession()).get(item.getProcessId())); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessParameter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import static org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil.computeIndex; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.parameter.ParameterCriterion; import org.bonitasoft.engine.bpm.parameter.ParameterInstance; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.web.rest.model.bpm.process.ProcessParameterDefinition; import org.bonitasoft.web.rest.model.bpm.process.ProcessParameterItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Nicolas Tith */ public class APIProcessParameter extends ConsoleAPI { @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(ProcessParameterDefinition.TOKEN); } protected ProcessAPI getProcessAPI() throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getProcessAPI(getEngineSession()); } @Override /* * id is a composite id which contain processId and parameterName */ public ProcessParameterItem get(final APIID id) { final List ids = id.getIds(); final String processId = ids.get(0); final String parameterName = ids.get(1); try { final ProcessAPI processAPI = getProcessAPI(); final Long lProcessid = Long.valueOf(processId); final ParameterInstance parameterInstance = processAPI.getParameterInstance(lProcessid, parameterName); final ProcessDefinition processDef = processAPI.getProcessDefinition(lProcessid); final ProcessDeploymentInfo processDeploy = processAPI.getProcessDeploymentInfo(lProcessid); if (parameterInstance == null) { throw new APIItemNotFoundException("parameter", APIID.makeAPIID(String.valueOf(lProcessid), parameterName)); } if (processDef == null) { throw new APIItemNotFoundException("process", APIID.makeAPIID(String.valueOf(lProcessid))); } final String paramValue = parameterInstance.getValue() == null ? "" : parameterInstance.getValue().toString(); return new ProcessParameterItem(processId, parameterInstance.getName(), parameterInstance.getType(), paramValue, parameterInstance.getDescription(), processDeploy.getDisplayName(), processDef.getVersion()); } catch (final BonitaException e) { throw new APIException(e); } } @Override public String defineDefaultSearchOrder() { return ParameterCriterion.NAME_ASC.toString(); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final List items = new ArrayList<>(); List parameters = new ArrayList<>(); int parametersCount = 0; long processId = -1; try { final ProcessAPI processAPI = getProcessAPI(); if (filters != null) { if (filters.containsKey(ProcessParameterItem.FILTER_PROCESS_ID)) { final String value = filters.get(ProcessParameterItem.FILTER_PROCESS_ID); processId = Long.parseLong(value); } } if (processId != -1) { parameters = processAPI.getParameterInstances(processId, computeIndex(page, resultsByPage), resultsByPage, ParameterCriterion.valueOf(orders.toUpperCase().replace(" ", "_"))); parametersCount = processAPI.getNumberOfParameterInstances(processId); } for (final ParameterInstance p : parameters) { final String paramValue = p.getValue() == null ? "" : p.getValue().toString(); items.add(new ProcessParameterItem(String.valueOf(processId), p.getName(), p.getType(), paramValue, p.getDescription(), "", "")); } return new ItemSearchResult<>(page, resultsByPage, parametersCount, items); } catch (final BonitaException e) { throw new APIException(e); } } @Override protected void fillDeploys(final ProcessParameterItem item, final List deploys) { } @Override protected void fillCounters(final ProcessParameterItem item, final List counters) { } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/APIProcessResolutionProblem.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.bpm.process; import java.util.Map; import org.bonitasoft.web.rest.model.bpm.process.ProcessResolutionProblemDefinition; import org.bonitasoft.web.rest.model.bpm.process.ProcessResolutionProblemItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.bpm.process.ProcessResolutionProblemDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class APIProcessResolutionProblem extends ConsoleAPI implements APIHasSearch { @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(ProcessResolutionProblemDefinition.TOKEN); } @Override protected Datastore defineDefaultDatastore() { return new ProcessResolutionProblemDatastore(getEngineSession()); } @Override public String defineDefaultSearchOrder() { return ProcessResolutionProblemItem.ATTRIBUTE_TARGET_TYPE; } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { if (!filters.containsKey(ProcessResolutionProblemItem.FILTER_PROCESS_ID)) { throw new APIFilterMandatoryException(ProcessResolutionProblemItem.FILTER_PROCESS_ID); } return super.search(page, resultsByPage, search, orders, filters); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/ProcessContractController.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.bpm.process; import static org.bonitasoft.console.common.server.utils.ContractTypeConverter.ISO_8601_DATE_PATTERNS; import static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL; import javax.servlet.http.HttpSession; import org.bonitasoft.console.common.server.utils.ContractTypeConverter; import org.bonitasoft.engine.bpm.contract.ContractDefinition; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/" + API_SPRING_INTERNAL + "/bpm/process/{processDefinitionId}/contract") public class ProcessContractController extends AbstractRESTController { protected final ContractTypeConverter typeConverterUtil = new ContractTypeConverter(ISO_8601_DATE_PATTERNS); @GetMapping public ResponseEntity getContract(@PathVariable final long processDefinitionId, HttpSession session) throws BonitaException { ContractDefinition processContract = getProcessAPI(session).getProcessContract(processDefinitionId); if (processContract == null) { return ResponseEntity.noContent().build(); } return ResponseEntity.ok(typeConverterUtil.getAdaptedContractDefinition(processContract)); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/ProcessDefinitionDesignController.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.bpm.process; import static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL; import java.io.IOException; import javax.servlet.http.HttpSession; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import org.bonitasoft.engine.bpm.process.DesignProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/" + API_SPRING_INTERNAL + "/bpm/process/{processDefinitionId}/design") public class ProcessDefinitionDesignController extends AbstractRESTController { final static ObjectMapper objectMapper = new ObjectMapper(); static { objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); } @GetMapping public String getDesign(@PathVariable long processDefinitionId, HttpSession httpSession) throws ProcessDefinitionNotFoundException, IOException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { final DesignProcessDefinition design = getProcessAPI(httpSession) .getDesignProcessDefinition(processDefinitionId); return replaceLongIdToString(objectMapper.writeValueAsString(design)); } /** * Replace numeric id values with string values in JSON to avoid precision loss in JavaScript * * @param design the JSON string containing the design * @return the JSON string with numeric ids replaced with string ids */ protected String replaceLongIdToString(final String design) { return design.replaceAll("([^\\\\]\"id\"\\s*:\\s*)(\\d+)", "$1\"$2\""); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/process/ProcessInstantiationController.java ================================================ /** * Copyright (C) 2025 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.web.rest.server.api.bpm.process; import static java.lang.String.format; import static org.bonitasoft.web.rest.server.api.AbstractRESTController.API_SPRING_INTERNAL; import java.io.FileNotFoundException; import java.io.Serializable; import java.util.Date; import java.util.Map; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory; import org.bonitasoft.console.common.server.utils.ContractTypeConverter; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.contract.ContractDefinition; import org.bonitasoft.engine.bpm.contract.ContractViolationException; import org.bonitasoft.engine.bpm.process.ProcessExecutionException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; /** * REST controller for process instantiation. * Public URL: /API/bpm/process/{processDefinitionId}/instantiation * Internal path (via URL rewrite): /APISpringInternal/bpm/process/{processDefinitionId}/instantiation * * @author Nicolas Tith */ @RestController @ConditionalOnSingleCandidate(ProcessInstantiationController.class) @RequestMapping("/" + API_SPRING_INTERNAL + "/bpm/process/{processDefinitionId}/instantiation") public class ProcessInstantiationController extends AbstractRESTController { private static final Logger LOGGER = LoggerFactory.getLogger(ProcessInstantiationController.class); private static final String CASE_ID_ATTRIBUTE = "caseId"; private static final long NO_RETRY_AFTER = -1L; protected final ContractTypeConverter typeConverterUtil = new ContractTypeConverter( ContractTypeConverter.ISO_8601_DATE_PATTERNS); @PostMapping public ResponseEntity instantiateProcess( @PathVariable final long processDefinitionId, @RequestParam(name = "user", required = false) final Long userId, @RequestBody(required = false) final Map inputs, final HttpSession httpSession, final HttpServletResponse response) throws BonitaException, FileNotFoundException { final ProcessAPI processAPI = getProcessAPI(httpSession); try { final ContractDefinition processContract = processAPI.getProcessContract(processDefinitionId); final long maxSizeForTenant = getMaxFileSize(); final Map processedInputs = typeConverterUtil.getProcessedInput(processContract, inputs, maxSizeForTenant); long processInstanceId; if (userId == null) { processInstanceId = processAPI.startProcessWithInputs(processDefinitionId, processedInputs).getId(); } else { processInstanceId = processAPI.startProcessWithInputs(userId, processDefinitionId, processedInputs) .getId(); } final ObjectNode returnedObject = JsonNodeFactory.instance.objectNode(); returnedObject.put(CASE_ID_ATTRIBUTE, processInstanceId); return ResponseEntity.ok(returnedObject.toString()); } catch (final ProcessExecutionException e) { String errorMessage = "Unable to start the process with ID " + processDefinitionId; LOGGER.error("{}. Caused by: {}", errorMessage, e.getMessage()); if (e.getRetryAfter() != NO_RETRY_AFTER) { // Return a 429 status code with Retry-After header to indicate the client // that he should retry later in case of case creation limit reached response.addHeader(HttpHeaders.RETRY_AFTER, new Date(e.getRetryAfter()).toString()); return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).body("Case creation limit reached."); } // Avoid throwing original exception that may contain sensitive information unwanted in the HTTP response throw new ProcessExecutionException(errorMessage + " (consult the logs for more information)."); } catch (final ContractViolationException e) { // Re-throw as a specific exception to be handled by the global exception // handler throw new IllegalArgumentException(e.getMessage(), e); } finally { // clean temp files typeConverterUtil.deleteTemporaryFiles(inputs); } } // Visible for testing protected long getMaxFileSize() { return PropertiesFactory.getConsoleProperties().getMaxSize(); } @ExceptionHandler(MethodArgumentTypeMismatchException.class) public ResponseEntity handleTypeMismatch(MethodArgumentTypeMismatchException ex) { if ("user".equals(ex.getName())) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(format("'user' URL query parameter should be Integer. Received '%s'", ex.getValue())); } // Re-throw for other parameters throw ex; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/signal/BPMSignal.java ================================================ /** * Copyright (C) 2022-2026 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.web.rest.server.api.bpm.signal; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public record BPMSignal(String name) {} ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/bpm/signal/BPMSignalController.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.bpm.signal; import javax.servlet.http.HttpSession; import org.bonitasoft.engine.bpm.flownode.SendEventException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/API/bpm/signal") public class BPMSignalController extends AbstractRESTController { @PostMapping @ResponseStatus(HttpStatus.NO_CONTENT) public void broadcast(@RequestBody BPMSignal signal, HttpSession httpSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, SendEventException { if (signal.name() == null) { throw new IllegalArgumentException("'name' attribute is mandatory"); } getProcessAPI(httpSession).sendSignal(signal.name()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/deployer/ApplicationDeployer.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.deployer; import org.bonitasoft.web.rest.model.application.AbstractApplicationItem; import org.bonitasoft.web.rest.server.framework.Deployer; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Julien Mege */ public class ApplicationDeployer implements Deployer { private final DatastoreHasGet getter; private final String attribute; public ApplicationDeployer(final DatastoreHasGet getter, final String attribute) { this.getter = getter; this.attribute = attribute; } @Override public String getDeployedAttribute() { return attribute; } @Override public void deployIn(final IItem item) { if (isDeployable(attribute, item)) { item.setDeploy(attribute, getter.get(item.getAttributeValueAsAPIID(attribute))); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/deployer/ApplicationPageDeployer.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.deployer; import org.bonitasoft.web.rest.model.applicationpage.ApplicationPageItem; import org.bonitasoft.web.rest.server.framework.Deployer; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Julien Mege */ public class ApplicationPageDeployer implements Deployer { private final DatastoreHasGet getter; private final String attribute; public ApplicationPageDeployer(final DatastoreHasGet getter, final String attribute) { this.getter = getter; this.attribute = attribute; } @Override public String getDeployedAttribute() { return attribute; } @Override public void deployIn(final IItem item) { if (isDeployable(attribute, item)) { item.setDeploy(attribute, getApplicationPage(getApplicationPageId(item))); } } private APIID getApplicationPageId(final IItem item) { return item.getAttributeValueAsAPIID(attribute); } private ApplicationPageItem getApplicationPage(final APIID applicationPageId) { return getter.get(applicationPageId); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/deployer/DeployerFactory.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.deployer; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.server.datastore.organization.UserDatastore; import org.bonitasoft.web.rest.server.datastore.profile.GetProfileHelper; import org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor; import org.bonitasoft.web.rest.server.engineclient.EngineClientFactory; import org.bonitasoft.web.rest.server.framework.Deployer; /** * @author Vincent Elcrin */ public class DeployerFactory { private final APISession apiSession; private final EngineClientFactory factory; public DeployerFactory(final APISession apiSession) { this.apiSession = apiSession; factory = new EngineClientFactory(new EngineAPIAccessor(apiSession)); } public UserDeployer createUserDeployer(final String attribute) { return new UserDeployer(new UserDatastore(apiSession), attribute); } public Deployer createProfileDeployer(final String attribute) { return new GenericDeployer<>(createProfileGetter(), attribute); } private GetProfileHelper createProfileGetter() { return new GetProfileHelper(factory.createProfileEngineClient()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/deployer/GenericDeployer.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.deployer; import org.bonitasoft.web.rest.server.framework.Deployer; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Vincent Elcrin */ public class GenericDeployer implements Deployer { private final DatastoreHasGet getter; private final String attribute; public GenericDeployer(DatastoreHasGet getter, String attribute) { this.getter = getter; this.attribute = attribute; } @Override public String getDeployedAttribute() { return attribute; } @Override public void deployIn(IItem item) { if (isDeployable(attribute, item)) { item.setDeploy(attribute, getItem(getItemId(item))); } } private APIID getItemId(IItem item) { return item.getAttributeValueAsAPIID(attribute); } private I getItem(APIID profileId) { return getter.get(profileId); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/deployer/PageDeployer.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.deployer; import org.bonitasoft.web.rest.model.portal.page.PageItem; import org.bonitasoft.web.rest.server.framework.Deployer; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Julien Mege */ public class PageDeployer implements Deployer { private final DatastoreHasGet getter; private final String attribute; public PageDeployer(final DatastoreHasGet getter, final String attribute) { this.getter = getter; this.attribute = attribute; } @Override public String getDeployedAttribute() { return attribute; } @Override public void deployIn(final IItem item) { if (isDeployable(attribute, item)) { item.setDeploy(attribute, getPage(getPageId(item))); } } private APIID getPageId(final IItem item) { return item.getAttributeValueAsAPIID(attribute); } private PageItem getPage(final APIID pageId) { return getter.get(pageId); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/deployer/UserDeployer.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.deployer; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.rest.server.framework.Deployer; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Vincent Elcrin */ public class UserDeployer implements Deployer { private final DatastoreHasGet getter; private final String attribute; public UserDeployer(DatastoreHasGet getter, String attribute) { this.getter = getter; this.attribute = attribute; } @Override public String getDeployedAttribute() { return attribute; } @Override public void deployIn(IItem item) { if (isDeployable(attribute, item)) { item.setDeploy(attribute, getUser(getUserId(item))); } } private APIID getUserId(IItem item) { return item.getAttributeValueAsAPIID(attribute); } private UserItem getUser(APIID userId) { return getter.get(userId); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/document/APIArchivedDocument.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.document; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.document.ArchivedDocument; import org.bonitasoft.engine.bpm.document.ArchivedDocumentsSearchDescriptor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.document.ArchivedDocumentDefinition; import org.bonitasoft.web.rest.model.document.ArchivedDocumentItem; import org.bonitasoft.web.rest.model.document.DocumentItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.api.document.api.impl.DocumentDatastore; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Julien Mege */ public class APIArchivedDocument extends ConsoleAPI { @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(ArchivedDocumentDefinition.TOKEN); } @Override public ArchivedDocumentItem get(final APIID id) { final APISession apiSession = getEngineSession(); ArchivedDocumentItem item = new ArchivedDocumentItem(); try { final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(apiSession); final DocumentDatastore dataStore = new DocumentDatastore(apiSession); final ArchivedDocument document = processAPI.getArchivedProcessDocument(id.toLong()); item = dataStore.mapToArchivedDocumentItem(document); } catch (final BonitaException e) { throw new APIException(e); } return item; } @Override public String defineDefaultSearchOrder() { return ""; } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final APISession apiSession = getEngineSession(); final List items = new ArrayList<>(); long nbOfDocument = 0; String caseId = null; String viewType = null; String documentName = null; long userId = -1; try { if (filters.containsKey(ArchivedDocumentItem.FILTER_CASE_ID)) { caseId = filters.get(ArchivedDocumentItem.FILTER_CASE_ID); } if (filters.containsKey(ArchivedDocumentItem.FILTER_VIEW_TYPE)) { viewType = filters.get(ArchivedDocumentItem.FILTER_VIEW_TYPE); } if (filters.containsKey(ArchivedDocumentItem.FILTER_USER_ID)) { final String user = filters.get(ArchivedDocumentItem.FILTER_USER_ID); if (user != null) { userId = Long.valueOf(user); } else { userId = apiSession.getUserId(); } } if (filters.containsKey(DocumentItem.DOCUMENT_NAME)) { documentName = filters.get(DocumentItem.DOCUMENT_NAME); } final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, orders, search); if (caseId != null) { builder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, caseId); } if (documentName != null) { builder.filter(ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME, documentName); } SearchResult result = null; final DocumentDatastore dataStore = new DocumentDatastore(apiSession); if (userId != -1 && viewType != null) { result = dataStore.searchArchivedDocuments(userId, viewType, builder); } if (result != null) { nbOfDocument = result.getCount(); for (final ArchivedDocument document : result.getResult()) { items.add(dataStore.mapToArchivedDocumentItem(document)); } } } catch (final BonitaException | IllegalArgumentException e) { throw new APIException(e); } return new ItemSearchResult<>(page, resultsByPage, nbOfDocument, items); } @Override protected void fillDeploys(final ArchivedDocumentItem item, final List deploys) { } @Override protected void fillCounters(final ArchivedDocumentItem item, final List counters) { } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/document/APIDocument.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.document; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.document.ArchivedDocumentsSearchDescriptor; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.document.DocumentDefinition; import org.bonitasoft.web.rest.model.document.DocumentItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.api.document.api.impl.DocumentDatastore; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Julien Mege */ public class APIDocument extends ConsoleAPI { @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(DocumentDefinition.TOKEN); } protected DocumentDatastore getDataStore() { return new DocumentDatastore(getEngineSession()); } @Override public DocumentItem get(final APIID id) { final APISession apiSession = getEngineSession(); DocumentItem item = new DocumentItem(); try { final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(apiSession); final Document document = processAPI.getDocument(id.toLong()); item = getDataStore().mapToDocumentItem(document); } catch (final BonitaException e) { throw new APIException(e); } return item; } @Override public String defineDefaultSearchOrder() { return ""; } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final APISession apiSession = getEngineSession(); final List items = new ArrayList<>(); long nbOfDocument = 0; String caseId = null; String viewType = null; String documentName = null; long userId = -1; try { if (filters != null) { if (filters.containsKey(DocumentItem.FILTER_CASE_ID)) { caseId = filters.get(DocumentItem.FILTER_CASE_ID); } if (filters.containsKey(DocumentItem.FILTER_VIEW_TYPE)) { viewType = filters.get(DocumentItem.FILTER_VIEW_TYPE); } if (filters.containsKey(DocumentItem.FILTER_USER_ID)) { final String user = filters.get(DocumentItem.FILTER_USER_ID); if (user != null) { userId = Long.valueOf(user); } else { userId = apiSession.getUserId(); } } if (filters.containsKey(DocumentItem.DOCUMENT_NAME)) { documentName = filters.get(DocumentItem.DOCUMENT_NAME); } } final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, orders, search); if (caseId != null) { builder.filter(ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID, caseId); } if (documentName != null) { builder.filter(ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME, documentName); } SearchResult result = null; if (viewType != null) { result = getDataStore().searchDocuments(userId, viewType, builder); } if (result != null) { nbOfDocument = result.getCount(); for (final Document document : result.getResult()) { items.add(getDataStore().mapToDocumentItem(document)); } } } catch (final BonitaException | IllegalArgumentException e) { throw new APIException(e); } return new ItemSearchResult<>(page, resultsByPage, nbOfDocument, items); } @Override public DocumentItem add(final DocumentItem item) { final long processInstanceId = Long.valueOf(item.getAttributeValue(DocumentItem.PROCESSINSTANCE_ID)); final String documentName = item.getAttributeValue(DocumentItem.DOCUMENT_NAME); final String path = item.getAttributeValue(DocumentItem.DOCUMENT_UPLOAD); final String documentCreationType = item.getAttributeValue(DocumentItem.DOCUMENT_CREATION_TYPE); final String urlPath = item.getAttributeValue(DocumentItem.DOCUMENT_URL); try { DocumentItem returnedItem = new DocumentItem(); if (processInstanceId != -1 && documentName != null && documentCreationType != null) { if (path != null && !path.isEmpty()) { returnedItem = getDataStore().createDocument(processInstanceId, documentName, documentCreationType, path, new BonitaHomeFolderAccessor()); } else if (urlPath != null && !urlPath.isEmpty()) { returnedItem = getDataStore().createDocumentFromUrl(processInstanceId, documentName, documentCreationType, urlPath); } return returnedItem; } else { throw new APIException("Error while attaching a new document. Request with bad param value."); } } catch (final BonitaException | IOException e) { throw new APIException(e); } } @Override protected void fillDeploys(final DocumentItem item, final List deploys) { } @Override protected void fillCounters(final DocumentItem item, final List counters) { } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/document/api/impl/DocumentDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.document.api.impl; import java.io.IOException; import java.io.InputStream; import java.text.DateFormat; import java.util.Date; import java.util.List; import java.util.Locale; import org.apache.commons.io.IOUtils; import org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.document.ArchivedDocument; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.document.DocumentAttachmentException; import org.bonitasoft.engine.bpm.document.DocumentException; import org.bonitasoft.engine.bpm.process.*; import org.bonitasoft.engine.exception.*; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.web.rest.model.document.ArchivedDocumentItem; import org.bonitasoft.web.rest.model.document.DocumentItem; /** * Document data store * * @author Yongtao Guo */ public class DocumentDatastore { private final APISession apiSession; public final static String CREATE_NEW_VERSION_DOCUMENT = "AddNewVersionDocument"; public final static String CREATE_NEW_DOCUMENT = "AddNewDocument"; /** * Default constructor. */ public DocumentDatastore(final APISession apiSession) { this.apiSession = apiSession; } public SearchResult searchDocuments(final long userId, final String viewType, final SearchOptionsBuilder builder) throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, SearchException, NotFoundException { final ProcessAPI processAPI = getProcessAPI(); if (DocumentItem.VALUE_VIEW_TYPE_ADMINISTRATOR.equals(viewType) || DocumentItem.VALUE_VIEW_TYPE_USER.equals(viewType)) { return processAPI.searchDocuments(builder.done()); } else if (DocumentItem.VALUE_VIEW_TYPE_TEAM_MANAGER.equals(viewType)) { return processAPI.searchDocuments(builder.done()); } else if (DocumentItem.VALUE_VIEW_TYPE_PROCESS_OWNER.equals(viewType)) { return processAPI.searchDocumentsSupervisedBy(userId, builder.done()); } throw new IllegalArgumentException("Invalid view type."); } public SearchResult searchArchivedDocuments(final long userId, final String viewType, final SearchOptionsBuilder builder) throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, SearchException, NotFoundException { final ProcessAPI processAPI = getProcessAPI(); if (DocumentItem.VALUE_VIEW_TYPE_ADMINISTRATOR.equals(viewType) || DocumentItem.VALUE_VIEW_TYPE_USER.equals(viewType) || DocumentItem.VALUE_VIEW_TYPE_TEAM_MANAGER.equals(viewType)) { return processAPI.searchArchivedDocuments(builder.done()); } else if (DocumentItem.VALUE_VIEW_TYPE_PROCESS_OWNER.equals(viewType)) { return processAPI.searchArchivedDocumentsSupervisedBy(userId, builder.done()); } throw new IllegalArgumentException("Invalid view type."); } public DocumentItem createDocument(final long processInstanceId, final String documentName, final String documentCreationType, final String path, final BonitaHomeFolderAccessor tenantFolder) throws BonitaException, IOException, InvalidSessionException, RetrieveException { DocumentItem item = new DocumentItem(); final ProcessAPI processAPI = getProcessAPI(); final FileContent theSourceFile = tenantFolder.retrieveUploadedTempContent(path); try (InputStream inputStream = theSourceFile.getInputStream()) { final long maxSize = PropertiesFactory.getConsoleProperties().getMaxSize(); if (theSourceFile.getSize() > maxSize * 1048576) { final String errorMessage = "This document is exceeded " + maxSize + "Mo"; throw new DocumentException(errorMessage); } byte[] fileContent = IOUtils.toByteArray(inputStream); String fileName = theSourceFile.getFileName(); String mimeType = theSourceFile.getMimeType(); // Attach a new document to a case if (CREATE_NEW_DOCUMENT.equals(documentCreationType)) { final Document document = processAPI.attachDocument(processInstanceId, documentName, fileName, mimeType, fileContent); item = mapToDocumentItem(document); } else if (CREATE_NEW_VERSION_DOCUMENT.equals(documentCreationType)) { final Document document = processAPI.attachNewDocumentVersion(processInstanceId, documentName, fileName, mimeType, fileContent); item = mapToDocumentItem(document); } return item; } finally { tenantFolder.removeUploadedTempContent(path); } } protected ProcessAPI getProcessAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getProcessAPI(apiSession); } public DocumentItem createDocumentFromUrl(final long processInstanceId, final String documentName, final String documentCreationType, final String path) throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, ProcessInstanceNotFoundException, DocumentAttachmentException, IOException, RetrieveException, ProcessDefinitionNotFoundException { DocumentItem item = new DocumentItem(); final ProcessAPI processAPI = getProcessAPI(); final String fileName = DocumentUtil.getFileNameFromUrl(path); final String mimeType = DocumentUtil.getMimeTypeFromUrl(path); if (fileName != null && mimeType != null) { // Attach a new document to a case if (CREATE_NEW_DOCUMENT.equals(documentCreationType)) { final Document document = processAPI.attachDocument(processInstanceId, documentName, fileName, mimeType, path); item = mapToDocumentItem(document); } else if (CREATE_NEW_VERSION_DOCUMENT.equals(documentCreationType)) { final Document document = processAPI.attachNewDocumentVersion(processInstanceId, documentName, fileName, mimeType, path); item = mapToDocumentItem(document); } } return item; } public DocumentItem mapToDocumentItem(final Document document) throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, ProcessDefinitionNotFoundException, RetrieveException { if (document == null) { throw new IllegalArgumentException("The document must be not null!"); } DocumentItem item = new DocumentItem(); final ProcessAPI processAPI = getProcessAPI(); ProcessInstance processInstance; String caseName = ""; String processDisplayName = ""; String version = ""; try { processInstance = processAPI.getProcessInstance(document.getProcessInstanceId()); caseName = processInstance.getName(); final ProcessDeploymentInfo processDeploymentInfo = processAPI .getProcessDeploymentInfo(processInstance.getProcessDefinitionId()); processDisplayName = processDeploymentInfo.getDisplayName(); version = processDeploymentInfo.getVersion(); } catch (final ProcessInstanceNotFoundException e) { item = buildDocumentItem(caseName, processDisplayName, version, document); return item; } item = buildDocumentItem(caseName, processDisplayName, version, document); return item; } private DocumentItem buildDocumentItem(final String caseName, final String processDisplayName, final String version, final Document document) { final DocumentItem item = new DocumentItem(); item.setDocumentId(String.valueOf(document.getId())); item.setCaseId(String.valueOf(document.getProcessInstanceId())); item.setDocumentName(document.getName()); item.setDocumentAuthor(document.getAuthor()); item.setDocumentFileName(document.getContentFileName()); item.setDocumentCreationDate(parseDate(document.getCreationDate())); item.setDocumentMIMEType(document.getContentMimeType()); item.setDocumentHasContent(String.valueOf(document.hasContent())); item.setDocumentStorageId(document.getContentStorageId()); item.setDocumentURL(document.getUrl()); item.setProcessDisplayName(processDisplayName); item.setProcessVersion(version); item.setCaseName(caseName); return item; } public ArchivedDocumentItem mapToArchivedDocumentItem(final ArchivedDocument document) throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, RetrieveException, ProcessDefinitionNotFoundException { if (document == null) { throw new IllegalArgumentException("The document must be not null!"); } final ProcessAPI processAPI = getProcessAPI(); ArchivedDocumentItem item = new ArchivedDocumentItem(); String caseName = ""; String processDisplayName = ""; String version = ""; List archivedCaseList; try { archivedCaseList = processAPI.getArchivedProcessInstances(document.getProcessInstanceId(), 0, 1); final ArchivedProcessInstance processInstance = archivedCaseList.get(0); caseName = processInstance.getName(); final ProcessDeploymentInfo processDeploymentInfo = processAPI .getProcessDeploymentInfo(processInstance.getProcessDefinitionId()); processDisplayName = processDeploymentInfo.getDisplayName(); version = processDeploymentInfo.getVersion(); } catch (final NotFoundException e) { item = buildArchivedDocumentItem(caseName, processDisplayName, version, document); return item; } item = buildArchivedDocumentItem(caseName, processDisplayName, version, document); return item; } private ArchivedDocumentItem buildArchivedDocumentItem(final String caseName, final String processDisplayName, final String version, final ArchivedDocument document) { final ArchivedDocumentItem item = new ArchivedDocumentItem(); item.setDocumentId(String.valueOf(document.getSourceObjectId())); item.setDocumentSourceObjectId(String.valueOf(document.getSourceObjectId())); item.setCaseId(String.valueOf(document.getProcessInstanceId())); item.setDocumentName(document.getName()); item.setDocumentAuthor(document.getAuthor()); item.setDocumentFileName(document.getContentFileName()); item.setDocumentCreationDate(parseDate(document.getCreationDate())); item.setDocumentMIMEType(document.getContentMimeType()); item.setDocumentHasContent(String.valueOf(document.hasContent())); item.setDocumentStorageId(document.getContentStorageId()); item.setDocumentURL(document.getUrl()); item.setProcessDisplayName(processDisplayName); item.setProcessVersion(version); item.setCaseName(caseName); item.setArchivedDate(parseDate(document.getArchiveDate())); return item; } private String parseDate(final Date date) { String dateStr = null; if (date != null) { final DateFormat time = DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.ENGLISH); final DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.ENGLISH); dateStr = time.format(date) + ", " + df.format(date); } return dateStr; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/document/api/impl/DocumentUtil.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.document.api.impl; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.FileNameMap; import java.net.URLConnection; /** * @author Yongtao Guo */ public class DocumentUtil { public static byte[] getArrayByte(final InputStream input, final int estimatedSize) throws IOException { final ByteArrayOutputStream output = new ByteArrayOutputStream(estimatedSize); try { final byte[] buf = new byte[8192]; int len; while ((len = input.read(buf)) >= 0) { output.write(buf, 0, len); } } finally { output.close(); } return output.toByteArray(); } public static byte[] getArrayByteFromFile(final File f) throws IOException { final long length = f.length(); if (length > Integer.MAX_VALUE) { // more than 2 GB throw new IOException("File too big"); } final FileInputStream input = new FileInputStream(f); try { return getArrayByte(input, (int) length); } finally { input.close(); } } public static String getFileNameFromUrl(final String file) { String fileName = null; if (file != null) { final int index = file.lastIndexOf("/"); if (index != -1 && index != file.length() - 1) { final String queryName = file.substring(index + 1); final int queryIndex = queryName.indexOf("?"); if (queryIndex != -1) { fileName = queryName.substring(0, queryIndex); } else { fileName = queryName; } } } return fileName; } public static String getMimeTypeFromUrl(final String fileUrl) { String type = null; final FileNameMap fileNameMap = URLConnection.getFileNameMap(); type = fileNameMap.getContentTypeFor(fileUrl); return type; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/extension/ApiExtensionController.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.api.extension; import java.nio.charset.Charset; import java.util.Map; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.console.common.server.page.PageMappingService; import org.bonitasoft.console.common.server.page.RestApiRenderer; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.web.extension.rest.RestApiResponse; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController @ConditionalOnSingleCandidate(ApiExtensionController.class) @RequestMapping({ "/API/extension", "/portal/custom-page/API/extension" }) public class ApiExtensionController extends AbstractRESTController { private final RestApiRenderer restApiRenderer; private final PageMappingService pageMappingService; public ApiExtensionController() { this(new RestApiRenderer(), new PageMappingService()); } protected ApiExtensionController(RestApiRenderer restApiRenderer, PageMappingService pageMappingService) { this.restApiRenderer = restApiRenderer; this.pageMappingService = pageMappingService; } @RequestMapping("/**") public ResponseEntity handleExtensionCall(HttpServletRequest request, HttpServletResponse response) { try { var resolver = new ResourceExtensionResolver(request, request.getMethod(), pageMappingService); var restApiResponse = restApiRenderer.handleRestApiCall(request, resolver); if (restApiResponse == null) { throw new BonitaException("error: restApiResponse is null"); } return buildResponse(restApiResponse, response); } catch (Exception e) { log.error("Failed to handle API Extension call", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } private ResponseEntity buildResponse(RestApiResponse restApiResponse, HttpServletResponse servletResponse) { // Set cookies on the servlet response (ResponseEntity doesn't support cookies) for (Cookie cookie : restApiResponse.getAdditionalCookies()) { servletResponse.addCookie(cookie); } MediaType mediaType = MediaType.parseMediaType(restApiResponse.getMediaType()); if (mediaType.getCharset() == null) { Charset characterSet = Charset.isSupported(restApiResponse.getCharacterSet()) ? Charset.forName(restApiResponse.getCharacterSet()) : RestApiResponse.DEFAULT_CHARSET; mediaType = new MediaType(mediaType, characterSet); } var bodyBuilder = ResponseEntity .status(restApiResponse.getHttpStatus()) .contentType(mediaType); // Pass through all additional headers directly for (Map.Entry entry : restApiResponse.getAdditionalHeaders().entrySet()) { bodyBuilder.header(entry.getKey(), entry.getValue()); } String body = restApiResponse.getResponse() != null ? restApiResponse.getResponse().toString() : null; return bodyBuilder.body(body); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/extension/ControllerClassName.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.extension; /** * This class has the responsability to store a REST API extension controller class name, and if this REST API is used * in * compiled mode or in source mode. * - In compiled mode, name is supposed to be the qualified name of a Java class present in the Jar. * - In source mode, name is supposed to be the path to the main Groovy source file */ public class ControllerClassName { private final String name; private final boolean source; public ControllerClassName(String name, boolean source) { this.name = name; this.source = source; } public String getName() { return name; } public boolean isSource() { return source; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/extension/ResourceExtensionResolver.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.extension; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.console.common.server.page.PageMappingService; import org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.session.APISession; /** * @author Laurent Leseigneur */ public class ResourceExtensionResolver { public static final String MAPPING_KEY_SEPARATOR = "|"; public static final String MAPPING_KEY_PREFIX = "apiExtension"; public static final String API_EXTENSION_TEMPLATE_PREFIX = "/API/extension/"; private final HttpServletRequest httpServletRequest; private final String httpMethod; private final PageMappingService pageMappingService; public ResourceExtensionResolver(HttpServletRequest httpServletRequest, String httpMethod, PageMappingService pageMappingService) { this.httpServletRequest = httpServletRequest; this.httpMethod = httpMethod; this.pageMappingService = pageMappingService; } public Long resolvePageId(APISession apiSession) throws BonitaException { return pageMappingService.getPage(httpServletRequest, apiSession, generateMappingKey(), httpServletRequest.getLocale(), false).getPageId(); } public String generateMappingKey() { final StringBuilder builder = new StringBuilder(); final String requestAsString = httpServletRequest.getRequestURI(); final String pathTemplate = StringUtils.substringAfter(requestAsString, API_EXTENSION_TEMPLATE_PREFIX); builder.append(MAPPING_KEY_PREFIX) .append(MAPPING_KEY_SEPARATOR) .append(httpMethod) .append(MAPPING_KEY_SEPARATOR) .append(pathTemplate); return builder.toString(); } public ControllerClassName resolveRestApiControllerClassName(PageResourceProviderImpl pageResourceProvider) throws NotFoundException { final Properties properties = new Properties(); try (final InputStream resourceAsStream = pageResourceProvider.getResourceAsStream("page.properties")) { properties.load(resourceAsStream); } catch (final IOException e) { throw new NotFoundException("error while getting resource:" + generateMappingKey()); } final String apiExtensionList = (String) properties.get("apiExtensions"); final String[] apiExtensions = apiExtensionList.split(","); return findMatchingControllerClassName(properties, apiExtensions); } /** * return the content of the property 'className' or 'classFileName' (if className isn't present). * - 'className' refers to the qualified name of the main class. If 'className' is present then it indicates that * the jar * file * containing the extension is present. * - 'classFileName' is the path to the groovy source file (lagacy mode), it indicates that the rest api needs to be * compiled * at runtime with a groovy compiler. */ private ControllerClassName findMatchingControllerClassName(Properties properties, String[] apiExtensions) throws NotFoundException { for (final String apiExtension : apiExtensions) { final String method = (String) properties.get(String.format("%s.method", apiExtension.trim())); String pathTemplate = (String) properties.get(String.format("%s.pathTemplate", apiExtension.trim())); if (pathTemplate != null && pathTemplate.startsWith("/")) { pathTemplate = pathTemplate.substring(1); } final String className = (String) properties.get(String.format("%s.className", apiExtension.trim())); final String classFileName = (String) properties .get(String.format("%s.classFileName", apiExtension.trim())); if (extensionMatches(method, pathTemplate)) { return className != null && !className.isEmpty() ? new ControllerClassName(className, false) : new ControllerClassName(classFileName, true); } } throw new NotFoundException("error while getting resource:" + generateMappingKey()); } private boolean extensionMatches(String method, String pathTemplate) { return httpMethod.equals(method) && httpServletRequest.getRequestURI() .endsWith(String.format("%s%s", API_EXTENSION_TEMPLATE_PREFIX, pathTemplate)); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/form/FormMappingController.java ================================================ /** * Copyright (C) 2025 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.web.rest.server.api.form; import static java.util.stream.Collectors.toList; import static org.bonitasoft.web.rest.server.APIPaginationUtils.buildContentRange; import static org.bonitasoft.web.rest.server.APIPaginationUtils.buildSearchOptions; import java.util.List; import javax.servlet.http.HttpSession; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.form.FormMapping; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * REST controller to search Form Mappings. * Note: Uses @ConditionalOnSingleCandidate to be registered only when no subclass is present. * In subscription builds, FormMappingControllerExt extends this class and takes precedence. * * @author Anthony Birembaut */ @RestController @RequestMapping("/API/form/mapping") @ConditionalOnSingleCandidate(FormMappingController.class) public class FormMappingController extends AbstractRESTController { /** * Search for form mappings based on query parameters. * * @param page the page number (0-indexed) * @param count the number of results per page * @param search the search term * @param order the sort order (e.g., "id ASC") * @param filters the filters as a list of "key=value" strings * @param httpSession the HTTP session * @return the list of form mapping items with Content-Range header * @throws BonitaException if an error occurs during the search */ @GetMapping public ResponseEntity> searchFormMapping( @RequestParam(value = "p", defaultValue = "0") int page, @RequestParam(value = "c", defaultValue = "10") int count, @RequestParam(value = "s", required = false) String search, @RequestParam(value = "o", required = false) String order, @RequestParam(value = "f", required = false) List filters, HttpSession httpSession) throws BonitaException { ProcessAPI processAPI = getProcessAPI(httpSession); SearchOptions searchOptions = buildSearchOptions(page, count, search, order, filters); SearchResult searchResult = processAPI.searchFormMappings(searchOptions); List result = searchResult.getResult().stream() .map(item -> new FormMappingItem((FormMapping) item)) .collect(toList()); HttpHeaders headers = new HttpHeaders(); headers.set(HttpHeaders.CONTENT_RANGE, buildContentRange(page, searchResult.getResult().size(), searchResult.getCount())); return ResponseEntity.ok() .headers(headers) .body(result); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/form/FormMappingItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.form; import java.io.Serializable; import java.util.Date; import org.bonitasoft.engine.form.FormMapping; import org.bonitasoft.engine.form.FormMappingTarget; import org.bonitasoft.engine.form.FormMappingType; /** * Created by Fabio Lombardi */ public class FormMappingItem implements Serializable { private String id; private String processDefinitionId; private FormMappingType type; private FormMappingTarget target; private String task; private String pageId; private String pageURL; private String pageMappingKey; private String lastUpdatedBy; private Date lastUpdateDate; private boolean formRequired; public FormMappingItem(final FormMapping item) { id = String.valueOf(item.getId()); processDefinitionId = String.valueOf(item.getProcessDefinitionId()); type = item.getType(); target = item.getTarget(); task = item.getTask(); pageId = null; if (item.getPageId() != null) { pageId = String.valueOf(item.getPageId()); } pageURL = item.getURL(); pageMappingKey = item.getPageMappingKey(); lastUpdatedBy = String.valueOf(item.getLastUpdatedBy()); lastUpdateDate = item.getLastUpdateDate(); formRequired = item.isFormRequired(); } public String getProcessDefinitionId() { return processDefinitionId; } public void setProcessDefinitionId(final String processDefinitionId) { this.processDefinitionId = processDefinitionId; } public String getPageMappingKey() { return pageMappingKey; } public void setPageMappingKey(final String pageMappingKey) { this.pageMappingKey = pageMappingKey; } public String getId() { return id; } public void setId(final String id) { this.id = id; } public String getTask() { return task; } public void setTask(final String task) { this.task = task; } public String getPageId() { return pageId; } public void setPageId(final String pageId) { this.pageId = pageId; } public String getURL() { return pageURL; } public void setPageURL(final String pageURL) { this.pageURL = pageURL; } public FormMappingType getType() { return type; } public void setType(final FormMappingType type) { this.type = type; } public String getLastUpdatedBy() { return lastUpdatedBy; } public void setLastUpdatedBy(final String lastUpdatedBy) { this.lastUpdatedBy = lastUpdatedBy; } public Date getLastUpdateDate() { return lastUpdateDate; } public void setLastUpdateDate(final Date lastUpdateDate) { this.lastUpdateDate = lastUpdateDate; } public FormMappingTarget getTarget() { return target; } public void setTarget(final FormMappingTarget target) { this.target = target; } public boolean isFormRequired() { return formRequired; } public void setFormRequired(final boolean formRequired) { this.formRequired = formRequired; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APICustomUserInfoDefinition.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import static org.bonitasoft.web.rest.server.api.APIPreconditions.check; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.identity.CustomUserInfoDefinition; import org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator; import org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinitionDefinition; import org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinitionItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClient; import org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClientCreator; import org.bonitasoft.web.rest.server.framework.api.APIHasAdd; import org.bonitasoft.web.rest.server.framework.api.APIHasDelete; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Vincent Elcrin */ public class APICustomUserInfoDefinition extends ConsoleAPI implements APIHasAdd, APIHasSearch, APIHasDelete { public static final String FIX_ORDER = "Fix order"; private final CustomUserInfoConverter converter = new CustomUserInfoConverter(); private final CustomUserInfoEngineClientCreator engineClientCreator; public APICustomUserInfoDefinition(CustomUserInfoEngineClientCreator engineClientCreator) { this.engineClientCreator = engineClientCreator; } public CustomUserInfoDefinitionItem add(CustomUserInfoDefinitionItem definition) { return converter.convert(engineClientCreator.create(getEngineSession()) .createDefinition(new CustomUserInfoDefinitionCreator( definition.getName(), definition.getDescription()))); } public void delete(final List ids) { for (APIID id : ids) { engineClientCreator.create(getEngineSession()).deleteDefinition(id.toLong()); } } public ItemSearchResult search( final int page, final int resultsByPage, final String search, final String orders, final Map filters) { check(search == null, new T_("Search terms are not supported by this API")); check(filters == null || filters.isEmpty(), new T_("Filters are not supported by this API")); check(orders.equals(FIX_ORDER), new T_("Sorting is not supported by this API")); CustomUserInfoEngineClient client = engineClientCreator.create(getEngineSession()); List result = new ArrayList<>(); for (CustomUserInfoDefinition definition : client.listDefinitions(page * resultsByPage, resultsByPage)) { result.add(converter.convert(definition)); } return new ItemSearchResult<>(page, resultsByPage, client.countDefinitions(), result); } @Override protected ItemDefinition defineItemDefinition() { return CustomUserInfoDefinitionDefinition.get(); } public String defineDefaultSearchOrder() { return FIX_ORDER; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APICustomUserInfoUser.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import static org.bonitasoft.web.rest.model.identity.CustomUserInfoItem.FILTER_USER_ID; import static org.bonitasoft.web.rest.server.api.APIPreconditions.check; import static org.bonitasoft.web.rest.server.api.APIPreconditions.containsOnly; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.identity.CustomUserInfo; import org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinition; import org.bonitasoft.web.rest.model.identity.CustomUserInfoItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClient; import org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClientCreator; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Vincent Elcrin */ public class APICustomUserInfoUser extends ConsoleAPI implements APIHasSearch { public static final String FIX_ORDER = "Fix order"; private final CustomUserInfoEngineClientCreator engineClientCreator; private final CustomUserInfoConverter converter = new CustomUserInfoConverter(); public APICustomUserInfoUser(CustomUserInfoEngineClientCreator engineClientCreator) { this.engineClientCreator = engineClientCreator; } @Override public ItemSearchResult search(int page, int resultsByPage, String search, String orders, Map filters) { check(containsOnly(FILTER_USER_ID, filters), new T_("The only mandatory filter is %name%", new Arg("name", FILTER_USER_ID))); check(orders.equals(FIX_ORDER), new T_("Sorting is not supported by this API")); check(search == null, new T_("Search terms are not supported by this API")); CustomUserInfoEngineClient client = engineClientCreator.create(getEngineSession()); List items = client.listCustomInformation( Long.parseLong(filters.get(FILTER_USER_ID)), page * resultsByPage, resultsByPage); List information = new ArrayList<>(); for (CustomUserInfo item : items) { information.add(converter.convert(item)); } return new ItemSearchResult<>(page, information.size(), client.countDefinitions(), information); } @Override protected ItemDefinition defineItemDefinition() { return CustomUserInfoDefinition.get(); } @Override public String defineDefaultSearchOrder() { return FIX_ORDER; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APICustomUserInfoValue.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import static org.bonitasoft.web.rest.model.identity.CustomUserInfoItem.ATTRIBUTE_VALUE; import static org.bonitasoft.web.rest.server.api.APIPreconditions.check; import static org.bonitasoft.web.rest.server.api.APIPreconditions.containsOnly; import java.util.Map; import org.bonitasoft.engine.identity.CustomUserInfoValue; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinition; import org.bonitasoft.web.rest.model.identity.CustomUserInfoItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.converter.ItemSearchResultConverter; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.datastore.utils.Sorts; import org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClient; import org.bonitasoft.web.rest.server.engineclient.CustomUserInfoEngineClientCreator; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Vincent Elcrin */ public class APICustomUserInfoValue extends ConsoleAPI implements APIHasSearch, APIHasUpdate { private final CustomUserInfoEngineClientCreator engineClientCreator; private final CustomUserInfoConverter converter = new CustomUserInfoConverter(); public APICustomUserInfoValue(CustomUserInfoEngineClientCreator engineClientCreator) { this.engineClientCreator = engineClientCreator; } @Override public ItemSearchResult search(int page, int resultsByPage, String search, String orders, Map filters) { SearchResult result = getClient().searchCustomUserInfoValues(new SearchOptionsCreator( page, resultsByPage, search, new Sorts(orders), new Filters(filters, new GenericFilterCreator(new CustomUserInfoAttributeConverter()))).create()); return new ItemSearchResultConverter<>( page, resultsByPage, result, new CustomUserInfoConverter()).toItemSearchResult(); } @Override public String defineDefaultSearchOrder() { return ATTRIBUTE_VALUE + " ASC"; } @Override public CustomUserInfoItem update(APIID id, Map attributes) { check(containsOnly(ATTRIBUTE_VALUE, attributes), new T_("Only the value attribute can be updated")); return converter.convert(getClient().setCustomUserInfoValue( id.getPartAsLong(1), id.getPartAsLong(0), attributes.get(ATTRIBUTE_VALUE))); } private CustomUserInfoEngineClient getClient() { return engineClientCreator.create(getEngineSession()); } @Override protected ItemDefinition defineItemDefinition() { return CustomUserInfoDefinition.get(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APIGroup.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import java.util.List; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.GroupCriterion; import org.bonitasoft.web.rest.model.identity.GroupDefinition; import org.bonitasoft.web.rest.model.identity.GroupItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.organization.GroupDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasAdd; import org.bonitasoft.web.rest.server.framework.api.APIHasDelete; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Nicolas Tith */ public class APIGroup extends ConsoleAPI implements APIHasAdd, APIHasGet, APIHasDelete, APIHasSearch, APIHasUpdate { @Override protected ItemDefinition defineItemDefinition() { return GroupDefinition.get(); } @Override protected GroupDatastore defineDefaultDatastore() { return new GroupDatastore(getEngineSession()); } @Override public String defineDefaultSearchOrder() { return GroupCriterion.NAME_ASC.toString(); } @Override protected void fillDeploys(final GroupItem item, final List deploys) { if (deploys.contains(GroupItem.ATTRIBUTE_PARENT_GROUP_ID) && item.getParentPath() != null && !item.getParentPath().isEmpty()) { Group parentGroup = ((GroupDatastore) getDefaultDatastore()).getGroupEngineClient() .getGroupByPath(item.getParentPath()); item.setParentGroupId(String.valueOf(parentGroup.getId())); } } @Override protected void fillCounters(final GroupItem item, final List counters) { if (counters.contains(GroupItem.COUNTER_NUMBER_OF_USERS)) { item.setAttribute(GroupItem.COUNTER_NUMBER_OF_USERS, ((GroupDatastore) getDefaultDatastore()).getNumberOfUsers(item.getId())); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APIMembership.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import java.util.Arrays; import java.util.List; import java.util.Map; import org.bonitasoft.engine.identity.UserMembershipCriterion; import org.bonitasoft.web.rest.model.identity.MembershipDefinition; import org.bonitasoft.web.rest.model.identity.MembershipItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.organization.GroupDatastore; import org.bonitasoft.web.rest.server.datastore.organization.MembershipDatastore; import org.bonitasoft.web.rest.server.datastore.organization.RoleDatastore; import org.bonitasoft.web.rest.server.datastore.organization.UserDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasAdd; import org.bonitasoft.web.rest.server.framework.api.APIHasDelete; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.rest.server.framework.exception.APIFilterException; import org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class APIMembership extends ConsoleAPI implements APIHasAdd, APIHasSearch, APIHasDelete { // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CONFIGURATION // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(MembershipDefinition.TOKEN); } @Override protected List defineReadOnlyAttributes() { return Arrays.asList( MembershipItem.ATTRIBUTE_ASSIGNED_BY_USER_ID, MembershipItem.ATTRIBUTE_ASSIGNED_DATE); } @Override protected void fillDeploys(final MembershipItem item, final List deploys) { if (isDeployable(MembershipItem.ATTRIBUTE_USER_ID, deploys, item)) { item.setDeploy(MembershipItem.ATTRIBUTE_USER_ID, new UserDatastore(getEngineSession()).get(item.getUserId())); } if (isDeployable(MembershipItem.ATTRIBUTE_ROLE_ID, deploys, item)) { item.setDeploy(MembershipItem.ATTRIBUTE_ROLE_ID, new RoleDatastore(getEngineSession()).get(item.getRoleId())); } if (isDeployable(MembershipItem.ATTRIBUTE_GROUP_ID, deploys, item)) { item.setDeploy(MembershipItem.ATTRIBUTE_GROUP_ID, new GroupDatastore(getEngineSession()).get(item.getGroupId())); } if (isDeployable(MembershipItem.ATTRIBUTE_ASSIGNED_BY_USER_ID, deploys, item)) { item.setDeploy(MembershipItem.ATTRIBUTE_ASSIGNED_BY_USER_ID, new UserDatastore(getEngineSession()).get(item.getAssignedByUserId())); } } @Override protected Datastore defineDefaultDatastore() { return new MembershipDatastore(getEngineSession()); } @Override public String defineDefaultSearchOrder() { return UserMembershipCriterion.ROLE_NAME_ASC.name(); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CRUDS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { // user_id filter mandatory if (filters.size() == 0) { throw new APIFilterMandatoryException(MembershipItem.ATTRIBUTE_USER_ID); } // only user_id filter allowed else if (filters.size() > 1 || !filters.containsKey(MembershipItem.ATTRIBUTE_USER_ID)) { throw new APIFilterException("Cant search on filter other than " + MembershipItem.ATTRIBUTE_USER_ID); } return super.search(page, resultsByPage, search, orders, filters); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APIPersonalContactData.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import org.bonitasoft.web.rest.model.identity.PersonalContactDataDefinition; import org.bonitasoft.web.rest.model.identity.PersonalContactDataItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.organization.PersonalContactDataDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasAdd; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Paul AMAR */ public class APIPersonalContactData extends ConsoleAPI implements APIHasGet, APIHasUpdate, APIHasAdd { @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(PersonalContactDataDefinition.TOKEN); } @Override protected Datastore defineDefaultDatastore() { return new PersonalContactDataDatastore(getEngineSession()); } @Override public PersonalContactDataItem add(final PersonalContactDataItem item) { return super.add(item); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APIProfessionalContactData.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import org.bonitasoft.web.rest.model.identity.ProfessionalContactDataDefinition; import org.bonitasoft.web.rest.model.identity.ProfessionalContactDataItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.organization.ProfessionalContactDataDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasAdd; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Paul AMAR */ public class APIProfessionalContactData extends ConsoleAPI implements APIHasGet, APIHasUpdate, APIHasAdd { @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(ProfessionalContactDataDefinition.TOKEN); } @Override protected Datastore defineDefaultDatastore() { return new ProfessionalContactDataDatastore(getEngineSession()); } @Override public ProfessionalContactDataItem add(final ProfessionalContactDataItem item) { return super.add(item); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APIRole.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.identity.RoleCriterion; import org.bonitasoft.web.rest.model.identity.RoleDefinition; import org.bonitasoft.web.rest.model.identity.RoleItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.organization.RoleDatastore; import org.bonitasoft.web.rest.server.datastore.organization.UserDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasAdd; import org.bonitasoft.web.rest.server.framework.api.APIHasDelete; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; public class APIRole extends ConsoleAPI implements APIHasGet, APIHasSearch, APIHasUpdate, APIHasAdd, APIHasDelete { @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(RoleDefinition.TOKEN); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected void fillDeploys(final RoleItem item, final List deploys) { if (isDeployable(RoleItem.ATTRIBUTE_CREATED_BY_USER_ID, deploys, item)) { item.setDeploy(RoleItem.ATTRIBUTE_CREATED_BY_USER_ID, new UserDatastore(getEngineSession()).get(item.getCreatedByUserId())); } } @Override protected void fillCounters(final RoleItem item, final List counters) { if (counters.contains(RoleItem.COUNTER_NUMBER_OF_USERS)) { item.setAttribute(RoleItem.COUNTER_NUMBER_OF_USERS, ((RoleDatastore) defineDefaultDatastore()).getNumberOfUsers(item.getId())); } } @Override protected List defineReadOnlyAttributes() { return Arrays.asList( RoleItem.ATTRIBUTE_CREATED_BY_USER_ID, RoleItem.ATTRIBUTE_CREATION_DATE, RoleItem.ATTRIBUTE_LAST_UPDATE_DATE); } @Override protected Datastore defineDefaultDatastore() { return new RoleDatastore(getEngineSession()); } @Override public String defineDefaultSearchOrder() { return RoleCriterion.DISPLAY_NAME_ASC.name(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/APIUser.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory; import org.bonitasoft.web.rest.model.identity.UserDefinition; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.HumanTaskDatastore; import org.bonitasoft.web.rest.server.datastore.organization.PersonalContactDataDatastore; import org.bonitasoft.web.rest.server.datastore.organization.ProfessionalContactDataDatastore; import org.bonitasoft.web.rest.server.datastore.organization.UserDatastore; import org.bonitasoft.web.rest.server.framework.api.*; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ValidationError; import org.bonitasoft.web.toolkit.client.data.item.attribute.ValidationException; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.AbstractStringValidator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Séverin Moussel */ // TODO : implements APIhasFile public class APIUser extends ConsoleAPI implements APIHasAdd, APIHasDelete, APIHasUpdate, APIHasGet, APIHasSearch { /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(APIUser.class.getName()); @Override protected ItemDefinition defineItemDefinition() { return UserDefinition.get(); } @Override public String defineDefaultSearchOrder() { return UserItem.ATTRIBUTE_LASTNAME; } @Override public UserItem add(final UserItem item) { // Finish the upload of the icon if (StringUtil.isBlank(item.getPassword())) { throw new ValidationException( Collections.singletonList(new ValidationError("Password", "%attribute% is mandatory"))); } checkPasswordRobustness(item.getPassword()); // Add return super.add(item); } @Override protected Datastore defineDefaultDatastore() { return new UserDatastore(getEngineSession()); } private void checkPasswordRobustness(final String password) { try { final Class validatorClass = Class.forName(getValidatorClassName()); Object instanceClass; try { instanceClass = validatorClass.newInstance(); final AbstractStringValidator validator = (AbstractStringValidator) instanceClass; validator.setLocale(getLocale()); validator.check(password); if (!validator.getErrors().isEmpty()) { throw new ValidationException(validator.getErrors()); } } catch (final InstantiationException e) { if (LOGGER.isErrorEnabled()) { LOGGER.error("Error while instantiating the class", e); } e.printStackTrace(); } catch (final IllegalAccessException e) { if (LOGGER.isErrorEnabled()) { LOGGER.error("Illegal access with the file ", e); } e.printStackTrace(); } } catch (final ClassNotFoundException e) { if (LOGGER.isErrorEnabled()) { LOGGER.error("Class not found", e); } e.printStackTrace(); } } String getValidatorClassName() { return PropertiesFactory.getSecurityProperties().getPasswordValidator(); } @Override public UserItem update(final APIID id, final Map item) { // Do not update password if not set MapUtil.removeIfBlank(item, UserItem.ATTRIBUTE_PASSWORD); if (item.get(UserItem.ATTRIBUTE_PASSWORD) != null) { checkPasswordRobustness(item.get(UserItem.ATTRIBUTE_PASSWORD)); } return super.update(id, item); } @Override public UserItem get(final APIID id) { final UserItem item = super.get(id); if (item != null) { // Do not let the password output from the API item.setPassword(null); final String iconPath = item.getIcon(); if (iconPath == null || iconPath.isEmpty()) { item.setIcon(UserItem.DEFAULT_USER_ICON); } } return item; } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final ItemSearchResult results = super.search(page, resultsByPage, search, orders, filters); for (final UserItem item : results.getResults()) { if (item != null) { // Do not let the password output from the API item.setPassword(null); } } return results; } @Override public void delete(final List ids) { super.delete(ids); } @Override protected void fillDeploys(final UserItem item, final List deploys) { if (isDeployable(UserItem.ATTRIBUTE_MANAGER_ID, deploys, item)) { item.setDeploy(UserItem.ATTRIBUTE_MANAGER_ID, ((UserDatastore) getDefaultDatastore()).get(item.getManagerId())); } if (isDeployable(UserItem.ATTRIBUTE_CREATED_BY_USER_ID, deploys, item)) { item.setDeploy(UserItem.ATTRIBUTE_CREATED_BY_USER_ID, ((UserDatastore) getDefaultDatastore()).get(item.getCreatedByUserId())); } if (deploys.contains(UserItem.DEPLOY_PERSONAL_DATA)) { item.setDeploy(UserItem.DEPLOY_PERSONAL_DATA, new PersonalContactDataDatastore(getEngineSession()).get(item.getId())); // not a real deploy. force attribute to fix json conversion (Item#toJson) item.setAttribute(UserItem.DEPLOY_PERSONAL_DATA, (String) null); } if (deploys.contains(UserItem.DEPLOY_PROFESSIONAL_DATA)) { item.setDeploy(UserItem.DEPLOY_PROFESSIONAL_DATA, new ProfessionalContactDataDatastore(getEngineSession()).get(item.getId())); // not a real deploy. force attribute to fix json conversion (Item#toJson) item.setAttribute(UserItem.DEPLOY_PROFESSIONAL_DATA, (String) null); } } @Override protected void fillCounters(final UserItem item, final List counters) { if (counters.contains(UserItem.COUNTER_OPEN_TASKS)) { item.setAttribute(UserItem.COUNTER_OPEN_TASKS, new HumanTaskDatastore(getEngineSession()).getNumberOfOpenTasks(item.getId())); } if (counters.contains(UserItem.COUNTER_OVERDUE_TASKS)) { item.setAttribute(UserItem.COUNTER_OVERDUE_TASKS, new HumanTaskDatastore(getEngineSession()).getNumberOfOverdueOpenTasks(item.getId())); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/CustomUserInfoAttributeConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import static org.bonitasoft.engine.identity.CustomUserInfoValueSearchDescriptor.*; import static org.bonitasoft.web.rest.model.identity.CustomUserInfoItem.*; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Vincent Elcrin */ public class CustomUserInfoAttributeConverter implements AttributeConverter { private static final Map attributes = new HashMap<>(); static { attributes.put(ATTRIBUTE_USER_ID, USER_ID); attributes.put(ATTRIBUTE_DEFINITION_ID, DEFINITION_ID); attributes.put(ATTRIBUTE_VALUE, VALUE); } @Override public String convert(String attribute) { return attributes.get(attribute); } @Override public Map getValueTypeMapping() { return Collections.emptyMap(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/CustomUserInfoConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization; import org.bonitasoft.engine.identity.CustomUserInfo; import org.bonitasoft.engine.identity.CustomUserInfoDefinition; import org.bonitasoft.engine.identity.CustomUserInfoValue; import org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinitionItem; import org.bonitasoft.web.rest.model.identity.CustomUserInfoItem; import org.bonitasoft.web.rest.server.datastore.converter.ItemConverter; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Vincent Elcrin */ public class CustomUserInfoConverter extends ItemConverter { public CustomUserInfoDefinitionItem convert(CustomUserInfoDefinition definition) { CustomUserInfoDefinitionItem item = new CustomUserInfoDefinitionItem(); item.setId(APIID.makeAPIID(definition.getId())); item.setName(definition.getName()); item.setDescription(definition.getDescription()); return item; } public CustomUserInfoItem convert(CustomUserInfo information) { CustomUserInfoItem item = new CustomUserInfoItem(); item.setUserId(information.getUserId()); item.setDefinition(convert(information.getDefinition())); item.setValue(information.getValue()); return item; } @Override public CustomUserInfoItem convert(CustomUserInfoValue value) { CustomUserInfoItem item = new CustomUserInfoItem(); item.setUserId(value.getUserId()); item.setDefinition(APIID.makeAPIID(value.getDefinitionId())); item.setValue(value.getValue()); return item; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/password/validator/DefaultPasswordValidator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization.password.validator; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.AbstractStringValidator; /** * Do not delete: this class is used by reflection ! * See 'security.password.validator' in file 'security-config.properties' * * @author Paul AMAR */ public class DefaultPasswordValidator extends AbstractStringValidator { @Override protected void _check(String password) { } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/organization/password/validator/RobustnessPasswordValidator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.organization.password.validator; import static org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.t_; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.AbstractStringValidator; /** * Do not delete: this class can be used by reflection ! * See 'security.password.validator' in file 'security-config.properties' * It is referenced in the documentation as an alternative Password Validation * * @author Paul AMAR */ public class RobustnessPasswordValidator extends AbstractStringValidator { @Override protected void _check(String password) { String regex; LOCALE Locale = AbstractI18n.stringToLocale(locale); // Check number of digits regex = "[0-9]"; int numberMinOccurences = 3; if (numberOfOccurenceOfRegex(regex, password) < numberMinOccurences) { addError(t_("Password must contain at least %number% digits", Locale, new Arg("number", numberMinOccurences))); } // Check number of lower case chars regex = "[a-z]"; numberMinOccurences = 2; if (numberOfOccurenceOfRegex(regex, password) < numberMinOccurences) { addError(t_("Password must contain at least %number% lower case characters", Locale, new Arg("number", numberMinOccurences))); } // Check number of upper case chars regex = "[A-Z]"; numberMinOccurences = 2; if (numberOfOccurenceOfRegex(regex, password) < numberMinOccurences) { addError(t_("Password must contain at least %number% upper case characters", Locale, new Arg("number", numberMinOccurences))); } // Check number of special chars regex = "[~@#\\^\\$&\\*\\(\\)-T_\\+=\\[\\]\\{\\}\\|\\,\\.\\?]"; numberMinOccurences = 2; if (numberOfOccurenceOfRegex(regex, password) < numberMinOccurences) { addError(t_("Password must contain at least %number% special characters", Locale, new Arg("number", numberMinOccurences))); } // Check number of length int minimalLength = 10; if (password.length() < minimalLength) { addError( t_("Password must be at least %number% characters long", Locale, new Arg("number", minimalLength))); } } private int numberOfOccurenceOfRegex(String regex, String password) { Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(password); int count = 0; while (matcher.find()) count++; return count; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/page/APIPage.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.page; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.web.rest.model.portal.page.PageDefinition; import org.bonitasoft.web.rest.model.portal.page.PageItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.api.deployer.DeployerFactory; import org.bonitasoft.web.rest.server.datastore.page.PageDatastore; import org.bonitasoft.web.rest.server.datastore.page.PageDatastoreFactory; import org.bonitasoft.web.rest.server.framework.api.APIHasAdd; import org.bonitasoft.web.rest.server.framework.api.APIHasDelete; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Fabio Lombardi */ public class APIPage extends ConsoleAPI implements APIHasGet, APIHasSearch, APIHasAdd, APIHasUpdate, APIHasDelete { @Override protected ItemDefinition defineItemDefinition() { return PageDefinition.get(); } @Override protected Datastore defineDefaultDatastore() { return getPageDatastore(); } @Override public PageItem get(final APIID id) { return getPageDatastore().get(id); } /** * @deprecated as of 9.0.0, a page should be created at startup. */ @Override @Deprecated(since = "9.0.0") public PageItem add(final PageItem item) { return getPageDatastore().add(item); } /** * @deprecated as of 9.0.0, a page should be updated at startup. */ @Override @Deprecated(since = "9.0.0") public PageItem update(final APIID id, final Map attributes) { return getPageDatastore().update(id, attributes); } @Override public void delete(final List ids) { getPageDatastore().delete(ids); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { return getPageDatastore().search(page, resultsByPage, search, orders, filters); } @Override protected void fillDeploys(final PageItem item, final List deploys) { /* * Need to be done there and not in constructor * because need the engine session which is set * by setter instead of being injected in API constructor... */ addDeployer(getDeployerFactory().createUserDeployer(PageItem.ATTRIBUTE_CREATED_BY_USER_ID)); addDeployer(getDeployerFactory().createUserDeployer(PageItem.ATTRIBUTE_UPDATED_BY_USER_ID)); super.fillDeploys(item, deploys); } protected DeployerFactory getDeployerFactory() { return new DeployerFactory(getEngineSession()); } @Override public String defineDefaultSearchOrder() { return PageItem.ATTRIBUTE_URL_TOKEN; } private PageDatastore getPageDatastore() { PageAPI pageAPI; try { pageAPI = TenantAPIAccessor.getCustomPageAPI(getEngineSession()); } catch (final BonitaException e) { throw new APIException(e); } final WebBonitaConstantsUtils constants = WebBonitaConstantsUtils.getTenantInstance(); final PageDatastoreFactory pageDatastoreFactory = new PageDatastoreFactory(); return pageDatastoreFactory.create(getEngineSession(), constants, pageAPI); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/platform/APIPlatform.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.platform; import java.util.Date; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.PlatformAPI; import org.bonitasoft.engine.api.PlatformAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.platform.Platform; import org.bonitasoft.engine.platform.PlatformNotFoundException; import org.bonitasoft.engine.platform.PlatformState; import org.bonitasoft.engine.platform.StartNodeException; import org.bonitasoft.web.rest.model.platform.PlatformDefinition; import org.bonitasoft.web.rest.model.platform.PlatformItem; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.ui.utils.DateFormat; /** * @author Séverin Moussel */ public class APIPlatform extends org.bonitasoft.web.rest.server.api.PlatformAPI implements APIHasGet, APIHasUpdate { @Override protected ItemDefinition defineItemDefinition() { return Definitions.get(PlatformDefinition.TOKEN); } @Override public PlatformItem update(final APIID id, final Map attributes) { final String platformState = attributes.get(PlatformItem.ATTRIBUTE_STATE); try { final PlatformAPI platformAPI = getPlatformAPI(); if (platformAPI.isPlatformCreated()) { try { if (platformState.equals("start")) { platformAPI.startNode(); } else if (platformState.equals("stop")) { platformAPI.stopNode(); } } catch (final StartNodeException sne) { throw new APIException(sne); } } return get(null); } catch (final BonitaException e) { throw new APIException(e); } } @Override public PlatformItem get(final APIID id) { PlatformItem platformItem; try { final PlatformAPI platformAPI = getPlatformAPI(); final Platform platform = platformAPI.getPlatform(); final String createdDate = DateFormat.dateToSql(new Date(platform.getCreated())); final PlatformState platformState = platformAPI.getPlatformState(); String platformStateStr = ""; if (platformState != null) { platformStateStr = platformState.toString(); } platformItem = new PlatformItem(platform.getVersion(), platform.getInitialVersion(), createdDate, platform.getCreatedBy(), platformStateStr); return platformItem; } catch (final PlatformNotFoundException ex) { platformItem = new PlatformItem(); return platformItem; } } private PlatformAPI getPlatformAPI() { try { return PlatformAPIAccessor.getPlatformAPI(getPlatformSession()); } catch (final BonitaException e) { throw new APIException(e); } } @Override protected void fillDeploys(final PlatformItem item, final List deploys) { } @Override protected void fillCounters(final PlatformItem item, final List counters) { } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/platform/SystemInformationController.java ================================================ /** * 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.web.rest.server.api.platform; import java.util.Map; import javax.servlet.http.HttpSession; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.api.platform.PlatformInformationAPI; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @Slf4j @RestController @RequestMapping("/API/system/information") public class SystemInformationController extends AbstractRESTController { @GetMapping public Map getPlatformInfo(HttpSession session) { try { return getPlatformInformationAPI(getApiSession(session)).getPlatformInformation(); } catch (final BonitaException e) { throw new APIException(e); } } protected PlatformInformationAPI getPlatformInformationAPI(APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getPlatformInformationAPI(apiSession); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/profile/APIProfile.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.profile; import java.util.List; import org.bonitasoft.web.rest.model.portal.page.PageItem; import org.bonitasoft.web.rest.model.portal.profile.ProfileDefinition; import org.bonitasoft.web.rest.model.portal.profile.ProfileItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.api.deployer.DeployerFactory; import org.bonitasoft.web.rest.server.datastore.ComposedDatastore; import org.bonitasoft.web.rest.server.datastore.profile.GetProfileHelper; import org.bonitasoft.web.rest.server.datastore.profile.SearchProfilesHelper; import org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor; import org.bonitasoft.web.rest.server.engineclient.EngineClientFactory; import org.bonitasoft.web.rest.server.engineclient.ProfileEngineClient; import org.bonitasoft.web.rest.server.framework.api.APIHasGet; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Nicolas Tith * @author Séverin Moussel */ public class APIProfile extends ConsoleAPI implements APIHasGet, APIHasSearch { @Override protected void fillDeploys(ProfileItem item, List deploys) { addDeployer(getDeployerFactory().createUserDeployer(PageItem.ATTRIBUTE_CREATED_BY_USER_ID)); addDeployer(getDeployerFactory().createUserDeployer(PageItem.ATTRIBUTE_UPDATED_BY_USER_ID)); super.fillDeploys(item, deploys); } protected DeployerFactory getDeployerFactory() { return new DeployerFactory(getEngineSession()); } @Override protected ComposedDatastore defineDefaultDatastore() { final ProfileEngineClient profileClient = createProfileEngineClient(); final ComposedDatastore datastore = new ComposedDatastore<>(); datastore.setGetHelper(new GetProfileHelper(profileClient)); datastore.setSearchHelper(new SearchProfilesHelper(profileClient)); return datastore; } private ProfileEngineClient createProfileEngineClient() { return new EngineClientFactory(new EngineAPIAccessor(getEngineSession())).createProfileEngineClient(); } @Override protected ItemDefinition defineItemDefinition() { return ProfileDefinition.get(); } @Override public String defineDefaultSearchOrder() { // FIXME Use an engine descriptor return "name ASC"; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/profile/APIProfileMember.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.profile; import java.util.List; import org.bonitasoft.web.rest.model.portal.profile.ProfileMemberDefinition; import org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem; import org.bonitasoft.web.rest.server.api.deployer.DeployerFactory; import org.bonitasoft.web.rest.server.datastore.ComposedDatastore; import org.bonitasoft.web.rest.server.datastore.profile.member.AddProfileMemberHelper; import org.bonitasoft.web.rest.server.datastore.profile.member.DeleteProfileMemberHelper; import org.bonitasoft.web.rest.server.datastore.profile.member.SearchProfileMembersHelper; import org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor; import org.bonitasoft.web.rest.server.engineclient.EngineClientFactory; import org.bonitasoft.web.rest.server.engineclient.ProfileMemberEngineClient; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Nicolas Tith * @author Séverin Moussel */ public class APIProfileMember extends AbstractAPIMember { @Override protected ItemDefinition defineItemDefinition() { return ProfileMemberDefinition.get(); } @Override protected Datastore defineDefaultDatastore() { ProfileMemberEngineClient engineClient = createProfileMemberEngineClient(); ComposedDatastore datastore = new ComposedDatastore<>(); datastore.setAddHelper(new AddProfileMemberHelper(engineClient)); datastore.setDeleteHelper(new DeleteProfileMemberHelper(engineClient)); datastore.setSearchHelper(new SearchProfileMembersHelper(engineClient)); return datastore; } @Override public String defineDefaultSearchOrder() { return ""; } @Override protected void fillDeploys(final ProfileMemberItem item, final List deploys) { /* * Need to be done there and not in constructor * because need the engine session which is set * by setter instead of being injected in API constructor... */ addDeployer(getDeployerFactory().createProfileDeployer(ProfileMemberItem.ATTRIBUTE_PROFILE_ID)); super.fillDeploys(item, deploys); } protected DeployerFactory getDeployerFactory() { return new DeployerFactory(getEngineSession()); } private ProfileMemberEngineClient createProfileMemberEngineClient() { return new EngineClientFactory(new EngineAPIAccessor(getEngineSession())) .createProfileMemberEngineClient(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/profile/AbstractAPIMember.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.profile; import static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.ATTRIBUTE_GROUP_ID; import static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.ATTRIBUTE_ROLE_ID; import static org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem.ATTRIBUTE_USER_ID; import java.util.Arrays; import java.util.List; import java.util.Map; import org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.rest.server.datastore.organization.GroupDatastore; import org.bonitasoft.web.rest.server.datastore.organization.RoleDatastore; import org.bonitasoft.web.rest.server.datastore.organization.UserDatastore; import org.bonitasoft.web.rest.server.framework.api.APIHasAdd; import org.bonitasoft.web.rest.server.framework.api.APIHasDelete; import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.exception.APIAttributesException; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public abstract class AbstractAPIMember extends ConsoleAPI implements APIHasAdd, APIHasSearch, APIHasDelete { // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected void checkAttributes(final APIID userId, final APIID roleId, final APIID groupId) { checkUserAttributeIsAloneOrNull(userId, roleId, groupId); checkAnAttributeIsSet(userId, roleId, groupId); } private void checkUserAttributeIsAloneOrNull(final APIID userId, final APIID roleId, final APIID groupId) { if (userId != null && (roleId != null || groupId != null)) { throw new APIAttributesException(Arrays.asList(ATTRIBUTE_USER_ID, ATTRIBUTE_ROLE_ID, ATTRIBUTE_GROUP_ID), "User attribute must be alone or null"); } } private void checkAnAttributeIsSet(final APIID userId, final APIID roleId, final APIID groupId) { if (userId == null && roleId == null && groupId == null) { throw new APIAttributesException(Arrays.asList(ATTRIBUTE_USER_ID, ATTRIBUTE_ROLE_ID, ATTRIBUTE_GROUP_ID), "At least one attribute must be set"); } } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CRUDS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public T add(final T item) { checkAttributes(item.getUserId(), item.getRoleId(), item.getGroupId()); return super.add(item); } @Override public void delete(final List ids) { // FIXME : uncomment when id in engine is deleted // for (APIID apiid : ids) { // checkAttributes(apiid.getPartAsAPIID(ATTRIBUTE_USER_ID), apiid.getPartAsAPIID(ATTRIBUTE_ROLE_ID), apiid.getPartAsAPIID(ATTRIBUTE_GROUP_ID)); // } super.delete(ids); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { checkUserAttributeIsAloneOrNull( APIID.makeAPIID(filters.get(ATTRIBUTE_USER_ID)), APIID.makeAPIID(filters.get(ATTRIBUTE_ROLE_ID)), APIID.makeAPIID(filters.get(ATTRIBUTE_GROUP_ID))); return super.search(page, resultsByPage, search, orders, filters); } @Override protected void fillDeploys(final T item, final List deploys) { if (isDeployable(ATTRIBUTE_USER_ID, deploys, item)) { item.setDeploy(ATTRIBUTE_USER_ID, new UserDatastore(getEngineSession()).get(item.getUserId())); } if (isDeployable(ATTRIBUTE_ROLE_ID, deploys, item)) { item.setDeploy(ATTRIBUTE_ROLE_ID, new RoleDatastore(getEngineSession()).get(item.getRoleId())); } if (isDeployable(ATTRIBUTE_GROUP_ID, deploys, item)) { item.setDeploy(ATTRIBUTE_GROUP_ID, new GroupDatastore(getEngineSession()).get(item.getGroupId())); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/resource/ErrorMessage.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.resource; import static org.bonitasoft.web.toolkit.client.common.json.JSonUtil.escape; /** * Representation for error entity * * @author Colin Puy */ public class ErrorMessage { private String exception; // might be 'type' with simple name of exception private String message; // DO NOT PUT stacktrace, this is not coherent with old API toolkit but as a client of REST API, I do not need stacktrace. public ErrorMessage() { // empty constructor for json serialization } public ErrorMessage(final Throwable t) { if (t != null) { exception = t.getClass().toString(); setMessage(t.getMessage()); } } public String getException() { return exception; } public void setException(final String exception) { this.exception = exception; } public String getMessage() { return message; } public void setMessage(final String message) { this.message = escape(message); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/resource/ErrorMessageWithExplanations.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.resource; import java.util.List; /** * Representation for error entity * * @author Colin Puy */ public class ErrorMessageWithExplanations extends ErrorMessage { private List explanations; // DO NOT PUT stacktrace, this is not coherent with old API toolkit but as a client of REST API, I do not need stacktrace. public ErrorMessageWithExplanations() { // empty constructor for json serialization } public ErrorMessageWithExplanations(final Throwable t) { super(t); } public List getExplanations() { return explanations; } public void setExplanations(final List explanations) { this.explanations = explanations; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/system/APII18nLocale.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.system; import java.util.LinkedList; import java.util.Map; import org.bonitasoft.console.common.server.i18n.I18n; import org.bonitasoft.web.rest.server.framework.API; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.i18n.model.I18nLocaleDefinition; import org.bonitasoft.web.toolkit.client.common.i18n.model.I18nLocaleItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Julien Mege */ // TODO add url pathParameter to load available locales by application (portal, platform, ...) public class APII18nLocale extends API { private static final String DEFAULT_APPLICATION = "portal"; @Override protected ItemDefinition defineItemDefinition() { return I18nLocaleDefinition.get(); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final Map availableLocales = I18n.getInstance().getAvailableLocalesFor(DEFAULT_APPLICATION); final LinkedList items = new LinkedList<>(); for (final String locale : availableLocales.keySet()) { final String name = availableLocales.get(locale); if (search == null || search.length() == 0 || locale.contains(search) || name.contains(search)) { items.add(new I18nLocaleItem(locale, availableLocales.get(locale))); } } return new ItemSearchResult<>(page * resultsByPage, resultsByPage, items.size(), items); } @Override public String defineDefaultSearchOrder() { return ""; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/system/APISession.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.system; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import org.bonitasoft.console.common.server.auth.AuthenticationManagerProperties; import org.bonitasoft.web.rest.server.api.ConsoleAPI; import org.bonitasoft.web.toolkit.client.common.json.JSonSerializer; import org.bonitasoft.web.toolkit.client.common.session.SessionDefinition; import org.bonitasoft.web.toolkit.client.common.session.SessionItem; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Definitions; /** * @author Julien Mege */ public class APISession extends ConsoleAPI { final BonitaVersion bonitaVersion = new BonitaVersion(new VersionFile()); @Override protected SessionDefinition defineItemDefinition() { return (SessionDefinition) Definitions.get(SessionDefinition.TOKEN); } @Override public SessionItem get(final APIID unusedId) { final org.bonitasoft.engine.session.APISession apiSession = getEngineSession(); final SessionItem session = new SessionItem(); if (apiSession != null) { session.setAttribute(SessionItem.ATTRIBUTE_SESSIONID, String.valueOf(apiSession.getId())); session.setAttribute(SessionItem.ATTRIBUTE_USERID, String.valueOf(apiSession.getUserId())); session.setAttribute(SessionItem.ATTRIBUTE_USERNAME, apiSession.getUserName()); session.setAttribute(SessionItem.ATTRIBUTE_IS_TECHNICAL_USER, String.valueOf(apiSession.isTechnicalUser())); session.setAttribute(SessionItem.ATTRIBUTE_IS_GUEST_USER, String.valueOf(isGuestUser(apiSession.getUserName()))); session.setAttribute(SessionItem.ATTRIBUTE_VERSION, getVersion()); session.setAttribute(SessionItem.ATTRIBUTE_BRANDING_VERSION, getBrandingVersion()); session.setAttribute(SessionItem.ATTRIBUTE_BRANDING_VERSION_WITH_DATE, getBrandingVersionWithDate()); session.setAttribute(SessionItem.ATTRIBUTE_COPYRIGHT, getCopyright()); session.setAttribute(SessionItem.ATTRIBUTE_CONF, getLogoutConfiguration(apiSession)); } return session; } protected boolean isGuestUser(final String loggedInUsername) { return false; } protected AuthenticationManagerProperties getAuthenticationManagerProperties() { return AuthenticationManagerProperties.getProperties(); } public String getLogoutConfiguration(final org.bonitasoft.engine.session.APISession apiSession) { if (!apiSession.isTechnicalUser() && isLogoutDisabled()) { return JSonSerializer.serialize(singletonList(AuthenticationManagerProperties.LOGOUT_DISABLED)); } return JSonSerializer.serialize(emptyList()); } /** * enable to know if the logout button is visible or not */ protected boolean isLogoutDisabled() { return getAuthenticationManagerProperties().isLogoutDisabled(); } public String getVersion() { return bonitaVersion.getVersion(); } public String getBrandingVersion() { return bonitaVersion.getBrandingVersion(); } public String getBrandingVersionWithDate() { return bonitaVersion.getBrandingVersionWithUpdate(); } public String getCopyright() { return bonitaVersion.getCopyright(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/system/BonitaVersion.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.system; import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Vincent Elcrin */ public class BonitaVersion { private static final Logger LOGGER = LoggerFactory.getLogger(BonitaVersion.class.getName()); private List metadata; private final VersionFile versionFile; public BonitaVersion(final VersionFile file) { this.versionFile = file; } private List read(VersionFile versionFile) { List result = new ArrayList<>(); try (Stream lines = new BufferedReader(new InputStreamReader(versionFile.getStream())).lines()) { result = lines.collect(Collectors.toList()); } catch (final Exception e) { if (LOGGER.isWarnEnabled()) { LOGGER.warn("Unable to read the file VERSION", e); } } return result; } public String getVersion() { if (metadata == null) { metadata = read(versionFile); } if (metadata.size() > 0) { return metadata.get(0).trim(); } else { return ""; } } public String getBrandingVersion() { String brandingVersionWithDate = getBrandingVersionWithUpdate(); return brandingVersionWithDate.substring(0, brandingVersionWithDate.indexOf("-")); } public String getBrandingVersionWithUpdate() { if (metadata == null) { metadata = read(versionFile); } if (metadata.size() > 1) { return metadata.get(1).trim(); } else { return ""; } } public String getCopyright() { if (metadata == null) { metadata = read(versionFile); } if (metadata.size() > 2) { return metadata.get(2).trim(); } else { return ""; } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/system/I18nTranslationController.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.system; import java.util.List; import java.util.stream.Collectors; import org.bonitasoft.console.common.server.i18n.I18n; import org.bonitasoft.web.rest.server.QueryParameterUtils; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * @author Julien Mege */ @RestController @RequestMapping("/API/system/i18ntranslation") public class I18nTranslationController extends AbstractRESTController { @GetMapping public List getI18nTranslation(@RequestParam(value = "f", required = false) List filters) { String locale = QueryParameterUtils.extractMandatoryStringFilter(filters, "locale"); return getI18n().getLocale(AbstractI18n.stringToLocale(locale)) .entrySet().stream() .map(entry -> new Translation(entry.getKey(), entry.getValue())) .collect(Collectors.toList()); } protected I18n getI18n() { return I18n.getInstance(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/system/MaintenanceController.java ================================================ /** * Copyright (C) 2023 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.web.rest.server.api.system; import javax.servlet.http.HttpSession; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.api.MaintenanceAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.maintenance.MaintenanceDetails; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.system.MaintenanceDetailsClient; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import org.springframework.web.server.ResponseStatusException; @Slf4j @RestController @RequestMapping("/API/system/maintenance") public class MaintenanceController extends AbstractRESTController { @GetMapping public MaintenanceDetails getMaintenanceDetails(HttpSession session) { APISession apiSession = getApiSession(session); try { return getMaintenanceAPI(apiSession).getMaintenanceDetails(); } catch (BonitaException e) { String errorMessage = "Error while getting the maintenance info"; log.error(errorMessage, e); throw new ResponseStatusException( HttpStatus.INTERNAL_SERVER_ERROR, errorMessage); } } @PutMapping public MaintenanceDetails changeMaintenanceState(@RequestBody MaintenanceDetailsClient maintenanceInfo, HttpSession session) { APISession apiSession = getApiSession(session); try { MaintenanceAPI maintenanceAPI = getMaintenanceAPI(apiSession); MaintenanceDetails currentMaintenanceDetails = maintenanceAPI.getMaintenanceDetails(); if (maintenanceInfo.getMaintenanceState() != null && currentMaintenanceDetails.getMaintenanceState() != maintenanceInfo.getMaintenanceState()) { //only enable/disable maintenance mode if needed if (MaintenanceDetails.State.ENABLED == maintenanceInfo.getMaintenanceState()) { maintenanceAPI.enableMaintenanceMode(); } else { maintenanceAPI.disableMaintenanceMode(); } } if (maintenanceInfo.isMaintenanceMessageActive() != null && currentMaintenanceDetails.isMaintenanceMessageActive() != maintenanceInfo .isMaintenanceMessageActive()) { //only update if different if (maintenanceInfo.isMaintenanceMessageActive()) { maintenanceAPI.enableMaintenanceMessage(); } else { maintenanceAPI.disableMaintenanceMessage(); } } if (maintenanceInfo.getMaintenanceMessage() != null && currentMaintenanceDetails.getMaintenanceMessage() != maintenanceInfo .getMaintenanceMessage()) { //only update if different maintenanceAPI.updateMaintenanceMessage(maintenanceInfo.getMaintenanceMessage()); } return maintenanceAPI.getMaintenanceDetails(); } catch (BonitaException e) { String errorMessage = "Error while setting the maintenance state"; log.error(errorMessage, e); throw new ResponseStatusException( HttpStatus.INTERNAL_SERVER_ERROR, errorMessage); } } protected MaintenanceAPI getMaintenanceAPI(APISession apiSession) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getMaintenanceAPI(apiSession); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/system/Translation.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.system; /** * @author Julien Mege */ public class Translation { private String key; private String value; public Translation(String key, String value) { this.key = key; this.value = value; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/system/VersionFile.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.system; import java.io.InputStream; /** * @author Vincent Elcrin */ public class VersionFile { public InputStream getStream() { return VersionFile.class.getClassLoader().getResourceAsStream("VERSION"); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/tenant/TenantResourceItem.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.tenant; import java.io.Serial; import java.io.Serializable; import java.time.format.DateTimeFormatter; import lombok.Getter; import lombok.Setter; import org.bonitasoft.engine.tenant.TenantResource; import org.bonitasoft.engine.tenant.TenantResourceState; import org.bonitasoft.engine.tenant.TenantResourceType; /** * @author Anthony Birembaut */ @Getter public class TenantResourceItem implements Serializable { @Serial private static final long serialVersionUID = -2269943868521108237L; @Setter private String id; @Setter private String name; @Setter private TenantResourceType type; @Setter private TenantResourceState state; @Setter private String lastUpdatedBy; private final String lastUpdateDate; @Setter private String fileUpload; public TenantResourceItem(final TenantResource tenantResource) { this(tenantResource, ""); } public TenantResourceItem(final TenantResource tenantResource, String fileUpload) { id = String.valueOf(tenantResource.getId()); name = tenantResource.getName(); type = tenantResource.getType(); lastUpdatedBy = String.valueOf(tenantResource.getLastUpdatedBy()); lastUpdateDate = tenantResource.getLastUpdateDate().format(DateTimeFormatter.ISO_DATE_TIME); state = tenantResource.getState(); this.fileUpload = fileUpload; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/CommonDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Julien Mege */ public abstract class CommonDatastore extends Datastore { private APISession engineSession; /** * Default Constructor. * * @param engineSession * The session that will allow to access the engine SDK */ public CommonDatastore(final APISession engineSession) { this.engineSession = engineSession; } /** * @return the engineSession */ protected final APISession getEngineSession() { return this.engineSession; } protected void addStringFilterToSearchBuilder(final Map filters, final SearchOptionsBuilder builder, final String filterName, final String engineAttributeName) { if (filters != null && filters.containsKey(filterName)) { final String filterValue = filters.get(filterName); if (filterValue != null) { if (filterValue.startsWith(">")) { builder.greaterThan(engineAttributeName, getFilterValueWithoutFirstCharacter(filterValue)); } else if (filterValue.startsWith("<")) { builder.lessThan(engineAttributeName, getFilterValueWithoutFirstCharacter(filterValue)); } else { builder.filter(engineAttributeName, filterValue); } } } } private String getFilterValueWithoutFirstCharacter(final String filterValue) { return filterValue.substring(1).trim(); } protected void addLongFilterToSearchBuilder(final Map filters, final SearchOptionsBuilder builder, final String filterName, final String engineAttributeName) { if (filters != null && filters.containsKey(filterName)) { final String filterValue = filters.get(filterName); if (filterValue != null) { if (filterValue.startsWith(">")) { builder.greaterThan(engineAttributeName, Long.valueOf(getFilterValueWithoutFirstCharacter(filterValue))); } else if (filterValue.startsWith("<")) { builder.lessThan(engineAttributeName, Long.valueOf(getFilterValueWithoutFirstCharacter(filterValue))); } else { builder.filter(engineAttributeName, Long.valueOf(filterValue)); } } } } protected abstract C convertEngineToConsoleItem(E item); protected ItemSearchResult convertEngineToConsoleSearch(final int page, final int resultsByPage, final SearchResult engineSearchResults) { return new ItemSearchResult<>( page, resultsByPage, engineSearchResults.getCount(), convertEngineToConsoleItemsList(engineSearchResults.getResult())); } protected List convertEngineToConsoleItemsList(final List engineSearchResults) { final List consoleSearchResults = new ArrayList<>(); for (final E engineItem : engineSearchResults) { consoleSearchResults.add(convertEngineToConsoleItem(engineItem)); } return consoleSearchResults; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/ComposedDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore; import java.util.List; import java.util.Map; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIMethodNotAllowedException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Vincent Elcrin */ public class ComposedDatastore extends Datastore implements DatastoreHasGet, DatastoreHasSearch, DatastoreHasAdd, DatastoreHasUpdate, DatastoreHasDelete { private DatastoreHasGet getHelper; private DatastoreHasSearch searchHelper; private DatastoreHasUpdate updateHelper; private DatastoreHasAdd addHelper; private DatastoreHasDelete deleteHelper; @Override public T get(APIID id) { if (getHelper != null) { return getHelper.get(id); } throw new APIMethodNotAllowedException("GET method not allowed."); } @Override public ItemSearchResult search(int page, int resultsByPage, String search, String orders, Map filters) { if (searchHelper != null) { return searchHelper.search(page, resultsByPage, search, orders, filters); } throw new APIMethodNotAllowedException("SEARCH method not allowed."); } @Override public T add(T item) { if (addHelper != null) { return addHelper.add(item); } throw new APIMethodNotAllowedException("ADD method not allowed."); } @Override public T update(APIID id, Map attributes) { if (updateHelper != null) { return updateHelper.update(id, attributes); } throw new APIMethodNotAllowedException("UPDATE method not allowed."); } @Override public void delete(List ids) { if (deleteHelper != null) { deleteHelper.delete(ids); } else { throw new APIMethodNotAllowedException("DELETE method not allowed."); } } public void setGetHelper(DatastoreHasGet getHelper) { this.getHelper = getHelper; } public void setSearchHelper(DatastoreHasSearch searchHelper) { this.searchHelper = searchHelper; } public void setAddHelper(DatastoreHasAdd addHelper) { this.addHelper = addHelper; } public void setDeleteHelper(DatastoreHasDelete deleteHelper) { this.deleteHelper = deleteHelper; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationDataStore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.application; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.business.application.Application; import org.bonitasoft.engine.business.application.ApplicationCreator; import org.bonitasoft.engine.business.application.ApplicationLink; import org.bonitasoft.engine.business.application.ApplicationLinkCreator; import org.bonitasoft.engine.business.application.ApplicationLinkUpdater; import org.bonitasoft.engine.business.application.ApplicationNotFoundException; import org.bonitasoft.engine.business.application.ApplicationPage; import org.bonitasoft.engine.business.application.ApplicationUpdater; import org.bonitasoft.engine.business.application.IApplication; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.application.AbstractApplicationItem; import org.bonitasoft.web.rest.model.application.ApplicationDefinition; import org.bonitasoft.web.rest.model.application.ApplicationItem; import org.bonitasoft.web.rest.model.application.ApplicationLinkItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.datastore.utils.Sorts; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Elias Ricken de Medeiros */ public class ApplicationDataStore extends CommonDatastore implements DatastoreHasAdd, DatastoreHasUpdate, DatastoreHasGet, DatastoreHasSearch, DatastoreHasDelete { private final ApplicationAPI applicationAPI; private final ApplicationItemConverter converter; private final PageAPI pageAPI; private static final String CUSTOMPAGE_HOME = "custompage_home"; public ApplicationDataStore(final APISession engineSession, final ApplicationAPI applicationAPI, final PageAPI pageAPI, final ApplicationItemConverter converter) { super(engineSession); this.applicationAPI = applicationAPI; this.pageAPI = pageAPI; this.converter = converter; } @Override public void delete(final List ids) { try { for (final APIID id : ids) { applicationAPI.deleteApplication(id.toLong()); } } catch (final BonitaException e) { if (e.getCause() instanceof ApplicationNotFoundException) { throw new APIItemNotFoundException(ApplicationDefinition.TOKEN); } else { throw new APIException(e); } } } @Override public AbstractApplicationItem get(final APIID id) { try { final IApplication application = applicationAPI.getIApplication(id.toLong()); return converter.toApplicationItem(application); } catch (final BonitaException e) { throw new APIException(e); } } /** * @deprecated as of 9.0.0, Applications should be created at startup. */ @Override @Deprecated(since = "9.0.0") public AbstractApplicationItem add(final AbstractApplicationItem item) { if (item instanceof ApplicationItem legacy) { return add(legacy); } else if (item instanceof ApplicationLinkItem link) { return add(link); } else { // should not occur anyway throw new APIException("Unknown application type."); } } /** * @deprecated as of 9.0.0, Applications should be created at startup. */ @Deprecated(since = "9.0.0") public ApplicationItem add(final ApplicationItem item) { try { final Page homePageDef = pageAPI.getPageByName(CUSTOMPAGE_HOME); final ApplicationCreator creator = converter.toApplicationCreator(item); final Application application = applicationAPI.createApplication(creator); final ApplicationPage appHomePage = applicationAPI.createApplicationPage(application.getId(), homePageDef.getId(), "home"); applicationAPI.setApplicationHomePage(application.getId(), appHomePage.getId()); var converted = converter.toApplicationItem(application); if (converted instanceof ApplicationItem res) { return res; } else { // should not occur anyway throw new APIException("Use dedicated API for application links."); } } catch (final BonitaException e) { throw new APIException(e); } } /** * @deprecated as of 9.0.0, Applications should be created at startup. */ @Deprecated(since = "9.0.0") public ApplicationLinkItem add(final ApplicationLinkItem item) { try { final ApplicationLinkCreator creator = converter.toApplicationLinkCreator(item); final ApplicationLink application = applicationAPI.createApplicationLink(creator); var converted = converter.toApplicationItem(application); if (converted instanceof ApplicationLinkItem res) { return res; } else { // should not occur anyway throw new APIException("Use dedicated API for legacy applications."); } } catch (final BonitaException e) { throw new APIException(e); } } /** * @deprecated as of 9.0.0, Applications should be updated at startup. */ @Override @Deprecated(since = "9.0.0") public AbstractApplicationItem update(final APIID id, final Map attributes) { try { // no way to know the application type without getting it first IApplication app = applicationAPI.getIApplication(id.toLong()); if (app instanceof Application) { final ApplicationUpdater applicationUpdater = converter.toApplicationUpdater(attributes); final Application application = applicationAPI.updateApplication(id.toLong(), applicationUpdater); return converter.toApplicationItem(application); } else if (app instanceof ApplicationLink) { final ApplicationLinkUpdater applicationUpdater = converter.toApplicationLinkUpdater(attributes); final ApplicationLink link = applicationAPI.updateApplicationLink(id.toLong(), applicationUpdater); return converter.toApplicationItem(link); } else { // should not occur anyway throw new APIException("Unknown application type."); } } catch (final BonitaException e) { throw new APIException(e); } } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { // Build search final SearchOptionsCreator creator = makeSearchOptionCreator(page, resultsByPage, search, orders, filters); // Run search depending on filters passed SearchResult searchResult; try { searchResult = runSearch(creator); // Convert to ConsoleItems return new ItemSearchResult<>(page, resultsByPage, searchResult.getCount(), convertEngineToConsoleItemsList(searchResult.getResult())); } catch (final SearchException e) { throw new APIException(e); } } protected SearchOptionsCreator makeSearchOptionCreator(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { return new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders, getSearchDescriptorConverter()), new Filters(filters, new ApplicationFilterCreator(getSearchDescriptorConverter()))); } protected SearchResult runSearch(final SearchOptionsCreator creator) throws SearchException { return applicationAPI.searchIApplications(creator.create()); } protected ApplicationSearchDescriptorConverter getSearchDescriptorConverter() { return new ApplicationSearchDescriptorConverter(); } @Override protected AbstractApplicationItem convertEngineToConsoleItem(final IApplication item) { return new ApplicationItemConverter(new BonitaHomeFolderAccessor()).toApplicationItem(item); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationDataStoreCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.application; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; /** * @author Elias Ricken de Medeiros */ public class ApplicationDataStoreCreator { public ApplicationDataStore create(final APISession session) { ApplicationAPI applicationAPI; PageAPI pageAPI; try { applicationAPI = getLivingApplicationAPI(session); pageAPI = getCustomPageAPI(session); return new ApplicationDataStore(session, applicationAPI, pageAPI, getApplicationConverter()); } catch (final BonitaException e) { throw new APIException(e); } } protected PageAPI getCustomPageAPI(APISession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getCustomPageAPI(session); } protected ApplicationAPI getLivingApplicationAPI(APISession session) throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getLivingApplicationAPI(session); } protected ApplicationItemConverter getApplicationConverter() { return new ApplicationItemConverter(new BonitaHomeFolderAccessor()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationFilterCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.application; import org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator; class ApplicationFilterCreator extends GenericFilterCreator { ApplicationFilterCreator(final ApplicationSearchDescriptorConverter converter) { super(converter); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationItemConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.application; import java.util.Map; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.IconDescriptor; import org.bonitasoft.engine.business.application.Application; import org.bonitasoft.engine.business.application.ApplicationCreator; import org.bonitasoft.engine.business.application.ApplicationLinkCreator; import org.bonitasoft.engine.business.application.ApplicationLinkUpdater; import org.bonitasoft.engine.business.application.ApplicationUpdater; import org.bonitasoft.engine.business.application.IApplication; import org.bonitasoft.web.rest.model.application.AbstractApplicationItem; import org.bonitasoft.web.rest.model.application.ApplicationItem; import org.bonitasoft.web.rest.model.application.ApplicationLinkItem; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasIcon; /** * @author Elias Ricken de Medeiros */ public class ApplicationItemConverter { protected BonitaHomeFolderAccessor bonitaHomeFolderAccessor; public ApplicationItemConverter(BonitaHomeFolderAccessor bonitaHomeFolderAccessor) { this.bonitaHomeFolderAccessor = bonitaHomeFolderAccessor; } public AbstractApplicationItem toApplicationItem(final IApplication application) { final AbstractApplicationItem item; if (application instanceof Application legacy) { var legacyItem = new ApplicationItem(); item = legacyItem; if (legacy.getHomePageId() != null) { legacyItem.setHomePageId(legacy.getHomePageId()); } else { legacyItem.setHomePageId(-1L); } if (legacy.getLayoutId() != null) { legacyItem.setLayoutId(legacy.getLayoutId()); } else { legacyItem.setLayoutId(-1L); } if (legacy.getThemeId() != null) { legacyItem.setThemeId(legacy.getThemeId()); } else { legacyItem.setThemeId(-1L); } } else { item = new ApplicationLinkItem(); } item.setId(application.getId()); item.setToken(application.getToken()); item.setDisplayName(application.getDisplayName()); item.setVersion(application.getVersion()); item.setDescription(application.getDescription()); item.setIcon(application.hasIcon() ? ApplicationItem.ICON_PATH_API_PREFIX + application.getId() + "?t=" + application.getLastUpdateDate().getTime() : ""); item.setCreationDate(Long.toString(application.getCreationDate().getTime())); item.setCreatedBy(application.getCreatedBy()); item.setLastUpdateDate(Long.toString(application.getLastUpdateDate().getTime())); item.setUpdatedBy(application.getUpdatedBy()); item.setState(application.getState()); item.setVisibility(application.getVisibility().name()); item.setEditable(application.isEditable()); if (application.getProfileId() != null) { item.setProfileId(application.getProfileId()); } else { item.setProfileId(-1L); } return item; } /** * @deprecated ApplicationCreator should no longer be used. Since 9.0.0, Applications should be created at startup. */ @Deprecated(since = "10.2.0") public ApplicationCreator toApplicationCreator(final ApplicationItem appItem) { final ApplicationCreator creator = new ApplicationCreator(appItem.getToken(), appItem.getDisplayName(), appItem.getVersion()); creator.setDescription(appItem.getDescription()); creator.setProfileId(appItem.getProfileId().toLong()); return creator; } /** * @deprecated ApplicationCreator should no longer be used. Since 9.0.0, Applications should be created at startup. */ @Deprecated(since = "10.2.0") public ApplicationLinkCreator toApplicationLinkCreator(final ApplicationLinkItem appLinkItem) { final ApplicationLinkCreator creator = new ApplicationLinkCreator(appLinkItem.getToken(), appLinkItem.getDisplayName(), appLinkItem.getVersion()); creator.setDescription(appLinkItem.getDescription()); creator.setProfileId(appLinkItem.getProfileId().toLong()); return creator; } /** * @deprecated ApplicationUpdater should no longer be used. Since 9.0.0, Applications should be created at startup. */ @Deprecated(since = "10.2.0") public ApplicationUpdater toApplicationUpdater(final Map attributes) { final ApplicationUpdater applicationUpdater = getApplicationUpdater(); if (attributes.containsKey(ApplicationItem.ATTRIBUTE_TOKEN)) { applicationUpdater.setToken(attributes.get(ApplicationItem.ATTRIBUTE_TOKEN)); } if (attributes.containsKey(ApplicationItem.ATTRIBUTE_DISPLAY_NAME)) { applicationUpdater.setDisplayName(attributes.get(ApplicationItem.ATTRIBUTE_DISPLAY_NAME)); } if (attributes.containsKey(ApplicationItem.ATTRIBUTE_DESCRIPTION)) { applicationUpdater.setDescription(attributes.get(ApplicationItem.ATTRIBUTE_DESCRIPTION)); } if (attributes.containsKey(ApplicationItem.ATTRIBUTE_PROFILE_ID)) { applicationUpdater.setProfileId(Long.parseLong(attributes.get(ApplicationItem.ATTRIBUTE_PROFILE_ID))); } if (attributes.containsKey(ApplicationItem.ATTRIBUTE_HOME_PAGE_ID)) { Long homePageId = Long.parseLong(attributes.get(ApplicationItem.ATTRIBUTE_HOME_PAGE_ID)); if (homePageId == -1) { homePageId = null; } applicationUpdater.setHomePageId(homePageId); } if (attributes.containsKey(ApplicationItem.ATTRIBUTE_STATE)) { applicationUpdater.setState(attributes.get(ApplicationItem.ATTRIBUTE_STATE)); } if (attributes.containsKey(ApplicationItem.ATTRIBUTE_VERSION)) { applicationUpdater.setVersion(attributes.get(ApplicationItem.ATTRIBUTE_VERSION)); } if (!MapUtil.isBlank(attributes, ItemHasIcon.ATTRIBUTE_ICON) && !attributes.get(ItemHasIcon.ATTRIBUTE_ICON).startsWith(ApplicationItem.ICON_PATH_API_PREFIX)) { IconDescriptor iconDescriptor = bonitaHomeFolderAccessor .getIconFromFileSystem(attributes.get(ItemHasIcon.ATTRIBUTE_ICON)); applicationUpdater.setIcon(iconDescriptor.getFilename(), iconDescriptor.getContent()); } return applicationUpdater; } /** * @deprecated ApplicationUpdater should no longer be used. Since 9.0.0, Applications should be created at startup. */ @Deprecated(since = "10.2.0") public ApplicationLinkUpdater toApplicationLinkUpdater(final Map attributes) { final ApplicationLinkUpdater applicationUpdater = getApplicationLinkUpdater(); if (attributes.containsKey(ApplicationItem.ATTRIBUTE_TOKEN)) { applicationUpdater.setToken(attributes.get(ApplicationItem.ATTRIBUTE_TOKEN)); } if (attributes.containsKey(ApplicationItem.ATTRIBUTE_DISPLAY_NAME)) { applicationUpdater.setDisplayName(attributes.get(ApplicationItem.ATTRIBUTE_DISPLAY_NAME)); } if (attributes.containsKey(ApplicationItem.ATTRIBUTE_DESCRIPTION)) { applicationUpdater.setDescription(attributes.get(ApplicationItem.ATTRIBUTE_DESCRIPTION)); } if (attributes.containsKey(ApplicationItem.ATTRIBUTE_PROFILE_ID)) { applicationUpdater.setProfileId(Long.parseLong(attributes.get(ApplicationItem.ATTRIBUTE_PROFILE_ID))); } if (attributes.containsKey(ApplicationItem.ATTRIBUTE_STATE)) { applicationUpdater.setState(attributes.get(ApplicationItem.ATTRIBUTE_STATE)); } if (attributes.containsKey(ApplicationItem.ATTRIBUTE_VERSION)) { applicationUpdater.setVersion(attributes.get(ApplicationItem.ATTRIBUTE_VERSION)); } if (!MapUtil.isBlank(attributes, ItemHasIcon.ATTRIBUTE_ICON) && !attributes.get(ItemHasIcon.ATTRIBUTE_ICON).startsWith(ApplicationItem.ICON_PATH_API_PREFIX)) { IconDescriptor iconDescriptor = bonitaHomeFolderAccessor .getIconFromFileSystem(attributes.get(ItemHasIcon.ATTRIBUTE_ICON)); applicationUpdater.setIcon(iconDescriptor.getFilename(), iconDescriptor.getContent()); } return applicationUpdater; } /** * @deprecated ApplicationUpdater should no longer be used. Since 9.0.0, Applications should be created at startup. */ @Deprecated(since = "10.2.0") protected ApplicationUpdater getApplicationUpdater() { return new ApplicationUpdater(); } /** * @deprecated ApplicationUpdater should no longer be used. Since 9.0.0, Applications should be created at startup. */ @Deprecated(since = "10.2.0") protected ApplicationLinkUpdater getApplicationLinkUpdater() { return new ApplicationLinkUpdater(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationSearchDescriptorConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.application; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.business.application.ApplicationSearchDescriptor; import org.bonitasoft.web.rest.model.application.AbstractApplicationItem; import org.bonitasoft.web.rest.model.application.ApplicationItem; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; public class ApplicationSearchDescriptorConverter implements AttributeConverter { private final Map mapping; private final Map valueTypeMapping = new HashMap<>(); public ApplicationSearchDescriptorConverter() { mapping = createMapping(); } private Map createMapping() { final Map mapping = new HashMap<>(); mapping.put(AbstractApplicationItem.ATTRIBUTE_ID, ApplicationSearchDescriptor.ID); mapping.put(AbstractApplicationItem.ATTRIBUTE_LINK, ApplicationSearchDescriptor.LINK); valueTypeMapping.put(AbstractApplicationItem.ATTRIBUTE_LINK, ItemAttribute.TYPE.BOOLEAN); mapping.put(AbstractApplicationItem.ATTRIBUTE_TOKEN, ApplicationSearchDescriptor.TOKEN); mapping.put(AbstractApplicationItem.ATTRIBUTE_DISPLAY_NAME, ApplicationSearchDescriptor.DISPLAY_NAME); mapping.put(AbstractApplicationItem.ATTRIBUTE_STATE, ApplicationSearchDescriptor.STATE); mapping.put(AbstractApplicationItem.ATTRIBUTE_CREATED_BY, ApplicationSearchDescriptor.CREATED_BY); mapping.put(AbstractApplicationItem.ATTRIBUTE_CREATION_DATE, ApplicationSearchDescriptor.CREATION_DATE); mapping.put(AbstractApplicationItem.ATTRIBUTE_LAST_UPDATE_DATE, ApplicationSearchDescriptor.LAST_UPDATE_DATE); mapping.put(AbstractApplicationItem.ATTRIBUTE_UPDATED_BY, ApplicationSearchDescriptor.UPDATED_BY); mapping.put(AbstractApplicationItem.ATTRIBUTE_VERSION, ApplicationSearchDescriptor.VERSION); mapping.put(AbstractApplicationItem.ATTRIBUTE_PROFILE_ID, ApplicationSearchDescriptor.PROFILE_ID); mapping.put(AbstractApplicationItem.FILTER_USER_ID, ApplicationSearchDescriptor.USER_ID); mapping.put(ApplicationItem.ATTRIBUTE_LAYOUT_ID, ApplicationSearchDescriptor.LAYOUT_ID); mapping.put(ApplicationItem.ATTRIBUTE_THEME_ID, ApplicationSearchDescriptor.THEME_ID); return mapping; } @Override public String convert(final String attribute) { return MapUtil.getMandatory(mapping, attribute); } @Override public Map getValueTypeMapping() { return valueTypeMapping; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuDataStore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationmenu; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.business.application.ApplicationMenu; import org.bonitasoft.engine.business.application.ApplicationMenuNotFoundException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuDefinition; import org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.datastore.utils.Sorts; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Julien Mege */ public class ApplicationMenuDataStore extends CommonDatastore implements DatastoreHasAdd, DatastoreHasUpdate, DatastoreHasGet, DatastoreHasSearch, DatastoreHasDelete { private final ApplicationAPI applicationAPI; private final ApplicationMenuItemConverter converter; public ApplicationMenuDataStore(final APISession engineSession, final ApplicationAPI applicationAPI, final ApplicationMenuItemConverter converter) { super(engineSession); this.applicationAPI = applicationAPI; this.converter = converter; } /** * @deprecated as of 9.0.0, Application menu should be created at startup. */ @Override @Deprecated(since = "9.0.0") public ApplicationMenuItem add(final ApplicationMenuItem item) { try { final ApplicationMenu applicationMenu = applicationAPI .createApplicationMenu(converter.toApplicationMenuCreator(item)); return converter.toApplicationMenuItem(applicationMenu); } catch (final BonitaException e) { throw new APIException(e); } } /** * @deprecated as of 9.0.0, Application menu should be updated at startup. */ @Override @Deprecated(since = "9.0.0") public ApplicationMenuItem update(final APIID id, final Map attributes) { try { final ApplicationMenu applicationMenu = applicationAPI.updateApplicationMenu(id.toLong(), converter.toApplicationMenuUpdater(attributes)); return converter.toApplicationMenuItem(applicationMenu); } catch (final BonitaException e) { throw new APIException(e); } } @Override public ApplicationMenuItem get(final APIID id) { try { final ApplicationMenu applicationMenu = applicationAPI.getApplicationMenu(id.toLong()); return converter.toApplicationMenuItem(applicationMenu); } catch (final BonitaException e) { throw new APIException(e); } } @Override public void delete(final List ids) { try { for (final APIID id : ids) { applicationAPI.deleteApplicationMenu(id.toLong()); } } catch (final BonitaException e) { if (e.getCause() instanceof ApplicationMenuNotFoundException) { throw new APIItemNotFoundException(ApplicationMenuDefinition.TOKEN); } else { throw new APIException(e); } } } protected ApplicationMenuSearchDescriptorConverter getSearchDescriptorConverter() { return new ApplicationMenuSearchDescriptorConverter(); } @Override protected ApplicationMenuItem convertEngineToConsoleItem(final ApplicationMenu item) { return new ApplicationMenuItemConverter().toApplicationMenuItem(item); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final SearchOptionsCreator creator = makeSearchOptionCreator(page, resultsByPage, search, orders, filters); try { final SearchResult searchResult = runSearch(creator); final List appMenuItems = convertEngineToConsoleItemsList(searchResult.getResult()); return new ItemSearchResult<>(page, resultsByPage, searchResult.getCount(), appMenuItems); } catch (final SearchException e) { throw new APIException(e); } } protected SearchOptionsCreator makeSearchOptionCreator(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { return new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders, getSearchDescriptorConverter()), new Filters(filters, new ApplicationMenuFilterCreator(getSearchDescriptorConverter()))); } protected SearchResult runSearch(final SearchOptionsCreator creator) throws SearchException { return applicationAPI.searchApplicationMenus(creator.create()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuDataStoreCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationmenu; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; /** * @author Julien Mege */ public class ApplicationMenuDataStoreCreator { public ApplicationMenuDataStore create(final APISession session) { ApplicationAPI applicationAPI; try { applicationAPI = TenantAPIAccessor.getLivingApplicationAPI(session); return new ApplicationMenuDataStore(session, applicationAPI, new ApplicationMenuItemConverter()); } catch (final BonitaException e) { throw new APIException(e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuFilterCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationmenu; import org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator; class ApplicationMenuFilterCreator extends GenericFilterCreator { ApplicationMenuFilterCreator(final ApplicationMenuSearchDescriptorConverter converter) { super(converter); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuItemConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationmenu; import java.util.Map; import org.bonitasoft.engine.business.application.ApplicationMenu; import org.bonitasoft.engine.business.application.ApplicationMenuCreator; import org.bonitasoft.engine.business.application.ApplicationMenuUpdater; import org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuItem; /** * @author Julien Mege */ public class ApplicationMenuItemConverter { public ApplicationMenuItem toApplicationMenuItem(final ApplicationMenu applicationMenu) { final ApplicationMenuItem item = new ApplicationMenuItem(); item.setId(applicationMenu.getId()); item.setApplicationId(applicationMenu.getApplicationId()); item.setDisplayName(applicationMenu.getDisplayName()); if (applicationMenu.getApplicationPageId() != null) { item.setApplicationPageId(applicationMenu.getApplicationPageId()); } else { item.setApplicationPageId("-1"); } if (applicationMenu.getParentId() != null) { item.setParentMenuId(applicationMenu.getParentId()); } else { item.setParentMenuId("-1"); } item.setMenuIndex(applicationMenu.getIndex()); return item; } public ApplicationMenuCreator toApplicationMenuCreator(final ApplicationMenuItem item) { Long applicationId = null; if (item.getApplicationId() != null && item.getApplicationId().isValidLongID()) { applicationId = item.getApplicationId().toLong(); } Long applicationPageId = null; if (item.getApplicationPageId() != null && item.getApplicationPageId().isValidLongID()) { applicationPageId = item.getApplicationPageId().toLong(); } final ApplicationMenuCreator menuCreator = new ApplicationMenuCreator(applicationId, item.getDisplayName(), applicationPageId); if (item.getParentMenuId() != null && item.getParentMenuId().isValidLongID()) { menuCreator.setParentId(item.getParentMenuId().toLong()); } return menuCreator; } public ApplicationMenuUpdater toApplicationMenuUpdater(final Map attributes) { final ApplicationMenuUpdater applicationMenuUpdater = new ApplicationMenuUpdater(); if (attributes.containsKey(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID)) { Long appPageId = Long.parseLong(attributes.get(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID)); if (appPageId == -1) { appPageId = null; } applicationMenuUpdater.setApplicationPageId(appPageId); } if (attributes.containsKey(ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME)) { applicationMenuUpdater.setDisplayName(attributes.get(ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME)); } if (attributes.containsKey(ApplicationMenuItem.ATTRIBUTE_MENU_INDEX)) { applicationMenuUpdater.setIndex(Integer.parseInt(attributes.get(ApplicationMenuItem.ATTRIBUTE_MENU_INDEX))); } if (attributes.containsKey(ApplicationMenuItem.ATTRIBUTE_PARENT_MENU_ID)) { Long parentMenuId = Long.parseLong(attributes.get(ApplicationMenuItem.ATTRIBUTE_PARENT_MENU_ID)); if (parentMenuId == -1) { parentMenuId = null; } applicationMenuUpdater.setParentId(parentMenuId); } return applicationMenuUpdater; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuSearchDescriptorConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationmenu; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.business.application.ApplicationMenuSearchDescriptor; import org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuItem; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Julien Mege */ public class ApplicationMenuSearchDescriptorConverter implements AttributeConverter { private final Map mapping; ApplicationMenuSearchDescriptorConverter() { mapping = createMapping(); } private Map createMapping() { final Map mapping = new HashMap<>(); mapping.put(ApplicationMenuItem.ATTRIBUTE_ID, ApplicationMenuSearchDescriptor.ID); mapping.put(ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME, ApplicationMenuSearchDescriptor.DISPLAY_NAME); mapping.put(ApplicationMenuItem.ATTRIBUTE_APPLICATION_ID, ApplicationMenuSearchDescriptor.APPLICATION_ID); mapping.put(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID, ApplicationMenuSearchDescriptor.APPLICATION_PAGE_ID); mapping.put(ApplicationMenuItem.ATTRIBUTE_MENU_INDEX, ApplicationMenuSearchDescriptor.INDEX); mapping.put(ApplicationMenuItem.ATTRIBUTE_PARENT_MENU_ID, ApplicationMenuSearchDescriptor.PARENT_ID); return mapping; } @Override public String convert(final String attribute) { return MapUtil.getMandatory(mapping, attribute); } @Override public Map getValueTypeMapping() { return Collections.emptyMap(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageDataStore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationpage; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.business.application.ApplicationPage; import org.bonitasoft.engine.business.application.ApplicationPageNotFoundException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.applicationpage.ApplicationPageDefinition; import org.bonitasoft.web.rest.model.applicationpage.ApplicationPageItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.datastore.utils.Sorts; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Julien Mege */ public class ApplicationPageDataStore extends CommonDatastore implements DatastoreHasAdd, DatastoreHasGet, DatastoreHasSearch, DatastoreHasDelete { private final ApplicationAPI applicationAPI; private final ApplicationPageItemConverter converter; public ApplicationPageDataStore(final APISession engineSession, final ApplicationAPI applicationAPI, final ApplicationPageItemConverter converter) { super(engineSession); this.applicationAPI = applicationAPI; this.converter = converter; } /** * @deprecated as of 9.0.0, Application page should be created at startup. */ @Override @Deprecated(since = "9.0.0") public ApplicationPageItem add(final ApplicationPageItem item) { try { final ApplicationPage applicationPage = applicationAPI.createApplicationPage( item.getApplicationId().toLong(), item.getPageId().toLong(), item.getToken()); return converter.toApplicationPageItem(applicationPage); } catch (final BonitaException e) { throw new APIException(e); } } @Override public ApplicationPageItem get(final APIID id) { try { final ApplicationPage applicationPage = applicationAPI.getApplicationPage(id.toLong()); return converter.toApplicationPageItem(applicationPage); } catch (final BonitaException e) { throw new APIException(e); } } @Override public void delete(final List ids) { try { for (final APIID id : ids) { applicationAPI.deleteApplicationPage(id.toLong()); } } catch (final BonitaException e) { if (e.getCause() instanceof ApplicationPageNotFoundException) { throw new APIItemNotFoundException(ApplicationPageDefinition.TOKEN); } else { throw new APIException(e); } } } protected ApplicationPageSearchDescriptorConverter getSearchDescriptorConverter() { return new ApplicationPageSearchDescriptorConverter(); } @Override protected ApplicationPageItem convertEngineToConsoleItem(final ApplicationPage item) { return converter.toApplicationPageItem(item); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final SearchOptionsCreator creator = makeSearchOptionCreator(page, resultsByPage, search, orders, filters); try { final SearchResult searchResult = runSearch(creator); final List appPageItems = convertEngineToConsoleItemsList(searchResult.getResult()); return new ItemSearchResult<>(page, resultsByPage, searchResult.getCount(), appPageItems); } catch (final SearchException e) { throw new APIException(e); } } protected SearchOptionsCreator makeSearchOptionCreator(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { return new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders, getSearchDescriptorConverter()), new Filters(filters, new ApplicationPageFilterCreator(getSearchDescriptorConverter()))); } protected SearchResult runSearch(final SearchOptionsCreator creator) throws SearchException { return applicationAPI.searchApplicationPages(creator.create()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageDataStoreCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationpage; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; /** * @author Julien Mege */ public class ApplicationPageDataStoreCreator { public ApplicationPageDataStore create(final APISession session) { try { final ApplicationAPI applicationAPI = TenantAPIAccessor.getLivingApplicationAPI(session); return new ApplicationPageDataStore(session, applicationAPI, new ApplicationPageItemConverter()); } catch (final BonitaException e) { throw new APIException(e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageFilterCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationpage; import org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator; class ApplicationPageFilterCreator extends GenericFilterCreator { ApplicationPageFilterCreator(final ApplicationPageSearchDescriptorConverter converter) { super(converter); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageItemConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationpage; import org.bonitasoft.engine.business.application.ApplicationPage; import org.bonitasoft.web.rest.model.applicationpage.ApplicationPageItem; /** * @author Julien Mege */ public class ApplicationPageItemConverter { public ApplicationPageItem toApplicationPageItem(final ApplicationPage applicationPage) { final ApplicationPageItem item = new ApplicationPageItem(); item.setId(applicationPage.getId()); item.setToken(applicationPage.getToken()); item.setPageId(applicationPage.getPageId()); item.setApplicationId(applicationPage.getApplicationId()); return item; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageSearchDescriptorConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationpage; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.business.application.ApplicationPageSearchDescriptor; import org.bonitasoft.web.rest.model.applicationpage.ApplicationPageItem; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Elias Ricken de Medeiros */ public class ApplicationPageSearchDescriptorConverter implements AttributeConverter { private final Map mapping; ApplicationPageSearchDescriptorConverter() { mapping = createMapping(); } private Map createMapping() { final Map mapping = new HashMap<>(); mapping.put(ApplicationPageItem.ATTRIBUTE_ID, ApplicationPageSearchDescriptor.ID); mapping.put(ApplicationPageItem.ATTRIBUTE_TOKEN, ApplicationPageSearchDescriptor.TOKEN); mapping.put(ApplicationPageItem.ATTRIBUTE_APPLICATION_ID, ApplicationPageSearchDescriptor.APPLICATION_ID); mapping.put(ApplicationPageItem.ATTRIBUTE_PAGE_ID, ApplicationPageSearchDescriptor.PAGE_ID); return mapping; } @Override public String convert(final String attribute) { return MapUtil.getMandatory(mapping, attribute); } @Override public Map getValueTypeMapping() { return Collections.emptyMap(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.process.*; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDefinition; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseItem; import org.bonitasoft.web.rest.model.bpm.cases.CaseItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public class ArchivedCaseDatastore extends CommonDatastore implements DatastoreHasGet, DatastoreHasSearch, DatastoreHasDelete { public ArchivedCaseDatastore(final APISession engineSession) { super(engineSession); } @Override protected ArchivedCaseItem convertEngineToConsoleItem(final ArchivedProcessInstance item) { final ArchivedCaseItem result = new ArchivedCaseItem(); result.setId(item.getId()); result.setLastUpdateDate(item.getLastUpdate()); result.setState(item.getState()); result.setStartDate(item.getStartDate()); result.setEndDate(item.getEndDate()); result.setProcessId(item.getProcessDefinitionId()); result.setArchivedDate(item.getArchiveDate()); result.setSourceObjectId(item.getSourceObjectId()); result.setRootCaseId(item.getRootProcessInstanceId()); result.setStartedByUserId(item.getStartedBy()); result.setStartedBySubstituteUserId(item.getStartedBySubstitute()); result.setSearchIndex1Label(item.getStringIndexLabel(1)); result.setSearchIndex2Label(item.getStringIndexLabel(2)); result.setSearchIndex3Label(item.getStringIndexLabel(3)); result.setSearchIndex4Label(item.getStringIndexLabel(4)); result.setSearchIndex5Label(item.getStringIndexLabel(5)); result.setSearchIndex1Value(item.getStringIndexValue(1)); result.setSearchIndex2Value(item.getStringIndexValue(2)); result.setSearchIndex3Value(item.getStringIndexValue(3)); result.setSearchIndex4Value(item.getStringIndexValue(4)); result.setSearchIndex5Value(item.getStringIndexValue(5)); result.setCallerId(item.getCallerId()); return result; } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final SearchOptionsBuilder builder = buildSearchOptions(page, resultsByPage, search, orders, filters); // Run search depending on filters passed final SearchResult searchResult = runSearch(filters, builder); // Convert to ConsoleItems return new ItemSearchResult<>( page, resultsByPage, searchResult.getCount(), convertEngineToConsoleItemsList(searchResult.getResult())); } protected SearchOptionsBuilder buildSearchOptions(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { // Build search final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, orders, search); addStringFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_PROCESS_NAME, ArchivedProcessInstancesSearchDescriptor.NAME); addLongFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_PROCESS_ID, ArchivedProcessInstancesSearchDescriptor.PROCESS_DEFINITION_ID); addLongFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_STARTED_BY_USER_ID, ArchivedProcessInstancesSearchDescriptor.STARTED_BY); addLongFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_SOURCE_OBJECT_ID, ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID); addLongFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_ROOT_CASE_ID, ArchivedProcessInstancesSearchDescriptor.ROOT_PROCESS_INSTANCE_ID); addAddDifferentFromRootIdFilterIfNecessary(filters, builder); addStringFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_SEARCH_INDEX_1_VALUE, ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_1); addStringFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_SEARCH_INDEX_2_VALUE, ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_2); addStringFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_SEARCH_INDEX_3_VALUE, ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_3); addStringFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_SEARCH_INDEX_4_VALUE, ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_4); addStringFilterToSearchBuilder(filters, builder, ArchivedCaseItem.ATTRIBUTE_SEARCH_INDEX_5_VALUE, ArchivedProcessInstancesSearchDescriptor.STRING_INDEX_5); addCallerFilterToSearchBuilderIfNecessary(filters, builder); return builder; } protected void addAddDifferentFromRootIdFilterIfNecessary(Map filters, SearchOptionsBuilder builder) { /* * When filtering on Root Case Id, we want all the subprocesses of the root case to be returned. * Not the root case itself. */ if (filters.containsKey(ArchivedCaseItem.ATTRIBUTE_ROOT_CASE_ID)) { builder.differentFrom(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID, MapUtil.getValueAsLong(filters, ArchivedCaseItem.ATTRIBUTE_ROOT_CASE_ID)); } } protected void addCallerFilterToSearchBuilderIfNecessary(final Map filters, final SearchOptionsBuilder builder) { /* * By default we add a caller filter of -1 to avoid having sub processes. * If caller is forced to any then we don't need to add the filter. */ if (!filters.containsKey(CaseItem.FILTER_CALLER)) { builder.filter(ArchivedProcessInstancesSearchDescriptor.CALLER_ID, -1); } else if (!"any".equalsIgnoreCase(filters.get(CaseItem.FILTER_CALLER))) { builder.filter(ArchivedProcessInstancesSearchDescriptor.CALLER_ID, MapUtil.getValueAsLong(filters, CaseItem.FILTER_CALLER)); } } // Overridden for testing protected void addLongFilterToSearchBuilder(final Map filters, final SearchOptionsBuilder builder, final String filterName, final String engineAttributeName) { super.addLongFilterToSearchBuilder(filters, builder, filterName, engineAttributeName); } // protected for testing protected SearchResult runSearch(final Map filters, final SearchOptionsBuilder builder) { try { final ProcessAPI processAPI = getProcessApi(); if (filters.containsKey(ArchivedCaseItem.FILTER_USER_ID)) { return processAPI.searchArchivedProcessInstancesInvolvingUser( MapUtil.getValueAsLong(filters, ArchivedCaseItem.FILTER_USER_ID), builder.done()); } if (filters.containsKey(ArchivedCaseItem.FILTER_SUPERVISOR_ID)) { return processAPI .searchArchivedProcessInstancesSupervisedBy( MapUtil.getValueAsLong(filters, ArchivedCaseItem.FILTER_SUPERVISOR_ID), builder.done()); } if (filters.containsKey(CaseItem.FILTER_CALLER) && !filters.containsKey(CaseItem.FILTER_STATE)) { builder.leftParenthesis() .filter(ArchivedProcessInstancesSearchDescriptor.STATE_ID, ProcessInstanceState.COMPLETED.getId()) .or() .filter(ArchivedProcessInstancesSearchDescriptor.STATE_ID, ProcessInstanceState.ABORTED.getId()) .or() .filter(ArchivedProcessInstancesSearchDescriptor.STATE_ID, ProcessInstanceState.CANCELLED.getId()) .rightParenthesis(); return processAPI.searchArchivedProcessInstancesInAllStates(builder.done()); } return processAPI.searchArchivedProcessInstances(builder.done()); } catch (final BonitaException e) { throw new APIException(e); } } @Override public ArchivedCaseItem get(final APIID id) { try { return convertEngineToConsoleItem(getProcessApi() .getArchivedProcessInstance(id.toLong())); } catch (ArchivedProcessInstanceNotFoundException e) { throw new APIItemNotFoundException(ArchivedCaseDefinition.TOKEN, id); } catch (final BonitaException e) { throw new APIException(e); } } public ArchivedCaseItem getUsingSourceObjectId(final APIID id) { try { return convertEngineToConsoleItem(getProcessApi() .getFinalArchivedProcessInstance(id.toLong())); } catch (final BonitaException e) { throw new APIException(e); } } @Override public void delete(final List ids) { try { final ProcessAPI processAPI = getProcessApi(); final List toDeleteIds = new ArrayList<>(); for (final APIID apiId : ids) { final ArchivedProcessInstance archivedProcessInstance = processAPI .getArchivedProcessInstance(apiId.toLong()); toDeleteIds.add(archivedProcessInstance.getSourceObjectId()); } processAPI.deleteArchivedProcessInstancesInAllStates(toDeleteIds); } catch (final BonitaException e) { throw new APIException(e); } } public ProcessAPI getProcessApi() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getProcessAPI(getEngineSession()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDocumentDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.document.ArchivedDocument; import org.bonitasoft.engine.bpm.document.DocumentException; import org.bonitasoft.engine.bpm.document.DocumentNotFoundException; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.identity.UserNotFoundException; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentItem; import org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.datastore.utils.Sorts; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Fabio Lombardi */ public class ArchivedCaseDocumentDatastore extends CommonDatastore implements DatastoreHasGet, DatastoreHasDelete { protected final ProcessAPI processAPI; protected SearchOptionsCreator searchOptionsCreator; /** * Default constructor. */ public ArchivedCaseDocumentDatastore(final APISession engineSession, final ProcessAPI processAPI) { super(engineSession); this.processAPI = processAPI; } // GET Method @Override public ArchivedCaseDocumentItem get(final APIID id) { try { final ArchivedDocument documentItem = processAPI.getArchivedProcessDocument(id.toLong()); return convertEngineToConsoleItem(documentItem); } catch (final BonitaException e) { throw new APIException(e); } } // GET Method for SEARCH public ItemSearchResult search(final int page, final int resultsByPage, final String search, final Map filters, final String orders) { return searchDocument(page, resultsByPage, search, filters, orders); } protected ItemSearchResult searchDocument(final int page, final int resultsByPage, final String search, final Map filters, final String orders) { try { final APIID supervisorAPIID = APIID.makeAPIID(filters.get(ArchivedCaseDocumentItem.FILTER_SUPERVISOR_ID)); final APIID archivedCaseId = APIID.makeAPIID(filters.get(ArchivedCaseDocumentItem.FILTER_ARCHIVED_CASE_ID)); if (supervisorAPIID != null) { filters.remove(ArchivedCaseDocumentItem.FILTER_SUPERVISOR_ID); } if (archivedCaseId != null && archivedCaseId.isValidLongID()) { filters.remove(ArchivedCaseDocumentItem.FILTER_ARCHIVED_CASE_ID); final ArchivedProcessInstance archivedProcessInstance = processAPI .getArchivedProcessInstance(archivedCaseId.toLong()); if (archivedProcessInstance != null) { final Long sourceCaseId = archivedProcessInstance.getSourceObjectId(); filters.put(ArchivedCaseDocumentItem.ATTRIBUTE_CASE_ID, sourceCaseId.toString()); } } searchOptionsCreator = buildSearchOptionCreator(page, resultsByPage, search, filters, orders); final SearchResult engineSearchResults; if (supervisorAPIID != null && supervisorAPIID.isValidLongID()) { engineSearchResults = processAPI.searchArchivedDocumentsSupervisedBy(supervisorAPIID.toLong(), searchOptionsCreator.create()); } else { engineSearchResults = processAPI.searchArchivedDocuments(searchOptionsCreator.create()); } return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(), convertEngineToConsoleItem(engineSearchResults.getResult())); } catch (final ArchivedProcessInstanceNotFoundException e) { throw new APIException("archivedCaseId not found. Request with bad param value."); } catch (final UserNotFoundException e) { throw new APIException("supervisor_id not found. Request with bad param value."); } catch (final SearchException e) { throw new APIException("Error while searching."); } } protected SearchOptionsCreator buildSearchOptionCreator(final int page, final int resultsByPage, final String search, final Map filters, final String orders) { return new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders, getArchivedDocumentSearchAttributeConverter()), new Filters(filters, new ArchivedCaseDocumentFilterCreator( getArchivedDocumentSearchAttributeConverter()))); } private ArchivedCaseDocumentSearchAttributeConverter getArchivedDocumentSearchAttributeConverter() { return new ArchivedCaseDocumentSearchAttributeConverter(); } // DELETE Method @Override public void delete(final List ids) { if (ids != null) { try { for (final APIID id : ids) { processAPI.deleteContentOfArchivedDocument(id.toLong()); } } catch (final DocumentNotFoundException e) { throw new APIException("Error while deleting a document. Document not found"); } catch (final DocumentException e) { throw new APIException(e); } } else { throw new APIException("Error while deleting a document. Document id not specified in the request"); } } @Override protected ArchivedCaseDocumentItem convertEngineToConsoleItem(final ArchivedDocument item) { if (item != null) { return new ArchivedCaseDocumentItemConverter().convert(item); } return null; } private List convertEngineToConsoleItem(final List result) { if (result != null) { return new ArchivedCaseDocumentItemConverter().convert(result); } return null; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDocumentFilterCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator; /** * @author Fabio Lombardi */ class ArchivedCaseDocumentFilterCreator extends GenericFilterCreator { ArchivedCaseDocumentFilterCreator(final ArchivedCaseDocumentSearchAttributeConverter converter) { super(converter); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDocumentItemConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import org.bonitasoft.engine.bpm.document.ArchivedDocument; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentItem; import org.bonitasoft.web.rest.server.datastore.converter.ItemConverter; public class ArchivedCaseDocumentItemConverter extends ItemConverter { @Override public ArchivedCaseDocumentItem convert(final ArchivedDocument engineItem) { final ArchivedCaseDocumentItem item = new ArchivedCaseDocumentItem(); item.setId(String.valueOf(engineItem.getId())); item.setCaseId(String.valueOf(engineItem.getProcessInstanceId())); item.setName(engineItem.getName()); item.setVersion(engineItem.getVersion()); item.setDescription(engineItem.getDescription()); item.setSubmittedBy(engineItem.getAuthor()); item.setFileName(engineItem.getContentFileName()); item.setCreationDate(engineItem.getCreationDate()); item.setMIMEType(engineItem.getContentMimeType()); item.setHasContent(String.valueOf(engineItem.hasContent())); item.setStorageId(engineItem.getContentStorageId()); item.setURL(engineItem.getUrl()); item.setIndex(engineItem.getIndex()); item.setSourceObjectId(engineItem.getSourceObjectId()); item.setArchivedDate(engineItem.getArchiveDate()); return item; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDocumentSearchAttributeConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.bpm.document.ArchivedDocumentsSearchDescriptor; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentItem; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Fabio Lombardi */ public class ArchivedCaseDocumentSearchAttributeConverter implements AttributeConverter { private final Map mapping; public ArchivedCaseDocumentSearchAttributeConverter() { mapping = createMapping(); } private Map createMapping() { final Map mapping = new HashMap<>(); mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_ID, ArchivedCaseDocumentItem.ATTRIBUTE_ID); mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_SUBMITTED_BY_USER_ID, ArchivedDocumentsSearchDescriptor.DOCUMENT_AUTHOR); mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_NAME, ArchivedDocumentsSearchDescriptor.DOCUMENT_NAME); mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_CREATION_DATE, ArchivedDocumentsSearchDescriptor.DOCUMENT_CREATIONDATE); mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_DESCRIPTION, ArchivedDocumentsSearchDescriptor.DOCUMENT_DESCRIPTION); mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_INDEX, ArchivedDocumentsSearchDescriptor.LIST_INDEX); mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_CASE_ID, ArchivedDocumentsSearchDescriptor.PROCESSINSTANCE_ID); mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_SOURCE_OBJECT_ID, ArchivedDocumentsSearchDescriptor.SOURCEOBJECT_ID); mapping.put(ArchivedCaseDocumentItem.ATTRIBUTE_ARCHIVED_DATE, ArchivedDocumentsSearchDescriptor.ARCHIVE_DATE); return mapping; } @Override public String convert(final String attribute) { return MapUtil.getMandatory(mapping, attribute); } @Override public Map getValueTypeMapping() { return Collections.emptyMap(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCommentDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.comment.ArchivedComment; import org.bonitasoft.engine.bpm.comment.ArchivedCommentsSearchDescriptor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCommentItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; /** * @author Paul AMAR */ public class ArchivedCommentDatastore extends CommonDatastore implements DatastoreHasSearch { /** * Conversion look up table to for sortable fields */ private static final String[][] SORTABLE_FIELDS_LUT = { { ArchivedCommentItem.ATTRIBUTE_POST_DATE, ArchivedCommentsSearchDescriptor.POSTDATE }, }; public ArchivedCommentDatastore(final APISession engineSession) { super(engineSession); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // C.R.U.D. // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* * (non-Javadoc) * @see org.bonitasoft.web.toolkit.server.api.DatastoreHasSearch#search(int, int, java.lang.String, * java.lang.String, java.util.Map) */ @Override public ItemSearchResult search(int page, int resultsByPage, String search, String orders, Map filters) { try { final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, "", search); adjustSearchBuilder(filters, builder); /** * Need to convert field name passed by API into sortable field provided by engine. */ if (orders != null && !orders.isEmpty()) { String[] sort = orders.split(" "); for (int i = 0; i < SORTABLE_FIELDS_LUT.length; i++) { if (sort[0].equals(SORTABLE_FIELDS_LUT[i][0])) { builder.sort(SORTABLE_FIELDS_LUT[i][1], Order.valueOf(sort[1])); } } } final SearchResult result = TenantAPIAccessor.getProcessAPI(getEngineSession()) .searchArchivedComments(builder.done()); final List archivedCommentList = new ArrayList<>(); for (final ArchivedComment item : result.getResult()) { final ArchivedCommentItem resultArchivedCommentItem = convertEngineToConsoleItem(item); archivedCommentList.add(resultArchivedCommentItem); } return new ItemSearchResult<>(page, resultsByPage, result.getCount(), archivedCommentList); } catch (final BonitaException e) { throw new APIException(e); } } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CONVERTS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Convert ProcessItem filters into engine filers * * @param filters * @param builder */ private void adjustSearchBuilder(final Map filters, final SearchOptionsBuilder builder) { addStringFilterToSearchBuilder(filters, builder, ArchivedCommentItem.ATTRIBUTE_PROCESS_INSTANCE_ID, ArchivedCommentsSearchDescriptor.PROCESS_INSTANCE_ID); } /* * (non-Javadoc) * @see * org.bonitasoft.console.server.credentials.bpm.CommonDatastore#convertEngineToConsoleItem(java.io.Serializable) */ @Override protected ArchivedCommentItem convertEngineToConsoleItem(final ArchivedComment item) { if (item == null) { return null; } final ArchivedCommentItem consoleItem = new ArchivedCommentItem(); // Il faudra rajouter les get() des comments consoleItem.setId(item.getId()); consoleItem.setUserId(item.getUserId()); consoleItem.setProcessInstanceId(item.getProcessInstanceId()); consoleItem.setPostDate(item.getPostDate()); consoleItem.setArchivedDate(item.getArchiveDate()); consoleItem.setContent(item.getContent()); return consoleItem; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.cases.CaseDefinition; import org.bonitasoft.web.rest.model.bpm.cases.CaseItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor; import org.bonitasoft.web.rest.server.engineclient.EngineClientFactory; import org.bonitasoft.web.rest.server.framework.api.*; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIIncorrectIdException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel * @author Celine Souchet */ public class CaseDatastore extends CommonDatastore implements DatastoreHasGet, DatastoreHasSearch, DatastoreHasDelete, DatastoreHasAdd, DatastoreHasUpdate { public CaseDatastore(final APISession engineSession) { super(engineSession); } @Override protected CaseItem convertEngineToConsoleItem(final ProcessInstance item) { return new CaseItemConverter().convert(item); } public long count(final String search, final String orders, final Map filters) { return search(0, 0, search, orders, filters).getTotal(); } /** * convenience for stubbing during unit test * * @see org.bonitasoft.web.rest.server.datastore.CommonDatastore#convertEngineToConsoleSearch(int, int, * org.bonitasoft.engine.search.SearchResult) */ @Override protected ItemSearchResult convertEngineToConsoleSearch(final int page, final int resultsByPage, final SearchResult engineSearchResults) { return super.convertEngineToConsoleSearch(page, resultsByPage, engineSearchResults); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { try { final SearchOptionsBuilder builder = buildSearchOptions(page, resultsByPage, search, orders, filters); final SearchResult searchResult = searchProcessInstances(filters, builder.done()); return convertEngineToConsoleSearch(page, resultsByPage, searchResult); } catch (final BonitaException e) { throw new APIException(e); } } protected SearchOptionsBuilder buildSearchOptions(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { // Build search final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, orders, search); addLongFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_START_DATE, ProcessInstanceSearchDescriptor.START_DATE); addLongFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_END_DATE, ProcessInstanceSearchDescriptor.END_DATE); addLongFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_LAST_UPDATE_DATE, ProcessInstanceSearchDescriptor.LAST_UPDATE); addLongFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_PROCESS_ID, ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID); addLongFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_ROOT_CASE_ID, ProcessInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID); addAddDifferentFromRootIdFilterIfNecessary(filters, builder); addStringFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_PROCESS_NAME, ProcessInstanceSearchDescriptor.NAME); addLongFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_STARTED_BY_USER_ID, ProcessInstanceSearchDescriptor.STARTED_BY); addStringFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_SEARCH_INDEX_1_VALUE, ProcessInstanceSearchDescriptor.STRING_INDEX_1); addStringFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_SEARCH_INDEX_2_VALUE, ProcessInstanceSearchDescriptor.STRING_INDEX_2); addStringFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_SEARCH_INDEX_3_VALUE, ProcessInstanceSearchDescriptor.STRING_INDEX_3); addStringFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_SEARCH_INDEX_4_VALUE, ProcessInstanceSearchDescriptor.STRING_INDEX_4); addStringFilterToSearchBuilder(filters, builder, CaseItem.ATTRIBUTE_SEARCH_INDEX_5_VALUE, ProcessInstanceSearchDescriptor.STRING_INDEX_5); addCallerFilterToSearchBuilderIfNecessary(filters, builder); builder.differentFrom(ProcessInstanceSearchDescriptor.STATE_ID, ProcessInstanceState.COMPLETED.getId()); builder.differentFrom(ProcessInstanceSearchDescriptor.STATE_ID, ProcessInstanceState.CANCELLED.getId()); builder.differentFrom(ProcessInstanceSearchDescriptor.STATE_ID, ProcessInstanceState.ABORTED.getId()); return builder; } protected void addAddDifferentFromRootIdFilterIfNecessary(Map filters, SearchOptionsBuilder builder) { /* * When filtering on Root Case Id, we want all the subprocesses of the root case to be returned. * Not the root case itself. */ if (filters.containsKey(CaseItem.ATTRIBUTE_ROOT_CASE_ID)) { builder.differentFrom(ProcessInstanceSearchDescriptor.ID, MapUtil.getValueAsLong(filters, CaseItem.ATTRIBUTE_ROOT_CASE_ID)); } } protected void addCallerFilterToSearchBuilderIfNecessary(final Map filters, final SearchOptionsBuilder builder) { /* * By default we add a caller filter of -1 to avoid having sub processes. * If caller is forced to any then we don't need to add the filter. */ if (!filters.containsKey(CaseItem.FILTER_CALLER)) { builder.filter(ProcessInstanceSearchDescriptor.CALLER_ID, -1); } else if (!"any".equalsIgnoreCase(filters.get(CaseItem.FILTER_CALLER))) { builder.filter(ProcessInstanceSearchDescriptor.CALLER_ID, MapUtil.getValueAsLong(filters, CaseItem.FILTER_CALLER)); } } protected SearchResult searchProcessInstances(final Map filters, final SearchOptions searchOptions) throws BonitaException { final ProcessAPI processAPI = getProcessAPI(); if (filters.containsKey(CaseItem.FILTER_USER_ID)) { return processAPI.searchOpenProcessInstancesInvolvingUser( MapUtil.getValueAsLong(filters, CaseItem.FILTER_USER_ID), searchOptions); } if (filters.containsKey(CaseItem.FILTER_SUPERVISOR_ID)) { if (filters.containsKey(CaseItem.FILTER_STATE) && ("failed".equalsIgnoreCase(filters.get(CaseItem.FILTER_STATE)) || "error".equalsIgnoreCase(filters.get(CaseItem.FILTER_STATE)))) { return processAPI.searchFailedProcessInstancesSupervisedBy( MapUtil.getValueAsLong(filters, CaseItem.FILTER_SUPERVISOR_ID), searchOptions); } else { return processAPI.searchOpenProcessInstancesSupervisedBy( MapUtil.getValueAsLong(filters, CaseItem.FILTER_SUPERVISOR_ID), searchOptions); } } if (filters.containsKey(CaseItem.FILTER_STATE) && ("failed".equalsIgnoreCase(filters.get(CaseItem.FILTER_STATE)) || "error".equalsIgnoreCase(filters.get(CaseItem.FILTER_STATE)))) { return processAPI.searchFailedProcessInstances(searchOptions); } return processAPI.searchProcessInstances(searchOptions); } @Override public CaseItem get(final APIID id) { try { return convertEngineToConsoleItem(getProcessAPI().getProcessInstance(id.toLong())); } catch (final ProcessInstanceNotFoundException e) { return null; } catch (final BonitaException e) { throw new APIException(e); } } @Override public void delete(final List ids) { try { final ProcessAPI processApi = getProcessAPI(); for (final APIID id : ids) { processApi.deleteProcessInstance(id.toLong()); processApi.deleteArchivedProcessInstancesInAllStates(id.toLong()); } } catch (final BonitaException e) { if (e.getCause() instanceof ProcessInstanceNotFoundException) { throw new APIItemNotFoundException(CaseDefinition.TOKEN); } else { throw new APIException(e); } } } @Override public CaseItem add(final CaseItem caseItem) { final EngineClientFactory factory = new EngineClientFactory(new EngineAPIAccessor(getEngineSession())); return new CaseSarter(caseItem, factory.createCaseEngineClient(), factory.createProcessEngineClient()).start(); } public ProcessAPI getProcessAPI() throws BonitaException { return TenantAPIAccessor.getProcessAPI(getEngineSession()); } @Override public CaseItem update(APIID id, Map attributes) { final String state = MapUtil.getValue(attributes, CaseItem.ATTRIBUTE_STATE, null); try { final ProcessAPI processApi = getProcessAPI(); if (state == null) { throw new APIForbiddenException("Only " + CaseItem.ATTRIBUTE_STATE + "can be updated on a case"); } ProcessInstanceState instanceState = ProcessInstanceState.valueOf(state.toUpperCase()); if (instanceState == ProcessInstanceState.CANCELLED) { processApi.cancelProcessInstance(id.toLong()); return null; } throw new APIForbiddenException("Can't update a case state to \"" + state + "\""); } catch (final BonitaException e) { if (e.getCause() instanceof ProcessInstanceNotFoundException) { throw new APIItemNotFoundException(CaseDefinition.TOKEN); } else { throw new APIException(e); } } catch (final IllegalArgumentException e) { throw new APIIncorrectIdException("Case state \"" + state + "\" doesn't exist"); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDocumentDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.Map; import javax.activation.FileTypeMap; import javax.activation.MimetypesFileTypeMap; import org.apache.commons.io.IOUtils; import org.bonitasoft.console.common.server.preferences.properties.PropertiesFactory; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.document.DocumentException; import org.bonitasoft.engine.bpm.document.DocumentNotFoundException; import org.bonitasoft.engine.bpm.document.DocumentValue; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.datastore.utils.Sorts; import org.bonitasoft.web.rest.server.framework.api.*; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Fabio Lombardi */ public class CaseDocumentDatastore extends CommonDatastore implements DatastoreHasAdd, DatastoreHasGet, DatastoreHasUpdate, DatastoreHasDelete { protected final ProcessAPI processAPI; final long maxSizeForTenant; final FileTypeMap mimetypesFileTypeMap; final BonitaHomeFolderAccessor tenantFolder; protected SearchOptionsCreator searchOptionsCreator; /** * Default constructor. */ public CaseDocumentDatastore(final APISession engineSession, final ProcessAPI processAPI, final BonitaHomeFolderAccessor tenantFolder) { super(engineSession); this.processAPI = processAPI; this.tenantFolder = tenantFolder; maxSizeForTenant = PropertiesFactory.getConsoleProperties().getMaxSize(); mimetypesFileTypeMap = new MimetypesFileTypeMap(); } // GET Method @Override public CaseDocumentItem get(final APIID id) { try { final Document documentItem = processAPI.getDocument(id.toLong()); return convertEngineToConsoleItem(documentItem); } catch (final BonitaException e) { throw new APIException(e); } } // POST Method @Override public CaseDocumentItem add(final CaseDocumentItem item) { long caseId = -1; int index = -1; String documentDescription = ""; DocumentValue documentValue = null; try { if (item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_CASE_ID) != null) { caseId = Long.valueOf(item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_CASE_ID)); } } catch (final NumberFormatException e) { throw new APIException("Error while attaching a new document. Request with bad case id value."); } final String documentName = item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_NAME); final String uploadPath = item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH); final String urlPath = item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_URL); if (item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_DESCRIPTION) != null) { documentDescription = item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_DESCRIPTION); } if (item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_INDEX) != null) { index = Integer.parseInt(item.getAttributeValue(CaseDocumentItem.ATTRIBUTE_INDEX)); } try { if (caseId != -1 && documentName != null) { if (urlPath != null) { documentValue = buildDocumentValueFromUrl(urlPath, -1); } else { documentValue = buildDocumentValueFromUploadPath(uploadPath, index, item.getFileName()); } final Document document = processAPI.addDocument(caseId, documentName, documentDescription, documentValue); return convertEngineToConsoleItem(document); } else { throw new APIException("Error while attaching a new document. Request with bad param value."); } } catch (final BonitaException | IOException e) { throw new APIException(e); } finally { if (urlPath == null) { tenantFolder.removeUploadedTempContent(uploadPath); } } } // PUT Method @Override public CaseDocumentItem update(final APIID id, final Map attributes) { DocumentValue documentValue = null; try { final String urlPath; if (attributes.containsKey(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH) || attributes.containsKey(CaseDocumentItem.ATTRIBUTE_URL)) { if (attributes.containsKey(CaseDocumentItem.ATTRIBUTE_URL)) { urlPath = attributes.get(CaseDocumentItem.ATTRIBUTE_URL); documentValue = buildDocumentValueFromUrl(urlPath, -1); } else { urlPath = attributes.get(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH); documentValue = buildDocumentValueFromUploadPath(urlPath, -1, attributes.get(CaseDocumentItem.ATTRIBUTE_CONTENT_FILENAME)); } final Document document = processAPI.updateDocument(id.toLong(), documentValue); return convertEngineToConsoleItem(document); } else { throw new APIException("Error while attaching a new document. Request with bad param value."); } } catch (final BonitaException | IOException e) { throw new APIException(e); } finally { if (attributes.containsKey(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH)) { tenantFolder.removeUploadedTempContent(attributes.get(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH)); } } } protected DocumentValue buildDocumentValueFromUploadPath(final String uploadKey, final int index, String fileName) throws IOException { String mimeType = null; byte[] fileContent = null; if (uploadKey != null) { try { final FileContent theSourceFile = tenantFolder.retrieveUploadedTempContent(uploadKey); if (theSourceFile.getSize() > maxSizeForTenant * 1048576) { final String errorMessage = "This document is exceeded " + maxSizeForTenant + "Mb"; throw new DocumentException(errorMessage); } try (InputStream inputStream = theSourceFile.getInputStream()) { fileContent = IOUtils.toByteArray(inputStream); if (StringUtil.isBlank(fileName)) { fileName = theSourceFile.getFileName(); } mimeType = theSourceFile.getMimeType(); } } catch (BonitaException e) { throw new FileNotFoundException("Cannot find " + uploadKey + " in the tenant temp directory."); } } final DocumentValue documentValue = new DocumentValue(fileContent, mimeType, fileName); if (index != -1) { documentValue.setIndex(index); } return documentValue; } protected DocumentValue buildDocumentValueFromUrl(final String urlPath, final int index) { final DocumentValue documentValue = new DocumentValue(urlPath); if (index != -1) { documentValue.setIndex(index); } return documentValue; } // GET Method for SEARCH public ItemSearchResult search(final int page, final int resultsByPage, final String search, final Map filters, final String orders) { return searchDocument(page, resultsByPage, search, filters, orders); } protected ItemSearchResult searchDocument(final int page, final int resultsByPage, final String search, final Map filters, final String orders) { try { final APIID supervisorAPIID = APIID.makeAPIID(filters.get(CaseDocumentItem.FILTER_SUPERVISOR_ID)); if (supervisorAPIID != null) { filters.remove(CaseDocumentItem.FILTER_SUPERVISOR_ID); } searchOptionsCreator = buildSearchOptionCreator(page, resultsByPage, search, filters, orders); final SearchResult engineSearchResults; if (supervisorAPIID != null && supervisorAPIID.isValidLongID()) { engineSearchResults = processAPI.searchDocumentsSupervisedBy(supervisorAPIID.toLong(), searchOptionsCreator.create()); } else { engineSearchResults = processAPI.searchDocuments(searchOptionsCreator.create()); } return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(), convertEngineToConsoleItem(engineSearchResults.getResult())); } catch (final BonitaException e) { throw new APIException(e); } } protected SearchOptionsCreator buildSearchOptionCreator(final int page, final int resultsByPage, final String search, final Map filters, final String orders) { return new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders, getDocumentSearchAttributeConverter()), new Filters(filters, new CaseDocumentFilterCreator(getDocumentSearchAttributeConverter()))); } private CaseDocumentSearchAttributeConverter getDocumentSearchAttributeConverter() { return new CaseDocumentSearchAttributeConverter(); } // DELETE Method @Override public void delete(final List ids) { if (ids != null) { try { for (final APIID id : ids) { processAPI.removeDocument(id.toLong()); } } catch (final DocumentNotFoundException e) { throw new APIException("Error while deleting a document. Document not found"); } catch (final DeletionException e) { throw new APIException(e); } } else { throw new APIException("Error while deleting a document. Document id not specified in the request"); } } @Override protected CaseDocumentItem convertEngineToConsoleItem(final Document item) { if (item != null) { return new CaseDocumentItemConverter().convert(item); } return null; } private List convertEngineToConsoleItem(final List result) { if (result != null) { return new CaseDocumentItemConverter().convert(result); } return null; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDocumentFilterCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator; /** * @author Fabio Lombardi */ class CaseDocumentFilterCreator extends GenericFilterCreator { CaseDocumentFilterCreator(final CaseDocumentSearchAttributeConverter converter) { super(converter); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDocumentItemConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem; import org.bonitasoft.web.rest.server.datastore.converter.ItemConverter; public class CaseDocumentItemConverter extends ItemConverter { @Override public CaseDocumentItem convert(final Document engineItem) { final CaseDocumentItem item = new CaseDocumentItem(); item.setId(String.valueOf(engineItem.getId())); item.setCaseId(String.valueOf(engineItem.getProcessInstanceId())); item.setName(engineItem.getName()); item.setVersion(engineItem.getVersion()); item.setDescription(engineItem.getDescription()); item.setSubmittedBy(engineItem.getAuthor()); item.setFileName(engineItem.getContentFileName()); item.setCreationDate(engineItem.getCreationDate()); item.setMIMEType(engineItem.getContentMimeType()); item.setHasContent(String.valueOf(engineItem.hasContent())); item.setStorageId(engineItem.getContentStorageId()); item.setURL(engineItem.getUrl()); item.setIndex(engineItem.getIndex()); return item; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDocumentSearchAttributeConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.bpm.document.DocumentsSearchDescriptor; import org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Fabio Lombardi */ public class CaseDocumentSearchAttributeConverter implements AttributeConverter { private final Map mapping; public CaseDocumentSearchAttributeConverter() { mapping = createMapping(); } private Map createMapping() { final Map mapping = new HashMap<>(); mapping.put(CaseDocumentItem.ATTRIBUTE_ID, CaseDocumentItem.ATTRIBUTE_ID); mapping.put(CaseDocumentItem.ATTRIBUTE_SUBMITTED_BY_USER_ID, DocumentsSearchDescriptor.DOCUMENT_AUTHOR); mapping.put(CaseDocumentItem.ATTRIBUTE_NAME, DocumentsSearchDescriptor.DOCUMENT_NAME); mapping.put(CaseDocumentItem.ATTRIBUTE_CREATION_DATE, DocumentsSearchDescriptor.DOCUMENT_CREATIONDATE); mapping.put(CaseDocumentItem.ATTRIBUTE_DESCRIPTION, DocumentsSearchDescriptor.DOCUMENT_DESCRIPTION); mapping.put(CaseDocumentItem.ATTRIBUTE_INDEX, DocumentsSearchDescriptor.LIST_INDEX); mapping.put(CaseDocumentItem.ATTRIBUTE_CASE_ID, DocumentsSearchDescriptor.PROCESSINSTANCE_ID); return mapping; } @Override public String convert(final String attribute) { return MapUtil.getMandatory(mapping, attribute); } @Override public Map getValueTypeMapping() { return Collections.emptyMap(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseItemConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.web.rest.model.bpm.cases.CaseItem; import org.bonitasoft.web.rest.server.datastore.converter.ItemConverter; public class CaseItemConverter extends ItemConverter { @Override public CaseItem convert(final ProcessInstance process) { final CaseItem item = new CaseItem(); item.setId(process.getId()); item.setLastUpdateDate(process.getLastUpdate()); item.setState(process.getState()); item.setStartDate(process.getStartDate()); item.setEndDate(process.getEndDate()); item.setProcessId(process.getProcessDefinitionId()); item.setRootCaseId(process.getRootProcessInstanceId()); item.setStartedByUserId(process.getStartedBy()); item.setStartedBySubstituteUserId(process.getStartedBySubstitute()); item.setSearchIndex1Label(process.getStringIndexLabel(1)); item.setSearchIndex2Label(process.getStringIndexLabel(2)); item.setSearchIndex3Label(process.getStringIndexLabel(3)); item.setSearchIndex4Label(process.getStringIndexLabel(4)); item.setSearchIndex5Label(process.getStringIndexLabel(5)); item.setSearchIndex1Value(process.getStringIndex1()); item.setSearchIndex2Value(process.getStringIndex2()); item.setSearchIndex3Value(process.getStringIndex3()); item.setSearchIndex4Value(process.getStringIndex4()); item.setSearchIndex5Value(process.getStringIndex5()); item.setCallerId(process.getCallerId()); return item; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseSarter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import java.io.Serializable; import java.util.HashMap; import java.util.List; import org.bonitasoft.engine.bpm.data.DataDefinition; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.web.rest.model.bpm.cases.CaseItem; import org.bonitasoft.web.rest.server.datastore.utils.VariableMapper; import org.bonitasoft.web.rest.server.datastore.utils.VariablesMapper; import org.bonitasoft.web.rest.server.engineclient.CaseEngineClient; import org.bonitasoft.web.rest.server.engineclient.ProcessEngineClient; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; import org.bonitasoft.web.toolkit.client.data.APIID; public class CaseSarter { private final CaseItem caseItem; private final CaseEngineClient caseEngineClient; private final ProcessEngineClient processEngineClient; private final Long processId; private final Long delegateUserId; public CaseSarter(final CaseItem caseItem, final CaseEngineClient caseEngineClient, final ProcessEngineClient processEngineClient) { this.caseItem = caseItem; processId = caseItem.getProcessId().toLong(); if (caseItem.hasAttribute(CaseItem.ATTRIBUTE_STARTED_BY_SUBSTITUTE_USER_ID) && caseItem.getStartedBySubstituteUserId() != null) { APIID startedBySubstituteUserId = caseItem.getStartedBySubstituteUserId(); if (startedBySubstituteUserId.isValidLongID()) { delegateUserId = startedBySubstituteUserId.toLong(); } else { delegateUserId = -1L; } } else { delegateUserId = -1L; } this.caseEngineClient = caseEngineClient; this.processEngineClient = processEngineClient; } public CaseItem start() { final HashMap variables = getVariables(caseItem); if (variables.isEmpty()) { return startCase(); } else { return startCaseWithVariables(variables); } } private HashMap getVariables(final CaseItem caseItem) { final String jsonVariables = caseItem.getAttributeValue(CaseItem.ATTRIBUTE_VARIABLES); if (StringUtil.isBlank(jsonVariables)) { return new HashMap<>(); } return buildVariablesMap(jsonVariables); } private HashMap buildVariablesMap(final String jsonValue) { final List dataDefinitions = processEngineClient.getProcessDataDefinitions(processId); final HashMap map = new HashMap<>(); for (final VariableMapper var : VariablesMapper.fromJson(jsonValue).getVariables()) { final DataDefinition data = getDataDefinitionByName(var.getName(), dataDefinitions); map.put(var.getName(), var.getSerializableValue(data.getClassName())); } return map; } private CaseItem startCaseWithVariables(final HashMap variables) { final ProcessInstance processInstance = caseEngineClient.start(delegateUserId, processId, variables); return new CaseItemConverter().convert(processInstance); } private CaseItem startCase() { final ProcessInstance processInstance = caseEngineClient.start(delegateUserId, processId); return new CaseItemConverter().convert(processInstance); } private DataDefinition getDataDefinitionByName(final String dataName, final List dataDefinitions) { for (final DataDefinition dataDefinition : dataDefinitions) { if (dataDefinition.getName().equals(dataName)) { return dataDefinition; } } throw new APIException(new T_("Data definition %dataName% doesn't exists for process %processId%", new Arg("dataName", dataName), new Arg("processId", caseItem.getProcessId()))); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseVariableDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import static org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil.computeIndex; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.cases.CaseVariableItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException; import org.bonitasoft.web.rest.server.framework.utils.converter.TypeConverter; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIMethodNotAllowedException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Colin PUY */ public class CaseVariableDatastore extends CommonDatastore implements DatastoreHasSearch, DatastoreHasUpdate { private final TypeConverter converter = new TypeConverter(); public CaseVariableDatastore(final APISession engineSession) { super(engineSession); } @Override protected CaseVariableItem convertEngineToConsoleItem(final DataInstance item) { return new CaseVariableItem(item.getContainerId(), item.getName(), item.getValue(), item.getClassName(), item.getDescription()); } private List convert(final List dataInstances) { final List caseVariables = new ArrayList<>(); for (final DataInstance dataInstance : dataInstances) { caseVariables.add(convertEngineToConsoleItem(dataInstance)); } return caseVariables; } protected ProcessAPI getEngineProcessAPI() { try { return TenantAPIAccessor.getProcessAPI(getEngineSession()); } catch (final BonitaException e) { throw new APIException(e); } } @Override public CaseVariableItem update(final APIID id, final Map attributes) { throw new APIMethodNotAllowedException("Not implemented / No need to / Not used"); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { throw new APIMethodNotAllowedException("Not implemented / No need to / Not used"); } public void updateVariableValue(final long caseId, final String variableName, final String className, final String newValue) { try { final Serializable converteValue = converter.convert(className, newValue); getEngineProcessAPI().updateProcessDataInstance(variableName, caseId, converteValue); } catch (final BonitaException | ConversionException e) { throw new APIException("Error when updating case variable", e); } } public ItemSearchResult findByCaseId(final long caseId, final int page, final int resultsByPage) { try { final List processDataInstances = getEngineProcessAPI().getProcessDataInstances(caseId, computeIndex(page, resultsByPage), resultsByPage); return new ItemSearchResult<>(page, resultsByPage, countByCaseId(caseId), convert(processDataInstances)); } catch (final BonitaException e) { throw new APIException("Error when getting case variables", e); } } private long countByCaseId(final long caseId) throws BonitaException { return getEngineProcessAPI().getNumberOfProcessDataInstances(caseId); } public CaseVariableItem findById(final long caseId, final String variableName) { try { return convertEngineToConsoleItem(getEngineProcessAPI().getProcessDataInstance(variableName, caseId)); } catch (final BonitaException e) { throw new APIException("Error while getting case variable", e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CommentDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.api.CommandCaller; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.comment.Comment; import org.bonitasoft.engine.bpm.comment.SearchCommentsDescriptor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.cases.CommentItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Vincent Elcrin */ public class CommentDatastore extends CommonDatastore implements DatastoreHasAdd, DatastoreHasSearch { /** * Conversion look up table to for sortable fields */ private static final String[][] SORTABLE_FIELDS_LUT = { { CommentItem.ATTRIBUTE_POST_DATE, SearchCommentsDescriptor.POSTDATE }, }; /** * Command to fetch comments supervised by */ private static final String COMMAND_SEARCH_COMMENTS_SUPERVISEDBY = "searchSCommentSupervisedBy"; private static final String PROPERTY_SEARCH_OPTION_KEY = "SEARCH_OPTIONS_KEY"; private static final String PROPERTY_SUPERVISOR_ID_KEY = "supervisorId"; /** * Default Constructor. * * @param engineSession */ public CommentDatastore(final APISession engineSession) { super(engineSession); } /* * (non-Javadoc) * @see org.bonitasoft.web.toolkit.server.api.DatastoreHasSearch#search(int, int, java.lang.String, * java.lang.String, java.util.Map) */ @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { // prepare search final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, "", search); adjustSearchBuilder(filters, builder); /** * Need to convert field name passed by API into sortable field provided by engine. */ if (orders != null && !orders.isEmpty()) { final String[] sort = orders.split(" "); for (int i = 0; i < SORTABLE_FIELDS_LUT.length; i++) { if (sort[0].equals(SORTABLE_FIELDS_LUT[i][0])) { builder.sort(SORTABLE_FIELDS_LUT[i][1], Order.valueOf(sort[1])); } } } SearchResult engineSearchResults = null; /* * Search depends on the type of user which is defined in the filter. Only one at a time. */ final APIID teamManagerAPIID = APIID.makeAPIID(filters.get(CommentItem.FILTER_TEAM_MANAGER_ID)); final APIID supervisorAPIID = APIID.makeAPIID(filters.get(CommentItem.FILTER_SUPERVISOR_ID)); final APIID userAPIID = APIID.makeAPIID(filters.get(CommentItem.FILTER_USER_ID)); if (teamManagerAPIID != null && teamManagerAPIID.isValidLongID()) { engineSearchResults = runTeamManagerSearch(teamManagerAPIID.toLong(), builder); } else if (supervisorAPIID != null && supervisorAPIID.isValidLongID()) { engineSearchResults = runSupervisorSearch(supervisorAPIID.toLong(), builder); } else if (userAPIID != null && userAPIID.isValidLongID()) { engineSearchResults = runUserSearch(userAPIID.toLong(), builder); } else { engineSearchResults = runCustomSearch(builder); } if (engineSearchResults != null) { /* * Process result to convert engine items into console items */ final List consoleSearchResults = new ArrayList<>(); for (final Comment comment : engineSearchResults.getResult()) { consoleSearchResults.add(convertEngineToConsoleItem(comment)); } return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(), consoleSearchResults); } else { throw new APIException("Search failed for the following parameters "); } } /** * Search comments managed by specified user (ex administrator) * * @param builder * @return */ private SearchResult runTeamManagerSearch(final long teamManagerId, final SearchOptionsBuilder builder) { try { final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(getEngineSession()); return processAPI.searchCommentsManagedBy(teamManagerId, builder.done()); } catch (final BonitaException e) { throw new APIException(e); } } /** * Search comment involving specified user. * * @param userId * @param builder * @return */ private SearchResult runUserSearch(final long userId, final SearchOptionsBuilder builder) { try { final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(getEngineSession()); return processAPI.searchCommentsInvolvingUser(userId, builder.done()); } catch (final BonitaException e) { throw new APIException(e); } } /** * Search custom. It's up to you! * * @param builder * @return */ private SearchResult runCustomSearch(final SearchOptionsBuilder builder) { try { final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(getEngineSession()); return processAPI.searchComments(builder.done()); } catch (final BonitaException e) { throw new APIException(e); } } /** * Search comment supervised by specified user (ex process owner) * * @param supervisorId * @param builder * @return */ @SuppressWarnings("unchecked") private SearchResult runSupervisorSearch(final long supervisorId, final SearchOptionsBuilder builder) { // FIXME change me with API method return (SearchResult) new CommandCaller(getEngineSession(), COMMAND_SEARCH_COMMENTS_SUPERVISEDBY) .addParameter(PROPERTY_SEARCH_OPTION_KEY, builder.done()) .addParameter(PROPERTY_SUPERVISOR_ID_KEY, supervisorId) .run(); } /* * (non-Javadoc) * @see org.bonitasoft.web.toolkit.server.api.DatastoreHasAdd#add(org.bonitasoft.web.toolkit.client.data.item.Item) */ @Override public CommentItem add(final CommentItem item) { try { final ProcessAPI processAPI = TenantAPIAccessor.getProcessAPI(getEngineSession()); return convertEngineToConsoleItem( processAPI.addProcessComment(item.getProcessInstanceId().toLong(), item.getContent())); } catch (final BonitaException e) { throw new APIException(e); } } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CONVERTS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Convert ProcessItem filters into engine filers * * @param filters * @param builder */ private void adjustSearchBuilder(final Map filters, final SearchOptionsBuilder builder) { addStringFilterToSearchBuilder(filters, builder, CommentItem.ATTRIBUTE_PROCESS_INSTANCE_ID, SearchCommentsDescriptor.PROCESS_INSTANCE_ID); } /* * (non-Javadoc) * @see * org.bonitasoft.console.server.credentials.bpm.CommonDatastore#convertEngineToConsoleItem(java.io.Serializable) */ @Override protected CommentItem convertEngineToConsoleItem(final Comment engineItem) { if (engineItem == null) { return null; } final CommentItem consoleItem = new CommentItem(); consoleItem.setId(engineItem.getId()); consoleItem.setUserId(engineItem.getUserId()); consoleItem.setProcessInstanceId(engineItem.getProcessInstanceId()); consoleItem.setPostDate(engineItem.getPostDate()); consoleItem.setContent(engineItem.getContent()); return consoleItem; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ArchivedConnectorInstanceDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.connector; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance; import org.bonitasoft.engine.bpm.connector.ConnectorInstancesSearchDescriptor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.connector.ArchivedConnectorInstanceItem; import org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; /** * @author Julien Mege */ public class ArchivedConnectorInstanceDatastore extends CommonDatastore implements DatastoreHasSearch { public ArchivedConnectorInstanceDatastore(final APISession engineSession) { super(engineSession); } protected ProcessAPI getProcessAPI() { try { return TenantAPIAccessor.getProcessAPI(getEngineSession()); } catch (final BonitaException e) { throw new APIException(e); } } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { SearchResult searchConnectorInstances; try { searchConnectorInstances = getProcessAPI() .searchArchivedConnectorInstances(buildSearchOptions(page, resultsByPage, search, orders, filters)); } catch (final BonitaException e) { throw new APIException(e); } if (searchConnectorInstances != null) { final List convertedResult = convertEngineItemsIntoConsoleItems( searchConnectorInstances.getResult()); return new ItemSearchResult<>(page, convertedResult.size(), searchConnectorInstances.getCount(), convertedResult); } else { throw new APIException("Search failed for the following parameters "); } } /** * Build search option converting console filters into engine filter * * @param page * @param resultsByPage * @param search * @param orders * @param filters * @return */ protected SearchOptions buildSearchOptions(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, orders, search); addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_CONTAINER_ID, ConnectorInstancesSearchDescriptor.CONTAINER_ID); addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_STATE, ConnectorInstancesSearchDescriptor.STATE); return builder.done(); } /** * Convert engine item into console item used by web * * @param engineItem * Item provided by engine */ @Override protected ArchivedConnectorInstanceItem convertEngineToConsoleItem(final ArchivedConnectorInstance engineItem) { return new ArchivedConnectorInstanceItemWrapper(engineItem); } /** * @param searchResult * @return */ protected List convertEngineItemsIntoConsoleItems( final List engineItemList) { if (engineItemList != null) { final List consoleItemList = new ArrayList<>(); for (final ArchivedConnectorInstance engineItem : engineItemList) { consoleItemList.add(convertEngineToConsoleItem(engineItem)); } return consoleItemList; } else { throw new RuntimeException("List of engine items is null"); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ArchivedConnectorInstanceItemWrapper.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.connector; import org.bonitasoft.engine.bpm.connector.ArchivedConnectorInstance; import org.bonitasoft.web.rest.model.bpm.connector.ArchivedConnectorInstanceItem; /** * Bridge object between engine and console web implementation of an item * * @author Julien Mege */ public class ArchivedConnectorInstanceItemWrapper extends ArchivedConnectorInstanceItem { public ArchivedConnectorInstanceItemWrapper(final ArchivedConnectorInstance engineItem) { if (engineItem == null) { throw new IllegalArgumentException("Can't wrap null item"); } this.setId(engineItem.getId()); setName(engineItem.getName()); setVersion(engineItem.getVersion()); setState(engineItem.getState().toString()); this.setConnectorId(engineItem.getConnectorId()); setActivationEvent(engineItem.getActivationEvent().toString()); this.setContainerId(engineItem.getContainerId()); setContainerType(engineItem.getContainerType()); this.setSourceObjectId(engineItem.getSourceObjectId()); setArchivedDate(engineItem.getArchiveDate()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ConnectorInstanceDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.connector; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.connector.ConnectorInstance; import org.bonitasoft.engine.bpm.connector.ConnectorInstancesSearchDescriptor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; /** * @author Vincent Elcrin */ public class ConnectorInstanceDatastore extends CommonDatastore implements DatastoreHasSearch { public ConnectorInstanceDatastore(final APISession engineSession) { super(engineSession); } protected ProcessAPI getProcessAPI() { try { return TenantAPIAccessor.getProcessAPI(getEngineSession()); } catch (final BonitaException e) { throw new APIException(e); } } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { SearchResult searchConnectorInstances; try { searchConnectorInstances = getProcessAPI() .searchConnectorInstances(buildSearchOptions(page, resultsByPage, search, orders, filters)); } catch (final BonitaException e) { throw new APIException(e); } if (searchConnectorInstances != null) { final List convertedResult = convertEngineItemsIntoConsoleItems( searchConnectorInstances.getResult()); return new ItemSearchResult<>(page, resultsByPage, searchConnectorInstances.getCount(), convertedResult); } else { throw new APIException("Search failed for the following parameters "); } } /** * Build search option converting console filters into engine filter * * @param page * @param resultsByPage * @param search * @param orders * @param filters * @return */ protected SearchOptions buildSearchOptions(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, orders, search); addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_CONTAINER_ID, ConnectorInstancesSearchDescriptor.CONTAINER_ID); addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_STATE, ConnectorInstancesSearchDescriptor.STATE); addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_CONTAINER_TYPE, ConnectorInstancesSearchDescriptor.CONTAINER_TYPE); addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_ACTIVATION_EVENT, ConnectorInstancesSearchDescriptor.ACTIVATION_EVENT); addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_CONNECTOR_ID, ConnectorInstancesSearchDescriptor.CONNECTOR_DEFINITION_ID); addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_VERSION, ConnectorInstancesSearchDescriptor.CONNECTOR_DEFINITION_VERSION); addStringFilterToSearchBuilder(filters, builder, ConnectorInstanceItem.ATTRIBUTE_NAME, ConnectorInstancesSearchDescriptor.NAME); return builder.done(); } /** * Convert engine item into console item used by web * * @param engineItem * Item provided by engine */ @Override protected ConnectorInstanceItem convertEngineToConsoleItem(final ConnectorInstance engineItem) { return new ConnectorInstanceItemWrapper(engineItem); } /** * @param searchResult * @return */ protected List convertEngineItemsIntoConsoleItems( final List engineItemList) { if (engineItemList != null) { final List consoleItemList = new ArrayList<>(); for (final ConnectorInstance engineItem : engineItemList) { consoleItemList.add(convertEngineToConsoleItem(engineItem)); } return consoleItemList; } else { throw new RuntimeException("List of engine items is null"); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ConnectorInstanceItemWrapper.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.connector; import org.bonitasoft.engine.bpm.connector.ConnectorInstance; import org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem; /** * Bridge object between engine and console web implementation of an item * * @author Vincent Elcrin */ public class ConnectorInstanceItemWrapper extends ConnectorInstanceItem { public ConnectorInstanceItemWrapper(ConnectorInstance engineItem) { if (engineItem == null) { throw new IllegalArgumentException("Can't wrap null item"); } this.setId(engineItem.getId()); this.setName(engineItem.getName()); this.setVersion(engineItem.getVersion()); this.setState(engineItem.getState().toString()); this.setConnectorId(engineItem.getConnectorId()); this.setActivationEvent(engineItem.getActivationEvent().toString()); this.setContainerId(engineItem.getContainerId()); this.setContainerType(engineItem.getContainerType()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ConnectorInstanceResetStateConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.connector; import org.bonitasoft.engine.bpm.connector.ConnectorStateReset; import org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem; import org.bonitasoft.web.rest.server.framework.api.EnumConverter; /** * Convenient object to convert ATTRIBUTE_VALUE from {@link ConnectorInstanceItem} into {@link ConnectorStateReset} * * @author Vincent Elcrin */ public class ConnectorInstanceResetStateConverter implements EnumConverter { public final ConnectorStateReset convert(final String attributeStateValue) { if (ConnectorInstanceItem.VALUE_RESET_STATE_TO_RE_EXECUTE.equals(attributeStateValue)) { return ConnectorStateReset.TO_RE_EXECUTE; } else if (ConnectorInstanceItem.VALUE_RESET_STATE_SKIPPED.equals(attributeStateValue)) { return ConnectorStateReset.SKIPPED; } else { throw new RuntimeException("Can't convert following state into engine state <" + attributeStateValue + ">"); } } @Override public String convert(ConnectorStateReset enumValue) { switch (enumValue) { case SKIPPED: return ConnectorInstanceItem.VALUE_RESET_STATE_SKIPPED; case TO_RE_EXECUTE: return ConnectorInstanceItem.VALUE_RESET_STATE_TO_RE_EXECUTE; default: throw new RuntimeException("Can't convert <" + enumValue + ">. Flow node type not supported."); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ConnectorInstanceStateConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.connector; import org.bonitasoft.engine.bpm.connector.ConnectorState; import org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem; import org.bonitasoft.web.rest.server.framework.api.EnumConverter; /** * Convenient object to convert ATTRIBUTE_VALUE from {@link ConnectorInstanceItem} into {@link ConnectorState} * * @author Vincent Elcrin */ public class ConnectorInstanceStateConverter implements EnumConverter { public final ConnectorState convert(final String attributeStateValue) { if (ConnectorInstanceItem.VALUE_STATE_DONE.equals(attributeStateValue)) { return ConnectorState.DONE; } else if (ConnectorInstanceItem.VALUE_STATE_FAILED.equals(attributeStateValue)) { return ConnectorState.FAILED; } else if (ConnectorInstanceItem.VALUE_STATE_SKIPPED.equals(attributeStateValue)) { return ConnectorState.SKIPPED; } else if (ConnectorInstanceItem.VALUE_STATE_TO_BE_EXECUTED.equals(attributeStateValue)) { return ConnectorState.TO_BE_EXECUTED; } else if (ConnectorInstanceItem.VALUE_STATE_TO_RE_EXECUTE.equals(attributeStateValue)) { return ConnectorState.TO_RE_EXECUTE; } else { throw new RuntimeException("Can't convert following state into engine state <" + attributeStateValue + ">"); } } @Override public String convert(ConnectorState enumValue) { switch (enumValue) { case DONE: return ConnectorInstanceItem.VALUE_STATE_DONE; case FAILED: return ConnectorInstanceItem.VALUE_STATE_FAILED; case SKIPPED: return ConnectorInstanceItem.VALUE_STATE_SKIPPED; case TO_BE_EXECUTED: return ConnectorInstanceItem.VALUE_STATE_TO_BE_EXECUTED; case TO_RE_EXECUTE: return ConnectorInstanceItem.VALUE_STATE_TO_RE_EXECUTE; default: throw new RuntimeException("Can't convert <" + enumValue + ">. Flow node type not supported."); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/AbstractActivityDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import static org.bonitasoft.web.toolkit.client.common.util.StringUtil.isBlank; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException; import org.bonitasoft.engine.bpm.flownode.ActivityStates; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.ActivityDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem; import org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem; import org.bonitasoft.web.rest.server.datastore.converter.ActivityAttributeConverter; import org.bonitasoft.web.rest.server.datastore.filter.ActivityFilterCreator; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.datastore.utils.Sorts; import org.bonitasoft.web.rest.server.datastore.utils.VariableMapper; import org.bonitasoft.web.rest.server.datastore.utils.VariablesMapper; import org.bonitasoft.web.rest.server.engineclient.ActivityEngineClient; import org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor; import org.bonitasoft.web.rest.server.engineclient.EngineClientFactory; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public class AbstractActivityDatastore extends AbstractFlowNodeDatastore implements DatastoreHasGet, DatastoreHasUpdate { public AbstractActivityDatastore(final APISession engineSession) { super(engineSession); } /** * Fill a console item using the engine item passed. * * @param result * The console item to fill * @param item * The engine item to use for filling * @return This method returns the result parameter passed. */ protected static ActivityItem fillConsoleItem(final ActivityItem result, final ActivityInstance item) { FlowNodeDatastore.fillConsoleItem(result, item); result.setReachStateDate(item.getReachedStateDate()); result.setLastUpdateDate(item.getLastUpdateDate()); return result; } @Override public CONSOLE_ITEM get(final APIID id) { try { @SuppressWarnings("unchecked") final ENGINE_ITEM activityInstance = (ENGINE_ITEM) getProcessAPI().getActivityInstance(id.toLong()); return convertEngineToConsoleItem(activityInstance); } catch (final ActivityInstanceNotFoundException e) { throw new APIItemNotFoundException(ActivityDefinition.TOKEN, id); } } @Override protected SearchResult runSearch(final SearchOptionsBuilder builder, final Map filters) { try { @SuppressWarnings("unchecked") final SearchResult results = (SearchResult) getProcessAPI() .searchActivities(builder.done()); return results; } catch (final BonitaException e) { throw new APIException(e); } } @Override protected SearchOptionsBuilder makeSearchOptionBuilder(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { return new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders, new ActivityAttributeConverter()), new Filters(filters, new ActivityFilterCreator())) .getBuilder(); } @Override public CONSOLE_ITEM update(final APIID id, final Map attributes) { final String jsonVariables = MapUtil.getValue(attributes, ActivityItem.ATTRIBUTE_VARIABLES, null); if (!isBlank(jsonVariables)) { updateActivityVariables(id.toLong(), jsonVariables); } update(get(id), attributes); return null; } private void updateActivityVariables(final long activityId, final String jsonValue) { final ActivityEngineClient activityEngineclient = getActivityEngineClient(); final HashMap variables = buildVariablesMap(activityId, jsonValue, activityEngineclient); activityEngineclient.updateVariables(activityId, variables); } private ActivityEngineClient getActivityEngineClient() { return new EngineClientFactory(new EngineAPIAccessor(getEngineSession())).createActivityEngineClient(); } private HashMap buildVariablesMap(final long activityId, final String jsonValue, final ActivityEngineClient client) { final HashMap map = new HashMap<>(); for (final VariableMapper var : VariablesMapper.fromJson(jsonValue).getVariables()) { final DataInstance data = client.getDataInstance(var.getName(), activityId); map.put(var.getName(), var.getSerializableValue(data.getClassName())); } return map; } protected void update(final CONSOLE_ITEM item, final Map attributes) { updateState(item, MapUtil.getValue(attributes, FlowNodeItem.ATTRIBUTE_STATE, null), MapUtil.getValue(attributes, FlowNodeItem.ATTRIBUTE_EXECUTED_BY_USER_ID, null)); } /** * @param item * The item to update * @param state * The state to set */ protected void updateState(final CONSOLE_ITEM item, final String state, String userExecuteById) { try { if (state == null) { return; } if (HumanTaskItem.VALUE_STATE_SKIPPED.equals(state) && item instanceof FlowNodeItem) { getProcessAPI().setActivityStateByName(item.getId().toLong(), ActivityStates.SKIPPED_STATE); } else if (HumanTaskItem.VALUE_STATE_COMPLETED.equals(state) && item instanceof ActivityItem) { if (userExecuteById != null) { getProcessAPI().executeFlowNode(Long.valueOf(userExecuteById), item.getId().toLong()); } else { getProcessAPI().executeFlowNode(item.getId().toLong()); } } else { throw new APIException("Can't update " + item.getClass().getName() + " state to \"" + state + "\""); } } catch (final BonitaException e) { throw new APIException(e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/AbstractFlowNodeDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem; import org.bonitasoft.web.rest.model.bpm.flownode.TaskItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIMethodNotAllowedException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel * @author Celine Souchet */ public class AbstractFlowNodeDatastore extends CommonDatastore implements DatastoreHasSearch, DatastoreHasGet, DatastoreHasUpdate { private DatastoreHasUpdate updateHelper; public AbstractFlowNodeDatastore(final APISession engineSession) { super(engineSession); } /** * Fill a console item using the engine item passed. * * @param result * The console item to fill * @param item * The engine item to use for filling * @return This method returns the result parameter passed. */ protected static FlowNodeItem fillConsoleItem(final FlowNodeItem result, final FlowNodeInstance item) { result.setId(item.getId()); result.setName(item.getName()); result.setDisplayName(item.getDisplayName()); result.setDescription(item.getDescription()); result.setDisplayDescription(item.getDisplayDescription()); result.setExecutedByUserId(item.getExecutedBy()); result.setRootCaseId(item.getRootContainerId()); result.setParentCaseId(item.getParentProcessInstanceId()); result.setProcessId(item.getProcessDefinitionId()); result.setState(item.getState()); result.setType(item.getType().name()); result.setRootContainerId(item.getRootContainerId()); result.setExecutedBySubstituteUserId(item.getExecutedBySubstitute()); return result; } protected ProcessAPI getProcessAPI() { try { return TenantAPIAccessor.getProcessAPI(getEngineSession()); } catch (final BonitaException e) { throw new APIException(e); } } @SuppressWarnings("unchecked") @Override protected CONSOLE_ITEM convertEngineToConsoleItem(final ENGINE_ITEM item) { return (CONSOLE_ITEM) FlowNodeConverter.convertEngineToConsoleItem(item); } public long count(final String search, final String orders, final Map filters) { return search(0, 0, search, orders, filters).getTotal(); } @Override public CONSOLE_ITEM get(final APIID id) { try { @SuppressWarnings("unchecked") final ENGINE_ITEM flowNodeInstance = (ENGINE_ITEM) getProcessAPI().getFlowNodeInstance(id.toLong()); return convertEngineToConsoleItem(flowNodeInstance); } catch (final NotFoundException e) { throw new APIItemNotFoundException(FlowNodeDefinition.TOKEN, id); } } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final SearchOptionsBuilder builder = makeSearchOptionBuilder(page, resultsByPage, search, orders, filters); final SearchResult results = runSearch(builder, filters); return new ItemSearchResult<>( page, resultsByPage, results.getCount(), convertEngineToConsoleItemsList(results.getResult())); } @SuppressWarnings("unchecked") protected SearchResult runSearch(final SearchOptionsBuilder builder, final Map filters) { try { return (SearchResult) getProcessAPI().searchFlowNodeInstances(builder.done()); } catch (final BonitaException e) { throw new APIException(e); } } protected SearchOptionsBuilder makeSearchOptionBuilder(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, orders, search); addStringFilterToSearchBuilder(filters, builder, FlowNodeItem.ATTRIBUTE_CASE_ID, FlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID); addStringFilterToSearchBuilder(filters, builder, FlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, FlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID); addStringFilterToSearchBuilder(filters, builder, FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, FlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID); addStringFilterToSearchBuilder(filters, builder, FlowNodeItem.ATTRIBUTE_PARENT_ACTIVITY_INSTANCE_ID, FlowNodeInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID); addStringFilterToSearchBuilder(filters, builder, FlowNodeItem.ATTRIBUTE_PROCESS_ID, FlowNodeInstanceSearchDescriptor.PROCESS_DEFINITION_ID); addStringFilterToSearchBuilder(filters, builder, TaskItem.ATTRIBUTE_LAST_UPDATE_DATE, FlowNodeInstanceSearchDescriptor.LAST_UPDATE_DATE); addStringFilterToSearchBuilder(filters, builder, TaskItem.ATTRIBUTE_NAME, FlowNodeInstanceSearchDescriptor.NAME); if (filters.containsKey(FlowNodeItem.ATTRIBUTE_STATE) && FlowNodeItem.VALUE_STATE_PENDING.equalsIgnoreCase(filters.get(FlowNodeItem.ATTRIBUTE_STATE))) { builder.leftParenthesis().filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, "ready") .or().filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, "waiting") .rightParenthesis(); } else if (filters.containsKey(FlowNodeItem.ATTRIBUTE_STATE) && FlowNodeItem.VALUE_STATE_ONGOING.equalsIgnoreCase(filters.get(FlowNodeItem.ATTRIBUTE_STATE))) { builder.leftParenthesis().filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, "executing") .or().filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, "completing") .or().filter(FlowNodeInstanceSearchDescriptor.STATE_NAME, "initializing") .rightParenthesis(); } else { addStringFilterToSearchBuilder(filters, builder, FlowNodeItem.ATTRIBUTE_STATE, FlowNodeInstanceSearchDescriptor.STATE_NAME); } builder.differentFrom(FlowNodeInstanceSearchDescriptor.STATE_NAME, "aborted"); builder.differentFrom(FlowNodeInstanceSearchDescriptor.STATE_NAME, "cancelled"); builder.differentFrom(FlowNodeInstanceSearchDescriptor.STATE_NAME, "completed"); return builder; } public AbstractFlowNodeDatastore setUpdateHelper( final DatastoreHasUpdate updateHelper) { this.updateHelper = updateHelper; return this; } @Override public CONSOLE_ITEM update(final APIID id, final Map attributes) { if (updateHelper != null) { /* * Generics are useless in this class. * It only result in casting issues. * It needs to be badly removed. */ return (CONSOLE_ITEM) updateHelper.update(id, attributes); } throw new APIMethodNotAllowedException("PUT method not allowed"); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/AbstractHumanTaskDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import java.util.Collections; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.TaskPriority; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem; import org.bonitasoft.web.rest.server.datastore.converter.ActivityAttributeConverter; import org.bonitasoft.web.rest.server.datastore.utils.Sort; import org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.ui.utils.DateFormat; /** * @author Séverin Moussel */ public class AbstractHumanTaskDatastore extends AbstractTaskDatastore { public AbstractHumanTaskDatastore(final APISession engineSession) { super(engineSession); } /** * Fill a console item using the engine item passed. * * @param result * The console item to fill * @param item * The engine item to use for filling * @return This method returns the result parameter passed. */ protected static HumanTaskItem fillConsoleItem(final HumanTaskItem result, final HumanTaskInstance item) { TaskDatastore.fillConsoleItem(result, item); result.setActorId(APIID.makeAPIID(item.getActorId())); result.setAssignedId(APIID.makeAPIID(item.getAssigneeId())); result.setAssignedDate(item.getClaimedDate()); result.setPriority(item.getPriority() != null ? item.getPriority().toString().toLowerCase() : null); result.setDueDate(item.getExpectedEndDate()); return result; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // C.R.U.D.S // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SEARCH @Override protected SearchOptionsBuilder makeSearchOptionBuilder(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { String convertedSort = null; if (orders != null) { convertedSort = new Sort(orders, new ActivityAttributeConverter()).toString(); } final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, convertedSort, search); addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_CASE_ID, HumanTaskInstanceSearchDescriptor.PROCESS_INSTANCE_ID); addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_ROOT_CASE_ID, FlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID); addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_PARENT_CASE_ID, FlowNodeInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID); if (!filters.containsKey(HumanTaskItem.FILTER_USER_ID) && !filters.containsKey(HumanTaskItem.FILTER_TEAM_MANAGER_ID)) { addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_PROCESS_ID, HumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID); } addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_STATE, HumanTaskInstanceSearchDescriptor.STATE_NAME); addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_TYPE, ActivityInstanceSearchDescriptor.ACTIVITY_TYPE); addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID, HumanTaskInstanceSearchDescriptor.ASSIGNEE_ID); addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_PRIORITY, HumanTaskInstanceSearchDescriptor.PRIORITY); addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_NAME, HumanTaskInstanceSearchDescriptor.NAME); addStringFilterToSearchBuilder(filters, builder, HumanTaskItem.ATTRIBUTE_DISPLAY_NAME, HumanTaskInstanceSearchDescriptor.DISPLAY_NAME); return builder; } @Override protected SearchResult runSearch(final SearchOptionsBuilder builder, final Map filters) { // Using the same id for each test to avoid useless memory usage. APIID id = null; // Tasks of all users using a specific supervisor's processes. id = APIID.makeAPIID(filters.get(HumanTaskItem.FILTER_SUPERVISOR_ID)); if (id != null && id.isValidLongID()) { filters.remove(HumanTaskItem.FILTER_SUPERVISOR_ID); return runSupervisorSearch(filters, builder, id.toLong()); } // Tasks of all members of a specific team manager's team. id = APIID.makeAPIID(filters.get(HumanTaskItem.FILTER_TEAM_MANAGER_ID)); if (id != null && id.isValidLongID()) { return runTeamManagerSearch(filters, builder, id.toLong()); } return runGenericSearch(builder, filters); } protected SearchResult runGenericSearch(final SearchOptionsBuilder builder, final Map filters) { try { // Using the same id for each test to avoid useless memory usage. APIID id = null; // Tasks claimed by a specific user id = APIID.makeAPIID(filters.get(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID)); if (id != null && id.isValidLongID()) { @SuppressWarnings("unchecked") final SearchResult searchHumanTaskInstances = (SearchResult) getProcessAPI() .searchPendingTasksAssignedToUser(id.toLong(), builder.done()); return searchHumanTaskInstances; } // Available tasks for a specific user (pending + assigned) id = APIID.makeAPIID(filters.get(HumanTaskItem.FILTER_USER_ID)); if (id != null && id.isValidLongID()) { // Show also assigned to other users if (filters.containsKey(HumanTaskItem.FILTER_SHOW_ASSIGNED_TO_OTHERS) && Boolean.parseBoolean(filters.get(HumanTaskItem.FILTER_SHOW_ASSIGNED_TO_OTHERS))) { @SuppressWarnings("unchecked") final SearchResult searchPendingTasks = (SearchResult) getProcessAPI() .searchPendingOrAssignedToUserOrAssignedToOthersTasks( id.toLong(), builder.done()); return searchPendingTasks; } // ATTRIBUTE_ASSIGNED_USER_ID explicitly passed as NULL else if (filters.containsKey(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID) && APIID.makeAPIID(filters.get(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID)) == null) { @SuppressWarnings("unchecked") final SearchResult searchPendingTasksForUser = (SearchResult) getProcessAPI() .searchPendingTasksForUser( id.toLong(), builder.done()); return searchPendingTasksForUser; } else if (filters.containsKey(HumanTaskItem.ATTRIBUTE_PROCESS_ID)) { @SuppressWarnings("unchecked") final SearchResult searchMyAvailableHumanTasks = (SearchResult) getProcessAPI() .searchAssignedAndPendingHumanTasksFor( APIID.makeAPIID(filters.get(HumanTaskItem.ATTRIBUTE_PROCESS_ID)).toLong(), id.toLong(), builder.done()); return searchMyAvailableHumanTasks; } else { @SuppressWarnings("unchecked") final SearchResult searchMyAvailableHumanTasks = (SearchResult) getProcessAPI() .searchMyAvailableHumanTasks( id.toLong(), builder.done()); return searchMyAvailableHumanTasks; } } if (HumanTaskItem.VALUE_STATE_READY.equals(filters.get(HumanTaskItem.ATTRIBUTE_STATE))) { @SuppressWarnings("unchecked") SearchResult searchHumanTaskInstances = (SearchResult) getProcessAPI() .searchAssignedAndPendingHumanTasks(builder.done()); return searchHumanTaskInstances; } // Custom search @SuppressWarnings("unchecked") final SearchResult searchHumanTaskInstances = (SearchResult) getProcessAPI() .searchHumanTaskInstances(builder.done()); return searchHumanTaskInstances; } catch (final BonitaException e) { throw new APIException(e); } } private SearchResult runTeamManagerSearch(final Map filters, final SearchOptionsBuilder builder, final Long teamManagerId) { try { if (filters.containsKey(HumanTaskItem.FILTER_IS_ASSIGNED)) { if (Boolean.TRUE.equals(MapUtil.getValueAsBoolean(filters, HumanTaskItem.FILTER_IS_ASSIGNED))) { @SuppressWarnings("unchecked") final SearchResult searchResult = (SearchResult) getProcessAPI() .searchAssignedTasksManagedBy(teamManagerId, builder.done()); return searchResult; } else { if (filters.containsKey(HumanTaskItem.ATTRIBUTE_PROCESS_ID)) { @SuppressWarnings("unchecked") final SearchResult searchMyAvailableHumanTasks = (SearchResult) getProcessAPI() .searchAssignedAndPendingHumanTasks( APIID.makeAPIID(filters.get(HumanTaskItem.ATTRIBUTE_PROCESS_ID)).toLong(), builder.done()); return searchMyAvailableHumanTasks; } else { @SuppressWarnings("unchecked") final SearchResult searchResult = (SearchResult) getProcessAPI() .searchPendingTasksManagedBy(teamManagerId, builder.done()); return searchResult; } } } else { // Custom search @SuppressWarnings("unchecked") final SearchResult searchHumanTaskInstances = (SearchResult) getProcessAPI() .searchHumanTaskInstances(builder.done()); return searchHumanTaskInstances; } } catch (final BonitaException e) { throw new APIException(e); } } private SearchResult runSupervisorSearch(final Map filters, final SearchOptionsBuilder builder, final Long supervisorId) { try { final String taskType = filters.get(HumanTaskItem.ATTRIBUTE_STATE); if (taskType == null) { throw new APIException("Can't retrieve mixed state tasks for a defined supervisor"); } if (StringUtils.equalsIgnoreCase(taskType, HumanTaskItem.VALUE_STATE_READY)) { @SuppressWarnings("unchecked") final SearchResult searchResult = (SearchResult) getProcessAPI() .searchPendingTasksSupervisedBy(supervisorId, builder.done()); return searchResult; } else { throw new APIException("Can't retrieve non pending human task for a Process Manager"); } } catch (final BonitaException e) { throw new APIException(e); } } // GET @Override public CONSOLE_ITEM get(final APIID id) { try { @SuppressWarnings("unchecked") final ENGINE_ITEM humanTaskInstance = (ENGINE_ITEM) getProcessAPI().getHumanTaskInstance(id.toLong()); return convertEngineToConsoleItem(humanTaskInstance); } catch (final ActivityInstanceNotFoundException e) { throw new APIItemNotFoundException(HumanTaskDefinition.TOKEN, id); } } // UPDATE @Override public CONSOLE_ITEM update(final APIID id, final Map attributes) { try { // Priority if (attributes.containsKey(HumanTaskItem.ATTRIBUTE_PRIORITY)) { getProcessAPI().setTaskPriority( id.toLong(), TaskPriority.valueOf(attributes.get(HumanTaskItem.ATTRIBUTE_PRIORITY).toUpperCase())); } // Due date if (attributes.containsKey(HumanTaskItem.ATTRIBUTE_DUE_DATE)) { getProcessAPI().updateDueDateOfTask( id.toLong(), DateFormat.sqlToDate(attributes.get(HumanTaskItem.ATTRIBUTE_DUE_DATE))); } // Assigned to if (attributes.containsKey(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID)) { final APIID userId = APIID.makeAPIID(attributes.get(HumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID)); if (userId == null || !userId.isValidLongID()) { getProcessAPI().releaseUserTask(id.toLong()); } else { getProcessAPI().assignUserTask(id.toLong(), userId.toLong()); } } return super.update(id, attributes); } catch (final BonitaException e) { throw new APIException(e); } } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // COUNTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public Long getNumberOfOpenTasks(final APIID userId) { return getProcessAPI().getNumberOfOpenTasks(Collections.singletonList(userId.toLong())).get(userId.toLong()); } public Long getNumberOfOverdueOpenTasks(final APIID userId) { return getProcessAPI().getNumberOfOverdueOpenTasks(Collections.singletonList(userId.toLong())) .get(userId.toLong()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/AbstractTaskDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import org.bonitasoft.engine.bpm.flownode.TaskInstance; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.TaskItem; /** * @author Séverin Moussel */ public class AbstractTaskDatastore extends AbstractActivityDatastore { public AbstractTaskDatastore(final APISession engineSession) { super(engineSession); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/AbstractUserTaskDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceNotFoundException; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.UserTaskInstance; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.UserTaskDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.UserTaskItem; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public class AbstractUserTaskDatastore extends AbstractHumanTaskDatastore { public AbstractUserTaskDatastore(final APISession engineSession) { super(engineSession); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // C.R.U.D.S // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @SuppressWarnings("unchecked") @Override public CONSOLE_ITEM get(final APIID id) { try { // FIXME replace by getUserTaskInstance final HumanTaskInstance humanTaskInstance = getProcessAPI().getHumanTaskInstance(id.toLong()); if (!(humanTaskInstance instanceof UserTaskInstance)) { throw new APIItemNotFoundException("User task", id); } return convertEngineToConsoleItem((ENGINE_ITEM) humanTaskInstance); } catch (final ActivityInstanceNotFoundException e) { throw new APIItemNotFoundException(UserTaskDefinition.TOKEN, id); } } @Override protected SearchOptionsBuilder makeSearchOptionBuilder(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final SearchOptionsBuilder builder = super.makeSearchOptionBuilder(page, resultsByPage, search, orders, filters); builder.filter(HumanTaskInstanceSearchDescriptor.PARENT_ACTIVITY_INSTANCE_ID, 0); return builder; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/ActivityDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem; /** * @author Séverin Moussel */ public class ActivityDatastore extends AbstractActivityDatastore { public ActivityDatastore(final APISession engineSession) { super(engineSession); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/FlowNodeConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import org.bonitasoft.engine.bpm.flownode.ActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedTaskInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedUserTaskInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.bpm.flownode.TaskInstance; import org.bonitasoft.engine.bpm.flownode.UserTaskInstance; import org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskItem; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedUserTaskItem; import org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem; import org.bonitasoft.web.rest.model.bpm.flownode.TaskItem; import org.bonitasoft.web.rest.model.bpm.flownode.UserTaskItem; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedActivityDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedFlowNodeDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedHumanTaskDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedTaskDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedUserTaskDatastore; /** * @author Séverin Moussel */ public class FlowNodeConverter { public static FlowNodeItem convertEngineToConsoleItem(final FlowNodeInstance item) { return INSTANCE._convertEngineToConsoleItem(item); } public static ArchivedFlowNodeItem convertEngineToConsoleItem(final ArchivedFlowNodeInstance item) { return INSTANCE._convertEngineToConsoleItem(item); } protected FlowNodeItem _convertEngineToConsoleItem(final FlowNodeInstance item) { if (item instanceof UserTaskInstance) { return UserTaskDatastore.fillConsoleItem(new UserTaskItem(), (UserTaskInstance) item); } else if (item instanceof HumanTaskInstance) { return HumanTaskDatastore.fillConsoleItem(new HumanTaskItem(), (HumanTaskInstance) item); } else if (item instanceof TaskInstance) { return TaskDatastore.fillConsoleItem(new TaskItem(), (TaskInstance) item); } else if (item instanceof ActivityInstance) { return ActivityDatastore.fillConsoleItem(new ActivityItem(), (ActivityInstance) item); } return FlowNodeDatastore.fillConsoleItem(new FlowNodeItem(), item); } protected ArchivedFlowNodeItem _convertEngineToConsoleItem(final ArchivedFlowNodeInstance item) { if (item instanceof ArchivedUserTaskInstance) { return ArchivedUserTaskDatastore.fillConsoleItem(new ArchivedUserTaskItem(), (ArchivedUserTaskInstance) item); } else if (item instanceof ArchivedHumanTaskInstance) { return ArchivedHumanTaskDatastore.fillConsoleItem(new ArchivedHumanTaskItem(), (ArchivedHumanTaskInstance) item); } else if (item instanceof ArchivedTaskInstance) { return ArchivedTaskDatastore.fillConsoleItem(new ArchivedTaskItem(), (ArchivedTaskInstance) item); } else if (item instanceof ArchivedActivityInstance) { return ArchivedActivityDatastore.fillConsoleItem(new ArchivedActivityItem(), (ArchivedActivityInstance) item); } return ArchivedFlowNodeDatastore.fillConsoleItem(new ArchivedFlowNodeItem(), item); } private static FlowNodeConverter INSTANCE; public static void setFlowNodeConverter(final FlowNodeConverter converter) { INSTANCE = converter; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/FlowNodeDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem; /** * @author Séverin Moussel */ public class FlowNodeDatastore extends AbstractFlowNodeDatastore { public FlowNodeDatastore(final APISession engineSession) { super(engineSession); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/FlowNodeTypeConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import org.bonitasoft.engine.bpm.flownode.FlowNodeType; import org.bonitasoft.web.rest.model.bpm.flownode.IFlowNodeItem; import org.bonitasoft.web.rest.server.datastore.converter.ValueConverter; import org.bonitasoft.web.rest.server.framework.api.EnumConverter; /** * @author Vincent Elcrin */ public class FlowNodeTypeConverter implements EnumConverter, ValueConverter { @Override public FlowNodeType convert(final String attributeValue) { if (IFlowNodeItem.VALUE_TYPE_AUTOMATIC_TASK.equals(attributeValue)) { return FlowNodeType.AUTOMATIC_TASK; } else if (IFlowNodeItem.VALUE_TYPE_BOUNDARY_EVENT.equals(attributeValue)) { return FlowNodeType.BOUNDARY_EVENT; } else if (IFlowNodeItem.VALUE_TYPE_CALL_ACTIVITY.equals(attributeValue)) { return FlowNodeType.CALL_ACTIVITY; } else if (IFlowNodeItem.VALUE_TYPE_END_EVENT.equals(attributeValue)) { return FlowNodeType.END_EVENT; } else if (IFlowNodeItem.VALUE_TYPE_GATEWAY.equals(attributeValue)) { return FlowNodeType.GATEWAY; } else if (IFlowNodeItem.VALUE_TYPE_INTERMEDIATE_CATCH_EVENT.equals(attributeValue)) { return FlowNodeType.INTERMEDIATE_CATCH_EVENT; } else if (IFlowNodeItem.VALUE_TYPE_INTERMEDIATE_THROW_EVENT.equals(attributeValue)) { return FlowNodeType.INTERMEDIATE_THROW_EVENT; } else if (IFlowNodeItem.VALUE_TYPE_LOOP_ACTIVITY.equals(attributeValue)) { return FlowNodeType.LOOP_ACTIVITY; } else if (IFlowNodeItem.VALUE_TYPE_MANUAL_TASK.equals(attributeValue)) { return FlowNodeType.MANUAL_TASK; } else if (IFlowNodeItem.VALUE_TYPE_START_EVENT.equals(attributeValue)) { return FlowNodeType.START_EVENT; } else if (IFlowNodeItem.VALUE_TYPE_USER_TASK.equals(attributeValue)) { return FlowNodeType.USER_TASK; } else if (IFlowNodeItem.VALUE_TYPE_SUB_PROCESS_ACTIVITY.equals(attributeValue)) { return FlowNodeType.SUB_PROCESS; } /* * else if (IFlowNodeItem.VALUE_TYPE_MULTI_INSTANCE_ACTIVITY.equals(attributeValue)) { * return FlowNodeType.MULTI_INSTANCE; * } */else { throw new RuntimeException("Can't convert <" + attributeValue + ">. Unknown flow node type."); } } @Override public String convert(final FlowNodeType enumValue) { switch (enumValue) { case AUTOMATIC_TASK: return IFlowNodeItem.VALUE_TYPE_AUTOMATIC_TASK; case BOUNDARY_EVENT: return IFlowNodeItem.VALUE_TYPE_BOUNDARY_EVENT; case CALL_ACTIVITY: return IFlowNodeItem.VALUE_TYPE_CALL_ACTIVITY; case END_EVENT: return IFlowNodeItem.VALUE_TYPE_END_EVENT; case GATEWAY: return IFlowNodeItem.VALUE_TYPE_GATEWAY; case INTERMEDIATE_CATCH_EVENT: return IFlowNodeItem.VALUE_TYPE_INTERMEDIATE_CATCH_EVENT; case INTERMEDIATE_THROW_EVENT: return IFlowNodeItem.VALUE_TYPE_INTERMEDIATE_THROW_EVENT; case LOOP_ACTIVITY: return IFlowNodeItem.VALUE_TYPE_LOOP_ACTIVITY; case MANUAL_TASK: return IFlowNodeItem.VALUE_TYPE_MANUAL_TASK; case START_EVENT: return IFlowNodeItem.VALUE_TYPE_START_EVENT; case USER_TASK: return IFlowNodeItem.VALUE_TYPE_USER_TASK; case SUB_PROCESS: return IFlowNodeItem.VALUE_TYPE_SUB_PROCESS_ACTIVITY; /* * case MULTI_INSTANCE: * return IFlowNodeItem.VALUE_TYPE_MULTI_INSTANCE_ACTIVITY; */ default: throw new RuntimeException("Can't convert <" + enumValue + ">. Flow node type not supported."); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/HumanTaskDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem; /** * @author Séverin Moussel */ public class HumanTaskDatastore extends AbstractHumanTaskDatastore { public HumanTaskDatastore(final APISession engineSession) { super(engineSession); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/TaskDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import org.bonitasoft.engine.bpm.flownode.TaskInstance; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.TaskItem; /** * @author Séverin Moussel */ public class TaskDatastore extends AbstractTaskDatastore { public TaskDatastore(final APISession engineSession) { super(engineSession); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/TaskFinder.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.search.Order; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskItem; import org.bonitasoft.web.rest.model.bpm.flownode.TaskItem; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Vincent Elcrin */ public class TaskFinder { private final DatastoreHasGet journal; private final DatastoreHasSearch archives; public TaskFinder(final DatastoreHasGet journal, final DatastoreHasSearch archives) { this.journal = journal; this.archives = archives; } public IItem find(final APIID taskId) { try { return journal.get(taskId); } catch (final APIItemNotFoundException e) { final Map filters = new HashMap<>(); filters.put(ArchivedActivityItem.ATTRIBUTE_SOURCE_OBJECT_ID, taskId.toString()); final ItemSearchResult result = archives.search(0, 1, null, ArchivedActivityItem.ATTRIBUTE_ARCHIVED_DATE + " " + Order.DESC, filters); if (result.getResults().isEmpty()) { throw new APIItemNotFoundException(ArchivedTaskDefinition.TOKEN, taskId); } return result.getResults().get(0); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/UserTaskDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import org.bonitasoft.engine.bpm.flownode.UserTaskInstance; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.UserTaskItem; /** * @author Séverin Moussel */ public class UserTaskDatastore extends AbstractUserTaskDatastore { public UserTaskDatastore(final APISession engineSession) { super(engineSession); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/AbstractArchivedActivityDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.converter.ArchivedActivitySearchDescriptorConverter; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public abstract class AbstractArchivedActivityDatastore extends AbstractArchivedFlowNodeDatastore { public AbstractArchivedActivityDatastore(final APISession engineSession, String token) { super(engineSession, token); } /** * Fill a console item using the engine item passed. * * @param result * The console item to fill * @param item * The engine item to use for filling * @return This method returns the result parameter passed. */ public static ArchivedActivityItem fillConsoleItem(final ArchivedActivityItem result, final ArchivedActivityInstance item) { ArchivedFlowNodeDatastore.fillConsoleItem(result, item); result.setReachStateDate(item.getReachedStateDate()); result.setLastUpdateDate(item.getLastUpdateDate()); return result; } @Override protected ArchivedActivitySearchDescriptorConverter getSearchDescriptorConverter() { return new ArchivedActivitySearchDescriptorConverter(); } @Override protected SearchResult runSearch(final SearchOptionsCreator creator, final Map filters) { try { @SuppressWarnings("unchecked") final SearchResult result = (SearchResult) getProcessAPI() .searchArchivedActivities( creator.create()); return result; } catch (final BonitaException e) { throw new APIException(e); } } @Override protected ENGINE_ITEM runGet(final APIID id) { try { return super.runGet(id); } catch (ClassCastException e) { throw new APIItemNotFoundException(this.token, id); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/AbstractArchivedFlowNodeDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceNotFoundException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.FlowNodeConverter; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.converter.ArchivedFlowNodeSearchDescriptorConverter; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.datastore.utils.Sorts; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public abstract class AbstractArchivedFlowNodeDatastore extends CommonDatastore implements DatastoreHasGet, DatastoreHasSearch { protected final String token; public AbstractArchivedFlowNodeDatastore(final APISession engineSession, String token) { super(engineSession); this.token = token; } /** * Fill a console item using the engine item passed. * * @param result * The console item to fill * @param item * The engine item to use for filling * @return This method returns the result parameter passed. */ public static ArchivedFlowNodeItem fillConsoleItem(final ArchivedFlowNodeItem result, final ArchivedFlowNodeInstance item) { result.setId(item.getId()); result.setName(item.getName()); result.setDisplayName(item.getDisplayName()); result.setDescription(item.getDescription()); result.setDisplayDescription(item.getDisplayDescription()); result.setExecutedByUserId(item.getExecutedBy()); result.setRootCaseId(item.getRootContainerId()); result.setParentCaseId(item.getProcessInstanceId()); result.setProcessId(item.getProcessDefinitionId()); result.setState(item.getState()); result.setType(item.getType().name()); result.setArchivedDate(item.getArchiveDate()); result.setSourceObjectId(item.getSourceObjectId()); result.setRootContainerId(item.getRootContainerId()); result.setExecutedBySubstituteUserId(item.getExecutedBySubstitute()); return result; } protected ProcessAPI getProcessAPI() { try { return TenantAPIAccessor.getProcessAPI(getEngineSession()); } catch (final BonitaException e) { throw new APIException(e); } } @Override protected CONSOLE_ITEM convertEngineToConsoleItem(final ENGINE_ITEM item) { @SuppressWarnings("unchecked") final CONSOLE_ITEM result = (CONSOLE_ITEM) FlowNodeConverter.convertEngineToConsoleItem(item); return result; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // C.R.U.D.S // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GET @Override public CONSOLE_ITEM get(final APIID id) { final ENGINE_ITEM archivedFlowNodeInstance = runGet(id); return convertEngineToConsoleItem(archivedFlowNodeInstance); } @SuppressWarnings("unchecked") protected ENGINE_ITEM runGet(final APIID id) { try { return (ENGINE_ITEM) getProcessAPI().getArchivedFlowNodeInstance(id.toLong()); } catch (ArchivedFlowNodeInstanceNotFoundException e) { throw new APIItemNotFoundException(this.token, id); } } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final SearchOptionsCreator creator = makeSearchOptionCreator(page, resultsByPage, search, orders, filters); final SearchResult results = runSearch(creator, filters); return new ItemSearchResult<>( page, resultsByPage, results.getCount(), convertEngineToConsoleItemsList(results.getResult())); } /** * Run the engine API search method */ protected SearchResult runSearch(final SearchOptionsCreator creator, final Map filters) { try { @SuppressWarnings("unchecked") final SearchResult result = (SearchResult) getProcessAPI() .searchArchivedFlowNodeInstances( creator.create()); return result; } catch (final BonitaException e) { throw new APIException(e); } } protected SearchOptionsCreator makeSearchOptionCreator(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { return new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders, getSearchDescriptorConverter()), new Filters(filters, new ArchivedFlowNodeFilterCreator(getSearchDescriptorConverter()))); } protected ArchivedFlowNodeSearchDescriptorConverter getSearchDescriptorConverter() { return new ArchivedFlowNodeSearchDescriptorConverter(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/AbstractArchivedHumanTaskDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskItem; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.converter.ArchivedActivitySearchDescriptorConverter; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.converter.ArchivedHumanTaskSearchDescriptorConverter; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public abstract class AbstractArchivedHumanTaskDatastore extends AbstractArchivedTaskDatastore { public AbstractArchivedHumanTaskDatastore(final APISession engineSession, String token) { super(engineSession, token); } /** * Fill a console item using the engine item passed. * * @param result The console item to fill * @param item The engine item to use for filling * @return This method returns the result parameter passed. */ public static ArchivedHumanTaskItem fillConsoleItem(final ArchivedHumanTaskItem result, final ArchivedHumanTaskInstance item) { ArchivedTaskDatastore.fillConsoleItem(result, item); result.setActorId(APIID.makeAPIID(item.getActorId())); result.setAssignedId(APIID.makeAPIID(item.getAssigneeId())); result.setAssignedDate(item.getClaimedDate()); result.setPriority(item.getPriority() != null ? item.getPriority().toString().toLowerCase() : null); result.setDueDate(item.getExpectedEndDate()); return result; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // C.R.U.D.S // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { // can't use the ArchivedFlowNodeSearchDescriptorConverter to map web filter to engine ones since // the supervisor id filter isn't handle in engine but is a specific method String supervisorIdString = filters.remove(HumanTaskItem.FILTER_SUPERVISOR_ID); final SearchOptionsCreator creator = makeSearchOptionCreator(page, resultsByPage, search, orders, filters); if (StringUtils.isNotBlank(supervisorIdString)) { filters.put(HumanTaskItem.FILTER_SUPERVISOR_ID, supervisorIdString); } final SearchResult results = runSearch(creator, filters); return new ItemSearchResult<>( page, resultsByPage, results.getCount(), convertEngineToConsoleItemsList(results.getResult())); } @SuppressWarnings("unchecked") @Override protected SearchResult runSearch(final SearchOptionsCreator creator, final Map filters) { try { final SearchResult result; if (!MapUtil.isBlank(filters, ArchivedHumanTaskItem.FILTER_SUPERVISOR_ID)) { result = (SearchResult) getProcessAPI().searchArchivedHumanTasksSupervisedBy( MapUtil.getValueAsLong(filters, ArchivedTaskItem.FILTER_SUPERVISOR_ID), creator.create()); } else if (!MapUtil.isBlank(filters, ArchivedHumanTaskItem.FILTER_TEAM_MANAGER_ID)) { result = (SearchResult) getProcessAPI().searchArchivedHumanTasksManagedBy( MapUtil.getValueAsLong(filters, ArchivedHumanTaskItem.FILTER_TEAM_MANAGER_ID), creator.create()); } else { result = (SearchResult) getProcessAPI().searchArchivedHumanTasks( creator.create()); } return result; } catch (final BonitaException e) { throw new APIException(e); } } @Override protected ArchivedActivitySearchDescriptorConverter getSearchDescriptorConverter() { return new ArchivedHumanTaskSearchDescriptorConverter(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/AbstractArchivedTaskDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive; import org.bonitasoft.engine.bpm.flownode.ArchivedTaskInstance; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskItem; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public abstract class AbstractArchivedTaskDatastore extends AbstractArchivedActivityDatastore { public AbstractArchivedTaskDatastore(final APISession engineSession, String token) { super(engineSession, token); } @Override protected ENGINE_ITEM runGet(final APIID id) { try { return super.runGet(id); } catch (ClassCastException e) { throw new APIItemNotFoundException(this.token, id); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/AbstractArchivedUserTaskDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.ArchivedUserTaskInstance; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedUserTaskItem; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public abstract class AbstractArchivedUserTaskDatastore extends AbstractArchivedHumanTaskDatastore { public AbstractArchivedUserTaskDatastore(final APISession engineSession, String token) { super(engineSession, token); } @Override protected SearchResult runSearch(final SearchOptionsCreator creator, final Map filters) { if (!filters.containsKey(ArchivedUserTaskItem.ATTRIBUTE_TYPE)) { filters.put(ArchivedUserTaskItem.ATTRIBUTE_TYPE, ArchivedUserTaskItem.VALUE_TYPE_USER_TASK); } return super.runSearch(creator, filters); } @Override protected ENGINE_ITEM runGet(final APIID id) { try { return super.runGet(id); } catch (ClassCastException e) { throw new APIItemNotFoundException(this.token, id); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedActivityDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstance; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem; /** * @author Séverin Moussel */ public class ArchivedActivityDatastore extends AbstractArchivedActivityDatastore { public ArchivedActivityDatastore(final APISession engineSession, String token) { super(engineSession, token); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedFlowNodeDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstance; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem; /** * @author Séverin Moussel */ public class ArchivedFlowNodeDatastore extends AbstractArchivedFlowNodeDatastore { public ArchivedFlowNodeDatastore(final APISession engineSession, String token) { super(engineSession, token); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedFlowNodeFilterCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive; import java.io.Serializable; import org.bonitasoft.web.rest.model.bpm.flownode.IFlowNodeItem; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.FlowNodeTypeConverter; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.converter.ArchivedFlowNodeSearchDescriptorConverter; import org.bonitasoft.web.rest.server.datastore.filter.Field; import org.bonitasoft.web.rest.server.datastore.filter.Filter; import org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator; import org.bonitasoft.web.rest.server.datastore.filter.Value; class ArchivedFlowNodeFilterCreator extends GenericFilterCreator { ArchivedFlowNodeFilterCreator(ArchivedFlowNodeSearchDescriptorConverter converter) { super(converter); } @Override public Filter create(String attribute, String value) { if (IFlowNodeItem.ATTRIBUTE_TYPE.equals(attribute)) { return new Filter<>(new Field(attribute, fieldConverter), new Value<>(value, new FlowNodeTypeConverter())); } return super.create(attribute, value); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedHumanTaskDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive; import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstance; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem; /** * @author Séverin Moussel */ public class ArchivedHumanTaskDatastore extends AbstractArchivedHumanTaskDatastore { public ArchivedHumanTaskDatastore(final APISession engineSession, String token) { super(engineSession, token); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedTaskDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive; import org.bonitasoft.engine.bpm.flownode.ArchivedTaskInstance; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskItem; /** * @author Séverin Moussel */ public class ArchivedTaskDatastore extends AbstractArchivedTaskDatastore { public ArchivedTaskDatastore(final APISession engineSession, String token) { super(engineSession, token); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedUserTaskDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive; import org.bonitasoft.engine.bpm.flownode.ArchivedUserTaskInstance; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedUserTaskItem; /** * @author Séverin Moussel */ public class ArchivedUserTaskDatastore extends AbstractArchivedUserTaskDatastore { public ArchivedUserTaskDatastore(final APISession engineSession, String token) { super(engineSession, token); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/converter/ArchivedActivitySearchDescriptorConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive.converter; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem; /** * @author Colin PUY, Vincent Elcrin */ public class ArchivedActivitySearchDescriptorConverter extends ArchivedFlowNodeSearchDescriptorConverter { private static final Map additionalAttributes = new HashMap<>(); static { additionalAttributes.put(ArchivedActivityItem.ATTRIBUTE_REACHED_STATE_DATE, ArchivedActivityInstanceSearchDescriptor.REACHED_STATE_DATE); additionalAttributes.put(ArchivedFlowNodeItem.ATTRIBUTE_CASE_ID, ArchivedActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID); additionalAttributes.put(ArchivedFlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID); additionalAttributes.put(ArchivedFlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, ArchivedActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID); additionalAttributes.put(ArchivedFlowNodeItem.ATTRIBUTE_PROCESS_ID, ArchivedActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID); additionalAttributes.put(ArchivedFlowNodeItem.ATTRIBUTE_STATE, ArchivedActivityInstanceSearchDescriptor.STATE_NAME); additionalAttributes.put(ArchivedHumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID, ArchivedActivityInstanceSearchDescriptor.ASSIGNEE_ID); additionalAttributes.put(ArchivedHumanTaskItem.ATTRIBUTE_PRIORITY, ArchivedActivityInstanceSearchDescriptor.PRIORITY); additionalAttributes.put(ArchivedHumanTaskItem.ATTRIBUTE_TYPE, ArchivedActivityInstanceSearchDescriptor.ACTIVITY_TYPE); additionalAttributes.put(ArchivedActivityItem.ATTRIBUTE_SOURCE_OBJECT_ID, ArchivedActivityInstanceSearchDescriptor.SOURCE_OBJECT_ID); additionalAttributes.put(ArchivedActivityItem.ATTRIBUTE_ARCHIVED_DATE, ArchivedActivityInstanceSearchDescriptor.ARCHIVE_DATE); } public ArchivedActivitySearchDescriptorConverter() { extendsMapping(additionalAttributes); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/converter/ArchivedFlowNodeSearchDescriptorConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive.converter; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.ArchivedFlowNodeInstanceSearchDescriptor; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE; /** * @author Vincent Elcrin */ public class ArchivedFlowNodeSearchDescriptorConverter implements AttributeConverter { protected static final Map mapping = new HashMap<>(); private static final Map valueTypeMapping = new HashMap<>(); private static void addAttributeConverterItem(String webSearchKey, String engineSearchKey, TYPE attributeType) { mapping.put(webSearchKey, engineSearchKey); valueTypeMapping.put(webSearchKey, attributeType); } private static void addAttributeConverterItem(String webSearchKey, String engineSearchKey) { addAttributeConverterItem(webSearchKey, engineSearchKey, TYPE.STRING); } @Override public Map getValueTypeMapping() { return valueTypeMapping; } public ArchivedFlowNodeSearchDescriptorConverter() { createMapping(); } private void createMapping() { addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_CASE_ID, ArchivedFlowNodeInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID); addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_NAME, ArchivedFlowNodeInstanceSearchDescriptor.NAME); addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_DISPLAY_NAME, ArchivedFlowNodeInstanceSearchDescriptor.DISPLAY_NAME); addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_PROCESS_ID, ArchivedFlowNodeInstanceSearchDescriptor.PROCESS_DEFINITION_ID); addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_STATE, ArchivedFlowNodeInstanceSearchDescriptor.STATE_NAME); addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_TYPE, ArchivedFlowNodeInstanceSearchDescriptor.FLOW_NODE_TYPE); addAttributeConverterItem(ArchivedFlowNodeItem.FILTER_IS_TERMINAL, ArchivedFlowNodeInstanceSearchDescriptor.TERMINAL, TYPE.BOOLEAN); addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_ARCHIVED_DATE, ArchivedFlowNodeInstanceSearchDescriptor.ARCHIVE_DATE); addAttributeConverterItem(ArchivedFlowNodeItem.ATTRIBUTE_SOURCE_OBJECT_ID, ArchivedFlowNodeInstanceSearchDescriptor.ORIGINAL_FLOW_NODE_ID); } @Override public String convert(final String attribute) { return MapUtil.getMandatory(mapping, attribute); } protected final void extendsMapping(final Map extension) { mapping.putAll(extension); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/converter/ArchivedHumanTaskSearchDescriptorConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive.converter; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.ArchivedHumanTaskInstanceSearchDescriptor; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskItem; /** * @author Vincent Elcrin */ public class ArchivedHumanTaskSearchDescriptorConverter extends ArchivedActivitySearchDescriptorConverter { private static final Map mapping = new HashMap<>(); static { mapping.put(ArchivedFlowNodeItem.ATTRIBUTE_CASE_ID, ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID); mapping.put(ArchivedFlowNodeItem.ATTRIBUTE_ROOT_CASE_ID, ArchivedActivityInstanceSearchDescriptor.ROOT_PROCESS_INSTANCE_ID); mapping.put(ArchivedFlowNodeItem.ATTRIBUTE_PARENT_CASE_ID, ArchivedActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID); mapping.put(ArchivedFlowNodeItem.ATTRIBUTE_PROCESS_ID, ArchivedHumanTaskInstanceSearchDescriptor.PROCESS_DEFINITION_ID); mapping.put(ArchivedFlowNodeItem.ATTRIBUTE_STATE, ArchivedHumanTaskInstanceSearchDescriptor.STATE_NAME); mapping.put(ArchivedActivityItem.ATTRIBUTE_REACHED_STATE_DATE, ArchivedHumanTaskInstanceSearchDescriptor.REACHED_STATE_DATE); // FIXME Add this filter in the engine // mapping.put(ArchivedFlowNodeItem.ATTRIBUTE_TYPE, ArchivedHumanTaskInstanceSearchDescriptor.FLOW_NODE_TYPE); mapping.put(ArchivedHumanTaskItem.ATTRIBUTE_ASSIGNED_USER_ID, ArchivedHumanTaskInstanceSearchDescriptor.ASSIGNEE_ID); mapping.put(ArchivedHumanTaskItem.ATTRIBUTE_PRIORITY, ArchivedHumanTaskInstanceSearchDescriptor.PRIORITY); mapping.put(ArchivedHumanTaskItem.ATTRIBUTE_ARCHIVED_DATE, ArchivedHumanTaskInstanceSearchDescriptor.ARCHIVE_DATE); mapping.put(ArchivedHumanTaskItem.ATTRIBUTE_SOURCE_OBJECT_ID, ArchivedHumanTaskInstanceSearchDescriptor.ORIGINAL_HUMAN_TASK_ID); mapping.put(ArchivedHumanTaskItem.FILTER_USER_ID, ""); } public ArchivedHumanTaskSearchDescriptorConverter() { extendsMapping(mapping); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ActorDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process; import static org.bonitasoft.web.rest.model.bpm.process.ActorItem.ATTRIBUTE_DESCRIPTION; import static org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName.ATTRIBUTE_DISPLAY_NAME; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.actor.ActorCriterion; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.actor.ActorUpdater; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.web.rest.model.bpm.process.ActorItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Vincent Elcrin * @author Séverin Moussel */ public class ActorDatastore extends CommonDatastore implements DatastoreHasGet, DatastoreHasSearch, DatastoreHasUpdate { public ActorDatastore(final APISession engineSession) { super(engineSession); } @Override protected ActorItem convertEngineToConsoleItem(final ActorInstance engineItem) { final ActorItem result = new ActorItem(); result.setId(engineItem.getId()); result.setName(engineItem.getName()); result.setDisplayName(engineItem.getDisplayName()); result.setDescription(engineItem.getDescription()); result.setProcessId(engineItem.getProcessDefinitionId()); return result; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * @throws InvalidSessionException * @throws BonitaHomeNotSetException * @throws ServerAPIException * @throws UnknownAPITypeException */ private ProcessAPI getProcessAPI() throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getProcessAPI(getEngineSession()); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CRUDS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public ActorItem get(final APIID id) { try { return convertEngineToConsoleItem(getProcessAPI().getActor(id.toLong())); } catch (final BonitaException e) { throw new APIException(e); } } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { try { final Long processId = MapUtil.getValueAsLong(filters, ActorItem.ATTRIBUTE_PROCESS_ID); final List actors = getProcessAPI().getActors(processId, SearchOptionsBuilderUtil.computeIndex(page, resultsByPage), resultsByPage, ActorCriterion.valueOf(orders.toUpperCase().replace(" ", "_"))); return new ItemSearchResult<>( page, resultsByPage, getProcessAPI().getNumberOfActors(processId), convertEngineToConsoleItemsList(actors)); } catch (final BonitaException e) { throw new APIException(e); } } /** * @deprecated as of 9.0.0, Actor should be updated at startup. */ @Override @Deprecated(since = "9.0.0") public ActorItem update(final APIID id, final Map attributes) { try { final ActorUpdater updater = new ActorUpdater(); if (attributes.containsKey(ATTRIBUTE_DISPLAY_NAME)) { updater.setDisplayName(attributes.get(ATTRIBUTE_DISPLAY_NAME)); } if (attributes.containsKey(ATTRIBUTE_DESCRIPTION)) { updater.setDescription(attributes.get(ATTRIBUTE_DESCRIPTION)); } return convertEngineToConsoleItem(getProcessAPI().updateActor(id.toLong(), updater)); } catch (final BonitaException e) { throw new APIException(e); } } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // COUNTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public Long countUsers(final APIID actorId) { try { return getProcessAPI().getNumberOfUsersOfActor(actorId.toLong()); } catch (final BonitaException e) { throw new APIException(e); } } public Long countGroups(final APIID actorId) { try { return getProcessAPI().getNumberOfGroupsOfActor(actorId.toLong()); } catch (final BonitaException e) { throw new APIException(e); } } public Long countRoles(final APIID actorId) { try { return getProcessAPI().getNumberOfRolesOfActor(actorId.toLong()); } catch (final BonitaException e) { throw new APIException(e); } } public Long countMemberships(final APIID actorId) { try { return getProcessAPI().getNumberOfMembershipsOfActor(actorId.toLong()); } catch (final BonitaException e) { throw new APIException(e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ActorMemberDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.utils.ListUtil; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.actor.ActorMember; import org.bonitasoft.engine.exception.*; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.web.rest.model.bpm.process.ActorMemberItem; import org.bonitasoft.web.rest.model.identity.MemberType; import org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.exception.APIAttributesException; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public class ActorMemberDatastore extends CommonDatastore implements DatastoreHasAdd, DatastoreHasSearch, DatastoreHasDelete { public ActorMemberDatastore(final APISession engineSession) { super(engineSession); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UTILS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * @throws InvalidSessionException * @throws BonitaHomeNotSetException * @throws ServerAPIException * @throws UnknownAPITypeException */ private ProcessAPI getProcessAPI() throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getProcessAPI(getEngineSession()); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CRUDS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public void delete(final List ids) { try { for (final APIID id : ids) { getProcessAPI().removeActorMember(id.toLong()); } } catch (final BonitaException e) { throw new APIException(e); } } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { try { final Long actorId = MapUtil.getValueAsLong(filters, ActorMemberItem.ATTRIBUTE_ACTOR_ID); final List unfilteredResults = getProcessAPI().getActorMembers( actorId, 0, Integer.MAX_VALUE); final List filteredResults = applyTypeFilter(filters.get(ActorMemberItem.FILTER_MEMBER_TYPE), unfilteredResults); @SuppressWarnings("unchecked") final List paginatedResults = (List) ListUtil.paginate(filteredResults, page, resultsByPage); final List finalResults = convertEngineToConsoleItemsList(paginatedResults); for (final ActorMemberItem actorMemberItem : finalResults) { actorMemberItem.setActorId(actorId); } return new ItemSearchResult<>( page, resultsByPage, filteredResults.size(), finalResults); } catch (final BonitaException e) { throw new APIException(e); } } /** * @param filterType * We accept filter value or {@link MemberType} enum value. Better use MemberType enum value */ private List applyTypeFilter(final String filterType, final List unfilteredResults) { final List filteredResults = new ArrayList<>(); if (StringUtil.isBlank(filterType)) { filteredResults.addAll(unfilteredResults); } else { if (isMemberTypeUser(filterType)) { filterToUser(unfilteredResults, filteredResults); } else if (isMemberTypeRole(filterType)) { filterToRole(unfilteredResults, filteredResults); } else if (isMemberTypeGroup(filterType)) { filterToGroup(unfilteredResults, filteredResults); } else if (isMemberTypeMembership(filterType)) { filterToMembership(unfilteredResults, filteredResults); } } return filteredResults; } private boolean isMemberTypeUser(final String filterType) { return AbstractMemberItem.VALUE_MEMBER_TYPE_USER.equalsIgnoreCase(filterType) || MemberType.USER.name().equals(filterType); } private boolean isMemberTypeRole(final String filterType) { return AbstractMemberItem.VALUE_MEMBER_TYPE_ROLE.equalsIgnoreCase(filterType) || MemberType.ROLE.name().equals(filterType); } private boolean isMemberTypeGroup(final String filterType) { return AbstractMemberItem.VALUE_MEMBER_TYPE_GROUP.equalsIgnoreCase(filterType) || MemberType.GROUP.name().equals(filterType); } private boolean isMemberTypeMembership(final String filterType) { return AbstractMemberItem.VALUE_MEMBER_TYPE_MEMBERSHIP.equalsIgnoreCase(filterType) || MemberType.MEMBERSHIP.name().equals(filterType); } private void filterToUser(final List unfilteredResults, final List filteredResults) { for (final ActorMember result : unfilteredResults) { if (APIID.makeAPIID(result.getUserId()) != null) { filteredResults.add(result); } } } private void filterToRole(final List unfilteredResults, final List filteredResults) { for (final ActorMember result : unfilteredResults) { if (APIID.makeAPIID(result.getRoleId()) != null && APIID.makeAPIID(result.getGroupId()) == null) { filteredResults.add(result); } } } private void filterToGroup(final List unfilteredResults, final List filteredResults) { for (final ActorMember result : unfilteredResults) { if (APIID.makeAPIID(result.getGroupId()) != null && APIID.makeAPIID(result.getRoleId()) == null) { filteredResults.add(result); } } } private void filterToMembership(final List unfilteredResults, final List filteredResults) { for (final ActorMember result : unfilteredResults) { if (APIID.makeAPIID(result.getGroupId()) != null && APIID.makeAPIID(result.getRoleId()) != null) { filteredResults.add(result); } } } @Override public ActorMemberItem add(final ActorMemberItem item) { try { ActorMember addedActorMember = null; if (isUserActorMember(item)) { addedActorMember = addUserActorMember(item); } else if (isMembershipActorMember(item)) { addedActorMember = addMembershipActorMember(item); } else if (isRoleActorMember(item)) { addedActorMember = addRoleActorMember(item); } else if (isGroupActorMember(item)) { addedActorMember = addGroupActorMember(item); } else { throw new APIAttributesException(ActorMemberItem.ATTRIBUTE_USER_ID, ActorMemberItem.ATTRIBUTE_ROLE_ID, ActorMemberItem.ATTRIBUTE_GROUP_ID); } final ActorMemberItem addedItem = convertEngineToConsoleItem(addedActorMember); addedItem.setActorId(item.getActorId()); return addedItem; } catch (final BonitaException e) { throw new APIException(e); } } private ActorMember addGroupActorMember(final ActorMemberItem item) throws InvalidSessionException, NotFoundException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { try { return getProcessAPI().addGroupToActor(item.getActorId().toLong(), item.getGroupId().toLong()); } catch (final AlreadyExistsException e) { throw new APIForbiddenException(new T_("This group has already been mapped to actor"), e); } catch (CreationException e) { throw new APIException(new T_("Error when adding group to actor member"), e); } } private ActorMember addRoleActorMember(final ActorMemberItem item) throws InvalidSessionException, NotFoundException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { try { return getProcessAPI().addRoleToActor(item.getActorId().toLong(), item.getRoleId().toLong()); } catch (final CreationException e) { throw new APIForbiddenException(new T_("This role has already been mapped to actor"), e); } } private ActorMember addMembershipActorMember(final ActorMemberItem item) throws InvalidSessionException, NotFoundException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { try { return getProcessAPI() .addRoleAndGroupToActor(item.getActorId().toLong(), item.getRoleId().toLong(), item.getGroupId().toLong()); } catch (final CreationException e) { throw new APIForbiddenException(new T_("This membership has already been mapped to actor"), e); } } private ActorMember addUserActorMember(final ActorMemberItem item) throws InvalidSessionException, NotFoundException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { try { return getProcessAPI().addUserToActor(item.getActorId().toLong(), item.getUserId().toLong()); } catch (final CreationException e) { throw new APIForbiddenException(new T_("This user has already been mapped to actor"), e); } } private boolean isGroupActorMember(final ActorMemberItem item) { return item.getGroupId() != null; } private boolean isRoleActorMember(final ActorMemberItem item) { return item.getRoleId() != null; } private boolean isMembershipActorMember(final ActorMemberItem item) { return isRoleActorMember(item) && isGroupActorMember(item); } private boolean isUserActorMember(final ActorMemberItem item) { return item.getUserId() != null; } @Override protected ActorMemberItem convertEngineToConsoleItem(final ActorMember item) { final ActorMemberItem actorMemberItem = new ActorMemberItem(); actorMemberItem.setId(item.getId()); actorMemberItem.setUserId(item.getUserId()); actorMemberItem.setRoleId(item.getRoleId()); actorMemberItem.setGroupId(item.getGroupId()); return actorMemberItem; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/CategoryDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.category.Category; import org.bonitasoft.engine.bpm.category.CategoryCriterion; import org.bonitasoft.engine.bpm.category.CategoryNotFoundException; import org.bonitasoft.engine.bpm.category.CategoryUpdater; import org.bonitasoft.engine.exception.*; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.web.rest.model.bpm.process.CategoryDefinition; import org.bonitasoft.web.rest.model.bpm.process.CategoryItem; import org.bonitasoft.web.rest.model.bpm.process.ProcessItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.bonitasoft.web.toolkit.client.data.APIID; /** * Category data store * * @author Nicolas TITH */ public class CategoryDatastore extends CommonDatastore implements DatastoreHasSearch, DatastoreHasGet, DatastoreHasDelete, DatastoreHasAdd, DatastoreHasUpdate { public CategoryDatastore(final APISession engineSession) { super(engineSession); } /** * Retrieve the total number of categories * * @return the total number of categories * @throws InvalidSessionException * When session time out throw this exception * @throws BonitaHomeNotSetException * When bonita home not set throw this exception * @throws ServerAPIException * When access server api have problem throw this exception * @throws UnknownAPITypeException * When didn't know the api type throw this exception */ public long getNumberOfCategories() { try { return getProcessAPI().getNumberOfCategories(); } catch (final InvalidSessionException e) { throw new APIException(e); } } /** * Retrieve the total number of categories for a given process */ public long getNumberOfCategories(long processId) { return getProcessAPI().getNumberOfCategories(processId); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { CategoryCriterion orderCrit = getOrder(orders); if (filters.containsKey(ProcessItem.ATTRIBUTE_ID)) { return searchProcessCategories(page, resultsByPage, filters, orderCrit); } else { return searchCategories(page, resultsByPage, orderCrit); } } private ItemSearchResult searchCategories(final int page, final int resultsByPage, CategoryCriterion orderCrit) { List searchResult = getProcessAPI().getCategories( SearchOptionsBuilderUtil.computeIndex(page, resultsByPage), resultsByPage, orderCrit); return new ItemSearchResult<>(page, resultsByPage, getNumberOfCategories(), convertEngineToConsoleItemsList(searchResult)); } private ItemSearchResult searchProcessCategories(final int page, final int resultsByPage, final Map filters, CategoryCriterion orderCrit) { final Long processId = Long.valueOf(filters.get(ProcessItem.ATTRIBUTE_ID)); List searchResult = getProcessAPI().getCategoriesOfProcessDefinition(processId, SearchOptionsBuilderUtil.computeIndex(page, resultsByPage), resultsByPage, orderCrit); return new ItemSearchResult<>(page, resultsByPage, getNumberOfCategories(processId), convertEngineToConsoleItemsList(searchResult)); } private CategoryCriterion getOrder(final String orders) { if (CategoryItem.ATTRIBUTE_NAME.equals(orders)) { return CategoryCriterion.NAME_ASC; } else { return CategoryCriterion.valueOf(orders); } } @Override public CategoryItem add(final CategoryItem item) { try { final Category result = getProcessAPI().createCategory(item.getName(), item.getDescription()); return convertEngineToConsoleItem(result); } catch (final AlreadyExistsException e) { throw new APIForbiddenException( new T_("Category with name %categoryName% already exists", new Arg("categoryName", item.getName())), e); } catch (final BonitaException e) { throw new APIException(e); } } @Override public CategoryItem get(final APIID id) { try { final Category result = getProcessAPI().getCategory(id.toLong()); return convertEngineToConsoleItem(result); } catch (CategoryNotFoundException e) { throw new APIItemNotFoundException(CategoryDefinition.TOKEN, id); } } @Override protected CategoryItem convertEngineToConsoleItem(final Category item) { final CategoryItem categoryItem = new CategoryItem(); categoryItem.setCreatedByUserId(item.getCreator()); categoryItem.setCreationDate(item.getCreationDate()); categoryItem.setDescription(item.getDescription()); categoryItem.setDisplayName(item.getName()); categoryItem.setId(item.getId()); categoryItem.setName(item.getName()); return categoryItem; } protected final CategoryUpdater createCategoryUpdater(final CategoryItem item) { if (item == null) { return null; } final CategoryUpdater updater = new CategoryUpdater(); if (item.getDescription() != null) { updater.setDescription(item.getDescription()); } return updater; } @Override public void delete(final List ids) { try { final ProcessAPI processAPI = getProcessAPI(); for (final APIID id : ids) { final Long idCat = id.toLong(); do { } while (processAPI.removeProcessDefinitionsFromCategory(idCat, 0, 20) > 0); processAPI.deleteCategory(idCat); } } catch (final BonitaException e) { if (e.getCause() instanceof CategoryNotFoundException) { throw new APIItemNotFoundException(CategoryDefinition.TOKEN); } else { throw new APIException(e); } } } @Override public CategoryItem update(final APIID id, final Map attributes) { try { final ProcessAPI processAPI = getProcessAPI(); final CategoryItem catItem = new CategoryItem(); catItem.setAttributes(attributes); catItem.setId(id); processAPI.updateCategory(id.toLong(), createCategoryUpdater(catItem)); return get(id); } catch (final BonitaException e) { throw new APIException(e); } } protected ProcessAPI getProcessAPI() { try { return TenantAPIAccessor.getProcessAPI(getEngineSession()); } catch (BonitaException e) { throw new APIException(e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessCategoryDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process; import java.io.Serializable; import java.util.*; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.process.ProcessCategoryItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public class ProcessCategoryDatastore extends CommonDatastore implements DatastoreHasAdd, DatastoreHasDelete { public ProcessCategoryDatastore(final APISession engineSession) { super(engineSession); } @Override protected ProcessCategoryItem convertEngineToConsoleItem(final Serializable item) { // No conversion here return null; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // C.R.U.D.S // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public void delete(final List ids) { MapUtil.iterate( buildCategoriesIdByProcessIdMapping(ids), new MapUtil.ForEach<>() { @Override protected void apply(Long processId, List categoriesId) { removeCategoriesFromProcess(processId, categoriesId); } }); } private void removeCategoriesFromProcess(Long processId, List categoriesId) { try { getProcessAPI().removeCategoriesFromProcess(processId, categoriesId); } catch (BonitaException e) { throw new APIException(e); } } private Map> buildCategoriesIdByProcessIdMapping(List ids) { Map> categoriesIdByProcessId = new HashMap<>(); for (APIID apiid : ids) { Long processId = apiid.getPartAsLong(ProcessCategoryItem.ATTRIBUTE_PROCESS_ID); Long categoryId = apiid.getPartAsLong(ProcessCategoryItem.ATTRIBUTE_CATEGORY_ID); if (categoriesIdByProcessId.containsKey(processId)) { categoriesIdByProcessId.get(processId).add(categoryId); } else { ArrayList categoryIds = new ArrayList<>(); categoryIds.add(categoryId); categoriesIdByProcessId.put(processId, categoryIds); } } return categoriesIdByProcessId; } @Override public ProcessCategoryItem add(final ProcessCategoryItem item) { try { getProcessAPI().addCategoriesToProcess(item.getProcessId().toLong(), Collections.singletonList(item.getCategoryId().toLong())); return item; } catch (AlreadyExistsException e) { throw new APIForbiddenException(new T_("This category has already been added to this process"), e); } catch (final BonitaException e) { throw new APIException(e); } } protected ProcessAPI getProcessAPI() { try { return TenantAPIAccessor.getProcessAPI(getEngineSession()); } catch (BonitaException e) { throw new APIException(e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessConnectorDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process; import static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem.ATTRIBUTE_NAME; import static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem.ATTRIBUTE_PROCESS_ID; import static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem.ATTRIBUTE_VERSION; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.connector.ConnectorCriterion; import org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public class ProcessConnectorDatastore extends CommonDatastore implements DatastoreHasGet, DatastoreHasSearch { public ProcessConnectorDatastore(final APISession engineSession) { super(engineSession); } protected ProcessAPI getProcessAPI() { try { return TenantAPIAccessor.getProcessAPI(getEngineSession()); } catch (final BonitaException e) { throw new APIException(e); } } @Override public ProcessConnectorItem get(final APIID id) { try { final ProcessConnectorItem connector = convertEngineToConsoleItem( getProcessAPI().getConnectorImplementation( id.getPartAsLong(ATTRIBUTE_PROCESS_ID), id.getPart(ATTRIBUTE_NAME), id.getPart(ATTRIBUTE_VERSION))); // Correct missing element in engine object connector.setProcessId(id.getPartAsLong(ATTRIBUTE_PROCESS_ID)); return connector; } catch (final BonitaException e) { throw new APIException(e); } } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final Long processId = MapUtil.getValueAsLong(filters, ATTRIBUTE_PROCESS_ID); final List connectors = getProcessAPI().getConnectorImplementations( processId, SearchOptionsBuilderUtil.computeIndex(page, resultsByPage), resultsByPage, ConnectorCriterion.valueOf(orders.toUpperCase().replace(" ", "_"))); final List results = new ArrayList<>(); for (final ConnectorImplementationDescriptor connector : connectors) { final ProcessConnectorItem result = convertEngineToConsoleItem(connector); // Correct missing element in engine object result.setProcessId(processId); results.add(result); } final long numtotalConnectorImplem = getProcessAPI().getNumberOfConnectorImplementations(processId); return new ItemSearchResult<>(page, resultsByPage, numtotalConnectorImplem, results); } @Override protected ProcessConnectorItem convertEngineToConsoleItem(final ConnectorImplementationDescriptor item) { final ProcessConnectorItem result = new ProcessConnectorItem(); result.setName(item.getDefinitionId()); result.setVersion(item.getDefinitionVersion()); // setted in get and search because attribute is missing in the engine oject // result.setProcessId(...); result.setImplementationName(item.getId()); result.setImplementationVersion(item.getVersion()); result.setClassname(item.getImplementationClassName()); return result; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessConnectorDependencyDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process; import static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_NAME; import static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_CONNECTOR_VERSION; import static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.ATTRIBUTE_PROCESS_ID; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.utils.ListUtil; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; /** * @author Séverin Moussel */ public class ProcessConnectorDependencyDatastore extends CommonDatastore implements DatastoreHasSearch { public ProcessConnectorDependencyDatastore(final APISession engineSession) { super(engineSession); } protected ProcessAPI getProcessAPI() { try { return TenantAPIAccessor.getProcessAPI(getEngineSession()); } catch (BonitaException e) { throw new APIException(e); } } @Override @SuppressWarnings("unchecked") public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { try { final Long processId = MapUtil.getValueAsLong(filters, ATTRIBUTE_PROCESS_ID); final String connectorName = filters.get(ATTRIBUTE_CONNECTOR_NAME); final String connectorVersion = filters.get(ATTRIBUTE_CONNECTOR_VERSION); // Get connector definition final ConnectorImplementationDescriptor connectorImplementation = getProcessAPI() .getConnectorImplementation(processId, connectorName, connectorVersion); // If connector definition doesn't exists returns an empty resultset if (connectorImplementation == null) { return new ItemSearchResult<>(page, 0, 0, new ArrayList<>()); } // Get Jar from definition and Simulate pagination final List jarDependencies = connectorImplementation.getJarDependencies(); final List dependencies = (List) ListUtil.paginate(jarDependencies, page, resultsByPage); // Convert to consoleItem final List results = convertEngineToConsoleItems(processId, connectorName, connectorVersion, dependencies); return new ItemSearchResult<>(page, results.size(), jarDependencies.size(), results); } catch (final BonitaException e) { throw new APIException(e); } } private List convertEngineToConsoleItems(final Long processId, final String connectorName, final String connectorVersion, final List dependencies) { final List results = new ArrayList<>(); for (final String filename : dependencies) { final ProcessConnectorDependencyItem dependencyItem = convertEngineToConsoleItem(processId, connectorName, connectorVersion, filename); results.add(dependencyItem); } return results; } private ProcessConnectorDependencyItem convertEngineToConsoleItem(final Long processId, final String connectorName, final String connectorVersion, final String filename) { final ProcessConnectorDependencyItem dependencyItem = new ProcessConnectorDependencyItem(); dependencyItem.setProcessId(processId); dependencyItem.setConnectorName(connectorName); dependencyItem.setConnectorVersion(connectorVersion); dependencyItem.setFilename(filename); return dependencyItem; } @Override protected ProcessConnectorDependencyItem convertEngineToConsoleItem(final ConnectorImplementationDescriptor item) { // Not used. Engine item is not fully populated return null; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.page.CustomPageService; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.PlatformManagementUtils; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.bar.BusinessArchiveFactory; import org.bonitasoft.engine.bpm.bar.InvalidBusinessArchiveFormatException; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoUpdater; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.page.PageSearchDescriptor; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.process.ProcessItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.datastore.bpm.process.helper.ProcessItemConverter; import org.bonitasoft.web.rest.server.datastore.bpm.process.helper.SearchProcessHelper; import org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor; import org.bonitasoft.web.rest.server.engineclient.EngineClientFactory; import org.bonitasoft.web.rest.server.engineclient.ProcessEngineClient; import org.bonitasoft.web.rest.server.framework.api.*; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Process data store * * @author Vincent Elcrin */ public class ProcessDatastore extends CommonDatastore implements DatastoreHasAdd, DatastoreHasUpdate, DatastoreHasGet, DatastoreHasSearch, DatastoreHasDelete { private static final Logger LOGGER = LoggerFactory.getLogger(ProcessDatastore.class.getName()); /** * process file */ private static final String FILE_UPLOAD = "fileupload"; private static final int DELETE_PAGES_BUNCH_SIZE = 100; public ProcessDatastore(final APISession engineSession) { super(engineSession); } /** * @deprecated as of 9.0.0, Process should be created at startup. */ @Override @Deprecated(since = "9.0.0") public ProcessItem add(final ProcessItem process) { final ProcessEngineClient engineClient = getProcessEngineClient(); FileContent processFile; try { processFile = getTenantFolder().retrieveUploadedTempContent(process.getAttributes().get(FILE_UPLOAD)); } catch (final BonitaException e) { throw new APIException("Process file not found", e); } try { //no need to handle the closing of the stream here as it is handled in BusinessArchiveFactory final BusinessArchive businessArchive = readBusinessArchive(processFile.getInputStream()); final ProcessDefinition deployedArchive = engineClient.deploy(businessArchive); final ProcessDeploymentInfo processDeploymentInfo = engineClient .getProcessDeploymentInfo(deployedArchive.getId()); return convertEngineToConsoleItem(processDeploymentInfo); } finally { getTenantFolder().removeUploadedTempContent(process.getAttributes().get(FILE_UPLOAD)); } } protected BonitaHomeFolderAccessor getTenantFolder() { return new BonitaHomeFolderAccessor(); } /* * Overridden in SP */ protected BusinessArchive readBusinessArchive(final InputStream inputStream) { try { return BusinessArchiveFactory.readBusinessArchive(inputStream); } catch (final IOException | InvalidBusinessArchiveFormatException e) { throw new APIException(e); } } /** * @deprecated as of 9.0.0, Process should be updated at startup. */ @Override @Deprecated(since = "9.0.0") public ProcessItem update(final APIID id, final Map attributes) { final ProcessDeploymentInfoUpdater updater = new ProcessDeploymentInfoUpdater(); final ProcessEngineClient engineClient = getProcessEngineClient(); if (attributes.containsKey(ProcessItem.ATTRIBUTE_DISPLAY_DESCRIPTION)) { updater.setDisplayDescription(attributes.get(ProcessItem.ATTRIBUTE_DISPLAY_DESCRIPTION)); } if (attributes.containsKey(ProcessItem.ATTRIBUTE_DISPLAY_NAME)) { updater.setDisplayName(attributes.get(ProcessItem.ATTRIBUTE_DISPLAY_NAME)); } // specific engine methods if (attributes.containsKey(ProcessItem.ATTRIBUTE_ACTIVATION_STATE)) { changeProcessState(engineClient, id.toLong(), attributes.get(ProcessItem.ATTRIBUTE_ACTIVATION_STATE)); } if (!updater.getFields().isEmpty()) { final ProcessDeploymentInfo processDeploymentInfo = engineClient.updateProcessDeploymentInfo(id.toLong(), updater); return convertEngineToConsoleItem(processDeploymentInfo); } else { return convertEngineToConsoleItem(engineClient.getProcessDeploymentInfo(id.toLong())); } } private void changeProcessState(final ProcessEngineClient engineClient, final Long processId, final String state) { if (ProcessItem.VALUE_ACTIVATION_STATE_DISABLED.equals(state)) { engineClient.disableProcess(processId); } else if (ProcessItem.VALUE_ACTIVATION_STATE_ENABLED.equals(state)) { engineClient.enableProcess(processId); } } protected PlatformManagementUtils getPlatformManagementUtils() { return new PlatformManagementUtils(); } @Override public ProcessItem get(final APIID id) { final ProcessEngineClient engineClient = getProcessEngineClient(); final ProcessDeploymentInfo processDeploymentInfo = engineClient.getProcessDeploymentInfo(id.toLong()); return convertEngineToConsoleItem(processDeploymentInfo); } @Override public void delete(final List ids) { for (final APIID id : ids) { removeProcessPagesFromHome(id); } final ProcessEngineClient engineClient = getProcessEngineClient(); engineClient.deleteDisabledProcesses(APIID.toLongList(ids)); } protected void removeProcessPagesFromHome(final APIID id) { try { int startIndex = 0; int count = 0; do { final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(startIndex, DELETE_PAGES_BUNCH_SIZE); searchOptionsBuilder.filter(PageSearchDescriptor.PROCESS_DEFINITION_ID, id.toLong()); final SearchResult result = getPageAPI().searchPages(searchOptionsBuilder.done()); if (count == 0) { count = (int) result.getCount(); } startIndex = startIndex + result.getResult().size(); for (final Page page : result.getResult()) { getCustomPageService().removePageLocally(page); } } while (startIndex < count); } catch (final BonitaException | IOException e) { if (LOGGER.isWarnEnabled()) { LOGGER.warn("Error when deleting pages for process with ID " + id, e); } } } protected CustomPageService getCustomPageService() { return new CustomPageService(); } protected PageAPI getPageAPI() { try { return TenantAPIAccessor.getCustomPageAPI(getEngineSession()); } catch (final BonitaException e) { throw new APIException(e); } } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final ProcessEngineClient engineClient = getProcessEngineClient(); return new SearchProcessHelper(engineClient).search(page, resultsByPage, search, orders, filters); } @Override protected ProcessItem convertEngineToConsoleItem(final ProcessDeploymentInfo item) { if (item != null) { return new ProcessItemConverter(getProcessEngineClient().getProcessApi()).convert(item); } return null; } protected ProcessEngineClient getProcessEngineClient() { return getEngineClientFactory().createProcessEngineClient(); } private EngineClientFactory getEngineClientFactory() { return new EngineClientFactory(new EngineAPIAccessor(getEngineSession())); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessResolutionProblemDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bpm.process.Problem; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.process.ProcessResolutionProblemItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; /** * @author Séverin Moussel */ public class ProcessResolutionProblemDatastore extends CommonDatastore implements DatastoreHasSearch { public ProcessResolutionProblemDatastore(final APISession engineSession) { super(engineSession); } @Override protected ProcessResolutionProblemItem convertEngineToConsoleItem(final Problem item) { final ProcessResolutionProblemItem consoleItem = new ProcessResolutionProblemItem(); consoleItem.setMessage(item.getDescription()); consoleItem.setTargetType(item.getResource()); consoleItem.setRessourceId(item.getResourceId()); return consoleItem; } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { try { final List errors = TenantAPIAccessor.getProcessAPI(getEngineSession()) .getProcessResolutionProblems( MapUtil.getValueAsLong(filters, ProcessResolutionProblemItem.FILTER_PROCESS_ID)); final int startIndex = page * resultsByPage; return new ItemSearchResult<>( page, resultsByPage, errors.size(), convertEngineToConsoleItemsList( errors.subList( startIndex, Math.min(startIndex + resultsByPage, errors.size())))); } catch (final BonitaException e) { throw new APIException(e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/helper/ProcessItemConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process.helper; import org.bonitasoft.console.common.server.utils.TenantCacheUtil; import org.bonitasoft.console.common.server.utils.TenantCacheUtilFactory; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.actor.ActorNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.web.rest.model.bpm.process.ProcessItem; import org.bonitasoft.web.rest.server.datastore.converter.ItemConverter; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; /** * @author Vincent Elcrin */ public class ProcessItemConverter extends ItemConverter { private final ProcessAPI processApi; public ProcessItemConverter(final ProcessAPI processApi) { this.processApi = processApi; } @Override public ProcessItem convert(ProcessDeploymentInfo engineItem) { final ProcessItem item = new ProcessItem(); item.setId(engineItem.getProcessId()); item.setName(engineItem.getName()); item.setVersion(engineItem.getVersion()); item.setDescription(engineItem.getDescription()); item.setDeployedByUserId(engineItem.getDeployedBy()); item.setDeploymentDate(engineItem.getDeploymentDate()); item.setActivationState(engineItem.getActivationState().name()); item.setConfigurationState(engineItem.getConfigurationState().name()); item.setDisplayName(engineItem.getDisplayName()); item.setDisplayDescription(engineItem.getDisplayDescription()); item.setLastUpdateDate(engineItem.getLastUpdateDate()); item.setActorInitiatorId(getActorInitiator(engineItem)); return item; } private Long getActorInitiator(ProcessDeploymentInfo engineItem) { TenantCacheUtil tenantCacheUtil = TenantCacheUtilFactory.getTenantCacheUtil(); Long actorInitiatorId = tenantCacheUtil.getProcessActorInitiatorId(engineItem.getProcessId()); if (actorInitiatorId == null) { actorInitiatorId = tenantCacheUtil.storeProcessActorInitiatorId(engineItem.getProcessId(), getActorInitiatorFromEngine(engineItem)); } return actorInitiatorId; } private Long getActorInitiatorFromEngine(ProcessDeploymentInfo engineItem) { Long actorInitiatorId; try { actorInitiatorId = processApi.getActorInitiator(engineItem.getProcessId()).getId(); } catch (ActorNotFoundException e) { actorInitiatorId = -1L; } catch (ProcessDefinitionNotFoundException e) { throw new APIException(AbstractI18n.t_("Process definition not found for id %processId%", new Arg("processId", String.valueOf(engineItem.getProcessId()))), e); } return actorInitiatorId; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/helper/ProcessSearchDescriptorConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process.helper; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfoSearchDescriptor; import org.bonitasoft.web.rest.model.bpm.process.ProcessItem; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE; /** * @author Vincent Elcrin */ public class ProcessSearchDescriptorConverter implements AttributeConverter { protected static final Map mapping = new HashMap<>(); @Override public Map getValueTypeMapping() { return Collections.emptyMap(); } static { mapping.put(ProcessItem.ATTRIBUTE_ID, ProcessDeploymentInfoSearchDescriptor.ID); mapping.put(ProcessItem.ATTRIBUTE_ACTIVATION_STATE, ProcessDeploymentInfoSearchDescriptor.ACTIVATION_STATE); mapping.put(ProcessItem.ATTRIBUTE_CONFIGURATION_STATE, ProcessDeploymentInfoSearchDescriptor.CONFIGURATION_STATE); mapping.put(ProcessItem.ATTRIBUTE_DEPLOYED_BY_USER_ID, ProcessDeploymentInfoSearchDescriptor.DEPLOYED_BY); mapping.put(ProcessItem.ATTRIBUTE_DEPLOYMENT_DATE, ProcessDeploymentInfoSearchDescriptor.DEPLOYMENT_DATE); mapping.put(ProcessItem.ATTRIBUTE_DISPLAY_NAME, ProcessDeploymentInfoSearchDescriptor.DISPLAY_NAME); mapping.put(ProcessItem.ATTRIBUTE_LAST_UPDATE_DATE, ProcessDeploymentInfoSearchDescriptor.LAST_UPDATE_DATE); mapping.put(ProcessItem.ATTRIBUTE_NAME, ProcessDeploymentInfoSearchDescriptor.NAME); mapping.put(ProcessItem.ATTRIBUTE_VERSION, ProcessDeploymentInfoSearchDescriptor.VERSION); mapping.put(ProcessItem.FILTER_CATEGORY_ID, ProcessDeploymentInfoSearchDescriptor.CATEGORY_ID); mapping.put(ProcessItem.FILTER_RECENT_PROCESSES, ""); // code smell. Should return empty object (EmptyField) mapping.put(ProcessItem.FILTER_SUPERVISOR_ID, ""); mapping.put(ProcessItem.FILTER_TEAM_MANAGER_ID, ""); mapping.put(ProcessItem.FILTER_USER_ID, ""); mapping.put(ProcessItem.FILTER_FOR_PENDING_OR_ASSIGNED_TASKS, ""); } @Override public String convert(String attribute) { String value = mapping.get(attribute); if (value == null) { throw new RuntimeException("Can't find search descriptor corresponding to " + attribute); } return value; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/helper/SearchProcessFilterCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process.helper; import org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator; /** * @author Vincent Elcrin */ class SearchProcessFilterCreator extends GenericFilterCreator { SearchProcessFilterCreator(ProcessSearchDescriptorConverter converter) { super(converter); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/bpm/process/helper/SearchProcessHelper.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process.helper; import java.util.Map; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.web.rest.model.bpm.process.ProcessItem; import org.bonitasoft.web.rest.server.datastore.converter.ItemSearchResultConverter; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.datastore.utils.Sorts; import org.bonitasoft.web.rest.server.engineclient.ProcessEngineClient; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Vincent Elcrin */ public class SearchProcessHelper implements DatastoreHasSearch { private final ProcessEngineClient engineClient; public SearchProcessHelper(ProcessEngineClient engineClient) { this.engineClient = engineClient; } @Override public ItemSearchResult search(int page, int resultsByPage, String search, String orders, Map filters) { SearchOptionsCreator searchOptions = new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders, new ProcessSearchDescriptorConverter()), new Filters(filters, new SearchProcessFilterCreator(new ProcessSearchDescriptorConverter()))); final SearchResult result = runSearch(filters, searchOptions.create()); return convertResult(page, resultsByPage, result); } private SearchResult runSearch(Map filters, SearchOptions searchOptions) { if (isFilteringOn(filters, ProcessItem.FILTER_USER_ID, ProcessItem.FILTER_RECENT_PROCESSES)) { return engineClient.searchRecentlyStartedProcessDefinitions(getApiId(filters, ProcessItem.FILTER_USER_ID), searchOptions); } else if (isFilteringOn(filters, ProcessItem.FILTER_USER_ID, ProcessItem.FILTER_CATEGORY_ID) && filters.get(ProcessItem.FILTER_CATEGORY_ID) == null) { return engineClient.searchUncategorizedProcessDefinitionsUserCanStart( getApiId(filters, ProcessItem.FILTER_USER_ID), searchOptions); } else if (isFilteringOn(filters, ProcessItem.FILTER_USER_ID, ProcessItem.FILTER_FOR_PENDING_OR_ASSIGNED_TASKS)) { return engineClient.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor( getApiId(filters, ProcessItem.FILTER_USER_ID), searchOptions); } else if (isFilteringOn(filters, ProcessItem.FILTER_USER_ID)) { return engineClient.searchProcessDeploymentInfos(getApiId(filters, ProcessItem.FILTER_USER_ID), searchOptions); } else if (isFilteringOn(filters, ProcessItem.FILTER_SUPERVISOR_ID, ProcessItem.FILTER_FOR_PENDING_OR_ASSIGNED_TASKS)) { return engineClient.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy( getApiId(filters, ProcessItem.FILTER_USER_ID), searchOptions); } else if (isFilteringOn(filters, ProcessItem.FILTER_SUPERVISOR_ID)) { return engineClient.searchProcessDefinitionsSupervisedBy( getApiId(filters, ProcessItem.FILTER_SUPERVISOR_ID), searchOptions); } else if (isFilteringOn(filters, ProcessItem.FILTER_FOR_PENDING_OR_ASSIGNED_TASKS)) { return engineClient.searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(searchOptions); } else { return engineClient.searchProcessDefinitions(searchOptions); } } private boolean isFilteringOn(Map filters, String... attributes) { for (String attribute : attributes) { if (!filters.containsKey(attribute)) { return false; } } return true; } private Long getApiId(Map filters, String attribute) { return APIID.makeAPIID(filters.get(attribute)).toLong(); } private ItemSearchResult convertResult(int page, int nbResultsByPage, final SearchResult result) { return new ItemSearchResultConverter<>(page, nbResultsByPage, result, new ProcessItemConverter( engineClient.getProcessApi())).toItemSearchResult(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/ActivityAttributeConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.converter; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor; import org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem; import org.bonitasoft.web.rest.model.bpm.flownode.TaskItem; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; public class ActivityAttributeConverter implements AttributeConverter { protected static final Map mapping = new HashMap<>(); static { mapping.put(ActivityItem.ATTRIBUTE_CASE_ID, ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID); mapping.put(ActivityItem.ATTRIBUTE_NAME, ActivityInstanceSearchDescriptor.NAME); mapping.put(ActivityItem.ATTRIBUTE_STATE, ActivityInstanceSearchDescriptor.STATE_NAME); mapping.put(ActivityItem.ATTRIBUTE_PROCESS_ID, ActivityInstanceSearchDescriptor.PROCESS_DEFINITION_ID); mapping.put(ActivityItem.ATTRIBUTE_ROOT_CASE_ID, ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID); mapping.put(ActivityItem.ATTRIBUTE_PARENT_CASE_ID, ActivityInstanceSearchDescriptor.PARENT_PROCESS_INSTANCE_ID); mapping.put(ActivityItem.ATTRIBUTE_TYPE, ActivityInstanceSearchDescriptor.ACTIVITY_TYPE); mapping.put(ActivityItem.FILTER_SUPERVISOR_ID, ActivityInstanceSearchDescriptor.SUPERVISOR_ID); mapping.put(TaskItem.ATTRIBUTE_LAST_UPDATE_DATE, FlowNodeInstanceSearchDescriptor.LAST_UPDATE_DATE); } @Override public String convert(final String attribute) { return mapping.get(attribute); } @Override public Map getValueTypeMapping() { return Collections.emptyMap(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/AttributeConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.converter; import java.util.Map; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE; /** * @author Colin PUY * @author Emmanuel Duchastenier */ public interface AttributeConverter { String convert(String attribute); /** * return a map of attribute name to the type to convert the value into. */ Map getValueTypeMapping(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/BooleanValueConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.converter; /** * @author Vincent Elcrin */ public class BooleanValueConverter implements ValueConverter { @Override public Boolean convert(String value) { return Boolean.valueOf(value); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/EmptyAttributeConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.converter; import java.util.Collections; import java.util.Map; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Colin PUY, Vincent Elcrin * Empty converter, do nothing */ public class EmptyAttributeConverter implements AttributeConverter { @Override public String convert(String attribute) { return attribute; } @Override public Map getValueTypeMapping() { return Collections.emptyMap(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/ItemConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.converter; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Vincent Elcrin */ public abstract class ItemConverter { public abstract I convert(E engineItem); public List convert(List profiles) { ArrayList profileItems = new ArrayList<>(); for (E profile : profiles) { profileItems.add(convert(profile)); } return profileItems; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/ItemSearchResultConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.converter; import java.io.Serializable; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Vincent Elcrin */ public class ItemSearchResultConverter { private final int page; private final SearchResult result; private final ItemConverter converter; private final long total; private final int nbResultsByPage; public ItemSearchResultConverter(int page, int nbResultsByPage, SearchResult result, ItemConverter converter) { this(page, nbResultsByPage, result, result.getCount(), converter); } public ItemSearchResultConverter(int page, int nbResultsByPage, SearchResult result, long total, ItemConverter converter) { this.page = page; this.nbResultsByPage = nbResultsByPage; this.result = result; this.total = total; this.converter = converter; } public ItemSearchResult toItemSearchResult() { return new ItemSearchResult<>(page, nbResultsByPage, total, converter.convert(result.getResult())); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/LongValueConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.converter; /** * @author Vincent Elcrin */ public class LongValueConverter implements ValueConverter { @Override public Long convert(String value) { return Long.valueOf(value); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/StringValueConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.converter; /** * @author Vincent Elcrin */ public class StringValueConverter implements ValueConverter { @Override public String convert(String value) { return value; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/converter/ValueConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.converter; import java.io.Serializable; /** * @author Vincent Elcrin */ public interface ValueConverter { V convert(String value); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/ActivityFilterCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import java.io.Serializable; import org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem; import org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.FlowNodeTypeConverter; import org.bonitasoft.web.rest.server.datastore.converter.ActivityAttributeConverter; import org.bonitasoft.web.rest.server.datastore.converter.EmptyAttributeConverter; import org.bonitasoft.web.rest.server.datastore.converter.StringValueConverter; import org.bonitasoft.web.rest.server.datastore.filter.Filter.Operator; /** * @author Florine Boudin */ public class ActivityFilterCreator extends GenericFilterCreator { public ActivityFilterCreator() { super(new EmptyAttributeConverter()); } /* * (non-Javadoc) * @see org.bonitasoft.web.rest.server.credentials.filter.FilterCreator#create(java.lang.String, java.lang.String) */ @Override public Filter create(String attribute, String value) { if (ActivityItem.ATTRIBUTE_TYPE.equals(attribute)) { return new Filter<>(new Field(attribute, new ActivityAttributeConverter()), new Value<>(value, new FlowNodeTypeConverter())); } if (ActivityItem.FILTER_IS_FAILED.equals(attribute)) { Operator operator = Boolean.valueOf(value) ? Operator.EQUAL : Operator.DIFFERENT_FROM; return new Filter<>(new Field(ActivityItem.ATTRIBUTE_STATE, new ActivityAttributeConverter()), new Value<>(FlowNodeItem.VALUE_STATE_FAILED, new StringValueConverter()), operator); } return new GenericFilterCreator(new ActivityAttributeConverter()).create(attribute, value); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/BooleanValue.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import org.bonitasoft.web.rest.server.datastore.converter.BooleanValueConverter; public class BooleanValue extends Value { public BooleanValue(String value) { super(value, new BooleanValueConverter()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/Field.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.rest.server.datastore.converter.EmptyAttributeConverter; /** * @author Vincent Elcrin */ public class Field { private final String field; public Field(String attribute, AttributeConverter convert) { field = convert.convert(attribute); } public Field(String attribute) { this(attribute, new EmptyAttributeConverter()); } @Override public String toString() { return field; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/Filter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import java.io.Serializable; /** * @author Vincent Elcrin */ public class Filter { public enum Operator { EQUAL, DIFFERENT_FROM } private final Field field; private final Value value; private Operator operator = Operator.EQUAL; public Filter(Field field, Value value) { this.field = field; this.value = value; } public Filter(Field field, Value value, Operator operator) { this(field, value); this.operator = operator; } public String getField() { return field.toString(); } public V getValue() { return value != null ? value.cast() : null; } public Operator getOperator() { return operator; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/FilterAccessor.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import java.io.Serializable; import java.util.Map; import org.bonitasoft.web.rest.server.datastore.converter.ValueConverter; import org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException; /** * @author Vincent Elcrin */ public class FilterAccessor { private final Map filters; public FilterAccessor(Map filters) { this.filters = filters; } /** * @throws APIFilterMandatoryException * if the value of the filter is null */ public String getMandatory(String filter) { ensureFilterValue(filter); return getFilters().get(filter); } /** * @throws APIFilterMandatoryException * In case of any exception during conversion */ public S getMandatory(String filter, ValueConverter converter) { String value = getMandatory(filter); try { return converter.convert(value); } catch (Exception e) { throw new APIFilterMandatoryException(filter, e); } } private Map getFilters() { return filters; } private void ensureFilterValue(String filter) { if (getFilters() == null || !getFilters().containsKey(filter)) { throw new APIFilterMandatoryException(filter); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/FilterCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import java.io.Serializable; /** * @author Vincent Elcrin */ public interface FilterCreator { Filter create(String attribute, String value); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/Filters.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.web.rest.server.datastore.converter.StringValueConverter; /** * @author Vincent Elcrin */ public class Filters { private final List> filters = new ArrayList<>(); public Filters(final Map filters, final FilterCreator filterCreator) { if (filters != null) { addFilters(filters, filterCreator); } } /** * Constructor that directly uses Engine filter keys, instead of having to transcode Web filter keys to Engine * filter keys. * * @param filters the filter key, value map */ public Filters(final Map filters) { if (filters != null) { final Iterator> it = filters.entrySet().iterator(); while (it.hasNext()) { final Entry filterEntry = it.next(); final Field field = new Field(filterEntry.getKey()); final Value fieldValue = new Value<>(filterEntry.getValue(), new StringValueConverter()); final Filter filter = new Filter<>(field, fieldValue); this.filters.add(filter); } } } private void addFilters(final Map filters, final FilterCreator filterCreator) { final Iterator> it = filters.entrySet().iterator(); while (it.hasNext()) { addEntry(it.next(), filterCreator); } } private void addEntry(final Entry entry, final FilterCreator filterCreator) { filters.add(createFilter(filterCreator, entry)); } private Filter createFilter(final FilterCreator filterCreator, final Entry entry) { return filterCreator.create(entry.getKey(), entry.getValue()); } public List> asList() { return filters; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/FormMappingTypeCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import java.io.Serializable; import org.bonitasoft.engine.form.FormMappingSearchDescriptor; import org.bonitasoft.engine.form.FormMappingType; import org.bonitasoft.web.rest.server.datastore.converter.EmptyAttributeConverter; /** * author Emmanuel Duchastenier */ public class FormMappingTypeCreator extends GenericFilterCreator { public FormMappingTypeCreator() { super(new EmptyAttributeConverter()); } @Override public Filter create(String attribute, String value) { if (FormMappingSearchDescriptor.TYPE.equals(attribute)) { return new Filter<>(new Field(attribute), new Value<>(value, FormMappingType::valueOf)); } return super.create(attribute, value); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/GenericFilterCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import java.io.Serializable; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE; /** * @author Vincent Elcrin * @author Emmanuel Duchastenier */ public class GenericFilterCreator implements FilterCreator { protected final AttributeConverter fieldConverter; public GenericFilterCreator(AttributeConverter fieldConverter) { this.fieldConverter = fieldConverter; } @Override public Filter create(String attribute, String value) { return new Filter<>(new Field(attribute, fieldConverter), getTypedValue(attribute, value)); } private Value getTypedValue(String attributeName, String attributeValue) { if (fieldConverter.getValueTypeMapping().get(attributeName) == TYPE.BOOLEAN) { return new BooleanValue(attributeValue); } return new StrValue(attributeValue); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/LongValue.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import org.bonitasoft.web.rest.server.datastore.converter.LongValueConverter; /** * @author Vincent Elcrin */ public class LongValue extends Value { public LongValue(String value) { super(value, new LongValueConverter()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/StrValue.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import org.bonitasoft.web.rest.server.datastore.converter.StringValueConverter; /** * @author Vincent Elcrin */ public class StrValue extends Value { public StrValue(String value) { super(value, new StringValueConverter()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/filter/Value.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import java.io.Serializable; import org.bonitasoft.web.rest.server.datastore.converter.ValueConverter; /** * @author Vincent Elcrin */ public class Value { private final String value; private final ValueConverter converter; public Value(String value, ValueConverter converter) { this.value = value; this.converter = converter; } public V cast() { return converter.convert(value); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/Avatars.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; public interface Avatars { String PATH = "../API/avatars/"; } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/ContactDataConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import org.bonitasoft.engine.identity.ContactData; import org.bonitasoft.web.rest.model.identity.AbstractContactDataItem; import org.bonitasoft.web.rest.server.datastore.converter.ItemConverter; /** * @author Vincent Elcrin */ public abstract class ContactDataConverter extends ItemConverter { @Override public C convert(ContactData item) { if (item == null) { return createContactDataItem(); } C contactData = createContactDataItem(); contactData.setAddress(item.getAddress()); contactData.setEmail(item.getEmail()); contactData.setPhoneNumber(item.getPhoneNumber()); contactData.setMobileNumber(item.getMobileNumber()); contactData.setFaxNumber(item.getFaxNumber()); contactData.setBuilding(item.getBuilding()); contactData.setRoom(item.getRoom()); contactData.setZipCode(item.getZipCode()); contactData.setCity(item.getCity()); contactData.setState(item.getState()); contactData.setCountry(item.getCountry()); contactData.setWebsite(item.getWebsite()); setContactId(contactData); return contactData; } public abstract C createContactDataItem(); public abstract void setContactId(C contactData); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/GroupCreatorConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import static org.bonitasoft.web.toolkit.client.common.util.StringUtil.isBlank; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.IconDescriptor; import org.bonitasoft.engine.identity.GroupCreator; import org.bonitasoft.web.rest.model.identity.GroupItem; import org.bonitasoft.web.rest.server.engineclient.GroupEngineClient; /** * @author Colin PUY */ public class GroupCreatorConverter { private final GroupEngineClient groupEngineClient; public GroupCreatorConverter(GroupEngineClient groupEngineClient) { this.groupEngineClient = groupEngineClient; } public GroupCreator convert(GroupItem item) { if (item == null) { return null; } GroupCreator builder = new GroupCreator(item.getName()); if (!isBlank(item.getDescription())) { builder.setDescription(item.getDescription()); } if (!isBlank(item.getDisplayName())) { builder.setDisplayName(item.getDisplayName()); } if (!isBlank(item.getIcon())) { IconDescriptor iconDescriptor = new BonitaHomeFolderAccessor().getIconFromFileSystem(item.getIcon()); builder.setIcon(iconDescriptor.getFilename(), iconDescriptor.getContent()); } if (!isBlank(item.getParentGroupId())) { String parentGroupPath = groupEngineClient.getPath(item.getParentGroupId()); builder.setParentPath(parentGroupPath); } return builder; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/GroupDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import static org.bonitasoft.web.toolkit.client.data.APIID.toLongList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.GroupCreator; import org.bonitasoft.engine.identity.GroupSearchDescriptor; import org.bonitasoft.engine.identity.GroupUpdater; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.identity.GroupItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor; import org.bonitasoft.web.rest.server.engineclient.EngineClientFactory; import org.bonitasoft.web.rest.server.engineclient.GroupEngineClient; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Nicolas Tith */ public class GroupDatastore extends CommonDatastore implements DatastoreHasAdd, DatastoreHasUpdate, DatastoreHasGet, DatastoreHasSearch, DatastoreHasDelete { public GroupDatastore(final APISession engineSession) { super(engineSession); } public GroupEngineClient getGroupEngineClient() { return new EngineClientFactory(new EngineAPIAccessor(getEngineSession())) .createGroupEngineClient(); } @Override public void delete(final List ids) { getGroupEngineClient().delete(toLongList(ids)); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { try { final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, orders, search); addStringFilterToSearchBuilder(filters, builder, GroupItem.ATTRIBUTE_NAME, GroupSearchDescriptor.NAME); addStringFilterToSearchBuilder(filters, builder, GroupItem.ATTRIBUTE_DISPLAY_NAME, GroupSearchDescriptor.DISPLAY_NAME); addStringFilterToSearchBuilder(filters, builder, GroupItem.ATTRIBUTE_PARENT_PATH, GroupSearchDescriptor.PARENT_PATH); SearchResult engineSearchResults; engineSearchResults = TenantAPIAccessor.getIdentityAPI(getEngineSession()).searchGroups(builder.done()); return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(), new GroupItemConverter().convert(engineSearchResults.getResult())); } catch (final BonitaException e) { throw new APIException(e); } } @Override public GroupItem get(final APIID id) { Group result = getGroupEngineClient().get(id.toLong()); return new GroupItemConverter().convert(result); } @Override public GroupItem update(final APIID id, final Map attributes) { GroupUpdater updater = new GroupUpdaterConverter(getGroupEngineClient()).convert(attributes); Group group = getGroupEngineClient().update(id.toLong(), updater); return new GroupItemConverter().convert(group); } @Override public GroupItem add(final GroupItem group) { GroupCreator creator = new GroupCreatorConverter(getGroupEngineClient()).convert(group); Group result = getGroupEngineClient().create(creator); return new GroupItemConverter().convert(result); } public Long getNumberOfUsers(final APIID groupId) { try { return TenantAPIAccessor.getIdentityAPI(getEngineSession()).getNumberOfUsersInGroup(groupId.toLong()); } catch (final BonitaException e) { throw new APIException(e); } } @Override protected GroupItem convertEngineToConsoleItem(final Group group) { throw new RuntimeException("Unimplemented method"); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/GroupItemConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.web.rest.model.identity.GroupItem; import org.bonitasoft.web.rest.server.datastore.converter.ItemConverter; public class GroupItemConverter extends ItemConverter { @Override public GroupItem convert(Group group) { GroupItem groupItem = new GroupItem(); groupItem.setCreatedByUserId(group.getCreatedBy()); groupItem.setCreationDate(group.getCreationDate()); groupItem.setDescription(group.getDescription()); groupItem.setDisplayName(group.getDisplayName()); groupItem.setIcon(group.getIconId() == null ? "" : Avatars.PATH + group.getIconId()); groupItem.setId(group.getId()); groupItem.setLastUpdateDate(group.getLastUpdate()); groupItem.setName(group.getName()); groupItem.setParentPath(group.getParentPath()); groupItem.setPath(group.getPath()); return groupItem; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/GroupUpdaterConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import java.util.Map; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.IconDescriptor; import org.bonitasoft.engine.identity.GroupUpdater; import org.bonitasoft.web.rest.model.identity.GroupItem; import org.bonitasoft.web.rest.server.engineclient.GroupEngineClient; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; /** * @author Colin PUY */ public class GroupUpdaterConverter { private final GroupEngineClient groupEngineClient; public GroupUpdaterConverter(GroupEngineClient groupEngineClient) { this.groupEngineClient = groupEngineClient; } public GroupUpdater convert(Map attributes) { GroupUpdater updater = new GroupUpdater(); if (attributes.containsKey(GroupItem.ATTRIBUTE_DESCRIPTION)) { updater.updateDescription(attributes.get(GroupItem.ATTRIBUTE_DESCRIPTION)); } if (!MapUtil.isBlank(attributes, GroupItem.ATTRIBUTE_ICON)) { IconDescriptor iconDescriptor = getBonitaHomeFolderAccessor() .getIconFromFileSystem(attributes.get(GroupItem.ATTRIBUTE_ICON)); updater.updateIcon(iconDescriptor.getFilename(), iconDescriptor.getContent()); } if (!MapUtil.isBlank(attributes, GroupItem.ATTRIBUTE_NAME)) { updater.updateName(attributes.get(GroupItem.ATTRIBUTE_NAME)); } if (attributes.containsKey(GroupItem.ATTRIBUTE_DISPLAY_NAME)) { updater.updateDisplayName(attributes.get(GroupItem.ATTRIBUTE_DISPLAY_NAME)); } if (attributes.containsKey(GroupItem.ATTRIBUTE_PARENT_GROUP_ID)) { String parentGroupPath = getParentGroupPath(attributes.get(GroupItem.ATTRIBUTE_PARENT_GROUP_ID)); updater.updateParentPath(parentGroupPath); } return updater; } BonitaHomeFolderAccessor getBonitaHomeFolderAccessor() { return new BonitaHomeFolderAccessor(); } private String getParentGroupPath(String groupId) { if (groupId.isEmpty()) { return ""; } else { return groupEngineClient.getPath(groupId); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/MembershipDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import static org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil.computeIndex; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.*; import org.bonitasoft.engine.identity.UserMembership; import org.bonitasoft.engine.identity.UserMembershipCriterion; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.web.rest.model.identity.MembershipItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public class MembershipDatastore extends CommonDatastore implements DatastoreHasAdd, DatastoreHasSearch, DatastoreHasDelete { public MembershipDatastore(final APISession engineSession) { super(engineSession); } /** * @return * @throws InvalidSessionException * @throws BonitaHomeNotSetException * @throws ServerAPIException * @throws UnknownAPITypeException */ private IdentityAPI getIdentityAPI() throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getIdentityAPI(getEngineSession()); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CONVERT // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected MembershipItem convertEngineToConsoleItem(final UserMembership item) { final MembershipItem result = new MembershipItem(); result.setUserId(item.getUserId()); result.setRoleId(item.getRoleId()); result.setGroupId(item.getGroupId()); result.setAssignedByUserId(item.getAssignedBy()); result.setAssignedDate(item.getAssignedDate()); return result; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CRUDS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public void delete(final List ids) { try { for (final APIID id : ids) { getIdentityAPI().deleteUserMembership(id.getPartAsLong(0), id.getPartAsLong(1), id.getPartAsLong(2)); } } catch (final BonitaException e) { throw new APIException(e); } } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { try { Long userId = MapUtil.getValueAsLong(filters, MembershipItem.ATTRIBUTE_USER_ID); // Get results final List engineItems = getIdentityAPI().getUserMemberships( userId, computeIndex(page, resultsByPage), resultsByPage, UserMembershipCriterion.valueOf(orders)); // Convert results final List consoleSearchResults = convertEngineToConsoleItemsList( engineItems); // Get total results final long total = getIdentityAPI() .getNumberOfUserMemberships(MapUtil.getValueAsLong(filters, MembershipItem.ATTRIBUTE_USER_ID)); // Return search object return new ItemSearchResult<>( page, resultsByPage, total, consoleSearchResults); } catch (final BonitaException e) { throw new APIException(e); } } @Override public MembershipItem add(final MembershipItem item) { try { return convertEngineToConsoleItem(getIdentityAPI() .addUserMembership(item.getUserId().toLong(), item.getGroupId().toLong(), item.getRoleId().toLong())); } catch (AlreadyExistsException e) { throw new APIForbiddenException(new T_("This membership is already added to user"), e); } catch (final BonitaException e) { throw new APIException(e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/PersonalContactDataDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import java.util.Map; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.identity.ContactData; import org.bonitasoft.engine.identity.ContactDataUpdater; import org.bonitasoft.engine.identity.UserUpdater; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.identity.PersonalContactDataItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Paul AMAR */ public class PersonalContactDataDatastore extends CommonDatastore implements DatastoreHasGet, DatastoreHasUpdate, DatastoreHasAdd { public PersonalContactDataDatastore(final APISession engineSession) { super(engineSession); } @Override public PersonalContactDataItem get(final APIID id) { try { // Hard-coded at true because we want to retrieve ContactData final ContactData result = TenantAPIAccessor.getIdentityAPI(getEngineSession()) .getUserContactData(id.toLong(), true); return createContactDataItemConverter(id).convert(result); } catch (final NotFoundException e) { return null; } catch (final BonitaException e) { throw new APIException(e); } } @Override public PersonalContactDataItem update(final APIID id, final Map attributes) { try { final ContactDataUpdater personalDataUpdater = new ContactDataUpdater(); if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_EMAIL)) { personalDataUpdater.setEmail(attributes.get(PersonalContactDataItem.ATTRIBUTE_EMAIL)); } if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_PHONE)) { personalDataUpdater.setPhoneNumber(attributes.get(PersonalContactDataItem.ATTRIBUTE_PHONE)); } if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_MOBILE)) { personalDataUpdater.setMobileNumber(attributes.get(PersonalContactDataItem.ATTRIBUTE_MOBILE)); } if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_FAX)) { personalDataUpdater.setFaxNumber(attributes.get(PersonalContactDataItem.ATTRIBUTE_FAX)); } if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_BUILDING)) { personalDataUpdater.setBuilding(attributes.get(PersonalContactDataItem.ATTRIBUTE_BUILDING)); } if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_ROOM)) { personalDataUpdater.setRoom(attributes.get(PersonalContactDataItem.ATTRIBUTE_ROOM)); } if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_ADDRESS)) { personalDataUpdater.setAddress(attributes.get(PersonalContactDataItem.ATTRIBUTE_ADDRESS)); } if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_ZIPCODE)) { personalDataUpdater.setZipCode(attributes.get(PersonalContactDataItem.ATTRIBUTE_ZIPCODE)); } if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_CITY)) { personalDataUpdater.setCity(attributes.get(PersonalContactDataItem.ATTRIBUTE_CITY)); } if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_STATE)) { personalDataUpdater.setState(attributes.get(PersonalContactDataItem.ATTRIBUTE_STATE)); } if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_COUNTRY)) { personalDataUpdater.setCountry(attributes.get(PersonalContactDataItem.ATTRIBUTE_COUNTRY)); } if (attributes.containsKey(PersonalContactDataItem.ATTRIBUTE_WEBSITE)) { personalDataUpdater.setWebsite(attributes.get(PersonalContactDataItem.ATTRIBUTE_WEBSITE)); } UserUpdater userUpdater = new UserUpdater() .setPersonalContactData(personalDataUpdater) // TODO remove once handle by engine .setProfessionalContactData(new ContactDataUpdater()); TenantAPIAccessor.getIdentityAPI(getEngineSession()).updateUser(id.toLong(), userUpdater); return get(id); } catch (final BonitaException e) { throw new APIException(e); } } @Override public PersonalContactDataItem add(final PersonalContactDataItem item) { // get the user id final APIID idUser = item.getId(); final Map attributes = item.getAttributes(); return update(idUser, attributes); } @Override protected PersonalContactDataItem convertEngineToConsoleItem(final ContactData item) { throw new RuntimeException("Use ContactDataConverter instead!"); } private ContactDataConverter createContactDataItemConverter(final APIID id) { return new ContactDataConverter<>() { @Override public PersonalContactDataItem createContactDataItem() { return new PersonalContactDataItem(); } @Override public void setContactId(PersonalContactDataItem contactData) { contactData.setId(id); } }; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/ProfessionalContactDataDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import java.util.Map; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.NotFoundException; import org.bonitasoft.engine.identity.ContactData; import org.bonitasoft.engine.identity.ContactDataUpdater; import org.bonitasoft.engine.identity.UserUpdater; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.identity.ProfessionalContactDataItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Paul AMAR */ public class ProfessionalContactDataDatastore extends CommonDatastore implements DatastoreHasGet, DatastoreHasUpdate, DatastoreHasAdd { public ProfessionalContactDataDatastore(final APISession engineSession) { super(engineSession); } @Override public ProfessionalContactDataItem get(final APIID id) { try { // Hard-coded at true because we want to retrieve ContactData final ContactData result = TenantAPIAccessor.getIdentityAPI(getEngineSession()) .getUserContactData(id.toLong(), false); return createContactDataItemConverter(id).convert(result); } catch (final NotFoundException e) { return null; } catch (final BonitaException e) { throw new APIException(e); } } @Override public ProfessionalContactDataItem update(final APIID id, final Map attributes) { try { final ContactDataUpdater professionalDataUpdater = new ContactDataUpdater(); if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_EMAIL)) { professionalDataUpdater.setEmail(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_EMAIL)); } if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_PHONE)) { professionalDataUpdater.setPhoneNumber(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_PHONE)); } if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_MOBILE)) { professionalDataUpdater.setMobileNumber(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_MOBILE)); } if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_FAX)) { professionalDataUpdater.setFaxNumber(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_FAX)); } if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_BUILDING)) { professionalDataUpdater.setBuilding(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_BUILDING)); } if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_ROOM)) { professionalDataUpdater.setRoom(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_ROOM)); } if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_ADDRESS)) { professionalDataUpdater.setAddress(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_ADDRESS)); } if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_ZIPCODE)) { professionalDataUpdater.setZipCode(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_ZIPCODE)); } if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_CITY)) { professionalDataUpdater.setCity(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_CITY)); } if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_STATE)) { professionalDataUpdater.setState(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_STATE)); } if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_COUNTRY)) { professionalDataUpdater.setCountry(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_COUNTRY)); } if (attributes.containsKey(ProfessionalContactDataItem.ATTRIBUTE_WEBSITE)) { professionalDataUpdater.setWebsite(attributes.get(ProfessionalContactDataItem.ATTRIBUTE_WEBSITE)); } UserUpdater userUpdater = new UserUpdater() .setProfessionalContactData(professionalDataUpdater) // TODO remove once handle by engine .setPersonalContactData(new ContactDataUpdater()); TenantAPIAccessor.getIdentityAPI(getEngineSession()).updateUser(id.toLong(), userUpdater); return get(id); } catch (final BonitaException e) { throw new APIException(e); } } @Override public ProfessionalContactDataItem add(final ProfessionalContactDataItem item) { return update(item.getId(), item.getAttributes()); } @Override protected ProfessionalContactDataItem convertEngineToConsoleItem(ContactData item) { throw new RuntimeException("Use ContactDataConverter instead!"); } private ContactDataConverter createContactDataItemConverter(final APIID id) { return new ContactDataConverter<>() { @Override public ProfessionalContactDataItem createContactDataItem() { return new ProfessionalContactDataItem(); } @Override public void setContactId(ProfessionalContactDataItem contactData) { contactData.setId(id); } }; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/RoleDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.IconDescriptor; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.exception.*; import org.bonitasoft.engine.identity.*; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.web.rest.model.identity.RoleItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public class RoleDatastore extends CommonDatastore implements DatastoreHasGet, DatastoreHasSearch, DatastoreHasAdd, DatastoreHasUpdate, DatastoreHasDelete { public RoleDatastore(final APISession engineSession) { super(engineSession); } @Override public void delete(final List ids) { try { final List longIds = new ArrayList<>(); for (final APIID id : ids) { longIds.add(id.toLong()); } getIdentityAPI().deleteRoles(longIds); } catch (final BonitaException e) { throw new APIException(e); } } @Override public RoleItem update(final APIID id, final Map attributes) { try { final RoleUpdater updater = new RoleUpdater(); if (attributes.containsKey(RoleItem.ATTRIBUTE_NAME)) { updater.setName(attributes.get(RoleItem.ATTRIBUTE_NAME)); } if (attributes.containsKey(RoleItem.ATTRIBUTE_DISPLAY_NAME)) { updater.setDisplayName(attributes.get(RoleItem.ATTRIBUTE_DISPLAY_NAME)); } if (attributes.containsKey(RoleItem.ATTRIBUTE_DESCRIPTION)) { updater.setDescription(attributes.get(RoleItem.ATTRIBUTE_DESCRIPTION)); } if (!MapUtil.isBlank(attributes, RoleItem.ATTRIBUTE_ICON)) { IconDescriptor iconDescriptor = getBonitaHomeFolderAccessor() .getIconFromFileSystem(attributes.get(RoleItem.ATTRIBUTE_ICON)); updater.setIcon(iconDescriptor.getFilename(), iconDescriptor.getContent()); } return convertEngineToConsoleItem(getIdentityAPI().updateRole(id.toLong(), updater)); } catch (final RoleNotFoundException e) { throw new APINotFoundException(new T_("Unable to find role %roleId%", new Arg("roleId", id))); } catch (final BonitaException e) { throw new APIException(e); } } @Override public RoleItem add(final RoleItem role) { try { final RoleCreator creator = new RoleCreator(role.getName()); if (!StringUtil.isBlank(role.getDisplayName())) { creator.setDisplayName(role.getDisplayName()); } if (!StringUtil.isBlank(role.getDescription())) { creator.setDescription(role.getDescription()); } if (!StringUtil.isBlank(role.getIcon())) { IconDescriptor iconDescriptor = getBonitaHomeFolderAccessor().getIconFromFileSystem(role.getIcon()); creator.setIcon(iconDescriptor.getFilename(), iconDescriptor.getContent()); } return convertEngineToConsoleItem(getIdentityAPI().createRole(creator)); } catch (AlreadyExistsException e) { throw new APIForbiddenException( new T_("Can't create role. Role '%roleName%' already exists", new Arg("roleName", role.getName())), e); } catch (final BonitaException e) { throw new APIException(e); } } BonitaHomeFolderAccessor getBonitaHomeFolderAccessor() { return new BonitaHomeFolderAccessor(); } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { try { final SearchOptionsBuilder builder = SearchOptionsBuilderUtil.buildSearchOptions(page, resultsByPage, orders, search); addStringFilterToSearchBuilder(filters, builder, RoleItem.ATTRIBUTE_NAME, RoleSearchDescriptor.NAME); addStringFilterToSearchBuilder(filters, builder, RoleItem.ATTRIBUTE_DISPLAY_NAME, RoleSearchDescriptor.DISPLAY_NAME); final SearchResult engineSearchResults = getIdentityAPI().searchRoles(builder.done()); final List consoleSearchResults = new ArrayList<>(); for (final Role engineItem : engineSearchResults.getResult()) { consoleSearchResults.add(convertEngineToConsoleItem(engineItem)); } return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(), consoleSearchResults); } catch (final BonitaException e) { throw new APIException(e); } } @Override public RoleItem get(final APIID id) { try { return convertEngineToConsoleItem(getIdentityAPI().getRole(id.toLong())); } catch (final RoleNotFoundException e) { throw new APINotFoundException(new T_("Unable to find role %roleId%", new Arg("roleId", id))); } catch (final BonitaException e) { throw new APIException(e); } } public Long getNumberOfUsers(final APIID roleId) { try { return getIdentityAPI().getNumberOfUsersInRole(roleId.toLong()); } catch (final BonitaException e) { throw new APIException(e); } } IdentityAPI getIdentityAPI() throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getIdentityAPI(getEngineSession()); } @Override protected RoleItem convertEngineToConsoleItem(final Role item) { final RoleItem roleItem = new RoleItem(); roleItem.setId(item.getId()); roleItem.setName(item.getName()); roleItem.setDisplayName(item.getDisplayName()); roleItem.setDescription(item.getDescription()); roleItem.setIcon(item.getIconId() == null ? "" : Avatars.PATH + item.getIconId()); roleItem.setCreatedByUserId(item.getCreatedBy()); roleItem.setCreationDate(item.getCreationDate()); roleItem.setLastUpdateDate(item.getLastUpdate()); return roleItem; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/UserCreatorConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.IconDescriptor; import org.bonitasoft.engine.identity.UserCreator; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.toolkit.client.data.APIID; public class UserCreatorConverter { public UserCreator convert(UserItem user) { if (user == null) { return null; } final UserCreator userCreator = new UserCreator(user.getUserName(), user.getPassword()) .setFirstName(user.getFirstName()) .setLastName(user.getLastName()) .setTitle(user.getTitle()) .setJobTitle(user.getJobTitle()) .setEnabled(user.isEnabled()); if (user.getIcon() != null && !user.getIcon().isEmpty()) { IconDescriptor iconDescriptor = new BonitaHomeFolderAccessor().getIconFromFileSystem(user.getIcon()); userCreator.setIcon(iconDescriptor.getFilename(), iconDescriptor.getContent()); } final APIID managerId = user.getManagerId(); if (managerId != null && managerId.isValidLongID()) { userCreator.setManagerUserId(managerId.toLong()); } return userCreator; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/UserDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserCreator; import org.bonitasoft.engine.identity.UserUpdater; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.datastore.utils.Sorts; import org.bonitasoft.web.rest.server.engineclient.EngineAPIAccessor; import org.bonitasoft.web.rest.server.engineclient.EngineClientFactory; import org.bonitasoft.web.rest.server.engineclient.ProcessEngineClient; import org.bonitasoft.web.rest.server.engineclient.UserEngineClient; import org.bonitasoft.web.rest.server.framework.api.*; import org.bonitasoft.web.rest.server.framework.exception.APIAttributeException; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public class UserDatastore extends CommonDatastore implements DatastoreHasAdd, DatastoreHasGet, DatastoreHasSearch, DatastoreHasUpdate, DatastoreHasDelete { protected EngineClientFactory engineClientFactory; protected UserItemConverter userItemConverter; public UserDatastore(final APISession engineSession) { super(engineSession); userItemConverter = new UserItemConverter(); engineClientFactory = new EngineClientFactory(new EngineAPIAccessor(engineSession)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // C.R.U.D. // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public UserItem add(final UserItem user) { UserCreator userCreator = new UserCreatorConverter().convert(user); User createdUser = getUserEngineClient().create(userCreator); return userItemConverter.convert(createdUser); } public UserItem update(final APIID id, final Map attributes) { UserUpdater userUpdater = new UserUpdaterConverter().convert(attributes, getBonitaHomeFolderAccessor()); User user = getUserEngineClient().update(id.toLong(), userUpdater); return userItemConverter.convert(user); } BonitaHomeFolderAccessor getBonitaHomeFolderAccessor() { return new BonitaHomeFolderAccessor(); } @Override public UserItem get(final APIID id) { User user = getUserEngineClient().get(id.toLong()); return userItemConverter.convert(user); } /** * Search for users * * @param page * The page to display * @param resultsByPage * The number of results by page * @param search * Search terms * @param filters * The filters to doAuthorize. There will be an AND operand between filters. * @param orders * The order to doAuthorize to the search * @return This method returns an ItemSearch result containing the returned data and information about the total * possible results. */ @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, Map filters) { if (filters.containsKey(UserItem.FILTER_PROCESS_ID)) { String processId = filters.get(UserItem.FILTER_PROCESS_ID); filters.remove(UserItem.FILTER_PROCESS_ID); return searchUsersWhoCanStartProcess(processId, page, resultsByPage, search, filters, orders); } else if (filters.containsKey(UserItem.FILTER_HUMAN_TASK_ID)) { String taskId = filters.get(UserItem.FILTER_HUMAN_TASK_ID); filters.remove(UserItem.FILTER_HUMAN_TASK_ID); return searchUsersWhoCanPerformTask(taskId, page, resultsByPage, search, filters, orders); } else { return searchUsers(page, resultsByPage, search, filters, orders); } } private ItemSearchResult searchUsers(final int page, final int resultsByPage, final String search, final Map filters, final String orders) { SearchOptionsCreator searchOptionsCreator = buildSearchOptionCreator(page, resultsByPage, search, filters, orders); SearchResult engineSearchResults = getUserEngineClient().search(searchOptionsCreator.create()); return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(), userItemConverter.convert(engineSearchResults.getResult())); } protected ItemSearchResult searchUsersWhoCanStartProcess(final String processId, final int page, final int resultsByPage, final String search, final Map filters, final String orders) { SearchOptionsCreator searchOptionsCreator = buildSearchOptionCreator(page, resultsByPage, search, filters, orders); SearchResult engineSearchResults; try { engineSearchResults = getProcessEngineClient().getProcessApi().searchUsersWhoCanStartProcessDefinition( Long.valueOf(processId), searchOptionsCreator.create()); } catch (NumberFormatException e) { throw new APIAttributeException(UserItem.FILTER_PROCESS_ID, "Cannot convert process id: " + processId + " into long."); } catch (SearchException e) { throw new APIException(e); } return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(), userItemConverter.convert(engineSearchResults.getResult())); } protected ItemSearchResult searchUsersWhoCanPerformTask(final String taskId, final int page, final int resultsByPage, final String search, final Map filters, final String orders) { SearchResult engineSearchResults; try { SearchOptionsCreator searchOptionsCreator = buildSearchOptionCreator(page, resultsByPage, search, filters, orders); engineSearchResults = getProcessEngineClient().getProcessApi().searchUsersWhoCanExecutePendingHumanTask( Long.valueOf(taskId), searchOptionsCreator.create()); } catch (NumberFormatException e) { throw new APIAttributeException(UserItem.FILTER_HUMAN_TASK_ID, "Cannot convert human task id: " + taskId + " into long."); } return new ItemSearchResult<>(page, resultsByPage, engineSearchResults.getCount(), userItemConverter.convert(engineSearchResults.getResult())); } protected SearchOptionsCreator buildSearchOptionCreator(final int page, final int resultsByPage, final String search, final Map filters, final String orders) { return new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders, new UserSearchAttributeConverter()), new Filters(filters, new UserFilterCreator(new UserSearchAttributeConverter()))); } /** * Delete users * * @param ids */ public void delete(final List ids) { getUserEngineClient().delete(APIID.toLongList(ids)); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CONVERTS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // protected for tests UserEngineClient getUserEngineClient() { return engineClientFactory.createUserEngineClient(); } ProcessEngineClient getProcessEngineClient() { return engineClientFactory.createProcessEngineClient(); } @Override protected UserItem convertEngineToConsoleItem(final User user) { throw new RuntimeException("Unimplemented method"); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/UserFilterCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator; class UserFilterCreator extends GenericFilterCreator { UserFilterCreator(UserSearchAttributeConverter converter) { super(converter); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/UserItemConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import org.bonitasoft.engine.identity.User; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.rest.server.datastore.converter.ItemConverter; import org.bonitasoft.web.toolkit.client.data.APIID; public class UserItemConverter extends ItemConverter { @Override public UserItem convert(User user) { if (user == null) { return null; } final UserItem result = new UserItem(); result.setId(APIID.makeAPIID(user.getId())); result.setFirstName(user.getFirstName()); result.setLastName(user.getLastName()); result.setPassword(null); result.setUserName(user.getUserName()); result.setManagerId(user.getManagerUserId()); result.setEnabled(user.isEnabled()); // Add default icon if icon if empty if (user.getIconId() != null) { result.setIcon(Avatars.PATH + user.getIconId()); } else { result.setIcon(UserItem.DEFAULT_USER_ICON); } result.setCreationDate(user.getCreationDate()); result.setCreatedByUserId(user.getCreatedBy()); result.setLastUpdateDate(user.getLastUpdate()); result.setLastConnectionDate(user.getLastConnection()); result.setTitle(user.getTitle()); result.setJobTitle(user.getJobTitle()); return result; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/UserSearchAttributeConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import static org.bonitasoft.web.rest.model.identity.UserItem.*; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.identity.UserSearchDescriptor; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE; /** * @author Colin PUY, Anthony Birembaut */ public class UserSearchAttributeConverter implements AttributeConverter { protected static final Map mapping = new HashMap<>(); private static final Map valueTypeMapping = new HashMap<>(); static { addAttributeConverterItem(ATTRIBUTE_ID, UserSearchDescriptor.ID); addAttributeConverterItem(ATTRIBUTE_FIRSTNAME, UserSearchDescriptor.FIRST_NAME); addAttributeConverterItem(ATTRIBUTE_LASTNAME, UserSearchDescriptor.LAST_NAME); addAttributeConverterItem(ATTRIBUTE_USERNAME, UserSearchDescriptor.USER_NAME); addAttributeConverterItem(ATTRIBUTE_ENABLED, UserSearchDescriptor.ENABLED, TYPE.BOOLEAN); addAttributeConverterItem(ATTRIBUTE_MANAGER_ID, UserSearchDescriptor.MANAGER_USER_ID); addAttributeConverterItem(FILTER_GROUP_ID, UserSearchDescriptor.GROUP_ID); addAttributeConverterItem(FILTER_ROLE_ID, UserSearchDescriptor.ROLE_ID); } @Override public String convert(final String attribute) { return mapping.get(attribute); } private static void addAttributeConverterItem(String webSearchKey, String engineSearchKey, TYPE attributeType) { mapping.put(webSearchKey, engineSearchKey); valueTypeMapping.put(webSearchKey, attributeType); } private static void addAttributeConverterItem(String webSearchKey, String engineSearchKey) { addAttributeConverterItem(webSearchKey, engineSearchKey, TYPE.STRING); } @Override public Map getValueTypeMapping() { return valueTypeMapping; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/organization/UserUpdaterConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import java.util.Map; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.IconDescriptor; import org.bonitasoft.engine.identity.UserUpdater; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; public class UserUpdaterConverter { public UserUpdater convert(Map attributes, BonitaHomeFolderAccessor bonitaHomeFolderAccessor) { UserUpdater userUpdater = new UserUpdater(); if (attributes.containsKey(UserItem.ATTRIBUTE_FIRSTNAME)) { userUpdater.setFirstName(attributes.get(UserItem.ATTRIBUTE_FIRSTNAME)); } if (attributes.containsKey(UserItem.ATTRIBUTE_LASTNAME)) { userUpdater.setLastName(attributes.get(UserItem.ATTRIBUTE_LASTNAME)); } if (attributes.containsKey(UserItem.ATTRIBUTE_PASSWORD)) { userUpdater.setPassword(attributes.get(UserItem.ATTRIBUTE_PASSWORD)); } if (attributes.containsKey(UserItem.ATTRIBUTE_USERNAME)) { userUpdater.setUserName(attributes.get(UserItem.ATTRIBUTE_USERNAME)); } if (attributes.containsKey(UserItem.ATTRIBUTE_MANAGER_ID)) { Long managerId = getManagerId(attributes); userUpdater.setManagerId(managerId); } if (!StringUtil.isBlank(attributes.get(UserItem.ATTRIBUTE_ICON))) { IconDescriptor iconDescriptor = bonitaHomeFolderAccessor .getIconFromFileSystem(attributes.get(UserItem.ATTRIBUTE_ICON)); userUpdater.setIcon(iconDescriptor.getFilename(), iconDescriptor.getContent()); } if (attributes.containsKey(UserItem.ATTRIBUTE_TITLE)) { userUpdater.setTitle(attributes.get(UserItem.ATTRIBUTE_TITLE)); } if (attributes.containsKey(UserItem.ATTRIBUTE_JOB_TITLE)) { userUpdater.setJobTitle(attributes.get(UserItem.ATTRIBUTE_JOB_TITLE)); } if (attributes.containsKey(UserItem.ATTRIBUTE_ENABLED)) { userUpdater.setEnabled("true".equals(attributes.get(UserItem.ATTRIBUTE_ENABLED))); } return userUpdater; } private Long getManagerId(final Map attributes) { try { return Long.valueOf(attributes.get(UserItem.ATTRIBUTE_MANAGER_ID)); } catch (NumberFormatException e) { return 0L; } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/page/CustomPageContentValidator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.page; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Objects; import java.util.Optional; import java.util.Properties; import java.util.stream.Stream; import org.bonitasoft.console.common.server.page.CustomPageService; import org.bonitasoft.engine.page.ContentType; public class CustomPageContentValidator { private static final String INDEX_GROOVY = "Index.groovy"; private static final String INDEX_HTML = "index.html"; private static final String PAGE_PROPERTIES = "page.properties"; private static final String THEME_CSS = "theme.css"; public void validate(File pageFolder) throws InvalidPageZipContentException { final File[] rootFiles = pageFolder.listFiles(); if (rootFiles == null) { throw new InvalidPageZipContentException( "Content not found."); } Properties pageProperties = Stream.of(rootFiles) .filter(file -> file.getName().matches(PAGE_PROPERTIES)) .findFirst() .map(this::loadProperties) .orElseThrow(() -> new InvalidPageZipContentException( String.format("%s descriptor is missing.", PAGE_PROPERTIES))); String contentType = pageProperties.getProperty(CustomPageService.PROPERTY_CONTENT_TYPE); Optional resouresFolder = Stream.of(rootFiles) .filter(file -> file.getName().matches(CustomPageService.RESOURCES_PROPERTY)) .findFirst(); if (Objects.equals(contentType, ContentType.THEME)) { if (!resouresFolder.filter(resources -> new File(resources, THEME_CSS).exists()).isPresent()) { throw new InvalidPageZipContentException(String.format("%s is missing.", THEME_CSS)); } } else if (!Objects.equals(contentType, ContentType.API_EXTENSION)) { if (Stream.of(rootFiles) .noneMatch(file -> file.getName().matches(INDEX_HTML) || file.getName().matches(INDEX_GROOVY)) && !resouresFolder.filter(resources -> new File(resources, INDEX_HTML).exists() || new File(resources, INDEX_GROOVY).exists()).isPresent()) { throw new InvalidPageZipContentException( String.format("%s or %s is missing.", INDEX_HTML, INDEX_GROOVY)); } } } private Properties loadProperties(File pagePropertyFile) { Properties pageProperties = new java.util.Properties(); try (InputStream is = new FileInputStream(pagePropertyFile)) { pageProperties.load(is); } catch (IOException e) { throw new RuntimeException("Failed to load page.properties file.", e); } return pageProperties; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/page/InvalidPageZipContentException.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.page; /** * @author Fabio Lombardi */ public class InvalidPageZipContentException extends Exception { /** * UID */ private static final long serialVersionUID = 1L; public InvalidPageZipContentException() { super(); } public InvalidPageZipContentException(final String message) { super(message); } public InvalidPageZipContentException(final String message, final Throwable cause) { super(message, cause); } public InvalidPageZipContentException(final Throwable cause) { super(cause); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/page/PageDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.page; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Random; import org.apache.commons.io.FileUtils; import org.bonitasoft.console.common.server.page.CustomPageService; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.console.common.server.servlet.FileUploadServlet; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.UnzipUtil; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.page.*; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.extension.page.PageResourceProvider; import org.bonitasoft.web.rest.model.portal.page.PageDefinition; import org.bonitasoft.web.rest.model.portal.page.PageItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.datastore.utils.Sorts; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.codehaus.groovy.control.CompilationFailedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Fabio Lombardi, Anthony Birembaut */ public class PageDatastore extends CommonDatastore implements DatastoreHasAdd, DatastoreHasUpdate, DatastoreHasGet, DatastoreHasSearch, DatastoreHasDelete { /** * Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(PageDatastore.class.getName()); /** * page files */ public static final String UNMAPPED_ATTRIBUTE_ZIP_FILE = "pageZip"; static final String PAGE_TOKEN_PREFIX = "custompage_"; protected final WebBonitaConstantsUtils constants; protected final PageAPI pageAPI; protected final CustomPageService customPageService; private final BonitaHomeFolderAccessor tenantFolder; private final CustomPageContentValidator pageContentValidator; public PageDatastore(final APISession engineSession, final WebBonitaConstantsUtils constantsValue, final PageAPI pageAPI, final CustomPageService customPageService, final BonitaHomeFolderAccessor tenantFolder) { super(engineSession); constants = constantsValue; this.pageAPI = pageAPI; this.customPageService = customPageService; this.tenantFolder = tenantFolder; this.pageContentValidator = new CustomPageContentValidator(); } /** * @deprecated as of 9.0.0, a page should be created at startup. */ @Override @Deprecated(since = "9.0.0") public PageItem add(final PageItem pageItem) { final String zipFileAttribute = pageItem.getAttributeValue(UNMAPPED_ATTRIBUTE_ZIP_FILE); // Name pattern: "TokenID::originalFileName" final String[] filenames = zipFileAttribute.split(FileUploadServlet.RESPONSE_SEPARATOR); final String filename = filenames[0]; String originalFileName = getOriginalFilename(filenames, filename, pageItem.getAttributes()); pageItem.setContentName(originalFileName); try { final APISession engineSession = getEngineSession(); final File zipFile = tenantFolder.getTempFile(filename); final File unzipPageTempFolder = unzipContentFile(zipFile); pageContentValidator.validate(unzipPageTempFolder); final Page page = createEnginePage(pageItem, zipFile); final PageItem addedPage = convertEngineToConsoleItem(page); PageResourceProvider pageResourceProvider = customPageService.getPageResourceProvider(page); customPageService.writePageToPageDirectory(page, pageResourceProvider, unzipPageTempFolder, engineSession); deleteTempDirectory(unzipPageTempFolder); return addedPage; } catch (final BonitaException | IOException | InvalidPageZipContentException e) { throw new APIException(e); } finally { tenantFolder.removeUploadedTempContent(filename); } } protected File unzipContentFile(final File zipFile) throws InvalidPageZipContentException { File unzipPageTempFolder = null; try { final Random randomGen = new Random(); final int tempPageFolder = randomGen.nextInt(); unzipPageTempFolder = new File(constants.getTempFolder(), String.valueOf(tempPageFolder)); UnzipUtil.unzip(zipFile, unzipPageTempFolder.getPath(), false); } catch (final Exception e) { deleteTempDirectory(unzipPageTempFolder); throw new InvalidPageZipContentException("Unable to unzip the page content.", e); } return unzipPageTempFolder; } protected boolean isPageTokenValid(final String urlToken) { return urlToken.matches(PAGE_TOKEN_PREFIX + "\\p{Alnum}+"); } protected void deleteTempDirectory(final File unzipPage) { try { if (unzipPage.isDirectory()) { IOUtilDeleteDir(unzipPage); } } catch (final IOException e) { throw new APIException(e); } } protected void IOUtilDeleteDir(final File unzipPage) throws IOException { IOUtil.deleteDir(unzipPage); } protected Page createEnginePage(final PageItem pageItem, final File zipFile) throws CreationException, IOException, UpdateException { try { final byte[] zipContent = readZipFile(zipFile); Page page = pageAPI.createPage(pageItem.getContentName(), zipContent); if (pageItem.getProcessId() != null) { final PageUpdater pageUpdater = new PageUpdater(); pageUpdater.setProcessDefinitionId(pageItem.getProcessId().toLong()); if (pageItem.getContentType() != null) { pageUpdater.setContentType(pageItem.getContentType()); } page = pageAPI.updatePage(page.getId(), pageUpdater); } return page; } finally { zipFile.delete(); } } protected byte[] readZipFile(final File zipFile) throws IOException { return FileUtils.readFileToByteArray(zipFile); } protected PageCreator buildPageCreatorFrom(final PageItem pageItem) { final PageCreator pageCreator = new PageCreator(pageItem.getUrlToken(), pageItem.getContentName()); pageCreator.setDescription(pageItem.getDescription()); pageCreator.setDisplayName(pageItem.getDisplayName()); return pageCreator; } @Override public PageItem get(final APIID id) { try { final Page pageItem = pageAPI.getPage(id.toLong()); return convertEngineToConsoleItem(pageItem); } catch (PageNotFoundException e) { throw new APIItemNotFoundException(PageDefinition.TOKEN, id); } } @Override public void delete(final List ids) { try { for (final APIID id : ids) { final Page page = pageAPI.getPage(id.toLong()); final APISession engineSession = getEngineSession(); PageResourceProvider pageResourceProvider = customPageService.getPageResourceProvider(page); customPageService.ensurePageFolderIsUpToDate(engineSession, pageResourceProvider); pageAPI.deletePage(id.toLong()); customPageService.removePageLocally(pageResourceProvider); } } catch (final BonitaException | IOException e) { throw new APIException(e); } } protected List APIIdsToLong(final List ids) { final List result = new ArrayList<>(ids.size()); for (final APIID id : ids) { result.add(id.toLong()); } return result; } @Override public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { // Build search final SearchOptionsCreator creator = makeSearchOptionCreator(page, resultsByPage, search, orders, filters); // Run search depending on filters passed SearchResult searchResult; try { searchResult = runSearch(creator); // Convert to ConsoleItems return new ItemSearchResult<>(page, resultsByPage, searchResult.getCount(), convertEngineToConsoleItemsList(searchResult.getResult())); } catch (final SearchException e) { throw new APIException(e); } } protected PageSearchDescriptorConverter getSearchDescriptorConverter() { return new PageSearchDescriptorConverter(); } protected SearchOptionsCreator makeSearchOptionCreator(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final SearchOptionsCreator searchOptionsCreator = new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders, getSearchDescriptorConverter()), new Filters(filters, new PageFilterCreator(getSearchDescriptorConverter()))); final SearchOptionsBuilder builder = searchOptionsCreator.getBuilder(); if (filters.containsKey(PageItem.FILTER_CONTENT_TYPE) && "processPage".equalsIgnoreCase(filters.get(PageSearchDescriptor.CONTENT_TYPE))) { builder.leftParenthesis().filter(PageSearchDescriptor.CONTENT_TYPE, "form") .or().filter(PageSearchDescriptor.CONTENT_TYPE, "page") .rightParenthesis(); } else { addStringFilterToSearchBuilder(filters, builder, PageItem.FILTER_CONTENT_TYPE, PageSearchDescriptor.CONTENT_TYPE); } return searchOptionsCreator; } /** * @param creator * @return * @throws SearchException */ protected SearchResult runSearch(final SearchOptionsCreator creator) throws SearchException { return pageAPI.searchPages(creator.create()); } /** * @deprecated as of 9.0.0, a page should be updated at startup. */ @Override @Deprecated(since = "9.0.0") public PageItem update(final APIID id, final Map attributes) { String filename = null; File zipFile = null; try { Long pageId = id.toLong(); Page page = pageAPI.getPage(pageId); PageItem updatedPage = null; if (attributes.containsKey(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE)) { final String zipFileAttribute = attributes.get(UNMAPPED_ATTRIBUTE_ZIP_FILE); if (zipFileAttribute != null && !zipFileAttribute.isEmpty()) { final String[] filenames = zipFileAttribute.split(FileUploadServlet.RESPONSE_SEPARATOR); filename = filenames[0]; String originalFileName = getOriginalFilename(filenames, filename, attributes); final APISession engineSession = getEngineSession(); zipFile = tenantFolder.getTempFile(filename); final File unzipPageTempFolder = unzipContentFile(zipFile); pageContentValidator.validate(unzipPageTempFolder); try { updatePageContent(page, zipFile); final PageUpdater pageUpdater = new PageUpdater(); pageUpdater.setContentName(originalFileName); page = pageAPI.updatePage(pageId, pageUpdater); updatedPage = convertEngineToConsoleItem(page); } finally { PageResourceProvider pageResourceProvider = customPageService.getPageResourceProvider(page); customPageService.writePageToPageDirectory(page, pageResourceProvider, unzipPageTempFolder, engineSession); deleteTempDirectory(unzipPageTempFolder); } } } return updatedPage; } catch (final BonitaException | IOException | InvalidPageZipContentException e) { throw new APIException(e); } finally { if (filename != null) { tenantFolder.removeUploadedTempContent(filename); } if (zipFile != null) { zipFile.delete(); } } } protected String getOriginalFilename(final String[] filenames, final String tempFilename, final Map attributes) { String originalFileName; if (filenames.length > 1) { originalFileName = filenames[1]; } else { originalFileName = attributes.getOrDefault(PageItem.ATTRIBUTE_CONTENT_NAME, tempFilename); } return originalFileName; } protected void updatePageContent(final Page page, final File zipFile) throws IOException, CompilationFailedException, BonitaException { if (zipFile != null) { PageResourceProvider pageResourceProvider = customPageService.getPageResourceProvider(page); customPageService.ensurePageFolderIsUpToDate(getEngineSession(), pageResourceProvider); pageAPI.updatePageContent(page.getId(), FileUtils.readFileToByteArray(zipFile)); } customPageService.removePageLocally(page); } @Override protected PageItem convertEngineToConsoleItem(final Page item) { if (item != null) { return new PageItemConverter().convert(item); } return null; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/page/PageDatastoreFactory.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.page; import org.bonitasoft.console.common.server.page.CustomPageService; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.session.APISession; public class PageDatastoreFactory { public PageDatastore create(final APISession engineSession, final WebBonitaConstantsUtils constantsValue, final PageAPI pageAPI) { return new PageDatastore(engineSession, constantsValue, pageAPI, new CustomPageService(), new BonitaHomeFolderAccessor()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/page/PageFilterCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.page; import org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator; class PageFilterCreator extends GenericFilterCreator { PageFilterCreator(final PageSearchDescriptorConverter converter) { super(converter); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/page/PageItemConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.page; import org.bonitasoft.engine.page.Page; import org.bonitasoft.web.rest.model.portal.page.PageItem; import org.bonitasoft.web.rest.server.datastore.converter.ItemConverter; import org.bonitasoft.web.toolkit.client.data.APIID; public class PageItemConverter extends ItemConverter { @Override public PageItem convert(final Page engineItem) { final PageItem pageItem = new PageItem(); pageItem.setId(engineItem.getId()); pageItem.setProcessId(engineItem.getProcessDefinitionId()); pageItem.setUrlToken(engineItem.getName()); pageItem.setDisplayName(engineItem.getDisplayName()); pageItem.setIsProvided(engineItem.isProvided()); pageItem.setDescription(engineItem.getDescription()); pageItem.setCreatedByUserId(APIID.makeAPIID(engineItem.getInstalledBy())); pageItem.setCreationDate(engineItem.getInstallationDate()); pageItem.setLastUpdateDate(engineItem.getLastModificationDate()); pageItem.setUpdatedByUserId(engineItem.getLastUpdatedBy()); pageItem.setContentName(engineItem.getContentName()); pageItem.setContentType(engineItem.getContentType()); pageItem.setIsEditable(engineItem.isEditable()); pageItem.setIsRemovable(engineItem.isRemovable()); return pageItem; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/page/PageSearchDescriptorConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.page; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.page.PageSearchDescriptor; import org.bonitasoft.web.rest.model.portal.page.PageItem; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE; /** * @author Emmanuel Duchastenier */ public class PageSearchDescriptorConverter implements AttributeConverter { private static final Map attributeNameMapping = new HashMap<>(); private static final Map valueTypeMapping = new HashMap<>(); static { createMappings(); } public Map getValueTypeMapping() { return valueTypeMapping; } private static void createMappings() { addAttributeConverterItem(PageItem.ATTRIBUTE_ID, PageSearchDescriptor.ID, TYPE.STRING); addAttributeConverterItem(PageItem.ATTRIBUTE_URL_TOKEN, PageSearchDescriptor.NAME, TYPE.STRING); addAttributeConverterItem(PageItem.ATTRIBUTE_DISPLAY_NAME, PageSearchDescriptor.DISPLAY_NAME, TYPE.STRING); addAttributeConverterItem(PageItem.ATTRIBUTE_IS_PROVIDED, PageSearchDescriptor.PROVIDED, TYPE.BOOLEAN); addAttributeConverterItem(PageItem.ATTRIBUTE_CREATED_BY_USER_ID, PageSearchDescriptor.INSTALLED_BY, TYPE.STRING); addAttributeConverterItem(PageItem.ATTRIBUTE_CREATION_DATE, PageSearchDescriptor.INSTALLATION_DATE, TYPE.STRING); addAttributeConverterItem(PageItem.ATTRIBUTE_LAST_UPDATE_DATE, PageSearchDescriptor.LAST_MODIFICATION_DATE, TYPE.STRING); //CONTENT_TYPE is managed differently in order to accept a OR with form and page //addAttributeConverterItem(PageItem.FILTER_CONTENT_TYPE, PageSearchDescriptor.CONTENT_TYPE, TYPE.STRING); addAttributeConverterItem(PageItem.ATTRIBUTE_PROCESS_ID, PageSearchDescriptor.PROCESS_DEFINITION_ID, TYPE.STRING); } @Override public String convert(final String attribute) { if (PageItem.FILTER_CONTENT_TYPE.equals(attribute)) { return MapUtil.getValue(attributeNameMapping, attribute, ""); } else { return MapUtil.getMandatory(attributeNameMapping, attribute); } } private static void addAttributeConverterItem(String webSearchKey, String engineSearchKey, TYPE attributeType) { attributeNameMapping.put(webSearchKey, engineSearchKey); valueTypeMapping.put(webSearchKey, attributeType); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/GetProfileHelper.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.web.rest.model.portal.profile.ProfileItem; import org.bonitasoft.web.rest.server.engineclient.ProfileEngineClient; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Vincent Elcrin */ public class GetProfileHelper implements DatastoreHasGet { private final ProfileEngineClient profileClient; public GetProfileHelper(ProfileEngineClient profileClient) { this.profileClient = profileClient; } @Override public ProfileItem get(APIID id) { Profile profile = profileClient.getProfile(id.toLong()); return new ProfileItemConverter().convert(profile); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/ProfileItemConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.web.rest.model.portal.profile.ProfileItem; import org.bonitasoft.web.rest.server.datastore.converter.ItemConverter; /** * @author Vincent Elcrin */ public class ProfileItemConverter extends ItemConverter { public static final String USER_PROFILE_NAME = "User"; public static final String ADMIN_PROFILE_NAME = "Administrator"; public static final String PROCESSMANAGER_PROFILE_NAME = "Process manager"; public static final String TEAMMANAGER_PROFILE_NAME = "Team manager"; public static final String USER_PROFILE_ICONPATH = "icons/profiles/profileUser.png"; public static final String ADMIN_PROFILE_ICONPATH = "icons/profiles/profileAdmin.png"; public static final String PROCESSMANAGER_PROFILE_ICONPATH = "icons/profiles/profileProcessManager.png"; public static final String TEAMMANAGER_PROFILE_ICONPATH = "icons/profiles/profileTeamManager.png"; public static final String DEFAULT_PROFILE_ICONPATH = "icons/profiles/profileDefault.png"; @Override public ProfileItem convert(final Profile profile) { final ProfileItem item = new ProfileItem(); item.setId(profile.getId()); item.setName(profile.getName()); item.setDescription(profile.getDescription()); item.setIsDefault(profile.isDefault()); item.setIcon(getIconPath(profile.getName())); item.setUpdatedByUserId(profile.getLastUpdatedBy()); item.setLastUpdateDate(profile.getLastUpdateDate()); item.setCreatedByUserId(profile.getCreatedBy()); item.setCreationDate(profile.getCreationDate()); return item; } protected String getIconPath(final String profileName) { if (USER_PROFILE_NAME.equals(profileName)) { return USER_PROFILE_ICONPATH; } else if (ADMIN_PROFILE_NAME.equals(profileName)) { return ADMIN_PROFILE_ICONPATH; } else if (PROCESSMANAGER_PROFILE_NAME.equals(profileName)) { return PROCESSMANAGER_PROFILE_ICONPATH; } else if (TEAMMANAGER_PROFILE_NAME.equals(profileName)) { return TEAMMANAGER_PROFILE_ICONPATH; } else { return DEFAULT_PROFILE_ICONPATH; } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/ProfileSearchDescriptorConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.profile.ProfileSearchDescriptor; import org.bonitasoft.web.rest.model.portal.profile.ProfileItem; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute.TYPE; /** * @author Vincent Elcrin */ public class ProfileSearchDescriptorConverter implements AttributeConverter { private static final Map mapping = new HashMap<>(); static { mapping.put(ProfileItem.ATTRIBUTE_ID, ProfileSearchDescriptor.ID); mapping.put(ProfileItem.ATTRIBUTE_NAME, ProfileSearchDescriptor.NAME); } @Override public String convert(String attribute) { String descriptor = mapping.get(attribute); if (descriptor == null) { throw new RuntimeException(attribute + " has no valid search descriptor"); } return descriptor; } @Override public Map getValueTypeMapping() { return Collections.emptyMap(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/SearchProfilesHelper.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile; import java.util.List; import java.util.Map; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.web.rest.model.portal.profile.ProfileItem; import org.bonitasoft.web.rest.server.datastore.converter.ItemSearchResultConverter; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.datastore.utils.Sorts; import org.bonitasoft.web.rest.server.engineclient.ProfileEngineClient; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; public class SearchProfilesHelper implements DatastoreHasSearch { private final ProfileEngineClient profileClient; public SearchProfilesHelper(ProfileEngineClient profileClient) { this.profileClient = profileClient; } @Override public ItemSearchResult search(int page, int resultsByPage, String search, String orders, Map filters) { if (isFilteredBy(filters, ProfileItem.FILTER_USER_ID)) { return searchProfilesForUser(page, filters); } return searchProfiles(page, resultsByPage, search, orders, filters); } private boolean isFilteredBy(Map filters, String filterName) { return filters != null && !MapUtil.isBlank(filters, filterName); } private ItemSearchResult searchProfiles(int page, int resultsByPage, String search, String orders, Map filters) { SearchOptionsCreator options = makeSearchOptions(page, resultsByPage, search, orders, filters); SearchResult searchProfiles = profileClient.searchProfiles(options.create()); return new ItemSearchResultConverter<>(page, resultsByPage, searchProfiles, new ProfileItemConverter()) .toItemSearchResult(); } private ItemSearchResult searchProfilesForUser(int page, Map filters) { long userId = Long.parseLong(filters.get(ProfileItem.FILTER_USER_ID)); List profiles = profileClient.listProfilesForUser(userId); return new ItemSearchResult<>(page, profiles.size(), profiles.size(), new ProfileItemConverter().convert(profiles)); } private SearchOptionsCreator makeSearchOptions(int page, int resultsByPage, String search, String orders, Map filters) { return new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders, new ProfileSearchDescriptorConverter()), new Filters(filters, new GenericFilterCreator(new ProfileSearchDescriptorConverter()))); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/member/AddProfileMemberHelper.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile.member; import org.bonitasoft.engine.profile.ProfileMember; import org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem; import org.bonitasoft.web.rest.server.engineclient.ProfileMemberEngineClient; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Vincent Elcrin */ public class AddProfileMemberHelper implements DatastoreHasAdd { private static final Long UNSET = null; private final ProfileMemberEngineClient engineClient; public AddProfileMemberHelper(ProfileMemberEngineClient engineClient) { this.engineClient = engineClient; } @Override public ProfileMemberItem add(ProfileMemberItem item) { ProfileMember addedProfileMember = addProfileMember(item.getProfileId(), item.getUserId(), item.getGroupId(), item.getRoleId()); return new ProfileMemberItemConverter().convert(addedProfileMember); } private ProfileMember addProfileMember(APIID profileId, APIID userId, APIID groupId, APIID roleId) { return engineClient.createProfileMember(toLong(profileId), toLong(userId), toLong(groupId), toLong(roleId)); } private Long toLong(APIID apiId) { if (apiId == null || !apiId.isValidLongID()) { return UNSET; } return apiId.toLong(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/member/DeleteProfileMemberHelper.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile.member; import java.util.List; import org.bonitasoft.web.rest.server.engineclient.ProfileMemberEngineClient; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Vincent Elcrin */ public class DeleteProfileMemberHelper implements DatastoreHasDelete { private final ProfileMemberEngineClient engineClient; public DeleteProfileMemberHelper(ProfileMemberEngineClient engineClient) { this.engineClient = engineClient; } @Override public void delete(List ids) { for (APIID id : ids) { delete(id); } } private void delete(APIID id) { engineClient.deleteProfileMember(id.toLong()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/member/MemberType.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile.member; import org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem; /** * @author Vincent Elcrin */ public enum MemberType { USER(AbstractMemberItem.VALUE_MEMBER_TYPE_USER), GROUP(AbstractMemberItem.VALUE_MEMBER_TYPE_GROUP), ROLE( AbstractMemberItem.VALUE_MEMBER_TYPE_ROLE), MEMBERSHIP(AbstractMemberItem.VALUE_MEMBER_TYPE_MEMBERSHIP); private final String type; MemberType(String type) { this.type = type; } public String getType() { return type; } public static MemberType from(String type) { for (MemberType candidate : MemberType.values()) { if (candidate.getType().equals(type)) { return candidate; } } throw new IllegalArgumentException("No enum const for " + type + " found"); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/member/MemberTypeConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile.member; import org.bonitasoft.web.rest.server.datastore.converter.ValueConverter; /** * @author Vincent Elcrin */ public class MemberTypeConverter implements ValueConverter { @Override public MemberType convert(String value) { return MemberType.from(value); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/member/ProfileMemberItemConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile.member; import org.bonitasoft.engine.profile.ProfileMember; import org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem; import org.bonitasoft.web.rest.server.datastore.converter.ItemConverter; /** * @author Vincent Elcrin */ public class ProfileMemberItemConverter extends ItemConverter { @Override public ProfileMemberItem convert(ProfileMember profileMember) { ProfileMemberItem item = new ProfileMemberItem(); item.setId(profileMember.getId()); item.setProfileId(profileMember.getProfileId()); item.setUserId(profileMember.getUserId()); item.setRoleId(profileMember.getRoleId()); item.setGroupId(profileMember.getGroupId()); return item; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/member/ProfileMemberSearchDescriptorConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile.member; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor; import org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Vincent Elcrin */ public class ProfileMemberSearchDescriptorConverter implements AttributeConverter { private static final Map mapping = new HashMap<>(); static { mapping.put(ProfileMemberItem.ATTRIBUTE_ID, ProfileMemberSearchDescriptor.ID); mapping.put(ProfileMemberItem.ATTRIBUTE_PROFILE_ID, ProfileMemberSearchDescriptor.PROFILE_ID); mapping.put(ProfileMemberItem.ATTRIBUTE_USER_ID, ProfileMemberSearchDescriptor.USER_ID); mapping.put(ProfileMemberItem.ATTRIBUTE_ROLE_ID, ProfileMemberSearchDescriptor.ROLE_ID); mapping.put(ProfileMemberItem.ATTRIBUTE_GROUP_ID, ProfileMemberSearchDescriptor.GROUP_ID); mapping.put(ProfileMemberItem.FILTER_MEMBER_TYPE, ""); } @Override public String convert(String attribute) { String descriptor = mapping.get(attribute); if (descriptor == null) { throw new RuntimeException(attribute + " has no valid search descriptor"); } return descriptor; } @Override public Map getValueTypeMapping() { return Collections.emptyMap(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/profile/member/SearchProfileMembersHelper.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile.member; import java.util.Map; import org.bonitasoft.engine.profile.ProfileMember; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem; import org.bonitasoft.web.rest.server.datastore.converter.ItemSearchResultConverter; import org.bonitasoft.web.rest.server.datastore.filter.FilterAccessor; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.filter.GenericFilterCreator; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.datastore.utils.Sorts; import org.bonitasoft.web.rest.server.engineclient.ProfileMemberEngineClient; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; /** * @author Vincent Elcrin */ public class SearchProfileMembersHelper implements DatastoreHasSearch { private final ProfileMemberEngineClient profileMemberClient; public SearchProfileMembersHelper(ProfileMemberEngineClient profileMemberClient) { this.profileMemberClient = profileMemberClient; } @Override public ItemSearchResult search(int page, int resultsByPage, String search, String orders, Map filters) { SearchOptionsCreator options = makeSearchOptions(page, resultsByPage, search, orders, filters); SearchResult searchResult = profileMemberClient .searchProfileMembers(getMemberType(filters).getType(), options.create()); return new ItemSearchResultConverter<>(page, resultsByPage, searchResult, new ProfileMemberItemConverter()).toItemSearchResult(); } private MemberType getMemberType(Map filters) { FilterAccessor filterAccess = new FilterAccessor(filters); return filterAccess.getMandatory(ProfileMemberItem.FILTER_MEMBER_TYPE, new MemberTypeConverter()); } private SearchOptionsCreator makeSearchOptions(int page, int resultsByPage, String search, String orders, Map filters) { return new SearchOptionsCreator(page, resultsByPage, search, new Sorts(orders, new ProfileMemberSearchDescriptorConverter()), new Filters(filters, new GenericFilterCreator(new ProfileMemberSearchDescriptorConverter()))); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/utils/SearchOptionsCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.utils; import static org.bonitasoft.web.rest.server.framework.utils.SearchOptionsBuilderUtil.computeIndex; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.web.rest.server.datastore.filter.Filter; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; /** * @author Vincent Elcrin */ public class SearchOptionsCreator { private final SearchOptionsBuilder builder; public SearchOptionsCreator(int page, int resultsByPage, String search, Sorts sorts, Filters filters) { builder = new SearchOptionsBuilder(computeIndex(page, resultsByPage), resultsByPage); builder.searchTerm(search); addSorts(builder, sorts); addFilters(builder, filters); } private void addSorts(SearchOptionsBuilder builder, Sorts sorts) { for (Sort sort : sorts.asList()) { builder.sort(sort.getField(), sort.getOrder()); } } private void addFilters(SearchOptionsBuilder builder, Filters filters) { for (Filter filter : filters.asList()) { addFilter(builder, filter); } } private void addFilter(SearchOptionsBuilder builder, Filter filter) { if (!StringUtil.isBlank(filter.getField())) { if (filter.getOperator() == Filter.Operator.DIFFERENT_FROM) { builder.differentFrom(filter.getField(), filter.getValue()); } else { builder.filter(filter.getField(), filter.getValue()); } } } public SearchOptions create() { return builder.done(); } public SearchOptionsBuilder getBuilder() { return builder; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/utils/Sort.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.utils; import org.bonitasoft.engine.search.Order; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.rest.server.datastore.converter.EmptyAttributeConverter; /** * Convenient object to deal with credentials sort options * Default sort order is ASCENDING * * @author Colin PUY */ public class Sort { public static final Order DEFAULT_ORDER = Order.ASC; private static final String SEPARATOR = " "; private final String field; private final Order order; private final AttributeConverter converter; public Sort(String sortValue, AttributeConverter converter) { this.converter = converter; field = getSortedFieldValue(sortValue); order = getOrder(sortValue); } public Sort(String sortValue) { this(sortValue, new EmptyAttributeConverter()); } private Order getOrder(String sortValue) { String[] split = sortValue.split(SEPARATOR); if (split.length > 1) { return Order.valueOf(split[1].toUpperCase()); } return DEFAULT_ORDER; } private String getSortedFieldValue(String sortValue) { String fieldValue = sortValue.split(SEPARATOR)[0]; String convertedFieldValue = converter.convert(fieldValue); return convertedFieldValue != null ? convertedFieldValue : fieldValue; } public String getField() { return field; } public Order getOrder() { return order; } @Override public String toString() { return getField() + SEPARATOR + getOrder(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Sort other = (Sort) obj; if (field == null) { if (other.field != null) { return false; } } else if (!field.equals(other.field)) { return false; } return order == other.order; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/utils/Sorts.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.utils; import java.util.ArrayList; import java.util.List; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.rest.server.datastore.converter.EmptyAttributeConverter; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; /** * @author Vincent Elcrin * Build list of sorts from a string order following the pattern * ,,... */ public class Sorts { private final List sorts; public Sorts(String orders, AttributeConverter converter) { sorts = parseOrders(orders, converter); } public Sorts(String orders) { this(orders, new EmptyAttributeConverter()); } private List parseOrders(final String orders, final AttributeConverter converter) { if (StringUtil.isBlank(orders)) { return new ArrayList<>(); } else { return buildSortList(orders, converter); } } /** * Convert orders and build list of sort adding items to sorts parameter * * @param sorts * @param orders * @param converter */ private List buildSortList(final String orders, final AttributeConverter converter) { final List sorts = new ArrayList<>(); for (String order : orders.split(",")) { sorts.add(new Sort(order, converter)); } return sorts; } public List asList() { return sorts; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/utils/Variable.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.utils; import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.annotation.JsonAnySetter; /** * Variable - Used for variable Json deserialization * * @author Colin PUY */ public class Variable { private final Map attributes = new HashMap<>(); @JsonAnySetter public void set(String name, Object value) { attributes.put(name, value); } public String getName() { return (String) attributes.get("name"); } public Object getValue() { return attributes.get("value"); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((getName() == null) ? 0 : getName().hashCode()); result = prime * result + ((getValue() == null) ? 0 : getValue().hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Variable other = (Variable) obj; return getName() != null && getName().equals(other.getName()) && getValue() != null && getValue().equals(other.getValue()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/utils/VariableMapper.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.utils; import java.io.Serializable; import org.bonitasoft.web.rest.server.framework.json.JacksonDeserializer; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; /** * Variable Mapper - Used for variable Json deserialization * * @author Colin PUY */ public class VariableMapper { private final JacksonDeserializer deserializer; private final Variable variable; public VariableMapper(Variable variable, JacksonDeserializer jacksonDeserializer) { this.variable = variable; this.deserializer = jacksonDeserializer; } public Serializable getSerializableValue(String className) { try { return deserializer.convertValue(variable.getValue(), Class.forName(className)); } catch (IllegalArgumentException e) { throw new APIException( new T_("%value% is not a valid value for %className%", new Arg("value", variable.getValue()), new Arg("className", className))); } catch (ClassNotFoundException e) { throw new APIException( new T_("%className% not found. Only jdk types are supported", new Arg("className", className))); } catch (ClassCastException e) { throw new APIException(new T_("%className% is not Serializable", new Arg("className", className))); } } public String getName() { return variable.getName(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((variable == null) ? 0 : variable.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; VariableMapper other = (VariableMapper) obj; return variable != null && variable.equals(other.variable); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/utils/VariablesMapper.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.utils; import java.util.ArrayList; import java.util.List; import org.bonitasoft.web.rest.server.framework.json.JacksonDeserializer; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; /** * Variables Mapper - Used for variables Json deserialization * * @author Colin PUY */ public class VariablesMapper { private final List variables; protected VariablesMapper(List variables, JacksonDeserializer deserializer) { this.variables = convertToMappers(variables, deserializer); } public static VariablesMapper fromJson(String json) { JacksonDeserializer deserializer = new JacksonDeserializer(); List variables = deserializer.deserializeList(json, Variable.class); return new VariablesMapper(variables, deserializer); } private List convertToMappers(List list, JacksonDeserializer deserializer) { ArrayList mappers = new ArrayList<>(); for (Variable variable : list) { if (!StringUtil.isBlank(variable.getName())) { mappers.add(new VariableMapper(variable, deserializer)); } } return mappers; } public List getVariables() { return variables; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/ActivityEngineClient.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import java.io.Serializable; import java.util.HashMap; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.data.DataInstance; import org.bonitasoft.engine.bpm.data.DataNotFoundException; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; /** * @author Colin PUY */ public class ActivityEngineClient { private final ProcessAPI processAPI; public ActivityEngineClient(ProcessAPI processAPI) { this.processAPI = processAPI; } public long countFailedActivities() { SearchOptions search = new SearchOptionsBuilder(0, 0) .filter(ActivityInstanceSearchDescriptor.STATE_NAME, ActivityItem.VALUE_STATE_FAILED).done(); try { return processAPI.searchActivities(search).getCount(); } catch (SearchException e) { throw new APIException("Error when counting failed activities", e); } } public DataInstance getDataInstance(String dataName, long activityId) { try { return processAPI.getActivityDataInstance(dataName, activityId); } catch (DataNotFoundException e) { throw new APINotFoundException(new T_("Unable to find data instance %dataName% for activity %activityId%", new Arg("dataName", dataName), new Arg("activityId", activityId)), e); } } public void updateVariables(long activityId, HashMap variables) { try { processAPI.updateActivityInstanceVariables(activityId, variables); } catch (UpdateException e) { throw new APIException(new T_("Error when updating %activityId% activity variables", new Arg("activityId", activityId)), e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/CaseEngineClient.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.process.ProcessActivationException; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessExecutionException; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.identity.UserNotFoundException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException; import org.bonitasoft.web.toolkit.client.common.exception.api.APITooManyRequestException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; /** * @author Colin PUY * @author Elias Ricken de Medeiros */ // TODO migrate all engine methods relating to cases (i.e. especially those in CaseDatastore) in this class public class CaseEngineClient { protected final ProcessAPI processAPI; public CaseEngineClient(final ProcessAPI processAPI) { this.processAPI = processAPI; } public ProcessInstance start(final long userId, final long processId) { return start(userId, processId, null); } public ProcessInstance start(final long userId, final long processId, final Map variables) { try { if (userId != -1L) { if (variables == null || variables.isEmpty()) { return processAPI.startProcess(userId, processId); } else { return processAPI.startProcess(userId, processId, variables); } } else { if (variables == null || variables.isEmpty()) { return processAPI.startProcess(processId); } else { return processAPI.startProcess(processId, variables); } } } catch (final ProcessDefinitionNotFoundException e) { throw new APINotFoundException( new T_("Can't start process, process %processId% not found", new Arg("processId", processId)), e); } catch (final ProcessActivationException e) { throw new APIException( new T_("Can't start process, process %processId% is not enabled", new Arg("processId", processId)), e); } catch (final ProcessExecutionException e) { if (e.getRetryAfter() != -1L) { throw new APITooManyRequestException( new T_("Error occurred when starting process %processId%. Case creation limit reached.", new Arg("processId", processId)), e.getRetryAfter()); } throw new APIException( new T_("Error occurred when starting process %processId%", new Arg("processId", processId)), e); } catch (final UserNotFoundException e) { throw new APIException( new T_("Can't start process %processId%, user %userId% not found", new Arg("processId", processId), new Arg("userId", userId)), e); } } public long countOpenedCases() { final SearchOptions search = new SearchOptionsBuilder(0, 0).done(); try { return processAPI.searchOpenProcessInstances(search).getCount(); } catch (final SearchException e) { throw new APIException("Error when counting opened cases", e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/CustomUserInfoEngineClient.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import java.util.List; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.CustomUserInfo; import org.bonitasoft.engine.identity.CustomUserInfoDefinition; import org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator; import org.bonitasoft.engine.identity.CustomUserInfoValue; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Vincent Elcrin */ public class CustomUserInfoEngineClient { private final IdentityAPI identity; public CustomUserInfoEngineClient(IdentityAPI identity) { this.identity = identity; } public CustomUserInfoDefinition createDefinition(CustomUserInfoDefinitionCreator creator) { try { return identity.createCustomUserInfoDefinition(creator); } catch (CreationException e) { throw new APIException(new T_("An error occurred while creating a definition"), e); } } public void deleteDefinition(long id) { try { identity.deleteCustomUserInfoDefinition(id); } catch (DeletionException e) { throw new APIException( new T_("An error occurred while deleting an item with the id %id%", new Arg("id", id)), e); } } public List listDefinitions(int startIndex, int maxResult) { return identity.getCustomUserInfoDefinitions(startIndex, maxResult); } public long countDefinitions() { return identity.getNumberOfCustomInfoDefinitions(); } public List listCustomInformation(long userId, int startIndex, int maxResult) { return identity.getCustomUserInfo(userId, startIndex, maxResult); } public SearchResult searchCustomUserInfoValues(SearchOptions options) { return identity.searchCustomUserInfoValues(options); } public CustomUserInfoValue setCustomUserInfoValue(long definitionId, long userId, String value) { try { return identity.setCustomUserInfoValue(definitionId, userId, value); } catch (UpdateException e) { throw new APIItemNotFoundException(org.bonitasoft.web.rest.model.identity.CustomUserInfoDefinition.TOKEN, APIID.makeAPIID(definitionId, userId)); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/CustomUserInfoEngineClientCreator.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import org.bonitasoft.engine.session.APISession; /** * @author Vincent Elcrin */ public class CustomUserInfoEngineClientCreator { public CustomUserInfoEngineClient create(APISession session) { return new CustomUserInfoEngineClient(new EngineAPIAccessor(session).getIdentityAPI()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/EngineAPIAccessor.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import org.bonitasoft.engine.api.GroupAPI; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.api.ProfileAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.api.TenantAdministrationAPI; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; /** * @author Vincent Elcrin */ public class EngineAPIAccessor { private final APISession session; public EngineAPIAccessor(final APISession session) { this.session = session; } public APISession getSession() { return session; } public ProfileAPI getProfileAPI() { try { return TenantAPIAccessor.getProfileAPI(getSession()); } catch (final BonitaException e) { throw new APIException("Error when getting engine process API", e); } } public ProcessAPI getProcessAPI() { try { return TenantAPIAccessor.getProcessAPI(getSession()); } catch (final BonitaException e) { throw new APIException("Error when getting engine process API", e); } } public IdentityAPI getIdentityAPI() { try { return TenantAPIAccessor.getIdentityAPI(getSession()); } catch (final BonitaException e) { throw new APIException("Error when getting engine identity API", e); } } public GroupAPI getGroupAPI() { try { return TenantAPIAccessor.getIdentityAPI(getSession()); } catch (final BonitaException e) { throw new APIException("Error when getting engine group API", e); } } public PageAPI getPageAPI() { try { return TenantAPIAccessor.getCustomPageAPI(getSession()); } catch (final BonitaException e) { throw new APIException("Error when getting engine page API", e); } } public TenantAdministrationAPI getTenantAdministrationAPI() { try { return TenantAPIAccessor.getTenantAdministrationAPI(getSession()); } catch (final BonitaException e) { throw new APIException("Error when getting engine tenant management API", e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/EngineClientFactory.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; /** * @author Vincent Elcrin */ public class EngineClientFactory { private final EngineAPIAccessor apiAccessor; public EngineClientFactory(final EngineAPIAccessor apiAccessor) { this.apiAccessor = apiAccessor; } public ProfileEngineClient createProfileEngineClient() { return new ProfileEngineClient(apiAccessor.getProfileAPI()); } public ProfileMemberEngineClient createProfileMemberEngineClient() { return new ProfileMemberEngineClient(apiAccessor.getProfileAPI()); } public ProcessEngineClient createProcessEngineClient() { return new ProcessEngineClient(apiAccessor.getProcessAPI()); } public CaseEngineClient createCaseEngineClient() { return new CaseEngineClient(apiAccessor.getProcessAPI()); } public HumanTaskEngineClient createHumanTaskEngineClient() { return new HumanTaskEngineClient(apiAccessor.getProcessAPI()); } public ActivityEngineClient createActivityEngineClient() { return new ActivityEngineClient(apiAccessor.getProcessAPI()); } public UserEngineClient createUserEngineClient() { return new UserEngineClient(apiAccessor.getIdentityAPI()); } public GroupEngineClient createGroupEngineClient() { return new GroupEngineClient(apiAccessor.getGroupAPI()); } public TenantManagementEngineClient createTenantManagementEngineClient() { return new TenantManagementEngineClient(apiAccessor.getTenantAdministrationAPI()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/GroupEngineClient.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import java.util.List; import org.bonitasoft.engine.api.GroupAPI; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.GroupCreator; import org.bonitasoft.engine.identity.GroupCreator.GroupField; import org.bonitasoft.engine.identity.GroupNotFoundException; import org.bonitasoft.engine.identity.GroupUpdater; import org.bonitasoft.web.rest.model.identity.GroupDefinition; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Paul AMAR */ public class GroupEngineClient { private final GroupAPI groupAPI; protected GroupEngineClient(GroupAPI groupAPI) { this.groupAPI = groupAPI; } public Group get(Long groupId) { try { return groupAPI.getGroup(groupId); } catch (GroupNotFoundException e) { throw new APIItemNotFoundException(GroupDefinition.TOKEN, APIID.makeAPIID(groupId)); } } public Group getGroupByPath(String groupPath) { try { return groupAPI.getGroupByPath(groupPath); } catch (GroupNotFoundException e) { throw new APIItemNotFoundException(GroupDefinition.TOKEN, APIID.makeAPIID(groupPath)); } } public String getPath(String groupId) { try { return groupAPI.getGroup(parseId(groupId)).getPath(); } catch (GroupNotFoundException e) { throw new APINotFoundException(new T_("Unable to get group path, group not found")); } } private long parseId(String groupId) { try { return Long.parseLong(groupId); } catch (NumberFormatException e) { throw new APIException("Illegal argument, groupId must be a number"); } } public void delete(List groupIds) { try { groupAPI.deleteGroups(groupIds); } catch (DeletionException e) { if (e.getCause() instanceof GroupNotFoundException) { throw new APIItemNotFoundException(GroupDefinition.TOKEN); } else { throw new APIException(new T_("Error when deleting groups"), e); } } } public Group update(long groupId, GroupUpdater groupUpdater) { try { return groupAPI.updateGroup(groupId, groupUpdater); } catch (GroupNotFoundException e) { throw new APIItemNotFoundException(GroupDefinition.TOKEN, APIID.makeAPIID(groupId)); } catch (UpdateException e) { throw new APIException(new T_("Error when updating group"), e); } catch (AlreadyExistsException e) { throw new APIForbiddenException(new T_("A group with the name %groupName% already exists", new Arg("groupName", groupUpdater.getFields().get(GroupField.NAME)))); } } public Group create(GroupCreator groupCreator) { try { return groupAPI.createGroup(groupCreator); } catch (AlreadyExistsException e) { throw new APIForbiddenException(new T_( "Can't create group. Group '%groupName%' already exists", new Arg("groupName", groupCreator.getFields().get(GroupField.NAME)))); } catch (CreationException e) { throw new APIException(new T_("Error when creating group"), e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/HumanTaskEngineClient.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstanceSearchDescriptor; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; /** * @author Colin PUY * @author Elias Ricken de Medeiros */ public class HumanTaskEngineClient { private final ProcessAPI processAPI; public HumanTaskEngineClient(final ProcessAPI processAPI) { this.processAPI = processAPI; } public long countOpenedHumanTasks() { final SearchOptions search = new SearchOptionsBuilder(0, 0) .filter(HumanTaskInstanceSearchDescriptor.STATE_NAME, HumanTaskItem.VALUE_STATE_READY).done(); try { return processAPI.searchHumanTaskInstances(search).getCount(); } catch (final BonitaException e) { throw new APIException("Error when counting opened cases", e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/ProcessEngineClient.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.data.DataDefinition; import org.bonitasoft.engine.bpm.process.*; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.ProcessInstanceHierarchicalDeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.web.rest.model.bpm.process.ProcessItem; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.bonitasoft.web.toolkit.client.data.APIID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Process engine API client * Wrapper to processAPI for all process methods * * @author Colin PUY */ public class ProcessEngineClient { private static final Logger LOGGER = LoggerFactory.getLogger(ProcessEngineClient.class.getName()); private static final int DELETE_PROCESS_BUNCH_SIZE = 100; protected final ProcessAPI processAPI; public ProcessEngineClient(final ProcessAPI processAPI) { this.processAPI = processAPI; } public ProcessDeploymentInfo getProcessDeploymentInfo(final long processId) { try { return getProcessApi().getProcessDeploymentInfo(processId); } catch (final ProcessDefinitionNotFoundException e) { LOGGER.debug("Unable to find process with id " + processId); throw new APIItemNotFoundException(org.bonitasoft.web.rest.model.bpm.process.ProcessDefinition.TOKEN, APIID.makeAPIID(processId)); } } public ProcessAPI getProcessApi() { return processAPI; } public ProcessDefinition deploy(final BusinessArchive businessArchive) { try { return getProcessApi().deploy(businessArchive); } catch (final AlreadyExistsException e) { final DesignProcessDefinition processDefinition = businessArchive.getProcessDefinition(); throw new APIForbiddenException(new T_("Process %appName% in version %version% already exists", new Arg("appName", processDefinition.getName()), new Arg( "version", processDefinition.getVersion())), e); } catch (final V6FormDeployException e) { final DesignProcessDefinition processDefinition = businessArchive.getProcessDefinition(); throw new APIException(new T_( "Process %appName% in version %version% contains 6.x Legacy artifacts (forms or case overview page). Those are based on Google Web Toolkit (GWT), a technology that is no longer supported by Bonita. To know more, check the documentation.", new Arg("appName", processDefinition.getName()), new Arg("version", processDefinition.getVersion())), e); } catch (final ProcessDeployException e) { throw new APIException(new T_("Unable to deploy business archive"), e); } } public void enableProcess(final long processId) { try { getProcessApi().enableProcess(processId); } catch (final BonitaException e) { throw new APIException(new T_("Unable to enable process"), e); } } public void disableProcess(final long processId) { try { getProcessApi().disableProcess(processId); } catch (final BonitaException e) { throw new APIException(new T_("Unable to disable process"), e); } } public ProcessDeploymentInfo updateProcessDeploymentInfo(final long processId, final ProcessDeploymentInfoUpdater processDeploymentInfoUpdater) { try { getProcessApi().updateProcessDeploymentInfo(processId, processDeploymentInfoUpdater); return getProcessApi().getProcessDeploymentInfo(processId); } catch (final BonitaException e) { throw new APIException(new T_("Error when updating process deployment informations"), e); } } public void deleteDisabledProcesses(final List processIds) { try { for (final Long id : processIds) { deleteProcessInstancesByBunch(id, DELETE_PROCESS_BUNCH_SIZE, processIds); deleteArchivedProcessInstancesByBunch(id, DELETE_PROCESS_BUNCH_SIZE, processIds); getProcessApi().deleteProcessDefinition(id); } } catch (final BonitaException e) { if (e.getCause() instanceof ProcessDefinitionNotFoundException) { throw new APIItemNotFoundException(org.bonitasoft.web.rest.model.bpm.process.ProcessDefinition.TOKEN); } else { throw new APIException("Error when deleting some of the processes in the list " + processIds, e); } } } /** * Delete archived process instances by bunch for a given processId */ public void deleteArchivedProcessInstancesByBunch(final long processId, final int bunchSize, final List processesAllowedToBeDeletedIds) throws DeletionException, ProcessDefinitionNotFoundException { long numberOfDeletedArchivedProcessInstances = 0; do { try { numberOfDeletedArchivedProcessInstances = getProcessApi().deleteArchivedProcessInstances(processId, 0, bunchSize); } catch (final ProcessInstanceHierarchicalDeletionException e) { final long parentProcessInstanceID = e.getProcessInstanceId(); final long parentProcessID = getProcessApi() .getProcessDefinitionIdFromProcessInstanceId(parentProcessInstanceID); if (processesAllowedToBeDeletedIds.contains(parentProcessID)) { deleteProcessInstancesByBunch(parentProcessID, DELETE_PROCESS_BUNCH_SIZE, processesAllowedToBeDeletedIds); } else { LOGGER.warn( "Process with ID " + processId + " cannot be deleted without also deleting its parent (" + parentProcessID + ")."); } } } while (numberOfDeletedArchivedProcessInstances >= bunchSize); } /** * Delete process instances by bunch for a given processId */ public void deleteProcessInstancesByBunch(final long processId, final int bunchSize, final List processesAllowedToBeDeletedIds) throws DeletionException, ProcessDefinitionNotFoundException { long numberOfDeletedProcessInstances = 0; do { try { numberOfDeletedProcessInstances = getProcessApi().deleteProcessInstances(processId, 0, bunchSize); } catch (final ProcessInstanceHierarchicalDeletionException e) { final long parentProcessInstanceID = e.getProcessInstanceId(); final long parentProcessID = getProcessApi() .getProcessDefinitionIdFromProcessInstanceId(parentProcessInstanceID); if (processesAllowedToBeDeletedIds.contains(parentProcessID)) { deleteProcessInstancesByBunch(parentProcessID, DELETE_PROCESS_BUNCH_SIZE, processesAllowedToBeDeletedIds); } else { LOGGER.warn( "Process with ID " + processId + " cannot be deleted without also deleting its parent (" + parentProcessID + ")."); } } } while (numberOfDeletedProcessInstances >= bunchSize); } public SearchResult searchProcessDefinitions(final SearchOptions searchOptions) { try { return getProcessApi().searchProcessDeploymentInfos(searchOptions); } catch (final SearchException e) { throw new APIException("Error when searching process definition", e); } } public SearchResult searchProcessDefinitionsSupervisedBy(final long userId, final SearchOptions searchOptions) { try { return getProcessApi().searchProcessDeploymentInfosSupervisedBy(userId, searchOptions); } catch (final SearchException e) { throw new APIException("Error when searching process definition supervised by user " + userId, e); } } public SearchResult searchUncategorizedProcessDefinitionsUserCanStart(final long userId, final SearchOptions searchOptions) { try { return getProcessApi().searchUncategorizedProcessDeploymentInfosCanBeStartedBy(userId, searchOptions); } catch (final SearchException e) { throw new APIException( "Error when searching uncategorized process definition which can be started by user " + userId, e); } } public SearchResult searchRecentlyStartedProcessDefinitions(final long userId, final SearchOptions searchOptions) { try { return getProcessApi().searchProcessDeploymentInfosStartedBy(userId, searchOptions); } catch (final SearchException e) { throw new APIException("Error when searching recently started process by user " + userId, e); } } public long countResolvedProcesses() { final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 0); builder.filter(ProcessDeploymentInfoSearchDescriptor.CONFIGURATION_STATE, ProcessItem.VALUE_CONFIGURATION_STATE_RESOLVED); try { return getProcessApi().searchProcessDeploymentInfos(builder.done()).getCount(); } catch (final SearchException e) { throw new APIException("Error when counting resolved processes", e); } } public SearchResult searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy( final long supervisorId, final SearchOptions searchOptions) { try { return getProcessApi().searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksSupervisedBy(supervisorId, searchOptions); } catch (final SearchException e) { throw new APIException("Error when searching process user can start", e); } } public SearchResult searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks( final SearchOptions searchOptions) { try { return getProcessApi().searchProcessDeploymentInfosWithAssignedOrPendingHumanTasks(searchOptions); } catch (final SearchException e) { throw new APIException("Error when searching process user can start", e); } } public SearchResult searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor( final long userId, final SearchOptions searchOptions) { try { return getProcessApi().searchProcessDeploymentInfosWithAssignedOrPendingHumanTasksFor(userId, searchOptions); } catch (final SearchException e) { throw new APIException("Error when searching process user can start", e); } } public SearchResult searchProcessDeploymentInfos(final long userId, final SearchOptions searchOptions) { try { return getProcessApi().searchProcessDeploymentInfosCanBeStartedBy(userId, searchOptions); } catch (final SearchException e) { throw new APIException("Error when searching process user can start", e); } } public List getProcessDataDefinitions(final long processId) { try { return processAPI.getProcessDataDefinitions(processId, 0, Integer.MAX_VALUE); } catch (final ProcessDefinitionNotFoundException e) { throw new APINotFoundException( new T_("Unable to get process data definitions, process %processId% not found", new Arg("processId", processId))); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/ProfileEngineClient.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import java.util.List; import org.bonitasoft.engine.api.ProfileAPI; import org.bonitasoft.engine.exception.RetrieveException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.profile.ProfileCriterion; import org.bonitasoft.engine.profile.ProfileNotFoundException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.web.rest.model.portal.profile.ProfileDefinition; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Vincent Elcrin */ public class ProfileEngineClient { private final ProfileAPI profileApi; public ProfileEngineClient(ProfileAPI profileApi) { this.profileApi = profileApi; } public Profile getProfile(Long id) { try { return profileApi.getProfile(id); } catch (RetrieveException e) { throw new APIException(e); } catch (ProfileNotFoundException e) { throw new APIItemNotFoundException(ProfileDefinition.TOKEN, APIID.makeAPIID(id)); } } public SearchResult searchProfiles(SearchOptions options) { try { return profileApi.searchProfiles(options); } catch (SearchException e) { throw new APIException(e); } } public List listProfilesForUser(long userId) { try { return profileApi.getProfilesForUser(userId, 0, Integer.MAX_VALUE, ProfileCriterion.ID_ASC); } catch (RetrieveException e) { throw new APIException(e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/ProfileMemberEngineClient.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import org.bonitasoft.engine.api.ProfileAPI; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.profile.ProfileMember; import org.bonitasoft.engine.profile.ProfileMemberNotFoundException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.web.rest.model.portal.profile.ProfileMemberDefinition; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; /** * @author Vincent Elcrin */ public class ProfileMemberEngineClient { private final ProfileAPI profileApi; protected ProfileMemberEngineClient(ProfileAPI profileApi) { this.profileApi = profileApi; } public SearchResult searchProfileMembers(String memberType, SearchOptions searchOptions) { try { return profileApi.searchProfileMembers(memberType, searchOptions); } catch (SearchException e) { throw new APIException(e); } } public ProfileMember createProfileMember(Long profileId, Long userId, Long groupId, Long roleId) { try { return profileApi.createProfileMember(profileId, userId, groupId, roleId); } catch (AlreadyExistsException e) { throw new APIForbiddenException(new T_("Profile member already exists"), e); } catch (CreationException e) { throw new APIException(e); } } public void deleteProfileMember(Long id) { try { profileApi.deleteProfileMember(id); } catch (DeletionException e) { if (e.getCause() instanceof ProfileMemberNotFoundException) { throw new APIItemNotFoundException(ProfileMemberDefinition.TOKEN); } else { throw new APIException(e); } } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/TenantManagementEngineClient.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import org.bonitasoft.engine.api.TenantAdministrationAPI; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; /** * @author Colin PUY */ public class TenantManagementEngineClient { private final TenantAdministrationAPI tenantAdministrationAPI; public TenantManagementEngineClient(final TenantAdministrationAPI tenantManagementAPI) { this.tenantAdministrationAPI = tenantManagementAPI; } public boolean isTenantPaused() { return tenantAdministrationAPI.isPaused(); } public void pauseTenant() { if (!isTenantPaused()) { pause(); } } private void pause() { try { tenantAdministrationAPI.pause(); } catch (final UpdateException e) { throw new APIException(new T_("Error when pausing BPM services"), e); } } public void resumeTenant() { if (isTenantPaused()) { resume(); } } private void resume() { try { tenantAdministrationAPI.resume(); } catch (final UpdateException e) { throw new APIException(new T_("Error when resuming BPM services"), e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/engineclient/UserEngineClient.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import java.util.List; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserCreator; import org.bonitasoft.engine.identity.UserCreator.UserField; import org.bonitasoft.engine.identity.UserNotFoundException; import org.bonitasoft.engine.identity.UserUpdater; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; public class UserEngineClient { private final IdentityAPI identityAPI; public UserEngineClient(IdentityAPI identityAPI) { this.identityAPI = identityAPI; } public User update(long userId, UserUpdater userUpdater) { try { return identityAPI.updateUser(userId, userUpdater); } catch (UserNotFoundException e) { throw new APINotFoundException(new T_("Can't update user. User not found"), e); } catch (UpdateException e) { throw new APIException(new T_("Error when updating user"), e); } } public User create(UserCreator creator) { try { return identityAPI.createUser(creator); } catch (AlreadyExistsException e) { throw new APIForbiddenException(new T_("Can't create user. User '%userName%' already exists", new Arg("userName", creator.getFields().get(UserField.NAME))), e); } catch (CreationException e) { throw new APIException(new T_("Error when creating user"), e); } } public User get(long userId) { try { return identityAPI.getUser(userId); } catch (UserNotFoundException e) { throw new APINotFoundException(new T_("User not found"), e); } } public void delete(List userIds) { try { identityAPI.deleteUsers(userIds); } catch (DeletionException e) { throw new APIException(new T_("Error when deleting users"), e); } } public SearchResult search(SearchOptions searchOptions) { try { return identityAPI.searchUsers(searchOptions); } catch (SearchException e) { throw new APIException(new T_("Error when searching users"), e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/API.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpSession; import org.bonitasoft.web.rest.server.framework.api.Datastore; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasAdd; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasDelete; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasGet; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasSearch; import org.bonitasoft.web.rest.server.framework.api.DatastoreHasUpdate; import org.bonitasoft.web.rest.server.framework.exception.ForbiddenAttributesException; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIMethodNotAllowedException; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Séverin Moussel */ public abstract class API { protected ItemDefinition itemDefinition = null; private final Map deployers = new HashMap<>(); private static Logger LOGGER = LoggerFactory.getLogger(API.class.getName()); // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CONSTRUCTORS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public API() { this.itemDefinition = defineItemDefinition(); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS AND GETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * The ServletCall responsible of this service. */ private APIServletCall caller = null; /** * Set the caller. * * @param caller * The ServletCall responsible of this service. */ public final void setCaller(final APIServletCall caller) { this.caller = caller; } /** * @return the itemDefinition */ public final ItemDefinition getItemDefinition() { return this.itemDefinition; } /** * Define the ItemDefinition for current class */ protected ItemDefinition defineItemDefinition() { // FIXME [API V2] Make this method abstract after suppression of API V1 return null; } /** * @see org.bonitasoft.web.toolkit.server.ServletCall#getHttpSession() */ protected final HttpSession getHttpSession() { return this.caller.getHttpSession(); } protected String getLocale() { return this.caller.getLocale(); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ENTRY POINTS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @SuppressWarnings("unchecked") public ITEM runAdd(final IItem item) { // FIXME Activate at end of APIs refactoring // if (!(this instanceof APIHasAdd)) { // throw new APIMethodNotAllowedException("POST method not allowed."); // } // Stop there if forbidden attributes are set checkForbiddenAttributes(item.getAttributes()); // Run specific implementation return add((ITEM) item); } @SuppressWarnings("unchecked") public ITEM add(final ITEM item) { final Datastore datastore = getDefaultDatastore(); if (!(datastore instanceof DatastoreHasAdd)) { throw new APIMethodNotAllowedException("POST method not allowed."); } return ((DatastoreHasAdd) datastore).add(item); } public ITEM runUpdate(final APIID id, final Map attributes) { // FIXME Activate at end of APIs refactoring // if (!(this instanceof APIHasUpdate)) { // throw new APIMethodNotAllowedException("PUT method not allowed."); // } id.setItemDefinition(getItemDefinition()); // Stop there if forbidden attributes are set checkForbiddenAttributes(attributes); // Run specific implementation return this.update(id, attributes); } @SuppressWarnings("unchecked") public ITEM update(final APIID id, final Map attributes) { final Datastore datastore = getDefaultDatastore(); if (datastore == null || !(datastore instanceof DatastoreHasUpdate)) { throw new APIMethodNotAllowedException("PUT method not allowed."); } return ((DatastoreHasUpdate) datastore).update(id, attributes); } public ITEM runGet(final APIID id, final List deploys, final List counters) { // FIXME Activate at end of APIs refactoring // if (!(this instanceof APIHasGet)) { // throw new APIMethodNotAllowedException("GET method not allowed."); // } id.setItemDefinition(getItemDefinition()); final ITEM item = get(id); if (item == null) { throw new APIItemNotFoundException(getItemDefinition().getToken(), id); } fillDeploys(item, deploys != null ? deploys : new ArrayList<>()); fillCounters(item, counters != null ? counters : new ArrayList<>()); return item; } @SuppressWarnings("unchecked") public ITEM get(final APIID id) { final Datastore datastore = getDefaultDatastore(); if (datastore == null || !(datastore instanceof DatastoreHasGet)) { throw new APIMethodNotAllowedException("GET method not allowed."); } return ((DatastoreHasGet) datastore).get(id); } public ItemSearchResult runSearch(final int page, final int resultsByPage, final String search, final String orders, final Map filters, final List deploys, final List counters) { // FIXME Activate at end of APIs refactoring // if (!(this instanceof APIHasSearch)) { // throw new APIMethodNotAllowedException("SEARCH method not allowed."); // } String realOrders = orders; if (orders == null || orders.length() == 0) { realOrders = defineDefaultSearchOrder(); // TODO remove this test and exception while the automated unit test over all APis if (realOrders == null) { throw new APIException( "No default search order defined. Please, override the defineDefaultSearchOrder method in " + this.getClass().toString() + "."); } } final ItemSearchResult searchResult = search(page, resultsByPage, search, realOrders, filters != null ? filters : new HashMap<>()); for (final ITEM item : searchResult.getResults()) { fillDeploys(item, deploys != null ? deploys : new ArrayList<>()); fillCountersDependingOnFilters(item, counters != null ? counters : List.of(), filters != null ? filters : Map.of()); } return searchResult; } @SuppressWarnings("unchecked") public ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters) { final Datastore datastore = getDefaultDatastore(); if (datastore == null || !(datastore instanceof DatastoreHasSearch)) { throw new APIMethodNotAllowedException("SEARCH method not allowed."); } return ((DatastoreHasSearch) datastore).search(page, resultsByPage, search, orders, filters); } /** * Define the default search order. */ public String defineDefaultSearchOrder() { return null; } public void runDelete(final List ids) { // FIXME Activate at end of APIs refactoring // if (!(this instanceof APIHasDelete)) { // throw new APIMethodNotAllowedException("DELETE method not allowed."); // } for (final APIID id : ids) { id.setItemDefinition(getItemDefinition()); } delete(ids); } public void delete(final List ids) { final Datastore datastore = getDefaultDatastore(); if (datastore == null || !(datastore instanceof DatastoreHasDelete)) { throw new APIMethodNotAllowedException("DELETE method not allowed."); } ((DatastoreHasDelete) datastore).delete(ids); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // AUTOMATED CRUDS BASED ON INTERFACES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected Datastore defineDefaultDatastore() { return null; } public final Datastore getDefaultDatastore() { return this.defineDefaultDatastore(); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEPLOYS AND COUNTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void addDeployer(final Deployer deployer) { deployers.put(deployer.getDeployedAttribute(), deployer); } public Map getDeployers() { return Collections.unmodifiableMap(deployers); } protected void fillDeploys(final ITEM item, final List deploys) { for (final String attribute : deploys) { deployAttribute(attribute, item); } } private void deployAttribute(final String attribute, final ITEM item) { if (deployers.containsKey(attribute)) { try { deployers.get(attribute).deployIn(item); } catch (final Exception e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(getFailedDeployMessage(attribute, item), e); } else if (LOGGER.isInfoEnabled()) { LOGGER.info(getFailedDeployMessage(attribute, item)); } } } } protected String getFailedDeployMessage(final String attribute, final ITEM item) { return "Could not deploy attribute '" + attribute + "' on item " + item.toString(); } protected void fillCounters(final ITEM item, final List counters) { // Do Nothing if not overridden } /** * When the values of the filters of a search are required to determine the counters queries, * this method can be overridden instead of fillCounters */ protected void fillCountersDependingOnFilters(final ITEM item, final List counters, final Map filters) { fillCounters(item, counters); // Do Nothing more if not overridden } /** * @param attributeName * @param deploys * @param item */ protected final boolean isDeployable(final String attributeName, final List deploys, final IItem item) { final String attributeValue = item.getAttributeValue(attributeName); if (deploys.contains(attributeName) && attributeValue != null && !attributeValue.isEmpty()) { try { final long longAttrValue = Long.parseLong(attributeValue); //only positive numeric Ids are supported return longAttrValue > 0L; } catch (final NumberFormatException e) { //non numeric Id are supported return true; } } return false; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UPLOADS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FORBIDDEN ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Override this method to define attributes that are not allowed to be set manually during ADD or UPDATE. * * @return This method must returns a List of forbidden attributes' name. */ protected List defineReadOnlyAttributes() { return null; } private void checkForbiddenAttributes(final Map attributes) { // List forbidden attributes final List forbiddenAttributes = new ArrayList<>(); final List definedForbiddenAttributes = defineReadOnlyAttributes(); if (definedForbiddenAttributes != null) { forbiddenAttributes.addAll(definedForbiddenAttributes); } // No forbidden attributes defined, no need to go further in the check process. if (forbiddenAttributes.size() == 0) { return; } // List forbidden attributes found in the request final List errorAttributes = new ArrayList<>(); for (final String forbiddenAttribute : forbiddenAttributes) { if (!MapUtil.isBlank(attributes, forbiddenAttribute)) { errorAttributes.add(forbiddenAttribute); } } // If at least one is found, throw a ForbiddenAttributesException if (errorAttributes.size() > 0) { throw new ForbiddenAttributesException(errorAttributes); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/APIServletCall.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.console.common.server.i18n.I18n; import org.bonitasoft.web.rest.server.framework.exception.APIMissingIdException; import org.bonitasoft.web.rest.server.framework.json.JSonSimpleDeserializer; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.rest.server.framework.utils.ParsedRestRequestURI; import org.bonitasoft.web.rest.server.framework.utils.RestRequestURIParser; import org.bonitasoft.web.toolkit.client.common.AbstractTreeNode; import org.bonitasoft.web.toolkit.client.common.Tree; import org.bonitasoft.web.toolkit.client.common.TreeLeaf; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIIncorrectIdException; import org.bonitasoft.web.toolkit.client.common.json.JSonItemReader; import org.bonitasoft.web.toolkit.client.common.json.JSonItemWriter; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorEngine; import org.bonitasoft.web.toolkit.server.ServletCall; import org.springframework.http.HttpHeaders; /** * @author Séverin Moussel * @author Baptiste Mesta * @author Fabio Lombardi */ public class APIServletCall extends ServletCall { public static final String PARAMETER_COUNTER = "n"; public static final String PARAMETER_DEPLOY = "d"; public static final String PARAMETER_FILTER = "f"; public static final String PARAMETER_SEARCH = "s"; public static final String PARAMETER_ORDER = "o"; public static final String PARAMETER_LIMIT = "c"; public static final String PARAMETER_PAGE = "p"; public static final String PARAMETER_QUERY = "q"; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // REQUEST PARSING // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected API api; private String apiName; private String resourceName; private APIID id; public APIServletCall(final HttpServletRequest request, final HttpServletResponse response) { super(request, response); } /** * Constructor for tests */ public APIServletCall() { super(); } public String getApiName() { return apiName; } public String getResourceName() { return resourceName; } public APIID getId() { return id; } /** * Read the inputStream and parse it as an IItem compatible with the called API. */ private IItem getJSonStreamAsItem() { final IItem item = JSonItemReader.parseItem(getInputStream(), api.getItemDefinition()); ValidatorEngine.validate(item, false); return item; } /** * Read elements form the request *
      *
    • API tokens
    • *
    • item id (if defined)
    • *
    • parameters
    • *
    * * @param request * @param response */ @Override protected final void parseRequest(final HttpServletRequest request, final HttpServletResponse response) { parsePath(request); // Fixes BS-400. This is ugly. I18n.getInstance(); api = APIs.get(apiName, resourceName); api.setCaller(this); super.parseRequest(request, response); } void parsePath(final HttpServletRequest request) { final ParsedRestRequestURI parsedURI = new RestRequestURIParser(request).parse(); APIID resourceQualifiers = parsedURI.getResourceQualifiers(); if (resourceQualifiers != null && resourceQualifiers.getIds().size() > 0 && isAnyNumberIdNegativeOrZero(resourceQualifiers.getIds())) { throw new APIIncorrectIdException("Id must be non-zero positive for " + parsedURI.getApiName() + " on resource " + parsedURI.getResourceName()); } id = resourceQualifiers; apiName = parsedURI.getApiName(); resourceName = parsedURI.getResourceName(); } private boolean isAnyNumberIdNegativeOrZero(List ids) { for (String id : ids) { try { if (Long.parseLong(id) <= 0L) { return true; } } catch (NumberFormatException e) { // Ignore non-number ids, since they are acceptable } } return false; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // EXECUTE METHODS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Entry point for GET and SEARCH */ @Override public final void doGet() { try { // GET one if (id != null) { output(api.runGet(id, getParameterAsList(PARAMETER_DEPLOY), getParameterAsList(PARAMETER_COUNTER))); } else if (countParameters() == 0) { throw new APIMissingIdException(getRequestURL()); } // Search else { final ItemSearchResult result = api.runSearch(Integer.parseInt(getParameter(PARAMETER_PAGE, "0")), Integer.parseInt(getParameter(PARAMETER_LIMIT, "10")), getParameter(PARAMETER_SEARCH), getParameter(PARAMETER_ORDER), parseFilters(getParameterAsList(PARAMETER_FILTER)), getParameterAsList(PARAMETER_DEPLOY), getParameterAsList(PARAMETER_COUNTER)); head(HttpHeaders.CONTENT_RANGE, result.getPage() + "-" + result.getLength() + "/" + result.getTotal()); output(result.getResults()); } } catch (final APIException e) { e.setApi(apiName); e.setResource(resourceName); throw e; } } @Override protected void output(final Object object) { super.output(object); } @Override protected void head(final String name, final String value) { super.head(name, value); } /** * Entry point for CREATE */ @Override public final void doPost() { try { final IItem jSonStreamAsItem = getJSonStreamAsItem(); final IItem outputItem = api.runAdd(jSonStreamAsItem); output(JSonItemWriter.itemToJSON(outputItem)); } catch (final APIException e) { e.setApi(apiName); e.setResource(resourceName); throw e; } } /** * Entry point for UPDATE */ @Override public final void doPut() { try { if (id == null) { throw new APIMissingIdException(getRequestURL()); } final String inputStream = getInputStream(); if (inputStream.length() == 0) { api.runUpdate(id, new HashMap<>()); return; } Item.setApplyValidatorMandatoryByDefault(false); final IItem item = getJSonStreamAsItem(); api.runUpdate(id, getAttributesWithDeploysAsJsonString(item)); } catch (final APIException e) { e.setApi(apiName); e.setResource(resourceName); throw e; } } /** * Get deploys and add them in json representation in map * Workaround to be able to have included json objects in main object in PUT request * You have to unserialize them to be able to use them in java representation */ private HashMap getAttributesWithDeploysAsJsonString(final IItem item) { final HashMap map = new HashMap<>(); map.putAll(item.getAttributes()); for (final Entry deploy : item.getDeploys().entrySet()) { map.put(deploy.getKey(), deploy.getValue().toJson()); } return map; } /** * Entry point for DELETE */ @Override public final void doDelete() { try { final List ids = new ArrayList<>(); // Using ids in json input stream if (id == null) { final String inputStream = getInputStream(); if (inputStream.length() == 0) { throw new APIMissingIdException(getRequestURL(), "Id of the element to delete is missing"); } // Parsing ids in Json input stream final AbstractTreeNode tree = JSonSimpleDeserializer.unserializeTree(inputStream); if (tree instanceof Tree) { final List> nodes = ((Tree) tree).getNodes(); for (final AbstractTreeNode node : nodes) { if (node instanceof Tree) { ids.add(APIID.makeAPIID(((Tree) node).getValues())); } else if (node instanceof TreeLeaf) { ids.add(APIID.makeAPIID(((TreeLeaf) node).getValue())); } else { throw new APIMissingIdException(getRequestURL(), "Id of the elements to delete are missing or malformed"); } } } else { throw new APIMissingIdException(getRequestURL(), "Id of the elements to delete are missing or malformed"); } } // Using id in URL else { ids.add(id); } api.runDelete(ids); } catch (final APIException e) { e.setApi(apiName); e.setResource(resourceName); throw e; } } /** * @param parameters * @return */ private Map parseFilters(final List parameters) { if (parameters == null) { return null; } final Map results = new HashMap<>(); for (final String parameter : parameters) { final String[] split = parameter.split("="); if (split.length < 2) { results.put(split[0], null); } else { results.put(split[0], split[1]); } } return results; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/APIs.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Séverin Moussel */ public class APIs { public static API get(final String apiToken, final String resourceToken) { return RestAPIFactory.getDefaultFactory().defineApis(apiToken, resourceToken); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/Deployer.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Vincent Elcrin */ public interface Deployer { String getDeployedAttribute(); void deployIn(IItem item); default boolean isDeployable(final String attributeName, final IItem item) { final String attributeValue = item.getAttributeValue(attributeName); if (attributeValue != null && !attributeValue.isEmpty()) { try { final long longAttrValue = Long.valueOf(attributeValue); //only positive numeric Ids are supported return longAttrValue > 0L; } catch (final NumberFormatException e) { //non numeric Id are supported return true; } } return false; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/RestAPIFactory.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Séverin Moussel */ public abstract class RestAPIFactory { private static RestAPIFactory defaultFactory = null; public static void setDefaultFactory(final RestAPIFactory factory) { defaultFactory = factory; } public static RestAPIFactory getDefaultFactory() { return defaultFactory; } public abstract API defineApis(final String apiToken, final String resourceToken); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/APIHasAdd.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.api; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Séverin Moussel */ public interface APIHasAdd { T add(final T item); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/APIHasDelete.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.api; import java.util.List; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public interface APIHasDelete { void delete(final List ids); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/APIHasGet.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.api; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Séverin Moussel */ public interface APIHasGet { T get(final APIID id); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/APIHasSearch.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.api; import java.util.Map; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Séverin Moussel */ public interface APIHasSearch { ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters); String defineDefaultSearchOrder(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/APIHasUpdate.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.api; import java.util.Map; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Séverin Moussel */ public interface APIHasUpdate { T update(final APIID id, final Map attributes); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/Datastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.api; /** * @author Julien Mege */ public class Datastore { } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/DatastoreHasAdd.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.api; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Séverin Moussel */ public interface DatastoreHasAdd { T add(final T item); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/DatastoreHasDelete.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.api; import java.util.List; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public interface DatastoreHasDelete { void delete(final List ids); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/DatastoreHasGet.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.api; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Séverin Moussel */ public interface DatastoreHasGet { T get(final APIID id); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/DatastoreHasSearch.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.api; import java.util.Map; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Séverin Moussel */ public interface DatastoreHasSearch { ItemSearchResult search(final int page, final int resultsByPage, final String search, final String orders, final Map filters); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/DatastoreHasUpdate.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.api; import java.util.Map; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Séverin Moussel */ public interface DatastoreHasUpdate { T update(final APIID id, final Map attributes); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/api/EnumConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.api; /** * @author Vincent Elcrin * Converter doing bridge between engine's enum and web's item */ public interface EnumConverter> { /** * Convert attribute value stored in items into engine's enum. */ E convert(final String attributeValue); /** * Convert engine's enum into an item's attribute. */ String convert(final E enumValue); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIAttributeException.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.exception; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer; /** * @author Séverin Moussel */ public class APIAttributeException extends APIException { private static final long serialVersionUID = -4611825491187153300L; private final String attributeName; public APIAttributeException(final String attributeName) { super((Exception) null); this.attributeName = attributeName; } public APIAttributeException(final String attributeName, final String message) { super(message); this.attributeName = attributeName; } /** * @return the attributeName */ public String getAttributeName() { return this.attributeName; } @Override protected JsonExceptionSerializer buildJson() { return super.buildJson() .appendAttribute("attributeName", getAttributeName()); } @Override protected String defaultMessage() { return "Malformed attribute : " + this.attributeName; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIAttributeMissingException.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.exception; /** * @author Séverin Moussel */ public class APIAttributeMissingException extends APIAttributeException { private static final long serialVersionUID = 1254220877755354148L; public APIAttributeMissingException(final String attributeName) { super(attributeName); } @Override protected String defaultMessage() { return "Attribute " + getAttributeName() + " is missing for API " + getApi() + "#" + getResource(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIAttributesException.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.exception; import java.util.Arrays; import java.util.List; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer; import org.bonitasoft.web.toolkit.client.ui.utils.ListUtils; /** * @author Séverin Moussel */ public class APIAttributesException extends APIException { private static final long serialVersionUID = 7808353918962735013L; private final List attributesNames; public APIAttributesException(final List attributesNames) { super((Exception) null); this.attributesNames = attributesNames; } public APIAttributesException(final String... attributesNames) { super((Exception) null); this.attributesNames = Arrays.asList(attributesNames); } public APIAttributesException(final List attributesNames, final String message, final Throwable cause) { super(message, cause); this.attributesNames = attributesNames; } public APIAttributesException(final List attributesNames, final String message) { super(message); this.attributesNames = attributesNames; } public APIAttributesException(final List attributesNames, final Throwable cause) { super(cause); this.attributesNames = attributesNames; } /** * @return the attributesNames */ public List getAttributesNames() { return this.attributesNames; } @Override protected JsonExceptionSerializer buildJson() { return super.buildJson() .appendAttribute("attributesNames", getAttributesNames()); } @Override protected String defaultMessage() { return "Malformed attributes : " + ListUtils.join(this.attributesNames, ", "); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIFileUploadNotFoundException.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.exception; import org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer; /** * @author Séverin Moussel */ public class APIFileUploadNotFoundException extends APIAttributeException { private static final long serialVersionUID = -5738651518696086103L; private final String filePath; public APIFileUploadNotFoundException(final String attributeName, final String filePath) { super(attributeName); this.filePath = filePath; } /** * @return the filePath */ public String getFilePath() { return this.filePath; } @Override protected String defaultMessage() { return "Uploaded file " + getAttributeName() + "(" + getFilePath() + ") not found for API \"" + getApi() + "#" + getResource() + "\""; } @Override protected JsonExceptionSerializer buildJson() { return super.buildJson() .appendAttribute("filepath", getFilePath()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIFilterEmptyException.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.exception; /** * @author Séverin Moussel */ public class APIFilterEmptyException extends APIFilterException { private static final long serialVersionUID = -3372478894238466120L; public APIFilterEmptyException(final String filterName) { super(filterName); } @Override protected String defaultMessage() { return "Filter " + getFilterName() + " mustn't be empty"; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIFilterException.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.exception; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer; /** * @author Séverin Moussel */ public class APIFilterException extends APIException { private static final long serialVersionUID = -3674362051034713685L; private final String filterName; public APIFilterException(final String filterName) { super(); this.filterName = filterName; } public APIFilterException(final String filterName, final String message, final Throwable cause) { super(message, cause); this.filterName = filterName; } public APIFilterException(final String filterName, final String message) { super(message); this.filterName = filterName; } public APIFilterException(final String filterName, final Throwable cause) { super(cause); this.filterName = filterName; } /** * @return the filterName */ public String getFilterName() { return this.filterName; } @Override protected JsonExceptionSerializer buildJson() { return super.buildJson() .appendAttribute("filterName", getFilterName()); } @Override protected String defaultMessage() { return "Error on filter : " + getFilterName(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIFilterMandatoryException.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.exception; /** * @author Séverin Moussel */ public class APIFilterMandatoryException extends APIFilterException { private static final long serialVersionUID = 7067237932975183746L; public APIFilterMandatoryException(final String filterName, final String message, final Throwable cause) { super(filterName, message, cause); } public APIFilterMandatoryException(final String filterName, final String message) { super(filterName, message); } public APIFilterMandatoryException(final String filterName, final Throwable cause) { super(filterName, cause); } public APIFilterMandatoryException(final String filterName) { super(filterName); } @Override protected String defaultMessage() { return "Filter " + getFilterName() + " is mandatory"; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/APIMissingIdException.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.exception; import org.bonitasoft.web.toolkit.client.common.exception.api.APIMalformedUrlException; /** * @author Séverin Moussel */ public class APIMissingIdException extends APIMalformedUrlException { private static final long serialVersionUID = 7387559368848683642L; public APIMissingIdException(final String url) { super(url); } public APIMissingIdException(final String url, final String message) { super(url, message); } @Override protected String defaultMessage() { return "Id of the item to retrieve is missing in url"; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/exception/ForbiddenAttributesException.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.exception; import java.util.List; import org.bonitasoft.web.toolkit.client.ui.utils.ListUtils; /** * @author Séverin Moussel */ public class ForbiddenAttributesException extends APIAttributesException { private static final long serialVersionUID = 8320315567291339726L; public ForbiddenAttributesException(final List attributeName) { super(attributeName); } @Override protected String defaultMessage() { return "Manual set of following attributes is forbidden : " + ListUtils.join(getAttributesNames(), ", "); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/json/JSonSimpleDeserializer.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.json; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.web.toolkit.client.common.AbstractTreeNode; import org.bonitasoft.web.toolkit.client.common.Tree; import org.bonitasoft.web.toolkit.client.common.TreeIndexed; import org.bonitasoft.web.toolkit.client.common.TreeLeaf; import org.bonitasoft.web.toolkit.client.common.json.JSonUnserializer; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; /** * @author Séverin Moussel */ public class JSonSimpleDeserializer implements JSonUnserializer { private static JSonSimpleDeserializer INSTANCE = null; private static JSonSimpleDeserializer getInstance() { if (INSTANCE == null) { INSTANCE = new JSonSimpleDeserializer(); } return INSTANCE; } public static AbstractTreeNode unserializeTree(final String json) { return getInstance()._unserializeTree(json); } @Override public AbstractTreeNode _unserializeTree(final String json) { try { if (json.length() == 0) { return null; } return unserializeTreeNode(new JSONParser().parse(json)); } catch (final ParseException e) { throw new IllegalArgumentException("Can't parse JSon", e); } } private AbstractTreeNode unserializeTreeNode(final Object object) { if (object instanceof JSONObject) { return unserializeTreeNode((JSONObject) object); } else if (object instanceof JSONArray) { return unserializeTreeNode((JSONArray) object); } else if (object instanceof Boolean) { return new TreeLeaf<>(((Boolean) object).booleanValue() ? "1" : "0"); } return new TreeLeaf<>(object.toString()); } private TreeIndexed unserializeTreeNode(final JSONObject object) { final TreeIndexed result = new TreeIndexed<>(); @SuppressWarnings("rawtypes") final Iterator iter = object.entrySet().iterator(); while (iter.hasNext()) { @SuppressWarnings("rawtypes") final Map.Entry entry = (Entry) iter.next(); result.addNode(entry.getKey().toString(), unserializeTreeNode(entry.getValue())); } return result; } private Tree unserializeTreeNode(final JSONArray array) { final Tree result = new Tree<>(); final int size = array.size(); for (int i = 0; i < size; i++) { result.addNode(unserializeTreeNode(array.get(i))); } return result; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/json/JacksonDeserializer.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.json; import java.io.IOException; import java.util.List; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * Jackson ObjectMapper Wrapper to fit our needs * * @author Colin PUY */ public class JacksonDeserializer { private final ObjectMapper mapper = new ObjectMapper(); public T deserialize(String json, Class clazz) { return deserialize(json, mapper.getTypeFactory().constructType(clazz)); } public List deserializeList(String json, Class clazz) { return deserialize(json, mapper.getTypeFactory().constructCollectionType(List.class, clazz)); } private T deserialize(String json, JavaType javaType) { try { return mapper.readValue(json.getBytes(), javaType); } catch (JsonParseException e) { throw new APIException(AbstractI18n.t_("Can't parse json, non-well formed content"), e); } catch (JsonMappingException e) { throw new APIException(AbstractI18n.t_("Json can't be mapped to " + javaType.getRawClass().getName()), e); } catch (IOException e) { // should never appear throw new APIException(e); } } @SuppressWarnings("unchecked") public T convertValue(Object fromValue, Class toValue) { return (T) mapper.convertValue(fromValue, toValue); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/json/JacksonSerializer.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.json; import java.io.IOException; import com.fasterxml.jackson.databind.ObjectMapper; /** * @author Fabio Lombardi */ public class JacksonSerializer { private final ObjectMapper mapper = new ObjectMapper(); public String serialize(Object obj) throws IOException { try { return mapper.writeValueAsString(obj); } catch (Throwable e) { e.printStackTrace(); throw new RuntimeException(e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/search/ISearchDirection.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.search; public interface ISearchDirection { String SORT_ORDER_ASCENDING = " ASC"; String SORT_ORDER_DESCENDING = " DESC"; } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/search/ItemSearchResult.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.search; import java.util.List; import org.bonitasoft.web.toolkit.client.common.exception.api.APISearchIndexOutOfRange; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Séverin Moussel */ public class ItemSearchResult { private final int page; private final int length; private final long total; private final List results; public ItemSearchResult(final int page, final int length, final long total, final List results) { this.page = page; this.length = length; this.total = total; this.results = results; if (page < 0 || page > total) { throw new APISearchIndexOutOfRange(page); } } /** * @return the page */ public int getPage() { return this.page; } /** * @return the length */ public int getLength() { return this.length; } /** * @return the total */ public long getTotal() { return this.total; } /** * @return the results */ public List getResults() { return this.results; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/servlet/APIServlet.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.servlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.web.rest.server.framework.APIServletCall; import org.bonitasoft.web.rest.server.framework.RestAPIFactory; import org.bonitasoft.web.toolkit.client.ItemDefinitionFactory; import org.bonitasoft.web.toolkit.server.ServletCall; import org.bonitasoft.web.toolkit.server.servlet.ToolkitHttpServlet; /** * @author Séverin Moussel */ public abstract class APIServlet extends ToolkitHttpServlet { private static final long serialVersionUID = 1852124460966605504L; @Override protected void initializeToolkit() { super.initializeToolkit(); ItemDefinitionFactory.setDefaultFactory(defineApplicatioFactoryCommon()); RestAPIFactory.setDefaultFactory(defineApplicatioFactoryServer()); } @Override protected ServletCall defineServletCall(final HttpServletRequest req, final HttpServletResponse resp) { return new APIServletCall(req, resp); } protected abstract ItemDefinitionFactory defineApplicatioFactoryCommon(); protected abstract RestAPIFactory defineApplicatioFactoryServer(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/FilePathBuilder.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils; import java.io.File; /** * @author Séverin Moussel */ public class FilePathBuilder { private final StringBuilder path = new StringBuilder(); public FilePathBuilder(final String path) { super(); insert(path); } public FilePathBuilder append(final String path) { // If null or empty, do nothing if (path == null || path.isEmpty()) { return this; } this.path.append(File.separator); insert(path); return this; } /** * @param path */ private void insert(final String path) { if (path.endsWith(File.separator)) { this.path.append(path, 0, path.length() - 1); } else { this.path.append(path); } } @Override public String toString() { return this.path.toString(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/ParsedRestRequestURI.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.framework.utils; import org.bonitasoft.web.toolkit.client.data.APIID; /** * Immutable result of parsing a REST API request URI. */ public class ParsedRestRequestURI { private final String apiName; private final String resourceName; private final APIID resourceQualifiers; public ParsedRestRequestURI(String apiName, String resourceName, APIID resourceQualifiers) { this.apiName = apiName; this.resourceName = resourceName; this.resourceQualifiers = resourceQualifiers; } public String getApiName() { return apiName; } public String getResourceName() { return resourceName; } public APIID getResourceQualifiers() { return resourceQualifiers; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/RestRequestURIParser.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils; import java.util.Arrays; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.bonitasoft.web.rest.server.api.AbstractRESTController; import org.bonitasoft.web.toolkit.client.common.exception.api.APIMalformedUrlException; import org.bonitasoft.web.toolkit.client.data.APIID; /** * Simple parser that extract parameters from a call to the REST api * * @author Baptiste Mesta */ public class RestRequestURIParser { //This constant should match the servlet name of DispatcherServlet as declared in servlet context public static final String SPRING_REST_SERVLET_NAME = "SpringRest"; //This constant should match the servlet name of CustomPageServlet as declared in servlet context public static final String CUSTOM_PAGE_SERVLET_NAME = "CustomPageServlet"; private static final String API_SEGMENT = "API"; private static final String API_TOOLKIT_SEGMENT = "APIToolkit"; private static final String API_SPRING_INTERNAL_SEGMENT = AbstractRESTController.API_SPRING_INTERNAL; private final HttpServletRequest request; public RestRequestURIParser(final HttpServletRequest request) { this.request = request; } public ParsedRestRequestURI parse() { if (isSpringMvcServlet() || isToolkitServlet() || isCustomPageServlet()) { return parseSpringMvcOrToolkitRequest(); } if (request.getPathInfo() != null) { // Non-Spring, non-toolkit wildcard servlet: parse from servletPath + pathInfo, // with special handling to preserve the first path segment as apiName. return parseWildcardServletRequest(); } return parseDirectPathRequest(); } private boolean isToolkitServlet() { String servletPath = request.getServletPath(); return ("/" + API_TOOLKIT_SEGMENT).equals(servletPath); } private boolean isSpringMvcServlet() { return SPRING_REST_SERVLET_NAME.equals(request.getHttpServletMapping().getServletName()); } /** * Checks whether the request is served by CustomPageServlet, mapped to /portal/custom-page/*. * This assumes the servlet is mapped to that single wildcard prefix; if it were ever mapped * to a deeper sub-path (e.g. /portal/custom-page/API/avatars/*), the request would not * carry this servlet name and would instead be handled by parseWildcardServletRequest(). */ private boolean isCustomPageServlet() { return CUSTOM_PAGE_SERVLET_NAME.equals(request.getHttpServletMapping().getServletName()); } /** * For Spring MVC DispatcherServlet (servletName == "SpringRest"), * CustomPageServlet (servletName == "CustomPageServlet"), * and BonitaRestAPIServlet (/APIToolkit/*). *

    * Handles both exact-match mappings (e.g. /API/system/maintenance where * servletPath = full path, pathInfo = null) and wildcard mappings * (e.g. /APISpringInternal/* where servletPath = /APISpringInternal, * pathInfo = /bpm/case/42; or /API/bpm/activityVariable/* where * servletPath = /API/bpm/activityVariable, pathInfo = /3/myVar). * Concatenating servletPath + pathInfo always produces the full request path. *

    * All three servlet types delegate to parseUsingApiSegmentLookup which * locates the API/APIToolkit/APISpringInternal segment dynamically. */ private ParsedRestRequestURI parseSpringMvcOrToolkitRequest() { String pathInfo = request.getPathInfo(); return parseUsingApiSegmentLookup( request.getServletPath() + (pathInfo != null ? pathInfo : "")); } /** * Other wildcard servlets not recognised by name (pathInfo != null, not Spring MVC, * not toolkit, not custom-page). *

    * If the path contains an API segment (e.g. /portal/custom-page/API/avatars/*), * parsing starts from that segment. Otherwise (e.g. /services/*), the servletPath * is just a mapping prefix and is skipped — parsing uses pathInfo alone. */ private ParsedRestRequestURI parseWildcardServletRequest() { String pathInfo = request.getPathInfo(); String fullPath = request.getServletPath() + pathInfo; String[] path = fullPath.split("/"); int apiIndex = findApiSegmentIndex(Arrays.asList(path)); if (apiIndex >= 0) { // When the servlet path includes a prefix before the API segment // (e.g. /portal/custom-page/API/avatars/*), we must skip the prefix // and parse from the API segment to match permission entries like "GET|API/avatars". return parseRequest(path, apiIndex); } // No API segment: servletPath is just the mapping prefix (e.g. /services). // Parse the resource structure from pathInfo alone. String[] pathInfoSegments = pathInfo.split("/"); return parseRequest(pathInfoSegments, 1); } /** * Exact-match servlets or default servlet (pathInfo is null, not Spring MVC). * Exact-match servlets: URLs like /API/documentDownload or /portal/imageUpload * that literally match a web.xml url-pattern. The container sets servletPath to * the full path and pathInfo to null. * Default servlet: URLs like /API/bpm/case/42 that don't match any specific * servlet mapping. The container routes these to the default servlet, which also * sets servletPath = full path and pathInfo = null. This case arises because * RestAPIAuthorizationFilter runs before UrlRewriteFilter in the filter chain * (see web.xml filter-mapping order): at auth time, the request still carries * its original URL. UrlRewriteFilter will later rewrite and forward to * /APIToolkit/* or /APISpringInternal/*, but this parser has already extracted * the API name and resource from the original URL. */ private ParsedRestRequestURI parseDirectPathRequest() { return parseUsingApiSegmentLookup(request.getServletPath()); } /** * Shared logic for Spring MVC, toolkit, and direct-path servlets: build the full path, * locate the API segment dynamically, then dispatch to parseRequest. */ private ParsedRestRequestURI parseUsingApiSegmentLookup(String fullPath) { String[] path = fullPath.split("/"); //find the API segment index to support multiple URL patterns (index 0 is always "" as fullPath starts with "/") int apiIndex = findApiSegmentIndex(Arrays.asList(path)); if (apiIndex >= 0) { int segmentsAfterApi = path.length - apiIndex - 1; if (segmentsAfterApi >= 2) { // Deep URL: /API/bpm/case/42 → apiName=bpm, resource=case return parseRequest(path, apiIndex + 1); } if (segmentsAfterApi == 1) { // Flat URL: /API/documentDownload → apiName=API, resource=documentDownload return parseRequest(path, apiIndex); } throw new APIMalformedUrlException(request.getRequestURL().toString(), "Missing API or resource name in request URL"); } // No API segment: /portal/imageUpload, /services/something, etc. if (path.length < 3) { //all the URL we support always at least have 2 path segments and index 0 is "" throw new APIMalformedUrlException(request.getRequestURL().toString(), "Missing API path segment in request URL"); } return parseRequest(path, 1); } /** * Find the index of the API prefix segment in the URL path. * Handles /API/..., /APIToolkit/..., and /APISpringInternal/... URL patterns. */ private int findApiSegmentIndex(List segments) { int index = segments.indexOf(API_SEGMENT); if (index < 0) { index = segments.indexOf(API_TOOLKIT_SEGMENT); } if (index < 0) { // /API/... URLs rewritten to /APISpringInternal/... by UrlRewriteFilter index = segments.indexOf(API_SPRING_INTERNAL_SEGMENT); } return index; } protected ParsedRestRequestURI parseRequest(String[] path, int indexOfAPINameSegment) { int minimalNumberOfPathSegments = indexOfAPINameSegment + 2; if (path.length < minimalNumberOfPathSegments) { throw new APIMalformedUrlException(request.getRequestURL().toString(), "Missing API or resource name in request URL"); } String apiName = path[indexOfAPINameSegment]; String resourceName = path[indexOfAPINameSegment + 1]; // Read id (if defined) APIID resourceQualifiers; if (path.length > minimalNumberOfPathSegments) { final List pathList = Arrays.asList(path); resourceQualifiers = APIID.makeAPIID(pathList.subList(minimalNumberOfPathSegments, pathList.size())); } else { resourceQualifiers = null; } return new ParsedRestRequestURI(apiName, resourceName, resourceQualifiers); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/SearchOptionsBuilderUtil.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; /** * @author Haojie Yuan */ public final class SearchOptionsBuilderUtil { private SearchOptionsBuilderUtil() { // Utility class } /** * build SearchOptionsBuilder * * @deprecated use {@link org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator} instead */ @Deprecated(since = "6.0") public static SearchOptionsBuilder buildSearchOptions(final int pageIndex, final int numberOfResults, final String sort, final String search) { final SearchOptionsBuilder builder = new SearchOptionsBuilder(computeIndex(pageIndex, numberOfResults), numberOfResults); if (sort != null) { final String[] order = sort.split(" "); if (order.length == 2) { builder.sort(order[0], Order.valueOf(order[1].toUpperCase())); } } if (search != null && !search.isEmpty()) { builder.searchTerm(search); } return builder; } public static int computeIndex(int page, int resultsByPage) { return page * resultsByPage; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/ConversionException.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter; /** * @author Colin PUY */ @SuppressWarnings("serial") public class ConversionException extends Exception { public ConversionException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/Converter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter; import java.io.Serializable; /** * @author Colin PUY */ public interface Converter { E convert(String convert) throws ConversionException; } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/ConverterFactory.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter; import java.util.Date; import org.bonitasoft.web.rest.server.framework.utils.converter.typed.BooleanConverter; import org.bonitasoft.web.rest.server.framework.utils.converter.typed.DateConverter; import org.bonitasoft.web.rest.server.framework.utils.converter.typed.DoubleConverter; import org.bonitasoft.web.rest.server.framework.utils.converter.typed.IntegerConverter; import org.bonitasoft.web.rest.server.framework.utils.converter.typed.LongConverter; import org.bonitasoft.web.rest.server.framework.utils.converter.typed.StringConverter; /** * @author Colin PUY */ public class ConverterFactory { public Converter createConverter(String className) { if (isClass(String.class, className)) { return new StringConverter(); } else if (isClass(Date.class, className)) { return new DateConverter(); } else if (isClass(Double.class, className)) { return new DoubleConverter(); } else if (isClass(Long.class, className)) { return new LongConverter(); } else if (isClass(Boolean.class, className)) { return new BooleanConverter(); } else if (isClass(Integer.class, className)) { return new IntegerConverter(); } throw new UnsupportedOperationException("Canno't create converter for class name : " + className); } private boolean isClass(Class classe, String className) { return classe.getName().equals(className); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/TypeConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter; import java.io.Serializable; /** * @author Colin PUY */ public class TypeConverter { private final ConverterFactory factory; // TODO delete this constructor when we'll be using DIP public TypeConverter() { this(new ConverterFactory()); } public TypeConverter(ConverterFactory factory) { this.factory = factory; } public Serializable convert(String className, String convert) throws ConversionException { return factory.createConverter(className).convert(convert); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/BooleanConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter.typed; import org.bonitasoft.web.rest.server.framework.utils.converter.Converter; /** * @author Colin PUY */ public class BooleanConverter implements Converter { @Override public Boolean convert(String toBeConverted) { if (toBeConverted == null || toBeConverted.isEmpty()) { return null; } return Boolean.valueOf(toBeConverted); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/DateConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter.typed; import java.text.DateFormatSymbols; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException; import org.bonitasoft.web.rest.server.framework.utils.converter.Converter; /** * @author Nicolas Tith */ public class DateConverter implements Converter { @Override public Date convert(String toBeConverted) throws ConversionException { if (toBeConverted == null || toBeConverted.isEmpty()) { return null; } try { SimpleDateFormat formatter = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", DateFormatSymbols.getInstance(Locale.ENGLISH)); return formatter.parse(toBeConverted); } catch (ParseException e) { throw new ConversionException(toBeConverted + " cannot be converted to Date", e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/DoubleConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter.typed; import org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException; import org.bonitasoft.web.rest.server.framework.utils.converter.Converter; /** * @author Colin PUY */ public class DoubleConverter implements Converter { @Override public Double convert(String toBeConverted) throws ConversionException { if (toBeConverted == null || toBeConverted.isEmpty()) { return null; } try { return Double.parseDouble(toBeConverted); } catch (NumberFormatException e) { throw new ConversionException(toBeConverted + " cannot be converted to Double", e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/IntegerConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter.typed; import org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException; import org.bonitasoft.web.rest.server.framework.utils.converter.Converter; /** * @author Colin PUY */ public class IntegerConverter implements Converter { @Override public Integer convert(String toBeConverted) throws ConversionException { if (toBeConverted == null || toBeConverted.isEmpty()) { return null; } try { return Integer.parseInt(toBeConverted); } catch (NumberFormatException e) { throw new ConversionException(toBeConverted + " cannot be converted to Integer", e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/LongConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter.typed; import org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException; import org.bonitasoft.web.rest.server.framework.utils.converter.Converter; /** * @author Colin PUY */ public class LongConverter implements Converter { @Override public Long convert(String toBeConverted) throws ConversionException { if (toBeConverted == null || toBeConverted.isEmpty()) { return null; } try { return Long.parseLong(toBeConverted); } catch (NumberFormatException e) { throw new ConversionException(toBeConverted + " cannot be converted to Long", e); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/StringConverter.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter.typed; import org.bonitasoft.web.rest.server.framework.utils.converter.Converter; /** * @author Colin PUY */ public class StringConverter implements Converter { @Override public String convert(String convert) { return convert; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/utils/BonitaJacksonModuleProvider.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.utils; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import org.bonitasoft.engine.bdm.serialization.CustomLocalDateDeserializer; import org.bonitasoft.engine.bdm.serialization.CustomLocalDateSerializer; import org.bonitasoft.engine.bdm.serialization.CustomLocalDateTimeDeserializer; import org.bonitasoft.engine.bdm.serialization.CustomLocalDateTimeSerializer; import org.bonitasoft.engine.bdm.serialization.CustomOffsetDateTimeDeserializer; import org.bonitasoft.engine.bdm.serialization.CustomOffsetDateTimeSerializer; import org.bonitasoft.engine.identity.User; /** * Provides Jackson module configuration for Bonita REST APIs. *

    * This ensures backward compatibility with the legacy JSON format, including: *

      *
    • _string suffix fields for numeric IDs (for JavaScript precision)
    • *
    • Custom date/time serialization
    • *
    *

    * Used by both Spring MVC configuration and test infrastructure. */ public final class BonitaJacksonModuleProvider { private BonitaJacksonModuleProvider() { // Utility class } /** * Creates a Jackson module with Bonita custom serializers. * Includes custom serializers for _string suffix fields and date/time handling. * * @return the configured Jackson module */ public static Module createBonitaModule() { SimpleModule bonitaModule = new SimpleModule("BonitaModule"); // Add custom serializers for _string suffix fields bonitaModule.addSerializer(new DataInstanceSerializer()); bonitaModule.addSerializer(new TimerEventTriggerInstanceSerializer()); // Date/time serializers and deserializers bonitaModule.addDeserializer(LocalDate.class, new CustomLocalDateDeserializer()); bonitaModule.addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer()); bonitaModule.addDeserializer(OffsetDateTime.class, new CustomOffsetDateTimeDeserializer()); bonitaModule.addSerializer(LocalDate.class, new CustomLocalDateSerializer()); bonitaModule.addSerializer(LocalDateTime.class, new CustomLocalDateTimeSerializer()); bonitaModule.addSerializer(OffsetDateTime.class, new CustomOffsetDateTimeSerializer()); // Until User extends BaseRestElement upstream, patch its id to serialize as a JSON string. bonitaModule.setMixInAnnotation(User.class, UserMixIn.class); return bonitaModule; } /** * Configures the given ObjectMapper with Bonita custom serializers. * * @param mapper the ObjectMapper to configure */ public static void configureObjectMapper(ObjectMapper mapper) { mapper.registerModule(createBonitaModule()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/utils/DataInstanceSerializer.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.utils; import java.io.IOException; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import org.bonitasoft.engine.bpm.data.impl.DataInstanceImpl; /** * @author Laurent Leseigneur */ public class DataInstanceSerializer extends JsonSerializer { final JacksonSerializerHelper jacksonSerializerHelper = new JacksonSerializerHelper(); @Override public void serialize(final DataInstanceImpl value, final JsonGenerator jgen, final SerializerProvider provider) throws IOException { jgen.writeStartObject(); jgen.writeObjectField("name", value.getName()); jgen.writeObjectField("description", value.getDescription()); jgen.writeObjectField("transientData", value.isTransientData()); jgen.writeObjectField("className", value.getClassName()); jgen.writeObjectField("containerType", value.getContainerType()); jacksonSerializerHelper.writeNumberField(jgen, "id", value.getId()); jacksonSerializerHelper.writeNumberField(jgen, "containerId", value.getContainerId()); jacksonSerializerHelper.writeNumberField(jgen, "value", value.getValue()); jgen.writeEndObject(); } @Override public Class handledType() { return DataInstanceImpl.class; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/utils/JacksonSerializerHelper.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.utils; import java.io.IOException; import java.io.Serializable; import com.fasterxml.jackson.core.JsonGenerator; /** * @author Laurent Leseigneur * @author Anthony Birembaut */ public class JacksonSerializerHelper { public JacksonSerializerHelper() { } public void writeNumberField(final JsonGenerator jgen, final String fieldName, final Serializable value) throws IOException { jgen.writeObjectField(fieldName, value); jgen.writeObjectField(fieldName + "_string", String.valueOf(value)); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/utils/TimerEventTriggerInstanceSerializer.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.utils; import java.io.IOException; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import org.bonitasoft.engine.bpm.flownode.TimerEventTriggerInstance; /** * @author Laurent Leseigneur */ public class TimerEventTriggerInstanceSerializer extends JsonSerializer { final JacksonSerializerHelper jacksonSerializerHelper = new JacksonSerializerHelper(); @Override public void serialize(final TimerEventTriggerInstance timerEventTriggerInstance, final JsonGenerator jgen, final SerializerProvider provider) throws IOException { jgen.writeStartObject(); jgen.writeObjectField("eventInstanceName", timerEventTriggerInstance.getEventInstanceName()); jgen.writeObjectField("executionDate", timerEventTriggerInstance.getExecutionDate()); jacksonSerializerHelper.writeNumberField(jgen, "id", timerEventTriggerInstance.getId()); jacksonSerializerHelper.writeNumberField(jgen, "eventInstanceId", timerEventTriggerInstance.getEventInstanceId()); jgen.writeEndObject(); } @Override public Class handledType() { return TimerEventTriggerInstance.class; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/utils/UserMixIn.java ================================================ /** * Copyright (C) 2026 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.web.rest.server.utils; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import org.bonitasoft.engine.identity.User; /** * Jackson mix-in patching {@link User} to serialize every long-typed id field as a JSON * string, to prevent JavaScript precision loss on values exceeding * {@code Number.MAX_SAFE_INTEGER}. *

    * Covers all long ids exposed by {@code User}: *

      *
    • {@code id} — the user's own primary key (the contract {@code BaseRestElement} will * enforce upstream)
    • *
    • {@code createdBy} — id of the user who created this account
    • *
    • {@code managerUserId} — id of this user's manager
    • *
    • {@code iconId} — id of the icon row used as avatar
    • *
    * Date fields are left to Jackson defaults (handled by the date (de)serializers also * registered on {@link BonitaJacksonModuleProvider}). *

    * This is a workaround until {@code User} extends {@code BaseRestElement} upstream in the * {@code bonita-organization-model} artifact, at which point the {@code id} entry can be * dropped from this mix-in (or the whole class, if upstream also annotates the other ids). *

    * Registered globally via {@link BonitaJacksonModuleProvider} so the contract applies to * every {@code User} reaching the REST layer (e.g. embedded in delegation DTOs). */ abstract class UserMixIn { @JsonSerialize(using = ToStringSerializer.class) public abstract long getId(); @JsonSerialize(using = ToStringSerializer.class) public abstract long getCreatedBy(); @JsonSerialize(using = ToStringSerializer.class) public abstract long getManagerUserId(); @JsonSerialize(using = ToStringSerializer.class) public abstract Long getIconId(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/server/login/LoginFailureTracker.java ================================================ /** * Copyright (C) 2026 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.web.server.login; /** * Interface for tracking login failures to implement brute-force attack protection. * Implementations can store failure counts in memory, a database, or any other storage mechanism. */ public interface LoginFailureTracker { boolean isLockedOut(String username); void recordFailure(String username); void resetFailures(String username); long getLockoutDurationSeconds(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/server/login/LoginFailureTrackerAccessor.java ================================================ /** * Copyright (C) 2026 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.web.server.login; import javax.servlet.ServletContext; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.web.context.support.WebApplicationContextUtils; @Slf4j public class LoginFailureTrackerAccessor { private LoginFailureTrackerAccessor() { } public static LoginFailureTracker getLoginFailureTracker(ServletContext servletContext) { try { ApplicationContext context = WebApplicationContextUtils .getWebApplicationContext(servletContext); if (context == null) { log.warn( "Spring ApplicationContext not available — brute-force login protection is disabled"); return null; } return context.getBean(LoginFailureTracker.class); } catch (NoSuchBeanDefinitionException e) { log.warn( "LoginFailureTracker bean not found — brute-force login protection is disabled", e); return null; } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/server/login/LoginFailureTrackerConfiguration.java ================================================ /** * Copyright (C) 2026 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.web.server.login; import static org.bonitasoft.engine.Profiles.NOT_IN_CLUSTER; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @Configuration @Profile(NOT_IN_CLUSTER) public class LoginFailureTrackerConfiguration { // Inline @Value defaults mirror bonita-platform-community.properties — keep them in sync. @Value("${bonita.runtime.security.bruteforce.enabled:true}") private boolean bruteForceEnabled; @Value("${bonita.runtime.security.bruteforce.max.attempts:5}") private int bruteForceMaxAttempts; @Value("${bonita.runtime.security.bruteforce.lockout.duration.seconds:600}") private int bruteForceLockoutDurationSeconds; @Bean public LoginFailureTracker loginFailureTracker() { return new MemoryLoginFailureTracker( bruteForceEnabled, bruteForceMaxAttempts, bruteForceLockoutDurationSeconds); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/server/login/MemoryLoginFailureTracker.java ================================================ /** * Copyright (C) 2026 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.web.server.login; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; /** * Memory implementation of {@link LoginFailureTracker} * Note: this implementation is not distributed and will not work across multiple server instances. *

    * Uses a fixed-window counting strategy: the failure count resets when the window expires. * This means an attacker could make up to {@code maxAttempts - 1} attempts per window indefinitely * without triggering a lockout. A sliding-window approach would be stricter, but the fixed window * is simpler and sufficient for the intended threat model (automated brute-force attacks). */ public class MemoryLoginFailureTracker implements LoginFailureTracker { private static final int EVICTION_INTERVAL = 100; /** Safety cap to prevent unbounded memory growth under sustained attack from many distinct usernames. */ static final int MAX_ENTRIES = 10_000; private final boolean enabled; private final int maxAttempts; private final long lockoutDurationMillis; private final ConcurrentHashMap failures = new ConcurrentHashMap<>(); private final AtomicInteger operationCount = new AtomicInteger(); public MemoryLoginFailureTracker(boolean enabled, int maxAttempts, int lockoutDurationSeconds) { if (maxAttempts <= 0) { throw new IllegalArgumentException("maxAttempts must be positive, got: " + maxAttempts); } if (lockoutDurationSeconds <= 0) { throw new IllegalArgumentException( "lockoutDurationSeconds must be positive, got: " + lockoutDurationSeconds); } this.enabled = enabled; this.maxAttempts = maxAttempts; this.lockoutDurationMillis = lockoutDurationSeconds * 1000L; } @Override public boolean isLockedOut(String username) { if (!enabled) { return false; } FailureRecord record = failures.get(username); if (record == null) { return false; } if (isExpired(record)) { failures.remove(username); return false; } return record.count >= maxAttempts; } @Override public void recordFailure(String username) { if (!enabled) { return; } failures.compute(username, (key, existing) -> { long now = currentTimeMillis(); if (existing == null || isExpired(existing)) { return new FailureRecord(1, now); } return new FailureRecord(existing.count + 1, existing.windowStartTimestamp); }); evictExpiredIfNeeded(); } @Override public long getLockoutDurationSeconds() { return lockoutDurationMillis / 1000; } @Override public void resetFailures(String username) { if (!enabled) { return; } failures.remove(username); } private void evictExpiredIfNeeded() { if (operationCount.incrementAndGet() % EVICTION_INTERVAL == 0) { failures.entrySet().removeIf(entry -> isExpired(entry.getValue())); // If still over the safety cap after expiry eviction, evict oldest entries int excess = failures.size() - MAX_ENTRIES; if (excess > 0) { failures.entrySet().stream() .sorted((a, b) -> Long.compare(a.getValue().windowStartTimestamp, b.getValue().windowStartTimestamp)) .limit(excess) .forEach(entry -> failures.remove(entry.getKey())); } } } private boolean isExpired(FailureRecord record) { return currentTimeMillis() - record.windowStartTimestamp >= lockoutDurationMillis; } protected long currentTimeMillis() { return System.currentTimeMillis(); } static class FailureRecord { final int count; final long windowStartTimestamp; FailureRecord(int count, long windowStartTimestamp) { this.count = count; this.windowStartTimestamp = windowStartTimestamp; } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/ItemDefinitionFactory.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; public abstract class ItemDefinitionFactory { private static ItemDefinitionFactory defaultFactory = null; public static void setDefaultFactory(final ItemDefinitionFactory factory) { defaultFactory = factory; } public static ItemDefinitionFactory getDefaultFactory() { return defaultFactory; } public abstract ItemDefinition defineItemDefinitions(String token); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/AbstractTreeNode.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common; import org.bonitasoft.web.toolkit.client.common.json.JsonSerializable; /** * @author Séverin Moussel * @param */ public abstract class AbstractTreeNode implements JsonSerializable { protected AbstractTreeNode parent = null; public AbstractTreeNode() { } public AbstractTreeNode(final AbstractTreeNode parent) { this.parent = parent; } public AbstractTreeNode getParent() { return this.parent; } public void setParent(final AbstractTreeNode parent) { this.parent = parent; } public abstract AbstractTreeNode copy(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/CommonDateFormater.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common; import java.util.Date; /** * @author Paul AMAR */ public abstract class CommonDateFormater { private static CommonDateFormater formater = null; abstract public Date _parse(final String value, final String format); abstract public String _toString(final Date value, final String format); public static void setDateFormater(final CommonDateFormater formater) { CommonDateFormater.formater = formater; } public static Date parse(final String value, final String format) { return formater._parse(value, format); } public static String toString(final Date value, final String format) { return formater._toString(value, format); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/Tree.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.bonitasoft.web.toolkit.client.common.json.JSonSerializer; /** * @author Séverin Moussel * @param */ public class Tree extends AbstractTreeNode implements Iterable> { protected List> children = new ArrayList<>(); public Tree() { } public void addNode(final AbstractTreeNode node) { if (node == null) { return; } node.setParent(this); this.children.add(node); } public void addValue(final VALUE_CLASS value) { if (value != null && !this.contains(value)) { this.addNode(new TreeLeaf<>(this, value)); } } public List getValues() { final List values = new ArrayList<>(); for (final AbstractTreeNode node : this.children) { if (node instanceof TreeLeaf) { values.add(((TreeLeaf) node).getValue()); } } return values; } public AbstractTreeNode get(final int index) { return this.children.get(index); } @Override public String toString() { return this.children.toString(); } public int size() { return this.children.size(); } public boolean contains(final VALUE_CLASS value) { return this.getValues().contains(value); } @Override public Iterator> iterator() { return this.children.iterator(); } @Override public String toJson() { return JSonSerializer.serializeCollection(this.children); } @Override public Tree copy() { final Tree result = new Tree<>(); for (final AbstractTreeNode child : this.children) { result.addNode(child.copy()); } return result; } @Override public boolean equals(final Object o) { if (o == this) { return true; } if (!(o instanceof Tree tree)) { return false; } if (tree.size() != this.size()) { return false; } for (final VALUE_CLASS valueThis : this.getValues()) { for (final Object valueOther : tree.getValues()) { if (!valueThis.equals(valueOther)) { return false; } } } for (final Object valueOther : tree.getValues()) { for (final VALUE_CLASS valueThis : this.getValues()) { if (!valueThis.equals(valueOther)) { return false; } } } return true; } public List> getNodes() { return this.children; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/TreeIndexed.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import org.bonitasoft.web.toolkit.client.common.json.JSonSerializer; /** * @author Séverin Moussel * @param */ public class TreeIndexed extends AbstractTreeNode { protected Map> children = new HashMap<>(); public TreeIndexed() { } public TreeIndexed(final AbstractTreeNode parent, final Map map) { this(parent); this.addValues(map); } public TreeIndexed(final AbstractTreeNode parent) { super(parent); } public void addNode(final String key, final AbstractTreeNode node) { if (node == null) { return; } node.setParent(this); this.children.put(key, node); } public void addValue(final String key, final VALUE_CLASS value) { if (value != null) { this.addNode(key, new TreeLeaf<>(this, value)); } } public void addValues(final Map values) { if (values != null) { for (final String key : values.keySet()) { this.addValue(key, values.get(key)); } } } public LinkedHashMap getValues() { final LinkedHashMap values = new LinkedHashMap<>(); for (final String key : this.children.keySet()) { final AbstractTreeNode node = this.children.get(key); if (node instanceof TreeLeaf) { values.put(key, ((TreeLeaf) node).getValue()); } } return values; } public AbstractTreeNode get(final String key) { return this.children.get(key); } public Set keySet() { return this.children.keySet(); } public VALUE_CLASS getValue(final String key) { final AbstractTreeNode node = this.children.get(key); if (node instanceof TreeLeaf) { return ((TreeLeaf) node).getValue(); } return null; } @Override public String toString() { return this.children.toString(); } public int size() { return this.children.size(); } @Override public String toJson() { return JSonSerializer.serializeMap(this.children); } @Override public TreeIndexed copy() { final TreeIndexed result = new TreeIndexed<>(); for (final String key : this.children.keySet()) { result.addNode(key, this.children.get(key).copy()); } return result; } @Override public boolean equals(final Object o) { if (o == this) { return true; } if (!(o instanceof TreeIndexed tree)) { return false; } if (tree.size() != this.size()) { return false; } for (final String key : this.keySet()) { if (!this.get(key).equals(tree.get(key))) { return false; } } for (final String key : tree.keySet()) { if (!tree.get(key).equals(this.get(key))) { return false; } } return true; } public Map> getNodes() { return this.children; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/TreeLeaf.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common; import org.bonitasoft.web.toolkit.client.common.json.JSonSerializer; /** * @author Séverin Moussel * @param */ public class TreeLeaf extends AbstractTreeNode { protected VALUE_CLASS value; public TreeLeaf(final VALUE_CLASS value) { super(); this.value = value; } public TreeLeaf(final AbstractTreeNode parent, final VALUE_CLASS value) { super(parent); this.value = value; } public VALUE_CLASS getValue() { return this.value; } @Override public String toString() { return this.value == null ? null : this.value.toString(); } @Override public String toJson() { return JSonSerializer.serialize(this.value); } @Override public TreeLeaf copy() { return new TreeLeaf<>(this.value); } @Override public boolean equals(final Object o) { if (o == this) { return true; } if (!(o instanceof TreeLeaf)) { return false; } return this.getValue().equals(((TreeLeaf) o).getValue()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/TreeNode.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common; /** * @author Séverin Moussel * @param */ public class TreeNode extends Tree { public TreeNode() { super(); } @Override public TreeNode copy() { final TreeNode result = new TreeNode<>(); for (final AbstractTreeNode child : this.children) { result.addNode(child.copy()); } return result; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/KnownException.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.exception; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; /** * @author Séverin Moussel */ public abstract class KnownException extends RuntimeException { private static final long serialVersionUID = 4288951779031159740L; public KnownException() { super(); } public KnownException(final String message, final Throwable cause) { super(message, cause); } public KnownException(final String message) { super(message); } public KnownException(final Throwable cause) { super(cause); } /** * Override this method to define a default message.
    * Default message will be the getMessage() result if no other message is defined. * * @return This method returns the default message of the exception. */ protected String defaultMessage() { return ""; } @Override public String getMessage() { String message = super.getMessage(); if (StringUtil.isBlank(message)) { message = defaultMessage(); } return message; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIException.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.exception.api; import static org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE; import org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer; import org.bonitasoft.web.toolkit.client.common.exception.http.ServerException; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.bonitasoft.web.toolkit.client.common.json.JsonSerializable; /** * @author Séverin Moussel */ public class APIException extends ServerException implements JsonSerializable { private static final long serialVersionUID = 1820639344042666872L; private LOCALE locale; private String api = "..."; private String resource = "..."; private T_ localizedMessage; protected APIException() { super(); } public APIException(final String message, final Throwable cause) { super(message, cause); } public APIException(final String message) { super(message); } public APIException(final Throwable cause) { super(cause); } public APIException(final T_ localizedMessage, final Throwable cause) { super(cause); this.localizedMessage = localizedMessage; } public APIException(final T_ localizedMessage) { this.localizedMessage = localizedMessage; } /** * @return the api */ public String getApi() { return this.api; } /** * @param api * the api to set */ public APIException setApi(final String api) { this.api = api; return this; } /** * @return the resource */ public String getResource() { return this.resource; } /** * @param resource * the resource to set */ public APIException setResource(final String resource) { this.resource = resource; return this; } @Override protected JsonExceptionSerializer buildJson() { return super.buildJson() .appendAttribute("api", getApi()) .appendAttribute("resource", getResource()); } @Override protected String defaultMessage() { return "The API \"" + getApi() + "#" + getResource() + "\" has encountered an unknown error"; } public void setLocale(LOCALE locale) { this.locale = locale; } @Override public String getMessage() { if (locale != null && localizedMessage != null) { return localizedMessage.localize(locale); } return super.getMessage(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIForbiddenException.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.exception.api; import org.bonitasoft.web.toolkit.client.common.i18n.T_; /** * @author Vincent Elcrin */ public class APIForbiddenException extends APIException { private static final long serialVersionUID = 4021139564084425851L; public APIForbiddenException(final String message) { super(message); } public APIForbiddenException(final String message, final Throwable cause) { super(message, cause); } public APIForbiddenException(T_ localizedMessage) { super(localizedMessage); } public APIForbiddenException(T_ localizedMessage, Throwable cause) { super(localizedMessage, cause); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIIncorrectIdException.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.exception.api; /** * @author Dumitru Corini */ public class APIIncorrectIdException extends APIException { private static final long serialVersionUID = 1054890811602278747L; public APIIncorrectIdException(final String message) { super(message); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIItemException.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.exception.api; import org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer; /** * @author Séverin Moussel */ public class APIItemException extends APIException { private static final long serialVersionUID = -5137218346282966251L; protected final String itemType; public APIItemException(final String itemType) { this.itemType = itemType.toLowerCase(); } public APIItemException(final String itemType, final String message) { super(message); this.itemType = itemType.toLowerCase(); } public String getItemType() { return this.itemType; } @Override protected JsonExceptionSerializer buildJson() { return super.buildJson() .appendAttribute("itemtype", getItemType()); } @Override protected String defaultMessage() { return "An unknown error occurred on item " + getItemType() + " for API " + getApi() + "#" + getResource(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIItemIdMalformedException.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.exception.api; import org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer; /** * @author Dumitru Corini */ public class APIItemIdMalformedException extends APIItemException { private static final long serialVersionUID = 7963164445474080661L; public APIItemIdMalformedException(final String itemType, final String message) { super(itemType, message); } /** * @return the itemType */ public String getItemType() { return this.itemType; } @Override protected JsonExceptionSerializer buildJson() { return super.buildJson() .appendAttribute("itemtype", getItemType()); } @Override protected String defaultMessage() { return "The format of the id was incorrect for " + getItemType() + " for API " + getApi() + "#" + getResource(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIItemNotFoundException.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.exception.api; import org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Séverin Moussel */ public class APIItemNotFoundException extends APIItemException { private static final long serialVersionUID = -1325943431826471828L; private final APIID id; public APIItemNotFoundException(final String itemType) { this(itemType, null); } public APIItemNotFoundException(final String itemType, final APIID id) { super(itemType); this.id = id; } /** * @return the id */ public APIID getId() { return this.id; } @Override protected JsonExceptionSerializer buildJson() { JsonExceptionSerializer json = super.buildJson(); if (id != null) { json.appendAttribute("id", getId()); } return json; } @Override protected String defaultMessage() { StringBuilder message = new StringBuilder(); message.append(this.itemType.substring(0, 1).toUpperCase()); message.append(this.itemType.substring(1)); if (id != null) { message.append(" with id ("); message.append(getId().toString()); message.append(")"); } message.append(" not found for API "); message.append(getApi()); message.append("#"); message.append(getResource()); return message.toString(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIMalformedUrlException.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.exception.api; import lombok.Getter; /** * @author Séverin Moussel */ public class APIMalformedUrlException extends APIException { private static final long serialVersionUID = 3418532139473113744L; @Getter private final String url; public APIMalformedUrlException(final String url) { super(); this.url = url; } public APIMalformedUrlException(final String url, final String message) { super(message); this.url = url; } @Override protected String defaultMessage() { return "Malformed url"; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APIMethodNotAllowedException.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.exception.api; /** * @author Séverin Moussel */ public class APIMethodNotAllowedException extends APIException { private static final long serialVersionUID = 1709426532382444831L; private final String method; public APIMethodNotAllowedException(final String method) { super((Exception) null); this.method = method; } @Override protected String defaultMessage() { return "HTTP method " + this.method.toUpperCase() + " not allowed for API " + getApi() + "#" + getResource(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APINotFoundException.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.exception.api; import org.bonitasoft.web.toolkit.client.common.i18n.T_; /** * @author Séverin Moussel */ public class APINotFoundException extends APIException { private static final long serialVersionUID = 7709846894799079466L; public APINotFoundException(final Throwable cause) { super(cause); } public APINotFoundException(final String api, final String resource) { super((Exception) null); setApi(api); setResource(resource); } public APINotFoundException(final T_ localizedMessage, final Throwable cause) { super(localizedMessage, cause); } public APINotFoundException(final T_ localizedMessage) { super(localizedMessage); } @Override protected String defaultMessage() { return "API " + getApi() + " or resource " + getResource() + " doesn't exist or is not declared"; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APISearchIndexOutOfRange.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.exception.api; import org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer; /** * @author Séverin Moussel */ public class APISearchIndexOutOfRange extends APIException { private static final long serialVersionUID = 2618198001118478389L; private final int pageNumber; public APISearchIndexOutOfRange(final int pageNumber) { super(); this.pageNumber = pageNumber; } /** * @return the pageNumber */ public int getPageNumber() { return this.pageNumber; } @Override protected String defaultMessage() { return "Page index (" + getPageNumber() + ") out of range for API " + getApi() + "#" + getResource(); } @Override protected JsonExceptionSerializer buildJson() { return super.buildJson() .appendAttribute("page", getPageNumber()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/api/APITooManyRequestException.java ================================================ /** * 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.web.toolkit.client.common.exception.api; import lombok.Getter; import org.bonitasoft.web.toolkit.client.common.i18n.T_; public class APITooManyRequestException extends APIException { private static final long serialVersionUID = 1820639344042666872L; @Getter private long retryAfter = -1L; public APITooManyRequestException(final T_ message, long retryAfter) { super(message); this.retryAfter = retryAfter; setStatusCode(429); } @Override protected String defaultMessage() { return getApi() + "#" + getResource() + ": Case creation limit reached."; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/http/HttpException.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.exception.http; import org.bonitasoft.web.toolkit.client.common.exception.KnownException; import org.bonitasoft.web.toolkit.client.common.json.JsonSerializable; /** * @author Séverin Moussel */ public class HttpException extends KnownException implements JsonSerializable { private static final long serialVersionUID = 4351214615157802308L; protected int statusCode = 503; public HttpException() { super(); } public HttpException(final String message, final Throwable cause) { super(message, cause); } public HttpException(final String message) { super(message); } public HttpException(final Throwable cause) { super(cause); } /** * @return the statusCode */ public final int getStatusCode() { return this.statusCode; } /** * @param statusCode * the statusCode to set */ public final HttpException setStatusCode(final int statusCode) { this.statusCode = statusCode; return this; } @Override public final String toJson() { return buildJson().end(); } protected JsonExceptionSerializer buildJson() { return new JsonExceptionSerializer(this); } @Override protected String defaultMessage() { return "The connection has encountered an unknown error"; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/http/JsonExceptionSerializer.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.exception.http; import static org.bonitasoft.web.toolkit.client.common.json.JSonUtil.quote; import org.bonitasoft.web.toolkit.client.common.json.JSonSerializer; /** * Created by Vincent Elcrin * Date: 23/09/13 * Time: 17:56 */ public class JsonExceptionSerializer { private final StringBuilder json; public JsonExceptionSerializer(Throwable exception) { if (exception == null) { throw new IllegalArgumentException("exception cannot be null"); } json = serialize(exception); } private StringBuilder serialize(final Throwable e) { final StringBuilder json = new StringBuilder().append("{"); json.append(exceptionInnerJson(e)); if (e.getCause() != null && e.getCause() != e) { json.append(","); // only add the first cause (used by some code in portal's frontend) json.append(quote("cause")).append(":{").append(exceptionInnerJson(e.getCause())).append("}"); } return json; } private String exceptionInnerJson(final Throwable e) { return quote("exception") + ":" + quote(e.getClass().toString()) + "," + quote("message") + ":" + quote(e.getMessage()); } public String end() { return json.append("}").toString(); } public JsonExceptionSerializer appendAttribute(final String name, final Object value) { addNextAttribute(json, name, value); return this; } private void addNextAttribute(final StringBuilder json, final String name, final Object value) { if (value != null) { json.append(","); json.append(quote(name)) .append(":") .append(JSonSerializer.serialize(value)); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/exception/http/ServerException.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.exception.http; /** * @author Séverin Moussel */ public class ServerException extends HttpException { private static final long serialVersionUID = -2822846735990124977L; public ServerException() { super(); } public ServerException(final String message, final Throwable cause) { super(message, cause); } public ServerException(final String message) { super(message); } public ServerException(final Throwable cause) { super(cause); } @Override protected String defaultMessage() { return "The server has encountered an unknown error"; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/i18n/AbstractI18n.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.i18n; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.bonitasoft.web.toolkit.client.common.texttemplate.TextTemplate; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; /** * @author Séverin Moussel */ public abstract class AbstractI18n { public enum LOCALE { en, en_US, fr, fr_FR, es, es_ES, it, it_IT, de, de_DE, pt_BR, aa_DJ, aa_ER_SAAHO, aa_ER, aa_ET, aa, af_NA, af, af_ZA, ak_GH, ak, am_ET, am, ar_001, ar_AE, ar_BH, ar_DZ, ar_EG, ar_IQ, ar_JO, ar_KW, ar_LB, ar_LY, ar_MA, ar_OM, ar_QA, ar_SA, ar_SD, ar_SY, ar_TN, ar, ar_YE, as_IN, as, az_AZ, az_Cyrl_AZ, az_Cyrl, az_Latn_AZ, az, be_BY, be, bg_BG, bg, bn_BD, bn_IN, bn, bs_BA, bs, byn_ER, byn, ca_ES, ca, cch_NG, cch, cop, cs_CZ, cs, cy_GB, cy, da_DK, da, de_AT, de_BE, de_CH, de_LI, de_LU, dv_MV, dv, dz_BT, dz, ee_GH, ee_TG, ee, el_CY, el_GR, el_POLYTON, el, en_001, en_150, en_AE, en_AS, en_AU, en_BE, en_BW, en_BZ, en_CA, en_Dsrt_US, en_Dsrt, en_GB, en_GU, en_HK, en_IE, en_IN, en_JM, en_MH, en_MP, en_MT, en_NA, en_NZ, en_PH, en_PK, en_SG, en_Shaw, en_TT, en_UM, en_US_POSIX, en_VI, en_ZA, en_ZW, eo, eo_001, es_419, es_AR, es_BO, es_CL, es_CO, es_CR, es_DO, es_EC, es_GT, es_HN, es_MX, es_NI, es_PA, es_PE, es_PR, es_PY, es_SV, es_US, es_UY, es_VE, et_EE, et, eu_ES, eu, fa_AF, fa_IR, fa, fi_FI, fil_PH, fil, fi, fo_FO, fo, fr_BE, fr_CA, fr_CH, fr_LU, fr_MC, fr_SN, fur_IT, fur, gaa_GH, gaa, ga_IE, ga, gez_ER, gez_ET, gez, gl_ES, gl, gu_IN, gu, gv_GB, gv, ha_Arab_NG, ha_Arab_SD, ha_Arab, ha_GH, ha_Latn_GH, ha_Latn_NE, ha_Latn_NG, ha_Latn, ha_NE, ha_NG, ha_SD, haw_US, haw, ha, he_IL, he, hi_IN, hi, hr_HR, hr, hu_HU, hu, hy_AM_REVISED, hy_AM, hy, ia, ia_001, id_ID, id, ig_NG, ig, ii_CN, ii, in, is_IS, is, it_CH, iu, iw, ja_JP, ja, ka_GE, kaj_NG, kaj, kam_KE, kam, ka, kcg_NG, kcg, kfo_CI, kfo, kk_Cyrl_KZ, kk_Cyrl, kk_KZ, kk, kl_GL, kl, km_KH, km, kn_IN, kn, kok_IN, ko_KR, kok, ko, kpe_GN, kpe_LR, kpe, ku_Arab, ku_Latn_TR, ku_Latn, ku_TR, ku, kw_GB, kw, ky_KG, ky, ln_CD, ln_CG, ln, lo_LA, lo, lt_LT, lt, lv_LV, lv, mk_MK, mk, ml_IN, ml, mn_CN, mn_Cyrl_MN, mn_Cyrl, mn_MN, mn_Mong_CN, mn_Mong, mn, mo, mr_IN, mr, ms_BN, ms_MY, ms, mt_MT, mt, my_MM, my, nb_NO, nb, ne_IN, ne_NP, ne, nl_BE, nl_NL, nl, nn_NO, nn, no, no_NO_NY, nr, nr_ZA, nso, nso_ZA, ny_MW, ny, om_ET, om_KE, om, or_IN, or, pa_Arab_PK, pa_Arab, pa_Guru_IN, pa_Guru, pa_IN, pa_PK, pa, pl_PL, pl, ps_AF, ps, pt_PT, pt, ro_MD, ro_RO, ro, ru_RU, ru_UA, ru, rw_RW, rw, sa_IN, sa, se_FI, se_NO, se, sh_BA, sh_CS, sh, sh_YU, sid_ET, sid, si_LK, si, sk_SK, sk, sl_SI, sl, so_DJ, so_ET, so_KE, so_SO, so, sq_AL, sq, sr_BA, sr_CS, sr_Cyrl_BA, sr_Cyrl_CS, sr_Cyrl_ME, sr_Cyrl_RS, sr_Cyrl, sr_Cyrl_YU, sr_Latn_BA, sr_Latn_CS, sr_Latn_ME, sr_Latn_RS, sr_Latn, sr_Latn_YU, sr_ME, sr_RS, sr, sr_YU, ss_SZ, ss, ss_ZA, st_LS, st, st_ZA, sv_FI, sv_SE, sv, sw_KE, sw_TZ, sw, syr_SY, syr, ta_IN, ta, te_IN, te, tg_Cyrl_TJ, tg_Cyrl, tg_TJ, tg, th_TH, th, ti_ER, ti_ET, tig_ER, tig, ti, tl, tn, tn_ZA, to_TO, to, tr_TR, tr, ts, ts_ZA, tt_RU, tt, ug_Arab_CN, ug_Arab, ug_CN, ug, uk_UA, uk, ur_IN, ur_PK, ur, uz_AF, uz_Arab_AF, uz_Arab, uz_Cyrl_UZ, uz_Cyrl, uz_Latn_UZ, uz_Latn, uz_UZ, uz, ve, ve_ZA, vi_VN, vi, wal_ET, wal, wo_Latn_SN, wo_Latn, wo_SN, wo, xh, xh_ZA, yi, yi_001, yo_BJ, yo_NG, yo, zh_CN, zh_Hans_CN, zh_Hans_HK, zh_Hans_MO, zh_Hans_SG, zh_Hans, zh_Hant_HK, zh_Hant_MO, zh_Hant_TW, zh_Hant, zh_HK, zh_MO, zh_SG, zh_TW, zh, zu, zu_ZA } public LOCALE defaultLocale = LOCALE.en; private final Map> locales = new HashMap<>(); // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SINGLETON // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected static AbstractI18n I18N_instance = null; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // LOCALES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected static Map getLocales() { return I18N_instance._getLocales(); } protected final Map _getLocales() { final Map locales = new LinkedHashMap<>(); locales.put("en_US", "U.S. English"); locales.put("en", "English"); locales.put("fr_FR", "français (France)"); locales.put("fr", "Français"); locales.put("es_ES", "español de España"); locales.put("es", "Español"); locales.put("it_IT", "italiano (Italia)"); locales.put("it", "Italiano"); locales.put("de_DE", "Deutsch (Deutschland)"); locales.put("de", "Deutsch"); // locales.put("pt_BR", "português do Brasil"); locales.put("pt_BR", "Português (Brasil)"); locales.put("aa_DJ", "Qafar (Yabuuti)"); locales.put("aa_ER_SAAHO", "Qafar - Eretria (Saho)"); locales.put("aa_ER", "Qafar (Eretria)"); locales.put("aa_ET", "Qafar (Otobbia)"); locales.put("aa", "Qafar"); locales.put("af_NA", "Afrikaans (Namibië)"); locales.put("af", "Afrikaans"); locales.put("af_ZA", "Afrikaans (Suid-Afrika)"); locales.put("ak_GH", "Akan (Ghana)"); locales.put("ak", "Akan"); locales.put("am_ET", "አማርኛ (ኢትዮጵያ)"); locales.put("am", "አማርኛ"); locales.put("ar_001", "العربية العالم"); locales.put("ar_AE", "العربية (الامارات العربية المتحدة)"); locales.put("ar_BH", "العربية (البحرين)"); locales.put("ar_DZ", "العربية (الجزائر)"); locales.put("ar_EG", "العربية (مصر)"); locales.put("ar_IQ", "العربية (العراق)"); locales.put("ar_JO", "العربية (الأردن)"); locales.put("ar_KW", "العربية (الكويت)"); locales.put("ar_LB", "العربية (لبنان)"); locales.put("ar_LY", "العربية (ليبيا)"); locales.put("ar_MA", "العربية (المغرب)"); locales.put("ar_OM", "العربية (عمان)"); locales.put("ar_QA", "العربية (قطر)"); locales.put("ar_SA", "العربية (المملكة العربية السعودية)"); locales.put("ar_SD", "العربية (السودان)"); locales.put("ar_SY", "العربية (سوريا)"); locales.put("ar_TN", "العربية (تونس)"); locales.put("ar", "العربية"); locales.put("ar_YE", "العربية (اليمن)"); locales.put("as_IN", "অসমীয়া (ভাৰত)"); locales.put("as", "অসমীয়া"); locales.put("az_AZ", "azərbaycanca - latın (Azərbaycan)"); locales.put("az_Cyrl_AZ", "Азәрбајҹан - kiril (Азәрбајҹан)"); locales.put("az_Cyrl", "Азәрбајҹан (kiril)"); locales.put("az_Latn_AZ", "azərbaycanca - latın (Azərbaycan)"); locales.put("az", "azərbaycanca"); locales.put("be_BY", "беларуская (Беларусь)"); locales.put("be", "беларуская"); locales.put("bg_BG", "български (България)"); locales.put("bg", "български"); locales.put("bn_BD", "বাংলা (বাংলাদেশ)"); locales.put("bn_IN", "বাংলা (ভারত)"); locales.put("bn", "বাংলা"); locales.put("bs_BA", "bosanski (Bosna i Hercegovina)"); locales.put("bs", "bosanski"); locales.put("byn_ER", "ብሊን (ኤርትራ)"); locales.put("byn", "ብሊን"); locales.put("ca_ES", "català (Espanya)"); locales.put("ca", "català"); locales.put("cch_NG", "Atsam (Nigeria)"); locales.put("cch", "Atsam"); locales.put("cop", "Coptic"); locales.put("cs_CZ", "čeština (Česká republika)"); locales.put("cs", "čeština"); locales.put("cy_GB", "Cymraeg (Prydain Fawr)"); locales.put("cy", "Cymraeg"); locales.put("da_DK", "dansk (Danmark)"); locales.put("da", "dansk"); locales.put("de_AT", "Österreichisches Deutsch"); locales.put("de_BE", "Deutsch (Belgien)"); locales.put("de_CH", "Schweizer Hochdeutsch"); locales.put("de_LI", "Deutsch (Liechtenstein)"); locales.put("de_LU", "Deutsch (Luxemburg)"); locales.put("dv_MV", "ދިވެހިބަސް (ދިވެހި ރާއްޖެ)"); locales.put("dv", "ދިވެހިބަސް"); locales.put("dz_BT", "རྫོང་ཁ (འབྲུག)"); locales.put("dz", "རྫོང་ཁ"); locales.put("ee_GH", "Ewe (Ghana)"); locales.put("ee_TG", "Ewe (Togo)"); locales.put("ee", "Ewe"); locales.put("el_CY", "Ελληνικά (Κύπρος)"); locales.put("el_GR", "Ελληνικά (Ελλάδα)"); locales.put("el_POLYTON", "Ἑλληνικά (Πολυτονικό)"); locales.put("el", "Ελληνικά"); locales.put("en_001", "English World"); locales.put("en_150", "English Europe"); locales.put("en_AE", "English United Arab Emirates"); locales.put("en_AS", "English (American Samoa)"); locales.put("en_AU", "Australian English"); locales.put("en_BE", "English (Belgium)"); locales.put("en_BW", "English (Botswana)"); locales.put("en_BZ", "English (Belize)"); locales.put("en_CA", "Canadian English"); locales.put("en_Dsrt_US", "𐐀𐑍𐑊𐐮𐑇 - 𐐔𐐯𐑆𐐲𐑉𐐯𐐻 (𐐏𐐭𐑌𐐴𐐻𐐲𐐼 𐐝𐐻𐐩𐐻𐑅)"); locales.put("en_Dsrt", "𐐀𐑍𐑊𐐮𐑇 (𐐔𐐯𐑆𐐲𐑉𐐯𐐻)"); locales.put("en_GB", "British English"); locales.put("en_GU", "English (Guam)"); locales.put("en_HK", "English (Hong Kong SAR China)"); locales.put("en_IE", "English (Ireland)"); locales.put("en_IN", "English (India)"); locales.put("en_JM", "English (Jamaica)"); locales.put("en_MH", "English (Marshall Islands)"); locales.put("en_MP", "English (Northern Mariana Islands)"); locales.put("en_MT", "English (Malta)"); locales.put("en_NA", "English (Namibia)"); locales.put("en_NZ", "English (New Zealand)"); locales.put("en_PH", "English (Philippines)"); locales.put("en_PK", "English (Pakistan)"); locales.put("en_SG", "English (Singapore)"); locales.put("en_Shaw", "English (Shavian)"); locales.put("en_TT", "English (Trinidad and Tobago)"); locales.put("en_UM", "English (United States Minor Outlying Islands)"); locales.put("en_US_POSIX", "U.S. English (Computer)"); locales.put("en_VI", "English (U.S. Virgin Islands)"); locales.put("en_ZA", "English (South Africa)"); locales.put("en_ZW", "English (Zimbabwe)"); locales.put("eo", "esperanto"); locales.put("eo_001", "esperanto world"); locales.put("es_419", "español (Latinoamérica)"); locales.put("es_AR", "español (Argentina)"); locales.put("es_BO", "español (Bolivia)"); locales.put("es_CL", "español (Chile)"); locales.put("es_CO", "español (Colombia)"); locales.put("es_CR", "español (Costa Rica)"); locales.put("es_DO", "español (República Dominicana)"); locales.put("es_EC", "español (Ecuador)"); locales.put("es_GT", "español (Guatemala)"); locales.put("es_HN", "español (Honduras)"); locales.put("es_MX", "español (México)"); locales.put("es_NI", "español (Nicaragua)"); locales.put("es_PA", "español (Panamá)"); locales.put("es_PE", "español (Perú)"); locales.put("es_PR", "español (Puerto Rico)"); locales.put("es_PY", "español (Paraguay)"); locales.put("es_SV", "español (El Salvador)"); locales.put("es_US", "español (Estados Unidos)"); locales.put("es_UY", "español (Uruguay)"); locales.put("es_VE", "español (Venezuela)"); locales.put("et_EE", "eesti (Eesti)"); locales.put("et", "eesti"); locales.put("eu_ES", "euskara (Espainia)"); locales.put("eu", "euskara"); locales.put("fa_AF", "دری (افغانستان)"); locales.put("fa_IR", "فارسی (ایران)"); locales.put("fa", "فارسی"); locales.put("fi_FI", "suomi (Suomi)"); locales.put("fil_PH", "Filipino (Pilipinas)"); locales.put("fil", "Filipino"); locales.put("fi", "suomi"); locales.put("fo_FO", "føroyskt (Føroyar)"); locales.put("fo", "føroyskt"); locales.put("fr_BE", "français (Belgique)"); locales.put("fr_CA", "français canadien"); locales.put("fr_CH", "français suisse"); locales.put("fr_LU", "français (Luxembourg)"); locales.put("fr_MC", "français (Monaco)"); locales.put("fr_SN", "français (Sénégal)"); locales.put("fur_IT", "furlan (Italie)"); locales.put("fur", "furlan"); locales.put("gaa_GH", "Ga (Ghana)"); locales.put("gaa", "Ga"); locales.put("ga_IE", "Gaeilge (Éire)"); locales.put("ga", "Gaeilge"); locales.put("gez_ER", "ግዕዝኛ (ኤርትራ)"); locales.put("gez_ET", "ግዕዝኛ (ኢትዮጵያ)"); locales.put("gez", "ግዕዝኛ"); locales.put("gl_ES", "galego (España)"); locales.put("gl", "galego"); locales.put("gu_IN", "ગુજરાતી (ભારત)"); locales.put("gu", "ગુજરાતી"); locales.put("gv_GB", "Gaelg (Rywvaneth Unys)"); locales.put("gv", "Gaelg"); locales.put("ha_Arab_NG", "Haoussa - Arabic (Nijeriya)"); locales.put("ha_Arab_SD", "Haoussa - Arabic (Sudan)"); locales.put("ha_Arab", "Haoussa (Arabic)"); locales.put("ha_GH", "Haoussa - Latin (Ghana)"); locales.put("ha_Latn_GH", "Haoussa - Latin (Ghana)"); locales.put("ha_Latn_NE", "Haoussa - Latin (Niger)"); locales.put("ha_Latn_NG", "Haoussa - Latin (Nijeriya)"); locales.put("ha_Latn", "Haoussa (Latin)"); locales.put("ha_NE", "Haoussa - Latin (Niger)"); locales.put("ha_NG", "Haoussa - Latin (Nijeriya)"); locales.put("ha_SD", "Haoussa - Arabic (Sudan)"); locales.put("haw_US", "ʻōlelo Hawaiʻi (ʻAmelika Hui Pū ʻIa)"); locales.put("haw", "ʻōlelo Hawaiʻi"); locales.put("ha", "Haoussa"); locales.put("he_IL", "עברית (ישראל)"); locales.put("he", "עברית"); locales.put("hi_IN", "हिन्दी (भारत)"); locales.put("hi", "हिन्दी"); locales.put("hr_HR", "hrvatski (Hrvatska)"); locales.put("hr", "hrvatski"); locales.put("hu_HU", "magyar (Magyarország)"); locales.put("hu", "magyar"); locales.put("hy_AM_REVISED", "Հայերէն - Հայաստանի Հանրապետութիւն (Revised Orthography)"); locales.put("hy_AM", "Հայերէն (Հայաստանի Հանրապետութիւն)"); locales.put("hy", "Հայերէն"); locales.put("ia", "interlingua"); locales.put("ia_001", "interlingua world"); locales.put("id_ID", "Bahasa Indonesia (Indonesia)"); locales.put("id", "Bahasa Indonesia"); locales.put("ig_NG", "Igbo (Nigeria)"); locales.put("ig", "Igbo"); locales.put("ii_CN", "ꆈꌠꉙ (ꍏꇩ)"); locales.put("ii", "ꆈꌠꉙ"); locales.put("in", "Bahasa Indonesia"); locales.put("is_IS", "íslenska (Ísland)"); locales.put("is", "íslenska"); locales.put("it_CH", "italiano (Svizzera)"); locales.put("iu", "ᐃᓄᒃᑎᑐᑦ ᑎᑎᕋᐅᓯᖅ"); locales.put("iw", "עברית"); locales.put("ja_JP", "日本語 (日本)"); locales.put("ja", "日本語"); locales.put("ka_GE", "ქართული (საქართველო)"); locales.put("kaj_NG", "Jju (Nigeria)"); locales.put("kaj", "Jju"); locales.put("kam_KE", "Kamba (Kenya)"); locales.put("kam", "Kamba"); locales.put("ka", "ქართული"); locales.put("kcg_NG", "Tyap (Nigeria)"); locales.put("kcg", "Tyap"); locales.put("kfo_CI", "Koro (Ivory Coast)"); locales.put("kfo", "Koro"); locales.put("kk_Cyrl_KZ", "Қазақ - Cyrillic (Қазақстан)"); locales.put("kk_Cyrl", "Қазақ (Cyrillic)"); locales.put("kk_KZ", "Қазақ - Cyrillic (Қазақстан)"); locales.put("kk", "Қазақ"); locales.put("kl_GL", "kalaallisut (Kalaallit Nunaat)"); locales.put("kl", "kalaallisut"); locales.put("km_KH", "ភាសាខ្មែរ (កម្ពុជា)"); locales.put("km", "ភាសាខ្មែរ"); locales.put("kn_IN", "ಕನ್ನಡ (ಭಾರತ)"); locales.put("kn", "ಕನ್ನಡ"); locales.put("kok_IN", "कोंकणी (भारत)"); locales.put("ko_KR", "한국어 (대한민국)"); locales.put("kok", "कोंकणी"); locales.put("ko", "한국어"); locales.put("kpe_GN", "Kpelle (Guinea)"); locales.put("kpe_LR", "Kpelle (Liberia)"); locales.put("kpe", "Kpelle"); locales.put("ku_Arab", "كوردی (Arabic)"); locales.put("ku_Latn_TR", "kurdî - Latin (Tirkiye)"); locales.put("ku_Latn", "kurdî (Latin)"); locales.put("ku_TR", "كوردی - Latin (Turkey)"); locales.put("ku", "كوردی"); locales.put("kw_GB", "kernewek (Rywvaneth Unys)"); locales.put("kw", "kernewek"); locales.put("ky_KG", "Кыргыз (Кыргызстан)"); locales.put("ky", "Кыргыз"); locales.put("ln_CD", "lingála (Kongó-Kinsásá)"); locales.put("ln_CG", "lingála (Kongó-Brazzaville)"); locales.put("ln", "lingála"); locales.put("lo_LA", "ລາວ (ລາວ)"); locales.put("lo", "ລາວ"); locales.put("lt_LT", "lietuvių (Lietuva)"); locales.put("lt", "lietuvių"); locales.put("lv_LV", "latviešu (Latvija)"); locales.put("lv", "latviešu"); locales.put("mk_MK", "македонски (Македонија)"); locales.put("mk", "македонски"); locales.put("ml_IN", "മലയാളം (ഇന്ത്യ)"); locales.put("ml", "മലയാളം"); locales.put("mn_CN", "монгол - Mongolian (China)"); locales.put("mn_Cyrl_MN", "монгол - Cyrillic (Монгол улс)"); locales.put("mn_Cyrl", "монгол (Cyrillic)"); locales.put("mn_MN", "монгол - Cyrillic (Монгол улс)"); locales.put("mn_Mong_CN", "монгол - Mongolian (China)"); locales.put("mn_Mong", "монгол (Mongolian)"); locales.put("mn", "монгол"); locales.put("mo", "Moldavian"); locales.put("mr_IN", "मराठी (भारत)"); locales.put("mr", "मराठी"); locales.put("ms_BN", "Bahasa Melayu (Brunei)"); locales.put("ms_MY", "Bahasa Melayu (Malaysia)"); locales.put("ms", "Bahasa Melayu"); locales.put("mt_MT", "Malti (Malta)"); locales.put("mt", "Malti"); locales.put("my_MM", "ဗမာ (မြန်မာ)"); locales.put("my", "ဗမာ"); locales.put("nb_NO", "norsk bokmål (Norge)"); locales.put("nb", "norsk bokmål"); locales.put("ne_IN", "नेपाली (भारत)"); locales.put("ne_NP", "नेपाली (नेपाल)"); locales.put("ne", "नेपाली"); locales.put("nl_BE", "Vlaams"); locales.put("nl_NL", "Nederlands (Nederland)"); locales.put("nl", "Nederlands"); locales.put("nn_NO", "nynorsk (Noreg)"); locales.put("nn", "nynorsk"); locales.put("no", "norsk bokmål"); locales.put("no_NO_NY", "nynorsk (Noreg)"); locales.put("nr", "isiNdebele"); locales.put("nr_ZA", "isiNdebele (South Africa)"); locales.put("nso", "Sesotho sa Leboa"); locales.put("nso_ZA", "Sesotho sa Leboa (South Africa)"); locales.put("ny_MW", "Nyanja (Malawi)"); locales.put("ny", "Nyanja"); locales.put("om_ET", "Oromoo (Itoophiyaa)"); locales.put("om_KE", "Oromoo (Keeniyaa)"); locales.put("om", "Oromoo"); locales.put("or_IN", "ଓଡ଼ିଆ (ଭାରତ)"); locales.put("or", "ଓଡ଼ିଆ"); locales.put("pa_Arab_PK", "پنجاب - العربية (پکستان)"); locales.put("pa_Arab", "پنجاب (العربية)"); locales.put("pa_Guru_IN", "ਪੰਜਾਬੀ - ਗੁਰਮੁਖੀ (ਭਾਰਤ)"); locales.put("pa_Guru", "ਪੰਜਾਬੀ (ਗੁਰਮੁਖੀ)"); locales.put("pa_IN", "ਪੰਜਾਬੀ - ਗੁਰਮੁਖੀ (ਭਾਰਤ)"); locales.put("pa_PK", "ਪੰਜਾਬੀ - Arabic (Pakistan)"); locales.put("pa", "ਪੰਜਾਬੀ"); locales.put("pl_PL", "polski (Polska)"); locales.put("pl", "polski"); locales.put("ps_AF", "پښتو (افغانستان)"); locales.put("ps", "پښتو"); locales.put("pt_PT", "português europeu"); locales.put("pt", "português"); locales.put("ro_MD", "română (Moldova, Republica)"); locales.put("ro_RO", "română (România)"); locales.put("ro", "română"); locales.put("ru_RU", "русский (Россия)"); locales.put("ru_UA", "русский (Украина)"); locales.put("ru", "русский"); locales.put("rw_RW", "Kinyarwanda (Rwanda)"); locales.put("rw", "Kinyarwanda"); locales.put("sa_IN", "संस्कृत भाषा (भारतम्)"); locales.put("sa", "संस्कृत भाषा"); locales.put("se_FI", "se (FI)"); locales.put("se_NO", "davvisámegiella (Norga)"); locales.put("se", "davvisámegiella"); locales.put("sh_BA", "Srpski - Latinica (Bosna i Hercegovina)"); locales.put("sh_CS", "Srpski - Latinica (Srbija)"); locales.put("sh", "Srpski (Latinica)"); locales.put("sh_YU", "Srpski - Latinica (Srbija)"); locales.put("sid_ET", "Sidaamu Afo (Itiyoophiya)"); locales.put("sid", "Sidaamu Afo"); locales.put("si_LK", "සිංහල (ශ්‍රී ලංකාව)"); locales.put("si", "සිංහල"); locales.put("sk_SK", "slovenský (Slovenská republika)"); locales.put("sk", "slovenský"); locales.put("sl_SI", "slovenščina (Slovenija)"); locales.put("sl", "slovenščina"); locales.put("so_DJ", "Soomaali (Jabuuti)"); locales.put("so_ET", "Soomaali (Itoobiya)"); locales.put("so_KE", "Soomaali (Kiiniya)"); locales.put("so_SO", "Soomaali (Soomaaliya)"); locales.put("so", "Soomaali"); locales.put("sq_AL", "shqipe (Shqipëria)"); locales.put("sq", "shqipe"); locales.put("sr_BA", "српски - Ћирилица (Босна и Херцеговина)"); locales.put("sr_CS", "Српски - Ћирилица (Србија)"); locales.put("sr_Cyrl_BA", "српски - Ћирилица (Босна и Херцеговина)"); locales.put("sr_Cyrl_CS", "Српски - Ћирилица (Србија)"); locales.put("sr_Cyrl_ME", "Српски - Ћирилица (Црна Гора)"); locales.put("sr_Cyrl_RS", "Српски - Ћирилица (Србија)"); locales.put("sr_Cyrl", "Српски (Ћирилица)"); locales.put("sr_Cyrl_YU", "Српски - Ћирилица (Србија)"); locales.put("sr_Latn_BA", "Srpski - Latinica (Bosna i Hercegovina)"); locales.put("sr_Latn_CS", "Srpski - Latinica (Srbija)"); locales.put("sr_Latn_ME", "Srpski - Latinica (Crna Gora)"); locales.put("sr_Latn_RS", "Srpski - Latinica (Srbija)"); locales.put("sr_Latn", "Srpski (Latinica)"); locales.put("sr_Latn_YU", "Srpski - Latinica (Srbija)"); locales.put("sr_ME", "Српски - Ћирилица (Црна Гора)"); locales.put("sr_RS", "Српски - Ћирилица (Србија)"); locales.put("sr", "Српски"); locales.put("sr_YU", "Српски - Ћирилица (Србија)"); locales.put("ss_SZ", "Siswati (Swaziland)"); locales.put("ss", "Siswati"); locales.put("ss_ZA", "Siswati (South Africa)"); locales.put("st_LS", "Sesotho (Lesotho)"); locales.put("st", "Sesotho"); locales.put("st_ZA", "Sesotho (South Africa)"); locales.put("sv_FI", "svenska (Finland)"); locales.put("sv_SE", "svenska (Sverige)"); locales.put("sv", "svenska"); locales.put("sw_KE", "Kiswahili (Kenya)"); locales.put("sw_TZ", "Kiswahili (Tanzania)"); locales.put("sw", "Kiswahili"); locales.put("syr_SY", "ܣܘܪܝܝܐ (ܣܘܪܝܝܐ)"); locales.put("syr", "ܣܘܪܝܝܐ"); locales.put("ta_IN", "தமிழ் (இந்தியா)"); locales.put("ta", "தமிழ்"); locales.put("te_IN", "తెలుగు (భారత దేళం)"); locales.put("te", "తెలుగు"); locales.put("tg_Cyrl_TJ", "Tajik - Cyrillic (Tajikistan)"); locales.put("tg_Cyrl", "Tajik (Cyrillic)"); locales.put("tg_TJ", "Tajik (Tajikistan)"); locales.put("tg", "Tajik"); locales.put("th_TH", "ไทย (ไทย)"); locales.put("th", "ไทย"); locales.put("ti_ER", "ትግርኛ (Eritrea)"); locales.put("ti_ET", "ትግርኛ (Ethiopia)"); locales.put("tig_ER", "ትግረ (ኤርትራ)"); locales.put("tig", "ትግረ"); locales.put("ti", "ትግርኛ"); locales.put("tl", "Filipino"); locales.put("tn", "Setswana"); locales.put("tn_ZA", "Setswana (South Africa)"); locales.put("to_TO", "lea fakatonga (Tonga)"); locales.put("to", "lea fakatonga"); locales.put("tr_TR", "Türkçe (Türkiye)"); locales.put("tr", "Türkçe"); locales.put("ts", "Xitsonga"); locales.put("ts_ZA", "Xitsonga (South Africa)"); locales.put("tt_RU", "Татар (Россия)"); locales.put("tt", "Татар"); locales.put("ug_Arab_CN", "Uighur - Arabic (China)"); locales.put("ug_Arab", "Uighur (Arabic)"); locales.put("ug_CN", "Uighur (China)"); locales.put("ug", "Uighur"); locales.put("uk_UA", "українська (Україна)"); locales.put("uk", "українська"); locales.put("ur_IN", "اردو (بھارت)"); locales.put("ur_PK", "اردو (پاکستان)"); locales.put("ur", "اردو"); locales.put("uz_AF", "Ўзбек - Араб (Афғонистон)"); locales.put("uz_Arab_AF", "اۉزبېک - Араб (افغانستان)"); locales.put("uz_Arab", "اۉزبېک (Араб)"); locales.put("uz_Cyrl_UZ", "Ўзбек - Кирил (Ўзбекистон)"); locales.put("uz_Cyrl", "Ўзбек (Кирил)"); locales.put("uz_Latn_UZ", "o'zbekcha - Lotin (Oʿzbekiston)"); locales.put("uz_Latn", "o'zbekcha (Lotin)"); locales.put("uz_UZ", "Ўзбек - Кирил (Ўзбекистон)"); locales.put("uz", "Ўзбек"); locales.put("ve", "Tshivenḓa"); locales.put("ve_ZA", "Tshivenḓa (South Africa)"); locales.put("vi_VN", "Tiếng Việt (Việt Nam)"); locales.put("vi", "Tiếng Việt"); locales.put("wal_ET", "ወላይታቱ (ኢትዮጵያ)"); locales.put("wal", "ወላይታቱ"); locales.put("wo_Latn_SN", "Wolof - Latin (Senegal)"); locales.put("wo_Latn", "Wolof (Latin)"); locales.put("wo_SN", "Wolof (Senegal)"); locales.put("wo", "Wolof"); locales.put("xh", "isiXhosa"); locales.put("xh_ZA", "isiXhosa (South Africa)"); locales.put("yi", "ייִדיש"); locales.put("yi_001", "ייִדיש וועלט"); locales.put("yo_BJ", "Yorùbá (BJ)"); locales.put("yo_NG", "Yorùbá (NG)"); locales.put("yo", "Yorùbá"); locales.put("zh_CN", "中文(简体) (中国)"); locales.put("zh_Hans_CN", "中文(简体) (中国)"); locales.put("zh_Hans_HK", "中文(简体) (中国香港特别行政区)"); locales.put("zh_Hans_MO", "中文(简体) (中国澳门特别行政区)"); locales.put("zh_Hans_SG", "中文(简体) (新加坡)"); locales.put("zh_Hans", "中文(简体)"); locales.put("zh_Hant_HK", "繁體中文 (中華人民共和國香港特別行政區)"); locales.put("zh_Hant_MO", "繁體中文 (中華人民共和國澳門特別行政區)"); locales.put("zh_Hant_TW", "繁體中文 (臺灣)"); locales.put("zh_Hant", "繁體中文"); locales.put("zh_HK", "中文(繁体) (中国香港特别行政区)"); locales.put("zh_MO", "中文(繁体) (中国澳门特别行政区)"); locales.put("zh_SG", "中文(简体) (新加坡)"); locales.put("zh_TW", "中文(繁体) (台湾)"); locales.put("zh", "中文"); locales.put("zu", "isiZulu"); locales.put("zu_ZA", "isiZulu (South Africa)"); return locales; } public Map getLocale(final LOCALE locale) { if (locale != null) { if (!this.locales.containsKey(locale)) { loadLocale(locale); } return this.locales.get(locale); } else { return getLocale(getDefaultLocale()); } } protected final void setLocale(final LOCALE locale, final Map map) { this.locales.put(locale, map); } public abstract void loadLocale(LOCALE locale); public static LOCALE getDefaultLocale() { return I18N_instance._getDefaultLocale(); } public final LOCALE _getDefaultLocale() { return this.defaultLocale; } public static LOCALE stringToLocale(final String localeString) { for (final LOCALE locale : LOCALE.values()) { if (locale.toString().equals(localeString)) { return locale; } } return null; } protected String getText(final String string) { return this.getText(this.defaultLocale, string); } protected String getText(final LOCALE locale, final String string) { final Map localeMap = getLocale(locale); if (localeMap == null) { return string; } final String translation = localeMap.get(string); if (StringUtil.isBlank(translation)) { return string; } return translation; } protected String getText(final String string, final Arg... args) { return new TextTemplate(t_(string)).toString(args); } protected String getText(final LOCALE locale, final String string, final Arg... args) { return new TextTemplate(t_(string, locale)).toString(args); } public static String t_(final String string) { return StringUtil.isBlank(string) ? "" : I18N_instance.getText(string); } public static String t_(final String string, final Arg... args) { return string.isEmpty() ? "" : I18N_instance.getText(string, args); } public static String t_(final String string, final LOCALE locale) { return string.isEmpty() ? "" : I18N_instance.getText(locale, string); } public static String t_(final String string, final LOCALE locale, final Arg... args) { return StringUtil.isBlank(string) ? "" : I18N_instance.getText(locale, string, args); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/i18n/T_.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.i18n; import static org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE; import static org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.t_; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; public class T_ { private final String message; private Arg[] args; public T_(String message) { this.message = message; } public T_(String message, Arg... args) { this(message); this.args = args; } public String localize(LOCALE locale) { if (args != null) { return t_(message, locale, args); } return AbstractI18n.t_(message, locale); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/i18n/model/I18nLocaleDefinition.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.i18n.model; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Séverin Moussel */ public class I18nLocaleDefinition extends ItemDefinition { /** * Singleton */ public static I18nLocaleDefinition get() { return (I18nLocaleDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "i18nlocale"; /** * the URL of i18nlocale resource */ private static final String API_URL = "../API/system/i18nlocale"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { setPrimaryKeys(I18nLocaleItem.ATTRIBUTE_LOCALE); } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(I18nLocaleItem.ATTRIBUTE_LOCALE, ItemAttribute.TYPE.STRING); createAttribute(I18nLocaleItem.ATTRIBUTE_NAME, ItemAttribute.TYPE.STRING); } @Override protected I18nLocaleItem _createItem() { return new I18nLocaleItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/i18n/model/I18nLocaleItem.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.i18n.model; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class I18nLocaleItem extends Item { public static final String ATTRIBUTE_NAME = "name"; public static final String ATTRIBUTE_LOCALE = "locale"; public I18nLocaleItem() { super(); } public I18nLocaleItem(final IItem item) { super(item); } public I18nLocaleItem(final String locale, final String name) { this(); this.setAttribute(ATTRIBUTE_LOCALE, locale); this.setAttribute(ATTRIBUTE_NAME, name); } @Override public ItemDefinition getItemDefinition() { return new I18nLocaleDefinition(); } public String getName() { return getAttributeValue(ATTRIBUTE_NAME); } public String getLocale() { return getAttributeValue(ATTRIBUTE_LOCALE); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/i18n/model/I18nTranslationDefinition.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.i18n.model; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Séverin Moussel */ public class I18nTranslationDefinition extends ItemDefinition { /** * Singleton */ public static I18nTranslationDefinition get() { return (I18nTranslationDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "i18ntranslation"; /** * the URL of i18nlocale resource */ private static final String API_URL = "../API/system/i18ntranslation"; private static final String PLURAL_RESOURCES_URL = "../API/system/i18ntranslation"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { setPrimaryKeys(I18nTranslationItem.ATTRIBUTE_KEY); } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(I18nTranslationItem.ATTRIBUTE_KEY, ItemAttribute.TYPE.TEXT); createAttribute(I18nTranslationItem.ATTRIBUTE_VALUE, ItemAttribute.TYPE.TEXT); } @Override protected IItem _createItem() { return new I18nTranslationItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/i18n/model/I18nTranslationItem.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.i18n.model; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Séverin Moussel */ public class I18nTranslationItem extends Item { public I18nTranslationItem() { super(); } public I18nTranslationItem(final IItem item) { super(item); } public static final String ATTRIBUTE_KEY = "key"; public static final String ATTRIBUTE_VALUE = "value"; public I18nTranslationItem(final String key, final String value) { this(); this.setAttribute(ATTRIBUTE_KEY, key); this.setAttribute(ATTRIBUTE_VALUE, value); } @Override public ItemDefinition getItemDefinition() { return new I18nTranslationDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JSonItemReader.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.json; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import org.bonitasoft.web.toolkit.client.common.AbstractTreeNode; import org.bonitasoft.web.toolkit.client.common.Tree; import org.bonitasoft.web.toolkit.client.common.TreeIndexed; import org.bonitasoft.web.toolkit.client.common.TreeLeaf; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * This class is just a set of functions used to read the JSon returned by the server side * * @author Séverin Moussel */ public class JSonItemReader { public static final boolean APPLY_VALIDATORS = true; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // UNSERIALIZER // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private static JSonUnserializer UNSERIALIZER = null; /** * @param unserializer * the unserializer to set */ public static void setUnserializer(final JSonUnserializer unserializer) { UNSERIALIZER = unserializer; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // PARSING // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Parse a Map on a JSon String * * @param json */ public static Map parseMap(final String json) { final AbstractTreeNode tree = UNSERIALIZER._unserializeTree(json); if (!(tree instanceof TreeIndexed)) { return new HashMap<>(); } return parseMap((TreeIndexed) tree); } /** * Parse a Map on a Tree * * @param tree */ private static Map parseMap(final TreeIndexed tree) { final Map result = new HashMap<>(); for (final Entry entry : tree.getValues().entrySet()) { result.put(entry.getKey(), entry.getValue()); } return result; } /** * Parse an item based on a JSon String * * @param json * @param itemDefinition */ public static E parseItem(final String json, final ItemDefinition itemDefinition) { AbstractTreeNode tree = UNSERIALIZER._unserializeTree(json); if (tree instanceof Tree) { tree = ((Tree) tree).get(0); if (!(tree instanceof TreeIndexed)) { return itemDefinition.createItem(); } } return parseItem((TreeIndexed) tree, itemDefinition); } /** * Parse an item based on a TreeIndexed */ private static E parseItem(final TreeIndexed tree, final ItemDefinition itemDefinition) { return parseItem(tree, itemDefinition, APPLY_VALIDATORS); } /** * Parse an item based on a TreeIndexed * * @param tree * @param itemDefinition */ private static E parseItem(final TreeIndexed tree, final ItemDefinition itemDefinition, final boolean applyValidators) { final Optional discriminatedItem = itemDefinition.getDiscriminatedHelper() .map(h -> h.findItemCreator(tree).get()); final E item = discriminatedItem.orElseGet(itemDefinition::createItem); item.setApplyValidators(applyValidators); for (final Entry> entry : tree.getNodes().entrySet()) { // primitive type if (entry.getValue() instanceof TreeLeaf) { item.setAttribute(entry.getKey(), ((TreeLeaf) entry.getValue()).getValue()); // json object } else if (entry.getValue() instanceof TreeIndexed) { item.setDeploy( entry.getKey(), parseItem( (TreeIndexed) entry.getValue(), itemDefinition.getDeployDefinition(entry.getKey())) ); // json list - set directly json in attribute value } else if (entry.getValue() instanceof Tree) { item.setAttribute(entry.getKey(), entry.getValue().toJson()); } } return item; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JSonItemWriter.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.json; import java.util.LinkedList; import java.util.List; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * This class is a set of functions used to write the JSon to return to the client side * * @author Séverin Moussel * @param * The class of the Items to write */ public class JSonItemWriter { /** * The items to write */ private final List itemList = new LinkedList<>(); /** * If you use this constructor, you will have to use one of the append functions. */ public JSonItemWriter() { } public JSonItemWriter append(final List datas) { this.itemList.addAll(datas); return this; } public JSonItemWriter append(final IItem item) { this.itemList.add(item); return this; } @Override public String toString() { return JSonSerializer.serializeCollection(this.itemList); } public static String itemToJSON(final IItem item) { return JSonSerializer.serialize(item); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JSonSerializer.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.json; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Date; import java.util.Map; import org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer; /** * @author Séverin Moussel */ public class JSonSerializer extends JSonUtil { // Thread local as recommended in the javadoc private static final ThreadLocal dateTimeFormat = ThreadLocal .withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")); public static String serialize(final JsonSerializable object) { return serializeInternal(object).toString(); } private static StringBuilder serializeInternal(JsonSerializable object) { if (object == null) { return new StringBuilder("null"); } return new StringBuilder(object.toJson()); } public static String serialize(final Object object) { return serializeInternal(object).toString(); } private static StringBuilder serializeInternal(Object object) { if (object == null) { return new StringBuilder("null"); } else if (object instanceof JsonSerializable) { return serializeInternal((JsonSerializable) object); } else if (object instanceof Collection) { return serializeCollectionInternal((Collection) object); } else if (object instanceof Map) { return serializeMapInternal((Map) object); } else if (object instanceof Number) { return new StringBuilder(object.toString()); } else if (object instanceof Boolean) { return new StringBuilder((Boolean) object ? "true" : "false"); } else if (object instanceof Date) { return quoteInternal(dateTimeFormat.get().format((Date) object)); } else if (object instanceof Throwable) { return new StringBuilder(serializeException((Throwable) object)); } return quoteInternal(object.toString()); } public static String serializeCollection(final Collection list) { return serializeCollectionInternal(list).toString(); } private static StringBuilder serializeCollectionInternal(Collection list) { final StringBuilder json = new StringBuilder("["); boolean first = true; for (final Object item : list) { json.append(!first ? "," : "").append(serializeInternal(item)); first = false; } json.append("]"); return json; } public static String serializeMap(final Map map) { return serializeMapInternal(map).toString(); } private static StringBuilder serializeMapInternal(Map map) { final StringBuilder json = new StringBuilder().append("{"); boolean first = true; for (final Object key : map.keySet()) { json.append(!first ? "," : "").append(quoteInternal(key.toString())).append(":") .append(serializeInternal(map.get(key))); first = false; } json.append("}"); return json; } public static String serializeException(final Throwable e) { return new JsonExceptionSerializer(e).end(); } public static String serializeStringMap(final Map map) { return serializeStringMapInternal(map).toString(); } private static StringBuilder serializeStringMapInternal(Map map) { final StringBuilder json = new StringBuilder("{"); boolean first = true; for (final Object key : map.keySet()) { json.append(!first ? "," : "").append(quoteInternal(key.toString())).append(":") .append(quoteInternal(map.get(key))); first = false; } json.append("}"); return json; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JSonUnserializer.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.json; import org.bonitasoft.web.toolkit.client.common.AbstractTreeNode; /** * @author Séverin Moussel */ public interface JSonUnserializer { AbstractTreeNode _unserializeTree(final String json); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JSonUtil.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.json; import static java.lang.Integer.toHexString; /** * @author Séverin Moussel */ public class JSonUtil { public static String quote(final String value) { return quoteInternal(value).toString(); } public static StringBuilder quoteInternal(final String value) { return new StringBuilder("\"").append(escape(value)).append("\""); } public static String escape(final String string) { return escapeInternal(string).toString(); } private static StringBuilder escapeInternal(String string) { if (string == null || string.length() == 0) { return new StringBuilder(); } char b; char c = 0; int i; final int len = string.length(); final StringBuilder sb = new StringBuilder(len + 4); for (i = 0; i < len; i += 1) { b = c; c = string.charAt(i); switch (c) { case '<': case '>': case '\'': case '\\': case '"': sb.append(convertToUnicodeInternal(c)); break; case '/': if (b == '<') { sb.append('\\'); } sb.append(c); break; case '\b': sb.append("\\b"); break; case '\t': sb.append("\\t"); break; case '\n': sb.append("\\n"); break; case '\f': sb.append("\\f"); break; case '\r': sb.append("\\r"); break; default: if (c < ' ' || c >= '\u0080' && c < '\u00a0' || c >= '\u2000' && c < '\u2100') { sb.append(convertToUnicodeInternal(c)); } else { sb.append(c); } } } return new StringBuilder(sb); } private static StringBuffer convertToUnicodeInternal(char character) { StringBuilder hexString = new StringBuilder("000").append(toHexString(character)); return new StringBuffer("\\u").append(hexString.substring(hexString.length() - 4)); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JsonSerializable.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.json; /** * @author Séverin Moussel */ public interface JsonSerializable { String toJson(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/session/SessionDefinition.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.session; import org.bonitasoft.web.toolkit.client.data.item.Definitions; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * User definition * * @author Julien Mege */ public class SessionDefinition extends ItemDefinition { /** * Singleton */ public static SessionDefinition get() { return (SessionDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "session"; /** * the URL of user resource */ private static final String API_URL = "../API/system/session"; @Override public String defineToken() { return TOKEN; } @Override protected void definePrimaryKeys() { setPrimaryKeys(SessionItem.ATTRIBUTE_USERID); } @Override protected String defineAPIUrl() { return API_URL; } @Override protected void defineAttributes() { createAttribute(SessionItem.ATTRIBUTE_FIRSTNAME, ItemAttribute.TYPE.STRING); createAttribute(SessionItem.ATTRIBUTE_LASTNAME, ItemAttribute.TYPE.STRING); createAttribute(SessionItem.ATTRIBUTE_ICON, ItemAttribute.TYPE.IMAGE); createAttribute(SessionItem.ATTRIBUTE_USERID, ItemAttribute.TYPE.STRING); createAttribute(SessionItem.ATTRIBUTE_USERNAME, ItemAttribute.TYPE.STRING); createAttribute(SessionItem.ATTRIBUTE_CONF, ItemAttribute.TYPE.STRING); } @Override public SessionItem _createItem() { return new SessionItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/session/SessionItem.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.session; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; /** * @author Julien Mege */ public class SessionItem extends Item { public SessionItem() { super(); } public static final String ATTRIBUTE_SESSIONID = "session_id"; public static final String ATTRIBUTE_USERID = "user_id"; public static final String ATTRIBUTE_FIRSTNAME = "first_name"; public static final String ATTRIBUTE_LASTNAME = "last_name"; public static final String ATTRIBUTE_USERNAME = "user_name"; public static final String ATTRIBUTE_ICON = "icon"; public static final String ATTRIBUTE_IS_TECHNICAL_USER = "is_technical_user"; public static final String ATTRIBUTE_VERSION = "version"; public static final String ATTRIBUTE_BRANDING_VERSION = "branding_version"; public static final String ATTRIBUTE_BRANDING_VERSION_WITH_DATE = "branding_version_with_date"; public static final String ATTRIBUTE_CONF = "conf"; public static final String ATTRIBUTE_COPYRIGHT = "copyright"; public static final String ATTRIBUTE_IS_GUEST_USER = "is_guest_user"; @Override public ItemDefinition getItemDefinition() { return new SessionDefinition(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/texttemplate/Arg.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.texttemplate; /** * @author Séverin Moussel */ public class Arg { private final String name; private final Object value; public Arg(final String name, final Object value) { this.name = name; this.value = value; } public String getName() { return name; } public String getValue() { return value == null ? null : value.toString(); } @Override public String toString() { return name + ":" + getValue(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/texttemplate/TextTemplate.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.texttemplate; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Text template using %...% syntax
    * The passed parameters will replace %parameter_name% by parameterValue. * * @author Séverin Moussel */ public class TextTemplate { private String template = null; private List expectedParameters = null; private final Pattern regex = Pattern.compile("%(.*?)%"); public TextTemplate(final String template) { this.template = template; } public String toString(final Map data) { String result = this.template; for (final Entry entry : data.entrySet()) { result = result.replaceAll("%" + entry.getKey() + "%", entry.getValue() != null ? entry.getValue() : ""); } return result; } public String toString(final Arg... parameters) { String result = this.template; for (final Arg parameter : parameters) { result = result.replaceAll("%" + parameter.getName() + "%", parameter.getValue()); } return result; } public String toString(final List parameters) { String result = this.template; for (final Arg parameter : parameters) { result = result.replaceAll("%" + parameter.getName() + "%", parameter.getValue()); } return result; } @Override public String toString() { return this.template; } /** * Read the template to get the excepted parameters.
    * Expected parameters are the strings between %...% */ public List getExpectedParameters() { // Cache result if (this.expectedParameters == null) { parseExpectedParameters(); } return this.expectedParameters; } /** * Read the template to get the excepted parameters and store the result in the class variable "expectedParameters" */ private void parseExpectedParameters() { this.expectedParameters = new LinkedList<>(); Matcher matcher = regex.matcher(this.template); while (matcher.find()) { this.expectedParameters.add(matcher.group(1)); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/util/MapUtil.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.util; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; public class MapUtil { /** * Get a value in a Map. * * @param map * The map to search in * @param key * The key of the element to get. * @param defaultValue * The value to return if the element at the defined key is not set. * @return This method returns the value corresponding or the defaultValue if the element doesn't exist. */ public static String getValue(final Map map, final String key, final String defaultValue) { if (!map.containsKey(key)) { return defaultValue; } return map.get(key); } /** * Get a value in a Map and convert it to a long. * * @param map * The map to search in * @param key * The key of the element to get. * @return This method returns the Long value corresponding or NULL if the element doesn't exist or is empty. */ public static Long getValueAsLong(final Map map, final String key) throws NumberFormatException { final String value = map.get(key); if (StringUtil.isBlank(value)) { return null; } return Long.valueOf(value); } /** * Get a value in a Map and convert it to an integer. * * @param map * The map to search in * @param key * The key of the element to get. * @return This method returns the long value corresponding or NULL if the element doesn't exist or is empty. */ public static Boolean getValueAsBoolean(final Map map, final String key) throws IllegalArgumentException { final String value = map.get(key); if (StringUtil.isBlank(value)) { return null; } return StringUtil.toBoolean(value); } /** * Check if a map entry is blank (not set, NULL or empty String). * * @param map * The map to check * @param key * The key to check * @return This method returns TRUE if the key is not set OR null OR an empty String, otherwise FALSE. */ public static boolean isBlank(final Map map, final String key) { return !map.containsKey(key) || StringUtil.isBlank(map.get(key)); } /** * Remove a map entry if its value is blank (NULL or empty String) * * @param map * The map to check * @param key * The key to check * @return This method returns TRUE if the key is not set OR has been removed, otherwise FALSE. */ public static boolean removeIfBlank(final Map map, final String key) { if (isBlank(map, key)) { map.remove(key); return true; } return false; } public static Map asMap(final Arg... args) { final Map results = new HashMap<>(); for (final Arg arg : args) { results.put(arg.getName(), arg.getValue()); } return results; } public static abstract class ForEach { protected abstract void apply(K key, V value); } public static void iterate(Map map, ForEach modifier) { Iterator> it = map.entrySet().iterator(); while (it.hasNext()) { Entry entry = it.next(); modifier.apply(entry.getKey(), entry.getValue()); } } public static String getMandatory(Map map, String key) { String value = map.get(key); if (value == null) { throw new RuntimeException("Can't find value corresponding to " + key); } return value; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/util/StringUtil.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.util; /** * @author Julien Mege */ public abstract class StringUtil { /** * Convert a String to a boolean using the smart way
    *

      *
    • if the String is NULL, the Boolean will be NULL.
    • *
    • if the String can be cast to a long, the boolean will be TRUE if the long value is > 0, otherwise FALSE.
    • *
    • if the String is equal to "true", "yes" or "ok" the boolean will be TRUE.
    • *
    • if the String is equal to "false", "no" or "ko" the boolean will be FALSE.
    • *
    • all other cases will throw an IllegalArgumentException
    • * * @param value * The value to convert * @return This method will return the Boolean value of the value passed. * @throw IllegalArgumentException */ public static Boolean toBoolean(final String value) throws IllegalArgumentException { if (value == null) { return null; } // FIXME Manage integer values (<=0 false, >=1 true) if ("true".equals(value) || "yes".equals(value) || "ok".equals(value)) { return true; } else if ("false".equals(value) || "no".equals(value) || "ko".equals(value)) { return false; } try { return Integer.parseInt(value) > 0; } catch (final NumberFormatException e) { throw new IllegalArgumentException(value + " is not a valid boolean value"); } } /** * Converts a String to an integer plus if the String is NULL, the returned value will be NULL. */ public static Integer toInteger(final String value) throws NumberFormatException { if (value == null) { return null; } return Integer.valueOf(value); } /** * Check if a value is blank (NULL or empty String). * * @param value * The value to check * @return This method returns TRUE if the value is null OR an empty String, otherwise FALSE. */ public static boolean isBlank(final String value) { return value == null || value.isEmpty() || value.trim().isEmpty(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/APIID.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIIncorrectIdException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemIdMalformedException; import org.bonitasoft.web.toolkit.client.common.json.JSonSerializer; import org.bonitasoft.web.toolkit.client.common.json.JsonSerializable; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.ui.utils.ListUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Julien Mege */ public class APIID implements JsonSerializable { private static final String SEPARATOR = "/"; private final List ids = new ArrayList<>(); private ItemDefinition itemDefinition = null; private static final Logger LOGGER = LoggerFactory.getLogger(APIID.class.getName()); // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CONSTRUCTORS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private APIID(final String... id) { this(Arrays.asList(id)); } private APIID(final Long... id) { for (final Long i : id) { this.ids.add(i != null ? String.valueOf(i) : null); } } private APIID(final List ids) { // if the id passed is a serialized APIID if (ids.size() == 1 && ids.get(0).contains("/")) { this.ids.addAll(Arrays.asList(ids.get(0).split("/"))); } else { this.ids.addAll(ids); } } public void setItemDefinition(final ItemDefinition definition) { this.itemDefinition = definition; final int size = this.itemDefinition.getPrimaryKeys().size(); if (this.ids.size() < size) { if (size == 0) { throw new APIException(this.itemDefinition.getClass().getName() + " is missing a valid primaryKey"); } if (size == 1) { throw new APIException( "Wrong APIID format for [" + this.itemDefinition.getClass().getName() + "]." + " This APIID must be a single id."); } throw new APIException( "Wrong APIID format for [" + this.itemDefinition.getClass().getName() + "]." + " This APIID must be compound of [" + ListUtils.join(this.itemDefinition.getPrimaryKeys(), ",") + "] in this exact order."); } } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // STATIC CONSTRUCTORS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static APIID makeAPIID(final String... id) { if (id == null) { return null; } return makeAPIID(Arrays.asList(id)); } public static APIID makeAPIID(final Long... ids) { if (ids == null || ids.length == 0) { return null; } // If at least one id is not null for (final Long id : ids) { if (id != null && id > 0L) { return new APIID(ids); } } return null; } public static APIID makeAPIID(final List ids) { if (ids == null || ids.size() == 0) { return null; } // If at least one id is not null for (final String id : ids) { if (id != null && !id.isEmpty()) { return new APIID(ids); } } return null; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public List getIds() { return this.ids; } @Override public String toString() { String resourceId = ""; if (this.ids != null && this.ids.size() > 0) { for (final String id : this.ids) { if (!"".equals(resourceId)) { resourceId = resourceId + SEPARATOR; } resourceId = resourceId + id; } } return resourceId; } public boolean isValidLongID() { if (this.ids.size() == 1) { try { final long lid = Long.parseLong(this.ids.get(0)); if (lid > 0L) { return true; } } catch (final NumberFormatException e) { LOGGER.debug(this.ids.get(0) + " is not a valid long ID. ID must be non-zero positive number."); } } else { LOGGER.debug("ID is not a valid long ID. ID must not be multiple."); } return false; } public Long toLong() { if (this.ids.size() > 1) { throw new IllegalArgumentException("Can't convert compound ID to long"); } try { final long lid = Long.parseLong(this.ids.get(0)); if (lid > 0L) { return lid; } else { //zero or negative ids are not allowed String errorMessage = lid + " is not a valid long ID. ID must be non-zero positive."; LOGGER.debug(errorMessage); throw new APIIncorrectIdException(errorMessage); } } catch (final NumberFormatException e) { throw new APIItemIdMalformedException("APIID", "Can't convert non numeric ID to long"); } } /* * Retrieve a part of the id with his index. * @return this method return a part of the id as a String. */ public final String getPart(final int partIndex) { return this.ids.get(partIndex); } public final Long getPartAsLong(final int partIndex) { return Long.parseLong(getPart(partIndex)); } public String getPart(final String attributeName) { final int index = this.itemDefinition.getPrimaryKeys().indexOf(attributeName); if (index == -1) { throw new APIException(attributeName + " is an invalid APIID index. " + "This APIID must be made of " + ListUtils.join(this.itemDefinition.getPrimaryKeys(), ", ") + " in this exact order."); } return this.ids.get(index); } public Long getPartAsLong(final String attributeName) { String part = getPart(attributeName); return part == null ? null : Long.valueOf(part); } public APIID getPartAsAPIID(final String attributeName) { return APIID.makeAPIID(getPart(attributeName)); } public static List toLongList(final List ids) { final List results = new ArrayList<>(); for (final APIID id : ids) { results.add(id.toLong()); } return results; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // EQUALS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public int hashCode() { return toString().hashCode(); } @Override public boolean equals(final Object obj) { if (obj == null) { return false; } return toString().equals(obj.toString()); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // JSON // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public String toJson() { return JSonSerializer.serialize(this.ids); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/Definitions.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item; import java.util.HashMap; import java.util.Map; import org.bonitasoft.web.toolkit.client.ItemDefinitionFactory; /** * @author Julien Mege */ public class Definitions { private final Map> itemDefinitions = new HashMap<>(); private static final Definitions INSTANCE = new Definitions(); /** * Get the ViewController instance. * * @return the unique instance of the ViewController. */ public static Definitions getInstance() { return INSTANCE; } public static ItemDefinition get(final String token) { return getInstance().getDefinition(token); } public final ItemDefinition getDefinition(final String token) { if (itemDefinitions.containsKey(token)) { return itemDefinitions.get(token); } else if (DummyItemDefinition.TOKEN.equals(token)) { return new DummyItemDefinition(); } else { final ItemDefinition itemDefinition = ItemDefinitionFactory.getDefaultFactory() .defineItemDefinitions(token); if (itemDefinition != null) { itemDefinitions.put(token, itemDefinition); return itemDefinition; } // TODO Throw exception return null; } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/DiscriminatedItemDefinitionHelper.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item; import java.util.Map; import java.util.function.Supplier; import org.bonitasoft.web.rest.model.application.AbstractApplicationDefinition; import org.bonitasoft.web.toolkit.client.common.TreeIndexed; /** * This helps Items definitions which needs an attribute value to determine their concrete implementation. * * @see ItemDefinition#getDiscriminatedHelper() * @see AbstractApplicationDefinition#getDiscriminatedHelper() for an example * @param the {@link IItem} which is defined, same as in {@link ItemDefinition} */ public interface DiscriminatedItemDefinitionHelper { /** * Find the appropriate creator with attributes to discriminate * * @param attributes the attributes for creation, one of which should be used to discriminate * @return the item creator */ public Supplier findItemCreator(final Map attributes); /** * Find the appropriate creator with properties tree to discriminate * * @param tree the tree of properties, one of which should be used to discriminate * @return the item creator */ /* * We could delegate to the attributes method with a default implementation, * but it wouldn't be as effective as getting the direct value. */ public Supplier findItemCreator(final TreeIndexed tree); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/DummyItem.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item; /** * @author Séverin Moussel */ public class DummyItem extends Item { public DummyItem() { super(); } @Override public ItemDefinition getItemDefinition() { return DummyItemDefinition.get(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/DummyItemDefinition.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item; /** * @author Séverin Moussel */ public class DummyItemDefinition extends ItemDefinition { /** * Singleton */ public static DummyItemDefinition get() { return (DummyItemDefinition) Definitions.get(TOKEN); } public static final String TOKEN = "dummy"; @Override protected String defineToken() { return TOKEN; } @Override protected String defineAPIUrl() { return null; } @Override protected void defineAttributes() { } @Override protected void definePrimaryKeys() { } @Override protected DummyItem _createItem() { return new DummyItem(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/IItem.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import org.bonitasoft.web.toolkit.client.common.json.JsonSerializable; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; /** * @author Séverin Moussel */ public interface IItem extends JsonSerializable { List getAPIIDOrder(); void setId(final APIID id); APIID getId(); /** * @param applyOutputModifiers * the applyOutputModifiers to set */ void setApplyOutputModifiers(final boolean applyOutputModifiers); /** * @param applyInputModifiers * the applyInputModifiers to set */ void setApplyInputModifiers(final boolean applyInputModifiers); /** * @param applyValidators * the applyValidators to set */ void setApplyValidators(final boolean applyValidators); /** * @param applyValidatorMandatory * the applyValidatorMandatory to set */ void setApplyValidatorMandatory(final boolean applyValidatorMandatory); /** * Set an attribute value. *

      * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it. * * @param name * The name of the attribute. Must be the same as in the ItemDefinition. * @param value * The value of the Item. */ void setAttribute(final String name, final String value); void setAttribute(final String name, final Object value); /** * Set a Date attribute value. *

      * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it. * * @param name * The name of the attribute. Must be the same as in the ItemDefinition. * @param value * The value of the Item. */ void setAttribute(final String name, final Date value); /** * Set an attribute value. *

      * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it. * * @param name * The name of the attribute. Must be the same as in the ItemDefinition. * @param value * The value of the Item. */ void setAttribute(final String name, final APIID value); /** * Set an attribute value. *

      * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it. * * @param name * The name of the attribute. Must be the same as in the ItemDefinition. * @param value * The value of the Item. */ void setAttribute(final String name, final String value, final boolean applyModifiers, final boolean applyValidators); /** * Set an attribute value. *

      * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it. * * @param name * The name of the attribute. Must be the same as in the ItemDefinition. * @param value * The value of the Item. */ void setAttribute(final String name, final Object value, final boolean applyModifiers, final boolean applyValidators); /** * /** * Set an attribute value. *

      * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it. * * @param name * The name of the attribute. Must be the same as in the ItemDefinition. * @param value * The value of the Item. */ void setAttribute(final String name, final APIID value, final boolean applyModifiers, final boolean applyValidators); /** * Set a Date attribute value. *

      * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it. * * @param name * The name of the attribute. Must be the same as in the ItemDefinition. * @param value * The value of the Item. */ void setAttribute(final String name, final Date value, final boolean applyModifiers, final boolean applyValidators); /** * Set a deployed version of an attribute * * @param attributeName * The name of the attribute to deploy * @param item * The deployed version of the attribute */ void setDeploy(final String attributeName, final IItem item); /** * Remove a deployed version of an attribute * * @param attributeName * The name of the attribute deploy to remove */ void removeDeploy(final String attributeName); /** * Indicate if there are no attribute defined. * * @return This methods returns TRUE if there are no attributes, otherwise FALSE. */ boolean isEmpty(); /** * Indicate if the attribute exists even if its value is NULL or empty. * * @param name * The name of the attribute to check. * @return This method returns TRUE if the attribute exists, otherwise FALSE. */ boolean hasAttribute(final String name); /** * Get the value of an attribute * * @param attributeName * The name of the attribute * @param applyModifiers * Indicate whether or not the output modifiers defined for this resource need to be apply. * @return This function returns the value of the attribute or the defaultValue set. */ String getAttributeValue(final String attributeName, final boolean applyModifiers); /** * Get the value of an attribute * * @param attributeName * The name of the attribute * @return This function returns the value of the attribute or NULL if not set. */ String getAttributeValue(final String attributeName); /** * Get the value of an attribute * * @param itemAttribute * The attribute * @return This function returns the value of the attribute or NULL if not set. */ String getAttributeValue(final ItemAttribute itemAttribute); /** * Get the value of an attribute * * @param itemAttribute * The name of the attribute * @param applyModifiers * Indicate whether or not the output modifiers defined for this resource need to be apply. * @return This function returns the value of the attribute or the defaultValue set. */ String getAttributeValue(final ItemAttribute itemAttribute, final boolean applyModifiers); APIID getAttributeValueAsAPIID(final String attributeName, final boolean applyModifiers); APIID getAttributeValueAsAPIID(final String attributeName); APIID getAttributeValueAsAPIID(final ItemAttribute itemAttribute, final boolean applyModifiers); APIID getAttributeValueAsAPIID(final ItemAttribute itemAttribute); Date getAttributeValueAsDate(final String attributeName, final boolean applyModifiers); Date getAttributeValueAsDate(final String attributeName); Date getAttributeValueAsDate(final ItemAttribute itemAttribute, final boolean applyModifiers); Date getAttributeValueAsDate(final ItemAttribute itemAttribute); Map getAttributes(); Map getAttributes(final boolean applyModifiers); /** * Get a deployed version of an attribute * * @param attributeName * The name of the attribute to deploy * @return This method returns the deployed version of an attribute if it's available, otherwise NULL. */ IItem getDeploy(final String attributeName); ArrayList getAttributeNames(); void setAttributes(final Map attributes, final boolean applyModifiers, final boolean applyValidators); void setAttributes(final Map attributes); /** * Get the definition of an Item *

      * This function must be overridden to return the definition corresponding to the IItem type.b * * @return This function return an instance of ItemDefinition for the current IItem type */ ItemDefinition getItemDefinition(); @Override String toString(); Map getDeploys(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/Item.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.bonitasoft.web.toolkit.client.common.json.JSonSerializer; import org.bonitasoft.web.toolkit.client.common.util.StringUtil; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; import org.bonitasoft.web.toolkit.client.data.item.attribute.ModifierEngine; import org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorEngine; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualDescription; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasDualName; import org.bonitasoft.web.toolkit.client.data.item.template.ItemHasUniqueId; import org.bonitasoft.web.toolkit.client.ui.utils.DateFormat; /** * @author Julien Mege, Séverin Moussel */ public abstract class Item implements IItem { public Item() { super(); } public Item(final IItem item) { super(); attributes.putAll(item.getAttributes()); } @Override public List getAPIIDOrder() { return getItemDefinition().getPrimaryKeys(); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEFAULT FILTERS SUPERVISOR AND TEAM MANAGER // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private final Map attributes = new HashMap<>(); private final Map deploys = new HashMap<>(); // private final Map counters = new HashMap(); @Override public final void setId(final APIID id) { setAttribute(ItemHasUniqueId.ATTRIBUTE_ID, id); } @Override public APIID getId() { APIID apiid = null; final ItemDefinition itemDefinition = getItemDefinition(); if (this instanceof ItemHasUniqueId) { apiid = getAttributeValueAsAPIID(ItemHasUniqueId.ATTRIBUTE_ID); } else { final List primaryKeysValues = new ArrayList<>(); // Filling values final List primaryKeys = itemDefinition.getPrimaryKeys(); if (primaryKeys.isEmpty()) { primaryKeys.add(ItemHasUniqueId.ATTRIBUTE_ID); } for (final String key : primaryKeys) { primaryKeysValues.add(this.getAttributeValue(key)); } apiid = APIID.makeAPIID(primaryKeysValues); } // Setting definition apiid.setItemDefinition(itemDefinition); return apiid; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEFAULT BEHAVIOR // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private static boolean applyOutputModifiersByDefault = true; private static boolean applyInputModifiersByDefault = true; private static boolean applyValidatorsByDefault = true; private static boolean applyValidatorMandatoryByDefault = true; private Boolean applyOutputModifiers = null; private Boolean applyInputModifiers = null; private Boolean applyValidators = null; private Boolean applyValidatorMandatory = null; /** * @param applyOutputModifiersByDefault * the applyOutputModifiersByDefault to set */ public static void setApplyOutputModifiersByDefault(final boolean applyOutputModifiersByDefault) { Item.applyOutputModifiersByDefault = applyOutputModifiersByDefault; } /** * @param applyInputModifiersByDefault * the applyInputModifiersByDefault to set */ public static void setApplyInputModifiersByDefault(final boolean applyInputModifiersByDefault) { Item.applyInputModifiersByDefault = applyInputModifiersByDefault; } /** * @param applyValidatorsByDefault * the applyValidatorsByDefault to set */ public static void setApplyValidatorsByDefault(final boolean applyValidatorsByDefault) { Item.applyValidatorsByDefault = applyValidatorsByDefault; } /** * @param applyValidatorMandatoryByDefault * the applyValidatorMandatoryByDefault to set */ public static void setApplyValidatorMandatoryByDefault(final boolean applyValidatorMandatoryByDefault) { Item.applyValidatorMandatoryByDefault = applyValidatorMandatoryByDefault; } /** * @param applyOutputModifiers * the applyOutputModifiers to set */ @Override public final void setApplyOutputModifiers(final boolean applyOutputModifiers) { this.applyOutputModifiers = applyOutputModifiers; } /** * @param applyInputModifiers * the applyInputModifiers to set */ @Override public final void setApplyInputModifiers(final boolean applyInputModifiers) { this.applyInputModifiers = applyInputModifiers; } /** * @param applyValidators * the applyValidators to set */ @Override public final void setApplyValidators(final boolean applyValidators) { this.applyValidators = applyValidators; } /** * @param applyValidatorMandatory * the applyValidatorMandatory to set */ @Override public final void setApplyValidatorMandatory(final boolean applyValidatorMandatory) { this.applyValidatorMandatory = applyValidatorMandatory; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Set an attribute value. *

      * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it. * * @param name * The name of the attribute. Must be the same as in the ItemDefinition. * @param value * The value of the Item. */ @Override public void setAttribute(final String name, final String value) { setAttribute( name, value, applyInputModifiers == null ? applyInputModifiersByDefault : applyInputModifiers, applyValidators == null ? applyValidatorsByDefault : applyValidators); } @Override public void setAttribute(final String name, final Object value) { this.setAttribute(name, value != null ? value.toString() : null); } /** * Set a Date attribute value. *

      * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it. * * @param name * The name of the attribute. Must be the same as in the ItemDefinition. * @param value * The value of the Item. */ @Override public final void setAttribute(final String name, final Date value) { this.setAttribute(name, DateFormat.dateToSql(value)); } /** * Set an attribute value. *

      * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it. * * @param name * The name of the attribute. Must be the same as in the ItemDefinition. * @param value * The value of the Item. */ @Override public final void setAttribute(final String name, final APIID value) { if (value != null) { this.setAttribute(name, value.toString()); } else { setAttribute(name, (String) null); } } /** * Set an attribute value. *

      * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it. * * @param name * The name of the attribute. Must be the same as in the ItemDefinition. * @param value * The value of the Item. */ @Override public final void setAttribute(final String name, final String value, final boolean applyModifiers, final boolean applyValidators) { final ItemAttribute attribute = getItemDefinition().getAttribute(name); String realValue = value; if (attribute != null && applyModifiers) { realValue = ModifierEngine.modify(realValue, attribute.getInputModifiers()); } attributes.put(name, realValue); if (applyValidators) { ValidatorEngine.validate(this, applyValidatorMandatory == null ? applyValidatorMandatoryByDefault : applyValidatorMandatory); } } /** * Set an attribute value. *

      * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it. * * @param name * The name of the attribute. Must be the same as in the ItemDefinition. * @param value * The value of the Item. */ @Override public final void setAttribute(final String name, final Object value, final boolean applyModifiers, final boolean applyValidators) { if (value != null) { setAttribute(name, value.toString(), applyModifiers, applyValidators); } else { setAttribute(name, (String) null, applyModifiers, applyValidators); } } /** * /** * Set an attribute value. *

      * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it. * * @param name * The name of the attribute. Must be the same as in the ItemDefinition. * @param value * The value of the Item. */ @Override public final void setAttribute(final String name, final APIID value, final boolean applyModifiers, final boolean applyValidators) { if (value != null) { setAttribute(name, value.toString(), applyModifiers, applyValidators); } else { setAttribute(name, (String) null, applyModifiers, applyValidators); } } /** * Set a Date attribute value. *

      * The attribute is consider as non existent before the first call of this function. The JSonItemReader fills it. * * @param name * The name of the attribute. Must be the same as in the ItemDefinition. * @param value * The value of the Item. */ @Override public final void setAttribute(final String name, final Date value, final boolean applyModifiers, final boolean applyValidators) { setAttribute(name, DateFormat.dateToSql(value), applyModifiers, applyValidators); } /** * Set a deployed version of an attribute * * @param attributeName * The name of the attribute to deploy * @param item * The deployed version of the attribute */ @Override public void setDeploy(final String attributeName, final IItem item) { deploys.put(attributeName, item); } /** * Remove a deployed version of an attribute * * @param attributeName * The name of the attribute deploy to remove */ @Override public final void removeDeploy(final String attributeName) { deploys.remove(attributeName); } /** * Set a counter value. * * @param counterName * The name of the counter to set * @param value * The value of the counter */ // public final void setCounterValue(final String counterName, final Long value) { // this.counters.put(counterName, value); // } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Indicate if there are no attribute defined. * * @return This methods returns TRUE if there are no attributes, otherwise FALSE. */ @Override public final boolean isEmpty() { return attributes.isEmpty(); } /** * Indicate if the attribute exists even if its value is NULL or empty. * * @param name * The name of the attribute to check. * @return This method returns TRUE if the attribute exists, otherwise FALSE. */ @Override public final boolean hasAttribute(final String name) { return attributes.containsKey(name); } /** * Get the value of an attribute * * @param attributeName * The name of the attribute * @param applyModifiers * Indicate whether or not the output modifiers defined for this resource need to be apply. * @return This function returns the value of the attribute or the defaultValue set. */ @Override public final String getAttributeValue(final String attributeName, final boolean applyModifiers) { // Detect deploy called using "thisAttributeToDeployName.deployedItemAttributeName" final String[] splittedAttribute = attributeName.split("\\."); // Read a deployed attribute if (splittedAttribute.length == 2) { final IItem deploy = getDeploy(splittedAttribute[0]); return deploy.getAttributeValue(splittedAttribute[1]); } // Read an id from a deployed attribute else if (deploys.containsKey(attributeName)) { final IItem deploy = getDeploy(attributeName); if (deploy == null) { return null; } return deploy.getId().toString(); } // Read a local attribute else { String realValue = attributes.get(attributeName); if (this instanceof ItemHasDualName) { if (ItemHasDualName.ATTRIBUTE_DISPLAY_NAME.equals(attributeName) && StringUtil.isBlank(realValue)) { realValue = this.getAttributeValue(ItemHasDualName.ATTRIBUTE_NAME); } } else if (this instanceof ItemHasDualDescription) { if (ItemHasDualDescription.ATTRIBUTE_DISPLAY_DESCRIPTION.equals(attributeName) && StringUtil.isBlank(realValue)) { realValue = this.getAttributeValue(ItemHasDualDescription.ATTRIBUTE_DESCRIPTION); } } return realValue; } } /** * Get the value of an attribute * * @param attributeName * The name of the attribute * @return This function returns the value of the attribute or NULL if not set. */ @Override public String getAttributeValue(final String attributeName) { return this.getAttributeValue(attributeName, applyOutputModifiers == null ? applyOutputModifiersByDefault : applyOutputModifiers); } /** * Get the value of an attribute * * @param itemAttribute * The attribute * @return This function returns the value of the attribute or NULL if not set. */ @Override public final String getAttributeValue(final ItemAttribute itemAttribute) { return this.getAttributeValue(itemAttribute.getName(), applyOutputModifiers == null ? applyOutputModifiersByDefault : applyOutputModifiers); } /** * Get the value of an attribute * * @param itemAttribute * The name of the attribute * @param applyModifiers * Indicate whether or not the output modifiers defined for this resource need to be apply. * @return This function returns the value of the attribute or the defaultValue set. */ @Override public final String getAttributeValue(final ItemAttribute itemAttribute, final boolean applyModifiers) { return this.getAttributeValue(itemAttribute.getName(), applyModifiers); } // AS APIID @Override public final APIID getAttributeValueAsAPIID(final String attributeName, final boolean applyModifiers) { return APIID.makeAPIID(getAttributeValue(attributeName, applyModifiers)); } @Override public final APIID getAttributeValueAsAPIID(final String attributeName) { return APIID.makeAPIID(getAttributeValue(attributeName)); } @Override public final APIID getAttributeValueAsAPIID(final ItemAttribute itemAttribute, final boolean applyModifiers) { return APIID.makeAPIID(getAttributeValue(itemAttribute, applyModifiers)); } @Override public final APIID getAttributeValueAsAPIID(final ItemAttribute itemAttribute) { return APIID.makeAPIID(getAttributeValue(itemAttribute)); } // AS Date @Override public final Date getAttributeValueAsDate(final String attributeName, final boolean applyModifiers) { return DateFormat.sqlToDate(getAttributeValue(attributeName, applyModifiers)); } @Override public final Date getAttributeValueAsDate(final String attributeName) { return DateFormat.sqlToDate(getAttributeValue(attributeName)); } @Override public final Date getAttributeValueAsDate(final ItemAttribute itemAttribute, final boolean applyModifiers) { return DateFormat.sqlToDate(getAttributeValue(itemAttribute, applyModifiers)); } @Override public final Date getAttributeValueAsDate(final ItemAttribute itemAttribute) { return DateFormat.sqlToDate(getAttributeValue(itemAttribute)); } // AS Long public final Long getAttributeValueAsLong(final String attributeName, final boolean applyModifiers) { return Long.valueOf(getAttributeValue(attributeName, applyModifiers)); } public final Long getAttributeValueAsLong(final String attributeName) { return Long.valueOf(getAttributeValue(attributeName)); } public final Long getAttributeValueAsLong(final ItemAttribute itemAttribute, final boolean applyModifiers) { return Long.valueOf(getAttributeValue(itemAttribute, applyModifiers)); } public final Long getAttributeValueAsLong(final ItemAttribute itemAttribute) { return Long.valueOf(getAttributeValue(itemAttribute)); } // Get all @Override public final Map getAttributes() { return this.getAttributes(applyOutputModifiers == null ? applyOutputModifiersByDefault : applyOutputModifiers); } @Override public final Map getAttributes(final boolean applyModifiers) { final Map results = new HashMap<>(); for (final String attributeName : attributes.keySet()) { results.put(attributeName, this.getAttributeValue(attributeName, applyModifiers)); } return results; } /** * Get a deployed version of an attribute * * @param attributeName * The name of the attribute to deploy * @return This method returns the deployed version of an attribute if it's available, otherwise NULL. */ @Override public final IItem getDeploy(final String attributeName) { // TODO If not deployed, automatically call the API to deploy. return deploys.get(attributeName); } @Override public Map getDeploys() { return deploys; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public final ArrayList getAttributeNames() { return new ArrayList<>(attributes.keySet()); } @Override public final void setAttributes(final Map attributes, final boolean applyModifiers, final boolean applyValidators) { if (attributes == null || attributes.size() == 0) { return; } for (final String attributeName : attributes.keySet()) { setAttribute(attributeName, attributes.get(attributeName), applyModifiers, false); } if (applyValidators) { ValidatorEngine.validate(this); } } @Override public final void setAttributes(final Map attributes) { setAttributes( attributes, applyInputModifiers == null ? applyInputModifiersByDefault : applyInputModifiers, applyValidators == null ? applyValidatorsByDefault : applyValidators); } /** * Get the definition of an Item *

      * This function must be overridden to return the definition corresponding to the Item type.b * * @return This function return an instance of ItemDefinition for the current Item type */ @Override abstract public ItemDefinition getItemDefinition(); // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CONVERT // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public final String toString() { final StringBuilder sb = new StringBuilder(); for (final String key : attributes.keySet()) { final String rawValue = attributes.get(key); final String cleanValue = this.getAttributeValue(key); sb.append(key).append(" : ").append(rawValue); if (rawValue != null && !rawValue.equals(cleanValue)) { sb.append(" >> ").append(cleanValue); } sb.append("\r\n"); } for (final Entry entry : deploys.entrySet()) { sb.append(entry.getKey()).append(" : ").append(entry.getValue()); sb.append("\r\n"); } return sb.toString(); } @Override public final String toJson() { final StringBuilder json = new StringBuilder().append("{"); boolean first = true; for (final String attribute : getAttributeNames()) { if (deploys.containsKey(attribute)) { json.append(!first ? "," : "").append(JSonSerializer.quote(attribute)).append(":") .append(JSonSerializer.serialize(deploys.get(attribute))); } else { json.append(!first ? "," : "").append(JSonSerializer.quote(attribute)).append(":") .append(JSonSerializer.quote(this.getAttributeValue(attribute))); } first = false; } json.append("}"); return json.toString(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (applyInputModifiers == null ? 0 : applyInputModifiers.hashCode()); result = prime * result + (applyOutputModifiers == null ? 0 : applyOutputModifiers.hashCode()); result = prime * result + (applyValidatorMandatory == null ? 0 : applyValidatorMandatory.hashCode()); result = prime * result + (applyValidators == null ? 0 : applyValidators.hashCode()); result = prime * result + (attributes == null ? 0 : attributes.hashCode()); result = prime * result + (deploys == null ? 0 : deploys.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Item other = (Item) obj; if (applyInputModifiers == null) { if (other.applyInputModifiers != null) { return false; } } else if (!applyInputModifiers.equals(other.applyInputModifiers)) { return false; } if (applyOutputModifiers == null) { if (other.applyOutputModifiers != null) { return false; } } else if (!applyOutputModifiers.equals(other.applyOutputModifiers)) { return false; } if (applyValidatorMandatory == null) { if (other.applyValidatorMandatory != null) { return false; } } else if (!applyValidatorMandatory.equals(other.applyValidatorMandatory)) { return false; } if (applyValidators == null) { if (other.applyValidators != null) { return false; } } else if (!applyValidators.equals(other.applyValidators)) { return false; } if (attributes == null) { if (other.attributes != null) { return false; } } else if (!attributes.equals(other.attributes)) { return false; } if (deploys == null) { return other.deploys == null; } else { return deploys.equals(other.deploys); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/ItemDefinition.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; import org.bonitasoft.web.toolkit.client.data.item.attribute.ValidationException; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.Validator; /** * This class is the super class of all Items definitions. *

      * To define a new IItem type, just create a class that extends ItemDefinition.
      * Then, you will have to override the function beginning with define.... and in each overridden functions, you * will call the corresponding setters. *

      * There are some functions for which the override is not mandatory but you can override any define function. *

      * It's highly recommended to define attributes names using public static final strings * * @author Séverin Moussel */ public abstract class ItemDefinition { public ItemDefinition() { setToken(defineToken()); defineAttributes(); setAPIUrl(defineAPIUrl()); definePrimaryKeys(); defineDeploys(); } // /////////////////////////////////////////////////////////////////////////////////////////////////// // Token // /////////////////////////////////////////////////////////////////////////////////////////////////// private String token = null; /** * This function must be override to define the token to use to access to the current view. */ protected abstract String defineToken(); /** * @param token * the token to set */ public final void setToken(final String token) { this.token = token; } /** * @return the token */ public final String getToken() { return this.token; } // /////////////////////////////////////////////////////////////////////////////////////////////////// // SINGULAR RESOURCE URL // /////////////////////////////////////////////////////////////////////////////////////////////////// private String APIUrl = null; public final String getAPIUrl() { return this.APIUrl; } protected final void setAPIUrl(final String url) { this.APIUrl = url; } /** * This function must be overridden to define the singular version of the resource URL. *

      * This function must call this.setSingularResourceUrl(...) *

      * Example :
      * this.setUrl("/API/organization/user"); */ protected abstract String defineAPIUrl(); // /////////////////////////////////////////////////////////////////////////////////////////////////// // ATTRIBUTES // /////////////////////////////////////////////////////////////////////////////////////////////////// private final LinkedHashMap attributes = new LinkedHashMap<>(); private final ArrayList primaryKeys = new ArrayList<>(); /** * Create and save a new attribute for the current item type. *

      * Example :
      * final ItemAttribute firstName = this.createAttribute(this.FIRSTNAME, ItemAttribute.TYPE.STRING); * * @param name * The name of the attribute * @param type * The type of the attribute. Must be one of ItemAttribute.TYPE.xxx * @return This function returns the Attribute created to allow to add other details on it. */ public ItemAttribute createAttribute(final String name, final ItemAttribute.TYPE type) { final ItemAttribute attribute = new ItemAttribute(name, type); this.attributes.put(name, attribute); return attribute; } /** * This function must be overridden to define the attributes of an Item. *

      * Defining attributes is made by calling several createAttribute() *

      * Example :
      * * final ItemAttribute firstName = this.createAttribute(this.FIRSTNAME, ItemAttribute.TYPE.STRING);
      * firstName.setLabel("Firstname");
      * firstName.setTooltip("Enter your firstname");
      * firstName.setTableSortable(true);
      * firstName.setFormEditable(true);
      *
      * final ItemAttribute firstName = this.createAttribute(this.LASTNAME, ItemAttribute.TYPE.STRING);
      * firstName.setLabel("Lastname");
      * firstName.setTooltip("Enter your lastname");
      * firstName.setTableSortable(true);
      * firstName.setFormEditable(true);
      * ... *
      */ protected abstract void defineAttributes(); public final ArrayList getAttributes() { return new ArrayList<>(this.attributes.values()); } protected abstract void definePrimaryKeys(); protected final void setPrimaryKeys(final String... primaryKeys) { this.primaryKeys.clear(); for (final String key : primaryKeys) { this.primaryKeys.add(key); // Create the attribute if it's missing if (!this.attributes.containsKey(key)) { createAttribute(key, ItemAttribute.TYPE.ITEM_ID); } } } public final ArrayList getPrimaryKeys() { return this.primaryKeys; } /** * Retrieve an attribute by its name. * * @param name * The name of the attribute * @return This function returns the attribute or null if there is no attribute with the defined name. */ public final ItemAttribute getAttribute(final String name) { return this.attributes.get(name); } public final boolean containsAttribute(final String attributeName) { return this.attributes.containsKey(attributeName); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEPLOYS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private final Map> deploys = new HashMap<>(); protected void defineDeploys() { // No deploys by default } /** * Declare an attribute as deployable. * * @param attributeName * The name of the deployable attribute. * @param definition * The ItemDefinition of the item contained in the deployed attribute. */ protected final void declareDeployable(final String attributeName, final ItemDefinition definition) { this.deploys.put(attributeName, definition); } public final ItemDefinition getDeployDefinition(final String attributeName) { return this.deploys.containsKey(attributeName) ? this.deploys.get(attributeName) : Definitions.get(DummyItemDefinition.TOKEN); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // VALIDATORS AND MODIFIERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Get the validators in a map */ public final Map> getValidators() { final Map> validators = new HashMap<>(); for (final ItemAttribute attribute : getAttributes()) { validators.put(attribute.getName(), attribute.getValidators()); } return validators; } // /////////////////////////////////////////////////////////////////////////////////////////////////// // AUTOMATICALY INSTANCIATE ITEM AND ITEM DEPENDENT TOOLS // /////////////////////////////////////////////////////////////////////////////////////////////////// /** * This helps Items definitions which needs an attribute value to determine their concrete implementation. * * @return helper for discriminating and find the concrete implementation */ public Optional> getDiscriminatedHelper() { return Optional.empty(); } /** * This function create a new empty Item * * @return This function returns the new Item */ abstract protected E _createItem(); public final E createItem() { try { return this.createItem((Map) null); } catch (final ValidationException e) { // DO NOTHING : this can't fail due to validation because validation is disabled. return null; } } public final E createItem(final Map attributes) throws ValidationException { final Optional discriminatedItem = getDiscriminatedHelper().map(h -> h.findItemCreator(attributes).get()); final E item = discriminatedItem.orElseGet(this::_createItem); if (attributes != null) { item.setAttributes(attributes); } return item; } public final E createItem(IItem sourceItem) { return sourceItem == null ? null : createItem(sourceItem.getAttributes()); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // MAKE APIID // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public final APIID makeAPIID(final String... id) { return makeAPIID(Arrays.asList(id)); } public final APIID makeAPIID(final Long... ids) { final APIID apiid = APIID.makeAPIID(ids); apiid.setItemDefinition(this); return apiid; } public final APIID makeAPIID(final List ids) { final APIID apiid = APIID.makeAPIID(ids); apiid.setItemDefinition(this); return apiid; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ItemAttribute.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute; import java.util.List; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.DefaultValueModifier; import org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.Modifier; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.FileIsImageValidator; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.FileIsNoScript; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.IsBooleanValidator; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.IsIntegerValidator; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.IsNumericValidator; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.MandatoryValidator; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringFormatColorValidator; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringFormatEmailValidator; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringFormatURLValidator; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringMaxLengthValidator; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.StringSingleLineValidator; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.Validator; /** * This class represents an attribute of an {@link Item} * * @author Séverin Moussel */ public final class ItemAttribute implements Validable, ModifiableInput { /** * The maximum length of a color string (#FDB975). */ public static final int MAX_LENGTH_COLOR = 7; /** * The maximum length of a simple String (varchar in database). */ public static final int MAX_LENGTH_STRING = 255; /** * The maximum length of a text string (text in database). */ public static final int MAX_LENGTH_TEXT = 2000; /** * The maximum length of a URL */ public static final int MAX_LENGTH_URL = 1024; /** * The type of value an attribute can get. * * @author Séverin Moussel */ public enum TYPE { /** * A single line String *

        *
      • Validate the string is a single line
      • *
      • Validate max length of {@value #MAX_LENGTH_STRING}
      • *
      */ STRING, /** * A multiline String *
        *
      • Validate max length of {@value #MAX_LENGTH_TEXT}
      • *
      */ TEXT, /** * An image path. *
        *
      • Validate the extension of the file is a valid image
      • *
      • Validate max length of {@value #MAX_LENGTH_STRING}
      • *
      */ IMAGE, PASSWORD, FILE, BOOLEAN, ENUM, NUMERIC, INTEGER, DATE, TIME, DATETIME, COLOR, /** * An email address. *
        *
      • Validate the email format xxx@xxx.xxx
      • *
      • Validate max length of 255
      • *
      */ EMAIL, /** * A URL *
        *
      • Validate max length of {@value #MAX_LENGTH_URL}
      • *
      */ URL, ITEM_ID } private final String name; private final TYPE type; private final String defaultValue = null; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CONSTRUCTORS + INIT // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Default constructor * * @param name * The name of the DataSource variable * @param type * The type of the attribute datas choosen in ItemAttribute.TYPE_XXX */ public ItemAttribute(final String name, final TYPE type) { this.name = name; this.type = type; initType(); } /** * Define the default validators using the provided type */ private void initType() { switch (this.type) { case PASSWORD: case STRING: // Because Text inputs are not multiline addValidator(new StringSingleLineValidator()); // Because databases varchar are limited to 255 addValidator(new StringMaxLengthValidator(MAX_LENGTH_STRING)); break; case IMAGE: addValidator(new FileIsImageValidator()); addValidator(new StringMaxLengthValidator(MAX_LENGTH_STRING)); break; case NUMERIC: addValidator(new IsNumericValidator()); break; case INTEGER: addValidator(new IsIntegerValidator()); break; case TEXT: addValidator(new StringMaxLengthValidator(MAX_LENGTH_TEXT)); break; case BOOLEAN: addValidator(new IsBooleanValidator()); break; case FILE: addValidator(new FileIsNoScript()); addValidator(new StringMaxLengthValidator(MAX_LENGTH_STRING)); break; case ENUM: break; case EMAIL: addValidator(new StringFormatEmailValidator()); addValidator(new StringMaxLengthValidator(MAX_LENGTH_STRING)); break; case URL: addValidator(new StringFormatURLValidator()); addValidator(new StringMaxLengthValidator(MAX_LENGTH_URL)); break; case COLOR: addValidator(new StringFormatColorValidator()); addValidator(new StringMaxLengthValidator(MAX_LENGTH_COLOR)); break; default: addValidator(new StringMaxLengthValidator(MAX_LENGTH_URL)); break; } } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GETTERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public String getName() { return this.name; } public TYPE getType() { return this.type; } public String getDefaultValue() { return this.defaultValue; } // // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // VALIDATORS AND MODIFIERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private final ModifiersList inputModifiers = new ModifiersList(); private final ValidatorsList validators = new ValidatorsList(); /** * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ModifiersList#getModifiers() */ @Override public List getInputModifiers() { return this.inputModifiers.getModifiers(); } /** * @param modifier * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ModifiersList#addModifier(org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.Modifier) */ @Override public void addInputModifier(final Modifier modifier) { this.inputModifiers.addModifier(modifier); } /** * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorsList#getValidators() */ @Override public List getValidators() { return this.validators.getValidators(); } @Override /** * @param validator * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorsList#addValidator(org.bonitasoft.web.toolkit.client.data.item.attribute.validator.Validator) */ public ItemAttribute addValidator(final Validator validator) { validator.setAttributeName(this.name); this.validators.addValidator(validator); return this; } /** * @param validators * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorsList#addValidators(java.util.List) */ @Override public ItemAttribute addValidators(final List validators) { this.validators.addValidators(validators); return this; } /** * @param validatorClassName * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorsList#removeValidator(java.lang.String) */ @Override public ItemAttribute removeValidator(final String validatorClassName) { this.validators.removeValidator(validatorClassName); return this; } /** * @param validatorClassName * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorsList#hasValidator(java.lang.String) */ @Override public boolean hasValidator(final String validatorClassName) { return this.validators.hasValidator(validatorClassName); } /** * @param validatorClassName * @see org.bonitasoft.web.toolkit.client.data.item.attribute.ValidatorsList#getValidator(java.lang.String) */ @Override public Validator getValidator(final String validatorClassName) { return this.validators.getValidator(validatorClassName); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // MODIFIERS / VALIDATORS EASY MAPPING // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Set the default value if no value is define for this attribute.
      * This method add a DefaultValueModifier to the attribute * * @param value */ public ItemAttribute setDefaultValue(final String value) { addInputModifier(new DefaultValueModifier(value)); return this; } /** * Define if this attribute is mandatory (mustn't be empty)
      * This method add a DefaultValueModifier to the attribute * * @param isMandatory */ public ItemAttribute isMandatory(final boolean isMandatory) { if (isMandatory) { addValidator(new MandatoryValidator()); } else { removeValidator(MandatoryValidator.class.getName()); } return this; } /** * Define if this attribute is mandatory (mustn't be empty)
      * This method add a DefaultValueModifier to the attribute */ public ItemAttribute isMandatory() { return isMandatory(true); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ModifiableInput.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute; import java.util.List; import org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.Modifier; /** * @author Séverin Moussel */ public interface ModifiableInput { List getInputModifiers(); void addInputModifier(final Modifier modifier); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ModifierEngine.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute; import java.util.List; import java.util.Map; import org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.AbstractStringModifier; import org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.Modifier; /** * @author Séverin Moussel */ public class ModifierEngine { public static void modify(final Map values, final Map> modifiers) { values.replaceAll((n, v) -> modify(values.get(n), modifiers.get(n))); } public static String modify(final String value, final List modifiers) { if (modifiers == null) { return value; } String result = value; for (final Modifier modifier : modifiers) { if (result == null) { return null; } if (modifier instanceof AbstractStringModifier) { result = ((AbstractStringModifier) modifier).clean(result); } } return result; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ModifiersList.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute; import java.util.LinkedList; import java.util.List; import org.bonitasoft.web.toolkit.client.data.item.attribute.modifier.Modifier; import org.bonitasoft.web.toolkit.client.ui.utils.ListUtils; /** * @author Séverin Moussel */ public class ModifiersList { private final List modifiers = new LinkedList<>(); public List getModifiers() { return this.modifiers; } public ModifiersList addModifier(final Modifier modifier) { ListUtils.removeFromListByClass(this.modifiers, modifier.getClass().toString(), true); this.modifiers.add(modifier); return this; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/Validable.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute; import java.util.List; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.Validator; /** * @author Séverin Moussel */ public interface Validable { List getValidators(); Validable addValidator(final Validator validator); Validable addValidators(final List validators); Validable removeValidator(final String validatorClassName); boolean hasValidator(final String validatorClassName); Validator getValidator(final String validatorClassName); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ValidationError.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute; import java.util.ArrayList; import java.util.List; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.bonitasoft.web.toolkit.client.common.texttemplate.TextTemplate; /** * @author Séverin Moussel */ public class ValidationError { private final String attributeName; private final TextTemplate template; public ValidationError(final String attributeName, final String template) { this.attributeName = attributeName; this.template = new TextTemplate(template); } public String getMessage() { final List args = new ArrayList<>(); for (final String parameterName : this.template.getExpectedParameters()) { args.add(new Arg(parameterName, parameterName)); } return this.template.toString(args); } @Override public String toString() { return this.getMessage(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ValidationException.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute; import java.util.List; import org.bonitasoft.web.toolkit.client.common.exception.KnownException; /** * @author Séverin Moussel */ @SuppressWarnings("serial") public class ValidationException extends KnownException { private final List errors; public ValidationException(final List errors) { super(errorsToMessage(errors)); this.errors = errors; } private static String errorsToMessage(final List errors) { final StringBuilder sb = new StringBuilder(); for (final ValidationError error : errors) { sb.append(error.getMessage()); sb.append("\r\n"); } return sb.toString(); } /** * @return the errors */ public List getErrors() { return this.errors; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ValidatorEngine.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.bonitasoft.web.toolkit.client.common.TreeIndexed; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.AbstractStringValidator; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.MandatoryValidator; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.Validator; /** * @author Séverin Moussel */ public class ValidatorEngine { public static void validateAttribute(final String attributeName, final Map values, final List validators, final boolean applyMandatory) throws ValidationException { final List errors = new LinkedList<>(); // Get validators if (validators != null) { // Check validators for (final Validator validator : validators) { // force attribute name as it could be different from the one set in the item definition (case of the deploys) validator.setAttributeName(attributeName); // Check mandatory validator if (validator instanceof MandatoryValidator) { if (applyMandatory) { ((MandatoryValidator) validator).check(values.get(attributeName)); } } // Check String based validator else if (validator instanceof AbstractStringValidator) { ((AbstractStringValidator) validator).check(values.get(attributeName)); } errors.addAll(validator.getErrors()); } } if (errors.size() > 0) { throw new ValidationException(errors); } } /** * Validate an Item */ public static void validate(final IItem item) throws ValidationException { validate(item, true); } /** * Validate an Item */ public static void validate(final IItem item, final boolean applyMandatory) throws ValidationException { validate(item.getAttributes(), item.getItemDefinition().getValidators(), applyMandatory); } /** * Validate a Tree */ public static void validate(final TreeIndexed tree, final Map> validators, final boolean applyMandatory) throws ValidationException { validate(tree.getValues(), validators, applyMandatory); } /** * Validate a Map */ public static void validate(final Map values, final Map> validators, final boolean applyMandatory) throws ValidationException { final List errors = new LinkedList<>(); for (final String attributeName : values.keySet()) { try { validateAttribute(attributeName, values, validators.get(attributeName), applyMandatory); } catch (final ValidationException e) { errors.addAll(e.getErrors()); } } if (errors.size() > 0) { throw new ValidationException(errors); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/ValidatorsList.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute; import java.util.LinkedList; import java.util.List; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.Validator; import org.bonitasoft.web.toolkit.client.ui.utils.ListUtils; /** * @author Séverin Moussel */ public class ValidatorsList implements Validable { private final List validators = new LinkedList<>(); @Override public List getValidators() { return this.validators; } @Override public ValidatorsList addValidator(final Validator validator) { ListUtils.removeFromListByClass(this.validators, validator.getClass().getName(), true); this.validators.add(validator); return this; } @Override public ValidatorsList addValidators(final List validators) { for (final Validator validator : validators) { addValidator(validator); } return this; } @Override public ValidatorsList removeValidator(final String validatorClassName) { ListUtils.removeFromListByClass(this.validators, validatorClassName); return this; } @Override public boolean hasValidator(final String validatorClassName) { return getValidator(validatorClassName) != null; } @Override public Validator getValidator(final String validatorClassName) { return (Validator) ListUtils.getFromListByClass(this.validators, validatorClassName); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/modifier/AbstractStringModifier.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.modifier; /** * @author Séverin Moussel */ public abstract class AbstractStringModifier extends Modifier { public String[] clean(final String[] values) { for (int i = 0; i < values.length; i++) { values[i] = this.clean(values[i]); } return values; } public abstract String clean(String value); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/modifier/DefaultValueModifier.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.modifier; /** * @author Séverin Moussel */ public class DefaultValueModifier extends AbstractStringModifier { private final String defaultValue; public DefaultValueModifier(final String defaultValue) { super(); this.defaultValue = defaultValue; } @Override public String clean(final String value) { return value != null ? value : this.defaultValue; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/modifier/Modifier.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.modifier; /** * @author Séverin Moussel */ public class Modifier { } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/modifier/ReplaceRegexpModifier.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.modifier; /** * @author Séverin Moussel */ public class ReplaceRegexpModifier extends AbstractStringModifier { protected final String regexp; protected final String replace; public ReplaceRegexpModifier(final String regexp, final String replace) { super(); this.regexp = regexp; this.replace = replace; } @Override public String clean(final String value) { return value.replaceAll(this.regexp, this.replace); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/AbstractCollectionValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; /** * @author Séverin Moussel */ public abstract class AbstractCollectionValidator extends Validator { public final void check(final String[] attributeValue) { reset(); _check(attributeValue); } protected abstract void _check(String[] attributeValue); @Override protected final void addError(final String error) { super.addError(error); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/AbstractNumericValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * @author Séverin Moussel */ public abstract class AbstractNumericValidator extends AbstractStringValidator { @Override /** * Check if the value is numeric, then delegate to abstract _check(Double) */ protected final void _check(final String attributeValue) { Double numericValue = null; try { numericValue = Double.valueOf(attributeValue); } catch (final NumberFormatException e) { addError(AbstractI18n.t_("%attribute% must be a numeric value")); } if (numericValue != null) { this._check(numericValue); } } protected abstract void _check(Double attributeValue); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/AbstractStringFormatValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import java.util.regex.Pattern; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * @author Séverin Moussel */ public abstract class AbstractStringFormatValidator extends AbstractStringValidator { private final Pattern regexp; private Boolean exclude; /** * Default Constructor. * * @param regexp */ public AbstractStringFormatValidator(final String regexp) { this(regexp, false); } /** * Default Constructor. * * @param regexp */ public AbstractStringFormatValidator(final String regexp, final boolean exclude) { super(); this.regexp = Pattern.compile(regexp); this.exclude = exclude; } protected void setExclude(final Boolean exclude) { this.exclude = exclude; } /* * (non-Javadoc) * @see org.bonitasoft.console.client.toolkit.item.attribute.checker.AttributeStringChecker#check(java.lang.String) */ @Override protected void _check(final String attributeValue) { // use `find()` instead of `matches()` because it was the implementation of the original `com.google.gwt.regexp.shared.RegExp#test()` method final boolean match = regexp.matcher(attributeValue).find(); if (attributeValue.contains("HTTP Error")) { addError(AbstractI18n .t_("Error uploading the file. Maybe your session expired. You can try to refresh the page.")); } else if (exclude && match || !exclude && !match) { addError(defineErrorMessage()); } } abstract protected String defineErrorMessage(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/AbstractStringValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; /** * @author Séverin Moussel */ public abstract class AbstractStringValidator extends AbstractCollectionValidator { protected String locale = ""; @Override protected final void _check(final String[] attributeValue) { for (int i = 0; i < attributeValue.length; i++) { this.check(attributeValue[i]); } } /** * Function to override to define the checking operation * * @param attributeValue */ public final void check(final String attributeValue) { reset(); if (attributeValue == null || attributeValue.length() == 0) { // Not an error. The null value will be detected by a mandatory validator. return; } this._check(attributeValue); } protected abstract void _check(String attributeValue); public void setLocale(String locale) { this.locale = locale; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/EnumValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * @author Paul AMAR */ public class EnumValidator extends AbstractStringValidator { private final List listString; /** * Default Constructor. */ public EnumValidator(final List listString) { this.listString = listString; } /** * Default Constructor. */ public EnumValidator(final String... listString) { this(new ArrayList<>(Arrays.asList(listString))); } /* * (non-Javadoc) * @see * org.bonitasoft.web.toolkit.client.data.item.attribute.validator.AbstractStringValidator#_check(java.lang.String) */ @Override protected void _check(final String attributeValue) { final StringBuilder cleanList = new StringBuilder(); for (final String s : listString) { cleanList.append(s).append(", "); if (s.equals(attributeValue)) { return; } } addError(AbstractI18n.t_("%attribute% must be one of {%list%}").replace("%list%", cleanList.substring(0, cleanList.length() - 2))); } public void addValue(final String value) { listString.add(value); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/FileExtensionAllowedValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import java.util.Arrays; import java.util.List; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.bonitasoft.web.toolkit.client.ui.utils.ListUtils; /** * @author Séverin Moussel */ public class FileExtensionAllowedValidator extends AbstractStringFormatValidator { private final List extensions; public FileExtensionAllowedValidator(final String... extensions) { super(makeRegexp(extensions)); this.extensions = Arrays.asList(extensions); } private static String makeRegexp(final String[] extensions) { final StringBuilder sb = new StringBuilder(); for (final String extension : extensions) { sb.append(extension).append("|"); } return "\\.(" + sb.substring(0, sb.length() - 1) + ")$"; } @Override protected String defineErrorMessage() { return AbstractI18n.t_("%attribute% file format is not allowed. Only %file_formats% files are allowed.", new Arg("file_formats", ListUtils.join(this.extensions, ", ", " " + AbstractI18n.t_("or") + " ", "\".", "\""))); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/FileExtensionForbiddenValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; /** * @author Paul AMAR */ public class FileExtensionForbiddenValidator extends FileExtensionAllowedValidator { public FileExtensionForbiddenValidator(final String... extensions) { super(extensions); setExclude(true); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/FileIsImageOrServletPathValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * This validates that the attribute has either an image file extension or starts with a servlet path * * @author Dumitru Corini * @author Anthony Birembaut */ public class FileIsImageOrServletPathValidator extends AbstractStringFormatValidator { private static final String[] IMAGE_EXTENSIONS = { "png", "jpg", "jpeg", "bmp", "wbmp", "tga", "gif", "PNG", "JPG", "JPEG", "BMP", "WBMP", "TGA", "GIF" }; public FileIsImageOrServletPathValidator(final String servletPath) { super(makeRegexp(IMAGE_EXTENSIONS, servletPath)); } private static String makeRegexp(final String[] extensions, final String servletPath) { final StringBuilder sb = new StringBuilder(); for (final String extension : extensions) { sb.append(extension).append("|"); } String preparedServletPath = servletPath.replace(".", "\\.").replace("/", "\\/"); return "^" + preparedServletPath + "|\\.(" + sb.substring(0, sb.length() - 1) + ")$"; } @Override protected String defineErrorMessage() { return AbstractI18n.t_("%attribute% file format not allowed or not starting with correct servlet path"); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/FileIsImageValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; /** * @author Séverin Moussel */ public class FileIsImageValidator extends FileExtensionAllowedValidator { public FileIsImageValidator() { super("png", "jpg", "jpeg", "bmp", "wbmp", "tga", "gif", "PNG", "JPG", "JPEG", "BMP", "WBMP", "TGA", "GIF"); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/FileIsNoScript.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; /** * @author Paul AMAR */ public class FileIsNoScript extends FileExtensionForbiddenValidator { /** * Default Constructor. */ public FileIsNoScript() { super("exe", "bat", "cmd", "sh", "com", "jsp", "js", "jar"); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/IsBooleanValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * @author Séverin Moussel */ public class IsBooleanValidator extends AbstractStringValidator { @Override protected void _check(final String attributeValue) { // Test the case of a String representing a boolean value if ("true".equalsIgnoreCase(attributeValue) || "false".equalsIgnoreCase(attributeValue) || "on".equalsIgnoreCase(attributeValue) || "off".equalsIgnoreCase(attributeValue) || "yes".equalsIgnoreCase(attributeValue) || "no".equalsIgnoreCase(attributeValue)) { return; } // Test the case of a Numeric representing a boolean value try { Double.valueOf(attributeValue); } catch (final NumberFormatException e) { addError(AbstractI18n.t_("%attribute% must be a boolean value")); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/IsIntegerValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * @author Séverin Moussel */ public class IsIntegerValidator extends AbstractStringValidator { @Override protected void _check(final String attributeValue) { try { Long.valueOf(attributeValue); } catch (final NumberFormatException e) { addError(AbstractI18n.t_("%attribute% must be an integer value")); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/IsNumericValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; /** * @author Séverin Moussel */ public class IsNumericValidator extends AbstractNumericValidator { @Override protected void _check(final Double attributeValue) { // Do nothing. The parent class check is enough } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/MandatoryValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * @author Séverin Moussel */ public class MandatoryValidator extends AbstractCollectionValidator { private final String message = AbstractI18n.t_("%attribute% is mandatory"); public MandatoryValidator() { super(); } @Override protected final void _check(final String[] attributeValue) { for (String s : attributeValue) { this.check(s); } } /** * Function to override to define the checking operation * * @param attributeValue */ public final void check(final String attributeValue) { reset(); if (attributeValue == null || attributeValue.trim().length() == 0) { addError(this.message); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/StringFormatColorValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * @author Paul AMAR */ public class StringFormatColorValidator extends AbstractStringFormatValidator { public StringFormatColorValidator() { super("#[A-Fa-f0-9]{6}"); } @Override protected String defineErrorMessage() { return AbstractI18n.t_("%attribute% is not a valid Color value"); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/StringFormatEmailValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * @author Séverin Moussel */ public class StringFormatEmailValidator extends AbstractStringFormatValidator { public StringFormatEmailValidator() { // RFC 2822 with permissive modification (allow not quoted name) and allow TLD from 2 characters to 32 (32 was arbitrary chosen) super("[a-zA-Z0-9!#$%&'*+/=?^T_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&'*+/=?^T_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+(?:[a-zA-Z]{2,32})"); } @Override protected final void _check(final String attributeValue) { if (attributeValue.contains(" ")) { addError(defineErrorMessage()); } else { super._check(attributeValue); } } @Override protected String defineErrorMessage() { return AbstractI18n.t_("%attribute% is not a valid email"); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/StringFormatURLValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * @author Paul AMAR */ public class StringFormatURLValidator extends AbstractStringFormatValidator { public StringFormatURLValidator() { super("(((?:http|https|ftp|mailto):\\/\\/[a-zA-Z0-9\\/\\?=#&%~\\-]+(.[a-zA-Z0-9\\/\\?=#&%~\\-]+)+)|(www(.[a-zA-Z0-9\\/\\?=T_#&%~\\-\\)\\(]+){2,}))"); } @Override protected String defineErrorMessage() { return AbstractI18n.t_("%attribute% is not a valid URL"); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/StringMaxLengthValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; /** * @author Séverin Moussel */ public class StringMaxLengthValidator extends AbstractStringValidator { private final Integer maxLength; private final Boolean includeMax; public StringMaxLengthValidator(final Integer maxLength) { this(maxLength, true); } public StringMaxLengthValidator(final Integer maxLength, final Boolean includeMax) { this.maxLength = maxLength; this.includeMax = includeMax; } @Override protected void _check(final String attributeValue) { final int length = attributeValue.length(); // Checking for including the maxLength if (includeMax) { if (maxLength != null && length > maxLength) { addError( AbstractI18n.t_("%attribute% must be less or equal than %value%", new Arg("value", maxLength))); } } else { if (maxLength != null && length >= maxLength) { addError(AbstractI18n.t_("%attribute% must be less than %value%", new Arg("value", maxLength))); } } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/StringRegexpValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * @author Séverin Moussel */ public class StringRegexpValidator extends AbstractStringFormatValidator { public StringRegexpValidator(final String regexp) { super(regexp); } public StringRegexpValidator(final String regexp, final boolean exclude) { super(regexp, exclude); } @Override protected String defineErrorMessage() { return AbstractI18n.t_("%attribute% is not well written"); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/StringSingleLineValidator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * @author Séverin Moussel */ public class StringSingleLineValidator extends AbstractStringValidator { @Override protected void _check(final String attributeValue) { if (attributeValue.indexOf('\n') >= 0) { addError(AbstractI18n.t_("%attribute% must be on a single line")); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/Validator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import java.util.ArrayList; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.bonitasoft.web.toolkit.client.common.texttemplate.TextTemplate; import org.bonitasoft.web.toolkit.client.data.item.attribute.ValidationError; /** * @author Séverin Moussel */ public abstract class Validator { private final ArrayList errors = new ArrayList<>(); private String attributeName = null; /** * @param attributeName * the attributeName to set */ public void setAttributeName(final String attributeName) { this.attributeName = attributeName; } /** * @return the attributeName */ public String getAttributeName() { return this.attributeName; } public final ArrayList getErrors() { return this.errors; } protected void addError(final String error) { this.errors.add( new ValidationError( this.attributeName, new TextTemplate(error).toString(new Arg("attribute", "%" + this.attributeName + "%")))); } public final boolean hasError() { return this.errors.size() > 0; } protected void reset() { this.errors.clear(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/template/ItemHasCreator.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.template; import java.util.Date; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Séverin Moussel */ public interface ItemHasCreator { String ATTRIBUTE_CREATION_DATE = "creation_date"; String ATTRIBUTE_CREATED_BY_USER_ID = "created_by_user_id"; void setCreationDate(String date); void setCreationDate(Date date); Date getCreationDate(); void setCreatedByUserId(String id); void setCreatedByUserId(Long id); void setCreatedByUserId(APIID id); APIID getCreatedByUserId(); IItem getCreatedByUser(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/template/ItemHasDualDescription.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.template; /** * @author Séverin Moussel */ public interface ItemHasDualDescription { String ATTRIBUTE_DESCRIPTION = "description"; String ATTRIBUTE_DISPLAY_DESCRIPTION = "displayDescription"; void setDescription(final String description); void setDisplayDescription(final String displayDescription); String getDescription(); String getDisplayDescription(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/template/ItemHasDualName.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.template; /** * Insert name and display_name in the allowed attributes. * * @author Julien Mege, Séverin Moussel */ public interface ItemHasDualName { String ATTRIBUTE_NAME = "name"; String ATTRIBUTE_DISPLAY_NAME = "displayName"; void setName(final String name); void setDisplayName(final String displayName); String getName(); String getDisplayName(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/template/ItemHasIcon.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.template; /** * @author Séverin Moussel */ public interface ItemHasIcon { String ATTRIBUTE_ICON = "icon"; String getIcon(); void setIcon(String icon); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/template/ItemHasLastUpdateDate.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.template; import java.util.Date; /** * @author Julien Mege */ public interface ItemHasLastUpdateDate { String ATTRIBUTE_LAST_UPDATE_DATE = "last_update_date"; void setLastUpdateDate(String date); void setLastUpdateDate(Date date); Date getLastUpdateDate(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/template/ItemHasLastUpdater.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.template; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; /** * @author Séverin Moussel */ public interface ItemHasLastUpdater extends ItemHasLastUpdateDate { String ATTRIBUTE_LAST_UPDATE_USER_ID = "last_update_user_id"; void setLastUpdateUserId(String id); void setLastUpdateUserId(APIID id); APIID getLastUpdateUserId(); IItem getLastUpdateUser(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/template/ItemHasUniqueId.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.template; import org.bonitasoft.web.toolkit.client.data.APIID; /** * @author Julien Mege */ public interface ItemHasUniqueId { String ATTRIBUTE_ID = "id"; void setId(String id); void setId(APIID id); void setId(Long id); APIID getId(); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/ui/utils/DateFormat.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.ui.utils; import java.util.Date; import org.bonitasoft.web.toolkit.client.common.CommonDateFormater; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * Available formats are *
        *
      • long : a simple long value representing the date in milliseconds
      • *
      • SQL (JSON compatible) : yyyy-MM-dd HH:mm:ss.SSS
      • *
      • Form input : MM/dd/YY (localized format)
      • *
      • Display as a short date : MM/dd/YY (localized format)
      • *
      • Display as a full date with time : MM/dd/YYYY HH:mm (localized format)
      • *
      • Display as time relative to current time : "1 hour ago", "in 5 minutes", "2 years ago"
      • *
      * * @author Séverin Moussel */ // TODO : // * pull out all kind of date formatter in different classes like RelativeStringDateFormatter // * make an interface implemented by all date formatter // * make a switch return a polimorph DateFormatter and just call dateFormatter.format(...) public abstract class DateFormat { public enum UNIT { YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND } public enum FORMAT { SQL("yyyy-MM-dd HH:mm:ss.SSS"), FORM(AbstractI18n.t_("MM/dd/yyyy")), DISPLAY( AbstractI18n.t_("MM/dd/yyyy h:mm a")), DISPLAY_SHORT( AbstractI18n.t_("MMMM dd, yyyy")), LONG, DISPLAY_RELATIVE; private final String formatString; FORMAT() { this(""); } FORMAT(final String formatString) { this.formatString = formatString; } public String getFormatString() { return this.formatString; } } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GENERIC TO DATE conversion // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static Date formatToDate(final String date, final FORMAT format) throws IllegalArgumentException { if (date == null) { return null; } if (format.equals(FORMAT.LONG)) { new Date(Long.parseLong(date)); } return formatToDate(date, format.getFormatString()); } public static Date formatToDate(final String date, final String format) throws IllegalArgumentException { if (date == null) { return null; } return CommonDateFormater.parse(date, format); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GENERIC TO LONG CONVERSION // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static Long formatToLong(final String date, final FORMAT format) throws IllegalArgumentException { if (date == null || date.isEmpty()) { return null; } if (format.equals(FORMAT.LONG)) { return Long.parseLong(date); } return formatToLong(date, format.getFormatString()); } public static Long formatToLong(final String date, final String format) throws IllegalArgumentException { if (date == null) { return null; } final Date _date = CommonDateFormater.parse(date, format); return _date.getTime(); } // // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DATE OBJECT CONVERSIONS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static String dateToFormat(final Date date, final String format) { if (date == null) { return null; } return CommonDateFormater.toString(date, format); } public static String dateToSql(final Date date) { return dateToFormat(date, FORMAT.SQL.getFormatString()); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SQL TO ??? // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static Long sqlToLong(final String date) { return formatToLong(date, FORMAT.SQL); } public static Date sqlToDate(final String date) { return formatToDate(date, FORMAT.SQL); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // FORM TO ??? // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/ui/utils/ListUtils.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.ui.utils; import java.util.List; /** * @author Séverin Moussel */ public class ListUtils { public static void removeFromListByClass(final List list, final String className) { removeFromListByClass(list, className, false); } public static void removeFromListByClass(final List list, final String className, final boolean firstOnly) { for (int i = 0; i < list.size(); i++) { final Object o = list.get(i); if (o.getClass().getName().equals(className)) { list.remove(i); if (firstOnly) { return; } i--; } } } public static Object getFromListByClass(final List list, final String className) { for (final Object o : list) { if (o.getClass().getName().equals(className)) { return o; } } return null; } public static String join(final List list, final String separator) { return join(list, separator, separator, "", ""); } public static String join(final List list, final String separator, final String lastSeparator, final String itemPrefix, final String itemSuffix) { if (list == null || list.isEmpty()) { return ""; } final StringBuilder result = new StringBuilder(); for (int i = 0; i < list.size(); i++) { final Object item = list.get(i); if (item != null) { if (i == list.size() - 1 && list.size() > 1) { result.append(lastSeparator); } else if (i > 0) { result.append(separator); } if (itemPrefix != null) { result.append(itemPrefix); } result.append(item); if (itemSuffix != null) { result.append(itemSuffix); } } } return result.toString(); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/Service.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.server; import java.io.IOException; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE; /** * This class represent a service.
      * It must define a public final static String TOKEN. * * @author Séverin Moussel */ public abstract class Service { /** * The ServletCall responsible of this service. */ private ServiceServletCall caller = null; /** * Set the caller. * * @param caller * The ServletCall responsible of this service. */ public void setCaller(final ServiceServletCall caller) { this.caller = caller; } /** * @see org.bonitasoft.web.toolkit.server.ServletCall#getHttpSession() */ protected HttpSession getHttpSession() { return caller.getHttpSession(); } /** * @see org.bonitasoft.web.toolkit.server.ServletCall#getResponse() */ public HttpServletResponse getHttpResponse() { return caller.getResponse(); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ENTRY POINT // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Entry point of the service.
      * The returned object will be send as response. *
        *
      • If the object is a String, Integer, Long, ..., the response content will be the value as is.
      • *
      • If the object implements JsonSerializable, the content will be returned as a json String.
      • *
      • If the object is a list or a map, the response will be a json String.
      • *
      • Otherwise, the response content will be the object.toString().
      • *
      * * @return This method returns the output to respond. * @throws IOException */ public abstract Object run() throws IOException; // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DELEGATIONS FOR PARAMETERS ACCESS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * @see org.bonitasoft.web.toolkit.server.ServletCall#getParameter(java.lang.String) */ public String getParameter(final String name) { return caller.getParameter(name); } public LOCALE getLocale() { try { return LOCALE.valueOf(caller.getLocale()); } catch (final IllegalArgumentException e) { return AbstractI18n.getDefaultLocale(); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/ServiceException.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.server; import org.bonitasoft.web.toolkit.client.common.exception.http.JsonExceptionSerializer; import org.bonitasoft.web.toolkit.client.common.exception.http.ServerException; /** * @author Séverin Moussel */ public class ServiceException extends ServerException { private static final long serialVersionUID = 4014993729649254349L; protected final String path; public ServiceException(final String path) { super(); this.path = path; } public ServiceException(final String path, final String message, final Throwable cause) { super(message, cause); this.path = path; } public ServiceException(final String path, final String message) { super(message); this.path = path; } public ServiceException(final String path, final Throwable cause) { super(cause); this.path = path; } /** * Get the path of the service called * * @return the path */ public String getPath() { return this.path; } @Override protected String defaultMessage() { return "The service \"" + getPath() + "\" has encountered an unknown error"; } @Override protected JsonExceptionSerializer buildJson() { return super.buildJson() .appendAttribute("path", getPath()); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/ServiceFactory.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.server; /** * @author Colin PUY */ public interface ServiceFactory { /** * @param calledToolToken * The token as a path. For example if the tool url is ".../TOOLS/actors/import", the token will be * "actors/import". */ Service getService(String calledToolToken); } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/ServiceNotFoundException.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.server; /** * @author Séverin Moussel */ public class ServiceNotFoundException extends ServiceException { private static final long serialVersionUID = -6084401627376022011L; public ServiceNotFoundException(final String path) { super(path); } @Override protected String defaultMessage() { return "The service \"" + getPath() + "\" doesn't exist or is not declared"; } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/ServiceServletCall.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.server; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.web.toolkit.client.common.exception.api.APIMalformedUrlException; import org.bonitasoft.web.toolkit.client.common.json.JSonItemReader; import org.bonitasoft.web.toolkit.client.common.json.JSonSerializer; import org.bonitasoft.web.toolkit.client.common.json.JsonSerializable; /** * This class represent a call to a service. * * @author Séverin Moussel */ public class ServiceServletCall extends ServletCall { private String calledToolToken; private final ServiceFactory serviceFactory; public ServiceServletCall(ServiceFactory serviceFactory, final HttpServletRequest request, final HttpServletResponse response) { super(request, response); this.serviceFactory = serviceFactory; } @Override protected void parseRequest(final HttpServletRequest request, HttpServletResponse response) { super.parseRequest(request, response); // Gather all the POST parameters final Map postParams = JSonItemReader.parseMap(getInputStream()); for (final Map.Entry entry : postParams.entrySet()) { this.parameters.put(entry.getKey(), new String[] { entry.getValue() }); } // Read TOOL tokens this.calledToolToken = request.getPathInfo(); if (this.calledToolToken.length() == 0) { throw new APIMalformedUrlException(getRequestURL(), "Missing tool name"); } } @Override public final void doGet() throws IOException { run(); } @Override public final void doPost() throws IOException { run(); } @Override public final void doPut() throws IOException { run(); } @Override public final void doDelete() throws IOException { run(); } /** * Instantiate and run the service. * * @throws IOException */ private void run() throws IOException { final Service service = serviceFactory.getService(this.calledToolToken); service.setCaller(this); final Object response = service.run(); if (response instanceof File) { output((File) response); } else if (response instanceof InputStream) { output((InputStream) response); } else if (response instanceof JsonSerializable || response instanceof Map || response instanceof List) { output(JSonSerializer.serialize(response)); } else if (response != null) { output(response.toString()); } } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/ServletCall.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.server; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.io.IOUtils; import org.bonitasoft.console.common.server.utils.LocaleUtils; import org.bonitasoft.web.toolkit.client.common.exception.http.ServerException; import org.bonitasoft.web.toolkit.client.common.json.JSonSerializer; /** * @author Séverin Moussel * @author Baptiste Mesta * @author Fabio Lombardi */ public abstract class ServletCall { private String inputStream = null; /** * The parameters of the URL.
      * Result of the parsing of the query string : "?a=b&c=d&..." */ protected final Map parameters = new HashMap<>(); /** * The request made to access this servletCall. */ private final HttpServletRequest request; /** * The response to return. */ private final HttpServletResponse response; /** * Default constructor. * * @param request * The request made to access this servletCall. * @param response * The response to return. */ public ServletCall(final HttpServletRequest request, final HttpServletResponse response) { super(); this.request = request; this.response = response; parseRequest(request, response); } /** * Constructor for tests */ public ServletCall() { request = null; response = null; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // PARAMETERS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Get the current call's HttpSession * * @return This method returns the session from the current call. */ public HttpSession getHttpSession() { return request.getSession(); } /** * @see javax.servlet.http.HttpServletRequest#getQueryString() */ public String getQueryString() { return request.getQueryString(); } /** * Reconstruct the URL the client used to make the request. * The returned URL contains a protocol, server name, port * number, and server path, but it does not include query * string parameters. * * @return This method returns the reconstructed URL */ public String getRequestURL() { return request.getRequestURL().toString(); } /** * Read the input stream and set it in a String */ public String getInputStream() { if (inputStream == null) { BufferedReader reader = null; try { // BS-8474 - use custom reader instead of request reader to avoid JBoss5.1 bug // see https://issues.jboss.org/browse/JBAS-7817 final ServletInputStream stream = request.getInputStream(); reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)); final StringBuilder sb = new StringBuilder(); String line = reader.readLine(); while (line != null) { sb.append(line + "\n"); line = reader.readLine(); } inputStream = sb.toString(); } catch (final IOException e) { throw new RuntimeException("Can't read input Stream.", e); } finally { closeQuietly(reader); } } return inputStream; } private void closeQuietly(final BufferedReader reader) { if (reader != null) { try { reader.close(); } catch (final IOException e) { // do nothing / close quietly } } } /** * Count the number of parameters passed in the URL * * @return This method returns the number of parameters in the URL */ public int countParameters() { return parameters.size(); } /** * Get a parameter values by its name * * @param name * The name of the parameter (case sensitive) * @return This method returns the values of a parameter as a list of String or null if the parameter isn't defined */ public List getParameterAsList(final String name) { return getParameterAsList(name, null); } /** * Get a parameter values by its name * * @param name * The name of the parameter (case sensitive) * @param defaultValue * The value to return if the parameter isn't define * @return This method returns the values of a parameter as a list of String */ public List getParameterAsList(final String name, final String defaultValue) { if (parameters.containsKey(name)) { return Arrays.asList(parameters.get(name)); } if (defaultValue != null) { final List results = new ArrayList<>(); results.add(defaultValue); return results; } return null; } /** * Get a parameter first value by its name * * @param name * The name of the parameter (case sensitive) * @return This method returns the first value of a parameter as a String or null if the parameter isn't define */ public String getParameter(final String name) { return getParameter(name, null); } /** * Get a parameter first value by its name * * @param name * The name of the parameter (case sensitive) * @param defaultValue * The value to return if the parameter isn't define * @return This method returns the first value of a parameter as a String */ public String getParameter(final String name, final String defaultValue) { if (parameters.containsKey(name)) { final String[] result = parameters.get(name); if (result.length > 0) { return result[0]; } } return defaultValue; } /** * Get all the parameters * * @return This method returns all the parameters as a Map of array of String */ public Map getParameters() { return parameters; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // GENERATE RESPONSES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Write into the output header. * * @param name * The name of the header to write. * @param value * The value of the header to write. */ protected void head(final String name, final String value) { response.addHeader(name, value); } /** * Output a file * * @param file * The file to output */ protected void output(final File file) { try (InputStream stream = new FileInputStream(file)) { output(stream); } catch (final FileNotFoundException e) { throw new ServerException(e); } catch (IOException e) { throw new ServerException(e); } } /** * Output a stream as a file * * @param stream * The stream to output * @param filename * The name of the file to retrieve with the stream. */ protected void output(final InputStream stream, final String filename) { response.addHeader("Content-Disposition", "attachment; filename=" + filename + ";"); output(stream); } /** * Output a stream as a file * * @param stream * The stream to output */ protected void output(final InputStream stream) { response.setContentType("application/octet-stream"); try { IOUtils.copy(stream, response.getOutputStream()); } catch (final IOException e) { throw new ServerException(e); } } /** * Write into the output * * @param string * The string to output */ protected void output(final String string) { final PrintWriter outputWriter = getOutputWriter(); outputWriter.print(string); outputWriter.flush(); } /** * Write into the output * * @param object * An object that will be transform into JSon */ protected void output(final Object object) { final PrintWriter outputWriter = getOutputWriter(); outputWriter.print(JSonSerializer.serialize(object)); outputWriter.flush(); } /** * The outputWriter in which to write the response String. */ private PrintWriter outputWriter = null; /** * Prepare the output * * @param response */ private PrintWriter getOutputWriter() { if (outputWriter == null) { response.setContentType("application/json;charset=UTF-8"); try { outputWriter = response.getWriter(); } catch (final IOException e) { throw new RuntimeException(e); } } return outputWriter; } /** * Read elements form the request *
        *
      • API tokens
      • *
      • item id (if defined)
      • *
      • parameters
      • *
      * * @param request * @param response */ @SuppressWarnings("unchecked") protected void parseRequest(final HttpServletRequest request, final HttpServletResponse response) { // Create a new HashMap and copy all the elements in the new one parameters.putAll(this.request.getParameterMap()); } public String getLocale() { return LocaleUtils.getUserLocaleAsString(request); } public HttpServletRequest getRequest() { return request; } public HttpServletResponse getResponse() { return response; } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // REQUEST ENTRY POINTS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Entry point for GET and SEARCH * * @throws IOException */ public abstract void doGet() throws IOException; /** * Entry point for CREATE * * @throws IOException */ public abstract void doPost() throws IOException; /** * Entry point for UPDATE * * @throws IOException */ public abstract void doPut() throws IOException; /** * Entry point for DELETE * * @throws IOException */ public abstract void doDelete() throws IOException; } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/servlet/ServiceServlet.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.server.servlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.web.toolkit.server.ServiceFactory; import org.bonitasoft.web.toolkit.server.ServiceServletCall; import org.bonitasoft.web.toolkit.server.ServletCall; /** * This class is the entry point of all server calls * * @author Séverin Moussel */ @SuppressWarnings("serial") public abstract class ServiceServlet extends ToolkitHttpServlet { private final ServiceFactory serviceFactory; public ServiceServlet(ServiceFactory serviceFactory) { this.serviceFactory = serviceFactory; } @Override protected ServletCall defineServletCall(final HttpServletRequest req, final HttpServletResponse resp) { return new ServiceServletCall(serviceFactory, req, resp); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/servlet/ToolkitHttpServlet.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.server.servlet; import java.io.IOException; import java.io.PrintWriter; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Locale; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.console.common.server.utils.LocaleUtils; import org.bonitasoft.web.rest.server.framework.json.JSonSimpleDeserializer; import org.bonitasoft.web.toolkit.client.common.CommonDateFormater; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIIncorrectIdException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemIdMalformedException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIMethodNotAllowedException; import org.bonitasoft.web.toolkit.client.common.exception.api.APINotFoundException; import org.bonitasoft.web.toolkit.client.common.exception.api.APITooManyRequestException; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE; import org.bonitasoft.web.toolkit.client.common.json.JSonItemReader; import org.bonitasoft.web.toolkit.client.common.json.JSonSerializer; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.bonitasoft.web.toolkit.server.ServiceException; import org.bonitasoft.web.toolkit.server.ServiceNotFoundException; import org.bonitasoft.web.toolkit.server.ServletCall; import org.bonitasoft.web.toolkit.server.utils.ServerDateFormater; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; /** * @author Séverin Moussel */ public abstract class ToolkitHttpServlet extends HttpServlet { private static final long serialVersionUID = -8470006030459575773L; public static final DateTimeFormatter RFC1123_DATE_TIME_FORMATTER = DateTimeFormatter .ofPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US) .withZone(ZoneId.of("GMT")); /** * Console logger */ protected static final Logger LOGGER = LoggerFactory.getLogger(ToolkitHttpServlet.class.getName()); // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CATCH ALL EXCEPTIONS // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public ToolkitHttpServlet() { super(); initializeToolkit(); } /** * Initialize * * @see HttpServlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) */ @Override protected final void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { try { super.service(req, resp); } catch (final Exception e) { catchAllExceptions(retrieveLowestAPIException(e), req, resp); } } private Throwable retrieveLowestAPIException(final Throwable e) { Throwable lowest = e; while (lowest.getCause() != null && lowest.getCause() instanceof APIException) { lowest = lowest.getCause(); } return lowest; } /** * Output an exception in JSon. * * @param e * The exception to output * @param resp * The response to fill * @param httpStatusCode * The status code to return */ protected final void outputException(final Throwable e, final HttpServletRequest req, final HttpServletResponse resp, final int httpStatusCode) { if (httpStatusCode >= 0) { resp.setStatus(httpStatusCode); } resp.setContentType("application/json;charset=UTF-8"); try { final PrintWriter output = resp.getWriter(); if (e instanceof APIException apiException) { setLocalization(apiException, LocaleUtils.getUserLocaleAsString(req)); } output.print(e == null ? "" : JSonSerializer.serialize(e)); output.flush(); } catch (final Exception e2) { throw new APIException(e2); } } protected final void outputException(final Throwable e, final HttpServletRequest req, final HttpServletResponse resp, final int httpStatusCode, Map headers) { for (var header : headers.entrySet()) { resp.addHeader(header.getKey(), header.getValue()); } outputException(e, req, resp, httpStatusCode); } /** * Output an exception in JSon. Expect the status code to be already set * * @param e * The exception to output * @param resp * The response to fill */ protected final void outputException(final Throwable e, final HttpServletRequest req, final HttpServletResponse resp) { outputException(e, req, resp, -1); } private void setLocalization(APIException localizable, String locale) { if (locale != null && !locale.isEmpty()) { localizable.setLocale(LOCALE.valueOf(locale)); } } /** * Initialize the toolkit */ protected void initializeToolkit() { Item.setApplyInputModifiersByDefault(false); Item.setApplyValidatorsByDefault(false); Item.setApplyOutputModifiersByDefault(false); Item.setApplyValidatorMandatoryByDefault(false); CommonDateFormater.setDateFormater(new ServerDateFormater()); JSonItemReader.setUnserializer(new JSonSimpleDeserializer()); } /** * @param req * The request called * @param resp * The response to send */ protected void catchAllExceptions(final Throwable exception, final HttpServletRequest req, final HttpServletResponse resp) { if (exception instanceof APIMethodNotAllowedException) { if (LOGGER.isInfoEnabled()) { LOGGER.info(exception.getMessage(), exception); } outputException(exception, req, resp, HttpServletResponse.SC_METHOD_NOT_ALLOWED); } else if (exception instanceof APINotFoundException || exception instanceof ServiceNotFoundException) { if (LOGGER.isInfoEnabled()) { LOGGER.info(exception.getMessage(), exception); } outputException(exception, req, resp, HttpServletResponse.SC_NOT_FOUND); } else if (exception instanceof APIItemNotFoundException) { LOGGER.debug(exception.getMessage(), exception); outputException(null, req, resp, HttpServletResponse.SC_NOT_FOUND); } else if (exception instanceof APIForbiddenException) { outputException(exception, req, resp, HttpServletResponse.SC_FORBIDDEN); } else if (exception instanceof ServiceException) { if (resp.getStatus() < HttpServletResponse.SC_BAD_REQUEST) { // Response status is not yet set with an error code outputException(exception, req, resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } else { // Response status is already set with an error code outputException(exception, req, resp); } } else if (exception instanceof APIIncorrectIdException) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(exception.getMessage(), exception); } outputException(exception, req, resp, HttpServletResponse.SC_BAD_REQUEST); } else if (exception instanceof APIItemIdMalformedException) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(exception.getMessage(), exception); } outputException(exception, req, resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } else if (exception instanceof APITooManyRequestException ex) { if (LOGGER.isErrorEnabled()) { LOGGER.error(exception.getMessage(), exception); } var headers = Map.of(HttpHeaders.RETRY_AFTER, RFC1123_DATE_TIME_FORMATTER.format(Instant.ofEpochMilli(ex.getRetryAfter()))); outputException(exception, req, resp, ex.getStatusCode(), headers); } else { if (LOGGER.isErrorEnabled()) { LOGGER.error(exception.getMessage(), exception); } outputException(exception, req, resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // DEFINE SERVLET // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected abstract ServletCall defineServletCall(final HttpServletRequest req, final HttpServletResponse resp); // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // INITIATE CALL // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected final void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { defineServletCall(req, resp).doGet(); } @Override protected final void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { defineServletCall(req, resp).doPost(); } @Override protected final void doPut(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { defineServletCall(req, resp).doPut(); } @Override protected final void doDelete(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { defineServletCall(req, resp).doDelete(); } // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // LOCKING OVERRIDES // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override protected final long getLastModified(final HttpServletRequest req) { return super.getLastModified(req); } @Override protected final void doHead(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { super.doHead(req, resp); } @Override protected final void doOptions(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { super.doOptions(req, resp); } @Override protected final void doTrace(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { super.doTrace(req, resp); } @Override public final void service(final ServletRequest req, final ServletResponse res) throws ServletException, IOException { super.service(req, res); } } ================================================ FILE: bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/server/utils/ServerDateFormater.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.server.utils; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import org.bonitasoft.web.toolkit.client.common.CommonDateFormater; /** * @author Paul AMAR */ // TODO : remove all reference in rest-server to DateFormat, Modifier, Reader, etc... Then delete this class public class ServerDateFormater extends CommonDateFormater { /** * Default Constructor. */ public ServerDateFormater() { // TODO Auto-generated constructor stub } /* * (non-Javadoc) * @see org.bonitasoft.web.toolkit.client.common.CommonDateFormater#_parse(java.lang.String, java.lang.String) */ @Override public Date _parse(final String value, final String format) { Date d = null; final SimpleDateFormat formatter = new SimpleDateFormat(format); try { d = formatter.parse(value); } catch (final ParseException e) { // Exception thrown by parse method e.printStackTrace(); } return d; } /* * (non-Javadoc) * @see org.bonitasoft.web.toolkit.client.common.CommonDateFormater#_toString(java.util.Date, java.lang.String) */ @Override public String _toString(final Date value, final String format) { final SimpleDateFormat formatter = new SimpleDateFormat(format); return formatter.format(value); } } ================================================ FILE: bpm/bonita-web-server/src/main/resources/VERSION ================================================ @projectVersion@ @brandingVersion@ Bonitasoft © @buildYear@ ================================================ FILE: bpm/bonita-web-server/src/main/resources/i18n/portal-js_es.po ================================================ msgid "" msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Project-Id-Version: bonita\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: bonita\n" "X-Crowdin-Project-ID: 13316\n" "X-Crowdin-Language: es-ES\n" "X-Crowdin-File: /dev/bonita-web/portal/portal-js.pot\n" "X-Crowdin-File-ID: 60594\n" "Language-Team: Spanish\n" "Language: es_ES\n" "PO-Revision-Date: 2024-01-01 00:00\n" msgid "1 case has been deleted" msgstr "Se ha eliminado 1 caso" msgid "A Link to your application made outside the portal, e.g. with Bonita UI Builder." msgstr "Un enlace a su aplicación hecha fuera del portal, por ejemplo, con Bonita UI Builder." msgid "A classic Bonita Living Application." msgstr "Una Bonita Living Application clásica." msgid "A page with this name already exists." msgstr "Ya existe una página con este nombre." msgid "A server error occurred. The operation did not complete successfully" msgstr "Se ha producido un error en el servidor. La operación no se completó correctamente" msgid "Above normal" msgstr "Por encima de la normal" msgid "Access denied. For more information, check the log file." msgstr "Acceso denegado. Para obtener más información, consulte el archivo de registro." msgid "Actions" msgstr "acciones" msgid "Activation state" msgstr "Estado de activación" msgid "Actor name" msgstr "Nombre del actor" msgid "Actors" msgstr "Actores" msgid "Actors must be resolved before enabling the Process." msgstr "Los actores deben resolverse antes de activar el proceso." msgid "Add" msgstr "Añadir" msgid "Add from custom pages" msgstr "Agregar desde páginas personalizadas" msgid "Add membership" msgstr "Añadir una membresía" msgid "Add menu" msgstr "Agregar menú" msgid "Add menu item" msgstr "Agregar elemento de menú" msgid "Add page" msgstr "Añade página" msgid "Add pages in Navigation" msgstr "Agregar páginas en Navegación" msgid "Add top-level menu" msgstr "Agregar menú superior" msgid "Adding on apply" msgstr "Aplicar para agregar" msgid "Address" msgstr "Dirección" msgid "All" msgstr "Todos" msgid "All done. Good job!" msgstr "Todo hecho. ¡Buen trabajo!" msgid "An Error occurred during process activation/deactivation." msgstr "Se ha producido un error durante la activación/desactivación del proceso." msgid "An Error occurred during process deletion." msgstr "Se ha producido un error durante la eliminación del proceso." msgid "An application is a customized environment for a specific user profile, in which users interact with business data and business processes in the most efficient way." msgstr "Una aplicación es un entorno personalizado para un perfil de usuario específico, en el cual los usuarios interactúan con Datos de Negocio y procesos de negocio de la manera más eficiente." msgid "An error has occurred. For more information, check the log file." msgstr "Ha ocurrido un error. Para más información, compruebe el archivo de registro." msgid "An error occurred during categories update" msgstr "Se produjo un error durante la actualización de las categorías" msgid "An error occurred when deploying the BDM. Consult the logs for more information." msgstr "Se ha producido un error al desplegar el BDM. Para más información, consulte los registros." msgid "An error occurred while submitting the form." msgstr "Ocurrió un error al enviar el formulario." msgid "An error occurred while submitting the form.
      The task may not be available anymore." msgstr "Error al enviar el formulario.
      Puede ser que la tarea ya no esta disponible." msgid "An error occurred with the requested operation." msgstr "Ha ocurrido un error con la operación solicitada" msgid "Application Link" msgstr "Application Link" msgid "Application XML file" msgstr "Archivo de la aplicación XML" msgid "Application id not provided. Unable to retrieve the application details." msgstr "Id de la aplicación no proporcionado. Imposible recuperar los detalles de la aplicación." msgid "Application list" msgstr "Lista de aplicaciones" msgid "Apply" msgstr "Aplicar" msgid "Archived cases" msgstr "Casos archivados" msgid "Are you sure you want to delete it?" msgstr "¿Seguro que deseas eliminarlo?" msgid "Are you sure you want to delete this membership ?" msgstr "¿Está seguro que desea eliminar esta membresía?" msgid "Assign to me. Only I will be able to do it" msgstr "Asignarme a mí. Sólo yo podré hacerlo" msgid "Assigned on" msgstr "Asignado el" msgid "Available or My tasks" msgstr "Disponible o en Mis tareas" msgid "BDM access control file can now be installed." msgstr "Ahora se puede instalar el archivo de control de acceso BDM." msgid "BDM file" msgstr "Archivo BDM" msgid "BDM has not been uploaded. System was not able to upload the provided BDM file for technical reasons. You can try again later." msgstr "El BDM no ha sido actualizado. El sistema no pudo validar el archivo de BDM proporcionado por razones técnicas. Puede intentarlo más tarde." msgid "BDM successfully updated." msgstr "El BDM ha sido actualizado." msgid "Back" msgstr "Atrás" msgid "Before updating the BDM, access control file should be deleted." msgstr "Antes de actualizar el BDM, el archivo de control de acceso debe ser suprimido." msgid "Before you import the application descriptor, the pages and the profile must already be loaded in the Portal." msgstr "Antes de importar el descriptor de aplicación, las páginas y el perfil ya deben estar cargados en el Portal." msgid "Business Data Model definition" msgstr "Definición del modelo de datos de negocio (BDM)" msgid "Business card" msgstr "Tarjeta de visita" msgid "Business card has not been updated. Please retry later or contact an administrator" msgstr "La Tarjeta de visita no ha sido actualizada. Por favor, vuelva a intentarlo más tarde o póngase en contacto con un administrador" msgid "Business card successfully updated" msgstr "Tarjeta de visita actualizada con éxito" msgid "Can't import page or form to the process" msgstr "No se puede importar una página o formulario al proceso" msgid "Can't update expression content" msgstr "No se puede actualizar el contenido de la expresión" msgid "Cancel" msgstr "Cancelar" msgid "Cancelled, skipped instances" msgstr "Instancias canceladas, omitidas" msgid "Cannot upload the file" msgstr "No se puede cargar el archivo" msgid "Case" msgstr "Caso" msgid "Case ID" msgstr "ID del Caso" msgid "Case comments" msgstr "Comentarios del Caso" msgid "Case id: {{caseId}} - Process: {{processName}} ({{processVersion}})" msgstr "Caso: {{caseId}} - Proceso: {{processName}} ({{processVersion}})" msgid "Case overview" msgstr "Resumen de caso" msgid "Case start" msgstr "Caso inicio" msgid "Cases with failures" msgstr "Casos con fallos" msgid "Categories" msgstr "Categorías" msgid "Check that each entity is mapped to a profile, for either Bonita Portal or for applications you have created." msgstr "Verifique que cada entidad está mapeada a un perfil, ya sea para el Bonita Portal o para las aplicaciones que ha creado." msgid "City" msgstr "Ciudad" msgid "Class name" msgstr "Nombre de la clase" msgid "Click here to choose the .xml file" msgstr "Haga clic para seleccionar el archivo .xml." msgid "Click here to choose your .zip file" msgstr "Haga clic aquí para seleccionar su archivo .zip" msgid "Click on Cancel to remain on this page and try to execute the operation again once you logged back in (e.g. in another tab)." msgstr "Haz click en Cancelar para permanecer en esta página e intentar de nuevo la operación cuando su sesión sera de nuevo iniciada (p.ej. en otra pestaña)." msgid "Click on Cancel to remain on this page and wait for the maintenance to end." msgstr "Haz click en Cancelar para permanecer en esta página y esperar a que finalice el mantenimiento." msgid "Click on OK to be redirected and log back in." msgstr "Haz click en Aceptar para volver a iniciar sesión." msgid "Click on OK to be redirected to the maintenance page." msgstr "Haz click en Aceptar para ser redirigido." msgid "Close" msgstr "Cerrar" msgid "Columns selection" msgstr "Selección de columnas" msgid "Comment:" msgstr "Comentario:" msgid "Comments" msgstr "Comentarios" msgid "Compilation failure. Verify that the Index.groovy class implements the PageController interface." msgstr "Error de compilación. Verificar que la clase Index.groovy implementa la interfaz PageController." msgid "Completed instances" msgstr "Instancias completadas" msgid "Configuration state" msgstr "Estado de la configuración" msgid "Confirm new Password" msgstr "Confirmar nueva Contraseña" msgid "Connectors" msgstr "Conectores" msgid "Connectors must be resolved before enabling the Process." msgstr "Los conectores deben resolverse antes de activar el proceso." msgid "Core to Bonita platform" msgstr "Core para la plataforma Bonita" msgid "Core to Bonita platform; cannot be deleted" msgstr "Core para la plataforma Bonita, no puede borrarse" msgid "Country" msgstr "País" msgid "Create a new membership" msgstr "Crear una nueva membresía" msgid "Create an application" msgstr "Crear una aplicación" msgid "Created by" msgstr "Creado por" msgid "Creation" msgstr "Creación" msgid "Creation date" msgstr "Fecha de creación" msgid "Creation on" msgstr "Creado el" msgid "Custom information" msgstr "Información personalizada" msgid "Custom information has not been updated. Please retry later or contact an administrator" msgstr "No se ha actualizado la información personalizada. Por favor, vuelva a intentarlo más tarde o póngase en contacto con un administrador" msgid "Custom information successfully updated" msgstr "Información personalizada actualizada correctamente" msgid "Delete" msgstr "Eliminar" msgid "Delete application" msgstr "Eliminar aplicación" msgid "Delete membership" msgstr "Eliminar membresía" msgid "Delete process" msgstr "Eliminar proceso" msgid "Deleting a process definition will also delete all open and archived cases of this process." msgstr "Eliminar una definición de proceso también eliminará todos los casos abiertos y archivados de este proceso." msgid "Description" msgstr "Descripción" msgid "Disable" msgstr "Desactivar" msgid "Display name" msgstr "Título dinámico" msgid "Done tasks" msgstr "Tareas realizadas" msgid "Drawing process..." msgstr "Dibujando proceso..." msgid "Due date" msgstr "Fecha de vencimiento" msgid "Edit" msgstr "Editar" msgid "Edit menu" msgstr "Editar menú" msgid "Edit menu item" msgstr "Editar elemento de menú" msgid "Email" msgstr "Correo Electrónico" msgid "Enable" msgstr "Activar" msgid "Enable all" msgstr "Activar todo" msgid "Enter a new category name and click \"Add\" button to create a new category." msgstr "Introduzca un nuevo nombre de categoría y haga clic en el botón \"Añadir\" para crear una nueva categoría." msgid "Enter the user to start the case for..." msgstr "Introduzca el usuario para el cual iniciar el caso..." msgid "Error" msgstr "Error" msgid "Error on drawing process!" msgstr "¡Error al dibujar el proceso!" msgid "Error while trying to start the case. Some required information is missing (contract not fulfilled)." msgstr "Error al intentar iniciar un caso. Falta información obligatoria (contrato incompleto)." msgid "Error!" msgstr "¡Error!" msgid "Error: value must be a boolean" msgstr "Error: el valor debe ser booleano" msgid "Error: value must be a double" msgstr "Error: el valor debe ser un doble" msgid "Error: value must be an integer" msgstr "Error: el valor debe ser un entero" msgid "Executing, completing, initializing instances" msgstr "Instancias in curso, completadas, inicializadas" msgid "Export" msgstr "Exportar" msgid "Export application descriptor" msgstr "Exportar el descriptor de aplicación" msgid "Expression content has been updated" msgstr "Se ha actualizado el contenido de la expresión" msgid "Failed instances" msgstr "Instancias fallidas" msgid "File format may be invalid. For more information, check the log file." msgstr "El formato de archivo puede ser inválido. Para más información, compruebe el archivo de log." msgid "Filename too long. The zip filename must be no longer than 50 characters" msgstr "Nombre de archivo demasiado largo. El nombre del archivo zip debe ser no más de 50 caracteres" msgid "Filters" msgstr "Filtros" msgid "First name" msgstr "Nombre" msgid "Form" msgstr "Formulario" msgid "Form submitted.
      The next task in the list is now selected." msgstr "Formulario enviado.
      La siguiente tarea en la lista está ahora seleccionada." msgid "General" msgstr "General" msgid "General information" msgstr "Información General" msgid "General information has not been updated. Please retry later or contact an administrator" msgstr "La Información general no ha sido actualizada. Por favor, vuelva a intentarlo más tarde o póngase en contacto con un administrador" msgid "General information successfully updated" msgstr "Información general actualizada correctamente" msgid "Groups" msgstr "Grupos" msgid "Healthy cases" msgstr "Casos sin fallos" msgid "Hide Menu" msgstr "Ocultar menú" msgid "Hide menu" msgstr "Ocultar menú" msgid "Hide panel" msgstr "Ocultar panel" msgid "Highest" msgstr "Mayor" msgid "However, due to the use of Hibernate for data persistence, some changes on the data may not be well handled. These changes may lead to a corrupted database and CANNOT BE REVERTED." msgstr "Sin embargo, debido al uso de Hibernate para la persistencia de datos, es posible que algunos cambios en los datos no estén bien manejados. Estos cambios pueden llevar a una base de datos corrupta y NO SE PUEDE REVERTIR." msgid "I have read and understood the previous warning message. I have prepared a database backup so I can rollback this operation." msgstr "He leído y entendido el mensaje de advertencia anterior. He preparado una copia de seguridad de la base de datos para poder deshacer esta operación." msgid "IN ANY CASE, you MUST CREATE A BACKUP of the current BDM database before undergoing a BDM update." msgstr "EN CUALQUIER CASO, DEBES CREAR UN BACKUP de la base de datos BDM actual antes de realizar una actualización BDM." msgid "If your changes do not fall into those change types, you can go ahead and proceed with the BDM update now." msgstr "Si sus cambios no están dentro de este tipo de cambios puede seguir adelante y proceder ahora con la actualización de BDM." msgid "Implementation version" msgstr "Versión de implementación" msgid "Import" msgstr "Importar" msgid "Import an application" msgstr "Importar una aplicación" msgid "Import an application descriptor" msgstr "Importar un descriptor de aplicación" msgid "In a production environment, this should be used with caution." msgstr "En un entorno de producción, esto debería ser utilizado con precaución." msgid "In such cases, you must CANCEL THIS UPDATE and let the database administrator carefully script the changes and apply them to the database. Only then will you be able to update the BDM from this page." msgstr "En tales casos, debe CANCELAR ESTA ACTUALIZACIÓN y permitir al administrador de bases de datos escribir cuidadosamente los cambios y aplicarlos a la base de datos. Sólo entonces podrá actualizar el BDM desde esta página." msgid "In task name column" msgstr "En la columna Nombre de tarea" msgid "In the menu Organization > Manage, go to the Users page of the wizard" msgstr "En el menú Organización > Administrar, ir a la página Usuarios del asistente" msgid "Install" msgstr "Instalar" msgid "Install Business Data Model" msgstr "Instalar el modelo de datos de negocio" msgid "Install a BDM file" msgstr "Instalar un archivo BDM" msgid "Installed" msgstr "Instalado" msgid "Installed by" msgstr "Instalado por" msgid "Installed on" msgstr "Instalado el" msgid "Installing" msgstr "Instalando…" msgid "Installing Business Data Model" msgstr "Instalando el modelo de datos de negocio" msgid "Installing or updating the BDM implies to be logged in as Technical User, and to activate the maintenance mode." msgstr "Instalar o actualizar la BDM requiere una conexión como Usuario Técnico y haber activado el modo de mantenimiento." msgid "Items per page" msgstr "Elementos por página" msgid "Job title" msgstr "Profesión" msgid "Last login" msgstr "Última sesión" msgid "Last name" msgstr "Apellidos" msgid "Last update" msgstr "Última actualización" msgid "Layout" msgstr "Layout" msgid "Legacy Application" msgstr "Legacy Application" msgid "Loading..." msgstr "Cargando…" msgid "Logo" msgstr "Logo" msgid "Look & Feel" msgstr "Apariencia" msgid "Lowest" msgstr "Menor" msgid "MM/dd/yyyy h:mm a" msgstr "dd/MM/yyyy HH:mm" msgid "MMM DD LT" msgstr "DD MMM LT" msgid "Make sure all artifacts enabled on the platform are compatible with the new BDM." msgstr "Asegúrese de que todos los artefactos instalados en la plataforma son compatibles con el nuevo BDM." msgid "Make sure to deactivate the maintenance mode." msgstr "Asegúrese de desactivar el modo de mantenimiento." msgid "Make this task available to other team members again" msgstr "Liberar - hacer esta tarea disponible de nuevo para los otros miembros del equipo" msgid "Make this task personal: only I will be able to do it" msgstr "Hacer esta tarea personal: sólo yo podré realizarla" msgid "Manage Categories" msgstr "Gestionar categorías" msgid "Manager" msgstr "Responsable" msgid "Mapped categories" msgstr "Categorías asociadas" msgid "Maximum size 255 characters" msgstr "Máximo 250 caracteres" msgid "Membership has not been added. Please retry later or contact an administrator" msgstr "La Membresía no ha sido añadida. Por favor, vuelva a intentarlo más tarde o póngase en contacto con un administrador" msgid "Membership has not been removed. Please retry later or contact an administrator" msgstr "La Membresía no ha sido eliminada. Por favor, vuelva a intentarlo más tarde o póngase en contacto con un administrador" msgid "Membership successfully added" msgstr "Membresía agregada con éxito" msgid "Membership successfully removed" msgstr "La membresía fue eliminada con éxito" msgid "Memberships" msgstr "Membresías" msgid "Memberships mapped to {}" msgstr "Membresias asignadas a {}" msgid "Mobile" msgstr "Móvil" msgid "Modifying or redeploying the Business Data Model may impact existing data retention rules. After redeployment, review your configuration in the Data Retention (GDPR) section." msgstr "" msgid "Monitoring status" msgstr "Monitorización" msgid "Multi-page menu" msgstr "Menú multi-página" msgid "My tasks" msgstr "Mis tareas" msgid "Name" msgstr "Nombre" msgid "Navigation" msgstr "Navegación" msgid "New" msgstr "Nuevo" msgid "New Password" msgstr "Nueva contraseña" msgid "New comment" msgstr "Nuevo comentario" msgid "No application available." msgstr "Ninguna aplicación disponible." msgid "No application found with id:" msgstr "No se encontró ninguna aplicación con id:" msgid "No comment on this case yet. To add one, use the input field at the bottom of this panel" msgstr "Ningún comentario sobre este caso todavía. Para agregar uno, utilice el campo de entrada en la parte inferior de este panel" msgid "No custom information defined." msgstr "No se ha definido información personalizada." msgid "No data" msgstr "No hay datos" msgid "No description." msgstr "No hay descripción." msgid "No done task yet." msgstr "Sin tareas realizadas." msgid "No form is needed. You can enter a comment and confirm." msgstr "No se necesita ningún formulario. Puede introducir un comentario y confirmar." msgid "No mapping" msgstr "Ningún mapeo" msgid "No process found with id:" msgstr "No se encontró ningún proceso con id:" msgid "No profile" msgstr "Sin perfil" msgid "No profile mapped to this application" msgstr "Sin perfil asignado a esta aplicación" msgid "No updates have been made on categories. For more details, check logs." msgstr "Ninguna actualización ha sido efectuada sobre las categorías. Para más información, consulte los registros (logs)." msgid "No user found with id:" msgstr "Ningún usuario encontrado con id:" msgid "Normal" msgstr "Normal" msgid "Not installed" msgstr "No instalado" msgid "OK" msgstr "Aceptar" msgid "One or more tasks are not available anymore. The list is now up to date." msgstr "Una o más tareas ya no están disponibles. La lista ahora está actualizada." msgid "One-page menu" msgstr "Menú de una página" msgid "Only I can do this task" msgstr "Sólo yo puedo hacer esta tarea" msgid "Only available when the platform is under maintenance. Go to the \"Platform maintenance\" page to activate the maintenance mode." msgstr "Únicamente disponible cuando la plataforma está en mantenimiento. Vaya a la página \"Mantenimiento de la plataforma\" para activar el modo de mantenimiento." msgid "Only one Business Data Model (BDM) can be active at a time." msgstr "Sólo un Modelo de Datos de Negocio (BDM) puede estar activo al mismo tiempo." msgid "Open cases" msgstr "Casos abiertos" msgid "Open in a popup" msgstr "Abrir en ventana emergente" msgid "Open the task to perform it" msgstr "Abra la tarea para realizarla" msgid "Overview" msgstr "Vista global" msgid "Page" msgstr "Página" msgid "Pages" msgstr "Páginas" msgid "Parameters" msgstr "Parámetros" msgid "Parameters must be resolved before enabling the Process." msgstr "Los parámetros deben resolverse antes de activar el proceso." msgid "Password" msgstr "Contraseña" msgid "Password has not been updated. Please retry later or contact an administrator" msgstr "La Contraseña no ha sido actualizada. Por favor, vuelva a intentarlo más tarde o póngase en contacto con un administrador" msgid "Password successfully updated" msgstr "Contraseña modificada correctamente" msgid "Passwords don't match" msgstr "Las contraseñas no coinciden" msgid "Personal information" msgstr "Información personal" msgid "Personal information has not been updated. Please retry later or contact an administrator" msgstr "La Información personal no ha sido actualizada. Por favor, vuelva a intentarlo más tarde o póngase en contacto con un administrador" msgid "Personal information successfully updated" msgstr "Información personal actualizada correctamente" msgid "Phone" msgstr "Teléfono" msgid "Please refresh the page." msgstr "Por favor, actualiza la página." msgid "Priority" msgstr "Prioridad" msgid "Process" msgstr "Proceso" msgid "Process id" msgstr "Id de proceso" msgid "Process id not provided. Unable to retrieve the process details." msgstr "Id de proceso no proporcionado. Imposible recuperar los detalles del proceso." msgid "Process name" msgstr "Nombre de Proceso" msgid "Process:" msgstr "Proceso:" msgid "Profile" msgstr "Perfil" msgid "Profiles" msgstr "Perfiles" msgid "Profiles / Memberships" msgstr "Perfiles / Membresías" msgid "Ready, waiting instances" msgstr "Instancias listas, en espera" msgid "Refresh data" msgstr "Actualizar datos" msgid "Release" msgstr "Liberar" msgid "Remove" msgstr "Suprimir" msgid "Remove all" msgstr "Eliminar todo" msgid "Removing on apply" msgstr "Aplicar para eliminar" msgid "Reset" msgstr "Reiniciar" msgid "Reset to default" msgstr "Restablecer al valor predeterminado" msgid "Roles" msgstr "Roles" msgid "Save" msgstr "Guardar" msgid "Search..." msgstr "Búsqueda..." msgid "Select a group..." msgstr "Seleccionar un grupo..." msgid "Select a role..." msgstr "Seleccionar un rol..." msgid "Select all" msgstr "Seleccionar todo" msgid "Select groups..." msgstr "Seleccionar los grupos..." msgid "Select none" msgstr "No seleccionar nada" msgid "Select roles..." msgstr "Seleccionar roles..." msgid "Select the User information management tab to add Custom information on the right" msgstr "Seleccione la pestaña Gestión de la información de usuario para agregar Información personalizada a la derecha" msgid "Select the entities (users, groups, roles, memberships) to map to the actors. These entities will do the human tasks in the process." msgstr "Seleccione las entidades (usuarios, grupos, roles, membresías) para asignar a los actores. Estas entidades harán las tareas humanas en el proceso." msgid "Select the page" msgstr "Seleccione la página" msgid "Select users..." msgstr "Seleccionar usuarios..." msgid "Server is under maintenance." msgstr "El servidor está en mantenimiento." msgid "Set as Home page" msgstr "Establecer como página de inicio" msgid "Show Menu" msgstr "Mostrar el menú" msgid "Show all the tasks I can do: both personal tasks and tasks available to other team members" msgstr "Mostrar todas las tareas que puedo hacer: tanto personales como disponibles para otros miembros del equipo" msgid "Show menu" msgstr "Mostrar menú" msgid "Show my personal tasks: the ones I took and the ones assigned to me, automatically or by a process manager" msgstr "Mostrar mis tareas personales: las tomadas y las asignadas a mí, automáticamente o por un gestor de procesos" msgid "Show panel" msgstr "Mostrar panel" msgid "Show the tasks that I have done" msgstr "Mostrar las tareas que he realizado" msgid "Something went wrong during the deletion. You might want to cancel and try again." msgstr "Algo salió mal durante la eliminación. Es posible que quiera cancelar y volver a intentarlo." msgid "Something went wrong during the modification. You might want to cancel and try again." msgstr "Algo salió mal durante la modificación. Es posible que quieras cancelar e intentarlo de nuevo." msgid "Something went wrong. You might want to cancel and try again." msgstr "Algo salió mal. Puede que quieras cancelar e intentarlo de nuevo." msgid "Sort by URL" msgstr "Ordenar por URL" msgid "Sort by name" msgstr "Ordenar por nombre" msgid "Sort by type" msgstr "Ordenar por tipo" msgid "Sort by updated on" msgstr "Ordenar por actualizada el" msgid "Sort by version" msgstr "Ordenar por versión" msgid "Specify the menu structure." msgstr "Especificar la estructura del menú." msgid "State" msgstr "Estado" msgid "Status" msgstr "Estado" msgid "Status of importation" msgstr "Estado de la importación" msgid "Still, you can customize its look & feel on this page. You can also update some of its pages in the Resources menu." msgstr "Aún así, puedes personalizar su apariencia en esta página. También puedes actualizar algunas de sus páginas en el menú de Recursos." msgid "Submit" msgstr "Enviar" msgid "Successfully updated categories" msgstr "Categorías actualizadas correctamente" msgid "Table settings" msgstr "Configuración de la tabla" msgid "Take" msgstr "Tomar" msgid "Task Id" msgstr "Id de tarea" msgid "Task form" msgstr "Formulario de tarea" msgid "Task list" msgstr "Lista de tareas" msgid "Task name" msgstr "Nombre de tarea" msgid "Technical User" msgstr "Usuario técnico" msgid "Technical error." msgstr "Error técnico." msgid "The BDM schema and data have been rolled-back to what they were before this update attempt." msgstr "El esquema y los datos de BDM han sido revertidos al estado anterior de este intento de actualización." msgid "The application" msgstr "La aplicación" msgid "The application does not exist. Go to application list page to see the new list of applications." msgstr "La aplicación no existe. Vaya a la página de lista de aplicaciones para ver la nueva lista de aplicaciones." msgid "The application does not exist. Reload the page to see the new list of applications." msgstr "La aplicación no existe. Recarga la página para ver la nueva lista de aplicaciones." msgid "The application page does not exist. Reload the page to see the new list of application pages." msgstr "La página de la aplicación no existe. Recarga la página para ver la nueva lista de páginas de la aplicación." msgid "The attachment is too big.
      Select a smaller attachment and submit the form again." msgstr "El archivo adjunto es demasiado grande.
      Seleccione un archivo más pequeño y envíe el formulario de nuevo." msgid "The business data: [ {} ] uses Business Objects which are not defined in the current Business Data model. Deploy a compatible Business Data model before enabling the process." msgstr "El Dato de Negocio: [ {} ] utiliza Objetos de Negocio que no están definidos en el Modelo de Datos de Negocio actual. Despliegue un Modelo de Datos de Negocio compatible antes de activar el proceso." msgid "The case is archived. You cannot add comments anymore" msgstr "El caso está archivado. Ya no se pueden agregar comentarios" msgid "The case {{caseId}} has been started successfully." msgstr "El caso {{caseId}} se ha iniciado con éxito." msgid "The category cannot be removed and added at the same time." msgstr "La categoría no puede ser eliminada y añadida al mismo tiempo." msgid "The current process has no connectors defined." msgstr "El proceso actual no tiene ningún conector definido" msgid "The current process has no parameters defined." msgstr "El proceso actual no tiene parámetros definidos." msgid "The file you are trying to upload has the wrong mime type" msgstr "El archivo que estás intentando subir tiene un mime type incorrecto" msgid "The form mappings must be resolved before enabling the Process." msgstr "El mapeo de formularios debe resolverse antes de activar el proceso." msgid "The image you are trying to upload is too big" msgstr "La imagen que estás intentando subir es demasiado grande" msgid "The item does not exist anymore. Refresh the page to see the new status" msgstr "El elemento ya no existe. Actualice la página para ver el nuevo estado" msgid "The menu does not exist. Reload the page to see the new list of menus." msgstr "El menú no existe. Recarga la página para ver la nueva lista de menús." msgid "The name for the URL must start with custompage_ followed only by alphanumeric characters." msgstr "El nombre de la URL debe comenzar con custompage_ seguido de sólo caracteres alfanuméricos." msgid "The task has been submitted but an error occurred while adding the comment" msgstr "La tarea ha sido enviada pero ocurrió un error al agregar el comentario" msgid "The whole list of such changes is listed here." msgstr "La lista completa de estos cambios está disponible aquí." msgid "The words 'content', 'API' and 'theme' are reserved for internal use. They must not be used in an application or page URL." msgstr "Las palabras 'content', 'API' y 'theme' están reservadas para uso interno. No debe utilizarse en la URL de una página o en una aplicación." msgid "Theme" msgstr "Tema" msgid "Then for each user, fill out the custom information value, in the Studio (List of users tab of the organization), or right on this Portal page." msgstr "Entonces, para cada usuario, complete el valor de la información personalizada, en el Studio (pestaña Organización Lista de usuarios), o en esta página del Portal." msgid "They will be removed from the database permanently." msgstr "Serán eliminados de la base de datos permanentemente." msgid "This URL is already in use" msgstr "Esta URL ya está en uso" msgid "This application is core to the Bonita platform. To keep it working as it should, it must remain available and its menu should stay as it is." msgstr "Esta aplicación es core para la plataforma Bonita. Para que siga funcionando como debería, debe permanecer disponible y su menú debe permanecer como está." msgid "This category has already been added to this process." msgstr "Esta categoría ya ha sido añadida a este proceso." msgid "This field is mandatory" msgstr "Este campo es obligatorio" msgid "This is the task details panel. No information to display" msgstr "Este es el panel de detalles de la tarea. No hay información para mostrar" msgid "This name is already in use" msgstr "Este nombre ya está en uso" msgid "This only changes the application link. The application itself is still deployed at the same location." msgstr "Esto sólo cambia el enlace de la aplicación. La aplicación misma está desplegada en la misma ubicación." msgid "This only deletes the application link. The application itself is not deleted and is still deployed." msgstr "Esto sólo elimina el enlace de la aplicación. La aplicación en sí misma no se elimina y todavía está desplegada." msgid "This process is no longer available." msgstr "Este proceso ya no está disponible." msgid "This process is not fully configured, but it is still enabled. Users may still be able to start cases or do tasks, which may lead to errors." msgstr "Este proceso no está completamente configurado, pero todavía está habilitado. Los usuarios todavía pueden iniciar casos o realizar tareas, lo que puede llevar a errores." msgid "This process is not fully configured." msgstr "Este proceso no está completamente configurado." msgid "This process is not fully configured. It cannot be enabled." msgstr "Este proceso no está completamente configurado. No se puede activar." msgid "This task cannot be executed from here." msgstr "Esta tarea no se puede ejecutar desde aquí." msgid "This task is completed." msgstr "Esta tarea se ha completado." msgid "This task is not available anymore. The list is now up to date." msgstr "Esta tarea ya no está disponible. La lista ahora está actualizada." msgid "This task is overdue. It was supposed to be completed by {{dueDate}}" msgstr "Esta tarea ha vencido. Debía ser completada el {{dueDate}}" msgid "Title" msgstr "Título" msgid "To create an application, click New or Import." msgstr "Para crear una aplicación, haga clic en nuevo o importar." msgid "To do" msgstr "Por hacer" msgid "To do so, go to" msgstr "Para ello, vaya a" msgid "To do so, log out and then log back in as the Technical User, whose credentials are set upon Bonita platform installation." msgstr "Para ello, cierre la sesión y luego inicie sesión con el Usuario Técnico, cuyas credenciales se han establecido durante la instalación de la plataforma de Bonita." msgid "To fill out the form, you need to take this task. This means you will be the only one able to do it. To make it available to the team again, release it." msgstr "Para rellenar el formulario, es necesario tomar esta tarea. Esto significa que serás el único que podrá realizarla. Para que esté disponible de nuevo para el equipo, libérala." msgid "To use this feature, create a new version of the process in Bonita Studio version 6.4 and above, then create and install the new .bar file in the Portal." msgstr "Para utilizar esta función, cree una nueva versión del proceso en Bonita Studio versión 6.4 o superior, posteriormente cree e instale el nuevo archivo .bar en el Portal." msgid "Type" msgstr "Tipo" msgid "Type here to search for a user..." msgstr "Escriba aquí para buscar un usuario..." msgid "Type here to search..." msgstr "Escriba aquí para buscar..." msgid "Type in a case id and
      press enter to search for
      human task of a specific case" msgstr "Escriba el id del caso y
      pulse intro para buscar tareas
      humanas de un caso en concreto" msgid "URL" msgstr "URL" msgid "Unable to add the comment. Refresh your task list and try again. If the problem persists, contact your administrator" msgstr "No se puede agregar el comentario. Actualiza la lista de tareas e inténtalo de nuevo. Si el problema persiste, ponte en contacto con tu administrador" msgid "Unable to map category:" msgstr "No se puede mapear la categoría:" msgid "Unassign. This will make it available to team again" msgstr "Desasignar. Esto la hará disponible para el equipo de nuevo" msgid "Under normal" msgstr "Bajo normal" msgid "Update" msgstr "Actualizar" msgid "Update Business Data Model" msgstr "Actualizar el Modelo de Datos de Negocio" msgid "Update business card" msgstr "Actualizar tarjeta de visita" msgid "Update custom information" msgstr "Actualizar la información personalizada" msgid "Update general information" msgstr "Actualizar la información general" msgid "Update password" msgstr "Actualizar contraseña" msgid "Update personal information" msgstr "Actualizar la información personal" msgid "Updated by" msgstr "Actualizado por" msgid "Updated on" msgstr "Actualizada" msgid "Updating the Business Data Model applies changes to both existing database tables and data." msgstr "Actualizar el Modelo de Datos de Negocio aplica cambios tanto a las tablas de base de datos existentes como a los datos." msgid "Upload new picture" msgstr "Subir nueva imagen" msgid "Use Arrow up and Arrow down keys to browse among existing categories." msgstr "Use la tecla de flecha arriba y flecha abajo para navegar entre las categorías existentes." msgid "User id not provided. Unable to retrieve the user details." msgstr "Id de usuario no proporcionado. No se pueden recuperar los detalles del usuario." msgid "Username" msgstr "Nombre de usuario" msgid "Users" msgstr "Usuarios" msgid "Users mapped to {}" msgstr "Usuarios asignados a {}" msgid "Value" msgstr "Valor" msgid "Version" msgstr "Version" msgid "View application details" msgstr "Ver detalles de la aplicación" msgid "View task" msgstr "Ver tarea" msgid "View this task" msgstr "Ver esta tarea" msgid "Warning:" msgstr "Advertencia:" msgid "We couldn't find what you are looking for." msgstr "No pudimos encontrar lo que estás buscando." msgid "When you export an application descriptor, pages and profiles are not exported in the file." msgstr "Al exportar un descriptor de aplicación, las páginas y perfiles no se exportan en el archivo." msgid "You are about to export the descriptor of the application" msgstr "Está a punto de exportar el descriptor de la aplicación" msgid "You are going to be redirected to the process list." msgstr "Va a ser redirigido a la lista de procesos." msgid "You can create custom information in Bonita Studio:" msgstr "Puede crear información personalizada en el Studio de Bonita:" msgid "You must add a page before you can reference it in a menu." msgstr "Debe agregar una página antes de que poder hacer referencia en un menú." msgid "Your personal task list is empty. You can take a task from the ​To do list." msgstr "Tu lista personal de tareas está vacía. Puedes realizar una tarea de la lista Por hacer." msgid "Your session is no longer active." msgstr "Su sesión ya no es válida o ha expirado." msgid "Zip code" msgstr "Código postal" msgid "Zip file structure error. Check that your .zip contains a well-formed page.properties and either the index.html or the Index.groovy file. For details, see the documentation or the example page readme (available in the custom page list)" msgstr "Error de estructura del archivo Zip. Verifica que el archivo .zip contiene un page.properties bien formado así como el index.html o el archivo Index.groovy. Para obtener más información, consulta la documentación o el ejemplo en la página readme (disponible en la lista de páginas personalizadas)" msgid "an Application" msgstr "una Aplicación" msgid "an application" msgstr "una Aplicación" msgid "application descriptor has been partially imported. The following items are not loaded in the Portal." msgstr "el descriptor de aplicación ha sido parcialmente importado. Los siguientes elementos no se han cargado en el Portal." msgid "application descriptor has been successfully imported, with complete page mapping and menu mapping." msgstr "el descriptor de aplicación ha sido importado con éxito, con un completo mapeo de páginas y de menú." msgid "here" msgstr "aquí" msgid "of" msgstr "de" msgid "will be permanently deleted." msgstr "se eliminará permanentemente." msgid "{{firstname}} {{lastname}} already has the membership {{role}} of {{group}}" msgstr "{{firstname}} {{lastname}} ya tiene la membresía {{role}} en {{group}}" msgid "{{membership.role_id.displayName}} of {{membership.group_id.displayName}}" msgstr "{{membership.role_id.displayName}} de {{membership.group_id.displayName}}" msgid "{{nbErrors}} errors on mapping updates" msgstr "{{nbErrors}} errores en la actualización de mapeos" msgid "{{nbMapping}} Process manager mappings could not be updated" msgstr "El mapeo del Gestor de Procesos {{nbMapping}} no puede ser modificado" msgid "{{nbMapping}} Process manager mappings have been updated" msgstr "El mapeo del Gestor de Procesos {{nbMapping}} ha sido modificado" msgid "{{nbOfDeletedCases}} cases have been deleted" msgstr "{{nbOfDeletedCases}} casos han sido eliminados" msgid "{{nbSucess}} actor mapping updates succeeded" msgstr "{{nbSucess}} actualizaciones de mapeo de actor tuvieron éxito" msgid "{{role}} of {{group}}" msgstr "{{role}} de {{group}}" msgid "{{role}} of {{group}} has been created" msgstr "{{role}} de {{group}} ha sido creado" msgid "{} mapped" msgstr "{} mapeado" msgid "{} mappings to delete" msgstr "{} mapeos para eliminar" msgid "{} more" msgstr "{} más" msgid "{} of {}" msgstr "{} de {}" ================================================ FILE: bpm/bonita-web-server/src/main/resources/i18n/portal-js_fr.po ================================================ msgid "" msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Project-Id-Version: bonita\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Crowdin-Project: bonita\n" "X-Crowdin-Project-ID: 13316\n" "X-Crowdin-Language: fr\n" "X-Crowdin-File: /dev/bonita-web/portal/portal-js.pot\n" "X-Crowdin-File-ID: 60594\n" "Language-Team: French\n" "Language: fr_FR\n" "PO-Revision-Date: 2024-01-01 00:00\n" msgid "1 case has been deleted" msgstr "1 cas a été supprimé" msgid "A Link to your application made outside the portal, e.g. with Bonita UI Builder." msgstr "Un Lien vers votre application créée en dehors du portail, par exemple avec Bonita UI Builder." msgid "A classic Bonita Living Application." msgstr "Une Living Application Bonita traditionnelle." msgid "A page with this name already exists." msgstr "Une page portant ce nom existe déjà." msgid "A server error occurred. The operation did not complete successfully" msgstr "Une erreur de serveur s'est produite. L'opération n'a pas été terminée avec succès" msgid "Above normal" msgstr "Haute priorité" msgid "Access denied. For more information, check the log file." msgstr "Accès refusé. Pour plus d'informations, consultez les logs." msgid "Actions" msgstr "Actions" msgid "Activation state" msgstr "État d'activation" msgid "Actor name" msgstr "Nom de l'acteur" msgid "Actors" msgstr "Acteurs" msgid "Actors must be resolved before enabling the Process." msgstr "Tous les acteurs doivent être associés à des entités de l'organisation pour que le processus puisse être activé." msgid "Add" msgstr "Ajouter" msgid "Add from custom pages" msgstr "Ajouter une page parmi les pages personalisées" msgid "Add membership" msgstr "Ajouter une adhésion" msgid "Add menu" msgstr "Ajouter un menu" msgid "Add menu item" msgstr "Ajouter une option de menu" msgid "Add page" msgstr "Ajouter une page" msgid "Add pages in Navigation" msgstr "Ajouter des pages à la navigation" msgid "Add top-level menu" msgstr "Ajouter un menu " msgid "Adding on apply" msgstr "Appliquer pour ajouter" msgid "Address" msgstr "Adresse" msgid "All" msgstr "Tous" msgid "All done. Good job!" msgstr "Vous avez accompli toutes vos tâches. Bravo !" msgid "An Error occurred during process activation/deactivation." msgstr "Une erreur s'est produite lors de l'activation/désactivation du processus." msgid "An Error occurred during process deletion." msgstr "Une erreur s'est produite durant la suppression du processus. " msgid "An application is a customized environment for a specific user profile, in which users interact with business data and business processes in the most efficient way." msgstr "Une application est un environnement adapté à un profil particulier, dans lequel les utilisateurs interagissent avec les données et les processus métier avec une efficacité optimale." msgid "An error has occurred. For more information, check the log file." msgstr "Une erreur s'est produite. Pour plus d'informations, consultez les logs." msgid "An error occurred during categories update" msgstr "Une erreur s'est produite à la mise à jour des catégories" msgid "An error occurred when deploying the BDM. Consult the logs for more information." msgstr "Une erreur s’est produite lors du déploiement du Modèle de Données Métier (ou BDM).\n" "Pour plus d’informations, consultez les logs." msgid "An error occurred while submitting the form." msgstr "Une erreur s'est produite lors de la soumission du formulaire." msgid "An error occurred while submitting the form.
      The task may not be available anymore." msgstr "Une erreur s’est produite lors de l’envoi du formulaire.
      Il se peut que la tâche ne soit plus disponible." msgid "An error occurred with the requested operation." msgstr "Une erreur s'est produite lors de l'opération demandée" msgid "Application Link" msgstr "Lien d'Application" msgid "Application XML file" msgstr "Fichier XML de l'application" msgid "Application id not provided. Unable to retrieve the application details." msgstr "L'identifiant de l'application n'a pas été fourni. Impossible de récupérer les informations." msgid "Application list" msgstr "Liste des applications" msgid "Apply" msgstr "Appliquer" msgid "Archived cases" msgstr "Cas archivés" msgid "Are you sure you want to delete it?" msgstr "Êtes-vous sûr(e) de vouloir le supprimer ?" msgid "Are you sure you want to delete this membership ?" msgstr "Êtes-vous sûr(e) de vouloir supprimer cette adhésion ?" msgid "Assign to me. Only I will be able to do it" msgstr "Me l'assigner. Je serai le seul en mesure de la faire" msgid "Assigned on" msgstr "Assignée le" msgid "Available or My tasks" msgstr "Disponible ou dans Mes tâches" msgid "BDM access control file can now be installed." msgstr "Le fichier de contrôle d’accès aux données métier peut maintenant être installé." msgid "BDM file" msgstr "Fichier BDM" msgid "BDM has not been uploaded. System was not able to upload the provided BDM file for technical reasons. You can try again later." msgstr "Le Business Data Model n’a pas été mis à jour. Le système n’était pas en mesure de télécharger le fichier fourni pour des raisons techniques. Vous pouvez réessayer plus tard." msgid "BDM successfully updated." msgstr "Le Modèle de Données Métier a été mis à jour avec succès." msgid "Back" msgstr "Retour" msgid "Before updating the BDM, access control file should be deleted." msgstr "Avant de mettre à jour le BDM, le fichier de contrôle d’accès doit être supprimé." msgid "Before you import the application descriptor, the pages and the profile must already be loaded in the Portal." msgstr "Avant d'importer un descripteur d'application, déployez dans le portail les pages utilisées et le profil concerné par l'application." msgid "Business Data Model definition" msgstr "Définition du Modèle de Données Métier (BDM)" msgid "Business card" msgstr "Carte de visite" msgid "Business card has not been updated. Please retry later or contact an administrator" msgstr "La carte de visite n’a pas été mise à jour. Veuillez réessayer ultérieurement ou contacter un administrateur" msgid "Business card successfully updated" msgstr "La carte de visite a été mise à jour avec succès" msgid "Can't import page or form to the process" msgstr "Impossible d'importer la page ou le formulaire dans le processus" msgid "Can't update expression content" msgstr "Impossible de mettre à jour le contenu de l'expression" msgid "Cancel" msgstr "Annuler" msgid "Cancelled, skipped instances" msgstr "Instances en état cancelled ou skipped (passées)" msgid "Cannot upload the file" msgstr "Impossible de charger le fichier" msgid "Case" msgstr "Cas" msgid "Case ID" msgstr "Cas" msgid "Case comments" msgstr "Commentaires du cas" msgid "Case id: {{caseId}} - Process: {{processName}} ({{processVersion}})" msgstr "Cas : {{caseId}} - Processus : {{processName}} ({{processVersion}})" msgid "Case overview" msgstr "Page de synthèse du cas" msgid "Case start" msgstr "Démarrage d'un cas" msgid "Cases with failures" msgstr "Cas en erreur" msgid "Categories" msgstr "Catégories" msgid "Check that each entity is mapped to a profile, for either Bonita Portal or for applications you have created." msgstr "Vérifiez que chaque entité est mappée à un profil, pour le Bonita Portal ou pour les applications que vous avez créées." msgid "City" msgstr "Ville" msgid "Class name" msgstr "Nom de la classe" msgid "Click here to choose the .xml file" msgstr "Cliquez ici pour choisir le fichier .xml" msgid "Click here to choose your .zip file" msgstr "Cliquez ici pour choisir le fichier .zip" msgid "Click on Cancel to remain on this page and try to execute the operation again once you logged back in (e.g. in another tab)." msgstr "Cliquez sur Annuler pour rester sur cette page et essayer d'exécuter à nouveau l'opération après vous être reconnecté (e.g. dans un autre onglet)." msgid "Click on Cancel to remain on this page and wait for the maintenance to end." msgstr "Cliquez sur Annuler pour rester sur cette page et attendre la fin des opérations de maintenance." msgid "Click on OK to be redirected and log back in." msgstr "Cliquez sur OK pour être redirigé et vous reconnecter." msgid "Click on OK to be redirected to the maintenance page." msgstr "Cliquez sur OK pour être redirigé vers la page de maintenance." msgid "Close" msgstr "Fermer" msgid "Columns selection" msgstr "Sélection des colonnes" msgid "Comment:" msgstr "Commentaire :" msgid "Comments" msgstr "Commentaires" msgid "Compilation failure. Verify that the Index.groovy class implements the PageController interface." msgstr "Échec de compilation. Vérifiez que le fichier Index.groovy implémente l'interface PageController." msgid "Completed instances" msgstr "Instances en état completed (faites)" msgid "Configuration state" msgstr "Etat de la configuration" msgid "Confirm new Password" msgstr "Confirmez le nouveau mot de passe" msgid "Connectors" msgstr "Connecteurs" msgid "Connectors must be resolved before enabling the Process." msgstr "Tous les connecteurs doivent avoir une implémentation pour que le processus puisse être activé." msgid "Core to Bonita platform" msgstr "Core pour la plateforme Bonita" msgid "Core to Bonita platform; cannot be deleted" msgstr "Core pour la plateforme Bonita; ne peut pas être supprimé" msgid "Country" msgstr "Pays" msgid "Create a new membership" msgstr "Créer une nouvelle adhésion" msgid "Create an application" msgstr "Créer une application" msgid "Created by" msgstr "Créé par" msgid "Creation" msgstr "Création" msgid "Creation date" msgstr "Créé le" msgid "Creation on" msgstr "Créée le" msgid "Custom information" msgstr "Informations spécifiques" msgid "Custom information has not been updated. Please retry later or contact an administrator" msgstr "Les informations spécifiques n’ont pas été mises à jour. Veuillez réessayer ultérieurement ou contacter un administrateur" msgid "Custom information successfully updated" msgstr "Les informations spécifiques ont été mises à jour avec succès" msgid "Delete" msgstr "Supprimer" msgid "Delete application" msgstr "Supprimer l'application" msgid "Delete membership" msgstr "Supprimer l'adhésion" msgid "Delete process" msgstr "Supprimer un processus" msgid "Deleting a process definition will also delete all open and archived cases of this process." msgstr "La suppression d'une définition de processus supprimera également tous les cas, en cours et archivés, de ce processus." msgid "Description" msgstr "Description" msgid "Disable" msgstr "Désactiver" msgid "Display name" msgstr "Nom métier" msgid "Done tasks" msgstr "Tâches terminées" msgid "Drawing process..." msgstr "Dessin du processus ..." msgid "Due date" msgstr "Echéance" msgid "Edit" msgstr "Modifier" msgid "Edit menu" msgstr "Editer l'option de menu" msgid "Edit menu item" msgstr "Editer l'option de menu" msgid "Email" msgstr "Courriel" msgid "Enable" msgstr "Activer" msgid "Enable all" msgstr "Tout associer" msgid "Enter a new category name and click \"Add\" button to create a new category." msgstr "Entrez un nouveau nom de catégorie et cliquez sur le bouton \"Ajouter\" pour créer une nouvelle catégorie." msgid "Enter the user to start the case for..." msgstr "Saisissez le nom de l'utilisateur pour qui vous allez démarrer le cas..." msgid "Error" msgstr "Erreur" msgid "Error on drawing process!" msgstr "Erreur lors du dessin de processus !" msgid "Error while trying to start the case. Some required information is missing (contract not fulfilled)." msgstr "Erreur lors de l'exécution la tâche. Certaines informations requises sont manquantes (contrat non respecté)." msgid "Error!" msgstr "Erreur!" msgid "Error: value must be a boolean" msgstr "Erreur : la valeur doit être un booléen" msgid "Error: value must be a double" msgstr "Erreur : valeur doit être un double" msgid "Error: value must be an integer" msgstr "Erreur : la valeur doit être un entier" msgid "Executing, completing, initializing instances" msgstr "Instances en état executing, completing, initializing" msgid "Export" msgstr "Exporter" msgid "Export application descriptor" msgstr "Exporter le descripteur d’application" msgid "Expression content has been updated" msgstr "Le contenu de l'expression a été mis à jour" msgid "Failed instances" msgstr "Instances en état failed (en échec)" msgid "File format may be invalid. For more information, check the log file." msgstr "Le format du fichier est peut-être invalide. Pour plus d'informations, consultez les logs." msgid "Filename too long. The zip filename must be no longer than 50 characters" msgstr "Le nom du fichier .zip est trop long. Il ne doit pas dépasser 50 caractères" msgid "Filters" msgstr "Filtres" msgid "First name" msgstr "Prénom" msgid "Form" msgstr "Formulaire" msgid "Form submitted.
      The next task in the list is now selected." msgstr "Formulaire soumis.
      La tâche suivante dans la liste est maintenant sélectionnée." msgid "General" msgstr "Général" msgid "General information" msgstr "Informations générales" msgid "General information has not been updated. Please retry later or contact an administrator" msgstr "Les informations générales n’ont pas été mises à jour. Veuillez réessayer ultérieurement ou contacter un administrateur" msgid "General information successfully updated" msgstr "Les informations générales ont été mises à jour avec succès" msgid "Groups" msgstr "Groupes" msgid "Healthy cases" msgstr "Cas sans erreur" msgid "Hide Menu" msgstr "Masquer le Menu" msgid "Hide menu" msgstr "Masquer le menu" msgid "Hide panel" msgstr "Masquer le panneau" msgid "Highest" msgstr "Plus haute" msgid "However, due to the use of Hibernate for data persistence, some changes on the data may not be well handled. These changes may lead to a corrupted database and CANNOT BE REVERTED." msgstr "Cependant, en raison de l'utilisation de Hibernate pour la persistance des données, certains changements sur les données peuvent ne pas être bien gérés. Ces changements peuvent conduire à une base de données corrompue et NE PEUT PAS ÊTRE REVERTES." msgid "I have read and understood the previous warning message. I have prepared a database backup so I can rollback this operation." msgstr "J'ai lu et compris le message d'avertissement précédent, j'ai préparé une sauvegarde de la base de données pour pouvoir annuler cette opération." msgid "IN ANY CASE, you MUST CREATE A BACKUP of the current BDM database before undergoing a BDM update." msgstr "DANS TOUS LES CAS, vous DEVEZ CRÉER UNE BACKUP de la base de données BDM actuelle avant de subir une mise à jour BDM." msgid "If your changes do not fall into those change types, you can go ahead and proceed with the BDM update now." msgstr "Si vos modifications ne tombent pas dans ces types de modifications, vous pouvez continuer avec la mise à jour du BDM maintenant." msgid "Implementation version" msgstr "Version de l'implémentation" msgid "Import" msgstr "Importer" msgid "Import an application" msgstr "Importer une application" msgid "Import an application descriptor" msgstr "Importer un descripteur d’application" msgid "In a production environment, this should be used with caution." msgstr "Cette action est à mener avec beaucoup de précaution dans un environnement de production." msgid "In such cases, you must CANCEL THIS UPDATE and let the database administrator carefully script the changes and apply them to the database. Only then will you be able to update the BDM from this page." msgstr "Dans de tels cas, vous devez ANNULER CETTE MISE À JOUR et laisser l'administrateur de la base de données script soigneusement les modifications et les appliquer à la base de données. Ce n'est qu'alors que vous pourrez mettre à jour le BDM à partir de cette page." msgid "In task name column" msgstr "Dans la colonne \"Tâche\"" msgid "In the menu Organization > Manage, go to the Users page of the wizard" msgstr "Dans le menu Organisation > Gérer, allez à la page Utilisateurs de l’assistant graphique" msgid "Install" msgstr "Installer" msgid "Install Business Data Model" msgstr "Installer le Modèle de Données Métier (BDM)" msgid "Install a BDM file" msgstr "Installer un fichier BDM" msgid "Installed" msgstr "Installé" msgid "Installed by" msgstr "Installé par" msgid "Installed on" msgstr "Installé le" msgid "Installing" msgstr "En cours d'installation" msgid "Installing Business Data Model" msgstr "Installation du Modèle de Données Métier (BDM)" msgid "Installing or updating the BDM implies to be logged in as Technical User, and to activate the maintenance mode." msgstr "Installer ou mettre à jour le BDM nécessite d'être authentifié en tant qu'Utilisateur Technique et d'avoir activé le mode maintenance." msgid "Items per page" msgstr "Eléments par page" msgid "Job title" msgstr "Intitulé du poste" msgid "Last login" msgstr "Dernière connexion" msgid "Last name" msgstr "Nom" msgid "Last update" msgstr "Dernière mise à jour" msgid "Layout" msgstr "Layout" msgid "Legacy Application" msgstr "Application Traditionnelle" msgid "Loading..." msgstr "Chargement..." msgid "Logo" msgstr "Logo" msgid "Look & Feel" msgstr "Look & Feel" msgid "Lowest" msgstr "Plus basse" msgid "MM/dd/yyyy h:mm a" msgstr "dd/MM/yyyy HH:mm" msgid "MMM DD LT" msgstr "DD MMM LT" msgid "Make sure all artifacts enabled on the platform are compatible with the new BDM." msgstr "Assurez-vous que tous les artefacts activés sur la plate-forme sont compatibles avec le nouveau BDM." msgid "Make sure to deactivate the maintenance mode." msgstr "Assurez-vous de désactiver le mode maintenance." msgid "Make this task available to other team members again" msgstr "Libérer - rendre cette tâche à nouveau disponible pour les autres membres de l’équipe" msgid "Make this task personal: only I will be able to do it" msgstr "Rendre cette tâche personnelle : je serai le(la) seul(e) à pouvoir la faire" msgid "Manage Categories" msgstr "Gérer les catégories" msgid "Manager" msgstr "Responsable" msgid "Mapped categories" msgstr "Catégories associées" msgid "Maximum size 255 characters" msgstr "Taille maximum de 255 caractères" msgid "Membership has not been added. Please retry later or contact an administrator" msgstr "L'adhésion n’a pas été mise à jour. Veuillez réessayer ultérieurement ou contacter un administrateur" msgid "Membership has not been removed. Please retry later or contact an administrator" msgstr "L'adhésion n'a pas été supprimée. Veuillez réessayer ultérieurement ou contacter un administrateur" msgid "Membership successfully added" msgstr "L'adhésion a été ajoutée avec succès" msgid "Membership successfully removed" msgstr "L'adhésion a été supprimée avec succès" msgid "Memberships" msgstr "Adhésions" msgid "Memberships mapped to {}" msgstr "Adhésions associées à {}" msgid "Mobile" msgstr "Portable" msgid "Modifying or redeploying the Business Data Model may impact existing data retention rules. After redeployment, review your configuration in the Data Retention (GDPR) section." msgstr "La modification ou le redéploiement du Modèle de Données Métier peuvent avoir une incidence sur les règles de rétention des données existantes. Après le redéploiement, vérifiez votre configuration dans la section Rétention des Données (RGPD)." msgid "Monitoring status" msgstr "Monitoring" msgid "Multi-page menu" msgstr "Menu" msgid "My tasks" msgstr "Mes tâches" msgid "Name" msgstr "Nom" msgid "Navigation" msgstr "Navigation" msgid "New" msgstr "Nouveau" msgid "New Password" msgstr "Nouveau mot de passe" msgid "New comment" msgstr "Nouveau commentaire" msgid "No application available." msgstr "Aucune application définie." msgid "No application found with id:" msgstr "Aucune application ne semble correspondre à l'identifiant :" msgid "No comment on this case yet. To add one, use the input field at the bottom of this panel" msgstr "Aucun commentaire sur ce cas pour l'instant. Le champ de saisie se trouve en bas de ce panneau" msgid "No custom information defined." msgstr "Aucune information personnalisée n'a été définie." msgid "No data" msgstr "Pas de donnée disponible" msgid "No description." msgstr "Aucune description disponible." msgid "No done task yet." msgstr "Aucune tâche accomplie pour l'instant." msgid "No form is needed. You can enter a comment and confirm." msgstr "Aucun formulaire à remplir. Vous pouvez saisir un commentaire et confirmer." msgid "No mapping" msgstr "Aucun mapping" msgid "No process found with id:" msgstr "Aucun processus ne semble correspondre à l'identifiant :" msgid "No profile" msgstr "Pas de profil" msgid "No profile mapped to this application" msgstr "Cette application n'a pas de profil associé" msgid "No updates have been made on categories. For more details, check logs." msgstr "Aucune mise à jour n'a été faite sur les catégories. Pour plus de détails, consultez les logs." msgid "No user found with id:" msgstr "Aucun utilisateur n'a été trouvé avec l'id :" msgid "Normal" msgstr "Normale" msgid "Not installed" msgstr "Non installé" msgid "OK" msgstr "OK" msgid "One or more tasks are not available anymore. The list is now up to date." msgstr "Une ou plusieurs tâches ne sont plus disponibles. La liste est maintenant à jour." msgid "One-page menu" msgstr "Lien vers une page" msgid "Only I can do this task" msgstr "Je suis le seul en mesure de faire cette tâche" msgid "Only available when the platform is under maintenance. Go to the \"Platform maintenance\" page to activate the maintenance mode." msgstr "Uniquement disponible lorsque la plateforme est en maintenance. Allez sur la page \"Maintenance de la plate-forme\" pour activer le mode maintenance." msgid "Only one Business Data Model (BDM) can be active at a time." msgstr "Il ne peut y avoir qu'un seul Modèle de Données Métier (BDM) actif à la fois." msgid "Open cases" msgstr "Cas démarrés" msgid "Open in a popup" msgstr "Ouvrir dans une pop-up" msgid "Open the task to perform it" msgstr "Ouvrez la tâche pour l'exécuter" msgid "Overview" msgstr "Synthèse" msgid "Page" msgstr "Page" msgid "Pages" msgstr "Pages" msgid "Parameters" msgstr "Paramètres" msgid "Parameters must be resolved before enabling the Process." msgstr "Tous les paramètres doivent avoir une valeur pour pouvoir activer le processus." msgid "Password" msgstr "Mot de passe" msgid "Password has not been updated. Please retry later or contact an administrator" msgstr "Le mot de passe n’a pas été mis à jour. Veuillez réessayer ultérieurement ou contacter un administrateur" msgid "Password successfully updated" msgstr "Le mot de passe a été mis à jour avec succès" msgid "Passwords don't match" msgstr "Les mots de passe ne correspondent pas" msgid "Personal information" msgstr "Informations personnelles" msgid "Personal information has not been updated. Please retry later or contact an administrator" msgstr "Les informations personnelles n’ont pas été mises à jour. Veuillez réessayer ultérieurement ou contacter un administrateur" msgid "Personal information successfully updated" msgstr "Les informations personnelles ont été mises à jour avec succès" msgid "Phone" msgstr "Téléphone" msgid "Please refresh the page." msgstr "Veuillez actualiser la page." msgid "Priority" msgstr "Priorité" msgid "Process" msgstr "Processus" msgid "Process id" msgstr "Id du processus" msgid "Process id not provided. Unable to retrieve the process details." msgstr "L'identifiant du processus n'a pas été fourni. Impossible de récupérer les informations." msgid "Process name" msgstr "Nom du processus" msgid "Process:" msgstr "Processus :" msgid "Profile" msgstr "Profils" msgid "Profiles" msgstr "Profils" msgid "Profiles / Memberships" msgstr "Profils / Adhésions" msgid "Ready, waiting instances" msgstr "Instances en état ready, waiting (en attente)" msgid "Refresh data" msgstr "Actualiser les données" msgid "Release" msgstr "Libérer" msgid "Remove" msgstr "Supprimer" msgid "Remove all" msgstr "Tout supprimer" msgid "Removing on apply" msgstr "Appliquer pour supprimer" msgid "Reset" msgstr "Réinitialiser" msgid "Reset to default" msgstr "Réinitialiser avec les paramètres par défaut" msgid "Roles" msgstr "Rôles" msgid "Save" msgstr "Enregistrer" msgid "Search..." msgstr "Chercher..." msgid "Select a group..." msgstr "Sélectionnez un groupe ..." msgid "Select a role..." msgstr "Sélectionnez un rôle ..." msgid "Select all" msgstr "Tout sélectionner " msgid "Select groups..." msgstr "Sélectionnez les groupes..." msgid "Select none" msgstr "Tout désélectionner" msgid "Select roles..." msgstr "Sélectionnez des rôles ..." msgid "Select the User information management tab to add Custom information on the right" msgstr "Sélectionnez l’onglet Gestion des informations utilisateur pour ajouter des informations spécifiques sur la droite" msgid "Select the entities (users, groups, roles, memberships) to map to the actors. These entities will do the human tasks in the process." msgstr "Sélectionnez les entités (utilisateurs, groupes, rôles, adhésions) à associer aux acteurs. Ces entités exécuteront les tâches \"humaines\" du processus." msgid "Select the page" msgstr "Sélectionnez la page" msgid "Select users..." msgstr "Sélectionnez des utilisateurs ..." msgid "Server is under maintenance." msgstr "Une maintenance est en cours sur le serveur." msgid "Set as Home page" msgstr "Définir en tant que page d'accueil" msgid "Show Menu" msgstr "Afficher le Menu" msgid "Show all the tasks I can do: both personal tasks and tasks available to other team members" msgstr "Afficher toutes les tâches que je peux faire : mes tâches personnelles et les tâches disponibles pour moi et les autres membres de l’équipe" msgid "Show menu" msgstr "Afficher le menu" msgid "Show my personal tasks: the ones I took and the ones assigned to me, automatically or by a process manager" msgstr "Afficher mes tâches personnelles : celles que j’ai prises et celles qui m'ont été assignées, automatiquement ou par un gestionnaire de processus" msgid "Show panel" msgstr "Afficher le panneau" msgid "Show the tasks that I have done" msgstr "Afficher les tâches que j’ai faites" msgid "Something went wrong during the deletion. You might want to cancel and try again." msgstr "Une erreur s'est produite lors de la suppression. Vous pouvez annuler et réessayer." msgid "Something went wrong during the modification. You might want to cancel and try again." msgstr "Une erreur s'est produite lors de la modification. Vous pouvez annuler et réessayer." msgid "Something went wrong. You might want to cancel and try again." msgstr "Une erreur s'est produite lors de la suppression. Vous pouvez annuler et réessayer." msgid "Sort by URL" msgstr "Trier par URL" msgid "Sort by name" msgstr "Trier par nom" msgid "Sort by type" msgstr "Trier par type" msgid "Sort by updated on" msgstr "Trier par date de mise à jour" msgid "Sort by version" msgstr "Trier par version" msgid "Specify the menu structure." msgstr "Définir le menu de navigation." msgid "State" msgstr "Etat" msgid "Status" msgstr "Statut" msgid "Status of importation" msgstr "Statut de l'importation" msgid "Still, you can customize its look & feel on this page. You can also update some of its pages in the Resources menu." msgstr "Néanmoins, vous pouvez personnaliser son apparence sur cette page. Vous pouvez également mettre à jour certaines de ses pages dans le menu Ressources." msgid "Submit" msgstr "Soumettre" msgid "Successfully updated categories" msgstr "Les catégories ont été mises à jour avec succès" msgid "Table settings" msgstr "Paramètres du tableau" msgid "Take" msgstr "Prendre" msgid "Task Id" msgstr "ID de tâche" msgid "Task form" msgstr "Formulaire de tâche" msgid "Task list" msgstr "Liste de tâches" msgid "Task name" msgstr "Tâche" msgid "Technical User" msgstr "Utilisateur Technique" msgid "Technical error." msgstr "Erreur technique." msgid "The BDM schema and data have been rolled-back to what they were before this update attempt." msgstr "Le schéma et les données du BDM ont été annulés à ce qu'ils étaient avant cette tentative de mise à jour." msgid "The application" msgstr "L'application" msgid "The application does not exist. Go to application list page to see the new list of applications." msgstr "L'application n'existe pas. Allez à la page de la liste des applications pour voir la nouvelle liste d'applications." msgid "The application does not exist. Reload the page to see the new list of applications." msgstr "L'application n'existe pas. Rechargez la page pour voir la nouvelle liste d'applications." msgid "The application page does not exist. Reload the page to see the new list of application pages." msgstr "La page d'application n'existe pas. Rechargez la, pour voir la nouvelle liste des pages d'application." msgid "The attachment is too big.
      Select a smaller attachment and submit the form again." msgstr "Le fichier joint est trop volumineux.
      Sélectionnez un fichier plus petit et soumettez le formulaire à nouveau." msgid "The business data: [ {} ] uses Business Objects which are not defined in the current Business Data model. Deploy a compatible Business Data model before enabling the process." msgstr "Les données métier [ {} ] font référence à des objets qui ne sont pas définis dans le Modèle de Données Métier actuel. Déployez un Modèle de Données Métier compatible pour pouvoir activer le processus." msgid "The case is archived. You cannot add comments anymore" msgstr "Le cas est archivé. Il n'est plus possible d'ajouter de nouveaux commentaires" msgid "The case {{caseId}} has been started successfully." msgstr "Le cas {{caseId}} a été démarré avec succès." msgid "The category cannot be removed and added at the same time." msgstr "La catégorie ne peut pas être supprimée et ajoutée en même temps." msgid "The current process has no connectors defined." msgstr "Aucun connecteur n'a été défini pour ce processus." msgid "The current process has no parameters defined." msgstr "Aucun paramètre n'a été défini pour ce processus." msgid "The file you are trying to upload has the wrong mime type" msgstr "Le fichier que vous essayez de charger n'a pas le bon type MIME" msgid "The form mappings must be resolved before enabling the Process." msgstr "Le mapping de tous les éléments en attente d'un formulaire ou d'une page doit être effectif pour pouvoir activer le processus." msgid "The image you are trying to upload is too big" msgstr "L'image que vous essayez de charger est trop grande" msgid "The item does not exist anymore. Refresh the page to see the new status" msgstr "L' élément n'existe plus. Rafraîchissez la page pour voir le nouveau statut" msgid "The menu does not exist. Reload the page to see the new list of menus." msgstr "Le menu n'existe pas. Rechargez la page pour voir la nouvelle liste de menus." msgid "The name for the URL must start with custompage_ followed only by alphanumeric characters." msgstr "Le nom de l'URL doit commencer par custompage_ puis contenir uniquement des caractères alphanumériques." msgid "The task has been submitted but an error occurred while adding the comment" msgstr "La tâche a été soumise, mais une erreur s'est produite à l'ajout du commentaire" msgid "The whole list of such changes is listed here." msgstr "La liste complète de ces changements est listée ici." msgid "The words 'content', 'API' and 'theme' are reserved for internal use. They must not be used in an application or page URL." msgstr "Les mots 'content', 'API' et 'theme' sont réservés pour un usage interne. Ils ne doivent pas être utilisés dans l'URL d'une page ou d'une application." msgid "Theme" msgstr " Thème" msgid "Then for each user, fill out the custom information value, in the Studio (List of users tab of the organization), or right on this Portal page." msgstr "Puis pour chaque utilisateur, remplissez la valeur des informations spécifiques, soit dans le studio (onglet Liste des utilisateurs de l’organisation), ou directement sur cette page du portail." msgid "They will be removed from the database permanently." msgstr "Ils seront définitivement effacés de la base de données." msgid "This URL is already in use" msgstr "Cette URL est déjà utilisée" msgid "This application is core to the Bonita platform. To keep it working as it should, it must remain available and its menu should stay as it is." msgstr "Cette application est le cœur de la plate-forme Bonita. Pour qu'il fonctionne comme il le devrait, il doit rester disponible et son menu doit rester tel qu'il est." msgid "This category has already been added to this process." msgstr "Cette catégorie a déjà été ajoutée à ce processus." msgid "This field is mandatory" msgstr "Ce champ est obligatoire" msgid "This is the task details panel. No information to display" msgstr "Cette zone est dédiée aux détails d'une tâche. Aucune information à afficher pour l'instant" msgid "This name is already in use" msgstr "Ce nom est déjà utilisé" msgid "This only changes the application link. The application itself is still deployed at the same location." msgstr "Cela ne modifie que le lien de l'application. L'application elle-même est toujours déployée au même endroit." msgid "This only deletes the application link. The application itself is not deleted and is still deployed." msgstr "Ceci ne supprime que le lien de l'application. L'application elle-même n'est pas supprimée et est toujours déployée." msgid "This process is no longer available." msgstr "Ce processus n'est plus disponible." msgid "This process is not fully configured, but it is still enabled. Users may still be able to start cases or do tasks, which may lead to errors." msgstr "Ce processus n'est pas entièrement configuré, mais il est toujours activé. Les utilisateurs peuvent toujours démarrer des cas ou exécuter des tâches, ce qui pourrait entraîner des erreurs." msgid "This process is not fully configured." msgstr "Ce processus n'est pas entièrement configuré. " msgid "This process is not fully configured. It cannot be enabled." msgstr "Ce processus n'est pas entièrement configuré. Il ne peut pas être activé." msgid "This task cannot be executed from here." msgstr "Cette tâche ne peut pas être exécutée dans le Portal. Elle doit être exécutée par le système." msgid "This task is completed." msgstr "Cette tâche est terminée." msgid "This task is not available anymore. The list is now up to date." msgstr "Cette tâche n'est plus disponible. La liste est maintenant à jour." msgid "This task is overdue. It was supposed to be completed by {{dueDate}}" msgstr "Cette tâche est en retard. Elle aurait dû être faite pour le {{dueDate}}" msgid "Title" msgstr "Titre" msgid "To create an application, click New or Import." msgstr "Pour créer une application, cliquez sur Nouveau ou Importer." msgid "To do" msgstr "À faire" msgid "To do so, go to" msgstr "Pour cela, consultez la page" msgid "To do so, log out and then log back in as the Technical User, whose credentials are set upon Bonita platform installation." msgstr "Pour cela, déconnectez-vous puis reconnectez-vous en tant qu’Utilisateur Technique, dont les informations de connexion sont définies à l’installation de la plate-forme Bonita." msgid "To fill out the form, you need to take this task. This means you will be the only one able to do it. To make it available to the team again, release it." msgstr "Pour remplir le formulaire, vous devez \"prendre\" cette tâche. Cela signifie que vous serez le(la) seul(e) à pouvoir la faire. Pour la rendre à nouveau disponible à l'équipe, \"libérez\"-la." msgid "To use this feature, create a new version of the process in Bonita Studio version 6.4 and above, then create and install the new .bar file in the Portal." msgstr "Pour utiliser cette fonctionnalité, créez une nouvelle version du processus dans un Bonita Studio version 6.4 ou ultérieure, générez le fichier .bar puis déployez-le dans le portail." msgid "Type" msgstr "Type" msgid "Type here to search for a user..." msgstr "Saisissez le nom d'un utilisateur..." msgid "Type here to search..." msgstr "Rechercher..." msgid "Type in a case id and
      press enter to search for
      human task of a specific case" msgstr "Saisissez un Id de cas puis
      tapez entrée pour lister
      les tâches de ce cas" msgid "URL" msgstr "URL" msgid "Unable to add the comment. Refresh your task list and try again. If the problem persists, contact your administrator" msgstr "Impossible d'ajouter le commentaire. Actualisez votre liste de tâches, puis réessayez. Si le problème persiste, contactez votre administrateur" msgid "Unable to map category:" msgstr "Impossible d'associer la catégorie:" msgid "Unassign. This will make it available to team again" msgstr "Désassigner. Cela la rendra à nouveau disponible pour les membres de l'équipe" msgid "Under normal" msgstr "Basse priorité" msgid "Update" msgstr "Mise à jour" msgid "Update Business Data Model" msgstr "Mettre à jour le Modèle de Données Métier (BDM)" msgid "Update business card" msgstr "Mettre à jour la carte de visite" msgid "Update custom information" msgstr "Mettre à jour les informations spécifiques" msgid "Update general information" msgstr "Mettre à jour les informations générales" msgid "Update password" msgstr "Mettre à jour le mot de passe" msgid "Update personal information" msgstr "Mettre à jour les informations personnelles" msgid "Updated by" msgstr "Mise à jour par" msgid "Updated on" msgstr "Mise à jour le" msgid "Updating the Business Data Model applies changes to both existing database tables and data." msgstr "La mise à jour du Modèle de Données Métier s'applique à la fois aux tables et aux données de base de données existantes." msgid "Upload new picture" msgstr "Modifier l'image" msgid "Use Arrow up and Arrow down keys to browse among existing categories." msgstr "Utilisez les flèches haut et bas du clavier pour parcourir la liste des catégories existantes." msgid "User id not provided. Unable to retrieve the user details." msgstr "Identifiant utilisateur non fourni. Impossible de récupérer les détails de l'utilisateur." msgid "Username" msgstr "Nom d'utilisateur" msgid "Users" msgstr "Utilisateurs" msgid "Users mapped to {}" msgstr "Utilisateurs associés à {}" msgid "Value" msgstr "Valeur" msgid "Version" msgstr "Version" msgid "View application details" msgstr "Voir les détails de l'application" msgid "View task" msgstr "Voir la tâche" msgid "View this task" msgstr "Voir cette tâche" msgid "Warning:" msgstr "Attention :" msgid "We couldn't find what you are looking for." msgstr "Nous n'avons pas trouvé ce que vous recherchez." msgid "When you export an application descriptor, pages and profiles are not exported in the file." msgstr "Lorsque vous exportez un descripteur d'application, les pages et profil associés ne sont pas exportés dans le fichier." msgid "You are about to export the descriptor of the application" msgstr "Vous allez exporter le descripteur de l'application" msgid "You are going to be redirected to the process list." msgstr "Vous allez être redirigé vers la liste des processus." msgid "You can create custom information in Bonita Studio:" msgstr "Vous pouvez créer des informations spécifiques dans Bonita Studio :" msgid "You must add a page before you can reference it in a menu." msgstr "Vous devez ajouter une page avant de pouvoir la référencer dans un menu." msgid "Your personal task list is empty. You can take a task from the ​To do list." msgstr "Votre liste de tâches personnelles est vide. Vous pouvez prendre une tâche dans la liste A faire." msgid "Your session is no longer active." msgstr "Votre session n'est plus active." msgid "Zip code" msgstr "Code postal" msgid "Zip file structure error. Check that your .zip contains a well-formed page.properties and either the index.html or the Index.groovy file. For details, see the documentation or the example page readme (available in the custom page list)" msgstr "Erreur dans la structure du fichier .zip. Vérifiez que dans votre .zip, le fichier page.properties est bien formé et qu'un fichier index.html ou un fichier Index.groovy est présent. Pour plus d'information, consultez la documentation ou le fichier readme des pages d'exemple (disponibles dans la liste des pages personnalisées)" msgid "an Application" msgstr "une application" msgid "an application" msgstr "une application" msgid "application descriptor has been partially imported. The following items are not loaded in the Portal." msgstr "descripteur d'application a été partiellement importé. Les composants suivants n'ont pas été ajoutés dans le portail." msgid "application descriptor has been successfully imported, with complete page mapping and menu mapping." msgstr "descripteur d'application a été importé avec succès, avec son menu de navigation et les liens vers les pages utilisées." msgid "here" msgstr "ici" msgid "of" msgstr "de" msgid "will be permanently deleted." msgstr "va être supprimée définitivement" msgid "{{firstname}} {{lastname}} already has the membership {{role}} of {{group}}" msgstr "{{firstname}} {{lastname}} a déjà l'adhésion {{role}} de {{group}}" msgid "{{membership.role_id.displayName}} of {{membership.group_id.displayName}}" msgstr "{{membership.role_id.displayName}} de {{membership.group_id.displayName}}" msgid "{{nbErrors}} errors on mapping updates" msgstr "La mise à jour des mappings a généré {{nbErrors}} erreurs" msgid "{{nbMapping}} Process manager mappings could not be updated" msgstr "{{nbMapping}} mappings du profil Gestionnaire de processus n'ont pas pu être mis à jour" msgid "{{nbMapping}} Process manager mappings have been updated" msgstr "{{nbMapping}} mappings du profil Gestionnaire de processus ont été mis à jour" msgid "{{nbOfDeletedCases}} cases have been deleted" msgstr "{{nbOfDeletedCases}} cas ont été supprimés avec succès" msgid "{{nbSucess}} actor mapping updates succeeded" msgstr "{{nbSucess}} mappings d'acteurs ont été mis à jour avec succès" msgid "{{role}} of {{group}}" msgstr "{{role}} de {{group}}" msgid "{{role}} of {{group}} has been created" msgstr "{{role}} de {{group}} a été créée" msgid "{} mapped" msgstr "{} associés" msgid "{} mappings to delete" msgstr "{} associés à supprimer" msgid "{} more" msgstr "{} autres" msgid "{} of {}" msgstr "{} de {}" ================================================ FILE: bpm/bonita-web-server/src/main/resources/i18n/portal-js_ja.po ================================================ msgid "" msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Project-Id-Version: bonita\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Crowdin-Project: bonita\n" "X-Crowdin-Project-ID: 13316\n" "X-Crowdin-Language: ja\n" "X-Crowdin-File: /dev/bonita-web/portal/portal-js.pot\n" "X-Crowdin-File-ID: 60594\n" "Language-Team: Japanese\n" "Language: ja_JP\n" "PO-Revision-Date: 2024-01-01 00:00\n" msgid "1 case has been deleted" msgstr "1 案件が削除されました" msgid "A Link to your application made outside the portal, e.g. with Bonita UI Builder." msgstr "Bonita UI Builderなどのポータル外で作成されたアプリケーションへのリンク." msgid "A classic Bonita Living Application." msgstr "クラシックなBonitaリビングアプリケーション." msgid "A page with this name already exists." msgstr "この名前のページは既に存在します。" msgid "A server error occurred. The operation did not complete successfully" msgstr "サーバーエラーが発生しました。操作が正常に完了していません。" msgid "Above normal" msgstr "平均以上" msgid "Access denied. For more information, check the log file." msgstr "アクセスが拒否されました。 詳細についてはログファイルを確認してください。" msgid "Actions" msgstr "アクション" msgid "Activation state" msgstr "有効化の状態" msgid "Actor name" msgstr "アクター名" msgid "Actors" msgstr "アクター" msgid "Actors must be resolved before enabling the Process." msgstr "アクターは、プロセスを有効にする前に設定する必要があります。" msgid "Add" msgstr "追加" msgid "Add from custom pages" msgstr "カスタム ページを追加します" msgid "Add membership" msgstr "所属を追加" msgid "Add menu" msgstr "メニューを追加します" msgid "Add menu item" msgstr "メニュー項目を追加します" msgid "Add page" msgstr "ページを追加" msgid "Add pages in Navigation" msgstr "ナビゲーションにページを追加します" msgid "Add top-level menu" msgstr "最上位のメニューを追加します" msgid "Adding on apply" msgstr "適用時に追加" msgid "Address" msgstr "住所" msgid "All" msgstr "すべて" msgid "All done. Good job!" msgstr "実行すべきタスクがアサインされていません。" msgid "An Error occurred during process activation/deactivation." msgstr "プロセスの有効化/無効化中にエラーが発生しました。" msgid "An Error occurred during process deletion." msgstr "プロセスの削除中にエラーが発生しました。" msgid "An application is a customized environment for a specific user profile, in which users interact with business data and business processes in the most efficient way." msgstr "アプリケーションは、特定のユーザー プロファイル用にカスタマイズされた環境になり、利用者はビジネス データおよびビジネス プロセスを最も効率的な方法で利用できるようになります。" msgid "An error has occurred. For more information, check the log file." msgstr "エラーが発生しました。 詳細についてはログファイルを確認してください。" msgid "An error occurred during categories update" msgstr "カテゴリの更新中にエラーが発生しました" msgid "An error occurred when deploying the BDM. Consult the logs for more information." msgstr "BDMのデプロイ中にエラーが発生しました。\n" "詳細についてはログを参照してください。" msgid "An error occurred while submitting the form." msgstr "フォームの送信中にエラーが発生しました。" msgid "An error occurred while submitting the form.
      The task may not be available anymore." msgstr "フォームの送信中にエラーが発生しました。
      このタスクはもう利用できません。" msgid "An error occurred with the requested operation." msgstr "要求された操作でエラーが発生しました。" msgid "Application Link" msgstr "アプリケーションリンク" msgid "Application XML file" msgstr "アプリケーションのXMLファイル" msgid "Application id not provided. Unable to retrieve the application details." msgstr "アプリケーション ID が提供されていません。アプリケーションの詳細を取得できません。" msgid "Application list" msgstr "アプリケーション リスト" msgid "Apply" msgstr "適用" msgid "Archived cases" msgstr "完了済み案件" msgid "Are you sure you want to delete it?" msgstr "削除してよろしいですか?" msgid "Are you sure you want to delete this membership ?" msgstr "この所属を削除してもよろしいですか?" msgid "Assign to me. Only I will be able to do it" msgstr "自分にアサイン。それを実行できるのは、自分だけになります。" msgid "Assigned on" msgstr "アサイン日時" msgid "Available or My tasks" msgstr "このマークはあなただけに指名されたタスクを示します" msgid "BDM access control file can now be installed." msgstr "BDM のアクセス コントロール ファイルをインストールできるようになりました。" msgid "BDM file" msgstr "BDM ファイル" msgid "BDM has not been uploaded. System was not able to upload the provided BDM file for technical reasons. You can try again later." msgstr "BDMはアップロードされていません。\n" "技術的な理由によりシステムは指定されたBDMファイルをアップロードできませんでした。\n" "後ほど再度お試しください。" msgid "BDM successfully updated." msgstr "BDMは正常に更新されました。" msgid "Back" msgstr "戻る" msgid "Before updating the BDM, access control file should be deleted." msgstr "BDMを更新する前にアクセス コントロールファイルを削除する必要があります。" msgid "Before you import the application descriptor, the pages and the profile must already be loaded in the Portal." msgstr "アプリケーション記述子をインポートする前に、ページとプロファイルは、ポータル内に既に読み込まれている必要があります。" msgid "Business Data Model definition" msgstr "ビジネス データ モデル定義" msgid "Business card" msgstr "業務上の連絡先" msgid "Business card has not been updated. Please retry later or contact an administrator" msgstr "業務上の連絡先が、更新されませんでした。後で再試行するか管理者に問い合わせてください。 " msgid "Business card successfully updated" msgstr "業務上の連絡先が正常に更新されました。" msgid "Can't import page or form to the process" msgstr "プロセスにページまたはフォームをインポートできません" msgid "Can't update expression content" msgstr "式のコンテンツを更新できません" msgid "Cancel" msgstr "キャンセル" msgid "Cancelled, skipped instances" msgstr "キャンセル/スキップ" msgid "Cannot upload the file" msgstr "ファイルをアップロードできません" msgid "Case" msgstr "案件" msgid "Case ID" msgstr "案件ID" msgid "Case comments" msgstr "案件コメント" msgid "Case id: {{caseId}} - Process: {{processName}} ({{processVersion}})" msgstr "案件ID: {{caseId}} - プロセス: {{processName}} ({{processVersion}})" msgid "Case overview" msgstr "案件概況" msgid "Case start" msgstr "案件の開始" msgid "Cases with failures" msgstr "ケース エラー" msgid "Categories" msgstr "カテゴリ" msgid "Check that each entity is mapped to a profile, for either Bonita Portal or for applications you have created." msgstr "各エンティティが Bonita ポータル、またはあなたが作成したアプリケーションのいずれかのプロファイルにマップされていることを確認します。" msgid "City" msgstr "市区町村" msgid "Class name" msgstr "クラス名" msgid "Click here to choose the .xml file" msgstr ".xml ファイルを選択するにはここをクリック" msgid "Click here to choose your .zip file" msgstr ".zip ファイルを選択しクリックしてください。" msgid "Click on Cancel to remain on this page and try to execute the operation again once you logged back in (e.g. in another tab)." msgstr "「キャンセル」をクリックすると、このページが表示されたままとなり、ログインし直した後(別のタブなどで)、再度操作を実行することができます。" msgid "Click on Cancel to remain on this page and wait for the maintenance to end." msgstr "キャンセルをクリックしてこのページに留まり、メンテナンスが終了するのを待ちます。" msgid "Click on OK to be redirected and log back in." msgstr "OKをクリックするとリダイレクトされ、再度ログインします。" msgid "Click on OK to be redirected to the maintenance page." msgstr "OKをクリックすると、メンテナンス ページにリダイレクトされます。" msgid "Close" msgstr "閉じる" msgid "Columns selection" msgstr "列の選択" msgid "Comment:" msgstr "コメント:" msgid "Comments" msgstr "コメント" msgid "Compilation failure. Verify that the Index.groovy class implements the PageController interface." msgstr "コンパイルに失敗しました。Index.groovy クラスが PageController インターフェイスを実装することを確認します。" msgid "Completed instances" msgstr "実行完了" msgid "Configuration state" msgstr "構成設定の状態" msgid "Confirm new Password" msgstr "新しいパスワードを確認" msgid "Connectors" msgstr "コネクタ" msgid "Connectors must be resolved before enabling the Process." msgstr "コネクタは、プロセスを有効にする前に設定する必要があります。" msgid "Core to Bonita platform" msgstr "Bonita プラットフォームのコア" msgid "Core to Bonita platform; cannot be deleted" msgstr "Bonita プラットフォームのコア; 削除できません" msgid "Country" msgstr "国" msgid "Create a new membership" msgstr "新しいメンバーシップを作成します" msgid "Create an application" msgstr "アプリケーションを作成します" msgid "Created by" msgstr "作成者" msgid "Creation" msgstr "作成" msgid "Creation date" msgstr "作成日時" msgid "Creation on" msgstr "作成対象:" msgid "Custom information" msgstr "カスタム情報" msgid "Custom information has not been updated. Please retry later or contact an administrator" msgstr "カスタム情報が更新されませんでした。後で再試行するか管理者に問い合わせてください。" msgid "Custom information successfully updated" msgstr "カスタム情報が正常に更新されました" msgid "Delete" msgstr "削除" msgid "Delete application" msgstr "アプリケーションを削除" msgid "Delete membership" msgstr "所属を削除しますか?" msgid "Delete process" msgstr "プロセスを削除" msgid "Deleting a process definition will also delete all open and archived cases of this process." msgstr "プロセス定義を削除すると、このプロセスのオープンおよびアーカイブされたすべてのケースも削除されます。" msgid "Description" msgstr "説明" msgid "Disable" msgstr "無効にする" msgid "Display name" msgstr "表示名" msgid "Done tasks" msgstr "完了したタスク" msgid "Drawing process..." msgstr "プロセス図を描画中 ..." msgid "Due date" msgstr "期限" msgid "Edit" msgstr "編集" msgid "Edit menu" msgstr "メニューを編集します" msgid "Edit menu item" msgstr "メニュー項目を編集" msgid "Email" msgstr "Eメール" msgid "Enable" msgstr "有効にする" msgid "Enable all" msgstr "すべて有効にする" msgid "Enter a new category name and click \"Add\" button to create a new category." msgstr "新しいカテゴリ名を入力し、「追加」ボタンをクリックして新しいカテゴリを作成します。" msgid "Enter the user to start the case for..." msgstr "案件を開始するユーザーを入力..." msgid "Error" msgstr "エラー" msgid "Error on drawing process!" msgstr "プロセス図の描画中にエラーが発生しました!" msgid "Error while trying to start the case. Some required information is missing (contract not fulfilled)." msgstr "案件を開始しようとしてエラーが発生しました。一部の必須情報がありません (コントラクトのデータが満たされていない)。" msgid "Error!" msgstr "エラー!" msgid "Error: value must be a boolean" msgstr "エラー: 値はブール値である必要があります" msgid "Error: value must be a double" msgstr "エラー: 値は double 型の値である必要があります" msgid "Error: value must be an integer" msgstr "エラー: 値は整数でなければなりません" msgid "Executing, completing, initializing instances" msgstr "初期化中, 実行中, 完了途中" msgid "Export" msgstr "エクスポート" msgid "Export application descriptor" msgstr "アプリケーション記述子をエクスポート" msgid "Expression content has been updated" msgstr "式のコンテンツが更新されました" msgid "Failed instances" msgstr "エラーがあった" msgid "File format may be invalid. For more information, check the log file." msgstr "ファイル形式が無効である可能性があります。 詳細についてはログ ファイルを確認してください。" msgid "Filename too long. The zip filename must be no longer than 50 characters" msgstr "ファイル名が長すぎます。Zip ファイル名は 50 文字以内である必要があります。" msgid "Filters" msgstr "フィルター" msgid "First name" msgstr "姓(英語の場合:First name)" msgid "Form" msgstr "フォーム" msgid "Form submitted.
      The next task in the list is now selected." msgstr "フォームを送信しました。
      リスト中の次のタスクが選択されました。" msgid "General" msgstr "全般" msgid "General information" msgstr "全般的な情報" msgid "General information has not been updated. Please retry later or contact an administrator" msgstr "全般的な情報が更新されませんでした。後で再試行するかシステム管理者に問い合わせてください。" msgid "General information successfully updated" msgstr "全般的な情報が正常に更新されました" msgid "Groups" msgstr "グループ" msgid "Healthy cases" msgstr "正常なケース" msgid "Hide Menu" msgstr "メニューを非表示" msgid "Hide menu" msgstr "メニューを非表示" msgid "Hide panel" msgstr "パネルを非表示" msgid "Highest" msgstr "最高" msgid "However, due to the use of Hibernate for data persistence, some changes on the data may not be well handled. These changes may lead to a corrupted database and CANNOT BE REVERTED." msgstr "ただし、データの永続化にHibernateを使用するため、データの一部の変更はうまく処理されない可能性があります。 これらの変更により、データベースが破損する可能性があり、元に戻すことはできません。" msgid "I have read and understood the previous warning message. I have prepared a database backup so I can rollback this operation." msgstr "以前の警告メッセージを読んで理解しました。 この操作をロールバックできるように、データベースのバックアップを準備しました。" msgid "IN ANY CASE, you MUST CREATE A BACKUP of the current BDM database before undergoing a BDM update." msgstr "いずれの場合も、BDMの更新を行う前に現在のBDMデータベースのバックアップを作成する必要があります。" msgid "If your changes do not fall into those change types, you can go ahead and proceed with the BDM update now." msgstr "変更がこれらの変更タイプに該当しない場合は、先に進んみ今すぐBDMアップデートを継続することができます。" msgid "Implementation version" msgstr "実装のバージョン" msgid "Import" msgstr "インポート" msgid "Import an application" msgstr "アプリケーションをインポート" msgid "Import an application descriptor" msgstr "アプリケーション記述子をインポート" msgid "In a production environment, this should be used with caution." msgstr "本番環境では、これは慎重に使用する必要があります。" msgid "In such cases, you must CANCEL THIS UPDATE and let the database administrator carefully script the changes and apply them to the database. Only then will you be able to update the BDM from this page." msgstr "このような場合、このアップデートをキャンセルし、データベース管理者に注意深く変更をスクリプトして、それらをデータベースに適用させなければなりません。 その時のみ、このページからBDMを更新することができます。" msgid "In task name column" msgstr "[タスク名] 列" msgid "In the menu Organization > Manage, go to the Users page of the wizard" msgstr "メニューの 組織 > 管理で、 ユーザー のウィザード ページに進む" msgid "Install" msgstr "インストール" msgid "Install Business Data Model" msgstr "ビジネス データ モデルのインストール" msgid "Install a BDM file" msgstr "BDM ファイルのインストール" msgid "Installed" msgstr "インストール済み" msgid "Installed by" msgstr "インストール者 " msgid "Installed on" msgstr "インストール日時 " msgid "Installing" msgstr "インストール中 " msgid "Installing Business Data Model" msgstr "ビジネス データ モデルのインストール " msgid "Installing or updating the BDM implies to be logged in as Technical User, and to activate the maintenance mode." msgstr "BDM をインストールまたはアップデートするには、テクニカルユーザーとしてログインし、メンテナンスモードを有効にする必要があります。" msgid "Items per page" msgstr "ページあたりの表示件数" msgid "Job title" msgstr "役職名" msgid "Last login" msgstr "最終ログイン日時" msgid "Last name" msgstr "名(英語の場合:Last name)" msgid "Last update" msgstr "最新更新日時" msgid "Layout" msgstr "レイアウト" msgid "Legacy Application" msgstr "従来のアプリケーション" msgid "Loading..." msgstr "ロード中..." msgid "Logo" msgstr "ロゴ" msgid "Look & Feel" msgstr "ルック&フィール" msgid "Lowest" msgstr "最低" msgid "MM/dd/yyyy h:mm a" msgstr "yyyy/MM/dd HH:mm" msgid "MMM DD LT" msgstr "MMM Do LT " msgid "Make sure all artifacts enabled on the platform are compatible with the new BDM." msgstr "プラットフォームで有効にされているすべてのアーティファクトが新しいBDMと互換性があることを確認してください。 " msgid "Make sure to deactivate the maintenance mode." msgstr "メンテナンスモードを無効にしてください。" msgid "Make this task available to other team members again" msgstr "このタスクは他のチーム メンバーでも行えるようにする" msgid "Make this task personal: only I will be able to do it" msgstr "このタスクは自分だけが行えるようにする" msgid "Manage Categories" msgstr "カテゴリを管理" msgid "Manager" msgstr "マネージャ" msgid "Mapped categories" msgstr "マップされたカテゴリ" msgid "Maximum size 255 characters" msgstr "最大 255 文字" msgid "Membership has not been added. Please retry later or contact an administrator" msgstr "所属が追加されませんでした。後で再試行するか管理者に問い合わせてください。 " msgid "Membership has not been removed. Please retry later or contact an administrator" msgstr "所属が削除されませんでした。後で再試行するか管理者に問い合わせてください。 " msgid "Membership successfully added" msgstr "所属が正常に追加されました" msgid "Membership successfully removed" msgstr "所属が正常に削除されました" msgid "Memberships" msgstr "所属" msgid "Memberships mapped to {}" msgstr "{} にマップされたメンバーシップ" msgid "Mobile" msgstr "携帯番号" msgid "Modifying or redeploying the Business Data Model may impact existing data retention rules. After redeployment, review your configuration in the Data Retention (GDPR) section." msgstr "" msgid "Monitoring status" msgstr "進捗状況のモニタリング " msgid "Multi-page menu" msgstr "複数ページ メニュー" msgid "My tasks" msgstr "マイタスク" msgid "Name" msgstr "名前" msgid "Navigation" msgstr "ナビゲーション" msgid "New" msgstr "新規" msgid "New Password" msgstr "新規パスワード" msgid "New comment" msgstr "新しいコメント" msgid "No application available." msgstr "利用可能なアプリケーションはありません。" msgid "No application found with id:" msgstr "ID付きのアプリケーションが見つかりません:" msgid "No comment on this case yet. To add one, use the input field at the bottom of this panel" msgstr "まだこの案件に対するコメントがありません。追加するには、このパネルの下部にあるコメント入力フィールドを使用します。" msgid "No custom information defined." msgstr "カスタムの情報が定義されていません。" msgid "No data" msgstr "データがありません。" msgid "No description." msgstr "説明がありません。" msgid "No done task yet." msgstr "まだ完了したタスクはありません。" msgid "No form is needed. You can enter a comment and confirm." msgstr "フォームは不要です。コメントを入力し、確認することができます。" msgid "No mapping" msgstr "マッピングなし" msgid "No process found with id:" msgstr "IDt付プロセスが見つかりません:" msgid "No profile" msgstr "プロファイルがありません" msgid "No profile mapped to this application" msgstr "このアプリケーションにマップされているプロファイルはありません。" msgid "No updates have been made on categories. For more details, check logs." msgstr "カテゴリに関する更新はありません。詳細についてはログを確認してください。" msgid "No user found with id:" msgstr "ID付ユーザーが見つかりません:" msgid "Normal" msgstr "普通" msgid "Not installed" msgstr "インストールされていません。" msgid "OK" msgstr "OK" msgid "One or more tasks are not available anymore. The list is now up to date." msgstr "1つ以上のタスクは、もうご使用いただけません。タスク一覧が新しくなっています。" msgid "One-page menu" msgstr "1 ページ メニュー" msgid "Only I can do this task" msgstr "私だけがこのタスクを実行できる" msgid "Only available when the platform is under maintenance. Go to the \"Platform maintenance\" page to activate the maintenance mode." msgstr "プラットフォームがメンテナンス中の場合のみ利用可能です。「プラットフォームのメンテナンス」ページでメンテナンスモードを有効にしてください。" msgid "Only one Business Data Model (BDM) can be active at a time." msgstr "一度にアクティブにできるビジネスデータモデル(BDM)は1つだけです。" msgid "Open cases" msgstr "進行中案件" msgid "Open in a popup" msgstr "ポップアップで開く" msgid "Open the task to perform it" msgstr "実行するタスクを開く" msgid "Overview" msgstr "案件概況" msgid "Page" msgstr "ページ" msgid "Pages" msgstr "ページ" msgid "Parameters" msgstr "パラメータ" msgid "Parameters must be resolved before enabling the Process." msgstr "プロセスを有効にする前にパラメータを解決する必要があります。" msgid "Password" msgstr "パスワード" msgid "Password has not been updated. Please retry later or contact an administrator" msgstr "パスワードが更新されませんでした。後で再試行するか管理者に問い合わせてください。" msgid "Password successfully updated" msgstr "パスワードが正常に更新されました" msgid "Passwords don't match" msgstr "パスワードが一致しません" msgid "Personal information" msgstr "個人情報" msgid "Personal information has not been updated. Please retry later or contact an administrator" msgstr "個人情報は更新されませんでした。後で再試行するか管理者に問い合わせてください。" msgid "Personal information successfully updated" msgstr "個人情報が正常に更新されました" msgid "Phone" msgstr "電話番号" msgid "Please refresh the page." msgstr "ページを更新してください。" msgid "Priority" msgstr "優先度" msgid "Process" msgstr "プロセス" msgid "Process id" msgstr "プロセス ID" msgid "Process id not provided. Unable to retrieve the process details." msgstr "プロセスID が提供されていません。プロセスの詳細を取得できません。" msgid "Process name" msgstr "プロセス名" msgid "Process:" msgstr "プロセス:" msgid "Profile" msgstr "プロファイル" msgid "Profiles" msgstr "プロファイル" msgid "Profiles / Memberships" msgstr "プロファイル / 所属" msgid "Ready, waiting instances" msgstr "作業開始を待機中" msgid "Refresh data" msgstr "データを再読み込み" msgid "Release" msgstr "手放す" msgid "Remove" msgstr "削除" msgid "Remove all" msgstr "すべて削除" msgid "Removing on apply" msgstr "適用時の削除" msgid "Reset" msgstr "リセット" msgid "Reset to default" msgstr "初期設定にリセットする" msgid "Roles" msgstr "役割" msgid "Save" msgstr "保存" msgid "Search..." msgstr "検索..." msgid "Select a group..." msgstr "グループを選択..." msgid "Select a role..." msgstr "役割を選択..." msgid "Select all" msgstr "すべて選択" msgid "Select groups..." msgstr "グループを選択..." msgid "Select none" msgstr "選択を解除" msgid "Select roles..." msgstr "役割を選択..." msgid "Select the User information management tab to add Custom information on the right" msgstr " ユーザー情報管理タブを選択し、右側の カスタム情報 を追加します" msgid "Select the entities (users, groups, roles, memberships) to map to the actors. These entities will do the human tasks in the process." msgstr "アクターにアサインするエンティティ(ユーザー、グループ、役割、所属など)を選択します。これらのエンティティは、プロセスにヒューマン タスクを実行します。" msgid "Select the page" msgstr "ページを選択します" msgid "Select users..." msgstr "ユーザーを選択..." msgid "Server is under maintenance." msgstr "サーバーはメンテナンス中です。" msgid "Set as Home page" msgstr "ホーム ページとして設定します" msgid "Show Menu" msgstr "メニューを表示" msgid "Show all the tasks I can do: both personal tasks and tasks available to other team members" msgstr "自分が行うことができるすべてのタスクを表示する: 自分だけにアサインされたタスクおよび他のチーム メンバーでも行えるタスクの両方を表示" msgid "Show menu" msgstr "メニューを表示" msgid "Show my personal tasks: the ones I took and the ones assigned to me, automatically or by a process manager" msgstr "自分だけにアサインされたタスクを表示する: 自分自身で引き受けたもの、自動的、またはプロセス管理者によってアサインされたもの" msgid "Show panel" msgstr "パネルを表示" msgid "Show the tasks that I have done" msgstr "自分が関与し実行したタスクを表示" msgid "Something went wrong during the deletion. You might want to cancel and try again." msgstr "削除中に問題が発生しました。 キャンセルして再試行してください。" msgid "Something went wrong during the modification. You might want to cancel and try again." msgstr "変更中に問題が発生しました。キャンセルして再試行してください。" msgid "Something went wrong. You might want to cancel and try again." msgstr "問題が発生しました。キャンセルして再試行してください。" msgid "Sort by URL" msgstr "URL で並べ替え" msgid "Sort by name" msgstr "名前で並べ替え" msgid "Sort by type" msgstr "種類で並び替え" msgid "Sort by updated on" msgstr "更新日で並べ替え" msgid "Sort by version" msgstr "バージョンで並び替え" msgid "Specify the menu structure." msgstr "メニュー構造を指定します" msgid "State" msgstr "状態" msgid "Status" msgstr "ステータス" msgid "Status of importation" msgstr "インポートの状態" msgid "Still, you can customize its look & feel on this page. You can also update some of its pages in the Resources menu." msgstr "それでも、このページでそのルック&フィールをカスタマイズすることができます。[リソース]メニューのいくつかのページを更新することもできます。" msgid "Submit" msgstr "送信" msgid "Successfully updated categories" msgstr "正常に更新されたカテゴリ" msgid "Table settings" msgstr "テーブルの設定" msgid "Take" msgstr "引き受ける" msgid "Task Id" msgstr "タスクID" msgid "Task form" msgstr "タスク フォーム" msgid "Task list" msgstr "タスク リスト" msgid "Task name" msgstr "タスク名" msgid "Technical User" msgstr "テクニカル ユーザー" msgid "Technical error." msgstr "テクニカルなエラーが発生。" msgid "The BDM schema and data have been rolled-back to what they were before this update attempt." msgstr "BDM スキーマとデータは、この更新試行される前のものにロールバックされています。" msgid "The application" msgstr "アプリケーション" msgid "The application does not exist. Go to application list page to see the new list of applications." msgstr "アプリケーションが存在しません。アプリケーションのリストページに移動して、アプリケーションの新しいリストを表示します。" msgid "The application does not exist. Reload the page to see the new list of applications." msgstr "アプリケーションが存在しません。 ページをリロードして、アプリケーションの新しいリストを表示します。" msgid "The application page does not exist. Reload the page to see the new list of application pages." msgstr "アプリケーションページが存在しません。 ページをリロードして、アプリケーションページの新しいリストを表示します。" msgid "The attachment is too big.
      Select a smaller attachment and submit the form again." msgstr "添付ファイルが大きすぎます。
      小さい添付ファイルを選択して、フォームを再度送信してください。" msgid "The business data: [ {} ] uses Business Objects which are not defined in the current Business Data model. Deploy a compatible Business Data model before enabling the process." msgstr "ビジネス データ: [ {} ] は、現在のビジネス データ モデル内に定義されていないビジネス オブジェクトを使用しています。プロセスを有効にする前に互換性のあるビジネス データ モデルをデプロイしてください。" msgid "The case is archived. You cannot add comments anymore" msgstr "この案件は完了しています。もうコメントを追加することはできません。" msgid "The case {{caseId}} has been started successfully." msgstr "案件ID: %caseId% が正常に開始されました。" msgid "The category cannot be removed and added at the same time." msgstr "カテゴリーの削除と追加を同時に行うことはできません。" msgid "The current process has no connectors defined." msgstr "現在のプロセスには、コネクタが定義されていません。" msgid "The current process has no parameters defined." msgstr "現在のプロセスには、パラメータが定義されていません。" msgid "The file you are trying to upload has the wrong mime type" msgstr "アップロードしようとしているファイルのmimeタイプが間違っています" msgid "The form mappings must be resolved before enabling the Process." msgstr "プロセスを有効にする前にフォームの マッピングを解決する必要があります。" msgid "The image you are trying to upload is too big" msgstr "アップロードしようとしている画像が大きすぎます" msgid "The item does not exist anymore. Refresh the page to see the new status" msgstr "アイテムは既に存在していません。新しいステータスを表示するにはページを更新してください" msgid "The menu does not exist. Reload the page to see the new list of menus." msgstr "メニューは存在しません。 ページをリロードしてメニューの新しいリストを表示します。" msgid "The name for the URL must start with custompage_ followed only by alphanumeric characters." msgstr "URL の名前は、英数字のみが続く custompage_ で始まらなければなりません。" msgid "The task has been submitted but an error occurred while adding the comment" msgstr "タスクが送信されましたが、コメントを追加するときにエラーが発生しました。" msgid "The whole list of such changes is listed here." msgstr "このような変更の一覧は こちらです。" msgid "The words 'content', 'API' and 'theme' are reserved for internal use. They must not be used in an application or page URL." msgstr "'content'、'API' 、 'theme' の単語は、内部使用のため予約されています。アプリケーション、またはページの URL にそれらを使用してはいけません。" msgid "Theme" msgstr "テーマ" msgid "Then for each user, fill out the custom information value, in the Studio (List of users tab of the organization), or right on this Portal page." msgstr "Studioの組織の ユーザーリスト タブ、またはこのポータル ページの右側で、各ユーザーのカスタム情報の値を入力します。" msgid "They will be removed from the database permanently." msgstr "それらは完全にデータベースから削除されます。" msgid "This URL is already in use" msgstr "この URL は、既に使用中" msgid "This application is core to the Bonita platform. To keep it working as it should, it must remain available and its menu should stay as it is." msgstr "このアプリケーションは、Bonitaプラットフォームの中核です。 正常に機能し続けるには、使用可能な状態を維持し、メニューをそのままにしておく必要があります。" msgid "This category has already been added to this process." msgstr "このカテゴリは既にこのプロセスに追加されています。" msgid "This field is mandatory" msgstr "このフィールドは必須です" msgid "This is the task details panel. No information to display" msgstr "これは、タスクの詳細パネルです。表示する情報がありません。" msgid "This name is already in use" msgstr "この名前は既に使用中" msgid "This only changes the application link. The application itself is still deployed at the same location." msgstr "これはアプリケーションのリンクを変更するだけです。アプリケーション自体は同じ場所にデプロイされます。" msgid "This only deletes the application link. The application itself is not deleted and is still deployed." msgstr "アプリケーションのリンクのみが削除されます。アプリケーション自体は削除されず、デプロイされたままです。" msgid "This process is no longer available." msgstr "このプロセスは既に使用できません。" msgid "This process is not fully configured, but it is still enabled. Users may still be able to start cases or do tasks, which may lead to errors." msgstr "このプロセスは完全には設定されていませんが有効です。 \n" "ユーザーは引き続きケースを開始したりタスクを実行できますがエラーになる可能性があります。" msgid "This process is not fully configured." msgstr "このプロセスは完全に設定されていません。" msgid "This process is not fully configured. It cannot be enabled." msgstr "このプロセスは完全い設定されてないので有効ではありません。" msgid "This task cannot be executed from here." msgstr "このタスクはここから実行できません。" msgid "This task is completed." msgstr "このタスクは完了しました。" msgid "This task is not available anymore. The list is now up to date." msgstr "1 つまたは複数のタスクは、もうご使用いただけません。タスク一覧が新しくなっています。" msgid "This task is overdue. It was supposed to be completed by {{dueDate}}" msgstr "このタスクは期限切れです。 {{dueDate}} までに完了する予定でした。" msgid "Title" msgstr "タイトル(性別)" msgid "To create an application, click New or Import." msgstr "アプリケーションを作成するには、新規またはインポート をクリックします。" msgid "To do" msgstr "To do" msgid "To do so, go to" msgstr "これを行うには、次へ進む: " msgid "To do so, log out and then log back in as the Technical User, whose credentials are set upon Bonita platform installation." msgstr "そのためにログアウトし、Bonita プラットフォーム のインストール時に設定されたテクニカル ユーザーの資格で再度ログインします。" msgid "To fill out the form, you need to take this task. This means you will be the only one able to do it. To make it available to the team again, release it." msgstr "フォームに記入するには、このタスクを引き受ける必要があります。つまり、あなただけがこのタスクを実行できることになります。もう一度、チームで実行できるようにする(つまり、応受援タスクにする)には、そのタスクを手放します。" msgid "To use this feature, create a new version of the process in Bonita Studio version 6.4 and above, then create and install the new .bar file in the Portal." msgstr "この機能を使用するには、Bonita Studio バージョン 6.4 以上でプロセスの新しいバージョンを作成し、その新しい .bar ファイルをポータルにインストールします。" msgid "Type" msgstr "タイプ" msgid "Type here to search for a user..." msgstr "ユーザーを検索するには、ここを入力..." msgid "Type here to search..." msgstr "検索キーワードをここで入力..." msgid "Type in a case id and
      press enter to search for
      human task of a specific case" msgstr "特定案件のヒューマンタスクを検索するには、
      案件ID を入力し
      エンターキーを押してください。" msgid "URL" msgstr "URL" msgid "Unable to add the comment. Refresh your task list and try again. If the problem persists, contact your administrator" msgstr "コメントを追加することができません。タスク一覧を更新し、もう一度やり直してください。問題が解決しない場合はシステム管理者に問い合わせてください。" msgid "Unable to map category:" msgstr "カテゴリをマッピングができません:" msgid "Unassign. This will make it available to team again" msgstr "アサインを解除します。このタスクはもう一度、応受援タスクに戻ります。" msgid "Under normal" msgstr "平均以下" msgid "Update" msgstr "更新" msgid "Update Business Data Model" msgstr "ビジネス データ モデルの更新" msgid "Update business card" msgstr "業務上の連絡先を更新" msgid "Update custom information" msgstr "カスタム情報を更新" msgid "Update general information" msgstr "全般な情報を更新" msgid "Update password" msgstr "パスワードを更新" msgid "Update personal information" msgstr "個人情報を更新" msgid "Updated by" msgstr "更新者" msgid "Updated on" msgstr "更新日時" msgid "Updating the Business Data Model applies changes to both existing database tables and data." msgstr "ビジネス データ モデルの更新は、既存のデータベース テーブルとデータの両方に変更を適用します。" msgid "Upload new picture" msgstr "新しい画像をアップロード" msgid "Use Arrow up and Arrow down keys to browse among existing categories." msgstr "既存のカテゴリを閲覧するには、上矢印キーおよび下矢印キーを使用します。" msgid "User id not provided. Unable to retrieve the user details." msgstr "ユーザーIDが提供されていません。 ユーザーの詳細を取得できません。" msgid "Username" msgstr "ユーザー名" msgid "Users" msgstr "ユーザー" msgid "Users mapped to {}" msgstr "{} にマップされているユーザー" msgid "Value" msgstr "値" msgid "Version" msgstr "バージョン" msgid "View application details" msgstr "アプリケーションの詳細表示" msgid "View task" msgstr "タスクを見る" msgid "View this task" msgstr "このタスクを見る" msgid "Warning:" msgstr "警告:" msgid "We couldn't find what you are looking for." msgstr "お探しのものが見つかりませんでした。" msgid "When you export an application descriptor, pages and profiles are not exported in the file." msgstr "アプリケーション記述子をエクスポートする場合、ページとプロファイルはそのファイルにエクスポートされません。" msgid "You are about to export the descriptor of the application" msgstr "アプリケーションの記述子をエクスポートしようとしています" msgid "You are going to be redirected to the process list." msgstr "プロセスリストにリダイレクトされます。" msgid "You can create custom information in Bonita Studio:" msgstr "Bonita Studio でカスタム情報を作成できます:" msgid "You must add a page before you can reference it in a menu." msgstr "それをメニューで参照する前に、ページを追加する必要があります。" msgid "Your personal task list is empty. You can take a task from the ​To do list." msgstr "マイタスク リストは空です。​To do リストからタスクを選ぶことができます。" msgid "Your session is no longer active." msgstr "セッションは既にアクティブではありません。" msgid "Zip code" msgstr "郵便番号" msgid "Zip file structure error. Check that your .zip contains a well-formed page.properties and either the index.html or the Index.groovy file. For details, see the documentation or the example page readme (available in the custom page list)" msgstr "Zip ファイル構造にエラーがあります。.zip に適切な形式の page.properties と、index.html または Index.groovy ファイルが 含まれているか確認してください。詳細については、ドキュメント、またはサンプル ページの readme (カスタム ページのリストで利用可能) を参照してください。" msgid "an Application" msgstr "アプリケーション" msgid "an application" msgstr "アプリケーション" msgid "application descriptor has been partially imported. The following items are not loaded in the Portal." msgstr "アプリケーション記述子は、部分的にインポートされています。ポータル内に次の項目が読み込まれていません。" msgid "application descriptor has been successfully imported, with complete page mapping and menu mapping." msgstr "アプリケーション記述子は正常にインポートされ、ページ マッピングとメニュー マッピングが完了しています。" msgid "here" msgstr "こちら" msgid "of" msgstr "日目/" msgid "will be permanently deleted." msgstr "恒久的に削除されます。" msgid "{{firstname}} {{lastname}} already has the membership {{role}} of {{group}}" msgstr "{{firstname}} {{lastname}} さんは、既に {{group}} に {{role}} としてに所属しています" msgid "{{membership.role_id.displayName}} of {{membership.group_id.displayName}}" msgstr "{{membership.group_id.displayName}} の {{membership.role_id.displayName}}" msgid "{{nbErrors}} errors on mapping updates" msgstr "マッピングの更新で {{nbErrors}} のエラーが発生しました" msgid "{{nbMapping}} Process manager mappings could not be updated" msgstr "{{nbMapping}} のプロセス管理者マッピングを更新できませんでした。" msgid "{{nbMapping}} Process manager mappings have been updated" msgstr "{{nbMapping}} のプロセス管理者マッピングが更新されました" msgid "{{nbOfDeletedCases}} cases have been deleted" msgstr "{{nbOfDeletedCases}} の案件が削除されました" msgid "{{nbSucess}} actor mapping updates succeeded" msgstr "{{nbSucess}} のアクターマッピングの更新に成功しました" msgid "{{role}} of {{group}}" msgstr "{{group}} の {{role}}" msgid "{{role}} of {{group}} has been created" msgstr "{{group}} の {{role}} が作成されました" msgid "{} mapped" msgstr "{} がマップされました" msgid "{} mappings to delete" msgstr "{} のマッピングが削除されます" msgid "{} more" msgstr "{} 以上" msgid "{} of {}" msgstr "{} / {}" ================================================ FILE: bpm/bonita-web-server/src/main/resources/i18n/portal-js_pt_BR.po ================================================ msgid "" msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Project-Id-Version: bonita\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: bonita\n" "X-Crowdin-Project-ID: 13316\n" "X-Crowdin-Language: pt-BR\n" "X-Crowdin-File: /dev/bonita-web/portal/portal-js.pot\n" "X-Crowdin-File-ID: 60594\n" "Language-Team: Portuguese, Brazilian\n" "Language: pt_BR\n" "PO-Revision-Date: 2024-01-01 00:00\n" msgid "1 case has been deleted" msgstr "1 caso foi excluído" msgid "A Link to your application made outside the portal, e.g. with Bonita UI Builder." msgstr "Um link para sua aplicação feita fora do portal, por exemplo, com o Bonita UI Builder." msgid "A classic Bonita Living Application." msgstr "Um clássico aplicativo Bonita Live." msgid "A page with this name already exists." msgstr "Já existe uma página com esse nome." msgid "A server error occurred. The operation did not complete successfully" msgstr "Ocorreu um erro de servidor. A operação não foi concluída com sucesso" msgid "Above normal" msgstr "Acima do nomal" msgid "Access denied. For more information, check the log file." msgstr "Acesso negado. Para mais informações, verifique o arquivo de log." msgid "Actions" msgstr "Ações" msgid "Activation state" msgstr "Estado de ativação" msgid "Actor name" msgstr "Nome do ator" msgid "Actors" msgstr "Atores" msgid "Actors must be resolved before enabling the Process." msgstr "Os atores devem ser resolvidos antes de ativar o processo." msgid "Add" msgstr "Adicionar" msgid "Add from custom pages" msgstr "Adicionar a partir de páginas personalizadas" msgid "Add membership" msgstr "Adicionar membros" msgid "Add menu" msgstr "Adicionar menu" msgid "Add menu item" msgstr "Adicionar item de menu" msgid "Add page" msgstr "Acionar página" msgid "Add pages in Navigation" msgstr "Acione as páginas de navegação" msgid "Add top-level menu" msgstr "Adicionar menu de nível superior" msgid "Adding on apply" msgstr "Adicionando em aplicar" msgid "Address" msgstr "Endereço" msgid "All" msgstr "Todos" msgid "All done. Good job!" msgstr "Tudo feito. Bom trabalho!" msgid "An Error occurred during process activation/deactivation." msgstr "Ocorreu um erro durante a ativação/desativação do processo" msgid "An Error occurred during process deletion." msgstr "Ocorreu um erro durante o processo de deleção." msgid "An application is a customized environment for a specific user profile, in which users interact with business data and business processes in the most efficient way." msgstr "Uma aplicação é um ambiente personalizado para um perfil específico, no qual usuários interagem com dados e processos de negócios de maneira mais eficaz." msgid "An error has occurred. For more information, check the log file." msgstr "Ocorreu um erro. Para mais informações, verifique o arquivo de log." msgid "An error occurred during categories update" msgstr "Ocorreu um erro durante a atualização de categorias" msgid "An error occurred when deploying the BDM. Consult the logs for more information." msgstr "Ocorreu um erro ao implantar o BDM. Consulte as logs para mais informações." msgid "An error occurred while submitting the form." msgstr "Ocorreu um erro ao enviar o formulário." msgid "An error occurred while submitting the form.
      The task may not be available anymore." msgstr "Ocorreu um erro ao enviar o formulário.
      A tarefa pode não estar disponível mais." msgid "An error occurred with the requested operation." msgstr "Ocorreu um erro com a operação solicitada" msgid "Application Link" msgstr "Link do aplicativo" msgid "Application XML file" msgstr "Arquivo XML do aplicativo" msgid "Application id not provided. Unable to retrieve the application details." msgstr "ID da aplicação não fornecida. Não foi possível recuperar os detalhes do aplicativo." msgid "Application list" msgstr "Lista de aplicativos" msgid "Apply" msgstr "Aplicar" msgid "Archived cases" msgstr "Instâncias arquivadas" msgid "Are you sure you want to delete it?" msgstr "Tem certeza que deseja excluir " msgid "Are you sure you want to delete this membership ?" msgstr "Tem certeza que deseja excluir este membro?" msgid "Assign to me. Only I will be able to do it" msgstr "Atribua a mim. Só eu serei capaz de fazê-lo" msgid "Assigned on" msgstr "Atribuído para" msgid "Available or My tasks" msgstr "Disponível ou minhas tarefas" msgid "BDM access control file can now be installed." msgstr "Arquivo de controle de acesso BDM agora pode ser instalado." msgid "BDM file" msgstr "Arquivo do BDM" msgid "BDM has not been uploaded. System was not able to upload the provided BDM file for technical reasons. You can try again later." msgstr "BDM não foi carregado. Sistema não foi capaz de fazer upload de arquivo BDM fornecido por razões técnicas. Você pode tentar novamente mais tarde." msgid "BDM successfully updated." msgstr "Arquivo BDM atualizado com sucesso." msgid "Back" msgstr "Voltar" msgid "Before updating the BDM, access control file should be deleted." msgstr "Antes de atualizar o BDM, arquivo de controle de acesso deve ser excluído." msgid "Before you import the application descriptor, the pages and the profile must already be loaded in the Portal." msgstr "Antes de importar o descritor de aplicativos, as páginas e o perfil devem já ser carregados no Portal." msgid "Business Data Model definition" msgstr "Definição de modelo de dados de negócios" msgid "Business card" msgstr "Cartão de visita" msgid "Business card has not been updated. Please retry later or contact an administrator" msgstr "Cartão de visita não foi atualizado. Por favor, tente novamente mais tarde ou entre em contato com o administrador" msgid "Business card successfully updated" msgstr "Cartão de crédito atualizado com sucesso" msgid "Can't import page or form to the process" msgstr "Não foi possível importar a página ou formulário no processo" msgid "Can't update expression content" msgstr "Não foi possível atualizar o conteúdo da expressão" msgid "Cancel" msgstr "Cancelar" msgid "Cancelled, skipped instances" msgstr "Instâncias canceladas, ignoradas" msgid "Cannot upload the file" msgstr "Não foi possível carregar o arquivo" msgid "Case" msgstr "Instância de processo" msgid "Case ID" msgstr "ID do processo" msgid "Case comments" msgstr "Comentários da instância" msgid "Case id: {{caseId}} - Process: {{processName}} ({{processVersion}})" msgstr "Caso: {{caseId}} - Processo: {{processName}} ({{processVersion}})" msgid "Case overview" msgstr "Visão geral do caso" msgid "Case start" msgstr "Iniciar Instância" msgid "Cases with failures" msgstr "Casos com erros" msgid "Categories" msgstr "Categorias" msgid "Check that each entity is mapped to a profile, for either Bonita Portal or for applications you have created." msgstr "Verifique que cada entidade é mapeada para um perfil, para qualquer Portal Bonita ou para aplicativos que você tenha criado." msgid "City" msgstr "Cidade" msgid "Class name" msgstr "Nome da classe" msgid "Click here to choose the .xml file" msgstr "Clique aqui para escolher o arquivo .xml" msgid "Click here to choose your .zip file" msgstr "Clique aqui para escolher o arquivo .zip" msgid "Click on Cancel to remain on this page and try to execute the operation again once you logged back in (e.g. in another tab)." msgstr "Clique em Cancelar para permanecer nesta página e tente executar a operação novamente depois de fazer login novamente (por exemplo, em outra guia)." msgid "Click on Cancel to remain on this page and wait for the maintenance to end." msgstr "Clique em Cancelar para permanecer nesta página e aguardar o término da manutenção." msgid "Click on OK to be redirected and log back in." msgstr "Clique em OK para ser redirecionado e faça login novamente." msgid "Click on OK to be redirected to the maintenance page." msgstr "Clique em OK para ser redirecionado para a página de manutenção." msgid "Close" msgstr "Fechar" msgid "Columns selection" msgstr "Seleção de colunas" msgid "Comment:" msgstr "Comentário:" msgid "Comments" msgstr "Comentários" msgid "Compilation failure. Verify that the Index.groovy class implements the PageController interface." msgstr "Erro de compilação. Verifique se a classe Index.groovy implementa a interface PageController." msgid "Completed instances" msgstr "Instâncias concluídas" msgid "Configuration state" msgstr "Estado da configuração" msgid "Confirm new Password" msgstr "Confirmar nova senha" msgid "Connectors" msgstr "Conectores" msgid "Connectors must be resolved before enabling the Process." msgstr "Conectores devem ser resolvidos antes de ativar o processo." msgid "Core to Bonita platform" msgstr "Núcleo da plataforma Bonita BPM" msgid "Core to Bonita platform; cannot be deleted" msgstr "O núcleo da plataforma Bonita, não pode ser excluído" msgid "Country" msgstr "País" msgid "Create a new membership" msgstr "Criar uma nova associação" msgid "Create an application" msgstr "Criar uma aplicação" msgid "Created by" msgstr "Criado por" msgid "Creation" msgstr "Criação" msgid "Creation date" msgstr "Data de criação" msgid "Creation on" msgstr "Criada em" msgid "Custom information" msgstr "Informação Personalizada" msgid "Custom information has not been updated. Please retry later or contact an administrator" msgstr "Informação personalizada não foi atualizada. Por favor, tente novamente mais tarde ou entre em contato com o administrador" msgid "Custom information successfully updated" msgstr "Informações personalizadas atualizadas com sucesso" msgid "Delete" msgstr "Excluir" msgid "Delete application" msgstr "Excluir aplicação" msgid "Delete membership" msgstr "Apagar membro" msgid "Delete process" msgstr "Excluir processo" msgid "Deleting a process definition will also delete all open and archived cases of this process." msgstr "Excluir uma definição de processo também irá apagar todos os casos abertos e arquivados deste processo." msgid "Description" msgstr "Descrição" msgid "Disable" msgstr "Desativar" msgid "Display name" msgstr "Nome de exibição" msgid "Done tasks" msgstr "Tarefas arquivadas" msgid "Drawing process..." msgstr "Desenhando o processo..." msgid "Due date" msgstr "Prazo final" msgid "Edit" msgstr "Editar" msgid "Edit menu" msgstr "Editar menu" msgid "Edit menu item" msgstr "Editar item de menu" msgid "Email" msgstr "Email" msgid "Enable" msgstr "Ativar" msgid "Enable all" msgstr "Ativar Tudo" msgid "Enter a new category name and click \"Add\" button to create a new category." msgstr "Insira um novo nome de categoria e clique no botão \"Adicionar\" para criar uma categoria nova." msgid "Enter the user to start the case for..." msgstr "Digite o usuário para o qual o caso será iniciado..." msgid "Error" msgstr "Erro" msgid "Error on drawing process!" msgstr "Erro ao desenhar o processo!" msgid "Error while trying to start the case. Some required information is missing (contract not fulfilled)." msgstr "Erro ao tentar iniciar o caso. Algumas informações necessárias estão faltando (contrato não cumprido)." msgid "Error!" msgstr "Erro!" msgid "Error: value must be a boolean" msgstr "Erro: o valor deve ser um booleano" msgid "Error: value must be a double" msgstr "Erro: o valor deve ser um double" msgid "Error: value must be an integer" msgstr "Erro: o valor deve ser um número inteiro" msgid "Executing, completing, initializing instances" msgstr "Executando, terminando, inicializando instâncias" msgid "Export" msgstr "Exportar" msgid "Export application descriptor" msgstr "Descritor de aplicativos de exportação" msgid "Expression content has been updated" msgstr "O conteúdo da expressão foi atualizado" msgid "Failed instances" msgstr "Instâncias em erro" msgid "File format may be invalid. For more information, check the log file." msgstr "O formato do arquivo pode ser inválido. Para mais informações, verifique o arquivo de log." msgid "Filename too long. The zip filename must be no longer than 50 characters" msgstr "O nome do arquivo muito grande. O nome do arquivo zip não deve exceder 50 caracteres" msgid "Filters" msgstr "Filtro" msgid "First name" msgstr "Primeiro nome" msgid "Form" msgstr "Formulário" msgid "Form submitted.
      The next task in the list is now selected." msgstr "Formulário enviado.
      A próxima tarefa na lista agora está selecionada." msgid "General" msgstr "Geral" msgid "General information" msgstr "Informações gerais" msgid "General information has not been updated. Please retry later or contact an administrator" msgstr "Informação gerais não foram atualizadas. Por favor, tente novamente mais tarde ou entre em contato com o administrador" msgid "General information successfully updated" msgstr "Informações gerais atualizadas com sucesso" msgid "Groups" msgstr "Grupos" msgid "Healthy cases" msgstr "Casos saudáveis" msgid "Hide Menu" msgstr "Esconder Menu" msgid "Hide menu" msgstr "Ocultar Menu" msgid "Hide panel" msgstr "Ocultar Painel" msgid "Highest" msgstr "Mais alto" msgid "However, due to the use of Hibernate for data persistence, some changes on the data may not be well handled. These changes may lead to a corrupted database and CANNOT BE REVERTED." msgstr "No entanto, devido ao uso da Hibernate para a persistência de dados, algumas alterações nos dados podem não ser bem tratadas. Essas alterações podem levar a uma base de dados corrompida e NÃO PODEM SER REVERTIDAS." msgid "I have read and understood the previous warning message. I have prepared a database backup so I can rollback this operation." msgstr "Eu li e entendi a mensagem de aviso anterior. Eu preparei um backup de banco de dados para poder reverter esta operação." msgid "IN ANY CASE, you MUST CREATE A BACKUP of the current BDM database before undergoing a BDM update." msgstr "EM QUALQUER CASE, você DEVE CRIAR UM BACKUP da base de dados BDM atual antes de passar por uma atualização BDM." msgid "If your changes do not fall into those change types, you can go ahead and proceed with the BDM update now." msgstr "Se suas mudanças não caírem nesses tipos de mudança, você pode prosseguir e prosseguir com a atualização BDM agora." msgid "Implementation version" msgstr "Versão de implementação" msgid "Import" msgstr "Importar" msgid "Import an application" msgstr "Importar uma aplicação" msgid "Import an application descriptor" msgstr "Importar um descritor de aplicação" msgid "In a production environment, this should be used with caution." msgstr "Num ambiente de produção, isto deve ser utilizado com prudência." msgid "In such cases, you must CANCEL THIS UPDATE and let the database administrator carefully script the changes and apply them to the database. Only then will you be able to update the BDM from this page." msgstr "Nesses casos, você deve CANCELAR ESTE UPDATE e permitir que o administrador da base de dados escreva cuidadosamente as alterações e as aplique ao banco de dados. Somente então você poderá atualizar o BDM a partir desta página." msgid "In task name column" msgstr "Na coluna nome da tarefa" msgid "In the menu Organization > Manage, go to the Users page of the wizard" msgstr "No menu Organização > Gerenciar, vá para a página Usuários do assistente" msgid "Install" msgstr "Instalar" msgid "Install Business Data Model" msgstr "Instalar o Modelo de Dados de Negócio" msgid "Install a BDM file" msgstr "Instalar um arquivo BDM" msgid "Installed" msgstr "Instalado" msgid "Installed by" msgstr "Instalado por" msgid "Installed on" msgstr "Instalado em" msgid "Installing" msgstr "Instalando" msgid "Installing Business Data Model" msgstr "Instalando o Modelo de Dados de Negócios" msgid "Installing or updating the BDM implies to be logged in as Technical User, and to activate the maintenance mode." msgstr "Instalar ou atualizar o BDM implica estar logado como Usuário Técnico e ativar o modo de manutenção." msgid "Items per page" msgstr "Itens por página" msgid "Job title" msgstr "Título do trabalho" msgid "Last login" msgstr "Último Acesso" msgid "Last name" msgstr "Sobrenome" msgid "Last update" msgstr "Última atualização" msgid "Layout" msgstr "Layout" msgid "Legacy Application" msgstr "Aplicação Legacy" msgid "Loading..." msgstr "Carregando..." msgid "Logo" msgstr "Logo" msgid "Look & Feel" msgstr "Aparência" msgid "Lowest" msgstr "Mais baixo" msgid "MM/dd/yyyy h:mm a" msgstr "MM/dd/yyyy hh:mm a" msgid "MMM DD LT" msgstr "MMM DD LT" msgid "Make sure all artifacts enabled on the platform are compatible with the new BDM." msgstr "Certifique-se de todos os artefatos habilitados na plataforma são compatíveis com o novo BDM." msgid "Make sure to deactivate the maintenance mode." msgstr "Certifique-se de desativar o modo de manutenção." msgid "Make this task available to other team members again" msgstr "Disponibilizar esta tarefa para outros membros da equipe novamente" msgid "Make this task personal: only I will be able to do it" msgstr "Tornar esta tarefa pessoal: somente você poderá fazer" msgid "Manage Categories" msgstr "Gerenciar categorias" msgid "Manager" msgstr "Gerente" msgid "Mapped categories" msgstr "Categoria mapeada" msgid "Maximum size 255 characters" msgstr "Tamanho máximo 255 caracteres" msgid "Membership has not been added. Please retry later or contact an administrator" msgstr "Membros não foram atualizados. Por favor, tente novamente mais tarde ou entre em contato com o administrador" msgid "Membership has not been removed. Please retry later or contact an administrator" msgstr "Membros não foram removidos. Por favor, tente novamente mais tarde ou entre em contato com o administrador" msgid "Membership successfully added" msgstr "Membros foram adicionados com sucesso" msgid "Membership successfully removed" msgstr "Membro removido com sucesso" msgid "Memberships" msgstr "Associação" msgid "Memberships mapped to {}" msgstr "Associações mapeadas para {}" msgid "Mobile" msgstr "Celular" msgid "Modifying or redeploying the Business Data Model may impact existing data retention rules. After redeployment, review your configuration in the Data Retention (GDPR) section." msgstr "" msgid "Monitoring status" msgstr "Status do monitoramento" msgid "Multi-page menu" msgstr "Grupo de menus" msgid "My tasks" msgstr "Minhas tarefas" msgid "Name" msgstr "Nome" msgid "Navigation" msgstr "Navegação" msgid "New" msgstr "Novo" msgid "New Password" msgstr "Nova Senha" msgid "New comment" msgstr "Novos comentários" msgid "No application available." msgstr "Nenhuma aplicação disponível." msgid "No application found with id:" msgstr "Nenhum aplicativo encontrado com o ID:" msgid "No comment on this case yet. To add one, use the input field at the bottom of this panel" msgstr "Nenhum comentário sobre este caso ainda. Para adicionar um, use o campo de entrada na parte inferior deste painel" msgid "No custom information defined." msgstr "Sem informação customizada definida." msgid "No data" msgstr "Não há dados" msgid "No description." msgstr "Nenhuma descrição." msgid "No done task yet." msgstr "Ainda não acabou a tarefa." msgid "No form is needed. You can enter a comment and confirm." msgstr "Nenhum formulário é necessário. Você pode inserir um comentário e confirmar." msgid "No mapping" msgstr "Nenhum mapeamento" msgid "No process found with id:" msgstr "Nenhum processo encontrado com o ID:" msgid "No profile" msgstr "Sem perfis" msgid "No profile mapped to this application" msgstr "Sem perfil mapeado para esta aplicação" msgid "No updates have been made on categories. For more details, check logs." msgstr "Nenhuma atualização foi feita nas categorias. Para mais detalhes, verifique os logs." msgid "No user found with id:" msgstr "Nenhum usuário encontrado com o ID:" msgid "Normal" msgstr "Normal" msgid "Not installed" msgstr "Não Instalado" msgid "OK" msgstr "OK" msgid "One or more tasks are not available anymore. The list is now up to date." msgstr "Uma ou mais tarefas não estão mais disponíveis. A lista agora esta atualizada." msgid "One-page menu" msgstr "Menu de uma página" msgid "Only I can do this task" msgstr "Só eu posso fazer essa tarefa" msgid "Only available when the platform is under maintenance. Go to the \"Platform maintenance\" page to activate the maintenance mode." msgstr "Apenas disponível quando a plataforma estiver em manutenção. Vá para a página de \"Manutenção da plataforma\" para ativar o modo de manutenção." msgid "Only one Business Data Model (BDM) can be active at a time." msgstr "Somente um Modelo de Dados de Negócios (BDM) pode ser ativado no momento." msgid "Open cases" msgstr "Instâncias Abertas" msgid "Open in a popup" msgstr "Aberto em um pop-up" msgid "Open the task to perform it" msgstr "Abra a tarefa para executá-lo" msgid "Overview" msgstr "Visão geral" msgid "Page" msgstr "Página" msgid "Pages" msgstr "Páginas" msgid "Parameters" msgstr "Parâmetro" msgid "Parameters must be resolved before enabling the Process." msgstr "Os parâmetros devem ser resolvidos antes de ativar o processo." msgid "Password" msgstr "Senha" msgid "Password has not been updated. Please retry later or contact an administrator" msgstr "Senha não foi atualizada. Por favor tente mais tarde ou contate um administrador" msgid "Password successfully updated" msgstr "Senha atualizada com sucesso" msgid "Passwords don't match" msgstr "Senhas não coincidem" msgid "Personal information" msgstr "Informações pessoais" msgid "Personal information has not been updated. Please retry later or contact an administrator" msgstr "Informações pessoais não foram atualizadas. Por favor tente mais tarde ou contate um administrador" msgid "Personal information successfully updated" msgstr "Informações pessoais atualizadas com sucesso" msgid "Phone" msgstr "Telefone" msgid "Please refresh the page." msgstr "Por favor, atualize a página." msgid "Priority" msgstr "Prioridade" msgid "Process" msgstr "Processo" msgid "Process id" msgstr "Id do processo" msgid "Process id not provided. Unable to retrieve the process details." msgstr "ID do processo não fornecido. Não foi possível recuperar os detalhes do processo." msgid "Process name" msgstr "Nome do Processo" msgid "Process:" msgstr "Processo:" msgid "Profile" msgstr "Perfil" msgid "Profiles" msgstr "Perfis" msgid "Profiles / Memberships" msgstr "Perfis / Membros" msgid "Ready, waiting instances" msgstr "Instâncias prontas, ou em espera" msgid "Refresh data" msgstr "Atualizar dados" msgid "Release" msgstr "Liberar" msgid "Remove" msgstr "Remover" msgid "Remove all" msgstr "Remover tudo" msgid "Removing on apply" msgstr "Removendo ao aplicar" msgid "Reset" msgstr "Restaurar" msgid "Reset to default" msgstr "Redefinir para padrão" msgid "Roles" msgstr "Funções" msgid "Save" msgstr "Salvar" msgid "Search..." msgstr "Pesquisar..." msgid "Select a group..." msgstr "Selecione um grupo..." msgid "Select a role..." msgstr "Selecione uma função..." msgid "Select all" msgstr "Selecionar todos" msgid "Select groups..." msgstr "Selecione grupos..." msgid "Select none" msgstr "Desmarcar tudo" msgid "Select roles..." msgstr "Selecione funções..." msgid "Select the User information management tab to add Custom information on the right" msgstr "Selecione a aba Gerenciamento de Informações de Usuários para adicionar Informações customizadas na direita" msgid "Select the entities (users, groups, roles, memberships) to map to the actors. These entities will do the human tasks in the process." msgstr "Selecione as entidades (usuários, grupos, funções, associações) a serem mapeadas aos atores. Estas entidades realizarão as tarefas humanas da aplicação." msgid "Select the page" msgstr "Selecionar a página" msgid "Select users..." msgstr "Selecione usuários..." msgid "Server is under maintenance." msgstr "O servidor está em manutenção." msgid "Set as Home page" msgstr "Definir como página principal" msgid "Show Menu" msgstr "Mostrar Menu" msgid "Show all the tasks I can do: both personal tasks and tasks available to other team members" msgstr "Mostrar todas as tarefas que pode fazer: tanto pessoais, tarefas e tarefas disponíveis para outros membros da equipe" msgid "Show menu" msgstr "Mostrar menu" msgid "Show my personal tasks: the ones I took and the ones assigned to me, automatically or by a process manager" msgstr "Mostrar minhas tarefas pessoais: as que eu peguei e as que foram designadas para mim, automaticamente ou por um gerente de processo" msgid "Show panel" msgstr "Mostrar painel" msgid "Show the tasks that I have done" msgstr "Mostrar as tarefas que eu fiz" msgid "Something went wrong during the deletion. You might want to cancel and try again." msgstr "Algo deu errado durante a exclusão. Você pode cancelar e tentar de novo." msgid "Something went wrong during the modification. You might want to cancel and try again." msgstr "Algo deu errado durante a modificação. Talvez você queira cancelar e tentar novamente." msgid "Something went wrong. You might want to cancel and try again." msgstr "Algo deu errado. Talvez você queira cancelar e tentar novamente." msgid "Sort by URL" msgstr "Ordenar pela URL" msgid "Sort by name" msgstr "Ordenar por nome" msgid "Sort by type" msgstr "Ordenar por Tipo" msgid "Sort by updated on" msgstr "Ordenar pela data de atualização" msgid "Sort by version" msgstr "Ordenar por versão" msgid "Specify the menu structure." msgstr "Defina a estrutura do menu." msgid "State" msgstr "Estado" msgid "Status" msgstr "Status" msgid "Status of importation" msgstr "Status da importação" msgid "Still, you can customize its look & feel on this page. You can also update some of its pages in the Resources menu." msgstr "Ainda assim, você pode personalizar sua aparência nesta página. Você também pode atualizar algumas de suas páginas no menu Recursos." msgid "Submit" msgstr "Enviar" msgid "Successfully updated categories" msgstr "As categorias foram atualizadas com sucesso" msgid "Table settings" msgstr "Parâmetros da tabela" msgid "Take" msgstr "Pegar" msgid "Task Id" msgstr "Id da tarefa" msgid "Task form" msgstr "Formulário da tarefa" msgid "Task list" msgstr "Lista de tarefas" msgid "Task name" msgstr "Nome da tarefa" msgid "Technical User" msgstr "Usuário Técnico" msgid "Technical error." msgstr "Erro técnico." msgid "The BDM schema and data have been rolled-back to what they were before this update attempt." msgstr "O esquema e os dados BDM foram revertidos para o que eram antes desta tentativa de atualização." msgid "The application" msgstr "A aplicação" msgid "The application does not exist. Go to application list page to see the new list of applications." msgstr "O aplicativo não existe. Vá para a página da lista de aplicativos para ver a nova lista de aplicativos." msgid "The application does not exist. Reload the page to see the new list of applications." msgstr "A aplicação não existe. Recarregue a página para ver a nova lista de aplicações." msgid "The application page does not exist. Reload the page to see the new list of application pages." msgstr "A página da aplicação não existe. Recarregue a página para ver a nova lista de páginas da aplicação." msgid "The attachment is too big.
      Select a smaller attachment and submit the form again." msgstr "O anexo é muito grande.
      Selecione um anexo menor e envie novamente." msgid "The business data: [ {} ] uses Business Objects which are not defined in the current Business Data model. Deploy a compatible Business Data model before enabling the process." msgstr "O dado de negócio: [ {} ] usa Objetos de Negócio que não estão definidos no modelo de Dados de Negócio atual. Instale um modelo de Dados de Negócio compatível antes de ativar o processo." msgid "The case is archived. You cannot add comments anymore" msgstr "O instância esta arquivada. Não é possível adicionar comentários mais" msgid "The case {{caseId}} has been started successfully." msgstr "A instância {{caseId}} foi iniciada com êxito." msgid "The category cannot be removed and added at the same time." msgstr "Categoria não pode ser removida e adicionada em simultâneo." msgid "The current process has no connectors defined." msgstr "O processo atual tem os conectores não definidos." msgid "The current process has no parameters defined." msgstr "O processo atual não tem parâmetros definidos." msgid "The file you are trying to upload has the wrong mime type" msgstr "O arquivo que você está tentando carregar tem um tipo errado de mime" msgid "The form mappings must be resolved before enabling the Process." msgstr "Os mapeamentos de formulário devem ser resolvidos antes de ativar o processo." msgid "The image you are trying to upload is too big" msgstr "A imagem que você está tentando enviar é muito grande" msgid "The item does not exist anymore. Refresh the page to see the new status" msgstr "O item não existe mais. Atualize a página para ver o novo status" msgid "The menu does not exist. Reload the page to see the new list of menus." msgstr "O menu não existe. Recarregue a página para ver a nova lista de menus." msgid "The name for the URL must start with custompage_ followed only by alphanumeric characters." msgstr "O nome da URL deve começar com custompage_ seguido somente por caracteres alfanuméricos." msgid "The task has been submitted but an error occurred while adding the comment" msgstr "A tarefa foi apresentada, mas ocorreu um erro ao adicionar o comentário" msgid "The whole list of such changes is listed here." msgstr "A lista inteira de tais alterações é listada aqui." msgid "The words 'content', 'API' and 'theme' are reserved for internal use. They must not be used in an application or page URL." msgstr "As palavras 'conteúdo', 'API' e 'tema' são reservados para uso interno. Eles não devem ser usados em um aplicativo ou a URL da página." msgid "Theme" msgstr "Tema" msgid "Then for each user, fill out the custom information value, in the Studio (List of users tab of the organization), or right on this Portal page." msgstr "Então para cada usuário, preencha o valor da informação personalizada, no Studio (Lista de usuários aba da organização), ou na direita da página do Portal." msgid "They will be removed from the database permanently." msgstr "Eles serão removidos permanentemente do banco de dados." msgid "This URL is already in use" msgstr "Essa URL já está em uso" msgid "This application is core to the Bonita platform. To keep it working as it should, it must remain available and its menu should stay as it is." msgstr "Essa aplicação é essencial para a plataforma Bonita. Para que continue a funcionar como deve ser, tem de permanecer disponível e o seu menu deve permanecer como está." msgid "This category has already been added to this process." msgstr "Esta categoria já foi adicionada para este processo." msgid "This field is mandatory" msgstr "Este campo é obrigatório" msgid "This is the task details panel. No information to display" msgstr "Este é o painel de detalhes de tarefa. Nenhuma informação para exibir" msgid "This name is already in use" msgstr "Este nome já está em uso" msgid "This only changes the application link. The application itself is still deployed at the same location." msgstr "Isso apenas muda o link do aplicativo. O próprio aplicativo ainda é implantado no mesmo local." msgid "This only deletes the application link. The application itself is not deleted and is still deployed." msgstr "Isso só exclui o link do aplicativo. O aplicativo em si não é excluído e ainda está implantado." msgid "This process is no longer available." msgstr "Este processo não está mais disponível." msgid "This process is not fully configured, but it is still enabled. Users may still be able to start cases or do tasks, which may lead to errors." msgstr "Esse processo não está totalmente configurado, mas ainda está habilitado. Os usuários ainda podem iniciar casos ou realizar tarefas, o que pode levar a erros." msgid "This process is not fully configured." msgstr "Este processo não está totalmente configurado." msgid "This process is not fully configured. It cannot be enabled." msgstr "Esse processo não está totalmente configurado. Não pode ser habilitado." msgid "This task cannot be executed from here." msgstr "Esta tarefa não pode ser executada a partir daqui." msgid "This task is completed." msgstr "Esta tarefa esta concluída." msgid "This task is not available anymore. The list is now up to date." msgstr "Essa tarefa não está mais disponível. A lista agora esta atualizada." msgid "This task is overdue. It was supposed to be completed by {{dueDate}}" msgstr "Esta tarefa está em atraso. Era para ser concluída em {{dueDate}}" msgid "Title" msgstr "Título" msgid "To create an application, click New or Import." msgstr "Para criar uma aplicação clique em Novo ou Importar." msgid "To do" msgstr "Para fazer" msgid "To do so, go to" msgstr "Para fazer isso, vá para" msgid "To do so, log out and then log back in as the Technical User, whose credentials are set upon Bonita platform installation." msgstr "Para fazer isso, efetue logout e, em seguida, efetue login novamente como Usuário Técnico, cujas credenciais são definidas na instalação da plataforma Bonita." msgid "To fill out the form, you need to take this task. This means you will be the only one able to do it. To make it available to the team again, release it." msgstr "Para preencher o formulário, você precisa assumir esta tarefa. Isto significa que você será o único capaz de fazê-la. Para torná-la disponível para a equipe novamente, clique no botão liberar." msgid "To use this feature, create a new version of the process in Bonita Studio version 6.4 and above, then create and install the new .bar file in the Portal." msgstr "Para usar esse recurso, crie uma nova versão do processo no Bonita Studio com versão 6.4 ou superior , então crie e instale o novo arquivo .bar no Portal." msgid "Type" msgstr "Tipo" msgid "Type here to search for a user..." msgstr "Digite aqui para pesquisar um usuário..." msgid "Type here to search..." msgstr "Digite aqui para pesquisar..." msgid "Type in a case id and
      press enter to search for
      human task of a specific case" msgstr "Digite um id de instância e
      e clique enter para procurar
      tarefa humana de uma instância específica" msgid "URL" msgstr "URL" msgid "Unable to add the comment. Refresh your task list and try again. If the problem persists, contact your administrator" msgstr "Não é possível adicionar o comentário. Atualizar sua lista de tarefas e tente novamente. Se o problema persistir, contate o administrador" msgid "Unable to map category:" msgstr "Não é possível mapear categoria:" msgid "Unassign. This will make it available to team again" msgstr "Cancelar a atribuição. Isto tornará disponível a equipe novamente" msgid "Under normal" msgstr "Em condições normais" msgid "Update" msgstr "Atualizar" msgid "Update Business Data Model" msgstr "Atualizar Modelo de Dados de Negócios" msgid "Update business card" msgstr "Atualizar cartão de visitas" msgid "Update custom information" msgstr "Atualizar informações personalizadas" msgid "Update general information" msgstr "Atualizar informações gerais" msgid "Update password" msgstr "Atualizar senha" msgid "Update personal information" msgstr "Atualizar informações pessoais" msgid "Updated by" msgstr "Atualizado por" msgid "Updated on" msgstr "Atualizado em" msgid "Updating the Business Data Model applies changes to both existing database tables and data." msgstr "A atualização do Modelo de Dados de Negócio aplica alterações nas tabelas e dados do banco de dados existentes." msgid "Upload new picture" msgstr "Carregar nova imagem" msgid "Use Arrow up and Arrow down keys to browse among existing categories." msgstr "Uso as teclas seta para cima e seta para baixo para navegar entre as categorias existentes." msgid "User id not provided. Unable to retrieve the user details." msgstr "ID do usuário não fornecido. Não foi possível recuperar os detalhes do usuário." msgid "Username" msgstr "Nome de usuário" msgid "Users" msgstr "Usuários" msgid "Users mapped to {}" msgstr "Usuários mapeados a {}" msgid "Value" msgstr "Valor" msgid "Version" msgstr "Versão" msgid "View application details" msgstr "Detalhes do aplicativo" msgid "View task" msgstr "Visualizar tarefas" msgid "View this task" msgstr "Visualizar esta tarefa" msgid "Warning:" msgstr "Aviso:" msgid "We couldn't find what you are looking for." msgstr "Não encontramos o que você está procurando." msgid "When you export an application descriptor, pages and profiles are not exported in the file." msgstr "Quando você exporta um descritor de aplicativo, páginas e perfis não são exportados no arquivo." msgid "You are about to export the descriptor of the application" msgstr "Você está prestes a exportar o descritor da aplicação" msgid "You are going to be redirected to the process list." msgstr "Você vai ser redirecionado para a lista de processos." msgid "You can create custom information in Bonita Studio:" msgstr "Você pode criar informações personalizadas no Bonita Studio:" msgid "You must add a page before you can reference it in a menu." msgstr "Você deve adicionar uma página antes de poder referenciá-la em um menu." msgid "Your personal task list is empty. You can take a task from the ​To do list." msgstr "Sua lista de tarefas pessoais está vazia. Você pode tomar uma tarefa da lista para fazer." msgid "Your session is no longer active." msgstr "Sua sessão não está mais ativa." msgid "Zip code" msgstr "Cep" msgid "Zip file structure error. Check that your .zip contains a well-formed page.properties and either the index.html or the Index.groovy file. For details, see the documentation or the example page readme (available in the custom page list)" msgstr "Erro de estrutura de arquivo de zip. Verifique que o seu .zip contém um page.properties bem formado e um index.html ou um Index.groovy. Para maiores detalhes, consulte a documentação ou a página de exemplo readme (disponível na lista de páginas personalizadas)" msgid "an Application" msgstr "uma Aplicação" msgid "an application" msgstr "uma aplicação" msgid "application descriptor has been partially imported. The following items are not loaded in the Portal." msgstr "descritor de aplicações foi importado parcialmente. Os seguintes itens não foram carregados no Portal." msgid "application descriptor has been successfully imported, with complete page mapping and menu mapping." msgstr "descritor de aplicativos foi importado com sucesso, com mapeamento completo de página e mapeamento de menu." msgid "here" msgstr "aqui" msgid "of" msgstr "de" msgid "will be permanently deleted." msgstr "será excluída permanentemente." msgid "{{firstname}} {{lastname}} already has the membership {{role}} of {{group}}" msgstr "{{firstname}}{{lastname}} já tem o membro {{role}} do {{group}}" msgid "{{membership.role_id.displayName}} of {{membership.group_id.displayName}}" msgstr "{{membership.role_id.displayName}} do {{membership.group_id.displayName}}" msgid "{{nbErrors}} errors on mapping updates" msgstr "Ocorreram {{nbErrors}} erros na atualização das associações" msgid "{{nbMapping}} Process manager mappings could not be updated" msgstr "{{nbMapping}} associações do perfil Gerenciador de processo de não puderam ser atualizadas" msgid "{{nbMapping}} Process manager mappings have been updated" msgstr "{{nbMapping}} associações do perfil Gerenciador de processo foram atualizadas" msgid "{{nbOfDeletedCases}} cases have been deleted" msgstr "{{nbOfDeletedCases}} casos foram excluídos" msgid "{{nbSucess}} actor mapping updates succeeded" msgstr "{{nbSucess}} mapeamento de atores atualizados com sucesso" msgid "{{role}} of {{group}}" msgstr "{{role}} de {{group}}" msgid "{{role}} of {{group}} has been created" msgstr "{{role}} de {{group}} foi criado" msgid "{} mapped" msgstr "{} mapeado" msgid "{} mappings to delete" msgstr "{} mapeamentos para excluir" msgid "{} more" msgstr "{} mais" msgid "{} of {}" msgstr "{} de {}" ================================================ FILE: bpm/bonita-web-server/src/main/resources/i18n/portal_es.po ================================================ msgid "" msgstr "" "Project-Id-Version: bonita\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-19 17:28+0000\n" "PO-Revision-Date: 2024-01-01 00:00\n" "Last-Translator: \n" "Language-Team: Spanish\n" "Language: es_ES\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: bonita\n" "X-Crowdin-Project-ID: 13316\n" "X-Crowdin-Language: es-ES\n" "X-Crowdin-File: /dev/bonita-web/portal/portal.po\n" "X-Crowdin-File-ID: 60581\n" msgid "%attribute% file format is not allowed. Only %file_formats% files are allowed." msgstr "%attribute% formato de archivo no permitido. Sólo se permiten archivos %file_formats%." msgid "%attribute% file format not allowed or not starting with correct servlet path" msgstr "El formato del archivo %attribute% no está autorizado o no comienza por un camino de servlet correcto" msgid "%attribute% is mandatory" msgstr "%attribute% es obligatorio" msgid "%attribute% is not a valid Color value" msgstr "%attribute% no es un Color válido" msgid "%attribute% is not a valid URL" msgstr "%attribute% no es una URL valida" msgid "%attribute% is not a valid email" msgstr "%attribute% no es un email válido" msgid "%attribute% is not well written" msgstr "%attribute% no esta bien escrito" msgid "%attribute% must be a boolean value" msgstr "%attribute% debe ser un valor booleano" msgid "%attribute% must be a numeric value" msgstr "%attribute% debe ser un valor numérico" msgid "%attribute% must be an integer value" msgstr "%attribute% debe ser un valor entero" msgid "%attribute% must be less or equal than %value%" msgstr "%attribute% debe ser menor o igual que %value%" msgid "%attribute% must be less than %value%" msgstr "%attribute% debe ser menor que %value%" msgid "%attribute% must be on a single line" msgstr "%attribute% debe estar en una sola línea" msgid "%attribute% must be one of {%list%}" msgstr "%attribute% debe ser uno de {%list%}" msgid "%className% is not Serializable" msgstr "%className% no es Serializable" msgid "%className% not found. Only jdk types are supported" msgstr "%className% no encontrada. Solo los tipos jdk son soportados" msgid "%value% is not a valid value for %className%" msgstr "%value% no es un valor valido para %className%" msgid "A group with the name %groupName% already exists" msgstr "Ya existe un grupo con el mismo nombre %groupName%" msgid "An error occurred while creating a definition" msgstr "Error al crear una definición" msgid "An error occurred while deleting an item with the id %id%" msgstr "Error al eliminar un elemento con el identificador %id%" msgid "Bonita Applications" msgstr "Aplicaciones Bonita" msgid "Can't create group. Group '%groupName%' already exists" msgstr "No se puede crear el grupo. El grupo '%groupName%' ya existe" msgid "Can't create role. Role '%roleName%' already exists" msgstr "No se puede crear el rol. El rol '%roleName%' ya existe" msgid "Can't create user. User '%userName%' already exists" msgstr "No se puede crear el usuario. El usuario '%userName%' ya existe" msgid "Can't import Applications." msgstr "No se puede importar las aplicaciones." msgid "Can't import applications. An application '%token%' already exists" msgstr "No se puede importar las aplicaciones. Ya existe una aplicación '%token%'" msgid "Can't import organization" msgstr "No se puede importar la organización" msgid "Can't import organization. Please check that your file is well-formed." msgstr "No se puede importar la organización. Por favor, compruebe que su archivo está bien formateado." msgid "Can't parse json, non-well formed content" msgstr "No se puede convertir json, el contenido no está bien formateado" msgid "Can't start process %processId%, user %userId% not found" msgstr "No se puede iniciar proceso %processId%, no se encuentra el usuario %userId%" msgid "Can't start process, process %processId% is not enabled" msgstr "No se puede iniciar el proceso, el proceso %processId% no esta activado" msgid "Can't start process, process %processId% not found" msgstr "No se puede iniciar el proceso, no se encuentra el proceso %processId%" msgid "Can't update user. User not found" msgstr "No se puede actualizar el usuario. Usuario no encontrado" msgid "Category with name %categoryName% already exists" msgstr "La categoría %categoryName% ya existe" msgid "Data definition %dataName% doesn't exists for process %processId%" msgstr "La definición de dato %dataName% no existe para el proceso %processId%" msgid "Error during Application import file reading." msgstr "Error durante la lectura del archivo de importación de la aplicación." msgid "Error occurred when starting process %processId%" msgstr "Ha ocurrido un error al iniciar el proceso %processId%" msgid "Error occurred when starting process %processId%. Case creation limit reached." msgstr "Ocurrió un error al iniciar el proceso %processId%. Se alcanzó el límite de creación de casos." msgid "Error uploading the file. Maybe your session expired. You can try to refresh the page." msgstr "Error subiendo el fichero. Posiblemente su sesión ha expirado. Puede probar a refrescar la página." msgid "Error when adding group to actor member" msgstr "Error al agregar grupo al actor" msgid "Error when creating group" msgstr "Error al crear grupo" msgid "Error when creating user" msgstr "Ha ocurrido un error al crear el usuario" msgid "Error when deleting groups" msgstr "Error al suprimir los grupos" msgid "Error when deleting users" msgstr "Ha ocurrido un error al borrar los usuarios" msgid "Error when pausing BPM services" msgstr "Error al pausar los Servicios BPM" msgid "Error when resuming BPM services" msgstr "Error al arrancar los Servicios BPM" msgid "Error when searching users" msgstr "Ha ocurrido un error al buscar usuarios" msgid "Error when updating %activityId% activity variables" msgstr "Ha ocurrido un error al actualizar las variables de la actividad %activityId%" msgid "Error when updating group" msgstr "Error al actualizar el grupo" msgid "Error when updating process deployment informations" msgstr "Ha ocurrido un error al actualizar la información de despliegue del proceso" msgid "Error when updating user" msgstr "Ha ocurrido un error al actualizar el usuario" msgid "Filters are not supported by this API" msgstr "Los filtros no son soportados con este API" msgid "Json can't be mapped to " msgstr "Json no puede ser mapeado a " msgid "Login failed. No profile has been set up for this user. Contact your administrator." msgstr "Error de login. Ningún perfil está definido para este usuario. Contacte con tu administrador." msgid "Login form" msgstr "Formulario de login" msgid "MM/dd/yyyy" msgstr "dd/MM/yyyy" msgid "MM/dd/yyyy h:mm a" msgstr "dd/MM/yyyy HH:mm" msgid "MMMM dd, yyyy" msgstr "MMMM dd, yyyy" msgid "Only the value attribute can be updated" msgstr "Sólo el atributo de valor puede ser actualizado" msgid "Password" msgstr "Contraseña" msgid "Password must be at least %number% characters long" msgstr "Contraseña debe tener al menos %number% caracteres" msgid "Password must contain at least %number% digits" msgstr "La contraseña debe tener al menos %number% dígitos" msgid "Password must contain at least %number% lower case characters" msgstr "La contraseña debe tener al menos %number% minúsculas" msgid "Password must contain at least %number% special characters" msgstr "La contraseña debe tener al menos %number% caracteres especiales" msgid "Password must contain at least %number% upper case characters" msgstr "La contraseña debe tener al menos %number% mayusculas" msgid "Please, contact your administrator." msgstr "Por favor, ponte en contacto con tu administrador." msgid "Process %appName% in version %version% already exists" msgstr "El proceso %appName% en la version %version% ya existe" msgid "Process %appName% in version %version% contains 6.x Legacy artifacts (forms or case overview page). Those are based on Google Web Toolkit (GWT), a technology that is no longer supported by Bonita. To know more, check the documentation." msgstr "El proceso %appName% en la versión %version% contiene 6.x artefactos heredados (formularios o página de resumen de casos). Estos se basan en Google Web Toolkit (GWT), una tecnología que ya no está soportada por Bonita. Para saber más, consulte la documentación." msgid "Process definition not found for id %processId%" msgstr "No se encuentra la definición del proceso con identificador %processId%" msgid "Profile member already exists" msgstr "El usuario ya está asociado con este perfil " msgid "Request parameter \"id\" must be set." msgstr "Debe establecerse el parámetro \"id\"." msgid "Search terms are not supported by this API" msgstr "Los términos de búsqueda no son soportados por este API" msgid "Session expired. Please log in again." msgstr "La sesión expiró. Por favor inicie sesión de nuevo." msgid "Sorting is not supported by this API" msgstr "Ordenes no son soportados por la API" msgid "The only mandatory filter is %name%" msgstr "El único filtro obligatorio es %name%" msgid "The server is not available" msgstr "El servidor no está disponible" msgid "This category has already been added to this process" msgstr "Esta categoría ya ha sido añadida a este proceso" msgid "This group has already been mapped to actor" msgstr "Este grupo ya ha sido mapeado a un actor" msgid "This membership has already been mapped to actor" msgstr "Esta membresía ya ha sido mapeada a un actor" msgid "This membership is already added to user" msgstr "Este usuario ya posee esta membresia" msgid "This role has already been mapped to actor" msgstr "Este rol ya ha sido asignado a un actor" msgid "This user has already been mapped to actor" msgstr "Este usuario ya ha sido mapeado a un actor" msgid "Too many failed login attempts. Please try again later." msgstr "Demasiados intentos de inicio de sesión. Por favor, inténtelo de nuevo más tarde." msgid "Unable to deploy business archive" msgstr "No se puede desplegar el archivo de negocios" msgid "Unable to disable process" msgstr "Incapaz de desactivar el proceso" msgid "Unable to enable process" msgstr "Incapaz de activar el proceso" msgid "Unable to find data instance %dataName% for activity %activityId%" msgstr "Imposible encontrar la instancia del dato %dataName% para la actividad %activityId%" msgid "Unable to find role %roleId%" msgstr "No se puede encontrar el rol %roleId%" msgid "Unable to get group path, group not found" msgstr "No se puede obtener la ruta al grupo, grupo no encontrado" msgid "Unable to get process data definitions, process %processId% not found" msgstr "Imposible recuperar la definición de los datos de proceso, no se encuentra el proceso %processId%" msgid "Unable to log in. Please check your username and password." msgstr "No se puede iniciar la sesión. Por favor comprueba tu nombre de usuario y tu contraseña." msgid "User" msgstr "Usuario" msgid "User not found" msgstr "Usuario no encontrado" msgid "Welcome to" msgstr "Bienvenido a" msgid "message" msgstr "mensaje" msgid "or" msgstr "o" ================================================ FILE: bpm/bonita-web-server/src/main/resources/i18n/portal_fr.po ================================================ msgid "" msgstr "" "Project-Id-Version: bonita\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-19 17:28+0000\n" "PO-Revision-Date: 2024-01-01 00:00\n" "Last-Translator: \n" "Language-Team: French\n" "Language: fr_FR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Crowdin-Project: bonita\n" "X-Crowdin-Project-ID: 13316\n" "X-Crowdin-Language: fr\n" "X-Crowdin-File: /dev/bonita-web/portal/portal.po\n" "X-Crowdin-File-ID: 60581\n" msgid "%attribute% file format is not allowed. Only %file_formats% files are allowed." msgstr "le format de fichier %attribute% n'est pas autorisé. Utiliser les fichiers de format %file_formats%." msgid "%attribute% file format not allowed or not starting with correct servlet path" msgstr "Le format de fichier %attribute% n'est pas autorisé ou ne commence pas par un chemin de servlet correct" msgid "%attribute% is mandatory" msgstr "%attribute% est obligatoire" msgid "%attribute% is not a valid Color value" msgstr "%attribute% n'est pas une valeur de couleur correcte" msgid "%attribute% is not a valid URL" msgstr "%attribute% n'est pas une URL valide" msgid "%attribute% is not a valid email" msgstr "%attribute% n'est pas une adresse email valide" msgid "%attribute% is not well written" msgstr "%attribute% n'est pas correctement écrit" msgid "%attribute% must be a boolean value" msgstr "%attribute% doit être une valeur booléenne" msgid "%attribute% must be a numeric value" msgstr "%attribute% doit être valeur numérique" msgid "%attribute% must be an integer value" msgstr "%attribute% doit être un entier" msgid "%attribute% must be less or equal than %value%" msgstr "%attribute% doit être inférieur ou égale à %value%" msgid "%attribute% must be less than %value%" msgstr "%attribute% doit être inférieure à %value%" msgid "%attribute% must be on a single line" msgstr "%attribute% doit être sur une ligne unique" msgid "%attribute% must be one of {%list%}" msgstr "%attribute% doit être dans {%list%}" msgid "%className% is not Serializable" msgstr "%className% n'est pas serializable" msgid "%className% not found. Only jdk types are supported" msgstr "%className% est introuvable. Seuls les éléments de types jdk sont supportés" msgid "%value% is not a valid value for %className%" msgstr "%value% n'est pas valide pour la valeur de %className%" msgid "A group with the name %groupName% already exists" msgstr "Il existe déjà un groupe portant le nom %groupName%" msgid "An error occurred while creating a definition" msgstr "Une erreur s'est produite lors de la création d'une définition" msgid "An error occurred while deleting an item with the id %id%" msgstr "Une erreur s'est produite lors de la suppression d'un élément dont l'id est %id%" msgid "Bonita Applications" msgstr "Applications Bonita" msgid "Can't create group. Group '%groupName%' already exists" msgstr "Impossible de créer le groupe. Le groupe '%groupName%' existe" msgid "Can't create role. Role '%roleName%' already exists" msgstr "Impossible de créer ce rôle, car '%roleName%' est déjà présent" msgid "Can't create user. User '%userName%' already exists" msgstr "Impossible de créer cet utilisateur, car '%userName%' est déjà présent" msgid "Can't import Applications." msgstr "Impossible d'importer l'application." msgid "Can't import applications. An application '%token%' already exists" msgstr "L'application n'a pas pu être importée. L'URL '%token%' est déjà utilisée." msgid "Can't import organization" msgstr "Impossible d'importer l'organisation" msgid "Can't import organization. Please check that your file is well-formed." msgstr "Impossible d'importer l'organisation. Vérifiez que votre fichier correspond au XSD." msgid "Can't parse json, non-well formed content" msgstr "Impossible de lire le json, le contenu n'est pas correctement structuré" msgid "Can't start process %processId%, user %userId% not found" msgstr "Impossible de démarrer le processus %processId%, l'utilisateur %userId% est introuvable" msgid "Can't start process, process %processId% is not enabled" msgstr "Impossible de démarrer le processus, car %processId% n'est pas activé" msgid "Can't start process, process %processId% not found" msgstr "Impossible de démarrer le processus, processus %processId% introuvable" msgid "Can't update user. User not found" msgstr "Impossible de mettre à jour les données de l'utilisateur. L'utilisateur est introuvable" msgid "Category with name %categoryName% already exists" msgstr "La catégorie %categoryName% existe déjà" msgid "Data definition %dataName% doesn't exists for process %processId%" msgstr "Data definition %dataName% n'existe pas pour le process %processId%" msgid "Error during Application import file reading." msgstr "Erreur lors de l'importation de l'Application." msgid "Error occurred when starting process %processId%" msgstr "Une erreur s'est produite au démarrage du processus %processId%" msgid "Error occurred when starting process %processId%. Case creation limit reached." msgstr "Une erreur s'est produite lors du démarrage du processus %processId%. La limite de création de cas a été atteinte." msgid "Error uploading the file. Maybe your session expired. You can try to refresh the page." msgstr "Une erreur est survenue lors du chargement du fichier. Votre session a peut-être expiré. Essayez de rafraîchir la page." msgid "Error when adding group to actor member" msgstr "Erreur lors de l'ajout du groupe aux membres de l'acteur" msgid "Error when creating group" msgstr "Erreur lors de la création du groupe" msgid "Error when creating user" msgstr "Erreur lors de la création de l'utilisateur" msgid "Error when deleting groups" msgstr "Erreur lors de la suppression du(es) groupe(s)" msgid "Error when deleting users" msgstr "Erreur lors de la désactivation des utilisateurs" msgid "Error when pausing BPM services" msgstr "Erreur pendant la suspension des services BPM" msgid "Error when resuming BPM services" msgstr "Erreur lors de la reprise des services BPM" msgid "Error when searching users" msgstr "Erreur lors de la recherche des utilisateurs" msgid "Error when updating %activityId% activity variables" msgstr "Erreur lors de la mise à jour des variables de l'activité de %activityId%" msgid "Error when updating group" msgstr "Erreur lors de la mise à jour du groupe" msgid "Error when updating process deployment informations" msgstr "Erreur lors de l'actualisation des informations de déploiement du processus" msgid "Error when updating user" msgstr "Erreur lors de l'actualisation des données de l'utilisateur" msgid "Filters are not supported by this API" msgstr "Les filtres ne sont pas supportés par cette API" msgid "Json can't be mapped to " msgstr "Json ne peut pas être mappée à" msgid "Login failed. No profile has been set up for this user. Contact your administrator." msgstr "Échec de la connexion. Aucun permission n'a été mise en place pour cet utilisateur. Contactez votre administrateur." msgid "Login form" msgstr "Formulaire de connexion" msgid "MM/dd/yyyy" msgstr "dd/MM/yyyy" msgid "MM/dd/yyyy h:mm a" msgstr "dd/MM/yyyy HH:mm" msgid "MMMM dd, yyyy" msgstr "dd/MM/yyyy" msgid "Only the value attribute can be updated" msgstr "Seul l'attribut valeur peut être mis à jour" msgid "Password" msgstr "Mot de passe" msgid "Password must be at least %number% characters long" msgstr "Le mot de passe doit contenir au moins %number% caractères" msgid "Password must contain at least %number% digits" msgstr "Le mot de passe doit contenir au moins %number% chiffres" msgid "Password must contain at least %number% lower case characters" msgstr "Le mot de passe doit contenir au moins %number% lettres en minuscules" msgid "Password must contain at least %number% special characters" msgstr "Le mot de passe doit contenir au moins %number% caractères spéciaux" msgid "Password must contain at least %number% upper case characters" msgstr "Le mot de passe doit contenir au moins %number% lettres en majuscules" msgid "Please, contact your administrator." msgstr "Merci de contacter votre administrateur." msgid "Process %appName% in version %version% already exists" msgstr " Le processus %appName% existe déjà en version %version% " msgid "Process %appName% in version %version% contains 6.x Legacy artifacts (forms or case overview page). Those are based on Google Web Toolkit (GWT), a technology that is no longer supported by Bonita. To know more, check the documentation." msgstr "Le processus %appName% en version %version% contient des artefacts (formulaires ou pages de synthèse des cas) anciens, datant de Bonita 6.x. Ils sont basés sur Google Web Toolkit (GWT), technologie qui n’est plus supportée par Bonita. Pour en savoir plus, consultez la documentation." msgid "Process definition not found for id %processId%" msgstr "La définition du processus est introuvable pour l'id %processId%" msgid "Profile member already exists" msgstr "Ce membre est déjà associé au profil" msgid "Request parameter \"id\" must be set." msgstr "Le paramètre de requête \"id\" doit être défini." msgid "Search terms are not supported by this API" msgstr "Les critères de recherche ne sont pas supportés par cette API" msgid "Session expired. Please log in again." msgstr "La session a expiré. Connectez-vous à nouveau." msgid "Sorting is not supported by this API" msgstr "Le tri n'est pas supporté par cette API" msgid "The only mandatory filter is %name%" msgstr "Seul le filtre %name% est obligatoire" msgid "The server is not available" msgstr "Le serveur n'est pas disponible" msgid "This category has already been added to this process" msgstr "Cette catégorie a déjà été ajoutée à ce processus" msgid "This group has already been mapped to actor" msgstr "Ce groupe est déjà lié à l'acteur" msgid "This membership has already been mapped to actor" msgstr "Cette adhésion est déjà liée à l'acteur" msgid "This membership is already added to user" msgstr "Cette adhésion est déjà présente pour cet utilisateur" msgid "This role has already been mapped to actor" msgstr "Ce rôle est déjà lié à l'acteur" msgid "This user has already been mapped to actor" msgstr "Cet utilisateur est déjà lié à l'acteur" msgid "Too many failed login attempts. Please try again later." msgstr "Trop de tentatives de connexion. Veuillez réessayer plus tard." msgid "Unable to deploy business archive" msgstr "Impossible de déployer le fichier .bar" msgid "Unable to disable process" msgstr "Impossible de desactiver l'app" msgid "Unable to enable process" msgstr "Impossible d'activer l'app" msgid "Unable to find data instance %dataName% for activity %activityId%" msgstr "Impossible de trouver les données de l'instance %dataName% pour l'activité %activityId%" msgid "Unable to find role %roleId%" msgstr "Impossible de trouver le rôle %roleId%" msgid "Unable to get group path, group not found" msgstr "Impossible d'obtenir le chemin d'accès du groupe, groupe introuvable" msgid "Unable to get process data definitions, process %processId% not found" msgstr "Impossible d'obtenir des définitions de données de processus, car %processId% est introuvable" msgid "Unable to log in. Please check your username and password." msgstr "Impossible de se connecter. Veuillez vérifier votre nom d'utilisateur et le mot de passe." msgid "User" msgstr "Utilisateur" msgid "User not found" msgstr "Utilisateur introuvable" msgid "Welcome to" msgstr "Bienvenue sur" msgid "message" msgstr "Message" msgid "or" msgstr "ou" ================================================ FILE: bpm/bonita-web-server/src/main/resources/i18n/portal_ja.po ================================================ msgid "" msgstr "" "Project-Id-Version: bonita\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-19 17:28+0000\n" "PO-Revision-Date: 2024-01-01 00:00\n" "Last-Translator: \n" "Language-Team: Japanese\n" "Language: ja_JP\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Crowdin-Project: bonita\n" "X-Crowdin-Project-ID: 13316\n" "X-Crowdin-Language: ja\n" "X-Crowdin-File: /dev/bonita-web/portal/portal.po\n" "X-Crowdin-File-ID: 60581\n" msgid "%attribute% file format is not allowed. Only %file_formats% files are allowed." msgstr "%attribute% ファイルの形式は許可されていません。%file_formats%のファイルのみが許可されます。" msgid "%attribute% file format not allowed or not starting with correct servlet path" msgstr "%attribute% ファイル形式が許可されていないか、正しいサーブレット パスで始まっていません" msgid "%attribute% is mandatory" msgstr "%attribute%は必須です。" msgid "%attribute% is not a valid Color value" msgstr "%attribute% は、有効なカラー値ではありません。" msgid "%attribute% is not a valid URL" msgstr "%attribute% は、有効な URL ではありません。" msgid "%attribute% is not a valid email" msgstr "%attribute% は、有効なメール・アドレスではありません。" msgid "%attribute% is not well written" msgstr "%attribute% は、よく書かれていません。" msgid "%attribute% must be a boolean value" msgstr "%attribute% は、ブール値である必要があります。" msgid "%attribute% must be a numeric value" msgstr "%attribute% は数値でなければなりません" msgid "%attribute% must be an integer value" msgstr "%attribute% は、 整数値を指定する必要があります。" msgid "%attribute% must be less or equal than %value%" msgstr "%attribute% は、%value%以下にする必要があります。" msgid "%attribute% must be less than %value%" msgstr "%attribute% は、%value%より小さくなければなりません" msgid "%attribute% must be on a single line" msgstr "%attribute% は、1 行にする必要があります。" msgid "%attribute% must be one of {%list%}" msgstr "%attribute% は、{%list%}のいずれかする必要があります。" msgid "%className% is not Serializable" msgstr "%className% は、シリアライズ可能ではありません" msgid "%className% not found. Only jdk types are supported" msgstr "%className% が見つかりません。jdk type だけがサポートされます" msgid "%value% is not a valid value for %className%" msgstr "%value% は、%className% の適正な値ではありません" msgid "A group with the name %groupName% already exists" msgstr "%groupName% と同名のグループが既に存在しています。" msgid "An error occurred while creating a definition" msgstr "定義の作成中にエラーが発生しました" msgid "An error occurred while deleting an item with the id %id%" msgstr "Id %id% を持つ項目を削除中にエラーが発生しました" msgid "Bonita Applications" msgstr "Bonita アプリケーション" msgid "Can't create group. Group '%groupName%' already exists" msgstr "グループを作成することはできません。グループ '%groupName%' が既に存在します。" msgid "Can't create role. Role '%roleName%' already exists" msgstr "役割を作成することはできません。役割 '%roleName%' が既に存在します。" msgid "Can't create user. User '%userName%' already exists" msgstr "利用者を登録することはできません。利用者: '%userName%' が既に存在します。" msgid "Can't import Applications." msgstr "アプリケーションをインポートできません。" msgid "Can't import applications. An application '%token%' already exists" msgstr "アプリケーションをインポートできません。アプリケーション: '%token%' は既に存在します。" msgid "Can't import organization" msgstr "組織をインポートできません" msgid "Can't import organization. Please check that your file is well-formed." msgstr "組織をインポートできません。ファイルが正しい形式であることを確認してください。" msgid "Can't parse json, non-well formed content" msgstr "json を解析できません。 しっかり形式化されたコンテンツではありません。" msgid "Can't start process %processId%, user %userId% not found" msgstr "プロセスID: %processId% を起動できません。ユーザーID: %userId% が見つかりません。" msgid "Can't start process, process %processId% is not enabled" msgstr "プロセスを開始できません。プロセス:%processId% は有効になっていません" msgid "Can't start process, process %processId% not found" msgstr "プロセスを開始できません。 プロセス:%processId% が見つかりません" msgid "Can't update user. User not found" msgstr "利用者が見つからないため、更新できません" msgid "Category with name %categoryName% already exists" msgstr "カテゴリ名 %categoryName% は既に存在します。" msgid "Data definition %dataName% doesn't exists for process %processId%" msgstr "データ定義:%dataName% は、プロセス:%processId% に存在しません" msgid "Error during Application import file reading." msgstr "アプリケーションのインポート ファイルの読み取り中にエラーが発生しました。" msgid "Error occurred when starting process %processId%" msgstr "プロセス:%processId% の開始時にエラーが発生しました" msgid "Error occurred when starting process %processId%. Case creation limit reached." msgstr "プロセス %processId%の開始時にエラーが発生しました。ケース作成の上限に達しました。" msgid "Error uploading the file. Maybe your session expired. You can try to refresh the page." msgstr "ファイルのアップロード中にエラーが発生しました。セッションの有効期限が切れた可能性があります。ページを更新してみてください。" msgid "Error when adding group to actor member" msgstr "アクターメンバーにグループを追加するときにエラーが発生しました" msgid "Error when creating group" msgstr "グループを作成するときにエラーが発生" msgid "Error when creating user" msgstr "利用者登録時のエラー" msgid "Error when deleting groups" msgstr "グループを削除するときにエラーが発生" msgid "Error when deleting users" msgstr "利用者削除時のエラー" msgid "Error when pausing BPM services" msgstr "BPM サービスを一時停止するときにエラーが発生しました。" msgid "Error when resuming BPM services" msgstr "BPM サービスを再開するときにエラーが発生しました。" msgid "Error when searching users" msgstr "利用者検索時のエラー" msgid "Error when updating %activityId% activity variables" msgstr "アクティビティ:%activityId% の変数を更新中にエラーが発生しました" msgid "Error when updating group" msgstr "グループを更新するときにエラーが発生" msgid "Error when updating process deployment informations" msgstr "プロセスのデプロイ情報を更新中にエラーが発生しました" msgid "Error when updating user" msgstr "利用者を更新時のエラー" msgid "Filters are not supported by this API" msgstr "この API では、フィルターはサポートされていません" msgid "Json can't be mapped to " msgstr "Json を次にマップできません" msgid "Login failed. No profile has been set up for this user. Contact your administrator." msgstr "ログインに失敗しました。この利用者のプロファイルが設定されていません。BPMのシステム管理者に問い合わせてください。" msgid "Login form" msgstr "ログイン フォーム" msgid "MM/dd/yyyy" msgstr "yyyy/MM/dd" msgid "MM/dd/yyyy h:mm a" msgstr "yyyy/MM/dd HH:mm" msgid "MMMM dd, yyyy" msgstr "yyyy年MM月dd日" msgid "Only the value attribute can be updated" msgstr "Value 属性のみを更新することができます" msgid "Password" msgstr "パスワード" msgid "Password must be at least %number% characters long" msgstr "パスワードは少なくとも、%number% 文字にする必要があります" msgid "Password must contain at least %number% digits" msgstr "パスワードは少なくとも、%number% 文字である必要があります" msgid "Password must contain at least %number% lower case characters" msgstr "パスワードは少なくとも、 %number% 文字の小文字を含める必要があります" msgid "Password must contain at least %number% special characters" msgstr "パスワードは少なくとも、%number% 文字の特殊文字を含める必要があります" msgid "Password must contain at least %number% upper case characters" msgstr "パスワードは少なくとも、 %number% 文字の大文字を含める必要があります" msgid "Please, contact your administrator." msgstr "BPMのシステム管理者に問い合わせてください。" msgid "Process %appName% in version %version% already exists" msgstr "プロセス: %appName% バージョン %version% では既に存在します" msgid "Process %appName% in version %version% contains 6.x Legacy artifacts (forms or case overview page). Those are based on Google Web Toolkit (GWT), a technology that is no longer supported by Bonita. To know more, check the documentation." msgstr "バージョンのプロセスには、6.xレガシーアーティファクト(フォームまたはケースの概要ページ)が含まれています。これらはGoogle Web Toolkit (GWT)に基づきBonitaでサポートされなくなったテクノロジーです。詳細については、ドキュメントを確認してください。" msgid "Process definition not found for id %processId%" msgstr "ID: %processId% のプロセス定義 が見つかりません" msgid "Profile member already exists" msgstr "プロファイルのメンバーは既に存在します" msgid "Request parameter \"id\" must be set." msgstr "リクエスト パラメータ \"id\" を設定する必要があります。" msgid "Search terms are not supported by this API" msgstr "その検索用語は、このAPI ではサポートされていません" msgid "Session expired. Please log in again." msgstr "セッションが切れました。再度ログインしてください。" msgid "Sorting is not supported by this API" msgstr "この API では、並べ替えはサポートされていません" msgid "The only mandatory filter is %name%" msgstr "唯一の必須のフィルターは、%name% です。" msgid "The server is not available" msgstr "サーバーは利用できません。" msgid "This category has already been added to this process" msgstr "このカテゴリは既にこのプロセスに追加されました" msgid "This group has already been mapped to actor" msgstr "このグループは、既にアクターにアサインされています" msgid "This membership has already been mapped to actor" msgstr "この所属は、既にアクターにアサインされています" msgid "This membership is already added to user" msgstr "この所属は既にユーザーに追加されています。" msgid "This role has already been mapped to actor" msgstr "この役割は、既にアクターにアサインされています" msgid "This user has already been mapped to actor" msgstr "このユーザーは既にアクターにアサインされています。" msgid "Too many failed login attempts. Please try again later." msgstr "ログイン試行回数が多すぎます。しばらくしてからもう一度お試しください。" msgid "Unable to deploy business archive" msgstr "ビジネス アーカイブをデプロイすることはできません。" msgid "Unable to disable process" msgstr "プロセスを無効にすることはできません。" msgid "Unable to enable process" msgstr "プロセスを有効にすることはできません。" msgid "Unable to find data instance %dataName% for activity %activityId%" msgstr "アクティビティ:%activityId% のデータ・インスタンス:%dataName% を見つけることができません" msgid "Unable to find role %roleId%" msgstr "ロール %roleId% が見つかりません" msgid "Unable to get group path, group not found" msgstr "グループが見つからないため、グループのパスを取得することができません" msgid "Unable to get process data definitions, process %processId% not found" msgstr "プロセスのデータ定義を取得できません。 プロセス:%processId% が見つかりません" msgid "Unable to log in. Please check your username and password." msgstr "ログインできません。ユーザー名とパスワードを確認してください。" msgid "User" msgstr "利用者" msgid "User not found" msgstr "利用者が見つかりません" msgid "Welcome to" msgstr "ようこそ" msgid "message" msgstr "Message" msgid "or" msgstr "または" ================================================ FILE: bpm/bonita-web-server/src/main/resources/i18n/portal_pt_BR.po ================================================ msgid "" msgstr "" "Project-Id-Version: bonita\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-03-19 17:28+0000\n" "PO-Revision-Date: 2024-01-01 00:00\n" "Last-Translator: \n" "Language-Team: Portuguese, Brazilian\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: bonita\n" "X-Crowdin-Project-ID: 13316\n" "X-Crowdin-Language: pt-BR\n" "X-Crowdin-File: /dev/bonita-web/portal/portal.po\n" "X-Crowdin-File-ID: 60581\n" msgid "%attribute% file format is not allowed. Only %file_formats% files are allowed." msgstr "formato de arquivo %attribute% não é permitido. Apenas arquivos %file_formats% são permitidos." msgid "%attribute% file format not allowed or not starting with correct servlet path" msgstr "" msgid "%attribute% is mandatory" msgstr "%attribute% é obrigatório" msgid "%attribute% is not a valid Color value" msgstr "%attribute% não é um valor válido de cor" msgid "%attribute% is not a valid URL" msgstr "%attribute% não é um URL válido" msgid "%attribute% is not a valid email" msgstr "%attribute% não é um e-mail válido" msgid "%attribute% is not well written" msgstr "%attribute% não está bem escrito" msgid "%attribute% must be a boolean value" msgstr "%attribute% deve ser um valor booleano" msgid "%attribute% must be a numeric value" msgstr "%attribute% deve ser um valor numérico" msgid "%attribute% must be an integer value" msgstr "%attribute% deve ser um valor inteiro" msgid "%attribute% must be less or equal than %value%" msgstr "%attribute% deve ser menor ou igual do que %value%" msgid "%attribute% must be less than %value%" msgstr "%attribute% deve ser menor que %value%" msgid "%attribute% must be on a single line" msgstr "%attribute% deve ser uma única linha" msgid "%attribute% must be one of {%list%}" msgstr "%attribute% deve ser um dos {%list%}" msgid "%className% is not Serializable" msgstr "%className% não é serializável" msgid "%className% not found. Only jdk types are supported" msgstr "%className% não encontrado. Somente tipos de jdk são suportados" msgid "%value% is not a valid value for %className%" msgstr "%value% não é um valor válido para %className%" msgid "A group with the name %groupName% already exists" msgstr "Um grupo com o nome %groupName% já existe" msgid "An error occurred while creating a definition" msgstr "Ocorreu um erro ao criar uma definição" msgid "An error occurred while deleting an item with the id %id%" msgstr "Ocorreu um erro ao excluir um item com o id %id%" msgid "Bonita Applications" msgstr "Aplicações Bonita BPM" msgid "Can't create group. Group '%groupName%' already exists" msgstr "Não é possível criar o grupo. Grupo '%groupName%' já existe" msgid "Can't create role. Role '%roleName%' already exists" msgstr "Não é possível criar a função. Função '%roleName%' já existe" msgid "Can't create user. User '%userName%' already exists" msgstr "Não é possível criar o usuário. Usuário '%userName%' já existe" msgid "Can't import Applications." msgstr "Não foi possível importar aplicações." msgid "Can't import applications. An application '%token%' already exists" msgstr "Não é possível importar as aplicações. Uma aplicação 'token %' já existe" msgid "Can't import organization" msgstr "Não é possível importar organização" msgid "Can't import organization. Please check that your file is well-formed." msgstr "Não é possível importar a organização. Por favor, verifique se o arquivo está bem formatado." msgid "Can't parse json, non-well formed content" msgstr "Impossível analisar json, o conteúdo não é bem formado" msgid "Can't start process %processId%, user %userId% not found" msgstr "Impossível iniciar o processo %processId%, usuário %userId% não encontrado" msgid "Can't start process, process %processId% is not enabled" msgstr "Não pode iniciar o processo, o processo %processId% não está ativado" msgid "Can't start process, process %processId% not found" msgstr "Não pode iniciar processo, o processo %processId% não foi encontrado" msgid "Can't update user. User not found" msgstr "Impossível atualizar usuário. Usuário não foi encontrado" msgid "Category with name %categoryName% already exists" msgstr "Já existe a categoria com o nome %categoryName%" msgid "Data definition %dataName% doesn't exists for process %processId%" msgstr "Definição do dado %dataName% não existe para o processo %processId%" msgid "Error during Application import file reading." msgstr "Erro na leitura do arquivo de importação de aplicações." msgid "Error occurred when starting process %processId%" msgstr "Ocorreu um erro ao iniciar o processo %processId%" msgid "Error occurred when starting process %processId%. Case creation limit reached." msgstr "Ocorreu um erro ao iniciar o processo %processId%. Limite de criação de casos atingido." msgid "Error uploading the file. Maybe your session expired. You can try to refresh the page." msgstr "Erro ao carregar o arquivo. Talvez sua sessão tenha expirado. Você pode tentar atualizar a página." msgid "Error when adding group to actor member" msgstr "Erro ao adicionar grupo como membro do ator" msgid "Error when creating group" msgstr "Erro ao criar grupo" msgid "Error when creating user" msgstr "Erro ao criar o usuário" msgid "Error when deleting groups" msgstr "Error when deleting groups" msgid "Error when deleting users" msgstr "Erro ao remover usuários" msgid "Error when pausing BPM services" msgstr "Erro ao suspender os serviços BPM" msgid "Error when resuming BPM services" msgstr "Erro ao relançar os serviços BPM" msgid "Error when searching users" msgstr "Erro ao pesquisar usuários" msgid "Error when updating %activityId% activity variables" msgstr "Erro ao atualizar as variáveis de atividade %activityId%" msgid "Error when updating group" msgstr "Erro ao atualizar o grupo" msgid "Error when updating process deployment informations" msgstr "Erro ao atualizar informações de implantação do processo" msgid "Error when updating user" msgstr "Erro ao atualizar usuário" msgid "Filters are not supported by this API" msgstr "Filtros não são suportados nesta API" msgid "Json can't be mapped to " msgstr "Json não pode ser mapeado para " msgid "Login failed. No profile has been set up for this user. Contact your administrator." msgstr "O seu login falhou. Nenhum perfil foi criado para este usuário. Contate o administrador." msgid "Login form" msgstr "Formulário de login" msgid "MM/dd/yyyy" msgstr "dd/MM/yyyy" msgid "MM/dd/yyyy h:mm a" msgstr "MM/dd/yyyy hh:mm a" msgid "MMMM dd, yyyy" msgstr "MMMM dd, yyyy" msgid "Only the value attribute can be updated" msgstr "Apenas o valor do atributo pode ser atualizado" msgid "Password" msgstr "Senha " msgid "Password must be at least %number% characters long" msgstr "Senha deve ter pelo menos %number% caracteres" msgid "Password must contain at least %number% digits" msgstr "Senha deve conter pelo menos dígitos %number%" msgid "Password must contain at least %number% lower case characters" msgstr "Senha deve conter pelo menos %number% minúsculas caracteres" msgid "Password must contain at least %number% special characters" msgstr "A senha deve conter pelo menos %number% caracteres especiais" msgid "Password must contain at least %number% upper case characters" msgstr "A senha deve conter pelo menos %number% letras maiúsculas" msgid "Please, contact your administrator." msgstr "Por favor, contate o administrador." msgid "Process %appName% in version %version% already exists" msgstr "Processo %appName% na versão %version% já existe" msgid "Process %appName% in version %version% contains 6.x Legacy artifacts (forms or case overview page). Those are based on Google Web Toolkit (GWT), a technology that is no longer supported by Bonita. To know more, check the documentation." msgstr "Processo %appName% na versão %version% contém artefatos de legado 6. x (formulários ou página visão geral do caso). Aqueles são baseados no Google Web Toolkit (GWT), uma tecnologia que não é mais suportada por Bonita. Para saber mais, consulte a documentação." msgid "Process definition not found for id %processId%" msgstr "Definição de processo não encontrada para o id %processId%" msgid "Profile member already exists" msgstr "Membro do perfil já existe" msgid "Request parameter \"id\" must be set." msgstr "O parâmetro id\" da interrogação deve ser definido." msgid "Search terms are not supported by this API" msgstr "Termos de pesquisa não são suportados nesta API" msgid "Session expired. Please log in again." msgstr "Sessão expirada. Por favor, conecte-se novamente." msgid "Sorting is not supported by this API" msgstr "Ordenação não é suportada nesta API" msgid "The only mandatory filter is %name%" msgstr "O único filtro obrigatório é %name%" msgid "The server is not available" msgstr "O servidor não está disponível" msgid "This category has already been added to this process" msgstr "Esta categoria já foi adicionada a este processo" msgid "This group has already been mapped to actor" msgstr "Este grupo já foi designado como ator" msgid "This membership has already been mapped to actor" msgstr "Esta associação já foi designada como ator" msgid "This membership is already added to user" msgstr "Esta associação já foi adicionada ao usuário" msgid "This role has already been mapped to actor" msgstr "Esta função já foi designada como ator" msgid "This user has already been mapped to actor" msgstr "Este usuário já foi designado como ator" msgid "Too many failed login attempts. Please try again later." msgstr "Muitas tentativas de login falharam. Por favor, tente novamente mais tarde." msgid "Unable to deploy business archive" msgstr "Não foi possível instalar o arquivo" msgid "Unable to disable process" msgstr "Impossível desativar processo" msgid "Unable to enable process" msgstr "Impossível ativar processo" msgid "Unable to find data instance %dataName% for activity %activityId%" msgstr "Impossível encontrar dados de instância %dataName% da atividade %activityId%" msgid "Unable to find role %roleId%" msgstr "Não é possível localizar a função %roleId%" msgid "Unable to get group path, group not found" msgstr "Não é possível obter o caminho do grupo, grupo não encontrado" msgid "Unable to get process data definitions, process %processId% not found" msgstr "Impossível obter definições de dados do processo, processo %processId% não foi encontrado" msgid "Unable to log in. Please check your username and password." msgstr "Não foi possível logar. Por favor, verifique seu nome de usuário e senha." msgid "User" msgstr "Usuário" msgid "User not found" msgstr "Usuário não foi encontrado" msgid "Welcome to" msgstr "Bem-vindo ao" msgid "message" msgstr "mensagem" msgid "or" msgstr "ou" ================================================ FILE: bpm/bonita-web-server/src/main/resources/i18n/resources_es.po ================================================ msgid "" msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Project-Id-Version: bonita\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: bonita\n" "X-Crowdin-Project-ID: 13316\n" "X-Crowdin-Language: es-ES\n" "X-Crowdin-File: /dev/bonita-web/distrib/resources.pot\n" "X-Crowdin-File-ID: 61547\n" "Language-Team: Spanish\n" "Language: es_ES\n" "PO-Revision-Date: 2024-01-01 00:00\n" msgid "Add" msgstr "Añadir" msgid "Attention" msgstr "Atención" msgid "Example:" msgstr "Ejemplo:" msgid "Execute" msgstr "Ejecutar" msgid "Expecting a {} value." msgstr "Esperando un valor {}." msgid "Not a valid number!" msgstr "No es un número válido!" msgid "Select a file" msgstr "Seleccionar un archivo" msgid "Start" msgstr "Inicio" msgid "no description defined in contract for this input" msgstr "No hay descripción definida en el contrato para esta entrada" msgid "remove" msgstr "Eliminar" msgid "this is a temporary form generated automatically for testing. Before you put your process into production, create and map the necessary forms." msgstr "Este es un formulario temporal generado automáticamente para la prueba. Antes de poner el proceso en producción, cree y asigne los formularios necesarios." ================================================ FILE: bpm/bonita-web-server/src/main/resources/i18n/resources_fr.po ================================================ msgid "" msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Project-Id-Version: bonita\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Crowdin-Project: bonita\n" "X-Crowdin-Project-ID: 13316\n" "X-Crowdin-Language: fr\n" "X-Crowdin-File: /dev/bonita-web/distrib/resources.pot\n" "X-Crowdin-File-ID: 61547\n" "Language-Team: French\n" "Language: fr_FR\n" "PO-Revision-Date: 2024-01-01 00:00\n" msgid "Add" msgstr "Ajouter" msgid "Attention" msgstr "Attention" msgid "Example:" msgstr "Exemple :" msgid "Execute" msgstr "Exécuter" msgid "Expecting a {} value." msgstr "Attend une valeur {}." msgid "Not a valid number!" msgstr "Nombre invalide !" msgid "Select a file" msgstr "Sélectionnez un fichier" msgid "Start" msgstr "Démarrer" msgid "no description defined in contract for this input" msgstr "Cet input n'a pas de description dans le contrat" msgid "remove" msgstr "Enlever" msgid "this is a temporary form generated automatically for testing. Before you put your process into production, create and map the necessary forms." msgstr "il s'agit d'un formulaire temporaire généré automatiquement à des fins de test. Avant de passer votre processus en production, vous devrez créer les formulaires définitifs et les associer au processus et aux tâches humaines." ================================================ FILE: bpm/bonita-web-server/src/main/resources/i18n/resources_ja.po ================================================ msgid "" msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Project-Id-Version: bonita\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Crowdin-Project: bonita\n" "X-Crowdin-Project-ID: 13316\n" "X-Crowdin-Language: ja\n" "X-Crowdin-File: /dev/bonita-web/distrib/resources.pot\n" "X-Crowdin-File-ID: 61547\n" "Language-Team: Japanese\n" "Language: ja_JP\n" "PO-Revision-Date: 2024-01-01 00:00\n" msgid "Add" msgstr "追加" msgid "Attention" msgstr "注意" msgid "Example:" msgstr "例:" msgid "Execute" msgstr "実行" msgid "Expecting a {} value." msgstr "{} の値を期待しています。" msgid "Not a valid number!" msgstr "有効な数値ではないです" msgid "Select a file" msgstr "ファイルを選択します。" msgid "Start" msgstr "開始" msgid "no description defined in contract for this input" msgstr "この入力項目のコントラクトには、説明が定義されていません" msgid "remove" msgstr "削除" msgid "this is a temporary form generated automatically for testing. Before you put your process into production, create and map the necessary forms." msgstr "これはテスト用に自動生成された一時的なフォームです。本番環境にプロセスを移行する前に必要なフォームを作成し、マップしてください。" ================================================ FILE: bpm/bonita-web-server/src/main/resources/i18n/resources_pt_BR.po ================================================ msgid "" msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Project-Id-Version: bonita\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Crowdin-Project: bonita\n" "X-Crowdin-Project-ID: 13316\n" "X-Crowdin-Language: pt-BR\n" "X-Crowdin-File: /dev/bonita-web/distrib/resources.pot\n" "X-Crowdin-File-ID: 61547\n" "Language-Team: Portuguese, Brazilian\n" "Language: pt_BR\n" "PO-Revision-Date: 2024-01-01 00:00\n" msgid "Add" msgstr "Adicionar" msgid "Attention" msgstr "Atenção" msgid "Example:" msgstr "Exemplo:" msgid "Execute" msgstr "Executar" msgid "Expecting a {} value." msgstr "Um valor {} é esperado." msgid "Not a valid number!" msgstr "Não é um número válido!" msgid "Select a file" msgstr "Selecione um arquivo" msgid "Start" msgstr "Iniciar" msgid "no description defined in contract for this input" msgstr "nenhuma descrição definida no contrato para esta entrada" msgid "remove" msgstr "remover" msgid "this is a temporary form generated automatically for testing. Before you put your process into production, create and map the necessary forms." msgstr "este é um formulário temporário gerado automaticamente para teste. Antes de colocar o processo em produção, crie e mapeie os formulários necessários." ================================================ FILE: bpm/bonita-web-server/src/main/webapp/403.jsp ================================================ <%-- Copyright (C) 2022 BonitaSoft S.A. BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2.0 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . --%> <%@page contentType="text/html; charset=UTF-8"%> Error 403

      Oh no...

      403 error

      You do not have access to this page or resource.

      403 Forbidden

      ================================================ FILE: bpm/bonita-web-server/src/main/webapp/404.jsp ================================================ <%-- Copyright (C) 2022 BonitaSoft S.A. BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2.0 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . --%> <%@page contentType="text/html; charset=UTF-8"%> Error 404

      Oops!

      404 error

      The page you are looking for doesn't exist. The page may have moved or you may have mistyped the address.

      404 Page not found

      ================================================ FILE: bpm/bonita-web-server/src/main/webapp/500.jsp ================================================ <%-- Copyright (C) 2022 BonitaSoft S.A. BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2.0 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . --%> <%@page contentType="text/html; charset=UTF-8"%> Error 500

      Sorry...

      It's not you, it's us.

      500 error

      The server is currently unable to handle this request. Please be patient or try again later.

      500 Internal server error

      ================================================ FILE: bpm/bonita-web-server/src/main/webapp/503.jsp ================================================ <%-- Copyright (C) 2023 BonitaSoft S.A. BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2.0 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . --%> <%@page contentType="text/html; charset=UTF-8"%> Error 503

      Under maintenance...

      Please try again later.

      503 error

      The server is currently unable to handle this request due to scheduled maintenance.

      503 Service Unavailable

      ================================================ FILE: bpm/bonita-web-server/src/main/webapp/WEB-INF/errors.html ================================================ Error %s
      ================================================ FILE: bpm/bonita-web-server/src/main/webapp/WEB-INF/urlrewrite.xml ================================================ ^(/portal/custom-page)?/API/bpm/activity([\?/]|$) /APIToolkit/bpm/activity$2 ^(/portal/custom-page)?/API/bpm/actor([\?/]|$) /APIToolkit/bpm/actor$2 ^(/portal/custom-page)?/API/bpm/actorMember([\?/]|$) /APIToolkit/bpm/actorMember$2 ^(/portal/custom-page)?/API/bpm/archivedActivity([\?/]|$) /APIToolkit/bpm/archivedActivity$2 ^(/portal/custom-page)?/API/bpm/archivedCase([\?]|$) /APIToolkit/bpm/archivedCase$2 ^(/portal/custom-page)?/API/bpm/archivedCase/([^/]*)$ /APIToolkit/bpm/archivedCase/$2 ^(/portal/custom-page)?/API/bpm/archivedComment([\?/]|$) /APIToolkit/bpm/archivedComment$2 ^(/portal/custom-page)?/API/bpm/archivedConnectorInstance([\?/]|$) /APIToolkit/bpm/archivedConnectorInstance$2 ^(/portal/custom-page)?/API/bpm/archiveddocument([\?/]|$) /APIToolkit/bpm/archiveddocument$2 ^(/portal/custom-page)?/API/bpm/archivedFlowNode([\?/]|$) /APIToolkit/bpm/archivedFlowNode$2 ^(/portal/custom-page)?/API/bpm/archivedHumanTask([\?/]|$) /APIToolkit/bpm/archivedHumanTask$2 ^(/portal/custom-page)?/API/bpm/archivedTask([\?/]|$) /APIToolkit/bpm/archivedTask$2 ^(/portal/custom-page)?/API/bpm/archivedUserTask([\?]|$) /APIToolkit/bpm/archivedUserTask$2 ^(/portal/custom-page)?/API/bpm/archivedUserTask/([^/]*)$ /APIToolkit/bpm/archivedUserTask/$2 ^(/portal/custom-page)?/API/bpm/case([\?]|$) /APIToolkit/bpm/case$2 ^(/portal/custom-page)?/API/bpm/case/([^/]*)$ /APIToolkit/bpm/case/$2 ^(/portal/custom-page)?/API/bpm/caseDocument([\?/]|$) /APIToolkit/bpm/caseDocument$2 ^(/portal/custom-page)?/API/bpm/archivedCaseDocument([\?/]|$) /APIToolkit/bpm/archivedCaseDocument$2 ^(/portal/custom-page)?/API/bpm/caseVariable([\?/]|$) /APIToolkit/bpm/caseVariable$2 ^(/portal/custom-page)?/API/bpm/category([\?/]|$) /APIToolkit/bpm/category$2 ^(/portal/custom-page)?/API/bpm/comment([\?/]|$) /APIToolkit/bpm/comment$2 ^(/portal/custom-page)?/API/bpm/connectorInstance([\?/]|$) /APIToolkit/bpm/connectorInstance$2 ^(/portal/custom-page)?/API/bpm/delegation([\?/]|$) /APIToolkit/bpm/delegation$2 ^(/portal/custom-page)?/API/bpm/document([\?/]|$) /APIToolkit/bpm/document$2 ^(/portal/custom-page)?/API/bpm/flowNode([\?/]|$) /APIToolkit/bpm/flowNode$2 ^(/portal/custom-page)?/API/bpm/humanTask([\?/]|$) /APIToolkit/bpm/humanTask$2 ^(/portal/custom-page)?/API/bpm/process([\?]|$) /APIToolkit/bpm/process$2 ^(/portal/custom-page)?/API/bpm/process/([^/]*)$ /APIToolkit/bpm/process/$2 Rewrite process instantiation URLs to Spring MVC internal endpoint ^(/portal.*)?/API/bpm/process/([^/]+)/instantiation$ /APISpringInternal/bpm/process/$2/instantiation Rewrite process design URLs to Spring MVC internal endpoint ^(/portal.*)?/API/bpm/process/([^/]+)/design$ /APISpringInternal/bpm/process/$2/design Rewrite process contract URLs to Spring MVC internal endpoint ^(/portal.*)?/API/bpm/process/([^/]+)/contract$ /APISpringInternal/bpm/process/$2/contract Rewrite user task contract URLs to Spring MVC internal endpoint ^(/portal.*)?/API/bpm/userTask/([^/]+)/contract$ /APISpringInternal/bpm/userTask/$2/contract Rewrite user task context URLs to Spring MVC internal endpoint ^(/portal.*)?/API/bpm/userTask/([^/]+)/context$ /APISpringInternal/bpm/userTask/$2/context Rewrite user task execution URLs to Spring MVC internal endpoint ^(/portal.*)?/API/bpm/userTask/([^/]+)/execution$ /APISpringInternal/bpm/userTask/$2/execution Rewrite archived user task context URLs to Spring MVC internal endpoint ^(/portal.*)?/API/bpm/archivedUserTask/([^/]+)/context$ /APISpringInternal/bpm/archivedUserTask/$2/context Rewrite case context URLs to Spring MVC internal endpoint ^(/portal.*)?/API/bpm/case/([^/]+)/context$ /APISpringInternal/bpm/case/$2/context Rewrite archived case context URLs to Spring MVC internal endpoint ^(/portal.*)?/API/bpm/archivedCase/([^/]+)/context$ /APISpringInternal/bpm/archivedCase/$2/context ^(/portal/custom-page)?/API/bpm/processCategory([\?/]|$) /APIToolkit/bpm/processCategory$2 ^(/portal/custom-page)?/API/bpm/processConnector([\?/]|$) /APIToolkit/bpm/processConnector$2 ^(/portal/custom-page)?/API/bpm/processConnectorDependency([\?/]|$) /APIToolkit/bpm/processConnectorDependency$2 ^(/portal/custom-page)?/API/bpm/processParameter([\?/]|$) /APIToolkit/bpm/processParameter$2 ^(/portal/custom-page)?/API/bpm/processResolutionProblem([\?/]|$) /APIToolkit/bpm/processResolutionProblem$2 ^(/portal/custom-page)?/API/bpm/task([\?/]|$) /APIToolkit/bpm/task$2 ^(/portal/custom-page)?/API/bpm/userTask([\?]|$) /APIToolkit/bpm/userTask$2 ^(/portal/custom-page)?/API/bpm/userTask/([^/]*)$ /APIToolkit/bpm/userTask/$2 ^(/portal/custom-page)?/API/customuserinfo/definition([\?/]|$) /APIToolkit/customuserinfo/definition$2 ^(/portal/custom-page)?/API/customuserinfo/user([\?/]|$) /APIToolkit/customuserinfo/user$2 ^(/portal/custom-page)?/API/customuserinfo/value([\?/]|$) /APIToolkit/customuserinfo/value$2 ^(/portal/custom-page)?/API/identity/group([\?/]|$) /APIToolkit/identity/group$2 ^(/portal/custom-page)?/API/identity/membership([\?/]|$) /APIToolkit/identity/membership$2 ^(/portal/custom-page)?/API/identity/personalcontactdata([\?/]|$) /APIToolkit/identity/personalcontactdata$2 ^(/portal/custom-page)?/API/identity/professionalcontactdata([\?/]|$) /APIToolkit/identity/professionalcontactdata$2 ^(/portal/custom-page)?/API/identity/role([\?/]|$) /APIToolkit/identity/role$2 ^(/portal/custom-page)?/API/identity/user([\?/]|$) /APIToolkit/identity/user$2 ^(/portal/custom-page)?/API/living/application-page([\?/]|$) /APIToolkit/living/application-page$2 ^(/portal/custom-page)?/API/living/application-menu([\?/]|$) /APIToolkit/living/application-menu$2 ^(/portal/custom-page)?/API/living/application([\?/]|$) /APIToolkit/living/application$2 ^(/portal/custom-page)?/API/platform/platform([\?/]|$) /APIToolkit/platform/platform$2 ^(/portal/custom-page)?/API/portal/page([\?/]|$) /APIToolkit/portal/page$2 ^(/portal/custom-page)?/API/portal/profile([\?/]|$) /APIToolkit/portal/profile$2 ^(/portal/custom-page)?/API/portal/profileMember([\?/]|$) /APIToolkit/portal/profileMember$2 ^(/portal/custom-page)?/API/system/i18nlocale([\?/]|$) /APIToolkit/system/i18nlocale$2 ^(/portal/custom-page)?/API/system/session([\?/]|$) /APIToolkit/system/session$2 Deal with custom page resources URL backward compatibility. Also there is a bug with qsappend="true" which always append query string using a &. Therefor the ?fix=true is used to fix that issue. ^/portal/custom-page/(.*)/pageResource$ %{context-path}/portal/pageResource?fix=true ^/portal/custom-page/(.*)/fileUpload$ /portal/fileUpload ^/portal/custom-page/(.*)/documentDownload?(.*)$ /portal/documentDownload?$2 ^/portal/resource/app/([^\/]+)/([^\/]+)/content/pageResource?(.*)$ /portal/pageResource?$3 ^/portal/resource/app/([^\/]+)/([^\/]+)/content/fileUpload$ /portal/fileUpload ^/portal/resource/app/([^\/]+)/([^\/]+)/content/documentDownload?(.*)$ /portal/documentDownload?$3 ^/portal/homepage /apps/appDirectoryBonita ================================================ FILE: bpm/bonita-web-server/src/main/webapp/WEB-INF/web.xml ================================================ Bonita 500 /error/500 503 /error/503 403 /error/403 404 /error/404 RequestIdFilter org.bonitasoft.console.common.server.filter.RequestIdFilter NoCacheFilter org.bonitasoft.console.common.server.filter.NoCacheFilter FrameSecurityFilter org.bonitasoft.console.common.server.login.filter.FrameSecurityFilter X-Frame-Options SAMEORIGIN Content-Security-Policy frame-ancestors 'self'; ContentTypeSecurityFilter org.bonitasoft.console.common.server.login.filter.ContentTypeSecurityFilter X-Content-Type-Options nosniff RestAPIAuthorizationFilter org.bonitasoft.console.common.server.login.filter.RestAPIAuthorizationFilter TokenGeneratorFilter org.bonitasoft.console.common.server.login.filter.TokenGeneratorFilter TokenValidatorFilter org.bonitasoft.console.common.server.login.filter.TokenValidatorFilter AuthenticationFilter org.bonitasoft.console.common.server.login.filter.AuthenticationFilter redirectWhenUnauthorized true RedirectFilter org.bonitasoft.console.common.server.filter.RedirectFilter SanitizerFilter org.bonitasoft.console.common.server.filter.SanitizerFilter CacheFilter org.bonitasoft.console.common.server.filter.CacheFilter duration 36000 alwaysCaching true CustomPageCacheFilter org.bonitasoft.console.common.server.filter.CacheFilter duration 36000 alwaysCaching false UrlRewriteFilter org.tuckey.web.filters.urlrewrite.UrlRewriteFilter logLevel slf4j RequestIdFilter /* REQUEST FORWARD NoCacheFilter /portal/formsDocumentDownload /portal/formsDocumentImage /portal/downloadDocument /portal/documentDownload /portal/runreport /API/* /APIToolkit/* /APISpringInternal/* /portal/custom-page/API/* /portal.js/index.html /portal/exportOrganization /portal/pageDownload /portal/exportActors REQUEST FORWARD FrameSecurityFilter /* REQUEST FORWARD ContentTypeSecurityFilter /* REQUEST FORWARD TokenValidatorFilter /API/* /APIToolkit/* /APISpringInternal/* /portal/custom-page/API/* /portal/resource/* /apps/* REQUEST FORWARD AuthenticationFilter /portal/* /portal.js/* /apps/* /services/* REQUEST FORWARD RedirectFilter /apps/sendRedirect REQUEST FORWARD RestAPIAuthorizationFilter /API/* /APIToolkit/* /APISpringInternal/* /portal/custom-page/API/* /services/* /portal/formsDocumentDownload /portal/documentDownload /portal/downloadDocument /portal/pageDownload /portal/exportOrganization /portal/fileUpload /portal/processUpload /portal/organizationUpload /portal/actorsUpload /portal/applicationsUpload /portal/pageUpload /portal/imageUpload REQUEST FORWARD INCLUDE TokenGeneratorFilter /API/system/session/* /APIToolkit/system/session/* /portal/custom-page/API/system/session/* FORWARD SanitizerFilter /API/* /APIToolkit/* /APISpringInternal/* REQUEST FORWARD CacheFilter /login.jsp /platformloginservice /platformlogoutservice /API/system/i18ntranslation /API/avatars/* /API/applicationIcon/* /css /images /portal-theme /portal.js/* REQUEST FORWARD CustomPageCacheFilter /portal/resource/* /portal/resource/app/* /apps/* /portal/custom-page/* REQUEST FORWARD UrlRewriteFilter /* REQUEST FORWARD org.bonitasoft.engine.api.internal.servlet.EngineInitializerListener org.bonitasoft.console.common.server.servlet.PlatformTenantListener errorPageServlet org.bonitasoft.console.common.server.servlet.ErrorPageServlet BonitaRestAPIServlet org.bonitasoft.web.rest.server.BonitaRestAPIServlet SpringRest org.springframework.web.servlet.DispatcherServlet contextClass org.springframework.web.context.support.AnnotationConfigWebApplicationContext contextConfigLocation org.bonitasoft.web.rest.server.SpringWebConfiguration 1 ConsoleServiceServlet org.bonitasoft.console.server.ConsoleServiceServlet fileUploadServlet org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet CheckUploadedFileSize true formFileUploadServlet org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet ContentType json ReturnOriginalFilename true CheckUploadedFileSize true processUploadServlet org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet SupportedExtensions bar apiProcessUploadServlet org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet SupportedExtensions bar ReturnOriginalFilename true ContentType json xmlUploadServlet org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet SupportedExtensions xml zipUploadServlet org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet SupportedExtensions zip imageUploadServlet org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet SupportedExtensions png,jpg,gif,jpeg,bmp,wbmp,tga CheckUploadedImageSize true apiImageUploadServlet org.bonitasoft.console.common.server.servlet.TenantFileUploadServlet SupportedExtensions png,jpg,gif,jpeg,bmp,wbmp,tga ContentType json ReturnOriginalFilename true CheckUploadedImageSize true organizationIconServlet org.bonitasoft.console.common.server.servlet.OrganizationIconServlet applicationIconServlet org.bonitasoft.console.common.server.servlet.ApplicationIconServlet loginService org.bonitasoft.console.common.server.login.servlet.LoginServlet logoutService org.bonitasoft.console.common.server.login.servlet.LogoutServlet platformLoginService org.bonitasoft.console.common.server.login.servlet.PlatformLoginServlet platformLogoutService org.bonitasoft.console.common.server.login.servlet.PlatformLogoutServlet exportOrganizationServlet org.bonitasoft.console.server.servlet.OrganizationExportServlet exportApplicationsServlet org.bonitasoft.console.server.servlet.ApplicationsExportServlet deprecatedDocumentDownloadServlet org.bonitasoft.console.common.server.servlet.DocumentDownloadServlet documentDownload org.bonitasoft.console.common.server.servlet.DocumentDownloadServlet formsDocumentDownload org.bonitasoft.console.common.server.servlet.DocumentDownloadServlet formsDocumentImage org.bonitasoft.console.common.server.servlet.DocumentImageServlet exportOrganizationServlet /portal/exportOrganization /portal/custom-page/API/exportOrganization /API/exportOrganization exportApplicationsServlet /portal/exportApplications exportProcessActorsServlet org.bonitasoft.console.server.servlet.ProcessActorsExportServlet CustomPageServlet org.bonitasoft.console.common.server.page.CustomPageServlet pageDownload org.bonitasoft.console.common.server.page.PageDownloadServlet pageUploadServlet org.bonitasoft.console.common.server.servlet.PageUploadServlet SupportedExtensions zip ReturnOriginalFilename true apiPageUploadServlet org.bonitasoft.console.common.server.servlet.PageUploadServlet SupportedExtensions zip ContentType json ReturnOriginalFilename true ProcessFormServlet org.bonitasoft.console.common.server.form.ProcessFormServlet livingApplicationServlet org.bonitasoft.livingapps.LivingApplicationServlet livingApplicationPageServlet org.bonitasoft.livingapps.LivingApplicationPageServlet PageServlet org.bonitasoft.console.common.server.page.PageServlet HttpAPIServlet org.bonitasoft.engine.api.internal.servlet.HttpAPIServlet errorPageServlet /error/* livingApplicationServlet /apps/* livingApplicationPageServlet /portal/resource/app/* processUploadServlet /portal/processUpload apiProcessUploadServlet /API/processUpload xmlUploadServlet /portal/organizationUpload xmlUploadServlet /portal/applicationsUpload xmlUploadServlet /portal/actorsUpload zipUploadServlet /portal/bdmUpload pageUploadServlet /portal/pageUpload apiPageUploadServlet /API/pageUpload imageUploadServlet /portal/imageUpload apiImageUploadServlet /API/imageUpload fileUploadServlet /portal/fileUpload formFileUploadServlet /API/formFileUpload /portal/custom-page/API/formFileUpload exportProcessActorsServlet /portal/exportActors organizationIconServlet /API/avatars/* /portal/custom-page/API/avatars/* applicationIconServlet /API/applicationIcon/* deprecatedDocumentDownloadServlet /portal/downloadDocument documentDownload /portal/documentDownload /API/documentDownload /portal/custom-page/API/documentDownload formsDocumentDownload /portal/formsDocumentDownload formsDocumentImage /portal/formsDocumentImage /API/formsDocumentImage /portal/custom-page/API/formsDocumentImage loginService /loginservice logoutService /logoutservice platformLoginService /platformloginservice platformLogoutService /platformlogoutservice BonitaRestAPIServlet /APIToolkit/* SpringRest /APISpringInternal/* /portal/custom-page/API/extension/* /API/bdm/businessDataReference/* /API/bdm/businessData/* /API/bpm/activityVariable/* /API/bpm/archivedActivityVariable/* /API/bpm/archivedCaseVariable/* /API/bpm/message /API/bpm/processInfo/* /API/bpm/signal /API/bpm/timerEventTrigger/* /API/extension/* /API/form/mapping /API/system/i18ntranslation /API/system/information /API/system/maintenance /API/tenant/bdm ConsoleServiceServlet /services/* /API/services/* /portal/custom-page/API/services/* CustomPageServlet /portal/custom-page/* pageDownload /portal/pageDownload /API/pageDownload ProcessFormServlet /portal/form/* PageServlet /portal/resource/* HttpAPIServlet /serverAPI/* java:comp/env/RawBonitaDS javax.sql.DataSource Container java:comp/env/bonitaDS javax.sql.DataSource Container java:comp/env/bonitaSequenceManagerDS javax.sql.DataSource Container java:comp/env/RawBusinessDataDS javax.sql.DataSource Container java:comp/env/BusinessDataDS javax.sql.DataSource Container java:comp/env/NotManagedBizDataDS javax.sql.DataSource Container index.html bonita-http-api-url /serverAPI/* bonita-http-api BASIC Restricted access bonita-http-api ================================================ FILE: bpm/bonita-web-server/src/main/webapp/css/error-style.css ================================================ * { margin: 0; padding: 0; } html { font-size: 1rem; } body { background-image: url(../images/background.svg); background-size: cover; background-repeat: no-repeat; } .main-container { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .text-primary { color: #2c3e50; } .error-title { display: flex; justify-content: center; align-items: center; flex-direction: column; } .error-title h1 { font-weight: bold; font-size: 7rem; } .error-title h2 { font-size: 3rem; } .error-message { display: flex; justify-content: center; align-items: center; flex-direction: column; padding: 1rem; text-align: center; } .error-message h2 { font-size: 2rem; } .error-message p { font-size: 1rem; line-height: 1.3rem; padding: 1rem; } .illustration-container { height: 25rem; width: 25rem; position: absolute; top: 0; left: -15rem; z-index: -5; } .illustration-container img { height: 100%; width: 100%; } .illustration-container-403 { left: auto; right: -15rem; } @media screen and (min-width: 768px) and (max-width: 991px) { .illustration-container-403 { left: 18rem; } .illustration-container { height: 20rem; width: 20rem; } } @media screen and (max-width: 767px) { .main-container { width: 100%; } .error-title h1 { font-size: 5rem; } .illustration-container { position: relative; height: 15rem; width: 15rem; margin: 0 auto; right: 0; left: auto; } .error-message h2 { font-size: 1.5rem; } } ================================================ FILE: bpm/bonita-web-server/src/main/webapp/index.html ================================================ ================================================ FILE: bpm/bonita-web-server/src/main/webapp/login.jsp ================================================ <%-- Copyright (C) 2009 BonitaSoft S.A. BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2.0 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . --%> <%@page language="java"%> <%@page contentType="text/html; charset=UTF-8"%> <%@page import="java.net.URLEncoder"%> <%@page import="org.apache.commons.lang3.StringEscapeUtils"%> <%@page import="org.bonitasoft.console.common.server.jsp.JSPUtils"%> <%@page import="org.bonitasoft.console.common.server.jsp.JSPI18n"%> <% JSPUtils JSP = new JSPUtils(request, session); JSPI18n i18n = new JSPI18n(JSP); // Build Action URL String redirectUrl = JSP.getParameter("redirectUrl"); StringBuffer actionUrl = new StringBuffer("loginservice?redirect=true"); StringBuffer styleUrl = new StringBuffer("portal-theme"); if (redirectUrl != null) { actionUrl.append("&redirectUrl=" + URLEncoder.encode(redirectUrl, "UTF-8")); } // Error messages String errorMessage = ""; boolean disableLogin = false; String noBonitaHomeMessage = request.getAttribute("noBonitaHomeMessage") + ""; String noBonitaClientFileMessage = request.getAttribute("noBonitaClientFileMessage") + ""; String loginFailMessage = request.getAttribute("loginFailMessage") + ""; // Technical problems if ( !JSP.getParameter("isPlatformCreated", true) || !JSP.getParameter("isTenantCreated", true) || "tenantNotActivated".equals(loginFailMessage) || "noBonitaHomeMessage".equals(noBonitaHomeMessage) || "noBonitaClientFileMessage".equals(noBonitaClientFileMessage) ) { errorMessage = i18n.t_("The server is not available") + "
      " + i18n.t_("Please, contact your administrator."); disableLogin = true; } // No profile for this user else if ("noProfileForUser".equals(loginFailMessage)) { errorMessage = i18n.t_("Login failed. No profile has been set up for this user. Contact your administrator."); } // Account locked due to too many failed attempts (constant defined in LoginServlet.ACCOUNT_LOCKED_MESSAGE) else if ("accountLockedMessage".equals(loginFailMessage)) { errorMessage = i18n.t_("Too many failed login attempts. Please try again later."); } // Login or password error else if ("loginFailMessage".equals(loginFailMessage)) { errorMessage = i18n.t_("Unable to log in. Please check your username and password."); } %> Bonita Applications

      <%= i18n.t_("Welcome to") %> <%= i18n.t_("Bonita Applications") %>

      <%=i18n.t_("Login form")%>

      <%=errorMessage.length() > 0 ? errorMessage : ""%>

      " placeholder="<%=i18n.t_("User")%>" type="text" autocomplete="off" tabindex="1" maxlength="255" <%=disableLogin ? "disabled=\"disabled\" " : ""%> />
      " <%=disableLogin ? "disabled=\"disabled\" " : ""%> />
      " <%=disableLogin ? "disabled=\"disabled\" " : ""%> />
      ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/FakeI18n.java ================================================ /** * Copyright (C) 2022 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.console.common; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; /** * Created by Vincent Elcrin * Date: 23/09/13 * Time: 18:40 */ public class FakeI18n extends AbstractI18n { private String l10n; public FakeI18n() { I18N_instance = this; } @Override public void loadLocale(LOCALE locale) { } @Override protected String getText(LOCALE locale, String key) { return l10n; } public void setL10n(String value) { l10n = value; } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/model/ImportStatusMessagesTest.java ================================================ /** * Copyright (C) 2022 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.console.common.model; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import org.bonitasoft.console.common.server.i18n.I18n; import org.bonitasoft.engine.api.ImportError; import org.bonitasoft.engine.api.ImportError.Type; import org.bonitasoft.engine.api.ImportStatus; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.InvalidSessionException; import org.bonitasoft.web.common.model.ImportStatusMessages; import org.junit.*; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; public class ImportStatusMessagesTest { @BeforeClass public static void initEnvironment() { HashMap availableLocales; availableLocales = new HashMap<>(); availableLocales.put("en", "English"); availableLocales.put("fr", "Français"); availableLocales.put("es", "Español"); availableLocales.put("pt_BR", "Português (Brasil)"); availableLocales.put("ja", "日本語"); I18n i18n = mock(I18n.class); I18n.setInstance(i18n); Mockito.when(i18n.getAvailableLocalesFor(anyString())).thenReturn(availableLocales); } @AfterClass public static void cleanUp() throws Exception { I18n.setInstance(null); } @Before public void init() throws InvalidSessionException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { MockitoAnnotations.initMocks(this); } @Test public void should_put_elements_Added_or_replaced_in_added_map() throws Exception { //given final ImportStatus statusAdded1 = new ImportStatus("statusAdded1"); statusAdded1.setStatus(ImportStatus.Status.ADDED); final ImportStatus statusAdded2 = new ImportStatus("statusAdded2"); statusAdded2.setStatus(ImportStatus.Status.REPLACED); final List importStatus = new ArrayList<>(Arrays.asList(statusAdded1, statusAdded2)); //when final ImportStatusMessages importStatusMessages = new ImportStatusMessages(importStatus); //then assertThat(importStatusMessages.getImported().size()).isEqualTo(2); assertThat(importStatusMessages.getImported().get(0).getName()).isEqualTo("statusAdded1"); assertThat(importStatusMessages.getImported().get(0).getStatusType()) .isEqualTo(ImportStatus.Status.ADDED.name()); assertThat(importStatusMessages.getImported().get(1).getName()).isEqualTo("statusAdded2"); assertThat(importStatusMessages.getImported().get(1).getStatusType()) .isEqualTo(ImportStatus.Status.REPLACED.name()); assertThat(importStatusMessages.getSkipped().size()).isEqualTo(0); assertThat(importStatusMessages.getErrors().size()).isEqualTo(0); } @Test public void should_put_elements_skipped_in_skipped_map() throws Exception { //given final ImportStatus status1 = new ImportStatus("statusSkipped1"); status1.setStatus(ImportStatus.Status.SKIPPED); final ImportStatus status2 = new ImportStatus("statusSkipped2"); status2.setStatus(ImportStatus.Status.SKIPPED); final List importStatus = new ArrayList<>(Arrays.asList(status1, status2)); //when final ImportStatusMessages importStatusMessages = new ImportStatusMessages(importStatus); //then assertThat(importStatusMessages.getSkipped().size()).isEqualTo(2); assertThat(importStatusMessages.getSkipped().get(0).getName()).isEqualTo("statusSkipped1"); assertThat(importStatusMessages.getSkipped().get(0).getStatusType()) .isEqualTo(ImportStatus.Status.SKIPPED.name()); assertThat(importStatusMessages.getSkipped().get(1).getName()).isEqualTo("statusSkipped2"); assertThat(importStatusMessages.getSkipped().get(1).getStatusType()) .isEqualTo(ImportStatus.Status.SKIPPED.name()); assertThat(importStatusMessages.getImported().size()).isEqualTo(0); assertThat(importStatusMessages.getErrors().size()).isEqualTo(0); } @Test public void should_put_elements_With_error_in_error_map() throws Exception { //given final ImportStatus status1 = new ImportStatus("statusError1"); status1.addError(new ImportError("Error1", Type.GROUP)); status1.addError(new ImportError("Error2", Type.GROUP)); status1.addError(new ImportError("Error3", Type.ROLE)); status1.addError(new ImportError("Error4", Type.USER)); final ImportStatus status2 = new ImportStatus("statusError2"); status2.addError(new ImportError("Error1", Type.PAGE)); final List importStatus = new ArrayList<>(Arrays.asList(status1, status2)); //when final ImportStatusMessages importStatusMessages = new ImportStatusMessages(importStatus); //then assertThat(importStatusMessages.getErrors().size()).isEqualTo(2); assertThat(importStatusMessages.getErrors().get(0).getName()).isEqualTo("statusError1"); assertThat(importStatusMessages.getErrors().get(0).getStatusType()).isEqualTo(ImportStatus.Status.ADDED.name()); assertThat(importStatusMessages.getErrors().get(0).getErrors().size()).isEqualTo(3); assertThat(importStatusMessages.getErrors().get(0).getErrors().get(Type.GROUP.name()).get(0)) .isEqualTo("Error1"); assertThat(importStatusMessages.getErrors().get(0).getErrors().get(Type.GROUP.name()).get(1)) .isEqualTo("Error2"); assertThat(importStatusMessages.getErrors().get(0).getErrors().get(Type.ROLE.name()).get(0)) .isEqualTo("Error3"); assertThat(importStatusMessages.getErrors().get(0).getErrors().get(Type.USER.name()).get(0)) .isEqualTo("Error4"); assertThat(importStatusMessages.getErrors().get(1).getName()).isEqualTo("statusError2"); assertThat(importStatusMessages.getErrors().get(1).getStatusType()).isEqualTo(ImportStatus.Status.ADDED.name()); assertThat(importStatusMessages.getErrors().get(1).getErrors().size()).isEqualTo(1); assertThat(importStatusMessages.getErrors().get(1).getErrors().get(Type.PAGE.name()).get(0)) .isEqualTo("Error1"); assertThat(importStatusMessages.getImported().size()).isEqualTo(0); assertThat(importStatusMessages.getSkipped().size()).isEqualTo(0); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/auth/AuthenticationManagerFactoryTest.java ================================================ /** * Copyright (C) 2022 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.console.common.server.auth; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.console.common.server.auth.impl.standard.StandardAuthenticationManagerImpl; import org.junit.Test; /** * @author Rohart Bastien * @author Emmanuel Duchastenier */ public class AuthenticationManagerFactoryTest { @Test public void testGetLoginManager() throws AuthenticationManagerNotFoundException { assertThat(AuthenticationManagerFactory.getAuthenticationManager()).as("Cannot get the login manager") .isNotNull(); } @Test public void default_manager_implementation_should_be_StandardAuthenticationManagerImpl_class() throws AuthenticationManagerNotFoundException { // when: AuthenticationManager managerImpl = AuthenticationManagerFactory.getAuthenticationManager(); // then: assertThat(managerImpl).isInstanceOf(StandardAuthenticationManagerImpl.class); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/auth/AuthenticationManagerPropertiesTest.java ================================================ /** * Copyright (C) 2022 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.console.common.server.auth; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.console.common.server.preferences.properties.ConfigurationFilesManager.getProperties; import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import java.io.IOException; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * @author Rohart Bastien */ public class AuthenticationManagerPropertiesTest { public AuthenticationManagerProperties loginManagerProperties; @Before public void setUp() throws IOException { loginManagerProperties = spy(new AuthenticationManagerProperties()); doReturn(getProperties(("OAuth.serviceProvider = LinkedIn\n" + "OAuth.consumerKey = ove2vcdjptar\n" + "OAuth.consumerSecret = vdaBrCmHvkgJoYz1\n" + "OAuth.callbackURL = http://127.0.0.1:8888/loginservice").getBytes())).when(loginManagerProperties) .getTenantPropertiesOfScope(); } @After public void tearDown() { loginManagerProperties = null; } @Test public void isLogoutDisabled_should_return_FALSE_if_not_set() { // given: final AuthenticationManagerProperties properties = AuthenticationManagerProperties.getProperties(); // when: final boolean isLogoutDisabled = properties.isLogoutDisabled(); // then: assertThat(isLogoutDisabled).isFalse(); } @Test public void testGetOAuthServiceProviderName() { assertNotNull("Cannot get OAuth service provider name", loginManagerProperties.getOAuthServiceProviderName()); } @Test public void testGetOAuthConsumerKey() { assertNotNull("Cannot get OAuth consumer key", loginManagerProperties.getOAuthConsumerKey()); } @Test public void testGetOAuthConsumerSecret() { assertNotNull("Cannot get OAuth consumer secret", loginManagerProperties.getOAuthConsumerSecret()); } @Test public void testGetOAuthCallbackURL() { assertNotNull("Cannot get OAuth callback URL", loginManagerProperties.getOAuthCallbackURL()); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/auth/impl/standard/StandardAuthenticationManagerImplTest.java ================================================ /** * Copyright (C) 2022 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.console.common.server.auth.impl.standard; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.spy; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.mock.web.MockHttpServletRequest; @RunWith(MockitoJUnitRunner.class) public class StandardAuthenticationManagerImplTest { private MockHttpServletRequest request; private HttpServletRequestAccessor requestAccessor; private StandardAuthenticationManagerImpl standardLoginManagerImpl = spy(new StandardAuthenticationManagerImpl()); @Before public void setUp() throws Exception { request = new MockHttpServletRequest(); request.setContextPath("bonita"); requestAccessor = new HttpServletRequestAccessor(request); } @Test public void testGetSimpleLoginpageURL() throws Exception { String redirectUrl = "%2Fapps%2FappDirectoryBonita"; String loginURL = standardLoginManagerImpl.getLoginPageURL(requestAccessor, redirectUrl); assertThat(loginURL).isEqualToIgnoringCase("bonita/login.jsp?redirectUrl=%2Fapps%2FappDirectoryBonita"); } @Test public void testGetLoginpageURLWithLocale() throws Exception { String redirectUrl = "%2Fapps%2FappDirectoryBonita"; request.setParameter("_l", "es"); String loginURL = standardLoginManagerImpl.getLoginPageURL(requestAccessor, redirectUrl); assertThat(loginURL).isEqualToIgnoringCase("bonita/login.jsp?_l=es&redirectUrl=%2Fapps%2FappDirectoryBonita"); } @Test public void testGetLoginpageURLFromPortal() throws Exception { String redirectUrl = "%2Fapps%2FappDirectoryBonita"; request.setServletPath("/portal/"); String loginURL = standardLoginManagerImpl.getLoginPageURL(requestAccessor, redirectUrl); assertThat(loginURL).isEqualToIgnoringCase("bonita/login.jsp?redirectUrl=%2Fapps%2FappDirectoryBonita"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/CacheFilterTest.java ================================================ /** * Copyright (C) 2022 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.console.common.server.filter; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.regex.Pattern; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.assertj.core.api.Assertions; import org.assertj.core.api.Condition; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.Spy; public class CacheFilterTest { @Mock private FilterChain chain; @Mock private HttpServletRequestAccessor request; @Mock private HttpServletRequest httpRequest; @Mock private HttpServletResponse httpResponse; @Mock private HttpSession httpSession; @Mock private FilterConfig filterConfig; @Mock private ServletContext servletContext; @Spy CacheFilter cacheFilter; @Before public void setUp() throws Exception { initMocks(this); doReturn(httpSession).when(request).getHttpSession(); when(request.asHttpServletRequest()).thenReturn(httpRequest); when(httpRequest.getRequestURL()).thenReturn(new StringBuffer()); when(servletContext.getContextPath()).thenReturn(""); when(filterConfig.getServletContext()).thenReturn(servletContext); when(filterConfig.getInitParameterNames()).thenReturn(Collections.emptyEnumeration()); } @Test public void testFilterWithExcludedURL() throws Exception { final String url = "test"; when(httpRequest.getRequestURL()).thenReturn(new StringBuffer(url)); doReturn(true).when(cacheFilter).matchExcludePatterns(url); cacheFilter.init(filterConfig); cacheFilter.doFilter(httpRequest, httpResponse, chain); verify(cacheFilter, times(0)).proceedWithFiltering(httpRequest, httpResponse, chain); verify(cacheFilter, times(1)).excludePatternFiltering(httpRequest, httpResponse, chain); verify(chain, times(1)).doFilter(httpRequest, httpResponse); } @Test public void testMatchExcludePatterns() throws Exception { cacheFilter.init(filterConfig); matchExcludePattern("/apps/home/", true); matchExcludePattern("/apps/home/css/style.css", false); matchExcludePattern("http://localhost:8080/bonita/portal/resource/page/content/", true); matchExcludePattern("http://localhost:8080/bonita/portal/resource/page/content/image/logo.png", false); matchExcludePattern("http://localhost:8080/portal/custom-page/API/identity/user/1", true); matchExcludePattern( "http://localhost:8080/bonita/portal/custom-page/custompage_cacheBustingBug1/?locale=en&profile=101&_f=allpagesfilter&_id=22", true); } @Test public void testCompileNullPattern() throws Exception { cacheFilter.init(filterConfig); assertThat(cacheFilter.compilePattern(null)).isNull(); } @Test public void testCompileWrongPattern() throws Exception { cacheFilter.init(filterConfig); assertThat(cacheFilter.compilePattern("((((")).isNull(); } @Test public void testCompileSimplePattern() throws Exception { cacheFilter.init(filterConfig); final String patternToCompile = "test"; assertThat(cacheFilter.compilePattern(patternToCompile)).isNotNull().has(new Condition<>() { @Override public boolean matches(final Pattern pattern) { return pattern.pattern().equalsIgnoreCase(patternToCompile); } }); } @Test public void testCompileExcludePattern() throws Exception { cacheFilter.init(filterConfig); final String patternToCompile = "^/(bonita/)?(?:(login\\.jsp$)|(images/)|(redirectCasToCatchHash\\.jsp)|(loginservice)|(serverAPI)|(maintenance\\.jsp$)|(API/platform/)|(platformloginservice$)|(portal/scripts)|(/bonita/?$)|(logoutservice))"; assertThat(cacheFilter.compilePattern(patternToCompile)).isNotNull().has(new Condition<>() { @Override public boolean matches(final Pattern pattern) { return pattern.pattern().equalsIgnoreCase(patternToCompile); } }); } @Test public void empty_excludePattern_init_param_should_override_default_excludePattern() throws Exception { final String url = "test"; List initParamsList = new ArrayList<>(); initParamsList.add("excludePattern"); Enumeration initParamsEnum = Collections.enumeration(initParamsList); when(filterConfig.getInitParameterNames()).thenReturn(initParamsEnum); when(filterConfig.getInitParameter("excludePattern")).thenReturn(""); when(httpRequest.getRequestURL()).thenReturn(new StringBuffer(url)); when(httpRequest.getRequestURI()).thenReturn(url); doNothing().when(cacheFilter).proceedWithFiltering(httpRequest, httpResponse, chain); cacheFilter.init(filterConfig); cacheFilter.doFilter(httpRequest, httpResponse, chain); assertThat(cacheFilter.getExcludePattern()).isNull(); verify(cacheFilter, times(1)).proceedWithFiltering(httpRequest, httpResponse, chain); verify(cacheFilter, times(0)).excludePatternFiltering(httpRequest, httpResponse, chain); } private void matchExcludePattern(final String urlToMatch, final Boolean mustMatch) { doReturn(Pattern.compile(CacheFilter.CACHE_FILTER_EXCLUDED_RESOURCES_PATTERN)).when(cacheFilter) .getExcludePattern(); if (cacheFilter.matchExcludePatterns(urlToMatch) != mustMatch) { Assertions.fail("Matching excludePattern and the Url " + urlToMatch + " must return " + mustMatch); } } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/ExcludingPatternFilterTest.java ================================================ /** * Copyright (C) 2025 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.console.common.server.filter; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; public class ExcludingPatternFilterTest { private int proceedCount; private HttpServletRequest request; private HttpServletResponse response; private FilterChain chain; private final Map attributes = new HashMap<>(); @Before public void setUp() { proceedCount = 0; attributes.clear(); request = mock(HttpServletRequest.class); response = mock(HttpServletResponse.class); chain = mock(FilterChain.class); when(request.getRequestURL()).thenReturn(new StringBuffer("http://localhost/API/bpm/process")); when(request.getAttribute(anyString())).thenAnswer(inv -> attributes.get(inv.getArgument(0))); doAnswer(inv -> { attributes.put(inv.getArgument(0), inv.getArgument(1)); return null; }).when(request).setAttribute(anyString(), any()); } @Test public void should_execute_filter_logic_on_first_call() throws Exception { var filter = createFilter("MyFilter"); filter.doFilter(request, response, chain); assertThat(proceedCount).isEqualTo(1); verifyNoInteractions(chain); } @Test public void should_skip_filter_logic_on_second_call() throws Exception { var filter = createFilter("MyFilter"); filter.doFilter(request, response, chain); filter.doFilter(request, response, chain); assertThat(proceedCount).isEqualTo(1); verify(chain, times(1)).doFilter(request, response); } @Test public void should_allow_different_filter_instances_to_each_execute() throws Exception { var filterA = createFilter("FilterA"); var filterB = createFilter("FilterB"); filterA.doFilter(request, response, chain); filterB.doFilter(request, response, chain); assertThat(proceedCount).isEqualTo(2); verifyNoInteractions(chain); } @Test public void should_set_request_attribute_with_filter_name() throws Exception { var filter = createFilter("TokenValidatorFilter"); filter.doFilter(request, response, chain); assertThat(attributes).containsKey("TokenValidatorFilter.FILTERED"); assertThat(attributes.get("TokenValidatorFilter.FILTERED")).isEqualTo(Boolean.TRUE); } private ExcludingPatternFilter createFilter(String filterName) throws ServletException { ExcludingPatternFilter filter = new ExcludingPatternFilter() { @Override public void proceedWithFiltering(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { proceedCount++; } @Override public String getDefaultExcludedPages() { return null; } }; ServletContext servletContext = mock(ServletContext.class); when(servletContext.getContextPath()).thenReturn(""); FilterConfig config = mock(FilterConfig.class); when(config.getFilterName()).thenReturn(filterName); when(config.getInitParameterNames()).thenReturn(Collections.emptyEnumeration()); when(config.getServletContext()).thenReturn(servletContext); filter.init(config); return filter; } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/MultiReadHttpServletRequestTest.java ================================================ /** * Copyright (C) 2022 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.console.common.server.filter; import static org.mockito.Mockito.doReturn; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.CharSequenceInputStream; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class MultiReadHttpServletRequestTest { @Mock HttpServletRequest request; @Test public void should_getInputStream_work_when_called_twice() throws Exception { ServletInputStream fakeInputStream = null; try { fakeInputStream = new FakeServletInputStream(); doReturn(fakeInputStream).when(request).getInputStream(); final MultiReadHttpServletRequest multiReadHttpServletRequest = new MultiReadHttpServletRequest(request); final InputStream inputStream = multiReadHttpServletRequest.getInputStream(); Assert.assertEquals("body content", IOUtils.toString(inputStream, StandardCharsets.UTF_8)); final InputStream inputStream2 = multiReadHttpServletRequest.getInputStream(); Assert.assertEquals("body content", IOUtils.toString(inputStream2, StandardCharsets.UTF_8)); } finally { if (fakeInputStream != null) { fakeInputStream.close(); } } } @Test public void should_getInputStream_work_when_called_twice_for_multipart() throws Exception { doReturn("POST").when(request).getMethod(); doReturn("multipart/form-data").when(request).getContentType(); ServletInputStream fakeInputStream = null; try { fakeInputStream = new FakeServletInputStream(); doReturn(fakeInputStream).when(request).getInputStream(); final MultiReadHttpServletRequest multiReadHttpServletRequest = new MultiReadHttpServletRequest(request); final InputStream inputStream = multiReadHttpServletRequest.getInputStream(); Assert.assertEquals("body content", IOUtils.toString(inputStream, StandardCharsets.UTF_8)); final InputStream inputStream2 = multiReadHttpServletRequest.getInputStream(); Assert.assertEquals("body content", IOUtils.toString(inputStream2, StandardCharsets.UTF_8)); } finally { if (fakeInputStream != null) { fakeInputStream.close(); } } } @Test public void should_getReader_work_when_called_twice() throws Exception { ServletInputStream fakeInputStream = null; try { fakeInputStream = new FakeServletInputStream(); doReturn(fakeInputStream).when(request).getInputStream(); final MultiReadHttpServletRequest multiReadHttpServletRequest = new MultiReadHttpServletRequest(request); final BufferedReader bufferedReader = multiReadHttpServletRequest.getReader(); Assert.assertEquals("body content", IOUtils.toString(bufferedReader)); final BufferedReader bufferedReader2 = multiReadHttpServletRequest.getReader(); Assert.assertEquals("body content", IOUtils.toString(bufferedReader2)); } finally { if (fakeInputStream != null) { fakeInputStream.close(); } } } class FakeServletInputStream extends ServletInputStream { private final CharSequenceInputStream inputStream = new CharSequenceInputStream("body content", StandardCharsets.UTF_8); @Override public int read() throws IOException { return inputStream.read(); } @Override public int read(final byte[] b) throws IOException { return inputStream.read(b); } @Override public void close() throws IOException { inputStream.close(); super.close(); } @Override public boolean isFinished() { try { return inputStream.available() == 0; } catch (IOException e) { throw new RuntimeException(e); } } @Override public boolean isReady() { return !isFinished(); } @Override public void setReadListener(ReadListener readListener) { throw new RuntimeException("Not implemented"); } } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/NoCacheFilterTest.java ================================================ /** * Copyright (C) 2026 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.console.common.server.filter; import static org.mockito.Mockito.*; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; public class NoCacheFilterTest { private NoCacheFilter filter; private HttpServletRequest request; private HttpServletResponse response; private FilterChain chain; private final Map attributes = new HashMap<>(); @Before public void setUp() throws Exception { attributes.clear(); request = mock(HttpServletRequest.class); response = mock(HttpServletResponse.class); chain = mock(FilterChain.class); when(request.getRequestURL()).thenReturn(new StringBuffer("http://localhost/API/bpm/process")); when(request.getAttribute(anyString())).thenAnswer(inv -> attributes.get(inv.getArgument(0))); doAnswer(inv -> { attributes.put(inv.getArgument(0), inv.getArgument(1)); return null; }).when(request).setAttribute(anyString(), any()); filter = new NoCacheFilter(); ServletContext servletContext = mock(ServletContext.class); when(servletContext.getContextPath()).thenReturn(""); FilterConfig config = mock(FilterConfig.class); when(config.getFilterName()).thenReturn("NoCacheFilter"); when(config.getInitParameterNames()).thenReturn(Collections.emptyEnumeration()); when(config.getServletContext()).thenReturn(servletContext); filter.init(config); } @Test public void should_set_cache_control_headers() throws Exception { filter.doFilter(request, response, chain); verify(response).setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate"); verify(chain).doFilter(request, response); } @Test public void should_not_reapply_headers_on_second_dispatch() throws Exception { filter.doFilter(request, response, chain); filter.doFilter(request, response, chain); // Headers set only once (first pass); second pass skips filter logic but still forwards verify(response, times(1)).setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate"); verify(chain, times(2)).doFilter(request, response); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/PathSanitizerTest.java ================================================ /** * Copyright (C) 2026 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.console.common.server.filter; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class PathSanitizerTest { @Test public void should_return_null_for_null_input() { assertThat(PathSanitizer.stripPathParameters(null)).isNull(); } @Test public void should_return_empty_for_empty_input() { assertThat(PathSanitizer.stripPathParameters("")).isEmpty(); } @Test public void should_not_modify_path_without_semicolons() { assertThat(PathSanitizer.stripPathParameters("/API/bpm/process/12345")) .isEqualTo("/API/bpm/process/12345"); } @Test public void should_strip_semicolons_from_dotdot_traversal() { assertThat(PathSanitizer.stripPathParameters("/API/..;/..;/serverAPI")) .isEqualTo("/API/../../serverAPI"); } @Test public void should_strip_semicolons_with_arbitrary_content() { assertThat(PathSanitizer.stripPathParameters("/API/..;anything/..;foo/serverAPI")) .isEqualTo("/API/../../serverAPI"); } @Test public void should_strip_trailing_semicolon() { assertThat(PathSanitizer.stripPathParameters("/API/resource;jsessionid=abc123")) .isEqualTo("/API/resource"); } @Test public void should_strip_multiple_semicolons_per_segment() { assertThat(PathSanitizer.stripPathParameters("/API/system/session/..;/..;/..;/serverAPI/something")) .isEqualTo("/API/system/session/../../../serverAPI/something"); } @Test public void should_handle_semicolon_at_start_of_path() { assertThat(PathSanitizer.stripPathParameters(";param/API/resource")) .isEqualTo("/API/resource"); } @Test public void should_handle_encoded_dotdot_semicolon_pattern() { assertThat(PathSanitizer.stripPathParameters("/API/a/..;anything/..;/..;/WEB-INF/web.xml")) .isEqualTo("/API/a/../../../WEB-INF/web.xml"); } @Test public void should_preserve_normal_path_segments() { assertThat(PathSanitizer.stripPathParameters("/apps/myapp/API/bpm/case/42")) .isEqualTo("/apps/myapp/API/bpm/case/42"); } @Test public void should_handle_consecutive_semicolons() { // Double semicolons: first ';' starts skipping until '/' or end of string assertThat(PathSanitizer.stripPathParameters("/API/a;;b/next")) .isEqualTo("/API/a/next"); assertThat(PathSanitizer.stripPathParameters("/API/..;;/serverAPI")) .isEqualTo("/API/../serverAPI"); } @Test public void should_not_strip_double_encoded_semicolons() { // %253b is double-encoded: %25 -> %, so %253b -> %3b (literal text, not a semicolon) // PathSanitizer only strips literal ';', not percent-encoded forms. // URL-decoding is the caller's responsibility (handled in URLExcludePattern). assertThat(PathSanitizer.stripPathParameters("/API/..%253b/..%253b/serverAPI")) .isEqualTo("/API/..%253b/..%253b/serverAPI"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/RedirectFilterTest.java ================================================ /** * 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.console.common.server.filter; import static org.mockito.Mockito.*; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; public class RedirectFilterTest { @Mock private HttpServletRequest httpRequest; @Mock private HttpServletResponse httpResponse; @Mock private FilterChain filterChain; private RedirectFilter redirectFilter; @Before public void setUp() { MockitoAnnotations.initMocks(this); redirectFilter = new RedirectFilter(); } @Test public void testDoFilterWithInternalRedirect() throws IOException, ServletException { when(httpRequest.getParameter("redirectUrl")).thenReturn("/redirectLink"); when(httpRequest.getRequestURL()).thenReturn(new StringBuffer("http://bonita:8080/currentPath")); when(httpRequest.getRequestURI()).thenReturn("/currentPath"); redirectFilter.doFilter(httpRequest, httpResponse, filterChain); verify(httpResponse).sendRedirect("/redirectLink"); verify(filterChain, never()).doFilter(httpRequest, httpResponse); } @Test public void testDoFilterWithExternalRedirect() throws IOException, ServletException { // using http:// as a prefix to simulate an external redirect when(httpRequest.getParameter("redirectUrl")).thenReturn("http://external:9090/redirectLink"); when(httpRequest.getRequestURL()).thenReturn(new StringBuffer("http://bonita:8080/currentPath")); when(httpRequest.getRequestURI()).thenReturn("/currentPath"); redirectFilter.doFilter(httpRequest, httpResponse, filterChain); verify(httpResponse, never()).sendRedirect(anyString()); verify(filterChain).doFilter(httpRequest, httpResponse); // using // as a prefix to simulate an external redirect when(httpRequest.getParameter("redirectUrl")).thenReturn("//external:9090/redirectLink"); when(httpRequest.getRequestURL()).thenReturn(new StringBuffer("http://bonita:8080/currentPath")); when(httpRequest.getRequestURI()).thenReturn("/currentPath"); redirectFilter.doFilter(httpRequest, httpResponse, filterChain); verify(httpResponse, never()).sendRedirect(anyString()); verify(filterChain, times(2)).doFilter(httpRequest, httpResponse); } @Test public void testDoFilterWithoutRedirect() throws IOException, ServletException { when(httpRequest.getParameter("redirectUrl")).thenReturn(null); redirectFilter.doFilter(httpRequest, httpResponse, filterChain); verify(httpResponse, never()).sendRedirect(anyString()); verify(filterChain).doFilter(httpRequest, httpResponse); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/RequestIdFilterTest.java ================================================ /** * 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.console.common.server.filter; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.quality.Strictness.LENIENT; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; import org.bonitasoft.engine.mdc.MDCConstants; 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.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.slf4j.MDC; /** * Test class RequestIdFilter */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = LENIENT) class RequestIdFilterTest { @Mock private FilterChain chain; @Mock private HttpServletRequestAccessor request; @Mock private HttpServletRequest httpRequest; @Mock private HttpServletResponse httpResponse; @Mock private HttpSession httpSession; @Mock private FilterConfig filterConfig; @Mock private ServletContext servletContext; @Spy RequestIdFilter requestIdFilter; @BeforeEach void setUp() { doReturn(httpSession).when(request).getHttpSession(); when(request.asHttpServletRequest()).thenReturn(httpRequest); when(httpRequest.getMethod()).thenReturn("POST"); when(httpRequest.getCharacterEncoding()).thenReturn("UTF-8"); when(httpRequest.getRequestURL()).thenReturn(new StringBuffer()); when(servletContext.getContextPath()).thenReturn(""); when(filterConfig.getServletContext()).thenReturn(servletContext); when(filterConfig.getInitParameterNames()).thenReturn(Collections.emptyEnumeration()); } @Test void shouldDetectIdsAttachedToRequest() throws Exception { String correlationId = Long.toHexString(System.nanoTime() - 10000000L); String requestId = Long.toHexString(System.nanoTime()); when(httpRequest.getContentType()).thenReturn("application/json"); when(httpRequest.getAttribute("track.requestId")).thenReturn(requestId); when(httpRequest.getAttribute("track.correlationId")).thenReturn(correlationId); Map contextMap = new HashMap<>(); doAnswer(invocation -> { contextMap.putAll(MDC.getCopyOfContextMap()); return null; }).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); requestIdFilter.init(filterConfig); requestIdFilter.doFilter(httpRequest, httpResponse, chain); // method chain was called once and put REQUEST_ID & CORRELATION_REQUEST_ID in context verify(chain, times(1)).doFilter(any(ServletRequest.class), any(ServletResponse.class)); assertThat(contextMap).containsEntry(MDCConstants.REQUEST_ID, requestId); assertThat(contextMap).containsEntry(MDCConstants.CORRELATION_REQUEST_ID, correlationId); } @Test void shouldDetectIdsInRequestHeader() throws Exception { String correlationId = Long.toHexString(System.nanoTime() - 10000000L); String requestId = Long.toHexString(System.nanoTime()); when(httpRequest.getContentType()).thenReturn("application/json"); when(httpRequest.getHeader("X-Request-ID")).thenReturn(requestId); when(httpRequest.getHeader("X-Correlation-ID")).thenReturn(correlationId); Map contextMap = new HashMap<>(); doAnswer(invocation -> { contextMap.putAll(MDC.getCopyOfContextMap()); return null; }).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); requestIdFilter.init(filterConfig); requestIdFilter.doFilter(httpRequest, httpResponse, chain); // method chain was called once and put REQUEST_ID & CORRELATION_REQUEST_ID in context verify(chain, times(1)).doFilter(any(ServletRequest.class), any(ServletResponse.class)); assertThat(contextMap).containsEntry(MDCConstants.REQUEST_ID, requestId); assertThat(contextMap).containsEntry(MDCConstants.CORRELATION_REQUEST_ID, correlationId); } @Test void shouldDetectUserAgent() throws Exception { String userAgent = "PostmanRuntime/7.43.0"; when(httpRequest.getHeader("User-Agent")).thenReturn(userAgent); Map contextMap = new HashMap<>(); doAnswer(invocation -> { contextMap.putAll(MDC.getCopyOfContextMap()); return null; }).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); requestIdFilter.init(filterConfig); requestIdFilter.doFilter(httpRequest, httpResponse, chain); // method chain was called once and put REQUEST_USER_AGENT_MDC_KEY in context verify(chain, times(1)).doFilter(any(ServletRequest.class), any(ServletResponse.class)); assertThat(contextMap).containsEntry(MDCConstants.REQUEST_USER_AGENT_MDC_KEY, userAgent); } @Test void shouldIgnoreNullUserAgent() throws Exception { when(httpRequest.getHeader("User-Agent")).thenReturn(null); Map contextMap = new HashMap<>(); doAnswer(invocation -> { contextMap.putAll(MDC.getCopyOfContextMap()); return null; }).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); requestIdFilter.init(filterConfig); requestIdFilter.doFilter(httpRequest, httpResponse, chain); // method chain was called once and put REQUEST_USER_AGENT_MDC_KEY in context verify(chain, times(1)).doFilter(any(ServletRequest.class), any(ServletResponse.class)); assertThat(contextMap).doesNotContainKey(MDCConstants.REQUEST_USER_AGENT_MDC_KEY); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/console/common/server/filter/SanitizerFilterTest.java ================================================ /** * 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.console.common.server.filter; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; import static org.mockito.quality.Strictness.LENIENT; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Collections; import java.util.List; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ReadListener; import javax.servlet.ServletContext; import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.io.IOUtils; import org.bonitasoft.console.common.server.login.HttpServletRequestAccessor; import org.bonitasoft.web.toolkit.client.common.json.JSonUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; /** * Test class SanitizerFilter * * @author Vincent Hemery */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = LENIENT) class SanitizerFilterTest { @Mock private FilterChain chain; @Mock private HttpServletRequestAccessor request; @Mock private HttpServletRequest httpRequest; @Mock private HttpServletResponse httpResponse; @Mock private HttpSession httpSession; @Mock private FilterConfig filterConfig; @Mock private ServletContext servletContext; @Spy SanitizerFilter sanitizerFilter; @BeforeEach void setUp() { doReturn(httpSession).when(request).getHttpSession(); when(request.asHttpServletRequest()).thenReturn(httpRequest); when(httpRequest.getMethod()).thenReturn("POST"); when(httpRequest.getCharacterEncoding()).thenReturn("UTF-8"); when(httpRequest.getRequestURL()).thenReturn(new StringBuffer()); when(servletContext.getContextPath()).thenReturn(""); when(filterConfig.getServletContext()).thenReturn(servletContext); when(filterConfig.getInitParameterNames()).thenReturn(Collections.emptyEnumeration()); } @Test void shouldNotSanitizeWhenDisabled() throws Exception { when(httpRequest.getContentType()).thenReturn("application/json"); when(sanitizerFilter.isSanitizerEnabled()).thenReturn(false); sanitizerFilter.init(filterConfig); sanitizerFilter.doFilter(httpRequest, httpResponse, chain); verify(sanitizerFilter, never()).sanitize(any(JsonNode.class)); verify(chain, times(1)).doFilter(httpRequest, httpResponse); } @Test void shouldNotAffectAttributeValue() throws Exception { when(httpRequest.getContentType()).thenReturn("application/json"); when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true); final String attributeName = "key"; final String attributeValue = "value"; when(httpRequest.getAttribute(attributeName)).thenReturn(attributeValue); sanitizerFilter.init(filterConfig); sanitizerFilter.doFilter(httpRequest, httpResponse, chain); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ServletRequest.class); verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class)); ServletRequest r = requestCaptor.getValue(); final String value = r.getAttribute(attributeName).toString(); assertThat(value).isEqualTo(attributeValue); } @Test void shouldNotAffectParameterValues() throws Exception { when(httpRequest.getContentType()).thenReturn("application/json"); when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true); final String parameterName = "key"; final String parameterValue = "value"; when(httpRequest.getParameter(parameterName)).thenReturn(parameterValue); sanitizerFilter.init(filterConfig); sanitizerFilter.doFilter(httpRequest, httpResponse, chain); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ServletRequest.class); verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class)); ServletRequest r = requestCaptor.getValue(); final String value = r.getParameter(parameterName); assertThat(value).isEqualTo(parameterValue); } @Test void shouldNotAffectFileUpload() throws Exception { when(httpRequest.getContentType()).thenReturn("text/xml"); when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true); final String body = String.format("%n" + "%n" + " %n" + " %n" + ""); var is = new ByteArrayInputStream(body.getBytes()); when(httpRequest.getInputStream()).thenReturn(getServletInputStream(is)); sanitizerFilter.init(filterConfig); sanitizerFilter.doFilter(httpRequest, httpResponse, chain); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ServletRequest.class); verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class)); ServletRequest r = requestCaptor.getValue(); var updatedBody = new String(r.getInputStream().readAllBytes()); assertThat(updatedBody).isEqualTo(body); } @Test void shouldNotAffectNonHtml() throws Exception { when(httpRequest.getContentType()).thenReturn("application/json"); when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true); when(sanitizerFilter.getAttributesExcluded()).thenReturn(Collections.emptyList()); final String body = "{\"key\":\"value\"}"; var is = new ByteArrayInputStream(body.getBytes()); when(httpRequest.getInputStream()).thenReturn(getServletInputStream(is)); sanitizerFilter.init(filterConfig); sanitizerFilter.doFilter(httpRequest, httpResponse, chain); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ServletRequest.class); verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class)); ServletRequest r = requestCaptor.getValue(); var updatedBody = new String(r.getInputStream().readAllBytes()); assertThat(updatedBody).isEqualTo(body); } @Test void shouldNotAffectPre() throws Exception { when(httpRequest.getContentType()).thenReturn("application/json"); when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true); when(sanitizerFilter.getAttributesExcluded()).thenReturn(Collections.emptyList()); final String body = "{\"key\":\"

      text

      value formatted
      \"}"; var is = new ByteArrayInputStream(body.getBytes()); when(httpRequest.getInputStream()).thenReturn(getServletInputStream(is)); sanitizerFilter.init(filterConfig); sanitizerFilter.doFilter(httpRequest, httpResponse, chain); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ServletRequest.class); verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class)); ServletRequest r = requestCaptor.getValue(); var updatedBody = new String(r.getInputStream().readAllBytes()); assertThat(updatedBody).isEqualTo(body); } @Test void shouldNotAffectLinks() throws Exception { when(httpRequest.getContentType()).thenReturn("application/json"); when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true); when(sanitizerFilter.getAttributesExcluded()).thenReturn(Collections.emptyList()); final String body = "{\"key\":\"

      link text

      \"}"; var is = new ByteArrayInputStream(body.getBytes()); when(httpRequest.getInputStream()).thenReturn(getServletInputStream(is)); sanitizerFilter.init(filterConfig); sanitizerFilter.doFilter(httpRequest, httpResponse, chain); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ServletRequest.class); verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class)); ServletRequest r = requestCaptor.getValue(); var updatedBody = new String(r.getInputStream().readAllBytes()); // Note: rel attribute order is non-deterministic after OWASP HTML Sanitizer removed Guava (v20240325.1+) // Check structure and all required rel values are present assertThat(updatedBody) .startsWith( "{\"key\":\"

      link text

      \"}") .contains("noreferrer") .contains("noopener") .contains("nofollow"); } @Test void shouldSanitizeAttackFromBody() throws Exception { when(httpRequest.getContentType()).thenReturn("application/JSON"); when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true); when(sanitizerFilter.getAttributesExcluded()).thenReturn(List.of("email", "password")); // Classic XSS attack in value final String attName1 = "test1"; final String saneValue1 = "Hello World"; final String attValue1 = saneValue1 + ""; // Another XSS attack in value final String attName2 = "test2"; final String saneValue2 = "
      test
      "; final String attValue2 = "
      test
      "; // XSS attack in name final String saneAttName3 = "test3"; final String attName3 = saneAttName3 + ""; final String attValue3 = "value3"; // XSS attack as exploited in IE (as seen on // https://github.com/OWASP/java-html-sanitizer/blob/master/docs/html-validation.md#valid-according-to-policy) final String attName4 = "test4"; final String saneValue4 = "v4"; final String attValue4 = saneValue4 + ""; // XSS attack as exploited in foreign content context (as seen on // https://github.com/OWASP/java-html-sanitizer/blob/master/docs/html-validation.md#valid-according-to-policy) final String attName5 = "test5"; final String saneValue5 = "v5"; final String attValue5 = saneValue5 + ""; // Don't break my heart (as seen on // https://github.com/OWASP/java-html-sanitizer/blob/master/docs/html-validation.md#dont-break-my-heart) final String attName6 = "test6"; final String saneValue6 = "I <3 Poniez!"; // but escaped chars are unescaped anyway... final String attName7 = "test7"; final String saneValue7 = "You <3 Poniez 2!"; final String attValue7 = "You <3 Poniez 2!"; // XSS attack escaped in value final String attName8 = "test8"; final String saneValue8 = "value8"; final String attValue8 = saneValue8 + "<script>alert('test')"; final String body = String.format("{%n" + " \"key1\": \"value1\",%n" + " \"keyOfEmpty\": \"\",%n" + " \"keyOfNull\": null,%n" + " \"%s\": \"%s\",%n" + " \"%s\": \"%s\",%n" + " \"%s\": \"%s\",%n" + " \"%s\": \"%s\",%n" + " \"%s\": \"%s\",%n" + " \"%s\": \"%s\",%n" + " \"%s\": \"%s\",%n" + " \"%s\": \"%s\",%n" + " \"email\": \"walter.bates@bonitasoft.com\"%n" + "}", attName1, JSonUtil.escape(attValue1), attName2, JSonUtil.escape(attValue2), attName3, JSonUtil.escape(attValue3), attName4, JSonUtil.escape(attValue4), attName5, JSonUtil.escape(attValue5), attName6, JSonUtil.escape(saneValue6), attName7, JSonUtil.escape(attValue7), attName8, JSonUtil.escape(attValue8)); var is = new ByteArrayInputStream(body.getBytes()); when(httpRequest.getInputStream()).thenReturn(getServletInputStream(is)); sanitizerFilter.init(filterConfig); sanitizerFilter.doFilter(httpRequest, httpResponse, chain); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ServletRequest.class); verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class)); ServletRequest r = requestCaptor.getValue(); try (ServletInputStream inputStream = r.getInputStream()) { var stringBody = IOUtils.toString(inputStream, r.getCharacterEncoding()); ObjectMapper mapper = new ObjectMapper(); var json = mapper.readTree(stringBody); // check normal values assertThat(json.get("key1").asText()).isEqualTo("value1"); assertThat(json.get("keyOfEmpty").asText()).isEqualTo(""); assertThat(json.get("keyOfNull").isNull()).isTrue(); assertThat(json.get("email").asText()).isEqualTo("walter.bates@bonitasoft.com"); // check sanitized values assertThat(json.get(attName1).asText()).isEqualTo(saneValue1); assertThat(json.get(attName2).asText()).isEqualTo(saneValue2); assertThat(json.get(attName3)).isNull(); assertThat(json.get(saneAttName3).asText()).isEqualTo(attValue3); assertThat(json.get(attName4).asText()).isEqualTo(saneValue4); assertThat(json.get(attName5).asText()).isEqualTo(saneValue5); assertThat(json.get(attName6).asText()).isEqualTo(saneValue6); assertThat(json.get(attName7).asText()).isEqualTo(saneValue7); assertThat(json.get(attName8).asText()).isEqualTo(saneValue8); } catch (IOException e) { throw new AssertionError(e); } } @Test public void shouldSanitizeAttackWhenRequestIsMultiReadHttpServletRequest() throws Exception { // Simulate the production scenario where TokenValidatorFilter or RestAPIAuthorizationFilter // has already wrapped the request in a MultiReadHttpServletRequest MultiReadHttpServletRequest multiReadRequest = mock(MultiReadHttpServletRequest.class); when(multiReadRequest.getMethod()).thenReturn("PUT"); when(multiReadRequest.getCharacterEncoding()).thenReturn("UTF-8"); when(multiReadRequest.getContentType()).thenReturn("application/json"); when(multiReadRequest.getRequestURL()).thenReturn(new StringBuffer()); when(sanitizerFilter.isSanitizerEnabled()).thenReturn(true); when(sanitizerFilter.getAttributesExcluded()).thenReturn(Collections.emptyList()); final String body = "{\"lastname\":\"XSS\"}"; var is = new ByteArrayInputStream(body.getBytes()); when(multiReadRequest.getInputStream()).thenReturn(getServletInputStream(is)); sanitizerFilter.init(filterConfig); sanitizerFilter.doFilter(multiReadRequest, httpResponse, chain); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ServletRequest.class); verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class)); ServletRequest r = requestCaptor.getValue(); var updatedBody = IOUtils.toString(r.getInputStream(), r.getCharacterEncoding()); ObjectMapper mapper = new ObjectMapper(); var json = mapper.readTree(updatedBody); // The marquee tag and onclick should be stripped by the sanitizer assertThat(json.get("lastname").asText()).doesNotContain("alert('xss')safe\"}"; var is = new ByteArrayInputStream(body.getBytes()); when(httpRequest.getInputStream()).thenReturn(getServletInputStream(is)); sanitizerFilter.init(filterConfig); sanitizerFilter.doFilter(httpRequest, httpResponse, chain); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ServletRequest.class); verify(chain, times(1)).doFilter(requestCaptor.capture(), any(ServletResponse.class)); ServletRequest r = requestCaptor.getValue(); // Read via getReader() instead of getInputStream() var readerBody = IOUtils.toString(r.getReader()); ObjectMapper mapper = new ObjectMapper(); var json = mapper.readTree(readerBody); assertThat(json.get("name").asText()).doesNotContain("")); assertThat(errorMessage.getMessage()) .isEqualTo("\\u003cscript\\u003ealert(\\u0027bad\\u0027)\\u003c\\/script\\u003e"); } @Test public void should_encode_message_when_using_setter() throws Exception { ErrorMessage errorMessage = new ErrorMessage(); errorMessage.setMessage(""); assertThat(errorMessage.getMessage()) .isEqualTo("\\u003cscript\\u003ealert(\\u0027bad\\u0027)\\u003c\\/script\\u003e"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/system/BonitaVersionTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.system; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import java.io.InputStream; import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Vincent Elcrin */ @RunWith(MockitoJUnitRunner.class) public class BonitaVersionTest { @Mock private VersionFile file; @Mock private InputStream stream; @Test public void should_read_version_stream_to_return_its_content() throws Exception { final InputStream stream = IOUtils.toInputStream("1.0.0\n2021.2-u0\nBonitasoft © 2021", StandardCharsets.UTF_8); given(file.getStream()).willReturn(stream); final BonitaVersion version = new BonitaVersion(file); assertThat(version.getVersion()).isEqualTo("1.0.0"); assertThat(version.getBrandingVersion()).isEqualTo("2021.2"); assertThat(version.getBrandingVersionWithUpdate()).isEqualTo("2021.2-u0"); assertThat(version.getCopyright()).isEqualTo("Bonitasoft © 2021"); IOUtils.closeQuietly(stream); } @Test public void should_read_version_stream_to_return_its_version() throws Exception { final InputStream stream = IOUtils.toInputStream("1.0.0"); given(file.getStream()).willReturn(stream); final BonitaVersion version = new BonitaVersion(file); assertThat(version.getVersion()).isEqualTo("1.0.0"); IOUtils.closeQuietly(stream); } @Test public void should_trim_extra_new_line_character() { final InputStream stream = IOUtils.toInputStream("1.0.0\n", StandardCharsets.UTF_8); given(file.getStream()).willReturn(stream); final BonitaVersion version = new BonitaVersion(file); assertThat(version.getVersion()).isEqualTo("1.0.0"); IOUtils.closeQuietly(stream); } @Test public void should_return_an_empty_version_when_file_is_invalid() { given(file.getStream()).willReturn(null); final BonitaVersion version = new BonitaVersion(file); assertThat(version.getVersion()).isEqualTo(""); assertThat(version.getCopyright()).isEqualTo(""); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/system/I18nTranslationControllerTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.system; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.util.HashMap; import java.util.Map; import org.bonitasoft.console.common.server.i18n.I18n; import org.bonitasoft.web.rest.server.api.AbstractControllerTest; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n; import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.springframework.http.MediaType; /** * @author Julien Mege */ class I18nTranslationControllerTest extends AbstractControllerTest { @Mock private I18n i18n; @Override protected I18nTranslationController createController() { return spy(new I18nTranslationController()); } @Override protected void configureMocks(I18nTranslationController controller) throws Exception { doReturn(i18n).when(controller).getI18n(); } @Test void should_return_translation_for_the_given_locale() throws Exception { Map translations = new HashMap<>(); translations.put("key1", "message 1"); translations.put("key2", "autre méssage"); translations.put("key3", "~%^*µ"); when(i18n.getLocale(AbstractI18n.LOCALE.fr)).thenReturn(translations); mockMvc.perform( get("/API/system/i18ntranslation") .param("f", "locale=fr") .sessionAttrs(sessionAttributes) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) .andExpect(content().json(""" [ {"key": "key1", "value": "message 1"}, {"key": "key2", "value": "autre méssage"}, {"key": "key3", "value": "~%^*µ"} ] """, true)); } @Test void should_return_http400_error_code_when_no_filter_param() throws Exception { mockMvc.perform(get("/API/system/i18ntranslation") .sessionAttrs(sessionAttributes) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()) .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.exception").value(IllegalArgumentException.class.toString())) .andExpect(jsonPath("$.message").value("filter locale is mandatory")); } @Test void should_return_http400_error_code_when_no_locale_filter_param() throws Exception { mockMvc.perform( get("/API/system/i18ntranslation") .param("f", "test") .sessionAttrs(sessionAttributes) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()) .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.exception").value(IllegalArgumentException.class.toString())) .andExpect(jsonPath("$.message").value("filter locale is mandatory")); } @Test void should_return_http400_error_code_when_empty_locale_filter_param() throws Exception { mockMvc.perform( get("/API/system/i18ntranslation") .param("f", "locale=") .sessionAttrs(sessionAttributes) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isBadRequest()) .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.exception").value(IllegalArgumentException.class.toString())) .andExpect(jsonPath("$.message").value("filter locale is mandatory")); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/system/MaintenanceControllerTest.java ================================================ /** * Copyright (C) 2023 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.web.rest.server.api.system; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.*; import javax.servlet.http.HttpSession; import org.bonitasoft.console.common.server.utils.SessionUtil; import org.bonitasoft.engine.api.MaintenanceAPI; import org.bonitasoft.engine.exception.BonitaException; import org.bonitasoft.engine.maintenance.MaintenanceDetails; import org.bonitasoft.engine.maintenance.impl.MaintenanceDetailsImpl; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.system.MaintenanceDetailsClient; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.HttpStatus; import org.springframework.web.server.ResponseStatusException; @ExtendWith(MockitoExtension.class) class MaintenanceControllerTest { @Mock private HttpSession session; @Mock private MaintenanceAPI maintenanceAPI; @Spy @InjectMocks private MaintenanceController maintenanceController; @Test public void should_get_maintenance_info_from_java_api() throws BonitaException { //given APISession apiSession = mock(APISession.class); doReturn(apiSession).when(maintenanceController).getApiSession(session); MaintenanceDetails maintenanceDetails = mock(MaintenanceDetails.class); doReturn(maintenanceAPI).when(maintenanceController).getMaintenanceAPI(apiSession); doReturn(maintenanceDetails).when(maintenanceAPI).getMaintenanceDetails(); //when MaintenanceDetails result = maintenanceController.getMaintenanceDetails(session); //then verify(maintenanceAPI).getMaintenanceDetails(); assertSame(maintenanceDetails, result); } @Test public void should_enable_maintenance_and_change_and_enable_maintenance_msg() throws BonitaException { //given APISession apiSession = mock(APISession.class); doReturn(apiSession).when(maintenanceController).getApiSession(session); MaintenanceDetails maintenanceDetails = MaintenanceDetailsImpl.builder() .maintenanceState(MaintenanceDetails.State.DISABLED) .maintenanceMessage(null) .maintenanceMessageActive(false) .build(); doReturn(maintenanceAPI).when(maintenanceController).getMaintenanceAPI(apiSession); doReturn(maintenanceDetails).when(maintenanceAPI).getMaintenanceDetails(); MaintenanceDetailsClient maintenanceInfoClient = new MaintenanceDetailsClient( MaintenanceDetails.State.ENABLED, "new Scheduled Maintenance msg", true); //when maintenanceController.changeMaintenanceState(maintenanceInfoClient, session); //then verify(maintenanceAPI).updateMaintenanceMessage("new Scheduled Maintenance msg"); verify(maintenanceAPI).enableMaintenanceMode(); verify(maintenanceAPI, never()).disableMaintenanceMode(); verify(maintenanceAPI).enableMaintenanceMessage(); verify(maintenanceAPI, never()).disableMaintenanceMessage(); } @Test public void should_disable_maintenance_and_change_and_disable_maintenance_msg() throws BonitaException { //given APISession apiSession = mock(APISession.class); doReturn(apiSession).when(maintenanceController).getApiSession(session); MaintenanceDetails maintenanceDetails = MaintenanceDetailsImpl.builder() .maintenanceState(MaintenanceDetails.State.ENABLED) .maintenanceMessage("msg") .maintenanceMessageActive(true) .build(); doReturn(maintenanceAPI).when(maintenanceController).getMaintenanceAPI(apiSession); doReturn(maintenanceDetails).when(maintenanceAPI).getMaintenanceDetails(); MaintenanceDetailsClient maintenanceInfoClient = new MaintenanceDetailsClient( MaintenanceDetails.State.DISABLED, "new Scheduled Maintenance msg", false); //when maintenanceController.changeMaintenanceState(maintenanceInfoClient, session); //then verify(maintenanceAPI).updateMaintenanceMessage("new Scheduled Maintenance msg"); verify(maintenanceAPI, never()).enableMaintenanceMode(); verify(maintenanceAPI).disableMaintenanceMode(); verify(maintenanceAPI, never()).enableMaintenanceMessage(); verify(maintenanceAPI).disableMaintenanceMessage(); } @Test public void should_disable_maintenance_only() throws BonitaException { //given APISession apiSession = mock(APISession.class); doReturn(apiSession).when(maintenanceController).getApiSession(session); MaintenanceDetails maintenanceDetails = MaintenanceDetailsImpl.builder() .maintenanceState(MaintenanceDetails.State.ENABLED) .maintenanceMessage("msg") .maintenanceMessageActive(true) .build(); doReturn(maintenanceAPI).when(maintenanceController).getMaintenanceAPI(apiSession); doReturn(maintenanceDetails).when(maintenanceAPI).getMaintenanceDetails(); MaintenanceDetailsClient maintenanceInfoClient = new MaintenanceDetailsClient( MaintenanceDetails.State.DISABLED, "msg", true); //when maintenanceController.changeMaintenanceState(maintenanceInfoClient, session); //then verify(maintenanceAPI, never()).updateMaintenanceMessage(any()); verify(maintenanceAPI, never()).enableMaintenanceMode(); verify(maintenanceAPI).disableMaintenanceMode(); verify(maintenanceAPI, never()).enableMaintenanceMessage(); verify(maintenanceAPI, never()).disableMaintenanceMessage(); } @Test public void should_disable_scheduled_maintenance_msg_only() throws BonitaException { //given APISession apiSession = mock(APISession.class); doReturn(apiSession).when(maintenanceController).getApiSession(session); MaintenanceDetails maintenanceDetails = MaintenanceDetailsImpl.builder() .maintenanceState(MaintenanceDetails.State.DISABLED) .maintenanceMessage("msg") .maintenanceMessageActive(true) .build(); doReturn(maintenanceAPI).when(maintenanceController).getMaintenanceAPI(apiSession); doReturn(maintenanceDetails).when(maintenanceAPI).getMaintenanceDetails(); MaintenanceDetailsClient maintenanceInfoClient = new MaintenanceDetailsClient( MaintenanceDetails.State.DISABLED, "msg", false); //when maintenanceController.changeMaintenanceState(maintenanceInfoClient, session); //then verify(maintenanceAPI, never()).updateMaintenanceMessage(any()); verify(maintenanceAPI, never()).enableMaintenanceMode(); verify(maintenanceAPI, never()).disableMaintenanceMode(); verify(maintenanceAPI, never()).enableMaintenanceMessage(); verify(maintenanceAPI).disableMaintenanceMessage(); } @Test public void should_respond_unauthorized_if_no_session() { //given when(session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY)).thenReturn(null); //when ResponseStatusException exception = assertThrows(ResponseStatusException.class, () -> maintenanceController.getApiSession(session)); //then assertThat(exception.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED); //when exception = assertThrows(ResponseStatusException.class, () -> maintenanceController.getMaintenanceDetails(session)); //then assertThat(exception.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED); //when exception = assertThrows(ResponseStatusException.class, () -> maintenanceController.changeMaintenanceState(null, session)); //then assertThat(exception.getStatus()).isEqualTo(HttpStatus.UNAUTHORIZED); } @Test public void should_getApiSession_return_session_from_HttpRequestSession() { // Create a mock APISession APISession apiSession = mock(APISession.class); when(session.getAttribute(SessionUtil.API_SESSION_PARAM_KEY)).thenReturn(apiSession); // Call the method and expect the same APISession to be returned APISession result = maintenanceController.getApiSession(session); assertSame(apiSession, result); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/tenant/TenantResourceItemTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.api.tenant; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; import java.time.Instant; import java.time.OffsetDateTime; import java.time.ZoneOffset; import org.bonitasoft.engine.tenant.TenantResource; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class TenantResourceItemTest { @Mock private TenantResource tenantResource; @Test public void should_format_lastUpdateDate_as_ISO8601_string_when_tenantResourceItem_is_created() { String dateAsString = "2018-01-05T09:04:19Z"; Instant instant = Instant.ofEpochSecond(1515143059); when(tenantResource.getLastUpdateDate()).thenReturn(OffsetDateTime.ofInstant(instant, ZoneOffset.UTC)); TenantResourceItem tenantResourceItem = new TenantResourceItem(tenantResource); assertThat(tenantResourceItem.getLastUpdateDate()).isEqualTo(dateAsString); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/CommonDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore; import static org.assertj.core.api.Assertions.assertThat; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.search.SearchFilterOperation; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.impl.SearchFilter; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class CommonDatastoreTest { @InjectMocks private FakeCommonDatastore commonDatastore; /** * Test method for {@link CommonDatastore#addStringFilterToSearchBuilder(java.util.Map, * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String). */ @Test public void addStringFilterToSearchBuilder_should_do_nothing_when_filters_is_null() { // given: final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); // when: commonDatastore.addStringFilterToSearchBuilder(null, searchOptionsBuilder, null, null); // then: assertThat(searchOptionsBuilder.done().getFilters()).isEmpty(); } /** * Test method for {@link CommonDatastore#addStringFilterToSearchBuilder(java.util.Map, * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String). */ @Test public void addStringFilterToSearchBuilder_should_do_nothing_when_filters_is_empty() { // given: final Map filters = new HashMap<>(0); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); // when: commonDatastore.addStringFilterToSearchBuilder(filters, searchOptionsBuilder, null, null); // then: assertThat(searchOptionsBuilder.done().getFilters()).isEmpty(); } /** * Test method for {@link CommonDatastore#addStringFilterToSearchBuilder(java.util.Map, * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String). */ @Test public void addStringFilterToSearchBuilder_should_add_greater_than_filter_when_is_applicable() { // given: final String filterName = "someFilterKey"; final String engineAttributeName = "EngineNumericKey"; final Map filters = new HashMap<>(1); filters.put(filterName, "> someNumericValue "); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); // when: commonDatastore.addStringFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, engineAttributeName); // then: assertThat(searchOptionsBuilder.done().getFilters().size()).isEqualTo(1); final SearchFilter searchFilter = searchOptionsBuilder.done().getFilters().get(0); assertThat(searchFilter.getOperation()).isEqualTo(SearchFilterOperation.GREATER_THAN); assertThat(searchOptionsBuilder.done().getFilters().get(0).getField()).isEqualTo(engineAttributeName); assertThat(searchOptionsBuilder.done().getFilters().get(0).getValue()).isEqualTo("someNumericValue"); } /** * Test method for {@link CommonDatastore#addStringFilterToSearchBuilder(java.util.Map, * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String). */ @Test public void addStringFilterToSearchBuilder_should_do_nothing_when_filter_value_is_null() { // given: final String filterName = "someFilterKey"; final Map filters = new HashMap<>(1); filters.put(filterName, null); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); // when: commonDatastore.addStringFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, null); // then: assertThat(searchOptionsBuilder.done().getFilters()).isEmpty(); } /** * Test method for {@link CommonDatastore#addStringFilterToSearchBuilder(java.util.Map, * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String). */ @Test public void addStringFilterToSearchBuilder_should_add_less_than_filter_when_is_applicable() { // given: final String filterName = "someFilterKey"; final String engineAttributeName = "EngineNumericKey"; final Map filters = new HashMap<>(1); filters.put(filterName, "< someNumericValue "); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); // when: commonDatastore.addStringFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, engineAttributeName); // then: assertThat(searchOptionsBuilder.done().getFilters().size()).isEqualTo(1); final SearchFilter searchFilter = searchOptionsBuilder.done().getFilters().get(0); assertThat(searchFilter.getOperation()).isEqualTo(SearchFilterOperation.LESS_THAN); assertThat(searchOptionsBuilder.done().getFilters().get(0).getField()).isEqualTo(engineAttributeName); assertThat(searchOptionsBuilder.done().getFilters().get(0).getValue()).isEqualTo("someNumericValue"); } /** * Test method for {@link CommonDatastore#addStringFilterToSearchBuilder(java.util.Map, * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String). */ @Test public void addStringFilterToSearchBuilder_should_add_equals_filter_when_is_applicable() { // given: final String filterName = "someFilterKey"; final String engineAttributeName = "EngineNumericKey"; final String filterValue = "174"; final Map filters = new HashMap<>(1); filters.put(filterName, filterValue); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); // when: commonDatastore.addStringFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, engineAttributeName); // then: assertThat(searchOptionsBuilder.done().getFilters().size()).isEqualTo(1); final SearchFilter searchFilter = searchOptionsBuilder.done().getFilters().get(0); assertThat(searchFilter.getOperation()).isEqualTo(SearchFilterOperation.EQUALS); assertThat(searchOptionsBuilder.done().getFilters().get(0).getField()).isEqualTo(engineAttributeName); assertThat(searchOptionsBuilder.done().getFilters().get(0).getValue()).isEqualTo(filterValue); } /** * Test method for {@link CommonDatastore#addLongFilterToSearchBuilder(java.util.Map, * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String). */ @Test public void addLongFilterToSearchBuilder_should_do_nothing_when_filters_is_null() { // given: final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); // when: commonDatastore.addLongFilterToSearchBuilder(null, searchOptionsBuilder, null, null); // then: assertThat(searchOptionsBuilder.done().getFilters()).isEmpty(); } /** * Test method for {@link CommonDatastore#addLongFilterToSearchBuilder(java.util.Map, * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String). */ @Test public void addLongFilterToSearchBuilder_should_do_nothing_when_filters_is_empty() { // given: final Map filters = new HashMap<>(0); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); // when: commonDatastore.addLongFilterToSearchBuilder(filters, searchOptionsBuilder, null, null); // then: assertThat(searchOptionsBuilder.done().getFilters()).isEmpty(); } /** * Test method for {@link CommonDatastore#addLongFilterToSearchBuilder(java.util.Map, * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String). */ @Test public void addLongFilterToSearchBuilder_should_add_greater_than_filter_when_is_applicable() { // given: final String filterName = "someFilterKey"; final String engineAttributeName = "EngineNumericKey"; final Map filters = new HashMap<>(1); filters.put(filterName, "> 2 "); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); // when: commonDatastore.addLongFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, engineAttributeName); // then: assertThat(searchOptionsBuilder.done().getFilters().size()).isEqualTo(1); final SearchFilter searchFilter = searchOptionsBuilder.done().getFilters().get(0); assertThat(searchFilter.getOperation()).isEqualTo(SearchFilterOperation.GREATER_THAN); assertThat(searchOptionsBuilder.done().getFilters().get(0).getField()).isEqualTo(engineAttributeName); assertThat(searchOptionsBuilder.done().getFilters().get(0).getValue()).isEqualTo(2L); } /** * Test method for {@link CommonDatastore#addLongFilterToSearchBuilder(java.util.Map, * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String). */ @Test public void addLongFilterToSearchBuilder_should_do_nothing_when_filter_value_is_null() { // given: final String filterName = "someFilterKey"; final Map filters = new HashMap<>(1); filters.put(filterName, null); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); // when: commonDatastore.addLongFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, null); // then: assertThat(searchOptionsBuilder.done().getFilters()).isEmpty(); } /** * Test method for {@link CommonDatastore#addLongFilterToSearchBuilder(java.util.Map, * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String). */ @Test public void addLongFilterToSearchBuilder_should_add_less_than_filter_when_is_applicable() { // given: final String filterName = "someFilterKey"; final String engineAttributeName = "EngineNumericKey"; final Map filters = new HashMap<>(1); filters.put(filterName, "< 8 "); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); // when: commonDatastore.addLongFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, engineAttributeName); // then: assertThat(searchOptionsBuilder.done().getFilters().size()).isEqualTo(1); final SearchFilter searchFilter = searchOptionsBuilder.done().getFilters().get(0); assertThat(searchFilter.getOperation()).isEqualTo(SearchFilterOperation.LESS_THAN); assertThat(searchOptionsBuilder.done().getFilters().get(0).getField()).isEqualTo(engineAttributeName); assertThat(searchOptionsBuilder.done().getFilters().get(0).getValue()).isEqualTo(8L); } /** * Test method for {@link CommonDatastore#addLongFilterToSearchBuilder(java.util.Map, * org.bonitasoft.engine.search.SearchOptionsBuilder, String, String). */ @Test public void addLongFilterToSearchBuilder_should_add_equals_filter_when_is_applicable() { // given: final String filterName = "someFilterKey"; final String engineAttributeName = "EngineNumericKey"; final String filterValue = "174"; final Map filters = new HashMap<>(1); filters.put(filterName, filterValue); final SearchOptionsBuilder searchOptionsBuilder = new SearchOptionsBuilder(0, 10); // when: commonDatastore.addLongFilterToSearchBuilder(filters, searchOptionsBuilder, filterName, engineAttributeName); // then: assertThat(searchOptionsBuilder.done().getFilters().size()).isEqualTo(1); final SearchFilter searchFilter = searchOptionsBuilder.done().getFilters().get(0); assertThat(searchFilter.getOperation()).isEqualTo(SearchFilterOperation.EQUALS); assertThat(searchOptionsBuilder.done().getFilters().get(0).getField()).isEqualTo(engineAttributeName); assertThat(searchOptionsBuilder.done().getFilters().get(0).getValue()).isEqualTo(174L); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/FakeCommonDatastore.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore; import org.bonitasoft.engine.bpm.process.ProcessDefinition; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.process.ProcessItem; /** * @author Celine Souchet */ public class FakeCommonDatastore extends CommonDatastore { public FakeCommonDatastore(final APISession engineSession) { super(engineSession); } @Override protected ProcessItem convertEngineToConsoleItem(final ProcessDefinition item) { return null; } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationDataStoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.application; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.business.application.ApplicationCreator; import org.bonitasoft.engine.business.application.ApplicationLinkCreator; import org.bonitasoft.engine.business.application.ApplicationLinkUpdater; import org.bonitasoft.engine.business.application.ApplicationNotFoundException; import org.bonitasoft.engine.business.application.ApplicationPage; import org.bonitasoft.engine.business.application.ApplicationUpdater; import org.bonitasoft.engine.business.application.ApplicationVisibility; import org.bonitasoft.engine.business.application.IApplication; import org.bonitasoft.engine.business.application.impl.ApplicationImpl; import org.bonitasoft.engine.business.application.impl.ApplicationLinkImpl; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.impl.SearchResultImpl; import org.bonitasoft.web.rest.model.application.AbstractApplicationItem; import org.bonitasoft.web.rest.model.application.ApplicationDefinition; import org.bonitasoft.web.rest.model.application.ApplicationItem; import org.bonitasoft.web.rest.model.application.ApplicationLinkItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.ItemDefinitionFactory; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.junit.Before; import org.junit.Test; 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 ApplicationDataStoreTest extends APITestWithMock { @Mock private ApplicationAPI applicationAPI; @Mock private PageAPI pageAPI; @Mock private ApplicationItemConverter converter; @InjectMocks private ApplicationDataStore dataStore; @Mock private ApplicationPage applicationPage; @Mock private Page homePage; @Before public void setUp() throws Exception { ItemDefinitionFactory.setDefaultFactory(new ItemDefinitionFactory() { @Override public ItemDefinition defineItemDefinitions(final String token) { return new ApplicationDefinition(); } }); } @Test public void should_return_application_created_by_ApplicationAPI_converted_to_ApplicationItem_on_add() throws Exception { //given final ApplicationCreator creator = new ApplicationCreator("app", "My application", "1.0"); ApplicationItem app = new ApplicationItem(); given(converter.toApplicationCreator(app)).willReturn(creator); final ApplicationImpl application = new ApplicationImpl("app", "1.0", "app desc", 2L, 3L); given(applicationAPI.createApplication(creator)).willReturn(application); final ApplicationItem item = new ApplicationItem(); given(converter.toApplicationItem(application)).willReturn(item); given(pageAPI.getPageByName("custompage_home")).willReturn(homePage); given(homePage.getId()).willReturn(1L); given(applicationAPI.createApplicationPage(application.getId(), 1, "home")).willReturn(applicationPage); //when final ApplicationItem createdItem = dataStore.add(new ApplicationItem()); //then assertThat(createdItem).isEqualTo(item); } @Test public void should_return_application_link_created_by_ApplicationAPI_converted_to_ApplicationLinkItem_on_add() throws Exception { //given final ApplicationLinkCreator creator = new ApplicationLinkCreator("app", "My application", "1.0"); ApplicationLinkItem app = new ApplicationLinkItem(); given(converter.toApplicationLinkCreator(app)).willReturn(creator); final ApplicationLinkImpl application = new ApplicationLinkImpl("app", "1.0", "app desc"); given(applicationAPI.createApplicationLink(creator)).willReturn(application); final ApplicationLinkItem item = new ApplicationLinkItem(); given(converter.toApplicationItem(application)).willReturn(item); //when final ApplicationLinkItem createdItem = dataStore.add(new ApplicationLinkItem()); //then assertThat(createdItem).isEqualTo(item); } @Test public void should_create_default_home_page_on_add() throws Exception { //given final ApplicationCreator creator = new ApplicationCreator("app", "My application", "1.0"); ApplicationItem item = new ApplicationItem(); given(converter.toApplicationCreator(item)).willReturn(creator); final ApplicationImpl application = new ApplicationImpl("app", "1.0", "app desc", 2L, 3L); application.setId(1); given(applicationAPI.createApplication(creator)).willReturn(application); final ApplicationItem convertedAppItem = new ApplicationItem(); given(converter.toApplicationItem(application)).willReturn(convertedAppItem); given(pageAPI.getPageByName("custompage_home")).willReturn(homePage); given(homePage.getId()).willReturn(1L); given(applicationAPI.createApplicationPage(application.getId(), 1L, "home")).willReturn(applicationPage); given(applicationPage.getId()).willReturn(3L); //when dataStore.add(new ApplicationItem()); //then verify(applicationAPI, times(1)).setApplicationHomePage(application.getId(), 3); } @Test public void should_return_application_updated_by_ApplicationAPI_converted_to_ApplicationItem_on_update() throws Exception { //given final HashMap attributesToUpDate = new HashMap<>(); attributesToUpDate.put(ApplicationItem.ATTRIBUTE_TOKEN, "app_name"); attributesToUpDate.put(ApplicationItem.ATTRIBUTE_DISPLAY_NAME, "App display name"); final ApplicationUpdater applicationUpdater = new ApplicationUpdater(); given(converter.toApplicationUpdater(attributesToUpDate)).willReturn(applicationUpdater); final ApplicationImpl application = new ApplicationImpl("app", "1.0", "app desc", 2L, 3L); given(applicationAPI.updateApplication(1, applicationUpdater)).willReturn(application); given(applicationAPI.getIApplication(1)).willReturn(application); final ApplicationItem item = new ApplicationItem(); given(converter.toApplicationItem(application)).willReturn(item); //when final ApplicationItem createdItem = (ApplicationItem) dataStore.update(APIID.makeAPIID(1L), attributesToUpDate); //then verify(converter, times(1)).toApplicationUpdater(attributesToUpDate); verify(applicationAPI, times(1)).updateApplication(1, applicationUpdater); verify(converter, times(1)).toApplicationItem(application); assertThat(createdItem).isEqualTo(new ApplicationItem()); } @Test public void should_return_application_link_updated_by_ApplicationAPI_converted_to_ApplicationLinkItem_on_update() throws Exception { //given final HashMap attributesToUpDate = new HashMap<>(); attributesToUpDate.put(ApplicationLinkItem.ATTRIBUTE_TOKEN, "app_name"); attributesToUpDate.put(ApplicationLinkItem.ATTRIBUTE_DISPLAY_NAME, "App display name"); final ApplicationLinkUpdater applicationUpdater = new ApplicationLinkUpdater(); given(converter.toApplicationLinkUpdater(attributesToUpDate)).willReturn(applicationUpdater); final ApplicationLinkImpl application = new ApplicationLinkImpl("app", "1.0", "app desc"); given(applicationAPI.updateApplicationLink(1, applicationUpdater)).willReturn(application); given(applicationAPI.getIApplication(1)).willReturn(application); final ApplicationLinkItem item = new ApplicationLinkItem(); given(converter.toApplicationItem(application)).willReturn(item); //when final ApplicationLinkItem createdItem = (ApplicationLinkItem) dataStore.update(APIID.makeAPIID(1L), attributesToUpDate); //then verify(converter, times(1)).toApplicationLinkUpdater(attributesToUpDate); verify(applicationAPI, times(1)).updateApplicationLink(1, applicationUpdater); verify(converter, times(1)).toApplicationItem(application); assertThat(createdItem).isEqualTo(new ApplicationLinkItem()); } @Test(expected = APIException.class) public void should_throw_APIException_when_ApplicationAPI_throws_an_exception_on_add() throws Exception { ApplicationItem app = new ApplicationItem(); final ApplicationCreator creator = new ApplicationCreator("app", "My application", "1.0"); given(pageAPI.getPageByName("custompage_home")).willReturn(homePage); given(converter.toApplicationCreator(app)).willReturn(creator); //given when(applicationAPI.createApplication(any())).thenThrow(new CreationException("")); //when dataStore.add(app); } @Test(expected = APIException.class) public void should_throw_APIException_when_ApplicationAPI_throws_an_exception_on_UpDate() throws Exception { //given given(applicationAPI.getIApplication(eq(1L))).willReturn(mock(ApplicationImpl.class)); when(applicationAPI.updateApplication(eq(1L), any())).thenThrow(new UpdateException("")); //when dataStore.update(APIID.makeAPIID(1L), new HashMap<>()); } @Test public void should_return_the_good_application_on_get() throws Exception { //given final ApplicationImpl application = new ApplicationImpl("app", "1.0", "app desc"); final ApplicationItem item = new ApplicationItem(); given(converter.toApplicationItem(application)).willReturn(item); application.setId(1); given(applicationAPI.getIApplication(1)).willReturn(application); //when final AbstractApplicationItem retrivedItem = dataStore.get(APIID.makeAPIID("1")); //then assertThat(retrivedItem).isEqualTo(item); } @Test public void should_return_the_good_application_link_on_get() throws Exception { //given final ApplicationLinkImpl application = new ApplicationLinkImpl("app", "1.0", "app desc"); final ApplicationLinkItem item = new ApplicationLinkItem(); given(converter.toApplicationItem(application)).willReturn(item); application.setId(1); given(applicationAPI.getIApplication(1)).willReturn(application); //when final AbstractApplicationItem retrivedItem = dataStore.get(APIID.makeAPIID("1")); //then assertThat(retrivedItem).isEqualTo(item); } @Test(expected = APIException.class) public void should_return_throw_APIException_on_get_when_engine_throws_exception() throws Exception { //given given(applicationAPI.getIApplication(1)).willThrow(new ApplicationNotFoundException(1)); //when dataStore.get(APIID.makeAPIID("1")); //then exception } @Test public void should_delete_the_good_Application_on_delete() throws Exception { //given //when dataStore.delete(Arrays. asList(APIID.makeAPIID("1"), APIID.makeAPIID("2"))); //then verify(applicationAPI, times(1)).deleteApplication(1); verify(applicationAPI, times(1)).deleteApplication(2); } @Test(expected = APIException.class) public void should_throw_APIException_on_delete_when_engine_throws_exception() throws Exception { doThrow(new DeletionException("")).when(applicationAPI).deleteApplication(1); //when dataStore.delete(Arrays. asList(APIID.makeAPIID("1"))); //then exception } @Test public void should_call_engine_with_good_parameters_on_search() throws Exception { //given final int page = 0; final int resultsByPage = 1; final String search = "string to Match"; final String orders = ApplicationItem.ATTRIBUTE_TOKEN + " DESC"; final HashMap filters = new HashMap<>(); filters.put(ApplicationItem.ATTRIBUTE_CREATED_BY, "1"); filters.put(ApplicationItem.ATTRIBUTE_VERSION, "1.0"); filters.put(ApplicationItem.FILTER_USER_ID, "4"); final ApplicationImpl application = new ApplicationImpl("app", "1.0", "app desccription"); application.setId(1); application.setCreationDate(new Date()); application.setLastUpdateDate(new Date()); application.setVisibility(ApplicationVisibility.ALL); given(applicationAPI.searchIApplications(any(SearchOptions.class))) .willReturn(new SearchResultImpl<>(2, Arrays. asList(application))); //when dataStore.search(page, resultsByPage, search, orders, filters); //then final ArgumentCaptor searchOptionCaptor = ArgumentCaptor.forClass(SearchOptions.class); verify(applicationAPI, times(1)).searchIApplications(searchOptionCaptor.capture()); final SearchOptions searchOption = searchOptionCaptor.getValue(); assertThat(searchOption.getFilters().get(0).getField()).isEqualTo(ApplicationItem.ATTRIBUTE_CREATED_BY); assertThat(searchOption.getFilters().get(0).getValue()).isEqualTo("1"); assertThat(searchOption.getFilters().get(1).getField()).isEqualTo(ApplicationItem.ATTRIBUTE_VERSION); assertThat(searchOption.getFilters().get(1).getValue()).isEqualTo("1.0"); assertThat(searchOption.getFilters().get(2).getField()).isEqualTo(ApplicationItem.FILTER_USER_ID); assertThat(searchOption.getFilters().get(2).getValue()).isEqualTo("4"); assertThat(searchOption.getSearchTerm()).isEqualTo(search); assertThat(searchOption.getMaxResults()).isEqualTo(1); assertThat(searchOption.getStartIndex()).isEqualTo(0); assertThat(searchOption.getSorts().get(0).getField()).isEqualTo(ApplicationItem.ATTRIBUTE_TOKEN); assertThat(searchOption.getSorts().get(0).getOrder()).isEqualTo(Order.DESC); } @Test public void should_return_a_valid_ItemSearchResult_on_search() throws Exception { //given final int page = 0; final int resultsByPage = 1; final String search = "string to Match"; final String orders = ApplicationItem.ATTRIBUTE_TOKEN + " DESC"; final HashMap filters = new HashMap<>(); filters.put(ApplicationItem.ATTRIBUTE_CREATED_BY, "1"); filters.put(ApplicationItem.ATTRIBUTE_VERSION, "1.0"); final ApplicationImpl application = new ApplicationImpl("app", "1.0", "app desccription"); application.setId(1); application.setCreationDate(new Date()); application.setLastUpdateDate(new Date()); application.setVisibility(ApplicationVisibility.ALL); given(applicationAPI.searchIApplications(any(SearchOptions.class))) .willReturn(new SearchResultImpl<>(2, Arrays. asList(application))); //when final ItemSearchResult retrievedItems = dataStore.search(page, resultsByPage, search, orders, filters); //then assertThat(retrievedItems.getLength()).isEqualTo(1); assertThat(retrievedItems.getPage()).isEqualTo(0); assertThat(retrievedItems.getTotal()).isEqualTo(2); assertThat(retrievedItems.getResults().get(0).getToken()).isEqualTo("app"); } @Test(expected = APIException.class) public void should_throw_APIException_on_search_when_engine_throws_exception() throws Exception { //given given(applicationAPI.searchIApplications(any(SearchOptions.class))) .willThrow(new SearchException(new Exception())); //when final String orders = ApplicationItem.ATTRIBUTE_TOKEN + " DESC"; dataStore.search(1, 2, "search", orders, Collections. emptyMap()); //then exception } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationItemConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.application; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.UUID; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.IconDescriptor; import org.bonitasoft.engine.business.application.*; import org.bonitasoft.engine.business.application.impl.ApplicationImpl; import org.bonitasoft.web.rest.model.application.ApplicationItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.junit.Before; import org.junit.Test; public class ApplicationItemConverterTest extends APITestWithMock { private static final String STATE = ApplicationState.DEACTIVATED.name(); private static final long UPDATED_BY = 12L; private static final long CREATED_BY = 11; private static final String ICON = UUID.randomUUID().toString(); private static final String DESCRIPTION = "App description"; private static final String VERSION = "1.0"; private static final String TOKEN = "app"; private static final String DISPLAY_NAME = "display app name"; private static final Date CREATION_DATE = new Date(); private static final Date UPDATE_DATE = new Date(CREATION_DATE.getTime() + 1000); private static final long PROFILE_ID = 1L; private static final long HOME_PAGE_ID = 2L; private static final long LAYOUT_ID = 3L; private static final long THEME_ID = 4L; private static final ApplicationVisibility APPLICATION_VISIBILITY = ApplicationVisibility.RESTRICTED; private ApplicationItemConverter converter; private BonitaHomeFolderAccessor bonitaHomeFolderAccessor = mock(BonitaHomeFolderAccessor.class); @Before public void setUp() throws Exception { converter = new ApplicationItemConverter(bonitaHomeFolderAccessor); } @Test public void toApplicationItem_should_map_all_fields() throws Exception { //given final ApplicationImpl application = new ApplicationImpl(TOKEN, VERSION, DESCRIPTION, LAYOUT_ID, THEME_ID); application.setId(15); application.setDisplayName(DISPLAY_NAME); application.setHasIcon(true); application.setCreationDate(CREATION_DATE); application.setCreatedBy(CREATED_BY); application.setLastUpdateDate(UPDATE_DATE); application.setUpdatedBy(UPDATED_BY); application.setState(STATE); application.setProfileId(PROFILE_ID); application.setHomePageId(HOME_PAGE_ID); application.setVisibility(APPLICATION_VISIBILITY); application.setEditable(true); //when ApplicationItem item = (ApplicationItem) converter.toApplicationItem(application); //then assertThat(item).isNotNull(); assertThat(item.getId().toLong()).isEqualTo(15); assertThat(item.getToken()).isEqualTo(TOKEN); assertThat(item.getDisplayName()).isEqualTo(DISPLAY_NAME); assertThat(item.getVersion()).isEqualTo(VERSION); assertThat(item.getDescription()).isEqualTo(DESCRIPTION); assertThat(item.getIcon()).isEqualTo("../API/applicationIcon/15?t=" + UPDATE_DATE.getTime()); assertThat(item.getCreationDate()).isEqualTo(String.valueOf(CREATION_DATE.getTime())); assertThat(item.getCreatedBy()).isEqualTo(CREATED_BY); assertThat(item.getLastUpdateDate()).isEqualTo(String.valueOf(UPDATE_DATE.getTime())); assertThat(item.getUpdatedBy()).isEqualTo(UPDATED_BY); assertThat(item.getState()).isEqualTo(STATE); assertThat(item.getProfileId().toLong()).isEqualTo(PROFILE_ID); assertThat(item.getHomePageId().toLong()).isEqualTo(HOME_PAGE_ID); assertThat(item.getLayoutId().toLong()).isEqualTo(LAYOUT_ID); assertThat(item.getVisibility()).isEqualTo(APPLICATION_VISIBILITY.name()); assertThat(item.isEditable()).isEqualTo(true); application.setHasIcon(false); item = (ApplicationItem) converter.toApplicationItem(application); assertThat(item.getIcon()).isEmpty(); } @Test public void applicationItem_with_null_homepage_id_should_not_return_null() throws Exception { //given final ApplicationImpl application = new ApplicationImpl(DISPLAY_NAME, VERSION, DESCRIPTION, LAYOUT_ID, THEME_ID); application.setId(15); application.setDisplayName(DISPLAY_NAME); application.setCreationDate(CREATION_DATE); application.setCreatedBy(CREATED_BY); application.setLastUpdateDate(UPDATE_DATE); application.setUpdatedBy(UPDATED_BY); application.setState(STATE); application.setProfileId(PROFILE_ID); application.setHomePageId(null); application.setVisibility(APPLICATION_VISIBILITY); //when final ApplicationItem item = (ApplicationItem) converter.toApplicationItem(application); //then assertThat(item).isNotNull(); assertThat(item.getAttributeValue(ApplicationItem.ATTRIBUTE_HOME_PAGE_ID)).isEqualTo("-1"); } @Test public void applicationItem_with_null_Layout_id_should_not_return_null() throws Exception { //given final ApplicationImpl application = new ApplicationImpl(DISPLAY_NAME, VERSION, DESCRIPTION, null, THEME_ID); application.setId(15); application.setDisplayName(DISPLAY_NAME); application.setCreationDate(CREATION_DATE); application.setCreatedBy(CREATED_BY); application.setLastUpdateDate(UPDATE_DATE); application.setUpdatedBy(UPDATED_BY); application.setState(STATE); application.setProfileId(PROFILE_ID); application.setHomePageId(null); application.setVisibility(APPLICATION_VISIBILITY); //when final ApplicationItem item = (ApplicationItem) converter.toApplicationItem(application); //then assertThat(item).isNotNull(); assertThat(item.getAttributeValue(ApplicationItem.ATTRIBUTE_LAYOUT_ID)).isEqualTo("-1"); } @Test public void applicationItem_with_null_Theme_id_should_not_return_null() throws Exception { //given final ApplicationImpl application = new ApplicationImpl(DISPLAY_NAME, VERSION, DESCRIPTION, LAYOUT_ID, null); application.setId(15); application.setDisplayName(DISPLAY_NAME); application.setCreationDate(CREATION_DATE); application.setCreatedBy(CREATED_BY); application.setLastUpdateDate(UPDATE_DATE); application.setUpdatedBy(UPDATED_BY); application.setState(STATE); application.setProfileId(PROFILE_ID); application.setHomePageId(null); application.setVisibility(APPLICATION_VISIBILITY); //when final ApplicationItem item = (ApplicationItem) converter.toApplicationItem(application); //then assertThat(item).isNotNull(); assertThat(item.getAttributeValue(ApplicationItem.ATTRIBUTE_THEME_ID)).isEqualTo("-1"); } @Test public void toApplicationCreator_should_map_all_fields() throws Exception { //given final ApplicationItem item = new ApplicationItem(); item.setToken(TOKEN); item.setDisplayName(DISPLAY_NAME); item.setVersion(VERSION); item.setDescription(DESCRIPTION); item.setProfileId(PROFILE_ID); //when final ApplicationCreator creator = converter.toApplicationCreator(item); //then assertThat(creator).isNotNull(); final Map fields = creator.getFields(); assertThat(fields.get(ApplicationField.TOKEN)).isEqualTo(TOKEN); assertThat(fields.get(ApplicationField.DISPLAY_NAME)).isEqualTo(DISPLAY_NAME); assertThat(fields.get(ApplicationField.VERSION)).isEqualTo(VERSION); assertThat(fields.get(ApplicationField.DESCRIPTION)).isEqualTo(DESCRIPTION); assertThat(fields.get(ApplicationField.PROFILE_ID)).isEqualTo(PROFILE_ID); } @Test public void toApplicationUpdater_should_map_all_fields() throws Exception { //given final HashMap fields = new HashMap<>(); fields.put(ApplicationItem.ATTRIBUTE_TOKEN, TOKEN); fields.put(ApplicationItem.ATTRIBUTE_DISPLAY_NAME, DISPLAY_NAME); fields.put(ApplicationItem.ATTRIBUTE_DESCRIPTION, DESCRIPTION); fields.put(ApplicationItem.ATTRIBUTE_VERSION, VERSION); fields.put(ApplicationItem.ATTRIBUTE_ICON, ICON); fields.put(ApplicationItem.ATTRIBUTE_PROFILE_ID, String.valueOf(PROFILE_ID)); fields.put(ApplicationItem.ATTRIBUTE_HOME_PAGE_ID, String.valueOf(HOME_PAGE_ID)); fields.put(ApplicationItem.ATTRIBUTE_STATE, STATE); IconDescriptor iconDescriptor = new IconDescriptor(ICON, "theContent".getBytes()); doReturn(iconDescriptor).when(bonitaHomeFolderAccessor).getIconFromFileSystem(ICON); //when ApplicationUpdater updater = converter.toApplicationUpdater(fields); //then assertThat(updater).isNotNull(); assertThat(updater.getFields().get(ApplicationField.TOKEN)).isEqualTo(TOKEN); assertThat(updater.getFields().get(ApplicationField.DISPLAY_NAME)).isEqualTo(DISPLAY_NAME); assertThat(updater.getFields().get(ApplicationField.DESCRIPTION)).isEqualTo(DESCRIPTION); assertThat(updater.getFields().get(ApplicationField.VERSION)).isEqualTo(VERSION); assertThat(updater.getFields().get(ApplicationField.ICON_CONTENT)).isEqualTo("theContent".getBytes()); assertThat(updater.getFields().get(ApplicationField.ICON_FILE_NAME)).isEqualTo(ICON); assertThat(updater.getFields().get(ApplicationField.PROFILE_ID)).isEqualTo(PROFILE_ID); assertThat(updater.getFields().get(ApplicationField.HOME_PAGE_ID)).isEqualTo(HOME_PAGE_ID); } @Test public void update_with_no_homePageId_should_send_null() { //given final HashMap fields = new HashMap<>(); fields.put(ApplicationItem.ATTRIBUTE_HOME_PAGE_ID, "-1"); //when final ApplicationUpdater updater = converter.toApplicationUpdater(fields); //then assertThat(updater).isNotNull(); assertThat(updater.getFields().get(ApplicationField.HOME_PAGE_ID)).isEqualTo(null); } @Test public void update_with_no_layout_page_should_send_null() { //given final HashMap fields = new HashMap<>(); fields.put(ApplicationItem.ATTRIBUTE_LAYOUT_ID, "-1"); //when final ApplicationUpdater updater = converter.toApplicationUpdater(fields); //then assertThat(updater).isNotNull(); assertThat(updater.getFields().get(ApplicationField.LAYOUT_ID)).isEqualTo(null); } @Test public void update_with_no_Theme_page_should_send_null() { //given final HashMap fields = new HashMap<>(); fields.put(ApplicationItem.ATTRIBUTE_THEME_ID, "-1"); //when final ApplicationUpdater updater = converter.toApplicationUpdater(fields); //then assertThat(updater).isNotNull(); assertThat(updater.getFields().get(ApplicationField.THEME_ID)).isEqualTo(null); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationSearchDescriptorConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.application; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.business.application.ApplicationSearchDescriptor; import org.bonitasoft.web.rest.model.application.ApplicationItem; import org.junit.Test; public class ApplicationSearchDescriptorConverterTest { private final ApplicationSearchDescriptorConverter converter = new ApplicationSearchDescriptorConverter(); @Test public void should_return_ApplicationSearchDescriptor_id_on_convert_attribute_id() { //when final String value = converter.convert(ApplicationItem.ATTRIBUTE_ID); //then assertThat(value).isEqualTo(ApplicationSearchDescriptor.ID); } @Test public void should_return_ApplicationSearchDescriptor_name_on_convert_attribute_name() { //when final String value = converter.convert(ApplicationItem.ATTRIBUTE_TOKEN); //then assertThat(value).isEqualTo(ApplicationSearchDescriptor.TOKEN); } @Test public void should_return_ApplicationSearchDescriptor_name_on_convert_attribute_displayName() { //when final String value = converter.convert(ApplicationItem.ATTRIBUTE_DISPLAY_NAME); //then assertThat(value).isEqualTo(ApplicationSearchDescriptor.DISPLAY_NAME); } @Test public void should_return_ApplicationSearchDescriptor_version_on_convert_attribute_version() { //when final String value = converter.convert(ApplicationItem.ATTRIBUTE_VERSION); //then assertThat(value).isEqualTo(ApplicationSearchDescriptor.VERSION); } @Test public void should_return_ApplicationSearchDescriptor_creationDate_on_convert_attribute_creationDate() { //when final String value = converter.convert(ApplicationItem.ATTRIBUTE_CREATION_DATE); //then assertThat(value).isEqualTo(ApplicationSearchDescriptor.CREATION_DATE); } @Test public void should_return_ApplicationSearchDescriptor_createdBy_on_convert_attribute_createdBy() { //when final String value = converter.convert(ApplicationItem.ATTRIBUTE_CREATED_BY); //then assertThat(value).isEqualTo(ApplicationSearchDescriptor.CREATED_BY); } @Test public void should_return_ApplicationSearchDescriptor_lastUpdateDate_on_convert_attribute_lastUpdateDate() { //when final String value = converter.convert(ApplicationItem.ATTRIBUTE_LAST_UPDATE_DATE); //then assertThat(value).isEqualTo(ApplicationSearchDescriptor.LAST_UPDATE_DATE); } @Test public void should_return_ApplicationSearchDescriptor_updatedBy_on_convert_attribute_updatedBy() { //when final String value = converter.convert(ApplicationItem.ATTRIBUTE_UPDATED_BY); //then assertThat(value).isEqualTo(ApplicationSearchDescriptor.UPDATED_BY); } @Test public void should_return_ApplicationSearchDescriptor_state_on_convert_attribute_state() { //when final String value = converter.convert(ApplicationItem.ATTRIBUTE_STATE); //then assertThat(value).isEqualTo(ApplicationSearchDescriptor.STATE); } @Test public void should_return_ApplicationSearchDescriptor_layoutid_on_convert_attribute_layoutid() { //when final String value = converter.convert(ApplicationItem.ATTRIBUTE_LAYOUT_ID); //then assertThat(value).isEqualTo(ApplicationSearchDescriptor.LAYOUT_ID); } @Test public void should_return_ApplicationSearchDescriptor_themeid_on_convert_attribute_themeid() { //when final String value = converter.convert(ApplicationItem.ATTRIBUTE_THEME_ID); //then assertThat(value).isEqualTo(ApplicationSearchDescriptor.THEME_ID); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuDataStoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationmenu; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.business.application.ApplicationMenu; import org.bonitasoft.engine.business.application.ApplicationMenuNotFoundException; import org.bonitasoft.engine.business.application.ApplicationMenuUpdater; import org.bonitasoft.engine.business.application.impl.ApplicationMenuImpl; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.impl.SearchResultImpl; import org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuDefinition; import org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.ItemDefinitionFactory; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.junit.Before; import org.junit.Test; 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 ApplicationMenuDataStoreTest extends APITestWithMock { @Mock private ApplicationAPI applicationAPI; @Mock private ApplicationMenuItemConverter converter; @Mock private ApplicationMenuUpdater applicationMenuUpdater; @InjectMocks private ApplicationMenuDataStore dataStore; @Before public void setUp() throws Exception { ItemDefinitionFactory.setDefaultFactory(new ItemDefinitionFactory() { @Override public ItemDefinition defineItemDefinitions(final String token) { return new ApplicationMenuDefinition(); } }); } @Test public void should_return_result_of_engine_call_converted_to_item_on_add() throws Exception { //given final ApplicationMenuItem itemToCreate = new ApplicationMenuItem(); final ApplicationMenuImpl applicationMenu = new ApplicationMenuImpl("firstMenu", 11L, 14L, 1); given(applicationAPI.createApplicationMenu(any())).willReturn(applicationMenu); given(converter.toApplicationMenuItem(applicationMenu)).willReturn(new ApplicationMenuItem()); //when final ApplicationMenuItem createdItem = dataStore.add(itemToCreate); //then assertThat(createdItem).isEqualTo(itemToCreate); } @Test(expected = APIException.class) public void should_throw_APIException_when_engine_throws_CreationException_on_add() throws Exception { //given given(applicationAPI.createApplicationMenu(any())) .willThrow(new CreationException("")); //when dataStore.add(new ApplicationMenuItem()); //then exception } @Test public void should_return_application_menu_updated_by_ApplicationAPI_and_converted_to_ApplicationItem_on_update() throws Exception { //given final HashMap attributesToUpDate = new HashMap<>(); given(converter.toApplicationMenuUpdater(any(Map.class))).willReturn(applicationMenuUpdater); final ApplicationMenuImpl applicationMenu = new ApplicationMenuImpl("menu name", 1L, 1L, 2); given(applicationAPI.updateApplicationMenu(1, applicationMenuUpdater)).willReturn(applicationMenu); final ApplicationMenuItem item = new ApplicationMenuItem(); given(converter.toApplicationMenuItem(applicationMenu)).willReturn(item); //when final ApplicationMenuItem createdItem = dataStore.update(APIID.makeAPIID(1L), attributesToUpDate); //then verify(converter, times(1)).toApplicationMenuUpdater(attributesToUpDate); verify(applicationAPI, times(1)).updateApplicationMenu(1, applicationMenuUpdater); verify(converter, times(1)).toApplicationMenuItem(applicationMenu); assertThat(createdItem).isEqualTo(new ApplicationMenuItem()); } @Test public void should_return_the_ApplicationMenu_supplied_by_the_engine_converted_to_item_on_get() throws Exception { //given final ApplicationMenuItem itemToCreate = new ApplicationMenuItem(); final ApplicationMenuImpl applicationMenu = new ApplicationMenuImpl("firstMenu", 11L, 14L, 1); given(applicationAPI.getApplicationMenu(1L)).willReturn(applicationMenu); given(converter.toApplicationMenuItem(applicationMenu)).willReturn(new ApplicationMenuItem()); //when final ApplicationMenuItem createdItem = dataStore.get(APIID.makeAPIID(1L)); //then assertThat(createdItem).isEqualTo(itemToCreate); } @Test(expected = APIException.class) public void should_throw_APIException_when_the_engine_throw_NotFoundException_on_get() throws Exception { //given given(applicationAPI.getApplicationMenu(1)).willThrow(new ApplicationMenuNotFoundException("")); //when dataStore.get(APIID.makeAPIID("1")); //then exception } @Test public void should_delete_the_good_Application_Page_on_delete() throws Exception { //given //when dataStore.delete(Arrays. asList(APIID.makeAPIID("1"), APIID.makeAPIID("2"))); //then verify(applicationAPI, times(1)).deleteApplicationMenu(1); verify(applicationAPI, times(1)).deleteApplicationMenu(2); } @Test(expected = APIException.class) public void should_throw_APIException_on_delete_when_engine_throws_exception() throws Exception { doThrow(new DeletionException("")).when(applicationAPI).deleteApplicationMenu(1); //when dataStore.delete(Arrays. asList(APIID.makeAPIID("1"))); //then exception } @Test public void should_return_a_valid_ItemSearchResult_on_search() throws Exception { //given final String orders = ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME + " DESC"; final ApplicationMenuImpl appMenu = new ApplicationMenuImpl("MyMenu", 11L, 2L, 1); appMenu.setParentId(-1L); given(applicationAPI.searchApplicationMenus(any(SearchOptions.class))).willReturn( new SearchResultImpl<>(2, Arrays. asList(appMenu))); //when final ItemSearchResult retrievedItems = dataStore.search(0, 1, null, orders, Collections. emptyMap()); //then assertThat(retrievedItems).isNotNull(); assertThat(retrievedItems.getLength()).isEqualTo(1); assertThat(retrievedItems.getPage()).isEqualTo(0); assertThat(retrievedItems.getTotal()).isEqualTo(2); assertThat(retrievedItems.getResults().get(0).getDisplayName()).isEqualTo("MyMenu"); } @Test public void should_call_engine_with_good_parameters_on_search() throws Exception { //given final int page = 0; final int resultsByPage = 1; final String search = "string to Match"; final String orders = ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME + " DESC"; final HashMap filters = new HashMap<>(); filters.put(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID, "1"); final ApplicationMenuImpl appPage = new ApplicationMenuImpl("MyMenu", 11L, 1L, 2); appPage.setParentId(-1L); given(applicationAPI.searchApplicationMenus(any(SearchOptions.class))).willReturn( new SearchResultImpl<>(2, Arrays. asList(appPage))); //when dataStore.search(page, resultsByPage, search, orders, filters); //then final ArgumentCaptor captor = ArgumentCaptor.forClass(SearchOptions.class); verify(applicationAPI, times(1)).searchApplicationMenus(captor.capture()); final SearchOptions searchOption = captor.getValue(); assertThat(searchOption.getFilters()).hasSize(1); assertThat(searchOption.getFilters().get(0).getField()) .isEqualTo(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID); assertThat(searchOption.getFilters().get(0).getValue()).isEqualTo("1"); assertThat(searchOption.getSearchTerm()).isEqualTo(search); assertThat(searchOption.getMaxResults()).isEqualTo(1); assertThat(searchOption.getStartIndex()).isEqualTo(0); assertThat(searchOption.getSorts()).hasSize(1); assertThat(searchOption.getSorts().get(0).getField()).isEqualTo(ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME); assertThat(searchOption.getSorts().get(0).getOrder()).isEqualTo(Order.DESC); } @Test(expected = APIException.class) public void should_throw_APIException_when_engine_throws_SearchException_on_search() throws Exception { //given final String orders = ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME + " DESC"; given(applicationAPI.searchApplicationMenus(any(SearchOptions.class))).willThrow(new SearchException(null)); //when dataStore.search(0, 1, null, orders, Collections. emptyMap()); //then exception } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuFilterCreatorTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationmenu; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import java.io.Serializable; import org.bonitasoft.web.rest.server.datastore.filter.Filter; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ApplicationMenuFilterCreatorTest { @Mock private ApplicationMenuSearchDescriptorConverter converter; @InjectMocks private ApplicationMenuFilterCreator creator; @Test public void should_return_filter_based_on_given_field_and_value_on_create() throws Exception { //given given(converter.convert("name")).willReturn("name"); //when final Filter filter = creator.create("name", "a name"); //then assertThat(filter).isNotNull(); assertThat(filter.getField()).isEqualTo("name"); assertThat(filter.getValue()).isEqualTo("a name"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuItemConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationmenu; import static org.assertj.core.api.Assertions.assertThat; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.business.application.ApplicationMenuCreator; import org.bonitasoft.engine.business.application.ApplicationMenuField; import org.bonitasoft.engine.business.application.ApplicationMenuUpdater; import org.bonitasoft.engine.business.application.impl.ApplicationMenuImpl; import org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.junit.Before; import org.junit.Test; public class ApplicationMenuItemConverterTest extends APITestWithMock { private static final String DISPLAY_NAME = "menu name"; private static final long APPLICATION_ID = 1L; private static final long APPLICATION_PAGE_ID = 2L; private static final long PARENT_MENU_ID = 3L; private static final int INDEX = 4; private static final long MENU_ID = 10L; private ApplicationMenuItemConverter converter; @Before public void setUp() throws Exception { converter = new ApplicationMenuItemConverter(); } @Test public void toApplicationMenuItem_should_map_all_fields() throws Exception { //given final ApplicationMenuImpl applicationMenu = new ApplicationMenuImpl(DISPLAY_NAME, APPLICATION_ID, APPLICATION_PAGE_ID, INDEX); applicationMenu.setParentId(PARENT_MENU_ID); applicationMenu.setId(MENU_ID); //when final ApplicationMenuItem item = converter.toApplicationMenuItem(applicationMenu); //then assertThat(item).isNotNull(); assertThat(item.getId().toLong()).isEqualTo(MENU_ID); assertThat(item.getDisplayName()).isEqualTo(DISPLAY_NAME); assertThat(item.getApplicationId().toLong()).isEqualTo(APPLICATION_ID); assertThat(item.getApplicationPageId().toLong()).isEqualTo(APPLICATION_PAGE_ID); assertThat(item.getParentMenuId().toLong()).isEqualTo(PARENT_MENU_ID); assertThat(item.getMenuIndex()).isEqualTo(INDEX); } @Test public void applicationMenuItem_with_null_parent_id_should_not_return_null() throws Exception { //given final ApplicationMenuImpl applicationMenu = new ApplicationMenuImpl(DISPLAY_NAME, APPLICATION_ID, APPLICATION_PAGE_ID, INDEX); applicationMenu.setParentId(null); applicationMenu.setId(MENU_ID); //when final ApplicationMenuItem item = converter.toApplicationMenuItem(applicationMenu); //then assertThat(item.getAttributeValue(ApplicationMenuItem.ATTRIBUTE_PARENT_MENU_ID)).isEqualTo("-1"); } @Test public void applicationMenuItem_with_null_page_id_should_not_return_null() throws Exception { //given final ApplicationMenuImpl applicationMenu = new ApplicationMenuImpl(DISPLAY_NAME, APPLICATION_ID, null, INDEX); applicationMenu.setParentId(PARENT_MENU_ID); applicationMenu.setId(MENU_ID); //when final ApplicationMenuItem item = converter.toApplicationMenuItem(applicationMenu); //then assertThat(item).isNotNull(); assertThat(item.getAttributeValue(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID)).isEqualTo("-1"); } @Test public void toApplicationMenuCreator_should_map_all_fields() throws Exception { //given final ApplicationMenuItem item = new ApplicationMenuItem(); item.setDisplayName(DISPLAY_NAME); item.setApplicationId(APPLICATION_ID); item.setApplicationPageId(APPLICATION_PAGE_ID); item.setMenuIndex(INDEX); item.setParentMenuId(PARENT_MENU_ID); //when final ApplicationMenuCreator creator = converter.toApplicationMenuCreator(item); //then assertThat(creator).isNotNull(); final Map fields = creator.getFields(); assertThat(fields.get(ApplicationMenuField.DISPLAY_NAME)).isEqualTo(DISPLAY_NAME); assertThat(fields.get(ApplicationMenuField.APPLICATION_ID)).isEqualTo(APPLICATION_ID); assertThat(fields.get(ApplicationMenuField.APPLICATION_PAGE_ID)).isEqualTo(APPLICATION_PAGE_ID); assertThat(fields.get(ApplicationMenuField.PARENT_ID)).isEqualTo(PARENT_MENU_ID); } @Test public void toApplicationMenuCreator_should_not_map_negativeId() throws Exception { //given final ApplicationMenuItem item = new ApplicationMenuItem(); item.setDisplayName(DISPLAY_NAME); item.setApplicationId(APPLICATION_ID); item.setApplicationPageId(-1L); item.setMenuIndex(INDEX); item.setParentMenuId(-1L); //when final ApplicationMenuCreator creator = converter.toApplicationMenuCreator(item); //then assertThat(creator).isNotNull(); final Map fields = creator.getFields(); assertThat(fields.get(ApplicationMenuField.DISPLAY_NAME)).isEqualTo(DISPLAY_NAME); assertThat(fields.get(ApplicationMenuField.APPLICATION_ID)).isEqualTo(APPLICATION_ID); assertThat(fields.get(ApplicationMenuField.APPLICATION_PAGE_ID)).isNull(); assertThat(fields.get(ApplicationMenuField.PARENT_ID)).isNull(); } @Test public void toApplicationMenuUpdater_should_map_all_fields() { //given final HashMap fields = new HashMap<>(); fields.put(ApplicationMenuItem.ATTRIBUTE_APPLICATION_ID, String.valueOf(APPLICATION_ID)); fields.put(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID, String.valueOf(APPLICATION_PAGE_ID)); fields.put(ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME, DISPLAY_NAME); fields.put(ApplicationMenuItem.ATTRIBUTE_MENU_INDEX, String.valueOf(INDEX)); fields.put(ApplicationMenuItem.ATTRIBUTE_PARENT_MENU_ID, String.valueOf(PARENT_MENU_ID)); //when final ApplicationMenuUpdater updater = converter.toApplicationMenuUpdater(fields); //then assertThat(updater).isNotNull(); assertThat(updater.getFields().get(ApplicationMenuField.DISPLAY_NAME)).isEqualTo(DISPLAY_NAME); assertThat(updater.getFields().get(ApplicationMenuField.APPLICATION_ID)).isEqualTo(null); assertThat(updater.getFields().get(ApplicationMenuField.APPLICATION_PAGE_ID)).isEqualTo(APPLICATION_PAGE_ID); assertThat(updater.getFields().get(ApplicationMenuField.INDEX)).isEqualTo(INDEX); assertThat(updater.getFields().get(ApplicationMenuField.PARENT_ID)).isEqualTo(PARENT_MENU_ID); } @Test public void update_with_no_pageId_should_send_null() { //given final HashMap fields = new HashMap<>(); fields.put(ApplicationMenuItem.ATTRIBUTE_APPLICATION_ID, "-1"); //when final ApplicationMenuUpdater updater = converter.toApplicationMenuUpdater(fields); //then assertThat(updater).isNotNull(); assertThat(updater.getFields().get(ApplicationMenuField.APPLICATION_ID)).isEqualTo(null); } @Test public void update_with_no_parent_menu_should_send_null() { //given final HashMap fields = new HashMap<>(); fields.put(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID, "-1"); //when final ApplicationMenuUpdater updater = converter.toApplicationMenuUpdater(fields); //then assertThat(updater).isNotNull(); assertThat(updater.getFields().get(ApplicationMenuField.PARENT_ID)).isEqualTo(null); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/applicationmenu/ApplicationMenuSearchDescriptorConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationmenu; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.business.application.ApplicationMenuSearchDescriptor; import org.bonitasoft.web.rest.model.applicationmenu.ApplicationMenuItem; import org.junit.Test; public class ApplicationMenuSearchDescriptorConverterTest { private final ApplicationMenuSearchDescriptorConverter converter = new ApplicationMenuSearchDescriptorConverter(); @Test public void should_return_ApplicationMenuSearchDescriptor_id_on_convert_attribute_id() throws Exception { //when final String convertedValue = converter.convert(ApplicationMenuItem.ATTRIBUTE_ID); //then assertThat(convertedValue).isEqualTo(ApplicationMenuSearchDescriptor.ID); } @Test public void should_return_ApplicationMenuSearchDescriptor_display_name_on_convert_attribute_display_name() throws Exception { //when final String convertedValue = converter.convert(ApplicationMenuItem.ATTRIBUTE_DISPLAY_NAME); //then assertThat(convertedValue).isEqualTo(ApplicationMenuSearchDescriptor.DISPLAY_NAME); } @Test public void should_return_ApplicationMenuSearchDescriptor_applicationPageId_on_convert_attribute_applicationPageId() throws Exception { //when final String convertedValue = converter.convert(ApplicationMenuItem.ATTRIBUTE_APPLICATION_PAGE_ID); //then assertThat(convertedValue).isEqualTo(ApplicationMenuSearchDescriptor.APPLICATION_PAGE_ID); } @Test public void should_return_ApplicationMenuSearchDescriptor_applicationId_on_convert_attribute_applicationId() throws Exception { //when final String convertedValue = converter.convert(ApplicationMenuItem.ATTRIBUTE_APPLICATION_ID); //then assertThat(convertedValue).isEqualTo(ApplicationMenuSearchDescriptor.APPLICATION_ID); } @Test public void should_return_ApplicationMenuSearchDescriptor_index_on_convert_attribute_menu_index() throws Exception { //when final String convertedValue = converter.convert(ApplicationMenuItem.ATTRIBUTE_MENU_INDEX); //then assertThat(convertedValue).isEqualTo(ApplicationMenuSearchDescriptor.INDEX); } @Test public void should_return_ApplicationMenuSearchDescriptor_index_on_convert_attribute_parent_menu() throws Exception { //when final String convertedValue = converter.convert(ApplicationMenuItem.ATTRIBUTE_PARENT_MENU_ID); //then assertThat(convertedValue).isEqualTo(ApplicationMenuSearchDescriptor.PARENT_ID); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageDataStoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationpage; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.*; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.business.application.ApplicationPage; import org.bonitasoft.engine.business.application.ApplicationPageNotFoundException; import org.bonitasoft.engine.business.application.impl.ApplicationPageImpl; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.impl.SearchResultImpl; import org.bonitasoft.web.rest.model.applicationpage.ApplicationPageDefinition; import org.bonitasoft.web.rest.model.applicationpage.ApplicationPageItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.ItemDefinitionFactory; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ApplicationPageDataStoreTest extends APITestWithMock { @Mock private ApplicationAPI applicationAPI; @Mock private ApplicationPageItemConverter converter; @Spy @InjectMocks private ApplicationPageDataStore dataStore; @Before public void setUp() throws Exception { ItemDefinitionFactory.setDefaultFactory(new ItemDefinitionFactory() { @Override public ItemDefinition defineItemDefinitions(final String token) { return new ApplicationPageDefinition(); } }); } @Test public void should_return_result_of_engine_call_converted_to_item_on_add() throws Exception { //given final ApplicationPageItem itemToCreate = new ApplicationPageItem(); itemToCreate.setToken("firstPage"); itemToCreate.setApplicationId(14L); itemToCreate.setPageId(28L); final ApplicationPageImpl applicationPage = new ApplicationPageImpl(14L, 28L, "firstPage"); given(applicationAPI.createApplicationPage(14L, 28L, "firstPage")).willReturn(applicationPage); //when final ApplicationPageItem createdItem = dataStore.add(itemToCreate); //then assertThat(createdItem).isEqualTo(createdItem); } @Test(expected = APIException.class) public void should_throw_APIException_when_engine_throws_CreationException_on_add() throws Exception { //given ApplicationPageItem applicationPageItem = new ApplicationPageItem(); applicationPageItem.setApplicationId(1L); applicationPageItem.setPageId(1L); applicationPageItem.setToken("token"); doThrow(new CreationException("test")).when(applicationAPI).createApplicationPage(anyLong(), anyLong(), anyString()); //when dataStore.add(applicationPageItem); //then exception } @Test public void should_return_the_applicationPage_supplied_by_the_engine_converted_to_item_on_get() throws Exception { //given final ApplicationPage applicationPage = mock(ApplicationPage.class); final ApplicationPageItem item = mock(ApplicationPageItem.class); given(applicationAPI.getApplicationPage(1)).willReturn(applicationPage); given(converter.toApplicationPageItem(applicationPage)).willReturn(item); //when final ApplicationPageItem retrievedItem = dataStore.get(APIID.makeAPIID("1")); //then assertThat(retrievedItem).isNotNull(); assertThat(retrievedItem).isEqualTo(item); } @Test(expected = APIException.class) public void should_throw_APIException_when_the_engine_throw_NotFoundException_on_get() throws Exception { //given given(applicationAPI.getApplicationPage(1)).willThrow(new ApplicationPageNotFoundException("")); //when dataStore.get(APIID.makeAPIID("1")); //then exception } @Test public void should_delete_the_good_Application_Page_on_delete() throws Exception { //given //when dataStore.delete(Arrays.asList(APIID.makeAPIID("1"), APIID.makeAPIID("2"))); //then verify(applicationAPI, times(1)).deleteApplicationPage(1); verify(applicationAPI, times(1)).deleteApplicationPage(2); } @Test(expected = APIException.class) public void should_throw_APIException_on_delete_when_engine_throws_exception() throws Exception { doThrow(new DeletionException("")).when(applicationAPI).deleteApplicationPage(1); //when dataStore.delete(List.of(APIID.makeAPIID("1"))); //then exception } @Test public void should_return_a_valid_ItemSearchResult_on_search() throws Exception { //given final String orders = ApplicationPageItem.ATTRIBUTE_TOKEN + " DESC"; final ApplicationPageImpl appPage = new ApplicationPageImpl(1, 11, "MyAppPage"); appPage.setId(1); final ApplicationPageItem item = new ApplicationPageItem(); item.setApplicationId(1L); item.setPageId(1L); item.setToken("MyAppPage"); given(converter.toApplicationPageItem(appPage)).willReturn(item); given(applicationAPI.searchApplicationPages(any(SearchOptions.class))).willReturn( new SearchResultImpl<>(2, List.of(appPage))); //when final ItemSearchResult retrievedItems = dataStore.search(0, 1, null, orders, Collections.emptyMap()); //then assertThat(retrievedItems).isNotNull(); assertThat(retrievedItems.getLength()).isEqualTo(1); assertThat(retrievedItems.getPage()).isEqualTo(0); assertThat(retrievedItems.getTotal()).isEqualTo(2); assertThat(retrievedItems.getResults().get(0).getToken()).isEqualTo("MyAppPage"); } @Test public void should_call_engine_with_good_parameters_on_search() throws Exception { //given final int page = 0; final int resultsByPage = 1; final String search = "string to Match"; final String orders = ApplicationPageItem.ATTRIBUTE_TOKEN + " DESC"; final HashMap filters = new HashMap<>(); filters.put(ApplicationPageItem.ATTRIBUTE_APPLICATION_ID, "1"); final ApplicationPageImpl appPage = new ApplicationPageImpl(1, 11, "MyAppPage"); appPage.setId(1); final ApplicationPageItem item = new ApplicationPageItem(); item.setApplicationId(1L); item.setPageId(1L); item.setToken("token"); doReturn(item).when(converter).toApplicationPageItem(appPage); given(applicationAPI.searchApplicationPages(any(SearchOptions.class))).willReturn( new SearchResultImpl<>(2, List.of(appPage))); //when dataStore.search(page, resultsByPage, search, orders, filters); //then final ArgumentCaptor captor = ArgumentCaptor.forClass(SearchOptions.class); verify(applicationAPI, times(1)).searchApplicationPages(captor.capture()); final SearchOptions searchOption = captor.getValue(); assertThat(searchOption.getFilters()).hasSize(1); assertThat(searchOption.getFilters().get(0).getField()).isEqualTo(ApplicationPageItem.ATTRIBUTE_APPLICATION_ID); assertThat(searchOption.getFilters().get(0).getValue()).isEqualTo("1"); assertThat(searchOption.getSearchTerm()).isEqualTo(search); assertThat(searchOption.getMaxResults()).isEqualTo(1); assertThat(searchOption.getStartIndex()).isEqualTo(0); assertThat(searchOption.getSorts()).hasSize(1); assertThat(searchOption.getSorts().get(0).getField()).isEqualTo(ApplicationPageItem.ATTRIBUTE_TOKEN); assertThat(searchOption.getSorts().get(0).getOrder()).isEqualTo(Order.DESC); } @Test(expected = APIException.class) public void should_throw_APIException_when_engine_throws_SearchException_on_search() throws Exception { //given final String orders = ApplicationPageItem.ATTRIBUTE_TOKEN + " DESC"; given(applicationAPI.searchApplicationPages(any(SearchOptions.class))).willThrow(new SearchException(null)); //when dataStore.search(0, 1, null, orders, Collections.emptyMap()); //then exception } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageFilterCreatorTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationpage; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import java.io.Serializable; import org.bonitasoft.web.rest.server.datastore.filter.Filter; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ApplicationPageFilterCreatorTest { @Mock private ApplicationPageSearchDescriptorConverter converter; @InjectMocks private ApplicationPageFilterCreator creator; @Test public void should_return_filter_based_on_given_field_and_value_on_create() throws Exception { //given given(converter.convert("name")).willReturn("name"); //when final Filter filter = creator.create("name", "a name"); //then assertThat(filter).isNotNull(); assertThat(filter.getField()).isEqualTo("name"); assertThat(filter.getValue()).isEqualTo("a name"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/applicationpage/ApplicationPageSearchDescriptorConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.applicationpage; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.business.application.ApplicationPageSearchDescriptor; import org.bonitasoft.web.rest.model.applicationpage.ApplicationPageItem; import org.junit.Test; public class ApplicationPageSearchDescriptorConverterTest { private final ApplicationPageSearchDescriptorConverter converter = new ApplicationPageSearchDescriptorConverter(); @Test public void should_return_ApplicationPageSearchDescriptor_id_on_convert_attribute_id() throws Exception { //when final String convertedValue = converter.convert(ApplicationPageItem.ATTRIBUTE_ID); //then assertThat(convertedValue).isEqualTo(ApplicationPageSearchDescriptor.ID); } @Test public void should_return_ApplicationPageSearchDescriptor_name_on_convert_attribute_name() throws Exception { //when final String convertedValue = converter.convert(ApplicationPageItem.ATTRIBUTE_TOKEN); //then assertThat(convertedValue).isEqualTo(ApplicationPageSearchDescriptor.TOKEN); } @Test public void should_return_ApplicationPageSearchDescriptor_applicationId_on_convert_attribute_applicationId() throws Exception { //when final String convertedValue = converter.convert(ApplicationPageItem.ATTRIBUTE_APPLICATION_ID); //then assertThat(convertedValue).isEqualTo(ApplicationPageSearchDescriptor.APPLICATION_ID); } @Test public void should_return_ApplicationPageSearchDescriptor_pageId_on_convert_attribute_pageId() throws Exception { //when final String convertedValue = converter.convert(ApplicationPageItem.ATTRIBUTE_PAGE_ID); //then assertThat(convertedValue).isEqualTo(ApplicationPageSearchDescriptor.PAGE_ID); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import static java.util.Collections.emptyMap; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstance; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstanceNotFoundException; import org.bonitasoft.engine.bpm.process.ArchivedProcessInstancesSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ArchivedCaseDatastoreTest extends APITestWithMock { private ArchivedCaseDatastore datastore; @Mock private ProcessAPI processAPI; @Mock private ArchivedProcessInstance archivedProcessInstance1; @Mock private ArchivedProcessInstance archivedProcessInstance2; @Mock private ArchivedProcessInstance archivedProcessInstance3; private long archivedProcessInstanceId1; private long archivedProcessInstanceId2; private long archivedProcessInstanceId3; private long sourceProcessInstanceId1; private long sourceProcessInstanceId2; private long sourceProcessInstanceId3; @Before public void initializeMocks() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException, ArchivedProcessInstanceNotFoundException { initMocks(this); datastore = spy(new ArchivedCaseDatastore(null)); doReturn(processAPI).when(datastore).getProcessApi(); archivedProcessInstanceId1 = 1L; archivedProcessInstanceId2 = 2L; archivedProcessInstanceId3 = 3L; sourceProcessInstanceId1 = 11L; sourceProcessInstanceId2 = 12L; sourceProcessInstanceId3 = 13L; doReturn(sourceProcessInstanceId1).when(archivedProcessInstance1).getSourceObjectId(); doReturn(sourceProcessInstanceId2).when(archivedProcessInstance2).getSourceObjectId(); doReturn(sourceProcessInstanceId3).when(archivedProcessInstance3).getSourceObjectId(); doReturn(archivedProcessInstance1).when(processAPI).getArchivedProcessInstance(archivedProcessInstanceId1); doReturn(archivedProcessInstance2).when(processAPI).getArchivedProcessInstance(archivedProcessInstanceId2); doReturn(archivedProcessInstance3).when(processAPI).getArchivedProcessInstance(archivedProcessInstanceId3); } @Test public void search_should_pass_long_parameters_as_long_to_engine_and_not_as_string() { // given: doReturn(mock(SearchResult.class)).when(datastore).runSearch(any(), any()); // when: datastore.search(0, 10, null, null, emptyMap()); // then: verify(datastore).addLongFilterToSearchBuilder(any(), any(), anyString(), eq(ProcessInstanceSearchDescriptor.PROCESS_DEFINITION_ID)); verify(datastore).addLongFilterToSearchBuilder(any(), any(), anyString(), eq(ProcessInstanceSearchDescriptor.STARTED_BY)); verify(datastore).addLongFilterToSearchBuilder(any(), any(), anyString(), eq(ArchivedProcessInstancesSearchDescriptor.SOURCE_OBJECT_ID)); } @Test public void should_delete_archive_case_call_right_engine_method() throws DeletionException, ArchivedProcessInstanceNotFoundException { //given final List idList = Arrays.asList(APIID.makeAPIID(archivedProcessInstanceId1), APIID.makeAPIID(archivedProcessInstanceId2), APIID.makeAPIID(archivedProcessInstanceId3)); //when datastore.delete(idList); //then verify(processAPI, times(3)).getArchivedProcessInstance(anyLong()); verify(processAPI).deleteArchivedProcessInstancesInAllStates( Arrays.asList( sourceProcessInstanceId1, sourceProcessInstanceId2, sourceProcessInstanceId3)); } @Test public void should_delete_all_archived_cases_when_one_id_is_given() throws DeletionException, ArchivedProcessInstanceNotFoundException { //given final List idList = Collections.singletonList(APIID.makeAPIID(archivedProcessInstanceId1)); //when datastore.delete(idList); //then verify(processAPI).getArchivedProcessInstance(archivedProcessInstanceId1); verify(processAPI, times(1)).getArchivedProcessInstance(archivedProcessInstanceId1); verify(processAPI) .deleteArchivedProcessInstancesInAllStates(Collections.singletonList(sourceProcessInstanceId1)); } @Test(expected = APIException.class) public void should_throw_an_api_exception_when_deletion_exception_is_rised() throws DeletionException { //given doThrow(new DeletionException("exception!")).when(processAPI) .deleteArchivedProcessInstancesInAllStates(anyList()); final List idList = Collections.singletonList(APIID.makeAPIID(archivedProcessInstanceId1)); //when datastore.delete(idList); } @Test public void testConvertEngineToConsoleItem() { //given ArchivedProcessInstance archivedProcessInstance = mock(ArchivedProcessInstance.class); doReturn("labelOne").when(archivedProcessInstance).getStringIndexLabel(1); doReturn("labelTwo").when(archivedProcessInstance).getStringIndexLabel(2); doReturn("labelThree").when(archivedProcessInstance).getStringIndexLabel(3); doReturn("labelFour").when(archivedProcessInstance).getStringIndexLabel(4); doReturn("labelFive").when(archivedProcessInstance).getStringIndexLabel(5); doReturn("valueOne").when(archivedProcessInstance).getStringIndexValue(1); doReturn("valueTwo").when(archivedProcessInstance).getStringIndexValue(2); doReturn("valueThree").when(archivedProcessInstance).getStringIndexValue(3); doReturn("valueFour").when(archivedProcessInstance).getStringIndexValue(4); doReturn("valueFive").when(archivedProcessInstance).getStringIndexValue(5); // when final ArchivedCaseItem archivedCaseItem = datastore.convertEngineToConsoleItem(archivedProcessInstance); // then //check labels assertThat(archivedCaseItem.getSearchIndex1Label()).isEqualTo("labelOne"); assertThat(archivedCaseItem.getSearchIndex2Label()).isEqualTo("labelTwo"); assertThat(archivedCaseItem.getSearchIndex3Label()).isEqualTo("labelThree"); assertThat(archivedCaseItem.getSearchIndex4Label()).isEqualTo("labelFour"); assertThat(archivedCaseItem.getSearchIndex5Label()).isEqualTo("labelFive"); //check values assertThat(archivedCaseItem.getSearchIndex1Value()).isEqualTo("valueOne"); assertThat(archivedCaseItem.getSearchIndex2Value()).isEqualTo("valueTwo"); assertThat(archivedCaseItem.getSearchIndex3Value()).isEqualTo("valueThree"); assertThat(archivedCaseItem.getSearchIndex4Value()).isEqualTo("valueFour"); assertThat(archivedCaseItem.getSearchIndex5Value()).isEqualTo("valueFive"); } @Test public void testConvertEngineToConsoleItemWithNullValues() { //given ArchivedProcessInstance archivedProcessInstance = mock(ArchivedProcessInstance.class); doReturn(null).when(archivedProcessInstance).getStringIndexLabel(1); doReturn(null).when(archivedProcessInstance).getStringIndexValue(1); // when final ArchivedCaseItem archivedCaseItem = datastore.convertEngineToConsoleItem(archivedProcessInstance); // then //check labels assertThat(archivedCaseItem.getSearchIndex1Label()).isNull(); assertThat(archivedCaseItem.getSearchIndex1Value()).isNull(); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDocumentDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.console.common.server.preferences.properties.ConfigurationFilesManager; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.document.ArchivedDocument; import org.bonitasoft.engine.bpm.document.ArchivedDocumentNotFoundException; import org.bonitasoft.engine.bpm.document.DocumentException; import org.bonitasoft.engine.bpm.document.DocumentNotFoundException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentItem; import org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ArchivedCaseDocumentDatastoreTest extends APITestWithMock { private ArchivedCaseDocumentDatastore documentDatastore; @Mock private WebBonitaConstantsUtils constantsValue; @Mock private APISession engineSession; @Mock private ProcessAPI processAPI; @Mock private ArchivedDocument mockedDocument; @Mock private SearchResult mockedEngineSearchResults; @Rule public ExpectedException expectedEx = ExpectedException.none(); @Before public void setUp() throws Exception { ConfigurationFilesManager.getInstance() .setTenantConfigurationFiles(Collections.singletonMap("console-config.properties", "form.attachment.max.size=30".getBytes())); initMocks(this); when(mockedDocument.getName()).thenReturn("Doc 1"); when(mockedDocument.getId()).thenReturn(1L); documentDatastore = spy(new ArchivedCaseDocumentDatastore(engineSession, processAPI)); } // ---------- GET METHOD TESTS ------------------------------// @Test public void it_should_call_engine_processAPI_getDocument() throws Exception { // Given final APIID id = APIID.makeAPIID(1l); // When documentDatastore.get(id); // Then verify(processAPI).getArchivedProcessDocument(id.toLong()); } @Test(expected = APIException.class) public void it_should_catch_and_throw_APIException_for_not_find_document() throws ArchivedDocumentNotFoundException { // Given final APIID id = APIID.makeAPIID(1l); when(processAPI.getArchivedProcessDocument(id.toLong())) .thenThrow(new ArchivedDocumentNotFoundException(new Exception())); // When documentDatastore.get(id); } @Test public void it_should_call_convertEngineToConsole_method() { // Given final APIID id = APIID.makeAPIID(1l); // When documentDatastore.get(id); // Then verify(documentDatastore).convertEngineToConsoleItem(any()); } // ---------- CONVERT ITEM TESTS ------------------------------// @Test public void it_should_convert_item_return_item() { // When final ArchivedCaseDocumentItem convertedEngineToConsoleItem = documentDatastore .convertEngineToConsoleItem(mockedDocument); // Then assertTrue(convertedEngineToConsoleItem != null); } @Test public void it_should_not_convert_null_item_return_null() { // When final CaseDocumentItem convertedEngineToConsoleItem = documentDatastore.convertEngineToConsoleItem(null); // Then assertTrue(convertedEngineToConsoleItem == null); } // ---------- SEARCH TESTS -------------------------------------------------// @Test public void it_should_call_buildSearchOptionCreator_method() throws SearchException { // Given when(processAPI.searchArchivedDocuments(any())).thenReturn(mockedEngineSearchResults); final Map filters = new HashMap<>(); filters.put("submittedBy", "1"); // When documentDatastore.searchDocument(0, 10, "hello", filters, "name ASC"); // Then verify(documentDatastore).buildSearchOptionCreator(0, 10, "hello", filters, "name ASC"); } @Test public void it_should_call_processAPI_searchDocuments_method() throws SearchException { // Given when(processAPI.searchArchivedDocuments(any(SearchOptions.class))).thenReturn(mockedEngineSearchResults); final Map filters = new HashMap<>(); filters.put("submittedBy", "1"); // When documentDatastore.searchDocument(0, 10, "hello", filters, "name ASC"); // Then verify(processAPI).searchArchivedDocuments(documentDatastore.searchOptionsCreator.create()); } // -------------DELETE METHOD TESTS ------------------------------------------// @Test public void it_should_delete_one_document() throws DocumentNotFoundException, DocumentException { final List docs = new ArrayList<>(); docs.add(APIID.makeAPIID(mockedDocument.getId())); // When documentDatastore.delete(docs); // Then verify(processAPI).deleteContentOfArchivedDocument(1L); verify(processAPI, times(1)).deleteContentOfArchivedDocument(any(Long.class)); } @Test public void it_should_delete_two_documents() throws DocumentNotFoundException, DocumentException { final List docs = new ArrayList<>(); docs.add(APIID.makeAPIID(mockedDocument.getId())); docs.add(APIID.makeAPIID(mockedDocument.getId())); // When documentDatastore.delete(docs); // Then verify(processAPI, times(2)).deleteContentOfArchivedDocument(1L); } @Test public void it_should_throw_an_exception_when_input_is_null() throws DocumentNotFoundException, DeletionException { expectedEx.expect(APIException.class); expectedEx.expectMessage("Error while deleting a document. Document id not specified in the request"); // When documentDatastore.delete(null); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/ArchivedCaseDocumentItemConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Date; import org.bonitasoft.engine.bpm.document.ArchivedDocument; import org.bonitasoft.web.rest.model.bpm.cases.ArchivedCaseDocumentItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ArchivedCaseDocumentItemConverterTest extends APITestWithMock { @Test public void should_convert_engine_document_into_portal_document() { // Given final ArchivedCaseDocumentItemConverter documentItemConverter = new ArchivedCaseDocumentItemConverter(); final ArchivedDocument engineItem = mock(ArchivedDocument.class); when(engineItem.getId()).thenReturn(1l); when(engineItem.getProcessInstanceId()).thenReturn(1l); when(engineItem.getName()).thenReturn("Doc 1"); when(engineItem.getAuthor()).thenReturn(1l); when(engineItem.getContentFileName()).thenReturn("doc.jpg"); when(engineItem.getCreationDate()).thenReturn(new Date()); when(engineItem.getContentMimeType()).thenReturn("image"); when(engineItem.hasContent()).thenReturn(true); when(engineItem.getContentStorageId()).thenReturn("1"); when(engineItem.getUrl()).thenReturn("http://url.com?test=d"); when(engineItem.getSourceObjectId()).thenReturn(1l); // When final ArchivedCaseDocumentItem documentItem = documentItemConverter.convert(engineItem); // Assert assertTrue(documentItem.getId().equals(1l)); assertTrue(documentItem.getCaseId().equals(1l)); assertTrue(documentItem.getName().equals("Doc 1")); assertTrue(documentItem.getSubmittedBy().equals(1l)); assertTrue(documentItem.getFileName().equals("doc.jpg")); assertTrue(documentItem.getCreationDate().equals(engineItem.getCreationDate())); assertTrue(documentItem.getMIMEType().equals("image")); assertTrue(documentItem.hasContent()); assertTrue(documentItem.getStorageId().equals("1")); assertTrue(documentItem.getURL().equals("http://url.com?test=d")); assertTrue(documentItem.getSourceObjectId().equals(1l)); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.engine.bpm.process.ProcessInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.process.ProcessInstanceState; import org.bonitasoft.engine.bpm.process.impl.internal.ProcessInstanceImpl; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.search.impl.SearchFilter; import org.bonitasoft.engine.search.impl.SearchResultImpl; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.cases.CaseItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class CaseDatastoreTest { private CaseDatastore caseDatastore; @Mock private ProcessAPI processAPI; @Before public void setUp() throws Exception { caseDatastore = spy(new CaseDatastore(mock(APISession.class))); doReturn(processAPI).when(caseDatastore).getProcessAPI(); } /** * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String, * String, Map). */ @Test public final void search_should_search_process_instances_and_convert_them_to_CaseItem() throws SearchException { // Given final int page = 0; final int resultsByPage = 1; final String search = "plop"; final String orders = CaseItem.ATTRIBUTE_ID; final Map filters = Collections.emptyMap(); final ProcessInstance processInstance = new ProcessInstanceImpl("name"); doReturn(new SearchResultImpl<>(1L, Arrays.asList(processInstance))).when(processAPI).searchProcessInstances( any(SearchOptions.class)); final CaseItem caseItem = new CaseItem(); doReturn(caseItem).when(caseDatastore).convertEngineToConsoleItem(processInstance); // When final ItemSearchResult result = caseDatastore.search(page, resultsByPage, search, orders, filters); // Then verify(processAPI).searchProcessInstances(any(SearchOptions.class)); verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters), any(SearchOptionsBuilder.class)); final List caseItems = result.getResults(); assertEquals(1, caseItems.size()); assertEquals(caseItem, caseItems.get(0)); } /** * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String, * String, Map). */ @Test public final void search_should_throw_an_exception_when_search_process_instances_failed() throws SearchException { // Given final int page = 0; final int resultsByPage = 1; final String search = "plop"; final String orders = CaseItem.ATTRIBUTE_END_DATE; final Map filters = Collections.singletonMap(CaseItem.FILTER_STATE, "started"); doThrow(new SearchException(new Exception("toto"))).when(processAPI) .searchProcessInstances(any(SearchOptions.class)); try { // When caseDatastore.search(page, resultsByPage, search, orders, filters); } catch (final APIException e) { // Then assertTrue(e.getCause() instanceof SearchException); } finally { // Then verify(processAPI).searchProcessInstances(any(SearchOptions.class)); verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters), any(SearchOptionsBuilder.class)); } } /** * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String, * String, Map). */ @Test public final void search_should_search_failed_process_instances_and_convert_them_to_CaseItem_when_filter_on_failed_state() throws SearchException { search_should_search_failed_process_instances_and_convert_them_to_CaseItem_when_filter_on_state("failed"); } /** * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String, * String, Map). */ @Test public final void search_should_throw_an_exception_when_search_failed_process_instances_failed_and_filter_on_failed_state() throws SearchException { search_should_throw_an_exception_when_search_failed_process_instances_failed_and_filter_on_state("failed"); } /** * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String, * String, Map). */ @Test public final void search_should_search_failed_process_instances_and_convert_them_to_CaseItem_when_filter_on_error_state() throws SearchException { search_should_search_failed_process_instances_and_convert_them_to_CaseItem_when_filter_on_state("error"); } /** * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String, * String, Map). */ @Test public final void search_should_search_failed_process_instances_and_convert_them_to_CaseItem_when_filter_on_error_of_ProcessInstanceState_state() throws SearchException { search_should_search_failed_process_instances_and_convert_them_to_CaseItem_when_filter_on_state( ProcessInstanceState.ERROR.name()); } /** * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String, * String, Map). */ @Test public final void search_should_throw_an_exception_when_search_failed_process_instances_failed_and_filter_on_error_state() throws SearchException { search_should_throw_an_exception_when_search_failed_process_instances_failed_and_filter_on_state("error"); } private void search_should_search_failed_process_instances_and_convert_them_to_CaseItem_when_filter_on_state( final String state) throws SearchException { // Given final int page = 0; final int resultsByPage = 1; final String search = "plop"; final String orders = CaseItem.ATTRIBUTE_ID; final Map filters = Collections.singletonMap(CaseItem.FILTER_STATE, state); final ProcessInstance processInstance = new ProcessInstanceImpl("name"); doReturn(new SearchResultImpl<>(1L, Arrays.asList(processInstance))).when(processAPI) .searchFailedProcessInstances( any(SearchOptions.class)); final CaseItem caseItem = new CaseItem(); doReturn(caseItem).when(caseDatastore).convertEngineToConsoleItem(processInstance); // When final ItemSearchResult result = caseDatastore.search(page, resultsByPage, search, orders, filters); // Then verify(processAPI).searchFailedProcessInstances(any(SearchOptions.class)); verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters), any(SearchOptionsBuilder.class)); final List caseItems = result.getResults(); assertEquals(1, caseItems.size()); assertEquals(caseItem, caseItems.get(0)); } private void search_should_throw_an_exception_when_search_failed_process_instances_failed_and_filter_on_state( final String state) throws SearchException { // Given final int page = 0; final int resultsByPage = 1; final String search = "plop"; final String orders = CaseItem.ATTRIBUTE_END_DATE; final Map filters = Collections.singletonMap(CaseItem.FILTER_STATE, state); doThrow(new SearchException(new Exception("toto"))).when(processAPI) .searchFailedProcessInstances(any(SearchOptions.class)); try { // When caseDatastore.search(page, resultsByPage, search, orders, filters); } catch (final APIException e) { // Then assertTrue(e.getCause() instanceof SearchException); } finally { // Then verify(processAPI).searchFailedProcessInstances(any(SearchOptions.class)); verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters), any(SearchOptionsBuilder.class)); } } /** * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String, * String, Map). */ @Test public final void search_should_search_open_process_instances_involving_user_and_convert_them_to_CaseItem_when_filter_on_user() throws SearchException { // Given final int page = 0; final int resultsByPage = 1; final String search = "plop"; final String orders = CaseItem.ATTRIBUTE_ID; final Map filters = new HashMap<>(); filters.put(CaseItem.FILTER_USER_ID, "9"); filters.put(CaseItem.ATTRIBUTE_STATE, "plop"); final ProcessInstance processInstance = new ProcessInstanceImpl("name"); doReturn(new SearchResultImpl<>(1L, Arrays.asList(processInstance))).when(processAPI) .searchOpenProcessInstancesInvolvingUser(eq(9L), any(SearchOptions.class)); final CaseItem caseItem = new CaseItem(); doReturn(caseItem).when(caseDatastore).convertEngineToConsoleItem(processInstance); // When final ItemSearchResult result = caseDatastore.search(page, resultsByPage, search, orders, filters); // Then verify(processAPI).searchOpenProcessInstancesInvolvingUser(eq(9L), any(SearchOptions.class)); verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters), any(SearchOptionsBuilder.class)); final List caseItems = result.getResults(); assertEquals(1, caseItems.size()); assertEquals(caseItem, caseItems.get(0)); } /** * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String, * String, Map). */ @Test public final void search_should_throw_an_exception_when_search_open_process_instances_involving_user_filter_on_user() throws SearchException { // Given final int page = 0; final int resultsByPage = 1; final String search = "plop"; final String orders = CaseItem.ATTRIBUTE_END_DATE; final Map filters = new HashMap<>(); filters.put(CaseItem.FILTER_USER_ID, "9"); filters.put(CaseItem.ATTRIBUTE_STATE, "plop"); doThrow(new SearchException(new Exception("toto"))).when(processAPI) .searchOpenProcessInstancesInvolvingUser(eq(9L), any(SearchOptions.class)); try { // When caseDatastore.search(page, resultsByPage, search, orders, filters); } catch (final APIException e) { // Then assertTrue(e.getCause() instanceof SearchException); } finally { // Then verify(processAPI).searchOpenProcessInstancesInvolvingUser(eq(9L), any(SearchOptions.class)); verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters), any(SearchOptionsBuilder.class)); } } /** * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String, * String, Map). */ @Test public final void search_should_search_open_process_instances_supervised_by_and_convert_them_to_CaseItem_when_filter_on_supervisor() throws SearchException { // Given final int page = 0; final int resultsByPage = 1; final String search = "plop"; final String orders = CaseItem.ATTRIBUTE_ID; final Map filters = new HashMap<>(); filters.put(CaseItem.FILTER_SUPERVISOR_ID, "9"); filters.put(CaseItem.ATTRIBUTE_STATE, "plop"); final ProcessInstance processInstance = new ProcessInstanceImpl("name"); doReturn(new SearchResultImpl<>(1L, Arrays.asList(processInstance))).when(processAPI) .searchOpenProcessInstancesSupervisedBy(eq(9L), any(SearchOptions.class)); final CaseItem caseItem = new CaseItem(); doReturn(caseItem).when(caseDatastore).convertEngineToConsoleItem(processInstance); // When final ItemSearchResult result = caseDatastore.search(page, resultsByPage, search, orders, filters); // Then verify(processAPI).searchOpenProcessInstancesSupervisedBy(eq(9L), any(SearchOptions.class)); verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters), any(SearchOptionsBuilder.class)); final List caseItems = result.getResults(); assertEquals(1, caseItems.size()); assertEquals(caseItem, caseItems.get(0)); } /** * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#search(int, int, String, * String, Map). */ @Test public final void search_should_throw_an_exception_when_search_open_process_instances_supervised_by_filter_on_supervisor() throws SearchException { // Given final int page = 0; final int resultsByPage = 1; final String search = "plop"; final String orders = CaseItem.ATTRIBUTE_END_DATE; final Map filters = new HashMap<>(); filters.put(CaseItem.FILTER_SUPERVISOR_ID, "9"); filters.put(CaseItem.ATTRIBUTE_STATE, "plop"); doThrow(new SearchException(new Exception("toto"))).when(processAPI) .searchOpenProcessInstancesSupervisedBy(eq(9L), any(SearchOptions.class)); try { // When caseDatastore.search(page, resultsByPage, search, orders, filters); } catch (final APIException e) { // Then assertTrue(e.getCause() instanceof SearchException); } finally { // Then verify(processAPI).searchOpenProcessInstancesSupervisedBy(eq(9L), any(SearchOptions.class)); verify(caseDatastore).addCallerFilterToSearchBuilderIfNecessary(eq(filters), any(SearchOptionsBuilder.class)); } } /** * Test method for * {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#addCallerFilterToSearchBuilderIfNecessary(Map, * SearchOptionsBuilder). */ @Test public final void addCallerFilterToSearchBuilderIfNecessary_should_add_caller_filter_to_builder_when_no_caller_filter() { // Given final Map filters = Collections.emptyMap(); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); // When caseDatastore.addCallerFilterToSearchBuilderIfNecessary(filters, builder); // Then final SearchFilter searchFilter = builder.done().getFilters().get(0); assertEquals(ProcessInstanceSearchDescriptor.CALLER_ID, searchFilter.getField()); assertEquals(-1, searchFilter.getValue()); } /** * Test method for * {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#addCallerFilterToSearchBuilderIfNecessary(Map, * SearchOptionsBuilder). */ @Test public final void addCallerFilterToSearchBuilderIfNecessary_should_add_caller_filter_to_builder_when_filter_on_caller_with_value_different_of_any() { // Given final Map filters = Collections.singletonMap(CaseItem.FILTER_CALLER, "9"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); // When caseDatastore.addCallerFilterToSearchBuilderIfNecessary(filters, builder); // Then final SearchFilter searchFilter = builder.done().getFilters().get(0); assertEquals(ProcessInstanceSearchDescriptor.CALLER_ID, searchFilter.getField()); assertEquals(9L, searchFilter.getValue()); } /** * Test method for * {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#addCallerFilterToSearchBuilderIfNecessary(Map, * SearchOptionsBuilder). */ @Test public final void addCallerFilterToSearchBuilderIfNecessary_should_do_nothing_when_filter_on_any_caller() { // Given final Map filters = Collections.singletonMap(CaseItem.FILTER_CALLER, "any"); final SearchOptionsBuilder builder = new SearchOptionsBuilder(0, 10); // When caseDatastore.addCallerFilterToSearchBuilderIfNecessary(filters, builder); // Then assertTrue(builder.done().getFilters().isEmpty()); } /** * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#count(String, String, * Map). */ @Test public final void count_should_return_total_of_search() { // Given final String search = "plop"; final String orders = CaseItem.ATTRIBUTE_ID; final Map filters = Collections.emptyMap(); final long total = 7L; final ItemSearchResult itemSearchResult = new ItemSearchResult<>(0, 0, total, null); doReturn(itemSearchResult).when(caseDatastore).search(0, 0, search, orders, filters); // When final long result = caseDatastore.count(search, orders, filters); // Then assertEquals(total, result); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void searchProcessInstances_With_PM_Filter_And_Failed_State_should_call_API() throws Exception { // Given final Map filters = new HashMap<>(); final SearchOptions searchOptions = mock(SearchOptions.class); final long userId = 9L; filters.put(CaseItem.FILTER_SUPERVISOR_ID, String.valueOf(userId)); filters.put(CaseItem.ATTRIBUTE_STATE, "failed"); final SearchOptionsBuilder searchOptionsBuilder = mock(SearchOptionsBuilder.class); final SearchResult searchResult = mock(SearchResult.class); when(searchOptionsBuilder.done()).thenReturn(searchOptions); doReturn(searchOptionsBuilder).when(caseDatastore).buildSearchOptions(0, 1, "", "", filters); final ItemSearchResult itemSearchResult = mock(ItemSearchResult.class); doReturn(itemSearchResult).when(caseDatastore).convertEngineToConsoleSearch(0, 1, searchResult); when(processAPI.searchFailedProcessInstancesSupervisedBy(userId, searchOptions)).thenReturn(searchResult); // when final ItemSearchResult caseSearchResult = caseDatastore.search(0, 1, "", "", filters); // Then verify(processAPI).searchFailedProcessInstancesSupervisedBy(userId, searchOptions); assertThat(itemSearchResult).isSameAs(caseSearchResult); } /** * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#update}. */ @Test public void update_should_cancel_instance_and_return_null_when_state_is_cancelled() throws Exception { // Given final APIID id = APIID.makeAPIID(42L); final Map attributes = new HashMap<>(); attributes.put(CaseItem.ATTRIBUTE_STATE, "CANCELLED"); // When final CaseItem result = caseDatastore.update(id, attributes); // Then verify(processAPI).cancelProcessInstance(42L); assertThat(result).isNull(); } /** * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#update}. */ @Test(expected = APIException.class) public void update_should_throw_APIException_when_state_is_missing() { // Given final APIID id = APIID.makeAPIID(43L); final Map attributes = new HashMap<>(); // no state // When caseDatastore.update(id, attributes); // Then (exception expected) } /** * Test method for {@link org.bonitasoft.web.rest.server.datastore.bpm.cases.CaseDatastore#update}. */ @Test(expected = APIException.class) public void update_should_throw_APIException_when_state_is_not_cancelled() { // Given final APIID id = APIID.makeAPIID(44L); final Map attributes = new HashMap<>(); attributes.put(CaseItem.ATTRIBUTE_STATE, "STARTED"); // anything != CANCELLED // When caseDatastore.update(id, attributes); // Then (exception expected) } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDocumentDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.document.DocumentNotFoundException; import org.bonitasoft.engine.bpm.document.DocumentValue; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.io.FileContent; import org.bonitasoft.engine.io.TemporaryFileNotFoundException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class CaseDocumentDatastoreTest extends APITestWithMock { private CaseDocumentDatastore documentDatastore; @Mock private APISession engineSession; @Mock private ProcessAPI processAPI; @Mock private Document mockedDocument; @Mock private BonitaHomeFolderAccessor tenantFolder; @Mock private SearchResult mockedEngineSearchResults; @Rule public ExpectedException expectedEx = ExpectedException.none(); private final CaseDocumentItem mockedDocumentItem = new CaseDocumentItem(); @Before public void setUp() throws Exception { initMocks(this); when(mockedDocument.getName()).thenReturn("Doc 1"); when(mockedDocument.getId()).thenReturn(1L); documentDatastore = spy(new CaseDocumentDatastore(engineSession, processAPI, tenantFolder)); } // ---------- GET METHOD TESTS ------------------------------// @Test public void it_should_call_engine_processAPI_getDocument() throws Exception { // Given final APIID id = APIID.makeAPIID(1L); // When documentDatastore.get(id); // Then verify(processAPI).getDocument(id.toLong()); } @Test(expected = APIException.class) public void it_should_catch_and_throw_APIException_for_not_find_document() throws Exception { // Given final APIID id = APIID.makeAPIID(1L); when(processAPI.getDocument(id.toLong())) .thenThrow(new DocumentNotFoundException("not found", new Exception())); // When documentDatastore.get(id); } @Test public void it_should_call_convertEngineToConsole_method() { // Given final APIID id = APIID.makeAPIID(1L); // When documentDatastore.get(id); // Then verify(documentDatastore).convertEngineToConsoleItem(any()); } // ---------- CONVERT ITEM TESTS ------------------------------// @Test public void it_should_convert_item_return_item() { // When final CaseDocumentItem convertedEngineToConsoleItem = documentDatastore .convertEngineToConsoleItem(mockedDocument); // Then assertTrue(convertedEngineToConsoleItem != null); } @Test public void it_should_not_convert_null_item_return_null() { // When final CaseDocumentItem convertedEngineToConsoleItem = documentDatastore.convertEngineToConsoleItem(null); // Then assertTrue(convertedEngineToConsoleItem == null); } // ---------- buildDocumentValueFromUploadPath TESTS ------------------------------// @Test public void it_should_create_documentvalue_with_given_filename() throws Exception { String uploadKey = "3544697"; when(tenantFolder.retrieveUploadedTempContent(uploadKey)) .thenReturn(new FileContent("doc.jpg", InputStream.nullInputStream(), "img/jpg")); // When final DocumentValue documentValue = documentDatastore.buildDocumentValueFromUploadPath(uploadKey, 1, "fileName"); // Then assertTrue(documentValue.getFileName().equals("fileName")); } @Test public void it_should_create_documentvalue_with_name_of_the_uploaded_file() throws Exception { String uploadKey = "46645"; when(tenantFolder.retrieveUploadedTempContent(uploadKey)) .thenReturn(new FileContent("doc.jpg", InputStream.nullInputStream(), "img/jpg")); // When final DocumentValue documentValue = documentDatastore.buildDocumentValueFromUploadPath(uploadKey, 1, ""); // Then assertTrue(documentValue.getFileName().equals("doc.jpg")); } // ---------- ADD METHOD TESTS ------------------------------// @Test public void it_should_add_a_document_calling_addDocument_with_upload_Path() throws Exception { // Given String uploadKey = "1456"; mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_CASE_ID, 1L); mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_NAME, "doc 1"); mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, uploadKey); when(tenantFolder.retrieveUploadedTempContent(uploadKey)) .thenReturn(new FileContent("doc.jpg", InputStream.nullInputStream(), "img/jpg")); // When documentDatastore.add(mockedDocumentItem); // Then verify(documentDatastore).buildDocumentValueFromUploadPath(uploadKey, -1, null); verify(processAPI).addDocument(eq(1L), eq("doc 1"), eq(""), any(DocumentValue.class)); } @Test public void it_should_add_a_document_calling_addDocument_with_upload_Path_and_fileName() throws Exception { // Given String uploadKey = "58768"; mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_CASE_ID, 1L); mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_NAME, "doc 1"); mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_CONTENT_FILENAME, "doc_file_name.jpg"); mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, uploadKey); when(tenantFolder.retrieveUploadedTempContent(uploadKey)) .thenReturn(new FileContent("doc.jpg", InputStream.nullInputStream(), "img/jpg")); // When documentDatastore.add(mockedDocumentItem); // Then verify(documentDatastore).buildDocumentValueFromUploadPath(uploadKey, -1, "doc_file_name.jpg"); verify(processAPI).addDocument(eq(1L), eq("doc 1"), eq(""), any(DocumentValue.class)); } @Test public void it_should_add_a_document_calling_addDocument_with_upload_Path_with_index_and_description() throws Exception { // Given String uploadKey = "546374"; mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_CASE_ID, 1L); mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_NAME, "doc 1"); mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, uploadKey); mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_DESCRIPTION, "This is a description"); mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_INDEX, "2"); when(tenantFolder.retrieveUploadedTempContent(uploadKey)) .thenReturn(new FileContent("doc.jpg", InputStream.nullInputStream(), "img/jpg")); // When documentDatastore.add(mockedDocumentItem); // Then verify(documentDatastore).buildDocumentValueFromUploadPath(uploadKey, 2, null); verify(processAPI).addDocument(eq(1L), eq("doc 1"), eq("This is a description"), any(DocumentValue.class)); } @Test(expected = APIException.class) public void it_should_not_add_a_document_calling_addDocument_with_invalid_upload_Path() throws Exception { // Given mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_CASE_ID, 1L); mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_NAME, "doc 1"); mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, "unexisting.document"); when(tenantFolder.retrieveUploadedTempContent("unexisting.document")) .thenThrow(TemporaryFileNotFoundException.class); try { // When documentDatastore.add(mockedDocumentItem); } finally { // Then verify(documentDatastore).buildDocumentValueFromUploadPath("unexisting.document", -1, null); verify(processAPI, times(0)).addDocument(eq(1L), eq("doc 1"), eq(""), any(DocumentValue.class)); } } @Test public void it_should_add_a_document_calling_addDocument_with_external_Url() throws Exception { // Given mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_CASE_ID, 1L); mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_NAME, "doc 1"); mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_URL, "http://images/doc.jpg"); // When documentDatastore.add(mockedDocumentItem); // Then verify(documentDatastore).buildDocumentValueFromUrl("http://images/doc.jpg", -1); verify(processAPI).addDocument(eq(1L), eq("doc 1"), eq(""), any(DocumentValue.class)); } @Test(expected = APIException.class) public void it_throws_an_exception_adding_a_document_with_invalid_inputs() { // Given mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_CASE_ID, -1); mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_NAME, ""); // byte[] fileContent = DocumentUtil.getArrayByteFromFile(new File(docUrl)); // When documentDatastore.add(mockedDocumentItem); } @Test(expected = APIException.class) public void it_throws_an_exception_adding_a_document_with_missing_inputs() { // Given mockedDocumentItem.setAttribute(CaseDocumentItem.ATTRIBUTE_NAME, ""); // byte[] fileContent = DocumentUtil.getArrayByteFromFile(new File(docUrl)); // When documentDatastore.add(mockedDocumentItem); } // ---------- UPDATE METHOD TESTS ------------------------------// @Test public void it_should_update_a_document_calling_updateDocument_with_upload_Path() throws Exception { // Given String uploadKey = "5464187"; final Map attributes = new HashMap<>(); attributes.put(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, uploadKey); when(tenantFolder.retrieveUploadedTempContent(uploadKey)) .thenReturn(new FileContent("doc.jpg", InputStream.nullInputStream(), "img/jpg")); // When documentDatastore.update(APIID.makeAPIID(1L), attributes); // Then verify(documentDatastore).buildDocumentValueFromUploadPath(uploadKey, -1, null); verify(processAPI).updateDocument(eq(1L), any(DocumentValue.class)); } @Test public void it_should_update_a_document_calling_updateDocument_with_upload_Path_and_fileName() throws Exception { // Given String uploadKey = "357898"; final Map attributes = new HashMap<>(); attributes.put(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, uploadKey); attributes.put(CaseDocumentItem.ATTRIBUTE_CONTENT_FILENAME, "doc_file_name.jpg"); when(tenantFolder.retrieveUploadedTempContent(uploadKey)) .thenReturn(new FileContent("doc.jpg", InputStream.nullInputStream(), "img/jpg")); // When documentDatastore.update(APIID.makeAPIID(1L), attributes); // Then verify(documentDatastore).buildDocumentValueFromUploadPath(uploadKey, -1, "doc_file_name.jpg"); verify(processAPI).updateDocument(eq(1L), any(DocumentValue.class)); } @Test public void it_should_update_a_document_calling_updateDocument_with_external_Url() throws Exception { // Given final Map attributes = new HashMap<>(); attributes.put(CaseDocumentItem.ATTRIBUTE_URL, "http://images/doc.jpg"); // When documentDatastore.update(APIID.makeAPIID(1L), attributes); // Then verify(documentDatastore).buildDocumentValueFromUrl("http://images/doc.jpg", -1); verify(processAPI).updateDocument(eq(1L), any(DocumentValue.class)); } @Test(expected = APIException.class) public void it_should_not_update_document_and_throws_exception_for_missing_uploadPath() { // Given final Map attributes = new HashMap<>(); attributes.put(CaseDocumentItem.ATTRIBUTE_NAME, "Doc 1"); final APIID id = APIID.makeAPIID(1L); // When documentDatastore.update(id, attributes); } @Test(expected = APIException.class) public void it_should_not_update_document_and_throws_exception_for_invalid_uploadPath() throws Exception { // Given final Map attributes = new HashMap<>(); attributes.put(CaseDocumentItem.ATTRIBUTE_NAME, "Doc 1"); attributes.put(CaseDocumentItem.ATTRIBUTE_UPLOAD_PATH, "unexisting.document"); final APIID id = APIID.makeAPIID(1L); doThrow(FileNotFoundException.class).when(documentDatastore) .buildDocumentValueFromUploadPath("unexisting.document", -1, null); try { // When documentDatastore.update(id, attributes); } finally { // Then verify(documentDatastore).buildDocumentValueFromUploadPath("unexisting.document", -1, null); verify(processAPI, times(0)).updateDocument(eq(1L), any(DocumentValue.class)); } } // ---------- SEARCH TESTS -------------------------------------------------// @Test public void it_should_call_buildSearchOptionCreator_method() throws SearchException { // Given when(processAPI.searchDocuments(any(SearchOptions.class))).thenReturn(mockedEngineSearchResults); final Map filters = new HashMap<>(); filters.put("submittedBy", "1"); // When documentDatastore.searchDocument(0, 10, "hello", filters, "name ASC"); // Then verify(documentDatastore).buildSearchOptionCreator(0, 10, "hello", filters, "name ASC"); } @Test public void it_should_call_processAPI_searchDocuments_method() throws SearchException { // Given when(processAPI.searchDocuments(any(SearchOptions.class))).thenReturn(mockedEngineSearchResults); final Map filters = new HashMap<>(); filters.put("submittedBy", "1"); // When documentDatastore.searchDocument(0, 10, "hello", filters, "name ASC"); // Then verify(processAPI).searchDocuments(documentDatastore.searchOptionsCreator.create()); } // -------------DELETE METHOD TESTS ------------------------------------------// @Test public void it_should_delete_one_document() throws DocumentNotFoundException, DeletionException { final List docs = new ArrayList<>(); docs.add(APIID.makeAPIID(mockedDocument.getId())); // When documentDatastore.delete(docs); // Then verify(processAPI).removeDocument(1L); verify(processAPI, times(1)).removeDocument(any(Long.class)); } @Test public void it_should_delete_two_documents() throws DocumentNotFoundException, DeletionException { final List docs = new ArrayList<>(); docs.add(APIID.makeAPIID(mockedDocument.getId())); docs.add(APIID.makeAPIID(mockedDocument.getId())); // When documentDatastore.delete(docs); // Then verify(processAPI, times(2)).removeDocument(1L); } @Test public void it_should_throw_an_exception_when_input_is_null() { expectedEx.expect(APIException.class); expectedEx.expectMessage("Error while deleting a document. Document id not specified in the request"); // When documentDatastore.delete(null); } @Test public void it_should_throw_an_exception_when_document_is_not_found() throws DocumentNotFoundException, DeletionException { expectedEx.expect(APIException.class); expectedEx.expectMessage("Error while deleting a document. Document not found"); // When when(processAPI.removeDocument(3L)).thenThrow(DocumentNotFoundException.class); final List docs = new ArrayList<>(); docs.add(APIID.makeAPIID(3L)); // When documentDatastore.delete(docs); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseDocumentItemConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Date; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.web.rest.model.bpm.cases.CaseDocumentItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class CaseDocumentItemConverterTest extends APITestWithMock { @Test public void should_convert_engine_document_into_portal_document() { // Given final CaseDocumentItemConverter documentItemConverter = new CaseDocumentItemConverter(); final Document engineItem = mock(Document.class); when(engineItem.getId()).thenReturn(1l); when(engineItem.getProcessInstanceId()).thenReturn(1l); when(engineItem.getName()).thenReturn("Doc 1"); when(engineItem.getAuthor()).thenReturn(1l); when(engineItem.getContentFileName()).thenReturn("doc.jpg"); when(engineItem.getCreationDate()).thenReturn(new Date()); when(engineItem.getContentMimeType()).thenReturn("image"); when(engineItem.hasContent()).thenReturn(true); when(engineItem.getContentStorageId()).thenReturn("1"); when(engineItem.getUrl()).thenReturn("http://url.com?test=d"); // When final CaseDocumentItem documentItem = documentItemConverter.convert(engineItem); // Assert assertTrue(documentItem.getId().equals(1l)); assertTrue(documentItem.getCaseId().equals(1l)); assertTrue(documentItem.getName().equals("Doc 1")); assertTrue(documentItem.getSubmittedBy().equals(1l)); assertTrue(documentItem.getFileName().equals("doc.jpg")); assertTrue(documentItem.getCreationDate().equals(engineItem.getCreationDate())); assertTrue(documentItem.getMIMEType().equals("image")); assertTrue(documentItem.hasContent()); assertTrue(documentItem.getStorageId().equals("1")); assertTrue(documentItem.getURL().equals("http://url.com?test=d")); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseItemConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import org.bonitasoft.console.common.server.i18n.I18n; import org.bonitasoft.engine.bpm.process.ProcessInstance; import org.bonitasoft.web.rest.model.bpm.cases.CaseItem; import org.bonitasoft.web.toolkit.client.common.CommonDateFormater; import org.bonitasoft.web.toolkit.server.utils.ServerDateFormater; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class CaseItemConverterTest { @Mock private ProcessInstance processInstance; private final CaseItemConverter caseItemConverter = new CaseItemConverter(); @BeforeClass public static void beforeClass() { I18n.getInstance(); CommonDateFormater.setDateFormater(new ServerDateFormater()); } @Test public void testConvertShouldReturnAEngineCaseConvertedIntoAConsoleCase() throws Exception { //given doReturn("labelOne").when(processInstance).getStringIndexLabel(1); doReturn("labelTwo").when(processInstance).getStringIndexLabel(2); doReturn("labelThree").when(processInstance).getStringIndexLabel(3); doReturn("labelFour").when(processInstance).getStringIndexLabel(4); doReturn("labelFive").when(processInstance).getStringIndexLabel(5); doReturn("valueOne").when(processInstance).getStringIndex1(); doReturn("valueTwo").when(processInstance).getStringIndex2(); doReturn("valueThree").when(processInstance).getStringIndex3(); doReturn("valueFour").when(processInstance).getStringIndex4(); doReturn("valueFive").when(processInstance).getStringIndex5(); // when final CaseItem caseItem = caseItemConverter.convert(processInstance); // then //check labels assertThat(caseItem.getSearchIndex1Label()).isEqualTo("labelOne"); assertThat(caseItem.getSearchIndex2Label()).isEqualTo("labelTwo"); assertThat(caseItem.getSearchIndex3Label()).isEqualTo("labelThree"); assertThat(caseItem.getSearchIndex4Label()).isEqualTo("labelFour"); assertThat(caseItem.getSearchIndex5Label()).isEqualTo("labelFive"); //check values assertThat(caseItem.getSearchIndex1Value()).isEqualTo("valueOne"); assertThat(caseItem.getSearchIndex2Value()).isEqualTo("valueTwo"); assertThat(caseItem.getSearchIndex3Value()).isEqualTo("valueThree"); assertThat(caseItem.getSearchIndex4Value()).isEqualTo("valueFour"); assertThat(caseItem.getSearchIndex5Value()).isEqualTo("valueFive"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/cases/CaseVariableDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.cases; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.web.rest.server.APITestWithMock; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; /** * @author Colin PUY */ public class CaseVariableDatastoreTest extends APITestWithMock { private CaseVariableDatastore datastore; @Mock private ProcessAPI processAPI; @Before public void initializeMocks() { initMocks(this); datastore = spy(new CaseVariableDatastore(null)); doReturn(processAPI).when(datastore).getEngineProcessAPI(); } @Test public void testUpdateVariableValue() throws Exception { long caseId = 1L; String name = "aName"; String newValue = "newValue"; datastore.updateVariableValue(caseId, name, String.class.getName(), newValue); verify(processAPI).updateProcessDataInstance(name, caseId, newValue); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ConnectorInstanceDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.connector; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import junit.framework.Assert; import org.bonitasoft.console.common.server.i18n.I18n; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.connector.ConnectorInstance; import org.bonitasoft.engine.bpm.connector.ConnectorState; import org.bonitasoft.engine.bpm.connector.impl.ConnectorInstanceImpl; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.search.impl.SearchResultImpl; import org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem; import org.bonitasoft.web.rest.server.BonitaRestAPIServlet; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.mockito.Spy; /** * @author Vincent Elcrin */ public class ConnectorInstanceDatastoreTest { @Spy private final ConnectorInstanceDatastore spiedDatastore = new ConnectorInstanceDatastore(null); @Mock private ProcessAPI mockedProcessAPI; @BeforeClass public static void initEnvironnement() { I18n.getInstance(); new BonitaRestAPIServlet(); } @Before public void init() { MockitoAnnotations.initMocks(this); Mockito.doReturn(this.mockedProcessAPI).when(this.spiedDatastore).getProcessAPI(); } // ////////////////////////////////////////////////////////////////////////// // / Test search // ////////////////////////////////////////////////////////////////////////// /** * Test right parameters go through engine API call method * * @throws Exception */ @Test public void searchBuildRightParameters() throws Exception { final Map filters = new HashMap<>(); filters.put(ConnectorInstanceItem.ATTRIBUTE_CONTAINER_ID, "1"); filters.put(ConnectorInstanceItem.ATTRIBUTE_STATE, "2"); final SearchOptions searchOptions = this.spiedDatastore.buildSearchOptions(0, 123, "searchTerm", "order " + Order.ASC, filters); Assert.assertEquals(0, searchOptions.getStartIndex()); Assert.assertEquals(123, searchOptions.getMaxResults()); Assert.assertEquals("searchTerm", searchOptions.getSearchTerm()); Assert.assertEquals("order", searchOptions.getSorts().get(0).getField()); Assert.assertEquals(Order.ASC, searchOptions.getSorts().get(0).getOrder()); Assert.assertEquals(filters.size(), searchOptions.getFilters().size()); } /** * Test search result conversion * * @throws Exception */ @Test public void searchReturnAllItems() throws Exception { final ConnectorInstance connectorInstance1 = createConnectorInstanceImpl(1L, "instance 1"); final ConnectorInstance connectorInstance2 = createConnectorInstanceImpl(1L, "instance 2"); final SearchResult expected = new SearchResultImpl<>(2, Arrays.asList(connectorInstance1, connectorInstance2)); Mockito.when(this.mockedProcessAPI.searchConnectorInstances(Mockito.any(SearchOptions.class))) .thenReturn(expected); final ItemSearchResult searchResult = this.spiedDatastore.search(0, 10, null, null, new HashMap<>()); Mockito.verify(this.mockedProcessAPI).searchConnectorInstances(Mockito.any(SearchOptions.class)); Assert.assertTrue(areEquals(new ConnectorInstanceItemWrapper(expected.getResult().get(0)), searchResult.getResults().get(0))); Assert.assertTrue(areEquals(new ConnectorInstanceItemWrapper(expected.getResult().get(1)), searchResult.getResults().get(1))); } // ////////////////////////////////////////////////////////////////////////// // / Convenient tools // ////////////////////////////////////////////////////////////////////////// private boolean areEquals(final Item expected, final Item actual) { return expected.getAttributes().equals(actual.getAttributes()); } private ConnectorInstance createConnectorInstanceImpl(final long id, final String name) { final ConnectorInstanceImpl connectorInstance = new ConnectorInstanceImpl(name, 2L, "containerType", String.valueOf(id), "version", ConnectorState.DONE, ConnectorEvent.ON_ENTER); connectorInstance.setId(1L); return connectorInstance; } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/connector/ConnectorInstanceItemWrapperTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.connector; import junit.framework.Assert; import org.bonitasoft.engine.bpm.connector.ConnectorEvent; import org.bonitasoft.engine.bpm.connector.ConnectorInstance; import org.bonitasoft.engine.bpm.connector.ConnectorState; import org.bonitasoft.engine.bpm.connector.impl.ConnectorInstanceImpl; import org.bonitasoft.web.rest.model.bpm.connector.ConnectorInstanceItem; import org.bonitasoft.web.rest.server.BonitaRestAPIServlet; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; /** * @author Colin PUY */ public class ConnectorInstanceItemWrapperTest { private static BonitaRestAPIServlet consoleAPIServlet; @BeforeClass public static void initContextForDefinition() { consoleAPIServlet = new BonitaRestAPIServlet(); } @AfterClass public static void destroyContext() { consoleAPIServlet.destroy(); } @Test(expected = IllegalArgumentException.class) public void cantWrapANullItem() throws Exception { new ConnectorInstanceItemWrapper(null); } @Test public void convertItemReturnRightItem() { final ConnectorInstance expected = createConnectorInstanceImpl(1L, "instance1"); final ConnectorInstanceItem actual = new ConnectorInstanceItemWrapper(expected); Assert.assertTrue(areEquals(expected, actual)); } private boolean areEquals(final ConnectorInstance expected, final ConnectorInstanceItem actual) { return APIID.makeAPIID(expected.getConnectorId()).equals(actual.getConnectorId()) && APIID.makeAPIID(expected.getContainerId()).equals(actual.getContainerId()) && expected.getContainerType().equals(actual.getContainerType()) && APIID.makeAPIID(expected.getId()).equals(actual.getId()) && expected.getName().equals(actual.getName()) && expected.getState().equals(new ConnectorInstanceStateConverter().convert(actual.getState())) && expected.getVersion().equals(actual.getVersion()); } private ConnectorInstance createConnectorInstanceImpl(final long id, final String name) { final ConnectorInstanceImpl connectorInstance = new ConnectorInstanceImpl(name, 2L, "containerType", String.valueOf(id), "version", ConnectorState.DONE, ConnectorEvent.ON_ENTER); connectorInstance.setId(1L); return connectorInstance; } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/AbstractFlowNodeDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstanceSearchDescriptor; import org.bonitasoft.engine.search.SearchFilterOperation; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class AbstractFlowNodeDatastoreTest { AbstractFlowNodeDatastore datastore; @Before public void setUp() throws Exception { final APISession engineSession = mock(APISession.class); datastore = new AbstractFlowNodeDatastore<>(engineSession); } @Test public void makeSearchOptionBuilder_state_Filter_Pending_Adds_multiple_entries_in_SearchOption() throws Exception { final Map filters = new HashMap<>(); filters.put(FlowNodeInstanceSearchDescriptor.STATE_NAME, "pending"); final SearchOptionsBuilder makeSearchOptionBuilder = datastore.makeSearchOptionBuilder(0, 10, "", "", filters); assertThat(makeSearchOptionBuilder.done().getFilters()).extracting("field", "operation", "value").contains( tuple(null, SearchFilterOperation.L_PARENTHESIS, null), tuple("state", SearchFilterOperation.EQUALS, "ready"), tuple(null, SearchFilterOperation.OR, null), tuple("state", SearchFilterOperation.EQUALS, "waiting"), tuple(null, SearchFilterOperation.R_PARENTHESIS, null), tuple("state", SearchFilterOperation.DIFFERENT, "aborted"), tuple("state", SearchFilterOperation.DIFFERENT, "cancelled"), tuple("state", SearchFilterOperation.DIFFERENT, "completed")); } @Test public void makeSearchOptionBuilder_state_Filter_Ongoing_Adds_multiple_entries_in_SearchOption() throws Exception { final Map filters = new HashMap<>(); filters.put(FlowNodeInstanceSearchDescriptor.STATE_NAME, "ongoing"); final SearchOptionsBuilder makeSearchOptionBuilder = datastore.makeSearchOptionBuilder(0, 10, "", "", filters); assertThat(makeSearchOptionBuilder.done().getFilters()).extracting("field", "operation", "value").contains( tuple(null, SearchFilterOperation.L_PARENTHESIS, null), tuple("state", SearchFilterOperation.EQUALS, "executing"), tuple(null, SearchFilterOperation.OR, null), tuple("state", SearchFilterOperation.EQUALS, "completing"), tuple(null, SearchFilterOperation.OR, null), tuple("state", SearchFilterOperation.EQUALS, "initializing"), tuple(null, SearchFilterOperation.R_PARENTHESIS, null), tuple("state", SearchFilterOperation.DIFFERENT, "aborted"), tuple("state", SearchFilterOperation.DIFFERENT, "cancelled"), tuple("state", SearchFilterOperation.DIFFERENT, "completed")); } @Test public void makeSearchOptionBuilder_state_Filter_Ready_Adds_single_entries_in_SearchOption() throws Exception { final Map filters = new HashMap<>(); filters.put(FlowNodeInstanceSearchDescriptor.STATE_NAME, "ready"); final SearchOptionsBuilder makeSearchOptionBuilder = datastore.makeSearchOptionBuilder(0, 10, "", "", filters); assertThat(makeSearchOptionBuilder.done().getFilters()).extracting("field", "operation", "value").contains( tuple("state", SearchFilterOperation.EQUALS, "ready"), tuple("state", SearchFilterOperation.DIFFERENT, "aborted"), tuple("state", SearchFilterOperation.DIFFERENT, "cancelled"), tuple("state", SearchFilterOperation.DIFFERENT, "completed")); } @Test public void makeSearchOptionBuilder_without_state_Filter_Adds_only_Unwanted_state_SearchOption() throws Exception { final Map filters = new HashMap<>(); filters.put(FlowNodeInstanceSearchDescriptor.STATE_NAME, "ready"); final SearchOptionsBuilder makeSearchOptionBuilder = datastore.makeSearchOptionBuilder(0, 10, "", "", filters); assertThat(makeSearchOptionBuilder.done().getFilters()).extracting("field", "operation", "value").contains( tuple("state", SearchFilterOperation.DIFFERENT, "aborted"), tuple("state", SearchFilterOperation.DIFFERENT, "cancelled"), tuple("state", SearchFilterOperation.DIFFERENT, "completed")); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/AbstractHumanTaskDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import static org.bonitasoft.web.rest.model.bpm.flownode.IHumanTaskItem.FILTER_SHOW_ASSIGNED_TO_OTHERS; import static org.bonitasoft.web.rest.model.bpm.flownode.IHumanTaskItem.FILTER_USER_ID; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import java.util.Collections; import java.util.Date; import java.util.HashMap; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.bpm.flownode.HumanTaskInstance; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.search.Order; import org.bonitasoft.engine.search.SearchOptionsBuilder; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.engine.session.impl.APISessionImpl; import org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem; import org.bonitasoft.web.rest.model.bpm.flownode.HumanTaskItem; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class AbstractHumanTaskDatastoreTest { @Mock private ProcessAPI processAPI; private final APISession session = new APISessionImpl(55L, new Date(), 5000, "john", 44L); private final AbstractHumanTaskDatastore datastore = new AbstractHumanTaskDatastore<>( session) { @Override protected ProcessAPI getProcessAPI() { return processAPI; } }; @Test public void should_use_searchAssignedAndPendingHumanTasks_when_filtering_ready_state() throws SearchException { datastore.runSearch(new SearchOptionsBuilder(1, 100), Collections.singletonMap("state", "ready")); verify(processAPI).searchAssignedAndPendingHumanTasks(any()); } @Test public void should_use_searchPendingOrAssignedToUserOrAssignedToOthersTasks_when_filtering_show_assigned_to_others_tasks() throws SearchException { HashMap filters = new HashMap<>(); filters.put(FILTER_USER_ID, "44"); filters.put(FILTER_SHOW_ASSIGNED_TO_OTHERS, "true"); datastore.runSearch(new SearchOptionsBuilder(1, 100), filters); verify(processAPI).searchPendingOrAssignedToUserOrAssignedToOthersTasks(eq(44L), any()); } @Test public void should_use_searchHumanTaskInstances_when_there_is_no_particular_filter() throws SearchException { datastore.runSearch(new SearchOptionsBuilder(1, 100), Collections.singletonMap("tpye", "toto")); verify(processAPI).searchHumanTaskInstances(any()); } @Test public void should_SearchOptionBuilderConvertSortParameter() throws SearchException { SearchOptionsBuilder builder = datastore.makeSearchOptionBuilder(0, 10, null, ActivityItem.ATTRIBUTE_ROOT_CASE_ID + " " + Order.DESC, new HashMap<>()); assertEquals(1, builder.done().getSorts().size()); assertEquals(ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID, builder.done().getSorts().get(0).getField()); assertEquals(Order.DESC, builder.done().getSorts().get(0).getOrder()); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/FlowNodeDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.spy; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.FlowNodeInstance; import org.bonitasoft.engine.bpm.flownode.impl.internal.UserTaskInstanceImpl; import org.bonitasoft.engine.exception.SearchException; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.search.impl.SearchResultImpl; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.ModelFactory; import org.bonitasoft.web.rest.model.bpm.flownode.FlowNodeItem; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.ItemDefinitionFactory; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class FlowNodeDatastoreTest { @Mock private APISession engineSession; @Mock private ProcessAPI processAPI; @Mock private FlowNodeConverter converter; @InjectMocks private FlowNodeDatastore flowNodeDatastore; @Before public void before() { ItemDefinitionFactory.setDefaultFactory(new ModelFactory()); FlowNodeConverter.setFlowNodeConverter(converter); flowNodeDatastore = spy(new FlowNodeDatastore(engineSession)); doReturn(processAPI).when(flowNodeDatastore).getProcessAPI(); } /** * Test method for * {@link org.bonitasoft.web.rest.server.datastore.bpm.flownode.AbstractFlowNodeDatastore#count(java.lang.String, java.lang.String, java.util.Map)}. */ @Test public final void count_should_return_number_of_flow_nodes_on_Engine() throws SearchException { final String search = "plop"; final String orders = FlowNodeItem.ATTRIBUTE_PARENT_CASE_ID; final Map filters = Collections.emptyMap(); final List flowNodeInstances = Arrays .asList((FlowNodeInstance) new UserTaskInstanceImpl("name", 9L, 18L)); final SearchResult searchResult = new SearchResultImpl<>(1L, flowNodeInstances); doReturn(searchResult).when(processAPI).searchFlowNodeInstances(any(SearchOptions.class)); // When final long result = flowNodeDatastore.count(search, orders, filters); // Then assertEquals(searchResult.getCount(), result); } /** * Test method for * {@link org.bonitasoft.web.rest.server.datastore.bpm.flownode.AbstractFlowNodeDatastore#search(int, int, java.lang.String, java.lang.String, java.util.Map)} * . */ @Test public final void search_should_get_list_of_flow_nodes_on_Engine_and_convert_them_to_FlowNodeItem() throws SearchException { final int page = 0; final int resultsByPage = 1; final String search = "plop"; final String orders = FlowNodeItem.ATTRIBUTE_DESCRIPTION; final Map filters = Collections.emptyMap(); final FlowNodeInstance flowNodeInstance = new UserTaskInstanceImpl("name", 9L, 18L); final List flowNodeInstances = Arrays.asList(flowNodeInstance); final SearchResult searchResult = new SearchResultImpl<>(1L, flowNodeInstances); doReturn(searchResult).when(processAPI).searchFlowNodeInstances(any(SearchOptions.class)); final FlowNodeItem flowNodeItem = new FlowNodeItem(); doReturn(flowNodeItem).when(converter)._convertEngineToConsoleItem(flowNodeInstance); // When final ItemSearchResult result = flowNodeDatastore.search(page, resultsByPage, search, orders, filters); // Then final List flowNodeItems = result.getResults(); assertEquals(1, flowNodeItems.size()); assertEquals(flowNodeItem, flowNodeItems.get(0)); } /** * Test method for * {@link org.bonitasoft.web.rest.server.datastore.bpm.flownode.AbstractFlowNodeDatastore#search(int, int, java.lang.String, java.lang.String, java.util.Map)} * . */ @Test(expected = APIException.class) public final void search_should_throw_an_exception_when_Engine_failed() throws SearchException { final int page = 0; final int resultsByPage = 1; final String search = "plop"; final String orders = FlowNodeItem.ATTRIBUTE_DESCRIPTION; final Map filters = Collections.emptyMap(); doThrow(new SearchException(new Exception("toto"))).when(processAPI) .searchFlowNodeInstances(any(SearchOptions.class)); // When flowNodeDatastore.search(page, resultsByPage, search, orders, filters); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/TaskFinderTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.bonitasoft.console.common.server.i18n.I18n; import org.bonitasoft.engine.bpm.flownode.ArchivedActivityInstanceSearchDescriptor; import org.bonitasoft.engine.search.Order; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedTaskItem; import org.bonitasoft.web.rest.model.bpm.flownode.TaskItem; import org.bonitasoft.web.rest.server.datastore.bpm.flownode.archive.ArchivedTaskDatastore; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.ItemDefinitionFactory; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class TaskFinderTest { @Mock private TaskDatastore journal; @Mock private ArchivedTaskDatastore archives; private TaskFinder taskFinder; private final APIID id = APIID.makeAPIID(658L); @Before public void setUp() throws Exception { I18n.getInstance(); ItemDefinitionFactory.setDefaultFactory(new ItemDefinitionFactory() { @Override public ItemDefinition defineItemDefinitions(final String token) { return null; } }); taskFinder = new TaskFinder(journal, archives); } @Test public void should_return_task_from_the_journal_when_it_belong_to_the_journal() throws Exception { final TaskItem task = new TaskItem(); task.setId(id); when(journal.get(id)).thenReturn(task); final IItem item = taskFinder.find(id); assertThat(item.getId()).isEqualTo(task.getId()); } @Test public void should_return_task_from_the_archives_when_not_found_in_the_journal() throws Exception { final ArchivedTaskItem task = new ArchivedTaskItem(); task.setId(id); when(journal.get(id)).thenThrow(new APIItemNotFoundException("type", id)); final Map filters = new HashMap<>(); filters.put(ArchivedActivityInstanceSearchDescriptor.SOURCE_OBJECT_ID, id.toString()); final ItemSearchResult result = mock(ItemSearchResult.class); when(result.getResults()).thenReturn(Arrays.asList(task)); when(archives.search(0, 1, null, ArchivedActivityItem.ATTRIBUTE_ARCHIVED_DATE + " " + Order.DESC, filters)).thenReturn(result); final IItem item = taskFinder.find(id); assertThat(item.getId()).isEqualTo(task.getId()); } @Test(expected = APIItemNotFoundException.class) public void should_throw_an_exception_when_the_task_does_not_exist() throws Exception { when(journal.get(id)).thenThrow(new APIItemNotFoundException("type", id)); final Map filters = new HashMap<>(); filters.put(ArchivedActivityInstanceSearchDescriptor.SOURCE_OBJECT_ID, id.toString()); when(archives.search(0, 1, null, ArchivedActivityItem.ATTRIBUTE_ARCHIVED_DATE + " " + Order.DESC, filters)).thenThrow(new APIItemNotFoundException("type", id)); taskFinder.find(id); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/AbstractArchivedFlowNodeDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive; import static org.assertj.core.api.Assertions.assertThat; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.search.impl.SearchFilter; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem; import org.junit.Test; /** * @author Emmanuel Duchastenier */ public class AbstractArchivedFlowNodeDatastoreTest { private ArchivedHumanTaskDatastore datastore = new ArchivedHumanTaskDatastore(null, "token"); @Test public void makeSearchOptionCreator_should_converts_TERMINAL_field_to_boolean() { final List filters = datastore.makeSearchOptionCreator(0, 10, "", "displayName ASC", Collections.singletonMap(ArchivedFlowNodeItem.FILTER_IS_TERMINAL, "true")).create().getFilters(); assertThat(filters.get(0).getValue()).isEqualTo(true); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedActivityDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.*; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedCallActivityInstanceImpl; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedGatewayInstanceImpl; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityDefinition; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedActivityItem; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedFlowNodeItem; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Emmanuel Duchastenier */ @RunWith(MockitoJUnitRunner.class) public class ArchivedActivityDatastoreTest { @Mock ProcessAPI processAPI; @Test public void should_return_ArchivedFlowNodeItem() throws Exception { APIID id = APIID.makeAPIID(1L); ArchivedActivityDatastore abstractArchivedActivityDatastore = spy( new ArchivedActivityDatastore(null, ArchivedActivityDefinition.TOKEN)); doReturn(processAPI).when(abstractArchivedActivityDatastore).getProcessAPI(); doReturn(new ArchivedCallActivityInstanceImpl("test")).when(processAPI) .getArchivedFlowNodeInstance(id.toLong()); doReturn(new ArchivedActivityItem()).when(abstractArchivedActivityDatastore).convertEngineToConsoleItem(any()); ArchivedFlowNodeItem archivedFlowNodeItem = abstractArchivedActivityDatastore.get(id); assertThat(archivedFlowNodeItem).isNotNull(); } @Test public void should_thrown_APIItemNotFoundException_when_ArchiveActivityInstance_not_exists() throws Exception { APIID id = APIID.makeAPIID(1L); ArchivedActivityDatastore archivedActivityDatastore = spy( new ArchivedActivityDatastore(null, ArchivedActivityDefinition.TOKEN)); doReturn(processAPI).when(archivedActivityDatastore).getProcessAPI(); doReturn(new ArchivedGatewayInstanceImpl("test")).when(processAPI).getArchivedFlowNodeInstance(id.toLong()); APIItemNotFoundException apiException = assertThrows(APIItemNotFoundException.class, () -> archivedActivityDatastore.get(id)); assertThat(apiException).isNotNull(); assertThat(apiException.getMessage()).containsIgnoringCase(ArchivedActivityDefinition.TOKEN); assertThat(apiException.getMessage()).containsIgnoringCase(id.toString()); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedHumanTaskDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.*; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedAutomaticTaskInstanceImpl; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskDefinition; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ArchivedHumanTaskDatastoreTest { ProcessAPI processAPI = mock(ProcessAPI.class); @Test public void should_thrown_APIItemNotFoundException_when_ArchivedHumanTaskInstance_not_exists() throws Exception { APIID id = APIID.makeAPIID(1L); ArchivedHumanTaskDatastore abstractArchivedActivityDatastore = spy( new ArchivedHumanTaskDatastore(null, ArchivedHumanTaskDefinition.TOKEN)); doReturn(processAPI).when(abstractArchivedActivityDatastore).getProcessAPI(); doReturn(new ArchivedAutomaticTaskInstanceImpl("test")).when(processAPI) .getArchivedFlowNodeInstance(id.toLong()); APIItemNotFoundException apiException = assertThrows(APIItemNotFoundException.class, () -> abstractArchivedActivityDatastore.get(id)); assertThat(apiException).isNotNull(); assertThat(apiException.getMessage()).containsIgnoringCase(ArchivedHumanTaskDefinition.TOKEN); assertThat(apiException.getMessage()).containsIgnoringCase(id.toString()); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/flownode/archive/ArchivedUserTaskDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.flownode.archive; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.flownode.impl.internal.ArchivedReceiveTaskInstanceImpl; import org.bonitasoft.web.rest.model.bpm.flownode.ArchivedHumanTaskDefinition; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemNotFoundException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ArchivedUserTaskDatastoreTest { @Mock ProcessAPI processAPI; @Test public void should_thrown_APIItemNotFoundException_when_ArchivedUserTaskInstance_not_exists() throws Exception { APIID id = APIID.makeAPIID(1L); ArchivedUserTaskDatastore abstractArchivedActivityDatastore = spy( new ArchivedUserTaskDatastore(null, ArchivedHumanTaskDefinition.TOKEN)); doReturn(processAPI).when(abstractArchivedActivityDatastore).getProcessAPI(); doReturn(new ArchivedReceiveTaskInstanceImpl("test")).when(processAPI).getArchivedFlowNodeInstance(id.toLong()); APIItemNotFoundException apiException = assertThrows(APIItemNotFoundException.class, () -> abstractArchivedActivityDatastore.get(id)); assertThat(apiException).isNotNull(); assertThat(apiException.getMessage()).containsIgnoringCase(ArchivedHumanTaskDefinition.TOKEN); assertThat(apiException.getMessage()).containsIgnoringCase(id.toString()); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/process/CategoryDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process; import static org.bonitasoft.web.rest.model.builder.bpm.process.CategoryItemBuilder.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.*; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; /** * @author Colin PUY */ public class CategoryDatastoreTest extends APITestWithMock { @Mock private ProcessAPI processAPI; private CategoryDatastore categoryDatastore; @Before public void initializeMocks() { initMocks(this); categoryDatastore = spy(new CategoryDatastore(null)); doReturn(this.processAPI).when(this.categoryDatastore).getProcessAPI(); } @SuppressWarnings("unchecked") @Test(expected = APIForbiddenException.class) public void addingTwiceSameCategoryIsForbidden() throws Exception { when(processAPI.createCategory(anyString(), anyString())) .thenThrow(new AlreadyExistsException("category already exists")); categoryDatastore.add(aCategoryItem().build()); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessCategoryDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process; import static java.util.Arrays.*; import static org.bonitasoft.web.rest.model.builder.bpm.process.ProcessCategoryItemBuilder.*; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.*; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.web.rest.model.bpm.process.ProcessCategoryItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.toolkit.client.common.exception.api.APIForbiddenException; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; /** * @author Colin PUY */ public class ProcessCategoryDatastoreTest extends APITestWithMock { @Mock private ProcessAPI processAPI; private ProcessCategoryDatastore processCategoryDatastore; @Before public void initializeMocks() { initMocks(this); processCategoryDatastore = spy(new ProcessCategoryDatastore(null)); doReturn(this.processAPI).when(processCategoryDatastore).getProcessAPI(); } @Test(expected = APIForbiddenException.class) public void addingTwiceSameCategoryOnProcessIsForbidden() throws Exception { ProcessCategoryItem processCategory = aProcessCategory().build(); doThrow(new AlreadyExistsException("this thing already exists!")).when(processAPI) .addCategoriesToProcess(processCategory.getProcessId().toLong(), asList(processCategory.getCategoryId().toLong())); processCategoryDatastore.add(processCategory); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessConnectorDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process; import static java.util.Arrays.*; import static java.util.Collections.*; import static junit.framework.Assert.*; import static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.*; import java.util.HashMap; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.connector.ConnectorCriterion; import org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor; import org.bonitasoft.engine.bpm.connector.ConnectorNotFoundException; import org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDefinition; import org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; /** * @author Colin PUY */ @SuppressWarnings("unchecked") public class ProcessConnectorDatastoreTest extends APITestWithMock { @Mock private ProcessAPI processAPI; private ProcessConnectorDatastore processConnectorDatastore; @Before public void initializeMocks() { initMocks(this); this.processConnectorDatastore = spy(new ProcessConnectorDatastore(null)); doReturn(this.processAPI).when(this.processConnectorDatastore).getProcessAPI(); } private APIID anAPIID(final String id, final String name, final String version) { final APIID apiid = APIID.makeAPIID(id, name, version); apiid.setItemDefinition(ProcessConnectorDefinition.get()); return apiid; } private ConnectorImplementationDescriptor aConnectorImplementationDescriptor(final String name) { final ConnectorImplementationDescriptor descriptor = new ConnectorImplementationDescriptor( "implementationClassName", name, "version", "definitionId", "definitionVersion", EMPTY_LIST); return descriptor; } private HashMap aProcessIdFilter(final String processId) { final HashMap filters = new HashMap<>(); filters.put(ATTRIBUTE_PROCESS_ID, processId); return filters; } private ProcessConnectorItem convertToItem(final ConnectorImplementationDescriptor descriptor1, final String processId) { final ProcessConnectorItem item = this.processConnectorDatastore.convertEngineToConsoleItem(descriptor1); item.setProcessId(processId); return item; } @Test public void getRetrieveConnectorImplementationAndSetProcessId() throws Exception { final ConnectorImplementationDescriptor descriptor = aConnectorImplementationDescriptor("aName"); when(this.processAPI.getConnectorImplementation(1L, "name", "1")).thenReturn(descriptor); final ProcessConnectorItem fetchedItem = this.processConnectorDatastore.get(anAPIID("1", "name", "1")); final ProcessConnectorItem expectedItem = convertToItem(descriptor, "1"); assertTrue(areEquals(expectedItem, fetchedItem)); } @Test(expected = APIException.class) public void getThrowExceptionIfProcessDefinitionIsNotFound() throws Exception { when(this.processAPI.getConnectorImplementation(anyLong(), anyString(), anyString())) .thenThrow(new ConnectorNotFoundException(null)); this.processConnectorDatastore.get(anAPIID("1", "name", "1")); } @Test public void searchReturnAllProcessConnectorForAProcessDefinitionId() throws Exception { final ConnectorImplementationDescriptor descriptor1 = aConnectorImplementationDescriptor("aName"); final ConnectorImplementationDescriptor descriptor2 = aConnectorImplementationDescriptor("anOtherName"); when(this.processAPI.getConnectorImplementations(anyLong(), anyInt(), anyInt(), any(ConnectorCriterion.class))) .thenReturn(asList(descriptor1, descriptor2)); final ItemSearchResult search = this.processConnectorDatastore.search(0, 10, null, "DEFINITION_ID_ASC", aProcessIdFilter("1")); final ProcessConnectorItem expectedItem1 = convertToItem(descriptor1, "1"); final ProcessConnectorItem expectedItem2 = convertToItem(descriptor2, "1"); assertTrue(areEquals(search.getResults().get(0), expectedItem1)); assertTrue(areEquals(search.getResults().get(1), expectedItem2)); } @Test public void testConvertEngineToConsoleItem() throws Exception { final ConnectorImplementationDescriptor descriptor = new ConnectorImplementationDescriptor( "implementationClassName", "name", "version", "definitionId", "definitionVersion", EMPTY_LIST); final ProcessConnectorItem expectedItem = new ProcessConnectorItem(); expectedItem.setName("definitionId"); expectedItem.setVersion("definitionVersion"); expectedItem.setImplementationName("name"); expectedItem.setImplementationVersion("version"); expectedItem.setClassname("implementationClassName"); final ProcessConnectorItem convertedItem = this.processConnectorDatastore .convertEngineToConsoleItem(descriptor); assertTrue(areEquals(expectedItem, convertedItem)); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessConnectorDependencyDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process; import static java.util.Arrays.*; import static junit.framework.Assert.*; import static org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.*; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.connector.ConnectorImplementationDescriptor; import org.bonitasoft.engine.bpm.connector.ConnectorNotFoundException; import org.bonitasoft.web.rest.model.bpm.process.ProcessConnectorDependencyItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; /** * @author Colin PUY */ public class ProcessConnectorDependencyDatastoreTest extends APITestWithMock { @Mock private ProcessAPI processAPI; private ProcessConnectorDependencyDatastore datastore; @Before public void initializeMocks() { initMocks(this); this.datastore = spy(new ProcessConnectorDependencyDatastore(null)); doReturn(this.processAPI).when(this.datastore).getProcessAPI(); } private Map buildFilters(final Long processId, final String connectorName, final String connectorVersion) { final Map filters = new HashMap<>(); filters.put(ATTRIBUTE_PROCESS_ID, String.valueOf(processId)); filters.put(ATTRIBUTE_CONNECTOR_NAME, connectorName); filters.put(ATTRIBUTE_CONNECTOR_VERSION, connectorVersion); return filters; } @Test public void searchReturnAnEmptyResultIfNoConnectorImplementationIsFound() throws Exception { when(this.processAPI.getConnectorImplementation(anyLong(), anyString(), anyString())).thenReturn(null); final Map filters = buildFilters(1L, "aConnectorName", "1"); final ItemSearchResult searchResult = this.datastore.search(0, 10, null, null, filters); assertTrue(searchResult.getResults().isEmpty()); assertEquals(0L, searchResult.getTotal()); } @Test(expected = APIException.class) @SuppressWarnings("unchecked") public void searchThrowExceptionIfProcessIdIsUnknown() throws Exception { when(this.processAPI.getConnectorImplementation(anyLong(), anyString(), anyString())).thenThrow( new ConnectorNotFoundException(new NullPointerException())); final Map filters = buildFilters(1L, "aConnectorName", "1"); this.datastore.search(0, 10, null, null, filters); } @Test public void searchCanBePaginated() throws Exception { final ConnectorImplementationDescriptor connectorWith3Dependencies = new ConnectorImplementationDescriptor( "implementationClassName", "connectorId", "connectorVersion", "1", "definitionVersion", asList("dependency1", "dependency2", "dependency3")); when(this.processAPI.getConnectorImplementation(1L, "connectorId", "connectorVersion")) .thenReturn(connectorWith3Dependencies); final Map filters = buildFilters(1L, "connectorId", "connectorVersion"); final ItemSearchResult searchResult = this.datastore.search(0, 2, null, null, filters); assertEquals(2L, searchResult.getResults().size()); assertEquals(3L, searchResult.getTotal()); assertEquals(searchResult.getResults().get(0).getFilename(), "dependency1"); assertEquals(searchResult.getResults().get(1).getFilename(), "dependency2"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/process/ProcessDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bonitasoft.console.common.server.page.CustomPageService; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.PlatformManagementUtils; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.page.Page; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.rest.model.bpm.process.ProcessItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.engineclient.ProcessEngineClient; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ProcessDatastoreTest extends APITestWithMock { private ProcessDatastore processDatastore; @Mock private APISession engineSession; @Mock private ProcessEngineClient processEngineClient; @Mock private BonitaHomeFolderAccessor tenantFolder; @Mock private PageAPI pageAPI; @Mock private CustomPageService customPageService; @Mock private SearchResult searchResult; @Mock private PlatformManagementUtils platformManagementUtils; private final ProcessItem processItem = new ProcessItem(); @Before public void setUp() throws Exception { processDatastore = spy(new ProcessDatastore(engineSession)); doReturn(processEngineClient).when(processDatastore).getProcessEngineClient(); doReturn(customPageService).when(processDatastore).getCustomPageService(); doReturn(pageAPI).when(processDatastore).getPageAPI(); doReturn(searchResult).when(pageAPI).searchPages(any(SearchOptions.class)); } @Test public void it_removes_the_pages_when_deleting_a_process() throws IOException { final Page page1 = mock(Page.class); final Page page2 = mock(Page.class); doReturn(Arrays.asList(page1, page2)).when(searchResult).getResult(); doReturn(2L).when(searchResult).getCount(); final APIID id = APIID.makeAPIID(2L); processDatastore.delete(List.of(id)); verify(processDatastore).removeProcessPagesFromHome(id); verify(customPageService, times(1)).removePageLocally(page1); verify(customPageService, times(1)).removePageLocally(page2); } @Test public void it_removes_the_pages_when_deleting_a_process_with_pagination() throws IOException { final long nbOfPages = 130L; final Page page = mock(Page.class); final List pages = new ArrayList<>(); for (int i = 0; i < nbOfPages; i++) { pages.add(page); } doReturn(pages).when(searchResult).getResult(); doReturn(nbOfPages).when(searchResult).getCount(); final APIID id = APIID.makeAPIID(2L); processDatastore.delete(List.of(id)); verify(processDatastore).removeProcessPagesFromHome(id); verify(customPageService, times((int) nbOfPages)).removePageLocally(page); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/bpm/process/helper/ProcessItemConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.bpm.process.helper; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; import java.util.Date; import org.bonitasoft.console.common.server.i18n.I18n; import org.bonitasoft.console.common.server.utils.TenantCacheUtilFactory; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.actor.ActorInstance; import org.bonitasoft.engine.bpm.actor.ActorNotFoundException; import org.bonitasoft.engine.bpm.process.ActivationState; import org.bonitasoft.engine.bpm.process.ConfigurationState; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessDeploymentInfo; import org.bonitasoft.engine.bpm.process.impl.internal.ProcessDeploymentInfoImpl; import org.bonitasoft.web.rest.model.ModelFactory; import org.bonitasoft.web.rest.model.bpm.process.ProcessItem; import org.bonitasoft.web.toolkit.client.ItemDefinitionFactory; import org.bonitasoft.web.toolkit.client.common.CommonDateFormater; import org.bonitasoft.web.toolkit.server.utils.ServerDateFormater; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ProcessItemConverterTest { private ProcessItemConverter processItemConverter; @Mock private ProcessAPI processAPI; @Mock private ActorInstance actorInstance1; @Mock private ActorInstance actorInstance2; @Before public void setUp() throws Exception { I18n.getInstance(); CommonDateFormater.setDateFormater(new ServerDateFormater()); ItemDefinitionFactory.setDefaultFactory(new ModelFactory()); processItemConverter = spy(new ProcessItemConverter(processAPI)); TenantCacheUtilFactory.clearTenantCacheUtil(); } @Test public void shouldReadActorInitiatorFromCacheOnSecondCall() throws ProcessDefinitionNotFoundException, ActorNotFoundException { when(processAPI.getActorInitiator(3L)).thenReturn(actorInstance2); doReturn(6L).when(actorInstance2).getId(); ProcessDeploymentInfo processDeploymentInfo = new ProcessDeploymentInfoImpl(1, 3L, "ProcessName", "Version", "Description", new Date(), 3, ActivationState.ENABLED, ConfigurationState.RESOLVED, "displayName", new Date(), "iconPath", "displayDescription"); ProcessItem processItem = processItemConverter.convert(processDeploymentInfo); //Get 2 ActorInitiatorId from engine then store them in cache assertEquals("6", processItem.getActorInitiatorId()); processItem = processItemConverter.convert(processDeploymentInfo); //Get ActorInitiatorId from cache assertEquals("6", processItem.getActorInitiatorId()); //it should call getActorInitiator only one times because the second should be read from the cache verify(processAPI, times(1)).getActorInitiator(3L); } @Test public void shouldStoreDifferentActorInitiatorIntoCache() throws ActorNotFoundException, ProcessDefinitionNotFoundException { when(processAPI.getActorInitiator(1L)).thenReturn(actorInstance1); when(processAPI.getActorInitiator(2L)).thenReturn(actorInstance2); doReturn(5L).when(actorInstance1).getId(); doReturn(6L).when(actorInstance2).getId(); ProcessDeploymentInfo firstProcessDeploymentInfo = new ProcessDeploymentInfoImpl(1, 1, "ProcessName1", "Version1", "Description1", new Date(), 3, ActivationState.ENABLED, ConfigurationState.RESOLVED, "displayName1", new Date(), "iconPath1", "displayDescription1"); ProcessDeploymentInfo secondProcessDeploymentInfo = new ProcessDeploymentInfoImpl(2, 2, "ProcessName2", "Version2", "Description2", new Date(), 3, ActivationState.ENABLED, ConfigurationState.RESOLVED, "displayName2", new Date(), "iconPath2", "displayDescription2"); //Get 2 ActorInitiatorId from engine then store them in cache ProcessItem processItem = processItemConverter.convert(firstProcessDeploymentInfo); assertEquals("5", processItem.getActorInitiatorId()); processItem = processItemConverter.convert(secondProcessDeploymentInfo); assertEquals("6", processItem.getActorInitiatorId()); // Read 2 different ActorInitiatorId from cache processItem = processItemConverter.convert(firstProcessDeploymentInfo); assertEquals("5", processItem.getActorInitiatorId()); processItem = processItemConverter.convert(secondProcessDeploymentInfo); assertEquals("6", processItem.getActorInitiatorId()); //it should call getActorInitiator only one times because the second should be read from the cache verify(processAPI, times(1)).getActorInitiator(1L); verify(processAPI, times(1)).getActorInitiator(2L); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/converter/ItemSearchResultConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.converter; import static java.util.Arrays.asList; import static junit.framework.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APISearchIndexOutOfRange; import org.bonitasoft.web.toolkit.client.data.item.IItem; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; /** * @author Vincent Elcrin */ public class ItemSearchResultConverterTest { @Mock SearchResult result; @Mock ItemConverter converter; @Before public void initializeMocks() { initMocks(this); } @Test public void testTotalCanBeRetrieved() { when(result.getCount()).thenReturn(5L); ItemSearchResultConverter itemSearchResult = new ItemSearchResultConverter<>(3, 2, result, converter); assertEquals(5L, itemSearchResult.toItemSearchResult().getTotal()); } @Test public void testTotalSetCanBeRetrieved() { ItemSearchResultConverter itemSearchResult = new ItemSearchResultConverter<>(3, 2, result, 8L, converter); assertEquals(8L, itemSearchResult.toItemSearchResult().getTotal()); } @Test(expected = APISearchIndexOutOfRange.class) public void testPageOutOfResultNumberThrowsException() { when(result.getCount()).thenReturn(1L); new ItemSearchResultConverter<>(2, 10, result, converter).toItemSearchResult(); } @Test public void testPageNumberCanBeRetrieved() { ItemSearchResultConverter itemSearchResult = new ItemSearchResultConverter<>(5, 10, result, 8L, converter); assertEquals(5, itemSearchResult.toItemSearchResult().getPage()); } @Test public void testResultingItemsCanBeRetrieved() { IItem item1 = mock(IItem.class); IItem item2 = mock(IItem.class); when(result.getResult()).thenReturn(asList("item1", "item2")); when(converter.convert(result.getResult())).thenReturn(asList(item1, item2)); ItemSearchResultConverter itemSearchResult = new ItemSearchResultConverter<>(1, 10, result, 2, converter); assertEquals(item1, itemSearchResult.toItemSearchResult().getResults().get(0)); assertEquals(item2, itemSearchResult.toItemSearchResult().getResults().get(1)); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/converter/LongValueConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.converter; import static junit.framework.Assert.assertEquals; import org.junit.Test; /** * @author Vincent Elcrin */ public class LongValueConverterTest { @Test public void testLongValueConvertion() throws Exception { Long value = new LongValueConverter().convert("54"); assertEquals(Long.valueOf(54L), value); } @Test(expected = NumberFormatException.class) public void testStringConvertionThrowsExceptio() throws Exception { new LongValueConverter().convert("abc"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/converter/StringValueConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.converter; import static org.junit.Assert.assertEquals; import org.junit.Test; /** * @author Vincent Elcrin */ public class StringValueConverterTest { @Test public void testStringConvertion() throws Exception { String value = new StringValueConverter().convert("cba"); assertEquals("cba", value); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/FieldTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import junit.framework.Assert; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.junit.Test; import org.mockito.Mock; import org.mockito.Mockito; /** * @author Vincent Elcrin */ public class FieldTest { @Mock private AttributeConverter converter = Mockito.mock(AttributeConverter.class); @Test public void testFieldConvertion() { Mockito.doReturn("converted").when(converter).convert("attribute"); Field field = new Field("attribute", converter); String s = field.toString(); Assert.assertEquals("converted", s); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/FilterAccessorTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import static org.junit.Assert.assertEquals; import java.util.Collections; import org.bonitasoft.web.rest.server.datastore.profile.member.MemberTypeConverter; import org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException; import org.junit.Test; /** * @author Vincent Elcrin */ public class FilterAccessorTest { @Test public void testWeCanRetrieveMandatoryValue() throws Exception { FilterAccessor filterAccess = new FilterAccessor(Collections.singletonMap("key", "value")); String value = filterAccess.getMandatory("key"); assertEquals("value", value); } @Test(expected = APIFilterMandatoryException.class) public void testAccessToMandatoryValueWhichItDoesntExitThrowException() { FilterAccessor filterAccess = new FilterAccessor(Collections. emptyMap()); filterAccess.getMandatory("key"); } @Test(expected = APIFilterMandatoryException.class) public void testAccessToMandatoryValueNotConvertibleThrowException() { FilterAccessor filterAccess = new FilterAccessor(Collections.singletonMap("key", "value")); filterAccess.getMandatory("key", new MemberTypeConverter()); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/FilterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import junit.framework.Assert; import org.junit.Test; import org.mockito.Mock; import org.mockito.Mockito; /** * @author Vincent Elcrin */ public class FilterTest { @Mock Field field = Mockito.mock(Field.class); @Mock @SuppressWarnings("unchecked") Value value = Mockito.mock(Value.class); @Test public void testFilterField() throws Exception { Mockito.doReturn("field").when(field).toString(); Filter filter = new Filter<>(field, value); Assert.assertEquals("field", filter.getField()); } @Test public void testFilterValue() throws Exception { Mockito.doReturn("value").when(value).cast(); Filter filter = new Filter<>(field, value); Assert.assertEquals("value", filter.getValue()); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/FiltersTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import java.util.List; import java.util.Map; import junit.framework.Assert; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.toolkit.client.common.texttemplate.Arg; import org.bonitasoft.web.toolkit.client.common.util.MapUtil; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; /** * @author Vincent Elcrin */ public class FiltersTest { @Mock FilterCreator filterCreator; @Mock Filter longFilter; @Mock Filter strFilter; @Mock AttributeConverter fiedConverter; @Before public void initFilters() { initMocks(this); doReturn("field1").when(longFilter).getField(); doReturn(3L).when(longFilter).getValue(); doReturn("field2").when(strFilter).getField(); doReturn("str").when(strFilter).getValue(); } @Test public void testFiltersListWithoutFilterCreator() throws Exception { when(fiedConverter.convert("field")) .thenReturn("field"); Filters filters = new Filters(aMapWith(new Arg("field", "value")), new GenericFilterCreator(fiedConverter)); List> filterList = filters.asList(); Assert.assertEquals("field", filterList.get(0).getField()); Assert.assertEquals("value", filterList.get(0).getValue()); } @Test public void testFilterListWithMultiTypeFilterCreator() throws Exception { Map map = aMapWith(new Arg("field1", "value"), new Arg("field2", "value")); doReturn(longFilter).when(filterCreator).create(eq("field1"), anyString()); doReturn(strFilter).when(filterCreator).create(eq("field2"), anyString()); Filters filters = new Filters(map, filterCreator); assertTrue(IsRightValue(filters.asList().get(0))); assertTrue(IsRightValue(filters.asList().get(1))); } private boolean IsRightValue(Filter filter) { if ("field1".equals(filter.getField())) { return filter.getValue().equals(3L); } else if ("field2".equals(filter.getField())) { return filter.getValue().equals("str"); } else { return false; } } private Map aMapWith(Arg... args) { return MapUtil.asMap(args); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/GenericFilterCreatorTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.mockito.MockitoAnnotations.initMocks; import java.io.Serializable; import java.util.Collections; import java.util.Map; import org.bonitasoft.web.rest.server.datastore.converter.AttributeConverter; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; import org.junit.Before; import org.junit.Test; /** * @author Vincent Elcrin * @author Emmanuel Duchastenier */ public class GenericFilterCreatorTest { @Before public void setUp() { initMocks(this); } @Test public void method_create_should_handle_value_as_String_by_default() { GenericFilterCreator creator = new GenericFilterCreator(new AttributeConverter() { @Override public String convert(String attribute) { return attribute; } @Override public Map getValueTypeMapping() { return Collections.emptyMap(); } }); Filter filter = creator.create("attribute", "value"); assertEquals("attribute", filter.getField()); assertEquals("value", filter.getValue()); } @Test public void method_create_should_handle_boolean_values() { AttributeConverter converter = new AttributeConverter() { @Override public String convert(String attribute) { return attribute; } @Override public Map getValueTypeMapping() { return Collections.singletonMap("myAttribute", ItemAttribute.TYPE.BOOLEAN); } }; GenericFilterCreator creator = new GenericFilterCreator(converter); Filter filter = creator.create("myAttribute", "true"); assertThat(filter.getValue()).isEqualTo(true); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/LongValueTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import static junit.framework.Assert.assertEquals; import org.junit.Test; /** * @author Vincent Elcrin */ public class LongValueTest { @Test public void testLongValue() { LongValue value = new LongValue("8"); assertEquals(Long.valueOf(8), value.cast()); } @Test(expected = NumberFormatException.class) public void testNoneLongValueThrowException() { LongValue value = new LongValue("abc"); value.cast(); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/StrValueTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import static org.junit.Assert.assertEquals; import org.junit.Test; /** * @author Vincent Elcrin */ public class StrValueTest { @Test public void testStringValue() throws Exception { StrValue value = new StrValue("abc"); assertEquals("abc", value.cast()); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/filter/ValueTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.filter; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import org.bonitasoft.web.rest.server.datastore.converter.ValueConverter; import org.junit.Test; /** * @author Vincent Elcrin */ public class ValueTest { @Test public void testName() throws Exception { @SuppressWarnings("unchecked") ValueConverter converter = mock(ValueConverter.class); doReturn(5L).when(converter).convert("12"); Value value = new Value<>("12", converter); assertEquals(Long.valueOf(5), value.cast()); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/organization/GroupUpdaterConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; import java.io.Serializable; import java.util.HashMap; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.IconDescriptor; import org.bonitasoft.engine.identity.GroupUpdater; import org.bonitasoft.engine.identity.GroupUpdater.GroupField; import org.bonitasoft.web.rest.model.identity.GroupItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.engineclient.GroupEngineClient; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Colin PUY */ @RunWith(MockitoJUnitRunner.class) public class GroupUpdaterConverterTest extends APITestWithMock { @Mock private GroupEngineClient groupEngineClient; @Mock private BonitaHomeFolderAccessor bonitaHomeFolderAccessor; @InjectMocks @Spy private GroupUpdaterConverter groupUpdaterConverter; @Before public void init() { doReturn(bonitaHomeFolderAccessor).when(groupUpdaterConverter).getBonitaHomeFolderAccessor(); } private Serializable getFieldValue(GroupUpdater groupUpdater, GroupField field) { return groupUpdater.getFields().get(field); } private HashMap buildSimpleAttribute(String attributeName, String attributeValue) { HashMap attributes = new HashMap<>(); attributes.put(attributeName, attributeValue); return attributes; } @Test public void convert_return_a_groupUpdater_with_required_fields() throws Exception { HashMap attributes = new HashMap<>(); attributes.put(GroupItem.ATTRIBUTE_DESCRIPTION, "aNewDescription"); attributes.put(GroupItem.ATTRIBUTE_ICON, "aNewIcon"); attributes.put(GroupItem.ATTRIBUTE_NAME, "aNewName"); attributes.put(GroupItem.ATTRIBUTE_DISPLAY_NAME, "aNewDisplayName"); byte[] content = { 1, 2, 3 }; doReturn(new IconDescriptor("aNewIcon.png", content)).when(bonitaHomeFolderAccessor) .getIconFromFileSystem("aNewIcon"); GroupUpdater updater = groupUpdaterConverter.convert(attributes); assertThat(getFieldValue(updater, GroupField.DESCRIPTION)).isEqualTo("aNewDescription"); assertThat(getFieldValue(updater, GroupField.ICON_FILENAME)).isEqualTo("aNewIcon.png"); assertThat(getFieldValue(updater, GroupField.ICON_CONTENT)).isEqualTo(content); assertThat(getFieldValue(updater, GroupField.NAME)).isEqualTo("aNewName"); assertThat(getFieldValue(updater, GroupField.DISPLAY_NAME)).isEqualTo("aNewDisplayName"); } @Test public void convert_should_skip_empty_icon() throws Exception { //given HashMap attributes = new HashMap<>(); attributes.put(GroupItem.ATTRIBUTE_ICON, ""); attributes.put(GroupItem.ATTRIBUTE_NAME, "aNewName"); //when GroupUpdater updater = groupUpdaterConverter.convert(attributes); //then assertThat(getFieldValue(updater, GroupField.ICON_FILENAME)).isNull(); assertThat(getFieldValue(updater, GroupField.ICON_CONTENT)).isNull(); assertThat(getFieldValue(updater, GroupField.NAME)).isEqualTo("aNewName"); verify(bonitaHomeFolderAccessor, never()).getIconFromFileSystem(anyString()); } @Test public void convert_dont_update_name_if_this_is_a_blank_value() throws Exception { String unexpectedName = " "; HashMap attribute = buildSimpleAttribute(GroupItem.ATTRIBUTE_NAME, unexpectedName); GroupUpdater updater = groupUpdaterConverter.convert(attribute); assertThat(getFieldValue(updater, GroupField.NAME)).isNull(); } @Test public void convert_update_parent_path_if_a_parent_group_id_is_specified() throws Exception { HashMap attributes = buildSimpleAttribute(GroupItem.ATTRIBUTE_PARENT_GROUP_ID, "101"); when(groupEngineClient.getPath("101")).thenReturn("/Expected/Parent/Path"); GroupUpdater updater = groupUpdaterConverter.convert(attributes); assertThat(getFieldValue(updater, GroupField.PARENT_PATH)).isEqualTo("/Expected/Parent/Path"); } @Test public void convert_set_parent_path_to_empty_if_parentGroupId_is_an_empty_string() throws Exception { HashMap attributes = buildSimpleAttribute(GroupItem.ATTRIBUTE_PARENT_GROUP_ID, ""); GroupUpdater updater = groupUpdaterConverter.convert(attributes); assertThat(getFieldValue(updater, GroupField.PARENT_PATH)).isEqualTo(""); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/organization/RoleDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; import java.util.Collections; import org.bonitasoft.console.common.server.i18n.I18n; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.IconDescriptor; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.identity.RoleCreator; import org.bonitasoft.engine.identity.RoleUpdater; import org.bonitasoft.engine.identity.impl.RoleImpl; import org.bonitasoft.web.rest.model.ModelFactory; import org.bonitasoft.web.rest.model.identity.RoleItem; import org.bonitasoft.web.toolkit.client.ItemDefinitionFactory; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.Item; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class RoleDatastoreTest { @InjectMocks @Spy private RoleDatastore roleDatastore; @Mock private IdentityAPI identityAPI; @Mock private BonitaHomeFolderAccessor bonitaHomeFolderAccessor; @Captor private ArgumentCaptor roleUpdaterArgumentCaptor; @Captor private ArgumentCaptor roleCreatorArgumentCaptor; @Before public void before() throws Exception { ItemDefinitionFactory.setDefaultFactory(new ModelFactory()); I18n.getInstance(); Item.setApplyInputModifiersByDefault(false); Item.setApplyValidatorsByDefault(false); Item.setApplyOutputModifiersByDefault(false); Item.setApplyValidatorMandatoryByDefault(false); doReturn(identityAPI).when(roleDatastore).getIdentityAPI(); doReturn(bonitaHomeFolderAccessor).when(roleDatastore).getBonitaHomeFolderAccessor(); } @Test public void should_get_retrieve_role_with_icon() throws Exception { //given RoleImpl myRole = new RoleImpl(12L, "myRole"); myRole.setIconId(2134L); doReturn(myRole).when(identityAPI).getRole(12L); //when RoleItem roleItem = roleDatastore.get(APIID.makeAPIID(12L)); //then assertThat(roleItem.getIcon()).isEqualTo("../API/avatars/2134"); } @Test public void should_get_retrieve_role_without_icon() throws Exception { //given RoleImpl myRole = new RoleImpl(12L, "myRole"); doReturn(myRole).when(identityAPI).getRole(12L); //when RoleItem roleItem = roleDatastore.get(APIID.makeAPIID(12L)); //then assertThat(roleItem.getIcon()).isEmpty(); } @Test public void should_retrieve_role_from_engine() throws Exception { //given doReturn(new RoleImpl(123, "myRole")).when(identityAPI).getRole(123L); //when RoleItem roleItem = roleDatastore.get(APIID.makeAPIID(123L)); //then assertThat(roleItem.getName()).isEqualTo("myRole"); } @Test public void should_update_role_in_the_engine() throws Exception { RoleUpdater roleUpdater = new RoleUpdater().setName("newName"); doReturn(new RoleImpl(123, "newName")).when(identityAPI).updateRole(eq(123L), eq(roleUpdater)); //when roleDatastore.update(APIID.makeAPIID(123L), Collections.singletonMap("name", "newName")); //then verify(identityAPI).updateRole(eq(123L), eq(roleUpdater)); } @Test public void should_update_icon_of_role_give_content_to_engine() throws Exception { doReturn(new RoleImpl(123, "newName")).when(identityAPI).updateRole(anyLong(), any(RoleUpdater.class)); IconDescriptor iconDescriptor = new IconDescriptor("iconName", "content".getBytes()); doReturn(iconDescriptor).when(bonitaHomeFolderAccessor).getIconFromFileSystem(eq("temp_icon_on_fs")); //when roleDatastore.update(APIID.makeAPIID(123L), Collections.singletonMap("icon", "temp_icon_on_fs")); //then verify(identityAPI).updateRole(eq(123L), roleUpdaterArgumentCaptor.capture()); RoleUpdater roleUpdater = roleUpdaterArgumentCaptor.getValue(); assertThat(roleUpdater.getFields().get(RoleUpdater.RoleField.ICON_FILENAME)).isEqualTo("iconName"); assertThat(roleUpdater.getFields().get(RoleUpdater.RoleField.ICON_CONTENT)).isEqualTo("content".getBytes()); } @Test public void should_update_role_with_empty_icon() throws Exception { //given doReturn(new RoleImpl(123, "newName")).when(identityAPI).updateRole(anyLong(), any(RoleUpdater.class)); //when roleDatastore.update(APIID.makeAPIID(123L), Collections.singletonMap("icon", "")); //then verify(identityAPI).updateRole(eq(123L), roleUpdaterArgumentCaptor.capture()); RoleUpdater roleUpdater = roleUpdaterArgumentCaptor.getValue(); assertThat(roleUpdater.getFields().get(RoleUpdater.RoleField.ICON_FILENAME)).isNull(); assertThat(roleUpdater.getFields().get(RoleUpdater.RoleField.ICON_CONTENT)).isNull(); verify(bonitaHomeFolderAccessor, never()).getIconFromFileSystem(anyString()); } @Test public void should_add_role_with_icon_give_content_to_engine() throws Exception { doReturn(new RoleImpl(123, "newName")).when(identityAPI).createRole(any(RoleCreator.class)); IconDescriptor iconDescriptor = new IconDescriptor("iconName", "content".getBytes()); doReturn(iconDescriptor).when(bonitaHomeFolderAccessor).getIconFromFileSystem(eq("temp_icon_on_fs")); RoleItem roleItem = new RoleItem(); roleItem.setIcon("temp_icon_on_fs"); roleItem.setName("name"); //when roleDatastore.add(roleItem); //then verify(identityAPI).createRole(roleCreatorArgumentCaptor.capture()); RoleCreator roleUpdater = roleCreatorArgumentCaptor.getValue(); assertThat(roleUpdater.getFields().get(RoleCreator.RoleField.ICON_FILENAME)).isEqualTo("iconName"); assertThat(roleUpdater.getFields().get(RoleCreator.RoleField.ICON_CONTENT)).isEqualTo("content".getBytes()); assertThat(roleUpdater.getFields().get(RoleCreator.RoleField.NAME)).isEqualTo("name"); } @Test public void add_role_without_icon_should_create_role_without_retrieving_icon_from_filesystem() throws Exception { doReturn(new RoleImpl(123, "name")).when(identityAPI).createRole(any(RoleCreator.class)); RoleItem roleItem = new RoleItem(); roleItem.setIcon(""); roleItem.setName("name"); //when roleDatastore.add(roleItem); //then verify(bonitaHomeFolderAccessor, never()).getIconFromFileSystem(anyString()); verify(identityAPI).createRole(any(RoleCreator.class)); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/organization/UserDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.console.common.server.utils.IconDescriptor; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.identity.User; import org.bonitasoft.engine.identity.UserUpdater; import org.bonitasoft.engine.identity.impl.UserImpl; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.SearchResult; import org.bonitasoft.engine.search.impl.SearchFilter; import org.bonitasoft.engine.search.impl.SearchResultImpl; import org.bonitasoft.web.rest.model.identity.UserItem; import org.bonitasoft.web.rest.server.engineclient.ProcessEngineClient; import org.bonitasoft.web.rest.server.engineclient.UserEngineClient; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Vincent Elcrin */ @RunWith(MockitoJUnitRunner.class) public class UserDatastoreTest { @Mock private ProcessAPI processAPI; @Mock private ProcessEngineClient processEngineClient; @Mock private IdentityAPI identityAPI; @Mock UserItemConverter userItemConverter; @Mock private BonitaHomeFolderAccessor bonitaHomeFolderAccessor; @Spy @InjectMocks private UserDatastore datastore = new UserDatastore(null); @Captor private ArgumentCaptor userUpdaterArgumentCaptor; @Before public void init() { UserEngineClient userEngineClient = new UserEngineClient(identityAPI); doReturn(userEngineClient).when(datastore).getUserEngineClient(); doReturn(processEngineClient).when(datastore).getProcessEngineClient(); when(processEngineClient.getProcessApi()).thenReturn(processAPI); doReturn(bonitaHomeFolderAccessor).when(datastore).getBonitaHomeFolderAccessor(); } @Test public void testSearchWithMultipleSortOrderDoesNotThrowException() throws Exception { final String sort = UserItem.ATTRIBUTE_FIRSTNAME + "," + UserItem.ATTRIBUTE_LASTNAME; Mockito.doReturn(new SearchResultImpl<>(0, Collections. emptyList())).when(identityAPI) .searchUsers(Mockito.any(SearchOptions.class)); try { datastore.search(0, 1, "search", sort, Collections.emptyMap()); } catch (Exception e) { Assert.fail("Search should be able to handle multiple sort"); } } @Test public void should_updateUser_call_engine_api() throws Exception { //given doReturn(new UserImpl(12L, "john")).when(identityAPI).updateUser(eq(12L), any(UserUpdater.class)); //when Map attributes = new HashMap<>(); attributes.put("userName", "jack"); attributes.put("icon", ""); datastore.update(APIID.makeAPIID(12L), attributes); //then verify(identityAPI).updateUser(eq(12L), userUpdaterArgumentCaptor.capture()); UserUpdater userUpdater = userUpdaterArgumentCaptor.getValue(); assertThat(userUpdater.getFields()).containsOnly(entry(UserUpdater.UserField.USER_NAME, "jack")); } @Test public void should_updateUser_with_icon_call_engine_api_with_content_from_FS() throws Exception { //given doReturn(new UserImpl(12L, "john")).when(identityAPI).updateUser(eq(12L), any(UserUpdater.class)); IconDescriptor iconDescriptor = new IconDescriptor("iconName", "content".getBytes()); doReturn(iconDescriptor).when(bonitaHomeFolderAccessor).getIconFromFileSystem(eq("temp_icon_on_fs")); //when datastore.update(APIID.makeAPIID(12L), Collections.singletonMap("icon", "temp_icon_on_fs")); //then verify(identityAPI).updateUser(eq(12L), userUpdaterArgumentCaptor.capture()); UserUpdater userUpdater = userUpdaterArgumentCaptor.getValue(); assertThat(userUpdater.getFields().get(UserUpdater.UserField.ICON_FILENAME)).isEqualTo("iconName"); assertThat(userUpdater.getFields().get(UserUpdater.UserField.ICON_CONTENT)).isEqualTo("content".getBytes()); } @Test public void testSearchUsersWhoCanPerformTask_with_should_return_nothing() { when(processAPI.searchUsersWhoCanExecutePendingHumanTask(eq(0L), any(SearchOptions.class))) .thenReturn(mock(SearchResult.class)); ItemSearchResult results = datastore.searchUsersWhoCanPerformTask("0", 0, 10, "jan", Collections.EMPTY_MAP, ""); verify(processAPI, times(1)).searchUsersWhoCanExecutePendingHumanTask(anyLong(), any(SearchOptions.class)); assertThat(results.getLength()).isEqualTo(10); assertThat(results.getPage()).isEqualTo(0); assertThat(results.getTotal()).isEqualTo(0); assertThat(results.getResults()).isEmpty(); } @Test public void testSearchUsersWhoCanPerformTask_with_should_return_one_result() { @SuppressWarnings("rawtypes") SearchResult engineSearchResults = mock(SearchResult.class); long expected = 1; when(engineSearchResults.getCount()).thenReturn(expected); User user = mock(User.class); List userList = Collections.singletonList(user); when(engineSearchResults.getResult()).thenReturn(userList); when(processAPI.searchUsersWhoCanExecutePendingHumanTask(eq(18L), any(SearchOptions.class))) .thenReturn(engineSearchResults); UserItem userItem = mock(UserItem.class); List userItemList = Collections.singletonList(userItem); when(userItemConverter.convert(userList)).thenReturn(userItemList); int page = 1; int resultsByPage = 8; ItemSearchResult results = datastore.searchUsersWhoCanPerformTask("18", page, resultsByPage, "jan", Collections.EMPTY_MAP, ""); assertThat(results.getLength()).isEqualTo(resultsByPage); assertThat(results.getPage()).isEqualTo(page); assertThat(results.getTotal()).isEqualTo(expected); assertThat(results.getResults()).isNotEmpty().hasSize(1).containsExactly(userItem); verify(processAPI, times(1)).searchUsersWhoCanExecutePendingHumanTask(anyLong(), any(SearchOptions.class)); verify(userItemConverter, times(1)).convert(userList); } @Test public void buildSearchOptionCreator_should_convert_enabled_attribute_to_boolean() { final List filters = datastore.buildSearchOptionCreator(0, 10, "", Collections.singletonMap(UserItem.ATTRIBUTE_ENABLED, "true"), "displayName ASC").create().getFilters(); assertThat(filters.get(0).getValue()).isEqualTo(true); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/organization/UserSearchAttributeConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.organization; import static junit.framework.Assert.assertEquals; import org.bonitasoft.engine.identity.UserSearchDescriptor; import org.bonitasoft.web.rest.model.identity.UserItem; import org.junit.Before; import org.junit.Test; /** * @author Colin PUY */ public class UserSearchAttributeConverterTest { private UserSearchAttributeConverter converter; @Before public void initConverter() { converter = new UserSearchAttributeConverter(); } @Test public void convertFistName() throws Exception { String convert = converter.convert(UserItem.ATTRIBUTE_FIRSTNAME); assertEquals(UserSearchDescriptor.FIRST_NAME, convert); } @Test public void convertLastName() throws Exception { String convert = converter.convert(UserItem.ATTRIBUTE_LASTNAME); assertEquals(UserSearchDescriptor.LAST_NAME, convert); } @Test public void convertUserName() throws Exception { String convert = converter.convert(UserItem.ATTRIBUTE_USERNAME); assertEquals(UserSearchDescriptor.USER_NAME, convert); } @Test public void convertGroupId() throws Exception { String convert = converter.convert(UserItem.FILTER_GROUP_ID); assertEquals(UserSearchDescriptor.GROUP_ID, convert); } @Test public void convertManagerId() throws Exception { String convert = converter.convert(UserItem.ATTRIBUTE_MANAGER_ID); assertEquals(UserSearchDescriptor.MANAGER_USER_ID, convert); } @Test public void convertRoleId() throws Exception { String convert = converter.convert(UserItem.FILTER_ROLE_ID); assertEquals(UserSearchDescriptor.ROLE_ID, convert); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/page/CustomPageContentValidatorTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.page; import java.io.File; import org.bonitasoft.console.common.server.utils.UnzipUtil; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class CustomPageContentValidatorTest { private CustomPageContentValidator customPageContentValidator; @Rule public ExpectedException expectedException = ExpectedException.none(); @Before public void setUp() throws Exception { customPageContentValidator = new CustomPageContentValidator(); } @Test public void zip_with_index_in_resources_should_be_valid() throws Exception { final File zipFileResource = new File(getClass().getResource("/pageWithIndexInResources.zip").toURI()); UnzipUtil.unzip(zipFileResource, new File("target" + File.separator + "pageWithIndexInResources").getPath(), false); final File unzipFolder = new File("target" + File.separator + "pageWithIndexInResources"); customPageContentValidator.validate(unzipFolder); } @Test public void should_throw_exception_when_theme_css_not_found() throws Exception { final File invalidThemePage = new File(getClass().getResource("/invalidThemePage").toURI()); expectedException.expect(InvalidPageZipContentException.class); expectedException.expectMessage("theme.css is missing."); customPageContentValidator.validate(invalidThemePage); } @Test public void should_throw_exception_when_page_properties_not_found() throws Exception { final File invalidCustomPage = new File(getClass().getResource("/invalidCustomPage").toURI()); expectedException.expect(InvalidPageZipContentException.class); expectedException.expectMessage("page.properties descriptor is missing."); customPageContentValidator.validate(invalidCustomPage); } @Test public void should_throw_exception_when_index_not_found() throws Exception { final File invalidFormPage = new File(getClass().getResource("/invalidFormPage").toURI()); expectedException.expect(InvalidPageZipContentException.class); expectedException.expectMessage("index.html or Index.groovy is missing."); customPageContentValidator.validate(invalidFormPage); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/page/PageDatastoreTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.page; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import static org.bonitasoft.web.toolkit.client.data.APIID.makeAPIID; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.net.URISyntaxException; import java.net.URL; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; import org.assertj.core.groups.Tuple; import org.bonitasoft.console.common.server.page.CustomPageService; import org.bonitasoft.console.common.server.page.extension.PageResourceProviderImpl; import org.bonitasoft.console.common.server.preferences.constants.WebBonitaConstantsUtils; import org.bonitasoft.console.common.server.servlet.FileUploadServlet; import org.bonitasoft.console.common.server.utils.BonitaHomeFolderAccessor; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.page.*; import org.bonitasoft.engine.page.PageCreator.PageField; import org.bonitasoft.engine.search.SearchFilterOperation; import org.bonitasoft.engine.search.impl.SearchFilter; import org.bonitasoft.engine.session.APISession; import org.bonitasoft.web.extension.page.PageResourceProvider; import org.bonitasoft.web.rest.model.portal.page.PageItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; import org.bonitasoft.web.rest.server.datastore.utils.Sorts; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Before; 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.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Fabio Lombardi */ @RunWith(MockitoJUnitRunner.class) public class PageDatastoreTest extends APITestWithMock { public static final long TENANT_ID = 1L; public static final long PAGE_ID = 123L; public static final String PAGE_ZIP = "page.zip"; public static final String PAGE_REST_API_ZIP = "pageApiExtension.zip"; private PageDatastore pageDatastore; @Mock APISession engineSession; @Mock PageAPI pageAPI; @Mock PageItem pageItem; @Mock WebBonitaConstantsUtils constantsValue; @Mock Page mockedPage; @Mock Page mockedApiExtension; @Mock BonitaHomeFolderAccessor tenantFolder; @Mock CustomPageService customPageService; @Mock private File mockedZipFile; @Rule public ExpectedException expectedEx = ExpectedException.none(); PageItem pageToBeAdded; PageItem apiExtensionToBeAdded; File pagesDir = new File("target/bonita-home/client/tenants/1/work/pages"); @Mock private PageResourceProviderImpl pageResourceProvider; @Before public void setUp() throws Exception { final Date mockedDate = new Date(0L); final File apiExtensionZipFile = deployZipFileToTarget(PAGE_REST_API_ZIP); final File pageZipFile = deployZipFileToTarget(PAGE_ZIP); deleteDir(pagesDir); // Given when(mockedPage.getId()).thenReturn(PAGE_ID); when(mockedPage.getName()).thenReturn("custompage_page1"); when(mockedPage.getDisplayName()).thenReturn("Page 1"); when(mockedPage.getDescription()).thenReturn("This is a page description"); when(mockedPage.getInstallationDate()).thenReturn(mockedDate); when(mockedPage.getLastModificationDate()).thenReturn(mockedDate); when(mockedPage.getInstalledBy()).thenReturn(1L); when(mockedPage.isProvided()).thenReturn(false); when(mockedApiExtension.getId()).thenReturn(PAGE_ID); when(mockedApiExtension.getName()).thenReturn("custompage_apiExt"); when(mockedApiExtension.getDisplayName()).thenReturn("Page 1"); when(mockedApiExtension.getDescription()).thenReturn("This is a page description"); when(mockedApiExtension.getInstallationDate()).thenReturn(mockedDate); when(mockedApiExtension.getLastModificationDate()).thenReturn(mockedDate); when(mockedApiExtension.getInstalledBy()).thenReturn(1L); when(tenantFolder.getTempFile(PAGE_ZIP)).thenReturn(pageZipFile); when(tenantFolder.getTempFile(PAGE_REST_API_ZIP)).thenReturn(pageZipFile); when(constantsValue.getTempFolder()).thenReturn(pageZipFile.getParentFile()); pageDatastore = spy(new PageDatastore(engineSession, constantsValue, pageAPI, customPageService, tenantFolder)); pageToBeAdded = new PageItem(); pageToBeAdded.setUrlToken("custompage_page1"); pageToBeAdded.setDisplayName("Page 1"); pageToBeAdded.setDescription("This is a page description"); pageToBeAdded.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, pageZipFile.getName()); apiExtensionToBeAdded = new PageItem(); apiExtensionToBeAdded.setUrlToken("custompage_apiExt"); apiExtensionToBeAdded.setDisplayName("Page 1"); apiExtensionToBeAdded.setDescription("This is a page description"); apiExtensionToBeAdded.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, apiExtensionZipFile.getName()); } private File deployZipFileToTarget(final String zipFileName) throws IOException, URISyntaxException { final File file = new File(getClass().getResource(zipFileName).toURI()); assertThat(file).as("file should exists " + file.getAbsolutePath()).exists(); FileUtils.copyFileToDirectory(file, new File("target")); return new File("target/" + zipFileName); } @Test public void should_add_a_page_return_valid_page() throws Exception { // Given deleteDir(pagesDir); when(pageAPI.createPage(any(String.class), any(byte[].class))).thenReturn(mockedPage); // When final PageItem addedPage = pageDatastore.add(pageToBeAdded); // Validate assertNotNull(addedPage); } @Test public void should_add_a_api_extension_add_related_permission() throws Exception { // given when(pageAPI.createPage(any(String.class), any(byte[].class))).thenReturn(mockedApiExtension); doReturn(pageResourceProvider).when(customPageService).getPageResourceProvider(eq(mockedApiExtension)); // when pageDatastore.add(apiExtensionToBeAdded); //then verify(customPageService).writePageToPageDirectory(any(Page.class), eq(pageResourceProvider), any(File.class), eq(engineSession)); } @Test(expected = APIException.class) public void should_add_a_not_valid_page_rise_exception() { // Given deleteDir(pagesDir); final URL zipFileUrl = getClass().getResource("/InvalidPage.zip"); pageToBeAdded.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, zipFileUrl.getPath()); // When final PageItem addedPage = pageDatastore.add(pageToBeAdded); // Validate assertNotNull(addedPage); } @Test(expected = APIException.class) public void should_get_a_not_existing_page_rise_exception() throws Exception { // Given deleteDir(pagesDir); when(pageAPI.getPage(1L)).thenThrow(new PageNotFoundException("newPage")); // When pageDatastore.get(makeAPIID(1L)); } @Test public void should_thrown_ApiException_and_not_remove_contentFile_when_delete_a_not_existing() throws Exception { // Given deleteDir(pagesDir); when(pageAPI.getPage(1L)).thenThrow(new PageNotFoundException("newPage")); // When final Throwable throwable = catchThrowable(() -> pageDatastore.delete(singletonList(makeAPIID(1L)))); assertThat(throwable).isInstanceOf(APIException.class); verify(pageAPI, never()).deletePage(1L); verify(customPageService, never()).removePageLocally(any(PageResourceProvider.class)); } @Test public void should_call_customPageService_for_custom_page_permissions_when_adding_a_page() throws Exception { // Given deleteDir(pagesDir); when(pageAPI.createPage(any(String.class), any(byte[].class))).thenReturn(mockedPage); // When pageDatastore.add(pageToBeAdded); // Then verify(customPageService).writePageToPageDirectory(any(Page.class), eq(null), any(File.class), eq(engineSession)); } @Test public void should_thrown_APIException_when_deletePage_thrown_DeletionException() throws Exception { // Given when(pageAPI.getPage(PAGE_ID)).thenReturn(mockedPage); doThrow(new DeletionException("")).when(pageAPI).deletePage(PAGE_ID); // When final Throwable throwable = catchThrowable(() -> pageDatastore.delete(singletonList(APIID.makeAPIID(PAGE_ID)))); assertThat(throwable).isInstanceOf(APIException.class); verify(customPageService).ensurePageFolderIsUpToDate(any(), any()); verify(customPageService, never()).removePageLocally(any(PageResourceProvider.class)); } @Test public void should_deletePage_and_remove_all_dependencies_when_deleting_page() throws Exception { // Given when(pageAPI.getPage(PAGE_ID)).thenReturn(mockedApiExtension); doReturn(pageResourceProvider).when(customPageService).getPageResourceProvider(eq(mockedApiExtension)); // When pageDatastore.delete(singletonList(makeAPIID(PAGE_ID))); // then verify(pageAPI).deletePage(mockedApiExtension.getId()); verify(customPageService).removePageLocally(pageResourceProvider); verify(customPageService).ensurePageFolderIsUpToDate(any(), any()); } @Test public void should_update_resource_permission_when_updating_api_extension() throws Exception { // Given when(pageAPI.getPage(mockedApiExtension.getId())).thenReturn(mockedApiExtension); when(pageAPI.updatePage(eq(mockedApiExtension.getId()), any(PageUpdater.class))).thenReturn(mockedApiExtension); doReturn(pageResourceProvider).when(customPageService).getPageResourceProvider(any(Page.class)); final File apiExtensionZipFile = deployZipFileToTarget(PAGE_REST_API_ZIP); doReturn(apiExtensionZipFile).when(tenantFolder).getTempFile(eq(apiExtensionZipFile.getAbsolutePath())); // When final Map attributes = new HashMap<>(); attributes.put(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, apiExtensionZipFile.getAbsolutePath() + FileUploadServlet.RESPONSE_SEPARATOR + apiExtensionZipFile.getName()); pageDatastore.update(makeAPIID(mockedApiExtension.getId()), attributes); // then verify(pageAPI).updatePage(eq(PAGE_ID), any()); verify(customPageService).ensurePageFolderIsUpToDate(any(), any()); verify(customPageService).removePageLocally(any(Page.class)); verify(customPageService).writePageToPageDirectory(any(Page.class), eq(pageResourceProvider), any(File.class), eq(engineSession)); } @Test public void should_not_update_resource_and_permission_pageContent_when_pageApi_updatePageContent_thown_exception() throws Exception { // Given when(pageAPI.getPage(mockedApiExtension.getId())).thenReturn(mockedApiExtension); doReturn(pageResourceProvider).when(customPageService).getPageResourceProvider(any(Page.class)); doThrow(new UpdateException("")).when(pageAPI).updatePageContent(eq(PAGE_ID), any()); final File apiExtensionZipFile = deployZipFileToTarget(PAGE_REST_API_ZIP); doReturn(apiExtensionZipFile).when(tenantFolder).getTempFile(eq(apiExtensionZipFile.getAbsolutePath())); // When final Map attributes = new HashMap<>(); attributes.put(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, apiExtensionZipFile.getAbsolutePath() + FileUploadServlet.RESPONSE_SEPARATOR + apiExtensionZipFile.getName()); Throwable throwable = catchThrowable( () -> pageDatastore.update(makeAPIID(mockedApiExtension.getId()), attributes)); // then assertThat(throwable).isInstanceOf(APIException.class); verify(customPageService).ensurePageFolderIsUpToDate(any(), any()); verify(customPageService, never()).removePageLocally(any(Page.class)); verify(customPageService).writePageToPageDirectory(any(Page.class), eq(pageResourceProvider), any(File.class), eq(engineSession)); } @Test public void it_throws_an_exception_when_cannot_write_file_on_add() throws IOException { // Given pageToBeAdded.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, "error_page.zip"); doThrow(new IOException("error")).when(tenantFolder).getTempFile("error_page.zip"); // When try { pageDatastore.add(pageToBeAdded); } catch (final APIException e) { assertEquals(e.getMessage(), "java.io.IOException: error"); } } @Test public void it_throws_an_exception_when_cannot_write_file_on_update() throws IOException, PageNotFoundException { // Given final Map attributes = new HashMap<>(); attributes.put(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, "error_page.zip"); pageToBeAdded.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, "error_page.zip"); doReturn(mockedPage).when(pageAPI).getPage(TENANT_ID); doThrow(new IOException("error")).when(tenantFolder).getTempFile("error_page.zip"); // When final Throwable throwable = catchThrowable(() -> pageDatastore.update(APIID.makeAPIID(TENANT_ID), attributes)); assertThat(throwable).isInstanceOf(APIException.class).hasMessage("java.io.IOException: error"); } private void deleteDir(final File pagesDir) { if (pagesDir.exists()) { try { IOUtil.deleteDir(pagesDir); } catch (final IOException e) { e.printStackTrace(); } } } @Test public void it_should_set_the_process_definition_id_on_creation() throws Exception { final byte[] zipContent = new byte[1]; final APIID processId = APIID.makeAPIID(2555L); doReturn("contentName").when(pageItem).getContentName(); doReturn(null).when(pageItem).getContentType(); doReturn(processId).when(pageItem).getProcessId(); doReturn(zipContent).when(pageDatastore).readZipFile(mockedZipFile); doReturn(mockedPage).when(pageAPI).createPage(anyString(), any(byte[].class)); pageDatastore.createEnginePage(pageItem, mockedZipFile); final PageUpdater pageUpdater = new PageUpdater(); pageUpdater.setProcessDefinitionId(processId.toLong()); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(PageUpdater.class); verify(pageAPI, times(1)).updatePage(eq(mockedPage.getId()), argumentCaptor.capture()); assertThat(argumentCaptor.getValue()).isEqualToComparingFieldByField(pageUpdater); } @Test public void it_should_not_set_the_process_definition_id_on_creation() throws Exception { final byte[] zipContent = new byte[1]; doReturn("contentName").when(pageItem).getContentName(); doReturn(null).when(pageItem).getProcessId(); doReturn(zipContent).when(pageDatastore).readZipFile(mockedZipFile); doReturn(mockedPage).when(pageAPI).createPage(anyString(), any(byte[].class)); pageDatastore.createEnginePage(pageItem, mockedZipFile); verify(pageAPI, times(0)).updatePage(anyLong(), any(PageUpdater.class)); } @Test public void it_should_set_the_content_type_on_creation_if_it_is_given() throws Exception { final byte[] zipContent = new byte[1]; final APIID processId = APIID.makeAPIID(2555L); doReturn("contentName").when(pageItem).getContentName(); doReturn(ContentType.PAGE).when(pageItem).getContentType(); doReturn(processId).when(pageItem).getProcessId(); doReturn(zipContent).when(pageDatastore).readZipFile(mockedZipFile); doReturn(mockedPage).when(pageAPI).createPage(anyString(), any(byte[].class)); pageDatastore.createEnginePage(pageItem, mockedZipFile); final PageUpdater pageUpdater = new PageUpdater(); pageUpdater.setContentType(ContentType.PAGE); pageUpdater.setProcessDefinitionId(processId.toLong()); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(PageUpdater.class); verify(pageAPI, times(1)).updatePage(eq(mockedPage.getId()), argumentCaptor.capture()); assertThat(argumentCaptor.getValue()).isEqualToComparingFieldByField(pageUpdater); } @Test public void it_should_set_the_new_zip_name_value_to_original_file_name_field_on_creation() throws Exception { // Given deleteDir(pagesDir); pageToBeAdded.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, "page.zip::newPage.zip"); when(pageAPI.createPage(any(String.class), any(byte[].class))).thenReturn(mockedPage); // When pageDatastore.add(pageToBeAdded); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(PageItem.class); //then verify(pageDatastore, times(1)).createEnginePage(argumentCaptor.capture(), any(File.class)); assertThat(argumentCaptor.getValue().getContentName()).isEqualTo("newPage.zip"); } @Test public void it_should_set_the_new_zip_name_value_to_original_file_name_field_on_creation_with_contentName_attribute() throws Exception { // Given deleteDir(pagesDir); pageToBeAdded.setAttribute(PageDatastore.UNMAPPED_ATTRIBUTE_ZIP_FILE, "page.zip"); pageToBeAdded.setAttribute(PageItem.ATTRIBUTE_CONTENT_NAME, "newPage.zip"); when(pageAPI.createPage(any(String.class), any(byte[].class))).thenReturn(mockedPage); // When pageDatastore.add(pageToBeAdded); final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(PageItem.class); //then verify(pageDatastore, times(1)).createEnginePage(argumentCaptor.capture(), any(File.class)); assertThat(argumentCaptor.getValue().getContentName()).isEqualTo("newPage.zip"); } @Test public void testBuildPageCreatorFrom() { final String testUrlTokenString = "testUrlTokenString"; final String testContentNameString = "testContentName"; final String testDescriptionString = "testDescriptionString"; final String testDisplayNameString = "testDescriptionString"; doReturn(testUrlTokenString).when(pageItem).getUrlToken(); doReturn(testContentNameString).when(pageItem).getContentName(); doReturn(testDescriptionString).when(pageItem).getDescription(); doReturn(testDisplayNameString).when(pageItem).getDisplayName(); final PageCreator pageCreator = pageDatastore.buildPageCreatorFrom(pageItem); final Map expectedFields = new HashMap<>(); expectedFields.put(PageField.NAME, testUrlTokenString); expectedFields.put(PageField.CONTENT_NAME, testContentNameString); expectedFields.put(PageField.CONTENT_TYPE, ContentType.PAGE); expectedFields.put(PageField.DESCRIPTION, testDescriptionString); expectedFields.put(PageField.DISPLAY_NAME, testDisplayNameString); assertThat(pageCreator.getFields()).isEqualTo(expectedFields); } @Test public void should_IsPageTokenValid_returns_true_when_url_token_starts_with_custom_page_prefix() { assertThat(pageDatastore.isPageTokenValid(PageDatastore.PAGE_TOKEN_PREFIX + "anySuffix123456")).isTrue(); } @Test public void should_IsPageTokenValid_returns_false_when_url_token_contains_non_alphanumeric_values() { assertThat(pageDatastore.isPageTokenValid(PageDatastore.PAGE_TOKEN_PREFIX + "suffixxx_dsqds")).isFalse(); } @Test public void should_IsPageTokenValid_returns_false_when_url_token_does_not_starts_with_custom_page_prefix() { assertThat(pageDatastore.isPageTokenValid("WrongStartString" + PageDatastore.PAGE_TOKEN_PREFIX + "suffixx")) .isFalse(); } @Test public void should_ConvertEngineToConsoleItem_returns_null_if_item_is_null() { assertThat(pageDatastore.convertEngineToConsoleItem(null)).isNull(); } @Test public void testAPIIdsToLong() { final APIID id1 = makeAPIID("1"); final APIID id2 = makeAPIID("2"); final List listId = Arrays.asList(id1, id2); assertThat(pageDatastore.APIIdsToLong(listId)).isEqualTo(Arrays.asList(1L, 2L)); } @Test public void testRunSearch() throws Exception { final SearchOptionsCreator creator = new SearchOptionsCreator(0, 1, null, new Sorts(null), new Filters(null)); pageDatastore.runSearch(creator); verify(pageAPI, times(1)).searchPages(creator.create()); } @Test public void should_Get_returns_a_page_item_for_a_given_APIID() throws Exception { final APIID apiid = makeAPIID(2L); doReturn(mockedPage).when(pageAPI).getPage(apiid.toLong()); pageDatastore.get(apiid); verify(pageAPI, times(1)).getPage(apiid.toLong()); verify(pageDatastore, times(1)).convertEngineToConsoleItem(mockedPage); } @Test public void should_ConvertEngineToConsoleItem_return_null_when_null_page_is_given() { //when final PageItem testPageItem = pageDatastore.convertEngineToConsoleItem(null); //then assertThat(testPageItem).isNull(); } @Test public void should_ConvertEngineToConsoleItem_return_a_page_item_when_page_is_given() { //when final PageItem testPageItem = pageDatastore.convertEngineToConsoleItem(mockedPage); //then assertThat(testPageItem).isNotNull(); } @Test public void should_DeleteTempDirectory_throw_API_Exception_when_an_exception_is_raised() throws Exception { //given when(mockedZipFile.isDirectory()).thenReturn(true); doThrow(new IOException("to be test")).when(pageDatastore).IOUtilDeleteDir(mockedZipFile); //when try { pageDatastore.deleteTempDirectory(mockedZipFile); } catch (final Exception e) { assertThat(e).isInstanceOf(APIException.class); } } @Test public void makeSearchOptionCreator_converts_isProvided_field_to_boolean() { final List filters = pageDatastore.makeSearchOptionCreator(0, 10, "", "displayName ASC", Collections.singletonMap(PageItem.ATTRIBUTE_IS_PROVIDED, "false")).create().getFilters(); assertThat(filters.get(0).getValue()).isEqualTo(false); } @Test public void makeSearchOptionCreator_with_empty_filter_map_should_return_empty_filter_list() { final SearchOptionsCreator searchOptionsCreator = pageDatastore.makeSearchOptionCreator(0, 10, "", "displayName ASC", new HashMap<>()); final List filters = searchOptionsCreator.create().getFilters(); assertThat(filters).isEmpty(); } @Test public void makeSearchOptionCreator_with_ATTRIBUTE_PROCESS_ID_filter_map_should_return_ATTRIBUTE_PROCESS_ID_in_filter_list() { final Map filters = new HashMap<>(); final String processID = "2124654"; filters.put(PageItem.ATTRIBUTE_PROCESS_ID, processID); final SearchOptionsCreator searchOptionsCreator = pageDatastore.makeSearchOptionCreator(0, 10, "", "displayName ASC", filters); final List filtersResult = searchOptionsCreator.create().getFilters(); assertThat(filtersResult).extracting("field", "operation", "value") .contains(new Tuple("processDefinitionId", SearchFilterOperation.EQUALS, processID)); } @Test public void makeSearchOptionCreator_with_FILTER_CONTENT_TYPE_form_filter_map_should_return_FILTER_CONTENT_TYPE_in_filter_list() { final Map filters = new HashMap<>(); final String form = "form"; filters.put(PageItem.FILTER_CONTENT_TYPE, form); final SearchOptionsCreator searchOptionsCreator = pageDatastore.makeSearchOptionCreator(0, 10, "", "displayName ASC", filters); final List filtersResult = searchOptionsCreator.create().getFilters(); assertThat(filtersResult).extracting("field", "operation", "value") .contains(new Tuple("contentType", SearchFilterOperation.EQUALS, form)); } @Test public void makeSearchOptionCreator_with_FILTER_CONTENT_TYPE_processPage_filter_map_should_return_FILTER_CONTENT_TYPE_form_or_page_in_filter_list() { final Map filters = new HashMap<>(); final String form = "processPage"; filters.put(PageItem.FILTER_CONTENT_TYPE, form); final SearchOptionsCreator searchOptionsCreator = pageDatastore.makeSearchOptionCreator(0, 10, "", "displayName ASC", filters); final List filtersResult = searchOptionsCreator.create().getFilters(); assertThat(filtersResult).extracting("field", "operation", "value").contains( new Tuple(null, SearchFilterOperation.L_PARENTHESIS, null), new Tuple("contentType", SearchFilterOperation.EQUALS, "form"), new Tuple(null, SearchFilterOperation.OR, null), new Tuple("contentType", SearchFilterOperation.EQUALS, "page"), new Tuple(null, SearchFilterOperation.R_PARENTHESIS, null)); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/page/PageItemConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.page; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Date; import org.bonitasoft.engine.page.ContentType; import org.bonitasoft.engine.page.Page; import org.bonitasoft.web.rest.model.ModelFactory; import org.bonitasoft.web.rest.model.portal.page.PageItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.BonitaRestAPIFactory; import org.bonitasoft.web.rest.server.framework.RestAPIFactory; import org.bonitasoft.web.toolkit.client.ItemDefinitionFactory; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class PageItemConverterTest extends APITestWithMock { @BeforeClass public static void initEnvironnement() { RestAPIFactory.setDefaultFactory(new BonitaRestAPIFactory()); ItemDefinitionFactory.setDefaultFactory(new ModelFactory()); } @Test public void should_convert_engineItem_Page_to_portalItem_PageItem() { //Given final PageItemConverter pageitemConverter = new PageItemConverter(); final Page engineItem = mock(Page.class); when(engineItem.getId()).thenReturn(1l); when(engineItem.getName()).thenReturn("page1"); when(engineItem.getDisplayName()).thenReturn("Page 1"); when(engineItem.isProvided()).thenReturn(false); when(engineItem.getDescription()).thenReturn("This is a page description"); when(engineItem.getInstalledBy()).thenReturn(1l); when(engineItem.getInstallationDate()).thenReturn(new Date(1)); when(engineItem.getLastModificationDate()).thenReturn(new Date(1)); when(engineItem.getLastUpdatedBy()).thenReturn(1l); when(engineItem.getContentName()).thenReturn("page1.zip"); when(engineItem.getProcessDefinitionId()).thenReturn(2L); when(engineItem.getContentType()).thenReturn(ContentType.FORM); when(engineItem.isEditable()).thenReturn(true); when(engineItem.isRemovable()).thenReturn(false); //When final PageItem pageItem = pageitemConverter.convert(engineItem); //Assert assertTrue(pageItem.getId().equals(engineItem.getId())); assertTrue(pageItem.getUrlToken().equals(engineItem.getName())); assertTrue(pageItem.getDisplayName().equals(engineItem.getDisplayName())); assertTrue(pageItem.isProvided() == engineItem.isProvided()); assertTrue(pageItem.getDescription().equals(engineItem.getDescription())); assertTrue(pageItem.getCreatedByUserId().equals(engineItem.getInstalledBy())); assertTrue(pageItem.getCreationDate().equals(engineItem.getInstallationDate())); assertTrue(pageItem.getLastUpdateDate().equals(engineItem.getLastModificationDate())); assertTrue(pageItem.getUpdatedByUserId().equals(engineItem.getLastUpdatedBy())); assertTrue(pageItem.getContentName().equals(engineItem.getContentName())); assertTrue(pageItem.getProcessId().equals(engineItem.getProcessDefinitionId())); assertTrue(pageItem.getContentType().equals(engineItem.getContentType())); assertTrue(pageItem.isEditable() == engineItem.isEditable()); assertTrue(pageItem.isRemovable() == engineItem.isRemovable()); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/EngineProfileBuilder.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.profile.impl.ProfileImpl; /** * @author Vincent Elcrin */ public class EngineProfileBuilder { private String name; private String description; public static EngineProfileBuilder anEngineProfile() { return new EngineProfileBuilder(); } public EngineProfileBuilder withName(final String name) { this.name = name; return this; } public EngineProfileBuilder withDescription(final String description) { this.description = description; return this; } public Profile build() { final ProfileImpl profile = new ProfileImpl(name); profile.setDescription(description); return profile; } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/GetProfileHelperTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile; import static junit.framework.Assert.assertTrue; import static org.bonitasoft.web.rest.model.builder.profile.ProfileItemBuilder.aProfileItem; import static org.bonitasoft.web.rest.server.datastore.profile.EngineProfileBuilder.anEngineProfile; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.web.rest.model.portal.profile.ProfileItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.engineclient.ProfileEngineClient; import org.bonitasoft.web.toolkit.client.data.APIID; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; /** * @author Vincent Elcrin */ public class GetProfileHelperTest extends APITestWithMock { @Mock ProfileEngineClient profileClient; GetProfileHelper getProfileHelper; @Before public void setUp() { initMocks(this); getProfileHelper = new GetProfileHelper(profileClient); } @Test public void testWeCanRetrieveAProfile() throws Exception { final Profile aKnownProfile = anEngineProfile().withName("aName").withDescription("aDescription").build(); when(profileClient.getProfile(1L)).thenReturn(aKnownProfile); final ProfileItem item = getProfileHelper.get(APIID.makeAPIID(1L)); assertTrue(areEquals(aProfileItem().fromEngineItem(aKnownProfile) .withIcon(ProfileItemConverter.DEFAULT_PROFILE_ICONPATH).build(), item)); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/ProfileItemConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile; import static junit.framework.Assert.assertTrue; import static org.bonitasoft.web.rest.model.builder.profile.ProfileItemBuilder.aProfileItem; import static org.bonitasoft.web.rest.server.datastore.profile.EngineProfileBuilder.anEngineProfile; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.web.rest.model.portal.profile.ProfileItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.junit.Test; /** * @author Vincent Elcrin */ public class ProfileItemConverterTest extends APITestWithMock { @Test public void testProfileItemConvertion() { final Profile profile = anEngineProfile().withName("aName").withDescription("aDescription").build(); final ProfileItem item = new ProfileItemConverter().convert(profile); assertTrue(areEquals( aProfileItem().fromEngineItem(profile).withIcon(ProfileItemConverter.DEFAULT_PROFILE_ICONPATH).build(), item)); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/ProfileSearchDescriptorConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile; import static org.junit.Assert.assertEquals; import org.bonitasoft.engine.profile.ProfileSearchDescriptor; import org.bonitasoft.web.rest.model.portal.profile.ProfileItem; import org.junit.Test; /** * @author Vincent Elcrin */ public class ProfileSearchDescriptorConverterTest { @Test public void testWeCanConvertTheProfileId() { String descriptor = new ProfileSearchDescriptorConverter().convert(ProfileItem.ATTRIBUTE_ID); assertEquals(ProfileSearchDescriptor.ID, descriptor); } @Test public void testWeCanConvertTheProfileName() { String descriptor = new ProfileSearchDescriptorConverter().convert(ProfileItem.ATTRIBUTE_NAME); assertEquals(ProfileSearchDescriptor.NAME, descriptor); } @Test(expected = RuntimeException.class) public void testTryingToConvertNonExistingDescriptorThrowAnException() { new ProfileSearchDescriptorConverter().convert("someUnsupportedAttribute"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/SearchProfilesHelperTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.web.rest.server.datastore.profile.EngineProfileBuilder.anEngineProfile; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import java.util.HashMap; import java.util.List; import org.bonitasoft.engine.api.ProfileAPI; import org.bonitasoft.engine.profile.Profile; import org.bonitasoft.engine.profile.ProfileCriterion; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.impl.SearchResultImpl; import org.bonitasoft.web.rest.model.portal.profile.ProfileItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.datastore.utils.SearchUtils; import org.bonitasoft.web.rest.server.engineclient.ProfileEngineClient; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; /** * @author Vincent Elcrin */ public class SearchProfilesHelperTest extends APITestWithMock { @Mock ProfileAPI profileAPI; SearchProfilesHelper searchProfilesHelper; @Before public void setUp() { initMocks(this); searchProfilesHelper = new SearchProfilesHelper(new ProfileEngineClient(profileAPI)); } @Test public void testWeCanSearchProfiles() throws Exception { final SearchResultImpl aKnownSearchResult = aKnownSearchResult(); final List expectedProfiles = new ProfileItemConverter().convert(aKnownSearchResult.getResult()); when(profileAPI.searchProfiles(any(SearchOptions.class))).thenReturn(aKnownSearchResult); final ItemSearchResult searchResult = searchProfilesHelper.search(0, 10, null, null, null); assertThat(SearchUtils.areEquals(expectedProfiles, searchResult.getResults())).isTrue(); } @Test public void testWeCanListUserProfiles() { final SearchResultImpl aKnownSearchResult = aKnownSearchResult(); final List expectedProfiles = new ProfileItemConverter().convert(aKnownSearchResult.getResult()); when(profileAPI.getProfilesForUser(2L, 0, Integer.MAX_VALUE, ProfileCriterion.ID_ASC)) .thenReturn(aKnownSearchResult.getResult()); final ItemSearchResult searchResult = searchProfilesHelper.search(0, 10, null, null, filterOnUserId(2L)); verify(profileAPI).getProfilesForUser(2L, 0, Integer.MAX_VALUE, ProfileCriterion.ID_ASC); assertThat(SearchUtils.areEquals(expectedProfiles, searchResult.getResults())).isTrue(); } private HashMap filterOnUserId(final long id) { final HashMap filters = new HashMap<>(); filters.put(ProfileItem.FILTER_USER_ID, String.valueOf(id)); return filters; } private SearchResultImpl aKnownSearchResult() { final Profile aKnownProfile = anEngineProfile().withName("aName").withDescription("aDescription").build(); final Profile anotherKnownProfile = anEngineProfile().withName("anotherName") .withDescription("anotherDescription").build(); return SearchUtils.createEngineSearchResult(aKnownProfile, anotherKnownProfile); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/member/AddProfileMemberHelperTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile.member; import static junit.framework.Assert.assertTrue; import static org.bonitasoft.web.rest.model.builder.profile.member.EngineProfileMemberBuilder.anEngineProfileMember; import static org.bonitasoft.web.rest.model.builder.profile.member.ProfileMemberItemBuilder.aProfileMemberItem; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import org.bonitasoft.engine.profile.ProfileMember; import org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.engineclient.ProfileMemberEngineClient; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; /** * @author Vincent Elcrin */ public class AddProfileMemberHelperTest extends APITestWithMock { @Mock ProfileMemberEngineClient engineClient; AddProfileMemberHelper addProfileHelper; @Before public void setUp() { initMocks(this); addProfileHelper = new AddProfileMemberHelper(engineClient); } @Test public void testWeCanCreateAMembershipForAUSer() { ProfileMember aKnownProfile = anEngineProfileMember().build(); when(engineClient.createProfileMember(1L, 2L, null, null)).thenReturn(aKnownProfile); ProfileMemberItem item = aProfileMemberItem().withProfileId(1L).withUserId(2L).withGroupId(null) .withRoleId(null).build(); ProfileMemberItem newItem = addProfileHelper.add(item); assertTrue(areEquals(aProfileMemberItem().from(aKnownProfile).build(), newItem)); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/member/MemberTypeConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile.member; import static junit.framework.Assert.assertEquals; import org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem; import org.junit.Test; /** * @author Vincent Elcrin */ public class MemberTypeConverterTest { @Test public void testMemberTypeConversion() throws Exception { MemberTypeConverter converter = new MemberTypeConverter(); MemberType value = converter.convert(AbstractMemberItem.VALUE_MEMBER_TYPE_ROLE); assertEquals(MemberType.ROLE, value); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/member/MemberTypeTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile.member; import static junit.framework.Assert.assertEquals; import org.bonitasoft.web.rest.model.portal.profile.AbstractMemberItem; import org.junit.Test; /** * @author Vincent Elcrin */ public class MemberTypeTest { @Test public void testWeCanRetrieveMemberTypeFromAString() throws Exception { MemberType type = MemberType.from(AbstractMemberItem.VALUE_MEMBER_TYPE_USER); assertEquals(MemberType.USER, type); } @Test(expected = IllegalArgumentException.class) public void testNotExistingConstConversionThrowsException() throws Exception { MemberType type = MemberType.from("notExistingConst"); assertEquals(MemberType.USER, type); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/member/ProfileMemberItemConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile.member; import static junit.framework.Assert.assertTrue; import static org.bonitasoft.web.rest.model.builder.profile.member.EngineProfileMemberBuilder.anEngineProfileMember; import static org.bonitasoft.web.rest.model.builder.profile.member.ProfileMemberItemBuilder.aProfileMemberItem; import org.bonitasoft.engine.profile.ProfileMember; import org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.junit.Test; /** * @author Vincent Elcrin */ public class ProfileMemberItemConverterTest extends APITestWithMock { @Test public void testProfileMemberItemConversion() { ProfileMemberItemConverter converter = new ProfileMemberItemConverter(); ProfileMember profileMember = anEngineProfileMember().build(); ProfileMemberItem item = converter.convert(profileMember); assertTrue(areEquals(aProfileMemberItem().from(profileMember).build(), item)); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/member/ProfileMemberSearchDescriptorConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile.member; import static junit.framework.Assert.assertTrue; import org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor; import org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem; import org.junit.Test; /** * @author Vincent Elcrin */ public class ProfileMemberSearchDescriptorConverterTest { @Test public void testWeCanConvertProfileMemberId() { assertTrue(testConversion(ProfileMemberSearchDescriptor.ID, ProfileMemberItem.ATTRIBUTE_ID)); } @Test public void testWeCanConvertProfileId() { assertTrue(testConversion(ProfileMemberSearchDescriptor.PROFILE_ID, ProfileMemberItem.ATTRIBUTE_PROFILE_ID)); } @Test public void testWeCanConvertUserId() { assertTrue(testConversion(ProfileMemberSearchDescriptor.USER_ID, ProfileMemberItem.ATTRIBUTE_USER_ID)); } @Test public void testWeCanConvertGroupId() { assertTrue(testConversion(ProfileMemberSearchDescriptor.GROUP_ID, ProfileMemberItem.ATTRIBUTE_GROUP_ID)); } @Test public void testWeCanConvertRoleId() { assertTrue(testConversion(ProfileMemberSearchDescriptor.ROLE_ID, ProfileMemberItem.ATTRIBUTE_ROLE_ID)); } public boolean testConversion(String expected, String actual) { ProfileMemberSearchDescriptorConverter converter = new ProfileMemberSearchDescriptorConverter(); String descriptor = converter.convert(actual); return expected.equals(descriptor); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/profile/member/SearchProfileMembersHelperTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.profile.member; import static junit.framework.Assert.assertTrue; import static org.bonitasoft.web.rest.model.builder.profile.member.EngineProfileMemberBuilder.anEngineProfileMember; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import java.util.Collections; import java.util.HashMap; import java.util.List; import org.bonitasoft.engine.profile.ProfileMember; import org.bonitasoft.engine.search.SearchOptions; import org.bonitasoft.engine.search.impl.SearchResultImpl; import org.bonitasoft.web.rest.model.portal.profile.ProfileMemberItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.datastore.utils.SearchUtils; import org.bonitasoft.web.rest.server.engineclient.ProfileMemberEngineClient; import org.bonitasoft.web.rest.server.framework.exception.APIFilterMandatoryException; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; /** * @author Vincent Elcrin */ public class SearchProfileMembersHelperTest extends APITestWithMock { @Mock ProfileMemberEngineClient engineClient; SearchProfileMembersHelper searchProfilesHelper; @Before public void setUp() { initMocks(this); searchProfilesHelper = new SearchProfileMembersHelper(engineClient); } @Test public void testWeCanSearchProfileMembers() throws Exception { SearchResultImpl aKnownSearchResult = aKnownSearchResult(); List expectedProfileMemberItems = new ProfileMemberItemConverter() .convert(aKnownSearchResult().getResult()); when(engineClient.searchProfileMembers(eq(MemberType.ROLE.getType()), any(SearchOptions.class))) .thenReturn(aKnownSearchResult); HashMap filters = filterOnProfileIdAndMemberType(5L, MemberType.ROLE); ItemSearchResult searchResult = searchProfilesHelper.search(0, 10, null, null, filters); assertTrue(SearchUtils.areEquals(expectedProfileMemberItems, searchResult.getResults())); } @Test(expected = APIFilterMandatoryException.class) public void testSearchWithoutMandatoryFiltersThrowError() { searchProfilesHelper.search(0, 10, null, null, Collections. emptyMap()); } private SearchResultImpl aKnownSearchResult() { return SearchUtils.createEngineSearchResult(aKnownProfile(), anotherKnownProfile()); } private HashMap filterOnProfileIdAndMemberType(long id, MemberType type) { HashMap filters = new HashMap<>(); filters.put(ProfileMemberItem.ATTRIBUTE_PROFILE_ID, String.valueOf(id)); filters.put(ProfileMemberItem.FILTER_MEMBER_TYPE, type.getType()); return filters; } private ProfileMember aKnownProfile() { return anEngineProfileMember().build(); } private ProfileMember anotherKnownProfile() { return anEngineProfileMember() .withId(2L) .withProfileId(3L) .withUserId(4L) .withGroupId(5L) .withRoleId(6l) .build(); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/utils/NotSerializableObject.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.utils; public class NotSerializableObject { } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/utils/SearchUtils.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.utils; import java.io.Serializable; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.search.impl.SearchResultImpl; import org.bonitasoft.web.toolkit.client.data.item.Item; /** * @author Vincent Elcrin */ public class SearchUtils { public static SearchResultImpl createEngineSearchResult(S... items) { return new SearchResultImpl<>(items.length, Arrays. asList(items)); } private static boolean areEquals(Item item1, Item item2) { return item1.getAttributes().equals(item2.getAttributes()); } public static boolean areEquals(List list1, List list2) { int i = 0; for (I item : list1) { if (!areEquals( item, list2.get(i++))) { return false; } } return true; } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/utils/SortTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.utils; import org.bonitasoft.engine.bpm.flownode.ActivityInstanceSearchDescriptor; import org.bonitasoft.engine.search.Order; import org.bonitasoft.web.rest.model.bpm.flownode.ActivityItem; import org.bonitasoft.web.rest.server.datastore.converter.ActivityAttributeConverter; import org.junit.Assert; import org.junit.Test; /** * @author Vincent Elcrin */ public class SortTest { @Test public void testAscSortCreation() { String order = "attribute " + Order.ASC; Sort sort = new Sort(order); Assert.assertEquals(sort.getField(), "attribute"); Assert.assertEquals(sort.getOrder(), Order.ASC); } @Test public void testDescSortCreation() { String order = "attribute " + Order.DESC; Sort sort = new Sort(order); Assert.assertEquals(sort.getField(), "attribute"); Assert.assertEquals(sort.getOrder(), Order.DESC); } @Test public void testDefaultSortOrder() { String order = "attribute"; Sort sort = new Sort(order); Assert.assertEquals(sort.getField(), "attribute"); Assert.assertEquals(sort.getOrder(), Sort.DEFAULT_ORDER); } @Test public void testToStringForParamWithConvertionValue() { Sort sort = new Sort(ActivityItem.ATTRIBUTE_ROOT_CASE_ID + " " + Order.DESC, new ActivityAttributeConverter()); Assert.assertEquals(sort.toString(), ActivityInstanceSearchDescriptor.PROCESS_INSTANCE_ID + " " + Order.DESC); } @Test public void testToStringForParamWithoutConvertionValue() { Sort sort = new Sort("TestParam " + Order.DESC, new ActivityAttributeConverter()); Assert.assertEquals(sort.toString(), "TestParam " + Order.DESC); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/utils/SortsTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.utils; import java.util.List; import org.bonitasoft.engine.search.Order; import org.junit.Assert; import org.junit.Test; /** * @author Vincent Elcrin */ public class SortsTest { @Test public void testNullSortListing() throws Exception { Sorts sorts = new Sorts(null); List sortList = sorts.asList(); Assert.assertTrue(sortList.isEmpty()); } @Test public void testEmptySortListing() throws Exception { Sorts sorts = new Sorts(""); List sortList = sorts.asList(); Assert.assertTrue(sortList.isEmpty()); } @Test public void testSingleSortListing() throws Exception { Sorts sorts = new Sorts("attribute " + Order.ASC); List sortList = sorts.asList(); Assert.assertEquals(new Sort("attribute " + Order.ASC), sortList.get(0)); } @Test public void testMultipleSortListing() throws Exception { String attribute1Order = "attribute1 " + Order.ASC; String attribute2Order = "attribute2 " + Order.DESC; Sorts sorts = new Sorts(attribute1Order + "," + attribute2Order); List sortList = sorts.asList(); Assert.assertEquals(new Sort(attribute1Order), sortList.get(0)); Assert.assertEquals(new Sort(attribute2Order), sortList.get(1)); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/utils/VariableMapperTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.utils; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import java.io.Serializable; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.framework.json.JacksonDeserializer; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.junit.Before; import org.junit.Test; public class VariableMapperTest extends APITestWithMock { private JacksonDeserializer jacksonDeserializer; @Before public void setUp() { jacksonDeserializer = new JacksonDeserializer(); } @Test(expected = APIException.class) public void getSerializableValue_throw_exception_if_class_is_not_in_classpath() { new VariableMapper(new Variable(), jacksonDeserializer).getSerializableValue("a.class.not.in.classpath"); } @Test(expected = APIException.class) public void getSerializableValue_throw_exception_if_classname_name_an_unserializable_object() { Variable variable = new Variable(); variable.set("value", new NotSerializableObject()); new VariableMapper(variable, jacksonDeserializer).getSerializableValue(NotSerializableObject.class.getName()); } @Test(expected = APIException.class) public void getSerializableValue_cannot_convert_values_that_ont_fits_to_given_className() { Variable variable = new Variable(); variable.set("value", "coucou"); Serializable value = new VariableMapper(variable, jacksonDeserializer) .getSerializableValue(Long.class.getName()); assertThat(value, is(1L)); } @Test public void getSerializableValue_return_variable_value_converted_in_given_class_name_object() { Variable variable = new Variable(); variable.set("value", 1); Serializable value = new VariableMapper(variable, jacksonDeserializer) .getSerializableValue(Long.class.getName()); assertThat(value, is(1L)); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/utils/VariablesMapperTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.datastore.utils; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItems; import org.bonitasoft.web.rest.server.framework.json.JacksonDeserializer; import org.junit.Test; public class VariablesMapperTest { private static final String JSON_VARIABLES = "[" + "{\"name\": \"variable1\", \"value\": \"newValue\"}," + "{\"name\": \"variable2\", \"value\": 9}," + "{\"name\": \"variable3\", \"value\": 349246800000}" + "]"; @Test public void variablesMapper_convert_json_variable_list_to_variableMappers() throws Exception { VariablesMapper variablesMapper = VariablesMapper.fromJson(JSON_VARIABLES); assertThat(variablesMapper.getVariables(), hasItems( aVariableMapper("variable1", "newValue"), aVariableMapper("variable2", 9), aVariableMapper("variable3", 349246800000L))); } private VariableMapper aVariableMapper(String name, Object value) { Variable variable = new Variable(); variable.set("name", name); variable.set("value", value); return new VariableMapper(variable, new JacksonDeserializer()); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/engineclient/ActivityEngineClientTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.*; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.data.DataNotFoundException; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; public class ActivityEngineClientTest extends APITestWithMock { @Mock private ProcessAPI processAPI; private ActivityEngineClient activityEngineClient; @Before public void initializeClient() { initMocks(this); activityEngineClient = new ActivityEngineClient(processAPI); } @Test(expected = APIException.class) public void getDataInstance_throw_exception_if_data_is_not_found() throws Exception { when(processAPI.getActivityDataInstance(anyString(), anyLong())) .thenThrow(new DataNotFoundException(new NullPointerException())); activityEngineClient.getDataInstance("aName", 1L); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/engineclient/CaseEngineClientTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.process.ProcessActivationException; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessExecutionException; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; public class CaseEngineClientTest extends APITestWithMock { @Mock private ProcessAPI processAPI; private CaseEngineClient caseEngineClient; @Before public void setUp() { initMocks(this); caseEngineClient = new CaseEngineClient(processAPI); } private Map someVariables() { final Map map = new HashMap<>(); map.put("variable", 1L); return map; } @Test public void a_process_can_be_started_without_variables() throws Exception { final long expectedProcessId = 1L; final long userId = 1L; caseEngineClient.start(userId, expectedProcessId); verify(processAPI).startProcess(userId, expectedProcessId); } @Test public void a_process_can_be_started_with_variables() throws Exception { final long expectedProcessId = 1L; final long userId = 1L; final Map variables = someVariables(); caseEngineClient.start(userId, expectedProcessId, variables); verify(processAPI).startProcess(userId, expectedProcessId, variables); } @Test(expected = APIException.class) public void cant_create_case_if_process_definition_is_not_found() throws Exception { when(processAPI.startProcess(anyLong())).thenThrow(new ProcessDefinitionNotFoundException("")); caseEngineClient.start(-1L, 1L); } @Test(expected = APIException.class) public void cant_create_case_if_process_is_not_activated() throws Exception { when(processAPI.startProcess(anyLong())).thenThrow(new ProcessActivationException("")); caseEngineClient.start(-1L, 1L); } @Test(expected = APIException.class) public void we_get_an_exception_if_process_fail_to_start() throws Exception { when(processAPI.startProcess(anyLong())).thenThrow(new ProcessExecutionException("")); caseEngineClient.start(-1L, 1L); } @SuppressWarnings("unchecked") @Test(expected = APIException.class) public void cant_create_case_with_variables_if_process_definition_is_not_found() throws Exception { when(processAPI.startProcess(anyLong(), anyMap())).thenThrow(new ProcessDefinitionNotFoundException("")); caseEngineClient.start(-1L, 1L, someVariables()); } @SuppressWarnings("unchecked") @Test(expected = APIException.class) public void cant_create_case_with_variables_if_process_is_not_activated() throws Exception { when(processAPI.startProcess(anyLong(), anyMap())).thenThrow(new ProcessActivationException("")); caseEngineClient.start(-1L, 1L, someVariables()); } @SuppressWarnings("unchecked") @Test(expected = APIException.class) public void we_get_an_exception_if_process_fail_to_start_with_variables() throws Exception { when(processAPI.startProcess(anyLong(), anyMap())).thenThrow(new ProcessExecutionException("")); caseEngineClient.start(-1L, 1L, someVariables()); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/engineclient/CustomUserInfoEngineClientTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willThrow; import static org.mockito.Mockito.verify; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.CustomUserInfo; import org.bonitasoft.engine.identity.CustomUserInfoDefinition; import org.bonitasoft.engine.identity.CustomUserInfoDefinitionCreator; import org.bonitasoft.engine.identity.CustomUserInfoValue; import org.bonitasoft.engine.identity.impl.CustomUserInfoValueImpl; import org.bonitasoft.web.rest.server.api.organization.EngineCustomUserInfoDefinition; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Vincent Elcrin */ @RunWith(MockitoJUnitRunner.class) public class CustomUserInfoEngineClientTest { @Mock private IdentityAPI engine; @InjectMocks private CustomUserInfoEngineClient client; @Test public void should_create_a_given_definition() throws Exception { given(engine.createCustomUserInfoDefinition(any(CustomUserInfoDefinitionCreator.class))) .willReturn(new EngineCustomUserInfoDefinition(1L, "foo", "bar")); CustomUserInfoDefinition definition = client.createDefinition(new CustomUserInfoDefinitionCreator("foo")); assertThat(definition.getName()).isEqualTo("foo"); } @Test(expected = APIException.class) public void should_fail_to_create_a_given_definition_when_engine_throw_an_exception() throws Exception { given(engine.createCustomUserInfoDefinition(any(CustomUserInfoDefinitionCreator.class))) .willThrow(new CreationException("failure")); client.createDefinition(new CustomUserInfoDefinitionCreator("foo")); } @Test public void should_delete_a_given_definition() throws Exception { client.deleteDefinition(1L); verify(engine).deleteCustomUserInfoDefinition(1L); } @Test(expected = APIException.class) public void should_fail_to_delete_a_given_definition_when_engine_throw_an_exception() throws Exception { willThrow(new DeletionException("failure")).given(engine) .deleteCustomUserInfoDefinition(1L); client.deleteDefinition(1L); } @Test public void should_list_definitions_for_a_given_range() throws Exception { given(engine.getCustomUserInfoDefinitions(0, 2)).willReturn(Arrays. asList( new EngineCustomUserInfoDefinition(1L), new EngineCustomUserInfoDefinition(2L))); List definitions = client.listDefinitions(0, 2); assertThat(definitions.get(0).getId()).isEqualTo(1L); assertThat(definitions.get(1).getId()).isEqualTo(2L); } @Test public void should_count_definitions() throws Exception { given(engine.getNumberOfCustomInfoDefinitions()).willReturn(5L); assertThat(client.countDefinitions()).isEqualTo(5); } @Test public void should_list_custom_information_for_a_given_user() { given(engine.getCustomUserInfo(1L, 0, 2)).willReturn(Arrays.asList( new CustomUserInfo(1L, new EngineCustomUserInfoDefinition(1L), new CustomUserInfoValueImpl()), new CustomUserInfo(1L, new EngineCustomUserInfoDefinition(2L), new CustomUserInfoValueImpl()))); List information = client.listCustomInformation(1L, 0, 2); assertThat(information.get(0).getDefinition().getId()).isEqualTo(1L); assertThat(information.get(1).getDefinition().getId()).isEqualTo(2L); } @Test public void should_the_value_of_a_given_custom_user_info() throws UpdateException { CustomUserInfoValueImpl value = new CustomUserInfoValueImpl(); value.setValue("foo"); given(engine.setCustomUserInfoValue(1L, 2L, "foo")).willReturn(value); CustomUserInfoValue foo = client.setCustomUserInfoValue(1L, 2L, "foo"); assertThat(foo.getValue()).isEqualTo("foo"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/engineclient/GroupEngineClientTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import static java.util.Arrays.asList; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import java.util.List; import org.bonitasoft.engine.api.GroupAPI; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.Group; import org.bonitasoft.engine.identity.GroupCreator; import org.bonitasoft.engine.identity.GroupNotFoundException; import org.bonitasoft.engine.identity.GroupUpdater; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; /** * @author Colin PUY */ public class GroupEngineClientTest extends APITestWithMock { @Mock private GroupAPI groupAPI; private GroupEngineClient groupEngineClient; @Before public void init() { initMocks(this); groupEngineClient = new GroupEngineClient(groupAPI); } @Test public void get_get_a_group_in_engine_repository() throws Exception { groupEngineClient.get(1L); verify(groupAPI).getGroup(1L); } @Test(expected = APIException.class) public void get_throw_APIException_if_group_is_not_found_in_engine_repository() throws Exception { when(groupAPI.getGroup(1L)).thenThrow(new GroupNotFoundException(new Exception())); groupEngineClient.get(1L); } @Test public void delete_delete_groups_in_engine_repository() throws Exception { List groupIds = asList(1L, 2L); groupEngineClient.delete(groupIds); verify(groupAPI).deleteGroups(groupIds); } @Test(expected = APIException.class) public void delete_throw_APIException_if_an_error_occurs_when_deleting_groups_in_engine_repository() throws Exception { List groupIds = asList(1L, 2L); doThrow(new DeletionException("error")).when(groupAPI).deleteGroups(groupIds); groupEngineClient.delete(groupIds); } @Test public void update_update_a_group_in_engine_repository() throws Exception { GroupUpdater groupUpdater = new GroupUpdater(); groupEngineClient.update(1L, groupUpdater); verify(groupAPI).updateGroup(1L, groupUpdater); } @Test(expected = APIException.class) public void update_throw_APIException_if_group_not_exists_in_engine_repository() throws Exception { when(groupAPI.updateGroup(eq(1L), any(GroupUpdater.class))) .thenThrow(new GroupNotFoundException(new Exception())); groupEngineClient.update(1L, new GroupUpdater()); } @Test(expected = APIException.class) public void update_throw_APIException_if_an_exception_occurs_when_updating_in_engine_repository() throws Exception { when(groupAPI.updateGroup(eq(1L), any(GroupUpdater.class))).thenThrow(new UpdateException("")); groupEngineClient.update(1L, new GroupUpdater()); } @Test(expected = APIException.class) public void update_throw_APIException_if_an_exception_occurs_when_updating_with_name_alreayExist() throws Exception { when(groupAPI.updateGroup(eq(1L), any(GroupUpdater.class))).thenThrow(new AlreadyExistsException("")); groupEngineClient.update(1L, new GroupUpdater()); } @Test(expected = APIException.class) public void getPath_throw_APIexception_if_groupId_is_not_a_number() throws Exception { groupEngineClient.getPath("notANumber"); } @Test public void getPath_return_the_group_path_for_the_specified_group_id() throws Exception { Group group = mock(Group.class); when(group.getPath()).thenReturn("/expected/group/path"); when(groupAPI.getGroup(1L)).thenReturn(group); String groupPath = groupEngineClient.getPath("1"); assertThat(groupPath, is("/expected/group/path")); } @Test public void create_create_a_group_in_engine_repository() throws Exception { GroupCreator creator = new GroupCreator("aName"); groupEngineClient.create(creator); verify(groupAPI).createGroup(creator); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/engineclient/ProcessEngineClientTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.api.ProcessAPI; import org.bonitasoft.engine.bpm.bar.BusinessArchive; import org.bonitasoft.engine.bpm.process.ProcessDefinitionNotFoundException; import org.bonitasoft.engine.bpm.process.ProcessDeployException; import org.bonitasoft.engine.bpm.process.V6FormDeployException; import org.bonitasoft.engine.bpm.process.impl.internal.DesignProcessDefinitionImpl; import org.bonitasoft.engine.exception.AlreadyExistsException; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.Mock; /** * @author Colin PUY */ public class ProcessEngineClientTest extends APITestWithMock { private static Integer BUNCH_SIZE = 10; @Rule public ExpectedException expectedException = ExpectedException.none(); @Mock private ProcessAPI processAPI; private ProcessEngineClient processEngineClient; @Before public void setUp() { initMocks(this); processEngineClient = new ProcessEngineClient(processAPI); } @Test public void deleteArchivedProcessInstancesByBunch_delete_archived_processes_instances_by_bunch() throws Exception { when(processAPI.deleteArchivedProcessInstances(1L, 0, BUNCH_SIZE)) .thenReturn(BUNCH_SIZE.longValue(), BUNCH_SIZE.longValue(), 0L); final List processList = new ArrayList<>(); processList.add(1L); processEngineClient.deleteArchivedProcessInstancesByBunch(1L, 10, processList); verify(processAPI, times(3)).deleteArchivedProcessInstances(1L, 0, 10); } @Test public void deleteProcessInstancesByBunch_delete_archived_processes_instances_by_bunch() throws Exception { when(processAPI.deleteProcessInstances(1L, 0, BUNCH_SIZE)) .thenReturn(BUNCH_SIZE.longValue(), BUNCH_SIZE.longValue(), 0L); final List processList = new ArrayList<>(); processList.add(1L); processEngineClient.deleteProcessInstancesByBunch(1L, 10, processList); verify(processAPI, times(3)).deleteProcessInstances(1L, 0, 10); } @Test(expected = Exception.class) public void getProcessDataDefinitions_throw_exception_if_process_definition_is_not_found() throws Exception { when(processAPI.getProcessDataDefinitions(anyLong(), anyInt(), anyInt())) .thenThrow(new ProcessDefinitionNotFoundException("")); processEngineClient.getProcessDataDefinitions(1L); } @Test public void getProcessDataDefinitions_get_all_process_data_definition() throws Exception { final long expectedProcessId = 1L; processEngineClient.getProcessDataDefinitions(expectedProcessId); verify(processAPI).getProcessDataDefinitions(expectedProcessId, 0, Integer.MAX_VALUE); } @Test public void deploying_a_process_with_v6_forms_fails_with_the_right_message() throws AlreadyExistsException, ProcessDeployException { DesignProcessDefinitionImpl designProcessDefinition = new DesignProcessDefinitionImpl("processName", "1.0"); BusinessArchive businessArchive = new BusinessArchive(); businessArchive.setProcessDefinition(designProcessDefinition); when(processAPI.deploy((BusinessArchive) any())) .thenThrow(new V6FormDeployException(new Exception("a cause"))); expectedException.expect(APIException.class); processEngineClient.deploy(businessArchive); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/engineclient/TenantManagementEngineClientTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.bonitasoft.engine.api.TenantAdministrationAPI; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class TenantManagementEngineClientTest { @Mock private TenantAdministrationAPI tenantAdministrationAPI; @InjectMocks private TenantManagementEngineClient tenantManagementEngineClient; private void pauseTenant() { when(tenantAdministrationAPI.isPaused()).thenReturn(true); } private void resumeTenant() { when(tenantAdministrationAPI.isPaused()).thenReturn(false); } @Before public void startTenant() { resumeTenant(); } @Test public void pauseTenant_pause_the_tenant() throws Exception { tenantManagementEngineClient.pauseTenant(); verify(tenantAdministrationAPI).pause(); } @Test public void pauseTenant_dont_pause_tenant_if_it_is_already_paused() throws Exception { pauseTenant(); tenantManagementEngineClient.pauseTenant(); verify(tenantAdministrationAPI, never()).pause(); } @Test(expected = APIException.class) public void pauseTenant_throw_APIException_if_error_occurs_when_pausing_tenant() throws Exception { doThrow(new UpdateException(new NullPointerException())).when(tenantAdministrationAPI).pause(); tenantManagementEngineClient.pauseTenant(); } @Test public void resumeTenant_resume_the_tenant() throws Exception { pauseTenant(); tenantManagementEngineClient.resumeTenant(); verify(tenantAdministrationAPI).resume(); } @Test public void resumeTenant_dont_resume_tenant_if_it_not_paused() throws Exception { tenantManagementEngineClient.resumeTenant(); verify(tenantAdministrationAPI, never()).resume(); } @Test(expected = APIException.class) public void resumeTenant_throw_APIException_if_error_occurs_when_resuming_tenant() throws Exception { pauseTenant(); doThrow(new UpdateException(new NullPointerException())).when(tenantAdministrationAPI).resume(); tenantManagementEngineClient.resumeTenant(); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/engineclient/UserEngineClientTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.engineclient; import static java.util.Arrays.asList; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import java.util.List; import org.bonitasoft.engine.api.IdentityAPI; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.identity.UserCreator; import org.bonitasoft.engine.identity.UserNotFoundException; import org.bonitasoft.engine.identity.UserUpdater; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; /** * @author Colin PUY */ public class UserEngineClientTest extends APITestWithMock { @Mock private IdentityAPI identityAPI; private UserEngineClient userEngineClient; @Before public void init() { initMocks(this); userEngineClient = new UserEngineClient(identityAPI); } @Test public void update_update_a_user_in_engine_repository() throws Exception { UserUpdater userUpdater = new UserUpdater(); userEngineClient.update(1L, userUpdater); verify(identityAPI).updateUser(1L, userUpdater); } @Test(expected = APIException.class) public void update_throw_APIException_if_user_is_not_found_in_engine_repository() throws Exception { UserUpdater userUpdater = new UserUpdater(); when(identityAPI.updateUser(1L, userUpdater)).thenThrow(new UserNotFoundException("aMessage")); userEngineClient.update(1L, userUpdater); } @Test(expected = APIException.class) public void update_throw_APIException_if_exception_occur_when_updating_user_in_engine_repository() throws Exception { UserUpdater userUpdater = new UserUpdater(); when(identityAPI.updateUser(1L, userUpdater)).thenThrow(new UpdateException("aMessage")); userEngineClient.update(1L, userUpdater); } @Test public void create_create_a_user_in_engine_repository() throws Exception { UserCreator userCreator = new UserCreator("aName", "aPassword"); userEngineClient.create(userCreator); verify(identityAPI).createUser(userCreator); } @Test(expected = APIException.class) public void create_throw_APIException_if_exception_occur_when_creating_user_in_engine_repository() throws Exception { UserCreator userCreator = new UserCreator("aName", "aPassword"); when(identityAPI.createUser(userCreator)).thenThrow(new CreationException("aMessage")); userEngineClient.create(userCreator); } @Test public void get_fetch_a_user_from_engine_repository() throws Exception { userEngineClient.get(1L); verify(identityAPI).getUser(1L); } @Test(expected = APIException.class) public void get_throw_APIException_if_user_is_not_found_in_engine_repository() throws Exception { when(identityAPI.getUser(1L)).thenThrow(new UserNotFoundException("aMessage")); userEngineClient.get(1L); } @Test public void delete_delete_users_in_engine_repository() throws Exception { List idsToBeDeleted = asList(1L, 2L); userEngineClient.delete(idsToBeDeleted); verify(identityAPI).deleteUsers(idsToBeDeleted); } @Test(expected = APIException.class) public void delete_throw_APIException_if_exception_occur_when_deleting_users_in_engine_repository() throws Exception { List idsToBeDeleted = asList(1L, 2L); doThrow(new DeletionException("aMessage")).when(identityAPI).deleteUsers(idsToBeDeleted); userEngineClient.delete(idsToBeDeleted); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/APIServletCallTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import javax.servlet.http.HttpServletMapping; import javax.servlet.http.HttpServletRequest; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.common.exception.api.APIIncorrectIdException; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.http.HttpHeaders; @RunWith(MockitoJUnitRunner.class) public class APIServletCallTest { @Mock private HttpServletRequest request; @Mock private HttpServletMapping httpServletMapping; @Mock private API api; @Spy private final APIServletCall apiServletCall = new APIServletCall(); @Rule public ExpectedException thrown = ExpectedException.none(); @Before public void before() { doReturn(httpServletMapping).when(request).getHttpServletMapping(); apiServletCall.api = api; } @Test public void should_parsePath_request_info_with_id() { doReturn("/bpm/case/15").when(request).getPathInfo(); apiServletCall.parsePath(request); assertThat(apiServletCall.getId().getPart(0)).isEqualTo("15"); assertThat(apiServletCall.getResourceName()).isEqualTo("case"); assertThat(apiServletCall.getApiName()).isEqualTo("bpm"); } @Test public void should_parsePath_request_info_with_negative_id() { doReturn("/identity/user/-1").when(request).getPathInfo(); thrown.expect(APIIncorrectIdException.class); thrown.expectMessage("Id must be non-zero positive for identity on resource user"); apiServletCall.parsePath(request); } @Test public void doGet_On_Search_Should_Set_Content_Range_Headers_Correctly() throws Exception { String parameterSearchValue = ""; String parameterPageValue = "10"; String parameterLimitValue = "0"; String parameterOrderValue = "id ASC"; List parameterDeployValue = new ArrayList<>(); List parameterCounterValue = new ArrayList<>(); doReturn(parameterDeployValue).when(apiServletCall).getParameterAsList("d"); doReturn(parameterCounterValue).when(apiServletCall).getParameterAsList("n"); doReturn(parameterPageValue).when(apiServletCall).getParameter("p", "0"); doReturn(parameterLimitValue).when(apiServletCall).getParameter("c", "10"); doReturn(parameterSearchValue).when(apiServletCall).getParameter("s"); doReturn(parameterOrderValue).when(apiServletCall).getParameter("o"); doReturn(new ArrayList<>()).when(apiServletCall).getParameterAsList("f"); doNothing().when(apiServletCall).head(anyString(), anyString()); doNothing().when(apiServletCall).output(any(List.class)); doReturn(2).when(apiServletCall).countParameters(); final ItemSearchResult itemSearchResult = mock(ItemSearchResult.class); when(itemSearchResult.getPage()).thenReturn(4); when(itemSearchResult.getLength()).thenReturn(8); when(itemSearchResult.getTotal()).thenReturn(789L); when(api.runSearch(Integer.parseInt(parameterPageValue), Integer.parseInt(parameterLimitValue), parameterSearchValue, parameterOrderValue, new HashMap<>(), parameterDeployValue, parameterCounterValue)).thenReturn(itemSearchResult); apiServletCall.doGet(); verify(api, times(1)).runSearch(Integer.parseInt(parameterPageValue), Integer.parseInt(parameterLimitValue), parameterSearchValue, parameterOrderValue, new HashMap<>(), parameterDeployValue, parameterCounterValue); verify(apiServletCall).head(HttpHeaders.CONTENT_RANGE, 4 + "-" + 8 + "/" + 789L); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/json/JSonSimpleDeserializerTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.json; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import org.bonitasoft.web.toolkit.client.common.AbstractTreeNode; import org.bonitasoft.web.toolkit.client.common.Tree; import org.bonitasoft.web.toolkit.client.common.TreeIndexed; import org.bonitasoft.web.toolkit.client.common.TreeLeaf; import org.junit.Test; /** * @author Séverin Moussel */ public class JSonSimpleDeserializerTest { @Test public void testMalformedJson() { try { JSonSimpleDeserializer.unserializeTree("[toto}"); } catch (final Exception e) { return; } fail("Malformed Json must throw an exception"); } @Test public void testEmptyInput() { final AbstractTreeNode tree = JSonSimpleDeserializer.unserializeTree(""); assertNull(tree); } @Test public void testSimpleObject() { final AbstractTreeNode tree = JSonSimpleDeserializer .unserializeTree("{\"name\":\"toto\",\"path\":\"titi\"}"); if (!(tree instanceof TreeIndexed)) { fail("Fail to parse a simple object in JSON"); } final TreeIndexed tree2 = (TreeIndexed) tree; assertEquals(tree2.getValue("name"), "toto"); assertEquals(tree2.getValue("path"), "titi"); assertNull(tree2.getValue("unassigned")); } @Test public void testEmptyObject() { final AbstractTreeNode tree = JSonSimpleDeserializer.unserializeTree("{}"); if (!(tree instanceof TreeIndexed)) { fail("Fail to parse a simple object in JSON"); } assertEquals("{}", tree.toJson()); } @Test public void testSimpleArray() { final AbstractTreeNode tree = JSonSimpleDeserializer.unserializeTree(" [\"name\",5,true]"); if (!(tree instanceof Tree)) { fail("Fail to parse a simple array in JSON"); } final Tree tree2 = (Tree) tree; assertEquals("name", tree2.getValues().get(0)); assertEquals("5", tree2.getValues().get(1)); assertEquals("1", tree2.getValues().get(2)); assertEquals("name", ((TreeLeaf) tree2.get(0)).getValue()); assertEquals("5", ((TreeLeaf) tree2.get(1)).getValue()); assertEquals("1", ((TreeLeaf) tree2.get(2)).getValue()); } @Test public void testEmptyArray() { final AbstractTreeNode tree = JSonSimpleDeserializer.unserializeTree("[]"); if (!(tree instanceof Tree)) { fail("Fail to parse a simple array in JSON"); } assertEquals("[]", tree.toJson()); } @Test public void testOneElementArray() { final AbstractTreeNode tree = JSonSimpleDeserializer.unserializeTree("[101]"); if (!(tree instanceof Tree)) { fail("Fail to parse a simple array in JSON"); } assertEquals("[\"101\"]", tree.toJson()); final Tree tree2 = (Tree) tree; assertEquals(tree2.getValues().get(0), "101"); } @Test public void testObjectWithArray() { final AbstractTreeNode tree = JSonSimpleDeserializer .unserializeTree("{\"name\":\"toto\",\"categories_id\":[1,2,5]}"); if (!(tree instanceof TreeIndexed)) { fail("Fail to parse a compound object in JSON"); } final AbstractTreeNode treeCat = ((TreeIndexed) tree).get("categories_id"); if (!(treeCat instanceof Tree)) { fail("Fail to parse an array in an object in JSON"); } final Tree tree2 = (Tree) treeCat; assertEquals(tree2.getValues().get(0), "1"); assertEquals(tree2.getValues().get(1), "2"); assertEquals(tree2.getValues().get(2), "5"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/json/JacksonDeserializerTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.json; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import java.util.Date; import java.util.List; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.framework.json.model.Address; import org.bonitasoft.web.rest.server.framework.json.model.User; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.junit.Before; import org.junit.Test; /** extends APITestWithMock to avoid NullPointerException on I18n */ public class JacksonDeserializerTest extends APITestWithMock { private JacksonDeserializer jacksonDeserializer; @Before public void initializeDeserializer() { jacksonDeserializer = new JacksonDeserializer(); } @Test(expected = APIException.class) public void deserialize_throw_exception_if_json_is_non_well_formed() { String nonWellFormedJson = "someJsonNonWellFormedJson"; jacksonDeserializer.deserialize(nonWellFormedJson, String.class); } @Test(expected = APIException.class) public void deserialize_throw_exception_if_mapping_between_class_and_json_is_incorrect() { String notUserJson = "{\"unknownUserAttribute\": \"unknownAttributeValue\"}"; jacksonDeserializer.deserialize(notUserJson, User.class); } @Test public void deserialize_can_deserialize_primitives_types() { Long deserializedLong = jacksonDeserializer.deserialize("1", Long.class); assertThat(deserializedLong, is(1L)); } @Test public void deserialize_can_deserialize_complex_types() { User expectedUser = new User(1, "Colin", "Puy", new Date(428558400000L), new Address("310 La Gouterie", "Charnecles")); String json = "{\"address\":{\"street\":\"310 La Gouterie\",\"city\":\"Charnecles\"},\"id\":1,\"firstName\":\"Colin\",\"lastName\":\"Puy\",\"birthday\":428558400000}"; User deserializedUser = jacksonDeserializer.deserialize(json, User.class); assertThat(deserializedUser, equalTo(expectedUser)); } @Test(expected = APIException.class) public void deserializeList_throw_exception_if_json_is_not_a_list() { String json = "{\"address\":{\"street\":\"310 La Gouterie\",\"city\":\"Charnecles\"},\"id\":1,\"firstName\":\"Colin\",\"lastName\":\"Puy\",\"birthday\":428558400000}"; jacksonDeserializer.deserializeList(json, User.class); } @Test public void deserializeList_can_deserialize_primitives_types() { List longs = jacksonDeserializer.deserializeList("[1, 2, 3]", Long.class); assertThat(longs, hasItems(1L, 2L, 3L)); } @Test public void deserializeList_can_deserialize_list_of_complex_type() { User expectedUser1 = new User(1, "Colin", "Puy", new Date(428558400000L), new Address("310 La Gouterie", "Charnecles")); User expectedUser2 = new User(2, "Clara", "Morgan", new Date(349246800000L), new Address("somewhere i don't know", "Paris")); String json = "[" + "{\"address\":{\"city\":\"Charnecles\",\"street\":\"310 La Gouterie\"},\"id\":1,\"firstName\":\"Colin\",\"lastName\":\"Puy\",\"birthday\":428558400000}," + "{\"address\":{\"city\":\"Paris\",\"street\":\"somewhere i don't know\"},\"id\":2,\"firstName\":\"Clara\",\"lastName\":\"Morgan\",\"birthday\":349246800000}" + "]"; List users = jacksonDeserializer.deserializeList(json, User.class); assertThat(users, hasItem(expectedUser1)); assertThat(users, hasItem(expectedUser2)); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/json/JacksonSerializerTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.json; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.web.rest.server.framework.json.model.ProfileImportStatusMessageFake; import org.junit.Test; public class JacksonSerializerTest { @Test public void testSerialize() throws Exception { // Given JacksonSerializer serializer = new JacksonSerializer(); ProfileImportStatusMessageFake message = new ProfileImportStatusMessageFake("profile1", "will be replaced"); message.addError("Organization: skks"); message.addError("Page: page1"); // When String serialize = serializer.serialize(message); // Then assertThat(serialize) .isEqualTo("{\"errors\":[\"Organization: skks\",\"Page: page1\"],\"profileName\":\"profile1\"}"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/json/model/Address.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.json.model; import java.io.Serializable; @SuppressWarnings("serial") public class Address implements Serializable { private String street; private String city; // Do not remove. Used by Json Serialization. public Address() { } public Address(String street, String city) { this.street = street; this.city = city; } // Do not remove. Used by Json Serialization. public String getStreet() { return street; } // Do not remove. Used by Json Serialization. public void setStreet(String street) { this.street = street; } // Do not remove. Used by Json Serialization. public String getCity() { return city; } // Do not remove. Used by Json Serialization. public void setCity(String city) { this.city = city; } @Override public String toString() { return "Address [street=" + street + ", city=" + city + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((city == null) ? 0 : city.hashCode()); result = prime * result + ((street == null) ? 0 : street.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Address other = (Address) obj; if (city == null) { if (other.city != null) return false; } else if (!city.equals(other.city)) return false; if (street == null) { if (other.street != null) return false; } else if (!street.equals(other.street)) return false; return true; } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/json/model/ProfileImportStatusMessageFake.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.json.model; import java.util.ArrayList; import java.util.List; /** * @author Fabio Lombardi */ public class ProfileImportStatusMessageFake { private List errors; private String profileName; private String status; public ProfileImportStatusMessageFake(String profileName, String status) { errors = new ArrayList<>(); this.profileName = profileName; this.status = status; } public void addError(String errorMessage) { errors.add(errorMessage); } public void addErrors(List errorMessages) { errors.addAll(errorMessages); } public void setProfileName(String name) { this.profileName = name; } public List getErrors() { return errors; } public String getProfileName() { return profileName; } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/json/model/User.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.json.model; import java.io.Serializable; import java.util.Date; @SuppressWarnings("serial") public class User implements Serializable { private Integer id; private String firstName; private String lastName; private Date birthday; private Address address; public User() { } public User(Integer id, String firstName, String lastName, Date birthday, Address address) { this.id = id; this.firstName = firstName; this.lastName = lastName; this.birthday = birthday; this.address = address; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Override public String toString() { return "User [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", address=" + address + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((address == null) ? 0 : address.hashCode()); result = prime * result + ((birthday == null) ? 0 : birthday.hashCode()); result = prime * result + ((firstName == null) ? 0 : firstName.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((lastName == null) ? 0 : lastName.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; User other = (User) obj; if (address == null) { if (other.address != null) return false; } else if (!address.equals(other.address)) return false; if (birthday == null) { if (other.birthday != null) return false; } else if (!birthday.equals(other.birthday)) return false; if (firstName == null) { if (other.firstName != null) return false; } else if (!firstName.equals(other.firstName)) return false; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; if (lastName == null) { if (other.lastName != null) return false; } else if (!lastName.equals(other.lastName)) return false; return true; } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/FilePathBuilderTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils; import static junit.framework.Assert.assertEquals; import java.io.File; import org.junit.Test; /** * @author Colin PUY */ public class FilePathBuilderTest { @Test public void testAppend() throws Exception { String path1 = "org", path2 = "bonita", path3 = "web/service"; FilePathBuilder path = new FilePathBuilder(path1).append(path2).append(path3); assertEquals(buildExpectedPath(path1, path2, path3), path.toString()); } private String buildExpectedPath(String root, String... paths) { StringBuilder expected = new StringBuilder(root); for (String path : paths) { addPath(expected, path); } return expected.toString(); } private void addPath(StringBuilder expected, String path) { expected.append(File.separator); expected.append(path); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/RestRequestURIParserTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.doReturn; import javax.servlet.http.HttpServletMapping; import javax.servlet.http.HttpServletRequest; import org.bonitasoft.web.toolkit.client.common.exception.api.APIMalformedUrlException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class RestRequestURIParserTest { @Mock private HttpServletRequest httpServletRequest; @Mock private HttpServletMapping httpServletMapping; @InjectMocks private RestRequestURIParser restRequestURIParser; @Before public void before() { doReturn(httpServletMapping).when(httpServletRequest).getHttpServletMapping(); doReturn("").when(httpServletRequest).getServletPath(); } @Test public void should_parsePath_request_info_with_id() { doReturn("/bpm/case/15").when(httpServletRequest).getPathInfo(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("bpm"); assertThat(result.getResourceName()).isEqualTo("case"); assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo("15"); } @Test public void should_parsePath_request_info() { doReturn("/bpm/case").when(httpServletRequest).getPathInfo(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getResourceName()).isEqualTo("case"); assertThat(result.getApiName()).isEqualTo("bpm"); assertThat(result.getResourceQualifiers()).isNull(); } @Test public void should_parsePath_when_exact_match_servlet_mapped_under_API() { // When an exact-match servlet is mapped to /API/documentDownload, // servletPath = "/API/documentDownload" and pathInfo = null. // "API" is included as apiName to match permission entry "GET|API/documentDownload". doReturn(null).when(httpServletRequest).getPathInfo(); doReturn("/API/documentDownload").when(httpServletRequest).getServletPath(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("API"); assertThat(result.getResourceName()).isEqualTo("documentDownload"); assertThat(result.getResourceQualifiers()).isNull(); } @Test public void should_throw_when_pathInfo_is_null_and_servletPath_has_only_API() { // When servletPath is just "/API" with no resource at all, parsing should fail. doReturn(null).when(httpServletRequest).getPathInfo(); doReturn("/API").when(httpServletRequest).getServletPath(); var requestUrl = new StringBuffer("http://my-host/API"); doReturn(requestUrl).when(httpServletRequest).getRequestURL(); assertThatThrownBy(() -> restRequestURIParser.parse()) .isInstanceOf(APIMalformedUrlException.class) .hasMessage("Missing API or resource name in request URL"); } @Test public void should_parsePath_when_exact_match_servlet_under_portal() { // When an exact-match servlet is mapped to /portal/imageUpload, // there is no "API" segment. Parse from index 1 so that // apiName = "portal" and resourceName = "imageUpload", // matching permission entry "POST|portal/imageUpload". doReturn(null).when(httpServletRequest).getPathInfo(); doReturn("/portal/imageUpload").when(httpServletRequest).getServletPath(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("portal"); assertThat(result.getResourceName()).isEqualTo("imageUpload"); assertThat(result.getResourceQualifiers()).isNull(); } @Test public void should_parsePath_when_wildcard_servlet_under_services() { // The RestAPIAuthorizationFilter is also mapped to /services/*. // servletPath = "/services" and pathInfo = "/application/import". doReturn("/application/import").when(httpServletRequest).getPathInfo(); doReturn("/services").when(httpServletRequest).getServletPath(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("application"); assertThat(result.getResourceName()).isEqualTo("import"); assertThat(result.getResourceQualifiers()).isNull(); } @Test public void should_parsePath_spring_mvc_request_info() { doReturn(RestRequestURIParser.SPRING_REST_SERVLET_NAME).when(httpServletMapping).getServletName(); doReturn("/API/system/maintenance").when(httpServletRequest).getServletPath(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("system"); assertThat(result.getResourceName()).isEqualTo("maintenance"); assertThat(result.getResourceQualifiers()).isNull(); } @Test public void should_parsePath_spring_mvc_request_info_with_id() { doReturn(RestRequestURIParser.SPRING_REST_SERVLET_NAME).when(httpServletMapping).getServletName(); doReturn("/API/system/maintenance").when(httpServletRequest).getServletPath(); doReturn("/1").when(httpServletRequest).getPathInfo(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("system"); assertThat(result.getResourceName()).isEqualTo("maintenance"); assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo("1"); } @Test public void should_parsePath_spring_mvc_custom_page_api_extension() { doReturn(RestRequestURIParser.SPRING_REST_SERVLET_NAME).when(httpServletMapping).getServletName(); doReturn("/portal/custom-page/API/extension").when(httpServletRequest).getServletPath(); doReturn("/my-rest-api").when(httpServletRequest).getPathInfo(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("extension"); assertThat(result.getResourceName()).isEqualTo("my-rest-api"); assertThat(result.getResourceQualifiers()).isNull(); } @Test public void should_throw_when_spring_mvc_request_has_no_resource_name() { doReturn(RestRequestURIParser.SPRING_REST_SERVLET_NAME).when(httpServletMapping).getServletName(); var path = "/API"; doReturn(path).when(httpServletRequest).getServletPath(); var requestUrl = new StringBuffer("http://my-host" + path); doReturn(requestUrl).when(httpServletRequest).getRequestURL(); assertThatThrownBy(() -> restRequestURIParser.parse()) .isInstanceOf(APIMalformedUrlException.class) .hasMessage("Missing API or resource name in request URL") .hasMessageNotContainingAny(requestUrl.toString(), path); } @Test public void should_parsePath_when_no_servlet_mapped_to_API_wildcard() { // When no servlet is mapped to /API/*, the default servlet handles the request. // In that case pathInfo is null and servletPath contains the full path including /API. doReturn(null).when(httpServletRequest).getPathInfo(); doReturn("/API/living/application").when(httpServletRequest).getServletPath(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("living"); assertThat(result.getResourceName()).isEqualTo("application"); assertThat(result.getResourceQualifiers()).isNull(); } @Test public void should_parsePath_when_no_servlet_mapped_to_API_wildcard_with_id() { doReturn(null).when(httpServletRequest).getPathInfo(); doReturn("/API/bpm/case/42").when(httpServletRequest).getServletPath(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("bpm"); assertThat(result.getResourceName()).isEqualTo("case"); assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo("42"); } @Test public void should_parsePath_when_no_servlet_mapped_to_APIToolkit_path() { doReturn(null).when(httpServletRequest).getPathInfo(); doReturn("/APIToolkit/bpm/process").when(httpServletRequest).getServletPath(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("bpm"); assertThat(result.getResourceName()).isEqualTo("process"); assertThat(result.getResourceQualifiers()).isNull(); } @Test public void should_parsePath_when_servlet_mapped_to_specific_API_subpath() { // When a servlet is mapped to a specific sub-path like /API/avatars/*, // servletPath = "/API/avatars" and pathInfo = "/17" (just the ID). // "API" must be included as apiName to match permission entry "GET|API/avatars". doReturn("/API/avatars").when(httpServletRequest).getServletPath(); doReturn("/17").when(httpServletRequest).getPathInfo(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("API"); assertThat(result.getResourceName()).isEqualTo("avatars"); assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo("17"); } @Test public void should_parsePath_when_servlet_mapped_to_portal_custom_page_API_subpath() { doReturn(RestRequestURIParser.CUSTOM_PAGE_SERVLET_NAME).when(httpServletMapping).getServletName(); // When a servlet is mapped to /portal/custom-page/*, doReturn("/portal/custom-page").when(httpServletRequest).getServletPath(); // pathInfo includes the API, resource and ID. doReturn("/API/identity/user/17").when(httpServletRequest).getPathInfo(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("identity"); assertThat(result.getResourceName()).isEqualTo("user"); assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo("17"); } @Test public void should_parsePath_when_servlet_mapped_to_portal_custom_page_API_avatars_subpath() { // When a servlet is mapped to /portal/custom-page/API/avatars/*, // servletPath includes the full prefix and pathInfo is just the ID. doReturn("/portal/custom-page/API/avatars").when(httpServletRequest).getServletPath(); doReturn("/17").when(httpServletRequest).getPathInfo(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("API"); assertThat(result.getResourceName()).isEqualTo("avatars"); assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo("17"); } @Test public void should_parsePath_spring_mvc_custom_page_api_extension_with_qualifier() { doReturn(RestRequestURIParser.SPRING_REST_SERVLET_NAME).when(httpServletMapping).getServletName(); doReturn("/portal/custom-page/API/extension").when(httpServletRequest).getServletPath(); doReturn("/my-rest-api/resource1").when(httpServletRequest).getPathInfo(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("extension"); assertThat(result.getResourceName()).isEqualTo("my-rest-api"); assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo("resource1"); } @Test public void should_parsePath_when_APIToolkit_servlet_with_pathInfo() { // BonitaRestAPIServlet is mapped to /APIToolkit/*. // servletPath = "/APIToolkit" and pathInfo = "/bpm/case/15". doReturn("/APIToolkit").when(httpServletRequest).getServletPath(); doReturn("/bpm/case/15").when(httpServletRequest).getPathInfo(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("bpm"); assertThat(result.getResourceName()).isEqualTo("case"); assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo("15"); } @Test public void should_parsePath_spring_mvc_with_APISpringInternal_rewritten_url() { // When UrlRewriteFilter rewrites /API/bpm/userTask/3/execution to // /APISpringInternal/bpm/userTask/3/execution and forwards to Spring MVC. doReturn(RestRequestURIParser.SPRING_REST_SERVLET_NAME).when(httpServletMapping).getServletName(); doReturn("/APISpringInternal").when(httpServletRequest).getServletPath(); doReturn("/bpm/userTask/3/execution").when(httpServletRequest).getPathInfo(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("bpm"); assertThat(result.getResourceName()).isEqualTo("userTask"); assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo("3"); assertThat(result.getResourceQualifiers().getPart(1)).isEqualTo("execution"); } @Test public void should_throw_when_url_path_too_short_and_no_API_segment() { // A path with fewer than 3 segments and no API marker should fail doReturn(null).when(httpServletRequest).getPathInfo(); doReturn("/x").when(httpServletRequest).getServletPath(); var requestUrl = new StringBuffer("http://my-host/x"); doReturn(requestUrl).when(httpServletRequest).getRequestURL(); assertThatThrownBy(() -> restRequestURIParser.parse()) .isInstanceOf(APIMalformedUrlException.class) .hasMessage("Missing API path segment in request URL"); } @Test public void should_parsePath_spring_mvc_with_direct_wildcard_mapping() { // When Spring MVC has a direct wildcard mapping like /API/bpm/activityVariable/*, // servletPath = "/API/bpm/activityVariable" and pathInfo = "/3/myVar". doReturn(RestRequestURIParser.SPRING_REST_SERVLET_NAME).when(httpServletMapping).getServletName(); doReturn("/API/bpm/activityVariable").when(httpServletRequest).getServletPath(); doReturn("/3/myVar").when(httpServletRequest).getPathInfo(); ParsedRestRequestURI result = restRequestURIParser.parse(); assertThat(result.getApiName()).isEqualTo("bpm"); assertThat(result.getResourceName()).isEqualTo("activityVariable"); assertThat(result.getResourceQualifiers().getPart(0)).isEqualTo("3"); assertThat(result.getResourceQualifiers().getPart(1)).isEqualTo("myVar"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/ConverterFactoryTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter; import static org.junit.Assert.assertTrue; import javax.servlet.Servlet; import org.bonitasoft.web.rest.server.framework.utils.converter.typed.BooleanConverter; import org.bonitasoft.web.rest.server.framework.utils.converter.typed.DoubleConverter; import org.bonitasoft.web.rest.server.framework.utils.converter.typed.IntegerConverter; import org.bonitasoft.web.rest.server.framework.utils.converter.typed.LongConverter; import org.bonitasoft.web.rest.server.framework.utils.converter.typed.StringConverter; import org.junit.Before; import org.junit.Test; /** * @author Colin PUY */ public class ConverterFactoryTest { private ConverterFactory factory; @Before public void initFactory() { factory = new ConverterFactory(); } @Test public void factoryCreateABooleanConverterForBooleanClassName() { Converter createConverter = factory.createConverter(Boolean.class.getName()); assertTrue(createConverter instanceof BooleanConverter); } @Test public void factoryCreateADoubleConverterForDoubleClassName() { Converter createConverter = factory.createConverter(Double.class.getName()); assertTrue(createConverter instanceof DoubleConverter); } @Test public void factoryCreateALongConverterForLongClassName() { Converter createConverter = factory.createConverter(Long.class.getName()); assertTrue(createConverter instanceof LongConverter); } @Test public void factoryCreateAStringConverterForStringClassName() { Converter createConverter = factory.createConverter(String.class.getName()); assertTrue(createConverter instanceof StringConverter); } @Test public void factoryCreateAnIntegerConverterForIntegerClassName() { Converter createConverter = factory.createConverter(Integer.class.getName()); assertTrue(createConverter instanceof IntegerConverter); } @Test(expected = UnsupportedOperationException.class) public void factoryThrowExceptionForUnsuportedConverter() { factory.createConverter(Servlet.class.getName()); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/TypeConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * @author Colin PUY */ public class TypeConverterTest { private TypeConverter converter; @Mock private ConverterFactory factory; @Before public void initConverter() { MockitoAnnotations.initMocks(this); converter = new TypeConverter(factory); } @Test @SuppressWarnings({ "rawtypes", "unchecked" }) public void convertCreateAFactoryAndConvertStringValueToSerializableObject() throws Exception { Converter typedConverter = mock(Converter.class); when(factory.createConverter(anyString())).thenReturn(typedConverter); converter.convert("aClassName", "somethingToconvert"); verify(typedConverter).convert("somethingToconvert"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/BooleanConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter.typed; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import org.junit.Before; import org.junit.Test; /** * @author Colin PUY */ public class BooleanConverterTest { private BooleanConverter converter; @Before public void initConverter() { converter = new BooleanConverter(); } @Test public void trueIsConvertedToTrue() throws Exception { Boolean converted = converter.convert("true"); assertTrue(converted); } @Test public void falseIsConvertedToFalse() throws Exception { Boolean converted = converter.convert("false"); assertFalse(converted); } @Test public void nullIsConvertedToNull() throws Exception { Boolean converted = converter.convert(null); assertNull(converted); } @Test public void emptyIsConvertedToNull() throws Exception { Boolean converted = converter.convert(""); assertNull(converted); } @Test public void anythingElseIsConvertedToFalse() throws Exception { Boolean converted = converter.convert("something"); assertFalse(converted); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/DateConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter.typed; import static junit.framework.Assert.assertNull; import static org.junit.Assert.assertEquals; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; import org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException; import org.junit.Before; import org.junit.Test; /** * @author Nicolas Tith */ public class DateConverterTest { private DateConverter converter; @Before public void initConverter() { converter = new DateConverter(); } @Test public void nullIsConvertedToNull() throws Exception { Date converted = converter.convert(null); assertNull(converted); } @Test public void emptyIsConvertedToNull() throws Exception { Date converted = converter.convert(""); assertNull(converted); } @Test public void shouldConvertDateStringIntoDate() throws Exception { Calendar c = Calendar.getInstance(); int hourOfDay = 11; int minute = 43; int second = 30; int dayOfDateate = 18; int year = 2014; c.set(year, Calendar.AUGUST, dayOfDateate, hourOfDay, minute, second); String timeZone = "GMT"; c.setTimeZone(TimeZone.getTimeZone(timeZone)); c.set(Calendar.MILLISECOND, 0); Date date = c.getTime(); Date converted = converter.convert("Mon Aug " + dayOfDateate + " " + hourOfDay + ":" + minute + ":" + second + " " + timeZone + " " + year); assertEquals(date.toString() + " is not well converted", date, converted); } @Test(expected = ConversionException.class) public void nonParsableStringThrowAConversionException() throws Exception { converter.convert("nonParsable"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/DoubleConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter.typed; import static junit.framework.Assert.assertNull; import static org.junit.Assert.assertEquals; import org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException; import org.junit.Before; import org.junit.Test; /** * @author Colin PUY */ public class DoubleConverterTest { private DoubleConverter converter; @Before public void initConverter() { converter = new DoubleConverter(); } @Test public void nullIsConvertedToNull() throws Exception { Double converted = converter.convert(null); assertNull(converted); } @Test public void emptyIsConvertedToNull() throws Exception { Double converted = converter.convert(""); assertNull(converted); } @Test public void doubleNumberIsParsedToDouble() throws Exception { double converted = converter.convert("1.23"); assertEquals(1.23d, converted, 0d); } @Test(expected = ConversionException.class) public void nonParsableStringThrowAConversionException() throws Exception { converter.convert("nonParsable"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/IntegerConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter.typed; import static junit.framework.Assert.assertNull; import static org.junit.Assert.assertEquals; import org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException; import org.junit.Before; import org.junit.Test; /** * @author Colin PUY */ public class IntegerConverterTest { private IntegerConverter converter; @Before public void initConverter() { converter = new IntegerConverter(); } @Test public void nullIsConvertedToNull() throws Exception { Integer converted = converter.convert(null); assertNull(converted); } @Test public void emptyIsConvertedToNull() throws Exception { Integer converted = converter.convert(""); assertNull(converted); } @Test public void intNumberIsParsedToInteger() throws Exception { int converted = converter.convert("456789"); assertEquals(456789, converted); } @Test(expected = ConversionException.class) public void nonParsableStringThrowAConversionException() throws Exception { converter.convert("nonParsable"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/LongConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter.typed; import static junit.framework.Assert.assertNull; import static org.junit.Assert.assertEquals; import org.bonitasoft.web.rest.server.framework.utils.converter.ConversionException; import org.junit.Before; import org.junit.Test; /** * @author Colin PUY */ public class LongConverterTest { private LongConverter converter; @Before public void initConverter() { converter = new LongConverter(); } @Test public void nullIsConvertedToNull() throws Exception { Long converted = converter.convert(null); assertNull(converted); } @Test public void emptyIsConvertedToNull() throws Exception { Long converted = converter.convert(""); assertNull(converted); } @Test public void longNumberIsParsedToLong() throws Exception { long converted = converter.convert("456789"); assertEquals(456789L, converted); } @Test(expected = ConversionException.class) public void nonParsableStringThrowAConversionException() throws Exception { converter.convert("nonParsable"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/framework/utils/converter/typed/StringConverterTest.java ================================================ /** * Copyright (C) 2022 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.web.rest.server.framework.utils.converter.typed; import static junit.framework.Assert.assertEquals; import org.junit.Test; /** * @author Colin PUY */ public class StringConverterTest { @Test public void stringConverterIsDummy() throws Exception { String converted = new StringConverter().convert("any string"); assertEquals("any string", converted); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/server/login/LoginFailureTrackerAccessorTest.java ================================================ /** * Copyright (C) 2026 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.web.server.login; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import javax.servlet.ServletContext; import org.junit.Test; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.web.context.WebApplicationContext; public class LoginFailureTrackerAccessorTest { @Test public void should_return_null_when_application_context_is_not_available() { ServletContext servletContext = mock(ServletContext.class); // getAttribute returns null by default → WebApplicationContextUtils returns null LoginFailureTracker result = LoginFailureTrackerAccessor.getLoginFailureTracker(servletContext); assertThat(result).isNull(); } @Test public void should_return_null_when_bean_is_not_found() { ServletContext servletContext = mock(ServletContext.class); WebApplicationContext appContext = mock(WebApplicationContext.class); when(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)) .thenReturn(appContext); when(appContext.getBean(LoginFailureTracker.class)) .thenThrow(new NoSuchBeanDefinitionException(LoginFailureTracker.class)); LoginFailureTracker result = LoginFailureTrackerAccessor.getLoginFailureTracker(servletContext); assertThat(result).isNull(); } @Test public void should_return_tracker_when_bean_is_found() { ServletContext servletContext = mock(ServletContext.class); WebApplicationContext appContext = mock(WebApplicationContext.class); LoginFailureTracker tracker = mock(LoginFailureTracker.class); when(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)) .thenReturn(appContext); when(appContext.getBean(LoginFailureTracker.class)).thenReturn(tracker); LoginFailureTracker result = LoginFailureTrackerAccessor.getLoginFailureTracker(servletContext); assertThat(result).isSameAs(tracker); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/common/exception/http/JsonExceptionSerializerTest.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.exception.http; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertThat; import org.bonitasoft.console.common.FakeI18n; import org.bonitasoft.console.common.server.i18n.I18n; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE; import org.bonitasoft.web.toolkit.client.common.i18n.T_; import org.junit.After; import org.junit.Before; import org.junit.Test; /** * Created by Vincent Elcrin * Date: 23/09/13 * Time: 18:12 */ public class JsonExceptionSerializerTest { private FakeI18n fakeI18n; @Before public void setUp() throws Exception { fakeI18n = new FakeI18n(); } @After public void cleanUp() throws Exception { I18n.setInstance(null); } @Test public void testWeCanConvertExceptionWithoutMessage() throws Exception { HttpException exception = new HttpException(); JsonExceptionSerializer serializer = new JsonExceptionSerializer(exception); String json = serializer.end(); assertEquals(exception, json); } @Test public void testWeCanConvertExceptionWithMessageToJson() throws Exception { HttpException exception = new HttpException("message"); JsonExceptionSerializer serializer = new JsonExceptionSerializer(exception); String json = serializer.end(); assertEquals(exception, json); } @Test public void testWeCanAppendNewAttributeBeforeEndCall() throws Exception { HttpException exception = new HttpException(); JsonExceptionSerializer serializer = new JsonExceptionSerializer(exception); String json = serializer .appendAttribute("attributeKey", "attributeValue") .end(); assertThat(json, equalTo( "{\"exception\":\"" + exception.getClass().toString() + "\"," + "\"message\":\"" + exception.getMessage() + "\"," + "\"attributeKey\":\"attributeValue\"}")); } @Test public void testJsonContainsInternationalizedMessageWhenLocalIsSet() throws Exception { APIException exception = new APIException(new T_("message")); fakeI18n.setL10n("localization"); exception.setLocale(LOCALE.en); JsonExceptionSerializer serializer = new JsonExceptionSerializer(exception); String json = serializer.end(); assertThat(json, equalTo( "{\"exception\":\"" + exception.getClass().toString() + "\"," + "\"message\":\"localization\"" + "}")); } private void assertEquals(Exception e, String json) { assertThat(json, equalTo("{\"exception\":\"" + e.getClass().toString() + "\"," + "\"message\":\"" + e.getMessage() + "\"}")); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/common/json/JSonItemReaderTest.java ================================================ /** * 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.web.toolkit.client.common.json; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.web.rest.model.application.AbstractApplicationDefinition; import org.bonitasoft.web.rest.model.application.ApplicationItem; import org.bonitasoft.web.rest.model.application.ApplicationLinkItem; import org.junit.Test; public class JSonItemReaderTest { @Test public void should_parse_an_implicit_LegacyApplication_from_AbstractApplicationDefinition() throws Exception { // given String json = """ { "version": "1.0", "profileId": "2", "token": "myapp", "displayName": "My app", "description": "My application description" } """; // when var app = JSonItemReader.parseItem(json, AbstractApplicationDefinition.get()); // then assertThat(app).isExactlyInstanceOf(ApplicationItem.class); } @Test public void should_parse_an_explicit_LegacyApplication_from_AbstractApplicationDefinition() throws Exception { // given String json = """ { "link": "false", "version": "1.0", "profileId": "2", "token": "myapp", "displayName": "My app", "description": "My application description" } """; // when var app = JSonItemReader.parseItem(json, AbstractApplicationDefinition.get()); // then assertThat(app).isExactlyInstanceOf(ApplicationItem.class); } @Test public void should_parse_an_ApplicationLink_from_AbstractApplicationDefinition() throws Exception { // given String json = """ { "link": "true", "version": "1.0", "profileId": "2", "token": "myapp", "displayName": "My app", "description": "My application description" } """; // when var app = JSonItemReader.parseItem(json, AbstractApplicationDefinition.get()); // then assertThat(app).isExactlyInstanceOf(ApplicationLinkItem.class); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/common/json/JSonSerializerTest.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.json; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class JSonSerializerTest { @Test public void should_serialize_an_exception() throws Exception { Exception exception = new Exception("an exception"); String serialize = JSonSerializer.serialize(exception); assertThat(serialize).isEqualTo("{\"exception\":\"class java.lang.Exception\",\"message\":\"an exception\"}"); } @Test public void should_serialize_only_first_cause_of_an_exception() throws Exception { Exception exception = new Exception("first one", new Exception("second one", new Exception("third one"))); String serialize = JSonSerializer.serialize(exception); assertThat(serialize).isEqualTo( "{\"exception\":\"class java.lang.Exception\"," + "\"message\":\"first one\"," + "\"cause\":{" + "\"exception\":\"class java.lang.Exception\"," + "\"message\":\"second one\"" + "}" + "}"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/common/json/JSonUtilTest.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.json; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.web.toolkit.client.common.json.JSonUtil.escape; import org.junit.Test; public class JSonUtilTest { @Test public void should_escape_unsafe_html_characters() { assertThat(escape("")) .isEqualTo("\\u003cscript\\u003ealert(\\u0027bad\\u0027)\\u003c\\/script\\u003e"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/common/texttemplate/TextTemplateTest.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.common.texttemplate; import static org.assertj.core.api.Assertions.assertThat; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Test; public class TextTemplateTest { public static final String BASE_TEXT = "This is a text template made by %the_user% \n" + "It has %multiple_vars% with even single %\n" + "and also %vars with spaces@@%\n" + "and %missing vars%"; @Test public void should_find_expected_parameters_of_text_template() { TextTemplate textTemplate = new TextTemplate(BASE_TEXT); List expectedParameters = textTemplate.getExpectedParameters(); assertThat(expectedParameters).containsExactly( "the_user", "multiple_vars", "vars with spaces@@", "missing vars"); } @Test public void should_replace_parameters_of_text_template() { TextTemplate textTemplate = new TextTemplate(BASE_TEXT); Map parameters = new HashMap<>(); parameters.put("the_user", "Walter bates"); parameters.put("multiple_vars", "Multiple variables to replace"); parameters.put("vars with spaces@@", "Variables With all kind of special \nchars and space: @#%ˆ&*(%%%"); String output = textTemplate.toString(parameters); assertThat(output).isEqualTo("This is a text template made by Walter bates \n" + "It has Multiple variables to replace with even single %\n" + "and also Variables With all kind of special \nchars and space: @#%ˆ&*(%%%\n" + "and %missing vars%"); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/data/APIIDTest.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data; import static org.assertj.core.api.Assertions.assertThat; import java.util.ArrayList; import java.util.List; import org.bonitasoft.web.toolkit.client.common.exception.api.APIItemIdMalformedException; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; /** * @author Dumitru Corini */ public class APIIDTest { @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void makeAPIID_with_list_should_return_correct_result() throws Exception { List ids = new ArrayList<>(); ids.add("1"); APIID result = APIID.makeAPIID(ids); assertThat(result.toLong()).isEqualTo(1L); ids = new ArrayList<>(); ids.add("1"); ids.add("6"); ids.add("-1"); result = APIID.makeAPIID(ids); assertThat(result.getIds()).isEqualTo(ids); ids = new ArrayList<>(); ids.add(""); ids.add("1"); ids.add("-1"); ids.add("instantiation"); ids.add(null); result = APIID.makeAPIID(ids); assertThat(result.getIds()).isEqualTo(ids); } @Test public void makeAPIID_with_array_should_return_correct_result() throws Exception { Long[] ids = new Long[] { 1L }; APIID result = APIID.makeAPIID(ids); assertThat(result.toLong()).isEqualTo(1L); ids = new Long[] { 1L, null, 6L, -1L }; List resultingAPIIDs = new ArrayList<>(); resultingAPIIDs.add("1"); resultingAPIIDs.add(null); resultingAPIIDs.add("6"); resultingAPIIDs.add("-1"); result = APIID.makeAPIID(ids); assertThat(result.getIds()).isEqualTo(resultingAPIIDs); } @Test(expected = APIItemIdMalformedException.class) public void toLong_should_throw_APIItemIdMalformedException_with_string() { APIID.makeAPIID("undefined").toLong(); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/data/item/attribute/validator/StringFormatURLValidatorTest.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.client.data.item.attribute.validator; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.console.common.server.i18n.I18n; import org.junit.Before; import org.junit.Test; public class StringFormatURLValidatorTest { private StringFormatURLValidator stringFormatURLValidator = new StringFormatURLValidator(); @Before public void before() { // to initialize the system: I18n.getInstance(); } @Test public void should_verify_urls() { checkUrl("toto", true); checkUrl("www.toto", false); checkUrl("https://toto", false); checkUrl("ftp://toto", false); checkUrl("http://toto", false); checkUrl("http://toto?tata.titi=tutu", false); } private void checkUrl(String url, boolean shouldHaveErrors) throws AssertionError { stringFormatURLValidator.reset(); stringFormatURLValidator._check(url); assertThat(stringFormatURLValidator.getErrors().isEmpty()).isEqualTo(!shouldHaveErrors); } } ================================================ FILE: bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/server/servlet/ToolkitHttpServletTest.java ================================================ /** * Copyright (C) 2022 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.web.toolkit.server.servlet; import static org.bonitasoft.web.toolkit.client.common.i18n.AbstractI18n.LOCALE; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; import java.io.PrintWriter; import java.util.HashMap; import java.util.Locale; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.bonitasoft.console.common.server.i18n.I18n; import org.bonitasoft.console.common.server.utils.LocaleUtils; import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.server.ServletCall; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.Mockito; /** * Created by Vincent Elcrin * Date: 23/09/13 * Time: 16:50 */ public class ToolkitHttpServletTest { ToolkitHttpServlet toolkitHttpServlet = new ToolkitHttpServlet() { @Override protected ServletCall defineServletCall(HttpServletRequest req, HttpServletResponse resp) { return null; } }; @Mock HttpServletRequest req; @Mock HttpServletResponse resp; @Mock PrintWriter writer; @Before public void setUp() throws Exception { initMocks(this); HashMap availableLocales; availableLocales = new HashMap<>(); availableLocales.put("en", "English"); availableLocales.put("fr", "Français"); availableLocales.put("es", "Español"); availableLocales.put("pt_BR", "Português (Brasil)"); availableLocales.put("ja", "日本語"); I18n i18n = mock(I18n.class); I18n.setInstance(i18n); Mockito.when(i18n.getAvailableLocalesFor(anyString())).thenReturn(availableLocales); } @After public void cleanUp() throws Exception { I18n.setInstance(null); } @Test public void testOutputExceptionPrintProperJson() throws Exception { APIException exception = new APIException("message"); doReturn(writer).when(resp).getWriter(); toolkitHttpServlet.outputException(exception, req, resp, 500); verify(writer).print(exception.toJson()); } @Test public void testLocaleIsPassedFromRequestToException() throws Exception { APIException exception = mock(APIException.class, withSettings().defaultAnswer(RETURNS_MOCKS)); doReturn(writer).when(resp).getWriter(); doReturn(new Cookie[] { new Cookie(LocaleUtils.LOCALE_COOKIE_NAME, "fr_FR") }).when(req).getCookies(); toolkitHttpServlet.outputException(exception, req, resp, 500); verify(exception).setLocale(LOCALE.fr); } @Test public void testIfLocaleIsNotInACookieThatBrowserLocaleIsPassedThrough() throws Exception { APIException exception = mock(APIException.class, withSettings().defaultAnswer(RETURNS_MOCKS)); doReturn(writer).when(resp).getWriter(); doReturn(new Cookie[0]).when(req).getCookies(); doReturn(Locale.CANADA_FRENCH).when(req).getLocale(); toolkitHttpServlet.outputException(exception, req, resp, 500); verify(exception).setLocale(LOCALE.fr); } @Test public void testIfLocaleIsNotInACookieNorBrowserThatDefaultLocaleIsPassedThrough() throws Exception { APIException exception = mock(APIException.class, withSettings().defaultAnswer(RETURNS_MOCKS)); doReturn(writer).when(resp).getWriter(); doReturn(new Cookie[0]).when(req).getCookies(); doReturn(null).when(req).getLocale(); toolkitHttpServlet.outputException(exception, req, resp, 500); verify(exception).setLocale(LOCALE.en); } } ================================================ FILE: bpm/bonita-web-server/src/test/resources/ARootPageFolder/AbstractIndex.groovy ================================================ public class AbstractIndex { public String name(){ return "name"; } } ================================================ FILE: bpm/bonita-web-server/src/test/resources/ARootPageFolder/Index.groovy ================================================ public class Index extends AbstractIndex { public void doGet() { } } ================================================ FILE: bpm/bonita-web-server/src/test/resources/ARootPageFolder/lib/bdm-client.jar ================================================ ================================================ FILE: bpm/bonita-web-server/src/test/resources/ARootPageFolder/lib/bdm-dao.jar ================================================ ================================================ FILE: bpm/bonita-web-server/src/test/resources/ARootPageFolder/lib/javassist-3.18.1-GA.jar ================================================ ================================================ FILE: bpm/bonita-web-server/src/test/resources/ARootPageFolder/lib/subdir/resource.properties ================================================ key=value ================================================ FILE: bpm/bonita-web-server/src/test/resources/ARootPageFolder/lib/util.jar ================================================ ================================================ FILE: bpm/bonita-web-server/src/test/resources/ARootPageFolder/org/company/test/Util.groovy ================================================ package org.company.test; public class Util { public static String name(){ return "My Name"; } } ================================================ FILE: bpm/bonita-web-server/src/test/resources/ARootPageFolder/org/company/test/config.properties ================================================ aProperty=Value ================================================ FILE: bpm/bonita-web-server/src/test/resources/Index.groovy ================================================ package org.bonitasoft.test.page import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse import javax.servlet.http.HttpSession import org.bonitasoft.engine.api.IdentityAPI import org.bonitasoft.engine.api.TenantAPIAccessor import org.bonitasoft.engine.identity.User import org.bonitasoft.web.extension.page.PageContext import org.bonitasoft.web.extension.page.PageController import org.bonitasoft.web.extension.page.PageResourceProvider class Index implements PageController { @Override void doGet(HttpServletRequest request, HttpServletResponse response, PageResourceProvider pageResourceProvider, PageContext pageContext) { try { HttpSession session = request.getSession() IdentityAPI identityAPI = TenantAPIAccessor.getIdentityAPI(pageContext.getApiSession()) User user = identityAPI.getUserByUserName((String)session.getAttribute("username")) PrintWriter out = response.getWriter() out.write("
      ") out.write("User details returned from Engine API Call using the existing API Session:") out.write(user.toString()) out.write("
      ") out.flush() out.close() } catch (Exception e) { e.printStackTrace() } } } ================================================ FILE: bpm/bonita-web-server/src/test/resources/IndexRestApi.groovy ================================================ package org.bonitasoft.test.page import javax.servlet.http.HttpServletRequest import org.bonitasoft.web.extension.rest.RestAPIContext import org.bonitasoft.web.extension.rest.RestApiController import org.bonitasoft.web.extension.rest.RestApiResponse import org.bonitasoft.web.extension.rest.RestApiResponseBuilder class IndexRestApi implements RestApiController { @Override RestApiResponse doHandle(HttpServletRequest request, RestApiResponseBuilder responseBuilder, RestAPIContext context) { return responseBuilder.withResponse("result").build() } } ================================================ FILE: bpm/bonita-web-server/src/test/resources/bdmDependencies/bdm-client.jar ================================================ ================================================ FILE: bpm/bonita-web-server/src/test/resources/bdmDependencies/bdm-dao.jar ================================================ ================================================ FILE: bpm/bonita-web-server/src/test/resources/bdmDependencies/javassist-3.18.1-GA.jar ================================================ ================================================ FILE: bpm/bonita-web-server/src/test/resources/compound-permissions-mapping.properties ================================================ processListingPage processVisualization caseListingPage caseVisualizationWithTrailingSpace taskListingPage [TaskVisualization, CaseVisualization] ================================================ FILE: bpm/bonita-web-server/src/test/resources/custom-permissions-mapping.properties ================================================ profile|User=[ManageLooknFeel, ManageProfiles] profile|HR\u0020manager=[ManageProfiles] ================================================ FILE: bpm/bonita-web-server/src/test/resources/custom_po_resource/added_custom_i18n_fr.po ================================================ msgid "about" msgstr "Copyright Bonitasoft 2016" ================================================ FILE: bpm/bonita-web-server/src/test/resources/custom_po_resource/test_es.po ================================================ msgid "test key" msgstr "valor de prueba en Espanol" ================================================ FILE: bpm/bonita-web-server/src/test/resources/custom_po_resource/test_fr.po ================================================ msgid "test key" msgstr "Valeur modifiée" ================================================ FILE: bpm/bonita-web-server/src/test/resources/i18n/other_translations_fr.po ================================================ msgid "web site title" msgstr "Bienvenue dans Bonita Portal 7+" ================================================ FILE: bpm/bonita-web-server/src/test/resources/i18n/portal_fr.po ================================================ # only here to make test org.bonitasoft.console.common.server.page.PageContextHelperTest work msgid "test key" msgstr "test value" ================================================ FILE: bpm/bonita-web-server/src/test/resources/i18n/test_fr.po ================================================ msgid "test key" msgstr "Valeur de test en français" ================================================ FILE: bpm/bonita-web-server/src/test/resources/invalidCustomPage/descriptor.properties ================================================ contentType=theme ================================================ FILE: bpm/bonita-web-server/src/test/resources/invalidCustomPage/resources/style.css ================================================ ================================================ FILE: bpm/bonita-web-server/src/test/resources/invalidFormPage/page.properties ================================================ contentType=form ================================================ FILE: bpm/bonita-web-server/src/test/resources/invalidFormPage/resources/index.js ================================================ ================================================ FILE: bpm/bonita-web-server/src/test/resources/invalidThemePage/page.properties ================================================ contentType=theme ================================================ FILE: bpm/bonita-web-server/src/test/resources/invalidThemePage/resources/style.css ================================================ ================================================ FILE: bpm/bonita-web-server/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: bpm/bonita-web-server/src/test/resources/myRestAPI-1.0.0-SNAPSHOT/page.properties ================================================ name=custompage_myRestAPI displayName=My REST API description=REST API to manage resourceName contentType=apiExtension apiExtensions=myRestAPI myRestAPI.method=GET myRestAPI.pathTemplate=myRestAPI myRestAPI.className=com.compagny.rest.api.MyController myRestAPI.permissions=myPermission ================================================ FILE: bpm/bonita-web-server/src/test/resources/org/bonitasoft/console/common/server/page/file.css ================================================ html, body { margin: 0; padding: 0; } ================================================ FILE: bpm/bonita-web-server/src/test/resources/org/bonitasoft/console/common/server/page/my_html_page_v_6/index.html ================================================ ================================================ FILE: bpm/bonita-web-server/src/test/resources/org/bonitasoft/console/common/server/page/my_html_page_v_7/resources/index.html ================================================ ================================================ FILE: bpm/bonita-web-server/src/test/resources/org/bonitasoft/console/common/server/page/page.properties ================================================ #The name must start with 'custompage_' name=custompage_restApi displayName=aDisplayName description=aDescription resources=[] contentType=apiExtension apiExtensions=restResource1,restResource2 restResource1.method=GET restResource1.pathTemplate=restApiGet restResource1.classFileName=restResource1.groovy restResource1.permissions=permission1 restResource2.method=POST restResource2.pathTemplate=restApiPost restResource2.classFileName=restResource1.groovy restResource2.permissions=permission2,permission3 ================================================ FILE: bpm/bonita-web-server/src/test/resources/org/bonitasoft/web/rest/server/api/bpm/flownode/contract.json ================================================ { "constraints":[ {"name":"aRule","expression":"an expression","explanation":"an explanation","inputNames":[]} ], "inputs":[ {"description":"aDescription","name":"anInput","multiple":false,"type":"TEXT","inputs":[]}, {"description":"description","name":"complexInput","multiple":true,"type":null,"inputs":[ {"description":"aDescription","name":"anInput","multiple":false,"type":"TEXT","inputs":[]} ]} ] } ================================================ FILE: bpm/bonita-web-server/src/test/resources/org/bonitasoft/web/rest/server/api/bpm/flownode/timerEventTriggerInstance.json ================================================ { "executionDate": 9223372036854775807 } ================================================ FILE: bpm/bonita-web-server/src/test/resources/org/bonitasoft/web/rest/server/api/bpm/flownode/timerEventTriggerInstances.json ================================================ [ { "eventInstanceId": 2, "eventInstanceId_string": "2", "id": 1, "id_string": "1", "executionDate": 123, "eventInstanceName": "name" } ] ================================================ FILE: bpm/bonita-web-server/src/test/resources/org/bonitasoft/web/rest/server/api/bpm/process/contract.json ================================================ { "constraints":[ {"name":"aRule","expression":"an expression","explanation":"an explanation","inputNames":[]} ], "inputs":[ {"description":"aDescription","name":"anInput","multiple":false,"type":"TEXT","inputs":[]}, {"description":"description","name":"complexInput","multiple":true,"type":null,"inputs":[ {"description":"aDescription","name":"anInput","multiple":false,"type":"TEXT","inputs":[]} ]} ] } ================================================ FILE: bpm/bonita-web-server/src/test/resources/org/bonitasoft/web/rest/server/api/extension/page.properties ================================================ # api extensions list apiExtensions=myGetResource,myPostResource,MyPutResource,myDeleteResource,myPostResourceB,myCompiledRestApi,myCompiledRestApi2 #http method myGetResource.method=GET # resulting url wille be ../API/extension/helloWorld myGetResource.pathTemplate=helloWorld # implementation file that will serve this resource myGetResource.classFileName=Index.groovy myPostResource.method=POST myPostResource.pathTemplate=myPostResource # Empty className -> classFileName should be used myPostResource.className= myPostResource.classFileName=PostResource.groovy myPostResourceB.method=POST myPostResourceB.pathTemplate=myPostResourceB myPostResourceB.classFileName=PostResourceB.groovy myCompiledRestApi.method=GET myCompiledRestApi.pathTemplate=myCompiledRestApi myCompiledRestApi.className=com.company.MyController myCompiledRestApi2.method=GET myCompiledRestApi2.pathTemplate=myCompiledRestApi2 myCompiledRestApi2.className=com.company.MyController myCompiledRestApi2.classFileName=MyController.groovy ================================================ FILE: bpm/bonita-web-server/src/test/resources/resources-permissions-mapping.properties ================================================ GET|bpm/identity [UserVisualization, groupVisualization] ================================================ FILE: bpm/bonita-web-server/src/test/resources/test.po ================================================ msgid "" msgstr "" "Project-Id-Version: bonita-bpm-60\n" "POT-Creation-Date: 2013-06-07 14:58+0100\n" "PO-Revision-Date: 2024-01-01 00:00\n" "Last-Translator: \n" "Language-Team: English\n" "Language: en_US\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.5.5\n" "X-Poedit-KeywordsList: _;gettext;gettext_noop\n" "X-Poedit-Basepath: .\n" "X-Poedit-SourceCharset: UTF-8\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Poedit-SearchPath-0: common\n" "X-Poedit-SearchPath-1: console\n" "X-Poedit-SearchPath-2: platform\n" "X-Poedit-SearchPath-3: toolkit\n" #: toolkit/toolkit-view/src/main/java/org/bonitasoft/web/toolkit/client/ui/page/SupportCodePage.java:42 msgid "" "Multiline\n" "Message\n" "Id" msgstr "" "This is a multilines\n" "Message" #: toolkit/toolkit-view/src/main/java/org/bonitasoft/web/toolkit/client/ui/page/SupportCodePage.java:43 msgid "theId" msgstr "theMsg" ================================================ FILE: build.gradle ================================================ plugins { id "org.sonarqube" version "7.2.3.7755" alias(libs.plugins.taskInfo) alias(libs.plugins.testRetry) } sonarqube { properties { property "sonar.projectKey", "bonitasoft_bonita-engine" property "sonar.organization", "bonitasoft" property "sonar.host.url", "https://sonarcloud.io" } } apply from: "common.gradle" ================================================ FILE: buildSrc/build.gradle ================================================ plugins { id("groovy") id("java-gradle-plugin") id "com.github.johnrengelman.shadow" version "8.1.1" } repositories { mavenCentral() gradlePluginPortal() } dependencies { api "com.github.johnrengelman:shadow:8.1.1" api "com.adarshr:gradle-test-logger-plugin:3.2.0" // for database tests: api "com.bmuschko:gradle-docker-plugin:6.7.0" } gradlePlugin { plugins { bonitaShade { id = "bonita-shade" implementationClass = "org.bonitasoft.engine.gradle.ShadePlugin" } bonitaTests { // Add this plugin to every module that has *IT.java (and only those modules): id = "bonita-tests" implementationClass = "org.bonitasoft.engine.gradle.TestsPlugin" } bonitaHttpTests { id = "bonita-http-test" implementationClass = "org.bonitasoft.engine.gradle.HttpTestPlugin" } bonitaDatabaseTest { id = "bonita-docker-database" implementationClass = "org.bonitasoft.engine.gradle.docker.DockerDatabasePlugin" } } } ================================================ FILE: buildSrc/settings.gradle ================================================ dependencyResolutionManagement { versionCatalogs { libs { from(files("../gradle/libs.versions.toml")) } } } ================================================ FILE: buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/HttpTestPlugin.groovy ================================================ package org.bonitasoft.engine.gradle import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.artifacts.VersionCatalogsExtension import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.testing.Test /** * @author Emmanuel Duchastenier */ class HttpTestPlugin implements Plugin { @Override void apply(Project project) { def httpTests = project.extensions.create("httpTests", TestsExtension) TaskProvider httpIntegrationTests = project.tasks.register("httpIT", Test) { group = "Verification" description = "Runs all integration tests to remote HTTP server." } project.afterEvaluate { if (httpTests.integrationTestsSuite) { httpIntegrationTests.configure { include(httpTests.integrationTestsSuite) } } else { // to be able to run only one class with right-click in IDE // Still not possible for now, as *IT classes are not yet in src/test/java folder httpIntegrationTests.configure { include("**/*IT.class") } } // So that TestEngine start in HTTP instead of local: httpIntegrationTests.configure { jvmArgs("-Dorg.bonitasoft.engine.access.mode=http") } // Add HTTP server dependencies only for this task: project.configurations { httpTestConfig } project.dependencies { def versionCatalog = project.extensions.getByType(VersionCatalogsExtension.class).named("libs") httpTestConfig(versionCatalog.findLibrary("jettyServer").get()) httpTestConfig(versionCatalog.findLibrary("jettyServlet").get()) } httpIntegrationTests.configure { classpath += project.configurations.httpTestConfig } JVMModifier.setTestJVM(project, httpIntegrationTests) JVMModifier.setJvmArgs(project, httpIntegrationTests) } } } ================================================ FILE: buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/JVMModifier.groovy ================================================ package org.bonitasoft.engine.gradle import org.gradle.api.Project import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.testing.Test import org.gradle.jvm.toolchain.JavaLanguageVersion import org.gradle.jvm.toolchain.JavaToolchainService /** * @author Emmanuel Duchastenier */ class JVMModifier { public static final String TEST_JVM_VERSION = "test.jvm.version" static void setJvmArgs(Project project, TaskProvider task) { ArrayList jvmArgs = ["-Dorg.bonitasoft.h2.database.dir=./build/h2databasedir"] def property = project.property('org.gradle.jvmargs') if (property) { jvmArgs.addAll property.toString().split(" ") } def sysProperty = System.getProperty("org.gradle.jvmargs") if (sysProperty) { jvmArgs.addAll sysProperty.split(" ") } System.getProperties().each { p -> if (p.key.contains('sysprop.bonita') || p.key.startsWith('bonita.runtime')) { jvmArgs.add("-D${p.key}=${p.value}") } } project.logger.info("jvmArgs: $jvmArgs") task.configure { it.jvmArgs(jvmArgs) } } static void setTestJVM(Project project, TaskProvider task) { if (project.hasProperty(TEST_JVM_VERSION)) { def alternateJvm = project.property(TEST_JVM_VERSION) project.logger.info("Parameter '$TEST_JVM_VERSION' detected...") // to work around error "Toolchain from `executable` property does not match toolchain from `javaLauncher` property", // when upgrading to Gradle 8: JavaToolchainService service = project.getExtensions().getByType(JavaToolchainService.class); task.configure { javaLauncher.set(service.launcherFor { languageVersion = JavaLanguageVersion.of(alternateJvm as int) }) } project.logger.info("${project.name} will use alternate JVM '$alternateJvm' (${task.configure { javaLauncher.get().executablePath.asFile.absolutePath }}) to run $task") } } } ================================================ FILE: buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/PomUtils.groovy ================================================ package org.bonitasoft.engine.gradle import org.gradle.api.publish.maven.MavenPom /** * Utility class to generate Community information necessary to publish to * Maven Central. * * @author Emmanuel Duchastenier */ class PomUtils { static void pomCommunityPublication(MavenPom pom) { pom.with { url = 'https://community.bonitasoft.com/' organization { name = 'Bonitasoft S.A.' url = 'https://community.bonitasoft.com/' } developers { developer { id = "bonita-engine-team" name = "The Bonita Engine Development Team" organization = "Bonitasoft S.A." organizationUrl = "http://community.bonitasoft.com/" } } scm { connection = "scm:git:http://github.com/bonitasoft/bonita-engine.git" developerConnection = "scm:git:git@github.com:bonitasoft/bonita-engine.git" url = "http://github.com/bonitasoft/bonita-engine" } licenses { license { name = 'GNU Lesser General Public License Version 2.1' url = 'http://www.gnu.org/licenses/lgpl-2.1.html' distribution = 'repo' } } } } } ================================================ FILE: buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/SetupE2ETask.groovy ================================================ package org.bonitasoft.engine.gradle import org.gradle.api.tasks.* import org.gradle.internal.os.OperatingSystem import static org.gradle.api.tasks.PathSensitivity.NONE @CacheableTask class SetupE2ETask extends Exec { @PathSensitive(NONE) @InputFile File scriptFile @OutputFile File outputFile = project.layout.buildDirectory.file("setup-e2e-output.log").get().asFile SetupE2ETask() { onlyIf { OperatingSystem.current().isLinux() } } @TaskAction @Override void exec() { commandLine 'sh', scriptFile.path // Ensure the parent directory of the output file exists outputFile.parentFile.mkdirs() // Redirect standard output to a file standardOutput = new FileOutputStream(outputFile) super.exec() } } ================================================ FILE: buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/ShadeDependency.groovy ================================================ package org.bonitasoft.engine.gradle import groovy.transform.EqualsAndHashCode import groovy.transform.ToString @ToString @EqualsAndHashCode class ShadeDependency { String group String name } ================================================ FILE: buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/ShadeExtension.groovy ================================================ package org.bonitasoft.engine.gradle import org.gradle.api.Project class ShadeExtension { List includes = [] List excludes = [] Map> libExclusions = [:] as Map def include(Map artifact) { includes.add(new ShadeDependency(artifact)) } def exclude(Project project) { excludes.add(project) } def excludeLibs(String refereeLib, ShadeDependency... libsToExclude) { libExclusions.put(refereeLib, Arrays.asList(libsToExclude)) } } ================================================ FILE: buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/ShadePlugin.groovy ================================================ package org.bonitasoft.engine.gradle import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.artifacts.ResolvedDependency import org.gradle.api.artifacts.component.ProjectComponentIdentifier import org.gradle.api.publish.maven.MavenPublication import org.gradle.jvm.tasks.Jar class ShadePlugin implements Plugin { private static final String PLATFORM_CONFIGURATION_NAME = "platform-runtime" @Override void apply(Project project) { project.plugins.apply("com.github.johnrengelman.shadow") project.plugins.apply("maven-publish") def extension = project.extensions.create("shade", ShadeExtension) project.jar { archiveClassifier = 'original' } project.afterEvaluate { project.shadowJar { archiveClassifier = "" // we replace the original jar by the shadow jar dependencies { include({ if (!project.ext.has("shadedDependencies")) { // "shadedDependencies" property is used for display only project.ext.shadedDependencies = [] as Set } def allProjectsAlreadyShaded = getProjectsAlreadyShaded(project, extension) if (shouldBeIncludedInShade(project, it, extension, allProjectsAlreadyShaded)) { project.ext.shadedDependencies.add(it) return true } return false }) } doFirst { project.logger.info("Shading for project {} : ", project.path) project.ext.projectsToShade.each { project.logger.info(" - {}", it) } project.logger.info("Effective shading of project {} : ", project.path) project.ext.shadedDependencies.each { project.logger.info(" - {}", it) } } } // not mandatory. Only here to have the production jars built when running `gradle build`: project.tasks.named("build") { dependsOn project.tasks.named("shadowJar") } project.javadoc { source { getShadedProjects(project, extension, getProjectsAlreadyShaded(project, extension)).collect { it.sourceSets.main.allJava } } classpath = project.files({ getShadedProjects(project, extension, getProjectsAlreadyShaded(project, extension)).collect { it.sourceSets.main.compileClasspath } }) options.addStringOption('Xdoclint:none', '-quiet') options.addBooleanOption("author", true) // FIXME update studio test org.bonitasoft.studio.tests.engine.TestJavaDoc.testHasJavaDoc } project.tasks.register("sourcesJar", Jar) { from { getShadedProjects(project, extension, getProjectsAlreadyShaded(project, extension)).collect { it.sourceSets.main.allJava } } archiveClassifier = 'sources' } project.tasks.register("javadocJar", Jar) { from project.javadoc archiveClassifier = 'javadoc' } project.publishing.publications { shadow(MavenPublication) { publication -> project.shadow.component(publication) PomUtils.pomCommunityPublication(publication.getPom()) pom.withXml { def allProjectsAlreadyShaded = getProjectsAlreadyShaded(project, extension) Set inPom = getPomDependencies(project, extension, allProjectsAlreadyShaded, true) project.logger.info("Include in pom:") inPom.each { project.logger.info(" - {}", it) } def rootNode = asNode() Node dependencies = rootNode.children().find { Node child -> child.name() == "dependencies" } inPom.each { gradleDep -> Node dependency = dependencies .appendNode("dependency") dependency.appendNode("groupId", gradleDep.moduleGroup) dependency.appendNode("artifactId", gradleDep.moduleName) dependency.appendNode("version", gradleDep.moduleVersion) if (extension.libExclusions.containsKey(gradleDep.moduleName)) { Node es = dependency.appendNode('exclusions') List excludes = extension.libExclusions.get(gradleDep.moduleName) excludes.each { Node e = es.appendNode('exclusion') e.appendNode('groupId', it.group) e.appendNode('artifactId', it.name) } } } } artifact project.sourcesJar artifact project.javadocJar } } } } private boolean shouldBeIncludedInShade(Project project, ResolvedDependency currentDependency, ShadeExtension extension, Set allProjectsAlreadyShaded) { Set projectsToShade = getShadedProjects(project, extension, allProjectsAlreadyShaded) if (extension.includes.contains(new ShadeDependency(group: currentDependency.moduleGroup, name: currentDependency.moduleName))) { return true } def projectDep = getAssociatedProjectFromDependency(project, currentDependency) if (projectDep == null) { return false // dependency is not a project } return projectsToShade.contains(projectDep) } /** * get the list of projects to shade */ private Set getShadedProjects(Project project, ShadeExtension extension, Set allProjectsAlreadyShaded) { if (!project.ext.has("projectsToShade")) { project.ext.projectsToShade = getAllProjectsToShade(project, extension, allProjectsAlreadyShaded) } project.ext.projectsToShade } private void printTree(Project project, String indentation) { project.configurations.compile.resolvedConfiguration.firstLevelModuleDependencies.each { def dependencyAsProject = getAssociatedProjectFromDependency(project, it) if (dependencyAsProject) { println indentation + dependencyAsProject.name printTree(dependencyAsProject, indentation + "--") } } } /** * get the list of project that are already shaded by other shade */ private Set getProjectsAlreadyShaded(Project rootProject, ShadeExtension extension) { if (!rootProject.ext.has("projectsAlreadyShaded")) { // add property "projectsAlreadyShaded" to act like a cache rootProject.ext.projectsAlreadyShaded = getAllProjectsAlreadyShaded(rootProject, extension) } rootProject.ext.projectsAlreadyShaded } /** * get the Project (object) from the ResolvedDependency (object) */ private Project getAssociatedProjectFromDependency(Project project, ResolvedDependency dependency) { def artifacts = dependency.getModuleArtifacts() if (artifacts.isEmpty()) { //it happens when a dependency is a bom pulled from gradle's artifacts metadata (variant) return null } def identifier = artifacts.first().id.componentIdentifier if (!(identifier instanceof ProjectComponentIdentifier)) { return null } return project.project(identifier.projectPath) } private boolean isAShadeProject(Project it) { it.plugins.find { it instanceof ShadePlugin } } private Set getAllProjectsToShade(Project project, ShadeExtension extension, Set allProjectsAlreadyShaded) { Set allProjects = [] project.configurations.runtimeClasspath.resolvedConfiguration.firstLevelModuleDependencies.forEach { def projectDependency = getAssociatedProjectFromDependency(project, it) if (projectDependency) { // is an engine project (service) if (!isAShadeProject(projectDependency) && !extension.excludes.contains(projectDependency) && !allProjectsAlreadyShaded.contains(projectDependency)) { allProjects.add(projectDependency) allProjects.addAll(getAllProjectsToShade(projectDependency, extension, allProjectsAlreadyShaded)) } } } allProjects } private Set getAllProjectsAlreadyShaded(Project project, ShadeExtension extension) { Set allProjects = [] // Take all declared compilation dependencies: project.configurations.runtimeClasspath.resolvedConfiguration.firstLevelModuleDependencies.forEach { def projectDependency = getAssociatedProjectFromDependency(project, it) if (projectDependency) { if (isAShadeProject(projectDependency)) { // this dependency is a shade project //all dependencies of this shade project are already shaded, let's add them all to the list: allProjects.addAll(getAllProjectsToShade(projectDependency, extension, [] as Set)) } else { allProjects.addAll(getAllProjectsAlreadyShaded(projectDependency, extension)) } } } allProjects } private Set getPomDependencies(Project project, ShadeExtension extension, Set allProjectsAlreadyShaded, boolean isRootProject) { Set allDependencies = [] def allScopes = project.configurations.runtimeClasspath.resolvedConfiguration.firstLevelModuleDependencies allScopes.forEach { Project projectDependency = getAssociatedProjectFromDependency(project, it) if (projectDependency) { if (allProjectsAlreadyShaded.contains(projectDependency)) { return // no need to go further } if (extension.excludes.contains(projectDependency)) { //excluded from shade, add this project but NOT its dependencies: allDependencies.add(it) } else if (isAShadeProject(projectDependency)) { // the project is a shaded project (e.g. bonita-common-sp in bonita-server-sp) // only add it if it is not a shade pulled by transitivity: if (isRootProject) { allDependencies.add(it) } else { project.logger.info(" Shade POM generation: ignoring {}, as it is pulled as transitive shade project", projectDependency.name) } } else { // do not add it: project is shaded inside this project allDependencies.addAll(getPomDependencies(projectDependency, extension, allProjectsAlreadyShaded, false)) } } else { // also add transitive dependencies of third-party libs: allDependencies.addAll(getTransitiveThirdPartyDependencies(it, "", project, extension)) } } // remove all dependencies that are in the configuration "platform-runtime". it's used by gradle to resolve versions (like a bom) // those dependencies should be imported in the dependency management instead. It is manually done right now (see `bonita-engine` bom) allDependencies.findAll { it.configuration != PLATFORM_CONFIGURATION_NAME } } /** * Returns a Set of the passed ResolvedDependency itself + all its children, recursively * @param indent indentation string, for display purposes */ private Set getTransitiveThirdPartyDependencies(ResolvedDependency current, String indent, Project project, ShadeExtension extension) { def res = [] as Set project.logger.debug(" Shade POM generation: adding ${indent}${current.name}") // if the external dependency is shaded, do not add it in the pom, but add its dependencies if (!extension.includes.contains(new ShadeDependency(group: current.moduleGroup, name: current.moduleName))) { res.add(current) } def thirdPartyExclusion = extension.libExclusions.get(current.moduleName) current.getChildren().forEach { child -> if (!thirdPartyExclusion || !thirdPartyExclusion.contains(new ShadeDependency(group: child.moduleGroup, name: child.moduleName))) { res.addAll(getTransitiveThirdPartyDependencies(child, indent + " ", project, extension)) } } return res } } ================================================ FILE: buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/TestsExtension.groovy ================================================ package org.bonitasoft.engine.gradle class TestsExtension { String testPattern = "**/*Test.class" String integrationTestsPattern = "**/*IT.class" String integrationTestsSuite } ================================================ FILE: buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/TestsPlugin.groovy ================================================ package org.bonitasoft.engine.gradle import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.testing.Test class TestsPlugin implements Plugin { @Override void apply(Project project) { def tests = project.extensions.create("tests", TestsExtension) TaskProvider integrationTest = project.tasks.register("integrationTest", Test) { group = "Verification" description = "Runs all integration tests on H2 database." } project.afterEvaluate { JVMModifier.setTestJVM(project, integrationTest) JVMModifier.setJvmArgs(project, integrationTest) if (tests.integrationTestsSuite) { integrationTest.configure { include(tests.integrationTestsSuite) } } else { integrationTest.configure { include(tests.integrationTestsPattern) } } integrationTest.configure { systemProperty("bonita.version", project.version) } TaskProvider testTask = project.tasks.named("test", Test) if (testTask) { JVMModifier.setTestJVM(project, testTask) JVMModifier.setJvmArgs(project, testTask) testTask.configure { include(tests.testPattern) } } } } } ================================================ FILE: buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/docker/DatabaseExtraConfiguration.groovy ================================================ package org.bonitasoft.engine.gradle.docker import groovy.transform.Canonical import org.gradle.api.Project @Canonical class DatabaseExtraConfiguration { /** * Include an additional project in the test classpath */ Project includeTestProject /** * Excludes test class patterns (e.g. '**/*Test.class') applied to this database vendor. * It can be combined with {@link #excludeTags}. */ List excludes /** * Excludes tests marked by JUnit tags (e.g. 'my-tag') applied to this database vendor. * It can be combined with {@link #excludes}. */ List excludeTags /** * Enable or disable the execution of the test task for this database configuration */ boolean enabled = false def excludes(String... excludes) { this.excludes = [] this.excludes.addAll(excludes) } def exclude(String excludes) { if (this.excludes == null) { this.excludes = [] } this.excludes.add(excludes) } def excludeTags(String... tags) { this.excludeTags = [] this.excludeTags.addAll(tags) } def excludeTag(String tag) { if (this.excludeTags == null) { this.excludeTags = [] } this.excludeTags.add(tag) } } ================================================ FILE: buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/docker/DatabasePluginExtension.groovy ================================================ /** * Copyright (C) 2015 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.gradle.docker import org.gradle.api.Action class DatabasePluginExtension { /** * Include test class patterns applied to database vendors */ List includes /** * Exclude test class patterns applied to all database vendors */ List excludes /** * Extra configuration for the postgres database */ DatabaseExtraConfiguration postgres = new DatabaseExtraConfiguration(enabled: true) /** * Extra configuration for the mysql database */ DatabaseExtraConfiguration mysql = new DatabaseExtraConfiguration() /** * Extra configuration for the oracle database */ DatabaseExtraConfiguration oracle = new DatabaseExtraConfiguration() /** * Extra configuration for the sqlserver database */ DatabaseExtraConfiguration sqlserver = new DatabaseExtraConfiguration() def includes(String... includes) { this.includes = [] this.includes.addAll(includes) } def include(String include) { if (this.includes == null) { this.includes = [] } this.includes.add(include) } def excludes(String... excludes) { this.excludes = [] this.excludes.addAll(excludes) } def exclude(String exclude) { if (this.excludes == null) { this.excludes = [] } this.excludes.add(exclude) } def postgres(Action action) { action.execute(postgres) } def mysql(Action action) { action.execute(mysql) } def oracle(Action action) { action.execute(oracle) } def sqlserver(Action action) { action.execute(sqlserver) } } ================================================ FILE: buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/docker/DbParser.groovy ================================================ /** * Copyright (C) 2018 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.gradle.docker import groovy.transform.EqualsAndHashCode import groovy.transform.ToString class DbParser { static DbConnectionSettings extractDbConnectionSettings(String dburl) { if (dburl.contains("sqlserver")) { return extractSqlServerDbConnectionSettings(dburl) } else if (dburl.contains("oracle")) { return extractOracleDbConnectionSettings(dburl) } return extractGenericDbConnectionSettings(dburl) } private static DbConnectionSettings extractGenericDbConnectionSettings(String dburl) { DbConnectionSettings settings = new DbConnectionSettings() settings.dbUrl = dburl def parsedUrl = (dburl =~ /(jdbc:\w+:\/\/)([\w\d\.-]+):(\d+)\/([\w\-_\d]+).*/) settings.serverName = parsedUrl[0][2] settings.portNumber = parsedUrl[0][3] settings.databaseName = parsedUrl[0][4] settings.genericUrl = parsedUrl[0][1] + settings.serverName + ":" + settings.portNumber + "/" settings } private static DbConnectionSettings extractOracleDbConnectionSettings(String dburl) { DbConnectionSettings settings = new DbConnectionSettings() settings.dbUrl = dburl def parsedUrl = (dburl =~ /(jdbc:.*:@\/\/)([\w\d\.-]+):(\d+)\/([\w\-_\.\d]+).*/) settings.serverName = parsedUrl[0][2] settings.portNumber = parsedUrl[0][3] settings.databaseName = parsedUrl[0][4] settings.genericUrl = dburl settings } private static DbConnectionSettings extractSqlServerDbConnectionSettings(String dburl) { DbConnectionSettings settings = new DbConnectionSettings() settings.dbUrl = dburl def parsedUrl = (dburl =~ /(jdbc:\w+:\/\/)([\w\d\.-]+):(\d+);database=([\w\-_\d]+).*/) settings.serverName = parsedUrl[0][2] settings.portNumber = parsedUrl[0][3] settings.databaseName = parsedUrl[0][4] settings.genericUrl = parsedUrl[0][1] + settings.serverName + ":" + settings.portNumber settings } @ToString @EqualsAndHashCode static class DbConnectionSettings { String dbUrl String serverName String portNumber String databaseName String genericUrl } } ================================================ FILE: buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/docker/DockerDatabaseContainerTasksCreator.groovy ================================================ /* * 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.gradle.docker import com.bmuschko.gradle.docker.tasks.container.DockerCreateContainer import com.bmuschko.gradle.docker.tasks.container.DockerInspectContainer import com.bmuschko.gradle.docker.tasks.container.DockerRemoveContainer import com.bmuschko.gradle.docker.tasks.container.DockerStartContainer import com.bmuschko.gradle.docker.tasks.container.extras.DockerWaitHealthyContainer import com.bmuschko.gradle.docker.tasks.image.DockerPullImage import org.bonitasoft.engine.gradle.JVMModifier import org.gradle.api.Project import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.Zip import org.gradle.api.tasks.testing.Test /** * Gradle plugin to start docker database containers and perform tests against them */ class DockerDatabaseContainerTasksCreator { private static String getDockerHost(def project) { def dockerHost = System.getenv('DOCKER_HOST') if (dockerHost?.trim()) { project.logger.quiet("using DOCKER_HOST: ${dockerHost}") return new URI(dockerHost).host } return 'localhost' } private static final String SYS_PROP_DB_URL = 'db.url' private static final String SYS_PROP_DB_USER = 'db.user' private static final String SYS_PROP_DB_PASSWORD = 'db.password' def static createTasks(Project project, DatabasePluginExtension extension, List vendors) { // required to have the environment correctly setup: see https://github.com/bmuschko/gradle-docker-plugin/issues/575#issuecomment-383704012 if (!project.rootProject.plugins.hasPlugin('com.bmuschko.docker-remote-api')) { project.rootProject.plugins.apply('com.bmuschko.docker-remote-api') } project.plugins.apply('com.bmuschko.docker-remote-api') vendors.each { vendor -> if (!extension."${vendor.name}".enabled) { return // do not create docker tasks for disabled database configurations } def uniqueName = vendor.name.capitalize() DbParser.DbConnectionSettings dbConnectionSettings = new DbParser.DbConnectionSettings() DbParser.DbConnectionSettings bdmDbConnectionSettings = new DbParser.DbConnectionSettings() def pullImage = project.tasks.register("pull${uniqueName}Image", DockerPullImage) { description "Pull docker image for $uniqueName db vendor" group null // do not show task when running `gradle tasks` image = vendor.image if (vendor.registryUrlEnv != null) { registryCredentials.with { url = System.getenv(vendor.registryUrlEnv) username = System.getenv(vendor.registryUsernameEnv) password = System.getenv(vendor.registryPasswordEnv) } } } def createContainer = project.tasks.register("create${uniqueName}Container", DockerCreateContainer) { description "Create a docker container for $uniqueName db vendor" group null // do not show task when running `gradle tasks` if (project.hasProperty("docker-container-alias")) { containerName = project.getProperty("docker-container-alias") } hostConfig.portBindings = [":$vendor.portBinding"] targetImageId pullImage.get().getImage() if ('oracle' == vendor.name) { // 1Go hostConfig.shmSize = 1099511627776 } hostConfig.autoRemove = true } def startContainer = project.tasks.register("start${uniqueName}Container", DockerStartContainer) { description "Start a docker container for $uniqueName db vendor" group "docker" targetContainerId createContainer.get().getContainerId() } def waitForContainerStartup = project.tasks.register("waitFor${uniqueName}ContainerStartup", DockerWaitHealthyContainer) { description "Wait for a started docker container for $vendor.name db vendor to be healthy" group null // do not show task when running `gradle tasks` targetContainerId startContainer.get().getContainerId() // Oracle requires more time due to initialization, other databases are faster awaitStatusTimeout = ('oracle' == vendor.name) ? 480 : 360 } def inspectContainer = project.tasks.register("inspect${uniqueName}ContainerUrl", DockerInspectContainer) { description = "Get url of a docker container for $uniqueName db vendor" group = null // do not show task when running `gradle tasks` targetContainerId(startContainer.get().getContainerId()) onNext { it.networkSettings.ports.getBindings().each { exposedPort, bindingArr -> if (exposedPort.port == vendor.portBinding) { int portBinding = bindingArr.first().hostPortSpec as int def dockerHost = getDockerHost(project) dbConnectionSettings.dbUrl = vendor.name == "oracle" ? String.format(vendor.uriTemplate, dockerHost, portBinding) : String.format(vendor.uriTemplate, dockerHost, portBinding, "bonita") dbConnectionSettings.serverName = dockerHost dbConnectionSettings.portNumber = portBinding bdmDbConnectionSettings.dbUrl = vendor.name == "oracle" ? String.format(vendor.uriTemplate, dockerHost, portBinding) : String.format(vendor.uriTemplate, dockerHost, portBinding, "business_data") bdmDbConnectionSettings.serverName = dockerHost bdmDbConnectionSettings.portNumber = portBinding project.logger.quiet("db.url set to ${dbConnectionSettings.dbUrl}") } } } } def removeContainer = project.tasks.register("remove${uniqueName}Container", DockerRemoveContainer) { description "Remove a docker container for $uniqueName db vendor" group "docker" force = true removeVolumes = true targetContainerId createContainer.get().getContainerId() } TaskProvider databaseTestTask = project.tasks.register("${vendor.name}DatabaseTest", Test) { group = "Verification" description = "Runs integration test suite on $vendor.name database." systemProperty "bonita.version", project.version jvmArgs += ['--add-opens', 'java.base/java.util=ALL-UNNAMED', '--add-opens', 'java.base/java.lang=ALL-UNNAMED', '-Dfile.encoding=UTF-8'] if (extension."${vendor.name}"?.includeTestProject) { testClassesDirs += extension."${vendor.name}".includeTestProject.sourceSets.test.output.classesDirs classpath += extension."${vendor.name}".includeTestProject.sourceSets.test.runtimeClasspath } classpath += project.files(project.configurations.drivers) if (extension."${vendor.name}"?.excludes) { exclude(extension."${vendor.name}".excludes) } if (extension."${vendor.name}"?.excludeTags) { useJUnitPlatform { excludeTags = extension."${vendor.name}".excludeTags } } onlyIf { extension."${vendor.name}"?.enabled } doFirst { String dbUrl = project.hasProperty(SYS_PROP_DB_URL) ? project.property(SYS_PROP_DB_URL) : dbConnectionSettings.dbUrl def connectionSettings = DbParser.extractDbConnectionSettings(dbUrl) def dbValues = [ "sysprop.bonita.db.vendor" : vendor.name, "sysprop.bonita.bdm.db.vendor": vendor.name, "db.url" : dbUrl, "db.user" : project.hasProperty('db.user') ? project.property(SYS_PROP_DB_URL) : (System.getProperty(SYS_PROP_DB_USER) ? System.getProperty(SYS_PROP_DB_USER) : 'bonita'), "db.password" : project.hasProperty('db.password') ? project.property(SYS_PROP_DB_URL) : (System.getProperty(SYS_PROP_DB_PASSWORD) ? System.getProperty(SYS_PROP_DB_PASSWORD) : 'bpm'), "bdm.db.url" : bdmDbConnectionSettings.dbUrl, "bdm.db.user" : project.hasProperty('db.user') ? project.property(SYS_PROP_DB_URL) : (System.getProperty(SYS_PROP_DB_USER) ? System.getProperty(SYS_PROP_DB_USER) : 'business_data'), "db.server.name" : connectionSettings.serverName, "db.server.port" : connectionSettings.portNumber, "db.database.name" : connectionSettings.databaseName ] if ('oracle' == vendor.name) { // fix for https://community.oracle.com/message/3701989 // http://www.thezonemanager.com/2015/07/whats-so-special-about-devurandom.html dbValues.put('java.security.egd', 'file:/dev/./urandom') // fix for ORA-01882 dbValues.put('user.timezone', 'UTC') } // /!\ warning: do NOT use setSystemProperties, as it would erase existing system properties. // rather use systemProperties to merge the new ones with the existing ones. systemProperties(dbValues) } } TaskProvider zipReport = project.tasks.register("zip${vendor.name}DatabaseTestReport" as String, Zip) { archiveFileName = databaseTestTask.get().reports.html.outputLocation.get().getAsFile().name + ".zip" destinationDirectory = databaseTestTask.get().reports.html.outputLocation.get().getAsFile().parentFile from databaseTestTask.get().reports.html.outputLocation.get().getAsFile() } project.afterEvaluate { JVMModifier.setTestJVM(project, databaseTestTask) JVMModifier.setJvmArgs(project, databaseTestTask) databaseTestTask.configure { if (extension.includes) { include(extension.includes) } if (extension.excludes) { exclude(extension.excludes) } } pullImage.configure { onlyIf { extension."${vendor.name}"?.enabled } } createContainer.configure { onlyIf { extension."${vendor.name}"?.enabled } } startContainer.configure { onlyIf { extension."${vendor.name}"?.enabled } } waitForContainerStartup.configure { onlyIf { extension."${vendor.name}"?.enabled } } inspectContainer.configure { onlyIf { extension."${vendor.name}"?.enabled } } removeContainer.configure { onlyIf { extension."${vendor.name}"?.enabled } } } if (createContainer) { createContainer.configure { dependsOn(pullImage) } } if (startContainer) { startContainer.configure { dependsOn(createContainer) finalizedBy(removeContainer) } } if (waitForContainerStartup) { waitForContainerStartup.configure { dependsOn(startContainer) } } if (inspectContainer) { inspectContainer.configure { dependsOn(waitForContainerStartup) } } if (databaseTestTask) { databaseTestTask.configure { dependsOn(inspectContainer) finalizedBy(zipReport) } } if (removeContainer) { removeContainer.configure { mustRunAfter(databaseTestTask) } } } } } ================================================ FILE: buildSrc/src/main/groovy/org/bonitasoft/engine/gradle/docker/DockerDatabasePlugin.groovy ================================================ package org.bonitasoft.engine.gradle.docker import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.artifacts.VersionCatalogsExtension /** * @author Emmanuel Duchastenier */ class DockerDatabasePlugin implements Plugin { @Override void apply(Project project) { project.configurations { drivers } driversConfiguration(project) def databaseIntegrationTest = project.extensions.create("databaseIntegrationTest", DatabasePluginExtension) project.afterEvaluate { DockerDatabaseContainerTasksCreator.createTasks(project, databaseIntegrationTest, getVendors()) if (!databaseIntegrationTest.includes) { println "No databaseIntegrationTest.include found. No tests to run!" } } } def driversConfiguration(project) { project.dependencies { // the following jdbc drivers are available for integration tests def versionCatalog = project.extensions.getByType(VersionCatalogsExtension.class).named("libs") drivers(versionCatalog.findLibrary("postgresql").get()) } } List getVendors() { return [ [name : 'postgres', image : 'bonitasoft/bonita-postgres:16.4', portBinding: 5432, uriTemplate: 'jdbc:postgresql://%s:%s/%s', ] ] } } ================================================ FILE: common.gradle ================================================ apply plugin: libs.plugins.taskInfo.get().pluginId allprojects { apply plugin: 'maven-publish' group = 'org.bonitasoft.engine' tasks.withType(Copy).configureEach { setDuplicatesStrategy(DuplicatesStrategy.INCLUDE) } publishing { repositories { if (project.hasProperty("altDeploymentRepository")) { def repoProperties = project.getProperties()."altDeploymentRepository".split("::") maven { name = repoProperties.first() url = repoProperties.last() //those credentials can be given using -PUsername and -PPassword see README.md credentials(PasswordCredentials) } } } } } subprojects { apply plugin: 'java-library' apply plugin: 'maven-publish' apply plugin: 'com.adarshr.test-logger' apply plugin: 'org.gradle.test-retry' repositories { mavenCentral() if (project.hasProperty("extraRepositories")) { def extraRepositories = project.getProperties().get("extraRepositories") extraRepositories.split(",").each { repo -> def repoProperties = repo.split("::") maven { name = repoProperties.first() url = repoProperties.last() //those credentials can be given using -PUsername and -PPassword see README.md credentials(PasswordCredentials) } } } maven { url="https://central.sonatype.com/repository/maven-snapshots/" mavenContent { snapshotsOnly() } } mavenLocal() } dependencies { testImplementation libs.junit5api testImplementation libs.mockitoJunitJupiter testCompileOnly libs.junit4 testRuntimeOnly libs.junitJupiterEngine // in Gradle 8, this declaration is mandatory. See https://docs.gradle.org/8.3/userguide/upgrading_version_8.html#manually_declaring_dependencies : testRuntimeOnly("org.junit.platform:junit-platform-launcher") // to support junit4 tests testRuntimeOnly libs.junitVintageEngine } java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } afterEvaluate { tasks.withType(AbstractCompile).configureEach { options.encoding = 'UTF-8' } } tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' options.fork = true } tasks.withType(Javadoc).configureEach { options.addStringOption('Xdoclint:none', '-quiet') options.encoding = 'UTF-8' } tasks.withType(DependencyReportTask).configureEach { group = "Documentation" description = "List runtime dependencies for a specified configuration" configurations = [project.configurations.runtimeClasspath] } // Configure all test tasks (test, integrationTest, httpIT, databaseTest) tasks.withType(Test).configureEach { useJUnitPlatform { includeEngines 'junit-jupiter', 'junit-vintage' } jvmArgs += ['--add-opens', 'java.base/java.util=ALL-UNNAMED', '--add-opens', 'java.base/java.lang=ALL-UNNAMED', '-Dfile.encoding=UTF-8'] if (!project.hasProperty("createTestReports")) { reports.html.required = false reports.junitXml.required = false } retry { // Test Retry Plugin Configuration // Can be overridden from command line: // -PtestRetryMaxRetries=5 (Gradle property - recommended) // -Dtest.retry.maxRetries=5 (System property) // -PtestRetryMaxFailures=20 (Max failures before stopping) // -Dtest.retry.maxFailures=20 (System property) // -PtestRetryFailOnFlaky=false (Don't fail on flaky tests) // // Examples: // ./gradlew integrationTest -PtestRetryMaxRetries=5 // ./gradlew iT -PtestRetryMaxRetries=0 # Disable retry // ./gradlew mysqlDatabaseTest -PtestRetryMaxRetries=10 maxRetries = project.hasProperty('testRetryMaxRetries') ? project.property('testRetryMaxRetries').toInteger() : System.getProperty('test.retry.maxRetries', '2').toInteger() maxFailures = project.hasProperty('testRetryMaxFailures') ? project.property('testRetryMaxFailures').toInteger() : System.getProperty('test.retry.maxFailures', '6').toInteger() if (!System.getenv().containsKey("GITHUB_ACTIONS")) { // locally, we want to fail the build on flaky tests passing after retry. On CI, no: failOnPassedAfterRetry = project.hasProperty('testRetryFailOnFlaky') ? project.property('testRetryFailOnFlaky').toBoolean() : true } filter { // filter by qualified class name (* matches zero or more of any character) includeClasses.add("*IT") } } } testlogger { showFullStackTraces=false showCauses=true showPassed=false showSkipped=true showSummary=false } } ================================================ FILE: engine-settings.gradle ================================================ include(':bonita-engine') include(':platform:platform-resources') include(':platform:platform-setup') include(':platform:platform-setup-test') include(':platform') include(':services:bonita-commons') include(':services:bonita-resources') include(':services:bonita-log') include(':services:bonita-identity') include(':services:bonita-transaction') include(':services:bonita-persistence') include(':services:bonita-cache') include(':services:bonita-classloader') include(':services:bonita-scheduler') include(':services:bonita-events') include(':services:bonita-platform') include(':services:bonita-archive') include(':services:bonita-authentication') include(':services:bonita-authorization') include(':services:bonita-session') include(':services:bonita-platform-authentication') include(':services:bonita-platform-session') include(':services:bonita-expression') include(':services:bonita-data-definition') include(':services:bonita-data-instance') include(':services:bonita-connector-executor') include(':services:bonita-command') include(':services:bonita-platform-command') include(':services:bonita-profile') include(':services:bonita-lock') include(':services:bonita-work') include(':services:bonita-incident') include(':services:bonita-builder') include(':services:bonita-page') include(':services:bonita-time-tracker') include(':services:bonita-business-application') include(':services:bonita-business-data:bonita-business-data-api') include(':services:bonita-business-data:bonita-business-data-client-resources') include(':services:bonita-business-data:bonita-business-data-impl') include(':services:bonita-business-data:bonita-business-data-generator') include(':services:bonita-business-data') include(':services:bonita-temporary-content') include(':services') include(':bpm:bonita-sap-jco-connector-api') include(':bpm:bonita-common') include(':bpm:bonita-client') include(':bpm:bonita-server') include(':bpm:bonita-web-extensions') include(':bpm:bonita-web-server') include(':bpm:bonita-core:bonita-process-definition') include(':bpm:bonita-core:bonita-process-instance') include(':bpm:bonita-core:bonita-login') include(':bpm:bonita-core:bonita-platform-login') include(':bpm:bonita-core:bonita-process-engine') include(':bpm:bonita-core:bonita-home-server') include(':bpm:bonita-core:bonita-actor-mapping') include(':bpm:bonita-core:bonita-core-data') include(':bpm:bonita-core:bonita-category') include(':bpm:bonita-core:bonita-supervisor-mapping') include(':bpm:bonita-core:bonita-process-comment') include(':bpm:bonita-core:bonita-user-filter') include(':bpm:bonita-core:bonita-contract-data') include(':bpm:bonita-core:bonita-parameter') include(':bpm:bonita-core:bonita-form-mapping') include(':bpm:bonita-core') include(':bpm:bonita-api:bonita-server-api-http') include(':bpm:bonita-api') include(':bpm:bonita-external') include(':bpm:bonita-synchro-repository:bonita-synchro-service') include(':bpm:bonita-synchro-repository:bonita-synchro-service-impl') include(':bpm:bonita-synchro-repository:bonita-synchro-register') include(':bpm:bonita-synchro-repository') include(':bonita-engine-standalone') include(':bonita-test-api') include(':bonita-engine-spring-boot-starter') include(':bonita-integration-tests:bonita-test-utils') include(':bonita-integration-tests:bonita-query-tests') include(':bonita-integration-tests:bonita-integration-tests-client') include(':bonita-integration-tests:bonita-integration-tests-local') include(':bonita-integration-tests:bonita-integration-tests-web') include(':bonita-integration-tests:benchmarks') include(':bonita-integration-tests') ================================================ FILE: gradle/libs.versions.toml ================================================ [versions] # The groovy version must be in synch with the bonita-project-parent POM (bonita-project repository): groovyVersion = "3.0.25" springVersion = "5.3.39" springSessionVersion = "2.7.4" springBootVersion = "2.7.18" commonsLangVersion = "3.20.0" bonitaArtifactsModelVersion = "1.2.2" bonitaSecurityVersion = "1.31.0" commonsIOVersion = "2.21.0" commonsFileUploadVersion = "1.6.0" commonsBeanutilsVersion = "1.11.0" commonsCollectionsVersion = "4.5.0" tomcatVersion = "9.0.117" commonsCLIVersion = "1.11.0" commonsTextVersion = "1.15.0" commonsValidatorVersion = "1.10.1" semver4jVersion = "3.1.0" slf4jVersion = "1.7.36" # Attention, see PassingPropertiesJCacheRegionFactory javadoc if this version changes: hibernateVersion = "5.6.15.Final" # version used by hibernate 5.6.15 (also used by BDM proxy generation): javassistVersion = "3.29.2-GA" # javax.persistence-api is used by hibernate: javaxPersistenceApiVersion = "2.2" jacksonBomVersion = "2.21.2" jakartaTransactionVersion = "1.3.3" jakartaServletVersion = "4.0.4" # Keep this until all client projects have migrated to jakarta or it will break their builds ! javaxServletVersion = "4.0.1" httpComponentsVersion = "4.5.14" xstreamVersion = "1.4.21" ehCacheVersion = "3.11.1" eclipseCompilerVersion = "3.45.0" jakartaActivationVersion = "1.2.2" jakartaValidationApiVersion = "3.1.1" quartzVersion = "2.3.2" micrometerVersion = "1.16.5" # DB drivers: mysqlVersion = "8.4.0" msSqlServerVersion = "12.10.2.jre11" oracleVersion = "23.26.1.0.0" postgresqlVersion = "42.7.11" narayanaVersion = "5.10.6.Final" logbackVersion = "1.2.13" jaxbVersion = "2.3.9" javaxAnnotationsVersion = "1.3.2" hazelcastVersion = "5.4.0" # Also update http://www.hazelcast.com/schema/config/hazelcast-config-.xsd if needed jcacheVersion = "1.1.1" guavaVersion = "33.6.0-jre" antlr4RuntimeVersion = "4.7.2" casClientCoreVersion = "4.0.4" jtidyVersion = "r938" squigglyFilterJacksonVersion = "1.3.18" aspectjVersion = "1.9.25.1" # bonita-web specific dependencies: jsonSimpleVersion = "1.1.1" urlrewritefilterVersion = "4.0.4" jakartaJstlVersion = "1.2.6" jakartaJstlApiVersion = "1.2.7" xbeanClassloaderVersion = "4.30" jgettextVersion = "0.15.1" hamcrestVersion = "3.0" woodstoxCoreVersion = "7.1.1" # When updating Keycloak version, if necessary, make sure to update the code in # subscription/bpm/bonita-web-server-sp package org.bonitasoft.console.common.server.auth.impl keycloakVersion = "21.1.2" xmlsecVersion = "2.3.5" bouncyCastleVersion = "1.84" spnegoVersion = "1.1.1" owaspHtmlSanitizerVersion = "20260313.1" # Test dependency versions junit4Version = "4.13.2" junit5Version = "6.0.3" awaitilityVersion = "4.3.0" assertjVersion = "3.27.7" xmlunitVersion = "1.6" mockitoVersion = "5.23.0" jsonUnitVersion = "5.1.1" systemRulesVersion = "1.19.0" systemLambdaVersion = "1.2.1" concurrentUnitVersion = "0.4.6" jettyVersion = "9.4.58.v20250814" jbossLoggingVersion = "3.6.3.Final" jsonassertVersion = "1.5.3" commonsExecVersion = "1.6.0" jmockitVersion = "1.50" mockServerJunitVersion = "5.15.0" [libraries] springCore = { module = "org.springframework:spring-core", version.ref = "springVersion" } springBeans = { module = "org.springframework:spring-beans", version.ref = "springVersion" } springContext = { module = "org.springframework:spring-context", version.ref = "springVersion" } springTx = { module = "org.springframework:spring-tx", version.ref = "springVersion" } springJdbc = { module = "org.springframework:spring-jdbc", version.ref = "springVersion" } springWebMvc = { module = "org.springframework:spring-webmvc", version.ref = "springVersion" } springWeb = { module = "org.springframework:spring-web", version.ref = "springVersion" } springOrm = { module = "org.springframework:spring-orm", version.ref = "springVersion" } springSessionCore = { module = "org.springframework.session:spring-session-core", version.ref = "springSessionVersion" } springSessionHazelcast = { module = "org.springframework.session:spring-session-hazelcast", version.ref = "springSessionVersion" } springAop = { module = "org.springframework:spring-aop", version.ref = "springVersion" } springBootAutoconfigure = { module = "org.springframework.boot:spring-boot-autoconfigure", version.ref = "springBootVersion" } springBootConfigurationProcessor = { module = "org.springframework.boot:spring-boot-configuration-processor", version.ref = "springBootVersion" } springBootTest = { module = "org.springframework.boot:spring-boot-test", version.ref = "springBootVersion" } springBootStarter = { module = "org.springframework.boot:spring-boot-starter", version.ref = "springBootVersion" } springBootStarterJdbc = { module = "org.springframework.boot:spring-boot-starter-jdbc", version.ref = "springBootVersion" } springBootStarterTest = { module = "org.springframework.boot:spring-boot-starter-test", version.ref = "springBootVersion" } springBootStarterWeb = { module = "org.springframework.boot:spring-boot-starter-web", version.ref = "springBootVersion" } groovyBom = { module = "org.codehaus.groovy:groovy-bom", version.ref = "groovyVersion" } groovyCore = { module = "org.codehaus.groovy:groovy", version.ref = "groovyVersion" } groovyServlet = { module = "org.codehaus.groovy:groovy-servlet", version.ref = "groovyVersion" } groovyXml = { module = "org.codehaus.groovy:groovy-xml", version.ref = "groovyVersion" } groovyJson = { module = "org.codehaus.groovy:groovy-json", version.ref = "groovyVersion" } groovyJmx = { module = "org.codehaus.groovy:groovy-jmx", version.ref = "groovyVersion" } groovyNio = { module = "org.codehaus.groovy:groovy-nio", version.ref = "groovyVersion" } groovyGroovysh = { module = "org.codehaus.groovy:groovy-groovysh", version.ref = "groovyVersion" } groovyDatetime = { module = "org.codehaus.groovy:groovy-datetime", version.ref = "groovyVersion" } groovyDateutil = { module = "org.codehaus.groovy:groovy-dateutil", version.ref = "groovyVersion" } groovyDocgenerator = { module = "org.codehaus.groovy:groovy-docgenerator", version.ref = "groovyVersion" } groovyJsr223 = { module = "org.codehaus.groovy:groovy-jsr223", version.ref = "groovyVersion" } groovySql = { module = "org.codehaus.groovy:groovy-sql", version.ref = "groovyVersion" } groovyTemplates = { module = "org.codehaus.groovy:groovy-templates", version.ref = "groovyVersion" } groovyYaml = { module = "org.codehaus.groovy:groovy-yaml", version.ref = "groovyVersion" } bonitaArtifactsModelBom = { module = "org.bonitasoft.engine:bonita-artifacts-model-dependencies", version.ref = "bonitaArtifactsModelVersion" } bonitaCommonArtifactsModel = { module = "org.bonitasoft.engine:bonita-common-artifacts-model", version.ref = "bonitaArtifactsModelVersion" } bonitaBusinessArchiveModel = { module = "org.bonitasoft.engine:bonita-business-archive", version.ref = "bonitaArtifactsModelVersion" } bonitaProcessDefinitionModel = { module = "org.bonitasoft.engine:bonita-process-definition-model", version.ref = "bonitaArtifactsModelVersion" } bonitaFormMappingModel = { module = "org.bonitasoft.engine:bonita-form-mapping-model", version.ref = "bonitaArtifactsModelVersion" } bonitaBusinessObjectModel = { module = "org.bonitasoft.engine:bonita-business-object-model", version.ref = "bonitaArtifactsModelVersion" } bonitaBdmAccessControlModel = { module = "org.bonitasoft.engine:bonita-bdm-access-control-model", version.ref = "bonitaArtifactsModelVersion" } bonitaProfileModel = { module = "org.bonitasoft.engine:bonita-profile-model", version.ref = "bonitaArtifactsModelVersion" } bonitaOrganizationModel = { module = "org.bonitasoft.engine:bonita-organization-model", version.ref = "bonitaArtifactsModelVersion" } bonitaApplicationModel = { module = "org.bonitasoft.engine:bonita-application-model", version.ref = "bonitaArtifactsModelVersion" } bonitaConnectorModel = { module = "org.bonitasoft.engine:bonita-connector-model", version.ref = "bonitaArtifactsModelVersion" } bonitaManager = { group = "com.bonitasoft.manager", name = "manager", version.ref = "bonitaSecurityVersion" } bonitaTestLicenses = { group = "org.bonitasoft.security", name = "test-licenses", version.ref = "bonitaSecurityVersion" } guava = { group = "com.google.guava", name = "guava", version.ref = "guavaVersion" } antlr4Runtime = { group = "org.antlr", name = "antlr4-runtime", version.ref = "antlr4RuntimeVersion" } commonsLang = { group = "org.apache.commons", name = "commons-lang3", version.ref = "commonsLangVersion" } commonsIO = { group = "commons-io", name = "commons-io", version.ref = "commonsIOVersion" } commonsFileUpload = { group = "commons-fileupload", name = "commons-fileupload", version.ref = "commonsFileUploadVersion" } commonsBeanUtils = { group = "commons-beanutils", name = "commons-beanutils", version.ref = "commonsBeanutilsVersion" } commonsCollections = { group = "org.apache.commons", name = "commons-collections4", version.ref = "commonsCollectionsVersion" } tomcatDbcp = { group = "org.apache.tomcat", name = "tomcat-dbcp", version.ref = "tomcatVersion" } tomcatEmbedCore = { group = "org.apache.tomcat.embed", name = "tomcat-embed-core", version.ref = "tomcatVersion"} commonsCLI = { group = "commons-cli", name = "commons-cli", version.ref = "commonsCLIVersion" } commonsText = { group = "org.apache.commons", name = "commons-text", version.ref = "commonsTextVersion" } commonsValidator = { group = "commons-validator", name = "commons-validator", version.ref = "commonsValidatorVersion" } semver4j = { group = "com.vdurmont", name = "semver4j", version.ref = "semver4jVersion" } slf4jApi = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4jVersion" } hibernateCore = { group = "org.hibernate", name = "hibernate-core", version.ref = "hibernateVersion" } javaxPersistenceApi = { group = "javax.persistence", name = "javax.persistence-api", version.ref = "javaxPersistenceApiVersion" } hibernateJCache = { group = "org.hibernate", name = "hibernate-jcache", version.ref = "hibernateVersion" } jcache = { group = "javax.cache", name = "cache-api", version.ref = "jcacheVersion" } javassist = { group = "org.javassist", name = "javassist", version.ref = "javassistVersion" } jacksonBom = { group = "com.fasterxml.jackson", name = "jackson-bom", version.ref = "jacksonBomVersion" } jakartaTransactionApi = { group = "jakarta.transaction", name = "jakarta.transaction-api", version.ref = "jakartaTransactionVersion" } jakartaServletApi = { group = "jakarta.servlet", name = "jakarta.servlet-api", version.ref = "jakartaServletVersion" } javaxServletApi = { group = "javax.servlet", name = "javax.servlet-api", version.ref = "javaxServletVersion" } httpComponentsClient = { group = "org.apache.httpcomponents", name = "httpclient", version.ref = "httpComponentsVersion" } httpComponentsMime = { group = "org.apache.httpcomponents", name = "httpmime", version.ref = "httpComponentsVersion" } xstream = { group = "com.thoughtworks.xstream", name = "xstream", version.ref = "xstreamVersion" } ehCache = { group = "org.ehcache", name = "ehcache", version.ref = "ehCacheVersion" } eclipseCompiler = { group = "org.eclipse.jdt", name = "ecj", version.ref = "eclipseCompilerVersion" } jakartaActivation = { group = "com.sun.activation", name = "jakarta.activation", version.ref = "jakartaActivationVersion" } jakartaValidationApi = { group = "jakarta.validation", name = "jakarta.validation-api", version.ref = "jakartaValidationApiVersion" } javaxAnnotations = { group = "javax.annotation", name = "javax.annotation-api", version.ref = "javaxAnnotationsVersion" } hazelcast = { group = "com.hazelcast", name = "hazelcast", version.ref = "hazelcastVersion" } hazelcastSpring = { group = "com.hazelcast", name = "hazelcast-spring", version.ref = "hazelcastVersion" } quartz = { group = "org.quartz-scheduler", name = "quartz", version.ref = "quartzVersion" } micrometerCore = { group = "io.micrometer", name = "micrometer-core", version.ref = "micrometerVersion" } micrometerRegistryJmx = { group = "io.micrometer", name = "micrometer-registry-jmx", version.ref = "micrometerVersion" } micrometerRegistryPrometheus = { group = "io.micrometer", name = "micrometer-registry-prometheus", version.ref = "micrometerVersion" } narayanaJta = { group = "org.jboss.narayana.jta", name = "narayana-jta", version.ref = "narayanaVersion" } jaxbCodeModel = { group = "org.glassfish.jaxb", name = "codemodel", version.ref = "jaxbVersion" } h2 = "com.h2database:h2:1.4.200" mysql = { group = "com.mysql", name = "mysql-connector-j", version.ref = "mysqlVersion" } msSqlServer = { group = "com.microsoft.sqlserver", name = "mssql-jdbc", version.ref = "msSqlServerVersion" } oracle = { group = "com.oracle.database.jdbc", name = "ojdbc11", version.ref = "oracleVersion" } postgresql = { group = "org.postgresql", name = "postgresql", version.ref = "postgresqlVersion" } lombok = "org.projectlombok:lombok:1.18.44" logback = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logbackVersion" } casClientCore = { group = "org.apereo.cas.client", name = "cas-client-core", version.ref = "casClientCoreVersion" } jtidy = { group = "net.sf.jtidy", name = "jtidy", version.ref = "jtidyVersion" } squigglyFilterJackson = { group = "com.github.bohnman", name = "squiggly-filter-jackson", version.ref = "squigglyFilterJacksonVersion" } aspectjWeaver = { module = "org.aspectj:aspectjweaver", version.ref = "aspectjVersion" } # bonita-web specific dependencies: jsonSimple = { group = "com.googlecode.json-simple", name = "json-simple", version.ref = "jsonSimpleVersion" } urlrewritefilter = { group = "org.tuckey", name = "urlrewritefilter", version.ref = "urlrewritefilterVersion" } jakartaJstl = { group = "org.glassfish.web", name = "jakarta.servlet.jsp.jstl", version.ref = "jakartaJstlVersion" } jakartaJstlApi = { group = "jakarta.servlet.jsp.jstl", name = "jakarta.servlet.jsp.jstl-api", version.ref = "jakartaJstlApiVersion" } xbeanClassloader = { group = "org.apache.xbean", name = "xbean-classloader", version.ref = "xbeanClassloaderVersion" } jgettext = { group = "org.fedorahosted.tennera", name = "jgettext", version.ref = "jgettextVersion" } hamcrest = { group = "org.hamcrest", name = "hamcrest", version.ref = "hamcrestVersion" } woodstoxCore = { group = "com.fasterxml.woodstox", name = "woodstox-core", version.ref = "woodstoxCoreVersion" } keycloakSamlAdapterApiPublic = { group = "org.keycloak", name = "keycloak-saml-adapter-api-public", version.ref = "keycloakVersion" } keycloakSamlServletFilterAdapter = { group = "org.keycloak", name = "keycloak-saml-servlet-filter-adapter", version.ref = "keycloakVersion" } keycloakAdapterCore = { group = "org.keycloak", name = "keycloak-adapter-core", version.ref = "keycloakVersion" } keycloakServletFilterAdapter = { group = "org.keycloak", name = "keycloak-servlet-filter-adapter", version.ref = "keycloakVersion" } xmlsec = { group = "org.apache.santuario", name = "xmlsec", version.ref = "xmlsecVersion" } bouncyCastleBcprov = { group = "org.bouncycastle", name = "bcprov-jdk18on", version.ref = "bouncyCastleVersion" } bouncyCastleBcpkix = { group = "org.bouncycastle", name = "bcpkix-jdk18on", version.ref = "bouncyCastleVersion" } bouncyCastleBcutil = { group = "org.bouncycastle", name = "bcutil-jdk18on", version.ref = "bouncyCastleVersion" } spnego = { group = "org.codelibs", name = "spnego", version.ref = "spnegoVersion" } owaspHtmlSanitizer = { group = "com.googlecode.owasp-java-html-sanitizer", name = "owasp-java-html-sanitizer", version.ref = "owaspHtmlSanitizerVersion" } # Test dependencies junit5api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit5Version" } junit5params = { group = "org.junit.jupiter", name = "junit-jupiter-params", version.ref = "junit5Version" } junitJupiterEngine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit5Version" } junitVintageEngine = { group = "org.junit.vintage", name = "junit-vintage-engine", version.ref = "junit5Version" } junit4 = { group = "junit", name = "junit", version.ref = "junit4Version" } springTest = { module = "org.springframework:spring-test", version.ref = "springVersion" } awaitility = { group = "org.awaitility", name = "awaitility", version.ref = "awaitilityVersion" } assertj = { group = "org.assertj", name = "assertj-core", version.ref = "assertjVersion" } xmlunit = { group = "xmlunit", name = "xmlunit", version.ref = "xmlunitVersion" } mockitoCore = { group = "org.mockito", name = "mockito-core", version.ref = "mockitoVersion" } mockitoJunitJupiter = { group = "org.mockito", name = "mockito-junit-jupiter", version.ref = "mockitoVersion" } jsonUnit = { group = "net.javacrumbs.json-unit", name = "json-unit-assertj", version.ref = "jsonUnitVersion" } systemRules = { group = "com.github.stefanbirkner", name = "system-rules", version.ref = "systemRulesVersion" } systemLambda = { group = "com.github.stefanbirkner", name = "system-lambda", version.ref = "systemLambdaVersion" } concurrentUnit = { group = "net.jodah", name = "concurrentunit", version.ref = "concurrentUnitVersion" } jettyServer = { group = "org.eclipse.jetty", name = "jetty-server", version.ref = "jettyVersion" } jettyServlet = { group = "org.eclipse.jetty", name = "jetty-servlet", version.ref = "jettyVersion" } jettySecurity = { group = "org.eclipse.jetty", name = "jetty-security", version.ref = "jettyVersion" } jbossLogging = { group = "org.jboss.logging", name = "jboss-logging", version.ref = "jbossLoggingVersion" } commonsExec = { group = "org.apache.commons", name = "commons-exec", version.ref = "commonsExecVersion" } jmockit = { group = "org.jmockit", name = "jmockit", version.ref = "jmockitVersion" } mockServerJunit = { group = "org.mock-server", name = "mockserver-junit-jupiter", version.ref = "mockServerJunitVersion" } jsonassert = { group = "org.skyscreamer", name = "jsonassert", version.ref = "jsonassertVersion" } [bundles] groovy = ["groovyCore", "groovyServlet", "groovyXml", "groovyJson", "groovyJmx", "groovyNio", "groovyGroovysh", "groovyDatetime", "groovyDateutil", "groovyDocgenerator", "groovyJsr223", "groovySql", "groovyTemplates", "groovyYaml"] [plugins] bonitaFormatting = { id = "com.bonitasoft.gradle.bonita-formatting", version = "1.0.1" } dependencyUpdates = { id = "com.github.ben-manes.versions", version = "0.53.0" } # used by "List out-of-date dependencies" script taskInfo = { id = "org.barfuin.gradle.taskinfo", version = "3.0.2" } # Adds a 'tiTree' task to display Gradle task-graph testRetry = { id = "org.gradle.test-retry", version = "1.6.4" } # used to retry flaky tests automatically ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.4-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: gradle.properties ================================================ org.gradle.jvmargs=--add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED -Dfile.encoding=UTF-8 org.gradle.caching=true org.gradle.vfs.watch=true version=11.1-SNAPSHOT brandingVersion=2026.2-SNAPSHOT ================================================ FILE: gradlew ================================================ #!/bin/sh # # Copyright © 2015-2021 the original authors. # # Licensed 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 # # https://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. # # SPDX-License-Identifier: Apache-2.0 # ############################################################################## # # Gradle start up script for POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "$( uname )" in #( CYGWIN* ) cygwin=true ;; #( Darwin* ) darwin=true ;; #( MSYS* | MINGW* ) msys=true ;; #( NONSTOP* ) nonstop=true ;; esac CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD=java if ! command -v java >/dev/null 2>&1 then die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @rem SPDX-License-Identifier: Apache-2.0 @rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute echo. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line set CLASSPATH= @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! set EXIT_CODE=%ERRORLEVEL% if %EXIT_CODE% equ 0 set EXIT_CODE=1 if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: license/license.txt ================================================ /** * Copyright (C) 2016 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. **/ ================================================ FILE: license/licenseHeaderDefinition.xml ================================================ (\s|\t)*/\*.*$ .*\*/(\s|\t)*$ false true false ================================================ FILE: platform/platform-resources/build.gradle ================================================ import org.apache.tools.ant.filters.PrefixLines import org.apache.tools.ant.filters.ReplaceTokens import org.bonitasoft.engine.gradle.PomUtils plugins { id 'distribution' } group = 'org.bonitasoft.platform' description = '' dependencies { implementation platform(libs.jacksonBom) annotationProcessor libs.lombok compileOnly libs.lombok api libs.springTx api libs.springJdbc api libs.springContext api libs.slf4jApi api libs.commonsIO api libs.springBootAutoconfigure implementation "com.fasterxml.jackson.core:jackson-databind" testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.logback } processResources { inputs.property("version", project.version) from('src/main/resources') { include 'PLATFORM_ENGINE_VERSION' filter(ReplaceTokens, tokens: [version: project.version]) } from("${projectDir}/../../bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-platform-community.properties") { filter(PrefixLines, prefix: "#") rename 'bonita-platform-community.properties', 'platform_engine/bonita-platform-community-custom.properties' } from("${projectDir}/../../bpm/bonita-core/bonita-process-engine/src/main/resources/bonita-tenant-community.properties") { filter(PrefixLines, prefix: "#") rename 'bonita-tenant-community.properties', 'tenant_engine/bonita-tenant-community-custom.properties' } } configurations { distributionZip } tasks.register("sourcesJar", Jar) { from sourceSets.main.allJava archiveClassifier = 'sources' } distTar.enabled = false distributions { main { contents { from sourceSets.main.output } } } artifacts { distributionZip distZip } publishing { publications { mavenJava(MavenPublication) { from project.components.java artifact distZip artifact project.sourcesJar pom { pom -> name = "Bonita Platform Resources" description = "Bonita Platform Resources serves to setup Bonita platform" PomUtils.pomCommunityPublication(pom) } } } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/ConfigurationService.java ================================================ /** * 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.platform.configuration; import java.io.File; import java.nio.file.Path; import java.util.List; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.bonitasoft.platform.configuration.model.FullBonitaConfiguration; import org.bonitasoft.platform.configuration.model.LightBonitaConfiguration; import org.bonitasoft.platform.exception.PlatformException; /** * Give access to Bonita Platform configuration. * Is used by setup mechanism to retrieve configuration before running the Engine + Portal, on a system that does give * access to a persistent filesystem. * * @author Emmanuel Duchastenier */ public interface ConfigurationService { /** * Retrieves the portal configuration at platform-level. * * @return a list of BonitaConfiguration that represents each file */ List getPlatformPortalConf(); /** * Retrieves the platform configuration at platform level. * * @return a list of BonitaConfiguration that represents each file */ List getPlatformEngineConf(); /** * Retrieves the engine tenant configuration for a tenant * * @return a list of BonitaConfiguration that represents each file */ List getTenantEngineConf(); /** * Retrieves the security scripts for a tenant * * @return a list of BonitaConfiguration that represents each file */ List getTenantSecurityScripts(); /** * store security script for a tenant * * @param bonitaConfigurations list of files */ void storeTenantSecurityScripts(List bonitaConfigurations); /** * store tenant configuration files for portal * * @param bonitaConfigurations list of files */ void storeTenantPortalConf(List bonitaConfigurations); /** * updates tenant configurations for portal, for all tenants and for tenant template. * * @param bonitaConfigurations list of configurations to store */ void updateTenantPortalConf(List bonitaConfigurations); void updateDefaultConfiguration(Path configurationRootFolder) throws PlatformException; /** * Retrieves the portal configuration for a tenant * * @return list of files */ List getTenantPortalConf(); /** * Retrieves a portal configuration file for a tenant */ BonitaConfiguration getTenantPortalConfiguration(String file); /** * store platform configuration file in database * * @param bonitaConfigurations list of files */ void storePlatformEngineConf(List bonitaConfigurations); /** * store platform configuration files for engine * * @param configurationRootFolder root folder containing configuration files */ void storePlatformConfiguration(File configurationRootFolder) throws PlatformException; /** * store whole configuration files for engine and portal, excluding licenses files * * @param configurationRootFolder path to root folder */ void storeAllConfiguration(Path configurationRootFolder) throws PlatformException; /** * write all configuration files * directory structure : * . * ├── platform_engine * ├── platform_portal * ├── tenant_engine * ├── tenant_portal * └── tenant_security_scripts */ List writeAllConfigurationToFolder(File configurationFolder, File licenseFolder) throws PlatformException; /** * read licensesFolder for license files * sub-folders are ignored * each *.lic file is stored in database */ void storeLicenses(File licensesFolder) throws PlatformException; /** * Retrieves all license files stored in database. * * @return a list of BonitaConfiguration that represents each license file */ List getLicenses() throws PlatformException; /** * Delete all configuration and license files */ void deleteAllConfiguration(); List getMandatoryStructureConfiguration(); void storeConfigurationsIfNotExist(List configurations); } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaAllConfigurationContentTypeCleaner.java ================================================ /** * 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.platform.configuration.impl; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; import org.bonitasoft.platform.configuration.model.FullBonitaConfiguration; import org.springframework.jdbc.core.BatchPreparedStatementSetter; /** * @author Laurent Leseigneur */ public class BonitaAllConfigurationContentTypeCleaner implements BatchPreparedStatementSetter { public static final String DELETE_CONFIGURATION = "DELETE from configuration where content_type = ? and resource_name = ? "; private final List bonitaConfigurations; public BonitaAllConfigurationContentTypeCleaner(List bonitaConfigurations) { this.bonitaConfigurations = bonitaConfigurations; } @Override public void setValues(PreparedStatement ps, int i) throws SQLException { final FullBonitaConfiguration bonitaConfiguration = bonitaConfigurations.get(i); ps.setString(1, bonitaConfiguration.getConfigurationType()); ps.setString(2, bonitaConfiguration.getResourceName()); } @Override public int getBatchSize() { return bonitaConfigurations.size(); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaAllConfigurationPreparedStatementSetter.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.impl; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; import org.bonitasoft.platform.configuration.model.FullBonitaConfiguration; import org.bonitasoft.platform.database.DatabaseVendor; import org.bonitasoft.platform.setup.PlatformSetup; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.support.lob.TemporaryLobCreator; /** * @author Laurent Leseigneur */ public class BonitaAllConfigurationPreparedStatementSetter implements BatchPreparedStatementSetter, ConfigurationColumns { public static final String INSERT_CONFIGURATION = "INSERT into configuration(content_type, resource_name, resource_content) values (?,?,?)"; private final List bonitaConfigurations; private final String dbVendor; public BonitaAllConfigurationPreparedStatementSetter(List bonitaConfigurations, String dbVendor) { this.bonitaConfigurations = bonitaConfigurations; this.dbVendor = dbVendor == null ? PlatformSetup.getPropertyBonitaDbVendor() : dbVendor; } @Override public void setValues(PreparedStatement ps, int i) throws SQLException { final FullBonitaConfiguration bonitaConfiguration = bonitaConfigurations.get(i); ps.setString(COLUMN_INDEX_TYPE, bonitaConfiguration.getConfigurationType()); ps.setString(COLUMN_INDEX_RESOURCE_NAME, bonitaConfiguration.getResourceName()); switch (DatabaseVendor.parseValue(dbVendor)) { case H2, POSTGRES: ps.setBytes(COLUMN_INDEX_RESOURCE_CONTENT, bonitaConfiguration.getResourceContent()); break; case ORACLE, MYSQL, SQLSERVER: TemporaryLobCreator temporaryLobCreator = new TemporaryLobCreator(); temporaryLobCreator.setBlobAsBytes(ps, COLUMN_INDEX_RESOURCE_CONTENT, bonitaConfiguration.getResourceContent()); break; default: throw new IllegalArgumentException("unsupported db vendor:" + dbVendor); } } @Override public int getBatchSize() { return bonitaConfigurations.size(); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaConfigurationCleaner.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.impl; import java.sql.PreparedStatement; import org.springframework.jdbc.core.BatchPreparedStatementSetter; /** * @author Laurent Leseigneur */ public class BonitaConfigurationCleaner implements BatchPreparedStatementSetter { public static final String DELETE_ALL_CONFIGURATION = "DELETE FROM configuration"; @Override public void setValues(PreparedStatement preparedStatement, int i) { } @Override public int getBatchSize() { return 1; } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaConfigurationContentTypeCleaner.java ================================================ /** * 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.platform.configuration.impl; import java.sql.PreparedStatement; import java.sql.SQLException; import org.bonitasoft.platform.configuration.type.ConfigurationType; import org.springframework.jdbc.core.BatchPreparedStatementSetter; /** * @author Laurent Leseigneur */ public class BonitaConfigurationContentTypeCleaner implements BatchPreparedStatementSetter { public static final String DELETE_CONFIGURATION = "DELETE from configuration where content_type = ? "; private final ConfigurationType type; public BonitaConfigurationContentTypeCleaner(ConfigurationType type) { this.type = type; } @Override public void setValues(PreparedStatement ps, int i) throws SQLException { ps.setString(1, type.toString()); } @Override public int getBatchSize() { return 1; } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaConfigurationPreparedStatementCleaner.java ================================================ /** * 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.platform.configuration.impl; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.bonitasoft.platform.configuration.type.ConfigurationType; import org.springframework.jdbc.core.BatchPreparedStatementSetter; /** * @author Laurent Leseigneur */ public class BonitaConfigurationPreparedStatementCleaner implements BatchPreparedStatementSetter { public static final String DELETE_CONFIGURATION = "DELETE from configuration where content_type = ? and resource_name = ?"; private final List bonitaConfigurations; private final ConfigurationType type; public BonitaConfigurationPreparedStatementCleaner(List bonitaConfigurations, ConfigurationType type) { this.bonitaConfigurations = bonitaConfigurations; this.type = type; } @Override public void setValues(PreparedStatement ps, int i) throws SQLException { final BonitaConfiguration bonitaConfiguration = bonitaConfigurations.get(i); ps.setString(1, type.toString()); ps.setString(2, bonitaConfiguration.getResourceName()); } @Override public int getBatchSize() { return bonitaConfigurations.size(); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaConfigurationPreparedStatementSetter.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.impl; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.bonitasoft.platform.configuration.type.ConfigurationType; import org.bonitasoft.platform.database.DatabaseVendor; import org.bonitasoft.platform.setup.PlatformSetup; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.support.lob.TemporaryLobCreator; /** * @author Laurent Leseigneur */ public class BonitaConfigurationPreparedStatementSetter implements BatchPreparedStatementSetter, ConfigurationColumns { public static final String INSERT_CONFIGURATION = "INSERT into configuration(content_type, resource_name, resource_content) values (?,?,?)"; private final List bonitaConfigurations; private final String dbVendor; private final ConfigurationType type; public BonitaConfigurationPreparedStatementSetter(List bonitaConfigurations, String dbVendor, ConfigurationType type) { this.bonitaConfigurations = bonitaConfigurations; this.dbVendor = dbVendor == null ? PlatformSetup.getPropertyBonitaDbVendor() : dbVendor; this.type = type; } @Override public void setValues(PreparedStatement ps, int i) throws SQLException { final BonitaConfiguration bonitaConfiguration = bonitaConfigurations.get(i); ps.setString(COLUMN_INDEX_TYPE, type.toString()); ps.setString(COLUMN_INDEX_RESOURCE_NAME, bonitaConfiguration.getResourceName()); switch (DatabaseVendor.parseValue(dbVendor)) { case H2, POSTGRES: ps.setBytes(COLUMN_INDEX_RESOURCE_CONTENT, bonitaConfiguration.getResourceContent()); break; case ORACLE, MYSQL, SQLSERVER: new TemporaryLobCreator().setBlobAsBytes(ps, COLUMN_INDEX_RESOURCE_CONTENT, bonitaConfiguration.getResourceContent()); break; default: throw new IllegalArgumentException("unsupported db vendor:" + dbVendor); } } @Override public int getBatchSize() { return bonitaConfigurations.size(); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaConfigurationRowMapper.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.impl; import static org.bonitasoft.platform.configuration.impl.ConfigurationFields.RESOURCE_CONTENT; import static org.bonitasoft.platform.configuration.impl.ConfigurationFields.RESOURCE_NAME; import java.sql.ResultSet; import java.sql.SQLException; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.springframework.jdbc.core.RowMapper; /** * @author Laurent Leseigneur */ public class BonitaConfigurationRowMapper implements RowMapper { public static final String SELECT_CONFIGURATION_FOR_TYPE = "SELECT content_type, resource_name, resource_content FROM configuration WHERE content_type = ? ORDER BY resource_name"; public static final String SELECT_CONFIGURATION = "SELECT content_type, resource_name, resource_content FROM configuration WHERE content_type = ? AND resource_name = ?"; @Override public BonitaConfiguration mapRow(ResultSet rs, int rowNum) throws SQLException { return new BonitaConfiguration(rs.getString(RESOURCE_NAME), rs.getBytes(RESOURCE_CONTENT)); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/BonitaConfigurationTenantUpdater.java ================================================ /** * Copyright (C) 2017 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.platform.configuration.impl; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.bonitasoft.platform.configuration.type.ConfigurationType; import org.bonitasoft.platform.database.DatabaseVendor; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.support.lob.TemporaryLobCreator; /** * @author Emmanuel Duchastenier */ public class BonitaConfigurationTenantUpdater implements BatchPreparedStatementSetter { public static final String UPDATE_ALL_TENANTS_CONFIGURATION = "UPDATE configuration SET resource_content=? WHERE content_type=? AND resource_name=?"; private final List bonitaConfigurations; private final String dbVendor; private final ConfigurationType type; public BonitaConfigurationTenantUpdater(List bonitaConfigurations, String dbVendor, ConfigurationType type) { this.bonitaConfigurations = bonitaConfigurations; this.dbVendor = dbVendor; this.type = type; } @Override public void setValues(PreparedStatement ps, int i) throws SQLException { final BonitaConfiguration bonitaConfiguration = bonitaConfigurations.get(i); ps.setString(2, type.toString()); ps.setString(3, bonitaConfiguration.getResourceName()); switch (DatabaseVendor.parseValue(dbVendor)) { case H2, POSTGRES: ps.setBytes(1, bonitaConfiguration.getResourceContent()); break; case ORACLE, MYSQL, SQLSERVER: new TemporaryLobCreator().setBlobAsBytes(ps, 1, bonitaConfiguration.getResourceContent()); break; default: throw new IllegalArgumentException("unsupported db vendor:" + dbVendor); } } @Override public int getBatchSize() { return bonitaConfigurations.size(); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/ConfigurationColumns.java ================================================ /** * 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.platform.configuration.impl; /** * @author Laurent Leseigneur */ public interface ConfigurationColumns { int COLUMN_INDEX_TYPE = 1; int COLUMN_INDEX_RESOURCE_NAME = 2; int COLUMN_INDEX_RESOURCE_CONTENT = 3; } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/ConfigurationFields.java ================================================ /** * 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.platform.configuration.impl; /** * @author Laurent Leseigneur */ public interface ConfigurationFields { String CONTENT_TYPE = "content_type"; String RESOURCE_NAME = "resource_name"; String RESOURCE_CONTENT = "resource_content"; } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/ConfigurationServiceImpl.java ================================================ /** * 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.platform.configuration.impl; import static org.bonitasoft.platform.configuration.type.ConfigurationType.*; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.IOUtils; import org.bonitasoft.platform.configuration.ConfigurationService; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.bonitasoft.platform.configuration.model.FullBonitaConfiguration; import org.bonitasoft.platform.configuration.model.LightBonitaConfiguration; import org.bonitasoft.platform.configuration.type.ConfigurationType; import org.bonitasoft.platform.configuration.util.AllConfigurationResourceVisitor; import org.bonitasoft.platform.configuration.util.AutoUpdateConfigurationVisitor; import org.bonitasoft.platform.configuration.util.CleanAndStoreAllConfigurationInTransaction; import org.bonitasoft.platform.configuration.util.CleanAndStoreConfigurationInTransaction; import org.bonitasoft.platform.configuration.util.ConfigurationResourceVisitor; import org.bonitasoft.platform.configuration.util.DeleteAllConfigurationInTransaction; import org.bonitasoft.platform.configuration.util.GetAllConfigurationInTransaction; import org.bonitasoft.platform.configuration.util.GetConfigurationInTransaction; import org.bonitasoft.platform.configuration.util.GetConfigurationsInTransaction; import org.bonitasoft.platform.configuration.util.GetMandatoryStructureConfiguration; import org.bonitasoft.platform.configuration.util.LicensesResourceVisitor; import org.bonitasoft.platform.configuration.util.StoreConfigurationInTransaction; import org.bonitasoft.platform.configuration.util.StoreConfigurationsIfNotExist; import org.bonitasoft.platform.configuration.util.UpdateConfigurationInTransaction; import org.bonitasoft.platform.exception.PlatformException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.support.TransactionTemplate; /** * @author Emmanuel Duchastenier */ @Service public class ConfigurationServiceImpl implements ConfigurationService { public static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationServiceImpl.class); private final JdbcTemplate jdbcTemplate; private final TransactionTemplate transactionTemplate; private final String dbVendor; public ConfigurationServiceImpl(JdbcTemplate jdbcTemplate, TransactionTemplate transactionTemplate, @Value("${db.vendor}") String dbVendor) { this.jdbcTemplate = jdbcTemplate; this.transactionTemplate = transactionTemplate; this.dbVendor = dbVendor; } @Override public List getPlatformPortalConf() { return getBonitaConfigurations(PLATFORM_PORTAL); } @Override public List getPlatformEngineConf() { return getBonitaConfigurations(PLATFORM_ENGINE); } @Override public void storeTenantSecurityScripts(List bonitaConfigurations) { storeConfiguration(bonitaConfigurations, TENANT_SECURITY_SCRIPTS); } @Override public void storePlatformConfiguration(File configurationRootFolder) throws PlatformException { storeConfiguration(configurationRootFolder, PLATFORM_ENGINE); } @Override public void storeAllConfiguration(Path configurationRootFolder) throws PlatformException { List fullBonitaConfigurations = new ArrayList<>(); AllConfigurationResourceVisitor allConfigurationResourceVisitor = new AllConfigurationResourceVisitor( fullBonitaConfigurations); try { Files.walkFileTree(configurationRootFolder, allConfigurationResourceVisitor); transactionTemplate.execute( new CleanAndStoreAllConfigurationInTransaction(jdbcTemplate, dbVendor, fullBonitaConfigurations)); } catch (IOException e) { throw new PlatformException(e); } } @Override public void updateDefaultConfiguration(Path configurationRootFolder) throws PlatformException { List bonitaConfigurations = new ArrayList<>(); try { Files.walkFileTree(configurationRootFolder, new AutoUpdateConfigurationVisitor(bonitaConfigurations)); } catch (IOException e) { throw new PlatformException(e); } updateTenantPortalConf(bonitaConfigurations); } @Override public void storeTenantPortalConf(List bonitaConfigurations) { storeConfiguration(bonitaConfigurations, TENANT_PORTAL); } @Override public void updateTenantPortalConf(List bonitaConfigurations) { // update default configuration at TENANT_PORTAL level: transactionTemplate.execute( new UpdateConfigurationInTransaction(jdbcTemplate, dbVendor, bonitaConfigurations, TENANT_PORTAL)); } @Override public List getTenantPortalConf() { return getBonitaConfigurations(TENANT_PORTAL); } @Override public BonitaConfiguration getTenantPortalConfiguration(String file) { return getBonitaConfiguration(TENANT_PORTAL, file); } @Override public List writeAllConfigurationToFolder(File configurationFolder, File licenseFolder) throws PlatformException { FolderResolver folderResolver = new FolderResolver(configurationFolder.toPath(), licenseFolder.toPath()); List writtenFiles = new ArrayList<>(); for (FullBonitaConfiguration fullBonitaConfiguration : getAllConfiguration()) { File confFile = new File(folderResolver.getFolder(fullBonitaConfiguration), fullBonitaConfiguration.getResourceName()); writtenFiles.add(confFile); LOGGER.debug(String.format("writing file %s to folder %s", confFile.getName(), confFile.getParentFile().getAbsolutePath())); try (FileOutputStream output = new FileOutputStream(confFile)) { IOUtils.write(fullBonitaConfiguration.getResourceContent(), output); } catch (IOException e) { throw new PlatformException(e); } } return writtenFiles; } protected List getAllConfiguration() { return transactionTemplate.execute(new GetAllConfigurationInTransaction(jdbcTemplate)); } private void storeConfiguration(File configurationRootFolder, ConfigurationType type) throws PlatformException { final Path path = configurationRootFolder.toPath(); List bonitaConfigurations = new ArrayList<>(); ConfigurationResourceVisitor configurationResourceVisitor = new ConfigurationResourceVisitor( bonitaConfigurations); try { Files.walkFileTree(path, configurationResourceVisitor); storeConfiguration(bonitaConfigurations, type); } catch (IOException e) { throw new PlatformException(e); } } // Needed by Bonita Central @Override public void storePlatformEngineConf(List bonitaConfigurations) { storeConfiguration(bonitaConfigurations, PLATFORM_ENGINE); } private void storeConfiguration(List bonitaConfigurations, ConfigurationType type) { transactionTemplate.execute( new StoreConfigurationInTransaction(jdbcTemplate, dbVendor, bonitaConfigurations, type)); } private void cleanAndStoreLicenseConfiguration(List bonitaConfigurations) { transactionTemplate.execute(new CleanAndStoreConfigurationInTransaction(jdbcTemplate, dbVendor, bonitaConfigurations, ConfigurationType.LICENSES)); } @Override public List getTenantEngineConf() { return getBonitaConfigurations(TENANT_ENGINE); } List getBonitaConfigurations(ConfigurationType type) { return transactionTemplate.execute(new GetConfigurationsInTransaction(jdbcTemplate, type)); } @Override public List getTenantSecurityScripts() { return getBonitaConfigurations(TENANT_SECURITY_SCRIPTS); } private BonitaConfiguration getBonitaConfiguration(ConfigurationType type, String resourceName) { return transactionTemplate .execute(new GetConfigurationInTransaction(jdbcTemplate, type, resourceName)); } @Override public void storeLicenses(File licensesFolder) throws PlatformException { final Path path = licensesFolder.toPath(); List bonitaConfigurations = new ArrayList<>(); LicensesResourceVisitor licensesResourceVisitor = new LicensesResourceVisitor(bonitaConfigurations); try { Files.walkFileTree(path, licensesResourceVisitor); cleanAndStoreLicenseConfiguration(bonitaConfigurations); } catch (IOException e) { throw new PlatformException(e); } } @Override public List getLicenses() { return getBonitaConfigurations(LICENSES); } @Override public void deleteAllConfiguration() { transactionTemplate.execute(new DeleteAllConfigurationInTransaction(jdbcTemplate)); } @Override public List getMandatoryStructureConfiguration() { return transactionTemplate.execute(new GetMandatoryStructureConfiguration(jdbcTemplate)); } @Override public void storeConfigurationsIfNotExist(List configurations) { transactionTemplate.execute(new StoreConfigurationsIfNotExist(jdbcTemplate, dbVendor, configurations)); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/FolderResolver.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.impl; import java.io.File; import java.nio.file.Path; import org.bonitasoft.platform.configuration.model.FullBonitaConfiguration; /** * utility class to map configuration files and licenses to pulling folder. *
        *
      • license files are pulled to licenseFolder
      • *
      • other files are pulled to configurationFolder/CONFIGURATION_TYPE
      • *
      * * @author Laurent Leseigneur */ public class FolderResolver { private final Path configurationFolder; private final Path licenseFolder; public FolderResolver(Path configurationFolder, Path licenseFolder) { this.configurationFolder = configurationFolder; this.licenseFolder = licenseFolder; } public File getFolder(FullBonitaConfiguration fullBonitaConfiguration) { File confFolder = resolveFolder(fullBonitaConfiguration).toFile(); confFolder.mkdirs(); return confFolder; } private Path resolveSubFolder(Path rootPath, FullBonitaConfiguration fullBonitaConfiguration) { if (fullBonitaConfiguration.isLicenseFile()) { return rootPath; } return rootPath.resolve(fullBonitaConfiguration.getConfigurationType().toLowerCase()); } private Path resolveFolder(FullBonitaConfiguration fullBonitaConfiguration) { if (fullBonitaConfiguration.isLicenseFile()) { return resolveSubFolder(licenseFolder, fullBonitaConfiguration); } return resolveSubFolder(configurationFolder, fullBonitaConfiguration); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/impl/FullBonitaConfigurationRowMapper.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.impl; import static org.bonitasoft.platform.configuration.impl.ConfigurationFields.*; import java.sql.ResultSet; import java.sql.SQLException; import org.bonitasoft.platform.configuration.model.FullBonitaConfiguration; import org.springframework.jdbc.core.RowMapper; /** * @author Laurent Leseigneur */ public class FullBonitaConfigurationRowMapper implements RowMapper { public static final String SELECT_CONFIGURATION = "SELECT content_type, resource_name, resource_content FROM configuration ORDER BY content_type, resource_name"; @Override public FullBonitaConfiguration mapRow(ResultSet rs, int rowNum) throws SQLException { return new FullBonitaConfiguration(rs.getString(RESOURCE_NAME), rs.getBytes(RESOURCE_CONTENT), rs.getString(CONTENT_TYPE)); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/model/BonitaConfiguration.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.model; import java.io.Serializable; import java.util.Arrays; import java.util.Objects; import lombok.Getter; import lombok.Setter; /** * @author Emmanuel Duchastenier */ @Setter @Getter public class BonitaConfiguration implements Serializable { private String resourceName; private byte[] resourceContent; public BonitaConfiguration(String resourceName, byte[] resourceContent) { this.resourceName = resourceName; this.resourceContent = resourceContent; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BonitaConfiguration that = (BonitaConfiguration) o; return Objects.equals(resourceName, that.resourceName) && Arrays.equals(resourceContent, that.resourceContent); } @Override public int hashCode() { return Objects.hash(resourceName, Arrays.hashCode(resourceContent)); } @Override public String toString() { return "BonitaConfiguration{resourceName='" + resourceName + "'}"; } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/model/FullBonitaConfiguration.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.model; import lombok.EqualsAndHashCode; import lombok.Getter; import org.bonitasoft.platform.configuration.type.ConfigurationType; /** * @author Laurent Leseigneur */ @Getter @EqualsAndHashCode(callSuper = true) public class FullBonitaConfiguration extends BonitaConfiguration { private final String configurationType; public FullBonitaConfiguration(String resourceName, byte[] resourceContent, String configurationType) { super(resourceName, resourceContent); this.configurationType = configurationType; } public boolean isLicenseFile() { return getConfigurationType().equals(ConfigurationType.LICENSES.name()); } @Override public String toString() { return String.format("FullBonitaConfiguration{ resourceName='%s' , configurationType='%s' }", getResourceName(), getConfigurationType()); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/model/LightBonitaConfiguration.java ================================================ /** * Copyright (C) 2018 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.platform.configuration.model; /** * @author Emmanuel Duchastenier */ public record LightBonitaConfiguration(String type) { } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/type/ConfigurationType.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.type; public enum ConfigurationType { LICENSES, PLATFORM_PORTAL, PLATFORM_ENGINE, TENANT_PORTAL, TENANT_ENGINE, TENANT_SECURITY_SCRIPTS } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/AllConfigurationResourceVisitor.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.util; import static org.bonitasoft.platform.configuration.type.ConfigurationType.*; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.Arrays; import java.util.List; import org.bonitasoft.platform.configuration.model.FullBonitaConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Laurent Leseigneur */ public class AllConfigurationResourceVisitor extends SimpleFileVisitor { private final List fullBonitaConfigurations; private static final Logger LOGGER = LoggerFactory.getLogger(AllConfigurationResourceVisitor.class); private static final List CONFIGURATION_FOLDERS = Arrays.asList(PLATFORM_PORTAL.name().toLowerCase(), PLATFORM_ENGINE.name().toLowerCase(), TENANT_PORTAL.name().toLowerCase(), TENANT_ENGINE.name().toLowerCase(), TENANT_SECURITY_SCRIPTS.name().toLowerCase()); public AllConfigurationResourceVisitor(List fullBonitaConfigurations) { this.fullBonitaConfigurations = fullBonitaConfigurations; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { return FileVisitResult.CONTINUE; } private String getFolderName(Path dir) { return dir.getFileName().toString().toUpperCase(); } private boolean isConfigurationFolder(Path dir) { return CONFIGURATION_FOLDERS.contains(dir.getFileName().toString()); } @Override public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException { if (isConfigurationFile(path)) { final String configurationType = getFolderName(path.getParent()); if (LOGGER.isDebugEnabled()) { LOGGER.debug("found file: {}/{}", configurationType.toLowerCase(), path.getFileName()); } fullBonitaConfigurations.add(new FullBonitaConfiguration(path.getFileName().toString(), Files.readAllBytes(path), configurationType)); } return FileVisitResult.CONTINUE; } private boolean isConfigurationFile(Path path) { return path.toFile().isFile() && isConfigurationFolder(path.getParent()); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/AutoUpdateConfigurationVisitor.java ================================================ /** * Copyright (C) 2017 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.platform.configuration.util; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.Arrays; import java.util.List; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Laurent Leseigneur */ public class AutoUpdateConfigurationVisitor extends SimpleFileVisitor { private final List bonitaConfigurations; private static final Logger LOGGER = LoggerFactory.getLogger(AutoUpdateConfigurationVisitor.class); public AutoUpdateConfigurationVisitor(List bonitaConfigurations) { this.bonitaConfigurations = bonitaConfigurations; } private static final List AUTO_UPDATE_CONFIGURATION_FILES = Arrays.asList( "compound-permissions-mapping.properties", "dynamic-permissions-checks.properties", "resources-permissions-mapping.properties", "user-creation-attribute-mapping.properties"); @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attributes) { return FileVisitResult.CONTINUE; } private String getFolderName(Path dir) { return dir.getFileName().toString().toUpperCase(); } @Override public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException { if (isAutoUpdateConfigurationFile(path)) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(buildMessage(path, getFolderName(path.getParent()))); } bonitaConfigurations.add(new BonitaConfiguration(path.getFileName().toString(), Files.readAllBytes(path))); } return FileVisitResult.CONTINUE; } private String buildMessage(Path path, String configurationType) { return "found file: " + configurationType.toLowerCase() + "/" + path.getFileName(); } boolean isAutoUpdateConfigurationFile(Path path) { return path.toFile().isFile() && AUTO_UPDATE_CONFIGURATION_FILES.contains(path.getFileName().toString()); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/CleanAndStoreAllConfigurationInTransaction.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.util; import java.util.List; import org.bonitasoft.platform.configuration.impl.BonitaAllConfigurationContentTypeCleaner; import org.bonitasoft.platform.configuration.impl.BonitaAllConfigurationPreparedStatementSetter; import org.bonitasoft.platform.configuration.impl.ConfigurationServiceImpl; import org.bonitasoft.platform.configuration.model.FullBonitaConfiguration; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; /** * @author Laurent Leseigneur */ public class CleanAndStoreAllConfigurationInTransaction extends TransactionCallbackWithoutResult { private final JdbcTemplate jdbcTemplate; private final List bonitaConfigurations; private final String dbVendor; private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ConfigurationServiceImpl.class); public CleanAndStoreAllConfigurationInTransaction(JdbcTemplate jdbcTemplate, String dbVendor, List bonitaConfigurations) { this.jdbcTemplate = jdbcTemplate; this.dbVendor = dbVendor; this.bonitaConfigurations = bonitaConfigurations; } @Override protected void doInTransactionWithoutResult(TransactionStatus status) { LOGGER.debug("delete existing configurations {}", bonitaConfigurations); jdbcTemplate.batchUpdate(BonitaAllConfigurationContentTypeCleaner.DELETE_CONFIGURATION, new BonitaAllConfigurationContentTypeCleaner(bonitaConfigurations)); jdbcTemplate.batchUpdate(BonitaAllConfigurationPreparedStatementSetter.INSERT_CONFIGURATION, new BonitaAllConfigurationPreparedStatementSetter(bonitaConfigurations, dbVendor)); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/CleanAndStoreConfigurationInTransaction.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.util; import java.util.List; import org.bonitasoft.platform.configuration.impl.BonitaConfigurationContentTypeCleaner; import org.bonitasoft.platform.configuration.impl.BonitaConfigurationPreparedStatementSetter; import org.bonitasoft.platform.configuration.impl.ConfigurationServiceImpl; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.bonitasoft.platform.configuration.type.ConfigurationType; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; /** * @author Laurent Leseigneur */ public class CleanAndStoreConfigurationInTransaction extends TransactionCallbackWithoutResult { private final JdbcTemplate jdbcTemplate; private final List bonitaConfigurations; private final ConfigurationType type; private final String dbVendor; private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ConfigurationServiceImpl.class); public CleanAndStoreConfigurationInTransaction(JdbcTemplate jdbcTemplate, String dbVendor, List bonitaConfigurations, ConfigurationType type) { this.jdbcTemplate = jdbcTemplate; this.dbVendor = dbVendor; this.bonitaConfigurations = bonitaConfigurations; this.type = type; } @Override protected void doInTransactionWithoutResult(TransactionStatus status) { LOGGER.debug("delete existing configurations for type:{}", type.name()); jdbcTemplate.batchUpdate(BonitaConfigurationContentTypeCleaner.DELETE_CONFIGURATION, new BonitaConfigurationContentTypeCleaner(type)); jdbcTemplate.batchUpdate(BonitaConfigurationPreparedStatementSetter.INSERT_CONFIGURATION, new BonitaConfigurationPreparedStatementSetter(bonitaConfigurations, dbVendor, type)); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/ConfigurationResourceVisitor.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.util; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.List; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Laurent Leseigneur */ public class ConfigurationResourceVisitor extends SimpleFileVisitor { private final List bonitaConfigurations; private final static Logger LOGGER = LoggerFactory.getLogger(ConfigurationResourceVisitor.class); public ConfigurationResourceVisitor(List bonitaConfigurations) { this.bonitaConfigurations = bonitaConfigurations; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException { LOGGER.info("found file {}", path.getFileName()); bonitaConfigurations.add(new BonitaConfiguration(path.getFileName().toString(), Files.readAllBytes(path))); return FileVisitResult.CONTINUE; } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/DeleteAllConfigurationInTransaction.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.util; import org.bonitasoft.platform.configuration.impl.BonitaConfigurationCleaner; import org.bonitasoft.platform.configuration.impl.ConfigurationServiceImpl; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; /** * @author Laurent Leseigneur */ public class DeleteAllConfigurationInTransaction extends TransactionCallbackWithoutResult { private final JdbcTemplate jdbcTemplate; private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ConfigurationServiceImpl.class); public DeleteAllConfigurationInTransaction(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { LOGGER.debug("Execute DeleteAllConfigurationInTransaction transaction."); jdbcTemplate.batchUpdate(BonitaConfigurationCleaner.DELETE_ALL_CONFIGURATION, new BonitaConfigurationCleaner()); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/FlattenFolderVisitor.java ================================================ /** * 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.platform.configuration.util; import java.io.File; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.Map; import java.util.Objects; /** * @author Laurent Leseigneur */ public class FlattenFolderVisitor extends SimpleFileVisitor { private final Map flatFileMap; public FlattenFolderVisitor(Map flatFileMap) { this.flatFileMap = flatFileMap; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException { Objects.requireNonNull(path); Objects.requireNonNull(basicFileAttributes); final File file = path.toFile(); if (file.isFile()) { flatFileMap.put(file.getName(), file); } return FileVisitResult.CONTINUE; } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/GetAllConfigurationInTransaction.java ================================================ /** * 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.platform.configuration.util; import java.util.List; import org.bonitasoft.platform.configuration.impl.FullBonitaConfigurationRowMapper; import org.bonitasoft.platform.configuration.model.FullBonitaConfiguration; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; /** * @author Laurent Leseigneur */ public class GetAllConfigurationInTransaction implements TransactionCallback> { private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(GetAllConfigurationInTransaction.class); private final JdbcTemplate jdbcTemplate; public GetAllConfigurationInTransaction(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public List doInTransaction(TransactionStatus transactionStatus) { LOGGER.debug("Get all configurations from database"); final List fullBonitaConfigurations = jdbcTemplate.query( FullBonitaConfigurationRowMapper.SELECT_CONFIGURATION, new FullBonitaConfigurationRowMapper()); LOGGER.debug("Configurations found:{}", fullBonitaConfigurations); return fullBonitaConfigurations; } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/GetConfigurationInTransaction.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.util; import java.util.List; import org.bonitasoft.platform.configuration.impl.BonitaConfigurationRowMapper; import org.bonitasoft.platform.configuration.impl.ConfigurationServiceImpl; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.bonitasoft.platform.configuration.type.ConfigurationType; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; /** * @author Laurent Leseigneur */ public class GetConfigurationInTransaction implements TransactionCallback { private final JdbcTemplate jdbcTemplate; private final ConfigurationType type; private final String resourceName; private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(ConfigurationServiceImpl.class); public GetConfigurationInTransaction(JdbcTemplate jdbcTemplate, ConfigurationType type, String resourceName) { this.jdbcTemplate = jdbcTemplate; this.type = type; this.resourceName = resourceName; } @Override public BonitaConfiguration doInTransaction(TransactionStatus status) { LOGGER.debug("get configurations for type:{} resource:{}", type.name(), resourceName); final List bonitaConfigurations = jdbcTemplate.query( BonitaConfigurationRowMapper.SELECT_CONFIGURATION, new Object[] { type.name(), resourceName }, new BonitaConfigurationRowMapper()); LOGGER.debug("configurations found:{}", bonitaConfigurations); if (bonitaConfigurations.size() == 1) { return bonitaConfigurations.get(0); } return null; } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/GetConfigurationsInTransaction.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.util; import java.util.List; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.platform.configuration.impl.BonitaConfigurationRowMapper; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.bonitasoft.platform.configuration.type.ConfigurationType; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; /** * @author Laurent Leseigneur */ @Slf4j public class GetConfigurationsInTransaction implements TransactionCallback> { private final JdbcTemplate jdbcTemplate; private final ConfigurationType type; public GetConfigurationsInTransaction(JdbcTemplate jdbcTemplate, ConfigurationType type) { this.jdbcTemplate = jdbcTemplate; this.type = type; } @Override public List doInTransaction(TransactionStatus status) { log.debug("get configurations for type:{}", type.name()); final List bonitaConfigurations = jdbcTemplate.query( BonitaConfigurationRowMapper.SELECT_CONFIGURATION_FOR_TYPE, new Object[] { type.name() }, new BonitaConfigurationRowMapper()); log.debug("configurations found:{}", bonitaConfigurations); return bonitaConfigurations; } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/GetMandatoryStructureConfiguration.java ================================================ /** * Copyright (C) 2018 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.platform.configuration.util; import static org.bonitasoft.platform.configuration.impl.ConfigurationFields.CONTENT_TYPE; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import org.bonitasoft.platform.configuration.model.LightBonitaConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; /** * @author Emmanuel Duchastenier */ public class GetMandatoryStructureConfiguration implements TransactionCallback> { private final static Logger LOGGER = LoggerFactory.getLogger(GetMandatoryStructureConfiguration.class); private final JdbcTemplate jdbcTemplate; public GetMandatoryStructureConfiguration(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public List doInTransaction(TransactionStatus transactionStatus) { final List lightBonitaConfigurations = jdbcTemplate .query(LightBonitaConfigurationRowMapper.SELECT, new LightBonitaConfigurationRowMapper()); LOGGER.debug("configurations found:{}", lightBonitaConfigurations); return lightBonitaConfigurations; } class LightBonitaConfigurationRowMapper implements RowMapper { public static final String SELECT = "SELECT distinct content_type" + " FROM configuration" + " WHERE content_type <> 'LICENSES'" + " ORDER BY content_type"; @Override public LightBonitaConfiguration mapRow(ResultSet rs, int rowNum) throws SQLException { return new LightBonitaConfiguration(rs.getString(CONTENT_TYPE)); } } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/LicensesResourceVisitor.java ================================================ /** * Copyright (C) 2016 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.platform.configuration.util; import static java.nio.file.FileVisitResult.CONTINUE; import static java.nio.file.FileVisitResult.SKIP_SUBTREE; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.List; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Laurent Leseigneur */ public class LicensesResourceVisitor extends SimpleFileVisitor { private final List bonitaConfigurations; private static final Logger LOGGER = LoggerFactory.getLogger(LicensesResourceVisitor.class); private Path dir; public LicensesResourceVisitor(List bonitaConfigurations) { this.bonitaConfigurations = bonitaConfigurations; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { initRootFolder(dir); if (isSubFolder(dir)) { return SKIP_SUBTREE; } return CONTINUE; } private boolean isSubFolder(Path dir) { return !this.dir.equals(dir); } private void initRootFolder(Path dir) { if (this.dir == null) { this.dir = dir; } } @Override public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes) throws IOException { if (isLicenseFile(path)) { LOGGER.info("Found license file: {}", path.getFileName()); bonitaConfigurations.add(new BonitaConfiguration(path.getFileName().toString(), Files.readAllBytes(path))); } return CONTINUE; } private boolean isLicenseFile(Path path) { return path.getFileName().toString().endsWith(".lic"); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/StoreConfigurationInTransaction.java ================================================ /** * 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.platform.configuration.util; import java.util.List; import org.bonitasoft.platform.configuration.impl.BonitaConfigurationPreparedStatementCleaner; import org.bonitasoft.platform.configuration.impl.BonitaConfigurationPreparedStatementSetter; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.bonitasoft.platform.configuration.type.ConfigurationType; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; /** * @author Laurent Leseigneur */ public class StoreConfigurationInTransaction extends TransactionCallbackWithoutResult { private final JdbcTemplate jdbcTemplate; private final List bonitaConfigurations; private final ConfigurationType type; private final String dbVendor; private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(StoreConfigurationInTransaction.class); public StoreConfigurationInTransaction(JdbcTemplate jdbcTemplate, String dbVendor, List bonitaConfigurations, ConfigurationType type) { this.jdbcTemplate = jdbcTemplate; this.dbVendor = dbVendor; this.bonitaConfigurations = bonitaConfigurations; this.type = type; } @Override protected void doInTransactionWithoutResult(TransactionStatus status) { LOGGER.debug("delete configurations for type:{} bonitaConfigurations:{}", type.name(), bonitaConfigurations); jdbcTemplate.batchUpdate(BonitaConfigurationPreparedStatementCleaner.DELETE_CONFIGURATION, new BonitaConfigurationPreparedStatementCleaner(bonitaConfigurations, type)); LOGGER.debug("store configurations for type:{} bonitaConfigurations:{}", type.name(), bonitaConfigurations); jdbcTemplate.batchUpdate(BonitaConfigurationPreparedStatementSetter.INSERT_CONFIGURATION, new BonitaConfigurationPreparedStatementSetter(bonitaConfigurations, dbVendor, type)); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/StoreConfigurationsIfNotExist.java ================================================ /** * Copyright (C) 2021 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.platform.configuration.util; import static java.util.Collections.singletonList; import static org.bonitasoft.platform.configuration.impl.ConfigurationServiceImpl.LOGGER; import java.util.List; import org.bonitasoft.platform.configuration.impl.BonitaAllConfigurationPreparedStatementSetter; import org.bonitasoft.platform.configuration.model.FullBonitaConfiguration; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; /** * @author Emmanuel Duchastenier */ public class StoreConfigurationsIfNotExist extends TransactionCallbackWithoutResult { public static final String SELECT_CONFIGURATION_EXISTS = "SELECT count(1) FROM configuration WHERE content_type = ? AND resource_name = ?"; private final JdbcTemplate jdbcTemplate; private final String dbVendor; private final List configurations; public StoreConfigurationsIfNotExist(JdbcTemplate jdbcTemplate, String dbVendor, List configurations) { this.jdbcTemplate = jdbcTemplate; this.dbVendor = dbVendor; this.configurations = configurations; } @Override public void doInTransactionWithoutResult(TransactionStatus status) { for (FullBonitaConfiguration configuration : configurations) { final Integer nbRows = jdbcTemplate.queryForObject(SELECT_CONFIGURATION_EXISTS, Integer.class, configuration.getConfigurationType(), configuration.getResourceName()); if (nbRows == 0) { // only keep elements that do not already exist in database... LOGGER.info("New configuration file detected '{}'. Storing it to database.", configuration.getResourceName()); jdbcTemplate.batchUpdate(BonitaAllConfigurationPreparedStatementSetter.INSERT_CONFIGURATION, new BonitaAllConfigurationPreparedStatementSetter(singletonList(configuration), dbVendor)); } else { LOGGER.debug("Configuration already exists for type: {}, resource: {}. Ignoring it.", configuration.getConfigurationType(), configuration.getResourceName()); } } } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/configuration/util/UpdateConfigurationInTransaction.java ================================================ /** * Copyright (C) 2017 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.platform.configuration.util; import java.util.List; import org.bonitasoft.platform.configuration.impl.BonitaConfigurationTenantUpdater; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.bonitasoft.platform.configuration.type.ConfigurationType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; /** * @author Emmanuel Duchastenier */ public class UpdateConfigurationInTransaction extends TransactionCallbackWithoutResult { private final JdbcTemplate jdbcTemplate; private final List bonitaConfigurations; private final ConfigurationType type; private final String dbVendor; private final static Logger LOGGER = LoggerFactory.getLogger(UpdateConfigurationInTransaction.class); public UpdateConfigurationInTransaction(JdbcTemplate jdbcTemplate, String dbVendor, List bonitaConfigurations, ConfigurationType type) { this.jdbcTemplate = jdbcTemplate; this.dbVendor = dbVendor; this.bonitaConfigurations = bonitaConfigurations; this.type = type; } @Override protected void doInTransactionWithoutResult(TransactionStatus status) { LOGGER.debug("Updating configuration files {} of type:{}", bonitaConfigurations.toString(), type.name()); jdbcTemplate.batchUpdate(BonitaConfigurationTenantUpdater.UPDATE_ALL_TENANTS_CONFIGURATION, new BonitaConfigurationTenantUpdater(bonitaConfigurations, dbVendor, type)); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/database/DatabaseVendor.java ================================================ /** * 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.platform.database; import lombok.Getter; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @Getter public enum DatabaseVendor { H2("h2"), // MYSQL("mysql"), // ORACLE("oracle"), // POSTGRES("postgres"), // SQLSERVER("sqlserver"); private final String value; public static DatabaseVendor parseValue(String databaseVendorValue) { for (DatabaseVendor databaseVendor : DatabaseVendor.values()) { if (databaseVendor.value.equalsIgnoreCase(databaseVendorValue)) { return databaseVendor; } } throw new IllegalArgumentException("Unknown database vendor: " + databaseVendorValue); } public boolean equalsValue(String anotherValue) { return value.equals(anotherValue); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/exception/PlatformException.java ================================================ /** * Copyright (C) 2016 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.platform.exception; /** * @author Laurent Leseigneur */ public class PlatformException extends Exception { public PlatformException(Exception e) { super(e); } public PlatformException(String message) { super(message); } public PlatformException(String message, Exception cause) { super(message, cause); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/setup/PlatformSetup.java ================================================ /** * 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.platform.setup; import static java.lang.System.lineSeparator; import static org.bonitasoft.platform.configuration.type.ConfigurationType.*; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import javax.sql.DataSource; import lombok.Getter; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.filefilter.RegexFileFilter; import org.bonitasoft.platform.configuration.ConfigurationService; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.bonitasoft.platform.configuration.model.FullBonitaConfiguration; import org.bonitasoft.platform.configuration.model.LightBonitaConfiguration; import org.bonitasoft.platform.configuration.type.ConfigurationType; import org.bonitasoft.platform.database.DatabaseVendor; import org.bonitasoft.platform.exception.PlatformException; import org.bonitasoft.platform.version.VersionService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.stereotype.Component; /** * Manages database schema creation, initialization, and configuration for the Bonita Platform. *

      * This component is responsible for preparing the database environment required by the Bonita Engine. * It handles both initial setup (first run) and updates (subsequent runs), ensuring the database schema * and configuration are consistent with the platform binaries version. *

      * Main Responsibilities: *

        *
      • Database Schema Management: Creates all required tables on first run via {@link ScriptExecutor} * (platform, configuration, sequence, and 50+ BPM tables)
      • *
      • Configuration Management: Stores and retrieves configuration files in the database via * {@link ConfigurationService}
      • *
      • Version Compatibility: Validates platform binaries version matches database schema version * via {@link VersionService}
      • *
      • Configuration Push/Pull: Synchronizes configuration files between filesystem and database
      • *
      • License Management: Handles license file storage (subscription edition)
      • *
      *

      * Key Methods: *

        *
      • {@link #init()}: Main entry point - creates tables on first run or updates configuration on subsequent runs
      • *
      • {@link #push()}: Pushes configuration from {@code platform_conf/current/} to database
      • *
      • {@link #pull()}: Pulls configuration from database to {@code platform_conf/current/}
      • *
      • {@link #destroy()}: Drops all database tables
      • *
      *

      * Configuration Types Managed: *

        *
      • PLATFORM_ENGINE: Platform-level engine configuration
      • *
      • PLATFORM_PORTAL: Platform-level portal configuration
      • *
      • TENANT_ENGINE: Tenant-level engine configuration
      • *
      • TENANT_PORTAL: Tenant-level portal configuration (permissions, security)
      • *
      • TENANT_SECURITY_SCRIPTS: Authorization and security scripts
      • *
      *

      * The class uses the following folder structure: * *

       * platform_conf/
       *   ├── initial/          - Initial configuration (used on first setup)
       *   ├── current/          - Current configuration (for push/pull operations)
       *   ├── licenses/         - License files (subscription only)
       *   └── backup-[timestamp]/ - Backup before push operations
       * 
      * * @author Baptiste Mesta * @see ScriptExecutor * @see ConfigurationService * @see VersionService */ @Component @ConditionalOnSingleCandidate(PlatformSetup.class) public class PlatformSetup { public static final String BONITA_SETUP_FOLDER = "org.bonitasoft.platform.setup.folder"; public static final String PLATFORM_CONF_FOLDER_NAME = "platform_conf"; public static final String BONITA_CLIENT_HOME_FOLDER = "bonita.client.home"; public static final String BONITA_DB_VENDOR_PROPERTY = "sysprop.bonita.db.vendor"; public static final String BONITA_BDM_DB_VENDOR_PROPERTY = "sysprop.bonita.bdm.db.vendor"; protected static final Logger LOGGER = LoggerFactory.getLogger(PlatformSetup.class); private final ScriptExecutor scriptExecutor; @Getter // Used by distrib bundle tests private final ConfigurationService configurationService; @Getter private final VersionService versionService; private final DataSource dataSource; protected String dbVendor; protected String bdmDbVendor; private Path initialConfigurationFolder; private Path currentConfigurationFolder; private Path backupConfigurationFolder; protected Path licensesFolder; private Path backupLicensesFolder; private final ResourcePatternResolver cpResourceResolver = new PathMatchingResourcePatternResolver( PlatformSetup.class.getClassLoader()); public PlatformSetup(ScriptExecutor scriptExecutor, ConfigurationService configurationService, VersionService versionService, DataSource dataSource, @Value("${db.vendor}") String dbVendor, @Value("${bdm.db.vendor}") String bdmDbVendor) { this.scriptExecutor = scriptExecutor; this.configurationService = configurationService; this.versionService = versionService; this.dataSource = dataSource; this.dbVendor = dbVendor; this.bdmDbVendor = bdmDbVendor; } /** * Gets the value of the system property indicated by the key {@link PlatformSetup#BONITA_DB_VENDOR_PROPERTY} * * @return the string value of the system property, or null if there is no property with that key */ public static String getPropertyBonitaDbVendor() { return System.getProperty(BONITA_DB_VENDOR_PROPERTY); } /** * Gets the value of the system property indicated by the key {@link PlatformSetup#BONITA_BDM_DB_VENDOR_PROPERTY} * * @return the string value of the system property, or null if there is no property with that key */ public static String getPropertyBonitaBdmDbVendor() { return System.getProperty(BONITA_BDM_DB_VENDOR_PROPERTY); } /** * Entry point that create the tables and insert the default configuration */ public void init() throws PlatformException { initPlatformSetup(); if (isPlatformAlreadyCreated()) { LOGGER.info("Platform is already created."); if (Files.isDirectory(initialConfigurationFolder)) { LOGGER.info("Upgrading default configuration with files from folder: {}", initialConfigurationFolder); updateDefaultConfigurationFromFolder(initialConfigurationFolder); } else { LOGGER.info("Upgrading default configuration with files from classpath"); updateDefaultConfigurationFromClasspath(); } insertNewConfigurationsFromClasspathIfExist(); return; } preventFromPushingZeroLicense(); initializePlatform(); LOGGER.info("Platform created."); if (Files.isDirectory(initialConfigurationFolder)) { LOGGER.info("Database will be initialized with configuration files from folder: {}", initialConfigurationFolder); pushFromFolder(initialConfigurationFolder); } else { LOGGER.warn("Database will be initialized with configuration files from classpath"); insertNewConfigurationsFromClasspathIfExist(); } pushLicenses(true); LOGGER.info("Initial configuration files successfully pushed to database"); } boolean isPlatformAlreadyCreated() { return scriptExecutor.isPlatformAlreadyCreated(); } private void pushFromFolder(Path folderToPush) throws PlatformException { configurationService.storeAllConfiguration(folderToPush); } private void checkPushFolderExists(Path folderToPush) throws PlatformException { if (!Files.isDirectory(folderToPush)) { throw new PlatformException( "Unable to push configuration from " + folderToPush + ", as directory does not exists. To modify your configuration, run 'setup pull', update your configuration files from " + currentConfigurationFolder + " folder, and then push your new configuration."); } } void clean() { configurationService.deleteAllConfiguration(); } /** * push all configuration files and licenses */ public void push() throws PlatformException { push(false); } public void forcePush() throws PlatformException { push(true); } /** * push all configuration files and licenses * * @param forcePush shall we skip the check for removed folders? */ public void push(boolean forcePush) throws PlatformException { initPlatformSetup(); if (!isPlatformAlreadyCreated()) { throw new PlatformException("Platform is not created. Run 'setup init' first."); } preventFromPushingZeroLicense(); checkPlatformVersion(); checkPushFolderExists(currentConfigurationFolder); LOGGER.info("Configuration currently in database will be replaced by configuration from folder: {}", currentConfigurationFolder); ensureNoCriticalFoldersAreDeleted(forcePush); pull(backupConfigurationFolder, backupLicensesFolder); LOGGER.info("Backup directory created: {}", backupConfigurationFolder); var hasLicenses = !getConfigurationService().getLicenses().isEmpty(); clean(); pushFromFolder(currentConfigurationFolder); pushLicenses(hasLicenses); LOGGER.info( "Configuration files successfully pushed to database. You can now restart Bonita to reflect your changes."); } private void ensureNoCriticalFoldersAreDeleted(boolean forcePush) throws PlatformException { final List configurations = configurationService .getMandatoryStructureConfiguration(); for (LightBonitaConfiguration configuration : configurations) { // check no mandatory folder from database is no more in the FileSystem and about to be deleted: final Path folder = getFolderFromConfiguration(configuration); if (!Files.isDirectory(folder)) { if (forcePush) { LOGGER.warn("Force-pushing the deletion of folder {}", folder); } else { throw new PlatformException("You are trying to remove a protected folder from configuration: " + getSpecificErrorMessage(folder)); } } } } protected Path getFolderFromConfiguration(LightBonitaConfiguration configuration) { return currentConfigurationFolder.resolve(configuration.type().toLowerCase()); } private String getSpecificErrorMessage(Path folder) { return "You are not allowed to remove folder '" + folder.toString() + "'" + lineSeparator() + "To restore the deleted folders, run 'setup pull'. You will lose the locally modified configuration."; } /** * Entry point to retrieve all configuration files and write them to folder * each file will be located under sub folder according to its purpose. See * {@link org.bonitasoft.platform.configuration.type.ConfigurationType} for all * available values */ public void pull() throws PlatformException { initPlatformSetup(); checkPlatformVersion(); LOGGER.info("Pulling configuration into folder: {}", currentConfigurationFolder); if (Files.isDirectory(licensesFolder)) { LOGGER.info("Pulling licenses into folder: {}", licensesFolder); } pull(currentConfigurationFolder, licensesFolder); LOGGER.info( "Configuration (and license) files successfully pulled. You can now edit them. Use \"setup push\" when done."); } public void pull(Path configurationFolder, Path licensesFolder) throws PlatformException { try { recreateDirectory(configurationFolder); if (Files.isDirectory(licensesFolder)) { FileUtils.cleanDirectory(licensesFolder.toFile()); } List licenses = new ArrayList<>(); List files = configurationService.writeAllConfigurationToFolder(configurationFolder.toFile(), licensesFolder.toFile()); LOGGER.info("Retrieved following files in {}", configurationFolder); for (File file : files) { if (file.toPath().getParent().equals(licensesFolder)) { licenses.add(file); } else { LOGGER.info(configurationFolder.relativize(file.toPath()).toString()); } } if (!licenses.isEmpty()) { LOGGER.info("Retrieved following licenses in {}", licensesFolder); for (File license : licenses) { LOGGER.info(licensesFolder.relativize(license.toPath()).toString()); } } } catch (IOException e) { throw new PlatformException(e); } } private void recreateDirectory(Path... folders) throws IOException { for (Path folder : folders) { if (Files.exists(folder)) { FileUtils.deleteDirectory(folder.toFile()); } Files.createDirectories(folder); } } private void checkPlatformVersion() throws PlatformException { if (!versionService.isValidPlatformVersion()) { String message = "The version of the platform (binaries) you are running [{0}] " + "only support database schema in version [{1}] but the current database schema version is [{2}]. " + "You might need to migrate your platform or use a different version of the binaries."; throw new PlatformException(MessageFormat.format(message, versionService.getPlatformSetupVersion(), versionService.getSupportedDatabaseSchemaVersion(), versionService.retrieveDatabaseSchemaVersion())); } } /** * lookup for license file and push them to database */ protected void pushLicenses(boolean hasLicenses) throws PlatformException { // Nothing to do in community } private void initializePlatform() throws PlatformException { scriptExecutor.createAndInitializePlatformIfNecessary(); } void initProperties() throws PlatformException { if (dbVendor == null) { dbVendor = getPropertyBonitaDbVendor(); } checkSupportedVendor(dbVendor); if (bdmDbVendor == null) { bdmDbVendor = getPropertyBonitaBdmDbVendor(); } checkSupportedVendor(bdmDbVendor); String setupFolderPath = System.getProperty(BONITA_SETUP_FOLDER); Path platformConfFolder; if (setupFolderPath != null) { LOGGER.info("System property {} is set to {}", BONITA_SETUP_FOLDER, setupFolderPath); platformConfFolder = Paths.get(setupFolderPath).resolve(PLATFORM_CONF_FOLDER_NAME); } else { platformConfFolder = Paths.get(PLATFORM_CONF_FOLDER_NAME); } initializeFoldersPaths(platformConfFolder); } /** * Check that the given `databaseVendor` is supported using the community edition. * * @param databaseVendor database vendor to check * @throws PlatformException if the given database vendor is not supported * @throws IllegalArgumentException if `databaseVendor` is null or is not a recognized database vendor */ protected void checkSupportedVendor(String databaseVendor) throws PlatformException, IllegalArgumentException { DatabaseVendor vendor = DatabaseVendor.parseValue(databaseVendor); if (vendor != DatabaseVendor.H2 && vendor != DatabaseVendor.POSTGRES) { throw new PlatformException("Database vendor '" + vendor + "' is not supported with the community edition"); } LOGGER.debug("Database vendor '{}' is supported", vendor); } private void initializeFoldersPaths(Path platformConfFolder) { initialConfigurationFolder = platformConfFolder.resolve("initial"); currentConfigurationFolder = platformConfFolder.resolve("current"); Path rootBackupFolder = platformConfFolder.resolve("backup-" + System.currentTimeMillis()); backupConfigurationFolder = rootBackupFolder.resolve("current"); backupLicensesFolder = rootBackupFolder.resolve("licenses"); licensesFolder = getLicenseInitialFolder(platformConfFolder); } private Path getLicenseInitialFolder(Path platformConfFolder) { final String bonita_client_home = System.getProperty(BONITA_CLIENT_HOME_FOLDER); if (bonita_client_home != null) { return Paths.get(bonita_client_home); } return platformConfFolder.resolve("licenses"); } private void updateDefaultConfigurationFromFolder(Path folderToPush) throws PlatformException { configurationService.updateDefaultConfiguration(folderToPush); } private void insertNewConfigurationsFromClasspathIfExist() throws PlatformException { final ArrayList configurations = new ArrayList<>(); try { configurations.addAll(getConfigurationsMatchingPattern(PLATFORM_ENGINE)); configurations.addAll(getConfigurationsMatchingPattern(PLATFORM_PORTAL)); configurations.addAll(getConfigurationsMatchingPattern(TENANT_ENGINE)); configurations.addAll(getConfigurationsMatchingPattern(TENANT_PORTAL)); configurations.addAll(getConfigurationsMatchingPattern(TENANT_SECURITY_SCRIPTS)); } catch (IOException e) { throw new PlatformException(e); } configurationService.storeConfigurationsIfNotExist(configurations); } public List getConfigurationsMatchingPattern(ConfigurationType type) throws IOException { final ArrayList configurations = new ArrayList<>(); final String typeLowercase = type.name().toLowerCase(); Resource[] resources = cpResourceResolver .getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "/" + typeLowercase + "/**"); for (Resource resource : resources) { if (resource.exists() && resource.isReadable() && resource.contentLength() > 0) { String resourceName = resource.getFilename(); LOGGER.debug("Found configuration file '{}' of type '{}' in classpath", resourceName, type); try (InputStream resourceAsStream = resource.getInputStream()) { final byte[] content = IOUtils.toByteArray(resourceAsStream); configurations.add(new FullBonitaConfiguration(resourceName, content, type.name())); } } } return configurations; } private void updateDefaultConfigurationFromClasspath() throws PlatformException { List portalTenant = new ArrayList<>(3); try { addIfExists(portalTenant, TENANT_PORTAL, "compound-permissions-mapping.properties"); addIfExists(portalTenant, TENANT_PORTAL, "dynamic-permissions-checks.properties"); addIfExists(portalTenant, TENANT_PORTAL, "resources-permissions-mapping.properties"); } catch (IOException e) { throw new PlatformException(e); } configurationService.updateTenantPortalConf(portalTenant); } private void addIfExists(List configurations, ConfigurationType configurationType, String resourceName) throws IOException { BonitaConfiguration bonitaConfiguration = getBonitaConfigurationFromClassPath( configurationType.name().toLowerCase(), resourceName); if (bonitaConfiguration != null) { configurations.add(bonitaConfiguration); } } private void initDataSource() throws PlatformException { try { try (Connection connection = dataSource.getConnection()) { DatabaseMetaData metaData = connection.getMetaData(); LOGGER.info("Connected to '{}' database with url: '{}' with user: '{}'", dbVendor, metaData.getURL(), metaData.getUserName()); } } catch (SQLException e) { throw new PlatformException(e); } } private BonitaConfiguration getBonitaConfigurationFromClassPath(String folder, String resourceName) throws IOException { try (InputStream resourceAsStream = this.getClass().getResourceAsStream("/" + folder + "/" + resourceName)) { if (resourceAsStream == null) { return null; } LOGGER.debug("Using configuration from classpath {}", resourceName); return new BonitaConfiguration(resourceName, IOUtils.toByteArray(resourceAsStream)); } } public void destroy() throws PlatformException { initPlatformSetup(); if (isPlatformAlreadyCreated()) { scriptExecutor.deleteTables(); } } public void initPlatformSetup() throws PlatformException { initProperties(); initDataSource(); } void preventFromPushingZeroLicense() throws PlatformException { if (Files.isDirectory(licensesFolder)) { final String[] licenseFiles = licensesFolder.toFile().list(new RegexFileFilter(".*\\.lic")); if (licenseFiles == null || licenseFiles.length == 0) { throw new PlatformException("No license (.lic file) found." + lineSeparator() + "This would prevent Bonita Platform subscription edition to start normally." + lineSeparator() + "Place your license file in '" + licensesFolder.toAbsolutePath().toString() + "' and then try again."); } } } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/setup/PlatformSetupAccessor.java ================================================ /** * 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.platform.setup; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import org.bonitasoft.platform.configuration.ConfigurationService; import org.bonitasoft.platform.configuration.impl.ConfigurationServiceImpl; import org.bonitasoft.platform.version.VersionService; import org.bonitasoft.platform.version.impl.VersionServiceImpl; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.support.TransactionTemplate; /** * access platform setup outside of a spring context (to be replaced by a spring context...) * * @author Baptiste Mesta */ public class PlatformSetupAccessor { private static final PlatformSetupAccessor _UNIQUE = new PlatformSetupAccessor(); private PlatformSetup instance; protected PlatformSetupAccessor() { // Empty protected constructor to prevent instantiation } public static PlatformSetupAccessor getInstance() { return _UNIQUE; } public PlatformSetup getPlatformSetup() throws NamingException { if (instance == null) { instance = initPlatformSetup(); } return instance; } private PlatformSetup initPlatformSetup() throws NamingException { final DataSource dataSource = lookupDataSource(); String dbVendor = PlatformSetup.getPropertyBonitaDbVendor(); String bdmDbVendor = PlatformSetup.getPropertyBonitaBdmDbVendor(); return createNewPlatformSetup(dataSource, dbVendor, bdmDbVendor); } /** * WARNING: for internal use only. In normal cases, you should use PlatformSetupAccessor.getPlatformSetup() ! * * @param dataSource the datasource to use to access the database * @param dbVendor the Database vendor (default H2) to point at * @param bdmDbVendor the BDM Database vendor (default H2) to point at * @return a NEW instance of Platform Setup */ public PlatformSetup createNewPlatformSetup(DataSource dataSource, String dbVendor, String bdmDbVendor) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); final DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource); TransactionTemplate transactionTemplate = new TransactionTemplate(dataSourceTransactionManager); VersionService versionService = new VersionServiceImpl(jdbcTemplate); return createPlatformSetup( createScriptExecutor(dataSource, dbVendor, versionService), new ConfigurationServiceImpl(jdbcTemplate, transactionTemplate, dbVendor), versionService, dataSource, dbVendor, bdmDbVendor); } protected PlatformSetup createPlatformSetup(ScriptExecutor scriptExecutor, ConfigurationService configurationService, VersionService versionService, DataSource dataSource, String dbVendor, String bdmDbVendor) { return new PlatformSetup(scriptExecutor, configurationService, versionService, dataSource, dbVendor, bdmDbVendor); } protected ScriptExecutor createScriptExecutor(DataSource dataSource, String dbVendor, VersionService versionService) { return new ScriptExecutor(dbVendor, dataSource, versionService); } private static DataSource lookupDataSource() throws NamingException { Context ctx = new InitialContext(); return (DataSource) ctx.lookup( System.getProperty("sysprop.bonita.database.sequence.manager.datasource.name", "java:comp/env/bonitaSequenceManagerDS")); } public static ConfigurationService getConfigurationService() throws NamingException { final DataSource dataSource = lookupDataSource(); JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); final DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource); TransactionTemplate transactionTemplate = new TransactionTemplate(dataSourceTransactionManager); return new ConfigurationServiceImpl(jdbcTemplate, transactionTemplate, PlatformSetup.getPropertyBonitaDbVendor()); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/setup/ScriptExecutor.java ================================================ /** * 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.platform.setup; import static java.util.Arrays.asList; import static org.bonitasoft.platform.setup.PlatformSetup.BONITA_SETUP_FOLDER; import static org.bonitasoft.platform.setup.PlatformSetup.PLATFORM_CONF_FOLDER_NAME; import static org.bonitasoft.platform.setup.SimpleEncryptor.encrypt; import java.io.File; import java.io.IOException; import java.net.URL; import java.nio.file.Path; import java.nio.file.Paths; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.sql.DataSource; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.platform.exception.PlatformException; import org.bonitasoft.platform.version.VersionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.context.annotation.PropertySource; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.stereotype.Component; /** * @author Emmanuel Duchastenier */ @Slf4j @Component @ConditionalOnSingleCandidate(ScriptExecutor.class) @PropertySource("classpath:/application.properties") public class ScriptExecutor { private static final boolean CONTINUE_ON_ERROR = true; public static final boolean FAIL_ON_ERROR = false; private final String sqlFolder; private final DataSource datasource; private final String dbVendor; private final VersionService versionService; @Autowired public ScriptExecutor(@Value("${db.vendor}") String dbVendor, DataSource datasource, VersionService versionService) { if (dbVendor == null) { throw new IllegalArgumentException("dbVendor is null"); } this.dbVendor = dbVendor; this.datasource = datasource; log.info("configuration for Database vendor: {}", dbVendor); this.sqlFolder = "/sql/" + dbVendor; this.versionService = versionService; } public void createTables() throws PlatformException { try { executeSQLResources(asList("createTables.sql", "createQuartzTables.sql"), FAIL_ON_ERROR); } catch (final IOException e) { throw new PlatformException(e); } } /** * Creates and initializes the platform if it is not already done. It creates database tables and populates it. * * @return true if it is a first initialization of the platform. * @throws PlatformException if it fails to create or initialize tables. */ public void createAndInitializePlatformIfNecessary() throws PlatformException { if (!isPlatformAlreadyCreated()) { createTables(); initializePlatformStructure(); insertPlatform(); } else { log.info("Bonita platform already exists. Nothing to do. Stopping."); } } protected void insertPlatform() { String version = versionService.getPlatformSetupVersion(); String databaseSchemaVersion = versionService.getSupportedDatabaseSchemaVersion(); final String sql = "INSERT INTO platform (id, version, initial_bonita_version, application_version, " + "maintenance_message_active, created, created_by, information, maintenance_enabled) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"; new JdbcTemplate(datasource).update(sql, 1L, databaseSchemaVersion, version, "0.0.0", false, System.currentTimeMillis(), "platformAdmin", getInformationInitialValue(), false); } protected String getInformationInitialValue() { try { return encrypt(new ObjectMapper().writeValueAsBytes(new ArrayList())); } catch (GeneralSecurityException | JsonProcessingException e) { log.debug(e.getMessage(), e); throw new IllegalStateException("Cannot properly setup Bonita platform"); } } public boolean isPlatformAlreadyCreated() { try { return new JdbcTemplate(datasource).queryForObject("select count(*) from sequence", Integer.class) > 0; } catch (DataAccessException e) { return false; } } /** * @param sqlFiles the sql files to execute * @param shouldContinueOnError */ protected void executeSQLResources(final List sqlFiles, boolean shouldContinueOnError) throws IOException { for (final String sqlFile : sqlFiles) { executeSQLResource(sqlFile, shouldContinueOnError); } } /** * @param sqlFolder the folder to look in. * @param sqlFile the name of the file to load. * @return null if not found, the SQL text content in normal cases. */ private Resource getSQLResource(final String sqlFolder, final String sqlFile) { String setupFolderPath = System.getProperty(BONITA_SETUP_FOLDER); if (setupFolderPath != null) { return getResourceFromFileSystem(setupFolderPath, sqlFile); } else { return getResourceFromClassPath(sqlFolder, sqlFile); } } private Resource getResourceFromFileSystem(String setupFolderPath, String sqlFile) { Path path = Paths.get(setupFolderPath).resolve(PLATFORM_CONF_FOLDER_NAME).resolve("sql").resolve(dbVendor) .resolve(sqlFile); final File file = path.toFile(); if (file.exists()) { return new FileSystemResource(file); } else { final String msg = "SQL resource file not found in filesystem: " + file.getAbsolutePath(); log.error(msg); throw new RuntimeException(msg); } } private Resource getResourceFromClassPath(String sqlFolder, String sqlFile) { final String resourcePath = sqlFolder + "/" + sqlFile; // Must always be forward slash, even on Windows. final URL url = this.getClass().getResource(resourcePath); if (url != null) { return new UrlResource(url); } else { final String msg = "SQL resource file not found in classpath: " + resourcePath; log.warn(msg); throw new RuntimeException(msg); } } /** * @param sqlFile the sql file to execute * @param shouldContinueOnError * @throws IOException */ protected void executeSQLResource(final String sqlFile, boolean shouldContinueOnError) throws IOException { final Resource sqlResource = getSQLResource(sqlFolder, sqlFile); ResourceDatabasePopulator populate = new ResourceDatabasePopulator(); populate.setContinueOnError(shouldContinueOnError); populate.setIgnoreFailedDrops(true); populate.addScript(sqlResource); populate.execute(datasource); log.info("Executed SQL script {}", sqlResource.getURL().getFile()); } public void initializePlatformStructure() throws PlatformException { try { executeSQLResources(Collections.singletonList("initTables.sql"), FAIL_ON_ERROR); } catch (final IOException e) { throw new PlatformException(e); } } public void deleteTables() throws PlatformException { try { executeSQLResources(asList("preDropStructure.sql", "dropQuartzTables.sql", "dropTables.sql"), CONTINUE_ON_ERROR); } catch (final IOException e) { throw new PlatformException(e); } } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/setup/SimpleEncryptor.java ================================================ /** * 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.platform.setup; import java.security.GeneralSecurityException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; /** * @author Emmanuel Duchastenier */ public final class SimpleEncryptor { private static final byte[] SALT = new byte[] { 0x47, 0x6f, 0x6f, 0x64, 0x4a, 0x6f, 0x62, 0x21 }; private static final char[] PASSPHRASE = new String(new byte[] { 0x48, 0x34, 0x76, 0x33, 0x46, 0x75, 0x6e, 0x57, 0x31, 0x37, 0x68, 0x38, 0x30, 0x6e, 0x31, 0x37, 0x34 }).toCharArray(); private static final SecretKey SECRET_KEY = SimpleEncryptor.generateKey(); private SimpleEncryptor() { } public static SecretKey generateKey() { try { SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); KeySpec spec = new PBEKeySpec(PASSPHRASE, SALT, 1000, 256); return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES"); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { throw new IllegalStateException(e); } } public static String encrypt(byte[] data) throws GeneralSecurityException { Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, SECRET_KEY); byte[] encryptedBytes = cipher.doFinal(data); return Base64.getEncoder().encodeToString(encryptedBytes); } public static byte[] decrypt(String encryptedData) throws GeneralSecurityException { Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, SECRET_KEY); byte[] decodedBytes = Base64.getDecoder().decode(encryptedData); return cipher.doFinal(decodedBytes); } } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/version/VersionService.java ================================================ /** * 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.platform.version; import org.bonitasoft.platform.exception.PlatformException; /** * @author Laurent Leseigneur */ public interface VersionService { /** * Retrieves the platform version in database * * @return platform current version */ String retrieveDatabaseSchemaVersion() throws PlatformException; /** * Retrieves the platform information in database * * @return platform information * @throws PlatformException */ String retrievePlatformInformation() throws PlatformException; /** * Clear platform information * * @throws PlatformException */ void clearPlatformInformation() throws PlatformException; /** * Retrieves the platform setup tool version * * @return platform setup tool current version */ String getPlatformSetupVersion(); /** * @return the version of the database schema used by this setup tool */ String getSupportedDatabaseSchemaVersion(); /** * Check if platform an platform setup tool are in same version * * @return true if same version, false otherwise */ boolean isValidPlatformVersion() throws PlatformException; } ================================================ FILE: platform/platform-resources/src/main/java/org/bonitasoft/platform/version/impl/VersionServiceImpl.java ================================================ /** * Copyright (C) 2016 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.platform.version.impl; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.IOUtils; import org.bonitasoft.platform.exception.PlatformException; import org.bonitasoft.platform.version.VersionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; /** * @author Laurent Leseigneur */ @Service public class VersionServiceImpl implements VersionService { private static final String SQL_PLATFORM_VERSION = "SELECT p.version FROM platform p"; private static final String SQL_PLATFORM_INFORMATION = "SELECT p.information FROM platform p"; private static final String SQL_CLEAR_PLATFORM_INFORMATION = "UPDATE platform SET information = NULL"; private JdbcTemplate jdbcTemplate; @Autowired public VersionServiceImpl(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public String retrieveDatabaseSchemaVersion() throws PlatformException { final List strings; try { strings = jdbcTemplate.queryForList(SQL_PLATFORM_VERSION, String.class); } catch (DataAccessException e) { throw new PlatformException("Platform is not created. Run 'setup init' first.", e); } if (hasNotSingleResult(strings)) { throw new PlatformException("Platform is not created. Run 'setup init' first."); } return strings.get(0); } @Override public String retrievePlatformInformation() throws PlatformException { final List strings; try { strings = jdbcTemplate.queryForList(SQL_PLATFORM_INFORMATION, String.class); } catch (DataAccessException e) { throw new PlatformException("Platform is not created. Run 'setup init' first.", e); } if (hasNotSingleResult(strings)) { throw new PlatformException("Platform is not created. Run 'setup init' first."); } return strings.get(0); } @Override public void clearPlatformInformation() throws PlatformException { try { jdbcTemplate.execute(SQL_CLEAR_PLATFORM_INFORMATION); } catch (DataAccessException e) { throw new PlatformException("Unable to clear platform information", e); } } private boolean hasNotSingleResult(List strings) { return strings == null || strings.size() != 1; } @Override public String getPlatformSetupVersion() { return getVersionProperty("PLATFORM_ENGINE_VERSION"); } @Override public String getSupportedDatabaseSchemaVersion() { // right now the supported database schema version is equal to the minor version of the product: return extractMinorVersion(getPlatformSetupVersion()); } /** * This method is duplicate in class CheckPlatformVersion. * This is accepted to limit over-engineering just to extract an util method. */ private String extractMinorVersion(String version) { final Matcher matcher = Pattern.compile("(\\d+\\.\\d+).*").matcher(version); if (matcher.matches()) { return matcher.group(1); } else { throw new IllegalArgumentException(version + " does not respect Semantic Versioning"); } } private String getVersionProperty(String versionFileName) { try { return IOUtils.toString(this.getClass().getResource("/" + versionFileName), StandardCharsets.UTF_8); } catch (IOException e) { throw new IllegalStateException(versionFileName + " file in jar resources does not exists"); } } @Override public boolean isValidPlatformVersion() throws PlatformException { return getSupportedDatabaseSchemaVersion().equals(retrieveDatabaseSchemaVersion()); } } ================================================ FILE: platform/platform-resources/src/main/resources/PLATFORM_ENGINE_VERSION ================================================ @version@ ================================================ FILE: platform/platform-resources/src/main/resources/platform_engine/bonita-platform-custom.xml ================================================ ================================================ FILE: platform/platform-resources/src/main/resources/platform_portal/security-config.properties ================================================ #Enable/disable CSRF security filter security.csrf.enabled true #Enable/disable the Sanitizer protection activation (true/false). This sanitizer protects against multiple attacks such as XSS, but may restrict the use of some character sequences. security.sanitizer.enabled true #Name of the Attributes excluded from sanitizer protection (comma separated) security.sanitizer.exclude email,password,password_confirm #Add or not the secure flag to the CSRF token cookie (HTTPS only) security.csrf.cookie.secure false #X-Frame-Options response header value bonita.runtime.security.csrf.header.frame.options=SAMEORIGIN #Content-Security-Policy response header value bonita.runtime.security.csrf.header.content.security.policy=frame-ancestors 'self'; ================================================ FILE: platform/platform-resources/src/main/resources/sql/h2/cleanTables.sql ================================================ DELETE FROM arch_contract_data; DELETE FROM contract_data; DELETE FROM actormember; DELETE FROM actor; DELETE FROM processcategorymapping; DELETE FROM category; DELETE FROM arch_process_comment; DELETE FROM process_comment; DELETE FROM process_definition; DELETE FROM arch_document_mapping; DELETE FROM document; DELETE FROM document_mapping; DELETE FROM arch_flownode_instance; DELETE FROM arch_process_instance; DELETE FROM arch_connector_instance; DELETE FROM arch_multi_biz_data; DELETE FROM arch_ref_biz_data_inst; DELETE FROM multi_biz_data; DELETE FROM ref_biz_data_inst; DELETE FROM pending_mapping; DELETE FROM message_instance; DELETE FROM waiting_event; DELETE FROM event_trigger_instance; DELETE FROM connector_instance; DELETE FROM flownode_instance; DELETE FROM process_instance; DELETE FROM processsupervisor; DELETE FROM business_app_menu; DELETE FROM business_app_page; DELETE FROM business_app; DELETE FROM command; DELETE FROM arch_data_instance; DELETE FROM data_instance; DELETE FROM dependencymapping; DELETE FROM dependency; DELETE FROM user_membership; DELETE FROM custom_usr_inf_val; DELETE FROM custom_usr_inf_def; DELETE FROM user_contactinfo; DELETE FROM user_login; DELETE FROM user_; DELETE FROM role; DELETE FROM group_; DELETE FROM queriable_log; DELETE FROM page; DELETE FROM sequence; DELETE FROM profilemember; DELETE FROM profile; DELETE FROM job_log; DELETE FROM job_param; DELETE FROM job_desc; DELETE FROM tenant; DELETE FROM platformCommand; DELETE FROM form_mapping; DELETE FROM page_mapping; DELETE FROM proc_parameter; DELETE FROM arch_bpm_failure; DELETE FROM bpm_failure; DELETE FROM data_retention_config; DELETE FROM data_retention_bdm_tracking; DELETE FROM delegation_rule_process; DELETE FROM delegation_rule; -- do NOT clear directly PLATFORM table, Hibernate needs to update its cache to know the platform has been deleted ================================================ FILE: platform/platform-resources/src/main/resources/sql/h2/createQuartzTables.sql ================================================ CREATE TABLE QRTZ_CALENDARS ( SCHED_NAME VARCHAR(120) NOT NULL, CALENDAR_NAME VARCHAR (200) NOT NULL , CALENDAR IMAGE NOT NULL ); CREATE TABLE QRTZ_CRON_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , CRON_EXPRESSION VARCHAR (120) NOT NULL , TIME_ZONE_ID VARCHAR (80) ); CREATE TABLE QRTZ_FIRED_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, ENTRY_ID VARCHAR (95) NOT NULL , TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , INSTANCE_NAME VARCHAR (200) NOT NULL , FIRED_TIME BIGINT NOT NULL , SCHED_TIME BIGINT NOT NULL , PRIORITY INTEGER NOT NULL , STATE VARCHAR (16) NOT NULL, JOB_NAME VARCHAR (200) NULL , JOB_GROUP VARCHAR (200) NULL , IS_NONCONCURRENT BOOLEAN NULL , REQUESTS_RECOVERY BOOLEAN NULL ); CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_GROUP VARCHAR (200) NOT NULL ); CREATE TABLE QRTZ_SCHEDULER_STATE ( SCHED_NAME VARCHAR(120) NOT NULL, INSTANCE_NAME VARCHAR (200) NOT NULL , LAST_CHECKIN_TIME BIGINT NOT NULL , CHECKIN_INTERVAL BIGINT NOT NULL ); CREATE TABLE QRTZ_LOCKS ( SCHED_NAME VARCHAR(120) NOT NULL, LOCK_NAME VARCHAR (40) NOT NULL ); CREATE TABLE QRTZ_JOB_DETAILS ( SCHED_NAME VARCHAR(120) NOT NULL, JOB_NAME VARCHAR (200) NOT NULL , JOB_GROUP VARCHAR (200) NOT NULL , DESCRIPTION VARCHAR (250) NULL , JOB_CLASS_NAME VARCHAR (250) NOT NULL , IS_DURABLE BOOLEAN NOT NULL , IS_NONCONCURRENT BOOLEAN NOT NULL , IS_UPDATE_DATA BOOLEAN NOT NULL , REQUESTS_RECOVERY BOOLEAN NOT NULL , JOB_DATA IMAGE NULL ); CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , REPEAT_COUNT BIGINT NOT NULL , REPEAT_INTERVAL BIGINT NOT NULL , TIMES_TRIGGERED BIGINT NOT NULL ); CREATE TABLE QRTZ_SIMPROP_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, STR_PROP_1 VARCHAR(512) NULL, STR_PROP_2 VARCHAR(512) NULL, STR_PROP_3 VARCHAR(512) NULL, INT_PROP_1 INTEGER NULL, INT_PROP_2 INTEGER NULL, LONG_PROP_1 BIGINT NULL, LONG_PROP_2 BIGINT NULL, DEC_PROP_1 NUMERIC(13,4) NULL, DEC_PROP_2 NUMERIC(13,4) NULL, BOOL_PROP_1 BOOLEAN NULL, BOOL_PROP_2 BOOLEAN NULL ); CREATE TABLE QRTZ_BLOB_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , BLOB_DATA IMAGE NULL ); CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , JOB_NAME VARCHAR (200) NOT NULL , JOB_GROUP VARCHAR (200) NOT NULL , DESCRIPTION VARCHAR (250) NULL , NEXT_FIRE_TIME BIGINT NULL , PREV_FIRE_TIME BIGINT NULL , PRIORITY INTEGER NULL , TRIGGER_STATE VARCHAR (16) NOT NULL , TRIGGER_TYPE VARCHAR (8) NOT NULL , START_TIME BIGINT NOT NULL , END_TIME BIGINT NULL , CALENDAR_NAME VARCHAR (200) NULL , MISFIRE_INSTR SMALLINT NULL , JOB_DATA IMAGE NULL ); ALTER TABLE QRTZ_CALENDARS ADD CONSTRAINT PK_QRTZ_CALENDARS PRIMARY KEY ( SCHED_NAME, CALENDAR_NAME ); ALTER TABLE QRTZ_CRON_TRIGGERS ADD CONSTRAINT PK_QRTZ_CRON_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ); ALTER TABLE QRTZ_FIRED_TRIGGERS ADD CONSTRAINT PK_QRTZ_FIRED_TRIGGERS PRIMARY KEY ( SCHED_NAME, ENTRY_ID ); ALTER TABLE QRTZ_PAUSED_TRIGGER_GRPS ADD CONSTRAINT PK_QRTZ_PAUSED_TRIGGER_GRPS PRIMARY KEY ( SCHED_NAME, TRIGGER_GROUP ); ALTER TABLE QRTZ_SCHEDULER_STATE ADD CONSTRAINT PK_QRTZ_SCHEDULER_STATE PRIMARY KEY ( SCHED_NAME, INSTANCE_NAME ); ALTER TABLE QRTZ_LOCKS ADD CONSTRAINT PK_QRTZ_LOCKS PRIMARY KEY ( SCHED_NAME, LOCK_NAME ); ALTER TABLE QRTZ_JOB_DETAILS ADD CONSTRAINT PK_QRTZ_JOB_DETAILS PRIMARY KEY ( SCHED_NAME, JOB_NAME, JOB_GROUP ); ALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD CONSTRAINT PK_QRTZ_SIMPLE_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ); ALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD CONSTRAINT PK_QRTZ_SIMPROP_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ); ALTER TABLE QRTZ_TRIGGERS ADD CONSTRAINT PK_QRTZ_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ); ALTER TABLE QRTZ_CRON_TRIGGERS ADD CONSTRAINT FK_QRTZ_CRON_TRIGGERS FOREIGN KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) REFERENCES QRTZ_TRIGGERS ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) ON DELETE CASCADE; ALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD CONSTRAINT FK_QRTZ_SIMPLE_TRIGGERS FOREIGN KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) REFERENCES QRTZ_TRIGGERS ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) ON DELETE CASCADE; ALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD CONSTRAINT FK_QRTZ_SIMPROP_TRIGGERS FOREIGN KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) REFERENCES QRTZ_TRIGGERS ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) ON DELETE CASCADE; ALTER TABLE QRTZ_TRIGGERS ADD CONSTRAINT FK_QRTZ_TRIGGERS FOREIGN KEY ( SCHED_NAME, JOB_NAME, JOB_GROUP ) REFERENCES QRTZ_JOB_DETAILS ( SCHED_NAME, JOB_NAME, JOB_GROUP ); CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY); CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME); CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME); CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY); CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP); CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); ================================================ FILE: platform/platform-resources/src/main/resources/sql/h2/createTables.sql ================================================ CREATE TABLE configuration ( content_type VARCHAR(50) NOT NULL, resource_name VARCHAR(120) NOT NULL, resource_content LONGBLOB NOT NULL, CONSTRAINT pk_configuration PRIMARY KEY (content_type, resource_name) ); CREATE INDEX idx_configuration ON configuration (content_type); CREATE TABLE contract_data ( id BIGINT NOT NULL, kind VARCHAR(20) NOT NULL, scopeId BIGINT NOT NULL, name VARCHAR(50) NOT NULL, val CLOB, CONSTRAINT pk_contract_data PRIMARY KEY (id, scopeId), CONSTRAINT uk_contract_data_kind_scopeid_name UNIQUE (kind, scopeId, name) ); CREATE TABLE arch_contract_data ( id BIGINT NOT NULL, kind VARCHAR(20) NOT NULL, scopeId BIGINT NOT NULL, name VARCHAR(50) NOT NULL, val CLOB, archiveDate BIGINT NOT NULL, sourceObjectId BIGINT NOT NULL, CONSTRAINT pk_arch_contract_data PRIMARY KEY (id, scopeId), CONSTRAINT uk_arch_contract_data_kind_scopeid_name UNIQUE (kind, scopeId, name) ); CREATE TABLE actor ( id BIGINT NOT NULL, scopeId BIGINT NOT NULL, name VARCHAR(50) NOT NULL, displayName VARCHAR(75), description TEXT, initiator BOOLEAN, CONSTRAINT uk_actor_id_scopeid_name UNIQUE (id, scopeId, name), CONSTRAINT pk_actor PRIMARY KEY (id) ); CREATE TABLE actormember ( id BIGINT NOT NULL, actorId BIGINT NOT NULL, userId BIGINT NOT NULL, groupId BIGINT NOT NULL, roleId BIGINT NOT NULL, CONSTRAINT uk_actormember_actorid_userid_groupid_roleid UNIQUE (actorId, userId, groupId, roleId), CONSTRAINT pk_actormember PRIMARY KEY (id) ); ALTER TABLE actormember ADD CONSTRAINT fk_actormember_actorid FOREIGN KEY (actorId) REFERENCES actor(id); CREATE TABLE category ( id BIGINT NOT NULL, name VARCHAR(50) NOT NULL, creator BIGINT, description LONGVARCHAR, creationDate BIGINT NOT NULL, lastUpdateDate BIGINT NOT NULL, CONSTRAINT pk_category PRIMARY KEY (id), CONSTRAINT uk_category_name UNIQUE (name) ); CREATE TABLE processcategorymapping ( id BIGINT NOT NULL, categoryid BIGINT NOT NULL, processid BIGINT NOT NULL, CONSTRAINT pk_processcategorymapping PRIMARY KEY (id), CONSTRAINT uk_processcategorymapping_categoryid_processid UNIQUE (categoryid, processid) ); ALTER TABLE processcategorymapping ADD CONSTRAINT fk_processcategorymapping_categoryid FOREIGN KEY (categoryid) REFERENCES category(id) ON DELETE CASCADE; CREATE TABLE arch_process_comment( id BIGINT NOT NULL, userId BIGINT, processInstanceId BIGINT NOT NULL, postDate BIGINT NOT NULL, content VARCHAR(512) NOT NULL, archiveDate BIGINT NOT NULL, sourceObjectId BIGINT NOT NULL, CONSTRAINT pk_arch_process_comment PRIMARY KEY (id) ); CREATE INDEX idx1_arch_process_comment on arch_process_comment (sourceobjectid); CREATE INDEX idx2_arch_process_comment on arch_process_comment (processInstanceId, archivedate); CREATE TABLE process_comment ( id BIGINT NOT NULL, kind VARCHAR(25) NOT NULL, userId BIGINT, processInstanceId BIGINT NOT NULL, postDate BIGINT NOT NULL, content VARCHAR(512) NOT NULL, CONSTRAINT pk_process_comment PRIMARY KEY (id) ); CREATE INDEX idx1_process_comment on process_comment (processInstanceId); CREATE TABLE process_content ( id BIGINT NOT NULL, content MEDIUMTEXT NOT NULL, CONSTRAINT pk_process_content PRIMARY KEY (id) ); CREATE TABLE process_definition ( id BIGINT NOT NULL, processId BIGINT NOT NULL, name VARCHAR(150) NOT NULL, version VARCHAR(50) NOT NULL, description VARCHAR(255), deploymentDate BIGINT NOT NULL, deployedBy BIGINT NOT NULL, activationState VARCHAR(30) NOT NULL, configurationState VARCHAR(30) NOT NULL, displayName VARCHAR(75), displayDescription VARCHAR(255), lastUpdateDate BIGINT, categoryId BIGINT, iconPath VARCHAR(255), content_id BIGINT NOT NULL, CONSTRAINT pk_process_definition PRIMARY KEY (id), CONSTRAINT uk_process_definition_name_version UNIQUE (name, version) ); ALTER TABLE process_definition ADD CONSTRAINT fk_process_definition_content_id FOREIGN KEY (content_id) REFERENCES process_content(id); CREATE TABLE document ( id BIGINT NOT NULL, author BIGINT, creationdate BIGINT NOT NULL, hascontent BOOLEAN NOT NULL, filename VARCHAR(255), mimetype VARCHAR(255), url VARCHAR(1024), content LONGBLOB NULL, CONSTRAINT pk_document PRIMARY KEY (id) ); CREATE TABLE document_mapping ( id BIGINT NOT NULL, processinstanceid BIGINT NOT NULL, documentid BIGINT NOT NULL, name VARCHAR(50) NOT NULL, description TEXT, version VARCHAR(50) NOT NULL, index_ INT NOT NULL, CONSTRAINT pk_document_mapping PRIMARY KEY (id) ); ALTER TABLE document_mapping ADD CONSTRAINT fk_document_mapping_documentid FOREIGN KEY (documentid) REFERENCES document(id) ON DELETE CASCADE; CREATE TABLE arch_document_mapping ( id BIGINT NOT NULL, sourceObjectId BIGINT, processinstanceid BIGINT NOT NULL, documentid BIGINT NOT NULL, name VARCHAR(50) NOT NULL, description TEXT, version VARCHAR(50) NOT NULL, index_ INT NOT NULL, archiveDate BIGINT NOT NULL, CONSTRAINT pk_arch_document_mapping PRIMARY KEY (id) ); CREATE INDEX idx_a_doc_mp_pr_id ON arch_document_mapping (processinstanceid); ALTER TABLE arch_document_mapping ADD CONSTRAINT fk_arch_document_mapping_documentid FOREIGN KEY (documentid) REFERENCES document(id) ON DELETE CASCADE; CREATE TABLE arch_process_instance ( id BIGINT NOT NULL, name VARCHAR(75) NOT NULL, processDefinitionId BIGINT NOT NULL, description VARCHAR(255), startDate BIGINT NOT NULL, startedBy BIGINT NOT NULL, startedBySubstitute BIGINT NOT NULL, endDate BIGINT NOT NULL, archiveDate BIGINT NOT NULL, stateId INT NOT NULL, lastUpdate BIGINT NOT NULL, rootProcessInstanceId BIGINT, callerId BIGINT, sourceObjectId BIGINT NOT NULL, stringIndex1 VARCHAR(255), stringIndex2 VARCHAR(255), stringIndex3 VARCHAR(255), stringIndex4 VARCHAR(255), stringIndex5 VARCHAR(255), CONSTRAINT pk_arch_process_instance PRIMARY KEY (id) ); CREATE INDEX idx1_arch_process_instance ON arch_process_instance (sourceObjectId, rootProcessInstanceId, callerId); CREATE INDEX idx2_arch_process_instance ON arch_process_instance (processDefinitionId, archiveDate); CREATE INDEX idx3_arch_process_instance ON arch_process_instance (sourceObjectId, callerId, stateId); CREATE TABLE arch_flownode_instance ( id BIGINT NOT NULL, flownodeDefinitionId BIGINT NOT NULL, kind VARCHAR(25) NOT NULL, sourceObjectId BIGINT, archiveDate BIGINT NOT NULL, rootContainerId BIGINT NOT NULL, parentContainerId BIGINT NOT NULL, name VARCHAR(255) NOT NULL, displayName VARCHAR(255), displayDescription VARCHAR(255), stateId INT NOT NULL, stateName VARCHAR(50), terminal BOOLEAN NOT NULL, stable BOOLEAN , actorId BIGINT NULL, assigneeId BIGINT DEFAULT 0 NOT NULL, reachedStateDate BIGINT, lastUpdateDate BIGINT, expectedEndDate BIGINT, claimedDate BIGINT, priority TINYINT, gatewayType VARCHAR(50), hitBys VARCHAR(255), logicalGroup1 BIGINT NOT NULL, logicalGroup2 BIGINT NOT NULL, logicalGroup3 BIGINT, logicalGroup4 BIGINT NOT NULL, loop_counter INT, loop_max INT, loopCardinality INT, loopDataInputRef VARCHAR(255), loopDataOutputRef VARCHAR(255), description VARCHAR(255), sequential BOOLEAN, dataInputItemRef VARCHAR(255), dataOutputItemRef VARCHAR(255), nbActiveInst INT, nbCompletedInst INT, nbTerminatedInst INT, executedBy BIGINT, executedBySubstitute BIGINT, activityInstanceId BIGINT, aborting BOOLEAN NOT NULL, triggeredByEvent BOOLEAN, interrupting BOOLEAN, CONSTRAINT pk_arch_flownode_instance PRIMARY KEY (id) ); CREATE INDEX idx_afi_kind_lg2_executedBy ON arch_flownode_instance(logicalGroup2, kind, executedBy); CREATE INDEX idx_afi_kind_lg3 ON arch_flownode_instance(kind, logicalGroup3); CREATE INDEX idx_afi_lg4 ON arch_flownode_instance(logicalGroup4); CREATE INDEX idx_afi_sourceid_kind ON arch_flownode_instance (sourceObjectId, kind); CREATE INDEX idx1_afi_root_parent ON arch_flownode_instance (rootContainerId, parentContainerId); CREATE INDEX idx_lg4_lg2 on arch_flownode_instance(logicalGroup4, logicalGroup2); CREATE TABLE arch_connector_instance ( id BIGINT NOT NULL, containerId BIGINT NOT NULL, containerType VARCHAR(10) NOT NULL, connectorId VARCHAR(255) NOT NULL, version VARCHAR(50) NOT NULL, name VARCHAR(255) NOT NULL, activationEvent VARCHAR(30), state VARCHAR(50), sourceObjectId BIGINT, archiveDate BIGINT NOT NULL, CONSTRAINT pk_arch_connector_instance PRIMARY KEY (id) ); CREATE INDEX idx1_arch_connector_instance ON arch_connector_instance (containerId, containerType); CREATE TABLE process_instance ( id BIGINT NOT NULL, name VARCHAR(75) NOT NULL, processDefinitionId BIGINT NOT NULL, description VARCHAR(255), startDate BIGINT NOT NULL, startedBy BIGINT NOT NULL, startedBySubstitute BIGINT NOT NULL, endDate BIGINT NOT NULL, stateId INT NOT NULL, stateCategory VARCHAR(50) NOT NULL, lastUpdate BIGINT NOT NULL, containerId BIGINT, rootProcessInstanceId BIGINT, callerId BIGINT, callerType VARCHAR(50), interruptingEventId BIGINT, stringIndex1 VARCHAR(255), stringIndex2 VARCHAR(255), stringIndex3 VARCHAR(255), stringIndex4 VARCHAR(255), stringIndex5 VARCHAR(255), PRIMARY KEY (id) ); CREATE INDEX idx1_proc_inst_pdef_state ON process_instance (processdefinitionid, stateid); CREATE TABLE flownode_instance ( id BIGINT NOT NULL, flownodeDefinitionId BIGINT NOT NULL, kind VARCHAR(25) NOT NULL, rootContainerId BIGINT NOT NULL, parentContainerId BIGINT NOT NULL, name VARCHAR(255) NOT NULL, displayName VARCHAR(255), displayDescription VARCHAR(255), stateId INT NOT NULL, stateName VARCHAR(50), prev_state_id INT NOT NULL, terminal BOOLEAN NOT NULL, stable BOOLEAN , actorId BIGINT NULL, assigneeId BIGINT DEFAULT 0 NOT NULL, reachedStateDate BIGINT, lastUpdateDate BIGINT, expectedEndDate BIGINT, claimedDate BIGINT, priority TINYINT, gatewayType VARCHAR(50), hitBys VARCHAR(255), stateCategory VARCHAR(50) NOT NULL, logicalGroup1 BIGINT NOT NULL, logicalGroup2 BIGINT NOT NULL, logicalGroup3 BIGINT, logicalGroup4 BIGINT NOT NULL, loop_counter INT, loop_max INT, description VARCHAR(255), sequential BOOLEAN, loopDataInputRef VARCHAR(255), loopDataOutputRef VARCHAR(255), dataInputItemRef VARCHAR(255), dataOutputItemRef VARCHAR(255), loopCardinality INT, nbActiveInst INT, nbCompletedInst INT, nbTerminatedInst INT, executedBy BIGINT, executedBySubstitute BIGINT, activityInstanceId BIGINT, state_executing BOOLEAN DEFAULT FALSE, abortedByBoundary BIGINT, triggeredByEvent BOOLEAN, interrupting BOOLEAN, tokenCount INT NOT NULL, PRIMARY KEY (id) ); CREATE INDEX idx_fni_rootcontid ON flownode_instance (rootContainerId); CREATE INDEX idx_fni_loggroup4 ON flownode_instance (logicalGroup4); CREATE INDEX idx_fni_loggroup3_terminal ON flownode_instance(logicalgroup3, terminal); CREATE INDEX idx_fn_lg2_state ON flownode_instance (logicalGroup2, stateName); CREATE INDEX idx_fni_activity_instance_id_kind ON flownode_instance(activityInstanceId, kind); CREATE TABLE connector_instance ( id BIGINT NOT NULL, containerId BIGINT NOT NULL, containerType VARCHAR(10) NOT NULL, connectorId VARCHAR(255) NOT NULL, version VARCHAR(50) NOT NULL, name VARCHAR(255) NOT NULL, activationEvent VARCHAR(30), state VARCHAR(50), executionOrder INT, exceptionMessage VARCHAR(255), stackTrace CLOB, CONSTRAINT pk_connector_instance PRIMARY KEY (id) ); CREATE INDEX idx_ci_container_activation ON connector_instance (containerId, containerType, activationEvent); CREATE TABLE event_trigger_instance ( id BIGINT NOT NULL, eventInstanceId BIGINT NOT NULL, eventInstanceName VARCHAR(50), executionDate BIGINT, jobTriggerName VARCHAR(255), CONSTRAINT pk_event_trigger_instance PRIMARY KEY (id) ); CREATE TABLE waiting_event ( id BIGINT NOT NULL, kind VARCHAR(15) NOT NULL, eventType VARCHAR(50), messageName VARCHAR(255), signalName VARCHAR(255), errorCode VARCHAR(255), processName VARCHAR(150), flowNodeName VARCHAR(50), flowNodeDefinitionId BIGINT, subProcessId BIGINT, processDefinitionId BIGINT, rootProcessInstanceId BIGINT, parentProcessInstanceId BIGINT, flowNodeInstanceId BIGINT, relatedActivityInstanceId BIGINT, locked BOOLEAN, active BOOLEAN, progress TINYINT, correlation1 VARCHAR(128), correlation2 VARCHAR(128), correlation3 VARCHAR(128), correlation4 VARCHAR(128), correlation5 VARCHAR(128), CONSTRAINT pk_waiting_event PRIMARY KEY (id) ); CREATE INDEX idx_waiting_event ON waiting_event (progress, kind, locked, active); CREATE INDEX idx_waiting_event_correl ON waiting_event (correlation1, correlation2, correlation3, correlation4, correlation5); CREATE TABLE message_instance ( id BIGINT NOT NULL, messageName VARCHAR(255) NOT NULL, targetProcess VARCHAR(255) NOT NULL, targetFlowNode VARCHAR(255) NULL, locked BOOLEAN NOT NULL, handled BOOLEAN NOT NULL, processDefinitionId BIGINT NOT NULL, flowNodeName VARCHAR(255), correlation1 VARCHAR(128), correlation2 VARCHAR(128), correlation3 VARCHAR(128), correlation4 VARCHAR(128), correlation5 VARCHAR(128), creationDate BIGINT NOT NULL, CONSTRAINT pk_message_instance PRIMARY KEY (id) ); CREATE INDEX idx_message_instance ON message_instance (messageName, targetProcess, correlation1, correlation2, correlation3); CREATE INDEX idx_message_instance_correl ON message_instance (correlation1, correlation2, correlation3, correlation4, correlation5); CREATE TABLE pending_mapping ( id BIGINT NOT NULL, activityId BIGINT NOT NULL, actorId BIGINT, userId BIGINT, CONSTRAINT pk_pending_mapping PRIMARY KEY (id), CONSTRAINT uk_pending_mapping_activityid_userid_actorid UNIQUE (activityId, userId, actorId) ); ALTER TABLE pending_mapping ADD CONSTRAINT fk_pending_mapping_activityid FOREIGN KEY (activityId) REFERENCES flownode_instance(id); CREATE TABLE ref_biz_data_inst ( id BIGINT NOT NULL, kind VARCHAR(15) NOT NULL, name VARCHAR(255) NOT NULL, proc_inst_id BIGINT, fn_inst_id BIGINT, data_id BIGINT, data_classname VARCHAR(255) NOT NULL, CONSTRAINT pk_ref_biz_data_inst PRIMARY KEY (id) ); CREATE INDEX idx_biz_data_inst2 ON ref_biz_data_inst (fn_inst_id); CREATE INDEX idx_biz_data_inst3 ON ref_biz_data_inst (proc_inst_id); ALTER TABLE ref_biz_data_inst ADD CONSTRAINT fk_ref_biz_data_inst_proc_inst_id FOREIGN KEY (proc_inst_id) REFERENCES process_instance(id) ON DELETE CASCADE; ALTER TABLE ref_biz_data_inst ADD CONSTRAINT fk_ref_biz_data_inst_fn_inst_id FOREIGN KEY (fn_inst_id) REFERENCES flownode_instance(id) ON DELETE CASCADE; CREATE TABLE multi_biz_data ( id BIGINT NOT NULL, idx BIGINT NOT NULL, data_id BIGINT NOT NULL, CONSTRAINT pk_multi_biz_data PRIMARY KEY (id, data_id) ); ALTER TABLE multi_biz_data ADD CONSTRAINT fk_multi_biz_data_id FOREIGN KEY (id) REFERENCES ref_biz_data_inst(id) ON DELETE CASCADE; CREATE TABLE arch_ref_biz_data_inst ( id BIGINT NOT NULL, kind VARCHAR(15) NOT NULL, name VARCHAR(255) NOT NULL, orig_proc_inst_id BIGINT, orig_fn_inst_id BIGINT, data_id BIGINT, data_classname VARCHAR(255) NOT NULL, CONSTRAINT pk_arch_ref_biz_data_inst PRIMARY KEY (id) ); CREATE INDEX idx_arch_biz_data_inst1 ON arch_ref_biz_data_inst (orig_proc_inst_id); CREATE INDEX idx_arch_biz_data_inst2 ON arch_ref_biz_data_inst (orig_fn_inst_id); CREATE TABLE arch_multi_biz_data ( id BIGINT NOT NULL, idx BIGINT NOT NULL, data_id BIGINT NOT NULL, CONSTRAINT pk_arch_multi_biz_data PRIMARY KEY (id, data_id) ); ALTER TABLE arch_multi_biz_data ADD CONSTRAINT fk_arch_multi_biz_data_id FOREIGN KEY (id) REFERENCES arch_ref_biz_data_inst(id) ON DELETE CASCADE; CREATE TABLE processsupervisor ( id BIGINT NOT NULL, processDefId BIGINT NOT NULL, userId BIGINT NOT NULL, groupId BIGINT NOT NULL, roleId BIGINT NOT NULL, CONSTRAINT pk_processsupervisor PRIMARY KEY (id), CONSTRAINT uk_processsupervisor_processdefid_userid_groupid_roleid UNIQUE (processDefId, userId, groupId, roleId) ); CREATE TABLE page ( id BIGINT NOT NULL, name VARCHAR(255) NOT NULL, displayName VARCHAR(255) NOT NULL, description LONGVARCHAR, installationDate BIGINT NOT NULL, installedBy BIGINT NOT NULL, provided BOOLEAN, editable BOOLEAN, removable BOOLEAN, lastModificationDate BIGINT NOT NULL, lastUpdatedBy BIGINT NOT NULL, contentName VARCHAR(280) NOT NULL, content LONGBLOB, contentType VARCHAR(50) NOT NULL, processDefinitionId BIGINT NOT NULL, pageHash VARCHAR(32), CONSTRAINT pk_page PRIMARY KEY (id), CONSTRAINT uk_page_name_processdefinitionid UNIQUE (name, processDefinitionId) ); CREATE TABLE profile ( id BIGINT NOT NULL, isDefault BOOLEAN NOT NULL, name VARCHAR(50) NOT NULL, description LONGVARCHAR, creationDate BIGINT NOT NULL, createdBy BIGINT NOT NULL, lastUpdateDate BIGINT NOT NULL, lastUpdatedBy BIGINT NOT NULL, CONSTRAINT uk_profile_name UNIQUE (name), CONSTRAINT pk_profile PRIMARY KEY (id) ); CREATE TABLE profilemember ( id BIGINT NOT NULL, profileId BIGINT NOT NULL, userId BIGINT NOT NULL, groupId BIGINT NOT NULL, roleId BIGINT NOT NULL, CONSTRAINT pk_profilemember PRIMARY KEY (id), CONSTRAINT uk_profilemember_profileid_userid_groupid_roleid UNIQUE (profileId, userId, groupId, roleId) ); ALTER TABLE profilemember ADD CONSTRAINT fk_profilemember_profileid FOREIGN KEY (profileId) REFERENCES profile(id); CREATE TABLE business_app ( id BIGINT NOT NULL, token VARCHAR(50) NOT NULL, version VARCHAR(50) NOT NULL, description LONGVARCHAR, iconPath VARCHAR(255), creationDate BIGINT NOT NULL, createdBy BIGINT NOT NULL, lastUpdateDate BIGINT NOT NULL, updatedBy BIGINT NOT NULL, state VARCHAR(30) NOT NULL, homePageId BIGINT, profileId BIGINT, layoutId BIGINT, themeId BIGINT, iconMimeType VARCHAR(255), iconContent LONGBLOB, displayName VARCHAR(255) NOT NULL, editable BOOLEAN, internalProfile VARCHAR(255), isLink BOOLEAN DEFAULT FALSE, CONSTRAINT pk_business_app PRIMARY KEY (id), CONSTRAINT uk_business_app_token_version UNIQUE (token, version) ); ALTER TABLE business_app ADD CONSTRAINT fk_business_app_profileid FOREIGN KEY (profileId) REFERENCES profile (id); ALTER TABLE business_app ADD CONSTRAINT fk_business_app_layoutid FOREIGN KEY (layoutId) REFERENCES page (id); ALTER TABLE business_app ADD CONSTRAINT fk_business_app_themeid FOREIGN KEY (themeId) REFERENCES page (id); CREATE INDEX idx_app_token ON business_app (token); CREATE INDEX idx_app_profile ON business_app (profileId); CREATE INDEX idx_app_homepage ON business_app (homePageId); CREATE TABLE business_app_page ( id BIGINT NOT NULL, applicationId BIGINT NOT NULL, pageId BIGINT NOT NULL, token VARCHAR(255) NOT NULL, CONSTRAINT pk_business_app_page PRIMARY KEY (id), CONSTRAINT uk_business_app_page_applicationid_token UNIQUE (applicationId, token) ); ALTER TABLE business_app_page ADD CONSTRAINT fk_business_app_page_applicationid FOREIGN KEY (applicationId) REFERENCES business_app (id) ON DELETE CASCADE; ALTER TABLE business_app_page ADD CONSTRAINT fk_business_app_page_pageid FOREIGN KEY (pageId) REFERENCES page (id); CREATE INDEX idx_app_page_token ON business_app_page (applicationId, token); CREATE INDEX idx_app_page_pageId ON business_app_page (pageId); CREATE TABLE business_app_menu ( id BIGINT NOT NULL, displayName VARCHAR(255) NOT NULL, applicationId BIGINT NOT NULL, applicationPageId BIGINT, parentId BIGINT, index_ BIGINT, CONSTRAINT pk_business_app_menu PRIMARY KEY (id) ); -- cannot have both fk_business_app_menu_applicationid and fk_business_app_menu_applicationpageid because this create to path for deletion of business_app_menu elements: -- business_app -> business_app_menu -- business_app -> business_app_page -> business_app_menu -- this is not allowed in SQL Server ALTER TABLE business_app_menu ADD CONSTRAINT fk_business_app_menu_applicationid FOREIGN KEY (applicationId) REFERENCES business_app (id); ALTER TABLE business_app_menu ADD CONSTRAINT fk_business_app_menu_applicationpageid FOREIGN KEY (applicationPageId) REFERENCES business_app_page (id); ALTER TABLE business_app_menu ADD CONSTRAINT fk_business_app_menu_parentid FOREIGN KEY (parentId) REFERENCES business_app_menu (id); CREATE INDEX idx_app_menu_app ON business_app_menu (applicationId); CREATE INDEX idx_app_menu_page ON business_app_menu (applicationPageId); CREATE INDEX idx_app_menu_parent ON business_app_menu (parentId); CREATE TABLE command ( id BIGINT NOT NULL, name VARCHAR(50) NOT NULL, description LONGVARCHAR, IMPLEMENTATION VARCHAR(100) NOT NULL, isSystem BOOLEAN, CONSTRAINT pk_command PRIMARY KEY (id), CONSTRAINT uk_command_name UNIQUE (name) ); CREATE TABLE arch_data_instance ( id BIGINT NOT NULL, name VARCHAR(50), description VARCHAR(50), transientData BOOLEAN, className VARCHAR(100), containerId BIGINT, containerType VARCHAR(60), namespace VARCHAR(100), element VARCHAR(60), intValue INT, longValue BIGINT, shortTextValue VARCHAR(255), booleanValue BOOLEAN, doubleValue NUMERIC(19,5), floatValue REAL, blobValue MEDIUMBLOB, clobValue CLOB, discriminant VARCHAR(50) NOT NULL, archiveDate BIGINT NOT NULL, sourceObjectId BIGINT NOT NULL, CONSTRAINT pk_arch_data_instance PRIMARY KEY (id) ); CREATE INDEX idx1_arch_data_instance ON arch_data_instance (containerId, containerType, archiveDate, name, sourceObjectId); CREATE INDEX idx2_arch_data_instance ON arch_data_instance (sourceObjectId, containerId, archiveDate, id); CREATE TABLE data_instance ( id BIGINT NOT NULL, name VARCHAR(50), description VARCHAR(50), transientData BOOLEAN, className VARCHAR(100), containerId BIGINT, containerType VARCHAR(60), namespace VARCHAR(100), element VARCHAR(60), intValue INT, longValue BIGINT, shortTextValue VARCHAR(255), booleanValue BOOLEAN, doubleValue NUMERIC(19,5), floatValue REAL, blobValue MEDIUMBLOB, clobValue CLOB, discriminant VARCHAR(50) NOT NULL, CONSTRAINT pk_data_instance PRIMARY KEY (id) ); CREATE INDEX idx_datai_container ON data_instance (containerId, containerType, name); CREATE TABLE dependency ( id BIGINT NOT NULL, name VARCHAR(150) NOT NULL, description LONGVARCHAR, filename VARCHAR(255) NOT NULL, value_ LONGVARBINARY NOT NULL, CONSTRAINT pk_dependency PRIMARY KEY (id), CONSTRAINT uk_dependency_name UNIQUE (name) ); CREATE TABLE dependencymapping ( id BIGINT NOT NULL, artifactid BIGINT NOT NULL, artifacttype VARCHAR(50) NOT NULL, dependencyid BIGINT NOT NULL, CONSTRAINT pk_dependencymapping PRIMARY KEY (id), CONSTRAINT uk_dependencymapping_dependencyid_artifactid_artifacttype UNIQUE (dependencyid, artifactid, artifacttype) ); CREATE INDEX idx_dependencymapping_depid ON dependencymapping (dependencyid); ALTER TABLE dependencymapping ADD CONSTRAINT fk_dependencymapping_dependencyid FOREIGN KEY (dependencyid) REFERENCES dependency(id) ON DELETE CASCADE; CREATE TABLE pdependency ( id BIGINT NOT NULL, name VARCHAR(50) NOT NULL, description LONGVARCHAR, filename VARCHAR(255) NOT NULL, value_ LONGVARBINARY NOT NULL, CONSTRAINT pk_pdependency PRIMARY KEY (id), CONSTRAINT uk_pdependency_name UNIQUE (name) ); CREATE TABLE pdependencymapping ( id BIGINT NOT NULL, artifactid BIGINT NOT NULL, artifacttype VARCHAR(50) NOT NULL, dependencyid BIGINT NOT NULL, CONSTRAINT pk_pdependencymapping PRIMARY KEY (id), CONSTRAINT uk_pdependencymapping_dependencyid_artifactid_artifacttype UNIQUE (dependencyid, artifactid, artifacttype) ); CREATE INDEX idx_pdependencymapping_depid ON pdependencymapping (dependencyid); ALTER TABLE pdependencymapping ADD CONSTRAINT fk_pdependencymapping_dependencyid FOREIGN KEY (dependencyid) REFERENCES pdependency(id) ON DELETE CASCADE; CREATE TABLE group_ ( id BIGINT NOT NULL, name VARCHAR(125) NOT NULL, parentPath VARCHAR(255), displayName VARCHAR(255), description LONGVARCHAR, createdBy BIGINT, creationDate BIGINT, lastUpdate BIGINT, iconid BIGINT, CONSTRAINT pk_group PRIMARY KEY (id) ); CREATE INDEX idx_group_name ON group_ (parentPath, name); CREATE TABLE role ( id BIGINT NOT NULL, name VARCHAR(255) NOT NULL, displayName VARCHAR(255), description LONGVARCHAR, createdBy BIGINT, creationDate BIGINT, lastUpdate BIGINT, iconid BIGINT, CONSTRAINT pk_role PRIMARY KEY (id), CONSTRAINT uk_role_name UNIQUE (name) ); CREATE TABLE user_ ( id BIGINT NOT NULL, enabled BOOLEAN NOT NULL, userName VARCHAR(255) NOT NULL, password VARCHAR(60), firstName VARCHAR(255), lastName VARCHAR(255), title VARCHAR(50), jobTitle VARCHAR(255), managerUserId BIGINT, createdBy BIGINT, creationDate BIGINT, lastUpdate BIGINT, iconid BIGINT, CONSTRAINT pk_user PRIMARY KEY (id), CONSTRAINT uk_user_username UNIQUE (userName) ); CREATE TABLE user_login ( id BIGINT NOT NULL, lastConnection BIGINT, CONSTRAINT pk_user_login PRIMARY KEY (id) ); CREATE TABLE user_contactinfo ( id BIGINT NOT NULL, userId BIGINT NOT NULL, email VARCHAR(255), phone VARCHAR(50), mobile VARCHAR(50), fax VARCHAR(50), building VARCHAR(50), room VARCHAR(50), address VARCHAR(255), zipCode VARCHAR(50), city VARCHAR(255), state VARCHAR(255), country VARCHAR(255), website VARCHAR(255), personal BOOLEAN NOT NULL, CONSTRAINT pk_user_contactinfo PRIMARY KEY (id), CONSTRAINT uk_user_contactinfo_userid_personal UNIQUE (userId, personal) ); ALTER TABLE user_contactinfo ADD CONSTRAINT fk_user_contactinfo_userid FOREIGN KEY (userId) REFERENCES user_ (id) ON DELETE CASCADE; CREATE TABLE custom_usr_inf_def ( id BIGINT NOT NULL, name VARCHAR(75) NOT NULL, description LONGVARCHAR, CONSTRAINT pk_custom_usr_inf_def PRIMARY KEY (id), CONSTRAINT uk_custom_usr_inf_def_name UNIQUE (name) ); CREATE TABLE custom_usr_inf_val ( id BIGINT NOT NULL, definitionId BIGINT NOT NULL, userId BIGINT NOT NULL, value VARCHAR(255), CONSTRAINT pk_custom_usr_inf_val PRIMARY KEY (id), CONSTRAINT uk_custom_usr_inf_val_definitionid_userid UNIQUE (definitionId, userId) ); ALTER TABLE custom_usr_inf_val ADD CONSTRAINT fk_custom_usr_inf_val_userid FOREIGN KEY (userId) REFERENCES user_ (id) ON DELETE CASCADE; ALTER TABLE custom_usr_inf_val ADD CONSTRAINT fk_custom_usr_inf_val_definitionid FOREIGN KEY (definitionId) REFERENCES custom_usr_inf_def (id) ON DELETE CASCADE; CREATE TABLE user_membership ( id BIGINT NOT NULL, userId BIGINT NOT NULL, roleId BIGINT NOT NULL, groupId BIGINT NOT NULL, assignedBy BIGINT, assignedDate BIGINT, CONSTRAINT pk_user_membership PRIMARY KEY (id), CONSTRAINT uk_user_membership_userid_roleid_groupid UNIQUE (userId, roleId, groupId) ); CREATE TABLE icon ( id BIGINT NOT NULL, mimetype VARCHAR(255) NOT NULL, content LONGBLOB NOT NULL, CONSTRAINT pk_icon PRIMARY KEY (id) ); CREATE TABLE queriable_log ( id BIGINT NOT NULL, log_timestamp BIGINT NOT NULL, whatYear SMALLINT NOT NULL, whatMonth TINYINT NOT NULL, dayOfYear SMALLINT NOT NULL, weekOfYear TINYINT NOT NULL, userId VARCHAR(255) NOT NULL, threadNumber BIGINT NOT NULL, clusterNode VARCHAR(50), productVersion VARCHAR(50) NOT NULL, severity VARCHAR(50) NOT NULL, actionType VARCHAR(50) NOT NULL, actionScope VARCHAR(100), actionStatus TINYINT NOT NULL, rawMessage VARCHAR(255) NOT NULL, callerClassName VARCHAR(200), callerMethodName VARCHAR(80), numericIndex1 BIGINT, numericIndex2 BIGINT, numericIndex3 BIGINT, numericIndex4 BIGINT, numericIndex5 BIGINT, CONSTRAINT pk_queriable_log PRIMARY KEY (id) ); CREATE TABLE sequence ( id BIGINT NOT NULL, nextid BIGINT NOT NULL, CONSTRAINT pk_sequence PRIMARY KEY (id) ); CREATE TABLE platform ( id BIGINT NOT NULL, version VARCHAR(50) NOT NULL, initial_bonita_version VARCHAR(50) NOT NULL, application_version VARCHAR(50) NOT NULL, maintenance_message LONGVARCHAR, maintenance_message_active BOOLEAN NOT NULL, created BIGINT NOT NULL, created_by VARCHAR(50) NOT NULL, information CLOB, maintenance_enabled BOOLEAN NOT NULL, CONSTRAINT pk_platform PRIMARY KEY (id) ); CREATE TABLE platformCommand ( id BIGINT PRIMARY KEY, name VARCHAR(50) NOT NULL UNIQUE, description LONGVARCHAR, IMPLEMENTATION VARCHAR(100) NOT NULL ); CREATE TABLE job_desc ( id BIGINT NOT NULL, jobclassname VARCHAR(100) NOT NULL, jobname VARCHAR(100) NOT NULL, description VARCHAR(50), CONSTRAINT pk_job_desc PRIMARY KEY (id) ); CREATE TABLE job_param ( id BIGINT NOT NULL, jobDescriptorId BIGINT NOT NULL, key_ VARCHAR(50) NOT NULL, value_ MEDIUMBLOB NOT NULL, CONSTRAINT pk_job_param PRIMARY KEY (id) ); ALTER TABLE job_param ADD CONSTRAINT fk_job_param_jobdescriptorid FOREIGN KEY (jobDescriptorId) REFERENCES job_desc(id) ON DELETE CASCADE; CREATE INDEX idx_job_param_jobid ON job_param(jobDescriptorId); CREATE TABLE job_log ( id BIGINT NOT NULL, jobDescriptorId BIGINT NOT NULL, retryNumber BIGINT, lastUpdateDate BIGINT, lastMessage LONGVARCHAR, CONSTRAINT pk_job_log PRIMARY KEY (id), CONSTRAINT uk_job_log_jobdescriptorid UNIQUE (jobDescriptorId) ); ALTER TABLE job_log ADD CONSTRAINT fk_job_log_jobdescriptorid FOREIGN KEY (jobDescriptorId) REFERENCES job_desc(id) ON DELETE CASCADE; CREATE TABLE page_mapping ( id BIGINT NOT NULL, key_ VARCHAR(255) NOT NULL, pageId BIGINT NULL, url VARCHAR(1024) NULL, urladapter VARCHAR(255) NULL, page_authoriz_rules TEXT NULL, lastUpdateDate BIGINT NULL, lastUpdatedBy BIGINT NULL, CONSTRAINT pk_page_mapping PRIMARY KEY (id), CONSTRAINT uk_page_mapping_key UNIQUE (key_) ); CREATE TABLE form_mapping ( id BIGINT NOT NULL, process BIGINT NOT NULL, type INT NOT NULL, task VARCHAR(255), page_mapping_id BIGINT, lastUpdateDate BIGINT, lastUpdatedBy BIGINT, target VARCHAR(16) NOT NULL, CONSTRAINT pk_form_mapping PRIMARY KEY (id) ); ALTER TABLE form_mapping ADD CONSTRAINT fk_form_mapping_key FOREIGN KEY (page_mapping_id) REFERENCES page_mapping(id); CREATE TABLE proc_parameter ( id BIGINT NOT NULL, process_id BIGINT NOT NULL, name VARCHAR(255) NOT NULL, value CLOB NULL, CONSTRAINT pk_proc_parameter PRIMARY KEY (id) ); CREATE TABLE bar_resource ( id BIGINT NOT NULL, process_id BIGINT NOT NULL, name VARCHAR(255) NOT NULL, type VARCHAR(16) NOT NULL, content LONGBLOB NOT NULL, CONSTRAINT pk_bar_resource PRIMARY KEY (id), CONSTRAINT uk_bar_resource_processid_name_type UNIQUE (process_id, name, type) ); CREATE TABLE temporary_content ( id BIGINT NOT NULL, creationDate BIGINT NOT NULL, key_ VARCHAR(255) NOT NULL, fileName VARCHAR(255) NOT NULL, mimeType VARCHAR(255) NOT NULL, content LONGBLOB NOT NULL, UNIQUE (key_), PRIMARY KEY (id) ); CREATE INDEX idx_temporary_content ON temporary_content (key_); CREATE TABLE tenant_resource ( id BIGINT NOT NULL, name VARCHAR(255) NOT NULL, type VARCHAR(16) NOT NULL, content LONGBLOB NOT NULL, lastUpdatedBy BIGINT NOT NULL, lastUpdateDate BIGINT, state VARCHAR(50) NOT NULL, CONSTRAINT pk_tenant_resource PRIMARY KEY (id), CONSTRAINT uk_tenant_resource_name_type UNIQUE (name, type) ); CREATE TABLE bpm_failure ( id BIGINT NOT NULL, processDefinitionId BIGINT NOT NULL, processInstanceId BIGINT NOT NULL, rootProcessInstanceId BIGINT, flowNodeInstanceId BIGINT, scope VARCHAR(255), context VARCHAR(1024), errorMessage VARCHAR(1024), stackTrace TEXT, failureDate BIGINT NOT NULL, CONSTRAINT pk_bpm_failure PRIMARY KEY (id) ); CREATE INDEX idx_bpm_failure_flownodeinstanceid ON bpm_failure (flowNodeInstanceId); CREATE INDEX idx_bpm_failure_processinstanceid ON bpm_failure (processInstanceId); CREATE INDEX idx_bpm_failure_rootprocessinstanceid ON bpm_failure (rootProcessInstanceId); CREATE INDEX idx_bpm_failure_processdefinitionid ON bpm_failure (processDefinitionId); CREATE TABLE arch_bpm_failure ( id BIGINT NOT NULL, processDefinitionId BIGINT NOT NULL, processInstanceId BIGINT NOT NULL, rootProcessInstanceId BIGINT, flowNodeInstanceId BIGINT, scope VARCHAR(255), context VARCHAR(1024), errorMessage VARCHAR(1024), stackTrace TEXT, failureDate BIGINT NOT NULL, archiveDate BIGINT NOT NULL, sourceObjectId BIGINT NOT NULL, CONSTRAINT pk_arch_bpm_failure PRIMARY KEY (id) ); CREATE INDEX idx_arch_bpm_failure_flownodeinstanceid ON arch_bpm_failure (flowNodeInstanceId); CREATE INDEX idx_arch_bpm_failure_processinstanceid ON arch_bpm_failure (processInstanceId); CREATE INDEX idx_arch_bpm_failure_rootprocessinstanceid ON arch_bpm_failure (rootProcessInstanceId); CREATE INDEX idx_arch_bpm_failure_processdefinitionid ON arch_bpm_failure (processDefinitionId); CREATE TABLE data_retention_config ( id BIGINT NOT NULL, data_classname VARCHAR(255) NOT NULL, reference_date VARCHAR(20) NOT NULL, retention_days INT NOT NULL, created_at BIGINT NOT NULL, updated_at BIGINT NOT NULL, CONSTRAINT pk_data_retention_config PRIMARY KEY (id), CONSTRAINT uk_data_retention_config_data_classname UNIQUE (data_classname) ); CREATE TABLE data_retention_bdm_tracking ( id BIGINT NOT NULL, data_id BIGINT NOT NULL, data_classname VARCHAR(255) NOT NULL, created_at BIGINT NOT NULL, last_modified_at BIGINT NOT NULL, CONSTRAINT pk_data_retention_bdm_tracking PRIMARY KEY (id), CONSTRAINT uk_data_retention_bdm_tracking_data_id_data_classname UNIQUE (data_id, data_classname) ); CREATE INDEX idx_data_retention_bdm_tracking_data_classname ON data_retention_bdm_tracking (data_classname); CREATE TABLE delegation_rule ( id BIGINT NOT NULL, delegator_id BIGINT NOT NULL, delegate_id BIGINT NOT NULL, start_date BIGINT NOT NULL, end_date BIGINT NOT NULL, last_updated_by BIGINT NOT NULL, last_updated_at BIGINT NOT NULL, CONSTRAINT pk_delegation_rule PRIMARY KEY (id), CONSTRAINT uk_delegation_rule_delegator_id UNIQUE (delegator_id) ); CREATE TABLE delegation_rule_process ( id BIGINT NOT NULL, delegation_rule_id BIGINT NOT NULL, process_name VARCHAR(255) NOT NULL, CONSTRAINT pk_delegation_rule_process PRIMARY KEY (id), CONSTRAINT uk_delegation_rule_process_delegation_rule_id_process_name UNIQUE (delegation_rule_id, process_name) ); ALTER TABLE delegation_rule_process ADD CONSTRAINT fk_delegation_rule_process_delegation_rule_id FOREIGN KEY (delegation_rule_id) REFERENCES delegation_rule(id) ON DELETE CASCADE; ================================================ FILE: platform/platform-resources/src/main/resources/sql/h2/dropQuartzTables.sql ================================================ DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; DROP TABLE IF EXISTS QRTZ_LOCKS; DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; DROP TABLE IF EXISTS QRTZ_TRIGGERS; DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; DROP TABLE IF EXISTS QRTZ_CALENDARS; ================================================ FILE: platform/platform-resources/src/main/resources/sql/h2/dropTables.sql ================================================ DROP TABLE configuration; DROP TABLE arch_contract_data; DROP TABLE contract_data; DROP TABLE actormember; DROP TABLE actor; DROP TABLE processcategorymapping; DROP TABLE category; DROP TABLE arch_process_comment; DROP TABLE process_comment; DROP TABLE process_definition; DROP TABLE arch_document_mapping; DROP TABLE document_mapping; DROP TABLE document; DROP TABLE arch_flownode_instance; DROP TABLE arch_process_instance; DROP TABLE arch_connector_instance; DROP TABLE arch_multi_biz_data; DROP TABLE arch_ref_biz_data_inst; DROP TABLE multi_biz_data; DROP TABLE ref_biz_data_inst; DROP TABLE pending_mapping; DROP TABLE connector_instance; DROP TABLE flownode_instance; DROP TABLE process_instance; DROP TABLE event_trigger_instance; DROP TABLE waiting_event; DROP TABLE message_instance; DROP TABLE processsupervisor; DROP TABLE business_app_menu; DROP TABLE business_app_page; DROP TABLE business_app; DROP TABLE command; DROP TABLE arch_data_instance; DROP TABLE data_instance; DROP TABLE dependencymapping; DROP TABLE dependency; DROP TABLE pdependencymapping; DROP TABLE pdependency; DROP TABLE user_membership; DROP TABLE custom_usr_inf_val; DROP TABLE custom_usr_inf_def; DROP TABLE user_contactinfo; DROP TABLE user_login; DROP TABLE user_; DROP TABLE role; DROP TABLE group_; DROP TABLE queriable_log; DROP TABLE page; DROP TABLE profilemember; DROP TABLE profile; DROP TABLE job_log; DROP TABLE job_param; DROP TABLE job_desc; DROP TABLE sequence; DROP TABLE tenant; DROP TABLE platform; DROP TABLE platformCommand; DROP TABLE form_mapping; DROP TABLE page_mapping; DROP TABLE process_content; DROP TABLE proc_parameter; DROP TABLE bar_resource; DROP TABLE temporary_content; DROP TABLE tenant_resource; DROP TABLE icon; DROP TABLE arch_bpm_failure; DROP TABLE bpm_failure; DROP TABLE data_retention_config; DROP TABLE data_retention_bdm_tracking; DROP TABLE delegation_rule_process; DROP TABLE delegation_rule; ================================================ FILE: platform/platform-resources/src/main/resources/sql/h2/initTables.sql ================================================ INSERT INTO sequence VALUES (1, 1); INSERT INTO sequence VALUES (2, 1); INSERT INTO sequence VALUES (3, 1); INSERT INTO sequence VALUES (4, 1); INSERT INTO sequence VALUES (5, 1); INSERT INTO sequence VALUES (6, 1); INSERT INTO sequence VALUES (7, 1); INSERT INTO sequence VALUES (8, 1); INSERT INTO sequence VALUES (9, 1); INSERT INTO sequence VALUES(10, 1); INSERT INTO sequence VALUES(11, 1); INSERT INTO sequence VALUES(20, 1); INSERT INTO sequence VALUES(21, 1); INSERT INTO sequence VALUES(22, 1); INSERT INTO sequence VALUES(23, 1); INSERT INTO sequence VALUES(24, 1); INSERT INTO sequence VALUES(25, 1); INSERT INTO sequence VALUES(26, 1); INSERT INTO sequence VALUES(27, 1); INSERT INTO sequence VALUES(30, 1); INSERT INTO sequence VALUES(70, 1); INSERT INTO sequence VALUES(71, 1); INSERT INTO sequence VALUES(72, 1); INSERT INTO sequence VALUES(90, 1); INSERT INTO sequence VALUES(9990, 1); INSERT INTO sequence VALUES(9992, 1); INSERT INTO sequence VALUES(10000, 1); INSERT INTO sequence VALUES(10001, 1); INSERT INTO sequence VALUES(10010, 1); INSERT INTO sequence VALUES(10011, 1); INSERT INTO sequence VALUES(10012, 1); INSERT INTO sequence VALUES(10014, 1); INSERT INTO sequence VALUES(10015, 1); INSERT INTO sequence VALUES(10016, 1); INSERT INTO sequence VALUES(10017, 1); INSERT INTO sequence VALUES(10018, 1); INSERT INTO sequence VALUES(10020, 1); INSERT INTO sequence VALUES(10021, 1); INSERT INTO sequence VALUES(10030, 1); INSERT INTO sequence VALUES(10031, 1); INSERT INTO sequence VALUES(10040, 1); INSERT INTO sequence VALUES(20051, 1); INSERT INTO sequence VALUES(10050, 1); INSERT INTO sequence VALUES(10060, 1); INSERT INTO sequence VALUES(10080, 1); INSERT INTO sequence VALUES(10090, 1); INSERT INTO sequence VALUES(10096, 1); INSERT INTO sequence VALUES(10120, 1); INSERT INTO sequence VALUES(10121, 1); INSERT INTO sequence VALUES(10200, 1); INSERT INTO sequence VALUES(10201, 1); INSERT INTO sequence VALUES(10202, 1); INSERT INTO sequence VALUES(10210, 1); INSERT INTO sequence VALUES(10220, 1); INSERT INTO sequence VALUES(10300, 1); INSERT INTO sequence VALUES(10310, 1); INSERT INTO sequence VALUES(10400, 1); INSERT INTO sequence VALUES(10500, 1); INSERT INTO sequence VALUES(10501, 1); INSERT INTO sequence VALUES(20010, 1); INSERT INTO sequence VALUES(20011, 1); INSERT INTO sequence VALUES(20013, 1); INSERT INTO sequence VALUES(20040, 1); INSERT INTO sequence VALUES(20050, 1); INSERT INTO sequence VALUES(20210, 1); INSERT INTO sequence VALUES(20220, 1); INSERT INTO sequence VALUES(20096, 1); INSERT INTO sequence VALUES(20097, 1); INSERT INTO sequence VALUES(20098, 1); ================================================ FILE: platform/platform-resources/src/main/resources/sql/h2/preDropStructure.sql ================================================ -- ------------------------------------------------ Foreign Keys ----------------------------------------------- ALTER TABLE actormember DROP CONSTRAINT fk_actormember_actorid; ALTER TABLE profilemember DROP CONSTRAINT fk_profilemember_profileid; ALTER TABLE document_mapping DROP CONSTRAINT fk_document_mapping_documentid; ALTER TABLE pending_mapping DROP CONSTRAINT fk_pending_mapping_activityid; ALTER TABLE process_definition DROP CONSTRAINT fk_process_definition_content_id; -- business application ALTER TABLE business_app_menu DROP CONSTRAINT fk_business_app_menu_applicationid; ALTER TABLE business_app_menu DROP CONSTRAINT fk_business_app_menu_applicationpageid; ALTER TABLE business_app_menu DROP CONSTRAINT fk_business_app_menu_parentid; ALTER TABLE business_app_page DROP CONSTRAINT fk_business_app_page_applicationid; ALTER TABLE business_app_page DROP CONSTRAINT fk_business_app_page_pageid; ALTER TABLE business_app DROP CONSTRAINT fk_business_app_profileid; ALTER TABLE business_app DROP CONSTRAINT fk_business_app_layoutid; ALTER TABLE business_app DROP CONSTRAINT fk_business_app_themeid; -- delegation ALTER TABLE delegation_rule_process DROP CONSTRAINT fk_delegation_rule_process_delegation_rule_id; -- ------------------------ Foreign Keys to disable if archiving is on another BD ------------------ ALTER TABLE arch_document_mapping DROP CONSTRAINT fk_arch_document_mapping_documentid; ================================================ FILE: platform/platform-resources/src/main/resources/sql/postgres/cleanTables.sql ================================================ DELETE FROM arch_contract_data; DELETE FROM contract_data; DELETE FROM actormember; DELETE FROM actor; DELETE FROM processcategorymapping; DELETE FROM category; DELETE FROM arch_process_comment; DELETE FROM process_comment; DELETE FROM process_definition; DELETE FROM arch_document_mapping; DELETE FROM document; DELETE FROM document_mapping; DELETE FROM arch_flownode_instance; DELETE FROM arch_process_instance; DELETE FROM arch_connector_instance; DELETE FROM arch_multi_biz_data; DELETE FROM arch_ref_biz_data_inst; DELETE FROM multi_biz_data; DELETE FROM ref_biz_data_inst; DELETE FROM pending_mapping; DELETE FROM message_instance; DELETE FROM waiting_event; DELETE FROM event_trigger_instance; DELETE FROM connector_instance; DELETE FROM flownode_instance; DELETE FROM process_instance; DELETE FROM processsupervisor; DELETE FROM business_app_menu; DELETE FROM business_app_page; DELETE FROM business_app; DELETE FROM command; DELETE FROM arch_data_instance; DELETE FROM data_instance; DELETE FROM dependencymapping; DELETE FROM dependency; DELETE FROM user_membership; DELETE FROM custom_usr_inf_val; DELETE FROM custom_usr_inf_def; DELETE FROM user_contactinfo; DELETE FROM user_login; DELETE FROM user_; DELETE FROM role; DELETE FROM group_; DELETE FROM queriable_log; DELETE FROM page; DELETE FROM sequence; DELETE FROM profilemember; DELETE FROM profile; DELETE FROM job_log; DELETE FROM job_param; DELETE FROM job_desc; DELETE FROM tenant; DELETE FROM platformCommand; DELETE FROM form_mapping; DELETE FROM page_mapping; DELETE FROM proc_parameter; DELETE FROM arch_bpm_failure; DELETE FROM bpm_failure; DELETE FROM data_retention_config; DELETE FROM data_retention_bdm_tracking; DELETE FROM delegation_rule_process; DELETE FROM delegation_rule; -- do NOT clear directly PLATFORM table, Hibernate needs to update its cache to know the platform has been deleted ================================================ FILE: platform/platform-resources/src/main/resources/sql/postgres/createQuartzTables.sql ================================================ CREATE TABLE QRTZ_CALENDARS ( SCHED_NAME VARCHAR(120) NOT NULL, CALENDAR_NAME VARCHAR (200) NOT NULL , CALENDAR BYTEA NOT NULL ); CREATE TABLE QRTZ_CRON_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , CRON_EXPRESSION VARCHAR (120) NOT NULL , TIME_ZONE_ID VARCHAR (80) ); CREATE TABLE QRTZ_FIRED_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, ENTRY_ID VARCHAR (95) NOT NULL , TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , INSTANCE_NAME VARCHAR (200) NOT NULL , FIRED_TIME INT8 NOT NULL , SCHED_TIME INT8 NOT NULL , PRIORITY INTEGER NOT NULL , STATE VARCHAR (16) NOT NULL, JOB_NAME VARCHAR (200) NULL , JOB_GROUP VARCHAR (200) NULL , IS_NONCONCURRENT BOOLEAN NULL , REQUESTS_RECOVERY BOOLEAN NULL ); CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_GROUP VARCHAR (200) NOT NULL ); CREATE TABLE QRTZ_SCHEDULER_STATE ( SCHED_NAME VARCHAR(120) NOT NULL, INSTANCE_NAME VARCHAR (200) NOT NULL , LAST_CHECKIN_TIME INT8 NOT NULL , CHECKIN_INTERVAL INT8 NOT NULL ); CREATE TABLE QRTZ_LOCKS ( SCHED_NAME VARCHAR(120) NOT NULL, LOCK_NAME VARCHAR (40) NOT NULL ); CREATE TABLE QRTZ_JOB_DETAILS ( SCHED_NAME VARCHAR(120) NOT NULL, JOB_NAME VARCHAR (200) NOT NULL , JOB_GROUP VARCHAR (200) NOT NULL , DESCRIPTION VARCHAR (250) NULL , JOB_CLASS_NAME VARCHAR (250) NOT NULL , IS_DURABLE BOOLEAN NOT NULL , IS_NONCONCURRENT BOOLEAN NOT NULL , IS_UPDATE_DATA BOOLEAN NOT NULL , REQUESTS_RECOVERY BOOLEAN NOT NULL , JOB_DATA BYTEA NULL ); CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , REPEAT_COUNT INT8 NOT NULL , REPEAT_INTERVAL INT8 NOT NULL , TIMES_TRIGGERED INT8 NOT NULL ); CREATE TABLE qrtz_simprop_triggers ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, STR_PROP_1 VARCHAR(512) NULL, STR_PROP_2 VARCHAR(512) NULL, STR_PROP_3 VARCHAR(512) NULL, INT_PROP_1 INTEGER NULL, INT_PROP_2 INTEGER NULL, LONG_PROP_1 INT8 NULL, LONG_PROP_2 INT8 NULL, DEC_PROP_1 NUMERIC(13,4) NULL, DEC_PROP_2 NUMERIC(13,4) NULL, BOOL_PROP_1 BOOLEAN NULL, BOOL_PROP_2 BOOLEAN NULL ); CREATE TABLE QRTZ_BLOB_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , BLOB_DATA BYTEA NULL ); CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR (200) NOT NULL , TRIGGER_GROUP VARCHAR (200) NOT NULL , JOB_NAME VARCHAR (200) NOT NULL , JOB_GROUP VARCHAR (200) NOT NULL , DESCRIPTION VARCHAR (250) NULL , NEXT_FIRE_TIME INT8 NULL , PREV_FIRE_TIME INT8 NULL , PRIORITY INTEGER NULL , TRIGGER_STATE VARCHAR (16) NOT NULL , TRIGGER_TYPE VARCHAR (8) NOT NULL , START_TIME INT8 NOT NULL , END_TIME INT8 NULL , CALENDAR_NAME VARCHAR (200) NULL , MISFIRE_INSTR SMALLINT NULL , JOB_DATA BYTEA NULL ); ALTER TABLE QRTZ_CALENDARS ADD CONSTRAINT PK_QRTZ_CALENDARS PRIMARY KEY ( SCHED_NAME, CALENDAR_NAME ); ALTER TABLE QRTZ_CRON_TRIGGERS ADD CONSTRAINT PK_QRTZ_CRON_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ); ALTER TABLE QRTZ_FIRED_TRIGGERS ADD CONSTRAINT PK_QRTZ_FIRED_TRIGGERS PRIMARY KEY ( SCHED_NAME, ENTRY_ID ); ALTER TABLE QRTZ_PAUSED_TRIGGER_GRPS ADD CONSTRAINT PK_QRTZ_PAUSED_TRIGGER_GRPS PRIMARY KEY ( SCHED_NAME, TRIGGER_GROUP ); ALTER TABLE QRTZ_SCHEDULER_STATE ADD CONSTRAINT PK_QRTZ_SCHEDULER_STATE PRIMARY KEY ( SCHED_NAME, INSTANCE_NAME ); ALTER TABLE QRTZ_LOCKS ADD CONSTRAINT PK_QRTZ_LOCKS PRIMARY KEY ( SCHED_NAME, LOCK_NAME ); ALTER TABLE QRTZ_JOB_DETAILS ADD CONSTRAINT PK_QRTZ_JOB_DETAILS PRIMARY KEY ( SCHED_NAME, JOB_NAME, JOB_GROUP ); ALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD CONSTRAINT PK_QRTZ_SIMPLE_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ); ALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD CONSTRAINT PK_QRTZ_SIMPROP_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ); ALTER TABLE QRTZ_TRIGGERS ADD CONSTRAINT PK_QRTZ_TRIGGERS PRIMARY KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ); ALTER TABLE QRTZ_CRON_TRIGGERS ADD CONSTRAINT FK_QRTZ_CRON_TRIGGERS FOREIGN KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) REFERENCES QRTZ_TRIGGERS ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) ON DELETE CASCADE; ALTER TABLE QRTZ_SIMPLE_TRIGGERS ADD CONSTRAINT FK_QRTZ_SIMPLE_TRIGGERS FOREIGN KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) REFERENCES QRTZ_TRIGGERS ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) ON DELETE CASCADE; ALTER TABLE QRTZ_SIMPROP_TRIGGERS ADD CONSTRAINT FK_QRTZ_SIMPROP_TRIGGERS FOREIGN KEY ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) REFERENCES QRTZ_TRIGGERS ( SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP ) ON DELETE CASCADE; ALTER TABLE QRTZ_TRIGGERS ADD CONSTRAINT FK_QRTZ_TRIGGERS FOREIGN KEY ( SCHED_NAME, JOB_NAME, JOB_GROUP ) REFERENCES QRTZ_JOB_DETAILS ( SCHED_NAME, JOB_NAME, JOB_GROUP ); CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY); CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME); CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME); CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME); CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY); CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP); CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); ================================================ FILE: platform/platform-resources/src/main/resources/sql/postgres/createTables.sql ================================================ CREATE TABLE configuration ( content_type VARCHAR(50) NOT NULL, resource_name VARCHAR(120) NOT NULL, resource_content BYTEA NOT NULL, CONSTRAINT pk_configuration PRIMARY KEY (content_type, resource_name) ); CREATE INDEX idx_configuration ON configuration (content_type); CREATE TABLE contract_data ( id INT8 NOT NULL, kind VARCHAR(20) NOT NULL, scopeId INT8 NOT NULL, name VARCHAR(50) NOT NULL, val TEXT, CONSTRAINT pk_contract_data PRIMARY KEY (id, scopeId), CONSTRAINT uk_contract_data_kind_scopeid_name UNIQUE (kind, scopeId, name) ); CREATE TABLE arch_contract_data ( id INT8 NOT NULL, kind VARCHAR(20) NOT NULL, scopeId INT8 NOT NULL, name VARCHAR(50) NOT NULL, val TEXT, archiveDate INT8 NOT NULL, sourceObjectId INT8 NOT NULL, CONSTRAINT pk_arch_contract_data PRIMARY KEY (id, scopeId), CONSTRAINT uk_arch_contract_data_kind_scopeid_name UNIQUE (kind, scopeId, name) ); CREATE TABLE actor ( id INT8 NOT NULL, scopeId INT8 NOT NULL, name VARCHAR(50) NOT NULL, displayName VARCHAR(75), description TEXT, initiator BOOLEAN, CONSTRAINT uk_actor_id_scopeid_name UNIQUE (id, scopeId, name), CONSTRAINT pk_actor PRIMARY KEY (id) ); CREATE TABLE actormember ( id INT8 NOT NULL, actorId INT8 NOT NULL, userId INT8 NOT NULL, groupId INT8 NOT NULL, roleId INT8 NOT NULL, CONSTRAINT uk_actormember_actorid_userid_groupid_roleid UNIQUE (actorId, userId, groupId, roleId), CONSTRAINT pk_actormember PRIMARY KEY (id) ); ALTER TABLE actormember ADD CONSTRAINT fk_actormember_actorid FOREIGN KEY (actorId) REFERENCES actor(id); CREATE TABLE category ( id INT8 NOT NULL, name VARCHAR(50) NOT NULL, creator INT8, description TEXT, creationDate INT8 NOT NULL, lastUpdateDate INT8 NOT NULL, CONSTRAINT pk_category PRIMARY KEY (id), CONSTRAINT uk_category_name UNIQUE (name) ); CREATE TABLE processcategorymapping ( id INT8 NOT NULL, categoryid INT8 NOT NULL, processid INT8 NOT NULL, CONSTRAINT pk_processcategorymapping PRIMARY KEY (id), CONSTRAINT uk_processcategorymapping_categoryid_processid UNIQUE (categoryid, processid) ); ALTER TABLE processcategorymapping ADD CONSTRAINT fk_processcategorymapping_categoryid FOREIGN KEY (categoryid) REFERENCES category(id) ON DELETE CASCADE; CREATE TABLE arch_process_comment( id INT8 NOT NULL, userId INT8, processInstanceId INT8 NOT NULL, postDate INT8 NOT NULL, content VARCHAR(512) NOT NULL, archiveDate INT8 NOT NULL, sourceObjectId INT8 NOT NULL, CONSTRAINT pk_arch_process_comment PRIMARY KEY (id) ); CREATE INDEX idx1_arch_process_comment on arch_process_comment (sourceobjectid); CREATE INDEX idx2_arch_process_comment on arch_process_comment (processInstanceId, archivedate); CREATE TABLE process_comment ( id INT8 NOT NULL, kind VARCHAR(25) NOT NULL, userId INT8, processInstanceId INT8 NOT NULL, postDate INT8 NOT NULL, content VARCHAR(512) NOT NULL, CONSTRAINT pk_process_comment PRIMARY KEY (id) ); CREATE INDEX idx1_process_comment on process_comment (processInstanceId); CREATE TABLE process_content ( id INT8 NOT NULL, content TEXT NOT NULL, CONSTRAINT pk_process_content PRIMARY KEY (id) ); CREATE TABLE process_definition ( id INT8 NOT NULL, processId INT8 NOT NULL, name VARCHAR(150) NOT NULL, version VARCHAR(50) NOT NULL, description VARCHAR(255), deploymentDate INT8 NOT NULL, deployedBy INT8 NOT NULL, activationState VARCHAR(30) NOT NULL, configurationState VARCHAR(30) NOT NULL, displayName VARCHAR(75), displayDescription VARCHAR(255), lastUpdateDate INT8, categoryId INT8, iconPath VARCHAR(255), content_id INT8 NOT NULL, CONSTRAINT pk_process_definition PRIMARY KEY (id), CONSTRAINT uk_process_definition_name_version UNIQUE (name, version) ); ALTER TABLE process_definition ADD CONSTRAINT fk_process_definition_content_id FOREIGN KEY (content_id) REFERENCES process_content(id); CREATE TABLE document ( id INT8 NOT NULL, author INT8, creationdate INT8 NOT NULL, hascontent BOOLEAN NOT NULL, filename VARCHAR(255), mimetype VARCHAR(255), url VARCHAR(1024), content BYTEA, CONSTRAINT pk_document PRIMARY KEY (id) ); CREATE TABLE document_mapping ( id INT8 NOT NULL, processinstanceid INT8 NOT NULL, documentid INT8 NOT NULL, name VARCHAR(50) NOT NULL, description TEXT, version VARCHAR(50) NOT NULL, index_ INT NOT NULL, CONSTRAINT pk_document_mapping PRIMARY KEY (id) ); ALTER TABLE document_mapping ADD CONSTRAINT fk_document_mapping_documentid FOREIGN KEY (documentid) REFERENCES document(id) ON DELETE CASCADE; CREATE TABLE arch_document_mapping ( id INT8 NOT NULL, sourceObjectId INT8, processinstanceid INT8 NOT NULL, documentid INT8 NOT NULL, name VARCHAR(50) NOT NULL, description TEXT, version VARCHAR(50) NOT NULL, index_ INT NOT NULL, archiveDate INT8 NOT NULL, CONSTRAINT pk_arch_document_mapping PRIMARY KEY (id) ); CREATE INDEX idx_a_doc_mp_pr_id ON arch_document_mapping (processinstanceid); ALTER TABLE arch_document_mapping ADD CONSTRAINT fk_arch_document_mapping_documentid FOREIGN KEY (documentid) REFERENCES document(id) ON DELETE CASCADE; CREATE TABLE arch_process_instance ( id INT8 NOT NULL, name VARCHAR(75) NOT NULL, processDefinitionId INT8 NOT NULL, description VARCHAR(255), startDate INT8 NOT NULL, startedBy INT8 NOT NULL, startedBySubstitute INT8 NOT NULL, endDate INT8 NOT NULL, archiveDate INT8 NOT NULL, stateId INT NOT NULL, lastUpdate INT8 NOT NULL, rootProcessInstanceId INT8, callerId INT8, sourceObjectId INT8 NOT NULL, stringIndex1 VARCHAR(255), stringIndex2 VARCHAR(255), stringIndex3 VARCHAR(255), stringIndex4 VARCHAR(255), stringIndex5 VARCHAR(255), CONSTRAINT pk_arch_process_instance PRIMARY KEY (id) ); CREATE INDEX idx1_arch_process_instance ON arch_process_instance (sourceObjectId, rootProcessInstanceId, callerId); CREATE INDEX idx2_arch_process_instance ON arch_process_instance (processDefinitionId, archiveDate); CREATE INDEX idx3_arch_process_instance ON arch_process_instance (sourceObjectId, callerId, stateId); CREATE TABLE arch_flownode_instance ( id INT8 NOT NULL, flownodeDefinitionId INT8 NOT NULL, kind VARCHAR(25) NOT NULL, sourceObjectId INT8, archiveDate INT8 NOT NULL, rootContainerId INT8 NOT NULL, parentContainerId INT8 NOT NULL, name VARCHAR(255) NOT NULL, displayName VARCHAR(255), displayDescription VARCHAR(255), stateId INT NOT NULL, stateName VARCHAR(50), terminal BOOLEAN NOT NULL, stable BOOLEAN , actorId INT8 NULL, assigneeId INT8 DEFAULT 0 NOT NULL, reachedStateDate INT8, lastUpdateDate INT8, expectedEndDate INT8, claimedDate INT8, priority SMALLINT, gatewayType VARCHAR(50), hitBys VARCHAR(255), logicalGroup1 INT8 NOT NULL, logicalGroup2 INT8 NOT NULL, logicalGroup3 INT8, logicalGroup4 INT8 NOT NULL, loop_counter INT, loop_max INT, loopCardinality INT, loopDataInputRef VARCHAR(255), loopDataOutputRef VARCHAR(255), description VARCHAR(255), sequential BOOLEAN, dataInputItemRef VARCHAR(255), dataOutputItemRef VARCHAR(255), nbActiveInst INT, nbCompletedInst INT, nbTerminatedInst INT, executedBy INT8, executedBySubstitute INT8, activityInstanceId INT8, aborting BOOLEAN NOT NULL, triggeredByEvent BOOLEAN, interrupting BOOLEAN, CONSTRAINT pk_arch_flownode_instance PRIMARY KEY (id) ); CREATE INDEX idx_afi_kind_lg2_executedBy ON arch_flownode_instance(logicalGroup2, kind, executedBy); CREATE INDEX idx_afi_kind_lg3 ON arch_flownode_instance(kind, logicalGroup3); CREATE INDEX idx_afi_lg4 ON arch_flownode_instance(logicalGroup4); CREATE INDEX idx_afi_sourceid_kind ON arch_flownode_instance (sourceObjectId, kind); CREATE INDEX idx1_afi_root_parent ON arch_flownode_instance (rootContainerId, parentContainerId); CREATE INDEX idx_lg4_lg2 on arch_flownode_instance(logicalGroup4, logicalGroup2); CREATE TABLE arch_connector_instance ( id INT8 NOT NULL, containerId INT8 NOT NULL, containerType VARCHAR(10) NOT NULL, connectorId VARCHAR(255) NOT NULL, version VARCHAR(50) NOT NULL, name VARCHAR(255) NOT NULL, activationEvent VARCHAR(30), state VARCHAR(50), sourceObjectId INT8, archiveDate INT8 NOT NULL, CONSTRAINT pk_arch_connector_instance PRIMARY KEY (id) ); CREATE INDEX idx1_arch_connector_instance ON arch_connector_instance (containerId, containerType); CREATE TABLE process_instance ( id INT8 NOT NULL, name VARCHAR(75) NOT NULL, processDefinitionId INT8 NOT NULL, description VARCHAR(255), startDate INT8 NOT NULL, startedBy INT8 NOT NULL, startedBySubstitute INT8 NOT NULL, endDate INT8 NOT NULL, stateId INT NOT NULL, stateCategory VARCHAR(50) NOT NULL, lastUpdate INT8 NOT NULL, containerId INT8, rootProcessInstanceId INT8, callerId INT8, callerType VARCHAR(50), interruptingEventId INT8, stringIndex1 VARCHAR(255), stringIndex2 VARCHAR(255), stringIndex3 VARCHAR(255), stringIndex4 VARCHAR(255), stringIndex5 VARCHAR(255), PRIMARY KEY (id) ); CREATE INDEX idx1_proc_inst_pdef_state ON process_instance (processdefinitionid, stateid); CREATE TABLE flownode_instance ( id INT8 NOT NULL, flownodeDefinitionId INT8 NOT NULL, kind VARCHAR(25) NOT NULL, rootContainerId INT8 NOT NULL, parentContainerId INT8 NOT NULL, name VARCHAR(255) NOT NULL, displayName VARCHAR(255), displayDescription VARCHAR(255), stateId INT NOT NULL, stateName VARCHAR(50), prev_state_id INT NOT NULL, terminal BOOLEAN NOT NULL, stable BOOLEAN , actorId INT8 NULL, assigneeId INT8 DEFAULT 0 NOT NULL, reachedStateDate INT8, lastUpdateDate INT8, expectedEndDate INT8, claimedDate INT8, priority SMALLINT, gatewayType VARCHAR(50), hitBys VARCHAR(255), stateCategory VARCHAR(50) NOT NULL, logicalGroup1 INT8 NOT NULL, logicalGroup2 INT8 NOT NULL, logicalGroup3 INT8, logicalGroup4 INT8 NOT NULL, loop_counter INT, loop_max INT, description VARCHAR(255), sequential BOOLEAN, loopDataInputRef VARCHAR(255), loopDataOutputRef VARCHAR(255), dataInputItemRef VARCHAR(255), dataOutputItemRef VARCHAR(255), loopCardinality INT, nbActiveInst INT, nbCompletedInst INT, nbTerminatedInst INT, executedBy INT8, executedBySubstitute INT8, activityInstanceId INT8, state_executing BOOLEAN DEFAULT FALSE, abortedByBoundary INT8, triggeredByEvent BOOLEAN, interrupting BOOLEAN, tokenCount INT NOT NULL, PRIMARY KEY (id) ); CREATE INDEX idx_fni_rootcontid ON flownode_instance (rootContainerId); CREATE INDEX idx_fni_loggroup4 ON flownode_instance (logicalGroup4); CREATE INDEX idx_fni_loggroup3_terminal ON flownode_instance(logicalgroup3, terminal); CREATE INDEX idx_fn_lg2_state ON flownode_instance (logicalGroup2, stateName); CREATE INDEX idx_fni_activity_instance_id_kind ON flownode_instance(activityInstanceId, kind); CREATE TABLE connector_instance ( id INT8 NOT NULL, containerId INT8 NOT NULL, containerType VARCHAR(10) NOT NULL, connectorId VARCHAR(255) NOT NULL, version VARCHAR(50) NOT NULL, name VARCHAR(255) NOT NULL, activationEvent VARCHAR(30), state VARCHAR(50), executionOrder INT, exceptionMessage VARCHAR(255), stackTrace TEXT, CONSTRAINT pk_connector_instance PRIMARY KEY (id) ); CREATE INDEX idx_ci_container_activation ON connector_instance (containerId, containerType, activationEvent); CREATE TABLE event_trigger_instance ( id INT8 NOT NULL, eventInstanceId INT8 NOT NULL, eventInstanceName VARCHAR(50), executionDate INT8, jobTriggerName VARCHAR(255), CONSTRAINT pk_event_trigger_instance PRIMARY KEY (id) ); CREATE TABLE waiting_event ( id INT8 NOT NULL, kind VARCHAR(15) NOT NULL, eventType VARCHAR(50), messageName VARCHAR(255), signalName VARCHAR(255), errorCode VARCHAR(255), processName VARCHAR(150), flowNodeName VARCHAR(50), flowNodeDefinitionId INT8, subProcessId INT8, processDefinitionId INT8, rootProcessInstanceId INT8, parentProcessInstanceId INT8, flowNodeInstanceId INT8, relatedActivityInstanceId INT8, locked BOOLEAN, active BOOLEAN, progress SMALLINT, correlation1 VARCHAR(128), correlation2 VARCHAR(128), correlation3 VARCHAR(128), correlation4 VARCHAR(128), correlation5 VARCHAR(128), CONSTRAINT pk_waiting_event PRIMARY KEY (id) ); CREATE INDEX idx_waiting_event ON waiting_event (progress, kind, locked, active); CREATE INDEX idx_waiting_event_correl ON waiting_event (correlation1, correlation2, correlation3, correlation4, correlation5); CREATE TABLE message_instance ( id INT8 NOT NULL, messageName VARCHAR(255) NOT NULL, targetProcess VARCHAR(255) NOT NULL, targetFlowNode VARCHAR(255) NULL, locked BOOLEAN NOT NULL, handled BOOLEAN NOT NULL, processDefinitionId INT8 NOT NULL, flowNodeName VARCHAR(255), correlation1 VARCHAR(128), correlation2 VARCHAR(128), correlation3 VARCHAR(128), correlation4 VARCHAR(128), correlation5 VARCHAR(128), creationDate INT8 NOT NULL, CONSTRAINT pk_message_instance PRIMARY KEY (id) ); CREATE INDEX idx_message_instance ON message_instance (messageName, targetProcess, correlation1, correlation2, correlation3); CREATE INDEX idx_message_instance_correl ON message_instance (correlation1, correlation2, correlation3, correlation4, correlation5); CREATE TABLE pending_mapping ( id INT8 NOT NULL, activityId INT8 NOT NULL, actorId INT8, userId INT8, CONSTRAINT pk_pending_mapping PRIMARY KEY (id), CONSTRAINT uk_pending_mapping_activityid_userid_actorid UNIQUE (activityId, userId, actorId) ); ALTER TABLE pending_mapping ADD CONSTRAINT fk_pending_mapping_activityid FOREIGN KEY (activityId) REFERENCES flownode_instance(id); CREATE TABLE ref_biz_data_inst ( id INT8 NOT NULL, kind VARCHAR(15) NOT NULL, name VARCHAR(255) NOT NULL, proc_inst_id INT8, fn_inst_id INT8, data_id INT8, data_classname VARCHAR(255) NOT NULL, CONSTRAINT pk_ref_biz_data_inst PRIMARY KEY (id) ); CREATE INDEX idx_biz_data_inst2 ON ref_biz_data_inst (fn_inst_id); CREATE INDEX idx_biz_data_inst3 ON ref_biz_data_inst (proc_inst_id); ALTER TABLE ref_biz_data_inst ADD CONSTRAINT fk_ref_biz_data_inst_proc_inst_id FOREIGN KEY (proc_inst_id) REFERENCES process_instance(id) ON DELETE CASCADE; ALTER TABLE ref_biz_data_inst ADD CONSTRAINT fk_ref_biz_data_inst_fn_inst_id FOREIGN KEY (fn_inst_id) REFERENCES flownode_instance(id) ON DELETE CASCADE; CREATE TABLE multi_biz_data ( id INT8 NOT NULL, idx INT8 NOT NULL, data_id INT8 NOT NULL, CONSTRAINT pk_multi_biz_data PRIMARY KEY (id, data_id) ); ALTER TABLE multi_biz_data ADD CONSTRAINT fk_multi_biz_data_id FOREIGN KEY (id) REFERENCES ref_biz_data_inst(id) ON DELETE CASCADE; CREATE TABLE arch_ref_biz_data_inst ( id BIGINT NOT NULL, kind VARCHAR(15) NOT NULL, name VARCHAR(255) NOT NULL, orig_proc_inst_id BIGINT, orig_fn_inst_id BIGINT, data_id BIGINT, data_classname VARCHAR(255) NOT NULL, CONSTRAINT pk_arch_ref_biz_data_inst PRIMARY KEY (id) ); CREATE INDEX idx_arch_biz_data_inst1 ON arch_ref_biz_data_inst (orig_proc_inst_id); CREATE INDEX idx_arch_biz_data_inst2 ON arch_ref_biz_data_inst (orig_fn_inst_id); CREATE TABLE arch_multi_biz_data ( id BIGINT NOT NULL, idx BIGINT NOT NULL, data_id BIGINT NOT NULL, CONSTRAINT pk_arch_multi_biz_data PRIMARY KEY (id, data_id) ); ALTER TABLE arch_multi_biz_data ADD CONSTRAINT fk_arch_multi_biz_data_id FOREIGN KEY (id) REFERENCES arch_ref_biz_data_inst(id) ON DELETE CASCADE; CREATE TABLE processsupervisor ( id INT8 NOT NULL, processDefId INT8 NOT NULL, userId INT8 NOT NULL, groupId INT8 NOT NULL, roleId INT8 NOT NULL, CONSTRAINT pk_processsupervisor PRIMARY KEY (id), CONSTRAINT uk_processsupervisor_processdefid_userid_groupid_roleid UNIQUE (processDefId, userId, groupId, roleId) ); CREATE TABLE page ( id INT8 NOT NULL, name VARCHAR(255) NOT NULL, displayName VARCHAR(255) NOT NULL, description TEXT, installationDate INT8 NOT NULL, installedBy INT8 NOT NULL, provided BOOLEAN, editable BOOLEAN, removable BOOLEAN, lastModificationDate INT8 NOT NULL, lastUpdatedBy INT8 NOT NULL, contentName VARCHAR(280) NOT NULL, content BYTEA, contentType VARCHAR(50) NOT NULL, processDefinitionId INT8 NOT NULL, pageHash VARCHAR(32), CONSTRAINT pk_page PRIMARY KEY (id), CONSTRAINT uk_page_name_processdefinitionid UNIQUE (name, processDefinitionId) ); CREATE TABLE profile ( id INT8 NOT NULL, isDefault BOOLEAN NOT NULL, name VARCHAR(50) NOT NULL, description TEXT, creationDate INT8 NOT NULL, createdBy INT8 NOT NULL, lastUpdateDate INT8 NOT NULL, lastUpdatedBy INT8 NOT NULL, CONSTRAINT uk_profile_name UNIQUE (name), CONSTRAINT pk_profile PRIMARY KEY (id) ); CREATE TABLE profilemember ( id INT8 NOT NULL, profileId INT8 NOT NULL, userId INT8 NOT NULL, groupId INT8 NOT NULL, roleId INT8 NOT NULL, CONSTRAINT pk_profilemember PRIMARY KEY (id), CONSTRAINT uk_profilemember_profileid_userid_groupid_roleid UNIQUE (profileId, userId, groupId, roleId) ); ALTER TABLE profilemember ADD CONSTRAINT fk_profilemember_profileid FOREIGN KEY (profileId) REFERENCES profile(id); CREATE TABLE business_app ( id INT8 NOT NULL, token VARCHAR(50) NOT NULL, version VARCHAR(50) NOT NULL, description TEXT, iconPath VARCHAR(255), creationDate INT8 NOT NULL, createdBy INT8 NOT NULL, lastUpdateDate INT8 NOT NULL, updatedBy INT8 NOT NULL, state VARCHAR(30) NOT NULL, homePageId INT8, profileId INT8, layoutId INT8, themeId INT8, iconMimeType VARCHAR(255), iconContent BYTEA, displayName VARCHAR(255) NOT NULL, editable BOOLEAN, internalProfile VARCHAR(255), isLink BOOLEAN DEFAULT FALSE, CONSTRAINT pk_business_app PRIMARY KEY (id), CONSTRAINT uk_business_app_token_version UNIQUE (token, version) ); CREATE INDEX idx_app_token ON business_app (token); CREATE INDEX idx_app_profile ON business_app (profileId); CREATE INDEX idx_app_homepage ON business_app (homePageId); ALTER TABLE business_app ADD CONSTRAINT fk_business_app_profileid FOREIGN KEY (profileId) REFERENCES profile (id); ALTER TABLE business_app ADD CONSTRAINT fk_business_app_layoutid FOREIGN KEY (layoutId) REFERENCES page (id); ALTER TABLE business_app ADD CONSTRAINT fk_business_app_themeid FOREIGN KEY (themeId) REFERENCES page (id); CREATE TABLE business_app_page ( id INT8 NOT NULL, applicationId INT8 NOT NULL, pageId INT8 NOT NULL, token VARCHAR(255) NOT NULL, CONSTRAINT pk_business_app_page PRIMARY KEY (id), CONSTRAINT uk_business_app_page_applicationid_token UNIQUE (applicationId, token) ); CREATE INDEX idx_app_page_token ON business_app_page (applicationId, token); CREATE INDEX idx_app_page_pageId ON business_app_page (pageId); ALTER TABLE business_app_page ADD CONSTRAINT fk_business_app_page_applicationid FOREIGN KEY (applicationId) REFERENCES business_app (id) ON DELETE CASCADE; ALTER TABLE business_app_page ADD CONSTRAINT fk_business_app_page_pageid FOREIGN KEY (pageId) REFERENCES page (id); CREATE TABLE business_app_menu ( id INT8 NOT NULL, displayName VARCHAR(255) NOT NULL, applicationId INT8 NOT NULL, applicationPageId INT8, parentId INT8, index_ INT8, CONSTRAINT pk_business_app_menu PRIMARY KEY (id) ); CREATE INDEX idx_app_menu_app ON business_app_menu (applicationId); CREATE INDEX idx_app_menu_page ON business_app_menu (applicationPageId); CREATE INDEX idx_app_menu_parent ON business_app_menu (parentId); -- cannot have both fk_business_app_menu_applicationid and fk_business_app_menu_applicationpageid because this create to path for deletion of business_app_menu elements: -- business_app -> business_app_menu -- business_app -> business_app_page -> business_app_menu -- this is not allowed in SQL Server ALTER TABLE business_app_menu ADD CONSTRAINT fk_business_app_menu_applicationid FOREIGN KEY (applicationId) REFERENCES business_app (id); ALTER TABLE business_app_menu ADD CONSTRAINT fk_business_app_menu_applicationpageid FOREIGN KEY (applicationPageId) REFERENCES business_app_page (id); ALTER TABLE business_app_menu ADD CONSTRAINT fk_business_app_menu_parentid FOREIGN KEY (parentId) REFERENCES business_app_menu (id); CREATE TABLE command ( id INT8 NOT NULL, name VARCHAR(50) NOT NULL, description TEXT, IMPLEMENTATION VARCHAR(100) NOT NULL, isSystem BOOLEAN, CONSTRAINT pk_command PRIMARY KEY (id), CONSTRAINT uk_command_name UNIQUE (name) ); CREATE TABLE arch_data_instance ( id INT8 NOT NULL, name VARCHAR(50), description VARCHAR(50), transientData BOOLEAN, className VARCHAR(100), containerId INT8, containerType VARCHAR(60), namespace VARCHAR(100), element VARCHAR(60), intValue INT, longValue INT8, shortTextValue VARCHAR(255), booleanValue BOOLEAN, doubleValue NUMERIC(19,5), floatValue REAL, blobValue BYTEA, clobValue TEXT, discriminant VARCHAR(50) NOT NULL, archiveDate INT8 NOT NULL, sourceObjectId INT8 NOT NULL, CONSTRAINT pk_arch_data_instance PRIMARY KEY (id) ); CREATE INDEX idx1_arch_data_instance ON arch_data_instance (containerId, containerType, archiveDate, name, sourceObjectId); CREATE INDEX idx2_arch_data_instance ON arch_data_instance (sourceObjectId, containerId, archiveDate, id); CREATE TABLE data_instance ( id INT8 NOT NULL, name VARCHAR(50), description VARCHAR(50), transientData BOOLEAN, className VARCHAR(100), containerId INT8, containerType VARCHAR(60), namespace VARCHAR(100), element VARCHAR(60), intValue INT, longValue INT8, shortTextValue VARCHAR(255), booleanValue BOOLEAN, doubleValue NUMERIC(19,5), floatValue REAL, blobValue BYTEA, clobValue TEXT, discriminant VARCHAR(50) NOT NULL, CONSTRAINT pk_data_instance PRIMARY KEY (id) ); CREATE INDEX idx_datai_container ON data_instance (containerId, containerType, name); CREATE TABLE dependency ( id INT8 NOT NULL, name VARCHAR(150) NOT NULL, description TEXT, filename VARCHAR(255) NOT NULL, value_ BYTEA NOT NULL, CONSTRAINT pk_dependency PRIMARY KEY (id), CONSTRAINT uk_dependency_name UNIQUE (name) ); CREATE TABLE dependencymapping ( id INT8 NOT NULL, artifactid INT8 NOT NULL, artifacttype VARCHAR(50) NOT NULL, dependencyid INT8 NOT NULL, CONSTRAINT pk_dependencymapping PRIMARY KEY (id), CONSTRAINT uk_dependencymapping_dependencyid_artifactid_artifacttype UNIQUE (dependencyid, artifactid, artifacttype) ); CREATE INDEX idx_dependencymapping_depid ON dependencymapping (dependencyid); ALTER TABLE dependencymapping ADD CONSTRAINT fk_dependencymapping_dependencyid FOREIGN KEY (dependencyid) REFERENCES dependency(id) ON DELETE CASCADE; CREATE TABLE pdependency ( id INT8 NOT NULL, name VARCHAR(50) NOT NULL, description TEXT, filename VARCHAR(255) NOT NULL, value_ BYTEA NOT NULL, CONSTRAINT pk_pdependency PRIMARY KEY (id), CONSTRAINT uk_pdependency_name UNIQUE (name) ); CREATE TABLE pdependencymapping ( id INT8 NOT NULL, artifactid INT8 NOT NULL, artifacttype VARCHAR(50) NOT NULL, dependencyid INT8 NOT NULL, CONSTRAINT pk_pdependencymapping PRIMARY KEY (id), CONSTRAINT uk_pdependencymapping_dependencyid_artifactid_artifacttype UNIQUE (dependencyid, artifactid, artifacttype) ); CREATE INDEX idx_pdependencymapping_depid ON pdependencymapping (dependencyid); ALTER TABLE pdependencymapping ADD CONSTRAINT fk_pdependencymapping_dependencyid FOREIGN KEY (dependencyid) REFERENCES pdependency(id) ON DELETE CASCADE; CREATE TABLE group_ ( id INT8 NOT NULL, name VARCHAR(125) NOT NULL, parentPath VARCHAR(255), displayName VARCHAR(255), description TEXT, createdBy INT8, creationDate INT8, lastUpdate INT8, iconid INT8, CONSTRAINT pk_group PRIMARY KEY (id) ); CREATE INDEX idx_group_name ON group_ (parentPath, name); CREATE TABLE role ( id INT8 NOT NULL, name VARCHAR(255) NOT NULL, displayName VARCHAR(255), description TEXT, createdBy INT8, creationDate INT8, lastUpdate INT8, iconid INT8, CONSTRAINT pk_role PRIMARY KEY (id), CONSTRAINT uk_role_name UNIQUE (name) ); CREATE TABLE user_ ( id INT8 NOT NULL, enabled BOOLEAN NOT NULL, userName VARCHAR(255) NOT NULL, password VARCHAR(60), firstName VARCHAR(255), lastName VARCHAR(255), title VARCHAR(50), jobTitle VARCHAR(255), managerUserId INT8, createdBy INT8, creationDate INT8, lastUpdate INT8, iconid INT8, CONSTRAINT pk_user PRIMARY KEY (id), CONSTRAINT uk_user_username UNIQUE (userName) ); CREATE TABLE user_login ( id INT8 NOT NULL, lastConnection INT8, CONSTRAINT pk_user_login PRIMARY KEY (id) ); CREATE TABLE user_contactinfo ( id INT8 NOT NULL, userId INT8 NOT NULL, email VARCHAR(255), phone VARCHAR(50), mobile VARCHAR(50), fax VARCHAR(50), building VARCHAR(50), room VARCHAR(50), address VARCHAR(255), zipCode VARCHAR(50), city VARCHAR(255), state VARCHAR(255), country VARCHAR(255), website VARCHAR(255), personal BOOLEAN NOT NULL, CONSTRAINT pk_user_contactinfo PRIMARY KEY (id), CONSTRAINT uk_user_contactinfo_userid_personal UNIQUE (userId, personal) ); ALTER TABLE user_contactinfo ADD CONSTRAINT fk_user_contactinfo_userid FOREIGN KEY (userId) REFERENCES user_ (id) ON DELETE CASCADE; CREATE TABLE custom_usr_inf_def ( id INT8 NOT NULL, name VARCHAR(75) NOT NULL, description TEXT, CONSTRAINT pk_custom_usr_inf_def PRIMARY KEY (id), CONSTRAINT uk_custom_usr_inf_def_name UNIQUE (name) ); CREATE TABLE custom_usr_inf_val ( id INT8 NOT NULL, definitionId INT8 NOT NULL, userId INT8 NOT NULL, value VARCHAR(255), CONSTRAINT pk_custom_usr_inf_val PRIMARY KEY (id), CONSTRAINT uk_custom_usr_inf_val_definitionid_userid UNIQUE (definitionId, userId) ); ALTER TABLE custom_usr_inf_val ADD CONSTRAINT fk_custom_usr_inf_val_userid FOREIGN KEY (userId) REFERENCES user_ (id) ON DELETE CASCADE; ALTER TABLE custom_usr_inf_val ADD CONSTRAINT fk_custom_usr_inf_val_definitionid FOREIGN KEY (definitionId) REFERENCES custom_usr_inf_def (id) ON DELETE CASCADE; CREATE TABLE user_membership ( id INT8 NOT NULL, userId INT8 NOT NULL, roleId INT8 NOT NULL, groupId INT8 NOT NULL, assignedBy INT8, assignedDate INT8, CONSTRAINT pk_user_membership PRIMARY KEY (id), CONSTRAINT uk_user_membership_userid_roleid_groupid UNIQUE (userId, roleId, groupId) ); CREATE TABLE icon ( id INT8 NOT NULL, mimetype VARCHAR(255) NOT NULL, content BYTEA NOT NULL, CONSTRAINT pk_icon PRIMARY KEY (id) ); CREATE TABLE queriable_log ( id INT8 NOT NULL, log_timestamp INT8 NOT NULL, whatYear SMALLINT NOT NULL, whatMonth SMALLINT NOT NULL, dayOfYear SMALLINT NOT NULL, weekOfYear SMALLINT NOT NULL, userId VARCHAR(255) NOT NULL, threadNumber INT8 NOT NULL, clusterNode VARCHAR(50), productVersion VARCHAR(50) NOT NULL, severity VARCHAR(50) NOT NULL, actionType VARCHAR(50) NOT NULL, actionScope VARCHAR(100), actionStatus SMALLINT NOT NULL, rawMessage VARCHAR(255) NOT NULL, callerClassName VARCHAR(200), callerMethodName VARCHAR(80), numericIndex1 INT8, numericIndex2 INT8, numericIndex3 INT8, numericIndex4 INT8, numericIndex5 INT8, CONSTRAINT pk_queriable_log PRIMARY KEY (id) ); CREATE TABLE sequence ( id INT8 NOT NULL, nextid INT8 NOT NULL, CONSTRAINT pk_sequence PRIMARY KEY (id) ); CREATE TABLE platform ( id INT8 NOT NULL, version VARCHAR(50) NOT NULL, initial_bonita_version VARCHAR(50) NOT NULL, application_version VARCHAR(50) NOT NULL, maintenance_message TEXT, maintenance_message_active BOOLEAN NOT NULL, created INT8 NOT NULL, created_by VARCHAR(50) NOT NULL, information TEXT, maintenance_enabled BOOLEAN NOT NULL, CONSTRAINT pk_platform PRIMARY KEY (id) ); CREATE TABLE platformCommand ( id INT8 PRIMARY KEY, name VARCHAR(50) NOT NULL UNIQUE, description TEXT, IMPLEMENTATION VARCHAR(100) NOT NULL ); CREATE TABLE job_desc ( id INT8 NOT NULL, jobclassname VARCHAR(100) NOT NULL, jobname VARCHAR(100) NOT NULL, description VARCHAR(50), CONSTRAINT pk_job_desc PRIMARY KEY (id) ); CREATE TABLE job_param ( id INT8 NOT NULL, jobDescriptorId INT8 NOT NULL, key_ VARCHAR(50) NOT NULL, value_ BYTEA NOT NULL, CONSTRAINT pk_job_param PRIMARY KEY (id) ); ALTER TABLE job_param ADD CONSTRAINT fk_job_param_jobdescriptorid FOREIGN KEY (jobDescriptorId) REFERENCES job_desc(id) ON DELETE CASCADE; CREATE INDEX idx_job_param_jobid ON job_param(jobDescriptorId); CREATE TABLE job_log ( id INT8 NOT NULL, jobDescriptorId INT8 NOT NULL, retryNumber INT8, lastUpdateDate INT8, lastMessage TEXT, CONSTRAINT pk_job_log PRIMARY KEY (id), CONSTRAINT uk_job_log_jobdescriptorid UNIQUE (jobDescriptorId) ); ALTER TABLE job_log ADD CONSTRAINT fk_job_log_jobdescriptorid FOREIGN KEY (jobDescriptorId) REFERENCES job_desc(id) ON DELETE CASCADE; CREATE TABLE page_mapping ( id INT8 NOT NULL, key_ VARCHAR(255) NOT NULL, pageId INT8 NULL, url VARCHAR(1024) NULL, urladapter VARCHAR(255) NULL, page_authoriz_rules TEXT NULL, lastUpdateDate INT8 NULL, lastUpdatedBy INT8 NULL, CONSTRAINT uk_page_mapping_key UNIQUE (key_), CONSTRAINT pk_page_mapping PRIMARY KEY (id) ); CREATE TABLE form_mapping ( id INT8 NOT NULL, process INT8 NOT NULL, type INT NOT NULL, task VARCHAR(255), page_mapping_id INT8, lastUpdateDate INT8, lastUpdatedBy INT8, target VARCHAR(16) NOT NULL, CONSTRAINT pk_form_mapping PRIMARY KEY (id) ); ALTER TABLE form_mapping ADD CONSTRAINT fk_form_mapping_key FOREIGN KEY (page_mapping_id) REFERENCES page_mapping(id); CREATE TABLE proc_parameter ( id INT8 NOT NULL, process_id INT8 NOT NULL, name VARCHAR(255) NOT NULL, value TEXT NULL, CONSTRAINT pk_proc_parameter PRIMARY KEY (id) ); CREATE TABLE bar_resource ( id INT8 NOT NULL, process_id INT8 NOT NULL, name VARCHAR(255) NOT NULL, type VARCHAR(16) NOT NULL, content BYTEA NOT NULL, CONSTRAINT pk_bar_resource PRIMARY KEY (id), CONSTRAINT uk_bar_resource_processid_name_type UNIQUE (process_id, name, type) ); CREATE TABLE temporary_content ( id INT8 NOT NULL, creationDate INT8 NOT NULL, key_ VARCHAR(255) NOT NULL, fileName VARCHAR(255) NOT NULL, mimeType VARCHAR(255) NOT NULL, content OID NOT NULL, UNIQUE (key_), PRIMARY KEY (id) ); CREATE INDEX idx_temporary_content ON temporary_content (key_); ------------------------- PostgreSQL large objects cleanup for temporary_content ---------------------- DO ' BEGIN EXECUTE ''CREATE OR REPLACE FUNCTION temporary_content_lo_cleanup() RETURNS trigger LANGUAGE plpgsql AS '''' BEGIN IF OLD.content IS NOT NULL THEN PERFORM lo_unlink(OLD.content); END IF; RETURN OLD; END; ''''''; END '; DROP TRIGGER IF EXISTS trg_temporary_content_lo_cleanup ON temporary_content; CREATE TRIGGER trg_temporary_content_lo_cleanup AFTER DELETE ON temporary_content FOR EACH ROW EXECUTE FUNCTION temporary_content_lo_cleanup(); CREATE TABLE tenant_resource ( id INT8 NOT NULL, name VARCHAR(255) NOT NULL, type VARCHAR(16) NOT NULL, content BYTEA NOT NULL, lastUpdatedBy INT8 NOT NULL, lastUpdateDate INT8, state VARCHAR(50) NOT NULL, CONSTRAINT pk_tenant_resource PRIMARY KEY (id), CONSTRAINT uk_tenant_resource_name_type UNIQUE (name, type) ); CREATE TABLE bpm_failure ( id INT8 NOT NULL, processDefinitionId INT8 NOT NULL, processInstanceId INT8 NOT NULL, rootProcessInstanceId INT8, flowNodeInstanceId INT8, scope VARCHAR(255), context VARCHAR(1024), errorMessage VARCHAR(1024), stackTrace TEXT, failureDate INT8 NOT NULL, CONSTRAINT pk_bpm_failure PRIMARY KEY (id) ); CREATE INDEX idx_bpm_failure_flownodeinstanceid ON bpm_failure (flowNodeInstanceId); CREATE INDEX idx_bpm_failure_processinstanceid ON bpm_failure (processInstanceId); CREATE INDEX idx_bpm_failure_rootprocessinstanceid ON bpm_failure (rootProcessInstanceId); CREATE INDEX idx_bpm_failure_processdefinitionid ON bpm_failure (processDefinitionId); CREATE TABLE arch_bpm_failure ( id INT8 NOT NULL, processDefinitionId INT8 NOT NULL, processInstanceId INT8 NOT NULL, rootProcessInstanceId INT8, flowNodeInstanceId INT8, scope VARCHAR(255), context VARCHAR(1024), errorMessage VARCHAR(1024), stackTrace TEXT, failureDate INT8 NOT NULL, archiveDate INT8 NOT NULL, sourceObjectId INT8 NOT NULL, CONSTRAINT pk_arch_bpm_failure PRIMARY KEY (id) ); CREATE INDEX idx_arch_bpm_failure_flownodeinstanceid ON arch_bpm_failure (flowNodeInstanceId); CREATE INDEX idx_arch_bpm_failure_processinstanceid ON arch_bpm_failure (processInstanceId); CREATE INDEX idx_arch_bpm_failure_rootprocessinstanceid ON arch_bpm_failure (rootProcessInstanceId); CREATE INDEX idx_arch_bpm_failure_processdefinitionid ON arch_bpm_failure (processDefinitionId); CREATE TABLE data_retention_config ( id INT8 NOT NULL, data_classname VARCHAR(255) NOT NULL, reference_date VARCHAR(20) NOT NULL, retention_days INT NOT NULL, created_at INT8 NOT NULL, updated_at INT8 NOT NULL, CONSTRAINT pk_data_retention_config PRIMARY KEY (id), CONSTRAINT uk_data_retention_config_data_classname UNIQUE (data_classname) ); CREATE TABLE data_retention_bdm_tracking ( id INT8 NOT NULL, data_id INT8 NOT NULL, data_classname VARCHAR(255) NOT NULL, created_at INT8 NOT NULL, last_modified_at INT8 NOT NULL, CONSTRAINT pk_data_retention_bdm_tracking PRIMARY KEY (id), CONSTRAINT uk_data_retention_bdm_tracking_data_id_data_classname UNIQUE (data_id, data_classname) ); CREATE INDEX idx_data_retention_bdm_tracking_data_classname ON data_retention_bdm_tracking (data_classname); CREATE TABLE delegation_rule ( id INT8 NOT NULL, delegator_id INT8 NOT NULL, delegate_id INT8 NOT NULL, start_date INT8 NOT NULL, end_date INT8 NOT NULL, last_updated_by INT8 NOT NULL, last_updated_at INT8 NOT NULL, CONSTRAINT pk_delegation_rule PRIMARY KEY (id), CONSTRAINT uk_delegation_rule_delegator_id UNIQUE (delegator_id) ); CREATE TABLE delegation_rule_process ( id INT8 NOT NULL, delegation_rule_id INT8 NOT NULL, process_name VARCHAR(255) NOT NULL, CONSTRAINT pk_delegation_rule_process PRIMARY KEY (id), CONSTRAINT uk_delegation_rule_process_delegation_rule_id_process_name UNIQUE (delegation_rule_id, process_name) ); ALTER TABLE delegation_rule_process ADD CONSTRAINT fk_delegation_rule_process_delegation_rule_id FOREIGN KEY (delegation_rule_id) REFERENCES delegation_rule(id) ON DELETE CASCADE; ================================================ FILE: platform/platform-resources/src/main/resources/sql/postgres/dropQuartzTables.sql ================================================ DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; DROP TABLE IF EXISTS QRTZ_LOCKS; DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; DROP TABLE IF EXISTS QRTZ_TRIGGERS; DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; DROP TABLE IF EXISTS QRTZ_CALENDARS; ================================================ FILE: platform/platform-resources/src/main/resources/sql/postgres/dropTables.sql ================================================ DROP TABLE IF EXISTS configuration; DROP TABLE IF EXISTS arch_contract_data; DROP TABLE IF EXISTS contract_data; DROP TABLE IF EXISTS actormember; DROP TABLE IF EXISTS actor; DROP TABLE IF EXISTS processcategorymapping; DROP TABLE IF EXISTS category; DROP TABLE IF EXISTS arch_process_comment; DROP TABLE IF EXISTS process_comment; DROP TABLE IF EXISTS process_definition; DROP TABLE IF EXISTS arch_document_mapping CASCADE; DROP TABLE IF EXISTS document_mapping; DROP TABLE IF EXISTS document; DROP TABLE IF EXISTS arch_flownode_instance; DROP TABLE IF EXISTS arch_process_instance; DROP TABLE IF EXISTS arch_connector_instance; DROP TABLE IF EXISTS arch_multi_biz_data; DROP TABLE IF EXISTS arch_ref_biz_data_inst; DROP TABLE IF EXISTS multi_biz_data; DROP TABLE IF EXISTS ref_biz_data_inst; DROP TABLE IF EXISTS pending_mapping; DROP TABLE IF EXISTS connector_instance; DROP TABLE IF EXISTS flownode_instance; DROP TABLE IF EXISTS process_instance; DROP TABLE IF EXISTS event_trigger_instance; DROP TABLE IF EXISTS waiting_event; DROP TABLE IF EXISTS message_instance; DROP TABLE IF EXISTS processsupervisor; DROP TABLE IF EXISTS business_app_menu; DROP TABLE IF EXISTS business_app_page; DROP TABLE IF EXISTS business_app; DROP TABLE IF EXISTS command; DROP TABLE IF EXISTS arch_data_instance; DROP TABLE IF EXISTS data_instance; DROP TABLE IF EXISTS dependencymapping; DROP TABLE IF EXISTS dependency; DROP TABLE IF EXISTS pdependencymapping; DROP TABLE IF EXISTS pdependency; DROP TABLE IF EXISTS user_membership; DROP TABLE IF EXISTS custom_usr_inf_val; DROP TABLE IF EXISTS custom_usr_inf_def; DROP TABLE IF EXISTS user_contactinfo; DROP TABLE IF EXISTS user_login; DROP TABLE IF EXISTS user_; DROP TABLE IF EXISTS role; DROP TABLE IF EXISTS group_; DROP TABLE IF EXISTS queriable_log; DROP TABLE IF EXISTS page; DROP TABLE IF EXISTS profilemember; DROP TABLE IF EXISTS profile; DROP TABLE IF EXISTS job_log; DROP TABLE IF EXISTS job_param; DROP TABLE IF EXISTS job_desc; DROP TABLE IF EXISTS sequence; DROP TABLE IF EXISTS tenant; DROP TABLE IF EXISTS platform; DROP TABLE IF EXISTS platformCommand; DROP TABLE IF EXISTS form_mapping; DROP TABLE IF EXISTS page_mapping; DROP TABLE IF EXISTS process_content; DROP TABLE IF EXISTS proc_parameter; DROP TABLE IF EXISTS bar_resource; DROP TABLE IF EXISTS temporary_content; DROP TABLE IF EXISTS tenant_resource; DROP TABLE IF EXISTS icon; DROP TABLE IF EXISTS arch_bpm_failure; DROP TABLE IF EXISTS bpm_failure; DROP TABLE IF EXISTS data_retention_config; DROP TABLE IF EXISTS data_retention_bdm_tracking; DROP TABLE IF EXISTS delegation_rule_process; DROP TABLE IF EXISTS delegation_rule; ================================================ FILE: platform/platform-resources/src/main/resources/sql/postgres/initTables.sql ================================================ INSERT INTO sequence VALUES (1, 1); INSERT INTO sequence VALUES (2, 1); INSERT INTO sequence VALUES (3, 1); INSERT INTO sequence VALUES (4, 1); INSERT INTO sequence VALUES (5, 1); INSERT INTO sequence VALUES (6, 1); INSERT INTO sequence VALUES (7, 1); INSERT INTO sequence VALUES (8, 1); INSERT INTO sequence VALUES (9, 1); INSERT INTO sequence VALUES(10, 1); INSERT INTO sequence VALUES(11, 1); INSERT INTO sequence VALUES(20, 1); INSERT INTO sequence VALUES(21, 1); INSERT INTO sequence VALUES(22, 1); INSERT INTO sequence VALUES(23, 1); INSERT INTO sequence VALUES(24, 1); INSERT INTO sequence VALUES(25, 1); INSERT INTO sequence VALUES(26, 1); INSERT INTO sequence VALUES(27, 1); INSERT INTO sequence VALUES(30, 1); INSERT INTO sequence VALUES(70, 1); INSERT INTO sequence VALUES(71, 1); INSERT INTO sequence VALUES(72, 1); INSERT INTO sequence VALUES(90, 1); INSERT INTO sequence VALUES(9990, 1); INSERT INTO sequence VALUES(9992, 1); INSERT INTO sequence VALUES(10000, 1); INSERT INTO sequence VALUES(10001, 1); INSERT INTO sequence VALUES(10010, 1); INSERT INTO sequence VALUES(10011, 1); INSERT INTO sequence VALUES(10012, 1); INSERT INTO sequence VALUES(10014, 1); INSERT INTO sequence VALUES(10015, 1); INSERT INTO sequence VALUES(10016, 1); INSERT INTO sequence VALUES(10017, 1); INSERT INTO sequence VALUES(10018, 1); INSERT INTO sequence VALUES(10020, 1); INSERT INTO sequence VALUES(10021, 1); INSERT INTO sequence VALUES(10030, 1); INSERT INTO sequence VALUES(10031, 1); INSERT INTO sequence VALUES(10040, 1); INSERT INTO sequence VALUES(20051, 1); INSERT INTO sequence VALUES(10050, 1); INSERT INTO sequence VALUES(10060, 1); INSERT INTO sequence VALUES(10080, 1); INSERT INTO sequence VALUES(10090, 1); INSERT INTO sequence VALUES(10096, 1); INSERT INTO sequence VALUES(10120, 1); INSERT INTO sequence VALUES(10121, 1); INSERT INTO sequence VALUES(10200, 1); INSERT INTO sequence VALUES(10201, 1); INSERT INTO sequence VALUES(10202, 1); INSERT INTO sequence VALUES(10210, 1); INSERT INTO sequence VALUES(10220, 1); INSERT INTO sequence VALUES(10300, 1); INSERT INTO sequence VALUES(10310, 1); INSERT INTO sequence VALUES(10400, 1); INSERT INTO sequence VALUES(10500, 1); INSERT INTO sequence VALUES(10501, 1); INSERT INTO sequence VALUES(20010, 1); INSERT INTO sequence VALUES(20011, 1); INSERT INTO sequence VALUES(20013, 1); INSERT INTO sequence VALUES(20040, 1); INSERT INTO sequence VALUES(20050, 1); INSERT INTO sequence VALUES(20210, 1); INSERT INTO sequence VALUES(20220, 1); INSERT INTO sequence VALUES(20096, 1); INSERT INTO sequence VALUES(20097, 1); INSERT INTO sequence VALUES(20098, 1); ================================================ FILE: platform/platform-resources/src/main/resources/sql/postgres/preDropStructure.sql ================================================ -- ------------------------------------------------ Foreign Keys ----------------------------------------------- ALTER TABLE actormember DROP CONSTRAINT fk_actormember_actorid; ALTER TABLE profilemember DROP CONSTRAINT fk_profilemember_profileid; ALTER TABLE document_mapping DROP CONSTRAINT fk_document_mapping_documentid; ALTER TABLE pending_mapping DROP CONSTRAINT fk_pending_mapping_activityid; ALTER TABLE process_definition DROP CONSTRAINT fk_process_definition_content_id; -- business application ALTER TABLE business_app_menu DROP CONSTRAINT fk_business_app_menu_applicationid; ALTER TABLE business_app_menu DROP CONSTRAINT fk_business_app_menu_applicationpageid; ALTER TABLE business_app_menu DROP CONSTRAINT fk_business_app_menu_parentid; ALTER TABLE business_app_page DROP CONSTRAINT fk_business_app_page_applicationid; ALTER TABLE business_app_page DROP CONSTRAINT fk_business_app_page_pageid; ALTER TABLE business_app DROP CONSTRAINT fk_business_app_profileid; ALTER TABLE business_app DROP CONSTRAINT fk_business_app_layoutid; ALTER TABLE business_app DROP CONSTRAINT fk_business_app_themeid; -- delegation ALTER TABLE delegation_rule_process DROP CONSTRAINT fk_delegation_rule_process_delegation_rule_id; -- ------------------------ Foreign Keys to disable if archiving is on another BD ------------------ ALTER TABLE arch_document_mapping DROP CONSTRAINT fk_arch_document_mapping_documentid; -- ------------------------ Temporary_content Trigger ------------------ DROP TRIGGER IF EXISTS trg_temporary_content_lo_cleanup ON temporary_content; DROP FUNCTION IF EXISTS temporary_content_lo_cleanup(); ================================================ FILE: platform/platform-resources/src/main/resources/tenant_engine/bonita-tenants-custom.xml ================================================ ================================================ FILE: platform/platform-resources/src/main/resources/tenant_portal/compound-permissions-mapping-custom.properties ================================================ # # List of permissions used for custom pages. # Default Bonita Portal permissions can be found in file 'compound-permissions-mapping.properties' # # Add you custom permissions below: # ================================================ FILE: platform/platform-resources/src/main/resources/tenant_portal/compound-permissions-mapping-internal.properties ================================================ # internal use only. Do not modify manually ================================================ FILE: platform/platform-resources/src/main/resources/tenant_portal/compound-permissions-mapping.properties ================================================ ## # List of permissions used for each pages. # The content of this file is handled by the portal, it should not be modified ## custompage_layoutBonita=[application_visualization, avatars, organization_visualization, profile_visualization, tenant_platform_visualization] custompage_layoutWithoutMenuBonita=[application_visualization] custompage_tasklist=[flownode_visualization, bdm_visualization, task_visualization, form_visualization, case_visualization, document_visualization, form_file_upload, task_management, process_visualization, download_document, tenant_platform_visualization, process_comment, avatars] custompage_userCaseListBonita=[case_visualization, process_visualization, tenant_platform_visualization] custompage_userCaseDetailsBonita=[flownode_visualization, task_visualization, bdm_visualization, document_visualization, case_visualization, tenant_platform_visualization, download_document, process_comment] custompage_processlistBonita=[process_categories_visualization, form_visualization, form_file_upload, case_start, process_visualization, download_document, tenant_platform_visualization] custompage_adminMonitoringBonita=[organization_visualization, profile_visualization, case_visualization, process_visualization, tenant_platform_visualization] custompage_adminProcessListBonita=[process_deploy, process_management, connector_management, process_visualization, process_enablement, process_categories_management, process_actor_mapping_management] custompage_adminProcessDetailsBonita=[connector_visualization, process_manager_visualization, activity_visualization, process_manager_management, case_visualization, process_deploy, form_file_upload, case_start, process_visualization, process_enablement, process_categories_management, tenant_platform_visualization, process_categories_visualization, flownode_visualization, organization_visualization, form_visualization, form_management, process_management, connector_management, process_actor_mapping_management] custompage_adminProcessVisuBonita=[process_management, process_visualization] custompage_adminCaseListBonita=[case_visualization, case_delete, process_visualization] custompage_adminCaseDetailsBonita=[flownode_visualization, task_visualization, bdm_visualization, document_visualization, case_visualization, case_management, process_visualization, download_document, tenant_platform_visualization, process_comment] custompage_adminCaseVisuBonita=[case_visualization, process_visualization] custompage_adminTaskListBonita=[flownode_visualization, process_visualization, task_visualization] custompage_adminTaskDetailsBonita=[connector_visualization, task_visualization, case_visualization, form_file_upload, tenant_platform_visualization, flownode_visualization, bdm_visualization, organization_visualization, form_visualization, task_management, flownode_management, download_document, process_comment] custompage_adminUserListBonita=[organization_management, organization_visualization] custompage_adminUserDetailsBonita=[organization_visualization, organization_management, avatars] custompage_adminGroupListBonita=[organization_visualization, organization_management] custompage_adminRoleListBonita=[organization_visualization, organization_management] custompage_adminInstallExportOrganizationBonita=[form_file_upload, organization_management, tenant_platform_visualization] custompage_adminProfileListBonita=[organization_visualization, profile_management, profile_member_management, profile_member_visualization, profile_visualization, tenant_platform_visualization] custompage_adminBDMBonita=[bdm_access_control, bdm_management, bdm_visualization, organization_visualization, tenant_platform_visualization] custompage_adminResourceListBonita=[application_visualization, form_visualization, page_management, profile_management, profile_visualization] custompage_adminApplicationListBonita=[application_management, application_visualization, organization_visualization, profile_visualization, tenant_platform_visualization] custompage_adminApplicationDetailsBonita=[application_management, application_visualization, organization_visualization, page_management, profile_visualization, tenant_platform_visualization] custompage_adminLicenseBonita=[tenant_platform_visualization] custompage_adminDataRetentionBonita=[data_retention_management, tenant_platform_visualization] custompage_tenantStatusBonita=[license, tenant_platform_management, tenant_platform_visualization] custompage_applicationDirectoryBonita=[application_visualization, organization_visualization, tenant_platform_visualization] custompage_home=[] custompage_error404Bonita=[] custompage_error500Bonita=[] ================================================ FILE: platform/platform-resources/src/main/resources/tenant_portal/console-config.properties ================================================ #This value represents MB, e.g 25 means 25MB. form.attachment.max.size 25 #This value represents KB, e.g 100 means 100KB. image.upload.max.size 100 #Set this value to true to reload the Index.groovy class every time a custom page is displayed custom.page.debug false #Min time (in millis) between two database check of custom pages and REST API extensions last update date #if the page has been modified in the database it is retrieved locally, but this property is used to avoid doing the database check at each HTTP request #the value can be increased to improve page or REST API extension loading/response time if live update is not used custom.page.lastupdate.database.check.interval.milliseconds 3000 #Name of attribute attached to the request to indicate the request id req.requestId.attributeName track.requestId #Name of request header containing the request id when not already attached req.requestId.headerName X-Request-ID #Name of attribute attached to the request to indicate the correlation id req.correlationId.attributeName track.correlationId #Name of request header containing the correlation id when not already attached req.correlationId.headerName X-Correlation-ID ================================================ FILE: platform/platform-resources/src/main/resources/tenant_portal/custom-permissions-mapping.properties ================================================ ## # Custom permissions file # # custom permissions can be defined like this # |=[] # # type can be 'profile' or 'user' # identifier is the username or the profile name. Special characters like white space must be replaced with their unicode value (For example \u0020 for the white space) # possible values for permissions can be found in the resources-permissions-mapping.properties file # ## # example: the profile User have now the permission Organization visualization #profile|User=[organization_visualization] #profile|Process\u0020manager=[organization_visualization] # # example: the user having username john have now the permission Organization management and Organization visualization #user|john=[organization_management, organization_visualization] ================================================ FILE: platform/platform-resources/src/main/resources/tenant_portal/resources-permissions-mapping-custom.properties ================================================ ## # Define which permissions are needed to access resources using the REST API # Resource permissions can be defined like this # |//=[] # # verb can be 'GET', 'POST', 'PUT' or 'DELETE' # ## # example of a default resource permission: # GET|identity/user=[organization_visualization] # It means that in order to GET user information, the logged user needs to have the organization_visualization permission. # # example of a custom resource permission: # GET|identity/user/3=[my_custom_permission] # It means that in order to GET the information of the user with id 3, the logged user needs to have my_custom_permission. ## # Default resource permissions can be found in file 'resources-permissions-mapping.properties' # # Add you custom resource permissions below: ================================================ FILE: platform/platform-resources/src/main/resources/tenant_portal/resources-permissions-mapping-internal.properties ================================================ # internal use only. Do not modify manually ================================================ FILE: platform/platform-resources/src/main/resources/tenant_portal/resources-permissions-mapping.properties ================================================ ## # Define which permissions allow to access a resources using the REST API # Resource permissions can be defined like this # |//=[] # # verb can be 'GET', 'POST', 'PUT' or 'DELETE' # ## # example of a default resource permission: # GET|identity/user=[organization_visualization] # It means that in order to GET user information, the logged user needs to have the organization_visualization permission. # # example of a custom resource permission: # GET|identity/user/3=[my_custom_permission] # It means that in order to GET the information of the user with id 3, the logged user needs to have my_custom_permission. ## # Identity resources GET|identity/user=[organization_visualization] POST|identity/user=[organization_management] PUT|identity/user=[organization_management] DELETE|identity/user=[organization_management] GET|identity/personalcontactdata=[organization_visualization] POST|identity/personalcontactdata=[organization_management] PUT|identity/personalcontactdata=[organization_management] GET|identity/professionalcontactdata=[organization_visualization] POST|identity/professionalcontactdata=[organization_management] PUT|identity/professionalcontactdata=[organization_management] GET|identity/role=[organization_visualization] POST|identity/role=[organization_management] PUT|identity/role=[organization_management] DELETE|identity/role=[organization_management] GET|identity/group=[organization_visualization] POST|identity/group=[organization_management] PUT|identity/group=[organization_management] DELETE|identity/group=[organization_management] GET|identity/membership=[organization_visualization] POST|identity/membership=[organization_management] PUT|identity/membership=[organization_management] DELETE|identity/membership=[organization_management] GET|customuserinfo/user=[organization_visualization] GET|customuserinfo/definition=[organization_visualization] POST|customuserinfo/definition=[organization_management] DELETE|customuserinfo/definition=[organization_management] GET|customuserinfo/value=[organization_visualization] PUT|customuserinfo/value=[organization_management] # BPM resources GET|bpm/process=[process_visualization] POST|bpm/process=[process_deploy] PUT|bpm/process=[process_management, process_enablement] DELETE|bpm/process=[process_deploy] GET|bpm/processInfo=[process_management] GET|bpm/process/*/contract=[process_visualization] POST|bpm/process/*/instantiation=[case_start] GET|bpm/processConnector=[process_management, connector_visualization] PUT|bpm/processConnector=[process_management, connector_management] GET|bpm/processConnectorDependency=[process_management, connector_visualization] POST|bpm/processCategory=[process_management, process_categories_management] DELETE|bpm/processCategory=[process_management, process_categories_management] GET|bpm/processParameter=[process_management] PUT|bpm/processParameter=[process_management] GET|bpm/processSupervisor=[process_manager_visualization] POST|bpm/processSupervisor=[process_manager_management] DELETE|bpm/processSupervisor=[process_manager_management] GET|bpm/actor=[process_visualization] GET|bpm/actorMember=[process_visualization, process_manager_visualization] POST|bpm/actorMember=[process_management, process_manager_management] PUT|bpm/actorMember=[process_management, process_manager_management] DELETE|bpm/actorMember=[process_management, process_manager_management] GET|bpm/category=[process_categories_visualization] POST|bpm/category=[process_categories_management] PUT|bpm/category=[process_categories_management] DELETE|bpm/category=[process_categories_management] GET|bpm/processResolutionProblem=[activity_visualization, flownode_visualization] GET|bpm/case=[case_visualization] PUT|bpm/case=[case_management] POST|bpm/case=[case_start] DELETE|bpm/case=[case_delete] GET|bpm/case/*/context=[case_visualization] GET|bpm/caseInfo=[case_visualization] GET|bpm/comment=[process_comment] POST|bpm/comment=[process_comment] GET|bpm/archivedComment=[process_comment] GET|bpm/archivedCase=[case_visualization] DELETE|bpm/archivedCase=[case_delete] GET|bpm/archivedCase/*/context=[case_visualization] GET|bpm/caseVariable=[case_visualization] PUT|bpm/caseVariable=[case_management] GET|bpm/archivedCaseVariable=[case_visualization] GET|bpm/caseDocument=[document_visualization, case_visualization] POST|bpm/caseDocument=[document_management, case_management] DELETE|bpm/caseDocument=[document_management, case_management] PUT|bpm/caseDocument=[document_management, case_management] GET|bpm/flowNode=[flownode_visualization] PUT|bpm/flowNode=[flownode_management] GET|bpm/activity=[flownode_visualization] PUT|bpm/activity=[flownode_management] PUT|bpm/activityReplay=[flownode_management] GET|bpm/task=[flownode_visualization] PUT|bpm/task=[flownode_management] GET|bpm/humanTask=[task_visualization] PUT|bpm/humanTask=[task_management] GET|bpm/userTask=[task_visualization] PUT|bpm/userTask=[task_management] POST|bpm/userTask=[task_management] GET|bpm/userTask/*/contract=[task_visualization] GET|bpm/userTask/*/context=[task_visualization] POST|bpm/userTask/*/execution=[task_management] GET|bpm/manualTask=[task_visualization] POST|bpm/manualTask=[task_management] PUT|bpm/manualTask=[task_management] GET|bpm/activityVariable=[flownode_visualization] GET|bpm/archivedActivityVariable=[flownode_visualization] GET|bpm/connectorInstance=[connector_visualization] PUT|bpm/connectorInstance=[connector_management] GET|bpm/archivedFlowNode=[flownode_visualization] GET|bpm/archivedActivity=[flownode_visualization] GET|bpm/archivedTask=[flownode_visualization] GET|bpm/archivedHumanTask=[task_visualization] GET|bpm/archivedUserTask=[task_visualization] GET|bpm/archivedUserTask/*/context=[task_visualization] GET|bpm/archivedManualTask=[task_visualization] GET|bpm/archivedConnectorInstance=[connector_visualization] GET|bpm/document=[document_visualization, case_visualization] POST|bpm/document=[document_management, case_management] PUT|bpm/document=[document_management, case_management] DELETE|bpm/document=[document_management, case_management] GET|bpm/archiveddocument=[document_visualization, case_visualization] GET|bpm/archivedCaseDocument=[document_visualization, case_visualization] DELETE|bpm/archivedCaseDocument=[document_management, case_management] GET|bpm/command=[command_visualization] POST|bpm/command=[command_management] PUT|bpm/command=[command_management] DELETE|bpm/command=[command_management] GET|bpm/connectorFailure=[connector_visualization] GET|bpm/timerEventTrigger=[flownode_visualization] PUT|bpm/timerEventTrigger=[flownode_management] GET|bpm/diagram=[process_visualization] POST|bpm/message=[flownode_management] POST|bpm/signal=[flownode_management] GET|bpm/failure=[flownode_management, case_management] GET|bpm/archivedFailure=[flownode_management, case_management] # Portal resources GET|portal/profile=[profile_visualization] POST|portal/profile=[profile_management] PUT|portal/profile=[profile_management] DELETE|portal/profile=[profile_management] GET|portal/page=[profile_visualization, page_management] POST|portal/page=[profile_management, page_management] PUT|portal/page=[profile_management, page_management] DELETE|portal/page=[profile_management, page_management] GET|portal/profileMember=[profile_member_visualization] POST|portal/profileMember=[profile_member_management] DELETE|portal/profileMember=[profile_member_management] # Platform resources GET|system/session=[tenant_platform_visualization] GET|system/log=[tenant_platform_visualization] GET|system/maintenance=[tenant_platform_visualization] PUT|system/maintenance=[tenant_platform_management] GET|system/feature=[tenant_platform_visualization] GET|system/license=[license] GET|system/monitoring=[tenant_platform_visualization] GET|system/i18nlocale=[tenant_platform_visualization] GET|system/i18ntranslation=[tenant_platform_visualization] GET|platform/platform=[tenant_platform_visualization] POST|platform/platform=[tenant_platform_management] PUT|platform/platform=[tenant_platform_management] DELETE|platform/platform=[tenant_platform_management] GET|platform/jvmDynamic=[tenant_platform_visualization] GET|platform/jvmStatic=[tenant_platform_visualization] GET|platform/systemProperty=[tenant_platform_visualization] GET|platform/license=[platform_management] POST|tenant/bdm=[bdm_management] GET|tenant/bdm=[bdm_management] GET|system/information=[tenant_platform_visualization] # Living apps GET|living/application=[application_visualization] POST|living/application=[application_management] PUT|living/application=[application_management] DELETE|living/application=[application_management] GET|living/application-page=[application_visualization] POST|living/application-page=[application_management] PUT|living/application-page=[application_management] DELETE|living/application-page=[application_management] GET|living/application-menu=[application_visualization] POST|living/application-menu=[application_management] PUT|living/application-menu=[application_management] DELETE|living/application-menu=[application_management] # BDM resources GET|bdm/businessData=[bdm_visualization] POST|bdm/businessData=[bdm_management] DELETE|bdm/businessData=[bdm_management] PUT|bdm/businessData=[bdm_management] GET|bdm/businessDataReference=[bdm_visualization] GET|bdm/businessDataQuery=[bdm_visualization] # BDM Access control resources GET|accessControl/bdm=[bdm_access_control] DELETE|accessControl/bdm=[bdm_access_control] # Retention resources GET|retention/object=[data_retention_management] GET|retention/schedule=[data_retention_management] POST|retention/rule=[data_retention_management] PUT|retention/rule=[data_retention_management] DELETE|retention/rule=[data_retention_management] # Delegation resources GET|delegation/rule=[task_delegation_management] POST|delegation/rule=[task_delegation_management] PUT|delegation/rule=[task_delegation_management] DELETE|delegation/rule=[task_delegation_management] GET|delegation/task=[task_delegation_visualization] # Form resources GET|form/mapping=[form_visualization] PUT|form/mapping=[form_management] # Servlets POST|API/formFileUpload=[form_file_upload] POST|portal/custom-page/API/formFileUpload=[form_file_upload] POST|API/imageUpload=[organization_management] POST|API/pageUpload=[profile_management] POST|API/processUpload=[process_deploy] POST|API/profilesUpload=[profile_management] POST|portal/fileUpload=[form_file_upload] POST|portal/processUpload=[process_deploy] POST|portal/organizationUpload=[organization_management] POST|portal/actorsUpload=[process_management] POST|portal/profilesUpload=[profile_management] POST|portal/applicationsUpload=[application_management] POST|portal/bdmUpload=[bdm_management] POST|portal/bdmAccessControlUpload=[bdm_access_control] POST|portal/connectorImplementation=[process_management] POST|portal/pageUpload=[profile_management] POST|portal/resourceUpload=[tenant_platform_management] POST|portal/imageUpload=[organization_management] GET|API/avatars=[avatars] GET|portal/custom-page/API/avatars=[avatars] DELETE|API/avatars=[organization_management] DELETE|portal/custom-page/API/avatars=[organization_management] GET|API/documentDownload=[download_document] GET|portal/custom-page/API/documentDownload=[download_document] GET|portal/documentDownload=[download_document] GET|API/formsDocumentImage=[download_document] GET|portal/custom-page/API/formsDocumentImage=[download_document] GET|portal/formsDocumentImage=[download_document] GET|portal/custom-page/API/formsDocumentDownload=[download_document] GET|portal/formsDocumentDownload=[download_document] GET|portal/exportOrganization=[organization_management] GET|API/exportOrganization=[organization_management] GET|portal/custom-page/API/exportOrganization=[organization_management] GET|portal/pageDownload=[profile_management] GET|API/pageDownload=[profile_management] GET|portal/exportProfiles=[profile_management] GET|API/exportProfiles=[profile_management] GET|portal/exportAccessControl=[bdm_access_control] GET|API/applicationIcon=[application_visualization] DELETE|API/applicationIcon=[application_management] # Deprecated GET|portal/downloadDocument=[download_document] GET|portal/custom-page/API/downloadDocument=[download_document] # Services POST|application/import=[application_management] POST|organization/import=[organization_management] POST|bpm/process/importActors=[process_actor_mapping_management] POST|profile/import=[profile_management] POST|bdmAccessControl/install=[bdm_access_control] POST|bdmAccessControl/validation=[bdm_access_control] # api extension examples GET|extension/demo/getExample=[demoPermission] GET|extension/demo/headerExample=[demoPermission] GET|extension/demo/logExample=[demoPermission] GET|extension/demo/soapExample=[demoPermission] GET|extension/demo/xmlExample=[demoPermission] POST|extension/demo/postExample=[demoPermission] ================================================ FILE: platform/platform-resources/src/main/resources/tenant_portal/security-config.properties ================================================ #It declares a Password Validator class, the default value can be changed according to your needs security.password.validator org.bonitasoft.web.rest.server.api.organization.password.validator.DefaultPasswordValidator #Setting this value to false will deactivate the permissions checks on the REST API security.rest.api.authorizations.check.enabled true ================================================ FILE: platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/impl/BonitaConfigurationRowMapperTest.java ================================================ /** * 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.platform.configuration.impl; import static org.bonitasoft.platform.configuration.impl.ConfigurationFields.RESOURCE_CONTENT; import static org.bonitasoft.platform.configuration.impl.ConfigurationFields.RESOURCE_NAME; import static org.bonitasoft.platform.configuration.model.BonitaConfigurationAssert.assertThat; import java.sql.ResultSet; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; /** * @author Laurent Leseigneur */ @RunWith(MockitoJUnitRunner.class) public class BonitaConfigurationRowMapperTest { @Mock private ResultSet rs; @Before public void setup() throws Exception { Mockito.doReturn("my resource").when(rs).getString(RESOURCE_NAME); Mockito.doReturn("my content".getBytes()).when(rs).getBytes(RESOURCE_CONTENT); } @Test public void testMapRow() throws Exception { //given BonitaConfigurationRowMapper bonitaConfigurationRowMapper = new BonitaConfigurationRowMapper(); //when final BonitaConfiguration bonitaConfiguration = bonitaConfigurationRowMapper.mapRow(rs, 5); //then assertThat(bonitaConfiguration) .hasResourceName("my resource") .hasResourceContent("my content".getBytes()); } } ================================================ FILE: platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/impl/ConfigurationServiceImplTest.java ================================================ /** * 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.platform.configuration.impl; import static org.mockito.Mockito.*; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.assertj.core.api.Assertions; import org.bonitasoft.platform.configuration.model.FullBonitaConfiguration; import org.bonitasoft.platform.configuration.type.ConfigurationType; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author laurent Leseigneur */ @RunWith(MockitoJUnitRunner.class) public class ConfigurationServiceImplTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Rule public ExpectedException expectedException = ExpectedException.none(); @InjectMocks @Spy ConfigurationServiceImpl configurationService; @Test public void getPlatformPortalConf_should_call_query_for_PLATFORM_PORTAL_type() { doReturn(Collections.EMPTY_LIST).when(configurationService) .getBonitaConfigurations(ArgumentMatchers.any(ConfigurationType.class)); configurationService.getPlatformPortalConf(); verify(configurationService).getBonitaConfigurations(ConfigurationType.PLATFORM_PORTAL); } @Test public void getPlatformEngineConf_should_call_query_for_PLATFORM_ENGINE_type() { doReturn(Collections.EMPTY_LIST).when(configurationService) .getBonitaConfigurations(ArgumentMatchers.any(ConfigurationType.class)); configurationService.getPlatformEngineConf(); verify(configurationService).getBonitaConfigurations(ConfigurationType.PLATFORM_ENGINE); } @Test public void getLicenses_should_call_query_for_LICENSES_type() { doReturn(Collections.EMPTY_LIST).when(configurationService) .getBonitaConfigurations(ArgumentMatchers.any(ConfigurationType.class)); configurationService.getLicenses(); verify(configurationService).getBonitaConfigurations(ConfigurationType.LICENSES); } @Test public void should_write_file_within_sub_folder() throws Exception { //given final File configFolder = temporaryFolder.newFolder("conf"); final File licFolder = temporaryFolder.newFolder("lic"); List confs = new ArrayList<>(); confs.add(new FullBonitaConfiguration("conf1.properties", "content 1".getBytes(), "PLATFORM_TYPE")); confs.add(new FullBonitaConfiguration("conf2.properties", "content 2".getBytes(), "TENANT_TYPE")); doReturn(confs).when(configurationService).getAllConfiguration(); doCallRealMethod().when(configurationService).writeAllConfigurationToFolder(configFolder, licFolder); //when configurationService.writeAllConfigurationToFolder(configFolder, licFolder); // then Assertions.assertThat(configFolder.toPath().resolve("platform_type").resolve("conf1.properties").toFile()) .as("should lowercase configuration type").exists(); Assertions .assertThat(configFolder.toPath().resolve("tenant_type").resolve("conf2.properties").toFile()) .as("should create 'tenant_type' sub folder").exists(); } } ================================================ FILE: platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/impl/FolderResolverTest.java ================================================ /** * 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.platform.configuration.impl; import java.nio.file.Path; import org.assertj.core.api.Assertions; import org.bonitasoft.platform.configuration.model.FullBonitaConfiguration; import org.bonitasoft.platform.configuration.type.ConfigurationType; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; /** * @author Laurent Leseigneur */ public class FolderResolverTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void should_resolve_license_folder() throws Exception { //given Path confFolder = temporaryFolder.newFolder("configuration").toPath(); Path LicFolder = temporaryFolder.newFolder("licenses").toPath(); FolderResolver folderResolver = new FolderResolver(confFolder, LicFolder); FullBonitaConfiguration fullBonitaConfiguration = new FullBonitaConfiguration("licence1.lic", "license content".getBytes(), ConfigurationType.LICENSES.name()); //then Assertions.assertThat(folderResolver.getFolder(fullBonitaConfiguration)).isEqualTo(LicFolder.toFile()); } @Test public void should_resolve_non_tenant_folder() throws Exception { //given Path confFolder = temporaryFolder.newFolder("configuration").toPath(); Path LicFolder = temporaryFolder.newFolder("licenses").toPath(); FolderResolver folderResolver = new FolderResolver(confFolder, LicFolder); FullBonitaConfiguration fullBonitaConfiguration = new FullBonitaConfiguration("conf.properties", "key=value".getBytes(), ConfigurationType.PLATFORM_ENGINE.name()); //then Assertions.assertThat(folderResolver.getFolder(fullBonitaConfiguration)) .isEqualTo(confFolder.resolve("platform_engine").toFile()); } @Test public void should_resolve_tenant_folder() throws Exception { //given Path confFolder = temporaryFolder.newFolder("configuration").toPath(); Path LicFolder = temporaryFolder.newFolder("licenses").toPath(); FolderResolver folderResolver = new FolderResolver(confFolder, LicFolder); FullBonitaConfiguration fullBonitaConfiguration = new FullBonitaConfiguration("conf.properties", "key=value".getBytes(), ConfigurationType.TENANT_PORTAL.name()); //then Assertions.assertThat(folderResolver.getFolder(fullBonitaConfiguration)) .isEqualTo(confFolder.resolve("tenant_portal").toFile()); } } ================================================ FILE: platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/model/BonitaConfigurationAssert.java ================================================ /** * 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.platform.configuration.model; import org.assertj.core.api.AbstractAssert; import org.assertj.core.api.Assertions; import org.assertj.core.util.Objects; /** * {@link BonitaConfiguration} specific assertions - Generated by CustomAssertionGenerator. */ public class BonitaConfigurationAssert extends AbstractAssert { /** * Creates a new {@link BonitaConfigurationAssert} to make assertions on actual BonitaConfiguration. * * @param actual the BonitaConfiguration we want to make assertions on. */ public BonitaConfigurationAssert(BonitaConfiguration actual) { super(actual, BonitaConfigurationAssert.class); } /** * An entry point for BonitaConfigurationAssert to follow AssertJ standard assertThat() statements.
      * With a static import, one can write directly: assertThat(myBonitaConfiguration) and get specific * assertion with code completion. * * @param actual the BonitaConfiguration we want to make assertions on. * @return a new {@link BonitaConfigurationAssert} */ public static BonitaConfigurationAssert assertThat(BonitaConfiguration actual) { return new BonitaConfigurationAssert(actual); } /** * Verifies that the actual BonitaConfiguration's resourceContent contains the given byte elements. * * @param resourceContent the given elements that should be contained in actual BonitaConfiguration's * resourceContent. * @return this assertion object. * @throws AssertionError if the actual BonitaConfiguration's resourceContent does not contain all given byte * elements. */ public BonitaConfigurationAssert hasResourceContent(byte... resourceContent) { // check that actual BonitaConfiguration we want to make assertions on is not null. isNotNull(); // check that given byte varargs is not null. if (resourceContent == null) failWithMessage("Expecting resourceContent parameter not to be null."); // check with standard error message (use overridingErrorMessage before contains to set your own message). Assertions.assertThat(actual.getResourceContent()).contains(resourceContent); // return the current assertion for method chaining return this; } /** * Verifies that the actual BonitaConfiguration's resourceContent contains only the given byte elements and * nothing else in whatever order. * * @param resourceContent the given elements that should be contained in actual BonitaConfiguration's * resourceContent. * @return this assertion object. * @throws AssertionError if the actual BonitaConfiguration's resourceContent does not contain all given byte * elements and nothing else. */ public BonitaConfigurationAssert hasOnlyResourceContent(byte... resourceContent) { // check that actual BonitaConfiguration we want to make assertions on is not null. isNotNull(); // check that given byte varargs is not null. if (resourceContent == null) failWithMessage("Expecting resourceContent parameter not to be null."); // check with standard error message (use overridingErrorMessage before contains to set your own message). Assertions.assertThat(actual.getResourceContent()).containsOnly(resourceContent); // return the current assertion for method chaining return this; } /** * Verifies that the actual BonitaConfiguration's resourceContent does not contain the given byte elements. * * @param resourceContent the given elements that should not be in actual BonitaConfiguration's resourceContent. * @return this assertion object. * @throws AssertionError if the actual BonitaConfiguration's resourceContent contains any given byte elements. */ public BonitaConfigurationAssert doesNotHaveResourceContent(byte... resourceContent) { // check that actual BonitaConfiguration we want to make assertions on is not null. isNotNull(); // check that given byte varargs is not null. if (resourceContent == null) failWithMessage("Expecting resourceContent parameter not to be null."); // check with standard error message (use overridingErrorMessage before contains to set your own message). Assertions.assertThat(actual.getResourceContent()).doesNotContain(resourceContent); // return the current assertion for method chaining return this; } /** * Verifies that the actual BonitaConfiguration has no resourceContent. * * @return this assertion object. * @throws AssertionError if the actual BonitaConfiguration's resourceContent is not empty. */ public BonitaConfigurationAssert hasNoResourceContent() { // check that actual BonitaConfiguration we want to make assertions on is not null. isNotNull(); // we override the default error message with a more explicit one String assertjErrorMessage = "\nExpecting :\n <%s>\nnot to have resourceContent but had :\n <%s>"; // check if (actual.getResourceContent().length > 0) { failWithMessage(assertjErrorMessage, actual, java.util.Arrays.toString(actual.getResourceContent())); } // return the current assertion for method chaining return this; } /** * Verifies that the actual BonitaConfiguration's resourceName is equal to the given one. * * @param resourceName the given resourceName to compare the actual BonitaConfiguration's resourceName to. * @return this assertion object. * @throws AssertionError - if the actual BonitaConfiguration's resourceName is not equal to the given one. */ public BonitaConfigurationAssert hasResourceName(String resourceName) { // check that actual BonitaConfiguration we want to make assertions on is not null. isNotNull(); // overrides the default error message with a more explicit one String assertjErrorMessage = "\nExpecting resourceName of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>"; // null safe check String actualResourceName = actual.getResourceName(); if (!Objects.areEqual(actualResourceName, resourceName)) { failWithMessage(assertjErrorMessage, actual, resourceName, actualResourceName); } // return the current assertion for method chaining return this; } } ================================================ FILE: platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/model/BonitaConfigurationTest.java ================================================ /** * 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.platform.configuration.model; import org.junit.Test; /** * @author Laurent Leseigneur */ public class BonitaConfigurationTest { @Test public void should_build_configuration() { //given BonitaConfiguration bonitaConfiguration = new BonitaConfiguration("my resource", "my content".getBytes()); //then BonitaConfigurationAssert.assertThat(bonitaConfiguration) .hasResourceName("my resource") .hasResourceContent("my content".getBytes()); } } ================================================ FILE: platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/model/FullBonitaConfigurationTest.java ================================================ /** * 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.platform.configuration.model; import static org.bonitasoft.platform.configuration.type.ConfigurationType.*; import java.util.Arrays; import java.util.List; import org.assertj.core.api.Assertions; import org.bonitasoft.platform.configuration.type.ConfigurationType; import org.junit.Test; /** * @author Laurent Leseigneur */ public class FullBonitaConfigurationTest { @Test public void should_have_readable_toString() { //given FullBonitaConfiguration fullBonitaConfiguration = new FullBonitaConfiguration("resourceName", "content".getBytes(), "type"); //then Assertions .assertThat(fullBonitaConfiguration.toString()) .isEqualTo( "FullBonitaConfiguration{ resourceName='resourceName' , configurationType='type' }"); } @Test public void should_be_a_licence_file() { //given FullBonitaConfiguration fullBonitaConfiguration = new FullBonitaConfiguration("resourceName", "content".getBytes(), LICENSES.name()); //then Assertions.assertThat(fullBonitaConfiguration.isLicenseFile()).isTrue(); } @Test public void should_not_be_a_licence_file() { //given final List allExceptLicense = Arrays.asList(PLATFORM_PORTAL, PLATFORM_ENGINE, TENANT_PORTAL, TENANT_ENGINE, TENANT_ENGINE, TENANT_SECURITY_SCRIPTS, TENANT_SECURITY_SCRIPTS, TENANT_PORTAL); for (ConfigurationType configurationType : allExceptLicense) { //when FullBonitaConfiguration fullBonitaConfiguration = new FullBonitaConfiguration("resourceName", "content".getBytes(), configurationType.name()); //then Assertions.assertThat(fullBonitaConfiguration.isLicenseFile()).isFalse(); } } } ================================================ FILE: platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/util/AllConfigurationResourceVisitorTest.java ================================================ /** * 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.platform.configuration.util; import static org.assertj.core.api.Assertions.assertThat; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import org.bonitasoft.platform.configuration.model.FullBonitaConfiguration; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Laurent Leseigneur */ public class AllConfigurationResourceVisitorTest { private final static Logger LOGGER = LoggerFactory.getLogger(ConfigurationResourceVisitor.class); @Test public void should_store_files_from_configuration_folder() throws Exception { //given Path rootFolder = Paths.get(getClass().getResource("/allConfiguration").toURI()); LOGGER.info("folder:" + rootFolder); final List bonitaConfigurations = new ArrayList<>(); //when final AllConfigurationResourceVisitor resourceVisitor = new AllConfigurationResourceVisitor( bonitaConfigurations); Files.walkFileTree(rootFolder, resourceVisitor); //then assertThat(bonitaConfigurations).hasSize(5); assertThat(bonitaConfigurations).as("should visit all configuration folders") .extracting("configurationType") .containsOnly("TENANT_ENGINE", "TENANT_PORTAL", "PLATFORM_PORTAL", "PLATFORM_ENGINE", "TENANT_SECURITY_SCRIPTS"); assertThat(bonitaConfigurations).as("should add all configuration files and skip licenses") .extracting("resourceName") .containsOnly("security-config.properties", "compound-permissions-mapping.properties", "SamplePermissionRule.groovy.sample", "bonita-tenant-community.properties", "bonita-platform-community.properties"); } } ================================================ FILE: platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/util/AutoUpdateConfigurationVisitorTest.java ================================================ /** * 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.platform.configuration.util; import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import org.apache.commons.io.FileUtils; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; /** * @author Emmanuel Duchastenier */ public class AutoUpdateConfigurationVisitorTest { private AutoUpdateConfigurationVisitor autoUpdateVisitor = new AutoUpdateConfigurationVisitor(null); private static Path initial; @BeforeClass public static void createFolders() throws IOException { initial = Paths.get(System.getProperty("java.io.tmpdir")).resolve("initial"); Files.createDirectories(initial); } @AfterClass public static void deleteFolders() throws IOException { FileUtils.deleteDirectory(initial.toFile()); } @Test public void isAutoUpdateConfigurationFile_should_return_true_for_compound_permissions_mapping() throws Exception { // given: final Path path = initial.resolve("compound-permissions-mapping.properties"); Files.createFile(path); // when: final boolean autoUpdateConfigurationFile = autoUpdateVisitor.isAutoUpdateConfigurationFile(path); // then: assertThat(autoUpdateConfigurationFile).isTrue(); } @Test public void isAutoUpdateConfigurationFile_should_return_true_for_dynamic_permissions_checks() throws Exception { // given: final Path path = initial.resolve("dynamic-permissions-checks.properties"); Files.createFile(path); // when: final boolean autoUpdateConfigurationFile = autoUpdateVisitor.isAutoUpdateConfigurationFile(path); // then: assertThat(autoUpdateConfigurationFile).isTrue(); } @Test public void isAutoUpdateConfigurationFile_should_return_true_for_resources_permissions_mapping() throws Exception { // given: final Path path = initial.resolve("resources-permissions-mapping.properties"); Files.createFile(path); // when: final boolean autoUpdateConfigurationFile = autoUpdateVisitor.isAutoUpdateConfigurationFile(path); // then: assertThat(autoUpdateConfigurationFile).isTrue(); } @Test public void isAutoUpdateConfigurationFile_should_return_true_for_user_creation_attribute_mapping() throws Exception { // given: final Path path = initial.resolve("user-creation-attribute-mapping.properties"); Files.createFile(path); // when: final boolean autoUpdateConfigurationFile = autoUpdateVisitor.isAutoUpdateConfigurationFile(path); // then: assertThat(autoUpdateConfigurationFile).isTrue(); } @Test public void isAutoUpdateConfigurationFile_should_return_false_for_other_file() throws Exception { // given: final Path path = initial.resolve("wrong_file_name.lst"); Files.createFile(path); // when: final boolean autoUpdateConfigurationFile = autoUpdateVisitor.isAutoUpdateConfigurationFile(path); // then: assertThat(autoUpdateConfigurationFile).isFalse(); } @Test public void isAutoUpdateConfigurationFile_should_return_false_for_non_existing_file() throws Exception { // given: final Path path = initial.resolve("non-existing-folder/resources-permissions-mapping.properties"); // when: final boolean autoUpdateConfigurationFile = autoUpdateVisitor.isAutoUpdateConfigurationFile(path); // then: assertThat(autoUpdateConfigurationFile).isFalse(); } } ================================================ FILE: platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/util/ConfigurationResourceVisitorTest.java ================================================ /** * 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.platform.configuration.util; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import org.assertj.core.api.Assertions; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Laurent Leseigneur */ public class ConfigurationResourceVisitorTest { public static final int CURRENT_NUMBER_OF_CONFIGURATION_FILES = 2; private final static Logger LOGGER = LoggerFactory.getLogger(ConfigurationResourceVisitor.class); @Test public void should_read_configuration_folder() throws Exception { //given Path rootFolder = Paths.get(getClass().getResource("/conf").toURI()); LOGGER.error("folder:" + rootFolder); final List bonitaConfigurations = new ArrayList<>(); //when final ConfigurationResourceVisitor resourceVisitor = new ConfigurationResourceVisitor(bonitaConfigurations); Files.walkFileTree(rootFolder, resourceVisitor); //then Assertions.assertThat(bonitaConfigurations).hasSize(CURRENT_NUMBER_OF_CONFIGURATION_FILES); } } ================================================ FILE: platform/platform-resources/src/test/java/org/bonitasoft/platform/configuration/util/LicensesResourceVisitorTest.java ================================================ /** * 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.platform.configuration.util; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import org.assertj.core.api.Assertions; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; /** * @author Laurent Leseigneur */ public class LicensesResourceVisitorTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void should_read_licenses_folder() throws Exception { //given Path licenseFolder = temporaryFolder.newFolder().toPath(); Files.createDirectories(licenseFolder.resolve("subFolder")); Files.write(licenseFolder.resolve("license1.lic"), "license 1 content".getBytes()); Files.write(licenseFolder.resolve("license2.lic"), "license 2 content".getBytes()); Files.write(licenseFolder.resolve("not_a_license"), "this is not a license".getBytes()); Files.write(licenseFolder.resolve("subFolder").resolve("ignoreMe.lic"), "this is an ignored license".getBytes()); final List bonitaConfigurations = new ArrayList<>(); BonitaConfiguration expectedLicense1 = new BonitaConfiguration("license1.lic", "license 1 content".getBytes()); BonitaConfiguration expectedLicense2 = new BonitaConfiguration("license2.lic", "license 2 content".getBytes()); //when final LicensesResourceVisitor resourceVisitor = new LicensesResourceVisitor(bonitaConfigurations); Files.walkFileTree(licenseFolder, resourceVisitor); //then Assertions.assertThat(bonitaConfigurations).containsOnly(expectedLicense1, expectedLicense2); } } ================================================ FILE: platform/platform-resources/src/test/java/org/bonitasoft/platform/version/impl/VersionServiceImplTest.java ================================================ /** * 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.platform.version.impl; import java.util.List; import org.assertj.core.api.Assertions; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.jdbc.core.JdbcTemplate; /** * @author laurent Leseigneur */ @RunWith(MockitoJUnitRunner.class) public class VersionServiceImplTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Rule public ExpectedException expectedException = ExpectedException.none(); @Mock JdbcTemplate jdbcTemplate; @InjectMocks @Spy VersionServiceImpl versionService; @Test public void should_return_platform_database_version() throws Exception { //given Mockito.doReturn(List.of("a.b.c")).when(jdbcTemplate).queryForList(ArgumentMatchers.anyString(), ArgumentMatchers.eq(String.class)); //when final String platformVersion = versionService.retrieveDatabaseSchemaVersion(); //then Assertions.assertThat(platformVersion).as("should return same version").isEqualTo("a.b.c"); } } ================================================ FILE: platform/platform-resources/src/test/resources/allConfiguration/licenses/license1.lic ================================================ license content ================================================ FILE: platform/platform-resources/src/test/resources/allConfiguration/platform_engine/bonita-platform-community.properties ================================================ #content not used for testing ================================================ FILE: platform/platform-resources/src/test/resources/allConfiguration/platform_portal/security-config.properties ================================================ # content not used for testing ================================================ FILE: platform/platform-resources/src/test/resources/allConfiguration/tenant_template_engine/bonita-tenant-community.properties ================================================ #content not used for testing ================================================ FILE: platform/platform-resources/src/test/resources/allConfiguration/tenant_template_security_scripts/SamplePermissionRule.groovy.sample ================================================ //content not used for testing ================================================ FILE: platform/platform-resources/src/test/resources/allConfiguration/tenants/456/tenant_engine/bonita-tenant-community.properties ================================================ #content not used for testing ================================================ FILE: platform/platform-resources/src/test/resources/allConfiguration/tenants/456/tenant_portal/compound-permissions-mapping.properties ================================================ #content not used for testing ================================================ FILE: platform/platform-resources/src/test/resources/allConfiguration/tenants/456/tenant_security_scripts/SamplePermissionRule.groovy.sample ================================================ //content not used for testing ================================================ FILE: platform/platform-resources/src/test/resources/conf/bonita-platform-community.properties ================================================ #content not used for testing ================================================ FILE: platform/platform-resources/src/test/resources/conf/bonita-platform-custom.xml ================================================ ================================================ FILE: platform/platform-setup/.gitignore ================================================ # this bin folder is useful for tests: !bin ================================================ FILE: platform/platform-setup/bin/.gitignore ================================================ /main/ /test/ ================================================ FILE: platform/platform-setup/build.gradle ================================================ import org.apache.tools.ant.filters.ReplaceTokens import org.bonitasoft.engine.gradle.PomUtils import org.bonitasoft.engine.gradle.SetupE2ETask plugins { id 'distribution' id 'bonita-tests' id 'bonita-docker-database' } configurations { distributionZip inDistrib } dependencies { api project(':platform:platform-resources') api libs.commonsText api libs.slf4jApi api(libs.springBootStarter) { exclude(module: 'jul-to-slf4j') exclude(module: 'log4j-to-slf4j') exclude(module: 'snakeyaml') } api libs.springBootStarterJdbc api libs.h2 api libs.postgresql api libs.commonsCLI annotationProcessor libs.lombok compileOnly libs.lombok runtimeOnly libs.logback testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.systemRules testImplementation(libs.springBootStarterTest) { exclude(module: 'json-path') } testImplementation libs.xmlunit testImplementation project(':platform:platform-setup-test') inDistrib project(path: ':platform:platform-resources', configuration: 'distributionZip') } group = 'org.bonitasoft.platform' publishing { publications { mavenJava(MavenPublication) { from project.components.java artifact distZip pom { pom -> name = "Bonita Platform Setup" description = "Bonita Platform Setup is the standalone tool to setup a new Bonita platform" PomUtils.pomCommunityPublication(pom) } } } } processResources { from('src/main/resources') { include '*' filter(ReplaceTokens, tokens: [version: project.version]) } } distTar.enabled = false distributions { main { distributionBaseName = "Bonita-platform-setup" contents { includeEmptyDirs = false into('/') { from('src/main/standalone') include('*.sh') include('*.bat') filePermissions { user { read = true write = true execute = true } group { read = true write = false execute = false } other { read = false write = false execute = false } } } into('/') { from('src/main/standalone') exclude('*.sh') exclude('*.bat') } into('/lib') { from jar from project.configurations.runtimeClasspath { exclude(module: 'jul-to-slf4j') exclude(module: 'log4j-over-slf4j') exclude(module: 'commons-logging') exclude(module: 'log4j-api') exclude(module: 'log4j-to-slf4j') exclude(module: 'snakeyaml') } } configurations.inDistrib.resolvedConfiguration.resolvedArtifacts.each { artifact -> // Copy all sql files under the distrib zip folder /platform_conf/sql/ from(zipTree(artifact.file)) { include '**/sql/**' eachFile { fcp -> fcp.path = fcp.path.replaceAll(".*/sql/", "/platform_conf/sql/") } } // Copy all files excepting sql and classes under the distrib zip folder /platform_conf/initial/ from(zipTree(artifact.file)) { exclude '**/sql/**' exclude '**/*.class' eachFile { fcp -> fcp.path = fcp.path.replaceAll(".*${version}/", "/platform_conf/initial/") } } } filePermissions { user { read = true write = true execute = false } group { read = true write = false execute = false } other { read = false write = false execute = false } } } } } tasks.distZip.dependsOn configurations.inDistrib artifacts { distributionZip distZip } databaseIntegrationTest { include '**/*IT.class' // No need to execute PlatformSetupDistributionIT on databases exclude '**/PlatformSetupDistributionIT.class' } tasks.matching { task -> task.name == "integrationTest" || task.name.endsWith("DatabaseTest") } .configureEach { task -> def testDir = layout.buildDirectory.dir(task.name).get().asFile doFirst { testDir.mkdirs() systemProperty "bonita.distribution.path", distZip.outputs.files.first() } workingDir testDir dependsOn distZip } def setupE2e = tasks.register('setup-e2e', SetupE2ETask) { scriptFile = file('src/test/e2e/e2e-postgres-bos.sh') inputs.files project(':platform:platform-setup').tasks.named('distZip').get().outputs.files dependsOn distZip } integrationTest.configure { dependsOn setupE2e } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/ConfigurationChecker.java ================================================ /** * 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.platform.setup; import java.nio.file.Paths; import java.util.Properties; import org.bonitasoft.platform.exception.PlatformException; import org.bonitasoft.platform.setup.command.configure.DatabaseConfiguration; /** * Preliminary checks done by platform setup tool before it tries to go further: *
        *
      • checks that all mandatory properties are set into database.properties
      • *
      • checks that the driver class if found and can be loaded
      • *
      * * @author Emmanuel Duchastenier */ class ConfigurationChecker { private Properties datasourceProperties; private String driverClassName; private DatabaseConfiguration dbConfiguration; ConfigurationChecker(Properties datasourceProperties) { this.datasourceProperties = datasourceProperties; } void loadProperties() throws PlatformException { dbConfiguration = new DatabaseConfiguration("", datasourceProperties, Paths.get(".")); driverClassName = dbConfiguration.getNonXaDriverClassName(); } public void validate() throws PlatformException { loadProperties(); tryToLoadDriverClass(); } void tryToLoadDriverClass() throws PlatformException { try { Class.forName(driverClassName); } catch (ClassNotFoundException e) { throw new PlatformException("The driver class named '" + driverClassName + "' specified in 'internal.properties' configuration file, to connect to your '" + dbConfiguration.getDbVendor() + "' database, cannot be found." + " Either there is an error in the name of the class or the class is not available in the classpath." + " Make sure the driver class name is correct and that the suitable driver is available in the lib/ folder and then try again.", e); } } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/PlatformSetupApplication.java ================================================ /** * Copyright (C) 2016 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.platform.setup; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.bonitasoft.platform.exception.PlatformException; import org.bonitasoft.platform.setup.command.CommandException; import org.bonitasoft.platform.setup.command.HelpCommand; import org.bonitasoft.platform.setup.command.InitCommand; import org.bonitasoft.platform.setup.command.PlatformSetupCommand; import org.bonitasoft.platform.setup.command.PullCommand; import org.bonitasoft.platform.setup.command.PushCommand; import org.bonitasoft.platform.setup.command.configure.ConfigureCommand; import org.bonitasoft.platform.setup.command.configure.PropertyLoader; import org.bonitasoft.platform.setup.jndi.MemoryJNDISetup; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; /** * @author Emmanuel Duchastenier */ @SpringBootApplication @ComponentScan(basePackages = { "org.bonitasoft.platform.setup", "org.bonitasoft.platform.configuration", "org.bonitasoft.platform.version", "com.bonitasoft.platform.setup" }) public class PlatformSetupApplication { // /!\ Leave this logger NON-STATIC, so that DEBUG property may be set before it is initialized: private final Logger LOGGER = LoggerFactory.getLogger(PlatformSetupApplication.class); private HelpCommand helpCommand; private List commands; private Options options; @Autowired MemoryJNDISetup memoryJNDISetup; @Autowired PlatformSetup platformSetup; public static void main(String[] args) { if (args != null && Arrays.asList(args).contains("--debug")) { System.setProperty("bonita.platform.setup.log", "DEBUG"); // so that it is set before used by Logger } new PlatformSetupApplication().run(args); } public static PlatformSetup getPlatformSetup(String[] args) throws PlatformException { new ConfigurationChecker(new PropertyLoader().loadProperties()).validate(); return SpringApplication.run(PlatformSetupApplication.class, args).getBean(PlatformSetup.class); } private void run(String[] args) { CommandLineParser parser = new DefaultParser(); options = createOptions(); commands = createCommands(); helpCommand.setCommands(commands); CommandLine line = parseArguments(args, parser); configureApplication(line); execute(line); } private PlatformSetupCommand getCommand(CommandLine line) { List argList = line.getArgList(); if (argList.isEmpty()) { return helpCommand; } final String commandName = argList.get(0); for (PlatformSetupCommand platformSetupCommand : commands) { if (commandName.equals(platformSetupCommand.getName())) { return platformSetupCommand; } } return helpCommand; } private void configureApplication(CommandLine line) { Properties systemProperties = line.getOptionProperties("D"); for (Map.Entry systemProperty : systemProperties.entrySet()) { System.setProperty(systemProperty.getKey().toString(), systemProperty.getValue().toString()); } } private void execute(CommandLine line) { try { getCommand(line).execute(options, line); } catch (CommandException e) { //this is an known exception we do not show any stack trace LOGGER.error(e.getMessage()); System.exit(1); } catch (Exception e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("ERROR: ", e); } else { LOGGER.error(e.getMessage()); LOGGER.error( "You might get more detailed information about the error by adding '--debug' to the command line, and run again"); } // Exit code allows the calling script to catch an invalid execution: System.exit(1); } System.exit(0); } private CommandLine parseArguments(String[] args, CommandLineParser parser) { try { // parse the command line arguments return parser.parse(options, args); } catch (ParseException exp) { System.err.println("ERROR: error while parsing arguments " + exp.getMessage()); System.exit(1); } return null; } private List createCommands() { List commandList = new ArrayList<>(); commandList.add(new InitCommand()); commandList.add(new ConfigureCommand()); commandList.add(new PullCommand()); commandList.add(new PushCommand()); helpCommand = new HelpCommand(); commandList.add(helpCommand); return commandList; } private Options createOptions() { Options opts = new Options(); Option systemPropertyOption = new Option("D", "specify system property to override configuration from database.properties"); systemPropertyOption.setArgName("property=value"); systemPropertyOption.setValueSeparator('='); systemPropertyOption.setArgs(2); opts.addOption(systemPropertyOption); opts.addOption("f", "force", false, "Force push even if critical folders will be deleted"); opts.addOption("d", "debug", false, "Provides more details in case of error"); return opts; } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/CommandException.java ================================================ /** * 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.platform.setup.command; /** * @author Baptiste Mesta */ public class CommandException extends Exception { public CommandException(String message) { super(message); } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/CommandUtils.java ================================================ /** * 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.platform.setup.command; import java.io.IOException; import org.apache.commons.io.IOUtils; /** * @author Baptiste Mesta */ public class CommandUtils { public static String getFileContentFromClassPath(String filename) { try { return IOUtils.toString(PlatformSetupCommand.class.getResourceAsStream("/" + filename), "UTF-8"); } catch (IOException e) { throw new IllegalStateException(e); } } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/HelpCommand.java ================================================ /** * 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.platform.setup.command; import static java.lang.System.lineSeparator; import java.util.ArrayList; import java.util.List; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Options; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.platform.exception.PlatformException; /** * @author Baptiste Mesta */ public class HelpCommand extends PlatformSetupCommand { private List commands; public HelpCommand() { super("help", "Display the help", "Display the help", null); } @Override public void execute(Options options, CommandLine commandLine) throws PlatformException, CommandException { String[] args = commandLine.getArgs(); if (args.length == 0) { printUsage(options); throw new CommandException("Need to specify a command, see usage above."); } else if (getName().equals(args[0])) { if (args.length > 1) { printHelpFor(options, args[1]); } else { printCommonHelp(options); } } else { printUsage(options); throw new CommandException("ERROR: no command named: " + args[0]); } } private void printHelpFor(Options options, String commandNameForHelp) throws CommandException { PlatformSetupCommand platformSetupCommand = getCommand(commandNameForHelp); if (platformSetupCommand == null) { printCommonHelp(options); throw new CommandException("ERROR: no command named: " + commandNameForHelp); } printHelpFor(options, platformSetupCommand); } public void setCommands(List commands) { this.commands = commands; } private PlatformSetupCommand getCommand(String commandName) { PlatformSetupCommand command = null; for (PlatformSetupCommand platformSetupCommand : commands) { if (commandName.equals(platformSetupCommand.getName())) { command = platformSetupCommand; break; } } return command; } private void printCommonHelp(Options options) { printUsage(options); printGlobalHelpHeader(); printCommandsUsage(); printGlobalHelpFooter(); } private void printUsage(Options options) { List names = new ArrayList<>(commands.size()); for (PlatformSetupCommand command : commands) { if (!command.equals(this)) { names.add(command.getName()); } } String footer = "use `setup help` or `setup help ` for more details" + lineSeparator(); printUsageFor(options, "( " + StringUtils.join(names.iterator(), " | ") + " )", footer); } private void printCommandsUsage() { StringBuilder usage = new StringBuilder(); usage.append(lineSeparator()); usage.append("Available commands:").append(lineSeparator()).append(lineSeparator()); for (PlatformSetupCommand command : commands) { usage.append(" ").append(command.getName()).append(" -- ").append(command.getSummary()) .append(lineSeparator()); } System.out.println(usage.toString()); } private void printHelpFor(Options options, PlatformSetupCommand command) { printUsageFor(options, command.getName(), lineSeparator()); printCommandDescriptionHeader(command); printCommandUsage(command); printCommandDescriptionFooter(command); } private void printCommandDescriptionHeader(PlatformSetupCommand command) { if (command.getDescriptionHeader() != null) { System.out.println(" " + command.getDescriptionHeader().replace(lineSeparator(), lineSeparator() + " ")); } } private void printCommandDescriptionFooter(PlatformSetupCommand command) { if (command.getDescriptionFooter() != null) { System.out.println(" " + command.getDescriptionFooter().replace(lineSeparator(), lineSeparator() + " ")); } } private void printUsageFor(Options options, String commandName, String footer) { HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("setup " + commandName, lineSeparator() + "Available options:", options, footer, true); } private void printCommandUsage(PlatformSetupCommand command) { System.out.println(lineSeparator() + " " + command.getName() + " -- " + command.getSummary() + lineSeparator()); } private void printGlobalHelpHeader() { System.out.println(CommandUtils.getFileContentFromClassPath("global_usage_header.txt")); } private void printGlobalHelpFooter() { System.out.println(CommandUtils.getFileContentFromClassPath("global_usage_footer.txt")); } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/InitCommand.java ================================================ /** * 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.platform.setup.command; import static org.bonitasoft.platform.setup.command.configure.DatabaseConfiguration.H2_DB_VENDOR; import java.io.IOException; import java.util.Properties; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.bonitasoft.platform.exception.PlatformException; import org.bonitasoft.platform.setup.command.configure.PropertyReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Baptiste Mesta */ public class InitCommand extends PlatformSetupCommand { private final static Logger LOGGER = LoggerFactory.getLogger(InitCommand.class); public InitCommand() { super("init", "Initialise the database so that Bonita is ready to run with this database", CommandUtils.getFileContentFromClassPath("init_header.txt"), CommandUtils.getFileContentFromClassPath("init_footer.txt")); } @Override public void execute(Options options, CommandLine commandLine) throws PlatformException, CommandException { askConfirmationIfH2(); getPlatformSetup(commandLine.getArgs()).init(); } void askConfirmationIfH2() throws PlatformException, CommandException { final Properties properties = new Properties(); try { properties.load(this.getClass().getResourceAsStream("/database.properties")); final PropertyReader propertyReader = new PropertyReader(properties); if (H2_DB_VENDOR.equals(propertyReader.getPropertyAndFailIfNull("db.vendor")) && H2_DB_VENDOR.equals(propertyReader.getPropertyAndFailIfNull("bdm.db.vendor")) && System.getProperty("h2.noconfirm") == null) { warn("Default H2 configuration detected. This is not recommended for production. If this is not the required configuration, change file 'database.properties' and run again."); warn("To skip this warning message, add '-Dh2.noconfirm' to your command line."); System.out.print("Are you sure you want to continue? (y/n): "); final String answer = readAnswer(); if (!"y".equalsIgnoreCase(answer)) { throw new CommandException("Default H2 configuration not confirmed. Exiting."); } } } catch (IOException e) { throw new PlatformException("Error reading configuration file database.properties." + " Please make sure the file is present at the root of the Platform Setup Tool folder, and that is has not been moved of deleted", e); } } String readAnswer() throws IOException { final byte[] read = new byte[1]; System.in.read(read); return new String(read); } void warn(String message) { LOGGER.warn(message); } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/PlatformSetupCommand.java ================================================ /** * 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.platform.setup.command; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.bonitasoft.platform.exception.PlatformException; import org.bonitasoft.platform.setup.PlatformSetup; import org.bonitasoft.platform.setup.PlatformSetupApplication; /** * @author Baptiste Mesta */ public abstract class PlatformSetupCommand { private String name; private String summary; private String descriptionHeader; private String descriptionFooter; public PlatformSetupCommand(String name, String summary, String descriptionHeader, String descriptionFooter) { this.name = name; this.summary = summary; this.descriptionHeader = descriptionHeader; this.descriptionFooter = descriptionFooter; } public abstract void execute(Options options, CommandLine commandLine) throws PlatformException, CommandException; public String getName() { return name; } public String getSummary() { return summary; } public String getDescriptionHeader() { return descriptionHeader; } public String getDescriptionFooter() { return descriptionFooter; } PlatformSetup getPlatformSetup(String[] args) throws PlatformException { return PlatformSetupApplication.getPlatformSetup(args); } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/PullCommand.java ================================================ /** * 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.platform.setup.command; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.bonitasoft.platform.exception.PlatformException; /** * @author Baptiste Mesta */ public class PullCommand extends PlatformSetupCommand { public PullCommand() { super("pull", "Pull configuration from the database", null, CommandUtils.getFileContentFromClassPath("pull.txt")); } @Override public void execute(Options options, CommandLine commandLine) throws PlatformException { getPlatformSetup(commandLine.getArgs()).pull(); } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/PushCommand.java ================================================ /** * 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.platform.setup.command; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.bonitasoft.platform.exception.PlatformException; /** * @author Baptiste Mesta */ public class PushCommand extends PlatformSetupCommand { public PushCommand() { super("push", "Push configuration to the database", null, CommandUtils.getFileContentFromClassPath("push.txt")); } @Override public void execute(Options options, CommandLine commandLine) throws PlatformException { getPlatformSetup(commandLine.getArgs()).push(commandLine.hasOption("force")); } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/configure/BundleConfigurator.java ================================================ /** * Copyright (C) 2016 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.platform.setup.command.configure; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.Collection; import java.util.Date; import java.util.Map; import java.util.Properties; import java.util.regex.Matcher; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOCase; import org.apache.commons.io.filefilter.RegexFileFilter; import org.apache.commons.text.StringEscapeUtils; import org.bonitasoft.platform.database.DatabaseVendor; import org.bonitasoft.platform.exception.PlatformException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class configures a supported Bundle with configuration values given in file database.properties. * For Tomcat bundle: *
        *
      • sets db vendor + bdm db vendor in file setenv.sh / .bat
      • *
      • sets all database configuration in file conf/Catalina/localhost/bonita.xml
      • *
      • copies the required drivers from setup/lib to lib/bonita
      • *
      * * @author Emmanuel Duchastenier */ abstract class BundleConfigurator { static final Logger LOGGER = LoggerFactory.getLogger(BundleConfigurator.class); private static final String TOMCAT_TEMPLATES_FOLDER = "tomcat-templates"; static final String APPSERVER_FOLDERNAME = "server"; private final Path rootPath; DatabaseConfiguration standardConfiguration; DatabaseConfiguration bdmConfiguration; private Path backupsFolder; private final String timestamp; BundleConfigurator(Path rootPath) throws PlatformException { try { this.rootPath = rootPath.toRealPath(); } catch (IOException e) { throw new PlatformException("Unable to determine root path for " + getBundleName()); } timestamp = new SimpleDateFormat("yyyy-MM-dd_HH'h'mm'm'ss's'").format(new Date()); } void loadProperties() throws PlatformException { final Properties properties = new PropertyLoader().loadProperties(); standardConfiguration = new DatabaseConfiguration("", properties, rootPath); bdmConfiguration = new DatabaseConfiguration("bdm.", properties, rootPath); try { final Path dbFile = Paths.get(this.getClass().getResource("/database.properties").toURI()); LOGGER.info(getBundleName() + " environment detected with root " + rootPath); LOGGER.info("Running auto-configuration using file " + dbFile.normalize()); } catch (URISyntaxException e) { throw new PlatformException("Configuration file 'database.properties' not found"); } } protected abstract String getBundleName(); abstract void configureApplicationServer() throws PlatformException; void createBackupFolderIfNecessary(String backupFolder) throws PlatformException { backupsFolder = getPath(backupFolder); if (Files.notExists(backupsFolder)) { try { Files.createDirectory(backupsFolder); } catch (IOException e) { throw new PlatformException("Could not create backup folder: " + backupFolder, e); } } } Path getTemplateFolderPath(String templateFile) throws PlatformException { return getPath("setup").resolve(TOMCAT_TEMPLATES_FOLDER).resolve(templateFile); } void backupAndReplaceContentIfNecessary(Path path, String newContent, String message) throws PlatformException { if (Files.exists(path)) { String previousContent = readContentFromFile(path); if (!previousContent.equals(newContent)) { makeBackupOfFile(path); writeContentToFile(path, newContent); LOGGER.info(message); } else { LOGGER.info( "Same configuration detected for file '" + getRelativePath(path) + "'. No need to change it."); } } else { LOGGER.info("File '" + getRelativePath(path) + "' did not already exist. Creating it."); writeContentToFile(path, newContent); } } void copyDatabaseDriversIfNecessary(Path srcDriverFile, Path targetDriverFile, String dbVendor) throws PlatformException { if (srcDriverFile == null || targetDriverFile == null) { return; } if (Files.exists(targetDriverFile)) { LOGGER.info("Your " + dbVendor + " driver file '" + getRelativePath(targetDriverFile) + "' already exists. Skipping the copy."); return; } copyDriverFile(srcDriverFile, targetDriverFile, dbVendor); } //This method will remove h2 from bonita the classPath to mitigate the cve https://nvd.nist.gov/vuln/detail/CVE-2022-23221 void removeH2DriverIfNecessary(Path targetBonitaDbDriverFile, String bonitaDbVendor, String bdmDbVendor) throws PlatformException { if (!bonitaDbVendor.equals("h2") && !bdmDbVendor.equals("h2")) { FilenameFilter filter = (file, s) -> s.contains("h2"); File[] driversFiles = targetBonitaDbDriverFile.toFile().listFiles(filter); if (driversFiles != null && driversFiles.length > 0) { for (File driver : driversFiles) { try { LOGGER.info( "H2 driver has been found located in bonita classpath {}, the driver file will be removed", driver.getPath()); Files.delete(driver.toPath()); LOGGER.info("File has been removed"); } catch (IOException e) { LOGGER.error( "Fail to delete driver file lib/ {}", driver.getPath(), e); } } } } } private Path getRelativePath(Path pathToRelativize) { return rootPath.toAbsolutePath().relativize(pathToRelativize.toAbsolutePath()); } void copyDriverFile(Path srcDriverFile, Path targetDriverFile, String dbVendor) throws PlatformException { try { final Path targetDriverFolder = targetDriverFile.getParent(); targetDriverFolder.toFile().mkdirs(); Files.copy(srcDriverFile, targetDriverFile); LOGGER.info("Copying your {} driver file ''{}'' to tomcat lib folder ''{}''", dbVendor, getRelativePath(srcDriverFile), getRelativePath(targetDriverFolder)); } catch (IOException e) { throw new PlatformException( "Fail to copy driver file lib/" + srcDriverFile.getFileName() + " to " + targetDriverFile.toAbsolutePath() + ": " + e.getMessage(), e); } } static String replaceValues(String content, Map replacementMap) { for (Map.Entry entry : replacementMap.entrySet()) { content = content.replaceAll(entry.getKey(), entry.getValue()); } return content; } // Visible for testing static String convertWindowsBackslashes(String value) { // forward slashes is valid in database connection URLs on Windows, and and easier and more homogeneous to manage in return value.replace("\\", "/"); } private void writeContentToFile(Path path, String content) throws PlatformException { try { Files.write(path, content.getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { throw new PlatformException("Fail to replace content in file " + path + ": " + e.getMessage(), e); } } String readContentFromFile(Path bonitaXmlFile) throws PlatformException { try { return new String(Files.readAllBytes(bonitaXmlFile), StandardCharsets.UTF_8); } catch (IOException e) { throw new PlatformException( "Cannot read content of text file " + bonitaXmlFile.toAbsolutePath().toString(), e); } } void restoreOriginalFile(Path bonitaXmlFile) throws PlatformException { try { if (Files.exists(bonitaXmlFile)) { Files.delete(bonitaXmlFile); } final Path backupFile = getBackupFile(bonitaXmlFile); if (Files.exists(backupFile)) { Files.move(backupFile, bonitaXmlFile); } } catch (IOException e) { throw new PlatformException("Fail to restore original file for " + bonitaXmlFile + ": " + e.getMessage(), e); } } private Path makeBackupOfFile(Path originalFile) throws PlatformException { final Path originalFileName = originalFile.getFileName(); final Path backup = getBackupFile(originalFile); LOGGER.info( "Creating a backup of configuration file '" + getRelativePath(originalFile).normalize() + "' to '" + getRelativePath(backup).normalize() + "'"); try { Files.copy(originalFile, backup); return backup; } catch (IOException e) { throw new PlatformException("Fail to make backup file for " + originalFileName + ": " + e.getMessage(), e); } } private Path getBackupFile(Path originalFile) { return backupsFolder.resolve(originalFile.getFileName() + "." + getTimestamp()); } private String getTimestamp() { return timestamp; } File getDriverFile(String dbVendor) throws PlatformException { final Path driverFolder = getPath("setup/lib"); if (!Files.exists(driverFolder)) { throw new PlatformException("Drivers folder not found: " + driverFolder.toString() + ". Make sure it exists and put a jar or zip file containing drivers there."); } final Collection driversFiles = FileUtils.listFiles(driverFolder.toFile(), getDriverFilter(dbVendor), null); if (driversFiles.isEmpty()) { throw new PlatformException("No " + dbVendor + " drivers found in folder " + driverFolder.toString() + ". Make sure to put a jar or zip file containing drivers there."); } else if (driversFiles.size() == 1) { return (File) driversFiles.toArray()[0]; } else { throw new PlatformException( "Found more than 1 file containing " + dbVendor + " drivers in folder " + driverFolder.toString() + ". Make sure to put only 1 jar or zip file containing drivers there."); } } RegexFileFilter getDriverFilter(String dbVendor) { return new RegexFileFilter(getDriverPattern(dbVendor), IOCase.INSENSITIVE); } private String getDriverPattern(String dbVendor) { if (DatabaseVendor.ORACLE.equalsValue(dbVendor)) { return ".*(ojdbc|oracle).*\\.(jar|zip)"; } if (DatabaseVendor.SQLSERVER.equalsValue(dbVendor)) { return ".*(sqlserver|mssql|sqljdbc).*\\.(jar|zip)"; } return ".*" + dbVendor + ".*"; } /** * Constructs path relative to rootPath. * * @param partialPath path relative to rootPath * @return the real path, constructed from rootPath, appending the partialPath */ private Path getPath(String partialPath) throws PlatformException { return getPath(partialPath, false); } /** * Constructs path relative to rootPath. * * @param partialPath path relative to rootPath * @param failIfNotExist should we fail if file does not exist? * @return the real path, constructed from rootPath, appending the partialPath */ private Path getPath(String partialPath, boolean failIfNotExist) throws PlatformException { final String[] paths = partialPath.split("/"); Path build = rootPath; for (String path : paths) { build = build.resolve(path); } if (failIfNotExist && Files.notExists(build)) { throw new PlatformException("File " + build.getFileName() + " is mandatory but is not found"); } return build; } // Visible for testing static String escapeXmlCharacters(String url) { return StringEscapeUtils.escapeXml11(url); } protected Path getOptionalPathUnderAppServer(String path) throws PlatformException { return getPath(APPSERVER_FOLDERNAME + "/" + path, false); } Path getPathUnderAppServer(String path, boolean failIfNotExist) throws PlatformException { return getPath(APPSERVER_FOLDERNAME + "/" + path, failIfNotExist); } static String getDatabaseConnectionUrlForXmlFile(DatabaseConfiguration configuration) { return escapeXmlCharacters(Matcher.quoteReplacement(getDatabaseConnectionUrl(configuration))); } static String getDatabaseConnectionUrlForPropertiesFile(DatabaseConfiguration configuration) { String url = getDatabaseConnectionUrl(configuration); if (DatabaseVendor.H2.equalsValue(configuration.getDbVendor())) { url = StringEscapeUtils.escapeJava(url); } return Matcher.quoteReplacement(url); } private static String getDatabaseConnectionUrl(DatabaseConfiguration configuration) { String url = configuration.getUrl(); if (DatabaseVendor.H2.equalsValue(configuration.getDbVendor())) { url = convertWindowsBackslashes(url); } return url; } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/configure/BundleResolver.java ================================================ /** * 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.platform.setup.command.configure; import static org.bonitasoft.platform.setup.PlatformSetup.BONITA_SETUP_FOLDER; import static org.bonitasoft.platform.setup.command.configure.BundleConfigurator.APPSERVER_FOLDERNAME; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.platform.exception.PlatformException; /** * This class contains the logic to determine if we are in the context of an application server that we must configure. * Only Tomcat is supported. */ @Slf4j class BundleResolver { private final Path rootPath; BundleResolver() { final String setupFolder = System.getProperty(BONITA_SETUP_FOLDER); if (setupFolder != null) { rootPath = Paths.get(setupFolder).getParent(); } else { rootPath = Paths.get(".."); } } private boolean fileExists(Path filePath) { final boolean exists = Files.exists(filePath); if (!exists) { log.debug("File {} does not exist.", filePath); } return exists; } private Path getPath(String partialPath) { final String[] paths = partialPath.split("/"); Path build = rootPath; for (String path : paths) { build = build.resolve(path); } return build; } private boolean isTomcatEnvironment() { return fileExists(getPath(APPSERVER_FOLDERNAME + "/bin/catalina.sh")) || fileExists(getPath(APPSERVER_FOLDERNAME + "/bin/catalina.bat")); } BundleConfigurator getConfigurator() throws PlatformException { if (isTomcatEnvironment()) { return new TomcatBundleConfigurator(rootPath); } else { log.info("No Application Server detected. You may need to manually configure the access to the database. " + "Only Tomcat 9.0.x is supported"); return null; } } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/configure/ConfigureCommand.java ================================================ /** * 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.platform.setup.command.configure; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.bonitasoft.platform.exception.PlatformException; import org.bonitasoft.platform.setup.command.CommandUtils; import org.bonitasoft.platform.setup.command.PlatformSetupCommand; /** * @author Baptiste Mesta */ public class ConfigureCommand extends PlatformSetupCommand { public ConfigureCommand() { super("configure", "Configure a Bonita bundle to use your specific database configuration (defined in database.properties or via command line parameters)", CommandUtils.getFileContentFromClassPath("configure_header.txt"), CommandUtils.getFileContentFromClassPath("configure_footer.txt")); } @Override public void execute(Options options, CommandLine commandLine) throws PlatformException { BundleConfigurator bundleConfigurator = createBundleResolver().getConfigurator(); if (bundleConfigurator != null) { bundleConfigurator.configureApplicationServer(); } } BundleResolver createBundleResolver() { return new BundleResolver(); } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/configure/DatabaseConfiguration.java ================================================ /** * Copyright (C) 2016 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.platform.setup.command.configure; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Properties; import lombok.Getter; import org.bonitasoft.platform.exception.PlatformException; /** * @author Emmanuel Duchastenier */ public class DatabaseConfiguration { public static final String H2_DB_VENDOR = "h2"; private static final String H2_DATABASE_DIR = "${h2.database.dir}"; @Getter private String dbVendor; @Getter private String nonXaDriverClassName; @Getter private String xaDriverClassName; @Getter private String xaDataSourceFactory; @Getter private String databaseUser; @Getter private String databasePassword; @Getter private String databaseName; @Getter private String serverName = ""; @Getter private String serverPort = ""; @Getter private String url; @Getter private String testQuery; @Getter private Integer connectionPoolInitialSize; @Getter private Integer connectionPoolMaxTotal; @Getter private Integer connectionPoolMaxIdle; @Getter private Integer connectionPoolMinIdle; private PropertyReader propertyReader; public DatabaseConfiguration(String prefix, Properties properties, Path rootPath) throws PlatformException { propertyReader = new PropertyReader(properties); dbVendor = getMandatoryProperty(prefix + "db.vendor"); nonXaDriverClassName = getMandatoryProperty(dbVendor + ".nonXaDriver"); xaDriverClassName = getMandatoryProperty(dbVendor + ".xaDriver"); xaDataSourceFactory = getMandatoryProperty(dbVendor + ".xaDSFactory"); databaseName = getMandatoryProperty(prefix + "db.database.name"); url = getMandatoryProperty(dbVendor + "." + prefix + "url"); // Configuration for H2 is a little different from other DB vendors: if (H2_DB_VENDOR.equals(dbVendor)) { String h2DatabaseDir = getMandatoryProperty("h2.database.dir"); Path h2DatabasePath = Paths.get(h2DatabaseDir); if (h2DatabasePath.isAbsolute() || h2DatabaseDir.startsWith("${")) { url = url.replace(H2_DATABASE_DIR, h2DatabasePath.normalize().toString()); } else { // generate absolute path url = url.replace(H2_DATABASE_DIR, rootPath.resolve("setup").resolve(h2DatabaseDir).toAbsolutePath().normalize().toString()); } // h2 path on windows must have forward slashes (H2 convention): url = url.replace("\\", "/"); } else { serverName = getMandatoryProperty(prefix + "db.server.name"); url = url.replace("${" + prefix + "db.server.name}", serverName); serverPort = getMandatoryProperty(prefix + "db.server.port"); url = url.replace("${" + prefix + "db.server.port}", serverPort); } url = url.replace("${" + prefix + "db.database.name}", databaseName); databaseUser = getMandatoryProperty(prefix + "db.user"); databasePassword = getMandatoryProperty(prefix + "db.password"); testQuery = getMandatoryProperty(dbVendor + "." + prefix + "testQuery"); connectionPoolInitialSize = getMandatoryIntegerProperty(prefix + "connection-pool.initialSize"); connectionPoolMaxTotal = getMandatoryIntegerProperty(prefix + "connection-pool.maxTotal"); connectionPoolMaxIdle = getMandatoryIntegerProperty(prefix + "connection-pool.maxIdle"); connectionPoolMinIdle = getMandatoryIntegerProperty(prefix + "connection-pool.minIdle"); } private String getMandatoryProperty(String s) throws PlatformException { return propertyReader.getPropertyAndFailIfNull(s); } private Integer getMandatoryIntegerProperty(String s) throws PlatformException { var value = propertyReader.getPropertyAndFailIfNull(s); try { return Integer.parseInt(value); } catch (NumberFormatException e) { throw new PlatformException(String.format("Invalid integer value '%s' for property '%s'", value, s)); } } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/configure/PropertyLoader.java ================================================ /** * 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.platform.setup.command.configure; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Properties; import org.bonitasoft.platform.exception.PlatformException; /** * @author Emmanuel Duchastenier */ public class PropertyLoader { private final List propertyFiles; public PropertyLoader(String... propertyFiles) { this.propertyFiles = Arrays.asList(propertyFiles); } public PropertyLoader() { this("/internal.properties", "/database.properties"); } public Properties loadProperties() throws PlatformException { final Properties properties = new Properties(); for (String propertyFile : propertyFiles) { try { properties.load(this.getClass().getResourceAsStream(propertyFile)); } catch (IOException e) { throw new PlatformException("Error reading configuration file " + propertyFile + ". Please make sure the file is present at the root of the Platform Setup Tool folder, and that is has not been moved of deleted", e); } } return properties; } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/configure/PropertyReader.java ================================================ /** * Copyright (C) 2016 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.platform.setup.command.configure; import java.util.Properties; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.platform.exception.PlatformException; @Slf4j public class PropertyReader { private final Properties properties; public PropertyReader(Properties properties) { this.properties = properties; } public String getPropertyAndFailIfNull(String propertyName) throws PlatformException { // Any property value can be overridden by system property with the same name: final String sysPropValue = System.getProperty(propertyName); if (sysPropValue != null) { log.info("System property '{}' set to '{}', overriding value from file database.properties.", propertyName, sysPropValue); return sysPropValue.trim(); } final String property = properties.getProperty(propertyName); if (property == null) { throw new PlatformException( "Mandatory property '" + propertyName + "' is missing." + " Ensure you did not remove lines from file 'database.properties' (neither from file 'internal.properties')" + " and that the line is NOT commented out with a '#' character at start of line."); } return property.trim(); } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/command/configure/TomcatBundleConfigurator.java ================================================ /** * 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.platform.setup.command.configure; import java.io.File; import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import org.bonitasoft.platform.exception.PlatformException; import org.bonitasoft.platform.setup.PlatformSetup; /** * @author Emmanuel Duchastenier */ class TomcatBundleConfigurator extends BundleConfigurator { private static final String TOMCAT_BACKUP_FOLDER = "tomcat-backups"; TomcatBundleConfigurator(Path rootPath) throws PlatformException { super(rootPath); } @Override protected String getBundleName() { return "Tomcat"; } @Override public void configureApplicationServer() throws PlatformException { loadProperties(); final String dbVendor = standardConfiguration.getDbVendor(); final String bdmDbVendor = bdmConfiguration.getDbVendor(); final Path setEnvUnixFile = getPathUnderAppServer("bin/setenv.sh", true); final Path setEnvWindowsFile = getPathUnderAppServer("bin/setenv.bat", true); final Path bonitaXmlFile = getPathUnderAppServer("conf/Catalina/localhost/bonita.xml", false); final File bonitaDbDriverFile = getDriverFile(dbVendor); final File bdmDriverFile = getDriverFile(bdmDbVendor); try { createBackupFolderIfNecessary("setup/" + TOMCAT_BACKUP_FOLDER); // 1. update setenv(.sh|.bat): String newContent = readContentFromFile(getTemplateFolderPath("setenv.bat")); newContent = updateSetEnvFile(newContent, dbVendor, PlatformSetup.BONITA_DB_VENDOR_PROPERTY); newContent = updateSetEnvFile(newContent, bdmDbVendor, PlatformSetup.BONITA_BDM_DB_VENDOR_PROPERTY); backupAndReplaceContentIfNecessary(setEnvWindowsFile, newContent, "Setting Bonita internal database vendor to '" + dbVendor + "' and Business Data database vendor to '" + bdmDbVendor + "' in 'setenv.bat' file"); newContent = readContentFromFile(getTemplateFolderPath("setenv.sh")); newContent = updateSetEnvFile(newContent, dbVendor, PlatformSetup.BONITA_DB_VENDOR_PROPERTY); newContent = updateSetEnvFile(newContent, bdmDbVendor, PlatformSetup.BONITA_BDM_DB_VENDOR_PROPERTY); backupAndReplaceContentIfNecessary(setEnvUnixFile, newContent, "Setting Bonita internal database vendor to '" + dbVendor + "' and Business Data database vendor to '" + bdmDbVendor + "' in 'setenv.sh' file"); //2. update bonita.xml: newContent = readContentFromFile(getTemplateFolderPath("bonita.xml")); newContent = updateBonitaXmlFile(newContent, standardConfiguration, "ds1"); newContent = updateBonitaXmlFile(newContent, bdmConfiguration, "ds2"); backupAndReplaceContentIfNecessary(bonitaXmlFile, newContent, "Configuring file 'conf/Catalina/localhost/bonita.xml' with your DB values for Bonita internal database on '" + dbVendor + "' and for Business Data database on '" + bdmDbVendor + "'"); //3. copy the JDBC drivers: final Path srcDriverFile = bonitaDbDriverFile.toPath(); final Path targetBonitaDbDriverFile = getPathUnderAppServer("lib/bonita", true) .resolve(srcDriverFile.getFileName()); copyDatabaseDriversIfNecessary(srcDriverFile, targetBonitaDbDriverFile, dbVendor); final Path srcBdmDriverFile = bdmDriverFile.toPath(); final Path targetBdmDriverFile = getPathUnderAppServer("lib/bonita", true) .resolve(srcBdmDriverFile.getFileName()); copyDatabaseDriversIfNecessary(srcBdmDriverFile, targetBdmDriverFile, bdmDbVendor); removeH2DriverIfNecessary(targetBonitaDbDriverFile.getParent(), dbVendor, bdmDbVendor); LOGGER.info("Tomcat auto-configuration complete."); } catch (PlatformException e) { restorePreviousConfiguration(setEnvUnixFile, setEnvWindowsFile, bonitaXmlFile); throw e; } } private String updateBonitaXmlFile(String content, DatabaseConfiguration configuration, final String datasourceAlias) { Map replacements = new HashMap<>(15); replacements.put("@@" + datasourceAlias + ".database_connection_user@@", Matcher.quoteReplacement(configuration.getDatabaseUser())); replacements.put("@@" + datasourceAlias + ".database_connection_password@@", Matcher.quoteReplacement(configuration.getDatabasePassword())); replacements.put("@@" + datasourceAlias + ".driver_class_name@@", configuration.getNonXaDriverClassName()); replacements.put("@@" + datasourceAlias + ".xa.driver_class_name@@", configuration.getXaDriverClassName()); replacements.put("@@" + datasourceAlias + ".xa_datasource_factory@@", configuration.getXaDataSourceFactory()); replacements.put("@@" + datasourceAlias + ".database_connection_url@@", getDatabaseConnectionUrlForXmlFile(configuration)); replacements.put("@@" + datasourceAlias + "_database_server_name@@", configuration.getServerName()); replacements.put("@@" + datasourceAlias + "_database_port_number@@", configuration.getServerPort()); replacements.put("@@" + datasourceAlias + "_database_database_name@@", Matcher.quoteReplacement(configuration.getDatabaseName())); replacements.put("@@" + datasourceAlias + ".database_test_query@@", configuration.getTestQuery()); replacements.put("@@" + datasourceAlias + "_connection_pool_initialSize@@", String.valueOf(configuration.getConnectionPoolInitialSize())); replacements.put("@@" + datasourceAlias + "_connection_pool_maxTotal@@", String.valueOf(configuration.getConnectionPoolMaxTotal())); replacements.put("@@" + datasourceAlias + "_connection_pool_minIdle@@", String.valueOf(configuration.getConnectionPoolMinIdle())); replacements.put("@@" + datasourceAlias + "_connection_pool_maxIdle@@", String.valueOf(configuration.getConnectionPoolMaxIdle())); return replaceValues(content, replacements); } private String updateSetEnvFile(String setEnvFileContent, String dbVendor, final String systemPropertyName) { final Map replacementMap = Collections.singletonMap("-D" + systemPropertyName + "=.*\"", "-D" + systemPropertyName + "=" + dbVendor + "\""); return replaceValues(setEnvFileContent, replacementMap); } void restorePreviousConfiguration(Path setEnvUnixFile, Path setEnvWindowsFile, Path bonitaXmlFile) throws PlatformException { LOGGER.warn("Problem encountered, restoring previous configuration"); // undo file copies: restoreOriginalFile(bonitaXmlFile); restoreOriginalFile(setEnvUnixFile); restoreOriginalFile(setEnvWindowsFile); } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/dbconfig/DataSourceConfig.java ================================================ /** * 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.platform.setup.dbconfig; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; /** * Used by Spring to create an instance of {@link javax.sql.DataSource} using parameters provided in properties. */ @Component @PropertySource(value = { "classpath:/database.properties", "classpath:/internal.properties" }) public class DataSourceConfig { } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/jndi/MemoryJNDISetup.java ================================================ /** * 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.platform.setup.jndi; import javax.naming.Context; import javax.naming.NamingException; import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jndi.JndiTemplate; import org.springframework.stereotype.Component; @Component public class MemoryJNDISetup implements DisposableBean, InitializingBean { public static final String BONITA_NON_MANAGED_DS_JNDI_NAME = "java:comp/env/bonitaSequenceManagerDS"; private final Logger logger = LoggerFactory.getLogger(MemoryJNDISetup.class.getSimpleName()); private final JndiTemplate jndiTemplate; private final DataSource datasource; @Autowired public MemoryJNDISetup(final DataSource datasource) throws NamingException { super(); this.datasource = datasource; System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.bonitasoft.platform.setup.jndi.SimpleMemoryContextFactory"); System.setProperty(Context.URL_PKG_PREFIXES, "org.bonitasoft.platform.setup.jndi"); jndiTemplate = new JndiTemplate(); } public void afterPropertiesSet() throws NamingException { logger.info("Binding " + BONITA_NON_MANAGED_DS_JNDI_NAME + " @ " + datasource.toString()); jndiTemplate.bind(BONITA_NON_MANAGED_DS_JNDI_NAME, datasource); } public void destroy() throws NamingException { logger.info("Unbinding " + BONITA_NON_MANAGED_DS_JNDI_NAME); jndiTemplate.unbind(BONITA_NON_MANAGED_DS_JNDI_NAME); } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/jndi/SimpleMemoryContext.java ================================================ /** * 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.platform.setup.jndi; import java.util.Hashtable; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.naming.Binding; import javax.naming.Context; import javax.naming.Name; import javax.naming.NameAlreadyBoundException; import javax.naming.NameClassPair; import javax.naming.NameNotFoundException; import javax.naming.NameParser; import javax.naming.NamingEnumeration; import javax.naming.NamingException; /** * A JNDI context implementation that uses the memory as a dictionary of objects. */ public class SimpleMemoryContext implements Context { private static final String NOT_SUPPORTED_YET = "Not supported yet."; private final Map dictionary = new ConcurrentHashMap(); public void clear() { dictionary.clear(); } @Override public Object lookup(final Name name) throws NamingException { return lookup(name.toString()); } @Override public Object lookup(final String name) throws NamingException { if (dictionary.containsKey(name)) { return dictionary.get(name); } throw new NameNotFoundException("Name " + name + " is not bound !"); } @Override public void bind(final Name name, final Object o) throws NamingException { bind(name.toString(), o); } @Override public void bind(final String name, final Object o) throws NamingException { if (dictionary.containsKey(name)) { throw new NameAlreadyBoundException("Name " + name + " already bound!"); } rebind(name, o); } @Override public void rebind(final Name name, final Object o) { rebind(name.toString(), o); } @Override public void rebind(final String name, final Object o) { dictionary.put(name, o); } @Override public void unbind(final Name name) throws NamingException { unbind(name.toString()); } @Override public void unbind(final String name) throws NamingException { if (!dictionary.containsKey(name)) { throw new NameNotFoundException("No such name " + name + " is bound!"); } dictionary.remove(name); } @Override public void rename(final Name oldName, final Name newName) throws NamingException { rename(oldName.toString(), newName.toString()); } @Override public void rename(final String oldName, final String newName) throws NamingException { final Object object = lookup(oldName); bind(newName, object); unbind(oldName); } @Override public NamingEnumeration list(final Name name) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public NamingEnumeration list(final String string) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public NamingEnumeration listBindings(final Name name) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public NamingEnumeration listBindings(final String string) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public void destroySubcontext(final Name name) { destroySubcontext(name.toString()); } @Override public void destroySubcontext(final String name) { dictionary.remove(name); } @Override public Context createSubcontext(final Name name) throws NamingException { return createSubcontext(name.toString()); } @Override public Context createSubcontext(final String name) throws NamingException { final Context subContext = new SimpleMemoryContext(); bind(name, subContext); return subContext; } @Override public Object lookupLink(final Name name) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public Object lookupLink(final String string) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public NameParser getNameParser(final Name name) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public NameParser getNameParser(final String name) { return new SimpleNameParser(name); } @Override public Name composeName(final Name name, final Name name1) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public String composeName(final String string, final String string1) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public Object addToEnvironment(final String string, final Object o) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public Object removeFromEnvironment(final String string) { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public Hashtable getEnvironment() { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } @Override public void close() { // Thread.dumpStack(); // clear(); } @Override public String getNameInNamespace() { throw new UnsupportedOperationException(NOT_SUPPORTED_YET); } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/jndi/SimpleMemoryContextFactory.java ================================================ /** * 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.platform.setup.jndi; import java.util.Hashtable; import javax.naming.Context; import javax.naming.spi.InitialContextFactory; /** * A factory of a naming context that uses the memory as dictionary of objects. Useful to tests * objects using JNDI to get dependencies. */ public class SimpleMemoryContextFactory implements InitialContextFactory { private static final SimpleMemoryContext context = new SimpleMemoryContext(); @Override public Context getInitialContext(final Hashtable environment) { return context; } } ================================================ FILE: platform/platform-setup/src/main/java/org/bonitasoft/platform/setup/jndi/SimpleNameParser.java ================================================ /** * 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.platform.setup.jndi; import javax.naming.CompositeName; import javax.naming.Name; import javax.naming.NameParser; import javax.naming.NamingException; public class SimpleNameParser implements NameParser { public SimpleNameParser(final String name) { // TODO Auto-generated constructor stub } @Override public Name parse(final String name) throws NamingException { return new CompositeName(name); } } ================================================ FILE: platform/platform-setup/src/main/resources/application.properties ================================================ db.vendor=${sysprop.bonita.db.vendor:h2} ================================================ FILE: platform/platform-setup/src/main/resources/banner.txt ================================================ ____ _ _ | _ \ (_) | | |_) | ___ _ __ _| |_ __ _ | _ < / _ \| '_ \| | __/ _` | | |_) | (_) | | | | | || (_| | |____/ \___/|_| |_|_|\__\__,_| (Platform Setup @version@) ================================================ FILE: platform/platform-setup/src/main/resources/configure_footer.txt ================================================ Remember that this command only works on a Tomcat bundle provided by Bonitasoft. During the `configure` script execution, the setup tool verifies that the parent folder is a Tomcat bundle. If it is so, it: - sets db vendor + bdm db vendor in file server/bin/setenv (.sh|.bat) - sets all database configuration in file server/conf/Catalina/localhost/bonita.xml - copies the required drivers from setup/lib/ to server/lib/bonita/ ADVANCED CONFIGURATION WARNING: If you need to edit the server configuration, do not directly edit Tomcat files anymore, as they will be overwritten by the templates used by the setup tool. Therefore, if you need to finely tune the server configuration, modify the following Tomcat templates: - setup/tomcat-templates/bonita.xml - setup/tomcat-templates/setenv.sh - setup/tomcat-templates/setenv.bat ================================================ FILE: platform/platform-setup/src/main/resources/configure_header.txt ================================================ Important: For basic bundle installations and starts, launch the `bonita-start`(.bat|.sh) script, at top level of Bonita bundles. It will create the database tables, configure the server using information from `database.properties`, and then start the server for you. So you normally don't need to run any of this `setup` script manually. However, for specific needs, here are details about `setup.(bat|sh) configure`: ================================================ FILE: platform/platform-setup/src/main/resources/global_usage_footer.txt ================================================ All commands here interact with the database configured in the file database.properties. By default, the bundle is configured with an embedded H2 database, suitable for development and testing, not suitable for production purposes. To switch to another database vendor, change values in the file database.properties or use system properties. You can specify system properties directly from the command line, using `-Dproperty=value`. For example, to change the database name and initialize the database, use: `setup.bat init -Ddb.database.name=myDatabase` Here is the list of available properties: * db.vendor * db.server.name * db.server.port * db.database.name * db.user * db.password * bdm.db.vendor * bdm.db.server.name * bdm.db.server.port * bdm.db.database.name * bdm.db.user * bdm.db.password To know more about the setup tool, go to the Documentation web site, and search with the keywords "platform setup" ================================================ FILE: platform/platform-setup/src/main/resources/global_usage_header.txt ================================================ Important: For basic bundle installations and starts, launch the `bonita-start`(.bat|.sh) script, at top level of Bonita bundles. It will create the database tables, configure the server using information from `database.properties`, and then start the server for you. So you normally don't need to run this `setup` script manually. However, for specific needs, here are the details of use: ================================================ FILE: platform/platform-setup/src/main/resources/init_footer.txt ================================================ Run this init command once to create database structure before launching Bonita. At execution, it: * creates database structure (tables) on target database. * writes all configuration files found in `platform_conf/initial` and licenses found in `platform_conf/licenses` in the database. ================================================ FILE: platform/platform-setup/src/main/resources/init_header.txt ================================================ Important: For basic bundle installations and starts, launch the `bonita-start`(.bat|.sh) script, at top level of Bonita bundles. It will create the database tables, configure the server using information from `database.properties`, and then start the server for you. So you normally don't need to run any of this `setup` script manually. However, for specific needs, here are details about `setup.(bat|sh) init`: ================================================ FILE: platform/platform-setup/src/main/resources/pull.txt ================================================ At execution, it: * writes the configuration currently in database in the folder platform_conf/current. * writes license files found in the database to the folder platform_conf/licenses (Subscription editions only). * overwrites any previous configuration and license files previously in platform_conf/current folder. Important: This step is mandatory before updating the configuration to ensure database consistency. For example, when a tenant is created by Bonita, its related configuration files need to be extracted before you can edit the configuration. ================================================ FILE: platform/platform-setup/src/main/resources/push.txt ================================================ At execution, it: * ensures no configuration folder has been deleted locally before pushing. Otherwise it stops and fails. * writes all configuration files found in platform_conf/current in database. * writes license files found in platform_conf/licenses folder in database (Subscription editions only). * overwrites any previous configuration in the database. Important: - Only use this command once the current configuration has been pulled from the database with `setup.(bat|sh) pull` script and modified locally. - Those modifications will be applied only after you restart Bonita. ================================================ FILE: platform/platform-setup/src/main/standalone/README.md ================================================ Bonita Platform Setup tool === What it does? --- **Bonita Platform Setup tool** sets up Bonita Platform before a Bonita runtime can be run: * it creates the structure of the database (tables, primary / foreign keys, indices, ...) * it inserts the default minimum data * it inserts the Bonita Engine + Bonita Portal minimum configuration in database * it also allows to automatically configure a Bonita Tomcat bundle to run on the right database Requirements --- > Java JDK 8 / 11 Running bonita-platform-setup --- To run the tool: * extract the Zip file * configure access to the database in file `database.properties` at the root directory. * run `./setup.sh [init | push | pull | configure | help]` More details --- See [the dedicated page](https://documentation.ofelia.com/bonita/latest/BonitaBPM_platform_setup) for more info about the Bonita Platform Setup tool. ================================================ FILE: platform/platform-setup/src/main/standalone/database.properties ================================================ #################################################################################### # # Modify the following values to suit your database needs. # Fore more information, see file ../HOW_TO_CONFIGURE_AND_RUN.txt # #################################################################################### ######################################### # Bonita database properties ######################################### # Valid values for Community edition: h2, postgres # Valid values for Enterprise editions: h2, postgres, sqlserver, oracle, mysql db.vendor=h2 # when using h2, no server or port setting is needed since connexion is made using file protocol mode using relative directory: db.server.name=SERVER_NAME db.server.port=SERVER_PORT # if your database name contains a backslash (\) character, you must double it (\\): db.database.name=bonita_journal.db db.user=sa # if your database password contains a backslash (\) character, you must double it (\\): db.password= ################################### # Business Data database properties ################################### # Valid values for Community edition: h2, postgres # Valid values for Enterprise editions: h2, postgres, sqlserver, oracle, mysql bdm.db.vendor=h2 bdm.db.server.name=SERVER_NAME bdm.db.server.port=SERVER_PORT bdm.db.database.name=business_data.db bdm.db.user=sa bdm.db.password= # IMPORTANT NOTE regarding H2 database: # in case you move whole setup folder to another directory, you must change property below # to point to original folder containing h2 database folder # new value can be relative or absolute since it still points to the right folder # WARNING for Windows users: keep forward slashes like below (instead of backslashes): h2.database.dir=../h2_database ================================================ FILE: platform/platform-setup/src/main/standalone/internal.properties ================================================ ################################################################################## # # # properties below must NOT be modified unless specific requirements # # # ################################################################################## # /!\ WARNING /!\ # # Any value containing a backslash (\) character MUST be doubled (\\) # ################################################################################## h2.nonXaDriver=org.h2.Driver h2.xaDriver=org.h2.jdbcx.JdbcDataSource h2.xaDSFactory=org.h2.jdbcx.JdbcDataSourceFactory postgres.nonXaDriver=org.postgresql.Driver postgres.xaDriver=org.postgresql.xa.PGXADataSource postgres.xaDSFactory=org.postgresql.xa.PGXADataSourceFactory ########################### ## Bonita database ########################### # h2 properties h2.url=jdbc:h2:file:${h2.database.dir}/${db.database.name};DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE; h2.testQuery=SELECT 1 # postgres properties postgres.url=jdbc:postgresql://${db.server.name}:${db.server.port}/${db.database.name} postgres.testQuery=SELECT 1 # spring properties spring.datasource.username=${db.user} spring.datasource.password=${db.password} spring.datasource.driver-class-name=${${db.vendor}.nonXaDriver} spring.datasource.url=${${db.vendor}.url} # The initial number of connections when the connection pool starts. connection-pool.initialSize=8 # The maximum number of active connections that can be allocated from this pool at the same time. connection-pool.maxTotal=50 # The minimum number of active connections that always established after pool created and connection has reached this size. connection-pool.minIdle=8 # The maximum number of connections that should be kept in the pool at all times. connection-pool.maxIdle=16 ########################### # Business Data database ########################### # h2 properties h2.bdm.url=jdbc:h2:file:${h2.database.dir}/${bdm.db.database.name};DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE; h2.bdm.testQuery=SELECT 1 # postgres properties postgres.bdm.url=jdbc:postgresql://${bdm.db.server.name}:${bdm.db.server.port}/${bdm.db.database.name} postgres.bdm.testQuery=SELECT 1 # The initial number of connections when the connection pool starts. bdm.connection-pool.initialSize=4 # The maximum number of active connections that can be allocated from this pool at the same time. bdm.connection-pool.maxTotal=20 # The minimum number of active connections that always established after pool created and connection has reached this size. bdm.connection-pool.minIdle=4 # The maximum number of connections that should be kept in the pool at all times. bdm.connection-pool.maxIdle=10 ================================================ FILE: platform/platform-setup/src/main/standalone/lib/PUT_YOUR_JDBC_DRIVER_HERE ================================================ ================================================ FILE: platform/platform-setup/src/main/standalone/logback.xml ================================================ [%level] %msg%n ================================================ FILE: platform/platform-setup/src/main/standalone/setup.bat ================================================ @echo off setlocal EnableDelayedExpansion :: Let's position into the folder containing this script: set CURRENTDIR="%cd%" set BASEDIR=%~dp0 cd %BASEDIR% :: Check if JAVA_CMD has been passed by start-bonita.bat: if not "%JAVA_CMD%" == "" goto gotJavaCmd set JAVA_CMD="java" :gotJavaCmd set CFG_FOLDER=%BASEDIR%\platform_conf set INITIAL_CFG_FOLDER=%CFG_FOLDER%\initial set LIB_FOLDER=%BASEDIR%\lib FOR /F "tokens=1,* delims== eol=#" %%A IN (database.properties) DO (set %%A=%%B) set BONITA_DATABASE=%db.vendor% set BONITA_BDM_DATABASE=%bdm.db.vendor% call :checkVendorSupported %BONITA_DATABASE% if ERRORLEVEL 1 ( exit /b 1 ) call :checkVendorSupported %BONITA_BDM_DATABASE% if ERRORLEVEL 1 ( exit /b 1 ) "%JAVA_CMD%" -cp "%BASEDIR%;%CFG_FOLDER%;%INITIAL_CFG_FOLDER%;%LIB_FOLDER%\*" ^ -Dsysprop.bonita.db.vendor=%BONITA_DATABASE% ^ -Dsysprop.bonita.bdm.db.vendor=%BONITA_BDM_DATABASE% ^ org.bonitasoft.platform.setup.PlatformSetupApplication %* if ERRORLEVEL 1 ( exit /b 1 ) :: restore previous folder: cd %CURRENTDIR% :: exit script with success exit /b 0 REM Function that exits with an error message if the vendor is not supported REM - first argument is the database vendor value to check :checkVendorSupported set DB_VENDOR=%1 set IS_SUPPORTED=false for %%d in (h2 postgres) do ( if "%%d" == "!DB_VENDOR!" ( set IS_SUPPORTED=true goto :breakLoop ) ) :breakLoop if "!IS_SUPPORTED!" == "false" ( echo ERROR: Unsupported database vendor ^(valid values are h2, postgres^). echo For access to additional databases ^(oracle, mysql, sqlserver^), please consider upgrading to the Enterprise Edition. echo Please update file %BASEDIR%database.properties to set a valid value. exit /b 1 ) exit /b 0 ================================================ FILE: platform/platform-setup/src/main/standalone/setup.sh ================================================ #!/bin/sh set -e # Function that exits with an error message if the vendor is not supported # - first argument is the database vendor value to check check_vendor_supported() { db_vendor=$1 is_supported=false # supported databases: set -- h2 postgres for db in "$@"; do if [ "$db" = "$db_vendor" ]; then is_supported=true break fi done if [ "$is_supported" = false ]; then echo "ERROR: Unsupported database vendor (valid values are h2, postgres)." echo "For access to additional databases (oracle, mysql, sqlserver), please consider upgrading to the Enterprise Edition." echo "Please update file ${BASEDIR}/database.properties to set a valid value." exit 1 fi } # Let's position into the folder containing this script: BASEDIR=$(cd "$(dirname "$(dirname "$0")/..")" && pwd -P) cd "${BASEDIR}" # JAVA_CMD is exported by start-bonita.sh, so that same Java command is used: JAVA_EXE=${JAVA_CMD:-java} CFG_FOLDER=${BASEDIR}/platform_conf INITIAL_CFG_FOLDER=$CFG_FOLDER/initial for lib in lib/*.jar; do LIBS_CP="${LIBS_CP}:${lib}" done BONITA_DATABASE=$(grep '^db.vendor=' database.properties | sed -e 's/db.vendor=//g') check_vendor_supported "$BONITA_DATABASE" BONITA_BDM_DATABASE=$(grep '^bdm.db.vendor=' database.properties | sed -e 's/bdm.db.vendor=//g') check_vendor_supported "$BONITA_BDM_DATABASE" "${JAVA_EXE}" -cp "${BASEDIR}:${CFG_FOLDER}:${INITIAL_CFG_FOLDER}${LIBS_CP}" \ -Dsysprop.bonita.db.vendor="${BONITA_DATABASE}" \ -Dsysprop.bonita.bdm.db.vendor="${BONITA_BDM_DATABASE}" \ org.bonitasoft.platform.setup.PlatformSetupApplication "$@" COD_RET=$? if [ ${COD_RET} -ne 0 ]; then cd - 1>/dev/null exit ${COD_RET} fi # restore previous folder: cd - 1>/dev/null ================================================ FILE: platform/platform-setup/src/main/standalone/tomcat-templates/bonita.xml ================================================ ================================================ FILE: platform/platform-setup/src/main/standalone/tomcat-templates/setenv.bat ================================================ @echo on rem Add the JAVA 9 specific start-up parameters required by Hazelcast set "JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-modules=java.se" set "JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-exports=java.base/jdk.internal.ref=ALL-UNNAMED" set "JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.base/java.nio=ALL-UNNAMED" set "JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.base/sun.nio.ch=ALL-UNNAMED" set "JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.management/sun.management=ALL-UNNAMED" set "JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED" rem Add the JAVA 9 specific start-up parameters required by Xstream serialization set "JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.base/java.time=ALL-UNNAMED" set "JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.base/java.time.chrono=ALL-UNNAMED" set "JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.base/java.text=ALL-UNNAMED" rem Add the JAVA 9 specific start-up parameters required by Webservice connector/Xstream serialization set "JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.xml/com.sun.org.apache.xerces.internal.dom=ALL-UNNAMED" set "JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.xml/com.sun.org.apache.xerces.internal.xni=ALL-UNNAMED" rem Add the JAVA 9 specific start-up parameters required by Salesforce connector/Xstream serialization set "JDK_JAVA_OPTIONS=%JDK_JAVA_OPTIONS% --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED" rem Set some JVM system properties required by Bonita rem This variable is automatically taken into account by catalina.bat: set LOGGING_MANAGER=-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager set LOG_CONF_FILE_PATH="-Dlog4j.configurationFile=%CATALINA_BASE%\conf\log4j2-appenders.xml,%CATALINA_BASE%\conf\log4j2-loggers.xml" set PLATFORM_SETUP="-Dorg.bonitasoft.platform.setup.folder=%CATALINA_HOME%\..\setup" set H2_DATABASE_DIR="-Dorg.bonitasoft.h2.database.dir=%CATALINA_HOME%\..\h2_database" set INCIDENT_LOG_DIR="-Dorg.bonitasoft.engine.incident.folder=%CATALINA_HOME%\logs" rem Define the RDMBS vendor use by Bonita Engine to store data. Valid values are: h2, postgres, sqlserver, oracle, mysql. set DB_OPTS="-Dsysprop.bonita.db.vendor=h2" rem Define the RDMBS vendor use by Bonita Engine to store Business Data. Valid values are: h2, postgres, sqlserver, oracle, mysql. rem If you use different DB engines by tenants, please update directly bonita-tenant-community-custom.properties set BDM_DB_OPTS="-Dsysprop.bonita.bdm.db.vendor=h2" rem Arjuna (JTA service added to Tomcat and required by Bonita Engine for transaction management) set ARJUNA_OPTS="-Dcom.arjuna.ats.arjuna.common.propertiesFile=%CATALINA_HOME%\conf\jbossts-properties.xml" rem use env variable BONITA_RUNTIME_TRANSACTION_XATIMEOUT=180 to override default XA Transaction timeout (in seconds): if not "%BONITA_RUNTIME_TRANSACTION_XATIMEOUT%" == "" (set XA_TIMEOUT_OPTS="-Dbonita.runtime.transaction.xa-timeout=%BONITA_RUNTIME_TRANSACTION_XATIMEOUT%") rem Optional JAAS configuration. Usually used when delegating authentication to LDAP / Active Directory server rem set SECURITY_OPTS="-Djava.security.auth.login.config=%CATALINA_HOME%\conf\jaas-standard.cfg" rem Optional JMX remote access Configuration. Used to enable remote JMX agent in tomcat to monitor Heap Memory, Threads, CPU Usage, Classes, and configure various MBeans. if "%JMX_REMOTE_ACCESS%" == "true" (set JMX_REMOTE_ACCESS_OPTS="-Dcom.sun.management.jmxremote.port=9000 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.password.file=%CATALINA_HOME%\conf\jmxremote.password -Dcom.sun.management.jmxremote.access.file=%CATALINA_HOME%\conf\jmxremote.access") rem Pass the JVM system properties to Tomcat JVM using CATALINA_OPTS variable set CATALINA_OPTS=%CATALINA_OPTS% %LOG_CONF_FILE_PATH% %PLATFORM_SETUP% %XA_TIMEOUT_OPTS% %H2_DATABASE_DIR% %DB_OPTS% %BDM_DB_OPTS% %ARJUNA_OPTS% %INCIDENT_LOG_DIR% %JMX_REMOTE_ACCESS_OPTS% -Dfile.encoding=UTF-8 -Xshare:auto -Xms1024m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -Dlog4j2.formatMsgNoLookups=true set CATALINA_PID=%CATALINA_BASE%\catalina.pid @rem extra lib at Tomcat startup set CLASSPATH="%CATALINA_HOME%\lib\ext\*" ================================================ FILE: platform/platform-setup/src/main/standalone/tomcat-templates/setenv.sh ================================================ #!/bin/sh # Add the JAVA 9 specific start-up parameters required by Hazelcast JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-modules=java.se" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-exports=java.base/jdk.internal.ref=ALL-UNNAMED" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.nio=ALL-UNNAMED" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/sun.nio.ch=ALL-UNNAMED" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.management/sun.management=ALL-UNNAMED" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED" # Add the JAVA 9 specific start-up parameters required by Xstream serialization JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.time=ALL-UNNAMED" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.time.chrono=ALL-UNNAMED" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.text=ALL-UNNAMED" # Add the JAVA 9 specific start-up parameters required by Webservice connector/Xstream serialization JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.xml/com.sun.org.apache.xerces.internal.dom=ALL-UNNAMED" JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.xml/com.sun.org.apache.xerces.internal.xni=ALL-UNNAMED" # Add the JAVA 9 specific start-up parameters required by Salesforce connector/Xstream serialization JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED" # Set some JVM system properties required by Bonita # This variable is automatically taken into account by catalina.sh LOGGING_MANAGER="-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager" LOG_CONF_FILE_PATH="-Dlog4j.configurationFile=${CATALINA_BASE}/conf/log4j2-appenders.xml,${CATALINA_BASE}/conf/log4j2-loggers.xml" PLATFORM_SETUP="-Dorg.bonitasoft.platform.setup.folder=${CATALINA_HOME}/../setup" H2_DATABASE_DIR="-Dorg.bonitasoft.h2.database.dir=${CATALINA_HOME}/../h2_database" INCIDENT_LOG_DIR="-Dorg.bonitasoft.engine.incident.folder=${CATALINA_HOME}/logs" # Define the RDMBS vendor use by Bonita Engine to store data. Valid values are: h2, postgres, sqlserver, oracle, mysql. DB_OPTS="-Dsysprop.bonita.db.vendor=h2" # Define the RDMBS vendor use by Bonita Engine to store Business Data. Valid values are: h2, postgres, sqlserver, oracle, mysql. # If you use different DB engines by tenants, please update directly bonita-tenant-community-custom.properties BDM_DB_OPTS="-Dsysprop.bonita.bdm.db.vendor=h2" # Arjuna (JTA service added to Tomcat and required by Bonita Engine for transaction management) ARJUNA_OPTS="-Dcom.arjuna.ats.arjuna.common.propertiesFile=${CATALINA_HOME}/conf/jbossts-properties.xml" # use env variable BONITA_RUNTIME_TRANSACTION_XATIMEOUT=180 to override default XA Transaction timeout (in seconds): XA_TIMEOUT_OPTS="-Dbonita.runtime.transaction.xa-timeout=${BONITA_RUNTIME_TRANSACTION_XATIMEOUT:-180}" # Optional JAAS configuration. Usually used when delegating authentication to LDAP / Active Directory server #SECURITY_OPTS="-Djava.security.auth.login.config=${CATALINA_HOME}/conf/jaas-standard.cfg" # Optional JMX remote access Configuration. Used to enable remote JMX agent in tomcat to monitor Heap Memory, Threads, CPU Usage, Classes, and configure various MBeans. if [ "$JMX_REMOTE_ACCESS" = 'true' ]; then JMX_REMOTE_ACCESS_OPTS="-Dcom.sun.management.jmxremote.port=9000 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.password.file=${CATALINA_HOME}/conf/jmxremote.password -Dcom.sun.management.jmxremote.access.file=${CATALINA_HOME}/conf/jmxremote.access" fi # Pass the JVM system properties to Tomcat JVM using CATALINA_OPTS variable CATALINA_OPTS="${CATALINA_OPTS} ${LOG_CONF_FILE_PATH} ${PLATFORM_SETUP} ${XA_TIMEOUT_OPTS} ${H2_DATABASE_DIR} ${DB_OPTS} ${BDM_DB_OPTS} ${ARJUNA_OPTS} ${INCIDENT_LOG_DIR} ${JMX_REMOTE_ACCESS_OPTS} -Dfile.encoding=UTF-8 -Xshare:auto -Xms1024m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -Djava.security.egd=file:/dev/./urandom -Dlog4j2.formatMsgNoLookups=true" export CATALINA_OPTS # Only set CATALINA_PID if not already set (check for empty value) by startup script (usually done by /etc/init.d/tomcat8 but not by startup.sh nor catalina.sh) if [ -z ${CATALINA_PID+x} ]; then CATALINA_PID=${CATALINA_BASE}/catalina.pid; export CATALINA_PID; fi # extra lib required at Tomcat startup export CLASSPATH="${CATALINA_HOME}/lib/ext/*" ================================================ FILE: platform/platform-setup/src/test/e2e/clean-postgres.sh ================================================ #!/bin/sh echo "========================================" echo "disconnect users" echo "========================================" psql postgresql://postgres:postgres@localhost:5432/postgres < pg_backend_pid()) EOF echo "========================================" echo "drop DB" echo "========================================" psql postgresql://postgres:postgres@localhost:5432/postgres < /dev/null echo "=============================================" echo "Start the Postgres database docker container" echo "=============================================" docker run --rm -p 5432:5432 --name bonita-postgres -d bonitasoft/bonita-postgres:16.4 export VERSION="$(cat ../platform-resources/build/resources/main/PLATFORM_ENGINE_VERSION)" echo "========================================" echo "version: ${VERSION}" echo "========================================" export E2E_DIR="build/e2e-postgres-bos" export ZIP=Bonita-platform-setup-${VERSION}.zip unzip -o -q -d ${E2E_DIR} build/distributions/${ZIP} echo "Setting 'org.bonitasoft.platform.setup' log level to DEBUG" # Activate in debug development phase: #sed -i "s/org.bonitasoft.platform.setup\" level=\"INFO/org.bonitasoft.platform.setup\" level=\"DEBUG/g" ${E2E_DIR}/logback.xml #sed -i "s/org.bonitasoft.platform\" level=\"INFO/org.bonitasoft.platform\" level=\"DEBUG/g" ${E2E_DIR}/logback.xml #sed -i "s/PlatformSetupApplication\" level=\"WARN/PlatformSetupApplication\" level=\"DEBUG/g" ${E2E_DIR}/logback.xml echo "==================================================================================================" echo "Default H2 configuration detected should NOT ask for confirmation if sysprop 'h2.noconfirm' is set" echo "==================================================================================================" ${E2E_DIR}/setup.sh init -Dh2.noconfirm testReturnCode $? "setup.sh init with default H2 configuration auto-confirmed with System property" echo "=============================================================" echo "Default H2 configuration detected should ask for confirmation" echo "=============================================================" ${E2E_DIR}/setup.sh init < /dev/null testReturnCode $? "Configuring bonita.xml file with Postgres" cat build/server/bin/setenv.sh | grep "\-Dsysprop.bonita.db.vendor=postgres" > /dev/null testReturnCode $? "Configuring setenv.sh file with Postgres" echo "========================================" echo "should store to database " echo "========================================" ${E2E_DIR}/setup.sh init testReturnCode $? "setup.sh init" echo "========================================" echo "check tables" echo "========================================" docker exec bonita-postgres psql postgresql://bonita:bpm@localhost:5432/bonita -c " select t.table_catalog, t.table_schema, t.table_name from information_schema.tables t where t.table_schema='public' order by t.table_name;" echo "========================================" echo "check platform data" echo "========================================" docker exec bonita-postgres psql postgresql://bonita:bpm@localhost:5432/bonita -c " SELECT p.id, p.version, p.initial_bonita_version, p.created_by, TO_CHAR( TO_TIMESTAMP( p.created / 1000 ), 'DD/MM/YYYY HH24:MI:SS' ) as creation_date, maintenance_enabled FROM platform p" echo "========================================" echo "check configuration" echo "========================================" docker exec bonita-postgres psql postgresql://bonita:bpm@localhost:5432/bonita -c " SELECT c.content_type, c.resource_name FROM configuration c ORDER BY c.content_type, c.resource_name" # #echo "========================================" #echo "simulation of engine start" #echo "========================================" #docker exec bonita-postgres psql postgresql://bonita:bpm@localhost:5432/bonita -c " #INSERT # INTO # configuration( # content_type, # resource_name, # resource_content # ) SELECT # 'TENANT_SECURITY_SCRIPTS', # c.resource_name, # c.resource_content # FROM # configuration c # WHERE c.content_type ='TENANT_SECURITY_SCRIPTS'" echo "================================================================================" echo "simulate a version upgrade (configuration files have changed in folder initial/)" echo "================================================================================" echo "dynamic-permissions-checks" > ${E2E_DIR}/platform_conf/initial/tenant_portal/dynamic-permissions-checks.properties echo "resources-permissions-mapping" > ${E2E_DIR}/platform_conf/initial/tenant_portal/resources-permissions-mapping.properties echo "compound-permissions-mapping" > ${E2E_DIR}/platform_conf/initial/tenant_portal/compound-permissions-mapping.properties ${E2E_DIR}/setup.sh init testReturnCode $? "setup.sh init" echo "===========================================" echo "retrieve configuration (to default folder) " echo "===========================================" ${E2E_DIR}/setup.sh pull echo "==================================================================================" echo "verify version upgrade has updated configuration file changes (in folder current/)" echo "==================================================================================" new_content=`cat ${E2E_DIR}/platform_conf/current/tenant_portal/dynamic-permissions-checks.properties` testValue $new_content "dynamic-permissions-checks" res_mapp=`cat ${E2E_DIR}/platform_conf/current/tenant_portal/resources-permissions-mapping.properties` testValue $res_mapp "resources-permissions-mapping" compound=`cat ${E2E_DIR}/platform_conf/current/tenant_portal/compound-permissions-mapping.properties` testValue $compound "compound-permissions-mapping" echo "=> Verification Ok" echo "========================================" echo "remove some files & push" echo "========================================" rm -rf ${E2E_DIR}/platform_conf/current mkdir -p ${E2E_DIR}/platform_conf/current/platform_engine cp ${E2E_DIR}/platform_conf/initial/platform_engine/bonita-platform-custom.xml ${E2E_DIR}/platform_conf/current/platform_engine tree ${E2E_DIR}/platform_conf/current ${E2E_DIR}/setup.sh push echo "===========================================" echo "setup.sh push should have failed just above" echo "Now let's FORCE push it" echo "===========================================" ${E2E_DIR}/setup.sh push --force testReturnCode $? "setup.sh push --force should be successful" echo "============================================" echo "Backup folder should be created when pushing" echo "============================================" if [ "`ls ${E2E_DIR}/platform_conf/ | grep 'backup-'`" = "" ]; then echo 'ERROR. Should have created backup folder'; exit 52 else echo 'OK. Backup folder present.' fi echo "========================================" echo "should contain only bonita-platform-custom.xml:" tree ${E2E_DIR}/platform_conf/current echo "========================================" echo "========================================" echo "should fail if driver class cannot be loaded:" echo "========================================" sed -i s/^postgres.nonXaDriver=.*$/postgres.nonXaDriver=org.UnknownClass/g ${E2E_DIR}/internal.properties ${E2E_DIR}/setup.sh push echo "========================================" echo "should fail if drivers not found:" echo "========================================" sed -i s/^postgres.nonXaDriver=org.UnknownClass*$/postgres.nonXaDriver=org.postgresql.Driver/g ${E2E_DIR}/internal.properties rm ${E2E_DIR}/lib/postgres*.jar ${E2E_DIR}/setup.sh push --debug echo "========================================" echo "Docker database cleanup" echo "========================================" docker rm -vf bonita-postgres echo "========================================" echo "Test cleanup" echo "========================================" echo "Cleaning up $(cd ${E2E_DIR} && pwd) and $(cd ./build/h2_database && pwd)" rm -Rf ${E2E_DIR} rm -Rf ./build/h2_database echo "========================================" echo "END" echo "========================================" ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/configuration/impl/ConfigurationServiceImplIT.java ================================================ /** * 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.platform.configuration.impl; import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.jdbc.datasource.init.ScriptUtils.*; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.Connection; import javax.sql.DataSource; import org.bonitasoft.platform.configuration.model.BonitaConfiguration; import org.bonitasoft.platform.configuration.util.FolderComparator; import org.bonitasoft.platform.setup.PlatformSetupApplication; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.PropertySource; import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.support.EncodedResource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.init.ScriptUtils; import org.springframework.stereotype.Component; import org.springframework.test.context.junit4.SpringRunner; /** * @author laurent Leseigneur */ @RunWith(SpringRunner.class) //keep order @SpringBootTest(classes = { PlatformSetupApplication.class }) @ComponentScan(basePackages = { "org.bonitasoft.platform.setup", "org.bonitasoft.platform.configuration" }) @PropertySource("classpath:/application.properties") @Component public class ConfigurationServiceImplIT { @Autowired JdbcTemplate jdbcTemplate; @Value("${db.vendor}") private String dbVendor; @Autowired ConfigurationServiceImpl configurationService; @Autowired DataSource dataSource; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Before public void setUpDb() throws Exception { dropTables(); createTables(); } @After public void cleanUpDB() throws Exception { dropTables(); } @Test public void should_store_configuration() throws Exception { //given Path configFolder = Paths.get(getClass().getResource("/conf").toURI()); //when configurationService.storePlatformConfiguration(configFolder.toFile()); //then assertThat(configurationService.getPlatformEngineConf()).as("should retrieve configuration") .extracting("resourceName") .containsOnly("bonita-platform-community.properties", "bonita-platform-custom.xml"); } @Test public void should_write_configuration_to_fileSystem() throws Exception { //given Path configFolder = Paths.get(getClass().getResource("/conf").toURI()); configurationService.storePlatformConfiguration(configFolder.toFile()); //when final File destFolder = temporaryFolder.newFolder(); final File licFolder = temporaryFolder.newFolder(); configurationService.writeAllConfigurationToFolder(destFolder, licFolder); //then assertThat(destFolder).as("should retrieve config files") .exists() .isDirectory(); new FolderComparator().compare(configFolder.toFile(), destFolder); } @Test public void should_store_licenses() throws Exception { //given File licenses = createLicenseFolder(); //when configurationService.storeLicenses(licenses); //then BonitaConfiguration expectedLicense1 = new BonitaConfiguration("license1.lic", "license 1 content".getBytes(UTF_8)); BonitaConfiguration expectedLicense2 = new BonitaConfiguration("license2.lic", "license 2 content".getBytes(UTF_8)); assertThat(configurationService.getLicenses()).as("should retrieve configuration") .containsOnly(expectedLicense1, expectedLicense2); } @Test public void should_store_licenses_override_previous_licenses() throws Exception { //given configurationService.storeLicenses(createLicenseFolder()); //when configurationService.storeLicenses(createNewLicenseFolder()); //then BonitaConfiguration newLicense2 = new BonitaConfiguration("license2.lic", "new license 2 content".getBytes(UTF_8)); BonitaConfiguration newLicense3 = new BonitaConfiguration("license3.lic", "license 3 content".getBytes(UTF_8)); assertThat(configurationService.getLicenses()).as("should retrieve configuration") .containsOnly(newLicense2, newLicense3); } private void createTables() throws Exception { final InputStream createTableResource = this.getClass() .getResourceAsStream("/sql/" + dbVendor + "/createTables.sql"); try (Connection connection = getConnection()) { ScriptUtils.executeSqlScript(connection, new EncodedResource(new InputStreamResource(createTableResource)), false, false, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER); } } private void dropTables() throws Exception { final InputStream dropTablesResource = this.getClass() .getResourceAsStream("/sql/" + dbVendor + "/dropTables.sql"); try (Connection connection = getConnection()) { ScriptUtils.executeSqlScript(connection, new EncodedResource(new InputStreamResource(dropTablesResource)), true, true, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER); } } private File createLicenseFolder() throws IOException { File licenses = temporaryFolder.newFolder("licenses"); Files.write(licenses.toPath().resolve("license1.lic"), "license 1 content".getBytes(UTF_8)); Files.write(licenses.toPath().resolve("license2.lic"), "license 2 content".getBytes(UTF_8)); Files.write(licenses.toPath().resolve("not_a_license.txt"), "*".getBytes(UTF_8)); Path subFolder = licenses.toPath().resolve("subFolder"); Files.createDirectories(licenses.toPath().resolve("subFolder")); Files.write(subFolder.resolve("ignoreMe.lic"), "ignore this license content".getBytes(UTF_8)); return licenses; } private File createNewLicenseFolder() throws IOException { File newLicenses = temporaryFolder.newFolder("newLicenses"); Files.write(newLicenses.toPath().resolve("license2.lic"), "new license 2 content".getBytes(UTF_8)); Files.write(newLicenses.toPath().resolve("license3.lic"), "license 3 content".getBytes(UTF_8)); return newLicenses; } private Connection getConnection() throws Exception { return dataSource.getConnection(); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/configuration/util/FolderComparator.java ================================================ /** * 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.platform.configuration.util; import static org.apache.commons.io.FilenameUtils.getExtension; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.IOException; import java.nio.file.Files; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.commons.io.comparator.ExtensionFileComparator; import org.assertj.core.api.Assertions; import org.custommonkey.xmlunit.DetailedDiff; import org.custommonkey.xmlunit.XMLUnit; /** * @author Laurent Leseigneur */ public class FolderComparator { private static final int ARE_EQUALS = 0; private static final String XML = "xml"; private static final String PROPERTIES = "properties"; public void compare(File configFolder, File destinationFolder) throws Exception { final Map expectedFiles = flattenFolderFiles(configFolder); final Map files = flattenFolderFiles(destinationFolder); Assertions.assertThat(files).as("should have same size").hasSize(expectedFiles.size()); Assertions.assertThat(expectedFiles.keySet()).as("should have same file names").isEqualTo(files.keySet()); for (String name : expectedFiles.keySet()) { compareFileContent(expectedFiles.get(name), files.get(name)); } } private Map flattenFolderFiles(File folder) throws IOException { Map fileMap = new HashMap<>(); final FlattenFolderVisitor flattenFolderVisitor = new FlattenFolderVisitor(fileMap); Files.walkFileTree(folder.toPath(), flattenFolderVisitor); return fileMap; } private void compareFileContent(File expectedFile, File givenFile) throws Exception { final String givenFileAbsolutePath = givenFile.getAbsolutePath(); final String expectedFileExtension = getExtension(expectedFile.getName()); final String expectedFileAbsolutePath = expectedFile.getAbsolutePath(); Assertions.assertThat(new ExtensionFileComparator().compare(expectedFile, givenFile)) .as(expectedFileAbsolutePath + " and " + givenFileAbsolutePath + " should have same extension") .isEqualTo(ARE_EQUALS); switch (expectedFileExtension) { case PROPERTIES: Assertions.assertThat(getProperties(expectedFile)) .as(expectedFileAbsolutePath + " and " + givenFileAbsolutePath + " should contain same properties") .isEqualTo(getProperties(givenFile)); break; case XML: final List allDifferences = new DetailedDiff( XMLUnit.compareXML(new FileReader(givenFile), new FileReader(expectedFile))) .getAllDifferences(); Assertions.assertThat(allDifferences).as("should xml file be equals").isEmpty(); break; default: Assertions.fail("unexpected file:" + expectedFile.getAbsolutePath()); break; } } private Properties getProperties(File propertyFile) throws IOException { final Properties properties = new Properties(); try (FileInputStream fileInputStream = new FileInputStream(propertyFile)) { properties.load(fileInputStream); return properties; } } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/ConfigurationCheckerTest.java ================================================ /** * 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.platform.setup; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import java.util.Properties; import org.bonitasoft.platform.exception.PlatformException; import org.bonitasoft.platform.setup.command.configure.PropertyLoader; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.ClearSystemProperties; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.junit.rules.TestRule; /** * @author Emmanuel Duchastenier */ public class ConfigurationCheckerTest { private static final String TEST_DATASOURCE_CONFIG_DIR = "/datasource-config/"; @Rule public TestRule clean = new ClearSystemProperties("db.admin.user", "sysprop.bonita.db.vendor", "db.user", "db.password", "db.vendor", "db.server.name=", "db.admin.password", "sysprop.bonita.bdm.db.vendor", "db.server.port", "db.database.name"); @Rule public final ExpectedException expectedException = ExpectedException.none(); @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void validate_should_load_class_if_present() throws Exception { // Default org.h2.Driver should be found: new ConfigurationChecker(new PropertyLoader().loadProperties()).validate(); } @Test public void validate_should_fail_to_load_class_if_not_found() throws Exception { final ConfigurationChecker configurationChecker = new ConfigurationChecker( new PropertyLoader(TEST_DATASOURCE_CONFIG_DIR + "database.properties", TEST_DATASOURCE_CONFIG_DIR + "missingDriverClass_internal.properties").loadProperties()); configurationChecker.loadProperties(); expectedException.expect(PlatformException.class); expectedException.expectMessage("The driver class named 'org.404.NonExistent'"); configurationChecker.validate(); } @Test public void validate_should_fail_mandatory_property_is_not_set() throws Exception { final String dbVendor = "dbVendor"; System.setProperty(PlatformSetup.BONITA_DB_VENDOR_PROPERTY, dbVendor); final Properties propertiesWithMissingServerName = new PropertyLoader( TEST_DATASOURCE_CONFIG_DIR + "incomplete_database.properties") .loadProperties(); expectedException.expect(PlatformException.class); expectedException.expectMessage("Mandatory property"); new ConfigurationChecker(propertiesWithMissingServerName).validate(); } @Test public void validate_should_fail_if_class_to_load_is_not_set() throws Exception { final ConfigurationChecker configurationChecker = new ConfigurationChecker( new PropertyLoader(TEST_DATASOURCE_CONFIG_DIR + "database.properties", TEST_DATASOURCE_CONFIG_DIR + "incomplete_internal.properties").loadProperties()); expectedException.expect(PlatformException.class); expectedException.expectMessage("Mandatory property 'postgres.nonXaDriver'"); configurationChecker.validate(); } @Test public void validate_should_check_driver_loadability() throws Exception { final ConfigurationChecker checker = spy(new ConfigurationChecker(new PropertyLoader().loadProperties())); checker.validate(); verify(checker).tryToLoadDriverClass(); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/PlatformSetupApplicationTest.java ================================================ /** * 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.platform.setup; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.ExpectedSystemExit; import org.junit.contrib.java.lang.system.SystemErrRule; import org.junit.contrib.java.lang.system.SystemOutRule; /** * @author Laurent Leseigneur */ public class PlatformSetupApplicationTest { @Rule public SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests(); @Rule public SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests(); @Rule public ExpectedSystemExit systemExit = ExpectedSystemExit.none(); @Test public void should_gracefully_fail_with_error_message_when_null_action() { //then systemExit.expectSystemExitWithStatus(1); //when PlatformSetupApplication.main(null); } @Test public void should_gracefully_fail_with_error_message_when_action_unknown() { systemExit.expectSystemExitWithStatus(1); systemExit.checkAssertionAfterwards( () -> assertThat(systemOutRule.getLog()).contains("no command named: wrong value")); PlatformSetupApplication.main(new String[] { "wrong value" }); } @Test public void main_should_accept_configure_action() { systemExit.expectSystemExitWithStatus(0); PlatformSetupApplication.main(new String[] { "configure" }); } @Test public void should_show_error_if_no_command_specified() { //then systemExit.expectSystemExitWithStatus(1); systemExit.checkAssertionAfterwards(() -> { assertThat(systemOutRule.getLog()).contains("Need to specify a command, see usage above."); assertThat(systemOutRule.getLog()) .contains("usage: setup ( init | configure | pull | push ) [-D ]"); }); //when PlatformSetupApplication.main(new String[] {}); } @Test public void should_show_help_of_a_command() { //then systemExit.expectSystemExitWithStatus(0); systemExit.checkAssertionAfterwards(() -> assertThat(systemOutRule.getLog()) .contains("Run this init command once to create database structure")); //when PlatformSetupApplication.main(new String[] { "help", "init" }); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/PlatformSetupDistributionIT.java ================================================ /** * 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.platform.setup; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.platform.setup.command.configure.BundleConfiguratorTest.checkFileContains; import static org.junit.Assert.assertNotNull; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.io.FileUtils; import org.apache.commons.io.file.PathUtils; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.ClearSystemProperties; import org.junit.rules.TemporaryFolder; import org.junit.rules.TestRule; /** * @author Baptiste Mesta */ public class PlatformSetupDistributionIT { private File setupFolder; @Rule public TestRule clean = new ClearSystemProperties("db.admin.user", "sysprop.bonita.db.vendor", "db.user", "db.password", "db.vendor", "db.server.name", "db.admin.password", "sysprop.bonita.bdm.db.vendor", "db.server.port", "db.database.name"); @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Before public void before() throws Exception { setupFolder = temporaryFolder.newFolder(); PlatformSetupTestUtils.extractDistributionTo(setupFolder); if (!PlatformSetupTestUtils.isCommunityEdition(getClass())) { // Tests executed in the subscription edition need a fake license file Files.createFile(Paths.get(setupFolder.getAbsolutePath()).resolve("platform_conf").resolve("licenses") .resolve("some.lic")); } } @Test public void setupSh_should_work_with_init_on_h2_and_prevent_pushing_deletion() throws Exception { //given CommandLine oCmdLine = PlatformSetupTestUtils.createCommandLine(); oCmdLine.addArgument("init"); DefaultExecutor executor = PlatformSetupTestUtils.createExecutor(setupFolder); executor.setStreamHandler(PlatformSetupTestUtils.getExecuteStreamHandler("yes")); //when int iExitValue = executor.execute(oCmdLine); //then assertThat(iExitValue).isZero(); Connection jdbcConnection = PlatformSetupTestUtils.getJdbcConnection(setupFolder); Statement statement = jdbcConnection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT COUNT(*) AS nb FROM CONFIGURATION"); resultSet.next(); assertThat(resultSet.getInt("nb")).isPositive(); oCmdLine = PlatformSetupTestUtils.createCommandLine(); oCmdLine.addArgument("pull"); iExitValue = executor.execute(oCmdLine); assertThat(iExitValue).isZero(); final Path platformEngine = setupFolder.toPath().resolve("platform_conf").resolve("current") .resolve("platform_engine"); FileUtils.deleteDirectory(platformEngine.toFile()); oCmdLine = PlatformSetupTestUtils.createCommandLine(); oCmdLine.addArgument("push"); executor.setExitValue(1); iExitValue = executor.execute(oCmdLine); assertThat(iExitValue).isEqualTo(1); oCmdLine.addArgument("--force"); executor.setExitValue(0); iExitValue = executor.execute(oCmdLine); assertThat(iExitValue).isZero(); } @Test public void setupSh_should_work_with_init_on_h2_with_overridden_system_property() throws Exception { //given CommandLine oCmdLine = PlatformSetupTestUtils.createCommandLine(); oCmdLine.addArguments("init -Ddb.user=myUser"); DefaultExecutor executor = PlatformSetupTestUtils.createExecutor(setupFolder); executor.setStreamHandler(PlatformSetupTestUtils.getExecuteStreamHandler("Y")); //when int iExitValue = executor.execute(oCmdLine); //then assertThat(iExitValue).isZero(); Connection jdbcConnection = PlatformSetupTestUtils.getJdbcConnection(setupFolder, "myUser"); Statement statement = jdbcConnection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT COUNT(*) AS nb FROM CONFIGURATION"); resultSet.next(); assertThat(resultSet.getInt("nb")).isPositive(); } @Test public void setupSh_should_have_error_with_no_argument() throws Exception { //given CommandLine oCmdLine = PlatformSetupTestUtils.createCommandLine(); DefaultExecutor oDefaultExecutor = PlatformSetupTestUtils.createExecutor(setupFolder); oDefaultExecutor.setExitValue(1); //when int iExitValue = oDefaultExecutor.execute(oCmdLine); //then assertThat(iExitValue).isEqualTo(1); } @Test public void commandLine_should_support_H2_path_with_space_characters() throws Exception { //setup final File temporaryFolderRoot = temporaryFolder.newFolder(); Path bundleFolder = temporaryFolderRoot.toPath().toRealPath(); Path tomcatFolder = bundleFolder.resolve("server"); var tomcatServerFolder = getClass().getResource("/tomcat_conf/server"); assertNotNull(tomcatServerFolder); PathUtils.copyDirectory(Paths.get(tomcatServerFolder.toURI()), tomcatFolder); final File newSetupFolder = bundleFolder.resolve("setup").toFile(); FileUtils.copyDirectory(setupFolder, newSetupFolder); //given CommandLine oCmdLine = PlatformSetupTestUtils.createCommandLine(); oCmdLine.addArguments("configure"); oCmdLine.addArguments("-Dh2.database.dir=\"my h2 path\"", false); DefaultExecutor executor = PlatformSetupTestUtils.createExecutor(newSetupFolder); executor.setStreamHandler(PlatformSetupTestUtils.getExecuteStreamHandler("yes")); //when int iExitValue = executor.execute(oCmdLine); //then assertThat(iExitValue).isZero(); checkFileContains(tomcatFolder.resolve("conf").resolve("Catalina").resolve("localhost").resolve("bonita.xml"), "/my h2 path/"); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/PlatformSetupIT.java ================================================ /** * Copyright (C) 2016 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.platform.setup; import static java.lang.System.lineSeparator; import static org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS; import static org.assertj.core.api.Assertions.*; import static org.bonitasoft.platform.configuration.type.ConfigurationType.PLATFORM_ENGINE; import static org.bonitasoft.platform.configuration.type.ConfigurationType.TENANT_PORTAL; import static org.bonitasoft.platform.setup.PlatformSetup.BONITA_SETUP_FOLDER; import static org.bonitasoft.platform.setup.PlatformSetup.PLATFORM_CONF_FOLDER_NAME; import static org.junit.jupiter.api.Assumptions.assumeFalse; import java.io.File; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.commons.io.FileUtils; import org.bonitasoft.platform.configuration.model.FullBonitaConfiguration; import org.bonitasoft.platform.configuration.type.ConfigurationType; import org.bonitasoft.platform.configuration.util.AllConfigurationResourceVisitor; import org.bonitasoft.platform.exception.PlatformException; import org.bonitasoft.platform.setup.jndi.MemoryJNDISetup; import org.bonitasoft.platform.util.ConfigurationFolderUtil; import org.bonitasoft.platform.version.VersionService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.io.TempDir; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.jdbc.JdbcTestUtils; /** * @author Baptiste Mesta */ @ExtendWith({ SpringExtension.class, OutputCaptureExtension.class }) @SpringBootTest(classes = { PlatformSetupApplication.class }) class PlatformSetupIT { @Value("${db.vendor}") private String dbVendor; @Autowired MemoryJNDISetup memoryJNDISetup; @Autowired JdbcTemplate jdbcTemplate; @Autowired PlatformSetup platformSetup; @Autowired VersionService versionService; private final ConfigurationFolderUtil configurationFolderUtil = new ConfigurationFolderUtil(); @BeforeEach void setUp() throws Exception { System.clearProperty(BONITA_SETUP_FOLDER); platformSetup.destroy(); } @Test @Tag("community-only") void init_method_should_init_table_and_insert_conf() throws Exception { //when platformSetup.init(); //then final Integer sequences = jdbcTemplate.queryForObject("select count(*) from sequence", Integer.class); assertThat(sequences).isGreaterThan(1); final List platformRows = jdbcTemplate.queryForList("SELECT information FROM platform", String.class); assertThat(platformRows).hasSize(1); assertThat(platformRows.get(0)).isNotBlank(); // In Community, should contain the initial case counter value for information final int configurationFiles = JdbcTestUtils.countRowsInTable(jdbcTemplate, "configuration"); assertThat(configurationFiles).isGreaterThan(1); } @Test void init_method_should_init_configuration_from_folder_if_exists(@TempDir Path setupFolder, CapturedOutput capturedOutput) throws Exception { //given System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toAbsolutePath().toString()); FileUtils.write(setupFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve("initial") .resolve("platform_engine") .resolve("whatever.properties").toFile(), "custom content", Charset.defaultCharset()); configurationFolderUtil.buildSqlFolder(setupFolder, dbVendor); //when platformSetup.init(); //then List> rows = jdbcTemplate .queryForList("SELECT * FROM configuration WHERE resource_name = 'whatever.properties'"); assertThat(rows).hasSize(1); assertThat(rows.get(0)).containsEntry("resource_content", "custom content".getBytes()); assertThat(capturedOutput.getOut()) .contains("Database will be initialized with configuration files from folder: " + setupFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve("initial")); } @Test @Tag("community-only") void init_method_should_store_tenant_portal_resources_from_classpath() throws Exception { //when platformSetup.init(); //then List> rows = jdbcTemplate .queryForList("SELECT * FROM configuration WHERE content_type= '" + ConfigurationType.TENANT_PORTAL + "' ORDER BY resource_name"); assertThat(rows).hasSize(9); int rowId = 0; assertThat(rows.get(rowId++)).containsEntry("RESOURCE_NAME", "compound-permissions-mapping-custom.properties"); assertThat(rows.get(rowId++)).containsEntry("RESOURCE_NAME", "compound-permissions-mapping-internal.properties"); assertThat(rows.get(rowId++)).containsEntry("RESOURCE_NAME", "compound-permissions-mapping.properties"); assertThat(rows.get(rowId++)).containsEntry("RESOURCE_NAME", "console-config.properties"); assertThat(rows.get(rowId++)).containsEntry("RESOURCE_NAME", "custom-permissions-mapping.properties"); assertThat(rows.get(rowId++)).containsEntry("RESOURCE_NAME", "resources-permissions-mapping-custom.properties"); assertThat(rows.get(rowId++)).containsEntry("RESOURCE_NAME", "resources-permissions-mapping-internal.properties"); assertThat(rows.get(rowId++)).containsEntry("RESOURCE_NAME", "resources-permissions-mapping.properties"); assertThat(rows.get(rowId)).containsEntry("RESOURCE_NAME", "security-config.properties"); } @Test void init_method_should_store_platform_portal_resources_from_classpath() throws Exception { //when platformSetup.init(); //then List> rows = jdbcTemplate .queryForList("SELECT * FROM configuration WHERE content_type= '" + ConfigurationType.PLATFORM_PORTAL + "' ORDER BY resource_name"); assertThat(rows).hasSize(1); assertThat(rows.get(0)).containsEntry("RESOURCE_NAME", "security-config.properties"); } @Test @Tag("community-only") void should_extract_configuration(@TempDir Path setupFolder) throws Exception { //given platformSetup.init(); //when System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toAbsolutePath().toString()); platformSetup.pull(); //then File folderContainingResultOfGet = setupFolder.resolve(PLATFORM_CONF_FOLDER_NAME) .resolve("current").toFile(); assertThat(folderContainingResultOfGet).as("should retrieve config files") .exists() .isDirectory(); List configurations = new ArrayList<>(); AllConfigurationResourceVisitor allConfigurationResourceVisitor = new AllConfigurationResourceVisitor( configurations); Files.walkFileTree(setupFolder, allConfigurationResourceVisitor); assertThat(configurations).extracting("resourceName").containsOnly( "bonita-platform-community-custom.properties", "bonita-platform-custom.xml", "security-config.properties", "bonita-tenant-community-custom.properties", "bonita-tenants-custom.xml", "compound-permissions-mapping.properties", "compound-permissions-mapping-custom.properties", "compound-permissions-mapping-internal.properties", "console-config.properties", "custom-permissions-mapping.properties", "resources-permissions-mapping.properties", "resources-permissions-mapping-custom.properties", "resources-permissions-mapping-internal.properties"); } @Test void init_method_should_log_when_created(CapturedOutput capturedOutput) throws Exception { //given assertThat(platformSetup.isPlatformAlreadyCreated()).isFalse(); //when platformSetup.init(); //then assertThat(platformSetup.isPlatformAlreadyCreated()).isTrue(); assertThat(capturedOutput.getOut()).as("should setup log message") .doesNotContain("Platform is already created. Nothing to do.") .contains("Platform created.") .contains("Initial configuration files successfully pushed to database"); } @Test void init_method_should_upgrade_default_configuration_when_already_created(CapturedOutput capturedOutput) throws Exception { //given platformSetup.init(); //when platformSetup.init(); //then assertThat(platformSetup.isPlatformAlreadyCreated()).isTrue(); assertThat(capturedOutput.getOut()) .containsOnlyOnce("Platform created.") .containsOnlyOnce("Platform is already created.") .containsOnlyOnce("Upgrading default configuration"); } @Test void push_method_should_log_when_created_and_create_backup(@TempDir File setupFolder, CapturedOutput capturedOutput) throws Exception { // given platformSetup.init(); System.setProperty(BONITA_SETUP_FOLDER, setupFolder.getAbsolutePath()); configurationFolderUtil.buildCurrentFolder(setupFolder.toPath()); // when platformSetup.forcePush(); // then assertThat(setupFolder.listFiles()).hasSize(1); List backupDirectory = Arrays.stream(setupFolder.listFiles()[0].listFiles()) .filter(it -> it.getName().contains("backup")).toList(); assertThat(backupDirectory).hasSize(1); assertThat(capturedOutput.getOut()) .contains("Backup directory created:") .as("should push new configuration and log message") .contains("Configuration files successfully pushed to database. " + "You can now restart Bonita to reflect your changes."); } @Test void push_should_fail_if_required_folder_would_be_deleted(@TempDir Path temporaryFolder) throws Exception { // on windows, the test fails to delete the 'platform init engine' directory // so do not run it for now assumeFalse(IS_OS_WINDOWS); // given platformSetup.init(); Path setupFolder = Files.createDirectory(temporaryFolder.resolve("conf")); System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toAbsolutePath().toString()); platformSetup.pull(); final Path platformEngine = setupFolder.resolve("platform_conf").resolve("current") .resolve("platform_engine"); FileUtils.deleteDirectory(platformEngine.toFile()); // when - then assertThatExceptionOfType(PlatformException.class) .isThrownBy(platformSetup::push) .withMessageStartingWith("You are trying to remove a protected folder from configuration") .withMessageContaining(platformEngine.toString()) .withMessageContaining("To restore the deleted folders"); } @Test void push_should_throw_exception_when_platform_is_not_created() { //given assertThat(platformSetup.isPlatformAlreadyCreated()).isFalse(); // when - then assertThatExceptionOfType(PlatformException.class) .isThrownBy(platformSetup::push) .withMessage("Platform is not created. Run 'setup init' first."); } @Test void clean_method_should_delete_and_log(@TempDir Path temporaryFolder, CapturedOutput capturedOutput) throws Exception { //given final Path path = Files.createDirectory(temporaryFolder.resolve("afterClean")); final Path licensePath = Files.createDirectory(temporaryFolder.resolve("licenses")); platformSetup.init(); //when platformSetup.clean(); //then assertThat(capturedOutput.getOut()).as("should log message") .isNotEmpty() .contains("Execute DeleteAllConfigurationInTransaction transaction."); platformSetup.pull(path, licensePath); List configurations = new ArrayList<>(); Files.walkFileTree(path, new AllConfigurationResourceVisitor(configurations)); assertThat(configurations).as("should remove all files").isEmpty(); Files.walkFileTree(licensePath, new AllConfigurationResourceVisitor(configurations)); assertThat(configurations).as("should remove all files").isEmpty(); } @Test void push_method_should_clean_previous_config(@TempDir Path temporaryFolder) throws Exception { //given List configurations = new ArrayList<>(); final Path initPath = Files.createDirectory(temporaryFolder.resolve("init")); final Path pushPath = Files.createDirectory(temporaryFolder.resolve("push")); final Path checkPath = Files.createDirectory(temporaryFolder.resolve("check")); final Path licensesPath = Files.createDirectory(temporaryFolder.resolve("lic")); FileUtils.writeByteArrayToFile( initPath.resolve(PLATFORM_CONF_FOLDER_NAME).resolve("initial") .resolve(PLATFORM_ENGINE.name().toLowerCase()).resolve("initial.properties") .toFile(), "key1=value1".getBytes()); FileUtils.writeByteArrayToFile( pushPath.resolve(PLATFORM_CONF_FOLDER_NAME).resolve("current") .resolve(TENANT_PORTAL.name().toLowerCase()) .resolve("current.properties").toFile(), "key2=value2".getBytes()); System.setProperty(BONITA_SETUP_FOLDER, initPath.toString()); configurationFolderUtil.buildSqlFolder(initPath.toFile().toPath(), dbVendor); platformSetup.init(); //when System.setProperty(BONITA_SETUP_FOLDER, pushPath.toString()); platformSetup.forcePush(); //then platformSetup.pull(checkPath, licensesPath); Files.walkFileTree(checkPath, new AllConfigurationResourceVisitor(configurations)); assertThat(configurations).as("should remove all files").hasSize(1) .extracting("resourceName").containsOnly("current.properties"); } @Test void push_method_should_throw_exception_if_no_current_folder(@TempDir Path temporaryFolder) throws Exception { //given List configurations = new ArrayList<>(); final Path confFolder = Files.createDirectory(temporaryFolder.resolve(UUID.randomUUID().toString())); configurationFolderUtil.buildPlatformEngineFolder(confFolder); configurationFolderUtil.buildSqlFolder(confFolder, dbVendor); System.setProperty(BONITA_SETUP_FOLDER, confFolder.toString()); platformSetup.init(); Path current = confFolder.resolve("platform_conf").resolve("current"); //when - then assertThatExceptionOfType(PlatformException.class).isThrownBy(platformSetup::push) .withMessage("Unable to push configuration from %1$s, as directory does not exists. " + "To modify your configuration, run 'setup pull', " + "update your configuration files from %1$s folder, " + "and then push your new configuration.", current); platformSetup.pull(); Files.walkFileTree(current, new AllConfigurationResourceVisitor(configurations)); assertThat(configurations).as("should have kept old files").hasSize(1) .extracting("resourceName").containsOnly("initialConfig.properties"); } @Test void should_push_check_platform_version(@TempDir Path temporaryFolder) throws Exception { //given platformSetup.init(); jdbcTemplate.execute("UPDATE platform SET version='bad version'"); final Path confFolder = Files.createDirectory(temporaryFolder.resolve(UUID.randomUUID().toString())); configurationFolderUtil.buildCurrentFolder(confFolder); System.setProperty(BONITA_SETUP_FOLDER, confFolder.toFile().getAbsolutePath()); // when - then assertThatExceptionOfType(PlatformException.class) .isThrownBy(platformSetup::push) .withMessage("The version of the platform (binaries) you are running [" + versionService.getPlatformSetupVersion() + "] only support database schema in version [" + versionService.getSupportedDatabaseSchemaVersion() + "]" + " but the current database schema version is [bad version]." + " You might need to migrate your platform or use a different version of the binaries."); } @Test void should_pull_check_platform_version() throws Exception { //given platformSetup.init(); jdbcTemplate.execute("UPDATE platform SET version='bad version'"); // when - then assertThatExceptionOfType(PlatformException.class) .isThrownBy(platformSetup::pull) .withMessage("The version of the platform (binaries) you are running [" + versionService.getPlatformSetupVersion() + "] only support database schema in version [" + versionService.getSupportedDatabaseSchemaVersion() + "]" + " but the current database schema version is [bad version]." + " You might need to migrate your platform or use a different version of the binaries."); } @Test void pushLicences_should_pass_if_licence_folder_does_not_exists() throws Exception { platformSetup.initProperties(); assertThatNoException().isThrownBy(platformSetup::preventFromPushingZeroLicense); } @Test void should_not_fail_when_pulling_twice_in_the_same_jvm(@TempDir Path temporaryFolder) throws Exception { final Path setupFolder = Files.createDirectory(temporaryFolder.resolve(UUID.randomUUID().toString())); System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toString()); configurationFolderUtil.buildPlatformEngineFolder(setupFolder); configurationFolderUtil.buildSqlFolder(setupFolder, dbVendor); platformSetup.init(); platformSetup.pull(); assertThatNoException().isThrownBy(platformSetup::pull); } @Test void pushLicences_should_fail_if_licence_folder_exists_but_is_empty(@TempDir Path temporaryFolder) throws Exception { final Path setupFolder = Files.createDirectory(temporaryFolder.resolve(UUID.randomUUID().toString())); System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toString()); final Path platformConf = configurationFolderUtil.buildPlatformConfFolder(setupFolder); final Path licenseFolder = platformConf.resolve("licenses"); Files.createDirectories(licenseFolder); platformSetup.initProperties(); assertThatExceptionOfType(PlatformException.class) .isThrownBy(platformSetup::preventFromPushingZeroLicense) .withMessageStartingWith("No license (.lic file) found." + lineSeparator() + "This would prevent Bonita Platform subscription edition" + " to start normally." + lineSeparator() + "Place your license file"); } @Test void pushLicences_should_fail_if_no_license_file_with_lic_extension_exists(@TempDir Path temporaryFolder) throws Exception { final Path setupFolder = Files.createDirectory(temporaryFolder.resolve(UUID.randomUUID().toString())); System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toString()); final Path platformConf = configurationFolderUtil.buildPlatformConfFolder(setupFolder); final Path licenseFolder = platformConf.resolve("licenses"); Files.createDirectories(licenseFolder); Files.createFile(licenseFolder.resolve("bonita-file.renamed")); platformSetup.initProperties(); assertThatExceptionOfType(PlatformException.class) .isThrownBy(platformSetup::preventFromPushingZeroLicense) .withMessageStartingWith("No license (.lic file) found." + lineSeparator() + "This would prevent Bonita Platform subscription edition" + " to start normally." + lineSeparator() + "Place your license file"); } @Test void init_method_should_update_configuration_files(@TempDir Path temporaryFolder) throws Exception { //given Path setupFolder = Files.createDirectory(temporaryFolder.resolve("conf")); System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toAbsolutePath().toString()); final File permissionFile = setupFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve("initial") .resolve("tenant_portal") .resolve("resources-permissions-mapping.properties").toFile(); FileUtils.write(permissionFile, "default 7.5.4 content", Charset.defaultCharset()); configurationFolderUtil.buildSqlFolder(setupFolder, dbVendor); platformSetup.init(); // Simulate new 7.6.0 configuration file content: final String new_7_6_0_content = "default 7.6.0 content"; FileUtils.write(permissionFile, new_7_6_0_content, Charset.defaultCharset()); //when platformSetup.init(); //then Map rows = jdbcTemplate .queryForMap( "SELECT * FROM configuration WHERE resource_name = 'resources-permissions-mapping.properties'"); assertThat(rows.get("RESOURCE_CONTENT")).isEqualTo(new_7_6_0_content.getBytes()); } @Test void init_on_existing_platform_should_add_new_config_files(CapturedOutput capturedOutput) throws Exception { //given platformSetup.init(); final String countConfigFile = "SELECT * FROM configuration WHERE resource_name = 'bonita-tenant-community-custom.properties'"; assertThat(jdbcTemplate.queryForList(countConfigFile)).hasSize(1) .anyMatch(map -> map.get("CONTENT_TYPE").equals("TENANT_ENGINE")); // Delete it to check that init method adds it again: jdbcTemplate .update("DELETE from configuration WHERE resource_name = 'bonita-tenant-community-custom.properties'"); assertThat(jdbcTemplate.queryForList(countConfigFile)).isEmpty(); //when platformSetup.init(); //then assertThat(jdbcTemplate.queryForList(countConfigFile)).hasSize(1) .anyMatch(map -> map.get("CONTENT_TYPE").equals("TENANT_ENGINE")); assertThat(capturedOutput.getOut()) .contains("New configuration file detected 'bonita-tenant-community-custom.properties'"); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/PlatformSetupTest.java ================================================ /** * 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.platform.setup; import static org.apache.commons.io.FilenameUtils.separatorsToSystem; import static org.assertj.core.api.Assertions.*; import static org.bonitasoft.platform.setup.PlatformSetup.*; import static org.mockito.Mockito.doReturn; import java.nio.file.Files; import java.nio.file.Path; import java.sql.Connection; import java.sql.DatabaseMetaData; import javax.sql.DataSource; import org.bonitasoft.platform.configuration.ConfigurationService; import org.bonitasoft.platform.configuration.model.LightBonitaConfiguration; import org.bonitasoft.platform.database.DatabaseVendor; import org.bonitasoft.platform.exception.PlatformException; import org.bonitasoft.platform.util.ConfigurationFolderUtil; import org.bonitasoft.platform.version.VersionService; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.rules.TemporaryFolder; import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class PlatformSetupTest { @Mock private DataSource dataSource; @Mock private Connection connection; @Mock private DatabaseMetaData metaData; @Mock private ScriptExecutor scriptExecutor; @Mock private ConfigurationService configurationService; @Mock private VersionService versionService; @InjectMocks private PlatformSetup platformSetup; private final ConfigurationFolderUtil configurationFolderUtil = new ConfigurationFolderUtil(); @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Rule public final TestRule restoreSystemProperties = new RestoreSystemProperties(); @Before public void before() throws Exception { setPlatformSetupDbVendor(DatabaseVendor.H2.getValue()); setPlatformSetupBdmDbVendor(DatabaseVendor.H2.getValue()); doReturn(connection).when(dataSource).getConnection(); doReturn(metaData).when(connection).getMetaData(); } private void setPlatformSetupDbVendor(String dbVendor) { ReflectionTestUtils.setField(platformSetup, "dbVendor", dbVendor); } private void setPlatformSetupBdmDbVendor(String bdmDbVendor) { ReflectionTestUtils.setField(platformSetup, "bdmDbVendor", bdmDbVendor); } @Test public void should_not_check_license_if_platform_already_init() throws Exception { final Path setupFolder = temporaryFolder.newFolder().toPath(); System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toString()); final Path platformConf = configurationFolderUtil.buildPlatformConfFolder(setupFolder); final Path licenseFolder = platformConf.resolve("licenses"); Files.createDirectories(licenseFolder); doReturn(true).when(scriptExecutor).isPlatformAlreadyCreated(); //no exception assertThatNoException().isThrownBy(platformSetup::init); } @Test public void should_fail_if_init_with_no_license() throws Exception { final Path setupFolder = temporaryFolder.newFolder().toPath(); System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toString()); final Path platformConf = configurationFolderUtil.buildPlatformConfFolder(setupFolder); final Path licenseFolder = platformConf.resolve("licenses"); Files.createDirectories(licenseFolder); assertThatExceptionOfType(PlatformException.class) .isThrownBy(platformSetup::init) .withMessageStartingWith("No license (.lic file) found."); } @Test public void should_fail_if_init_with_incorrect_database_vendor() { //given String dbVendor = "foobar"; setPlatformSetupDbVendor(dbVendor); //when - then assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(platformSetup::init) .withMessage("Unknown database vendor: %s", dbVendor); } @Test public void should_fail_if_init_with_incorrect_bdm_database_vendor() { //given String dbVendor = "foobar"; setPlatformSetupBdmDbVendor(dbVendor); //when - then assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(platformSetup::init) .withMessage("Unknown database vendor: %s", dbVendor); } @Test public void should_fail_if_init_with_unsupported_database_vendor() { //given DatabaseVendor dbVendor = DatabaseVendor.ORACLE; setPlatformSetupDbVendor(dbVendor.getValue()); //when - then assertThatExceptionOfType(PlatformException.class) .isThrownBy(platformSetup::init) .withMessage("Database vendor '%s' is not supported with the community edition", dbVendor); } @Test public void should_fail_if_init_with_unsupported_bdm_database_vendor() { //given DatabaseVendor dbVendor = DatabaseVendor.ORACLE; setPlatformSetupBdmDbVendor(dbVendor.getValue()); //when - then assertThatExceptionOfType(PlatformException.class) .isThrownBy(platformSetup::init) .withMessage("Database vendor '%s' is not supported with the community edition", dbVendor); } @Test public void should_not_fail_if_init_with_postgres_database_vendor() { //given setPlatformSetupDbVendor(DatabaseVendor.POSTGRES.getValue()); //when - then assertThatNoException().isThrownBy(platformSetup::init); } @Test public void should_not_fail_if_init_with_postgres_bdm_database_vendor() { //given setPlatformSetupBdmDbVendor(DatabaseVendor.POSTGRES.getValue()); //when - then assertThatNoException().isThrownBy(platformSetup::init); } @Test public void should_init_dbVendor_with_system_prop_if_null() throws Exception { //given setPlatformSetupDbVendor(null); DatabaseVendor dbVendor = DatabaseVendor.POSTGRES; System.setProperty(BONITA_DB_VENDOR_PROPERTY, dbVendor.getValue()); //when platformSetup.initProperties(); //then assertThat(platformSetup.dbVendor).isEqualTo(dbVendor.getValue()); } @Test public void should_init_bdmDbVendor_with_system_prop_if_null() throws Exception { //given setPlatformSetupBdmDbVendor(null); DatabaseVendor dbVendor = DatabaseVendor.POSTGRES; System.setProperty(BONITA_BDM_DB_VENDOR_PROPERTY, dbVendor.getValue()); //when platformSetup.initProperties(); //then assertThat(platformSetup.bdmDbVendor).isEqualTo(dbVendor.getValue()); } @Test public void getFolderFromConfiguration_should_work_for_platform_level_folder() throws Exception { // given: final Path setupFolder = temporaryFolder.newFolder().toPath(); System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toString()); platformSetup.initProperties(); LightBonitaConfiguration configuration = new LightBonitaConfiguration("some_folder"); // when: final Path folder = platformSetup.getFolderFromConfiguration(configuration); // then: assertThat(folder).hasToString(separatorsToSystem(setupFolder + "/platform_conf/current/some_folder")); } @Test public void getFolderFromConfiguration_should_work_for_tenant_level_folder() throws Exception { // given: final Path setupFolder = temporaryFolder.newFolder().toPath(); System.setProperty(BONITA_SETUP_FOLDER, setupFolder.toString()); platformSetup.initProperties(); LightBonitaConfiguration configuration = new LightBonitaConfiguration("TENANT-LEVEL-FOLDER"); // when: final Path folder = platformSetup.getFolderFromConfiguration(configuration); // then: assertThat(folder) .hasToString(separatorsToSystem(setupFolder + "/platform_conf/current/tenant-level-folder")); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/ScriptExecutorIT.java ================================================ /** * 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.platform.setup; import static org.assertj.core.api.Assertions.*; import static org.bonitasoft.platform.setup.PlatformSetup.BONITA_SETUP_FOLDER; import static org.bonitasoft.platform.setup.PlatformSetup.PLATFORM_CONF_FOLDER_NAME; import static org.bonitasoft.platform.setup.ScriptExecutor.FAIL_ON_ERROR; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; import org.bonitasoft.platform.setup.jndi.MemoryJNDISetup; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.ClearSystemProperties; import org.junit.rules.TemporaryFolder; import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.jdbc.JdbcTestUtils; /** * author Laurent Leseigneur */ @RunWith(SpringRunner.class) @SpringBootTest(classes = { PlatformSetupApplication.class }) public class ScriptExecutorIT { @Rule public TestRule clean = new ClearSystemProperties(BONITA_SETUP_FOLDER); @Autowired MemoryJNDISetup memoryJNDISetup; @Autowired JdbcTemplate jdbcTemplate; @Autowired private ScriptExecutor scriptExecutor; @Value("${db.vendor}") String dbVendor; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Before public void before() throws Exception { removeSetupFolderProperty(); } @After public void after() throws Exception { removeSetupFolderProperty(); scriptExecutor.deleteTables(); } private void removeSetupFolderProperty() { System.clearProperty(BONITA_SETUP_FOLDER); } @Test public void should_be_able_to_create_platform_tables() throws Exception { scriptExecutor.createAndInitializePlatformIfNecessary(); final Integer sequences = jdbcTemplate.queryForObject("select count(*) from sequence", Integer.class); assertThat(sequences).isEqualTo(69); final int platformRows = JdbcTestUtils.countRowsInTable(jdbcTemplate, "platform"); assertThat(platformRows).isEqualTo(1); final Map rowPlatform = jdbcTemplate.queryForMap("select * from platform"); assertThat("" + rowPlatform.get("id")).isEqualTo("1"); // convert to String as not all RDBMS convert the same way (long, int, bigDecimal...) assertThat(rowPlatform).containsEntry("created_by", "platformAdmin"); } @Test public void executeSQLResource_should_be_successful_using_filesystem_scripts() throws Exception { //given final File confFolder = temporaryFolder.newFolder(); final Path createTableScript = confFolder.toPath().resolve(PLATFORM_CONF_FOLDER_NAME).resolve("sql") .resolve(dbVendor).resolve("myScript.sql"); final Path dropTableScript = confFolder.toPath().resolve(PLATFORM_CONF_FOLDER_NAME).resolve("sql") .resolve(dbVendor).resolve("cleanup.sql"); Files.createDirectories(createTableScript.getParent()); final String tableName = dbVendor + "_test"; final String createTable = "CREATE TABLE " + tableName + " (id INT NOT NULL)"; Files.write(createTableScript, createTable.getBytes()); Files.write(dropTableScript, ("DROP TABLE " + tableName).getBytes()); System.setProperty(BONITA_SETUP_FOLDER, confFolder.getAbsolutePath()); //when - then assertThatNoException().isThrownBy(() -> scriptExecutor.executeSQLResource("myScript.sql", FAIL_ON_ERROR)); final int platformRows = JdbcTestUtils.countRowsInTable(jdbcTemplate, tableName); assertThatNoException().isThrownBy(() -> scriptExecutor.executeSQLResource("cleanup.sql", FAIL_ON_ERROR)); assertThat(platformRows).as("should create empty table from filesystem ").isZero(); } @Test public void executeSQLResource_should_fail_when_script_is_missing() throws Exception { //given final File confFolder = temporaryFolder.newFolder(); final Path sqlScript = confFolder.toPath().resolve(PLATFORM_CONF_FOLDER_NAME).resolve("sql").resolve(dbVendor) .resolve("missingScript.sql"); System.setProperty(BONITA_SETUP_FOLDER, confFolder.getAbsolutePath()); //when - then assertThatExceptionOfType(RuntimeException.class) .isThrownBy(() -> scriptExecutor.executeSQLResource("missingScript.sql", FAIL_ON_ERROR)) .withMessage("SQL resource file not found in filesystem: " + sqlScript.toFile().getAbsolutePath()); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/ScriptExecutorTest.java ================================================ /** * 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.platform.setup; import static org.mockito.Mockito.*; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; /** * author Emmanuel Duchastenier */ @RunWith(SpringRunner.class) @SpringBootTest(classes = PlatformSetupApplication.class, // use an in-memory h2 database for those tests properties = "h2.url=jdbc:h2:mem:${db.database.name}") public class ScriptExecutorTest { @Autowired private ScriptExecutor scriptExecutor; @Test public void createAndInitializePlatformIfNecessary_should_not_create_platform_if_already_existing() throws Exception { //given ScriptExecutor spy = spy(scriptExecutor); doReturn(true).when(spy).isPlatformAlreadyCreated(); //when spy.createAndInitializePlatformIfNecessary(); //then verify(spy, times(0)).createTables(); verify(spy, times(0)).initializePlatformStructure(); verify(spy, times(0)).insertPlatform(); } @Test public void createAndInitializePlatformIfNecessary_should_create_platform_if_not_already_existing() throws Exception { //given ScriptExecutor spy = spy(scriptExecutor); //when spy.createAndInitializePlatformIfNecessary(); //then verify(spy).createTables(); verify(spy).initializePlatformStructure(); verify(spy).insertPlatform(); //cleanup scriptExecutor.deleteTables(); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/CommandTestUtils.java ================================================ /** * 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.platform.setup.command; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; /** * @author Emmanuel Duchastenier */ public class CommandTestUtils { public static CommandLine buildCommandLine(String... strings) throws ParseException { return new GnuParser().parse(new Options(), strings); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/HelpCommandTest.java ================================================ /** * 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.platform.setup.command; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Fail.fail; import static org.bonitasoft.platform.setup.command.CommandTestUtils.buildCommandLine; import java.util.Arrays; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.bonitasoft.platform.exception.PlatformException; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemErrRule; import org.junit.contrib.java.lang.system.SystemOutRule; /** * @author Baptiste Mesta */ public class HelpCommandTest { @Rule public SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests(); @Rule public SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests(); private HelpCommand helpCommand; @Before public void before() throws Exception { helpCommand = new HelpCommand(); helpCommand.setCommands( Arrays.asList(new PlatformSetupCommand("command1", "summary1", "description of command 1", null) { @Override public void execute(Options options, CommandLine commandLine) throws PlatformException { } }, new PlatformSetupCommand("command2", "summary2", "description of command 2", null) { @Override public void execute(Options options, CommandLine commandLine) throws PlatformException { } }, helpCommand)); } @Test public void should_print_only_usage_when_no_command_is_given() throws Exception { //when try { helpCommand.execute(new Options(), buildCommandLine()); fail("no option should throw exception"); } catch (CommandException e) { assertThat(e.getMessage()).isEqualTo("Need to specify a command, see usage above."); } //then assertThat(systemOutRule.getLog()).as("contains the usage").contains("usage: setup ( command1 | command2 )"); assertThat(systemOutRule.getLog()).as("do not contains other help").doesNotContain("command1 -- summary1"); assertThat(systemOutRule.getLog()).as("contains how to run help") .contains("use `setup help` or `setup help ` for more details"); } @Test public void should_print_common_help_when_asking_help_on_unknown_command() throws Exception { try { helpCommand.execute(new Options(), buildCommandLine("help", "thisIsAnUnknownCommand")); fail("should throw exception"); } catch (CommandException e) { assertThat(e.getMessage()).isEqualTo("ERROR: no command named: thisIsAnUnknownCommand"); } assertThat(systemOutRule.getLog()).as("contains the usage").contains("usage: setup ( command1 | command2 )"); assertThat(systemOutRule.getLog()).as("contains how to run help") .contains("use `setup help` or `setup help ` for more details"); } @Test public void should_print_only_usage_when_called_with_unknown_command() throws Exception { try { helpCommand.execute(new Options(), buildCommandLine("thisIsAnUnknownCommand")); fail("should throw exception"); } catch (CommandException e) { assertThat(e.getMessage()).isEqualTo("ERROR: no command named: thisIsAnUnknownCommand"); } assertThat(systemOutRule.getLog()).as("contains the usage").contains("usage: setup ( command1 | command2 )"); assertThat(systemOutRule.getLog()).as("do not contains other help").doesNotContain("command1 -- summary1"); assertThat(systemOutRule.getLog()).as("contains how to run help") .contains("use `setup help` or `setup help ` for more details"); } @Test public void should_print_command_help_when_asking_help_on_a_command() throws Exception { helpCommand.execute(new Options(), buildCommandLine("help", "command2")); assertThat(systemOutRule.getLog()).contains("description of command 2"); assertThat(systemOutRule.getLog()).contains("usage: setup command2"); } @Test public void should_print_usage_with_options() throws Exception { helpCommand.execute(new Options().addOption("a", "anOption", false, "the option"), buildCommandLine("help", "command2")); assertThat(systemOutRule.getLog()).as("contains the usage").contains("usage: setup command2 [-a]"); assertThat(systemOutRule.getLog()).as("contains the option").contains("-a,--anOption the option"); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/InitCommandTest.java ================================================ /** * 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.platform.setup.command; import static org.bonitasoft.platform.setup.command.CommandTestUtils.buildCommandLine; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; import org.apache.commons.cli.Options; import org.bonitasoft.platform.setup.PlatformSetup; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class InitCommandTest { @Spy private InitCommand initCommand; @Mock private PlatformSetup platformSetup; @Rule public RestoreSystemProperties rule = new RestoreSystemProperties(); @Rule public ExpectedException expectedException = ExpectedException.none(); @Before public void before() throws Exception { doReturn(platformSetup).when(initCommand).getPlatformSetup(any(String[].class)); } @Test public void should_execute_init() throws Exception { doNothing().when(initCommand).askConfirmationIfH2(); initCommand.execute(new Options(), buildCommandLine()); verify(platformSetup).init(); } @Test public void should_call_h2_confirmation() throws Exception { doNothing().when(initCommand).askConfirmationIfH2(); initCommand.execute(new Options(), buildCommandLine()); verify(initCommand).askConfirmationIfH2(); } @Test public void execute_should_not_ask_confirmation_if_dbVendor_not_H2() throws Exception { // given: System.setProperty("db.vendor", "postgres"); System.setProperty("bdm.db.vendor", "h2"); // when: initCommand.execute(new Options(), buildCommandLine()); // then: verify(initCommand, times(0)).warn(anyString()); } @Test public void execute_should_not_ask_confirmation_if_bdmDbVendor_not_H2() throws Exception { // given: System.setProperty("db.vendor", "h2"); System.setProperty("bdm.db.vendor", "postgres"); // when: initCommand.execute(new Options(), buildCommandLine()); // then: verify(initCommand, times(0)).warn(anyString()); } @Test public void execute_should_throw_CommandException_if_answer_is_not_YES() throws Exception { // given: System.setProperty("db.vendor", "h2"); System.setProperty("bdm.db.vendor", "h2"); doReturn("N").when(initCommand).readAnswer(); // then: expectedException.expect(CommandException.class); expectedException.expectMessage("Exiting"); // when: initCommand.execute(new Options(), buildCommandLine()); } @Test public void execute_should_continue_if_answer_is_YES() throws Exception { // given: System.setProperty("db.vendor", "h2"); System.setProperty("bdm.db.vendor", "h2"); doReturn("y").when(initCommand).readAnswer(); // when: initCommand.execute(new Options(), buildCommandLine()); // then: verify(platformSetup).init(); } @Test public void execute_should_continue_if_H2_YES_property_is_defined() throws Exception { // given: System.setProperty("db.vendor", "h2"); System.setProperty("bdm.db.vendor", "h2"); System.setProperty("h2.noconfirm", ""); // when: initCommand.execute(new Options(), buildCommandLine()); // then: verify(platformSetup).init(); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/PullCommandTest.java ================================================ /** * 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.platform.setup.command; import static org.bonitasoft.platform.setup.command.CommandTestUtils.buildCommandLine; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import org.apache.commons.cli.Options; import org.bonitasoft.platform.setup.PlatformSetup; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class PullCommandTest { @Spy private PullCommand pullCommand; @Mock private PlatformSetup platformSetup; @Before public void before() throws Exception { doReturn(platformSetup).when(pullCommand).getPlatformSetup(any(String[].class)); } @Test public void should_execute_pull() throws Exception { pullCommand.execute(new Options(), buildCommandLine()); verify(platformSetup).pull(); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/PushCommandTest.java ================================================ /** * 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.platform.setup.command; import static org.bonitasoft.platform.setup.command.CommandTestUtils.buildCommandLine; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import org.apache.commons.cli.Options; import org.bonitasoft.platform.setup.PlatformSetup; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class PushCommandTest { @Spy private PushCommand pushCommand; @Mock private PlatformSetup platformSetup; @Before public void before() throws Exception { doReturn(platformSetup).when(pushCommand).getPlatformSetup(any(String[].class)); } @Test public void should_execute_push() throws Exception { pushCommand.execute(new Options(), buildCommandLine()); verify(platformSetup).push(false); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/configure/BundleConfiguratorTest.java ================================================ /** * 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.platform.setup.command.configure; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import org.apache.commons.io.filefilter.RegexFileFilter; import org.assertj.core.api.SoftAssertions; import org.bonitasoft.platform.exception.PlatformException; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class BundleConfiguratorTest { private final BundleConfigurator configurator = Mockito.spy(_BundleConfigurator.class); private abstract static class _BundleConfigurator extends BundleConfigurator { public _BundleConfigurator() throws PlatformException { super(Paths.get("")); } } @Test public void escapeWindowsBackslashesIfAny_should_double_backslashes() { // when: final String windowsValue = BundleConfigurator.convertWindowsBackslashes("C:\\Windows\\Path"); // then: assertThat(windowsValue).isEqualTo("C:/Windows/Path"); } @Test public void xml_chars_in_URLs_should_be_escaped_before_replacing() { // given: String url = "jdbc:mysql://${bdm.db.server.name}:${bdm.db.server.port}/${bdm.db.database.name}" + "?dontTrackOpenResources=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true"; // when: final String escapedXmlCharacters = BundleConfigurator.escapeXmlCharacters(url); // then: assertThat(escapedXmlCharacters) .isEqualTo("jdbc:mysql://${bdm.db.server.name}:${bdm.db.server.port}/${bdm.db.database.name}" + "?dontTrackOpenResources=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true"); } @Test public void getDriverFilter_should_detect_Oracle_drivers() { // when: final RegexFileFilter driverFilter = configurator.getDriverFilter("oracle"); // then: assertThat(driverFilter.accept(new File("myFavoriteOjdbc1.4drivers.JAR"))).isTrue(); assertThat(driverFilter.accept(new File("OraCLE-4.jar"))).isTrue(); assertThat(driverFilter.accept(new File("OraCLE.zip"))).isTrue(); assertThat(driverFilter.accept(new File("OJdbc-1.4.2.ZIP"))).isTrue(); } @Test public void getDriverFilter_should_detect_Postgres_drivers() { // when: final RegexFileFilter driverFilter = configurator.getDriverFilter("postgres"); // then: assertThat(driverFilter.accept(new File("postgres.JAR"))).isTrue(); assertThat(driverFilter.accept(new File("POSTGRESsql-5.jar"))).isTrue(); assertThat(driverFilter.accept(new File("drivers_postgres.LAST.zip"))).isTrue(); } @Test public void getDriverFilter_should_detect_SQLSERVER_drivers() { // when: final RegexFileFilter driverFilter = configurator.getDriverFilter("sqlserver"); // then: assertThat(driverFilter.accept(new File("sqlserver.JAR"))).isTrue(); assertThat(driverFilter.accept(new File("SQLSERVER-5.jar"))).isTrue(); assertThat(driverFilter.accept(new File("drivers_SQLServer.zip"))).isTrue(); assertThat(driverFilter.accept(new File("old-sqljdbc.jar"))).isTrue(); assertThat(driverFilter.accept(new File("sqljdbc.jar"))).isTrue(); assertThat(driverFilter.accept(new File("sqljdbc4.jar"))).isTrue(); assertThat(driverFilter.accept(new File("sqljdbc41.jar"))).isTrue(); assertThat(driverFilter.accept(new File("sqljdbc42.jar"))).isTrue(); assertThat(driverFilter.accept(new File("mssql-jdbc-6.2.1.jre8.jar"))).isTrue(); } @Test public void getDriverFilter_should_detect_MySQL_drivers() { // when: final RegexFileFilter driverFilter = configurator.getDriverFilter("mysql"); // then: assertThat(driverFilter.accept(new File("MySQL.JAR"))).isTrue(); assertThat(driverFilter.accept(new File("mySQL-5.jar"))).isTrue(); assertThat(driverFilter.accept(new File("drivers_mysql.zIp"))).isTrue(); } @Test public void getDriverFilter_should_detect_H2_drivers() { // when: final RegexFileFilter driverFilter = configurator.getDriverFilter("h2"); // then: assertThat(driverFilter.accept(new File("h2-1.4.JAR"))).isTrue(); assertThat(driverFilter.accept(new File("drivers-H2.ZIP"))).isTrue(); assertThat(driverFilter.accept(new File("my-custom-h2_package.jar"))).isTrue(); } @Test public void should_escape_db_url_in_properties_for_H2() throws Exception { // given: DatabaseConfiguration dbConfig = mock(DatabaseConfiguration.class); when(dbConfig.getUrl()).thenReturn("/opt/bonità"); when(dbConfig.getDbVendor()).thenReturn("h2"); // when: String escapedURL = BundleConfigurator.getDatabaseConnectionUrlForPropertiesFile(dbConfig); // then: assertThat(escapedURL).isEqualTo("/opt/bonit\\\\u00E0"); } // ================================================================================================================= // UTILS // ================================================================================================================= public static void checkFileContains(Path file, String... expectedTexts) throws IOException { final String content = new String(Files.readAllBytes(file), StandardCharsets.UTF_8); assertThat(content).contains(expectedTexts); } public static void checkFileDoesNotContain(Path file, String... expectedTexts) throws IOException { final String content = new String(Files.readAllBytes(file), StandardCharsets.UTF_8); SoftAssertions softly = new SoftAssertions(); for (String text : expectedTexts) { softly.assertThat(content).doesNotContain(text); } softly.assertAll(); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/configure/ConfigureCommandTest.java ================================================ /** * 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.platform.setup.command.configure; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class ConfigureCommandTest { @Spy private ConfigureCommand configureCommand; @Mock private BundleConfigurator bundleConfigurator; @Mock private BundleResolver bundleResolver; @Before public void before() throws Exception { doReturn(bundleResolver).when(configureCommand).createBundleResolver(); doReturn(bundleConfigurator).when(bundleResolver).getConfigurator(); } @Test public void execute() throws Exception { configureCommand.execute(any(Options.class), any(CommandLine.class)); verify(bundleConfigurator).configureApplicationServer(); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/configure/DatabaseConfigurationTest.java ================================================ /** * 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.platform.setup.command.configure; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.io.File; import java.nio.file.Path; import java.util.Properties; import org.bonitasoft.platform.exception.PlatformException; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.rules.TestRule; /** * @author Emmanuel Duchastenier */ public class DatabaseConfigurationTest { private static final String TEST_DATASOURCE_CONFIG_DIR = "/datasource-config/"; @Rule public TestRule clean = new RestoreSystemProperties(); @Test public void bonita_database_values_can_be_overridden_by_system_properties() throws Exception { // given: final Properties properties = new PropertyLoader().loadProperties(); System.setProperty("db.vendor", "postgres"); System.setProperty("db.server.name", "postgresServer"); System.setProperty("db.server.port", "3333"); System.setProperty("db.database.name", "bonita_database"); System.setProperty("db.user", "root"); System.setProperty("db.password", "secret"); // when: final DatabaseConfiguration bonitaConfig = new DatabaseConfiguration("", properties, null); // then: assertThat(bonitaConfig.getDbVendor()).isEqualTo("postgres"); assertThat(bonitaConfig.getServerName()).isEqualTo("postgresServer"); assertThat(bonitaConfig.getServerPort()).isEqualTo("3333"); assertThat(bonitaConfig.getDatabaseName()).isEqualTo("bonita_database"); assertThat(bonitaConfig.getDatabaseUser()).isEqualTo("root"); assertThat(bonitaConfig.getDatabasePassword()).isEqualTo("secret"); } @Test public void bdm_database_values_can_be_overridden_by_system_properties() throws Exception { // given: final Properties properties = new Properties(); properties.load(this.getClass().getResourceAsStream(TEST_DATASOURCE_CONFIG_DIR + "internal.properties")); System.setProperty("bdm.db.vendor", "postgres"); System.setProperty("bdm.db.server.name", "myServer"); System.setProperty("bdm.db.server.port", "1111"); System.setProperty("bdm.db.database.name", "internal_database"); System.setProperty("bdm.db.user", "_user_"); System.setProperty("bdm.db.password", "_pwd_"); // when: final DatabaseConfiguration bdmConfig = new DatabaseConfiguration("bdm.", properties, null); // then: assertThat(bdmConfig.getDbVendor()).isEqualTo("postgres"); assertThat(bdmConfig.getServerName()).isEqualTo("myServer"); assertThat(bdmConfig.getServerPort()).isEqualTo("1111"); assertThat(bdmConfig.getDatabaseName()).isEqualTo("internal_database"); assertThat(bdmConfig.getDatabaseUser()).isEqualTo("_user_"); assertThat(bdmConfig.getDatabasePassword()).isEqualTo("_pwd_"); } @Test public void database_values_should_be_trimmed() throws Exception { // given: final Properties properties = new Properties(); properties.load(this.getClass() .getResourceAsStream(TEST_DATASOURCE_CONFIG_DIR + "database_with_space_values.properties")); properties.load(this.getClass().getResourceAsStream(TEST_DATASOURCE_CONFIG_DIR + "internal.properties")); System.setProperty("db.server.name", " localhost "); System.setProperty("db.server.port", " 5135 "); // when: final DatabaseConfiguration dbConfig = new DatabaseConfiguration("", properties, null); final DatabaseConfiguration bdmDbConfig = new DatabaseConfiguration("bdm.", properties, null); // then: assertThat(dbConfig.getUrl()).isEqualTo("jdbc:postgresql://localhost:5135/bonita"); assertThat(bdmDbConfig.getUrl()) .isEqualTo("jdbc:postgresql://postgres.rd.lan:5432/business_data"); } @Test public void support_absolute_path_in_h2_database_dir() throws Exception { // given: Path rootPath = new File(".").toPath(); String h2DatabaseDir = "/h2Database"; Properties properties = new PropertyLoader().loadProperties(); if (org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS) { h2DatabaseDir = "C:/h2Database"; } System.setProperty("db.vendor", "h2"); System.setProperty("h2.database.dir", h2DatabaseDir); // when: DatabaseConfiguration bonitaConfig = new DatabaseConfiguration("", properties, rootPath); // then: assertThat(bonitaConfig.getUrl()) .isEqualTo("jdbc:h2:file:" + h2DatabaseDir + "/bonita_journal.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;"); } @Test public void support_properties_in_h2_database_dir() throws Exception { // given: Path rootPath = new File(".").toPath(); String h2DatabaseDir = "${org.bonitasoft.h2.database.dir}"; Properties properties = new PropertyLoader().loadProperties(); System.setProperty("db.vendor", "h2"); System.setProperty("h2.database.dir", h2DatabaseDir); // when: DatabaseConfiguration dbConfig = new DatabaseConfiguration("", properties, rootPath); // then: assertThat(dbConfig.getUrl()) .isEqualTo("jdbc:h2:file:" + h2DatabaseDir + "/bonita_journal.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;"); } @Test public void convert_relative_path_to_absolute_path_in_h2_database_dir() throws Exception { // given: Path rootPath = new File(".").toPath(); String h2DatabaseDir = "../h2Database"; Properties properties = new PropertyLoader().loadProperties(); System.setProperty("db.vendor", "h2"); System.setProperty("h2.database.dir", h2DatabaseDir); // when: DatabaseConfiguration dbConfig = new DatabaseConfiguration("", properties, rootPath); // then: assertThat(dbConfig.getUrl()) .isEqualTo("jdbc:h2:file:" + rootPath.resolve("setup").resolve(h2DatabaseDir).toAbsolutePath().normalize().toString() .replace("\\", "/") + "/bonita_journal.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;"); } @Test public void jdbc_pool_size_values_must_be_integers() throws Exception { // given: final Properties properties = new Properties(); properties.load(this.getClass().getResourceAsStream(TEST_DATASOURCE_CONFIG_DIR + "internal.properties")); System.setProperty("bdm.db.vendor", "postgres"); System.setProperty("bdm.db.server.name", "myServer"); System.setProperty("bdm.db.server.port", "1111"); System.setProperty("bdm.db.database.name", "internal_database"); System.setProperty("bdm.db.user", "_user_"); System.setProperty("bdm.db.password", "_pwd_"); System.setProperty("bdm.connection-pool.maxIdle", "ten"); // expect: assertThatThrownBy( () -> new DatabaseConfiguration("bdm.", properties, null)) .isExactlyInstanceOf(PlatformException.class) .hasMessage("Invalid integer value 'ten' for property 'bdm.connection-pool.maxIdle'"); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/configure/PropertyReaderTest.java ================================================ /** * 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.platform.setup.command.configure; import static org.assertj.core.api.Assertions.assertThat; import java.util.Properties; import org.bonitasoft.platform.exception.PlatformException; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.rules.ExpectedException; import org.junit.rules.TestRule; /** * @author Emmanuel Duchastenier */ public class PropertyReaderTest { @Rule public TestRule clean = new RestoreSystemProperties(); @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void properties_file_values_can_be_overridden_by_system_properties() throws Exception { // given: final Properties properties = new Properties(); properties.load(this.getClass().getResourceAsStream("/datasource-config/database.properties")); final PropertyReader bdmConfig = new PropertyReader(properties); assertThat(bdmConfig.getPropertyAndFailIfNull("bdm.db.vendor")).isEqualTo("oracle"); // when: System.setProperty("bdm.db.vendor", "otherValue"); // then: assertThat(bdmConfig.getPropertyAndFailIfNull("bdm.db.vendor")).isEqualTo("otherValue"); } @Test public void should_fail_if_mandatory_property_is_not_set() throws Exception { // given: final PropertyReader reader = new PropertyReader(new Properties()); // then: expectedException.expect(PlatformException.class); expectedException.expectMessage("Mandatory property"); // when: reader.getPropertyAndFailIfNull("db.server.name"); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/setup/command/configure/TomcatBundleConfiguratorTest.java ================================================ /** * Copyright (C) 2016-2017 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.platform.setup.command.configure; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.bonitasoft.platform.setup.PlatformSetup.BONITA_SETUP_FOLDER; import static org.bonitasoft.platform.setup.command.configure.BundleConfiguratorTest.checkFileContains; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import org.apache.commons.io.FileUtils; import org.apache.commons.io.file.PathUtils; import org.apache.commons.io.filefilter.RegexFileFilter; import org.bonitasoft.platform.exception.PlatformException; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.rules.TemporaryFolder; import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; /** * @author Emmanuel Duchastenier */ @RunWith(MockitoJUnitRunner.class) public class TomcatBundleConfiguratorTest { @Rule public final TestRule restoreSystemProperties = new RestoreSystemProperties(); @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); private TomcatBundleConfigurator configurator; private TomcatBundleConfigurator spy; private Path bundleFolder; private Path tomcatFolder; private String databaseAbsolutePath; @Before public void setupTempConfFolder() throws Exception { final File temporaryFolderRoot = temporaryFolder.newFolder(); bundleFolder = temporaryFolderRoot.toPath().toRealPath(); tomcatFolder = bundleFolder.resolve("server"); configurator = new TomcatBundleConfigurator(bundleFolder); FileUtils.copyDirectory(Paths.get("src/test/resources/tomcat_conf").toFile(), temporaryFolderRoot); System.setProperty(BONITA_SETUP_FOLDER, bundleFolder.resolve("setup").toString()); databaseAbsolutePath = BundleConfigurator .convertWindowsBackslashes(bundleFolder.resolve("h2_database").normalize().toString()); spy = spy(configurator); } @Test public void should_delete_h2_jar_from_classpath_if_h2_is_not_used() throws Exception { // given: System.setProperty("db.vendor", "postgres"); System.setProperty("db.database.name", "bonita"); System.setProperty("db.user", "bonita"); System.setProperty("db.password", "bpm"); System.setProperty("bdm.db.vendor", "postgres"); System.setProperty("bdm.db.database.name", "business_data"); System.setProperty("bdm.db.user", "bizUser"); System.setProperty("bdm.db.password", "bizPwd"); // given: final Path h2jarPath = tomcatFolder.resolve("lib").resolve("bonita").resolve("h2.jar"); assertThat(h2jarPath).exists(); // when: configurator.configureApplicationServer(); // then: assertThat(h2jarPath).doesNotExist(); } @Test public void should_not_delete_h2_jar_from_classpath_if_h2_is_used_for_bdm() throws Exception { // given: System.setProperty("db.vendor", "postgres"); System.setProperty("db.database.name", "bonita"); System.setProperty("db.user", "bonita"); System.setProperty("db.password", "bpm"); System.setProperty("bdm.db.vendor", "h2"); System.setProperty("bdm.db.database.name", "business_data.db"); System.setProperty("bdm.db.user", "sa"); System.setProperty("bdm.db.password", ""); // given: final Path h2jarPath = tomcatFolder.resolve("lib").resolve("bonita").resolve("h2.jar"); assertThat(h2jarPath).exists(); // when: configurator.configureApplicationServer(); // then: assertThat(h2jarPath).exists(); } @Test public void should_not_delete_h2_jar_from_classpath_if_h2_is_used_for_bonita() throws Exception { // given: System.setProperty("db.vendor", "h2"); System.setProperty("db.database.name", "internal_database.db"); System.setProperty("db.user", "myUser"); System.setProperty("db.password", "myPwd"); System.setProperty("bdm.db.vendor", "postgres"); System.setProperty("bdm.db.database.name", "business_data"); System.setProperty("bdm.db.user", "bizUser"); System.setProperty("bdm.db.password", "bizPwd"); // given: final Path h2jarPath = tomcatFolder.resolve("lib").resolve("bonita").resolve("h2.jar"); assertThat(h2jarPath).exists(); // when: configurator.configureApplicationServer(); // then: assertThat(h2jarPath).exists(); } @Test public void should_not_delete_h2_jar_from_classpath_if_h2_is_used_for_bonita_and_for_BDM() throws Exception { // given: System.setProperty("db.vendor", "h2"); System.setProperty("db.database.name", "internal_database.db"); System.setProperty("db.user", "myUser"); System.setProperty("db.password", "myPwd"); System.setProperty("bdm.db.vendor", "h2"); System.setProperty("bdm.db.database.name", "business_data.db"); System.setProperty("bdm.db.user", "sa"); System.setProperty("bdm.db.password", ""); // given: final Path h2jarPath = tomcatFolder.resolve("lib").resolve("bonita").resolve("h2.jar"); assertThat(h2jarPath).exists(); // when: configurator.configureApplicationServer(); // then: assertThat(h2jarPath).exists(); } @Test public void should_not_fail_if_bonitaXml_file_does_not_pre_exist() throws Exception { // given: final Path bonitaXmlPath = tomcatFolder.resolve("conf").resolve("Catalina").resolve("localhost") .resolve("bonita.xml"); PathUtils.deleteFile(bonitaXmlPath); assertThat(bonitaXmlPath).doesNotExist(); // when: configurator.configureApplicationServer(); // then: assertThat(bonitaXmlPath).exists(); } @Test public void configureApplicationServer_should_update_setEnv_file() throws Exception { // given: System.setProperty("db.vendor", "postgres"); System.setProperty("bdm.db.vendor", "postgres"); // when: configurator.configureApplicationServer(); // then: assertThat(numberOfBackups("setenv.sh")).isEqualTo(1); assertThat(numberOfBackups("setenv.bat")).isEqualTo(1); checkFileContains(tomcatFolder.resolve("bin").resolve("setenv.sh"), "-Dsysprop.bonita.db.vendor=postgres", "-Dsysprop.bonita.bdm.db.vendor=postgres"); checkFileContains(tomcatFolder.resolve("bin").resolve("setenv.bat"), "-Dsysprop.bonita.db.vendor=postgres", "-Dsysprop.bonita.bdm.db.vendor=postgres"); } private int numberOfBackups(String file) { final String[] backupFiles = bundleFolder.resolve("setup").resolve("tomcat-backups").toFile() .list(new RegexFileFilter(file + "\\.[0-9-_hms]*")); return backupFiles == null ? 0 : backupFiles.length; } @Test public void configureApplicationServer_should_fail_if_no_driver_folder() throws Exception { // given: final Path driverFolder = bundleFolder.resolve("setup").resolve("lib"); FileUtils.deleteDirectory(driverFolder.toFile()); // when - then: assertThatExceptionOfType(PlatformException.class).isThrownBy(configurator::configureApplicationServer) .withMessage("Drivers folder not found: " + driverFolder + ". Make sure it exists and put a jar or zip file containing drivers there."); } @Test public void configureApplicationServer_should_update_bonitaXml_file() throws Exception { // given: System.setProperty("db.vendor", "postgres"); System.setProperty("db.server.name", "db.localhost"); System.setProperty("db.server.port", "5432"); System.setProperty("db.database.name", "bonita"); System.setProperty("db.user", "bonita"); System.setProperty("db.password", "bpm"); System.setProperty("bdm.db.vendor", "postgres"); System.setProperty("bdm.db.server.name", "biz.localhost"); System.setProperty("bdm.db.server.port", "5433"); System.setProperty("bdm.db.database.name", "business_data"); System.setProperty("bdm.db.user", "bizUser"); System.setProperty("bdm.db.password", "bizPwd"); System.setProperty("connection-pool.maxTotal", "200"); // when: configurator.configureApplicationServer(); // then: final Path bonitaXml = tomcatFolder.resolve("conf").resolve("Catalina").resolve("localhost") .resolve("bonita.xml"); checkFileContains(bonitaXml, "validationQuery=\"SELECT 1\"", "username=\"bonita\"", "password=\"bpm\"", "serverName=\"db.localhost\"", "portNumber=\"5432\"", "port=\"5432\"", "databaseName=\"bonita\"", "url=\"jdbc:postgresql://db.localhost:5432/bonita\"", "dataSourceURL=\"jdbc:postgresql://db.localhost:5432/bonita\""); checkFileContains(bonitaXml, "validationQuery=\"SELECT 1\"", "username=\"bizUser\"", "password=\"bizPwd\"", "serverName=\"biz.localhost\"", "portNumber=\"5433\"", "port=\"5433\"", "databaseName=\"business_data\"", "url=\"jdbc:postgresql://biz.localhost:5433/business_data\"", "dataSourceURL=\"jdbc:postgresql://biz.localhost:5433/business_data\""); checkFileContains(bonitaXml, "driverClassName=\"org.postgresql.Driver\"", "type=\"org.postgresql.xa.PGXADataSource\"", "class=\"org.postgresql.xa.PGXADataSource\"", "factory=\"org.postgresql.xa.PGXADataSourceFactory\""); checkFileContains(bonitaXml, "initialSize=\"8\"", // maxTotal value overridden in database.properties "maxTotal=\"200\"", "minIdle=\"8\"", "maxIdle=\"16\""); assertThat(numberOfBackups("bonita.xml")).isEqualTo(1); } @Test public void configureApplicationServer_should_support_H2_replacements_for_Bonita_database_and_BDM() throws Exception { // given: System.setProperty("db.vendor", "h2"); System.setProperty("db.database.name", "internal_database.db"); System.setProperty("db.user", "myUser"); System.setProperty("db.password", "myPwd"); System.setProperty("bdm.db.vendor", "h2"); System.setProperty("bdm.db.database.name", "internal_business_data.db"); System.setProperty("bdm.db.user", "bizUser"); System.setProperty("bdm.db.password", "bizPwd"); // when: configurator.configureApplicationServer(); // then: final Path bonitaXml = tomcatFolder.resolve("conf").resolve("Catalina").resolve("localhost") .resolve("bonita.xml"); checkFileContains(bonitaXml, "validationQuery=\"SELECT 1\"", "username=\"myUser\"", "password=\"myPwd\"", "driverClassName=\"org.h2.Driver\"", "url=\"jdbc:h2:file:" + databaseAbsolutePath + "/internal_database.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\"", "dataSourceURL=\"jdbc:h2:file:" + databaseAbsolutePath + "/internal_database.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\""); checkFileContains(bonitaXml, "validationQuery=\"SELECT 1\"", "username=\"bizUser\"", "password=\"bizPwd\"", "driverClassName=\"org.h2.Driver\"", "url=\"jdbc:h2:file:" + databaseAbsolutePath + "/internal_business_data.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\"", "dataSourceURL=\"jdbc:h2:file:" + databaseAbsolutePath + "/internal_business_data.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\""); } @Test public void configureApplicationServer_should_support_special_characters_for_h2_database() throws Exception { // given: System.setProperty("db.vendor", "h2"); System.setProperty("db.database.name", "bonita_with$dollarXXX.db"); System.setProperty("db.user", "_bonita_with$dollar\\andBackSlash"); System.setProperty("db.password", "bpm_With$dollar\\andBackSlash"); System.setProperty("bdm.db.vendor", "h2"); System.setProperty("bdm.db.database.name", "bonita_bdm_with$dollarXXX.db"); System.setProperty("bdm.db.user", "_bdmWith$dollar\\andBackSlash"); System.setProperty("bdm.db.password", "bdm_bpm_With$dollar\\andBackSlash"); // when: configurator.configureApplicationServer(); // then: final Path bonitaXml = tomcatFolder.resolve("conf").resolve("Catalina").resolve("localhost") .resolve("bonita.xml"); checkFileContains(bonitaXml, "validationQuery=\"SELECT 1\"", "username=\"_bonita_with$dollar\\andBackSlash\"", "password=\"bpm_With$dollar\\andBackSlash\"", "driverClassName=\"org.h2.Driver\"", "url=\"jdbc:h2:file:" + databaseAbsolutePath + "/bonita_bdm_with$dollarXXX.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\"", "dataSourceURL=\"jdbc:h2:file:" + databaseAbsolutePath + "/bonita_bdm_with$dollarXXX.db;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE;\""); } @Test public void configureApplicationServer_should_support_special_characters_for_database() throws Exception { // given: System.setProperty("db.vendor", "postgres"); System.setProperty("db.server.name", "localhost"); System.setProperty("db.server.port", "5432"); System.setProperty("db.database.name", "bonita_with$dollarXXX\\myInstance.of.bonita&perf=good"); System.setProperty("db.user", "_bonita_with$dollar\\andBackSlash"); System.setProperty("db.password", "bpm_With$dollar\\andBackSlash"); System.setProperty("bdm.db.vendor", "postgres"); System.setProperty("bdm.db.server.name", "localhost"); System.setProperty("bdm.db.server.port", "5432"); System.setProperty("bdm.db.database.name", "bonita_bdm_with$dollarXXX\\myInstance.of.bdm&perf=good?host.net.disableOob=true"); System.setProperty("bdm.db.user", "_bdmWith$dollar\\andBackSlash"); System.setProperty("bdm.db.password", "bdm_bpm_With$dollar\\andBackSlash"); // when: configurator.configureApplicationServer(); // then: final Path bonitaXml = tomcatFolder.resolve("conf").resolve("Catalina").resolve("localhost") .resolve("bonita.xml"); checkFileContains(bonitaXml, "validationQuery=\"SELECT 1\"", "username=\"_bonita_with$dollar\\andBackSlash\"", "password=\"bpm_With$dollar\\andBackSlash\"", "driverClassName=\"org.postgresql.Driver\"", "url=\"jdbc:postgresql://localhost:5432/bonita_with$dollarXXX\\myInstance.of.bonita&perf=good\"", "dataSourceURL=\"jdbc:postgresql://localhost:5432/bonita_with$dollarXXX\\myInstance.of.bonita&perf=good\"", "url=\"jdbc:postgresql://localhost:5432/bonita_bdm_with$dollarXXX\\myInstance.of.bdm&perf=good?host.net.disableOob=true\"", "dataSourceURL=\"jdbc:postgresql://localhost:5432/bonita_bdm_with$dollarXXX\\myInstance.of.bdm&perf=good?host.net.disableOob=true\""); } @Test public void should_copy_both_drivers_if_not_the_same_dbVendor_for_bdm() throws Exception { // given: System.setProperty("db.vendor", "h2"); System.setProperty("bdm.db.vendor", "postgres"); // when: spy.configureApplicationServer(); // then: verify(spy).copyDriverFile(any(Path.class), any(Path.class), eq("h2")); verify(spy).copyDriverFile(any(Path.class), any(Path.class), eq("postgres")); } @Test public void should_not_copy_drivers_again_if_same_dbVendor_for_bdm() throws Exception { // given: System.setProperty("db.vendor", "postgres"); System.setProperty("bdm.db.vendor", "postgres"); // when: spy.configureApplicationServer(); // then: verify(spy, times(1)).copyDriverFile(any(Path.class), any(Path.class), anyString()); } @Test public void should_fail_if_tomcat_mandatory_file_not_present() throws Exception { final Path confFile = tomcatFolder.resolve("bin").resolve("setenv.sh"); FileUtils.delete(confFile.toFile()); // when - then: assertThatExceptionOfType(PlatformException.class).isThrownBy(configurator::configureApplicationServer) .withMessage("File setenv.sh is mandatory but is not found"); } @Test public void configureApplicationServer_should_fail_if_drivers_not_found() throws Exception { // given: final String dbVendor = "postgres"; System.setProperty("db.vendor", dbVendor); final Path libFolder = bundleFolder.resolve("setup").resolve("lib"); final Path driverJar = libFolder.resolve("postgres9.2-drivers.jar"); FileUtils.delete(driverJar.toFile()); // when - then: assertThatExceptionOfType(PlatformException.class).isThrownBy(configurator::configureApplicationServer) .withMessage("No " + dbVendor + " drivers found in folder " + libFolder + ". Make sure to put a jar or zip file containing drivers there."); } @Test public void exception_in_configure_should_restore_previous_configuration() throws Exception { // given: doThrow(PlatformException.class).when(spy).copyDatabaseDriversIfNecessary(any(Path.class), any(Path.class), eq("h2")); // when - then: assertThatExceptionOfType(PlatformException.class).isThrownBy(spy::configureApplicationServer); verify(spy).restorePreviousConfiguration(any(Path.class), any(Path.class), any(Path.class)); } @Test public void should_not_make_backup_if_content_is_the_same() throws Exception { // given: System.setProperty("db.vendor", "postgres"); System.setProperty("db.database.name", "bonita"); System.setProperty("db.user", "bonita"); System.setProperty("db.password", "bpm"); configurator.configureApplicationServer(); assertThat(numberOfBackups("bonita.xml")).isEqualTo(1); assertThat(numberOfBackups("setenv.bat")).isEqualTo(1); assertThat(numberOfBackups("setenv.sh")).isEqualTo(1); // when: configurator.configureApplicationServer(); // then: assertThat(numberOfBackups("bonita.xml")).isEqualTo(1); assertThat(numberOfBackups("setenv.bat")).isEqualTo(1); assertThat(numberOfBackups("setenv.sh")).isEqualTo(1); } @Test public void should_make_new_backup_if_configuration_changes() throws Exception { // given: System.setProperty("db.vendor", "postgres"); System.setProperty("db.database.name", "bonita"); System.setProperty("db.user", "bonita"); System.setProperty("db.password", "bpm"); configurator.configureApplicationServer(); assertThat(numberOfBackups("bonita.xml")).isEqualTo(1); assertThat(numberOfBackups("setenv.bat")).isEqualTo(1); assertThat(numberOfBackups("setenv.sh")).isEqualTo(1); System.setProperty("db.vendor", "h2"); System.setProperty("db.database.name", "bonita_journal.db"); System.setProperty("db.user", "sa"); System.setProperty("db.password", ""); // so that horodated file has different Thread.sleep(1020); // when: new TomcatBundleConfigurator(bundleFolder).configureApplicationServer(); // then: assertThat(numberOfBackups("bonita.xml")).isEqualTo(2); assertThat(numberOfBackups("setenv.bat")).isEqualTo(2); assertThat(numberOfBackups("setenv.sh")).isEqualTo(2); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/util/ConfigurationFolderUtilTest.java ================================================ /** * 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.platform.util; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.platform.setup.PlatformSetup.PLATFORM_CONF_FOLDER_NAME; import static org.bonitasoft.platform.util.ConfigurationFolderUtil.ALL_SQL_FILES; import java.io.File; import java.nio.file.Path; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; /** * @author Laurent Leseigneur */ public class ConfigurationFolderUtilTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void should_copy_all_sql_files() throws Exception { //given ConfigurationFolderUtil configurationFolderUtil = new ConfigurationFolderUtil(); Path setupFolder = temporaryFolder.newFolder().toPath(); //when configurationFolderUtil.buildSqlFolder(setupFolder, "h2"); //then final File expectedFolder = setupFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve("sql").resolve("h2") .toFile(); assertThat(expectedFolder).exists().isDirectory(); assertThat(expectedFolder.listFiles()).extracting("name").hasSize(7).containsOnly(ALL_SQL_FILES); } @Test public void should_build_initial_folder() throws Exception { //given ConfigurationFolderUtil configurationFolderUtil = new ConfigurationFolderUtil(); Path setupFolder = temporaryFolder.newFolder().toPath(); //when configurationFolderUtil.buildPlatformEngineFolder(setupFolder); //then final File expectedFolder = setupFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve("initial") .resolve("platform_engine").toFile(); assertThat(expectedFolder).exists().isDirectory(); assertThat(expectedFolder.listFiles()).extracting("name").hasSize(1).containsOnly("initialConfig.properties"); } @Test public void should_build_current_folder() throws Exception { //given ConfigurationFolderUtil configurationFolderUtil = new ConfigurationFolderUtil(); Path setupFolder = temporaryFolder.newFolder().toPath(); //when configurationFolderUtil.buildCurrentFolder(setupFolder); //then final File expectedFolder = setupFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve("current") .resolve("platform_engine").toFile(); assertThat(expectedFolder).exists().isDirectory(); assertThat(expectedFolder.listFiles()).extracting("name").hasSize(1).containsOnly("currentConfig.properties"); } } ================================================ FILE: platform/platform-setup/src/test/java/org/bonitasoft/platform/version/impl/VersionServiceImplIT.java ================================================ /** * 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.platform.version.impl; import static org.assertj.core.api.Assertions.assertThat; import javax.sql.DataSource; import org.bonitasoft.platform.setup.PlatformSetupApplication; import org.bonitasoft.platform.setup.ScriptExecutor; import org.bonitasoft.platform.setup.jndi.MemoryJNDISetup; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.PropertySource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; import org.springframework.test.context.junit4.SpringRunner; /** * @author laurent Leseigneur */ @RunWith(SpringRunner.class) //keep order @SpringBootTest(classes = { PlatformSetupApplication.class }) @ComponentScan(basePackages = { "org.bonitasoft.platform.setup", "org.bonitasoft.platform.configuration", "org.bonitasoft.platform.version" }) @PropertySource("classpath:/application.properties") @Component public class VersionServiceImplIT { @Autowired MemoryJNDISetup memoryJNDISetup; @Autowired JdbcTemplate jdbcTemplate; @Value("${db.vendor}") String dbVendor; @Autowired VersionServiceImpl versionService; @Autowired DataSource dataSource; @Autowired ScriptExecutor scriptExecutor; @Before public void setUpDb() throws Exception { scriptExecutor.createAndInitializePlatformIfNecessary(); } @After public void cleanUpDB() throws Exception { scriptExecutor.deleteTables(); } @Test public void should_insert_version_in_database() throws Exception { //when final String platformVersion = versionService.retrieveDatabaseSchemaVersion(); //then assertThat(platformVersion).as("should return same version") .isEqualTo(versionService.getSupportedDatabaseSchemaVersion()); } @Test public void should_have_same_version() throws Exception { //then assertThat(versionService.isValidPlatformVersion()).as("should insert valid version").isTrue(); } @Test public void should_not_have_same_version() throws Exception { //given jdbcTemplate.execute("UPDATE platform set version='a.b.c' "); //then assertThat(versionService.isValidPlatformVersion()).as("should insert valid version").isFalse(); } } ================================================ FILE: platform/platform-setup/src/test/resources/ConfigurationChecker/lib/postgres_drivers_1.2.3.4.jar ================================================ ================================================ FILE: platform/platform-setup/src/test/resources/ConfigurationChecker_KO/lib/otherJar.jar ================================================ ================================================ FILE: platform/platform-setup/src/test/resources/conf/bonita-platform-community.properties ================================================ #content not used for testing ================================================ FILE: platform/platform-setup/src/test/resources/conf/bonita-platform-custom.xml ================================================ ================================================ FILE: platform/platform-setup/src/test/resources/database.properties ================================================ ##################################################################### # Nominal database.properties copied from src/main/standalone folder ##################################################################### #################################################################################### # # Modify the following values to suit your database needs. # Fore more information, see file ../HOW_TO_CONFIGURE_AND_RUN.txt # #################################################################################### ######################################### # Bonita database properties ######################################### # Valid values for Community edition: h2, postgres # Valid values for Enterprise editions: h2, postgres, sqlserver, oracle, mysql db.vendor=h2 # when using h2, no server or port setting is needed since connexion is made using file protocol mode using relative directory: db.server.name=SERVER_NAME db.server.port=SERVER_PORT # if your database name contains a backslash (\) character, you must double it (\\): db.database.name=bonita_journal.db db.user=sa # if your database password contains a backslash (\) character, you must double it (\\): db.password= ################################### # Business Data database properties ################################### # Valid values for Community edition: h2, postgres # Valid values for Enterprise editions: h2, postgres, sqlserver, oracle, mysql bdm.db.vendor=h2 bdm.db.server.name=SERVER_NAME bdm.db.server.port=SERVER_PORT bdm.db.database.name=business_data.db bdm.db.user=sa bdm.db.password= # IMPORTANT NOTE regarding H2 database: # in case you move whole setup folder to another directory, you must change property below # to point to original folder containing h2 database folder # new value can be relative or absolute since it still points to the right folder # WARNING for Windows users: keep forward slashes like below (instead of backslashes): h2.database.dir=../h2_database ================================================ FILE: platform/platform-setup/src/test/resources/datasource-config/database.properties ================================================ ##################################### # Bonita internal database properties ##################################### db.vendor=postgres db.server.name=localhost db.server.port=5432 db.database.name=bonita db.user=bonita db.password=bpm # Override default maxTotal value connection-pool.maxTotal = 200 ##################################### # Business Data database properties ##################################### bdm.db.vendor=oracle bdm.db.server.name=ora1.rd.lan bdm.db.server.port=1521 bdm.db.database.name=ORCL_with\\backslash bdm.db.user=bizUser bdm.db.password=bizPwd h2.database.dir=../h2_database ================================================ FILE: platform/platform-setup/src/test/resources/datasource-config/database_with_space_values.properties ================================================ ##################################### # Bonita internal database properties ##################################### db.vendor=postgres db.server.name=localhost db.server.port=5432 db.database.name= bonita db.user=bonita db.password=bpm ##################################### # Business Data database properties ##################################### bdm.db.vendor=postgres bdm.db.server.name= postgres.rd.lan bdm.db.server.port= 5432 bdm.db.database.name= business_data bdm.db.user=bonita bdm.db.password=bpm ================================================ FILE: platform/platform-setup/src/test/resources/datasource-config/incomplete_database.properties ================================================ ##################################### # Test: db.server.port property is missing ##################################### db.vendor=postgres db.server.name=localhost # db.server.port=5432 db.database.name=bonita db.user=bonita db.password=bpm ================================================ FILE: platform/platform-setup/src/test/resources/datasource-config/incomplete_internal.properties ================================================ postgres.xaDriver=org.postgresql.xa.PGXADataSource postgres.xaDSFactory=org.postgresql.xa.PGXADataSourceFactory # non-XA Driver is missing here !! ########################### ## Bonita internal database ########################### # postgres properties postgres.url=jdbc:postgresql://${db.server.name}:${db.server.port}/${db.database.name} postgres.testQuery=SELECT 1 # spring properties spring.datasource.username=${db.user} spring.datasource.password=${db.password} spring.datasource.driver-class-name=${${db.vendor}.nonXaDriver} spring.datasource.url=${${db.vendor}.url} connection-pool.initialSize=8 connection-pool.maxTotal=50 connection-pool.minIdle=8 connection-pool.maxIdle=16 ########################### # BusinessData database ########################### # postgres properties postgres.bdm.url=jdbc:postgresql://${bdm.db.server.name}:${bdm.db.server.port}/${bdm.db.database.name} postgres.bdm.testQuery=SELECT 1 bdm.connection-pool.initialSize=4 bdm.connection-pool.maxTotal=20 bdm.connection-pool.minIdle=4 bdm.connection-pool.maxIdle=10 ================================================ FILE: platform/platform-setup/src/test/resources/datasource-config/internal.properties ================================================ # Nominal internal.properties copied from src/main/standalone module h2.nonXaDriver=org.h2.Driver h2.xaDriver=org.h2.jdbcx.JdbcDataSource h2.xaDSFactory=org.h2.jdbcx.JdbcDataSourceFactory postgres.nonXaDriver=org.postgresql.Driver postgres.xaDriver=org.postgresql.xa.PGXADataSource postgres.xaDSFactory=org.postgresql.xa.PGXADataSourceFactory ########################### ## Bonita database ########################### # h2 properties h2.url=jdbc:h2:file:${h2.database.dir}/${db.database.name};DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE; h2.testQuery=SELECT 1 # postgres properties postgres.url=jdbc:postgresql://${db.server.name}:${db.server.port}/${db.database.name} postgres.testQuery=SELECT 1 # spring properties spring.datasource.username=${db.user} spring.datasource.password=${db.password} spring.datasource.driver-class-name=${${db.vendor}.nonXaDriver} spring.datasource.url=${${db.vendor}.url} # The initial number of connections when the connection pool starts. connection-pool.initialSize=8 # The maximum number of active connections that can be allocated from this pool at the same time. connection-pool.maxTotal=50 # The minimum number of active connections that always established after pool created and connection has reached this size. connection-pool.minIdle=8 # The maximum number of connections that should be kept in the pool at all times. connection-pool.maxIdle=16 ########################### # Business Data database ########################### # h2 properties h2.bdm.url=jdbc:h2:file:${h2.database.dir}/${bdm.db.database.name};DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE; h2.bdm.testQuery=SELECT 1 # postgres properties postgres.bdm.url=jdbc:postgresql://${bdm.db.server.name}:${bdm.db.server.port}/${bdm.db.database.name} postgres.bdm.testQuery=SELECT 1 # The initial number of connections when the connection pool starts. bdm.connection-pool.initialSize=4 # The maximum number of active connections that can be allocated from this pool at the same time. bdm.connection-pool.maxTotal=20 # The minimum number of active connections that always established after pool created and connection has reached this size. bdm.connection-pool.minIdle=4 # The maximum number of connections that should be kept in the pool at all times. bdm.connection-pool.maxIdle=10 ================================================ FILE: platform/platform-setup/src/test/resources/datasource-config/missingDriverClass_internal.properties ================================================ postgres.xaDriver=org.postgresql.xa.PGXADataSource postgres.nonXaDriver=org.404.NonExistent postgres.xaDSFactory=org.postgresql.xa.PGXADataSourceFactory connection-pool.initialSize=8 connection-pool.maxTotal=50 connection-pool.minIdle=8 connection-pool.maxIdle=16 # non-XA Driver is missing here !! ########################### ## Bonita internal database ########################### # postgres properties postgres.url=jdbc:postgresql://${db.server.name}:${db.server.port}/${db.database.name} postgres.testQuery=SELECT 1 # spring properties spring.datasource.username=${db.user} spring.datasource.password=${db.password} spring.datasource.driver-class-name=${${db.vendor}.nonXaDriver} spring.datasource.url=${${db.vendor}.url} ########################### # BusinessData database ########################### # postgres properties postgres.bdm.url=jdbc:postgresql://${bdm.db.server.name}:${bdm.db.server.port}/${bdm.db.database.name} postgres.bdm.testQuery=SELECT 1 bdm.connection-pool.initialSize=4 bdm.connection-pool.maxTotal=20 bdm.connection-pool.minIdle=4 bdm.connection-pool.maxIdle=10 ================================================ FILE: platform/platform-setup/src/test/resources/internal.properties ================================================ ##################################################################### # Nominal internal.properties copied from src/main/standalone folder ##################################################################### ################################################################################## # # # properties below must NOT be modified unless specific requirements # # # ################################################################################## # /!\ WARNING /!\ # # Any value containing a backslash (\) character MUST be doubled (\\) # ################################################################################## h2.nonXaDriver=org.h2.Driver h2.xaDriver=org.h2.jdbcx.JdbcDataSource h2.xaDSFactory=org.h2.jdbcx.JdbcDataSourceFactory postgres.nonXaDriver=org.postgresql.Driver postgres.xaDriver=org.postgresql.xa.PGXADataSource postgres.xaDSFactory=org.postgresql.xa.PGXADataSourceFactory ########################### ## Bonita database ########################### # h2 properties h2.url=jdbc:h2:file:${h2.database.dir}/${db.database.name};DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE; h2.testQuery=SELECT 1 # postgres properties postgres.url=jdbc:postgresql://${db.server.name}:${db.server.port}/${db.database.name} postgres.testQuery=SELECT 1 # spring properties spring.datasource.username=${db.user} spring.datasource.password=${db.password} spring.datasource.driver-class-name=${${db.vendor}.nonXaDriver} spring.datasource.url=${${db.vendor}.url} # The initial number of connections when the connection pool starts. connection-pool.initialSize=8 # The maximum number of active connections that can be allocated from this pool at the same time. connection-pool.maxTotal=50 # The minimum number of active connections that always established after pool created and connection has reached this size. connection-pool.minIdle=8 # The maximum number of connections that should be kept in the pool at all times. connection-pool.maxIdle=16 ########################### # Business Data database ########################### # h2 properties h2.bdm.url=jdbc:h2:file:${h2.database.dir}/${bdm.db.database.name};DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTO_SERVER=TRUE; h2.bdm.testQuery=SELECT 1 # postgres properties postgres.bdm.url=jdbc:postgresql://${bdm.db.server.name}:${bdm.db.server.port}/${bdm.db.database.name} postgres.bdm.testQuery=SELECT 1 # The initial number of connections when the connection pool starts. bdm.connection-pool.initialSize=4 # The maximum number of active connections that can be allocated from this pool at the same time. bdm.connection-pool.maxTotal=20 # The minimum number of active connections that always established after pool created and connection has reached this size. bdm.connection-pool.minIdle=4 # The maximum number of connections that should be kept in the pool at all times. bdm.connection-pool.maxIdle=10 ================================================ FILE: platform/platform-setup/src/test/resources/logback.xml ================================================ %d{HH:mm:ss.SSS}|%-5level|%logger{16}| %msg%n ================================================ FILE: platform/platform-setup/src/test/resources/tomcat_conf/server/bin/catalina.sh ================================================ ================================================ FILE: platform/platform-setup/src/test/resources/tomcat_conf/server/bin/setenv.bat ================================================ rem This is a test file, no need to put the full content here set DB_OPTS="-Dsysprop.bonita.db.vendor=h2" set BDM_DB_OPTS="-Dsysprop.bonita.bdm.db.vendor=h2" ================================================ FILE: platform/platform-setup/src/test/resources/tomcat_conf/server/bin/setenv.sh ================================================ # This is a test file, no need to put the full content here DB_OPTS="-Dsysprop.bonita.db.vendor=h2" BDM_DB_OPTS="-Dsysprop.bonita.bdm.db.vendor=h2" ================================================ FILE: platform/platform-setup/src/test/resources/tomcat_conf/server/conf/Catalina/localhost/bonita.xml ================================================ ================================================ FILE: platform/platform-setup/src/test/resources/tomcat_conf/server/lib/bonita/.gitignore ================================================ # Created by .ignore support plugin (hsz.mobi) ================================================ FILE: platform/platform-setup/src/test/resources/tomcat_conf/server/lib/bonita/h2.jar ================================================ ================================================ FILE: platform/platform-setup/src/test/resources/tomcat_conf/setup/lib/drivers-h2-2.12.117.jar ================================================ ================================================ FILE: platform/platform-setup/src/test/resources/tomcat_conf/setup/lib/ojdbc-6.jar ================================================ ================================================ FILE: platform/platform-setup/src/test/resources/tomcat_conf/setup/lib/postgres9.2-drivers.jar ================================================ ================================================ FILE: platform/platform-setup/src/test/resources/tomcat_conf/setup/tomcat-templates/bonita.xml ================================================ ================================================ FILE: platform/platform-setup/src/test/resources/tomcat_conf/setup/tomcat-templates/setenv.bat ================================================ rem This is a test file, no need to put the full content here set DB_OPTS="-Dsysprop.bonita.db.vendor=h2" set BDM_DB_OPTS="-Dsysprop.bonita.bdm.db.vendor=h2" ================================================ FILE: platform/platform-setup/src/test/resources/tomcat_conf/setup/tomcat-templates/setenv.sh ================================================ # This is a test file, no need to put the full content here DB_OPTS="-Dsysprop.bonita.db.vendor=h2" BDM_DB_OPTS="-Dsysprop.bonita.bdm.db.vendor=h2" ================================================ FILE: platform/platform-setup-test/build.gradle ================================================ dependencies { api libs.commonsLang api libs.commonsText api libs.commonsExec } group = 'org.bonitasoft.platform' ================================================ FILE: platform/platform-setup-test/src/main/java/org/bonitasoft/platform/setup/PlatformSetupTestUtils.java ================================================ /** * 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.platform.setup; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Paths; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.HashMap; import java.util.Objects; import java.util.Properties; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.OS; import org.apache.commons.exec.PumpStreamHandler; import org.apache.commons.text.StringSubstitutor; /** * @author Baptiste Mesta */ public class PlatformSetupTestUtils { private static void writeZipInputToFile(final ZipInputStream zipInputstream, final File outputFile) throws FileNotFoundException, IOException { // The input is a file. An FileOutputStream is created to write the content of the new file. outputFile.getParentFile().mkdirs(); try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile)) { // The contents of the new file, that is read from the ZipInputStream using a buffer (byte []), is written. int bytesRead; final byte[] buffer = new byte[1024]; while ((bytesRead = zipInputstream.read(buffer)) > -1) { fileOutputStream.write(buffer, 0, bytesRead); } fileOutputStream.flush(); } catch (final IOException ioe) { // In case of error, the file is deleted outputFile.delete(); throw ioe; } } public static PumpStreamHandler getExecuteStreamHandler(String answer) { return new PumpStreamHandler(System.out, System.err, new ByteArrayInputStream(answer.getBytes())); } private static void extractZipEntries(final ZipInputStream zipInputstream, final File outputFolder) throws IOException { final String canonicalDestDir = outputFolder.getCanonicalPath() + File.separator; ZipEntry zipEntry; while ((zipEntry = zipInputstream.getNextEntry()) != null) { try { // For each entry, a file is created in the output directory "folder" final File outputFile = new File(outputFolder.getAbsolutePath(), zipEntry.getName()); // Prevent Zip Slip: ensure the resolved path stays within the output folder if (!outputFile.getCanonicalPath().startsWith(canonicalDestDir)) { throw new IOException("Zip entry is outside of the target directory"); } // If the entry is a directory, it creates in the output folder, and we go to the next entry (continue). if (zipEntry.isDirectory()) { outputFile.mkdirs(); continue; } writeZipInputToFile(zipInputstream, outputFile); } finally { zipInputstream.closeEntry(); } } } private static void unzipToFolder(final InputStream inputStream, final File outputFolder) throws IOException { try (ZipInputStream zipInputstream = new ZipInputStream(inputStream)) { extractZipEntries(zipInputstream, outputFolder); } } public static void extractDistributionTo(File distFolder) throws IOException { String distZipPath = System.getProperty("bonita.distribution.path"); File dist = null; if (distZipPath != null) { dist = new File(distZipPath); } else { File target = Paths.get("build").resolve("distributions").toFile(); if (!target.isDirectory()) { throw new IllegalStateException("No bonita.distribution.path set and no build folder exists"); } Pattern distribPattern = Pattern.compile("Bonita-platform-setup-.*\\.zip"); for (File file : Objects.requireNonNull(target.listFiles())) { if (distribPattern.matcher(file.getName()).matches()) { dist = file; break; } } } if (dist == null) { throw new IllegalStateException("Unable to locate the distribution"); } if (!dist.isFile()) { throw new IllegalStateException("Distribution is not a file: " + dist.getPath()); } try (InputStream inputStream = new FileInputStream(dist)) { unzipToFolder(inputStream, distFolder); } } public static Connection getJdbcConnection(File distFolder) throws Exception { return getJdbcConnection(distFolder, null); } public static Connection getJdbcConnection(File distFolder, String dbUser) throws IOException, SQLException { Properties properties = getDatabaseProperties(distFolder); properties.put("h2.database.dir", distFolder.toPath().resolve(properties.getProperty("h2.database.dir")).toString()); StringSubstitutor stringSubstitutor = new StringSubstitutor(new HashMap(properties)); return DriverManager.getConnection(stringSubstitutor.replace(properties.getProperty("h2.url")), dbUser != null ? dbUser : properties.getProperty("db.user"), properties.getProperty("db.password")); } private static Properties getDatabaseProperties(File distFolder) throws IOException { Properties properties = new Properties(); properties.load(new FileInputStream(distFolder.toPath().resolve("database.properties").toFile())); properties.load(new FileInputStream(distFolder.toPath().resolve("internal.properties").toFile())); return properties; } public static DefaultExecutor createExecutor(File distFolder) { DefaultExecutor oDefaultExecutor = new DefaultExecutor(); oDefaultExecutor.setWorkingDirectory(distFolder); return oDefaultExecutor; } public static CommandLine createCommandLine() { if (OS.isFamilyWindows() || OS.isFamilyWin9x()) { CommandLine oCmdLine = new CommandLine("cmd"); oCmdLine.addArgument("/c"); oCmdLine.addArgument("setup.bat"); return oCmdLine; } else { CommandLine oCmdLine = new CommandLine("sh"); oCmdLine.addArgument("setup.sh"); return oCmdLine; } } public static boolean isCommunityEdition(Class clazz) { try { return clazz.getClassLoader().loadClass("com.bonitasoft.platform.setup.PlatformSetupSP") == null; } catch (ClassNotFoundException e) { return true; } } } ================================================ FILE: platform/platform-setup-test/src/main/java/org/bonitasoft/platform/util/ConfigurationFolderUtil.java ================================================ /** * Copyright (C) 2016 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.platform.util; import static java.util.Arrays.asList; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; /** * @author Laurent Leseigneur */ public class ConfigurationFolderUtil { public static final String PLATFORM_CONF_FOLDER_NAME = "platform_conf"; public static final String[] ALL_SQL_FILES = new String[] { "cleanTables.sql", "createQuartzTables.sql", "createTables.sql", "dropQuartzTables.sql", "dropTables.sql", "initTables.sql", "preDropStructure.sql" }; public Path buildPlatformConfFolder(Path rootFolder) throws IOException { Path initialFolder = rootFolder.resolve(PLATFORM_CONF_FOLDER_NAME); Files.createDirectories(initialFolder); return initialFolder; } public void buildPlatformEngineFolder(Path rootFolder) throws IOException { Path platform_engine = rootFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve("initial") .resolve("platform_engine"); Files.createDirectories(platform_engine); Files.write(platform_engine.resolve("initialConfig.properties"), "key=value".getBytes()); } public Path buildCurrentFolder(Path rootFolder) throws IOException { Path platform_engine = rootFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve("current") .resolve("platform_engine"); Files.createDirectories(platform_engine); Files.write(platform_engine.resolve("currentConfig.properties"), "key=value".getBytes()); return platform_engine; } public void buildSqlFolder(Path rootFolder, String dbVendor) throws IOException { Path sqlPath = rootFolder.resolve(PLATFORM_CONF_FOLDER_NAME).resolve("sql").resolve(dbVendor); Files.createDirectories(sqlPath); for (String sqlFile : asList(ALL_SQL_FILES)) { Files.copy(ConfigurationFolderUtil.class.getResourceAsStream("/sql/" + dbVendor + "/" + sqlFile), sqlPath.resolve(sqlFile)); } } } ================================================ FILE: services/bonita-archive/build.gradle ================================================ dependencies { api project(':services:bonita-commons') api project(':services:bonita-transaction') api project(':services:bonita-persistence') testImplementation libs.mockitoCore testImplementation libs.logback } ================================================ FILE: services/bonita-archive/src/main/java/org/bonitasoft/engine/archive/ArchiveInsertRecord.java ================================================ /** * 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.archive; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; import org.bonitasoft.engine.recorder.model.InsertRecord; /** * @author Baptiste Mesta */ public class ArchiveInsertRecord extends InsertRecord { public ArchiveInsertRecord(final ArchivedPersistentObject entity) { super(entity); } @Override public ArchivedPersistentObject getEntity() { return (ArchivedPersistentObject) super.getEntity(); } } ================================================ FILE: services/bonita-archive/src/main/java/org/bonitasoft/engine/archive/ArchiveService.java ================================================ /** * 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.archive; import java.util.Map; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; /** * @author Feng Hui * @author Matthieu Chaffotte * @since 6.0 */ public interface ArchiveService { /** * Archive the given entity in sliding archive if relevant and in the appropriate definitive archive * * @param time * The archive date * @param record * Archive insert record containing the entity to be archived * @throws SRecorderException */ void recordInsert(long time, ArchiveInsertRecord record) throws SRecorderException; /** * Archive the given entities in the definitive archive * * @param time * the time of archiving * @param records * Archive inserts record containing the entity to be archived * @throws SRecorderException * in case of a write error */ void recordInserts(long time, ArchiveInsertRecord... records) throws SRecorderException; /** * Remove the given entity from both sliding archive (if present) and the right archive level (if present) * This operation should normally to be used. This is for admin purpose only * * @param record * The delete record containing archived entity to be deleted * @throws SRecorderException */ void recordDelete(DeleteRecord record) throws SRecorderException; /** * Get the ReadPersistenceService corresponding to the definitive archive * * @return the ReadPersistenceService corresponding to the definitive archive */ ReadPersistenceService getDefinitiveArchiveReadPersistenceService(); /** * @param sourceObjectClass * Persistent object to be judged achievable or not * @return Return true if the objects of the given class can be archived. */ boolean isArchivable(Class sourceObjectClass); int deleteFromQuery(String queryName, Map parameters) throws SRecorderException; } ================================================ FILE: services/bonita-archive/src/main/java/org/bonitasoft/engine/archive/ArchivingStrategy.java ================================================ /** * 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.archive; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Matthieu Chaffotte */ public interface ArchivingStrategy { boolean isArchivable(Class srcClass); } ================================================ FILE: services/bonita-archive/src/main/java/org/bonitasoft/engine/archive/impl/AbstractArchivingStrategy.java ================================================ /** * 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.archive.impl; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.archive.ArchivingStrategy; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Celine Souchet */ public abstract class AbstractArchivingStrategy implements ArchivingStrategy { final Map archives; AbstractArchivingStrategy() { archives = new HashMap<>(); } public AbstractArchivingStrategy(final Map archives) { this.archives = archives; } @Override public boolean isArchivable(final Class srcClass) { final Boolean isArchivable = archives.get(srcClass.getName()); if (isArchivable == null) { throw new SBonitaRuntimeException("The class '" + srcClass.getName() + "' is not known as archivable"); } return isArchivable; } } ================================================ FILE: services/bonita-archive/src/main/java/org/bonitasoft/engine/archive/impl/ArchiveServiceImpl.java ================================================ /** * 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.archive.impl; import java.util.Map; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.archive.ArchivingStrategy; import org.bonitasoft.engine.commons.ClassReflector; import org.bonitasoft.engine.commons.LogUtil; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.SPersistenceException; import org.bonitasoft.engine.transaction.STransactionNotFoundException; import org.bonitasoft.engine.transaction.UserTransactionService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Matthieu Chaffotte * @author Hongwen Zang * @author Celine Souchet */ public class ArchiveServiceImpl implements ArchiveService { private static final Logger log = LoggerFactory.getLogger(ArchiveServiceImpl.class); private final UserTransactionService transactionService; private final PersistenceService definitiveArchivePersistenceService; private ArchivingStrategy archivingStrategy; public ArchiveServiceImpl(final PersistenceService definitiveArchivePersistenceService, final ArchivingStrategy archivingStrategy, final UserTransactionService transactionService) { super(); this.definitiveArchivePersistenceService = definitiveArchivePersistenceService; this.archivingStrategy = archivingStrategy; this.transactionService = transactionService; } @Override public void recordInsert(final long time, final ArchiveInsertRecord record) throws SRecorderException { if (isArchivable(record.getEntity().getPersistentObjectInterface())) { recordInserts(time, record); } } @Override public void recordInserts(final long time, final ArchiveInsertRecord... records) throws SRecorderException { final String methodName = "recordInserts"; logBeforeMethod(methodName); if (records != null) { assignArchiveDate(time, records); final BatchArchiveCallable callable = buildBatchArchiveCallable(records); try { transactionService.registerBeforeCommitCallable(callable); } catch (final STransactionNotFoundException e) { if (log.isTraceEnabled()) { log.error( "Unable to register the beforeCommitCallable to log queriable logs: transaction not found", e); } } } logAfterMethod(methodName); } // As a protected method for test purposes. protected BatchArchiveCallable buildBatchArchiveCallable(final ArchiveInsertRecord... records) throws SRecorderException { return new BatchArchiveCallable(definitiveArchivePersistenceService, records); } private void assignArchiveDate(final long time, final ArchiveInsertRecord... records) throws SRecorderException { for (final ArchiveInsertRecord record : records) { if (record != null) { setArchiveDate(record.getEntity(), time); } } } private void setArchiveDate(final ArchivedPersistentObject entity, final long time) throws SRecorderException { if (entity.getArchiveDate() <= 0) { try { ClassReflector.invokeSetter(entity, "setArchiveDate", long.class, time); } catch (final Exception e) { throw new SRecorderException(e); } } } @Override public void recordDelete(final DeleteRecord record) throws SRecorderException { String methodName = "recordDelete"; try { logBeforeMethod(methodName); definitiveArchivePersistenceService.delete(record.getEntity()); logAfterMethod(methodName); } catch (final SPersistenceException e) { logOnExceptionMethod(methodName, e); throw new SRecorderException(e); } } private void logOnExceptionMethod(final String methodName, final Exception e) { if (log.isTraceEnabled()) { log.trace(LogUtil.getLogOnExceptionMethod(this.getClass(), methodName, e)); } } private void logAfterMethod(final String methodName) { if (log.isTraceEnabled()) { log.trace(LogUtil.getLogAfterMethod(this.getClass(), methodName)); } } private void logBeforeMethod(final String methodName) { if (log.isTraceEnabled()) { log.trace(LogUtil.getLogBeforeMethod(this.getClass(), methodName)); } } @Override public boolean isArchivable(final Class sourceObjectClass) { return archivingStrategy.isArchivable(sourceObjectClass); } @Override public ReadPersistenceService getDefinitiveArchiveReadPersistenceService() { return definitiveArchivePersistenceService; } @Override public int deleteFromQuery(String queryName, Map parameters) throws SRecorderException { try { return definitiveArchivePersistenceService.update(queryName, parameters); } catch (SPersistenceException e) { throw new SRecorderException(e); } } } ================================================ FILE: services/bonita-archive/src/main/java/org/bonitasoft/engine/archive/impl/BatchArchiveCallable.java ================================================ /** * 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.archive.impl; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.SPersistenceException; public class BatchArchiveCallable implements Callable { private final PersistenceService persistenceService; private final List archivedObjects; public BatchArchiveCallable(final PersistenceService persistenceService, final ArchiveInsertRecord... records) { this.persistenceService = persistenceService; if (records == null) { archivedObjects = new ArrayList<>(); } else { archivedObjects = createArchivedObjectsList(records); } } /** * @param records * @return */ protected List createArchivedObjectsList(final ArchiveInsertRecord... records) { final List archivedObjects = new ArrayList<>(); for (final ArchiveInsertRecord record : records) { if (record != null) { archivedObjects.add(record.getEntity()); } } return archivedObjects; } @Override public Void call() throws SPersistenceException { if (hasObjects()) { try { if (archivedObjects.size() == 1) { persistenceService.insert(archivedObjects.get(0)); } else { persistenceService.insertInBatch(new ArrayList(archivedObjects)); } } finally { // Do we still need to clear the list even if there was some Exceptions ? // What happens with the retry ? archivedObjects.clear(); } } return null; } public boolean hasObjects() { return archivedObjects != null && !archivedObjects.isEmpty(); } } ================================================ FILE: services/bonita-archive/src/main/java/org/bonitasoft/engine/archive/impl/DefaultArchivingStrategy.java ================================================ /** * 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.archive.impl; import java.util.Map; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class DefaultArchivingStrategy extends AbstractArchivingStrategy { public DefaultArchivingStrategy(Map additionalConfiguration) { super(); archives.put("org.bonitasoft.engine.core.process.comment.model.SComment", true); archives.put("org.bonitasoft.engine.core.document.model.SDocumentMapping", true); archives.put("org.bonitasoft.engine.core.process.instance.model.SProcessInstance", true); archives.put("org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance", true); archives.put("org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance", true); archives.put("org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance", true); archives.put("org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance", true); archives.put("org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance", true); archives.put("org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance", true); archives.put("org.bonitasoft.engine.core.process.instance.model.SGatewayInstance", true); archives.put("org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance", true); archives.put("org.bonitasoft.engine.core.process.instance.model.SConnectorInstance", true); archives.put("org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance", true); archives.put("org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance", true); archives.put("org.bonitasoft.engine.data.instance.model.SDataInstance", true); archives.put("org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance", true); archives.put("org.bonitasoft.engine.core.contract.data.SContractData", true); archives.put("org.bonitasoft.engine.core.process.instance.model.SBPMFailure", true); for (Map.Entry entry : additionalConfiguration.entrySet()) { if (!archives.containsKey(entry.getKey())) { archives.put(entry.getKey(), entry.getValue()); } } } } ================================================ FILE: services/bonita-archive/src/test/java/org/bonitasoft/engine/archive/impl/ArchiveServiceImplTest.java ================================================ /** * 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.archive.impl; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; import org.bonitasoft.engine.transaction.UserTransactionService; import org.junit.Test; public class ArchiveServiceImplTest { @Test public void should_recordInserts_register_beforeCommitCallable_v2() throws Exception { final UserTransactionService transactionService = mock(UserTransactionService.class); ArchiveServiceImpl archiveService = spy(new ArchiveServiceImpl(null, null, transactionService)); final ArchivedPersistentObjectWithSetter mockArchivedPersistentObject = mock( ArchivedPersistentObjectWithSetter.class); ArchiveInsertRecord record = new ArchiveInsertRecord(mockArchivedPersistentObject); BatchArchiveCallable mockBatchArchiveCallable = mock(BatchArchiveCallable.class); when(archiveService.buildBatchArchiveCallable(any(ArchiveInsertRecord.class))) .thenReturn(mockBatchArchiveCallable); long archiveDate = 3L; archiveService.recordInserts(archiveDate, record); verify(mockArchivedPersistentObject).setArchiveDate(eq(archiveDate)); verify(transactionService).registerBeforeCommitCallable(eq(mockBatchArchiveCallable)); } // Test with exception on TxService // Seen with Nicolas C. for this "interface extension" :) // Needed as the implementation calls setArchiveDate through reflection. interface ArchivedPersistentObjectWithSetter extends ArchivedPersistentObject { void setArchiveDate(long archiveDate); } } ================================================ FILE: services/bonita-archive/src/test/java/org/bonitasoft/engine/archive/impl/BatchArchiveCallableTest.java ================================================ /** * 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.archive.impl; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import java.util.List; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.SPersistenceException; import org.junit.Test; public class BatchArchiveCallableTest { @Test public void testCreateArchivedObjectsList() { final ArchiveInsertRecord record1 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class)); final ArchiveInsertRecord record2 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class)); final ArchiveInsertRecord record3 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class)); final ArchiveInsertRecord[] records = new ArchiveInsertRecord[] { record1, record2, record3 }; final BatchArchiveCallable callable = new BatchArchiveCallable(null, records); final List createArchivedObjectsList = callable.createArchivedObjectsList(records); assertThat(createArchivedObjectsList.size(), is(records.length)); for (int i = 0; i < records.length; i++) { final ArchivedPersistentObject archivedPersistentObject = createArchivedObjectsList.get(i); assertThat(archivedPersistentObject, is(records[i].getEntity())); } } @Test public void testCreateArchivedObjectsListWithNoRecords() { final ArchiveInsertRecord[] records = new ArchiveInsertRecord[] {}; final BatchArchiveCallable callable = new BatchArchiveCallable(null, records); final List createArchivedObjectsList = callable.createArchivedObjectsList(records); assertTrue(createArchivedObjectsList.isEmpty()); } @Test public void testCreateArchivedObjectsListWithNullRecords() { final ArchiveInsertRecord record1 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class)); final ArchiveInsertRecord record2 = null; final ArchiveInsertRecord record3 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class)); final ArchiveInsertRecord[] records = new ArchiveInsertRecord[] { record1, record2, record3 }; final BatchArchiveCallable callable = new BatchArchiveCallable(null, records); final List createArchivedObjectsList = callable.createArchivedObjectsList(records); // The second one that is null was skipped. assertThat(createArchivedObjectsList.size(), is(2)); ArchivedPersistentObject archivedPersistentObject; // Check the first one archivedPersistentObject = createArchivedObjectsList.get(0); assertThat(archivedPersistentObject, is(records[0].getEntity())); // Check the second that is the third one. archivedPersistentObject = createArchivedObjectsList.get(1); assertThat(archivedPersistentObject, is(records[2].getEntity())); } @Test public void testHasObjectsReturnsTrueWhenNotEmpty() { final ArchiveInsertRecord record1 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class)); final ArchiveInsertRecord record2 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class)); final ArchiveInsertRecord record3 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class)); final ArchiveInsertRecord[] records = new ArchiveInsertRecord[] { record1, record2, record3 }; final BatchArchiveCallable callable = new BatchArchiveCallable(null, records); assertTrue(callable.hasObjects()); } @Test public void testHasObjectsReturnsFalseWhenEmpty() { final BatchArchiveCallable callable = new BatchArchiveCallable(null, new ArchiveInsertRecord[] {}); assertFalse(callable.hasObjects()); } @Test public void testHasObjectsReturnsFalseWhenNull() { final BatchArchiveCallable callable = new BatchArchiveCallable(null, (ArchiveInsertRecord) null); assertFalse(callable.hasObjects()); } @Test public void testCallWithOneRecord() throws SPersistenceException { final ArchivedPersistentObject entity = mock(ArchivedPersistentObject.class); final ArchiveInsertRecord record1 = new ArchiveInsertRecord(entity); final ArchiveInsertRecord[] records = new ArchiveInsertRecord[] { record1 }; final PersistenceService persistenceService = mock(PersistenceService.class); final BatchArchiveCallable callable = new BatchArchiveCallable(persistenceService, records); callable.call(); verify(persistenceService).insert(eq(entity)); } @Test public void testCallWithMoreThanOneRecord() throws SPersistenceException { final ArchiveInsertRecord record1 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class)); final ArchiveInsertRecord record2 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class)); final ArchiveInsertRecord record3 = new ArchiveInsertRecord(mock(ArchivedPersistentObject.class)); final ArchiveInsertRecord[] records = new ArchiveInsertRecord[] { record1, record2, record3 }; final PersistenceService persistenceService = mock(PersistenceService.class); final BatchArchiveCallable callable = new BatchArchiveCallable(persistenceService, records); callable.call(); verify(persistenceService).insertInBatch(anyList()); } } ================================================ FILE: services/bonita-authentication/build.gradle ================================================ dependencies { api project(':services:bonita-identity') api project(':services:bonita-commons') testImplementation libs.mockitoCore } ================================================ FILE: services/bonita-authentication/src/main/java/org/bonitasoft/engine/authentication/AuthenticationConstants.java ================================================ /** * 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.authentication; /** * @author Julien Reboul * @author Celine Souchet */ public class AuthenticationConstants { public static final String BASIC_USERNAME = "authentication.username"; public static final String BASIC_PASSWORD = "authentication.password"; public static final String SAML_CREDENTIALS_PASSPHRASE = "authentication.passphrase"; public static final String BASIC_TENANT_ID = "authentication.tenant.id"; public static final String CAS_TICKET = "ticket"; public static final String CAS_SERVICE = "service"; private AuthenticationConstants() { // For Sonar } } ================================================ FILE: services/bonita-authentication/src/main/java/org/bonitasoft/engine/authentication/AuthenticationException.java ================================================ /** * 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.authentication; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public class AuthenticationException extends SBonitaException { private static final long serialVersionUID = 7204454677855421061L; public AuthenticationException(String message) { super(message); } public AuthenticationException() { super("The user name or password is not valid."); } public AuthenticationException(final Throwable t) { super(t); } } ================================================ FILE: services/bonita-authentication/src/main/java/org/bonitasoft/engine/authentication/GenericAuthenticationService.java ================================================ /** * 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.authentication; import java.io.Serializable; import java.util.Map; /** * @author Julien Reboul * @since 6.3 */ public interface GenericAuthenticationService { /** * Check user credentials by give user name and passwordHash * * @param credentials * the credentials elements to use to authenticate * @return the username of the user authenticated. * @throws AuthenticationException * Error thrown if either the password is invalid or the user is not found. */ String checkUserCredentials(Map credentials) throws AuthenticationException; } ================================================ FILE: services/bonita-authentication/src/main/java/org/bonitasoft/engine/authentication/GenericAuthenticationServiceAccessor.java ================================================ /** * 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.authentication; /** * @author Charles Souillard * @since 7.0 */ public class GenericAuthenticationServiceAccessor { private final GenericAuthenticationService genericAuthenticationService; public GenericAuthenticationServiceAccessor(GenericAuthenticationService genericAuthenticationService) { this.genericAuthenticationService = genericAuthenticationService; } public GenericAuthenticationService getAuthenticationService() { return this.genericAuthenticationService; } } ================================================ FILE: services/bonita-authentication/src/main/java/org/bonitasoft/engine/authentication/impl/AuthenticationServiceImpl.java ================================================ /** * 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.authentication.impl; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.authentication.AuthenticationConstants; import org.bonitasoft.engine.authentication.GenericAuthenticationService; import org.bonitasoft.engine.commons.LogUtil; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SUserNotFoundException; import org.bonitasoft.engine.identity.model.SUser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Hongwen Zang * @author Julien Reboul * @author Celine Souchet */ @Component("authenticationService") @Lazy public class AuthenticationServiceImpl implements GenericAuthenticationService { private Logger logger = LoggerFactory.getLogger(AuthenticationServiceImpl.class); private final IdentityService identityService; public AuthenticationServiceImpl(final IdentityService identityService) { this.identityService = identityService; } /** * @see org.bonitasoft.engine.authentication.GenericAuthenticationService#checkUserCredentials(java.util.Map) */ @Override public String checkUserCredentials(Map credentials) { final String methodName = "checkUserCredentials"; try { final String password = String.valueOf(credentials.get(AuthenticationConstants.BASIC_PASSWORD)); final String userName = String.valueOf(credentials.get(AuthenticationConstants.BASIC_USERNAME)); if (logger.isTraceEnabled()) { logger.trace( LogUtil.getLogBeforeMethod(this.getClass(), methodName)); } final SUser user = identityService.getUserByUserName(userName); if (identityService.checkCredentials(user, password)) { if (logger.isTraceEnabled()) { logger.trace( LogUtil.getLogAfterMethod(this.getClass(), methodName)); } return userName; } if (logger.isTraceEnabled()) { logger.trace( LogUtil.getLogAfterMethod(this.getClass(), methodName)); } } catch (final SUserNotFoundException sunfe) { if (logger.isTraceEnabled()) { logger.trace( LogUtil.getLogOnExceptionMethod(this.getClass(), methodName, sunfe)); } } return null; } } ================================================ FILE: services/bonita-authorization/build.gradle ================================================ dependencies { api project(':services:bonita-cache') api project(':services:bonita-business-application') api project(':services:bonita-persistence') api project(':services:bonita-session') api project(':services:bonita-commons') api project(':bpm:bonita-core:bonita-home-server') api project(':bpm:bonita-common') testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.logback annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/PermissionService.java ================================================ /** * 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.authorization; import java.util.Properties; import java.util.Set; import org.bonitasoft.engine.api.permission.APICallContext; import org.bonitasoft.engine.commons.TenantLifecycleService; import org.bonitasoft.engine.commons.exceptions.SExecutionException; /** * @author Baptiste Mesta */ public interface PermissionService extends TenantLifecycleService { public String USER_TYPE_AUTHORIZATION_PREFIX = "user"; public String PROFILE_TYPE_AUTHORIZATION_PREFIX = "profile"; public String SCRIPT_TYPE_AUTHORIZATION_PREFIX = "check"; boolean isAuthorized(APICallContext apiCallContext) throws SExecutionException; void addPermissions(String pageName, Properties pageProperties); void removePermissions(Properties pageProperties); Set getResourcePermissions(String resourceKey); } ================================================ FILE: services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/PermissionsBuilder.java ================================================ /** * Copyright (C) 2021 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.authorization; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bonitasoft.engine.authorization.properties.CompoundPermissionsMapping; import org.bonitasoft.engine.authorization.properties.CustomPermissionsMapping; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.springframework.stereotype.Component; @Component public class PermissionsBuilder { public static final String PROFILE_TYPE_AUTHORIZATION_PREFIX = "profile"; public static final String USER_TYPE_AUTHORIZATION_PREFIX = "user"; private final CustomPermissionsMapping customPermissionsMapping; private final CompoundPermissionsMapping compoundPermissionsMapping; private final ApplicationService applicationService; PermissionsBuilder(final ApplicationService applicationService, final CustomPermissionsMapping customPermissionsMapping, final CompoundPermissionsMapping compoundPermissionsMapping) { this.applicationService = applicationService; this.customPermissionsMapping = customPermissionsMapping; this.compoundPermissionsMapping = compoundPermissionsMapping; } public Set getPermissions(boolean isTechnicalUser, List profiles, String userName) throws SBonitaReadException { Set permissions; if (isTechnicalUser) { permissions = Collections.emptySet(); } else { permissions = new HashSet<>(); permissions.addAll(getProfilesPermissions(profiles)); permissions.addAll(getCustomUserPermissions(userName)); permissions.add(getUserPermission(userName)); } return permissions; } protected Set getProfilesPermissions(List profiles) throws SBonitaReadException { Set permissions = new HashSet<>(); for (final String pageToken : getAllPagesForUser(profiles)) { permissions.addAll(getCompoundPermissions(pageToken)); } for (String profile : profiles) { permissions.addAll(getCustomProfilePermissions(profile)); permissions.add(getProfilePermission(profile)); } return permissions; } private Set getAllPagesForUser(List profiles) throws SBonitaReadException { final Set pageTokens = new HashSet<>(); for (final String profile : profiles) { pageTokens.addAll(getPageTokensForApplicationsMappedToProfile(profile)); } return pageTokens; } private String getProfilePermission(final String profile) { return PROFILE_TYPE_AUTHORIZATION_PREFIX + "|" + profile; } private String getUserPermission(final String username) { return USER_TYPE_AUTHORIZATION_PREFIX + "|" + username; } private List getPageTokensForApplicationsMappedToProfile(final String profile) throws SBonitaReadException { return applicationService.getAllPagesForProfile(profile); } private Set getCustomProfilePermissions(final String profile) { return getCustomPermissions(PROFILE_TYPE_AUTHORIZATION_PREFIX, profile); } protected Set getCustomUserPermissions(String userName) { return getCustomPermissions(USER_TYPE_AUTHORIZATION_PREFIX, userName); } protected Set getCustomPermissions(final String type, final String identifier) { final Set profileSinglePermissions = new HashSet<>(); final Set customPermissionsForEntity = getCustomPermissionsRaw(type, identifier); for (final String customPermissionForEntity : customPermissionsForEntity) { final Set simplePermissions = getCompoundPermissions(customPermissionForEntity); if (!simplePermissions.isEmpty()) { profileSinglePermissions.addAll(simplePermissions); } else { profileSinglePermissions.add(customPermissionForEntity); } } return profileSinglePermissions; } private Set getCustomPermissionsRaw(final String type, final String identifier) { return customPermissionsMapping.getPropertyAsSet(type + "|" + identifier); } private Set getCompoundPermissions(final String compoundName) { return compoundPermissionsMapping.getPropertyAsSet(compoundName); } } ================================================ FILE: services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/properties/CompoundPermissionsMapping.java ================================================ /** * Copyright (C) 2021 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.authorization.properties; import org.bonitasoft.engine.cache.CacheService; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * @author Anthony Birembaut */ @Component @Order(3) public class CompoundPermissionsMapping extends ConfigurationFile { public static final String PROPERTIES_FILENAME = "compound-permissions-mapping.properties"; public CompoundPermissionsMapping(CacheService cacheService, ConfigurationFilesManager configurationFilesManager) { super(cacheService, configurationFilesManager); } @Override protected String getPropertiesFileName() { return PROPERTIES_FILENAME; } @Override protected boolean hasCustomVersion() { return true; } @Override protected boolean hasInternalVersion() { return true; } } ================================================ FILE: services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/properties/ConfigurationFile.java ================================================ /** * Copyright (C) 2021 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.authorization.properties; import static java.lang.String.format; import static org.bonitasoft.engine.authorization.properties.ConfigurationFilesManager.getCustomPropertiesFilename; import static org.bonitasoft.engine.authorization.properties.ConfigurationFilesManager.getInternalPropertiesFilename; import java.io.IOException; import java.util.Properties; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.cache.SCacheException; /** * @author Anthony Birembaut */ @Slf4j public abstract class ConfigurationFile { protected static final String CONFIGURATION_FILES_CACHE = "CONFIGURATION_FILES_CACHE"; private final String propertiesFilename; protected final String cacheKey; protected boolean setKeysToLowerCase; CacheService cacheService; ConfigurationFilesManager configurationFilesManager; public ConfigurationFile(CacheService cacheService, ConfigurationFilesManager configurationFilesManager) { this(cacheService, configurationFilesManager, false); } public ConfigurationFile(CacheService cacheService, ConfigurationFilesManager configurationFilesManager, boolean setKeysToLowerCase) { this.propertiesFilename = getPropertiesFileName(); this.cacheKey = propertiesFilename; this.cacheService = cacheService; this.configurationFilesManager = configurationFilesManager; this.setKeysToLowerCase = setKeysToLowerCase; } protected Properties readPropertiesAndStoreThemInCache() { return readPropertiesFromDatabaseAndStoreThemInCache(); } protected Properties readPropertiesFromDatabaseAndStoreThemInCache() { Properties properties = configurationFilesManager.getTenantProperties(propertiesFilename, setKeysToLowerCase); storePropertiesInCache(properties); return properties; } protected Properties readPropertiesFromClasspathAndStoreThemInCache() { //Read properties from classpath Properties classpathProperties = new Properties(); try { classpathProperties.load(ConfigurationFile.class.getResourceAsStream(getPropertiesFileName())); storePropertiesInCache(classpathProperties); } catch (IOException e) { log.error("Cannot retrieve dynamic authorizations", e); } return classpathProperties; } protected abstract String getPropertiesFileName(); abstract protected boolean hasCustomVersion(); abstract protected boolean hasInternalVersion(); void storePropertiesInCache(Properties properties) { try { cacheService.store(CONFIGURATION_FILES_CACHE, cacheKey, properties); log.debug("Successfully stored configuration file {} in dedicated cache", propertiesFilename); } catch (SCacheException e) { log.warn("Problem storing configuration file {} in dedicated cache ({})", propertiesFilename, e.getMessage()); } } Properties getProperties() { Properties properties; try { properties = (Properties) cacheService.get(CONFIGURATION_FILES_CACHE, cacheKey); } catch (SCacheException e) { log.warn("Problem retrieving configuration file {} from dedicated cache", propertiesFilename); return new Properties(); // Should we return null? } if (properties == null) { properties = readPropertiesAndStoreThemInCache(); } return properties; } public String getProperty(final String propertyName) { final String propertyValue = getProperties().getProperty(propertyName); return propertyValue != null ? propertyValue.trim() : null; } public Set getPropertyAsSet(final String propertyName) { return PropertiesWithSet.stringToSet(getProperty(propertyName)); } public void removeProperty(final String propertyName) { try { if (hasCustomVersion() || hasInternalVersion()) { throw new IllegalArgumentException( format("File %s cannot be modified directly, as a writable version exists", propertyName)); } final Properties properties = getProperties(); if (properties.remove(propertyName) != null) { // if the property was present storePropertiesInCache(properties); configurationFilesManager.removeProperty(propertiesFilename, propertyName); } } catch (IOException e) { throw new RuntimeException(e); } } public void removeCustomProperty(final String propertyName) { try { if (!hasCustomVersion()) { throw new IllegalArgumentException(format("File %s does not have a -custom version", propertyName)); } final Properties properties = getProperties(); // FIXME: is there a risk to remove a property that is not custom, here? if (properties.remove(propertyName) != null) { // if the property was present storePropertiesInCache(properties); configurationFilesManager.removeProperty(getCustomPropertiesFilename(propertiesFilename), propertyName); } } catch (IOException e) { throw new RuntimeException(e); } } public void removeInternalProperty(final String propertyName) { try { if (!hasInternalVersion()) { throw new IllegalArgumentException(format("File %s does not have a -internal version", propertyName)); } final Properties properties = getProperties(); // FIXME: is there a risk to remove a property that is not internal, here? if (properties.remove(propertyName) != null) { // if the property was present storePropertiesInCache(properties); configurationFilesManager.removeProperty(getInternalPropertiesFilename(propertiesFilename), propertyName); } } catch (IOException e) { throw new RuntimeException(e); } } public void setProperty(final String propertyName, final String propertyValue) { try { if (hasCustomVersion() || hasInternalVersion()) { throw new IllegalArgumentException( format("File %s cannot be modified directly, as a writable version exists", propertyName)); } final Properties properties = getProperties(); properties.setProperty(propertyName, propertyValue); storePropertiesInCache(properties); configurationFilesManager.setProperty(propertiesFilename, propertyName, propertyValue); } catch (IOException e) { throw new RuntimeException(e); } } public void setCustomProperty(final String propertyName, final String propertyValue) { try { if (!hasCustomVersion()) { throw new IllegalArgumentException(format("File %s does not have a -custom version", propertyName)); } final Properties properties = getProperties(); properties.setProperty(propertyName, propertyValue); storePropertiesInCache(properties); configurationFilesManager.setProperty(getCustomPropertiesFilename(propertiesFilename), propertyName, propertyValue); } catch (IOException e) { throw new RuntimeException(e); } } public void setInternalProperty(final String propertyName, final String propertyValue) { try { if (!hasInternalVersion()) { throw new IllegalArgumentException(format("File %s does not have a -internal version", propertyName)); } final Properties properties = getProperties(); properties.setProperty(propertyName, propertyValue); storePropertiesInCache(properties); configurationFilesManager.setProperty(getInternalPropertiesFilename(propertiesFilename), propertyName, propertyValue); } catch (IOException e) { throw new RuntimeException(e); } } public void setPropertyAsSet(final String property, final Set permissions) { setProperty(property, permissions.toString()); } public void setCustomPropertyAsSet(final String property, final Set permissions) { setCustomProperty(property, permissions.toString()); } public void setInternalPropertyAsSet(final String property, final Set permissions) { setInternalProperty(property, permissions.toString()); } } ================================================ FILE: services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/properties/ConfigurationFilesManager.java ================================================ /** * Copyright (C) 2021 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.authorization.properties; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.exception.UpdateException; import org.bonitasoft.engine.home.BonitaHomeServer; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta * @author Emmanuel Duchastenier * @author Anthony Birembaut */ @Slf4j @Component public class ConfigurationFilesManager { protected Properties getAlsoCustomAndInternalPropertiesFromFilename(String propertiesFileName, boolean setKeysToLowerCase) { Properties properties = new Properties(); try { final Map propertiesByFilename = getTenantConfigurations(); if (propertiesByFilename.containsKey(propertiesFileName)) { properties.putAll(propertiesKeysToLowerCaseIfNeeded(propertiesByFilename.get(propertiesFileName), setKeysToLowerCase)); // if -internal properties also exists, merge key/value pairs: final String internalSuffixedVersion = getInternalPropertiesFilename(propertiesFileName); if (propertiesByFilename.containsKey(internalSuffixedVersion)) { properties.putAll(propertiesKeysToLowerCaseIfNeeded( propertiesByFilename.get(internalSuffixedVersion), setKeysToLowerCase)); } // if -custom properties also exists, merge key/value pairs (and overwrite previous values if same key name): final String customSuffixedVersion = getCustomPropertiesFilename(propertiesFileName); if (propertiesByFilename.containsKey(customSuffixedVersion)) { properties.putAll(propertiesKeysToLowerCaseIfNeeded(propertiesByFilename.get(customSuffixedVersion), setKeysToLowerCase)); } } else { if (log.isTraceEnabled()) { log.trace("File {} not found. Returning empty properties object.", propertiesFileName); } } } catch (IOException e) { log.error("Cannot retrieve tenant configurations", e); } return properties; } protected Properties propertiesKeysToLowerCaseIfNeeded(Properties properties, boolean setKeysToLowerCase) { if (setKeysToLowerCase) { Properties reworkedProperties = new Properties(); properties.forEach((k, v) -> { reworkedProperties.put(k.toString().toLowerCase(), v); }); return reworkedProperties; } else { return properties; } } public Properties getTenantProperties(String propertiesFileName) { return getTenantProperties(propertiesFileName, false); } public Properties getTenantProperties(String propertiesFileName, boolean setKeysToLowerCase) { return getAlsoCustomAndInternalPropertiesFromFilename(propertiesFileName, setKeysToLowerCase); } /** * Parses the content as a Properties object. * If content is null, return empty properties. */ public static Properties getProperties(byte[] content) { Properties properties = new Properties(); if (content != null) { try (ByteArrayInputStream inputStream = new ByteArrayInputStream(content)) { properties.load(inputStream); } catch (IOException ioe) { log.error("Cannot parse properties file content", ioe); } } return properties; } public void removeProperty(String propertiesFilename, String propertyName) throws IOException { Map resources = getTenantConfigurations(); Properties properties = resources.get(propertiesFilename); if (properties != null) { properties.remove(propertyName); update(propertiesFilename, properties); } else { if (log.isDebugEnabled()) { log.debug("File {} not found. Cannot remove property '{}'.", propertiesFilename, propertyName); } } } public static String getInternalPropertiesFilename(String propertiesFilename) { // Internal behavior stores and removes from -internal file (for automatic updates when deploying/updating a page/API extension) return propertiesFilename.replaceAll("\\.properties$", "-internal" + ".properties"); } public static String getCustomPropertiesFilename(String propertiesFilename) { // Custom behavior stores and removes from -custom files (for manual updates): return propertiesFilename.replaceAll("\\.properties$", "-custom" + ".properties"); } protected void update(String propertiesFilename, Properties properties) throws IOException { try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { properties.store(byteArrayOutputStream, ""); getConfigurationFilesUtils().updateTenantPortalConfigurationFile(propertiesFilename, byteArrayOutputStream.toByteArray()); } catch (UpdateException e) { throw new IOException(e); } } protected BonitaHomeServer getConfigurationFilesUtils() { return BonitaHomeServer.getInstance(); } protected Properties getTenantPortalConfiguration(String propertiesFilename) { return ConfigurationFilesManager .getProperties(getConfigurationFilesUtils().getTenantPortalConfiguration(propertiesFilename)); } protected Map getTenantConfigurations() throws IOException { Map clientTenantConfigurations = getConfigurationFilesUtils() .getTenantPortalConfigurations(); return clientTenantConfigurations.entrySet().stream().collect(Collectors.toMap( Entry::getKey, v -> ConfigurationFilesManager.getProperties(v.getValue()))); } public void setProperty(String propertiesFilename, String propertyName, String propertyValue) throws IOException { Properties properties = getTenantPortalConfiguration(propertiesFilename); if (properties != null) { properties.setProperty(propertyName, propertyValue); update(propertiesFilename, properties); // store them back in database } else { if (log.isDebugEnabled()) { log.debug("File {} not found. Cannot set property '{}'.", propertiesFilename, propertyName); } } } } ================================================ FILE: services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/properties/CustomPermissionsMapping.java ================================================ /** * Copyright (C) 2021 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.authorization.properties; import org.bonitasoft.engine.cache.CacheService; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * @author Anthony Birembaut */ @Component @Order(3) public class CustomPermissionsMapping extends ConfigurationFile { /** * Default name of the preferences file */ public static final String PROPERTIES_FILENAME = "custom-permissions-mapping.properties"; @Override protected String getPropertiesFileName() { return PROPERTIES_FILENAME; } public CustomPermissionsMapping(CacheService cacheService, ConfigurationFilesManager configurationFilesManager) { super(cacheService, configurationFilesManager); } @Override protected boolean hasCustomVersion() { return false; } @Override protected boolean hasInternalVersion() { return false; } } ================================================ FILE: services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/properties/DynamicPermissionsChecks.java ================================================ /** * Copyright (C) 2021 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.authorization.properties; import java.util.Properties; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.cache.CacheService; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * @author Anthony Birembaut */ @Component @Order(3) @Slf4j @ConditionalOnSingleCandidate(DynamicPermissionsChecks.class) public class DynamicPermissionsChecks extends ResourcesPermissionsMapping { /** * Default name of the preferences file */ public static final String PROPERTIES_FILENAME = "dynamic-permissions-checks.properties"; public DynamicPermissionsChecks(CacheService cacheService, ConfigurationFilesManager configurationFilesManager) { super(cacheService, configurationFilesManager); } @Override protected String getPropertiesFileName() { return PROPERTIES_FILENAME; } @Override protected boolean hasInternalVersion() { return false; } @Override protected Properties readPropertiesAndStoreThemInCache() { return readPropertiesFromClasspathAndStoreThemInCache(); } } ================================================ FILE: services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/properties/PropertiesWithSet.java ================================================ /** * Copyright (C) 2016 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.authorization.properties; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Collections; import java.util.HashSet; import java.util.Properties; import java.util.Set; /** * @author Baptiste Mesta */ public class PropertiesWithSet extends Properties { private static final long serialVersionUID = 756391412537822704L; public PropertiesWithSet(Properties properties) { super(properties); } public PropertiesWithSet(File file) { try (FileInputStream inStream = new FileInputStream(file)) { this.load(inStream); } catch (IOException e) { throw new IllegalStateException(e); } } public Set getPropertyAsSet(final String propertyName) { return stringToSet(getProperty(propertyName)); } public static Set stringToSet(final String propertyValueAsString) { if (propertyValueAsString != null) { final Set propertiesSet = new HashSet<>(); final String propertyValueAsStringTrimmed = propertyValueAsString.trim(); if (propertyValueAsStringTrimmed.startsWith("[") && propertyValueAsStringTrimmed.endsWith("]")) { String propertyCSV = propertyValueAsStringTrimmed.substring(1, propertyValueAsStringTrimmed.length() - 1); propertyCSV = propertyCSV.trim(); if (propertyCSV.isEmpty()) { return Collections.emptySet(); } final String[] propertyArray = propertyCSV.split(","); for (final String propertyValue : propertyArray) { propertiesSet.add(propertyValue.trim()); } } else { propertiesSet.add(propertyValueAsString); } return propertiesSet; } else { return Collections.emptySet(); } } } ================================================ FILE: services/bonita-authorization/src/main/java/org/bonitasoft/engine/authorization/properties/ResourcesPermissionsMapping.java ================================================ /** * Copyright (C) 2021 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.authorization.properties; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import org.bonitasoft.engine.cache.CacheService; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * @author Anthony Birembaut * @author Baptiste Mesta * @author Fabio Lombardi */ @Component @Order(3) public class ResourcesPermissionsMapping extends ConfigurationFile { public static final String RESOURCE_IDS_SEPARATOR = "/"; public static final String API_METHOD_SEPARATOR = "|"; public static final String WILDCARD = "*"; /** * Default name of the preferences file */ public static final String PROPERTIES_FILENAME = "resources-permissions-mapping.properties"; @Override protected String getPropertiesFileName() { return PROPERTIES_FILENAME; } public ResourcesPermissionsMapping(CacheService cacheService, ConfigurationFilesManager configurationFilesManager) { super(cacheService, configurationFilesManager); } public Set getResourcePermissions(final String method, final String apiName, final String resourceName, final List resourceQualifiers) { final String key = buildResourceKey(method, apiName, resourceName, resourceQualifiers); return getPropertyAsSet(key); } public Set getResourcePermissionsWithWildCard(final String method, final String apiName, final String resourceName, final List resourceQualifiers) { if (resourceQualifiers != null && resourceQualifiers.size() > 0) { for (int i = resourceQualifiers.size() - 1; i >= 0; i--) { final List resourceQualifiersWithWildCard = getResourceQualifiersWithWildCard( resourceQualifiers, i); final String key = buildResourceKey(method, apiName, resourceName, resourceQualifiersWithWildCard); final Set permissions = getPropertyAsSet(key); if (!permissions.isEmpty()) { return permissions; } } final List reducedResourceQualifiers = new ArrayList<>(resourceQualifiers); reducedResourceQualifiers.remove(resourceQualifiers.size() - 1); return getResourcePermissionsWithWildCard(method, apiName, resourceName, reducedResourceQualifiers); } return Collections.emptySet(); } protected List getResourceQualifiersWithWildCard(final List resourceQualifiers, final int wildCardPosition) { final List resourceQualifiersWithWildCard = new ArrayList<>(resourceQualifiers); resourceQualifiersWithWildCard.set(wildCardPosition, WILDCARD); return resourceQualifiersWithWildCard; } protected String buildResourceKey(final String method, final String apiName, final String resourceName, final List resourceQualifiers) { StringBuilder key = new StringBuilder(method + API_METHOD_SEPARATOR + apiName + "/" + resourceName); if (resourceQualifiers != null) { for (final String resourceQualifier : resourceQualifiers) { key.append(RESOURCE_IDS_SEPARATOR).append(resourceQualifier); } } return key.toString(); } public Set getResourcePermissions(final String method, final String apiName, final String resourceName) { return getResourcePermissions(method, apiName, resourceName, null); } @Override protected boolean hasCustomVersion() { return true; } @Override protected boolean hasInternalVersion() { return true; } } ================================================ FILE: services/bonita-authorization/src/main/resources/org/bonitasoft/engine/authorization/properties/dynamic-permissions-checks.properties ================================================ # Dynamic checks on resources # # If a dynamic check is defined on a resource it overrides the static check behavior. # dynamic checks are defined like this # |=[, check ] # exclusions is a list of elements like this: | where type is user or profile and identifier is the username or the profile name. # Special characters like white space must be replaced with their unicode value (For example \u0020 for the white space) # # example: to protect a case to only users that can start the process and to william.jobs, walter.bates and all users having the Administrator or Process manager profile # POST|bpm/case=[user|william.jobs, user|walter.bates, profile|Administrator, profile|Process\u0020manager, check|org.bonitasoft.permissions.CasePermissionRule] # ## # rules bellow are included by default in Bonita # # CasePermissionRule # Let a user access only cases that he is involved in and start cases that he can start GET|bpm/case=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule] PUT|bpm/case=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule] POST|bpm/case=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule] DELETE|bpm/case=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule] GET|bpm/archivedCase=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule] GET|bpm/caseInfo=[profile|Administrator, check|org.bonitasoft.permissions.CasePermissionRule] GET|bpm/case/*/context=[profile|Administrator, check|org.bonitasoft.permissions.CaseContextPermissionRule] GET|bpm/archivedCase/*/context=[profile|Administrator, check|org.bonitasoft.permissions.CaseContextPermissionRule] # CaseVariablePermissionRule # Let a user get and update a variable of a case only if he is the process owner GET|bpm/caseVariable=[profile|Administrator, check|org.bonitasoft.permissions.CaseVariablePermissionRule] PUT|bpm/caseVariable=[profile|Administrator, check|org.bonitasoft.permissions.CaseVariablePermissionRule] # CommentPermissionRule # Let a user access only comments on cases that he is involved in GET|bpm/comment=[profile|Administrator, check|org.bonitasoft.permissions.CommentPermissionRule] POST|bpm/comment=[profile|Administrator, check|org.bonitasoft.permissions.CommentPermissionRule] GET|bpm/archivedComment=[profile|Administrator, check|org.bonitasoft.permissions.CommentPermissionRule] # DocumentPermissionRule # Let a user access only document on cases that he is involved in GET|bpm/document=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule] POST|bpm/document=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule] PUT|bpm/document=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule] DELETE|bpm/document=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule] GET|bpm/archiveddocument=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule] GET|bpm/archivedCaseDocument=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule] GET|bpm/caseDocument=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule] POST|bpm/caseDocument=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule] PUT|bpm/caseDocument=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule] DELETE|bpm/caseDocument=[profile|Administrator, check|org.bonitasoft.permissions.DocumentPermissionRule] # ProcessPermissionRule # Let the user do get only on processes he deployed or that he supervised GET|bpm/process=[profile|Administrator, check|org.bonitasoft.permissions.ProcessPermissionRule] POST|bpm/process=[profile|Administrator, check|org.bonitasoft.permissions.ProcessPermissionRule] PUT|bpm/process=[profile|Administrator, check|org.bonitasoft.permissions.ProcessPermissionRule] DELETE|bpm/process=[profile|Administrator, check|org.bonitasoft.permissions.ProcessPermissionRule] GET|bpm/process/*/contract=[profile|Administrator, check|org.bonitasoft.permissions.ProcessPermissionRule] GET|bpm/processInfo=[profile|Administrator, check|org.bonitasoft.permissions.ProcessPermissionRule] GET|bpm/diagram=[profile|Administrator, check|org.bonitasoft.permissions.ProcessPermissionRule] POST|bpm/process/*/instantiation=[profile|Administrator, check|org.bonitasoft.permissions.ProcessInstantiationPermissionRule] # ProcessResolutionProblemPermissionRule # Let a user see process resolution problem only if he is process owner GET|bpm/processResolutionProblem=[profile|Administrator, check|org.bonitasoft.permissions.ProcessResolutionProblemPermissionRule] # ProcessConfigurationPermissionRule # Let a user manage process connectors and parameters only if he is process owner GET|bpm/processParameter=[profile|Administrator, check|org.bonitasoft.permissions.ProcessConfigurationPermissionRule] GET|bpm/processConnector=[profile|Administrator, check|org.bonitasoft.permissions.ProcessConfigurationPermissionRule] PUT|bpm/processConnector=[profile|Administrator, check|org.bonitasoft.permissions.ProcessConfigurationPermissionRule] # ProcessConnectorDependencyPermissionRule # Let a user see process connector dependency problem only if he is process owner GET|bpm/processConnectorDependency=[profile|Administrator, check|org.bonitasoft.permissions.ProcessConnectorDependencyPermissionRule] # ActorPermissionRule # Let a user manage actors only if he is process owner GET|bpm/actor=[profile|Administrator, check|org.bonitasoft.permissions.ActorPermissionRule] PUT|bpm/actor=[profile|Administrator, check|org.bonitasoft.permissions.ActorPermissionRule] # ActorMemberPermissionRule # Let a user add an actorMember only if he is process owner GET|bpm/actorMember=[profile|Administrator, check|org.bonitasoft.permissions.ActorMemberPermissionRule] POST|bpm/actorMember=[profile|Administrator, check|org.bonitasoft.permissions.ActorMemberPermissionRule] DELETE|bpm/actorMember=[profile|Administrator, profile|Process\u0020manager, check|org.bonitasoft.permissions.ActorMemberPermissionRule] # ProcessSupervisorPermissionRule # Let a user view and add process only if he is process owner GET|bpm/processSupervisor=[profile|Administrator, check|org.bonitasoft.permissions.ProcessSupervisorPermissionRule] POST|bpm/processSupervisor=[profile|Administrator, check|org.bonitasoft.permissions.ProcessSupervisorPermissionRule] DELETE|bpm/processSupervisor=[profile|Administrator, check|org.bonitasoft.permissions.ProcessSupervisorPermissionRule] # TaskPermissionRule # Let a user access only tasks that are assigned or pending to him GET|bpm/flowNode=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] PUT|bpm/flowNode=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] GET|bpm/activity=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] PUT|bpm/activity=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] PUT|bpm/activityReplay=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] GET|bpm/task=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] PUT|bpm/task=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] GET|bpm/humanTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] PUT|bpm/humanTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] GET|bpm/userTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] PUT|bpm/userTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] GET|bpm/hiddenUserTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] POST|bpm/hiddenUserTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] DELETE|bpm/hiddenUserTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] GET|bpm/manualTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] POST|bpm/manualTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] PUT|bpm/manualTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] GET|bpm/archivedFlowNode=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] GET|bpm/archivedActivity=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] GET|bpm/archivedTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] GET|bpm/archivedHumanTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] GET|bpm/archivedUserTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] GET|bpm/archivedManualTask=[profile|Administrator, check|org.bonitasoft.permissions.TaskPermissionRule] GET|bpm/archivedUserTask/*/context=[profile|Administrator, check|org.bonitasoft.permissions.TaskExecutionPermissionRule] GET|bpm/userTask/*/context=[profile|Administrator, check|org.bonitasoft.permissions.TaskExecutionPermissionRule] GET|bpm/userTask/*/contract=[profile|Administrator, check|org.bonitasoft.permissions.TaskExecutionPermissionRule] POST|bpm/userTask/*/execution=[profile|Administrator, check|org.bonitasoft.permissions.TaskExecutionPermissionRule] # ConnectorInstancePermissionRule # Let a user see process configuration only if he is process owner GET|bpm/connectorInstance=[profile|Administrator, check|org.bonitasoft.permissions.ConnectorInstancePermissionRule] PUT|bpm/connectorInstance=[profile|Administrator, profile|Process\u0020manager, check|org.bonitasoft.permissions.ConnectorInstancePermissionRule] GET|bpm/archivedConnectorInstance=[profile|Administrator, check|org.bonitasoft.permissions.ConnectorInstancePermissionRule] GET|bpm/connectorFailure=[profile|Administrator, profile|Process\u0020manager] # BPM failures GET|bpm/failure=[profile|Administrator] GET|bpm/archivedFailure=[profile|Administrator] # UserPermissionRule # Let the user access and modify only himself GET|identity/user=[profile|Administrator, profile|Process\u0020manager, check|org.bonitasoft.permissions.UserPermissionRule] POST|identity/user=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule] PUT|identity/user=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule] GET|identity/personalcontactdata=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule] POST|identity/personalcontactdata=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule] PUT|identity/personalcontactdata=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule] GET|identity/professionalcontactdata=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule] POST|identity/professionalcontactdata=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule] PUT|identity/professionalcontactdata=[profile|Administrator, check|org.bonitasoft.permissions.UserPermissionRule] #ProfilePermissionRule # Secure profile related resources GET|portal/profile=[profile|Administrator, check|org.bonitasoft.permissions.ProfilePermissionRule] POST|portal/profile=[profile|Administrator, check|org.bonitasoft.permissions.ProfilePermissionRule] PUT|portal/profile=[profile|Administrator, check|org.bonitasoft.permissions.ProfilePermissionRule] DELETE|portal/profile=[profile|Administrator, check|org.bonitasoft.permissions.ProfilePermissionRule] #ApplicationPermissionRule # Secure application resource GET|living/application=[profile|Administrator, check|org.bonitasoft.permissions.ApplicationPermissionRule] #ApplicationMenuPermissionRule # Secure application menu resource GET|living/application-menu=[profile|Administrator, check|org.bonitasoft.permissions.ApplicationMenuPermissionRule] # Platform information GET|system/information=[profile|Administrator] #Servlets GET|portal/documentDownload=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule] GET|API/documentDownload=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule] GET|portal/custom-page/API/documentDownload=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule] GET|portal/formsDocumentDownload=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule] GET|portal/downloadDocument=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule] GET|portal/formsDocumentImage=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule] GET|API/formsDocumentImage=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule] GET|portal/custom-page/API/formsDocumentImage=[profile|Administrator, check|org.bonitasoft.permissions.DownloadDocumentPermissionRule] ================================================ FILE: services/bonita-authorization/src/test/java/org/bonitasoft/engine/authorization/PermissionsBuilderTest.java ================================================ /** * Copyright (C) 2021 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.authorization; import static java.util.Arrays.asList; import static java.util.Collections.emptySet; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bonitasoft.engine.authorization.properties.CompoundPermissionsMapping; import org.bonitasoft.engine.authorization.properties.CustomPermissionsMapping; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.session.model.SSession; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class PermissionsBuilderTest { @Mock private CustomPermissionsMapping customPermissionsMapping; @Mock private CompoundPermissionsMapping compoundPermissionsMapping; @Mock private ApplicationService applicationService; private PermissionsBuilder permissionsBuilder; @Before public void setUp() { init(); } private void init() { permissionsBuilder = spy(new PermissionsBuilder(applicationService, customPermissionsMapping, compoundPermissionsMapping)); } @Test public void should_getPermissions_work_without_profile_nor_custom_user_permissions() throws Exception { doReturn(emptySet()).when(permissionsBuilder).getProfilesPermissions(anyList()); doReturn(emptySet()).when(permissionsBuilder).getCustomUserPermissions(any()); final SSession session = mock(SSession.class); doReturn("Jean-Claude").when(session).getUserName(); final Set permissions = permissionsBuilder.getPermissions(session.isTechnicalUser(), session.getProfiles(), session.getUserName()); assertThat(permissions).as("No permissions other than the user should have been returned") .containsOnly("user|Jean-Claude"); } @Test public void should_getPermissions_add_profile_and_custom_permissions() throws Exception { doReturn(emptySet()).when(permissionsBuilder).getProfilesPermissions(anyList()); doReturn(emptySet()).when(permissionsBuilder).getCustomUserPermissions(anyString()); final SSession session = mock(SSession.class); doReturn("Loïc").when(session).getUserName(); permissionsBuilder.getPermissions(session.isTechnicalUser(), session.getProfiles(), session.getUserName()); verify(permissionsBuilder).getProfilesPermissions(anyList()); verify(permissionsBuilder).getCustomUserPermissions(anyString()); } @Test public void should_getProfilesPermissions_return_all_types_of_permissions() throws Exception { // given doReturn(List.of("custompage_page1", "custompage_page2")).when(applicationService) .getAllPagesForProfile("profile1"); doReturn(List.of("custompage_page1", "custompage_page3")).when(applicationService) .getAllPagesForProfile("profile2"); doReturn(aSet("Perm1", "Perm2")).when(compoundPermissionsMapping).getPropertyAsSet(eq("custompage_page1")); doReturn(aSet("Perm1", "Perm21")).when(compoundPermissionsMapping).getPropertyAsSet(eq("custompage_page2")); doReturn(aSet("Perm31", "Perm32")).when(compoundPermissionsMapping).getPropertyAsSet(eq("custompage_page3")); doReturn(Set.of("customprofile_permission11")).when(customPermissionsMapping) .getPropertyAsSet("profile|profile1"); doReturn(Set.of("customprofile_permission31")).when(customPermissionsMapping) .getPropertyAsSet("user|Jean-Claude"); doReturn(Set.of("customprofile_permission21", "customprofile_permission22")).when(customPermissionsMapping) .getPropertyAsSet("profile|profile2"); doReturn(aSet("Perm41", "Perm42")).when(compoundPermissionsMapping) .getPropertyAsSet(eq("customprofile_permission22")); final SSession session = mock(SSession.class); final List myProfiles = List.of("profile1", "profile2"); doReturn(myProfiles).when(session).getProfiles(); doReturn("Jean-Claude").when(session).getUserName(); // when final Set permissions = permissionsBuilder.getPermissions(session.isTechnicalUser(), session.getProfiles(), session.getUserName()); // then assertThat(permissions).containsExactlyInAnyOrder("Perm1", "Perm2", "Perm21", "Perm31", "Perm32", "customprofile_permission11", "customprofile_permission21", "Perm41", "Perm42", "profile|profile1", "profile|profile2", "customprofile_permission31", "user|Jean-Claude"); } @Test public void should_getCustomPermissions_work_with_compound_permissions() { doReturn(aSet("Perm1", "Perm2", "taskListing")).when(customPermissionsMapping).getPropertyAsSet("user|myUser"); doReturn(aSet("Perm3", "Perm4")).when(compoundPermissionsMapping).getPropertyAsSet("taskListing"); final Set permissions = permissionsBuilder.getCustomPermissions("user", "myUser"); assertThat(permissions).containsOnly("Perm1", "Perm2", "Perm3", "Perm4"); } @Test public void getPermissions_should_return_empty_list_for_technical_user() throws Exception { // given: final SSession session = mock(SSession.class); doReturn(true).when(session).isTechnicalUser(); // when: final Set permissions = permissionsBuilder.getPermissions(session.isTechnicalUser(), session.getProfiles(), session.getUserName()); // then: assertThat(permissions).isEmpty(); } private Set aSet(String... elements) { return new HashSet<>(asList(elements)); } } ================================================ FILE: services/bonita-authorization/src/test/java/org/bonitasoft/engine/authorization/properties/ConfigurationFileTest.java ================================================ /** * Copyright (C) 2021 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.authorization.properties; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.authorization.properties.ConfigurationFile.CONFIGURATION_FILES_CACHE; import static org.bonitasoft.engine.authorization.properties.ConfigurationFilesManager.getProperties; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; import java.util.Properties; import java.util.Set; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.commons.io.IOUtil; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ConfigurationFileTest { public static final long TENANT_ID = 12L; private static Properties compoundProperties; private static Properties resourcesProperties; private static Properties customProperties; private ConfigurationFile resourcesPermissionsMapping; private ConfigurationFile customPermissionsMapping; private ConfigurationFile compoundPermissionsMapping; @Mock private CacheService cacheService; @Mock private ConfigurationFilesManager configurationFilesManager; @BeforeClass public static void init() throws Exception { compoundProperties = getProperties(IOUtil.getAllContentFrom( ConfigurationFileTest.class.getResourceAsStream("/compound-permissions-mapping.properties"))); resourcesProperties = getProperties(IOUtil.getAllContentFrom( ConfigurationFileTest.class.getResourceAsStream("/resources-permissions-mapping.properties"))); customProperties = getProperties(IOUtil.getAllContentFrom( ConfigurationFileTest.class.getResourceAsStream("/custom-permissions-mapping.properties"))); } @Before public void setupMocksAndSpies() { resourcesPermissionsMapping = spy( new ResourcesPermissionsMapping(cacheService, configurationFilesManager)); doReturn(resourcesProperties).when(resourcesPermissionsMapping).getProperties(); customPermissionsMapping = spy( new CustomPermissionsMapping(cacheService, configurationFilesManager)); doReturn(customProperties).when(customPermissionsMapping).getProperties(); compoundPermissionsMapping = spy( new CompoundPermissionsMapping(cacheService, configurationFilesManager)); doReturn(compoundProperties).when(compoundPermissionsMapping).getProperties(); } @Test public void should_getProperty_return_the_right_custom_permissions_with_special_characters() { final String customValue = customPermissionsMapping.getProperty("profile|HR manager"); assertEquals("[ManageProfiles]", customValue); } @Test public void should_getPropertyAsSet_return_the_right_permissions_with_trailing_spaces() { final String value = compoundPermissionsMapping.getProperty("caseListingPage"); final Set valueAsList = compoundPermissionsMapping.getPropertyAsSet("caseListingPage"); assertEquals("caseVisualizationWithTrailingSpace", value); assertThat(valueAsList).containsOnly("caseVisualizationWithTrailingSpace"); } @Test public void should_getProperty_return_null_with_unknown_permissions() { final String value = compoundPermissionsMapping.getProperty("unknownListingPage"); assertThat(value).isNull(); } @Test public void should_getProperty_return_the_right_compound_permissions() { final String compoundValue = compoundPermissionsMapping.getProperty("taskListingPage"); assertEquals("[TaskVisualization, CaseVisualization]", compoundValue); } @Test public void should_getProperty_return_the_right_resource_permissions() { final String resourcesValue = resourcesPermissionsMapping.getProperty("GET|bpm/identity"); assertEquals("[UserVisualization, groupVisualization]", resourcesValue); } @Test public void should_getProperty_return_the_right_custom_permissions() { final String customValue = customPermissionsMapping.getProperty("profile|User"); assertEquals("[ManageLooknFeel, ManageProfiles]", customValue); } @Test public void should_getPropertyAsSet_return_the_right_permissions_list() { final Set compoundPermissionsList = compoundPermissionsMapping.getPropertyAsSet("taskListingPage"); assertThat(compoundPermissionsList).containsOnly("TaskVisualization", "CaseVisualization"); } @Test public void should_getPropertyAsSet_return_the_right_permissions_list_with_single_value() { final Set compoundPermissionsList = compoundPermissionsMapping.getPropertyAsSet("processListingPage"); assertThat(compoundPermissionsList).containsOnly("processVisualization"); } @Test public void getTenantProperties_should_get_from_cache_and_store_to_cache_if_not_already_in() throws Exception { // given: final ResourcesPermissionsMapping configFile = spy( new ResourcesPermissionsMapping(cacheService, configurationFilesManager)); final Properties props = new Properties(); doReturn(props).when(configurationFilesManager).getTenantProperties("resources-permissions-mapping.properties", false); // when: configFile.getProperties(); // then: verify(cacheService).get(CONFIGURATION_FILES_CACHE, "resources-permissions-mapping.properties"); verify(cacheService).store(CONFIGURATION_FILES_CACHE, "resources-permissions-mapping.properties", props); } } ================================================ FILE: services/bonita-authorization/src/test/java/org/bonitasoft/engine/authorization/properties/ConfigurationFilesManagerTest.java ================================================ /** * Copyright (C) 2021 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.authorization.properties; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.authorization.properties.ConfigurationFilesManager.getProperties; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.bonitasoft.engine.home.BonitaHomeServer; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class ConfigurationFilesManagerTest { private static final long TENANT_ID = 543892L; private static final String MY_PROP_INTERNAL_PROPERTIES = "myProp-internal.properties"; @Mock private BonitaHomeServer bonitaHomeServer; @Spy private ConfigurationFilesManager configurationFilesManager; @Captor private ArgumentCaptor contentCaptor; @Before public void before() { doReturn(bonitaHomeServer).when(configurationFilesManager).getConfigurationFilesUtils(); } @Test public void should_removeProperty_call_update_with_new_content() throws Exception { //given final HashMap configurationFiles = new HashMap<>(); configurationFiles.put("configFile1.properties", getProperties("myProp1=authKey\nmyProp2=passHash".getBytes())); configurationFiles.put(MY_PROP_INTERNAL_PROPERTIES, getProperties("testProperty=testValue\npropToRemove=willBeRemoved".getBytes())); doReturn(configurationFiles).when(configurationFilesManager).getTenantConfigurations(); //when configurationFilesManager.removeProperty(MY_PROP_INTERNAL_PROPERTIES, "propToRemove"); //then verify(bonitaHomeServer).updateTenantPortalConfigurationFile(eq(MY_PROP_INTERNAL_PROPERTIES), contentCaptor.capture()); assertThat(new String(contentCaptor.getValue())).doesNotContain("propToRemove").contains("testProperty", "testValue"); } @Test public void should_setProperty_call_update_with_new_content() throws Exception { //given doReturn(getProperties("testProperty=testValue\npropToRemove=willBeRemoved".getBytes())) .when(configurationFilesManager).getTenantPortalConfiguration(MY_PROP_INTERNAL_PROPERTIES); //when configurationFilesManager.setProperty(MY_PROP_INTERNAL_PROPERTIES, "testProperty", "new Value"); //then verify(bonitaHomeServer).updateTenantPortalConfigurationFile(eq(MY_PROP_INTERNAL_PROPERTIES), contentCaptor.capture()); assertThat(new String(contentCaptor.getValue())).doesNotContain("testValue").contains("testProperty", "new Value"); } @Test public void getTenantProperties_should_merge_custom_properties_if_exist() throws IOException { //given final Properties defaultProps = new Properties(); defaultProps.put("defaultKey", "defaultValue"); final Properties customProps = new Properties(); customProps.put("customKey", "customValue"); Map propertiesMap = new HashMap<>(); propertiesMap.put("toto.properties", defaultProps); propertiesMap.put("toto-custom.properties", customProps); doReturn(propertiesMap).when(configurationFilesManager).getTenantConfigurations(); //when final Properties properties = configurationFilesManager.getTenantProperties("toto.properties"); //then assertThat(properties).containsEntry("defaultKey", "defaultValue").containsEntry("customKey", "customValue"); } @Test public void getTenantProperties_should_merge_internal_properties_if_exist() throws IOException { //given final Properties defaultProps = new Properties(); defaultProps.put("defaultKey", "defaultValue"); final Properties internalProps = new Properties(); internalProps.put("internalKey", "internalValue"); Map propertiesMap = new HashMap<>(); propertiesMap.put("toto.properties", defaultProps); propertiesMap.put("toto-internal.properties", internalProps); doReturn(propertiesMap).when(configurationFilesManager).getTenantConfigurations(); //when final Properties properties = configurationFilesManager.getTenantProperties("toto.properties"); //then assertThat(properties).containsEntry("defaultKey", "defaultValue").containsEntry("internalKey", "internalValue"); } @Test public void getTenantProperties_should_not_fail_if_base_file_does_not_exist() throws IOException { //given final Properties defaultProps = new Properties(); defaultProps.put("defaultKey", "defaultValue"); Map propertiesMap = new HashMap<>(); propertiesMap.put("toto.properties", defaultProps); doReturn(propertiesMap).when(configurationFilesManager).getTenantConfigurations(); //when final Properties properties = configurationFilesManager.getTenantProperties("non-existing.properties"); //then assertThat(properties).isEmpty(); } @Test public void custom_properties_should_overwrite_internal_properties() throws IOException { //given final Properties defaultProps = new Properties(); defaultProps.put("defaultKey", "defaultValue"); final Properties internalProps = new Properties(); internalProps.put("otherKey", "someInternallyManagedValue"); final Properties customProps = new Properties(); final String expectedOverwrittenValue = "custom_changed_value"; customProps.put("otherKey", expectedOverwrittenValue); Map propertiesMap = new HashMap<>(); propertiesMap.put("overwrite.properties", defaultProps); propertiesMap.put("overwrite-internal.properties", internalProps); propertiesMap.put("overwrite-custom.properties", customProps); doReturn(propertiesMap).when(configurationFilesManager).getTenantConfigurations(); //when final Properties properties = configurationFilesManager.getTenantProperties("overwrite.properties"); //then assertThat(properties).containsEntry("defaultKey", "defaultValue").containsEntry("otherKey", expectedOverwrittenValue); } } ================================================ FILE: services/bonita-authorization/src/test/java/org/bonitasoft/engine/authorization/properties/ResourcesPermissionsMappingTest.java ================================================ /** * Copyright (C) 2021 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.authorization.properties; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.authorization.properties.ConfigurationFilesManager.getProperties; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import java.util.List; import java.util.Set; import org.bonitasoft.engine.cache.CacheService; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ResourcesPermissionsMappingTest { @Mock private CacheService cacheService; @Mock private ConfigurationFilesManager configurationFilesManager; @Test public void testGetResourcePermission() { //given final String fileContent = "GET|bpm/process [Process visualization, Process categories, Process actor mapping visualization, Connector visualization]\n" + "POST|bpm/process [Process Deploy]\n" + "POST|bpm/process/6 [Custom permission]\n" + "PUT|bpm/process []"; final ResourcesPermissionsMapping resourcesPermissionsMapping = getResourcesPermissionsMapping(fileContent); //when final Set getPermissions = resourcesPermissionsMapping.getResourcePermissions("GET", "bpm", "process"); final Set postPermission = resourcesPermissionsMapping.getResourcePermissions("POST", "bpm", "process"); final Set postOnSinglePermission = resourcesPermissionsMapping.getResourcePermissions("POST", "bpm", "process", singletonList("6")); final Set putPermissions = resourcesPermissionsMapping.getResourcePermissions("PUT", "bpm", "process"); final Set unknown = resourcesPermissionsMapping.getResourcePermissions("unknown", "unknown", "unknown", singletonList("unknown")); //then assertThat(getPermissions).containsOnly("Process visualization", "Process categories", "Process actor mapping visualization", "Connector visualization"); assertThat(postPermission).containsOnly("Process Deploy"); assertThat(postOnSinglePermission).containsOnly("Custom permission"); assertThat(putPermissions).isEmpty(); assertThat(unknown).isEmpty(); } @Test public void testGetResourcePermissionWithWildCard() { //given final String fileContent = "POST|bpm/process/* [Process Deploy]\n" + "POST|bpm/process/*/instantiation [Custom permission]\n" + "PUT|bpm/process/*/expression [Expression update]"; final ResourcesPermissionsMapping resourcesPermissionsMapping = getResourcesPermissionsMapping(fileContent); //when final Set getWithResourcesQualifier = resourcesPermissionsMapping.getResourcePermissionsWithWildCard( "GET", "bpm", "process", List.of("6")); final Set postWithResourcesQualifier = resourcesPermissionsMapping.getResourcePermissionsWithWildCard( "POST", "bpm", "process", List.of("6")); final Set postWithResourcesQualifiers = resourcesPermissionsMapping.getResourcePermissionsWithWildCard( "POST", "bpm", "process", List.of("6", "instantiation")); final Set putWithResourcesQualifiers = resourcesPermissionsMapping.getResourcePermissionsWithWildCard( "PUT", "bpm", "process", List.of("6", "expression", "10")); //then assertThat(getWithResourcesQualifier).isEmpty(); assertThat(postWithResourcesQualifier).containsOnly("Process Deploy"); assertThat(postWithResourcesQualifiers).containsOnly("Custom permission"); assertThat(putWithResourcesQualifiers).containsOnly("Expression update"); } public ResourcesPermissionsMapping getResourcesPermissionsMapping(final String fileContent) { final ResourcesPermissionsMapping resourcesPermissionsMapping = spy( new ResourcesPermissionsMapping(cacheService, configurationFilesManager)); doReturn(getProperties(fileContent.getBytes())).when(resourcesPermissionsMapping).getProperties(); return resourcesPermissionsMapping; } } ================================================ FILE: services/bonita-authorization/src/test/resources/compound-permissions-mapping.properties ================================================ processListingPage processVisualization caseListingPage caseVisualizationWithTrailingSpace taskListingPage [TaskVisualization, CaseVisualization] ================================================ FILE: services/bonita-authorization/src/test/resources/custom-permissions-mapping.properties ================================================ profile|User=[ManageLooknFeel, ManageProfiles] profile|HR\u0020manager=[ManageProfiles] ================================================ FILE: services/bonita-authorization/src/test/resources/resources-permissions-mapping.properties ================================================ GET|bpm/identity [UserVisualization, groupVisualization] ================================================ FILE: services/bonita-builder/build.gradle ================================================ description = 'Bonita Builder: Service Implementation' ================================================ FILE: services/bonita-builder/src/main/java/org/bonitasoft/engine/builder/BuilderFactory.java ================================================ /** * 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.builder; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.Properties; public class BuilderFactory { private static final String BUILDER_FACTORIES_DEFAULT_FILE = "builder-factories.properties"; private static final String BUILDER_FACTORIES_EXTENSION_FILE = "builder-factories-ext.properties"; private final Map factoryCache; private final Properties properties; private static final Object MUTEX = new BuilderFactoryMutex(); private static BuilderFactory INSTANCE = null; private BuilderFactory(final Properties properties) { this.properties = properties; factoryCache = new HashMap(); } private static final class BuilderFactoryMutex { } public static BuilderFactory getInstance() { if (INSTANCE == null) { synchronized (MUTEX) { // ensure we do not create many instances of this class if (INSTANCE == null) { URL defaultFileURL = null; try { defaultFileURL = BuilderFactory.class.getResource(BUILDER_FACTORIES_DEFAULT_FILE); final Properties defaultProperties = getProperties(defaultFileURL); final Properties allProperties = new Properties(defaultProperties); final URL extensionFileURL = BuilderFactory.class.getResource(BUILDER_FACTORIES_EXTENSION_FILE); if (extensionFileURL != null) { final Properties extensionProperties = getProperties(extensionFileURL); allProperties.putAll(extensionProperties); } INSTANCE = new BuilderFactory(allProperties); } catch (final Exception e) { throw new RuntimeException("Unable to load builder factories from : fileURL=" + defaultFileURL); } } } } return INSTANCE; } private synchronized void cacheFactory(final String interfaceName, final String className) { try { if (className == null || "null".equals(className)) { throw new Exception("Factory implementation of " + interfaceName + " is required."); } final Class clazz = Class.forName(className, true, Thread.currentThread().getContextClassLoader()); final Object factory = clazz.newInstance(); factoryCache.put(interfaceName, factory); } catch (final Exception e) { throw new RuntimeException(e); } } public static T get(final Class clazz) { final T factoryImplementation = getInstance().getInternalBuilderFactory(clazz); if (factoryImplementation == null) { throw new RuntimeException("No factory found for interface: " + clazz); } return factoryImplementation; } @SuppressWarnings("unchecked") private T getInternalBuilderFactory(final Class clazz) { if (!factoryCache.containsKey(clazz.getName())) { cacheFactory(clazz.getName(), properties.getProperty(clazz.getName())); } return (T) factoryCache.get(clazz.getName()); } public static Properties getProperties(final URL url) throws IOException { final InputStreamReader reader = new InputStreamReader(url.openStream()); return getProperties(reader); } private static Properties getProperties(final Reader reader) throws IOException { final Properties properties = new Properties(); try { properties.load(reader); return properties; } finally { reader.close(); } } } ================================================ FILE: services/bonita-builder/src/main/resources/org/bonitasoft/engine/builder/builder-factories.properties ================================================ org.bonitasoft.engine.actor.mapping.model.SActorLogBuilderFactory = org.bonitasoft.engine.actor.mapping.impl.SActorLogBuilderFactoryImpl org.bonitasoft.engine.actor.mapping.model.SActorUpdateBuilderFactory = org.bonitasoft.engine.actor.mapping.model.impl.SActorUpdateBuilderFactoryImpl org.bonitasoft.engine.command.model.SCommandLogBuilderFactory = org.bonitasoft.engine.command.model.SCommandLogBuilderFactoryImpl org.bonitasoft.engine.command.model.SCommandUpdateBuilderFactory = org.bonitasoft.engine.command.model.SCommandUpdateBuilderFactoryImpl org.bonitasoft.engine.core.category.model.builder.SCategoryLogBuilderFactory = org.bonitasoft.engine.core.category.model.builder.impl.SCategoryLogBuilderFactoryImpl org.bonitasoft.engine.core.category.model.builder.SCategoryUpdateBuilderFactory = org.bonitasoft.engine.core.category.model.builder.impl.SCategoryUpdateBuilderFactoryImpl org.bonitasoft.engine.core.category.model.builder.SProcessCategoryMappingBuilderFactory = org.bonitasoft.engine.core.category.model.builder.impl.SProcessCategoryMappingBuilderFactoryImpl org.bonitasoft.engine.core.operation.model.builder.SLeftOperandBuilderFactory = org.bonitasoft.engine.core.operation.model.builder.impl.SLeftOperandBuilderFactoryImpl org.bonitasoft.engine.core.operation.model.builder.SOperationBuilderFactory = org.bonitasoft.engine.core.operation.model.builder.impl.SOperationBuilderFactoryImpl org.bonitasoft.engine.core.process.comment.model.builder.SCommentLogBuilderFactory = org.bonitasoft.engine.core.process.comment.model.builder.impl.SCommentLogBuilderFactoryImpl org.bonitasoft.engine.core.process.definition.model.builder.SActorLogBuilderFactory = org.bonitasoft.engine.actor.mapping.impl.SActorLogBuilderFactoryImpl org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.impl.SProcessDefinitionBuilderFactoryImpl org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionDeployInfoUpdateBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.impl.SProcessDefinitionDeployInfoUpdateBuilderFactoryImpl org.bonitasoft.engine.core.process.definition.model.builder.SProcessDefinitionLogBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.impl.SProcessDefinitionLogBuilderFactoryImpl org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SEndEventDefinitionBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl.SEndEventDefinitionBuilderFactoryImpl org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowErrorEventTriggerDefinitionBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl.SThrowErrorEventTriggerDefinitionBuilderFactoryImpl org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowMessageEventTriggerDefinitionBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl.SThrowMessageEventTriggerDefinitionBuilderFactoryImpl org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.SThrowSignalEventTriggerDefinitionBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.event.trigger.impl.SThrowSignalEventTriggerDefinitionBuilderFactoryImpl org.bonitasoft.engine.core.document.model.builder.SDocumentMappingBuilderFactory = org.bonitasoft.engine.core.document.model.builder.impl.SDocumentMappingBuilderFactoryImpl org.bonitasoft.engine.core.document.model.builder.SDocumentMappingLogBuilderFactory = org.bonitasoft.engine.core.document.model.builder.impl.SDocumentMappingLogBuilderFactoryImpl org.bonitasoft.engine.core.document.model.builder.SDocumentMappingUpdateBuilderFactory = org.bonitasoft.engine.core.document.model.builder.impl.SDocumentMappingUpdateBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.SAAutomaticTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAAutomaticTaskInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.SACallActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SACallActivityInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.SAConnectorInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAConnectorInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.SAGatewayInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAGatewayInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.SALoopActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SALoopActivityInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.SAManualTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAManualTaskInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.SAMultiInstanceActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAMultiInstanceActivityInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.SANamedElementBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SANamedElementBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAProcessInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.SAReceiveTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAReceiveTaskInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.SASendTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SASendTaskInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.SASubProcessActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SASubProcessActivityInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAUserTaskInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAEndEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.event.impl.SAEndEventInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.event.SAStartEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.event.impl.SAStartEventInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.SAutomaticTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SAutomaticTaskInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SCallActivityInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.SConnectorInstanceLogBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SConnectorInstanceLogBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.SGatewayInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SGatewayInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.SLoopActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SLoopActivityInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.SManualTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SManualTaskInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.SMultiInstanceActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SMultiInstanceActivityInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.SReceiveTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SReceiveTaskInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.SSendTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SSendTaskInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.SSubProcessActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SSubProcessActivityInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.SUserTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SUserTaskInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.impl.SBoundaryEventInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.event.SEndEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.impl.SEndEventInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.impl.SIntermediateCatchEventInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.impl.SIntermediateThrowEventInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.event.SStartEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.impl.SStartEventInstanceBuilderFactoryImpl #org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SMessageInstanceLogBuilderFactory = null org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.handling.impl.SWaitingErrorEventBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingEventKeyProviderBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.handling.impl.SWaitingEventKeyProviderBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingMessageEventBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.handling.impl.SWaitingMessageEventBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingSignalEventBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.handling.impl.SWaitingSignalEventBuilderFactoryImpl org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory = org.bonitasoft.engine.data.definition.model.builder.impl.SDataDefinitionBuilderFactoryImpl org.bonitasoft.engine.core.process.definition.model.builder.SBusinessDataDefinitionBuilderFactory = org.bonitasoft.engine.core.process.definition.model.builder.impl.SBusinessDataDefinitionBuilderFactoryImpl #org.bonitasoft.engine.data.definition.model.builder.SEnumationDataDefinitionBuilderFactory = null #org.bonitasoft.engine.data.definition.model.builder.STextDataDefinitionBuilderFactory = null org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilderFactory = org.bonitasoft.engine.data.definition.model.builder.impl.SXMLDataDefinitionBuilderFactoryImpl org.bonitasoft.engine.data.instance.model.archive.builder.SADataInstanceLogBuilderFactory = org.bonitasoft.engine.data.instance.model.archive.builder.impl.SADataInstanceLogBuilderFactoryImpl org.bonitasoft.engine.dependency.model.builder.SDependencyLogBuilderFactory = org.bonitasoft.engine.dependency.model.builder.impl.SDependencyLogBuilderFactoryImpl org.bonitasoft.engine.dependency.model.builder.SDependencyMappingLogBuilderFactory = org.bonitasoft.engine.dependency.model.builder.impl.SDependencyMappingLogBuilderFactoryImpl org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory = org.bonitasoft.engine.expression.model.builder.impl.SExpressionBuilderFactoryImpl org.bonitasoft.engine.identity.model.builder.SContactInfoLogBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SContactInfoLogBuilderFactoryImpl org.bonitasoft.engine.identity.model.builder.SContactInfoUpdateBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SContactInfoUpdateBuilderFactoryImpl org.bonitasoft.engine.identity.model.builder.SGroupLogBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SGroupLogBuilderFactoryImpl org.bonitasoft.engine.identity.model.builder.SGroupUpdateBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SGroupUpdateBuilderFactoryImpl #org.bonitasoft.engine.identity.model.builder.SIdentityUpdateBuilderFactory = null #org.bonitasoft.engine.identity.model.builder.SMembershipLogBuilderFactory = null org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionLogBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SCustomUserInfoDefinitionLogBuilderFactoryImpl org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SCustomUserInfoDefinitionUpdateBuilderFactoryImpl #org.bonitasoft.engine.identity.model.builder.SProfileMetadataValueLogBuilderFactory = null org.bonitasoft.engine.identity.model.builder.SCustomUserInfoValueUpdateBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SCustomUserInfoValueUpdateBuilderFactoryImpl org.bonitasoft.engine.identity.model.builder.SRoleLogBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SRoleLogBuilderFactoryImpl org.bonitasoft.engine.identity.model.builder.SRoleUpdateBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SRoleUpdateBuilderFactoryImpl org.bonitasoft.engine.identity.model.builder.SUserLogBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SUserLogBuilderFactoryImpl org.bonitasoft.engine.identity.model.builder.SUserMembershipLogBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SUserMembershipLogBuilderFactoryImpl org.bonitasoft.engine.identity.model.builder.SUserMembershipUpdateBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SUserMembershipUpdateBuilderFactoryImpl org.bonitasoft.engine.identity.model.builder.SUserUpdateBuilderFactory = org.bonitasoft.engine.identity.model.builder.impl.SUserUpdateBuilderFactoryImpl org.bonitasoft.engine.platform.command.model.SPlatformCommandLogBuilderFactory = org.bonitasoft.engine.platform.command.model.impl.SPlatformCommandLogBuilderFactoryImpl org.bonitasoft.engine.platform.command.model.SPlatformCommandUpdateBuilderFactory = org.bonitasoft.engine.platform.command.model.impl.SPlatformCommandUpdateBuilderFactoryImpl org.bonitasoft.engine.platform.session.model.builder.SPlatformSessionBuilderFactory = org.bonitasoft.engine.platform.session.model.builder.impl.SPlatformSessionBuilderFactoryImpl org.bonitasoft.engine.profile.builder.SProfileMemberUpdateBuilderFactory = org.bonitasoft.engine.profile.builder.impl.SProfileMemberUpdateBuilderFactoryImpl org.bonitasoft.engine.profile.builder.SProfileUpdateBuilderFactory = org.bonitasoft.engine.profile.builder.impl.SProfileUpdateBuilderFactoryImpl org.bonitasoft.engine.supervisor.mapping.model.SProcessSupervisorLogBuilderFactory = org.bonitasoft.engine.supervisor.mapping.model.impl.SProcessSupervisorLogBuilderFactoryImpl org.bonitasoft.engine.dependency.model.builder.SPlatformDependencyLogBuilderFactory = org.bonitasoft.engine.dependency.model.builder.impl.SPlatformDependencyLogBuilderFactoryImpl org.bonitasoft.engine.dependency.model.builder.SPlatformDependencyMappingLogBuilderFactory = org.bonitasoft.engine.dependency.model.builder.impl.SPlatformDependencyMappingLogBuilderFactoryImpl org.bonitasoft.engine.page.SPageLogBuilderFactory = org.bonitasoft.engine.page.impl.SPageLogBuilderFactoryImpl org.bonitasoft.engine.page.SPageUpdateBuilderFactory = org.bonitasoft.engine.page.impl.SPageUpdateBuilderFactoryImpl ## business data org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.business.data.SRefBusinessDataInstanceBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.builder.business.data.SRefBusinessDataInstanceLogBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.business.data.SRefBusinessDataInstanceLogBuilderFactoryImpl org.bonitasoft.engine.core.process.instance.model.archive.builder.business.data.SARefBusinessDataInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.business.data.SARefBusinessDataInstanceBuilderFactoryImpl ##Abstract factories, cannot be instantiated ##org.bonitasoft.engine.core.process.instance.model.archive.builder.SAActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAActivityInstanceBuilderFactoryImpl ##org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowElementInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAFlowElementInstanceBuilderFactoryImpl ##org.bonitasoft.engine.core.process.instance.model.archive.builder.SAFlowNodeInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.archive.builder.impl.SAFlowNodeInstanceBuilderFactoryImpl ##org.bonitasoft.engine.core.process.instance.model.builder.SActivityInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SActivityInstanceBuilderFactoryImpl ##org.bonitasoft.engine.core.process.instance.model.builder.SFlowElementInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SFlowElementInstanceBuilderFactoryImpl ##org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SFlowNodeInstanceBuilderFactoryImpl ##org.bonitasoft.engine.core.process.instance.model.builder.SHumanTaskInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.impl.SHumanTaskInstanceBuilderFactoryImpl ##org.bonitasoft.engine.core.process.instance.model.builder.event.SEventInstanceBuilderFactory = org.bonitasoft.engine.core.process.instance.model.builder.event.impl.SEventInstanceBuilderFactoryImpl ================================================ FILE: services/bonita-business-application/build.gradle ================================================ dependencies { api project(':services:bonita-persistence') api project(':services:bonita-events') api project(':services:bonita-log') api project(':services:bonita-builder') testImplementation libs.mockitoCore testImplementation libs.assertj annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/ApplicationService.java ================================================ /** * 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.business.application; import java.util.List; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; import org.bonitasoft.engine.commons.TenantLifecycleService; import org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Elias Ricken de Medeiros */ public interface ApplicationService extends TenantLifecycleService { String APPLICATION = "APPLICATION"; String APPLICATION_PAGE = "APPLICATION_PAGE"; String APPLICATION_MENU = "APPLICATION_MENU"; String DEFAULT_LAYOUT_NAME = "custompage_layoutBonita"; String DEFAULT_THEME_NAME = "custompage_themeBonita"; SApplicationWithIcon createApplication(SApplicationWithIcon application) throws SObjectCreationException, SObjectAlreadyExistsException; SApplication getApplication(long applicationId) throws SBonitaReadException, SObjectNotFoundException; SApplicationWithIcon getApplicationWithIcon(long applicationId) throws SBonitaReadException, SObjectNotFoundException; SApplication getApplicationByToken(String token) throws SBonitaReadException; void deleteApplication(long applicationId) throws SObjectModificationException, SObjectNotFoundException; void forceDeleteApplication(SApplication application) throws SObjectModificationException; SApplicationWithIcon updateApplication(long applicationId, EntityUpdateDescriptor updateDescriptor) throws SObjectModificationException, SObjectAlreadyExistsException, SObjectNotFoundException; SApplicationWithIcon updateApplication(SApplicationWithIcon application, EntityUpdateDescriptor updateDescriptor) throws SObjectModificationException, SObjectAlreadyExistsException; long getNumberOfApplications(QueryOptions options) throws SBonitaReadException; List searchApplications(QueryOptions options) throws SBonitaReadException; SApplicationPage createApplicationPage(SApplicationPage applicationPage) throws SObjectCreationException, SObjectAlreadyExistsException; SApplicationPage getApplicationPage(String applicationName, String applicationPageToken) throws SBonitaReadException, SObjectNotFoundException; SApplicationPage getApplicationPage(long applicationPageId) throws SBonitaReadException, SObjectNotFoundException; SApplicationPage getApplicationHomePage(long applicationId) throws SBonitaReadException, SObjectNotFoundException; SApplicationPage deleteApplicationPage(long applicationPageId) throws SObjectModificationException, SObjectNotFoundException; void deleteApplicationPage(SApplicationPage applicationPage) throws SObjectModificationException; long getNumberOfApplicationPages(final QueryOptions options) throws SBonitaReadException; List searchApplicationPages(final QueryOptions options) throws SBonitaReadException; SApplicationMenu createApplicationMenu(SApplicationMenu applicationMenu) throws SObjectCreationException; SApplicationMenu updateApplicationMenu(long applicationMenuId, EntityUpdateDescriptor updateDescriptor) throws SObjectModificationException, SObjectNotFoundException; SApplicationMenu updateApplicationMenu(SApplicationMenu applicationMenu, EntityUpdateDescriptor updateDescriptor, boolean organizeIndexes) throws SObjectModificationException; SApplicationMenu getApplicationMenu(long applicationMenuId) throws SBonitaReadException, SObjectNotFoundException; SApplicationMenu deleteApplicationMenu(long applicationMenuId) throws SObjectModificationException, SObjectNotFoundException; void deleteApplicationMenu(SApplicationMenu applicationMenu) throws SObjectModificationException; long getNumberOfApplicationMenus(QueryOptions options) throws SBonitaReadException; List searchApplicationMenus(QueryOptions options) throws SBonitaReadException; List getAllPagesForProfile(long profileId) throws SBonitaReadException; List getAllPagesForProfile(String profile) throws SBonitaReadException; int getNextAvailableIndex(Long parentMenuId) throws SBonitaReadException; int getLastUsedIndex(Long parentMenuId) throws SBonitaReadException; long getNumberOfApplicationsOfUser(long userId, QueryOptions options) throws SBonitaReadException; List searchApplicationsOfUser(long userId, QueryOptions options) throws SBonitaReadException; } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/ApplicationServiceImpl.java ================================================ /** * 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.business.application.impl; import static java.lang.String.format; import static org.bonitasoft.engine.business.application.model.SApplicationWithIcon.ALWAYS_MODIFIABLE_FIELDS; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.impl.cleaner.ApplicationDestructor; import org.bonitasoft.engine.business.application.impl.cleaner.ApplicationMenuCleaner; import org.bonitasoft.engine.business.application.impl.cleaner.ApplicationMenuDestructor; import org.bonitasoft.engine.business.application.impl.cleaner.ApplicationPageDestructor; import org.bonitasoft.engine.business.application.impl.converter.MenuIndexConverter; import org.bonitasoft.engine.business.application.model.AbstractSApplication; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; import org.bonitasoft.engine.business.application.model.builder.SApplicationLogBuilder; import org.bonitasoft.engine.business.application.model.builder.SApplicationMenuLogBuilder; import org.bonitasoft.engine.business.application.model.builder.SApplicationPageLogBuilder; import org.bonitasoft.engine.business.application.model.builder.impl.SApplicationLogBuilderImpl; import org.bonitasoft.engine.business.application.model.builder.impl.SApplicationMenuLogBuilderImpl; import org.bonitasoft.engine.business.application.model.builder.impl.SApplicationPageLogBuilderImpl; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.cache.SCacheException; import org.bonitasoft.engine.cache.configuration.CacheConfigurationBeans; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author Elias Ricken de Medeiros */ @Service public class ApplicationServiceImpl implements ApplicationService { public static final int MAX_RESULTS = 1000; private final Recorder recorder; private final ReadPersistenceService persistenceService; private final QueriableLoggerService queriableLoggerService; private final IndexManager indexManager; private final MenuIndexConverter menuIndexConverter; private final ApplicationDestructor applicationDestructor; private final ApplicationPageDestructor applicationPageDestructor; private final ApplicationMenuDestructor applicationMenuDestructor; private final CacheService cacheService; @Autowired public ApplicationServiceImpl(Recorder recorder, ReadPersistenceService persistenceService, final QueriableLoggerService queriableLoggerService, CacheService cacheService) { this.recorder = recorder; this.persistenceService = persistenceService; this.queriableLoggerService = queriableLoggerService; this.cacheService = cacheService; indexManager = new IndexManager(new IndexUpdater(this, MAX_RESULTS), new MenuIndexValidator()); menuIndexConverter = new MenuIndexConverter(this); final ApplicationMenuCleaner applicationMenuCleaner = new ApplicationMenuCleaner(this); applicationDestructor = new ApplicationDestructor(applicationMenuCleaner); applicationPageDestructor = new ApplicationPageDestructor(applicationMenuCleaner, new HomePageChecker(this)); applicationMenuDestructor = new ApplicationMenuDestructor(applicationMenuCleaner); } //Visible for tests only ApplicationServiceImpl(Recorder recorder, ReadPersistenceService persistenceService, QueriableLoggerService queriableLoggerService, IndexManager indexManager, MenuIndexConverter menuIndexConverter, ApplicationDestructor applicationDestructor, ApplicationPageDestructor applicationPageDestructor, ApplicationMenuDestructor applicationMenuDestructor, CacheService cacheService) { this.recorder = recorder; this.persistenceService = persistenceService; this.queriableLoggerService = queriableLoggerService; this.cacheService = cacheService; this.indexManager = indexManager; this.menuIndexConverter = menuIndexConverter; this.applicationDestructor = applicationDestructor; this.applicationPageDestructor = applicationPageDestructor; this.applicationMenuDestructor = applicationMenuDestructor; } @Override public SApplicationWithIcon createApplication(final SApplicationWithIcon application) throws SObjectCreationException, SObjectAlreadyExistsException { final String methodName = "createApplication"; final SApplicationLogBuilder logBuilder = getApplicationLogBuilder(ActionType.CREATED, "Creating application named " + application.getToken()); try { validateApplication(application); recorder.recordInsert(new InsertRecord(application), APPLICATION); log(application.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } catch (final SObjectAlreadyExistsException e) { return logAndRetrowException(application.getId(), methodName, logBuilder, e); } catch (final SBonitaException e) { handleCreationException(application, logBuilder, e, methodName); } return application; } private void validateApplication(final SApplicationWithIcon application) throws SBonitaReadException, SObjectAlreadyExistsException { validateApplicationToken(application.getToken()); } private void validateApplicationToken(final String applicationToken) throws SBonitaReadException, SObjectAlreadyExistsException { if (hasApplicationWithToken(applicationToken)) { throw new SObjectAlreadyExistsException( "An application already exists with token '" + applicationToken + "'."); } } private void handleCreationException(final PersistentObject persistentObject, final SPersistenceLogBuilder logBuilder, final Exception e, final String methodName) throws SObjectCreationException { log(persistentObject.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SObjectCreationException(e); } public boolean hasApplicationWithToken(final String name) throws SBonitaReadException { final SApplication application = getApplicationByToken(name); return application != null; } @Override public SApplication getApplicationByToken(final String token) throws SBonitaReadException { try { SApplication application = (SApplication) cacheService.get( CacheConfigurationBeans.APPLICATION_TOKEN_CACHE_NAME, token); if (application == null) { application = persistenceService .selectOne(new SelectOneDescriptor<>("getApplicationByToken", Collections .singletonMap("token", token), SApplication.class)); if (application != null) { cacheService.store(CacheConfigurationBeans.APPLICATION_TOKEN_CACHE_NAME, token, application); } } return application; } catch (SCacheException e) { throw new SBonitaReadException(e); } } private void initializeLogBuilder(final T logBuilder, final String message, final ActionType actionType) { logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message); logBuilder.setActionType(actionType); } private SApplicationLogBuilder getApplicationLogBuilder(final ActionType actionType, final String message) { final SApplicationLogBuilder logBuilder = new SApplicationLogBuilderImpl(); initializeLogBuilder(logBuilder, message, actionType); return logBuilder; } private SApplicationPageLogBuilder getApplicationPageLogBuilder(final ActionType actionType, final String message) { final SApplicationPageLogBuilderImpl logBuilder = new SApplicationPageLogBuilderImpl(); initializeLogBuilder(logBuilder, message, actionType); return logBuilder; } private SApplicationMenuLogBuilder getApplicationMenuLogBuilder(final ActionType actionType, final String message) { final SApplicationMenuLogBuilderImpl logBuilder = new SApplicationMenuLogBuilderImpl(); initializeLogBuilder(logBuilder, message, actionType); return logBuilder; } private void log(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder, final String methodName) { logBuilder.actionScope(String.valueOf(objectId)); logBuilder.actionStatus(sQueriableLogStatus); logBuilder.objectId(objectId); final SQueriableLog log = logBuilder.build(); if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) { queriableLoggerService.log(this.getClass().getName(), methodName, log); } } @Override public SApplication getApplication(final long applicationId) throws SBonitaReadException, SObjectNotFoundException { SApplication application = persistenceService .selectById(new SelectByIdDescriptor<>(SApplication.class, applicationId)); if (application == null) { throw new SObjectNotFoundException("No application found with id '" + applicationId + "'."); } return application; } @Override public SApplicationWithIcon getApplicationWithIcon(long applicationId) throws SBonitaReadException, SObjectNotFoundException { SApplicationWithIcon application = persistenceService .selectById(new SelectByIdDescriptor<>(SApplicationWithIcon.class, applicationId)); if (application == null) { throw new SObjectNotFoundException("No application found with id '" + applicationId + "'."); } return application; } @Override public void forceDeleteApplication(SApplication application) throws SObjectModificationException { try { deleteApplication(application); } catch (final SBonitaException e) { throw new SObjectModificationException(e); } } @Override public void deleteApplication(final long applicationId) throws SObjectModificationException, SObjectNotFoundException { try { final SApplication application = getApplication(applicationId); if (!application.isEditable()) { throw new SObjectModificationException(format( "The application '%s' is set as non modifiable. It cannot be deleted.", application.getDisplayName())); } deleteApplication(application); } catch (final SObjectNotFoundException e) { throw e; } catch (final SBonitaException e) { throw new SObjectModificationException(e); } } private void deleteApplication(SApplication application) throws SBonitaException { if (application.getToken() != null) { cacheService.remove(CacheConfigurationBeans.APPLICATION_TOKEN_CACHE_NAME, application.getToken()); } applicationDestructor.onDeleteApplication(application); recorder.recordDelete(new DeleteRecord(application), APPLICATION); log(application.getId(), SQueriableLog.STATUS_OK, getApplicationLogBuilder(ActionType.DELETED, "Deleting application with id " + application.getId()), "deleteApplication"); } @Override public SApplicationWithIcon updateApplication(final long applicationId, final EntityUpdateDescriptor updateDescriptor) throws SObjectModificationException, SObjectAlreadyExistsException, SObjectNotFoundException { final String methodName = "updateApplication"; final SApplicationLogBuilder logBuilder = getApplicationLogBuilder(ActionType.UPDATED, "Updating application with id " + applicationId); try { final SApplicationWithIcon application = getApplicationWithIcon(applicationId); if (!application.isEditable()) { for (String field : updateDescriptor.getFields().keySet()) { if (!ALWAYS_MODIFIABLE_FIELDS.contains(field)) { throw new SObjectModificationException("The application is provided." + " Only the theme, the layout, and the icon can be updated."); } } } verifyNewHomePageExists(updateDescriptor); return updateApplication(application, updateDescriptor); } catch (final SObjectNotFoundException | SObjectAlreadyExistsException | SObjectModificationException e) { throw e; } catch (final SBonitaException e) { log(applicationId, SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SObjectModificationException(e); } } private void verifyNewHomePageExists(final EntityUpdateDescriptor updateDescriptor) throws SBonitaReadException, SObjectModificationException { final Long homePageId = (Long) updateDescriptor.getFields().get(AbstractSApplication.HOME_PAGE_ID); if (homePageId != null) { final SApplicationPage applicationPage = executeGetApplicationPageById(homePageId); if (applicationPage == null) { throw new SObjectModificationException( "Invalid home page id: no application page found with id '" + homePageId + "'"); } } } @Override public SApplicationWithIcon updateApplication(final SApplicationWithIcon application, final EntityUpdateDescriptor updateDescriptor) throws SObjectModificationException, SObjectAlreadyExistsException { final String methodName = "updateApplication"; final long now = System.currentTimeMillis(); final SApplicationLogBuilder logBuilder = getApplicationLogBuilder(ActionType.UPDATED, "Updating application with id " + application.getId()); try { validateUpdatedFields(updateDescriptor, application); if (application.getToken() != null) { cacheService.remove(CacheConfigurationBeans.APPLICATION_TOKEN_CACHE_NAME, application.getToken()); } updateDescriptor.addField(AbstractSApplication.LAST_UPDATE_DATE, now); recorder.recordUpdate(UpdateRecord.buildSetFields(application, updateDescriptor), APPLICATION); log(application.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); return application; } catch (final SObjectAlreadyExistsException e) { return logAndRetrowException(application.getId(), methodName, logBuilder, e); } catch (final SBonitaException e) { log(application.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SObjectModificationException(e); } } private T logAndRetrowException(final long objectId, final String methodName, final SPersistenceLogBuilder logBuilder, final E e) throws E { log(objectId, SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw e; } private void validateUpdatedFields(final EntityUpdateDescriptor updateDescriptor, final SApplicationWithIcon application) throws SBonitaReadException, SObjectAlreadyExistsException, SObjectModificationException { if (updateDescriptor.getFields().containsKey(AbstractSApplication.TOKEN) && !application.getToken().equals(updateDescriptor.getFields().get(AbstractSApplication.TOKEN))) { validateApplicationToken((String) updateDescriptor.getFields().get(AbstractSApplication.TOKEN)); } if (updateDescriptor.getFields().containsKey(AbstractSApplication.LINK) && !Boolean.valueOf(application.isLink()) .equals(updateDescriptor.getFields().get(AbstractSApplication.LINK))) { throw new SObjectModificationException(format( "The 'link' nature of application '%s' is not modifiable. You can not switch between legacy and application links.", application.getDisplayName())); } } @Override public long getNumberOfApplications(final QueryOptions options) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SApplication.class, options, null); } @Override public long getNumberOfApplicationsOfUser(long userId, QueryOptions options) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userId", userId); return persistenceService.getNumberOfEntities(SApplication.class, "OfUser", options, parameters); } @Override public List searchApplications(final QueryOptions options) throws SBonitaReadException { return persistenceService.searchEntity(SApplication.class, options, null); } @Override public List searchApplicationsOfUser(long userId, QueryOptions options) throws SBonitaReadException { final Map parameters = Collections.singletonMap("userId", userId); return persistenceService.searchEntity(SApplication.class, "OfUser", options, parameters); } @Override public SApplicationPage createApplicationPage(final SApplicationPage applicationPage) throws SObjectCreationException, SObjectAlreadyExistsException { final String methodName = "createApplicationPage"; final SApplicationPageLogBuilder logBuilder = getApplicationPageLogBuilder(ActionType.CREATED, "Creating application page with token " + applicationPage.getToken()); try { validateApplicationPage(applicationPage); recorder.recordInsert(new InsertRecord(applicationPage), APPLICATION_PAGE); log(applicationPage.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } catch (final SObjectAlreadyExistsException e) { return logAndRetrowException(applicationPage.getId(), methodName, logBuilder, e); } catch (final SBonitaException e) { handleCreationException(applicationPage, logBuilder, e, methodName); } return applicationPage; } private void validateApplicationPage(final SApplicationPage applicationPage) throws SBonitaReadException, SObjectAlreadyExistsException { final String applicationPageToken = applicationPage.getToken(); if (hasApplicationPage(applicationPage.getApplicationId(), applicationPageToken)) { String stb = "An application page with token '" + applicationPageToken + "' already exists for the application with id '" + applicationPage.getApplicationId() + "'"; throw new SObjectAlreadyExistsException(stb); } } private boolean hasApplicationPage(final long applicationId, final String name) throws SBonitaReadException { return getApplicationPage(applicationId, name) != null; } public SApplicationPage getApplicationPage(final long applicationId, final String applicationPageToken) throws SBonitaReadException { final Map inputParameters = new HashMap<>(2); inputParameters.put("applicationId", applicationId); inputParameters.put("applicationPageToken", applicationPageToken); return persistenceService.selectOne(new SelectOneDescriptor<>("getApplicationPageByTokenAndApplicationId", inputParameters, SApplicationPage.class)); } @Override public SApplicationPage getApplicationPage(final String applicationToken, final String applicationPageToken) throws SBonitaReadException, SObjectNotFoundException { final Map inputParameters = new HashMap<>(2); inputParameters.put("applicationToken", applicationToken); inputParameters.put("applicationPageToken", applicationPageToken); final SApplicationPage applicationPage = persistenceService .selectOne(new SelectOneDescriptor<>("getApplicationPageByTokenAndApplicationToken", inputParameters, SApplicationPage.class)); if (applicationPage == null) { throw new SObjectNotFoundException("No application page found with name '" + applicationPageToken + "' and application token '" + applicationToken + "'."); } return applicationPage; } @Override public SApplicationPage getApplicationPage(final long applicationPageId) throws SBonitaReadException, SObjectNotFoundException { final SApplicationPage applicationPage = executeGetApplicationPageById(applicationPageId); if (applicationPage == null) { throw new SObjectNotFoundException("No application page found with id '" + applicationPageId + "'."); } return applicationPage; } private SApplicationPage executeGetApplicationPageById(final long applicationPageId) throws SBonitaReadException { return persistenceService .selectById(new SelectByIdDescriptor<>(SApplicationPage.class, applicationPageId)); } @Override public SApplicationPage deleteApplicationPage(final long applicationPageId) throws SObjectModificationException, SObjectNotFoundException { final String methodName = "deleteApplicationPage"; final SApplicationPageLogBuilder logBuilder = getApplicationPageLogBuilder(ActionType.DELETED, "Deleting application page with id " + applicationPageId); SApplicationPage applicationPage = null; try { applicationPage = getApplicationPage(applicationPageId); deleteApplicationPage(applicationPage); } catch (final SObjectNotFoundException e) { logAndRetrowException(applicationPageId, methodName, logBuilder, e); } catch (final SObjectModificationException e) { throw e; } catch (final SBonitaException e) { throw new SObjectModificationException(e); } return applicationPage; } @Override public void deleteApplicationPage(final SApplicationPage applicationPage) throws SObjectModificationException { final String methodName = "deleteApplicationPage"; final SApplicationPageLogBuilder logBuilder = getApplicationPageLogBuilder(ActionType.DELETED, "Deleting application page with id " + applicationPage.getId()); try { applicationPageDestructor.onDeleteApplicationPage(applicationPage); recorder.recordDelete(new DeleteRecord(applicationPage), APPLICATION_PAGE); log(applicationPage.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } catch (final SObjectModificationException e) { logAndRetrowException(applicationPage.getId(), methodName, logBuilder, e); } catch (final SBonitaException e) { throw new SObjectModificationException(e); } } @Override public SApplicationPage getApplicationHomePage(final long applicationId) throws SBonitaReadException, SObjectNotFoundException { final Map inputParameters = new HashMap<>(2); inputParameters.put("applicationId", applicationId); final SApplicationPage applicationPage = persistenceService .selectOne( new SelectOneDescriptor<>("getApplicationHomePage", inputParameters, SApplicationPage.class)); if (applicationPage == null) { throw new SObjectNotFoundException("No home page found for application with id '" + applicationId + "'."); } return applicationPage; } @Override public long getNumberOfApplicationPages(final QueryOptions options) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SApplicationPage.class, options, null); } @Override public List searchApplicationPages(final QueryOptions options) throws SBonitaReadException { return persistenceService.searchEntity(SApplicationPage.class, options, null); } @Override public SApplicationMenu createApplicationMenu(final SApplicationMenu applicationMenu) throws SObjectCreationException { final String methodName = "createApplicationMenu"; final SApplicationMenuLogBuilder logBuilder = getApplicationMenuLogBuilder(ActionType.CREATED, "Creating application menu with display name " + applicationMenu.getDisplayName()); try { recorder.recordInsert(new InsertRecord(applicationMenu), APPLICATION_MENU); log(applicationMenu.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } catch (final SRecorderException e) { handleCreationException(applicationMenu, logBuilder, e, methodName); } return applicationMenu; } @Override public SApplicationMenu updateApplicationMenu(final long applicationMenuId, final EntityUpdateDescriptor updateDescriptor) throws SObjectModificationException, SObjectNotFoundException { final String methodName = "updateApplicationMenu"; final SApplicationMenuLogBuilder logBuilder = getApplicationMenuLogBuilder(ActionType.UPDATED, "Updating application menu with id " + applicationMenuId); try { final SApplicationMenu applicationMenu = getApplicationMenu(applicationMenuId); updateApplicationMenu(applicationMenu, updateDescriptor, true); return applicationMenu; } catch (final SObjectNotFoundException e) { return logAndRetrowException(applicationMenuId, methodName, logBuilder, e); } catch (final SBonitaReadException e) { throw new SObjectModificationException(e); } } @Override public SApplicationMenu updateApplicationMenu(final SApplicationMenu applicationMenu, final EntityUpdateDescriptor updateDescriptor, final boolean organizeIndexes) throws SObjectModificationException { try { organizeIndexesOnUpdate(applicationMenu, updateDescriptor, organizeIndexes); recorder.recordUpdate(UpdateRecord.buildSetFields(applicationMenu, updateDescriptor), APPLICATION_MENU); log(applicationMenu.getId(), SQueriableLog.STATUS_OK, getApplicationMenuLogBuilder(ActionType.UPDATED, "Updating application menu with id " + applicationMenu.getId()), "updateApplicationMenu"); return applicationMenu; } catch (final SBonitaException e) { throw new SObjectModificationException(e); } } private void organizeIndexesOnUpdate(final SApplicationMenu applicationMenu, final EntityUpdateDescriptor updateDescriptor, final boolean organizeIndexes) throws SObjectModificationException, SBonitaReadException { final Map fields = updateDescriptor.getFields(); if (fields.containsKey(SApplicationMenu.PARENT_ID) && !fields.containsKey(SApplicationMenu.INDEX)) { //we need to force the update of index, as it has change of parent fields.put(SApplicationMenu.INDEX, getNextAvailableIndex((Long) fields.get(SApplicationMenu.PARENT_ID))); } final Integer newIndexValue = (Integer) fields.get(SApplicationMenu.INDEX); if (newIndexValue != null && organizeIndexes) { final MenuIndex oldIndex = menuIndexConverter.toMenuIndex(applicationMenu); final MenuIndex newIndex = menuIndexConverter.toMenuIndex(applicationMenu, updateDescriptor); indexManager.organizeIndexesOnUpdate(oldIndex, newIndex); } } @Override public SApplicationMenu getApplicationMenu(final long applicationMenuId) throws SBonitaReadException, SObjectNotFoundException { final SApplicationMenu applicationMenu = persistenceService .selectById(new SelectByIdDescriptor<>(SApplicationMenu.class, applicationMenuId)); if (applicationMenu == null) { throw new SObjectNotFoundException("No application found with id '" + applicationMenuId + "'."); } return applicationMenu; } @Override public SApplicationMenu deleteApplicationMenu(final long applicationMenuId) throws SObjectModificationException, SObjectNotFoundException { final String methodName = "deleteApplicationMenu"; final SApplicationMenuLogBuilder logBuilder = getApplicationMenuLogBuilder(ActionType.DELETED, "Deleting application menu with id " + applicationMenuId); SApplicationMenu applicationMenu = null; try { applicationMenu = getApplicationMenu(applicationMenuId); deleteApplicationMenu(applicationMenu); log(applicationMenu.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } catch (final SObjectNotFoundException e) { logAndRetrowException(applicationMenuId, methodName, logBuilder, e); } catch (final SBonitaReadException e) { throw new SObjectModificationException(e); } return applicationMenu; } @Override public void deleteApplicationMenu(final SApplicationMenu applicationMenu) throws SObjectModificationException { try { applicationMenuDestructor.onDeleteApplicationMenu(applicationMenu); final int lastUsedIndex = getLastUsedIndex(applicationMenu.getParentId()); indexManager.organizeIndexesOnDelete( new MenuIndex(applicationMenu.getParentId(), applicationMenu.getIndex(), lastUsedIndex)); recorder.recordDelete(new DeleteRecord(applicationMenu), APPLICATION_MENU); } catch (final SBonitaException e) { throw new SObjectModificationException(e); } } @Override public long getNumberOfApplicationMenus(final QueryOptions options) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SApplicationMenu.class, options, null); } @Override public List searchApplicationMenus(final QueryOptions options) throws SBonitaReadException { return persistenceService.searchEntity(SApplicationMenu.class, options, null); } @Override public int getNextAvailableIndex(final Long parentMenuId) throws SBonitaReadException { final int lastIndex = getLastUsedIndex(parentMenuId); return lastIndex + 1; } @Override public List getAllPagesForProfile(final long profileId) throws SBonitaReadException { final SelectListDescriptor selectList = new SelectListDescriptor<>("getAllPagesForProfile", Collections.singletonMap("profileId", profileId), SApplicationPage.class, new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS)); return persistenceService.selectList(selectList); } @Override public List getAllPagesForProfile(String profile) throws SBonitaReadException { final SelectListDescriptor selectList = new SelectListDescriptor<>("getAllPagesForProfileName", Collections.singletonMap("profileName", profile), SApplicationPage.class, new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS)); return persistenceService.selectList(selectList); } protected Integer executeGetLastUsedIndexQuery(final Long parentMenuId) throws SBonitaReadException { SelectOneDescriptor selectDescriptor; if (parentMenuId == null) { selectDescriptor = new SelectOneDescriptor<>("getLastIndexForRootMenu", Collections.emptyMap(), SApplicationMenu.class); } else { selectDescriptor = new SelectOneDescriptor<>("getLastIndexForChildOf", Collections.singletonMap(SApplicationMenu.PARENT_ID, parentMenuId), SApplicationMenu.class); } return persistenceService.selectOne(selectDescriptor); } @Override public int getLastUsedIndex(final Long parentMenuId) throws SBonitaReadException { final Integer lastUsedIndex = executeGetLastUsedIndexQuery(parentMenuId); return lastUsedIndex == null ? 0 : lastUsedIndex; } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/HomePageChecker.java ================================================ /** * 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.business.application.impl; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Elias Ricken de Medeiros */ public class HomePageChecker { private ApplicationService applicationService; public HomePageChecker(ApplicationService applicationService) { this.applicationService = applicationService; } public boolean isHomePage(SApplicationPage applicationPage) throws SBonitaReadException, SObjectNotFoundException { SApplication application = applicationService.getApplication(applicationPage.getApplicationId()); return application.getHomePageId() != null && applicationPage.getId() == application.getHomePageId(); } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/IndexManager.java ================================================ /** * 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.business.application.impl; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Elias Ricken de Medeiros */ public class IndexManager { private IndexUpdater updater; private MenuIndexValidator validator; public IndexManager(IndexUpdater updater, MenuIndexValidator validator) { this.updater = updater; this.validator = validator; } public void organizeIndexesOnDelete(MenuIndex deletedMenuIndex) throws SBonitaReadException, SObjectModificationException { updater.decrementIndexes(deletedMenuIndex.getParentId(), deletedMenuIndex.getValue() + 1, deletedMenuIndex.getLastUsedIndex()); } public void organizeIndexesOnUpdate(MenuIndex oldIndex, MenuIndex newIndex) throws SBonitaReadException, SObjectModificationException { validateNewIndex(oldIndex, newIndex); if (oldIndex.getParentId() == newIndex.getParentId()) { if (newIndex.getValue() < oldIndex.getValue()) { updater.incrementIndexes(oldIndex.getParentId(), newIndex.getValue(), oldIndex.getValue() - 1); } else { updater.decrementIndexes(oldIndex.getParentId(), oldIndex.getValue() + 1, newIndex.getValue()); } } else { updater.incrementIndexes(newIndex.getParentId(), newIndex.getValue(), newIndex.getLastUsedIndex()); updater.decrementIndexes(oldIndex.getParentId(), oldIndex.getValue() + 1, oldIndex.getLastUsedIndex()); } } private void validateNewIndex(MenuIndex oldIndex, MenuIndex newIndex) throws SObjectModificationException { List validationProblems = validator.validate(oldIndex, newIndex); if (!validationProblems.isEmpty()) { throw new SObjectModificationException(validationProblems.toString()); } } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/IndexUpdater.java ================================================ /** * 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.business.application.impl; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.business.application.model.builder.SApplicationMenuUpdateBuilder; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Elias Ricken de Medeiros */ public class IndexUpdater { private ApplicationService applicationService; private int maxResults; public IndexUpdater(ApplicationService applicationService, int maxResults) { this.applicationService = applicationService; this.maxResults = maxResults; } public void incrementIndexes(Long parentId, int from, int to) throws SBonitaReadException, SObjectModificationException { updateIndexes(parentId, from, to, 1); } public void decrementIndexes(Long parentId, int from, int to) throws SBonitaReadException, SObjectModificationException { updateIndexes(parentId, from, to, -1); } private void updateIndexes(Long parentId, int from, int to, int offSet) throws SObjectModificationException, SBonitaReadException { if (to >= from) { List menusToUpdate = null; int firstResult = 0; do { menusToUpdate = getCurrentPage(parentId, from, to, firstResult); firstResult += maxResults; updateIndexes(menusToUpdate, offSet); } while (menusToUpdate.size() == maxResults); } } private List getCurrentPage(Long parentId, int from, int to, int firstResult) throws SBonitaReadException { List orderBy = Collections .singletonList(new OrderByOption(SApplicationMenu.class, SApplicationMenu.INDEX, OrderByType.ASC)); List filters = Arrays .asList(new FilterOption(SApplicationMenu.class, SApplicationMenu.INDEX, from, to), new FilterOption( SApplicationMenu.class, SApplicationMenu.PARENT_ID, parentId)); QueryOptions options = new QueryOptions(firstResult, maxResults, orderBy, filters, null); return applicationService.searchApplicationMenus(options); } private void updateIndexes(List menusToUpdate, int offSet) throws SObjectModificationException { for (SApplicationMenu menuToUpdate : menusToUpdate) { applicationService.updateApplicationMenu(menuToUpdate, new SApplicationMenuUpdateBuilder().updateIndex(menuToUpdate.getIndex() + offSet).done(), false); } } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/MenuIndex.java ================================================ /** * 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.business.application.impl; /** * @author Elias Ricken de Medeiros */ public class MenuIndex { private int value; private Long parentId; private int lastUsedIndex; public MenuIndex(Long parentId, int value, int lastUsedIndex) { this.value = value; this.parentId = parentId; this.lastUsedIndex = lastUsedIndex; } public int getValue() { return value; } public Long getParentId() { return parentId; } public int getLastUsedIndex() { return lastUsedIndex; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof MenuIndex menuIndex)) return false; if (lastUsedIndex != menuIndex.lastUsedIndex) return false; if (value != menuIndex.value) return false; if (parentId != null ? !parentId.equals(menuIndex.parentId) : menuIndex.parentId != null) return false; return true; } @Override public int hashCode() { int result = value; result = 31 * result + (parentId != null ? parentId.hashCode() : 0); result = 31 * result + lastUsedIndex; return result; } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/MenuIndexValidator.java ================================================ /** * 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.business.application.impl; import java.util.ArrayList; import java.util.List; /** * @author Elias Ricken de Medeiros */ public class MenuIndexValidator { public List validate(MenuIndex oldIndex, MenuIndex newIndex) { final List problems = new ArrayList(1); int lastValidIndex = getLastValidIndex(oldIndex, newIndex); if (newIndex.getValue() < 1 || newIndex.getValue() > lastValidIndex) { problems.add(new StringBuilder().append("Invalid menu index: ").append(newIndex.getValue()) .append(". It must be between 1 and the number of menu in your application having the same parent. The last valid index for parent ") .append(newIndex.getParentId()).append(" is ").append(lastValidIndex).toString()); } return problems; } private int getLastValidIndex(MenuIndex oldIndex, MenuIndex newIndex) { int lastValidIndex = newIndex.getLastUsedIndex(); if (oldIndex.getParentId() != newIndex.getParentId()) { // a new element will be added in this parent lastValidIndex = newIndex.getLastUsedIndex() + 1; } return lastValidIndex; } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationDestructor.java ================================================ /** * 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.business.application.impl.cleaner; import org.bonitasoft.engine.business.application.impl.ApplicationServiceImpl; import org.bonitasoft.engine.business.application.impl.filter.ApplicationRelatedMenusFilterBuilder; import org.bonitasoft.engine.business.application.impl.filter.SelectRange; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class ApplicationDestructor { private ApplicationMenuCleaner applicationMenuCleaner; public ApplicationDestructor(ApplicationMenuCleaner applicationMenuCleaner) { this.applicationMenuCleaner = applicationMenuCleaner; } public void onDeleteApplication(SApplication application) throws SBonitaException { applicationMenuCleaner.deleteRelatedApplicationMenus( new ApplicationRelatedMenusFilterBuilder(new SelectRange(0, ApplicationServiceImpl.MAX_RESULTS), application.getId())); } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationMenuCleaner.java ================================================ /** * 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.business.application.impl.cleaner; import java.util.List; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.impl.filter.FilterBuilder; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.persistence.QueryOptions; /** * @author Elias Ricken de Medeiros */ public class ApplicationMenuCleaner { private ApplicationService applicationService; public ApplicationMenuCleaner(ApplicationService applicationService) { this.applicationService = applicationService; } public void deleteRelatedApplicationMenus(FilterBuilder filterBuilder) throws SBonitaException { QueryOptions options = filterBuilder.buildQueryOptions(); List relatedMenus; do { relatedMenus = applicationService.searchApplicationMenus(options); for (SApplicationMenu relatedMenu : relatedMenus) { applicationService.deleteApplicationMenu(relatedMenu); } } while (relatedMenus.size() == options.getNumberOfResults()); } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationMenuDestructor.java ================================================ /** * 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.business.application.impl.cleaner; import org.bonitasoft.engine.business.application.impl.ApplicationServiceImpl; import org.bonitasoft.engine.business.application.impl.filter.ChildrenMenusFilterBuilder; import org.bonitasoft.engine.business.application.impl.filter.SelectRange; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class ApplicationMenuDestructor { private ApplicationMenuCleaner applicationMenuCleaner; public ApplicationMenuDestructor(ApplicationMenuCleaner applicationMenuCleaner) { this.applicationMenuCleaner = applicationMenuCleaner; } public void onDeleteApplicationMenu(SApplicationMenu applicationMenu) throws SBonitaException { applicationMenuCleaner.deleteRelatedApplicationMenus(new ChildrenMenusFilterBuilder( new SelectRange(0, ApplicationServiceImpl.MAX_RESULTS), applicationMenu.getId())); } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationPageDestructor.java ================================================ /** * 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.business.application.impl.cleaner; import org.bonitasoft.engine.business.application.impl.ApplicationServiceImpl; import org.bonitasoft.engine.business.application.impl.HomePageChecker; import org.bonitasoft.engine.business.application.impl.filter.ApplicationPageRelatedMenusFilterBuilder; import org.bonitasoft.engine.business.application.impl.filter.SelectRange; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Elias Ricken de Medeiros */ public class ApplicationPageDestructor { private ApplicationMenuCleaner applicationMenuCleaner; private HomePageChecker homePageChecker; public ApplicationPageDestructor(ApplicationMenuCleaner applicationMenuCleaner, HomePageChecker homePageChecker) { this.applicationMenuCleaner = applicationMenuCleaner; this.homePageChecker = homePageChecker; } public void onDeleteApplicationPage(SApplicationPage applicationPage) throws SBonitaException { verifyIfIsHomePage(applicationPage); applicationMenuCleaner.deleteRelatedApplicationMenus(new ApplicationPageRelatedMenusFilterBuilder( new SelectRange(0, ApplicationServiceImpl.MAX_RESULTS), applicationPage.getId())); } private void verifyIfIsHomePage(SApplicationPage applicationPage) throws SBonitaReadException, SObjectNotFoundException, SObjectModificationException { if (homePageChecker.isHomePage(applicationPage)) { throw new SObjectModificationException("The application page with id '" + applicationPage.getId() + "' cannot be deleted because it is set as the application home page"); } } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/converter/MenuIndexConverter.java ================================================ /** * 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.business.application.impl.converter; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.impl.MenuIndex; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Elias Ricken de Medeiros */ public class MenuIndexConverter { private ApplicationService applicationService; public MenuIndexConverter(ApplicationService applicationService) { this.applicationService = applicationService; } public MenuIndex toMenuIndex(SApplicationMenu appMenu) throws SBonitaReadException { int lastUsedIndex = applicationService.getLastUsedIndex(appMenu.getParentId()); return new MenuIndex(appMenu.getParentId(), appMenu.getIndex(), lastUsedIndex); } public MenuIndex toMenuIndex(SApplicationMenu oldAppMenu, EntityUpdateDescriptor updateDescriptor) throws SBonitaReadException { Long parentId = getParentId(oldAppMenu, updateDescriptor); Integer indexValue = getIndexValue(oldAppMenu, updateDescriptor); int lastUsedIndex = applicationService.getLastUsedIndex(parentId); MenuIndex menuIndex = new MenuIndex(parentId, indexValue, lastUsedIndex); return menuIndex; } private Integer getIndexValue(SApplicationMenu oldAppMenu, EntityUpdateDescriptor updateDescriptor) { Integer indexValue; indexValue = (Integer) updateDescriptor.getFields().get(SApplicationMenu.INDEX); if (indexValue == null) { indexValue = oldAppMenu.getIndex(); } return indexValue; } private Long getParentId(SApplicationMenu oldAppMenu, EntityUpdateDescriptor updateDescriptor) { Long parentId; if (updateDescriptor.getFields().containsKey(SApplicationMenu.PARENT_ID)) { parentId = (Long) updateDescriptor.getFields().get(SApplicationMenu.PARENT_ID); } else { parentId = oldAppMenu.getParentId(); } return parentId; } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/filter/ApplicationPageRelatedMenusFilterBuilder.java ================================================ /** * 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.business.application.impl.filter; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; /** * @author Elias Ricken de Medeiros */ public class ApplicationPageRelatedMenusFilterBuilder implements FilterBuilder { private SelectRange range; private long applicationPageId; public ApplicationPageRelatedMenusFilterBuilder(SelectRange range, long applicationPageId) { this.range = range; this.applicationPageId = applicationPageId; } @Override public QueryOptions buildQueryOptions() { List orderByOptions = Collections .singletonList(new OrderByOption(SApplicationMenu.class, SApplicationMenu.ID, OrderByType.ASC)); List filters = Collections.singletonList( new FilterOption(SApplicationMenu.class, SApplicationMenu.APPLICATION_PAGE_ID, applicationPageId)); return new QueryOptions(range.getStartIndex(), range.getMaxResults(), orderByOptions, filters, null); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ApplicationPageRelatedMenusFilterBuilder that)) return false; if (applicationPageId != that.applicationPageId) return false; if (range != null ? !range.equals(that.range) : that.range != null) return false; return true; } @Override public int hashCode() { int result = range != null ? range.hashCode() : 0; result = 31 * result + (int) (applicationPageId ^ (applicationPageId >>> 32)); return result; } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/filter/ApplicationRelatedMenusFilterBuilder.java ================================================ /** * 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.business.application.impl.filter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; /** * @author Elias Ricken de Medeiros */ public class ApplicationRelatedMenusFilterBuilder implements FilterBuilder { private SelectRange range; private long applicationId; public ApplicationRelatedMenusFilterBuilder(SelectRange range, long applicationId) { this.range = range; this.applicationId = applicationId; } @Override public QueryOptions buildQueryOptions() { List orderByOptions = Collections .singletonList(new OrderByOption(SApplicationMenu.class, SApplicationMenu.ID, OrderByType.ASC)); List filters = new ArrayList(2); filters.add(new FilterOption(SApplicationMenu.class, SApplicationMenu.APPLICAITON_ID, applicationId)); //only too menu will be deleted as children menus will be deleted by the parent filters.add(new FilterOption(SApplicationMenu.class, SApplicationMenu.PARENT_ID, null)); return new QueryOptions(range.getStartIndex(), range.getMaxResults(), orderByOptions, filters, null); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ApplicationRelatedMenusFilterBuilder that)) return false; if (applicationId != that.applicationId) return false; if (range != null ? !range.equals(that.range) : that.range != null) return false; return true; } @Override public int hashCode() { int result = range != null ? range.hashCode() : 0; result = 31 * result + (int) (applicationId ^ (applicationId >>> 32)); return result; } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/filter/ChildrenMenusFilterBuilder.java ================================================ /** * 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.business.application.impl.filter; import java.util.Collections; import java.util.List; import java.util.Objects; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; /** * @author Elias Ricken de Medeiros */ public class ChildrenMenusFilterBuilder implements FilterBuilder { private final SelectRange range; private final long parentId; public ChildrenMenusFilterBuilder(SelectRange range, long parentId) { this.range = range; this.parentId = parentId; } @Override public QueryOptions buildQueryOptions() { List orderByOptions = Collections .singletonList(new OrderByOption(SApplicationMenu.class, SApplicationMenu.ID, OrderByType.ASC)); List filters = Collections .singletonList(new FilterOption(SApplicationMenu.class, SApplicationMenu.PARENT_ID, parentId)); return new QueryOptions(range.getStartIndex(), range.getMaxResults(), orderByOptions, filters, null); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ChildrenMenusFilterBuilder that)) return false; if (parentId != that.parentId) return false; if (!Objects.equals(range, that.range)) return false; return true; } @Override public int hashCode() { int result = range != null ? range.hashCode() : 0; result = 31 * result + (int) (parentId ^ (parentId >>> 32)); return result; } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/filter/FilterBuilder.java ================================================ /** * 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.business.application.impl.filter; import org.bonitasoft.engine.persistence.QueryOptions; /** * @author Elias Ricken de Medeiros */ public interface FilterBuilder { QueryOptions buildQueryOptions(); } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/impl/filter/SelectRange.java ================================================ /** * 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.business.application.impl.filter; /** * @author Elias Ricken de Medeiros */ public class SelectRange { private int startIndex; private int maxResults; public SelectRange(int startIndex, int maxResults) { this.startIndex = startIndex; this.maxResults = maxResults; } public int getStartIndex() { return startIndex; } public int getMaxResults() { return maxResults; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof SelectRange that)) return false; if (maxResults != that.maxResults) return false; if (startIndex != that.startIndex) return false; return true; } @Override public int hashCode() { int result = startIndex; result = 31 * result + maxResults; return result; } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/AbstractSApplication.java ================================================ /** * Copyright (C) 2021 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.business.application.model; import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @MappedSuperclass public abstract class AbstractSApplication implements PersistentObject { public static final String ID = "id"; public static final String TOKEN = "token"; public static final String DISPLAY_NAME = "displayName"; public static final String VERSION = "version"; public static final String DESCRIPTION = "description"; public static final String ICON_PATH = "iconPath"; public static final String CREATION_DATE = "creationDate"; public static final String CREATED_BY = "createdBy"; public static final String LAST_UPDATE_DATE = "lastUpdateDate"; public static final String UPDATED_BY = "updatedBy"; public static final String STATE = "state"; public static final String HOME_PAGE_ID = "homePageId"; public static final String PROFILE_ID = "profileId"; public static final String LAYOUT_ID = "layoutId"; public static final String THEME_ID = "themeId"; public static final String ICON_MIME_TYPE = "iconMimeType"; public static final String EDITABLE = "editable"; public static final String INTERNAL_PROFILE = "internalProfile"; public static final String LINK = "isLink"; @Id private long id; @Column private String token; @Column private String description; @Column private String version; @Column private String iconPath; @Column private long creationDate; @Column private long createdBy; @Column private long lastUpdateDate; @Column private long updatedBy; @Column private String state; @Column private Long homePageId; @Column private String displayName; @Column private Long profileId; @Column private Long layoutId; @Column private Long themeId; @Column private String iconMimeType; @Column private boolean editable = true; @Column private String internalProfile; // "link" is a reserved keyword in some databases @Column private boolean isLink; public AbstractSApplication(String token, String displayName, String version, long creationDate, long createdBy, String state, boolean editable) { this.token = token; this.displayName = displayName; this.version = version; this.creationDate = creationDate; lastUpdateDate = creationDate; //at instantiation the creation date is the same as last update date this.createdBy = createdBy; updatedBy = createdBy; this.state = state; this.editable = editable; } public boolean hasIcon() { return iconMimeType != null && !iconMimeType.isBlank(); } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/SApplication.java ================================================ /** * 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.business.application.model; import javax.persistence.Cacheable; import javax.persistence.Entity; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; @Data @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) @AllArgsConstructor @Entity @Table(name = "business_app") @Cacheable(false) public class SApplication extends AbstractSApplication { public SApplication(final String token, final String displayName, final String version, final long creationDate, final long createdBy, final String state, boolean editable) { super(token, displayName, version, creationDate, createdBy, state, editable); } public SApplication(final String token, final String displayName, final String version, final long creationDate, final long createdBy, final String state) { super(token, displayName, version, creationDate, createdBy, state, true); } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/SApplicationMenu.java ================================================ /** * 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.business.application.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "business_app_menu") public class SApplicationMenu implements PersistentObject { public static String ID = "id"; public static String DISPLAY_NAME = "displayName"; public static String APPLICAITON_ID = "applicationId"; public static String APPLICATION_PAGE_ID = "applicationPageId"; public static String PARENT_ID = "parentId"; public static String INDEX = "index"; @Id private long id; @Column private String displayName; @Column private long applicationId; @Column private Long applicationPageId; @Column private Long parentId; @Column(name = "index_") private int index; public SApplicationMenu(final String displayName, long applicationId, final Long applicationPageId, final int index) { this.displayName = displayName; this.applicationId = applicationId; this.applicationPageId = applicationPageId; this.index = index; } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/SApplicationPage.java ================================================ /** * 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.business.application.model; import javax.persistence.Cacheable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "business_app_page") @Cacheable(false) public class SApplicationPage implements PersistentObject { public static final String ID = "id"; public static final String TOKEN = "token"; public static final String PAGE_ID = "pageId"; public static final String APPLICATION_ID = "applicationId"; @Id private long id; @Column private long applicationId; @Column private long pageId; @Column private String token; public SApplicationPage(final long applicationId, final long pageId, final String token) { super(); this.applicationId = applicationId; this.pageId = pageId; this.token = token; } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/SApplicationState.java ================================================ /** * 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.business.application.model; /** * @author Elias Ricken de Medeiros */ public enum SApplicationState { ACTIVATED, DEACTIVATED } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/SApplicationWithIcon.java ================================================ /** * 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.business.application.model; import java.util.Arrays; import java.util.List; import javax.persistence.Cacheable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import org.hibernate.annotations.Type; @Data @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "business_app") @Cacheable(false) public class SApplicationWithIcon extends AbstractSApplication { public static final String ICON_CONTENT = "iconContent"; public static List ALWAYS_MODIFIABLE_FIELDS = Arrays.asList(LAYOUT_ID, THEME_ID, ICON_MIME_TYPE, ICON_CONTENT, UPDATED_BY, LAST_UPDATE_DATE); @Type(type = "materialized_blob") @Column private byte[] iconContent; public SApplicationWithIcon(String token, String displayName, String version, long creationDate, long createdBy, String state, boolean editable) { super(token, displayName, version, creationDate, createdBy, state, editable); } public SApplicationWithIcon(String token, String displayName, String version, long creationDate, long createdBy, String state) { super(token, displayName, version, creationDate, createdBy, state, true); } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationLogBuilder.java ================================================ /** * 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.business.application.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Elias Ricken de Medeiros */ public interface SApplicationLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationLogBuilderFactory.java ================================================ /** * 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.business.application.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Elias Ricken de Medeiros */ public interface SApplicationLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationMenuLogBuilder.java ================================================ /** * 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.business.application.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Elias Ricken de Medeiros */ public interface SApplicationMenuLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationMenuLogBuilderFactory.java ================================================ /** * 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.business.application.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Elias Ricken de Medeiros */ public interface SApplicationMenuLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationMenuUpdateBuilder.java ================================================ /** * 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.business.application.model.builder; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Elias Ricken de Medeiros */ public class SApplicationMenuUpdateBuilder { protected EntityUpdateDescriptor descriptor; public SApplicationMenuUpdateBuilder() { descriptor = new EntityUpdateDescriptor(); } public EntityUpdateDescriptor done() { return descriptor; } public SApplicationMenuUpdateBuilder updateDisplayName(String displayName) { descriptor.addField(SApplicationMenu.DISPLAY_NAME, displayName); return this; } public SApplicationMenuUpdateBuilder updateApplicationPageId(Long applicationPageId) { descriptor.addField(SApplicationMenu.APPLICATION_PAGE_ID, applicationPageId); return this; } public SApplicationMenuUpdateBuilder updateIndex(int index) { descriptor.addField(SApplicationMenu.INDEX, index); return this; } public SApplicationMenuUpdateBuilder updateParentId(Long parentId) { descriptor.addField(SApplicationMenu.PARENT_ID, parentId); return this; } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationMenuUpdateBuilderFactory.java ================================================ /** * 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.business.application.model.builder; /** * @author Elias Ricken de Medeiros */ public interface SApplicationMenuUpdateBuilderFactory { SApplicationMenuUpdateBuilder createNewInstance(); } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationPageLogBuilder.java ================================================ /** * 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.business.application.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Elias Ricken de Medeiros */ public interface SApplicationPageLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationPageLogBuilderFactory.java ================================================ /** * 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.business.application.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Elias Ricken de Medeiros */ public interface SApplicationPageLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/SApplicationUpdateBuilder.java ================================================ /** * 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.business.application.model.builder; import org.bonitasoft.engine.business.application.model.AbstractSApplication; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; public class SApplicationUpdateBuilder { protected final EntityUpdateDescriptor descriptor; public SApplicationUpdateBuilder(final long updaterUserId) { descriptor = new EntityUpdateDescriptor(); descriptor.addField(AbstractSApplication.UPDATED_BY, updaterUserId); descriptor.addField(AbstractSApplication.LAST_UPDATE_DATE, System.currentTimeMillis()); } public EntityUpdateDescriptor done() { return descriptor; } public SApplicationUpdateBuilder updateToken(final String token) { descriptor.addField(AbstractSApplication.TOKEN, token); return this; } public SApplicationUpdateBuilder updateDisplayName(final String displayName) { descriptor.addField(AbstractSApplication.DISPLAY_NAME, displayName); return this; } public SApplicationUpdateBuilder updateVersion(final String version) { descriptor.addField(AbstractSApplication.VERSION, version); return this; } public SApplicationUpdateBuilder updateDescription(final String description) { descriptor.addField(AbstractSApplication.DESCRIPTION, description); return this; } public SApplicationUpdateBuilder updateIconPath(final String iconPath) { descriptor.addField(AbstractSApplication.ICON_PATH, iconPath); return this; } public SApplicationUpdateBuilder updateState(final String state) { descriptor.addField(AbstractSApplication.STATE, state); return this; } public SApplicationUpdateBuilder updateProfileId(final Long profileId) { descriptor.addField(AbstractSApplication.PROFILE_ID, profileId); return this; } public SApplicationUpdateBuilder updateHomePageId(final Long homePageId) { descriptor.addField(AbstractSApplication.HOME_PAGE_ID, homePageId); return this; } public SApplicationUpdateBuilder updateLayoutId(final Long layoutId) { descriptor.addField(AbstractSApplication.LAYOUT_ID, layoutId); return this; } public SApplicationUpdateBuilder updateThemeId(final Long themeId) { descriptor.addField(AbstractSApplication.THEME_ID, themeId); return this; } public SApplicationUpdateBuilder updateIconMimeType(String mimeType) { descriptor.addField(AbstractSApplication.ICON_MIME_TYPE, mimeType); return this; } public SApplicationUpdateBuilder updateIconContent(byte[] content) { descriptor.addField(SApplicationWithIcon.ICON_CONTENT, content); return this; } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/impl/SApplicationLogBuilderFactoryImpl.java ================================================ /** * 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.business.application.model.builder.impl; import org.bonitasoft.engine.business.application.model.builder.SApplicationLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Elias Ricken de Medeiros */ public class SApplicationLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SApplicationLogBuilderFactory { public static final int APPLICATION_INDEX = 1; public static final String APPLICATION_INDEX_NAME = "numericIndex2"; @Override public String getObjectIdKey() { return APPLICATION_INDEX_NAME; } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/impl/SApplicationLogBuilderImpl.java ================================================ /** * 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.business.application.model.builder.impl; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.model.builder.SApplicationLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Elias Ricken de Medeiros */ public class SApplicationLogBuilderImpl extends CRUDELogBuilder implements SApplicationLogBuilder { @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(SApplicationLogBuilderFactoryImpl.APPLICATION_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return ApplicationService.APPLICATION; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(SApplicationLogBuilderFactoryImpl.APPLICATION_INDEX) == 0L) { throw new MissingMandatoryFieldsException( "Some mandatory fields are missing: business application identifier"); } } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/impl/SApplicationMenuLogBuilderFactoryImpl.java ================================================ /** * 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.business.application.model.builder.impl; import org.bonitasoft.engine.business.application.model.builder.SApplicationMenuLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Elias Ricken de Medeiros */ public class SApplicationMenuLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SApplicationMenuLogBuilderFactory { public static final int APPLICATION_MENU_INDEX = 1; public static final String APPLICATION_MENU_INDEX_NAME = "numericIndex2"; @Override public String getObjectIdKey() { return APPLICATION_MENU_INDEX_NAME; } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/impl/SApplicationMenuLogBuilderImpl.java ================================================ /** * 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.business.application.model.builder.impl; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.model.builder.SApplicationMenuLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Elias Ricken de Medeiros */ public class SApplicationMenuLogBuilderImpl extends CRUDELogBuilder implements SApplicationMenuLogBuilder { @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(SApplicationMenuLogBuilderFactoryImpl.APPLICATION_MENU_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return ApplicationService.APPLICATION_MENU; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(SApplicationMenuLogBuilderFactoryImpl.APPLICATION_MENU_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatoryFields are missing: application menu identifier"); } } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/impl/SApplicationPageLogBuilderFactoryImpl.java ================================================ /** * 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.business.application.model.builder.impl; import org.bonitasoft.engine.business.application.model.builder.SApplicationLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Elias Ricken de Medeiros */ public class SApplicationPageLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SApplicationLogBuilderFactory { public static final int APPLICATION_PAGE_INDEX = 1; public static final String APPLICATION_PAGE_INDEX_NAME = "numericIndex2"; @Override public String getObjectIdKey() { return APPLICATION_PAGE_INDEX_NAME; } } ================================================ FILE: services/bonita-business-application/src/main/java/org/bonitasoft/engine/business/application/model/builder/impl/SApplicationPageLogBuilderImpl.java ================================================ /** * 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.business.application.model.builder.impl; import org.bonitasoft.engine.business.application.model.builder.SApplicationPageLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Elias Ricken de Medeiros */ public class SApplicationPageLogBuilderImpl extends CRUDELogBuilder implements SApplicationPageLogBuilder { @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(SApplicationPageLogBuilderFactoryImpl.APPLICATION_PAGE_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return "APPLICATION_PAGE"; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(SApplicationPageLogBuilderFactoryImpl.APPLICATION_PAGE_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: application page identifier"); } } } ================================================ FILE: services/bonita-business-application/src/main/resources/org/bonitasoft/engine/business/application/impl/hibernate/application.queries.hbm.xml ================================================ SELECT application FROM org.bonitasoft.engine.business.application.model.SApplication AS application WHERE token = :token SELECT count(application.id) FROM org.bonitasoft.engine.business.application.model.SApplication AS application SELECT count(DISTINCT application.id) FROM org.bonitasoft.engine.business.application.model.SApplication AS application, org.bonitasoft.engine.profile.model.SProfileMember as profileMember WHERE application.profileId = profileMember.profileId AND ( profileMember.userId = :userId OR profileMember.id IN ( SELECT profileMember.id FROM org.bonitasoft.engine.profile.model.SProfileMember AS profileMember, org.bonitasoft.engine.identity.model.SUserMembership AS userMember WHERE userMember.userId = :userId AND ((profileMember.groupId = userMember.groupId AND profileMember.roleId <= 0) OR (profileMember.roleId = userMember.roleId AND profileMember.groupId <= 0) OR (profileMember.groupId = userMember.groupId AND profileMember.roleId = userMember.roleId) ) ) ) SELECT application FROM org.bonitasoft.engine.business.application.model.SApplication AS application SELECT DISTINCT application FROM org.bonitasoft.engine.business.application.model.SApplication AS application, org.bonitasoft.engine.profile.model.SProfileMember as profileMember WHERE application.profileId = profileMember.profileId AND ( profileMember.userId = :userId OR profileMember.id IN ( SELECT profileMember.id FROM org.bonitasoft.engine.profile.model.SProfileMember AS profileMember, org.bonitasoft.engine.identity.model.SUserMembership AS userMember WHERE userMember.userId = :userId AND ((profileMember.groupId = userMember.groupId AND profileMember.roleId <= 0) OR (profileMember.roleId = userMember.roleId AND profileMember.groupId <= 0) OR (profileMember.groupId = userMember.groupId AND profileMember.roleId = userMember.roleId) ) ) ) SELECT applicationPage FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage WHERE id = :id SELECT applicationPage FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage, org.bonitasoft.engine.business.application.model.SApplication as application WHERE applicationPage.applicationId = application.id AND applicationPage.token = :applicationPageToken AND application.token = :applicationToken SELECT applicationPage FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage WHERE applicationPage.applicationId = :applicationId AND applicationPage.token = :applicationPageToken SELECT applicationPage FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage, org.bonitasoft.engine.business.application.model.SApplication as application WHERE applicationPage.id = application.homePageId AND applicationPage.applicationId = application.id AND application.id = :applicationId SELECT count(applicationPage.id) FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage SELECT applicationPage FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage SELECT applicationMenu FROM org.bonitasoft.engine.business.application.model.SApplicationMenu as applicationMenu WHERE id = :id SELECT count(applicationMenu.id) FROM org.bonitasoft.engine.business.application.model.SApplicationMenu as applicationMenu SELECT applicationMenu FROM org.bonitasoft.engine.business.application.model.SApplicationMenu as applicationMenu SELECT max(applicationMenu.index) FROM org.bonitasoft.engine.business.application.model.SApplicationMenu as applicationMenu WHERE parentId is null SELECT max(applicationMenu.index) FROM org.bonitasoft.engine.business.application.model.SApplicationMenu as applicationMenu WHERE parentId = :parentId SELECT DISTINCT page.name FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage, org.bonitasoft.engine.business.application.model.SApplication as application, org.bonitasoft.engine.page.SPage as page WHERE application.profileId = :profileId AND ( application.layoutId = page.id OR application.themeId = page.id OR ( applicationPage.applicationId = application.id AND applicationPage.pageId = page.id ) ) ORDER BY page.name SELECT DISTINCT page.name FROM org.bonitasoft.engine.business.application.model.SApplicationPage as applicationPage, org.bonitasoft.engine.business.application.model.SApplication as application, org.bonitasoft.engine.page.SPage as page, org.bonitasoft.engine.profile.model.SProfile as profile WHERE profile.name = :profileName AND application.profileId = profile.id AND ( application.layoutId = page.id OR application.themeId = page.id OR ( applicationPage.applicationId = application.id AND applicationPage.pageId = page.id ) ) ORDER BY page.name ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/ApplicationServiceImplTest.java ================================================ /** * 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.business.application.impl; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.business.application.ApplicationService.APPLICATION; import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.*; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; import static org.mockito.Mockito.argThat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.impl.cleaner.ApplicationDestructor; import org.bonitasoft.engine.business.application.impl.cleaner.ApplicationMenuDestructor; import org.bonitasoft.engine.business.application.impl.cleaner.ApplicationPageDestructor; import org.bonitasoft.engine.business.application.impl.converter.MenuIndexConverter; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.bonitasoft.engine.business.application.model.SApplicationState; import org.bonitasoft.engine.business.application.model.SApplicationWithIcon; import org.bonitasoft.engine.business.application.model.builder.SApplicationMenuUpdateBuilder; import org.bonitasoft.engine.business.application.model.builder.SApplicationUpdateBuilder; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ApplicationServiceImplTest { private static final int CREATED_BY = 10; private static final String APPLICATION_TOKEN = "app"; private static final String APPLICATION_DISPLAY_NAME = "My app"; public static final long LAYOUT_ID = 15L; public static final long THEME_ID = 16L; @Mock private Recorder recorder; @Mock private ReadPersistenceService persistenceService; @Mock private QueriableLoggerService queriableLogService; @Mock private IndexManager indexManager; @Mock private MenuIndexConverter convertor; @Mock private ApplicationDestructor applicationDestructor; @Mock private ApplicationPageDestructor applicationPageDestructor; @Mock private ApplicationMenuDestructor applicationMenuDestructor; @Mock private CacheService cacheService; private SApplicationWithIcon application; private ApplicationServiceImpl applicationServiceImpl; @Before public void setUp() { applicationServiceImpl = new ApplicationServiceImpl(recorder, persistenceService, queriableLogService, indexManager, convertor, applicationDestructor, applicationPageDestructor, applicationMenuDestructor, cacheService); when(queriableLogService.isLoggable(anyString(), any(SQueriableLogSeverity.class))).thenReturn(true); application = buildApplication(APPLICATION_TOKEN, APPLICATION_DISPLAY_NAME); application.setId(10L); } private SApplicationWithIcon buildApplication(final String applicationName, final String applicationDisplayName) { final long currentDate = System.currentTimeMillis(); SApplicationWithIcon application = new SApplicationWithIcon(); application.setToken(applicationName); application.setDisplayName(applicationDisplayName); application.setVersion("1.0"); application.setCreationDate(currentDate); application.setLastUpdateDate(currentDate); application.setCreatedBy(CREATED_BY); application.setState(SApplicationState.ACTIVATED.name()); application.setLayoutId(LAYOUT_ID); application.setThemeId(THEME_ID); return application; } @Test public void createApplication_should_call_recordInsert_and_return_created_object() throws Exception { //given final InsertRecord record = new InsertRecord(application); //when final SApplicationWithIcon createdApplication = applicationServiceImpl.createApplication(application); //then assertThat(createdApplication).isEqualTo(application); verify(recorder, times(1)).recordInsert(record, APPLICATION); } @Test(expected = SObjectCreationException.class) public void createApplication_should_throw_SObjectCreationException_when_record_insert_throws_Exception() throws Exception { //given doThrow(new SRecorderException("")).when(recorder).recordInsert(any(InsertRecord.class), anyString()); //when applicationServiceImpl.createApplication(application); //then exception } @Test public void createApplication_should_throw_SObjectAlreadyExistsException_when_an_application_with_the_same_name_already_exists() throws Exception { //given SApplication app = new SApplication(); app.setId(125); given(persistenceService.selectOne(new SelectOneDescriptor("getApplicationByToken", Collections.singletonMap("name", APPLICATION_TOKEN), SApplication.class))).willReturn(app); final SApplicationWithIcon newApp = buildApplication(APPLICATION_TOKEN, APPLICATION_DISPLAY_NAME); //when try { applicationServiceImpl.createApplication(newApp); fail("Exception expected"); } catch (final SObjectAlreadyExistsException e) { //then assertThat(e.getMessage()) .isEqualTo("An application already exists with token '" + APPLICATION_TOKEN + "'."); verify(recorder, never()).recordInsert(any(InsertRecord.class), anyString()); } } @Test public void getApplication_should_return_result_of_persistence_service_selectById() throws Exception { //given SApplication app = new SApplication(); app.setId(10L); given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplication.class, 10L))) .willReturn(app); //when final SApplication retrievedApp = applicationServiceImpl.getApplication(10L); //then assertThat(retrievedApp).isEqualTo(app); } @Test public void getApplication_should_throw_SObjectNotFoundException_when_persitence_service_returns_null() throws Exception { //given final long applicationId = 10L; given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplication.class, applicationId))) .willReturn(null); //when try { applicationServiceImpl.getApplication(applicationId); fail("Exception expected"); } catch (final SObjectNotFoundException e) { //then assertThat(e.getMessage()).isEqualTo("No application found with id '" + applicationId + "'."); } } @Test public void getApplicationByToken_should_return_result_of_persistence_service_getApplicationByToken() throws Exception { //given SApplication app = new SApplication(); app.setToken("name"); given(persistenceService.selectOne(new SelectOneDescriptor("getApplicationByToken", singletonMap("name", APPLICATION_TOKEN), SApplication.class))).willReturn(app); //when SApplication retriedApplication = applicationServiceImpl.getApplicationByToken(APPLICATION_TOKEN); //then assertThat(retriedApplication).isEqualTo(app); } @Test public void deleteApplication_should_call_record_delete() throws Exception { //given final long applicationId = 10L; SApplication app = new SApplication(); app.setId(10L); given(persistenceService.selectById(argThat(s -> s.getId() == 10L))).willReturn(app); //when applicationServiceImpl.deleteApplication(applicationId); //then verify(recorder, times(1)).recordDelete(new DeleteRecord(app), APPLICATION); } @Test public void deleteApplication_should_throw_exception_when_targeting_non_editable_app() throws Exception { //given final long applicationId = 10L; SApplication app = new SApplication(); app.setDisplayName("My App"); app.setId(10L); app.setEditable(false); given(persistenceService.selectById(argThat(s -> s.getId() == 10L))).willReturn(app); //when String exceptionMessage = assertThrows("Not the right exception", SObjectModificationException.class, () -> applicationServiceImpl.deleteApplication(applicationId)).getMessage(); //then assertThat(exceptionMessage) .contains("The application 'My App' is set as non modifiable. It cannot be deleted"); } @Test public void deleteApplication_should_call_applicationDestructor() throws Exception { //given final ApplicationServiceImpl applicationService = spy(applicationServiceImpl); final long applicationId = 10L; SApplication app = new SApplication(); app.setId(10L); doReturn(app).when(applicationService).getApplication(applicationId); //when applicationService.deleteApplication(applicationId); //then verify(applicationDestructor, times(1)).onDeleteApplication(app); } @Test(expected = SObjectNotFoundException.class) public void deleteApplication_should_throw_SObjectNotFoundException_when_no_application_with_the_given_id_is_found() throws Exception { //given final long applicationId = 10L; given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplication.class, applicationId))) .willReturn(null); //when applicationServiceImpl.deleteApplication(applicationId); //then exception } @Test(expected = SObjectModificationException.class) public void deleteApplication_should_throw_SObjectModificationException_when_recorder_throws_SRecorderException() throws Exception { //given given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplication.class, 10L))) .willReturn(new SApplication()); doThrow(new SRecorderException("")).when(recorder).recordDelete(any(DeleteRecord.class), anyString()); //when applicationServiceImpl.deleteApplication(10L); //then exception } @Test public void getNumberOfApplications_should_return_the_result_of_persitenceService_getNumberOfEntities() throws Exception { //given final QueryOptions options = new QueryOptions(0, 10); final long count = 7; given(persistenceService.getNumberOfEntities(SApplication.class, options, null)).willReturn(count); //when final long nbOfApp = applicationServiceImpl.getNumberOfApplications(options); //then assertThat(nbOfApp).isEqualTo(count); } @Test public void searchApplications_should_return_the_result_of_persitenceService_searchEntity() throws Exception { //given final QueryOptions options = new QueryOptions(0, 10); final List applications = new ArrayList<>(1); applications.add(mock(SApplication.class)); given(persistenceService.searchEntity(SApplication.class, options, null)).willReturn(applications); //when final List retrievedApplications = applicationServiceImpl.searchApplications(options); //then assertThat(retrievedApplications).isEqualTo(applications); } @Test(expected = SBonitaReadException.class) public void searchApplications_should_throw_SBonitaReadException_when_persistenceSevice_throws_SBonitaReadException() throws Exception { //given final QueryOptions options = new QueryOptions(0, 10); given(applicationServiceImpl.searchApplications(options)).willThrow(new SBonitaReadException("")); //when applicationServiceImpl.searchApplications(options); //then exception } private SApplicationPage buildApplicationPage(final long applicationId, final long pageId, final String name) { return SApplicationPage.builder().applicationId(applicationId).pageId(pageId).token(name).build(); } private SApplicationPage buildApplicationPage(final long applicationPageId, final long applicationId, final long pageId, final String pageToken) { final SApplicationPage applicationPage = buildApplicationPage(applicationId, pageId, pageToken); applicationPage.setId(applicationPageId); return applicationPage; } @Test public void createApplicationPage_should_call_recordInsert_and_return_created_object() throws Exception { //given final SApplicationPage applicationPage = buildApplicationPage(15, 5, 15, "mainDashBoard"); final InsertRecord record = new InsertRecord(applicationPage); //when final SApplicationPage createdApplicationPage = applicationServiceImpl.createApplicationPage(applicationPage); //then assertThat(createdApplicationPage).isEqualTo(applicationPage); verify(recorder, times(1)).recordInsert(record, ApplicationService.APPLICATION_PAGE); } @Test(expected = SObjectCreationException.class) public void createApplicationPage_should_throw_SObjectCreationException_when_recorder_throws_SBonitaException() throws Exception { //given final SApplicationPage applicationPage = buildApplicationPage(15, 5, 15, "mainDashBoard"); doThrow(new SRecorderException("")).when(recorder).recordInsert(any(InsertRecord.class), anyString()); //when applicationServiceImpl.createApplicationPage(applicationPage); //then exception } @Test(expected = SObjectAlreadyExistsException.class) public void createApplicationPage_should_throw_SObjectAlreadyExistsException_when_an_applicationPage_with_the_same_name_in_the_same_application_exists() throws Exception { //given final SApplicationPage applicationPage = buildApplicationPage(5, 15, "mainDashBoard"); final Map inputParameters = new HashMap<>(2); inputParameters.put("applicationId", 5); inputParameters.put("applicationPageToken", "mainDashBoard"); given(persistenceService.selectOne( new SelectOneDescriptor("getApplicationPageByTokenAndApplicationId", inputParameters, SApplicationPage.class))) .willReturn(applicationPage); //when final SApplicationPage applicationPageToCreate = buildApplicationPage(7, 5, 16, "mainDashBoard"); applicationServiceImpl.createApplicationPage(applicationPageToCreate); //then exception } @Test public void getApplicationPage_should_return_result_of_persitence_service_selectById() throws Exception { //given final SApplicationPage applicationPage = buildApplicationPage(10, 20, "myPage"); given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationPage.class, 10L))).willReturn( applicationPage); //when final SApplicationPage retrievedAppPage = applicationServiceImpl.getApplicationPage(10L); //then assertThat(retrievedAppPage).isEqualTo(applicationPage); } @Test public void getApplicationPage_by_name_and_appName_should_return_result_of_persistence_service_selectOne() throws Exception { //given final SApplicationPage applicationPage = buildApplicationPage(10, 20, "myPage"); final Map inputParameters = new HashMap<>(2); inputParameters.put("applicationName", "app"); inputParameters.put("applicationPageToken", "firstPage"); given(persistenceService.selectOne(new SelectOneDescriptor( "getApplicationPageByTokenAndApplicationToken", inputParameters, SApplicationPage.class))).willReturn(applicationPage); //when final SApplicationPage retrievedAppPage = applicationServiceImpl.getApplicationPage("app", "firstPage"); //then assertThat(retrievedAppPage).isEqualTo(applicationPage); } @Test(expected = SObjectNotFoundException.class) public void getApplicationPage_by_name_and_appName_should_throw_SObjectNotFoundException_when_persitence_service_selectOne_returns_null() throws Exception { //given given(persistenceService.selectOne(ArgumentMatchers.> any())) .willReturn(null); //when applicationServiceImpl.getApplicationPage("app", "firstPage"); //then exception } @Test public void deleteApplicationPage_should_call_record_delete_with_applicationPage_identified_by_the_given_id() throws Exception { //given final ApplicationServiceImpl applicationService = spy(applicationServiceImpl); final long applicationPageId = 10L; final SApplicationPage applicationPage = buildApplicationPage(applicationPageId, 20, 30, "myPage"); doReturn(applicationPage).when(applicationService).getApplicationPage(applicationPageId); final long applicationId = 20L; //when applicationService.deleteApplicationPage(applicationPageId); //then final ArgumentCaptor deleteRecordCaptor = ArgumentCaptor.forClass(DeleteRecord.class); verify(recorder, times(1)).recordDelete(deleteRecordCaptor.capture(), eq(ApplicationService.APPLICATION_PAGE)); assertThat(deleteRecordCaptor.getValue().getEntity()).isEqualTo(applicationPage); } @Test public void deleteApplicationPage_should_call_applicationPageDestructor() throws Exception { //given final ApplicationServiceImpl applicationService = spy(applicationServiceImpl); //application page final long applicationPageId = 10L; final long applicationId = 20L; final SApplicationPage applicationPage = buildApplicationPage(applicationPageId, applicationId, 30, "myPage"); //when applicationService.deleteApplicationPage(applicationPage); //then verify(applicationPageDestructor, times(1)).onDeleteApplicationPage(applicationPage); } @Test(expected = SObjectModificationException.class) public void deleteApplicationPage_should_throw_SObjectModificationException_when_applicationPageDestructor_throws_SObjectModificationException() throws Exception { //given final ApplicationServiceImpl applicationService = spy(applicationServiceImpl); //application page final long applicationPageId = 10L; final long applicationId = 20L; final SApplicationPage applicationPage = buildApplicationPage(applicationPageId, applicationId, 30, "myPage"); doThrow(new SObjectModificationException()).when(applicationPageDestructor) .onDeleteApplicationPage(applicationPage); //when applicationService.deleteApplicationPage(applicationPage); //then exception } @Test(expected = SObjectNotFoundException.class) public void deleteApplicationPage_should_throw_SObjectNotFound_when_there_is_no_applicationPage_for_the_given_id() throws Exception { //given final long applicationPageId = 10L; given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationPage.class, applicationPageId))) .willReturn(null); //when applicationServiceImpl.deleteApplicationPage(applicationPageId); //then exception } @Test(expected = SObjectModificationException.class) public void deleteApplicationPage_should_throw_SObjectModificationException_when_recorder_throws_SRecorderException() throws Exception { //given final long applicationPageId = 10L; final SApplicationPage applicationPage = buildApplicationPage(27, 20, 30, "myPage"); given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationPage.class, applicationPageId))) .willReturn(applicationPage); doThrow(new SRecorderException("")).when(recorder).recordDelete(any(DeleteRecord.class), anyString()); //when applicationServiceImpl.deleteApplicationPage(applicationPageId); //then exception } @Test public void updateApplication_should_call_recorder_recordUpdate_and_return_updated_object() throws Exception { //given EntityUpdateDescriptor updateDescriptor = new SApplicationUpdateBuilder(0L) .updateDisplayName("new display name").done(); long applicationId = 17; given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationWithIcon.class, applicationId))) .willReturn( application); //when final SApplicationWithIcon updatedApplication = applicationServiceImpl.updateApplication(applicationId, updateDescriptor); //then final UpdateRecord updateRecord = UpdateRecord.buildSetFields(application, updateDescriptor); verify(recorder, times(1)).recordUpdate(updateRecord, APPLICATION); assertThat(updatedApplication).isEqualTo(application); } @Test public void update_application_display_name_should_throw_exception_called_on_non_editable_application() throws Exception { //given application.setEditable(false); EntityUpdateDescriptor updateDescriptor = new SApplicationUpdateBuilder(0L) .updateDisplayName("new display name").done(); long applicationId = 17; given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationWithIcon.class, applicationId))) .willReturn(application); //when String exceptionMessage = assertThrows("Not the right exception", SObjectModificationException.class, () -> applicationServiceImpl.updateApplication(applicationId, updateDescriptor)).getMessage(); assertThat(exceptionMessage) .contains("The application is provided. Only the theme, the layout, and the icon can be updated"); //cleanup application.setEditable(true); } @Test public void update_non_editable_application_should_allow_to_change_icon_theme_and_layout() throws Exception { //given application.setEditable(false); EntityUpdateDescriptor updateDescriptor = new SApplicationUpdateBuilder(0L) .updateThemeId(1L).updateLayoutId(2L).updateIconMimeType("image/jpg") .updateIconContent("toto".getBytes()).done(); long applicationId = 17; given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationWithIcon.class, applicationId))) .willReturn(application); //when applicationServiceImpl.updateApplication(applicationId, updateDescriptor); //then: verify(recorder).recordUpdate(UpdateRecord.buildSetFields(application, updateDescriptor), APPLICATION); //cleanup application.setEditable(true); } @Test(expected = SObjectModificationException.class) public void updateApplication_should_throw_SObjectModificationException_if_set_homepage_references_invalid_page() throws Exception { //given final SApplicationUpdateBuilder updateBuilder = new SApplicationUpdateBuilder(0L); final long homePageId = 150L; updateBuilder.updateHomePageId(homePageId); final EntityUpdateDescriptor updateDescriptor = updateBuilder.done(); final int applicationId = 17; final SApplicationWithIcon applicationWithIcon = mock(SApplicationWithIcon.class); doReturn(true).when(applicationWithIcon).isEditable(); given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationWithIcon.class, applicationId))) .willReturn(applicationWithIcon); given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationPage.class, homePageId))) .willReturn(null); //when applicationServiceImpl.updateApplication(applicationId, updateDescriptor); //then exception } @Test(expected = SObjectNotFoundException.class) public void updateApplication_should_throw_SObjectModificationException_when_recorder_throws_SRecorderException() throws Exception { //given final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor(); updateDescriptor.addField("name", "newName"); final int applicationId = 17; //when applicationServiceImpl.updateApplication(applicationId, updateDescriptor); //then exception } @Test(expected = SObjectAlreadyExistsException.class) public void updateApplication_should_throw_SObjectAlreadyExistsException_when_another_application_exists_with_the_same_name() throws Exception { //given final EntityUpdateDescriptor updateDescriptor = new SApplicationUpdateBuilder(0L).updateToken("newToken") .done(); SApplicationWithIcon applicationToUpdate = new SApplicationWithIcon(); applicationToUpdate.setToken("oldToken"); doReturn(applicationToUpdate).when(persistenceService).selectById(argThat(s -> s.getId() == 17)); SApplication existingApplication = new SApplication(); existingApplication.setToken("newToken"); doReturn(existingApplication).when(persistenceService) .selectOne(argThat(s -> s.getQueryName().equals("getApplicationByToken") && s.getInputParameter("token").equals("newToken"))); //when applicationServiceImpl.updateApplication(17, updateDescriptor); //then exception } @Test public void getApplicationHomePage_should_return_result_of_persitence_service_selectOne() throws Exception { //given final SApplicationPage applicationPage = buildApplicationPage(10, 20, "myPage"); final Map inputParameters = new HashMap<>(2); inputParameters.put("applicationId", 100); given(persistenceService .selectOne(new SelectOneDescriptor("getApplicationHomePage", inputParameters, SApplicationPage.class))) .willReturn(applicationPage); //when final SApplicationPage homePage = applicationServiceImpl.getApplicationHomePage(100); //then assertThat(homePage).isEqualTo(applicationPage); } @Test(expected = SObjectNotFoundException.class) public void getApplicationHomePage_should_throw_SObjectNotFoundException_when_persitence_service_selectOne_returns_null() throws Exception { //given final Map inputParameters = new HashMap<>(2); inputParameters.put("applicationId", 100); given(persistenceService .selectOne(new SelectOneDescriptor("getApplicationHomePage", inputParameters, SApplicationPage.class))) .willReturn(null); //when applicationServiceImpl.getApplicationHomePage(100); //then exception } @Test public void getNumberOfApplicationPages_should_return_the_result_of_persitenceService_getNumberOfEntities() throws Exception { //given final QueryOptions options = new QueryOptions(0, 10); final long count = 7; given(persistenceService.getNumberOfEntities(SApplicationPage.class, options, null)).willReturn(count); //when final long nbOfApp = applicationServiceImpl.getNumberOfApplicationPages(options); //then assertThat(nbOfApp).isEqualTo(count); } @Test public void searchApplicationPages_should_return_the_result_of_persitenceService_searchEntity() throws Exception { //given final QueryOptions options = new QueryOptions(0, 10); final List applicationPages = new ArrayList<>(1); applicationPages.add(mock(SApplicationPage.class)); given(persistenceService.searchEntity(SApplicationPage.class, options, null)).willReturn(applicationPages); //when final List retrievedAppPages = applicationServiceImpl.searchApplicationPages(options); //then assertThat(retrievedAppPages).isEqualTo(applicationPages); } @Test(expected = SBonitaReadException.class) public void searchApplicationPages_should_throw_SBonitaReadException_when_persistenceSevice_throws_SBonitaReadException() throws Exception { //given final QueryOptions options = new QueryOptions(0, 10); given(applicationServiceImpl.searchApplicationPages(options)).willThrow(new SBonitaReadException("")); //when applicationServiceImpl.searchApplicationPages(options); //then exception } @Test public void createApplicationMenu_should_call_recordInsert_and_return_created_object() throws Exception { //given final SApplicationMenu appMenu = buildApplicationMenu("main", 5, 1, 12); appMenu.setId(15); //when final SApplicationMenu createdAppMenu = applicationServiceImpl.createApplicationMenu(appMenu); //then assertThat(createdAppMenu).isEqualTo(appMenu); final ArgumentCaptor insertRecordCaptor = ArgumentCaptor.forClass(InsertRecord.class); verify(recorder, times(1)).recordInsert(insertRecordCaptor.capture(), eq(ApplicationService.APPLICATION_MENU)); assertThat(insertRecordCaptor.getValue().getEntity()).isEqualTo(appMenu); } private SApplicationMenu buildApplicationMenu(final String displayName, final long applicationPageId, final int index, final long applicationId) { return SApplicationMenu.builder().displayName(displayName).applicationId(applicationId) .applicationPageId(applicationPageId).index(index).build(); } @Test(expected = SObjectCreationException.class) public void createApplicationMenu_should_throw_SObjectCreationException_when_recorder_throws_Exception() throws Exception { //given doThrow(new SRecorderException("")).when(recorder).recordInsert(any(InsertRecord.class), anyString()); //when applicationServiceImpl.createApplicationMenu(buildApplicationMenu("main", 4, 1, 12)); //then exception } @Test public void updateApplicationMenu_should_call_recorder_recordUpdate_and_return_updated_object() throws Exception { //given final EntityUpdateDescriptor updateDescriptor = new SApplicationMenuUpdateBuilder() .updateDisplayName("new display name").done(); final SApplicationMenu appMenu = buildApplicationMenu("main", 5, 1, 12); appMenu.setId(17); final int applicationMenuId = 17; given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationMenu.class, applicationMenuId))) .willReturn( appMenu); //when final SApplicationMenu updatedApplicationMenu = applicationServiceImpl.updateApplicationMenu(applicationMenuId, updateDescriptor); //then final UpdateRecord updateRecord = UpdateRecord.buildSetFields(appMenu, updateDescriptor); verify(recorder, times(1)).recordUpdate(updateRecord, ApplicationService.APPLICATION_MENU); assertThat(updatedApplicationMenu).isEqualTo(appMenu); } @Test public void updateApplicationMenu_should_organize_indexes_when_field_index_is_updated() throws Exception { //given final ApplicationServiceImpl applicationService = spy(applicationServiceImpl); final int newIndexValue = 1; final int oldIndexValue = 5; final EntityUpdateDescriptor updateDescriptor = new SApplicationMenuUpdateBuilder().updateIndex(newIndexValue) .done(); final int applicationMenuId = 17; final SApplicationMenu appMenu = buildApplicationMenu("main", 5, oldIndexValue, 12); appMenu.setId(applicationMenuId); doReturn(appMenu).when(applicationService).getApplicationMenu(applicationMenuId); final MenuIndex newIndex = new MenuIndex(null, newIndexValue, 5); final MenuIndex oldIndex = new MenuIndex(null, oldIndexValue, 6); given(convertor.toMenuIndex(appMenu)).willReturn(oldIndex); given(convertor.toMenuIndex(appMenu, updateDescriptor)).willReturn(newIndex); //when applicationService.updateApplicationMenu(applicationMenuId, updateDescriptor); //then verify(indexManager, times(1)).organizeIndexesOnUpdate(oldIndex, newIndex); } @Test public void updateApplicationMenu_should_force_update_index_when_field_parent_is_updated() throws Exception { //given final ApplicationServiceImpl applicationService = spy(applicationServiceImpl); final EntityUpdateDescriptor updateDescriptor = new SApplicationMenuUpdateBuilder().updateParentId(28L).done(); final int applicationMenuId = 17; final SApplicationMenu appMenu = buildApplicationMenu("main", 5, 4, 12); appMenu.setId(applicationMenuId); doReturn(appMenu).when(applicationService).getApplicationMenu(applicationMenuId); final MenuIndex newIndex = new MenuIndex(28L, 6, 5); final MenuIndex oldIndex = new MenuIndex(null, 4, 6); given(convertor.toMenuIndex(appMenu)).willReturn(oldIndex); given(convertor.toMenuIndex(appMenu, updateDescriptor)).willReturn(newIndex); given(applicationService.getLastUsedIndex(28L)).willReturn(5); //when applicationService.updateApplicationMenu(applicationMenuId, updateDescriptor); //then final ArgumentCaptor updateRecordCaptor = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder, times(1)).recordUpdate(updateRecordCaptor.capture(), anyString()); final UpdateRecord updateRecord = updateRecordCaptor.getValue(); assertThat(updateRecord.getFields().get(SApplicationMenu.INDEX)).isEqualTo(6); } @Test public void updateApplicationMenu_should_not_organize_indexes_when_field_index_is_not_updated() throws Exception { //given final EntityUpdateDescriptor updateDescriptor = new SApplicationMenuUpdateBuilder() .updateDisplayName("new display name").done(); final SApplicationMenu appMenu = buildApplicationMenu("main", 5, 1, 12); appMenu.setId(17); final int applicationMenuId = 17; given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationMenu.class, applicationMenuId))) .willReturn( appMenu); //when applicationServiceImpl.updateApplicationMenu(applicationMenuId, updateDescriptor); //then verifyNoInteractions(indexManager); } @Test(expected = SObjectNotFoundException.class) public void updateApplicationMenu_should_throw_SObjectNotFoundException_when_recorder_returns_null() throws Exception { //given final EntityUpdateDescriptor updateDescriptor = new SApplicationMenuUpdateBuilder() .updateDisplayName("new display name").done(); final int applicationMenuId = 17; given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationMenu.class, applicationMenuId))) .willReturn( null); //when applicationServiceImpl.updateApplicationMenu(applicationMenuId, updateDescriptor); //then exception } @Test(expected = SObjectModificationException.class) public void updateApplicationMenu_should_throw_SObjectModificationException_when_recorder_throws_SRecorderException() throws Exception { //given final EntityUpdateDescriptor updateDescriptor = new SApplicationMenuUpdateBuilder() .updateDisplayName("new display name").done(); final SApplicationMenu appMenu = buildApplicationMenu("main", 5, 1, 12); appMenu.setId(17); final int applicationMenuId = 17; given(persistenceService.selectById(new SelectByIdDescriptor<>(SApplicationMenu.class, applicationMenuId))) .willReturn( appMenu); doThrow(new SRecorderException("")).when(recorder).recordUpdate(any(UpdateRecord.class), anyString()); //when applicationServiceImpl.updateApplicationMenu(applicationMenuId, updateDescriptor); //then exception } @Test public void getApplicationMenu_by_id_should_return_result_of_persitence_service() throws Exception { //given final SApplicationMenu applicationMenu = buildApplicationMenu("main", 2, 1, 12); final SelectByIdDescriptor selectDescriptor = new SelectByIdDescriptor<>( SApplicationMenu.class, 3); given(persistenceService.selectById(selectDescriptor)).willReturn(applicationMenu); //when final SApplicationMenu retrievedAppMenu = applicationServiceImpl.getApplicationMenu(3); //then assertThat(retrievedAppMenu).isEqualTo(applicationMenu); } @Test(expected = SObjectNotFoundException.class) public void getApplicationMenu_by_id_should_throw_SObjectNotFoundException_when_persistence_service_returns_null() throws Exception { //given final SelectByIdDescriptor selectDescriptor = new SelectByIdDescriptor<>( SApplicationMenu.class, 3); given(persistenceService.selectById(selectDescriptor)).willReturn(null); //when applicationServiceImpl.getApplicationMenu(3); //then exception } @Test public void deleteApplicationMenu_should_delete_application_menu_identified_for_the_given_identifier() throws Exception { //given final int applicationMenuId = 3; final SApplicationMenu applicationMenu = buildApplicationMenu("main", 2, 1, 12); applicationMenu.setId(applicationMenuId); final SelectByIdDescriptor selectDescriptor = new SelectByIdDescriptor<>( SApplicationMenu.class, applicationMenuId); given(persistenceService.selectById(selectDescriptor)).willReturn(applicationMenu); final SelectOneDescriptor descriptor = new SelectOneDescriptor<>("getLastIndexForRootMenu", Collections.emptyMap(), SApplicationMenu.class); given(persistenceService.selectOne(descriptor)).willReturn(2); //when applicationServiceImpl.deleteApplicationMenu(applicationMenuId); //then final ArgumentCaptor deleteRecordCaptor = ArgumentCaptor.forClass(DeleteRecord.class); verify(recorder, times(1)).recordDelete(deleteRecordCaptor.capture(), eq(ApplicationService.APPLICATION_MENU)); assertThat(deleteRecordCaptor.getValue().getEntity()).isEqualTo(applicationMenu); } @Test public void deleteApplicationMenu_should_call_applicationMenuDestructor() throws Exception { //given final ApplicationServiceImpl applicationService = spy(applicationServiceImpl); final int applicationMenuId = 3; final SApplicationMenu applicationMenu = buildApplicationMenu("main", 2, 1, 12); applicationMenu.setId(applicationMenuId); //when applicationService.deleteApplicationMenu(applicationMenu); //then verify(applicationMenuDestructor, times(1)).onDeleteApplicationMenu(applicationMenu); } @Test public void deleteApplicationMenu_without_parent_should_update_indexes() throws Exception { //given final int indexValue = 6; final int lastUsedIndex = 10; final int applicationId = 3; final SApplicationMenu applicationMenu = buildApplicationMenu("main", 2, indexValue, 12); applicationMenu.setId(applicationId); final SelectByIdDescriptor selectDescriptor = new SelectByIdDescriptor<>( SApplicationMenu.class, applicationId); given(persistenceService.selectById(selectDescriptor)).willReturn(applicationMenu); final SelectOneDescriptor descriptor = new SelectOneDescriptor<>("getLastIndexForRootMenu", Collections.emptyMap(), SApplicationMenu.class); given(persistenceService.selectOne(descriptor)).willReturn(lastUsedIndex); final MenuIndex menuIndex = new MenuIndex(null, indexValue, lastUsedIndex); //when applicationServiceImpl.deleteApplicationMenu(applicationId); //then verify(indexManager, times(1)).organizeIndexesOnDelete(menuIndex); } @Test(expected = SObjectNotFoundException.class) public void deleteApplicationMenu_should_throw_SObjectObjectNotFoundException_if_no_application_menu_is_found_with_the_given_id() throws Exception { //given given(persistenceService.selectById(ArgumentMatchers.> any())) .willReturn(null); //when applicationServiceImpl.deleteApplicationMenu(5); //then exception } @Test(expected = SObjectModificationException.class) public void deleteApplicationMenu_should_throw_SObjectObjectModificationException_recorder_throws_exception() throws Exception { //given final SApplicationMenu applicationMenu = buildApplicationMenu("main", 1, 1, 12); given(persistenceService.selectById(ArgumentMatchers.> any())) .willReturn(applicationMenu); final SelectOneDescriptor descriptor = new SelectOneDescriptor<>("getLastIndexForRootMenu", Collections.emptyMap(), SApplicationMenu.class); given(persistenceService.selectOne(descriptor)).willReturn(2); doThrow(new SRecorderException("")).when(recorder).recordDelete(any(DeleteRecord.class), anyString()); //when applicationServiceImpl.deleteApplicationMenu(5); //then exception } @Test public void getNumberOfApplicationMenus_should_return_the_result_of_persistenc_service() throws Exception { //given final QueryOptions options = new QueryOptions(0, 10); final long count = 7; given(persistenceService.getNumberOfEntities(SApplicationMenu.class, options, null)).willReturn(count); //when final long numberOfMenus = applicationServiceImpl.getNumberOfApplicationMenus(options); //then assertThat(numberOfMenus).isEqualTo(count); } @Test public void searchApplicationMenus_should_return_the_result_of_persistence_service() throws Exception { //given final QueryOptions options = new QueryOptions(0, 2); final List applicationMenus = new ArrayList<>(1); applicationMenus.add(mock(SApplicationMenu.class)); given(persistenceService.searchEntity(SApplicationMenu.class, options, null)).willReturn(applicationMenus); //when final List retrievedAppMenus = applicationServiceImpl.searchApplicationMenus(options); //then assertThat(retrievedAppMenus).isEqualTo(applicationMenus); } @Test(expected = SBonitaReadException.class) public void searchApplicationMenus_should_throw_SBonitaSearchException_when_persistence_service_throws_exception() throws Exception { //given final QueryOptions options = new QueryOptions(0, 2); given(persistenceService.searchEntity(SApplicationMenu.class, options, null)) .willThrow(new SBonitaReadException("")); //when applicationServiceImpl.searchApplicationMenus(options); //then exception } @Test public void getNextIndex_should_return_getLastUsedIndex_more_one() throws Exception { //given final ApplicationServiceImpl applicationService = spy(applicationServiceImpl); given(applicationService.getLastUsedIndex(4L)).willReturn(7); //when final int next = applicationService.getNextAvailableIndex(4L); //then assertThat(next).isEqualTo(8); } @Test public void executeGetLastUsedIndexQuery_should_use_persistence_service_getLastIndexForRootMenu_if_parent_is_null() throws Exception { //given final SelectOneDescriptor descriptor = new SelectOneDescriptor<>("getLastIndexForRootMenu", Collections.emptyMap(), SApplicationMenu.class); given(persistenceService.selectOne(descriptor)).willReturn(1); //when final Integer result = applicationServiceImpl.executeGetLastUsedIndexQuery(null); //then assertThat(result).isEqualTo(1); } @Test public void executeGetLastUsedIndexQuery_should_use_persistence_service_getLastIndexForChildOf_if_parent_is_not_null() throws Exception { //given final long parentId = 10; final SelectOneDescriptor descriptor = new SelectOneDescriptor<>("getLastIndexForChildOf", Collections.singletonMap( SApplicationMenu.PARENT_ID, parentId), SApplicationMenu.class); given(persistenceService.selectOne(descriptor)).willReturn(1); //when final Integer result = applicationServiceImpl.executeGetLastUsedIndexQuery(parentId); //then assertThat(result).isEqualTo(1); } @Test public void getLastUsedIndex_should_return_executeGetLastUsedIndexQuery_if_result_is_not_null() throws Exception { //given final ApplicationServiceImpl applicationService = spy(applicationServiceImpl); given(applicationService.executeGetLastUsedIndexQuery(1L)).willReturn(4); //when final int lastUsedIndex = applicationService.getLastUsedIndex(1L); //then assertThat(lastUsedIndex).isEqualTo(4); } @Test public void getLastUsedIndex_should_return_zero_executeGetLastUsedIndexQuery_if_result_is_null() throws Exception { //given final ApplicationServiceImpl applicationService = spy(applicationServiceImpl); given(applicationService.executeGetLastUsedIndexQuery(1L)).willReturn(null); //when final int lastUsedIndex = applicationService.getLastUsedIndex(1L); //then assertThat(lastUsedIndex).isEqualTo(0); } @Test public void getLastUsedIndex_should_return_zero_when_persistence_service_getLastIndexForChildOf_return_null() throws Exception { //given final SelectOneDescriptor descriptor = new SelectOneDescriptor<>("getLastIndexForRootMenu", Collections.emptyMap(), SApplicationMenu.class); given(persistenceService.selectOne(descriptor)).willReturn(null); //when final Integer lastUsedIndex = applicationServiceImpl.getLastUsedIndex(null); //then assertThat(lastUsedIndex).isEqualTo(0); } @Test public void getLastUsedIndex_for_child_menu_should_return_zero_when_persistence_service_getLastIndexForChildOf_return_null() throws Exception { //given final long parentId = 10; final SelectOneDescriptor descriptor = new SelectOneDescriptor<>("getLastIndexForChildOf", Collections.singletonMap( SApplicationMenu.PARENT_ID, parentId), SApplicationMenu.class); given(persistenceService.selectOne(descriptor)).willReturn(null); //when final int next = applicationServiceImpl.getLastUsedIndex(parentId); //then assertThat(next).isEqualTo(0); } } ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/HomePageCheckerTest.java ================================================ /** * 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.business.application.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class HomePageCheckerTest { public static final long APPLICATION_ID = 10L; public static final long APPLICATION_PAGE_ID = 2L; @Mock private ApplicationService applicationService; @InjectMocks private HomePageChecker checker; @Mock private SApplication application; @Mock private SApplicationPage page; @Before public void setUp() throws Exception { given(page.getId()).willReturn(APPLICATION_PAGE_ID); given(page.getApplicationId()).willReturn(APPLICATION_ID); given(applicationService.getApplication(APPLICATION_ID)).willReturn(application); } @Test public void isHomePage_should_return_true_if_application_home_page_id_is_equals_to_applicationPage_id() throws Exception { //given given(application.getHomePageId()).willReturn(APPLICATION_PAGE_ID); //when boolean isHomePage = checker.isHomePage(page); //then assertThat(isHomePage).isTrue(); } @Test public void isHomePage_should_return_false_if_application_home_page_id_is_different_of_applicationPage_id() throws Exception { //given given(application.getHomePageId()).willReturn(1L); //when boolean isHomePage = checker.isHomePage(page); //then assertThat(isHomePage).isFalse(); } @Test public void isHomePage_should_return_false_if_application_home_page_id_is_null() throws Exception { //given given(application.getHomePageId()).willReturn(null); //when boolean isHomePage = checker.isHomePage(page); //then assertThat(isHomePage).isFalse(); } } ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/IndexManagerTest.java ================================================ /** * 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.business.application.impl; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import java.util.Arrays; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class IndexManagerTest { @Mock private IndexUpdater indexUpdater; @Mock private MenuIndexValidator validator; @InjectMocks private IndexManager indexManager; @Test public void organizeIndexesOnDelete_should_call_decrement_indexes() throws Exception { //given MenuIndex menuIndex = new MenuIndex(1L, 2, 9); //when indexManager.organizeIndexesOnDelete(menuIndex); //then verify(indexUpdater).decrementIndexes(menuIndex.getParentId(), menuIndex.getValue() + 1, menuIndex.getLastUsedIndex()); verify(indexUpdater, never()).incrementIndexes(anyLong(), anyInt(), anyInt()); } @Test public void organizeIndexesOnUpdate_should_call_increment_index_when_move_up_and_parent_doesnt_change() throws Exception { //given MenuIndex oldIndex = new MenuIndex(1L, 6, 15); MenuIndex newIndex = new MenuIndex(1L, 2, 15); //when indexManager.organizeIndexesOnUpdate(oldIndex, newIndex); //then verify(indexUpdater).incrementIndexes(oldIndex.getParentId(), newIndex.getValue(), oldIndex.getValue() - 1); verify(indexUpdater, never()).decrementIndexes(anyLong(), anyInt(), anyInt()); } @Test public void organizeIndexesOnUpdate_should_call_decrement_index_when_move_down_and_parent_doesnt_change() throws Exception { //given MenuIndex oldIndex = new MenuIndex(1L, 1, 15); MenuIndex newIndex = new MenuIndex(1L, 5, 15); //when indexManager.organizeIndexesOnUpdate(oldIndex, newIndex); //then verify(indexUpdater).decrementIndexes(1L, oldIndex.getValue() + 1, newIndex.getValue()); verify(indexUpdater, never()).incrementIndexes(anyLong(), anyInt(), anyInt()); } @Test public void organizeIndexesOnUpdate_should_call_increment_on_new_parent_and_decrement_on_old_parent_when_parent_changes() throws Exception { MenuIndex oldIndex = new MenuIndex(1L, 2, 15); MenuIndex newIndex = new MenuIndex(2L, 6, 9); //when indexManager.organizeIndexesOnUpdate(oldIndex, newIndex); //then verify(indexUpdater).decrementIndexes(oldIndex.getParentId(), oldIndex.getValue() + 1, oldIndex.getLastUsedIndex()); verify(indexUpdater).incrementIndexes(newIndex.getParentId(), newIndex.getValue(), newIndex.getLastUsedIndex()); } @Test(expected = SObjectModificationException.class) public void organizeIndexesOnUpdate_should_throws_SObjectModificationException_when_new_menuIndex_is_invalid() throws Exception { //given MenuIndex oldIndex = new MenuIndex(1L, 1, 15); MenuIndex newIndex = new MenuIndex(1L, 5, 4); given(validator.validate(oldIndex, newIndex)).willReturn(Arrays.asList("invalid")); //when indexManager.organizeIndexesOnUpdate(oldIndex, newIndex); //then exception } } ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/IndexUpdaterTest.java ================================================ /** * 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.business.application.impl; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.business.application.model.builder.SApplicationMenuUpdateBuilder; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class IndexUpdaterTest { public static final int MAX_RESULTS = 2; @Mock private ApplicationService applicationService; @Mock private SApplicationMenu menu3; @Mock private SApplicationMenu menu4; @Mock private SApplicationMenu menu5; private IndexUpdater indexUpdater; @Before public void setUp() { indexUpdater = new IndexUpdater(applicationService, MAX_RESULTS); given(menu3.getIndex()).willReturn(3); given(menu4.getIndex()).willReturn(4); given(menu5.getIndex()).willReturn(5); } @Test public void incrementIndexes_should_increment_indexes_of_all_elements_in_the_specified_interval() throws Exception { //given List orderBy = Collections .singletonList(new OrderByOption(SApplicationMenu.class, SApplicationMenu.INDEX, OrderByType.ASC)); List filters = Arrays .asList(new FilterOption(SApplicationMenu.class, SApplicationMenu.INDEX, 3, 5), new FilterOption( SApplicationMenu.class, SApplicationMenu.PARENT_ID, 1L)); given(applicationService.searchApplicationMenus(new QueryOptions(0, MAX_RESULTS, orderBy, filters, null))) .willReturn(Arrays.asList(menu3, menu4)); given(applicationService .searchApplicationMenus(new QueryOptions(MAX_RESULTS, MAX_RESULTS, orderBy, filters, null))) .willReturn(Arrays.asList(menu5)); //when indexUpdater.incrementIndexes(1L, 3, 5); //then verify(applicationService).updateApplicationMenu(menu3, getUpdateDescriptorForIndex(4), false); verify(applicationService).updateApplicationMenu(menu4, getUpdateDescriptorForIndex(5), false); verify(applicationService).updateApplicationMenu(menu5, getUpdateDescriptorForIndex(6), false); } @Test public void incrementIndexes_should_do_nothing_when_from_is_greater_then_to() throws Exception { //when indexUpdater.incrementIndexes(1L, 4, 3); //then verify(applicationService, never()).searchApplicationMenus(any(QueryOptions.class)); verify(applicationService, never()).updateApplicationMenu(any(SApplicationMenu.class), any(EntityUpdateDescriptor.class), anyBoolean()); } private EntityUpdateDescriptor getUpdateDescriptorForIndex(int newIndex) { return new SApplicationMenuUpdateBuilder().updateIndex(newIndex).done(); } @Test public void decrementIndexes_should_decrement_indexes_of_all_elements_in_the_specified_interval() throws Exception { //given List orderBy = Collections .singletonList(new OrderByOption(SApplicationMenu.class, SApplicationMenu.INDEX, OrderByType.ASC)); List filters = Arrays .asList(new FilterOption(SApplicationMenu.class, SApplicationMenu.INDEX, 3, 5), new FilterOption( SApplicationMenu.class, SApplicationMenu.PARENT_ID, 1L)); given(applicationService.searchApplicationMenus(new QueryOptions(0, MAX_RESULTS, orderBy, filters, null))) .willReturn(Arrays.asList(menu3, menu4)); given(applicationService .searchApplicationMenus(new QueryOptions(MAX_RESULTS, MAX_RESULTS, orderBy, filters, null))) .willReturn(Arrays.asList(menu5)); //when indexUpdater.decrementIndexes(1L, 3, 5); //then verify(applicationService).updateApplicationMenu(menu3, getUpdateDescriptorForIndex(2), false); verify(applicationService).updateApplicationMenu(menu4, getUpdateDescriptorForIndex(3), false); verify(applicationService).updateApplicationMenu(menu5, getUpdateDescriptorForIndex(4), false); } } ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/MenuIndexValidatorTest.java ================================================ /** * 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.business.application.impl; import static org.assertj.core.api.Assertions.assertThat; import java.util.List; import org.junit.Test; public class MenuIndexValidatorTest { private MenuIndexValidator validator = new MenuIndexValidator(); @Test public void isValid_should_return_problem_when_new_index_is_less_than_or_equal_zero() { //given MenuIndex oldIndex = new MenuIndex(null, 2, 5); MenuIndex newIndex = new MenuIndex(null, 0, 5); //when List problems = validator.validate(oldIndex, newIndex); //then assertThat(problems) .containsExactly( "Invalid menu index: 0. It must be between 1 and the number of menu in your application having the same parent. The last valid index for parent null is 5"); } @Test public void isValid_should_return_problem_when_parent_is_same_and_new_index_is_greater_than_last_used_index() { //given MenuIndex oldIndex = new MenuIndex(null, 5, 5); MenuIndex newIndex = new MenuIndex(null, 6, 5); //when List problems = validator.validate(oldIndex, newIndex); //then assertThat(problems) .containsExactly( "Invalid menu index: 6. It must be between 1 and the number of menu in your application having the same parent. The last valid index for parent null is 5"); } @Test public void isValid_should_return_no_problems_when_parent_is_not_the_same_and_new_index_is_greater_than_last_used_index_by_one() { //given MenuIndex oldIndex = new MenuIndex(null, 5, 5); MenuIndex newIndex = new MenuIndex(2L, 4, 3); //when List problems = validator.validate(oldIndex, newIndex); //then assertThat(problems).isEmpty(); } @Test public void isValid_should_return_problem_when_parent_is_not_the_same_and_new_index_is_greater_than_last_used_index_by_more_than_one() { //given MenuIndex oldIndex = new MenuIndex(null, 5, 5); MenuIndex newIndex = new MenuIndex(2L, 5, 3); //when List problems = validator.validate(oldIndex, newIndex); //then assertThat(problems).containsExactly( "Invalid menu index: 5. It must be between 1 and the number of menu in your application having the same parent. The last valid index for parent 2 is 4"); } @Test public void isValid_should_return_true_when_new_index_is_valid() { //given MenuIndex oldIndex = new MenuIndex(null, 5, 5); MenuIndex newIndex = new MenuIndex(null, 4, 5); //when List problems = validator.validate(oldIndex, newIndex); //then assertThat(problems).isEmpty(); } } ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationDestructorTest.java ================================================ /** * 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.business.application.impl.cleaner; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import org.bonitasoft.engine.business.application.impl.ApplicationServiceImpl; import org.bonitasoft.engine.business.application.impl.filter.ApplicationRelatedMenusFilterBuilder; import org.bonitasoft.engine.business.application.impl.filter.SelectRange; import org.bonitasoft.engine.business.application.model.SApplication; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ApplicationDestructorTest { public static final long APPLICATION_ID = 5L; @Mock private ApplicationMenuCleaner applicationMenuCleaner; @InjectMocks private ApplicationDestructor applicationDestructor; @Test public void onDeleteApplication_should_call_applicationMenuCleaner() throws Exception { //when SApplication application = new SApplication(); application.setId(APPLICATION_ID); applicationDestructor.onDeleteApplication(application); //then verify(applicationMenuCleaner, times(1)).deleteRelatedApplicationMenus(new ApplicationRelatedMenusFilterBuilder( new SelectRange(0, ApplicationServiceImpl.MAX_RESULTS), APPLICATION_ID)); } } ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationMenuCleanerTest.java ================================================ /** * 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.business.application.impl.cleaner; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; import java.util.Arrays; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.impl.filter.FilterBuilder; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.persistence.QueryOptions; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ApplicationMenuCleanerTest { @Mock private ApplicationService applicationService; @Mock private FilterBuilder filterBuilder; @Mock private QueryOptions options; private static int MAX_RESULTS = 2; @InjectMocks private ApplicationMenuCleaner cleaner; @Before public void setUp() { given(options.getNumberOfResults()).willReturn(MAX_RESULTS); } @Test public void deleteApplicationPage_should_delete_related_applicationMenus() throws Exception { //given SApplicationMenu menu1 = mock(SApplicationMenu.class); SApplicationMenu menu2 = mock(SApplicationMenu.class); SApplicationMenu menu3 = mock(SApplicationMenu.class); given(filterBuilder.buildQueryOptions()).willReturn(options); given(applicationService.searchApplicationMenus(options)).willReturn(Arrays.asList(menu1, menu2)) .willReturn(Arrays.asList(menu3)); //when cleaner.deleteRelatedApplicationMenus(filterBuilder); //then verify(applicationService, times(1)).deleteApplicationMenu(menu1); verify(applicationService, times(1)).deleteApplicationMenu(menu2); verify(applicationService, times(1)).deleteApplicationMenu(menu3); } } ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationMenuDestructorTest.java ================================================ /** * 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.business.application.impl.cleaner; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; import org.bonitasoft.engine.business.application.impl.ApplicationServiceImpl; import org.bonitasoft.engine.business.application.impl.filter.ChildrenMenusFilterBuilder; import org.bonitasoft.engine.business.application.impl.filter.SelectRange; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ApplicationMenuDestructorTest { @Mock private ApplicationMenuCleaner cleaner; @InjectMocks private ApplicationMenuDestructor destructor; @Test public void onDeleteApplicationMenu_should_clean_children_menus() throws Exception { //given long applicationMenuId = 3L; SApplicationMenu menu = mock(SApplicationMenu.class); given(menu.getId()).willReturn(applicationMenuId); //when destructor.onDeleteApplicationMenu(menu); //then verify(cleaner, times(1)).deleteRelatedApplicationMenus(new ChildrenMenusFilterBuilder( new SelectRange(0, ApplicationServiceImpl.MAX_RESULTS), applicationMenuId)); } } ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/cleaner/ApplicationPageDestructorTest.java ================================================ /** * 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.business.application.impl.cleaner; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.business.application.impl.ApplicationServiceImpl; import org.bonitasoft.engine.business.application.impl.HomePageChecker; import org.bonitasoft.engine.business.application.impl.filter.ApplicationPageRelatedMenusFilterBuilder; import org.bonitasoft.engine.business.application.impl.filter.SelectRange; import org.bonitasoft.engine.business.application.model.SApplicationPage; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ApplicationPageDestructorTest { public static final long APPLICATION_PAGE_ID = 2L; @Mock private ApplicationMenuCleaner applicationMenuCleaner; @Mock private HomePageChecker homePageChecker; @InjectMocks private ApplicationPageDestructor destructor; @Test public void onDeleteApplicationPage_should_call_applicationMenuCleaner() throws Exception { //given SApplicationPage applicationPage = mock(SApplicationPage.class); given(applicationPage.getId()).willReturn(APPLICATION_PAGE_ID); //when destructor.onDeleteApplicationPage(applicationPage); //then verify(applicationMenuCleaner, times(1)).deleteRelatedApplicationMenus( new ApplicationPageRelatedMenusFilterBuilder(new SelectRange(0, ApplicationServiceImpl.MAX_RESULTS), APPLICATION_PAGE_ID)); } @Test public void onDeleteApplicationPage_should_throw_SObjectModificationException_when_homePageChecker_returns_true() throws Exception { //given SApplicationPage applicationPage = mock(SApplicationPage.class); given(applicationPage.getId()).willReturn(APPLICATION_PAGE_ID); given(homePageChecker.isHomePage(applicationPage)).willReturn(true); try { //when destructor.onDeleteApplicationPage(applicationPage); Assertions.fail("Exception expected"); } catch (SObjectModificationException e) { //then assertThat(e.getMessage()).isEqualTo("The application page with id '" + APPLICATION_PAGE_ID + "' cannot be deleted because it is set as the application home page"); } } } ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/converter/MenuIndexConverterTest.java ================================================ /** * 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.business.application.impl.converter; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.impl.MenuIndex; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.business.application.model.builder.SApplicationMenuUpdateBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class MenuIndexConverterTest { @Mock private ApplicationService applicationService; @InjectMocks private MenuIndexConverter convertor; @Test public void toMenuIndex_should_return_a_MenuIndex_based_on_ApplicationMenu_and_set_lastUsedIndex() throws Exception { //given SApplicationMenu appMenu = new SApplicationMenu("my menu", 1, null, 2); appMenu.setParentId(20L); given(applicationService.getLastUsedIndex(appMenu.getParentId())).willReturn(11); //when MenuIndex menuIndex = convertor.toMenuIndex(appMenu); //then assertThat(menuIndex).isNotNull(); assertThat(menuIndex.getValue()).isEqualTo(2); assertThat(menuIndex.getParentId()).isEqualTo(20L); assertThat(menuIndex.getLastUsedIndex()).isEqualTo(11); } @Test public void toMenuIndex_with_updateDescriptor_should_reuse_app_menu_parentId_when_parent_doesnt_change() throws Exception { //given SApplicationMenu appMenu = new SApplicationMenu("my menu", 1, null, 2); appMenu.setParentId(20L); given(applicationService.getLastUsedIndex(appMenu.getParentId())).willReturn(11); //when MenuIndex menuIndex = convertor.toMenuIndex(appMenu, new SApplicationMenuUpdateBuilder().updateIndex(5).done()); //then assertThat(menuIndex).isNotNull(); assertThat(menuIndex.getValue()).isEqualTo(5); assertThat(menuIndex.getParentId()).isEqualTo(20L); assertThat(menuIndex.getLastUsedIndex()).isEqualTo(11); } @Test public void toMenuIndex_with_updateDescriptor_should_use_new_parent_when_parent_changes() throws Exception { //given SApplicationMenu appMenu = new SApplicationMenu("my menu", 1, null, 2); appMenu.setParentId(20L); given(applicationService.getLastUsedIndex(7L)).willReturn(11); //when MenuIndex menuIndex = convertor.toMenuIndex(appMenu, new SApplicationMenuUpdateBuilder().updateIndex(5).updateParentId(7L).done()); //then assertThat(menuIndex).isNotNull(); assertThat(menuIndex.getValue()).isEqualTo(5); assertThat(menuIndex.getParentId()).isEqualTo(7L); assertThat(menuIndex.getLastUsedIndex()).isEqualTo(11); } @Test public void toMenuIndex_with_updateDescriptor_should_reuse_app_menu_index_when_index_and_parent_dont_change() throws Exception { //given SApplicationMenu appMenu = new SApplicationMenu("my menu", 1, null, 2); appMenu.setParentId(20L); given(applicationService.getLastUsedIndex(4L)).willReturn(11); //when MenuIndex menuIndex = convertor.toMenuIndex(appMenu, new SApplicationMenuUpdateBuilder().updateParentId(4L).done()); //then assertThat(menuIndex).isNotNull(); assertThat(menuIndex.getValue()).isEqualTo(2); assertThat(menuIndex.getParentId()).isEqualTo(4L); assertThat(menuIndex.getLastUsedIndex()).isEqualTo(11); } } ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/filter/ApplicationPageRelatedMenusFilterBuilderTest.java ================================================ /** * 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.business.application.impl.filter; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.junit.Test; public class ApplicationPageRelatedMenusFilterBuilderTest { public static final int START_INDEX = 0; public static final int MAX_RESULTS = 10; @Test public void buildQueryOptions_should_filter_on_applicationPageId() { //given long applicationPageId = 2L; ApplicationPageRelatedMenusFilterBuilder builder = new ApplicationPageRelatedMenusFilterBuilder( new SelectRange(START_INDEX, MAX_RESULTS), applicationPageId); //when QueryOptions options = builder.buildQueryOptions(); //then assertThat(options).isNotNull(); assertThat(options.getFromIndex()).isEqualTo(START_INDEX); assertThat(options.getNumberOfResults()).isEqualTo(MAX_RESULTS); assertThat(options.getOrderByOptions()) .containsExactly(new OrderByOption(SApplicationMenu.class, SApplicationMenu.ID, OrderByType.ASC)); assertThat(options.getFilters()).containsExactly( new FilterOption(SApplicationMenu.class, SApplicationMenu.APPLICATION_PAGE_ID, applicationPageId)); } } ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/filter/ApplicationRelatedMenusFilterBuilderTest.java ================================================ /** * 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.business.application.impl.filter; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.junit.Test; public class ApplicationRelatedMenusFilterBuilderTest { public static final int START_INDEX = 0; public static final int MAX_RESULTS = 10; @Test public void buildQueryOptions_should_filter_on_applicationId() { //given long applicationId = 1; ApplicationRelatedMenusFilterBuilder builder = new ApplicationRelatedMenusFilterBuilder( new SelectRange(START_INDEX, MAX_RESULTS), applicationId); //when QueryOptions options = builder.buildQueryOptions(); //then assertThat(options).isNotNull(); assertThat(options.getFromIndex()).isEqualTo(START_INDEX); assertThat(options.getNumberOfResults()).isEqualTo(MAX_RESULTS); assertThat(options.getOrderByOptions()) .containsExactly(new OrderByOption(SApplicationMenu.class, SApplicationMenu.ID, OrderByType.ASC)); FilterOption appFilter = new FilterOption(SApplicationMenu.class, SApplicationMenu.APPLICAITON_ID, applicationId); FilterOption parentFilter = new FilterOption(SApplicationMenu.class, SApplicationMenu.PARENT_ID, null); assertThat(options.getFilters()).containsExactly(appFilter, parentFilter); } } ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/filter/ChildrenMenusFilterBuilderTest.java ================================================ /** * 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.business.application.impl.filter; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.business.application.model.SApplicationMenu; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.junit.Test; public class ChildrenMenusFilterBuilderTest { public static final int START_INDEX = 0; public static final int MAX_RESULTS = 10; @Test public void build_query_options_should_filter_on_parent_id() { //given long parentId = 4L; ChildrenMenusFilterBuilder builder = new ChildrenMenusFilterBuilder(new SelectRange(START_INDEX, MAX_RESULTS), parentId); //when QueryOptions options = builder.buildQueryOptions(); //then assertThat(options).isNotNull(); assertThat(options.getFromIndex()).isEqualTo(START_INDEX); assertThat(options.getNumberOfResults()).isEqualTo(MAX_RESULTS); assertThat(options.getOrderByOptions()) .containsExactly(new OrderByOption(SApplicationMenu.class, SApplicationMenu.ID, OrderByType.ASC)); assertThat(options.getFilters()) .containsExactly(new FilterOption(SApplicationMenu.class, SApplicationMenu.PARENT_ID, parentId)); } } ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/impl/filter/SelectRangeTest.java ================================================ /** * 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.business.application.impl.filter; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class SelectRangeTest { @Test public void getStartIndex_should_return_startIndex_used_in_constructor() { //given SelectRange range = new SelectRange(5, 10); //then assertThat(range.getStartIndex()).isEqualTo(5); } @Test public void getMaxResults_should_return_maxResults_used_in_constructor() { //given SelectRange range = new SelectRange(5, 10); //then assertThat(range.getMaxResults()).isEqualTo(10); } } ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/model/SApplicationWithIconTest.java ================================================ /** * 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.business.application.model; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class SApplicationWithIconTest { @Test public void constructor_and_setter_are_ok() { //given long creationDate = System.currentTimeMillis(); String state = SApplicationState.ACTIVATED.name(); long createdBy = 1L; long homePageId = 5L; long profileId = 10L; long updatedBy = 2L; long layoutId = 20L; long themeId = 21L; //when SApplicationWithIcon application = new SApplicationWithIcon("token", "Name to display", "1.0", creationDate, createdBy, state); application.setDescription("This is my application"); application.setLayoutId(layoutId); application.setThemeId(themeId); application.setHomePageId(homePageId); application.setIconPath("/icon.jpg"); application.setLastUpdateDate(creationDate + 1); application.setProfileId(profileId); application.setUpdatedBy(updatedBy); //then assertThat(application.getToken()).isEqualTo("token"); assertThat(application.getDisplayName()).isEqualTo("Name to display"); assertThat(application.getVersion()).isEqualTo("1.0"); assertThat(application.getCreationDate()).isEqualTo(creationDate); assertThat(application.getCreatedBy()).isEqualTo(createdBy); assertThat(application.getState()).isEqualTo(state); assertThat(application.getDescription()).isEqualTo("This is my application"); assertThat(application.getHomePageId()).isEqualTo(homePageId); assertThat(application.getIconPath()).isEqualTo("/icon.jpg"); assertThat(application.getLastUpdateDate()).isEqualTo(creationDate + 1); assertThat(application.getProfileId()).isEqualTo(profileId); assertThat(application.getUpdatedBy()).isEqualTo(updatedBy); assertThat(application.getLayoutId()).isEqualTo(layoutId); assertThat(application.getThemeId()).isEqualTo(themeId);; } } ================================================ FILE: services/bonita-business-application/src/test/java/org/bonitasoft/engine/business/application/model/builder/SApplicationUpdateBuilderTest.java ================================================ /** * 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.business.application.model.builder; import static org.assertj.core.api.Assertions.assertThat; import java.util.Map; import org.bonitasoft.engine.business.application.model.AbstractSApplication; import org.bonitasoft.engine.business.application.model.SApplicationState; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.junit.Test; public class SApplicationUpdateBuilderTest { @Test public void constructorShouldUpdateUpdatedByAndLastUpdateDateFields() { //given long homePageId = 4L; long updaterUserId = 17L; long profileId = 20L; long layoutId = 25L; long themeId = 26L; //when SApplicationUpdateBuilder builder = new SApplicationUpdateBuilder(updaterUserId); builder.updateDescription("new desc"); builder.updateHomePageId(homePageId); builder.updateDisplayName("new display name"); builder.updateIconPath("/icon.jpg"); builder.updateProfileId(profileId); builder.updateState(SApplicationState.DEACTIVATED.name()); builder.updateToken("newToken"); builder.updateVersion("2.0"); builder.updateLayoutId(layoutId); builder.updateThemeId(themeId); //then final EntityUpdateDescriptor desc = builder.done(); final Map fields = desc.getFields(); assertThat(fields).hasSize(12); assertThat(fields.get(AbstractSApplication.UPDATED_BY)).isEqualTo(updaterUserId); assertThat(fields.get(AbstractSApplication.LAST_UPDATE_DATE)).isNotNull(); assertThat(fields.get(AbstractSApplication.DESCRIPTION)).isEqualTo("new desc"); assertThat(fields.get(AbstractSApplication.HOME_PAGE_ID)).isEqualTo(homePageId); assertThat(fields.get(AbstractSApplication.DISPLAY_NAME)).isEqualTo("new display name"); assertThat(fields.get(AbstractSApplication.ICON_PATH)).isEqualTo("/icon.jpg"); assertThat(fields.get(AbstractSApplication.PROFILE_ID)).isEqualTo(profileId); assertThat(fields.get(AbstractSApplication.STATE)).isEqualTo(SApplicationState.DEACTIVATED.name()); assertThat(fields.get(AbstractSApplication.TOKEN)).isEqualTo("newToken"); assertThat(fields.get(AbstractSApplication.VERSION)).isEqualTo("2.0"); assertThat(fields.get(AbstractSApplication.LAYOUT_ID)).isEqualTo(layoutId); assertThat(fields.get(AbstractSApplication.THEME_ID)).isEqualTo(themeId); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/build.gradle ================================================ dependencies { api project(':services:bonita-commons') api libs.javassist testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.logback compileOnly project(':bpm:bonita-common') testImplementation project(':bpm:bonita-common') } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/BusinessDataModelRepository.java ================================================ /** * 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.business.data; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; /** * @author Colin PUY */ public interface BusinessDataModelRepository { /** * Deploys a Business Data Model / repository * * @param bdmArchive * the Business Data Model, as a jar containing the Business Object classes to deploy. * @param userId the ID of the user installing the BDM, typically tech admin (id=-1) * @return the version of the BDM just deployed. * @throws SBusinessDataRepositoryDeploymentException * if a deployment exception occurs. */ String install(byte[] bdmArchive, long userId) throws SBusinessDataRepositoryDeploymentException, InvalidBusinessDataModelException; /** * Undeploy Business Data Model * * @throws SBusinessDataRepositoryException if error occurs during undeployement */ void uninstall() throws SBusinessDataRepositoryException; boolean isBDMDeployed(); /** * Retrieve the client side BDM generated zip. * This zip contains jars with BDM Pojos and DAOs * * @return the zip content * @throws SBusinessDataRepositoryException */ byte[] getClientBDMZip() throws SBusinessDataRepositoryException; void dropAndUninstall() throws SBusinessDataRepositoryException; /** * Returns the currently deployed BDM version, or null if no BDM is deployed. * * @return the currently deployed BDM version, or null if no BDM is deployed. * @throws SBusinessDataRepositoryException * if the BDM cannot be retrieved. */ String getInstalledBDMVersion() throws SBusinessDataRepositoryException; /** * Returns the currently deployed Business Object Data Model, or null if no BDM is deployed. * * @return the currently deployed Business Object Data Model, or null if no BDM is deployed. * @throws SBusinessDataRepositoryException * if the BDM cannot be retrieved. */ BusinessObjectModel getBusinessObjectModel() throws SBusinessDataRepositoryException; /** * Determine if the given BDM archive is equivalent to the currently deployed BDM. * * @param bdmArchive * the Business Data Model, as a zip containing the Business Object Model. * @return true If the given BDM archive is the same as the one already deployed. False otherwise. * @throws InvalidBusinessDataModelException if the given BDM archive is invalid * @throws SBusinessDataRepositoryDeploymentException if the server jar generation of the given BDM fails */ boolean isDeployed(byte[] bdmArchive) throws InvalidBusinessDataModelException, SBusinessDataRepositoryDeploymentException; } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/BusinessDataRepository.java ================================================ /** * 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.business.data; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.commons.TenantLifecycleService; /** * The BusinessDataRepository service allows to manage Business Data operations. It includes deploy / undeploy of a * Business Data Model, search / find / create * / update of Business Data entity objects. * * @author Matthieu Chaffotte * @author Emmanuel Duchastenier * @see Entity */ public interface BusinessDataRepository extends TenantLifecycleService { /** * Finds an Entity that is defined in a deployed Business Data Model. * * @param entityClass the class of the entity to search for. * @param primaryKey the primary key to search by. * @return the found entity, if any. * @throws SBusinessDataNotFoundException if the Business Data could not be found with the provided primary key. */ T findById(Class entityClass, Long primaryKey) throws SBusinessDataNotFoundException; /** * Finds entities that is defined in a deployed Business Data Model. If a primary key does not match an existing * entity no exception is thrown and nothing * is added in the list. * * @param entityClass the class of the entity to search for. * @param primaryKeys the primary keys. * @return the list of found entities */ List findByIds(Class entityClass, List primaryKeys); /** * Finds (well-loaded) entities that is defined in a deployed Business Data Model. If a primary key does not match * an existing entity no exception is thrown * and nothing is added in the list. * * @param entityClass the class of the entity to search for. * @param primaryKeys the primary keys. * @return the list of found entities */ List findByIdentifiers(Class entityClass, List primaryKeys); /** * Finds an Entity that is defined in a deployed Business Data Model, through JPQL query. * * @param resultClass the class of the entity to search for. * @param jpqlQuery the JPQL query string to search the entity. * @param parameters the parameters needed to execute the query. * @return the found entity, if any. * @throws SBusinessDataNotFoundException if the Business Data could not be found with the provided primary key. * @throws NonUniqueResultException if more than one result was found. */ T find(Class resultClass, String jpqlQuery, Map parameters) throws NonUniqueResultException; List findList(Class resultClass, String jpqlQuery, Map parameters, int startIndex, int maxResults); T findByNamedQuery(String queryName, Class resultClass, Map parameters) throws NonUniqueResultException; List findListByNamedQuery(String queryName, Class resultClass, Map parameters, int startIndex, int maxResults); /** * Saves or updates an entity in the Business Data Repository. *

      * This operation also inserts or updates a data retention tracking record in the Bonita DB * via {@code DataRetentionBdmTrackingRepository} when a BDM entity is created or updated. * * @param entity the entity to save / update. */ void persist(Entity entity); /** * Removes an entity from the Business Data Repository. *

      * This operation also deletes the associated data retention tracking record in the Bonita DB * via {@code DataRetentionBdmTrackingService} when a BDM entity is removed. * * @param entity the entity to remove. */ void remove(Entity entity); /** * Removes the entity with the given persistence ID. Also deletes the associated data * retention tracking record in the Bonita DB. *

      * Fires a {@code BUSINESS_DATA_DELETED} AOP event on the removed entity, like * {@link #remove(Entity)}. * * @param entityClass the class of the entity to remove * @param persistenceId the persistence ID of the entity to remove * @return the removed entity * @throws SBusinessDataNotFoundException if no entity exists with the given ID */ Entity removeById(Class entityClass, long persistenceId) throws SBusinessDataNotFoundException; /** * Reconnect the given entity with the persistence unit. *

      * This operation also inserts or updates a data retention tracking record in the Bonita DB * via {@code DataRetentionBdmTrackingRepository} when a BDM entity is created or updated. * * @param entity the entity to reconnect. * @return the connected entity. */ Entity merge(Entity entity); /** * Retrieves the Set of known Entity class names in this Business Data Repository. * * @return the Set of known Entity class names, as qualified class names. */ Set getEntityClassNames(); /** * Unwraps the Entity if necessary. * * @param wrapped the potential wrapped entity * @return the unwrapped entity */ Entity unwrap(final Entity wrapped); } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/BusinessDataService.java ================================================ /** * 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.business.data; import java.io.Serializable; import java.util.List; import java.util.Map; /** * @author Matthieu Chaffotte */ public interface BusinessDataService { Object callJavaOperation(Object businessObject, Object valueToSetObjectWith, String methodName, String parameterType) throws SBusinessDataNotFoundException, SBusinessDataRepositoryException; boolean isBusinessData(Object valueToSetObjectWith); Serializable getJsonEntity(String entityClassName, Long identifier, String businessDataURIPattern) throws SBusinessDataNotFoundException, SBusinessDataRepositoryException; Serializable getJsonChildEntity(String entityClassName, Long identifier, String childName, String businessDataURIPattern) throws SBusinessDataNotFoundException, SBusinessDataRepositoryException; Serializable getJsonQueryEntities(String entityClassName, String queryName, Map queryParameters, Integer startIndex, Integer maxResults, String businessDataURIPattern) throws SBusinessDataRepositoryException; Serializable getJsonEntities(String entityClassName, List identifiers, String businessDataURIPattern) throws SBusinessDataRepositoryException; } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/DataRetentionBdmTrackingService.java ================================================ /** * Copyright (C) 2026 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.business.data; /** * Service that manages data retention tracking records for BDM entity instances. *

      * Each tracking record stores the creation and last-modification timestamps of a * BDM object instance. These timestamps are consumed by the data retention service * to determine whether a BDM object has exceeded its configured retention period. *

      * This interface lives in the API module so that it can be injected by modules * (e.g. {@code bonita-process-engine}) that do not depend on {@code bonita-persistence}. * The implementation delegates to {@code DataRetentionBdmTrackingRepository} in the * impl module, wrapping persistence exceptions into {@link SDataRetentionBdmTrackingException}. */ public interface DataRetentionBdmTrackingService { /** * Persists a new tracking record. * * @param dataId the persistence ID of the tracked BDM object instance * @param dataClassname the fully qualified class name of the BDM object type * @throws SDataRetentionBdmTrackingException if the insert fails */ void create(long dataId, String dataClassname) throws SDataRetentionBdmTrackingException; /** * Upserts the {@code last_modified_at} timestamp for a BDM entity in the * data retention tracking table. *

      If a tracking record already exists for the given {@code dataId} and * {@code dataClassname}, its {@code lastModifiedAt} is updated to the current time. * Otherwise, a new tracking record is created. * * @param dataId the persistence ID of the BDM entity * @param dataClassname the fully qualified Java class name of the BDM entity * @throws SDataRetentionBdmTrackingException if the upsert fails */ void upsert(long dataId, String dataClassname) throws SDataRetentionBdmTrackingException; /** * Updates the {@code last_modified_at} column of an existing tracking record. * * @param id the ID of the tracking record to update * @throws SDataRetentionBdmTrackingException if the update fails */ void updateLastModifiedDate(long id) throws SDataRetentionBdmTrackingException; /** * Deletes the tracking record for a specific BDM entity instance. * If no tracking record exists (e.g. the entity was created before the tracking * feature was deployed), this method does nothing. * * @param dataId the persistence ID of the BDM entity * @param dataClassname the fully qualified Java class name of the BDM entity * @throws SDataRetentionBdmTrackingException if the delete fails */ void delete(long dataId, String dataClassname) throws SDataRetentionBdmTrackingException; /** * Deletes ALL tracking records. * * @throws SDataRetentionBdmTrackingException if the delete operation fails */ void deleteAll() throws SDataRetentionBdmTrackingException; } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/JsonBusinessDataSerializer.java ================================================ /** * 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.business.data; import java.util.List; import org.bonitasoft.engine.bdm.Entity; public interface JsonBusinessDataSerializer { String EMPTY_OBJECT = "{}"; /** * Returns whether standard JSON shape is enabled for BDM serialization. * Standard shape returns: * - Scalar/count queries as {"value": n} instead of [n] * - Single-entity queries as {...} instead of [{...}] * * @return true if standard shape is enabled, false for legacy array format */ boolean isStandardShapeEnabled(); String serializeEntity(Entity entity, String businessDataURIPattern) throws SBusinessDataRepositorySerializationException; String serializeEntities(List entities, String businessDataURIPattern) throws SBusinessDataRepositorySerializationException; /** * @deprecated Use {@link #serializeScalarResult(List, String, boolean)} instead. * This method is kept for backward compatibility but will be removed in future versions. */ @Deprecated(forRemoval = true) String serializeCountResult(List list, String entityClassName) throws SBusinessDataRepositorySerializationException; /** * Serialize scalar query results (Long, Double, Float, Integer). * * @param list the list containing the scalar result (typically single element) * @param entityClassName the fully qualified name of the entity class * @param useStandardShape if true, returns {"value": 10} format; if false, returns [10] format * @return JSON serialized scalar result, e.g., "[10]" or "{\"value\": 10}" depending on format * @throws SBusinessDataRepositorySerializationException if an error occurs during serialization */ String serializeScalarResult(List list, String entityClassName, boolean useStandardShape) throws SBusinessDataRepositorySerializationException; /** * Serialize entity query results handling both single and multiple entity returns. * Standard shape behavior: * - Query defined to return single entity: {...} (object) or {} if empty * - Query defined to return List (multiple results): [{...}] (array) even if only 1 result * Legacy shape behavior (backward compatible): * - All queries return arrays: [{...}] even for single entity queries * * @param entities the list of entities to serialize * @param businessDataURIPattern the URI pattern for generating entity links * @param useStandardShape if true, uses standard shape rules; if false, always returns array * @param queryReturnsMultipleResults if true, the query is designed to return a List (always array output) * @return JSON serialized entity result * @throws SBusinessDataRepositorySerializationException if an error occurs during serialization */ String serializeEntityQueryResult(List entities, String businessDataURIPattern, boolean useStandardShape, boolean queryReturnsMultipleResults) throws SBusinessDataRepositorySerializationException; } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/NonUniqueResultException.java ================================================ /** * 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.business.data; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class NonUniqueResultException extends SBonitaException { private static final long serialVersionUID = 7573132495695445017L; public NonUniqueResultException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/SBusinessDataCrudOperationException.java ================================================ /** * Copyright (C) 2025 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.business.data; /** * Exception indicating an error occurred during CRUD (Create, Read, Update, Delete) operations on business data. * This exception is a specific type of {@link SBusinessDataRepositoryException} and is used to signal issues * related to the manipulation of business data records in a repository. * * @author Emmanuel Duchastenier */ public class SBusinessDataCrudOperationException extends SBusinessDataRepositoryException { public SBusinessDataCrudOperationException(String message) { super(message); } public SBusinessDataCrudOperationException(Throwable cause) { super(cause); } public SBusinessDataCrudOperationException(String message, Throwable cause) { super(message, cause); } public BusinessDataCrudOperationException convertToClientException() { return new BusinessDataCrudOperationException(getMessage(), getCause()); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/SBusinessDataNotFoundException.java ================================================ /** * 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.business.data; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SBusinessDataNotFoundException extends SBonitaException { private static final long serialVersionUID = -4470717601583219790L; public SBusinessDataNotFoundException(final String message) { super(message); } public SBusinessDataNotFoundException(final String message, final Throwable cause) { super(message, cause); } public BusinessDataNotFoundException convertToClientException() { return new BusinessDataNotFoundException(getMessage(), getCause()); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/SBusinessDataRepositoryDeploymentException.java ================================================ /** * 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.business.data; /** * @author Matthieu Chaffotte */ public class SBusinessDataRepositoryDeploymentException extends SBusinessDataRepositoryException { private static final long serialVersionUID = 3729004984895038663L; public SBusinessDataRepositoryDeploymentException(final String message) { super(message); } public SBusinessDataRepositoryDeploymentException(final Throwable cause) { super(cause); } public SBusinessDataRepositoryDeploymentException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/SBusinessDataRepositoryException.java ================================================ /** * 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.business.data; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SBusinessDataRepositoryException extends SBonitaException { private static final long serialVersionUID = -2517115818095061435L; public SBusinessDataRepositoryException(final String message) { super(message); } public SBusinessDataRepositoryException(final Throwable cause) { super(cause); } public SBusinessDataRepositoryException(final String message, final Throwable cause) { super(message, cause); } public BusinessDataRepositoryException convertToClientException() { return new BusinessDataRepositoryException(getMessage(), getCause()); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/SBusinessDataRepositorySerializationException.java ================================================ /** * 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.business.data; public class SBusinessDataRepositorySerializationException extends SBusinessDataRepositoryException { public SBusinessDataRepositorySerializationException(final String message) { super(message); } public SBusinessDataRepositorySerializationException(final Throwable cause) { super(cause); } public SBusinessDataRepositorySerializationException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/SDataRetentionBdmTrackingException.java ================================================ /** * Copyright (C) 2026 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.business.data; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Checked exception thrown by {@link DataRetentionBdmTrackingService} when a * tracking operation (create, upsert, update, delete) fails. *

      Wraps lower-level persistence exceptions so that callers in modules that * do not depend on {@code bonita-persistence} can still handle tracking errors. */ public class SDataRetentionBdmTrackingException extends SBonitaException { public SDataRetentionBdmTrackingException(String message) { super(message); } public SDataRetentionBdmTrackingException(String message, Throwable cause) { super(message, cause); } public SDataRetentionBdmTrackingException(Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/SchemaManager.java ================================================ /** * 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.business.data; import java.util.List; import java.util.Set; /** * @author Pablo Alonso de Linaje García */ public interface SchemaManager { List drop(Set managedClasses); List update(Set managedClasses); } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/proxy/Capitalizer.java ================================================ /** * 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.business.data.proxy; public class Capitalizer { public static String capitalize(final String str) { if (str == null || str.isEmpty()) { return str; } return Character.toUpperCase(str.charAt(0)) + str.substring(1); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/proxy/EntityGetter.java ================================================ /** * 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.business.data.proxy; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.util.List; import org.bonitasoft.engine.bdm.model.field.Field; /** * Wrapper over entity getter method * * @author Colin Puy */ public class EntityGetter { private final Method method; public EntityGetter(Method method) { checkIsGetter(method); this.method = method; } private void checkIsGetter(Method method) { String methodName = method.getName(); if (!methodName.startsWith("get") || methodName.length() <= 3) { throw new IllegalArgumentException(methodName + " is not a valid getter name."); } } public String getSourceEntityName() { return method.getDeclaringClass().getSimpleName(); } public String getCapitalizedFieldName() { return method.getName().substring(3); } public String getReturnTypeClassName() { return getTargetEntityClass().getName(); } public String getAssociatedNamedQuery() { String targetEntityName = getTargetEntityClass().getSimpleName(); return targetEntityName + ".find" + getCapitalizedFieldName() + "By" + getSourceEntityName() + Capitalizer.capitalize(Field.PERSISTENCE_ID); } public boolean returnsList() { Class returnTypeClass = method.getReturnType(); return List.class.isAssignableFrom(returnTypeClass); } public Class getTargetEntityClass() { if (returnsList()) { final ParameterizedType listType = (ParameterizedType) method.getGenericReturnType(); final Class type = (Class) listType.getActualTypeArguments()[0]; return type; } return method.getReturnType(); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/proxy/ServerLazyLoader.java ================================================ /** * 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.business.data.proxy; import java.io.Serializable; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.bdm.model.field.Field; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.NonUniqueResultException; public class ServerLazyLoader { private BusinessDataRepository businessDataRepository; public ServerLazyLoader(BusinessDataRepository bdBusinessDataRepository) { this.businessDataRepository = bdBusinessDataRepository; } public Object load(final Method method, final long persistenceId) { EntityGetter getter = new EntityGetter(method); final Map queryParameters = new HashMap<>(); queryParameters.put(Field.PERSISTENCE_ID, persistenceId); if (getter.returnsList()) { return businessDataRepository.findListByNamedQuery(getter.getAssociatedNamedQuery(), (Class) getter.getTargetEntityClass(), queryParameters, 0, Integer.MAX_VALUE); } try { return businessDataRepository.findByNamedQuery(getter.getAssociatedNamedQuery(), (Class) getter.getTargetEntityClass(), queryParameters); } catch (NonUniqueResultException e) { // cannot appear throw new RuntimeException(); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/main/java/org/bonitasoft/engine/business/data/proxy/ServerProxyfier.java ================================================ /** * 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.business.data.proxy; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.Proxy; import javassist.util.proxy.ProxyFactory; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.bdm.lazy.LazyLoaded; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Colin Puy * @author Laurent Leseigneur */ public class ServerProxyfier { private static final Logger LOGGER = LoggerFactory.getLogger(ServerProxyfier.class); private final ServerLazyLoader lazyLoader; public ServerProxyfier(final ServerLazyLoader lazyLoader) { this.lazyLoader = lazyLoader; } public static boolean isLazyMethodProxyfied(final Entity e) { return ProxyFactory.isProxyClass(e.getClass()) && ProxyFactory.getHandler((Proxy) e) instanceof LazyMethodHandler; } @SuppressWarnings("unchecked") public T proxify(final T entity) { if (entity == null || isLazyMethodProxyfied(entity)) { return entity; } return (T) proxifyEntity(entity); } private Entity proxifyEntity(final Entity entity) { if (entity == null) { return null; } final ProxyFactory factory = new ProxyFactory(); Class classForProxy = entity.getClass(); //It's not possible to create a Proxy on a Proxy //Here Entity can already be an Hibernate Proxy if (ProxyFactory.isProxyClass(classForProxy)) { classForProxy = classForProxy.getSuperclass(); } factory.setSuperclass(classForProxy); factory.setFilter((Method m) -> true); try { return (Entity) factory.create(new Class[0], new Object[0], new LazyMethodHandler(entity, lazyLoader)); } catch (final Exception e) { throw new RuntimeException("Error when proxifying object", e); } } // This methods seems unused but is called by reflection, I think: @SuppressWarnings({ "unchecked" }) public List proxify(final List entities) { if (entities == null) { return null; } return (List) proxifyEntities((List) entities); } private List proxifyEntities(final List entities) { final List proxies = new ArrayList<>(); for (final Entity entity : entities) { proxies.add(proxifyEntity(entity)); } return proxies; } /** * Handler that lazy load values for lazy loading methods that hasn't been loaded */ public class LazyMethodHandler implements MethodHandler { private final ServerLazyLoader lazyLoader; private final Entity entity; public LazyMethodHandler(final Entity entity, final ServerLazyLoader lazyLoader) { this.entity = entity; this.lazyLoader = lazyLoader; } public Entity getEntity() { return entity; } @Override public Object invoke(final Object self, final Method thisMethod, final Method proceed, final Object[] args) throws Throwable { Object invocationResult; if (isMethodGetterOnLazyLoadedField(thisMethod)) { invocationResult = lazyLoader.load(thisMethod, entity.getPersistenceId()); } else { invocationResult = thisMethod.invoke(entity, args); } return proxifyIfNeeded(invocationResult); } private boolean isMethodGetterOnLazyLoadedField(final Method thisMethod) { return isGetter(thisMethod) && thisMethod.isAnnotationPresent(LazyLoaded.class); } @SuppressWarnings("unchecked") private Object proxifyIfNeeded(final Object invocationResult) { if (isAnEntity(invocationResult)) { return proxifyEntity((Entity) invocationResult); } if (isAListOfEntities(invocationResult)) { return proxifyEntities((List) invocationResult); } return invocationResult; } private boolean isAListOfEntities(final Object invocationResult) { if (invocationResult instanceof List list) { return !list.isEmpty() && list.get(0) instanceof Entity; } return false; } private boolean isAnEntity(final Object invocationResult) { return invocationResult instanceof Entity; } private boolean isGetter(final Method method) { return method.getName().startsWith("get"); } } public static Entity unProxifyIfNeeded(final Entity entity) { Set visitedEntities = new HashSet<>(); return unProxifyIfNeeded(entity, visitedEntities); } private static Entity unProxifyIfNeeded(Entity entity, Set alreadyUnproxyfiedEntities) { Entity detachedEntity = entity; if (entity != null && isLazyMethodProxyfied(entity)) { detachedEntity = ((LazyMethodHandler) ProxyFactory.getHandler((Proxy) entity)).getEntity(); } if (detachedEntity == null) { return null; } boolean wasNotAlreadyUnproxified = alreadyUnproxyfiedEntities.add(detachedEntity); // If we just unproxified this entity, we must also unproxify its related Entity fields: if (wasNotAlreadyUnproxified) { final Field[] declaredFields = detachedEntity.getClass().getDeclaredFields(); for (final Field field : declaredFields) { try { if (Entity.class.isAssignableFrom(field.getType())) { field.setAccessible(true); field.set(detachedEntity, unProxifyIfNeeded((Entity) field.get(detachedEntity), alreadyUnproxyfiedEntities)); } else if (List.class.isAssignableFrom(field.getType())) { field.setAccessible(true); final List list = (List) field.get(detachedEntity); if (list != null && !list.isEmpty() && isThereAnyNonNullEntityInTheList(list)) { final List realEntities = ((List) list).stream() .map(element -> unProxifyIfNeeded(element, alreadyUnproxyfiedEntities)) .collect(Collectors.toList()); list.clear(); list.addAll(realEntities); field.set(detachedEntity, list); } } } catch (IllegalAccessException e) { LOGGER.error(String.format("Illegal access to field '%s' of Entity '%s'", field, detachedEntity)); } } } return detachedEntity; } private static boolean isThereAnyNonNullEntityInTheList(List list) { return list.stream().anyMatch(e -> e != null && Entity.class.isAssignableFrom(e.getClass())); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/A.java ================================================ /** * 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.business.data.proxy; import org.bonitasoft.engine.bdm.Entity; public class A implements Entity { private Long persistenceId; private Long persistenceVersion; private B b; private A a; @Override public Long getPersistenceId() { return persistenceId; } @Override public Long getPersistenceVersion() { return persistenceVersion; } public B getB() { return b; } public void setB(B b) { this.b = b; } public A getA() { return a; } public void setA(A a) { this.a = a; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/Address.java ================================================ /** * 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.business.data.proxy; import org.bonitasoft.engine.bdm.Entity; public class Address implements Entity { private static final long serialVersionUID = 326174219656126594L; @Override public Long getPersistenceId() { return 0L; } @Override public Long getPersistenceVersion() { return 1L; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/B.java ================================================ /** * 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.business.data.proxy; import org.bonitasoft.engine.bdm.Entity; public class B implements Entity { private Long persistenceId; private Long persistenceVersion; private A a; @Override public Long getPersistenceId() { return persistenceId; } @Override public Long getPersistenceVersion() { return persistenceVersion; } public A getA() { return a; } public void setA(A a) { this.a = a; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/CapitalizerTest.java ================================================ /** * 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.business.data.proxy; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class CapitalizerTest { @Test public void testCapitalizer() { assertThat(Capitalizer.capitalize("name")).isEqualTo("Name"); } @Test public void capitalizer_null_value() { assertThat(Capitalizer.capitalize(null)).isEqualTo(null); } @Test public void capitalizer_empty_value() { assertThat(Capitalizer.capitalize("")).isEqualTo(""); } @Test public void capitalizer_1_char_value() { assertThat(Capitalizer.capitalize("a")).isEqualTo("A"); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/Employee.java ================================================ /** * 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.business.data.proxy; import java.util.List; import org.bonitasoft.engine.bdm.Entity; public class Employee implements Entity { private static final long serialVersionUID = 4877386043381866907L; private Long persistenceId; private Long persistenceVersion; private String firstName; private String lastName; private Address address; private List

      addresses; public Employee() { super(); } public Employee(final Long id, final Long persistenceVersion, final String firstName, final String lastName) { super(); persistenceId = id; this.persistenceVersion = persistenceVersion; this.firstName = firstName; this.lastName = lastName; } @Override public Long getPersistenceId() { return persistenceId; } public void setPersistenceId(final Long id) { persistenceId = id; } @Override public Long getPersistenceVersion() { return persistenceVersion; } public String getFirstName() { return firstName; } public void setFirstName(final String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(final String lastName) { this.lastName = lastName; } public Address getAddress() { return address; } public void setAddress(final Address address) { this.address = address; } public List
      getAddresses() { return addresses; } public void setAddresses(final List
      addresses) { this.addresses = addresses; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/EntityGetterTest.java ================================================ /** * 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.business.data.proxy; import static org.assertj.core.api.Assertions.assertThat; import java.lang.reflect.Method; import org.junit.Test; public class EntityGetterTest { @Test(expected = IllegalArgumentException.class) public void can_only_be_applicable_to_getters() throws Exception { //given Method setter = Employee.class.getMethod("setLastName", String.class); //when then new EntityGetter(setter); } @Test public void should_be_able_to_retrieve_source_entity_name() throws Exception { //given Method methodOfEmployee = Employee.class.getMethod("getAddress"); //when String sourceEntityName = new EntityGetter(methodOfEmployee).getSourceEntityName(); //then assertThat(sourceEntityName).isEqualTo(Employee.class.getSimpleName()); } @Test public void should_be_able_to_retrieve_target_entity_class_even_if_method_return_a_list() throws Exception { //given Method methodReturningListOfAdresses = Employee.class.getMethod("getAddresses"); //when final EntityGetter entityGetter = new EntityGetter(methodReturningListOfAdresses); //then assertThat(entityGetter.getTargetEntityClass()).isEqualTo(Address.class); assertThat(entityGetter.getReturnTypeClassName()) .isEqualTo("org.bonitasoft.engine.business.data.proxy.Address"); assertThat(entityGetter.returnsList()).isTrue(); } @Test public void should_be_able_to_retrieve_capitalized_field_name() throws Exception { //given Method getAdresses = Employee.class.getMethod("getAddresses"); //when String sourceEntityName = new EntityGetter(getAdresses).getCapitalizedFieldName(); //then assertThat(sourceEntityName).isEqualTo("Addresses"); } @Test public void should_return_entity_class_name_if_getter_return_a_unique_object() throws Exception { //given Method uniqueObjectGetter = Employee.class.getMethod("getAddress"); //when final EntityGetter entityGetter = new EntityGetter(uniqueObjectGetter); //then assertThat(entityGetter.getReturnTypeClassName()).isEqualTo(Address.class.getName()); assertThat(entityGetter.returnsList()).isFalse(); } @Test public void should_be_able_to_get_associated_named_query() throws Exception { //given Method getManager = Employee.class.getMethod("getAddresses"); //when String namedQuery = new EntityGetter(getManager).getAssociatedNamedQuery(); //then assertThat(namedQuery).isEqualTo("Address.findAddressesByEmployeePersistenceId"); } @Test public void should_be_able_to_determine_if_getter_return_a_list() throws Exception { //given Method multipleObjectGetter = Employee.class.getMethod("getAddresses"); //when final EntityGetter entityGetter = new EntityGetter(multipleObjectGetter); //then assertThat(entityGetter.returnsList()).isTrue(); assertThat(entityGetter.getReturnTypeClassName()).isEqualTo(entityGetter.getTargetEntityClass().getName()); } @Test public void should_be_able_to_determine_if_getter_do_not_return_a_list() throws Exception { //given Method multipleObjectGetter = Employee.class.getMethod("getLastName"); //when final EntityGetter entityGetter = new EntityGetter(multipleObjectGetter); //then assertThat(entityGetter.returnsList()).isFalse(); assertThat(entityGetter.getReturnTypeClassName()).isEqualTo(entityGetter.getTargetEntityClass().getName()); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/PersonEntity.java ================================================ /** * 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.business.data.proxy; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.bdm.lazy.LazyLoaded; /** * @author Romain Bioteau * @author Laurent Leseigneur */ public class PersonEntity implements Entity { public PersonEntity() { } /* * (non-Javadoc) * @see org.bonitasoft.engine.bdm.Entity#getPersistenceId() */ @Override public Long getPersistenceId() { return 1L; } /* * (non-Javadoc) * @see org.bonitasoft.engine.bdm.Entity#getPersistenceVersion() */ @Override public Long getPersistenceVersion() { return null; } @LazyLoaded public String getWithLazyLoadedAnnotation() { return "getWithLazyLoadedAnnotation"; } public String getWithoutLazyLoadedAnnotation() { return "getWithoutLazyLoadedAnnotation"; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/ServerLazyLoaderTest.java ================================================ /** * 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.business.data.proxy; import static org.mockito.Mockito.*; import java.io.Serializable; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.NonUniqueResultException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ServerLazyLoaderTest { public class Addresses implements Serializable { private static final long serialVersionUID = 1L; public String getCity() { return "city"; } } public class Employee { public Employee() { } public List getAddresses() { return new ArrayList<>(); } public String getName() { return "name"; } } @Mock private BusinessDataRepository businessDataRepository; private ServerLazyLoader serverLazyLoader; final long persistenceId = 22L; Employee employee = new Employee(); @Before public void setUp() throws Exception { serverLazyLoader = spy(new ServerLazyLoader(businessDataRepository)); } @Test public void should_load_list_of_objects() throws Exception { //given final Method method = employee.getClass().getMethod("getAddresses"); //when serverLazyLoader.load(method, persistenceId); final String queryName = "Addresses.findAddressesByEmployeePersistenceId"; final Class resultClass = Addresses.class; final Map parameters = new HashMap<>(); parameters.put("persistenceId", persistenceId); final int startIndex = 0; final int maxResults = Integer.MAX_VALUE; //then verify(businessDataRepository).findListByNamedQuery(queryName, resultClass, parameters, startIndex, maxResults); verify(businessDataRepository, never()).findByNamedQuery(queryName, resultClass, parameters); } @Test public void should_load_single_object() throws Exception { //given final String queryName = "String.findNameByEmployeePersistenceId"; final Class resultClass = String.class; final Map parameters = new HashMap<>(); parameters.put("persistenceId", persistenceId); final int startIndex = 0; final int maxResults = Integer.MAX_VALUE; final Method method = employee.getClass().getMethod("getName"); //when serverLazyLoader.load(method, persistenceId); //then verify(businessDataRepository, never()).findListByNamedQuery(queryName, resultClass, parameters, startIndex, maxResults); verify(businessDataRepository).findByNamedQuery(queryName, resultClass, parameters); } @Test(expected = RuntimeException.class) public void should_load_single_object_throw_exception() throws Exception { final String queryName = "String.findNameByEmployeePersistenceId"; final Class resultClass = String.class; final Map parameters = new HashMap<>(); parameters.put("persistenceId", persistenceId); final Method method = employee.getClass().getMethod("getName"); //given doThrow(NonUniqueResultException.class).when(businessDataRepository).findByNamedQuery(queryName, resultClass, parameters); //when serverLazyLoader.load(method, persistenceId); //then exception } } ================================================ FILE: services/bonita-business-data/bonita-business-data-api/src/test/java/org/bonitasoft/engine/business/data/proxy/ServerProxyfierTest.java ================================================ /** * 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.business.data.proxy; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.*; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyFactory; import javassist.util.proxy.ProxyObject; import org.bonitasoft.engine.bdm.Entity; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Romain Bioteau * @author Laurent Leseigneur */ @RunWith(MockitoJUnitRunner.class) public class ServerProxyfierTest { @Mock private ServerLazyLoader lazyLoader; private ServerProxyfier serverProxyfier; @Before public void setUp() throws Exception { serverProxyfier = new ServerProxyfier(lazyLoader); } @Test public void should_proxify_an_entity() { final PersonEntity proxy = serverProxyfier.proxify(new PersonEntity()); assertThat(proxy).isInstanceOf(ProxyObject.class); assertThat(proxy.getClass().getSuperclass()).isEqualTo(PersonEntity.class); } @Test public void should_not_reproxify_a_server_proxy() { final PersonEntity originalProxy = serverProxyfier.proxify(new PersonEntity()); final PersonEntity proxy = serverProxyfier.proxify(originalProxy); assertThat(proxy).isSameAs(originalProxy); } @Test public void should_reproxify_an_hibernate_proxy() throws Exception { final ProxyFactory factory = new ProxyFactory(); factory.setSuperclass(PersonEntity.class); final Entity aProxy = (Entity) factory.create(new Class[0], new Object[0], new MethodHandler() { @Override public Object invoke(final Object self, final Method thisMethod, final Method proceed, final Object[] args) { return null; } }); final PersonEntity proxy = (PersonEntity) serverProxyfier.proxify(aProxy); assertThat(proxy).isNotSameAs(aProxy); } @Test public void should_call_on_lazy_loaded_getter_use_lazyLoader() throws Exception { //given final PersonEntity personEntity = new PersonEntity(); final Method method = PersonEntity.class.getMethod("getWithLazyLoadedAnnotation"); doReturn("lazyResult").when(lazyLoader).load(any(Method.class), anyLong()); //when final PersonEntity proxy = serverProxyfier.proxify(personEntity); final String withLazyLoadedAnnotation = proxy.getWithLazyLoadedAnnotation(); // verify(lazyLoader).load(method, personEntity.getPersistenceId()); assertThat(withLazyLoadedAnnotation).isEqualTo("lazyResult"); } @Test public void should_not_call_lazyLoader() throws Exception { //given final PersonEntity personEntity = new PersonEntity(); final Method method = PersonEntity.class.getMethod("getWithoutLazyLoadedAnnotation"); //when final PersonEntity proxy = serverProxyfier.proxify(personEntity); final String withLazyLoadedAnnotation = proxy.getWithoutLazyLoadedAnnotation(); // verify(lazyLoader, never()).load(method, personEntity.getPersistenceId()); assertThat(withLazyLoadedAnnotation).isEqualTo("getWithoutLazyLoadedAnnotation"); } @Test public void unProxy_should_not_remove_a_proxy_on_a_null_entity() { final Entity entity = ServerProxyfier.unProxifyIfNeeded(null); assertThat(entity).isNull(); } @Test public void unProxy_should_not_remove_a_proxy_on_an_entity() { final PersonEntity proxy = new PersonEntity(); final Entity entity = ServerProxyfier.unProxifyIfNeeded(proxy); assertThat(ServerProxyfier.isLazyMethodProxyfied(entity)).isFalse(); } @Test public void unProxy_should_remove_a_proxy_on_an_entity() { final PersonEntity proxy = serverProxyfier.proxify(new PersonEntity()); final Entity entity = ServerProxyfier.unProxifyIfNeeded(proxy); assertThat(ServerProxyfier.isLazyMethodProxyfied(entity)).isFalse(); } @Test public void should_unproxify_object_with_cycle() { A a = new A(); B b = new B(); A aProxy = serverProxyfier.proxify(a); B bProxy = serverProxyfier.proxify(b); b.setA(aProxy); a.setB(bProxy); A unproxyfied = (A) ServerProxyfier.unProxifyIfNeeded(aProxy); assertThat(ServerProxyfier.isLazyMethodProxyfied(unproxyfied)).isFalse(); assertThat(ServerProxyfier.isLazyMethodProxyfied(unproxyfied.getB())).isFalse(); } @Test public void should_unproxify_object_directReferenceToItself() { A a = new A(); A aProxy = serverProxyfier.proxify(a); a.setA(aProxy); A unproxyfied = (A) ServerProxyfier.unProxifyIfNeeded(aProxy); assertThat(ServerProxyfier.isLazyMethodProxyfied(unproxyfied)).isFalse(); assertThat(ServerProxyfier.isLazyMethodProxyfied(unproxyfied.getA())).isFalse(); } @Test public void unProxy_should_remove_a_proxy_on_an_ref_attribute_of_an_entity() { final Address proxy = serverProxyfier.proxify(new Address()); final Employee employee = new Employee(10L, 45L, "John", "Doe"); employee.setAddress(proxy); final Employee entity = (Employee) ServerProxyfier.unProxifyIfNeeded(employee); assertThat(ServerProxyfier.isLazyMethodProxyfied(entity.getAddress())).isFalse(); } @Test public void unProxy_should_remove_a_proxy_on_a_list_attribute_of_an_entity() { final List
      addresses = new ArrayList<>(); addresses.add(new Address()); addresses.add(serverProxyfier.proxify(new Address())); final Employee employee = new Employee(10L, 45L, "John", "Doe"); employee.setAddresses(addresses); final Employee entity = (Employee) ServerProxyfier.unProxifyIfNeeded(employee); for (final Address address : entity.getAddresses()) { assertThat(ServerProxyfier.isLazyMethodProxyfied(address)).isFalse(); } } @Test public void unproxify_should_not_fail_on_a_first_null_object_in_a_list_attribute_of_an_entity() { final List
      addresses = new ArrayList<>(); addresses.add(null); final Address address = new Address(); addresses.add(serverProxyfier.proxify(address)); final Employee employee = new Employee(11L, 42L, "Ol", "Delaf"); employee.setAddresses(addresses); final Employee entity = (Employee) ServerProxyfier.unProxifyIfNeeded(employee); assertThat(entity.getAddresses()).hasSize(2).containsExactly(null, address); } @Test public void unproxify_should_not_fail_on_a_later_null_object_in_a_list_attribute_of_an_entity() { final List
      addresses = new ArrayList<>(); final Address address = new Address(); addresses.add(serverProxyfier.proxify(address)); addresses.add(null); final Employee employee = new Employee(11L, 42L, "Ol", "Delaf"); employee.setAddresses(addresses); final Employee entity = (Employee) ServerProxyfier.unProxifyIfNeeded(employee); assertThat(entity.getAddresses()).hasSize(2).containsExactly(address, null); } @Test public void unproxify_should_not_fail_when_all_objects_in_a_list_attribute_of_an_entity_are_null() { final List
      addresses = new ArrayList<>(); addresses.add(null); addresses.add(null); final Employee employee = new Employee(11L, 42L, "Ol", "Delaf"); employee.setAddresses(addresses); final Employee entity = (Employee) ServerProxyfier.unProxifyIfNeeded(employee); assertThat(entity.getAddresses()).hasSize(2).containsExactly(null, null); } @Test public void should_return_null_when_proxifying_a_null_entity() { final Entity proxy = serverProxyfier.proxify((Entity) null); assertThat(proxy).isNull(); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/README.md ================================================ Java files included in this module are added to business data generated code for client side, compiled and included into dao-client.jar They are mainly used for lazy mode and client proxies. ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/build.gradle ================================================ import org.bonitasoft.engine.gradle.PomUtils plugins { id 'maven-publish' } dependencies { api project(':bpm:bonita-common') api libs.javassist compileOnly project(':bpm:bonita-client') testImplementation project(':bpm:bonita-client') testImplementation libs.mockitoCore testImplementation libs.assertj testImplementation(libs.hibernateCore) { exclude(module: 'jboss-transaction-api_1.2_spec') } testImplementation libs.lombok testAnnotationProcessor libs.lombok } group = 'org.bonitasoft.engine.data' tasks.register("sourcesJar", Jar) { from sourceSets.main.allJava archiveClassifier = 'sources' } tasks.register("javadocJar", Jar) { from javadoc archiveClassifier = 'javadoc' } publishing { publications { mavenJava(MavenPublication) { from project.components.java artifact project.sourcesJar artifact project.javadocJar pom { pom -> name = "Bonita Business Data Client Resources" description = "Bonita Business Data Client Resources is a library used in BDM client DAO implementation" PomUtils.pomCommunityPublication(pom) } } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/main/java/org/bonitasoft/engine/bdm/dao/client/resources/BusinessObjectDeserializer.java ================================================ /** * 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.bdm.dao.client.resources; import java.io.IOException; import java.lang.reflect.Type; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.util.List; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.type.TypeFactory; import org.bonitasoft.engine.bdm.serialization.CustomLocalDateDeserializer; import org.bonitasoft.engine.bdm.serialization.CustomLocalDateTimeDeserializer; import org.bonitasoft.engine.bdm.serialization.CustomOffsetDateTimeDeserializer; /** * @author Romain Bioteau */ public class BusinessObjectDeserializer { private final ObjectMapper mapper; private final TypeFactory typeFactory; public BusinessObjectDeserializer() { mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); final SimpleModule customModule = new SimpleModule(); customModule.addDeserializer(LocalDate.class, new CustomLocalDateDeserializer()); customModule.addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer()); customModule.addDeserializer(OffsetDateTime.class, new CustomOffsetDateTimeDeserializer()); mapper.registerModule(customModule); typeFactory = mapper.getTypeFactory(); } @SuppressWarnings("unchecked") public T deserialize(final byte[] serializedResult, final Class targetType) throws IOException { return (T) mapper.readValue(serializedResult, createJavaType(targetType)); } @SuppressWarnings("unchecked") public List deserializeList(final byte[] serializedResult, final Class targetType) throws IOException { return (List) mapper.readValue(serializedResult, createListJavaType(targetType)); } private JavaType createListJavaType(final Type elementType) { return typeFactory.constructCollectionType(List.class, createJavaType(elementType)); } private JavaType createJavaType(final Type elementType) { return typeFactory.constructType(elementType); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/main/java/org/bonitasoft/engine/bdm/dao/client/resources/proxy/LazyLoader.java ================================================ /** * 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.bdm.dao.client.resources.proxy; import java.io.Serializable; import java.lang.reflect.Method; import java.util.Map; import org.bonitasoft.engine.api.CommandAPI; import org.bonitasoft.engine.api.TenantAPIAccessor; import org.bonitasoft.engine.bdm.dao.client.resources.BusinessObjectDeserializer; import org.bonitasoft.engine.bdm.dao.client.resources.utils.BDMQueryCommandParameters; import org.bonitasoft.engine.bdm.dao.client.resources.utils.EntityGetter; import org.bonitasoft.engine.exception.BonitaHomeNotSetException; import org.bonitasoft.engine.exception.ServerAPIException; import org.bonitasoft.engine.exception.UnknownAPITypeException; import org.bonitasoft.engine.session.APISession; public class LazyLoader { private final APISession apiSession; private final BusinessObjectDeserializer deserializer; public LazyLoader(final APISession apiSession) { if (apiSession == null) { throw new IllegalArgumentException("apiSession cannot be null"); } this.apiSession = apiSession; deserializer = new BusinessObjectDeserializer(); } public Object load(final Method method, final long persistenceId) { try { EntityGetter getter = new EntityGetter(method); final Map commandParameters = BDMQueryCommandParameters .createCommandParameters(getter, persistenceId); final byte[] serializedResult = (byte[]) getCommandAPI().execute("executeBDMQuery", commandParameters); if (getter.returnsList()) { return deserializer.deserializeList(serializedResult, getter.getTargetEntityClass()); } return deserializer.deserialize(serializedResult, getter.getTargetEntityClass()); } catch (final Exception e) { throw new IllegalArgumentException(e); } } /** * protected for testing */ protected CommandAPI getCommandAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException { return TenantAPIAccessor.getCommandAPI(apiSession); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/main/java/org/bonitasoft/engine/bdm/dao/client/resources/proxy/Proxyfier.java ================================================ /** * 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.bdm.dao.client.resources.proxy; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import javassist.util.proxy.MethodFilter; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyFactory; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.bdm.lazy.LazyLoaded; /** * @author Colin Puy */ public class Proxyfier { private final LazyLoader lazyLoader; public Proxyfier(final LazyLoader lazyLoader) { this.lazyLoader = lazyLoader; } @SuppressWarnings("unchecked") public T proxify(final T entity) { return (T) proxifyEntity(entity); } private Entity proxifyEntity(final Entity entity) { if (entity == null) { return null; } final ProxyFactory factory = new ProxyFactory(); factory.setSuperclass(entity.getClass()); factory.setFilter(new AllMethodFilter()); try { return (Entity) factory.create(new Class[0], new Object[0], new LazyMethodHandler(entity, lazyLoader)); } catch (final Exception e) { throw new RuntimeException("Error when proxifying object", e); } } @SuppressWarnings("unchecked") public List proxify(final List entities) { if (entities == null) { return null; } return (List) proxifyEntities((List) entities); } private List proxifyEntities(final List entities) { final List proxies = new ArrayList(); for (final Entity entity : entities) { proxies.add(proxifyEntity(entity)); } return proxies; } /** * Handler that lazy load values for lazy loading methods that hasn't been loaded */ private class LazyMethodHandler implements MethodHandler { private final LazyLoader lazyloader; private final List alreadyLoaded = new ArrayList(); private final Entity entity; public LazyMethodHandler(final Entity entity, final LazyLoader lazyloader) { this.entity = entity; this.lazyloader = lazyloader; } @Override public Object invoke(final Object self, final Method thisMethod, final Method proceed, final Object[] args) throws Throwable { Object invocationResult = thisMethod.invoke(entity, args); if (isGetterOrSetter(thisMethod)) { if (isGetter(thisMethod) && shouldBeLoaded(thisMethod, invocationResult)) { invocationResult = lazyloader.load(thisMethod, entity.getPersistenceId()); callSetterOnEntity(invocationResult, thisMethod); } alreadyLoaded.add(toFieldName(thisMethod.getName())); } return proxifyIfNeeded(invocationResult); } private void callSetterOnEntity(final Object invocationResult, final Method getter) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (invocationResult != null) { final Method setter = getAssociatedSetter(invocationResult, getter); setter.invoke(entity, invocationResult); } } private Method getAssociatedSetter(final Object invocationResult, final Method getter) throws NoSuchMethodException, SecurityException { return entity.getClass().getMethod(getter.getName().replaceFirst("^get", "set"), getter.getReturnType()); } @SuppressWarnings("unchecked") private Object proxifyIfNeeded(final Object invocationResult) { if (isAnEntity(invocationResult)) { return proxifyEntity((Entity) invocationResult); } if (isAListOfEntities(invocationResult)) { return proxifyEntities((List) invocationResult); } return invocationResult; } private boolean isAListOfEntities(final Object invocationResult) { if (invocationResult instanceof List list) { if (!list.isEmpty() && list.get(0) instanceof Entity) { return true; } } return false; } private boolean isAnEntity(final Object invocationResult) { return invocationResult instanceof Entity; } private boolean shouldBeLoaded(final Method thisMethod, final Object notLazyLoaded) { return thisMethod.isAnnotationPresent(LazyLoaded.class) && !alreadyLoaded.contains(toFieldName(thisMethod.getName())); } private boolean isGetterOrSetter(final Method method) { return isGetter(method) || method.getName().startsWith("set") && method.getName().length() > 3; } private boolean isGetter(final Method method) { return method.getName().startsWith("get"); } private String toFieldName(final String methodName) { if (methodName.startsWith("get") || methodName.startsWith("set") && methodName.length() > 3) { return methodName.substring(3).toLowerCase(); } throw new IllegalArgumentException(methodName + " is not a valid getter or setter name."); } } /** * Filter all methods */ private class AllMethodFilter implements MethodFilter { @Override public boolean isHandled(final Method m) { return true; } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/main/java/org/bonitasoft/engine/bdm/dao/client/resources/utils/BDMQueryCommandParameters.java ================================================ /** * 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.bdm.dao.client.resources.utils; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.bdm.model.field.Field; public class BDMQueryCommandParameters { public static Map createCommandParameters(final EntityGetter getter, final long persistenceId) { final Map commandParameters = new HashMap(); commandParameters.put("queryName", getter.getAssociatedNamedQuery()); commandParameters.put("returnType", getter.getReturnTypeClassName()); commandParameters.put("returnsList", getter.returnsList()); commandParameters.put("startIndex", 0); commandParameters.put("maxResults", Integer.MAX_VALUE); final Map queryParameters = new HashMap(); queryParameters.put(Field.PERSISTENCE_ID, persistenceId); commandParameters.put("queryParameters", (Serializable) queryParameters); return commandParameters; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/main/java/org/bonitasoft/engine/bdm/dao/client/resources/utils/Capitalizer.java ================================================ /** * 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.bdm.dao.client.resources.utils; public class Capitalizer { public static String capitalize(final String str) { if (str == null || str.isEmpty()) { return str; } return Character.toUpperCase(str.charAt(0)) + str.substring(1); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/main/java/org/bonitasoft/engine/bdm/dao/client/resources/utils/EntityGetter.java ================================================ /** * 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.bdm.dao.client.resources.utils; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.util.List; import org.bonitasoft.engine.bdm.model.field.Field; /** * Wrapper over entity getter method * * @author Colin Puy */ public class EntityGetter { private final Method method; public EntityGetter(Method method) { checkIsGetter(method); this.method = method; } private void checkIsGetter(Method method) { String methodName = method.getName(); if (!methodName.startsWith("get") || methodName.length() <= 3) { throw new IllegalArgumentException(methodName + " is not a valid getter name."); } } public String getSourceEntityName() { return method.getDeclaringClass().getSimpleName(); } public String getCapitalizedFieldName() { return method.getName().substring(3); } public String getReturnTypeClassName() { return getTargetEntityClass().getName(); } public String getAssociatedNamedQuery() { String targetEntityName = getTargetEntityClass().getSimpleName(); return targetEntityName + ".find" + getCapitalizedFieldName() + "By" + getSourceEntityName() + Capitalizer.capitalize(Field.PERSISTENCE_ID); } public boolean returnsList() { Class returnTypeClass = method.getReturnType(); return List.class.isAssignableFrom(returnTypeClass); } public Class getTargetEntityClass() { if (returnsList()) { final ParameterizedType listType = (ParameterizedType) method.getGenericReturnType(); final Class type = (Class) listType.getActualTypeArguments()[0]; return type; } return method.getReturnType(); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonita/pojo/AddressForTesting.java ================================================ /** * Copyright (C) 2015 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.bonita.pojo; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; import javax.persistence.Version; /** * */ @javax.persistence.Entity(name = "Address") @Table(name = "ADDRESS") @NamedQueries({ @NamedQuery(name = "Address.findByStreet", query = "SELECT a\nFROM Address a\nWHERE a.street= :street\nORDER BY a.persistenceId"), @NamedQuery(name = "Address.findByCity", query = "SELECT a\nFROM Address a\nWHERE a.city= :city\nORDER BY a.persistenceId"), @NamedQuery(name = "Address.find", query = "SELECT a\nFROM Address a\nORDER BY a.persistenceId"), @NamedQuery(name = "Address.findAddressesByEmployeePersistenceId", query = "SELECT e.addresses\nFROM Employee e\nWHERE e.persistenceId= :persistenceId") }) public class AddressForTesting implements org.bonitasoft.engine.bdm.Entity { private static final long serialVersionUID = 1L; @Id @GeneratedValue private Long persistenceId; @Version private Long persistenceVersion; @Column(name = "STREET", nullable = true) private String street; @Column(name = "CITY", nullable = true) private String city; public AddressForTesting() { } public void setPersistenceId(Long persistenceId) { this.persistenceId = persistenceId; } @Override public Long getPersistenceId() { return persistenceId; } public void setPersistenceVersion(Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } @Override public Long getPersistenceVersion() { return persistenceVersion; } public void setStreet(String street) { this.street = street; } public String getStreet() { return street; } public void setCity(String city) { this.city = city; } public String getCity() { return city; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final AddressForTesting other = (AddressForTesting) obj; if (persistenceId == null) { if (other.persistenceId != null) { return false; } } else { if (!persistenceId.equals(other.persistenceId)) { return false; } } if (persistenceVersion == null) { if (other.persistenceVersion != null) { return false; } } else { if (!persistenceVersion.equals(other.persistenceVersion)) { return false; } } if (street == null) { if (other.street != null) { return false; } } else { if (!street.equals(other.street)) { return false; } } if (city == null) { if (other.city != null) { return false; } } else { if (!city.equals(other.city)) { return false; } } return true; } @Override public int hashCode() { final int prime = 31; int result = 1; int persistenceIdCode = 0; if (persistenceId != null) { persistenceIdCode = persistenceId.hashCode(); } result = prime * result + persistenceIdCode; int persistenceVersionCode = 0; if (persistenceVersion != null) { persistenceVersionCode = persistenceVersion.hashCode(); } result = prime * result + persistenceVersionCode; int streetCode = 0; if (street != null) { streetCode = street.hashCode(); } result = prime * result + streetCode; int cityCode = 0; if (city != null) { cityCode = city.hashCode(); } result = prime * result + cityCode; return result; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonita/pojo/EmployeeForTesting.java ================================================ /** * Copyright (C) 2015 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.bonita.pojo; import java.util.ArrayList; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OrderColumn; import javax.persistence.UniqueConstraint; import javax.persistence.Version; import com.fasterxml.jackson.annotation.JsonIgnore; import org.bonitasoft.engine.bdm.lazy.LazyLoaded; import org.hibernate.annotations.Index; /** * Describe a simple employee */ @javax.persistence.Entity(name = "Employee") @org.hibernate.annotations.Table(appliesTo = "EMPLOYEE", indexes = { @Index(name = "IDX_LSTNM", columnNames = { "LASTNAME" }) }) @javax.persistence.Table(name = "EMPLOYEE", uniqueConstraints = { @UniqueConstraint(name = "UK_FL", columnNames = { "FIRSTNAME", "LASTNAME" }) }) @NamedQueries({ @NamedQuery(name = "Employee.findByFirstNameAndLastName", query = "SELECT e\nFROM Employee e\nWHERE e.firstName= :firstName\nAND e.lastName= :lastName\n"), @NamedQuery(name = "Employee.findByFirstName", query = "SELECT e\nFROM Employee e\nWHERE e.firstName= :firstName\nORDER BY e.persistenceId"), @NamedQuery(name = "Employee.findByLastName", query = "SELECT e\nFROM Employee e\nWHERE e.lastName= :lastName\nORDER BY e.persistenceId"), @NamedQuery(name = "Employee.find", query = "SELECT e\nFROM Employee e\nORDER BY e.persistenceId"), @NamedQuery(name = "Employee.findByPhoneNumber", query = "SELECT e FROM Employee e WHERE :phoneNumber IN ELEMENTS(e.phoneNumbers)"), @NamedQuery(name = "Employee.findByFirstNameAndLastNameNewOrder", query = "SELECT e FROM Employee e WHERE e.firstName =:firstName AND e.lastName = :lastName ORDER BY e.lastName"), @NamedQuery(name = "Employee.findByFirstNameFetchAddresses", query = "SELECT e FROM Employee e INNER JOIN FETCH e.addresses WHERE e.firstName =:firstName ORDER BY e.lastName"), @NamedQuery(name = "Employee.countEmployee", query = "SELECT COUNT(e) FROM Employee e") }) public class EmployeeForTesting implements org.bonitasoft.engine.bdm.Entity { private static final long serialVersionUID = 6685785209798954931L; @Id @GeneratedValue private Long persistenceId; @Version private Long persistenceVersion; @Column(name = "FIRSTNAME", nullable = true, length = 10) private String firstName; @Column(name = "LASTNAME", nullable = false) private String lastName; @ElementCollection(fetch = FetchType.EAGER) @OrderColumn @Column(name = "PHONENUMBERS", nullable = true, length = 10) private List phoneNumbers = new ArrayList(10); @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.MERGE) @JoinTable(name = "EMPLOYEE_ADDRESSES", joinColumns = { @JoinColumn(name = "EMPLOYEE_PID") }, inverseJoinColumns = { @JoinColumn(name = "ADDRESS_PID") }) @OrderColumn @JsonIgnore private List addresses = new ArrayList(10); public EmployeeForTesting() { } public void setPersistenceId(final Long persistenceId) { this.persistenceId = persistenceId; } @Override public Long getPersistenceId() { return persistenceId; } public void setPersistenceVersion(final Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } @Override public Long getPersistenceVersion() { return persistenceVersion; } public void setFirstName(final String firstName) { this.firstName = firstName; } public String getFirstName() { return firstName; } public void setLastName(final String lastName) { this.lastName = lastName; } public String getLastName() { return lastName; } public void setPhoneNumbers(final List phoneNumbers) { this.phoneNumbers = phoneNumbers; } public List getPhoneNumbers() { return phoneNumbers; } public void addToPhoneNumbers(final String addTo) { final List phoneNumbers = getPhoneNumbers(); phoneNumbers.add(addTo); } public void removeFromPhoneNumbers(final String removeFrom) { final List phoneNumbers = getPhoneNumbers(); phoneNumbers.remove(removeFrom); } public void setAddresses(final List addresses) { this.addresses = addresses; } @LazyLoaded public List getAddresses() { return addresses; } public void addToAddresses(final org.bonita.pojo.AddressForTesting addTo) { final List addresses = getAddresses(); addresses.add(addTo); } public void removeFromAddresses(final org.bonita.pojo.AddressForTesting removeFrom) { final List addresses = getAddresses(); addresses.remove(removeFrom); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final EmployeeForTesting other = (EmployeeForTesting) obj; if (persistenceId == null) { if (other.persistenceId != null) { return false; } } else { if (!persistenceId.equals(other.persistenceId)) { return false; } } if (persistenceVersion == null) { if (other.persistenceVersion != null) { return false; } } else { if (!persistenceVersion.equals(other.persistenceVersion)) { return false; } } if (firstName == null) { if (other.firstName != null) { return false; } } else { if (!firstName.equals(other.firstName)) { return false; } } if (lastName == null) { if (other.lastName != null) { return false; } } else { if (!lastName.equals(other.lastName)) { return false; } } if (phoneNumbers == null) { if (other.phoneNumbers != null) { return false; } } else { if (!phoneNumbers.equals(other.phoneNumbers)) { return false; } } if (addresses == null) { if (other.addresses != null) { return false; } } else { if (!addresses.equals(other.addresses)) { return false; } } return true; } @Override public int hashCode() { final int prime = 31; int result = 1; int persistenceIdCode = 0; if (persistenceId != null) { persistenceIdCode = persistenceId.hashCode(); } result = prime * result + persistenceIdCode; int persistenceVersionCode = 0; if (persistenceVersion != null) { persistenceVersionCode = persistenceVersion.hashCode(); } result = prime * result + persistenceVersionCode; int firstNameCode = 0; if (firstName != null) { firstNameCode = firstName.hashCode(); } result = prime * result + firstNameCode; int lastNameCode = 0; if (lastName != null) { lastNameCode = lastName.hashCode(); } result = prime * result + lastNameCode; int phoneNumbersCode = 0; if (phoneNumbers != null) { phoneNumbersCode = phoneNumbers.hashCode(); } result = prime * result + phoneNumbersCode; int addressesCode = 0; if (addresses != null) { addressesCode = addresses.hashCode(); } result = prime * result + addressesCode; return result; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/dao/client/resources/BusinessObjectDeserializerTest.java ================================================ /** * 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.bdm.dao.client.resources; import static org.assertj.core.api.Assertions.assertThat; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.Date; import java.util.List; import org.bonitasoft.engine.bdm.proxy.model.Child; import org.junit.Before; import org.junit.Test; public class BusinessObjectDeserializerTest { private BusinessObjectDeserializer deserializer; @Before public void setUp() { deserializer = new BusinessObjectDeserializer(); } @Test public void should_deserialize_an_entity() throws Exception { final OffsetDateTime nextAppointment = OffsetDateTime.of(LocalDateTime.of(2019, 12, 12, 10, 30, 0), ZoneOffset.ofHours(2)); Child jules = new Child("jules", 1, new Date(), LocalDate.of(2017, 3, 6), LocalDateTime.of(2018, 1, 2, 23, 59, 59), nextAppointment); final String julesAsJson = jules.toJson(); jules.setNextAppointment(nextAppointment.withOffsetSameInstant(ZoneOffset.UTC)); Child deserialized = deserializer.deserialize(julesAsJson.getBytes(), Child.class); assertThat(deserialized).isEqualTo(jules); } @Test public void should_deserialize_a_list_of_entities() throws Exception { Child jules = new Child("jules", 1); Child manon = new Child("manon", 0); String json = "[" + jules.toJson() + "," + manon.toJson() + "]"; List deserialized = deserializer.deserializeList(json.getBytes(), Child.class); assertThat(deserialized).containsOnly(jules, manon); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/dao/client/resources/proxy/LazyLoaderTest.java ================================================ /** * 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.bdm.dao.client.resources.proxy; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.mockito.Mockito.*; import java.io.Serializable; import java.lang.reflect.Method; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.CommandAPI; import org.bonitasoft.engine.bdm.dao.client.resources.utils.BDMQueryCommandParameters; import org.bonitasoft.engine.bdm.dao.client.resources.utils.EntityGetter; import org.bonitasoft.engine.bdm.model.field.Field; import org.bonitasoft.engine.bdm.proxy.model.Child; import org.bonitasoft.engine.bdm.proxy.model.Parent; import org.bonitasoft.engine.session.impl.APISessionImpl; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class LazyLoaderTest { @Mock private CommandAPI commandAPI; private LazyLoader lazyLoader; @Before public void setUp() throws Exception { lazyLoader = spy(new LazyLoader(new APISessionImpl(1, new Date(), 3000, "john", 1))); doReturn(commandAPI).when(lazyLoader).getCommandAPI(); } @Test public void should_load_object_through_command_api() throws Exception { long persistenceId = 22L; Child luce = new Child("Luce", 2); Method getChild = Parent.class.getMethod("getChild"); when(commandAPI.execute("executeBDMQuery", parameters(getChild, persistenceId))) .thenReturn(luce.toJson().getBytes()); Object loadedChild = lazyLoader.load(getChild, persistenceId); assertThat(loadedChild).isEqualTo(luce); } @Test public void should_load_list_of_objects_through_command_api() throws Exception { long persistenceId = 22L; Child luce = new Child("Luce", 2); Child julien = new Child("Julien", 5); String json = "[" + luce.toJson() + "," + julien.toJson() + "]"; Method getChildren = Parent.class.getMethod("getChildren"); when(commandAPI.execute("executeBDMQuery", parameters(getChildren, persistenceId))).thenReturn(json.getBytes()); Object loadedChild = lazyLoader.load(getChildren, persistenceId); assertThat(loadedChild).isInstanceOf(List.class); assertThat((List) loadedChild).containsOnly(luce, julien); } @Test public void should_getParameters_return_real_type_when_query_returns_list() throws Exception { long persistenceId = 22L; Method getChildren = Parent.class.getMethod("getChildren"); //when // use the concrete type as we need a Serializable object in later in the code final Map parameters = parameters(getChildren, persistenceId); //then final HashMap queryParameters = new HashMap<>(); // use the concrete type as we need a Serializable object in later in the code queryParameters.put(Field.PERSISTENCE_ID, persistenceId); assertThat(parameters).hasSize(6) .containsOnly( entry("queryName", "Child.findChildrenByParentPersistenceId"), entry("returnsList", true), entry("startIndex", 0), entry("maxResults", Integer.MAX_VALUE), entry("returnType", Child.class.getName()), entry("queryParameters", queryParameters)); } private Map parameters(Method method, long persistenceId) { return BDMQueryCommandParameters.createCommandParameters(new EntityGetter(method), persistenceId); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/dao/client/resources/proxy/ProxyTest.java ================================================ /** * 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.bdm.dao.client.resources.proxy; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import java.lang.reflect.Method; import java.util.List; import org.bonitasoft.engine.bdm.proxy.assertion.ProxyAssert; import org.bonitasoft.engine.bdm.proxy.model.TestEntity; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ProxyTest { @Mock private LazyLoader lazyLoader; @InjectMocks private Proxyfier proxyfier; private TestEntity mockLazyLoaderToReturn(final TestEntity entity) { when(lazyLoader.load(any(Method.class), any(Long.class))).thenReturn(entity); return entity; } @Test public void should_load_object_when_method_is_lazy_and_object_is_not_loaded() { final TestEntity expectedEntity = mockLazyLoaderToReturn(new TestEntity()); final TestEntity entity = proxyfier.proxify(new TestEntity()); final TestEntity lazyEntity = entity.getLazyEntity(); verify(lazyLoader).load(any(Method.class), any(Long.class)); assertThat(lazyEntity).isEqualTo(expectedEntity); } @Test public void should_load_object_when_method_is_lazy_and_object_is_an_empty_list() { final TestEntity entity = proxyfier.proxify(new TestEntity()); entity.getLazyEntityList(); verify(lazyLoader).load(any(Method.class), any(Long.class)); } @Test public void should_return_value_when_it_has_been_already_loaded_before_proxyfication() { final String name = "this is a preloaded value"; TestEntity entity = new TestEntity(); entity.setName(name); entity = proxyfier.proxify(entity); final String proxyName = entity.getName(); verifyNoInteractions(lazyLoader); assertThat(proxyName).isEqualTo(name); } @Test public void should_not_load_object_which_has_been_already_lazy_loaded() { final TestEntity expectedEntity = mockLazyLoaderToReturn(new TestEntity()); final TestEntity entity = proxyfier.proxify(new TestEntity()); entity.getLazyEntity(); final TestEntity lazyEntity = entity.getLazyEntity(); verify(lazyLoader, times(1)).load(any(Method.class), any(Long.class)); assertThat(lazyEntity).isEqualTo(expectedEntity); } @Test public void should_not_load_object_for_a_non_lazy_loading_method() { final TestEntity entity = proxyfier.proxify(new TestEntity()); entity.getEagerEntity(); verifyNoInteractions(lazyLoader); } @Test public void should_not_load_object_that_has_been_set_by_a_setter() { final TestEntity expectedEntity = new TestEntity(); final TestEntity entity = proxyfier.proxify(new TestEntity()); entity.setLazyEntity(expectedEntity); final TestEntity lazyEntity = entity.getLazyEntity(); verifyNoInteractions(lazyLoader); assertThat(lazyEntity).isEqualTo(expectedEntity); } @Test public void should_return_a_proxy_when_calling_a_getter_returning_an_entity() { final TestEntity entity = proxyfier.proxify(new TestEntity()); final TestEntity eagerEntity = entity.getEagerEntity(); ProxyAssert.assertThat(eagerEntity).isAProxy(); } @Test public void should_not_return_a_proxy_when_calling_a_getter_not_returning_an_entity() { final TestEntity entity = proxyfier.proxify(new TestEntity()); entity.setName("aName"); final String name = entity.getName(); ProxyAssert.assertThat(name).isNotAProxy(); } @Test public void should_return_a_list_of_proxies_when_calling_a_getter_returning_a_list_of_entities() { final TestEntity entity = proxyfier.proxify(new TestEntity()); final List entities = entity.getEagerEntities(); for (final TestEntity e : entities) { ProxyAssert.assertThat(e).isAProxy(); } } @Test public void should_not_return_a_list_of_proxies_when_calling_a_getter_not_returning_a_list_of_entities() { final TestEntity entity = proxyfier.proxify(new TestEntity()); final List strings = entity.getStrings(); for (final String string : strings) { ProxyAssert.assertThat(string).isNotAProxy(); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/dao/client/resources/proxy/ProxyfierTest.java ================================================ /** * 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.bdm.dao.client.resources.proxy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.doReturn; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.assertj.core.api.Assertions; import org.bonita.pojo.AddressForTesting; import org.bonita.pojo.EmployeeForTesting; import org.bonitasoft.engine.bdm.proxy.assertion.ProxyAssert; import org.bonitasoft.engine.bdm.proxy.model.TestEntity; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ProxyfierTest { @Mock private LazyLoader lazyLoader; @InjectMocks private Proxyfier proxyfier; @Test public void should_return_null_when_entity_is_null() throws Exception { TestEntity entity = null; TestEntity proxy = proxyfier.proxify(entity); ProxyAssert.assertThat(proxy).isNull(); } @Test public void should_return_null_when_list_of_entities_is_null() throws Exception { List entities = null; List proxies = proxyfier.proxify(entities); ProxyAssert.assertThat(proxies).isNull(); } @Test public void should_proxify_an_entity() { final TestEntity entity = new TestEntity(); final TestEntity proxy = proxyfier.proxify(entity); ProxyAssert.assertThat(proxy).isAProxy(); } @Test public void should_proxify_a_list_of_entities() { final List entities = Arrays.asList(new TestEntity(), new TestEntity()); final List proxies = proxyfier.proxify(entities); for (final TestEntity entity : proxies) { ProxyAssert.assertThat(entity).isAProxy(); } } @Test public void shouldProxyfier_retrieve_list_setter_when_lazyLoader_returns_arraylist() throws Exception { //given final AddressForTesting address1 = new AddressForTesting(); final AddressForTesting address2 = new AddressForTesting(); final List addresses = new ArrayList(); addresses.add(address1); addresses.add(address2); doReturn(addresses).when(lazyLoader).load(any(Method.class), anyLong()); //when final long persistenceId = 1L; final EmployeeForTesting employee = new EmployeeForTesting(); employee.setPersistenceId(persistenceId); final EmployeeForTesting proxify = proxyfier.proxify(employee); //then final List lazyAddresses = (List) proxify.getClass().getMethod("getAddresses", new Class[0]) .invoke(proxify); ProxyAssert.assertThat(proxify).isAProxy(); Assertions.assertThat(lazyAddresses).hasSize(2); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/dao/client/resources/utils/BDMQueryCommandParametersTest.java ================================================ /** * 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.bdm.dao.client.resources.utils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.bdm.proxy.model.Employee; import org.junit.Test; public class BDMQueryCommandParametersTest { @Test @SuppressWarnings("unchecked") public void should_create_bdm_command_parameters_for_method_returning_a_list() throws Exception { EntityGetter getter = new EntityGetter(Employee.class.getDeclaredMethod("getEmployees")); long persistenceId = 12L; Map commandParameters = BDMQueryCommandParameters.createCommandParameters(getter, persistenceId); assertThat(commandParameters).contains( entry("queryName", "Employee.findEmployeesByEmployeePersistenceId"), entry("returnType", Employee.class.getName()), entry("returnsList", Boolean.TRUE), entry("startIndex", 0), entry("maxResults", Integer.MAX_VALUE)); assertThat((Map) commandParameters.get("queryParameters")) .contains(entry("persistenceId", persistenceId)); } @Test @SuppressWarnings("unchecked") public void should_createCommandParameters_with_simple_ref_returns_valid_parameters() throws Exception { EntityGetter getter = new EntityGetter(Employee.class.getDeclaredMethod("getManager")); long persistenceId = 5L; Map commandParameters = BDMQueryCommandParameters.createCommandParameters(getter, persistenceId); assertThat(commandParameters).contains( entry("queryName", "Employee.findManagerByEmployeePersistenceId"), entry("returnType", Employee.class.getName()), entry("returnsList", Boolean.FALSE), entry("startIndex", 0), entry("maxResults", Integer.MAX_VALUE)); assertThat((Map) commandParameters.get("queryParameters")) .contains(entry("persistenceId", persistenceId)); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/dao/client/resources/utils/CapitalizerTest.java ================================================ /** * 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.bdm.dao.client.resources.utils; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class CapitalizerTest { @Test public void should_capitalize_a_string() { String capitalized = Capitalizer.capitalize("uncapitalized"); assertThat(capitalized).isEqualTo("Uncapitalized"); } @Test public void should_do_nothing_for_a_null_string() { String capitalized = Capitalizer.capitalize(null); assertThat(capitalized).isNull(); } @Test public void should_do_nothing_for_an_empty_string() { String capitalized = Capitalizer.capitalize(""); assertThat(capitalized).isEmpty(); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/dao/client/resources/utils/EntityGetterTest.java ================================================ /** * 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.bdm.dao.client.resources.utils; import static org.assertj.core.api.Assertions.assertThat; import java.lang.reflect.Method; import org.bonitasoft.engine.bdm.proxy.model.Address; import org.bonitasoft.engine.bdm.proxy.model.Employee; import org.junit.Test; public class EntityGetterTest { @Test(expected = IllegalArgumentException.class) public void can_only_be_applicable_to_getters() throws Exception { //given Method setter = Employee.class.getMethod("setManager", Employee.class); //when then new EntityGetter(setter); } @Test public void should_be_able_to_retrieve_source_entity_name() throws Exception { //given Method methodOfEmployee = Employee.class.getMethod("getManager"); //when String sourceEntityName = new EntityGetter(methodOfEmployee).getSourceEntityName(); //then assertThat(sourceEntityName).isEqualTo(Employee.class.getSimpleName()); } @Test public void should_be_able_to_retrieve_target_entity_class_even_if_method_return_a_list() throws Exception { //given Method methodReturningListOfAdresses = Employee.class.getMethod("getAddresses"); //when final EntityGetter entityGetter = new EntityGetter(methodReturningListOfAdresses); //then assertThat(entityGetter.getTargetEntityClass()).isEqualTo(Address.class); assertThat(entityGetter.getReturnTypeClassName()).isEqualTo("org.bonitasoft.engine.bdm.proxy.model.Address"); assertThat(entityGetter.returnsList()).isTrue(); } @Test public void should_be_able_to_retrieve_capitalized_field_name() throws Exception { //given Method getAdresses = Employee.class.getMethod("getAddresses"); //when String sourceEntityName = new EntityGetter(getAdresses).getCapitalizedFieldName(); //then assertThat(sourceEntityName).isEqualTo("Addresses"); } @Test public void should_return_entity_class_name_if_getter_return_a_unique_object() throws Exception { //given Method uniqueObjectGetter = Employee.class.getMethod("getAddress"); //when final EntityGetter entityGetter = new EntityGetter(uniqueObjectGetter); //then assertThat(entityGetter.getReturnTypeClassName()).isEqualTo(Address.class.getName()); assertThat(entityGetter.returnsList()).isFalse(); } @Test public void should_be_able_to_get_associated_named_query() throws Exception { //given Method getManager = Employee.class.getMethod("getManager"); //when String namedQuery = new EntityGetter(getManager).getAssociatedNamedQuery(); //then assertThat(namedQuery).isEqualTo("Employee.findManagerByEmployeePersistenceId"); } @Test public void should_be_able_to_determine_if_getter_return_a_list() throws Exception { //given Method multipleObjectGetter = Employee.class.getMethod("getAddresses"); //when final EntityGetter entityGetter = new EntityGetter(multipleObjectGetter); //then assertThat(entityGetter.returnsList()).isTrue(); assertThat(entityGetter.getReturnTypeClassName()).isEqualTo(entityGetter.getTargetEntityClass().getName()); } @Test public void should_be_able_to_determine_if_getter_do_not_return_a_list() throws Exception { //given Method multipleObjectGetter = Employee.class.getMethod("getManager"); //when final EntityGetter entityGetter = new EntityGetter(multipleObjectGetter); //then assertThat(entityGetter.returnsList()).isFalse(); assertThat(entityGetter.getReturnTypeClassName()).isEqualTo(entityGetter.getTargetEntityClass().getName()); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/proxy/assertion/ProxyAssert.java ================================================ /** * 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.bdm.proxy.assertion; import static javassist.util.proxy.ProxyFactory.isProxyClass; import org.assertj.core.api.AbstractAssert; public class ProxyAssert extends AbstractAssert { protected ProxyAssert(Object actual) { super(actual, ProxyAssert.class); } public static ProxyAssert assertThat(Object entity) { return new ProxyAssert(entity); } public ProxyAssert isAProxy() { isNotNull(); if (!isProxyClass(actual.getClass())) { failWithMessage("Expected <%s> to be a proxy", actual); } return this; } public ProxyAssert isNotAProxy() { isNotNull(); if (isProxyClass(actual.getClass())) { failWithMessage("Expected <%s> to not be a proxy", actual); } return this; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/proxy/model/Address.java ================================================ /** * 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.bdm.proxy.model; import org.bonitasoft.engine.bdm.Entity; @SuppressWarnings("serial") public class Address implements Entity { @Override public Long getPersistenceId() { return null; } @Override public Long getPersistenceVersion() { return null; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/proxy/model/Child.java ================================================ /** * 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.bdm.proxy.model; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.util.Date; import lombok.AllArgsConstructor; import lombok.Data; import org.bonitasoft.engine.bdm.Entity; @Data @AllArgsConstructor public class Child implements Entity { private static final long serialVersionUID = 3000056129946432830L; private String name; private Integer age; private Date oldDate; private LocalDate birthdate; private LocalDateTime localDateTime; private OffsetDateTime nextAppointment; public Child() { // Empty constructor required for json serialization } public Child(String name, Integer age) { this.name = name; this.age = age; } @Override public Long getPersistenceId() { return null; } @Override public Long getPersistenceVersion() { return null; } public String toJson() { return "{\"name\" : \"" + name + "\", \"age\" : " + age + ", \"oldDate\" : " + (oldDate != null ? oldDate.getTime() : null) + ", \"birthdate\" : " + (birthdate != null ? "\"" + birthdate.toString() + "\"" : null) + ", \"localDateTime\" : " + (localDateTime != null ? "\"" + localDateTime.toString() + "\"" : null) + ", \"nextAppointment\" : " + (nextAppointment != null ? "\"" // leave TimeZone offset as is, if any, as this is the Deserializer job to convert it to UTC: + nextAppointment.toString() + "\"" : null) + " }"; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/proxy/model/Employee.java ================================================ /** * 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.bdm.proxy.model; import java.util.ArrayList; import java.util.List; public class Employee { private List employees = new ArrayList(10); private List
      addresses = new ArrayList
      (10); private Employee manager; private Address address; public Employee() { } public void setEmployees(final List employees) { this.employees = employees; } public List getEmployees() { return employees; } public Employee getManager() { return manager; } public void setManager(Employee manager) { this.manager = manager; } @SuppressWarnings({ "unchecked", "rawtypes" }) public void addToAddresses(final Employee addTo) { final List employees = getEmployees(); employees.add(addTo); } @SuppressWarnings("rawtypes") public void removeFromAddresses(final Employee removeFrom) { final List employees = getEmployees(); employees.remove(removeFrom); } public List
      getAddresses() { return addresses; } public void setAddresses(List
      addresses) { this.addresses = addresses; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/proxy/model/Parent.java ================================================ /** * 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.bdm.proxy.model; import java.util.List; import org.bonitasoft.engine.bdm.Entity; @SuppressWarnings("serial") public class Parent implements Entity { private Child child; private List children; public Child getChild() { return child; } public void setChild(Child child) { this.child = child; } public List getChildren() { return children; } public void setChildren(List children) { this.children = children; } @Override public Long getPersistenceId() { // TODO Auto-generated method stub return null; } @Override public Long getPersistenceVersion() { // TODO Auto-generated method stub return null; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-client-resources/src/test/java/org/bonitasoft/engine/bdm/proxy/model/TestEntity.java ================================================ /** * 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.bdm.proxy.model; import static java.util.Arrays.asList; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.bdm.lazy.LazyLoaded; @SuppressWarnings("serial") public class TestEntity implements Entity { private TestEntity entity; private String name; public TestEntity getEagerEntity() { return new TestEntity(); } public void setLazyEntity(final TestEntity entity) { this.entity = entity; } @LazyLoaded public TestEntity getLazyEntity() { return entity; } @LazyLoaded public List getLazyEntityList() { return Collections.emptyList(); } public List getEagerEntities() { return asList(new TestEntity(), new TestEntity()); } public String getName() { return name; } public void setName(final String name) { this.name = name; } public List getStrings() { return asList("aString", "anotherString"); } @Override public Long getPersistenceId() { return 0L; } @Override public Long getPersistenceVersion() { return 0L; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((entity == null) ? 0 : entity.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (obj instanceof TestEntity other) { if (entity == null) { if (other.entity != null) return false; } else if (!entity.equals(other.entity)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } return false; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/build.gradle ================================================ import org.bonitasoft.engine.gradle.PomUtils plugins { id 'com.github.johnrengelman.shadow' id 'maven-publish' } dependencies { api project(':services:bonita-commons') api libs.hibernateCore api(libs.eclipseCompiler) api libs.springCore api libs.javassist api project(':bpm:bonita-common') api libs.commonsLang api libs.commonsText api libs.jaxbCodeModel testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.logback testImplementation testFixtures(project(':bpm:bonita-common')) testImplementation project(':services:bonita-business-data:bonita-business-data-api') } group = 'org.bonitasoft.engine.data' sourceSets { main { resources { srcDirs "src/main/resources", "../bonita-business-data-client-resources/src/main/java" } } } tasks.register("sourcesJar", Jar) { from sourceSets.main.allJava archiveClassifier = 'sources' } tasks.register("javadocJar", Jar) { from javadoc archiveClassifier = 'javadoc' } publishing { publications { mavenJava(MavenPublication) { from project.components.java artifact project.sourcesJar artifact project.javadocJar pom { pom -> name = "Bonita Business Data Generator" description = "Bonita Business Data Generator is the library used to generate and compile a Business Data Model" PomUtils.pomCommunityPublication(pom) } } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/AbstractBDMCodeGenerator.java ================================================ /** * 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.business.data.generator; import static org.bonitasoft.engine.bdm.validator.rule.QueryParameterValidationRule.FORBIDDEN_PARAMETER_NAMES; import java.io.File; import java.io.IOException; import java.util.Collection; import com.sun.codemodel.JBlock; import com.sun.codemodel.JClass; import com.sun.codemodel.JClassAlreadyExistsException; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JExpr; import com.sun.codemodel.JMethod; import com.sun.codemodel.JType; import com.sun.codemodel.JVar; import org.bonitasoft.engine.bdm.BDMQueryUtil; import org.bonitasoft.engine.bdm.BusinessObjectModelValidationException; import org.bonitasoft.engine.bdm.dao.BusinessObjectDAO; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.Query; import org.bonitasoft.engine.bdm.model.QueryParameter; import org.bonitasoft.engine.bdm.model.field.Field; import org.bonitasoft.engine.bdm.model.field.RelationField; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.bdm.validator.BusinessObjectModelValidator; import org.bonitasoft.engine.bdm.validator.ValidationStatus; /** * @author Romain Bioteau * @author Matthieu Chaffotte */ public abstract class AbstractBDMCodeGenerator extends CodeGenerator { private static final String DAO_SUFFIX = "DAO"; protected static final String DAO_IMPL_SUFFIX = "DAOImpl"; private static final String NEW_INSTANCE_METHOD_NAME = "newInstance"; protected AbstractBDMCodeGenerator() { super(); } @Override public AbstractBDMCodeGenerator disableRuntimeClassesValidation() { super.disableRuntimeClassesValidation(); return this; } public void generateBom(final BusinessObjectModel bom, final File destDir) throws IOException, JClassAlreadyExistsException, BusinessObjectModelValidationException, ClassNotFoundException { final BusinessObjectModelValidator validator = new BusinessObjectModelValidator(); final ValidationStatus validationStatus = validator.validate(bom); if (!validationStatus.isOk()) { throw new BusinessObjectModelValidationException(validationStatus); } buildJavaModelFromBom(bom); super.generate(destDir); } private void buildJavaModelFromBom(final BusinessObjectModel bom) throws JClassAlreadyExistsException, ClassNotFoundException { if (bom == null) { throw new IllegalArgumentException("bom is null"); } final EntityCodeGenerator entityCodeGenerator = new EntityCodeGenerator(this, bom); for (final BusinessObject bo : bom.getBusinessObjects()) { final JDefinedClass entity = entityCodeGenerator.addEntity(bo); addDAO(bo, entity); } } protected void addNewInstanceMethodBody(final JMethod method, final JDefinedClass entity) { final JBlock methodBody = method.body(); for (final JVar param : method.params()) { addNotNullParamCheck(methodBody, param); } final JVar instanceRef = methodBody.decl(entity, "instance", JExpr._new(entity)); for (final JVar param : method.params()) { callSetter(methodBody, param, instanceRef, entity); } methodBody._return(instanceRef); } private void callSetter(final JBlock methodBody, final JVar param, final JVar instanceRef, final JDefinedClass entity) { final JMethod setter = getSetterForParam(param, entity); if (setter == null) { throw new IllegalStateException("No setter found for parameter " + param.name()); } methodBody.add(instanceRef.invoke(setter).arg(param)); } private JMethod getSetterForParam(final JVar param, final JDefinedClass entity) { final String setterName = getSetterName(param); return findMethodWithSignature(param, entity, setterName); } private JMethod findMethodWithSignature(final JVar param, final JDefinedClass entity, final String name) { for (final JMethod m : entity.methods()) { if (!m.name().equals(name)) { continue; } if (m.params().size() == 1) { final JVar jVar = m.params().get(0); if (jVar.type().equals(param.type()) || jVar.type().fullName().equals(param.type().fullName())) { return m; } } } return null; } private void addNotNullParamCheck(final JBlock methodBody, final JVar param) { methodBody._if(param.eq(JExpr._null()))._then() ._throw(JExpr._new(getModel().ref(IllegalArgumentException.class)) .arg(JExpr.lit(param.name() + " cannot be null"))); } protected abstract void addDAO(final BusinessObject bo, JDefinedClass entity) throws JClassAlreadyExistsException, ClassNotFoundException; protected JDefinedClass createDAOInterface(final BusinessObject bo, final JDefinedClass entity) throws JClassAlreadyExistsException { final String daoInterfaceClassName = toDaoInterfaceClassname(bo); final JDefinedClass daoInterface = addInterface(daoInterfaceClassName); addInterface(daoInterface, BusinessObjectDAO.class.getName()); // Add method signature in interface for provided queries for (final Query q : BDMQueryUtil.createProvidedQueriesForBusinessObject(bo)) { createMethodForQuery(entity, daoInterface, q); } // Add method signature in interface for custom queries for (final Query q : bo.getQueries()) { createMethodForQuery(entity, daoInterface, q); } createMethodForNewInstance(bo, entity, daoInterface); return daoInterface; } protected JMethod createMethodForNewInstance(final BusinessObject bo, final JDefinedClass entity, final JDefinedClass daoInterface) { final JMethod newInstanceMethod = addMethodSignature(daoInterface, NEW_INSTANCE_METHOD_NAME, entity); for (final Field field : bo.getFields()) { if (!field.isNullable()) { String typeClassName = null; if (field instanceof SimpleField) { typeClassName = ((SimpleField) field).getType().getClazz().getName(); } else if (field instanceof RelationField) { typeClassName = ((RelationField) field).getReference().getQualifiedName(); } newInstanceMethod.param(getModel().ref(typeClassName), field.getName()); } } return newInstanceMethod; } protected JMethod createMethodForQuery(final JDefinedClass entity, final JDefinedClass targetClass, final Query query) { final String methodName = query.getName(); final JMethod queryMethod = createQueryMethod(entity, targetClass, methodName, query.getReturnType()); for (final QueryParameter param : query.getQueryParameters()) { queryMethod.param(getModel().ref(param.getClassName()), param.getName()); } addOptionalPaginationParameters(queryMethod, query); return queryMethod; } private JMethod createQueryMethod(final JDefinedClass entity, final JDefinedClass targetClass, final String name, final String returnTypeName) { JType returnType; if (returnTypeName.equals(entity.fullName())) { returnType = entity; } else { returnType = getModel().ref(returnTypeName); } final JClass collectionType = getModel().ref(Collection.class.getName()); if (returnType instanceof JClass && collectionType.isAssignableFrom((JClass) returnType)) { returnType = ((JClass) returnType).narrow(entity); } return addMethodSignature(targetClass, name, returnType); } private String toDaoInterfaceClassname(final BusinessObject bo) { return bo.getQualifiedName() + DAO_SUFFIX; } private void addOptionalPaginationParameters(final JMethod queryMethod, final Query query) { if (query.hasMultipleResults()) { for (final String param : FORBIDDEN_PARAMETER_NAMES) { queryMethod.param(getModel().ref(int.class.getName()), param); } } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/AbstractBDMJarBuilder.java ================================================ /** * 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.business.data.generator; import static org.apache.commons.io.FileUtils.deleteDirectory; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import javax.persistence.Entity; import com.fasterxml.jackson.annotation.JsonIgnore; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.IOFileFilter; import org.apache.commons.io.filefilter.TrueFileFilter; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.business.data.generator.compiler.JDTCompiler; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.commons.io.IOUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Matthieu Chaffotte */ public abstract class AbstractBDMJarBuilder { private static final Logger log = LoggerFactory.getLogger(AbstractBDMJarBuilder.class); private final JDTCompiler compiler; private final AbstractBDMCodeGenerator bdmCodeGenerator; @Deprecated protected AbstractBDMJarBuilder(AbstractBDMCodeGenerator bdmCodeGenerator, final JDTCompiler compiler) { this.bdmCodeGenerator = bdmCodeGenerator; this.compiler = compiler; } protected AbstractBDMJarBuilder(AbstractBDMCodeGenerator bdmCodeGenerator) { this.bdmCodeGenerator = bdmCodeGenerator; this.compiler = new JDTCompiler(); } /** * @param bom the business object model to generate the jar from * @param fileFilter * filter the entries to be added or not in generated jar * @return the content of the generated jar * @throws BDMJarGenerationException if an error occurs during the generation of the jar */ public byte[] build(final BusinessObjectModel bom, final IOFileFilter fileFilter) throws BDMJarGenerationException { try { final File tmpBDMDirectory = Files.createTempDirectory("bdm").toFile(); try { addSourceFilesToDirectory(bom, tmpBDMDirectory); var additionalClasspath = getCompileDependencies(); if (log.isDebugEnabled()) { log.debug("Compiling BDM classes using classpath: {}", additionalClasspath.stream() .map(File::getName) .collect(Collectors.joining(File.pathSeparator))); } compiler.compile(tmpBDMDirectory, tmpBDMDirectory, additionalClasspath.toArray(File[]::new)); return generateJar(tmpBDMDirectory, fileFilter); } finally { deleteDirectory(tmpBDMDirectory); } } catch (final Exception e) { throw new BDMJarGenerationException(e); } } /** * Add BDM compile dependencies required to compile the BDM classes. * It uses the current classloader to find the required classes and retrieve their corresponding jar files. * * @throws ClassNotFoundException if a required class cannot be found in the current classloader */ protected Set getCompileDependencies() throws ClassNotFoundException { var compileDependencies = new HashSet(); compileDependencies.add(JDTCompiler.lookupJarContaining(Entity.class)); compileDependencies.add(JDTCompiler.lookupJarContaining(org.bonitasoft.engine.bdm.Entity.class)); compileDependencies.add(JDTCompiler.lookupJarContaining(JsonIgnore.class)); compileDependencies.add(JDTCompiler.lookupJarContaining("org.hibernate.annotations.Parameter")); compileDependencies.add(JDTCompiler.lookupJarContaining(DateConverter.class)); compileDependencies.add(JDTCompiler.lookupJarContaining(SBonitaRuntimeException.class)); compileDependencies .add(JDTCompiler.lookupJarContaining("org.bonitasoft.engine.business.data.BusinessDataRepository")); return compileDependencies; } protected void addSourceFilesToDirectory(final BusinessObjectModel bom, final File directory) throws CodeGenerationException { try { bdmCodeGenerator.generateBom(bom, directory); } catch (Exception e) { throw new CodeGenerationException("Error when generating source files for business object model", e); } } private byte[] generateJar(final File directory, final IOFileFilter fileFilter) throws IOException { final Collection files = FileUtils.listFiles(directory, fileFilter, TrueFileFilter.TRUE); final Map resources = new HashMap<>(); for (final File file : files) { final String relativeName = directory.toURI().relativize(file.toURI()).getPath(); final byte[] content = FileUtils.readFileToByteArray(file); resources.put(relativeName, content); } return IOUtil.generateJar(resources); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/BDMJarGenerationException.java ================================================ /** * 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.business.data.generator; public class BDMJarGenerationException extends Exception { public BDMJarGenerationException(Exception e) { super(e); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/CodeGenerationException.java ================================================ /** * 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.business.data.generator; public class CodeGenerationException extends Exception { private static final long serialVersionUID = 5686073269279155335L; public CodeGenerationException(String message, Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/CodeGenerator.java ================================================ /** * 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.business.data.generator; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Target; import java.lang.management.ManagementFactory; import java.net.URL; import java.net.URLClassLoader; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.lang.model.SourceVersion; import com.sun.codemodel.ClassType; import com.sun.codemodel.CodeWriter; import com.sun.codemodel.JAnnotatable; import com.sun.codemodel.JAnnotationUse; import com.sun.codemodel.JBlock; import com.sun.codemodel.JClass; import com.sun.codemodel.JClassAlreadyExistsException; import com.sun.codemodel.JCodeModel; import com.sun.codemodel.JConditional; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JExpr; import com.sun.codemodel.JExpression; import com.sun.codemodel.JFieldRef; import com.sun.codemodel.JFieldVar; import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import com.sun.codemodel.JType; import com.sun.codemodel.JVar; import com.sun.codemodel.writer.FileCodeWriter; import com.sun.codemodel.writer.ProgressCodeWriter; import org.apache.commons.text.WordUtils; import org.bonitasoft.engine.bdm.model.field.Field; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.RelationField; import org.bonitasoft.engine.bdm.model.field.SimpleField; /** * @author Romain Bioteau * @author Matthieu Chaffotte */ public class CodeGenerator { private final JCodeModel model; protected boolean shouldValidateRuntimeClasses = true; public CodeGenerator() { model = new JCodeModel(); } public CodeGenerator disableRuntimeClassesValidation() { this.shouldValidateRuntimeClasses = false; return this; } public void generate(final File destDir) throws IOException { Charset encoding = StandardCharsets.UTF_8; try (PrintStream statusStream = new PrintStream(new NullStream(), false, encoding)) { CodeWriter src = new ProgressCodeWriter(new FileCodeWriter(destDir, encoding.name()), statusStream); CodeWriter res = new ProgressCodeWriter(new FileCodeWriter(destDir, encoding.name()), statusStream); model.build(src, res); } } public JDefinedClass addClass(final String fullyQualifiedName) throws JClassAlreadyExistsException { if (fullyQualifiedName == null || fullyQualifiedName.isEmpty()) { throw new IllegalArgumentException("Classname cannot be null or empty"); } if (!SourceVersion.isName(fullyQualifiedName)) { throw new IllegalArgumentException("Classname " + fullyQualifiedName + " is not a valid qualified name"); } if (shouldValidateRuntimeClasses) { validateClassNotExistsInRuntime(fullyQualifiedName); } return model._class(fullyQualifiedName); } private void validateClassNotExistsInRuntime(final String qualifiedName) { final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { var clazz = contextClassLoader.loadClass(qualifiedName); // Here the class is found, which is NOT normal! Let's investigate: final StringBuilder message = new StringBuilder( "Class " + qualifiedName + " already exists in target runtime environment"); final ClassLoader classLoader = clazz.getClassLoader(); if (classLoader != null) { if (classLoader instanceof URLClassLoader) { for (URL url : ((URLClassLoader) classLoader).getURLs()) { message.append("\n").append(url.toString()); } } else { message.append("\nCurrent classloader is NOT an URLClassLoader: ").append(classLoader.toString()); } } message.append("\nCurrent JVM Id where the class is found: ") .append(ManagementFactory.getRuntimeMXBean().getName()); message.append( "\nMake sure you did not manually add the jar files bdm-model.jar / bdm-dao.jar somewhere on the classpath."); message.append( "\nThose jar files are handled by Bonita internally and should not be manipulated outside Bonita."); throw new IllegalArgumentException(message.toString()); } catch (final ClassNotFoundException ignored) { // here is the normal behaviour } } public JDefinedClass addInterface(final JDefinedClass definedClass, final String fullyQualifiedName) { return definedClass._implements(model.ref(fullyQualifiedName)); } public JDefinedClass addInterface(final String fullyQualifiedName) throws JClassAlreadyExistsException { if (!fullyQualifiedName.contains(".")) { return model.rootPackage()._class(JMod.PUBLIC, fullyQualifiedName, ClassType.INTERFACE); } return model._class(fullyQualifiedName, ClassType.INTERFACE); } public JFieldVar addField(final JDefinedClass definedClass, final String fieldName, final Class type) { validateFieldName(fieldName); if (type == null) { throw new IllegalArgumentException("Field type cannot be null"); } return definedClass.field(JMod.PRIVATE, type, fieldName); } public JFieldVar addField(final JDefinedClass definedClass, final Field field) { return addField(definedClass, field.getName(), toJavaClass(field)); } public JFieldVar addField(final JDefinedClass definedClass, final String fieldName, final JClass type) { validateFieldName(fieldName); if (type == null) { throw new IllegalArgumentException("Field type cannot be null"); } return definedClass.field(JMod.PRIVATE, type, fieldName); } private void validateFieldName(final String fieldName) { if (fieldName == null || fieldName.isEmpty()) { throw new IllegalArgumentException("Field name cannot be null or empty"); } if (SourceVersion.isKeyword(fieldName)) { throw new IllegalArgumentException("Field " + fieldName + " is a resered keyword"); } if (!SourceVersion.isIdentifier(fieldName)) { throw new IllegalArgumentException("Field " + fieldName + " is not a valid Java identifier"); } } @SuppressWarnings("rawtypes") private JClass narrowClass(final Class collectionClass, final JClass narrowClass) { final JClass collectionJClass = getModel().ref(collectionClass); return collectionJClass.narrow(narrowClass); } JFieldVar addListField(final JDefinedClass entityClass, final Field field) { final JClass fieldClass = toJavaClass(field); final JClass fieldListClass = narrowClass(List.class, fieldClass); final JClass arrayListFieldClazz = narrowClass(ArrayList.class, fieldClass); final JFieldVar listFieldVar = entityClass.field(JMod.PRIVATE, fieldListClass, field.getName()); final JExpression newInstance = JExpr._new(arrayListFieldClazz).arg(JExpr.lit(10)); listFieldVar.init(newInstance); return listFieldVar; } JClass toJavaClass(final Field field) { if (field instanceof SimpleField) { final Class fieldClass = ((SimpleField) field).getType().getClazz(); return getModel().ref(fieldClass); } final String qualifiedName = ((RelationField) field).getReference().getQualifiedName(); return getModel().ref(qualifiedName); } public JClass toJavaClass(final FieldType type) { return getModel().ref(type.getClazz()); } void addDefaultConstructor(final JDefinedClass definedClass) { definedClass.constructor(JMod.PUBLIC); } public JMethod addSetter(final JDefinedClass definedClass, final JFieldVar field) { final JMethod method = definedClass.method(JMod.PUBLIC, Void.TYPE, getSetterName(field)); method.param(field.type(), field.name()); method.body().assign(JExpr._this().ref(field.name()), JExpr.ref(field.name())); return method; } JMethod addListSetter(final JDefinedClass definedClass, final JFieldVar field) { final JMethod method = definedClass.method(JMod.PUBLIC, Void.TYPE, getSetterName(field)); method.param(field.type(), field.name()); final JFieldRef thisField = JExpr._this().ref(field.name()); final JConditional ifListIsNull = method.body()._if(thisField.eq(JExpr._null())); ifListIsNull._then().assign(JExpr._this().ref(field.name()), JExpr.ref(field.name())); final JBlock elseBlock = ifListIsNull._else(); final JVar copyVar = elseBlock.decl(field.type(), "copy", JExpr._new(getModel().ref(ArrayList.class)).arg(field)); elseBlock.invoke(JExpr._this().ref(field.name()), "clear"); elseBlock.invoke(JExpr._this().ref(field.name()), "addAll").arg(copyVar); return method; } public JMethod addGetter(final JDefinedClass definedClass, final JFieldVar field) { final JMethod method = definedClass.method(JMod.PUBLIC, field.type(), getGetterName(field)); final JBlock block = method.body(); block._return(field); return method; } public JMethod addMethodSignature(final JDefinedClass definedClass, final String methodName, final JType returnType) { return definedClass.method(JMod.PUBLIC, returnType, methodName); } public JMethod addAddMethod(final JDefinedClass definedClass, final Field field) { return addListMethod(definedClass, field, "add", "addTo"); } public JMethod addRemoveMethod(final JDefinedClass definedClass, final Field field) { return addListMethod(definedClass, field, "remove", "removeFrom"); } private JMethod addListMethod(final JDefinedClass definedClass, final Field field, final String listMethodName, final String parameterName) { final JClass fieldClass = toJavaClass(field); final StringBuilder builder = new StringBuilder(parameterName); builder.append(WordUtils.capitalize(field.getName())); final JMethod method = definedClass.method(JMod.PUBLIC, void.class, builder.toString()); final JVar adderParam = method.param(fieldClass, parameterName); final JBlock body = method.body(); final JVar decl = body.decl(getModel().ref(List.class), field.getName(), JExpr.invoke(getGetterName(field))); body.add(decl.invoke(listMethodName).arg(adderParam)); return method; } private String getGetterName(final JVar field) { final JType type = field.type(); final boolean bool = Boolean.class.getName().equals(type.fullName()); return getGetterName(bool, field.name()); } private String getGetterName(final Field field) { final boolean bool = field instanceof SimpleField && FieldType.BOOLEAN.equals(((SimpleField) field).getType()) && !field.isCollection(); return getGetterName(bool, field.getName()); } private String getGetterName(final boolean bool, final String fieldName) { final StringBuilder builder = new StringBuilder(); if (bool) { builder.append("is"); } else { builder.append("get"); } builder.append(WordUtils.capitalize(fieldName)); return builder.toString(); } String getSetterName(final JVar field) { return "set" + WordUtils.capitalize(field.name()); } public JCodeModel getModel() { return model; } protected JAnnotationUse addAnnotation(final JAnnotatable annotable, final Class annotationType) { final Set supportedElementTypes = getSupportedElementTypes(annotationType); checkAnnotationTarget(annotable, annotationType, supportedElementTypes); return annotable.annotate(model.ref(annotationType)); } private void checkAnnotationTarget(final JAnnotatable annotable, final Class annotationType, final Set supportedElementTypes) { if (annotable instanceof JClass && !supportedElementTypes.isEmpty() && !supportedElementTypes.contains(ElementType.TYPE)) { throw new IllegalArgumentException(annotationType.getName() + " is not supported for " + annotable); } if (annotable instanceof JFieldVar && !supportedElementTypes.isEmpty() && !supportedElementTypes.contains(ElementType.FIELD)) { throw new IllegalArgumentException(annotationType.getName() + " is not supported for " + annotable); } if (annotable instanceof JMethod && !supportedElementTypes.isEmpty() && !supportedElementTypes.contains(ElementType.METHOD)) { throw new IllegalArgumentException(annotationType.getName() + " is not supported for " + annotable); } } private Set getSupportedElementTypes(final Class annotationType) { final Set elementTypes = new HashSet(); final Target targetAnnotation = annotationType.getAnnotation(Target.class); if (targetAnnotation != null) { final ElementType[] value = targetAnnotation.value(); if (value != null) { for (final ElementType et : value) { elementTypes.add(et); } } } return elementTypes; } private static class NullStream extends OutputStream { NullStream() { } public void write(int b) throws IOException { } public void close() throws IOException { } public void flush() throws IOException { } public void write(byte[] b, int off, int len) throws IOException { } public void write(byte[] b) throws IOException { } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/DateAndTimeConverter.java ================================================ /** * 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.business.data.generator; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import javax.persistence.AttributeConverter; import javax.persistence.Converter; /** * @author Danila Mazour */ @Converter public class DateAndTimeConverter implements AttributeConverter { @Override public String convertToDatabaseColumn(LocalDateTime localDateTime) { if (localDateTime != null) { return localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); } else { return null; } } @Override public LocalDateTime convertToEntityAttribute(String s) { if (s != null) { try { return LocalDateTime.parse(s, DateTimeFormatter.ISO_LOCAL_DATE_TIME); } catch (DateTimeParseException e) { throw new RuntimeException( "Database date & time format must be ISO-8601 compliant ( yyyy-mm-ddThh:mm:ss )", e); } } else { return null; } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/DateConverter.java ================================================ /** * 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.business.data.generator; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import javax.persistence.AttributeConverter; import javax.persistence.Converter; /** * @author Danila Mazour */ @Converter public class DateConverter implements AttributeConverter { @Override public String convertToDatabaseColumn(LocalDate localDate) { if (localDate != null) { return localDate.toString(); } else { return null; } } @Override public LocalDate convertToEntityAttribute(String s) { if (s != null) { try { return LocalDate.parse(s, DateTimeFormatter.ISO_LOCAL_DATE); } catch (DateTimeParseException e) { throw new RuntimeException("Database date format must be ISO-8601 compliant ( yyyy-mm-dd )", e); } } else { return null; } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/EntityCodeGenerator.java ================================================ /** * 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.business.data.generator; import java.util.List; import javax.persistence.*; import com.sun.codemodel.JAnnotationArrayMember; import com.sun.codemodel.JAnnotationUse; import com.sun.codemodel.JClassAlreadyExistsException; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JFieldVar; import com.sun.codemodel.JMethod; import org.bonitasoft.engine.bdm.BDMQueryUtil; import org.bonitasoft.engine.bdm.lazy.LazyLoaded; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.Index; import org.bonitasoft.engine.bdm.model.Query; import org.bonitasoft.engine.bdm.model.UniqueConstraint; import org.bonitasoft.engine.bdm.model.field.Field; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.RelationField; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.Parameter; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Colin PUY, * @author Matthieu Chaffotte */ public class EntityCodeGenerator { private final CodeGenerator codeGenerator; private final RelationFieldAnnotator relationFieldAnnotator; private final BusinessObjectModel bom; protected static final Logger LOGGER = LoggerFactory.getLogger(EntityCodeGenerator.class); public EntityCodeGenerator(final CodeGenerator codeGenerator, final BusinessObjectModel bom) { this.codeGenerator = codeGenerator; this.bom = bom; relationFieldAnnotator = new RelationFieldAnnotator(codeGenerator); } public JDefinedClass addEntity(final BusinessObject bo) throws JClassAlreadyExistsException { final String qualifiedName = bo.getQualifiedName(); JDefinedClass entityClass = codeGenerator.addClass(qualifiedName); entityClass = codeGenerator.addInterface(entityClass, org.bonitasoft.engine.bdm.Entity.class.getName()); entityClass.javadoc().add(bo.getDescription()); final JAnnotationUse entityAnnotation = codeGenerator.addAnnotation(entityClass, Entity.class); entityAnnotation.param("name", entityClass.name()); addIndexAnnotations(bo, entityClass); addUniqueConstraintAnnotations(bo, entityClass); addQueriesAnnotation(bo, entityClass); String dbVendor = determineDbVendor(); addFieldsAndMethods(bo, entityClass, dbVendor); codeGenerator.addDefaultConstructor(entityClass); return entityClass; } private void addFieldsAndMethods(final BusinessObject bo, final JDefinedClass entityClass, String dbVendor) { addPersistenceIdFieldAndAccessors(entityClass, dbVendor); addPersistenceVersionFieldAndAccessors(entityClass); for (final Field field : bo.getFields()) { final JFieldVar fieldVar = addField(entityClass, field, dbVendor); addAccessors(entityClass, fieldVar, field); addModifiers(entityClass, field); } } private void addQueriesAnnotation(final BusinessObject bo, final JDefinedClass entityClass) { final JAnnotationUse namedQueriesAnnotation = codeGenerator.addAnnotation(entityClass, NamedQueries.class); final JAnnotationArrayMember valueArray = namedQueriesAnnotation.paramArray("value"); // Add provided queries for (final Query providedQuery : BDMQueryUtil.createProvidedQueriesForBusinessObject(bo)) { addNamedQuery(entityClass, valueArray, providedQuery.getName(), providedQuery.getContent()); } // Add method for lazy fields for (final Query query : BDMQueryUtil.createProvidedQueriesForLazyField(bom, bo)) { addNamedQuery(entityClass, valueArray, query.getName(), query.getContent()); } // Add custom queries for (final Query query : bo.getQueries()) { addNamedQuery(entityClass, valueArray, query.getName(), query.getContent()); } } private void addUniqueConstraintAnnotations(final BusinessObject businessObject, final JDefinedClass entityClass) { final JAnnotationUse tableAnnotation = codeGenerator.addAnnotation(entityClass, Table.class); tableAnnotation.param("name", entityClass.name().toUpperCase()); final List uniqueConstraints = businessObject.getUniqueConstraints(); if (!uniqueConstraints.isEmpty()) { final JAnnotationArrayMember uniqueConstraintsArray = tableAnnotation.paramArray("uniqueConstraints"); for (final UniqueConstraint uniqueConstraint : uniqueConstraints) { final JAnnotationUse uniqueConstraintAnnotation = uniqueConstraintsArray .annotate(javax.persistence.UniqueConstraint.class); uniqueConstraintAnnotation.param("name", uniqueConstraint.getName().toUpperCase()); final JAnnotationArrayMember columnNamesParamArray = uniqueConstraintAnnotation .paramArray("columnNames"); for (final String fieldName : uniqueConstraint.getFieldNames()) { columnNamesParamArray.param(getFieldRealColumnName(businessObject, fieldName)); } } } } private void addIndexAnnotations(final BusinessObject businessObject, final JDefinedClass entityClass) { final List indexes = businessObject.getIndexes(); if (indexes != null && !indexes.isEmpty()) { final JAnnotationUse hibTabAnnotation = codeGenerator.addAnnotation(entityClass, org.hibernate.annotations.Table.class); hibTabAnnotation.param("appliesTo", entityClass.name().toUpperCase()); final JAnnotationArrayMember indexesArray = hibTabAnnotation.paramArray("indexes"); for (final Index index : indexes) { final JAnnotationUse indexAnnotation = indexesArray.annotate(org.hibernate.annotations.Index.class); indexAnnotation.param("name", index.getName().toUpperCase()); final JAnnotationArrayMember columnParamArray = indexAnnotation.paramArray("columnNames"); for (final String fieldName : index.getFieldNames()) { columnParamArray.param(getFieldRealColumnName(businessObject, fieldName).toUpperCase()); } } } } /** * get real column name used in database * * @return fieldName for simple fields or reduced name suffix by "_PID" when we have an entity relationship */ private String getFieldRealColumnName(BusinessObject businessObject, String fieldName) { String columnName; if (businessObject.isARelationField(fieldName)) { columnName = relationFieldAnnotator.getJoinColumnName(fieldName); } else { columnName = fieldName; } return columnName; } private void addNamedQuery(final JDefinedClass entityClass, final JAnnotationArrayMember valueArray, final String name, final String content) { final JAnnotationUse nameQueryAnnotation = valueArray.annotate(NamedQuery.class); nameQueryAnnotation.param("name", entityClass.name() + "." + name); nameQueryAnnotation.param("query", content); } public void addPersistenceIdFieldAndAccessors(final JDefinedClass entityClass, String dbVendor) { final JFieldVar idFieldVar = codeGenerator.addField(entityClass, Field.PERSISTENCE_ID, codeGenerator.toJavaClass(FieldType.LONG)); codeGenerator.addAnnotation(idFieldVar, Id.class); JAnnotationUse generateValue = codeGenerator.addAnnotation(idFieldVar, GeneratedValue.class); switch (dbVendor) { case "h2": case "postgres": case "oracle": // This generates the following annotation: // @GeneratedValue(generator = "default_bonita_seq_generator") // @GenericGenerator( // name = "default_bonita_seq_generator", // strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", // parameters = { // @Parameter(name = "sequence_name", value = "hibernate_sequence") // }) // maps to default Hibernate sequence name in database 'hibernate_sequence' generateValue.param("generator", "default_bonita_seq_generator"); JAnnotationUse genericGenerator = codeGenerator.addAnnotation(idFieldVar, GenericGenerator.class); genericGenerator.param("name", "default_bonita_seq_generator"); genericGenerator.param("strategy", SequenceStyleGenerator.class.getName()); final JAnnotationArrayMember parametersAnnotation = genericGenerator.paramArray("parameters"); final JAnnotationUse paramAnnotation = parametersAnnotation.annotate(Parameter.class); paramAnnotation.param("name", "sequence_name"); paramAnnotation.param("value", "hibernate_sequence"); break; case "mysql": case "sqlserver": generateValue.param("strategy", GenerationType.IDENTITY); break; } addAccessors(entityClass, idFieldVar); } public void addPersistenceVersionFieldAndAccessors(final JDefinedClass entityClass) { final JFieldVar versionField = codeGenerator.addField(entityClass, Field.PERSISTENCE_VERSION, codeGenerator.toJavaClass(FieldType.LONG)); codeGenerator.addAnnotation(versionField, Version.class); addAccessors(entityClass, versionField); } public JFieldVar addField(final JDefinedClass entityClass, final Field field, String dbVendor) { JFieldVar fieldVar; if (field.isCollection()) { fieldVar = codeGenerator.addListField(entityClass, field); } else { fieldVar = codeGenerator.addField(entityClass, field.getName(), codeGenerator.toJavaClass(field)); } annotateField(entityClass, field, fieldVar, dbVendor); return fieldVar; } private void annotateField(final JDefinedClass entityClass, final Field field, final JFieldVar fieldVar, String dbVendor) { if (field instanceof SimpleField) { annotateSimpleField((SimpleField) field, fieldVar, dbVendor); } else if (field instanceof RelationField) { annotateRelationField(entityClass, (RelationField) field, fieldVar); } } private void annotateRelationField(final JDefinedClass entityClass, final RelationField rfield, final JFieldVar fieldVar) { relationFieldAnnotator.annotateRelationField(entityClass, rfield, fieldVar); } private void annotateSimpleField(final SimpleField sfield, final JFieldVar fieldVar, String dbVendor) { if (sfield.isCollection()) { final JAnnotationUse collectionAnnotation = codeGenerator.addAnnotation(fieldVar, ElementCollection.class); collectionAnnotation.param("fetch", FetchType.EAGER); codeGenerator.addAnnotation(fieldVar, OrderColumn.class); } final JAnnotationUse columnAnnotation = codeGenerator.addAnnotation(fieldVar, Column.class); columnAnnotation.param("name", sfield.getName().toUpperCase()); columnAnnotation.param("nullable", sfield.isNullable()); if (sfield.getType() == FieldType.DATE) { final JAnnotationUse temporalAnnotation = codeGenerator.addAnnotation(fieldVar, Temporal.class); temporalAnnotation.param("value", TemporalType.TIMESTAMP); } else if (FieldType.TEXT == sfield.getType()) { if (isPostgreSQLDialect(dbVendor)) { // Use custom type that forces TEXT instead of OID for PostgreSQL final JAnnotationUse typeAnnotation = codeGenerator.addAnnotation(fieldVar, org.hibernate.annotations.Type.class); // Use fully qualified class name (SPI doesn't apply to EntityManagerFactory) typeAnnotation.param("type", "org.bonitasoft.engine.persistence.PostgresMaterializedClobType"); } else { // Other databases: standard @Lob annotation codeGenerator.addAnnotation(fieldVar, Lob.class); } } else if (FieldType.STRING == sfield.getType() && sfield.getLength() != null && sfield.getLength() > 0) { columnAnnotation.param("length", sfield.getLength()); } else if (FieldType.LOCALDATE == sfield.getType()) { // 10 = to support ISO-8801 date format: columnAnnotation.param("length", 10); final JAnnotationUse converterAnnotation = codeGenerator.addAnnotation(fieldVar, Convert.class); converterAnnotation.param("converter", DateConverter.class); } else if (FieldType.LOCALDATETIME == sfield.getType()) { columnAnnotation.param("length", 30); final JAnnotationUse converterAnnotation = codeGenerator.addAnnotation(fieldVar, Convert.class); converterAnnotation.param("converter", DateAndTimeConverter.class); } else if (FieldType.OFFSETDATETIME == sfield.getType()) { columnAnnotation.param("length", 30); final JAnnotationUse converterAnnotation = codeGenerator.addAnnotation(fieldVar, Convert.class); converterAnnotation.param("converter", OffsetDateTimeConverter.class); } } public void addAccessors(final JDefinedClass entityClass, final JFieldVar fieldVar) { addAccessors(entityClass, fieldVar, null); } public void addAccessors(final JDefinedClass entityClass, final JFieldVar fieldVar, final Field field) { if (isCollectionField(field)) { codeGenerator.addListSetter(entityClass, fieldVar); } else { codeGenerator.addSetter(entityClass, fieldVar); } final JMethod getter = codeGenerator.addGetter(entityClass, fieldVar); if (field instanceof RelationField && ((RelationField) field).isLazy()) { getter.annotate(LazyLoaded.class); } } protected void addModifiers(final JDefinedClass entityClass, final Field field) { if (isCollectionField(field)) { codeGenerator.addAddMethod(entityClass, field); codeGenerator.addRemoveMethod(entityClass, field); } } private boolean isCollectionField(final Field field) { if (field == null) { return false; } final Boolean collection = field.isCollection(); return collection != null && collection; } private String determineDbVendor() { String dbVendor = System.getProperty("sysprop.bonita.bdm.db.vendor"); if (dbVendor != null) { return dbVendor; } else { // The situation is not normally possible at runtime. // here to allow testing without too much code change LOGGER.error( "sysprop.bonita.bdm.db.vendor is not set. This should not happen at runtime. Defaulting to h2."); return "h2"; } } private boolean isPostgreSQLDialect(String dbVendor) { return dbVendor != null && dbVendor.toLowerCase().equals("postgres"); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/ForeignKeyAnnotator.java ================================================ /** * 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.business.data.generator; import javax.persistence.ForeignKey; import com.sun.codemodel.JAnnotationUse; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JFieldVar; import org.bonitasoft.engine.bdm.model.field.RelationField; /** * @author Laurent Leseigneur */ public class ForeignKeyAnnotator { public void annotateForeignKeyName(JAnnotationUse joinColumn, JDefinedClass entityClass, JFieldVar jFieldVar, RelationField relationField) { JAnnotationUse foreignKey = joinColumn.annotationParam("foreignKey", ForeignKey.class); StringBuilder uniqueForeignKeyName = new StringBuilder() .append("FK_") .append(getReducedUniqueName(entityClass, jFieldVar, relationField)); foreignKey.param("name", uniqueForeignKeyName.toString()); } protected int getReducedUniqueName(JDefinedClass entityClass, JFieldVar jFieldVar, RelationField relationField) { final StringBuilder builder = new StringBuilder(); builder.append(entityClass.name()); builder.append("_"); builder.append(jFieldVar.name()); builder.append("_"); builder.append(relationField.getReference().getSimpleName()); return Math.abs(builder.toString().hashCode()); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/JExprHelper.java ================================================ /** * 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.business.data.generator; import com.sun.codemodel.JExpr; import com.sun.codemodel.JFieldRef; import com.sun.codemodel.JFieldVar; /** * @author Elias Ricken de Medeiros */ public class JExprHelper { /** * Build a {@link JFieldRef} prefixed by {@code 'this.'} * * @param fieldVar field to be referenced via {@code 'this.'} * @return a {@link JFieldRef} prefixed by {@code 'this.'} */ public static JFieldRef buildFieldRef(JFieldVar fieldVar) { return JExpr._this().ref(fieldVar); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/OffsetDateTimeConverter.java ================================================ /** * 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.business.data.generator; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import javax.persistence.AttributeConverter; /** * @author Danila Mazour */ public class OffsetDateTimeConverter implements AttributeConverter { @Override public String convertToDatabaseColumn(OffsetDateTime offsetDateTime) { if (offsetDateTime != null) { return offsetDateTime.withOffsetSameInstant(ZoneOffset.UTC).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); } else { return null; } } @Override public OffsetDateTime convertToEntityAttribute(String dbData) { if (dbData != null) { try { return OffsetDateTime.parse(dbData, DateTimeFormatter.ISO_OFFSET_DATE_TIME); } catch (DateTimeParseException e) { throw new RuntimeException( "Database OffsetDate&Time format must be ISO-8601 compliant yyyy-MM-dd'T'HH:mm:ss(.SSS)Z ", e); } } else { return null; } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/PersistenceUnitBuilder.java ================================================ /** * 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.business.data.generator; import java.io.IOException; import java.io.InputStream; import java.util.HashSet; import java.util.Set; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * @author Romain Bioteau * @author Matthieu Chaffotte */ public class PersistenceUnitBuilder { private final Document document; private final Set classes = new HashSet(); public PersistenceUnitBuilder() throws ParserConfigurationException, SAXException, IOException { document = initializeDefaultPersistenceDocument(); } protected Document initializeDefaultPersistenceDocument() throws ParserConfigurationException, SAXException, IOException { final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setValidating(false); try { documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // security-compliant documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // security-compliant } catch (IllegalArgumentException e) { //ignored, if not supported by the implementation } final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); final InputStream is = PersistenceUnitBuilder.class.getResourceAsStream("persistence.xml"); try { return documentBuilder.parse(is); } finally { is.close(); } } public Document done() { insertClasses(); return document; } protected void insertClasses() { final Node persistenceUnitNode = getPersistenceUnitNode(); final Node refChild = ((Element) persistenceUnitNode).getElementsByTagName("properties").item(0); for (final String classname : classes) { final Element classNode = document.createElement("class"); classNode.setTextContent(classname); persistenceUnitNode.insertBefore(classNode, refChild); } } private Node getPersistenceUnitNode() { final NodeList parentElement = document.getElementsByTagName("persistence-unit"); return parentElement.item(0); } public PersistenceUnitBuilder addClass(final String classname) { classes.add(classname); return this; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/RelationFieldAnnotator.java ================================================ /** * 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.business.data.generator; import static org.apache.commons.lang3.StringUtils.left; import javax.persistence.CascadeType; import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.OrderColumn; import com.fasterxml.jackson.annotation.JsonIgnore; import com.sun.codemodel.JAnnotationArrayMember; import com.sun.codemodel.JAnnotationUse; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JFieldVar; import org.bonitasoft.engine.bdm.model.field.RelationField; import org.bonitasoft.engine.bdm.model.field.RelationField.Type; /** * @author Colin PUY */ public class RelationFieldAnnotator { private final CodeGenerator codeGenerator; private final ForeignKeyAnnotator foreignKeyAnnotator; public RelationFieldAnnotator(final CodeGenerator codeGenerator) { this.codeGenerator = codeGenerator; this.foreignKeyAnnotator = new ForeignKeyAnnotator(); } public void annotateRelationField(final JDefinedClass entityClass, final RelationField field, final JFieldVar fieldVar) { JAnnotationUse relation = null; if (field.isCollection()) { relation = annotateMultipleReference(entityClass, field, fieldVar); } else { relation = annotateSingleReference(entityClass, field, fieldVar); } if (field.isLazy()) { relation.param("fetch", FetchType.LAZY); codeGenerator.addAnnotation(fieldVar, JsonIgnore.class); } else { relation.param("fetch", FetchType.EAGER); } if (field.getType() == Type.COMPOSITION) { relation.param("cascade", CascadeType.ALL); } else if (field.getType() == Type.AGGREGATION) { relation.param("cascade", CascadeType.MERGE); } } private JAnnotationUse annotateSingleReference(JDefinedClass entityClass, final RelationField field, final JFieldVar fieldVar) { JAnnotationUse relation; if (field.getType() == Type.AGGREGATION) { relation = codeGenerator.addAnnotation(fieldVar, ManyToOne.class); } else { relation = codeGenerator.addAnnotation(fieldVar, OneToOne.class); relation.param("orphanRemoval", true); } JAnnotationUse joinColumnAnnotation = addJoinColumn(fieldVar, field.getName()); relation.param("optional", field.isNullable()); foreignKeyAnnotator.annotateForeignKeyName(joinColumnAnnotation, entityClass, fieldVar, field); return relation; } private JAnnotationUse annotateMultipleReference(final JDefinedClass entityClass, final RelationField field, final JFieldVar fieldVar) { JAnnotationUse relation; if (field.getType() == Type.AGGREGATION) { relation = codeGenerator.addAnnotation(fieldVar, ManyToMany.class); addJoinTable(entityClass, field, fieldVar); } else { relation = codeGenerator.addAnnotation(fieldVar, OneToMany.class); relation.param("orphanRemoval", true); final JAnnotationUse joinColumn = addJoinColumn(fieldVar, entityClass.name()); joinColumn.param("nullable", false); } codeGenerator.addAnnotation(fieldVar, OrderColumn.class); return relation; } private void addJoinTable(final JDefinedClass entityClass, final RelationField field, final JFieldVar fieldVar) { final JAnnotationUse joinTable = codeGenerator.addAnnotation(fieldVar, JoinTable.class); joinTable.param("name", getJoinTableName(entityClass.name(), field.getName())); final JAnnotationArrayMember joinColumns = joinTable.paramArray("joinColumns"); final JAnnotationUse nameQueryAnnotation = joinColumns.annotate(JoinColumn.class); nameQueryAnnotation.param("name", getJoinColumnName(entityClass.name())); final JAnnotationArrayMember inverseJoinColumns = joinTable.paramArray("inverseJoinColumns"); final JAnnotationUse a = inverseJoinColumns.annotate(JoinColumn.class); a.param("name", getJoinColumnName(field.getReference().getSimpleName())); } private JAnnotationUse addJoinColumn(final JFieldVar fieldVar, final String columnName) { final JAnnotationUse joinColumn = codeGenerator.addAnnotation(fieldVar, JoinColumn.class); joinColumn.param("name", getJoinColumnName(columnName)); return joinColumn; } /** * Split names to 26 char to avoid joinColumn names longer than 30 char * protected for testing */ protected String getJoinColumnName(final String entityName) { return left(entityName.toUpperCase(), 26) + "_PID"; } /** * Split names to 14 chars max to avoid joinTable names longer than 30 char (oracle restriction). * protected for testing */ protected String getJoinTableName(final String entityName, final String relatedEntityName) { final String name = left(entityName.toUpperCase(), 14); final String refName = left(relatedEntityName.toUpperCase(), 14); return name + "_" + refName; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/client/ClientBDMCodeGenerator.java ================================================ /** * 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.business.data.generator.client; import static org.bonitasoft.engine.bdm.validator.rule.QueryParameterValidationRule.FORBIDDEN_PARAMETER_NAMES; import java.io.Serializable; import java.util.Collection; import java.util.HashMap; import java.util.Map; import com.sun.codemodel.JBlock; import com.sun.codemodel.JCatchBlock; import com.sun.codemodel.JClass; import com.sun.codemodel.JClassAlreadyExistsException; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JExpr; import com.sun.codemodel.JExpression; import com.sun.codemodel.JFieldRef; import com.sun.codemodel.JInvocation; import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import com.sun.codemodel.JTryBlock; import com.sun.codemodel.JVar; import org.bonitasoft.engine.bdm.BDMQueryUtil; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.Query; import org.bonitasoft.engine.business.data.generator.AbstractBDMCodeGenerator; /** * @author Romain Bioteau * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public class ClientBDMCodeGenerator extends AbstractBDMCodeGenerator { private static final String CLIENT_RESOURCES_PACKAGES = "org.bonitasoft.engine.bdm.dao.client.resources"; public ClientBDMCodeGenerator() { super(); } @Override protected void addDAO(final BusinessObject bo, final JDefinedClass entity) throws JClassAlreadyExistsException { final JDefinedClass daoInterface = createDAOInterface(bo, entity); createDAOImpl(bo, entity, daoInterface); } private void createDAOImpl(final BusinessObject bo, final JDefinedClass entity, final JDefinedClass daoInterface) throws JClassAlreadyExistsException { final String daoImplClassName = toDaoImplClassname(bo); final JDefinedClass implClass = addClass(daoImplClassName); implClass._implements(daoInterface); createConstructor(implClass); // Add method for provided queries for (final Query q : BDMQueryUtil.createProvidedQueriesForBusinessObject(bo)) { final JMethod method = createMethodForQuery(entity, implClass, q); addQueryMethodBody(entity.name(), method, q.getName(), entity.fullName(), q.getReturnType()); } // Add method for queries for (final Query q : bo.getQueries()) { final JMethod method = createMethodForQuery(entity, implClass, q); addQueryMethodBody(entity.name(), method, q.getName(), entity.fullName(), q.getReturnType()); } final JMethod method = createMethodForNewInstance(bo, entity, implClass); addNewInstanceMethodBody(method, entity); } private void createConstructor(final JDefinedClass implClass) { final JClass apiSessionJClass = getModel().ref("org.bonitasoft.engine.session.APISession"); implClass.field(JMod.PRIVATE, apiSessionJClass, "session"); final JClass deserializerClass = getModel().ref(CLIENT_RESOURCES_PACKAGES + ".BusinessObjectDeserializer"); implClass.field(JMod.PRIVATE, deserializerClass, "deserializer"); final JClass proxyfierClass = getModel().ref(CLIENT_RESOURCES_PACKAGES + ".proxy.Proxyfier"); implClass.field(JMod.PRIVATE, proxyfierClass, "proxyfier"); final JMethod constructor = implClass.constructor(JMod.PUBLIC); constructor.param(apiSessionJClass, "session"); final JBlock body = constructor.body(); body.assign(JExpr.refthis("session"), JExpr.ref("session")); body.assign(JExpr.refthis("deserializer"), JExpr._new(deserializerClass)); final JClass lazyLoaderClass = getModel().ref(CLIENT_RESOURCES_PACKAGES + ".proxy.LazyLoader"); final JVar lazyLoaderRef = body.decl(lazyLoaderClass, "lazyLoader", JExpr._new(lazyLoaderClass).arg(JExpr.ref("session"))); body.assign(JExpr.refthis("proxyfier"), JExpr._new(proxyfierClass).arg(lazyLoaderRef)); } private void addQueryMethodBody(final String entityName, final JMethod method, final String queryName, final String entityClassName, final String queryReturnType) { final JBlock body = method.body(); final JTryBlock tryBlock = body._try(); final JBlock tryBody = tryBlock.body(); // Get CommandAPI final JClass tenantApiAccessorClass = getModel().ref("org.bonitasoft.engine.api.TenantAPIAccessor"); final JClass commandApiType = getModel().ref("org.bonitasoft.engine.api.CommandAPI"); final JVar commandApiRef = tryBody.decl(commandApiType, "commandApi", tenantApiAccessorClass.staticInvoke("getCommandAPI").arg(JExpr.ref("session"))); // Create command parameters JClass mapClass = getModel().ref(Map.class); mapClass = mapClass.narrow(String.class, Serializable.class); JClass hashMapClass = getModel().ref(HashMap.class); hashMapClass = hashMapClass.narrow(String.class, Serializable.class); final JVar commandParametersRef = tryBody.decl(mapClass, "commandParameters", JExpr._new(hashMapClass)); tryBody.invoke(commandParametersRef, "put").arg(JExpr.lit("queryName")) .arg(JExpr.lit(entityName + "." + queryName)); // Set if should returns a List or a single value boolean isCollection = false; final JClass collectionClass = getModel().ref(Collection.class); if (method.type() instanceof JClass) { isCollection = collectionClass.isAssignableFrom((JClass) method.type()); } tryBody.invoke(commandParametersRef, "put").arg(JExpr.lit("returnsList")).arg(JExpr.lit(isCollection)); if (isCollection) { tryBody.invoke(commandParametersRef, "put").arg(JExpr.lit("returnType")).arg(JExpr.lit(entityClassName)); for (final String param : FORBIDDEN_PARAMETER_NAMES) { tryBody.invoke(commandParametersRef, "put").arg(JExpr.lit(param)).arg(JExpr.ref(param)); } } else { tryBody.invoke(commandParametersRef, "put").arg(JExpr.lit("returnType")).arg(JExpr.lit(queryReturnType)); } // Add query parameters addQueryParameters(method, tryBody, mapClass, hashMapClass, commandParametersRef); // Execute command final JInvocation executeQuery = commandApiRef.invoke("execute").arg("executeBDMQuery") .arg(commandParametersRef); final JClass byteArrayClass = getModel().ref(byte[].class); final JFieldRef deserializerFieldRef = JExpr.ref("deserializer"); final JFieldRef proxyfierFieldRef = JExpr.ref("proxyfier"); JExpression entityClassExpression = null; if (isCollection) { entityClassExpression = JExpr.dotclass(getModel().ref(entityClassName)); } else { entityClassExpression = JExpr.dotclass(getModel().ref(queryReturnType)); } JInvocation deserialize = null; if (isCollection) { deserialize = deserializerFieldRef.invoke("deserializeList").arg(JExpr.cast(byteArrayClass, executeQuery)) .arg(entityClassExpression); tryBody._return(proxyfierFieldRef.invoke("proxify").arg(deserialize)); } else if (queryReturnType.equals(entityClassName)) { deserialize = deserializerFieldRef.invoke("deserialize").arg(JExpr.cast(byteArrayClass, executeQuery)) .arg(entityClassExpression); tryBody._return(proxyfierFieldRef.invoke("proxify").arg(deserialize)); } else { deserialize = deserializerFieldRef.invoke("deserialize").arg(JExpr.cast(byteArrayClass, executeQuery)) .arg(entityClassExpression); tryBody._return(JExpr.cast(getModel().ref(queryReturnType), deserialize)); } final JClass exceptionClass = getModel().ref(Exception.class); final JCatchBlock catchBlock = tryBlock._catch(exceptionClass); final JVar param = catchBlock.param("e"); final JBlock catchBody = catchBlock.body(); final JClass iaeClass = getModel().ref(IllegalArgumentException.class); catchBody._throw(JExpr._new(iaeClass).arg(JExpr.ref(null, param))); } protected void addQueryParameters(final JMethod method, final JBlock body, final JClass mapClass, final JClass hashMapClass, final JVar commandParametersRef) { if (!method.params().isEmpty()) { final JVar queryParametersRef = body.decl(mapClass, "queryParameters", JExpr._new(hashMapClass)); for (final JVar param : method.params()) { if (!FORBIDDEN_PARAMETER_NAMES.contains(param.name())) { body.invoke(queryParametersRef, "put").arg(JExpr.lit(param.name())).arg(param); } } body.invoke(commandParametersRef, "put").arg(JExpr.lit("queryParameters")) .arg(JExpr.cast(getModel().ref(Serializable.class), queryParametersRef)); } } private String toDaoImplClassname(final BusinessObject bo) { return bo.getQualifiedName() + DAO_IMPL_SUFFIX; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/client/ClientBDMJarBuilder.java ================================================ /** * 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.business.data.generator.client; import java.io.File; import java.util.HashSet; import java.util.Set; import javassist.util.proxy.MethodHandler; import com.fasterxml.jackson.core.ObjectCodec; import com.fasterxml.jackson.databind.ObjectMapper; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.Field; import org.bonitasoft.engine.business.data.generator.AbstractBDMJarBuilder; import org.bonitasoft.engine.business.data.generator.CodeGenerationException; import org.bonitasoft.engine.business.data.generator.compiler.JDTCompiler; /** * @author Romain Bioteau */ public class ClientBDMJarBuilder extends AbstractBDMJarBuilder { private ResourcesLoader resourcesLoader; @Deprecated public ClientBDMJarBuilder(final JDTCompiler compiler, ResourcesLoader resourcesLoader) { super(new ClientBDMCodeGenerator(), compiler); this.resourcesLoader = resourcesLoader; } public ClientBDMJarBuilder(ResourcesLoader resourcesLoader) { super(new ClientBDMCodeGenerator()); this.resourcesLoader = resourcesLoader; } @Override protected Set getCompileDependencies() throws ClassNotFoundException { var compileDependencies = new HashSet<>(super.getCompileDependencies()); // Add dependencies to compile client jar compileDependencies.add(JDTCompiler.lookupJarContaining("org.bonitasoft.engine.api.TenantAPIAccessor")); compileDependencies.add(JDTCompiler.lookupJarContaining( "org.bonitasoft.engine.bdm.dao.client.resources.BusinessObjectDeserializer")); compileDependencies.add(JDTCompiler.lookupJarContaining(MethodHandler.class)); compileDependencies.add(JDTCompiler.lookupJarContaining(ObjectMapper.class)); compileDependencies.add(JDTCompiler.lookupJarContaining(ObjectCodec.class)); compileDependencies.add(JDTCompiler.lookupJarContaining(Field.class)); return compileDependencies; } @Override protected void addSourceFilesToDirectory(BusinessObjectModel bom, File directory) throws CodeGenerationException { super.addSourceFilesToDirectory(bom, directory); addClientResources(directory); } private void addClientResources(final File directory) throws CodeGenerationException { try { resourcesLoader.copyJavaFilesToDirectory("org.bonitasoft.engine.bdm.dao.client.resources", directory); } catch (Exception e) { throw new CodeGenerationException("Error when adding compilation dependencies to client jar", e); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/client/ResourcesLoader.java ================================================ /** * 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.business.data.generator.client; import static org.apache.commons.io.FilenameUtils.getName; import static org.bonitasoft.engine.io.IOUtils.createDirectoryIfNotExists; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; public class ResourcesLoader { public void copyJavaFilesToDirectory(String packageName, File directory) throws IOException { List javaFileURLs = getJavaFileURLs(packageName); for (URL url : javaFileURLs) { addJavaFileToDirectory(url, packageName, directory); } } private static List getJavaFileURLs(String packageName) throws IOException { String pattern = "/" + packageName.replace(".", "/") + "/**/*.java"; Resource[] resources = new PathMatchingResourcePatternResolver().getResources(pattern); return getURLs(resources); } private static List getURLs(Resource[] resources) throws IOException { List classNames = new ArrayList<>(); for (Resource resource : resources) { classNames.add(resource.getURL()); } return classNames; } private static void addJavaFileToDirectory(URL javaFile, String originalPackage, File destDirectory) throws IOException { File packageDirectory = createPackageDirectory(javaFile, originalPackage, destDirectory); File destinationFile = new File(packageDirectory, getName(javaFile.toString())); FileUtils.copyURLToFile(javaFile, destinationFile); } private static File createPackageDirectory(URL javaFile, String originalPackage, File destDirectory) { File packageDirectory = new File(destDirectory, packageOf(javaFile.toString(), originalPackage)); return createDirectoryIfNotExists(packageDirectory); } //@VisibleForTesting static String packageOf(final String javaFile, String originalPackage) { String firstChar = StringUtils.substringBefore(originalPackage, "."); String className = javaFile.substring(javaFile.indexOf(firstChar)); if (className.indexOf("!") != -1) {//support osgi classpath url className = className.substring(className.indexOf("!") + 2, className.length()); } return className.substring(0, className.lastIndexOf("/")); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/compiler/CompilationException.java ================================================ /** * 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.business.data.generator.compiler; /** * @author Colin PUY */ public class CompilationException extends Exception { private static final long serialVersionUID = 726068559808420441L; public CompilationException(String message) { super(message); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/compiler/DummyCompilationProgress.java ================================================ /** * 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.business.data.generator.compiler; import org.eclipse.jdt.core.compiler.CompilationProgress; /** * CompilationProgress which do nothing * Used by JdtCompiler * * @author Colin PUY */ public class DummyCompilationProgress extends CompilationProgress { @Override public void worked(int workIncrement, int remainingWork) { } @Override public void setTaskName(String name) { } @Override public boolean isCanceled() { return false; } @Override public void done() { } @Override public void begin(int remainingWork) { } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/compiler/JDTCompiler.java ================================================ /** * 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.business.data.generator.compiler; import static java.lang.String.join; import static java.util.Collections.emptyList; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.PrintWriter; import java.net.URISyntaxException; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.eclipse.jdt.internal.compiler.batch.Main; /** * Compiler based on JDTCompiler * * @author Colin PUY * @author Matthieu Chaffotte */ public class JDTCompiler { private static final String COMPILER_COMPLIANCE_LEVEL = "-17"; //Used to keep parameters name in bytecode to allow reflection in DAO private static final String PARAMETERS_NAME_ARG = "-parameters"; public static File lookupJarContaining(Class clazz) { try { File jarFile = Path.of(clazz.getProtectionDomain().getCodeSource().getLocation().toURI()).toFile(); if (!jarFile.exists()) { throw new IllegalArgumentException("Cannot find jar file for class " + clazz.getName() + ". Resolved jar file: " + jarFile.getAbsolutePath() + " does not exist."); } return jarFile; } catch (URISyntaxException e) { throw new IllegalArgumentException("Cannot find jar file for class " + clazz.getName(), e); } } public static File lookupJarContaining(String className) throws ClassNotFoundException { return lookupJarContaining(Class.forName(className)); } /** * Compile files in output directory using provided classpath * Put null for classpath argument to take current classpath * * @throws CompilationException * if compilation errors occurs */ @Deprecated public void compile(final Collection filesToBeCompiled, final File outputDirectory, File... additionalClasspath) throws CompilationException { compile(filesToBeCompiled, outputDirectory, additionalClasspath); } public void compile(final File srcDirectory, File outputDirectory, File... additionalClasspath) throws CompilationException { Map sourceFiles = listJavaFilesAndClasses(srcDirectory); launchCompiler(buildCommandLineArguments(sourceFiles.values(), outputDirectory, additionalClasspath)); } private Map listJavaFilesAndClasses(File srcFile) { Map result = new HashMap<>(); File[] files = srcFile.listFiles(); if (files == null) { return result; } for (File file : files) { doListJavaFilesAndClasses(file, result, emptyList()); } return result; } private void doListJavaFilesAndClasses(File srcFile, Map files, List parentPath) { if (!srcFile.exists()) { return; } if (srcFile.isFile() && srcFile.getName().endsWith(".java")) { files.put(join(".", getPath(parentPath, srcFile)), srcFile); } else { List path = getPath(parentPath, srcFile); File[] children = srcFile.listFiles(); if (children == null) { return; } for (File file : children) { doListJavaFilesAndClasses(file, files, path); } } } private List getPath(List parentPath, File srcFile) { List path = new ArrayList<>(parentPath); path.add(srcFile.getName().replace(".java", "")); return path; } private String[] buildCommandLineArguments(final Collection files, final File outputDirectory, File... additionalClasspath) { final List arguments = new ArrayList<>(); if (additionalClasspath != null) { arguments.add("-classpath"); arguments.add( Arrays.stream(additionalClasspath) .map(File::getAbsolutePath) .collect(Collectors.joining(File.pathSeparator))); } arguments.add(COMPILER_COMPLIANCE_LEVEL); arguments.add(PARAMETERS_NAME_ARG); arguments.addAll(outputDirectoryArguments(outputDirectory)); arguments.addAll(filesToBeCompiledArguments(files)); return arguments.toArray(new String[0]); } private List filesToBeCompiledArguments(final Collection files) { final List arguments = new ArrayList<>(files.size()); for (final File file : files) { arguments.add(file.getAbsolutePath()); } return arguments; } private List outputDirectoryArguments(final File outputDirectory) { if (outputDirectory == null) { return Collections.emptyList(); } return Arrays.asList("-d", outputDirectory.getAbsolutePath()); } private void launchCompiler(final String[] commandLine) throws CompilationException { final PrintWriter outWriter = new PrintWriter(new ByteArrayOutputStream()); // closing outwriter since we don't want to see compilation out stream outWriter.close(); final ByteArrayOutputStream errorStream = new ByteArrayOutputStream(); try (var errorWriter = new PrintWriter(errorStream)) { doCompilation(commandLine, outWriter, errorStream, errorWriter); } } private void doCompilation(final String[] commandLine, final PrintWriter outWriter, final ByteArrayOutputStream errorStream, final PrintWriter errorWriter) throws CompilationException { final Main mainCompiler = new Main(outWriter, errorWriter, false /* systemExit */, null /* options */, new DummyCompilationProgress()); final boolean succeeded = mainCompiler.compile(commandLine); if (!succeeded) { throw new CompilationException(errorStream.toString()); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/filter/OnlyDAOImplementationFileFilter.java ================================================ /** * 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.business.data.generator.filter; import java.io.File; import org.apache.commons.io.filefilter.AbstractFileFilter; /** * @author Romain Bioteau */ public class OnlyDAOImplementationFileFilter extends AbstractFileFilter { public static final String ORG = "org"; public static final String BONITASOFT = "bonitasoft"; @Override public boolean accept(final File file) { final String name = file.getName(); return name.endsWith("DAOImpl.class") || file.getAbsolutePath() .contains(new StringBuilder().append(ORG).append(File.separator).append(BONITASOFT).toString()); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/filter/WithoutDAOImplementationFileFilter.java ================================================ /** * 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.business.data.generator.filter; import java.io.File; import org.apache.commons.io.filefilter.AbstractFileFilter; /** * @author Romain Bioteau */ public class WithoutDAOImplementationFileFilter extends AbstractFileFilter { @Override public boolean accept(final File file) { return acceptFile(file, ".class") || acceptFile(file, ".java"); } private boolean acceptFile(File file, String fileExtension) { return file.getName().endsWith(fileExtension) && !file.getName().endsWith("DAOImpl" + fileExtension); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/server/ServerBDMCodeGenerator.java ================================================ /** * 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.business.data.generator.server; import static org.bonitasoft.engine.bdm.validator.rule.QueryParameterValidationRule.FORBIDDEN_PARAMETER_NAMES; import java.io.Serializable; import java.util.Collection; import java.util.HashMap; import java.util.Map; import com.sun.codemodel.JBlock; import com.sun.codemodel.JCatchBlock; import com.sun.codemodel.JClass; import com.sun.codemodel.JClassAlreadyExistsException; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JExpr; import com.sun.codemodel.JFieldVar; import com.sun.codemodel.JMethod; import com.sun.codemodel.JMod; import com.sun.codemodel.JTryBlock; import com.sun.codemodel.JVar; import org.bonitasoft.engine.bdm.BDMQueryUtil; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.Query; import org.bonitasoft.engine.business.data.generator.AbstractBDMCodeGenerator; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; /** * @author Romain Bioteau * @author Emmanuel Duchastenier */ public class ServerBDMCodeGenerator extends AbstractBDMCodeGenerator { private static final String SERVER_DAO_PACKAGE_NAME = "server."; public ServerBDMCodeGenerator() { super(); } @Override protected void addDAO(final BusinessObject bo, final JDefinedClass entity) throws JClassAlreadyExistsException { final JDefinedClass daoInterface = createDAOInterface(bo, entity); createDAOImpl(bo, entity, daoInterface); } private void createDAOImpl(final BusinessObject bo, final JDefinedClass entity, final JDefinedClass daoInterface) throws JClassAlreadyExistsException { final String daoImplClassName = toDaoImplClassname(bo); final JDefinedClass implClass = addClass(daoImplClassName); implClass._implements(daoInterface); final JFieldVar businessDataRepository = addConstructor(implClass); // Add method for provided queries for (final Query q : BDMQueryUtil.createProvidedQueriesForBusinessObject(bo)) { final JMethod method = createMethodForQuery(entity, implClass, q); addQueryMethodBody(entity, method, q.getName(), businessDataRepository); } // Add method for queries for (final Query q : bo.getQueries()) { final JMethod method = createMethodForQuery(entity, implClass, q); addQueryMethodBody(entity, method, q.getName(), businessDataRepository); } final JMethod method = createMethodForNewInstance(bo, entity, implClass); addNewInstanceMethodBody(method, entity); } protected JFieldVar addConstructor(final JDefinedClass implClass) { final JClass serviceClass = getModel().ref("org.bonitasoft.engine.business.data.BusinessDataRepository"); final JFieldVar service = addField(implClass, "businessDataRepository", serviceClass); final JMethod constructor = implClass.constructor(JMod.PUBLIC); constructor.param(serviceClass, "businessDataRepository"); final JBlock body = constructor.body(); body.assign(JExpr.refthis("businessDataRepository"), JExpr.ref("businessDataRepository")); return service; } private void addQueryMethodBody(final JDefinedClass entity, final JMethod method, final String queryName, final JFieldVar businessDataRepository) { final String entityName = entity.name(); final JTryBlock tryBlock = method.body()._try(); final JBlock tryBody = tryBlock.body(); JClass queryParameterMapClass = getModel().ref(Map.class); queryParameterMapClass = queryParameterMapClass.narrow(String.class, Serializable.class); JClass hashMapClass = getModel().ref(HashMap.class); hashMapClass = hashMapClass.narrow(String.class, Serializable.class); final JVar queryParameterMap = tryBody.decl(queryParameterMapClass, "queryParameters", JExpr._new(hashMapClass)); for (final JVar param : method.params()) { if (!FORBIDDEN_PARAMETER_NAMES.contains(param.name())) { tryBody.invoke(queryParameterMap, "put").arg(JExpr.lit(param.name())).arg(param); } } boolean isCollection = false; final JClass collectionClass = getModel().ref(Collection.class); if (method.type() instanceof JClass) { isCollection = collectionClass.isAssignableFrom((JClass) method.type()); } if (isCollection) { final JVar[] listParams = method.listParams(); final JVar startIndex = getMethodParam(listParams, BDMQueryUtil.START_INDEX_PARAM_NAME); final JVar maxResults = getMethodParam(listParams, BDMQueryUtil.MAX_RESULTS_PARAM_NAME); if (startIndex == null || maxResults == null) { throw new IllegalArgumentException("Neither 'startIndex' nor 'maxResults' parameters should be null"); } tryBody._return(businessDataRepository.invoke("findListByNamedQuery") .arg(JExpr.lit(entityName + "." + queryName)).arg(JExpr.dotclass(entity)) .arg(queryParameterMap).arg(startIndex).arg(maxResults)); } else { final JClass returnTypeClass = entity.fullName().equals(method.type().fullName()) ? entity : getModel().ref(method.type().fullName()); tryBody._return(businessDataRepository.invoke("findByNamedQuery") .arg(JExpr.lit(entityName + "." + queryName)).arg(JExpr.dotclass(returnTypeClass)) .arg(queryParameterMap)); } final JClass exceptionClass = getModel().ref(Exception.class); final JCatchBlock catchBlock = tryBlock._catch(exceptionClass); final JVar param = catchBlock.param("e"); final JBlock catchBody = catchBlock.body(); final JClass iaeClass = getModel().ref(SBonitaRuntimeException.class); catchBody._throw(JExpr._new(iaeClass).arg(JExpr.ref(null, param))); } protected JVar getMethodParam(final JVar[] params, final String paramName) { for (final JVar jVar : params) { if (paramName.equals(jVar.name())) { return jVar; } } return null; } protected String toDaoImplClassname(final BusinessObject bo) { return serverDAOQualifiedName(bo.getQualifiedName()); } private String serverDAOQualifiedName(final String boQualifiedName) { int pointIdx = boQualifiedName.lastIndexOf('.'); return boQualifiedName.substring(0, pointIdx + 1) + SERVER_DAO_PACKAGE_NAME + boQualifiedName.substring(pointIdx + 1) + DAO_IMPL_SUFFIX; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/java/org/bonitasoft/engine/business/data/generator/server/ServerBDMJarBuilder.java ================================================ /** * 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.business.data.generator.server; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import org.bonitasoft.engine.bdm.BusinessObjectModelConverter; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.business.data.generator.AbstractBDMJarBuilder; import org.bonitasoft.engine.business.data.generator.CodeGenerationException; import org.bonitasoft.engine.business.data.generator.PersistenceUnitBuilder; import org.bonitasoft.engine.business.data.generator.compiler.JDTCompiler; import org.bonitasoft.engine.io.IOUtils; import org.w3c.dom.Document; /** * @author Matthieu Chaffotte * @author Romain Bioteau */ public class ServerBDMJarBuilder extends AbstractBDMJarBuilder { @Deprecated public ServerBDMJarBuilder(final JDTCompiler compiler) { super(new ServerBDMCodeGenerator(), compiler); } public ServerBDMJarBuilder(ServerBDMCodeGenerator generator) { super(generator); } @Override protected void addSourceFilesToDirectory(BusinessObjectModel bom, File directory) throws CodeGenerationException { super.addSourceFilesToDirectory(bom, directory); addPersistenceFile(directory, bom); addBOMFile(directory, bom); } /** * protected for testing - must be changed */ protected void addPersistenceFile(final File directory, final BusinessObjectModel bom) throws CodeGenerationException { try { final List entities = bom.getBusinessObjects(); final PersistenceUnitBuilder builder = new PersistenceUnitBuilder(); for (final BusinessObject businessObject : entities) { builder.addClass(businessObject.getQualifiedName()); } final Document document = builder.done(); final File metaInf = IOUtils.createSubDirectory(directory, "META-INF"); IOUtils.saveDocument(document, new File(metaInf, "persistence.xml")); } catch (Exception e) { throw new CodeGenerationException("Error when generating persistence.xml file", e); } } private void addBOMFile(final File directory, final BusinessObjectModel bom) throws CodeGenerationException { Path file = new File(directory, "bom.xml").toPath(); try { Files.write(file, new BusinessObjectModelConverter().marshall(bom)); } catch (Exception e) { throw new CodeGenerationException("Error when adding business object model metadata to server jar", e); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/main/resources/org/bonitasoft/engine/business/data/generator/persistence.xml ================================================ org.hibernate.jpa.HibernatePersistenceProvider ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/AbstractBDMCodeGeneratorTest.java ================================================ /** * 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.business.data.generator; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO; import static org.bonitasoft.engine.bdm.builder.FieldBuilder.aStringField; import static org.bonitasoft.engine.bdm.builder.FieldBuilder.anIntegerField; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import com.sun.codemodel.JCodeModel; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JMethod; import com.sun.codemodel.JType; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.junit.Test; public class AbstractBDMCodeGeneratorTest { @Test public void should_createMethodForNewInstance_return_jmethod_with_valid_name_and_parameters() throws Exception { final AbstractBDMCodeGenerator abstractBDMCodeGenerator = mock(AbstractBDMCodeGenerator.class); when(abstractBDMCodeGenerator.createMethodForNewInstance(any(BusinessObject.class), any(JDefinedClass.class), any(JDefinedClass.class))) .thenCallRealMethod(); when(abstractBDMCodeGenerator.addMethodSignature(any(JDefinedClass.class), anyString(), any(JType.class))) .thenCallRealMethod(); when(abstractBDMCodeGenerator.getModel()).thenReturn(new JCodeModel()); final BusinessObject businessObject = aBO("org.bonita.Employee") .withField(aStringField("name").notNullable().build()) .withField(anIntegerField("age").build()).build(); final CodeGenerator codeGenerator = new CodeGenerator(); final JMethod jMethod = abstractBDMCodeGenerator.createMethodForNewInstance(businessObject, codeGenerator.addClass("org.bonita.Employee"), codeGenerator.addInterface("org.bonita.EmployeeDAO")); assertThat(jMethod).isNotNull(); assertThat(jMethod.name()).isEqualTo("newInstance"); assertThat(jMethod.params()).hasSize(1); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/BOMBuilder.java ================================================ /** * 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.business.data.generator; import java.util.List; import org.bonitasoft.engine.bdm.BusinessObjectModelConverter; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.SimpleField; public class BOMBuilder { public static BOMBuilder aBOM() { return new BOMBuilder(); } public BusinessObjectModel build() { final SimpleField firstName = new SimpleField(); firstName.setName("firstName"); firstName.setType(FieldType.STRING); final SimpleField lastName = new SimpleField(); lastName.setName("lastName"); lastName.setType(FieldType.STRING); final BusinessObject employee = new BusinessObject(); employee.setQualifiedName("com.company.Employee"); employee.addField(firstName); employee.addField(lastName); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(employee); return bom; } public BusinessObjectModel buildModelWithMultipleBoolean() { final SimpleField booleans = new SimpleField(); booleans.setName("booleanList"); booleans.setType(FieldType.BOOLEAN); booleans.setCollection(true); final BusinessObject booleansBO = new BusinessObject(); booleansBO.setQualifiedName("com.company.Demo"); booleansBO.addField(booleans); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(booleansBO); return bom; } public BusinessObjectModel buildModelWithAllSupportedTypes() { final BusinessObject invoice = new BusinessObject(); invoice.setQualifiedName("com.company.pojo.ComplexInvoice"); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(invoice); return bom; } public BusinessObjectModel buildModelWithConstrainedFields() { final BusinessObject constrained = new BusinessObject(); constrained.setQualifiedName("com.company.pojo.ConstrainedItem"); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(constrained); return bom; } public BusinessObjectModel buildComplex() { final SimpleField firstName = new SimpleField(); firstName.setName("firstName"); firstName.setType(FieldType.STRING); final SimpleField lastName = new SimpleField(); lastName.setName("lastName"); lastName.setType(FieldType.STRING); final BusinessObject employee = new BusinessObject(); employee.setQualifiedName("com.company.Employee"); employee.addField(firstName); employee.addField(lastName); employee.addQuery("getEmployee", "SELECT e FROM Employee e", List.class.getName()); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(employee); return bom; } public BusinessObjectModel buildPerson() { final SimpleField nickNames = new SimpleField(); nickNames.setName("nickNames"); nickNames.setType(FieldType.STRING); nickNames.setLength(Integer.valueOf(15)); nickNames.setCollection(Boolean.TRUE); final BusinessObject employee = new BusinessObject(); employee.setQualifiedName("com.company.Person"); employee.addField(nickNames); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(employee); return bom; } public byte[] buildZip() { final BusinessObjectModelConverter converter = new BusinessObjectModelConverter(); try { return converter.zip(build()); } catch (final Exception e) { throw new RuntimeException("Unable to build BOM zip"); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/CodeGeneratorTest.java ================================================ /** * 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.business.data.generator; import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.persistence.Basic; import javax.persistence.Entity; import javax.persistence.Temporal; import javax.persistence.TemporalType; import com.sun.codemodel.JAnnotationUse; import com.sun.codemodel.JClass; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JFieldVar; import com.sun.codemodel.JMethod; import org.apache.commons.io.FileUtils; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.junit.Before; import org.junit.Test; /** * @author Romain Bioteau */ public class CodeGeneratorTest { private CodeGenerator codeGenerator; @Before public void setUp() { codeGenerator = new CodeGenerator(); } @Test public void shouldAddClass_AddAJDefinedClassInModel_AndReturnIt() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); assertThat(definedClass).isNotNull().isInstanceOf(JDefinedClass.class); assertThat(definedClass.name()).isEqualTo("Entity"); assertThat(definedClass.fullName()).isEqualTo("org.bonitasoft.Entity"); assertThat(codeGenerator.getModel()._getClass("org.bonitasoft.Entity")).isNotNull().isSameAs(definedClass); } @Test(expected = IllegalArgumentException.class) public void shouldAddClass_ThrowAnIllegalArgumentExcpetionForEmptyName() throws Exception { codeGenerator.addClass(""); } @Test(expected = IllegalArgumentException.class) public void shouldAddClass_ThrowAnIllegalArgumentExcpetionForNullName() throws Exception { codeGenerator.addClass(null); } @Test(expected = IllegalArgumentException.class) public void shouldAddClass_ThrowAnIllegalArgumentExcpetionForInvalidName() throws Exception { codeGenerator.addClass("org.bonitasoft*.Entity"); } @Test(expected = IllegalArgumentException.class) public void shouldAddField_ThrowAnIllegalArgumentExcpetionForEmptyName() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); codeGenerator.addField(definedClass, "", String.class); } @Test(expected = IllegalArgumentException.class) public void shouldAddField_ThrowAnIllegalArgumentExcpetionForNullName() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); codeGenerator.addField(definedClass, null, String.class); } @Test(expected = IllegalArgumentException.class) public void shouldAddField_ThrowAnIllegalArgumentExcpetionForInvalidName() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); codeGenerator.addField(definedClass, "enum", String.class); } @Test(expected = IllegalArgumentException.class) public void shouldAddField_ThrowAnIllegalArgumentExcpetionForNullClassType() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); codeGenerator.addField(definedClass, "name", (Class) null); } @Test(expected = IllegalArgumentException.class) public void shouldAddField_ThrowAnIllegalArgumentExcpetionForNullJType() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); codeGenerator.addField(definedClass, "name", (JClass) null); } @Test public void shouldAddPrivateField_FromClass_AddAJVarFieldInDefinedClass_AndReturnIt() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); final JFieldVar privateField = codeGenerator.addField(definedClass, "name", String.class); assertThat(privateField).isNotNull().isInstanceOf(JFieldVar.class); assertThat(privateField.name()).isEqualTo("name"); assertThat(privateField.type().name()).isEqualTo(String.class.getSimpleName()); assertThat(codeGenerator.getModel()._getClass("org.bonitasoft.Entity").fields()).containsEntry("name", privateField); } @Test public void shouldAddPrivateField_FromJType_AddAJVarFieldInDefinedClass_AndReturnIt() throws Exception { final JDefinedClass employeeClass = codeGenerator.addClass("org.bonitasoft.Employee"); final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); final JFieldVar privateField = codeGenerator.addField(definedClass, "employee", employeeClass); assertThat(privateField).isNotNull().isInstanceOf(JFieldVar.class); assertThat(privateField.name()).isEqualTo("employee"); assertThat(privateField.type().name()).isEqualTo("Employee"); assertThat(codeGenerator.getModel()._getClass("org.bonitasoft.Entity").fields()).containsEntry("employee", privateField); final JClass stringList = codeGenerator.getModel().ref(List.class).narrow(String.class); final JFieldVar collectionField = codeGenerator.addField(definedClass, "skills", stringList); assertThat(collectionField).isNotNull(); assertThat(collectionField.type().name()) .isEqualTo(List.class.getSimpleName() + "<" + String.class.getSimpleName() + ">"); } @Test public void shouldAddAnnotation_AddAJAnnotation_AndReturnIt() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); final JAnnotationUse annotation = codeGenerator.addAnnotation(definedClass, Deprecated.class); assertThat(annotation).isNotNull().isInstanceOf(JAnnotationUse.class); assertThat(annotation.getAnnotationClass().fullName()).isEqualTo(Deprecated.class.getName()); } @Test public void shouldAddSetter_AddAJMethodInDefinedClass_AndReturnIt() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); final JFieldVar privateField = codeGenerator.addField(definedClass, "name", String.class); final JMethod setter = codeGenerator.addSetter(definedClass, privateField); assertThat(setter).isNotNull().isInstanceOf(JMethod.class); assertThat(setter.name()).isEqualTo("setName"); assertThat(codeGenerator.getModel()._getClass("org.bonitasoft.Entity").methods()).contains(setter); } @Test public void shouldAddGetter_AddAJMethodInDefinedClass_AndReturnIt() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); final JFieldVar privateField = codeGenerator.addField(definedClass, "name", String.class); final JMethod setter = codeGenerator.addGetter(definedClass, privateField); assertThat(setter).isNotNull().isInstanceOf(JMethod.class); assertThat(setter.name()).isEqualTo("getName"); assertThat(codeGenerator.getModel()._getClass("org.bonitasoft.Entity").methods()).contains(setter); } @Test public void shouldAddGetter_AddAJMethodInDefinedClass_AndReturnIt_ForBoolean() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); final JFieldVar privateField = codeGenerator.addField(definedClass, "married", Boolean.class); final JMethod getter = codeGenerator.addGetter(definedClass, privateField); assertThat(getter).isNotNull().isInstanceOf(JMethod.class); assertThat(getter.name()).isEqualTo("isMarried"); assertThat(codeGenerator.getModel()._getClass("org.bonitasoft.Entity").methods()).contains(getter); } @Test public void shouldCheckAnnotationTarget_IsValid() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); codeGenerator.addAnnotation(definedClass, Deprecated.class); } @Test(expected = IllegalArgumentException.class) public void shouldCheckAnnotationTarget_ThrowIllegalArgumentExceptionForType() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); codeGenerator.addAnnotation(definedClass, Basic.class); } @Test(expected = IllegalArgumentException.class) public void shouldCheckAnnotationTarget_ThrowIllegalArgumentExceptionForField() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); final JFieldVar privateField = codeGenerator.addField(definedClass, "name", String.class); codeGenerator.addAnnotation(privateField, Entity.class); } @Test(expected = IllegalArgumentException.class) public void shouldCheckAnnotationTarget_ThrowIllegalArgumentExceptionForMethod() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); final JFieldVar privateField = codeGenerator.addField(definedClass, "name", String.class); codeGenerator.addAnnotation(privateField, Entity.class); } @Test public void shoulAddInterface_AddInterface_ToADefinedClass() throws Exception { JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); definedClass = codeGenerator.addInterface(definedClass, Serializable.class.getName()); List elements = new ArrayList<>(); definedClass._implements().forEachRemaining(elements::add); assertThat(elements).contains(codeGenerator.getModel().ref(Serializable.class.getName())); } @Test public void shouldGenerate_CreatePojoFile() throws Exception { final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Entity"); final JFieldVar nameField = codeGenerator.addField(definedClass, "name", String.class); final JClass stringList = codeGenerator.getModel().ref(List.class).narrow(String.class); final JClass booleanList = codeGenerator.getModel().ref(List.class).narrow(Boolean.class); final JClass booleanField = codeGenerator.getModel().ref(Boolean.class); final JFieldVar booleanMultipleField = codeGenerator.addField(definedClass, "bools", booleanList); final JFieldVar booleanSingleField = codeGenerator.addField(definedClass, "bool", booleanField); final JFieldVar skillField = codeGenerator.addField(definedClass, "skills", stringList); final JFieldVar dateField = codeGenerator.addField(definedClass, "returnDate", codeGenerator.getModel().ref(Date.class)); final JAnnotationUse tAnnotation = codeGenerator.addAnnotation(dateField, Temporal.class); tAnnotation.param("value", TemporalType.TIMESTAMP); codeGenerator.addGetter(definedClass, nameField); codeGenerator.addSetter(definedClass, nameField); codeGenerator.addAnnotation(definedClass, Entity.class); codeGenerator.addGetter(definedClass, skillField); codeGenerator.addSetter(definedClass, skillField); codeGenerator.addGetter(definedClass, booleanMultipleField); codeGenerator.addSetter(definedClass, booleanMultipleField); codeGenerator.addGetter(definedClass, booleanSingleField); codeGenerator.addSetter(definedClass, booleanSingleField); final File destDir = createTempDirectory("generatedPojo"); try { codeGenerator.generate(destDir); final File rootFolder = new File(destDir, "org" + File.separatorChar + "bonitasoft"); assertThat(rootFolder.listFiles()).isNotEmpty().contains(new File(rootFolder, "Entity.java")); } finally { FileUtils.deleteQuietly(destDir); } } protected File createTempDirectory(final String tmpDirName) throws IOException { final File destDir = File.createTempFile(tmpDirName, null); destDir.delete(); destDir.mkdirs(); return destDir; } @Test public void should_add_and_remove_generate_on_boolean_list() throws Exception { //given final JDefinedClass definedClass = codeGenerator.addClass("org.bonitasoft.Demo"); SimpleField booleanField = new SimpleField(); booleanField.setName("booleanField"); booleanField.setType(FieldType.BOOLEAN); booleanField.setCollection(true); SimpleField stringField = new SimpleField(); stringField.setName("stringField"); stringField.setType(FieldType.STRING); stringField.setCollection(true); //when codeGenerator.addAddMethod(definedClass, booleanField); codeGenerator.addRemoveMethod(definedClass, booleanField); codeGenerator.addAddMethod(definedClass, stringField); codeGenerator.addRemoveMethod(definedClass, stringField); //then final File tempDirectory = createTempDirectory("generatedPojo"); try { codeGenerator.generate(tempDirectory); final File rootFolder = new File(tempDirectory, "org" + File.separatorChar + "bonitasoft"); assertThat(rootFolder.listFiles()).isNotEmpty().contains(new File(rootFolder, "Demo.java")); } finally { FileUtils.deleteQuietly(tempDirectory); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/CompilableCode.java ================================================ /** * 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.business.data.generator; import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import org.bonitasoft.engine.bdm.Entity; /** * @author Romain Bioteau */ public abstract class CompilableCode { protected void assertCompilationSuccessful(final File sourceFileToCompile) { final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); final StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); final Iterable compUnits = fileManager.getJavaFileObjects(sourceFileToCompile); final List optionList = new ArrayList(); String javaxPersistencefilePath = findJarPath(javax.persistence.Basic.class); String bdmEntityfilePath = findJarPath(Entity.class); optionList .addAll(Arrays.asList("-classpath", javaxPersistencefilePath + File.pathSeparator + bdmEntityfilePath)); final Boolean compiled = compiler.getTask(null, fileManager, null, optionList, null, compUnits).call(); assertThat(compiled).isTrue(); } private String findJarPath(final Class clazzToFind) { URL jarURL = clazzToFind.getResource(clazzToFind.getSimpleName() + ".class"); String jarPath = jarURL.getFile(); if (jarPath.indexOf("!") == -1) { jarPath = getDotClassParentPath(jarPath); } else { jarPath = jarPath.split("!")[0]; } return jarPath; } private String getDotClassParentPath(final String completeClassUrl) { int indexOf = completeClassUrl.indexOf(Entity.class.getName().replace('.', File.separatorChar)); if (indexOf != -1) { return completeClassUrl.substring(0, indexOf); } File f = new File(completeClassUrl); if (f.exists()) { return f.getParent(); } return completeClassUrl; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/DateAndTimeConverterTest.java ================================================ /** * 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.business.data.generator; import static org.assertj.core.api.Assertions.assertThat; import java.time.LocalDateTime; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; /** * @author Danila Mazour */ public class DateAndTimeConverterTest { @Test public void convertToDatabaseColumn_should_generate_a_string_ISO_compliant() { DateAndTimeConverter dateAndTimeConverter = new DateAndTimeConverter(); LocalDateTime localDateTime = LocalDateTime.of(2017, 2, 22, 13, 00, 00); String dateAndTimeString = dateAndTimeConverter.convertToDatabaseColumn(localDateTime); assertThat(dateAndTimeString).isNotNull(); assertThat(dateAndTimeString).isEqualTo("2017-02-22T13:00:00"); } @Test public void convertToEntityAttribute_should_generate_the_correct_attribute() { DateAndTimeConverter dateAndTimeConverter = new DateAndTimeConverter(); String dateAndTimeString = "2011-11-26T15:02:00.654"; LocalDateTime localDateTime = dateAndTimeConverter.convertToEntityAttribute(dateAndTimeString); assertThat(localDateTime).isNotNull(); assertThat(localDateTime.getYear()).isEqualTo(2011); assertThat(localDateTime.getMonthValue()).isEqualTo(11); assertThat(localDateTime.getDayOfMonth()).isEqualTo(26); assertThat(localDateTime.getHour()).isEqualTo(15); assertThat(localDateTime.getMinute()).isEqualTo(2); assertThat(localDateTime.getSecond()).isEqualTo(0); assertThat(localDateTime.getNano()).isEqualTo(654000000); } @Test public void dateAndTimeConverter_should_generate_the_same_object_in_and_out() { DateAndTimeConverter dateAndTimeConverter = new DateAndTimeConverter(); LocalDateTime localDateTime = LocalDateTime.of(1961, 4, 12, 6, 7, 00); LocalDateTime resultLocalDateTime = dateAndTimeConverter .convertToEntityAttribute(dateAndTimeConverter.convertToDatabaseColumn(localDateTime)); assertThat(resultLocalDateTime).isNotNull(); assertThat(resultLocalDateTime).isEqualTo(localDateTime); } @Test public void convertToDatabaseColumn_should_return_null_when_given_a_null_string() { DateAndTimeConverter dateAndTimeConverter = new DateAndTimeConverter(); String localDateinString = dateAndTimeConverter.convertToDatabaseColumn(null); assertThat(localDateinString).isNull(); } @Test public void convertToEntityAttribute_should_return_null_when_given_a_null_object() { DateAndTimeConverter dateAndTimeConverter = new DateAndTimeConverter(); LocalDateTime localDate = dateAndTimeConverter.convertToEntityAttribute(null); assertThat(localDate).isNull(); } @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void convertToEntityAttribute_should_throw_an_exception_when_given_a_faulty_string() { DateAndTimeConverter dateAndTimeConverter = new DateAndTimeConverter(); expectedException.expect(RuntimeException.class); expectedException .expectMessage("Database date & time format must be ISO-8601 compliant ( yyyy-mm-ddThh:mm:ss )"); LocalDateTime localDate = dateAndTimeConverter.convertToEntityAttribute("LaLaLand"); } @Test public void convertToDatabaseColumn_should_account_up_to_nanoseconds_in_the_generated_string() throws Exception { DateAndTimeConverter dateAndTimeConverter = new DateAndTimeConverter(); LocalDateTime localDateTime = LocalDateTime.of(2017, 2, 28, 17, 42, 12, 9649); String dateAndTimeString = dateAndTimeConverter.convertToDatabaseColumn(localDateTime); assertThat(dateAndTimeString).isNotNull(); assertThat(dateAndTimeString).isEqualTo("2017-02-28T17:42:12.000009649"); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/DateConverterTest.java ================================================ /** * 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.business.data.generator; import static org.assertj.core.api.Assertions.assertThat; import java.time.LocalDate; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; /** * @author Danila Mazour */ public class DateConverterTest { @Test public void convertToDatabaseColumn_should_generate_a_string_ISO_compliant() { DateConverter dateConverter = new DateConverter(); LocalDate localDate = LocalDate.of(2001, 9, 11); String localDateinString = dateConverter.convertToDatabaseColumn(localDate); assertThat(localDateinString).isNotNull(); assertThat(localDateinString).isEqualTo("2001-09-11"); } @Test public void convertToEntityAttribute_should_generate_the_correct_attribute() { DateConverter dateConverter = new DateConverter(); String localDateinString = "1993-01-19"; LocalDate localDate = dateConverter.convertToEntityAttribute(localDateinString); assertThat(localDate).isNotNull(); assertThat(localDate.getMonthValue()).isEqualTo(01); assertThat(localDate.getYear()).isEqualTo(1993); assertThat(localDate.getDayOfMonth()).isEqualTo(19); } @Test public void dateConverter_should_generate_the_same_object_in_and_out() { DateConverter dateConverter = new DateConverter(); LocalDate localDate = LocalDate.of(2017, 02, 24); LocalDate localDateResult = dateConverter .convertToEntityAttribute(dateConverter.convertToDatabaseColumn(localDate)); assertThat(localDateResult).isNotNull(); assertThat(localDateResult).isEqualTo(localDate); } @Test public void convertToDatabaseColumn_should_return_null_when_given_a_null_string() { DateConverter dateConverter = new DateConverter(); String localDateinString = dateConverter.convertToDatabaseColumn(null); assertThat(localDateinString).isNull(); } @Test public void convertToEntityAttribute_should_return_null_when_given_a_null_object() { DateConverter dateConverter = new DateConverter(); LocalDate localDate = dateConverter.convertToEntityAttribute(null); assertThat(localDate).isNull(); } @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void convertToEntityAttribute_should_throw_an_exception_when_given_a_faulty_string() { DateConverter dateConverter = new DateConverter(); expectedException.expect(RuntimeException.class); expectedException.expectMessage("Database date format must be ISO-8601 compliant ( yyyy-mm-dd )"); LocalDate localDate = dateConverter.convertToEntityAttribute("Logan"); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/EntityCodeGeneratorTest.java ================================================ /** * 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.business.data.generator; import static org.assertj.core.api.Assertions.assertThat; import java.io.StringWriter; import java.io.Writer; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.persistence.Column; import javax.persistence.Convert; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Lob; import javax.persistence.NamedQueries; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.Version; import com.sun.codemodel.JAnnotationUse; import com.sun.codemodel.JAnnotationValue; import com.sun.codemodel.JClass; import com.sun.codemodel.JClassAlreadyExistsException; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JFieldVar; import com.sun.codemodel.JFormatter; import com.sun.codemodel.JMethod; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.Field; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.RelationField; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.hibernate.annotations.GenericGenerator; import org.junit.Before; import org.junit.Test; public class EntityCodeGeneratorTest { private static final String EMPLOYEE_QUALIFIED_NAME = "org.bonitasoft.hr.Employee"; private static final String UNIQUE_RELATION_QUALIFIED_NAME = "org.bonitasoft.hr.Unique"; private EntityCodeGenerator entityCodeGenerator; private CodeGenerator codeGenerator; private BusinessObjectModel bom = new BusinessObjectModel(); @Before public void setUp() { codeGenerator = new CodeGenerator(); entityCodeGenerator = new EntityCodeGenerator(codeGenerator, bom); } @Test public void shouldAddEntity_CreateAValidEntityFromBusinessObject() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addEntity(employeeBO); final JDefinedClass definedClass = codeGenerator.getModel()._getClass(employeeBO.getQualifiedName()); assertThat(definedClass).isNotNull(); assertThat(definedClass._package().name()).isEqualTo("org.bonitasoft.hr"); final Iterator it = definedClass._implements(); final JClass jClass = it.next(); assertThat(jClass.fullName()).isEqualTo(org.bonitasoft.engine.bdm.Entity.class.getName()); assertThat(definedClass.annotations()).hasSize(3); final Iterator iterator = definedClass.annotations().iterator(); final JAnnotationUse entityAnnotation = iterator.next(); assertThat(entityAnnotation.getAnnotationClass().fullName()).isEqualTo(Entity.class.getName()); assertThat(entityAnnotation.getAnnotationMembers()).hasSize(1); final JAnnotationUse tableAnnotation = iterator.next(); assertThat(tableAnnotation.getAnnotationClass().fullName()).isEqualTo(Table.class.getName()); assertThat(tableAnnotation.getAnnotationMembers()).hasSize(1); } @Test(expected = IllegalArgumentException.class) public void shouldAddEntity_ThrowAnIllegalArgumentException() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName("java.lang.String"); entityCodeGenerator.addEntity(employeeBO); } @Test public void shouldAddNamedQueries_InDefinedClass() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); employeeBO.addQuery("getEmployees", "SELECT e FROM Employee e", List.class.getName()); final JDefinedClass entity = entityCodeGenerator.addEntity(employeeBO); final JAnnotationUse namedQueriesAnnotation = getAnnotation(entity, NamedQueries.class.getName()); assertThat(namedQueriesAnnotation).isNotNull(); final Map annotationMembers = namedQueriesAnnotation.getAnnotationMembers(); assertThat(annotationMembers).hasSize(1); } @Test public void shouldAddPersistenceIdFieldAndAccessors_AddPersistenceId() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addPersistenceIdFieldAndAccessors(definedClass, "postgres"); final JFieldVar idFieldVar = definedClass.fields().get(Field.PERSISTENCE_ID); assertThat(idFieldVar).isNotNull(); assertThat(idFieldVar.type()).isEqualTo(codeGenerator.getModel().ref(Long.class.getName())); assertThat(idFieldVar.annotations()).hasSize(3); final Iterator iterator = idFieldVar.annotations().iterator(); JAnnotationUse annotationUse = iterator.next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Id.class.getName()); annotationUse = iterator.next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(GeneratedValue.class.getName()); annotationUse = iterator.next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(GenericGenerator.class.getName()); } @Test public void should_add_sequence_strategy_to_fields_with_generatedValue_in_h2() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addPersistenceIdFieldAndAccessors(definedClass, "h2"); final JFieldVar idFieldVar = definedClass.fields().get(Field.PERSISTENCE_ID); assertThat(idFieldVar.annotations()).hasSize(3); final Iterator iterator = idFieldVar.annotations().iterator(); iterator.next(); JAnnotationUse annotationUse = iterator.next(); assertThat(getAnnotationParamValue(annotationUse, "generator")).isEqualTo("default_bonita_seq_generator"); } @Test public void should_add_sequence_strategy_to_fields_with_generatedValue_in_postgres() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addPersistenceIdFieldAndAccessors(definedClass, "postgres"); final JFieldVar idFieldVar = definedClass.fields().get(Field.PERSISTENCE_ID); assertThat(idFieldVar.annotations()).hasSize(3); final Iterator iterator = idFieldVar.annotations().iterator(); iterator.next(); JAnnotationUse annotationUse = iterator.next(); assertThat(getAnnotationParamValue(annotationUse, "generator")).isEqualTo("default_bonita_seq_generator"); } @Test public void should_add_sequence_strategy_to_fields_with_generatedValue_in_oracle() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addPersistenceIdFieldAndAccessors(definedClass, "oracle"); final JFieldVar idFieldVar = definedClass.fields().get(Field.PERSISTENCE_ID); assertThat(idFieldVar.annotations()).hasSize(3); final Iterator iterator = idFieldVar.annotations().iterator(); iterator.next(); JAnnotationUse annotationUse = iterator.next(); assertThat(getAnnotationParamValue(annotationUse, "generator")).isEqualTo("default_bonita_seq_generator"); } @Test public void should_add_identity_strategy_to_fields_with_generatedValue_in_mysql() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addPersistenceIdFieldAndAccessors(definedClass, "mysql"); final JFieldVar idFieldVar = definedClass.fields().get(Field.PERSISTENCE_ID); assertThat(idFieldVar.annotations()).hasSize(2); final Iterator iterator = idFieldVar.annotations().iterator(); iterator.next(); JAnnotationUse annotationUse = iterator.next(); assertThat(getAnnotationParamValue(annotationUse, "strategy")) .isEqualTo("javax.persistence.GenerationType.IDENTITY"); } @Test public void should_add_identity_strategy_to_fields_with_generatedValue_in_SQLServer() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addPersistenceIdFieldAndAccessors(definedClass, "sqlserver"); final JFieldVar idFieldVar = definedClass.fields().get(Field.PERSISTENCE_ID); assertThat(idFieldVar.annotations()).hasSize(2); final Iterator iterator = idFieldVar.annotations().iterator(); iterator.next(); JAnnotationUse annotationUse = iterator.next(); assertThat(getAnnotationParamValue(annotationUse, "strategy")) .isEqualTo("javax.persistence.GenerationType.IDENTITY"); } @Test public void shouldAddAccessors_AddAccessorMethods_InDefinedClass() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final SimpleField nameField = new SimpleField(); nameField.setName("name"); nameField.setType(FieldType.STRING); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); final JFieldVar basicField = entityCodeGenerator.addField(definedClass, nameField, "h2"); entityCodeGenerator.addAccessors(definedClass, basicField); assertThat(definedClass.methods()).hasSize(2); final JMethod setter = (JMethod) definedClass.methods().toArray()[0]; assertThat(setter.name()).isEqualTo("setName"); final JMethod getter = (JMethod) definedClass.methods().toArray()[1]; assertThat(getter.name()).isEqualTo("getName"); } @Test public void shouldAddBasicField_AddAFieldWithTemporalAnnotation_InDefinedClass() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final SimpleField nameField = new SimpleField(); nameField.setName("name"); nameField.setType(FieldType.DATE); nameField.setNullable(Boolean.FALSE); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addField(definedClass, nameField, "h2"); final JFieldVar nameFieldVar = definedClass.fields().get("name"); assertThat(nameFieldVar).isNotNull(); assertThat(nameFieldVar.type()).isEqualTo(codeGenerator.getModel().ref(Date.class.getName())); assertThat(nameFieldVar.annotations()).hasSize(2); final Iterator iterator = nameFieldVar.annotations().iterator(); JAnnotationUse annotationUse = iterator.next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Column.class.getName()); final String name = getAnnotationParamValue(annotationUse, "name"); assertThat(name).isNotNull().isEqualTo("NAME"); final String nullable = getAnnotationParamValue(annotationUse, "nullable"); assertThat(nullable).isNotNull().isEqualTo("false"); annotationUse = iterator.next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Temporal.class.getName()); assertThat(annotationUse.getAnnotationMembers()).hasSize(1); final String value = getAnnotationParamValue(annotationUse, "value"); assertThat(value).isNotNull().isEqualTo("javax.persistence.TemporalType.TIMESTAMP"); } private String getAnnotationParamValue(final JAnnotationUse annotationUse, final String paramName) { final Map annotationParams = annotationUse.getAnnotationMembers(); final JAnnotationValue nullableValue = annotationParams.get(paramName); final StringWriter writer = new StringWriter(); nullableValue.generate(new JFormatter(writer)); return writer.toString().replace("\"", ""); } @Test public void shouldAddBooleanAccessors_AddAccessorMethods_InDefinedClass() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final SimpleField foundField = new SimpleField(); foundField.setName("found"); foundField.setType(FieldType.BOOLEAN); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); final JFieldVar basicField = entityCodeGenerator.addField(definedClass, foundField, "h2"); entityCodeGenerator.addAccessors(definedClass, basicField); assertThat(definedClass.methods()).hasSize(2); final JMethod setter = (JMethod) definedClass.methods().toArray()[0]; assertThat(setter.name()).isEqualTo("setFound"); final JMethod getter = (JMethod) definedClass.methods().toArray()[1]; assertThat(getter.name()).isEqualTo("isFound"); } @Test public void shouldAddColumnField_CreatePrimitiveAttribute_InDefinedClass() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final SimpleField nameField = new SimpleField(); nameField.setName("name"); nameField.setType(FieldType.STRING); nameField.setLength(Integer.valueOf(45)); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addField(definedClass, nameField, "h2"); final JFieldVar nameFieldVar = definedClass.fields().get("name"); assertThat(nameFieldVar).isNotNull(); assertThat(nameFieldVar.type()).isEqualTo(codeGenerator.getModel().ref(String.class.getName())); assertThat(nameFieldVar.annotations()).hasSize(1); final JAnnotationUse annotationUse = nameFieldVar.annotations().iterator().next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Column.class.getName()); final String name = getAnnotationParamValue(annotationUse, "name"); assertThat(name).isNotNull().isEqualTo("NAME"); final String nullable = getAnnotationParamValue(annotationUse, "nullable"); assertThat(nullable).isNotNull().isEqualTo("true"); final String length = getAnnotationParamValue(annotationUse, "length"); assertThat(length).isNotNull().isEqualTo("45"); } @Test public void shouldAddColumnField() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final SimpleField nameField = new SimpleField(); nameField.setName("description"); nameField.setType(FieldType.TEXT); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addField(definedClass, nameField, "h2"); final JFieldVar nameFieldVar = definedClass.fields().get("description"); assertTextField(nameFieldVar); } private void assertTextField(final JFieldVar fieldVar) { final Collection annotations = fieldVar.annotations(); assertThat(annotations).hasSize(2); final Iterator iterator = annotations.iterator(); JAnnotationUse annotationUse = iterator.next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Column.class.getName()); annotationUse = iterator.next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Lob.class.getName()); } @Test public void shouldAddColumnField_WithCustomTypeForPostgreSQL() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final SimpleField descriptionField = new SimpleField(); descriptionField.setName("description"); descriptionField.setType(FieldType.TEXT); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addField(definedClass, descriptionField, "postgres"); final JFieldVar descriptionFieldVar = definedClass.fields().get("description"); assertTextFieldForPostgreSQL(descriptionFieldVar); } @Test public void shouldAddColumnField_WithLobForMySQL() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final SimpleField descriptionField = new SimpleField(); descriptionField.setName("description"); descriptionField.setType(FieldType.TEXT); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addField(definedClass, descriptionField, "mysql"); final JFieldVar descriptionFieldVar = definedClass.fields().get("description"); // For non-PostgreSQL databases, should still use @Lob assertTextField(descriptionFieldVar); } private void assertTextFieldForPostgreSQL(final JFieldVar fieldVar) { final Collection annotations = fieldVar.annotations(); assertThat(annotations).hasSize(2); final Iterator iterator = annotations.iterator(); JAnnotationUse annotationUse = iterator.next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Column.class.getName()); annotationUse = iterator.next(); // For PostgreSQL, should use @Type annotation instead of @Lob assertThat(annotationUse.getAnnotationClass().fullName()) .isEqualTo(org.hibernate.annotations.Type.class.getName()); // Verify the type parameter value assertThat(annotationUse.getAnnotationMembers()).hasSize(1); final String typeValue = getAnnotationParamValue(annotationUse, "type"); assertThat(typeValue).isEqualTo("org.bonitasoft.engine.persistence.PostgresMaterializedClobType"); } @Test public void shouldAddPersistenceVersionFieldAndAccessors_AddPersistenceVersion() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addPersistenceVersionFieldAndAccessors(definedClass); final JFieldVar versionFieldVar = definedClass.fields().get(Field.PERSISTENCE_VERSION); assertThat(versionFieldVar).isNotNull(); assertThat(versionFieldVar.type()).isEqualTo(codeGenerator.getModel().ref(Long.class.getName())); assertThat(versionFieldVar.annotations()).hasSize(1); final Iterator iterator = versionFieldVar.annotations().iterator(); final JAnnotationUse annotationUse = iterator.next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Version.class.getName()); } public JAnnotationUse getAnnotation(final JDefinedClass definedClass, final String annotationClassName) { final Iterator iterator = definedClass.annotations().iterator(); JAnnotationUse annotation = null; while (annotation == null && iterator.hasNext()) { final JAnnotationUse next = iterator.next(); if (next.getAnnotationClass().fullName().equals(annotationClassName)) { annotation = next; } } return annotation; } @Test public void should_add_real_columnName_in_unique_key() throws Exception { //given final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final BusinessObject uniqueRelationBO = new BusinessObject(); uniqueRelationBO.setQualifiedName(UNIQUE_RELATION_QUALIFIED_NAME); final RelationField relationField = new RelationField(); relationField.setName("uniqueRelation"); relationField.setReference(uniqueRelationBO); employeeBO.addField(relationField); employeeBO.addUniqueConstraint("unique_constraint", "uniqueRelation"); //when final JDefinedClass jDefinedClass = entityCodeGenerator.addEntity(employeeBO); //then final Writer writer = new StringWriter(); final JAnnotationUse tableAnnotation = getAnnotation(jDefinedClass, Table.class.getCanonicalName()); assertThat(tableAnnotation).as("should have Table Annotation").isNotNull(); tableAnnotation.generate(new JFormatter(writer)); final String EOL = System.getProperty("line.separator"); assertThat(writer.toString()).as("should rename relation field to real column name") .isEqualTo("@javax.persistence.Table(name = \"EMPLOYEE\", uniqueConstraints = {" + EOL + " @javax.persistence.UniqueConstraint(name = \"UNIQUE_CONSTRAINT\", columnNames = {" + EOL + " \"UNIQUERELATION_PID\"" + EOL + " })" + EOL + "})"); } @Test public void annotateSimpleField_should_generate_the_correct_annotation_for_localDate_type() throws JClassAlreadyExistsException { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); SimpleField nameField = new SimpleField(); nameField.setName("name"); nameField.setType(FieldType.LOCALDATE); nameField.setNullable(Boolean.FALSE); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addField(definedClass, nameField, "h2"); final JFieldVar nameFieldVar = definedClass.fields().get("name"); assertThat(nameFieldVar).isNotNull(); assertThat(nameFieldVar.type()).isEqualTo(codeGenerator.getModel().ref(LocalDate.class.getName())); assertThat(nameFieldVar.annotations().size()).isEqualTo(2); final Iterator iterator = nameFieldVar.annotations().iterator(); JAnnotationUse annotationUse = iterator.next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Column.class.getName()); final String name = getAnnotationParamValue(annotationUse, "name"); assertThat(name).isNotNull().isEqualTo("NAME"); final String nullable = getAnnotationParamValue(annotationUse, "nullable"); assertThat(nullable).isNotNull().isEqualTo("false"); final int length = Integer.parseInt(getAnnotationParamValue(annotationUse, "length")); assertThat(length).isEqualTo(10); annotationUse = iterator.next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Convert.class.getName()); assertThat(annotationUse.getAnnotationMembers()).hasSize(1); final String value = getAnnotationParamValue(annotationUse, "converter"); assertThat(value).isNotNull().isEqualTo("org.bonitasoft.engine.business.data.generator.DateConverter.class"); } @Test public void annotateSimpleField_should_generate_the_correct_annotation_for_localDateAndTime_type() throws JClassAlreadyExistsException { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); SimpleField nameField = new SimpleField(); nameField.setName("name"); nameField.setType(FieldType.LOCALDATETIME); nameField.setNullable(Boolean.FALSE); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addField(definedClass, nameField, "h2"); final JFieldVar nameFieldVar = definedClass.fields().get("name"); assertThat(nameFieldVar).isNotNull(); assertThat(nameFieldVar.type()).isEqualTo(codeGenerator.getModel().ref(LocalDateTime.class.getName())); assertThat(nameFieldVar.annotations().size()).isEqualTo(2); final Iterator iterator = nameFieldVar.annotations().iterator(); JAnnotationUse annotationUse = iterator.next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Column.class.getName()); final String name = getAnnotationParamValue(annotationUse, "name"); assertThat(name).isNotNull().isEqualTo("NAME"); final String nullable = getAnnotationParamValue(annotationUse, "nullable"); assertThat(nullable).isNotNull().isEqualTo("false"); final int length = Integer.parseInt(getAnnotationParamValue(annotationUse, "length")); assertThat(length).isEqualTo(30); annotationUse = iterator.next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Convert.class.getName()); assertThat(annotationUse.getAnnotationMembers()).hasSize(1); final String value = getAnnotationParamValue(annotationUse, "converter"); assertThat(value).isNotNull() .isEqualTo("org.bonitasoft.engine.business.data.generator.DateAndTimeConverter.class"); } @Test public void annotateSimpleField_should_generate_the_correct_annotation_for_OffsetDateAndTime_type() throws JClassAlreadyExistsException { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); SimpleField nameField = new SimpleField(); nameField.setName("reunion"); nameField.setType(FieldType.OFFSETDATETIME); nameField.setNullable(Boolean.FALSE); final JDefinedClass definedClass = codeGenerator.addClass(EMPLOYEE_QUALIFIED_NAME); entityCodeGenerator.addField(definedClass, nameField, "h2"); final JFieldVar nameFieldVar = definedClass.fields().get("reunion"); assertThat(nameFieldVar).isNotNull(); assertThat(nameFieldVar.type()).isEqualTo(codeGenerator.getModel().ref(OffsetDateTime.class.getName())); assertThat(nameFieldVar.annotations().size()).isEqualTo(2); final Iterator iterator = nameFieldVar.annotations().iterator(); JAnnotationUse annotationUse = iterator.next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Column.class.getName()); final String name = getAnnotationParamValue(annotationUse, "name"); assertThat(name).isNotNull().isEqualTo("REUNION"); final String nullable = getAnnotationParamValue(annotationUse, "nullable"); assertThat(nullable).isNotNull().isEqualTo("false"); final int length = Integer.parseInt(getAnnotationParamValue(annotationUse, "length")); assertThat(length).isEqualTo(30); annotationUse = iterator.next(); assertThat(annotationUse.getAnnotationClass().fullName()).isEqualTo(Convert.class.getName()); assertThat(annotationUse.getAnnotationMembers()).hasSize(1); final String value = getAnnotationParamValue(annotationUse, "converter"); assertThat(value).isNotNull() .isEqualTo("org.bonitasoft.engine.business.data.generator.OffsetDateTimeConverter.class"); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/EntityPojo.java ================================================ /** * 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.business.data.generator; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import org.bonitasoft.engine.bdm.Entity; @NamedQueries({ @NamedQuery(name = "EntityPojo.findByFirstName", query = "SELECT e\nFROM Employee e\nWHERE e.firstName= :firstName\nORDER BY e.persistenceId"), @NamedQuery(name = "EntityPojo.find", query = "SELECT e\nFROM Employee e\nORDER BY e.persistenceId"), @NamedQuery(name = "EntityPojo.countForFind", query = "SELECT count(e)\nFROM Employee e\n") }) public class EntityPojo implements Entity { private static final long serialVersionUID = 1L; private String name; private Boolean bool; private Date date; private List numbers; @OneToOne(cascade = CascadeType.MERGE) private Entity aggregationEntity; @OneToOne(cascade = CascadeType.ALL) private Entity compositionEntity; @OneToOne(cascade = CascadeType.ALL) private Entity nullChildEntity; @OneToMany(cascade = CascadeType.MERGE) private List aggregationEntities; @OneToMany(cascade = CascadeType.ALL) private List compositionEntities; private Long persistenceId; public EntityPojo(final Long persistenceId) { this.persistenceId = persistenceId; aggregationEntities = new ArrayList<>(); compositionEntities = new ArrayList<>(); } public EntityPojo() { aggregationEntities = new ArrayList<>(); compositionEntities = new ArrayList<>(); } @Override public Long getPersistenceId() { return persistenceId; } @Override public Long getPersistenceVersion() { return 2L; } public Date getDate() { return date; } public void setDate(final Date date) { this.date = date; } public Boolean getBool() { return bool; } public void setBool(final Boolean bool) { this.bool = bool; } public String getName() { return name; } public void setName(final String name) { this.name = name; } public List getNumbers() { return numbers; } public void setNumbers(final List numbers) { this.numbers = numbers; } public Entity getCompositionEntity() { return compositionEntity; } public void setCompositionEntity(final Entity compositionEntity) { this.compositionEntity = compositionEntity; } public Entity getAggregationEntity() { return aggregationEntity; } public void setAggregationEntity(final Entity aggregationEntity) { this.aggregationEntity = aggregationEntity; } public Entity getNullChildEntity() { return null; } public void setNullChildEntity(final Entity nullChildEntity) { this.nullChildEntity = nullChildEntity; } public List getAggregationEntities() { return aggregationEntities; } public void setAggregationEntities(List aggregationEntities) { this.aggregationEntities = aggregationEntities; } public List getCompositionEntities() { return compositionEntities; } public void setCompositionEntities(List compositionEntities) { this.compositionEntities = compositionEntities; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/ForeignKeyAnnotatorTest.java ================================================ /** * 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.business.data.generator; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import java.io.StringWriter; import java.io.Writer; import java.util.Collection; import java.util.Map; import javax.persistence.JoinColumn; import com.sun.codemodel.JAnnotationUse; import com.sun.codemodel.JAnnotationValue; import com.sun.codemodel.JDefinedClass; import com.sun.codemodel.JFieldVar; import com.sun.codemodel.JFormatter; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.field.RelationField; import org.bonitasoft.engine.business.data.generator.client.ClientBDMCodeGenerator; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Laurent Leseigneur */ @RunWith(MockitoJUnitRunner.class) public class ForeignKeyAnnotatorTest { public static final String EXPECTED_UNIQUE_NAME = "EntityPojo_fieldName_businessObjectSimpleName"; private CodeGenerator codeGenerator; private JDefinedClass jDefinedClass; private JFieldVar jFieldVar; @Mock private RelationField relationField; @Mock private BusinessObject businessObject; private ForeignKeyAnnotator foreignKeyAnnotator; @Before public void before() throws Exception { codeGenerator = new ClientBDMCodeGenerator().disableRuntimeClassesValidation(); foreignKeyAnnotator = new ForeignKeyAnnotator(); jDefinedClass = codeGenerator.addClass(EntityPojo.class.getName()); jFieldVar = codeGenerator.addField(jDefinedClass, "fieldName", EntityPojo.class); doReturn(businessObject).when(relationField).getReference(); doReturn("businessObjectSimpleName").when(businessObject).getSimpleName(); } @Test public void should_annotate_with_foreign_key_name() throws Exception { // when var joinColumn = codeGenerator.addAnnotation(jFieldVar, JoinColumn.class); foreignKeyAnnotator.annotateForeignKeyName(joinColumn, jDefinedClass, jFieldVar, relationField); // then final Collection annotations = jFieldVar.annotations(); assertThat(annotations).hasSize(1); final JAnnotationUse jAnnotationUse = annotations.iterator().next(); final Map annotationMembers = jAnnotationUse.getAnnotationMembers(); assertThat(annotationMembers).containsKey("foreignKey"); assertThat(jAnnotationUse.getAnnotationClass().fullName()).isEqualTo(JoinColumn.class.getCanonicalName()); final JAnnotationUse foreignKeyAnnotation = (JAnnotationUse) annotationMembers.get("foreignKey"); JAnnotationValue jAnnotationValue = foreignKeyAnnotation.getAnnotationMembers().get("name"); Writer stringWriter = new StringWriter(); JFormatter jFormatter = new JFormatter(stringWriter); jAnnotationValue.generate(jFormatter); final String foreignKeyName = stringWriter.toString(); assertThat(foreignKeyName).isEqualTo( "\"FK_" + foreignKeyAnnotator.getReducedUniqueName(jDefinedClass, jFieldVar, relationField) + "\""); assertThat(foreignKeyName.length()).as("should match db vendor name limitation").isLessThanOrEqualTo(30); } @Test public void should_generate_reduced_name() throws Exception { // when final int reducedUniqueName = foreignKeyAnnotator.getReducedUniqueName(jDefinedClass, jFieldVar, relationField); // then assertThat(reducedUniqueName).as("should be a positive value").isGreaterThan(0); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/OffsetDateTimeConverterTest.java ================================================ /** * 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.business.data.generator; import static org.assertj.core.api.Assertions.assertThat; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; /** * @author Danila Mazour */ public class OffsetDateTimeConverterTest { @Test public void convertToDatabaseColumn_should_generate_a_string_ISO_compliant() { OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter(); OffsetDateTime offsetDateTime = OffsetDateTime.of(LocalDateTime.of(2017, 2, 22, 13, 00, 00), ZoneOffset.ofHours(-5)); String offsetDateAndTimeString = offsetDateAndTimeConverter.convertToDatabaseColumn(offsetDateTime); assertThat(offsetDateAndTimeString).isNotNull(); assertThat(offsetDateAndTimeString).isEqualTo("2017-02-22T18:00:00Z"); } @Test public void convertToEntityAttribute_should_generate_the_correct_attribute() { OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter(); String offsetDateAndTimeString = "2011-11-26T15:02:00.654Z"; OffsetDateTime offsetDateTime = offsetDateAndTimeConverter.convertToEntityAttribute(offsetDateAndTimeString); assertThat(offsetDateTime).isNotNull(); assertThat(offsetDateTime.getYear()).isEqualTo(2011); assertThat(offsetDateTime.getMonthValue()).isEqualTo(11); assertThat(offsetDateTime.getDayOfMonth()).isEqualTo(26); assertThat(offsetDateTime.getHour()).isEqualTo(15); assertThat(offsetDateTime.getMinute()).isEqualTo(2); assertThat(offsetDateTime.getSecond()).isEqualTo(0); assertThat(offsetDateTime.getNano()).isEqualTo(654000000); } @Test public void offsetDateTimeConverter_should_generate_the_same_object_in_and_out() { OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter(); OffsetDateTime offsetDateTime = OffsetDateTime.of(LocalDateTime.of(1961, 4, 12, 6, 7, 00), ZoneOffset.ofHours(5)); OffsetDateTime resultOffsetDateTime = offsetDateAndTimeConverter .convertToEntityAttribute(offsetDateAndTimeConverter.convertToDatabaseColumn(offsetDateTime)) .withOffsetSameInstant(ZoneOffset.ofHours(5)); assertThat(resultOffsetDateTime).isNotNull(); assertThat(resultOffsetDateTime).isEqualTo(offsetDateTime); } @Test public void offsetDateTimeConverter_should_generate_the_same_time_for_OffsetDateTimes_in_and_out() { OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter(); OffsetDateTime offsetDateTime = OffsetDateTime.of(LocalDateTime.of(1961, 4, 12, 6, 7, 00), ZoneOffset.ofHours(5)); OffsetDateTime resultOffsetDateTime = offsetDateAndTimeConverter .convertToEntityAttribute(offsetDateAndTimeConverter.convertToDatabaseColumn(offsetDateTime)); assertThat(offsetDateTime.isEqual(resultOffsetDateTime)).isTrue(); } @Test public void convertToDatabaseColumn_should_return_null_when_given_a_null_string() { OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter(); String nullOffsetDateTime = offsetDateAndTimeConverter.convertToDatabaseColumn(null); assertThat(nullOffsetDateTime).isNull(); } @Test public void convertToEntityAttribute_should_return_null_when_given_a_null_object() { OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter(); OffsetDateTime nullOffsetDateTime = offsetDateAndTimeConverter.convertToEntityAttribute(null); assertThat(nullOffsetDateTime).isNull(); } @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void convertToEntityAttribute_should_throw_an_exception_when_given_a_faulty_string() { OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter(); expectedException.expect(RuntimeException.class); expectedException.expectMessage( "Database OffsetDate&Time format must be ISO-8601 compliant yyyy-MM-dd'T'HH:mm:ss(.SSS)Z "); OffsetDateTime offsetDateTime = offsetDateAndTimeConverter.convertToEntityAttribute("Django"); } @Test public void convertToDatabaseColumn_should_modify_the_day_month_and_year_when_switching_to_UTC_in_the_generated_string() throws Exception { OffsetDateTimeConverter offsetDateAndTimeConverter = new OffsetDateTimeConverter(); OffsetDateTime offsetDateTime = OffsetDateTime.of(LocalDateTime.of(2017, 1, 1, 7, 42, 12, 9649), ZoneOffset.ofHours(10)); String offsetDateAndTimeString = offsetDateAndTimeConverter.convertToDatabaseColumn(offsetDateTime); assertThat(offsetDateAndTimeString).isNotNull(); assertThat(offsetDateAndTimeString).isEqualTo("2016-12-31T21:42:12.000009649Z"); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/PersistenceUnitBuilderTest.java ================================================ /** * 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.business.data.generator; import static org.assertj.core.api.Assertions.assertThat; import java.util.HashSet; import java.util.Set; import org.junit.Before; import org.junit.Test; import org.w3c.dom.Document; import org.w3c.dom.NodeList; /** * @author Romain Bioteau */ public class PersistenceUnitBuilderTest { private PersistenceUnitBuilder builder; /** * @throws Exception */ @Before public void setUp() throws Exception { builder = new PersistenceUnitBuilder(); } @Test public void shouldDone_WhenNothingIsCalledReturnsDefaultPersistenceUnits() { Document document = builder.done(); assertThat(document).isNotNull(); assertThat(document.getElementsByTagName("persistence-unit").getLength()).isEqualTo(1); assertThat(document.getElementsByTagName("class").getLength()).isZero(); } @Test public void shouldAddClass_AddOneClassTagByClassname() throws Exception { builder.addClass("org.bonitasoft.businessdata.Employee").addClass("org.bonitasoft.businessdata.LeaveRequesst"); Document document = builder.done(); assertThat(document.getElementsByTagName("class").getLength()).isEqualTo(2); NodeList nodeList = document.getElementsByTagName("class"); Set classnames = new HashSet(); for (int i = 0; i < nodeList.getLength(); i++) { classnames.add(nodeList.item(i).getTextContent()); } assertThat(classnames).containsOnly("org.bonitasoft.businessdata.Employee", "org.bonitasoft.businessdata.LeaveRequesst"); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/RelationFieldAnnotatorTest.java ================================================ /** * 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.business.data.generator; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Before; import org.junit.Test; public class RelationFieldAnnotatorTest { private RelationFieldAnnotator annotator; @Before public void setUp() { annotator = new RelationFieldAnnotator(new CodeGenerator()); } @Test public void getJoinTableName_should_truncate_names_longer_than_14_chars() { String joinTableName = annotator.getJoinTableName("someLongNameLongerThanFourteen", "anotherLongNameLongerThanFourteen"); assertThat(joinTableName).isEqualTo("SOMELONGNAMELO_ANOTHERLONGNAM"); } @Test public void getJoinColumnName_should_truncate_names_longer_thab_26_chars() { String joinColumnName = annotator.getJoinColumnName("someLongNameLongerThantwentySix"); assertThat(joinColumnName).isEqualTo("SOMELONGNAMELONGERTHANTWEN_PID"); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/client/ClientBDMCodeGeneratorTest.java ================================================ /** * 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.business.data.generator.client; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.util.Strings.concat; import static org.bonitasoft.engine.bdm.builder.BusinessObjectBuilder.aBO; import static org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder.aBOM; import static org.bonitasoft.engine.bdm.builder.FieldBuilder.aRelationField; import static org.bonitasoft.engine.bdm.builder.FieldBuilder.aStringField; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; import java.net.URL; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.UUID; import com.sun.codemodel.JAnnotationUse; import com.sun.codemodel.JDefinedClass; import org.apache.commons.io.FileUtils; import org.assertj.core.util.Files; import org.bonitasoft.engine.bdm.BusinessObjectModelConverter; import org.bonitasoft.engine.bdm.builder.BusinessObjectModelBuilder; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.Query; import org.bonitasoft.engine.bdm.model.QueryParameter; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.RelationField; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.bonitasoft.engine.business.data.generator.AbstractBDMCodeGenerator; import org.bonitasoft.engine.business.data.generator.CompilableCode; import org.bonitasoft.engine.commons.io.IOUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; public class ClientBDMCodeGeneratorTest extends CompilableCode { private static final String EMPLOYEE_QUALIFIED_NAME = "com.company.hr.Employee"; private AbstractBDMCodeGenerator bdmCodeGenerator; private File destDir; @Before public void setUp() { bdmCodeGenerator = new ClientBDMCodeGenerator(); try { destDir = Files.newTemporaryFolder(); } catch (final Exception fe) { System.err.println("Seems we cannot create temporary folder. Retrying..."); final String tempFileName = String.valueOf(UUID.randomUUID().getLeastSignificantBits()); destDir = Files.newFolder(concat(Files.temporaryFolderPath(), tempFileName)); } } @After public void tearDown() throws Exception { FileUtils.deleteDirectory(destDir); } @Test public void shouldToJavaClass_ReturnIntegerClass() { assertThat(bdmCodeGenerator.toJavaClass(FieldType.INTEGER).name()).isEqualTo(Integer.class.getSimpleName()); } @Test public void shouldToJavaClass_ReturnStringClass() { assertThat(bdmCodeGenerator.toJavaClass(FieldType.STRING).name()).isEqualTo(String.class.getSimpleName()); } @Test public void shouldToJavaClass_ReturnLongClass() { assertThat(bdmCodeGenerator.toJavaClass(FieldType.LONG).name()).isEqualTo(Long.class.getSimpleName()); } @Test public void shouldToJavaClass_ReturnDoubleClass() { assertThat(bdmCodeGenerator.toJavaClass(FieldType.DOUBLE).name()).isEqualTo(Double.class.getSimpleName()); } @Test public void shouldToJavaClass_ReturnFloatClass() { assertThat(bdmCodeGenerator.toJavaClass(FieldType.FLOAT).name()).isEqualTo(Float.class.getSimpleName()); } @Test public void shouldToJavaClass_ReturnBooleanClass() { assertThat(bdmCodeGenerator.toJavaClass(FieldType.BOOLEAN).name()).isEqualTo(Boolean.class.getSimpleName()); } @Test public void shouldToJavaClass_ReturnDateClass() { assertThat(bdmCodeGenerator.toJavaClass(FieldType.DATE).name()).isEqualTo(Date.class.getSimpleName()); } @Test public void shouldToJavaClass_ReturnStringTextClass() { assertThat(bdmCodeGenerator.toJavaClass(FieldType.TEXT).name()).isEqualTo(String.class.getSimpleName()); } @Test public void should_AddDao_generate_Dao_interface_with_query_methods_signature() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final SimpleField nameField = new SimpleField(); nameField.setName("name"); nameField.setType(FieldType.STRING); employeeBO.getFields().add(nameField); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(employeeBO); bdmCodeGenerator = new ClientBDMCodeGenerator(); bdmCodeGenerator.generateBom(bom, destDir); final String daoContent = readGeneratedDAOInterface(); assertThat(daoContent) .contains("public List findByName(String name, int startIndex, int maxResults)"); } @Test public void queryGenerationReturningListShouldAddPaginationParameters() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final SimpleField nameField = new SimpleField(); nameField.setName("name"); nameField.setType(FieldType.STRING); employeeBO.getFields().add(nameField); final SimpleField ageField = new SimpleField(); ageField.setName("age"); ageField.setType(FieldType.INTEGER); employeeBO.getFields().add(ageField); final Query query = new Query("getEmployeesByNameAndAge", "SELECT e FROM Employee e WHERE e.name = :myName AND e.age = :miEdad", List.class.getName()); query.addQueryParameter("miEdad", Integer.class.getName()); query.addQueryParameter("myName", String.class.getName()); employeeBO.getQueries().add(query); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(employeeBO); bdmCodeGenerator = new ClientBDMCodeGenerator(); bdmCodeGenerator.generateBom(bom, destDir); final String daoContent = readGeneratedDAOInterface(); assertThat(daoContent).contains( "public List getEmployeesByNameAndAge(Integer miEdad, String myName, int startIndex, int maxResults)"); } protected String getQueryMethodSignature(final Query query, final String queryReturnType, final String businessObjectName, final boolean returnsList) { String signature = "public " + getSimpleClassName(queryReturnType) + "<" + getSimpleClassName(businessObjectName) + "> " + query.getName() + "("; boolean first = true; for (final QueryParameter param : query.getQueryParameters()) { signature = appendCommaIfNotFirstParam(signature, first); signature += getSimpleClassName(param.getClassName()) + " " + param.getName(); first = false; } if (returnsList) { signature = appendCommaIfNotFirstParam(signature, first); signature += "int startIndex, int maxResults"; } signature += ")"; return signature; } protected String appendCommaIfNotFirstParam(final String signature, final boolean first) { String newSignature = signature; if (!first) { newSignature += ", "; } return newSignature; } private String getSimpleClassName(final String qualifedClassName) { return qualifedClassName.substring(qualifedClassName.lastIndexOf('.') + 1); } @Test public void should_AddDao_generate_Dao_interface_with_unique_constraint_methods_signature() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final SimpleField nameField = new SimpleField(); nameField.setName("firstName"); nameField.setType(FieldType.STRING); final SimpleField lastnameField = new SimpleField(); lastnameField.setName("lastName"); lastnameField.setType(FieldType.STRING); employeeBO.getFields().add(nameField); employeeBO.getFields().add(lastnameField); employeeBO.addUniqueConstraint("TOTO", "firstName", "lastName"); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(employeeBO); bdmCodeGenerator = new ClientBDMCodeGenerator(); bdmCodeGenerator.generateBom(bom, destDir); } private String readGeneratedDAOInterface() throws IOException { final File daoInterface = getGeneratedFile(EMPLOYEE_QUALIFIED_NAME.replace(".", File.separator) + "DAO.java"); return FileUtils.readFileToString(daoInterface); } public JAnnotationUse getAnnotation(final JDefinedClass definedClass, final String annotationClassName) { final Iterator iterator = definedClass.annotations().iterator(); JAnnotationUse annotation = null; while (annotation == null && iterator.hasNext()) { final JAnnotationUse next = iterator.next(); if (next.getAnnotationClass().fullName().equals(annotationClassName)) { annotation = next; } } return annotation; } @Test public void addIndexAnnotation() throws Exception { final BusinessObjectModel model = new BusinessObjectModel(); final SimpleField field = new SimpleField(); field.setName("firstName"); field.setType(FieldType.STRING); final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName("Employee"); employeeBO.addField(field); employeeBO.addIndex("IDX_1", "firstName, lastName"); model.addBusinessObject(employeeBO); bdmCodeGenerator = new ClientBDMCodeGenerator(); bdmCodeGenerator.generateBom(model, destDir); assertFilesAreEqual("Employee.java", "Employee.java.txt"); } @Test public void addSimpleReferenceWithComposition() throws Exception { final RelationField aggregation = aRelationField().withName("address").composition().referencing(addressBO()) .build(); final BusinessObjectModel bom = employeeWithRelations(aggregation); bdmCodeGenerator = new ClientBDMCodeGenerator(); bdmCodeGenerator.generateBom(bom, destDir); assertFilesAreEqual("Employee.java", "EmployeeSimpleComposition.java.txt"); } @Test public void addListReferenceWithComposition() throws Exception { final RelationField eager = aRelationField().withName("addresses").composition().multiple() .referencing(addressBO()) .build(); final RelationField lazy = aRelationField().withName("skills").composition().multiple().lazy() .referencing(skillBO()) .build(); final BusinessObjectModel bom = employeeWithRelations(eager, lazy); bdmCodeGenerator = new ClientBDMCodeGenerator(); bdmCodeGenerator.generateBom(bom, destDir); assertFilesAreEqual("Employee.java", "EmployeeListComposition.java.txt"); assertFilesAreEqual("Skill.java", "Skill.java.txt"); } @Test public void newInstance_is_generated_with_mandatory_fields_in_parameters() throws Exception { final InputStream resourceAsStream = ClientBDMCodeGeneratorTest.class.getResourceAsStream("/failing_bdm.zip"); final BusinessObjectModel businessObjectModel = new BusinessObjectModelConverter() .unzip(IOUtil.getAllContentFrom(resourceAsStream)); bdmCodeGenerator = new ClientBDMCodeGenerator(); bdmCodeGenerator.generateBom(businessObjectModel, destDir); assertFilesAreEqual("com/test/model/PersonneDAOImpl.java", "PersonneDAOImpl.java.txt"); } @Test public void addSimpleReferenceWithAggregation() throws Exception { final RelationField aggregation = aRelationField().withName("address").aggregation().referencing(addressBO()) .build(); final BusinessObjectModel bom = employeeWithRelations(aggregation); bdmCodeGenerator = new ClientBDMCodeGenerator(); bdmCodeGenerator.generateBom(bom, destDir); assertFilesAreEqual("Employee.java", "EmployeeSimpleAggregation.java.txt"); } @Test public void addListReferenceWithAggregation() throws Exception { final RelationField aggregationMultiple = aRelationField().withName("addresses").aggregation().multiple() .referencing(addressBO()).build(); final BusinessObjectModel bom = employeeWithRelations(aggregationMultiple); bdmCodeGenerator = new ClientBDMCodeGenerator(); bdmCodeGenerator.generateBom(bom, destDir); assertFilesAreEqual("Employee.java", "EmployeeListAggregation.java.txt"); } @Test public void addListReferenceWithLazyAggregation() throws Exception { final BusinessObject addressBO = addressBO(); addressBO.addUniqueConstraint("city", "city"); final RelationField aggregationMultiple = aRelationField().withName("addresses").aggregation().multiple().lazy() .referencing(addressBO).build(); final BusinessObjectModel bom = employeeWithRelations(aggregationMultiple); bdmCodeGenerator = new ClientBDMCodeGenerator(); bdmCodeGenerator.generateBom(bom, destDir); assertFilesAreEqual("AddressDAO.java", "AddressDAOWithLazyReferenceOnEmployee.java.txt"); assertFilesAreEqual("AddressDAOImpl.java", "AddressDAOImplWithLazyReferenceOnEmployee.java.txt"); } @Test public void addList() throws Exception { final BusinessObjectModel model = build(); bdmCodeGenerator = new ClientBDMCodeGenerator(); bdmCodeGenerator.generateBom(model, destDir); assertFilesAreEqual("Forecast.java", "ForecastList.java.txt"); } private BusinessObject skillBO() { return aBO("Skill").withField(aStringField("skill").build()).build(); } private BusinessObjectModel build() { final SimpleField field = new SimpleField(); field.setName("temperatures"); field.setType(FieldType.DOUBLE); field.setCollection(Boolean.TRUE); final BusinessObject forecastBO = new BusinessObject(); forecastBO.setQualifiedName("Forecast"); forecastBO.addField(field); final BusinessObjectModel model = new BusinessObjectModel(); model.addBusinessObject(forecastBO); return model; } private BusinessObjectModel employeeWithRelations(final RelationField... field) { final BusinessObject employeeBO = employeeBO(); BusinessObjectModelBuilder aBom = aBOM().withBO(employeeBO); for (final RelationField relationField : field) { employeeBO.addField(relationField); aBom = aBom.withBO(relationField.getReference()); } return aBom.build(); } private BusinessObject employeeBO() { return aBO("Employee").withField(aStringField("firstName").build()).build(); } private BusinessObject addressBO() { return aBO("Address").withField(aStringField("street").build()).withField(aStringField("city").build()).build(); } private void assertFilesAreEqual(final String qualifiedName, final String resourceName) throws Exception { assertThat(getGeneratedFile(qualifiedName)).hasContentEqualTo(getExpectedFile(resourceName)); } private File getExpectedFile(String resourceName) throws URISyntaxException { File expected; final URL resource = ClientBDMCodeGeneratorTest.class.getResource(resourceName); expected = new File(resource.toURI()); return expected; } private File getGeneratedFile(String qualifiedName) { return new File(destDir, qualifiedName); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/client/ClientBDMJarBuilderTest.java ================================================ /** * 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.business.data.generator.client; import static org.mockito.Mockito.verify; import java.io.File; import org.apache.commons.io.FileUtils; import org.bonitasoft.engine.business.data.generator.BOMBuilder; import org.bonitasoft.engine.io.IOUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ClientBDMJarBuilderTest { @Mock private ResourcesLoader resourcesLoader; @InjectMocks private ClientBDMJarBuilder clientBDMJarBuilder; @Before public void setUp() { clientBDMJarBuilder = new ClientBDMJarBuilder(resourcesLoader); } @Test public void should_add_client_resources_to_compilation_folder() throws Exception { File directory = IOUtils.createTempDirectory("bdmtest"); clientBDMJarBuilder.addSourceFilesToDirectory(BOMBuilder.aBOM().build(), directory); verify(resourcesLoader).copyJavaFilesToDirectory("org.bonitasoft.engine.bdm.dao.client.resources", directory); FileUtils.deleteDirectory(directory); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/client/ResourcesLoaderTest.java ================================================ /** * 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.business.data.generator.client; import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import org.apache.commons.io.FileUtils; import org.bonitasoft.engine.io.IOUtils; import org.junit.Test; public class ResourcesLoaderTest { @Test public void should_copy_files_from_package_in_classpath_to_destination_directory() throws Exception { String packageName = "org.bonitasoft.engine.package"; File directory = IOUtils.createTempDirectory("bdmtest"); new ResourcesLoader().copyJavaFilesToDirectory(packageName, directory); assertThat(new File(directory, "org/bonitasoft/engine/package/Test.java")).exists(); assertThat(new File(directory, "org/bonitasoft/engine/package/subfolder/Other.java")).exists(); FileUtils.deleteDirectory(directory); } @Test public void should_return_the_package_of_in_regular_environment() { assertThat(ResourcesLoader.packageOf("jar:file:/bonita_install/folder/com/company/Person.java", "com.company")).isEqualTo("com/company"); } @Test public void should_return_the_package_of_in_osgi_environment() { assertThat(ResourcesLoader.packageOf("jar:file:/bonita_install/jars/my.jar!/com/company/Address.java", "com.company")).isEqualTo("com/company"); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/compiler/JDTCompilerTest.java ================================================ /** * 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.business.data.generator.compiler; import static java.lang.Thread.currentThread; import static java.util.Arrays.asList; import static org.apache.commons.io.IOUtils.toByteArray; import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import java.io.IOException; import java.lang.reflect.Method; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import javax.persistence.Entity; import org.bonitasoft.engine.commons.io.IOUtil; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; /** * @author Colin PUY * @author Celine Souchet */ public class JDTCompilerTest { private JDTCompiler jdtCompiler; private File outputDirectory; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Before public void instanciateCompiler() throws IOException { jdtCompiler = new JDTCompiler(); outputDirectory = temporaryFolder.newFolder(); } @After public void after() throws IOException { IOUtil.deleteDir(outputDirectory); } private File getTestResourceAsFile(final String fileName) throws URISyntaxException { final URL resource = JDTCompilerTest.class.getResource(fileName); if (resource == null) { throw new RuntimeException("Test resource " + fileName + " not found"); } return new File(resource.toURI()); } @Test public void should_compile_files_in_output_directory() throws Exception { File srcDir = temporaryFolder.newFolder(); writeTestSrcDir(srcDir, "org", "bonitasoft", "CompilableOne.java"); writeTestSrcDir(srcDir, "org", "bonitasoft", "CompilableTwo.java"); jdtCompiler.compile(srcDir, outputDirectory); assertThat(new File(outputDirectory, "org/bonitasoft/CompilableOne.class")).exists(); assertThat(new File(outputDirectory, "org/bonitasoft/CompilableTwo.class")).exists(); try (URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { outputDirectory.toURI().toURL() }, currentThread().getContextClassLoader())) { final Class compilableOneClass = urlClassLoader.loadClass("org.bonitasoft.CompilableOne"); final Method method = compilableOneClass.getMethod("setaClassVariable", int.class); assertThat(method.getParameters()[0].getName()).isEqualTo("aClassVariable"); } } @Test(expected = CompilationException.class) public void should_throw_exception_if_compilation_errors_occurs() throws Exception { File srcDir = temporaryFolder.newFolder(); writeTestSrcDir(srcDir, "com", "bonitasoft", "CannotBeResolvedToATypeError.java"); jdtCompiler.compile(srcDir, outputDirectory); } @Test public void should_show_compilation_errors_in_exception_message() throws Exception { File srcDir = temporaryFolder.newFolder(); writeTestSrcDir(srcDir, "com", "bonitasoft", "CannotBeResolvedToATypeError.java"); try { jdtCompiler.compile(srcDir, outputDirectory); } catch (final CompilationException e) { assertThat(e.getMessage()).contains("cannot be resolved to a type"); } } @Test public void should_compile_class_with_external_dependencies() throws Exception { File srcDir = temporaryFolder.newFolder(); writeTestSrcDir(srcDir, "DependenciesNeeded.java"); final File externalLib = getTestResourceAsFile("external-lib.jar"); jdtCompiler.compile(srcDir, outputDirectory, externalLib, JDTCompiler.lookupJarContaining(Entity.class)); } @Test public void should_compile_class_with_lowercase_name() throws Exception { File srcDir = temporaryFolder.newFolder(); writeTestSrcDir(srcDir, "org", "bonitasoft", "employee.java"); jdtCompiler.compile(srcDir, outputDirectory); assertThat(new File(outputDirectory, "org/bonitasoft/employee.class")).exists(); try (URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { outputDirectory.toURI().toURL() }, currentThread().getContextClassLoader())) { final Class compilableOneClass = urlClassLoader.loadClass("org.bonitasoft.employee"); final Method method = compilableOneClass.getMethod("setaClassVariable", int.class); assertThat(method.getParameters()[0].getName()).isEqualTo("aClassVariable"); } } private void writeTestSrcDir(File srcDir, String... packageName) throws IOException { Path path = srcDir.toPath(); List strings = asList(packageName); String fileName = strings.get(strings.size() - 1); for (String p : packageName) { path = path.resolve(p); } path.getParent().toFile().mkdirs(); Files.write(path, toByteArray(JDTCompilerTest.class.getResourceAsStream(fileName))); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/filter/OnlyDAOImplementationFileFilterTest.java ================================================ /** * 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.business.data.generator.filter; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import java.io.File; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Romain * @author Laurent Leseigneur */ @RunWith(MockitoJUnitRunner.class) public class OnlyDAOImplementationFileFilterTest { private OnlyDAOImplementationFileFilter fileFilter; @Mock File file; /** * @throws Exception */ @Before public void setUp() { fileFilter = new OnlyDAOImplementationFileFilter(); } @Test public void should_accept_return_true() { File f = new File("EmployeeDAO.class"); assertThat(fileFilter.accept(f)).isFalse(); f = new File("EmployeeDAOImpl.class"); assertThat(fileFilter.accept(f)).isTrue(); checkAcceptShouldReturns("org", "bonitasoft", "class", true); checkAcceptShouldReturns("com", "bonitasoft", "java", false); checkAcceptShouldReturns("com", "company", "java", false); } @Test public void should_accept_return_false() { File f = new File("Employee.class"); assertThat(fileFilter.accept(f)).isFalse(); f = new File("Employee.java.txt"); assertThat(fileFilter.accept(f)).isFalse(); } private void checkAcceptShouldReturns(String domain, String subDomain, String extension, boolean expectedResult) { // given doReturn("Employee." + extension).when(file).getName(); doReturn(domain + File.separatorChar + subDomain + File.separatorChar + "model" + File.separatorChar + "Employee." + extension).when(file) .getAbsolutePath(); // when then assertThat(fileFilter.accept(file)) .as("should return " + expectedResult + " when accepting file " + file.getAbsolutePath()) .isEqualTo(expectedResult); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/filter/WithoutDAOImplementationFileFilterTest.java ================================================ /** * 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.business.data.generator.filter; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import java.io.File; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class WithoutDAOImplementationFileFilterTest { private WithoutDAOImplementationFileFilter fileFilter; @Mock File file; /** * @throws Exception */ @Before public void setUp() { fileFilter = new WithoutDAOImplementationFileFilter(); } @Test public void should_accept_return_false() { File f = new File("EmployeeDAOImpl.class"); assertThat(fileFilter.accept(f)).isFalse(); f = new File("EmployeeDAOImpl.java"); assertThat(fileFilter.accept(f)).isFalse(); } @Test public void should_accept_exclude_reserved_package() { checkAcceptShouldReturns("net", "bonitasoft", "class", true); checkAcceptShouldReturns("net", "bonitasoft", "java", true); } private void checkAcceptShouldReturns(String domain, String subDomain, String extension, boolean expectedResult) { // given doReturn("Employee." + extension).when(file).getName(); doReturn(domain + File.separatorChar + subDomain + File.separatorChar + "model" + File.separatorChar + "Employee." + extension).when(file) .getAbsolutePath(); // when then assertThat(fileFilter.accept(file)) .as("should return " + expectedResult + " when accepting file " + file.getAbsolutePath()) .isEqualTo(expectedResult); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/server/ServerBDMCodeGeneratorTest.java ================================================ /** * 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.business.data.generator.server; import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import org.apache.commons.io.FileUtils; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.UniqueConstraint; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; /** * @author Romain Bioteau */ @RunWith(MockitoJUnitRunner.class) public class ServerBDMCodeGeneratorTest { private static final String EMPLOYEE_QUALIFIED_NAME = "com.company.hr.Employee"; private static final String SERVER_EMPLOYEE_DAO_IMPL_FILE = "com/company/hr/server/EmployeeDAOImpl.java"; private ServerBDMCodeGenerator serverBDMCodeGenerator; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); private File destDir; @Before public void setUp() throws Exception { serverBDMCodeGenerator = new ServerBDMCodeGenerator(); destDir = temporaryFolder.newFolder(); } @Test public void toDaoImplClassnameShouldAddPointServerToLastPackagePart() { final BusinessObject myBo = new BusinessObject(); myBo.setQualifiedName("com.bonitasoft.business.domain.Stool"); final String daoImplClassname = new ServerBDMCodeGenerator().toDaoImplClassname(myBo); assertThat(daoImplClassname).isEqualTo("com.bonitasoft.business.domain.server.StoolDAOImpl"); } @Test public void toDaoImplClassnameShouldAddPointServerOnDefaultPackageBO() { final BusinessObject myBo = new BusinessObject(); myBo.setQualifiedName("Zucchini"); final String daoImplClassname = new ServerBDMCodeGenerator().toDaoImplClassname(myBo); assertThat(daoImplClassname).isEqualTo("server.ZucchiniDAOImpl"); } @Test public void serverDAOImplShouldGenerateConstructorWithBusinessDataRepository() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(employeeBO); final SimpleField aField = new SimpleField(); aField.setName("aField"); aField.setType(FieldType.STRING); employeeBO.getFields().add(aField); serverBDMCodeGenerator = new ServerBDMCodeGenerator(); serverBDMCodeGenerator.generateBom(bom, destDir); final String serverDaoImplContent = readGeneratedServerDAOImpl(); assertThat(serverDaoImplContent).contains( "public EmployeeDAOImpl(BusinessDataRepository businessDataRepository)", "this.businessDataRepository = businessDataRepository;"); } @Test public void serverDAOImplShouldGenerateDefaultFindMethod() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(employeeBO); final SimpleField aField = new SimpleField(); aField.setName("aField"); aField.setType(FieldType.STRING); employeeBO.getFields().add(aField); serverBDMCodeGenerator = new ServerBDMCodeGenerator(); serverBDMCodeGenerator.generateBom(bom, destDir); final String serverDaoImplContent = readGeneratedServerDAOImpl(); assertThat(serverDaoImplContent).contains("public List find(int startIndex, int maxResults)", "return businessDataRepository.findListByNamedQuery(\"Employee.find\", Employee.class, queryParameters, startIndex, maxResults);", "catch (Exception e)"); } @Test public void serverDAOImplShouldGenerate_FindByNamedQuery_ForBOReturningQuery() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final SimpleField nameField = new SimpleField(); nameField.setName("name"); nameField.setType(FieldType.STRING); employeeBO.getFields().add(nameField); final UniqueConstraint uniqueConstraint = new UniqueConstraint(); uniqueConstraint.setName("uniqueName"); uniqueConstraint.setFieldNames(Arrays.asList("name")); employeeBO.setUniqueConstraints(Arrays.asList(uniqueConstraint)); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(employeeBO); serverBDMCodeGenerator = new ServerBDMCodeGenerator(); serverBDMCodeGenerator.generateBom(bom, destDir); final String serverDaoImplContent = readGeneratedServerDAOImpl(); assertThat(serverDaoImplContent).contains("public Employee findByName(String name)", "return businessDataRepository.findByNamedQuery(\"Employee.findByName\", Employee.class, queryParameters);"); } @Test public void serverDAOImplShouldGenerate_FindListByNamedQuery_ForListOfBOReturningQuery() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final SimpleField nameField = new SimpleField(); nameField.setName("name"); nameField.setType(FieldType.STRING); employeeBO.getFields().add(nameField); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(employeeBO); serverBDMCodeGenerator = new ServerBDMCodeGenerator(); serverBDMCodeGenerator.generateBom(bom, destDir); final String serverDaoImplContent = readGeneratedServerDAOImpl(); assertThat(serverDaoImplContent).contains( "public List findByName(String name, int startIndex, int maxResults)", "return businessDataRepository.findListByNamedQuery(\"Employee.findByName\", Employee.class, queryParameters, startIndex, maxResults);"); } @Test public void serverDAOImplShouldGenerateGoodReturnTypeForCustomQuery() throws Exception { final BusinessObject employeeBO = new BusinessObject(); employeeBO.setQualifiedName(EMPLOYEE_QUALIFIED_NAME); final SimpleField nameField = new SimpleField(); nameField.setName("name"); nameField.setType(FieldType.STRING); employeeBO.getFields().add(nameField); employeeBO.addQuery("howManyAreWe", "SELECT count(*) FROM Employee", Long.class.getName()); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(employeeBO); serverBDMCodeGenerator = new ServerBDMCodeGenerator(); serverBDMCodeGenerator.generateBom(bom, destDir); final String serverDaoImplContent = readGeneratedServerDAOImpl(); assertThat(serverDaoImplContent).contains("public Long howManyAreWe()", "return businessDataRepository.findByNamedQuery(\"Employee.howManyAreWe\", Long.class, queryParameters);"); } private String readGeneratedServerDAOImpl() throws IOException { final File daoImplem = new File(destDir, SERVER_EMPLOYEE_DAO_IMPL_FILE); return FileUtils.readFileToString(daoImplem, StandardCharsets.UTF_8); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/java/org/bonitasoft/engine/business/data/generator/server/ServerBDMJarBuilderTest.java ================================================ /** * 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.business.data.generator.server; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; import java.io.File; import org.apache.commons.io.filefilter.TrueFileFilter; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.business.data.generator.BOMBuilder; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ServerBDMJarBuilderTest { @Mock private BusinessObjectModel bom; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); private File directory; private ServerBDMJarBuilder bdmJarBuilder; @Before public void setUp() throws Exception { directory = temporaryFolder.newFolder(); bdmJarBuilder = new ServerBDMJarBuilder(new ServerBDMCodeGenerator()); } @Test public void should_addPersistenceUnittestGetPersistenceFileContentFor() throws Exception { bdmJarBuilder.addPersistenceFile(directory, bom); verify(bom).getBusinessObjects(); assertThat(directory).isDirectory(); final File metaInf = new File(directory, "META-INF"); assertThat(metaInf).exists(); assertThat(new File(metaInf, "persistence.xml")).exists(); } /* Just to test we have no errors in full chain. Must be improved */ @Test public void jar_builder_should_go_well_without_errors() throws Exception { bdmJarBuilder.build(BOMBuilder.aBOM().build(), TrueFileFilter.TRUE); } @Test public void jar_builder_should_go_well_without_errors_with_queries() throws Exception { final BOMBuilder builder = new BOMBuilder(); bdmJarBuilder.build(builder.buildComplex(), TrueFileFilter.TRUE); } @Test public void jar_builder_should_go_well_without_errors_with_queries2() throws Exception { bdmJarBuilder.build(BOMBuilder.aBOM().buildPerson(), TrueFileFilter.TRUE); } @Test public void jar_builder_should_go_well_with_multipleBoolean() throws Exception { final BOMBuilder builder = new BOMBuilder(); bdmJarBuilder.build(builder.buildModelWithMultipleBoolean(), TrueFileFilter.TRUE); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/AddressDAOImplWithLazyReferenceOnEmployee.java.txt ================================================ import java.io.Serializable; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.CommandAPI; import org.bonitasoft.engine.bdm.dao.client.resources.BusinessObjectDeserializer; import org.bonitasoft.engine.bdm.dao.client.resources.proxy.LazyLoader; import org.bonitasoft.engine.bdm.dao.client.resources.proxy.Proxyfier; import org.bonitasoft.engine.session.APISession; public class AddressDAOImpl implements AddressDAO { private APISession session; private BusinessObjectDeserializer deserializer; private Proxyfier proxyfier; public AddressDAOImpl(APISession session) { this.session = session; this.deserializer = new BusinessObjectDeserializer(); LazyLoader lazyLoader = new LazyLoader(session); this.proxyfier = new Proxyfier(lazyLoader); } public Address findByPersistenceId(Long persistenceId) { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Address.findByPersistenceId"); commandParameters.put("returnsList", false); commandParameters.put("returnType", "Address"); Map queryParameters = new HashMap(); queryParameters.put("persistenceId", persistenceId); commandParameters.put("queryParameters", ((Serializable) queryParameters)); return proxyfier.proxify(deserializer.deserialize(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), Address.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public Address findByCity(String city) { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Address.findByCity"); commandParameters.put("returnsList", false); commandParameters.put("returnType", "Address"); Map queryParameters = new HashMap(); queryParameters.put("city", city); commandParameters.put("queryParameters", ((Serializable) queryParameters)); return proxyfier.proxify(deserializer.deserialize(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), Address.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public List
      findByStreet(String street, int startIndex, int maxResults) { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Address.findByStreet"); commandParameters.put("returnsList", true); commandParameters.put("returnType", "Address"); commandParameters.put("startIndex", startIndex); commandParameters.put("maxResults", maxResults); Map queryParameters = new HashMap(); queryParameters.put("street", street); commandParameters.put("queryParameters", ((Serializable) queryParameters)); return proxyfier.proxify(deserializer.deserializeList(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), Address.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public List
      find(int startIndex, int maxResults) { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Address.find"); commandParameters.put("returnsList", true); commandParameters.put("returnType", "Address"); commandParameters.put("startIndex", startIndex); commandParameters.put("maxResults", maxResults); Map queryParameters = new HashMap(); commandParameters.put("queryParameters", ((Serializable) queryParameters)); return proxyfier.proxify(deserializer.deserializeList(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), Address.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public Long countForFindByStreet(String street) { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Address.countForFindByStreet"); commandParameters.put("returnsList", false); commandParameters.put("returnType", "java.lang.Long"); Map queryParameters = new HashMap(); queryParameters.put("street", street); commandParameters.put("queryParameters", ((Serializable) queryParameters)); return ((Long) deserializer.deserialize(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), Long.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public Long countForFind() { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Address.countForFind"); commandParameters.put("returnsList", false); commandParameters.put("returnType", "java.lang.Long"); return ((Long) deserializer.deserialize(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), Long.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public Address newInstance() { Address instance = new Address(); return instance; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/AddressDAOWithLazyReferenceOnEmployee.java.txt ================================================ import java.util.List; import org.bonitasoft.engine.bdm.dao.BusinessObjectDAO; public interface AddressDAO extends BusinessObjectDAO { public Address findByPersistenceId(Long persistenceId); public Address findByCity(String city); public List
      findByStreet(String street, int startIndex, int maxResults); public List
      find(int startIndex, int maxResults); public Long countForFindByStreet(String street); public Long countForFind(); public Address newInstance(); } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/Employee.java.txt ================================================ import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Version; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.Index; import org.hibernate.annotations.Parameter; /** * */ @javax.persistence.Entity(name = "Employee") @org.hibernate.annotations.Table(appliesTo = "EMPLOYEE", indexes = { @Index(name = "IDX_1", columnNames = { "FIRSTNAME, LASTNAME" }) }) @javax.persistence.Table(name = "EMPLOYEE") @NamedQueries({ @NamedQuery(name = "Employee.findByPersistenceId", query = "SELECT e\nFROM Employee e\nWHERE e.persistenceId= :persistenceId\n"), @NamedQuery(name = "Employee.findByFirstName", query = "SELECT e\nFROM Employee e\nWHERE e.firstName= :firstName\nORDER BY e.persistenceId"), @NamedQuery(name = "Employee.find", query = "SELECT e\nFROM Employee e\nORDER BY e.persistenceId"), @NamedQuery(name = "Employee.countForFindByFirstName", query = "SELECT COUNT(e)\nFROM Employee e\nWHERE e.firstName= :firstName\n"), @NamedQuery(name = "Employee.countForFind", query = "SELECT COUNT(e)\nFROM Employee e\n") }) public class Employee implements org.bonitasoft.engine.bdm.Entity { @Id @GeneratedValue(generator = "default_bonita_seq_generator") @GenericGenerator(name = "default_bonita_seq_generator", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = { @Parameter(name = "sequence_name", value = "hibernate_sequence") }) private Long persistenceId; @Version private Long persistenceVersion; @Column(name = "FIRSTNAME", nullable = true) private String firstName; public Employee() { } public void setPersistenceId(Long persistenceId) { this.persistenceId = persistenceId; } public Long getPersistenceId() { return persistenceId; } public void setPersistenceVersion(Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } public Long getPersistenceVersion() { return persistenceVersion; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getFirstName() { return firstName; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/EmployeeListAggregation.java.txt ================================================ import java.util.ArrayList; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OrderColumn; import javax.persistence.Table; import javax.persistence.Version; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.Parameter; /** * */ @javax.persistence.Entity(name = "Employee") @Table(name = "EMPLOYEE") @NamedQueries({ @NamedQuery(name = "Employee.findByPersistenceId", query = "SELECT e\nFROM Employee e\nWHERE e.persistenceId= :persistenceId\n"), @NamedQuery(name = "Employee.findByFirstName", query = "SELECT e\nFROM Employee e\nWHERE e.firstName= :firstName\nORDER BY e.persistenceId"), @NamedQuery(name = "Employee.find", query = "SELECT e\nFROM Employee e\nORDER BY e.persistenceId"), @NamedQuery(name = "Employee.countForFindByFirstName", query = "SELECT COUNT(e)\nFROM Employee e\nWHERE e.firstName= :firstName\n"), @NamedQuery(name = "Employee.countForFind", query = "SELECT COUNT(e)\nFROM Employee e\n") }) public class Employee implements org.bonitasoft.engine.bdm.Entity { @Id @GeneratedValue(generator = "default_bonita_seq_generator") @GenericGenerator(name = "default_bonita_seq_generator", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = { @Parameter(name = "sequence_name", value = "hibernate_sequence") }) private Long persistenceId; @Version private Long persistenceVersion; @Column(name = "FIRSTNAME", nullable = true) private String firstName; @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE) @JoinTable(name = "EMPLOYEE_ADDRESSES", joinColumns = { @JoinColumn(name = "EMPLOYEE_PID") }, inverseJoinColumns = { @JoinColumn(name = "ADDRESS_PID") }) @OrderColumn private List
      addresses = new ArrayList
      (10); public Employee() { } public void setPersistenceId(Long persistenceId) { this.persistenceId = persistenceId; } public Long getPersistenceId() { return persistenceId; } public void setPersistenceVersion(Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } public Long getPersistenceVersion() { return persistenceVersion; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getFirstName() { return firstName; } public void setAddresses(List
      addresses) { if (this.addresses == null) { this.addresses = addresses; } else { List
      copy = new ArrayList(addresses); this.addresses.clear(); this.addresses.addAll(copy); } } public List
      getAddresses() { return addresses; } public void addToAddresses(Address addTo) { List addresses = getAddresses(); addresses.add(addTo); } public void removeFromAddresses(Address removeFrom) { List addresses = getAddresses(); addresses.remove(removeFrom); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/EmployeeListComposition.java.txt ================================================ import java.util.ArrayList; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OrderColumn; import javax.persistence.Table; import javax.persistence.Version; import com.fasterxml.jackson.annotation.JsonIgnore; import org.bonitasoft.engine.bdm.lazy.LazyLoaded; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.Parameter; /** * */ @javax.persistence.Entity(name = "Employee") @Table(name = "EMPLOYEE") @NamedQueries({ @NamedQuery(name = "Employee.findByPersistenceId", query = "SELECT e\nFROM Employee e\nWHERE e.persistenceId= :persistenceId\n"), @NamedQuery(name = "Employee.findByFirstName", query = "SELECT e\nFROM Employee e\nWHERE e.firstName= :firstName\nORDER BY e.persistenceId"), @NamedQuery(name = "Employee.find", query = "SELECT e\nFROM Employee e\nORDER BY e.persistenceId"), @NamedQuery(name = "Employee.countForFindByFirstName", query = "SELECT COUNT(e)\nFROM Employee e\nWHERE e.firstName= :firstName\n"), @NamedQuery(name = "Employee.countForFind", query = "SELECT COUNT(e)\nFROM Employee e\n") }) public class Employee implements org.bonitasoft.engine.bdm.Entity { @Id @GeneratedValue(generator = "default_bonita_seq_generator") @GenericGenerator(name = "default_bonita_seq_generator", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = { @Parameter(name = "sequence_name", value = "hibernate_sequence") }) private Long persistenceId; @Version private Long persistenceVersion; @Column(name = "FIRSTNAME", nullable = true) private String firstName; @OneToMany(orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name = "EMPLOYEE_PID", nullable = false) @OrderColumn private List
      addresses = new ArrayList
      (10); @OneToMany(orphanRemoval = true, fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "EMPLOYEE_PID", nullable = false) @OrderColumn @JsonIgnore private List skills = new ArrayList(10); public Employee() { } public void setPersistenceId(Long persistenceId) { this.persistenceId = persistenceId; } public Long getPersistenceId() { return persistenceId; } public void setPersistenceVersion(Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } public Long getPersistenceVersion() { return persistenceVersion; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getFirstName() { return firstName; } public void setAddresses(List
      addresses) { if (this.addresses == null) { this.addresses = addresses; } else { List
      copy = new ArrayList(addresses); this.addresses.clear(); this.addresses.addAll(copy); } } public List
      getAddresses() { return addresses; } public void addToAddresses(Address addTo) { List addresses = getAddresses(); addresses.add(addTo); } public void removeFromAddresses(Address removeFrom) { List addresses = getAddresses(); addresses.remove(removeFrom); } public void setSkills(List skills) { if (this.skills == null) { this.skills = skills; } else { List copy = new ArrayList(skills); this.skills.clear(); this.skills.addAll(copy); } } @LazyLoaded public List getSkills() { return skills; } public void addToSkills(Skill addTo) { List skills = getSkills(); skills.add(addTo); } public void removeFromSkills(Skill removeFrom) { List skills = getSkills(); skills.remove(removeFrom); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/EmployeeSimpleAggregation.java.txt ================================================ import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.FetchType; import javax.persistence.ForeignKey; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; import javax.persistence.Version; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.Parameter; /** * */ @javax.persistence.Entity(name = "Employee") @Table(name = "EMPLOYEE") @NamedQueries({ @NamedQuery(name = "Employee.findByPersistenceId", query = "SELECT e\nFROM Employee e\nWHERE e.persistenceId= :persistenceId\n"), @NamedQuery(name = "Employee.findByFirstName", query = "SELECT e\nFROM Employee e\nWHERE e.firstName= :firstName\nORDER BY e.persistenceId"), @NamedQuery(name = "Employee.find", query = "SELECT e\nFROM Employee e\nORDER BY e.persistenceId"), @NamedQuery(name = "Employee.countForFindByFirstName", query = "SELECT COUNT(e)\nFROM Employee e\nWHERE e.firstName= :firstName\n"), @NamedQuery(name = "Employee.countForFind", query = "SELECT COUNT(e)\nFROM Employee e\n") }) public class Employee implements org.bonitasoft.engine.bdm.Entity { @Id @GeneratedValue(generator = "default_bonita_seq_generator") @GenericGenerator(name = "default_bonita_seq_generator", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = { @Parameter(name = "sequence_name", value = "hibernate_sequence") }) private Long persistenceId; @Version private Long persistenceVersion; @Column(name = "FIRSTNAME", nullable = true) private String firstName; @ManyToOne(optional = true, fetch = FetchType.EAGER, cascade = CascadeType.MERGE) @JoinColumn(name = "ADDRESS_PID", foreignKey = @ForeignKey(name = "FK_157386520")) private Address address; public Employee() { } public void setPersistenceId(Long persistenceId) { this.persistenceId = persistenceId; } public Long getPersistenceId() { return persistenceId; } public void setPersistenceVersion(Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } public Long getPersistenceVersion() { return persistenceVersion; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getFirstName() { return firstName; } public void setAddress(Address address) { this.address = address; } public Address getAddress() { return address; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/EmployeeSimpleComposition.java.txt ================================================ import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.FetchType; import javax.persistence.ForeignKey; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToOne; import javax.persistence.Table; import javax.persistence.Version; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.Parameter; /** * */ @javax.persistence.Entity(name = "Employee") @Table(name = "EMPLOYEE") @NamedQueries({ @NamedQuery(name = "Employee.findByPersistenceId", query = "SELECT e\nFROM Employee e\nWHERE e.persistenceId= :persistenceId\n"), @NamedQuery(name = "Employee.findByFirstName", query = "SELECT e\nFROM Employee e\nWHERE e.firstName= :firstName\nORDER BY e.persistenceId"), @NamedQuery(name = "Employee.find", query = "SELECT e\nFROM Employee e\nORDER BY e.persistenceId"), @NamedQuery(name = "Employee.countForFindByFirstName", query = "SELECT COUNT(e)\nFROM Employee e\nWHERE e.firstName= :firstName\n"), @NamedQuery(name = "Employee.countForFind", query = "SELECT COUNT(e)\nFROM Employee e\n") }) public class Employee implements org.bonitasoft.engine.bdm.Entity { @Id @GeneratedValue(generator = "default_bonita_seq_generator") @GenericGenerator(name = "default_bonita_seq_generator", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = { @Parameter(name = "sequence_name", value = "hibernate_sequence") }) private Long persistenceId; @Version private Long persistenceVersion; @Column(name = "FIRSTNAME", nullable = true) private String firstName; @OneToOne(orphanRemoval = true, optional = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name = "ADDRESS_PID", foreignKey = @ForeignKey(name = "FK_157386520")) private Address address; public Employee() { } public void setPersistenceId(Long persistenceId) { this.persistenceId = persistenceId; } public Long getPersistenceId() { return persistenceId; } public void setPersistenceVersion(Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } public Long getPersistenceVersion() { return persistenceVersion; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getFirstName() { return firstName; } public void setAddress(Address address) { this.address = address; } public Address getAddress() { return address; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/ForecastList.java.txt ================================================ import java.util.ArrayList; import java.util.List; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OrderColumn; import javax.persistence.Table; import javax.persistence.Version; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.Parameter; /** * */ @javax.persistence.Entity(name = "Forecast") @Table(name = "FORECAST") @NamedQueries({ @NamedQuery(name = "Forecast.findByPersistenceId", query = "SELECT f\nFROM Forecast f\nWHERE f.persistenceId= :persistenceId\n"), @NamedQuery(name = "Forecast.find", query = "SELECT f\nFROM Forecast f\nORDER BY f.persistenceId"), @NamedQuery(name = "Forecast.countForFind", query = "SELECT COUNT(f)\nFROM Forecast f\n") }) public class Forecast implements org.bonitasoft.engine.bdm.Entity { @Id @GeneratedValue(generator = "default_bonita_seq_generator") @GenericGenerator(name = "default_bonita_seq_generator", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = { @Parameter(name = "sequence_name", value = "hibernate_sequence") }) private Long persistenceId; @Version private Long persistenceVersion; @ElementCollection(fetch = FetchType.EAGER) @OrderColumn @Column(name = "TEMPERATURES", nullable = true) private List temperatures = new ArrayList(10); public Forecast() { } public void setPersistenceId(Long persistenceId) { this.persistenceId = persistenceId; } public Long getPersistenceId() { return persistenceId; } public void setPersistenceVersion(Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } public Long getPersistenceVersion() { return persistenceVersion; } public void setTemperatures(List temperatures) { if (this.temperatures == null) { this.temperatures = temperatures; } else { List copy = new ArrayList(temperatures); this.temperatures.clear(); this.temperatures.addAll(copy); } } public List getTemperatures() { return temperatures; } public void addToTemperatures(Double addTo) { List temperatures = getTemperatures(); temperatures.add(addTo); } public void removeFromTemperatures(Double removeFrom) { List temperatures = getTemperatures(); temperatures.remove(removeFrom); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/PersonneDAOImpl.java.txt ================================================ package com.test.model; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.api.CommandAPI; import org.bonitasoft.engine.bdm.dao.client.resources.BusinessObjectDeserializer; import org.bonitasoft.engine.bdm.dao.client.resources.proxy.LazyLoader; import org.bonitasoft.engine.bdm.dao.client.resources.proxy.Proxyfier; import org.bonitasoft.engine.session.APISession; public class PersonneDAOImpl implements PersonneDAO { private APISession session; private BusinessObjectDeserializer deserializer; private Proxyfier proxyfier; public PersonneDAOImpl(APISession session) { this.session = session; this.deserializer = new BusinessObjectDeserializer(); LazyLoader lazyLoader = new LazyLoader(session); this.proxyfier = new Proxyfier(lazyLoader); } public com.test.model.Personne findByPersistenceId(Long persistenceId) { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Personne.findByPersistenceId"); commandParameters.put("returnsList", false); commandParameters.put("returnType", "com.test.model.Personne"); Map queryParameters = new HashMap(); queryParameters.put("persistenceId", persistenceId); commandParameters.put("queryParameters", ((Serializable) queryParameters)); return proxyfier.proxify(deserializer.deserialize(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), com.test.model.Personne.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public com.test.model.Personne findByPrenomAndNomAndBirthDate(String prenom, String nom, Date birthDate) { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Personne.findByPrenomAndNomAndBirthDate"); commandParameters.put("returnsList", false); commandParameters.put("returnType", "com.test.model.Personne"); Map queryParameters = new HashMap(); queryParameters.put("prenom", prenom); queryParameters.put("nom", nom); queryParameters.put("birthDate", birthDate); commandParameters.put("queryParameters", ((Serializable) queryParameters)); return proxyfier.proxify(deserializer.deserialize(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), com.test.model.Personne.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public List findByPrenom(String prenom, int startIndex, int maxResults) { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Personne.findByPrenom"); commandParameters.put("returnsList", true); commandParameters.put("returnType", "com.test.model.Personne"); commandParameters.put("startIndex", startIndex); commandParameters.put("maxResults", maxResults); Map queryParameters = new HashMap(); queryParameters.put("prenom", prenom); commandParameters.put("queryParameters", ((Serializable) queryParameters)); return proxyfier.proxify(deserializer.deserializeList(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), com.test.model.Personne.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public List findByNom(String nom, int startIndex, int maxResults) { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Personne.findByNom"); commandParameters.put("returnsList", true); commandParameters.put("returnType", "com.test.model.Personne"); commandParameters.put("startIndex", startIndex); commandParameters.put("maxResults", maxResults); Map queryParameters = new HashMap(); queryParameters.put("nom", nom); commandParameters.put("queryParameters", ((Serializable) queryParameters)); return proxyfier.proxify(deserializer.deserializeList(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), com.test.model.Personne.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public List findByBirthDate(Date birthDate, int startIndex, int maxResults) { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Personne.findByBirthDate"); commandParameters.put("returnsList", true); commandParameters.put("returnType", "com.test.model.Personne"); commandParameters.put("startIndex", startIndex); commandParameters.put("maxResults", maxResults); Map queryParameters = new HashMap(); queryParameters.put("birthDate", birthDate); commandParameters.put("queryParameters", ((Serializable) queryParameters)); return proxyfier.proxify(deserializer.deserializeList(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), com.test.model.Personne.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public List find(int startIndex, int maxResults) { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Personne.find"); commandParameters.put("returnsList", true); commandParameters.put("returnType", "com.test.model.Personne"); commandParameters.put("startIndex", startIndex); commandParameters.put("maxResults", maxResults); Map queryParameters = new HashMap(); commandParameters.put("queryParameters", ((Serializable) queryParameters)); return proxyfier.proxify(deserializer.deserializeList(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), com.test.model.Personne.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public Long countForFindByPrenom(String prenom) { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Personne.countForFindByPrenom"); commandParameters.put("returnsList", false); commandParameters.put("returnType", "java.lang.Long"); Map queryParameters = new HashMap(); queryParameters.put("prenom", prenom); commandParameters.put("queryParameters", ((Serializable) queryParameters)); return ((Long) deserializer.deserialize(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), Long.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public Long countForFindByNom(String nom) { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Personne.countForFindByNom"); commandParameters.put("returnsList", false); commandParameters.put("returnType", "java.lang.Long"); Map queryParameters = new HashMap(); queryParameters.put("nom", nom); commandParameters.put("queryParameters", ((Serializable) queryParameters)); return ((Long) deserializer.deserialize(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), Long.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public Long countForFindByBirthDate(Date birthDate) { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Personne.countForFindByBirthDate"); commandParameters.put("returnsList", false); commandParameters.put("returnType", "java.lang.Long"); Map queryParameters = new HashMap(); queryParameters.put("birthDate", birthDate); commandParameters.put("queryParameters", ((Serializable) queryParameters)); return ((Long) deserializer.deserialize(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), Long.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public Long countForFind() { try { CommandAPI commandApi = org.bonitasoft.engine.api.TenantAPIAccessor.getCommandAPI(session); Map commandParameters = new HashMap(); commandParameters.put("queryName", "Personne.countForFind"); commandParameters.put("returnsList", false); commandParameters.put("returnType", "java.lang.Long"); return ((Long) deserializer.deserialize(((byte[]) commandApi.execute("executeBDMQuery", commandParameters)), Long.class)); } catch (Exception e) { throw new IllegalArgumentException(e); } } public com.test.model.Personne newInstance(String prenom, String nom, Date birthDate, Adresse adressePersonne) { if (prenom == null) { throw new IllegalArgumentException("prenom cannot be null"); } if (nom == null) { throw new IllegalArgumentException("nom cannot be null"); } if (birthDate == null) { throw new IllegalArgumentException("birthDate cannot be null"); } if (adressePersonne == null) { throw new IllegalArgumentException("adressePersonne cannot be null"); } com.test.model.Personne instance = new com.test.model.Personne(); instance.setPrenom(prenom); instance.setNom(nom); instance.setBirthDate(birthDate); instance.setAdressePersonne(adressePersonne); return instance; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/client/Skill.java.txt ================================================ import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; import javax.persistence.Version; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.Parameter; /** * */ @javax.persistence.Entity(name = "Skill") @Table(name = "SKILL") @NamedQueries({ @NamedQuery(name = "Skill.findByPersistenceId", query = "SELECT s\nFROM Skill s\nWHERE s.persistenceId= :persistenceId\n"), @NamedQuery(name = "Skill.findBySkill", query = "SELECT s\nFROM Skill s\nWHERE s.skill= :skill\nORDER BY s.persistenceId"), @NamedQuery(name = "Skill.find", query = "SELECT s\nFROM Skill s\nORDER BY s.persistenceId"), @NamedQuery(name = "Skill.countForFindBySkill", query = "SELECT COUNT(s)\nFROM Skill s\nWHERE s.skill= :skill\n"), @NamedQuery(name = "Skill.countForFind", query = "SELECT COUNT(s)\nFROM Skill s\n"), @NamedQuery(name = "Skill.findSkillsByEmployeePersistenceId", query = "SELECT skills_1 FROM Employee employee_0 JOIN employee_0.skills as skills_1 WHERE employee_0.persistenceId= :persistenceId"), @NamedQuery(name = "Skill.countForFindSkillsByEmployeePersistenceId", query = "SELECT COUNT(skills_1) FROM Employee employee_0 JOIN employee_0.skills as skills_1 WHERE employee_0.persistenceId= :persistenceId") }) public class Skill implements org.bonitasoft.engine.bdm.Entity { @Id @GeneratedValue(generator = "default_bonita_seq_generator") @GenericGenerator(name = "default_bonita_seq_generator", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = { @Parameter(name = "sequence_name", value = "hibernate_sequence") }) private Long persistenceId; @Version private Long persistenceVersion; @Column(name = "SKILL", nullable = true) private String skill; public Skill() { } public void setPersistenceId(Long persistenceId) { this.persistenceId = persistenceId; } public Long getPersistenceId() { return persistenceId; } public void setPersistenceVersion(Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } public Long getPersistenceVersion() { return persistenceVersion; } public void setSkill(String skill) { this.skill = skill; } public String getSkill() { return skill; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/compiler/CannotBeResolvedToATypeError.java ================================================ /** * 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 com.bonitasoft; /** * Not compilable java file */ public class UnCompilable implements NotInClassPathInterface { } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/compiler/CompilableOne.java ================================================ /** * 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; public class CompilableOne { private int aClassVariable; public CompilableOne(int aClassVariable) { super(); this.aClassVariable = aClassVariable; } public int getaClassVariable() { return aClassVariable; } public void setaClassVariable(int aClassVariable) { this.aClassVariable = aClassVariable; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/compiler/CompilableTwo.java ================================================ /** * 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; public class CompilableTwo { private String aClassVarible; public CompilableTwo(String aClassVarible) { super(); this.aClassVarible = aClassVarible; } public String getaClassVarible() { return aClassVarible; } public void setaClassVarible(String aClassVarible) { this.aClassVarible = aClassVarible; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/compiler/DependenciesNeeded.java ================================================ /** * 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 com.bonitasoft; import java.io.Externalizable; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import org.external.dependency.ExternalInterface; /** * JPA and org.external.dependency.ExternalInterface (located in external-lib.jar) needed to be compiled */ @Entity public class DependenciesNeeded implements ExternalInterface { @Id() @GeneratedValue(strategy = GenerationType.AUTO) private String id; } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/compiler/employee.java ================================================ /** * 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; public class employee { private int aClassVariable; public employee(int aClassVariable) { super(); this.aClassVariable = aClassVariable; } public int getaClassVariable() { return aClassVariable; } public void setaClassVariable(int aClassVariable) { this.aClassVariable = aClassVariable; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/business/data/generator/server/ServerEmployee.java ================================================ /** * 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.bdm.server; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Version; import org.hibernate.annotations.Index; /** * */ @javax.persistence.Entity(name = "Employee") @org.hibernate.annotations.Table(appliesTo = "EMPLOYEE", indexes = { @Index(name = "IDX_1", columnNames = { "FIRSTNAME, LASTNAME" }) }) @javax.persistence.Table(name = "EMPLOYEE") @NamedQueries({ @NamedQuery(name = "Employee.findByFirstName", query = "SELECT e\nFROM Employee e\nWHERE e.firstName= :firstName\nORDER BY e.persistenceId"), @NamedQuery(name = "Employee.find", query = "SELECT e\nFROM Employee e\nORDER BY e.persistenceId") }) public class Employee implements org.bonitasoft.engine.bdm.Entity { @Id @GeneratedValue private Long persistenceId; @Version private Long persistenceVersion; @Column(name = "FIRSTNAME", nullable = true) private String firstName; public Employee() { } public void setPersistenceId(Long persistenceId) { this.persistenceId = persistenceId; } public Long getPersistenceId() { return persistenceId; } public void setPersistenceVersion(Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } public Long getPersistenceVersion() { return persistenceVersion; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getFirstName() { return firstName; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass()!= obj.getClass()) { return false; } Employee other = ((Employee) obj); if (persistenceId == null) { if (other.persistenceId!= null) { return false; } } else { if (!persistenceId.equals(other.persistenceId)) { return false; } } if (persistenceVersion == null) { if (other.persistenceVersion!= null) { return false; } } else { if (!persistenceVersion.equals(other.persistenceVersion)) { return false; } } if (firstName == null) { if (other.firstName!= null) { return false; } } else { if (!firstName.equals(other.firstName)) { return false; } } return true; } @Override public int hashCode() { final int prime = 31; int result = 1; int persistenceIdCode = 0; if (persistenceId!= null) { persistenceIdCode = persistenceId.hashCode(); } result = ((prime*result)+ persistenceIdCode); int persistenceVersionCode = 0; if (persistenceVersion!= null) { persistenceVersionCode = persistenceVersion.hashCode(); } result = ((prime*result)+ persistenceVersionCode); int firstNameCode = 0; if (firstName!= null) { firstNameCode = firstName.hashCode(); } result = ((prime*result)+ firstNameCode); return result; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/package/Test.java ================================================ /** * 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 com.bonitasoft.engine. ================================================ FILE: services/bonita-business-data/bonita-business-data-generator/src/test/resources/org/bonitasoft/engine/package/subfolder/Other.java ================================================ /** * 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 com.bonitasoft.engine. ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/build.gradle ================================================ plugins { id 'bonita-docker-database' id 'bonita-tests' } dependencies { api project(':bpm:bonita-common') api project(':services:bonita-business-data:bonita-business-data-generator') api project(':services:bonita-classloader') api project(':services:bonita-business-data:bonita-business-data-api') api project(':services:bonita-resources') api project(':services:bonita-transaction') api project(':services:bonita-commons') api(libs.hibernateCore) { exclude(module: 'jboss-transaction-api_1.2_spec') } api libs.commonsLang api project(':services:bonita-classloader') annotationProcessor libs.lombok compileOnly libs.lombok // AspectJ and Spring AOP for event aspects api libs.springAop api libs.aspectjWeaver testImplementation libs.assertj testImplementation(libs.jsonUnit) { exclude(group: "org.slf4j", module: "slf4j-api") } testImplementation libs.mockitoCore testImplementation libs.mockitoJunitJupiter testImplementation libs.logback testImplementation libs.narayanaJta testImplementation libs.springTest testImplementation libs.springJdbc testImplementation testFixtures(project(':bpm:bonita-common')) testRuntimeOnly libs.tomcatDbcp testRuntimeOnly libs.h2 } databaseIntegrationTest { includes '**/*IT.class' } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/bdm/DateAndTimeConverter.java ================================================ /** * 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.bdm; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import javax.persistence.AttributeConverter; import javax.persistence.Converter; /** * @author Danila Mazour * @deprecated {@link org.bonitasoft.engine.business.data.generator.DateAndTimeConverter} is now used. Keep this class * for backward runtime compatibility */ @Deprecated @Converter public class DateAndTimeConverter implements AttributeConverter { @Override public String convertToDatabaseColumn(LocalDateTime localDateTime) { if (localDateTime != null) { return localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); } else { return null; } } @Override public LocalDateTime convertToEntityAttribute(String s) { if (s != null) { try { return LocalDateTime.parse(s, DateTimeFormatter.ISO_LOCAL_DATE_TIME); } catch (DateTimeParseException e) { throw new RuntimeException( "Database date & time format must be ISO-8601 compliant ( yyyy-mm-ddThh:mm:ss )", e); } } else { return null; } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/bdm/DateConverter.java ================================================ /** * 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.bdm; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import javax.persistence.AttributeConverter; import javax.persistence.Converter; /** * @author Danila Mazour * @deprecated {@link org.bonitasoft.engine.business.data.generator.DateConverter} is now used. Keep this class for * backward runtime compatibility */ @Deprecated @Converter public class DateConverter implements AttributeConverter { @Override public String convertToDatabaseColumn(LocalDate localDate) { if (localDate != null) { return localDate.toString(); } else { return null; } } @Override public LocalDate convertToEntityAttribute(String s) { if (s != null) { try { return LocalDate.parse(s, DateTimeFormatter.ISO_LOCAL_DATE); } catch (DateTimeParseException e) { throw new RuntimeException("Database date format must be ISO-8601 compliant ( yyyy-mm-dd )", e); } } else { return null; } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/bdm/OffsetDateTimeConverter.java ================================================ /** * 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.bdm; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import javax.persistence.AttributeConverter; /** * @author Danila Mazour * @deprecated {@link org.bonitasoft.engine.business.data.generator.OffsetDateTimeConverter} is now used. Keep this * class for backward runtime compatibility */ @Deprecated public class OffsetDateTimeConverter implements AttributeConverter { @Override public String convertToDatabaseColumn(OffsetDateTime offsetDateTime) { if (offsetDateTime != null) { return offsetDateTime.withOffsetSameInstant(ZoneOffset.UTC).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); } else { return null; } } @Override public OffsetDateTime convertToEntityAttribute(String dbData) { if (dbData != null) { try { return OffsetDateTime.parse(dbData, DateTimeFormatter.ISO_OFFSET_DATE_TIME); } catch (DateTimeParseException e) { throw new RuntimeException( "Database OffsetDate&Time format must be ISO-8601 compliant yyyy-MM-dd'T'HH:mm:ss(.SSS)Z ", e); } } else { return null; } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/DataRetentionBdmTrackingRepository.java ================================================ /** * Copyright (C) 2026 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.business.data; import java.util.List; import org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.services.SPersistenceException; /** * Repository for CRUD operations on {@link SDataRetentionBdmTracking} entities. *

      * Tracking records are stored in the Bonita DB and record the creation and last * modification timestamps of BDM object instances for data retention purposes. */ public interface DataRetentionBdmTrackingRepository { /** * Persists a new tracking record. * * @param tracking the tracking record to create * @throws SPersistenceException if the insert fails */ void create(SDataRetentionBdmTracking tracking) throws SPersistenceException; /** * Updates the {@code last_modified_at} column of an existing tracking record. * * @param tracking the tracking record whose {@code lastModifiedAt} has been set to the new value * @throws SPersistenceException if the update fails */ void updateLastModifiedDate(SDataRetentionBdmTracking tracking) throws SPersistenceException; /** * Finds a tracking record by BDM object identifier and class name. * * @param dataId the persistence ID of the tracked BDM object instance * @param dataClassname the fully qualified class name of the BDM object type * @return the matching tracking record, or {@code null} if not found * @throws SBonitaReadException if the read operation fails */ SDataRetentionBdmTracking getByDataIdAndClassname(long dataId, String dataClassname) throws SBonitaReadException; /** * Returns all tracking records for the given BDM class name. * * @param dataClassname the fully qualified class name of the BDM object type * @return the list of matching tracking records, or an empty list if none found * @throws SBonitaReadException if the read operation fails */ List getByClassname(String dataClassname) throws SBonitaReadException; /** * Returns all tracking records for the given BDM class name whose creation date * is at or before the specified deadline. * * @param dataClassname the fully qualified class name of the BDM object type * @param deadline epoch timestamp (milliseconds) — records with {@code createdAt <= deadline} are returned * @return the list of expired tracking records, or an empty list if none found * @throws SBonitaReadException if the read operation fails */ List getExpiredByCreatedDate(String dataClassname, long deadline) throws SBonitaReadException; /** * Returns all tracking records for the given BDM class name whose last modification date * is at or before the specified deadline. * * @param dataClassname the fully qualified class name of the BDM object type * @param deadline epoch timestamp (milliseconds) — records with {@code lastModifiedAt <= deadline} are returned * @return the list of expired tracking records, or an empty list if none found * @throws SBonitaReadException if the read operation fails */ List getExpiredByLastModifiedDate(String dataClassname, long deadline) throws SBonitaReadException; /** * Deletes a single tracking record. * * @param tracking the tracking record to delete * @throws SPersistenceException if the delete operation fails */ void delete(SDataRetentionBdmTracking tracking) throws SPersistenceException; /** * Deletes the tracking record matching the unique key ({@code dataId}, {@code dataClassname}). * * @param dataId the persistence ID of the tracked BDM object instance * @param dataClassname the fully qualified class name of the BDM object type * @return the number of rows deleted (0 if no matching record was found, 1 otherwise) * @throws SPersistenceException if the delete operation fails */ int delete(long dataId, String dataClassname) throws SPersistenceException; /** * Deletes all tracking records matching the given filter options. * * @param filterOptions the filter criteria to restrict the deletion. * Warning: passing an empty list deletes ALL tracking records * @throws SPersistenceException if the delete operation fails */ void deleteAll(List filterOptions) throws SPersistenceException; } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/BdmFieldTypeConverter.java ================================================ /** * Copyright (C) 2025 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.business.data.impl; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.temporal.Temporal; /** * Utility class to convert a value to a specific type if compatible. * Notably, number types are converted to the requested type. * This class is used by the BDM insert / update methods called by the REST API, so types are converted from JSON, * which need specific treatment. * * @author Emmanuel Duchastenier */ public class BdmFieldTypeConverter { @SuppressWarnings("unchecked") public static T convert(final Object value, final Class targetType) { if (value == null) { return null; } if (targetType.isInstance(value)) { return targetType.cast(value); } if (Number.class.isAssignableFrom(targetType) && value instanceof Number numberValue) { double doubleValue = numberValue.doubleValue(); if (targetType == Integer.class) { return (T) Integer.valueOf((int) doubleValue); } else if (targetType == Long.class) { return (T) Long.valueOf((long) doubleValue); } else if (targetType == Float.class) { return (T) Float.valueOf((float) doubleValue); } else if (targetType == Double.class) { if (numberValue instanceof Float) { // to avoid having extra unwanted precision: return (T) Double.valueOf(numberValue.toString()); } return (T) Double.valueOf(doubleValue); } else if (targetType == Short.class) { return (T) Short.valueOf((short) doubleValue); } else if (targetType == Byte.class) { return (T) Byte.valueOf((byte) doubleValue); } } else if (Temporal.class.isAssignableFrom(targetType) && value instanceof String) { if (targetType == LocalDate.class) { return (T) LocalDate.parse((String) value); } else if (targetType == LocalDateTime.class) { return (T) LocalDateTime.parse((String) value); } else if (targetType == OffsetDateTime.class) { return (T) OffsetDateTime.parse((String) value); } } throw new IllegalArgumentException("Cannot convert " + value.getClass() + " to " + targetType); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/BusinessDataModelRepositoryImpl.java ================================================ /** * 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.business.data.impl; import static org.apache.commons.lang3.StringUtils.strip; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.security.MessageDigest; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import javax.xml.bind.JAXBException; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.filefilter.IOFileFilter; import org.apache.commons.io.filefilter.SuffixFileFilter; import org.apache.commons.lang3.exception.ExceptionUtils; import org.bonitasoft.engine.bdm.BusinessObjectModelConverter; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.business.data.BusinessDataModelRepository; import org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService; import org.bonitasoft.engine.business.data.InvalidBusinessDataModelException; import org.bonitasoft.engine.business.data.SBusinessDataRepositoryDeploymentException; import org.bonitasoft.engine.business.data.SBusinessDataRepositoryException; import org.bonitasoft.engine.business.data.SDataRetentionBdmTrackingException; import org.bonitasoft.engine.business.data.SchemaManager; import org.bonitasoft.engine.business.data.generator.AbstractBDMJarBuilder; import org.bonitasoft.engine.business.data.generator.BDMJarGenerationException; import org.bonitasoft.engine.business.data.generator.client.ClientBDMJarBuilder; import org.bonitasoft.engine.business.data.generator.client.ResourcesLoader; import org.bonitasoft.engine.business.data.generator.filter.OnlyDAOImplementationFileFilter; import org.bonitasoft.engine.business.data.generator.filter.WithoutDAOImplementationFileFilter; import org.bonitasoft.engine.business.data.generator.server.ServerBDMCodeGenerator; import org.bonitasoft.engine.business.data.generator.server.ServerBDMJarBuilder; import org.bonitasoft.engine.classloader.ClassLoaderIdentifier; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.classloader.SClassLoaderException; import org.bonitasoft.engine.commons.io.IOUtil; import org.bonitasoft.engine.dependency.SDependencyException; import org.bonitasoft.engine.dependency.SDependencyNotFoundException; import org.bonitasoft.engine.dependency.impl.TenantDependencyService; import org.bonitasoft.engine.dependency.model.AbstractSDependency; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.io.IOUtils; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.resources.STenantResource; import org.bonitasoft.engine.resources.STenantResourceLight; import org.bonitasoft.engine.resources.TenantResourceType; import org.bonitasoft.engine.resources.TenantResourcesService; import org.springframework.stereotype.Service; import org.xml.sax.SAXException; /** * @author Colin PUY */ @Slf4j @Service("businessDataModelRepository") @RequiredArgsConstructor public class BusinessDataModelRepositoryImpl implements BusinessDataModelRepository { private static final String BDR_DEPENDENCY_NAME = "BDR"; public static final String BDR_DEPENDENCY_FILENAME = BDR_DEPENDENCY_NAME + ".jar"; private static final String CLIENT_BDM_ZIP = "client-bdm.zip"; private static final String MODEL_JAR_NAME = "bdm-model.jar"; private static final String DAO_JAR_NAME = "bdm-dao.jar"; private static final String BOM_NAME = "bom.zip"; private final PlatformService platformService; private final TenantDependencyService dependencyService; private final ClassLoaderService classLoaderService; private final SchemaManager schemaManager; private final TenantResourcesService tenantResourcesService; private final DataRetentionBdmTrackingService dataRetentionBdmTrackingService; @Override public byte[] getClientBDMZip() throws SBusinessDataRepositoryException { STenantResource sTenantResource; try { sTenantResource = tenantResourcesService.get(TenantResourceType.BDM, CLIENT_BDM_ZIP); } catch (SBonitaReadException e) { throw new SBusinessDataRepositoryException(e); } if (sTenantResource == null) { throw new SBusinessDataRepositoryException("no client-bdm.zip found in tenant resources"); } return sTenantResource.getContent(); } @Override public String getInstalledBDMVersion() throws SBusinessDataRepositoryException { try { Optional returnedId = dependencyService.getIdOfDependencyOfArtifactForTenant(BDR_DEPENDENCY_FILENAME); if (returnedId.isPresent()) { return String.valueOf(returnedId.get()); } } catch (SBonitaReadException e) { throw new SBusinessDataRepositoryException(e); } return null; } @Override public BusinessObjectModel getBusinessObjectModel() throws SBusinessDataRepositoryException { try { byte[] clientBdmZip = getClientBDMZip(); final Map zipContent = IOUtils.unzip(clientBdmZip); if (zipContent.containsKey(BOM_NAME)) { final byte[] bomZip = zipContent.get(BOM_NAME); return getBusinessObjectModel(bomZip); } } catch (SBusinessDataRepositoryException e) { if (isBDMDeployed()) { // This is not a problem of BDM not deployed, let exception go up: throw e; } } catch (IOException e) { throw new SBusinessDataRepositoryException(e); } catch (InvalidBusinessDataModelException e) { throw new SBusinessDataRepositoryDeploymentException("BDM zip file is invalid in database.", e); } return null; } @Override public boolean isBDMDeployed() { try { return dependencyService.getIdOfDependencyOfArtifactForTenant(BDR_DEPENDENCY_FILENAME).isPresent(); } catch (SBonitaReadException e) { return false; } } @Override public String install(final byte[] bdmZip, long userId) throws SBusinessDataRepositoryDeploymentException, InvalidBusinessDataModelException { final BusinessObjectModel model = getBusinessObjectModel(bdmZip); createAndDeployClientBDMZip(model, userId); final long bdmVersion = createAndDeployServerBDMJar(model); return String.valueOf(bdmVersion); } protected long createAndDeployServerBDMJar(final BusinessObjectModel model) throws SBusinessDataRepositoryDeploymentException { final byte[] serverBdmJar = generateServerBDMJar(model); try { final AbstractSDependency mappedDependency = dependencyService.createMappedDependency(BDR_DEPENDENCY_NAME, serverBdmJar, BDR_DEPENDENCY_FILENAME, -1L, ScopeType.TENANT); //refresh classloader now, it is used to update the schema ClassLoaderIdentifier tenantClassLoader = ClassLoaderIdentifier.TENANT; classLoaderService.refreshClassLoaderImmediatelyWithRollback(tenantClassLoader); classLoaderService.refreshClassLoaderOnOtherNodes(tenantClassLoader); //replace the tenant classloader by the one that was just refreshed Thread.currentThread().setContextClassLoader(classLoaderService.getClassLoader(tenantClassLoader)); update(model.getBusinessObjectsClassNames()); return mappedDependency.getId(); } catch (final SDependencyException | SClassLoaderException e) { throw new SBusinessDataRepositoryDeploymentException(e); } } protected void update(final Set annotatedClassNames) throws SBusinessDataRepositoryDeploymentException { final List exceptions = schemaManager.update(annotatedClassNames); if (!exceptions.isEmpty()) { throw new SBusinessDataRepositoryDeploymentException( "Updating schema failed.\n" + convertExceptions(exceptions)); } } String convertExceptions(List exceptions) { StringBuilder exceptionMessage = new StringBuilder("Error(s) encountered:"); int counter = 1; for (Throwable exception : exceptions) { exceptionMessage.append("\n").append(counter++).append(": ") .append( ExceptionUtils.getThrowableList(exception).stream() .map(throwable -> strip(throwable.toString(), "\n")) // to filter out empty lines .collect(Collectors.joining("\ncaused by "))); } return exceptionMessage.toString(); } void createAndDeployClientBDMZip(final BusinessObjectModel model, long userId) throws SBusinessDataRepositoryDeploymentException { STenantResourceLight accessControl; try { accessControl = tenantResourcesService.getSingleLightResource(TenantResourceType.BDM_ACCESS_CTRL); } catch (SBonitaReadException e) { throw new SBusinessDataRepositoryDeploymentException(e); } if (accessControl != null) { throw new SBusinessDataRepositoryDeploymentException( "A BDM Access Control file is installed. Uninstall it before deploying the BDM"); } try { tenantResourcesService.add(CLIENT_BDM_ZIP, TenantResourceType.BDM, generateClientBDMZip(model), userId); } catch (IOException | SRecorderException e) { throw new SBusinessDataRepositoryDeploymentException(e); } } protected BusinessObjectModel getBusinessObjectModel(final byte[] bdmZip) throws InvalidBusinessDataModelException { final BusinessObjectModelConverter converter = new BusinessObjectModelConverter(); try { return converter.unzip(bdmZip); } catch (IOException | SAXException | JAXBException e) { throw new InvalidBusinessDataModelException(e); } } protected byte[] generateServerBDMJar(final BusinessObjectModel model) throws SBusinessDataRepositoryDeploymentException { return generateServerBDMJar(model, true); } protected byte[] generateServerBDMJar(final BusinessObjectModel model, boolean validateRuntimeClasses) throws SBusinessDataRepositoryDeploymentException { var generator = new ServerBDMCodeGenerator(); if (!validateRuntimeClasses) { generator.disableRuntimeClassesValidation(); } final AbstractBDMJarBuilder builder = new ServerBDMJarBuilder(generator); final IOFileFilter classFileAndXmlFileFilter = new SuffixFileFilter(Arrays.asList(".class", ".xml")); try { // Force productVersion to current platform version. // The bom.xml file stored in the generated jar will have the proper product version. // Thus, when comparing these files, a BDM update will be forced if the platform version has changed // between the existing server jar and the new one. model.setProductVersion(platformService.getSPlatformProperties().getPlatformVersion()); return builder.build(model, classFileAndXmlFileFilter); } catch (BDMJarGenerationException e) { throw new SBusinessDataRepositoryDeploymentException(e); } } protected byte[] generateClientBDMZip(final BusinessObjectModel model) throws SBusinessDataRepositoryDeploymentException, IOException { AbstractBDMJarBuilder builder = new ClientBDMJarBuilder(new ResourcesLoader()); final Map resources = new HashMap<>(); try { // Build jar with Model byte[] modelJarContent = builder.build(model, new WithoutDAOImplementationFileFilter()); resources.put(MODEL_JAR_NAME, modelJarContent); // Build jar with DAO builder = new ClientBDMJarBuilder(new ResourcesLoader()); final byte[] daoJarContent = builder.build(model, new OnlyDAOImplementationFileFilter()); resources.put(DAO_JAR_NAME, daoJarContent); } catch (BDMJarGenerationException e1) { throw new SBusinessDataRepositoryDeploymentException(e1); } //Add bom.xml try { resources.put(BOM_NAME, new BusinessObjectModelConverter().zip(model)); } catch (final JAXBException | SAXException e) { throw new SBusinessDataRepositoryDeploymentException(e); } putResourceFromClassPath(resources, "example-pom.xml"); putResourceFromClassPath(resources, "README.md"); return IOUtil.generateZip(resources); } private void putResourceFromClassPath(Map resources, String name) throws IOException { try (InputStream resource = BusinessDataModelRepositoryImpl.class.getResourceAsStream("/" + name)) { resources.put(name, IOUtil.getAllContentFrom(resource)); } } @Override public void uninstall() throws SBusinessDataRepositoryException { try { dependencyService.deleteDependency(BDR_DEPENDENCY_NAME); classLoaderService.refreshClassLoaderImmediatelyWithRollback(ClassLoaderIdentifier.TENANT); ClassLoader classLoader = classLoaderService.getClassLoader(ClassLoaderIdentifier.TENANT); Thread.currentThread() .setContextClassLoader(classLoader); } catch (final SDependencyNotFoundException sde) { // do nothing } catch (final SDependencyException | SClassLoaderException e) { throw new SBusinessDataRepositoryException(e); } try { STenantResource clientBDMZip = tenantResourcesService.get(TenantResourceType.BDM, CLIENT_BDM_ZIP); if (clientBDMZip != null) { tenantResourcesService.remove(clientBDMZip); } } catch (SBonitaReadException | SRecorderException e) { throw new SBusinessDataRepositoryException(e); } } @Override public void dropAndUninstall() throws SBusinessDataRepositoryException { final URL resource = Thread.currentThread().getContextClassLoader().getResource("bom.xml"); if (resource != null) { try { final byte[] content = IOUtil.getAllContentFrom(resource); final BusinessObjectModel model = new BusinessObjectModelConverter().unmarshall(content); final List exceptions = schemaManager.drop(model.getBusinessObjectsClassNames()); if (!exceptions.isEmpty()) { if (exceptions.size() == 1) { throw new SBusinessDataRepositoryDeploymentException("Drop of the schema failed.", exceptions.get(0)); } else { throw new SBusinessDataRepositoryDeploymentException( "Drop of the schema failed due multiple exceptions: " + exceptions, exceptions.get(0)); } } uninstall(); deleteAllBdmTracking(); } catch (final IOException | JAXBException | SAXException ioe) { throw new SBusinessDataRepositoryException(ioe); } } } /** * Deletes all BDM tracking records when the BDM is uninstalled. * Tracking data becomes obsolete once the BDM schema is dropped. */ private void deleteAllBdmTracking() throws SBusinessDataRepositoryException { try { // Delete all records without any restriction dataRetentionBdmTrackingService.deleteAll(); } catch (SDataRetentionBdmTrackingException e) { throw new SBusinessDataRepositoryException( "Failed to delete data retention BDM tracking records during BDM uninstall", e); } } @Override public boolean isDeployed(byte[] bdmArchive) throws InvalidBusinessDataModelException, SBusinessDataRepositoryDeploymentException { try { var bdmDependencyId = dependencyService.getIdOfDependencyOfArtifactForTenant(BDR_DEPENDENCY_FILENAME); if (bdmDependencyId.isEmpty()) { log.debug("No BDM currently deployed."); return false; } var existingSha3 = sha256ToHex(dependencyService.getDependency(bdmDependencyId.get()).getValue()); var newServerJar = generateServerBDMJar(getBusinessObjectModel(bdmArchive), false); var newSha3 = sha256ToHex(newServerJar); log.debug("BDM binary sha3256 comparison: current={}, new={}", existingSha3, newSha3); return Objects.equals(existingSha3, newSha3); } catch (SDependencyNotFoundException e) { log.error("Failed to retrieve existing bdm dependency", e); return false; } catch (SBonitaReadException e) { log.error("Failed to retrieve {} dependency", BDR_DEPENDENCY_FILENAME, e); return false; } } @SneakyThrows private static String sha256ToHex(byte[] source) { final MessageDigest digest = MessageDigest.getInstance("SHA3-256"); return bytesToHex(digest.digest(source)); } private static String bytesToHex(byte[] hash) { StringBuilder hexString = new StringBuilder(2 * hash.length); for (byte b : hash) { hexString.append(String.format("%02x", b)); } return hexString.toString(); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/BusinessDataReloader.java ================================================ /** * 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.business.data.impl; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.SBusinessDataNotFoundException; import org.bonitasoft.engine.business.data.proxy.ServerProxyfier; import org.hibernate.proxy.HibernateProxyHelper; /** * @author Elias Ricken de Medeiros */ public class BusinessDataReloader { private final BusinessDataRepository businessDataRepository; public BusinessDataReloader(BusinessDataRepository businessDataRepository) { this.businessDataRepository = businessDataRepository; } /** * Reloads the {@link Entity} from database using the current {@code Entity} className and persistenceId * * @param entityToReload the entity to be reloaded * @return the {@link Entity} reload from the database * @throws SBusinessDataNotFoundException */ public Entity reloadEntity(Entity entityToReload) throws SBusinessDataNotFoundException { final Class realClass = getEntityRealClass(entityToReload); return businessDataRepository.findById(realClass, entityToReload.getPersistenceId()); } public Class getEntityRealClass(Entity entity) { return HibernateProxyHelper.getClassWithoutInitializingProxy(ServerProxyfier.unProxifyIfNeeded(entity)); } /** * Reloads the {@link Entity} from database using the current {@code Entity} className and persistenceId if * persistenceId is set. Otherwise returns the * object itself. * * @param entityToReload the entity to be reloaded * @return the {@link Entity} reload from the database if the persistenceId is set or the object itself. * @throws SBusinessDataNotFoundException */ public Entity reloadEntitySoftly(Entity entityToReload) throws SBusinessDataNotFoundException { if (entityToReload.getPersistenceId() == null) { return entityToReload; } return reloadEntity(entityToReload); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/BusinessDataRepositoryEventAspect.java ================================================ /** * Copyright (C) 2025 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.business.data.impl; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.events.model.SDeleteEvent; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.events.model.SFireEventException; import org.bonitasoft.engine.events.model.SInsertEvent; import org.bonitasoft.engine.events.model.SUpdateEvent; import org.springframework.stereotype.Component; /** * Aspect to handle events for business data repository operations. * Fires events on persist, merge, and remove operations. * This aspect is enabled in EngineConfiguration class through annotation @EnableAspectJAutoProxy. */ @Aspect @Component public class BusinessDataRepositoryEventAspect { private final EventService eventService; public BusinessDataRepositoryEventAspect(EventService eventService) { this.eventService = eventService; } @Around("execution(* org.bonitasoft.engine.business.data.BusinessDataRepository.persist(..)) && args(entity)") public Object aroundPersist(ProceedingJoinPoint target, Entity entity) throws Throwable { return persistOrMerge(target, entity); } @Around("execution(* org.bonitasoft.engine.business.data.BusinessDataRepository.merge(..)) && args(entity)") public Object aroundMerge(ProceedingJoinPoint target, Entity entity) throws Throwable { return persistOrMerge(target, entity); } private Object persistOrMerge(ProceedingJoinPoint target, Entity entity) throws Throwable { if (entity != null) { var originalPersistenceId = entity.getPersistenceId(); Object result = target.proceed(); var event = (originalPersistenceId == null) ? new SInsertEvent(getEventType(SEvent.CREATED)) : new SUpdateEvent(getEventType(SEvent.UPDATED)); event.setObject(entity); eventService.fireEvent(event); return result; } return null; } @AfterReturning("execution(* org.bonitasoft.engine.business.data.BusinessDataRepository.remove(..)) && args(entity)") public void afterRemove(Entity entity) throws SFireEventException { fireDeleteEvent(entity); } @AfterReturning(pointcut = "execution(* org.bonitasoft.engine.business.data.BusinessDataRepository.removeById(..))", returning = "entity") public void afterRemoveById(Entity entity) throws SFireEventException { fireDeleteEvent(entity); } private void fireDeleteEvent(Entity entity) throws SFireEventException { var event = new SDeleteEvent(getEventType(SEvent.DELETED)); event.setObject(entity); eventService.fireEvent(event); } private static String getEventType(String type) { return "BUSINESS_DATA" + type; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/BusinessDataServiceImpl.java ================================================ /** * 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.business.data.impl; import static java.lang.String.format; import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.engine.bdm.BDMQueryUtil; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.Query; import org.bonitasoft.engine.bdm.model.QueryParameter; import org.bonitasoft.engine.bdm.model.field.RelationField.Type; import org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryResult; import org.bonitasoft.engine.bpm.businessdata.impl.BusinessDataQueryMetadataImpl; import org.bonitasoft.engine.bpm.businessdata.impl.BusinessDataQueryResultImpl; import org.bonitasoft.engine.business.data.BusinessDataModelRepository; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.BusinessDataService; import org.bonitasoft.engine.business.data.JsonBusinessDataSerializer; import org.bonitasoft.engine.business.data.NonUniqueResultException; import org.bonitasoft.engine.business.data.SBusinessDataNotFoundException; import org.bonitasoft.engine.business.data.SBusinessDataRepositoryException; import org.bonitasoft.engine.commons.ClassReflector; import org.bonitasoft.engine.commons.JavaMethodInvoker; import org.bonitasoft.engine.commons.TypeConverterUtil; import org.bonitasoft.engine.commons.exceptions.SReflectException; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Service; /** * Orchestrates Business Data Model (BDM) operations and JSON serialization for REST APIs. *

      * This service acts as the main facade for business data operations, coordinating between: *

        *
      • {@link BusinessDataRepository} for entity persistence and query execution
      • *
      • {@link JsonBusinessDataSerializer} for JSON serialization with standard/legacy shape support
      • *
      • {@link BusinessDataModelRepository} for BDM metadata and query definitions
      • *
      *

      * Key responsibilities: *

        *
      • Execute named queries and serialize results to JSON (via {@link #getJsonQueryEntities})
      • *
      • Retrieve entities by ID and serialize them (via {@link #getJsonEntity}, {@link #getJsonEntities})
      • *
      • Handle Java operations on entities (setters/getters) with aggregation/composition relationship management
      • *
      • Validate query parameters against BDM query definitions
      • *
      • Provide count metadata for paginated query results
      • *
      • Determine which JSON serialization shape to use (standard vs legacy) based on configuration
      • *
      * * @see BusinessDataService * @see JsonBusinessDataSerializerImpl for JSON serialization details */ @Service @ConditionalOnSingleCandidate(BusinessDataService.class) public class BusinessDataServiceImpl implements BusinessDataService { protected final BusinessDataRepository businessDataRepository; private final JsonBusinessDataSerializer jsonBusinessDataSerializer; protected final BusinessDataModelRepository businessDataModelRepository; private final TypeConverterUtil typeConverterUtil; private final BusinessDataReloader businessDataReloader; private final CountQueryProvider countQueryProvider; public BusinessDataServiceImpl(final BusinessDataRepository businessDataRepository, final JsonBusinessDataSerializer jsonBusinessDataSerializer, final BusinessDataModelRepository businessDataModelRepository, final TypeConverterUtil typeConverterUtil, BusinessDataReloader businessDataReloader, CountQueryProvider countQueryProvider) { this.businessDataRepository = businessDataRepository; this.jsonBusinessDataSerializer = jsonBusinessDataSerializer; this.businessDataModelRepository = businessDataModelRepository; this.typeConverterUtil = typeConverterUtil; this.businessDataReloader = businessDataReloader; this.countQueryProvider = countQueryProvider; } @Override public boolean isBusinessData(final Object data) { return isEntity(data) || isListOfEntities(data); } private boolean isListOfEntities(final Object data) { if (data == null) { return false; } if (!List.class.isAssignableFrom(data.getClass())) { return false; } @SuppressWarnings("rawtypes") final List dataList = (List) data; if (dataList.isEmpty()) { return true; } return isEntity(dataList.get(0)); } private boolean isEntity(final Object data) { if (data == null) { return false; } return Entity.class.isAssignableFrom(data.getClass()); } @SuppressWarnings("unchecked") @Override public Object callJavaOperation(final Object businessObject, final Object valueToSetObjectWith, final String methodName, final String parameterType) throws SBusinessDataNotFoundException, SBusinessDataRepositoryException { if (businessObject == null) { throw new SBusinessDataNotFoundException("business data is null"); } if (isEntity(businessObject)) { return callJavaOperationOnEntity((Entity) businessObject, valueToSetObjectWith, methodName, parameterType); } if (isListOfEntities(businessObject)) { return callJavaOperationOnEntityList((List) businessObject, valueToSetObjectWith, methodName, parameterType); } throw new SBusinessDataRepositoryException("not a business data"); } private Object callJavaOperationOnEntityList(final List businessObject, final Object valueToSetObjectWith, final String methodName, final String parameterType) throws SBusinessDataRepositoryException, SBusinessDataNotFoundException { try { invokeJavaMethod(businessObject, methodName, parameterType, valueToSetObjectWith); return businessObject; } catch (final Exception e) { throw new SBusinessDataRepositoryException(e); } } private Object callJavaOperationOnEntity(final Entity businessObject, final Object valueToSetObjectWith, final String methodName, final String parameterType) throws SBusinessDataRepositoryException, SBusinessDataNotFoundException { Entity jpaEntity = businessDataReloader.reloadEntitySoftly(businessObject); final Object valueToSet = loadValueToSet(businessObject, valueToSetObjectWith, methodName); try { invokeJavaMethod(jpaEntity, methodName, parameterType, valueToSet); // TODO Auto-generated method stub return jpaEntity; } catch (final Exception e) { throw new SBusinessDataRepositoryException(e); } } protected void invokeJavaMethod(final Object objectToSet, final String methodName, final String parameterType, final Object valueToSet) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { final JavaMethodInvoker methodInvoker = new JavaMethodInvoker(); methodInvoker.invokeJavaMethod(parameterType, valueToSet, objectToSet, methodName, parameterType); } @SuppressWarnings("unchecked") private Object loadValueToSet(final Entity businessObject, final Object valueToSetObjectWith, final String methodName) throws SBusinessDataNotFoundException, SBusinessDataRepositoryException { Object valueToSet; if (isEntity(valueToSetObjectWith)) { final Type relationType = getRelationType(businessObject, methodName); valueToSet = getPersistedValue((Entity) valueToSetObjectWith, relationType); } else if (isListOfEntities(valueToSetObjectWith)) { final Type relationType = getRelationType(businessObject, methodName); valueToSet = getPersistedValues((List) valueToSetObjectWith, relationType); } else { valueToSet = valueToSetObjectWith; } return valueToSet; } private List getPrimaryKeys(final List entities) throws SBusinessDataNotFoundException { List primaryKeys; primaryKeys = new ArrayList<>(); for (final Entity entity : entities) { if (entity.getPersistenceId() == null) { throw new SBusinessDataNotFoundException(format( "Forbidden instance of %s found. It is only possible to reference persisted instances in an aggregation relation.", businessDataReloader.getEntityRealClass(entity).getName())); } primaryKeys.add(entity.getPersistenceId()); } return primaryKeys; } private Object getPersistedValues(final List entities, final Type type) throws SBusinessDataNotFoundException { if (entities.isEmpty()) { return new ArrayList(); } if (Type.AGGREGATION.equals(type)) { return businessDataRepository.findByIds(businessDataReloader.getEntityRealClass(entities.get(0)), getPrimaryKeys(entities)); } else { return entities; } } private Entity getPersistedValue(final Entity entity, final Type type) throws SBusinessDataNotFoundException { if (Type.AGGREGATION.equals(type)) { try { return businessDataReloader.reloadEntity(entity); } catch (SBusinessDataNotFoundException e) { throw new SBusinessDataNotFoundException(format( "Forbidden instance of %s found. It is only possible to reference persisted instances in an aggregation relation.", businessDataReloader.getEntityRealClass(entity).getName()), e); } } else { return entity; } } private Type getRelationType(final Entity businessObject, final String methodName) throws SBusinessDataRepositoryException { final String fieldName = ClassReflector.getFieldName(methodName); Annotation[] annotations; try { annotations = businessObject.getClass().getDeclaredField(fieldName).getAnnotations(); } catch (final NoSuchFieldException e) { return null; } catch (final SecurityException e) { throw new SBusinessDataRepositoryException(e); } for (final Annotation annotation : annotations) { final Set> annotationKeySet = getAnnotationKeySet(); if (annotationKeySet.contains(annotation.annotationType())) { try { final Method cascade = annotation.getClass().getMethod("cascade"); final CascadeType[] cascadeTypes = (CascadeType[]) cascade.invoke(annotation); if (CascadeType.MERGE.equals(cascadeTypes[0])) { return Type.AGGREGATION; } if (CascadeType.ALL.equals(cascadeTypes[0])) { return Type.COMPOSITION; } } catch (final Exception e) { throw new SBusinessDataRepositoryException(e); } } } return null; } private Set> getAnnotationKeySet() { // FIXME use custom annotation on methods final Set> annotationKeySet = new HashSet<>(); annotationKeySet.add(OneToOne.class); annotationKeySet.add(OneToMany.class); annotationKeySet.add(ManyToMany.class); annotationKeySet.add(ManyToOne.class); return annotationKeySet; } @Override public Serializable getJsonEntity(final String entityClassName, final Long identifier, final String businessDataURIPattern) throws SBusinessDataNotFoundException, SBusinessDataRepositoryException { final Class entityClass = loadClass(entityClassName); final Entity entity = businessDataRepository.findById(entityClass, identifier); return jsonBusinessDataSerializer.serializeEntity(entity, businessDataURIPattern); } @Override @SuppressWarnings("unchecked") public Serializable getJsonEntities(final String entityClassName, final List identifiers, final String businessDataURIPattern) throws SBusinessDataRepositoryException { final Class entityClass = loadClass(entityClassName); final List entities = businessDataRepository.findByIdentifiers(entityClass, identifiers); return jsonBusinessDataSerializer.serializeEntities(entities, businessDataURIPattern); } @SuppressWarnings("unchecked") @Override public Serializable getJsonChildEntity(final String entityClassName, final Long identifier, final String childFieldName, final String businessDataURIPattern) throws SBusinessDataNotFoundException, SBusinessDataRepositoryException { final Class entityClass = loadClass(entityClassName); final Object entity = businessDataRepository.findById(entityClass, identifier); Object childEntity; java.lang.reflect.Type getterReturnType; try { final String getterName = ClassReflector.getGetterName(childFieldName); childEntity = ClassReflector.invokeGetter(entity, getterName); getterReturnType = ClassReflector.getGetterReturnType(entityClass, getterName); } catch (final SReflectException e) { throw new SBusinessDataRepositoryException(e); } if (childEntity == null) { return JsonBusinessDataSerializer.EMPTY_OBJECT; } if (childEntity instanceof Entity) { final Entity unwrap = businessDataRepository.unwrap((Entity) childEntity); return jsonBusinessDataSerializer.serializeEntity(unwrap, businessDataURIPattern); } else if (childEntity instanceof List) { final Class type = (Class) ((ParameterizedType) getterReturnType).getActualTypeArguments()[0]; if (Entity.class.isAssignableFrom(type)) { return jsonBusinessDataSerializer.serializeEntities((List) childEntity, businessDataURIPattern); } } return null; } @Override public BusinessDataQueryResult getJsonQueryEntities(final String entityClassName, final String queryName, final Map parameters, final Integer startIndex, final Integer maxResults, final String businessDataURIPattern) throws SBusinessDataRepositoryException { final Class businessDataClass = loadClass(entityClassName); BusinessObject businessObject = getBusinessObjectFromClassName(entityClassName); final Query queryDefinition = getQueryDefinition(businessObject, entityClassName, queryName); final Map queryParameters = getQueryParameters(queryDefinition, parameters); final List list = businessDataRepository.findListByNamedQuery( getQualifiedQueryName(businessDataClass, queryName), getQueryReturnType(queryDefinition, entityClassName), queryParameters, startIndex, maxResults); BusinessDataQueryMetadataImpl businessDataQueryMetadata = null; final Query countQueryDefinition = getCountQueryDefinition(businessDataClass, businessObject, queryDefinition); if (countQueryDefinition != null) { try { businessDataQueryMetadata = new BusinessDataQueryMetadataImpl(startIndex, maxResults, (Long) businessDataRepository.findByNamedQuery( getQualifiedQueryName(businessDataClass, countQueryDefinition.getName()), getQueryReturnType(countQueryDefinition, entityClassName), queryParameters)); } catch (NonUniqueResultException e) { throw new SBusinessDataRepositoryException("unable to count results for query " + queryName); } } Serializable jsonResults; boolean useStandardShape = jsonBusinessDataSerializer.isStandardShapeEnabled(); if (isScalarQuery(queryDefinition)) { // SCALAR QUERY (Double, Float, Integer, Long) jsonResults = jsonBusinessDataSerializer.serializeScalarResult(list, entityClassName, useStandardShape); } else { // ENTITY QUERY (List or single entity) jsonResults = jsonBusinessDataSerializer.serializeEntityQueryResult((List) list, businessDataURIPattern, useStandardShape, queryDefinition.hasMultipleResults()); } return new BusinessDataQueryResultImpl(jsonResults, businessDataQueryMetadata); } private Query getCountQueryDefinition(Class businessDataClass, BusinessObject businessObject, Query queryDefinition) { final Query countQueryDefinition = countQueryProvider.getCountQueryDefinition(businessObject, queryDefinition); if (ensureQueryIsDefinedInEntity(businessDataClass, countQueryDefinition)) { return countQueryDefinition; } return null; } private boolean isScalarQuery(Query queryDefinition) { String type = queryDefinition.getReturnType(); if (type == null) { return false; } return type.equals("java.lang.Double") || type.equals("java.lang.Integer") || type.equals("java.lang.Float") || type.equals("java.lang.Long"); // Includes COUNT queries and other Long scalar queries } private boolean ensureQueryIsDefinedInEntity(Class businessDataClass, Query countQueryDefinition) { final NamedQueries namedQueries = businessDataClass.getAnnotation(NamedQueries.class); if (namedQueries == null || countQueryDefinition == null) { return false; } for (NamedQuery namedQuery : namedQueries.value()) { if (namedQuery.name().equals(getQualifiedQueryName(businessDataClass, countQueryDefinition.getName()))) { return true; } } return false; } private Class getQueryReturnType(final Query queryDefinition, final String entityClassName) throws SBusinessDataRepositoryException { if (queryDefinition.hasMultipleResults()) { return loadClass(entityClassName); } try { return (Class) Thread.currentThread().getContextClassLoader() .loadClass(queryDefinition.getReturnType()); } catch (final ClassNotFoundException e) { throw new SBusinessDataRepositoryException("unable to load class " + queryDefinition.getReturnType()); } } private String getQualifiedQueryName(final Class businessDataClass, final String queryName) { return format("%s.%s", businessDataClass.getSimpleName(), queryName); } private Map getQueryParameters(final Query queryDefinition, final Map parameters) throws SBusinessDataRepositoryException { final Set errors = new HashSet<>(); final Map queryParameters = new HashMap<>(); for (final QueryParameter queryParameter : queryDefinition.getQueryParameters()) { if (parameters != null && parameters.containsKey(queryParameter.getName())) { queryParameters.put(queryParameter.getName(), convertToType(loadSerializableClass(queryParameter.getClassName()), parameters.get(queryParameter.getName()))); } else { errors.add(queryParameter.getName()); } } if (!errors.isEmpty()) { final StringBuilder errorMessage = new StringBuilder().append("parameter(s) are missing for query named ") .append(queryDefinition.getName()) .append(" : "); errorMessage.append(StringUtils.join(errors, ",")); throw new SBusinessDataRepositoryException(errorMessage.toString()); } return queryParameters; } private Serializable convertToType(final Class clazz, final Serializable parameterValue) { return (Serializable) typeConverterUtil.convertToType(clazz, parameterValue); } private Query getQueryDefinition(BusinessObject businessObject, String className, final String queryName) throws SBusinessDataRepositoryException { final List allQueries = new ArrayList<>(); allQueries.addAll(businessObject.getQueries()); allQueries.addAll(BDMQueryUtil.createProvidedQueriesForBusinessObject(businessObject)); for (final Query query : allQueries) { if (query.getName().equals(queryName)) { return query; } } throw new SBusinessDataRepositoryException( "unable to get query " + queryName + " for business object " + className); } private BusinessObject getBusinessObjectFromClassName(String className) throws SBusinessDataRepositoryException { BusinessObject businessObject = null; final BusinessObjectModel businessObjectModel = businessDataModelRepository.getBusinessObjectModel(); if (businessObjectModel != null) { for (final BusinessObject currentBo : businessObjectModel.getBusinessObjects()) { if (currentBo.getQualifiedName().equals(className)) { businessObject = currentBo; } } } return businessObject; } @SuppressWarnings("unchecked") protected Class loadClass(final String returnType) throws SBusinessDataRepositoryException { try { return (Class) Thread.currentThread().getContextClassLoader().loadClass(returnType); } catch (final ClassNotFoundException e) { throw new SBusinessDataRepositoryException(e); } } protected Class loadSerializableClass(final String className) throws SBusinessDataRepositoryException { try { return (Class) Thread.currentThread().getContextClassLoader().loadClass(className); } catch (final ClassNotFoundException e) { throw new SBusinessDataRepositoryException("unable to load class " + className); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/CountQueryProvider.java ================================================ /** * 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.business.data.impl; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.bonitasoft.engine.bdm.BDMQueryUtil; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.Query; /** * @author Elias Ricken de Medeiros */ public class CountQueryProvider { public Query getCountQueryDefinition(BusinessObject businessObject, Query baseQuery) { if (!baseQuery.hasMultipleResults()) { return null; } List queries = new ArrayList<>(); queries.addAll(BDMQueryUtil.createCountProvidedQueriesForBusinessObject(businessObject)); queries.addAll(businessObject.getQueries()); return findRelatedCountQuery(baseQuery, queries); } private Query findRelatedCountQuery(Query baseQuery, List queryList) { Query countQuery = null; Iterator iterator = queryList.iterator(); while (iterator.hasNext() && countQuery == null) { Query currentQuery = iterator.next(); if (Long.class.getName().equals(currentQuery.getReturnType()) && currentQuery.getName().equals(BDMQueryUtil.getCountQueryName(baseQuery.getName()))) { countQuery = currentQuery; } } return countQuery; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/DataRetentionBdmTrackingRepositoryImpl.java ================================================ /** * Copyright (C) 2026 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.business.data.impl; import java.util.List; import java.util.Map; import lombok.RequiredArgsConstructor; import org.bonitasoft.engine.business.data.DataRetentionBdmTrackingRepository; import org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.SPersistenceException; import org.bonitasoft.engine.services.UpdateDescriptor; import org.springframework.stereotype.Repository; /** * Hibernate-based implementation of {@link DataRetentionBdmTrackingRepository}. *

      * Uses {@link PersistenceService} directly (not {@code Recorder}) to avoid generating * queriable log entries — tracking records are internal bookkeeping for the data retention * service, not user-auditable actions. *

      * All operations target the Bonita DB (not the Business Data DB). */ @Repository @RequiredArgsConstructor public class DataRetentionBdmTrackingRepositoryImpl implements DataRetentionBdmTrackingRepository { private final PersistenceService persistenceService; @Override public void create(SDataRetentionBdmTracking tracking) throws SPersistenceException { persistenceService.insert(tracking); } @Override public void updateLastModifiedDate(SDataRetentionBdmTracking tracking) throws SPersistenceException { persistenceService.update( UpdateDescriptor.buildSetField(tracking, "lastModifiedAt", tracking.getLastModifiedAt())); } @Override public SDataRetentionBdmTracking getByDataIdAndClassname(long dataId, String dataClassname) throws SBonitaReadException { return persistenceService.selectOne(new SelectOneDescriptor<>( "getDataRetentionBdmTrackingByDataIdAndClassname", Map.of("dataId", dataId, "dataClassname", dataClassname), SDataRetentionBdmTracking.class)); } @Override public List getByClassname(String dataClassname) throws SBonitaReadException { return persistenceService.selectList(new SelectListDescriptor<>( "getDataRetentionBdmTrackingByClassname", Map.of("dataClassname", dataClassname), SDataRetentionBdmTracking.class, QueryOptions.ALL_RESULTS)); } @Override public List getExpiredByCreatedDate(String dataClassname, long deadline) throws SBonitaReadException { return persistenceService.selectList(new SelectListDescriptor<>( "getExpiredTrackingByClassnameAndCreatedAt", Map.of("dataClassname", dataClassname, "deadline", deadline), SDataRetentionBdmTracking.class, QueryOptions.ALL_RESULTS)); } @Override public List getExpiredByLastModifiedDate(String dataClassname, long deadline) throws SBonitaReadException { return persistenceService.selectList(new SelectListDescriptor<>( "getExpiredTrackingByClassnameAndLastModifiedAt", Map.of("dataClassname", dataClassname, "deadline", deadline), SDataRetentionBdmTracking.class, QueryOptions.ALL_RESULTS)); } @Override public void delete(SDataRetentionBdmTracking tracking) throws SPersistenceException { persistenceService.delete(tracking); } @Override public int delete(long dataId, String dataClassname) throws SPersistenceException { return persistenceService.update("deleteDataRetentionBdmTrackingByDataIdAndClassname", Map.of("dataId", dataId, "dataClassname", dataClassname)); } @Override public void deleteAll(List filterOptions) throws SPersistenceException { persistenceService.deleteAll(SDataRetentionBdmTracking.class, filterOptions); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/DataRetentionBdmTrackingServiceImpl.java ================================================ /** * Copyright (C) 2026 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.business.data.impl; import java.util.Collections; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.business.data.DataRetentionBdmTrackingRepository; import org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService; import org.bonitasoft.engine.business.data.SDataRetentionBdmTrackingException; import org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.services.SPersistenceException; import org.springframework.stereotype.Service; /** * Implementation of {@link DataRetentionBdmTrackingService} that delegates to * {@link DataRetentionBdmTrackingRepository} and wraps persistence-layer exceptions * into {@link SDataRetentionBdmTrackingException}. */ @Service("dataRetentionBdmTrackingService") @Slf4j @RequiredArgsConstructor public class DataRetentionBdmTrackingServiceImpl implements DataRetentionBdmTrackingService { private final DataRetentionBdmTrackingRepository bdmTrackingRepository; @Override public void create(long dataId, String dataClassname) throws SDataRetentionBdmTrackingException { try { long now = System.currentTimeMillis(); bdmTrackingRepository.create( SDataRetentionBdmTracking.builder() .dataId(dataId) .dataClassname(dataClassname) .createdAt(now) .lastModifiedAt(now) .build()); } catch (SPersistenceException e) { throw new SDataRetentionBdmTrackingException( "Failed to create BDM tracking record with data id " + dataId + " and classname " + dataClassname, e); } } @Override public void upsert(long dataId, String dataClassname) throws SDataRetentionBdmTrackingException { try { SDataRetentionBdmTracking tracking = bdmTrackingRepository.getByDataIdAndClassname(dataId, dataClassname); if (tracking != null) { tracking.setLastModifiedAt(System.currentTimeMillis()); bdmTrackingRepository.updateLastModifiedDate(tracking); } else { log.debug("No tracking record found for {}#{}, creating one", dataClassname, dataId); create(dataId, dataClassname); } } catch (SBonitaReadException | SPersistenceException e) { throw new SDataRetentionBdmTrackingException( "Failed to upsert data retention tracking record for " + dataClassname + "#" + dataId, e); } } @Override public void updateLastModifiedDate(long id) throws SDataRetentionBdmTrackingException { try { bdmTrackingRepository.updateLastModifiedDate( SDataRetentionBdmTracking.builder() .id(id) .lastModifiedAt(System.currentTimeMillis()) .build()); } catch (SPersistenceException e) { throw new SDataRetentionBdmTrackingException("Failed to update BDM tracking record with id " + id, e); } } @Override public void delete(long dataId, String dataClassname) throws SDataRetentionBdmTrackingException { try { int rowsDeleted = bdmTrackingRepository.delete(dataId, dataClassname); if (rowsDeleted == 0) { log.debug("No tracking record found for {}#{}, nothing to delete", dataClassname, dataId); } } catch (SPersistenceException e) { throw new SDataRetentionBdmTrackingException( "Failed to delete data retention tracking record for " + dataClassname + "#" + dataId, e); } } @Override public void deleteAll() throws SDataRetentionBdmTrackingException { try { bdmTrackingRepository.deleteAll(Collections.emptyList()); } catch (SPersistenceException e) { throw new SDataRetentionBdmTrackingException("Failed to delete all BDM tracking records", e); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/EntityManagerFactoryAware.java ================================================ /** * Copyright (C) 2025 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.business.data.impl; import javax.persistence.EntityManagerFactory; /** * Interface to be implemented by classes that need access to the {@link EntityManagerFactory}. * This is typically used in the context of business data repositories. * This class was introduced with the use of aspects for business data repository: A proxy class is generated, such that * {@link JPABusinessDataRepositoryImpl} cannot be cast directly. Introducing this interface allows * JPABusinessDataRepositoryImpl to be cast to this interface (See BDRepositoryLocalIT class). */ public interface EntityManagerFactoryAware { EntityManagerFactory getEntityManagerFactory(); } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/JPABusinessDataRepositoryImpl.java ================================================ /** * Copyright (C) 2015 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.business.data.impl; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; 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 javax.persistence.EntityGraph; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.NoResultException; import javax.persistence.Persistence; import javax.persistence.PersistenceException; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Root; import javax.persistence.metamodel.EntityType; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.bdm.model.QueryParameterTypes; import org.bonitasoft.engine.bdm.model.field.Field; import org.bonitasoft.engine.business.data.BusinessDataModelRepository; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService; import org.bonitasoft.engine.business.data.NonUniqueResultException; import org.bonitasoft.engine.business.data.SBusinessDataNotFoundException; import org.bonitasoft.engine.business.data.SDataRetentionBdmTrackingException; import org.bonitasoft.engine.classloader.ClassLoaderIdentifier; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.classloader.SingleClassLoaderListener; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.commons.exceptions.SRetryableException; import org.bonitasoft.engine.transaction.STransactionNotFoundException; import org.bonitasoft.engine.transaction.UserTransactionService; import org.hibernate.Hibernate; import org.hibernate.QueryException; import org.hibernate.boot.archive.scan.internal.DisabledScanner; import org.hibernate.proxy.HibernateProxy; /** * Some of these methods are enriched with aspects to throw events. See BusinessDataRepositoryEventAspect for details. * * @author Matthieu Chaffotte * @author Romain Bioteau */ @Slf4j public class JPABusinessDataRepositoryImpl implements BusinessDataRepository, EntityManagerFactoryAware, SingleClassLoaderListener { private static final String BDR_PERSISTENCE_UNIT = "BDR"; private final Map configuration; private EntityManagerFactory entityManagerFactory; private final ThreadLocal managers = new ThreadLocal<>(); private final BusinessDataModelRepository businessDataModelRepository; private final UserTransactionService transactionService; private final DataRetentionBdmTrackingService dataRetentionBdmTrackingService; public JPABusinessDataRepositoryImpl( final UserTransactionService transactionService, final BusinessDataModelRepository businessDataModelRepository, final Map configuration, ClassLoaderService classLoaderService, DataRetentionBdmTrackingService dataRetentionBdmTrackingService) { this.transactionService = transactionService; this.businessDataModelRepository = businessDataModelRepository; this.configuration = new HashMap<>(configuration); this.configuration.put("hibernate.archive.scanner", DisabledScanner.class.getName()); classLoaderService.addListener(ClassLoaderIdentifier.TENANT, this); this.dataRetentionBdmTrackingService = dataRetentionBdmTrackingService; } @Override public void start() { if (entityManagerFactory == null && businessDataModelRepository.isBDMDeployed()) { log.debug("Creating Entity Manager Factory"); recreateEntityManagerFactoryEvenIfExisting(); } } EntityManagerFactory createEntityManagerFactory() { return Persistence.createEntityManagerFactory(BDR_PERSISTENCE_UNIT, configuration); } @Override public void stop() { if (getEntityManagerFactory() != null) { log.debug("Closing Entity Manager Factory because service is stopping"); getEntityManagerFactory().close(); entityManagerFactory = null; log.debug("Entity Manager Factory closed"); } } private synchronized void recreateEntityManagerFactoryOnClassLoaderChange(ClassLoader newClassLoader) { if (businessDataModelRepository.isBDMDeployed()) { log.debug("Recreating Entity Manager Factory for classloader {}", newClassLoader); final ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(newClassLoader); recreateEntityManagerFactoryEvenIfExisting(); } finally { Thread.currentThread().setContextClassLoader(currentClassLoader); } log.debug("Entity Manager Factory recreated"); } else { log.debug("No BDM deployed. No Entity Manager Factory to recreate."); } } private void recreateEntityManagerFactoryEvenIfExisting() { if (entityManagerFactory != null) { log.warn("Entity Manager Factory should be null. Closing it and recreating a new one."); entityManagerFactory.close(); } entityManagerFactory = createEntityManagerFactory(); log.debug("Recreated Entity Manager Factory: {}", entityManagerFactory); } public EntityManagerFactory getEntityManagerFactory() { if (entityManagerFactory == null) { /* * in case the entity manager factory is reloading inside #recreateEntityManagerFactory * we get it inside a method synchronized with #recreateEntityManagerFactory */ return synchronizedGetEntityManagerFactory(); } return entityManagerFactory; } private synchronized EntityManagerFactory synchronizedGetEntityManagerFactory() { return entityManagerFactory; } @Override public void pause() { stop(); } @Override public void resume() { start(); } @Override public Set getEntityClassNames() { if (getEntityManagerFactory() == null) { return Collections.emptySet(); } final EntityManager em = getEntityManager(); final Set> entities = em.getMetamodel().getEntities(); final Set entityClassNames = new HashSet<>(); for (final EntityType entity : entities) { entityClassNames.add(entity.getJavaType().getName()); } return entityClassNames; } protected EntityManager getEntityManager() { if (getEntityManagerFactory() == null) { throw new IllegalStateException("The BDR is not started"); } EntityManager manager = managers.get(); if (manager != null && !isPartOfCurrentTransaction(manager)) { log.warn("Stale BDM EntityManager detected: not part of the current transaction. " + "It will be replaced with a fresh one. " + "This typically happens after a JTA transaction timeout."); closeQuietly(manager); managers.remove(); manager = null; } if (manager == null) { manager = getEntityManagerFactory().createEntityManager(); try { transactionService.registerBonitaSynchronization(new RemoveEntityManagerSynchronization(managers)); } catch (final STransactionNotFoundException stnfe) { closeQuietly(manager); throw new IllegalStateException(stnfe); } managers.set(manager); log.debug("Created new BDM EntityManager for current transaction"); } else { log.debug("Reusing existing BDM EntityManager in current transaction."); } try { manager.joinTransaction(); } catch (Exception e) { log.warn( "BDM EntityManager failed to join current transaction. This may indicate the transaction is already marked for rollback.", e); closeQuietly(manager); managers.remove(); throw e; } return manager; } private boolean isPartOfCurrentTransaction(EntityManager manager) { try { return manager.isOpen() && manager.isJoinedToTransaction(); } catch (Exception e) { log.warn("Stale BDM EntityManager detected (exception during transaction state check). " + "A fresh EntityManager will be created for the current transaction.", e); return false; } } private void closeQuietly(EntityManager manager) { try { if (manager.isOpen()) { manager.close(); } } catch (Exception e) { log.warn("Failed to close stale BDM EntityManager. " + "This may indicate a resource leak if it happens repeatedly.", e); } } @Override public T findById(final Class entityClass, final Long primaryKey) throws SBusinessDataNotFoundException { if (primaryKey == null) { throw new SBusinessDataNotFoundException( "Impossible to get data of type " + entityClass.getName() + " with a null identifier"); } final EntityManager em = getEntityManager(); final T entity; try { entity = em.find(entityClass, primaryKey); } catch (final PersistenceException e) { log.debug("BDM findById({}, id={}) failed", entityClass.getSimpleName(), primaryKey, e); //wrap in retryable exception because the issue might come from BDR reloading throw new SRetryableException(e); } if (entity == null) { throw new SBusinessDataNotFoundException( "Impossible to get data of type " + entityClass.getName() + " with id: " + primaryKey); } return entity; } @Override public List findByIds(final Class entityClass, final List primaryKeys) { if (primaryKeys == null || primaryKeys.isEmpty()) { return new ArrayList<>(); } final EntityManager em = getEntityManager(); try { final CriteriaBuilder cb = em.getCriteriaBuilder(); final CriteriaQuery criteriaQuery = cb.createQuery(entityClass); final Root row = criteriaQuery.from(entityClass); criteriaQuery.select(row).where(row.get(Field.PERSISTENCE_ID).in(primaryKeys)); return em.createQuery(criteriaQuery).getResultList(); } catch (final PersistenceException e) { //wrap in retryable exception because the issue might come from BDR reloading throw new SRetryableException(e); } } @Override public List findByIdentifiers(final Class entityClass, final List primaryKeys) { if (primaryKeys == null || primaryKeys.isEmpty()) { return new ArrayList<>(); } final List entities = new ArrayList<>(); for (final Long primaryKey : primaryKeys) { try { entities.add(findById(entityClass, primaryKey)); } catch (final SBusinessDataNotFoundException e) { // If the business data does not exist, do not add it in the result list in order to have the same behaviour as findByIds } } return entities; } protected T find(final Class resultClass, final TypedQuery query, final Map parameters) throws NonUniqueResultException { if (query == null) { throw new IllegalArgumentException("query is null"); } if (parameters != null) { for (final Entry parameter : parameters.entrySet()) { query.setParameter(parameter.getKey(), checkParameterValue(parameter.getValue())); } } try { return query.getSingleResult(); } catch (final javax.persistence.NonUniqueResultException nure) { throw new NonUniqueResultException(nure); } catch (final NoResultException e) { return null; } } Object checkParameterValue(final Serializable parameterValue) { if (parameterValue != null && !QueryParameterTypes.contains(parameterValue.getClass())) { throw new IllegalArgumentException(String.format( "'%s' is not a supported type for a query parameter.", parameterValue.getClass().getName())); } if (parameterValue instanceof Object[]) { return Arrays.asList((Object[]) parameterValue); } return parameterValue; } @Override public T find(final Class resultClass, final String jpqlQuery, final Map parameters) throws NonUniqueResultException { final TypedQuery typedQuery = createTypedQuery(jpqlQuery, resultClass); try { return find(resultClass, typedQuery, parameters); } catch (final PersistenceException e) { throw new SRetryableException(e); } } @Override public List findList(final Class resultClass, final String jpqlQuery, final Map parameters, final int startIndex, final int maxResults) { final TypedQuery typedQuery = createTypedQuery(jpqlQuery, resultClass); try { return findList(typedQuery, parameters, startIndex, maxResults); } catch (final QueryException e) { throw new IllegalArgumentException(e); } catch (final PersistenceException e) { throw new SRetryableException(e); } } @Override public T findByNamedQuery(final String queryName, final Class resultClass, final Map parameters) throws NonUniqueResultException { final EntityManager em = getEntityManager(); try { final TypedQuery query = em.createNamedQuery(queryName, resultClass); return find(resultClass, query, parameters); } catch (final PersistenceException e) { log.debug("BDM findByNamedQuery('{}', {}) failed", queryName, resultClass.getSimpleName(), e); //wrap in retryable exception because the issue might come from BDR reloading throw new SRetryableException(e); } } @Override public List findListByNamedQuery(final String queryName, final Class resultClass, final Map parameters, final int startIndex, final int maxResults) { final EntityManager em = getEntityManager(); try { final TypedQuery query = em.createNamedQuery(queryName, resultClass); return findList(query, parameters, startIndex, maxResults); } catch (final PersistenceException e) { log.debug("BDM findListByNamedQuery('{}', {}) failed", queryName, resultClass.getSimpleName(), e); //wrap in retryable exception because the issue might come from BDR reloading throw new SRetryableException(e); } } private TypedQuery createTypedQuery(final String jpqlQuery, final Class resultClass) { return getEntityManager().createQuery(jpqlQuery, resultClass); } protected List findList(final TypedQuery query, final Map parameters, final int startIndex, final int maxResults) { if (query == null) { throw new IllegalArgumentException("query is null"); } if (maxResults > 0) { if (parameters != null) { for (final Entry parameter : parameters.entrySet()) { query.setParameter(parameter.getKey(), checkParameterValue(parameter.getValue())); } } query.setFirstResult(startIndex); query.setMaxResults(maxResults); return query.getResultList(); } return Collections.emptyList(); } /** * Loads the root entity with an empty {@code EntityGraph} ({@code fetchgraph} hint) to * override {@code FetchType.EAGER} associations, then calls {@code em.remove()}. *

      * The effect on memory and query shape depends on the relationship type: *

        *
      • AGGREGATION (no cascade REMOVE — e.g. Invoice → Customer): the referenced * entity is never loaded, since cascade delete does not need it. This is the main * win — without the fetchgraph, EAGER would force loading entities just to discard them.
      • *
      • COMPOSITION (cascade REMOVE — e.g. Invoice → InvoiceLine): children are still * loaded into memory because cascade delete requires the collection contents. The benefit * is that they are loaded via a separate SELECT per collection instead of a single wide * JOIN, avoiding cartesian-product row explosion when multiple or deep compositions exist.
      • *
      *

      * The returned entity is used by {@link BusinessDataRepositoryEventAspect} to fire the * {@code BUSINESS_DATA_DELETED} event. */ @Override public Entity removeById(Class entityClass, long persistenceId) throws SBusinessDataNotFoundException { log.trace("Removing entity of type {} with id {} using EntityGraph", entityClass.getName(), persistenceId); final EntityManager em = getEntityManager(); try { // Empty fetchgraph overrides EAGER to LAZY for all associations. Aggregations are // then skipped entirely; compositions are still fetched at cascade time, but via // per-collection SELECTs rather than a single wide JOIN. EntityGraph minimalGraph = em.createEntityGraph(entityClass); Entity entity = em.find(entityClass, persistenceId, Map.of("javax.persistence.fetchgraph", minimalGraph)); if (entity == null) { throw new SBusinessDataNotFoundException( "Impossible to get data of type " + entityClass.getName() + " with id: " + persistenceId); } em.remove(entity); // Delete the tracking record linked to the removed BDM entity in the Bonita DB deleteTrackingRecord(persistenceId, entityClass.getName()); return entity; } catch (final PersistenceException e) { throw new SRetryableException( "Failed to remove entity " + entityClass.getName() + " with id " + persistenceId, e); } } @Override public void remove(final Entity entity) { if (entity != null && entity.getPersistenceId() != null) { log.trace("Removing entity of type {} with id {}", entity.getClass().getName(), entity.getPersistenceId()); final EntityManager em = getEntityManager(); try { em.remove(entity); // Delete the tracking record linked to the removed BDM entity in the Bonita DB deleteTrackingRecord(entity.getPersistenceId(), entity.getClass().getName()); } catch (final PersistenceException e) { throw new SRetryableException(e); } } else { log.trace("Entity is null or has null id, nothing to remove"); } } /** * Deletes the data retention tracking record associated with a removed BDM entity. *

      * Unlike {@link #trackCreation} and {@link #trackUpdate}, failures are logged * but do not roll back the transaction — the BDM entity removal takes priority. * Any orphan tracking record will be cleaned up later by the data retention job. */ private void deleteTrackingRecord(long entityId, String entityClassname) { log.debug("Deleting data retention tracking record for removed BDM entity {}#{}", entityClassname, entityId); try { dataRetentionBdmTrackingService.delete(entityId, entityClassname); } catch (Exception e) { // Ignore exceptions because the BDM entity is already removed, and we don't want to roll back that // removal if tracking deletion fails. The orphan tracking record will be cleaned up later by the data // retention cleanup job. log.warn("Failed to delete data retention tracking record for {}#{}", entityClassname, entityId, e); } } @Override public void persist(final Entity entity) { if (entity == null) { log.trace("Entity is null, nothing to persist"); return; } log.trace("Persisting entity of type {} with id {}", entity.getClass().getName(), entity.getPersistenceId()); try { // Capture whether the entity is new before JPA assigns an ID var isNew = entity.getPersistenceId() == null; getEntityManager().persist(entity); // Insert or update a data retention tracking record in the Bonita DB if (isNew) { trackCreation(entity.getPersistenceId(), entity.getClass().getName()); } else { trackUpdate(entity.getPersistenceId(), entity.getClass().getName()); } } catch (final PersistenceException e) { throw new SRetryableException(e); } } @Override public Entity merge(final Entity entity) { if (entity == null) { log.trace("Entity is null, nothing to merge"); return null; } log.trace("Merging entity of type {} with id {}", entity.getClass().getName(), entity.getPersistenceId()); try { // Capture whether the entity is new before JPA assigns an ID var isNew = entity.getPersistenceId() == null; // Capture the real class name before merge, because merge() may return a Hibernate proxy // whose getClass().getName() would be e.g. "Invoice$HibernateProxyXxx" instead of "Invoice" var entityClassname = entity.getClass().getName(); Entity merged = getEntityManager().merge(entity); // Insert or update a data retention tracking record in the Bonita DB, // use the merged entity which has the JPA-assigned persistenceId if (isNew) { trackCreation(merged.getPersistenceId(), entityClassname); } else { trackUpdate(merged.getPersistenceId(), entityClassname); } return merged; } catch (final PersistenceException e) { throw new SRetryableException(e); } } /** * Inserts a tracking record in the Bonita DB for a newly created BDM entity. * Called within the same JTA transaction as the BDM persist/merge, so both * are rolled back together if either fails. */ private void trackCreation(long entityId, String entityClassname) { log.debug("Tracking creation of new BDM entity {}#{}", entityClassname, entityId); try { dataRetentionBdmTrackingService.create(entityId, entityClassname); } catch (SDataRetentionBdmTrackingException e) { // Intentionally throwing an unchecked exception to roll back the entire JTA transaction, // including the BDM entity creation. Tracking and BDM data must stay consistent. throw new SBonitaRuntimeException("Failed to insert data retention tracking record for " + entityClassname + "#" + entityId, e); } } /** * Updates the {@code lastModifiedAt} timestamp of an existing tracking record * in the Bonita DB. If no record is found (e.g. the entity was created before * the tracking feature was deployed), a new one is created instead. */ private void trackUpdate(long entityId, String entityClassname) { log.debug("Tracking update of existing BDM entity {}#{}", entityClassname, entityId); try { dataRetentionBdmTrackingService.upsert(entityId, entityClassname); } catch (SDataRetentionBdmTrackingException e) { // Intentionally throwing an unchecked exception to roll back the entire JTA transaction, // including the BDM entity update. Tracking and BDM data must stay consistent. throw new SBonitaRuntimeException("Failed to update data retention tracking record for " + entityClassname + "#" + entityId, e); } } @Override public Entity unwrap(final Entity wrapped) { Entity entity = wrapped; if (entity instanceof HibernateProxy) { Hibernate.initialize(entity); entity = (Entity) ((HibernateProxy) entity).getHibernateLazyInitializer().getImplementation(); } return entity; } @Override public void onUpdate(ClassLoader newClassLoader) { clearProxyFactoryCache(); recreateEntityManagerFactoryOnClassLoaderChange(newClassLoader); } private void clearProxyFactoryCache() { log.debug("Clearing BDM proxy cache"); try { new ProxyCacheManager().clearCache(); log.debug("BDM proxy cache cleared"); } catch (NoSuchFieldException | IllegalAccessException e) { throw new SRetryableException(e); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/JsonBusinessDataSerializerImpl.java ================================================ /** * 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.business.data.impl; import java.io.IOException; import java.util.List; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.module.SimpleModule; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.bdm.serialization.BusinessDataObjectMapper; import org.bonitasoft.engine.business.data.JsonBusinessDataSerializer; import org.bonitasoft.engine.business.data.SBusinessDataRepositorySerializationException; import org.bonitasoft.engine.business.data.impl.jackson.EntityBeanSerializerModifier; import org.bonitasoft.engine.business.data.impl.jackson.EntityJacksonAnnotationIntrospector; import org.bonitasoft.engine.business.data.impl.jackson.EntityMixin; import org.bonitasoft.engine.business.data.impl.jackson.utils.LinkUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; /** * Jackson-based serializer for Business Data Model (BDM) entities to JSON. *

      * This service is responsible for converting BDM entities and query results into JSON format * for REST API responses. It handles two JSON output shapes: *

        *
      • Standard shape: Single-entity queries return objects {@code {...}}, scalar queries return * {@code { "value": X }}
      • *
      • Legacy shape: All queries return arrays {@code [...]}
      • *
      *

      * The serializer configures Jackson to: *

        *
      • Use field-based introspection (avoiding proxy getters)
      • *
      • Generate links for entity relationships
      • *
      • Apply custom entity serialization logic via {@link EntityBeanSerializerModifier}
      • *
      *

      * This is a stateless service - the decision of which shape to use is made by the calling service * ({@link BusinessDataServiceImpl}) and passed as method parameters. * * @see JsonBusinessDataSerializer * @see BusinessDataServiceImpl for the orchestration layer that determines which shape to use */ @Component @ConditionalOnSingleCandidate(JsonBusinessDataSerializer.class) @Slf4j public class JsonBusinessDataSerializerImpl extends BusinessDataObjectMapper implements JsonBusinessDataSerializer { /** * Property name for the standard shape configuration. */ public static final String STANDARD_SHAPE_PROPERTY = "bonita.runtime.business-data.serialization.standard-shape.enabled"; @Value("${" + STANDARD_SHAPE_PROPERTY + ":true}") private boolean standardShapeEnabled; public JsonBusinessDataSerializerImpl() { SimpleModule module = new SimpleModule(); module.setSerializerModifier(new EntityBeanSerializerModifier()); objectMapper.registerModule(module); objectMapper.addMixIn(Entity.class, EntityMixin.class); objectMapper.setAnnotationIntrospector(new EntityJacksonAnnotationIntrospector()); // Ensure Jackson use only fields to get properties. Reasons: // - entity can be wrapped in a javassist or hibernate proxy that have additional getters // - generated getters may not exactly match the field name (getANumber for aNumber field) objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); } private ObjectWriter newObjectWriter(String uriPattern) { return LinkUtils.putUriPatternIntoContext(objectMapper.writer(), uriPattern); } @Override public boolean isStandardShapeEnabled() { return standardShapeEnabled; } @Override public String serializeEntity(final Entity entity, final String businessDataURIPattern) throws SBusinessDataRepositorySerializationException { try { if (log.isTraceEnabled()) { log.trace("Serializing entity"); } String json = newObjectWriter(businessDataURIPattern).writeValueAsString(entity); if (log.isTraceEnabled()) { log.trace("Entity serialization result: {}", json); } return json; } catch (IOException e) { throw new SBusinessDataRepositorySerializationException( "Unable to serialize Entity of type " + entity.getClass().getSimpleName(), e); } } @Override public String serializeEntities(final List entities, final String businessDataURIPattern) throws SBusinessDataRepositorySerializationException { try { if (log.isTraceEnabled()) { log.trace("Serializing a list of entities"); } String json = newObjectWriter(businessDataURIPattern).writeValueAsString(entities); if (log.isTraceEnabled()) { log.trace("List of entities serialization result: {}", json); } return json; } catch (IOException e) { throw new SBusinessDataRepositorySerializationException("Unable to serialize list of Entity", e); } } @Override @Deprecated public String serializeCountResult(List list, String entityClassName) { String json = "[" + list.get(0).toString() + "]"; if (log.isTraceEnabled()) { log.trace("Count serialization result: {}", json); } return json; } @Override public String serializeScalarResult(List list, String entityClassName, boolean useStandardShape) { Object value = (list == null || list.isEmpty()) ? null : list.get(0); String json; if (useStandardShape) { // Standard shape: { "value": 10 } json = "{ \"value\": " + (value == null ? "null" : value.toString()) + " }"; } else { // Legacy shape: [ 10 ] json = "[" + (value == null ? "null" : value.toString()) + "]"; } if (log.isTraceEnabled()) { log.trace("Scalar serialization result: {}", json); } return json; } @Override public String serializeEntityQueryResult(List entities, String businessDataURIPattern, boolean useStandardShape, boolean queryReturnsMultipleResults) throws SBusinessDataRepositorySerializationException { if (!useStandardShape || queryReturnsMultipleResults) { // Legacy shape (always array) OR query designed to return List: return array [{...}] return serializeEntities(entities, businessDataURIPattern); } // Standard shape with query designed to return single entity if (entities != null && entities.size() == 1) { // Return object {...} return serializeEntity(entities.get(0), businessDataURIPattern); } else if (entities == null || entities.isEmpty()) { // Return empty object {} return EMPTY_OBJECT; } else { // Multiple results from single-entity query (shouldn't happen normally): return array return serializeEntities(entities, businessDataURIPattern); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/ProxyCacheManager.java ================================================ /** * Copyright (C) 2016 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.business.data.impl; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.WeakHashMap; import javassist.util.proxy.ProxyFactory; public class ProxyCacheManager { static void setAccessible(final AccessibleObject ao, final boolean accessible) { if (System.getSecurityManager() == null) ao.setAccessible(accessible); else { AccessController.doPrivileged(new PrivilegedAction() { @Override public Object run() { ao.setAccessible(accessible); return null; } }); } } static Field getDeclaredField(final Class clazz, final String name) throws NoSuchFieldException { if (System.getSecurityManager() == null) return clazz.getDeclaredField(name); else { try { return (Field) AccessController .doPrivileged(new PrivilegedExceptionAction() { @Override public Object run() throws Exception { return clazz.getDeclaredField(name); } }); } catch (final PrivilegedActionException e) { if (e.getCause() instanceof NoSuchFieldException) throw (NoSuchFieldException) e.getCause(); throw new RuntimeException(e.getCause()); } } } static Object get(final Field fld, final Object target) throws IllegalAccessException { if (System.getSecurityManager() == null) return fld.get(target); else { try { return AccessController.doPrivileged(new PrivilegedExceptionAction() { @Override public Object run() throws Exception { return fld.get(target); } }); } catch (final PrivilegedActionException e) { if (e.getCause() instanceof NoSuchMethodException) throw (IllegalAccessException) e.getCause(); throw new RuntimeException(e.getCause()); } } } public WeakHashMap/* > */ get() throws NoSuchFieldException, IllegalAccessException { final Field proxyCacheField = getDeclaredField(ProxyFactory.class, "proxyCache"); setAccessible(proxyCacheField, true); return (WeakHashMap) get(proxyCacheField, null); } public void clearCache() throws NoSuchFieldException, IllegalAccessException { final WeakHashMap cache = get(); if (cache != null) { cache.clear(); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/RemoveEntityManagerSynchronization.java ================================================ /** * 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.business.data.impl; import javax.persistence.EntityManager; import javax.transaction.Status; import org.bonitasoft.engine.transaction.BonitaTransactionSynchronization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Matthieu Chaffotte */ public class RemoveEntityManagerSynchronization implements BonitaTransactionSynchronization { private static final Logger log = LoggerFactory.getLogger(RemoveEntityManagerSynchronization.class); private final ThreadLocal localManager; public RemoveEntityManagerSynchronization(final ThreadLocal localManager) { super(); this.localManager = localManager; } @Override public void afterCompletion(final int txState) { if (txState == Status.STATUS_UNKNOWN) { log.error("BDM EntityManager cleanup after transaction completed with STATUS_UNKNOWN " + "(heuristic mixed outcome). Some XA resources may have committed while others rolled back. " + "The persistence context is in an undefined state and will be discarded. " + "Manual verification of business data consistency may be required."); } try { EntityManager entityManager = localManager.get(); // Ensure the EntityManager is not already closed before attempting to close it (EntityManager.close() throws // an exception if the EntityManager is already closed) if (entityManager != null && entityManager.isOpen()) { try { entityManager.close(); } catch (Exception e) { log.warn("Failed to close BDM EntityManager during transaction completion (txState={}). " + "This may indicate a resource leak. The EntityManager reference will still be removed " + "from the thread to prevent stale state on the next operation.", txState, e); } } } finally { localManager.remove(); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/SchemaManagerReadOnly.java ================================================ /** * 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.business.data.impl; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.bonitasoft.engine.business.data.SchemaManager; import org.hibernate.HibernateException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Pablo Alonso de Linaje García */ public class SchemaManagerReadOnly implements SchemaManager { private static final Logger log = LoggerFactory.getLogger(SchemaManagerReadOnly.class); public SchemaManagerReadOnly() throws HibernateException { log.warn("Ready-Only Schema manager. No change will be performed on the BDM DB Schema." + " Please ensure that this update is done before using the new BDM"); } @Override public List drop(Set managedClasses) { log.warn( "No drop of BDM DB Schema will be performed. Please ensure that this update is done before using the new BDM"); return new ArrayList<>(); } @Override public List update(Set managedClasses) { log.warn( "No update of BDM DB Schema will be performed. Please ensure that this update is done before using the new BDM"); return new ArrayList<>(); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/SchemaManagerUpdate.java ================================================ /** * 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.business.data.impl; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.business.data.SchemaManager; import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.boot.Metadata; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.tool.hbm2ddl.SchemaExport; import org.hibernate.tool.hbm2ddl.SchemaUpdate; import org.hibernate.tool.schema.TargetType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Matthieu Chaffotte */ public class SchemaManagerUpdate implements SchemaManager { private static final Logger log = LoggerFactory.getLogger(SchemaManagerUpdate.class); private final Map configuration; public SchemaManagerUpdate(final Map configuration) throws HibernateException { this.configuration = new HashMap<>(configuration); final Object remove = this.configuration.remove("hibernate.hbm2ddl.auto"); if (remove != null && log.isInfoEnabled()) { log.info("'hibernate.hbm2ddl.auto' is not a valid property so it has been ignored"); } } private Metadata buildConfiguration(final Set managedClasses) { MetadataSources metadata = new MetadataSources( new StandardServiceRegistryBuilder() .applySettings(configuration) .build()); for (final String entity : managedClasses) { metadata.addAnnotatedClass(getMappedClass(entity)); } return metadata.buildMetadata(); } public Class getMappedClass(final String className) throws MappingException { if (className == null) { return null; } try { return ReflectHelper.classForName(className); } catch (final ClassNotFoundException notFound) { throw new MappingException("entity class not found: " + className, notFound); } } @Override @SuppressWarnings("unchecked") public List drop(final Set managedClasses) { log.info("Dropping classes: {}", managedClasses); SchemaExport schemaExport = new SchemaExport(); schemaExport.drop(EnumSet.of(TargetType.DATABASE), buildConfiguration(managedClasses)); log.info("Drop operation done"); return schemaExport.getExceptions(); } @Override @SuppressWarnings("unchecked") public List update(final Set managedClasses) { log.info("Updating classes: {}", managedClasses); final SchemaUpdate schemaUpdate = new SchemaUpdate(); schemaUpdate.execute(EnumSet.of(TargetType.DATABASE), buildConfiguration(managedClasses)); log.info("Update operation done"); return schemaUpdate.getExceptions(); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/EntityBeanSerializerModifier.java ================================================ /** * 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.business.data.impl.jackson; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.Proxy; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializationConfig; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; import org.apache.commons.lang3.ClassUtils; import org.bonitasoft.engine.business.data.impl.jackson.utils.ExtraPropertyUtils; import org.bonitasoft.engine.business.data.impl.jackson.writer.ExtraBeanPropertyWriter; import org.bonitasoft.engine.business.data.impl.jackson.writer.IgnoredPropertyWriter; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class EntityBeanSerializerModifier extends BeanSerializerModifier { private static Logger LOG = LoggerFactory.getLogger(EntityBeanSerializerModifier.class); @Override public List changeProperties(SerializationConfig config, BeanDescription beanDesc, List beanProperties) { LOG.trace("Changing list of property writers for {}", beanDesc.getClassInfo()); List newProperties = new ArrayList<>(); if (shouldBeIgnored(beanDesc)) { LOG.trace("Ignoring all properties of this bean"); return newProperties; } for (BeanPropertyWriter beanPropertyWriter : beanProperties) { LOG.trace("{}", beanPropertyWriter); LOG.trace("Bean type {}", beanPropertyWriter.getType()); if (shouldBeReplacedByLink(beanPropertyWriter)) { LOG.trace("Has to be replaced by link"); BeanPropertyWriter ignoredPropertyWriter = new IgnoredPropertyWriter(beanPropertyWriter); LOG.trace("Adding only an ignored property writer {}", ignoredPropertyWriter); newProperties.add(ignoredPropertyWriter); } else { newProperties.add(beanPropertyWriter); if (ExtraPropertyUtils.shouldAddExtraProperty(beanPropertyWriter)) { LOG.trace("Will have an additional property"); BeanPropertyWriter additionalPropertyWriter = ExtraBeanPropertyWriter.newWriter(beanPropertyWriter); LOG.trace("Adding new property {}", additionalPropertyWriter); newProperties.add(additionalPropertyWriter); } } } return newProperties; } private static boolean shouldBeReplacedByLink(BeanPropertyWriter propertyWriter) { return propertyWriter != null && propertyWriter.getAnnotation(JsonIgnore.class) != null; } private static boolean shouldBeIgnored(BeanDescription beanDescription) { JavaType type = beanDescription.getType(); Class rawClass = type.getRawClass(); if (LOG.isTraceEnabled()) { LOG.trace("Checking if it has to be ignored - {} / {}", type, rawClass); LOG.trace("Interfaces: {}", getNames(ClassUtils.getAllInterfaces(rawClass))); LOG.trace("Superclasses: {}", getNames(ClassUtils.getAllSuperclasses(rawClass))); } return MethodHandler.class.isAssignableFrom(rawClass) || Proxy.class.isAssignableFrom(rawClass); } @Override public JsonSerializer modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer serializer) { if (HibernateProxy.class.isAssignableFrom(beanDesc.getBeanClass())) { LOG.trace("Registering HibernateProxy unwrapping serializer for {}", beanDesc.getBeanClass().getName()); return new HibernateProxyUnwrappingSerializer(serializer); } return serializer; } private static List getNames(List> classes) { return classes.stream().map(Class::getName).collect(Collectors.toList()); } private static class HibernateProxyUnwrappingSerializer extends JsonSerializer { private final JsonSerializer defaultSerializer; @SuppressWarnings("unchecked") HibernateProxyUnwrappingSerializer(JsonSerializer defaultSerializer) { this.defaultSerializer = (JsonSerializer) defaultSerializer; } @Override public void serialize(HibernateProxy value, JsonGenerator gen, SerializerProvider serializers) throws IOException { if (value == null) { gen.writeNull(); return; } LazyInitializer lazyInitializer = value.getHibernateLazyInitializer(); if (!lazyInitializer.isUninitialized()) { Object implementation = lazyInitializer.getImplementation(); if (implementation != null) { LOG.debug("Unwrapping initialized HibernateProxy for entity {} (id={}) to actual type {}", lazyInitializer.getEntityName(), lazyInitializer.getIdentifier(), implementation.getClass().getName()); serializers.defaultSerializeValue(implementation, gen); } else { LOG.debug( "HibernateProxy for entity {} (id={}) getImplementation() returned null — serializing proxy as-is", lazyInitializer.getEntityName(), lazyInitializer.getIdentifier()); defaultSerializer.serialize(value, gen, serializers); } } else { LOG.debug( "Serializing uninitialized HibernateProxy for entity {} (id={}) without unwrapping — JSON may be empty", lazyInitializer.getEntityName(), lazyInitializer.getIdentifier()); defaultSerializer.serialize(value, gen, serializers); } } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/EntityJacksonAnnotationIntrospector.java ================================================ /** * 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.business.data.impl.jackson; import com.fasterxml.jackson.databind.ext.Java7Support; import com.fasterxml.jackson.databind.introspect.Annotated; import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; /** * Do not ignore property annotated with JsonIgnore as we manage this with a links property instead. */ public class EntityJacksonAnnotationIntrospector extends JacksonAnnotationIntrospector { // taken from super class, except that we can directly set the field as we are running in a Java 7+ environment // NOTE: loading of Java7 dependencies is encapsulated by handlers in Java7Support, // here we do not really need any handling; but for extra-safety use try-catch private static final Java7Support _java7Helper = Java7Support.instance(); @Override protected boolean _isIgnorable(Annotated a) { Boolean b = _java7Helper.findTransient(a); if (b != null) { return b.booleanValue(); } return false; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/EntityMixin.java ================================================ /** * 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.business.data.impl.jackson; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.annotation.JsonAppend; import org.bonitasoft.engine.business.data.impl.jackson.writer.LinkPropertyWriter; @JsonAppend(props = { @JsonAppend.Prop(value = LinkPropertyWriter.class, name = EntityMixin.PROPERTY_LINK, include = JsonInclude.Include.NON_EMPTY) }) public abstract class EntityMixin { public static final String PROPERTY_LINK = "links"; } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/serializer/ExtraPropertyStringAbstractSerializer.java ================================================ /** * 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.business.data.impl.jackson.serializer; import java.io.IOException; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; public abstract class ExtraPropertyStringAbstractSerializer extends JsonSerializer { public ExtraPropertyStringAbstractSerializer() { // needed by Jackson } @Override public void serialize(T value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeObject(convert(value)); } protected abstract Object convert(T value); } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/serializer/ExtraPropertyStringListSerializer.java ================================================ /** * 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.business.data.impl.jackson.serializer; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; public class ExtraPropertyStringListSerializer extends ExtraPropertyStringAbstractSerializer { @Override protected Object convert(List value) { return Optional.ofNullable(value).orElseGet(Collections::emptyList) .stream() .map(o -> Objects.toString(o, null)) .collect(Collectors.toList()); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/serializer/ExtraPropertyStringSerializer.java ================================================ /** * 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.business.data.impl.jackson.serializer; import java.util.Objects; public class ExtraPropertyStringSerializer extends ExtraPropertyStringAbstractSerializer { @Override protected Object convert(Object value) { return Objects.toString(value, null); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/utils/ExtraPropertyUtils.java ================================================ /** * 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.business.data.impl.jackson.utils; import java.util.HashSet; import java.util.Set; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; import com.fasterxml.jackson.databind.type.CollectionType; public class ExtraPropertyUtils { private static final String STRING_SUFFIX = "_string"; private static final Set numberTypes; static { numberTypes = new HashSet<>(); numberTypes.add(Long.class.getCanonicalName()); numberTypes.add(Float.class.getCanonicalName()); numberTypes.add(Double.class.getCanonicalName()); } public static String getExtraPropertyName(BeanPropertyWriter propertyWriter) { return propertyWriter.getName().concat(STRING_SUFFIX); } public static boolean shouldAddExtraProperty(BeanPropertyWriter writer) { return shouldAddExtraProperty(writer.getType()); } private static boolean shouldAddExtraProperty(JavaType javaType) { if (javaType.getClass().isAssignableFrom(CollectionType.class)) { CollectionType collectionType = (CollectionType) javaType; return shouldAddExtraProperty(collectionType.getContentType()); } return numberTypes.contains(javaType.getRawClass().getCanonicalName()); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/utils/Link.java ================================================ /** * Copyright (C) 2017 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.business.data.impl.jackson.utils; import java.util.Objects; /** * @author Matthieu Chaffotte */ public class Link { private final String rel; private final String href; public Link(final String rel, final String href) { super(); this.rel = rel; this.href = href; } public String getRel() { return rel; } public String getHref() { return href; } @Override public String toString() { return "Link{" + "rel='" + rel + '\'' + ", href='" + href + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Link link)) return false; return Objects.equals(rel, link.rel) && Objects.equals(href, link.href); } @Override public int hashCode() { return Objects.hash(rel, href); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/utils/LinkUtils.java ================================================ /** * 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.business.data.impl.jackson.utils; import java.util.LinkedHashSet; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.SerializerProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LinkUtils { private static Logger LOG = LoggerFactory.getLogger(LinkUtils.class); private static final String ATTRIBUTE_KEY_LINK_BASE_NAME = "IgnoredToLinkSerializer$$ATTRIBUTE_KEY_LINK"; private static final String ATTRIBUTE_KEY_URI_PATTERN = "IgnoredToLinkSerializer$$PATTERN_URI"; public static LinkedHashSet getLinksFromContext(Object value, SerializerProvider prov) { LOG.trace("Retrieving links from context"); String attributeKeyLink = getAttributeKeyLink(value); LinkedHashSet links = (LinkedHashSet) prov.getAttribute(attributeKeyLink); if (links == null) { LOG.trace("No found links, initialize them in the context"); links = new LinkedHashSet<>(); prov.setAttribute(attributeKeyLink, links); } LOG.trace("Links: {}", links); return links; } public static void addLinkToContext(Object value, Link link, SerializerProvider prov) { LinkedHashSet links = getLinksFromContext(value, prov); links.add(link); LOG.trace("Added to context: {}", link); } private static String getAttributeKeyLink(Object value) { return ATTRIBUTE_KEY_LINK_BASE_NAME + "$$" + value.getClass().getCanonicalName() + "@" + value.hashCode(); } public static String getUriPatternFromContext(SerializerProvider prov) { return (String) prov.getAttribute(ATTRIBUTE_KEY_URI_PATTERN); } public static ObjectWriter putUriPatternIntoContext(ObjectWriter writer, String uriPattern) { return writer.withAttribute(ATTRIBUTE_KEY_URI_PATTERN, uriPattern); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/writer/ExtraBeanPropertyWriter.java ================================================ /** * 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.business.data.impl.jackson.writer; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.PropertyName; import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; import com.fasterxml.jackson.databind.type.CollectionType; import org.bonitasoft.engine.business.data.impl.jackson.serializer.ExtraPropertyStringListSerializer; import org.bonitasoft.engine.business.data.impl.jackson.serializer.ExtraPropertyStringSerializer; import org.bonitasoft.engine.business.data.impl.jackson.utils.ExtraPropertyUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ExtraBeanPropertyWriter extends BeanPropertyWriter { private static Logger LOG = LoggerFactory.getLogger(ExtraBeanPropertyWriter.class); private ExtraBeanPropertyWriter(BeanPropertyWriter base) { super(base, new PropertyName(ExtraPropertyUtils.getExtraPropertyName(base))); } public static ExtraBeanPropertyWriter newWriter(BeanPropertyWriter base) { LOG.trace("Creating new instance"); JavaType initialJavaType = base.getType(); LOG.trace("Initial java type: {}", initialJavaType); final JsonSerializer serializer; if (initialJavaType.getClass().isAssignableFrom(CollectionType.class)) { serializer = new ExtraPropertyStringListSerializer(); } else { serializer = new ExtraPropertyStringSerializer(); } ExtraBeanPropertyWriter writer = new ExtraBeanPropertyWriter(base); writer.assignSerializer(serializer); return writer; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/writer/IgnoredPropertyWriter.java ================================================ /** * 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.business.data.impl.jackson.writer; import java.util.Objects; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonStreamContext; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.business.data.impl.jackson.utils.Link; import org.bonitasoft.engine.business.data.impl.jackson.utils.LinkUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This implementation does not serialize fields by calling the JsonGenerator. Instead, it creates and stores link * instances in the Jackson per-call context.
      * The actual serialization is delegated to the {@link LinkPropertyWriter} class. */ public class IgnoredPropertyWriter extends BeanPropertyWriter { private static Logger LOG = LoggerFactory.getLogger(IgnoredPropertyWriter.class); private final String ignoredFieldName; public IgnoredPropertyWriter(BeanPropertyWriter base) { super(base); this.ignoredFieldName = base.getName(); } @Override public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception { LOG.trace("Managing ignored field {} with value {}", ignoredFieldName, bean); Object currentlyProcessedObject = getCurrentObjectFromContext(gen); if (currentlyProcessedObject instanceof Entity) { LOG.trace("Parent is an Entity, so managing links"); String uriPattern = LinkUtils.getUriPatternFromContext(prov); LOG.trace("URI Pattern retrieved from the context: {}", uriPattern); Link link = new Link(ignoredFieldName, buildURI((Entity) currentlyProcessedObject, uriPattern, ignoredFieldName)); LinkUtils.addLinkToContext(currentlyProcessedObject, link, prov); } } private static Object getCurrentObjectFromContext(JsonGenerator gen) { JsonStreamContext outputContext = gen.getOutputContext(); Object currentObj = outputContext.getCurrentValue(); LOG.trace("Current Object from context: {}", currentObj); return currentObj; } private static String buildURI(final Entity parent, final String uriPattern, final String fieldName) { String uri = uriPattern.replace("{className}", parent.getClass().getName()); uri = uri.replace("{id}", Objects.toString(parent.getPersistenceId(), null)); return uri.replace("{field}", fieldName); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/impl/jackson/writer/LinkPropertyWriter.java ================================================ /** * 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.business.data.impl.jackson.writer; import java.util.LinkedHashSet; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.cfg.MapperConfig; import com.fasterxml.jackson.databind.introspect.AnnotatedClass; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; import com.fasterxml.jackson.databind.ser.VirtualBeanPropertyWriter; import com.fasterxml.jackson.databind.util.Annotations; import org.bonitasoft.engine.business.data.impl.jackson.utils.Link; import org.bonitasoft.engine.business.data.impl.jackson.utils.LinkUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LinkPropertyWriter extends VirtualBeanPropertyWriter { private static Logger LOG = LoggerFactory.getLogger(LinkPropertyWriter.class); // Needed by Jackson protected LinkPropertyWriter() { super(); } private LinkPropertyWriter(BeanPropertyDefinition propDef, Annotations ctxtAnn, JavaType type) { super(propDef, ctxtAnn, type); } @Override protected Object value(Object bean, JsonGenerator jgen, SerializerProvider prov) { LOG.trace("Post processing links for {}", bean); // valid cast we only apply this writer to Entity bean LinkedHashSet links = LinkUtils.getLinksFromContext(bean, prov); LOG.trace("Retrieved links: {}", links); return links; } @Override public VirtualBeanPropertyWriter withConfig(MapperConfig config, AnnotatedClass declaringClass, BeanPropertyDefinition propDef, JavaType type) { return new LinkPropertyWriter(propDef, declaringClass.getAnnotations(), type); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/model/SDataRetentionBdmTracking.java ================================================ /** * Copyright (C) 2026 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.business.data.model; import javax.persistence.Cacheable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * Persistent entity that tracks the creation and last modification timestamps * of BDM object instances for data retention purposes. *

      Each row records when a specific BDM object instance (identified by its * {@code dataId} and {@code dataClassname}) was created and last modified. * These timestamps are used by the data retention service to determine whether * a BDM object instance has exceeded its configured retention period. *

      Mapped to the {@code data_retention_bdm_tracking} table in the Bonita engine schema. */ @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "data_retention_bdm_tracking") // Disabling cache because this entity is not expected to be accessed // frequently (read by the data retention service executed by a CRON job) @Cacheable(false) public class SDataRetentionBdmTracking implements PersistentObject { @Id private long id; /** * The persistence ID of the tracked BDM object instance. */ @Column(name = "data_id", nullable = false) private long dataId; /** * Fully qualified Java class name of the BDM object type. *

      Example: {@code com.company.model.ContratClient} */ @Column(name = "data_classname", nullable = false) private String dataClassname; /** * Epoch timestamp (milliseconds) when the BDM object instance was first created. */ @Column(name = "created_at", nullable = false) private long createdAt; /** * Epoch timestamp (milliseconds) when the BDM object instance was last modified. */ @Column(name = "last_modified_at", nullable = false) private long lastModifiedAt; } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/model/SDataRetentionConfig.java ================================================ /** * Copyright (C) 2026 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.business.data.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * Persistent entity representing a data retention rule configured by a Platform Administrator * for a specific BDM object type. *

      Each instance of this class defines when and how BDM object instances of a given Java type * should be automatically deleted by the data retention service. One rule exists * per BDM object type — the {@code dataClassname} column carries a unique constraint. *

      A rule is considered active as soon as it is persisted. There is no enable/disable toggle: * to stop retention processing for an object type, the rule must be deleted entirely. *

      Mapped to the {@code data_retention_config} table in the Bonita engine schema. * This table is never present in the BDM schema. */ @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "data_retention_config") public class SDataRetentionConfig implements PersistentObject { @Id private long id; /** * Fully qualified Java class name of the BDM object type this rule applies to. *

      Example: {@code com.company.model.ContratClient} *

      This value is derived from the deployed BDM model and must match an existing * object type at rule creation time. If the BDM is redeployed and this class no longer * exists in the new model, the rule is automatically deleted. *

      Unique — only one retention rule may exist per BDM object type. */ @Column(name = "data_classname", nullable = false) private String dataClassname; /** * The date field used as the starting point for the retention period calculation. *

      Possible values: *

        *
      • {@code CREATION} — retention is calculated from the date the BDM object * instance was first created, as recorded in {@code data_retention_bdm_tracking.created_at}. * The clock never resets, regardless of subsequent modifications.
      • *
      • {@code LAST_UPDATE} — retention is calculated from the date the BDM object * instance was last modified, as recorded in {@code data_retention_bdm_tracking.last_modified_at}. * The clock resets to zero on every modification.
      • *
      * * @see SReferenceDate */ @Enumerated(EnumType.STRING) @Column(name = "reference_date", nullable = false) private SReferenceDate referenceDate; /** * Duration of the retention period, expressed in days. * Must be a positive integer. */ @Column(name = "retention_days", nullable = false) private int retentionDays; @Column(name = "created_at", nullable = false) private long createdAt; @Column(name = "updated_at", nullable = false) private long updatedAt; } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/java/org/bonitasoft/engine/business/data/model/SReferenceDate.java ================================================ /** * Copyright (C) 2026 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.business.data.model; /** * Defines which date field on a BDM object instance is used as the starting point * for the data retention period calculation. * * @see SDataRetentionConfig#getReferenceDate() */ public enum SReferenceDate { /** * Retention is calculated from the date the BDM object instance was first created. * The clock never resets, regardless of subsequent modifications. */ CREATION, /** * Retention is calculated from the date the BDM object instance was last modified. * The clock resets to zero on every modification. */ LAST_UPDATE } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/resources/README.md ================================================ # How to use This zip contains required files to interact with your deployed business object in your application. To do that: * Create a new maven project using `example-pom.xml` * Extract the two jars `bdm-dao.jar` and `bdm-model.jar` from the zip file, next to your `pom.xml` * Make sure the engine is accessible using [the HTTP API](https://documentation.ofelia.com/bonita/latest/configure-client-of-bonita-bpm-engine#_server_configuration_to_accept_http_connection). Here is an example on how to retrieve the DAO to find your objects ```java // configure APIClient to connect via HTTP HashMap params = new HashMap<>(); params.put("server.url", "http://localhost:8080/"); params.put("application.name", "bonita"); APITypeManager.setAPITypeAndParams(ApiAccessType.HTTP, params); // connect to the engine APIClient apiClient = new APIClient(); apiClient.login("walter.bates","bpm"); // retrieve an instance of the DAO MyBusinessObjectDAO dao = apiClient.getDAO(MyBusinessObjectDAO.class); // use the DAO List myBusinessObjects = dao.find(0, 100); apiClient.logout(); ``` ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/resources/example-pom.xml ================================================ 4.0.0 com.mycompagny my-application 1.0.0 jar PUT_BONITA_VERSION_HERE com.mycompagny bdm-dao 1.0.0 system ${basedir}/bdm-dao.jar com.mycompagny bdm-model 1.0.0 system ${basedir}/bdm-model.jar org.bonitasoft.engine bonita-client ${bonita.version} org.javassist javassist 3.18.1-GA ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/main/resources/org/bonitasoft/engine/business/data/model/impl/hibernate/data-retention-bdm-tracking.queries.hbm.xml ================================================ SELECT t FROM org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking AS t WHERE t.dataId = :dataId AND t.dataClassname = :dataClassname SELECT t FROM org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking AS t WHERE t.dataClassname = :dataClassname SELECT t FROM org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking AS t WHERE t.dataClassname = :dataClassname AND t.createdAt <= :deadline SELECT t FROM org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking AS t WHERE t.dataClassname = :dataClassname AND t.lastModifiedAt <= :deadline DELETE FROM org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking AS t WHERE t.dataId = :dataId AND t.dataClassname = :dataClassname ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/model/Address.java ================================================ /** * Copyright (C) 2017 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 com.company.model; import java.util.List; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Version; import com.fasterxml.jackson.annotation.JsonIgnore; import org.bonitasoft.engine.bdm.Entity; public class Address implements Entity { @Id @GeneratedValue private Long persistenceId; @Version private Long persistenceVersion; private String street; private Float number; private List floors; @JsonIgnore private String doorCode; public void setPersistenceId(Long persistenceId) { this.persistenceId = persistenceId; } @Override public Long getPersistenceId() { return persistenceId; } @Override public Long getPersistenceVersion() { return persistenceVersion; } public void setPersistenceVersion(Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getDoorCode() { return doorCode; } public void setDoorCode(String doorCode) { this.doorCode = doorCode; } public Float getNumber() { return number; } public void setNumber(Float number) { this.number = number; } public List getFloors() { return floors; } public void setFloors(List floors) { this.floors = floors; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/model/Person.java ================================================ /** * Copyright (C) 2015 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 com.company.model; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OrderColumn; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Version; /** * */ @javax.persistence.Entity(name = "Person") @Table(name = "PERSON") @NamedQueries({ @NamedQuery(name = "Person.findByName", query = "SELECT p\nFROM Person p\nWHERE p.name= :name\nORDER BY p.persistenceId"), @NamedQuery(name = "Person.findByAge", query = "SELECT p\nFROM Person p\nWHERE p.age= :age\nORDER BY p.persistenceId"), @NamedQuery(name = "Person.findByBirthday", query = "SELECT p\nFROM Person p\nWHERE p.birthday= :birthday\nORDER BY p.persistenceId"), @NamedQuery(name = "Person.findByHasMobile", query = "SELECT p\nFROM Person p\nWHERE p.hasMobile= :hasMobile\nORDER BY p.persistenceId"), @NamedQuery(name = "Person.find", query = "SELECT p\nFROM Person p\nORDER BY p.persistenceId"), @NamedQuery(name = "Person.findById", query = "SELECT p \nFROM Person p \nWHERE p.persistenceId = :persistenceId"), @NamedQuery(name = "Person.findAdults", query = "SELECT p \nFROM Person p \nWHERE p.age > 18\nORDER BY p.persistenceId ASC"), @NamedQuery(name = "Person.query1", query = "SELECT p \nFROM Person p \nWHERE p.hasMobile = :hasMobile\nORDER BY p.persistenceId ASC"), @NamedQuery(name = "Person.findByRange", query = "SELECT p \nFROM Person p \nWHERE p.birthday >= :date1 AND p.birthday <= :date2\nORDER BY p.persistenceId ASC") }) public class Person implements org.bonitasoft.engine.bdm.Entity { @Id @GeneratedValue private Long persistenceId; @Version private Long persistenceVersion; @Column(name = "NAME", nullable = true, length = 255) private String name; @Column(name = "AGE", nullable = true) private Integer age; @OneToMany(orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name = "PERSON_PID", nullable = false) @OrderColumn private List phones = new ArrayList(10); @Column(name = "BIRTHDAY", nullable = true) @Temporal(TemporalType.TIMESTAMP) private Date birthday; @Column(name = "HASMOBILE", nullable = false) private Boolean hasMobile; @ElementCollection(fetch = FetchType.EAGER) @OrderColumn @Column(name = "BOOLS", nullable = true) private List bools = new ArrayList(10); public Person() { } public void setPersistenceId(Long persistenceId) { this.persistenceId = persistenceId; } public Long getPersistenceId() { return persistenceId; } public void setPersistenceVersion(Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } public Long getPersistenceVersion() { return persistenceVersion; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setAge(Integer age) { this.age = age; } public Integer getAge() { return age; } public void setPhones(List phones) { if (this.phones == null) { this.phones = phones; } else { this.phones.clear(); this.phones.addAll(phones); } } public List getPhones() { return phones; } public void addToPhones(com.company.model.Phone addTo) { List phones = getPhones(); phones.add(addTo); } public void removeFromPhones(com.company.model.Phone removeFrom) { List phones = getPhones(); phones.remove(removeFrom); } public void setBirthday(Date birthday) { this.birthday = birthday; } public Date getBirthday() { return birthday; } public void setHasMobile(Boolean hasMobile) { this.hasMobile = hasMobile; } public Boolean isHasMobile() { return hasMobile; } public void setBools(List bools) { if (this.bools == null) { this.bools = bools; } else { this.bools.clear(); this.bools.addAll(bools); } } public List getBools() { return bools; } public void addToBools(Boolean addTo) { List bools = getBools(); bools.add(addTo); } public void removeFromBools(Boolean removeFrom) { List bools = getBools(); bools.remove(removeFrom); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Person other = ((Person) obj); if (persistenceId == null) { if (other.persistenceId != null) { return false; } } else { if (!persistenceId.equals(other.persistenceId)) { return false; } } if (persistenceVersion == null) { if (other.persistenceVersion != null) { return false; } } else { if (!persistenceVersion.equals(other.persistenceVersion)) { return false; } } if (name == null) { if (other.name != null) { return false; } } else { if (!name.equals(other.name)) { return false; } } if (age == null) { if (other.age != null) { return false; } } else { if (!age.equals(other.age)) { return false; } } if (phones == null) { if (other.phones != null) { return false; } } else { if (!phones.equals(other.phones)) { return false; } } if (birthday == null) { if (other.birthday != null) { return false; } } else { if (!birthday.equals(other.birthday)) { return false; } } if (hasMobile == null) { if (other.hasMobile != null) { return false; } } else { if (!hasMobile.equals(other.hasMobile)) { return false; } } if (bools == null) { if (other.bools != null) { return false; } } else { if (!bools.equals(other.bools)) { return false; } } return true; } @Override public int hashCode() { final int prime = 31; int result = 1; int persistenceIdCode = 0; if (persistenceId != null) { persistenceIdCode = persistenceId.hashCode(); } result = ((prime * result) + persistenceIdCode); int persistenceVersionCode = 0; if (persistenceVersion != null) { persistenceVersionCode = persistenceVersion.hashCode(); } result = ((prime * result) + persistenceVersionCode); int nameCode = 0; if (name != null) { nameCode = name.hashCode(); } result = ((prime * result) + nameCode); int ageCode = 0; if (age != null) { ageCode = age.hashCode(); } result = ((prime * result) + ageCode); int phonesCode = 0; if (phones != null) { phonesCode = phones.hashCode(); } result = ((prime * result) + phonesCode); int birthdayCode = 0; if (birthday != null) { birthdayCode = birthday.hashCode(); } result = ((prime * result) + birthdayCode); int hasMobileCode = 0; if (hasMobile != null) { hasMobileCode = hasMobile.hashCode(); } result = ((prime * result) + hasMobileCode); int boolsCode = 0; if (bools != null) { boolsCode = bools.hashCode(); } result = ((prime * result) + boolsCode); return result; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/model/PersonWithDetails.java ================================================ /** * Copyright (C) 2017 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 com.company.model; import java.util.ArrayList; import java.util.Date; import java.util.List; import javassist.util.proxy.MethodHandler; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.OrderColumn; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Version; import com.company.model.javassist.MethodHandlerImpl; import com.company.model.javassist.ProxyImpl; import com.company.model.javassist.ProxyObjectImpl; import com.fasterxml.jackson.annotation.JsonIgnore; @javax.persistence.Entity(name = "Person") @Table(name = "PERSON") @NamedQueries({ @NamedQuery(name = "Person.findByName", query = "SELECT p\nFROM Person p\nWHERE p.name= :name\nORDER BY p.persistenceId"), @NamedQuery(name = "Person.findByAge", query = "SELECT p\nFROM Person p\nWHERE p.age= :age\nORDER BY p.persistenceId"), @NamedQuery(name = "Person.findByBirthday", query = "SELECT p\nFROM Person p\nWHERE p.birthday= :birthday\nORDER BY p.persistenceId"), @NamedQuery(name = "Person.findByHasMobile", query = "SELECT p\nFROM Person p\nWHERE p.hasMobile= :hasMobile\nORDER BY p.persistenceId"), @NamedQuery(name = "Person.find", query = "SELECT p\nFROM Person p\nORDER BY p.persistenceId"), @NamedQuery(name = "Person.findById", query = "SELECT p \nFROM Person p \nWHERE p.persistenceId = :persistenceId"), @NamedQuery(name = "Person.findAdults", query = "SELECT p \nFROM Person p \nWHERE p.age > 18\nORDER BY p.persistenceId ASC"), @NamedQuery(name = "Person.query1", query = "SELECT p \nFROM Person p \nWHERE p.hasMobile = :hasMobile\nORDER BY p.persistenceId ASC"), @NamedQuery(name = "Person.findByRange", query = "SELECT p \nFROM Person p \nWHERE p.birthday >= :date1 AND p.birthday <= :date2\nORDER BY p.persistenceId ASC") }) public class PersonWithDetails implements org.bonitasoft.engine.bdm.Entity { @Id @GeneratedValue private Long persistenceId; @Version private Long persistenceVersion; @Column(name = "NAME", nullable = true, length = 255) private String name; @Column(name = "AGE", nullable = true) private Integer age; @JsonIgnore private Phone secretPhone = null; @OneToMany(orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name = "PERSON_PID", nullable = false) @OrderColumn private List phones = new ArrayList(10); @Column(name = "BIRTHDAY", nullable = true) @Temporal(TemporalType.TIMESTAMP) private Date birthday; @Column(name = "HASMOBILE", nullable = false) private Boolean hasMobile; @ElementCollection(fetch = FetchType.EAGER) @OrderColumn @Column(name = "BOOLS", nullable = true) private List bools = new ArrayList(10); @ElementCollection(fetch = FetchType.LAZY) @OrderColumn @Column(name = "TOIGNORE", nullable = true) @JsonIgnore private List toIgnores = new ArrayList<>(10); @OneToOne(orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name = "PERSON_PID", nullable = false) private Address address1; @OneToOne(orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name = "PERSON_PID", nullable = false) private Address address2; private List toIncludes = new ArrayList<>(10); private MethodHandler methodHandlerObject; private List proxys = new ArrayList<>(10); public PersonWithDetails() { // needed by jackson (otherwise, add an annotation for constructor) } public void setPersistenceId(Long persistenceId) { this.persistenceId = persistenceId; } public Long getPersistenceId() { return persistenceId; } public void setPersistenceVersion(Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } public Long getPersistenceVersion() { return persistenceVersion; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setAge(Integer age) { this.age = age; } public Integer getAge() { return age; } public void setPhones(List phones) { if (this.phones == null) { this.phones = phones; } else { this.phones.clear(); this.phones.addAll(phones); } } public List getPhones() { return phones; } public void addToPhones(Phone addTo) { List phones = getPhones(); phones.add(addTo); } public void removeFromPhones(Phone removeFrom) { List phones = getPhones(); phones.remove(removeFrom); } public void setBirthday(Date birthday) { this.birthday = birthday; } public Date getBirthday() { return birthday; } public void setHasMobile(Boolean hasMobile) { this.hasMobile = hasMobile; } public Boolean isHasMobile() { return hasMobile; } public void setBools(List bools) { if (this.bools == null) { this.bools = bools; } else { this.bools.clear(); this.bools.addAll(bools); } } public List getBools() { return bools; } public void addToBools(Boolean addTo) { List bools = getBools(); bools.add(addTo); } public void removeFromBools(Boolean removeFrom) { List bools = getBools(); bools.remove(removeFrom); } public List getToIgnores() { return toIgnores; } public void addToIgnores(Long addTo) { toIgnores.add(addTo); } public List getToIncludes() { return toIncludes; } public void setToIncludes(List toIncludes) { this.toIncludes = toIncludes; } public void addToIncludes(Long addTo) { toIncludes.add(addTo); } public Boolean getHasMobile() { return hasMobile; } public void setToIgnores(List toIgnores) { this.toIgnores = toIgnores; } public Phone getSecretPhone() { return secretPhone; } public void setSecretPhone(Phone secretPhone) { this.secretPhone = secretPhone; } public Address getAddress1() { return address1; } public void setAddress1(Address address1) { this.address1 = address1; } public Address getAddress2() { return address2; } public void setAddress2(Address address2) { this.address2 = address2; } public MethodHandler getMethodHandlerObject() { return methodHandlerObject; } public void setMethodHandlerObject(MethodHandler methodHandlerObject) { this.methodHandlerObject = methodHandlerObject; } public void addToProxysAsMethodHandler(Object object) { proxys.add(new MethodHandlerImpl(object)); } public void addToProxysAsProxyImpl(Object object) { proxys.add(new ProxyImpl(object)); } public void addToProxysAsProxyObjectImpl(Object object) { proxys.add(new ProxyObjectImpl(object)); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/model/Phone.java ================================================ /** * Copyright (C) 2015 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 com.company.model; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; import javax.persistence.Version; /** * */ @javax.persistence.Entity(name = "Phone") @Table(name = "PHONE") @NamedQueries({ @NamedQuery(name = "Phone.findByNumber", query = "SELECT p\nFROM Phone p\nWHERE p.number= :number\nORDER BY p.persistenceId"), @NamedQuery(name = "Phone.find", query = "SELECT p\nFROM Phone p\nORDER BY p.persistenceId") }) public class Phone implements org.bonitasoft.engine.bdm.Entity { @Id @GeneratedValue private Long persistenceId; @Version private Long persistenceVersion; @Column(name = "NUMBER", nullable = true, length = 255) private String number; public Phone() { } public void setPersistenceId(Long persistenceId) { this.persistenceId = persistenceId; } public Long getPersistenceId() { return persistenceId; } public void setPersistenceVersion(Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } public Long getPersistenceVersion() { return persistenceVersion; } public void setNumber(String number) { this.number = number; } public String getNumber() { return number; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Phone other = ((Phone) obj); if (persistenceId == null) { if (other.persistenceId != null) { return false; } } else { if (!persistenceId.equals(other.persistenceId)) { return false; } } if (persistenceVersion == null) { if (other.persistenceVersion != null) { return false; } } else { if (!persistenceVersion.equals(other.persistenceVersion)) { return false; } } if (number == null) { if (other.number != null) { return false; } } else { if (!number.equals(other.number)) { return false; } } return true; } @Override public int hashCode() { final int prime = 31; int result = 1; int persistenceIdCode = 0; if (persistenceId != null) { persistenceIdCode = persistenceId.hashCode(); } result = ((prime * result) + persistenceIdCode); int persistenceVersionCode = 0; if (persistenceVersion != null) { persistenceVersionCode = persistenceVersion.hashCode(); } result = ((prime * result) + persistenceVersionCode); int numberCode = 0; if (number != null) { numberCode = number.hashCode(); } result = ((prime * result) + numberCode); return result; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/model/javassist/MethodHandlerImpl.java ================================================ /** * Copyright (C) 2017 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 com.company.model.javassist; import java.lang.reflect.Method; import javassist.util.proxy.MethodHandler; public class MethodHandlerImpl implements MethodHandler { private Object obj; public MethodHandlerImpl(Object obj) { this.obj = obj; } @Override public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { return obj; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/model/javassist/ProxyImpl.java ================================================ /** * Copyright (C) 2017 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 com.company.model.javassist; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.Proxy; public class ProxyImpl implements Proxy { private Object obj; public ProxyImpl(Object obj) { this.obj = obj; } @Override public void setHandler(MethodHandler mi) { // do nothing } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/model/javassist/ProxyObjectImpl.java ================================================ /** * Copyright (C) 2017 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 com.company.model.javassist; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyObject; public class ProxyObjectImpl implements ProxyObject { private Object obj; public ProxyObjectImpl(Object obj) { this.obj = obj; } @Override public void setHandler(MethodHandler mi) { // do nothing } @Override public MethodHandler getHandler() { return null; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/pojo/ComplexInvoice.java ================================================ /** * Copyright (C) 2015 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 com.company.pojo; import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Lob; import javax.persistence.Version; @Entity public class ComplexInvoice implements org.bonitasoft.engine.bdm.Entity { private static final long serialVersionUID = -230L; @Id @GeneratedValue private Long persistenceId; @Version private Long persistenceVersion; private String string; @Lob private String aLongText; private byte aByte; private char aChar; private boolean aBoolean; private Date aDate; private double aDouble; private float aFloat; private int anInteger; private long aLong; private short aShort; @Override public Long getPersistenceId() { return persistenceId; } @Override public Long getPersistenceVersion() { return persistenceVersion; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/pojo/ConstrainedItem.java ================================================ /** * Copyright (C) 2015 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 com.company.pojo; import java.util.Date; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Version; @Entity @Table(uniqueConstraints = @javax.persistence.UniqueConstraint(columnNames = { "bonito", "string" })) public class ConstrainedItem implements org.bonitasoft.engine.bdm.Entity { private static final long serialVersionUID = -230L; @Id @GeneratedValue private Long persistenceId; @Version private Long persistenceVersion; private long bonito; private String string; private Date un_constrained; @Override public Long getPersistenceId() { return persistenceId; } @Override public Long getPersistenceVersion() { return persistenceVersion; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/pojo/Employee.java ================================================ /** * Copyright (C) 2015 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 com.company.pojo; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Version; @Entity public class Employee implements org.bonitasoft.engine.bdm.Entity { private static final long serialVersionUID = -506130279298072307L; @Id @GeneratedValue private Long persistenceId; @Version private Long persistenceVersion; private String firstName; private String lastName; protected Employee() { super(); } public Employee(final long persistenceId, final String firstName, final String lastName) { this(firstName, lastName); this.persistenceId = persistenceId; } public Employee(final String firstName, final String lastName) { super(); this.firstName = firstName; this.lastName = lastName; } public Employee(final Employee employee) { persistenceId = employee.getPersistenceId(); persistenceVersion = employee.getPersistenceVersion(); firstName = employee.getFirstName(); lastName = employee.getLastName(); } @Override public Long getPersistenceId() { return persistenceId; } @Override public Long getPersistenceVersion() { return persistenceVersion; } public String getFirstName() { return firstName; } public void setFirstName(final String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(final String lastName) { this.lastName = lastName; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (firstName == null ? 0 : firstName.hashCode()); result = prime * result + (persistenceId == null ? 0 : persistenceId.hashCode()); result = prime * result + (lastName == null ? 0 : lastName.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Employee other = (Employee) obj; if (firstName == null) { if (other.firstName != null) { return false; } } else if (!firstName.equals(other.firstName)) { return false; } if (persistenceId == null) { if (other.persistenceId != null) { return false; } } else if (!persistenceId.equals(other.persistenceId)) { return false; } if (lastName == null) { if (other.lastName != null) { return false; } } else if (!lastName.equals(other.lastName)) { return false; } return true; } @Override public String toString() { return "Employee [persistenceId=" + persistenceId + ", firstName=" + firstName + ", lastName=" + lastName + "]"; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/pojo/EmployeeBuilder.java ================================================ /** * Copyright (C) 2015 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 com.company.pojo; public class EmployeeBuilder { private String firstName = "aFirstName"; private String lastName = "aLastName"; public static EmployeeBuilder anEmployee() { return new EmployeeBuilder(); } public Employee build() { return new Employee(firstName, lastName); } public EmployeeBuilder withFirstName(final String firstName) { this.firstName = firstName; return this; } public EmployeeBuilder withLastName(final String lastName) { this.lastName = lastName; return this; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/com/company/pojo/Person.java ================================================ /** * Copyright (C) 2015 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 com.company.pojo; import java.util.ArrayList; import java.util.List; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OrderColumn; import javax.persistence.Table; import javax.persistence.Version; import org.bonitasoft.engine.bdm.Entity; /** * */ @javax.persistence.Entity(name = "Person") @Table(name = "PERSON") public class Person implements Entity { private static final long serialVersionUID = 7686491164348658989L; @Id @GeneratedValue private Long persistenceId; @Version private Long persistenceVersion; @ElementCollection(fetch = FetchType.EAGER) @Column(name = "NICKNAMES", nullable = true, length = 15) @OrderColumn private List nickNames = new ArrayList(10); public Person() { super(); } public Person(final Person person) { persistenceId = person.getPersistenceId(); persistenceVersion = person.getPersistenceVersion(); nickNames = person.getNickNames(); } public void setPersistenceId(final Long persistenceId) { this.persistenceId = persistenceId; } @Override public Long getPersistenceId() { return persistenceId; } public void setPersistenceVersion(final Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } @Override public Long getPersistenceVersion() { return persistenceVersion; } public void setNickNames(final List nickNames) { this.nickNames = nickNames; } public List getNickNames() { return nickNames; } public void addTo(final String addTo) { nickNames.add(addTo); } public void removeFrom(final String removeFrom) { nickNames.remove(removeFrom); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Person other = (Person) obj; if (persistenceId == null) { if (other.persistenceId != null) { return false; } } else { if (!persistenceId.equals(other.persistenceId)) { return false; } } if (persistenceVersion == null) { if (other.persistenceVersion != null) { return false; } } else { if (!persistenceVersion.equals(other.persistenceVersion)) { return false; } } if (nickNames == null) { if (other.nickNames != null) { return false; } } else { if (!nickNames.equals(other.nickNames)) { return false; } } return true; } @Override public int hashCode() { final int prime = 31; int result = 1; int persistenceIdCode = 0; if (persistenceId != null) { persistenceIdCode = persistenceId.hashCode(); } result = prime * result + persistenceIdCode; int persistenceVersionCode = 0; if (persistenceVersion != null) { persistenceVersionCode = persistenceVersion.hashCode(); } result = prime * result + persistenceVersionCode; int nickNamesCode = 0; if (nickNames != null) { nickNamesCode = nickNames.hashCode(); } result = prime * result + nickNamesCode; return result; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/BOMBuilder.java ================================================ /** * 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; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.SimpleField; public class BOMBuilder { public static BOMBuilder aBOM() { return new BOMBuilder(); } public BusinessObjectModel build() { final SimpleField firstName = new SimpleField(); firstName.setName("firstName"); firstName.setType(FieldType.STRING); final SimpleField lastName = new SimpleField(); lastName.setName("lastName"); lastName.setType(FieldType.STRING); final BusinessObject employee = new BusinessObject(); employee.setQualifiedName("com.company.Employee"); employee.addField(firstName); employee.addField(lastName); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(employee); return bom; } public BusinessObjectModel buildModelWithAllSupportedTypes() { final BusinessObject invoice = new BusinessObject(); invoice.setQualifiedName("com.company.pojo.ComplexInvoice"); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(invoice); return bom; } public BusinessObjectModel buildModelWithConstrainedFields() { final BusinessObject constrained = new BusinessObject(); constrained.setQualifiedName("com.company.pojo.ConstrainedItem"); final BusinessObjectModel bom = new BusinessObjectModel(); bom.addBusinessObject(constrained); return bom; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/JpaTestConfiguration.java ================================================ /** * 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.business.data; import java.util.Map; /** * @author Emmanuel Duchastenier */ public class JpaTestConfiguration { private Map jpaConfiguration; private Map jpaModelConfiguration; public JpaTestConfiguration(Map jpaConfiguration, Map jpaModelConfiguration) { this.jpaConfiguration = jpaConfiguration; this.jpaModelConfiguration = jpaModelConfiguration; } public Map getJpaConfiguration() { return jpaConfiguration; } public Map getJpaModelConfiguration() { return jpaModelConfiguration; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/AddNewEmployeeThread.java ================================================ /** * 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.business.data.impl; import javax.transaction.UserTransaction; import com.company.pojo.Employee; import org.bonitasoft.engine.business.data.BusinessDataRepository; public class AddNewEmployeeThread extends Thread { private final BusinessDataRepository repository; private final long employeeId; public AddNewEmployeeThread(final BusinessDataRepository repository, final long employeeId) { this.repository = repository; this.employeeId = employeeId; } @Override public void run() { UserTransaction transaction = com.arjuna.ats.jta.UserTransaction.userTransaction(); try { transaction.begin(); final Employee myEmployee = new Employee("John" + employeeId, "Doe"); Thread.sleep(150); repository.persist(myEmployee); transaction.commit(); } catch (final Exception e) { try { transaction.rollback(); } catch (final Exception e1) { e.printStackTrace(); throw new IllegalArgumentException(e1); } throw new IllegalArgumentException(e); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/AddressHibernateProxy.java ================================================ /** * 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.business.data.impl; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import java.util.List; import com.company.model.Address; import org.bonitasoft.engine.bdm.Entity; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; /** * A fake HibernateProxy that wraps an Address entity. *

      * This mimics Hibernate's proxy behavior where the proxy class itself has null/empty fields, * but the real data is accessible via {@link LazyInitializer#getImplementation()}. *

      * Used to reproduce the bug where HibernateProxy entities are serialized as empty objects * because {@link org.bonitasoft.engine.business.data.impl.jackson.EntityBeanSerializerModifier#changeProperties} * returns an empty property list for HibernateProxy types. */ public class AddressHibernateProxy implements HibernateProxy, Entity { private final Address realAddress; private final boolean initialized; private final LazyInitializer lazyInitializer; public AddressHibernateProxy(Address realAddress) { this(realAddress, true); } public AddressHibernateProxy(Address realAddress, boolean initialized) { this.realAddress = realAddress; this.initialized = initialized; this.lazyInitializer = createMockLazyInitializer(realAddress, initialized); } private static LazyInitializer createMockLazyInitializer(Address realAddress, boolean initialized) { LazyInitializer mockInitializer = mock(LazyInitializer.class); doReturn(Address.class).when(mockInitializer).getPersistentClass(); doReturn(realAddress).when(mockInitializer).getImplementation(); doReturn(!initialized).when(mockInitializer).isUninitialized(); return mockInitializer; } @Override public Object writeReplace() { return null; } @Override public LazyInitializer getHibernateLazyInitializer() { return lazyInitializer; } @Override public Long getPersistenceId() { return realAddress != null ? realAddress.getPersistenceId() : null; } @Override public Long getPersistenceVersion() { return realAddress != null ? realAddress.getPersistenceVersion() : null; } public String getStreet() { throwIfUninitialized("street"); return realAddress != null ? realAddress.getStreet() : null; } public Float getNumber() { throwIfUninitialized("number"); return realAddress != null ? realAddress.getNumber() : null; } public List getFloors() { throwIfUninitialized("floors"); return realAddress != null ? realAddress.getFloors() : null; } public String getDoorCode() { throwIfUninitialized("doorCode"); return realAddress != null ? realAddress.getDoorCode() : null; } private void throwIfUninitialized(String propertyName) { if (!initialized) { throw new org.hibernate.LazyInitializationException( "could not initialize proxy - no Session: " + propertyName); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/BdmFieldTypeConverterTest.java ================================================ /** * Copyright (C) 2025 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.business.data.impl; import static org.assertj.core.api.Assertions.*; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import org.junit.jupiter.api.Test; class BdmFieldTypeConverterTest { @Test void should_convert_float_to_double() { assertThat(BdmFieldTypeConverter.convert(125f, Double.class)).isEqualTo(125d); } @Test void should_convert_double_float() { assertThat(BdmFieldTypeConverter.convert(125d, Float.class)).isEqualTo(125f); } @Test void should_convert_integer_to_double() { assertThat(BdmFieldTypeConverter.convert(1255598556, Double.class)).isEqualTo(1255598556d); } @Test void should_convert_integer_to_long() { assertThat(BdmFieldTypeConverter.convert(9988877, Long.class)).isEqualTo(9988877L); } @Test void should_convert_long_to_integer() { assertThat(BdmFieldTypeConverter.convert(9988877L, Integer.class)).isEqualTo(9988877); } @Test void should_convert_integer_to_short() { assertThat(BdmFieldTypeConverter.convert(55000, Short.class)).isEqualTo((short) 55000); } @Test void should_convert_integer_to_byte() { assertThat(BdmFieldTypeConverter.convert(124, Byte.class)).isEqualTo((byte) 124); } @Test void converting_null_should_return_null() { assertThat(BdmFieldTypeConverter.convert(null, Long.class)).isNull(); } @Test void converting_supported_type_should_return_it_directly_String() { final String someString = "someString"; assertThat(BdmFieldTypeConverter.convert(someString, String.class)).isSameAs(someString); } @Test void converting_supported_type_should_return_it_directly_Float() { Float someFloat = 1.0f; assertThat(BdmFieldTypeConverter.convert(someFloat, Float.class)).isSameAs(someFloat); } @Test void should_convert_string_to_localdate() { assertThat(BdmFieldTypeConverter.convert("2025-07-17", LocalDate.class)).isEqualTo(LocalDate.of(2025, 7, 17)); } @Test void should_convert_string_to_localdatetime() { assertThat(BdmFieldTypeConverter.convert("2025-01-12T10:15:30", LocalDateTime.class)) .isEqualTo(LocalDateTime.of(2025, 1, 12, 10, 15, 30)); } @Test void should_convert_string_to_offsetdatetime() { OffsetDateTime result = BdmFieldTypeConverter.convert("2025-11-29T10:15:30+01:00", OffsetDateTime.class); assertThat(result.toLocalDateTime()).isEqualTo(LocalDateTime.of(2025, 11, 29, 10, 15, 30)); assertThat(result.getOffset()).isEqualTo(ZoneOffset.of("+01:00")); } @Test void should_throw_exception_for_incompatible_conversion() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> BdmFieldTypeConverter.convert("invalid", Integer.class)) .withMessageContaining("Cannot convert"); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/BusinessDataModelRepositoryImplTest.java ================================================ /** * 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.business.data.impl; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.*; import static org.bonitasoft.engine.commons.Pair.pair; import static org.bonitasoft.engine.commons.io.IOUtil.zip; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import java.io.InputStream; import java.sql.SQLException; import java.sql.SQLSyntaxErrorException; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Optional; import org.bonitasoft.engine.BOMBuilder; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService; import org.bonitasoft.engine.business.data.InvalidBusinessDataModelException; import org.bonitasoft.engine.business.data.SBusinessDataRepositoryDeploymentException; import org.bonitasoft.engine.business.data.SBusinessDataRepositoryException; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.dependency.SDependencyDeletionException; import org.bonitasoft.engine.dependency.SDependencyNotFoundException; import org.bonitasoft.engine.dependency.impl.TenantDependencyService; import org.bonitasoft.engine.dependency.model.SDependency; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.io.IOUtil; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.platform.model.SPlatformProperties; import org.bonitasoft.engine.resources.TenantResourceType; import org.bonitasoft.engine.resources.TenantResourcesService; import org.hibernate.tool.schema.spi.CommandAcceptanceException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class BusinessDataModelRepositoryImplTest { private static final long MEANINGLESS_ARTIFACT_ID = -1L; @Mock private TenantDependencyService dependencyService; @Mock private ClassLoaderService classLoaderService; @Mock private TenantResourcesService tenantResourcesService; @Mock private SchemaManagerUpdate schemaManager; @Mock(lenient = true) private PlatformService platformService; @Mock(lenient = true) private SPlatformProperties platformProperties; @Mock private DataRetentionBdmTrackingService bdmTrackingService; private BusinessDataModelRepositoryImpl businessDataModelRepository; @Before public void setUp() throws Exception { doReturn(platformProperties).when(platformService).getSPlatformProperties(); doReturn("1.0").when(platformProperties).getPlatformVersion(); // Prevent uninstall()/install() from nullifying the thread context classloader // via classLoaderService.getClassLoader() returning null (default mock behavior) doReturn(getClass().getClassLoader()).when(classLoaderService).getClassLoader(any()); businessDataModelRepository = spy(new BusinessDataModelRepositoryImpl(platformService, dependencyService, classLoaderService, schemaManager, tenantResourcesService, bdmTrackingService)); } @Test public void should_createAndDeployServerBDMJar_add_dependency_on_bdm_server_jar() throws Exception { BusinessObjectModel bom = BOMBuilder.aBOM().build(); doReturn("some bytes".getBytes()).when(businessDataModelRepository).generateServerBDMJar(bom); doReturn(mock(SDependency.class)).when(dependencyService).createMappedDependency("BDR", "some bytes".getBytes(), "BDR.jar", MEANINGLESS_ARTIFACT_ID, ScopeType.TENANT); businessDataModelRepository.createAndDeployServerBDMJar(bom); verify(dependencyService).createMappedDependency("BDR", "some bytes".getBytes(), "BDR.jar", MEANINGLESS_ARTIFACT_ID, ScopeType.TENANT); } @Test public void should_createAndDeployClientBDMZip_add_resource_on_tenantResourcesService() throws Exception { BusinessObjectModel bom = BOMBuilder.aBOM().build(); doReturn("some bytes".getBytes()).when(businessDataModelRepository).generateClientBDMZip(bom); businessDataModelRepository.createAndDeployClientBDMZip(bom, 1154222L); verify(tenantResourcesService).add(eq("client-bdm.zip"), eq(TenantResourceType.BDM), eq("some bytes".getBytes()), anyLong()); } @Test public void uninstall_should_delete_a_dependency() throws Exception { businessDataModelRepository.uninstall(); verify(dependencyService).deleteDependency("BDR"); } @Test public void uninstall_should_ignore_exception_if_the_dependency_does_not_exist() throws Exception { doThrow(new SDependencyNotFoundException("error")).when(dependencyService).deleteDependency("BDR"); assertThatNoException().isThrownBy(() -> businessDataModelRepository.uninstall()); } @Test(expected = SBusinessDataRepositoryException.class) public void uninstall_should_throw_an_exception_if_an_exception_occurs_during_the_dependency_deletion() throws Exception { doThrow(new SDependencyDeletionException("error")).when(dependencyService).deleteDependency("BDR"); businessDataModelRepository.uninstall(); } @Test public void should_return_getBusinessObjectModel_return_null_when_no_bdm() throws Exception { //given doReturn(false).when(businessDataModelRepository).isBDMDeployed(); //when then assertThat(businessDataModelRepository.getBusinessObjectModel()).isNull(); } @Test public void should_return_getBusinessObjectModel_return_bdm() throws Exception { //given final InputStream resourceAsStream = this.getClass().getResourceAsStream("client-bdm.zip"); final byte[] clientBDMZip = IOUtil.getAllContentFrom(resourceAsStream); doReturn(clientBDMZip).when(businessDataModelRepository).getClientBDMZip(); //when final BusinessObjectModel model = businessDataModelRepository.getBusinessObjectModel(); //then assertThat(model).as("should return the business model").as("should return the bom").isNotNull(); } @Test(expected = SBusinessDataRepositoryDeploymentException.class) public void should_getBusinessObjectModel_throw_exception() throws Exception { //given final InputStream resourceAsStream = this.getClass().getResourceAsStream("client-bdm.zip"); final byte[] clientBDMZip = IOUtil.getAllContentFrom(resourceAsStream); doReturn(clientBDMZip).when(businessDataModelRepository).getClientBDMZip(); doThrow(new InvalidBusinessDataModelException(new Exception())).when(businessDataModelRepository) .getBusinessObjectModel(any(clientBDMZip.getClass())); //when then exception businessDataModelRepository.getBusinessObjectModel(); } @Test public void install_should_pass_userId_to_tenantResourceService() throws Exception { // given: final BusinessObjectModel businessObjectModel = BOMBuilder.aBOM().build(); doReturn(businessObjectModel).when(businessDataModelRepository).getBusinessObjectModel(any(byte[].class)); final byte[] bom = "some generated bytes".getBytes(); doReturn(bom).when(businessDataModelRepository).generateClientBDMZip(businessObjectModel); doReturn(9L).when(businessDataModelRepository).createAndDeployServerBDMJar(businessObjectModel); // when: final long userId = 47L; businessDataModelRepository.install(bom, userId); // then: verify(tenantResourcesService).add("client-bdm.zip", TenantResourceType.BDM, bom, userId); } @Test(expected = InvalidBusinessDataModelException.class) public void install_should_throw_an_SInvalidBusinessDataModelException_when_zip_is_invalid() throws Exception { // given: final byte[] bom = "some invalid context".getBytes(); // when: businessDataModelRepository.install(bom, 47L); } @Test(expected = InvalidBusinessDataModelException.class) public void install_should_throw_an_SInvalidBusinessDataModelException_when_bomXml_is_invalid() throws Exception { // given: final byte[] bom = zip(pair("bom.xml", "".getBytes())); // when: businessDataModelRepository.install(bom, 47L); } @Test public void getInstalledBDMVersion_should_return_version_number() throws Exception { for (long version : new long[] { 0L, 1L, -1L, 42L, Long.MAX_VALUE, Long.MIN_VALUE }) { // given: doReturn(Optional.of(version)).when(dependencyService) .getIdOfDependencyOfArtifactForTenant(BusinessDataModelRepositoryImpl.BDR_DEPENDENCY_FILENAME); // when: final String installedBDMVersion = businessDataModelRepository.getInstalledBDMVersion(); // then: assertThat(installedBDMVersion).isEqualTo(String.valueOf(version)); } } @Test public void update_should_convert_exceptions_to_allow_to_see_entire_root_cause() { // given: doReturn(singletonList(new CommandAcceptanceException("Error executing DDL bla bla bla...", new SQLSyntaxErrorException("ORA-02275: une telle contrainte référentielle existe déjà dans la table", new Exception("Root Oracle Cause"))))) .when(schemaManager) .update(anySet()); // when - then: assertThatExceptionOfType(SBusinessDataRepositoryDeploymentException.class) .isThrownBy(() -> businessDataModelRepository.update(new HashSet<>())) .withMessageContainingAll( "1: org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL bla bla bla...", "caused by java.sql.SQLSyntaxErrorException", "caused by java.lang.Exception: Root Oracle Cause"); } @Test public void update_should_convert_all_exceptions_in_the_list() { // given: doReturn(Arrays.asList( new CommandAcceptanceException("Error executing DDL bla bla bla...", new SQLSyntaxErrorException( "ORA-02275: une telle contrainte référentielle existe déjà dans la table", new Exception("Root Oracle Cause"))), new CommandAcceptanceException("CommandAcceptanceException bliblibli", new SQLSyntaxErrorException("Hibernate error")))) .when(schemaManager) .update(anySet()); // when - then: assertThatExceptionOfType(SBusinessDataRepositoryDeploymentException.class) .isThrownBy(() -> businessDataModelRepository.update(new HashSet<>())) .withMessageContainingAll( "1: org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL bla bla bla...", "caused by java.lang.Exception: Root Oracle Cause", "2: org.hibernate.tool.schema.spi.CommandAcceptanceException: CommandAcceptanceException bliblibli", "caused by java.sql.SQLSyntaxErrorException: Hibernate error"); } @Test public void convertExceptions_should_filter_out_empty_message_lines() { // given: final List exceptions = singletonList(new SQLSyntaxErrorException( "message with trailing carriage return\n", new SQLException("syntax error"))); // when: final String message = businessDataModelRepository.convertExceptions(exceptions); // then: assertThat(Arrays.asList(message.split("\n"))).doesNotContain(""); } @Test public void isDeployedComparesBdmWithSameContent() throws Exception { // given: var bdmArchive = "fake archive content".getBytes(); var generatedJarContent = "fake jar content".getBytes(); when(dependencyService .getIdOfDependencyOfArtifactForTenant(BusinessDataModelRepositoryImpl.BDR_DEPENDENCY_FILENAME)) .thenReturn(Optional.of(1L)); var deployedBdm = new SDependency(); deployedBdm.setValue_(generatedJarContent); when(dependencyService.getDependency(1L)).thenReturn(deployedBdm); doReturn(null).when(businessDataModelRepository).getBusinessObjectModel(bdmArchive); doReturn(generatedJarContent).when(businessDataModelRepository).generateServerBDMJar(any(), eq(false)); // when: var isDeployed = businessDataModelRepository.isDeployed(bdmArchive); // then: assertThat(isDeployed).isTrue(); } @Test public void isDeployedComparesBdmWithDifferentContent() throws Exception { // given: var bdmArchive = "fake archive content".getBytes(); var existingJarContent = "fake existing jar content".getBytes(); var generatedJarContent = "fake jar content".getBytes(); when(dependencyService .getIdOfDependencyOfArtifactForTenant(BusinessDataModelRepositoryImpl.BDR_DEPENDENCY_FILENAME)) .thenReturn(Optional.of(1L)); var deployedBdm = new SDependency(); deployedBdm.setValue_(existingJarContent); when(dependencyService.getDependency(1L)).thenReturn(deployedBdm); doReturn(null).when(businessDataModelRepository).getBusinessObjectModel(bdmArchive); doReturn(generatedJarContent).when(businessDataModelRepository).generateServerBDMJar(any(), eq(false)); // when: var isDeployed = businessDataModelRepository.isDeployed(bdmArchive); // then: assertThat(isDeployed).isFalse(); } @Test public void isDeployedWithoutBdmDeployed() throws Exception { // given: var bdmArchive = "fake archive content".getBytes(); when(dependencyService .getIdOfDependencyOfArtifactForTenant(BusinessDataModelRepositoryImpl.BDR_DEPENDENCY_FILENAME)) .thenReturn(Optional.empty()); // when: var isDeployed = businessDataModelRepository.isDeployed(bdmArchive); // then: assertThat(isDeployed).isFalse(); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/BusinessDataReloaderTest.java ================================================ /** * 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.business.data.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.verifyNoInteractions; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.proxy.ServerLazyLoader; import org.bonitasoft.engine.business.data.proxy.ServerProxyfier; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Elias Ricken de Medeiros */ @RunWith(MockitoJUnitRunner.class) public class BusinessDataReloaderTest { @Mock private BusinessDataRepository repository; @Mock private ServerLazyLoader lazyLoader; @InjectMocks private ServerProxyfier proxyfier; @InjectMocks private BusinessDataReloader reloader; @Test public void reloadEntity_should_call_findById_on_repository() throws Exception { //given long id = 5L; EntityPojo entity = new EntityPojo(id); EntityPojo entityUpdated = new EntityPojo(6L); given(repository.findById(EntityPojo.class, id)).willReturn(entityUpdated); //when Entity reloadedEntity = reloader.reloadEntity(entity); //then assertThat(reloadedEntity).isEqualTo(entityUpdated); } @Test public void reloadEntity_should_use_real_class_to_reload_entity() throws Exception { //given long id = 5L; EntityPojo proxyfiedEntityPojo = proxyfier.proxify(new EntityPojo(id)); EntityPojo entityUpdated = new EntityPojo(6L); // use the real class here given(repository.findById(EntityPojo.class, id)).willReturn(entityUpdated); //when Entity reloadedEntity = reloader.reloadEntity(proxyfiedEntityPojo); //then assertThat(reloadedEntity).isEqualTo(entityUpdated); } @Test public void reloadEntitySoftly_should_return_entity_itself_when_id_is_not_set() throws Exception { //given EntityPojo entity = new EntityPojo(null); //when Entity reloadedEntity = reloader.reloadEntitySoftly(entity); //then assertThat(reloadedEntity).isEqualTo(entity); verifyNoInteractions(repository); } @Test public void reloadEntitySoftly_should_reload_entity_id_is_set() throws Exception { //given long id = 2L; EntityPojo entity = new EntityPojo(id); EntityPojo entityUpdated = new EntityPojo(3L); given(repository.findById(EntityPojo.class, id)).willReturn(entityUpdated); //when Entity reloadedEntity = reloader.reloadEntitySoftly(entity); //then assertThat(reloadedEntity).isEqualTo(entityUpdated); } @Test public void reloadEntitySoftly_should_use_real_class_to_reload_entity() throws Exception { //given long id = 2L; EntityPojo proxyfiedEntity = new EntityPojo(id); EntityPojo entityUpdated = new EntityPojo(3L); //use real class given(repository.findById(EntityPojo.class, id)).willReturn(entityUpdated); //when Entity reloadedEntity = reloader.reloadEntitySoftly(proxyfiedEntity); //then assertThat(reloadedEntity).isEqualTo(entityUpdated); } @Test public void getEntityRealClass_should_handle_Bonita_proxy() throws Exception { final Class entityRealClass = reloader.getEntityRealClass(proxyfier.proxify(new EntityPojo(451L))); assertThat(entityRealClass).isEqualTo(EntityPojo.class); } @Test public void getEntityRealClass_should_handle_Hibernate_proxy() throws Exception { final Class entityRealClass = reloader.getEntityRealClass(new FakeHibernateProxyEntity()); assertThat(entityRealClass).isEqualTo(EntityPojo.class); } @Test public void getEntityRealClass_should_handle_Hibernate_proxy_inside_Bonita_proxy() throws Exception { final Class entityRealClass = reloader.getEntityRealClass(proxyfier.proxify(new FakeHibernateProxyEntity())); assertThat(entityRealClass).isEqualTo(EntityPojo.class); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/BusinessDataRepositoryEventAspectTest.java ================================================ /** * Copyright (C) 2025 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.business.data.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.events.model.SDeleteEvent; import org.bonitasoft.engine.events.model.SInsertEvent; import org.bonitasoft.engine.events.model.SUpdateEvent; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; class BusinessDataRepositoryEventAspectTest { private EventService eventService; private BusinessDataRepositoryEventAspect aspect; private Entity entity; @BeforeEach void setUp() { eventService = mock(EventService.class); aspect = new BusinessDataRepositoryEventAspect(eventService); entity = mock(Entity.class); } @Test void should_fire_insert_event_on_persist_when_id_is_null() throws Throwable { when(entity.getPersistenceId()).thenReturn(null); Object dummyResult = new Object(); var joinPoint = mock(org.aspectj.lang.ProceedingJoinPoint.class); when(joinPoint.proceed()).thenReturn(dummyResult); Object result = aspect.aroundPersist(joinPoint, entity); ArgumentCaptor captor = ArgumentCaptor.forClass(SInsertEvent.class); verify(eventService).fireEvent(captor.capture()); SInsertEvent event = captor.getValue(); assertThat(event.getType()).isEqualTo("BUSINESS_DATA_CREATED"); assertThat(event.getObject()).isEqualTo(entity); assertThat(result).isSameAs(dummyResult); } @Test void should_fire_update_event_on_persist_when_id_is_not_null() throws Throwable { when(entity.getPersistenceId()).thenReturn(123L); Object dummyResult = new Object(); var joinPoint = mock(org.aspectj.lang.ProceedingJoinPoint.class); when(joinPoint.proceed()).thenReturn(dummyResult); Object result = aspect.aroundPersist(joinPoint, entity); ArgumentCaptor captor = ArgumentCaptor.forClass(SUpdateEvent.class); verify(eventService).fireEvent(captor.capture()); SUpdateEvent event = captor.getValue(); assertThat(event.getType()).isEqualTo("BUSINESS_DATA_UPDATED"); assertThat(event.getObject()).isEqualTo(entity); assertThat(result).isSameAs(dummyResult); } @Test void should_fire_delete_event_on_remove() throws Exception { aspect.afterRemove(entity); ArgumentCaptor captor = ArgumentCaptor.forClass(SDeleteEvent.class); verify(eventService).fireEvent(captor.capture()); SDeleteEvent event = captor.getValue(); assertThat(event.getType()).isEqualTo("BUSINESS_DATA_DELETED"); assertThat(event.getObject()).isEqualTo(entity); } @Test void should_fire_delete_event_on_removeById() throws Exception { aspect.afterRemoveById(entity); ArgumentCaptor captor = ArgumentCaptor.forClass(SDeleteEvent.class); verify(eventService).fireEvent(captor.capture()); SDeleteEvent event = captor.getValue(); assertThat(event.getType()).isEqualTo("BUSINESS_DATA_DELETED"); assertThat(event.getObject()).isEqualTo(entity); } @Test void should_fire_insert_event_on_merge_when_id_is_null() throws Throwable { when(entity.getPersistenceId()).thenReturn(null); Object dummyResult = new Object(); var joinPoint = mock(org.aspectj.lang.ProceedingJoinPoint.class); when(joinPoint.proceed()).thenReturn(dummyResult); Object result = aspect.aroundMerge(joinPoint, entity); ArgumentCaptor captor = ArgumentCaptor.forClass(SInsertEvent.class); verify(eventService).fireEvent(captor.capture()); SInsertEvent event = captor.getValue(); assertThat(event.getType()).isEqualTo("BUSINESS_DATA_CREATED"); assertThat(event.getObject()).isEqualTo(entity); assertThat(result).isSameAs(dummyResult); } @Test void should_fire_update_event_on_merge_when_id_is_not_null() throws Throwable { when(entity.getPersistenceId()).thenReturn(123L); Object dummyResult = new Object(); var joinPoint = mock(org.aspectj.lang.ProceedingJoinPoint.class); when(joinPoint.proceed()).thenReturn(dummyResult); Object result = aspect.aroundMerge(joinPoint, entity); ArgumentCaptor captor = ArgumentCaptor.forClass(SUpdateEvent.class); verify(eventService).fireEvent(captor.capture()); SUpdateEvent event = captor.getValue(); assertThat(event.getType()).isEqualTo("BUSINESS_DATA_UPDATED"); assertThat(event.getObject()).isEqualTo(entity); assertThat(result).isSameAs(dummyResult); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/BusinessDataServiceImplTest.java ================================================ /** * 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.business.data.impl; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; import java.io.Serializable; 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 org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.bdm.model.Query; import org.bonitasoft.engine.bdm.model.QueryParameter; import org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryMetadata; import org.bonitasoft.engine.bpm.businessdata.BusinessDataQueryResult; import org.bonitasoft.engine.business.data.BusinessDataModelRepository; import org.bonitasoft.engine.business.data.BusinessDataRepository; import org.bonitasoft.engine.business.data.JsonBusinessDataSerializer; import org.bonitasoft.engine.business.data.SBusinessDataNotFoundException; import org.bonitasoft.engine.business.data.SBusinessDataRepositoryException; import org.bonitasoft.engine.business.data.SBusinessDataRepositorySerializationException; import org.bonitasoft.engine.business.data.proxy.ServerLazyLoader; import org.bonitasoft.engine.business.data.proxy.ServerProxyfier; import org.bonitasoft.engine.commons.TypeConverterUtil; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class BusinessDataServiceImplTest { public static final String PARAMETER_STRING = "parameterString"; public static final String PARAMETER_INTEGER = "parameterInteger"; public static final String PARAMETER_LONG = "parameterLong"; private static final String PARAMETER_BUSINESSDATA_CLASS_URI_VALUE = "/businessdata/{className}/{id}/{field}"; private static final String NEW_NAME = "new name"; private final Entity pojo = new EntityPojo(1L); @Mock JsonBusinessDataSerializer jsonEntitySerializer; @Mock BusinessDataModelRepository businessDataModelRepository; @Mock private BusinessDataRepository businessDataRepository; @Mock private BusinessDataReloader businessDataReloader; @Mock private CountQueryProvider countQueryProvider; private BusinessDataServiceImpl businessDataService; @Before public void before() { final String[] datePatterns = new String[] { "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd", "HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ss.SSS" }; businessDataService = spy(new BusinessDataServiceImpl(businessDataRepository, jsonEntitySerializer, businessDataModelRepository, new TypeConverterUtil(datePatterns), businessDataReloader, countQueryProvider)); // Mock default standard shape enabled to true (matches runtime default) when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(true); } @Test public void isBusinessDataShouldBeTrue() { assertThat(businessDataService.isBusinessData(pojo)).isTrue(); } @Test public void isBusinessDataShouldBeTrueWithList() { assertThat(businessDataService.isBusinessData(List.of(pojo))).isTrue(); assertThat(businessDataService.isBusinessData(new ArrayList())).isTrue(); } @Test public void isBusinessDataShouldBeFalseWithList() { assertThat(businessDataService.isBusinessData("not a list")).isFalse(); assertThat(businessDataService.isBusinessData(List.of(1L))).isFalse(); } @Test public void isBusinessDataShouldBeFalse() { final Object pojoObject = new Object(); assertThat(businessDataService.isBusinessData(pojoObject)).isFalse(); } @Test public void isBusinessDataShouldBeFalseWhenDataIsNull() { assertThat(businessDataService.isBusinessData(null)).isFalse(); } @Test public void callJavaOperationShouldThrowExceptionWhenBusinessDataIsNull() { assertThatExceptionOfType(SBusinessDataNotFoundException.class) .isThrownBy(() -> businessDataService.callJavaOperation(null, pojo, "someMethod", String.class.getName())); } @Test public void callJavaOperationShouldInvokeListMethod() { final List entities = List.of(pojo); assertThatNoException() .isThrownBy(() -> businessDataService.callJavaOperation(entities, entities, "contains", Object.class.getName())); } @Test public void callJavaOperationShouldThrowExceptionWhenNotAnEntity() { assertThatExceptionOfType(SBusinessDataRepositoryException.class) .isThrownBy(() -> businessDataService.callJavaOperation("not an entity", null, "getLengh", String.class.getName())); } @Test public void callJavaOperationShouldThrowExceptionWhenBusinessDataIsNotFound() throws Exception { //given doThrow(SBusinessDataNotFoundException.class).when(businessDataReloader).reloadEntitySoftly(pojo); //when - then assertThatExceptionOfType(SBusinessDataNotFoundException.class) .isThrownBy(() -> businessDataService.callJavaOperation(pojo, pojo, "getName", String.class.getName())); } @Test public void callJavaOperationShouldThrowExceptionWhenInvokeFails() { assertThatExceptionOfType(SBusinessDataRepositoryException.class) .isThrownBy(() -> businessDataService.callJavaOperation(pojo, pojo, "someMethod", String.class.getName())); } @Test public void callJavaOperationShouldSetValue() throws Exception { //given doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo); //when final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, NEW_NAME, "setName", String.class.getName()); assertThat(pojoObject).as("should return object").isNotNull(); assertThat(pojoObject.getName()).as("should have set name").isEqualTo(NEW_NAME); } @Test public void callJavaOperationSetValueToNull() throws Exception { //given doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo); //when businessDataService.callJavaOperation(pojo, NEW_NAME, "setName", String.class.getName()); final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, null, "setName", String.class.getName()); //then assertThat(pojoObject).as("should return object").isNotNull(); assertThat(pojoObject.getName()).as("should have set name to null").isNull(); } @Test public void callJavaOperationShouldSetEntityComposition() throws Exception { //given final EntityPojo compositionEntity = new EntityPojo(2L); doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo); //when final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, compositionEntity, "setCompositionEntity", Entity.class.getName()); assertThat(pojoObject).as("should return object").isNotNull(); assertThat(pojoObject.getCompositionEntity()).as("should have set entity").isEqualTo(compositionEntity); verify(businessDataReloader, never()).reloadEntity(any(Entity.class)); } @Test public void callJavaOperationShouldSetEntityAggregation() throws Exception { //given final EntityPojo aggregationEntity = new EntityPojo(2L); doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo); doReturn(aggregationEntity).when(businessDataReloader).reloadEntity(aggregationEntity); //when final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, aggregationEntity, "setAggregationEntity", Entity.class.getName()); assertThat(pojoObject).as("should return object").isNotNull(); assertThat(pojoObject.getAggregationEntity()).as("should have set entity").isEqualTo(aggregationEntity); verify(businessDataReloader).reloadEntity(aggregationEntity); } @Test public void callJavaOperationShouldWithProxyfiedEntityShouldUsedRealEntityClass() throws Exception { //given final EntityPojo entity = new EntityPojo(2L); ServerProxyfier proxyfier = new ServerProxyfier(new ServerLazyLoader(businessDataRepository)); EntityPojo proxyfiedEntity = proxyfier.proxify(entity); doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo); //when businessDataService.callJavaOperation(pojo, proxyfiedEntity, "setAggregationEntity", Entity.class.getName()); verify(businessDataReloader).reloadEntitySoftly(pojo); ArgumentCaptor captor = ArgumentCaptor.forClass(EntityPojo.class); verify(businessDataReloader).reloadEntity(captor.capture()); assertThat(captor.getValue().getPersistenceId()).isEqualTo(2L); } @Test public void callJavaOperationWithListOfProxyfiedEntitiesShouldUsedRealEntityClass() throws Exception { //given final EntityPojo entity = new EntityPojo(2L); ServerProxyfier proxyfier = new ServerProxyfier(new ServerLazyLoader(businessDataRepository)); EntityPojo proxyfiedEntity = proxyfier.proxify(entity); doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo); // Cannot specify the real instance (instead of any()) because of proxy object that does not match (Mockito): doReturn(pojo.getClass()).when(businessDataReloader).getEntityRealClass(any(Entity.class)); //when businessDataService.callJavaOperation(pojo, Collections.singletonList(proxyfiedEntity), "setAggregationEntities", List.class.getName()); verify(businessDataRepository).findByIds(entity.getClass(), Collections.singletonList(2L)); } @Test public void callJavaOperationShouldLoadEntitiesIfAggregation() throws Exception { //given final Long persistenceId1 = 1562L; final Long persistenceId2 = 9658L; final EntityPojo entity1 = new EntityPojo(persistenceId1); final EntityPojo entity2 = new EntityPojo(persistenceId2); final List entities = Arrays.asList(entity1, entity2); final List keys = Arrays.asList(persistenceId1, persistenceId2); doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo); // Cannot specify the real instance (instead of any()) because of proxy object that does not match (Mockito): doReturn(pojo.getClass()).when(businessDataReloader).getEntityRealClass(any(Entity.class)); doReturn(entities).when(businessDataRepository).findByIds(EntityPojo.class, keys); //when final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, entities, "setAggregationEntities", List.class.getName()); assertThat(pojoObject).as("should return object").isNotNull(); assertThat(pojoObject.getAggregationEntities()).as("should have set entities").isEqualTo(entities); verify(businessDataRepository).findByIds(EntityPojo.class, keys); } @Test public void callJavaOperationShouldNotLoadEntitiesIfComposition() throws Exception { //given final Long persistenceId1 = 1562L; final Long persistenceId2 = 9658L; final EntityPojo entity1 = new EntityPojo(persistenceId1); final EntityPojo entity2 = new EntityPojo(persistenceId2); final List entities = Arrays.asList(entity1, entity2); final List keys = Arrays.asList(persistenceId1, persistenceId2); doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo); //when final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, entities, "setCompositionEntities", List.class.getName()); assertThat(pojoObject).as("should return object").isNotNull(); assertThat(pojoObject.getCompositionEntities()).as("should have set entities").isEqualTo(entities); verify(businessDataRepository, never()).findByIds(EntityPojo.class, keys); } @Test public void callJavaOperationShouldThrowExceptionWhenPersistenceIdIsNull() { //given final Long persistenceId1 = 1562L; final EntityPojo entity1 = new EntityPojo(persistenceId1); final EntityPojo entity2 = new EntityPojo(null); final List entities = Arrays.asList(entity1, entity2); doReturn(EntityPojo.class).when(businessDataReloader).getEntityRealClass(entity2); //when - then assertThatExceptionOfType(SBusinessDataNotFoundException.class) .isThrownBy(() -> businessDataService.callJavaOperation(pojo, entities, "setAggregationEntities", List.class.getName())) .withMessage("Forbidden instance of " + EntityPojo.class.getName() + " found. " + "It is only possible to reference persisted instances in an aggregation relation."); } @Test public void callJavaOperationWithEmptyList() throws Exception { //given doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo); //when final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, List.of(), "setAggregationEntities", List.class.getName()); assertThat(pojoObject).as("should return object").isNotNull(); assertThat(pojoObject.getAggregationEntities()).as("should have set entities").isEmpty(); } @Test public void callJavaOperationShouldSetListValue() throws Exception { //given final List longs = Arrays.asList(1L, 2L); doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo); //when final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, longs, "setNumbers", List.class.getName()); //then assertThat(pojoObject).as("should return object").isNotNull(); assertThat(pojoObject.getNumbers()).as("should have set list").hasSize(2).contains(1L, 2L); } @Test public void callJavaOperationShouldSetListToNull() throws Exception { //given final List longs = Arrays.asList(1L, 2L); doReturn(pojo).when(businessDataReloader).reloadEntitySoftly(pojo); //when businessDataService.callJavaOperation(pojo, longs, "setNumbers", List.class.getName()); final EntityPojo pojoObject = (EntityPojo) businessDataService.callJavaOperation(pojo, null, "setNumbers", List.class.getName()); //then assertThat(pojoObject).as("should return object").isNotNull(); assertThat(pojoObject.getNumbers()).as("should have set list to null").isNull(); } @Test public void shouldSetListOnBusinessDataReplaceTheList() throws Exception { //given final Long persistenceId2 = 9658L; final EntityPojo entity1 = new EntityPojo(1562L); final EntityPojo entity2 = new EntityPojo(persistenceId2); final EntityPojo entityPojo = new EntityPojo(1L); entityPojo.getAggregationEntities().add(entity1); final List newEntities = List.of(entity2); final List keys2 = List.of(persistenceId2); doReturn(entityPojo).when(businessDataReloader).reloadEntitySoftly(entityPojo); // Cannot specify the real instance (instead of any()) because of proxy object that does not match (Mockito): doReturn(pojo.getClass()).when(businessDataReloader).getEntityRealClass(any(Entity.class)); doReturn(newEntities).when(businessDataRepository).findByIds(entity2.getClass(), keys2); //when final EntityPojo resultPojo = (EntityPojo) businessDataService.callJavaOperation(entityPojo, newEntities, "setAggregationEntities", List.class.getName()); assertThat(resultPojo).as("should return object").isNotNull(); assertThat(resultPojo.getAggregationEntities()).as("should have set entities").containsExactly(entity2); } @Test public void should_loadClass_find_the_class() throws Exception { //when final Class loadClass = businessDataService.loadClass(pojo.getClass().getName()); //then assertThat(loadClass).isEqualTo(pojo.getClass()); } @Test public void should_loadClass_throw_exception() { assertThatExceptionOfType(SBusinessDataRepositoryException.class) .isThrownBy(() -> businessDataService.loadClass("not a class")); } @Test public void should_getJsonEntity_serialize_entity() throws Exception { //given doReturn(pojo).when(businessDataRepository).findById(pojo.getClass(), pojo.getPersistenceId()); //when businessDataService.getJsonEntity(pojo.getClass().getName(), pojo.getPersistenceId(), PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); //then verify(jsonEntitySerializer).serializeEntity(pojo, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); } @Test public void should_getJsonEntity_throw_exception() throws Exception { //given doReturn(pojo).when(businessDataRepository).findById(pojo.getClass(), pojo.getPersistenceId()); doThrow(SBusinessDataRepositorySerializationException.class).when(jsonEntitySerializer).serializeEntity(pojo, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); //when then exception assertThatExceptionOfType(SBusinessDataRepositoryException.class) .isThrownBy(() -> businessDataService.getJsonEntity(pojo.getClass().getName(), pojo.getPersistenceId(), PARAMETER_BUSINESSDATA_CLASS_URI_VALUE)); } @Test public void should_getJsonChildEntity_return_emptyObject() throws Exception { //given final EntityPojo parentEntity = new EntityPojo(1562L); final EntityPojo childEntity = new EntityPojo(156842L); parentEntity.setAggregationEntity(childEntity); doReturn(parentEntity).when(businessDataRepository).findById(parentEntity.getClass(), parentEntity.getPersistenceId()); //when final Serializable jsonChildEntity = businessDataService.getJsonChildEntity(parentEntity.getClass().getName(), parentEntity.getPersistenceId(), "nullChildEntity", PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); //then assertThat(jsonChildEntity).isEqualTo(JsonBusinessDataSerializer.EMPTY_OBJECT); } @Test public void should_getJsonChildEntity_serialize_entity() throws Exception { //given final EntityPojo parentEntity = new EntityPojo(1562L); final EntityPojo childEntity = new EntityPojo(156842L); parentEntity.setAggregationEntity(childEntity); doReturn(parentEntity).when(businessDataRepository).findById(parentEntity.getClass(), parentEntity.getPersistenceId()); doReturn(childEntity).when(businessDataRepository).unwrap(childEntity); //when businessDataService.getJsonChildEntity(parentEntity.getClass().getName(), parentEntity.getPersistenceId(), "aggregationEntity", PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); //then verify(jsonEntitySerializer).serializeEntity(childEntity, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); } @Test public void should_getJsonChildEntity_serialize_entity_list() throws Exception { //given final EntityPojo parentEntity = new EntityPojo(1562L); final EntityPojo childEntity = new EntityPojo(156842L); parentEntity.getAggregationEntities().add(childEntity); doReturn(parentEntity).when(businessDataRepository).findById(parentEntity.getClass(), parentEntity.getPersistenceId()); //when businessDataService.getJsonChildEntity(parentEntity.getClass().getName(), parentEntity.getPersistenceId(), "aggregationEntities", PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); //then final List list = new ArrayList<>(); list.add(childEntity); verify(jsonEntitySerializer).serializeEntities(list, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); } @Test public void getJsonQueryEntities_should_return_list_of_entities_as_json() throws Exception { //given final EntityPojo entity = new EntityPojo(1562L); final Map parameters = new HashMap<>(); parameters.put(PARAMETER_STRING, "a"); parameters.put(PARAMETER_INTEGER, "12"); parameters.put(PARAMETER_LONG, "34"); doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName()); final List entities = new ArrayList<>(); entities.add(entity); doReturn(entities).when(businessDataRepository).findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt()); //given final BusinessObjectModel businessObjectModel = getBusinessObjectModel(entity); doReturn(businessObjectModel).when(businessDataModelRepository).getBusinessObjectModel(); //when businessDataService.getJsonQueryEntities(entity.getClass().getName(), "query", parameters, 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); //then // Query returns String.class (not List), so hasMultipleResults is false verify(jsonEntitySerializer).serializeEntityQueryResult(entities, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true, false); } @Test public void getJsonQueryEntities_should_return_count_result_as_json() throws Exception { //given doReturn(EntityPojo.class).when(businessDataService).loadClass(EntityPojo.class.getName()); doReturn(Collections.singletonList(5L)).when(businessDataRepository).findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt()); doReturn(getBusinessObjectModel(new EntityPojo(1562L))).when(businessDataModelRepository) .getBusinessObjectModel(); //when businessDataService.getJsonQueryEntities(EntityPojo.class.getName(), "countForFind", new HashMap<>(), 0, 1, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); //then verify(jsonEntitySerializer).serializeScalarResult(Collections.singletonList(5L), EntityPojo.class.getName(), true); } @Test public void getJsonQueryEntities_should_throw_exception_when_query_not_found() throws Exception { //given final EntityPojo entity = new EntityPojo(1562L); final Map parameters = new HashMap<>(); parameters.put(PARAMETER_STRING, "a"); parameters.put(PARAMETER_INTEGER, "12"); parameters.put(PARAMETER_LONG, "34"); doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName()); final BusinessObjectModel businessObjectModel = getBusinessObjectModel(entity); doReturn(businessObjectModel).when(businessDataModelRepository).getBusinessObjectModel(); //when then exception assertThatExceptionOfType(SBusinessDataRepositoryException.class) .isThrownBy(() -> businessDataService.getJsonQueryEntities(entity.getClass().getName(), "wrongQuery", parameters, 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE)) .withMessage("unable to get query wrongQuery for business object " + EntityPojo.class.getName()); } @Test public void getJsonQueryEntities_should_find_provided_query() throws Exception { //given final EntityPojo entity = new EntityPojo(1562L); final Map parameters = new HashMap<>(); parameters.put(PARAMETER_STRING, "a"); parameters.put(PARAMETER_INTEGER, "12"); parameters.put(PARAMETER_LONG, "34"); doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName()); final BusinessObjectModel businessObjectModel = getBusinessObjectModel(entity); doReturn(businessObjectModel).when(businessDataModelRepository).getBusinessObjectModel(); Query countQuery = new Query("countForFind", "query", Long.class.getName()); doReturn(countQuery).when(countQueryProvider).getCountQueryDefinition(any(BusinessObject.class), any(Query.class)); final long count = 5L; doReturn(count).when(businessDataRepository).findByNamedQuery(eq("EntityPojo.countForFind"), eq(Long.class), anyMap()); //when final int startIndex = 3; final int maxResults = 10; final BusinessDataQueryResult queryResult = businessDataService.getJsonQueryEntities( entity.getClass().getName(), "find", parameters, startIndex, maxResults, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); //then verify(businessDataRepository).findByNamedQuery(eq("EntityPojo.countForFind"), eq(Long.class), anyMap()); final BusinessDataQueryMetadata businessDataQueryMetadata = queryResult.getBusinessDataQueryMetadata(); assertThat(businessDataQueryMetadata) .as("should retrieve metadata") .isNotNull(); assertThat(businessDataQueryMetadata.getStartIndex()).isEqualTo(startIndex); assertThat(businessDataQueryMetadata.getMaxResults()).isEqualTo(maxResults); assertThat(businessDataQueryMetadata.getCount()).isEqualTo(count); } @Test public void getJsonQueryEntities_should_check_parameters() throws Exception { //given final EntityPojo entity = new EntityPojo(1562L); doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName()); final BusinessObjectModel businessObjectModel = getBusinessObjectModel(entity); doReturn(businessObjectModel).when(businessDataModelRepository).getBusinessObjectModel(); //when then exception assertThatExceptionOfType(SBusinessDataRepositoryException.class) .isThrownBy(() -> businessDataService.getJsonQueryEntities(entity.getClass().getName(), "query", null, 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE)) .withMessageContainingAll("parameter(s) are missing for query named query :", PARAMETER_INTEGER, PARAMETER_STRING, PARAMETER_LONG); } private BusinessObjectModel getBusinessObjectModel(final EntityPojo entity) { BusinessObjectModel businessObjectModel; businessObjectModel = new BusinessObjectModel(); final Query query = new Query("query", "content", String.class.getName()); query.getQueryParameters().add(new QueryParameter(PARAMETER_STRING, String.class.getName())); query.getQueryParameters().add(new QueryParameter(PARAMETER_INTEGER, Integer.class.getName())); query.getQueryParameters().add(new QueryParameter(PARAMETER_LONG, Long.class.getName())); final BusinessObject businessObject = new BusinessObject(); businessObject.setQualifiedName(entity.getClass().getName()); businessObject.setQueries(List.of(query)); businessObjectModel.getBusinessObjects().add(businessObject); return businessObjectModel; } @Test public void getJsonEntities_should_serialize_entities() throws Exception { final long identifier1 = 1983L; final long identifier2 = 1990L; final EntityPojo pojo1 = new EntityPojo(identifier1); final EntityPojo pojo2 = new EntityPojo(identifier2); final List identifiers = new ArrayList<>(); identifiers.add(identifier1); identifiers.add(identifier2); final List pojos = new ArrayList<>(); pojos.add(pojo1); pojos.add(pojo2); when(businessDataRepository.findByIdentifiers(EntityPojo.class, identifiers)).thenReturn(pojos); businessDataService.getJsonEntities(EntityPojo.class.getName(), identifiers, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); verify(jsonEntitySerializer).serializeEntities(pojos, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); } @Test public void getJsonEntities_should_throw_exception_if_the_serialization_fails() throws Exception { final long identifier1 = 1983L; final long identifier2 = 1990L; final EntityPojo pojo1 = new EntityPojo(identifier1); final EntityPojo pojo2 = new EntityPojo(identifier2); final List identifiers = new ArrayList<>(); identifiers.add(identifier1); identifiers.add(identifier2); final List pojos = new ArrayList<>(); pojos.add(pojo1); pojos.add(pojo2); when(businessDataRepository.findByIdentifiers(EntityPojo.class, identifiers)).thenReturn(pojos); when(jsonEntitySerializer.serializeEntities(pojos, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE)) .thenThrow(new SBusinessDataRepositorySerializationException("exception")); assertThatExceptionOfType(SBusinessDataRepositoryException.class) .isThrownBy(() -> businessDataService.getJsonEntities(EntityPojo.class.getName(), identifiers, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE)); } @Test public void getJsonQueryEntities_should_return_legacy_count_format_when_standardShapeDisabled() throws Exception { when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(false); final EntityPojo entity = new EntityPojo(1562L); doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName()); // Build a BDM model with the count query final Query countQuery = new Query("customcount", "SELECT count(w) FROM Watched w", Long.class.getName()); final BusinessObject businessObject = new BusinessObject(); businessObject.setQualifiedName(entity.getClass().getName()); businessObject.setQueries(List.of(countQuery)); final BusinessObjectModel bom = new BusinessObjectModel(); bom.setBusinessObjects(List.of(businessObject)); doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel(); final List countList = Collections.singletonList(10L); doReturn(countList).when(businessDataRepository) .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt()); businessDataService.getJsonQueryEntities(entity.getClass().getName(), "customcount", new HashMap<>(), 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); verify(jsonEntitySerializer).serializeScalarResult(countList, entity.getClass().getName(), false); } @Test public void getJsonQueryEntities_should_return_legacy_single_entity_list_format_when_standardShapeDisabled() throws Exception { when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(false); final EntityPojo entity = new EntityPojo(1562L); doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName()); // Build a BDM model with a query returning one entity final Query singleQuery = new Query("findByUserAndRequest", "SELECT w FROM Watched w", entity.getClass().getName()); final BusinessObject businessObject = new BusinessObject(); businessObject.setQualifiedName(entity.getClass().getName()); businessObject.setQueries(List.of(singleQuery)); final BusinessObjectModel bom = new BusinessObjectModel(); bom.setBusinessObjects(List.of(businessObject)); doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel(); final List results = Collections.singletonList(entity); doReturn(results).when(businessDataRepository) .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt()); businessDataService.getJsonQueryEntities(entity.getClass().getName(), "findByUserAndRequest", new HashMap<>(), 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); // Legacy mode serializes a single result as a list // Query returns entity class (not List), so hasMultipleResults is false verify(jsonEntitySerializer).serializeEntityQueryResult(results, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, false, false); } @Test public void getJsonQueryEntities_should_return_legacy_list_format_when_standardShapeDisabled() throws Exception { when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(false); final EntityPojo entity1 = new EntityPojo(1562L); final EntityPojo entity2 = new EntityPojo(2000L); doReturn(entity1.getClass()).when(businessDataService).loadClass(entity1.getClass().getName()); // Build a BDM model with a list-returning query final Query listQuery = new Query("findByCaseId", "SELECT w FROM Watched w", List.class.getName()); final BusinessObject businessObject = new BusinessObject(); businessObject.setQualifiedName(entity1.getClass().getName()); businessObject.setQueries(List.of(listQuery)); final BusinessObjectModel bom = new BusinessObjectModel(); bom.setBusinessObjects(List.of(businessObject)); doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel(); final List results = List.of(entity1, entity2); doReturn(results).when(businessDataRepository) .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt()); businessDataService.getJsonQueryEntities(entity1.getClass().getName(), "findByCaseId", new HashMap<>(), 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); // Legacy list behavior is unchanged // Query returns List.class, so hasMultipleResults is true verify(jsonEntitySerializer).serializeEntityQueryResult(results, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, false, true); } @Test public void getJsonQueryEntities_should_handle_Long_scalar_query_with_standard_shape() throws Exception { when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(true); final EntityPojo entity = new EntityPojo(1562L); doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName()); final Query countQuery = new Query("customcount", "SELECT count(w) FROM Watched w", Long.class.getName()); final BusinessObject businessObject = new BusinessObject(); businessObject.setQualifiedName(entity.getClass().getName()); businessObject.setQueries(List.of(countQuery)); final BusinessObjectModel bom = new BusinessObjectModel(); bom.setBusinessObjects(List.of(businessObject)); doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel(); final List countList = Collections.singletonList(10L); doReturn(countList).when(businessDataRepository) .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt()); doReturn("{ \"value\": 10 }").when(jsonEntitySerializer) .serializeScalarResult(countList, entity.getClass().getName(), true); BusinessDataQueryResult result = businessDataService.getJsonQueryEntities(entity.getClass().getName(), "customcount", new HashMap<>(), 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); assertThat(result.getJsonResults().toString()).contains("{", "value", "10"); } @Test public void getJsonQueryEntities_should_return_standard_list_format_when_standardShapeEnabled() throws Exception { when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(true); final EntityPojo entity1 = new EntityPojo(1562L); final EntityPojo entity2 = new EntityPojo(2000L); doReturn(entity1.getClass()).when(businessDataService).loadClass(entity1.getClass().getName()); final Query listQuery = new Query("findByCaseId", "SELECT w FROM Watched w", List.class.getName()); final BusinessObject businessObject = new BusinessObject(); businessObject.setQualifiedName(entity1.getClass().getName()); businessObject.setQueries(List.of(listQuery)); final BusinessObjectModel bom = new BusinessObjectModel(); bom.setBusinessObjects(List.of(businessObject)); doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel(); final List results = List.of(entity1, entity2); doReturn(results).when(businessDataRepository) .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt()); businessDataService.getJsonQueryEntities(entity1.getClass().getName(), "findByCaseId", new HashMap<>(), 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); // Lists remain the same in standard mode // Query returns List.class, so hasMultipleResults is true verify(jsonEntitySerializer).serializeEntityQueryResult(results, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true, true); } @Test public void getJsonQueryEntities_should_return_standard_single_entity_format_when_standardShapeEnabled() throws Exception { when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(true); final EntityPojo entity = new EntityPojo(1562L); doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName()); // Build a BDM model with a query returning one entity final Query singleQuery = new Query("findByUserAndRequest", "SELECT w FROM Watched w", entity.getClass().getName()); final BusinessObject businessObject = new BusinessObject(); businessObject.setQualifiedName(entity.getClass().getName()); businessObject.setQueries(List.of(singleQuery)); final BusinessObjectModel bom = new BusinessObjectModel(); bom.setBusinessObjects(List.of(businessObject)); doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel(); final List results = Collections.singletonList(entity); doReturn(results).when(businessDataRepository) .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt()); businessDataService.getJsonQueryEntities(entity.getClass().getName(), "findByUserAndRequest", new HashMap<>(), 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); // Standard mode serializes a single result as an object (not array) // Query returns entity class (not List), so hasMultipleResults is false verify(jsonEntitySerializer).serializeEntityQueryResult(results, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true, false); } @Test public void getJsonQueryEntities_should_handle_Double_scalar_query_with_standard_shape() throws Exception { when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(true); final EntityPojo entity = new EntityPojo(1562L); doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName()); // Build a BDM model with a Double-returning query (e.g., AVG, MAX) final Query avgQuery = new Query("averageSalary", "SELECT AVG(e.salary) FROM Employee e", Double.class.getName()); final BusinessObject businessObject = new BusinessObject(); businessObject.setQualifiedName(entity.getClass().getName()); businessObject.setQueries(List.of(avgQuery)); final BusinessObjectModel bom = new BusinessObjectModel(); bom.setBusinessObjects(List.of(businessObject)); doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel(); final List avgResult = Collections.singletonList(55000.75); doReturn(avgResult).when(businessDataRepository) .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt()); doReturn("{ \"value\": 55000.75 }").when(jsonEntitySerializer) .serializeScalarResult(avgResult, entity.getClass().getName(), true); BusinessDataQueryResult result = businessDataService.getJsonQueryEntities(entity.getClass().getName(), "averageSalary", new HashMap<>(), 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); assertThat(result.getJsonResults().toString()).contains("{", "value", "55000.75"); verify(jsonEntitySerializer).serializeScalarResult(avgResult, entity.getClass().getName(), true); } @Test public void getJsonQueryEntities_should_handle_Float_scalar_query_with_legacy_shape() throws Exception { when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(false); final EntityPojo entity = new EntityPojo(1562L); doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName()); // Build a BDM model with a Float-returning query final Query maxQuery = new Query("maxScore", "SELECT MAX(e.score) FROM Employee e", Float.class.getName()); final BusinessObject businessObject = new BusinessObject(); businessObject.setQualifiedName(entity.getClass().getName()); businessObject.setQueries(List.of(maxQuery)); final BusinessObjectModel bom = new BusinessObjectModel(); bom.setBusinessObjects(List.of(businessObject)); doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel(); final List maxResult = Collections.singletonList(98.5f); doReturn(maxResult).when(businessDataRepository) .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt()); businessDataService.getJsonQueryEntities(entity.getClass().getName(), "maxScore", new HashMap<>(), 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); verify(jsonEntitySerializer).serializeScalarResult(maxResult, entity.getClass().getName(), false); } @Test public void getJsonQueryEntities_should_handle_Integer_scalar_query_with_standard_shape() throws Exception { when(jsonEntitySerializer.isStandardShapeEnabled()).thenReturn(true); final EntityPojo entity = new EntityPojo(1562L); doReturn(entity.getClass()).when(businessDataService).loadClass(entity.getClass().getName()); // Build a BDM model with an Integer-returning query final Query sumQuery = new Query("sumAge", "SELECT SUM(e.age) FROM Employee e", Integer.class.getName()); final BusinessObject businessObject = new BusinessObject(); businessObject.setQualifiedName(entity.getClass().getName()); businessObject.setQueries(List.of(sumQuery)); final BusinessObjectModel bom = new BusinessObjectModel(); bom.setBusinessObjects(List.of(businessObject)); doReturn(bom).when(businessDataModelRepository).getBusinessObjectModel(); final List sumResult = Collections.singletonList(450); doReturn(sumResult).when(businessDataRepository) .findListByNamedQuery(anyString(), any(), anyMap(), anyInt(), anyInt()); doReturn("{ \"value\": 450 }").when(jsonEntitySerializer) .serializeScalarResult(sumResult, entity.getClass().getName(), true); BusinessDataQueryResult result = businessDataService.getJsonQueryEntities(entity.getClass().getName(), "sumAge", new HashMap<>(), 0, 10, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); assertThat(result.getJsonResults().toString()).contains("{", "value", "450"); verify(jsonEntitySerializer).serializeScalarResult(sumResult, entity.getClass().getName(), true); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/ConcurrencyIT.java ================================================ /** * 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.business.data.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.sql.DataSource; import javax.transaction.UserTransaction; import com.company.pojo.Employee; import org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService; import org.bonitasoft.engine.business.data.JpaTestConfiguration; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.dependency.impl.TenantDependencyService; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.resources.TenantResourcesService; import org.bonitasoft.engine.transaction.UserTransactionService; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) public class ConcurrencyIT { private JPABusinessDataRepositoryImpl businessDataRepository; @Autowired @Qualifier("businessDataDataSource") private DataSource datasource; @Autowired @Qualifier("notManagedBizDataSource") private DataSource modelDatasource; @Autowired @Qualifier("jpa-test-configuration") private JpaTestConfiguration configuration; private JdbcTemplate jdbcTemplate; private UserTransaction ut; private final ClassLoaderService classLoaderService = mock(ClassLoaderService.class); @Before public void setUp() throws Exception { if (jdbcTemplate == null) { jdbcTemplate = new JdbcTemplate(datasource); } final SchemaManagerUpdate schemaManager = new SchemaManagerUpdate(configuration.getJpaModelConfiguration()); final BusinessDataModelRepositoryImpl businessDataModelRepositoryImpl = spy( new BusinessDataModelRepositoryImpl(mock(PlatformService.class), mock(TenantDependencyService.class), classLoaderService, schemaManager, mock(TenantResourcesService.class), mock(DataRetentionBdmTrackingService.class))); final UserTransactionService transactionService = mock(UserTransactionService.class); businessDataRepository = spy( new JPABusinessDataRepositoryImpl(transactionService, businessDataModelRepositoryImpl, configuration.getJpaConfiguration(), classLoaderService, mock(DataRetentionBdmTrackingService.class))); doReturn(true).when(businessDataModelRepositoryImpl).isBDMDeployed(); ut = com.arjuna.ats.jta.UserTransaction.userTransaction(); ut.begin(); final Set classNames = new HashSet<>(); classNames.add(Employee.class.getName()); businessDataModelRepositoryImpl.update(classNames); businessDataRepository.start(); ut.commit(); } @After public void tearDown() { businessDataRepository.stop(); final JdbcTemplate jdbcTemplate = new JdbcTemplate(modelDatasource); try { jdbcTemplate.update("drop table Employee"); } catch (final Exception e) { // ignore drop of non-existing table } } @Test public void addConcurrentlyEmployeesShouldCreateAllTheEmployees() throws Exception { final ExecutorService threadPoolExecutor = new ThreadPoolExecutor(5, 10, 5000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); final int expected = 10; for (int i = 0; i < expected; i++) { final Thread thread = new AddNewEmployeeThread(businessDataRepository, i); threadPoolExecutor.submit(thread); } threadPoolExecutor.shutdown(); threadPoolExecutor.awaitTermination(3 * expected, TimeUnit.SECONDS); ut = com.arjuna.ats.jta.UserTransaction.userTransaction(); ut.begin(); final List employees = businessDataRepository.findList(Employee.class, "SELECT e FROM Employee e", null, 0, 100); ut.commit(); assertThat(employees).hasSize(expected); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/CountQueryProviderTest.java ================================================ /** * 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.business.data.impl; import java.util.List; import org.bonitasoft.engine.bdm.model.BusinessObject; import org.bonitasoft.engine.bdm.model.Query; import org.bonitasoft.engine.bdm.model.assertion.QueryAssert; import org.bonitasoft.engine.bdm.model.field.FieldType; import org.bonitasoft.engine.bdm.model.field.SimpleField; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class CountQueryProviderTest { public static final String QUALIFIED_NAME = "com.corp.Arrival"; private CountQueryProvider provider; @Before public void setUp() throws Exception { provider = new CountQueryProvider(); } @Test public void getCountQueryDefinition_should_return_related_count_query_for_generated_queries() throws Exception { //given final BusinessObject bo = buildBusinessObject(); Query find = new Query("find", "", List.class.getName()); //when Query countQueryDefinition = provider.getCountQueryDefinition(bo, find); //then QueryAssert.assertThat(countQueryDefinition).isNotNull(); QueryAssert.assertThat(countQueryDefinition).hasName("countForFind"); } @Test public void getCountQueryDefinition_should_return_null_when_base_query_is_not_multiple() throws Exception { //given final BusinessObject bo = buildBusinessObject(); Query find = new Query("findByPeople", "", QUALIFIED_NAME); //when Query countQueryDefinition = provider.getCountQueryDefinition(bo, find); //then QueryAssert.assertThat(countQueryDefinition).isNull(); } @Test public void getCountQueryDefinition_should_return_related_query_for_multiple_custom_queries() throws Exception { //given final BusinessObject bo = buildBusinessObject(); bo.addQuery("firstMultiCustomQuery", "", List.class.getName()); Query secondMultiCustomQuery = bo.addQuery("secondMultiCustomQuery", "", List.class.getName()); bo.addQuery("thirdMultiCustomQuery", "", List.class.getName()); bo.addQuery("countForFirstMultiCustomQuery", "", Long.class.getName()); Query countForSecondMultiCustomQuery = bo.addQuery("countForSecondMultiCustomQuery", "", Long.class.getName()); bo.addQuery("countForThirdMultiCustomQuery", "", Long.class.getName()); //when Query countQueryDefinition = provider.getCountQueryDefinition(bo, secondMultiCustomQuery); //then QueryAssert.assertThat(countQueryDefinition).isNotNull(); QueryAssert.assertThat(countQueryDefinition).hasName(countForSecondMultiCustomQuery.getName()); } @Test public void getCountQueryDefinition_should_return_null_when_count_query_does_not_return_a_long() throws Exception { //given final BusinessObject bo = buildBusinessObject(); Query secondMultiCustomQuery = bo.addQuery("secondMultiCustomQuery", "", List.class.getName()); bo.addQuery("countForSecondMultiCustomQuery", "", QUALIFIED_NAME); //when Query countQueryDefinition = provider.getCountQueryDefinition(bo, secondMultiCustomQuery); //then QueryAssert.assertThat(countQueryDefinition).isNull(); } @Test public void getCountQueryDefinition_should_return_null_when_base_query_returns_single_result() throws Exception { //given final BusinessObject bo = buildBusinessObject(); Query secondMultiCustomQuery = bo.addQuery("secondMultiCustomQuery", "", QUALIFIED_NAME); bo.addQuery("countForSecondMultiCustomQuery", "", Long.class.getName()); //when Query countQueryDefinition = provider.getCountQueryDefinition(bo, secondMultiCustomQuery); //then QueryAssert.assertThat(countQueryDefinition).isNull(); } private BusinessObject buildBusinessObject() { final BusinessObject bo = new BusinessObject(); bo.setQualifiedName(QUALIFIED_NAME); final SimpleField field = new SimpleField(); field.setName("people"); field.setType(FieldType.INTEGER); bo.addField(field); bo.addUniqueConstraint("someName", "people"); return bo; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/DataRetentionBdmTrackingServiceImplTest.java ================================================ /** * Copyright (C) 2026 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.business.data.impl; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import org.bonitasoft.engine.business.data.DataRetentionBdmTrackingRepository; import org.bonitasoft.engine.business.data.SDataRetentionBdmTrackingException; import org.bonitasoft.engine.business.data.model.SDataRetentionBdmTracking; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.services.SPersistenceException; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) class DataRetentionBdmTrackingServiceImplTest { @Mock private DataRetentionBdmTrackingRepository bdmTrackingRepository; @InjectMocks private DataRetentionBdmTrackingServiceImpl service; @Test void create_should_insert_tracking_record() throws Exception { //when service.create(42L, "com.example.Invoice"); //then var captor = ArgumentCaptor.forClass(SDataRetentionBdmTracking.class); verify(bdmTrackingRepository).create(captor.capture()); var tracking = captor.getValue(); assertThat(tracking.getDataId()).isEqualTo(42L); assertThat(tracking.getDataClassname()).isEqualTo("com.example.Invoice"); assertThat(tracking.getCreatedAt()).isPositive(); assertThat(tracking.getLastModifiedAt()).isEqualTo(tracking.getCreatedAt()); } @Test void create_should_wrap_persistence_exception() throws Exception { //given doThrow(new SPersistenceException("DB error")).when(bdmTrackingRepository).create(any()); //when-then assertThatThrownBy(() -> service.create(1L, "com.example.Invoice")) .isInstanceOf(SDataRetentionBdmTrackingException.class) .hasMessageContaining("Failed to create BDM tracking record") .hasCauseInstanceOf(SPersistenceException.class); } @Test void upsert_should_update_existing_record() throws Exception { //given var existing = SDataRetentionBdmTracking.builder() .id(1L).dataId(42L).dataClassname("com.example.Invoice") .createdAt(1000L).lastModifiedAt(1000L).build(); when(bdmTrackingRepository.getByDataIdAndClassname(42L, "com.example.Invoice")) .thenReturn(existing); //when service.upsert(42L, "com.example.Invoice"); //then verify(bdmTrackingRepository).updateLastModifiedDate(existing); assertThat(existing.getLastModifiedAt()).isGreaterThan(1000L); assertThat(existing.getCreatedAt()).isEqualTo(1000L); // unchanged } @Test void upsert_should_create_record_when_missing() throws Exception { //given when(bdmTrackingRepository.getByDataIdAndClassname(42L, "com.example.Invoice")) .thenReturn(null); //when service.upsert(42L, "com.example.Invoice"); //then var captor = ArgumentCaptor.forClass(SDataRetentionBdmTracking.class); verify(bdmTrackingRepository).create(captor.capture()); var tracking = captor.getValue(); assertThat(tracking.getDataId()).isEqualTo(42L); assertThat(tracking.getDataClassname()).isEqualTo("com.example.Invoice"); } @Test void upsert_should_wrap_read_exception() throws Exception { //given when(bdmTrackingRepository.getByDataIdAndClassname(42L, "com.example.Invoice")) .thenThrow(new SBonitaReadException("DB error")); //when-then assertThatThrownBy(() -> service.upsert(42L, "com.example.Invoice")) .isInstanceOf(SDataRetentionBdmTrackingException.class) .hasMessageContaining("Failed to upsert data retention tracking record") .hasCauseInstanceOf(SBonitaReadException.class); } @Test void updateLastModifiedDate_should_delegate_to_repository() throws Exception { //when service.updateLastModifiedDate(5L); //then var captor = ArgumentCaptor.forClass(SDataRetentionBdmTracking.class); verify(bdmTrackingRepository).updateLastModifiedDate(captor.capture()); var tracking = captor.getValue(); assertThat(tracking.getId()).isEqualTo(5L); assertThat(tracking.getLastModifiedAt()).isPositive(); } @Test void updateLastModifiedDate_should_wrap_persistence_exception() throws Exception { //given doThrow(new SPersistenceException("DB error")).when(bdmTrackingRepository).updateLastModifiedDate(any()); //when-then assertThatThrownBy(() -> service.updateLastModifiedDate(5L)) .isInstanceOf(SDataRetentionBdmTrackingException.class) .hasMessageContaining("Failed to update BDM tracking record") .hasCauseInstanceOf(SPersistenceException.class); } @Test void delete_should_delegate_to_repository() throws Exception { //given when(bdmTrackingRepository.delete(42L, "com.example.Invoice")).thenReturn(1); //when service.delete(42L, "com.example.Invoice"); //then verify(bdmTrackingRepository).delete(42L, "com.example.Invoice"); } @Test void delete_should_not_fail_when_no_record_found() throws Exception { //given when(bdmTrackingRepository.delete(42L, "com.example.Invoice")).thenReturn(0); //when-then — should not throw assertThatNoException().isThrownBy(() -> service.delete(42L, "com.example.Invoice")); verify(bdmTrackingRepository).delete(42L, "com.example.Invoice"); } @Test void delete_should_wrap_persistence_exception() throws Exception { //given when(bdmTrackingRepository.delete(42L, "com.example.Invoice")) .thenThrow(new SPersistenceException("DB error")); //when-then assertThatThrownBy(() -> service.delete(42L, "com.example.Invoice")) .isInstanceOf(SDataRetentionBdmTrackingException.class) .hasMessageContaining("Failed to delete data retention tracking record") .hasCauseInstanceOf(SPersistenceException.class); } @Test void deleteAll_should_delegate_to_repository() throws Exception { //when service.deleteAll(); //then verify(bdmTrackingRepository).deleteAll(any()); } @Test void deleteAll_should_wrap_persistence_exception() throws Exception { //given doThrow(new SPersistenceException("DB error")).when(bdmTrackingRepository).deleteAll(any()); //when-then assertThatThrownBy(() -> service.deleteAll()) .isInstanceOf(SDataRetentionBdmTrackingException.class) .hasMessageContaining("Failed to delete all BDM tracking records") .hasCauseInstanceOf(SPersistenceException.class); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/EntityPojo.java ================================================ /** * 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.business.data.impl; import java.io.Serial; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import org.bonitasoft.engine.bdm.Entity; @NamedQueries({ @NamedQuery(name = "EntityPojo.findByFirstName", query = "SELECT e\nFROM Employee e\nWHERE e.firstName= :firstName\nORDER BY e.persistenceId"), @NamedQuery(name = "EntityPojo.find", query = "SELECT e\nFROM Employee e\nORDER BY e.persistenceId"), @NamedQuery(name = "EntityPojo.countForFind", query = "SELECT count(e)\nFROM Employee e\n") }) public class EntityPojo implements Entity { @Serial private static final long serialVersionUID = 1L; private String name; private Boolean bool; private Date date; private List numbers; @OneToOne(cascade = CascadeType.MERGE) private Entity aggregationEntity; @OneToOne(cascade = CascadeType.ALL) private Entity compositionEntity; @OneToOne(cascade = CascadeType.ALL) private Entity nullChildEntity; @OneToMany(cascade = CascadeType.MERGE) private List aggregationEntities; @OneToMany(cascade = CascadeType.ALL) private List compositionEntities; private Long persistenceId; public EntityPojo(final Long persistenceId) { this.persistenceId = persistenceId; aggregationEntities = new ArrayList<>(); compositionEntities = new ArrayList<>(); } public EntityPojo() { aggregationEntities = new ArrayList<>(); compositionEntities = new ArrayList<>(); } @Override public Long getPersistenceId() { return persistenceId; } public void setPersistenceId(final Long persistenceId) { this.persistenceId = persistenceId; } @Override public Long getPersistenceVersion() { return 2L; } public Date getDate() { return date; } public void setDate(final Date date) { this.date = date; } public Boolean getBool() { return bool; } public void setBool(final Boolean bool) { this.bool = bool; } public String getName() { return name; } public void setName(final String name) { this.name = name; } public List getNumbers() { return numbers; } public void setNumbers(final List numbers) { this.numbers = numbers; } public Entity getCompositionEntity() { return compositionEntity; } public void setCompositionEntity(final Entity compositionEntity) { this.compositionEntity = compositionEntity; } public Entity getAggregationEntity() { return aggregationEntity; } public void setAggregationEntity(final Entity aggregationEntity) { this.aggregationEntity = aggregationEntity; } public Entity getNullChildEntity() { return null; } public void setNullChildEntity(final Entity nullChildEntity) { this.nullChildEntity = nullChildEntity; } public List getAggregationEntities() { return aggregationEntities; } public void setAggregationEntities(List aggregationEntities) { this.aggregationEntities = aggregationEntities; } public List getCompositionEntities() { return compositionEntities; } public void setCompositionEntities(List compositionEntities) { this.compositionEntities = compositionEntities; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/EntitySerializerPojo.java ================================================ /** * 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.business.data.impl; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.persistence.Column; import javax.persistence.Convert; import javax.persistence.ElementCollection; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Lob; import javax.persistence.OrderColumn; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Version; import org.apache.commons.beanutils.converters.DateTimeConverter; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.business.data.generator.DateConverter; import org.bonitasoft.engine.business.data.generator.OffsetDateTimeConverter; public class EntitySerializerPojo implements Entity { @Id @GeneratedValue private Long persistenceId; @Version private Long persistenceVersion; @Column(name = "ASTRING", nullable = true, length = 255) private String aString; @Column(name = "ABOOLEAN", nullable = true) private Boolean aBoolean; @Column(name = "ADATE", nullable = true) @Temporal(TemporalType.TIMESTAMP) private Date aDate; @Column(name = "ADOUBLE", nullable = true) private Double aDouble; @Column(name = "AFLOAT", nullable = true) private Float aFloat; @Column(name = "AINTEGER", nullable = true) private Integer aInteger; @Column(name = "ALONG", nullable = true) private Long aLong; @Column(name = "ATEXT", nullable = true) @Lob private String aText; @ElementCollection(fetch = FetchType.EAGER) @OrderColumn @Column(name = "MANYLONG", nullable = true) private List manyLong = new ArrayList(10); @ElementCollection(fetch = FetchType.EAGER) @OrderColumn @Column(name = "MANYSTRING", nullable = true, length = 255) private List manyString = new ArrayList(10); @Convert(converter = DateConverter.class) @Column(name = "LOCALDATE", nullable = true, length = 10) private LocalDate aLocalDate; @Convert(converter = DateTimeConverter.class) @Column(name = "LOCALDATETIME", nullable = true, length = 30) private LocalDateTime aLocalDateTime; @Convert(converter = OffsetDateTimeConverter.class) @Column(name = "OFFSETDATETIME", nullable = true, length = 40) private OffsetDateTime anOffsetDateTime; public EntitySerializerPojo() { } public OffsetDateTime getAnOffsetDateTime() { return anOffsetDateTime; } public void setAnOffsetDateTime(OffsetDateTime anOffsetDateTime) { this.anOffsetDateTime = anOffsetDateTime; } public void setPersistenceId(Long persistenceId) { this.persistenceId = persistenceId; } public Long getPersistenceId() { return persistenceId; } public void setPersistenceVersion(Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } public Long getPersistenceVersion() { return persistenceVersion; } public void setAString(String aString) { this.aString = aString; } public String getAString() { return aString; } public void setABoolean(Boolean aBoolean) { this.aBoolean = aBoolean; } public Boolean isABoolean() { return aBoolean; } public void setADate(Date aDate) { this.aDate = aDate; } public LocalDate getALocalDate() { return aLocalDate; } public void setALocalDate(LocalDate localDate) { this.aLocalDate = localDate; } public Date getADate() { return aDate; } public void setADouble(Double aDouble) { this.aDouble = aDouble; } public Double getADouble() { return aDouble; } public void setAFloat(Float aFloat) { this.aFloat = aFloat; } public Float getAFloat() { return aFloat; } public void setAInteger(Integer aInteger) { this.aInteger = aInteger; } public Integer getAInteger() { return aInteger; } public void setALong(Long aLong) { this.aLong = aLong; } public Long getALong() { return aLong; } public void setAText(String aText) { this.aText = aText; } public String getAText() { return aText; } public void setManyLong(List manyLong) { if (this.manyLong == null) { this.manyLong = manyLong; } else { this.manyLong.clear(); this.manyLong.addAll(manyLong); } } public List getManyLong() { return manyLong; } public void addToManyLong(Long addTo) { List manyLong = getManyLong(); manyLong.add(addTo); } public void removeFromManyLong(Long removeFrom) { List manyLong = getManyLong(); manyLong.remove(removeFrom); } public void setManyString(List manyString) { if (this.manyString == null) { this.manyString = manyString; } else { this.manyString.clear(); this.manyString.addAll(manyString); } } public List getManyString() { return manyString; } public void addToManyString(String addTo) { List manyString = getManyString(); manyString.add(addTo); } public void removeFromManyString(String removeFrom) { List manyString = getManyString(); manyString.remove(removeFrom); } public LocalDateTime getALocalDateTime() { return aLocalDateTime; } public void setALocalDateTime(LocalDateTime aLocalDateTime) { this.aLocalDateTime = aLocalDateTime; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof EntitySerializerPojo that)) return false; return new EqualsBuilder() .append(getPersistenceId(), that.getPersistenceId()) .append(getPersistenceVersion(), that.getPersistenceVersion()) .append(aString, that.aString) .append(aBoolean, that.aBoolean) .append(aDate, that.aDate) .append(aDouble, that.aDouble) .append(aFloat, that.aFloat) .append(aInteger, that.aInteger) .append(aLong, that.aLong) .append(aText, that.aText) .append(getManyLong(), that.getManyLong()) .append(getManyString(), that.getManyString()) .append(aLocalDate, that.aLocalDate) .append(aLocalDateTime, that.aLocalDateTime) .append(getAnOffsetDateTime(), that.getAnOffsetDateTime()) .isEquals(); } @Override public int hashCode() { return new HashCodeBuilder(17, 37) .append(getPersistenceId()) .append(getPersistenceVersion()) .append(aString) .append(aBoolean) .append(aDate) .append(aDouble) .append(aFloat) .append(aInteger) .append(aLong) .append(aText) .append(getManyLong()) .append(getManyString()) .append(aLocalDate) .append(aLocalDateTime) .append(getAnOffsetDateTime()) .toHashCode(); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/FakeHibernateProxyEntity.java ================================================ /** * 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.business.data.impl; import org.bonitasoft.engine.bdm.Entity; import org.hibernate.proxy.AbstractLazyInitializer; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; /** * author Emmanuel Duchastenier */ public class FakeHibernateProxyEntity implements HibernateProxy, Entity { @Override public Object writeReplace() { return null; } @Override public LazyInitializer getHibernateLazyInitializer() { return new AbstractLazyInitializer() { @Override public Class getPersistentClass() { return EntityPojo.class; } }; } @Override public Long getPersistenceId() { return null; } @Override public Long getPersistenceVersion() { return null; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/HibernateProxySerializationTest.java ================================================ /** * Copyright (C) 2026 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.business.data.impl; import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; import static org.assertj.core.api.Assertions.assertThatNoException; import com.company.model.Address; import org.bonitasoft.engine.bdm.Entity; import org.hibernate.proxy.HibernateProxy; import org.junit.Test; /** * Verifies JSON serialization behavior for BDM entities when Hibernate returns * {@link HibernateProxy} instances for relationships or top-level entities. *

      * Initialized proxies are unwrapped so their fields serialize as if they were real entities, * while uninitialized proxies remain opaque to avoid triggering lazy loading. */ public class HibernateProxySerializationTest { private static final String PARAMETER_BUSINESSDATA_CLASS_URI_VALUE = "/businessdata/{className}/{id}/{field}"; private final JsonBusinessDataSerializerImpl serializer = new JsonBusinessDataSerializerImpl(); /** * An initialized {@link HibernateProxy} child entity serializes with all its fields. */ @Test public void serializeEntity_should_include_child_fields_when_child_is_hibernate_proxy() throws Exception { // given Address realAddress = createAddress(123L, "Rue Gustave Eiffel", 32f); AddressHibernateProxy proxyAddress = new AddressHibernateProxy(realAddress); PersonWithProxyAddress person = new PersonWithProxyAddress(1L, "John Doe"); person.setAddress(proxyAddress); // when String json = serializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); // then - all fields should be present, including child entity fields assertThatJson(json).node("persistenceId").isEqualTo(1); assertThatJson(json).node("name").isEqualTo("John Doe"); assertThatJson(json).node("address.persistenceId").isEqualTo(123); assertThatJson(json).node("address.street").isEqualTo("Rue Gustave Eiffel"); assertThatJson(json).node("address.number").isEqualTo(32.0); } /** * A non-proxy child entity serializes with all its fields. */ @Test public void serializeEntity_should_include_all_fields_when_child_is_not_proxy() throws Exception { // given Address realAddress = createAddress(123L, "Rue Gustave Eiffel", 32f); PersonWithProxyAddress person = new PersonWithProxyAddress(1L, "John Doe"); person.setAddress(realAddress); // when String json = serializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); // then - all fields should be present assertThatJson(json).node("persistenceId").isEqualTo(1); assertThatJson(json).node("name").isEqualTo("John Doe"); assertThatJson(json).node("address.persistenceId").isEqualTo(123); assertThatJson(json).node("address.street").isEqualTo("Rue Gustave Eiffel"); assertThatJson(json).node("address.number").isEqualTo(32.0); } /** * Manually unwrapping a {@link HibernateProxy} before serialization produces the * same result as letting the serializer unwrap it. */ @Test public void serializeEntity_should_include_all_fields_when_proxy_is_manually_unwrapped() throws Exception { // given Address realAddress = createAddress(123L, "Rue Gustave Eiffel", 32f); AddressHibernateProxy proxyAddress = new AddressHibernateProxy(realAddress); // Unwrap the proxy manually Entity unwrappedAddress = unwrapProxy(proxyAddress); PersonWithProxyAddress person = new PersonWithProxyAddress(1L, "John Doe"); person.setAddress(unwrappedAddress); // when String json = serializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); // then - all fields should be present when proxy is unwrapped assertThatJson(json).node("persistenceId").isEqualTo(1); assertThatJson(json).node("name").isEqualTo("John Doe"); assertThatJson(json).node("address.persistenceId").isEqualTo(123); assertThatJson(json).node("address.street").isEqualTo("Rue Gustave Eiffel"); assertThatJson(json).node("address.number").isEqualTo(32.0); } /** * A top-level {@link HibernateProxy} entity serializes with all its fields. */ @Test public void serializeEntity_should_include_all_fields_when_entity_is_hibernate_proxy() throws Exception { // given Address realAddress = createAddress(123L, "Rue Gustave Eiffel", 32f); AddressHibernateProxy proxyAddress = new AddressHibernateProxy(realAddress); // when String json = serializer.serializeEntity(proxyAddress, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); // then - all fields should be present assertThatJson(json).node("persistenceId").isEqualTo(123); assertThatJson(json).node("persistenceVersion").isEqualTo(1); assertThatJson(json).node("street").isEqualTo("Rue Gustave Eiffel"); assertThatJson(json).node("number").isEqualTo(32.0); } @Test public void serializeEntity_should_not_unwrap_lazy_uninitialized_proxy() throws Exception { Address realAddress = createAddress(123L, "Rue Gustave Eiffel", 32f); AddressHibernateProxy lazyProxy = new AddressHibernateProxy(realAddress, false); PersonWithProxyAddress person = new PersonWithProxyAddress(1L, "John Doe"); person.setAddress(lazyProxy); String json = serializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); assertThatJson(json).node("persistenceId").isEqualTo(1); assertThatJson(json).node("name").isEqualTo("John Doe"); // LAZY proxy should NOT have its fields unwrapped assertThatJson(json).node("address").isObject(); assertThatJson(json).node("address.street").isAbsent(); } @Test public void serializeEntity_should_not_throw_LazyInitializationException_for_initialized_proxy() throws Exception { Address realAddress = createAddress(123L, "Rue Gustave Eiffel", 32f); // initialized=true: proxy delegates to real entity without LazyInitializationException AddressHibernateProxy proxyAddress = new AddressHibernateProxy(realAddress, true); PersonWithProxyAddress person = new PersonWithProxyAddress(1L, "John Doe"); person.setAddress(proxyAddress); // should NOT throw LazyInitializationException assertThatNoException() .isThrownBy(() -> serializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE)); } @Test public void serializeEntity_should_serialize_cleanly_when_optional_child_is_null() throws Exception { PersonWithProxyAddress person = new PersonWithProxyAddress(1L, "John Doe"); person.setAddress(null); String json = serializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); assertThatJson(json).node("persistenceId").isEqualTo(1); assertThatJson(json).node("name").isEqualTo("John Doe"); } @Test public void serializeEntity_should_generate_links_for_json_ignore_field_on_proxy_child() throws Exception { Address realAddress = createAddress(123L, "Rue Gustave Eiffel", 32f); realAddress.setDoorCode("1234A"); AddressHibernateProxy proxyAddress = new AddressHibernateProxy(realAddress); PersonWithProxyAddress person = new PersonWithProxyAddress(1L, "John Doe"); person.setAddress(proxyAddress); String json = serializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); // doorCode should NOT be a direct field (it's @JsonIgnore) assertThatJson(json).node("address.doorCode").isAbsent(); // But address should have links for the ignored field assertThatJson(json).node("address.links").isArray().isNotEmpty(); } @Test public void serializeEntity_should_serialize_collection_fields_on_proxy_entity() throws Exception { Address realAddress = createAddress(123L, "Rue Gustave Eiffel", 32f); realAddress.setFloors(java.util.Arrays.asList(0.0, 1.0, 4.0)); AddressHibernateProxy proxyAddress = new AddressHibernateProxy(realAddress); String json = serializer.serializeEntity(proxyAddress, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); assertThatJson(json).node("street").isEqualTo("Rue Gustave Eiffel"); assertThatJson(json).node("floors").isArray().hasSize(3); } @Test public void serializeEntities_should_include_all_fields_when_child_is_proxy() throws Exception { Address realAddress = createAddress(123L, "Rue Gustave Eiffel", 32f); AddressHibernateProxy proxyAddress = new AddressHibernateProxy(realAddress); PersonWithProxyAddress person = new PersonWithProxyAddress(1L, "John Doe"); person.setAddress(proxyAddress); String json = serializer.serializeEntities( java.util.Collections.singletonList(person), PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); assertThatJson(json).isArray().hasSize(1); assertThatJson(json).node("[0].address.street").isEqualTo("Rue Gustave Eiffel"); assertThatJson(json).node("[0].address.number").isEqualTo(32.0); } private static Address createAddress(Long persistenceId, String street, Float number) { Address address = new Address(); address.setPersistenceId(persistenceId); address.setPersistenceVersion(1L); address.setStreet(street); address.setNumber(number); return address; } private static Entity unwrapProxy(Entity entity) { if (entity instanceof HibernateProxy) { HibernateProxy proxy = (HibernateProxy) entity; return (Entity) proxy.getHibernateLazyInitializer().getImplementation(); } return entity; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/JPABusinessDataRepositoryImplIT.java ================================================ /** * 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.business.data.impl; import static com.company.pojo.EmployeeBuilder.anEmployee; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.sql.DataSource; import javax.transaction.UserTransaction; import com.company.pojo.Employee; import com.company.pojo.Person; import org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService; import org.bonitasoft.engine.business.data.JpaTestConfiguration; import org.bonitasoft.engine.business.data.NonUniqueResultException; import org.bonitasoft.engine.business.data.SBusinessDataNotFoundException; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.dependency.impl.TenantDependencyService; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.resources.TenantResourcesService; import org.bonitasoft.engine.transaction.UserTransactionService; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) public class JPABusinessDataRepositoryImplIT { private JPABusinessDataRepositoryImpl businessDataRepository; private UserTransactionService transactionService; @Autowired @Qualifier("businessDataDataSource") private DataSource datasource; @Autowired @Qualifier("notManagedBizDataSource") private DataSource modelDatasource; @Autowired @Qualifier("jpa-test-configuration") private JpaTestConfiguration configuration; private JdbcTemplate jdbcTemplate; private UserTransaction ut; private ClassLoaderService classLoaderService = mock(ClassLoaderService.class); @Before public void setUp() throws Exception { initMocks(this); if (jdbcTemplate == null) { jdbcTemplate = new JdbcTemplate(datasource); } transactionService = mock(UserTransactionService.class); final SchemaManagerUpdate schemaManager = new SchemaManagerUpdate(configuration.getJpaModelConfiguration()); final BusinessDataModelRepositoryImpl businessDataModelRepositoryImpl = spy( new BusinessDataModelRepositoryImpl(mock(PlatformService.class), mock(TenantDependencyService.class), classLoaderService, schemaManager, mock(TenantResourcesService.class), mock(DataRetentionBdmTrackingService.class))); businessDataRepository = spy( new JPABusinessDataRepositoryImpl(transactionService, businessDataModelRepositoryImpl, configuration.getJpaConfiguration(), classLoaderService, mock(DataRetentionBdmTrackingService.class))); doReturn(true).when(businessDataModelRepositoryImpl).isBDMDeployed(); ut = com.arjuna.ats.jta.UserTransaction.userTransaction(); ut.begin(); final Set classNames = new HashSet<>(); classNames.add(Employee.class.getName()); classNames.add(Person.class.getName()); businessDataModelRepositoryImpl.update(classNames); businessDataRepository.start(); } @After public void tearDown() throws Exception { ut.rollback(); businessDataRepository.stop(); final JdbcTemplate jdbcTemplate = new JdbcTemplate(modelDatasource); for (final String tableName : Arrays.asList("Person_nickNames", "Employee", "PERSON")) { try { jdbcTemplate.update("drop table " + tableName); } catch (final Exception e) { System.out.println(e.getMessage()); // ignore drop of non-existing table } } } private Employee addEmployeeToRepository(final Employee employee) { businessDataRepository.persist(employee); return employee; } @Test(expected = SBusinessDataNotFoundException.class) public void throwAnExceptionIfTheIdentifierIsNull() throws Exception { businessDataRepository.findById(Employee.class, null); } @Test public void findAnEmployeeByPrimaryKey() throws Exception { Employee expectedEmployee = anEmployee().build(); expectedEmployee = addEmployeeToRepository(expectedEmployee); final Employee employee = businessDataRepository.findById(Employee.class, expectedEmployee.getPersistenceId()); assertThat(employee).isEqualTo(expectedEmployee); } @Test(expected = SBusinessDataNotFoundException.class) public void throwExceptionWhenEmployeeNotFound() throws Exception { businessDataRepository.findById(Employee.class, -145l); } @Test public void persistNewEmployeeShouldAddEmployeeInRepository() throws Exception { final Employee employee = anEmployee().build(); businessDataRepository.persist(employee); final Employee myEmployee = businessDataRepository.findById(Employee.class, employee.getPersistenceId()); assertThat(myEmployee).isEqualTo(employee); } @Test public void persistANullEmployeeShouldDoNothing() throws Exception { businessDataRepository.persist(null); final Long count = businessDataRepository.find(Long.class, "SELECT COUNT(*) FROM Employee e", null); assertThat(count).isEqualTo(0); } @Test public void findListShouldAcceptParameterizedQuery() throws Exception { final String firstName = "anyName"; Employee expectedEmployee = anEmployee().withFirstName(firstName).build(); expectedEmployee = addEmployeeToRepository(expectedEmployee); final Map parameters = Collections.singletonMap("firstName", (Serializable) firstName); final Employee matti = businessDataRepository.find(Employee.class, "FROM Employee e WHERE e.firstName = :firstName", parameters); assertThat(matti).isEqualTo(expectedEmployee); } @Test(expected = NonUniqueResultException.class) public void findShouldThrowExceptionWhenSeveralResultsMatch() throws Exception { final String lastName = "Kangaroo"; addEmployeeToRepository(anEmployee().withLastName(lastName).build()); addEmployeeToRepository(anEmployee().withLastName(lastName).build()); final Map parameters = Collections.singletonMap("lastName", (Serializable) lastName); businessDataRepository.find(Employee.class, "FROM Employee e WHERE e.lastName = :lastName", parameters); } @Test public void should_get_employees_by_id() throws Exception { final String lastName = "Kangaroo"; Employee emp1 = addEmployeeToRepository(anEmployee().withLastName(lastName).build()); Employee emp2 = addEmployeeToRepository(anEmployee().withLastName(lastName).build()); Employee emp3 = addEmployeeToRepository(anEmployee().withLastName(lastName).build()); List emps = businessDataRepository.findByIds(Employee.class, Arrays.asList(emp1.getPersistenceId(), emp2.getPersistenceId())); assertThat(emps).contains(emp1, emp2); assertThat(emps).doesNotContain(emp3); } @Test public void should_return_an_empty_list_when_getting_entities_with_empty_ids_list() throws Exception { ArrayList emptyIdsList = new ArrayList<>(); List emps = businessDataRepository.findByIds(Employee.class, emptyIdsList); assertThat(emps).isEmpty(); } @Test public void returnNullnWhenFindingAnUnknownEmployee() throws Exception { final Map parameters = Collections.singletonMap("lastName", (Serializable) "Unknown_lastName"); assertThat( businessDataRepository.find(Employee.class, "FROM Employee e WHERE e.lastName = :lastName", parameters)) .isNull(); } @Test(expected = IllegalStateException.class) public void throwExceptionWhenUsingBDRWihtoutStartingIt() throws Exception { businessDataRepository.stop(); businessDataRepository.findById(Employee.class, 124L); businessDataRepository.start(); } @Test public void entityClassNames_is_an_empty_set_if_bdr_is_not_started() { businessDataRepository.stop(); final Set classNames = businessDataRepository.getEntityClassNames(); assertThat(classNames).isEmpty(); } @Test public void updateTwoFieldsInSameTransactionShouldModifySameObject() throws Exception { final Employee originalEmployee = addEmployeeToRepository(anEmployee().build()); originalEmployee.setLastName("NewLastName"); originalEmployee.setFirstName("NewFirstName"); final Employee updatedEmployee = businessDataRepository.findById(Employee.class, originalEmployee.getPersistenceId()); assertThat(updatedEmployee).isEqualTo(originalEmployee); } @Test public void getEntityClassNames_should_return_the_classes_managed_by_the_bdr() { final Set classNames = businessDataRepository.getEntityClassNames(); assertThat(classNames).containsExactly(Employee.class.getName(), Person.class.getName()); } @Test(expected = SBusinessDataNotFoundException.class) public void aRemovedEntityShouldNotBeRetrievableAnyLonger() throws SBusinessDataNotFoundException { Employee employee = null; try { employee = addEmployeeToRepository(anEmployee().build()); businessDataRepository.remove(employee); } catch (final Exception e) { fail("Should not fail here"); } if (employee != null) { businessDataRepository.findById(Employee.class, employee.getPersistenceId()); } } @Test public void remove_should_not_throw_an_exception_with_a_null_entity() { businessDataRepository.remove(null); } @Test public void remove_should_not_throw_an_exception_with_an_unknown_entity_without_an_id() { businessDataRepository.remove(anEmployee().build()); } @Test public void remove_should_not_throw_an_exception_with_an_unknown_entity() { final Employee newEmployee = addEmployeeToRepository(anEmployee().build()); businessDataRepository.remove(newEmployee); businessDataRepository.remove(newEmployee); } @Test public void findList_should_return_employee_list() { final Employee e1 = addEmployeeToRepository(anEmployee().withFirstName("Hannu").withLastName("balou").build()); final Employee e2 = addEmployeeToRepository(anEmployee().withFirstName("Aliz").withLastName("akkinen").build()); final Employee e3 = addEmployeeToRepository( anEmployee().withFirstName("Jean-Luc").withLastName("akkinen").build()); final List employees = businessDataRepository.findList(Employee.class, "SELECT e FROM Employee e ORDER BY e.lastName ASC, e.firstName ASC", null, 0, 10); assertThat(employees).containsExactly(e2, e3, e1); } @Test public void findList_should_return_an_empty_list_when_asked_for_no_result() { addEmployeeToRepository(anEmployee().withFirstName("Hannu").withLastName("balou").build()); addEmployeeToRepository(anEmployee().withFirstName("Aliz").withLastName("akkinen").build()); addEmployeeToRepository(anEmployee().withFirstName("Jean-Luc").withLastName("akkinen").build()); final List employees = businessDataRepository.findList(Employee.class, "SELECT e FROM Employee e ORDER BY e.lastName ASC, e.firstName ASC", null, 0, 0); assertThat(employees).isEmpty(); } @Test public void findListShouldReturnEmptyListIfNoResults() { final Map parameters = Collections.singletonMap("firstName", (Serializable) "Jaakko"); final List employees = businessDataRepository.findList(Employee.class, "SELECT e FROM Employee e WHERE e.firstName=:firstName ORDER BY e.lastName, e.firstName", parameters, 0, 10); assertThat(employees).isEmpty(); } @Test(expected = IllegalArgumentException.class) public void findListShouldThrowAnExceptionIfAtLeastOneQueryParameterIsNotSet() { businessDataRepository.findList(Employee.class, "SELECT e FROM Employee e WHERE e.firstName=:firstName ORDER BY e.lastName, e.firstName", null, 0, 10); } @Test public void findBasedOnAMultipleAttributeShouldReturnTheEntity() throws Exception { final Person person = new Person(); person.addTo("John"); person.addTo("James"); person.addTo("Jack"); businessDataRepository.persist(person); final Person actual = businessDataRepository.find(Person.class, "SELECT p FROM Person p WHERE 'James' IN ELEMENTS(p.nickNames)", null); assertThat(actual).isEqualTo(person); actual.getNickNames().remove("James"); final Person actual2 = businessDataRepository.find(Person.class, "SELECT p FROM Person p WHERE 'James' IN ELEMENTS(p.nickNames)", null); assertThat(actual2).isNull(); } @Test public void getEntityManagerAddATransactionSynchroInOrderToCleanTheThreadLocalWhenTheTxIsOver() throws Exception { businessDataRepository.getEntityManager(); verify(transactionService).registerBonitaSynchronization(any(RemoveEntityManagerSynchronization.class)); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/JPABusinessDataRepositoryImplTest.java ================================================ /** * 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.business.data.impl; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; import java.io.Serializable; import java.lang.reflect.Field; import java.util.Collection; import java.util.Collections; import java.util.Map; import javax.persistence.EntityGraph; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.PersistenceException; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Path; import javax.persistence.criteria.Root; import javax.persistence.criteria.Selection; import org.bonitasoft.engine.bdm.Entity; import org.bonitasoft.engine.business.data.BusinessDataModelRepository; import org.bonitasoft.engine.business.data.DataRetentionBdmTrackingService; import org.bonitasoft.engine.business.data.SBusinessDataNotFoundException; import org.bonitasoft.engine.business.data.SDataRetentionBdmTrackingException; import org.bonitasoft.engine.classloader.ClassLoaderIdentifier; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.commons.exceptions.SRetryableException; import org.bonitasoft.engine.transaction.STransactionNotFoundException; import org.bonitasoft.engine.transaction.UserTransactionService; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class JPABusinessDataRepositoryImplTest { private static final long PRIMARY_KEY_1 = 1L; private JPABusinessDataRepositoryImpl repository; @Mock private UserTransactionService transactionService; @Mock private BusinessDataModelRepository businessDataModelRepository; @Mock private Map configuration; @Mock private ClassLoaderService classLoaderService; @Mock private DataRetentionBdmTrackingService bdmTrackingService; @Mock EntityManager manager; private JPABusinessDataRepositoryImpl realJPABusinessDataRepository; @BeforeEach void setUp() { realJPABusinessDataRepository = new JPABusinessDataRepositoryImpl(transactionService, businessDataModelRepository, configuration, classLoaderService, bdmTrackingService); repository = spy( realJPABusinessDataRepository); doReturn(manager).when(repository).getEntityManager(); doReturn(true).when(businessDataModelRepository).isBDMDeployed(); CriteriaBuilder criteriaBuilder = mock(CriteriaBuilder.class); doReturn(criteriaBuilder).when(manager).getCriteriaBuilder(); CriteriaQuery criteriaQuery = mock(CriteriaQuery.class); doReturn(criteriaQuery).when(criteriaBuilder).createQuery(any(Class.class)); doReturn(criteriaQuery).when(criteriaQuery).select(any(Selection.class)); Root root = mock(Root.class); doReturn(root).when(criteriaQuery).from(any(Class.class)); doReturn(mock(Path.class)).when(root).get(anyString()); } @Test void should_constructor_add_listener_on_classloader() { //then verify(classLoaderService).addListener(ClassLoaderIdentifier.TENANT, realJPABusinessDataRepository); } @Test void should_stop_close_entityManagerFactory() { // given EntityManagerFactory entityManagerFactory = mock(EntityManagerFactory.class); doReturn(entityManagerFactory).when(repository).getEntityManagerFactory(); //when repository.stop(); //then verify(entityManagerFactory).close(); } @Test void should_onUpdate_recreate_the_entity_manager_factory() { //given EntityManagerFactory entityManagerFactory = mock(EntityManagerFactory.class); doReturn(entityManagerFactory).when(repository).createEntityManagerFactory(); repository.start(); //when repository.onUpdate(null); //then verify(entityManagerFactory).close(); verify(repository, times(2)).createEntityManagerFactory(); } @Test void onUpdate_should_not_throw_NPE_if_entity_manager_factory_is_null_but_still_recreate_the_factory() { //given EntityManagerFactory entityManagerFactory = mock(EntityManagerFactory.class); doReturn(entityManagerFactory).when(repository).createEntityManagerFactory(); //when // entityManagerFactory == null here repository.onUpdate(mock(ClassLoader.class)); //then verifyNoInteractions(entityManagerFactory); verify(repository, times(1)).createEntityManagerFactory(); } @Test void findById_should_not_detach_entities() throws Exception { final Address address1 = new Address(PRIMARY_KEY_1); doReturn(address1).when(manager).find(Address.class, PRIMARY_KEY_1); final Address result = repository.findById(Address.class, PRIMARY_KEY_1); assertThat(result).isEqualTo(address1); verify(manager, never()).detach(any(Address.class)); } @Test void findById_should_throw_an_exception_when_not_found() { doReturn(null).when(manager).find(Address.class, PRIMARY_KEY_1); assertThatExceptionOfType(SBusinessDataNotFoundException.class) .isThrownBy(() -> repository.findById(Address.class, PRIMARY_KEY_1)); } @Test void findById_should_throw_an_exception_with_a_null_identifier() { assertThatExceptionOfType(SBusinessDataNotFoundException.class) .isThrownBy(() -> repository.findById(Address.class, null)); } @Test void findById_should_throw_retryable_when_persistenceException() { //given doThrow(PersistenceException.class).when(manager).find(Address.class, PRIMARY_KEY_1); //when/then assertThatExceptionOfType(SRetryableException.class) .isThrownBy(() -> repository.findById(Address.class, PRIMARY_KEY_1)); } @Test void findByIds_should_throw_retryable_when_persistenceException() { //given doThrow(PersistenceException.class).when(manager).createQuery(any(CriteriaQuery.class)); //when/then assertThatExceptionOfType(SRetryableException.class) .isThrownBy(() -> repository.findByIds(Address.class, Collections.singletonList(PRIMARY_KEY_1))); } @Test void findByIdentifiers_should_throw_retryable_when_persistenceException() { //given doThrow(PersistenceException.class).when(manager).find(Address.class, PRIMARY_KEY_1); //when/then assertThatExceptionOfType(SRetryableException.class) .isThrownBy( () -> repository.findByIdentifiers(Address.class, Collections.singletonList(PRIMARY_KEY_1))); } @Test void findByNamedQuery_should_throw_retryable_when_persistenceException() { //given TypedQuery typedQuery = mock(TypedQuery.class); doThrow(PersistenceException.class).when(typedQuery).getSingleResult(); doReturn(typedQuery).when(manager).createNamedQuery(anyString(), any(Class.class)); //when/then assertThatExceptionOfType(SRetryableException.class) .isThrownBy(() -> repository.findByNamedQuery("queryName", Address.class, Collections. emptyMap())); } @Test void findListByNamedQuery_should_throw_retryable_when_persistenceException() { //given TypedQuery typedQuery = mock(TypedQuery.class); doThrow(PersistenceException.class).when(typedQuery).getResultList(); doReturn(typedQuery).when(manager).createNamedQuery(anyString(), any(Class.class)); //when/then assertThatExceptionOfType(SRetryableException.class) .isThrownBy(() -> repository.findListByNamedQuery("queryName", Address.class, Collections. emptyMap(), 0, 10)); } @Test void find_should_throw_retryable_when_persistenceException() { //given TypedQuery typedQuery = mock(TypedQuery.class); doThrow(PersistenceException.class).when(typedQuery).getSingleResult(); doReturn(typedQuery).when(manager).createQuery(anyString(), any(Class.class)); //when/then assertThatExceptionOfType(SRetryableException.class) .isThrownBy(() -> repository.find(Address.class, "the query as string", Collections. emptyMap())); } @Test void findList_should_throw_retryable_when_persistenceException() { //given TypedQuery typedQuery = mock(TypedQuery.class); doThrow(PersistenceException.class).when(typedQuery).getResultList(); doReturn(typedQuery).when(manager).createQuery(anyString(), any(Class.class)); //when/then assertThatExceptionOfType(SRetryableException.class) .isThrownBy(() -> repository.findList(Address.class, "the query as string", Collections. emptyMap(), 0, 10)); } @Test void persist_should_throw_retryable_exception_in_case_of_persistenceException() { //given doThrow(PersistenceException.class).when(manager).persist(any(Address.class)); //when/then assertThatExceptionOfType(SRetryableException.class) .isThrownBy(() -> repository.persist(new Address(12))); } @Test void persist_should_track_creation_for_new_entity() throws SBonitaException { //given var entity = new EntityPojo(); // persistenceId == null → new entity doAnswer(invocation -> { ((EntityPojo) invocation.getArgument(0)).setPersistenceId(42L); return null; }).when(manager).persist(entity); //when repository.persist(entity); //then verify(bdmTrackingService).create(42L, EntityPojo.class.getName()); } @Test void persist_should_upsert_tracking_for_existing_entity() throws SBonitaException { //given var entity = new EntityPojo(99L); // persistenceId != null → existing entity //when repository.persist(entity); //then verify(bdmTrackingService).upsert(99L, EntityPojo.class.getName()); verify(bdmTrackingService, never()).create(anyLong(), anyString()); } @Test void merge_should_track_creation_for_new_entity() throws SBonitaException { //given var entity = new EntityPojo(); // persistenceId == null → new entity var mergedEntity = new EntityPojo(55L); when(manager.merge(entity)).thenReturn(mergedEntity); //when repository.merge(entity); //then verify(bdmTrackingService).create(55L, EntityPojo.class.getName()); } @Test void merge_should_upsert_tracking_for_existing_entity() throws SBonitaException { //given var entity = new EntityPojo(10L); // persistenceId != null → existing entity when(manager.merge(entity)).thenReturn(entity); //when repository.merge(entity); //then verify(bdmTrackingService).upsert(10L, EntityPojo.class.getName()); verify(bdmTrackingService, never()).create(anyLong(), anyString()); } @Test void persist_null_entity_should_not_track() throws SBonitaException { //when repository.persist(null); //then verify(manager, never()).persist(any()); verify(bdmTrackingService, never()).create(anyLong(), anyString()); } @Test void merge_null_entity_should_not_track() throws SBonitaException { //when repository.merge(null); //then verify(manager, never()).merge(any()); verify(bdmTrackingService, never()).create(anyLong(), anyString()); } @Test void merge_should_use_original_classname_not_hibernate_proxy() throws SBonitaException { // given — input is a plain EntityPojo, but merge() returns a different type (simulating proxy) var entity = new EntityPojo(); // persistenceId == null → new entity var proxyResult = mock(Entity.class); when(proxyResult.getPersistenceId()).thenReturn(77L); doReturn(proxyResult).when(manager).merge(entity); //when repository.merge(entity); // then — dataClassname should be EntityPojo (from input), not the mock/proxy class verify(bdmTrackingService).create(77L, EntityPojo.class.getName()); } @Test void persist_should_throw_SBonitaRuntimeException_when_tracking_fails() throws SBonitaException { //given var entity = new EntityPojo(); // new entity doAnswer(invocation -> { ((EntityPojo) invocation.getArgument(0)).setPersistenceId(7L); return null; }).when(manager).persist(entity); doThrow(new SDataRetentionBdmTrackingException("DB error")) .when(bdmTrackingService).create(anyLong(), anyString()); //when-then assertThatThrownBy(() -> repository.persist(entity)) .isInstanceOf(SBonitaRuntimeException.class) .hasMessageContaining("Failed to insert data retention tracking record") .hasCauseInstanceOf(SDataRetentionBdmTrackingException.class); } @Test void merge_should_throw_SBonitaRuntimeException_when_tracking_fails() throws SBonitaException { //given var entity = new EntityPojo(); // new entity var mergedEntity = new EntityPojo(8L); when(manager.merge(entity)).thenReturn(mergedEntity); doThrow(new SDataRetentionBdmTrackingException("DB error")) .when(bdmTrackingService).create(anyLong(), anyString()); //when-then assertThatThrownBy(() -> repository.merge(entity)) .isInstanceOf(SBonitaRuntimeException.class) .hasMessageContaining("Failed to insert data retention tracking record") .hasCauseInstanceOf(SDataRetentionBdmTrackingException.class); } @Test void merge_should_throw_retryable_exception_in_case_of_persistenceException() { //given doThrow(PersistenceException.class).when(manager).merge(any(Address.class)); //when/then assertThatExceptionOfType(SRetryableException.class) .isThrownBy(() -> repository.merge(new Address(12))); } @Test void remove_should_throw_retryable_exception_in_case_of_persistenceException() { //given doThrow(PersistenceException.class).when(manager).remove(any(Address.class)); //when/then assertThatExceptionOfType(SRetryableException.class) .isThrownBy(() -> repository.remove(new Address(12))); } @Test void remove_should_delete_tracking_record() throws SBonitaException { //given var entity = new EntityPojo(42L); //when repository.remove(entity); //then verify(manager).remove(entity); verify(bdmTrackingService).delete(42L, EntityPojo.class.getName()); } @Test void remove_null_entity_should_not_delete_tracking_record() throws SBonitaException { //when repository.remove(null); //then verify(manager, never()).remove(any()); verify(bdmTrackingService, never()).delete(anyLong(), anyString()); } @Test void remove_entity_without_id_should_not_delete_tracking_record() throws SBonitaException { //given var entity = new EntityPojo(); // persistenceId == null //when repository.remove(entity); //then verify(manager, never()).remove(any()); verify(bdmTrackingService, never()).delete(anyLong(), anyString()); } @Test void remove_should_not_fail_when_tracking_deletion_fails_with_checked_exception() throws SBonitaException { //given var entity = new EntityPojo(42L); doThrow(new SDataRetentionBdmTrackingException("DB error")) .when(bdmTrackingService).delete(anyLong(), anyString()); //when-then — should not throw assertThatNoException().isThrownBy(() -> repository.remove(entity)); // entity was still removed verify(manager).remove(entity); } @Test void remove_should_not_fail_when_tracking_deletion_fails_with_runtime_exception() throws SBonitaException { //given var entity = new EntityPojo(42L); doThrow(new RuntimeException("unexpected error")) .when(bdmTrackingService).delete(anyLong(), anyString()); //when-then — should not throw assertThatNoException().isThrownBy(() -> repository.remove(entity)); // entity was still removed verify(manager).remove(entity); } @Test void removeById_should_remove_entity_and_delete_tracking_record() throws Exception { //given var entity = new EntityPojo(42L); var entityGraph = mock(EntityGraph.class); doReturn(entityGraph).when(manager).createEntityGraph(EntityPojo.class); var expectedHints = Map. of("javax.persistence.fetchgraph", entityGraph); when(manager.find(EntityPojo.class, 42L, expectedHints)).thenReturn(entity); //when var removed = repository.removeById(EntityPojo.class, 42L); //then assertThat(removed).isSameAs(entity); verify(manager).find(EntityPojo.class, 42L, expectedHints); verify(manager).remove(entity); verify(bdmTrackingService).delete(42L, EntityPojo.class.getName()); } @Test void removeById_should_throw_when_entity_not_found() { //given var entityGraph = mock(EntityGraph.class); doReturn(entityGraph).when(manager).createEntityGraph(EntityPojo.class); when(manager.find(eq(EntityPojo.class), eq(999L), anyMap())).thenReturn(null); //when-then assertThatExceptionOfType(SBusinessDataNotFoundException.class) .isThrownBy(() -> repository.removeById(EntityPojo.class, 999L)); verify(manager, never()).remove(any()); } @Test void removeById_should_throw_retryable_on_persistence_exception() { //given var entityGraph = mock(EntityGraph.class); doReturn(entityGraph).when(manager).createEntityGraph(EntityPojo.class); when(manager.find(eq(EntityPojo.class), eq(42L), anyMap())) .thenThrow(new PersistenceException("db error")); //when-then assertThatExceptionOfType(SRetryableException.class) .isThrownBy(() -> repository.removeById(EntityPojo.class, 42L)); } @Test void removeById_should_not_fail_when_tracking_deletion_fails() throws Exception { //given var entity = new EntityPojo(42L); var entityGraph = mock(EntityGraph.class); doReturn(entityGraph).when(manager).createEntityGraph(EntityPojo.class); when(manager.find(eq(EntityPojo.class), eq(42L), anyMap())).thenReturn(entity); doThrow(new SDataRetentionBdmTrackingException("DB error")) .when(bdmTrackingService).delete(anyLong(), anyString()); //when-then — should not throw assertThatNoException().isThrownBy(() -> repository.removeById(EntityPojo.class, 42L)); verify(manager).remove(entity); } @Test void checkParameterValue_should_transform_query_parameter_values_from_string_array_to_collection() { Object collection = repository .checkParameterValue(new String[] { "v1", "v2" }); assertThat((Collection) collection).contains("v1", "v2"); } @Test void checkParameterValue_should_transform_query_parameter_values_from_int_array_to_collection() { Object collection = repository .checkParameterValue(new Integer[] { 1, 2 }); assertThat((Collection) collection).contains(1, 2); } @Test void checkParameterValue_should_transform_query_parameter_values_from_float_array_to_collection() { Object collection = repository .checkParameterValue(new Float[] { 1.2f, 2.0f }); assertThat((Collection) collection).contains(1.2f, 2.0f); } @Test void checkParameterValue_should_transform_query_parameter_values_from_double_array_to_collection() { Object collection = repository .checkParameterValue(new Double[] { 1.2d, 2.0d }); assertThat((Collection) collection).contains(1.2d, 2.0d); } @Test void checkParameterValue_should_transform_query_parameter_values_from_long_array_to_collection() { Object collection = repository .checkParameterValue(new Long[] { 12l, 23456l }); assertThat((Collection) collection).contains(12l, 23456l); } @Test void checkParameterValue_should_check_supported_query_parameter_types() { assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> repository.checkParameterValue(new Byte[] { 0x1, 0x2 })); } // --- Defensive getEntityManager() tests (BPA-321) --- /** * Helper to inject an EntityManager into the private ThreadLocal "managers" field * and call the real getEntityManager() method (not the spied version). */ private JPABusinessDataRepositoryImpl createRepositoryWithStaleEM(EntityManager staleEM, EntityManager freshEM, EntityManagerFactory emf) throws Exception { JPABusinessDataRepositoryImpl repo = spy( new JPABusinessDataRepositoryImpl(transactionService, businessDataModelRepository, configuration, classLoaderService, bdmTrackingService)); doReturn(emf).when(repo).getEntityManagerFactory(); // Inject the stale EM into the private ThreadLocal Field managersField = JPABusinessDataRepositoryImpl.class.getDeclaredField("managers"); managersField.setAccessible(true); @SuppressWarnings("unchecked") ThreadLocal managers = (ThreadLocal) managersField.get(repo); managers.set(staleEM); // The EMF will produce a fresh EM when asked doReturn(freshEM).when(emf).createEntityManager(); return repo; } @Test void getEntityManager_should_discard_stale_EM_not_joined_to_transaction() throws Exception { //given EntityManager staleEM = mock(EntityManager.class); doReturn(true).when(staleEM).isOpen(); doReturn(false).when(staleEM).isJoinedToTransaction(); EntityManager freshEM = mock(EntityManager.class); EntityManagerFactory emf = mock(EntityManagerFactory.class); JPABusinessDataRepositoryImpl repo = createRepositoryWithStaleEM(staleEM, freshEM, emf); //when EntityManager result = repo.getEntityManager(); //then verify(staleEM).close(); verify(emf).createEntityManager(); verify(freshEM).joinTransaction(); assertThat(result).isSameAs(freshEM); } @Test void getEntityManager_should_discard_stale_EM_that_is_no_longer_open() throws Exception { //given EntityManager staleEM = mock(EntityManager.class); doReturn(false).when(staleEM).isOpen(); EntityManager freshEM = mock(EntityManager.class); EntityManagerFactory emf = mock(EntityManagerFactory.class); JPABusinessDataRepositoryImpl repo = createRepositoryWithStaleEM(staleEM, freshEM, emf); //when EntityManager result = repo.getEntityManager(); //then // staleEM.isOpen() returned false, so close() should NOT be called (already closed) verify(staleEM, never()).close(); verify(emf).createEntityManager(); assertThat(result).isSameAs(freshEM); } @Test void getEntityManager_should_discard_stale_EM_that_throws_on_state_check() throws Exception { //given EntityManager staleEM = mock(EntityManager.class); // Simulates a broken EM where even isOpen() throws (completely broken session) doThrow(new PersistenceException("Session/EntityManager is closed")).when(staleEM).isOpen(); EntityManager freshEM = mock(EntityManager.class); EntityManagerFactory emf = mock(EntityManagerFactory.class); JPABusinessDataRepositoryImpl repo = createRepositoryWithStaleEM(staleEM, freshEM, emf); //when EntityManager result = repo.getEntityManager(); //then // isOpen() threw, so closeQuietly catches the exception and moves on verify(staleEM, never()).close(); verify(emf).createEntityManager(); assertThat(result).isSameAs(freshEM); } @Test void getEntityManager_should_reuse_EM_when_joined_to_current_transaction() throws Exception { //given EntityManager activeEM = mock(EntityManager.class); doReturn(true).when(activeEM).isOpen(); doReturn(true).when(activeEM).isJoinedToTransaction(); EntityManagerFactory emf = mock(EntityManagerFactory.class); JPABusinessDataRepositoryImpl repo = spy( new JPABusinessDataRepositoryImpl(transactionService, businessDataModelRepository, configuration, classLoaderService, bdmTrackingService)); doReturn(emf).when(repo).getEntityManagerFactory(); // Inject the active EM Field managersField = JPABusinessDataRepositoryImpl.class.getDeclaredField("managers"); managersField.setAccessible(true); @SuppressWarnings("unchecked") ThreadLocal managers = (ThreadLocal) managersField.get(repo); managers.set(activeEM); //when EntityManager result = repo.getEntityManager(); //then verify(emf, never()).createEntityManager(); verify(activeEM, never()).close(); verify(activeEM).joinTransaction(); assertThat(result).isSameAs(activeEM); // cleanup ThreadLocal managers.remove(); } @Test void getEntityManager_should_close_new_EM_when_registerBonitaSynchronization_throws() throws Exception { //given EntityManager freshEM = mock(EntityManager.class); doReturn(true).when(freshEM).isOpen(); EntityManagerFactory emf = mock(EntityManagerFactory.class); doReturn(freshEM).when(emf).createEntityManager(); JPABusinessDataRepositoryImpl repo = spy( new JPABusinessDataRepositoryImpl(transactionService, businessDataModelRepository, configuration, classLoaderService, bdmTrackingService)); doReturn(emf).when(repo).getEntityManagerFactory(); doThrow(new STransactionNotFoundException("no active transaction")) .when(transactionService).registerBonitaSynchronization(any()); //when assertThatExceptionOfType(IllegalStateException.class) .isThrownBy(repo::getEntityManager) .withCauseInstanceOf(STransactionNotFoundException.class); //then verify(freshEM).close(); } @Test void getEntityManager_should_cleanup_and_rethrow_when_joinTransaction_fails() throws Exception { //given EntityManager freshEM = mock(EntityManager.class); doReturn(true).when(freshEM).isOpen(); doThrow(new javax.persistence.TransactionRequiredException("TX is rollback-only")) .when(freshEM).joinTransaction(); EntityManagerFactory emf = mock(EntityManagerFactory.class); doReturn(freshEM).when(emf).createEntityManager(); JPABusinessDataRepositoryImpl repo = spy( new JPABusinessDataRepositoryImpl(transactionService, businessDataModelRepository, configuration, classLoaderService, bdmTrackingService)); doReturn(emf).when(repo).getEntityManagerFactory(); //when + then assertThatExceptionOfType(javax.persistence.TransactionRequiredException.class) .isThrownBy(repo::getEntityManager) .withMessageContaining("TX is rollback-only"); verify(freshEM).close(); // Verify ThreadLocal was cleaned up: a second call should create a fresh EM, // not reuse the poisoned one EntityManager secondEM = mock(EntityManager.class); doReturn(secondEM).when(emf).createEntityManager(); doNothing().when(secondEM).joinTransaction(); EntityManager result = repo.getEntityManager(); assertThat(result).isSameAs(secondEM); verify(emf, times(2)).createEntityManager(); } class Address implements Entity { private static final long serialVersionUID = 2603989953326533907L; private final long persistenceId; public Address(final long persistenceId) { this.persistenceId = persistenceId; } @Override public Long getPersistenceId() { return persistenceId; } @Override public Long getPersistenceVersion() { return 0L; } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/JsonBusinessDataSerializerImplTest.java ================================================ /** * 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.business.data.impl; import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; import static org.mockito.Mockito.mock; import java.io.IOException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; import javassist.util.proxy.MethodHandler; import com.company.model.Address; import com.company.model.Person; import com.company.model.PersonWithDetails; import com.company.model.Phone; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.bdm.Entity; import org.junit.Test; public class JsonBusinessDataSerializerImplTest { private static final String PARAMETER_BUSINESSDATA_CLASS_URI_VALUE = "/businessdata/{className}/{id}/{field}"; private final JsonBusinessDataSerializerImpl jsonBusinessDataSerializer = new JsonBusinessDataSerializerImpl(); @Test public void entity_should_be_serialized() throws Exception { // given Entity person = initPerson(1L); // when final String jsonPerson = jsonBusinessDataSerializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); // then assertThatJson(jsonPerson).as("entity serialization").isEqualTo(getJsonContent("singlePerson.json")); } @Test public void count_result_should_be_serialized() throws Exception { // when final String countJson = jsonBusinessDataSerializer.serializeCountResult(Collections.singletonList(59L), Person.class.getName()); // then assertThatJson(countJson).isEqualTo("[59]"); } @Test public void entity_with_nested_entity_fields_should_be_serialized() throws Exception { // given PersonWithDetails person = initPersonWithDetails(666L); // when final String jsonPerson = jsonBusinessDataSerializer.serializeEntity(person, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); // then assertThatJson(jsonPerson).as("entity with nested entity fields serialization") .isEqualTo(getJsonContent("personWithDetails.json")); } @Test public void entity_list_should_be_serialized() throws Exception { // given List persons = IntStream.range(1, 3).mapToObj(i -> initPerson(i)).collect(Collectors.toList()); // when final String jsonPersonList = jsonBusinessDataSerializer.serializeEntities(persons, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); // then assertThatJson(jsonPersonList).as("entity list serialization").isEqualTo(getJsonContent("multiplePerson.json")); } @Test public void serialization_of_entity_should_use_fields_and_not_getters() throws Exception { //given EntitySerializerPojo value = createPojo(); //when final String serializedEntity = jsonBusinessDataSerializer.serializeEntity(value, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE); //then assertThatJson(serializedEntity).as("serialization uses fields instead of getters to compute json properties") .isEqualTo(getJsonContent("EntitySerializerPojo.json")); } @Test public void scalar_result_should_be_serialized_in_legacy_format() throws Exception { // when - Long String longJson = jsonBusinessDataSerializer.serializeScalarResult(Collections.singletonList(59L), Person.class.getName(), false); // then assertThatJson(longJson).isEqualTo("[59]"); // when - Double String doubleJson = jsonBusinessDataSerializer.serializeScalarResult(Collections.singletonList(42.5), Person.class.getName(), false); // then assertThatJson(doubleJson).isEqualTo("[42.5]"); // when - Integer String intJson = jsonBusinessDataSerializer.serializeScalarResult(Collections.singletonList(100), Person.class.getName(), false); // then assertThatJson(intJson).isEqualTo("[100]"); } @Test public void scalar_result_should_be_serialized_in_standard_format() throws Exception { // when - Long String longJson = jsonBusinessDataSerializer.serializeScalarResult(Collections.singletonList(59L), Person.class.getName(), true); // then assertThatJson(longJson).isEqualTo("{ \"value\": 59 }"); // when - Double String doubleJson = jsonBusinessDataSerializer.serializeScalarResult(Collections.singletonList(42.5), Person.class.getName(), true); // then assertThatJson(doubleJson).isEqualTo("{ \"value\": 42.5 }"); // when - null String nullJson = jsonBusinessDataSerializer.serializeScalarResult(Collections.singletonList(null), Person.class.getName(), true); // then assertThatJson(nullJson).isEqualTo("{ \"value\": null }"); } @Test public void entity_query_result_should_be_serialized_as_array_in_legacy_mode() throws Exception { // given - single entity Entity person = initPerson(1L); // when - legacy mode always returns array regardless of queryReturnsMultipleResults final String jsonSingle = jsonBusinessDataSerializer.serializeEntityQueryResult( Collections.singletonList(person), PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, false, false); // then - returns array even for single entity assertThatJson(jsonSingle).isArray(); assertThatJson(jsonSingle).node("[0]").isObject(); } @Test public void entity_query_result_should_be_serialized_as_object_in_standard_mode_for_single_entity_query() throws Exception { // given - single entity from a query designed to return single entity Entity person = initPerson(1L); // when - standard mode with query returning single entity (queryReturnsMultipleResults = false) final String jsonSingle = jsonBusinessDataSerializer.serializeEntityQueryResult( Collections.singletonList(person), PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true, false); // then - returns object (not array) for single entity query assertThatJson(jsonSingle).isObject(); assertThatJson(jsonSingle).node("persistenceId").isEqualTo(1); } @Test public void entity_query_result_should_be_serialized_as_array_in_standard_mode_for_list_query_with_single_result() throws Exception { // given - single entity from a query designed to return List Entity person = initPerson(1L); // when - standard mode with query returning List (queryReturnsMultipleResults = true) final String jsonSingle = jsonBusinessDataSerializer.serializeEntityQueryResult( Collections.singletonList(person), PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true, true); // then - returns array even for single result because query is designed to return List assertThatJson(jsonSingle).isArray(); assertThatJson(jsonSingle).node("[0]").isObject(); assertThatJson(jsonSingle).node("[0].persistenceId").isEqualTo(1); } @Test public void entity_query_result_should_be_serialized_as_array_in_standard_mode_for_multiple_entities() throws Exception { // given - multiple entities List persons = IntStream.range(1, 3).mapToObj(i -> initPerson(i)).collect(Collectors.toList()); // when - standard mode with query returning List (queryReturnsMultipleResults = true) final String jsonMultiple = jsonBusinessDataSerializer.serializeEntityQueryResult(persons, PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true, true); // then - returns array for multiple entities assertThatJson(jsonMultiple).isArray(); assertThatJson(jsonMultiple).node("[0].persistenceId").isEqualTo(1); assertThatJson(jsonMultiple).node("[1].persistenceId").isEqualTo(2); } @Test public void entity_query_result_should_return_empty_object_for_no_results_in_standard_mode_single_entity_query() throws Exception { // when - standard mode with query returning single entity (queryReturnsMultipleResults = false) final String jsonEmpty = jsonBusinessDataSerializer.serializeEntityQueryResult(Collections.emptyList(), PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true, false); // then - returns empty object for single entity query assertThatJson(jsonEmpty).isEqualTo("{}"); } @Test public void entity_query_result_should_return_empty_array_for_no_results_in_standard_mode_list_query() throws Exception { // when - standard mode with query returning List (queryReturnsMultipleResults = true) final String jsonEmpty = jsonBusinessDataSerializer.serializeEntityQueryResult(Collections.emptyList(), PARAMETER_BUSINESSDATA_CLASS_URI_VALUE, true, true); // then - returns empty array for list query assertThatJson(jsonEmpty).isEqualTo("[]"); } // ================================================================================================================= // UTILS // ================================================================================================================= private static EntitySerializerPojo createPojo() { final EntitySerializerPojo entitySerializerPojo = new EntitySerializerPojo(); entitySerializerPojo.setPersistenceId(Long.MAX_VALUE); entitySerializerPojo.setPersistenceVersion(1L); entitySerializerPojo.setABoolean(Boolean.TRUE); entitySerializerPojo.setADate(new Date(123L)); entitySerializerPojo.setALocalDate(LocalDate.of(2017, 3, 6)); entitySerializerPojo.setALocalDateTime(LocalDateTime.of(1945, 5, 8, 12, 31, 17)); entitySerializerPojo .setAnOffsetDateTime(OffsetDateTime.of(LocalDateTime.of(1949, 4, 9, 12, 14, 5), ZoneOffset.ofHours(1))); entitySerializerPojo.setADouble(Double.MAX_VALUE); entitySerializerPojo.setAFloat(Float.MAX_VALUE); entitySerializerPojo.setAInteger(Integer.MAX_VALUE); entitySerializerPojo.setALong(Long.MAX_VALUE); entitySerializerPojo.setAString("string"); entitySerializerPojo.setAText("text"); entitySerializerPojo.addToManyLong(1L); entitySerializerPojo.addToManyLong(2L); entitySerializerPojo.addToManyString("abc"); entitySerializerPojo.addToManyString("def"); return entitySerializerPojo; } private static Person initPerson(long persistenceId) { Person person; person = new Person(); person.setPersistenceId(persistenceId); person.setPersistenceVersion(2L); person.setName("John"); person.setAge(50); person.setBirthday(new Date(123456789L)); person.setHasMobile(Boolean.TRUE); for (int i = 0; i < 3; i++) { Phone ph = new Phone(); ph.setNumber("123456" + i); person.addToPhones(ph); } person.addToBools(true); person.addToBools(false); person.addToBools(true); person.addToBools(false); return person; } private String getJsonContent(String jsonFileName) throws IOException { return new String(IOUtils.toByteArray(this.getClass().getResourceAsStream(jsonFileName))); } private static PersonWithDetails initPersonWithDetails(long persistenceId) { PersonWithDetails person = new PersonWithDetails(); person.setPersistenceId(persistenceId); person.setPersistenceVersion(2L); person.setName("John"); person.setAge(50); person.setBirthday(new Date(123456789L)); person.setHasMobile(Boolean.TRUE); for (int i = 0; i < 3; i++) { Phone ph = new Phone(); ph.setPersistenceVersion(365L + i); ph.setPersistenceId(22365L + i); ph.setNumber("123456" + i); person.addToPhones(ph); } person.addToBools(true); person.addToBools(false); person.addToBools(true); person.addToBools(false); person.addToIgnores(1L); person.addToIgnores(3L); person.addToIgnores(5L); person.addToIgnores(6L); Phone secretPhone = new Phone(); secretPhone.setPersistenceId(3615L); secretPhone.setNumber("999999"); person.setSecretPhone(secretPhone); person.addToIncludes(1235L); person.addToIncludes(6666L); person.addToIncludes(7777L); Address address1 = new Address(); address1.setPersistenceId(2598L); address1.setPersistenceVersion(99992598L); address1.setStreet("Rue Gustave Eiffel"); address1.setNumber(32f); address1.setFloors(Arrays.asList(1d, 4d)); address1.setDoorCode("my-secret-password"); person.setAddress1(address1); Address address2 = new Address(); address2.setPersistenceId(358L); address2.setPersistenceVersion(118L); address2.setDoorCode(null); person.setAddress2(address2); person.setMethodHandlerObject(mock(MethodHandler.class)); person.addToProxysAsMethodHandler("I love Maths"); person.addToProxysAsProxyImpl(365L); person.addToProxysAsProxyObjectImpl(secretPhone); return person; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/PersonWithProxyAddress.java ================================================ /** * 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.business.data.impl; import javax.persistence.CascadeType; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.Version; import org.bonitasoft.engine.bdm.Entity; /** * A parent entity with a @OneToOne child relationship to Address. *

      * This entity is used to reproduce the bug where a HibernateProxy child entity * (address field) is serialized as an empty object {} instead of including its fields. *

      * The address field can hold either a real Address or an AddressHibernateProxy. */ public class PersonWithProxyAddress implements Entity { @Id @GeneratedValue private Long persistenceId; @Version private Long persistenceVersion; private String name; @OneToOne(optional = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL) private Entity address; public PersonWithProxyAddress() { } public PersonWithProxyAddress(Long persistenceId, String name) { this.persistenceId = persistenceId; this.name = name; } @Override public Long getPersistenceId() { return persistenceId; } public void setPersistenceId(Long persistenceId) { this.persistenceId = persistenceId; } @Override public Long getPersistenceVersion() { return persistenceVersion; } public void setPersistenceVersion(Long persistenceVersion) { this.persistenceVersion = persistenceVersion; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Entity getAddress() { return address; } public void setAddress(Entity address) { this.address = address; } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/ProxyCacheManagerTest.java ================================================ /** * Copyright (C) 2016 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.business.data.impl; import static org.assertj.core.api.Assertions.assertThat; import java.util.WeakHashMap; import javassist.util.proxy.ProxyFactory; import com.company.pojo.Employee; import org.bonitasoft.engine.bdm.Entity; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ProxyCacheManagerTest { @Test public void should_retrieve_the_proxyCache_map() throws Exception { final ProxyCacheManager proxyCacheManager = new ProxyCacheManager(); createProxy(new EntityPojo()); createProxy(new EntityPojo()); createProxy(new Employee("John", "Doe")); createProxy(new Employee("Jane", "Doe")); WeakHashMap cache = proxyCacheManager.get(); assertThat(cache).hasSize(1); proxyCacheManager.clearCache(); cache = proxyCacheManager.get(); assertThat(cache).isEmpty(); } private Entity createProxy(Entity entity) { final ProxyFactory factory = new ProxyFactory(); factory.setSuperclass(entity.getClass()); try { return (Entity) factory.create(new Class[0], new Object[0], null); } catch (final Exception e) { throw new RuntimeException("Error when proxifying object", e); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/RemoveEntityManagerSynchronizationTest.java ================================================ /** * 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.business.data.impl; import static javax.transaction.Status.STATUS_COMMITTED; import static javax.transaction.Status.STATUS_ROLLEDBACK; import static javax.transaction.Status.STATUS_UNKNOWN; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import javax.persistence.EntityManager; import javax.persistence.PersistenceException; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; /** * @author Laurent Leseigneur */ @ExtendWith(MockitoExtension.class) class RemoveEntityManagerSynchronizationTest { @Mock EntityManager entityManager; @Test void beforeCompletion_should_not_close_entityManager() { //given ThreadLocal localManager = new ThreadLocal<>(); localManager.set(entityManager); RemoveEntityManagerSynchronization removeEntityManagerSynchronization = new RemoveEntityManagerSynchronization( localManager); //when removeEntityManagerSynchronization.beforeCompletion(); //then verify(entityManager, never()).close(); assertThat(localManager.get()).as("should remove entity manager").isNotNull(); } @Test void afterCompletion_should_close_entityManager_on_STATUS_COMMITTED() { //given ThreadLocal localManager = new ThreadLocal<>(); localManager.set(entityManager); doReturn(true).when(entityManager).isOpen(); RemoveEntityManagerSynchronization sync = new RemoveEntityManagerSynchronization(localManager); //when sync.afterCompletion(STATUS_COMMITTED); //then verify(entityManager).close(); assertThat(localManager.get()).as("should remove entity manager after commit").isNull(); } @Test void afterCompletion_should_close_entityManager_on_STATUS_ROLLEDBACK() { //given ThreadLocal localManager = new ThreadLocal<>(); localManager.set(entityManager); doReturn(true).when(entityManager).isOpen(); RemoveEntityManagerSynchronization sync = new RemoveEntityManagerSynchronization(localManager); //when sync.afterCompletion(STATUS_ROLLEDBACK); //then verify(entityManager).close(); assertThat(localManager.get()).as("should remove entity manager after rollback").isNull(); } @Test void afterCompletion_should_close_entityManager_and_cleanup_threadLocal_on_STATUS_UNKNOWN() { //given ThreadLocal localManager = new ThreadLocal<>(); localManager.set(entityManager); doReturn(true).when(entityManager).isOpen(); RemoveEntityManagerSynchronization sync = new RemoveEntityManagerSynchronization(localManager); //when sync.afterCompletion(STATUS_UNKNOWN); //then verify(entityManager).close(); assertThat(localManager.get()).as("should remove entity manager after heuristic outcome").isNull(); } @Test void afterCompletion_should_not_close_already_closed_entityManager() { //given ThreadLocal localManager = new ThreadLocal<>(); localManager.set(entityManager); doReturn(false).when(entityManager).isOpen(); RemoveEntityManagerSynchronization sync = new RemoveEntityManagerSynchronization(localManager); //when sync.afterCompletion(STATUS_COMMITTED); //then — EM is already closed, so close() should not be called again verify(entityManager, never()).close(); assertThat(localManager.get()).as("should remove entity manager even if already closed").isNull(); } @Test void afterCompletion_should_not_throw_NPE_when_threadLocal_is_null() { //given ThreadLocal localManager = new ThreadLocal<>(); localManager.set(null); RemoveEntityManagerSynchronization sync = new RemoveEntityManagerSynchronization(localManager); //when sync.afterCompletion(STATUS_COMMITTED); //then assertThat(localManager.get()).as("should remove entity manager").isNull(); } @Test void afterCompletion_should_still_remove_threadLocal_when_close_throws() { //given ThreadLocal localManager = new ThreadLocal<>(); localManager.set(entityManager); doReturn(true).when(entityManager).isOpen(); doThrow(new PersistenceException("connection reset")).when(entityManager).close(); RemoveEntityManagerSynchronization sync = new RemoveEntityManagerSynchronization(localManager); //when - then: close() failure should be ignored, and ThreadLocal is still cleaned up (via finally) assertThatNoException().isThrownBy(() -> sync.afterCompletion(STATUS_COMMITTED)); verify(entityManager).close(); assertThat(localManager.get()) .as("ThreadLocal should be cleaned up even when close() throws") .isNull(); } /** * Simulates what happens when Narayana's TransactionReaper calls afterCompletion() * on its own reaper thread instead of the application (worker) thread. * Since RemoveEntityManagerSynchronization uses ThreadLocal.get(), the reaper thread * sees null and the EM is never closed — the worker thread's ThreadLocal retains * the stale EntityManager. * This test documents the current known limitation (BPA-321). */ @Test void afterCompletion_should_not_close_entityManager_when_called_from_different_thread() throws Exception { //given ThreadLocal localManager = new ThreadLocal<>(); // Set the EM on the current (application) thread localManager.set(entityManager); RemoveEntityManagerSynchronization sync = new RemoveEntityManagerSynchronization(localManager); // Simulate: afterCompletion is called on a different thread (the reaper) CountDownLatch reaperDone = new CountDownLatch(1); AtomicReference reaperThreadLocalValue = new AtomicReference<>(); Thread reaperThread = new Thread(() -> { // On the reaper thread, localManager.get() returns null (different thread) reaperThreadLocalValue.set(localManager.get()); sync.afterCompletion(STATUS_ROLLEDBACK); reaperDone.countDown(); }, "Simulated-TransactionReaper"); //when reaperThread.start(); reaperDone.await(5, TimeUnit.SECONDS); //then // The reaper thread's ThreadLocal was null — it could not find the EM assertThat(reaperThreadLocalValue.get()).as("reaper thread should not see the worker's EM").isNull(); // The EM was never closed because the reaper thread couldn't access it verify(entityManager, never()).close(); // The worker thread's ThreadLocal still holds the stale EM (the leak) assertThat(localManager.get()) .as("worker thread's ThreadLocal should still hold the stale EM (known limitation)") .isSameAs(entityManager); // Cleanup: remove from current thread to avoid test pollution localManager.remove(); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/SchemaManagerUpdateIT.java ================================================ /** * 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.business.data.impl; import static org.junit.Assert.fail; import java.util.List; import org.bonitasoft.engine.BOMBuilder; import org.bonitasoft.engine.bdm.model.BusinessObjectModel; import org.bonitasoft.engine.business.data.JpaTestConfiguration; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @ContextConfiguration(locations = { "/testContext.xml" }) public class SchemaManagerUpdateIT { @Autowired @Qualifier("jpa-test-configuration") private JpaTestConfiguration configuration; private SchemaManagerUpdate schemaManager; @Before public void setUp() { schemaManager = new SchemaManagerUpdate(configuration.getJpaModelConfiguration()); } @Test public void executeUpdateAndDropScriptsShouldWorkWithAllSupportedTypes() { final BusinessObjectModel bom = BOMBuilder.aBOM().buildModelWithAllSupportedTypes(); updateAndDropSchema(bom); } @Test public void executeUpdateAndDropScriptsShouldSupportConstraints() { final BusinessObjectModel bom = BOMBuilder.aBOM().buildModelWithConstrainedFields(); updateAndDropSchema(bom); } private void updateAndDropSchema(final BusinessObjectModel bom) { final List updateExceptions = schemaManager.update(bom.getBusinessObjectsClassNames()); if (!updateExceptions.isEmpty()) { fail("Updating schema fails due to: " + updateExceptions); } final List dropExceptions = schemaManager.drop(bom.getBusinessObjectsClassNames()); if (!dropExceptions.isEmpty()) { fail("Updating schema fails due to: " + dropExceptions); } } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/java/org/bonitasoft/engine/business/data/impl/jackson/serializer/ExtraPropertyStringListSerializerTest.java ================================================ /** * 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.business.data.impl.jackson.serializer; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import java.util.List; import org.junit.Test; public class ExtraPropertyStringListSerializerTest { private ExtraPropertyStringListSerializer serializer = new ExtraPropertyStringListSerializer(); @Test public void should_convert_null_list_to_empty() throws Exception { assertThat((List) serializer.convert(null)).isEmpty(); } @Test public void should_convert_list_to_string() throws Exception { assertThat((List) serializer.convert(asList(1L, 2L))).containsExactly("1", "2"); } @Test public void should_convert_list_with_null_elements() throws Exception { assertThat((List) serializer.convert(asList(1L, null, 2L))).containsExactly("1", null, "2"); } } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/resources/META-INF/persistence.xml ================================================ org.hibernate.jpa.HibernatePersistenceProvider com.company.pojo.Employee com.company.pojo.Person ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/resources/datasource/datasource-dependency-h2.xml ================================================ org.hibernate.dialect.H2Dialect org.h2.jdbcx.JdbcDataSource jdbc:h2:mem:datamanagement;DB_CLOSE_ON_EXIT=FALSE;IGNORECASE=TRUE;AUTOCOMMIT=OFF; sa ${db.user} ${db.password} ${db.url} ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/resources/datasource/datasource-dependency-postgres.xml ================================================ org.hibernate.dialect.PostgreSQL10Dialect org.postgresql.xa.PGXADataSource localhost 5432 bonita bonita bonita jdbc:postgresql://${db.server.name}:${db.server.port}/${db.database.name} ${db.user} ${db.password} ${db.server.name} ${db.server.port} ${db.database.name} ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/resources/org/bonitasoft/engine/business/data/impl/EntitySerializerPojo.json ================================================ { "persistenceId": 9223372036854775807, "persistenceId_string": "9223372036854775807", "persistenceVersion": 1, "persistenceVersion_string": "1", "aString": "string", "aBoolean": true, "aDate": 123, "anOffsetDateTime": "1949-04-09T11:14:05Z", "aLocalDate": "2017-03-06", "aLocalDateTime": "1945-05-08T12:31:17", "aDouble": 1.7976931348623157E308, "aDouble_string": "1.7976931348623157E308", "aFloat": 3.4028235E38, "aFloat_string": "3.4028235E38", "aInteger": 2147483647, "aLong": 9223372036854775807, "aLong_string": "9223372036854775807", "aText": "text", "manyLong": [ 1, 2 ], "manyLong_string": [ "1", "2" ], "manyString": [ "abc", "def" ] } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/resources/org/bonitasoft/engine/business/data/impl/multiplePerson.json ================================================ [ { "persistenceId": 1, "persistenceVersion": 2, "persistenceId_string": "1", "persistenceVersion_string": "2", "name": "John", "age": 50, "phones": [ { "persistenceId": null, "persistenceVersion": null, "persistenceId_string": null, "persistenceVersion_string": null, "number": "1234560" }, { "persistenceId": null, "persistenceVersion": null, "persistenceId_string": null, "persistenceVersion_string": null, "number": "1234561" }, { "persistenceId": null, "persistenceVersion": null, "persistenceId_string": null, "persistenceVersion_string": null, "number": "1234562" } ], "birthday": 123456789, "hasMobile": true, "bools": [ true, false, true, false ] }, { "persistenceId": 2, "persistenceVersion": 2, "persistenceId_string": "2", "persistenceVersion_string": "2", "name": "John", "age": 50, "phones": [ { "persistenceId": null, "persistenceVersion": null, "persistenceId_string": null, "persistenceVersion_string": null, "number": "1234560" }, { "persistenceId": null, "persistenceVersion": null, "persistenceId_string": null, "persistenceVersion_string": null, "number": "1234561" }, { "persistenceId": null, "persistenceVersion": null, "persistenceId_string": null, "persistenceVersion_string": null, "number": "1234562" } ], "birthday": 123456789, "hasMobile": true, "bools": [ true, false, true, false ] } ] ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/resources/org/bonitasoft/engine/business/data/impl/personWithDetails.json ================================================ { "persistenceId": 666, "persistenceId_string": "666", "persistenceVersion": 2, "persistenceVersion_string": "2", "name": "John", "age": 50, "phones": [ { "persistenceId": 22365, "persistenceId_string": "22365", "persistenceVersion": 365, "persistenceVersion_string": "365", "number": "1234560" }, { "persistenceId": 22366, "persistenceId_string": "22366", "persistenceVersion": 366, "persistenceVersion_string": "366", "number": "1234561" }, { "persistenceId": 22367, "persistenceId_string": "22367", "persistenceVersion": 367, "persistenceVersion_string": "367", "number": "1234562" } ], "birthday": 123456789, "hasMobile": true, "bools": [ true, false, true, false ], "address1": { "persistenceId": 2598, "persistenceId_string": "2598", "persistenceVersion": 99992598, "persistenceVersion_string": "99992598", "street": "Rue Gustave Eiffel", "number": 32.0, "number_string": "32.0", "floors": [ 1.0, 4.0 ], "floors_string": [ "1.0", "4.0" ], "links": [ { "rel": "doorCode", "href": "/businessdata/com.company.model.Address/2598/doorCode" } ] }, "address2": { "persistenceId": 358, "persistenceId_string": "358", "persistenceVersion": 118, "persistenceVersion_string": "118", "street": null, "number": null, "number_string": null, "floors": null, "floors_string": null, "links": [ { "rel": "doorCode", "href": "/businessdata/com.company.model.Address/358/doorCode" } ] }, "toIncludes": [ 1235, 6666, 7777 ], "toIncludes_string": [ "1235", "6666", "7777" ], "methodHandlerObject": {}, "proxys": [ {}, {}, {} ], "links": [ { "rel": "secretPhone", "href": "/businessdata/com.company.model.PersonWithDetails/666/secretPhone" }, { "rel": "toIgnores", "href": "/businessdata/com.company.model.PersonWithDetails/666/toIgnores" } ] } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/resources/org/bonitasoft/engine/business/data/impl/singlePerson.json ================================================ { "persistenceId": 1, "persistenceVersion": 2, "persistenceId_string": "1", "persistenceVersion_string": "2", "name": "John", "age": 50, "phones": [ { "persistenceId": null, "persistenceVersion": null, "persistenceId_string": null, "persistenceVersion_string": null, "number": "1234560" }, { "persistenceId": null, "persistenceVersion": null, "persistenceId_string": null, "persistenceVersion_string": null, "number": "1234561" }, { "persistenceId": null, "persistenceVersion": null, "persistenceId_string": null, "persistenceVersion_string": null, "number": "1234562" } ], "birthday": 123456789, "hasMobile": true, "bools": [ true, false, true, false ] } ================================================ FILE: services/bonita-business-data/bonita-business-data-impl/src/test/resources/testContext.xml ================================================ ================================================ FILE: services/bonita-cache/build.gradle ================================================ dependencies { api libs.ehCache api project(':services:bonita-commons') api project(':services:bonita-session') api libs.springContext annotationProcessor libs.lombok compileOnly libs.lombok testImplementation libs.mockitoCore testImplementation libs.logback testImplementation libs.systemLambda testImplementation libs.assertj } ================================================ FILE: services/bonita-cache/src/main/java/org/bonitasoft/engine/cache/CacheConfiguration.java ================================================ /** * 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.cache; import lombok.Getter; import lombok.Setter; /** * @author Emmanuel Duchastenier */ @Setter @Getter public class CacheConfiguration { /** * most implementation support LRU and LFU * some implementation (ehcache) support FIFO also * by default set to LRU */ private String evictionPolicy = "LRU"; /** * The time to live is the time elements from this cache will be kept. * After this time the element can be evicted */ private long timeToLiveSeconds = 60 * 60; /** * the maximum number of elements the cache will keep in memory */ private int maxElementsInMemory = 10000; /** * @param maxElementsInMemory * the maxElementsInMemory to set. Zero is an invalid value (infinite). * If value is set to 0 or less, the value will be reset to 1. */ public void setMaxElementsInMemory(final int maxElementsInMemory) { this.maxElementsInMemory = maxElementsInMemory; if (this.maxElementsInMemory <= 0) { this.maxElementsInMemory = 1; } } /** * true if the elements are never evicted automatically */ private boolean eternal = false; /** * Are the elements stored in the cache read more often than written ? */ private boolean readIntensive = false; /** * Off-heap memory size in megabytes. * Zero means no off-heap storage (heap-only). *

      * Off-heap storage provides overflow capacity for the heap tier without requiring disk I/O. * It uses native memory (outside JVM heap) which doesn't participate in garbage collection, * reducing GC pressure while maintaining good performance. *

      *

      * Example: 512 = 512MB of off-heap memory *

      *

      * Note: Off-heap storage requires that cached objects are serializable. *

      */ private int offHeapSizeMB = 0; // Default: no off-heap (heap-only) /** * Name of this cache configuration */ private String name; } ================================================ FILE: services/bonita-cache/src/main/java/org/bonitasoft/engine/cache/CacheService.java ================================================ /** * 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.cache; import java.io.Serializable; import java.util.List; /** * @author Baptiste Mesta */ public interface CacheService { /** * Store an object in the cache. If the cache don't exists it will be created. * * @param cacheName The name of the cache in which the object must be stored * @param key The key that will allow to retrieve the object * @param value The object to store * @throws SCacheException Error thrown if has exceptions during the cache store. */ void store(String cacheName, Serializable key, Object value) throws SCacheException; /** * Remove the element according to the cache name and the key * * @param cacheName the name of the cache to remove from * @param key the key to remove from the cache * @return true if an element was removed * @throws SCacheException Error thrown if has exceptions during the cache removal. */ boolean remove(String cacheName, Object key) throws SCacheException; /** * Get a cached object. * * @param cacheName * The name of the cache on which to get the object * @param key * The key that is used to store the object * @return the cached object, or null if it doesn't exists * @throws SCacheException * Error thrown if has exceptions during the cache object get. */ Object get(String cacheName, Object key) throws SCacheException; /** * Get list of keys on a cache. * * @param cacheName * The name of the cache on which to get the key list * @return the list of keys on the cache, or null if no keys exist * @throws SCacheException if the cache cannot be read */ List getKeys(String cacheName) throws SCacheException; /** * Clear the cache named by cacheName * * @param cacheName * The name of the cache to clear * @return true if no cache was found with the given name * @throws SCacheException * Error thrown if has exceptions during the cache clear. */ boolean clear(String cacheName) throws SCacheException; /** * Clear all cache of the service * * @throws SCacheException * Error thrown if has exceptions during the cache clear. */ void clearAll() throws SCacheException; /** * Return the size of the cache with cacheName. * * @param cacheName * The name of cache * @return the size of the named cache * @throws SCacheException * if no cache is found with that name. */ int getCacheSize(String cacheName) throws SCacheException; /** * Get the names of all the caches * * @return a list of caches names */ List getCachesNames(); boolean isStopped(); } ================================================ FILE: services/bonita-cache/src/main/java/org/bonitasoft/engine/cache/SCacheException.java ================================================ /** * 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.cache; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class SCacheException extends SBonitaException { private static final long serialVersionUID = 3608017699323834096L; public SCacheException(final String message) { super(message); } public SCacheException(final Throwable t) { super(t); } public SCacheException(final String message, final Exception t) { super(message, t); } } ================================================ FILE: services/bonita-cache/src/main/java/org/bonitasoft/engine/cache/configuration/CacheConfigurationBeans.java ================================================ /** * 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.cache.configuration; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Spring configuration class that defines cache configuration beans for the Bonita platform. *

      * This class provides factory methods for creating {@link org.bonitasoft.engine.cache.CacheConfiguration} * beans with their respective properties loaded from application configuration files. * Each cache configuration is parameterized through Spring's {@code @Value} annotations, * allowing runtime configuration via properties files. *

      *

      * The configured caches include: *

      *
        *
      • Application Token Cache - Stores application authentication tokens
      • *
      • Default Platform Cache - General-purpose platform-level cache
      • *
      • Synchro Service Cache - Synchronization service cache
      • *
      • Config Files Cache - Configuration files cache
      • *
      • Connector Cache - Connector definitions and instances
      • *
      • Process Definition Cache - BPMN process definitions
      • *
      • Parameter Cache - Process and tenant parameters
      • *
      • User Filter Cache - User filter definitions
      • *
      • Groovy Script Cache - Compiled Groovy scripts
      • *
      • Transient Data Cache - Transient process data
      • *
      * * @see org.bonitasoft.engine.cache.CacheConfiguration */ @Configuration public class CacheConfigurationBeans { public static final String APPLICATION_TOKEN_CACHE_NAME = "application-token"; /** * Creates the cache configuration for application tokens. *

      * This cache stores tokens used for applications. * Configuration properties are loaded from {@code bonita.runtime.cache.application-token.*}. *

      * * @param maxElementsInMemory the maximum number of elements that can be stored in memory * @param eternal whether cached elements never expire * @param evictionPolicy the eviction policy (e.g., "LRU", "LFU", "FIFO") * @param readIntensive whether this cache is optimized for read-intensive workloads * @param timeToLiveSeconds the time-to-live in seconds for cached elements * @param offHeapSizeMB the size of off-heap memory in MB (0 = no off-heap) * @return the configured cache configuration bean */ @Bean public org.bonitasoft.engine.cache.CacheConfiguration applicationTokenCacheConfiguration( @Value("${bonita.runtime.cache.application-token.maxElementsInMemory:1000}") final int maxElementsInMemory, @Value("${bonita.runtime.cache.application-token.eternal:false}") final boolean eternal, @Value("${bonita.runtime.cache.application-token.evictionPolicy:LRU}") final String evictionPolicy, @Value("${bonita.runtime.cache.application-token.readIntensive:false}") final boolean readIntensive, @Value("${bonita.runtime.cache.application-token.timeToLiveSeconds:3600}") final int timeToLiveSeconds, @Value("${bonita.runtime.cache.application-token.offHeapSizeMB:0}") final int offHeapSizeMB) { return createCacheConfiguration(APPLICATION_TOKEN_CACHE_NAME, maxElementsInMemory, eternal, evictionPolicy, readIntensive, timeToLiveSeconds, offHeapSizeMB); } @Bean public org.bonitasoft.engine.cache.CacheConfiguration defaultCacheConfiguration( @Value("${bonita.platform.cache.default.maxElementsInMemory}") final int maxElementsInMemory, @Value("${bonita.platform.cache.default.eternal}") final boolean eternal, @Value("${bonita.platform.cache.default.evictionPolicy}") final String evictionPolicy, @Value("${bonita.platform.cache.default.readIntensive}") final boolean readIntensive, @Value("${bonita.platform.cache.default.timeToLiveSeconds}") final int timeToLiveSeconds, @Value("${bonita.platform.cache.default.offHeapSizeMB:0}") final int offHeapSizeMB) { return createCacheConfiguration("DEFAULT_PLATFORM", maxElementsInMemory, eternal, evictionPolicy, readIntensive, timeToLiveSeconds, offHeapSizeMB); } @Bean public org.bonitasoft.engine.cache.CacheConfiguration synchroServiceCacheConfig( @Value("${bonita.platform.cache.synchro.maxElementsInMemory}") final int maxElementsInMemory, @Value("${bonita.platform.cache.synchro.eternal}") final boolean eternal, @Value("${bonita.platform.cache.synchro.evictionPolicy}") final String evictionPolicy, @Value("${bonita.platform.cache.synchro.readIntensive}") final boolean readIntensive, @Value("${bonita.platform.cache.synchro.timeToLiveSeconds}") final int timeToLiveSeconds, @Value("${bonita.platform.cache.synchro.offHeapSizeMB:0}") final int offHeapSizeMB) { return createCacheConfiguration("SYNCHRO_SERVICE_CACHE", maxElementsInMemory, eternal, evictionPolicy, readIntensive, timeToLiveSeconds, offHeapSizeMB); } @Bean public org.bonitasoft.engine.cache.CacheConfiguration configFilesCacheConfig( @Value("${bonita.platform.cache.configfiles.maxElementsInMemory}") final int maxElementsInMemory, @Value("${bonita.platform.cache.configfiles.eternal}") final boolean eternal, @Value("${bonita.platform.cache.configfiles.evictionPolicy}") final String evictionPolicy, @Value("${bonita.platform.cache.configfiles.readIntensive}") final boolean readIntensive, @Value("${bonita.platform.cache.configfiles.timeToLiveSeconds}") final int timeToLiveSeconds, @Value("${bonita.platform.cache.configfiles.offHeapSizeMB:0}") final int offHeapSizeMB) { return createCacheConfiguration("CONFIGURATION_FILES_CACHE", maxElementsInMemory, eternal, evictionPolicy, readIntensive, timeToLiveSeconds, offHeapSizeMB); } @Bean public org.bonitasoft.engine.cache.CacheConfiguration connectorCacheConfig( @Value("${bonita.tenant.cache.connector.maxElementsInMemory}") final int maxElementsInMemory, @Value("${bonita.tenant.cache.connector.eternal}") final boolean eternal, @Value("${bonita.tenant.cache.connector.evictionPolicy}") final String evictionPolicy, @Value("${bonita.tenant.cache.connector.readIntensive}") final boolean readIntensive, @Value("${bonita.tenant.cache.connector.timeToLiveSeconds}") final int timeToLiveSeconds, @Value("${bonita.tenant.cache.connector.offHeapSizeMB:0}") final int offHeapSizeMB) { return createCacheConfiguration("CONNECTOR", maxElementsInMemory, eternal, evictionPolicy, readIntensive, timeToLiveSeconds, offHeapSizeMB); } @Bean public org.bonitasoft.engine.cache.CacheConfiguration processDefinitionCacheConfig( @Value("${bonita.tenant.cache.processdef.maxElementsInMemory}") final int maxElementsInMemory, @Value("${bonita.tenant.cache.processdef.eternal}") final boolean eternal, @Value("${bonita.tenant.cache.processdef.evictionPolicy}") final String evictionPolicy, @Value("${bonita.tenant.cache.processdef.readIntensive}") final boolean readIntensive, @Value("${bonita.tenant.cache.processdef.timeToLiveSeconds}") final int timeToLiveSeconds, @Value("${bonita.tenant.cache.processdef.offHeapSizeMB:0}") final int offHeapSizeMB) { return createCacheConfiguration("_PROCESSDEF", maxElementsInMemory, eternal, evictionPolicy, readIntensive, timeToLiveSeconds, offHeapSizeMB); } @Bean public org.bonitasoft.engine.cache.CacheConfiguration parameterCacheConfig( @Value("${bonita.tenant.cache.parameter.maxElementsInMemory}") final int maxElementsInMemory, @Value("${bonita.tenant.cache.parameter.eternal}") final boolean eternal, @Value("${bonita.tenant.cache.parameter.evictionPolicy}") final String evictionPolicy, @Value("${bonita.tenant.cache.parameter.readIntensive}") final boolean readIntensive, @Value("${bonita.tenant.cache.parameter.timeToLiveSeconds}") final int timeToLiveSeconds, @Value("${bonita.tenant.cache.parameter.offHeapSizeMB:0}") final int offHeapSizeMB) { return createCacheConfiguration("parameters", maxElementsInMemory, eternal, evictionPolicy, readIntensive, timeToLiveSeconds, offHeapSizeMB); } @Bean public org.bonitasoft.engine.cache.CacheConfiguration userFilterCacheConfig( @Value("${bonita.tenant.cache.userfilter.maxElementsInMemory}") final int maxElementsInMemory, @Value("${bonita.tenant.cache.userfilter.eternal}") final boolean eternal, @Value("${bonita.tenant.cache.userfilter.evictionPolicy}") final String evictionPolicy, @Value("${bonita.tenant.cache.userfilter.readIntensive}") final boolean readIntensive, @Value("${bonita.tenant.cache.userfilter.timeToLiveSeconds}") final int timeToLiveSeconds, @Value("${bonita.tenant.cache.userfilter.offHeapSizeMB:0}") final int offHeapSizeMB) { return createCacheConfiguration("USER_FILTER", maxElementsInMemory, eternal, evictionPolicy, readIntensive, timeToLiveSeconds, offHeapSizeMB); } @Bean public org.bonitasoft.engine.cache.CacheConfiguration groovyScriptCacheConfig( @Value("${bonita.tenant.cache.groovy.maxElementsInMemory}") final int maxElementsInMemory, @Value("${bonita.tenant.cache.groovy.eternal}") final boolean eternal, @Value("${bonita.tenant.cache.groovy.evictionPolicy}") final String evictionPolicy, @Value("${bonita.tenant.cache.groovy.readIntensive}") final boolean readIntensive, @Value("${bonita.tenant.cache.groovy.timeToLiveSeconds}") final int timeToLiveSeconds, @Value("${bonita.tenant.cache.groovy.offHeapSizeMB:0}") final int offHeapSizeMB) { return createCacheConfiguration("GROOVY_SCRIPT_CACHE_NAME", maxElementsInMemory, eternal, evictionPolicy, readIntensive, timeToLiveSeconds, offHeapSizeMB); } @Bean public org.bonitasoft.engine.cache.CacheConfiguration transientDataCacheConfig( @Value("${bonita.tenant.cache.transientdata.maxElementsInMemory}") final int maxElementsInMemory, @Value("${bonita.tenant.cache.transientdata.eternal}") final boolean eternal, @Value("${bonita.tenant.cache.transientdata.evictionPolicy}") final String evictionPolicy, @Value("${bonita.tenant.cache.transientdata.readIntensive}") final boolean readIntensive, @Value("${bonita.tenant.cache.transientdata.timeToLiveSeconds}") final int timeToLiveSeconds, @Value("${bonita.tenant.cache.transientdata.offHeapSizeMB:0}") final int offHeapSizeMB) { return createCacheConfiguration("transient_data", maxElementsInMemory, eternal, evictionPolicy, readIntensive, timeToLiveSeconds, offHeapSizeMB); } private org.bonitasoft.engine.cache.CacheConfiguration createCacheConfiguration(String name, int maxElementsInMemory, boolean eternal, String evictionPolicy, boolean readIntensive, int timeToLiveSeconds, int offHeapSizeMB) { var cacheConfiguration = new org.bonitasoft.engine.cache.CacheConfiguration(); cacheConfiguration.setName(name); cacheConfiguration.setMaxElementsInMemory(maxElementsInMemory); cacheConfiguration.setEternal(eternal); cacheConfiguration.setEvictionPolicy(evictionPolicy); cacheConfiguration.setReadIntensive(readIntensive); cacheConfiguration.setTimeToLiveSeconds(timeToLiveSeconds); cacheConfiguration.setOffHeapSizeMB(offHeapSizeMB); return cacheConfiguration; } } ================================================ FILE: services/bonita-cache/src/main/java/org/bonitasoft/engine/cache/ehcache/EhCacheCacheService.java ================================================ /** * 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.cache.ehcache; import static org.ehcache.config.builders.ExpiryPolicyBuilder.timeToLiveExpiration; import java.io.Serializable; import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.cache.CacheConfiguration; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.cache.SCacheException; import org.bonitasoft.engine.commons.PlatformLifecycleService; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.ehcache.Cache; import org.ehcache.CacheManager; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; import org.ehcache.core.internal.statistics.DefaultStatisticsService; import org.ehcache.core.spi.service.StatisticsService; import org.ehcache.core.statistics.CacheStatistics; import org.ehcache.core.statistics.TierStatistics; import org.ehcache.impl.config.serializer.DefaultSerializerConfiguration; import org.ehcache.impl.config.serializer.DefaultSerializerConfiguration.Type; import org.ehcache.impl.serialization.PlainJavaSerializer; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * @author Matthieu Chaffotte */ // Must be started before the work service so it has a "higher" priority @Order(2) @Component @ConditionalOnSingleCandidate(CacheService.class) @Slf4j public class EhCacheCacheService implements CacheService, PlatformLifecycleService { protected CacheManager cacheManager; protected StatisticsService statisticsService; protected final Map> cacheConfigurations; private final org.ehcache.config.CacheConfiguration defaultCacheConfiguration; public EhCacheCacheService(List cacheConfigurations, @Qualifier("defaultCacheConfiguration") CacheConfiguration defaultCacheConfiguration) { this.defaultCacheConfiguration = getEhCacheConfiguration(defaultCacheConfiguration); if (cacheConfigurations != null && !cacheConfigurations.isEmpty()) { this.cacheConfigurations = new HashMap<>(cacheConfigurations.size()); for (final CacheConfiguration cacheConfig : cacheConfigurations) { this.cacheConfigurations.put(cacheConfig.getName(), getEhCacheConfiguration(cacheConfig)); } } else { this.cacheConfigurations = Collections.emptyMap(); } } // VisibleForTesting protected Set getCacheConfigurationNames() { return cacheConfigurations.keySet(); } protected org.ehcache.config.CacheConfiguration getEhCacheConfiguration( final CacheConfiguration cacheConfig) { // Build resource pools based on configuration // Ehcache 3 disk storage requires explicit serialization configuration which is complex // For simplicity, use heap-only caching or heap + offheap (which doesn't require complex serialization) // If more capacity is needed beyond heap, offheap provides better performance than disk anyway ResourcePoolsBuilder poolsBuilder = ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(cacheConfig.getMaxElementsInMemory(), EntryUnit.ENTRIES); // Add off-heap tier if configured (provides overflow capacity without disk I/O) // Off-heap memory is outside JVM heap, so it doesn't participate in GC // This reduces GC pressure while maintaining good performance (all in RAM) if (cacheConfig.getOffHeapSizeMB() > 0) { poolsBuilder = poolsBuilder.offheap(cacheConfig.getOffHeapSizeMB(), MemoryUnit.MB); log.debug("Configuring cache '{}' with off-heap storage: {}MB", cacheConfig.getName(), cacheConfig.getOffHeapSizeMB()); } org.ehcache.config.ResourcePools resourcePools = poolsBuilder.build(); // Note: Disk storage removed to avoid serialization complexity // Ehcache 2 disk overflow is replaced by heap + optional off-heap tiers in Ehcache 3 // Build cache configuration CacheConfigurationBuilder builder = CacheConfigurationBuilder .newCacheConfigurationBuilder(Object.class, Object.class, resourcePools); // Configure serialization for off-heap storage // Off-heap requires serialization to convert objects to byte arrays for native memory storage if (cacheConfig.getOffHeapSizeMB() > 0) { builder = builder .withService(new DefaultSerializerConfiguration(PlainJavaSerializer.class, Type.KEY)) .withService(new DefaultSerializerConfiguration(PlainJavaSerializer.class, Type.VALUE)); } // Set expiry policy if (!cacheConfig.isEternal()) { final long timeToLiveSeconds = cacheConfig.getTimeToLiveSeconds(); builder = builder.withExpiry(timeToLiveExpiration(Duration.ofSeconds(timeToLiveSeconds))); } return builder.build(); } @Override public List getCachesNames() { if (cacheManager == null) { return Collections.emptyList(); } return new ArrayList<>(cacheManager.getRuntimeConfiguration().getCacheConfigurations().keySet()); } @Override public synchronized void stop() { shutdownCacheManager(); } @Override public void pause() { try { clearAll(); } catch (final SCacheException sce) { throw new SBonitaRuntimeException(sce); } } @Override public void resume() throws SCacheException { if (cacheManager == null) { start(); } } protected synchronized Cache createCache(final String cacheName) throws SCacheException { if (cacheManager == null) { throw new SCacheException("The cache is not started, call start() on the cache service"); } Cache cache = cacheManager.getCache(cacheName, Object.class, Object.class); if (cache == null) { final org.ehcache.config.CacheConfiguration cacheConfiguration = cacheConfigurations .get(cacheName); final org.ehcache.config.CacheConfiguration configToUse; if (cacheConfiguration != null) { configToUse = cacheConfiguration; } else { log.warn("No specific cache configuration found for cache '{}'. Using default configuration", cacheName); configToUse = defaultCacheConfiguration; } // In Ehcache 3, we need to create the cache through the CacheManager // This requires rebuilding the CacheManager with the new cache, or using a mutable manager // For now, we'll create it dynamically - this may require CacheManagerBuilder.build() with persistence cache = cacheManager.createCache(cacheName, configToUse); } return cache; } @Override public void store(final String cacheName, final Serializable key, final Object value) throws SCacheException { if (cacheManager == null) { throw new SCacheException("The cache is not started, call start() on the cache service"); } try { Cache cache = cacheManager.getCache(cacheName, Object.class, Object.class); if (cache == null) { cache = createCache(cacheName); } // In Ehcache 3, we directly put key-value without Element wrapper cache.put(key, value); } catch (final IllegalStateException e) { throw new SCacheException("The cache '" + cacheName + "' is not alive", e); } catch (final RuntimeException e) { throw new SCacheException("Error storing value in cache '" + cacheName + "'", e); } } @Override public Object get(final String cacheName, final Object key) throws SCacheException { if (cacheManager == null) { return null; } try { final Cache cache = cacheManager.getCache(cacheName, Object.class, Object.class); if (cache == null) { // the cache does not exist = the key was not stored return null; } // In Ehcache 3, get() returns the value directly (no Element wrapper) return cache.get(key); } catch (final IllegalStateException e) { throw new SCacheException("The cache '" + cacheName + "' is not alive", e); } catch (final Exception e) { throw new SCacheException("Error getting value from cache '" + cacheName + "'", e); } } @Override public boolean clear(final String cacheName) throws SCacheException { if (cacheManager == null) { return true; } try { final Cache cache = cacheManager.getCache(cacheName, Object.class, Object.class); if (cache != null) { // In Ehcache 3, clear() is used instead of removeAll() cache.clear(); } return cache == null; } catch (final IllegalStateException e) { throw new SCacheException("The cache '" + cacheName + "' is not alive", e); } catch (final Exception e) { throw new SCacheException("Error clearing cache '" + cacheName + "'", e); } } /** *
           * getCacheSize(cacheName)
           *           ↓
           * [Check cache exists]
           *           ↓
           * [Try Statistics API] ← O(1) - FAST
           *     ├─ Success → Return size from tier statistics
           *     └─ Failure → Log debug + Fall through
           *           ↓
           * [Iterate cache entries] ← O(n) - Fallback
           *           ↓
           * Return count
           * 
      * * @param cacheName The name of cache */ @Override public int getCacheSize(final String cacheName) throws SCacheException { if (cacheManager == null) { return 0; } try { final Cache cache = cacheManager.getCache(cacheName, Object.class, Object.class); if (cache == null) { return 0; } // Try to use statistics API for efficient size retrieval (O(1) complexity) if (statisticsService != null) { try { CacheStatistics cacheStatistics = statisticsService.getCacheStatistics(cacheName); if (cacheStatistics != null) { Map tierStatistics = cacheStatistics.getTierStatistics(); if (tierStatistics != null) { // Sum up entries across all tiers (OnHeap, OffHeap, Disk) long totalSize = tierStatistics.values().stream() .mapToLong(TierStatistics::getMappings) .sum(); return (int) totalSize; } } } catch (Exception e) { log.debug("Failed to get cache size from statistics for cache '{}', falling back to iteration: {}", cacheName, e.getMessage()); // Fall through to iteration method } } // Fallback: iterate through cache entries (O(n) complexity) // This can be expensive for large caches return countEntriesByIteration(cache); } catch (final IllegalStateException e) { throw new SCacheException("The cache '" + cacheName + "' is not alive", e); } catch (final RuntimeException e) { throw new SCacheException("Error getting size of cache '" + cacheName + "'", e); } } private int countEntriesByIteration(Cache cache) { int count = 0; for (Cache.Entry ignored : cache) { count++; } return count; } @Override public void clearAll() throws SCacheException { if (cacheManager == null) { return; } try { final List cacheNames = getCachesNames(); for (final String cacheName : cacheNames) { clear(cacheName); } } catch (final RuntimeException e) { throw new SCacheException("Error clearing all caches", e); } } @Override public boolean remove(final String cacheName, final Object key) throws SCacheException { if (cacheManager == null) { return false; } final Cache cache = cacheManager.getCache(cacheName, Object.class, Object.class); if (cache == null) { return false; } // In Ehcache 3, remove() returns void, so we need to check if key existed first boolean existed = cache.containsKey(key); cache.remove(key); return existed; } @Override public List getKeys(final String cacheName) throws SCacheException { if (cacheManager == null) { return Collections.emptyList(); } try { final Cache cache = cacheManager.getCache(cacheName, Object.class, Object.class); if (cache == null) { return Collections.emptyList(); } // In Ehcache 3, we need to iterate over entries to get keys List keys = new ArrayList<>(); for (Cache.Entry entry : cache) { keys.add(entry.getKey()); } return keys; } catch (final IllegalStateException e) { throw new SCacheException("The cache '" + cacheName + "' is not alive", e); } catch (final RuntimeException e) { throw new SCacheException("Error getting keys from cache '" + cacheName + "'", e); } } protected void shutdownCacheManager() { if (cacheManager != null) { // In Ehcache 3, close() is used instead of shutdown() cacheManager.close(); cacheManager = null; } } @Override public boolean isStopped() { return cacheManager == null; } protected String getCacheManagerName() { return "BONITA"; } @Override public synchronized void start() throws SCacheException { if (cacheManager == null) { // Initialize statistics service for efficient cache size retrieval statisticsService = new DefaultStatisticsService(); // In Ehcache 3, create a simple heap-only CacheManager with statistics enabled // Disk persistence removed for simplicity (requires complex serialization configuration) cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .using(statisticsService) .build(true); // true = initialize immediately } } } ================================================ FILE: services/bonita-cache/src/test/java/org/bonitasoft/engine/cache/CacheServiceTest.java ================================================ /** * 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.cache; import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOut; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.cache.ehcache.EhCacheCacheService; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CacheServiceTest { protected static final String SOME_DEFAULT_CACHE_NAME = "SOME_DEFAULT_CACHE_NAME"; protected static final String ETERNAL_CACHE = "ETERNAL_CACHE"; private static final String TEST1 = "test1"; private static final String TEST2 = "test2"; private static final String ONE_ELEMENT_IN_MEMORY_ONLY = "ONE_ELEMENT_IN_MEMORY_ONLY"; private static final String OFF_HEAP_CACHE = "OFF_HEAP_CACHE"; private static final String LARGE_OFF_HEAP_CACHE = "LARGE_OFF_HEAP_CACHE"; private final static Logger LOGGER = LoggerFactory.getLogger(CacheServiceTest.class); private static final int ONE_ELEMENT_ONLY_MAX_ELEMENTS_IN_MEMORY = 1; private EhCacheCacheService cacheService; @Rule public TestName name = new TestName(); @Before public void setUp() throws Exception { LOGGER.info("Testing : {}", name.getMethodName()); cacheService = (EhCacheCacheService) getCacheService(); cacheService.clearAll(); cacheService.start(); } @After public void tearDown() { LOGGER.info("Tested: {}", name.getMethodName()); cacheService.stop(); cacheService = null; } protected CacheService getCacheService() { final List configurationsList = new ArrayList<>(2); final CacheConfiguration cacheConfiguration = new CacheConfiguration(); cacheConfiguration.setName(SOME_DEFAULT_CACHE_NAME); cacheConfiguration.setTimeToLiveSeconds(1); cacheConfiguration.setMaxElementsInMemory(200); configurationsList.add(cacheConfiguration); final CacheConfiguration cacheConfigurationEternal = new CacheConfiguration(); cacheConfigurationEternal.setName(ETERNAL_CACHE); cacheConfigurationEternal.setTimeToLiveSeconds(1); cacheConfigurationEternal.setMaxElementsInMemory(200); cacheConfigurationEternal.setEternal(true); configurationsList.add(cacheConfigurationEternal); final CacheConfiguration cacheConfiguration1 = new CacheConfiguration(); cacheConfiguration1.setName(TEST1); cacheConfiguration1.setTimeToLiveSeconds(1); cacheConfiguration1.setMaxElementsInMemory(10_000); cacheConfiguration1.setEternal(false); configurationsList.add(cacheConfiguration1); final CacheConfiguration cacheConfiguration2 = new CacheConfiguration(); cacheConfiguration2.setName(TEST2); cacheConfiguration2.setTimeToLiveSeconds(1); cacheConfiguration2.setMaxElementsInMemory(100_000); cacheConfiguration2.setEternal(false); configurationsList.add(cacheConfiguration2); final CacheConfiguration cacheWithOneElementInMemoryOnly = new CacheConfiguration(); cacheWithOneElementInMemoryOnly.setName(ONE_ELEMENT_IN_MEMORY_ONLY); cacheWithOneElementInMemoryOnly.setTimeToLiveSeconds(10); cacheWithOneElementInMemoryOnly.setMaxElementsInMemory(ONE_ELEMENT_ONLY_MAX_ELEMENTS_IN_MEMORY); cacheWithOneElementInMemoryOnly.setEternal(false); configurationsList.add(cacheWithOneElementInMemoryOnly); final CacheConfiguration offHeapCache = new CacheConfiguration(); offHeapCache.setName(OFF_HEAP_CACHE); offHeapCache.setTimeToLiveSeconds(1); offHeapCache.setMaxElementsInMemory(10); offHeapCache.setOffHeapSizeMB(1); // 1MB off-heap offHeapCache.setEternal(false); configurationsList.add(offHeapCache); final CacheConfiguration largeOffHeapCache = new CacheConfiguration(); largeOffHeapCache.setName(LARGE_OFF_HEAP_CACHE); largeOffHeapCache.setTimeToLiveSeconds(60); largeOffHeapCache.setMaxElementsInMemory(100); largeOffHeapCache.setOffHeapSizeMB(10); // 10MB off-heap largeOffHeapCache.setEternal(false); configurationsList.add(largeOffHeapCache); return new EhCacheCacheService(configurationsList, new CacheConfiguration()); } @Test public void cache_should_expire_entries_after_short_timeout() throws Exception { final String key = "shortTimeout"; cacheService.store(SOME_DEFAULT_CACHE_NAME, key, new Object()); Object object = cacheService.get(SOME_DEFAULT_CACHE_NAME, key); assertNotNull("Object should be in cache", object); Thread.sleep(1020); object = cacheService.get(SOME_DEFAULT_CACHE_NAME, key); assertNull("Object should not be in cache any longer", object); } @Test public void eternal_cache_should_not_expire_entries() throws Exception { final String key = "eternalCacheTest"; final Object value = new Object(); cacheService.store(ETERNAL_CACHE, key, value); Object object = cacheService.get(ETERNAL_CACHE, key); assertNotNull("Object should be in cache", object); Thread.sleep(1020); object = cacheService.get(ETERNAL_CACHE, key); assertEquals("Object should still be in cache", value, object); } @Test public void cache_store_should_log_when_cache_not_found() throws Exception { // given final String key = "defaultTimeout"; final String anotherCacheName = "Should_use_default_config"; // when final String log = tapSystemOut(() -> cacheService.store(anotherCacheName, key, "")); // then assertThat(log) .containsPattern("WARN.*No specific cache configuration found for cache 'Should_use_default_config'"); } @Test public void cache_should_store_simple_object_and_increase_size() throws SCacheException { final int cacheSize = cacheService.getCacheSize(TEST1); final String myObject = "testObject"; cacheService.store(TEST1, "test", myObject); assertEquals("cache size did not increased", cacheSize + 1, cacheService.getCacheSize(TEST1)); } @Test public void cache_should_retrieve_stored_simple_object() throws SCacheException { final String myObject = "testObject"; cacheService.store(TEST1, "test", myObject); final String inObject = (String) cacheService.get(TEST1, "test"); assertEquals("we didn't retrieve the same object", myObject, inObject); } @Test public void cache_should_store_complex_object_and_increase_size() throws SCacheException { final int cacheSize = cacheService.getCacheSize(TEST1); final ArrayList> list = new ArrayList<>(); final HashMap map = new HashMap<>(); map.put("bpm", "bonita"); list.add(map); cacheService.store(TEST1, "complex", list); assertEquals("cache size did not increased", cacheSize + 1, cacheService.getCacheSize(TEST1)); } @SuppressWarnings("unchecked") @Test public void cache_should_retrieve_stored_complex_object() throws SCacheException { final ArrayList> list = new ArrayList<>(); final HashMap map = new HashMap<>(); map.put("bpm", "bonita"); list.add(map); cacheService.store(TEST1, "complex", list); final Object object = cacheService.get(TEST1, "complex"); assertNotNull("the object does not exists", object); assertEquals("Not the same object", "bonita", ((ArrayList>) object).get(0).get("bpm")); } @Test public void cache_should_be_cleared_when_clear_called() throws SCacheException { cacheService.store(TEST1, "test1", "test1 value"); assertTrue("cache was not empty", cacheService.getCacheSize(TEST1) > 0); cacheService.clear(TEST1); assertEquals("cache was not cleared", 0, cacheService.getCacheSize(TEST1)); } @Test public void all_caches_should_be_cleared_when_clear_all_called() throws SCacheException { cacheService.store(TEST1, "test1", "test1"); cacheService.store(TEST2, "test2", "test2"); assertTrue(cacheService.getCacheSize(TEST1) > 0); assertTrue(cacheService.getCacheSize(TEST2) > 0); cacheService.clearAll(); assertEquals("TEST1 was not cleared", 0, cacheService.getCacheSize(TEST1)); assertEquals("TEST2 was not cleared", 0, cacheService.getCacheSize(TEST2)); } @Test public void storing_null_values_should_fail() { assertThrows(SCacheException.class, () -> cacheService.store(TEST1, "test2", null)); } @Test public void cache_should_not_duplicate_when_storing_same_item_twice() throws SCacheException { cacheService.store(TEST1, "sameItem", "value"); assertEquals("first element not added", 1, cacheService.getCacheSize(TEST1)); cacheService.store(TEST1, "sameItem", "value"); assertEquals("element added twice", 1, cacheService.getCacheSize(TEST1)); } @Test public void cache_should_update_element_value_when_key_already_exists() throws SCacheException { cacheService.store(TEST1, "sameItem2", "value1"); assertEquals("first element not added", 1, cacheService.getCacheSize(TEST1)); cacheService.store(TEST1, "sameItem2", "value2"); assertEquals("element added 2 times", 1, cacheService.getCacheSize(TEST1)); assertEquals("element was not updated", "value2", cacheService.get(TEST1, "sameItem2")); } @Test public void store_items_with_overflow_should_evict_old_items() throws SCacheException, InterruptedException { for (int i = 0; i < 2; i++) { cacheService.store(ONE_ELEMENT_IN_MEMORY_ONLY, "testLotOfItems" + i, "value" + i); Thread.sleep(5); // to make sure the Least Recently Used is evicted first } assertEquals("Not all elements were added with the overflow", ONE_ELEMENT_ONLY_MAX_ELEMENTS_IN_MEMORY, cacheService.getCacheSize(ONE_ELEMENT_IN_MEMORY_ONLY)); assertThat(cacheService.get(ONE_ELEMENT_IN_MEMORY_ONLY, "testLotOfItems1")).isEqualTo("value1"); } @Test public void cache_should_evict_items_when_memory_limit_reached() throws SCacheException { final int j = 2; for (int i = 0; i < j; i++) { cacheService.store(ONE_ELEMENT_IN_MEMORY_ONLY, "testLotOfItems" + i, "value" + i); } assertEquals("Too many elements added although limited memory.", 1, cacheService.getCacheSize(ONE_ELEMENT_IN_MEMORY_ONLY)); } @SuppressWarnings("unchecked") @Test public void cache_should_store_objects_by_reference() throws SCacheException { final ArrayList list = new ArrayList<>(); cacheService.store(TEST2, "mylist", list); list.add("kikoo"); final ArrayList cachedList = (ArrayList) cacheService.get(TEST2, "mylist"); assertEquals("object was copied in cached", 1, cachedList.size()); assertEquals("'kikoo' should be in cache", "kikoo", cachedList.get(0)); } @Test public void cache_should_return_all_stored_keys() throws SCacheException { final List keys = cacheService.getKeys(TEST1); assertFalse(keys.contains("aKeyThatMustBeHere")); final int cacheKeySize = keys.size(); cacheService.store(TEST1, "aKeyThatMustBeHere", "value1"); final List keys2 = cacheService.getKeys(TEST1); assertEquals(cacheKeySize + 1, keys2.size()); assertTrue(keys2.contains("aKeyThatMustBeHere")); } @Test public void remove_should_delete_existing_key() throws SCacheException { final String key = "keyToRemove"; final String value = "valueToRemove"; // Store the key cacheService.store(TEST1, key, value); assertEquals("Value should be in cache", value, cacheService.get(TEST1, key)); final int initialSize = cacheService.getCacheSize(TEST1); // Remove the key final boolean removed = cacheService.remove(TEST1, key); // Verify removal assertTrue("remove() should return true for existing key", removed); assertNull("Key should no longer be in cache", cacheService.get(TEST1, key)); assertEquals("Cache size should decrease by 1", initialSize - 1, cacheService.getCacheSize(TEST1)); } @Test public void remove_should_return_false_for_non_existing_key() throws SCacheException { final String key = "nonExistentKey"; // Try to remove a key that doesn't exist final boolean removed = cacheService.remove(TEST1, key); // Verify it returns false assertFalse("remove() should return false for non-existent key", removed); } @Test public void remove_should_not_affect_other_keys() throws SCacheException { // Store multiple keys cacheService.store(TEST1, "key1", "value1"); cacheService.store(TEST1, "key2", "value2"); cacheService.store(TEST1, "key3", "value3"); // Remove one key cacheService.remove(TEST1, "key2"); // Verify other keys are still present assertEquals("key1 should still be in cache", "value1", cacheService.get(TEST1, "key1")); assertNull("key2 should be removed", cacheService.get(TEST1, "key2")); assertEquals("key3 should still be in cache", "value3", cacheService.get(TEST1, "key3")); } @Test public void remove_should_work_on_different_caches() throws SCacheException { final String key = "sharedKey"; // Store same key in two different caches cacheService.store(TEST1, key, "value1"); cacheService.store(TEST2, key, "value2"); // Remove from TEST1 only cacheService.remove(TEST1, key); // Verify removal only affected TEST1 assertNull("Key should be removed from TEST1", cacheService.get(TEST1, key)); assertEquals("Key should still exist in TEST2", "value2", cacheService.get(TEST2, key)); } // Off-heap storage tests @Test public void offHeapCache_should_store_and_retrieve_simple_objects() throws SCacheException { final String value = "offHeapTestValue"; cacheService.store(OFF_HEAP_CACHE, "key1", value); final String retrieved = (String) cacheService.get(OFF_HEAP_CACHE, "key1"); assertEquals("Should retrieve value from off-heap cache", value, retrieved); } @Test public void offHeapCache_should_store_and_retrieve_complex_objects() throws SCacheException { final ArrayList> list = new ArrayList<>(); final HashMap map = new HashMap<>(); map.put("engine", "bonita"); map.put("feature", "off-heap"); list.add(map); cacheService.store(OFF_HEAP_CACHE, "complexKey", list); @SuppressWarnings("unchecked") final ArrayList> retrieved = (ArrayList>) cacheService .get(OFF_HEAP_CACHE, "complexKey"); assertNotNull("Complex object should be retrieved from off-heap cache", retrieved); assertEquals("bonita", retrieved.get(0).get("engine")); assertEquals("off-heap", retrieved.get(0).get("feature")); } @Test public void offHeapCache_should_handle_overflow_from_heap_to_offheap() throws SCacheException { // Fill heap (10 elements) for (int i = 0; i < 10; i++) { cacheService.store(OFF_HEAP_CACHE, "heapKey" + i, "heapValue" + i); } // Add more elements that should overflow to off-heap for (int i = 10; i < 20; i++) { cacheService.store(OFF_HEAP_CACHE, "overflowKey" + i, "overflowValue" + i); } // Verify all elements are still accessible (some in heap, some in off-heap) for (int i = 0; i < 20; i++) { final String key = i < 10 ? "heapKey" + i : "overflowKey" + i; final String expectedValue = i < 10 ? "heapValue" + i : "overflowValue" + i; final Object retrieved = cacheService.get(OFF_HEAP_CACHE, key); assertThat(retrieved).as("Element " + key + " should be accessible").isEqualTo(expectedValue); } } @Test public void offHeapCache_should_evict_elements_when_both_tiers_full() throws SCacheException { // Fill both heap and off-heap by adding many elements // Heap: 10 elements, Off-heap: 1MB (can hold many more serialized strings) final int totalElements = 100; for (int i = 0; i < totalElements; i++) { cacheService.store(OFF_HEAP_CACHE, "key" + i, "value" + i); } // Cache should have evicted some elements based on LRU policy final int cacheSize = cacheService.getCacheSize(OFF_HEAP_CACHE); assertThat(cacheSize).isGreaterThan(0).isLessThanOrEqualTo(totalElements); } @Test public void offHeapCache_should_respect_ttl() throws Exception { final String key = "ttlKey"; final String value = "ttlValue"; cacheService.store(OFF_HEAP_CACHE, key, value); assertNotNull("Value should be in cache initially", cacheService.get(OFF_HEAP_CACHE, key)); // Wait for TTL to expire (OFF_HEAP_CACHE has 1 second TTL) Thread.sleep(1200); assertNull("Value should be evicted after TTL", cacheService.get(OFF_HEAP_CACHE, key)); } @Test public void largeOffHeapCache_should_store_many_elements() throws SCacheException { final int elementCount = 1000; for (int i = 0; i < elementCount; i++) { cacheService.store(LARGE_OFF_HEAP_CACHE, "key" + i, "value" + i); } // Verify cache size final int cacheSize = cacheService.getCacheSize(LARGE_OFF_HEAP_CACHE); assertThat(cacheSize).isGreaterThan(0); // Spot check some elements assertEquals("value0", cacheService.get(LARGE_OFF_HEAP_CACHE, "key0")); assertEquals("value500", cacheService.get(LARGE_OFF_HEAP_CACHE, "key500")); assertEquals("value999", cacheService.get(LARGE_OFF_HEAP_CACHE, "key999")); } @Test public void offHeapCache_should_handle_updates() throws SCacheException { // Ensure cache is empty before starting this test cacheService.clear(OFF_HEAP_CACHE); final String key = "updateKey"; cacheService.store(OFF_HEAP_CACHE, key, "initialValue"); assertEquals("initialValue", cacheService.get(OFF_HEAP_CACHE, key)); cacheService.store(OFF_HEAP_CACHE, key, "updatedValue"); assertEquals("Value should be updated in off-heap cache", "updatedValue", cacheService.get(OFF_HEAP_CACHE, key)); // Cache size should be small (update, not large insert) // Note: With off-heap, statistics may temporarily show 2 if entry moves between tiers int size = cacheService.getCacheSize(OFF_HEAP_CACHE); assertThat(size).as("Cache size after update").isLessThanOrEqualTo(2); } @Test public void offHeapCache_should_be_cleared() throws SCacheException { // Add elements for (int i = 0; i < 5; i++) { cacheService.store(OFF_HEAP_CACHE, "clearKey" + i, "clearValue" + i); } assertThat(cacheService.getCacheSize(OFF_HEAP_CACHE)).isGreaterThan(0); // Clear cache cacheService.clear(OFF_HEAP_CACHE); assertEquals("Off-heap cache should be empty after clear", 0, cacheService.getCacheSize(OFF_HEAP_CACHE)); } @Test public void offHeapCache_should_store_serializable_objects() throws SCacheException { // Test with a custom serializable object final HashMap complexObject = new HashMap<>(); complexObject.put("id", 12345L); complexObject.put("name", "Process Definition"); complexObject.put("version", "1.0"); complexObject.put("data", new ArrayList<>(List.of("item1", "item2", "item3"))); cacheService.store(OFF_HEAP_CACHE, "serializableKey", complexObject); @SuppressWarnings("unchecked") final HashMap retrieved = (HashMap) cacheService.get(OFF_HEAP_CACHE, "serializableKey"); assertNotNull("Serializable object should be retrieved", retrieved); assertEquals(12345L, retrieved.get("id")); assertEquals("Process Definition", retrieved.get("name")); assertEquals("1.0", retrieved.get("version")); @SuppressWarnings("unchecked") final ArrayList data = (ArrayList) retrieved.get("data"); assertEquals(3, data.size()); } } ================================================ FILE: services/bonita-cache/src/test/java/org/bonitasoft/engine/cache/ehcache/EhCacheCacheServiceTest.java ================================================ /** * 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.cache.ehcache; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.cache.CacheConfiguration; import org.bonitasoft.engine.cache.SCacheException; import org.ehcache.Cache; import org.ehcache.CacheManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class EhCacheCacheServiceTest { private final List cacheConfigurations = Collections.emptyList(); @Mock private CacheManager cacheManager; @Mock private Cache cache; private EhCacheCacheService cacheService; @Before public void setup() { // Create a valid default cache configuration for Ehcache 3 CacheConfiguration defaultCacheConfiguration = new CacheConfiguration(); defaultCacheConfiguration.setMaxElementsInMemory(1_000); cacheService = new EhCacheCacheService(cacheConfigurations, defaultCacheConfiguration) { @Override public synchronized void start() { // Mock the cache manager but keep the real statistics service initialization statisticsService = new org.ehcache.core.internal.statistics.DefaultStatisticsService(); cacheManager = EhCacheCacheServiceTest.this.cacheManager; } }; } @Test public void should_getKeys_return_empty_list_when_cache_manager_is_null() throws Exception { final List keys = cacheService.getKeys("unknownCache"); assertThat(keys).isEmpty(); } @Test public void should_getKeys_return_empty_list_when_cache_manager_have_no_cache() throws Exception { cacheService.start(); final List keys = cacheService.getKeys("unknownCache"); assertThat(keys).isEmpty(); } @Test public void should_getCacheSize_return_zero_when_cache_manager_is_null() throws Exception { final int size = cacheService.getCacheSize("unknownCache"); assertThat(size).isZero(); } @Test public void should_getCacheSize_return_zero_when_cache_does_not_exist() throws Exception { cacheService.start(); when(cacheManager.getCache(eq("unknownCache"), any(), any())).thenReturn(null); final int size = cacheService.getCacheSize("unknownCache"); assertThat(size).isZero(); } @Test(expected = SCacheException.class) public void should_getCacheSize_throw_SCacheException_when_cache_is_not_alive() throws Exception { cacheService.start(); when(cacheManager.getCache(eq("testCache"), any(), any())).thenReturn(cache); when(cache.iterator()).thenThrow(new IllegalStateException("Cache is not alive")); cacheService.getCacheSize("testCache"); } @Test(expected = SCacheException.class) public void should_getCacheSize_throw_SCacheException_on_runtime_exception() throws Exception { cacheService.start(); when(cacheManager.getCache(eq("testCache"), any(), any())).thenReturn(cache); when(cache.iterator()).thenThrow(new RuntimeException("Unexpected error")); cacheService.getCacheSize("testCache"); } @Test public void should_getCacheSize_use_statistics_when_available() throws Exception { // Note: This test verifies that statistics are enabled // The actual CacheServiceTest provides integration-level validation // that the cache size is correctly retrieved cacheService.start(); // Verify that statisticsService was initialized assertThat(cacheService.statisticsService).as("StatisticsService should be initialized").isNotNull(); } } ================================================ FILE: services/bonita-cache/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: services/bonita-classloader/build.gradle ================================================ dependencies { api project(':services:bonita-commons') api project(':services:bonita-events') api project(':bpm:bonita-core:bonita-home-server') api project(':services:bonita-transaction') api project(':services:bonita-builder') api project(':services:bonita-persistence') api project(':services:bonita-log') api libs.commonsIO // Dependency on javax.annotations as it is not provided anymore in Java 11: api(libs.javaxAnnotations) testImplementation libs.mockitoCore testImplementation libs.assertj testImplementation libs.logback testImplementation libs.systemRules annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/BonitaClassLoader.java ================================================ /** * 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.classloader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.time.Instant; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.StringJoiner; import java.util.UUID; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.bonitasoft.engine.commons.ExceptionUtils; import org.bonitasoft.engine.data.instance.model.impl.XStreamFactory; /** * @author Elias Ricken de Medeiros * @author Charles Souillard * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Slf4j public class BonitaClassLoader extends URLClassLoader { private final ClassLoaderIdentifier id; protected Map nonJarResources; // that directory contains the jars and resources given in the constructor private final File temporaryDirectory; private boolean isActive = true; private final Instant creationTime = Instant.now(); private final String uuid = generateUUID(); private final Set children = new HashSet<>(); BonitaClassLoader(ClassLoaderIdentifier id, ClassLoader parent, Set jars, Map nonJarResources, File temporaryDirectory) { super(id.toString(), jars.stream().map(BonitaClassLoader::toURL).toArray(URL[]::new), parent); this.id = id; //TODO: These non-jar resources might be added along with jars without having to do special handling this.nonJarResources = new HashMap<>(nonJarResources); this.temporaryDirectory = temporaryDirectory; if (parent instanceof BonitaClassLoader) { //The parent is not a BonitaClassloader when we are on the Global classloader ((BonitaClassLoader) parent).children.add(this); log.debug("Classloader {} added as a child of {}", this, parent); } if (log.isDebugEnabled()) { log.debug("Classloader {} created with \n jars: {}, \n nonJarResources: {}", this, jars.stream().map(File::getPath).collect(Collectors.joining(", ")), nonJarResources.values().stream().map(File::getPath).collect(Collectors.joining(", "))); } } private static String generateUUID() { return UUID.randomUUID().toString(); } private static URL toURL(File f) { try { return f.toURI().toURL(); } catch (MalformedURLException e) { throw new IllegalArgumentException(e); } } @Override public InputStream getResourceAsStream(final String name) { InputStream is = getInternalInputStream(name); if (is == null && name.length() > 0 && name.charAt(0) == '/') { is = getInternalInputStream(name.substring(1)); } return is; } private InputStream getInternalInputStream(final String name) { File classData = nonJarResources.get(name); if (classData != null) { try { return new FileInputStream(classData); } catch (FileNotFoundException e) { throw new RuntimeException(e); } } return super.getResourceAsStream(name); } @Override protected Class loadClass(final String name, final boolean resolve) throws ClassNotFoundException { Class c = null; c = findLoadedClass(name); if (c == null) { try { c = findClass(name); } catch (final ClassNotFoundException e) { // ignore } catch (final LinkageError le) { // might be because of a duplicate loading (concurrency loading), retry to find it one time See BS-2483 c = findLoadedClass(name); if (c == null) { // was not because of duplicate loading: throw the exception throw le; } } } if (c == null) { c = getParent().loadClass(name); } if (resolve) { resolveClass(c); } return c; } public void destroy() { XStreamFactory.remove(this); ClassLoader parent = getParent(); if (parent instanceof BonitaClassLoader) { //The parent is not a BonitaClassloader when we are on the Global classloader ((BonitaClassLoader) parent).children.remove(this); } try { super.close(); } catch (IOException e) { log.warn("Unable to close the classloader {}. Some file might still be present in {}. Cause {}", id, temporaryDirectory.getAbsolutePath(), ExceptionUtils.printLightWeightStacktrace(e)); log.debug("Full cause:", e); } FileUtils.deleteQuietly(temporaryDirectory); isActive = false; log.debug("Destroyed {}", this); } public boolean isDestroyed() { return !isActive; } public ClassLoaderIdentifier getIdentifier() { return id; } public File getTemporaryFolder() { return temporaryDirectory; } public boolean hasChildren() { return !children.isEmpty(); } public Set getChildren() { return Collections.unmodifiableSet(new HashSet<>(children)); } @Override public String toString() { return new StringJoiner(", ", BonitaClassLoader.class.getSimpleName() + "[", "]") .add("id=" + id) .add("isActive=" + isActive) .add("creationTime=" + creationTime) .add("uuid='" + uuid + "'") .add("children=" + children.stream().map(BonitaClassLoader::getIdentifier) .map(ClassLoaderIdentifier::toString).collect(Collectors.joining(", ", "[", "]"))) .toString(); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/BonitaClassLoaderFactory.java ================================================ /** * Copyright (C) 2021 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.classloader; import java.io.File; import java.io.IOException; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; import org.bonitasoft.engine.commons.io.IOUtil; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.home.BonitaResource; public class BonitaClassLoaderFactory { private static final Pattern jarMatcher = Pattern.compile(".*\\.jar"); static BonitaClassLoader createClassLoader(Stream resources, ClassLoaderIdentifier id, URI temporaryDirectoryUri, ClassLoader parent) throws IOException { File temporaryDirectory = createTemporaryDirectory(temporaryDirectoryUri); Map allFiles = writeResourcesOnFileSystem(resources, temporaryDirectory); Set jars = allFiles.entrySet().stream().filter(u -> jarMatcher.matcher(u.getKey()).matches()) .map(Map.Entry::getValue).collect(Collectors.toSet()); Map nonJarResources = allFiles.entrySet().stream() .filter(u -> !jarMatcher.matcher(u.getKey()).matches()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); return new BonitaClassLoader(id, parent, jars, nonJarResources, temporaryDirectory); } private static File createTemporaryDirectory(URI temporaryDirectoryUri) throws IOException { Path temporaryDirectory = new File(temporaryDirectoryUri).toPath(); if (!Files.exists(temporaryDirectory)) { Files.createDirectory(temporaryDirectory); } return Files.createTempDirectory(temporaryDirectory, "engine-classloader").toFile(); } private static Map writeResourcesOnFileSystem(final Stream resources, File temporaryDirectory) { return resources.map(resource -> { try { return writeResource(resource, temporaryDirectory); } catch (final IOException e) { throw new BonitaRuntimeException(e); } }).collect(Collectors.toMap(Pair::getKey, Pair::getValue)); } static Pair writeResource(BonitaResource resource, File temporaryDirectory) throws IOException { String name = resource.getName(); int i = name.lastIndexOf("."); final File file = File.createTempFile(i < 3 ? "tmp" : name.substring(0, i), i < 3 ? name : name.substring(i), temporaryDirectory); IOUtil.write(file, resource.getContent()); return Pair.of(name, file); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/ClassLoaderIdentifier.java ================================================ /** * 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.classloader; import java.io.Serializable; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Data; import org.bonitasoft.engine.dependency.model.ScopeType; /** *
       * -            APPLICATION
       * -                 |
       * -               GLOBAL
       * -                 |
       * -      ------- TENANT ------
       * -     |          |         |
       * - PROCESS1 - PROCESS2 - PROCESS3
       * 
      * * @author Baptiste Mesta * @author Emmanuel Duchastenier */ @Data @AllArgsConstructor(access = AccessLevel.PRIVATE) public class ClassLoaderIdentifier implements Serializable { public static final ScopeType GLOBAL_TYPE = ScopeType.GLOBAL; public static final long GLOBAL_ID = -1; protected static final int FIXED_TENANT_ID = 1; /** * The APPLICATION classloader is the parent classloader of the GLOBAL classloader. It the one in which bonita is * bootstrapped */ public static final ClassLoaderIdentifier APPLICATION = identifier(null, Long.MIN_VALUE); /** * The GLOBAL classloader is the unique one at platform level */ public static final ClassLoaderIdentifier GLOBAL = identifier(GLOBAL_TYPE, GLOBAL_ID); public static final ClassLoaderIdentifier TENANT = identifier(ScopeType.TENANT, FIXED_TENANT_ID); private ScopeType type; private long id; public static ClassLoaderIdentifier identifier(ScopeType scopeType, long id) { return new ClassLoaderIdentifier(scopeType, id); } @Override public String toString() { if (this.equals(GLOBAL)) { return "GLOBAL"; } else if (this.type == ScopeType.TENANT) { return "TENANT"; } else return type.name() + ':' + id; } public boolean isTenantClassloader() { return ScopeType.TENANT.equals(type); } public boolean isGlobalClassloader() { return ScopeType.GLOBAL.equals(type); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/ClassLoaderService.java ================================================ /** * 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.classloader; import org.bonitasoft.engine.commons.PlatformLifecycleService; import org.bonitasoft.engine.dependency.impl.TenantDependencyService; /** * @author Elias Ricken de Medeiros * @author Celine Souchet * @since 6.0 */ public interface ClassLoaderService extends PlatformLifecycleService { void registerDependencyService(TenantDependencyService tenantDependencyService); /** * Get the local ClassLoader for the given type and id. * If no ClassLoader already exists, a new one is created and initialized. * This initialization is executed in a different thread/transaction. * It eagerly initialize parent classloaders. * * @return the local ClassLoader for the given type and id * @throws SClassLoaderException Error thrown if it's impossible to get a local ClassLoader for the given type and * id * @param identifier of the classloader to refresh */ ClassLoader getClassLoader(ClassLoaderIdentifier identifier) throws SClassLoaderException; void removeLocalClassloader(ClassLoaderIdentifier identifier) throws SClassLoaderException; /** * add listener on a classloader * * @param identifier the classloader id * @param singleClassLoaderListener the listener to add * @return true if the listener was added */ boolean addListener(ClassLoaderIdentifier identifier, SingleClassLoaderListener singleClassLoaderListener); /** * @param identifier the classloader id * @param singleClassLoaderListener classloader listener to remove * @return true if the listener was removed */ boolean removeListener(ClassLoaderIdentifier identifier, SingleClassLoaderListener singleClassLoaderListener); void refreshClassLoaderAfterUpdate(ClassLoaderIdentifier identifier) throws SClassLoaderException; void refreshClassLoaderOnOtherNodes(ClassLoaderIdentifier identifier) throws SClassLoaderException; /** * This method refreshes in the current thread/transaction the classLoader with the given identifier. * Contrary to refreshClassLoaderImmediately, it creates a synchronization that triggers a reload of the classloader * in case the transaction was rolled back, insuring there is no new loaded class in the classloader after the * rollback. *

      * e.g. If the classloader was set as the current context classloader, it should be reset like this * *

           *  {@code
           * Thread.currentThread().setContextClassLoader(classLoaderService.getLocalClassLoader(identifier));
           * }
           * 
      * * @param identifier of the classloader to refresh */ void refreshClassLoaderImmediatelyWithRollback(ClassLoaderIdentifier identifier) throws SClassLoaderException; /** * This method refreshes in the current thread/transaction the classLoader with the given identifier. * It eagerly initializes parents classloaders. *

      * A new classloader will be created. In order to use the new classloader, references to the old one should be * updated. *

      * e.g. If the classloader was set as the current context classloader, it should be reset like this * *

           *  {@code
           * Thread.currentThread().setContextClassLoader(classLoaderService.getLocalClassLoader(identifier));
           * }
           * 
      * * @param identifier of the classloader to refresh */ void refreshClassLoaderImmediately(ClassLoaderIdentifier identifier) throws SClassLoaderException; void removeRefreshClassLoaderSynchronization(); } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/ClassLoaderServiceImpl.java ================================================ /** * 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.classloader; import java.io.IOException; import java.net.URI; 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.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import java.util.function.Function; import java.util.stream.Stream; import javax.transaction.Status; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.NullCheckingUtil; import org.bonitasoft.engine.dependency.SDependencyException; import org.bonitasoft.engine.dependency.impl.PlatformDependencyService; import org.bonitasoft.engine.dependency.impl.TenantDependencyService; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.bonitasoft.engine.home.BonitaHomeServer; import org.bonitasoft.engine.home.BonitaResource; import org.bonitasoft.engine.service.BroadcastService; import org.bonitasoft.engine.service.TaskResult; import org.bonitasoft.engine.transaction.BonitaTransactionSynchronization; import org.bonitasoft.engine.transaction.STransactionNotFoundException; import org.bonitasoft.engine.transaction.UserTransactionService; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; /** * @author Elias Ricken de Medeiros * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Slf4j @Component("classLoaderService") //id used by RefreshClassLoaderTask and @InjectedService public class ClassLoaderServiceImpl implements ClassLoaderService { private final Object synchroLock = new Object(); private final ThreadLocal currentRefreshTask = new ThreadLocal<>(); private final ParentClassLoaderResolver parentClassLoaderResolver; private final Map classLoaders = new ConcurrentHashMap<>(); private final Set platformClassLoaderListeners = new HashSet<>(); private final Map> singleClassLoaderListenersMap = Collections .synchronizedMap(new HashMap<>()); private boolean shuttingDown = false; private final EventService eventService; private final PlatformDependencyService platformDependencyService; private TenantDependencyService dependencyService; private final UserTransactionService userTransactionService; private final BroadcastService broadcastService; private final ClassLoaderUpdater classLoaderUpdater; public ClassLoaderServiceImpl(final ParentClassLoaderResolver parentClassLoaderResolver, @Qualifier("platformEventService") EventService eventService, PlatformDependencyService platformDependencyService, UserTransactionService userTransactionService, BroadcastService broadcastService, ClassLoaderUpdater classLoaderUpdater, List platformClassLoaderListeners) { this.parentClassLoaderResolver = parentClassLoaderResolver; this.eventService = eventService; this.platformDependencyService = platformDependencyService; this.userTransactionService = userTransactionService; this.broadcastService = broadcastService; this.classLoaderUpdater = classLoaderUpdater; this.platformClassLoaderListeners.addAll(platformClassLoaderListeners); } @Override public void registerDependencyService(TenantDependencyService tenantDependencyService) { dependencyService = tenantDependencyService; } private void warnOnShuttingDown(final ClassLoaderIdentifier key) { if (shuttingDown) { log.warn("Using local classloader after ClassLoaderService has already shut down: {}", key); } } @Override public BonitaClassLoader getClassLoader(ClassLoaderIdentifier id) { NullCheckingUtil.checkArgsNotNull(id); log.trace("Get classloader {}", id); warnOnShuttingDown(id); // when the classloader is initialized, it is done in another thread/transaction // We might not need to do so. return getOrInitializeClassloader(id, k -> classLoaderUpdater.initializeClassLoader(this, k)); } public BonitaClassLoader getOrInitializeClassloader(ClassLoaderIdentifier id, Function createClassloaderFunction) { if (!classLoaders.containsKey(id)) { log.trace("getOrInitializeClassloader: classloader not found {}, it will be created", id); //computed before to avoid having nested "computeIfAbsent" BonitaClassLoader newClassLoader = createClassloaderFunction.apply(id); BonitaClassLoader classLoader = classLoaders.computeIfAbsent(id, k -> newClassLoader); if (!classLoader.equals(newClassLoader)) { log.debug("Due to concurrent initialization, the Classloader created here {} will not be used " + "and will be destroyed. {} is the one that will be used", newClassLoader, classLoader); newClassLoader.destroy(); } return classLoader; } else { return classLoaders.get(id); } } ClassLoader getParentClassLoader(ClassLoaderIdentifier identifier) { final ClassLoaderIdentifier parentIdentifier = parentClassLoaderResolver .getParentClassLoaderIdentifier(identifier); if (ClassLoaderIdentifier.APPLICATION.equals(parentIdentifier)) { // Application classloader is the one bootstrapping bonita platform return ClassLoaderServiceImpl.class.getClassLoader(); } else { //get or initialize parent in the same thread/transaction return getOrInitializeClassloader(parentIdentifier, k -> { try { return createClassloader(k); } catch (IOException | SClassLoaderException e) { throw new BonitaRuntimeException(e); } }); } } @Override public void removeLocalClassloader(ClassLoaderIdentifier identifier) throws SClassLoaderException { NullCheckingUtil.checkArgsNotNull(identifier); log.debug("Removing local classloader with {}", identifier); BonitaClassLoader localClassLoader = classLoaders.get(identifier); if (localClassLoader != null) { destroyAndRemoveClassLoader(localClassLoader); notifyDestroyed(localClassLoader); } } private void destroyAndRemoveClassLoader(BonitaClassLoader localClassLoader) throws SClassLoaderException { if (localClassLoader.hasChildren()) { throw new SClassLoaderException( "Unable to delete classloader " + localClassLoader.getIdentifier() + " because it has children: " + localClassLoader.getChildren()); } localClassLoader.destroy(); classLoaders.remove(localClassLoader.getIdentifier()); } private List getClassLoaderTreeLeavesFirst(BonitaClassLoader root) { List tree = new ArrayList<>(); for (BonitaClassLoader child : root.getChildren()) { tree.addAll(getClassLoaderTreeLeavesFirst(child)); } tree.add(root); return tree; } URI getLocalTemporaryFolder(ClassLoaderIdentifier identifier) throws IOException { if (identifier.isGlobalClassloader() || identifier.isTenantClassloader()) { // For tenant and platform (=GLOBAL), no need to have a sub-folder with the identifier: return BonitaHomeServer.getInstance().getLocalTemporaryFolder(identifier.getType().name()); } else { // For process-level classloader, store jar files in sub-folder named with the identifier: return BonitaHomeServer.getInstance().getLocalTemporaryFolder(identifier.getType().name(), identifier.getId()); } } BonitaClassLoader createClassloader(ClassLoaderIdentifier id) throws IOException, SClassLoaderException { log.debug("Creating classloader {}", id); BonitaClassLoader classLoader = BonitaClassLoaderFactory.createClassLoader(getDependencies(id), id, getLocalTemporaryFolder(id), getParentClassLoader(id)); log.info("Created classloader {}: {}", id, classLoader); return classLoader; } @Override public void start() { log.debug("Starting classloader service, creating the platform classloader"); shuttingDown = false; //we do not create or destroy the global classloader because it does not point to a bonita classloader } @Override public void stop() { log.debug("Stopping classloader service, destroying all classloaders"); shuttingDown = true; destroyAllLocalClassLoaders(); } private void destroyAllLocalClassLoaders() { log.debug("Destroying all classloaders"); //remove elements only that don't have children //there is no loop in this so the algorithm finishes BonitaClassLoader global = classLoaders.get(ClassLoaderIdentifier.GLOBAL); if (global == null) { log.debug("No ClassLoaders to destroy"); return; } //destroy classloader starting from the leaves to avoid checking for children List allClassLoaders = getClassLoaderTreeLeavesFirst(global); for (BonitaClassLoader currentClassLoader : allClassLoaders) { currentClassLoader.destroy(); notifyDestroyed(currentClassLoader); if (classLoaders.remove(currentClassLoader.getIdentifier()) == null) { log.warn("One classloader of the tree is not present in the list: classloader = {} and list = {}", currentClassLoader, classLoaders); } } //the classloader map should be empty at this point. Clearing it to ensure that we never reuse old classloader in that case if (!classLoaders.isEmpty()) { log.warn("Classloader tree was destroyed but some classloaders were still references in the map {}", classLoaders); } classLoaders.clear(); } @Override public boolean addListener(ClassLoaderIdentifier identifier, SingleClassLoaderListener singleClassLoaderListener) { log.debug("Added listener {} on {}", singleClassLoaderListener, identifier.toString()); return getListeners(identifier).add(singleClassLoaderListener); } @Override public boolean removeListener(ClassLoaderIdentifier identifier, SingleClassLoaderListener singleClassLoaderListener) { log.debug("Removed listener {} on {}", singleClassLoaderListener, identifier.toString()); return getListeners(identifier).remove(singleClassLoaderListener); } Set getListeners(ClassLoaderIdentifier identifier) { return this.singleClassLoaderListenersMap.computeIfAbsent(identifier, k -> new HashSet<>()); } /** * Notify listeners that the classloader was destroyed * That method do not notify children because we can't destroy a classloader that have children */ private void notifyDestroyed(BonitaClassLoader classLoader) { getListeners(classLoader.getIdentifier()).forEach(l -> { log.debug("Notify listener that classloader {} was destroyed: {}", classLoader.getIdentifier(), l); l.onDestroy(classLoader); }); platformClassLoaderListeners.forEach(l -> { log.debug("Notify listener that platform classloader {} was destroyed: {}", classLoader.getIdentifier(), l); l.onDestroy(classLoader); }); } /** * Notify listeners that the classloader was updated * Also notify that children classloader were updated */ void notifyUpdated(BonitaClassLoader newClassLoader) { getListeners(newClassLoader.getIdentifier()).forEach(l -> { log.debug("Notify listener that classloader {} was updated: {}", newClassLoader.getIdentifier(), l); l.onUpdate(newClassLoader); }); platformClassLoaderListeners.forEach(l -> { log.debug("Notify listener that platform classloader {} was updated: {}", newClassLoader.getIdentifier(), l); l.onUpdate(newClassLoader); }); } @Override public void refreshClassLoaderImmediatelyWithRollback(ClassLoaderIdentifier identifier) throws SClassLoaderException { // Register the rollback before refreshing classloader in case refreshClassLoaderImmediately() fails: registerAfterCommitClassloaderUpdate(identifier); refreshClassLoaderImmediately(identifier); } @Override public void refreshClassLoaderImmediately(ClassLoaderIdentifier identifier) throws SClassLoaderException { try { log.info("Refreshing classloader {}", identifier.toString()); BonitaClassLoader newClassloader = createClassloader(identifier); BonitaClassLoader previous = classLoaders.put(identifier, newClassloader); // Destroy and remove all children classloaders of the `previous` classloader. They need to be recreated if (previous != null) { //destroy classloader starting from the leaves to avoid checking for children for (BonitaClassLoader classLoader : getClassLoaderTreeLeavesFirst(previous)) { classLoader.destroy(); notifyDestroyed(classLoader); // remove(key,value) only remove the value if its the one given, // We do that to avoid removing the one we just added to the map classLoaders.remove(classLoader.getIdentifier(), classLoader); } log.debug("Refreshed classloader {}, {} was replaced by {}", identifier, previous, classLoaders); } else { log.debug("Refreshed classloader {}, There was no classloader, classloader set: {}", identifier, classLoaders); } notifyUpdated(newClassloader); final SEvent event = new SEvent("ClassLoaderRefreshed"); event.setObject(identifier); eventService.fireEvent(event); } catch (Exception e) { throw new SClassLoaderException(e); } } private void registerAfterCommitClassloaderUpdate(ClassLoaderIdentifier identifier) throws SClassLoaderException { try { userTransactionService.registerBonitaSynchronization((BonitaTransactionSynchronization) i -> { if (i != Status.STATUS_COMMITTED) { log.warn( "The transaction was not committed. Refreshing classloader on platform to return to a clean state."); classLoaderUpdater.refreshClassloaders(this, Collections.singleton(identifier)); } }); } catch (STransactionNotFoundException e) { throw new SClassLoaderException(e); } } Stream getDependencies(ClassLoaderIdentifier identifier) throws SClassLoaderException { Stream resources; try { if (ScopeType.GLOBAL == identifier.getType()) { resources = platformDependencyService.getDependenciesResources(identifier.getType(), identifier.getId()); } else { if (dependencyService == null) { log.warn("No dependency service is initialized. Initializing empty classloader"); return Stream.empty(); } resources = dependencyService.getDependenciesResources(identifier.getType(), identifier.getId()); } } catch (SDependencyException e) { throw new SClassLoaderException(e); } return resources; } @Override public void refreshClassLoaderAfterUpdate(ClassLoaderIdentifier identifier) throws SClassLoaderException { try { registerRefreshOnAllNodes(identifier); } catch (STransactionNotFoundException e) { throw new SClassLoaderException(e); } } @Override public void refreshClassLoaderOnOtherNodes(ClassLoaderIdentifier identifier) throws SClassLoaderException { try { userTransactionService .registerBonitaSynchronization((BonitaTransactionSynchronization) transactionState -> { if (transactionState != Status.STATUS_COMMITTED) { return; } Map> execute; try { execute = broadcastService.executeOnOthersAndWait(new RefreshClassLoaderTask(identifier)); } catch (TimeoutException | ExecutionException | InterruptedException e) { throw new BonitaRuntimeException(e); } for (Map.Entry> resultEntry : execute.entrySet()) { if (resultEntry.getValue().isError()) { throw new IllegalStateException(resultEntry.getValue().getThrowable()); } } }); } catch (STransactionNotFoundException e) { throw new SClassLoaderException(e); } } private void registerRefreshOnAllNodes(ClassLoaderIdentifier identifier) throws STransactionNotFoundException { synchronized (synchroLock) { RefreshClassloaderSynchronization refreshTaskSynchronization = currentRefreshTask.get(); if (refreshTaskSynchronization == null) { RefreshClassLoaderTask callable = new RefreshClassLoaderTask(identifier); refreshTaskSynchronization = new RefreshClassloaderSynchronization(this, broadcastService, callable, classLoaderUpdater, identifier); userTransactionService.registerBonitaSynchronization(refreshTaskSynchronization); currentRefreshTask.set(refreshTaskSynchronization); } else { refreshTaskSynchronization.addClassloaderToRefresh(identifier); } } } @Override public void removeRefreshClassLoaderSynchronization() { currentRefreshTask.remove(); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/ClassLoaderUpdater.java ================================================ /** * 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.classloader; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import javax.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.service.BonitaTaskExecutor; import org.bonitasoft.engine.transaction.UserTransactionService; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * This class is responsible for triggering the refresh of classloaders in separated threads/transactions * It is useful when refreshing classloader at the end of the transaction when one transaction is still active and * we need to open a new one. */ @Slf4j @Component class ClassLoaderUpdater { final static long DEFAULT_CLASSLOADER_INITIALIZATION_TIMEOUT_MINUTES = 5L; private final BonitaTaskExecutor bonitaTaskExecutor; private final UserTransactionService userTransactionService; @Value("${bonita.runtime.classloader.initialization.timeout-minutes:-1}") private long classLoaderInitializationTimeoutMinutes; public ClassLoaderUpdater(BonitaTaskExecutor bonitaTaskExecutor, UserTransactionService userTransactionService) { this.bonitaTaskExecutor = bonitaTaskExecutor; this.userTransactionService = userTransactionService; } public void refreshClassloaders(ClassLoaderServiceImpl classLoaderService, Set ids) { execute(() -> { for (ClassLoaderIdentifier id : ids) { classLoaderService.refreshClassLoaderImmediately(id); } return null; }); } BonitaClassLoader initializeClassLoader(ClassLoaderServiceImpl classLoaderService, ClassLoaderIdentifier identifier) { log.debug("Request creation of classloader in an other thread: {}. A {} minutes timeout will be used.", identifier, classLoaderInitializationTimeoutMinutes); return execute(() -> classLoaderService.createClassloader(identifier)); } private T execute(Callable callable) { Future execute = bonitaTaskExecutor.execute(inTransaction(callable)); try { return execute.get(classLoaderInitializationTimeoutMinutes, TimeUnit.MINUTES); } catch (InterruptedException | ExecutionException e) { throw new SBonitaRuntimeException("Unable to refresh the classloaders", e); } catch (TimeoutException toe) { throw new SBonitaRuntimeException("Unable to refresh the classloaders within " + classLoaderInitializationTimeoutMinutes + " minutes. You may want to adjust the timeout using either the " + "bonita.runtime.classloader.initialization.timeout-minutes property or the " + "BONITA_RUNTIME_CLASSLOADER_INITIALIZATION_TIMEOUT_MINUTES environment variable." + "Make sure the value is within [1..XA_TRANSACTION_TIMEOUT].", toe); } } private Callable inTransaction(Callable callable) { return () -> userTransactionService.executeInTransaction(callable); } @PostConstruct void validateClassLoaderInitializationTimeoutMinutes() { if (classLoaderInitializationTimeoutMinutes <= 0) { classLoaderInitializationTimeoutMinutes = DEFAULT_CLASSLOADER_INITIALIZATION_TIMEOUT_MINUTES; } log.debug( "Using a timeout of {} minutes for class loader initialization or refresh. This can be configured " + "via the bonita.runtime.classloader.initialization.timeout-minutes property or the " + "BONITA_RUNTIME_CLASSLOADER_INITIALIZATION_TIMEOUT_MINUTES environment variable. " + "Make sure the value is within [1..XA_TRANSACTION_TIMEOUT].", classLoaderInitializationTimeoutMinutes); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/ParentClassLoaderResolver.java ================================================ /** * 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.classloader; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.exception.BonitaRuntimeException; import org.springframework.stereotype.Component; @Component public class ParentClassLoaderResolver { /** * @return the key of the parent or null if it is the global */ public ClassLoaderIdentifier getParentClassLoaderIdentifier(ClassLoaderIdentifier childId) { if (ScopeType.PROCESS.equals(childId.getType())) { // We should not depend on the session to know what is the parent of a classloader return ClassLoaderIdentifier.TENANT; } else if (ScopeType.TENANT.equals(childId.getType())) { return ClassLoaderIdentifier.GLOBAL; } else if (ClassLoaderIdentifier.GLOBAL.equals(childId)) { return ClassLoaderIdentifier.APPLICATION; } else { throw new BonitaRuntimeException("unable to find a parent for type: " + childId); } } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/PlatformClassLoaderListener.java ================================================ /** * 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.classloader; /** * Listen to events happening to all classloaders (all tenants, platform-wide) */ public interface PlatformClassLoaderListener { void onUpdate(ClassLoader newClassLoader); void onDestroy(ClassLoader oldClassLoader); } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/RefreshClassLoaderTask.java ================================================ /** * 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.classloader; import java.io.Serializable; import java.util.concurrent.Callable; import org.bonitasoft.engine.service.InjectedService; import org.bonitasoft.engine.transaction.TransactionService; /** * @author Baptiste Mesta */ public class RefreshClassLoaderTask implements Callable, Serializable { private final ClassLoaderIdentifier id; private ClassLoaderService classLoaderService; private TransactionService transactionService; public RefreshClassLoaderTask(ClassLoaderIdentifier id) { this.id = id; } @Override public Void call() throws Exception { transactionService.executeInTransaction(() -> { getClassLoaderService().refreshClassLoaderImmediately(id); return null; }); return null; } public ClassLoaderService getClassLoaderService() { return classLoaderService; } @InjectedService public void setClassLoaderService(ClassLoaderService classLoaderService) { this.classLoaderService = classLoaderService; } @InjectedService public void setTransactionService(TransactionService transactionService) { this.transactionService = transactionService; } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/RefreshClassloaderSynchronization.java ================================================ /** * 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.classloader; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import javax.transaction.Status; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.service.BroadcastService; import org.bonitasoft.engine.service.TaskResult; import org.bonitasoft.engine.transaction.BonitaTransactionSynchronization; /** * @author Baptiste Mesta */ class RefreshClassloaderSynchronization implements BonitaTransactionSynchronization { private final ClassLoaderServiceImpl classLoaderService; private final ClassLoaderUpdater classLoaderUpdater; private final BroadcastService broadcastService; private final RefreshClassLoaderTask callable; private final Set identifiers = new HashSet<>(); public RefreshClassloaderSynchronization(ClassLoaderServiceImpl classLoaderService, BroadcastService broadcastService, RefreshClassLoaderTask callable, ClassLoaderUpdater classLoaderUpdater, ClassLoaderIdentifier identifier) { this.classLoaderService = classLoaderService; this.classLoaderUpdater = classLoaderUpdater; this.broadcastService = broadcastService; this.callable = callable; addClassloaderToRefresh(identifier); } @Override public void afterCompletion(final int txState) { classLoaderService.removeRefreshClassLoaderSynchronization(); if (txState == Status.STATUS_COMMITTED) { //we use the ClassLoaderUpdater to refresh those classloader in an other thread/transaction. //This can't be done in the current thread because we are still executing afterCompletion transactionSync classLoaderUpdater.refreshClassloaders(classLoaderService, identifiers); refreshClassLoaderOnOtherNodes(); } } private void refreshClassLoaderOnOtherNodes() { try { Map> execute = broadcastService.executeOnOthersAndWait(callable); for (Map.Entry> resultEntry : execute.entrySet()) { if (resultEntry.getValue().isError()) { throw new IllegalStateException(resultEntry.getValue().getThrowable()); } } } catch (InterruptedException | ExecutionException | TimeoutException e) { throw new SBonitaRuntimeException("Unable to refresh the classloaders on all nodes: " + identifiers, e); } } //Testing purpose only Set getIdentifiers() { return identifiers; } void addClassloaderToRefresh(ClassLoaderIdentifier id) { identifiers.add(id); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/SClassLoaderException.java ================================================ /** * 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.classloader; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class SClassLoaderException extends SBonitaException { private static final long serialVersionUID = 6760479336490227757L; public SClassLoaderException(final String message) { super(message); } public SClassLoaderException(final Throwable t) { super(t); } public SClassLoaderException(final String message, final Exception e) { super(message, e); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/SingleClassLoaderListener.java ================================================ /** * 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.classloader; /** * Listens to events occurring on a single tenant-level classloader. * * @author Baptiste Mesta */ public interface SingleClassLoaderListener { default void onUpdate(ClassLoader newClassLoader) { } default void onDestroy(ClassLoader oldClassLoader) { } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/listeners/ClassReflectorClearer.java ================================================ /** * 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.classloader.listeners; import org.bonitasoft.engine.classloader.PlatformClassLoaderListener; import org.bonitasoft.engine.commons.ClassReflector; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta */ @Component public class ClassReflectorClearer implements PlatformClassLoaderListener { @Override public void onUpdate(ClassLoader newClassLoader) { ClassReflector.clearCache(); } @Override public void onDestroy(ClassLoader oldClassLoader) { ClassReflector.clearCache(); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/classloader/listeners/JacksonCacheClearer.java ================================================ /** * 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.classloader.listeners; import com.fasterxml.jackson.databind.type.TypeFactory; import org.bonitasoft.engine.classloader.PlatformClassLoaderListener; import org.springframework.stereotype.Component; @Component public class JacksonCacheClearer implements PlatformClassLoaderListener { @Override public void onUpdate(ClassLoader newClassLoader) { TypeFactory.defaultInstance().clearCache(); } @Override public void onDestroy(ClassLoader oldClassLoader) { TypeFactory.defaultInstance().clearCache(); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/ArtifactAccessor.java ================================================ /** * 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.dependency; import org.bonitasoft.engine.dependency.model.ScopeType; public interface ArtifactAccessor { boolean artifactExists(ScopeType scopeType, long artifactId); } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/DependencyService.java ================================================ /** * 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.dependency; import java.util.Collection; import java.util.List; import java.util.stream.Stream; import org.bonitasoft.engine.dependency.model.AbstractSDependency; import org.bonitasoft.engine.dependency.model.DependencyContent; import org.bonitasoft.engine.dependency.model.SDependencyMapping; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.home.BonitaResource; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Matthieu Chaffotte * @author Celine Souchet * @since 6.0 */ public interface DependencyService { String DEPENDENCY = "DEPENDENCY"; String DEPENDENCYMAPPING = "DEPENDENCYMAPPING"; /** * Delete the specific dependency * * @param dependency * The dependency will be deleted * @throws SDependencyNotFoundException * Error thrown if the dependency not found. * @throws SDependencyDeletionException * Error thrown if has exception during the dependency deletion. */ void deleteDependency(AbstractSDependency dependency) throws SDependencyException; /** * Delete the dependency specified by name * * @param name * @throws SDependencyNotFoundException * Error thrown if no dependency have a name corresponding to the parameter. * @throws SDependencyDeletionException * Error thrown if has exception during the dependency deletion. */ void deleteDependency(String name) throws SDependencyException; /** * Get dependency by its id * * @param id * Identifier of dependency * @return * @throws SDependencyNotFoundException * Error thrown if no dependency have an id corresponding to the parameter. */ AbstractSDependency getDependency(long id) throws SDependencyNotFoundException; /** * Get only the content and file name of a dependency. * This object will not be connected to the hibernate session and therefore will avoid * issues related to dirty checking mechanism, see https://bonitasoft.atlassian.net/browse/BS-19262 * * @param id of the dependency * @return an object containing the file content and name * @throws SDependencyNotFoundException */ DependencyContent getDependencyContentOnly(long id) throws SDependencyNotFoundException, SBonitaReadException; /** * Get dependencies for the specified ids * * @param ids * Identifiers of dependencies * @return a list of SDependency object * @throws SDependencyException */ List getDependencies(Collection ids) throws SDependencyException; /** * Get all dependencyMappings for specific the queryOptions * * @param queryOptions * QueryOptions object, it contains some query conditions. * @return a list of SDependencyMapping objects * @throws SDependencyException */ List getDependencyMappings(QueryOptions queryOptions) throws SDependencyException; Stream getDependenciesResources(ScopeType type, long id) throws SDependencyException; /** * Get all dependency ids for specific artifact * * @param artifactId * Identifier of artifact * @param artifactType * Type of artifact * @param startIndex * @param maxResult * @return a list of Long objects * @throws SDependencyException */ List getDependencyIds(long artifactId, ScopeType artifactType, int startIndex, int maxResult) throws SDependencyException; /** * @param id * @param type * @throws SDependencyException */ void deleteDependencies(long id, ScopeType type) throws SDependencyException; AbstractSDependency createMappedDependency(String name, byte[] jarContent, String fileName, long artifactId, ScopeType scopeType) throws SDependencyException; AbstractSDependency updateDependencyOfArtifact(String name, byte[] jarContent, String fileName, long artifactId, ScopeType scopeType) throws SDependencyException; AbstractSDependency getDependencyOfArtifact(long artifactId, ScopeType artifactType, String fileName) throws SBonitaReadException; } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/SDependencyAlreadyExistsException.java ================================================ /** * 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.dependency; /** * @author Matthieu Chaffotte */ public class SDependencyAlreadyExistsException extends SDependencyException { private static final long serialVersionUID = -4974628261438631313L; public SDependencyAlreadyExistsException(final String message) { super(message); } public SDependencyAlreadyExistsException(final String message, final Throwable cause) { super(message, cause); } public SDependencyAlreadyExistsException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/SDependencyCreationException.java ================================================ /** * 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.dependency; /** * @author Matthieu Chaffotte */ public class SDependencyCreationException extends SDependencyException { private static final long serialVersionUID = 6262111905648333928L; public SDependencyCreationException(final String message) { super(message); } public SDependencyCreationException(final String message, final Throwable cause) { super(message, cause); } public SDependencyCreationException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/SDependencyDeletionException.java ================================================ /** * 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.dependency; /** * @author Matthieu Chaffotte */ public class SDependencyDeletionException extends SDependencyException { private static final long serialVersionUID = 5542868086645864234L; public SDependencyDeletionException(final String message) { super(message); } public SDependencyDeletionException(final String message, final Throwable cause) { super(message, cause); } public SDependencyDeletionException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/SDependencyException.java ================================================ /** * 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.dependency; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SDependencyException extends SBonitaException { private static final long serialVersionUID = 8403122121819176217L; public SDependencyException(final String message) { super(message); } public SDependencyException(final String message, final Throwable cause) { super(message, cause); } public SDependencyException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/SDependencyMappingNotFoundException.java ================================================ /** * 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.dependency; /** * @author Matthieu Chaffotte */ public class SDependencyMappingNotFoundException extends SDependencyException { private static final long serialVersionUID = -1886189816198433722L; public SDependencyMappingNotFoundException(final String message) { super(message); } public SDependencyMappingNotFoundException(final String message, final Throwable cause) { super(message, cause); } public SDependencyMappingNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/SDependencyNotFoundException.java ================================================ /** * 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.dependency; /** * @author Matthieu Chaffotte */ public class SDependencyNotFoundException extends SDependencyException { private static final long serialVersionUID = 6276746788016462991L; public SDependencyNotFoundException(final String message) { super(message); } public SDependencyNotFoundException(final String message, final Throwable cause) { super(message, cause); } public SDependencyNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/impl/AbstractDependencyService.java ================================================ /** * 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.dependency.impl; import static org.bonitasoft.engine.home.BonitaResource.resource; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Stream; import org.bonitasoft.engine.commons.NullCheckingUtil; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.dependency.DependencyService; import org.bonitasoft.engine.dependency.SDependencyDeletionException; import org.bonitasoft.engine.dependency.SDependencyException; import org.bonitasoft.engine.dependency.SDependencyNotFoundException; import org.bonitasoft.engine.dependency.model.AbstractSDependency; import org.bonitasoft.engine.dependency.model.SAbstractDependencyMapping; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.home.BonitaResource; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; /** * @author Baptiste Mesta */ public abstract class AbstractDependencyService implements DependencyService { protected static final int BATCH_SIZE = 100; private ReadPersistenceService persistenceService; public AbstractDependencyService(ReadPersistenceService persistenceService) { this.persistenceService = persistenceService; } protected abstract void delete(AbstractSDependency dependency) throws SDependencyDeletionException; protected abstract List getDependencies(QueryOptions queryOptions) throws SDependencyException; protected abstract AbstractSDependency getDependency(String name) throws SDependencyNotFoundException, SDependencyDeletionException; @Override public void deleteDependency(final String name) throws SDependencyException { deleteDependency(getDependency(name)); } @Override public Stream getDependenciesResources(final ScopeType type, final long id) throws SDependencyException { List dependencyIds = getDependencyIds(id, type, 0, Integer.MAX_VALUE); return dependencyIds.stream() .map(dependencyId -> { try { // get only the content of the dependency to avoid having connected objects return getDependencyContentOnly(dependencyId); } catch (SDependencyNotFoundException | SBonitaReadException e) { throw new SBonitaRuntimeException(e); } }) .map(dependency -> resource(dependency.getFileName(), dependency.getContent())); } protected abstract void createDependencyMapping(SAbstractDependencyMapping dependencyMapping) throws SDependencyException; protected abstract void deleteDependencyMapping(SAbstractDependencyMapping dependencyMapping) throws SDependencyException; protected abstract List getDependencyMappings(long dependencyId, QueryOptions queryOptions) throws SDependencyException; @Override public List getDependencyIds(final long artifactId, final ScopeType artifactType, final int startIndex, final int maxResult) throws SDependencyException { NullCheckingUtil.checkArgsNotNull(artifactId, artifactType, startIndex, maxResult); final QueryOptions queryOptions = new QueryOptions(startIndex, maxResult); try { final Map parameters = new HashMap<>(); parameters.put("artifactId", artifactId); parameters.put("artifactType", artifactType); final SelectListDescriptor desc = getSelectDescriptorForDependencyIds(queryOptions, parameters); return persistenceService.selectList(desc); } catch (final SBonitaReadException e) { throw new SDependencyException("Can't get dependencies", e); } } protected abstract SelectListDescriptor getSelectDescriptorForDependencyIds(QueryOptions queryOptions, Map parameters); protected abstract QueryOptions getDefaultQueryOptionForDependencyMapping(); @Override public void deleteDependency(final AbstractSDependency dependency) throws SDependencyException { for (SAbstractDependencyMapping dependencyMapping : getDependencyMappings(dependency.getId(), getDefaultQueryOptionForDependencyMapping())) { deleteDependencyMapping(dependencyMapping); } delete(dependency); } @Override public void deleteDependencies(final long id, final ScopeType type) throws SDependencyException { int fromIndex = 0; List dependencyIds = getDependencyIds(id, type, fromIndex, BATCH_SIZE); while (!dependencyIds.isEmpty()) { for (final Long dependencyId : dependencyIds) { final List dependencyMappings = getDependencyMappings(dependencyId, getDefaultQueryOptionForDependencyMapping()); if (dependencyMappings.size() == 1) {// only when the dependency is linked only to on element final SAbstractDependencyMapping dependencyMapping = dependencyMappings.get(0); deleteDependencyMapping(dependencyMapping); deleteDependency(getDependency(dependencyId)); } else { fromIndex++; } } dependencyIds = getDependencyIds(id, type, fromIndex, BATCH_SIZE); } } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/impl/DependencyServiceRegistrator.java ================================================ /** * 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.dependency.impl; import javax.annotation.PostConstruct; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.springframework.stereotype.Component; @Component public class DependencyServiceRegistrator { private final TenantDependencyService tenantDependencyService; private final ClassLoaderService classLoaderService; public DependencyServiceRegistrator(TenantDependencyService tenantDependencyService, ClassLoaderService classLoaderService) { this.tenantDependencyService = tenantDependencyService; this.classLoaderService = classLoaderService; } @PostConstruct private void registerDependencyService() { classLoaderService.registerDependencyService(tenantDependencyService); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/impl/PlatformDependencyService.java ================================================ /** * 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.dependency.impl; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import org.bonitasoft.engine.commons.NullCheckingUtil; import org.bonitasoft.engine.dependency.SDependencyCreationException; import org.bonitasoft.engine.dependency.SDependencyDeletionException; import org.bonitasoft.engine.dependency.SDependencyException; import org.bonitasoft.engine.dependency.SDependencyNotFoundException; import org.bonitasoft.engine.dependency.model.AbstractSDependency; import org.bonitasoft.engine.dependency.model.DependencyContent; import org.bonitasoft.engine.dependency.model.SAbstractDependencyMapping; import org.bonitasoft.engine.dependency.model.SDependency; import org.bonitasoft.engine.dependency.model.SDependencyMapping; import org.bonitasoft.engine.dependency.model.SPlatformDependency; import org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.SPersistenceException; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class PlatformDependencyService extends AbstractDependencyService { private final PersistenceService platformPersistenceService; public PlatformDependencyService(final PersistenceService platformPersistenceService) { super(platformPersistenceService); this.platformPersistenceService = platformPersistenceService; } @Override public List getDependencies(final Collection ids) throws SDependencyException { final Map parameters = Collections.singletonMap("ids", ids); final QueryOptions queryOptions = new QueryOptions(0, ids.size(), SPlatformDependency.class, "id", OrderByType.ASC); try { return platformPersistenceService.selectList(new SelectListDescriptor<>("getPlatformDependenciesById", parameters, SPlatformDependency.class, queryOptions)); } catch (final SBonitaReadException bre) { throw new SDependencyException(bre); } } @Override protected void delete(AbstractSDependency dependency) throws SDependencyDeletionException { try { platformPersistenceService.delete(dependency); } catch (final SPersistenceException pe) { throw new SDependencyDeletionException(pe); } } @Override protected List getDependencies(QueryOptions queryOptions) throws SDependencyException { List dependencies; try { dependencies = platformPersistenceService.selectList(new SelectListDescriptor<>("getPlatformDependencies", Collections.emptyMap(), SPlatformDependency.class, queryOptions)); } catch (final SBonitaReadException bre) { throw new SDependencyException(bre); } return dependencies; } @Override public AbstractSDependency getDependency(final long id) throws SDependencyNotFoundException { final SelectByIdDescriptor selectByIdDescriptor = new SelectByIdDescriptor<>( SPlatformDependency.class, id); try { final SPlatformDependency sDependency = platformPersistenceService.selectById(selectByIdDescriptor); if (sDependency == null) { throw new SDependencyNotFoundException("No dependency exists using id: " + id); } return sDependency; } catch (final SBonitaReadException bre) { throw new SDependencyNotFoundException(bre); } } @Override public DependencyContent getDependencyContentOnly(final long id) throws SDependencyNotFoundException, SBonitaReadException { NullCheckingUtil.checkArgsNotNull(id); SelectOneDescriptor desc = new SelectOneDescriptor<>("getPlatformDependencyContentOnly", Collections.singletonMap("id", id), SPlatformDependency.class, DependencyContent.class); return Optional.ofNullable(platformPersistenceService.selectOne(desc)) .orElseThrow(() -> new SDependencyNotFoundException("Can't get content of dependency with id: " + id)); } @Override protected AbstractSDependency getDependency(final String name) throws SDependencyNotFoundException { final Map parameters = Collections.singletonMap("name", name); try { final SPlatformDependency sDependency = platformPersistenceService.selectOne(new SelectOneDescriptor<>( "getPlatformDependencyByName", parameters, SPlatformDependency.class)); if (sDependency == null) { throw new SDependencyNotFoundException("No dependency exists using name: " + name); } return sDependency; } catch (final SBonitaReadException bre) { throw new SDependencyNotFoundException(bre); } } @Override protected void createDependencyMapping(final SAbstractDependencyMapping dependencyMapping) throws SDependencyException { try { platformPersistenceService.insert(dependencyMapping); } catch (final SPersistenceException pe) { throw new SDependencyException(pe); } } @Override protected void deleteDependencyMapping(final SAbstractDependencyMapping dependencyMapping) throws SDependencyException { try { platformPersistenceService.delete(dependencyMapping); } catch (final SPersistenceException pe) { throw new SDependencyException(pe); } } @Override public List getDependencyMappings(final QueryOptions queryOptions) throws SDependencyException { try { return platformPersistenceService .selectList(new SelectListDescriptor<>("getPlatformDependencyMappings", null, SPlatformDependencyMapping.class, queryOptions)); } catch (final SBonitaReadException e) { throw new SDependencyException("can't get dependency mappings", e); } } @Override protected List getDependencyMappings(final long dependencyId, final QueryOptions queryOptions) throws SDependencyException { try { final Map parameters = new HashMap<>(); parameters.put("dependencyId", dependencyId); final SelectListDescriptor desc = new SelectListDescriptor<>( "getPlatformDependencyMappingsByDependency", parameters, SPlatformDependencyMapping.class, queryOptions); return platformPersistenceService.selectList(desc); } catch (final SBonitaReadException e) { throw new SDependencyException("can't get dependency mappings by dependencyId: " + dependencyId, e); } } @Override protected SelectListDescriptor getSelectDescriptorForDependencyIds(QueryOptions queryOptions, Map parameters) { return new SelectListDescriptor<>("getPlatformDependencyIds", parameters, SPlatformDependency.class, Long.class, queryOptions); } @Override public AbstractSDependency createMappedDependency(String name, byte[] jarContent, String fileName, long artifactId, ScopeType scopeType) throws SDependencyException { final SPlatformDependency sDependency = new SPlatformDependency(name, fileName, jarContent); NullCheckingUtil.checkArgsNotNull(sDependency); try { platformPersistenceService.insert(sDependency); } catch (final SPersistenceException pe) { throw new SDependencyCreationException(pe); } final SPlatformDependencyMapping sDependencyMapping = new SPlatformDependencyMapping(artifactId, scopeType, sDependency.getId()); createDependencyMapping(sDependencyMapping); return sDependency; } @Override public SDependency getDependencyOfArtifact(long artifactId, ScopeType artifactType, String fileName) { return null; } @Override protected QueryOptions getDefaultQueryOptionForDependencyMapping() { return new QueryOptions(0, 100, SPlatformDependencyMapping.class, "id", OrderByType.ASC); } @Override public SDependency updateDependencyOfArtifact(String name, byte[] jarContent, String fileName, long artifactId, ScopeType scopeType) { throw new UnsupportedOperationException("NYI"); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/impl/TenantDependencyService.java ================================================ /** * 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.dependency.impl; import static java.util.Collections.emptyMap; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.CollectionUtil; import org.bonitasoft.engine.commons.NullCheckingUtil; import org.bonitasoft.engine.dependency.SDependencyCreationException; import org.bonitasoft.engine.dependency.SDependencyDeletionException; import org.bonitasoft.engine.dependency.SDependencyException; import org.bonitasoft.engine.dependency.SDependencyNotFoundException; import org.bonitasoft.engine.dependency.model.AbstractSDependency; import org.bonitasoft.engine.dependency.model.DependencyContent; import org.bonitasoft.engine.dependency.model.SAbstractDependencyMapping; import org.bonitasoft.engine.dependency.model.SDependency; import org.bonitasoft.engine.dependency.model.SDependencyMapping; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.dependency.model.builder.SDependencyBuilderFactory; import org.bonitasoft.engine.dependency.model.builder.SDependencyLogBuilder; import org.bonitasoft.engine.dependency.model.builder.SDependencyLogBuilderFactory; import org.bonitasoft.engine.dependency.model.builder.SDependencyMappingLogBuilder; import org.bonitasoft.engine.dependency.model.builder.SDependencyMappingLogBuilderFactory; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class TenantDependencyService extends AbstractDependencyService { private final ReadPersistenceService persistenceService; private final Recorder recorder; private final QueriableLoggerService queriableLoggerService; public TenantDependencyService(final ReadPersistenceService persistenceService, final Recorder recorder, final QueriableLoggerService queriableLoggerService) { super(persistenceService); this.persistenceService = persistenceService; this.recorder = recorder; this.queriableLoggerService = queriableLoggerService; } private SDependencyLogBuilder getQueriableLog(final ActionType actionType, final String message) { final SDependencyLogBuilder logBuilder = BuilderFactory.get(SDependencyLogBuilderFactory.class) .createNewInstance(); initializeLogBuilder(logBuilder, message); updateLog(actionType, logBuilder); return logBuilder; } private void initializeLogBuilder(final T logBuilder, final String message) { logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message); } private void updateLog(final ActionType actionType, final T logBuilder) { logBuilder.setActionType(actionType); } private SDependencyMappingLogBuilder getQueriableLog(final ActionType actionType, final String message, final SAbstractDependencyMapping dependencyMapping) { final SDependencyMappingLogBuilder logBuilder = BuilderFactory.get(SDependencyMappingLogBuilderFactory.class) .createNewInstance(); initializeLogBuilder(logBuilder, message); updateLog(actionType, logBuilder); logBuilder.dependencyId(dependencyMapping.getDependencyId()); logBuilder.objectId(dependencyMapping.getId()); return logBuilder; } @Override protected List getDependencies(QueryOptions queryOptions) throws SDependencyException { List dependencies; try { dependencies = persistenceService.selectList(new SelectListDescriptor<>("getDependencies", null, AbstractSDependency.class, queryOptions)); } catch (final SBonitaReadException e) { throw new SDependencyException("Can't get dependencies", e); } return dependencies; } @Override protected SDependency getDependency(String name) throws SDependencyNotFoundException { final Map parameters = Collections.singletonMap("name", name); final SelectOneDescriptor desc = new SelectOneDescriptor<>("getDependencyByName", parameters, SDependency.class); final SDependency sDependency; try { sDependency = persistenceService.selectOne(desc); } catch (SBonitaReadException e) { throw new SDependencyNotFoundException("Dependency with name " + name + " does not exist."); } if (sDependency == null) { throw new SDependencyNotFoundException("Dependency with name " + name + " does not exist."); } return sDependency; } @Override protected List getDependencyMappings(final long dependencyId, final QueryOptions queryOptions) throws SDependencyException { NullCheckingUtil.checkArgsNotNull(dependencyId, queryOptions); try { final Map parameters = new HashMap<>(); parameters.put("dependencyId", dependencyId); final SelectListDescriptor desc = new SelectListDescriptor<>( "getDependencyMappingsByDependency", parameters, SDependencyMapping.class, queryOptions); return persistenceService.selectList(desc); } catch (final SBonitaReadException e) { throw new SDependencyException("Can't get dependency mappings by dependencyId: " + dependencyId, e); } } @Override protected QueryOptions getDefaultQueryOptionForDependencyMapping() { return new QueryOptions(0, 100, SDependencyMapping.class, "id", OrderByType.ASC); } @Override protected void delete(AbstractSDependency dependency) throws SDependencyDeletionException { NullCheckingUtil.checkArgsNotNull(dependency); final SDependencyLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, "Deleting a dependency named " + dependency.getName()); try { delete(dependency, DEPENDENCY); log(dependency.getId(), SQueriableLog.STATUS_OK, logBuilder, "deleteDependency"); } catch (final SRecorderException e) { log(dependency.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "deleteDependency"); throw new SDependencyDeletionException("Can't delete dependency" + dependency, e); } } private void delete(PersistentObject object, String eventType) throws SRecorderException { recorder.recordDelete(new DeleteRecord(object), eventType); } @Override public void deleteDependencyMapping(final SAbstractDependencyMapping dependencyMapping) throws SDependencyException { NullCheckingUtil.checkArgsNotNull(dependencyMapping); final SDependencyMappingLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, "Deleting a dependency mapping", dependencyMapping); try { delete(dependencyMapping, DEPENDENCYMAPPING); log(dependencyMapping.getId(), SQueriableLog.STATUS_OK, logBuilder, "deleteDependencyMapping"); } catch (final SRecorderException e) { log(dependencyMapping.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "deleteDependencyMapping"); throw new SDependencyException("Can't delete dependency mapping" + dependencyMapping, e); } } @Override public List getDependencies(final Collection ids) throws SDependencyException { NullCheckingUtil.checkArgsNotNull(ids); try { final SelectListDescriptor desc = new SelectListDescriptor<>("getDependenciesByIds", CollectionUtil.buildSimpleMap("ids", ids), SDependency.class, QueryOptions.countQueryOptions()); return persistenceService.selectList(desc); } catch (final SBonitaReadException e) { throw new SDependencyException("Can't get dependencies", e); } } @Override protected SelectListDescriptor getSelectDescriptorForDependencyIds(QueryOptions queryOptions, Map parameters) { if (parameters.get("artifactType") == ScopeType.TENANT) { return new SelectListDescriptor<>("getDependencyIdsForTenant", emptyMap(), SDependencyMapping.class, Long.class, queryOptions); } else { return new SelectListDescriptor<>("getDependencyIds", parameters, SDependencyMapping.class, Long.class, queryOptions); } } @Override public List getDependencyMappings(final QueryOptions queryOptions) throws SDependencyException { NullCheckingUtil.checkArgsNotNull(queryOptions); try { return persistenceService.selectList(new SelectListDescriptor<>( "getDependencyMappings", null, SDependencyMapping.class, queryOptions)); } catch (final SBonitaReadException e) { throw new SDependencyException("Can't get dependency mappings", e); } } @Override public SDependency getDependency(final long id) throws SDependencyNotFoundException { NullCheckingUtil.checkArgsNotNull(id); try { final SelectByIdDescriptor desc = new SelectByIdDescriptor<>(SDependency.class, id); final SDependency sDependency = persistenceService.selectById(desc); if (sDependency == null) { throw new SDependencyNotFoundException("Can't get dependency with id: " + id); } return sDependency; } catch (final SBonitaReadException e) { throw new SDependencyNotFoundException("Can't get dependency with id: " + id, e); } } @Override public DependencyContent getDependencyContentOnly(final long id) throws SDependencyNotFoundException, SBonitaReadException { NullCheckingUtil.checkArgsNotNull(id); SelectOneDescriptor desc = new SelectOneDescriptor<>("getDependencyContentOnly", Collections.singletonMap("id", id), SDependency.class, DependencyContent.class); return Optional.ofNullable(persistenceService.selectOne(desc)) .orElseThrow(() -> new SDependencyNotFoundException("Can't get content of dependency with id: " + id)); } private void log(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder, final String callerMethodName) { logBuilder.actionScope(String.valueOf(objectId)); logBuilder.actionStatus(sQueriableLogStatus); logBuilder.objectId(objectId); final SQueriableLog log = logBuilder.build(); if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) { queriableLoggerService.log(this.getClass().getName(), callerMethodName, log); } } public SDependency createMappedDependency(String name, byte[] jarContent, String fileName, long artifactId, ScopeType scopeType) throws SDependencyException { final SDependency sDependency = createDependency(name, jarContent, fileName, artifactId, scopeType); createDependencyMapping(artifactId, scopeType, sDependency); return sDependency; } public SDependency updateDependencyOfArtifact(String name, byte[] jarContent, String fileName, long artifactId, ScopeType scopeType) throws SDependencyException { try { final SDependency sDependency = getDependencyOfArtifact(artifactId, scopeType, fileName); if (sDependency == null) { throw new SDependencyNotFoundException("unable to find dependency " + fileName + " on artifact: " + artifactId + " with type " + scopeType); } recorder.recordUpdate( UpdateRecord.buildSetFields(sDependency, Collections.singletonMap("value_", jarContent)), DEPENDENCY); return sDependency; } catch (SBonitaReadException | SRecorderException e) { throw new SDependencyException(e); } } private SDependency createDependency(String name, byte[] jarContent, String fileName, long artifactId, ScopeType scopeType) throws SDependencyCreationException { final SDependency sDependency = new SDependencyBuilderFactory().createNewInstance(name, artifactId, scopeType, fileName, jarContent); final SDependencyLogBuilder logBuilder = getQueriableLog(ActionType.CREATED, "Creating a dependency with name " + sDependency.getName()); NullCheckingUtil.checkArgsNotNull(sDependency); try { insert(sDependency, DEPENDENCY); } catch (final SRecorderException e) { log(sDependency.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "createDependency"); throw new SDependencyCreationException("Can't create dependency " + sDependency, e); } return sDependency; } private void insert(PersistentObject object, String eventType) throws SRecorderException { recorder.recordInsert(new InsertRecord(object), eventType); } private void createDependencyMapping(long artifactId, ScopeType scopeType, SDependency sDependency) throws SDependencyException { final SDependencyMapping sDependencyMapping = new SDependencyMapping(artifactId, scopeType, sDependency.getId()); createDependencyMapping(sDependencyMapping); } @Override protected void createDependencyMapping(SAbstractDependencyMapping dependencyMapping) throws SDependencyException { final SDependencyMappingLogBuilder logBuilder1 = getQueriableLog(ActionType.CREATED, "Creating a dependency mapping", dependencyMapping); NullCheckingUtil.checkArgsNotNull(dependencyMapping); try { insert(dependencyMapping, DEPENDENCYMAPPING); log(dependencyMapping.getId(), SQueriableLog.STATUS_OK, logBuilder1, "createDependencyMapping"); } catch (final SRecorderException e) { log(dependencyMapping.getId(), SQueriableLog.STATUS_FAIL, logBuilder1, "createDependencyMapping"); throw new SDependencyException("Can't create dependency mapping" + dependencyMapping, e); } } @Override public SDependency getDependencyOfArtifact(long artifactId, ScopeType artifactType, String fileName) throws SBonitaReadException { if (artifactType == ScopeType.TENANT) { return getTenantDependencyByFilename(fileName); } else { final Map inputParameters = new HashMap<>(3); inputParameters.put("artifactId", artifactId); inputParameters.put("artifactType", artifactType); inputParameters.put("fileName", fileName); return persistenceService .selectOne( new SelectOneDescriptor<>("getDependencyOfArtifact", inputParameters, SDependency.class)); } } public SDependency getTenantDependencyByFilename(String fileName) throws SBonitaReadException { final Map inputParameters = new HashMap<>(); inputParameters.put("fileName", fileName); return persistenceService .selectOne(new SelectOneDescriptor<>("getTenantDependencyByFilename", inputParameters, SDependency.class)); } public Optional getIdOfDependencyOfArtifactForTenant(String fileName) throws SBonitaReadException { final Map inputParameters = new HashMap<>(3); inputParameters.put("fileName", fileName); Long idOfDependencyOfArtifact = persistenceService.selectOne( new SelectOneDescriptor<>("getIdOfDependencyOfArtifactForTenant", inputParameters, SDependency.class)); return Optional.ofNullable(idOfDependencyOfArtifact); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/AbstractSDependency.java ================================================ /** * 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.dependency.model; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @AllArgsConstructor @SuperBuilder @MappedSuperclass public abstract class AbstractSDependency implements PersistentObject { public static final String DESCRIPTION = "description"; public static final String FILE_NAME = "fileName"; public static final String ID = "id"; public static final String NAME = "name"; public static final String VALUE = "value_"; @Id private long id; private String name; private String fileName; private String description; @ToString.Exclude @EqualsAndHashCode.Exclude @Getter(AccessLevel.NONE) private byte[] value_; protected AbstractSDependency(final String name, final String fileName, final byte[] value) { super(); this.name = name; this.fileName = fileName; this.value_ = value; } public byte[] getValue() { return value_; } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/DependencyContent.java ================================================ /** * 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.dependency.model; public class DependencyContent { private String fileName; private byte[] content; public DependencyContent(String fileName, byte[] content) { this.fileName = fileName; this.content = content; } public byte[] getContent() { return content; } public void setContent(byte[] content) { this.content = content; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/SAbstractDependencyMapping.java ================================================ /** * 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.dependency.model; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @AllArgsConstructor @SuperBuilder @MappedSuperclass public abstract class SAbstractDependencyMapping implements PersistentObject { @Id private long id; private long artifactId; @Enumerated(EnumType.STRING) private ScopeType artifactType; private long dependencyId; protected SAbstractDependencyMapping(final long artifactId, final ScopeType artifactType, final long dependencyId) { super(); this.artifactId = artifactId; this.artifactType = artifactType; this.dependencyId = dependencyId; } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/SDependency.java ================================================ /** * 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.dependency.model; import javax.persistence.Entity; import javax.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @Data @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @SuperBuilder @Entity @Table(name = "dependency") public class SDependency extends AbstractSDependency { public SDependency(final String name, final String fileName, final byte[] value) { super(name, fileName, value); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/SDependencyMapping.java ================================================ /** * 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.dependency.model; import javax.persistence.Cacheable; import javax.persistence.Entity; import javax.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @Data @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @SuperBuilder @Entity @Table(name = "dependencymapping") @Cacheable(false) public class SDependencyMapping extends SAbstractDependencyMapping { private static final long MEANINGLESS_ID = -1L; public SDependencyMapping(final long artifactId, final ScopeType artifactType, final long dependencyId) { super(artifactId, artifactType, dependencyId); // Need to set it afterwards because the call to super() MUST be the first statement if (ScopeType.TENANT == artifactType) { // If the scope is TENANT, the artifactId is meaningless: setArtifactId(MEANINGLESS_ID); } } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/SPlatformDependency.java ================================================ /** * 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.dependency.model; import javax.persistence.Entity; import javax.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @Data @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @SuperBuilder @Entity @Table(name = "pdependency") public class SPlatformDependency extends AbstractSDependency { public SPlatformDependency(final String name, final String fileName, final byte[] value) { super(name, fileName, value); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/SPlatformDependencyMapping.java ================================================ /** * 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.dependency.model; import javax.persistence.Cacheable; import javax.persistence.Entity; import javax.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; @Data @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @SuperBuilder @Entity @Table(name = "pdependencymapping") @Cacheable(false) public class SPlatformDependencyMapping extends SAbstractDependencyMapping { public SPlatformDependencyMapping(final long artifactId, final ScopeType artifactType, final long dependencyId) { super(artifactId, artifactType, dependencyId); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/ScopeType.java ================================================ /** * 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.dependency.model; /** * @author Celine Souchet */ public enum ScopeType { /** * The dependency is map with a process instance. */ PROCESS, /** * The dependency is map with a tenant. */ TENANT, /** * For the platform dependency. */ GLOBAL } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SDependencyBuilderFactory.java ================================================ /** * 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.dependency.model.builder; import org.bonitasoft.engine.dependency.model.SDependency; import org.bonitasoft.engine.dependency.model.ScopeType; public class SDependencyBuilderFactory { public SDependency createNewInstance(final String name, final long artifactId, final ScopeType artifactType, final String fileName, final byte[] value) { if (artifactType == ScopeType.PROCESS) { return new SDependency(artifactId + "_" + name, fileName, value); } return new SDependency(name, fileName, value); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SDependencyLogBuilder.java ================================================ /** * 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.dependency.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Yanyan Liu */ public interface SDependencyLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SDependencyLogBuilderFactory.java ================================================ /** * 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.dependency.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Yanyan Liu */ public interface SDependencyLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { SDependencyLogBuilder createNewInstance(); } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SDependencyMappingLogBuilder.java ================================================ /** * 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.dependency.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Yanyan Liu */ public interface SDependencyMappingLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { SDependencyMappingLogBuilder dependencyId(final long dependencyId); } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SDependencyMappingLogBuilderFactory.java ================================================ /** * 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.dependency.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Yanyan Liu */ public interface SDependencyMappingLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { SDependencyMappingLogBuilder createNewInstance(); String getDependencyIdKey(); } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SPlatformDependencyBuilder.java ================================================ /** * 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.dependency.model.builder; import org.bonitasoft.engine.dependency.model.SPlatformDependency; public interface SPlatformDependencyBuilder { SPlatformDependencyBuilder setDescription(final String description); SPlatformDependency done(); } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SPlatformDependencyLogBuilderFactory.java ================================================ /** * 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.dependency.model.builder; /** * @author Yanyan Liu */ public interface SPlatformDependencyLogBuilderFactory extends SDependencyLogBuilderFactory { } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/SPlatformDependencyMappingLogBuilderFactory.java ================================================ /** * 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.dependency.model.builder; /** * @author Yanyan Liu */ public interface SPlatformDependencyMappingLogBuilderFactory extends SDependencyMappingLogBuilderFactory { } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SDependencyLogBuilderFactoryImpl.java ================================================ /** * 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.dependency.model.builder.impl; import org.bonitasoft.engine.dependency.model.builder.SDependencyLogBuilder; import org.bonitasoft.engine.dependency.model.builder.SDependencyLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Yanyan Liu */ public class SDependencyLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SDependencyLogBuilderFactory { @Override public SDependencyLogBuilder createNewInstance() { return new SDependencyLogBuilderImpl(); } @Override public String getObjectIdKey() { return SDependencyLogIndexesMapper.DEPENDENCY_INDEX_NAME; } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SDependencyLogBuilderImpl.java ================================================ /** * 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.dependency.model.builder.impl; import org.bonitasoft.engine.dependency.model.builder.SDependencyLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Yanyan Liu */ public class SDependencyLogBuilderImpl extends CRUDELogBuilder implements SDependencyLogBuilder { private static final String PREFIX = "DEPENDENCY"; @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(SDependencyLogIndexesMapper.DEPENDENCY_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return PREFIX; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL) { if (log.getNumericIndex(SDependencyLogIndexesMapper.DEPENDENCY_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + "Dependency Id"); } } } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SDependencyLogIndexesMapper.java ================================================ /** * 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.dependency.model.builder.impl; /** * @author Yanyan Liu * @author Matthieu Chaffotte */ public class SDependencyLogIndexesMapper { public static final int DEPENDENCY_INDEX = 0; public static final int DEPENDENCY_MAPPING_INDEX = 1; public static final String DEPENDENCY_INDEX_NAME = "numericIndex1"; public static final String DEPENDENCY_MAPPING_INDEX_NAME = "numericIndex2"; } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SDependencyMappingLogBuilderFactoryImpl.java ================================================ /** * 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.dependency.model.builder.impl; import org.bonitasoft.engine.dependency.model.builder.SDependencyMappingLogBuilder; import org.bonitasoft.engine.dependency.model.builder.SDependencyMappingLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Yanyan Liu */ public class SDependencyMappingLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SDependencyMappingLogBuilderFactory { @Override public SDependencyMappingLogBuilder createNewInstance() { return new SDependencyMappingLogBuilderImpl(); } @Override public String getObjectIdKey() { return SDependencyLogIndexesMapper.DEPENDENCY_MAPPING_INDEX_NAME; } @Override public String getDependencyIdKey() { return SDependencyLogIndexesMapper.DEPENDENCY_INDEX_NAME; } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SDependencyMappingLogBuilderImpl.java ================================================ /** * 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.dependency.model.builder.impl; import org.bonitasoft.engine.dependency.model.builder.SDependencyMappingLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Yanyan Liu */ public class SDependencyMappingLogBuilderImpl extends CRUDELogBuilder implements SDependencyMappingLogBuilder { private static final String PREFIX = "DEPENDENCY_MAPPING"; @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(SDependencyLogIndexesMapper.DEPENDENCY_MAPPING_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return PREFIX; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getNumericIndex(SDependencyLogIndexesMapper.DEPENDENCY_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + "Dependency Id"); } if (log.getActionStatus() != SQueriableLog.STATUS_FAIL) { if (log.getNumericIndex(SDependencyLogIndexesMapper.DEPENDENCY_MAPPING_INDEX) == 0L) { throw new MissingMandatoryFieldsException( "Some mandatory fields are missing: " + "Dependency Mapping Id"); } } } @Override public SDependencyMappingLogBuilder dependencyId(final long dependencyId) { queriableLogBuilder.numericIndex(SDependencyLogIndexesMapper.DEPENDENCY_INDEX, dependencyId); return this; } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SPlatformDependencyLogBuilderFactoryImpl.java ================================================ /** * 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.dependency.model.builder.impl; import org.bonitasoft.engine.dependency.model.builder.SDependencyLogBuilder; import org.bonitasoft.engine.dependency.model.builder.SPlatformDependencyLogBuilderFactory; /** * @author Matthieu Chaffotte */ public class SPlatformDependencyLogBuilderFactoryImpl extends SDependencyLogBuilderFactoryImpl implements SPlatformDependencyLogBuilderFactory { @Override public SDependencyLogBuilder createNewInstance() { return new SDependencyLogBuilderImpl(); } } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SPlatformDependencyLogIndexesMapper.java ================================================ /** * 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.dependency.model.builder.impl; /** * @author Matthieu Chaffotte */ public class SPlatformDependencyLogIndexesMapper extends SDependencyLogIndexesMapper { } ================================================ FILE: services/bonita-classloader/src/main/java/org/bonitasoft/engine/dependency/model/builder/impl/SPlatformDependencyMappingLogBuilderFactoryImpl.java ================================================ /** * 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.dependency.model.builder.impl; import org.bonitasoft.engine.dependency.model.builder.SDependencyMappingLogBuilder; import org.bonitasoft.engine.dependency.model.builder.SPlatformDependencyMappingLogBuilderFactory; /** * @author Yanyan Liu */ public class SPlatformDependencyMappingLogBuilderFactoryImpl extends SDependencyMappingLogBuilderFactoryImpl implements SPlatformDependencyMappingLogBuilderFactory { @Override public SDependencyMappingLogBuilder createNewInstance() { return new SDependencyMappingLogBuilderImpl(); } } ================================================ FILE: services/bonita-classloader/src/main/resources/org/bonitasoft/engine/dependency/model/impl/hibernate/dependency.queries.hbm.xml ================================================ SELECT dependency FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency WHERE dependency.name = :name SELECT dependency FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency WHERE dependency.id IN (:ids) ORDER BY dependency.id ASC SELECT new org.bonitasoft.engine.dependency.model.DependencyContent(dependency.fileName, dependency.value_) FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency WHERE dependency.id = :id SELECT dependency FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency SELECT dependencymapping FROM org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping WHERE dependencymapping.id = :id SELECT dependencymapping FROM org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping SELECT dependencymapping FROM org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping WHERE dependencymapping.dependencyId = :dependencyId SELECT dependencymapping.dependencyId FROM org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping WHERE dependencymapping.artifactId = :artifactId AND dependencymapping.artifactType = :artifactType ORDER BY dependencymapping.dependencyId SELECT dependencymapping.dependencyId FROM org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping WHERE dependencymapping.artifactType = 'TENANT' ORDER BY dependencymapping.dependencyId SELECT dependency FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency, org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping WHERE dependencymapping.artifactId = :artifactId AND dependencymapping.artifactType = :artifactType AND dependencymapping.dependencyId = dependency.id AND dependency.fileName = :fileName SELECT dependency FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency, org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping WHERE dependencymapping.artifactType = 'TENANT' AND dependencymapping.dependencyId = dependency.id AND dependency.fileName = :fileName SELECT dependency.id FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency, org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping WHERE dependencymapping.artifactId = :artifactId AND dependencymapping.artifactType = :artifactType AND dependencymapping.dependencyId = dependency.id AND dependency.fileName = :fileName SELECT dependency.id FROM org.bonitasoft.engine.dependency.model.SDependency AS dependency, org.bonitasoft.engine.dependency.model.SDependencyMapping AS dependencymapping WHERE dependencymapping.artifactType = 'TENANT' AND dependencymapping.dependencyId = dependency.id AND dependency.fileName = :fileName ================================================ FILE: services/bonita-classloader/src/main/resources/org/bonitasoft/engine/dependency/model/impl/hibernate/platform-dependency.queries.hbm.xml ================================================ SELECT pdependency FROM org.bonitasoft.engine.dependency.model.SPlatformDependency AS pdependency WHERE pdependency.name = :name SELECT pdependency FROM org.bonitasoft.engine.dependency.model.SPlatformDependency AS pdependency WHERE pdependency.id IN (:ids) SELECT pdependency FROM org.bonitasoft.engine.dependency.model.SPlatformDependency AS pdependency SELECT new org.bonitasoft.engine.dependency.model.DependencyContent(pdependency.fileName, pdependency.value_) FROM org.bonitasoft.engine.dependency.model.SPlatformDependency AS pdependency WHERE pdependency.id = :id SELECT pdependencymapping FROM org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping AS pdependencymapping SELECT pdependencymapping FROM org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping AS pdependencymapping WHERE pdependencymapping.artifactId = :artifactId AND pdependencymapping.artifactType = :artifactType SELECT pdependencymapping FROM org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping AS pdependencymapping WHERE pdependencymapping.dependencyId = :dependencyId SELECT pdependencymapping.dependencyId FROM org.bonitasoft.engine.dependency.model.SPlatformDependencyMapping AS pdependencymapping WHERE pdependencymapping.artifactId = :artifactId AND pdependencymapping.artifactType = :artifactType ORDER BY pdependencymapping.id ASC ================================================ FILE: services/bonita-classloader/src/test/java/org/bonitasoft/engine/classloader/BonitaClassLoaderTest.java ================================================ /** * 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.classloader; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.GLOBAL; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS; import static org.bonitasoft.engine.home.BonitaResource.resource; import static org.bonitasoft.engine.io.IOUtil.generateJar; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.stream.Stream; import com.thoughtworks.xstream.XStream; import org.apache.commons.io.FileUtils; import org.bonitasoft.engine.commons.JavaMethodInvoker; import org.bonitasoft.engine.data.instance.model.impl.XStreamFactory; import org.bonitasoft.engine.home.BonitaResource; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; public class BonitaClassLoaderTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Rule public ExpectedException expectedException = ExpectedException.none(); private ClassLoader testClassLoader; private int idCounter = 1; @Before public void before() { testClassLoader = Thread.currentThread().getContextClassLoader(); } @After public void after() { Thread.currentThread().setContextClassLoader(testClassLoader); } @Test public void destroyShouldRemoveAllScopeFolderAndItsContent() throws IOException { final BonitaClassLoader bonitaClassLoader = BonitaClassLoaderFactory.createClassLoader( Stream.of(resource("myJar.jar", "Salut le monde".getBytes())), identifier(PROCESS, 154L), temporaryFolder.newFolder().toURI(), BonitaClassLoader.class.getClassLoader()); File temporaryFolder = bonitaClassLoader.getTemporaryFolder(); assertThat(temporaryFolder).as("bonitaClassLoader tempDir:%s should exists after bonitaClassLoader creation", temporaryFolder.getAbsolutePath()) .exists(); // when bonitaClassLoader.destroy(); // then assertThat(temporaryFolder).as("bonitaClassLoader tempDir:%s should not exists after bonitaClassLoader release", temporaryFolder.getAbsolutePath()) .doesNotExist(); } @Test public void should_create_second_classloader_use_other_folder() throws Exception { //given File tempFolder = temporaryFolder.newFolder(); //when BonitaClassLoader classLoader1 = BonitaClassLoaderFactory.createClassLoader( Stream.of(resource("myJar1.jar", "content".getBytes())), identifier(PROCESS, 12L), tempFolder.toURI(), BonitaClassLoaderTest.class.getClassLoader()); BonitaClassLoader classLoader2 = BonitaClassLoaderFactory.createClassLoader( Stream.of(resource("myJar2.jar", "content".getBytes())), identifier(PROCESS, 13L), tempFolder.toURI(), BonitaClassLoaderTest.class.getClassLoader()); //then assertThat(classLoader1.getTemporaryFolder().getAbsolutePath()) .isNotEqualTo(classLoader2.getTemporaryFolder().getAbsolutePath()); assertThat(classLoader1.getTemporaryFolder().getParentFile()).isEqualTo(tempFolder); assertThat(classLoader2.getTemporaryFolder().getParentFile()).isEqualTo(tempFolder); } @Test public void should_be_able_to_get_resources_inside_jars() throws Exception { final BonitaClassLoader bonitaClassLoader = BonitaClassLoaderFactory.createClassLoader( Stream.of(resource("UOSFaasApplication.jar", FileUtils.readFileToByteArray(new File("src/test/resources/UOSFaasApplication.jar")))), identifier(PROCESS, 154L), temporaryFolder.newFolder().toURI(), testClassLoader); URL url = bonitaClassLoader.getResource("au/edu/sydney/faas/applicationstudent/StudentInformation.class"); assertThat(url).isNotNull(); assertThat(url.toString()) .containsIgnoringCase( "!/au/edu/sydney/faas/applicationstudent/StudentInformation.class"); bonitaClassLoader.destroy(); } @Test public void should_be_able_to_use_the_JavaMethodInvoker_concurrently_on_a_BonitaClassLoader() throws Exception { final BonitaClassLoader bonitaClassLoader = BonitaClassLoaderFactory.createClassLoader( Stream.of(resource("UOSFaasApplication.jar", FileUtils.readFileToByteArray(new File("src/test/resources/UOSFaasApplication.jar")))), identifier(PROCESS, 154L), temporaryFolder.newFolder().toURI(), testClassLoader); final Object objectToInvokeJavaMethodOn = bonitaClassLoader .loadClass("au.edu.sydney.faas.applicationstudent.StudentRequest") .getConstructors()[0].newInstance(); final Object valueToSetObjectWith = bonitaClassLoader .loadClass("au.edu.sydney.faas.applicationstudent.StudentInformation") .getConstructors()[0].newInstance(); ExecutorService executor = Executors.newSingleThreadExecutor(r -> { Thread t = new Thread(r); t.setContextClassLoader(bonitaClassLoader); return t; }); Future jmiFuture = executor.submit(() -> { try { JavaMethodInvoker jmi = new JavaMethodInvoker(); jmi.invokeJavaMethod("au.edu.sydney.faas.applicationstudent.StudentInformation", valueToSetObjectWith, objectToInvokeJavaMethodOn, "setStudentInformation", "au.edu.sydney.faas.applicationstudent.StudentInformation"); } catch (Exception e) { throw new RuntimeException(e); } return null; }); jmiFuture.get(); // To clean bonitaClassLoader.destroy(); } @Test public void destroy_should_update_XStream_instance() throws Exception { BonitaClassLoader classLoader = BonitaClassLoaderFactory.createClassLoader(Stream.empty(), GLOBAL, temporaryFolder.newFolder().toURI(), testClassLoader); Thread.currentThread().setContextClassLoader(classLoader); // retrieve the XStream instance related to this class loader XStream xStreamBeforeDestroy = XStreamFactory.getXStream(); classLoader.destroy(); // the XStream instance retrieved after destroy must have changed XStream xStreamAfterDestroy = XStreamFactory.getXStream(); assertThat(xStreamAfterDestroy).isNotSameAs(xStreamBeforeDestroy); } @Test public void should_be_able_to_replace_class_with_same_name_in_different_classloader() throws Exception { BonitaClassLoader parent = createClassloader(testClassLoader); BonitaClassLoader c1 = createClassloader(parent, resource("jar1.jar", generateJar("Hello", "public class Hello{", "public String there(){", "return \"hello\";", "}", "}"))); // Class.forName keep a reference in the classloader. We can't override that, it's an native method // We removed VirtualClassLoader to support that use case: assertThat(invoke(Class.forName("Hello", false, c1), "there")).isEqualTo("hello"); assertThat(invoke(c1.loadClass("Hello"), "there")).isEqualTo("hello"); BonitaClassLoader c2 = createClassloader(resource("jar2.jar", generateJar("Hello", "public class Hello{", "public String there(){", "return \"hello there\";", "}", "}"))); assertThat(invoke(Class.forName("Hello", false, c2), "there")).isEqualTo("hello there"); assertThat(invoke(c2.loadClass("Hello"), "there")).isEqualTo("hello there"); c1.destroy(); c2.destroy(); parent.destroy(); } @Test public void should_be_able_to_replace_implementation_of_parent_classloader() throws Exception { BonitaClassLoader parent1 = createClassloader(testClassLoader, resource("lib.jar", generateJar("ParentLib", "public class ParentLib {", " public String getVersion(){", " return \"1.0\";", " }", "}"))); byte[] childJar = generateJar("Child", "public class Child {", " public String getVersion() throws Exception {", " Class parentLibClass = Class.forName(\"ParentLib\");", " return \"Version of the lib in parent is \" + parentLibClass.getMethod(\"getVersion\").invoke(parentLibClass.newInstance());", " }", "}"); BonitaClassLoader child1 = createClassloader(parent1, resource("child.jar", childJar)); assertThat(invoke(child1.loadClass("Child"), "getVersion")).isEqualTo("Version of the lib in parent is 1.0"); //We recreate a hierarchy of classloader. We removed VirtualClassLoader so there is no way to change a parent classloader. BonitaClassLoader parent2 = createClassloader(resource("lib.jar", generateJar("ParentLib", "public class ParentLib {", " public String getVersion(){", " return \"2.0\";", " }", "}"))); BonitaClassLoader child2 = createClassloader(parent2, resource("child.jar", childJar)); assertThat(invoke(child2.loadClass("Child"), "getVersion")).isEqualTo("Version of the lib in parent is 2.0"); } private BonitaClassLoader createClassloader(BonitaResource... resources) throws IOException { return createClassloader(testClassLoader, resources); } private BonitaClassLoader createClassloader(ClassLoader parent, BonitaResource... resources) throws IOException { return BonitaClassLoaderFactory.createClassLoader(Stream.of(resources), identifier(PROCESS, idCounter++), temporaryFolder.newFolder().toURI(), parent); } protected Object invoke(Class class1, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, InstantiationException { return class1.getMethod(name).invoke(class1.newInstance()); } } ================================================ FILE: services/bonita-classloader/src/test/java/org/bonitasoft/engine/classloader/ClassLoaderServiceImplTest.java ================================================ /** * 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.classloader; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.*; import static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; import java.util.Arrays; import org.bonitasoft.engine.dependency.impl.PlatformDependencyService; import org.bonitasoft.engine.dependency.impl.TenantDependencyService; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.service.BroadcastService; import org.bonitasoft.engine.transaction.UserTransactionService; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class ClassLoaderServiceImplTest { @Rule public SystemOutRule systemOutRule = new SystemOutRule().enableLog(); @Mock private EventService eventService; @Mock private PlatformDependencyService platformDependencyService; @Mock private TenantDependencyService tenantDependencyService; @Mock private UserTransactionService userTransactionService; @Mock private BroadcastService broadcastService; @Mock private ClassLoaderUpdater classLoaderUpdater; @Mock private PlatformClassLoaderListener platformClassLoaderListener1; @Mock private PlatformClassLoaderListener platformClassLoaderListener2; @Captor private ArgumentCaptor synchronizationArgumentCaptor; private ClassLoaderServiceImpl classLoaderService; private ClassLoader testClassLoader; private BonitaClassLoader processClassLoader; private MyClassLoaderListener myClassLoaderListener; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); private final long PROCESS_ID = 12; @Before public void before() throws Exception { classLoaderService = new ClassLoaderServiceImpl(new ParentClassLoaderResolver(), eventService, platformDependencyService, userTransactionService, broadcastService, classLoaderUpdater, Arrays.asList(platformClassLoaderListener1, platformClassLoaderListener2)); when(classLoaderUpdater.initializeClassLoader(eq(classLoaderService), any())) .thenAnswer(a -> classLoaderService.createClassloader(a.getArgument(1))); classLoaderService.registerDependencyService(tenantDependencyService); processClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, PROCESS_ID)); classLoaderService.getClassLoader(TENANT); testClassLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(processClassLoader); myClassLoaderListener = new MyClassLoaderListener(); temporaryFolder.create(); } @After public void after() { Thread.currentThread().setContextClassLoader(testClassLoader); } @Test public void should_addListener_add_on_specified_classloader_do_not_call_on_others() { //given classLoaderService.addListener(TENANT, myClassLoaderListener); //when processClassLoader.destroy(); //then assertThat(myClassLoaderListener.isOnDestroyCalled()).isFalse(); } @Test public void should_addListener_add_on_specified_classloader_call_listener() throws SClassLoaderException { //given classLoaderService.addListener(identifier(PROCESS, PROCESS_ID), myClassLoaderListener); //when classLoaderService.removeLocalClassloader(identifier(PROCESS, PROCESS_ID)); //then assertThat(myClassLoaderListener.isOnDestroyCalled()).isTrue(); } @Test public void should_not_be_able_to_destroy_classloader_having_children() { assertThatThrownBy(() -> classLoaderService.removeLocalClassloader(TENANT)) .hasMessageContaining( "Unable to delete classloader TENANT because it has children: [BonitaClassLoader[id=PROCESS:12"); } @Test public void should_removeListener_remove_the_listener() { //given classLoaderService.addListener(TENANT, myClassLoaderListener); classLoaderService.removeListener(TENANT, myClassLoaderListener); //when processClassLoader.destroy(); //then assertThat(myClassLoaderListener.isOnDestroyCalled()).isFalse(); } @Test public void should_refreshClassLoader_call_replace_classloader() throws Exception { //given classLoaderService.addListener(identifier(PROCESS, PROCESS_ID), myClassLoaderListener); //when classLoaderService.refreshClassLoaderImmediately(identifier(PROCESS, PROCESS_ID)); //then assertThat(myClassLoaderListener.isOnUpdateCalled()).isTrue(); assertThat(myClassLoaderListener.isOnDestroyCalled()).isTrue(); } @Test public void should_stop_destroy_all_classloaders() throws Exception { //given classLoaderService.addListener(identifier(PROCESS, PROCESS_ID), myClassLoaderListener); classLoaderService.addListener(TENANT, myClassLoaderListener); classLoaderService.getClassLoader(identifier(PROCESS, 125)); classLoaderService.addListener(identifier(PROCESS, 125), myClassLoaderListener); classLoaderService.getClassLoader(identifier(PROCESS, 126)); classLoaderService.addListener(identifier(PROCESS, 126), myClassLoaderListener); classLoaderService.getClassLoader(identifier(PROCESS, 127)); classLoaderService.addListener(identifier(PROCESS, 127), myClassLoaderListener); classLoaderService.getClassLoader(identifier(PROCESS, 128)); classLoaderService.addListener(identifier(PROCESS, 128), myClassLoaderListener); classLoaderService.getClassLoader(identifier(PROCESS, 129)); classLoaderService.addListener(identifier(PROCESS, 129), myClassLoaderListener); classLoaderService.getClassLoader(identifier(PROCESS, 130)); classLoaderService.addListener(identifier(PROCESS, 130), myClassLoaderListener); //when classLoaderService.stop(); //then assertThat(myClassLoaderListener.getOnUpdateCalled()).isEqualTo(0); assertThat(myClassLoaderListener.getOnDestroyCalled()).isEqualTo(8); } @Test public void should_removeLocalClassLoader_call_destroy() throws Exception { //given classLoaderService.addListener(identifier(PROCESS, PROCESS_ID), myClassLoaderListener); classLoaderService.addListener(TENANT, myClassLoaderListener); //when classLoaderService.removeLocalClassloader(identifier(PROCESS, PROCESS_ID)); //then assertThat(myClassLoaderListener.getOnDestroyCalled()).isEqualTo(1); } @Test(expected = SClassLoaderException.class) public void should_removeLocalClassLoader_throw_exception_if_parent_not_removed() throws Exception { //given classLoaderService.getClassLoader(identifier(PROCESS, 17));//second classloader //when classLoaderService.removeLocalClassloader(identifier(PROCESS, PROCESS_ID)); classLoaderService.removeLocalClassloader(TENANT); } @Test public void should_removeLocalClassLoader_work_if_remove_in_right_order() throws Exception { //given //when classLoaderService.removeLocalClassloader(identifier(PROCESS, PROCESS_ID)); classLoaderService.removeLocalClassloader(TENANT); } @Test public void should_getLocalClassLoader_create_expected_hierarchy() { //given BonitaClassLoader localClassLoader = classLoaderService.getClassLoader(identifier(PROCESS, PROCESS_ID)); //when assertThat(localClassLoader.getIdentifier()).isEqualTo(identifier(PROCESS, PROCESS_ID)); ClassLoader parent = localClassLoader.getParent(); assertThat(parent).isInstanceOf(BonitaClassLoader.class); assertThat(((BonitaClassLoader) parent).getIdentifier()) .isEqualTo(TENANT); ClassLoader global = parent.getParent(); assertThat(global).isInstanceOf(BonitaClassLoader.class); assertThat(((BonitaClassLoader) global).getIdentifier()).isEqualTo(ClassLoaderIdentifier.GLOBAL); ClassLoader root = global.getParent(); assertThat(root).isNotInstanceOf(BonitaClassLoader.class); } @Test public void should_globalListeners_be_called_on_destroy() throws Exception { //given classLoaderService.getClassLoader(identifier(PROCESS, 17)); //second classloader //when classLoaderService.removeLocalClassloader(identifier(PROCESS, PROCESS_ID)); classLoaderService.removeLocalClassloader(identifier(PROCESS, 17)); //then verify(platformClassLoaderListener1, times(2)).onDestroy(any(BonitaClassLoader.class)); verify(platformClassLoaderListener2, times(2)).onDestroy(any(BonitaClassLoader.class)); } @Test public void should_globalListeners_be_called_on_update() throws Exception { //given classLoaderService.getClassLoader(identifier(PROCESS, 17));//second classloader //when classLoaderService.refreshClassLoaderImmediately(identifier(PROCESS, PROCESS_ID)); classLoaderService.refreshClassLoaderImmediately(identifier(PROCESS, 17)); //then verify(platformClassLoaderListener1, times(2)).onUpdate( any(BonitaClassLoader.class)); verify(platformClassLoaderListener2, times(2)).onUpdate( any(BonitaClassLoader.class)); } @Test public void should_refresh_classloader_after_transaction() throws Exception { doNothing().when(userTransactionService).registerBonitaSynchronization(synchronizationArgumentCaptor.capture()); classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42)); assertThat(synchronizationArgumentCaptor.getValue()).isInstanceOf(RefreshClassloaderSynchronization.class); } @Test public void should_register_only_one_synchronization_when_refreshing_multiple_classloader_in_the_same_transaction() throws Exception { doNothing().when(userTransactionService).registerBonitaSynchronization(synchronizationArgumentCaptor.capture()); classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42)); classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42)); classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42)); classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42)); assertThat(synchronizationArgumentCaptor.getAllValues()).hasSize(1); } @Test public void should_refresh_multiple_classloaders_after_transaction() throws Exception { doNothing().when(userTransactionService).registerBonitaSynchronization(synchronizationArgumentCaptor.capture()); classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 41)); classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42)); classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 43)); classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42)); assertThat(synchronizationArgumentCaptor.getAllValues()).hasSize(1); assertThat(synchronizationArgumentCaptor.getValue().getIdentifiers()) .containsExactlyInAnyOrder(identifier(PROCESS, 41L), identifier(PROCESS, 42L), identifier(PROCESS, 43L)); } @Test public void should_refresh_classloader_after_transaction_once_per_transaction() throws Exception { doNothing().when(userTransactionService).registerBonitaSynchronization(synchronizationArgumentCaptor.capture()); classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42)); // Simulate an end + begin of a transaction: classLoaderService.removeRefreshClassLoaderSynchronization(); classLoaderService.refreshClassLoaderAfterUpdate(identifier(PROCESS, 42)); assertThat(synchronizationArgumentCaptor.getAllValues()).hasSize(2); } @Test public void should_initialize_process_class_loader_when_getting_it() { classLoaderService.getClassLoader(identifier(PROCESS, 42)); verify(classLoaderUpdater).initializeClassLoader(classLoaderService, identifier(PROCESS, 42)); } @Test public void should_initialize_only_once_classloader() { classLoaderService.getClassLoader(identifier(PROCESS, 42)); classLoaderService.getClassLoader(identifier(PROCESS, 42)); verify(classLoaderUpdater, times(1)).initializeClassLoader(classLoaderService, identifier(PROCESS, 42)); } @Test public void should_not_initialize_classloader_when_adding_and_removing_listener() { SingleClassLoaderListener singleClassLoaderListener = mock(SingleClassLoaderListener.class); assertThat(classLoaderService.addListener(TENANT, singleClassLoaderListener)).isTrue(); assertThat(classLoaderService.removeListener(TENANT, singleClassLoaderListener)).isTrue(); verify(classLoaderUpdater, never()).initializeClassLoader(classLoaderService, TENANT); } @Test public void should_add_and_remove_listeners_for_one_classloader() throws Exception { //given SingleClassLoaderListener classLoaderListener1 = new SingleClassLoaderListener() { }; SingleClassLoaderListener classLoaderListener2 = new SingleClassLoaderListener() { }; classLoaderService.addListener(TENANT, classLoaderListener1); classLoaderService.addListener(TENANT, classLoaderListener2); //when classLoaderService.removeListener(TENANT, classLoaderListener1); //then assertThat(classLoaderService.getListeners(TENANT)).containsExactly(classLoaderListener2); } @Test public void should_call_destroy_on_all_class_loader_when_refreshing_a_parent_classloader() throws Exception { MyClassLoaderListener process12Listener = new MyClassLoaderListener(); classLoaderService.addListener(identifier(PROCESS, 12), process12Listener); MyClassLoaderListener process13Listener = new MyClassLoaderListener(); classLoaderService.addListener(identifier(PROCESS, 13), process13Listener); MyClassLoaderListener tenantListener = new MyClassLoaderListener(); classLoaderService.addListener(TENANT, tenantListener); MyClassLoaderListener globalListener = new MyClassLoaderListener(); classLoaderService.addListener(GLOBAL, globalListener); classLoaderService.getClassLoader(identifier(PROCESS, 12)); classLoaderService.getClassLoader(identifier(PROCESS, 13)); classLoaderService.refreshClassLoaderImmediately(GLOBAL); //process and tenant classloaders are only destroyed assertThat(process12Listener.isOnDestroyCalled()).isTrue(); assertThat(process12Listener.isOnUpdateCalled()).isFalse(); assertThat(process13Listener.isOnDestroyCalled()).isTrue(); assertThat(process13Listener.isOnUpdateCalled()).isFalse(); assertThat(tenantListener.isOnDestroyCalled()).isTrue(); assertThat(tenantListener.isOnUpdateCalled()).isFalse(); //global classloader is destroyed and updated assertThat(globalListener.isOnDestroyCalled()).isTrue(); assertThat(globalListener.isOnUpdateCalled()).isTrue(); } } ================================================ FILE: services/bonita-classloader/src/test/java/org/bonitasoft/engine/classloader/ClassLoaderUpdaterTest.java ================================================ /** * 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.classloader; import static java.util.concurrent.CompletableFuture.completedFuture; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS; import static org.mockito.Mockito.*; import java.util.HashSet; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.commons.lang3.reflect.FieldUtils; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.service.BonitaTaskExecutor; import org.bonitasoft.engine.transaction.UserTransactionService; import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; public class ClassLoaderUpdaterTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Mock private UserTransactionService userTransactionService; @Mock private BonitaTaskExecutor bonitaTaskExecutor; @Captor private ArgumentCaptor> callableGivenToTheTaskExecutor; @Captor private ArgumentCaptor> callableGivenToTheTransactionService; @Mock private ClassLoaderServiceImpl classLoaderService; @InjectMocks private ClassLoaderUpdater classLoaderUpdater; @Test public void should_refresh_classloaders_in_transaction() throws Exception { doReturn(completedFuture(null)).when(bonitaTaskExecutor).execute(callableGivenToTheTaskExecutor.capture()); doReturn(null).when(userTransactionService) .executeInTransaction(callableGivenToTheTransactionService.capture()); HashSet ids = new HashSet<>(); ids.add(ClassLoaderIdentifier.TENANT); ids.add(identifier(PROCESS, 45L)); classLoaderUpdater.refreshClassloaders(classLoaderService, ids); callableGivenToTheTaskExecutor.getValue().call(); callableGivenToTheTransactionService.getValue().call(); verify(classLoaderService).refreshClassLoaderImmediately(ClassLoaderIdentifier.TENANT); verify(classLoaderService).refreshClassLoaderImmediately(identifier(PROCESS, 45L)); } @Test public void should_throw_SBonitaRuntimeException_on_timeout_in_execute() throws Exception { // Make executor return a Future whose timed get throws TimeoutException Future future = mock(Future.class); doReturn(future).when(bonitaTaskExecutor).execute((Callable) any()); doThrow(new TimeoutException("simulated timeout")) .when(future).get(anyLong(), any(TimeUnit.class)); assertThatExceptionOfType(SBonitaRuntimeException.class) .isThrownBy( () -> classLoaderUpdater.initializeClassLoader(classLoaderService, identifier(PROCESS, 45L))) .withCauseExactlyInstanceOf(TimeoutException.class) .withMessageStartingWith("Unable to refresh the classloaders"); } @Test public void should_keep_positive_timeout_value_when_validating() throws Exception { // given setTimeoutValue(classLoaderUpdater, 10L); // when classLoaderUpdater.validateClassLoaderInitializationTimeoutMinutes(); // then assertThat(getTimeoutValue(classLoaderUpdater)).isEqualTo(10L); } @Test public void should_reset_to_default_when_timeout_is_zero() throws Exception { // given setTimeoutValue(classLoaderUpdater, 0L); // when classLoaderUpdater.validateClassLoaderInitializationTimeoutMinutes(); // then assertThat(getTimeoutValue(classLoaderUpdater)).isEqualTo(5L); } @Test public void should_reset_to_default_when_timeout_is_negative() throws Exception { // given setTimeoutValue(classLoaderUpdater, -5L); // when classLoaderUpdater.validateClassLoaderInitializationTimeoutMinutes(); // then assertThat(getTimeoutValue(classLoaderUpdater)).isEqualTo(5L); } @Test public void should_keep_minimum_positive_value_when_validating() throws Exception { // given setTimeoutValue(classLoaderUpdater, 1L); // when classLoaderUpdater.validateClassLoaderInitializationTimeoutMinutes(); // then assertThat(getTimeoutValue(classLoaderUpdater)).isEqualTo(1L); } @Test public void should_keep_large_timeout_value_when_validating() throws Exception { // given setTimeoutValue(classLoaderUpdater, 120L); // when classLoaderUpdater.validateClassLoaderInitializationTimeoutMinutes(); // then assertThat(getTimeoutValue(classLoaderUpdater)).isEqualTo(120L); } private void setTimeoutValue(ClassLoaderUpdater updater, long value) throws Exception { FieldUtils.writeField(updater, "classLoaderInitializationTimeoutMinutes", value, true); } private long getTimeoutValue(ClassLoaderUpdater updater) throws Exception { return (long) FieldUtils.readField(updater, "classLoaderInitializationTimeoutMinutes", true); } } ================================================ FILE: services/bonita-classloader/src/test/java/org/bonitasoft/engine/classloader/MyClassLoaderListener.java ================================================ /** * 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.classloader; /** * @author Baptiste Mesta */ class MyClassLoaderListener implements SingleClassLoaderListener { int onDestroyCalled = 0; int onUpdateCalled = 0; @Override public void onUpdate(ClassLoader newClassLoader) { onUpdateCalled++; } @Override public void onDestroy(ClassLoader oldClassLoader) { onDestroyCalled++; } public boolean isOnDestroyCalled() { return onDestroyCalled > 0; } public boolean isOnUpdateCalled() { return onUpdateCalled > 0; } public int getOnDestroyCalled() { return onDestroyCalled; } public int getOnUpdateCalled() { return onUpdateCalled; } } ================================================ FILE: services/bonita-classloader/src/test/java/org/bonitasoft/engine/classloader/ParentClassLoaderResolverTest.java ================================================ /** * 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.classloader; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class ParentClassLoaderResolverTest { @InjectMocks private ParentClassLoaderResolver parentClassLoaderResolver; @Test public void should_getParentClassLoaderIdentifier_return_global_on_tenant_classloader() { //given final ClassLoaderIdentifier childId = ClassLoaderIdentifier.TENANT; //when final ClassLoaderIdentifier parentClassLoaderIdentifier = parentClassLoaderResolver .getParentClassLoaderIdentifier(childId); //then assertThat(parentClassLoaderIdentifier).isEqualTo(ClassLoaderIdentifier.GLOBAL); } @Test public void should_getParentClassLoaderIdentifier_return_tenant() { //given final ClassLoaderIdentifier childId = identifier(PROCESS, 124); //when final ClassLoaderIdentifier parentClassLoaderIdentifier = parentClassLoaderResolver .getParentClassLoaderIdentifier(childId); //then assertThat(parentClassLoaderIdentifier).isEqualTo(ClassLoaderIdentifier.TENANT); } @Test public void should_give_APPLICATION_for_parent_of_global_classloader() { ClassLoaderIdentifier parentClassLoaderIdentifier = parentClassLoaderResolver .getParentClassLoaderIdentifier(ClassLoaderIdentifier.GLOBAL); assertThat(parentClassLoaderIdentifier).isEqualTo(ClassLoaderIdentifier.APPLICATION); } } ================================================ FILE: services/bonita-classloader/src/test/java/org/bonitasoft/engine/classloader/RefreshClassloaderSynchronizationTest.java ================================================ /** * 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.classloader; import static java.util.Collections.emptyMap; import static java.util.Collections.singleton; import static javax.transaction.Status.*; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.service.BroadcastService; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; public class RefreshClassloaderSynchronizationTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Mock private BroadcastService broadcastService; @Mock private RefreshClassLoaderTask refreshClassLoaderTask; @Mock private ClassLoaderServiceImpl classLoaderService; @Mock private ClassLoaderUpdater classLoaderUpdater; private RefreshClassloaderSynchronization refreshClassloaderSynchronization; @Before public void before() { refreshClassloaderSynchronization = new RefreshClassloaderSynchronization(classLoaderService, broadcastService, refreshClassLoaderTask, classLoaderUpdater, identifier(ScopeType.PROCESS, 111L)); } @Test public void should_remove_the_synchronization_when_its_executed() { refreshClassloaderSynchronization.afterCompletion(STATUS_NO_TRANSACTION); verify(classLoaderService).removeRefreshClassLoaderSynchronization(); } @Test public void should_refresh_classloader_using_classLoaderUpdater() throws Exception { doReturn(emptyMap()).when(broadcastService).executeOnOthersAndWait(any()); refreshClassloaderSynchronization.afterCompletion(STATUS_COMMITTED); verify(classLoaderUpdater).refreshClassloaders(classLoaderService, singleton(identifier(ScopeType.PROCESS, 111L))); } @Test public void should_refresh_classloader_on_other_nodes_after_commit() throws Exception { doReturn(emptyMap()).when(broadcastService).executeOnOthersAndWait(any()); refreshClassloaderSynchronization.afterCompletion(STATUS_COMMITTED); verify(broadcastService).executeOnOthersAndWait(refreshClassLoaderTask); } @Test public void should_not_refresh_classloader_when_transaction_is_not_committed() { refreshClassloaderSynchronization.afterCompletion(STATUS_MARKED_ROLLBACK); verifyNoInteractions(broadcastService); verifyNoInteractions(classLoaderUpdater); } } ================================================ FILE: services/bonita-classloader/src/test/java/org/bonitasoft/engine/classloader/listeners/ClassReflectorClearerTest.java ================================================ /** * 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.classloader.listeners; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.commons.ClassReflector; import org.junit.Test; /** * @author Baptiste Mesta */ public class ClassReflectorClearerTest { @Test public void testOnUpdate() throws Exception { ClassReflector.getMethod(this.getClass(), "testOnUpdate"); Assertions.assertThat(ClassReflector.getCacheSize()).isPositive(); new ClassReflectorClearer().onUpdate(null); Assertions.assertThat(ClassReflector.getCacheSize()).isZero(); } @Test public void testOnDestroy() throws Exception { ClassReflector.getMethod(this.getClass(), "testOnUpdate"); Assertions.assertThat(ClassReflector.getCacheSize()).isPositive(); new ClassReflectorClearer().onDestroy(null); Assertions.assertThat(ClassReflector.getCacheSize()).isZero(); } } ================================================ FILE: services/bonita-classloader/src/test/java/org/bonitasoft/engine/dependency/impl/TenantDependencyServiceTest.java ================================================ /** * 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.dependency.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.dependency.model.ScopeType.PROCESS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.dependency.SDependencyException; import org.bonitasoft.engine.dependency.SDependencyNotFoundException; import org.bonitasoft.engine.dependency.model.SDependency; import org.bonitasoft.engine.dependency.model.SDependencyMapping; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.services.QueriableLoggerService; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class TenantDependencyServiceTest { @Mock private ReadPersistenceService persistenceService; @Mock private Recorder recorder; @Mock private QueriableLoggerService queriableLoggerService; @InjectMocks private TenantDependencyService tenantDependencyService; /** * Test method for {@link TenantDependencyService#getDependency(long)}. */ @Test public final void getDependencyById() throws SBonitaReadException, SDependencyNotFoundException { final SDependency sDependency = mock(SDependency.class); when(persistenceService.selectById(ArgumentMatchers.> any())) .thenReturn(sDependency); Assert.assertEquals(sDependency, tenantDependencyService.getDependency(456L)); } @Test(expected = SDependencyNotFoundException.class) public final void getDependencyByIdNotExists() throws SBonitaReadException, SDependencyNotFoundException { when(persistenceService.selectById(ArgumentMatchers.> any())) .thenReturn(null); tenantDependencyService.getDependency(456L); } @Test(expected = SDependencyNotFoundException.class) public final void getDependencyByIdThrowException() throws SBonitaReadException, SDependencyNotFoundException { when(persistenceService.selectById(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); tenantDependencyService.getDependency(456L); } /** * Test method for {@link TenantDependencyService#getDependencies(java.util.Collection)}. */ @Test public final void getDependenciesByIds() throws SBonitaReadException, SDependencyException { final List sDependencies = new ArrayList<>(); when(persistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(sDependencies); Assert.assertEquals(sDependencies, tenantDependencyService.getDependencies(Collections.singletonList(456L))); } @Test(expected = SDependencyException.class) public final void getDependenciesByIdsThrowException() throws SBonitaReadException, SDependencyException { when(persistenceService.selectList(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); tenantDependencyService.getDependencies(Collections.singletonList(456L)); } /** * Test method for {@link TenantDependencyService#getDependencies(org.bonitasoft.engine.persistence.QueryOptions)}. */ @Test public final void getDependenciesWithOptions() throws SBonitaReadException, SDependencyException { final List sDependencies = new ArrayList<>(); when(persistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(sDependencies); final QueryOptions options = new QueryOptions(0, 10); Assert.assertEquals(sDependencies, tenantDependencyService.getDependencies(options)); } @Test(expected = SDependencyException.class) public final void getDependenciesWithOptionsThrowException() throws SBonitaReadException, SDependencyException { when(persistenceService.selectList(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); final QueryOptions options = new QueryOptions(0, 10); tenantDependencyService.getDependencies(options); } @Test public final void getDependencyIds() throws SBonitaReadException, SDependencyException { final List sDependencies = new ArrayList<>(); when(persistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(sDependencies); assertThat(tenantDependencyService.getDependencyIds(54156L, PROCESS, 1, 100)).isEqualTo(sDependencies); } @Test(expected = SDependencyException.class) public final void getDependencyIdsThrowException() throws SBonitaReadException, SDependencyException { when(persistenceService.selectList(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); tenantDependencyService.getDependencyIds(54156L, PROCESS, 1, 100); } /** * Test method for * {@link TenantDependencyService#getDependencyMappings(long, org.bonitasoft.engine.persistence.QueryOptions)}. */ @Test public final void getDependencyMappingsWithDependencyIdAndQueryOptions() throws SBonitaReadException, SDependencyException { final List sDependencyMappings = new ArrayList<>(); when(persistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(sDependencyMappings); final QueryOptions options = new QueryOptions(0, 10); Assert.assertEquals(sDependencyMappings, tenantDependencyService.getDependencyMappings(54156L, options)); } @Test(expected = SDependencyException.class) public final void getDependencyMappingsWithDependencyIdAndQueryOptionsThrowException() throws SBonitaReadException, SDependencyException { when(persistenceService.selectList(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); final QueryOptions options = new QueryOptions(0, 10); tenantDependencyService.getDependencyMappings(54156L, options); } /** * Test method for * {@link TenantDependencyService#getDependencyMappings(org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test public final void getDependencyMappingsWithOptions() throws SBonitaReadException, SDependencyException { final List sDependencyMappings = new ArrayList<>(); when(persistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(sDependencyMappings); final QueryOptions options = new QueryOptions(0, 10); Assert.assertEquals(sDependencyMappings, tenantDependencyService.getDependencyMappings(options)); } @Test(expected = SDependencyException.class) public final void getDependencyMappingsWithOptionsThrowException() throws SBonitaReadException, SDependencyException { when(persistenceService.selectList(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); final QueryOptions options = new QueryOptions(0, 10); tenantDependencyService.getDependencyMappings(options); } @Test(expected = SDependencyNotFoundException.class) public void deleteDependencyByNonExistingNameShouldThrowSDependencyNotFoundException() throws Exception { when(persistenceService.selectOne(ArgumentMatchers.> any())).thenReturn(null); tenantDependencyService.deleteDependency("notFound"); } @Test(expected = SDependencyNotFoundException.class) public void deleteDependencyWithReadExceptionShouldThrowSDependencyNotFoundException() throws Exception { Mockito.doThrow(new SBonitaReadException("")).when(persistenceService) .selectOne(ArgumentMatchers.> any()); tenantDependencyService.deleteDependency("notFound"); } } ================================================ FILE: services/bonita-command/build.gradle ================================================ dependencies { api project(':services:bonita-log') api project(':services:bonita-commons') api project(':services:bonita-builder') api project(':services:bonita-events') api project(':services:bonita-persistence') testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.logback annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/CommandService.java ================================================ /** * 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.command; import java.util.List; import org.bonitasoft.engine.command.model.SCommand; import org.bonitasoft.engine.command.model.SCommandCriterion; import org.bonitasoft.engine.commons.TenantLifecycleService; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Matthieu Chaffotte * @since 6.0 */ public interface CommandService extends TenantLifecycleService { String COMMAND = "COMMAND"; /** * Create command by given command * * @param command * command without Id * @throws SCommandAlreadyExistsException * Error thrown when relative command already exists * @throws SCommandCreationException * Error thrown if has exceptions during the creating command. */ void create(SCommand command) throws SCommandAlreadyExistsException, SCommandCreationException; /** * Delete command by given command name * * @param name * Name of command which will be deleted * @throws SCommandNotFoundException * Error thrown if no command have name corresponding to the parameter. * @throws SCommandDeletionException * Error thrown if has exception during the deleting command. */ void delete(String name) throws SCommandNotFoundException, SCommandDeletionException; /** * Delete all commands * * @throws SCommandDeletionException * Error thrown if has exception during the deleting command. */ void deleteAll() throws SCommandDeletionException; /** * Get command by given name * * @param name * Name of command * @return a command object * @throws SCommandNotFoundException * Error thrown if no command have name corresponding to the parameter. */ SCommand get(String name) throws SCommandNotFoundException; /** * Retrieves a paginated list of commands, The returned list is paginated * * @param startIndex * Start index of command record * @param maxResults * Number of commands we want to get. Maximum number of commands returned. * @param sort * The criterion used to sort the retried commands * @return a list of command objects * @throws SCommandGettingException * Error thrown if has exception during the command getting. */ List getAllCommands(int startIndex, int maxResults, SCommandCriterion sort) throws SCommandGettingException; /** * Update the command by its id * * @param command * The command will be updated * @param updateDescriptor * The description for update command * @throws SCommandNotFoundException * Error thrown if no command have name corresponding to the parameter. * @throws SCommandUpdateException * Error thrown if has exception during the command updating. */ void update(SCommand command, EntityUpdateDescriptor updateDescriptor) throws SCommandNotFoundException, SCommandUpdateException; /** * Retrieves a paginated list of commands with System is false * * @param startIndex * Start index of command record * @param maxResults * Number of commands we want to get. Maximum number of commands returned. * @param sCommandCriterion * The criterion used to sort the retried commands * @return A list of command objects * @throws SCommandGettingException * Error thrown if has exception during the command getting. * @since 6.0 */ List getUserCommands(int startIndex, int maxResults, SCommandCriterion sCommandCriterion) throws SCommandGettingException; /** * Get command by given id * * @param commandId * identifier of command * @return a command object * @throws SCommandNotFoundException * Error thrown if no command have name corresponding to the parameter. * @author Yanyan Liu */ SCommand get(final long commandId) throws SCommandNotFoundException; /** * Delete command by given command id * * @param commandId * identifier of command which will be deleted * @throws SCommandNotFoundException * Error thrown if no command have name corresponding to the parameter. * @throws SCommandDeletionException * Error thrown if has exception during the deleting command. * @author Yanyan Liu */ void delete(long commandId) throws SCommandNotFoundException, SCommandDeletionException; /** * Get total number of commands according to the specific criteria * * @param options * search criteria * @return total number of commands corresponding to the specific criteria * @throws SBonitaReadException * @author Yanyan Liu */ long getNumberOfCommands(QueryOptions options) throws SBonitaReadException; /** * Get a list of commands according to the specific criteria * * @param options * search criteria * @return a list of command objects * @throws SBonitaReadException * @author Yanyan Liu */ List searchCommands(QueryOptions options) throws SBonitaReadException; } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/SCommandAlreadyExistsException.java ================================================ /** * 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.command; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SCommandAlreadyExistsException extends SBonitaException { private static final long serialVersionUID = -3017980604615886777L; public SCommandAlreadyExistsException(final String message, final Throwable cause) { super(message, cause); } public SCommandAlreadyExistsException(final String message) { super(message); } public SCommandAlreadyExistsException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/SCommandCreationException.java ================================================ /** * 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.command; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SCommandCreationException extends SBonitaException { private static final long serialVersionUID = 634050853254691398L; public SCommandCreationException(final String message, final Throwable cause) { super(message, cause); } public SCommandCreationException(final String message) { super(message); } public SCommandCreationException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/SCommandDeletionException.java ================================================ /** * 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.command; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SCommandDeletionException extends SBonitaException { private static final long serialVersionUID = -1160779784480102515L; public SCommandDeletionException(final String message, final Throwable cause) { super(message, cause); } public SCommandDeletionException(final String message) { super(message); } public SCommandDeletionException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/SCommandGettingException.java ================================================ /** * 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.command; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SCommandGettingException extends SBonitaException { private static final long serialVersionUID = -4051373026112211800L; public SCommandGettingException(final String message, final Throwable cause) { super(message, cause); } public SCommandGettingException(final String message) { super(message); } public SCommandGettingException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/SCommandNotFoundException.java ================================================ /** * 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.command; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SCommandNotFoundException extends SBonitaException { private static final long serialVersionUID = -3017980604615886777L; public SCommandNotFoundException(final String message, final Throwable cause) { super(message, cause); } public SCommandNotFoundException(final String message) { super(message); } public SCommandNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/SCommandUpdateException.java ================================================ /** * 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.command; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SCommandUpdateException extends SBonitaException { private static final long serialVersionUID = 8667781514957065049L; public SCommandUpdateException(final String message, final Throwable cause) { super(message, cause); } public SCommandUpdateException(final String message) { super(message); } public SCommandUpdateException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/api/impl/CommandDeployment.java ================================================ /** * 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.command.api.impl; import lombok.Data; import lombok.NoArgsConstructor; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor public class CommandDeployment { private String name; private String description; private String implementation; public CommandDeployment(final String name, final String description, final String implementation) { this.name = name; this.description = description; this.implementation = implementation; } } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/api/impl/CommandProvider.java ================================================ /** * 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.command.api.impl; import java.util.Collections; import java.util.List; /** * @author Matthieu Chaffotte */ public class CommandProvider { private List defaultCommands; public void setDefaultCommands(final List defaultCommands) { this.defaultCommands = defaultCommands; } public List getDefaultCommands() { if (defaultCommands == null) { return Collections.emptyList(); } return defaultCommands; } } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/api/impl/CommandServiceImpl.java ================================================ /** * 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.command.api.impl; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.command.CommandService; import org.bonitasoft.engine.command.SCommandAlreadyExistsException; import org.bonitasoft.engine.command.SCommandCreationException; import org.bonitasoft.engine.command.SCommandDeletionException; import org.bonitasoft.engine.command.SCommandGettingException; import org.bonitasoft.engine.command.SCommandNotFoundException; import org.bonitasoft.engine.command.SCommandUpdateException; import org.bonitasoft.engine.command.api.record.SelectDescriptorBuilder; import org.bonitasoft.engine.command.model.SCommand; import org.bonitasoft.engine.command.model.SCommandCriterion; import org.bonitasoft.engine.command.model.SCommandLogBuilder; import org.bonitasoft.engine.command.model.SCommandLogBuilderFactory; import org.bonitasoft.engine.command.model.SCommandUpdateBuilderImpl; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; /** * @author Zhang Bole * @author Matthieu Chaffotte * @author Hongwen Zang * @author Celine Souchet */ @Slf4j public class CommandServiceImpl implements CommandService { public static final int FETCH_SIZE = 1000; private final ReadPersistenceService persistenceService; private final Recorder recorder; private final EventService eventService; private final QueriableLoggerService queriableLoggerService; private final CommandProvider defaultCommandProvider; private final int fetchSize; public CommandServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder, final EventService eventService, final QueriableLoggerService queriableLoggerService, CommandProvider defaultCommandProvider) { this(persistenceService, recorder, eventService, queriableLoggerService, defaultCommandProvider, FETCH_SIZE); } public CommandServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder, final EventService eventService, final QueriableLoggerService queriableLoggerService, CommandProvider defaultCommandProvider, int fetchSize) { super(); this.persistenceService = persistenceService; this.recorder = recorder; this.eventService = eventService; this.queriableLoggerService = queriableLoggerService; this.defaultCommandProvider = defaultCommandProvider; this.fetchSize = fetchSize; } private SCommandLogBuilder getQueriableLog(final ActionType actionType, final String message) { final SCommandLogBuilder logBuilder = BuilderFactory.get(SCommandLogBuilderFactory.class).createNewInstance(); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); return logBuilder; } private void initializeLogBuilder(final T logBuilder, final String message) { logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message); } private void updateLog(final ActionType actionType, final T logBuilder) { logBuilder.setActionType(actionType); } @Override public void create(final SCommand command) throws SCommandAlreadyExistsException, SCommandCreationException { try { this.get(command.getName()); throw new SCommandAlreadyExistsException("Command '" + command.getName() + "' already exists"); } catch (final SCommandNotFoundException e) { final SCommandLogBuilder logBuilder = getQueriableLog(ActionType.CREATED, "Creating a new command with name " + command.getName()); try { recorder.recordInsert(new InsertRecord(command), COMMAND); log(command.getId(), SQueriableLog.STATUS_OK, logBuilder, "create"); } catch (final SRecorderException re) { log(command.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "create"); throw new SCommandCreationException(re); } } } @Override public void delete(final long commandId) throws SCommandNotFoundException, SCommandDeletionException { final SCommandLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, "Deleting command with id " + commandId); final SCommand command = this.get(commandId); delete(command, logBuilder); } protected void delete(final SCommand command, final SCommandLogBuilder logBuilder) throws SCommandDeletionException { try { recorder.recordDelete(new DeleteRecord(command), COMMAND); log(command.getId(), SQueriableLog.STATUS_OK, logBuilder, "delete"); } catch (final SRecorderException re) { log(command.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "delete"); throw new SCommandDeletionException(re); } } @Override public void delete(final String commandName) throws SCommandNotFoundException, SCommandDeletionException { final SCommandLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, "Deleting command with name " + commandName); final SCommand command = this.get(commandName); delete(command, logBuilder); } @Override public void deleteAll() throws SCommandDeletionException { List commands; try { do { commands = getAllCommands(0, 1000, SCommandCriterion.NAME_ASC); for (final SCommand command : commands) { final SCommandLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, "Deleting command with name " + command.getName()); delete(command, logBuilder); } } while (!commands.isEmpty()); } catch (final SCommandGettingException scge) { throw new SCommandDeletionException(scge); } } @Override public SCommand get(final String commandName) throws SCommandNotFoundException { try { final SelectOneDescriptor descriptor = SelectDescriptorBuilder.getCommandByName(commandName); final SCommand scommand = persistenceService.selectOne(descriptor); if (scommand == null) { throw new SCommandNotFoundException("command '" + commandName + "' does not exist"); } return scommand; } catch (final SBonitaReadException e) { throw new SCommandNotFoundException("Cannot get command: " + commandName, e); } } @Override public List getAllCommands(final int startIndex, final int maxResults, final SCommandCriterion sCommandCriterion) throws SCommandGettingException { OrderByType orderByType; switch (sCommandCriterion) { case NAME_ASC: orderByType = OrderByType.ASC; break; case NAME_DESC: orderByType = OrderByType.DESC; break; default: throw new IllegalStateException(); } try { return persistenceService.selectList(SelectDescriptorBuilder.getCommands("name", orderByType, startIndex, maxResults)); } catch (final SBonitaReadException e) { throw new SCommandGettingException("can't get the commands", e); } } @Override public void update(final SCommand command, final EntityUpdateDescriptor updateDescriptor) throws SCommandUpdateException { final SCommandLogBuilder logBuilder = getQueriableLog(ActionType.UPDATED, "Updating command with name " + command.getName()); try { recorder.recordUpdate(UpdateRecord.buildSetFields(command, updateDescriptor), COMMAND); log(command.getId(), SQueriableLog.STATUS_OK, logBuilder, "update"); } catch (final SRecorderException re) { log(command.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "update"); throw new SCommandUpdateException(re); } } @Override public List getUserCommands(final int startIndex, final int maxResults, final SCommandCriterion sCommandCriterion) throws SCommandGettingException { OrderByType orderByType; switch (sCommandCriterion) { case NAME_ASC: orderByType = OrderByType.ASC; break; case NAME_DESC: orderByType = OrderByType.DESC; break; default: throw new IllegalStateException(); } try { return persistenceService.selectList(SelectDescriptorBuilder.getUserCommands("name", orderByType, startIndex, maxResults)); } catch (final SBonitaReadException e) { throw new SCommandGettingException("can't get the commands", e); } } @Override public SCommand get(final long commandId) throws SCommandNotFoundException { final SelectByIdDescriptor selectByIdDescriptor = SelectDescriptorBuilder.getCommandById(commandId); try { final SCommand command = persistenceService.selectById(selectByIdDescriptor); if (command == null) { throw new SCommandNotFoundException(commandId + " does not refer to any command"); } return command; } catch (final SBonitaReadException bre) { throw new SCommandNotFoundException(bre); } } @Override public long getNumberOfCommands(final QueryOptions options) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SCommand.class, options, null); } @Override public List searchCommands(final QueryOptions options) throws SBonitaReadException { return persistenceService.searchEntity(SCommand.class, options, null); } private void log(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder, final String callerClassName) { logBuilder.actionScope(String.valueOf(objectId)); logBuilder.actionStatus(sQueriableLogStatus); logBuilder.objectId(objectId); final SQueriableLog log = logBuilder.build(); if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) { queriableLoggerService.log(this.getClass().getName(), callerClassName, log); } } @Override public void start() throws SBonitaException { Map commandDeployments = toCommandDeploymentMap( defaultCommandProvider.getDefaultCommands()); Map availableSystemCommands = getAllAvailableSystemCommands(); createAndUpdateCommands(commandDeployments, availableSystemCommands); deleteSystemCommandsNotPresentInDeployments(commandDeployments, availableSystemCommands); } private void createAndUpdateCommands(final Map commandDeployements, final Map availableSystemCommands) throws SBonitaException { for (String commandName : commandDeployements.keySet()) { if (!availableSystemCommands.containsKey(commandName)) { createMissingCommand(commandDeployements.get(commandName)); } else { SCommand sCommand = availableSystemCommands.get(commandName); updateExistingCommandIfNecessary(sCommand, commandDeployements.get(commandName)); } } } private void updateExistingCommandIfNecessary(final SCommand command, final CommandDeployment commandDeployment) throws SCommandUpdateException { SCommandUpdateBuilderImpl updateBuilder = new SCommandUpdateBuilderImpl(); if (!command.getDescription().equals(commandDeployment.getDescription())) { updateBuilder.updateDescription(commandDeployment.getDescription()); } if (!command.getImplementation().equals(commandDeployment.getImplementation())) { updateBuilder.updateImplementation(commandDeployment.getImplementation()); } EntityUpdateDescriptor updateDescriptor = updateBuilder.done(); if (!updateDescriptor.getFields().isEmpty()) { if (log.isInfoEnabled()) { log.info("Updating system command. Before update: {}. After update: {}", command, commandDeployment); } update(command, updateDescriptor); } } private void createMissingCommand(final CommandDeployment commandDeployment) throws SCommandAlreadyExistsException, SCommandCreationException { log.debug("Creating missing system command: {}", commandDeployment); create(SCommand.builder() .name(commandDeployment.getName()) .description(commandDeployment.getDescription()) .implementation(commandDeployment.getImplementation()) .isSystem(true).build()); } private void deleteSystemCommandsNotPresentInDeployments(final Map commandDeployments, final Map availableSystemCommands) throws SCommandDeletionException { for (String commandName : availableSystemCommands.keySet()) { if (!commandDeployments.containsKey(commandName)) { final SCommandLogBuilder logBuilder = getQueriableLog(ActionType.DELETED, "Deleting command with name " + commandName); SCommand command = availableSystemCommands.get(commandName); if (log.isInfoEnabled()) { log.info("The following system command is not used any more and will be deleted: {}", command); } delete(command, logBuilder); } } } private Map getAllAvailableSystemCommands() throws SBonitaReadException { Map commands = new HashMap<>(); List currentPage; int fromIndex = 0; do { currentPage = getSystemCommands(fromIndex, fetchSize); commands.putAll(toCommandMap(getSystemCommands(fromIndex, fetchSize))); fromIndex += fetchSize; } while (currentPage.size() == fetchSize); return commands; } private List getSystemCommands(int fromIndex, int maxResults) throws SBonitaReadException { QueryOptions queryOptions = new QueryOptions(fromIndex, maxResults, Collections.singletonList(new OrderByOption(SCommand.class, SCommand.ID, OrderByType.ASC)), Collections.singletonList(new FilterOption(SCommand.class, SCommand.SYSTEM, true)), null); return searchCommands(queryOptions); } private Map toCommandMap(List commands) { Map map = new HashMap<>(commands.size()); for (SCommand command : commands) { map.put(command.getName(), command); } return map; } private Map toCommandDeploymentMap(List commandDeployments) { Map map = new HashMap<>(commandDeployments.size()); for (CommandDeployment deployment : commandDeployments) { map.put(deployment.getName(), deployment); } return map; } } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/api/record/SelectDescriptorBuilder.java ================================================ /** * 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.command.api.record; import java.util.Collections; import java.util.Map; import org.bonitasoft.engine.command.model.SCommand; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; /** * @author Bole Zhang * @author Matthieu Chaffotte * @author Emmanuel Duchastenier */ public class SelectDescriptorBuilder { public static SelectOneDescriptor getCommandByName(final String commandName) { final Map parameters = Collections.singletonMap("name", commandName); return new SelectOneDescriptor<>("getCommandByName", parameters, SCommand.class); } public static SelectListDescriptor getCommands(final String field, final OrderByType order, final int fromIndex, final int numberOfElements) { final Map parameters = Collections.emptyMap(); final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, SCommand.class, field, order); return new SelectListDescriptor<>("getCommands", parameters, SCommand.class, queryOptions); } public static SelectListDescriptor getUserCommands(final String field, final OrderByType order, final int fromIndex, final int numberOfElements) { final Map parameters = Collections.singletonMap("isSystem", false); final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, SCommand.class, field, order); return new SelectListDescriptor<>("getUserCommands", parameters, SCommand.class, queryOptions); } public static SelectByIdDescriptor getCommandById(final long commandId) { return new SelectByIdDescriptor<>(SCommand.class, commandId); } } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/CommandUpdateBuilder.java ================================================ /** * 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.command.model; /** * @author Matthieu Chaffotte */ public interface CommandUpdateBuilder { } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommand.java ================================================ /** * 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.command.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "command") public class SCommand implements PersistentObject { public static final String ID = "id"; public static final String NAME = "name"; public static final String DESCRIPTION = "description"; public static final String IMPLEMENTATION = "implementation"; public static final String SYSTEM = "isSystem"; @Id private long id; @Column private boolean isSystem; @Column private String name; @Column private String description; @Column private String implementation; } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandCriterion.java ================================================ /** * 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.command.model; /** * @author Matthieu Chaffotte */ public enum SCommandCriterion { NAME_ASC, NAME_DESC; } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandLogBuilder.java ================================================ /** * 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.command.model; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Matthieu Chaffotte */ public interface SCommandLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandLogBuilderFactory.java ================================================ /** * 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.command.model; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Matthieu Chaffotte */ public interface SCommandLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { SCommandLogBuilder createNewInstance(); } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandLogBuilderFactoryImpl.java ================================================ /** * 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.command.model; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Matthieu Chaffotte */ public class SCommandLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SCommandLogBuilderFactory { @Override public SCommandLogBuilder createNewInstance() { return new SCommandLogBuilderImpl(); } @Override public String getObjectIdKey() { return SCommandLogIndexesMapper.COMMAND_INDEX_NAME; } } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandLogBuilderImpl.java ================================================ /** * 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.command.model; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Matthieu Chaffotte */ public class SCommandLogBuilderImpl extends CRUDELogBuilder implements SCommandLogBuilder { @Override public SPersistenceLogBuilder objectId(final long objectId) { this.queriableLogBuilder.numericIndex(SCommandLogIndexesMapper.COMMAND_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return "COMMAND"; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL) { if (log.getNumericIndex(SCommandLogIndexesMapper.COMMAND_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: command id "); } } } } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandLogIndexesMapper.java ================================================ /** * 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.command.model; /** * @author Matthieu Chaffotte */ public class SCommandLogIndexesMapper { public static final int COMMAND_INDEX = 0; public static final String COMMAND_INDEX_NAME = "numericIndex1"; } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandUpdateBuilder.java ================================================ /** * 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.command.model; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Matthieu Chaffotte */ public interface SCommandUpdateBuilder { SCommandUpdateBuilder updateName(String name); SCommandUpdateBuilder updateDescription(String description); SCommandUpdateBuilder updateImplementation(String implementation); EntityUpdateDescriptor done(); } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandUpdateBuilderFactory.java ================================================ /** * 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.command.model; /** * @author Matthieu Chaffotte */ public interface SCommandUpdateBuilderFactory { SCommandUpdateBuilder createNewInstance(); } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandUpdateBuilderFactoryImpl.java ================================================ /** * 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.command.model; /** * @author Matthieu Chaffotte */ public class SCommandUpdateBuilderFactoryImpl implements SCommandUpdateBuilderFactory { public SCommandUpdateBuilder createNewInstance() { return new SCommandUpdateBuilderImpl(); } } ================================================ FILE: services/bonita-command/src/main/java/org/bonitasoft/engine/command/model/SCommandUpdateBuilderImpl.java ================================================ /** * 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.command.model; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Matthieu Chaffotte */ public class SCommandUpdateBuilderImpl implements SCommandUpdateBuilder { private final EntityUpdateDescriptor descriptor; public SCommandUpdateBuilderImpl() { super(); descriptor = new EntityUpdateDescriptor(); } @Override public SCommandUpdateBuilder updateName(final String name) { this.descriptor.addField(SCommand.NAME, name); return this; } @Override public SCommandUpdateBuilder updateDescription(final String description) { this.descriptor.addField(SCommand.DESCRIPTION, description); return this; } @Override public SCommandUpdateBuilder updateImplementation(final String implementation) { descriptor.addField(SCommand.IMPLEMENTATION, implementation); return this; } @Override public EntityUpdateDescriptor done() { return this.descriptor; } } ================================================ FILE: services/bonita-command/src/main/resources/org/bonitasoft/engine/command/model/impl/hibernate/command.queries.hbm.xml ================================================ SELECT command FROM org.bonitasoft.engine.command.model.SCommand AS command WHERE command.name = :name SELECT command FROM org.bonitasoft.engine.command.model.SCommand AS command SELECT command FROM org.bonitasoft.engine.command.model.SCommand AS command WHERE command.isSystem = :isSystem SELECT COUNT(*) FROM org.bonitasoft.engine.command.model.SCommand AS command SELECT command FROM org.bonitasoft.engine.command.model.SCommand AS command ================================================ FILE: services/bonita-command/src/test/java/org/bonitasoft/engine/command/api/impl/CommandServiceImplTest.java ================================================ /** * 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.command.api.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.command.SCommandGettingException; import org.bonitasoft.engine.command.SCommandNotFoundException; import org.bonitasoft.engine.command.api.record.SelectDescriptorBuilder; import org.bonitasoft.engine.command.comparator.CommandComparator; import org.bonitasoft.engine.command.model.SCommand; import org.bonitasoft.engine.command.model.SCommandCriterion; import org.bonitasoft.engine.command.model.SCommandLogBuilder; import org.bonitasoft.engine.command.model.SCommandUpdateBuilderImpl; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.services.QueriableLoggerService; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class CommandServiceImplTest { public static final int FETCH_SIZE = 2; @Mock private Recorder recorder; @Mock private ReadPersistenceService persistence; @Mock private EventService eventService; @Mock private QueriableLoggerService queriableLoggerService; @Mock private CommandProvider commandProvider; private CommandServiceImpl commandServiceImpl; @Before public final void setUp() { commandServiceImpl = new CommandServiceImpl(persistence, recorder, eventService, queriableLoggerService, commandProvider, FETCH_SIZE); } /** * Test method for * {@link org.bonitasoft.engine.command.api.impl.CommandServiceImpl#getAllCommands(int, int, org.bonitasoft.engine.command.model.SCommandCriterion)}. * * @throws SCommandGettingException * @throws SBonitaReadException */ @Test public final void getAllCommands() throws SCommandGettingException, SBonitaReadException { // Given final List sCommands = new ArrayList(); final String field = "name"; final OrderByType orderByType = OrderByType.ASC; final int startIndex = 0; final int maxResults = 1; when(persistence.selectList(SelectDescriptorBuilder.getCommands(field, orderByType, startIndex, maxResults))) .thenReturn(sCommands); // When final List allCommands = commandServiceImpl.getAllCommands(startIndex, maxResults, SCommandCriterion.NAME_ASC); // Then Assert.assertEquals(sCommands, allCommands); } @Test(expected = SCommandGettingException.class) public final void getAllCommandsThrowException() throws SCommandGettingException, SBonitaReadException { // Given final String field = "name"; final OrderByType orderByType = OrderByType.ASC; final int startIndex = 0; final int maxResults = 1; when(persistence.selectList(SelectDescriptorBuilder.getCommands(field, orderByType, startIndex, maxResults))) .thenThrow(new SBonitaReadException("")); // When commandServiceImpl.getAllCommands(startIndex, maxResults, SCommandCriterion.NAME_DESC); } /** * Test method for {@link org.bonitasoft.engine.command.api.impl.CommandServiceImpl#get(long)}. * * @throws SCommandNotFoundException * @throws SBonitaReadException */ @Test public final void getById() throws SCommandNotFoundException, SBonitaReadException { // Given final SCommand sCommand = mock(SCommand.class); final long commandId = 456L; when(persistence.selectById(SelectDescriptorBuilder.getCommandById(commandId))).thenReturn(sCommand); // When final SCommand actual = commandServiceImpl.get(commandId); // Then Assert.assertEquals(sCommand, actual); } @Test(expected = SCommandNotFoundException.class) public final void getByIdNotExists() throws SBonitaReadException, SCommandNotFoundException { // Given final long commandId = 456L; when(persistence.selectById(SelectDescriptorBuilder.getCommandById(commandId))).thenReturn(null); // When commandServiceImpl.get(commandId); } @Test(expected = SCommandNotFoundException.class) public final void getByIdThrowException() throws SBonitaReadException, SCommandNotFoundException { // Given final long commandId = 456L; when(persistence.selectById(SelectDescriptorBuilder.getCommandById(commandId))) .thenThrow(new SBonitaReadException("")); // When commandServiceImpl.get(commandId); } /** * Test method for {@link org.bonitasoft.engine.command.api.impl.CommandServiceImpl#get(java.lang.String)}. * * @throws SCommandNotFoundException * @throws SBonitaReadException */ @Test public final void getByName() throws SCommandNotFoundException, SBonitaReadException { // Given final SCommand sCommand = mock(SCommand.class); final String commandName = "name"; when(persistence.selectOne(SelectDescriptorBuilder.getCommandByName(commandName))).thenReturn(sCommand); // When final SCommand actual = commandServiceImpl.get(commandName); // Then Assert.assertEquals(sCommand, actual); } @Test(expected = SCommandNotFoundException.class) public final void getByNameNotExists() throws SBonitaReadException, SCommandNotFoundException { // Given final String commandName = "name"; when(persistence.selectOne(SelectDescriptorBuilder.getCommandByName(commandName))).thenReturn(null); // When commandServiceImpl.get(commandName); } @Test(expected = SCommandNotFoundException.class) public final void getByNameThrowException() throws SBonitaReadException, SCommandNotFoundException { // Given final String commandName = "name"; when(persistence.selectOne(SelectDescriptorBuilder.getCommandByName(commandName))) .thenThrow(new SBonitaReadException("")); // When commandServiceImpl.get(commandName); } /** * Test method for * {@link org.bonitasoft.engine.command.api.impl.CommandServiceImpl#getNumberOfCommands(org.bonitasoft.engine.persistence.QueryOptions)}. * * @throws SBonitaReadException * @throws SBonitaReadException */ @Test public final void getNumberOfCommands() throws SBonitaReadException { // Given final long numberOfCommands = 54165L; final QueryOptions options = mock(QueryOptions.class); when(persistence.getNumberOfEntities(SCommand.class, options, null)).thenReturn(numberOfCommands); // When final long result = commandServiceImpl.getNumberOfCommands(options); // Then Assert.assertEquals(numberOfCommands, result); } @Test(expected = SBonitaReadException.class) public final void getNumberOfCommandsThrowException() throws SBonitaReadException { // Given final QueryOptions options = mock(QueryOptions.class); when(persistence.getNumberOfEntities(SCommand.class, options, null)).thenThrow(new SBonitaReadException("")); // When commandServiceImpl.getNumberOfCommands(options); } /** * Test method for * {@link org.bonitasoft.engine.command.api.impl.CommandServiceImpl#getUserCommands(int, int, org.bonitasoft.engine.command.model.SCommandCriterion)}. * * @throws SCommandGettingException * @throws SBonitaReadException */ @Test public final void getUserCommands() throws SCommandGettingException, SBonitaReadException { // Given final List sCommands = new ArrayList(); final String field = "name"; final OrderByType orderByType = OrderByType.ASC; final int startIndex = 0; final int maxResults = 1; when(persistence .selectList(SelectDescriptorBuilder.getUserCommands(field, orderByType, startIndex, maxResults))) .thenReturn(sCommands); // When final List userCommands = commandServiceImpl.getUserCommands(startIndex, maxResults, SCommandCriterion.NAME_ASC); // Then Assert.assertEquals(sCommands, userCommands); } @Test(expected = SCommandGettingException.class) public final void getUserCommandsThrowException() throws SCommandGettingException, SBonitaReadException { // Given final String field = "name"; final OrderByType orderByType = OrderByType.ASC; final int startIndex = 0; final int maxResults = 1; when(persistence .selectList(SelectDescriptorBuilder.getUserCommands(field, orderByType, startIndex, maxResults))) .thenThrow( new SBonitaReadException("")); // When commandServiceImpl.getUserCommands(startIndex, maxResults, SCommandCriterion.NAME_DESC); } /** * Test method for * {@link org.bonitasoft.engine.command.api.impl.CommandServiceImpl#searchCommands(org.bonitasoft.engine.persistence.QueryOptions)}. * * @throws SBonitaReadException * @throws SBonitaReadException */ @Test public final void searchCommands() throws SBonitaReadException { // Given final List sCommands = new ArrayList(); final QueryOptions options = mock(QueryOptions.class); when(persistence.searchEntity(SCommand.class, options, null)).thenReturn(sCommands); // When final List searchCommands = commandServiceImpl.searchCommands(options); // Then Assert.assertEquals(sCommands, searchCommands); } @Test(expected = SBonitaReadException.class) public final void searchCommandsThrowException() throws SBonitaReadException { // Given final QueryOptions options = mock(QueryOptions.class); when(persistence.searchEntity(SCommand.class, options, null)).thenThrow(new SBonitaReadException("")); // When commandServiceImpl.searchCommands(options); } @Test public void create_should_call_record_insert() throws Exception { //given //when //then } @Test public void start_should_add_missing_system_commands() throws Exception { //given CommandDeployment firstDeploy = new CommandDeployment("first", "the first command", "com.company.FirstCommand"); CommandDeployment secondDeploy = new CommandDeployment("second", "the second command", "com.company.SecondCommand"); CommandDeployment thirdDeploy = new CommandDeployment("third", "the third command", "com.company.ThirdCommand"); CommandDeployment fourthDeploy = new CommandDeployment("fourth", "the fourth command", "com.company.FourthCommand"); CommandDeployment fifthDeploy = new CommandDeployment("fifth", "the fifth command", "com.company.FifthCommand"); CommandDeployment sixthDeploy = new CommandDeployment("sixth", "the sixth command", "com.company.sixthCommand"); SCommand first = SCommand.builder().name("first").description("the first command") .implementation("com.company.FirstCommand").build(); SCommand second = SCommand.builder().name("second").description("the second command") .implementation("com.company.SecondCommand").build(); SCommand third = SCommand.builder().name("third").description("the third command") .implementation("com.company.ThirdCommand").build(); SCommand fourth = SCommand.builder().name("fourth").description("the fourth command") .implementation("com.company.FourthCommand").build(); SCommand fifth = SCommand.builder().name("fifth").description("the fifth command") .implementation("com.company.FifthCommand").build(); SCommand sixth = SCommand.builder().name("sixth").description("the sixth command") .implementation("com.company.sixthCommand").build(); given(commandProvider.getDefaultCommands()).willReturn( Arrays.asList(firstDeploy, secondDeploy, thirdDeploy, fourthDeploy, fifthDeploy, sixthDeploy)); CommandServiceImpl mockedCommandService = spy(commandServiceImpl); given(mockedCommandService.searchCommands(getQueryOptions(0, FETCH_SIZE))) .willReturn(Arrays.asList(first, third)); given(mockedCommandService.searchCommands(getQueryOptions(2, FETCH_SIZE))).willReturn(Arrays.asList(sixth)); doNothing().when(mockedCommandService).create(any(SCommand.class)); //when mockedCommandService.start(); //then ArgumentCaptor commandArgumentCaptor = ArgumentCaptor.forClass(SCommand.class); verify(mockedCommandService, times(3)).create(commandArgumentCaptor.capture()); assertThat(commandArgumentCaptor.getAllValues()).usingElementComparator(new CommandComparator()) .contains(second, fourth, fifth); } @Test public void start_should_delete_system_commands_not_present_in_default_commands() throws Exception { //given CommandDeployment secondDeploy = new CommandDeployment("second", "the second command", "com.company.SecondCommand"); CommandDeployment fifthDeploy = new CommandDeployment("fifth", "the fifth command", "com.company.FifthCommand"); SCommand first = SCommand.builder().name("first").description("the first command") .implementation("com.company.FirstCommand").build(); SCommand second = SCommand.builder().name("second").description("the second command") .implementation("com.company.SecondCommand").build(); SCommand third = SCommand.builder().name("third").description("the third command") .implementation("com.company.ThirdCommand").build(); SCommand fourth = SCommand.builder().name("fourth").description("the fourth command") .implementation("com.company.FourthCommand").build(); SCommand fifth = SCommand.builder().name("fifth").description("the fifth command") .implementation("com.company.FifthCommand").build(); given(commandProvider.getDefaultCommands()).willReturn(Arrays.asList(secondDeploy, fifthDeploy)); CommandServiceImpl mockedCommandService = spy(commandServiceImpl); given(mockedCommandService.searchCommands(getQueryOptions(0, FETCH_SIZE))) .willReturn(Arrays.asList(first, second)); given(mockedCommandService.searchCommands(getQueryOptions(2, FETCH_SIZE))) .willReturn(Arrays.asList(third, fourth)); given(mockedCommandService.searchCommands(getQueryOptions(4, FETCH_SIZE))).willReturn(Arrays.asList(fifth)); doNothing().when(mockedCommandService).delete(any(SCommand.class), any(SCommandLogBuilder.class)); //when mockedCommandService.start(); //then ArgumentCaptor commandArgumentCaptor = ArgumentCaptor.forClass(SCommand.class); verify(mockedCommandService, times(3)).delete(commandArgumentCaptor.capture(), any(SCommandLogBuilder.class)); assertThat(commandArgumentCaptor.getAllValues()).usingElementComparator(new CommandComparator()).contains(first, third, fourth); } @Test public void start_should_update_existing_system_commands() throws Exception { //given CommandDeployment firstDeploy = new CommandDeployment("first", "the first command with a modified description", "com.company.FirstCommand"); CommandDeployment secondDeploy = new CommandDeployment("second", "the second command", "com.company.SecondCommand"); CommandDeployment thirdDeploy = new CommandDeployment("third", "the third command", "com.company.ThirdCommandModified"); SCommand first = SCommand.builder().name("first").description("the first command") .implementation("com.company.FirstCommand").build(); SCommand second = SCommand.builder().name("second").description("the second command") .implementation("com.company.SecondCommand").build(); SCommand third = SCommand.builder().name("third").description("the third command") .implementation("com.company.ThirdCommand").build(); given(commandProvider.getDefaultCommands()).willReturn(Arrays.asList(firstDeploy, secondDeploy, thirdDeploy)); CommandServiceImpl mockedCommandService = spy(commandServiceImpl); given(mockedCommandService.searchCommands(getQueryOptions(0, FETCH_SIZE))) .willReturn(Arrays.asList(first, second)); given(mockedCommandService.searchCommands(getQueryOptions(2, FETCH_SIZE))).willReturn(Arrays.asList(third)); doNothing().when(mockedCommandService).update(any(SCommand.class), any(EntityUpdateDescriptor.class)); //when mockedCommandService.start(); //then ArgumentCaptor commandCaptor = ArgumentCaptor.forClass(SCommand.class); ArgumentCaptor updateDescriptorCaptor = ArgumentCaptor .forClass(EntityUpdateDescriptor.class); verify(mockedCommandService, times(2)).update(commandCaptor.capture(), updateDescriptorCaptor.capture()); assertThat(commandCaptor.getAllValues()).usingElementComparator(new CommandComparator()).contains(first, third); assertThat(updateDescriptorCaptor.getAllValues()).contains( new SCommandUpdateBuilderImpl().updateDescription("the first command with a modified description") .done(), new SCommandUpdateBuilderImpl().updateImplementation("com.company.ThirdCommandModified").done()); } private QueryOptions getQueryOptions(final int fromIndex, int maxResults) { return new QueryOptions(fromIndex, maxResults, Collections.singletonList(new OrderByOption(SCommand.class, "id", OrderByType.ASC)), Collections.singletonList(new FilterOption(SCommand.class, "isSystem", true)), null); } } ================================================ FILE: services/bonita-command/src/test/java/org/bonitasoft/engine/command/comparator/CommandComparator.java ================================================ /** * 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.command.comparator; import java.util.Comparator; import org.bonitasoft.engine.command.model.SCommand; /** * @author Elias Ricken de Medeiros */ public class CommandComparator implements Comparator { @Override public int compare(final SCommand o1, final SCommand o2) { if (o1.getName().equals(o2.getName()) && o1.getDescription().equals(o2.getDescription()) && o1.getImplementation().equals(o2.getImplementation())) { return 0; } return 1; } } ================================================ FILE: services/bonita-commons/build.gradle ================================================ import org.bonitasoft.engine.gradle.PomUtils dependencies { api project(':bpm:bonita-common') api(libs.commonsBeanUtils) { exclude(module: 'commons-collections') } api libs.commonsIO api libs.jakartaActivation api libs.commonsLang api libs.commonsText api(libs.xstream) api libs.micrometerCore api libs.springContext api libs.springBootAutoconfigure api libs.slf4jApi testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.systemRules testImplementation libs.systemLambda testImplementation libs.logback annotationProcessor libs.lombok compileOnly libs.lombok } description = 'Bonita Engine Util Classes' tasks.register("sourcesJar", Jar) { from sourceSets.main.allJava archiveClassifier = 'sources' } tasks.register("javadocJar", Jar) { from javadoc archiveClassifier = 'javadoc' } publishing { publications { mavenJava(MavenPublication) { from project.components.java artifact project.sourcesJar artifact project.javadocJar pom { pom -> name = "Bonita Business Data Generator" description = 'Bonita Engine Util Classes' PomUtils.pomCommunityPublication(pom) } } } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/DeepRegexFileFilter.java ================================================ /** * 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; import java.io.File; import java.util.regex.Pattern; import org.apache.commons.io.filefilter.AbstractFileFilter; import org.bonitasoft.engine.commons.StringUtils; /** * Identical to {@link org.apache.commons.io.filefilter.RegexFileFilter}, but accept files based on the complete file * path and name, not only its name. * * @author Emmanuel Duchastenier */ public class DeepRegexFileFilter extends AbstractFileFilter { /** * The regular expression pattern that will be used to match filenames */ private final Pattern regExPattern; public DeepRegexFileFilter(final String pattern) { if (pattern == null) { throw new IllegalArgumentException("Pattern is missing"); } regExPattern = Pattern.compile(pattern); } public DeepRegexFileFilter(final File parentDir, final String pattern) { this(StringUtils.uniformizePathPattern(parentDir.getAbsolutePath()) + "/" + pattern); } @Override public boolean accept(final File file) { final String fullName = StringUtils.uniformizePathPattern(file.getAbsolutePath()); return regExPattern.matcher(fullName).matches(); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/Experimental.java ================================================ /** * 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; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * An experimental SPI that can be used but might change in any version of Bonita. * Also no guarantee is provided on the correct behavior of this SPI. * * @author Baptiste Mesta. */ @Retention(value = RUNTIME) @Target(value = { TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, PACKAGE }) public @interface Experimental { } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/Profiles.java ================================================ /** * Copyright (C) 2020 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; public class Profiles { public static final String CLUSTER = "cluster"; public static final String NOT_IN_CLUSTER = "!cluster"; } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/ClassDataUtil.java ================================================ /** * 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.commons; import java.io.IOException; import java.io.InputStream; import org.bonitasoft.engine.commons.io.IOUtil; /** * @author Elias Ricken de Medeiros */ public class ClassDataUtil { public static byte[] getClassData(final Class clazz) throws IOException { if (clazz == null) { final String message = "Class is null"; throw new IOException(message); } final String resource = clazz.getName().replace('.', '/') + ".class"; final InputStream inputStream = clazz.getClassLoader().getResourceAsStream(resource); byte[] data = null; try { if (inputStream == null) { throw new IOException( "Impossible to get stream from class: " + clazz.getName() + ", className= " + resource); } data = IOUtil.getAllContentFrom(inputStream); } finally { if (inputStream != null) { inputStream.close(); } } return data; } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/ClassReflector.java ================================================ /** * 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.commons; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import org.apache.commons.text.WordUtils; import org.bonitasoft.engine.commons.exceptions.SReflectException; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Laurent Leseigneur */ public class ClassReflector { private static final String EMPTY = ""; private static final String SET = "set"; private static final String IS = "is"; private static final String GET = "get"; private static final Map methods; static { methods = new HashMap<>(); } private static final Object MUTEX = new Object(); public static Collection getAccessibleGetters(final Class clazz) { final Collection methods = new HashSet<>(); for (final Method method : clazz.getMethods()) { if (isAGetterMethod(method)) { methods.add(method); } } return methods; } public static Class getClass(final Class clazz, final String className) throws SReflectException { try { return (Class) Class.forName(className); } catch (final Exception e) { throw new SReflectException(e); } } public static T getObject(final Class clazz, final String className) throws SReflectException { try { return getClass(clazz, className).newInstance(); } catch (final Exception e) { throw new SReflectException(e); } } public static Constructor getConstructor(final Class clazz, final Class... parameterTypes) throws SReflectException { try { return clazz.getConstructor(parameterTypes); } catch (final Exception e) { throw new SReflectException(e); } } public static Constructor getConstructor(final Class clazz, final String className, final Class... parameterTypes) throws SReflectException { try { return getClass(clazz, className).getConstructor(parameterTypes); } catch (final Exception e) { throw new SReflectException(e); } } public static T getInstance(final Constructor constructor, final Object... parameters) throws SReflectException { try { return constructor.newInstance(parameters); } catch (final Exception e) { throw new SReflectException(e); } } @SuppressWarnings("unchecked") public static T invokeGetter(final Object entity, final String getterName) throws SReflectException { try { final Method getter = getMethod(entity.getClass(), getterName); return (T) getter.invoke(entity, (Object[]) null); } catch (final Exception e) { throw new SReflectException(e); } } public static void invokeSetter(final Object entity, final String setterName, final Class parameterType, final Object parameterValue) throws SReflectException { try { final Method setter = getMethod(entity.getClass(), setterName, parameterType); setter.invoke(entity, parameterValue); } catch (final Exception e) { throw new SReflectException(e); } } public static Method getMethod(final Class clazz, final String methodName, final Class... parameterTypes) throws NoSuchMethodException { final StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(clazz.hashCode()); stringBuilder.append(':'); stringBuilder.append(clazz.getName()); stringBuilder.append('.'); stringBuilder.append(methodName); stringBuilder.append('('); if (parameterTypes != null) { for (final Class class1 : parameterTypes) { stringBuilder.append(class1.getName()); stringBuilder.append(','); } } stringBuilder.append(')'); final String key = stringBuilder.toString(); putIfAbsent(clazz, methodName, key, parameterTypes); return methods.get(key); } private static void putIfAbsent(final Class clazz, final String methodName, final String key, final Class... parameterTypes) throws NoSuchMethodException { if (!methods.containsKey(key)) { synchronized (MUTEX) { // ensure that key was not put before between check and lock if (!methods.containsKey(key)) { methods.put(key, clazz.getMethod(methodName, parameterTypes)); } } } } public static Method getMethodByName(final Class clazz, final String methodName) { final String key = clazz.getName() + '.' + methodName; putIfAbsent(clazz, methodName, key); return methods.get(key); } private static void putIfAbsent(final Class clazz, final String methodName, final String key) { if (!methods.containsKey(key)) { synchronized (MUTEX) { // ensure that key was not put before between check and lock if (!methods.containsKey(key)) { final Method method = getFirstMethodWithName(clazz, methodName); methods.put(key, method); } } } } public static Method getFirstMethodWithName(final Class clazz, final String methodName) { Method selectedMethod = null; for (final Method method : clazz.getMethods()) { if (method.getName().equals(methodName)) { selectedMethod = method; break; } } return selectedMethod; } public static Object invokeMethodByName(final Object entity, final String methodName, final Object... parameterValues) throws SReflectException { final Class clazz = entity.getClass(); // no check on parameters final Method methodToInvoke = getMethodByName(clazz, methodName); if (methodToInvoke == null) { throw new SReflectException( "unable to find a method with name '" + methodName + "' within class " + clazz.getName()); } try { return methodToInvoke.invoke(entity, parameterValues); } catch (final Exception e) { throw new SReflectException(e); } } public static Object invokeMethod(final Object entity, final String methodName, final Class parameterType, final Object parameterValue) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { final Method method = getMethod(entity.getClass(), methodName, parameterType); return method.invoke(entity, parameterValue); } public static Object invokeMethod(final Object entity, final String methodName, final Class[] parameterType, final Object[] parameterValue) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { final Method method = getMethod(entity.getClass(), methodName, parameterType); return method.invoke(entity, parameterValue); } private static boolean isWrapped(final Class a, final Class b) { return a.equals(int.class) && b.equals(Integer.class) || a.equals(double.class) && b.equals(Double.class) || a.equals(boolean.class) && b.equals(Boolean.class) || a.equals(char.class) && b.equals(Character.class) || a.equals(long.class) && b.equals(Long.class) || a.equals(short.class) && b.equals(Short.class) || a.equals(float.class) && b.equals(Float.class) || a.equals(byte.class) && b.equals(Byte.class); } public static Method getCompatibleMethod(final Class clazz, final String methodName, final Class... paramTypes) throws SReflectException { try { return clazz.getMethod(methodName, paramTypes); } catch (final Exception e) { if (paramTypes != null) { final Method[] methods = clazz.getMethods(); for (final Method method : methods) { if (methodName.equals(method.getName())) { final Class[] types = method.getParameterTypes(); boolean check = true; if (!(types.length == paramTypes.length)) { throw new SReflectException("wrong parameters"); } for (int i = 0; i < types.length; i++) { if (!(types[i].isAssignableFrom(paramTypes[i]) || paramTypes[i].isAssignableFrom(types[i]) || isWrapped(types[i], paramTypes[i]))) { check = false; break; } } if (check) { return method; } } } } throw new SReflectException(e); } } public static Type getGetterReturnType(final Class classConnector, final String getterName) throws SReflectException { Method m; try { m = getMethod(classConnector, getterName); } catch (final Exception e) { throw new SReflectException(e); } return m.getGenericReturnType(); } public static Method[] getDeclaredSetters(final Class clazz) { final List setters = new ArrayList<>(); final Method[] methods = clazz.getDeclaredMethods(); for (final Method method : methods) { if (isASetterMethod(method)) { setters.add(method); } } return setters.toArray(new Method[setters.size()]); } public static Method[] getDeclaredGetters(final Class clazz) { final List getters = new ArrayList<>(); final Method[] methods = clazz.getDeclaredMethods(); for (final Method method : methods) { if (isAGetterMethod(method)) { getters.add(method); } } return getters.toArray(new Method[getters.size()]); } public static boolean isAGetterMethod(final Method method) { final String methodName = method.getName(); return (methodName.startsWith(GET) || methodName.startsWith(IS)) && method.getParameterTypes().length == 0 && !Void.class.equals(method.getReturnType()); } public static boolean isASetterMethod(final Method method) { final String methodName = method.getName(); return methodName.startsWith(SET) && "void".equals(method.getReturnType().toString()) && method.getParameterTypes().length == 1; } public static String getGetterName(final String fieldName) { return "get" + WordUtils.capitalize(fieldName); } public static String getGetterName(final String fieldName, final Class fieldType) { return getGetterPrefix(fieldType) + WordUtils.capitalize(fieldName); } private static String getGetterPrefix(Class fieldType) { if (fieldType.isAssignableFrom(Boolean.class)) { return IS; } return GET; } public static String getFieldName(final String methodName) { int cut = 4; if (methodName.startsWith(IS)) { cut = 3; } if (methodName.length() < cut) { return EMPTY; } final String end = methodName.substring(cut); final char c = methodName.charAt(cut - 1); final String begin = String.valueOf(c).toLowerCase(); return begin.concat(end); } /** * call a setter by reflection * support pointed notation like pojo.child.name * * @param object * object on with to call the setter * @param fieldName * @param parameterValue * @throws SReflectException */ public static void setField(Object object, String fieldName, Object parameterValue) throws SReflectException { String[] getters = fieldName.split("\\."); int i; for (i = 0; i < getters.length - 1; i++) { object = invokeMethodByName(object, getGetterName(getters[i])); } invokeMethodByName(object, getSetterName(getters[i]), parameterValue); } private static String getSetterName(String getter) { return "set" + WordUtils.capitalize(getter); } public static void clearCache() { methods.clear(); } public static int getCacheSize() { return methods.size(); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/CollectionUtil.java ================================================ /** * 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.commons; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.IntStream; public class CollectionUtil { public static Map buildSimpleMap(final String key, final Object value) { final Map result = new HashMap(); result.put(key, value); return result; } public static List emptyOrUnmodifiable(final List list) { return list == null ? Collections. emptyList() : Collections.unmodifiableList(list); } public static List> split(List source, int chunkSize) { if (chunkSize <= 0) { throw new IllegalArgumentException("can't split a list with a chunkSize = " + chunkSize); } int size = source.size(); if (size <= 0) { return Collections.emptyList(); } return IntStream.range(0, size / chunkSize + (size % chunkSize == 0 ? 0 : 1)) .mapToObj(n -> source.subList(n * chunkSize, Math.min(size, (n + 1) * chunkSize))) .collect(Collectors.toList()); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/Container.java ================================================ /** * 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.commons; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; /** * @author Elias Ricken de Medeiros */ public class Container { private long id; private String type; public Container(final long id, final String type) { this.id = id; this.type = type; } public long getId() { return id; } public String getType() { return type; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Container container = (Container) o; return new EqualsBuilder() .append(id, container.id) .append(type, container.type) .isEquals(); } @Override public int hashCode() { return new HashCodeBuilder(17, 37) .append(id) .append(type) .toHashCode(); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/EnumToObjectConvertible.java ================================================ /** * 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.commons; /** * Represent an enum which enum values can be convertible to other types. * One method: fromEnum, returns an object value, result of the conversion of that enum * * @author Emmanuel Duchastenier * @author Matthieu Chaffotte * @author Celine Souchet */ public interface EnumToObjectConvertible { /** * Convert this enum to an Object value * * @return The object corresponding to the Enum * @since 6.0 */ int fromEnum(); } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/ExceptionUtils.java ================================================ /** * Copyright (C) 2020 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.commons; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; public class ExceptionUtils { public static String printLightWeightStacktrace(Throwable exception) { return printLightWeightStacktrace(exception, 5); } public static String printLightWeightStacktrace(Throwable exception, int numberOfFrames) { List throwableList = org.apache.commons.lang3.exception.ExceptionUtils.getThrowableList(exception); if (throwableList.isEmpty()) { return null; } Collections.reverse(throwableList); Throwable rootCause = throwableList.get(0); return throwableList.stream().map(ExceptionUtils::print).collect(Collectors.joining("\n\twrapped by ")) + "\n exception was generated here:" + getStacktrace(rootCause, numberOfFrames); } private static String getStacktrace(Throwable t, int numberOfFrames) { String[] stackFrames = org.apache.commons.lang3.exception.ExceptionUtils.getStackFrames(t); return Arrays.stream(stackFrames).skip(1).limit(numberOfFrames).collect(Collectors.joining("\n")); } protected static String print(Throwable e) { return e.getClass().getName() + ": " + StringUtils.defaultString(e.getMessage()); } public static String printRootCauseOnly(Throwable exception) { return print(org.apache.commons.lang3.exception.ExceptionUtils.getRootCause(exception)); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/JavaMethodInvoker.java ================================================ /** * 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.commons; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import org.apache.commons.beanutils.MethodUtils; /** * Invokes a method on a Java Object. * * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public class JavaMethodInvoker { protected static Map> primitiveTypes; private static Map> autoboxableTypes; static { primitiveTypes = new HashMap>(8); primitiveTypes.put("char", char.class); primitiveTypes.put("byte", byte.class); primitiveTypes.put("long", long.class); primitiveTypes.put("int", int.class); primitiveTypes.put("float", float.class); primitiveTypes.put("double", double.class); primitiveTypes.put("short", short.class); primitiveTypes.put("boolean", boolean.class); autoboxableTypes = new HashMap>(8); autoboxableTypes.put(Character.class.getName(), char.class); autoboxableTypes.put(Byte.class.getName(), byte.class); autoboxableTypes.put(Long.class.getName(), long.class); autoboxableTypes.put(Integer.class.getName(), int.class); autoboxableTypes.put(Float.class.getName(), float.class); autoboxableTypes.put(Double.class.getName(), double.class); autoboxableTypes.put(Short.class.getName(), short.class); autoboxableTypes.put(Boolean.class.getName(), boolean.class); } protected Class getClassOrPrimitiveClass(final String type) throws ClassNotFoundException { if (primitiveTypes.containsKey(type)) { return primitiveTypes.get(type); } return Thread.currentThread().getContextClassLoader().loadClass(type); } public Object invokeJavaMethod(final String typeOfValueToSet, final Object valueToSetObjectWith, final Object objectToInvokeJavaMethodOn, final String operator, final String operatorParameterClassName) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { final Class expressionResultType = getClassOrPrimitiveClass(typeOfValueToSet); final Class dataType = Thread.currentThread().getContextClassLoader() .loadClass(objectToInvokeJavaMethodOn.getClass().getName()); final Method method = MethodUtils.getMatchingAccessibleMethod(dataType, operator, new Class[] { getClassOrPrimitiveClass(operatorParameterClassName) }); if (method != null) { final Object o = dataType.cast(objectToInvokeJavaMethodOn); method.invoke(o, expressionResultType.cast(valueToSetObjectWith)); return o; } else { throw new NoSuchMethodException( dataType.toGenericString() + "." + operator + "(" + operatorParameterClassName + ")."); } } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/LifecycleService.java ================================================ /** * 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.commons; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public interface LifecycleService { /** * Start the service * * @throws SBonitaException */ default void start() throws SBonitaException { } default void stop() throws SBonitaException { } /** * Temporary halt the execution of this service. */ default void pause() throws SBonitaException { } /** * resume the execution the service */ default void resume() throws SBonitaException { } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/LogUtil.java ================================================ /** * 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.commons; /** * This class is for Add call to techinalLoggerService.log with severity TRACE in all Service Implementation public * method * * @author Hongwen Zang * @author Matthieu Chaffotte * @since 6.0 */ public class LogUtil { /** * Get the log message on enter of the method * * @param classes * class name * @param methodName * method name * @return log message with String type */ public static String getLogBeforeMethod(final Object classes, final String methodName) { final String serviceName = classes.toString(); return "Executing method " + methodName + " on Service " + serviceName; } /** * Get the log message before quitting the method * * @param classes * class name * @param methodName * method name * @return log message with String type */ public static String getLogAfterMethod(final Object classes, final String methodName) { final String serviceName = classes.toString(); return "Quitting method " + methodName + " on Service " + serviceName; } /** * Get the log message in each catch block * * @param classes * class name * @param methodName * method name * @param e * Exception * @return log message with String type */ public static String getLogOnExceptionMethod(final Object classes, final String methodName, final Exception e) { final String serviceName = classes.toString(); return "Quitting method " + methodName + " on Service " + serviceName + " with exception " + e.getMessage(); } /** * Get the log message in each catch block * * @param classes * @param methodName * @param exceptionMessage * @return log message with String type */ public static String getLogOnExceptionMethod(final Object classes, final String methodName, final String exceptionMessage) { final String serviceName = classes.toString(); return "Quitting method " + methodName + " on Service " + serviceName + " with exception" + exceptionMessage; } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/NullCheckingUtil.java ================================================ /** * 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.commons; import java.util.BitSet; /** * @author Elias Ricken de Medeiros */ public class NullCheckingUtil { /** * Check that the given parameters are not null. * This method should only be used to check that some parameters given to a * given method are not null. The exception message tries its best to produce * a helpful message by scanning the stack trace. * * @param params * the parameters to check * @throws IllegalArgumentException * if at least one of the parameters is null */ public static void checkArgsNotNull(final Object... params) { checkArgsNotNull(1, params); } /** * Check that the given parameters are not null. * This method should only be used to check that some parameters given to a * given method are not null. The exception message tries its best to produce * a helpful message by scanning the stack trace. * * @param offset * the offset to use in the stack trace to produce error message * @param params * the parameters to check * @throws IllegalArgumentException * if at least one of the parameters is null */ private static void checkArgsNotNull(final int offset, final Object... params) { final NullCheckResult result = findNull(params); if (result.hasNull()) { // Guess the signature of the caller final StackTraceElement callerSTE = getCaller(offset + 1); final String className = callerSTE.getClassName(); final String methodName = callerSTE.getMethodName(); final StringBuilder sb = new StringBuilder(); for (int i = 0; i < result.getSize(); i++) { if (result.isNull(i)) { sb.append("null"); } else { sb.append(params[i].getClass().getName()); } if (i < result.getSize() - 1) { sb.append(", "); } } final StringBuilder messageStb = new StringBuilder("Some parameters are null in "); messageStb.append(className); messageStb.append('.'); messageStb.append(methodName); messageStb.append("(): "); messageStb.append(sb.toString()); throw new IllegalArgumentException(messageStb.toString()); } } /** * Find null parameters in the given list. * This method returns a {@link NullCheckResult}. * * @param params * the parameters to check * @return a {@link NullCheckResult} representing null parameters. * @see NullCheckResult */ public static NullCheckResult findNull(final Object... params) { if (params == null) { final BitSet bitSet = new BitSet(1); bitSet.set(0); return new NullCheckResult(bitSet, 1); } final BitSet bitSet = new BitSet(params.length); for (int i = 0; i < params.length; i++) { bitSet.set(i, params[i] == null); } return new NullCheckResult(bitSet, params.length); } /** * Return the StackTraceElement at the given offset from this method * invocation. * * @param offset * @return a StackTraceElement */ public static StackTraceElement getCaller(final int offset) { final StackTraceElement[] stes = Thread.currentThread().getStackTrace(); StackTraceElement callerSTE = null; for (int i = 0; i < stes.length - offset - 1; i++) { if (stes[i].getClassName().equals(NullCheckingUtil.class.getName()) && stes[i].getMethodName().equals("getCaller")) { callerSTE = stes[i + 1 + offset]; break; } } badStateIfNull(callerSTE, "Ouch! Can't get the stack trace back to the caller of this method!"); return callerSTE; } /** * This method throw an IllegalStateException if the given parameter is null * * @param valueToCheck * the value to check * @param msg * the message for the thrown exception * @see IllegalStateException */ public static void badStateIfNull(final Object valueToCheck, final String msg) { badStateIfTrue(valueToCheck == null, msg); } /** * This method throw an IllegalStateException if the given parameter is true * * @param valueToCheck * the value to check * @param msg * the message for the thrown exception * @see IllegalStateException */ public static void badStateIfTrue(final boolean valueToCheck, final String msg) { if (valueToCheck) { throw new IllegalStateException(msg); } } /** * Represents null value returned by {@link #findNull(Object...)}. * * @see #findNull(Object...) */ public static class NullCheckResult { private final int size; private final BitSet bitSet; NullCheckResult(final BitSet bitSet, final int size) { this.bitSet = bitSet; this.size = size; } /** * Returns true if some parameters given to {@link #findNull(Object...)} were null. * * @return true if some parameters given to {@link #findNull(Object...)} were null. * @see #findNull(Object...) */ public boolean hasNull() { return bitSet.cardinality() != 0; } /** * Returns the number of parameters given to {@link #findNull(Object...)} * * @return the number of parameters given to {@link #findNull(Object...)} * @see #findNull(Object...) */ public int getSize() { return size; } /** * Returns true if the i th parameter given to {@link #findNull(Object...)} was null. * * @param i * the rank of the parameter given to {@link #findNull(Object...)}. * @return true if the i th parameter given to {@link #findNull(Object...)} was null. */ public boolean isNull(final int i) { return bitSet.get(i); } } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/Pair.java ================================================ /** * 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.commons; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * @author Baptiste Mesta */ public class Pair implements Serializable, Map.Entry { private static final long serialVersionUID = 1L; private L left; private R right; public Pair(final L left, final R right) { this.left = left; this.right = right; } public static Pair of(final L left, final R right) { return new Pair(left, right); } public static Pair pair(final L left, final R right) { return of(left, right); } public static Map mapOf(Pair... entries) { Map map = new HashMap<>(); for (Pair entry : entries) { map.put(entry.getKey(), entry.getValue()); } return map; } public L getLeft() { return left; } public R getRight() { return right; } @Override public final L getKey() { return getLeft(); } @Override public R getValue() { return getRight(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (left == null ? 0 : left.hashCode()); result = prime * result + (right == null ? 0 : right.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } @SuppressWarnings("unchecked") final Pair other = (Pair) obj; if (left == null) { if (other.left != null) { return false; } } else if (!left.equals(other.left)) { return false; } if (right == null) { if (other.right != null) { return false; } } else if (!right.equals(other.right)) { return false; } return true; } @Override public String toString() { return "(" + getLeft() + "," + getRight() + ")"; } @Override public R setValue(final R right) { final R oldRight = this.right; this.right = right; return oldRight; } public L setKey(final L left) { final L oldLeft = this.left; this.left = left; return oldLeft; } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/PlatformLifecycleService.java ================================================ /** * 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.commons; /** * @author Matthieu Chaffotte */ public interface PlatformLifecycleService extends LifecycleService { } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/PlatformRestartHandler.java ================================================ /** * 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.commons; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public interface PlatformRestartHandler { void execute() throws SBonitaException; } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/StringUtils.java ================================================ /** * 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.commons; /** * String manipulation utilitary class. * * @author Emmanuel Duchastenier */ public class StringUtils { /** * Replaces all "\" character with "/" character, and ensures that there is no double "/". * * @param path the path-like string to clean. * @return the cleaned path-like string. */ public static String uniformizePathPattern(final String path) { return path.replaceAll("\\\\", "/").replaceAll("/{2,}", "/"); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/SystemOperationUtil.java ================================================ /** * 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.commons; /** * @author Elias Ricken de Medeiros */ public class SystemOperationUtil { /** * The line separator as defined by the property line.separator. */ public static final String LINE_SEPARATOR = System.getProperty("line.separator"); public static boolean isOnWindows() { return System.getProperty("os.name").contains("Windows"); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/TenantLifecycleService.java ================================================ /** * 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.commons; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public interface TenantLifecycleService extends LifecycleService { /** * This method performs actions to initialize a TenantLifecycleService * This method is called when creating a new tenant and when starting the platform. * * @throws SBonitaException */ default void init() throws SBonitaException { // nothing } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/TypeConverterUtil.java ================================================ /** * 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.commons; import java.io.Serializable; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZonedDateTime; import java.time.format.DateTimeParseException; import java.util.Date; import java.util.TimeZone; import org.apache.commons.beanutils.ConversionException; import org.apache.commons.beanutils.ConvertUtilsBean; import org.apache.commons.beanutils.converters.AbstractConverter; import org.apache.commons.beanutils.converters.DateConverter; /** * @author Laurent Leseigneur */ public class TypeConverterUtil { private ConvertUtilsBean convertUtilsBean; public TypeConverterUtil(String[] datePatterns) { convertUtilsBean = new ConvertUtilsBean(); final DateConverter dateConverter = new DateConverter(); dateConverter.setPatterns(datePatterns); dateConverter.setTimeZone(TimeZone.getTimeZone("GMT")); convertUtilsBean.register(dateConverter, Date.class); convertUtilsBean.register(new LocalDateTimeConverter(), LocalDateTime.class); convertUtilsBean.register(new LocalDateConverter(), LocalDate.class); convertUtilsBean.register(new OffsetDateTimeConverter(), OffsetDateTime.class); } public Object convertToType(Class clazz, Serializable parameterValue) { try { return convertUtilsBean.convert(parameterValue, clazz); } catch (ConversionException e) { throw new IllegalArgumentException("unable to parse '" + parameterValue + "' to type " + clazz.getName()); } } private class LocalDateConverter extends AbstractConverter { static final int MAX_LOCAL_DATE_LENGTH = 10; @Override protected T convertToType(Class type, Object value) throws Throwable { if (!(value instanceof String valueAsString)) { throw conversionException(type, value); } if (valueAsString.length() > MAX_LOCAL_DATE_LENGTH) { valueAsString = valueAsString.substring(0, MAX_LOCAL_DATE_LENGTH); } return type.cast(LocalDate.parse(valueAsString)); } @Override protected Class getDefaultType() { return LocalDate.class; } } private class LocalDateTimeConverter extends AbstractConverter { @Override protected T convertToType(Class type, Object value) throws Throwable { if (!(value instanceof String paramValueString)) { throw conversionException(type, value); } try { return type.cast(LocalDateTime.parse(paramValueString)); } catch (DateTimeParseException e) { //We drop the timezone info from the String: return type.cast(ZonedDateTime.parse(paramValueString).toLocalDateTime()); } } @Override protected Class getDefaultType() { return LocalDateTime.class; } } private class OffsetDateTimeConverter extends AbstractConverter { @Override protected T convertToType(Class type, Object value) throws Throwable { if (!(value instanceof String)) { throw conversionException(type, value); } return type.cast(OffsetDateTime.parse((CharSequence) value)); } @Override protected Class getDefaultType() { return OffsetDateTime.class; } } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/ExceptionContext.java ================================================ /** * 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.commons.exceptions; import java.io.Serializable; import java.util.Map; public interface ExceptionContext { Map getContext(); void setProcessDefinitionIdOnContext(final long id); void setProcessDefinitionNameOnContext(final String name); void setProcessDefinitionVersionOnContext(final String version); void setProcessInstanceIdOnContext(final long id); void setRootProcessInstanceIdOnContext(final long id); void setConnectorDefinitionIdOnContext(final String id); void setConnectorInputOnContext(String inputName); void setConnectorNameOnContext(String name); void setConnectorImplementationClassNameOnContext(final String name); void setConnectorDefinitionVersionOnContext(final String version); void setConnectorActivationEventOnContext(final String activationEvent); void setConnectorInstanceIdOnContext(final long id); void setFlowNodeDefinitionIdOnContext(final long id); void setFlowNodeInstanceIdOnContext(final long id); void setFlowNodeNameOnContext(final String name); void setMessageInstanceNameOnContext(final String name); void setMessageInstanceTargetProcessOnContext(final String name); void setMessageInstanceTargetFlowNodeOnContext(final String name); void setWaitingMessageEventTypeOnContext(final String eventType); void setDocumentIdOnContext(final long id); void setUserIdOnContext(final long userId); void setGroupIdOnContext(final long groupId); void setRoleIdOnContext(final long roleId); } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SAlreadyExistsException.java ================================================ /** * 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.commons.exceptions; /** * @author Baptiste Mesta */ public class SAlreadyExistsException extends SBonitaException { public SAlreadyExistsException(String s) { super(s); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SBonitaException.java ================================================ /** * 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.commons.exceptions; import java.io.Serializable; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public abstract class SBonitaException extends Exception implements ExceptionContext, ScopedException { private static final long serialVersionUID = -500856379312027085L; private final String exceptionId; private final Object[] arguments; private String scope = ScopedException.UNKNOWN_SCOPE; private final Map context = new TreeMap(); /** * Default constructor */ protected SBonitaException() { this((Object[]) null); } /** * @param arguments */ protected SBonitaException(final Object... arguments) { super(); exceptionId = this.getClass().getName(); this.arguments = arguments; } /** * @param message */ protected SBonitaException(final String message) { super(message); exceptionId = this.getClass().getName(); arguments = null; } protected SBonitaException(final String message, String scope) { super(message); this.scope = scope; exceptionId = this.getClass().getName(); arguments = null; } /** * @param message * @param cause */ protected SBonitaException(final String message, final Throwable cause) { super(message, cause); exceptionId = this.getClass().getName(); arguments = null; if (cause instanceof SBonitaException e && e.getContext() != null) { this.context.putAll(e.getContext()); } if (cause instanceof ScopedException e) { this.scope = e.getScope(); } } protected SBonitaException(final String message, String scope, final Throwable cause) { super(message, cause); exceptionId = this.getClass().getName(); arguments = null; this.scope = scope; if (cause instanceof SBonitaException e && e.getContext() != null) { this.context.putAll(e.getContext()); } } /** * @param cause */ protected SBonitaException(final Throwable cause) { this(cause, (Object[]) null); if (cause instanceof ScopedException e) { this.scope = e.getScope(); } } /** * @param cause * @param arguments */ protected SBonitaException(final Throwable cause, final Object... arguments) { super(cause); exceptionId = this.getClass().getName(); this.arguments = arguments; if (cause instanceof SBonitaException e && e.getContext() != null) { this.context.putAll(e.getContext()); } if (cause instanceof ScopedException e) { this.scope = e.getScope(); } } @Override public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } /** * This exception id is used to find potential causes * * @return the Id of the exception */ public String getExceptionId() { return exceptionId; } /** * @return */ public Object[] getParameters() { return arguments; } /** * @return The context of the exception * @since 6.3 */ @Override public Map getContext() { return context; } /** * @param id * The identifier of the process definition to set * @since 6.3 */ @Override public void setProcessDefinitionIdOnContext(final long id) { context.put(SExceptionContext.PROCESS_DEFINITION_ID, id); } /** * @param name * The name of the process definition to set * @since 6.3 */ @Override public void setProcessDefinitionNameOnContext(final String name) { context.put(SExceptionContext.PROCESS_NAME, name); } /** * @param version * The version of the process definition to set * @since 6.3 */ @Override public void setProcessDefinitionVersionOnContext(final String version) { context.put(SExceptionContext.PROCESS_VERSION, version); } /** * @param id * The identifier of the process instance to set * @since 6.3 */ @Override public void setProcessInstanceIdOnContext(final long id) { context.put(SExceptionContext.PROCESS_INSTANCE_ID, id); } /** * @param id * The identifier of the root process instance to set * @since 6.3 */ @Override public void setRootProcessInstanceIdOnContext(final long id) { context.put(SExceptionContext.ROOT_PROCESS_INSTANCE_ID, id); } /** * @param id * The identifier of the connector definition * @since 6.3 */ @Override public void setConnectorDefinitionIdOnContext(final String id) { context.put(SExceptionContext.CONNECTOR_DEFINITION_ID, id); } /** * @param name * The class name of the implementation of the connector definition to set * @since 6.3 */ @Override public void setConnectorImplementationClassNameOnContext(final String name) { context.put(SExceptionContext.CONNECTOR_IMPLEMENTATION_CLASS_NAME, name); } /** * @param version * The version of the connector definition * @since 6.3 */ @Override public void setConnectorDefinitionVersionOnContext(final String version) { context.put(SExceptionContext.CONNECTOR_DEFINITION_VERSION, version); } /** * @param activationEvent * The event which activates the connector to set * @since 6.3 */ @Override public void setConnectorActivationEventOnContext(final String activationEvent) { context.put(SExceptionContext.CONNECTOR_ACTIVATION_EVENT, activationEvent); } /** * @param id * The identifier of the connector instance to set * @since 6.3 */ @Override public void setConnectorInstanceIdOnContext(final long id) { context.put(SExceptionContext.CONNECTOR_INSTANCE_ID, id); } /** * @param id * The identifier of the flow node definition to set * @since 6.3 */ @Override public void setFlowNodeDefinitionIdOnContext(final long id) { context.put(SExceptionContext.FLOW_NODE_DEFINITION_ID, id); } /** * @param id * The identifier of the flow node instance to set * @since 6.3 */ @Override public void setFlowNodeInstanceIdOnContext(final long id) { context.put(SExceptionContext.FLOW_NODE_INSTANCE_ID, id); } /** * @param name * The name of the flow node to set * @since 6.3 */ @Override public void setFlowNodeNameOnContext(final String name) { context.put(SExceptionContext.FLOW_NODE_NAME, name); } /** * @param name * The name of the message instance to set * @since 6.3 */ @Override public void setMessageInstanceNameOnContext(final String name) { context.put(SExceptionContext.MESSAGE_INSTANCE_NAME, name); } /** * @param name * The target process name of the message instance to set * @since 6.3 */ @Override public void setMessageInstanceTargetProcessOnContext(final String name) { context.put(SExceptionContext.MESSAGE_INSTANCE_TARGET_PROCESS_NAME, name); } /** * @param name * The target flow node name of the message instance to set * @since 6.3 */ @Override public void setMessageInstanceTargetFlowNodeOnContext(final String name) { context.put(SExceptionContext.MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME, name); } /** * @param eventType * The event type of the waiting message instance to set * @since 6.3 */ @Override public void setWaitingMessageEventTypeOnContext(final String eventType) { context.put(SExceptionContext.WAITING_MESSAGE_INSTANCE_TYPE, eventType); } /** * @param id * The identifier of the document * @since 6.3 */ @Override public void setDocumentIdOnContext(final long id) { context.put(SExceptionContext.DOCUMENT_ID, id); } /** * @param userId * The identifier of the user * @since 6.3 */ @Override public void setUserIdOnContext(final long userId) { context.put(SExceptionContext.USER_ID, userId); } /** * @param groupId * The identifier of the group * @since 6.3 */ @Override public void setGroupIdOnContext(final long groupId) { context.put(SExceptionContext.GROUP_ID, groupId); } /** * @param roleId * The identifier of the role * @since 6.3 */ @Override public void setRoleIdOnContext(final long roleId) { context.put(SExceptionContext.ROLE_ID, roleId); } @Override public void setConnectorInputOnContext(String inputName) { context.put(SExceptionContext.CONNECTOR_INPUT_NAME, inputName); } @Override public void setConnectorNameOnContext(String name) { context.put(SExceptionContext.CONNECTOR_NAME, name); } @Override public String getMessage() { final StringBuilder stringBuilder = new StringBuilder(); appendContextMessage(stringBuilder); appendCauseMessage(stringBuilder); return stringBuilder.toString(); } private void appendCauseMessage(final StringBuilder stringBuilder) { String message = super.getMessage(); if (message != null && message.isEmpty() && getCause() != null) { message = getCause().getMessage(); } if (message != null && !message.trim().isEmpty()) { stringBuilder.append(message); } } private void appendContextMessage(final StringBuilder stringBuilder) { if (context != null && !context.isEmpty()) { for (final Entry entry : context.entrySet()) { stringBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append(" | "); } } } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SBonitaRuntimeException.java ================================================ /** * 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.commons.exceptions; import java.io.Serializable; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class SBonitaRuntimeException extends RuntimeException implements ExceptionContext, ScopedException { private static final long serialVersionUID = 7268935639620676043L; private final Map context = new TreeMap<>(); private String scope = ScopedException.UNKNOWN_SCOPE; public SBonitaRuntimeException(final Throwable cause) { super(cause); if (cause instanceof SBonitaException e && e.getContext() != null) { this.context.putAll(e.getContext()); } if (cause instanceof ScopedException e) { this.scope = e.getScope(); } } public SBonitaRuntimeException(final String message, final Throwable cause) { super(message, cause); if (cause instanceof SBonitaException e && e.getContext() != null) { this.context.putAll(e.getContext()); } if (cause instanceof ScopedException e) { this.scope = e.getScope(); } } public SBonitaRuntimeException(final String message, String scope, final Throwable cause) { super(message, cause); this.scope = scope; if (cause instanceof SBonitaException e && e.getContext() != null) { this.context.putAll(e.getContext()); } } public SBonitaRuntimeException(final String message) { super(message); } @Override public String getScope() { return this.scope; } /** * @return The context of the exception * @since 6.3 */ public Map getContext() { return context; } /** * @param id * The identifier of the process definition to set * @since 6.3 */ @Override public void setProcessDefinitionIdOnContext(final long id) { context.put(SExceptionContext.PROCESS_DEFINITION_ID, id); } /** * @param name * The name of the process definition to set * @since 6.3 */ @Override public void setProcessDefinitionNameOnContext(final String name) { context.put(SExceptionContext.PROCESS_NAME, name); } /** * @param version * The version of the process definition to set * @since 6.3 */ @Override public void setProcessDefinitionVersionOnContext(final String version) { context.put(SExceptionContext.PROCESS_VERSION, version); } /** * @param id * The identifier of the process instance to set * @since 6.3 */ @Override public void setProcessInstanceIdOnContext(final long id) { context.put(SExceptionContext.PROCESS_INSTANCE_ID, id); } /** * @param id * The identifier of the root process instance to set * @since 6.3 */ @Override public void setRootProcessInstanceIdOnContext(final long id) { context.put(SExceptionContext.ROOT_PROCESS_INSTANCE_ID, id); } /** * @param id * The identifier of the connector definition * @since 6.3 */ @Override public void setConnectorDefinitionIdOnContext(final String id) { context.put(SExceptionContext.CONNECTOR_DEFINITION_ID, id); } @Override public void setConnectorImplementationClassNameOnContext(String name) { context.put(SExceptionContext.CONNECTOR_IMPLEMENTATION_CLASS_NAME, name); } /** * @param version * The version of the connector definition * @since 6.3 */ @Override public void setConnectorDefinitionVersionOnContext(final String version) { context.put(SExceptionContext.CONNECTOR_DEFINITION_VERSION, version); } /** * @param activationEvent * The event which activates the connector to set * @since 6.3 */ @Override public void setConnectorActivationEventOnContext(final String activationEvent) { context.put(SExceptionContext.CONNECTOR_ACTIVATION_EVENT, activationEvent); } /** * @param id * The identifier of the connector instance to set * @since 6.3 */ @Override public void setConnectorInstanceIdOnContext(final long id) { context.put(SExceptionContext.CONNECTOR_INSTANCE_ID, id); } /** * @param id * The identifier of the flow node definition to set * @since 6.3 */ @Override public void setFlowNodeDefinitionIdOnContext(final long id) { context.put(SExceptionContext.FLOW_NODE_DEFINITION_ID, id); } /** * @param id * The identifier of the flow node instance to set * @since 6.3 */ @Override public void setFlowNodeInstanceIdOnContext(final long id) { context.put(SExceptionContext.FLOW_NODE_INSTANCE_ID, id); } /** * @param name * The name of the flow node to set * @since 6.3 */ @Override public void setFlowNodeNameOnContext(final String name) { context.put(SExceptionContext.FLOW_NODE_NAME, name); } /** * @param name * The name of the message instance to set * @since 6.3 */ @Override public void setMessageInstanceNameOnContext(final String name) { context.put(SExceptionContext.MESSAGE_INSTANCE_NAME, name); } /** * @param name * The target process name of the message instance to set * @since 6.3 */ @Override public void setMessageInstanceTargetProcessOnContext(final String name) { context.put(SExceptionContext.MESSAGE_INSTANCE_TARGET_PROCESS_NAME, name); } /** * @param name * The target flow node name of the message instance to set * @since 6.3 */ @Override public void setMessageInstanceTargetFlowNodeOnContext(final String name) { context.put(SExceptionContext.MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME, name); } /** * @param eventType * The event type of the waiting message instance to set * @since 6.3 */ @Override public void setWaitingMessageEventTypeOnContext(final String eventType) { context.put(SExceptionContext.WAITING_MESSAGE_INSTANCE_TYPE, eventType); } @Override public void setDocumentIdOnContext(long id) { context.put(SExceptionContext.DOCUMENT_ID, id); } @Override public void setUserIdOnContext(long userId) { context.put(SExceptionContext.USER_ID, userId); } @Override public void setGroupIdOnContext(long groupId) { context.put(SExceptionContext.GROUP_ID, groupId); } @Override public void setRoleIdOnContext(long roleId) { context.put(SExceptionContext.ROLE_ID, roleId); } @Override public void setConnectorInputOnContext(String inputName) { context.put(SExceptionContext.CONNECTOR_INPUT_NAME, inputName); } @Override public void setConnectorNameOnContext(String name) { context.put(SExceptionContext.CONNECTOR_NAME, name); } @Override public String getMessage() { final StringBuilder stringBuilder = new StringBuilder(); appendContextMessage(stringBuilder); appendCauseMessage(stringBuilder); return stringBuilder.toString(); } private void appendCauseMessage(final StringBuilder stringBuilder) { String message = super.getMessage(); if (message != null && message.isEmpty() && getCause() != null) { message = getCause().getMessage(); } if (message != null && !message.trim().isEmpty()) { stringBuilder.append(message); } } private void appendContextMessage(final StringBuilder stringBuilder) { if (!context.isEmpty()) { for (final Entry entry : context.entrySet()) { stringBuilder.append(entry.getKey()); stringBuilder.append("="); stringBuilder.append(entry.getValue()); stringBuilder.append(" | "); } } } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SDeletionException.java ================================================ /** * 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.commons.exceptions; /** * @author Baptiste Mesta */ public class SDeletionException extends SBonitaException { private static final long serialVersionUID = 1L; public SDeletionException() { } public SDeletionException(Object... arguments) { super(arguments); } public SDeletionException(String message) { super(message); } public SDeletionException(String message, Throwable cause) { super(message, cause); } public SDeletionException(Throwable cause) { super(cause); } public SDeletionException(Throwable cause, Object... arguments) { super(cause, arguments); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SExceptionContext.java ================================================ /** * 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.commons.exceptions; /** * To define the context of an exception in the message. * * @author Celine Souchet */ public enum SExceptionContext { /** * Corresponding to the identifier of the process definition */ PROCESS_DEFINITION_ID, /** * Corresponding to the name of the process definition */ PROCESS_NAME, /** * Corresponding to the version of the process definition */ PROCESS_VERSION, /** * Corresponding to the identifier of the process instance */ PROCESS_INSTANCE_ID, /** * Corresponding to the identifier of the root process instance */ ROOT_PROCESS_INSTANCE_ID, /** * Corresponding to the identifier of the flow node definition */ FLOW_NODE_DEFINITION_ID, /** * Corresponding to the identifier of the flow node instance */ FLOW_NODE_INSTANCE_ID, /** * Corresponding to the name of the flow node */ FLOW_NODE_NAME, /** * Corresponding to the name of the Message Instance */ MESSAGE_INSTANCE_NAME, /** * Corresponding to the target process name of the Message Instance */ MESSAGE_INSTANCE_TARGET_PROCESS_NAME, /** * Corresponding to the target flow node name of the Message Instance */ MESSAGE_INSTANCE_TARGET_FLOW_NODE_NAME, /** * Corresponding to the event type of the Waiting Message Instance */ WAITING_MESSAGE_INSTANCE_TYPE, /** * Corresponding to the name of the connector */ CONNECTOR_NAME, /** * Corresponding to the identifier of the connector definition */ CONNECTOR_DEFINITION_ID, /** * Corresponding to the name of the input of the connector */ CONNECTOR_INPUT_NAME, /** * Corresponding to the class name of the implementation of the connector definition */ CONNECTOR_IMPLEMENTATION_CLASS_NAME, /** * Corresponding to the version of the connector definition */ CONNECTOR_DEFINITION_VERSION, /** * Corresponding to the event which activates the connector */ CONNECTOR_ACTIVATION_EVENT, /** * Corresponding to the identifier of the connector instance */ CONNECTOR_INSTANCE_ID, /** * Corresponding to the identifier of the user */ USER_ID, /** * Corresponding to the identifier of the group */ GROUP_ID, /** * Corresponding to the identifier of the role */ ROLE_ID, /** * Corresponding to the identifier of the document */ DOCUMENT_ID, /** * Corresponding to the name of the transition */ TRANSITION_NAME, /** * Corresponding to the target flownode name of the transition */ TRANSITION_TARGET_FLOWNODE_NAME; } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SExecutionException.java ================================================ /** * 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.commons.exceptions; /** * when something went wrong during an execution * * @author Baptiste Mesta */ public class SExecutionException extends SBonitaException { public SExecutionException(String message) { super(message); } public SExecutionException(Throwable t) { super(t); } public SExecutionException(String message, Throwable cause) { super(message, cause); } public SExecutionException(SBonitaException e) { super(e); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SLifecycleException.java ================================================ /** * Copyright (C) 2020 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.commons.exceptions; /** * Exception happening during a life cycle operation such as start/stop of tenant services */ public class SLifecycleException extends SBonitaException { public SLifecycleException(String message, Exception cause) { super(message, cause); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SObjectAlreadyExistsException.java ================================================ /** * 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.commons.exceptions; /** * @author Baptiste Mesta */ public class SObjectAlreadyExistsException extends SBonitaException { private static final long serialVersionUID = -4043938145910922261L; /** * */ public SObjectAlreadyExistsException() { super(); } /** * @param arguments */ public SObjectAlreadyExistsException(final Object... arguments) { super(arguments); } /** * @param message * @param cause */ public SObjectAlreadyExistsException(final String message, final Throwable cause) { super(message, cause); } /** * @param message */ public SObjectAlreadyExistsException(final String message) { super(message); } /** * @param cause * @param arguments */ public SObjectAlreadyExistsException(final Throwable cause, final Object... arguments) { super(cause, arguments); } /** * @param cause */ public SObjectAlreadyExistsException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SObjectCreationException.java ================================================ /** * 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.commons.exceptions; /** * @author Baptiste Mesta */ public class SObjectCreationException extends SBonitaException { private static final long serialVersionUID = -4043938145910922261L; /** * */ public SObjectCreationException() { super(); } /** * @param arguments */ public SObjectCreationException(final Object... arguments) { super(arguments); } /** * @param message * @param cause */ public SObjectCreationException(final String message, final Throwable cause) { super(message, cause); } /** * @param message */ public SObjectCreationException(final String message) { super(message); } /** * @param cause * @param arguments */ public SObjectCreationException(final Throwable cause, final Object... arguments) { super(cause, arguments); } /** * @param cause */ public SObjectCreationException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SObjectModificationException.java ================================================ /** * 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.commons.exceptions; /** * @author Baptiste Mesta */ public class SObjectModificationException extends SBonitaException { private static final long serialVersionUID = 7771832630450258316L; /** * */ public SObjectModificationException() { super(); } /** * @param arguments */ public SObjectModificationException(final Object... arguments) { super(arguments); } /** * @param message * @param cause */ public SObjectModificationException(final String message, final Throwable cause) { super(message, cause); } /** * @param message */ public SObjectModificationException(final String message) { super(message); } /** * @param cause * @param arguments */ public SObjectModificationException(final Throwable cause, final Object... arguments) { super(cause, arguments); } /** * @param cause */ public SObjectModificationException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SObjectNotFoundException.java ================================================ /** * 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.commons.exceptions; /** * @author Baptiste Mesta */ public class SObjectNotFoundException extends SBonitaException { private static final long serialVersionUID = -4043938145910922261L; /** * */ public SObjectNotFoundException() { super(); } /** * @param arguments */ public SObjectNotFoundException(final Object... arguments) { super(arguments); } /** * @param message * @param cause */ public SObjectNotFoundException(final String message, final Throwable cause) { super(message, cause); } /** * @param message */ public SObjectNotFoundException(final String message) { super(message); } /** * @param cause * @param arguments */ public SObjectNotFoundException(final Throwable cause, final Object... arguments) { super(cause, arguments); } /** * @param cause */ public SObjectNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SObjectReadException.java ================================================ /** * 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.commons.exceptions; /** * @author Baptiste Mesta */ public class SObjectReadException extends SBonitaException { private static final long serialVersionUID = -183875236263807679L; /** * */ public SObjectReadException() { super(); } /** * @param arguments */ public SObjectReadException(final Object... arguments) { super(arguments); } /** * @param message * @param cause */ public SObjectReadException(final String message, final Throwable cause) { super(message, cause); } /** * @param message */ public SObjectReadException(final String message) { super(message); } /** * @param cause * @param arguments */ public SObjectReadException(final Throwable cause, final Object... arguments) { super(cause, arguments); } /** * @param cause */ public SObjectReadException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SReflectException.java ================================================ /** * 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.commons.exceptions; public class SReflectException extends SBonitaException { private static final long serialVersionUID = 3908092138962641331L; public SReflectException(final Throwable cause) { super(cause); } public SReflectException(final String message, final Throwable cause) { super(message, cause); } public SReflectException(final String message) { super(message); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SRetryableException.java ================================================ /** * 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.commons.exceptions; import java.io.Serial; /** * Runtime exception signaling that the failed operation may succeed on retry. *

      * This exception does not carry retry semantics in its type hierarchy — retryability * is determined at runtime by {@code DefaultExceptionRetryabilityEvaluator}, which is * configured in {@code bonita-community.xml} with a list of exception classes to retry. * {@code SRetryableException} is registered in that list by default. *

      * In the Work execution layer ({@code RetryingWorkExecutorService}), throwing this * exception triggers automatic retry of the failed Work. Outside that context (e.g. * scheduled jobs, direct API calls), callers catch it as a regular runtime exception. */ public class SRetryableException extends SBonitaRuntimeException { @Serial private static final long serialVersionUID = -2007050544702839857L; public SRetryableException(final Exception cause) { super(cause); } public SRetryableException(String message) { super(message); } public SRetryableException(String message, Exception cause) { super(message, cause); } @Override public synchronized Exception getCause() { return (Exception) super.getCause(); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/SV6FormsDeployException.java ================================================ /** * 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.commons.exceptions; /** * Thrown when a process fails to deploy because of V6 form presence in the bar to deploy. * * @author Danila Mazour * @since 7.8.0 */ public class SV6FormsDeployException extends SObjectCreationException { /** * @param message */ public SV6FormsDeployException(final String message) { super(message); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/exceptions/ScopedException.java ================================================ /** * 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.commons.exceptions; public interface ScopedException { String UNKNOWN_SCOPE = "UNKNOWN"; String GENERAL_INFORMATION = "General information"; String OPERATION = "Operation"; String EVENT = "Event"; String ITERATION = "Iteration"; String CONNECTOR = "Connector"; String DATA = "Data initialization"; String ACTOR_MAPPING = "Actor mapping"; String OUTGOING_TRANSITION = "Outgoing transition"; /** * Describe the scope of the exception with a human-readable category. * * @return The scope of the exception */ default String getScope() { return UNKNOWN_SCOPE; } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/io/IOUtil.java ================================================ /** * 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.commons.io; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Writer; import java.math.BigInteger; import java.net.URI; import java.net.URL; import java.nio.file.attribute.FileTime; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Properties; import java.util.Scanner; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; import java.util.jar.JarOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import javax.activation.MimetypesFileTypeMap; import javax.xml.XMLConstants; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.commons.io.FileUtils; import org.bonitasoft.engine.commons.ClassDataUtil; import org.bonitasoft.engine.commons.NullCheckingUtil; import org.bonitasoft.engine.commons.Pair; import org.w3c.dom.Document; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public class IOUtil { private static final String LINE_SEPARATOR = System.getProperty("line.separator"); public static final String TMP_DIRECTORY = System.getProperty("java.io.tmpdir"); public static final MimetypesFileTypeMap MIMETYPES_FILE_TYPE_MAP = new MimetypesFileTypeMap(); static { //on jdk 8 there is no png by default in mime types IOUtil.MIMETYPES_FILE_TYPE_MAP.addMimeTypes("image/png\t\tpng PNG"); IOUtil.MIMETYPES_FILE_TYPE_MAP.addMimeTypes("image/gif\t\tgif GIF"); IOUtil.MIMETYPES_FILE_TYPE_MAP.addMimeTypes("image/jpeg\t\tjpeg jpg jpe JPG"); } private static final int BUFFER_SIZE = 100000; public static final String FILE_ENCODING = "UTF-8"; private IOUtil() { // For Sonar } public static List getClassNameList(final byte[] jarContent) throws IOException { final List classes = new ArrayList<>(10); JarInputStream stream = null; ByteArrayInputStream byteArrayInputStream = null; try { byteArrayInputStream = new ByteArrayInputStream(jarContent); stream = new JarInputStream(byteArrayInputStream); JarEntry nextJarEntry = null; while ((nextJarEntry = stream.getNextJarEntry()) != null) { final String name = nextJarEntry.getName(); if (name.endsWith(".class")) { classes.add(toQualifiedClassName(name)); } } } finally { if (stream != null) { stream.close(); } if (byteArrayInputStream != null) { byteArrayInputStream.close(); } } return classes; } private static String toQualifiedClassName(final String name) { return name.replace('/', '.').replaceAll(".class", ""); } public static void write(final File file, final byte[] fileContent) throws IOException { NullCheckingUtil.checkArgsNotNull(file, fileContent); if (!file.exists()) { file.getParentFile().mkdirs(); file.createNewFile(); } final OutputStream os = new FileOutputStream(file); try { os.write(fileContent); os.flush(); } finally { os.close(); } } public static byte[] generateJar(final Class... classes) throws IOException { return generateJar(getResources(classes)); } public static Map getResources(final Class... classes) throws IOException { if (classes == null || classes.length == 0) { final String message = "No classes available"; throw new IOException(message); } final Map resources = new HashMap<>(); for (final Class clazz : classes) { resources.put(clazz.getName().replace(".", "/") + ".class", ClassDataUtil.getClassData(clazz)); for (final Class internalClass : clazz.getDeclaredClasses()) { resources.put(internalClass.getName().replace(".", "/") + ".class", ClassDataUtil.getClassData(internalClass)); } } return resources; } public static byte[] generateJar(final Map resources) throws IOException { if (resources == null || resources.isEmpty()) { throw new IOException("No resources available"); } try (var baos = new ByteArrayOutputStream(); var jarOutStream = new JarOutputStream(new BufferedOutputStream(baos));) { for (final Map.Entry resource : resources.entrySet()) { var entry = new JarEntry(resource.getKey()); // Force entry timestamp to an arbitrary date to ease jar content comparison entry.setCreationTime(FileTime.from(Instant.ofEpochMilli(0))); entry.setLastModifiedTime(FileTime.from(Instant.ofEpochMilli(0))); entry.setLastAccessTime(FileTime.from(Instant.ofEpochMilli(0))); entry.setTimeLocal(LocalDateTime.ofEpochSecond(0, 0, ZoneOffset.UTC)); entry.setTime(0); jarOutStream.putNextEntry(entry); jarOutStream.write(resource.getValue()); } jarOutStream.flush(); baos.flush(); baos.close(); jarOutStream.close(); return baos.toByteArray(); } } public static byte[] generateZip(final Map resources) throws IOException { if (resources == null || resources.isEmpty()) { throw new IOException("No resources available"); } ByteArrayOutputStream baos = null; ZipOutputStream zipOutStream = null; BufferedOutputStream bufferedOutputStream = null; try { baos = new ByteArrayOutputStream(); bufferedOutputStream = new BufferedOutputStream(baos); zipOutStream = new ZipOutputStream(bufferedOutputStream); for (final Map.Entry resource : resources.entrySet()) { zipOutStream.putNextEntry(new ZipEntry(resource.getKey())); zipOutStream.write(resource.getValue()); } zipOutStream.flush(); baos.flush(); } finally { if (zipOutStream != null) { zipOutStream.close(); } if (baos != null) { baos.close(); } if (bufferedOutputStream != null) { bufferedOutputStream.close(); } } return baos.toByteArray(); } /** * Return the whole underlying stream content into a single String. * Warning: the whole content of stream will be kept in memory!! Use with * care! * * @param in * the stream to read * @return the whole content of the stream in a single String. * @throws IOException * if an I/O exception occurs */ public static byte[] getAllContentFrom(final InputStream in) throws IOException { if (in == null) { throw new IOException("The InputStream is null!"); } final byte[] buffer = new byte[BUFFER_SIZE]; final byte[] resultArray; BufferedInputStream bis = null; ByteArrayOutputStream result = null; try { bis = new BufferedInputStream(in); result = new ByteArrayOutputStream(); int amountRead; while ((amountRead = bis.read(buffer)) > 0) { result.write(buffer, 0, amountRead); } resultArray = result.toByteArray(); result.flush(); } finally { if (bis != null) { bis.close(); } if (result != null) { result.close(); } } return resultArray; } /** * Read the contents from the given FileInputStream. Return the result as a String. * * @param inputStream * the stream to read from * @return the content read from the inputStream, as a String */ public static String read(final InputStream inputStream) { final Scanner scanner = new Scanner(inputStream, FILE_ENCODING); final StringBuilder text = new StringBuilder(); try { boolean isFirst = true; while (scanner.hasNextLine()) { if (isFirst) { text.append(scanner.nextLine()); } else { text.append(LINE_SEPARATOR + scanner.nextLine()); } isFirst = false; } } finally { scanner.close(); } return text.toString(); } /** * Read the contents of the given file. * * @param file */ public static String read(final File file) throws IOException { final FileInputStream inputStream = new FileInputStream(file); try { return read(inputStream); } finally { inputStream.close(); } } /** * Equivalent to {@link #getAllContentFrom(InputStream) getAllContentFrom(new * FileInputStream(file))}; * * @param file * the file to read * @return the whole content of the file in a single String. * @throws IOException * If an I/O exception occurs */ public static byte[] getAllContentFrom(final File file) throws IOException { InputStream in = null; try { in = new FileInputStream(file); return getAllContentFrom(in); } finally { if (in != null) { in.close(); } } } /** * Return the whole underlying stream content into a single String. * Warning: the whole content of stream will be kept in memory!! Use with * care! * * @param url * the URL to read * @return the whole content of the stream in a single String. * @throws IOException * if an I/O exception occurs */ public static byte[] getAllContentFrom(final URL url) throws IOException { final InputStream in = url.openStream(); try { return getAllContentFrom(in); } finally { in.close(); } } public static boolean deleteDir(final File dir) throws IOException { return deleteDir(dir, 1, 0); } public static boolean deleteDir(final File dir, final int attempts, final long sleepTime) throws IOException { boolean result = true; if (!dir.exists()) { return true; //already deleted } if (!dir.isDirectory()) { throw new IOException("Unable to delete directory: " + dir + ", it is not a directory"); } for (final File file : dir.listFiles()) { if (file.isDirectory()) { result &= deleteDir(file, attempts, sleepTime); } else { result &= deleteFile(file, attempts, sleepTime); } } return result && deleteFile(dir, attempts, sleepTime); } public static boolean deleteFile(final File file, final int attempts, final long sleepTime) { int retries = attempts; while (retries > 0) { if (file.delete()) { break; } retries--; try { Thread.sleep(sleepTime); } catch (final InterruptedException e) { } } return retries > 0; } public static void writeFile(final File file, final String fileContent) throws IOException { if (file == null) { throw new IllegalArgumentException("File should not be null."); } if (!file.exists()) { throw new FileNotFoundException("File does not exist: " + file); } if (!file.isFile()) { throw new IllegalArgumentException("Should not be a directory: " + file); } if (!file.canWrite()) { throw new IllegalArgumentException("File cannot be written: " + file); } // use buffering final Writer output = new BufferedWriter(new FileWriter(file)); try { output.write(fileContent); } finally { output.close(); } } public static byte[] zip(final Map files) throws IOException { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ZipOutputStream zos = new ZipOutputStream(baos); try { for (final Entry file : files.entrySet()) { zos.putNextEntry(new ZipEntry(file.getKey())); zos.write(file.getValue()); zos.flush(); zos.closeEntry(); } } finally { zos.close(); baos.close(); } return baos.toByteArray(); } public static byte[] zip(final Pair... files) throws IOException { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ZipOutputStream zos = new ZipOutputStream(baos); try { for (final Entry file : files) { zos.putNextEntry(new ZipEntry(file.getKey())); zos.write(file.getValue()); zos.flush(); zos.closeEntry(); } } finally { zos.close(); baos.close(); } return baos.toByteArray(); } public static final Map unzip(final byte[] zipFile) throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(zipFile); final ZipInputStream zipInputstream = new ZipInputStream(bais); ZipEntry zipEntry = null; final Map files = new HashMap<>(); try { while ((zipEntry = zipInputstream.getNextEntry()) != null) { final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); int bytesRead; final byte[] buffer = new byte[BUFFER_SIZE]; while ((bytesRead = zipInputstream.read(buffer)) > -1) { byteArrayOutputStream.write(buffer, 0, bytesRead); } files.put(zipEntry.getName(), byteArrayOutputStream.toByteArray()); } } finally { zipInputstream.close(); } return files; } private static void writeZipInputToFile(final ZipInputStream zipInputstream, final File outputFile) throws FileNotFoundException, IOException { // The input is a file. An FileOutputStream is created to write the content of the new file. mkdirs(outputFile.getParentFile()); try { final FileOutputStream fileOutputStream = new FileOutputStream(outputFile); try { // The contents of the new file, that is read from the ZipInputStream using a buffer (byte []), is written. int bytesRead; final byte[] buffer = new byte[BUFFER_SIZE]; while ((bytesRead = zipInputstream.read(buffer)) > -1) { fileOutputStream.write(buffer, 0, bytesRead); } } finally { fileOutputStream.close(); } } catch (final IOException ioe) { // In case of error, the file is deleted outputFile.delete(); throw ioe; } } private static boolean mkdirs(final File file) { if (!file.exists()) { return file.mkdirs(); } return true; } public static byte[] getZipEntryContent(final String entryName, final InputStream inputStream) throws IOException { final ZipInputStream zipInputstream = new ZipInputStream(inputStream); ZipEntry zipEntry = null; try { while ((zipEntry = zipInputstream.getNextEntry()) != null) { if (!entryName.equals(zipEntry.getName())) { continue; } return getBytes(zipInputstream); } } finally { zipInputstream.close(); } throw new IOException("Entry " + entryName + " does not exists in the zip file"); } public static byte[] getBytes(ZipInputStream zipInputstream) throws IOException { final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); try { int bytesRead; final byte[] buffer = new byte[BUFFER_SIZE]; while ((bytesRead = zipInputstream.read(buffer)) > -1) { byteArrayOutputStream.write(buffer, 0, bytesRead); } return byteArrayOutputStream.toByteArray(); } finally { byteArrayOutputStream.close(); } } public static byte[] getZipEntryContent(final String entryName, final byte[] zipFile) throws IOException { return getZipEntryContent(entryName, new ByteArrayInputStream(zipFile)); } public static byte[] toByteArray(final Document document) throws IOException, TransformerException { if (document == null) { throw new IllegalArgumentException("Document should not be null."); } final TransformerFactory transformerFactory = TransformerFactory.newInstance(); try { transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // security-compliant transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, ""); // security-compliant } catch (IllegalArgumentException e) { //ignored, if not supported by the implementation } final Transformer tf = transformerFactory.newTransformer(); tf.setOutputProperty(OutputKeys.ENCODING, FILE_ENCODING); tf.setOutputProperty(OutputKeys.INDENT, "yes"); try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { tf.transform(new DOMSource(document), new StreamResult(out)); return out.toByteArray(); } } public static byte[] addJarEntry(final byte[] jarToUpdate, final String entryName, final byte[] entryContent) throws IOException { final byte[] buffer = new byte[4096]; try (ByteArrayInputStream bais = new ByteArrayInputStream(jarToUpdate); JarInputStream jis = new JarInputStream(bais); ByteArrayOutputStream out = new ByteArrayOutputStream(); JarOutputStream jos = new JarOutputStream(out)) { JarEntry inEntry; while ((inEntry = (JarEntry) jis.getNextEntry()) != null) { if (!inEntry.getName().equals(entryName)) { jos.putNextEntry(new JarEntry(inEntry)); } else { throw new IllegalArgumentException("Jar entry " + entryName + " already exists in jar to update"); } int len; while ((len = jis.read(buffer)) > 0) { jos.write(buffer, 0, len); } jos.flush(); } final JarEntry entry = new JarEntry(entryName); jos.putNextEntry(entry); jos.write(entryContent); jos.closeEntry(); jos.finish(); out.flush(); return out.toByteArray(); } } public static byte[] getPropertyAsString(final Properties prop, final String comment) throws IOException { final ByteArrayOutputStream out = new ByteArrayOutputStream(); prop.store(out, comment); return out.toByteArray(); } public static String readResource(String fileName) throws IOException { final String xmlContent; final InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName); if (inputStream == null) { return null; } try { xmlContent = read(inputStream); } finally { inputStream.close(); } return xmlContent; } public static String md5(byte[] content) throws NoSuchAlgorithmException { MessageDigest md5 = MessageDigest.getInstance("MD5"); return new BigInteger(1, md5.digest(content)).toString(16); } public static void writeMD5(File file, byte[] bytes) throws NoSuchAlgorithmException, IOException { write(file, md5(bytes).getBytes()); } public static boolean checkMD5(File md5File, byte[] contentToCheck) throws NoSuchAlgorithmException { if (!md5File.exists()) { return false; } try { return read(md5File).equals(md5(contentToCheck)); } catch (IOException e) { return false; } } public static void updatePropertyValue(File propertiesFile, final Map pairs) throws IOException { final BufferedReader br = new BufferedReader(new FileReader(propertiesFile)); try { StringBuilder sb = new StringBuilder(); String line = br.readLine(); while (line != null) { String lineToWrite = line; if (!line.startsWith("#")) { //it is not a comment final int splitCharIndex = line.indexOf("="); if (splitCharIndex >= 0) { final String key = line.substring(0, splitCharIndex); //this is a key-value pair if (pairs.containsKey(key.trim())) { String value = pairs.get(key.trim()); value = value.replace("\\", "\\\\"); lineToWrite = key + "=" + value; } } } sb.append(lineToWrite); sb.append(System.lineSeparator()); line = br.readLine(); } IOUtil.writeFile(propertiesFile, sb.toString()); } finally { if (br != null) { br.close(); } } } public static String getContentTypeForIcon(String iconFilename) { String contentType = MIMETYPES_FILE_TYPE_MAP.getContentType(iconFilename); if (!contentType.startsWith("image")) { throw new IllegalArgumentException("An icon can't have mimetype " + contentType); } return contentType; } /** * return the content of the file from the classpath, the file must be at the root of the classpath * * @param fileName name of the file * @return the optional, or empty if there is no file with that name * @throws IOException when we are unable to read the file from the classpath */ public static Optional getFileContent(final String fileName) throws IOException { try (final InputStream inputStream = Thread.currentThread().getContextClassLoader() .getResourceAsStream(fileName)) { if (inputStream == null) { return Optional.empty(); } return Optional.of(getAllContentFrom(inputStream)); } } // Copied from org.bonitasoft.engine.io.IOUtil, as it is used by client applications (bonita-web-sp) public static File createTempDirectory(final URI directoryPath) { final File tmpDir = new File(directoryPath); tmpDir.setReadable(true); tmpDir.setWritable(true); mkdirs(tmpDir); FileUtils.isSymlink(tmpDir); try { Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { final boolean deleted = deleteDir(tmpDir); if (!deleted) { System.err.println( "Unable to delete directory: " + tmpDir + ". Trying with an alternative force delete."); FileUtils.forceDelete(tmpDir); } } catch (final IOException e) { throw new RuntimeException(e); } })); } catch (IllegalStateException ignored) { // happen in case of hook already registered and when shutting down } return tmpDir; } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/io/PropertiesManager.java ================================================ /** * 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.commons.io; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; import java.util.Properties; /** * @author Matthieu Chaffotte * @author Frederic Bouquet * @author Celine Souchet */ public class PropertiesManager { private PropertiesManager() { // For Sonar } public static void saveProperties(final Properties properties, final String pathName) throws IOException { saveProperties(properties, new File(pathName)); } public static void saveProperties(final Properties properties, final File file) throws IOException { final FileOutputStream outputStream = new FileOutputStream(file); try { properties.store(outputStream, "Storing modified properties"); } finally { outputStream.close(); } } public static Properties getProperties(final URL url) throws IOException { final InputStreamReader reader = new InputStreamReader(url.openStream()); try { return getProperties(reader); } finally { reader.close(); } } public static Properties getProperties(final String fileName) throws IOException { return getProperties(new File(fileName)); } public static Properties getProperties(final File file) throws IOException { final FileReader reader = new FileReader(file); try { return getProperties(reader); } finally { reader.close(); } } private static Properties getProperties(final Reader reader) throws IOException { final Properties properties = new Properties(); properties.load(reader); return properties; } public static void savePropertiesToXML(final Properties properties, final File file) throws IOException { final FileOutputStream outputStream = new FileOutputStream(file); try { properties.storeToXML(outputStream, "Storing modified properties", "UTF-8"); } finally { outputStream.close(); } } public static Properties getPropertiesFromXML(final File file) throws IOException { final Properties properties = new Properties(); final FileInputStream fileInputStream = new FileInputStream(file); try { properties.loadFromXML(fileInputStream); return properties; } finally { fileInputStream.close(); } } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/time/DefaultEngineClock.java ================================================ /** * 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.commons.time; import java.time.Instant; /** * This is the default engine clock that return the real system time * * @author Baptiste Mesta. */ public class DefaultEngineClock implements EngineClock { @Override public Instant now() { return Instant.now(); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/time/EngineClock.java ================================================ /** * 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.commons.time; import java.time.Instant; /** * The {@link EngineClock} give the current time to the engine. * It can be overridden mainly for testing purpose * * @author Baptiste Mesta. */ public interface EngineClock { Instant now(); } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/time/FixedEngineClock.java ================================================ /** * 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.commons.time; import java.time.Instant; import java.time.temporal.TemporalUnit; /** * This is a fixed clock with some methods to change this fixed time * Only used for testing purpose * * @author Baptiste Mesta. */ public class FixedEngineClock implements EngineClock { private Instant now; public FixedEngineClock(Instant now) { this.now = now; } @Override public Instant now() { return now; } public void setNow(Instant now) { this.now = now; } public void addTime(long amountToAdd, TemporalUnit unit) { now = now.plus(amountToAdd, unit); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/transaction/TransactionContent.java ================================================ /** * 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.commons.transaction; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public interface TransactionContent { void execute() throws SBonitaException; } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/commons/transaction/TransactionContentWithResult.java ================================================ /** * 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.commons.transaction; /** * @author Baptiste Mesta */ public interface TransactionContentWithResult extends TransactionContent { T getResult(); } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/data/instance/model/impl/OffsetDateTimeXStreamConverter.java ================================================ /** * 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.data.instance.model.impl; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter; /** * @author Emmanuel Duchastenier */ public class OffsetDateTimeXStreamConverter extends AbstractSingleValueConverter { public boolean canConvert(Class type) { return OffsetDateTime.class.equals(type); } public String toString(Object source) { return source == null ? null : ((OffsetDateTime) source).withOffsetSameInstant(ZoneOffset.UTC) .format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); } public Object fromString(String str) { try { return OffsetDateTime.parse(str).withOffsetSameInstant(ZoneOffset.UTC); } catch (DateTimeParseException e) { throw new RuntimeException("OffsetDateTime failed to parse the incoming string", e); } } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/data/instance/model/impl/XStreamFactory.java ================================================ /** * 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.data.instance.model.impl; import java.util.Map; import java.util.WeakHashMap; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.security.AnyTypePermission; import org.bonitasoft.engine.xml.XStreamDenyList; public class XStreamFactory { private static final Map XSTREAM_MAP = new WeakHashMap<>(); public static XStream getXStream() { final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); return XSTREAM_MAP.computeIfAbsent(classLoader, cl -> { var xStream = new XStream(); // Make the deserialization loose to avoid issues like in RUNTIME-1884 xStream.ignoreUnknownElements(); xStream.addPermission(AnyTypePermission.ANY); // Block known deserialization gadget chain libraries to prevent RCE. // A strict allowlist is not possible here because process variables can contain any Serializable type. xStream.denyTypesByWildcard(XStreamDenyList.getDenyPatterns()); // Even though xStream now supports Java 8 date types, Bonita needs to convert offset date-time to UTC, by contract: xStream.registerConverter(new OffsetDateTimeXStreamConverter()); return xStream; }); } /** * Removes the XStream object related from given ClassLoader from the cache * * @param classLoader classLoader related to the XStreamObject to be removed. */ public static void remove(ClassLoader classLoader) { XSTREAM_MAP.remove(classLoader); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/home/Folder.java ================================================ /** * 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.home; import static org.bonitasoft.engine.commons.io.IOUtil.*; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.net.URI; import java.util.HashMap; import java.util.Map; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.FileFileFilter; /** * @author Charles Souillard */ public class Folder { private final File folder; public Folder(final File folder) throws IOException { if (folder == null) { throw new IOException("Folder is null"); } this.folder = folder; } public Folder(final Folder folder, final String subFolder) throws IOException { if (folder == null) { throw new IOException("Folder is null"); } this.folder = new File(folder.getFile(), subFolder); } public File getFile() { return this.folder; } private void checkFolderExists() throws IOException { if (!folder.exists()) { throw new IOException("Folder denoted by path " + folder.getAbsolutePath() + " does not exist."); } if (!folder.isDirectory()) { throw new IOException("Folder denoted by path " + folder.getAbsolutePath() + " is not a folder."); } } public void delete() throws IOException { //System.err.println("DELETING FOLDER: " + folder); checkFolderExists(); deleteDir(folder); } public File getFile(final String name) throws IOException { checkFolderExists(); return new File(folder, name); } public File newFile(final String name) throws IOException { checkFolderExists(); final File newFile = new File(folder, name); if (!newFile.createNewFile()) { throw new IOException("File " + newFile.getAbsolutePath() + " cannot be created"); } return newFile; } public void create() throws IOException { if (!folder.getParentFile().exists()) { throw new IOException("Folder denoted by path " + folder.getAbsolutePath() + " cannot be created as its parent does not exist."); } if (!folder.getParentFile().isDirectory()) { throw new IOException("Folder denoted by path " + folder.getAbsolutePath() + " cannot be created as its parent is not a folder."); } folder.mkdir(); } public void copyTo(Folder destFolder) throws IOException { checkFolderExists(); destFolder.create(); FileUtils.copyDirectory(this.getFile(), destFolder.getFile()); } public Map listFilesAsResources() throws IOException { checkFolderExists(); final Map resources = new HashMap<>(); final File[] files = this.folder.listFiles((FileFilter) FileFileFilter.FILE); if (files != null) { for (File file : files) { resources.put(file.getName(), getFileContent(file)); } } return resources; } protected byte[] getFileContent(File file) throws IOException { return getAllContentFrom(file); } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("Folder{folder="); sb.append(folder); sb.append(" --- exists:"); sb.append(folder.exists()); sb.append(" --- is directory:"); sb.append(folder.isDirectory()); sb.append('}'); return sb.toString(); } public boolean exists() { return this.folder.exists(); } public URI toURI() throws IOException { checkFolderExists(); return this.folder.toURI(); } public Folder createIfNotExists() throws IOException { if (!this.folder.exists()) { create(); } return this; } public void createAsTemporaryFolder() { createTempDirectory(this.folder.toURI()); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/home/FolderMgr.java ================================================ /** * 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.home; import java.io.File; import java.io.IOException; import java.lang.management.ManagementFactory; import java.util.Arrays; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Charles Souillard * @author Emmanuel Duchastenier */ public class FolderMgr { public static final Logger LOGGER = LoggerFactory.getLogger(FolderMgr.class); public static final String TEMP_FOLDER_NAME_PREFIX = "bonita_engine_"; private static Folder getFolder(final File baseFolder, final String subFolder) throws IOException { return new Folder(new Folder(baseFolder), subFolder); } static Folder getFolder(final Folder baseFolder, final String subFolder) throws IOException { return new Folder(baseFolder, subFolder); } static Folder getTempFolder() throws IOException { File systemTempFolder = new File(System.getProperty("java.io.tmpdir")); final Folder tempFolder = getFolder(systemTempFolder, TEMP_FOLDER_NAME_PREFIX + getJvmName()); if (!tempFolder.exists()) { warnIfSomeTempFolderAlreadyExists(systemTempFolder); tempFolder.createAsTemporaryFolder(); } return tempFolder; } private static void warnIfSomeTempFolderAlreadyExists(File systemTempFolder) { File[] files = systemTempFolder .listFiles((dir, name) -> dir.isDirectory() && name.startsWith(TEMP_FOLDER_NAME_PREFIX)); List temporaryFolders = Arrays.asList(files != null ? files : new File[0]); if (!temporaryFolders.isEmpty()) { LOGGER.warn("The following temporary folders were not deleted on the previous shutdown. " + "This can happen when your JVM crashed or if you did not properly stop the platform."); LOGGER.warn("Delete these folders to free up space:"); for (File temporaryFolder : temporaryFolders) { LOGGER.warn(temporaryFolder.getAbsolutePath()); } } } static Folder getPlatformTempFolder() throws IOException { return getFolder(getTempFolder(), "platform").createIfNotExists(); } public static Folder getLicensesFolder() throws IOException { return getFolder(getTempFolder(), "licenses"); } private static Folder getPlatformClassLoaderFolder() throws IOException { return getFolder(getPlatformTempFolder(), "classloaders").createIfNotExists(); } private static String getJvmName() { return ManagementFactory.getRuntimeMXBean().getName(); } static Folder getPlatformGlobalClassLoaderFolder() throws IOException { final Folder globalFolder = getFolder(getPlatformClassLoaderFolder(), "global"); globalFolder.createIfNotExists(); return globalFolder; } private static Folder getPlatformLocalClassLoaderFolder() throws IOException { final Folder localFolder = getFolder(getPlatformClassLoaderFolder(), "local"); localFolder.createIfNotExists(); return localFolder; } static Folder getPlatformLocalClassLoaderFolder(String artifactType) throws IOException { final Folder localFolder = getPlatformLocalClassLoaderFolder(); final Folder artifactTypeFolder = getFolder(localFolder, artifactType); artifactTypeFolder.createIfNotExists(); return artifactTypeFolder; } static Folder getPlatformLocalClassLoaderFolder(String artifactType, long artifactId) throws IOException { final Folder artifactTypeFolder = getPlatformLocalClassLoaderFolder(artifactType); final Folder artifactIdFolder = getFolder(artifactTypeFolder, Long.toString(artifactId)); artifactIdFolder.createIfNotExists(); return artifactIdFolder; } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/mdc/AbstractMDC.java ================================================ /** * 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.mdc; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; import org.slf4j.MDC; /** * The base class for our Mapped Diagnostic Contexts (MDC). * * @author Vincent Hemery */ public abstract class AbstractMDC implements AutoCloseable, MDCConstants { private Map entriesToRestore; public AbstractMDC(Map contextValues) { if (contextValues != null) { /* * We do not prevent nested MDCs with conflicting values, * as it may be legit e.g. in case of an ActivityCall * (which executes other flow nodes inside the parent flow node). * So we allow erasing any value in the context, * but also remember to restore the context in its original state at closure. */ // remember entries to restore/remove Map oldMap = Optional.ofNullable(MDC.getCopyOfContextMap()) .orElseGet(Collections::emptyMap); entriesToRestore = new HashMap<>(contextValues.size()); contextValues.forEach((k, newValue) -> { var valueToRestore = oldMap.getOrDefault(k, null); entriesToRestore.put(k, valueToRestore); }); // store new values contextValues.forEach((key, value) -> { if (value != null) { MDC.put(key, value); } }); } else { entriesToRestore = Collections.emptyMap(); } } @Override public void close() { // restore previous context entriesToRestore.forEach((k, v) -> { if (v == null) { MDC.remove(k); } else { MDC.put(k, v); } }); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/mdc/FlowNodeInstanceMDC.java ================================================ /** * 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.mdc; import java.util.Map; import java.util.Optional; public class FlowNodeInstanceMDC extends AbstractMDC implements AutoCloseable, MDCConstants { /** * Build a flow node execution context when no user is involved (e.g. service task). * * @param flowNodeInstanceId the id of the flow node instance * @param processDefinitionId the id of the process definition * @param processInstanceId the id of the process instance / case * @param rootProcessInstanceId the id of the root process instance / case */ public FlowNodeInstanceMDC(long flowNodeInstanceId, long processDefinitionId, long processInstanceId, long rootProcessInstanceId) { this(flowNodeInstanceId, Optional.empty(), Optional.empty(), processDefinitionId, processInstanceId, rootProcessInstanceId); } /** * Build a flow node execution context when a user is authenticated. * * @param flowNodeInstanceId the id of the flow node instance * @param executerId the id of the user executing the flow node * @param substituteUserId the id of the admin user activating the flow node for the executer * @param processDefinitionId the id of the process definition * @param processInstanceId the id of the process instance / case * @param rootProcessInstanceId the id of the root process instance / case */ public FlowNodeInstanceMDC(long flowNodeInstanceId, long executerId, long substituteUserId, long processDefinitionId, long processInstanceId, long rootProcessInstanceId) { this(flowNodeInstanceId, Optional.of(executerId), Optional.of(substituteUserId), processDefinitionId, processInstanceId, rootProcessInstanceId); } /** * Build a flow node execution context. * * @param flowNodeInstanceId the id of the flow node instance * @param executerId the eventual id of the user executing the flow node * @param substituteUserId the eventual id of the admin user activating the flow node for the executer * @param processDefinitionId the id of the process definition * @param processInstanceId the id of the process instance / case * @param rootProcessInstanceId the id of the root process instance / case */ public FlowNodeInstanceMDC(long flowNodeInstanceId, Optional executerId, Optional substituteUserId, long processDefinitionId, long processInstanceId, long rootProcessInstanceId) { super(buildContextMap(flowNodeInstanceId, executerId, substituteUserId, processDefinitionId, processInstanceId, rootProcessInstanceId)); } private static Map buildContextMap(long flowNodeInstanceId, Optional executerId, Optional substituteUserId, long processDefinitionId, long processInstanceId, long rootProcessInstanceId) { if (!substituteUserId.equals(executerId)) { // executed by substituteUserId for executerId return Map.of( FLOW_NODE_INSTANCE_ID, String.valueOf(flowNodeInstanceId), USER_ID, executerId.map(String::valueOf).orElse(null), SUBSTITUTE_USER_ID, substituteUserId.map(String::valueOf).orElse(null), PROCESS_DEFINITION_ID, String.valueOf(processDefinitionId), PROCESS_INSTANCE_ID, String.valueOf(processInstanceId), ROOT_PROCESS_INSTANCE_ID, String.valueOf(rootProcessInstanceId)); } else if (executerId.isPresent()) { // no need for SUBSTITUTE_USER_ID return Map.of( FLOW_NODE_INSTANCE_ID, String.valueOf(flowNodeInstanceId), USER_ID, executerId.map(String::valueOf).orElse(null), PROCESS_DEFINITION_ID, String.valueOf(processDefinitionId), PROCESS_INSTANCE_ID, String.valueOf(processInstanceId), ROOT_PROCESS_INSTANCE_ID, String.valueOf(rootProcessInstanceId)); } else { // no authenticated user return Map.of( FLOW_NODE_INSTANCE_ID, String.valueOf(flowNodeInstanceId), PROCESS_DEFINITION_ID, String.valueOf(processDefinitionId), PROCESS_INSTANCE_ID, String.valueOf(processInstanceId), ROOT_PROCESS_INSTANCE_ID, String.valueOf(rootProcessInstanceId)); } } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/mdc/MDCConstants.java ================================================ /** * 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.mdc; /** *

      These constants are used as logging keys for Mapped Diagnostic Context (MDC).

      *

      The REQUEST_* constants are the same as in ch.qos.logback.classic.ClassicConstants (but with no * dependency): *

      • req.remoteHost
      • req.userAgent
      • req.requestURI
      • req.queryString
      • req.requestURL
      • req.method
      • req.xForwardedFor
      *

      */ public interface MDCConstants { /** The technical ID of the concerned process instance (a.k.a. case ID). */ String PROCESS_INSTANCE_ID = "processInstanceId"; /** The technical ID of the concerned root process instance (a.k.a. root case ID). */ String ROOT_PROCESS_INSTANCE_ID = "rootProcessInstanceId"; /** The technical ID of the process definition. */ String PROCESS_DEFINITION_ID = "processDefinitionId"; /** The technical ID of the executing flow node instance in the process. */ String FLOW_NODE_INSTANCE_ID = "flowNodeInstanceId"; /** The technical ID of the user who performed the current operation. */ String USER_ID = "userId"; /** The technical ID of the admin user who triggered an operation for another user. */ String SUBSTITUTE_USER_ID = "substituteUserId"; /** The ID of the JPA transaction. */ String TRANSACTION_ID = "txUid"; /** The ID of the HTTP request. */ String REQUEST_ID = "req.requestId"; /** The correlation ID identifying a larger operation than the HTTP request. */ String CORRELATION_REQUEST_ID = "req.correlationId"; public static final String REQUEST_REMOTE_HOST_MDC_KEY = "req.remoteHost"; public static final String REQUEST_USER_AGENT_MDC_KEY = "req.userAgent"; public static final String REQUEST_REQUEST_URI = "req.requestURI"; public static final String REQUEST_QUERY_STRING = "req.queryString"; public static final String REQUEST_REQUEST_URL = "req.requestURL"; public static final String REQUEST_METHOD = "req.method"; public static final String REQUEST_X_FORWARDED_FOR = "req.xForwardedFor"; } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/mdc/MDCHelper.java ================================================ /** * 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.mdc; import java.util.Map; import java.util.Optional; import java.util.WeakHashMap; import java.util.concurrent.Callable; import java.util.function.Supplier; import org.slf4j.MDC; /** *

      Provides usefull methods for working with MDC.

      *

      Use the various {@link #tryWithMDC} methods to execute a runnable or a callable with MDC information in the * context.
      * You may provide a context {@link Supplier} directly, or a key object for which a {@link Supplier} was previously * registered with {@link #supplyMDC} * methods.

      *

      Use the appropriate Runnable or Callable implementation dependending on the number of Exception you want to * throw.
      * When used with lambdas, lambda will take only the common exception superclass. So instead of writing * *

       * Supplier<AbstractMDC> mdc = () -> new AbstractMDC(Map.of(key, value)) {};
       * MDCHelper.tryWithMDC(mdc, ()->{
       *     // throws Exception1 or Exception2
       *     ...
       * });
       * 
      * * You should declare the type and write * *
       * Supplier<AbstractMDC> mdc = () -> new AbstractMDC(Map.of(key, value)) {};
       * CheckedRunnable2<Exception1, Exception2> run = ()->{
       *     // throws Exception1 or Exception2
       *     ...
       * };
       * MDCHelper.tryWithMDC(mdc, run);
       * 
      * *

      When an exception is thrown within a {@link #tryWithMDC} method and not caught by the Runnable or Callable, the * current context is registered with * {@link #supplyMDC} for the exception.
      * So calling {@link #tryWithMDC(Object, CheckedRunnable4)} with the exception as usingObject key will * allow you to log the exception with the * original context.
      * You can even call this method when no context was supplied, as it would have no noticeable effect.

      * * @see AbstractMDC the base implementation for all MDC classes */ public class MDCHelper { /** * Like {@link Runnable} but throwing 4 checked exceptions */ @FunctionalInterface public interface CheckedRunnable4 { void run() throws E1, E2, E3, E4; } /** * Like {@link Runnable} but throwing 3 checked exceptions */ @FunctionalInterface public interface CheckedRunnable3 extends CheckedRunnable4 { }; /** * Like {@link Runnable} but throwing 2 checked exceptions */ @FunctionalInterface public interface CheckedRunnable2 extends CheckedRunnable3 { }; /** * Like {@link Runnable} but throwing a checked exception */ @FunctionalInterface public interface CheckedRunnable extends CheckedRunnable2 { }; /** * Like {@link Callable} but throwing 4 checked exceptions */ @FunctionalInterface public interface CheckedCallable4 { V call() throws E1, E2, E3, E4; } /** * Like {@link Callable} but throwing 3 checked exceptions */ @FunctionalInterface public interface CheckedCallable3 extends CheckedCallable4 { } /** * Like {@link Callable} but throwing 2 checked exceptions */ @FunctionalInterface public interface CheckedCallable2 extends CheckedCallable3 { } /** * Like {@link Callable} but throwing a checked exception */ @FunctionalInterface public interface CheckedCallable extends CheckedCallable2 { } private static Map instances = new WeakHashMap<>(); /** Builds the MDC */ private Supplier supplier; private MDCHelper(Supplier mdcSupplier) { supplier = mdcSupplier; } /** * Make a supplier that will get the current context, to create a similar MDC in its time (usually in another * thread) * * @return MDC supplier holding information from current context in this thread */ public static Supplier makeCurrentContextSupplier() { var contextMap = MDC.getCopyOfContextMap(); return () -> new AbstractMDC(contextMap) { }; } /** * Supply a MDC to an object that may use it later. * * @param mdcSupplier supplies a MDC * @param usingObject the object that will need MDC */ public static void supplyMDC(Supplier mdcSupplier, Object usingObject) { supplyMDC(mdcSupplier, usingObject, true); } /** * Supply a MDC to an object that may use it later. * * @param mdcSupplier supplies a MDC * @param usingObject the object that will need MDC * @param overwrite whether to overwrite an existing supplier */ public static void supplyMDC(Supplier mdcSupplier, Object usingObject, boolean overwrite) { if (overwrite) { instances.put(usingObject, new MDCHelper(mdcSupplier)); } else { // do not build a new Helper when one is already present instances.computeIfAbsent(usingObject, k -> new MDCHelper(mdcSupplier)); } } /** * Get an MDC, to encapsulate in a try with. * * @param usingObject the object using the MDC, for which a supplier may have been provided * @return the supplied MDC or null */ public static AbstractMDC getMDC(Object usingObject) { var helper = getHelper(usingObject); return helper.map(h -> h.supplier).map(Supplier::get).orElse(null); } /** * Get helper associated to using object. * * @param usingObject the object using the MDC, for which a supplier may have been provided * @return the associated helper */ private static Optional getHelper(Object usingObject) { var helper = Optional.ofNullable(instances.remove(usingObject)); // when not present, try and take the context associated to the cause exception if available if (!helper.isPresent() && usingObject instanceof Throwable) { var cause = ((Throwable) usingObject).getCause(); return getHelper(cause); } return helper; } /** * Try and invoke a callable with MDC * * @param the result type * @param mdcSupplier supplies a MDC * @param callable the callable with result * @return the callable result * @throws E exception in callable */ public static V tryWithMDC( Supplier mdcSupplier, CheckedCallable4 callable) throws E1, E2, E3, E4 { try (var mdc = mdcSupplier.get()) { try { return callable.call(); } catch (Throwable exception) { // attach context to exception before rethrowing it supplyMDC(makeCurrentContextSupplier(), exception, false); throw exception; } } } /** * Try and invoke a callable with MDC * * @param the result type * @param usingObject the object using the MDC, for which a supplier may have been provided * @param callable the callable with result * @return the callable result * @throws E exception in callable */ public static V tryWithMDC( Object usingObject, CheckedCallable4 callable) throws E1, E2, E3, E4 { try (var mdc = getMDC(usingObject)) { try { return callable.call(); } catch (Throwable exception) { // attach context to exception before rethrowing it supplyMDC(makeCurrentContextSupplier(), exception, false); throw exception; } } } /** * Try and invoke a runnable with MDC * * @param mdcSupplier supplies a MDC * @param runnable the runnable (without result) * @throws E exception in runnable */ public static void tryWithMDC( Supplier mdcSupplier, CheckedRunnable4 runnable) throws E1, E2, E3, E4 { CheckedCallable4 callable = () -> { runnable.run(); return null; }; tryWithMDC(mdcSupplier, callable); } /** * Try and invoke a runnable with MDC * * @param usingObject the object using the MDC, for which a supplier may have been provided * @param runnable the runnable (without result) * @throws E exception in runnable */ public static void tryWithMDC( Object usingObject, CheckedRunnable4 runnable) throws E1, E2, E3, E4 { CheckedCallable4 callable = () -> { runnable.run(); return null; }; tryWithMDC(usingObject, callable); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/mdc/MDCTransmitingThreadPoolExecutor.java ================================================ /** * 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.mdc; import java.util.concurrent.BlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * A {@link ThreadPoolExecutor} that transmits the MDC to the executing thread * * @author Vincent Hemery */ public class MDCTransmitingThreadPoolExecutor extends ThreadPoolExecutor { public MDCTransmitingThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); } @Override public void execute(Runnable command) { var mdcSupplier = MDCHelper.makeCurrentContextSupplier(); super.execute(() -> MDCHelper.tryWithMDC(mdcSupplier, command::run)); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/mdc/ProcessInstanceMDC.java ================================================ /** * 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.mdc; import java.util.Map; import java.util.Objects; import java.util.Optional; public class ProcessInstanceMDC extends AbstractMDC implements AutoCloseable, MDCConstants { /** * Build a process instance context. * * @param processInstanceId the id of the process instance / case * @param executerId the id of process starter execution * @param substituteUserId the id of substitute process starter execution * @param processDefinitionId the id of the process definition * @param rootProcessInstanceId the id of the root process instance / case */ public ProcessInstanceMDC(long processInstanceId, Optional executerId, Optional substituteUserId, long processDefinitionId, long rootProcessInstanceId) { super(buildContextMap(executerId, substituteUserId, processDefinitionId, processInstanceId, rootProcessInstanceId)); } private static Map buildContextMap(Optional executerId, Optional substituteUserId, long processDefinitionId, long processInstanceId, long rootProcessInstanceId) { if (!substituteUserId.equals(executerId)) { // executed by substituteUserId for executerId return Map.of( USER_ID, Objects.requireNonNull(executerId.map(String::valueOf).orElse(null)), SUBSTITUTE_USER_ID, Objects.requireNonNull(substituteUserId.map(String::valueOf).orElse(null)), PROCESS_DEFINITION_ID, String.valueOf(processDefinitionId), PROCESS_INSTANCE_ID, String.valueOf(processInstanceId), ROOT_PROCESS_INSTANCE_ID, String.valueOf(rootProcessInstanceId)); } else if (executerId.isPresent()) { // no need for SUBSTITUTE_USER_ID return Map.of( USER_ID, executerId.map(String::valueOf).orElse(null), PROCESS_DEFINITION_ID, String.valueOf(processDefinitionId), PROCESS_INSTANCE_ID, String.valueOf(processInstanceId), ROOT_PROCESS_INSTANCE_ID, String.valueOf(rootProcessInstanceId)); } else { // no authenticated user return Map.of( PROCESS_DEFINITION_ID, String.valueOf(processDefinitionId), PROCESS_INSTANCE_ID, String.valueOf(processInstanceId), ROOT_PROCESS_INSTANCE_ID, String.valueOf(rootProcessInstanceId)); } } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/mdc/UserIdMDC.java ================================================ /** * 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.mdc; import java.util.Map; import java.util.Optional; import java.util.function.Supplier; public class UserIdMDC extends AbstractMDC { /** * Build MDC with user id. * * @param userId authenticated user id */ public UserIdMDC(Long userId) { super(Map.of(MDCConstants.USER_ID, Long.toString(userId))); } /** * Get a supplier from an optional user id * * @param userId authenticated user id or empty * @return supplier supplies a valid MDC or null */ public static Supplier supplierFromOptionalId(Optional userId) { return () -> userId.map(UserIdMDC::new).orElse(null); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/monitoring/DefaultExecutorServiceMetricsProvider.java ================================================ /** * 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.monitoring; import java.util.List; import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics; /** * @author Emmanuel Duchastenier */ public class DefaultExecutorServiceMetricsProvider implements ExecutorServiceMetricsProvider { @Override public ExecutorService bind(MeterRegistry meterRegistry, ThreadPoolExecutor executorService, String executorServiceName) { return ExecutorServiceMetrics.monitor(meterRegistry, executorService, executorServiceName, List.of()); } @Override public void bindMetricsOnly(MeterRegistry meterRegistry, ThreadPoolExecutor executorService, String executorServiceName) { new ExecutorServiceMetrics(executorService, executorServiceName, List.of()).bindTo(meterRegistry); } @Override public void unbind(MeterRegistry meterRegistry, String executorServiceName) { //right now, there is no unbind method on the MeterBinder, manually unbind them Optional.ofNullable(meterRegistry.find("executor").timer()).ifPresent(meterRegistry::remove); Optional.ofNullable(meterRegistry.find("executor.active").gauge()).ifPresent(meterRegistry::remove); Optional.ofNullable(meterRegistry.find("executor.pool.size").gauge()) .ifPresent(meterRegistry::remove); Optional.ofNullable(meterRegistry.find("executor.pool.max").gauge()) .ifPresent(meterRegistry::remove); Optional.ofNullable(meterRegistry.find("executor.pool.core").gauge()) .ifPresent(meterRegistry::remove); Optional.ofNullable(meterRegistry.find("executor.queue.remaining").gauge()) .ifPresent(meterRegistry::remove); Optional.ofNullable(meterRegistry.find("executor.queued").gauge()).ifPresent(meterRegistry::remove); Optional.ofNullable(meterRegistry.find("executor.completed").functionCounter()) .ifPresent(meterRegistry::remove); Optional.ofNullable(meterRegistry.find("executor.idle").timer()).ifPresent(meterRegistry::remove); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/monitoring/ExecutorServiceMetricsProvider.java ================================================ /** * 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.monitoring; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import io.micrometer.core.instrument.MeterRegistry; /** * @author Emmanuel Duchastenier */ public interface ExecutorServiceMetricsProvider { /** * bind the executor service to the registry, only support ThreadPool right now, see * {@link ExecutorServiceMetricsProvider#unbind(MeterRegistry, String)} * * @return the monitored executor service with monitoring on execution time */ ExecutorService bind(MeterRegistry meterRegistry, ThreadPoolExecutor executorService, String executorServiceName); /** * bind the executor service to the registry, only support ThreadPool right now, see * {@link ExecutorServiceMetricsProvider#unbind(MeterRegistry, String)} * This will only bind statisctics of the Threadpool, and not time taks. */ void bindMetricsOnly(MeterRegistry meterRegistry, ThreadPoolExecutor executorService, String executorServiceName); /** * unbind all metrics of the named executor service from the meter registry */ void unbind(MeterRegistry meterRegistry, String executorServiceName); } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/monitoring/NoOpExecutorServiceMetricsProvider.java ================================================ /** * 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.monitoring; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import io.micrometer.core.instrument.MeterRegistry; /** * @author Emmanuel Duchastenier */ public class NoOpExecutorServiceMetricsProvider implements ExecutorServiceMetricsProvider { @Override public ExecutorService bind(MeterRegistry meterRegistry, ThreadPoolExecutor executorService, String executorServiceName) { return executorService; } @Override public void bindMetricsOnly(MeterRegistry meterRegistry, ThreadPoolExecutor executorService, String executorServiceName) { } @Override public void unbind(MeterRegistry meterRegistry, String executorServiceName) { } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/properties/BonitaConfigProperty.java ================================================ /** * Copyright (C) 2025 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.properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import lombok.extern.slf4j.Slf4j; /** * Represents a Bonita configuration property. It supports properties passed as Java System property (like * -Dmy.custom-property.subproperty=MY_VALUE) or as environment variables (like MY_CUSTOMPROPERTY_SUBPROPERTY=MY_VALUE). * If both are defined, System Property has precedence. * * @author Emmanuel Duchastenier */ @Slf4j public abstract class BonitaConfigProperty { /* * System property version of the property (lowercase, with dots): */ protected final String propertyKey; /* * Display name for logs */ protected final String displayName; // A simple "cache" to avoid logging the same property multiple times private static final Set alreadyLoggedProperties = ConcurrentHashMap.newKeySet(); /** * @param displayName the display name of the property, used in logs * @param propertyKey the "system property" version of the property, typically in lowercase and with dots. */ public BonitaConfigProperty(String displayName, String propertyKey) { this.displayName = displayName; this.propertyKey = propertyKey; } protected void logInitializationMessagesIfFirstTime() { if (alreadyLoggedProperties.add(this.propertyKey)) { log.info(getInitializationMessage()); } } abstract String getInitializationMessage(); protected String envPropertyKey() { return propertyKey.toUpperCase().replace(".", "_").replaceAll("-", ""); } protected String getProperty(String defaultValue) { return System.getProperty(propertyKey, System.getenv().getOrDefault(envPropertyKey(), defaultValue)); } // for test reset static void clearLoggedProperties() { alreadyLoggedProperties.clear(); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/properties/BonitaProperty.java ================================================ /** * Copyright (C) 2025 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.properties; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; /** * Annotation to be used on fields to inject a property value from the environment. * It works very much like the @Value annotation from Spring. * Can be provided with a list of deprecated property names. If so, when used, the annotation processor will first look * for the * deprecated properties and if found, will log a warning. * Eg: * *
       *
       * @BonitaProperty(name = "my.property", deprecated = { "old.my.property", "older.my.property" })
       * private String myProperty;
       * 
      * * In simple cases where no deprecated properties are defined, the name attribute can be omitted (and passed a default * unnamed 'value' attribute): * *
       *
       * @BonitaProperty("my.property")
       * private String myProperty;
       * 
      * * @see BonitaPropertyAnnotationProcessor how this Annotation is processed */ @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) public @interface BonitaProperty { @AliasFor("name") String value() default ""; @AliasFor("value") String name() default ""; String[] deprecated() default {}; } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/properties/BonitaPropertyAnnotationProcessor.java ================================================ /** * Copyright (C) 2025 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.properties; import java.lang.reflect.Field; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeansException; import org.springframework.beans.InvalidPropertyException; import org.springframework.beans.factory.BeanInitializationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; /** * Spring {@link BeanPostProcessor} that parses {@link BonitaProperty} and sets the value of the annotated field. * If a deprecated property name is detected, then a warning message is issued. * * @see BonitaProperty */ @Component @Slf4j public class BonitaPropertyAnnotationProcessor implements BeanPostProcessor { @Autowired private Environment environment; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { for (Field field : bean.getClass().getDeclaredFields()) { if (field.isAnnotationPresent(BonitaProperty.class)) { BonitaProperty bonitaProperty = field.getAnnotation(BonitaProperty.class); String propertyName = bonitaProperty.value(); if (propertyName.isEmpty()) { propertyName = bonitaProperty.name(); } if (propertyName.isEmpty()) { throw new InvalidPropertyException(bean.getClass(), "unset property name", "@BonitaProperty 'name' or 'value' attribute is mandatory"); } String[] deprecated = bonitaProperty.deprecated(); String resolvedValue = null; for (String deprecatedPropertyName : deprecated) { resolvedValue = environment.getProperty(deprecatedPropertyName); if (resolvedValue != null) { log.warn("Warning: property '{}' is deprecated. Please use '{}' instead", deprecatedPropertyName, propertyName); break; } } if (resolvedValue == null) { resolvedValue = environment.getProperty(propertyName); } // Set the field value if necessary field.setAccessible(true); try { field.set(bean, resolvedValue); } catch (IllegalAccessException e) { throw new BeanInitializationException( "Failed to set BonitaProperty-annotated field value for bean " + beanName, e); } } } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/properties/BooleanProperty.java ================================================ /** * Copyright (C) 2021 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.properties; import static java.lang.String.format; import static java.lang.String.valueOf; /** * @author Emmanuel Duchastenier */ public class BooleanProperty extends BonitaConfigProperty { private final boolean propertyValue; public BooleanProperty(String displayName, String propertyKey, boolean defaultValue) { super(displayName, propertyKey); propertyValue = Boolean.parseBoolean(getProperty(valueOf(defaultValue))); logInitializationMessagesIfFirstTime(); } public boolean isEnabled() { return propertyValue; } @Override String getInitializationMessage() { return format("%s %s, you may %s it using env property %s or System property -D%s [=true/false]", displayName, isEnabled() ? "enabled" : "disabled", isEnabled() ? "disable" : "enable", envPropertyKey(), propertyKey); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/properties/CustomValueConfig.java ================================================ /** * Copyright (C) 2025 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.properties; import java.util.Set; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CustomValueConfig { @Bean public AutowiredAnnotationBeanPostProcessor customAutowiredAnnotationBeanPostProcessor() { AutowiredAnnotationBeanPostProcessor processor = new AutowiredAnnotationBeanPostProcessor(); processor.setAutowiredAnnotationTypes(Set.of(BonitaProperty.class, Value.class, Autowired.class)); return processor; } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/properties/StringProperty.java ================================================ /** * Copyright (C) 2025 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.properties; import static java.lang.String.format; public class StringProperty extends BonitaConfigProperty { private final String propertyValue; public StringProperty(String displayName, String propertyKey, String defaultValue) { super(displayName, propertyKey); propertyValue = getProperty(defaultValue); logInitializationMessagesIfFirstTime(); } public String getValue() { return propertyValue; } @Override String getInitializationMessage() { return format("%s %s, you may set it using env property %s or System property -D%s", displayName, propertyValue, envPropertyKey(), propertyKey); } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/service/BonitaTaskExecutor.java ================================================ /** * 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; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.bonitasoft.engine.commons.PlatformLifecycleService; /** * simply hold a cached thread pool executor to handle common asynchronous tasks */ public class BonitaTaskExecutor implements PlatformLifecycleService { private ExecutorService bonitaTaskExecutor; private void checkStarted() { if (bonitaTaskExecutor == null) { throw new IllegalStateException(this.getClass().getName() + " is not running"); } } public Future execute(RunnableWithException runnable) { return execute(() -> { runnable.run(); return null; }); } public Future execute(Callable callable) { checkStarted(); return bonitaTaskExecutor.submit(callable); } @Override public void start() { if (bonitaTaskExecutor == null) { bonitaTaskExecutor = Executors.newCachedThreadPool(r -> new Thread(r, "BonitaTaskExecutor")); } } @Override public void stop() { if (bonitaTaskExecutor != null) { bonitaTaskExecutor.shutdown(); bonitaTaskExecutor = null; } } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/service/BroadcastService.java ================================================ /** * 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; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; /** * Service allowing to broadcast a call made on services to the other nodes of a Cluster * * @author Baptiste Mesta */ public interface BroadcastService { /** * Broadcast the execution of a callable on other nodes and returns immediately * a future holding the execution result on each node (once available). *
      * The callable will be executed using a platform level session on other nodes. * * @param callable * callable that will be executed on all nodes except the current one * @param * type of the returned value * @return * a future of a map containing the name of the node and the result of the callable */ Future>> executeOnOthers(Callable callable); Map> executeOnOthersAndWait(Callable callable) throws TimeoutException, InterruptedException, ExecutionException; } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/service/InjectedService.java ================================================ /** * Copyright (C) 2016 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; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * to tell the engine that a setter is used to inject services * * @author Baptiste Mesta */ @Retention(RetentionPolicy.RUNTIME) public @interface InjectedService { } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/service/RunnableWithException.java ================================================ /** * 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; public interface RunnableWithException { void run() throws Exception; } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/service/ServicesLookup.java ================================================ /** * 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; /** * @author Baptiste Mesta */ public interface ServicesLookup { /** * lookup for a service on the platform * * @param serviceName * name of the service, it is the id of the bean in the spring context * @param * type of the service to return * @return * the service */ T lookupService(String serviceName); } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/service/ServicesResolver.java ================================================ /** * Copyright (C) 2016 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; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.apache.commons.text.WordUtils; /** * @author Baptiste Mesta */ public class ServicesResolver { private final ServicesLookup servicesLookup; public ServicesResolver(ServicesLookup servicesLookup) { this.servicesLookup = servicesLookup; } public void injectServices(Object target) throws InvocationTargetException, IllegalAccessException { final Method[] methods = target.getClass().getMethods(); for (final Method method : methods) { if (method.getAnnotation(InjectedService.class) != null) { String serviceName = WordUtils.uncapitalize(method.getName().substring(3)); final Object lookup = servicesLookup.lookupService(serviceName); method.invoke(target, lookup); } } } } ================================================ FILE: services/bonita-commons/src/main/java/org/bonitasoft/engine/service/TaskResult.java ================================================ /** * 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; import java.util.Objects; import java.util.concurrent.TimeUnit; /** * result of the execution of a task using the broadcast service * * @author Baptiste Mesta */ public class TaskResult { private Throwable throwable; private T result; private Long timeout; private TimeUnit timeunit; public TaskResult(final Throwable e) { this.throwable = e; } public TaskResult(final T result) { this.result = result; } public TaskResult(final Long timeout, final TimeUnit timeunit) { this.timeout = timeout; this.timeunit = timeunit; } public static TaskResult error(final Throwable e) { return new TaskResult(e); } public static TaskResult ok(final T result) { return new TaskResult(result); } public static TaskResult timeout(final long timeout, final TimeUnit timeunit) { return new TaskResult(timeout, timeunit); } public boolean isError() { return throwable != null; } public boolean isOk() { return !isError() && !isTimeout(); } public boolean isTimeout() { return timeout != null; } /** * @return the result */ public T getResult() { return result; } /** * @return the throwable */ public Throwable getThrowable() { return throwable; } /** * @return the timeout */ public Long getTimeout() { return timeout; } /** * @return the time unit */ public TimeUnit getTimeunit() { return timeunit; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TaskResult that = (TaskResult) o; return Objects.equals(throwable, that.throwable) && Objects.equals(result, that.result) && Objects.equals(timeout, that.timeout) && timeunit == that.timeunit; } @Override public int hashCode() { return Objects.hash(throwable, result, timeout, timeunit); } @Override public String toString() { return "TaskResult{" + "throwable=" + throwable + ", result=" + result + ", timeout=" + timeout + ", timeunit=" + timeunit + '}'; } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/DeepRegexFileFilterTest.java ================================================ /** * 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; import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import org.junit.Test; public class DeepRegexFileFilterTest { @Test public void acceptShouldWorkForMatchingPattern() { final String pattern = "folder/sub/.*\\.txt"; final String parentPatternPathname = "/media/drive/some_folder"; assertThat(new DeepRegexFileFilter(new File(parentPatternPathname), pattern) .accept(new File("/media/drive/some_folder/folder/sub/matchingFile.txt"))) .isTrue(); } @Test public void acceptShouldWorkForMatchingPatternOnFolderWithTrailingSlash() { final String pattern = "folder/sub/.*\\.txt"; final String parentPatternPathname = "/home/some_folder/"; assertThat(new DeepRegexFileFilter(new File(parentPatternPathname), pattern) .accept(new File("/home/some_folder/folder/sub/matchingFile.txt"))) .isTrue(); } @Test public void acceptShouldRejectNonMatchingFile() { final String pattern = "folder/sub/.*\\.txt"; final String parentPatternPathname = "/home/some_folder/"; assertThat(new DeepRegexFileFilter(new File(parentPatternPathname), pattern) .accept(new File("/home/some_folder/folder/sub/someReport.pdf"))) .isFalse(); } @Test public void acceptShouldMatchDeepSubFolders() { final String pattern = "folder/.*\\.txt"; final String parentPatternPathname = "/home"; assertThat(new DeepRegexFileFilter(new File(parentPatternPathname), pattern) .accept(new File("/home/folder/sub/sub2/fileHiddenInDeepFolder.txt"))) .isTrue(); } @Test public void acceptShouldNotMatchSlashBeginingPatterns() { final String pattern = "/folder/.*\\.txt"; final String parentPatternPathname = "/home"; assertThat(new DeepRegexFileFilter(new File(parentPatternPathname), pattern) .accept(new File("/home/folder/sub/sub2/fileHiddenInDeepFolder.txt"))) .isFalse(); } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/ClassReflectorTest.java ================================================ /** * 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.commons; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import org.bonitasoft.engine.commons.exceptions.SReflectException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class ClassReflectorTest { private static final String NOT_A_METHOD = "not a method"; private static final String NOT_A_CLASS = "not a class"; private Pojo pojo; @BeforeEach void before() { pojo = new Pojo(); } @Test void testGetGetterName() { // can't work with boolean since field name type is unknown assertThat(ClassReflector.getGetterName("longs")).isEqualTo("getLongs"); assertThat(ClassReflector.getGetterName("bigChoices")).isEqualTo("getBigChoices"); } @Test void testGetGetterMethod() { assertThat(ClassReflector.getGetterName("bigChoice", Boolean.class)).isEqualTo("isBigChoice"); assertThat(ClassReflector.getGetterName("longs", Long.class)).isEqualTo("getLongs"); assertThat(ClassReflector.getGetterName("bigChoices", ArrayList.class)).isEqualTo("getBigChoices"); } @Test void testGetterReturnType() throws Exception { assertThat(ClassReflector.getGetterReturnType(pojo.getClass(), "getDate")).isEqualTo(Date.class); } @Test void testGetAccessibleGetters() { final Collection accessibleGetters = ClassReflector.getAccessibleGetters(pojo.getClass()); // isChoice, getDate, getClass, getLongs, getBigChoice, getBigChoices assertThat(accessibleGetters).hasSize(7); } @Test void testGetClass() throws Exception { final Class class1 = ClassReflector.getClass(pojo.getClass(), pojo.getClass().getName()); assertThat(class1).isEqualTo(pojo.getClass()); } @Test void testGetClassException() { assertThrows(SReflectException.class, () -> ClassReflector.getClass(pojo.getClass(), NOT_A_CLASS)); } @Test void testGetObject() throws Exception { assertThat(ClassReflector.getObject(pojo.getClass(), pojo.getClass().getName())).isNotNull(); } @Test void testGetObjectException() { assertThrows(SReflectException.class, () -> ClassReflector.getObject(pojo.getClass(), NOT_A_CLASS)); } @Test void testGetConstructor() throws Exception { ClassReflector.getConstructor(pojo.getClass(), pojo.getClass().getName()); } @Test void testGetConstructorException() { assertThrows(SReflectException.class, () -> assertThat(ClassReflector.getConstructor(pojo.getClass(), NOT_A_CLASS)).isNotNull()); } @Test void testGetConstructorNoClassName() throws Exception { final Constructor constructor = ClassReflector.getConstructor(pojo.getClass()); assertThat(constructor).isNotNull(); } @Test void testGetConstructorNoClassNameException() { assertThrows(SReflectException.class, () -> ClassReflector.getConstructor(pojo.getClass(), String.class)); } @Test void testGetInstance() throws Exception { ClassReflector.getInstance(ClassReflector.getConstructor(pojo.getClass())); } @Test void testGetInstanceException() { assertThrows(SReflectException.class, () -> ClassReflector.getInstance(ClassReflector.getConstructor(pojo.getClass()), String.class)); } @Test void testInvokeGetter() throws Exception { final Date date = new Date(); pojo.setDate(date); final Object invokeGetter = ClassReflector.invokeGetter(pojo, "getDate"); assertThat(invokeGetter).isEqualTo(date); } @Test void testInvokeGetterException() { assertThrows(SReflectException.class, () -> ClassReflector.invokeGetter(pojo, NOT_A_METHOD)); } @Test void testInvokeSetter() throws Exception { final Date date = new Date(); ClassReflector.invokeSetter(pojo, "setDate", date.getClass(), date); assertThat(pojo.getDate()).isEqualTo(date); } @Test void testInvokeSetterException() { assertThrows(SReflectException.class, () -> ClassReflector.invokeSetter(pojo, NOT_A_METHOD, Date.class, new Date())); } @Test void testGetMethod() throws Exception { final Method method = ClassReflector.getMethod(pojo.getClass(), "getDate"); assertThat(method).isEqualTo(ClassReflector.getMethodByName(pojo.getClass(), "getDate")); } @Test void testInvokeMethodByName() throws Exception { final Date date = new Date(); ClassReflector.invokeMethodByName(pojo, "setDate", date); assertThat(pojo.getDate()).isEqualTo(date); } @Test void testInvokeMethodByName_should_throw_exception_on_method_name() { assertThrows(SReflectException.class, () -> ClassReflector.invokeMethodByName(pojo, NOT_A_METHOD, new Date())); } @Test void testInvokeMethodByName_should_throw_exception_on_bad_parameter() { assertThrows(SReflectException.class, () -> ClassReflector.invokeMethodByName(pojo, "setDate", "not a date")); } @Test void testInvokeMethod() throws Exception { final Date date = new Date(); ClassReflector.invokeMethod(pojo, "setDate", Date.class, date); assertThat(pojo.getDate()).isEqualTo(date); } @Test void testInvokeMethodWithParams() throws Exception { final Class[] parameterType = new Class[] { String.class, Integer.class }; final Object[] parameterValues = new Object[] { "string", 1 }; final Object result = ClassReflector.invokeMethod(pojo, "twoParamMethod", parameterType, parameterValues); assertThat(result.toString()).isEqualTo("string*1"); } @Test void testGetCompatibleMethod() throws Exception { final Method compatibleMethod = ClassReflector.getCompatibleMethod(pojo.getClass(), "setChoice", Boolean.class); assertThat(compatibleMethod).isNotNull(); } @Test void testGetCompatibleMethod_with_existing_method() throws Exception { final Method compatibleMethod = ClassReflector.getCompatibleMethod(pojo.getClass(), "isChoice"); assertThat(compatibleMethod).isNotNull(); } @Test void testGetCompatibleMethod_with_wrong_parameters_type() { assertThrows(SReflectException.class, () -> ClassReflector.getCompatibleMethod(pojo.getClass(), "setChoice", String.class)); } @Test void testGetCompatibleMethod_with_wrong_parameters_count() { assertThrows(SReflectException.class, () -> ClassReflector.getCompatibleMethod(pojo.getClass(), "setChoice", String.class, Date.class)); } @Test void testGetCompatibleMethod_with_wrong_null_parameters() { assertThrows(SReflectException.class, () -> ClassReflector.getCompatibleMethod(pojo.getClass(), "setChoice", (Class[]) null)); } @Test void testGetCompatibleMethod_with_parameters() throws Exception { final Method compatibleMethod = ClassReflector.getCompatibleMethod(pojo.getClass(), "twoParamMethod", String.class, Integer.class); assertThat(compatibleMethod).isNotNull(); } @Test void testGetGetterReturnType() throws Exception { final Type result = ClassReflector.getGetterReturnType(pojo.getClass(), "getDate"); assertThat(result).isEqualTo(Date.class); } @Test void testGetGetterReturnType_should_throw_exception() { assertThrows(SReflectException.class, () -> ClassReflector.getGetterReturnType(pojo.getClass(), NOT_A_METHOD)); } @Test void testGetDeclaredSetters() { final Method[] declaredSetters = ClassReflector.getDeclaredSetters(pojo.getClass()); // setDate,setChoice,setLongs,setBigChoice,setBigChoices assertThat(declaredSetters).hasSize(6); for (final Method method : declaredSetters) { assertThat(ClassReflector.isAGetterMethod(method)).isFalse(); assertThat(ClassReflector.isASetterMethod(method)).isTrue(); } } @Test void testGetDeclaredGetters() { final Method[] declaredSetters = ClassReflector.getDeclaredGetters(pojo.getClass()); // isChoice, getDate, getLongs, getBigChoice, getBigChoices assertThat(declaredSetters).hasSize(6); for (final Method method : declaredSetters) { assertThat(ClassReflector.isAGetterMethod(method)).isTrue(); assertThat(ClassReflector.isASetterMethod(method)).isFalse(); } } @Test void testGetFieldName() { assertThat(ClassReflector.getFieldName("isChoice")).isEqualTo("choice"); assertThat(ClassReflector.getFieldName("getDate")).isEqualTo("date"); assertThat(ClassReflector.getFieldName("get")).isEqualTo(""); } @Test void testSetField_on_sub_object() throws Exception { Date date = new Date(); pojo.setChild(new Pojo()); ClassReflector.setField(pojo, "child.date", date); assertThat(pojo.getChild().getDate()).isEqualTo(date); } @Test void testSetField_on_object() throws Exception { Pojo parameterValue = new Pojo(); ClassReflector.setField(pojo, "child", parameterValue); assertThat(pojo.getChild()).isEqualTo(parameterValue); } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/CollectionUtilTest.java ================================================ /** * 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.commons; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.junit.jupiter.api.Test; class CollectionUtilTest { @Test void should_emptyOrUnmodifiable_return_unmodifiable_list() { ArrayList list = new ArrayList<>(); list.add("plop"); List result = CollectionUtil.emptyOrUnmodifiable(list); assertEquals("plop", result.get(0)); assertEquals(1, result.size()); assertThrows(UnsupportedOperationException.class, () -> result.add("plop2")); } @Test void should_emptyOrUnmodifiable_return_empty_list() { List result = CollectionUtil.emptyOrUnmodifiable(null); assertTrue(result.isEmpty()); } @Test void should_split_a_list_of_9_elements_in_lists_of_3() { List original = asList(1, 2, 3, 4, 5, 6, 7, 8, 9); List> split = CollectionUtil.split(original, 3); assertThat(split).containsExactly(asList(1, 2, 3), asList(4, 5, 6), asList(7, 8, 9)); } @Test void should_split_a_list() { List original = asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); List> split = CollectionUtil.split(original, 3); assertThat(split).containsExactly(asList(1, 2, 3), asList(4, 5, 6), asList(7, 8, 9), asList(10)); } @Test void should_split_a_list_in_lists_of_2() { List original = asList(1, 2, 3, 4, 5, 6, 7, 8, 9); List> split = CollectionUtil.split(original, 2); assertThat(split).containsExactly(asList(1, 2), asList(3, 4), asList(5, 6), asList(7, 8), asList(9)); } @Test void should_split_a_list_of_110_elements_in_lists_of_100() { List original = IntStream.range(0, 110).boxed().collect(Collectors.toList()); List> split = CollectionUtil.split(original, 100); assertThat(split.get(0)).isEqualTo(IntStream.range(0, 100).boxed().collect(Collectors.toList())); assertThat(split.get(1)).isEqualTo(IntStream.range(100, 110).boxed().collect(Collectors.toList())); } @Test void should_split_an_empty_list() { List original = Collections.emptyList(); List> split = CollectionUtil.split(original, 3); assertThat(split).isEmpty(); } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/ContainerTest.java ================================================ /** * 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.commons; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; class ContainerTest { @Test void constructor_should_create_container_based_on_given_information() { //when Container container = new Container(10, "cType"); //then assertThat(container.getId()).isEqualTo(10); assertThat(container.getType()).isEqualTo("cType"); } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/ExceptionUtilsTest.java ================================================ /** * Copyright (C) 2020 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.commons; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.junit.Test; public class ExceptionUtilsTest { @Test public void should_print_root_cause_of_an_exception() { Exception exception = new Exception("direct exception", new Exception("intermediate exception", new SBonitaRuntimeException("This is the root cause"))); String rootCause = ExceptionUtils.printRootCauseOnly(exception); assertThat(rootCause) .isEqualTo("org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException: This is the root cause"); } @Test public void should_print_root_cause_of_an_exception_when_there_is_no_root_cause() { Exception exception = new Exception("direct exception with no root cause"); String rootCause = ExceptionUtils.printRootCauseOnly(exception); assertThat(rootCause).isEqualTo("java.lang.Exception: direct exception with no root cause"); } @Test public void should_print_lightweight_stacktrace() { Exception exception = doSomeBusiness(); String lightWeightStacktrace = ExceptionUtils.printLightWeightStacktrace(exception, 3); assertThat(lightWeightStacktrace).startsWith( "org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException: This is the root cause\n" + "\twrapped by java.lang.Exception: intermediate exception\n" + "\twrapped by java.lang.Exception: Wrap all other exceptions\n" + " exception was generated here:\tat org.bonitasoft.engine.commons.ExceptionUtilsTest.methodThatCauseRootException(ExceptionUtilsTest.java:"); } protected Exception doSomeBusiness() { try { doSomeBusiness2(); } catch (Exception e) { return new Exception("Wrap all other exceptions", e); } return null; } protected void doSomeBusiness2() throws Exception { try { methodThatCauseRootException(); } catch (SBonitaRuntimeException e) { throw new Exception("intermediate exception", e); } } protected void methodThatCauseRootException() { throw new SBonitaRuntimeException("This is the root cause"); } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/JavaMethodInvokerTest.java ================================================ /** * 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.commons; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Test; class JavaMethodInvokerTest { @Test void invokeJavaMethodShouldNotModifyObjectReference() throws Exception { // given: final JavaMethodInvoker invoker = new JavaMethodInvoker(); final User user = new User("Jo la frite"); final String initialUserReference = user.toString(); // when: invoker.invokeJavaMethod(String.class.getName(), "Manu", user, "setName", String.class.getName()); // then: final String newUserReference = user.toString(); assertThat(newUserReference).isEqualTo(initialUserReference); } class User { private String name; public User(final String name) { this.name = name; } public void setName(final String name) { this.name = name; } } public class MyClass { private int thing = 0; public void setThing(final int thing) { this.thing = thing; } int getThing() { return thing; } } @Test void invokeJavaMethod_should_update_a_list() throws Exception { final JavaMethodInvoker invoker = new JavaMethodInvoker(); final List users = new ArrayList(); final List createdUsers = new ArrayList(); createdUsers.add(new User("Matti")); invoker.invokeJavaMethod(createdUsers.getClass().getName(), createdUsers, users, "addAll", List.class.getName()); assertThat(users).isEqualTo(createdUsers); } @Test void invokeJavaMethod_should_use_autoboxing() throws Exception { final MyClass myData = new MyClass(); final JavaMethodInvoker invoker = new JavaMethodInvoker(); final MyClass object = (MyClass) invoker.invokeJavaMethod(Integer.class.getName(), 83, myData, "setThing", "int"); assertThat(object.getThing()).isEqualTo(83); } @Test void invokeJavaMethod_should_throw_nosuchmethodexception() throws Exception { assertThatThrownBy( () -> { final MyClass myData = new MyClass(); final JavaMethodInvoker invoker = new JavaMethodInvoker(); final MyClass object = (MyClass) invoker.invokeJavaMethod(String.class.getName(), "A STRING", myData, "setDoesNotExistMethod", "java.lang.String"); }) .isInstanceOf(NoSuchMethodException.class); } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/Pojo.java ================================================ /** * 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.commons; import java.util.Date; import java.util.List; public class Pojo { private boolean choice; private Boolean bigChoice; private List bigChoices; private Date date; private List longs; private Pojo child; public Pojo() { // } public boolean isChoice() { return choice; } public void setChoice(final boolean choice) { this.choice = choice; } public Date getDate() { return date; } public void setDate(final Date date) { this.date = date; } public List getLongs() { return longs; } public void setLongs(final List longs) { this.longs = longs; } public Boolean getBigChoice() { return bigChoice; } public void setBigChoice(final Boolean bigChoice) { this.bigChoice = bigChoice; } public List getBigChoices() { return bigChoices; } public void setBigChoices(final List bigChoices) { this.bigChoices = bigChoices; } public String notAGetter() { return null; } public String twoParamMethod(final String a, final Integer i) { return String.format("%s*%d", a, i); } public Pojo getChild() { return child; } public void setChild(Pojo child) { this.child = child; } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/StringUtilsTest.java ================================================ /** * 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.commons; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; class StringUtilsTest { @Test void uniformizePathPatternShouldChangeAllBackslashesToForwardSlashes() { final String uniformized = StringUtils.uniformizePathPattern("C:\\toto\\my path\\my\\/file.bak.txt"); assertThat(uniformized).isEqualTo("C:/toto/my path/my/file.bak.txt"); } @Test void uniformizePathPatternShouldLeaveNoDoubleSeparator() { final String uniformized = StringUtils .uniformizePathPattern("C:///toto//my path/////full_slashes/my file.bak.txt"); assertThat(uniformized).isEqualTo("C:/toto/my path/full_slashes/my file.bak.txt"); } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/TypeConvertUtilTest.java ================================================ /** * 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.commons; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import java.math.BigDecimal; import java.util.Date; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class TypeConvertUtilTest { private TypeConverterUtil typeConverterUtil; @BeforeEach void before() { String[] datePatterns = new String[] { "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd", "HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ss.SSS" }; typeConverterUtil = new TypeConverterUtil(datePatterns); } @Test void should_convert_primitives() { assertThat((Long) typeConverterUtil.convertToType(Long.class, "15")).isEqualTo(15L); assertThat((Integer) typeConverterUtil.convertToType(Integer.class, "16")).isEqualTo(16); assertThat((Float) typeConverterUtil.convertToType(Float.class, "-17.596")).isEqualTo(-17.596F); final BigDecimal bigDecimal = new BigDecimal(12.3650000000000002131628207280300557613372802734375); assertThat((BigDecimal) typeConverterUtil.convertToType(BigDecimal.class, "12.3650000000000002131628207280300557613372802734375")).isEqualTo(bigDecimal); assertThat((Boolean) typeConverterUtil.convertToType(Boolean.class, "true")).isTrue(); assertThat((Boolean) typeConverterUtil.convertToType(Boolean.class, "false")).isFalse(); checkDateConvert("2015-08-06T22:00:00.000", 1438898400000L); checkDateConvert("2015-01-31", 1422662400000L); checkDateConvert("2015-01-31 23:15:59", 1422746159000L); checkDateConvert("2015-01-31T23:15:59", 1422746159000L); checkDateConvert("2015-01-31T23:15:59.001", 1422746159001L); checkDateConvert("12:00:00", 43200000L); } @Test void dateConvert_should_fail() { assertThrows(IllegalArgumentException.class, () -> typeConverterUtil.convertToType(Date.class, "not a date"), "unable to parse 'not a date' to type java.util.Date"); } private void checkDateConvert(String dateToConvert, long expectedDateAsLong) { final Date expectedDate = new Date(expectedDateAsLong); final Date returnedDate = (Date) typeConverterUtil.convertToType(Date.class, dateToConvert); assertThat(returnedDate).as("error while converting date:" + dateToConvert + " (assuming provided date is GMT)") .hasTime(expectedDateAsLong).isEqualTo(expectedDate); } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/TypeConverterUtilTest.java ================================================ /** * 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.commons; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.Date; import org.junit.jupiter.api.Test; /** * @author Emmanuel Duchastenier */ class TypeConverterUtilTest { private final TypeConverterUtil typeConverterUtil = new TypeConverterUtil(null); @Test void convertToType_should_convert_to_LocalDate() { final Object date = typeConverterUtil.convertToType(LocalDate.class, "2014-07-14"); // then: assertThat(date).isEqualTo(LocalDate.of(2014, 7, 14)); } @Test void getDefaultType_should_truncate_extra_LocalDate_string_characters() { final Object date = typeConverterUtil.convertToType(LocalDate.class, "2015-07-14TOTO12:00"); // then: assertThat(date).isEqualTo(LocalDate.of(2015, 7, 14)); } @Test void getDefaultType_should_gracefully_handle_null_values() { assertThrows(IllegalArgumentException.class, () -> typeConverterUtil.convertToType(Date.class, null), "unable to parse"); } @Test void convertToType_should_convert_to_LocalDateTime() { final Object date = typeConverterUtil.convertToType(LocalDateTime.class, "2014-07-14T17:42:01"); // then: assertThat(date).isEqualTo(LocalDateTime.of(2014, 7, 14, 17, 42, 1)); } @Test void getDefaultType_should_truncate_extra_LocalDateTime_string_characters() { final Object date = typeConverterUtil.convertToType(LocalDateTime.class, "2015-07-14T12:00:07+03:00"); // then: assertThat(date).isEqualTo(LocalDateTime.of(2015, 7, 14, 12, 0, 7)); } @Test void convertToType_should_convert_to_OffsetDateTime() { final Object date = typeConverterUtil.convertToType(OffsetDateTime.class, "2014-07-14T17:42:01Z"); // then: assertThat(date).isEqualTo(OffsetDateTime.of(2014, 7, 14, 17, 42, 1, 0, ZoneOffset.UTC)); } @Test void getDefaultType_should_throw_exception_for_wrong_date_format() { assertThrows(IllegalArgumentException.class, () -> typeConverterUtil.convertToType(OffsetDateTime.class, "2015-07-14T12:00:07+03:00:17:15:123.000"), "unable to parse"); } @Test void getDefaultType_should_throw_exception_for_wrong_LocalDate_type() { assertThrows(IllegalArgumentException.class, () -> typeConverterUtil.convertToType(LocalDate.class, 225L), "unable to parse"); } @Test void getDefaultType_should_throw_exception_for_wrong_LocalDateTime_type() { assertThrows(IllegalArgumentException.class, () -> typeConverterUtil.convertToType(LocalDateTime.class, new Date()), "unable to parse"); } @Test void getDefaultType_should_throw_exception_for_wrong_OffsetDateTime_type() { assertThrows(IllegalArgumentException.class, () -> typeConverterUtil.convertToType(OffsetDateTime.class, (byte) 0), "unable to parse"); } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/commons/io/IOUtilTest.java ================================================ /** * 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.commons.io; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.w3c.dom.Document; public class IOUtilTest { @Rule public TemporaryFolder tempFolderRule = new TemporaryFolder(); @Test public void getClassNameList() throws Exception { // given: final byte[] jarContent = getFromResources("bdr-jar.bak"); // when: final List classNameList = IOUtil.getClassNameList(jarContent); // then: assertThat(classNameList).containsOnly("org.bonita.pojo.Employee"); } private byte[] getFromResources(String name) throws IOException { InputStream resourceAsStream = getClass().getResourceAsStream(name); if (resourceAsStream == null) { throw new IllegalStateException(String.format("no resource %s found", name)); } return IOUtil.getAllContentFrom(resourceAsStream); } @Test(expected = IllegalArgumentException.class) public void shouldToByteArray_ThrowIllegalArgumentException_ForNullDocument() throws Exception { final Document document = null; IOUtil.toByteArray(document); } @Test public void shouldToByteArray_ForDocumentReturnAByteArray() throws Exception { final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setValidating(false); final DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); Document document; try (InputStream is = IOUtilTest.class.getResourceAsStream("persistence.xml")) { document = documentBuilder.parse(is); } final byte[] byteArray = IOUtil.toByteArray(document); assertThat(byteArray).isNotNull(); } @Test public void shouldAddJarEntry_AddAnEntryInExistingJar() throws Exception { final byte[] jarContent = getFromResources("bdr-jar.bak"); final byte[] entryContent = getFromResources("persistence.xml"); final String entryName = "META-INF/myNewEntry.xml"; final byte[] updatedJar = IOUtil.addJarEntry(jarContent, entryName, entryContent); assertThat(updatedJar).isNotNull(); final ByteArrayInputStream bais = new ByteArrayInputStream(updatedJar); final JarInputStream jis = new JarInputStream(bais); JarEntry entry; final Map entryNames = new HashMap<>(); final byte[] buffer = new byte[4096]; while ((entry = jis.getNextJarEntry()) != null) { if (!entry.isDirectory()) { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); int len; while ((len = jis.read(buffer)) > 0) { baos.write(buffer, 0, len); } baos.close(); entryNames.put(entry.getName(), baos.toByteArray()); } } jis.close(); assertThat(entryNames.keySet()).contains(entryName); assertThat(entryNames.get(entryName)).isEqualTo(entryContent); } @Test(expected = IllegalArgumentException.class) public void shouldAddJarEntry_ThrowIllegalArgumentExceptionIfEntryAlreadyExists() throws Exception { final byte[] jarContent = getFromResources("bdr-jar.bak"); final byte[] entryContent = getFromResources("persistence.xml"); final String entryName = "META-INF/persistence.xml"; IOUtil.addJarEntry(jarContent, entryName, entryContent); } @Test public void testUpdatePropertyValue() throws IOException { final Properties properties = new Properties(); properties.put("key1", "value1"); properties.put("key2", "value2"); properties.put("key3", "value3"); final File file = tempFolderRule.newFile("testPropertiesFile"); PropertiesManager.saveProperties(properties, file); final String updatedValue2 = "@\\[||sfgf23465"; final Map pairs = new HashMap<>(); pairs.put("key2", updatedValue2); IOUtil.updatePropertyValue(file, pairs); final Properties updatedProperties = PropertiesManager.getProperties(file); Assert.assertEquals(updatedValue2, updatedProperties.get("key2")); } @Test public void getFileContent_should_return_content_of_file() throws Exception { Optional fileContent = IOUtil.getFileContent("myFile.txt"); assertThat(fileContent).get().isEqualTo("some content".getBytes()); } @Test public void getFileContent_should_return_empty_when_file_does_not_exists() throws Exception { Optional fileContent = IOUtil.getFileContent("myFile2.txt"); assertThat(fileContent).isNotPresent(); } @Test public void getContentTypeForIcon_should_throw_exception_when_not_an_image() { assertThat(IOUtil.getContentTypeForIcon("a.png")).isEqualTo("image/png"); assertThat(IOUtil.getContentTypeForIcon("a.jpg")).isEqualTo("image/jpeg"); } @Test public void getContentTypeForIcon_should_return_the_image_content() { assertThatThrownBy(() -> IOUtil.getContentTypeForIcon("")).isInstanceOf(IllegalArgumentException.class); assertThatThrownBy(() -> IOUtil.getContentTypeForIcon("a.b")).isInstanceOf(IllegalArgumentException.class); assertThatThrownBy(() -> IOUtil.getContentTypeForIcon("a.exe")).isInstanceOf(IllegalArgumentException.class); assertThatThrownBy(() -> IOUtil.getContentTypeForIcon("a")).isInstanceOf(IllegalArgumentException.class); } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/home/FolderMgrTest.java ================================================ /** * 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.home; import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import org.assertj.core.api.Assertions; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.contrib.java.lang.system.SystemOutRule; import org.junit.rules.TemporaryFolder; /** * @author Baptiste Mesta * @author Emmanuel Duchastenier */ public class FolderMgrTest { @Rule public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); @Rule public SystemOutRule systemOutRule = new SystemOutRule().enableLog(); @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void getPlatformGlobalClassLoaderFolder_should_create_all_parents() throws Exception { final Folder platformGlobalClassLoaderFolder = FolderMgr.getPlatformGlobalClassLoaderFolder(); assertThat(platformGlobalClassLoaderFolder.getFile()).exists().isDirectory(); } @Test public void getTempFolder_should_WARN_when_old_folder_still_exists() throws Exception { //given File tempFolder = temporaryFolder.newFolder(); System.setProperty("java.io.tmpdir", tempFolder.getAbsolutePath()); File bonita_engine_old1 = new File(tempFolder, "bonita_engine_old1"); File bonita_engine_old2 = new File(tempFolder, "bonita_engine_old2"); bonita_engine_old1.mkdir(); bonita_engine_old2.mkdir(); //when Folder tempFolder1 = FolderMgr.getTempFolder(); //then Assertions.assertThat(systemOutRule.getLog()).contains("Delete these folders to free up space:"); Assertions.assertThat(systemOutRule.getLog()).contains(bonita_engine_old1.getAbsolutePath()); Assertions.assertThat(systemOutRule.getLog()).contains(bonita_engine_old2.getAbsolutePath()); Assertions.assertThat(systemOutRule.getLog()).doesNotContain(tempFolder1.getFile().getName()); } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/home/FolderTest.java ================================================ /** * 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.home; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.util.Map; import org.apache.commons.io.filefilter.FileFileFilter; import org.junit.Before; import org.junit.Test; /** * author Emmanuel Duchastenier */ public class FolderTest { // @Spy private File f; // @InjectMocks private Folder folder; @Before public void setUp() throws IOException { // MockitoAnnotations.initMocks(this); f = spy(new File("/tmp/non-existent")); folder = spy(new Folder(f)); } @Test public void listFilesAsResources_should_ignore_subFolders() throws Exception { final File file = mock(File.class); doReturn("file").when(file).getName(); doReturn(true).when(file).isFile(); final File directory = mock(File.class); doReturn(false).when(directory).isFile(); File[] filesAndFolders = new File[] { file, directory }; doReturn(filesAndFolders).when(f).listFiles(any(FileFilter.class)); doReturn(filesAndFolders).when(f).listFiles(); doReturn(new File[] { file }).when(f).listFiles((FileFilter) FileFileFilter.FILE); doReturn(true).when(f).exists(); doReturn(true).when(f).isDirectory(); doReturn(new byte[1]).when(folder).getFileContent(file); doThrow(new RuntimeException("Directories should be filtered by Folder.listFilesAsResources() method")) .when(folder).getFileContent(directory); // should not fail: final Map map = folder.listFilesAsResources(); verify(folder).getFileContent(file); verify(folder, times(0)).getFileContent(directory); assertThat(map).hasSize(1); assertThat(map.get("file")).isNotNull(); } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/mdc/AbstractMDCTest.java ================================================ /** * 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.mdc; import static org.assertj.core.api.Assertions.assertThat; import java.io.ByteArrayOutputStream; import java.util.Map; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.OutputStreamAppender; import org.assertj.core.api.Assertions; import org.junit.BeforeClass; import org.junit.Test; import org.slf4j.LoggerFactory; import org.slf4j.MDC; /** * @author Vincent Hemery */ public class AbstractMDCTest { private static Logger log; private static ByteArrayOutputStream logStream; @BeforeClass public static void configureLogger() { LoggerContext logCtx = (LoggerContext) LoggerFactory.getILoggerFactory(); PatternLayoutEncoder logEncoder = new PatternLayoutEncoder(); logEncoder.setContext(logCtx); logEncoder.setPattern("%-12date{YYYY-MM-dd HH:mm:ss.SSS} %-5level – %msg {%X}%n"); logEncoder.start(); OutputStreamAppender logStreamAppender = new OutputStreamAppender<>(); logStreamAppender.setContext(logCtx); logStreamAppender.setName("console"); logStreamAppender.setEncoder(logEncoder); logStreamAppender.setImmediateFlush(true); logStream = new ByteArrayOutputStream(); logStreamAppender.setOutputStream(logStream); logStreamAppender.start(); log = logCtx.getLogger(AbstractMDCTest.class); log.addAppender(logStreamAppender); } @Test public void nestedMdc_should_temporarilyOverrideContext() { try (var firstMdc = new AbstractMDC(Map.of("key", "value1")) { }) { try (var secondMdc = new AbstractMDC(Map.of("key", "value2")) { }) { assertThat(MDC.get("key")).isEqualTo("value2"); } assertThat(MDC.get("key")).isEqualTo("value1"); } } @Test public void mdcClose_should_clearContext() { // given Map ctxMap = Map.of("key", "value1", "other", "value2"); for (int i = 0; i < 2; i++) { // when try (var mdc = new AbstractMDC(ctxMap) { }) { // then assertThat(MDC.get("key")).isEqualTo("value1"); assertThat(MDC.get("other")).isEqualTo("value2"); // when } // then assertThat(MDC.get("key")).isNull(); assertThat(MDC.get("other")).isNull(); // when logStream.reset(); log.info("This is an empty context test"); // then var str = logStream.toString(); Assertions.assertThat(str).contains("This is an empty context test {}"); } } @Test public void mdc_should_log() { // given Map ctxMap = Map.of(MDCConstants.USER_ID, "2", MDCConstants.SUBSTITUTE_USER_ID, "1"); try (var mdc = new AbstractMDC(ctxMap) { }) { // when logStream.reset(); log.info("This is a test"); // then var str = logStream.toString(); Assertions.assertThat(str).contains("This is a test {"); ctxMap.forEach((k, v) -> Assertions.assertThat(str).contains(k + "=" + v)); } } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/monitoring/DefaultExecutorServiceMetricsProviderTest.java ================================================ /** * 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.monitoring; import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.Test; public class DefaultExecutorServiceMetricsProviderTest { private ThreadPoolExecutor executorService = new ThreadPoolExecutor(1, 1, 100, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(10)); private MeterRegistry meterRegistry = new SimpleMeterRegistry(); private DefaultExecutorServiceMetricsProvider defaultExecutorServiceMetricsProvider = new DefaultExecutorServiceMetricsProvider(); @Test public void should_register_metrics_when_binding_the_threadpool() { defaultExecutorServiceMetricsProvider.bind(meterRegistry, executorService, "my-executor"); assertThat(meterRegistry.getMeters()).hasSize(9); } @Test public void should_have_no_more_metrics_when_we_unbind_the_executor() { defaultExecutorServiceMetricsProvider.bind(meterRegistry, executorService, "my-executor"); defaultExecutorServiceMetricsProvider.unbind(meterRegistry, "my-executor"); assertThat(meterRegistry.getMeters()).hasSize(0); } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/properties/BooleanPropertyTest.java ================================================ /** * Copyright (C) 2021 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.properties; import static com.github.stefanbirkner.systemlambda.SystemLambda.restoreSystemProperties; import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOut; import static com.github.stefanbirkner.systemlambda.SystemLambda.withEnvironmentVariable; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * @author Emmanuel Duchastenier */ class BooleanPropertyTest { @BeforeEach void setUp() { // Reset the static cache of already logged properties before each test BooleanProperty.clearLoggedProperties(); } @Test void booleanProperty_should_take_System_property_if_set() throws Exception { restoreSystemProperties(() -> { final String propertyKey = "my.feature.enable"; System.setProperty(propertyKey, "false"); Boolean enabled = withEnvironmentVariable(propertyKey, "true") .execute(() -> new BooleanProperty("Some feature", propertyKey, true).isEnabled()); assertThat(enabled).isFalse(); }); } @Test void booleanProperty_should_take_envVar_if_no_System_property_if_set() throws Exception { final String systemPropertyKey = "my.super-cool.feature.enabled"; final String envPropertyKey = "MY_SUPERCOOL_FEATURE_ENABLED"; Boolean enabled = withEnvironmentVariable(envPropertyKey, "false") .execute(() -> new BooleanProperty("Some feature", systemPropertyKey, true).isEnabled()); assertThat(enabled).isFalse(); } @Test void booleanProperty_should_take_default_value_if_no_System_property_nor_env_variable_if_set() { assertThat(new BooleanProperty("Some feature", "some.key", false).isEnabled()).isFalse(); } @Test void initialization_message_should_be_logged_once_only() throws Exception { String log = tapSystemOut(() -> new BooleanProperty("my boolean property", "my.boolean.property", false)); assertThat(log).contains( "my boolean property disabled, you may enable it using env property MY_BOOLEAN_PROPERTY or System property -Dmy.boolean.property [=true/false]"); // should not log again: log = tapSystemOut(() -> new BooleanProperty("my property", "my.boolean.property", false)); assertThat(log).doesNotContain("my.property"); } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/properties/StringPropertyTest.java ================================================ /** * Copyright (C) 2025 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.properties; import static com.github.stefanbirkner.systemlambda.SystemLambda.tapSystemOut; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class StringPropertyTest { @BeforeEach void setUp() { // Reset the static cache of already logged properties before each test StringProperty.clearLoggedProperties(); } @Test void initialization_message_should_be_logged_once_only() throws Exception { String log = tapSystemOut(() -> new StringProperty("my property", "my.property", "default value")); assertThat(log).contains( "my property default value, you may set it using env property MY_PROPERTY or System property -Dmy.property"); // should not log again: log = tapSystemOut(() -> new StringProperty("my property", "my.property", "default value")); assertThat(log).doesNotContain("my.property"); } } ================================================ FILE: services/bonita-commons/src/test/java/org/bonitasoft/engine/service/ServicesResolverTest.java ================================================ /** * 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; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; import java.io.Serializable; import java.util.Map; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; /** * @author Baptiste Mesta */ @ExtendWith(MockitoExtension.class) class ServicesResolverTest { @Mock private ServicesLookup servicesLookup; @InjectMocks private ServicesResolver servicesResolver; private final class BeanThatNeedMyService { private Object myService; @InjectedService public void setMyService(final Object myService) { this.myService = myService; } Object getMyService() { return myService; } public String getName() { return null; } public String getDescription() { return null; } public void execute() { } public void setAttributes(final Map attributes) { } } @Test void should_injectService_inject_setter_having_the_annotation() throws Exception { final BeanThatNeedMyService beanThatNeedMyService = new BeanThatNeedMyService(); final Object myService = new Object(); when(servicesLookup.lookupService("myService")).thenReturn(myService); servicesResolver.injectServices(beanThatNeedMyService); assertThat(beanThatNeedMyService.getMyService()).isEqualTo(myService); } } ================================================ FILE: services/bonita-commons/src/test/resources/myFile.txt ================================================ some content ================================================ FILE: services/bonita-commons/src/test/resources/org/bonitasoft/engine/commons/io/persistence.xml ================================================ org.hibernate.jpa.HibernatePersistenceProvider java:/comp/env/jdbc/PGDS1 com.bonitasoft.pojo.Employee ================================================ FILE: services/bonita-connector-executor/build.gradle ================================================ plugins { id('bonita-tests') } dependencies { runtimeOnly libs.logback api project(':services:bonita-commons') api project(':services:bonita-time-tracker') api project(':services:bonita-session') testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.mockitoJunitJupiter testImplementation libs.systemRules annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/AbstractSConnector.java ================================================ /** * 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.connector; import java.util.HashMap; import java.util.Map; /** * @author Feng Hui */ public abstract class AbstractSConnector implements SConnector { private final Map inputParameters; private final Map outputParameters; public AbstractSConnector() { inputParameters = new HashMap<>(); outputParameters = new HashMap<>(); } @Override public final void setInputParameters(final Map parameters) { inputParameters.putAll(parameters); } protected Object getInputParameter(final String paramName) { Object obj = null; if (inputParameters.containsKey(paramName)) { obj = inputParameters.get(paramName); } return obj; } protected void setOutputParameter(final String paramName, final Object value) { outputParameters.put(paramName, value); } protected Map getOutputParameters() { return outputParameters; } } ================================================ FILE: services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/BonitaConnectorExecutorFactory.java ================================================ /** * 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.connector; import java.util.concurrent.ThreadPoolExecutor; public interface BonitaConnectorExecutorFactory { ThreadPoolExecutor create(); } ================================================ FILE: services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/ConnectorExecutionResult.java ================================================ /** * Copyright (C) 2020 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.connector; import java.util.Map; import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor public class ConnectorExecutionResult { public static class ConnectorExecutionResultBuilder { private ConnectorExecutionResult connectorExecutionResult; ConnectorExecutionResultBuilder(Map outputs) { connectorExecutionResult = new ConnectorExecutionResult(outputs); } public ConnectorExecutionResult tookMillis(long executionTimeMillis) { connectorExecutionResult.executionTimeMillis = executionTimeMillis; return connectorExecutionResult; } } public static ConnectorExecutionResultBuilder result(Map outputs) { return new ConnectorExecutionResultBuilder(outputs); } private long executionTimeMillis; private Map outputs; private ConnectorExecutionResult(Map outputs) { this.outputs = outputs; } } ================================================ FILE: services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/ConnectorExecutor.java ================================================ /** * 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.connector; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.bonitasoft.engine.commons.TenantLifecycleService; import org.bonitasoft.engine.connector.exception.SConnectorException; /** * @author Feng Hui * @author Matthieu Chaffotte * @since 6.0 */ public interface ConnectorExecutor extends TenantLifecycleService { /** * Executes a connector. * * @param sConnector * The connector will be executed * @param inputParameters * The input parameters of connector * @param classLoader * The classLoader within the connector will be executed * @return a completable future with the result * @throws SConnectorException * Error thrown when error occurs in connector executing */ CompletableFuture execute(SConnector sConnector, Map inputParameters, ClassLoader classLoader) throws SConnectorException; /** * call disconnect method of the connector * * @param sConnector * @throws SConnectorException */ void disconnect(SConnector sConnector) throws SConnectorException; } ================================================ FILE: services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/SConnector.java ================================================ /** * 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.connector; import java.util.Map; import org.bonitasoft.engine.connector.exception.SConnectorException; import org.bonitasoft.engine.connector.exception.SConnectorValidationException; /** * @author Feng Hui */ public interface SConnector { /** * Set the input parameter for a connector. * * @param parameters * parameters is a map with parameter names and their value. */ void setInputParameters(Map parameters); /** * Validate the input parameters. Check the parameters types and boundaries. * * @throws SConnectorValidationException */ void validate() throws SConnectorValidationException; /** * Execute the connector. * * @return the connector outputs map corresponding to the output definition. * @throws SConnectorException */ Map execute() throws SConnectorException; /** * Called by the engine before the connector is executed * This method can be implemented by connectors to handle here opening of connections like database connection * * @throws SConnectorException */ void connect() throws SConnectorException; /** * Called by the engine after the connector and its output operations are executed * This method can be implemented by connectors to close connections here. * The typical use of this is to be able to return connected objects that will be used in output operation and then * disconnect them. * * @throws SConnectorException */ void disconnect() throws SConnectorException; } ================================================ FILE: services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/exception/SConnectorException.java ================================================ /** * 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.connector.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Feng Hui */ public class SConnectorException extends SBonitaException { private static final long serialVersionUID = -3113075377405323282L; public SConnectorException(String message) { super(message); } public SConnectorException(Throwable t) { super(t); } public SConnectorException(String message, Exception e) { super(message, e); } } ================================================ FILE: services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/exception/SConnectorValidationException.java ================================================ /** * 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.connector.exception; /** * @author Feng Hui * @author Celine Souchet */ public class SConnectorValidationException extends SConnectorException { private static final long serialVersionUID = -7025831546419799447L; public SConnectorValidationException(String message) { super(message); } public SConnectorValidationException(Throwable t) { super(t); } public SConnectorValidationException(String message, Exception e) { super(message, e); } } ================================================ FILE: services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/exception/SInvalidEvaluationConnectorConditionException.java ================================================ /** * 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.connector.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Thrown when the evaluation of the condition of the connector instance is invalid. * The class SInvalidEvaluationConnectorConditionException is a form of Throwable that indicates conditions that a * reasonable application might want to catch. * The class SInvalidEvaluationConnectorConditionException that is not also subclasses of {@link RuntimeException} are * checked exceptions. * Checked exceptions need to be declared in a method or constructor's {@literal throws} clause if they can be thrown by * the execution of the method or * constructor and propagate outside the method or constructor boundary. * * @author Celine Souchet */ public class SInvalidEvaluationConnectorConditionException extends SBonitaException { private static final long serialVersionUID = -7035298849808114112L; /** * Constructs a new exception with the two conditions to compare * * @param condition1 * The first condition * @param condition2 * The second condition */ public SInvalidEvaluationConnectorConditionException(final int condition1, final int condition2) { super(condition1 + " is not equal to " + condition2 + " ."); } } ================================================ FILE: services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/impl/ConnectorExecutorImpl.java ================================================ /** * 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.connector.impl; import static org.bonitasoft.engine.connector.ConnectorExecutionResult.result; import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.connector.BonitaConnectorExecutorFactory; import org.bonitasoft.engine.connector.ConnectorExecutionResult; import org.bonitasoft.engine.connector.ConnectorExecutor; import org.bonitasoft.engine.connector.SConnector; import org.bonitasoft.engine.connector.exception.SConnectorException; import org.bonitasoft.engine.monitoring.ExecutorServiceMetricsProvider; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException; import org.bonitasoft.engine.tracking.TimeTracker; import org.bonitasoft.engine.tracking.TimeTrackerRecords; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; /** * Execute connectors directly * * @author Baptiste Mesta * @author Celine Souchet * @author Matthieu Chaffotte */ @Slf4j @Component @ConditionalOnSingleCandidate(ConnectorExecutor.class) public class ConnectorExecutorImpl implements ConnectorExecutor { public static final String NUMBER_OF_CONNECTORS_PENDING = "bonita.bpmengine.connector.pending"; public static final String NUMBER_OF_CONNECTORS_RUNNING = "bonita.bpmengine.connector.running"; public static final String NUMBER_OF_CONNECTORS_EXECUTED = "bonita.bpmengine.connector.executed"; public static final String CONNECTORS_UNIT = "connectors"; private ExecutorService executorService; private final BonitaConnectorExecutorFactory bonitaConnectorExecutorFactory; private final SessionAccessor sessionAccessor; private final SessionService sessionService; private final TimeTracker timeTracker; private final MeterRegistry meterRegistry; private final ExecutorServiceMetricsProvider executorServiceMetricsProvider; private final AtomicLong runningWorks = new AtomicLong(); private Counter executedWorkCounter; private Gauge numberOfConnectorsPending; private Gauge numberOfConnectorsRunning; public ConnectorExecutorImpl(final SessionAccessor sessionAccessor, final SessionService sessionService, final TimeTracker timeTracker, final MeterRegistry meterRegistry, ExecutorServiceMetricsProvider executorServiceMetricsProvider, BonitaConnectorExecutorFactory bonitaConnectorExecutorFactory) { this.sessionAccessor = sessionAccessor; this.sessionService = sessionService; this.timeTracker = timeTracker; this.meterRegistry = meterRegistry; this.executorServiceMetricsProvider = executorServiceMetricsProvider; this.bonitaConnectorExecutorFactory = bonitaConnectorExecutorFactory; } @Override public CompletableFuture execute(final SConnector sConnector, final Map inputParameters, final ClassLoader classLoader) throws SConnectorException { if (executorService == null) { throw new SConnectorException("Unable to execute a connector, if the node is not started. Start it first"); } ExecuteConnectorCallable task = new ExecuteConnectorCallable(inputParameters, sConnector, classLoader); return execute(sConnector, task); } protected CompletableFuture execute(SConnector sConnector, InterruptibleCallable> task) { return CompletableFuture.supplyAsync(() -> { try { return wrapForStats(task).call(); } catch (Throwable e) { disconnectSilently(sConnector); throw new SBonitaRuntimeException(e); } }, executorService); } private Callable wrapForStats(final Callable> task) { return () -> { runningWorks.incrementAndGet(); try { long startTime = System.currentTimeMillis(); Map call = task.call(); executedWorkCounter.increment(); return result(call).tookMillis(System.currentTimeMillis() - startTime); } finally { runningWorks.decrementAndGet(); } }; } private void track(final TimeTrackerRecords recordName, final long startTime, final SConnector sConnector, final Map inputParameters) { if (timeTracker.isTrackable(recordName)) { final long endTime = System.currentTimeMillis(); final StringBuilder desc = new StringBuilder(); desc.append("Connector: "); desc.append(sConnector); desc.append(" - "); desc.append("inputParameters: "); desc.append(inputParameters); timeTracker.track(recordName, desc.toString(), endTime - startTime); } } void disconnectSilently(final SConnector sConnector) { try { sConnector.disconnect(); } catch (final Exception t) { log.warn("An error occurred while disconnecting the connector: {}", sConnector, t); } } @Override public void disconnect(final SConnector sConnector) throws SConnectorException { try { sConnector.disconnect(); } catch (final SConnectorException e) { throw e; } catch (final Exception t) { throw new SConnectorException(t); } } /** * @author Baptiste Mesta */ public final class ExecuteConnectorCallable implements InterruptibleCallable> { private final Map inputParameters; private final SConnector sConnector; private final ClassLoader loader; private Thread thread; private boolean interrupted; private boolean completed; private ExecuteConnectorCallable(final Map inputParameters, final SConnector sConnector, final ClassLoader loader) { this.inputParameters = inputParameters; this.sConnector = sConnector; this.loader = loader; } @Override public Map call() throws Exception { log.debug("Start execution of connector {}", sConnector.getClass()); if (interrupted) { throw new InterruptedException(); } final long startTime = System.currentTimeMillis(); //Fix Classloading issue with ThreadLocal implementation of SessionAccessor Thread.currentThread().setContextClassLoader(loader); sConnector.setInputParameters(inputParameters); try { thread = Thread.currentThread(); sConnector.validate(); sConnector.connect(); return sConnector.execute(); } finally { thread = null; completed = true; log.info("Finish execution of connector {}", sConnector.getClass()); // in case a session has been created: see ConnectorAPIAccessorImpl try { final long sessionId = sessionAccessor.getSessionId(); sessionAccessor.deleteSessionId(); sessionService.deleteSession(sessionId); } catch (final SessionIdNotSetException e) { // nothing, no session has been created } track(TimeTrackerRecords.EXECUTE_CONNECTOR_CALLABLE, startTime, sConnector, inputParameters); } } @Override public void interrupt() { interrupted = true; if (thread != null) { StackTraceElement[] stackTrace = thread.getStackTrace(); String stack = Arrays.stream(stackTrace).map(StackTraceElement::toString) .collect(Collectors.joining("\n")); log.warn( "Interrupt thread of connector {}, thread is {}, {}, connectors was doing :\n {}, activate debug logs to have the full execution stacktrace.", sConnector.getClass(), thread.getName(), thread.getId(), stackTrace[0].toString()); log.debug("Interrupt thread of connector {}, thread is {}, {}, stack is:\n {}", sConnector.getClass(), thread.getName(), thread.getId(), stack); thread.interrupt(); } } @Override public boolean isCompleted() { return completed; } } @Override public void start() { if (executorService == null) { var threadPoolExecutor = bonitaConnectorExecutorFactory.create(); executorService = executorServiceMetricsProvider .bind(meterRegistry, threadPoolExecutor, "bonita-connector-executor"); numberOfConnectorsPending = Gauge .builder(NUMBER_OF_CONNECTORS_PENDING, threadPoolExecutor.getQueue(), Collection::size) .baseUnit(CONNECTORS_UNIT).description("Connectors pending in the execution queue") .register(meterRegistry); numberOfConnectorsRunning = Gauge.builder(NUMBER_OF_CONNECTORS_RUNNING, runningWorks, AtomicLong::get) .baseUnit(CONNECTORS_UNIT).description("Connectors currently executing") .register(meterRegistry); executedWorkCounter = Counter.builder(NUMBER_OF_CONNECTORS_EXECUTED) .baseUnit(CONNECTORS_UNIT) .description("Total connectors executed since last server start") .register(meterRegistry); } } // For unit tests ExecutorService getExecutorService() { return executorService; } @Override public void stop() { if (executorService != null) { meterRegistry.remove(executedWorkCounter); meterRegistry.remove(numberOfConnectorsRunning); meterRegistry.remove(numberOfConnectorsPending); executorServiceMetricsProvider.unbind(meterRegistry, "bonita-connector-executor"); executorService.shutdown(); try { if (!executorService.awaitTermination(5000, TimeUnit.MILLISECONDS)) { log.warn("Timeout (5s) trying to stop the connector executor thread pool."); } } catch (final InterruptedException e) { log.warn("Error while stopping the connector executor thread pool.", e); } executorService = null; } } @Override public void pause() { stop(); } @Override public void resume() { start(); } } ================================================ FILE: services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/impl/ConnectorExecutorThreadFactory.java ================================================ /** * 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.connector.impl; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; /** * @author Baptiste Mesta */ public class ConnectorExecutorThreadFactory implements ThreadFactory { private static AtomicInteger nbThread = new AtomicInteger(1); private final String name; public ConnectorExecutorThreadFactory(final String name) { this.name = name; } @Override public Thread newThread(final Runnable runnable) { return new Thread(runnable, name + "-" + nbThread.getAndIncrement()); } } ================================================ FILE: services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/impl/ConnectorSingleThreadExecutorFactory.java ================================================ /** * 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.connector.impl; import static java.util.concurrent.TimeUnit.MILLISECONDS; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; import org.bonitasoft.engine.connector.BonitaConnectorExecutorFactory; import org.bonitasoft.engine.mdc.MDCTransmitingThreadPoolExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; @Component @ConditionalOnSingleCandidate(BonitaConnectorExecutorFactory.class) public class ConnectorSingleThreadExecutorFactory implements BonitaConnectorExecutorFactory { private final int queueCapacity; public ConnectorSingleThreadExecutorFactory(@Value("${bonita.tenant.connector.queueCapacity}") int queueCapacity) { this.queueCapacity = queueCapacity; } @Override public ThreadPoolExecutor create() { return new MDCTransmitingThreadPoolExecutor(1, 1, 0L, MILLISECONDS, new ArrayBlockingQueue<>(queueCapacity), new ConnectorExecutorThreadFactory("ConnectorExecutor"), new QueueRejectedExecutionHandler()); } public static class QueueRejectedExecutionHandler implements RejectedExecutionHandler { private static final Logger log = LoggerFactory.getLogger(QueueRejectedExecutionHandler.class); @Override public void rejectedExecution(final Runnable task, final ThreadPoolExecutor executor) { log.warn("The work was rejected. Requeue work : {}", task); try { executor.getQueue().put(task); } catch (final InterruptedException e) { throw new RejectedExecutionException("Queuing " + task + " got interrupted.", e); } } } } ================================================ FILE: services/bonita-connector-executor/src/main/java/org/bonitasoft/engine/connector/impl/InterruptibleCallable.java ================================================ /** * 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.connector.impl; import java.util.concurrent.Callable; public interface InterruptibleCallable extends Callable { void interrupt(); boolean isCompleted(); } ================================================ FILE: services/bonita-connector-executor/src/test/java/org/bonitasoft/engine/connector/impl/ConnectorExecutionTest.java ================================================ /** * 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.connector.impl; import static org.assertj.core.api.Assertions.assertThat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.bonitasoft.engine.connector.AbstractSConnector; import org.bonitasoft.engine.connector.ConnectorExecutionResult; import org.bonitasoft.engine.connector.exception.SConnectorException; import org.bonitasoft.engine.connector.exception.SConnectorValidationException; import org.bonitasoft.engine.monitoring.DefaultExecutorServiceMetricsProvider; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.tracking.TimeTracker; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ConnectorExecutionTest { @Mock private SessionAccessor sessionAccessor; @Mock private SessionService sessionService; @Mock private TimeTracker timeTracker; private ConnectorExecutorImpl connectorExecutor; @Before public void setUp() { connectorExecutor = new ConnectorExecutorImpl(sessionAccessor, sessionService, timeTracker, new SimpleMeterRegistry(), new DefaultExecutorServiceMetricsProvider(), new ConnectorSingleThreadExecutorFactory(10)); connectorExecutor.start(); } @After public void tearDown() { connectorExecutor.stop(); } @Test public void should_execute_a_simple_connector() throws Exception { connectorExecutor.execute(new ResourceConnector("test2"), null, new ResourceClassLoader("test2")); } @Test public void should_execute_connectors_concurrently() throws Exception { final Callable task = buildConnectorExecutionCallable("test"); final Callable task2 = buildConnectorExecutionCallable("test2"); final ExecutorService service = Executors.newFixedThreadPool(25); final List> tasks1 = Collections.nCopies(50, task); final List> tasks2 = Collections.nCopies(50, task2); final List> tasks = new ArrayList<>(tasks1); tasks.addAll(tasks2); final List> all = service.invokeAll(tasks); service.shutdown(); for (final Future future : all) { future.get(); } } @Test public void should_return_execution_time_of_the_connector() throws Exception { ConnectorExecutionResult connectorExecutionResult = connectorExecutor .execute(new SleepConnector(50), Collections.emptyMap(), Thread.currentThread().getContextClassLoader()) .get(); assertThat(connectorExecutionResult.getExecutionTimeMillis()).isGreaterThanOrEqualTo(50).isLessThan(1000); } private Callable buildConnectorExecutionCallable(final String resourceName) { return () -> { connectorExecutor.execute(new ResourceConnector(resourceName), null, new ResourceClassLoader(resourceName)); return null; }; } private static class SleepConnector extends AbstractSConnector { private int sleepMillis; public SleepConnector(int sleepMillis) { this.sleepMillis = sleepMillis; } @Override public void validate() throws SConnectorValidationException { } @Override public Map execute() throws SConnectorException { try { sleepMillis = 100; Thread.sleep(sleepMillis); } catch (InterruptedException e) { throw new SConnectorException(e); } return null; } @Override public void connect() throws SConnectorException { } @Override public void disconnect() throws SConnectorException { } } } ================================================ FILE: services/bonita-connector-executor/src/test/java/org/bonitasoft/engine/connector/impl/ConnectorExecutorImplTest.java ================================================ /** * 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.connector.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import java.time.Duration; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.bonitasoft.engine.connector.AbstractSConnector; import org.bonitasoft.engine.connector.ConnectorExecutionResult; import org.bonitasoft.engine.connector.SConnector; import org.bonitasoft.engine.connector.exception.SConnectorException; import org.bonitasoft.engine.monitoring.DefaultExecutorServiceMetricsProvider; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.tracking.TimeTracker; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ConnectorExecutorImplTest { @Mock private SessionAccessor sessionAccessor; @Mock private SessionService sessionService; @Mock private SConnector connector; @Mock private TimeTracker timeTracker; private ConnectorExecutorImpl connectorExecutorImpl; private SimpleMeterRegistry meterRegistry; @Rule public SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests(); @Before public void before() { meterRegistry = new SimpleMeterRegistry( // So that micrometer updates its counters every 1 ms: k -> k.equals("simple.step") ? Duration.ofMillis(1).toString() : null, Clock.SYSTEM); connectorExecutorImpl = new ConnectorExecutorImpl(sessionAccessor, sessionService, timeTracker, meterRegistry, new DefaultExecutorServiceMetricsProvider(), new ConnectorSingleThreadExecutorFactory(1)); connectorExecutorImpl.start(); } @Test public void should_execute_submit_callable() throws Exception { SConnector connector = new SConnector() { @Override public void setInputParameters(Map parameters) { } @Override public void validate() { } @Override public Map execute() { return Collections.singletonMap("result", "resultValue"); } @Override public void connect() { } @Override public void disconnect() { } }; final ConnectorExecutionResult result = connectorExecutorImpl.execute(connector, Collections.singletonMap("key", "value"), Thread.currentThread().getContextClassLoader()) .get(100, TimeUnit.MILLISECONDS); assertThat(result.getOutputs().size()).isEqualTo(1); assertThat(result.getOutputs().get("result")).isEqualTo("resultValue"); } @Test(expected = SConnectorException.class) public void should_execute_throw_exception_when_not_started() throws Exception { // given connectorExecutorImpl.stop(); // when connectorExecutorImpl.execute(connector, Collections.singletonMap("key", "value"), Thread.currentThread().getContextClassLoader()); } @Test public void should_disconnect_call_disconnect_on_connector() throws Exception { // when connectorExecutorImpl.disconnect(connector); // then verify(connector).disconnect(); } @Test public void should_disconnect_rethrow_connector_exceptions() throws Exception { // given final SConnectorException exception = new SConnectorException("myException"); doThrow(exception).when(connector).disconnect(); // when try { connectorExecutorImpl.disconnect(connector); fail("should have thrown the exception"); } catch (final SConnectorException e) { // then assertThat(e).isEqualTo(exception); } } @Test public void should_disconnectSilently_only_logException() throws Exception { // given final SConnectorException exception = new SConnectorException("myException"); doThrow(exception).when(connector).disconnect(); // when systemOutRule.clearLog(); connectorExecutorImpl.disconnectSilently(connector); // then assertThat(systemOutRule.getLog()) .contains("An error occurred while disconnecting the connector: " + connector); } @Test public void should_stop_await_termination_of_thread_pool() { ExecutorService executorService = connectorExecutorImpl.getExecutorService(); connectorExecutorImpl.stop(); assertThat(executorService.isShutdown()).isTrue(); } @Test public void pause_should_await_termination_of_thread_pool() { ExecutorService executorService = connectorExecutorImpl.getExecutorService(); connectorExecutorImpl.stop(); assertThat(executorService.isShutdown()).isTrue(); } @Test public void start_should_await_termination_of_thread_pool() { // when // start in before // then assertThat(connectorExecutorImpl.getExecutorService()).as("The executor service must be not null.").isNotNull(); } @Test public void resume_should_await_termination_of_thread_pool() { // when connectorExecutorImpl.pause(); connectorExecutorImpl.resume(); // then assertThat(connectorExecutorImpl.getExecutorService()).as("The executor service must be not null.").isNotNull(); } @Test public void should_update_connectors_counters_when_adding_a_connector_with_immediate_execution() throws Exception { //when: executeAConnector(); TimeUnit.MILLISECONDS.sleep(50); // give some time to consider the connector to process //then: assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_EXECUTED).counter().count()) .as("Executed connectors number").isEqualTo(1); assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_RUNNING).gauge().value()) .as("Running connectors number").isEqualTo(0); assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_PENDING).gauge().value()) .as("Pending connectors number").isEqualTo(0); } @Test public void should_reset_and_have_counters_after_pause_and_resume() throws Exception { //when: executeAConnector(); TimeUnit.MILLISECONDS.sleep(50); // give some time to consider the connector to process assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_EXECUTED).counter().count()) .as("Executed connectors number").isEqualTo(1); assertThat(meterRegistry.find("executor.completed").functionCounter().count()).isEqualTo(1); connectorExecutorImpl.pause(); assertThat(meterRegistry.getMeters()).hasSize(0); connectorExecutorImpl.resume(); executeAConnector(); executeAConnector(); TimeUnit.MILLISECONDS.sleep(50); // give some time to consider the connector to process //then: assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_EXECUTED).counter().count()) .as("Executed connectors number").isEqualTo(2); assertThat(meterRegistry.find("executor.completed").functionCounter().count()).isEqualTo(2); } private void executeAConnector() throws InterruptedException, java.util.concurrent.ExecutionException, SConnectorException { connectorExecutorImpl .execute(new LocalSConnector(-1), new HashMap<>(), Thread.currentThread().getContextClassLoader()) .get(); } @Test public void should_update_connectors_counters_when_enqueuing_connectors_with_long_processing_time() throws Exception { connectorExecutorImpl.execute(new LocalSConnector(2), new HashMap<>(), Thread.currentThread().getContextClassLoader()); connectorExecutorImpl.execute(new LocalSConnector(2), new HashMap<>(), Thread.currentThread().getContextClassLoader()); TimeUnit.MILLISECONDS.sleep(50); // give some time to consider the connector to process //then: one is in queue (only one thread to execute connectors) and one is pending assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_EXECUTED).counter().count()) .as("Executed connectors number").isEqualTo(0); assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_RUNNING).gauge().value()) .as("Running connectors number").isEqualTo(1); assertThat(meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_PENDING).gauge().value()) .as("Pending connectors number").isEqualTo(1); } @Test public void createExecutorService_should_register_ExecutorServiceMetrics() { assertThat( meterRegistry.find("executor.pool.size") .tag("name", "bonita-connector-executor") .gauge()) .isNotNull(); } // ================================================================================================================= // UTILS // ================================================================================================================= private static class LocalSConnector extends AbstractSConnector { private final long sleepPeriodInSeconds; private LocalSConnector(long sleepPeriodInSeconds) { this.sleepPeriodInSeconds = sleepPeriodInSeconds; } @Override public void validate() { // do nothing } @Override public Map execute() { if (sleepPeriodInSeconds > 0) { try { TimeUnit.SECONDS.sleep(sleepPeriodInSeconds); } catch (InterruptedException e) { throw new RuntimeException(e); } } return new HashMap<>(); } @Override public void connect() { // do nothing } @Override public void disconnect() { // do nothing } } } ================================================ FILE: services/bonita-connector-executor/src/test/java/org/bonitasoft/engine/connector/impl/ConnectorExecutorSingleThreadTest.java ================================================ /** * 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.connector.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.time.Duration; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.bonitasoft.engine.connector.AbstractSConnector; import org.bonitasoft.engine.connector.ConnectorExecutionResult; import org.bonitasoft.engine.monitoring.DefaultExecutorServiceMetricsProvider; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.tracking.TimeTracker; import org.junit.jupiter.api.AfterEach; 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.junit.jupiter.MockitoExtension; /** * Tests for ConnectorExecutorImpl with pool size 1 (single thread). * Documents that the pool correctly replaces its worker thread after connector exceptions, * so that subsequent connectors are not stuck forever. */ @ExtendWith(MockitoExtension.class) class ConnectorExecutorSingleThreadTest { private static final long TENANT_ID = 12L; @Mock private SessionAccessor sessionAccessor; @Mock private SessionService sessionService; @Mock private TimeTracker timeTracker; private ConnectorExecutorImpl connectorExecutorImpl; private SimpleMeterRegistry meterRegistry; @BeforeEach void setUp() { meterRegistry = new SimpleMeterRegistry( // So that micrometer updates its counters every 1 ms: k -> k.equals("simple.step") ? Duration.ofMillis(1).toString() : null, Clock.SYSTEM); connectorExecutorImpl = new ConnectorExecutorImpl(sessionAccessor, sessionService, timeTracker, meterRegistry, new DefaultExecutorServiceMetricsProvider(), new ConnectorSingleThreadExecutorFactory(1)); connectorExecutorImpl.start(); } @AfterEach void tearDown() { connectorExecutorImpl.stop(); } @Test void execute_should_continue_processing_after_connector_exception() throws Exception { // Execute a failing connector assertThatThrownBy(() -> connectorExecutorImpl .execute(new FailingSConnector(), new HashMap<>(), Thread.currentThread().getContextClassLoader()) .get(5, TimeUnit.SECONDS)) .isInstanceOf(ExecutionException.class); // Execute a successful connector — must succeed even with pool size 1 ConnectorExecutionResult result = connectorExecutorImpl .execute(new LocalSConnector(), new HashMap<>(), Thread.currentThread().getContextClassLoader()) .get(5, TimeUnit.SECONDS); assertThat(result.getOutputs()).containsEntry("result", "success"); } @Test void execute_should_complete_future_exceptionally_on_connector_failure() { assertThatThrownBy(() -> connectorExecutorImpl .execute(new FailingSConnector(), new HashMap<>(), Thread.currentThread().getContextClassLoader()) .get(5, TimeUnit.SECONDS)) .isInstanceOf(ExecutionException.class) .hasCauseInstanceOf(RuntimeException.class) .hasMessageContaining("connector failure"); } @Test void execute_should_maintain_accurate_metrics_after_exception() throws Exception { // Execute a failing connector assertThatThrownBy(() -> connectorExecutorImpl .execute(new FailingSConnector(), new HashMap<>(), Thread.currentThread().getContextClassLoader()) .get(5, TimeUnit.SECONDS)) .isInstanceOf(ExecutionException.class); // Execute a successful connector connectorExecutorImpl .execute(new LocalSConnector(), new HashMap<>(), Thread.currentThread().getContextClassLoader()) .get(5, TimeUnit.SECONDS); TimeUnit.MILLISECONDS.sleep(50); // give micrometer time to update counters // Note: unlike the worker pool, the connector executor only counts successful executions var counter = meterRegistry.find(ConnectorExecutorImpl.NUMBER_OF_CONNECTORS_EXECUTED).counter(); assertThat(counter) .as("Counter for number of connectors executed should be present") .isNotNull(); assertThat(counter.count()) .as("Only the successful connector should be counted as executed") .isEqualTo(1); } // ================================================================================================================= // UTILS // ================================================================================================================= private static class LocalSConnector extends AbstractSConnector { @Override public void validate() { } @Override public Map execute() { return Collections.singletonMap("result", "success"); } @Override public void connect() { } @Override public void disconnect() { } } private static class FailingSConnector extends AbstractSConnector { @Override public void validate() { } @Override public Map execute() { throw new RuntimeException("connector failure"); } @Override public void connect() { } @Override public void disconnect() { } } } ================================================ FILE: services/bonita-connector-executor/src/test/java/org/bonitasoft/engine/connector/impl/ResourceClassLoader.java ================================================ /** * 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.connector.impl; public class ResourceClassLoader extends ClassLoader { private final String resourceName; public ResourceClassLoader(final String resourceName) { this.resourceName = resourceName; } @Override public Class loadClass(final String name) throws ClassNotFoundException { if (resourceName.equals(name)) { return null; } else { return super.loadClass(name); } } } ================================================ FILE: services/bonita-connector-executor/src/test/java/org/bonitasoft/engine/connector/impl/ResourceConnector.java ================================================ /** * 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.connector.impl; import java.util.Map; import org.bonitasoft.engine.connector.SConnector; import org.bonitasoft.engine.connector.exception.SConnectorException; import org.bonitasoft.engine.connector.exception.SConnectorValidationException; public class ResourceConnector implements SConnector { private final String resourceName; public ResourceConnector(final String resourceName) { this.resourceName = resourceName; } @Override public void setInputParameters(final Map parameters) { // Nothing to do } @Override public void validate() throws SConnectorValidationException { // Nothing to do } @Override public Map execute() throws SConnectorException { try { Thread.currentThread().getContextClassLoader().loadClass(resourceName); return null; } catch (final ClassNotFoundException cnfe) { throw new SConnectorException(cnfe); } } @Override public void connect() throws SConnectorException { // Nothing to do } @Override public void disconnect() throws SConnectorException { // Nothing to do } } ================================================ FILE: services/bonita-connector-executor/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: services/bonita-data-definition/build.gradle ================================================ dependencies { api project(':services:bonita-expression') } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/SDataDefinition.java ================================================ /** * 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.data.definition.model; import java.io.Serializable; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Zhao Na * @author Matthieu Chaffotte */ public interface SDataDefinition extends Serializable { String getName(); String getDescription(); String getClassName(); Boolean isTransientData(); SExpression getDefaultValueExpression(); } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/STextDataDefinition.java ================================================ /** * 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.data.definition.model; /** * @author Zhao Na */ public interface STextDataDefinition extends SDataDefinition { boolean isLongText(); } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/SXMLDataDefinition.java ================================================ /** * 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.data.definition.model; /** * @author Elias Ricken de Medeiros */ public interface SXMLDataDefinition extends SDataDefinition { String getNamespace(); String getElement(); } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/AbstractSDataDefinitionBuilder.java ================================================ /** * 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.data.definition.model.builder; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Zhao Na * @author Matthieu Chaffotte */ public interface AbstractSDataDefinitionBuilder { SDataDefinitionBuilder setName(String name); SDataDefinitionBuilder setDescription(String description); SDataDefinitionBuilder setTransient(boolean transientData); SDataDefinitionBuilder setDefaultValue(SExpression expression); } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/SDataDefinitionBuilder.java ================================================ /** * 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.data.definition.model.builder; import org.bonitasoft.engine.data.definition.model.SDataDefinition; /** * @author Zhao Na * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SDataDefinitionBuilder extends AbstractSDataDefinitionBuilder { SDataDefinitionBuilder setAsLongText(boolean value); SDataDefinition done(); } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/SDataDefinitionBuilderFactory.java ================================================ /** * 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.data.definition.model.builder; /** * @author Zhao Na * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SDataDefinitionBuilderFactory { SDataDefinitionBuilder createNewInstance(String name, String className); SDataDefinitionBuilder createNewTextData(final String name); } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/SEnumationDataDefinitionBuilder.java ================================================ /** * 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.data.definition.model.builder; import java.util.List; /** * @author Zhao Na * @author Matthieu Chaffotte */ public interface SEnumationDataDefinitionBuilder extends AbstractSDataDefinitionBuilder { SDataDefinitionBuilder enumarationValues(List enumerationValues); } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/SEnumationDataDefinitionBuilderFactory.java ================================================ /** * 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.data.definition.model.builder; /** * @author Zhao Na * @author Matthieu Chaffotte */ public interface SEnumationDataDefinitionBuilderFactory extends AbstractSDataDefinitionBuilder { SDataDefinitionBuilder createNewInstance(String name); } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/STextDataDefinitionBuilder.java ================================================ /** * 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.data.definition.model.builder; /** * @author Zhao Na * @author Elias Ricken de Medeiros */ public interface STextDataDefinitionBuilder { SDataDefinitionBuilder setAsLongText(boolean value); } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/STextDataDefinitionBuilderFactory.java ================================================ /** * 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.data.definition.model.builder; /** * @author Zhao Na * @author Elias Ricken de Medeiros */ public interface STextDataDefinitionBuilderFactory { SDataDefinitionBuilder setAsLongText(boolean value); } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/SXMLDataDefinitionBuilder.java ================================================ /** * 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.data.definition.model.builder; import org.bonitasoft.engine.data.definition.model.SXMLDataDefinition; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Zhao Na * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SXMLDataDefinitionBuilder { SXMLDataDefinitionBuilder setDescription(String description); SXMLDataDefinitionBuilder setTransient(boolean transientData); SXMLDataDefinitionBuilder setDefaultValue(SExpression expression); SXMLDataDefinitionBuilder setNamespace(final String namespace); SXMLDataDefinitionBuilder setElement(final String element); SXMLDataDefinition done(); } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/SXMLDataDefinitionBuilderFactory.java ================================================ /** * 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.data.definition.model.builder; /** * @author Zhao Na * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SXMLDataDefinitionBuilderFactory { SXMLDataDefinitionBuilder createNewXMLData(final String name); } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/impl/SDataDefinitionBuilderFactoryImpl.java ================================================ /** * 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.data.definition.model.builder.impl; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilderFactory; import org.bonitasoft.engine.data.definition.model.impl.SDataDefinitionImpl; import org.bonitasoft.engine.data.definition.model.impl.STextDefinitionImpl; /** * @author Zhao Na * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public class SDataDefinitionBuilderFactoryImpl implements SDataDefinitionBuilderFactory { @Override public SDataDefinitionBuilder createNewTextData(final String name) { final STextDefinitionImpl dataDefinitionImpl = new STextDefinitionImpl(); dataDefinitionImpl.setName(name); dataDefinitionImpl.setClassName(String.class.getName()); return new SDataDefinitionBuilderImpl(dataDefinitionImpl); } @Override public SDataDefinitionBuilder createNewInstance(final String name, final String className) { final SDataDefinitionImpl dataDefinitionImpl = new SDataDefinitionImpl(); dataDefinitionImpl.setName(name); dataDefinitionImpl.setClassName(className); return new SDataDefinitionBuilderImpl(dataDefinitionImpl); } } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/impl/SDataDefinitionBuilderImpl.java ================================================ /** * 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.data.definition.model.builder.impl; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.definition.model.builder.SDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.impl.SDataDefinitionImpl; import org.bonitasoft.engine.data.definition.model.impl.STextDefinitionImpl; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Zhao Na * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public class SDataDefinitionBuilderImpl implements SDataDefinitionBuilder { private final SDataDefinitionImpl dataDefinitionImpl; public SDataDefinitionBuilderImpl(final SDataDefinitionImpl dataDefinitionImpl) { super(); this.dataDefinitionImpl = dataDefinitionImpl; } @Override public SDataDefinition done() { return dataDefinitionImpl; } public SDataDefinitionBuilder setAsLongText(final boolean value) { if (dataDefinitionImpl instanceof STextDefinitionImpl && dataDefinitionImpl.getClassName().equals(String.class.getName())) { ((STextDefinitionImpl) dataDefinitionImpl).setIsLongText(value); } return this; } @Override public SDataDefinitionBuilder setName(final String name) { dataDefinitionImpl.setName(name); return this; } @Override public SDataDefinitionBuilder setDescription(final String description) { dataDefinitionImpl.setDescription(description); return this; } @Override public SDataDefinitionBuilder setTransient(final boolean transientData) { dataDefinitionImpl.setTransientData(transientData); return this; } @Override public SDataDefinitionBuilder setDefaultValue(final SExpression expression) { dataDefinitionImpl.setDefaultValueExpression(expression); return this; } public static SDataDefinitionBuilder getInstance() { return new SDataDefinitionBuilderImpl(null); } } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/impl/SXMLDataDefinitionBuilderFactoryImpl.java ================================================ /** * 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.data.definition.model.builder.impl; import org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilderFactory; import org.bonitasoft.engine.data.definition.model.impl.SXMLDataDefinitionImpl; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public class SXMLDataDefinitionBuilderFactoryImpl implements SXMLDataDefinitionBuilderFactory { @Override public SXMLDataDefinitionBuilder createNewXMLData(final String name) { final SXMLDataDefinitionImpl dataDefinitionImpl = new SXMLDataDefinitionImpl(); dataDefinitionImpl.setName(name); dataDefinitionImpl.setClassName(String.class.getName()); return new SXMLDataDefinitionBuilderImpl(dataDefinitionImpl); } } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/builder/impl/SXMLDataDefinitionBuilderImpl.java ================================================ /** * 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.data.definition.model.builder.impl; import org.bonitasoft.engine.data.definition.model.SXMLDataDefinition; import org.bonitasoft.engine.data.definition.model.builder.SXMLDataDefinitionBuilder; import org.bonitasoft.engine.data.definition.model.impl.SXMLDataDefinitionImpl; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public class SXMLDataDefinitionBuilderImpl implements SXMLDataDefinitionBuilder { private final SXMLDataDefinitionImpl dataDefinitionImpl; public SXMLDataDefinitionBuilderImpl( SXMLDataDefinitionImpl dataDefinitionImpl) { super(); this.dataDefinitionImpl = dataDefinitionImpl; } @Override public SXMLDataDefinitionBuilder setDescription(final String description) { dataDefinitionImpl.setDescription(description); return this; } @Override public SXMLDataDefinitionBuilder setTransient(final boolean transientData) { dataDefinitionImpl.setTransientData(transientData); return this; } @Override public SXMLDataDefinitionBuilder setDefaultValue(final SExpression expression) { dataDefinitionImpl.setDefaultValueExpression(expression); return this; } @Override public SXMLDataDefinitionBuilder setNamespace(final String namespace) { dataDefinitionImpl.setNamespace(namespace); return this; } @Override public SXMLDataDefinitionBuilder setElement(final String element) { dataDefinitionImpl.setElement(element); return this; } @Override public SXMLDataDefinition done() { return dataDefinitionImpl; } } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/impl/SDataDefinitionImpl.java ================================================ /** * 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.data.definition.model.impl; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Zhao Na * @author Matthieu Chaffotte */ public class SDataDefinitionImpl implements SDataDefinition { private static final long serialVersionUID = 1L; private String name; private String description; private boolean transientData; private String className; private SExpression defaultValueExpression; public SDataDefinitionImpl() { super(); } @Override public String getName() { return name; } @Override public String getDescription() { return description; } @Override public String getClassName() { return className; } @Override public Boolean isTransientData() { return transientData; } @Override public SExpression getDefaultValueExpression() { return defaultValueExpression; } public void setTransientData(final boolean transientData) { this.transientData = transientData; } public void setName(final String name) { this.name = name; } public void setDescription(final String description) { this.description = description; } public void setDefaultValueExpression(final SExpression defaultValueExpression) { this.defaultValueExpression = defaultValueExpression; } public void setClassName(final String className) { this.className = className; } } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/impl/STextDefinitionImpl.java ================================================ /** * 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.data.definition.model.impl; import org.bonitasoft.engine.data.definition.model.STextDataDefinition; /** * @author Zhao Na */ public class STextDefinitionImpl extends SDataDefinitionImpl implements STextDataDefinition { private static final long serialVersionUID = 1L; private boolean isLongText; @Override public boolean isLongText() { return isLongText; } public void setIsLongText(final boolean isLongText) { this.isLongText = isLongText; } } ================================================ FILE: services/bonita-data-definition/src/main/java/org/bonitasoft/engine/data/definition/model/impl/SXMLDataDefinitionImpl.java ================================================ /** * 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.data.definition.model.impl; import org.bonitasoft.engine.data.definition.model.SXMLDataDefinition; /** * @author Elias Ricken de Medeiros */ public class SXMLDataDefinitionImpl extends SDataDefinitionImpl implements SXMLDataDefinition { private static final long serialVersionUID = 1L; private String namespace; private String element; @Override public String getNamespace() { return namespace; } @Override public String getElement() { return element; } public void setNamespace(final String namespace) { this.namespace = namespace; } public void setElement(final String element) { this.element = element; } } ================================================ FILE: services/bonita-data-instance/build.gradle ================================================ dependencies { api project(':services:bonita-expression') api project(':services:bonita-builder') api project(':services:bonita-persistence') api project(':services:bonita-data-definition') api project(':services:bonita-commons') api project(':services:bonita-events') api project(':services:bonita-log') api project(':services:bonita-archive') testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.logback testImplementation libs.systemRules annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/api/DataContainer.java ================================================ /** * 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.data.instance.api; import java.util.Objects; /** * @author Baptiste Mesta */ public class DataContainer { private long id; private String type; public DataContainer(long id, String type) { this.id = id; this.type = type; } public long getId() { return id; } public String getType() { return type; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DataContainer that = (DataContainer) o; return id == that.id && Objects.equals(type, that.type); } @Override public int hashCode() { return Objects.hash(id, type); } @Override public String toString() { return "DataContainer{" + "id=" + id + ", type='" + type + '\'' + '}'; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/api/DataInstanceContainer.java ================================================ /** * 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.data.instance.api; /** * @author Feng Hui * @author Celine Souchet */ public enum DataInstanceContainer { PROCESS_INSTANCE, ACTIVITY_INSTANCE, MESSAGE_INSTANCE } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/api/DataInstanceService.java ================================================ /** * 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.data.instance.api; import java.util.List; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SADataInstance; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Zhao Na * @author Elias Ricken de Medeiros * @author Feng Hui * @author Matthieu Chaffotte * @since 6.0 */ public interface DataInstanceService { // just insert dataInstance to DB /** * Create dataInstance in DB for given dataInstance * * @param dataInstance * SDataInstance object * @throws SDataInstanceException */ void createDataInstance(final SDataInstance dataInstance) throws SDataInstanceException; /** * Update the specific dataInstance according to the given descriptor * * @param dataInstance * SDataInstance object will be updated * @param descriptor * Update description * @throws SDataInstanceException */ void updateDataInstance(final SDataInstance dataInstance, final EntityUpdateDescriptor descriptor) throws SDataInstanceException; /** * Delete the specific dataInstance * * @param dataInstance * SDataInstance object will be deleted * @throws SDataInstanceException */ void deleteDataInstance(final SDataInstance dataInstance) throws SDataInstanceException; /** * Get dataInstance by its id * * @param dataInstanceId * Identifier of dataInstance * @return a SDataInstance object * @throws SDataInstanceException */ SDataInstance getDataInstance(final long dataInstanceId) throws SDataInstanceException; /** * Get dataInstance visible in the specific container * * @param dataName * Name of data instance * @param containerId * Identifier of container * @param containerType * Type of container, e.g process instance, activity instance and so on. * @return a SDataInstance object * @throws SDataInstanceException */ SDataInstance getDataInstance(final String dataName, final long containerId, final String containerType, final ParentContainerResolver parentContainerResolver) throws SDataInstanceException; /** * Get dataInstances visible in the specific container for given names * * @param dataNames * A list of names of data instances * @param containerId * Identifier of container * @param containerType * Type of container, e.g process instance, activity instance and so on. * @return a list of SDataInstance objects * @throws SDataInstanceException */ List getDataInstances(final List dataNames, final long containerId, final String containerType, final ParentContainerResolver parentContainerResolver) throws SDataInstanceException; /** * Get all dataInstances visible in the specific container * * @param containerId * Identifier of container * @param containerType * Type of container, e.g process instance, activity instance and so on. * @return * @throws SDataInstanceException */ List getDataInstances(final long containerId, final String containerType, final ParentContainerResolver parentContainerResolver, final int fromIndex, final int numberOfResults) throws SDataInstanceException; /** * Get the local dataInstance by name in a certain container, the dataInstance is existed in this container * * @param dataName * Name of dataInstance * @param containerId * Identifier of container * @param containerType * Type of container, e.g process instance, activity instance and so on. * @return an SDataInstance object * @throws SDataInstanceException */ SDataInstance getLocalDataInstance(String dataName, long containerId, String containerType) throws SDataInstanceException; /** * Get a list of local dataInstances for the specific container, those dataInstances must belong to the specified * container. This method is paginated. * * @param containerId * Identifier of container * @param containerType * Type of container, e.g process instance, activity instance and so on. * @return a list of SDataInstance objects * @throws SDataInstanceException */ List getLocalDataInstances(long containerId, String containerType, int fromIndex, int numberOfResults) throws SDataInstanceException; /** * Get SADataInstance object for specific dataInstance at the specific time * * @param sourceObjectId * Identifier of data instance which has been archived * @param time * The archive time * @return an SADataInstance object * @throws SDataInstanceException */ SADataInstance getSADataInstance(long sourceObjectId, long time) throws SDataInstanceException; /** * Get SADataInstance object archived in the specific time for name specified dataInstance in a container * * @param containerId * Identifier of container * @param containerType * Type of container, e.g process instance, activity instance and so on. * @param dataName * Name of data instance * @param time * The archive time * @return an SADataInstance object * @throws SDataInstanceException */ SADataInstance getSADataInstance(long containerId, String containerType, final ParentContainerResolver parentContainerResolver, String dataName, long time) throws SDataInstanceException; /** * Get all SADataInstance objects archived after specific time for specific dataInstance in a container * * @param containerId * Identifier of container * @param containerType * Type of container, e.g process instance, activity instance and so on. * @param dataNames * Name of data * @param time * The archive time * @return a list of SADataInstance objects * @throws SDataInstanceException */ List getSADataInstances(long containerId, String containerType, final ParentContainerResolver parentContainerResolver, List dataNames, long time) throws SDataInstanceException; /** * Get number of dataInstance for specified container * * @param containerId * Identifier of container * @param containerType * Type of container, e.g process instance, activity instance and so on. * @return the number of dataInstances * @throws SDataInstanceException */ long getNumberOfDataInstances(long containerId, String containerType, final ParentContainerResolver parentContainerResolver) throws SDataInstanceException; /** * Gets the last archived SADataInstance object for the named data in the container. * * @param dataName * the name of the data * @param containerId * the identifier of the container * @param containerType * the type of the container * @return the last archived SADataInstance * @throws SDataInstanceException */ SADataInstance getLastSADataInstance(String dataName, long containerId, String containerType, final ParentContainerResolver parentContainerResolver) throws SDataInstanceException; /** * Gets the last archived SADataInstance objects of the container. * * @param containerId * the identifier of the container * @param containerType * the type of the container * @param startIndex * @param maxResults * @return the last archived SADataInstance * @throws SDataInstanceException */ List getLastLocalSADataInstances(long containerId, String containerType, int startIndex, int maxResults) throws SDataInstanceException; /** * Get the local SADataInstances for this element * * @param containerId * @param containerType * @param fromIndex * @param maxResults * @return * @throws SDataInstanceException */ List getLocalSADataInstances(long containerId, String containerType, int fromIndex, int maxResults) throws SDataInstanceException; /** * Delete all local archived data instances for a specified container * * @param containerId * @param dataInstanceContainerType * @throws SDataInstanceException * @since 6.1 */ void deleteLocalArchivedDataInstances(long containerId, String dataInstanceContainerType) throws SDataInstanceException; /** * Delete all local archived data instances for multiple containers having the same type * * @param containerIds containers * @param dataInstanceContainerType type of the containers * @throws SDataInstanceException * @since 7.8 */ void deleteLocalArchivedDataInstances(List containerIds, String dataInstanceContainerType) throws SDataInstanceException; /** * Delete all local active data instances for a specified container * * @param containerId * @param dataInstanceContainerType * @param dataPresent * @throws SDataInstanceException * @since 6.1 */ void deleteLocalDataInstances(long containerId, String dataInstanceContainerType, boolean dataPresent) throws SDataInstanceException; } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/api/ParentContainerResolver.java ================================================ /** * 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.data.instance.api; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.commons.exceptions.SObjectReadException; public interface ParentContainerResolver { List getContainerHierarchy(final DataContainer currentContainer) throws SObjectNotFoundException, SObjectReadException; List getArchivedContainerHierarchy(final DataContainer currentContainer) throws SObjectNotFoundException, SObjectReadException; } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/api/impl/ArchivedDataInContainersComparator.java ================================================ /** * 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.data.instance.api.impl; import java.util.Comparator; import java.util.List; import org.bonitasoft.engine.data.instance.api.DataContainer; import org.bonitasoft.engine.data.instance.model.archive.SADataInstance; /** * The purpose of the comparator is to order data in a list starting with the most recent one in the 'closer' container *

      * a container is closer than an other one if it is defined before in the container hierarchy. *

      * we first order on containers then in a same containers 2 data are ordered using their archive dates. *

      * if sorted using this comparator, a list will have as its first element the most recent data in the highest container. * This is useful in order to get the last archive version of a data in a current context. * * @author Baptiste Mesta */ class ArchivedDataInContainersComparator implements Comparator { private final List containerHierarchy; ArchivedDataInContainersComparator(List containerHierarchy) { this.containerHierarchy = containerHierarchy; } @Override public int compare(SADataInstance data1, SADataInstance data2) { final DataContainer data1Container = new DataContainer(data1.getContainerId(), data1.getContainerType()); final DataContainer data2Container = new DataContainer(data2.getContainerId(), data2.getContainerType()); if (areInTheSameContainer(data1Container, data2Container)) { return compareUsingArchiveDateInverted(data1, data2); } return compareUsingContainersOrder(data1Container, data2Container); } private int compareUsingContainersOrder(DataContainer o1Container, DataContainer o2Container) { return Integer.compare(containerHierarchy.indexOf(o1Container), containerHierarchy.indexOf(o2Container)); } private int compareUsingArchiveDateInverted(SADataInstance o1, SADataInstance o2) { return Long.compare(o2.getArchiveDate(), o1.getArchiveDate()); } private boolean areInTheSameContainer(DataContainer o1Container, DataContainer o2Container) { return containerHierarchy.indexOf(o1Container) == containerHierarchy.indexOf(o2Container); } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/api/impl/DataInContainersComparator.java ================================================ /** * 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.data.instance.api.impl; import java.util.Comparator; import java.util.List; import org.bonitasoft.engine.data.instance.api.DataContainer; import org.bonitasoft.engine.data.instance.model.SDataInstance; /** * The purpose of the comparator is to order data in a list starting with the one in the 'closer' container *

      * a container is closer than an other one if it is defined before in the container hierarchy. *

      * if sorted using this comparator, a list will have as its first element the data in the closer container. * This is useful in order to get the data in a current context with scope shadowing. * * @author Baptiste Mesta */ class DataInContainersComparator implements Comparator { private final List containerHierarchy; public DataInContainersComparator(List containerHierarchy) { this.containerHierarchy = containerHierarchy; } @Override public int compare(SDataInstance o1, SDataInstance o2) { final DataContainer o1Container = new DataContainer(o1.getContainerId(), o1.getContainerType()); final DataContainer o2Container = new DataContainer(o2.getContainerId(), o2.getContainerType()); return containerHierarchy.indexOf(o1Container) - containerHierarchy.indexOf(o2Container); } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/api/impl/DataInstanceServiceImpl.java ================================================ /** * 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.data.instance.api.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.commons.CollectionUtil; import org.bonitasoft.engine.commons.LogUtil; import org.bonitasoft.engine.commons.NullCheckingUtil; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.commons.exceptions.SObjectReadException; import org.bonitasoft.engine.data.instance.api.DataContainer; import org.bonitasoft.engine.data.instance.api.DataInstanceService; import org.bonitasoft.engine.data.instance.api.ParentContainerResolver; import org.bonitasoft.engine.data.instance.exception.SCreateDataInstanceException; import org.bonitasoft.engine.data.instance.exception.SDataInstanceException; import org.bonitasoft.engine.data.instance.exception.SDataInstanceNotFoundException; import org.bonitasoft.engine.data.instance.exception.SDataInstanceReadException; import org.bonitasoft.engine.data.instance.exception.SDeleteDataInstanceException; import org.bonitasoft.engine.data.instance.exception.SUpdateDataInstanceException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SADataInstance; import org.bonitasoft.engine.data.instance.model.archive.builder.SADataInstanceBuilder; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; /** * General mechanism for lookup is to look in specific flow node to search a data instance. When referring to "local" * data instance, it means the lookup is * performed only on the specific element, and not on inherited data for parent containers. * * @author Zhao Na * @author Elias Ricken de Medeiros * @author Feng Hui * @author Hongwen Zang * @author Matthieu Chaffotte * @author Baptiste Mesta: include data instance data source directly here */ @Slf4j public class DataInstanceServiceImpl implements DataInstanceService { private static final String DATA_INSTANCE = "DATA_INSTANCE"; protected final Recorder recorder; protected final ReadPersistenceService persistenceService; protected final ArchiveService archiveService; public DataInstanceServiceImpl(final Recorder recorder, final ReadPersistenceService persistenceService, final ArchiveService archiveService) { this.recorder = recorder; this.persistenceService = persistenceService; this.archiveService = archiveService; } private void archiveDataInstance(final SDataInstance sDataInstance) throws SDataInstanceException { if (!sDataInstance.isTransientData()) { try { final SADataInstance saDataInstance = new SADataInstanceBuilder().createNewInstance(sDataInstance); final ArchiveInsertRecord archiveInsertRecord = new ArchiveInsertRecord(saDataInstance); archiveService.recordInsert(System.currentTimeMillis(), archiveInsertRecord); } catch (final SRecorderException e) { logOnExceptionMethod("updateDataInstance", e); throw new SDataInstanceException("Unable to create SADataInstance", e); } } } @Override public SDataInstance getDataInstance(final String dataName, final long containerId, final String containerType, final ParentContainerResolver parentContainerResolver) throws SDataInstanceException { NullCheckingUtil.checkArgsNotNull(dataName, containerType); final String queryName = "getDataInstancesWithNames"; final Map inputParameters = new HashMap(); inputParameters.put("dataNames", Collections.singletonList(dataName)); final List dataInstances = getSDatainstanceOfContainers(containerId, containerType, parentContainerResolver, queryName, inputParameters); if (dataInstances.size() == 0) { throw new SDataInstanceNotFoundException( "DataInstance with name not found: [name: " + dataName + ", container type: " + containerType + ", container id: " + containerId + ']'); } else if (dataInstances.size() > 1) { //should never happen but in case... throw new SDataInstanceReadException( "Several data have been retrieved for: [name: " + dataName + ", container type: " + containerType + ", container id: " + containerId + ']'); } else { return dataInstances.get(0); } } @Override public List getDataInstances(final long containerId, final String containerType, final ParentContainerResolver parentContainerResolver, final int fromIndex, final int numberOfResults) throws SDataInstanceException { NullCheckingUtil.checkArgsNotNull(containerType); final String queryName = "getDataInstances"; final Map inputParameters = new HashMap(); final List dataInstances = getSDatainstanceOfContainers(containerId, containerType, parentContainerResolver, queryName, inputParameters); //apply pagination here because we could not do the request only in database cause of a post data's processing to remove duplicate variable final int startIndex = Math.max(0, fromIndex); final int toIndex = Math.min(dataInstances.size(), fromIndex + numberOfResults); if (toIndex > startIndex) { return dataInstances.subList(startIndex, toIndex); } return Collections.emptyList(); } private Map> buildContainersMap(final List containerHierarchy, final Map inputParameters) { final Map> containers = new HashMap>(); for (DataContainer container : containerHierarchy) { final String containerTypeKey = container.getType(); if (!containers.containsKey(containerTypeKey)) { containers.put(containerTypeKey, new ArrayList()); inputParameters.put("containerType" + containers.size(), containerTypeKey); inputParameters.put("containerType" + containers.size() + "Ids", containers.get(containerTypeKey)); } containers.get(containerTypeKey).add(container.getId()); } return containers; } private List getSDatainstanceOfContainers(long containerId, String containerType, ParentContainerResolver parentContainerResolver, String queryName, Map inputParameters) throws SDataInstanceNotFoundException, SDataInstanceReadException { //getAllContainers from me to root final List containerHierarchy; try { containerHierarchy = parentContainerResolver .getContainerHierarchy(new DataContainer(containerId, containerType)); } catch (SObjectNotFoundException | SObjectReadException e) { throw new SDataInstanceNotFoundException(e); } final Map> containers = buildContainersMap(containerHierarchy, inputParameters); //get all data of any possible containers List dataInstances; try { dataInstances = persistenceService.selectList( new SelectListDescriptor(getDynamicContainersQueryName(queryName, containers.size()), inputParameters, SDataInstance.class, new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS))); } catch (final SBonitaReadException e) { throw new SDataInstanceReadException("Unable to check if a data instance already exists: " + e.getMessage(), e); } //order the retrieved list by container level Collections.sort(dataInstances, new DataInContainersComparator(containerHierarchy)); //remove duplicates Set alreadyUsedNames = new HashSet(); final Iterator it = dataInstances.iterator(); while (it.hasNext()) { SDataInstance current = it.next(); if (alreadyUsedNames.contains(current.getName())) { it.remove(); } else { alreadyUsedNames.add(current.getName()); } } return dataInstances; } @Override public SDataInstance getLocalDataInstance(final String dataName, final long containerId, final String containerType) throws SDataInstanceReadException { final SDataInstance dataInstance = internalGetLocalDataInstance(dataName, containerId, containerType); if (dataInstance == null) { throw new SDataInstanceReadException("No data instance found"); } return dataInstance; } private SDataInstance internalGetLocalDataInstance(final String dataName, final long containerId, final String containerType) throws SDataInstanceReadException { NullCheckingUtil.checkArgsNotNull(dataName, containerType); final Map paraMap = CollectionUtil.buildSimpleMap(SDataInstance.NAME, dataName); paraMap.put(SDataInstance.CONTAINER_ID, containerId); paraMap.put(SDataInstance.CONTAINER_TYPE, containerType); try { return persistenceService .selectOne(new SelectOneDescriptor("getDataInstancesByNameAndContainer", paraMap, SDataInstance.class, SDataInstance.class)); } catch (final SBonitaReadException e) { throw new SDataInstanceReadException("Unable to check if a data instance already exists: " + e.getMessage(), e); } } @Override public List getLocalDataInstances(final long containerId, final String containerType, final int fromIndex, final int numberOfResults) throws SDataInstanceReadException { NullCheckingUtil.checkArgsNotNull(containerType); final Map paraMap = CollectionUtil.buildSimpleMap(SDataInstance.CONTAINER_ID, containerId); final OrderByOption orderByOption = new OrderByOption(SDataInstance.class, SDataInstance.ID, OrderByType.ASC); paraMap.put(SDataInstance.CONTAINER_TYPE, containerType); try { return persistenceService.selectList(new SelectListDescriptor("getDataInstancesByContainer", paraMap, SDataInstance.class, SDataInstance.class, new QueryOptions(fromIndex, numberOfResults, Arrays.asList(orderByOption)))); } catch (final SBonitaReadException e) { throw new SDataInstanceReadException( "Unable to check if a data instance already exists for the data container of type " + containerType + " with id " + containerId + " for reason: " + e.getMessage(), e); } } private List getSADatainstanceOfContainers(long containerId, String containerType, ParentContainerResolver parentContainerResolver, String queryName, Map inputParameters) throws SDataInstanceReadException, SObjectReadException, SObjectNotFoundException, SBonitaReadException { //getAllContainers from me to root final List containerHierarchy; containerHierarchy = parentContainerResolver .getArchivedContainerHierarchy(new DataContainer(containerId, containerType)); final Map> containers = buildContainersMap(containerHierarchy, inputParameters); //get all data of any of th possible containers List dataInstances; dataInstances = persistenceService.selectList(new SelectListDescriptor( getDynamicContainersQueryName(queryName, containers.size()), inputParameters, SADataInstance.class, new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS))); //order the retrieved list by container level and by archive date Collections.sort(dataInstances, new ArchivedDataInContainersComparator(containerHierarchy)); //remove duplicates Set alreadyUsedNames = new HashSet<>(); final Iterator it = dataInstances.iterator(); while (it.hasNext()) { SADataInstance current = it.next(); if (alreadyUsedNames.contains(current.getName())) { it.remove(); } else { alreadyUsedNames.add(current.getName()); } } return dataInstances; } private String getDynamicContainersQueryName(String queryName, long nbOfContainers) { return queryName + "Of" + nbOfContainers + "Containers"; } @Override public SADataInstance getSADataInstance(final long containerId, final String containerType, final ParentContainerResolver parentContainerResolver, final String dataName, final long time) throws SDataInstanceReadException { logBeforeMethod("getSADataInstance"); final String queryName = "getArchivedDataInstancesWithNames"; final Map inputParameters = new HashMap<>(); inputParameters.put("dataNames", Collections.singletonList(dataName)); inputParameters.put("time", time); final List dataInstances; try { dataInstances = getSADatainstanceOfContainers(containerId, containerType, parentContainerResolver, queryName, inputParameters); } catch (SObjectReadException | SObjectNotFoundException | SBonitaReadException e) { throw new SDataInstanceReadException(e); } if (dataInstances.size() == 0) { throw new SDataInstanceReadException( "DataInstance with name not found: [name: " + dataName + ", container type: " + containerType + ", container id: " + containerId + ']'); } else { return dataInstances.get(0); } } @Override public SADataInstance getSADataInstance(final long sourceObjectId, final long time) throws SDataInstanceReadException { logBeforeMethod("getSADataInstance"); try { final ReadPersistenceService readPersistenceService = archiveService .getDefinitiveArchiveReadPersistenceService(); final Map parameters = new HashMap(2); parameters.put("dataInstanceId", sourceObjectId); parameters.put("time", time); final SADataInstance saDataInstance = readPersistenceService .selectOne(new SelectOneDescriptor( "getSADataInstanceByDataInstanceIdAndArchiveDate", parameters, SADataInstance.class)); logAfterMethod("getSADataInstance"); return saDataInstance; } catch (final SBonitaReadException e) { logOnExceptionMethod("getSADataInstance", e); throw new SDataInstanceReadException("Unable to read SADataInstance", e); } } @Override public SADataInstance getLastSADataInstance(final String dataName, final long containerId, final String containerType, final ParentContainerResolver parentContainerResolver) throws SDataInstanceException { logBeforeMethod("getLastSADataInstance"); SADataInstance saDataInstance = getSADataInstance(containerId, containerType, parentContainerResolver, dataName, System.currentTimeMillis()); if (saDataInstance == null) { throw new SDataInstanceNotFoundException("No archived data instance found for data:" + dataName + " in container: " + containerType + " " + containerId); } return saDataInstance; } @Override public List getSADataInstances(final long containerId, final String containerType, final ParentContainerResolver parentContainerResolver, final List dataNames, final long time) throws SDataInstanceReadException { logBeforeMethod("getSADataInstances"); if (dataNames.isEmpty()) { return Collections.emptyList(); } final String queryName = "getArchivedDataInstancesWithNames"; final Map inputParameters = new HashMap(); inputParameters.put("time", time); inputParameters.put("dataNames", dataNames); try { return getSADatainstanceOfContainers(containerId, containerType, parentContainerResolver, queryName, inputParameters); } catch (SObjectReadException | SObjectNotFoundException | SBonitaReadException e) { throw new SDataInstanceReadException(e); } } @Override public List getLastLocalSADataInstances(final long containerId, final String containerType, final int startIndex, final int maxResults) throws SDataInstanceReadException { logBeforeMethod("getLastLocalSADataInstances"); try { final ReadPersistenceService readPersistenceService = archiveService .getDefinitiveArchiveReadPersistenceService(); final Map parameters = new HashMap(2); parameters.put("containerId", containerId); parameters.put("containerType", containerType); final List saDataInstances = readPersistenceService .selectList(new SelectListDescriptor( "getLastLocalSADataInstances", parameters, SADataInstance.class, new QueryOptions(startIndex, maxResults))); logAfterMethod("getLastLocalSADataInstances"); return saDataInstances; } catch (final SBonitaReadException e) { logOnExceptionMethod("getLastLocalSADataInstances", e); throw new SDataInstanceReadException("Unable to read SADataInstance", e); } } @Override public long getNumberOfDataInstances(final long containerId, final String containerType, final ParentContainerResolver parentContainerResolver) throws SDataInstanceReadException { logBeforeMethod("getNumberOfDataInstances"); final List dataInstances; try { dataInstances = getDataInstances(containerId, containerType, parentContainerResolver, 0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS); } catch (SDataInstanceException e) { throw new SDataInstanceReadException(e); } logAfterMethod("getNumberOfDataInstances"); return dataInstances.size(); } @Override public List getDataInstances(final List dataNames, final long containerId, final String containerType, final ParentContainerResolver parentContainerResolver) throws SDataInstanceException { logBeforeMethod("getDataInstances"); NullCheckingUtil.checkArgsNotNull(dataNames, containerType); if (dataNames.isEmpty()) { return Collections.emptyList(); } final String queryName = "getDataInstancesWithNames"; final Map inputParameters = new HashMap(); inputParameters.put("dataNames", dataNames); return getSDatainstanceOfContainers(containerId, containerType, parentContainerResolver, queryName, inputParameters); } @Override public List getLocalSADataInstances(final long containerId, final String containerType, final int fromIndex, final int numberOfResults) throws SDataInstanceReadException { logBeforeMethod("getLocalSADataInstances"); try { final ReadPersistenceService readPersistenceService = archiveService .getDefinitiveArchiveReadPersistenceService(); final Map parameters = new HashMap(2); parameters.put("containerId", containerId); parameters.put("containerType", containerType); final List saDataInstances = readPersistenceService .selectList(new SelectListDescriptor("getLocalSADataInstances", parameters, SADataInstance.class, new QueryOptions(fromIndex, numberOfResults))); logAfterMethod("getLocalSADataInstances"); return saDataInstances; } catch (final SBonitaReadException e) { logOnExceptionMethod("getLocalSADataInstances", e); throw new SDataInstanceReadException("Unable to read SADataInstance", e); } } @Override public void deleteLocalArchivedDataInstances(final long containerId, final String containerType) throws SDataInstanceException { HashMap map = new HashMap<>(); map.put("containerId", containerId); map.put("containerType", containerType); try { archiveService.deleteFromQuery("deleteLocalSADataInstances", map); } catch (SRecorderException e) { throw new SDataInstanceException(e); } } @Override public void deleteLocalArchivedDataInstances(List containerIds, final String containerType) throws SDataInstanceException { HashMap map = new HashMap<>(); map.put("containerIds", containerIds); map.put("containerType", containerType); try { archiveService.deleteFromQuery("deleteLocalSADataInstancesOfContainers", map); } catch (SRecorderException e) { throw new SDataInstanceException(e); } } @Override public void deleteLocalDataInstances(final long containerId, final String dataInstanceContainerType, final boolean dataPresent) throws SDataInstanceException { if (dataPresent) { final int deleteBatchSize = 80; List sDataInstances = getLocalDataInstances(containerId, dataInstanceContainerType, 0, deleteBatchSize); while (sDataInstances.size() > 0) { for (final SDataInstance sDataInstance : sDataInstances) { deleteDataInstance(sDataInstance); } sDataInstances = getLocalDataInstances(containerId, dataInstanceContainerType, 0, deleteBatchSize); } } } private void logBeforeMethod(final String methodName) { if (log.isTraceEnabled()) { log.trace(LogUtil.getLogBeforeMethod(this.getClass(), methodName)); } } private void logAfterMethod(final String methodName) { if (log.isTraceEnabled()) { log.trace(LogUtil.getLogAfterMethod(this.getClass(), methodName)); } } private void logOnExceptionMethod(final String methodName, final Exception e) { if (log.isTraceEnabled()) { log.trace(LogUtil.getLogOnExceptionMethod(this.getClass(), methodName, e)); } } @Override public void createDataInstance(final SDataInstance dataInstance) throws SDataInstanceException { try { recorder.recordInsert(new InsertRecord(dataInstance), DATA_INSTANCE); } catch (final SRecorderException e) { throw new SCreateDataInstanceException("Impossible to create data instance.", e); } archiveDataInstance(dataInstance); } @Override public void updateDataInstance(final SDataInstance dataInstance, final EntityUpdateDescriptor descriptor) throws SDataInstanceException { NullCheckingUtil.checkArgsNotNull(dataInstance); try { recorder.recordUpdate(UpdateRecord.buildSetFields(dataInstance, descriptor), DATA_INSTANCE); } catch (final SRecorderException e) { throw new SUpdateDataInstanceException( "Impossible to update data instance '" + dataInstance.getName() + "': " + e.getMessage(), e); } archiveDataInstance(dataInstance); } @Override public void deleteDataInstance(final SDataInstance dataInstance) throws SDataInstanceException { NullCheckingUtil.checkArgsNotNull(dataInstance); try { recorder.recordDelete(new DeleteRecord(dataInstance), DATA_INSTANCE); } catch (final SRecorderException e) { throw new SDeleteDataInstanceException("Impossible to delete data instance", e); } } @Override public SDataInstance getDataInstance(final long dataInstanceId) throws SDataInstanceException { NullCheckingUtil.checkArgsNotNull(dataInstanceId); try { final SelectByIdDescriptor selectDescriptor = new SelectByIdDescriptor( SDataInstance.class, dataInstanceId); final SDataInstance dataInstance = persistenceService.selectById(selectDescriptor); if (dataInstance == null) { throw new SDataInstanceNotFoundException("Cannot get the data instance with id " + dataInstanceId); } return dataInstance; } catch (final SBonitaReadException e) { throw new SDataInstanceReadException("Cannot get the data instance with id " + dataInstanceId, e); } } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/exception/SCreateDataInstanceException.java ================================================ /** * 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.data.instance.exception; /** * @author Elias Ricken de Medeiros */ public class SCreateDataInstanceException extends SDataInstanceException { private static final long serialVersionUID = 3429369002565466683L; public SCreateDataInstanceException(final String message, final Throwable cause) { super(message, cause); } public SCreateDataInstanceException(final String message) { super(message); } public SCreateDataInstanceException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/exception/SDataInstanceException.java ================================================ /** * 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.data.instance.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SDataInstanceException extends SBonitaException { private static final long serialVersionUID = -4274958205096504600L; public SDataInstanceException(final String message, final Throwable cause) { super(message, cause); } public SDataInstanceException(final String message) { super(message); } public SDataInstanceException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/exception/SDataInstanceNotFoundException.java ================================================ /** * 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.data.instance.exception; /** * @author Elias Ricken de Medeiros */ public class SDataInstanceNotFoundException extends SDataInstanceException { private static final long serialVersionUID = 8088523217807814937L; public SDataInstanceNotFoundException(final String message, final Throwable cause) { super(message, cause); } public SDataInstanceNotFoundException(final String message) { super(message); } public SDataInstanceNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/exception/SDataInstanceReadException.java ================================================ /** * 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.data.instance.exception; /** * @author Celine Souchet */ public class SDataInstanceReadException extends SDataInstanceException { private static final long serialVersionUID = -4274958205096504600L; public SDataInstanceReadException(final String message, final Throwable cause) { super(message, cause); } public SDataInstanceReadException(final String message) { super(message); } public SDataInstanceReadException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/exception/SDeleteDataInstanceException.java ================================================ /** * 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.data.instance.exception; /** * @author Elias Ricken de Medeiros */ public class SDeleteDataInstanceException extends SDataInstanceException { private static final long serialVersionUID = 4352318021179590101L; public SDeleteDataInstanceException(final String message, final Throwable cause) { super(message, cause); } public SDeleteDataInstanceException(final String message) { super(message); } public SDeleteDataInstanceException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/exception/SUpdateDataInstanceException.java ================================================ /** * 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.data.instance.exception; /** * @author Elias Ricken de Medeiros */ public class SUpdateDataInstanceException extends SDataInstanceException { private static final long serialVersionUID = 5535287890003619850L; public SUpdateDataInstanceException(final String message, final Throwable cause) { super(message, cause); } public SUpdateDataInstanceException(final String message) { super(message); } public SUpdateDataInstanceException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SBlobDataInstance.java ================================================ /** * 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.data.instance.model; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.hibernate.annotations.Type; /** * @author Zhao Na * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SBlobDataInstanceImpl") public final class SBlobDataInstance extends SDataInstance { @Column(name = "blobValue") @Type(type = "materialized_blob") private byte[] value; public SBlobDataInstance(final SDataDefinition dataDefinition) { super(dataDefinition); } @Override public void setValue(final Serializable value) { this.value = (byte[]) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SBooleanDataInstance.java ================================================ /** * 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.data.instance.model; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.definition.model.SDataDefinition; /** * @author Zhao Na * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SBooleanDataInstanceImpl") public class SBooleanDataInstance extends SDataInstance { @Column(name = "booleanValue") private Boolean value; public SBooleanDataInstance(final SDataDefinition dataDefinition) { super(dataDefinition); } @Override public void setValue(final Serializable value) { this.value = (Boolean) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SDataInstance.java ================================================ /** * 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.data.instance.model; import java.io.Serializable; import javax.persistence.DiscriminatorColumn; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.instance.model.exceptions.SDataInstanceNotWellFormedException; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @SuperBuilder @Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "DISCRIMINANT") @Table(name = "data_instance") public abstract class SDataInstance implements PersistentObject { public static final String ID = "id"; public static final String NAME = "name"; public static final String DESCRIPTION = "description"; public static final String VALUE = "value"; public static final String CONTAINER_ID = "containerId"; public static final String CONTAINER_TYPE = "containerType"; @Id private long id; private String name; private String description; private boolean transientData; private String className; private long containerId; private String containerType; protected SDataInstance(final SDataDefinition dataDefinition) { name = dataDefinition.getName(); description = dataDefinition.getDescription(); transientData = dataDefinition.isTransientData(); className = dataDefinition.getClassName(); } public abstract void setValue(Serializable value); public abstract Serializable getValue(); public Boolean isTransientData() { return transientData; } public void setDataTypeClassName(final String className) { this.className = className; } /** * Check if the data is well formed * * @throws org.bonitasoft.engine.data.instance.model.exceptions.SDataInstanceNotWellFormedException * thrown if the data is not well formed */ public void validate() throws SDataInstanceNotWellFormedException { } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SDataInstanceBuilder.java ================================================ /** * 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.data.instance.model; import java.io.Serializable; import java.util.Date; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.definition.model.STextDataDefinition; import org.bonitasoft.engine.data.definition.model.SXMLDataDefinition; import org.bonitasoft.engine.data.instance.model.exceptions.SDataInstanceNotWellFormedException; /** * @author Zhao Na */ public class SDataInstanceBuilder { private final SDataInstance dataInstance; public static SDataInstanceBuilder createNewInstance(final SDataDefinition dataDefinition) { final String className = dataDefinition.getClassName(); SDataInstance dataInstance; if (dataDefinition instanceof STextDataDefinition) { dataInstance = getTextDataInstance((STextDataDefinition) dataDefinition); } else if (dataDefinition instanceof SXMLDataDefinition) { dataInstance = new SXMLDataInstance((SXMLDataDefinition) dataDefinition); } else { if (Integer.class.getName().equals(className)) { dataInstance = new SIntegerDataInstance(dataDefinition); } else if (Long.class.getName().equals(className)) { dataInstance = new SLongDataInstance(dataDefinition); } else if (String.class.getName().equals(className)) { dataInstance = new SShortTextDataInstance(dataDefinition); } else if (Boolean.class.getName().equals(className)) { dataInstance = new SBooleanDataInstance(dataDefinition); } else if (Double.class.getName().equals(className)) { dataInstance = new SDoubleDataInstance(dataDefinition); } else if (Float.class.getName().equals(className)) { dataInstance = new SFloatDataInstance(dataDefinition); } else if (byte[].class.getName().equals(className)) { dataInstance = new SBlobDataInstance(dataDefinition); } else if (Date.class.getName().equals(className)) { dataInstance = new SDateDataInstance(dataDefinition); } else { dataInstance = new SXMLObjectDataInstance(dataDefinition); } } return new SDataInstanceBuilder(dataInstance); } public static SDataInstance createNewInstance(final SDataDefinition dataDefinition, long containerId, String containerType, Serializable value) throws SDataInstanceNotWellFormedException { return createNewInstance(dataDefinition).setContainerId(containerId).setContainerType(containerType) .setValue(value).done(); } private static SDataInstance getTextDataInstance(final STextDataDefinition dataDefinition) { SDataInstance dataInstance; if (dataDefinition.isLongText()) { dataInstance = new SLongTextDataInstance(dataDefinition); } else { dataInstance = new SShortTextDataInstance(dataDefinition); } dataInstance.setValue(null); return dataInstance; } private SDataInstanceBuilder(final SDataInstance dataInstance) { super(); this.dataInstance = dataInstance; } public SDataInstanceBuilder setValue(final Serializable value) { dataInstance.setValue(value); return this; } public SDataInstanceBuilder setContainerId(final long containerId) { dataInstance.setContainerId(containerId); return this; } public SDataInstanceBuilder setContainerType(final String containerType) { dataInstance.setContainerType(containerType); return this; } public SDataInstance done() throws SDataInstanceNotWellFormedException { dataInstance.validate(); return dataInstance; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SDateDataInstance.java ================================================ /** * 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.data.instance.model; import java.io.Serializable; import java.util.Date; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.hibernate.annotations.Type; /** * @author Zhao Na * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SDateDataInstanceImpl") public class SDateDataInstance extends SDataInstance { @Column(name = "longValue") @Type(type = "org.bonitasoft.engine.persistence.DateStoredAsLongUserType") private Date value; public SDateDataInstance(final SDataDefinition dataDefinition) { super(dataDefinition); } @Override public void setValue(final Serializable value) { this.value = (Date) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SDoubleDataInstance.java ================================================ /** * 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.data.instance.model; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.definition.model.SDataDefinition; /** * @author Zhao Na * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SDoubleDataInstanceImpl") public class SDoubleDataInstance extends SDataInstance { @Column(name = "doubleValue") private Double value; public SDoubleDataInstance(final SDataDefinition dataDefinition) { super(dataDefinition); } @Override public void setValue(final Serializable value) { this.value = (Double) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SFloatDataInstance.java ================================================ /** * 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.data.instance.model; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.definition.model.SDataDefinition; /** * @author Celine Souchet * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SFloatDataInstanceImpl") public class SFloatDataInstance extends SDataInstance { @Column(name = "floatValue") private Float value; public SFloatDataInstance(final SDataDefinition dataDefinition) { super(dataDefinition); } @Override public void setValue(final Serializable value) { this.value = (Float) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SIntegerDataInstance.java ================================================ /** * 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.data.instance.model; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.definition.model.SDataDefinition; /** * @author Zhao Na * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SIntegerDataInstanceImpl") public class SIntegerDataInstance extends SDataInstance { @Column(name = "intValue") private Integer value; public SIntegerDataInstance(final SDataDefinition dataDefinition) { super(dataDefinition); } @Override public Integer getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = (Integer) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SLongDataInstance.java ================================================ /** * 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.data.instance.model; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.definition.model.SDataDefinition; /** * @author Zhao Na * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SLongDataInstanceImpl") public class SLongDataInstance extends SDataInstance { @Column(name = "longValue") private Long value; public SLongDataInstance(final SDataDefinition dataDefinition) { super(dataDefinition); } @Override public void setValue(final Serializable value) { this.value = (Long) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SLongTextDataInstance.java ================================================ /** * 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.data.instance.model; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.hibernate.annotations.Type; /** * @author Zhao Na * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SLongTextDataInstanceImpl") public class SLongTextDataInstance extends SDataInstance { @Column(name = "clobValue") @Type(type = "materialized_clob") private String value; public SLongTextDataInstance(final SDataDefinition dataDefinition) { super(dataDefinition); } @Override public void setValue(final Serializable value) { this.value = (String) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SShortTextDataInstance.java ================================================ /** * 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.data.instance.model; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.instance.model.exceptions.SDataInstanceNotWellFormedException; /** * @author Zhao Na * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SShortTextDataInstanceImpl") public class SShortTextDataInstance extends SDataInstance { public static final int MAX_LENGTH = 255; @Column(name = "shortTextValue") private String value; public SShortTextDataInstance(final SDataDefinition dataDefinition) { super(dataDefinition); } @Override public void setValue(final Serializable value) { this.value = (String) value; } @Override public void validate() throws SDataInstanceNotWellFormedException { if (getValue() != null && getValue().length() > MAX_LENGTH) { throw new SDataInstanceNotWellFormedException( "Data " + getName() + " must not be longer than " + MAX_LENGTH + " characters"); } } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SXMLDataInstance.java ================================================ /** * 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.data.instance.model; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.definition.model.SXMLDataDefinition; import org.hibernate.annotations.Type; /** * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SXMLDataInstanceImpl") public class SXMLDataInstance extends SDataInstance { @Column(name = "clobValue") @Type(type = "materialized_clob") private String value; @Column private String namespace; @Column private String element; public SXMLDataInstance(final SXMLDataDefinition dataDefinition) { super(dataDefinition); namespace = dataDefinition.getNamespace(); element = dataDefinition.getElement(); } @Override public void setValue(final Serializable value) { this.value = (String) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/SXMLObjectDataInstance.java ================================================ /** * 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.data.instance.model; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.instance.model.impl.XStreamFactory; import org.hibernate.annotations.Type; /** * @author Matthieu Chaffotte * @author Celine Souchet */ @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SXMLObjectDataInstanceImpl") public final class SXMLObjectDataInstance extends SDataInstance { @Column(name = "clobValue") @Type(type = "materialized_clob") private String value; public SXMLObjectDataInstance(final SDataDefinition dataDefinition) { super(dataDefinition); } @Override public Serializable getValue() { return revert(value); } @Override public void setValue(final Serializable value) { this.value = convert(value); } private String convert(final Serializable value) { return XStreamFactory.getXStream().toXML(value); } private Serializable revert(final String value) { if (value != null) { return (Serializable) XStreamFactory.getXStream().fromXML(value); } return null; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SABlobDataInstance.java ================================================ /** * 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.data.instance.model.archive; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.persistence.PersistentObject; import org.hibernate.annotations.Type; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SABlobDataInstanceImpl") public class SABlobDataInstance extends SADataInstance { @Column(name = "blobValue") @Type(type = "materialized_blob") private byte[] value; public SABlobDataInstance(final SDataInstance sDataInstance) { super(sDataInstance); setValue(sDataInstance.getValue()); } @Override public Serializable getValue() { return revert(value); } @Override public void setValue(final Serializable value) { this.value = convert(value); } @Override public Class getPersistentObjectInterface() { return SDataInstance.class; } private byte[] convert(final Serializable value) { ObjectOutputStream oos = null; try { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(baos); oos.writeObject(value); oos.flush(); return baos.toByteArray(); } catch (final IOException ioe) { throw new SBonitaRuntimeException(ioe); } finally { try { if (oos != null) { oos.close(); } } catch (final IOException ioe) { throw new SBonitaRuntimeException(ioe); } } } private Serializable revert(final byte[] bytes) { if (bytes == null) { return null; } ObjectInputStream ois = null; try { final ByteArrayInputStream bais = new ByteArrayInputStream(bytes); ois = new ObjectInputStream(bais) { @Override protected Class resolveClass(final ObjectStreamClass desc) throws ClassNotFoundException { final String className = desc.getName(); final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); return Class.forName(className, true, classLoader); } }; return (Serializable) ois.readObject(); } catch (final IOException ioe) { throw new SBonitaRuntimeException(ioe); } catch (final ClassNotFoundException cnfe) { throw new SBonitaRuntimeException(cnfe); } finally { if (ois != null) { try { ois.close(); } catch (final IOException ioe) { throw new SBonitaRuntimeException(ioe); } } } } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SABooleanDataInstance.java ================================================ /** * 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.data.instance.model.archive; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.instance.model.SDataInstance; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SABooleanDataInstanceImpl") public class SABooleanDataInstance extends SADataInstance { @Column(name = "booleanValue") private Boolean value; public SABooleanDataInstance(final SDataInstance sDataInstance) { super(sDataInstance); value = (Boolean) sDataInstance.getValue(); } @Override public Boolean getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = (Boolean) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SADataInstance.java ================================================ /** * 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.data.instance.model.archive; import java.io.Serializable; import javax.persistence.DiscriminatorColumn; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.persistence.ArchivedPersistentObject; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @SuperBuilder @Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "DISCRIMINANT") @Table(name = "arch_data_instance") public abstract class SADataInstance implements ArchivedPersistentObject { @Id private long id; private String name; private String description; private boolean transientData; private String className; private long containerId; private String containerType; private long archiveDate; private long sourceObjectId; protected SADataInstance(final SDataInstance sDataInstance) { name = sDataInstance.getName(); description = sDataInstance.getDescription(); transientData = sDataInstance.isTransientData(); className = sDataInstance.getClassName(); containerId = sDataInstance.getContainerId(); containerType = sDataInstance.getContainerType(); sourceObjectId = sDataInstance.getId(); } public abstract Serializable getValue(); public abstract void setValue(Serializable value); @Override public Class getPersistentObjectInterface() { return SDataInstance.class; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SADateDataInstance.java ================================================ /** * 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.data.instance.model.archive; import java.io.Serializable; import java.util.Date; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.hibernate.annotations.Type; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SADateDataInstanceImpl") public class SADateDataInstance extends SADataInstance { @Column(name = "longValue") @Type(type = "org.bonitasoft.engine.persistence.DateStoredAsLongUserType") private Date value; public SADateDataInstance(final SDataInstance sDataInstance) { super(sDataInstance); value = (Date) sDataInstance.getValue(); } @Override public Date getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = (Date) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SADoubleDataInstance.java ================================================ /** * 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.data.instance.model.archive; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.instance.model.SDataInstance; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SADoubleDataInstanceImpl") public class SADoubleDataInstance extends SADataInstance { @Column(name = "doubleValue") private Double value; public SADoubleDataInstance(final SDataInstance sDataInstance) { super(sDataInstance); value = (Double) sDataInstance.getValue(); } @Override public Double getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = (Double) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SAFloatDataInstance.java ================================================ /** * 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.data.instance.model.archive; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.instance.model.SDataInstance; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SAFloatDataInstanceImpl") public class SAFloatDataInstance extends SADataInstance { @Column(name = "floatValue") private Float value; public SAFloatDataInstance(final SDataInstance sDataInstance) { super(sDataInstance); value = (Float) sDataInstance.getValue(); } @Override public Float getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = (Float) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SAIntegerDataInstance.java ================================================ /** * 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.data.instance.model.archive; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.instance.model.SDataInstance; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SAIntegerDataInstanceImpl") public class SAIntegerDataInstance extends SADataInstance { @Column(name = "intValue") private Integer value; public SAIntegerDataInstance(final SDataInstance sDataInstance) { super(sDataInstance); value = (Integer) sDataInstance.getValue(); } @Override public Integer getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = (Integer) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SALongDataInstance.java ================================================ /** * 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.data.instance.model.archive; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.instance.model.SDataInstance; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SALongDataInstanceImpl") public class SALongDataInstance extends SADataInstance { @Column(name = "longValue") private Long value; public SALongDataInstance(final SDataInstance sDataInstance) { super(sDataInstance); value = (Long) sDataInstance.getValue(); } @Override public Long getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = (Long) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SALongTextDataInstance.java ================================================ /** * 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.data.instance.model.archive; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.hibernate.annotations.Type; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SALongTextDataInstanceImpl") public class SALongTextDataInstance extends SADataInstance { @Column(name = "clobValue") @Type(type = "materialized_clob") private String value; public SALongTextDataInstance(final SDataInstance sDataInstance) { super(sDataInstance); value = (String) sDataInstance.getValue(); } @Override public String getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = (String) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SAShortTextDataInstance.java ================================================ /** * 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.data.instance.model.archive; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.instance.model.SDataInstance; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SAShortTextDataInstanceImpl") public class SAShortTextDataInstance extends SADataInstance { @Column(name = "shortTextValue") private String value; public SAShortTextDataInstance(final SDataInstance sDataInstance) { super(sDataInstance); value = (String) sDataInstance.getValue(); } @Override public Serializable getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = (String) value; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SAXMLDataInstance.java ================================================ /** * 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.data.instance.model.archive; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.hibernate.annotations.Type; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SAXMLDataInstanceImpl") public class SAXMLDataInstance extends SADataInstance { @Column(name = "clobValue") @Type(type = "materialized_clob") private String value; private String namespace; private String element; public SAXMLDataInstance(final SDataInstance sDataInstance) { super(sDataInstance); value = (String) sDataInstance.getValue(); } @Override public Serializable getValue() { return value; } @Override public void setValue(final Serializable value) { this.value = (String) value; } public String getNamespace() { return namespace; } public void setNamespace(final String namespace) { this.namespace = namespace; } public String getElement() { return element; } public void setElement(final String element) { this.element = element; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/SAXMLObjectDataInstance.java ================================================ /** * 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.data.instance.model.archive; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.impl.XStreamFactory; import org.hibernate.annotations.Type; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @SuperBuilder @Entity @DiscriminatorValue("SAXMLObjectDataInstanceImpl") public final class SAXMLObjectDataInstance extends SADataInstance { @Column(name = "clobValue") @Type(type = "materialized_clob") private String value; public SAXMLObjectDataInstance(final SDataInstance sDataInstance) { super(sDataInstance); setValue(sDataInstance.getValue()); } @Override public Serializable getValue() { if (value != null) { return (Serializable) XStreamFactory.getXStream().fromXML(value); } return null; } @Override public void setValue(final Serializable value) { this.value = XStreamFactory.getXStream().toXML(value); } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/builder/SADataInstanceBuilder.java ================================================ /** * 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.data.instance.model.archive.builder; import java.util.Date; import org.bonitasoft.engine.data.instance.model.SBlobDataInstance; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.SLongTextDataInstance; import org.bonitasoft.engine.data.instance.model.SShortTextDataInstance; import org.bonitasoft.engine.data.instance.model.SXMLDataInstance; import org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SABlobDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SABooleanDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SADataInstance; import org.bonitasoft.engine.data.instance.model.archive.SADateDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SADoubleDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SAFloatDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SAIntegerDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SALongDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SAShortTextDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SAXMLDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SAXMLObjectDataInstance; public class SADataInstanceBuilder { public SADataInstance createNewInstance(final SDataInstance sDataInstance) { final String className = sDataInstance.getClassName(); SADataInstance saDataInstance = null; if (sDataInstance instanceof SShortTextDataInstance) { saDataInstance = new SAShortTextDataInstance(sDataInstance); } else if (sDataInstance instanceof SLongTextDataInstance) { saDataInstance = new SALongTextDataInstance(sDataInstance); } else if (sDataInstance instanceof SXMLDataInstance) { saDataInstance = new SAXMLDataInstance(sDataInstance); } else if (sDataInstance instanceof SBlobDataInstance) { saDataInstance = new SABlobDataInstance(sDataInstance); } else if (sDataInstance instanceof SXMLObjectDataInstance) { saDataInstance = new SAXMLObjectDataInstance(sDataInstance); } else { if (Integer.class.getName().equals(className)) { saDataInstance = new SAIntegerDataInstance(sDataInstance); } else if (Long.class.getName().equals(className)) { saDataInstance = new SALongDataInstance(sDataInstance); } else if (Boolean.class.getName().equals(className)) { saDataInstance = new SABooleanDataInstance(sDataInstance); } else if (Date.class.getName().equals(className)) { saDataInstance = new SADateDataInstance(sDataInstance); } else if (Double.class.getName().equals(className)) { saDataInstance = new SADoubleDataInstance(sDataInstance); } else if (Float.class.getName().equals(className)) { saDataInstance = new SAFloatDataInstance(sDataInstance); } } return saDataInstance; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/builder/SADataInstanceLogBuilder.java ================================================ /** * 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.data.instance.model.archive.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Feng Hui */ public interface SADataInstanceLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/builder/SADataInstanceLogBuilderFactory.java ================================================ /** * 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.data.instance.model.archive.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Feng Hui */ public interface SADataInstanceLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/builder/impl/SADataInstanceLogBuilderFactoryImpl.java ================================================ /** * 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.data.instance.model.archive.builder.impl; import org.bonitasoft.engine.data.instance.model.archive.builder.SADataInstanceLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Feng Hui * @author Matthieu Chaffotte */ public class SADataInstanceLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SADataInstanceLogBuilderFactory { private static final String SA_DATA_INSTANCE_INDEX_NAME = "numericIndex1"; @Override public String getObjectIdKey() { return SA_DATA_INSTANCE_INDEX_NAME; } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/archive/builder/impl/SADataInstanceLogBuilderImpl.java ================================================ /** * 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.data.instance.model.archive.builder.impl; import org.bonitasoft.engine.data.instance.model.archive.builder.SADataInstanceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Feng Hui * @author Matthieu Chaffotte */ public class SADataInstanceLogBuilderImpl extends CRUDELogBuilder implements SADataInstanceLogBuilder { private static final String SA_DATA_INSTANCE = "SA_DATA_INSTANCE"; private static final int SA_DATA_INSTANCE_INDEX = 0; @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(SA_DATA_INSTANCE_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return SA_DATA_INSTANCE; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(SA_DATA_INSTANCE_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + "data instance Id"); } } } ================================================ FILE: services/bonita-data-instance/src/main/java/org/bonitasoft/engine/data/instance/model/exceptions/SDataInstanceNotWellFormedException.java ================================================ /** * 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.data.instance.model.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * This exception is thrown if a SDataInstance is not well formed. * For example: when a string gets a too long value. * * @Author: Frederic Bouquet */ public class SDataInstanceNotWellFormedException extends SBonitaException { private static final long serialVersionUID = 8447390597638287376L; public SDataInstanceNotWellFormedException(String message) { super(message); } } ================================================ FILE: services/bonita-data-instance/src/main/resources/org/bonitasoft/engine/data/instance/model/impl/hibernate/archived.data.instance.queries.hbm.xml ================================================ SELECT saDataInstance FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS saDataInstance WHERE saDataInstance.sourceObjectId = :dataInstanceId AND :time >= saDataInstance.archiveDate ORDER BY archiveDate DESC SELECT saDataInstance FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS saDataInstance WHERE saDataInstance.sourceObjectId IN (:dataInstanceIds) AND saDataInstance.id IN( SELECT max(sadi.id) FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS sadi WHERE :time >= sadi.archiveDate GROUP BY sadi.sourceObjectId ) SELECT saDataInstance FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS saDataInstance WHERE saDataInstance.sourceObjectId = :dataInstanceId SELECT sa FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS sa WHERE sa.sourceObjectId = :dataInstanceId ORDER BY sa.archiveDate DESC SELECT sa FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS sa WHERE sa.name = :dataName AND sa.containerId = :containerId AND sa.containerType = :containerType ORDER BY sa.archiveDate DESC SELECT sa FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS sa WHERE sa.containerId = :containerId AND sa.containerType = :containerType ORDER BY sa.id SELECT sa FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS sa WHERE sa.containerId = :containerId AND sa.containerType = :containerType AND sa.archiveDate IN ( SELECT MAX(archiveDate) FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance WHERE containerId = :containerId AND containerType = :containerType AND name = sa.name GROUP BY name ) ORDER BY sa.archiveDate DESC SELECT dataInstance FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS dataInstance WHERE dataInstance.name in (:dataNames) AND dataInstance.archiveDate <= :time AND ( dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids) ) SELECT dataInstance FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS dataInstance WHERE dataInstance.name in (:dataNames) AND dataInstance.archiveDate <= :time AND ( dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids) OR dataInstance.containerType = :containerType2 AND dataInstance.containerId in(:containerType2Ids) ) SELECT dataInstance FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS dataInstance WHERE dataInstance.name in (:dataNames) AND dataInstance.archiveDate <= :time AND ( dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids) OR dataInstance.containerType = :containerType2 AND dataInstance.containerId in(:containerType2Ids) OR dataInstance.containerType = :containerType3 AND dataInstance.containerId in(:containerType3Ids) ) DELETE FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS dataInstance WHERE dataInstance.containerType = :containerType AND dataInstance.containerId = :containerId DELETE FROM org.bonitasoft.engine.data.instance.model.archive.SADataInstance AS dataInstance WHERE dataInstance.containerType = :containerType AND dataInstance.containerId IN (:containerIds) ================================================ FILE: services/bonita-data-instance/src/main/resources/org/bonitasoft/engine/data/instance/model/impl/hibernate/data.instance.queries.hbm.xml ================================================ SELECT dataInstance FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance WHERE dataInstance.id IN (:ids) ORDER BY dataInstance.id SELECT dataInstance FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance WHERE dataInstance.name = :name AND dataInstance.containerId = :containerId AND dataInstance.containerType = :containerType SELECT dataInstance FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance WHERE dataInstance.containerId = :containerId AND dataInstance.containerType = :containerType SELECT count(dataInstance.id) FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance WHERE dataInstance.containerId = :containerId AND dataInstance.containerType = :containerType SELECT dataInstance FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance WHERE dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids) SELECT dataInstance FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance WHERE dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids) OR dataInstance.containerType = :containerType2 AND dataInstance.containerId in(:containerType2Ids) SELECT dataInstance FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance WHERE dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids) OR dataInstance.containerType = :containerType2 AND dataInstance.containerId in(:containerType2Ids) OR dataInstance.containerType = :containerType3 AND dataInstance.containerId in(:containerType3Ids) SELECT dataInstance FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance WHERE dataInstance.name in (:dataNames) AND ( dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids) ) SELECT dataInstance FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance WHERE dataInstance.name in (:dataNames) AND ( dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids) OR dataInstance.containerType = :containerType2 AND dataInstance.containerId in(:containerType2Ids) ) SELECT dataInstance FROM org.bonitasoft.engine.data.instance.model.SDataInstance AS dataInstance WHERE dataInstance.name in (:dataNames) AND ( dataInstance.containerType = :containerType1 AND dataInstance.containerId in(:containerType1Ids) OR dataInstance.containerType = :containerType2 AND dataInstance.containerId in(:containerType2Ids) OR dataInstance.containerType = :containerType3 AND dataInstance.containerId in(:containerType3Ids) ) ================================================ FILE: services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/api/impl/ArchivedDataInContainersComparatorTest.java ================================================ /** * 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.data.instance.api.impl; import static org.assertj.core.api.Assertions.assertThat; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.UUID; import org.bonitasoft.engine.data.instance.api.DataContainer; import org.bonitasoft.engine.data.instance.model.archive.SADataInstance; import org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance; import org.junit.Test; /** * @author Baptiste Mesta */ public class ArchivedDataInContainersComparatorTest { private ArchivedDataInContainersComparator comparator = new ArchivedDataInContainersComparator( Arrays.asList(new DataContainer(1L, "SUBTASK"), new DataContainer(2L, "TASK"), new DataContainer(3L, "PROC"))); private DateFormat formatter = new SimpleDateFormat("dd/MM/yy"); private SADataInstance data(long containerId, String containerType, String archivedDate) throws ParseException { SALongTextDataInstance sLongTextDataInstance = new SALongTextDataInstance(); sLongTextDataInstance.setId(UUID.randomUUID().getLeastSignificantBits()); sLongTextDataInstance.setContainerId(containerId); sLongTextDataInstance.setContainerType(containerType); sLongTextDataInstance.setArchiveDate(formatter.parse(archivedDate).getTime()); return sLongTextDataInstance; } @Test public void should_compare_return_0_when_same_date_and_container() throws Exception { //given SADataInstance data1 = data(1L, "SUBTASK", "27/05/1987"); SADataInstance data2 = data(1L, "SUBTASK", "27/05/1987"); //then assertThat(comparator.compare(data1, data2)).isEqualTo(0); assertThat(comparator.compare(data2, data1)).isEqualTo(0); } @Test public void should_sort_in_same_container_return_the_most_recent_first() throws Exception { //given SADataInstance data1 = data(1L, "SUBTASK", "27/05/1987"); SADataInstance data2 = data(1L, "SUBTASK", "28/05/1987"); List list = Arrays.asList(data1, data2); //when Collections.sort(list, comparator); //then assertThat(list).containsExactly(data2, data1); } @Test public void should_compare_very_different_date_in_same_container() throws Exception { //given SADataInstance data1 = data(1L, "SUBTASK", "01/01/0001"); SADataInstance data2 = data(1L, "SUBTASK", "28/05/3000"); List list = Arrays.asList(data1, data2); //when Collections.sort(list, comparator); //then assertThat(list).containsExactly(data2, data1); } @Test public void should_sort_in_different_container_using_same_date_return_closer_data_first() throws Exception { //given SADataInstance data1 = data(1L, "SUBTASK", "27/05/1987"); SADataInstance data2 = data(2L, "TASK", "27/05/1987"); SADataInstance data3 = data(3L, "PROC", "27/05/1987"); List list = Arrays.asList(data3, data1, data2); //when Collections.sort(list, comparator); //then assertThat(list).containsExactly(data1, data2, data3); } } ================================================ FILE: services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/api/impl/DataInContainersComparatorTest.java ================================================ /** * 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.data.instance.api.impl; import static org.assertj.core.api.Assertions.assertThat; import java.text.ParseException; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.UUID; import org.bonitasoft.engine.data.instance.api.DataContainer; import org.bonitasoft.engine.data.instance.model.archive.SADataInstance; import org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance; import org.junit.Test; /** * @author Baptiste Mesta */ public class DataInContainersComparatorTest { private ArchivedDataInContainersComparator comparator = new ArchivedDataInContainersComparator( Arrays.asList(new DataContainer(1L, "SUBTASK"), new DataContainer(2L, "TASK"), new DataContainer(3L, "PROC"))); private SADataInstance data(long containerId, String containerType) throws ParseException { SALongTextDataInstance sLongTextDataInstance = new SALongTextDataInstance(); sLongTextDataInstance.setId(UUID.randomUUID().getLeastSignificantBits()); sLongTextDataInstance.setContainerId(containerId); sLongTextDataInstance.setContainerType(containerType); return sLongTextDataInstance; } @Test public void should_compare_return_0_when_same_container() throws Exception { //given SADataInstance data1 = data(1L, "SUBTASK"); SADataInstance data2 = data(1L, "SUBTASK"); //then assertThat(comparator.compare(data1, data2)).isEqualTo(0); assertThat(comparator.compare(data2, data1)).isEqualTo(0); } @Test public void should_sort_in_different_container_return_closer_data_first() throws Exception { //given SADataInstance data1 = data(1L, "SUBTASK"); SADataInstance data2 = data(2L, "TASK"); SADataInstance data3 = data(3L, "PROC"); List list = Arrays.asList(data3, data1, data2); //when Collections.sort(list, comparator); //then assertThat(list).containsExactly(data1, data2, data3); } } ================================================ FILE: services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/api/impl/DataInstanceServiceImplTest.java ================================================ /** * 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.data.instance.api.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.archive.ArchiveInsertRecord; import org.bonitasoft.engine.archive.ArchiveService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.data.instance.api.DataContainer; import org.bonitasoft.engine.data.instance.api.ParentContainerResolver; import org.bonitasoft.engine.data.instance.exception.SDataInstanceReadException; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.SLongTextDataInstance; import org.bonitasoft.engine.data.instance.model.SShortTextDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SADataInstance; import org.bonitasoft.engine.data.instance.model.archive.SALongTextDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SAShortTextDataInstance; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class DataInstanceServiceImplTest { @Mock private Recorder recorder; @Mock private ReadPersistenceService persistenceService; @Mock private ArchiveService archiveService; @Mock private ParentContainerResolver parentContainerResolver; @Captor private ArgumentCaptor archiveInsertRecordArgumentCaptor; @InjectMocks private DataInstanceServiceImpl dataInstanceServiceImpl; @Test(expected = SDataInstanceReadException.class) public final void should_throw_read_exception_when_persistence_service_has_read_exception() throws SBonitaException { dataInstanceServiceImpl.getLastSADataInstance("kaupunki", 1, "PROCESS_INSTANCE", parentContainerResolver); } @Test public final void getLastSADataInstancesFromContainer() throws SBonitaException { final List archiveInstances = Collections.emptyList(); doReturn(persistenceService).when(archiveService).getDefinitiveArchiveReadPersistenceService(); doReturn(archiveInstances).when(persistenceService) .selectList(ArgumentMatchers.> any()); final List dataInstances = dataInstanceServiceImpl.getLastLocalSADataInstances(1, "PROCESS_INSTANCE", 0, 10); Assert.assertEquals(archiveInstances, dataInstances); } @Test public final void getEmptyLastSADataInstancesFromContainer() throws SBonitaException { doReturn(persistenceService).when(archiveService).getDefinitiveArchiveReadPersistenceService(); doReturn(Collections.emptyList()).when(persistenceService) .selectList(ArgumentMatchers.> any()); final List dataInstances = dataInstanceServiceImpl.getLastLocalSADataInstances(1, "PROCESS_INSTANCE", 0, 10); Assert.assertEquals(Collections.emptyList(), dataInstances); } @Test(expected = SDataInstanceReadException.class) public final void getLastSADataInstancesFromContainerThrowsAnExceptionDueToProblemOnPersistenceService() throws SBonitaException { final List archiveInstances = Collections.emptyList(); doReturn(persistenceService).when(archiveService).getDefinitiveArchiveReadPersistenceService(); doThrow(new SBonitaReadException("moustache")).when(persistenceService) .selectList(ArgumentMatchers.> any()); final List dataInstances = dataInstanceServiceImpl.getLastLocalSADataInstances(1, "PROCESS_INSTANCE", 0, 10); Assert.assertEquals(archiveInstances, dataInstances); } @Test public final void should_archive_the_first_value_of_a_data_when_creating_it() throws Exception { //given SShortTextDataInstance dataInstance = new SShortTextDataInstance(); dataInstance.setValue("theValue"); //when dataInstanceServiceImpl.createDataInstance(dataInstance); //then verify(archiveService).recordInsert(anyLong(), archiveInsertRecordArgumentCaptor.capture()); final SAShortTextDataInstance entity = (SAShortTextDataInstance) archiveInsertRecordArgumentCaptor.getValue() .getEntity(); assertThat(entity.getValue()).isEqualTo("theValue"); } @Test public final void should_archive_new_value_on_data_value_update() throws Exception { //given SShortTextDataInstance dataInstance = new SShortTextDataInstance(); //set directly to "theNewValue" because the set is done by the persistence service dataInstance.setValue("theNewValue"); EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor(); updateDescriptor.addField("value", "theNewValue"); //when dataInstanceServiceImpl.updateDataInstance(dataInstance, updateDescriptor); //then verify(archiveService).recordInsert(anyLong(), archiveInsertRecordArgumentCaptor.capture()); final SAShortTextDataInstance entity = (SAShortTextDataInstance) archiveInsertRecordArgumentCaptor.getValue() .getEntity(); assertThat(entity.getValue()).isEqualTo("theNewValue"); } @Test public void should_return_the_last_version_of_an_archived_data_for_long_running_process() throws Exception { //see bug BS-15990 List dataInstances = new ArrayList<>(); dataInstances.add(createArchDataInstance(1, 66L, "TASK", 1475800000000L, "VALUE1")); dataInstances.add(createArchDataInstance(2, 66L, "TASK", 1478080000000L, "VALUE2")); dataInstances.add(createArchDataInstance(3, 66L, "TASK", 1478081000000L, "VALUE3")); dataInstances.add(createArchDataInstance(4, 66L, "TASK", 1478087736549L, "VALUE4")); doReturn(dataInstances).when(persistenceService).selectList(any(SelectListDescriptor.class)); //order the retrieved list by container level and by archive date SADataInstance dataInstance = dataInstanceServiceImpl.getLastSADataInstance("testData", 1L, "TASK", parentContainerResolver); //should return the last version assertThat(dataInstance.getValue()).isEqualTo("VALUE4"); } @Test public void should_return_the_last_version_of_an_archived_data() throws Exception { //see bug BS-15990 List dataInstances = new ArrayList<>(); dataInstances.add(createArchDataInstance(1, 66L, "TASK", 1475800000000L, "VALUE1")); dataInstances.add(createArchDataInstance(2, 66L, "TASK", 1475800000001L, "VALUE2")); dataInstances.add(createArchDataInstance(3, 66L, "TASK", 1475800000002L, "VALUE3")); dataInstances.add(createArchDataInstance(4, 66L, "TASK", 1475800000003L, "VALUE4")); doReturn(dataInstances).when(persistenceService).selectList(any(SelectListDescriptor.class)); //order the retrieved list by container level and by archive date SADataInstance dataInstance = dataInstanceServiceImpl.getLastSADataInstance("testData", 1L, "TASK", parentContainerResolver); //should return the last version assertThat(dataInstance.getValue()).isEqualTo("VALUE4"); } @Test public void should_return_the_archived_data_in_the_up_most_container() throws Exception { //given List dataInstances = new ArrayList<>(); dataInstances.add(createArchDataInstance(1, 66L, "PROC", 1475800000002L, "PROC_VALUE")); dataInstances.add(createArchDataInstance(2, 67L, "TASK", 1475800000001L, "TASK_VALUE")); dataInstances.add(createArchDataInstance(2, 68L, "SUBTASK", 1475800000000L, "SUBTASK_VALUE")); doReturn(dataInstances).when(persistenceService).selectList(any(SelectListDescriptor.class)); doReturn(Arrays.asList(new DataContainer(68L, "SUBTASK"), new DataContainer(67L, "TASK"), new DataContainer(66L, "PROC"))).when(parentContainerResolver) .getArchivedContainerHierarchy(new DataContainer(68L, "SUBTASK")); //when SADataInstance dataInstance = dataInstanceServiceImpl.getLastSADataInstance("testData", 68L, "SUBTASK", parentContainerResolver); //then assertThat(dataInstance.getValue()).isEqualTo("SUBTASK_VALUE"); } private SALongTextDataInstance createArchDataInstance(long id, long containerId, String containerType, long archiveDate, String value) { SALongTextDataInstance dataInstance = new SALongTextDataInstance(); dataInstance.setId(id); dataInstance.setContainerId(containerId); dataInstance.setContainerType(containerType); dataInstance.setArchiveDate(archiveDate); dataInstance.setValue(value); return dataInstance; } private SLongTextDataInstance createDataInstance(long id, long containerId, String containerType, String value) { SLongTextDataInstance dataInstance = new SLongTextDataInstance(); dataInstance.setId(id); dataInstance.setContainerId(containerId); dataInstance.setContainerType(containerType); dataInstance.setValue(value); return dataInstance; } @Test public void should_return_the_data_in_the_up_most_container() throws Exception { //given List dataInstances = new ArrayList<>(); dataInstances.add(createDataInstance(1, 66L, "PROC", "PROC_VALUE")); dataInstances.add(createDataInstance(2, 67L, "TASK", "TASK_VALUE")); dataInstances.add(createDataInstance(2, 68L, "SUBTASK", "SUBTASK_VALUE")); doReturn(dataInstances).when(persistenceService).selectList(any(SelectListDescriptor.class)); doReturn(Arrays.asList(new DataContainer(68L, "SUBTASK"), new DataContainer(67L, "TASK"), new DataContainer(66L, "PROC"))).when(parentContainerResolver) .getContainerHierarchy(new DataContainer(68L, "SUBTASK")); //when SDataInstance dataInstance = dataInstanceServiceImpl.getDataInstance("testData", 68L, "SUBTASK", parentContainerResolver); //then assertThat(dataInstance.getValue()).isEqualTo("SUBTASK_VALUE"); } } ================================================ FILE: services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/model/archive/impl/SAXMLDataInstanceImplTest.java ================================================ /** * 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.data.instance.model.archive.impl; import static org.assertj.core.api.Assertions.assertThat; import java.io.Serializable; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SAXMLDataInstance; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class SAXMLDataInstanceImplTest { @Mock private SDataInstance dataInstance; @Test public void SAXMLDataInstanceImpl_should_return_given_value() { //given final Serializable givenValue = new String("serializable value"); final SAXMLDataInstance sxmlObjectDataInstanceImpl = new SAXMLDataInstance(dataInstance); //when sxmlObjectDataInstanceImpl.setValue(givenValue); final Serializable returnedValue = sxmlObjectDataInstanceImpl.getValue(); //then assertThat(returnedValue).as("should be equal to given value").isEqualTo(givenValue); } @Test public void SAXMLDataInstanceImpl_should_return_null_when_no_given_value() { //given final SAXMLDataInstance sxmlObjectDataInstanceImpl = new SAXMLDataInstance(dataInstance); //when final Serializable returnedValue = sxmlObjectDataInstanceImpl.getValue(); //then assertThat(returnedValue).as("should be not null").isNull(); } @Test public void nameSpaceTest() { //given final String expected = "namspace"; final SAXMLDataInstance sxmlObjectDataInstanceImpl = new SAXMLDataInstance(); //when sxmlObjectDataInstanceImpl.setNamespace(expected); final String returnedValue = sxmlObjectDataInstanceImpl.getNamespace(); //then assertThat(returnedValue).as("should be not null").isSameAs(expected); } @Test public void elementTest() { //given final String expected = "namspace"; final SAXMLDataInstance sxmlObjectDataInstanceImpl = new SAXMLDataInstance(); //when sxmlObjectDataInstanceImpl.setElement(expected); final String returnedValue = sxmlObjectDataInstanceImpl.getElement(); //then assertThat(returnedValue).as("should be not null").isSameAs(expected); } } ================================================ FILE: services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/model/archive/impl/SAXMLObjectDataInstanceTest.java ================================================ /** * 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.data.instance.model.archive.impl; import static org.junit.Assert.assertEquals; import org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance; import org.bonitasoft.engine.data.instance.model.archive.SAXMLObjectDataInstance; import org.junit.Before; import org.junit.Test; /** * @author Celine Souchet */ public class SAXMLObjectDataInstanceTest { private SXMLObjectDataInstance sDataInstance; @Before public void setUp() { sDataInstance = new SXMLObjectDataInstance(); sDataInstance.setValue(1); sDataInstance.setClassName(Long.class.getName()); sDataInstance.setContainerId(2); sDataInstance.setContainerType("containerType"); sDataInstance.setDataTypeClassName(String.class.getName()); sDataInstance.setDescription("description"); sDataInstance.setId(3); sDataInstance.setName("name"); sDataInstance.setTransientData(false); sDataInstance.setValue("value"); } /** * Test method for * {@link SAXMLObjectDataInstance#SAXMLObjectDataInstance(org.bonitasoft.engine.data.instance.model.SDataInstance)} * . */ @Test public final void sAXMLObjectDataInstanceImplSDataInstance() { final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance(sDataInstance); assertEquals(sDataInstance.getClassName(), saxmlObjectDataInstance.getClassName()); assertEquals(sDataInstance.getContainerId(), saxmlObjectDataInstance.getContainerId()); assertEquals(sDataInstance.getContainerType(), saxmlObjectDataInstance.getContainerType()); assertEquals(sDataInstance.getDescription(), saxmlObjectDataInstance.getDescription()); assertEquals(sDataInstance.getId(), saxmlObjectDataInstance.getSourceObjectId()); assertEquals(sDataInstance.getName(), saxmlObjectDataInstance.getName()); assertEquals(sDataInstance.getValue(), saxmlObjectDataInstance.getValue()); } /** * Test method for {@link SAXMLObjectDataInstance#getValue()}. * Test method for {@link SAXMLObjectDataInstance#setValue(java.io.Serializable)}. */ @Test public final void getSetValue() { final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance(); saxmlObjectDataInstance.setValue("plop"); assertEquals("plop", saxmlObjectDataInstance.getValue()); } /** * Test method for {@link SAXMLObjectDataInstance#getValue()}. * Test method for {@link SAXMLObjectDataInstance#setValue(java.io.Serializable)}. */ @Test public final void getValueShouldBeNull() { final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance(); assertEquals(null, saxmlObjectDataInstance.getValue()); } @Test public final void getSetId() { final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance(); saxmlObjectDataInstance.setId(64); assertEquals(64, saxmlObjectDataInstance.getId()); } @Test public final void getSetName() { final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance(); saxmlObjectDataInstance.setName("plop5"); assertEquals("plop5", saxmlObjectDataInstance.getName()); } @Test public final void getDescription() { final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance(); saxmlObjectDataInstance.setDescription("plop8"); assertEquals("plop8", saxmlObjectDataInstance.getDescription()); } @Test public final void isSetTransientData() { final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance(); saxmlObjectDataInstance.setTransientData(true); assertEquals(true, saxmlObjectDataInstance.isTransientData()); } @Test public final void getSetClassName() { final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance(); saxmlObjectDataInstance.setClassName("plipou"); assertEquals("plipou", saxmlObjectDataInstance.getClassName()); } @Test public final void getSetContainerId() { final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance(); saxmlObjectDataInstance.setContainerId(14); assertEquals(14, saxmlObjectDataInstance.getContainerId()); } @Test public final void getSetContainerType() { final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance(); saxmlObjectDataInstance.setContainerType("plipou95"); assertEquals("plipou95", saxmlObjectDataInstance.getContainerType()); } @Test public final void getArchiveDate() { final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance(); saxmlObjectDataInstance.setArchiveDate(83); assertEquals(83, saxmlObjectDataInstance.getArchiveDate()); } @Test public final void getSetSourceObjectId() { final SAXMLObjectDataInstance saxmlObjectDataInstance = new SAXMLObjectDataInstance(); saxmlObjectDataInstance.setSourceObjectId(85); assertEquals(85, saxmlObjectDataInstance.getSourceObjectId()); } } ================================================ FILE: services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/model/builder/impl/SDataInstanceBuilderFactoryImplTest.java ================================================ /** * 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.data.instance.model.builder.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Date; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.definition.model.STextDataDefinition; import org.bonitasoft.engine.data.definition.model.SXMLDataDefinition; import org.bonitasoft.engine.data.instance.model.SBlobDataInstance; import org.bonitasoft.engine.data.instance.model.SBooleanDataInstance; import org.bonitasoft.engine.data.instance.model.SDataInstance; import org.bonitasoft.engine.data.instance.model.SDataInstanceBuilder; import org.bonitasoft.engine.data.instance.model.SDateDataInstance; import org.bonitasoft.engine.data.instance.model.SDoubleDataInstance; import org.bonitasoft.engine.data.instance.model.SFloatDataInstance; import org.bonitasoft.engine.data.instance.model.SIntegerDataInstance; import org.bonitasoft.engine.data.instance.model.SLongDataInstance; import org.bonitasoft.engine.data.instance.model.SLongTextDataInstance; import org.bonitasoft.engine.data.instance.model.SShortTextDataInstance; import org.bonitasoft.engine.data.instance.model.SXMLDataInstance; import org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance; import org.bonitasoft.engine.data.instance.model.exceptions.SDataInstanceNotWellFormedException; import org.junit.Test; public class SDataInstanceBuilderFactoryImplTest { @Test public void createNewInstanceHandlesDateType() throws Exception { testSpecificDataType(Date.class.getName(), SDateDataInstance.class); } @Test public void createNewInstanceHandlesIntegerType() throws Exception { testSpecificDataType(Integer.class.getName(), SIntegerDataInstance.class); } @Test public void createNewInstanceHandlesLongType() throws Exception { testSpecificDataType(Long.class.getName(), SLongDataInstance.class); } @Test public void createNewInstanceHandlesStringType() throws Exception { testSpecificDataType(String.class.getName(), SShortTextDataInstance.class); } @Test public void createNewInstanceHandlesDoubleType() throws Exception { testSpecificDataType(Double.class.getName(), SDoubleDataInstance.class); } @Test public void createNewInstanceHandlesFloatType() throws Exception { testSpecificDataType(Float.class.getName(), SFloatDataInstance.class); } @Test public void createNewInstanceHandlesByteArrayAsBLOBType() throws Exception { testSpecificDataType(byte[].class.getName(), SBlobDataInstance.class); } @Test public void createNewInstanceHandlesBooleanType() throws Exception { testSpecificDataType(Boolean.class.getName(), SBooleanDataInstance.class); } @Test public void createNewInstanceHandlesCustomTypeAsXMLObject() throws Exception { testSpecificDataType(Object.class.getName(), SXMLObjectDataInstance.class); } private void testSpecificDataType(final String dataTypeName, final Class dataInstanceClass) throws SDataInstanceNotWellFormedException { final SDataDefinition dataDefinition = mock(SDataDefinition.class); when(dataDefinition.getClassName()).thenReturn(dataTypeName); final SDataInstanceBuilder newInstance = SDataInstanceBuilder.createNewInstance(dataDefinition); assertThat(newInstance.done()).isInstanceOf(dataInstanceClass); } @Test public void createNewInstanceHandlesXMLType() throws Exception { assertThat(SDataInstanceBuilder.createNewInstance(mock(SXMLDataDefinition.class)).done()) .isInstanceOf(SXMLDataInstance.class); } @Test public void createNewInstanceHandlesShortTextType() throws Exception { assertThat(SDataInstanceBuilder.createNewInstance(mock(STextDataDefinition.class)).done()).isInstanceOf( SShortTextDataInstance.class); } @Test public void createNewInstanceHandlesLongTextType() throws Exception { final STextDataDefinition dataDef = mock(STextDataDefinition.class); when(dataDef.isLongText()).thenReturn(true); assertThat(SDataInstanceBuilder.createNewInstance(dataDef).done()).isInstanceOf(SLongTextDataInstance.class); } } ================================================ FILE: services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/model/impl/OffsetDateTimeXStreamConverterTest.java ================================================ /** * 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.data.instance.model.impl; import static org.assertj.core.api.Assertions.assertThat; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; import org.junit.Test; /** * @author Emmanuel Duchastenier */ public class OffsetDateTimeXStreamConverterTest { @Test public void toString_should_OffsetDateTime_converted_to_UTC() throws Exception { // given: final OffsetDateTimeXStreamConverter offsetDateTimeConverter = new OffsetDateTimeXStreamConverter(); final OffsetDateTime offsetDateTime = OffsetDateTime.of(LocalDateTime.of(1973, 10, 17, 13, 42, 0), ZoneOffset.ofHours(-4)); // when: final String toString = offsetDateTimeConverter.toString(offsetDateTime); // then: assertThat(toString).isEqualTo("1973-10-17T17:42:00Z"); } @Test public void fromString_should_reset_offset_to_UTC() throws Exception { // given: String dateAsString = "1973-10-17T11:50:00-02:00"; final OffsetDateTimeXStreamConverter offsetDateTimeConverter = new OffsetDateTimeXStreamConverter(); // when: final Object offsetDateTime = offsetDateTimeConverter.fromString(dateAsString); // then: assertThat(offsetDateTime) .isEqualTo(OffsetDateTime.of(LocalDateTime.of(1973, 10, 17, 13, 50, 0), ZoneOffset.UTC)); } @Test public void toString_should_return_null_for_null_input() throws Exception { // given: final OffsetDateTimeXStreamConverter offsetDateTimeConverter = new OffsetDateTimeXStreamConverter(); // when: final String toString = offsetDateTimeConverter.toString(null); // then: assertThat(toString).isNull(); } } ================================================ FILE: services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/model/impl/SXMLObjectDataInstanceTest.java ================================================ /** * 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.data.instance.model.impl; import static org.assertj.core.api.Assertions.assertThat; import java.io.Serializable; import org.bonitasoft.engine.data.definition.model.SDataDefinition; import org.bonitasoft.engine.data.instance.model.SXMLObjectDataInstance; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class SXMLObjectDataInstanceTest { @Mock private SDataDefinition dataDefinition; @Test public void SXMLObjectDataInstanceImpl_should_return_given_value() { //given final Serializable givenValue = new String("serializable value"); final SXMLObjectDataInstance sxmlObjectDataInstance = new SXMLObjectDataInstance(dataDefinition); //when sxmlObjectDataInstance.setValue(givenValue); final Serializable returnedValue = sxmlObjectDataInstance.getValue(); //then assertThat(returnedValue).as("should be equal to given value").isEqualTo(givenValue); } @Test public void SXMLObjectDataInstanceImpl_should_return_null_when_no_given_value() { //given final SXMLObjectDataInstance sxmlObjectDataInstance = new SXMLObjectDataInstance(dataDefinition); //when final Serializable returnedValue = sxmlObjectDataInstance.getValue(); //then assertThat(returnedValue).as("should be null").isNull(); } } ================================================ FILE: services/bonita-data-instance/src/test/java/org/bonitasoft/engine/data/instance/model/impl/XStreamFactoryTest.java ================================================ /** * 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.data.instance.model.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.io.Serializable; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.security.ForbiddenClassException; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.RestoreSystemProperties; import org.junit.contrib.java.lang.system.SystemErrRule; public class XStreamFactoryTest { @Rule public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); @Test public void should_provide_xstream() { //when final XStream xStream = XStreamFactory.getXStream(); //then assertThat(xStream).as("should provide xstream instance").isNotNull(); } @Test public void should_create_xstream_only_once_in_same_classLoader() { //when final XStream xStream = XStreamFactory.getXStream(); final XStream xStream2 = XStreamFactory.getXStream(); //then assertThat(xStream2).as("should provide the xstream instance when we are in the same classloader") .isSameAs(xStream); } @Rule public SystemErrRule systemErrRule = new SystemErrRule().enableLog(); @Test public void xStream_should_not_warn_about_security() throws Exception { // given: final XStream xStream = XStreamFactory.getXStream(); // when: xStream.fromXML( "17executable"); // then: assertThat(systemErrRule.getLog()).doesNotContain("XStream is probably vulnerable"); } @Test public void xStream_should_ignoreUnknownElements() throws Exception { // given: final XStream xStream = XStreamFactory.getXStream(); var myData = new MyDataObject("Romain", "nope"); var serialized = xStream.toXML(myData); // Artificially add a field from serialized object serialized = serialized.replace("nope", "nope\n1985-04-02"); // when: MyDataObject deserialized = (MyDataObject) xStream.fromXML(serialized); // then: assertThat(deserialized.getUsername()).isEqualTo("Romain"); assertThat(deserialized.getPassword()).isEqualTo("nope"); } @Test public void xStream_should_reject_commons_collections4_gadget_chain_types() { // given: final XStream xStream = XStreamFactory.getXStream(); String maliciousXml = "" + "" + ""; // when / then: assertThatThrownBy(() -> xStream.fromXML(maliciousXml)) .isInstanceOf(ForbiddenClassException.class); } @Test public void xStream_should_reject_javax_script_gadget_chain_types() { // given: final XStream xStream = XStreamFactory.getXStream(); String maliciousXml = ""; // when / then: assertThatThrownBy(() -> xStream.fromXML(maliciousXml)) .isInstanceOf(ForbiddenClassException.class); } @Test public void xStream_should_reject_java_net_URL_gadget_chain_types() { // given: final XStream xStream = XStreamFactory.getXStream(); String maliciousXml = "" + "http" + "attacker.example.com" + "80" + "/" + ""; // when / then: assertThatThrownBy(() -> xStream.fromXML(maliciousXml)) .isInstanceOf(ForbiddenClassException.class); } @Test public void xStream_should_use_custom_deny_list_from_system_property() { // given: custom deny list via system property XStreamFactory.remove(Thread.currentThread().getContextClassLoader()); System.setProperty("bonita.xstream.deny.packages", "java.awt.**"); // when: final XStream xStream = XStreamFactory.getXStream(); // then: type matching the custom deny pattern should be rejected assertThatThrownBy(() -> xStream.fromXML("")) .isInstanceOf(ForbiddenClassException.class); // cleanup: remove cached instance so other tests get fresh defaults XStreamFactory.remove(Thread.currentThread().getContextClassLoader()); } static class MyDataObject implements Serializable { private String username; private String password; public MyDataObject(String username, String password) { this.username = username; this.password = password; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } } } ================================================ FILE: services/bonita-events/build.gradle ================================================ dependencies { api project(':services:bonita-commons') testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.logback } tasks.register("testsJar", Jar) { archiveClassifier = 'tests' from(sourceSets.test.output) } group = 'org.bonitasoft.engine.events' description = 'Bonita Event' publishing { publications { mavenJava(MavenPublication) { from project.components.java artifact testsJar } } } ================================================ FILE: services/bonita-events/src/main/java/org/bonitasoft/engine/events/EventActionType.java ================================================ /** * 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.events; /** * @author Baptiste Mesta */ public enum EventActionType { CREATED, UPDATED, DELETED } ================================================ FILE: services/bonita-events/src/main/java/org/bonitasoft/engine/events/EventService.java ================================================ /** * 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.events; import java.util.Set; import org.bonitasoft.engine.events.model.HandlerRegistrationException; import org.bonitasoft.engine.events.model.HandlerUnregistrationException; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.events.model.SFireEventException; import org.bonitasoft.engine.events.model.SHandler; /** * This is the manager of all the events triggered by other services. Handlers are registered into the Event service. * When a * service fire an event, it calls the right handler corresponding to the given Event. * * @author Christophe Havard * @author Baptiste Mesta * @since 6.0 */ public interface EventService { /** * Fire the specified Event to the registered handlers. * * @param event * A specific Event */ void fireEvent(final SEvent event) throws SFireEventException; /** * Allows to check if an handler is listening to this event type * * @param eventType * the type of the event * @return * true if an handler is interested by the event having type eventType */ boolean hasHandlers(final String eventType, EventActionType actionType); /** * Add the given handler to the Event Manager's handlers list. * * @param eventType The type of the event the handler is interested in. * @param userHandler * The handler to register in the Event Manager * @throws HandlerRegistrationException */ void addHandler(final String eventType, final SHandler userHandler) throws HandlerRegistrationException; /** * Registers a handler for the given event type only if a handler with the same identifier * is not already registered. This ensures idempotent registration in clustered environments * where multiple nodes may attempt to register the same handler. * * @param eventType the event type to register the handler for * @param handler the handler to register * @throws HandlerRegistrationException if registration fails * @since 10.0.9 */ void registerHandlerIfNotExists(String eventType, SHandler handler) throws HandlerRegistrationException; /** * Remove the given handler from the Event Service's handlers lists. * * @param handler * The handler to remove */ void removeAllHandlers(final SHandler handler) throws HandlerUnregistrationException; /** * Remove the given handler from the given event type filter * * @param handler * The handler to remove from the given event type */ void removeHandler(final String eventType, final SHandler handler) throws HandlerUnregistrationException; /** * Retrieve the list of all registered Handlers or the given EventType */ Set> getHandlers(String eventType); } ================================================ FILE: services/bonita-events/src/main/java/org/bonitasoft/engine/events/impl/AbstractEventServiceImpl.java ================================================ /** * 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.events.impl; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.bonitasoft.engine.commons.ExceptionUtils; import org.bonitasoft.engine.events.EventActionType; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.events.model.HandlerRegistrationException; import org.bonitasoft.engine.events.model.HandlerUnregistrationException; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.events.model.SFireEventException; import org.bonitasoft.engine.events.model.SHandler; import org.slf4j.Logger; public abstract class AbstractEventServiceImpl implements EventService { protected abstract Logger getLogger(); protected AbstractEventServiceImpl() { } /** * Fire the given Event only to interested handlers */ @Override public void fireEvent(final SEvent event) throws SFireEventException { if (event == null) { throw new SFireEventException("Event is null"); } // if at least 1 eventFilter contains a group of handlers for the given event type if (containsHandlerFor(event.getType())) { // retrieve the handler list concerned by the given event final Collection> handlers = getHandlersFor(event.getType()); if (!handlers.isEmpty()) { if (getLogger().isTraceEnabled()) { getLogger().trace("Found {} for event {}. All handlers: {}", handlers.size(), event.getType(), handlers); } SFireEventException sFireEventException = null; for (final SHandler handler : handlers) { // for each handler, I check if it's interested or not by the given event try { if (handler.isInterested(event)) { handler.execute(event); } } catch (final Exception e) { if (sFireEventException == null) { sFireEventException = new SFireEventException("Unable to execute some handler.", e); } sFireEventException.addHandlerException(e); if (getLogger().isDebugEnabled()) { getLogger().debug("Handler failed {}", ExceptionUtils.printLightWeightStacktrace(e)); } } } if (sFireEventException != null) { throw sFireEventException; } } } } protected abstract Collection> getHandlersFor(final String type); protected abstract boolean containsHandlerFor(final String type); /** * No handler duplication in a list for a given event type */ @Override public final void addHandler(final String eventType, final SHandler handler) throws HandlerRegistrationException { if (handler != null && eventType != null) { addHandlerFor(eventType, handler); } else { throw new HandlerRegistrationException( "One of the parameters is null : " + " eventType: " + eventType + " handler:" + handler); } } @Override public void registerHandlerIfNotExists(String eventType, SHandler handler) throws HandlerRegistrationException { // Check if a handler with the same identifier already exists boolean alreadyRegistered = getHandlers(eventType).stream() .anyMatch(h -> h.getIdentifier().equals(handler.getIdentifier())); if (!alreadyRegistered) { addHandler(eventType, handler); getLogger().debug("Registered handler {} for event type {}", handler.getIdentifier(), eventType); } else { getLogger().debug("Handler {} already registered for event type {}. Ignoring registration.", handler.getIdentifier(), eventType); } } protected abstract void addHandlerFor(String eventType, SHandler handler) throws HandlerRegistrationException; @Override public final void removeAllHandlers(final SHandler handler) throws HandlerUnregistrationException { if (handler == null) { throw new HandlerUnregistrationException("Unable to remove a null handler"); } removeAllHandlersFor(handler); } protected abstract void removeAllHandlersFor(SHandler handler); @Override public final void removeHandler(final String eventType, final SHandler h) throws HandlerUnregistrationException { if (h == null || eventType == null) { throw new HandlerUnregistrationException("Unable to remove a null handler"); } removeHandlerFor(eventType, h); } protected abstract void removeHandlerFor(final String eventType, final SHandler h) throws HandlerUnregistrationException; @Override public final Set> getHandlers(final String eventType) { final Collection> handlers = getHandlersFor(eventType); if (handlers == null) { return Collections.emptySet(); } return new HashSet<>(handlers); } @Override public final boolean hasHandlers(final String eventType, final EventActionType actionType) { String key = eventType; if (actionType != null) { switch (actionType) { case CREATED: key += SEvent.CREATED; break; case DELETED: key += SEvent.DELETED; break; case UPDATED: key += SEvent.UPDATED; break; default: return false; } } return containsHandlerFor(key); } } ================================================ FILE: services/bonita-events/src/main/java/org/bonitasoft/engine/events/impl/EventServiceImpl.java ================================================ /** * 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.events.impl; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.bonitasoft.engine.events.model.HandlerRegistrationException; import org.bonitasoft.engine.events.model.HandlerUnregistrationException; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.events.model.SHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Christophe Havard * @author Matthieu Chaffotte * @author Laurent Vaills */ public class EventServiceImpl extends AbstractEventServiceImpl { private final Logger logger = LoggerFactory.getLogger(EventServiceImpl.class); /** * Contains a list of all events type and their registered handlers */ protected Map>> registeredHandlers; @Override protected Logger getLogger() { return logger; } public EventServiceImpl() { super(); registeredHandlers = new HashMap>>(); } @Override protected boolean containsHandlerFor(final String key) { return registeredHandlers.containsKey(key); } @Override protected Collection> getHandlersFor(final String eventType) { return registeredHandlers.get(eventType); } @Override protected void addHandlerFor(final String eventType, final SHandler handler) throws HandlerRegistrationException { // check if the given event type is already registered in the Event Service if (containsHandlerFor(eventType)) { // if the handler already exists for the same eventType, an Exception is thrown final List> handlers = registeredHandlers.get(eventType); // Check if another handler of the same class is already registered for (SHandler tmpHandler : handlers) { if (tmpHandler.getIdentifier().equals(handler.getIdentifier())) { throw new HandlerRegistrationException("The handler with identifier " + tmpHandler.getIdentifier() + " is already registered for the event " + eventType); } } handlers.add(handler); } else { // if the given type doesn't already exist in the eventFilters list, we create it final List> newHandlerList = new ArrayList>(3); newHandlerList.add(handler); registeredHandlers.put(eventType, newHandlerList); } } @Override protected void removeAllHandlersFor(final SHandler handler) { for (final String eventType : registeredHandlers.keySet()) { try { removeHandler(eventType, handler); } catch (HandlerUnregistrationException e) { // Nothing to do. } } } @Override protected void removeHandlerFor(final String eventType, final SHandler h) throws HandlerUnregistrationException { boolean removed = false; Collection> handlers = getHandlersFor(eventType); if (handlers != null) { Iterator> it = handlers.iterator(); while (!removed && it.hasNext()) { SHandler handler = it.next(); if (h.getIdentifier().equals(handler.getIdentifier())) { it.remove(); removed = true; } } } if (!removed) { throw new HandlerUnregistrationException("Handler did not exists"); } } } ================================================ FILE: services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/HandlerRegistrationException.java ================================================ /** * 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.events.model; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Christophe Havard */ public class HandlerRegistrationException extends SBonitaException { private static final long serialVersionUID = -8854372636800811528L; public HandlerRegistrationException() { super(); } public HandlerRegistrationException(final String message, final Throwable cause) { super(message, cause); } public HandlerRegistrationException(final String message) { super(message); } } ================================================ FILE: services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/HandlerUnregistrationException.java ================================================ /** * 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.events.model; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Christophe Havard */ public class HandlerUnregistrationException extends SBonitaException { private static final long serialVersionUID = -6460850460472780165L; public HandlerUnregistrationException(String message) { super(message); } } ================================================ FILE: services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/SDeleteEvent.java ================================================ /** * 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.events.model; /** * Represent the deletion of a persisted element */ public class SDeleteEvent extends SEvent { public SDeleteEvent(String type) { super(type); } } ================================================ FILE: services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/SEvent.java ================================================ /** * 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.events.model; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; /** * @author Baptiste Mesta */ public class SEvent { public static final String UPDATED = "_UPDATED"; public static final String CREATED = "_CREATED"; public static final String DELETED = "_DELETED"; private final String type; private Object object; public SEvent(String type) { this.type = type; } public String getType() { return type; } /** * Retrieve the object which the event occurred on */ public Object getObject() { return object; } /** * Set the object passed inside the fired Event */ public void setObject(Object object) { this.object = object; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SEvent sEvent = (SEvent) o; return new EqualsBuilder() .append(type, sEvent.type) .append(object, sEvent.object) .isEquals(); } @Override public int hashCode() { return new HashCodeBuilder(17, 37) .append(type) .append(object) .toHashCode(); } @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) .append("type", type) .append("object", object) .toString(); } } ================================================ FILE: services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/SFireEventException.java ================================================ /** * 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.events.model; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Christophe Havard * @author Celine Souchet */ public class SFireEventException extends SBonitaException { private static final long serialVersionUID = 752699780388742999L; private List handlerExceptions = new ArrayList<>(); public SFireEventException(String message) { super(message); } public SFireEventException(final String message, Exception firstCause) { super(message, firstCause); } public void addHandlerException(final Exception e) { handlerExceptions.add(e); } public List getHandlerExceptions() { return handlerExceptions; } @Override public String getMessage() { return super.getMessage() + handlerExceptions.stream() .map(e -> e.getClass().getName() + ": " + e.getMessage()) .collect(Collectors.joining("\n", " [", "]")); } } ================================================ FILE: services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/SHandler.java ================================================ /** * 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.events.model; import java.io.Serializable; /** * @author Christophe Havard * @author Matthieu Chaffotte */ public interface SHandler extends Serializable { /** * Performs the action corresponding to the given Event */ void execute(T event) throws SHandlerExecutionException; /** * Precise if the current Handler is interested by the given event. * If so, it could run the execute(SEvent e) method. */ boolean isInterested(T event); /** * Returns a unique identifier for each instance of the Handler */ String getIdentifier(); } ================================================ FILE: services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/SHandlerExecutionException.java ================================================ /** * 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.events.model; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SHandlerExecutionException extends SBonitaException { private static final long serialVersionUID = 297807701528986021L; public SHandlerExecutionException(final SBonitaException e) { super(e); } public SHandlerExecutionException(final String message, final SBonitaException cause) { super(message, cause); } public SHandlerExecutionException(final String message, final Throwable cause) { super(message, cause); } public SHandlerExecutionException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/SInsertEvent.java ================================================ /** * 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.events.model; public class SInsertEvent extends SEvent { public SInsertEvent(String type) { super(type); } } ================================================ FILE: services/bonita-events/src/main/java/org/bonitasoft/engine/events/model/SUpdateEvent.java ================================================ /** * 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.events.model; import java.util.Map; public class SUpdateEvent extends SEvent { private Object oldObject; private Map updatedFields; public SUpdateEvent(String type) { super(type); } @Deprecated public Object getOldObject() { return oldObject; } public Map getUpdatedFields() { return updatedFields; } @Deprecated public void setOldObject(Object oldObject) { this.oldObject = oldObject; } public void setUpdatedFields(Map updatedFields) { this.updatedFields = updatedFields; } } ================================================ FILE: services/bonita-events/src/test/java/org/bonitasoft/engine/events/EventServiceImplTest.java ================================================ /** * 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.events; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.events.impl.EventServiceImpl; import org.bonitasoft.engine.events.model.HandlerRegistrationException; import org.bonitasoft.engine.events.model.HandlerUnregistrationException; import org.bonitasoft.engine.events.model.SFireEventException; import org.junit.Before; import org.junit.Test; /** * @author Laurent Vaills */ public class EventServiceImplTest { protected EventService instantiateEventServiceImplementation() { return new EventServiceImpl(); } private EventService eventSvc; // Test events private final String EVT_INTERESTING = "INTERESTING"; private final String EVT_IRRELEVANT = "IRRELEVANT"; @Before public void beforeEachTest() { eventSvc = instantiateEventServiceImplementation(); } @Test(expected = SFireEventException.class) public void fireNullEvent() throws Exception { eventSvc.fireEvent(null); } @Test(expected = HandlerRegistrationException.class) public void addHandlerNull() throws Exception { eventSvc.addHandler(EVT_INTERESTING, null); } @Test(expected = HandlerRegistrationException.class) public void addTwiceTheSameHandler() throws Exception { final TestHandler h = new TestHandler(); eventSvc.addHandler(EVT_INTERESTING, h); assertThat(eventSvc.getHandlers(EVT_INTERESTING)).hasSize(1); eventSvc.addHandler(EVT_INTERESTING, h); } @Test public void addHandlerInEventFilters() throws Exception { final TestHandler h = new TestHandler(); eventSvc.addHandler(EVT_INTERESTING, h); assertThat(eventSvc.getHandlers(EVT_INTERESTING)).contains(h); eventSvc.removeHandler(EVT_INTERESTING, h); } @Test public void addNewTypeInRegisteredHandlers() throws Exception { assertThat(eventSvc.getHandlers(EVT_INTERESTING)).isEmpty(); final TestHandler h = new TestHandler(); eventSvc.addHandler(EVT_INTERESTING, h); assertThat(eventSvc.getHandlers(EVT_INTERESTING)).isNotEmpty(); eventSvc.removeHandler(EVT_INTERESTING, h); } @Test(expected = HandlerUnregistrationException.class) public void removeUnknownHandler() throws Exception { final TestHandler h = new TestHandler(); eventSvc.removeHandler(EVT_INTERESTING, h); } @Test public void removeHandler() throws Exception { final TestHandler h = new TestHandler(); eventSvc.addHandler(EVT_INTERESTING, h); assertThat(eventSvc.getHandlers(EVT_INTERESTING)).hasSize(1); eventSvc.removeHandler(EVT_INTERESTING, h); assertThat(eventSvc.getHandlers(EVT_INTERESTING)).isEmpty(); } @Test public void isEventFiltered() throws HandlerRegistrationException, SFireEventException, HandlerUnregistrationException { // Register handler on a given event type. final TestHandlerCallback h = new TestHandlerCallback(); eventSvc.addHandler(EVT_INTERESTING, h); // Fire 2 different events final TestEvent interesting = new TestEvent(EVT_INTERESTING); final TestEvent irrelevant = new TestEvent(EVT_IRRELEVANT); eventSvc.fireEvent(interesting); eventSvc.fireEvent(irrelevant); // Check that only "interesting" events have been received by the registered handler assertThat(irrelevant.isFlagged()).isFalse(); assertThat(interesting.isFlagged()).isTrue(); eventSvc.removeHandler(EVT_INTERESTING, h); } @Test public void isEventReceivedByHandler() throws SFireEventException, HandlerRegistrationException, HandlerUnregistrationException { final TestHandlerCallback h = new TestHandlerCallback(); final TestEvent interesting = new TestEvent(EVT_INTERESTING); eventSvc.addHandler(EVT_INTERESTING, h); eventSvc.fireEvent(interesting); assertThat(interesting.isFlagged()).isTrue(); eventSvc.removeHandler(EVT_INTERESTING, h); } @Test public void getAllHandlersByEvent() throws HandlerRegistrationException, HandlerUnregistrationException { assertThat(eventSvc.getHandlers(EVT_INTERESTING)).isEmpty(); // add 2 different handlers for 1 event type final TestHandler h1 = new TestHandler(); final TestHandler h2 = new TestHandler(); eventSvc.addHandler(EVT_INTERESTING, h1); eventSvc.addHandler(EVT_INTERESTING, h2); // now i check if the evtList contains my both handlers assertThat(eventSvc.getHandlers(EVT_INTERESTING)).isNotEmpty(); eventSvc.removeHandler(EVT_INTERESTING, h1); eventSvc.removeHandler(EVT_INTERESTING, h2); } @Test public void registerHandlerIfNotExists_shouldRegisterWhenNoHandlerExists() throws HandlerRegistrationException { assertThat(eventSvc.getHandlers(EVT_INTERESTING)).isEmpty(); final TestHandler handler = new TestHandler(); eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler); assertThat(eventSvc.getHandlers(EVT_INTERESTING)).hasSize(1); assertThat(eventSvc.getHandlers(EVT_INTERESTING)).contains(handler); } @Test public void registerHandlerIfNotExists_shouldNotRegisterWhenHandlerAlreadyExists() throws HandlerRegistrationException { final TestHandler handler = new TestHandler(); // Register handler first time eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler); assertThat(eventSvc.getHandlers(EVT_INTERESTING)).hasSize(1); // Try to register same handler again (same identifier) eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler); // Should still have only 1 handler assertThat(eventSvc.getHandlers(EVT_INTERESTING)).hasSize(1); } @Test public void registerHandlerIfNotExists_shouldRegisterDifferentHandlers() throws HandlerRegistrationException { // Create handlers with explicit different identifiers final TestHandler handler1 = new TestHandler("handler-1"); final TestHandler handler2 = new TestHandler("handler-2"); eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler1); eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler2); // Should have 2 handlers with different identifiers assertThat(eventSvc.getHandlers(EVT_INTERESTING)).hasSize(2); } @Test public void registerHandlerIfNotExists_shouldBeIdempotentAcrossMultipleCalls() throws HandlerRegistrationException { final TestHandler handler = new TestHandler(); // Register multiple times eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler); eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler); eventSvc.registerHandlerIfNotExists(EVT_INTERESTING, handler); // Should still have only 1 handler assertThat(eventSvc.getHandlers(EVT_INTERESTING)).hasSize(1); } } ================================================ FILE: services/bonita-events/src/test/java/org/bonitasoft/engine/events/TestEvent.java ================================================ /** * 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.events; import org.bonitasoft.engine.events.model.SEvent; class TestEvent extends SEvent { private boolean flagged = false; TestEvent(String type) { super(type); } void flag() { this.flagged = true; } boolean isFlagged() { return flagged; } } ================================================ FILE: services/bonita-events/src/test/java/org/bonitasoft/engine/events/TestHandler.java ================================================ /** * 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.events; import java.util.ArrayList; import java.util.List; import java.util.UUID; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.events.model.SHandler; /** * @author Christophe Havard */ public class TestHandler implements SHandler { private static final long serialVersionUID = 1L; private boolean isCalled = false; private List receivedEvent; private final String identifier; public TestHandler() { this(UUID.randomUUID().toString()); } public TestHandler(final String identifier) { this.identifier = identifier; } @Override public void execute(final SEvent event) { setCalled(true); if (receivedEvent == null) { receivedEvent = new ArrayList(); } receivedEvent.add(event); } public void setCalled(final boolean isCalled) { this.isCalled = isCalled; } public boolean isCalled() { return isCalled; } public List getReceivedEvents() { return receivedEvent; } @Override public boolean isInterested(final SEvent event) { return "INTERESTING".equals(event.getType()); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((identifier == null) ? 0 : identifier.hashCode()); result = prime * result + (isCalled ? 1231 : 1237); result = prime * result + ((receivedEvent == null) ? 0 : receivedEvent.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } TestHandler other = (TestHandler) obj; if (identifier == null) { if (other.identifier != null) { return false; } } else if (!identifier.equals(other.identifier)) { return false; } if (isCalled != other.isCalled) { return false; } if (receivedEvent == null) { if (other.receivedEvent != null) { return false; } } else if (!receivedEvent.equals(other.receivedEvent)) { return false; } return true; } @Override public String getIdentifier() { return identifier; } } ================================================ FILE: services/bonita-events/src/test/java/org/bonitasoft/engine/events/TestHandlerCallback.java ================================================ /** * 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.events; import java.util.UUID; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.events.model.SHandler; /** * @author Christophe Havard */ public class TestHandlerCallback implements SHandler { private static final long serialVersionUID = 1L; private final String identifier; public TestHandlerCallback() { this(UUID.randomUUID().toString()); } public TestHandlerCallback(final String identifier) { this.identifier = identifier; } @Override public void execute(final SEvent event) { TestEvent testEvent = (TestEvent) event; testEvent.flag(); } @Override public boolean isInterested(final SEvent event) { return "INTERESTING".equals(event.getType()); } @Override public String getIdentifier() { return identifier; } } ================================================ FILE: services/bonita-expression/build.gradle ================================================ dependencies { api project(':services:bonita-session') api project(':services:bonita-commons') api project(':services:bonita-cache') api project(':services:bonita-time-tracker') api project(':services:bonita-classloader') compileOnly libs.lombok api libs.bundles.groovy api libs.commonsLang api libs.commonsText testImplementation libs.logback testImplementation libs.mockitoCore testImplementation libs.assertj } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/ContainerState.java ================================================ /** * 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.expression; /** * @author Celine Souchet */ public enum ContainerState { ARCHIVED, ACTIVE; } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/ExpressionExecutor.java ================================================ /** * 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.expression; import java.io.Serializable; import java.util.Map; /** * @author Zhao Na */ public interface ExpressionExecutor { Serializable evaluate(final String expressionContent, final Map map); Boolean validate(final String expressionContent); } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/ExpressionExecutorStrategy.java ================================================ /** * 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.expression; import java.util.Arrays; import java.util.List; import java.util.Map; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; /** * Allow to evaluate one kind of expression * the kind of expression that this evaluator is responsible for is define by the {@link #getExpressionKind()} Client * implements this interface in order to add * a new kind of expression * * @author Zhao Na * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public interface ExpressionExecutorStrategy { String DEFINITION_ID = "processDefinitionId";// hum should not be process here String CONTAINER_ID_KEY = "containerId"; String CONTAINER_TYPE_KEY = "containerType"; String INTERPRETER_GROOVY = "GROOVY"; String TYPE_CONSTANT = "TYPE_CONSTANT"; String TYPE_INPUT = "TYPE_INPUT"; String TYPE_READ_ONLY_SCRIPT = "TYPE_READ_ONLY_SCRIPT"; String TYPE_READ_ONLY_CONDITION_SCRIPT = "TYPE_READ_ONLY__CONDITION_SCRIPT"; String TYPE_VARIABLE = "TYPE_VARIABLE"; String TYPE_TRANSIENT_VARIABLE = "TYPE_TRANSIENT_VARIABLE"; String TYPE_PATTERN = "TYPE_PATTERN"; String TYPE_JAVA_METHOD_CALL = "TYPE_JAVA_METHOD_CALL"; String TYPE_PARAMETER = "TYPE_PARAMETER"; String TYPE_DOCUMENT = "TYPE_DOCUMENT"; String TYPE_DOCUMENT_LIST = "TYPE_DOCUMENT_LIST"; String TYPE_ENGINE_CONSTANT = "TYPE_ENGINE_CONSTANT"; String TYPE_LIST = "TYPE_LIST"; String TYPE_XPATH_READ = "TYPE_XPATH_READ"; String TYPE_BUSINESS_DATA = "TYPE_BUSINESS_DATA"; String TYPE_BUSINESS_DATA_REFERENCE = "TYPE_BUSINESS_DATA_REFERENCE"; String TYPE_BUSINESS_OBJECT_DAO = "TYPE_BUSINESS_OBJECT_DAO"; String TYPE_QUERY_BUSINESS_DATA = "TYPE_QUERY_BUSINESS_DATA"; String TYPE_CONTRACT_INPUT = "TYPE_CONTRACT_INPUT"; ExpressionKind KIND_CONSTANT = new ExpressionKind(TYPE_CONSTANT); ExpressionKind KIND_READ_ONLY_SCRIPT_GROOVY = new ExpressionKind(TYPE_READ_ONLY_SCRIPT, INTERPRETER_GROOVY); ExpressionKind KIND_READ_ONLY_CONDITION_SCRIPT_GROOVY = new ExpressionKind(TYPE_READ_ONLY_CONDITION_SCRIPT, INTERPRETER_GROOVY); ExpressionKind KIND_INPUT = new ExpressionKind(TYPE_INPUT); ExpressionKind KIND_VARIABLE = new ExpressionKind(TYPE_VARIABLE); ExpressionKind KIND_TRANSIENT_VARIABLE = new ExpressionKind(TYPE_TRANSIENT_VARIABLE); ExpressionKind KIND_PATTERN = new ExpressionKind(TYPE_PATTERN); ExpressionKind KIND_JAVA_METHOD_CALL = new ExpressionKind(TYPE_JAVA_METHOD_CALL); ExpressionKind KIND_PARAMETER = new ExpressionKind(TYPE_PARAMETER); ExpressionKind KIND_DOCUMENT = new ExpressionKind(TYPE_DOCUMENT); ExpressionKind KIND_DOCUMENT_LIST = new ExpressionKind(TYPE_DOCUMENT_LIST); ExpressionKind KIND_ENGINE_CONSTANT = new ExpressionKind(TYPE_ENGINE_CONSTANT); ExpressionKind KIND_LIST = new ExpressionKind(TYPE_LIST); ExpressionKind KIND_XPATH_READ = new ExpressionKind(TYPE_XPATH_READ); ExpressionKind KIND_BUSINESS_DATA = new ExpressionKind(TYPE_BUSINESS_DATA); ExpressionKind KIND_BUSINESS_DATA_REFERENCE = new ExpressionKind(TYPE_BUSINESS_DATA_REFERENCE); ExpressionKind KIND_BUSINESS_OBJECT_DAO = new ExpressionKind(TYPE_BUSINESS_OBJECT_DAO); ExpressionKind KIND_QUERY_BUSINESS_DATA = new ExpressionKind(TYPE_QUERY_BUSINESS_DATA); ExpressionKind KIND_CONTRACT_INPUT = new ExpressionKind(TYPE_CONTRACT_INPUT); /** * This list must contain only types with no dependencies */ List NO_DEPENDENCY_EXPRESSION_EVALUATION_ORDER = Arrays .asList(KIND_ENGINE_CONSTANT, KIND_VARIABLE, KIND_CONSTANT, KIND_INPUT, KIND_PARAMETER, KIND_DOCUMENT, KIND_BUSINESS_DATA, KIND_BUSINESS_OBJECT_DAO, KIND_CONTRACT_INPUT /* * , KIND_PATTERN, KIND_READ_ONLY_SCRIPT_GROOVY, * KIND_LIST */); /** * @param expression * the expression to evaluate * @param context * map containing the result of the evaluation of dependencies * and also informations about the context of evaluation given by {@link #CONTAINER_ID_KEY} and * {@link #CONTAINER_TYPE_KEY} * @return * the result of the evaluation of the expression of appropriate type * @throws SExpressionEvaluationException * @throws SExpressionDependencyMissingException */ Object evaluate(SExpression expression, Map context, Map resolvedExpressions, ContainerState containerState) throws SExpressionEvaluationException, SExpressionDependencyMissingException; /** * Validate the expression, an exception is thrown it is invalid * * @param expression * the expression to validate * @throws SInvalidExpressionException * if the exception is invalid * @since 6.0 */ void validate(SExpression expression) throws SInvalidExpressionException; ExpressionKind getExpressionKind(); List evaluate(List expressions, Map context, Map resolvedExpressions, ContainerState containerState) throws SExpressionEvaluationException, SExpressionDependencyMissingException; /** * Should we put the evaluated expressions of this strategy in the evaluation context? */ boolean mustPutEvaluatedExpressionInContext(); } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/ExpressionExecutorStrategyProvider.java ================================================ /** * 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.expression; import java.util.List; public class ExpressionExecutorStrategyProvider { public ExpressionExecutorStrategyProvider(ExpressionService expressionService, List expressionStrategies) { expressionService.setExpressionExecutorStrategy(expressionStrategies); } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/ExpressionService.java ================================================ /** * 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.expression; import java.util.List; import java.util.Map; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Zhao Na * @author Baptiste Mesta * @author Celine Souchet * @since 6.0 */ public interface ExpressionService { /** * Evaluate the specific expression * * @param expression * the expression will be evaluated * @return the evaluated expression result * @throws SExpressionTypeUnknownException * @throws SExpressionEvaluationException * @throws SExpressionDependencyMissingException * @throws SInvalidExpressionException */ Object evaluate(SExpression expression, Map resolvedExpressions, ContainerState containerState) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException; /** * Evaluate the specific expression with dependency values * * @param expression * the expression will be evaluated * @param dependencyValues * the dependency values, it may contain values for expression * @return the evaluated expression result * @throws SExpressionTypeUnknownException * @throws SExpressionEvaluationException * @throws SExpressionDependencyMissingException * @throws SInvalidExpressionException */ Object evaluate(SExpression expression, Map dependencyValues, Map resolvedExpressions, ContainerState containerState) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException; /** * Evaluate type specified expressions with dependency values * * @param expressionKind * the expression kind to indicate which type of ExpressionExecutorStrategy will be used to evaluate the * expressions * @param expressions * a list of expressions to be evaluated * @param dependencyValues * the dependency values for the expressions, it may contain value informations for expressions * @return a list of evaluated expression results * @throws SExpressionTypeUnknownException * @throws SExpressionEvaluationException * @throws SExpressionDependencyMissingException * @throws SInvalidExpressionException */ List evaluate(ExpressionKind expressionKind, List expressions, Map dependencyValues, Map resolvedExpressions, ContainerState containerState) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException; /** * Should we throw exception if the real return type is incompatible with the declared one? * * @return true if an exception must be thrown if check fails, false otherwise. */ boolean mustCheckExpressionReturnType(); /** * Should an expression result of a specified {@link ExpressionKind} must be put in the evaluation context? * * @param expressionKind * the {@link ExpressionKind} */ boolean mustPutEvaluatedExpressionInContext(ExpressionKind expressionKind); void setExpressionExecutorStrategy(List expressionStrategies); } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/NonEmptyContentExpressionExecutorStrategy.java ================================================ /** * 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.expression; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public abstract class NonEmptyContentExpressionExecutorStrategy implements ExpressionExecutorStrategy { @Override public void validate(final SExpression expression) throws SInvalidExpressionException { if (expression == null) { throw new SInvalidExpressionException("The expression cannot be null.", null); } final String expressionContent = expression.getContent(); if (expressionContent == null || expressionContent.trim().isEmpty()) { throw new SInvalidExpressionException( "The expression content cannot be null or empty. Expression : " + expression, expression.getName()); } } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/exception/SExpressionDependencyMissingException.java ================================================ /** * 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.expression.exception; /** * @author Zhao Na */ public class SExpressionDependencyMissingException extends SExpressionException { private static final long serialVersionUID = 7265041910539116858L; public SExpressionDependencyMissingException(final String message, final Throwable cause) { super(message, cause); } public SExpressionDependencyMissingException(final String message) { super(message); } public SExpressionDependencyMissingException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/exception/SExpressionEvaluationException.java ================================================ /** * 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.expression.exception; /** * @author Zhao Na * @author Celine Souchet */ public class SExpressionEvaluationException extends SExpressionException { private static final long serialVersionUID = 2040156586924261425L; private final String expressionName; /** * @param message * @param cause * @param expressionName * The expression's name that failed on the evaluation. */ public SExpressionEvaluationException(final String message, final Throwable cause, final String expressionName) { super(message, cause); this.expressionName = expressionName; } /** * @param message * @param expressionName * The expression's name that failed on the evaluation. */ public SExpressionEvaluationException(final String message, final String expressionName) { super(message); this.expressionName = expressionName; } /** * @param cause * @param expressionName * The expression's name that failed on the evaluation. */ public SExpressionEvaluationException(final Throwable cause, final String expressionName) { super(cause); this.expressionName = expressionName; } /** * Return null or empty string, if the context of evaluation is wrong. * * @return The expression's name that failed on the evaluation. */ public String getExpressionName() { return expressionName; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/exception/SExpressionException.java ================================================ /** * 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.expression.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Zhao Na */ public class SExpressionException extends SBonitaException { private static final long serialVersionUID = 1689881771691541843L; public SExpressionException(final String message, final Throwable cause) { super(message, cause); } public SExpressionException(final String message) { super(message); } public SExpressionException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/exception/SExpressionTypeUnknownException.java ================================================ /** * 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.expression.exception; /** * @author Zhao Na */ public class SExpressionTypeUnknownException extends SExpressionException { private static final long serialVersionUID = 7904793231822823603L; public SExpressionTypeUnknownException(final String message, final Throwable cause) { super(message, cause); } public SExpressionTypeUnknownException(final String message) { super(message); } public SExpressionTypeUnknownException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/exception/SInvalidExpressionException.java ================================================ /** * 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.expression.exception; /** * @author Zhao Na * @author Celine Souchet */ public class SInvalidExpressionException extends SExpressionException { private static final long serialVersionUID = 5287292772348995383L; private final String expressionName; public SInvalidExpressionException(final String message, final Throwable cause, final String expressionName) { super(message, cause); this.expressionName = expressionName; } public SInvalidExpressionException(final String message, final String expressionName) { super(message); this.expressionName = expressionName; } public SInvalidExpressionException(final Throwable cause, final String expressionName) { super(cause); this.expressionName = expressionName; } /** * Return null or empty string, if the context of evaluation is wrong. * * @return The expression's name that failed on the evaluation. */ public String getExpressionName() { return expressionName; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/ConditionExpressionExecutorStrategy.java ================================================ /** * 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.expression.impl; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.NonEmptyContentExpressionExecutorStrategy; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.impl.condition.BinaryComparatorExecutor; import org.bonitasoft.engine.expression.impl.condition.LogicalComplementExecutor; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public class ConditionExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy { public static final String LOGICAL_COMPLEMENT_OPERATOR = "!"; public static final String NOT_EQUALS_COMPARATOR = "!="; public static final String EQUALS_COMPARATOR = "=="; public static final String GREATER_THAN_OR_EQUALS_COMPARATOR = ">="; public static final String lESS_THAN_OR_EQUALS_COMPARATOR = "<="; public static final String GREATER_THAN_COMPARATOR = ">"; public static final String LESS_THAN_COMPARATOR = "<"; private final List validOperators; private final LogicalComplementExecutor logicalComplementExecutor; private final BinaryComparatorExecutor binaryComparatorExecutor; public ConditionExpressionExecutorStrategy(LogicalComplementExecutor logicalComplementExecutor, BinaryComparatorExecutor binaryComparatorExecutor) { this.logicalComplementExecutor = logicalComplementExecutor; this.binaryComparatorExecutor = binaryComparatorExecutor; validOperators = new ArrayList(7); validOperators.add(LESS_THAN_COMPARATOR); validOperators.add(GREATER_THAN_COMPARATOR); validOperators.add(lESS_THAN_OR_EQUALS_COMPARATOR); validOperators.add(GREATER_THAN_OR_EQUALS_COMPARATOR); validOperators.add(EQUALS_COMPARATOR); validOperators.add(NOT_EQUALS_COMPARATOR); validOperators.add(LOGICAL_COMPLEMENT_OPERATOR); } @Override public Object evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final String content = expression.getContent(); Object result; if (LOGICAL_COMPLEMENT_OPERATOR.equals(content)) { result = logicalComplementExecutor.evaluate(resolvedExpressions, expression); } else { result = binaryComparatorExecutor.evaluate(resolvedExpressions, expression); } return result; } @Override public void validate(final SExpression expression) throws SInvalidExpressionException { super.validate(expression); if (!validOperators.contains(expression.getContent())) { throw new SInvalidExpressionException( "The content of expression must be among: " + validOperators + " for expression: " + expression.toString(), expression.getName()); } } @Override public ExpressionKind getExpressionKind() { return new ExpressionKind(SExpression.TYPE_CONDITION); } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final List evaluatedExpressions = new ArrayList(expressions.size()); for (final SExpression sExpression : expressions) { evaluatedExpressions.add(evaluate(sExpression, context, resolvedExpressions, containerState)); } return evaluatedExpressions; } @Override public boolean mustPutEvaluatedExpressionInContext() { return false; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/ConstantExpressionExecutorStrategy.java ================================================ /** * 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.expression.impl; import java.io.Serializable; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Map; import java.util.TimeZone; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.ExpressionExecutorStrategy; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Zhao na * @author Baptiste Mesta * @author Celine Souchet * @author Matthieu Chaffotte */ public class ConstantExpressionExecutorStrategy implements ExpressionExecutorStrategy { private static final String REGEX_PARSE_DATE = "(\\d{4})(-([01]\\d)((-([0-3]\\d)(T(\\d\\d):(\\d\\d)(((:(\\d\\d))?(\\.(\\d\\d))?(([\\+-])(\\d\\d):(\\d\\d))?)?)?)?)?)?)?"; @Override public void validate(final SExpression expression) throws SInvalidExpressionException { if ("".equals(expression.getContent().trim())) { throw new SInvalidExpressionException("The expression content cannot be empty. Expression : " + expression, expression.getName()); } } @Override public ExpressionKind getExpressionKind() { return KIND_CONSTANT; } @Override public Object evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final String expressionContent = expression.getContent(); Serializable result; final String returnType = expression.getReturnType(); // here need to improve try { if (Boolean.class.getName().equals(returnType)) { result = Boolean.parseBoolean(expressionContent); } else if (Long.class.getName().equals(returnType)) { result = Long.parseLong(expressionContent); } else if (Double.class.getName().equals(returnType)) { result = Double.parseDouble(expressionContent); } else if (Float.class.getName().equals(returnType)) { result = Float.parseFloat(expressionContent); } else if (Integer.class.getName().equals(returnType)) { result = Integer.parseInt(expressionContent); } else if (String.class.getName().equals(returnType)) { result = expressionContent; } else if (Date.class.getName().equals(returnType)) { // "2013-01-02T02:42:12.17+02:00" result = parseDate(expressionContent); } else if (LocalDate.class.getName().equals(returnType)) { result = LocalDate.parse(expressionContent); } else if (LocalDateTime.class.getName().equals(returnType)) { result = LocalDateTime.parse(expressionContent); } else if (OffsetDateTime.class.getName().equals(returnType)) { result = OffsetDateTime.parse(expressionContent); } else { throw new SExpressionEvaluationException( "Unknown return type: " + returnType + " for expression " + expression.getName() + " : " + expressionContent, expression.getName()); } } catch (final NumberFormatException e) { throw new SExpressionEvaluationException( "The content of the expression \"" + expression.getName() + "\" is not a number :" + expressionContent, e, expression.getName()); } return result; } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final List list = new ArrayList<>(expressions.size()); for (final SExpression expression : expressions) { list.add(evaluate(expression, context, resolvedExpressions, containerState)); } return list; } @Override public boolean mustPutEvaluatedExpressionInContext() { return false; } /** * @param dateToParse the date to parse, as a String * @return null if not a Date, new Date with properties is ISO format is recognized */ private Date parseDate(final String dateToParse) { if (dateToParse.matches(REGEX_PARSE_DATE)) { final Calendar calendar = Calendar.getInstance(); final String year = dateToParse.replaceFirst(REGEX_PARSE_DATE, "$1"); if (year != null && !year.isEmpty() && Integer.valueOf(year) > 1900) { calendar.set(Calendar.YEAR, Integer.valueOf(year)); } final String month = dateToParse.replaceFirst(REGEX_PARSE_DATE, "$3"); if (month != null && !month.isEmpty() && Integer.valueOf(month) < 13) { // MONTH value from 0 to 11 calendar.set(Calendar.MONTH, Integer.valueOf(month) - 1); } final String day = dateToParse.replaceFirst(REGEX_PARSE_DATE, "$6"); if (day != null && !day.isEmpty() && Integer.valueOf(day) < 32) { calendar.set(Calendar.DAY_OF_MONTH, Integer.valueOf(day)); } final String hour = dateToParse.replaceFirst(REGEX_PARSE_DATE, "$8"); if (hour != null && !hour.isEmpty() && Integer.valueOf(hour) < 24) { calendar.set(Calendar.HOUR_OF_DAY, Integer.valueOf(hour)); } final String minutes = dateToParse.replaceFirst(REGEX_PARSE_DATE, "$9"); if (minutes != null && !minutes.isEmpty() && Integer.valueOf(minutes) < 60) { calendar.set(Calendar.MINUTE, Integer.valueOf(minutes)); } final String seconds = dateToParse.replaceFirst(REGEX_PARSE_DATE, "$13"); if (seconds != null && !seconds.isEmpty() && Integer.valueOf(seconds) < 60) { calendar.set(Calendar.SECOND, Integer.valueOf(seconds)); } final String fractional = dateToParse.replaceFirst(REGEX_PARSE_DATE, "$15"); if (fractional != null && !fractional.isEmpty() && Integer.valueOf(fractional) < 60) { calendar.set(Calendar.MILLISECOND, Integer.valueOf(fractional)); } final String tzSign = dateToParse.replaceFirst(REGEX_PARSE_DATE, "$17"); final String tzHour = dateToParse.replaceFirst(REGEX_PARSE_DATE, "$18"); final String tzMinutes = dateToParse.replaceFirst(REGEX_PARSE_DATE, "$19"); final TimeZone tz = TimeZone.getTimeZone("GMT" + tzSign + tzHour + tzMinutes); if (!tzSign.isEmpty() && !tzHour.isEmpty() && !tzMinutes.isEmpty() && tz != null) { calendar.setTimeZone(tz); } return calendar.getTime(); } return null; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/ExpressionServiceImpl.java ================================================ /** * 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.expression.impl; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.commons.LogUtil; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.ExpressionExecutorStrategy; import org.bonitasoft.engine.expression.ExpressionService; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionTypeUnknownException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.tracking.TimeTracker; import org.bonitasoft.engine.tracking.TimeTrackerRecords; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Zhao na * @author Emmanuel Duchastenier * @author Baptiste Mesta * @author Celine Souchet */ public class ExpressionServiceImpl implements ExpressionService { private static final Logger log = LoggerFactory.getLogger(ExpressionServiceImpl.class); private final Map expressionExecutorsMap = new HashMap<>(); private boolean checkExpressionReturnType = false; private final TimeTracker timeTracker; public ExpressionServiceImpl(final boolean checkExpressionReturnType, final TimeTracker timeTracker) { super(); this.checkExpressionReturnType = checkExpressionReturnType; this.timeTracker = timeTracker; } @Override public void setExpressionExecutorStrategy(List expressionExecutors) { for (final ExpressionExecutorStrategy expressionExecutorStrategy : expressionExecutors) { expressionExecutorsMap.put(expressionExecutorStrategy.getExpressionKind(), expressionExecutorStrategy); } } @Override public Object evaluate(final SExpression expression, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { return evaluate(expression, new HashMap(1), resolvedExpressions, containerState); } @Override public Object evaluate(final SExpression expression, final Map dependencyValues, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException, SInvalidExpressionException { if (log.isTraceEnabled()) { log.trace(LogUtil.getLogBeforeMethod(this.getClass(), "evaluate")); } final ExpressionExecutorStrategy expressionExecutorStrategy = getStrategy(expression.getExpressionKind()); validateExpression(expressionExecutorStrategy, expression); Object expressionResult = null; final long startTime = System.currentTimeMillis(); try { expressionResult = expressionExecutorStrategy.evaluate(expression, dependencyValues, resolvedExpressions, containerState); } finally { if (timeTracker.isTrackable(TimeTrackerRecords.EVALUATE_EXPRESSION)) { final long endTime = System.currentTimeMillis(); timeTracker.track(TimeTrackerRecords.EVALUATE_EXPRESSION, "Expression: " + expression + " - " + "dependencyValues: " + dependencyValues + " - " + "strategy: " + expressionExecutorStrategy, endTime - startTime); } } if (mustCheckExpressionReturnType()) { new ReturnTypeChecker().checkReturnType(expression, expressionResult, dependencyValues); } if (log.isTraceEnabled()) { log.trace(LogUtil.getLogAfterMethod(this.getClass(), "evaluate")); } return expressionResult; } private void validateExpression(final ExpressionExecutorStrategy expressionExecutorStrategy, final SExpression expression) throws SInvalidExpressionException { try { // this will throw exception if the expression is invalid expressionExecutorStrategy.validate(expression); } catch (final SInvalidExpressionException e) { log.trace(LogUtil.getLogOnExceptionMethod(this.getClass(), "evaluate", "Invalid Expression : " + expression.getContent())); throw e; } } private ExpressionExecutorStrategy getStrategy(final ExpressionKind expressionKind) throws SExpressionTypeUnknownException { final ExpressionExecutorStrategy expressionExecutorStrategy = expressionExecutorsMap.get(expressionKind); if (expressionExecutorStrategy == null) { log.trace(LogUtil.getLogOnExceptionMethod(this.getClass(), "evaluate", "Unable to find an executor for expression type " + expressionKind)); throw new SExpressionTypeUnknownException( "Unable to find an executor for expression type " + expressionKind); } return expressionExecutorStrategy; } @Override public List evaluate(final ExpressionKind expressionKind, final List expressions, final Map dependencyValues, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionTypeUnknownException, SExpressionEvaluationException, SExpressionDependencyMissingException { if (log.isTraceEnabled()) { log.trace(LogUtil.getLogBeforeMethod(this.getClass(), "evaluate")); } final ExpressionExecutorStrategy expressionExecutorStrategy = getStrategy(expressionKind); List list = null; final long startTime = System.currentTimeMillis(); try { list = expressionExecutorStrategy.evaluate(expressions, dependencyValues, resolvedExpressions, containerState); } finally { if (timeTracker.isTrackable(TimeTrackerRecords.EVALUATE_EXPRESSIONS)) { final long endTime = System.currentTimeMillis(); timeTracker.track(TimeTrackerRecords.EVALUATE_EXPRESSIONS, "Expressions: " + expressions + " - " + "dependencyValues: " + dependencyValues + " - " + "strategy: " + expressionExecutorStrategy, endTime - startTime); } } if (list == null || list.size() != expressions.size()) { final String exceptionMessage = "Result list size " + (list == null ? 0 : list.size()) + " is different from expression list size " + expressions.size(); if (log.isTraceEnabled()) { log.trace(LogUtil.getLogOnExceptionMethod(this.getClass(), "evaluate", exceptionMessage)); } throw new SExpressionEvaluationException(exceptionMessage, null); } if (mustCheckExpressionReturnType()) { for (int i = 0; i < list.size(); i++) { new ReturnTypeChecker().checkReturnType(expressions.get(i), list.get(i), dependencyValues); } } if (log.isTraceEnabled()) { log.trace(LogUtil.getLogAfterMethod(this.getClass(), "evaluate")); } return list; } @Override public boolean mustCheckExpressionReturnType() { return checkExpressionReturnType; } @Override public boolean mustPutEvaluatedExpressionInContext(final ExpressionKind expressionKind) { return expressionExecutorsMap.get(expressionKind).mustPutEvaluatedExpressionInContext(); } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/GroovyScriptConditionExpressionExecutorStrategy.java ================================================ /** * 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.expression.impl; import java.util.Map; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; public class GroovyScriptConditionExpressionExecutorStrategy extends GroovyScriptExpressionExecutorCacheStrategy { public GroovyScriptConditionExpressionExecutorStrategy(CacheService cacheService, ClassLoaderService classLoaderService) { super(cacheService, classLoaderService); } @Override public ExpressionKind getExpressionKind() { return KIND_READ_ONLY_CONDITION_SCRIPT_GROOVY; } @Override public Object evaluate(SExpression expression, Map context, Map resolvedExpressions, ContainerState containerState) throws SExpressionEvaluationException { Object result = super.evaluate(expression, context, resolvedExpressions, containerState); return result instanceof Boolean ? result : result != null; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/GroovyScriptExpressionExecutorCacheStrategy.java ================================================ /** * 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.expression.impl; import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicLong; import groovy.lang.Binding; import groovy.lang.GroovyCodeSource; import groovy.lang.GroovyRuntimeException; import groovy.lang.GroovyShell; import groovy.lang.MissingPropertyException; import groovy.lang.Script; import org.bonitasoft.engine.cache.CacheService; import org.bonitasoft.engine.cache.SCacheException; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.classloader.SClassLoaderException; import org.bonitasoft.engine.classloader.SingleClassLoaderListener; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.dependency.model.ScopeType; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.NonEmptyContentExpressionExecutorStrategy; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; import org.codehaus.groovy.runtime.InvokerHelper; import org.codehaus.groovy.runtime.typehandling.GroovyCastException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class GroovyScriptExpressionExecutorCacheStrategy extends NonEmptyContentExpressionExecutorStrategy implements SingleClassLoaderListener { private static final Logger log = LoggerFactory.getLogger(GroovyScriptExpressionExecutorCacheStrategy.class); public static final String GROOVY_SCRIPT_CACHE_NAME = "GROOVY_SCRIPT_CACHE_NAME"; public static final String SCRIPT_KEY = "SCRIPT_"; public static final String COERCION_SCRIPT_KEY = "COERCION_SCRIPT_"; public static final String SHELL_KEY = "SHELL_"; private final CacheService cacheService; private final ClassLoaderService classLoaderService; private static final AtomicLong counter = new AtomicLong(); public GroovyScriptExpressionExecutorCacheStrategy(final CacheService cacheService, final ClassLoaderService classLoaderService) { this.cacheService = cacheService; this.classLoaderService = classLoaderService; } private String generateScriptName() { return String.format("BScript%s.groovy", counter.incrementAndGet()); } Class getScriptFromCache(final String expressionContent, final Long definitionId) throws SCacheException, SClassLoaderException { if (definitionId == null) { throw new SBonitaRuntimeException("Unable to evaluate expression without a definitionId"); } final GroovyShell shell = getShell(definitionId); GroovyCodeSource gcs = getOrCreateGroovyCodeSource(SCRIPT_KEY + expressionContent.hashCode(), expressionContent); // parse the groovy source code with cache set to true return shell.getClassLoader().parseClass(gcs, true); } private GroovyCodeSource getOrCreateGroovyCodeSource(String key, String scriptContent) throws SCacheException { GroovyCodeSource gcs = (GroovyCodeSource) cacheService.get(GROOVY_SCRIPT_CACHE_NAME, key); if (gcs == null) { gcs = AccessController .doPrivileged((PrivilegedAction) () -> new GroovyCodeSource(scriptContent, generateScriptName(), GroovyShell.DEFAULT_CODE_BASE)); cacheService.store(GROOVY_SCRIPT_CACHE_NAME, key, gcs); } return gcs; } GroovyShell getShell(final Long definitionId) throws SClassLoaderException, SCacheException { String key = SHELL_KEY + definitionId; GroovyShell shell = (GroovyShell) cacheService.get(GROOVY_SCRIPT_CACHE_NAME, key); if (shell == null) { ClassLoader classLoader = getClassLoaderForShell(definitionId); log.debug("Create a new groovy classloader for {} {}", definitionId, classLoader); shell = new GroovyShell(classLoader); cacheService.store(GROOVY_SCRIPT_CACHE_NAME, key, shell); } return shell; } private ClassLoader getClassLoaderForShell(Long definitionId) throws SClassLoaderException { ClassLoader classLoader; if (definitionId == null) { classLoader = Thread.currentThread().getContextClassLoader(); //do not has listener, should not happen... if (log.isDebugEnabled()) { IllegalStateException illegalStateException = new IllegalStateException(); log.debug("Creating a shell without definition id, might cause issue when reloading classes {}", illegalStateException.getMessage()); } } else { classLoader = classLoaderService.getClassLoader(identifier(ScopeType.PROCESS, definitionId)); classLoaderService.addListener(identifier(ScopeType.PROCESS, definitionId), this); } return classLoader; } @Override public Object evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final String expressionContent = expression.getContent(); final String expressionName = expression.getName(); try { final Binding binding = new Binding(context); Long definitionId = (Long) context.get(DEFINITION_ID); final Script script = InvokerHelper .createScript(getScriptFromCache(expressionContent, definitionId), binding); script.setBinding(binding); return coerceResult(getShell(definitionId), script.run(), expression.getReturnType()); } catch (final MissingPropertyException e) { final String property = e.getProperty(); throw new SExpressionEvaluationException("Expression " + expressionName + " with content = <" + expressionContent + "> depends on " + property + " is neither defined in the script nor in dependencies.", e, expressionName); } catch (final GroovyRuntimeException e) { throw new SExpressionEvaluationException(e, expressionName); } catch (final SCacheException e) { throw new SExpressionEvaluationException( "Problem accessing the Script Cache from GroovyScriptExpressionExecutorCacheStrategy.", e, expressionName); } catch (final SClassLoaderException e) { throw new SExpressionEvaluationException( "Unable to retrieve the correct classloader to execute the groovy script : " + expression, e, expressionName); } catch (final Throwable e) { //catch throwable because we do not handle contents of scripts String message = e.getMessage(); if (message == null || message.isEmpty()) { message = "No message"; } throw new SExpressionEvaluationException( "Groovy script throws an exception of type " + e.getClass() + " with message = " + message + System.lineSeparator() + "Expression : " + expression, e, expressionName); } } @Override public void onUpdate(ClassLoader newClassLoader) { log.debug("Groovy cache cleared after update on {}", newClassLoader); clearCache(); } @Override public void onDestroy(ClassLoader oldClassLoader) { log.debug("Groovy cache cleared after destroy of {}", oldClassLoader); clearCache(); } private void clearCache() { try { cacheService.clear(GROOVY_SCRIPT_CACHE_NAME); } catch (SCacheException e) { log.error( "error while clearing the cache of the groovy script executor strategy, you might have classloading issue, restart the server if it's the case", e); } } @Override public ExpressionKind getExpressionKind() { return KIND_READ_ONLY_SCRIPT_GROOVY; } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException, SExpressionDependencyMissingException { final List list = new ArrayList<>(expressions.size()); for (final SExpression expression : expressions) { list.add(evaluate(expression, context, resolvedExpressions, containerState)); } return list; } /** * Execute a Groovy expression that coerce the result into the returnType * * @param shell, the Groovy shell use for script evaluation * @param result, the evaluation result * @param returnType, expected expression return type * @return the result with the expected type or a {@link GroovyCastException} if the coercion fails * @throws ClassNotFoundException */ protected Object coerceResult(GroovyShell shell, Object result, String returnType) throws ClassNotFoundException, SCacheException { if (result == null) { return null; } String resultClassName = result.getClass().getName(); if (Objects.equals(resultClassName, returnType) // Already in the expected type || ReturnTypeChecker.isConvertible(returnType, resultClassName)) { // Bonita specific type conversion return result; } String scriptContent = String.format("result as %s", returnType.startsWith("[") ? canonicalClassName(returnType) : returnType); GroovyCodeSource gcs = getOrCreateGroovyCodeSource(COERCION_SCRIPT_KEY + returnType, scriptContent); Binding binding = new Binding(); binding.setVariable("result", result); Script script = InvokerHelper .createScript(shell.getClassLoader().parseClass(gcs, true), binding); return script.run(); } private String canonicalClassName(String returnType) throws ClassNotFoundException { return Class.forName(returnType).getCanonicalName(); } @Override public boolean mustPutEvaluatedExpressionInContext() { return false; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/InputExpressionExecutorStrategy.java ================================================ /** * 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.expression.impl; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.NonEmptyContentExpressionExecutorStrategy; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Zhao na * @author Emmanuel Duchastenier * @author Matthieu Chaffotte * @author Celine Souchet */ public class InputExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy { @Override public Object evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { if (context != null && !context.isEmpty()) { final String key = expression.getContent(); if (context.containsKey(key)) { return context.get(key); } } throw new SExpressionEvaluationException( "No value found for mandatory expression '" + expression.getContent() + "' of type Input Expression", expression.getName()); } @Override public ExpressionKind getExpressionKind() { return KIND_INPUT; } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final List list = new ArrayList(expressions.size()); for (final SExpression expression : expressions) { list.add(evaluate(expression, context, resolvedExpressions, containerState)); } return list; } @Override public boolean mustPutEvaluatedExpressionInContext() { return false; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/JavaMethodCallExpressionExecutorStrategy.java ================================================ /** * 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.expression.impl; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.commons.ClassReflector; import org.bonitasoft.engine.commons.exceptions.SReflectException; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.NonEmptyContentExpressionExecutorStrategy; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public class JavaMethodCallExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy { @Override public Object evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final SExpression dependency = expression.getDependencies().get(0); final Object object = resolvedExpressions.get(dependency.getDiscriminant()); try { return ClassReflector.invokeGetter(object, expression.getContent()); } catch (final SReflectException e) { throw new SExpressionEvaluationException(e, expression.getName()); } } @Override public void validate(final SExpression expression) throws SInvalidExpressionException { super.validate(expression); if (expression.getDependencies() == null || expression.getDependencies().size() != 1) { throw new SInvalidExpressionException("An expression of type " + TYPE_JAVA_METHOD_CALL + " must have exactly one dependency. This dependency represents the object where the method will be called. Expression :" + expression, expression.getName()); } } @Override public ExpressionKind getExpressionKind() { return KIND_JAVA_METHOD_CALL; } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { final List result = new ArrayList(2); for (final SExpression expression : expressions) { result.add(evaluate(expression, context, resolvedExpressions, containerState)); } return result; } @Override public boolean mustPutEvaluatedExpressionInContext() { return false; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/ListExpressionExecutorStrategy.java ================================================ /** * 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.expression.impl; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.ExpressionExecutorStrategy; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; /** * Evaluate a list of SExpression, represented by the {@link SExpression#getDependencies()} method. The result is a * {@link java.util.List} of Serializable * objects. * * @author Emmanuel Duchastenier * @author Celine Souchet */ public class ListExpressionExecutorStrategy implements ExpressionExecutorStrategy { @Override public void validate(final SExpression expression) { // nothing to validate, as Business logic resides in dependencies: } @Override public ExpressionKind getExpressionKind() { return KIND_LIST; } @Override public Serializable evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) { final List result = new ArrayList(expression.getDependencies().size()); for (final SExpression exp : expression.getDependencies()) { result.add(resolvedExpressions.get(exp.getDiscriminant())); } // Let's put this ListExpression ExpressionResult in the list of resolved dependencies: resolvedExpressions.put(expression.getDiscriminant(), result); return (Serializable) result; } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) { final List list = new ArrayList(expressions.size()); for (final SExpression expression : expressions) { list.add(evaluate(expression, context, resolvedExpressions, containerState)); } return list; } @Override public boolean mustPutEvaluatedExpressionInContext() { return false; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/PatternExpressionExecutorStrategy.java ================================================ /** * 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.expression.impl; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.text.StringSubstitutor; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.NonEmptyContentExpressionExecutorStrategy; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; /** * ExpressionExecutorStrategy to evaluate a 'Pattern', that is a String message into which some parameters * are replaced by their values. * * @author Emmanuel Duchastenier */ public class PatternExpressionExecutorStrategy extends NonEmptyContentExpressionExecutorStrategy { @Override public Serializable evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionDependencyMissingException { final List dependencies = expression.getDependencies(); final Map values = new HashMap(dependencies.size()); for (final SExpression exp : dependencies) { final String name = exp.getName(); final Object value = resolvedExpressions.get(exp.getDiscriminant()); if (value == null) { throw new SExpressionDependencyMissingException("Expression dependency not found: " + name); } values.put(name, value); } final StringSubstitutor stringSubstitutor = new StringSubstitutor(values); return stringSubstitutor.replace(expression.getContent()); } @Override public ExpressionKind getExpressionKind() { return KIND_PATTERN; } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionDependencyMissingException { final List list = new ArrayList(expressions.size()); for (final SExpression expression : expressions) { list.add(evaluate(expression, context, resolvedExpressions, containerState)); } return list; } @Override public boolean mustPutEvaluatedExpressionInContext() { return false; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/ReturnTypeChecker.java ================================================ /** * 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.expression.impl; import static java.lang.String.format; import static java.util.Arrays.asList; import static java.util.Optional.ofNullable; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Emmanuel Duchastenier */ public class ReturnTypeChecker { private static final String CONTAINER_ID = "containerId"; private static final String CONTAINER_TYPE = "containerType"; private static final String ACTIVITY_INSTANCE_SCOPE = "ACTIVITY_INSTANCE"; private static final String PROCESS_INSTANCE_SCOPE = "PROCESS_INSTANCE"; /** * Describes types that can be be converted later to other types.
      *
    • key: the type which can be converted to *
    • values: the input types that can be converted to the key type */ private static final Map> CONVERTIBLE_TYPES = new HashMap<>(); static { CONVERTIBLE_TYPES.put("org.bonitasoft.engine.bpm.document.Document", asList("org.bonitasoft.engine.bpm.contract.FileInputValue")); CONVERTIBLE_TYPES.put("org.bonitasoft.engine.bpm.document.DocumentValue", asList("org.bonitasoft.engine.bpm.contract.FileInputValue")); } /** * Check if the declared return type is compatible with the real Expression evaluation return type. If the result of * the Expression evaluation is null, then this method returns true. * * @param expression * the evaluated expression * @param result * the expression result to check * @throws SExpressionEvaluationException * if the condition is not fulfilled, does nothing otherwise */ public void checkReturnType(final SExpression expression, final Object result, final Map context) throws SExpressionEvaluationException { if (result == null) { return; } final String declaredReturnType = expression.getReturnType(); final String evaluatedClassName = result.getClass().getName(); if (!evaluatedClassName.equals(declaredReturnType)) { if (isConvertible(declaredReturnType, evaluatedClassName)) { return; } try { final String expressionName = expression.getName(); try { final Class declaredReturnedClass = getClazz(declaredReturnType); final Class evaluatedClass = result.getClass(); if (!declaredReturnedClass.isAssignableFrom(evaluatedClass)) { throw new SExpressionEvaluationException(format( "Declared return type %s is not compatible with evaluated type %s for expression %s", declaredReturnedClass, evaluatedClass, expressionName), expressionName); } } catch (final ClassNotFoundException e) { throw new SExpressionEvaluationException( format("Unknown declared return type %s for expression %s", declaredReturnType, expressionName), e, expressionName); } } catch (final SExpressionEvaluationException e) { if (isContextOnActivity(context)) { e.setFlowNodeInstanceIdOnContext((Long) context.get(CONTAINER_ID)); } if (isContextOnProcess(context)) { e.setProcessInstanceIdOnContext((Long) context.get(CONTAINER_ID)); } throw e; } } } private static Class getClazz(String type) throws ClassNotFoundException { return Thread.currentThread().getContextClassLoader().loadClass(type); } public static boolean isConvertible(String targetType, String sourceType) { return ofNullable(CONVERTIBLE_TYPES.get(targetType)).map(targets -> targets.contains(sourceType)).orElse(false); } private static boolean isContextOnProcess(final Map context) { return context.containsKey(CONTAINER_TYPE) && PROCESS_INSTANCE_SCOPE.equals(context.get(CONTAINER_TYPE)); } private static boolean isContextOnActivity(final Map context) { return context.containsKey(CONTAINER_TYPE) && ACTIVITY_INSTANCE_SCOPE.equals(context.get(CONTAINER_TYPE)); } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/XPathReadExpressionExecutorStrategy.java ================================================ /** * 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.expression.impl; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.xml.XMLConstants; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.ExpressionExecutorStrategy; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * Supported return types are: *
        *
      • java.lang.String
      • *
      • java.lang.Double
      • *
      • java.lang.Boolean
      • *
      • java.lang.Long
      • *
      • java.lang.Integer
      • *
      • java.lang.Float
      • *
      • org.w3c.dom.Node
      • *
      • org.w3c.dom.NodeList
      • *
      * * @author Emmanuel Duchastenier * @author Matthieu Chaffotte * @author Celine Souchet */ public class XPathReadExpressionExecutorStrategy implements ExpressionExecutorStrategy { @Override public Object evaluate(final SExpression expression, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException, SExpressionDependencyMissingException { if (expression.getDependencies().size() != 1 || expression.getDependencies().get(0) == null) { throw new SExpressionDependencyMissingException( "XPathReadExpressionExecutorStrategy must have exactly one dependency"); } final String expressionName = expression.getName(); final String returnType = expression.getReturnType(); final String messageForException = "Error evaluating expression " + expression + " with strategy XPathReadExpressionExecutorStrategy"; try { final QName qname = getXPathConstants(returnType); if (qname == null) { throw new SExpressionEvaluationException( "XPathReadExpressionExecutorStrategy return type not supported: " + expression.getReturnType(), expressionName); } final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); // security-compliant factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); // security-compliant } catch (IllegalArgumentException e) { //ignored, if not supported by the implementation } final DocumentBuilder builder = factory.newDocumentBuilder(); // Check has already been done above: final SExpression dep = expression.getDependencies().get(0); final String xmlContent = (String) resolvedExpressions.get(dep.getDiscriminant()); if (xmlContent == null || xmlContent.isEmpty()) { throw new SExpressionEvaluationException("The content of the xml is nul or empty: " + expression, expressionName); } final Document document = builder.parse(new InputSource(new StringReader(xmlContent))); final XPathFactory xpFactory = XPathFactory.newInstance(); final XPath xpath = xpFactory.newXPath(); final XPathExpression exp = xpath.compile(expression.getContent()); return transType(exp.evaluate(document, qname), returnType); } catch (final XPathExpressionException | SBonitaRuntimeException | IOException | SAXException | ParserConfigurationException e) { throw new SExpressionEvaluationException(messageForException, e, expressionName); } } private Object transType(final Object result, final String returnType) { try { if (Boolean.class.getName().equals(returnType)) { return result != null && ("true".equalsIgnoreCase((String) result) || "1".equals(result)); } if (Long.class.getName().equals(returnType)) { return Long.parseLong((String) result); } if (Double.class.getName().equals(returnType)) { return Double.parseDouble((String) result); } if (Float.class.getName().equals(returnType)) { return Float.parseFloat((String) result); } if (Integer.class.getName().equals(returnType)) { return Integer.parseInt((String) result); } if (String.class.getName().equals(returnType)) { return result; } } catch (final NumberFormatException e) { throw new SBonitaRuntimeException("Wrong format for " + returnType + " value was " + result, e); } return result; } private QName getXPathConstants(final String expReturnType) { if (String.class.getName().equals(expReturnType)) { return XPathConstants.STRING; } else if (Long.class.getName().equals(expReturnType) || Double.class.getName().equals(expReturnType) || Float.class.getName().equals(expReturnType) || Integer.class.getName().equals(expReturnType)) { return XPathConstants.STRING; } else if (Boolean.class.getName().equals(expReturnType)) { return XPathConstants.STRING; } else if (Node.class.getName().equals(expReturnType)) { return XPathConstants.NODE; } else if (NodeList.class.getName().equals(expReturnType)) { return XPathConstants.NODESET; } return null; } @Override public void validate(final SExpression expression) throws SInvalidExpressionException { if (expression == null) { throw new SInvalidExpressionException("The expression cannot be null.", null); } final String expressionContent = expression.getContent(); if (expressionContent == null) { throw new SInvalidExpressionException("The expression content cannot be null : " + expression, expression.getName()); } } @Override public ExpressionKind getExpressionKind() { return KIND_XPATH_READ; } @Override public List evaluate(final List expressions, final Map context, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException, SExpressionDependencyMissingException { final List list = new ArrayList<>(expressions.size()); for (final SExpression expression : expressions) { list.add(evaluate(expression, context, resolvedExpressions, containerState)); } return list; } @Override public boolean mustPutEvaluatedExpressionInContext() { // false because expression can't be referenced using it's content return false; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/BinaryComparator.java ================================================ /** * 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.expression.impl.condition; /** * @author Elias Ricken de Medeiros */ public interface BinaryComparator { public Boolean evaluate(final T left, final T right) throws SComparisonException; } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/BinaryComparatorExecutor.java ================================================ /** * 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.expression.impl.condition; import java.math.BigDecimal; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.impl.ConditionExpressionExecutorStrategy; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros */ public class BinaryComparatorExecutor { private final BinaryComparatorMapper mapper; private final Set numericTypes; public BinaryComparatorExecutor(BinaryComparatorMapper mapper) { this.mapper = mapper; numericTypes = new HashSet(); numericTypes.add(Integer.class.getName()); numericTypes.add(Long.class.getName()); numericTypes.add(Double.class.getName()); numericTypes.add(Float.class.getName()); numericTypes.add(Short.class.getName()); numericTypes.add(Byte.class.getName()); numericTypes.add(int.class.getName()); numericTypes.add(long.class.getName()); numericTypes.add(double.class.getName()); numericTypes.add(float.class.getName()); numericTypes.add(short.class.getName()); numericTypes.add(byte.class.getName()); } public Boolean evaluate(final Map resolvedExpressions, SExpression expression) throws SExpressionEvaluationException { validate(expression); BinaryComparator evaluator = mapper.getEvaluator(expression.getContent()); if (evaluator == null) { throw new SExpressionEvaluationException( "Unable to find evaluator for operator '" + expression.getContent() + "'", expression.getName()); } SExpression leftExpression = expression.getDependencies().get(0); SExpression rightExpression = expression.getDependencies().get(1); final Object resolvedLeftExpr = transtypeIfApplicable(leftExpression.getReturnType(), resolvedExpressions.get(leftExpression.getDiscriminant())); final Object resolvedRightExpr = transtypeIfApplicable(rightExpression.getReturnType(), resolvedExpressions.get(rightExpression.getDiscriminant())); try { return evaluator.evaluate(resolvedLeftExpr, resolvedRightExpr); } catch (SComparisonException e) { throw new SExpressionEvaluationException("Unable to evaluate expression '" + expression.getName() + "'", e, expression.getName()); } } /* * Transtype integer to long and float to double * see bug ENGINE-1261 */ protected Object transtypeIfApplicable(final String type, final Object object) { if (object == null) { return null; } if (numericTypes.contains(type)) { return new BigDecimal(String.valueOf(object)); } return object; } private void validate(SExpression expression) throws SExpressionEvaluationException { validateNumberOfDependencies(expression); validateReturnType(expression); } private void validateReturnType(final SExpression expression) throws SExpressionEvaluationException { List dependencies = expression.getDependencies(); if (!areReturnTypeCompatible(dependencies.get(0).getReturnType(), dependencies.get(1).getReturnType(), expression.getContent())) { throw new SExpressionEvaluationException( "The two dependencies of expression '" + expression.getContent() + "' must have the same return type.", expression.getName()); } } private void validateNumberOfDependencies(final SExpression expression) throws SExpressionEvaluationException { int expectedNbOfDep = 2; int actualNbOfDep = expression.getDependencies().size(); if (actualNbOfDep != expectedNbOfDep) { StringBuilder stb = new StringBuilder(); stb.append("The expression '"); stb.append(expression.getContent()); stb.append("' has "); stb.append(actualNbOfDep); stb.append(" dependencies, but it must have exactly "); stb.append(expectedNbOfDep); stb.append(" dependencies."); throw new SExpressionEvaluationException(stb.toString(), expression.getName()); } } protected boolean areReturnTypeCompatible(String leftReturnType, String rightReturnType, final String content) { return (numericTypes.contains(leftReturnType) && numericTypes.contains(rightReturnType)) || leftReturnType.equals(rightReturnType) || ConditionExpressionExecutorStrategy.EQUALS_COMPARATOR.equals(content); } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/BinaryComparatorMapper.java ================================================ /** * 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.expression.impl.condition; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.expression.impl.ConditionExpressionExecutorStrategy; /** * @author Elias Ricken de Medeiros */ public class BinaryComparatorMapper { private Map evaluators; public BinaryComparatorMapper(EqualityComparator equalityComparator, InequalityComparator inequalityComparator) { evaluators = new HashMap(); evaluators.put(ConditionExpressionExecutorStrategy.EQUALS_COMPARATOR, new EqualsComparator(equalityComparator)); evaluators.put(ConditionExpressionExecutorStrategy.NOT_EQUALS_COMPARATOR, new DifferentComparator(equalityComparator)); evaluators.put(ConditionExpressionExecutorStrategy.GREATER_THAN_COMPARATOR, new GreaterThanComparator(inequalityComparator)); evaluators.put(ConditionExpressionExecutorStrategy.GREATER_THAN_OR_EQUALS_COMPARATOR, new GreaterThanOrEqualsComparator(inequalityComparator)); evaluators.put(ConditionExpressionExecutorStrategy.LESS_THAN_COMPARATOR, new LessThanComparator(inequalityComparator)); evaluators.put(ConditionExpressionExecutorStrategy.lESS_THAN_OR_EQUALS_COMPARATOR, new LessThanOrEqualsComparator(inequalityComparator)); } public BinaryComparator getEvaluator(String operator) { return evaluators.get(operator); } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/DifferentComparator.java ================================================ /** * 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.expression.impl.condition; /** * @author Elias Ricken de Medeiros */ public class DifferentComparator implements BinaryComparator { private final EqualityComparator equalityComparator; public DifferentComparator(EqualityComparator equalityComparator) { this.equalityComparator = equalityComparator; } @Override public Boolean evaluate(final T left, final T right) { return !equalityComparator.areEquals(left, right); } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/EqualityComparator.java ================================================ /** * 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.expression.impl.condition; /** * @author Elias Ricken de Medeiros */ public class EqualityComparator { public Boolean areEquals(final T left, final T right) { if (left == null) { return right == null; } return left.equals(right); } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/EqualsComparator.java ================================================ /** * 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.expression.impl.condition; /** * @author Elias Ricken de Medeiros */ public class EqualsComparator implements BinaryComparator { private final EqualityComparator equalityComparator; public EqualsComparator(EqualityComparator equalityComparator) { this.equalityComparator = equalityComparator; } @Override public Boolean evaluate(final T left, final T right) { return equalityComparator.areEquals(left, right); } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/GreaterThanComparator.java ================================================ /** * 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.expression.impl.condition; /** * @author Elias Ricken de Medeiros */ public class GreaterThanComparator implements BinaryComparator { private final InequalityComparator comparator; public GreaterThanComparator(InequalityComparator comparator) { this.comparator = comparator; } @Override public Boolean evaluate(final T left, final T right) throws SComparisonException { Integer compare = comparator.compareTo(left, right); return compare == null ? null : compare > 0; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/GreaterThanOrEqualsComparator.java ================================================ /** * 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.expression.impl.condition; /** * @author Elias Ricken de Medeiros */ public class GreaterThanOrEqualsComparator implements BinaryComparator { private final InequalityComparator comparator; public GreaterThanOrEqualsComparator(InequalityComparator comparator) { this.comparator = comparator; } @Override public Boolean evaluate(final T left, final T right) throws SComparisonException { Integer compare = comparator.compareTo(left, right); return compare == null ? null : compare >= 0; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/InequalityComparator.java ================================================ /** * 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.expression.impl.condition; /** * @author Elias Ricken de Medeiros */ public class InequalityComparator { public Integer compareTo(T left, T right) throws SComparisonException { if (left == null || right == null) { return null; } if (!(left instanceof Comparable) || !(right instanceof Comparable)) { throw new SComparisonException( "The following class must implement java.lang.Comparable: " + left.getClass().getName()); } return compare((Comparable) left, (Comparable) right); } private , R extends Comparable> Integer compare(final T left, final R right) { return left.compareTo(right); } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/LessThanComparator.java ================================================ /** * 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.expression.impl.condition; /** * @author Elias Ricken de Medeiros */ public class LessThanComparator implements BinaryComparator { private final InequalityComparator comparator; public LessThanComparator(InequalityComparator comparator) { this.comparator = comparator; } @Override public Boolean evaluate(final T left, final T right) throws SComparisonException { Integer compare = comparator.compareTo(left, right); return compare == null ? null : compare < 0; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/LessThanOrEqualsComparator.java ================================================ /** * 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.expression.impl.condition; /** * @author Elias Ricken de Medeiros */ public class LessThanOrEqualsComparator implements BinaryComparator { private final InequalityComparator comparator; public LessThanOrEqualsComparator(InequalityComparator comparator) { this.comparator = comparator; } @Override public Boolean evaluate(final T left, final T right) throws SComparisonException { Integer compare = comparator.compareTo(left, right); return compare == null ? null : compare <= 0; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/LogicalComplementExecutor.java ================================================ /** * 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.expression.impl.condition; import java.util.List; import java.util.Map; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.impl.ConditionExpressionExecutorStrategy; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Elias Ricken de Medeiros */ public class LogicalComplementExecutor { public Boolean evaluate(final Map resolvedExpressions, SExpression expression) throws SExpressionEvaluationException { validate(expression); List dependencies = expression.getDependencies(); Boolean sourceValue = (Boolean) resolvedExpressions.get(dependencies.get(0).getDiscriminant()); return sourceValue != null ? !sourceValue : null; } private void validate(SExpression expression) throws SExpressionEvaluationException { List dependencies = expression.getDependencies(); if (dependencies.size() != 1) { throw new SExpressionEvaluationException( "The expression '" + ConditionExpressionExecutorStrategy.LOGICAL_COMPLEMENT_OPERATOR + "' must have exactly 1 dependency.", expression.getName()); } if (!Boolean.class.getName().equals(dependencies.get(0).getReturnType())) { StringBuilder stb = new StringBuilder(); stb.append("The dependency of expression '"); stb.append(ConditionExpressionExecutorStrategy.LOGICAL_COMPLEMENT_OPERATOR); stb.append("' must have the return type "); stb.append(Boolean.class.getName()); stb.append(", but "); stb.append(dependencies.get(0).getReturnType()); stb.append(" was found."); throw new SExpressionEvaluationException(stb.toString(), expression.getName()); } } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/impl/condition/SComparisonException.java ================================================ /** * 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.expression.impl.condition; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SComparisonException extends SBonitaException { public SComparisonException(final String message) { super(message); } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/ExpressionKind.java ================================================ /** * 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.expression.model; import java.io.Serializable; /** * Used to identify a kind of expression * e.g. * for constant kind is : [ type = TYPE_CONSTANT, interpreter = null ] * for groovy kind is : [ type = TYPE_READ_SCRIPT, interpreter = GROOVY ] * * @author Baptiste Mesta */ public class ExpressionKind implements Serializable { private static final long serialVersionUID = 1L; public static final String NONE = "NONE"; private String interpreter = NONE; private String type; public ExpressionKind() { } public ExpressionKind(final String type) { this.type = type; interpreter = NONE; } public ExpressionKind(final String type, final String interpreter) { this.type = type; this.interpreter = interpreter == null || interpreter.isEmpty() ? NONE : interpreter; } public String getExpressionType() { return type; } public String getInterpreter() { return interpreter; } public String getType() { return type; } public void setType(final String type) { this.type = type; } public void setInterpreter(final String interpreter) { this.interpreter = interpreter == null || interpreter.isEmpty() ? NONE : interpreter; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (interpreter == null ? 0 : interpreter.hashCode()); result = prime * result + (type == null ? 0 : type.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final ExpressionKind other = (ExpressionKind) obj; if (interpreter == null) { if (other.interpreter != null) { return false; } } else if (!interpreter.equals(other.interpreter)) { return false; } if (type == null) { if (other.type != null) { return false; } } else if (!type.equals(other.type)) { return false; } return true; } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("ExpressionKind [interpreter="); builder.append(interpreter); builder.append(", type="); builder.append(type); builder.append("]"); return builder.toString(); } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/SExpression.java ================================================ /** * 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.expression.model; import java.io.Serializable; import java.util.List; /** * @author Zhao na * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public interface SExpression extends Serializable { String TYPE_CONSTANT = "TYPE_CONSTANT"; String TYPE_VARIABLE = "TYPE_VARIABLE"; String TYPE_PATTERN = "TYPE_PATTERN"; String TYPE_READ_ONLY_SCRIPT = "TYPE_READ_ONLY_SCRIPT"; String TYPE_READ_WRITE_SCRIPT = "TYPE_READ_WRITE_SCRIPT"; String TYPE_PARAMETER = "TYPE_PARAMETER"; String TYPE_I18N = "TYPE_I18N"; String GROOVY = "GROOVY"; String JAVASCRIPT = "JAVASCRIPT"; String TYPE_INPUT = "TYPE_INPUT"; String TYPE_LIST = "TYPE_LIST"; String TYPE_CONDITION = "TYPE_CONDITION"; String getName(); String getContent(); String getExpressionType(); ExpressionKind getExpressionKind(); String getReturnType(); String getInterpreter(); List getDependencies(); boolean hasDependencies(); int getDiscriminant(); } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/SExpressionType.java ================================================ /** * 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.expression.model; /** * FIXME: this class is only used in tests. Delete it. * * @author Zhao na * @author Emmanuel Duchastenier */ public enum SExpressionType { TYPE_CONSTANT, TYPE_VARIABLE, TYPE_PATTERN, TYPE_READ_WRITE_SCRIPT, // Optional TYPE_PARAMETER, TYPE_I18N, TYPE_READ_ONLY_SCRIPT_GROOVY, TYPE_READ_ONLY_SCRIPT_JAVASCRIPT, TYPE_INPUT, TYPE_XPATH_READ } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/builder/SExpressionBuilder.java ================================================ /** * 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.expression.model.builder; import java.util.List; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Zhao na */ public interface SExpressionBuilder { SExpressionBuilder setName(String name); SExpressionBuilder setContent(String content); SExpressionBuilder setExpressionType(String expressionType); SExpressionBuilder setReturnType(final String returnType); SExpressionBuilder setInterpreter(final String interpreter); SExpressionBuilder setDependencies(final List dependencies); SExpression done() throws SInvalidExpressionException; } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/builder/SExpressionBuilderFactory.java ================================================ /** * 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.expression.model.builder; /** * @author Zhao na */ public interface SExpressionBuilderFactory { SExpressionBuilder createNewInstance(); } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/builder/impl/SExpressionBuilderFactoryImpl.java ================================================ /** * 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.expression.model.builder.impl; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilder; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilderFactory; import org.bonitasoft.engine.expression.model.impl.SExpressionImpl; /** * @author Zhao Na */ public class SExpressionBuilderFactoryImpl implements SExpressionBuilderFactory { @Override public SExpressionBuilder createNewInstance() { final SExpressionImpl expression = new SExpressionImpl(); return new SExpressionBuilderImpl(expression); } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/builder/impl/SExpressionBuilderImpl.java ================================================ /** * 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.expression.model.builder.impl; import java.util.List; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilder; import org.bonitasoft.engine.expression.model.impl.SExpressionImpl; /** * @author Zhao Na * @author Celine Souchet */ public class SExpressionBuilderImpl implements SExpressionBuilder { private final SExpressionImpl expression; public SExpressionBuilderImpl(final SExpressionImpl expression) { super(); this.expression = expression; } @Override public SExpression done() throws SInvalidExpressionException { if (expression.getReturnType() == null) { throw new SInvalidExpressionException("Expression return type must be set.", expression.getName()); } return expression; } @Override public SExpressionBuilder setName(final String name) { expression.setName(name); return this; } @Override public SExpressionBuilder setContent(final String content) { expression.setContent(content); return this; } @Override public SExpressionBuilder setExpressionType(final String expressionType) { expression.setExpressionType(expressionType); return this; } @Override public SExpressionBuilder setReturnType(final String returnType) { expression.setReturnType(returnType); return this; } @Override public SExpressionBuilder setInterpreter(final String interpreter) { expression.setInterpreter(interpreter); return this; } @Override public SExpressionBuilder setDependencies(final List dependencies) { expression.setDependencies(dependencies); return this; } } ================================================ FILE: services/bonita-expression/src/main/java/org/bonitasoft/engine/expression/model/impl/SExpressionImpl.java ================================================ /** * 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.expression.model.impl; import java.util.Collections; import java.util.List; import java.util.Objects; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; /** * @author Zhao Na * @author Matthieu Chaffotte * @author Emmanuel Duchastenier * @author Baptiste Mesta */ public class SExpressionImpl implements SExpression { private static final long serialVersionUID = 1L; private String name; private String content; private String returnType; private List dependencies; private final ExpressionKind expressionKind = new ExpressionKind(); public SExpressionImpl() { super(); } public SExpressionImpl(final String name, final String content, final String expressionType, final String returnType, final String interpreter, final List dependencies) { this.name = name; this.content = content; expressionKind.setType(expressionType); this.returnType = returnType; expressionKind.setInterpreter(interpreter); this.dependencies = dependencies; } public void setContent(final String content) { this.content = content; } public void setExpressionType(final String expressionType) { expressionKind.setType(expressionType); } public void setReturnType(final String returnType) { this.returnType = returnType; } public void setInterpreter(final String interpreter) { expressionKind.setInterpreter(interpreter); } public void setDependencies(final List dependencies) { this.dependencies = dependencies; } @Override public String getName() { return name; } public void setName(final String name) { this.name = name; } @Override public String getContent() { return content; } @Override public String getExpressionType() { return expressionKind.getExpressionType(); } @Override public String getReturnType() { return returnType; } @Override public String getInterpreter() { return expressionKind.getInterpreter(); } @Override public List getDependencies() { if (dependencies == null) { return Collections.emptyList(); } return dependencies; } @Override public boolean hasDependencies() { return !getDependencies().isEmpty(); } @Override public ExpressionKind getExpressionKind() { return expressionKind; } @Override public int hashCode() { return Objects.hash(name, content, returnType, dependencies, expressionKind); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof SExpressionImpl that)) return false; return Objects.equals(name, that.name) && Objects.equals(content, that.content) && Objects.equals(returnType, that.returnType) && Objects.equals(dependencies, that.dependencies) && Objects.equals(expressionKind, that.expressionKind); } @Override public String toString() { final int maxLen = 5; final StringBuilder builder = new StringBuilder(); builder.append("SExpressionImpl [name="); builder.append(name); builder.append(", content="); builder.append(content); builder.append(", returnType="); builder.append(returnType); builder.append(", dependencies="); builder.append(dependencies != null ? dependencies.subList(0, Math.min(dependencies.size(), maxLen)) : null); builder.append(", expressionKind="); builder.append(expressionKind); builder.append("]"); return builder.toString(); } @Override public int getDiscriminant() { return hashCode(); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/exception/SExpressionEvaluationExceptionTest.java ================================================ /** * 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.expression.exception; import static org.junit.Assert.assertEquals; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class SExpressionEvaluationExceptionTest { @Mock private Throwable cause; private final String message = "message"; private final String expressionName = "plop"; /** * Test method for * {@link org.bonitasoft.engine.expression.exception.SExpressionEvaluationException#getExpressionName()}. */ @Test public final void return_expression_name_when_use_constructor_with_cause() { final SExpressionEvaluationException sExpressionEvaluationException = new SExpressionEvaluationException(cause, expressionName); final String result = sExpressionEvaluationException.getExpressionName(); assertEquals(expressionName, result); } /** * Test method for * {@link org.bonitasoft.engine.expression.exception.SExpressionEvaluationException#getExpressionName()}. */ @Test public final void return_expression_name_when_use_constructor_with_message() { final SExpressionEvaluationException sExpressionEvaluationException = new SExpressionEvaluationException( message, expressionName); final String result = sExpressionEvaluationException.getExpressionName(); assertEquals(expressionName, result); } /** * Test method for * {@link org.bonitasoft.engine.expression.exception.SExpressionEvaluationException#getExpressionName()}. */ @Test public final void return_expression_name_when_use_constructor_with_message_and_cause() { final SExpressionEvaluationException sExpressionEvaluationException = new SExpressionEvaluationException( message, cause, expressionName); final String result = sExpressionEvaluationException.getExpressionName(); assertEquals(expressionName, result); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/exception/SInvalidExpressionExceptionTest.java ================================================ /** * 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.expression.exception; import static org.junit.Assert.assertEquals; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class SInvalidExpressionExceptionTest { @Mock private Throwable cause; private final String expressionName = "expressionName"; private final String message = "message"; /** * Test method for * {@link org.bonitasoft.engine.expression.exception.SInvalidExpressionException#getExpressionName()}. */ @Test public final void return_expression_name_when_use_constructor_with_cause() { final SInvalidExpressionException sExpressionEvaluationException = new SInvalidExpressionException(cause, expressionName); final String result = sExpressionEvaluationException.getExpressionName(); assertEquals(expressionName, result); } /** * Test method for * {@link org.bonitasoft.engine.expression.exception.SInvalidExpressionException#getExpressionName()}. */ @Test public final void return_expression_name_when_use_constructor_with_message() { final SInvalidExpressionException sExpressionEvaluationException = new SInvalidExpressionException(message, expressionName); final String result = sExpressionEvaluationException.getExpressionName(); assertEquals(expressionName, result); } /** * Test method for * {@link org.bonitasoft.engine.expression.exception.SInvalidExpressionException#getExpressionName()}. */ @Test public final void return_expression_name_when_use_constructor_with_message_and_cause() { final SInvalidExpressionException sExpressionEvaluationException = new SInvalidExpressionException(message, cause, expressionName); final String result = sExpressionEvaluationException.getExpressionName(); assertEquals(expressionName, result); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/ConditionExpressionExecutorStrategyTest.java ================================================ /** * 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.expression.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; 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 org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.impl.condition.BinaryComparatorExecutor; import org.bonitasoft.engine.expression.impl.condition.LogicalComplementExecutor; import org.bonitasoft.engine.expression.model.ExpressionKind; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.impl.SExpressionImpl; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class ConditionExpressionExecutorStrategyTest { @InjectMocks private ConditionExpressionExecutorStrategy strategy; private ArrayList dependFirstLtSecond; private ArrayList dependFirstGtSecond; private ArrayList dependEquals; private HashMap resolvedDependencies; @Mock private LogicalComplementExecutor logicalComplementExecutor; @Mock private BinaryComparatorExecutor binaryComparatorExecutor; @Before public void initialiseDependencies() { final SExpression constExpr1 = buildExpression("5", SExpression.TYPE_CONSTANT, Integer.class.getName(), null, null); final SExpression constExpr2 = buildExpression("7", SExpression.TYPE_CONSTANT, Integer.class.getName(), null, null); final SExpression constExpr3 = buildExpression("2", SExpression.TYPE_CONSTANT, Integer.class.getName(), null, null); dependFirstLtSecond = new ArrayList(2); dependFirstLtSecond.add(constExpr1); dependFirstLtSecond.add(constExpr2); dependFirstGtSecond = new ArrayList(2); dependFirstGtSecond.add(constExpr2); dependFirstGtSecond.add(constExpr1); dependEquals = new ArrayList(2); dependEquals.add(constExpr1); dependEquals.add(constExpr1); resolvedDependencies = new HashMap(2); resolvedDependencies.put(constExpr1.getDiscriminant(), 5); resolvedDependencies.put(constExpr2.getDiscriminant(), 7); resolvedDependencies.put(constExpr3.getDiscriminant(), 2); } private SExpression buildExpression(final String content, final String expressionType, final String returnType, final String interpreter, final List dependencies) { final SExpressionImpl eb = new SExpressionImpl(); eb.setName(content); eb.setContent(content); eb.setExpressionType(expressionType); eb.setInterpreter(interpreter); eb.setReturnType(returnType); eb.setDependencies(dependencies); return eb; } protected List evaluate(final List expression, final Map resolvedExpressions, final ContainerState containerState) throws SExpressionEvaluationException { return strategy.evaluate(expression, new HashMap(0), resolvedExpressions, containerState); } @Test public void evaluate_lstOfExpressions_should_return_list_of_results() throws Exception { //given final SExpression expr1 = buildExpression("!=", SExpression.TYPE_CONDITION, Boolean.class.getName(), null, dependFirstGtSecond); final SExpression expr2 = buildExpression("!=", SExpression.TYPE_CONDITION, Boolean.class.getName(), null, dependFirstLtSecond); final SExpression expr3 = buildExpression("!=", SExpression.TYPE_CONDITION, Boolean.class.getName(), null, dependEquals); Map context = Collections.emptyMap(); ConditionExpressionExecutorStrategy mockedStrategy = spy(strategy); given(mockedStrategy.evaluate(expr1, context, resolvedDependencies, ContainerState.ACTIVE)).willReturn(true); given(mockedStrategy.evaluate(expr2, context, resolvedDependencies, ContainerState.ACTIVE)).willReturn(false); given(mockedStrategy.evaluate(expr3, context, resolvedDependencies, ContainerState.ACTIVE)).willReturn(null); //when List resolvedExpressions = mockedStrategy.evaluate(Arrays.asList(expr1, expr2, expr3), context, resolvedDependencies, ContainerState.ACTIVE); //then assertThat(resolvedExpressions).containsExactly(true, false, null); } @Test public void evaluate_should_return_result_of_LogicalComplementExecutor_when_content_is_logical_complement_operator() throws Exception { //given Map resolvedExpressions = Collections.emptyMap(); SExpression expression = mock(SExpression.class); given(expression.getContent()).willReturn(ConditionExpressionExecutorStrategy.LOGICAL_COMPLEMENT_OPERATOR); given(logicalComplementExecutor.evaluate(resolvedExpressions, expression)).willReturn(true); //when Object value = strategy.evaluate(expression, new HashMap(0), resolvedExpressions, ContainerState.ACTIVE); //then assertThat(value).isEqualTo(true); } @Test public void evaluate_should_return_result_of_BinaryComparatorExecutor_when_content_is_a_binary_operator() throws Exception { //given Map resolvedExpressions = Collections.emptyMap(); SExpression expression = mock(SExpression.class); given(expression.getContent()).willReturn(ConditionExpressionExecutorStrategy.GREATER_THAN_COMPARATOR); given(binaryComparatorExecutor.evaluate(resolvedExpressions, expression)).willReturn(true); //when Object value = strategy.evaluate(expression, new HashMap(0), resolvedExpressions, ContainerState.ACTIVE); //then assertThat(value).isEqualTo(true); } @Test public void mustPutEvaluatedExpressionInContext_should_return_false() throws Exception { //given //when boolean value = strategy.mustPutEvaluatedExpressionInContext(); //then assertThat(value).isFalse(); } @Test public void getExpressionKind_should_return() throws Exception { //given //when ExpressionKind kind = strategy.getExpressionKind(); //then assertThat(kind.getInterpreter()).isEqualTo(ExpressionKind.NONE); assertThat(kind.getType()).isEqualTo(SExpression.TYPE_CONDITION); } @Test(expected = SInvalidExpressionException.class) public void validate_should_throw_exception_if_expression_is_null() throws Exception { //given //when strategy.validate(null); //then exception } @Test(expected = SInvalidExpressionException.class) public void validate_should_throw_exception_if_expression_content_is_null() throws Exception { //given SExpression expression = mock(SExpression.class); given(expression.getContent()).willReturn(null); //when strategy.validate(expression); //then exception } @Test(expected = SInvalidExpressionException.class) public void validate_should_throw_exception_if_expression_content_is_an_invalid_operator() throws Exception { //given SExpression expression = mock(SExpression.class); given(expression.getContent()).willReturn("^"); //when strategy.validate(expression); //then exception } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/ConstantExpressionExecutorStrategyTest.java ================================================ /** * 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.expression.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertNotNull; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.util.Date; import java.util.List; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.impl.SExpressionImpl; import org.junit.Before; import org.junit.Test; /** * @author Celine Souchet */ public class ConstantExpressionExecutorStrategyTest { private ConstantExpressionExecutorStrategy strategy; @Before public void setup() { strategy = new ConstantExpressionExecutorStrategy(); } /** * Test method for * {@link org.bonitasoft.engine.expression.impl.ConstantExpressionExecutorStrategy#evaluate(org.bonitasoft.engine.expression.model.SExpression, java.util.Map, java.util.Map)} * . * * @throws SExpressionEvaluationException */ @Test public final void evaluateDate() throws SExpressionEvaluationException { final SExpression sExpression = buildExpression("2013-07-18T14:49:26.86+02:00", SExpression.TYPE_CONSTANT, Date.class.getName(), null, null); final Date result = (Date) strategy.evaluate(sExpression, null, null, ContainerState.ACTIVE); assertNotNull(result); } @Test public final void evaluateDateWithoutTimeZone() throws SExpressionEvaluationException { final SExpression sExpression = buildExpression("2013-07-18T14:49:26.86", SExpression.TYPE_CONSTANT, Date.class.getName(), null, null); final Date result = (Date) strategy.evaluate(sExpression, null, null, ContainerState.ACTIVE); assertNotNull(result); } @Test public final void evaluateDateWithoutMilliseconds() throws SExpressionEvaluationException { final SExpression sExpression = buildExpression("2013-07-18T14:49:26+02:00", SExpression.TYPE_CONSTANT, Date.class.getName(), null, null); final Date result = (Date) strategy.evaluate(sExpression, null, null, ContainerState.ACTIVE); assertNotNull(result); } @Test public void should_evaluate_local_date() throws Exception { final SExpression sExpression = buildExpression("2013-07-18", SExpression.TYPE_CONSTANT, LocalDate.class.getName(), null, null); final LocalDate result = (LocalDate) strategy.evaluate(sExpression, null, null, ContainerState.ACTIVE); assertThat(result.getYear()).isEqualTo(2013); assertThat(result.getMonth()).isEqualTo(Month.JULY); assertThat(result.getDayOfMonth()).isEqualTo(18); } @Test public void should_evaluate_local_date_time() throws Exception { final SExpression sExpression = buildExpression("2013-07-18T12:00:00", SExpression.TYPE_CONSTANT, LocalDateTime.class.getName(), null, null); final LocalDateTime result = (LocalDateTime) strategy.evaluate(sExpression, null, null, ContainerState.ACTIVE); assertThat(result.getYear()).isEqualTo(2013); assertThat(result.getMonth()).isEqualTo(Month.JULY); assertThat(result.getDayOfMonth()).isEqualTo(18); assertThat(result.getHour()).isEqualTo(12); assertThat(result.getMinute()).isEqualTo(0); assertThat(result.getSecond()).isEqualTo(0); } @Test public void should_evaluate_offset_date_time() throws Exception { final SExpression sExpression = buildExpression("2007-12-03T10:15:30+01:00", SExpression.TYPE_CONSTANT, OffsetDateTime.class.getName(), null, null); final OffsetDateTime result = (OffsetDateTime) strategy.evaluate(sExpression, null, null, ContainerState.ACTIVE); assertThat(result.getYear()).isEqualTo(2007); assertThat(result.getMonth()).isEqualTo(Month.DECEMBER); assertThat(result.getDayOfMonth()).isEqualTo(3); assertThat(result.getHour()).isEqualTo(10); assertThat(result.getMinute()).isEqualTo(15); assertThat(result.getSecond()).isEqualTo(30); assertThat(result.getOffset()).isEqualTo(ZoneOffset.ofHours(1)); } private SExpression buildExpression(final String content, final String expressionType, final String returnType, final String interpreter, final List dependencies) { final SExpressionImpl eb = new SExpressionImpl(); eb.setName(content); eb.setContent(content); eb.setExpressionType(expressionType); eb.setInterpreter(interpreter); eb.setReturnType(returnType); eb.setDependencies(dependencies); return eb; } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/ExpressionServiceImplTest.java ================================================ /** * 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.expression.impl; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.ExpressionExecutorStrategy; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.tracking.TimeTracker; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Emmanuel Duchastenier */ @RunWith(MockitoJUnitRunner.class) @SuppressWarnings("javadoc") public class ExpressionServiceImplTest { @Mock private ExpressionExecutorStrategy expressionExecutorStrategy; private ExpressionServiceImpl expressionService; @Before public void setUp() { } @Test public void evaluateInvalidExpressionFailsAtValidationStep() throws Exception { final SExpression expression = mock(SExpression.class); final TimeTracker timeTracker = mock(TimeTracker.class); expressionService = new ExpressionServiceImpl(true, timeTracker); expressionService.setExpressionExecutorStrategy(Arrays.asList(expressionExecutorStrategy)); expressionService.evaluate(expression, Collections. singletonMap("processDefinitionId", 546l), new HashMap(0), ContainerState.ACTIVE); verify(expressionExecutorStrategy, times(1)).validate(any(SExpression.class)); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/GroovyScriptConditionExpressionExecutorStrategyTest.java ================================================ /** * 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.expression.impl; import static java.util.Collections.emptyMap; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.cache.CacheConfiguration; import org.bonitasoft.engine.cache.ehcache.EhCacheCacheService; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.ExpressionExecutorStrategy; import org.bonitasoft.engine.expression.model.impl.SExpressionImpl; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class GroovyScriptConditionExpressionExecutorStrategyTest { @Mock private ClassLoaderService classLoaderService; private EhCacheCacheService cacheService; private GroovyScriptConditionExpressionExecutorStrategy executorStrategy; private Map context; @Before public void setup() throws Exception { // Create valid cache configurations for Ehcache 3 (heap-only) final CacheConfiguration cacheConfiguration = new CacheConfiguration(); cacheConfiguration.setName("GROOVY_SCRIPT_CACHE_NAME"); cacheConfiguration.setMaxElementsInMemory(1000); final CacheConfiguration defaultCacheConfiguration = new CacheConfiguration(); defaultCacheConfiguration.setMaxElementsInMemory(1000); final List cacheConfigurations = Collections.singletonList(cacheConfiguration); cacheService = new EhCacheCacheService(cacheConfigurations, defaultCacheConfiguration); cacheService.start(); executorStrategy = new GroovyScriptConditionExpressionExecutorStrategy(cacheService, classLoaderService); doReturn(GroovyScriptExpressionExecutorCacheStrategyTest.class.getClassLoader()).when(classLoaderService) .getClassLoader(any()); context = new HashMap<>(); context.put(ExpressionExecutorStrategy.DEFINITION_ID, 123456789L); } @After public void stop() { cacheService.stop(); } @Test public void should_return_a_true_boolean_value() throws Exception { //given final SExpressionImpl expression = new SExpressionImpl("myExpr", "'toto'", null, "java.lang.Boolean", null, Collections.emptyList()); // when final Object evaluate = executorStrategy.evaluate(expression, context, emptyMap(), ContainerState.ACTIVE); // then assertThat(evaluate).isEqualTo(true); } @Test public void should_return_a_false_boolean_value() throws Exception { //given context.put("toto", null); final SExpressionImpl expression = new SExpressionImpl("myExpr", "toto", null, "java.lang.Boolean", null, Collections.emptyList()); // when final Object evaluate = executorStrategy.evaluate(expression, context, emptyMap(), ContainerState.ACTIVE); // then assertThat(evaluate).isEqualTo(false); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/GroovyScriptExpressionExecutorCacheStrategyTest.java ================================================ /** * 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.expression.impl; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.expression.ExpressionExecutorStrategy.DEFINITION_ID; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import groovy.lang.GroovyShell; import org.bonitasoft.engine.bpm.contract.FileInputValue; import org.bonitasoft.engine.bpm.document.Document; import org.bonitasoft.engine.bpm.document.DocumentValue; import org.bonitasoft.engine.cache.CacheConfiguration; import org.bonitasoft.engine.cache.SCacheException; import org.bonitasoft.engine.cache.ehcache.EhCacheCacheService; import org.bonitasoft.engine.classloader.ClassLoaderService; import org.bonitasoft.engine.classloader.SClassLoaderException; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.builder.SExpressionBuilder; import org.bonitasoft.engine.expression.model.builder.impl.SExpressionBuilderFactoryImpl; import org.bonitasoft.engine.expression.model.impl.SExpressionImpl; import org.codehaus.groovy.runtime.typehandling.GroovyCastException; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class GroovyScriptExpressionExecutorCacheStrategyTest { @Mock private ClassLoaderService classLoaderService; private EhCacheCacheService cacheService; private GroovyScriptExpressionExecutorCacheStrategy groovyScriptExpressionExecutorCacheStrategy; private Class script2; private Map context; @Before public void setup() throws Exception { // Create valid cache configurations for Ehcache 3 (heap-only) final CacheConfiguration cacheConfiguration = new CacheConfiguration(); cacheConfiguration.setName("GROOVY_SCRIPT_CACHE_NAME"); cacheConfiguration.setMaxElementsInMemory(1000); final CacheConfiguration defaultCacheConfiguration = new CacheConfiguration(); defaultCacheConfiguration.setMaxElementsInMemory(1000); final List cacheConfigurations = Collections.singletonList(cacheConfiguration); cacheService = new EhCacheCacheService(cacheConfigurations, defaultCacheConfiguration); cacheService.start(); groovyScriptExpressionExecutorCacheStrategy = new GroovyScriptExpressionExecutorCacheStrategy(cacheService, classLoaderService); doReturn(GroovyScriptExpressionExecutorCacheStrategyTest.class.getClassLoader()).when(classLoaderService) .getClassLoader(any()); context = new HashMap<>(); context.put(DEFINITION_ID, 123456789L); } @After public void teardown() { cacheService.stop(); } @Test public void should_getShell_return_a_shell_for_each_definition() throws Exception { // given // when final GroovyShell shell1 = groovyScriptExpressionExecutorCacheStrategy.getShell(12L); final GroovyShell shell2 = groovyScriptExpressionExecutorCacheStrategy.getShell(13L); // then assertThat(shell1).isNotNull(); assertThat(shell2).isNotNull(); assertThat(shell1).isNotEqualTo(shell2); } @Test public void should_update_on_classloader_listener_clear_shell_cache() throws Exception { // given // when final GroovyShell shell1 = groovyScriptExpressionExecutorCacheStrategy.getShell(12L); groovyScriptExpressionExecutorCacheStrategy.onUpdate(null); final GroovyShell shell2 = groovyScriptExpressionExecutorCacheStrategy.getShell(12L); // then assertThat(shell1).isNotNull(); assertThat(shell2).isNotNull(); assertThat(shell1).isNotEqualTo(shell2); } @Test public void should_destroy_on_classloader_listener_clear_shell_cache() throws Exception { // given // when final GroovyShell shell1 = groovyScriptExpressionExecutorCacheStrategy.getShell(12L); groovyScriptExpressionExecutorCacheStrategy.onDestroy(null); final GroovyShell shell2 = groovyScriptExpressionExecutorCacheStrategy.getShell(12L); // then assertThat(shell1).isNotNull(); assertThat(shell2).isNotNull(); assertThat(shell1).isNotEqualTo(shell2); } @Test public void should_getShell_return_same_shell_for_1_definition() throws Exception { // when final GroovyShell shell1 = groovyScriptExpressionExecutorCacheStrategy.getShell(12L); final GroovyShell shell2 = groovyScriptExpressionExecutorCacheStrategy.getShell(12L); // then assertThat(shell1).isNotNull(); assertThat(shell1).isEqualTo(shell2); } @Test public void should_getScriptFromCache_return_a_the_same_script_class_if_in_same_definition() throws Exception { // when final Class script1 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache("MyScriptContent", 12L); final Class script2 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache("MyScriptContent", 12L); // then assertThat(script1).isNotNull(); assertThat(script1).isEqualTo(script2); } @Test public void should_getScriptFromCache_should_cache_only_once_when_on_different_threads() throws Exception { // when final Class script1 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache("MyScriptContent", 12L); Thread thread = new Thread(new Runnable() { @Override public void run() { try { script2 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache("MyScriptContent", 12L); } catch (SCacheException | SClassLoaderException e) { e.printStackTrace(); } } }); thread.start(); thread.join(); // then assertThat(script1).isNotNull(); assertThat(script2).isNotNull(); assertThat(script1).isEqualTo(script2); } @Test public void should_getScriptFromCache_return_different_script_if_different_definition() throws Exception { // when final Class script1 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache("MyScriptContent", 12L); final Class script2 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache("MyScriptContent", 13L); // then assertThat(script1).isNotNull(); assertThat(script2).isNotNull(); assertThat(script1).isNotEqualTo(script2); } @Test public void should_getScriptFromCache_return_different_script_if_content_is_different_definition() throws Exception { // when final Class script1 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache("MyScriptContent1", 12L); final Class script2 = groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache("MyScriptContent2", 12L); // then assertThat(script1).isNotNull(); assertThat(script2).isNotNull(); assertThat(script1).isNotEqualTo(script2); } @Test(expected = SBonitaRuntimeException.class) public void should_not_put_in_cache_script_without_definition_id() throws Exception { // when groovyScriptExpressionExecutorCacheStrategy.getScriptFromCache("MyScriptContent1", null); // then //exception } @Test public void should_evaluate_return_the_evaluation() throws Exception { //given final SExpressionImpl expression = new SExpressionImpl("myExpr", "'toto'", null, "java.lang.String", null, Collections.emptyList()); // when final Object evaluate = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, context, Collections.emptyMap(), ContainerState.ACTIVE); // then assertThat(evaluate).isEqualTo("toto"); } @Test(expected = SExpressionEvaluationException.class) public void should_evaluate_throw_SExpressionEvaluationException_when_script_throws_Error() throws Exception { //given final SExpressionImpl expression = new SExpressionImpl("myExpr", "throw new java.lang.NoClassDefFoundError()", null, "java.lang.String", null, Collections.emptyList()); // when groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, context, Collections.emptyMap(), ContainerState.ACTIVE); // then //exception } @Test(expected = SExpressionEvaluationException.class) public void should_evaluate_throw_SExpressionEvaluationException_when_script_throws_Throwable() throws Exception { //given final SExpressionImpl expression = new SExpressionImpl("myExpr", "throw new java.lang.Throwable()", null, "java.lang.String", null, Collections.emptyList()); // when groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, context, Collections.emptyMap(), ContainerState.ACTIVE); // then //exception } @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void evaluation_with_DefaultGroovyMethod_size_should_return_4() throws SExpressionEvaluationException, SInvalidExpressionException { String content = "import static org.codehaus.groovy.runtime.DefaultGroovyMethods.*\n" + "size('test'.toCharArray())"; SExpression expression = integerExpression(content); Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(), null); assertThat(value) .isInstanceOf(Integer.class) .isEqualTo(4); } @Test public void evaluation_with_string_size_should_return_4() throws SExpressionEvaluationException, SInvalidExpressionException { String content = "new StringBuffer('test').size()"; SExpression expression = integerExpression(content); Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(), null); assertThat(value) .isInstanceOf(Integer.class) .isEqualTo(4); } private static SExpressionBuilder expressionBuilder() { return new SExpressionBuilderFactoryImpl().createNewInstance().setName("test"); } private static SExpression integerExpression(String content) throws SInvalidExpressionException { return expressionBuilder().setContent(content).setReturnType(Integer.class.getName()).done(); } @Test public void evaluation_with_jsonBuilder_toString_should_work_on_java_8_and_java_11() throws SExpressionEvaluationException, SInvalidExpressionException { String content = "import groovy.json.JsonBuilder\n" + "new JsonBuilder('hello').toString()"; SExpression expression = expressionBuilder().setContent(content).setReturnType(String.class.getName()).done(); Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(), null); assertThat(value) .isInstanceOf(String.class) .isEqualTo("\"hello\""); } @Test public void evaluation_should_coerce_to_string_return_type() throws SExpressionEvaluationException, SInvalidExpressionException { String content = "1"; SExpression expression = expressionBuilder().setContent(content).setReturnType(String.class.getName()).done(); Object stringValue = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(), null); assertThat(stringValue) .isInstanceOf(String.class) .isEqualTo("1"); } @Test public void evaluation_should_coerce_groovy_string_to_string_return_type() throws SExpressionEvaluationException, SInvalidExpressionException { String content = "\"\"\"Hello ${firstName}\"\"\""; Map context = new HashMap<>(); context.put(DEFINITION_ID, 42L); context.put("firstName", "Romain"); SExpression expression = expressionBuilder().setContent(content).setReturnType(String.class.getName()).done(); Object stringValue = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, context, emptyMap(), null); assertThat(stringValue) .isInstanceOf(String.class) .isEqualTo("Hello Romain"); } @Test public void evaluation_should_coerce_to_integer_return_type() throws SExpressionEvaluationException, SInvalidExpressionException { String content = "1"; SExpression expression = expressionBuilder().setContent(content).setReturnType(Integer.class.getName()).done(); Object intergerValue = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(), null); assertThat(intergerValue) .isInstanceOf(Integer.class) .isEqualTo(1); } @Test public void evaluation_should_coerce_to_double_return_type() throws SExpressionEvaluationException, SInvalidExpressionException { String content = "1"; SExpression expression = expressionBuilder().setContent(content).setReturnType(Double.class.getName()).done(); Object doubleValue = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(), null); assertThat(doubleValue) .isInstanceOf(Double.class) .isEqualTo(1d); } @Test public void evaluation_should_coerce_thruthy_expression_to_boolean_return_type() throws SExpressionEvaluationException, SInvalidExpressionException { String thruthyContent = "1"; // 1 is truthy SExpression expression = expressionBuilder().setContent(thruthyContent).setReturnType(Boolean.class.getName()) .done(); Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(), null); assertThat(value) .isInstanceOf(Boolean.class) .isEqualTo(true); } @Test public void evaluation_should_coerce_falsy_expression_to_boolean_return_type() throws SExpressionEvaluationException, SInvalidExpressionException { String falsyContent = "0"; // 0 is falsy SExpression expression = expressionBuilder().setContent(falsyContent).setReturnType(Boolean.class.getName()) .done(); Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(), null); assertThat(value) .isInstanceOf(Boolean.class) .isEqualTo(false); } @Test public void evaluation_should_coerce_list_expression_to_array_return_type() throws SExpressionEvaluationException, SInvalidExpressionException { String falsyContent = "[1,2,3]"; SExpression expression = expressionBuilder().setContent(falsyContent).setReturnType(String[].class.getName()) .done(); Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(), null); assertThat(value) .isInstanceOf(String[].class) .isEqualTo(new String[] { "1", "2", "3" }); } @Test public void evaluation_should_not_coerce_FileInputValue_to_Document_return_type() throws SExpressionEvaluationException, SInvalidExpressionException { String content = "document"; SExpression expression = expressionBuilder().setContent(content).setReturnType(Document.class.getName()) .done(); Map context = new HashMap<>(); FileInputValue fileInputValue = new FileInputValue("someDoc", null); context.put("document", fileInputValue); context.put(DEFINITION_ID, 42L); Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, context, emptyMap(), null); assertThat(value) .isInstanceOf(FileInputValue.class) .isEqualTo(fileInputValue); } @Test public void evaluation_should_not_coerce_FileInputValue_to_DocumentValue_return_type() throws SExpressionEvaluationException, SInvalidExpressionException { String content = "documentValue"; SExpression expression = expressionBuilder().setContent(content).setReturnType(DocumentValue.class.getName()) .done(); Map context = new HashMap<>(); FileInputValue fileInputValue = new FileInputValue("someDoc", null); context.put("documentValue", fileInputValue); context.put(DEFINITION_ID, 42L); Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, context, emptyMap(), null); assertThat(value) .isInstanceOf(FileInputValue.class) .isEqualTo(fileInputValue); } @Test public void evaluation_should_not_coerce_null_result() throws SExpressionEvaluationException, SInvalidExpressionException { String content = "null"; SExpression expression = expressionBuilder().setContent(content).setReturnType(String.class.getName()) .done(); Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(), null); assertThat(value).isNull(); } @Test public void evaluation_should_throw_a_GroovyCastException_when_coercing_incompatible_types() throws SExpressionEvaluationException, SInvalidExpressionException { String notAList = "[1]"; // cannot be casted into Long SExpression expression = expressionBuilder().setContent(notAList).setReturnType(Long.class.getName()).done(); expectedException.expectCause(is(instanceOf(GroovyCastException.class))); groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(), null); } @Test public void evaluation_should_coerce_string_into_long() throws SExpressionEvaluationException, SInvalidExpressionException { String content = "'123'"; SExpression expression = expressionBuilder().setContent(content).setReturnType(Long.class.getName()).done(); Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(), null); assertThat(value).isEqualTo(123L); } @Test public void evaluation_should_coerce_double_into_long() throws SExpressionEvaluationException, SInvalidExpressionException { String content = "123d"; SExpression expression = expressionBuilder().setContent(content).setReturnType(Long.class.getName()).done(); Object value = groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(), null); assertThat(value).isEqualTo(123L); } @Test public void evaluation_should_throw_a_NumberFormatException_when_string_cannot_be_formatted() throws SExpressionEvaluationException, SInvalidExpressionException { String notAList = "'123a'"; // cannot be coerce into Long SExpression expression = expressionBuilder().setContent(notAList).setReturnType(Long.class.getName()).done(); expectedException.expectCause(is(instanceOf(NumberFormatException.class))); groovyScriptExpressionExecutorCacheStrategy.evaluate(expression, singletonMap(DEFINITION_ID, 42L), emptyMap(), null); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/JavaMethodCallExpressionExecutorStrategyTest.java ================================================ /** * 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.expression.impl; import static org.junit.Assert.assertEquals; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.ExpressionExecutorStrategy; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.impl.SExpressionImpl; import org.junit.Before; import org.junit.Test; /** * @author Elias Ricken de Medeiros */ public class JavaMethodCallExpressionExecutorStrategyTest { private final JavaMethodCallExpressionExecutorStrategy methodCall = new JavaMethodCallExpressionExecutorStrategy(); private Order order; private SExpression orderDep; private Map orderResolvedExp; private List list; private SExpression listDep; private Map listResolvedExp; @Before public void setUp() { list = new ArrayList(2); list.add(1); list.add(2); listDep = new SExpressionImpl("dep", "myValues", ExpressionExecutorStrategy.TYPE_VARIABLE, List.class.getName(), null, null); listResolvedExp = new HashMap(1); listResolvedExp.put(listDep.getDiscriminant(), list); order = new Order("32, rue Gustave Eiffel - 38500 - Grenoble", 123L); orderDep = new SExpressionImpl("dep", "order", ExpressionExecutorStrategy.TYPE_VARIABLE, Order.class.getName(), null, null); orderResolvedExp = new HashMap(1); orderResolvedExp.put(orderDep.getDiscriminant(), order); } @Test public void testJavaMethodCallOfSuperClass() throws Exception { final SExpression expression = new SExpressionImpl("exp1", "toString", ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL, String.class.getName(), null, Collections.singletonList(listDep)); final Object result = methodCall.evaluate(expression, Collections. emptyMap(), listResolvedExp, ContainerState.ACTIVE); assertEquals(list.toString(), result); } @Test public void testCallMethodOnClientObjectReturningString() throws Exception { final SExpression expression = new SExpressionImpl("exp1", "getShippingAddress", ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL, String.class.getName(), null, Collections.singletonList(orderDep)); final Object result = methodCall.evaluate(expression, Collections. emptyMap(), orderResolvedExp, ContainerState.ACTIVE); assertEquals(order.getShippingAddress(), result); } @Test public void testCallMethodOnClientObjectReturningLong() throws Exception { final SExpression expression = new SExpressionImpl("exp1", "getReferenceNumber", ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL, Long.class.getName(), null, Collections.singletonList(orderDep)); final Object result = methodCall.evaluate(expression, Collections. emptyMap(), orderResolvedExp, ContainerState.ACTIVE); assertEquals(order.getReferenceNumber(), result); } @Test public void testEvaluateListOfExpressions() throws Exception { final List expressions = new ArrayList(2); expressions.add(new SExpressionImpl("exp1", "getShippingAddress", ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL, String.class.getName(), null, Collections.singletonList(orderDep))); expressions.add(new SExpressionImpl("exp1", "getReferenceNumber", ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL, Long.class.getName(), null, Collections.singletonList(orderDep))); final List result = methodCall.evaluate(expressions, Collections. emptyMap(), orderResolvedExp, ContainerState.ACTIVE); assertEquals(order.getShippingAddress(), result.get(0)); assertEquals(order.getReferenceNumber(), result.get(1)); } @Test(expected = SInvalidExpressionException.class) public void testDepencenciesCannotBeNull() throws Exception { final SExpression expression = new SExpressionImpl("exp1", "getReferenceNumber", ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL, Long.class.getName(), null, null); methodCall.validate(expression); } @Test(expected = SInvalidExpressionException.class) public void testDepencenciesCannotBeEmpty() throws Exception { final SExpression expression = new SExpressionImpl("exp1", "getReferenceNumber", ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL, Long.class.getName(), null, Collections. emptyList()); methodCall.validate(expression); } @Test(expected = SInvalidExpressionException.class) public void testDepencenciesCannotHasMoreThanOneElement() throws Exception { final List dependencies = new ArrayList(2); dependencies.add(orderDep); dependencies.add(listDep); final SExpression expression = new SExpressionImpl("exp1", "getReferenceNumber", ExpressionExecutorStrategy.TYPE_JAVA_METHOD_CALL, Long.class.getName(), null, dependencies); methodCall.validate(expression); } @Test(expected = SInvalidExpressionException.class) public void testExpressionCannotBeNull() throws Exception { methodCall.validate(null); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/Order.java ================================================ /** * 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.expression.impl; public class Order { private final String shippingAddress; private final long referenceNumber; public Order(final String shippingAddress, final long referenceNumber) { super(); this.shippingAddress = shippingAddress; this.referenceNumber = referenceNumber; } public String getShippingAddress() { return shippingAddress; } public long getReferenceNumber() { return referenceNumber; } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/PatternExpressionExecutorStrategyTest.java ================================================ /** * 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.expression.impl; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.SExpressionType; import org.bonitasoft.engine.expression.model.impl.SExpressionImpl; import org.junit.Before; import org.junit.Test; /** * @author Baptiste Mesta */ public class PatternExpressionExecutorStrategyTest { private PatternExpressionExecutorStrategy strategy; @Before public void setup() { strategy = new PatternExpressionExecutorStrategy(); } private void patternTest(final String expressionContent, final String result, final List dependencyNames, final List dependencyContent) throws SExpressionDependencyMissingException { final SExpressionImpl expression = new SExpressionImpl("pattern", expressionContent, SExpressionType.TYPE_PATTERN.name(), String.class.getName(), null, getIntegerExpressions(dependencyNames, dependencyContent)); final Map resolvedExpressions = getResolvedExpressionMap(expression); final HashMap dependencyValues = new HashMap(1); assertEquals(result, strategy.evaluate(expression, dependencyValues, resolvedExpressions, ContainerState.ACTIVE)); } @Test public void patternTestWithMultipleExpressions() throws SExpressionDependencyMissingException { final SExpression expression1 = new SExpressionImpl("pattern1", "${bla} ${bla} test", SExpressionType.TYPE_PATTERN.name(), String.class.getName(), null, getIntegerExpressions(Arrays.asList("bla"), Arrays.asList(12))); final SExpression expression2 = new SExpressionImpl("pattern1", "${bla} ${bli} test", SExpressionType.TYPE_PATTERN.name(), String.class.getName(), null, getIntegerExpressions(Arrays.asList("bla", "bli"), Arrays.asList(12, 13))); final Map resolvedExpressions = getResolvedExpressionMap(expression1, expression2); final HashMap dependencyValues = new HashMap(1); final List evaluate = strategy.evaluate(Arrays.asList(expression1, expression2), dependencyValues, resolvedExpressions, ContainerState.ACTIVE); assertArrayEquals(new Object[] { "12 12 test", "12 13 test" }, evaluate.toArray()); } @Test(expected = SExpressionDependencyMissingException.class) public void patternTestWithMissingValue() throws SExpressionDependencyMissingException { final SExpression expression1 = new SExpressionImpl("pattern1", "${bla} ${bla} test", SExpressionType.TYPE_PATTERN.name(), String.class.getName(), null, getIntegerExpressions(Arrays.asList("bla"), Arrays.asList(12))); final Map resolvedExpressions = new HashMap(1); final HashMap dependencyValues = new HashMap(1); strategy.evaluate(Arrays.asList(expression1), dependencyValues, resolvedExpressions, ContainerState.ACTIVE); } /** * @param expression * @return */ private Map getResolvedExpressionMap(final SExpression... expressions) { final HashMap hashMap = new HashMap(); for (final SExpression expression : expressions) { final List dependencies = expression.getDependencies(); for (final SExpression sExpression : dependencies) { hashMap.put(sExpression.getDiscriminant(), Integer.valueOf(sExpression.getContent())); } } return hashMap; } /** * @param dependencyNames * @param dependencyContent * @return */ private List getIntegerExpressions(final List dependencyNames, final List dependencyContent) { final ArrayList arrayList = new ArrayList(dependencyNames.size()); final Iterator contentIterator = dependencyContent.iterator(); for (final Iterator nameIterator = dependencyNames.iterator(); nameIterator.hasNext();) { arrayList.add(getIntegerExpression(nameIterator.next(), contentIterator.next())); } return arrayList; } private SExpression getIntegerExpression(final String name, final Integer content) { return new SExpressionImpl(name, String.valueOf(content), SExpression.TYPE_CONSTANT, Integer.class.getName(), null, null); } @Test public void testPatternWithEscape() throws Exception { patternTest("ahah bla ${bla}", "ahah bla 12", Arrays.asList("bla"), Arrays.asList(12)); } @Test public void testPatternWithNotInDependencies() throws Exception { patternTest("ahah bla bla", "ahah bla bla", Arrays.asList("blo"), Arrays.asList(12)); } @Test public void testPatternWithSpaces() throws Exception { patternTest("${bla} ${blablabla} ${bla} ${bla}a bla${bla} bla", "12 ${blablabla} 12 12a bla12 bla", Arrays.asList("bla"), Arrays.asList(12)); } @Test public void testPatternWithSpacesAndLineBreak() throws Exception { patternTest("${bla} ${bla}${bla}${bla} ${bla} ${bla}a ${bla}\n${bla} ${bla}", "12 121212 12 12a 12\n12 12", Arrays.asList("bla"), Arrays.asList(12)); } @Test public void testPatternMultipleReplacements() throws Exception { patternTest("${bla} ${blo} ${blu}${bla}${bla}", "1 2 311", Arrays.asList("bla", "blo", "blu"), Arrays.asList(1, 2, 3)); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/ReturnTypeCheckerTest.java ================================================ /** * 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.expression.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.bpm.contract.FileInputValue; import org.bonitasoft.engine.commons.exceptions.SExceptionContext; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.impl.SExpressionImpl; import org.junit.Test; public class ReturnTypeCheckerTest { @Test public void checkReturnType_should_skip_processing_if_result_is_null() throws SExpressionEvaluationException { new ReturnTypeChecker().checkReturnType(null, null, null); } @Test public void checkReturnType_should_detect_result_and_expression_return_type_incompatibility() { // given: final SExpression expression = expression("expressionName", "java.util.List"); // when: Throwable thrown = catchThrowable( () -> new ReturnTypeChecker().checkReturnType(expression, "a String expression result", Collections.emptyMap())); //then: assertThat(thrown).isInstanceOf(SExpressionEvaluationException.class) .hasMessageContaining("expressionName") .hasMessageContaining("java.util.List") .hasMessageContaining("java.lang.String"); } @Test public void checkReturnType_should_allow_result_and_expression_return_type_that_are_convertible() throws Exception { final SExpression expression = expression("expressionName", "org.bonitasoft.engine.bpm.document.Document"); final FileInputValue result = new FileInputValue("name.txt", "a content".getBytes(StandardCharsets.UTF_8)); new ReturnTypeChecker().checkReturnType(expression, result, Collections.emptyMap()); } @Test public void checkReturnType_should_fill_exception_context_with_activity_instance_id_if_any() { // given: final SExpression expression = expression("expressionName", "InvalidClassName_causing_ClassNotFound"); final Map context = new HashMap<>(); final long activityInstanceId = 1499999L; context.put("containerType", "ACTIVITY_INSTANCE"); context.put("containerId", activityInstanceId); // when: Throwable thrown = catchThrowable( () -> new ReturnTypeChecker().checkReturnType(expression, "some evaluated expression result", context)); // then: assertThat(thrown).isInstanceOf(SExpressionEvaluationException.class) .hasMessageContaining("expressionName") .hasMessageContaining("InvalidClassName_causing_ClassNotFound"); assertThat(((SExpressionEvaluationException) thrown).getContext().get(SExceptionContext.FLOW_NODE_INSTANCE_ID)) .isEqualTo(activityInstanceId); } @Test public void checkReturnType_should_fill_exception_context_with_process_instance_id_if_any() { // given: final SExpression expression = expression("expressionName", "InvalidClassName_causing_ClassNotFound"); final Map context = new HashMap<>(); final long processInstanceId = 8779L; context.put("containerType", "PROCESS_INSTANCE"); context.put("containerId", processInstanceId); // when: Throwable thrown = catchThrowable( () -> new ReturnTypeChecker().checkReturnType(expression, "some evaluated expression result", context)); // then: assertThat(thrown).isInstanceOf(SExpressionEvaluationException.class) .hasMessageContaining("expressionName") .hasMessageContaining("InvalidClassName_causing_ClassNotFound"); assertThat(((SExpressionEvaluationException) thrown).getContext().get(SExceptionContext.PROCESS_INSTANCE_ID)) .isEqualTo(processInstanceId); } // ================================================================================================================= // UTILS // ================================================================================================================= private static SExpression expression(String name, String returnType) { return new SExpressionImpl(name, "not_used_here", null, returnType, null, null); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/XPathReadExpressionExecutorStrategyTest.java ================================================ /** * 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.expression.impl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.commons.io.IOUtil; import org.bonitasoft.engine.expression.ContainerState; import org.bonitasoft.engine.expression.exception.SExpressionDependencyMissingException; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.exception.SExpressionException; import org.bonitasoft.engine.expression.exception.SInvalidExpressionException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.SExpressionType; import org.bonitasoft.engine.expression.model.impl.SExpressionImpl; import org.junit.Before; import org.junit.Test; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * @author Emmanuel Duchastenier */ public class XPathReadExpressionExecutorStrategyTest { private XPathReadExpressionExecutorStrategy strategy; private final String XML_CONTENT_BOOKS; private final String XML_CONTENT_AUTHOR; /** * @throws IOException */ public XPathReadExpressionExecutorStrategyTest() throws IOException { XML_CONTENT_AUTHOR = new String(IOUtil.getAllContentFrom(this.getClass().getResourceAsStream("/authors.xml"))); XML_CONTENT_BOOKS = new String(IOUtil.getAllContentFrom(this.getClass().getResourceAsStream("/books.xml"))); } @Before public void setup() { strategy = new XPathReadExpressionExecutorStrategy(); } private String getXPathType() { return SExpressionType.TYPE_XPATH_READ.name(); } /** * Test method for * {@link org.bonitasoft.engine.expression.impl.XPathReadExpressionExecutorStrategy#evaluate(org.bonitasoft.engine.expression.model.SExpression, java.util.Map, java.util.Map)} * * @throws SExpressionException */ @Test public void evaluateXpathReturnsStringAttr() throws SExpressionException { final String obj = evaluate(XML_CONTENT_AUTHOR, String.class, "//article/@nom"); assertEquals("XPath", obj); } @Test public void evaluateXPathExpressionWithDataAsContent() throws SExpressionException { final String dataName = "myVariable"; final SExpressionImpl dep = new SExpressionImpl(dataName, dataName, SExpressionType.TYPE_VARIABLE.name(), String.class.getName(), null, null); final String xPathSelector = "//article/@nom"; final SExpression expression = new SExpressionImpl("expName1", xPathSelector, getXPathType(), String.class.getName(), null, Arrays. asList(dep)); final Map resolvedExpressions = new HashMap(1); resolvedExpressions.put(dep.getDiscriminant(), XML_CONTENT_AUTHOR); final HashMap dependencyValues = new HashMap(1); final Object result = strategy.evaluate(expression, dependencyValues, resolvedExpressions, ContainerState.ACTIVE); assertEquals("XPath", result); } @Test public void evaluateXpathReturnsStringNode() throws SExpressionException { final String obj = evaluate(XML_CONTENT_BOOKS, String.class, "//book[@id='bk101']/title"); assertEquals("XML Developer's Guide", obj); } @Test(expected = SExpressionEvaluationException.class) public void evaluateXpathInvalidReturnType() throws SExpressionException { final SExpressionImpl dep = new SExpressionImpl(null, XML_CONTENT_AUTHOR, SExpressionType.TYPE_CONSTANT.name(), String.class.getName(), null, null); final SExpression expression = new SExpressionImpl("expName1", "//article", getXPathType(), "InvalidReturnType", null, Arrays. asList(dep)); final Map resolvedExpressions = getResolvedExpressionMap(expression); final HashMap dependencyValues = new HashMap(1); strategy.evaluate(expression, dependencyValues, resolvedExpressions, ContainerState.ACTIVE); } @Test(expected = SExpressionEvaluationException.class) public void evaluateXpathWrongReturnType() throws SExpressionException { evaluate(XML_CONTENT_BOOKS, Integer.class, "//book[@id='bk101']/title"); } @Test(expected = SExpressionEvaluationException.class) public void evaluateEnptyContent() throws Exception { try { evaluate("", Integer.class, "//book[@id='bk101']/title"); } catch (final Exception e) { throw e; } } @Test public void evaluateXpathReturnsBoolean() throws SExpressionException { final Boolean ownedText = evaluate(XML_CONTENT_BOOKS, Boolean.class, "//catalog/book[@id='bk101']/owned"); assertTrue(ownedText); final Boolean ownedNumeric = evaluate(XML_CONTENT_BOOKS, Boolean.class, "//catalog/book[@id='bk102']/owned"); assertTrue(ownedNumeric); final Boolean notOwnedText = evaluate(XML_CONTENT_BOOKS, Boolean.class, "//catalog/book[@id='bk103']/owned"); assertFalse(notOwnedText); final Boolean notOwnedNumeric = evaluate(XML_CONTENT_BOOKS, Boolean.class, "//catalog/book[@id='bk104']/owned"); assertFalse(notOwnedNumeric); } @Test public void evaluateXpathReturnsLong() throws SExpressionException { final Long quantity = evaluate(XML_CONTENT_BOOKS, Long.class, "//catalog/book[@id='bk101']/quantity"); assertEquals(123456789l, quantity.longValue()); } @Test public void evaluateXpathReturnsInteger() throws SExpressionException { final Integer quantity = evaluate(XML_CONTENT_BOOKS, Integer.class, "//catalog/book[@id='bk101']/quantity"); assertEquals(123456789, quantity.intValue()); } @Test public void evaluateXpathReturnsFloat() throws SExpressionException { final Float quantity = evaluate(XML_CONTENT_BOOKS, Float.class, "//catalog/book[@id='bk101']/price"); assertEquals(Float.valueOf(44.95f), quantity); } @Test public void evaluateXpathReturnsDouble() throws SExpressionException { final Double authorCount = evaluate(XML_CONTENT_BOOKS, Double.class, "//catalog/book[@id='bk101']/price"); assertEquals(Double.valueOf(44.95d), authorCount); } @SuppressWarnings("unchecked") private T evaluate(final String content, final Class returnType, final String selector) throws SExpressionEvaluationException, SExpressionDependencyMissingException { final SExpressionImpl dep = new SExpressionImpl(null, content, SExpressionType.TYPE_CONSTANT.name(), String.class.getName(), null, null); final SExpression expression = new SExpressionImpl("expName1", selector, getXPathType(), returnType.getName(), null, Arrays. asList(dep)); final Map resolvedExpressions = getResolvedExpressionMap(expression); final HashMap dependencyValues = new HashMap(1); final Object result = strategy.evaluate(expression, dependencyValues, resolvedExpressions, ContainerState.ACTIVE); return (T) result; } @Test public void evaluateXpathReturnsNode() throws SExpressionException { final Node obj = evaluate(XML_CONTENT_AUTHOR, Node.class, "//article"); assertEquals("article", obj.getNodeName()); } @Test public void evaluateXpathReturnsNodeList() throws SExpressionException { final NodeList nodeList = evaluate(XML_CONTENT_AUTHOR, NodeList.class, "//article/auteurs/auteur/nom"); assertEquals(2, nodeList.getLength()); assertEquals("Dupont", nodeList.item(0).getTextContent()); assertEquals("Dubois", nodeList.item(1).getTextContent()); } @Test(expected = SExpressionDependencyMissingException.class) public void evaluateInvalidDependencies() throws SExpressionException { final SExpression expression = new SExpressionImpl("expName1", "//article/@nom", getXPathType(), String.class.getName(), null, null); final Map resolvedExpressions = new HashMap(0); final HashMap dependencyValues = new HashMap(0); strategy.evaluate(expression, dependencyValues, resolvedExpressions, ContainerState.ACTIVE); } /** * Test method for * {@link org.bonitasoft.engine.expression.impl.XPathReadExpressionExecutorStrategy#validate(SExpression)}. */ @Test(expected = SInvalidExpressionException.class) public void validateNullExpressionContent() throws Exception { final SExpressionImpl dep = new SExpressionImpl(null, XML_CONTENT_AUTHOR, SExpressionType.TYPE_CONSTANT.name(), String.class.getName(), null, null); final SExpression expression = new SExpressionImpl("expName1", null, getXPathType(), Double.class.getName(), null, Arrays. asList(dep)); strategy.validate(expression); } private Map getResolvedExpressionMap(final SExpression... expressions) { final HashMap hashMap = new HashMap(); for (final SExpression expression : expressions) { final List dependencies = expression.getDependencies(); for (final SExpression dependency : dependencies) { hashMap.put(dependency.getDiscriminant(), dependency.getContent()); } } return hashMap; } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/BinaryComparatorExecutorTest.java ================================================ /** * 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.expression.impl.condition; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import java.math.BigDecimal; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.impl.SExpressionImpl; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class BinaryComparatorExecutorTest { @Mock BinaryComparatorMapper mapper; @Mock BinaryComparator evaluator; @InjectMocks private BinaryComparatorExecutor manager; @Test public void evaluate_should_return_the_result_of_selected_evaluator() throws Exception { //given SExpression left = buildIntegerExpression(5); SExpression right = buildIntegerExpression(4); Map resolvedExpressions = new HashMap(); resolvedExpressions.put(left.getDiscriminant(), 5); resolvedExpressions.put(right.getDiscriminant(), 4); SExpression comparisonExpression = buildComparisonExpression(left, right, ">"); given(mapper.getEvaluator(">")).willReturn(evaluator); given(evaluator.evaluate(new BigDecimal(5), new BigDecimal(4))).willReturn(true); //when Boolean value = manager.evaluate(resolvedExpressions, comparisonExpression); //then assertThat(value).isTrue(); } @Test public void evaluate_should_throw_SExpressionEvaluationException_when_no_evaluator_is_found() throws Exception { //given SExpression left = buildIntegerExpression(5); SExpression right = buildIntegerExpression(4); String operator = ">"; SExpression comparisonExpression = buildComparisonExpression(left, right, operator); given(mapper.getEvaluator(operator)).willReturn(null); try { //when manager.evaluate(Collections. emptyMap(), comparisonExpression); fail("exception expected"); } catch (SExpressionEvaluationException e) { //then assertThat(e.getMessage()).isEqualTo("Unable to find evaluator for operator '" + operator + "'"); } } @Test public void evaluate_should_throw_SExpressionEvaluationException_when_number_there_are_not_exactly_2_dependencies() throws Exception { //given SExpression expression = mock(SExpression.class); String operator = ">"; given(expression.getContent()).willReturn(operator); given(expression.getName()).willReturn(operator); given(expression.getDependencies()) .willReturn(Arrays.asList(mock(SExpression.class), mock(SExpression.class), mock(SExpression.class))); try { //when manager.evaluate(Collections. emptyMap(), expression); fail("exception expected"); } catch (SExpressionEvaluationException e) { //then assertThat(e.getMessage()).isEqualTo( "The expression '" + expression.getContent() + "' has " + expression.getDependencies().size() + " dependencies, but it must have exactly " + 2 + " dependencies."); } } @Test public void evaluate_should_throw_SExpressionEvaluationException_when_return_types_are_incompatible() throws Exception { //given SExpression left = buildIntegerExpression(5); SExpression right = buildBooleanExpression(true); String operator = ">"; SExpression comparisonExpression = buildComparisonExpression(left, right, operator); try { //when manager.evaluate(Collections. emptyMap(), comparisonExpression); fail("exception expected"); } catch (SExpressionEvaluationException e) { //then assertThat(e.getMessage()) .isEqualTo("The two dependencies of expression '" + operator + "' must have the same return type."); } } @Test public void evaluate_should_throw_SExpressionEvaluationException_when_evaluator_throws_SComparisonException() throws Exception { //given String operator = ">"; given(mapper.getEvaluator(operator)).willReturn(evaluator); given(evaluator.evaluate(new BigDecimal(5), new BigDecimal(5))).willThrow(new SComparisonException("")); SExpression left = buildIntegerExpression(5); SExpression right = buildIntegerExpression(5); Map resolvedExpressions = new HashMap(); resolvedExpressions.put(left.getDiscriminant(), 5); resolvedExpressions.put(right.getDiscriminant(), 5); SExpression comparisonExpression = buildComparisonExpression(left, right, operator); try { //when manager.evaluate(resolvedExpressions, comparisonExpression); fail("exception expected"); } catch (SExpressionEvaluationException e) { //then assertThat(e.getMessage()) .isEqualTo("Unable to evaluate expression '" + comparisonExpression.getName() + "'"); } } @Test public void areCompatible_should_return_true_when_two_parameters_has_the_same_return_type() throws Exception { //given //when boolean value = manager.areReturnTypeCompatible(String.class.getName(), String.class.getName(), ">"); //then assertThat(value).isTrue(); } @Test public void areCompatible_should_return_false_when_two_parameters_has_different_return_types() throws Exception { //given //when boolean value = manager.areReturnTypeCompatible(String.class.getName(), Integer.class.getName(), ">"); //then assertThat(value).isFalse(); } @Test public void areCompatible_should_return_true_when_two_parameters_has_different_return_types_but_operator_is_equals_to() throws Exception { //given //when boolean value = manager.areReturnTypeCompatible(String.class.getName(), Integer.class.getName(), "=="); //then assertThat(value).isTrue(); } @Test public void areCompatible_should_return_true_when_two_parameters_has_different_return_types_but_both_are_numeric() throws Exception { //given //when boolean value = manager.areReturnTypeCompatible(Long.class.getName(), Integer.class.getName(), ">"); //then assertThat(value).isTrue(); } @Test public void transtype_integer_should_return_bigDecimal() throws Exception { //given //when Object value = manager.transtypeIfApplicable(Integer.class.getName(), 5); //then assertThat(value).isInstanceOf(BigDecimal.class); assertThat(value).isEqualTo(new BigDecimal(5)); } @Test public void transtype_long_should_return_bigDecimal() throws Exception { //given //when Object value = manager.transtypeIfApplicable(Long.class.getName(), 5L); //then assertThat(value).isInstanceOf(BigDecimal.class); assertThat(value).isEqualTo(new BigDecimal(5)); } @Test public void transtype_float_should_return_bigDecimal() throws Exception { //given //when Object value = manager.transtypeIfApplicable(Float.class.getName(), 5.2f); //then assertThat(value).isInstanceOf(BigDecimal.class); assertThat(value).isEqualTo(new BigDecimal("5.2")); } @Test public void transtype_double_should_return_bigDecimal() throws Exception { //given //when Object value = manager.transtypeIfApplicable(Double.class.getName(), 5.2d); //then assertThat(value).isInstanceOf(BigDecimal.class); assertThat(value).isEqualTo(new BigDecimal("5.2")); } @Test public void transtype_null_should_return_null() throws Exception { //given //when Object value = manager.transtypeIfApplicable(Integer.class.getName(), null); //then assertThat(value).isNull(); } @Test public void transtype_non_numeric_should_return_same_object() throws Exception { //given //when Object value = manager.transtypeIfApplicable(String.class.getName(), "just a string"); //then assertThat(value).isInstanceOf(String.class); assertThat(value).isEqualTo("just a string"); } private SExpression buildIntegerExpression(int value) { final SExpressionImpl expression = new SExpressionImpl(); String strValue = String.valueOf(value); expression.setName(strValue); expression.setContent(strValue); expression.setExpressionType(SExpression.TYPE_CONSTANT); expression.setReturnType(Integer.class.getName()); expression.setDependencies(Collections. emptyList()); return expression; } private SExpression buildComparisonExpression(SExpression leftExpression, SExpression rightExpression, String operator) { final SExpressionImpl expression = new SExpressionImpl(); expression.setName("compare"); expression.setContent(operator); expression.setExpressionType(SExpression.TYPE_CONDITION); expression.setReturnType(Boolean.class.getName()); expression.setDependencies(Arrays.asList(leftExpression, rightExpression)); return expression; } private SExpression buildBooleanExpression(boolean value) { final SExpressionImpl expression = new SExpressionImpl(); String strValue = String.valueOf(value); expression.setName(strValue); expression.setContent(strValue); expression.setExpressionType(SExpression.TYPE_CONSTANT); expression.setReturnType(Boolean.class.getName()); expression.setDependencies(Collections. emptyList()); return expression; } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/BinaryComparatorMapperTest.java ================================================ /** * 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.expression.impl.condition; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Before; import org.junit.Test; public class BinaryComparatorMapperTest { private BinaryComparatorMapper mapper; @Before public void setUp() throws Exception { mapper = new BinaryComparatorMapper(new EqualityComparator(), new InequalityComparator()); } @Test public void getEvaluator_for_equals_operator_should_return_EqualsEvaluator() throws Exception { //given //when BinaryComparator evaluator = mapper.getEvaluator("=="); //then assertThat(evaluator).isInstanceOf(EqualsComparator.class); } @Test public void getEvaluator_for_Different_operator_should_return_DifferentEvaluator() throws Exception { //given //when BinaryComparator evaluator = mapper.getEvaluator("!="); //then assertThat(evaluator).isInstanceOf(DifferentComparator.class); } @Test public void getEvaluator_for_greaterThan_operator_should_return_GreaterThanEvaluator() throws Exception { //given //when BinaryComparator evaluator = mapper.getEvaluator(">"); //then assertThat(evaluator).isInstanceOf(GreaterThanComparator.class); } @Test public void getEvaluator_for_greaterThanOrEquals_operator_should_return_GreaterThanOrEqualsEvaluator() throws Exception { //given //when BinaryComparator evaluator = mapper.getEvaluator(">="); //then assertThat(evaluator).isInstanceOf(GreaterThanOrEqualsComparator.class); } @Test public void getEvaluator_for_LessThan_operator_should_return_LessThanEvaluator() throws Exception { //given //when BinaryComparator evaluator = mapper.getEvaluator("<"); //then assertThat(evaluator).isInstanceOf(LessThanComparator.class); } @Test public void getEvaluator_for_LessThanOrEquals_operator_should_return_LessThanOrEqualsEvaluator() throws Exception { //given //when BinaryComparator evaluator = mapper.getEvaluator("<="); //then assertThat(evaluator).isInstanceOf(LessThanOrEqualsComparator.class); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/BinaryComparatorTest.java ================================================ /** * 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.expression.impl.condition; import org.junit.Test; public class BinaryComparatorTest { @Test public void evaluate() throws Exception { //given //when //then } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/DifferentComparatorTest.java ================================================ /** * 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.expression.impl.condition; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class DifferentComparatorTest { @Mock private EqualityComparator comparator; @InjectMocks private DifferentComparator evaluator; @Test public void evaluate_should_return_true_when_comparator_returns_false() throws Exception { //given given(comparator.areEquals("a", "b")).willReturn(false); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isTrue(); } @Test public void evaluate_should_return_false_when_comparator_returns_true() throws Exception { //given given(comparator.areEquals("a", "a")).willReturn(true); //when Boolean value = evaluator.evaluate("a", "a"); //then assertThat(value).isFalse(); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/EqualityComparatorTest.java ================================================ /** * 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.expression.impl.condition; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Before; import org.junit.Test; public class EqualityComparatorTest { private EqualityComparator comparator; @Before public void setUp() throws Exception { comparator = new EqualityComparator(); } @Test public void areEquals_should_return_true_when_two_parameters_are_null() throws Exception { //given //when Boolean equals = comparator.areEquals(null, null); //then assertThat(equals).isTrue(); } @Test public void areEquals_should_return_false_when_first_parameter_is_null_and_second_one_is_not_null() throws Exception { //given //when Boolean equals = comparator.areEquals(null, "not null"); //then assertThat(equals).isFalse(); } @Test public void areEquals_should_return_false_when_first_parameter_is_not_null_and_second_one_is_null() throws Exception { //given //when Boolean equals = comparator.areEquals("not null", null); //then assertThat(equals).isFalse(); } @Test public void areEquals_should_return_true_when_two_parameters_are_equals() throws Exception { //given //when Boolean equals = comparator.areEquals("equal", "equal"); //then assertThat(equals).isTrue(); } @Test public void areEquals_should_return_false_when_two_parameters_are_different() throws Exception { //given //when Boolean equals = comparator.areEquals("not null", "not null, but different"); //then assertThat(equals).isFalse(); } @Test public void can_compare_different_types() throws Exception { //given //when Boolean equals = comparator.areEquals("not null", 5); //then assertThat(equals).isFalse(); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/EqualsComparatorTest.java ================================================ /** * 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.expression.impl.condition; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class EqualsComparatorTest { @Mock private EqualityComparator comparator; @InjectMocks private EqualsComparator evaluator; @Test public void evaluate_should_return_true_when_comparator_return_true() throws Exception { //given given(comparator.areEquals("a", "a")).willReturn(true); //when Boolean value = evaluator.evaluate("a", "a"); //then assertThat(value).isTrue(); } @Test public void evaluate_should_return_false_when_comparator_return_false() throws Exception { //given given(comparator.areEquals("a", "b")).willReturn(false); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isFalse(); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/GreaterThanComparatorTest.java ================================================ /** * 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.expression.impl.condition; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class GreaterThanComparatorTest { @Mock private InequalityComparator comparator; @InjectMocks private GreaterThanComparator evaluator; @Test public void evaluate_should_return_true_when_compare_is_greater_than_zero() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(1); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isTrue(); } @Test public void evaluate_should_return_false_when_compare_is_zero() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(0); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isFalse(); } @Test public void evaluate_should_return_false_when_compare_is_less_than_zero() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(-1); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isFalse(); } @Test public void evaluate_should_return_null_when_compare_is_null() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(null); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isNull(); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/GreaterThanOrEqualsComparatorTest.java ================================================ /** * 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.expression.impl.condition; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class GreaterThanOrEqualsComparatorTest { @Mock private InequalityComparator comparator; @InjectMocks private GreaterThanOrEqualsComparator evaluator; @Test public void evaluate_should_return_true_when_compare_is_greater_than_zero() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(1); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isTrue(); } @Test public void evaluate_should_return_true_when_compare_is_zero() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(0); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isTrue(); } @Test public void evaluate_should_return_false_when_compare_is_less_than_zero() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(-1); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isFalse(); } @Test public void evaluate_should_return_null_when_compare_is_null() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(null); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isNull(); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/InequalityComparatorTest.java ================================================ /** * 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.expression.impl.condition; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Before; import org.junit.Test; public class InequalityComparatorTest { private InequalityComparator comparator; @Before public void setUp() throws Exception { comparator = new InequalityComparator(); } @Test public void compareTo_should_return_positive_integer_when_left_is_greater_than_right() throws Exception { //given //when Integer value = comparator.compareTo(5, 4); //then assertThat(value).isGreaterThan(0); } @Test public void compareTo_should_return_negative_integer_when_left_is_greater_than_right() throws Exception { //given //when Integer value = comparator.compareTo(3, 4); //then assertThat(value).isLessThan(0); } @Test public void compareTo_should_return_null_when_first_parameter_is_null() throws Exception { //given //when Integer value = comparator.compareTo(null, 4); //then assertThat(value).isNull(); } @Test public void compareTo_should_return_null_when_second_parameter_is_null() throws Exception { //given //when Integer value = comparator.compareTo(3, null); //then assertThat(value).isNull(); } @Test(expected = SComparisonException.class) public void compareTo_should_return_throw_SComparisonException_when_parameters_does_not_implement_comparable() throws Exception { //given //when comparator.compareTo(new NotComparableClass(), new NotComparableClass()); //then exception } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/LessThanComparatorTest.java ================================================ /** * 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.expression.impl.condition; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class LessThanComparatorTest { @Mock private InequalityComparator comparator; @InjectMocks private LessThanComparator evaluator; @Test public void evaluate_should_return_true_when_compare_is_less_than_zero() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(-1); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isTrue(); } @Test public void evaluate_should_return_false_when_compare_is_zero() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(0); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isFalse(); } @Test public void evaluate_should_return_false_when_compare_is_greater_than_zero() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(1); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isFalse(); } @Test public void evaluate_should_return_null_when_compare_is_null() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(null); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isNull(); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/LessThanOrEqualsComparatorTest.java ================================================ /** * 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.expression.impl.condition; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class LessThanOrEqualsComparatorTest { @Mock private InequalityComparator comparator; @InjectMocks private LessThanOrEqualsComparator evaluator; @Test public void evaluate_should_return_true_when_compare_is_less_than_zero() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(-1); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isTrue(); } @Test public void evaluate_should_return_true_when_compare_is_zero() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(0); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isTrue(); } @Test public void evaluate_should_return_false_when_compare_is_greater_than_zero() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(1); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isFalse(); } @Test public void evaluate_should_return_null_when_compare_is_null() throws Exception { //given given(comparator.compareTo("a", "b")).willReturn(null); //when Boolean value = evaluator.evaluate("a", "b"); //then assertThat(value).isNull(); } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/LogicalComplementExecutorTest.java ================================================ /** * 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.expression.impl.condition; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.expression.exception.SExpressionEvaluationException; import org.bonitasoft.engine.expression.impl.ConditionExpressionExecutorStrategy; import org.bonitasoft.engine.expression.model.SExpression; import org.bonitasoft.engine.expression.model.impl.SExpressionImpl; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class LogicalComplementExecutorTest { @InjectMocks private LogicalComplementExecutor logicalComplementExecutor; @Test public void evaluate_should_return_true_when_the_resolution_of_first_dependency_is_false() throws Exception { //given SExpression sourceExpression = buildBooleanExpression(false); Map resolvedExpressions = Collections . singletonMap(sourceExpression.getDiscriminant(), false); SExpression logicalComplementExpression = buildLogicalComplementExpression(sourceExpression); //when Boolean value = logicalComplementExecutor.evaluate(resolvedExpressions, logicalComplementExpression); //then assertThat(value).isTrue(); } @Test public void evaluate_should_return_false_when_the_resolution_of_first_dependency_is_true() throws Exception { //given SExpression sourceExpression = buildBooleanExpression(true); Map resolvedExpressions = Collections . singletonMap(sourceExpression.getDiscriminant(), true); SExpression logicalComplementExpression = buildLogicalComplementExpression(sourceExpression); //when Boolean value = logicalComplementExecutor.evaluate(resolvedExpressions, logicalComplementExpression); //then assertThat(value).isFalse(); } @Test public void evaluate_should_return_null_when_the_resolution_of_first_dependency_is_null() throws Exception { //given SExpression sourceExpression = buildBooleanExpression(true); Map resolvedExpressions = Collections.emptyMap(); SExpression logicalComplementExpression = buildLogicalComplementExpression(sourceExpression); //when Boolean value = logicalComplementExecutor.evaluate(resolvedExpressions, logicalComplementExpression); //then assertThat(value).isNull(); } @Test public void evaluate_should_throws_SExpressionEvaluationException_when_dependencies_has_size_different_of_one() throws Exception { //given List dependencies = Arrays.asList(mock(SExpression.class), mock(SExpression.class)); SExpression expression = mock(SExpression.class); given(expression.getName()).willReturn("my expr"); given(expression.getDependencies()).willReturn(dependencies); try { //when logicalComplementExecutor.evaluate(Collections. emptyMap(), expression); fail("Exception expected"); } catch (SExpressionEvaluationException e) { //then assertThat(e.getMessage()).isEqualTo( "The expression '" + ConditionExpressionExecutorStrategy.LOGICAL_COMPLEMENT_OPERATOR + "' must have exactly 1 dependency."); assertThat(e.getExpressionName()).isEqualTo("my expr"); } } @Test public void evaluate_should_throws_SExpressionEvaluationException_when_dependency_does_not_return_a_boolean() throws Exception { //given SExpression sourceExpression = mock(SExpression.class); given(sourceExpression.getReturnType()).willReturn(Integer.class.getName()); SExpression expression = mock(SExpression.class); given(expression.getName()).willReturn("my expr"); given(expression.getDependencies()).willReturn(Collections.singletonList(sourceExpression)); try { //when logicalComplementExecutor.evaluate(Collections. emptyMap(), expression); fail("Exception expected"); } catch (SExpressionEvaluationException e) { //then assertThat(e.getMessage()).isEqualTo( "The dependency of expression '!' must have the return type java.lang.Boolean, but java.lang.Integer was found."); assertThat(e.getExpressionName()).isEqualTo("my expr"); } } private SExpression buildBooleanExpression(boolean value) { final SExpressionImpl expression = new SExpressionImpl(); String strValue = String.valueOf(value); expression.setName(strValue); expression.setContent(strValue); expression.setExpressionType(SExpression.TYPE_CONSTANT); expression.setReturnType(Boolean.class.getName()); expression.setDependencies(Collections. emptyList()); return expression; } private SExpression buildLogicalComplementExpression(SExpression sourceExpression) { final SExpressionImpl expression = new SExpressionImpl(); expression.setName("not"); expression.setContent(ConditionExpressionExecutorStrategy.LOGICAL_COMPLEMENT_OPERATOR); expression.setExpressionType(SExpression.TYPE_CONDITION); expression.setReturnType(Boolean.class.getName()); expression.setDependencies(Collections.singletonList(sourceExpression)); return expression; } } ================================================ FILE: services/bonita-expression/src/test/java/org/bonitasoft/engine/expression/impl/condition/NotComparableClass.java ================================================ /** * 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.expression.impl.condition; /** * @author Elias Ricken de Medeiros */ public class NotComparableClass { } ================================================ FILE: services/bonita-expression/src/test/resources/authors.xml ================================================
      Dupont Dubois
      ================================================ FILE: services/bonita-expression/src/test/resources/books.xml ================================================ Gambardella, Matthew XML Developer's Guide Computer 44.95 2000-10-01 An in-depth look at creating applications with XML. true 123456789 Ralls, Kim Midnight Rain Fantasy 5.95 2000-12-16 A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world. 1 Corets, Eva Maeve Ascendant Fantasy 5.95 2000-11-17 After the collapse of a nanotechnology society in England, the young survivors lay the foundation for a new society. false Corets, Eva Oberon's Legacy Fantasy 5.95 2001-03-10 In post-apocalypse England, the mysterious agent known only as Oberon helps to create a new life for the inhabitants of London. Sequel to Maeve Ascendant. 0 Corets, Eva The Sundered Grail Fantasy 5.95 2001-09-10 The two daughters of Maeve, half-sisters, battle one another for control of England. Sequel to Oberon's Legacy. Randall, Cynthia Lover Birds Romance 4.95 2000-09-02 When Carla meets Paul at an ornithology conference, tempers fly as feathers get ruffled. Thurman, Paula Splish Splash Romance 4.95 2000-11-02 A deep sea diver finds true love twenty thousand leagues beneath the sea. Knorr, Stefan Creepy Crawlies Horror 4.95 2000-12-06 An anthology of horror stories about roaches, centipedes, scorpions and other insects. Kress, Peter Paradox Lost Science Fiction 6.95 2000-11-02 After an inadvertant trip through a Heisenberg Uncertainty Device, James Salway discovers the problems of being quantum. O'Brien, Tim Microsoft .NET: The Programming Bible Computer 36.95 2000-12-09 Microsoft's .NET initiative is explored in detail in this deep programmer's reference. O'Brien, Tim MSXML3: A Comprehensive Guide Computer 36.95 2000-12-01 The Microsoft MSXML3 parser is covered in detail, with attention to XML DOM interfaces, XSLT processing, SAX and more. Galos, Mike Visual Studio 7: A Comprehensive Guide Computer 49.95 2001-04-16 Microsoft Visual Studio 7 is explored in depth, looking at how Visual Basic, Visual C++, C#, and ASP+ are integrated into a comprehensive development environment. ================================================ FILE: services/bonita-expression/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: services/bonita-identity/build.gradle ================================================ dependencies { api project(':bpm:bonita-common') api project(':services:bonita-log') api project(':services:bonita-builder') api project(':services:bonita-persistence') api project(':services:bonita-commons') api project(':services:bonita-events') api libs.jakartaActivation testImplementation libs.mockitoCore testImplementation libs.assertj annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/IconService.java ================================================ /** * Copyright (C) 2021 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.identity; import java.util.Optional; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; public interface IconService { String EVENT_NAME = "ICON"; Optional replaceIcon(String iconFilename, byte[] iconContent, Long iconIdToReplace) throws SBonitaReadException, SRecorderException; SIcon createIcon(String iconFilename, byte[] iconContent) throws SRecorderException; SIcon getIcon(Long id) throws SBonitaReadException; void deleteIcon(Long iconId) throws SBonitaReadException, SRecorderException; } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/IconServiceImpl.java ================================================ /** * Copyright (C) 2021 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.identity; import java.util.Optional; import org.bonitasoft.engine.commons.io.IOUtil; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.services.PersistenceService; import org.springframework.stereotype.Service; @Service public class IconServiceImpl implements IconService { private final Recorder recorder; private final PersistenceService persistenceService; public IconServiceImpl(Recorder recorder, PersistenceService persistenceService) { this.recorder = recorder; this.persistenceService = persistenceService; } @Override public Optional replaceIcon(String iconFilename, byte[] iconContent, Long iconIdToReplace) throws SBonitaReadException, SRecorderException { deleteIcon(iconIdToReplace); if (iconContent != null) { return Optional.of(createIcon(iconFilename, iconContent).getId()); } return Optional.empty(); } @Override public SIcon createIcon(String iconFilename, byte[] iconContent) throws SRecorderException { if (iconFilename == null || iconFilename.isBlank() || iconContent == null || iconContent.length == 0) { throw new IllegalArgumentException("Unable to create an icon without filename or content"); } SIcon entity = new SIcon(IOUtil.getContentTypeForIcon(iconFilename), iconContent); recorder.recordInsert(new InsertRecord(entity), EVENT_NAME); return entity; } @Override public SIcon getIcon(Long iconId) throws SBonitaReadException { if (iconId == null) { return null; } return persistenceService.selectById(new SelectByIdDescriptor<>(SIcon.class, iconId)); } @Override public void deleteIcon(Long iconId) throws SBonitaReadException, SRecorderException { if (iconId == null) { return; } SIcon icon = getIcon(iconId); if (icon == null) { return; } recorder.recordDelete(new DeleteRecord(icon), EVENT_NAME); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/IdentityService.java ================================================ /** * 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.identity; import java.util.List; import org.bonitasoft.engine.identity.model.SContactInfo; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.model.SCustomUserInfoValue; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * This service allow to manage the users and organizations and membership and the relations between them. * * @author Anthony Birembaut, Baptiste Mesta, Matthieu Chaffotte * @since 6.0 */ public interface IdentityService { String GROUP = "GROUP"; String CUSTOM_USER_INFO_DEFINITION = "CUSTOM_USER_INFO_DEFINITION"; String CUSTOM_USER_INFO_VALUE = "CUSTOM_USER_INFO_VALUE"; String ROLE = "ROLE"; String USER = "USER"; String USER_LOGIN = "USER_LOGIN"; String USER_CONTACT_INFO = "USER_CONTACT_INFO"; String USERMEMBERSHIP = "USERMEMBERSHIP"; String ICON = "ICON"; /** * Gets the {@link SRole} given by its identifier. * * @param roleId * the role identifier * @return the role of the given id * @throws SRoleNotFoundException * occurs when the roleId does not refer to any role. */ SRole getRole(long roleId) throws SRoleNotFoundException; /** * Get the {@link SRole} given by its name. * * @param roleName * The name of role * @return the role of the given name * @throws SRoleNotFoundException * occurs when the roleName does not refer to any role. */ SRole getRoleByName(String roleName) throws SRoleNotFoundException; /** * Get total number of {@link SRole} for this tenant * * @return the total number of roles for this tenant * @throws SIdentityException * occurs on persistence layer access problem */ long getNumberOfRoles() throws SIdentityException; /** * Get a {@link List} of {@link SRole} by their identifiers * * @param roleIds * the role identifiers * @return a list of {@link SRole} objects * @throws SRoleNotFoundException * Occurs when roleId does not refer to any role. */ List getRoles(List roleIds) throws SRoleNotFoundException; /** * Get a {@link List} of {@link SRole} in specific interval * If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfRoles * Number of result to retrieve * @return a list of SRole objects * @throws SIdentityException * occurs on persistence layer access problem */ List getRoles(int fromIndex, int numberOfRoles) throws SIdentityException; /** * Get a {@link List} of {@link SRole} in specific interval, sorted by field attribute in the given * {@link OrderByType} order. *

      For instance, getRoles(0,10,"displayName", OrderByType.DESC) returns the 10 first roles sorted by * "displayName" in desc order *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. *

      check {@link SRoleBuilderFactory} to get a list of available field keys to sort * * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfRoles * Number of result to retrieve * @param field * The field used by the sort * @param order * ASC or DESC * @return a list of paginated SRole objects * @throws SIdentityException * occurs on persistence layer access problem * @see SRoleBuilderFactory to get a list of available field keys to use */ List getRoles(int fromIndex, int numberOfRoles, String field, OrderByType order) throws SIdentityException; /** * Get the {@link SGroup} by its path * * @param groupPath * The group path * @return the group * @throws SGroupNotFoundException * Occurs when the groupPath does not refer to any group. */ SGroup getGroupByPath(String groupPath) throws SGroupNotFoundException; /** * Get {@link SGroup} by its identifier * * @param groupId * The group identifier * @return the group * @throws SGroupNotFoundException * occurs when the groupId does not refer to any group. */ SGroup getGroup(long groupId) throws SGroupNotFoundException; /** * Get total number of {@link SGroup} in the current tenant * * @return the total number of {@link SGroup} * @throws SIdentityException * occurs on persistence layer access problem */ long getNumberOfGroups() throws SIdentityException; /** * Get a {@link List} of {@link SGroup} for specific groupIds * * @param groupIds * The group identifiers * @return a {@link List} of {@link SGroup} object * @throws SGroupNotFoundException * occurs when no given group identifiers refer to any group. */ List getGroups(List groupIds) throws SGroupNotFoundException; /** * Get a {@link List} of {@link SGroup} in a specific interval, this is used for pagination * If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfGroups * Number of result to retrieve * @return a {@link List} of {@link SGroup} * @throws SIdentityException * occurs on persistence layer access problem */ List getGroups(int fromIndex, int numberOfGroups) throws SIdentityException; /** * Get a {@link List} of {@link SGroup} in specific interval, sorted by field attribute in the given * {@link OrderByType} order. *

      For instance, getGroups(0,10,"displayName", OrderByType.DESC) returns the 10 first {@link SGroup} sorted by * "displayName" in desc order *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. *

      check {@link SGroupBuilderFactory} to get a list of available field keys to sort * * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfGroups * Number of result to retrieve * @param field * The field used to sort * @param order * ASC or DESC * @return a {@link List} of paginated {@link SGroup} objects * @throws SIdentityException * occurs on persistence layer access problem * @see SGroupBuilderFactory */ List getGroups(int fromIndex, int numberOfGroups, String field, OrderByType order) throws SIdentityException; /** * Get number of child groups for the group identified by the given id * * @param parentGroupId * The parent group identifier * @return the number of child groups * @throws SIdentityException * occurs on persistence layer access problem */ long getNumberOfGroupChildren(long parentGroupId) throws SIdentityException; /** * Get a {@link List} child {@link SGroup} in a specific interval for specific group. This is used for pagination * If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * * @param parentGroupId * The parent group identifier * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfGroups * Number of result to retrieve * @return a {@link List} of child {@link SGroup} * @throws SIdentityException * occurs on persistence layer access problem */ List getGroupChildren(long parentGroupId, int fromIndex, int numberOfGroups) throws SIdentityException; /** * Get a {@link List} of child {@link SGroup} in specific interval, sorted by field attribute in the given * {@link OrderByType} order. *

      For instance, getGroupChildren(0,10,"displayName", OrderByType.DESC) returns the 10 first child * {@link SGroup} of the parent {@link SGroup} * identified by parentGroupId sorted by "displayName" in desc order *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. *

      check {@link SGroupBuilderFactory} to get a list of available field keys to sort * * @param parentGroupId * The parent group identifier * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfGroups * Number of result to retrieve * @param field * The field used to sort * @param order * ASC or DESC * @return a {@link List} of child {@link SGroup} * @throws SIdentityException * occurs on persistence layer access problem */ List getGroupChildren(long parentGroupId, int fromIndex, int numberOfGroups, String field, OrderByType order) throws SIdentityException; /** * Get {@link SUser} by its id * * @param userId * The user identifier * @return the user * @throws SUserNotFoundException * occurs when the userId does not refer to any user. */ SUser getUser(long userId) throws SUserNotFoundException; /** * Checks whether the couple user/password is valid. * * @param user * the user * @param password * the password * @return true if the couple user/password is valid; false otherwise */ boolean checkCredentials(SUser user, String password); /** * Get {@link SUser} by its name * * @param username * The user name * @return the user * @throws SUserNotFoundException * occurs when the user name does not refer to any user. */ SUser getUserByUserName(String username) throws SUserNotFoundException; /** * Get total number of {@link SUser} on the current tenant * * @return the total number of users * @throws SIdentityException * occurs on persistence layer access problem */ long getNumberOfUsers() throws SIdentityException; /** * Get {@link SUser} by their userIds * * @param userIds * A {@link List} of user identifiers * @return a {@link List} of {@link SUser} * @throws SUserNotFoundException * occurs when none of the given userIds refer to any user. */ List getUsers(List userIds) throws SUserNotFoundException; /** * retrieve a {@link List} of {@link SUser} from their names. * * @param userNames * the list of user names * @return a {@link List} of {@link SUser} * @throws SIdentityException * If an exception occurs when retrieving the users */ List getUsersByUsername(List userNames) throws SIdentityException; /** * Get a {@link List} of {@link SUser} from a specific interval. * If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfUsers * Number of result to retrieve * @return a {@link List} of {@link SUser} * @throws SIdentityException * occurs on persistence layer access problem */ List getUsers(int fromIndex, int numberOfUsers) throws SIdentityException; /** * Get a {@link List} of child {@link SUser} from specific interval, sorted by field attribute in the given * {@link OrderByType} order order. *

      For instance, getUsers(0,10,"displayName", OrderByType.DESC) returns the 10 first {@link SUser} sorted by * "displayName" in desc order *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. *

      check {@link SUser} to get a list of available field keys to sort * * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfUsers * Number of result we want to get. Maximum number of result returned. * @param field * The field used by the order * @param order * ASC or DESC * @return a {@link List} of paginated {@link SUser} * @throws SIdentityException * occurs on persistence layer access problem */ List getUsers(int fromIndex, int numberOfUsers, String field, OrderByType order) throws SIdentityException; /** * Get all users (both active and inactive) managed by a specific manager from a specific interval. * If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * * @param managerId * The manager identifier, actually it is user identifier * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberMaxOfUsers * Number of result to retrieve * @param field * The field used to sort * @param order * ASC or DESC * @return a {@link List} of {@link SUser} * @throws SIdentityException * occurs on persistence layer access problem */ List getUsersWithManager(long managerId, int fromIndex, int numberMaxOfUsers, String field, OrderByType order) throws SIdentityException; /** * Get all active users managed by a specific manager from a specific interval. * If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * * @param managerId * The manager identifier, actually it is user identifier * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberMaxOfUsers * Number of result to retrieve * @param field * The field used to sort * @param order * ASC or DESC * @return a {@link List} of {@link SUser} * @throws SIdentityException * occurs on persistence layer access problem */ List getActiveUsersWithManager(long managerId, int fromIndex, int numberMaxOfUsers, String field, OrderByType order) throws SIdentityException; /** * Get all inactive users managed by a specific manager from a specific interval. * If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * * @param managerId * The manager identifier, actually it is user identifier * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberMaxOfUsers * Number of result to retrieve * @param field * The field used to sort * @param order * ASC or DESC * @return a {@link List} of {@link SUser} * @throws SIdentityException * occurs on persistence layer access problem */ List getInactiveUsersWithManager(long managerId, int fromIndex, int numberMaxOfUsers, String field, OrderByType order) throws SIdentityException; /** * Get total number of users for the given role on the current tenant * * @param roleId * The identifier of role * @return total number of users related to the given role * @throws SIdentityException * occurs on persistence layer access problem */ long getNumberOfUsersByRole(long roleId) throws SIdentityException; /** * Get a {@link List} of {@link SUser} from a specific interval for the given {@link SRole} identifier. * If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * Note that this method returns both active and inactive users. * * @param roleId * The identifier of the {@link SRole} of the {@link SUser} to retrieve * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfUsers * Number of result to retrieve * @return a {@link List} of {@link SUser} * @throws SIdentityException * occurs on persistence layer access problem */ List getUsersWithRole(long roleId, int fromIndex, int numberOfUsers) throws SIdentityException; /** * Get a {@link List} of active {@link SUser} from a specific interval for the given {@link SRole} identifier. * If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * * @param roleId * The identifier of the {@link SRole} of the {@link SUser} to retrieve * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfUsers * Number of result to retrieve * @return a {@link List} of {@link SUser} * @throws SIdentityException * occurs on persistence layer access problem */ List getActiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers) throws SIdentityException; /** * Get a {@link List} of inactive {@link SUser} from a specific interval for the given {@link SRole} identifier. * If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * * @param roleId * The identifier of the {@link SRole} of the {@link SUser} to retrieve * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfUsers * Number of result to retrieve * @return a {@link List} of {@link SUser} * @throws SIdentityException * occurs on persistence layer access problem */ List getInactiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers) throws SIdentityException; /** * Get a {@link List} of {@link SUser} for given {@link SRole} from specific interval, sorted by field * attribute in the given {@link OrderByType} * order order. *

      For instance, getUsersWithRole(1,0,10,"displayName", OrderByType.DESC) returns the 10 first {@link SUser} of * the {@link SRole} with id '1' sorted by * "displayName" in desc order *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. *

      check {@link SUser} to get a list of available field keys to sort * * @param roleId * The identifier of the {@link SRole} * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfUsers * Number of result to retrieve * @param field * The field used to sort * @param order * ASC or DESC * @return a {@link List} of {@link SUser} * @throws SIdentityException * occurs on persistence layer access problem */ List getUsersWithRole(long roleId, int fromIndex, int numberOfUsers, String field, OrderByType order) throws SIdentityException; /** * Get a {@link List} of active {@link SUser} for given {@link SRole} from specific interval, sorted by field * attribute in the given * {@link OrderByType} * order order. *

      For instance, getActiveUsersWithRole(1,0,10,"displayName", OrderByType.DESC) returns the 10 first * {@link SUser} of the {@link SRole} with id '1' * sorted * by * "displayName" in desc order *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. *

      check {@link SUser} to get a list of available field keys to sort * * @param roleId * The identifier of the {@link SRole} * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfUsers * Number of result to retrieve * @param field * The field used to sort * @param order * ASC or DESC * @return a {@link List} of {@link SUser} * @throws SIdentityException * occurs on persistence layer access problem */ List getActiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers, String field, OrderByType order) throws SIdentityException; /** * Get a {@link List} of Inactive {@link SUser} for given {@link SRole} from specific interval, sorted by * field attribute in the given * {@link OrderByType} * order order. *

      For instance, getInactiveUsersWithRole(1,0,10,"displayName", OrderByType.DESC) returns the 10 first * {@link SUser} of the {@link SRole} with id '1' * sorted by * "displayName" in desc order *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. *

      check {@link SUser} to get a list of available field keys to sort * * @param roleId * The identifier of the {@link SRole} * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfUsers * Number of result to retrieve * @param field * The field used to sort * @param order * ASC or DESC * @return a {@link List} of {@link SUser} * @throws SIdentityException * occurs on persistence layer access problem */ List getInactiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers, String field, OrderByType order) throws SIdentityException; /** * Get total number of users for the given {@link SGroup} * * @param groupId * The identifier of the {@link SGroup} * @return total number of users in the given group * @throws SIdentityException * occurs on persistence layer access problem */ long getNumberOfUsersByGroup(long groupId) throws SIdentityException; /** * Get a {@link List} of Active {@link SUser} for given {@link SGroup} from specific interval, sorted by * field attribute in the given * {@link OrderByType} * order order. *

      For instance, getActiveUsersInGroup(1,0,10,"displayName", OrderByType.DESC) returns the 10 first * {@link SUser} of the {@link SGroup} with id '1' * sorted by * "displayName" in desc order *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. *

      check {@link SUser} to get a list of available field keys to sort * * @param groupId * Identifier of the {@link SGroup} * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfUsers * Number of result to retrieve * @param field * The field used to sort * @param order * ASC or DESC * @return a {@link List} of {@link SUser} * @throws SIdentityException * occurs on persistence layer access problem */ List getActiveUsersInGroup(long groupId, int fromIndex, int numberOfUsers, String field, OrderByType order) throws SIdentityException; /** * Get a {@link List} of Inactive {@link SUser} for given {@link SGroup} from specific interval, sorted by * field attribute in the given * {@link OrderByType} * order order. *

      For instance, getInactiveUsersInGroup(1,0,10,"displayName", OrderByType.DESC) returns the 10 first * {@link SUser} of the {@link SGroup} with id '1' * sorted by * "displayName" in desc order *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. *

      check {@link SUser} to get a list of available field keys to sort * * @param groupId * Identifier of the {@link SGroup} * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfUsers * Number of result to retrieve * @param field * The field used to sort * @param order * ASC or DESC * @return a {@link List} of {@link SUser} * @throws SIdentityException * occurs on persistence layer access problem */ List getInactiveUsersInGroup(long groupId, int fromIndex, int numberOfUsers, String field, OrderByType order) throws SIdentityException; /** * Get a {@link List} of {@link SUser} for given {@link SGroup} from specific interval, sorted by field * attribute in the given {@link OrderByType} * order order. *

      For instance, getUsersInGroup(1,0,10,"displayName", OrderByType.DESC) returns the 10 first {@link SUser} of * the {@link SGroup} with id '1' sorted by * "displayName" in desc order *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. *

      check {@link SUser} to get a list of available field keys to sort * * @param groupId * Identifier of the {@link SGroup} * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfUsers * Number of result to retrieve * @param field * The field used to sort * @param order * ASC or DESC * @return a {@link List} of {@link SUser} * @throws SIdentityException * occurs on persistence layer access problem */ List getUsersInGroup(long groupId, int fromIndex, int numberOfUsers, String field, OrderByType order) throws SIdentityException; /** * Get a {@link List} of {@link SUserMembership} for given group from specific interval *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * * @param groupId * Identifier of the group * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfResults * Number of result to retrieve * @return a {@link List} of {@link SUserMembership} * @throws SIdentityException * occurs on persistence layer access problem */ List getUserMembershipsOfGroup(long groupId, int fromIndex, int numberOfResults) throws SIdentityException; /** * Get a {@link List} of {@link SUserMembership} for given role from specific interval *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * * @param roleId * Identifier of the role * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfResults * Number of result to retrieve * @return a {@link List} of {@link SUserMembership} * @throws SIdentityException * occurs on persistence layer access problem */ List getUserMembershipsOfRole(long roleId, int fromIndex, int numberOfResults) throws SIdentityException; /** * Get {@link SUserMembership} by given id * * @param userMembershipId * The identifier of userMembership * @return the {@link SUserMembership} of the given id * @throws SIdentityException * occurs on persistence layer access problem or if the {@link SUserMembership} does not exists */ SUserMembership getUserMembership(long userMembershipId) throws SIdentityException; /** * Get {@link SUserMembership} of specific {@link SUser}, {@link SGroup} and {@link SRole} * * @param userId * The user's identifier * @param groupId * The group's identifier * @param roleId * The role's identifier * @return the {@link SUserMembership} * @throws SIdentityException * occurs on persistence layer access problem or if the {@link SUserMembership} does not exists */ SUserMembership getUserMembership(long userId, long groupId, long roleId) throws SIdentityException; /** * Get the {@link SUserMembership} of the specific user, group and role without userName, groupName and roleName * being set. * * @param userId * The user's identifier * @param groupId * The group's identifier * @param roleId * The role's identifier * @return the lightened {@link SUserMembership} * @throws SIdentityException * occurs on persistence layer access problem or if the {@link SUserMembership} does not exists */ SUserMembership getLightUserMembership(long userId, long groupId, long roleId) throws SIdentityException; /** * Get a {@link List} of {@link SUserMembership} of the given identifiers if they exists * * @param userMembershipIds * The {@link SUserMembership}'s identifiers * @return a {@link List} of {@link SUserMembership} * @throws SIdentityException * occurs on persistence layer access problem or if none identifiers match an existing * {@link SUserMembership} */ List getUserMemberships(List userMembershipIds) throws SIdentityException; /** * Get a {@link List} of {@link SUserMembership} from specific interval *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfResults * Number of result to retrieve * @return a {@link List} of {@link SUserMembership} * @throws SIdentityException * occurs on persistence layer access problem */ List getUserMemberships(int fromIndex, int numberOfResults) throws SIdentityException; /** * Get a {@link List} of {@link SUserMembership} from specific interval, sorted by field attribute in the * given {@link OrderByType} order * order. *

      For instance, getUserMemberships(0,10,"id", OrderByType.DESC) returns the 10 first {@link SUserMembership} * sorted by * "displayName" in desc order *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. *

      check {@link SUserMembershipBuilderFactory} to check available field keys to sort * * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfUserMemberships * Number of result to retrieve * @param field * The field used to sort * @param order * ASC or DESC * @return a {@link List} of {@link SUserMembership} * @throws SIdentityException * occurs on persistence layer access problem * @see SUserMembershipBuilderFactory */ List getUserMemberships(int fromIndex, int numberOfUserMemberships, OrderByOption orderByOption) throws SIdentityException; /** * Get {@link SCustomUserInfoDefinition} by its id * * @param customUserInfoDefinitionId * The {@link SCustomUserInfoDefinition}'s identifier * @return the customUserInfoDefinition * @throws SIdentityException * occurs on persistence layer access problem */ SCustomUserInfoDefinition getCustomUserInfoDefinition(long customUserInfoDefinitionId) throws SIdentityException; /** * Get {@link SCustomUserInfoValue} by its id * * @param customUserInfoValueId * The identifier of the custom user info value * @return the {@link SCustomUserInfoValue} * @throws SCustomUserInfoValueNotFoundException * if no custom user info value is found for the given id * @throws SCustomUserInfoValueReadException * if an exception occurs while trying to get the custom user info value */ SCustomUserInfoValue getCustomUserInfoValue(long customUserInfoValueId) throws SCustomUserInfoValueNotFoundException, SCustomUserInfoValueReadException; /** * Get a {@link SCustomUserInfoDefinition} by its name * * @param name * The name of the {@link SCustomUserInfoDefinition} to retrieve * @return the {@link SCustomUserInfoDefinition} identified by the given name * @throws SCustomUserInfoDefinitionNotFoundException * if there is no custom user info definition for the given name * @throws SCustomUserInfoDefinitionReadException * if an exception occurs when trying to retrieve the custom user info definition */ SCustomUserInfoDefinition getCustomUserInfoDefinitionByName(String name) throws SCustomUserInfoDefinitionNotFoundException, SCustomUserInfoDefinitionReadException; /** * Verify if there is a {@link SCustomUserInfoDefinition} of the given name * * @param name * The {@link SCustomUserInfoDefinition}'s name to check * @return the {@link SCustomUserInfoDefinition} of the given name * @throws SCustomUserInfoDefinitionNotFoundException * if there is no custom user info definition for the given name * @throws SCustomUserInfoDefinitionReadException * if an exception occurs when trying to retrieve the custom user info definition */ boolean hasCustomUserInfoDefinition(String name) throws SCustomUserInfoDefinitionReadException; /** * Get total number of {@link SCustomUserInfoDefinition} of the current tenant * * @return the total number of {@link SCustomUserInfoDefinition} of the current tenant * @throws SIdentityException * occurs on persistence layer access problem */ long getNumberOfCustomUserInfoDefinition() throws SIdentityException; /** * Get total number of {@link SCustomUserInfoValue} of the current tenant matching the criterias of the given * {@link QueryOptions} * * @param options * The {@link QueryOptions} containing filtering conditions * @return the total number of custom user info value * @throws SIdentityException * occurs on persistence layer access problem */ long getNumberOfCustomUserInfoValue(QueryOptions options) throws SBonitaReadException; /** * Get a {@link List} of {@link SCustomUserInfoDefinition} of the given identifiers * * @param customUserInfoDefinitionIds * A {@link List} of {@link SCustomUserInfoDefinition} identifiers * @return a {@link List} of {@link SCustomUserInfoDefinition} * @throws SIdentityException * occurs on persistence layer access problem or if none identifiers match an existing * {@link SCustomUserInfoDefinition} */ List getCustomUserInfoDefinitions(List customUserInfoDefinitionIds) throws SIdentityException; /** * Get a {@link List} of {@link SCustomUserInfoValue} of the given identifiers * * @param customUserInfoValueIds * A {@link List} of {@link SCustomUserInfoValue} identifiers * @return A {@link List} of {@link SCustomUserInfoValue} * @throws SIdentityException * occurs on persistence layer access problem or if none identifiers match an existing * {@link SCustomUserInfoDefinition} */ List getCustomUserInfoValues(List customUserInfoValueIds) throws SIdentityException; /** * Get a {@link List} of {@link SCustomUserInfoDefinition} from specific interval *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param maxResults * Number of result to retrieve * @return a {@link List} of {@link SCustomUserInfoDefinition} * @throws SIdentityException * occurs on persistence layer access problem */ List getCustomUserInfoDefinitions(int fromIndex, int maxResults) throws SIdentityException; /** * Retrieve a {@link List} of user identifiers from specific interval. The returned user's ids have their associated * user's userInfo of the given * name userInfoName matches the given value userInfoValue partially or not depending on the * usePartialMatch parameter. *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * * @param userInfoName * The user's information name. * @param userInfoValue * The user's information value. * @param usePartialMatch * Defines whether the custom user information value should use a partial match. * @param fromIndex * The index of the first record to be retrieved. First record has index 0. * @param maxResults * The max results to be retrieved. * @return the list identifiers of users matching the given user's information name & value. * @throws SIdentityException * occurs on persistence layer access problem * @since 6.3.2 */ List getUserIdsWithCustomUserInfo(String userInfoName, String userInfoValue, boolean usePartialMatch, int fromIndex, int maxResults) throws SIdentityException; /** * Search {@link SCustomUserInfoValue} matching the criterias of the given {@link QueryOptions} * * @param options * The QueryOptions object containing some query conditions * @return a list of SCustomUserInfoValue objects * @throws SBonitaReadException * occurs on persistence layer access problem or if search parameters are not correct */ List searchCustomUserInfoValue(QueryOptions options) throws SBonitaReadException; /** * Get SCustomUserInfoValues of definitions for a user */ List getCustomUserInfoValueOfUserAndDefinitions(long userId, List definitionsIds) throws SBonitaReadException; /** * Get {@link SUserMembership} for a specific interval for a given user * * @param userId * The user's identifier * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfMemberships * Number of result to retrieve * @return a {@link List} of {@link SUserMembership} * @throws SIdentityException * occurs on persistence layer access problem */ List getUserMembershipsOfUser(long userId, int fromIndex, int numberOfMemberships) throws SIdentityException; /** * Get a {@link List} of {@link SUserMembership} of a given user from specific interval, sorted by field * attribute in the given {@link OrderByType} * sortOrder order. *

      For instance, getUserMembershipsOfUser(1,0,10,"id", OrderByType.DESC) returns the 10 first * {@link SUserMembership} of the given user sorted by * "id" in desc order *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. *

      check {@link SUserMembershipBuilderFactory} to check available field keys to sort * * @param userId * The identifier of user * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfMemberships * Number of result to retrieve * @param field * The field user to do order * @param sortOrder * ASC or DESC * @return a {@link List} of {@link SUserMembership} * @throws SIdentityException * occurs on persistence layer access problem * @see SUserMembershipBuilderFactory */ List getUserMembershipsOfUser(long userId, int fromIndex, int numberOfMemberships, String field, OrderByType sortOrder) throws SIdentityException; /** * Get a {@link List} of {@link SUserMembership} of a given user from specific interval, sorted by default field in * the given {@link OrderByType} * sortOrder order. *

      For instance, getUserMembershipsOfUser(1,0,10, OrderByType.DESC) returns the 10 first {@link SUserMembership} * of the given user sorted by * the default field in desc order *

      If the number of existing results are lower than the number asked, all results from the given index are * retrieved. * * @param userId * The identifier of user * @param fromIndex * Index of the record to be retrieved from. First record has index 0 * @param numberPerPage * Number of result o retrieve * @param sortOrder * OrderByOption object containing order information * @return a list of SUserMembership objects * @throws SIdentityException * occurs on persistence layer access problem */ List getUserMembershipsOfUser(long userId, int fromIndex, int numberPerPage, OrderByOption sortOrder) throws SIdentityException; /** * Get total number of {@link SUserMembership} for a specific user * * @param userId * The user's identifier * @return total number of userMemberships for the specific user * @throws SIdentityException * occurs on persistence layer access problem */ long getNumberOfUserMembershipsOfUser(long userId) throws SIdentityException; /** * Get total number of userMemberships of the current tenant * * @return total number of userMemberships * @throws SIdentityException * occurs on persistence layer access problem */ long getNumberOfUserMemberships() throws SIdentityException; /** * Create {@link SUser} and store it to persistence layer * * @param user * The user to create and store * @return the created user with its identifier set * @throws SUserCreationException * occurs on persistence layer access problem */ SUser createUser(SUser user) throws SUserCreationException; /** * Update {@link SUser} according to the {@link EntityUpdateDescriptor} * * @param user * The {@link SUser} to update * @param descriptor * The {@link SUser} contents to update * @throws SUserUpdateException * occurs on persistence layer access problem */ void updateUser(SUser user, EntityUpdateDescriptor descriptor) throws SUserUpdateException; /** * Update {@link SUser} according to the {@link EntityUpdateDescriptor} * * @param user * The {@link SUser} to update * @param descriptor * The {@link SUser} contents to update * @param isPasswordEncrypted * allow to know if the password given in the {@link EntityUpdateDescriptor} is encrypted or not * @throws SUserUpdateException */ @Deprecated void updateUser(SUser user, EntityUpdateDescriptor descriptor, boolean isPasswordEncrypted) throws SUserUpdateException; /** * Create custom user info definition in DB for a server given custom user info definition * * @param customUserInfo * SCustomUserInfoDefinition object * @throws SCustomUserInfoDefinitionAlreadyExistsException * TODO * @throws SCustomUserInfoDefinitionCreationException * TODO */ SCustomUserInfoDefinition createCustomUserInfoDefinition(SCustomUserInfoDefinition customUserInfo) throws SCustomUserInfoDefinitionAlreadyExistsException, SCustomUserInfoDefinitionCreationException; /** * Update customUserInfoDefinition according to the descriptor * * @param customUserInfo * The customUserInfoDefinition will be updated * @param descriptor * The update description * @throws SIdentityException */ void updateCustomUserInfoDefinition(SCustomUserInfoDefinition customUserInfo, EntityUpdateDescriptor descriptor) throws SIdentityException; /** * Create profileMetadataValue in DB for give profileMetadataValue object * * @param customUserInfo * A profileMetadataValue object * @throws SIdentityException */ SCustomUserInfoValue createCustomUserInfoValue(SCustomUserInfoValue customUserInfo) throws SIdentityException; /** * Update profileMetadataValue according to the descriptor * * @param customUserInfo * The profileMetadataValue will be updated * @param descriptor * The update description * @throws SIdentityException */ void updateCustomUserInfoValue(SCustomUserInfoValue customUserInfo, EntityUpdateDescriptor descriptor) throws SIdentityException; /** * Create role in DB for the given role * * @param role * A role object * @param iconFilename * @param iconContent * @throws SIdentityException */ void createRole(SRole role, String iconFilename, byte[] iconContent) throws SIdentityException; /** * Update role according to the descriptor * * @param role * The role will be updated * @param descriptor * The update description * @param iconUpdater * @throws SIdentityException */ SRole updateRole(SRole role, EntityUpdateDescriptor descriptor, EntityUpdateDescriptor iconUpdater) throws SIdentityException; /** * Create group in DB for the given group object * * @param group * A group object * @param iconFileName * @param iconContent * @throws SGroupCreationException */ void createGroup(SGroup group, String iconFileName, byte[] iconContent) throws SGroupCreationException; /** * Update group according to the descriptor * * @param group * The group will be updated * @param descriptor * The update description * @param iconUpdater * @throws SIdentityException */ void updateGroup(SGroup group, EntityUpdateDescriptor descriptor, EntityUpdateDescriptor iconUpdater) throws SIdentityException; /** * Create userMembership in DB for the given userMembership object * * @param userMembership * A userMembership object * @throws SUserMembershipCreationException */ void createUserMembership(SUserMembership userMembership) throws SUserMembershipCreationException; /** * Update userMembership according to the descriptor * * @param userMembership * The userMembership will be updated * @param descriptor * The update description * @throws SIdentityException */ void updateUserMembership(SUserMembership userMembership, EntityUpdateDescriptor descriptor) throws SIdentityException; /** * Delete the specific user * * @param user * The user will be deleted * @throws SUserDeletionException */ void deleteUser(SUser user) throws SUserDeletionException; /** * Delete user by its id * * @param userId * The identifier of user * @throws SUserDeletionException */ void deleteUser(long userId) throws SUserDeletionException; /** * Delete all users for the connected tenant * * @throws SUserDeletionException * @since 6.1 */ void deleteAllUsers() throws SUserDeletionException; /** * Delete the specific custom user info * * @param metadataDefinition * The custom user info object will be deleted * @throws SIdentityException */ void deleteCustomUserInfoDefinition(SCustomUserInfoDefinition metadataDefinition) throws SIdentityException; /** * Delete the id specified custom user info * * @param customUserInfoDefinitionId * The identifier of custom user info * @throws SIdentityException */ void deleteCustomUserInfoDefinition(long customUserInfoDefinitionId) throws SIdentityException; /** * Delete the specific profileMetadataValue * * @param customUserInfo * The profileMetadataValue object will be deleted * @throws SIdentityException */ void deleteCustomUserInfoValue(SCustomUserInfoValue customUserInfo) throws SIdentityException; /** * Delete the id specified profileMetadataValue * * @param customUserInfoValueId * The identifier of profileMetadataValue * @throws SIdentityException */ void deleteCustomUserInfoValue(long customUserInfoValueId) throws SIdentityException; /** * Delete the specific role * * @param role * The role will be deleted * @throws SRoleDeletionException */ void deleteRole(SRole role) throws SRoleDeletionException; /** * Delete the id specified role * * @param roleId * The role identifier * @throws SRoleNotFoundException * Error occurs when no role found with the specific roleId * @throws SRoleDeletionException */ void deleteRole(long roleId) throws SRoleNotFoundException, SRoleDeletionException; /** * Delete all roles for the connected tenant * * @throws SRoleDeletionException * @since 6.1 */ void deleteAllRoles() throws SRoleDeletionException; /** * Delete the specific group * * @param group * The group will be deleted * @throws SGroupDeletionException */ void deleteGroup(SGroup group) throws SGroupDeletionException; /** * Delete the id specified group * * @param groupId * The identifier of group * @throws SGroupNotFoundException * Error occurs when no group found with the specific groupId * @throws SGroupDeletionException */ void deleteGroup(long groupId) throws SGroupNotFoundException, SGroupDeletionException; /** * Delete all groups for the connected tenant * * @throws SGroupDeletionException * @since 6.1 */ void deleteAllGroups() throws SGroupDeletionException; /** * Delete the specific userMembership * * @param userMembership * The userMembership will be deleted * @throws SMembershipDeletionException */ void deleteUserMembership(SUserMembership userMembership) throws SMembershipDeletionException; /** * Delete the specific light userMembership * * @param userMembership * @throws SMembershipDeletionException * @since 6.1 */ void deleteLightUserMembership(SUserMembership userMembership) throws SMembershipDeletionException; /** * Delete the id specified userMembership * * @param userMembershipId * The identifier of userMembership * @throws SMembershipDeletionException */ void deleteUserMembership(long userMembershipId) throws SMembershipDeletionException; /** * Delete all user memberships for the connected tenant * * @throws SMembershipDeletionException * @since 6.1 */ void deleteAllUserMemberships() throws SMembershipDeletionException; /** * Get total number of users according to specific query options * * @param options * The QueryOptions object containing some query conditions * @return the satisfied user number * @throws SBonitaReadException */ long getNumberOfUsers(QueryOptions options) throws SBonitaReadException; /** * Search users according to specific query options * * @param options * The QueryOptions object containing some query conditions * @return a list of SUser objects * @throws SBonitaReadException */ List searchUsers(QueryOptions options) throws SBonitaReadException; /** * Get total number of roles according to specific query options * * @param options * The QueryOptions object containing some query conditions * @return the satisfied role number * @throws SBonitaReadException */ long getNumberOfRoles(QueryOptions options) throws SBonitaReadException; /** * Search roles according to specific query options * * @param options * The QueryOptions object containing some query conditions * @return a list of SRole objects * @throws SBonitaReadException */ List searchRoles(QueryOptions options) throws SBonitaReadException; /** * Get total number of groups according to specific query options * * @param options * The QueryOptions object containing some query conditions * @return the group number * @throws SBonitaReadException */ long getNumberOfGroups(QueryOptions options) throws SBonitaReadException; /** * Search groups according to specific query options * * @param options * The QueryOptions object containing some query conditions * @return a list of SGroup objects * @throws SBonitaReadException */ List searchGroups(QueryOptions options) throws SBonitaReadException; /** * Get total number of userMemberships contains specific group and role * * @param groupId * The identifier of group * @param roleId * The identifier of role * @return the number of userMemberships * @throws SIdentityException */ long getNumberOfUsersByMembership(long groupId, long roleId) throws SIdentityException; /** * Get light userMembership by its id * * @param userMembershipId * The identifier of userMembership * @return a SUserMembership object without userName, groupName and roleName * @throws SIdentityException */ SUserMembership getLightUserMembership(long userMembershipId) throws SIdentityException; /** * Get light userMembership in a specific interval, this is used for pagination * * @param startIndex * Index of the record to be retrieved from. First record has index 0 * @param numberOfElements * Number of result we want to get. Maximum number of result returned. * @return a list of SUserMembership objects without userName, groupName and roleName * @throws SIdentityException */ List getLightUserMemberships(int startIndex, int numberOfElements) throws SIdentityException; /** * delete children groups of the given group if there is some * * @param groupId * The index of the group to delete * @throws SGroupDeletionException * @throws SGroupNotFoundException */ List deleteChildrenGroup(long groupId) throws SGroupDeletionException, SGroupNotFoundException; /** * Return the user contact info for a specific user. * * @param userId * the ID of the user to retrieve the contact info from * @param isPersonal * Do we want personal contact information (or professional) ? * @return the corresponding SContactInfo, if found * @throws SIdentityException * if a Read problem occurred */ SContactInfo getUserContactInfo(long userId, boolean isPersonal) throws SIdentityException; /** * Create user contact information for given data * * @param contactInfo * The user contact information object * @return The contact info created * @throws SUserCreationException */ SContactInfo createUserContactInfo(SContactInfo contactInfo) throws SUserCreationException; /** * Update user contact information according to the descriptor * * @param contactInfo * The user contact information to be updated * @param descriptor * The update description * @throws SUserUpdateException */ void updateUserContactInfo(SContactInfo contactInfo, EntityUpdateDescriptor descriptor) throws SIdentityException; /** * Create user in DB for given user and * * @param user * The user object * @throws SUserCreationException */ @Deprecated SUser createUserWithoutEncryptingPassword(SUser user) throws SUserCreationException; /** * Update the user having id userId according to the entity update descriptors in parameters * * @param userId * the user to update * @param userUpdateDescriptor * the entity update descriptor of the user itself * @param personalDataUpdateDescriptor * the entity update descriptor for the personal data * @param professionalDataUpdateDescriptor * the entity update descriptor for the professional data * @return the updated user * @throws SIdentityException */ SUser updateUser(long userId, EntityUpdateDescriptor userUpdateDescriptor, EntityUpdateDescriptor personalDataUpdateDescriptor, EntityUpdateDescriptor professionalDataUpdateDescriptor, EntityUpdateDescriptor iconUpdater) throws SIdentityException; /** * create a new user in database along with its contact data * * @param sUser * the user to persist * @param personalContactInfo * its personal contact info * @param proContactInfo * its professional contact info * @param iconFilename * original name of the icon, used to determine the mime type * @param iconContent * content of the icon * @return the user persisted * @throws SUserCreationException */ SUser createUser(SUser sUser, SContactInfo personalContactInfo, SContactInfo proContactInfo, String iconFilename, byte[] iconContent) throws SUserCreationException; } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SCustomUserInfoDefinitionAlreadyExistsException.java ================================================ /** * 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.identity; /** * Identity related exception indicating that the {@link SCustomUserInfoDefinition} already exists * * @author Elias Ricken de Medeiros */ public class SCustomUserInfoDefinitionAlreadyExistsException extends SIdentityException { private static final long serialVersionUID = 1061806810101480038L; private final String definitionName; /** * creates a new instance with a given definition Name that already exists * * @param definitionName the definition name that already exists */ public SCustomUserInfoDefinitionAlreadyExistsException(final String definitionName) { super("A custom user info definition already exists with name '" + definitionName + "'"); this.definitionName = definitionName; } /** * @return the definition name that already exists */ public String getDefinitionName() { return definitionName; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SCustomUserInfoDefinitionCreationException.java ================================================ /** * 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.identity; /** * @author Elias Ricken de Medeiros */ public class SCustomUserInfoDefinitionCreationException extends SIdentityException { private static final long serialVersionUID = -429177983510812485L; private String definitionName; public SCustomUserInfoDefinitionCreationException(String definitionName, Throwable cause) { super("Unable to create cutom user info definition with name '" + definitionName + "'", cause); this.definitionName = definitionName; } public String getDefinitionName() { return definitionName; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SCustomUserInfoDefinitionNotFoundException.java ================================================ /** * 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.identity; /** * @author Elias Ricken de Medeiros */ public class SCustomUserInfoDefinitionNotFoundException extends SIdentityException { private static final long serialVersionUID = 1410274244862871978L; private String definitionName; public SCustomUserInfoDefinitionNotFoundException(String definitionName) { super("No custom user info value found with name '" + definitionName + "'"); this.definitionName = definitionName; } public String getDefinitionName() { return definitionName; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SCustomUserInfoDefinitionReadException.java ================================================ /** * 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.identity; /** * @author Elias Ricken de Medeiros */ public class SCustomUserInfoDefinitionReadException extends SIdentityException { private static final long serialVersionUID = -674750082481752577L; private String definitionName; public SCustomUserInfoDefinitionReadException(final Throwable cause) { super(cause); } public SCustomUserInfoDefinitionReadException(String definitionName, final Throwable cause) { super("Can't get the custom user info definition with name " + definitionName, cause); this.definitionName = definitionName; } public String getDefinitionName() { return definitionName; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SCustomUserInfoValueNotFoundException.java ================================================ /** * 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.identity; /** * @author Elias Ricken de Medeiros */ public class SCustomUserInfoValueNotFoundException extends SIdentityException { private static final long serialVersionUID = 2269304406690155075L; public SCustomUserInfoValueNotFoundException(long id) { super("No custom user info value found with id '" + id + "'"); } public SCustomUserInfoValueNotFoundException(long definitionId, long userId) { super("No custom user info value found with definition id '" + definitionId + "' and user id '" + userId + "'"); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SCustomUserInfoValueReadException.java ================================================ /** * 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.identity; /** * @author Elias Ricken de Medeiros */ public class SCustomUserInfoValueReadException extends SIdentityException { private static final long serialVersionUID = -9066126826360443609L; public SCustomUserInfoValueReadException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SGroupCreationException.java ================================================ /** * 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.identity; /** * @author Matthieu Chaffotte */ public class SGroupCreationException extends SIdentityException { private static final long serialVersionUID = -5737090607568879160L; public SGroupCreationException(final String message) { super(message); } public SGroupCreationException(final Throwable e) { super(e); } public SGroupCreationException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SGroupDeletionException.java ================================================ /** * 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.identity; /** * @author Matthieu Chaffotte */ public class SGroupDeletionException extends SIdentityException { private static final long serialVersionUID = -5737090607568879160L; public SGroupDeletionException(final String message) { super(message); } public SGroupDeletionException(final Throwable e) { super(e); } public SGroupDeletionException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SGroupNotFoundException.java ================================================ /** * 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.identity; /** * @author Matthieu Chaffotte */ public class SGroupNotFoundException extends SIdentityException { private static final long serialVersionUID = -184772534030377163L; public SGroupNotFoundException(final String message) { super(message); } public SGroupNotFoundException(final Throwable cause) { super(cause); } public SGroupNotFoundException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SIcon.java ================================================ /** * 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.identity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; import org.hibernate.annotations.Type; /** * @author Baptiste Mesta */ @Data @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "icon") public class SIcon implements PersistentObject { @Id private long id; @Column(name = "mimetype") private String mimeType; @Type(type = "materialized_blob") @Column(name = "content") private byte[] content; public SIcon(String mimeType, byte[] content) { this.mimeType = mimeType; this.content = content; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SIdentityException.java ================================================ /** * 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.identity; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Exception related to the identity module * * @author Baptiste Mesta */ public class SIdentityException extends SBonitaException { private static final long serialVersionUID = 7615655279956204016L; /** * @see SBonitaException#SBonitaException(Object...) */ public SIdentityException(final Object... parameters) { super(parameters); } /** * @see SBonitaException#SBonitaException(Throwable) */ public SIdentityException(final Throwable cause) { super(cause); } /** * @see SBonitaException#SBonitaException(String, Throwable) */ public SIdentityException(final String message, final Throwable cause) { super(message, cause); } /** * @see SBonitaException#SBonitaException(String) */ public SIdentityException(final String message) { super(message); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SMembershipCreationException.java ================================================ /** * 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.identity; /** * @author Matthieu Chaffotte */ public class SMembershipCreationException extends SIdentityException { private static final long serialVersionUID = -5737090607568879160L; public SMembershipCreationException(final String message) { super(message); } public SMembershipCreationException(final Throwable e) { super(e); } public SMembershipCreationException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SMembershipDeletionException.java ================================================ /** * 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.identity; /** * @author Matthieu Chaffotte */ public class SMembershipDeletionException extends SIdentityException { private static final long serialVersionUID = -5737090607568879160L; public SMembershipDeletionException(final String message) { super(message); } public SMembershipDeletionException(final Throwable e) { super(e); } public SMembershipDeletionException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SRoleDeletionException.java ================================================ /** * 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.identity; /** * @author Matthieu Chaffotte */ public class SRoleDeletionException extends SIdentityException { private static final long serialVersionUID = -2601746060361773240L; public SRoleDeletionException(final String message) { super(message); } public SRoleDeletionException(final Throwable e) { super(e); } public SRoleDeletionException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SRoleNotFoundException.java ================================================ /** * 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.identity; /** * @author Matthieu Chaffotte */ public class SRoleNotFoundException extends SIdentityException { private static final long serialVersionUID = -2601746060361773240L; public SRoleNotFoundException(final String message) { super(message); } public SRoleNotFoundException(final Throwable e) { super(e); } public SRoleNotFoundException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SUserCreationException.java ================================================ /** * 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.identity; /** * @author Matthieu Chaffotte */ public class SUserCreationException extends SIdentityException { private static final long serialVersionUID = -236864152193578968L; public SUserCreationException(final String message) { super(message); } public SUserCreationException(final Throwable e) { super(e); } public SUserCreationException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SUserDeletionException.java ================================================ /** * 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.identity; /** * @author Matthieu Chaffotte */ public class SUserDeletionException extends SIdentityException { private static final long serialVersionUID = -788582886409946761L; public SUserDeletionException(final String message) { super(message); } public SUserDeletionException(final Throwable e) { super(e); } public SUserDeletionException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SUserMembershipCreationException.java ================================================ /** * 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.identity; /** * @author Matthieu Chaffotte */ public class SUserMembershipCreationException extends SIdentityException { private static final long serialVersionUID = 543585701760706593L; public SUserMembershipCreationException(final String message) { super(message); } public SUserMembershipCreationException(final Throwable cause) { super(cause); } public SUserMembershipCreationException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SUserMembershipNotFoundException.java ================================================ /** * 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.identity; /** * @author Matthieu Chaffotte */ public class SUserMembershipNotFoundException extends SIdentityException { private static final long serialVersionUID = -4179049965591664132L; public SUserMembershipNotFoundException(final String message) { super(message); } public SUserMembershipNotFoundException(final Throwable cause) { super(cause); } public SUserMembershipNotFoundException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SUserNotFoundException.java ================================================ /** * 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.identity; import org.bonitasoft.engine.identity.model.SUser; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class SUserNotFoundException extends SIdentityException { private static final long serialVersionUID = -8190385127379005323L; public SUserNotFoundException(final SUser user) { this(user.getUserName()); setUserIdOnContext(user.getId()); } public SUserNotFoundException(final long userId) { super("Can't find the user"); setUserIdOnContext(userId); } public SUserNotFoundException(final String userName) { super("?", userName); } public SUserNotFoundException(final String message, final Throwable cause) { super(message, cause); } public SUserNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/SUserUpdateException.java ================================================ /** * 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.identity; /** * @author Matthieu Chaffotte */ public class SUserUpdateException extends SIdentityException { private static final long serialVersionUID = 618893893652667252L; public SUserUpdateException(final String message) { super(message); } public SUserUpdateException(final Throwable e) { super(e); } public SUserUpdateException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/impl/CredentialsEncrypter.java ================================================ /** * 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.identity.impl; /** * @author Matthieu Chaffotte */ public interface CredentialsEncrypter { String hash(final String password); boolean check(final String password, final String hashPassword); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/impl/IdentityServiceImpl.java ================================================ /** * 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.identity.impl; import java.util.*; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.identity.*; import org.bonitasoft.engine.identity.model.*; import org.bonitasoft.engine.identity.model.builder.*; import org.bonitasoft.engine.identity.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.persistence.*; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.*; import org.bonitasoft.engine.services.QueriableLoggerService; import org.springframework.stereotype.Service; /** * Default implementation of the Identity service * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Bole Zhang * @author Hongwen Zang * @author Celine Souchet */ @Service("identityService") public class IdentityServiceImpl implements IdentityService { private final ReadPersistenceService persistenceService; private final Recorder recorder; private final QueriableLoggerService queriableLoggerService; private final CredentialsEncrypter encrypter; private final IconService iconService; public IdentityServiceImpl(ReadPersistenceService persistenceService, Recorder recorder, QueriableLoggerService queriableLoggerService, CredentialsEncrypter encrypter, IconService iconService) { this.persistenceService = persistenceService; this.recorder = recorder; this.queriableLoggerService = queriableLoggerService; this.encrypter = encrypter; this.iconService = iconService; } @Override public void createGroup(final SGroup group, String iconFileName, byte[] iconContent) throws SGroupCreationException { final String methodName = "createGroup"; final long objectId = group.getId(); final SGroupLogBuilder logBuilder = getGroupLog(ActionType.CREATED, "Adding a new group with name " + group.getName()); try { if (iconFileName != null && iconContent != null) { SIcon icon = iconService.createIcon(iconFileName, iconContent); group.setIconId(icon.getId()); } final InsertRecord insertRecord = new InsertRecord(group); recorder.recordInsert(insertRecord, GROUP); final int status = SQueriableLog.STATUS_OK; log(insertRecord.getEntity().getId(), status, logBuilder, methodName); } catch (final SRecorderException re) { final int status = SQueriableLog.STATUS_FAIL; log(objectId, status, logBuilder, methodName); throw new SGroupCreationException(re); } } @Override public SCustomUserInfoDefinition createCustomUserInfoDefinition(final SCustomUserInfoDefinition customUserInfo) throws SCustomUserInfoDefinitionAlreadyExistsException, SCustomUserInfoDefinitionCreationException { final String methodName = "createCustomUserInfoDefinition"; final SCustomUserInfoDefinitionLogBuilder logBuilder = getSCustomUserInfoDefinitionLog(ActionType.CREATED, "Adding a custom user info with name " + customUserInfo.getName()); try { throwExceptionIfAlreadyExists(customUserInfo); recorder.recordInsert(new InsertRecord(customUserInfo), CUSTOM_USER_INFO_DEFINITION); log(customUserInfo.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); return customUserInfo; } catch (final SRecorderException | SBonitaReadException e) { throw handleCustomUserInfoDefinitionCreationFailure(customUserInfo, methodName, logBuilder, e); } } private void throwExceptionIfAlreadyExists(final SCustomUserInfoDefinition customUserInfo) throws SBonitaReadException, SCustomUserInfoDefinitionAlreadyExistsException { final SCustomUserInfoDefinition storedDef = getCustomUserInfoDefinitionWithoutCheck(customUserInfo.getName()); if (storedDef != null) { throw new SCustomUserInfoDefinitionAlreadyExistsException(customUserInfo.getName()); } } private SCustomUserInfoDefinitionCreationException handleCustomUserInfoDefinitionCreationFailure( final SCustomUserInfoDefinition customUserInfo, final String methodName, final SCustomUserInfoDefinitionLogBuilder logBuilder, final SBonitaException exception) { log(customUserInfo.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); return new SCustomUserInfoDefinitionCreationException(customUserInfo.getName(), exception); } @Override public SCustomUserInfoValue createCustomUserInfoValue(final SCustomUserInfoValue customUserInfo) throws SIdentityException { try { recorder.recordInsert(new InsertRecord(customUserInfo), CUSTOM_USER_INFO_VALUE); return customUserInfo; } catch (final SRecorderException e) { throw new SIdentityException("Can't add custom user info value " + customUserInfo, e); } } @Override public void createRole(final SRole role, String iconFilename, byte[] iconContent) throws SIdentityException { final String methodName = "createRole"; final SRoleLogBuilder logBuilder = getRoleLog(ActionType.CREATED, "Adding a new role with name " + role.getName()); try { if (iconFilename != null && iconContent != null) { SIcon icon = iconService.createIcon(iconFilename, iconContent); role.setIconId(icon.getId()); } recorder.recordInsert(new InsertRecord(role), ROLE); log(role.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } catch (final SRecorderException e) { log(role.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SIdentityException("Can't add role " + role, e); } } @Override public SUser createUser(final SUser user) throws SUserCreationException { final String methodName = "createUser"; final String hash = encrypter.hash(user.getPassword()); final SUser hashedUser = new SUser(user).toBuilder().password(hash).build(); return createUser(user, methodName, hashedUser); } @Override @Deprecated public SUser createUserWithoutEncryptingPassword(final SUser user) throws SUserCreationException { final String methodName = "createUserWithoutEncryptingPassword"; final SUser hashedUser = new SUser(user).toBuilder().build(); return createUser(user, methodName, hashedUser); } private SUser createUser(final SUser user, final String methodName, final SUser hashedUser) throws SUserCreationException { final String message = "Adding a new user with user name " + user.getUserName() + ", first name " + user.getFirstName() + ", last name " + user.getLastName(); final SUserLogBuilder logBuilder = getUserLog(ActionType.CREATED, message); try { insertUser(hashedUser); insertUserLogin(methodName, hashedUser, logBuilder); return hashedUser; } catch (final SRecorderException re) { log(hashedUser.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SUserCreationException(re); } } private void insertUserLogin(String methodName, SUser hashedUser, SUserLogBuilder logBuilder) throws SRecorderException { SUserLogin sUserLogin = new SUserLogin(); hashedUser.setSUserLogin(sUserLogin); sUserLogin.setSUser(hashedUser); sUserLogin.setId(hashedUser.getId()); recorder.recordInsert(new InsertRecord(sUserLogin), USER_LOGIN); log(hashedUser.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } private void insertUser(SUser hashedUser) throws SRecorderException { final InsertRecord insertRecord = new InsertRecord(hashedUser); recorder.recordInsert(insertRecord, USER); } @Override public SContactInfo createUserContactInfo(final SContactInfo contactInfo) throws SUserCreationException { final String methodName = "createUserContactInfo"; final String message = "Adding a new user contact information for user with id " + contactInfo.getUserId(); final SContactInfoLogBuilder logBuilder = getUserContactInfoLog(ActionType.CREATED, message, contactInfo); try { recorder.recordInsert(new InsertRecord(contactInfo), USER_CONTACT_INFO); log(contactInfo.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); return contactInfo; } catch (final SRecorderException re) { log(contactInfo.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SUserCreationException(re); } } @Override public void createUserMembership(final SUserMembership userMembership) throws SUserMembershipCreationException { final String methodName = "createUserMembership"; final String message = "Adding a new user membership for user " + userMembership.getUsername() + " with role " + userMembership.getRoleName() + " in group " + userMembership.getGroupName(); final SUserMembershipLogBuilder logBuilder = getUserMembershipLog(ActionType.CREATED, message, userMembership); try { recorder.recordInsert(new InsertRecord(userMembership), USERMEMBERSHIP); log(userMembership.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } catch (final SRecorderException re) { log(userMembership.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SUserMembershipCreationException(re); } } @Override public void deleteGroup(final long groupId) throws SGroupNotFoundException, SGroupDeletionException { final SGroup group = getGroup(groupId); this.deleteGroup(group); } @Override public void deleteGroup(final SGroup group) throws SGroupDeletionException { final SGroupLogBuilder logBuilder = getGroupLog(ActionType.DELETED, "Deleting group " + group.getName()); try { if (group.getIconId() != null) { iconService.deleteIcon(group.getIconId()); } recorder.recordDelete(new DeleteRecord(group), GROUP); log(group.getId(), SQueriableLog.STATUS_OK, logBuilder, "deleteGroup"); } catch (final SRecorderException | SBonitaReadException re) { log(group.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "deleteGroup"); throw new SGroupDeletionException(re); } } @Override public void deleteAllGroups() throws SGroupDeletionException { try { final DeleteAllRecord record = new DeleteAllRecord(SGroup.class, null); recorder.recordDeleteAll(record); } catch (final SRecorderException e) { throw new SGroupDeletionException("Can't delete all groups.", e); } } @Override public List deleteChildrenGroup(final long groupId) throws SGroupDeletionException, SGroupNotFoundException { final ArrayList deletedGroups = new ArrayList<>(); try { List childrenGroup; final int nbGroup = 20; while (!(childrenGroup = getGroupChildren(groupId, 0, nbGroup)).isEmpty()) { for (final SGroup sGroup : childrenGroup) { deletedGroups.addAll(deleteChildrenGroup(sGroup.getId())); deletedGroups.add(sGroup.getId()); deleteGroup(sGroup); } } } catch (final SGroupNotFoundException e) { throw e; } catch (final SIdentityException e) { throw new SGroupDeletionException(e); } return deletedGroups; } private void initializeLogBuilder(final T logBuilder, final String message) { logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message); } private void updateLog(final ActionType actionType, final T logBuilder) { logBuilder.setActionType(actionType); } private SGroupLogBuilder getGroupLog(final ActionType actionType, final String message) { final SGroupLogBuilder logBuilder = BuilderFactory.get(SGroupLogBuilderFactory.class).createNewInstance(); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); return logBuilder; } private SRoleLogBuilder getRoleLog(final ActionType actionType, final String message) { final SRoleLogBuilder logBuilder = BuilderFactory.get(SRoleLogBuilderFactory.class).createNewInstance(); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); return logBuilder; } private SCustomUserInfoDefinitionLogBuilder getSCustomUserInfoDefinitionLog(final ActionType actionType, final String message) { final SCustomUserInfoDefinitionLogBuilder logBuilder = BuilderFactory .get(SCustomUserInfoDefinitionLogBuilderFactory.class).createNewInstance(); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); return logBuilder; } SUserLogBuilder getUserLog(final ActionType actionType, final String message) { final SUserLogBuilder logBuilder = BuilderFactory.get(SUserLogBuilderFactory.class).createNewInstance(); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); return logBuilder; } private SContactInfoLogBuilder getUserContactInfoLog(final ActionType actionType, final String message, final SContactInfo contactInfo) { final SContactInfoLogBuilder logBuilder = BuilderFactory.get(SContactInfoLogBuilderFactory.class) .createNewInstance(); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); logBuilder.setContactInfoUserId(contactInfo.getUserId()); return logBuilder; } private SUserMembershipLogBuilder getUserMembershipLog(final ActionType actionType, final String message, final SUserMembership userMemberShip) { final SUserMembershipLogBuilder logBuilder = BuilderFactory.get(SUserMembershipLogBuilderFactory.class) .createNewInstance(); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); logBuilder.identityUserId(userMemberShip.getUserId()); logBuilder.roleID(userMemberShip.getRoleId()); logBuilder.groupId(userMemberShip.getGroupId()); return logBuilder; } @Override public void deleteCustomUserInfoDefinition(final long customUserInfoDefinitionId) throws SIdentityException { this.deleteCustomUserInfoDefinition(getCustomUserInfoDefinition(customUserInfoDefinitionId)); } @Override public void deleteCustomUserInfoDefinition(final SCustomUserInfoDefinition info) throws SIdentityException { final String methodName = "deleteCustomUserInfoDefinition"; final SCustomUserInfoDefinitionLogBuilder logBuilder = getSCustomUserInfoDefinitionLog(ActionType.DELETED, "Deleting profile custom user info definition with name " + info.getName()); try { recorder.recordDelete(new DeleteRecord(info), CUSTOM_USER_INFO_DEFINITION); log(info.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } catch (final SRecorderException e) { log(info.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SIdentityException("Can't delete profile custom user info definition " + info, e); } } @Override public void deleteCustomUserInfoValue(final long customUserInfoValueId) throws SIdentityException { this.deleteCustomUserInfoValue(getCustomUserInfoValue(customUserInfoValueId)); } @Override public void deleteCustomUserInfoValue(final SCustomUserInfoValue customUserInfo) throws SIdentityException { try { recorder.recordDelete(new DeleteRecord(customUserInfo), CUSTOM_USER_INFO_VALUE); } catch (final SRecorderException e) { throw new SIdentityException("Can't delete custom user info value" + customUserInfo, e); } } @Override public void deleteRole(final long roleId) throws SRoleNotFoundException, SRoleDeletionException { final SRole role = getRole(roleId); this.deleteRole(role); } @Override public void deleteRole(final SRole role) throws SRoleDeletionException { final String methodName = "deleteRole"; final SRoleLogBuilder logBuilder = getRoleLog(ActionType.DELETED, "Deleting role with name " + role.getName()); try { if (role.getIconId() != null) { iconService.deleteIcon(role.getIconId()); } recorder.recordDelete(new DeleteRecord(role), ROLE); log(role.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } catch (final SRecorderException | SBonitaReadException re) { log(role.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SRoleDeletionException(re); } } @Override public void deleteAllRoles() throws SRoleDeletionException { try { final DeleteAllRecord record = new DeleteAllRecord(SRole.class, null); recorder.recordDeleteAll(record); } catch (final SRecorderException e) { throw new SRoleDeletionException("Can't delete all roles.", e); } } @Override public void deleteUser(final long userId) throws SUserDeletionException { SUser user; try { user = getUser(userId); if (user.getIconId() != null) { iconService.deleteIcon(user.getIconId()); } deleteUser(user); } catch (final SUserNotFoundException e) { // ignored, let's switch to the next one } catch (SRecorderException | SBonitaReadException e) { throw new SUserDeletionException(e); } } @Override public void deleteUser(final SUser user) throws SUserDeletionException { final String methodName = "deleteUser"; final SUserLogBuilder logBuilder = getUserLog(ActionType.DELETED, "Deleting user with username " + user.getUserName()); try { recorder.recordDelete(new DeleteRecord(user), USER); log(user.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } catch (final SRecorderException re) { log(user.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SUserDeletionException(re); } } @Override public void deleteAllUsers() throws SUserDeletionException { try { final DeleteAllRecord record = new DeleteAllRecord(SUser.class, null); recorder.recordDeleteAll(record); final DeleteAllRecord record2 = new DeleteAllRecord(SUserLogin.class, null); recorder.recordDeleteAll(record2); } catch (final SRecorderException e) { throw new SUserDeletionException("Can't delete all users.", e); } } @Override public SUserMembership getLightUserMembership(final long userMembershipId) throws SIdentityException { try { final SUserMembership selectOne = persistenceService .selectById(SelectDescriptorBuilder.getLightElementById(SUserMembership.class, "SUserMembership", userMembershipId)); if (selectOne == null) { throw new SIdentityException("Can't get the userMembership with id " + userMembershipId, null); } return selectOne; } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the user membership with id " + userMembershipId, e); } } @Override public void deleteUserMembership(SUserMembership userMembership) throws SMembershipDeletionException { try { // fat object, hibernate won't delete id if (userMembership.getGroupName() != null || userMembership.getUsername() != null || userMembership.getRoleName() != null) { userMembership = getLightUserMembership(userMembership.getId()); } deleteLightUserMembership(userMembership); } catch (final SIdentityException e) { throw new SMembershipDeletionException("Can't delete membership " + userMembership, e); } } @Override public void deleteUserMembership(final long id) throws SMembershipDeletionException { try { final SUserMembership userMembership = getLightUserMembership(id); deleteLightUserMembership(userMembership); } catch (final SIdentityException e) { throw new SMembershipDeletionException("Can't delete membership with id " + id, e); } } @Override public void deleteLightUserMembership(final SUserMembership userMembership) throws SMembershipDeletionException { final String methodName = "deleteLightUserMembership"; final String message = "Deleting user membership for user " + userMembership.getUsername() + " with role " + userMembership.getRoleName() + " in group " + userMembership.getGroupName(); final SUserMembershipLogBuilder logBuilder = getUserMembershipLog(ActionType.DELETED, message, userMembership); try { recorder.recordDelete(new DeleteRecord(userMembership), USERMEMBERSHIP); log(userMembership.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } catch (final SRecorderException e) { log(userMembership.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SMembershipDeletionException("Can't delete membership " + userMembership, e); } } @Override public void deleteAllUserMemberships() throws SMembershipDeletionException { try { final DeleteAllRecord record = new DeleteAllRecord(SUserMembership.class, null); recorder.recordDeleteAll(record); } catch (final SRecorderException e) { throw new SMembershipDeletionException("Can't delete all user memberships.", e); } } @Override public SGroup getGroup(final long groupId) throws SGroupNotFoundException { try { final SGroup group = persistenceService .selectById(SelectDescriptorBuilder.getElementById(SGroup.class, "Group", groupId)); if (group == null) { throw new SGroupNotFoundException("No group exists with id: " + groupId); } return group; } catch (final SBonitaReadException bre) { throw new SGroupNotFoundException(bre); } } @Override public List getGroupChildren(final long groupId, final int fromIndex, final int numberOfGroups) throws SIdentityException { try { final SGroup group = getGroup(groupId); return persistenceService .selectList(SelectDescriptorBuilder.getChildrenOfGroup(group, fromIndex, numberOfGroups)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the children of the group", e); } } @Override public List getGroupChildren(final long groupId, final int fromIndex, final int numberOfGroups, final String field, final OrderByType order) throws SIdentityException { try { final SGroup group = getGroup(groupId); return persistenceService .selectList(SelectDescriptorBuilder.getChildrenOfGroup(group, field, order, fromIndex, numberOfGroups)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the children of the group", e); } } @Override public List getGroups(final int fromIndex, final int numberOfGroups) throws SIdentityException { try { return persistenceService .selectList(SelectDescriptorBuilder.getElements(SGroup.class, "Group", fromIndex, numberOfGroups)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the groups", e); } } @Override public List getGroups(final int fromIndex, final int numberOfGroups, final String field, final OrderByType order) throws SIdentityException { try { return persistenceService .selectList(SelectDescriptorBuilder.getElements(SGroup.class, "Group", field, order, fromIndex, numberOfGroups)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the groups", e); } } @Override public List getGroups(final List groupIds) throws SGroupNotFoundException { if (groupIds == null || groupIds.isEmpty()) { return Collections.emptyList(); } try { return persistenceService .selectList(SelectDescriptorBuilder.getElementsByIds(SGroup.class, "Group", groupIds)); } catch (final SBonitaReadException e) { throw new SGroupNotFoundException(e); } } @Override public List getUserMemberships(final int fromIndex, final int numberOfResult, final OrderByOption orderByOption) throws SIdentityException { try { List listSUserMembership; if (orderByOption.getClazz() == SRole.class) { listSUserMembership = persistenceService .selectList(SelectDescriptorBuilder.getUserMembershipsWithRole(new QueryOptions(fromIndex, numberOfResult, Collections.singletonList(orderByOption)))); } else if (orderByOption.getClazz() == SGroup.class) { listSUserMembership = persistenceService .selectList(SelectDescriptorBuilder.getUserMembershipsWithGroup(new QueryOptions(fromIndex, numberOfResult, Collections.singletonList(orderByOption)))); } else { listSUserMembership = persistenceService .selectList(SelectDescriptorBuilder.getElements(SUserMembership.class, "UserMembership", new QueryOptions(fromIndex, numberOfResult, Collections.singletonList(orderByOption)))); } return listSUserMembership; } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the memberships", e); } } @Override public List getUserMembershipsOfGroup(final long groupId, final int startIndex, final int maxResults) throws SIdentityException { try { return persistenceService.selectList(SelectDescriptorBuilder.getUserMembershipsByGroup(groupId, startIndex, maxResults)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the users memberships the group " + groupId, e); } } @Override public List getUserMembershipsOfRole(final long roleId, final int startIndex, final int maxResults) throws SIdentityException { try { return persistenceService.selectList(SelectDescriptorBuilder.getUserMembershipsByRole(roleId, startIndex, maxResults)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the memberships having the role " + roleId, e); } } @Override public long getNumberOfGroupChildren(final long parentGroupId) throws SIdentityException { try { final SGroup parentGroup = getGroup(parentGroupId); return persistenceService .selectOne(SelectDescriptorBuilder.getNumberOfGroupChildren(parentGroup.getPath())); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the number children of group", e); } } @Override public long getNumberOfGroups() throws SIdentityException { try { return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfElement("SGroup", SGroup.class)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the number of group", e); } } @Override public long getNumberOfCustomUserInfoDefinition() throws SIdentityException { try { return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfElement("CustomUserInfoDefinition", SCustomUserInfoDefinition.class)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the number of custom user info", e); } } @Override public long getNumberOfCustomUserInfoValue(final QueryOptions options) throws SBonitaReadException { try { return persistenceService.getNumberOfEntities(SCustomUserInfoValue.class, options, null); } catch (final SBonitaReadException e) { throw new SBonitaReadException(e); } } @Override public long getNumberOfRoles() throws SIdentityException { try { return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfElement("SRole", SRole.class)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the number of role", e); } } @Override public long getNumberOfUsers() throws SIdentityException { try { return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfElement("SUser", SUser.class)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the number of user", e); } } @Override public long getNumberOfUserMembershipsOfUser(final long userId) throws SIdentityException { try { return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUserMembershipsOfUser(userId)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the number of usermemberships having the user " + userId, e); } } @Override public long getNumberOfUsersByGroup(final long groupId) throws SIdentityException { try { return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByGroup(groupId)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the number of users having the group " + groupId, e); } } @Override public long getNumberOfUsersByRole(final long roleId) throws SIdentityException { try { return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByRole(roleId)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the number of users having the role " + roleId, e); } } @Override public long getNumberOfUsersByMembership(final long groupId, final long roleId) throws SIdentityException { try { return persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByMembership(groupId, roleId)); } catch (final SBonitaReadException e) { throw new SIdentityException( "Can't get the number of users having the membership with group:" + groupId + " and role:" + roleId, e); } } @Override public SCustomUserInfoDefinition getCustomUserInfoDefinitionByName(final String name) throws SCustomUserInfoDefinitionNotFoundException, SCustomUserInfoDefinitionReadException { SCustomUserInfoDefinition definition; try { definition = getCustomUserInfoDefinitionWithoutCheck(name); } catch (final SBonitaReadException e) { throw new SCustomUserInfoDefinitionReadException(name, e); } if (definition == null) { throw new SCustomUserInfoDefinitionNotFoundException(name); } return definition; } @Override public boolean hasCustomUserInfoDefinition(final String name) throws SCustomUserInfoDefinitionReadException { SCustomUserInfoDefinition definition; try { definition = getCustomUserInfoDefinitionWithoutCheck(name); } catch (final SBonitaReadException e) { throw new SCustomUserInfoDefinitionReadException(name, e); } return definition != null; } private SCustomUserInfoDefinition getCustomUserInfoDefinitionWithoutCheck(final String name) throws SBonitaReadException { return persistenceService.selectOne(SelectDescriptorBuilder.getCustomUserInfoDefinitionByName(name)); } @Override public List getCustomUserInfoDefinitions(final int fromIndex, final int maxResults) throws SIdentityException { try { return persistenceService.selectList(SelectDescriptorBuilder.getElements( SCustomUserInfoDefinition.class, "CustomUserInfoDefinition", fromIndex, maxResults)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the custom user info definitions", e); } } @Override public List getUserIdsWithCustomUserInfo(final String userInfoName, String userInfoValue, final boolean usePartialMatch, final int fromIndex, final int maxResults) throws SIdentityException { try { String queryName; if (usePartialMatch) { queryName = "getUserIdsWithCustomUserInfoContains"; userInfoValue = "%" + userInfoValue + "%"; } else { queryName = "getUserIdsWithCustomUserInfo"; } final Map parameters = new HashMap<>(2); parameters.put("userInfoName", userInfoName); parameters.put("userInfoValue", userInfoValue); final SelectListDescriptor descriptor = new SelectListDescriptor<>(queryName, parameters, SUser.class, Long.class, new QueryOptions(fromIndex, maxResults)); return persistenceService.selectList(descriptor); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the custom user info definitions", e); } } @Override public SCustomUserInfoDefinition getCustomUserInfoDefinition(final long customUserInfoDefinitionId) throws SIdentityException { try { final SCustomUserInfoDefinition selectOne = persistenceService .selectById(SelectDescriptorBuilder.getElementById(SCustomUserInfoDefinition.class, "CustomUserInfoDefinition", customUserInfoDefinitionId)); if (selectOne == null) { throw new SIdentityException( "Can't get the custom user info definition with id " + customUserInfoDefinitionId, null); } return selectOne; } catch (final SBonitaReadException e) { throw new SIdentityException( "Can't get the custom user info definition with id " + customUserInfoDefinitionId, e); } } @Override public List getCustomUserInfoDefinitions(final List customUserInfoDefinitionIds) throws SIdentityException { if (customUserInfoDefinitionIds == null || customUserInfoDefinitionIds.isEmpty()) { return Collections.emptyList(); } try { return persistenceService.selectList(SelectDescriptorBuilder.getElementsByIds( SCustomUserInfoDefinition.class, "SCustomUserInfoDefinition", customUserInfoDefinitionIds)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get custom user info definitions with ids " + Arrays.toString(customUserInfoDefinitionIds.toArray()), e); } } @Override public SCustomUserInfoValue getCustomUserInfoValue(final long customUserInfoValueId) throws SCustomUserInfoValueNotFoundException, SCustomUserInfoValueReadException { try { final SCustomUserInfoValue selectOne = persistenceService .selectById(SelectDescriptorBuilder.getElementById(SCustomUserInfoValue.class, "SCustomUserInfoValue", customUserInfoValueId)); if (selectOne == null) { throw new SCustomUserInfoValueNotFoundException(customUserInfoValueId); } return selectOne; } catch (final SBonitaReadException e) { throw new SCustomUserInfoValueReadException(e); } } @Override public List getCustomUserInfoValues(final List customUserInfoValueIds) throws SIdentityException { if (customUserInfoValueIds == null || customUserInfoValueIds.isEmpty()) { return Collections.emptyList(); } try { return persistenceService.selectList( SelectDescriptorBuilder.getElementsByIds(SCustomUserInfoValue.class, "SCustomUserInfoValue", customUserInfoValueIds)); } catch (final SBonitaReadException e) { throw new SIdentityException( "Can't get custom user info values with ids " + Arrays.toString(customUserInfoValueIds.toArray()), e); } } @Override public SRole getRole(final long roleId) throws SRoleNotFoundException { try { final SRole selectOne = persistenceService .selectById(SelectDescriptorBuilder.getElementById(SRole.class, "Role", roleId)); if (selectOne == null) { throw new SRoleNotFoundException("The role with id= " + roleId + " does not exist"); } return selectOne; } catch (final SBonitaReadException e) { throw new SRoleNotFoundException(e); } } @Override public SRole getRoleByName(final String roleName) throws SRoleNotFoundException { try { final SRole role = persistenceService.selectOne(SelectDescriptorBuilder.getRoleByName(roleName)); if (role == null) { throw new SRoleNotFoundException("The role named " + roleName + " does not exist"); } return role; } catch (final SBonitaReadException e) { throw new SRoleNotFoundException(e); } } @Override public SGroup getGroupByPath(final String groupPath) throws SGroupNotFoundException { SelectOneDescriptor descriptor; final int lastIndexOf = groupPath.lastIndexOf('/'); if (lastIndexOf > 0) { final String groupName = groupPath.substring(lastIndexOf + 1); final String parentPath = groupPath.substring(0, lastIndexOf); descriptor = SelectDescriptorBuilder.getGroupByPath(parentPath, groupName); } else if (lastIndexOf == 0) { final String groupName = groupPath.substring(lastIndexOf + 1); descriptor = SelectDescriptorBuilder.getGroupByName(groupName); } else { descriptor = SelectDescriptorBuilder.getGroupByName(groupPath); } try { final SGroup group = persistenceService.selectOne(descriptor); if (group == null) { throw new SGroupNotFoundException("The group '" + groupPath + "' does not exist"); } return group; } catch (final SBonitaReadException bre) { throw new SGroupNotFoundException(bre); } } @Override public List getRoles(final int fromIndex, final int numberOfRoles) throws SIdentityException { try { return persistenceService .selectList(SelectDescriptorBuilder.getElements(SRole.class, "Role", fromIndex, numberOfRoles)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the roles", e); } } @Override public List getRoles(final int fromIndex, final int numberOfRoles, final String field, final OrderByType order) throws SIdentityException { try { return persistenceService .selectList(SelectDescriptorBuilder.getElements(SRole.class, "Role", field, order, fromIndex, numberOfRoles)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the roles", e); } } @Override public List getRoles(final List roleIds) throws SRoleNotFoundException { if (roleIds == null || roleIds.isEmpty()) { return Collections.emptyList(); } try { return persistenceService .selectList(SelectDescriptorBuilder.getElementsByIds(SRole.class, "Role", roleIds)); } catch (final SBonitaReadException e) { throw new SRoleNotFoundException(e); } } @Override public SUser getUser(final long userId) throws SUserNotFoundException { try { final SUser user = persistenceService .selectById(SelectDescriptorBuilder.getElementById(SUser.class, "User", userId)); if (user == null) { throw new SUserNotFoundException(userId); } return user; } catch (final SBonitaReadException e) { throw new SUserNotFoundException("Cannot get user with id: " + userId, e); } } @Override public SContactInfo getUserContactInfo(final long userId, final boolean isPersonal) throws SIdentityException { try { return persistenceService.selectOne(SelectDescriptorBuilder.getUserContactInfo(userId, isPersonal)); } catch (final SBonitaReadException e) { throw new SUserNotFoundException("Cannot get user contact info for user id: " + userId, e); } } @Override public SUser getUserByUserName(final String userName) throws SUserNotFoundException { try { final SUser user = persistenceService.selectOne(SelectDescriptorBuilder.getUserByUserName(userName)); if (user == null) { throw new SUserNotFoundException(userName); } return user; } catch (final SBonitaReadException e) { throw new SUserNotFoundException("Cannot get user: " + userName, e); } } @Override public SUserMembership getUserMembership(final long userMembershipId) throws SIdentityException { try { final Map parameters = Collections.singletonMap("id", (Object) userMembershipId); final SelectOneDescriptor desc = new SelectOneDescriptor<>("getSUserMembershipById", parameters, SUserMembership.class); final SUserMembership selectOne = persistenceService.selectOne(desc); if (selectOne == null) { throw new SIdentityException("Can't get the userMembership with id " + userMembershipId, null); } return selectOne; } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the user membership with id " + userMembershipId, e); } } @Override public List getUserMemberships(final List userMembershipIds) throws SIdentityException { List localUserMembershipIds = userMembershipIds; if (localUserMembershipIds == null || localUserMembershipIds.isEmpty()) { localUserMembershipIds = Collections.emptyList(); } try { return persistenceService.selectList(SelectDescriptorBuilder.getElementsByIds(SUserMembership.class, "SUserMembership", localUserMembershipIds)); } catch (final SBonitaReadException e) { throw new SIdentityException( "Can't get users memberships with ids " + Arrays.toString(localUserMembershipIds.toArray()), e); } } @Override public List getUsers(final int fromIndex, final int numberOfUsers) throws SIdentityException { try { return persistenceService .selectList(SelectDescriptorBuilder.getElements(SUser.class, "User", fromIndex, numberOfUsers)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the users", e); } } @Override public List getUsers(final int fromIndex, final int numberOfUsers, final String field, final OrderByType order) throws SIdentityException { try { return persistenceService .selectList(SelectDescriptorBuilder.getElements(SUser.class, "User", field, order, fromIndex, numberOfUsers)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the users", e); } } @Override public List getUsers(final List userIds) throws SUserNotFoundException { if (userIds == null || userIds.isEmpty()) { return Collections.emptyList(); } try { return persistenceService .selectList(SelectDescriptorBuilder.getElementsByIds(SUser.class, "User", userIds)); } catch (final SBonitaReadException e) { throw new SUserNotFoundException(e); } } @Override public List getUsersByUsername(final List userNames) throws SIdentityException { if (userNames == null || userNames.isEmpty()) { return Collections.emptyList(); } try { final QueryOptions queryOptions = new QueryOptions(0, userNames.size(), SUser.class, "userName", OrderByType.ASC); final Map parameters = Collections.singletonMap("userNames", (Object) userNames); return persistenceService.selectList( new SelectListDescriptor("getUsersByName", parameters, SUser.class, queryOptions)); } catch (final SBonitaReadException e) { throw new SUserNotFoundException(e); } } @Override public List getUsersInGroup(final long groupId, final int fromIndex, final int numberOfUsers, final String field, final OrderByType order) throws SIdentityException { try { if (order != null) { return persistenceService .selectList(SelectDescriptorBuilder.getUsersByGroup(groupId, field, order, fromIndex, numberOfUsers)); } else { return persistenceService .selectList(SelectDescriptorBuilder.getUsersByGroup(groupId, fromIndex, numberOfUsers)); } } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the users having the group " + groupId, e); } } @Override public List getActiveUsersInGroup(final long groupId, final int fromIndex, final int numberOfUsers, final String field, final OrderByType order) throws SIdentityException { try { if (order != null) { return persistenceService .selectList(SelectDescriptorBuilder.getActiveUsersByGroup(groupId, field, order, fromIndex, numberOfUsers)); } else { return persistenceService .selectList(SelectDescriptorBuilder.getActiveUsersByGroup(groupId, fromIndex, numberOfUsers)); } } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the active users having the group " + groupId, e); } } @Override public List getInactiveUsersInGroup(final long groupId, final int fromIndex, final int numberOfUsers, final String field, final OrderByType order) throws SIdentityException { try { if (order != null) { return persistenceService .selectList(SelectDescriptorBuilder.getInactiveUsersByGroup(groupId, field, order, fromIndex, numberOfUsers)); } else { return persistenceService .selectList(SelectDescriptorBuilder.getInactiveUsersByGroup(groupId, fromIndex, numberOfUsers)); } } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the inactive users having the group " + groupId, e); } } @Override public List getUsersWithManager(long managerId, int fromIndex, int numberMaxOfUsers, String field, OrderByType order) throws SIdentityException { try { final QueryOptions queryOptions; if (order != null) { queryOptions = new QueryOptions(fromIndex, numberMaxOfUsers, SUser.class, field, order); } else { queryOptions = new QueryOptions(fromIndex, numberMaxOfUsers, SUser.class, "id", OrderByType.DESC); } return persistenceService.selectList(SelectDescriptorBuilder.getUsersWithManager(managerId, queryOptions)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the users having the manager " + managerId, e); } } @Override public List getActiveUsersWithManager(long managerId, int fromIndex, int numberMaxOfUsers, String field, OrderByType order) throws SIdentityException { try { final QueryOptions queryOptions; if (order != null) { queryOptions = new QueryOptions(fromIndex, numberMaxOfUsers, SUser.class, field, order); } else { queryOptions = new QueryOptions(fromIndex, numberMaxOfUsers, SUser.class, "id", OrderByType.DESC); } return persistenceService .selectList(SelectDescriptorBuilder.getActiveUsersWithManager(managerId, queryOptions)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the active users having the manager " + managerId, e); } } @Override public List getInactiveUsersWithManager(long managerId, int fromIndex, int numberMaxOfUsers, String field, OrderByType order) throws SIdentityException { try { final QueryOptions queryOptions; if (order != null) { queryOptions = new QueryOptions(fromIndex, numberMaxOfUsers, SUser.class, field, order); } else { queryOptions = new QueryOptions(fromIndex, numberMaxOfUsers, SUser.class, "id", OrderByType.DESC); } return persistenceService .selectList(SelectDescriptorBuilder.getInactiveUsersWithManager(managerId, queryOptions)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the inactive users having the manager " + managerId, e); } } @Override public List getUsersWithRole(final long roleId, final int fromIndex, final int numberOfUsers) throws SIdentityException { try { return persistenceService .selectList(SelectDescriptorBuilder.getUsersWithRole(roleId, fromIndex, numberOfUsers)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the users having the role " + roleId, e); } } @Override public List getActiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers) throws SIdentityException { try { return persistenceService .selectList(SelectDescriptorBuilder.getActiveUsersWithRole(roleId, fromIndex, numberOfUsers)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the active users having the role " + roleId, e); } } @Override public List getInactiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers) throws SIdentityException { try { return persistenceService .selectList(SelectDescriptorBuilder.getInactiveUsersWithRole(roleId, fromIndex, numberOfUsers)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the inactive users having the role " + roleId, e); } } @Override public List getUsersWithRole(final long roleId, final int fromIndex, final int numberOfUsers, final String field, final OrderByType order) throws SIdentityException { try { if (order != null) { return persistenceService .selectList(SelectDescriptorBuilder.getUsersWithRole(roleId, field, order, fromIndex, numberOfUsers)); } else { return persistenceService .selectList(SelectDescriptorBuilder.getUsersWithRole(roleId, fromIndex, numberOfUsers)); } } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the users having the role " + roleId, e); } } @Override public List getActiveUsersWithRole(final long roleId, final int fromIndex, final int numberOfUsers, final String field, final OrderByType order) throws SIdentityException { try { if (order != null) { return persistenceService .selectList(SelectDescriptorBuilder.getActiveUsersWithRole(roleId, field, order, fromIndex, numberOfUsers)); } else { return persistenceService .selectList(SelectDescriptorBuilder.getActiveUsersWithRole(roleId, fromIndex, numberOfUsers)); } } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the active users having the role " + roleId, e); } } @Override public List getInactiveUsersWithRole(final long roleId, final int fromIndex, final int numberOfUsers, final String field, final OrderByType order) throws SIdentityException { try { if (order != null) { return persistenceService .selectList(SelectDescriptorBuilder.getInactiveUsersWithRole(roleId, field, order, fromIndex, numberOfUsers)); } else { return persistenceService .selectList(SelectDescriptorBuilder.getInactiveUsersWithRole(roleId, fromIndex, numberOfUsers)); } } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the inactive users having the role " + roleId, e); } } @Override public void updateGroup(final SGroup group, final EntityUpdateDescriptor descriptor, EntityUpdateDescriptor iconUpdater) throws SIdentityException { final SGroupLogBuilder logBuilder = getGroupLog(ActionType.UPDATED, "Updating the group"); try { updateIcon(descriptor, group.getIconId(), iconUpdater); recorder.recordUpdate(UpdateRecord.buildSetFields(group, descriptor), GROUP); log(group.getId(), SQueriableLog.STATUS_OK, logBuilder, "updateGroup"); } catch (final SRecorderException e) { log(group.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "updateGroup"); throw new SIdentityException("Can't update group " + group, e); } } @Override public void updateCustomUserInfoDefinition(final SCustomUserInfoDefinition customUserInfo, final EntityUpdateDescriptor descriptor) throws SIdentityException { final String methodName = "updateCustomUserInfoDefinition"; final SCustomUserInfoDefinitionLogBuilder logBuilder = getSCustomUserInfoDefinitionLog(ActionType.UPDATED, "Updating the custom user info definition with name " + customUserInfo.getName()); try { recorder.recordUpdate(UpdateRecord.buildSetFields(customUserInfo, descriptor), CUSTOM_USER_INFO_DEFINITION); log(customUserInfo.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } catch (final SRecorderException e) { log(customUserInfo.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SIdentityException("Can't update custom user info definition " + customUserInfo, e); } } @Override public void updateCustomUserInfoValue(final SCustomUserInfoValue customUserInfo, final EntityUpdateDescriptor descriptor) throws SIdentityException { try { recorder.recordUpdate(UpdateRecord.buildSetFields(customUserInfo, descriptor), CUSTOM_USER_INFO_VALUE); } catch (final SRecorderException e) { throw new SIdentityException("Can't update custom user info definition " + customUserInfo, e); } } @Override public SRole updateRole(final SRole role, final EntityUpdateDescriptor descriptor, EntityUpdateDescriptor iconUpdater) throws SIdentityException { final String methodName = "updateRole"; final SRoleLogBuilder logBuilder = getRoleLog(ActionType.UPDATED, "Updating the role with name " + role.getName()); try { updateIcon(descriptor, role.getIconId(), iconUpdater); recorder.recordUpdate(UpdateRecord.buildSetFields(role, descriptor), ROLE); log(role.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); } catch (final SRecorderException e) { log(role.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SIdentityException("Can't update role " + role, e); } return role; } @Override public void updateUser(final SUser user, final EntityUpdateDescriptor descriptor) throws SUserUpdateException { updateUser(user, descriptor, false); } @Deprecated @Override public void updateUser(final SUser user, final EntityUpdateDescriptor descriptor, final boolean isPasswordEncrypted) throws SUserUpdateException { final String methodName = "updateUser"; if (!isPasswordEncrypted) { final String password = (String) descriptor.getFields().get("password"); if (password != null) { final String hash = encrypter.hash(password); descriptor.getFields().put("password", hash); } } final SUserLogBuilder logBuilder = getUserLog(ActionType.UPDATED, "Updating user with user name " + user.getUserName() + ", first name " + user.getFirstName() + ", last name " + user.getLastName()); try { recorder.recordUpdate(UpdateRecord.buildSetFields(user, descriptor), USER); log(user.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } catch (final SRecorderException re) { log(user.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SUserUpdateException(re); } } @Override public void updateUserContactInfo(final SContactInfo contactInfo, final EntityUpdateDescriptor descriptor) throws SIdentityException { final String methodName = "updateUserContactInfo"; final SContactInfoLogBuilder logBuilder = getUserContactInfoLog(ActionType.UPDATED, "Updating " + (contactInfo.isPersonal() ? "personal" : "professional") + " user contact Info for user with Id " + contactInfo.getUserId(), contactInfo); try { recorder.recordUpdate(UpdateRecord.buildSetFields(contactInfo, descriptor), USER_CONTACT_INFO); log(contactInfo.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } catch (final SRecorderException re) { log(contactInfo.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SUserUpdateException(re); } } @Override public void updateUserMembership(final SUserMembership userMembership, final EntityUpdateDescriptor descriptor) throws SIdentityException { final String methodName = "updateUserMembership"; final SUserMembershipLogBuilder logBuilder = getUserMembershipLog(ActionType.UPDATED, "Updating user membership for user " + userMembership.getUsername() + " with role " + userMembership.getRoleName() + " in group " + userMembership.getGroupName(), userMembership); try { recorder.recordUpdate(UpdateRecord.buildSetFields(userMembership, descriptor), USERMEMBERSHIP); log(userMembership.getId(), SQueriableLog.STATUS_OK, logBuilder, methodName); } catch (final SRecorderException e) { log(userMembership.getId(), SQueriableLog.STATUS_FAIL, logBuilder, methodName); throw new SIdentityException("Can't update user membership " + userMembership, e); } } @Override public List getUserMemberships(final int fromIndex, final int numberOfUserMemberships) throws SIdentityException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getElements( SUserMembership.class, "UserMembership", fromIndex, numberOfUserMemberships); try { return persistenceService.selectList(descriptor); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the user memberships", e); } } @Override public List getUserMembershipsOfUser(final long userId, final int fromIndex, final int numberOfUsers) throws SIdentityException { try { final SelectListDescriptor descriptor = SelectDescriptorBuilder .getUserMembershipsOfUser(userId, fromIndex, numberOfUsers); return persistenceService.selectList(descriptor); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the memberships having the user " + userId, e); } } @Override public List getUserMembershipsOfUser(final long userId, final int fromIndex, final int numberOfMemberships, final String field, final OrderByType order) throws SIdentityException { try { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getUserMembershipsOfUser( userId, field, order, fromIndex, numberOfMemberships); return persistenceService.selectList(descriptor); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the memberships having the user" + userId, e); } } @Override public List getUserMembershipsOfUser(final long userId, final int fromIndex, final int numberPerPage, final OrderByOption orderByOption) throws SIdentityException { try { final SelectListDescriptor descriptor = SelectDescriptorBuilder .getUserMembershipsOfUser(userId, new QueryOptions(fromIndex, numberPerPage, Collections.singletonList(orderByOption))); return persistenceService.selectList(descriptor); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the memberships having the user" + userId, e); } } @Override public SUserMembership getUserMembership(final long userId, final long groupId, final long roleId) throws SIdentityException { final SelectOneDescriptor descriptor = SelectDescriptorBuilder.getUserMembership(userId, groupId, roleId); try { return getUserMembership(userId, groupId, roleId, descriptor); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the userMembership with userId = " + userId + ", groupId = " + groupId + ", roleId = " + roleId, e); } } @Override public SUserMembership getLightUserMembership(final long userId, final long groupId, final long roleId) throws SIdentityException { final SelectOneDescriptor descriptor = SelectDescriptorBuilder.getLightUserMembership(userId, groupId, roleId); try { return getUserMembership(userId, groupId, roleId, descriptor); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the userMembership with userId = " + userId + ", groupId = " + groupId + ", roleId = " + roleId, e); } } private SUserMembership getUserMembership(final long userId, final long groupId, final long roleId, final SelectOneDescriptor descriptor) throws SBonitaReadException, SIdentityException { final SUserMembership sUserMembership = persistenceService.selectOne(descriptor); if (sUserMembership == null) { throw new SIdentityException("Can't get the userMembership with userId = " + userId + ", groupId = " + groupId + ", roleId = " + roleId); } return sUserMembership; } @Override public long getNumberOfUserMemberships() throws SIdentityException { try { return persistenceService .selectOne(SelectDescriptorBuilder.getNumberOfElement("UserMembership", SUserMembership.class)); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the number of user membership", e); } } @Override public List getLightUserMemberships(final int startIndex, final int numberOfElements) throws SIdentityException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getElements( SUserMembership.class, "LightUserMembership", startIndex, numberOfElements); try { return persistenceService.selectList(descriptor); } catch (final SBonitaReadException e) { throw new SIdentityException("Can't get the user memberships", e); } } @Override public long getNumberOfUsers(final QueryOptions options) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SUser.class, options, null); } @Override public List searchUsers(final QueryOptions options) throws SBonitaReadException { return persistenceService.searchEntity(SUser.class, options, null); } @Override public long getNumberOfRoles(final QueryOptions options) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SRole.class, options, null); } @Override public List searchRoles(final QueryOptions options) throws SBonitaReadException { return persistenceService.searchEntity(SRole.class, options, null); } @Override public long getNumberOfGroups(final QueryOptions options) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SGroup.class, options, null); } @Override public List searchGroups(final QueryOptions options) throws SBonitaReadException { return persistenceService.searchEntity(SGroup.class, options, null); } private void log(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder, final String methodName) { logBuilder.actionScope(String.valueOf(objectId)); logBuilder.actionStatus(sQueriableLogStatus); logBuilder.objectId(objectId); final SQueriableLog log = logBuilder.build(); if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) { queriableLoggerService.log(this.getClass().getName(), methodName, log); } } @Override public boolean checkCredentials(final SUser user, final String password) { final String hashPassword = user.getPassword(); return encrypter.check(password, hashPassword); } @Override public List searchCustomUserInfoValue(final QueryOptions options) throws SBonitaReadException { return persistenceService.searchEntity(SCustomUserInfoValue.class, options, null); } @Override public List getCustomUserInfoValueOfUserAndDefinitions(long userId, List definitionsIds) throws SBonitaReadException { HashMap parameters = new HashMap<>(); parameters.put("userId", userId); parameters.put("definitionIds", definitionsIds); return persistenceService.selectList(new SelectListDescriptor<>( "getCustomUserInfoValueOfUserAndDefinitions", parameters, SCustomUserInfoValue.class, QueryOptions.ALL_RESULTS)); } @Override public SUser updateUser(long userId, EntityUpdateDescriptor userUpdateDescriptor, EntityUpdateDescriptor personalDataUpdateDescriptor, EntityUpdateDescriptor professionalDataUpdateDescriptor, EntityUpdateDescriptor iconUpdater) throws SIdentityException { // User change SUser sUser = getUser(userId); updateIcon(userUpdateDescriptor, sUser.getIconId(), iconUpdater); if (userUpdateDescriptor != null && !userUpdateDescriptor.getFields().isEmpty()) { updateUser(sUser, userUpdateDescriptor); } // Personal data change if (personalDataUpdateDescriptor != null && !personalDataUpdateDescriptor.getFields().isEmpty()) { SContactInfo persoContactInfo = getUserContactInfo(userId, true); if (persoContactInfo == null) { persoContactInfo = SContactInfo.builder().userId(userId).personal(true).build(); createUserContactInfo(persoContactInfo); } updateUserContactInfo(persoContactInfo, personalDataUpdateDescriptor); } // Professional data change if (professionalDataUpdateDescriptor != null && !professionalDataUpdateDescriptor.getFields().isEmpty()) { SContactInfo professContactInfo = getUserContactInfo(userId, false); if (professContactInfo == null) { professContactInfo = SContactInfo.builder().userId(userId).personal(false).build(); createUserContactInfo(professContactInfo); } updateUserContactInfo(professContactInfo, professionalDataUpdateDescriptor); } return sUser; } private void updateIcon(EntityUpdateDescriptor updateDescriptor, Long iconId, EntityUpdateDescriptor iconUpdater) throws SIdentityException { try { if (iconUpdater != null && !iconUpdater.getFields().isEmpty()) { updateDescriptor.addField("iconId", iconService.replaceIcon((String) iconUpdater.getFields().get("filename"), (byte[]) iconUpdater.getFields().get("content"), iconId).orElse(null)); } } catch (SBonitaReadException | SRecorderException e) { throw new SIdentityException(e); } } @Override public SUser createUser(SUser sUser, SContactInfo personalContactInfo, SContactInfo proContactInfo, String iconFilename, byte[] iconContent) throws SUserCreationException { if (iconFilename != null && iconContent != null) { try { SIcon icon = iconService.createIcon(iconFilename, iconContent); sUser.setIconId(icon.getId()); } catch (SRecorderException e) { throw new SUserCreationException(e); } } SUser user = createUser(sUser); if (personalContactInfo != null) { SContactInfo sContactInfo = new SContactInfo(personalContactInfo); sContactInfo.setUserId(user.getId()); createUserContactInfo(sContactInfo); } if (proContactInfo != null) { SContactInfo sContactInfo = new SContactInfo(proContactInfo); sContactInfo.setUserId(user.getId()); createUserContactInfo(sContactInfo); } return user; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/impl/MD5CredentialsEncrypter.java ================================================ /** * 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.identity.impl; import org.bonitasoft.engine.digest.DigestUtils; import org.springframework.stereotype.Component; @Component public class MD5CredentialsEncrypter implements CredentialsEncrypter { @Override public String hash(final String password) { final byte[] hash = DigestUtils.md5(password); return DigestUtils.encodeBase64AsUtf8String(hash); } @Override public boolean check(final String password, final String hashPassword) { final String hashedPassword = hash(password); return hashedPassword.equals(hashPassword); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SContactInfo.java ================================================ /** * 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.identity.model; import javax.persistence.Cacheable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * User contact info: can be personal or professional contact information. * * @author Emmanuel Duchastenier */ @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "user_contactinfo") @Cacheable(false) public class SContactInfo implements PersistentObject { public static final String ID = "id"; public static final String WEBSITE = "website"; public static final String COUNTRY = "country"; public static final String STATE = "state"; public static final String CITY = "city"; public static final String ZIP_CODE = "zipCode"; public static final String ADDRESS = "address"; public static final String ROOM = "room"; public static final String BUILDING = "building"; public static final String FAX_NUMBER = "faxNumber"; public static final String MOBILE_NUMBER = "mobileNumber"; public static final String PHONE_NUMBER = "phoneNumber"; public static final String EMAIL = "email"; public static final String IS_PERSONAL = "personal"; @Id private long id; @Column private Long userId; @Column private String email; @Column(name = "phone") private String phoneNumber; @Column(name = "mobile") private String mobileNumber; @Column(name = "fax") private String faxNumber; @Column private String building; @Column private String room; @Column private String address; @Column private String zipCode; @Column private String city; @Column private String state; @Column private String country; @Column private String website; @Column private boolean personal; public SContactInfo(final SContactInfo contactInfo) { this(); userId = contactInfo.getUserId(); personal = contactInfo.isPersonal(); address = contactInfo.getAddress(); building = contactInfo.getBuilding(); city = contactInfo.getCity(); country = contactInfo.getCountry(); email = contactInfo.getEmail(); faxNumber = contactInfo.getFaxNumber(); mobileNumber = contactInfo.getMobileNumber(); phoneNumber = contactInfo.getPhoneNumber(); room = contactInfo.getRoom(); state = contactInfo.getState(); website = contactInfo.getWebsite(); zipCode = contactInfo.getZipCode(); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SCustomUserInfoDefinition.java ================================================ /** * 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.identity.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Anthony Birembaut * @author Baptiste Mesta */ @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "custom_usr_inf_def") public class SCustomUserInfoDefinition implements PersistentObject { public static final String ID = "id"; public static final String NAME = "name"; public static final String DESCRIPTION = "description"; public static final String DISPLAY_NAME = "displayName"; @Id private long id; @Column private String name; @Column private String description; } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SCustomUserInfoValue.java ================================================ /** * 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.identity.model; import javax.persistence.Cacheable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Anthony Birembaut * @author Baptiste Mesta * @author Elias Ricken de Medeiros */ @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "custom_usr_inf_val") @Cacheable(false) public class SCustomUserInfoValue implements PersistentObject { public static final String ID = "id"; public static final String USER_ID = "userId"; public static final String DEFINITION_ID = "definitionId"; public static final String VALUE = "value"; @Id private long id; @Column protected long userId; @Column protected long definitionId; @Column protected String value; } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SGroup.java ================================================ /** * 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.identity.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Anthony Birembaut * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "group_") public class SGroup implements PersistentObject, SHavingIcon { public static final String PARENT_PATH = "parentPath"; public static final String ID = "id"; public static final String NAME = "name"; public static final String DESCRIPTION = "description"; public static final String DISPLAY_NAME = "displayName"; public static final String CREATED_BY = "createdBy"; public static final String CREATION_DATE = "creationDate"; public static final String LAST_UPDATE = "lastUpdate"; @Id private long id; @Column private String name; @Column private String description; @Column private String displayName; @Column private String parentPath; @Column private long createdBy; @Column private long creationDate; @Column private long lastUpdate; @Column(name = "iconid") private Long iconId; public String getPath() { if (parentPath == null) { return "/" + getName(); } return parentPath + "/" + getName(); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SHavingIcon.java ================================================ /** * 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.identity.model; /** * @author Baptiste Mesta */ public interface SHavingIcon { Long getIconId(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SRole.java ================================================ /** * 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.identity.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Anthony Birembaut * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "role") public class SRole implements PersistentObject, SHavingIcon { public static final String ID = "id"; public static final String NAME = "name"; public static final String DESCRIPTION = "description"; public static final String DISPLAY_NAME = "displayName"; public static final String ICON_PATH = "iconPath"; public static final String CREATED_BY = "createdBy"; public static final String CREATION_DATE = "creationDate"; public static final String LAST_UPDATE = "lastUpdate"; @Id private long id; @Column(name = "name") private String name; @Column(name = "description") private String description; @Column(name = "displayName") private String displayName; @Column(name = "createdBy") private long createdBy; @Column(name = "creationDate") private long creationDate; @Column(name = "lastUpdate") private long lastUpdate; @Column(name = "iconid") private Long iconId; } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SUser.java ================================================ /** * 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.identity.model; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Anthony Birembaut * @author Baptiste Mesta * @author Yanyan Liu * @author Emmanuel Duchastenier * @author Celine Souchet */ @Data @ToString(exclude = "password") @NoArgsConstructor @AllArgsConstructor @Builder(toBuilder = true) @Entity @Table(name = "user_") public class SUser implements PersistentObject, SHavingIcon { public static final String ID = "id"; public static final String MANAGER_USER_ID = "managerUserId"; public static final String JOB_TITLE = "jobTitle"; public static final String TITLE = "title"; public static final String LAST_NAME = "lastName"; public static final String FIRST_NAME = "firstName"; public static final String USER_NAME = "userName"; public static final String PASSWORD = "password"; public static final String LAST_UPDATE = "lastUpdate"; public static final String LAST_CONNECTION = "lastConnection"; public static final String CREATED_BY = "createdBy"; public static final String CREATION_DATE = "creationDate"; public static final String ENABLED = "enabled"; @Id private long id; @Column private String firstName; @Column private String lastName; @Column private String password; @Column private String userName; @Column private long managerUserId; @Column private String title; @Column private String jobTitle; @Column private long creationDate; @Column private long createdBy; @Column private long lastUpdate; @Column private boolean enabled; @OneToOne(cascade = CascadeType.ALL) @PrimaryKeyJoinColumn private SUserLogin sUserLogin; @Column(name = "iconid") private Long iconId; public SUser(final SUser user) { firstName = user.getFirstName(); lastName = user.getLastName(); password = user.getPassword(); userName = user.getUserName(); jobTitle = user.getJobTitle(); managerUserId = user.getManagerUserId(); createdBy = user.getCreatedBy(); creationDate = user.getCreationDate(); lastUpdate = user.getLastUpdate(); title = user.getTitle(); enabled = user.isEnabled(); iconId = user.getIconId(); } // called by reflection public void setLastConnection(final Long lastConnection) { sUserLogin.setLastConnection(lastConnection); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SUserLogin.java ================================================ /** * 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.identity.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @ToString(exclude = "sUser") @EqualsAndHashCode(exclude = "sUser") @Builder @AllArgsConstructor @Entity @Table(name = "user_login") public class SUserLogin implements PersistentObject { @Id private long id; @Column private Long lastConnection; @OneToOne @PrimaryKeyJoinColumn private SUser sUser; public SUserLogin(Long lastConnection) { this.lastConnection = lastConnection; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/SUserMembership.java ================================================ /** * 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.identity.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Anthony Birembaut * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "user_membership") public class SUserMembership implements PersistentObject { public static final String ID = "id"; public static final String USER_ID = "userId"; public static final String ROLE_ID = "roleId"; public static final String GROUP_ID = "groupId"; public static final String ASSIGNED_BY = "assignedBy"; public static final String ASSIGNED_DATE = "assignedDate"; @Id private long id; @Column private long roleId; @Column private long groupId; @Column private long userId; @Column private long assignedBy; @Column private long assignedDate; private transient String groupParentPath; private transient String roleName; private transient String groupName; private transient String username; public SUserMembership(final long userId, final long groupId, final long roleId) { this.userId = userId; this.groupId = groupId; this.roleId = roleId; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SContactInfoLogBuilder.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public interface SContactInfoLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { SPersistenceLogBuilder setContactInfoUserId(long userId); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SContactInfoLogBuilderFactory.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public interface SContactInfoLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { @Override SContactInfoLogBuilder createNewInstance(); String getContactInfoUserIdKey(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SContactInfoUpdateBuilder.java ================================================ /** * 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.identity.model.builder; /** * @author Emmanuel Duchastenier */ public interface SContactInfoUpdateBuilder extends SIdentityUpdateBuilder { SContactInfoUpdateBuilder updateEmail(final String email); SContactInfoUpdateBuilder updatePhoneNumber(final String phoneNumber); SContactInfoUpdateBuilder updateMobileNumber(final String mobileNumber); SContactInfoUpdateBuilder updateFaxNumber(final String faxNumber); SContactInfoUpdateBuilder updateBuilding(final String building); SContactInfoUpdateBuilder updateRoom(final String room); SContactInfoUpdateBuilder updateAddress(final String address); SContactInfoUpdateBuilder updateZipCode(final String zipCode); SContactInfoUpdateBuilder updateCity(final String city); SContactInfoUpdateBuilder updateState(final String state); SContactInfoUpdateBuilder updateCountry(final String country); SContactInfoUpdateBuilder updateWebsite(final String website); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SContactInfoUpdateBuilderFactory.java ================================================ /** * 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.identity.model.builder; /** * @author Emmanuel Duchastenier */ public interface SContactInfoUpdateBuilderFactory extends SIdentityUpdateBuilderFactory { @Override SContactInfoUpdateBuilder createNewInstance(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SCustomUserInfoDefinitionLogBuilder.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Yanyan Liu */ public interface SCustomUserInfoDefinitionLogBuilder extends HasCRUDEAction, SPersistenceLogBuilder { } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SCustomUserInfoDefinitionLogBuilderFactory.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Yanyan Liu */ public interface SCustomUserInfoDefinitionLogBuilderFactory extends HasCRUDEActionFactory, SPersistenceLogBuilderFactory { @Override SCustomUserInfoDefinitionLogBuilder createNewInstance(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SCustomUserInfoDefinitionUpdateBuilder.java ================================================ /** * 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.identity.model.builder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SCustomUserInfoDefinitionUpdateBuilder extends SIdentityUpdateBuilder { SCustomUserInfoDefinitionUpdateBuilder updateName(final String name); SCustomUserInfoDefinitionUpdateBuilder updateDisplayName(final String displayName); SCustomUserInfoDefinitionUpdateBuilder updateDescription(final String description); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SCustomUserInfoDefinitionUpdateBuilderFactory.java ================================================ /** * 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.identity.model.builder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SCustomUserInfoDefinitionUpdateBuilderFactory extends SIdentityUpdateBuilderFactory { @Override SCustomUserInfoDefinitionUpdateBuilder createNewInstance(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SCustomUserInfoValueUpdateBuilder.java ================================================ /** * 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.identity.model.builder; /** * @author Baptiste Mesta */ public interface SCustomUserInfoValueUpdateBuilder extends SIdentityUpdateBuilder { SCustomUserInfoValueUpdateBuilder updateValue(final String value); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SCustomUserInfoValueUpdateBuilderFactory.java ================================================ /** * 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.identity.model.builder; /** * @author Baptiste Mesta */ public interface SCustomUserInfoValueUpdateBuilderFactory extends SIdentityUpdateBuilderFactory { @Override SCustomUserInfoValueUpdateBuilder createNewInstance(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SGroupLogBuilder.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Yanyan Liu */ public interface SGroupLogBuilder extends HasCRUDEAction, SPersistenceLogBuilder { } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SGroupLogBuilderFactory.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Yanyan Liu */ public interface SGroupLogBuilderFactory extends HasCRUDEActionFactory, SPersistenceLogBuilderFactory { @Override SGroupLogBuilder createNewInstance(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SGroupUpdateBuilder.java ================================================ /** * 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.identity.model.builder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SGroupUpdateBuilder extends SIdentityUpdateBuilder { SGroupUpdateBuilder updateName(final String name); SGroupUpdateBuilder updateDisplayName(final String displayName); SGroupUpdateBuilder updateDescription(final String description); SGroupUpdateBuilder updateParentPath(final String parentPath); SGroupUpdateBuilder updateCreatedBy(final long createdBy); SGroupUpdateBuilder updateCreationDate(final long creationDate); SGroupUpdateBuilder updateLastUpdate(final long lastUpdate); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SGroupUpdateBuilderFactory.java ================================================ /** * 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.identity.model.builder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SGroupUpdateBuilderFactory extends SIdentityUpdateBuilderFactory { @Override SGroupUpdateBuilder createNewInstance(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SIdentityUpdateBuilder.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta */ public interface SIdentityUpdateBuilder { EntityUpdateDescriptor done(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SIdentityUpdateBuilderFactory.java ================================================ /** * 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.identity.model.builder; /** * @author Baptiste Mesta */ public interface SIdentityUpdateBuilderFactory { SIdentityUpdateBuilder createNewInstance(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SMembershipLogBuilder.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SMembershipLogBuilder extends HasCRUDEAction, SPersistenceLogBuilder { SMembershipLogBuilder roleName(final String roleName); SMembershipLogBuilder groupPath(final String groupPath); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SMembershipLogBuilderFactory.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SMembershipLogBuilderFactory extends HasCRUDEAction, SPersistenceLogBuilder { String getRoleNameKey(); String getGroupPathKey(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SProfileMetadataValueLogBuilder.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SProfileMetadataValueLogBuilder extends HasCRUDEAction, SPersistenceLogBuilder { SProfileMetadataValueLogBuilder identityUserName(final String userName); SProfileMetadataValueLogBuilder metadataName(final String metadataName); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SProfileMetadataValueLogBuilderFactory.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SProfileMetadataValueLogBuilderFactory extends HasCRUDEActionFactory, SPersistenceLogBuilderFactory { String getIdentityUserNameKey(); String getCustomUserInfoDefinitionNameKey(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SRoleLogBuilder.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Yanyan Liu */ public interface SRoleLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SRoleLogBuilderFactory.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Yanyan Liu */ public interface SRoleLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { @Override SRoleLogBuilder createNewInstance(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SRoleUpdateBuilder.java ================================================ /** * 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.identity.model.builder; /** * @author Baptiste Mesta * @author Bole Zhang * @author Matthieu Chaffotte */ public interface SRoleUpdateBuilder extends SIdentityUpdateBuilder { SRoleUpdateBuilder updateName(final String name); SRoleUpdateBuilder updateDisplayName(final String displayName); SRoleUpdateBuilder updateDescription(final String description); SRoleUpdateBuilder updateCreatedBy(final long createdBy); SRoleUpdateBuilder updateCreationDate(final long creationDate); SRoleUpdateBuilder updateLastUpdate(final long lastUpdate); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SRoleUpdateBuilderFactory.java ================================================ /** * 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.identity.model.builder; /** * @author Baptiste Mesta * @author Bole Zhang * @author Matthieu Chaffotte */ public interface SRoleUpdateBuilderFactory extends SIdentityUpdateBuilderFactory { @Override SRoleUpdateBuilder createNewInstance(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserLogBuilder.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Yanyan Liu */ public interface SUserLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserLogBuilderFactory.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Yanyan Liu */ public interface SUserLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { @Override SUserLogBuilder createNewInstance(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserMembershipLogBuilder.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SUserMembershipLogBuilder extends HasCRUDEAction, SPersistenceLogBuilder { SUserMembershipLogBuilder roleID(final long roleId); SUserMembershipLogBuilder groupId(final long groupId); SUserMembershipLogBuilder identityUserId(final long userId); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserMembershipLogBuilderFactory.java ================================================ /** * 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.identity.model.builder; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Yanyan Liu * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface SUserMembershipLogBuilderFactory extends HasCRUDEActionFactory, SPersistenceLogBuilderFactory { @Override SUserMembershipLogBuilder createNewInstance(); String getRoleNameKey(); String getGroupPathKey(); String getIdentityUserNameKey(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserMembershipUpdateBuilder.java ================================================ /** * 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.identity.model.builder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SUserMembershipUpdateBuilder extends SIdentityUpdateBuilder { SUserMembershipUpdateBuilder updateUserId(final long userId); SUserMembershipUpdateBuilder updateGroupId(final long groupId); SUserMembershipUpdateBuilder updateRoleId(final long roleId); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserMembershipUpdateBuilderFactory.java ================================================ /** * 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.identity.model.builder; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SUserMembershipUpdateBuilderFactory extends SIdentityUpdateBuilderFactory { @Override SUserMembershipUpdateBuilder createNewInstance(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserUpdateBuilder.java ================================================ /** * 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.identity.model.builder; /** * @author Baptiste Mesta * @author Yanyan Liu * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SUserUpdateBuilder extends SIdentityUpdateBuilder { SUserUpdateBuilder updateUserName(final String userName); SUserUpdateBuilder updatePassword(final String password); SUserUpdateBuilder updateFirstName(final String firstName); SUserUpdateBuilder updateLastName(final String lastName); SUserUpdateBuilder updateTitle(final String title); SUserUpdateBuilder updateJobTitle(final String jobTitle); SUserUpdateBuilder updateManagerUserId(final long managerUserId); SUserUpdateBuilder updateLastUpdate(final long lastUpdate); SUserUpdateBuilder updateLastConnection(final long lastConnection); SUserUpdateBuilder updateEnabled(final boolean enabled); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/SUserUpdateBuilderFactory.java ================================================ /** * 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.identity.model.builder; /** * @author Baptiste Mesta * @author Yanyan Liu * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface SUserUpdateBuilderFactory extends SIdentityUpdateBuilderFactory { @Override SUserUpdateBuilder createNewInstance(); } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SContactInfoLogBuilderFactoryImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SContactInfoLogBuilder; import org.bonitasoft.engine.identity.model.builder.SContactInfoLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public class SContactInfoLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SContactInfoLogBuilderFactory { public static final int USER_CONTACT_INFO_INDEX = 1; public static final int USER_CONTACT_INFO_USERID_INDEX = 2; public static final String USER_CONTACT_INFO_INDEX_NAME = "numericIndex2"; public static final String USER_CONTACT_INFO_USERID_INDEX_NAME = "numericIndex3"; @Override public SContactInfoLogBuilder createNewInstance() { return new SContactInfoLogBuilderImpl(); } @Override public String getObjectIdKey() { return USER_CONTACT_INFO_INDEX_NAME; } @Override public String getContactInfoUserIdKey() { return USER_CONTACT_INFO_USERID_INDEX_NAME; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SContactInfoLogBuilderImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SContactInfoLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public class SContactInfoLogBuilderImpl extends CRUDELogBuilder implements SContactInfoLogBuilder { private static final String PREFIX = "IDENTITY_USER_CONTACT_INFO"; @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(SContactInfoLogBuilderFactoryImpl.USER_CONTACT_INFO_INDEX, objectId); return this; } @Override public SPersistenceLogBuilder setContactInfoUserId(final long userId) { queriableLogBuilder.numericIndex(SContactInfoLogBuilderFactoryImpl.USER_CONTACT_INFO_USERID_INDEX, userId); return this; } @Override protected String getActionTypePrefix() { return PREFIX; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(SContactInfoLogBuilderFactoryImpl.USER_CONTACT_INFO_USERID_INDEX) == 0L) { throw new MissingMandatoryFieldsException( "Some mandatory fields are missing: Identity Contact info User Id"); } } public static SContactInfoLogBuilder getInstance() { return new SContactInfoLogBuilderImpl(); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SContactInfoUpdateBuilderFactoryImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SContactInfoUpdateBuilder; import org.bonitasoft.engine.identity.model.builder.SContactInfoUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public class SContactInfoUpdateBuilderFactoryImpl implements SContactInfoUpdateBuilderFactory { @Override public SContactInfoUpdateBuilder createNewInstance() { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); return new SContactInfoUpdateBuilderImpl(descriptor); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SContactInfoUpdateBuilderImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.SContactInfo; import org.bonitasoft.engine.identity.model.builder.SContactInfoUpdateBuilder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public class SContactInfoUpdateBuilderImpl implements SContactInfoUpdateBuilder { private final EntityUpdateDescriptor descriptor; public SContactInfoUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) { super(); this.descriptor = descriptor; } @Override public EntityUpdateDescriptor done() { return descriptor; } @Override public SContactInfoUpdateBuilder updateEmail(final String email) { descriptor.addField(SContactInfo.EMAIL, email); return this; } @Override public SContactInfoUpdateBuilder updatePhoneNumber(final String phoneNumber) { descriptor.addField(SContactInfo.PHONE_NUMBER, phoneNumber); return this; } @Override public SContactInfoUpdateBuilder updateMobileNumber(final String mobileNumber) { descriptor.addField(SContactInfo.MOBILE_NUMBER, mobileNumber); return this; } @Override public SContactInfoUpdateBuilder updateFaxNumber(final String faxNumber) { descriptor.addField(SContactInfo.FAX_NUMBER, faxNumber); return this; } @Override public SContactInfoUpdateBuilder updateBuilding(final String building) { descriptor.addField(SContactInfo.BUILDING, building); return this; } @Override public SContactInfoUpdateBuilder updateRoom(final String room) { descriptor.addField(SContactInfo.ROOM, room); return this; } @Override public SContactInfoUpdateBuilder updateAddress(final String address) { descriptor.addField(SContactInfo.ADDRESS, address); return this; } @Override public SContactInfoUpdateBuilder updateZipCode(final String zipCode) { descriptor.addField(SContactInfo.ZIP_CODE, zipCode); return this; } @Override public SContactInfoUpdateBuilder updateCity(final String city) { descriptor.addField(SContactInfo.CITY, city); return this; } @Override public SContactInfoUpdateBuilder updateState(final String state) { descriptor.addField(SContactInfo.STATE, state); return this; } @Override public SContactInfoUpdateBuilder updateCountry(final String country) { descriptor.addField(SContactInfo.COUNTRY, country); return this; } @Override public SContactInfoUpdateBuilder updateWebsite(final String website) { descriptor.addField(SContactInfo.WEBSITE, website); return this; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SCustomUserInfoDefinitionLogBuilderFactoryImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionLogBuilder; import org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Yanyan Liu * @author Matthieu Chaffotte */ public class SCustomUserInfoDefinitionLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SCustomUserInfoDefinitionLogBuilderFactory { public static final int SPROFILE_METADATA_DEFINITION_INDEX = 1; public static final String SPROFILE_METADATA_DEFINITION_INDEX_NAME = "numericIndex2"; @Override public SCustomUserInfoDefinitionLogBuilder createNewInstance() { return new SCustomUserInfoDefinitionLogBuilderImpl(); } @Override public String getObjectIdKey() { return SPROFILE_METADATA_DEFINITION_INDEX_NAME; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SCustomUserInfoDefinitionLogBuilderImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Yanyan Liu * @author Matthieu Chaffotte */ public class SCustomUserInfoDefinitionLogBuilderImpl extends CRUDELogBuilder implements SCustomUserInfoDefinitionLogBuilder { private static final String PREFIX = "IDENTITY_CUSTOM_USER_INFO_DEFINITION"; @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex( SCustomUserInfoDefinitionLogBuilderFactoryImpl.SPROFILE_METADATA_DEFINITION_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return PREFIX; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex( SCustomUserInfoDefinitionLogBuilderFactoryImpl.SPROFILE_METADATA_DEFINITION_INDEX) == 0L) { throw new MissingMandatoryFieldsException( "Some mandatory fields are missing: " + "Identity SCustomUserInfoDefinition Id"); } } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SCustomUserInfoDefinitionUpdateBuilderFactoryImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilder; import org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class SCustomUserInfoDefinitionUpdateBuilderFactoryImpl implements SCustomUserInfoDefinitionUpdateBuilderFactory { @Override public SCustomUserInfoDefinitionUpdateBuilder createNewInstance() { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); return new SCustomUserInfoDefinitionUpdateBuilderImpl(descriptor); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SCustomUserInfoDefinitionUpdateBuilderImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.model.builder.SCustomUserInfoDefinitionUpdateBuilder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class SCustomUserInfoDefinitionUpdateBuilderImpl implements SCustomUserInfoDefinitionUpdateBuilder { private final EntityUpdateDescriptor descriptor; public SCustomUserInfoDefinitionUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) { super(); this.descriptor = descriptor; } @Override public EntityUpdateDescriptor done() { return this.descriptor; } @Override public SCustomUserInfoDefinitionUpdateBuilder updateName(final String name) { this.descriptor.addField(SCustomUserInfoDefinition.NAME, name); return this; } @Override public SCustomUserInfoDefinitionUpdateBuilder updateDisplayName(final String displayName) { this.descriptor.addField(SCustomUserInfoDefinition.DISPLAY_NAME, displayName); return this; } @Override public SCustomUserInfoDefinitionUpdateBuilder updateDescription(final String description) { this.descriptor.addField(SCustomUserInfoDefinition.DESCRIPTION, description); return this; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SCustomUserInfoValueUpdateBuilderFactoryImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SCustomUserInfoValueUpdateBuilder; import org.bonitasoft.engine.identity.model.builder.SCustomUserInfoValueUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class SCustomUserInfoValueUpdateBuilderFactoryImpl implements SCustomUserInfoValueUpdateBuilderFactory { @Override public SCustomUserInfoValueUpdateBuilder createNewInstance() { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); return new SCustomUserInfoValueUpdateBuilderImpl(descriptor); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SCustomUserInfoValueUpdateBuilderImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.SCustomUserInfoValue; import org.bonitasoft.engine.identity.model.builder.SCustomUserInfoValueUpdateBuilder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class SCustomUserInfoValueUpdateBuilderImpl implements SCustomUserInfoValueUpdateBuilder { private final EntityUpdateDescriptor descriptor; public SCustomUserInfoValueUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) { super(); this.descriptor = descriptor; } @Override public EntityUpdateDescriptor done() { return this.descriptor; } @Override public SCustomUserInfoValueUpdateBuilder updateValue(final String value) { this.descriptor.addField(SCustomUserInfoValue.VALUE, value); return this; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SGroupLogBuilderFactoryImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SGroupLogBuilder; import org.bonitasoft.engine.identity.model.builder.SGroupLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Yanyan Liu * @author Matthieu Chaffotte */ public class SGroupLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SGroupLogBuilderFactory { public static final int GROUP_INDEX = 2; public static final String GROUP_INDEX_NAME = "numericIndex3"; @Override public SGroupLogBuilder createNewInstance() { return new SGroupLogBuilderImpl(); } @Override public String getObjectIdKey() { return GROUP_INDEX_NAME; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SGroupLogBuilderImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SGroupLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Yanyan Liu * @author Matthieu Chaffotte */ public class SGroupLogBuilderImpl extends CRUDELogBuilder implements SGroupLogBuilder { private static final String PREFIX = "IDENTITY_GROUP"; @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(SGroupLogBuilderFactoryImpl.GROUP_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return PREFIX; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(SGroupLogBuilderFactoryImpl.GROUP_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + "Identity Group Id"); } } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SGroupUpdateBuilderFactoryImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SGroupUpdateBuilder; import org.bonitasoft.engine.identity.model.builder.SGroupUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class SGroupUpdateBuilderFactoryImpl implements SGroupUpdateBuilderFactory { @Override public SGroupUpdateBuilder createNewInstance() { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); return new SGroupUpdateBuilderImpl(descriptor); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SGroupUpdateBuilderImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.builder.SGroupUpdateBuilder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class SGroupUpdateBuilderImpl implements SGroupUpdateBuilder { private static final String PARENT_PATH = "parentPath"; private final EntityUpdateDescriptor descriptor; public SGroupUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) { super(); this.descriptor = descriptor; } @Override public EntityUpdateDescriptor done() { return descriptor; } @Override public SGroupUpdateBuilder updateName(final String name) { descriptor.addField(SGroup.NAME, name); return this; } @Override public SGroupUpdateBuilder updateDisplayName(final String displayName) { descriptor.addField(SGroup.DISPLAY_NAME, displayName); return this; } @Override public SGroupUpdateBuilder updateDescription(final String description) { descriptor.addField(SGroup.DESCRIPTION, description); return this; } @Override public SGroupUpdateBuilder updateParentPath(final String parentPath) { descriptor.addField(PARENT_PATH, parentPath); return this; } @Override public SGroupUpdateBuilder updateCreatedBy(final long createdBy) { descriptor.addField(SGroup.CREATED_BY, createdBy); return this; } @Override public SGroupUpdateBuilder updateCreationDate(final long creationDate) { descriptor.addField(SGroup.CREATION_DATE, creationDate); return this; } @Override public SGroupUpdateBuilder updateLastUpdate(final long lastUpdate) { descriptor.addField(SGroup.LAST_UPDATE, lastUpdate); return this; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SRoleLogBuilderFactoryImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SRoleLogBuilder; import org.bonitasoft.engine.identity.model.builder.SRoleLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Yanyan Liu * @author Matthieu Chafotte */ public class SRoleLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SRoleLogBuilderFactory { public static final int ROLE_INDEX = 0; public static final String ROLE_INDEX_NAME = "numericIndex1"; @Override public SRoleLogBuilder createNewInstance() { return new SRoleLogBuilderImpl(); } @Override public String getObjectIdKey() { return ROLE_INDEX_NAME; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SRoleLogBuilderImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SRoleLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Yanyan Liu * @author Matthieu Chafotte */ public class SRoleLogBuilderImpl extends CRUDELogBuilder implements SRoleLogBuilder { private static final String PREFIX = "IDENTITY_ROLE"; @Override protected String getActionTypePrefix() { return PREFIX; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(SRoleLogBuilderFactoryImpl.ROLE_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + "Identity Role Id"); } } @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(SRoleLogBuilderFactoryImpl.ROLE_INDEX, objectId); return this; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SRoleUpdateBuilderFactoryImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SRoleUpdateBuilder; import org.bonitasoft.engine.identity.model.builder.SRoleUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta * @author Bole Zhang * @author Matthieu Chaffotte */ public class SRoleUpdateBuilderFactoryImpl implements SRoleUpdateBuilderFactory { @Override public SRoleUpdateBuilder createNewInstance() { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); return new SRoleUpdateBuilderImpl(descriptor); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SRoleUpdateBuilderImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.builder.SRoleUpdateBuilder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta * @author Bole Zhang * @author Matthieu Chaffotte */ public class SRoleUpdateBuilderImpl implements SRoleUpdateBuilder { private final EntityUpdateDescriptor descriptor; public SRoleUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) { super(); this.descriptor = descriptor; } @Override public EntityUpdateDescriptor done() { return descriptor; } @Override public SRoleUpdateBuilder updateName(final String name) { descriptor.addField(SRole.NAME, name); return this; } @Override public SRoleUpdateBuilder updateDisplayName(final String displayName) { descriptor.addField(SRole.DISPLAY_NAME, displayName); return this; } @Override public SRoleUpdateBuilder updateDescription(final String description) { descriptor.addField(SRole.DESCRIPTION, description); return this; } @Override public SRoleUpdateBuilder updateCreatedBy(final long createdBy) { descriptor.addField(SRole.CREATED_BY, createdBy); return this; } @Override public SRoleUpdateBuilder updateCreationDate(final long creationDate) { descriptor.addField(SRole.CREATION_DATE, creationDate); return this; } @Override public SRoleUpdateBuilder updateLastUpdate(final long lastUpdate) { descriptor.addField(SRole.LAST_UPDATE, lastUpdate); return this; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserLogBuilderFactoryImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SUserLogBuilder; import org.bonitasoft.engine.identity.model.builder.SUserLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Yanyan Liu * @author Matthieu Chaffotte */ public class SUserLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SUserLogBuilderFactory { public static final int USER_INDEX = 1; public static final String USER_INDEX_NAME = "numericIndex2"; @Override public SUserLogBuilder createNewInstance() { return new SUserLogBuilderImpl(); } @Override public String getObjectIdKey() { return USER_INDEX_NAME; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserLogBuilderImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SUserLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Yanyan Liu * @author Matthieu Chaffotte */ public class SUserLogBuilderImpl extends CRUDELogBuilder implements SUserLogBuilder { private static final String PREFIX = "IDENTITY_USER"; @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(SUserLogBuilderFactoryImpl.USER_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return PREFIX; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(SUserLogBuilderFactoryImpl.USER_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: Identity User Id"); } } public static SUserLogBuilder getInstance() { return new SUserLogBuilderImpl(); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserMembershipLogBuilderFactoryImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SUserMembershipLogBuilder; import org.bonitasoft.engine.identity.model.builder.SUserMembershipLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Yanyan Liu * @author Matthieu Chaffotte */ public class SUserMembershipLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SUserMembershipLogBuilderFactory { public static final int USER_MEMBERSHIP_INDEX = 4; public static final String USER_MEMBERSHIP_INDEX_NAME = "numericIndex5"; public static final int ROLE_INDEX = 3; public static final String ROLE_INDEX_NAME = "textualIndex4"; public static final int USER_INDEX = 1; public static final String USER_INDEX_NAME = "textualIndex2"; public static final int GROUP_INDEX = 2; public static final String GROUP_INDEX_NAME = "textualIndex3"; @Override public SUserMembershipLogBuilder createNewInstance() { return new SUserMembershipLogBuilderImpl(); } @Override public String getObjectIdKey() { return USER_MEMBERSHIP_INDEX_NAME; } @Override public String getRoleNameKey() { return ROLE_INDEX_NAME; } @Override public String getIdentityUserNameKey() { return USER_INDEX_NAME; } @Override public String getGroupPathKey() { return GROUP_INDEX_NAME; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserMembershipLogBuilderImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SUserMembershipLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Yanyan Liu * @author Matthieu Chaffotte */ public class SUserMembershipLogBuilderImpl extends CRUDELogBuilder implements SUserMembershipLogBuilder { private static final String PREFIX = "IDENTITY_USER_MEMBERSHIP"; @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(SUserMembershipLogBuilderFactoryImpl.USER_MEMBERSHIP_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return PREFIX; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(SUserMembershipLogBuilderFactoryImpl.USER_MEMBERSHIP_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + "UserMembership Id"); } if (log.getNumericIndex(SUserMembershipLogBuilderFactoryImpl.ROLE_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + "Role Id"); } if (log.getNumericIndex(SUserMembershipLogBuilderFactoryImpl.USER_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + "User Id"); } } @Override public SUserMembershipLogBuilder roleID(final long roleId) { queriableLogBuilder.numericIndex(SUserMembershipLogBuilderFactoryImpl.ROLE_INDEX, roleId); return this; } @Override public SUserMembershipLogBuilder identityUserId(final long userId) { queriableLogBuilder.numericIndex(SUserMembershipLogBuilderFactoryImpl.USER_INDEX, userId); return this; } @Override public SUserMembershipLogBuilder groupId(final long groupId) { queriableLogBuilder.numericIndex(SUserMembershipLogBuilderFactoryImpl.GROUP_INDEX, groupId); return this; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserMembershipUpdateBuilderFactoryImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SUserMembershipUpdateBuilder; import org.bonitasoft.engine.identity.model.builder.SUserMembershipUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class SUserMembershipUpdateBuilderFactoryImpl implements SUserMembershipUpdateBuilderFactory { @Override public SUserMembershipUpdateBuilder createNewInstance() { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); return new SUserMembershipUpdateBuilderImpl(descriptor); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserMembershipUpdateBuilderImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.identity.model.builder.SUserMembershipUpdateBuilder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class SUserMembershipUpdateBuilderImpl implements SUserMembershipUpdateBuilder { private final EntityUpdateDescriptor descriptor; public SUserMembershipUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) { super(); this.descriptor = descriptor; } @Override public EntityUpdateDescriptor done() { return descriptor; } @Override public SUserMembershipUpdateBuilder updateUserId(final long userId) { descriptor.addField(SUserMembership.USER_ID, userId); return this; } @Override public SUserMembershipUpdateBuilder updateGroupId(final long groupId) { descriptor.addField(SUserMembership.GROUP_ID, groupId); return this; } @Override public SUserMembershipUpdateBuilder updateRoleId(final long roleId) { descriptor.addField(SUserMembership.ROLE_ID, roleId); return this; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserUpdateBuilderFactoryImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.builder.SUserUpdateBuilder; import org.bonitasoft.engine.identity.model.builder.SUserUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta * @author Yanyan Liu * @author Matthieu Chaffotte * @author Celine Souchet */ public class SUserUpdateBuilderFactoryImpl implements SUserUpdateBuilderFactory { @Override public SUserUpdateBuilder createNewInstance() { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); return new SUserUpdateBuilderImpl(descriptor); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/model/builder/impl/SUserUpdateBuilderImpl.java ================================================ /** * 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.identity.model.builder.impl; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.builder.SUserUpdateBuilder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta * @author Yanyan Liu * @author Matthieu Chaffotte * @author Celine Souchet */ public class SUserUpdateBuilderImpl implements SUserUpdateBuilder { private final EntityUpdateDescriptor descriptor; public SUserUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) { super(); this.descriptor = descriptor; } public static SUserUpdateBuilder updateBuilder() { return new SUserUpdateBuilderImpl(new EntityUpdateDescriptor()); } @Override public EntityUpdateDescriptor done() { return descriptor; } @Override public SUserUpdateBuilder updateUserName(final String username) { descriptor.addField(SUser.USER_NAME, username); return this; } @Override public SUserUpdateBuilder updatePassword(final String password) { descriptor.addField(SUser.PASSWORD, password); return this; } @Override public SUserUpdateBuilder updateFirstName(final String firstName) { descriptor.addField(SUser.FIRST_NAME, firstName); return this; } @Override public SUserUpdateBuilder updateLastName(final String lastName) { descriptor.addField(SUser.LAST_NAME, lastName); return this; } @Override public SUserUpdateBuilder updateTitle(final String title) { descriptor.addField(SUser.TITLE, title); return this; } @Override public SUserUpdateBuilder updateJobTitle(final String jobTitle) { descriptor.addField(SUser.JOB_TITLE, jobTitle); return this; } @Override public SUserUpdateBuilder updateManagerUserId(final long managerUserId) { descriptor.addField(SUser.MANAGER_USER_ID, managerUserId); return this; } @Override public SUserUpdateBuilder updateLastUpdate(final long lastUpdate) { descriptor.addField(SUser.LAST_UPDATE, lastUpdate); return this; } @Override public SUserUpdateBuilder updateLastConnection(final long lastConnection) { descriptor.addField(SUser.LAST_CONNECTION, lastConnection); return this; } @Override public SUserUpdateBuilder updateEnabled(final boolean enabled) { descriptor.addField(SUser.ENABLED, enabled); return this; } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/package-info.java ================================================ /** * Copyright (C) 2015 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. **/ /** *

      Manages information about an organization, that is, the set of users who can act in processes. Handles creation, * modification, and deletion of * organizations, groups, roles, memberships, and users.

      *

      The identity service relies upon database access and produce database access event * {@link org.bonitasoft.engine.events.EventService}

      * * @see org.bonitasoft.engine.identity.IdentityService * @since 6.0.0 */ package org.bonitasoft.engine.identity; ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/recorder/SelectDescriptorBuilder.java ================================================ /** * 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.identity.recorder; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.identity.model.SContactInfo; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; /** * @author Matthieu Chaffotte * @author Bole Zhang */ public class SelectDescriptorBuilder { public static SelectListDescriptor getChildrenOfGroup(final SGroup group, final int fromIndex, final int numberOfGroups) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfGroups, SGroup.class, "id", OrderByType.ASC); return getChildrenOfGroup(group, queryOptions); } public static SelectListDescriptor getChildrenOfGroup(final SGroup group, final QueryOptions queryOptions) { final Map parameters = Collections.singletonMap("groupPath", group.getPath()); return new SelectListDescriptor<>("getChildrenOfGroup", parameters, SGroup.class, queryOptions); } public static SelectListDescriptor getChildrenOfGroup(final SGroup group, final String field, final OrderByType order, final int fromIndex, final int numberOfGroups) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfGroups, SGroup.class, field, order); return getChildrenOfGroup(group, queryOptions); } public static SelectByIdDescriptor getElementById(final Class clazz, final String elementName, final long id) { return new SelectByIdDescriptor<>(clazz, id); } public static SelectListDescriptor getElements(final Class clazz, final String elementName, final int fromIndex, final int numberOfElements) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements); return getElements(clazz, elementName, queryOptions); } public static SelectListDescriptor getElements(final Class clazz, final String elementName, final QueryOptions queryOptions) { final Map parameters = Collections.emptyMap(); return new SelectListDescriptor<>("get" + elementName + "s", parameters, clazz, queryOptions); } public static SelectListDescriptor getElements(final Class clazz, final String elementName, final String field, final OrderByType order, final int fromIndex, final int numberOfElements) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, clazz, field, order); return getElements(clazz, elementName, queryOptions); } public static SelectListDescriptor getElementsByIds(final Class clazz, final String elementName, final Collection ids) { final Map parameters = Collections.singletonMap("ids", ids); final int maxResults = ids != null ? ids.size() : 0; return new SelectListDescriptor<>("get" + elementName + "sByIds", parameters, clazz, new QueryOptions(0, maxResults)); } public static SelectOneDescriptor getGroupByName(final String groupName) { final Map parameters = Collections.singletonMap("name", groupName); return new SelectOneDescriptor<>("getGroupByName", parameters, SGroup.class); } public static SelectOneDescriptor getGroupByPath(final String parentPath, final String groupName) { final Map parameters = new HashMap<>(); parameters.put("name", groupName); parameters.put("parentPath", parentPath); return new SelectOneDescriptor<>("getGroupByNameAndPath", parameters, SGroup.class); } public static SelectByIdDescriptor getLightElementById(final Class clazz, final String elementName, final long id) { return new SelectByIdDescriptor<>(clazz, id); } public static SelectOneDescriptor getLightUserMembership(final long userId, final long groupId, final long roleId) { final Map parameters = new HashMap<>(); parameters.put("userId", userId); parameters.put("roleId", roleId); parameters.put("groupId", groupId); return new SelectOneDescriptor<>("getLightUserMembershipWithIds", parameters, SUserMembership.class); } public static SelectOneDescriptor getCustomUserInfoDefinitionByName(final String name) { final Map parameters = Collections.singletonMap("name", name); return new SelectOneDescriptor<>("getCustomUserInfoDefinitionByName", parameters, SCustomUserInfoDefinition.class); } public static SelectOneDescriptor getNumberOfElement(final String elementName, final Class clazz) { final Map emptyMap = Collections.emptyMap(); return new SelectOneDescriptor<>("getNumberOf" + elementName, emptyMap, clazz, Long.class); } public static SelectOneDescriptor getNumberOfGroupChildren(final String groupParentPath) { final Map parameters = Collections.singletonMap("parentPath", groupParentPath); return new SelectOneDescriptor<>("getNumberOfGroupChildren", parameters, SGroup.class, Long.class); } public static SelectOneDescriptor getNumberOfUserMembershipsOfUser(final long userId) { final Map parameters = Collections.singletonMap("userId", userId); return new SelectOneDescriptor<>("getNumberOfUserMembershipsOfUser", parameters, SUserMembership.class, Long.class); } public static SelectOneDescriptor getNumberOfUsersByGroup(final long groupId) { final Map parameters = new HashMap<>(); parameters.put("groupId", groupId); return new SelectOneDescriptor<>("getNumberOfUsersByGroup", parameters, SUser.class, Long.class); } public static SelectOneDescriptor getNumberOfUsersByMembership(final long groupId, final long roleId) { final Map parameters = new HashMap<>(); parameters.put("roleId", roleId); parameters.put("groupId", groupId); return new SelectOneDescriptor<>("getNumberOfUsersByMembership", parameters, SUser.class, Long.class); } public static SelectOneDescriptor getNumberOfUsersByRole(final long roleId) { final Map parameters = Collections.singletonMap("roleId", roleId); return new SelectOneDescriptor<>("getNumberOfUsersByRole", parameters, SUser.class, Long.class); } public static SelectOneDescriptor getRoleByName(final String roleName) { final Map parameters = Collections.singletonMap("name", roleName); return new SelectOneDescriptor<>("getRoleByName", parameters, SRole.class); } public static SelectOneDescriptor getUserByUserName(final String userName) { final Map parameters = Collections.singletonMap("userName", userName); return new SelectOneDescriptor<>("getUserByUserName", parameters, SUser.class); } public static SelectOneDescriptor getUserContactInfo(final long userId, final boolean isPersonal) { final Map parameters = new HashMap<>(); parameters.put("userId", userId); parameters.put("personal", isPersonal); return new SelectOneDescriptor<>("getUserContactInfo", parameters, SContactInfo.class); } public static SelectOneDescriptor getUserMembership(final long userId, final long groupId, final long roleId) { final Map parameters = new HashMap<>(); parameters.put("userId", userId); parameters.put("roleId", roleId); parameters.put("groupId", groupId); return new SelectOneDescriptor<>("getUserMembershipWithIds", parameters, SUserMembership.class); } public static SelectListDescriptor getUserMembershipsByGroup(final long groupId, final int startIndex, final int maxResults) { final Map parameters = new HashMap<>(); parameters.put("groupId", groupId); return new SelectListDescriptor<>("getUserMembershipsByGroup", parameters, SUserMembership.class, new QueryOptions(startIndex, maxResults)); } public static SelectListDescriptor getUserMembershipsByRole(final long roleId, final int startIndex, final int maxResults) { final Map parameters = Collections.singletonMap("roleId", roleId); return new SelectListDescriptor<>("getUserMembershipsByRole", parameters, SUserMembership.class, new QueryOptions(startIndex, maxResults)); } public static SelectListDescriptor getUserMembershipsOfUser(final long userId, final int fromIndex, final int numberOfMemberships) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfMemberships, List.of(new OrderByOption(SUserMembership.class, "id", OrderByType.ASC))); return getUserMembershipsOfUser(userId, queryOptions); } public static SelectListDescriptor getUserMembershipsOfUser(final long userId, final QueryOptions queryOptions) { final Map parameters = Collections.singletonMap("userId", userId); return new SelectListDescriptor<>("getUserMembershipsOfUser", parameters, SUserMembership.class, queryOptions); } public static SelectListDescriptor getUserMembershipsOfUser(final long userId, final String field, final OrderByType order, final int fromIndex, final int numberOfMemberships) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfMemberships, SUserMembership.class, field, order); return getUserMembershipsOfUser(userId, queryOptions); } public static SelectListDescriptor getUserMembershipsWithGroup(final QueryOptions queryOptions) { final Map parameters = Collections.emptyMap(); return new SelectListDescriptor<>("getUserMembershipsWithGroup", parameters, SUserMembership.class, queryOptions); } public static SelectListDescriptor getUserMembershipsWithRole(final QueryOptions queryOptions) { final Map parameters = Collections.emptyMap(); return new SelectListDescriptor<>("getUserMembershipsWithRole", parameters, SUserMembership.class, queryOptions); } public static SelectListDescriptor getUsersByGroup(final long groupId, final int fromIndex, final int numberOfUsers) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers); return getUsersByGroup(groupId, queryOptions); } private static SelectListDescriptor getUsersByGroup(final long groupId, final QueryOptions queryOptions) { final Map parameters = new HashMap<>(); parameters.put("groupId", groupId); return new SelectListDescriptor<>("getUsersInGroup", parameters, SUser.class, queryOptions); } public static SelectListDescriptor getActiveUsersByGroup(final long groupId, final int fromIndex, final int numberOfUsers) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers); return getActiveUsersByGroup(groupId, queryOptions); } public static SelectListDescriptor getInactiveUsersByGroup(final long groupId, final int fromIndex, final int numberOfUsers) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers); return getInactiveUsersByGroup(groupId, queryOptions); } private static SelectListDescriptor getInactiveUsersByGroup(final long groupId, final QueryOptions queryOptions) { final Map parameters = new HashMap<>(); parameters.put("groupId", groupId); parameters.put("enabled", false); return new SelectListDescriptor<>("getUsersInGroupWithEnabledParameter", parameters, SUser.class, queryOptions); } private static SelectListDescriptor getActiveUsersByGroup(final long groupId, final QueryOptions queryOptions) { final Map parameters = new HashMap<>(); parameters.put("groupId", groupId); parameters.put("enabled", true); return new SelectListDescriptor<>("getUsersInGroupWithEnabledParameter", parameters, SUser.class, queryOptions); } public static SelectListDescriptor getActiveUsersByGroup(final long groupId, final String field, final OrderByType order, final int fromIndex, final int numberOfUsers) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, field, order); return getActiveUsersByGroup(groupId, queryOptions); } public static SelectListDescriptor getInactiveUsersByGroup(final long groupId, final String field, final OrderByType order, final int fromIndex, final int numberOfUsers) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, field, order); return getInactiveUsersByGroup(groupId, queryOptions); } public static SelectListDescriptor getUsersByGroup(final long groupId, final String field, final OrderByType order, final int fromIndex, final int numberOfUsers) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, field, order); return getUsersByGroup(groupId, queryOptions); } public static SelectListDescriptor getUsersWithManager(final long managerUserId, final QueryOptions queryOptions) { final Map parameters = new HashMap<>(); parameters.put("managerUserId", managerUserId); return new SelectListDescriptor<>("getUsersWithManager", parameters, SUser.class, queryOptions); } public static SelectListDescriptor getActiveUsersWithManager(long managerUserId, QueryOptions queryOptions) { final Map parameters = new HashMap<>(); parameters.put("managerUserId", managerUserId); parameters.put("enabled", true); return new SelectListDescriptor<>("getUsersWithManagerWithEnabledParameter", parameters, SUser.class, queryOptions); } public static SelectListDescriptor getInactiveUsersWithManager(long managerUserId, QueryOptions queryOptions) { final Map parameters = new HashMap<>(); parameters.put("managerUserId", managerUserId); parameters.put("enabled", false); return new SelectListDescriptor<>("getUsersWithManagerWithEnabledParameter", parameters, SUser.class, queryOptions); } public static SelectListDescriptor getUsersByMembership(final long groupId, final long roleId) { return getUsersByMembership(groupId, roleId, null); } public static SelectListDescriptor getUsersByMembership(final long groupId, final long roleId, final int fromIndex, final int numberOfUsers) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, "id", OrderByType.DESC); // FIXME should have "id" here return getUsersByMembership(groupId, roleId, queryOptions); } private static SelectListDescriptor getUsersByMembership(final long groupId, final long roleId, final QueryOptions queryOptions) { final Map parameters = new HashMap<>(); parameters.put("roleId", roleId); parameters.put("groupId", groupId); return new SelectListDescriptor<>("getUsersByMembership", parameters, SUser.class, queryOptions); } public static SelectListDescriptor getUsersByMembership(final long groupId, final long roleId, final String field, final OrderByType order, final int fromIndex, final int numberOfUsers) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, field, order); return getUsersByMembership(groupId, roleId, queryOptions); } public static SelectListDescriptor getUsersWithRole(final long roleId) { return getUsersWithRole(roleId, null); } public static SelectListDescriptor getUsersWithRole(final long roleId, final int fromIndex, final int numberOfUsers) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers); return getUsersWithRole(roleId, queryOptions); } public static SelectListDescriptor getActiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers); return getActiveUsersWithRole(roleId, queryOptions); } public static SelectListDescriptor getInactiveUsersWithRole(long roleId, int fromIndex, int numberOfUsers) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers); return getInactiveUsersWithRole(roleId, queryOptions); } public static SelectListDescriptor getUsersWithRole(final long roleId, final QueryOptions queryOptions) { final Map parameters = Collections.singletonMap("roleId", roleId); return new SelectListDescriptor<>("getUsersWithRole", parameters, SUser.class, queryOptions); } public static SelectListDescriptor getActiveUsersWithRole(long roleId, final QueryOptions queryOptions) { final Map parameters = new HashMap<>(); parameters.put("roleId", roleId); parameters.put("enabled", true); return new SelectListDescriptor<>("getUsersWithRoleWithEnabledParameter", parameters, SUser.class, queryOptions); } public static SelectListDescriptor getInactiveUsersWithRole(long roleId, final QueryOptions queryOptions) { final Map parameters = new HashMap<>(); parameters.put("roleId", roleId); parameters.put("enabled", false); return new SelectListDescriptor<>("getUsersWithRoleWithEnabledParameter", parameters, SUser.class, queryOptions); } public static SelectListDescriptor getUsersWithRole(final long roleId, final String field, final OrderByType order, final int fromIndex, final int numberOfUsers) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, field, order); return getUsersWithRole(roleId, queryOptions); } public static SelectListDescriptor getActiveUsersWithRole(final long roleId, final String field, final OrderByType order, final int fromIndex, final int numberOfUsers) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, field, order); return getActiveUsersWithRole(roleId, queryOptions); } public static SelectListDescriptor getInactiveUsersWithRole(final long roleId, final String field, final OrderByType order, final int fromIndex, final int numberOfUsers) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfUsers, SUser.class, field, order); return getInactiveUsersWithRole(roleId, queryOptions); } } ================================================ FILE: services/bonita-identity/src/main/java/org/bonitasoft/engine/identity/recorder/UserRecordType.java ================================================ /** * 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.identity.recorder; /** * List all possible change of elements of the identity service * * @author Baptiste Mesta * @author Matthieu Chaffotte */ public enum UserRecordType { DELETE_USER, ADD_USER, UPDATE_USER_PASSWORD, ADD_PROFILE_METADATA_DEFINITION, ADD_PROFILE_METADATA_VALUE, DELETE_PROFILE_METADATA_DEFINITION, DELETE_PROFILE_METADATA_VALUE, DELETE_ROLE, ADD_GROUP, ADD_ROLE, DELETE_GROUP, DELETE_USER_MEMBERSHIP, setUsersMemberships, UPDATE_USER, UPDATE_ROLE, UPDATE_GROUP, ADD_USER_MEMBERSHIP, UPDTAE_PROFILE_METADATA_DEFINITION, UPDATE_PROFILE_METADATA_VALUE, UPDATE_USER_MEMBERSHIP; } ================================================ FILE: services/bonita-identity/src/main/resources/org/bonitasoft/engine/identity/model/impl/hibernate/identity.queries.hbm.xml ================================================ SELECT user FROM org.bonitasoft.engine.identity.model.SUser AS user WHERE user.userName = :userName SELECT user FROM org.bonitasoft.engine.identity.model.SUser as user ORDER BY user.userName SELECT user FROM org.bonitasoft.engine.identity.model.SUser as user ORDER BY user.firstName ASC SELECT user FROM org.bonitasoft.engine.identity.model.SUser as user ORDER BY user.lastName ASC SELECT user FROM org.bonitasoft.engine.identity.model.SUser as user ORDER BY user.userName ASC SELECT user FROM org.bonitasoft.engine.identity.model.SUser as user ORDER BY user.firstName DESC SELECT user FROM org.bonitasoft.engine.identity.model.SUser as user ORDER BY user.lastName DESC SELECT user FROM org.bonitasoft.engine.identity.model.SUser as user ORDER BY user.userName DESC SELECT role FROM org.bonitasoft.engine.identity.model.SRole AS role WHERE role.name = :name SELECT group_ FROM org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE group_.name = :name AND group_.parentPath IS NULL SELECT group_ FROM org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE group_.name = :name AND group_.parentPath = :parentPath SELECT custom_usr_inf_def FROM org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition AS custom_usr_inf_def WHERE custom_usr_inf_def.name = :name SELECT user FROM org.bonitasoft.engine.identity.model.SUser AS user WHERE user.id IN (:ids) ORDER BY user.userName SELECT user FROM org.bonitasoft.engine.identity.model.SUser AS user WHERE user.userName IN (:userNames) SELECT user FROM org.bonitasoft.engine.identity.model.SUser AS user SELECT DISTINCT user FROM org.bonitasoft.engine.identity.model.SUser AS user, org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user.id = user_membership.userId AND user_membership.roleId = :roleId SELECT DISTINCT user FROM org.bonitasoft.engine.identity.model.SUser AS user, org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user.id = user_membership.userId AND user.enabled = :enabled AND user_membership.roleId = :roleId SELECT DISTINCT user FROM org.bonitasoft.engine.identity.model.SUser AS user, org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user_membership.groupId = :groupId AND user.id = user_membership.userId SELECT DISTINCT user FROM org.bonitasoft.engine.identity.model.SUser AS user, org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user_membership.groupId = :groupId AND user.enabled = :enabled AND user.id = user_membership.userId SELECT DISTINCT user FROM org.bonitasoft.engine.identity.model.SUser AS user, org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user_membership.groupId = :groupId AND user_membership.roleId = :roleId AND user.id = user_membership.userId SELECT user FROM org.bonitasoft.engine.identity.model.SUser as user WHERE user.managerUserId = :managerUserId SELECT user FROM org.bonitasoft.engine.identity.model.SUser as user WHERE user.managerUserId = :managerUserId AND user.enabled = :enabled SELECT contactinfo FROM org.bonitasoft.engine.identity.model.SContactInfo as contactinfo WHERE userId = :userId AND personal = :personal SELECT role FROM org.bonitasoft.engine.identity.model.SRole AS role SELECT role FROM org.bonitasoft.engine.identity.model.SRole AS role WHERE role.id IN (:ids) ORDER BY role.name SELECT group_ FROM org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE group_.id IN (:ids) ORDER BY group_.name SELECT group_ FROM org.bonitasoft.engine.identity.model.SGroup AS group_ SELECT group_ FROM org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE group_.parentPath = :groupPath SELECT new org.bonitasoft.engine.identity.model.SUserMembership( user_membership.id, user_membership.roleId, user_membership.groupId, user_membership.userId, user_membership.assignedBy, user_membership.assignedDate, group_.parentPath, role.name, group_.name, user.userName ) FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership, org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.identity.model.SGroup AS group_, org.bonitasoft.engine.identity.model.SUser AS user WHERE user_membership.roleId = role.id AND user_membership.groupId = group_.id AND user_membership.userId = user.id AND user_membership.groupId = :groupId SELECT new org.bonitasoft.engine.identity.model.SUserMembership( user_membership.id, user_membership.roleId, user_membership.groupId, user_membership.userId, user_membership.assignedBy, user_membership.assignedDate, group_.parentPath, role.name, group_.name, user.userName ) FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership, org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.identity.model.SGroup AS group_, org.bonitasoft.engine.identity.model.SUser AS user WHERE user_membership.roleId = role.id AND user_membership.groupId = group_.id AND user_membership.userId = user.id AND user_membership.roleId = :roleId SELECT new org.bonitasoft.engine.identity.model.SUserMembership( user_membership.id, user_membership.roleId, user_membership.groupId, user_membership.userId, user_membership.assignedBy, user_membership.assignedDate, group_.parentPath, role.name, group_.name, user.userName ) FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership, org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.identity.model.SGroup AS group_, org.bonitasoft.engine.identity.model.SUser AS user WHERE user_membership.roleId = role.id AND user_membership.groupId = group_.id AND user_membership.userId = user.id AND user_membership.userId = :userId SELECT new org.bonitasoft.engine.identity.model.SUserMembership( user_membership.id, user_membership.roleId, user_membership.groupId, user_membership.userId, user_membership.assignedBy, user_membership.assignedDate, group_.parentPath, role.name, group_.name, user.userName ) FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership, org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.identity.model.SGroup AS group_, org.bonitasoft.engine.identity.model.SUser AS user WHERE user_membership.roleId = role.id AND user_membership.groupId = group_.id AND user_membership.userId = user.id AND user_membership.roleId = :roleId AND user_membership.groupId = :groupId AND user_membership.userId = :userId SELECT user_membership FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user_membership.roleId = :roleId AND user_membership.groupId = :groupId AND user_membership.userId = :userId SELECT new org.bonitasoft.engine.identity.model.SUserMembership( user_membership.id, user_membership.roleId, user_membership.groupId, user_membership.userId, user_membership.assignedBy, user_membership.assignedDate, group_.parentPath, role.name, group_.name, user.userName ) FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership, org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.identity.model.SGroup AS group_, org.bonitasoft.engine.identity.model.SUser AS user WHERE user_membership.roleId = role.id AND user_membership.groupId = group_.id AND user_membership.userId = user.id SELECT user_membership FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership SELECT new org.bonitasoft.engine.identity.model.SUserMembership( user_membership.id, user_membership.roleId, user_membership.groupId, user_membership.userId, user_membership.assignedBy, user_membership.assignedDate, group_.parentPath, role.name, group_.name, user.userName ) FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership, org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.identity.model.SGroup AS group_, org.bonitasoft.engine.identity.model.SUser AS user WHERE user_membership.roleId = role.id AND user_membership.groupId = group_.id AND user_membership.userId = user.id AND user_membership.id = :id SELECT user_membership FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user_membership.id = :id SELECT custom_usr_inf_def FROM org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition AS custom_usr_inf_def WHERE custom_usr_inf_def.id = :id SELECT custom_usr_inf_def FROM org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition AS custom_usr_inf_def ORDER BY name SELECT COUNT(user.id) FROM org.bonitasoft.engine.identity.model.SUser AS user SELECT COUNT(user_membership.id) FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user_membership.groupId = :groupId SELECT COUNT(user_membership.id) FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user_membership.roleId = :roleId SELECT COUNT(user_membership.id) FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership SELECT COUNT(user_membership.id) FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user_membership.userId = :userId SELECT COUNT(user_membership.id) FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user_membership.roleId = :roleId AND user_membership.groupId = :groupId SELECT COUNT(role.id) FROM org.bonitasoft.engine.identity.model.SRole AS role SELECT COUNT(group_.id) FROM org.bonitasoft.engine.identity.model.SGroup AS group_ SELECT COUNT(group_.id) FROM org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE group_.parentPath = :parentPath SELECT COUNT(custom_usr_inf_def.id) FROM org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition AS custom_usr_inf_def SELECT COUNT(custom_usr_inf_val.id) FROM org.bonitasoft.engine.identity.model.SCustomUserInfoValue AS custom_usr_inf_val SELECT user FROM org.bonitasoft.engine.identity.model.SUser AS user SELECT COUNT(DISTINCT user.id) FROM org.bonitasoft.engine.identity.model.SUser AS user, org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user.id = user_membership.userId SELECT DISTINCT user FROM org.bonitasoft.engine.identity.model.SUser AS user, org.bonitasoft.engine.identity.model.SUserMembership AS user_membership WHERE user.id = user_membership.userId SELECT group_ FROM org.bonitasoft.engine.identity.model.SGroup AS group_ SELECT custom_usr_inf_def FROM org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition AS custom_usr_inf_def SELECT custom_usr_inf_val FROM org.bonitasoft.engine.identity.model.SCustomUserInfoValue AS custom_usr_inf_val SELECT custom_usr_inf_val FROM org.bonitasoft.engine.identity.model.SCustomUserInfoValue AS custom_usr_inf_val WHERE custom_usr_inf_val.userId = :userId AND custom_usr_inf_val.definitionId IN (:definitionIds) SELECT role FROM org.bonitasoft.engine.identity.model.SRole AS role SELECT new org.bonitasoft.engine.identity.model.SUserMembership( user_membership.id, user_membership.roleId, user_membership.groupId, user_membership.userId, user_membership.assignedBy, user_membership.assignedDate, group_.parentPath, role.name, group_.name, user.userName ) FROM org.bonitasoft.engine.identity.model.SUserMembership AS user_membership, org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.identity.model.SGroup AS group_, org.bonitasoft.engine.identity.model.SUser AS user WHERE user_membership.roleId = role.id AND user_membership.groupId = group_.id AND user_membership.userId = user.id SELECT user.id FROM org.bonitasoft.engine.identity.model.SUser AS user, org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition AS infoDef, org.bonitasoft.engine.identity.model.SCustomUserInfoValue AS infoValue WHERE infoValue.userId = user.id AND infoValue.definitionId = infoDef.id AND infoDef.name = :userInfoName AND infoValue.value = :userInfoValue ORDER BY user.id SELECT user.id FROM org.bonitasoft.engine.identity.model.SUser AS user, org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition AS infoDef, org.bonitasoft.engine.identity.model.SCustomUserInfoValue AS infoValue WHERE infoValue.userId = user.id AND infoValue.definitionId = infoDef.id AND infoDef.name = :userInfoName AND infoValue.value like :userInfoValue ORDER BY user.id ================================================ FILE: services/bonita-identity/src/test/java/org/bonitasoft/engine/identity/IconServiceImplTest.java ================================================ /** * Copyright (C) 2021 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.identity; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import java.util.Optional; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.Record; import org.bonitasoft.engine.services.PersistenceService; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class IconServiceImplTest { @Mock Recorder recorder; @Mock PersistenceService persistenceService; @Captor ArgumentCaptor record; @InjectMocks IconServiceImpl iconService; @Test public void should_create_icon_with_specific_mimetype() throws Exception { iconService.createIcon("myAvatar12.png", new byte[] { 1, 2, 3 }); //the file name is not kept, do not verify it verify(recorder).recordInsert(((InsertRecord) record.capture()), eq("ICON")); assertThat(((SIcon) record.getValue().getEntity())).satisfies( entity -> { assertThat(entity.getMimeType()).isEqualTo("image/png"); assertThat(entity.getContent()).isEqualTo(new byte[] { 1, 2, 3 }); }); } @Test public void should_replace_existing_icon() throws Exception { SIcon previousIcon = new SIcon(42L, "image/jpeg", new byte[] { 1, 2, 3 }); doReturn(previousIcon).when(persistenceService) .selectById(new SelectByIdDescriptor<>(SIcon.class, 42L)); Optional newIconId = iconService.replaceIcon("myAvatar12.png", "contents".getBytes(), 42L); verify(recorder).recordInsert(((InsertRecord) record.capture()), eq("ICON")); assertThat(((SIcon) record.getValue().getEntity())).satisfies( entity -> { assertThat(entity.getMimeType()).isEqualTo("image/png"); assertThat(entity.getContent()).isEqualTo("contents".getBytes()); }); verify(recorder).recordDelete(argThat(r -> r.getEntity().equals(previousIcon)), eq("ICON")); assertThat(newIconId).get().isEqualTo(0L);//id is set by reflection by the persistence service } @Test public void should_replace_non_existing_icon() throws Exception { Optional newIconId = iconService.replaceIcon("myAvatar12.png", "contents".getBytes(), null); verify(recorder).recordInsert((any()), eq("ICON")); verifyNoMoreInteractions(recorder); assertThat(newIconId).get().isEqualTo(0L);//id is set by reflection by the persistence service } @Test public void should_remove_existing_icon() throws Exception { SIcon previousIcon = new SIcon(42L, "image/jpeg", new byte[] { 1, 2, 3 }); doReturn(previousIcon).when(persistenceService) .selectById(new SelectByIdDescriptor<>(SIcon.class, 42L)); Optional newIconId = iconService.replaceIcon(null, null, 42L); verify(recorder).recordDelete(argThat(r -> r.getEntity().equals(previousIcon)), eq("ICON")); verifyNoMoreInteractions(recorder); assertThat(newIconId).isNotPresent(); } @Test public void should_delete_icon_when_it_exists() throws Exception { doReturn(new SIcon(42L, "image/jpeg", new byte[] { 1, 2, 3 })).when(persistenceService) .selectById(new SelectByIdDescriptor<>(SIcon.class, 42L)); iconService.deleteIcon(42L); verify(recorder).recordDelete(argThat(r -> r.getEntity().getId() == 42L), eq("ICON")); } @Test public void should_not_delete_icon_when_it_does_not_exists() throws Exception { iconService.deleteIcon(42L); verifyNoInteractions(recorder); } } ================================================ FILE: services/bonita-identity/src/test/java/org/bonitasoft/engine/identity/impl/IdentityServiceImplForCustomUserInfoTest.java ================================================ /** * 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.identity.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.identity.IdentityService; import org.bonitasoft.engine.identity.SCustomUserInfoDefinitionAlreadyExistsException; import org.bonitasoft.engine.identity.SCustomUserInfoDefinitionCreationException; import org.bonitasoft.engine.identity.SCustomUserInfoDefinitionNotFoundException; import org.bonitasoft.engine.identity.SCustomUserInfoDefinitionReadException; import org.bonitasoft.engine.identity.SCustomUserInfoValueNotFoundException; import org.bonitasoft.engine.identity.SCustomUserInfoValueReadException; import org.bonitasoft.engine.identity.SIdentityException; import org.bonitasoft.engine.identity.model.SCustomUserInfoDefinition; import org.bonitasoft.engine.identity.model.SCustomUserInfoValue; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class IdentityServiceImplForCustomUserInfoTest { private static final long CUSTOM_USER_INFO_DEFINITION_ID = 10L; @Mock private ReadPersistenceService persistenceService; @Mock private Recorder recorder; @Mock private QueriableLoggerService queriableLoggerService; @Mock private EventService eventService; @Mock private CredentialsEncrypter encrypter; @Mock private SCustomUserInfoValue userInfoValue; @Mock private SCustomUserInfoDefinition userInfoDef; @InjectMocks private IdentityServiceImpl identityServiceImpl; private static String DEFAULT_NAME = "skill"; @Before public void setUp() { when(userInfoDef.getId()).thenReturn(CUSTOM_USER_INFO_DEFINITION_ID); when(userInfoDef.getName()).thenReturn(DEFAULT_NAME); } @Test public void createCustomUserInfoDefinition_should_call_recorder_insert() throws Exception { // when identityServiceImpl.createCustomUserInfoDefinition(userInfoDef); // then verify(recorder, times(1)).recordInsert(eq(new InsertRecord(userInfoDef)), eq(IdentityService.CUSTOM_USER_INFO_DEFINITION)); } @Test public void createCustomUserInfoDefinition_should_throw_SCustomUserInfoDefinitionCreationException_when_recorder_throws_SRecorderException() throws Exception { // given final SRecorderException recorderException = new SRecorderException(""); doThrow(recorderException).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); try { // when identityServiceImpl.createCustomUserInfoDefinition(userInfoDef); fail("Creation exception expected"); } catch (final SCustomUserInfoDefinitionCreationException e) { // then assertThat(e.getDefinitionName()).isEqualTo(DEFAULT_NAME); assertThat(e.getCause()).isEqualTo(recorderException); } } @Test public void createCustomUserInfoDefinition_should_throw_SCustomUserInfoDefinitionAlreadyExistsException_when_persistence_service_returns_result_for_check() throws Exception { // given final SCustomUserInfoDefinition duplicateDef = mock(SCustomUserInfoDefinition.class); given(duplicateDef.getName()).willReturn(DEFAULT_NAME); given(persistenceService.selectOne(SelectDescriptorBuilder.getCustomUserInfoDefinitionByName(DEFAULT_NAME))) .willReturn(userInfoDef); try { // when identityServiceImpl.createCustomUserInfoDefinition(duplicateDef); fail("Already exists exception found"); } catch (final SCustomUserInfoDefinitionAlreadyExistsException e) { // then assertThat(e.getDefinitionName()).isEqualTo(DEFAULT_NAME); verify(recorder, never()).recordInsert(any(InsertRecord.class), anyString()); } } @Test public void getCustomUserInfoDefinitionByName_should_return_result_of_persistence_service() throws Exception { // given given(persistenceService.selectOne(SelectDescriptorBuilder.getCustomUserInfoDefinitionByName(DEFAULT_NAME))) .willReturn(userInfoDef); // when final SCustomUserInfoDefinition retrievedDef = identityServiceImpl .getCustomUserInfoDefinitionByName(DEFAULT_NAME); // then assertThat(retrievedDef).isEqualTo(userInfoDef); } @Test public void getCustomUserInfoDefinitionByName_should_throw_SCustomUserInfoDefinitionNotFoundException_when_persistence_service_returns_null() throws Exception { // given given(persistenceService.selectOne(SelectDescriptorBuilder.getCustomUserInfoDefinitionByName(DEFAULT_NAME))) .willReturn(null); try { // when identityServiceImpl.getCustomUserInfoDefinitionByName(DEFAULT_NAME); fail("Not found exception expected"); } catch (final SCustomUserInfoDefinitionNotFoundException e) { // then assertThat(e.getDefinitionName()).isEqualTo(DEFAULT_NAME); } } @Test public void getCustomUserInfoDefinitionByName_should_throw_SCustomUserInfoDefinitionReadException_when_persistence_service_throws_SBonitaReadException() throws Exception { // given final SBonitaReadException persistenceException = new SBonitaReadException(""); given(persistenceService.selectOne(SelectDescriptorBuilder.getCustomUserInfoDefinitionByName(DEFAULT_NAME))) .willThrow(persistenceException); try { // when identityServiceImpl.getCustomUserInfoDefinitionByName(DEFAULT_NAME); fail("Read exception expected"); } catch (final SCustomUserInfoDefinitionReadException e) { // then assertThat(e.getDefinitionName()).isEqualTo(DEFAULT_NAME); assertThat(e.getCause()).isEqualTo(persistenceException); } } @Test public void hasCustomUserInfoDefinition_should_return_true_if_persistence_service_finds_an_element() throws Exception { // given given(persistenceService.selectOne(SelectDescriptorBuilder.getCustomUserInfoDefinitionByName(DEFAULT_NAME))) .willReturn(userInfoDef); // when final boolean hasDef = identityServiceImpl.hasCustomUserInfoDefinition(DEFAULT_NAME); // then assertThat(hasDef).isTrue(); } @Test public void hasCustomUserInfoDefinition_should_throw_SCustomUserInfoDefinitionReadException_when_persistence_service_throws_SBonitaReadException() throws Exception { // given final SBonitaReadException persistenceException = new SBonitaReadException(""); given(persistenceService.selectOne(SelectDescriptorBuilder.getCustomUserInfoDefinitionByName(DEFAULT_NAME))) .willThrow(persistenceException); try { // when identityServiceImpl.hasCustomUserInfoDefinition(DEFAULT_NAME); fail("Read exception expected"); } catch (final SCustomUserInfoDefinitionReadException e) { // then assertThat(e.getDefinitionName()).isEqualTo(DEFAULT_NAME); assertThat(e.getCause()).isEqualTo(persistenceException); } } @Test public void searchCustomUserInfoValues_should_return_values_from_persistence_service() throws Exception { // given final QueryOptions queryOptions = new QueryOptions(0, 10); final List persistenceResult = Arrays.asList(userInfoValue); given(persistenceService.searchEntity(SCustomUserInfoValue.class, queryOptions, null)) .willReturn(persistenceResult); // when final List userInfoValues = identityServiceImpl.searchCustomUserInfoValue(queryOptions); // then assertThat(userInfoValues).isEqualTo(persistenceResult); } @Test( // then expected = SBonitaReadException.class) public void searchCustomUserInfoValues_should_throw_exception_SBonitaReadException_when_persistence_service_throws_SBonitaReadException() throws Exception { // given final QueryOptions queryOptions = new QueryOptions(0, 10); given(persistenceService.searchEntity(SCustomUserInfoValue.class, queryOptions, null)) .willThrow(new SBonitaReadException("")); // when identityServiceImpl.searchCustomUserInfoValue(queryOptions); } @Test public void createCustomUserInfoValue_should_call_recorder_insert() throws Exception { // given final ArgumentCaptor recordCaptor = ArgumentCaptor.forClass(InsertRecord.class); // when identityServiceImpl.createCustomUserInfoValue(userInfoValue); // then verify(recorder, times(1)).recordInsert(recordCaptor.capture(), eq(IdentityService.CUSTOM_USER_INFO_VALUE)); assertThat(recordCaptor.getValue().getEntity()).isEqualTo(userInfoValue); } @Test(expected = SIdentityException.class) public void createCustomUserInfoValue_should_throw_SIdentityException_when_recorder_throws_SRecorderException() throws Exception { // given doThrow(SRecorderException.class).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); // when identityServiceImpl.createCustomUserInfoValue(userInfoValue); } @Test public void deleteCustomUserInfoValue_should_call_recorder_delete() throws Exception { // given final ArgumentCaptor recordCaptor = ArgumentCaptor.forClass(DeleteRecord.class); // when identityServiceImpl.deleteCustomUserInfoValue(userInfoValue); // then verify(recorder, times(1)).recordDelete(recordCaptor.capture(), eq(IdentityService.CUSTOM_USER_INFO_VALUE)); assertThat(recordCaptor.getValue().getEntity()).isEqualTo(userInfoValue); } @Test(expected = SIdentityException.class) public void deleteCustomUserInfoValue_should_throw_SIdentityException_when_recorder_throws_SRecorderException() throws Exception { // given doThrow(SRecorderException.class).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); // when identityServiceImpl.deleteCustomUserInfoValue(userInfoValue); } @Test public void getCustomUserInfoValue_shoul_return_value_returned_by_persistence_service() throws Exception { final long id = 10; // given final SelectByIdDescriptor selector = SelectDescriptorBuilder.getElementById( SCustomUserInfoValue.class, "SCustomUserInfoValue", id); given(persistenceService.selectById(selector)).willReturn(userInfoValue); // when final SCustomUserInfoValue retriveidUserInfo = identityServiceImpl.getCustomUserInfoValue(id); // then assertThat(retriveidUserInfo).isEqualTo(userInfoValue); } @Test(expected = SCustomUserInfoValueNotFoundException.class) public void getCustomUserInfoValue_shoul_throw_SCustomUserInfoValueNotFoundException_when_persistence_service_returns_null() throws Exception { final long id = 10; // given final SelectByIdDescriptor selector = SelectDescriptorBuilder.getElementById( SCustomUserInfoValue.class, "SCustomUserInfoValue", id); given(persistenceService.selectById(selector)).willReturn(null); // when identityServiceImpl.getCustomUserInfoValue(id); } @Test(expected = SCustomUserInfoValueReadException.class) public void getCustomUserInfoValue_shoul_throw_SCustomUserInfoValueReadException_when_persistence_service_throws_SBonitaReadException() throws Exception { final long id = 10; // given final SelectByIdDescriptor selector = SelectDescriptorBuilder.getElementById( SCustomUserInfoValue.class, "SCustomUserInfoValue", id); given(persistenceService.selectById(selector)).willThrow(new SBonitaReadException("")); // when identityServiceImpl.getCustomUserInfoValue(id); } @Test public void updateCustomUserInfoValue_should_call_recorder_update() throws Exception { // given final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); final ArgumentCaptor recordCaptor = ArgumentCaptor.forClass(UpdateRecord.class); // when identityServiceImpl.updateCustomUserInfoValue(userInfoValue, descriptor); // then verify(recorder, times(1)).recordUpdate(recordCaptor.capture(), eq(IdentityService.CUSTOM_USER_INFO_VALUE)); assertThat(recordCaptor.getValue().getEntity()).isEqualTo(userInfoValue); } @Test public void getUserIdsWithCustomUserInfo_should_use_query_getUserIdsWithCustomUserInfo_when_no_partial_match() throws Exception { //given final Map parameters = new HashMap(2); parameters.put("userInfoName", DEFAULT_NAME); parameters.put("userInfoValue", "Java"); final SelectListDescriptor descriptor = new SelectListDescriptor("getUserIdsWithCustomUserInfo", parameters, SUser.class, Long.class, new QueryOptions(0, 10)); given(persistenceService.selectList(descriptor)).willReturn(Arrays.asList(10L, 20L)); //when final List userIds = identityServiceImpl.getUserIdsWithCustomUserInfo(DEFAULT_NAME, "Java", false, 0, 10); //then assertThat(userIds).containsExactly(10L, 20L); } @Test public void getUserIdsWithCustomUserInfo_should_use_query_getUserIdsWithCustomUserInfoContains_when_partial_match() throws Exception { //given final Map parameters = new HashMap(2); parameters.put("userInfoName", DEFAULT_NAME); parameters.put("userInfoValue", "Java"); final SelectListDescriptor descriptor = new SelectListDescriptor( "getUserIdsWithCustomUserInfoContains", parameters, SUser.class, Long.class, new QueryOptions(0, 10)); given(persistenceService.selectList(descriptor)).willReturn(Arrays.asList(10L, 20L)); //when final List userIds = identityServiceImpl.getUserIdsWithCustomUserInfo(DEFAULT_NAME, "Java", true, 0, 10); //then assertThat(userIds).containsExactly(10L, 20L); } @Test(expected = SIdentityException.class) //then public void getUserIdsWithCustomUserInfo_should_throw_SIdentityException_when_persistence_service_throws_exception() throws Exception { //given given(persistenceService.selectList(ArgumentMatchers.> any())) .willThrow(new SBonitaReadException("")); //when identityServiceImpl.getUserIdsWithCustomUserInfo(DEFAULT_NAME, "Java", false, 0, 10); } } ================================================ FILE: services/bonita-identity/src/test/java/org/bonitasoft/engine/identity/impl/IdentityServiceImplForGroupTest.java ================================================ /** * 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.identity.impl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.Collections; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.identity.SGroupNotFoundException; import org.bonitasoft.engine.identity.SIdentityException; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class IdentityServiceImplForGroupTest { @Mock private Recorder recorder; @Mock private ReadPersistenceService persistenceService; @Mock private EventService eventService; @InjectMocks private IdentityServiceImpl identityServiceImpl; @Before public void setUp() { MockitoAnnotations.initMocks(this); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfGroupChildren(long)}. */ @Test public void getNumberOfGroupChildren() throws Exception { final SGroup group = mock(SGroup.class); when(group.getParentPath()).thenReturn("/thePath"); when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, "Group", 123l))) .thenReturn(group); when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfGroupChildren("/thePath"))) .thenReturn(12l); assertEquals(12l, identityServiceImpl.getNumberOfGroupChildren(123l)); } @Test(expected = SIdentityException.class) public void getNumberOfGroupChildrenThrowException() throws Exception { final SGroup group = mock(SGroup.class); when(group.getParentPath()).thenReturn("/thePath"); when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, "Group", 123l))) .thenReturn(group); when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfGroupChildren("/thePath"))) .thenThrow(new SBonitaReadException("")); identityServiceImpl.getNumberOfGroupChildren(123l); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getGroupChildren(long, int, int)}. */ @Test public void getGroupChildrenPaginatedById() throws Exception { final SGroup group = mock(SGroup.class); final SGroup child = mock(SGroup.class); when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, "Group", 123l))) .thenReturn(group); when(persistenceService.selectList(SelectDescriptorBuilder.getChildrenOfGroup(group, 0, 10))) .thenReturn(Collections.singletonList(child)); assertEquals(child, identityServiceImpl.getGroupChildren(123l, 0, 10).get(0)); } @Test(expected = SIdentityException.class) public void getGroupChildrenPaginatedByIdThrowException() throws Exception { final SGroup group = mock(SGroup.class); when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, "Group", 123l))) .thenReturn(group); when(persistenceService.selectList(SelectDescriptorBuilder.getChildrenOfGroup(group, 0, 10))) .thenThrow(new SBonitaReadException("")); identityServiceImpl.getGroupChildren(123l, 0, 10); } /** * Test method for * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getGroupChildren(long, int, int, java.lang.String, org.bonitasoft.engine.persistence.OrderByType)} * . */ @Test public void getGroupChildrenPaginatedByIdWithOrder() throws Exception { final SGroup group = mock(SGroup.class); final SGroup child = mock(SGroup.class); when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, "Group", 123l))) .thenReturn(group); when(persistenceService .selectList(SelectDescriptorBuilder.getChildrenOfGroup(group, "name", OrderByType.ASC, 0, 10))) .thenReturn( Collections.singletonList(child)); assertEquals(child, identityServiceImpl.getGroupChildren(123l, 0, 10, "name", OrderByType.ASC).get(0)); } @Test(expected = SIdentityException.class) public void getGroupChildrenPaginatedByIdWithOrderThrowException() throws Exception { final SGroup group = mock(SGroup.class); when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, "Group", 123l))) .thenReturn(group); when(persistenceService .selectList(SelectDescriptorBuilder.getChildrenOfGroup(group, "name", OrderByType.ASC, 0, 10))) .thenThrow( new SBonitaReadException("")); identityServiceImpl.getGroupChildren(123l, 0, 10, "name", OrderByType.ASC); } /** * Test method for * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfGroups(org.bonitasoft.engine.persistence.QueryOptions)}. */ @Test public void getNumberOfGroupsWithOptions() throws Exception { final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.getNumberOfEntities(SGroup.class, options, null)).thenReturn(125l); assertEquals(125, identityServiceImpl.getNumberOfGroups(options)); } @Test(expected = SBonitaReadException.class) public void getNumberOfGroupsWithOptionsThrowException() throws Exception { final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.getNumberOfEntities(SGroup.class, options, null)) .thenThrow(new SBonitaReadException("")); identityServiceImpl.getNumberOfGroups(options); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfGroups()}. */ @Test public void getNumberOfGroups() throws Exception { when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfElement("SGroup", SGroup.class))) .thenReturn(125l); assertEquals(125, identityServiceImpl.getNumberOfGroups()); } @Test(expected = SIdentityException.class) public void getNumberOfGroupsThrowException() throws Exception { when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfElement("SGroup", SGroup.class))) .thenThrow(new SBonitaReadException("")); identityServiceImpl.getNumberOfGroups(); } /** * Test method for * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#searchGroups(org.bonitasoft.engine.persistence.QueryOptions)}. */ @Test public void searchGroups() throws Exception { final QueryOptions options = new QueryOptions(0, 10); final SGroup group = mock(SGroup.class); when(persistenceService.searchEntity(SGroup.class, options, null)).thenReturn(Collections.singletonList(group)); assertEquals(group, identityServiceImpl.searchGroups(options).get(0)); } @Test(expected = SBonitaReadException.class) public void searchGroupsThrowException() throws Exception { final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.searchEntity(SGroup.class, options, null)).thenThrow(new SBonitaReadException("")); identityServiceImpl.searchGroups(options); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getGroup(long)}. */ @Test public void getGroup() throws Exception { final SGroup group = mock(SGroup.class); when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, "Group", 123l))) .thenReturn(group); assertEquals(group, identityServiceImpl.getGroup(123l)); } @Test(expected = SIdentityException.class) public void getGroupThrowException() throws Exception { when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SGroup.class, "Group", 123l))) .thenThrow(new SBonitaReadException("")); identityServiceImpl.getGroup(123l); } @Test(expected = SGroupNotFoundException.class) public void getGroupNotExist() throws Exception { final SGroup group = mock(SGroup.class); assertEquals(group, identityServiceImpl.getGroup(123l)); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getGroupByPath(java.lang.String)}. */ @Test public void getGroupByPath() throws Exception { final SGroup group = mock(SGroup.class); when(persistenceService.selectOne(SelectDescriptorBuilder.getGroupByName("path"))).thenReturn(group); assertEquals(group, identityServiceImpl.getGroupByPath("/path")); } @Test(expected = SGroupNotFoundException.class) public void getGroupByPathNotExist() throws Exception { final SGroup group = mock(SGroup.class); assertEquals(group, identityServiceImpl.getGroupByPath("/path")); } @Test public void getGroupByPathWithNoSlash() throws Exception { final SGroup group = mock(SGroup.class); when(persistenceService.selectOne(SelectDescriptorBuilder.getGroupByName("path"))).thenReturn(group); assertEquals(group, identityServiceImpl.getGroupByPath("path")); } @Test public void getGroupByPathThatIsNotRoot() throws Exception { final SGroup group = mock(SGroup.class); when(persistenceService.selectOne(SelectDescriptorBuilder.getGroupByPath("/path", "subPath"))) .thenReturn(group); assertEquals(group, identityServiceImpl.getGroupByPath("/path/subPath")); } @Test(expected = SIdentityException.class) public void getGroupByPathThrowException() throws Exception { when(persistenceService.selectOne(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); identityServiceImpl.getGroupByPath("path"); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getGroups(java.util.List)}. */ @Test public final void getGroupsByIds() throws SBonitaReadException, SGroupNotFoundException { final SGroup group = mock(SGroup.class); when(persistenceService .selectList(SelectDescriptorBuilder.getElementsByIds(SGroup.class, "Group", Arrays.asList(123l)))) .thenReturn( Arrays.asList(group)); assertEquals(group, identityServiceImpl.getGroups(Arrays.asList(123l)).get(0)); } @Test public void getGroupsByNullIds() throws Exception { assertTrue(identityServiceImpl.getGroups(null).isEmpty()); } @Test public void getGroupsByEmptyIds() throws Exception { assertTrue(identityServiceImpl.getGroups(Collections. emptyList()).isEmpty()); } @Test(expected = SIdentityException.class) public void getGroupsByIdsThrowException() throws Exception { when(persistenceService .selectList(SelectDescriptorBuilder.getElementsByIds(SGroup.class, "Group", Arrays.asList(123l)))) .thenThrow( new SBonitaReadException("")); identityServiceImpl.getGroups(Arrays.asList(123l)); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getGroups(int, int)}. */ @Test public final void getGroupsPaginated() throws SBonitaReadException, SIdentityException { final SGroup group = mock(SGroup.class); when(persistenceService.selectList(SelectDescriptorBuilder.getElements(SGroup.class, "Group", 0, 10))) .thenReturn(Arrays.asList(group)); assertEquals(group, identityServiceImpl.getGroups(0, 10).get(0)); } @Test(expected = SIdentityException.class) public void getGroupsPaginatedThrowException() throws Exception { when(persistenceService.selectList(SelectDescriptorBuilder.getElements(SGroup.class, "Group", 0, 10))) .thenThrow(new SBonitaReadException("")); identityServiceImpl.getGroups(0, 10); } /** * Test method for * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getGroups(int, int, java.lang.String, org.bonitasoft.engine.persistence.OrderByType)}. */ @Test public void getGroupsPaginatedWithOrder() throws Exception { final SGroup group = mock(SGroup.class); when(persistenceService .selectList(SelectDescriptorBuilder.getElements(SGroup.class, "Group", "name", OrderByType.ASC, 0, 10))) .thenReturn( Arrays.asList(group)); assertEquals(group, identityServiceImpl.getGroups(0, 10, "name", OrderByType.ASC).get(0)); } @Test(expected = SIdentityException.class) public void getGroupsPaginatedWithOrderThrowException() throws Exception { when(persistenceService .selectList(SelectDescriptorBuilder.getElements(SGroup.class, "Group", "name", OrderByType.ASC, 0, 10))) .thenThrow( new SBonitaReadException("")); identityServiceImpl.getGroups(0, 10, "name", OrderByType.ASC); } } ================================================ FILE: services/bonita-identity/src/test/java/org/bonitasoft/engine/identity/impl/IdentityServiceImplForUserMembershipTest.java ================================================ /** * 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.identity.impl; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.identity.SIdentityException; import org.bonitasoft.engine.identity.model.SGroup; import org.bonitasoft.engine.identity.model.SRole; import org.bonitasoft.engine.identity.model.SUserMembership; import org.bonitasoft.engine.identity.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public class IdentityServiceImplForUserMembershipTest { @Mock private Recorder recorder; @Mock private ReadPersistenceService persistenceService; @Mock private EventService eventService; @InjectMocks private IdentityServiceImpl identityServiceImpl; @Before public void setUp() { MockitoAnnotations.initMocks(this); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getLightUserMembership(long)}. */ @Test public final void getLightUserMembershipById() throws SBonitaReadException, SIdentityException { final SUserMembership userMembership = mock(SUserMembership.class); final long userMembershipId = 546L; doReturn(userMembership).when(persistenceService) .selectById(SelectDescriptorBuilder.getLightElementById(SUserMembership.class, "SUserMembership", userMembershipId)); assertEquals(userMembership, identityServiceImpl.getLightUserMembership(userMembershipId)); } @Test(expected = SIdentityException.class) public final void getLightUserMembershipByIdNotExist() throws SBonitaReadException, SIdentityException { final long userMembershipId = 546L; doReturn(null).when(persistenceService) .selectById(SelectDescriptorBuilder.getLightElementById(SUserMembership.class, "SUserMembership", userMembershipId)); identityServiceImpl.getLightUserMembership(userMembershipId); } @Test(expected = SIdentityException.class) public final void getLightUserMembershipByIdThrowException() throws SBonitaReadException, SIdentityException { final long userMembershipId = 546L; doThrow(new SBonitaReadException("")).when(persistenceService) .selectById(SelectDescriptorBuilder.getLightElementById(SUserMembership.class, "SUserMembership", userMembershipId)); identityServiceImpl.getLightUserMembership(userMembershipId); } /** * Test method for * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getLightUserMembership(long, long, long)}. */ @Test public final void getLightUserMembershipByUserAndGroupAndRole() throws SBonitaReadException, SIdentityException { final SUserMembership userMembership = mock(SUserMembership.class); final long userId = 546L; final long groupId = 565L; final long roleId = 54L; doReturn(userMembership).when(persistenceService) .selectOne(SelectDescriptorBuilder.getLightUserMembership(userId, groupId, roleId)); assertEquals(userMembership, identityServiceImpl.getLightUserMembership(userId, groupId, roleId)); } @Test(expected = SIdentityException.class) public final void getLightUserMembershipByUserAndGroupAndRoleNotExist() throws SBonitaReadException, SIdentityException { final long userId = 546L; final long groupId = 565L; final long roleId = 54L; doReturn(null).when(persistenceService) .selectOne(SelectDescriptorBuilder.getLightUserMembership(userId, groupId, roleId)); identityServiceImpl.getLightUserMembership(userId, groupId, roleId); } @Test(expected = SIdentityException.class) public final void getLightUserMembershipByUserAndGroupAndRoleThrowException() throws SBonitaReadException, SIdentityException { final long userId = 546L; final long groupId = 565L; final long roleId = 54L; doThrow(new SBonitaReadException("")).when(persistenceService) .selectOne(SelectDescriptorBuilder.getLightUserMembership(userId, groupId, roleId)); identityServiceImpl.getLightUserMembership(userId, groupId, roleId); } /** * Test method for * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getLightUserMemberships(int, int)}. */ @Test public final void getLightUserMembershipsPaginated() throws SBonitaReadException, SIdentityException { final SUserMembership userMembership = mock(SUserMembership.class); final List userMemberships = Collections.singletonList(userMembership); final int startIndex = 546; final int numberOfElements = 565; doReturn(userMemberships).when(persistenceService).selectList( SelectDescriptorBuilder.getElements(SUserMembership.class, "LightUserMembership", startIndex, numberOfElements)); assertEquals(userMemberships, identityServiceImpl.getLightUserMemberships(startIndex, numberOfElements)); } @Test(expected = SIdentityException.class) public final void getLightUserMembershipsPaginatedThrowException() throws SBonitaReadException, SIdentityException { final int startIndex = 546; final int numberOfElements = 565; doThrow(new SBonitaReadException("")).when(persistenceService).selectList( SelectDescriptorBuilder.getElements(SUserMembership.class, "LightUserMembership", startIndex, numberOfElements)); identityServiceImpl.getLightUserMemberships(startIndex, numberOfElements); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfUserMemberships()}. */ @Test public final void getNumberOfUserMemberships() throws SBonitaReadException, SIdentityException { final long numberOfUserMemberships = 3; doReturn(numberOfUserMemberships).when(persistenceService).selectOne( SelectDescriptorBuilder.getNumberOfElement("UserMembership", SUserMembership.class)); assertEquals(numberOfUserMemberships, identityServiceImpl.getNumberOfUserMemberships()); } @Test(expected = SIdentityException.class) public final void getNumberOfUserMembershipsThrowException() throws SBonitaReadException, SIdentityException { doThrow(new SBonitaReadException("")).when(persistenceService).selectOne( SelectDescriptorBuilder.getNumberOfElement("UserMembership", SUserMembership.class)); identityServiceImpl.getNumberOfUserMemberships(); } /** * Test method for * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfUserMembershipsOfUser(long)}. */ @Test public final void getNumberOfUserMembershipsOfUser() throws SBonitaReadException, SIdentityException { final long numberOfUserMemberships = 3; final long userId = 554L; doReturn(numberOfUserMemberships).when(persistenceService) .selectOne(SelectDescriptorBuilder.getNumberOfUserMembershipsOfUser(userId)); assertEquals(numberOfUserMemberships, identityServiceImpl.getNumberOfUserMembershipsOfUser(userId)); } @Test(expected = SIdentityException.class) public final void getNumberOfUserMembershipsOfUserThrowException() throws SBonitaReadException, SIdentityException { final long userId = 554L; doThrow(new SBonitaReadException("")).when(persistenceService) .selectOne(SelectDescriptorBuilder.getNumberOfUserMembershipsOfUser(userId)); identityServiceImpl.getNumberOfUserMembershipsOfUser(userId); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUserMembership(long)}. */ @Test public final void getUserMembershipById() throws SBonitaReadException, SIdentityException { final SUserMembership userMembership = mock(SUserMembership.class); doReturn(userMembership).when(persistenceService) .selectOne(ArgumentMatchers.> any()); assertEquals(userMembership, identityServiceImpl.getUserMembership(546L)); } @Test(expected = SIdentityException.class) public final void getUserMembershipByIdNotExist() throws SBonitaReadException, SIdentityException { doReturn(null).when(persistenceService) .selectOne(ArgumentMatchers.> any()); identityServiceImpl.getUserMembership(546L); } @Test(expected = SIdentityException.class) public final void getUserMembershipByIdThrowException() throws SBonitaReadException, SIdentityException { doThrow(new SBonitaReadException("")).when(persistenceService) .selectOne(ArgumentMatchers.> any()); identityServiceImpl.getUserMembership(546L); } /** * Test method for * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUserMembership(long, long, long)}. */ @Test public final void getUserMembershipByUserAndGroupAndRole() throws SBonitaReadException, SIdentityException { final SUserMembership userMembership = mock(SUserMembership.class); doReturn(userMembership).when(persistenceService) .selectOne(ArgumentMatchers.> any()); assertEquals(userMembership, identityServiceImpl.getUserMembership(546L, 565L, 54L)); } @Test(expected = SIdentityException.class) public final void getUserMembershipByUserAndGroupAndRoleNotExist() throws SBonitaReadException, SIdentityException { doReturn(null).when(persistenceService) .selectOne(ArgumentMatchers.> any()); identityServiceImpl.getUserMembership(546L, 565L, 54L); } @Test(expected = SIdentityException.class) public final void getUserMembershipByUserAndGroupAndRoleThrowException() throws SBonitaReadException, SIdentityException { doThrow(new SBonitaReadException("")).when(persistenceService) .selectOne(ArgumentMatchers.> any()); identityServiceImpl.getUserMembership(546L, 565L, 54L); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUserMembershipsOfGroup(long)}. */ @Test public void getUserMembershipsOfGroup() throws Exception { final SUserMembership userMembership = mock(SUserMembership.class); when(persistenceService.selectList(SelectDescriptorBuilder.getUserMembershipsByGroup(1l, 0, 20))) .thenReturn(Collections.singletonList(userMembership)); final List userMemberships = identityServiceImpl.getUserMembershipsOfGroup(1l, 0, 20); assertEquals(userMembership, userMemberships.get(0)); } @Test(expected = SIdentityException.class) public void getUserMembershipsOfGroupThrowException() throws Exception { when(persistenceService.selectList(SelectDescriptorBuilder.getUserMembershipsByGroup(1l, 0, 20))) .thenThrow(new SBonitaReadException("")); identityServiceImpl.getUserMembershipsOfGroup(1l, 0, 20); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUserMembershipsOfRole(long)}. */ @Test public void getUserMembershipsOfRole() throws Exception { final SUserMembership userMembership = mock(SUserMembership.class); when(persistenceService.selectList(SelectDescriptorBuilder.getUserMembershipsByRole(1l, 0, 20))) .thenReturn(Collections.singletonList(userMembership)); final List userMemberships = identityServiceImpl.getUserMembershipsOfRole(1l, 0, 20); assertEquals(userMembership, userMemberships.get(0)); } @Test(expected = SIdentityException.class) public void getUserMembershipsOfRoleThrowException() throws Exception { when(persistenceService.selectList(SelectDescriptorBuilder.getUserMembershipsByRole(1l, 0, 20))) .thenThrow(new SBonitaReadException("")); identityServiceImpl.getUserMembershipsOfRole(1l, 0, 20); } /** * Test method for * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUserMemberships(int, int, org.bonitasoft.engine.persistence.OrderByOption)}. */ @Test public void getUserMembershipsPaginatedWithOrder() throws Exception { final SUserMembership userMembership = mock(SUserMembership.class); final OrderByOption orderByOption = new OrderByOption(SUserMembership.class, "username", OrderByType.ASC); doReturn(Collections.singletonList(userMembership)).when(persistenceService) .selectList( SelectDescriptorBuilder.getElements(SUserMembership.class, "UserMembership", new QueryOptions(0, 10, Collections.singletonList(orderByOption)))); final List userMemberships = identityServiceImpl.getUserMemberships(0, 10, orderByOption); assertEquals(userMembership, userMemberships.get(0)); } @Test(expected = SIdentityException.class) public void getUserMembershipsPaginatedWithOrderThrowException() throws Exception { final OrderByOption orderByOption = new OrderByOption(SUserMembership.class, "username", OrderByType.ASC); when( persistenceService .selectList(SelectDescriptorBuilder.getElements(SUserMembership.class, "UserMembership", new QueryOptions(0, 10, Collections.singletonList(orderByOption))))) .thenThrow(new SBonitaReadException("")); identityServiceImpl.getUserMemberships(0, 10, orderByOption); } @Test public void getUserMembershipsOrderByRole() throws Exception { final SUserMembership userMembership = mock(SUserMembership.class); final OrderByOption orderByOption = new OrderByOption(SRole.class, "name", OrderByType.ASC); when( persistenceService .selectList(SelectDescriptorBuilder.getUserMembershipsWithRole(new QueryOptions(0, 10, Collections .singletonList(orderByOption))))) .thenReturn(Collections.singletonList(userMembership)); final List userMemberships = identityServiceImpl.getUserMemberships(0, 10, orderByOption); assertEquals(userMembership, userMemberships.get(0)); } @Test public void getUserMembershipsOrderByGroup() throws Exception { final SUserMembership userMembership = mock(SUserMembership.class); final OrderByOption orderByOption = new OrderByOption(SGroup.class, "name", OrderByType.ASC); when( persistenceService .selectList(SelectDescriptorBuilder.getUserMembershipsWithGroup(new QueryOptions(0, 10, Collections .singletonList(orderByOption))))) .thenReturn(Collections.singletonList(userMembership)); final List userMemberships = identityServiceImpl.getUserMemberships(0, 10, orderByOption); assertEquals(userMembership, userMemberships.get(0)); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUserMemberships(int, int)}. */ @Test public final void getUserMembershipsPaginated() throws SBonitaReadException, SIdentityException { final SUserMembership userMembership = mock(SUserMembership.class); final List userMemberships = Collections.singletonList(userMembership); doReturn(userMemberships).when(persistenceService) .selectList(ArgumentMatchers.> any()); assertEquals(userMemberships, identityServiceImpl.getUserMemberships(0, 10)); } @Test(expected = SIdentityException.class) public final void getUserMembershipsPaginatedThrowException() throws SBonitaReadException, SIdentityException { doThrow(new SBonitaReadException("")).when(persistenceService) .selectList(ArgumentMatchers.> any()); identityServiceImpl.getUserMemberships(0, 10); } } ================================================ FILE: services/bonita-identity/src/test/java/org/bonitasoft/engine/identity/impl/IdentityServiceImplForUserTest.java ================================================ /** * 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.identity.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.identity.IconService; import org.bonitasoft.engine.identity.SIcon; import org.bonitasoft.engine.identity.SIdentityException; import org.bonitasoft.engine.identity.SUserNotFoundException; import org.bonitasoft.engine.identity.model.SUser; import org.bonitasoft.engine.identity.model.SUserLogin; import org.bonitasoft.engine.identity.model.builder.SUserLogBuilder; import org.bonitasoft.engine.identity.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.persistence.*; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.*; import org.mockito.junit.MockitoJUnitRunner; /** * @author Matthieu Chaffotte * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class IdentityServiceImplForUserTest { public static final long USER_ID = 6543L; public static final long ICON_ID = 5247890L; public static final long NEW_ICON_ID = 4328976L; @Mock private CredentialsEncrypter encrypter; @Mock private Recorder recorder; @Mock private ReadPersistenceService persistenceService; @Mock private IconService iconService; @Mock private EventService eventService; @Spy @InjectMocks private IdentityServiceImpl identityServiceImpl; @Captor private ArgumentCaptor insertRecordArgumentCaptor; @Captor private ArgumentCaptor updateRecordArgumentCaptor; @Captor private ArgumentCaptor deleteRecordArgumentCaptor; @Mock private SUserLogBuilder sUserLogBuilder; @Mock private SQueriableLog log; @Mock private QueriableLoggerService queriableLoggerService; @Before public void setUp() throws SRecorderException { doReturn(sUserLogBuilder).when(identityServiceImpl).getUserLog(any(ActionType.class), anyString()); doReturn(log).when(sUserLogBuilder).build(); SIcon newIcon = new SIcon("", null); newIcon.setId(NEW_ICON_ID); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfUsers()}. */ @Test public void getNumberOfUsers() throws Exception { when(persistenceService.selectOne(ArgumentMatchers.> any())).thenReturn(1L); Assert.assertEquals(1L, identityServiceImpl.getNumberOfUsers()); verifyNoInteractions(recorder); } @Test(expected = SIdentityException.class) public void getNumberOfUsersThrowException() throws Exception { when(persistenceService.selectOne(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); identityServiceImpl.getNumberOfUsers(); } /** * Test method for * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfUsers(org.bonitasoft.engine.persistence.QueryOptions)}. */ @Test public void getNumberOfUsersWithOptions() throws Exception { final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.getNumberOfEntities(SUser.class, options, null)).thenReturn(1L); Assert.assertEquals(1L, identityServiceImpl.getNumberOfUsers(options)); } @Test(expected = SBonitaReadException.class) public void getNumberOfUsersWithOptionsThrowException() throws Exception { final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.getNumberOfEntities(SUser.class, options, null)) .thenThrow(new SBonitaReadException("")); identityServiceImpl.getNumberOfUsers(options); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfUsersByGroup(long)}. */ @Test public void getNumberOfUsersByGroup() throws Exception { final long groupId = 1; when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByGroup(groupId))).thenReturn(3L); Assert.assertEquals(3L, identityServiceImpl.getNumberOfUsersByGroup(groupId)); } @Test(expected = SIdentityException.class) public void getNumberOfUsersByGroupThrowException() throws Exception { final long groupId = 1; when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByGroup(groupId))) .thenThrow(new SBonitaReadException("")); identityServiceImpl.getNumberOfUsersByGroup(groupId); } /** * Test method for * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfUsersByMembership(long, long)}. */ @Test public void getNumberOfUsersByMembership() throws Exception { final long groupId = 1; final long roleId = 2; when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByMembership(groupId, roleId))) .thenReturn(3L); Assert.assertEquals(3L, identityServiceImpl.getNumberOfUsersByMembership(groupId, roleId)); } @Test(expected = SIdentityException.class) public void getNumberOfUsersByMembershipThrowException() throws Exception { final long groupId = 1; final long roleId = 2; when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByMembership(groupId, roleId))) .thenThrow(new SBonitaReadException("")); identityServiceImpl.getNumberOfUsersByMembership(groupId, roleId); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getNumberOfUsersByRole(long)}. */ @Test public void getNumberOfUsersByRole() throws Exception { final long roleId = 2; when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByRole(roleId))).thenReturn(3L); Assert.assertEquals(3L, identityServiceImpl.getNumberOfUsersByRole(roleId)); } @Test(expected = SIdentityException.class) public void getNumberOfUsersByRoleThrowException() throws Exception { final long roleId = 2; when(persistenceService.selectOne(SelectDescriptorBuilder.getNumberOfUsersByRole(roleId))) .thenThrow(new SBonitaReadException("")); identityServiceImpl.getNumberOfUsersByRole(roleId); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUser(long)}. */ @Test public void getUserById() throws SBonitaReadException, SUserNotFoundException { final long userId = 1; final SUser sUser = mock(SUser.class); when(persistenceService.selectById(SelectDescriptorBuilder.getElementById(SUser.class, "User", userId))) .thenReturn(sUser); Assert.assertEquals(sUser, identityServiceImpl.getUser(userId)); } @Test(expected = SUserNotFoundException.class) public void getUserByIdNotExist() throws SBonitaReadException, SUserNotFoundException { final long userId = 455; doReturn(null).when(persistenceService) .selectById(SelectDescriptorBuilder.getElementById(SUser.class, "User", userId)); identityServiceImpl.getUser(userId); } @Test(expected = SUserNotFoundException.class) public void getUserByIdThrowException() throws SBonitaReadException, SUserNotFoundException { final long userId = 1; doThrow(new SBonitaReadException("")).when(persistenceService) .selectById(SelectDescriptorBuilder.getElementById(SUser.class, "User", userId)); identityServiceImpl.getUser(userId); } /** * Test method for {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#getUsers(java.util.List)}. */ @Test public void getUsersByIds() throws Exception { final SUser sUser1 = mock(SUser.class); final SUser sUser2 = mock(SUser.class); final SUser sUser3 = mock(SUser.class); final List users = Arrays.asList(sUser1, sUser2, sUser3); final List ids = Arrays.asList(1l, 2l, 3l); when(persistenceService.selectList(SelectDescriptorBuilder.getElementsByIds(SUser.class, "User", ids))) .thenReturn(users); Assert.assertEquals(users, identityServiceImpl.getUsers(ids)); } @Test public void getUsersByNullIds() throws Exception { assertTrue(identityServiceImpl.getUsers(null).isEmpty()); } @Test public void getUsersByEmptyIds() throws Exception { assertTrue(identityServiceImpl.getUsers(Collections. emptyList()).isEmpty()); } @SuppressWarnings("unchecked") @Test(expected = SUserNotFoundException.class) public void getUsersByIdsThrowException() throws Exception { when(persistenceService.selectList(any(SelectListDescriptor.class))).thenThrow( new SBonitaReadException("plop")); identityServiceImpl.getUsers(Collections.singletonList(1L)); } @SuppressWarnings("unchecked") @Test public void getUsersByNames() throws Exception { final SUser sUser1 = mock(SUser.class); final SUser sUser2 = mock(SUser.class); final List users = Arrays.asList(sUser1, sUser2); final List names = Arrays.asList("matti", "marja", "taina"); doReturn(users).when(persistenceService).selectList(any(SelectListDescriptor.class)); Assert.assertEquals(users, identityServiceImpl.getUsersByUsername(names)); } @Test public void getUsersByNullNames() throws Exception { assertTrue(identityServiceImpl.getUsersByUsername(null).isEmpty()); } @Test public void getUsersByEmptyNames() throws Exception { assertTrue(identityServiceImpl.getUsersByUsername(Collections. emptyList()).isEmpty()); } @SuppressWarnings("unchecked") @Test(expected = SIdentityException.class) public void getUsersByNamesThrowException() throws Exception { when(persistenceService.selectList(any(SelectListDescriptor.class))) .thenThrow(new SBonitaReadException("plop")); identityServiceImpl.getUsersByUsername(Arrays.asList("hannu")); } /** * Test method for * {@link org.bonitasoft.engine.identity.impl.IdentityServiceImpl#searchUsers(org.bonitasoft.engine.persistence.QueryOptions)}. */ @Test public void searchUsers() throws Exception { final QueryOptions options = new QueryOptions(0, 10); final SUser user = mock(SUser.class); when(persistenceService.searchEntity(SUser.class, options, null)).thenReturn(Collections.singletonList(user)); assertEquals(user, identityServiceImpl.searchUsers(options).get(0)); } @Test(expected = SBonitaReadException.class) public void searchUsersThrowException() throws SBonitaReadException { final QueryOptions options = new QueryOptions(0, 10); doThrow(new SBonitaReadException("")).when(persistenceService).searchEntity(SUser.class, options, null); identityServiceImpl.searchUsers(options).get(0); } @Test public void should_create_user_with_icon_and_userLogin() throws Exception { //given SUser sUser = new SUser(); sUser.setId(NEW_ICON_ID); SIcon sIcon = new SIcon("image/jpeg", "iconContent".getBytes()); sIcon.setId(NEW_ICON_ID); when(iconService.createIcon(any(), any())).thenReturn(sIcon); //when identityServiceImpl.createUser(sUser, null, null, "test.jpg", "iconContent".getBytes()); //then verify(recorder, times(1)) .recordInsert(argThat(perInsertRecord -> (perInsertRecord.getEntity() instanceof SUser) && ((SUser) perInsertRecord.getEntity()).getIconId() == NEW_ICON_ID), eq("USER")); verify(recorder, times(1)).recordInsert( argThat(perInsertRecord -> perInsertRecord.getEntity() instanceof SUserLogin), eq("USER_LOGIN")); verify(iconService).createIcon("test.jpg", "iconContent".getBytes()); } @Test public void should_updateUser_create_the_icon_if_it_does_not_exists() throws Exception { //given haveUser(); //when EntityUpdateDescriptor iconUpdateDescriptor = new EntityUpdateDescriptor(); iconUpdateDescriptor.addField("filename", "theNewIcon.gif"); iconUpdateDescriptor.addField("content", "theContent".getBytes()); identityServiceImpl.updateUser(USER_ID, new EntityUpdateDescriptor(), null, null, iconUpdateDescriptor); //then verify(recorder).recordUpdate(argThat(updateRecord -> updateRecord.getEntity() instanceof SUser && updateRecord.getFields().containsKey("iconId")), nullable(String.class)); verify(iconService).replaceIcon("theNewIcon.gif", "theContent".getBytes(), null); } @Test public void should_updateUser_create_new_icon_if_it_exists() throws Exception { //given SUser sUser = haveUser(); SIcon sIcon = haveIcon(sUser); //when EntityUpdateDescriptor iconUpdateDescriptor = new EntityUpdateDescriptor(); iconUpdateDescriptor.addField("filename", "theNewIcon.jpg"); iconUpdateDescriptor.addField("content", "updated content".getBytes()); identityServiceImpl.updateUser(USER_ID, new EntityUpdateDescriptor(), null, null, iconUpdateDescriptor); //then verify(recorder).recordUpdate(updateRecordArgumentCaptor.capture(), nullable(String.class)); verify(iconService).replaceIcon("theNewIcon.jpg", "updated content".getBytes(), sIcon.getId()); } @Test public void should_update_user_and_invoke_replace_icon_with_icon_content_null() throws Exception { //given SUser sUser = haveUser(); SIcon sIcon = haveIcon(sUser); //when EntityUpdateDescriptor iconUpdateDescriptor = new EntityUpdateDescriptor(); iconUpdateDescriptor.addField("filename", "filename"); iconUpdateDescriptor.addField("content", null); identityServiceImpl.updateUser(USER_ID, new EntityUpdateDescriptor(), null, null, iconUpdateDescriptor); //then verify(recorder).recordUpdate(argThat(updateRecord -> updateRecord.getEntity() instanceof SUser && updateRecord.getFields().containsKey("iconId")), nullable(String.class)); verify(iconService).replaceIcon("filename", null, sIcon.getId()); verifyNoMoreInteractions(iconService); } private SUser haveUser() throws SUserNotFoundException { SUser sUser = new SUser(); sUser.setId(USER_ID); doReturn(sUser).when(identityServiceImpl).getUser(USER_ID); return sUser; } @Test public void should_deleteUser_delete_the_icon_if_it_exists() throws Exception { //given SUser sUser = haveUser(); SIcon icon = haveIcon(sUser); //when identityServiceImpl.deleteUser(USER_ID); //then verify(recorder).recordDelete(new DeleteRecord(sUser), "USER"); verify(iconService).deleteIcon(icon.getId()); } @Test public void should_deleteUser_not_delete_the_icon_if_it_does_not_exists() throws Exception { //given SUser sUser = haveUser(); //when identityServiceImpl.deleteUser(USER_ID); //then verify(recorder, times(1)).recordDelete(deleteRecordArgumentCaptor.capture(), nullable(String.class)); assertThat(deleteRecordArgumentCaptor.getAllValues()).extracting("entity").containsOnly(sUser); } private SIcon haveIcon(SUser sUser) throws SBonitaReadException { sUser.setIconId(ICON_ID); SIcon icon = new SIcon("image/gif", "theContent".getBytes()); icon.setId(ICON_ID); return icon; } } ================================================ FILE: services/bonita-identity/src/test/java/org/bonitasoft/engine/identity/impl/MD5CredentialsEncrypterTest.java ================================================ /** * 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.identity.impl; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class MD5CredentialsEncrypterTest { @Test public void hash_should_hash_password_using_md5() { final MD5CredentialsEncrypter encrypter = new MD5CredentialsEncrypter(); final String hash = encrypter.hash("123ñ"); assertThat(hash).isEqualTo("jTIqcNwrbMbZJEf20AFxAQ=="); } @Test public void check_should_compare_hash_password_with_a_clear_password() { final MD5CredentialsEncrypter encrypter = new MD5CredentialsEncrypter(); final boolean match = encrypter.check("123ñ", "jTIqcNwrbMbZJEf20AFxAQ=="); assertThat(match).isTrue(); } } ================================================ FILE: services/bonita-identity/src/test/java/org/bonitasoft/engine/identity/model/SUserTest.java ================================================ /** * 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.identity.model; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class SUserTest { @Test public void toString_should_not_display_password() throws Exception { //given final SUser sUser = new SUser(); sUser.setUserName("walter.bates"); sUser.setPassword("bpm"); //when final String string = sUser.toString(); //then assertThat(string).as("should not display password!").doesNotContain("password").doesNotContain("bpm"); } } ================================================ FILE: services/bonita-incident/build.gradle ================================================ dependencies { api project(':bpm:bonita-common') api project(':bpm:bonita-core:bonita-home-server') testImplementation libs.mockitoCore testImplementation libs.systemRules testRuntimeOnly libs.logback testImplementation libs.assertj } ================================================ FILE: services/bonita-incident/src/main/java/org/bonitasoft/engine/incident/FileLoggerIncidentHandler.java ================================================ /** * 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.incident; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; import org.slf4j.MarkerFactory; /** * Report incident in a log file located inside the bonita home * Is a tenant service must be declared in tenant part * * @author Baptiste Mesta * @author Matthieu Chaffotte */ public class FileLoggerIncidentHandler implements IncidentHandler { Logger logger = LoggerFactory.getLogger(FileLoggerIncidentHandler.class); public FileLoggerIncidentHandler() { } @Override public void handle(final Incident incident) { Marker INCIDENT = MarkerFactory.getMarker("INCIDENT"); logger.error(INCIDENT, "An incident occurred: {}", incident.getDescription()); logger.error(INCIDENT, "Exception was:", incident.getCause()); logger.error(INCIDENT, "We were unable to handle the failure on the elements because of", incident.getExceptionWhenHandlingFailure()); final String recoveryProcedure = incident.getRecoveryProcedure(); if (recoveryProcedure != null && !recoveryProcedure.isEmpty()) { logger.error(INCIDENT, "Procedure to recover: {}", recoveryProcedure); } } } ================================================ FILE: services/bonita-incident/src/main/java/org/bonitasoft/engine/incident/Incident.java ================================================ /** * 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.incident; /** * @author Baptiste Mesta */ public class Incident { private final String description; private final String recoveryProcedure; private final Throwable cause; private final Throwable exceptionWhenHandlingFailure; public Incident(final String description, final String recoveryProcedure, final Throwable cause, final Throwable exceptionWhenHandlingFailure) { super(); this.description = description; this.recoveryProcedure = recoveryProcedure; this.cause = cause; this.exceptionWhenHandlingFailure = exceptionWhenHandlingFailure; } public String getDescription() { return description; } public String getRecoveryProcedure() { return recoveryProcedure; } public Throwable getCause() { return cause; } public Throwable getExceptionWhenHandlingFailure() { return exceptionWhenHandlingFailure; } @Override public String toString() { return "Incident [description=" + description + ", recoveryProcedure=" + recoveryProcedure + "]"; } } ================================================ FILE: services/bonita-incident/src/main/java/org/bonitasoft/engine/incident/IncidentHandler.java ================================================ /** * 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.incident; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface IncidentHandler { void handle(Incident incident); } ================================================ FILE: services/bonita-incident/src/main/java/org/bonitasoft/engine/incident/IncidentService.java ================================================ /** * 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.incident; /** * Service that report incidents to an administrator * * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface IncidentService { /** * Report an incident * * @param incident the incident to be reported */ void report(Incident incident); } ================================================ FILE: services/bonita-incident/src/main/java/org/bonitasoft/engine/incident/IncidentServiceImpl.java ================================================ /** * 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.incident; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Baptsite Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public class IncidentServiceImpl implements IncidentService { private final Logger logger = LoggerFactory.getLogger(IncidentServiceImpl.class); private final List handlers; public IncidentServiceImpl(final List handlers) { this.handlers = handlers; } @Override public void report(final Incident incident) { for (final IncidentHandler handler : handlers) { try { handler.handle(incident); } catch (final Exception t) { logger.error("Unable to report an incident using the handler {} incident was {}", handler, incident); } } } } ================================================ FILE: services/bonita-incident/src/test/java/org/bonitasoft/engine/incident/BeforeAllLoggerInitializer.java ================================================ /** * Copyright (C) 2022 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.incident; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class is used to set System Property before the logger initializes. * This class must be initialized before everything else in all test classes of this module ! * Otherwise, the logger is initialized before the System Property is set, resulting in the logback conf not aware of * the System Property value. * * @author Emmanuel Duchastenier */ public class BeforeAllLoggerInitializer { static final String INCIDENT_LOG_PATH = "build/log/"; static final String INCIDENT_LOG_PATH_PROP_NAME = "INCIDENT_LOG_PATH"; static { System.setProperty(INCIDENT_LOG_PATH_PROP_NAME, INCIDENT_LOG_PATH); LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); } public static void initialize() { } } ================================================ FILE: services/bonita-incident/src/test/java/org/bonitasoft/engine/incident/FileLoggerIncidentHandlerTest.java ================================================ /** * 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.incident; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.incident.BeforeAllLoggerInitializer.INCIDENT_LOG_PATH; import static org.bonitasoft.engine.incident.BeforeAllLoggerInitializer.INCIDENT_LOG_PATH_PROP_NAME; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import org.bonitasoft.engine.io.IOUtil; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class FileLoggerIncidentHandlerTest { static { BeforeAllLoggerInitializer.initialize(); } @Rule public final SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests(); @Before public void init() throws IOException { Files.createDirectories(Path.of(INCIDENT_LOG_PATH)); } @After public void clean() throws IOException { IOUtil.deleteDir(new File(INCIDENT_LOG_PATH)); } @Test public void should_write_into_file_when_handle_incident() throws Exception { //Given long tenantId = 1; String cause = "an unexpected exception"; String handlingFailure = "unable to handle failure"; String recovery = "recovery"; String description = "test"; final Incident incident = new Incident(description, recovery, new Exception(cause), new Exception(handlingFailure)); FileLoggerIncidentHandler fileLoggerIncidentHandler = new FileLoggerIncidentHandler(); //When fileLoggerIncidentHandler.handle(incident); String expectedDescription = "An incident occurred: " + description; String expectedRecoveryProcedureMessage = "Procedure to recover: " + recovery; // as defined in logback.xml all logs are logged in system out, so we check that incidents are also logged to the console. assertThat(systemOutRule.getLog()) .contains(expectedDescription) .contains(cause) .contains(handlingFailure) .contains(expectedRecoveryProcedureMessage); // as defined in logback.xml, logs marked with INCIDENT should be logged also in a specific file. String log = Files.readString(new File(INCIDENT_LOG_PATH + "/incidents.log").toPath()); assertThat(log).contains(expectedDescription) .contains(cause) .contains(handlingFailure) .contains(expectedRecoveryProcedureMessage); System.clearProperty(INCIDENT_LOG_PATH_PROP_NAME); } } ================================================ FILE: services/bonita-incident/src/test/java/org/bonitasoft/engine/incident/IncidentServiceImplTest.java ================================================ /** * 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.incident; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.*; import java.util.ArrayList; import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class IncidentServiceImplTest { static { BeforeAllLoggerInitializer.initialize(); } private IncidentHandler handler1; private IncidentHandler handler2; private IncidentService incidentService; @Before public void before() { final List handlers = new ArrayList<>(2); handler1 = mock(IncidentHandler.class); handlers.add(handler1); handler2 = mock(IncidentHandler.class); handlers.add(handler2); incidentService = new IncidentServiceImpl(handlers); } @Test public void reportCallAllHandlers() { final Incident incident = new Incident("test", "recovery", null, null); incidentService.report(incident); verify(handler1, times(1)).handle(incident); verify(handler2, times(1)).handle(incident); } @Test public void reportCallAllHandlersEvenIfFirstThrowException() { doThrow(new RuntimeException()).when(handler1).handle(nullable(Incident.class)); final Incident incident = new Incident("test", "recovery", null, null); incidentService.report(incident); verify(handler2, times(1)).handle(incident); } } ================================================ FILE: services/bonita-incident/src/test/resources/logback.xml ================================================ %d{HH:mm:ss.SSS}|%-5level|%logger{16}| %msg%n INCIDENT DENY ${INCIDENT_LOG_PATH}/incidents.log %d{HH:mm:ss.SSS}|%-5level|%logger{16}| %msg%n ================================================ FILE: services/bonita-lock/build.gradle ================================================ dependencies { api project(':services:bonita-commons') api project(':services:bonita-session') testImplementation libs.assertj testImplementation libs.logback } ================================================ FILE: services/bonita-lock/src/main/java/org/bonitasoft/engine/lock/BonitaLock.java ================================================ /** * 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.lock; import java.util.Objects; public class BonitaLock { private final String objectType; private final long objectToLockId; public BonitaLock(final String objectType, final long objectToLockId) { super(); this.objectType = objectType; this.objectToLockId = objectToLockId; } public String getObjectType() { return objectType; } public long getObjectToLockId() { return objectToLockId; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BonitaLock that = (BonitaLock) o; return objectToLockId == that.objectToLockId && Objects.equals(objectType, that.objectType); } @Override public int hashCode() { return Objects.hash(objectType, objectToLockId); } @Override public String toString() { return "BonitaLock[" + objectType + ":" + objectToLockId + "]"; } } ================================================ FILE: services/bonita-lock/src/main/java/org/bonitasoft/engine/lock/LockService.java ================================================ /** * 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.lock; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * This service allows to synchronize access to a resource using ReadWrite lock pattern. * Creating an exclusive lock allows to be the only one at a time accessing the resource. * * @see ReentrantReadWriteLock * @author Elias Ricken de Medeiros * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface LockService { void unlock(BonitaLock lock) throws SLockException; /** * Acquire the lock for the object having type and id in parameters
      * This method wait for the lock to be available * * @param objectToLockId * @param objectType * @return the lock * @throws SLockException when we were unable to lock due to an unexpected error * @throws SLockTimeoutException when we were unable to lock due to a timeout */ BonitaLock lock(long objectToLockId, String objectType) throws SLockException, SLockTimeoutException; /** * Acquire the lock for the object having type and id in parameters waiting maximum timeout
      * This method wait for the lock to be available. If it becomes available before the timeout expires the returns the * obtained lock, else returns null * * @param objectToLockId * @param objectType * @param timeout * @param timeUnit * @return the obtained lock if it has been acquired before the timeout expires or null if the timeout has expired. * @throws SLockException when we were unable to lock (not because of the timeout) */ BonitaLock tryLock(long objectToLockId, String objectType, long timeout, TimeUnit timeUnit) throws SLockException; } ================================================ FILE: services/bonita-lock/src/main/java/org/bonitasoft/engine/lock/MemoryLockService.java ================================================ /** * 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.lock; import static java.util.Collections.synchronizedMap; import static java.util.concurrent.TimeUnit.SECONDS; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; @Component @ConditionalOnSingleCandidate(LockService.class) public class MemoryLockService implements LockService { private final Logger logger = LoggerFactory.getLogger(MemoryLockService.class); // need to have a synchronized map to synchronize the get with map modifications private final Map locks = synchronizedMap(new HashMap<>()); private final int lockTimeoutSeconds; public MemoryLockService(@Value("${bonita.platform.lock.memory.timeout}") int lockTimeoutSeconds) { this.lockTimeoutSeconds = lockTimeoutSeconds; } @Override public void unlock(BonitaLock lock) throws SLockException { String key = buildKey(lock.getObjectToLockId(), lock.getObjectType()); locks.computeIfPresent(key, (k, l) -> { if (l.hasQueuedThreads()) { logger.debug("Lock released {}, keeping it, some other threads are requesting it", lock); l.unlock(); return l; } if (l.getHoldCount() > 1) { logger.debug("Lock released {}, keeping it, it was locked multiple times by the current thread", lock); l.unlock(); return l; } else { logger.debug("Lock released {}, removing it, no other thread is requesting it", lock); l.unlock(); return null; } }); } private String buildKey(final long objectToLockId, final String objectType) { return String.format("%s_%s", objectType, objectToLockId); } @Override public BonitaLock lock(long objectToLockId, String objectType) throws SLockException, SLockTimeoutException { BonitaLock bonitaLock = tryLock(objectToLockId, objectType, lockTimeoutSeconds, SECONDS); if (bonitaLock == null) { throw new SLockTimeoutException(String.format("Unable to acquire lock %s,%s in %s seconds", objectToLockId, objectType, lockTimeoutSeconds)); } return bonitaLock; } private ReentrantLock createLock(long objectToLockId, String objectType) { String key = buildKey(objectToLockId, objectType); return locks.computeIfAbsent(key, k -> { ReentrantLock lock = new ReentrantLock(); logger.debug("Created new lock for key {}", key); return lock; }); } @Override public BonitaLock tryLock(long objectToLockId, String objectType, long timeout, TimeUnit timeUnit) throws SLockException { String key = buildKey(objectToLockId, objectType); ReentrantLock lock = createLock(objectToLockId, objectType); try { if (lock.tryLock(timeout, timeUnit)) { //this get need to be synchronized with the unlock that can change the map ReentrantLock currentLockInTheMap = locks.get(key); if (!lock.equals(currentLockInTheMap)) { //lock was taken but someone replace it in the map, get it next time // this can happen when the lock is removed just between `createLock` and `tryLock` // retry it... logger.debug( "Lock for key {} was acquired but it was replaced due to a race condition. We will retry.", key); lock.unlock(); return tryLock(objectToLockId, objectType, timeout, timeUnit); } logger.debug("Locked acquired for key {}", key); return new BonitaLock(objectType, objectToLockId); } else { logger.debug("Locked was not acquired for key {}", key); return null; } } catch (InterruptedException e) { throw new SLockException("interrupted while trying to get the lock", e); } } } ================================================ FILE: services/bonita-lock/src/main/java/org/bonitasoft/engine/lock/SLockException.java ================================================ /** * 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.lock; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SLockException extends SBonitaException { private static final long serialVersionUID = -3762406542199357594L; public SLockException(final String message, final Throwable cause) { super(message, cause); } public SLockException(final Throwable cause) { super(cause); } public SLockException(String message) { super(message); } } ================================================ FILE: services/bonita-lock/src/main/java/org/bonitasoft/engine/lock/SLockTimeoutException.java ================================================ /** * 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.lock; import org.bonitasoft.engine.commons.exceptions.SBonitaException; public class SLockTimeoutException extends SBonitaException { public SLockTimeoutException(String message) { super(message); } } ================================================ FILE: services/bonita-lock/src/test/java/org/bonitasoft/engine/lock/MemoryLockServiceTest.java ================================================ /** * 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.lock; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; /** * @author Baptiste Mesta */ public class MemoryLockServiceTest { private ExecutorService executorService = Executors.newSingleThreadExecutor(); private final class LockThread extends Thread { private BonitaLock lock; private final int id; private final String type; public LockThread(final int id, final String type) { this.id = id; this.type = type; } @Override public void run() { try { lock = memoryLockService.lock(id, type); } catch (final SLockException | SLockTimeoutException e) { // NOTHING } } public boolean isLockObtained() { return lock != null; } } private final class TryLockThread extends Thread { private BonitaLock lock; private final int id; private final String type; private final Semaphore semaphore; public TryLockThread(final int id, final String type, final Semaphore semaphore) { this.id = id; this.type = type; this.semaphore = semaphore; } @Override public void run() { try { semaphore.acquire(); lock = memoryLockService.tryLock(id, type, 20, TimeUnit.SECONDS); semaphore.acquire(); memoryLockService.unlock(lock); lock = null; } catch (final InterruptedException e) { e.printStackTrace(); } catch (SLockException e) { e.printStackTrace(); } } public void waitForLockToBeObtained(final long maxTime) throws InterruptedException { final long initialTimestamp = System.currentTimeMillis(); while (maxTime >= System.currentTimeMillis() - initialTimestamp) { if (lock != null) { return; } Thread.sleep(20); } throw new InterruptedException("Lock not obtained after waiting " + maxTime + " ms"); } } private MemoryLockService memoryLockService; @Before public void before() { memoryLockService = new MemoryLockService(1); } @Test public void testUnlock() throws Exception { final BonitaLock lock = memoryLockService.lock(5, "a"); final LockThread lockThread = new LockThread(5, "a"); lockThread.start(); Thread.sleep(100); memoryLockService.unlock(lock); lockThread.join(1200); assertTrue("should not be able to lock", lockThread.isLockObtained()); } @Test public void should_lock_multiple_times_on_the_same_thread() throws Exception { BonitaLock bonitaLock = memoryLockService.lock(123, "abc"); memoryLockService.lock(123, "abc"); //Unable to lock in an other thread assertThat(tryLockInAnOtherThread(123, "abc")).isNull(); //Unable to lock in an other thread: the lock is still hold once by the current thread memoryLockService.unlock(bonitaLock); assertThat(tryLockInAnOtherThread(123, "abc")).isNull(); //Able to unlock the thread release all holds memoryLockService.unlock(bonitaLock); assertThat(tryLockInAnOtherThread(123, "abc")).isNotNull(); } private BonitaLock tryLockInAnOtherThread(int objectToLockId, String abc) throws InterruptedException, java.util.concurrent.ExecutionException { return executorService .submit(() -> memoryLockService.tryLock(objectToLockId, abc, 10, TimeUnit.MILLISECONDS)) .get(); } @Test public void testLock() throws Exception { memoryLockService.lock(3, "a"); final LockThread lockThread = new LockThread(4, "a"); lockThread.start(); lockThread.join(100); assertTrue("should be able to lock", lockThread.isLockObtained()); } @Test public void testLockTimeout() throws Exception { memoryLockService.lock(2, "a"); final LockThread lockThread = new LockThread(2, "a"); lockThread.start(); lockThread.join(1200); assertFalse("should not be able to lock", lockThread.isLockObtained()); } /* * Issue with lock removed from map too early: the lock is removed from the map but a thread have a reference on it * T1 have the lock L1 * T2 fait trylock et est blocké * T1 release * T2 block L1 * T1 enleve de la map * T3 appel tryLock * T3 lock L2 */ @Test public void testRemoveLockFromMap() throws Exception { int rd = new Random().nextInt(); final Semaphore s1 = new Semaphore(1); final Semaphore s2 = new Semaphore(1); final Semaphore s3 = new Semaphore(1); final TryLockThread t1 = new TryLockThread(rd, "t", s1); final TryLockThread t2 = new TryLockThread(rd, "t", s2); final TryLockThread t3 = new TryLockThread(rd, "t", s3); s1.acquire(); s2.acquire(); s3.acquire(); t1.start(); t2.start(); t3.start(); // state : S1 ∈ T, S2 ∈ T, S3 ∈ T, S1 <- T1, S2 <- T2, S3 <- T3, BL free s1.release(); Thread.sleep(5); // t1 take the lock | state : S1 ∈ T1, S2 ∈ T, S3 ∈ T, S1 <- T1, S2 <- T2, S3 <- T3, BL ∈ T1 s2.release(); Thread.sleep(5); // t2 is locked | state : S1 ∈ T1, S2 ∈ T2, S3 ∈ T, S1 <- T1, S3 <- T3, BL ∈ T1, BL <- T1 s1.release(); Thread.sleep(20); // t1 unlock & t2 have the lock | state : S1, S2 ∈ T2, S3 ∈ T, S3 <- T3, BL ∈ T2 t2.waitForLockToBeObtained(500); s3.release(); Thread.sleep(20); // t3 try acquire | state : S1, S2 ∈ T2, S3 ∈ T3, BL ∈ T2, BL <- T3 // t3 should not be able to lock try { t3.waitForLockToBeObtained(500); fail(); } catch (final InterruptedException ignored) { } s2.release(); Thread.sleep(20); // now it should be able to have it | state : S1, S2, S3 ∈ T3, BL ∈ T3 t3.waitForLockToBeObtained(500); s3.release(); // state : S1, S2, S3, BL } } ================================================ FILE: services/bonita-log/build.gradle ================================================ dependencies { api project(':services:bonita-session') api project(':services:bonita-commons') api project(':services:bonita-persistence') api project(':services:bonita-builder') api project(':services:bonita-platform') api project(':services:bonita-transaction') testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.logback annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/SQueriableLog.java ================================================ /** * 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.queriablelogger.model; import java.util.Calendar; import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; @Entity @Data @AllArgsConstructor @NoArgsConstructor @Builder(toBuilder = true) @Table(name = "queriable_log") public class SQueriableLog implements PersistentObject { public static final int STATUS_FAIL = 0; public static final int STATUS_OK = 1; public static final String TIME_STAMP = "timeStamp"; public static final String YEAR = "year"; public static final String MONTH = "month"; public static final String DAY_OF_YEAR = "dayOfYear"; public static final String WEEK_OF_YEAR = "weekOfYear"; public static final String USERID = "userId"; public static final String THREAD_NUMBER = "threadNumber"; public static final String CLUSTER_NODE = "clusterNode"; public static final String PRODUCT_VERSION = "productVersion"; public static final String SEVERITY = "severity"; public static final String ACTION_TYPE = "actionType"; public static final String ACTION_SCOPE = "actionScope"; public static final String ACTION_STATUS = "actionStatus"; public static final String RAW_MESSAGE = "rawMessage"; public static final String CALLER_CLASS_NAME = "callerClassName"; public static final String CALLER_METHOD_NAME = "callerMethodName"; public static final String NUMERIC_INDEX1 = "numericIndex1"; public static final String NUMERIC_INDEX2 = "numericIndex2"; public static final String NUMERIC_INDEX3 = "numericIndex3"; public static final String NUMERIC_INDEX4 = "numericIndex4"; public static final String NUMERIC_INDEX5 = "numericIndex5"; @Id private long id; @Column(name = "log_timestamp") private long timeStamp; @Column(name = "whatYear") private int year; @Column(name = "whatMonth") private int month; private int dayOfYear; private int weekOfYear; private String userId; @Builder.Default private long threadNumber = Thread.currentThread().getId(); private String clusterNode; private String productVersion; @Enumerated(EnumType.STRING) private SQueriableLogSeverity severity; private String actionType; private String actionScope; @Builder.Default private int actionStatus = -1; @Column(name = "RAWMESSAGE") private String rawMessage; private String callerClassName; private String callerMethodName; @Builder.Default private long numericIndex1 = -1; @Builder.Default private long numericIndex2 = -1; @Builder.Default private long numericIndex3 = -1; @Builder.Default private long numericIndex4 = -1; @Builder.Default private long numericIndex5 = -1; public static class SQueriableLogBuilder { public SQueriableLogBuilder initializeNow() { final Date date = new Date(); final Calendar calendar = Calendar.getInstance(); calendar.setTime(date); timeStamp(date.getTime()); year(calendar.get(Calendar.YEAR)); month(calendar.get(Calendar.MONTH) + 1); weekOfYear(calendar.get(Calendar.WEEK_OF_YEAR)); dayOfYear(calendar.get(Calendar.DAY_OF_YEAR)); return this; } } public long getNumericIndex(final int pos) { return switch (pos) { case 0 -> numericIndex1; case 1 -> numericIndex2; case 2 -> numericIndex3; case 3 -> numericIndex4; case 4 -> numericIndex5; default -> throw new IllegalStateException(); }; } public void setNumericIndex(final int pos, final long value) { switch (pos) { case 0: numericIndex1 = value; break; case 1: numericIndex2 = value; break; case 2: numericIndex3 = value; break; case 3: numericIndex4 = value; break; case 4: numericIndex5 = value; break; default: throw new IllegalStateException(); } } } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/SQueriableLogSeverity.java ================================================ /** * 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.queriablelogger.model; /** * @author Elias Ricken de Medeiros */ public enum SQueriableLogSeverity { // TODO FIXME use an int for the database BUSINESS, INTERNAL; } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/ActionType.java ================================================ /** * 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.queriablelogger.model.builder; public enum ActionType { CREATED, UPDATED, DELETED, EXECUTED, STARTED, STOPPED, SCHEDULED, PAUSED, RESUMED, RESCHEDULED, CLEANED_UP } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/HasCRUDEAction.java ================================================ /** * 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.queriablelogger.model.builder; /** * @author Elias Ricken de Medeiros * @author Nicolas Chabanoles * @author Matthieu Chaffotte */ public interface HasCRUDEAction { SLogBuilder setActionType(final ActionType actionType); } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/HasCRUDEActionFactory.java ================================================ /** * 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.queriablelogger.model.builder; /** * @author Elias Ricken de Medeiros * @author Nicolas Chabanoles * @author Matthieu Chaffotte */ public interface HasCRUDEActionFactory { } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/SLogBuilder.java ================================================ /** * 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.queriablelogger.model.builder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; /** * @author Elias Ricken de Medeiros * @author Nicolas Chabanoles * @author Matthieu Chaffotte */ public interface SLogBuilder { SLogBuilder userId(String userId); SLogBuilder clusterNode(String clusterNode); SLogBuilder productVersion(String productVersion); SLogBuilder severity(SQueriableLogSeverity severity); SLogBuilder actionScope(String scope); SLogBuilder actionStatus(int status); SLogBuilder rawMessage(String rawMessage); SLogBuilder callerClassName(String callerClassName); SLogBuilder callerMethodName(String callerMethodName); SQueriableLog build(); } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/SLogBuilderFactory.java ================================================ /** * 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.queriablelogger.model.builder; /** * @author Elias Ricken de Medeiros * @author Nicolas Chabanoles * @author Matthieu Chaffotte */ public interface SLogBuilderFactory { SLogBuilder createNewInstance(); } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/SPersistenceLogBuilder.java ================================================ /** * 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.queriablelogger.model.builder; /** * @author Elias Ricken de Medeiros */ public interface SPersistenceLogBuilder extends SLogBuilder { SPersistenceLogBuilder objectId(final long objectId); } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/SPersistenceLogBuilderFactory.java ================================================ /** * 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.queriablelogger.model.builder; /** * @author Elias Ricken de Medeiros */ public interface SPersistenceLogBuilderFactory extends SLogBuilderFactory { String getObjectIdKey(); } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/SQueriableLogBuilder.java ================================================ /** * 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.queriablelogger.model.builder; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Elias Ricken de Medeiros * @author Nicolas Chabanoles */ public class SQueriableLogBuilder implements SLogBuilder { private final SQueriableLog.SQueriableLogBuilder builder; public SQueriableLogBuilder() { this.builder = SQueriableLog.builder().initializeNow(); } @Override public SQueriableLogBuilder userId(final String userId) { builder.userId(userId); return this; } @Override public SQueriableLogBuilder clusterNode(final String clusterNode) { builder.clusterNode(clusterNode); return this; } @Override public SQueriableLogBuilder productVersion(final String productVersion) { builder.productVersion(productVersion); return this; } @Override public SQueriableLogBuilder severity(final SQueriableLogSeverity severity) { builder.severity(severity); return this; } public SQueriableLogBuilder actionType(final String actionType) { builder.actionType(actionType); return this; } @Override public SQueriableLogBuilder actionScope(final String scope) { builder.actionScope(scope); return this; } @Override public SQueriableLogBuilder actionStatus(final int status) { builder.actionStatus(status); return this; } @Override public SQueriableLogBuilder rawMessage(final String rawMessage) { builder.rawMessage(rawMessage); return this; } @Override public SQueriableLogBuilder callerClassName(final String callerClassName) { builder.callerClassName(callerClassName); return this; } @Override public SQueriableLogBuilder callerMethodName(final String callerMethodName) { builder.callerMethodName(callerMethodName); return this; } public SQueriableLogBuilder numericIndex(final int pos, final long value) { switch (pos) { case 0: builder.numericIndex1(value); break; case 1: builder.numericIndex2(value); break; case 2: builder.numericIndex3(value); break; case 3: builder.numericIndex4(value); break; case 4: builder.numericIndex5(value); break; default: throw new IllegalStateException(); } return this; } @Override public SQueriableLog build() { final List problems = checkMandatoryFields(); if (problems.size() > 0) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + problems); } return builder.build(); } private List checkMandatoryFields() { SQueriableLog entity = builder.build(); final List problems = new ArrayList(); if (entity.getSeverity() == null) { problems.add("severity"); } if (entity.getActionType() == null) { problems.add("actionType"); } if (SQueriableLog.STATUS_FAIL != entity.getActionStatus() && entity.getActionScope() == null) { problems.add("actionScope"); } if (entity.getActionStatus() != 0 && entity.getActionStatus() != 1) { problems.add("actionStatus (must be 0, for fail or 1, for ok)"); } if (entity.getRawMessage() == null) { problems.add("rawMessage"); } return problems; } } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/impl/CRUDELogBuilder.java ================================================ /** * 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.queriablelogger.model.builder.impl; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.SQueriableLogBuilder; /** * @author Elias Ricken de Medeiros * @author Nicolas Chabanoles * @author Matthieu Chaffotte */ public abstract class CRUDELogBuilder implements HasCRUDEAction, SLogBuilder { protected SQueriableLogBuilder queriableLogBuilder; private static final String SEPARATOR = "_"; protected CRUDELogBuilder() { queriableLogBuilder = new SQueriableLogBuilder(); } protected abstract String getActionTypePrefix(); @Override public SLogBuilder setActionType(final ActionType actionType) { queriableLogBuilder.actionType(getActionTypePrefix() + SEPARATOR + actionType.name()); return this; } @Override public SLogBuilder userId(final String userId) { queriableLogBuilder.userId(userId); return this; } @Override public SLogBuilder clusterNode(final String clusterNode) { queriableLogBuilder.clusterNode(clusterNode); return this; } @Override public SLogBuilder productVersion(final String productVersion) { queriableLogBuilder.productVersion(productVersion); return this; } @Override public SLogBuilder severity(final SQueriableLogSeverity severity) { queriableLogBuilder.severity(severity); return this; } @Override public SLogBuilder actionScope(final String scope) { queriableLogBuilder.actionScope(scope); return this; } @Override public SLogBuilder actionStatus(final int status) { queriableLogBuilder.actionStatus(status); return this; } @Override public SLogBuilder rawMessage(final String rawMessage) { queriableLogBuilder.rawMessage(rawMessage); return this; } @Override public SLogBuilder callerClassName(final String callerClassName) { queriableLogBuilder.callerClassName(callerClassName); return this; } @Override public SLogBuilder callerMethodName(final String callerMethodName) { queriableLogBuilder.callerMethodName(callerMethodName); return this; } @Override public SQueriableLog build() { final SQueriableLog log = queriableLogBuilder.build(); checkExtraRules(log); return log; } protected abstract void checkExtraRules(final SQueriableLog log); } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/impl/CRUDELogBuilderFactory.java ================================================ /** * 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.queriablelogger.model.builder.impl; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SQueriableLogBuilder; /** * @author Elias Ricken de Medeiros * @author Nicolas Chabanoles * @author Matthieu Chaffotte */ public abstract class CRUDELogBuilderFactory implements HasCRUDEActionFactory, SLogBuilderFactory { public CRUDELogBuilderFactory() { } @Override public SLogBuilder createNewInstance() { return new SQueriableLogBuilder(); } } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/queriablelogger/model/builder/impl/MissingMandatoryFieldsException.java ================================================ /** * 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.queriablelogger.model.builder.impl; /** * @author Elias Ricken de Medeiros */ public class MissingMandatoryFieldsException extends RuntimeException { private static final long serialVersionUID = 6207633035429372416L; public MissingMandatoryFieldsException(final String message) { super(message); } } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/services/IllegalIndexPositionException.java ================================================ /** * 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.services; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class IllegalIndexPositionException extends SBonitaException { private static final long serialVersionUID = 1L; public IllegalIndexPositionException(final String message) { super(message); } } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/services/QueriableLogSessionProvider.java ================================================ /** * 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.services; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ public interface QueriableLogSessionProvider { /** * Get the logged user * * @return the logged user */ String getUserId(); /** * Get the cluster node when the engine is running in a cluster. * * @return the cluster node */ String getClusterNode(); } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/services/QueriableLoggerService.java ================================================ /** * 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.services; import java.util.List; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; /** * @author Charles Souillard * @author Celine Souchet */ public interface QueriableLoggerService { /** * Log the given queriable logs. Only logs having the action type and the severity configured * to as loggable will be logged * * @param queriableLogs * @since 6.0 */ void log(String callerClassName, String callerMethodName, SQueriableLog... queriableLogs); /** * Verify if the given action type and severity are loggable * * @param actionType * the action type * @param severity * the severity * @return true if the log is active for the given action type and severity; false otherwise * @since 6.0 */ boolean isLoggable(final String actionType, final SQueriableLogSeverity severity); /** * Get the queriable log from its id. * * @param logId * @return the queriable log * @throws SQueriableLogException * @since 6.0 */ SQueriableLog getLog(long logId) throws SQueriableLogNotFoundException, SBonitaReadException; /** * Get total number of queriable logs * * @return the number of queriable logs * @since 6.0 */ long getNumberOfLogs() throws SBonitaReadException; /** * Get the queriable logs having the given value for the given int index * * @param fromIndex * first result to be considered(>=0) * @param numberOfLogs * the max number of queriable logs to be returned (>=0) * @param field * @param order * @return the queriable logs having the given value for the given int index * @throws SQueriableLogException * @since 6.0 */ List getLogs(int startIndex, int maxResults, final String field, final OrderByType order) throws SBonitaReadException; /** * Gets the queriable logs number matching to the given QueryOptions. * * @param QueryOptions * The criterion used to search sQueriableLog * @return queriable logs number matching to the given searchOptions. * @throws SBonitaReadException * @since 6.0 */ long getNumberOfLogs(final QueryOptions searchOptions) throws SBonitaReadException; /** * Gets the queriable logs matching to the given searchOptions. * * @param searchOptions * The criterion used to search sQueriableLog * @return logs list matching to the given searchOptions. * @throws SBonitaReadException * @since 6.0 */ List searchLogs(final QueryOptions searchOptions) throws SBonitaReadException; } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/services/QueriableLoggerStrategy.java ================================================ /** * 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.services; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; /** * @author Elias Ricken de Medeiros * @author Celine Souchet * @author Matthieu Chaffotte */ public interface QueriableLoggerStrategy { boolean isLoggable(String actionType, SQueriableLogSeverity severity); } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/services/SQueriableLogException.java ================================================ /** * 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.services; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SQueriableLogException extends SBonitaException { private static final long serialVersionUID = -1052948025332283376L; public SQueriableLogException(final String message) { super(message); } public SQueriableLogException(final Throwable cause) { super(cause); } public SQueriableLogException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/services/SQueriableLogNotFoundException.java ================================================ /** * 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.services; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SQueriableLogNotFoundException extends SBonitaException { private static final long serialVersionUID = -7562889822565946111L; public SQueriableLogNotFoundException(final long logId) { super("The log identified by :" + logId + " does not exist"); } } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/services/impl/BatchLogSynchronization.java ================================================ /** * Copyright (C) 2017 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.services.impl; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.SPersistenceException; import org.bonitasoft.engine.transaction.BonitaTransactionSynchronization; /** * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros */ class BatchLogSynchronization implements BonitaTransactionSynchronization { private final PersistenceService persistenceService; private final QueriableLoggerImpl queriableLogger; private final List logs = new ArrayList<>(); public BatchLogSynchronization(PersistenceService persistenceService, QueriableLoggerImpl queriableLogger) { super(); this.persistenceService = persistenceService; this.queriableLogger = queriableLogger; } @Override public void afterCompletion(final int transactionState) { queriableLogger.clearSynchronization(); } @Override public void beforeCompletion() { if (!logs.isEmpty()) { try { persistenceService.insertInBatch(logs); // this is mandatory (probably because we are in a synchronization) persistenceService.flushStatements(); } catch (final SPersistenceException e) { throw new SBonitaRuntimeException(e); } } } public void addLog(final SQueriableLog sQueriableLog) { logs.add(sQueriableLog); } List getLogs() { return logs; } } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/services/impl/QueriableLogSessionProviderImpl.java ================================================ /** * 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.services.impl; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.ExceptionUtils; import org.bonitasoft.engine.services.QueriableLogSessionProvider; import org.bonitasoft.engine.session.SSessionNotFoundException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.session.model.SSession; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; import org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Celine Souchet */ @Slf4j public class QueriableLogSessionProviderImpl implements QueriableLogSessionProvider { private final SessionService sessionService; private final ReadSessionAccessor sessionAccessor; private final ThreadLocal localSession = new ThreadLocal<>(); public QueriableLogSessionProviderImpl(final SessionService sessionService, final ReadSessionAccessor sessionAccessor) { this.sessionService = sessionService; this.sessionAccessor = sessionAccessor; } private SSession getSession() { SSession session = localSession.get(); try { if (session == null || session.getId() != sessionAccessor.getSessionId()) { long sessionId; sessionId = sessionAccessor.getSessionId(); session = sessionService.getSession(sessionId); localSession.set(session); } } catch (final SessionIdNotSetException e) { // system: no session return null; } catch (final SSessionNotFoundException e) { log.warn(ExceptionUtils.printLightWeightStacktrace(e)); } return session; } @Override public String getUserId() { final SSession session = getSession(); if (session != null) { return session.getUserName(); } return SessionService.SYSTEM; } @Override public String getClusterNode() { final SSession session = getSession(); if (session != null) { return session.getClusterNode(); } return ""; } } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/services/impl/QueriableLogUpdater.java ================================================ /** * 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.services.impl; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.services.QueriableLogSessionProvider; /** * @author Elias Ricken de Medeiros */ @Slf4j public class QueriableLogUpdater { private static final int MAX_MESSAGE_LENGTH = 255; private final QueriableLogSessionProvider sessionProvider; private final PlatformService platformService; public QueriableLogUpdater(final QueriableLogSessionProvider sessionProvider, final PlatformService platformService) { this.sessionProvider = sessionProvider; this.platformService = platformService; } public SQueriableLog buildFinalLog(final String callerClassName, final String callerMethodName, final SQueriableLog sQueriableLog) { final SQueriableLog.SQueriableLogBuilder builder = sQueriableLog.toBuilder(); final String rawMessage = sQueriableLog.getRawMessage(); if (rawMessage.length() > MAX_MESSAGE_LENGTH) { final String truncatedMessage = rawMessage.substring(0, MAX_MESSAGE_LENGTH); builder.rawMessage(truncatedMessage); if (log.isInfoEnabled()) { final StringBuilder stb = new StringBuilder(); stb.append("The queriable log message is too long and will be truncated to "); stb.append(MAX_MESSAGE_LENGTH); stb.append(" characters. The original message is '"); stb.append(rawMessage); stb.append("'. It will be truncated to '"); stb.append(truncatedMessage); stb.append("'"); log.info(stb.toString()); } } return builder.callerClassName(callerClassName).callerMethodName(callerMethodName) .userId(sessionProvider.getUserId()).clusterNode(sessionProvider.getClusterNode()) .productVersion(platformService.getSPlatformProperties().getPlatformVersion()) .build(); } } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/services/impl/QueriableLoggerImpl.java ================================================ /** * 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.services.impl; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.QueriableLogSessionProvider; import org.bonitasoft.engine.services.QueriableLoggerService; import org.bonitasoft.engine.services.QueriableLoggerStrategy; import org.bonitasoft.engine.services.SQueriableLogNotFoundException; import org.bonitasoft.engine.transaction.STransactionNotFoundException; import org.bonitasoft.engine.transaction.TransactionService; /** * @author Elias Ricken de Medeiros * @author Bole Zhang * @author Matthieu Chaffotte * @author Celine Souchet */ public class QueriableLoggerImpl implements QueriableLoggerService { private final PersistenceService persistenceService; private final QueriableLoggerStrategy loggerStrategy; private final TransactionService transactionService; private final QueriableLogUpdater logUpdater; private final ThreadLocal synchronizations = new ThreadLocal<>(); public QueriableLoggerImpl(PersistenceService persistenceService, TransactionService transactionService, QueriableLoggerStrategy loggerStrategy, QueriableLogSessionProvider sessionProvider, PlatformService platformService) { this.transactionService = transactionService; this.persistenceService = persistenceService; this.loggerStrategy = loggerStrategy; logUpdater = new QueriableLogUpdater(sessionProvider, platformService); } @Override public long getNumberOfLogs() throws SBonitaReadException { final Map emptyMap = Collections.emptyMap(); return persistenceService .selectOne(new SelectOneDescriptor<>("getNumberOfLogs", emptyMap, SQueriableLog.class, Long.class)); } @Override public List getLogs(final int startIndex, final int maxResults, final String field, final OrderByType order) throws SBonitaReadException { return persistenceService.selectList( new SelectListDescriptor<>("getLogs", null, SQueriableLog.class, new QueryOptions(startIndex, maxResults, SQueriableLog.class, field, order))); } @Override public long getNumberOfLogs(final QueryOptions searchOptions) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SQueriableLog.class, searchOptions, null); } @Override public List searchLogs(final QueryOptions searchOptions) throws SBonitaReadException { return persistenceService.searchEntity(SQueriableLog.class, searchOptions, null); } @Override public void log(final String callerClassName, final String callerMethodName, final SQueriableLog... queriableLogs) { if (queriableLogs.length == 0) { return; } BatchLogSynchronization synchronization = getBatchLogSynchronization(); for (SQueriableLog log : queriableLogs) { if (isLoggable(log.getActionType(), log.getSeverity())) { log = logUpdater.buildFinalLog(callerClassName, callerMethodName, log); synchronization.addLog(log); } } } private synchronized BatchLogSynchronization getBatchLogSynchronization() { BatchLogSynchronization synchronization = synchronizations.get(); if (synchronization == null) { synchronization = new BatchLogSynchronization(persistenceService, this); synchronizations.set(synchronization); registerSynchronization(synchronization); } return synchronization; } private void registerSynchronization(BatchLogSynchronization synchro) { try { transactionService.registerBonitaSynchronization(synchro); } catch (STransactionNotFoundException e) { throw new SBonitaRuntimeException(e); } } void clearSynchronization() { synchronizations.remove(); } @Override public boolean isLoggable(final String actionType, final SQueriableLogSeverity severity) { return loggerStrategy.isLoggable(actionType, severity); } @Override public SQueriableLog getLog(final long logId) throws SQueriableLogNotFoundException, SBonitaReadException { final SQueriableLog selectOne = persistenceService .selectById(new SelectByIdDescriptor<>(SQueriableLog.class, logId)); if (selectOne == null) { throw new SQueriableLogNotFoundException(logId); } return selectOne; } } ================================================ FILE: services/bonita-log/src/main/java/org/bonitasoft/engine/services/impl/SimpleQueriableLoggerStrategy.java ================================================ /** * 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.services.impl; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.services.QueriableLoggerStrategy; /** * @author Elias Ricken de Medeiros * @author Celine Souchet * @author Matthieu Chaffotte */ public class SimpleQueriableLoggerStrategy implements QueriableLoggerStrategy { private final boolean loggable = System.getProperty("org.bonitasoft.engine.services.queryablelog.disable") == null; @Override public boolean isLoggable(final String actionType, final SQueriableLogSeverity severity) { return loggable; } } ================================================ FILE: services/bonita-log/src/main/resources/org/bonitasoft/engine/queriablelogger/model/impl/hibernate/queriablelogger.queries.hbm.xml ================================================ SELECT log FROM org.bonitasoft.engine.queriablelogger.model.SQueriableLog AS log DELETE FROM org.bonitasoft.engine.queriablelogger.model.SQueriableLog SELECT COUNT(log.id) FROM org.bonitasoft.engine.queriablelogger.model.SQueriableLog AS log SELECT log FROM org.bonitasoft.engine.queriablelogger.model.SQueriableLog AS log SELECT log FROM org.bonitasoft.engine.queriablelogger.model.SQueriableLog AS log SELECT COUNT(log.id) FROM org.bonitasoft.engine.queriablelogger.model.SQueriableLog AS log ================================================ FILE: services/bonita-log/src/test/java/org/bonitasoft/engine/queriablelogger/model/SQueriableLogTest.java ================================================ /** * 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.queriablelogger.model; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Elias Ricken de Medeiros */ public class SQueriableLogTest { private final static Logger LOGGER = LoggerFactory.getLogger(SQueriableLogTest.class); private SQueriableLog queriableLog; @Rule public TestName name = new TestName(); @Before public void setUp() { LOGGER.info("Testing : {}", name.getMethodName()); queriableLog = SQueriableLog.builder().initializeNow().build(); } @After public void tearDown() { LOGGER.info("Tested: {}", name.getMethodName()); queriableLog = null; } @Test public void testSetID() { assertEquals(0, queriableLog.getId()); queriableLog.setId(123L); assertEquals(123L, queriableLog.getId()); } @Test public void testSetDateElements() { assertTrue(queriableLog.getYear() > 0); assertTrue(queriableLog.getMonth() > 0); assertTrue(queriableLog.getDayOfYear() > 0); assertTrue(queriableLog.getWeekOfYear() > 0); } @Test public void testSetUsername() { assertEquals(null, queriableLog.getUserId()); queriableLog.setUserId("john"); assertEquals("john", queriableLog.getUserId()); } @Test public void testSetThreadID() { assertEquals(Thread.currentThread().getId(), queriableLog.getThreadNumber()); } @Test public void testSetClusterNode() { assertEquals(null, queriableLog.getClusterNode()); queriableLog.setClusterNode("node1"); assertEquals("node1", queriableLog.getClusterNode()); } @Test public void testSetProductVersion() { assertEquals(null, queriableLog.getProductVersion()); queriableLog.setProductVersion("SP-5.4.1"); assertEquals("SP-5.4.1", queriableLog.getProductVersion()); } @Test public void testSetSeverity() { assertEquals(null, queriableLog.getSeverity()); queriableLog.setSeverity(SQueriableLogSeverity.BUSINESS); assertEquals(SQueriableLogSeverity.BUSINESS, queriableLog.getSeverity()); } @Test public void testSetActionType() { assertEquals(null, queriableLog.getActionType()); queriableLog.setActionType("excecute_connector"); assertEquals("excecute_connector", queriableLog.getActionType()); } @Test public void testSetActionScope() { assertEquals(null, queriableLog.getActionScope()); queriableLog.setActionScope("setVariable"); assertEquals("setVariable", queriableLog.getActionScope()); } @Test public void testSetActionStatus() { assertEquals(-1, queriableLog.getActionStatus()); queriableLog.setActionStatus(1); assertEquals(1, queriableLog.getActionStatus()); } @Test public void testSetRawMessage() { assertEquals(null, queriableLog.getRawMessage()); queriableLog.setRawMessage("successfully executed"); assertEquals("successfully executed", queriableLog.getRawMessage()); } @Test public void testSetNumericIndexes() { final int numberOfNumericIndexes = 5; for (int i = 0; i < numberOfNumericIndexes; i++) { assertEquals(-1L, queriableLog.getNumericIndex(i)); } for (int i = 0; i < numberOfNumericIndexes; i++) { queriableLog.setNumericIndex(i, i); assertEquals(i, queriableLog.getNumericIndex(i)); } } @Test(expected = IllegalStateException.class) public void testSetNumericIndexOutOfRange() { queriableLog.setNumericIndex(10, 10); } @Test(expected = IllegalStateException.class) public void getNumericIndexOutOfRange() { queriableLog.getNumericIndex(10); } @Test public void testSetCallerClassName() { assertNull(queriableLog.getCallerClassName()); queriableLog.setCallerClassName("RecorderImpl.class"); assertEquals("RecorderImpl.class", queriableLog.getCallerClassName()); } @Test public void testSetCallerMethodName() { assertNull(queriableLog.getCallerMethodName()); queriableLog.setCallerMethodName("insertRecord"); assertEquals("insertRecord", queriableLog.getCallerMethodName()); } } ================================================ FILE: services/bonita-log/src/test/java/org/bonitasoft/engine/services/impl/QueriableLogSessionProviderImplTest.java ================================================ /** * 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.services.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.session.model.SSession; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class QueriableLogSessionProviderImplTest { /** * */ private static final long SESSION_ID = 5010L; @Mock private SessionService sessionService; @Mock private ReadSessionAccessor sessionAccessor; @Mock private SSession session; @InjectMocks private QueriableLogSessionProviderImpl sessionProvider; @Before public void setUp() throws Exception { doReturn(SESSION_ID).when(sessionAccessor).getSessionId(); } @Test public void getUserId_should_return_session_user_name_if_there_is_a_session() throws Exception { //given doReturn(session).when(sessionService).getSession(SESSION_ID); doReturn("john").when(session).getUserName(); //when String userId = sessionProvider.getUserId(); //then assertThat(userId).isEqualTo("john"); } @Test public void getUserId_should_return_system_if_there_is_no_session() throws Exception { //given doReturn(null).when(sessionService).getSession(SESSION_ID); //when String userId = sessionProvider.getUserId(); //then assertThat(userId).isEqualTo("system"); } @Test public void getClusterNode_should_return_the_session_cluster_node_if_there_is_a_session() throws Exception { //given doReturn(session).when(sessionService).getSession(SESSION_ID); doReturn("aNode").when(session).getClusterNode(); //when String clusterNode = sessionProvider.getClusterNode(); //then assertThat(clusterNode).isEqualTo("aNode"); } @Test public void getClusterNode_should_return_empty_string_if_there_is_no_session() throws Exception { //given doReturn(null).when(sessionService).getSession(SESSION_ID); //when String clusterNode = sessionProvider.getClusterNode(); //then assertThat(clusterNode).isEmpty(); } } ================================================ FILE: services/bonita-log/src/test/java/org/bonitasoft/engine/services/impl/QueriableLogUpdaterTest.java ================================================ /** * 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.services.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.platform.model.SPlatformProperties; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.services.QueriableLogSessionProvider; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class QueriableLogUpdaterTest { @Mock private QueriableLogSessionProvider sessionProvider; @Mock private PlatformService platformService; private SQueriableLog log; @InjectMocks private QueriableLogUpdater updater; @Before public void setUp() { given(sessionProvider.getUserId()).willReturn("user"); given(sessionProvider.getClusterNode()).willReturn("node1"); final SPlatformProperties properties = mock(SPlatformProperties.class); given(platformService.getSPlatformProperties()).willReturn(properties); given(properties.getPlatformVersion()).willReturn("platform.version"); log = getLogBuilderWithMandatoryFields(); } private SQueriableLog getLogBuilderWithMandatoryFields() { final SQueriableLog log = SQueriableLog.builder().build(); log.setSeverity(SQueriableLogSeverity.INTERNAL); log.setActionType("insert"); log.setActionStatus(SQueriableLog.STATUS_OK); log.setActionScope("scope"); log.setRawMessage("message"); return log; } @Test public void buildFinalLog_should_add_meta_information() { //when final SQueriableLog finalLog = updater.buildFinalLog("class", "method", log); //then assertThat(finalLog.getCallerClassName()).isEqualTo("class"); assertThat(finalLog.getCallerMethodName()).isEqualTo("method"); assertThat(finalLog.getUserId()).isEqualTo("user"); assertThat(finalLog.getClusterNode()).isEqualTo("node1"); assertThat(finalLog.getProductVersion()).isEqualTo("platform.version"); } @Test public void raw_message_should_be_trunked_to_255_characters() { //given final String base = "base message "; final StringBuilder stb = new StringBuilder(); while (stb.length() <= 255) { stb.append(base); } final SQueriableLog log = getLogBuilderWithMandatoryFields(); log.setRawMessage(stb.toString()); //when final SQueriableLog finalLog = updater.buildFinalLog("class", "method", log); //then assertThat(finalLog.getRawMessage()).hasSize(255); } } ================================================ FILE: services/bonita-log/src/test/java/org/bonitasoft/engine/services/impl/QueriableLoggerImplTest.java ================================================ /** * 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.services.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.List; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.platform.model.SPlatformProperties; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.QueriableLogSessionProvider; import org.bonitasoft.engine.services.QueriableLoggerStrategy; import org.bonitasoft.engine.transaction.TransactionService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class QueriableLoggerImplTest { private static final String FIRST_ACTION = "first_action"; private static final String SECOND_ACTION = "sencond_action"; @Mock private PersistenceService persistenceService; @Mock private TransactionService transactionService; @Mock private QueriableLoggerStrategy loggerStrategy; @Mock private QueriableLogSessionProvider sessionProvider; private SQueriableLog log1 = SQueriableLog.builder().build(); private SQueriableLog log2 = SQueriableLog.builder().build(); @Mock private PlatformService platformService; @Captor private ArgumentCaptor> selectOneCaptor; @Captor private ArgumentCaptor> selectByIdCaptor; @Captor private ArgumentCaptor> selectListCaptor; @Captor private ArgumentCaptor synchroCaptor; @InjectMocks private QueriableLoggerImpl logService; @Before public void setUp() { SPlatformProperties platformProperties = mock(SPlatformProperties.class); log1.setActionType(FIRST_ACTION); log1.setSeverity(SQueriableLogSeverity.INTERNAL); log1.setRawMessage("msg"); log2.setActionType(SECOND_ACTION); log2.setSeverity(SQueriableLogSeverity.INTERNAL); log2.setRawMessage("new action msg"); doReturn(platformProperties).when(platformService).getSPlatformProperties(); doReturn("6.3").when(platformProperties).getPlatformVersion(); } @Test public void isLoggable_should_return_true_if_strategy_is_loggable() { // given doReturn(true).when(loggerStrategy).isLoggable(FIRST_ACTION, SQueriableLogSeverity.INTERNAL); // when boolean loggable = logService.isLoggable(FIRST_ACTION, SQueriableLogSeverity.INTERNAL); // then assertThat(loggable).isTrue(); } @Test public void isLoggable_should_return_false_if_strategy_is_not_loggable() { // given doReturn(false).when(loggerStrategy).isLoggable(FIRST_ACTION, SQueriableLogSeverity.INTERNAL); // when boolean loggable = logService.isLoggable(FIRST_ACTION, SQueriableLogSeverity.INTERNAL); // then assertThat(loggable).isFalse(); } @Test public void calling_log_must_insert_log_if_isLogable() throws Exception { // given doReturn(true).when(loggerStrategy).isLoggable(FIRST_ACTION, SQueriableLogSeverity.INTERNAL); doReturn("walter.bates").when(sessionProvider).getUserId(); doReturn("node1").when(sessionProvider).getClusterNode(); // when logService.log("CallerClass", "callerMethod", log1); // then verify(transactionService).registerBonitaSynchronization(synchroCaptor.capture()); BatchLogSynchronization value = synchroCaptor.getValue(); assertThat(value.getLogs()).hasSize(1); SQueriableLog insertedLog = value.getLogs().get(0); assertThat(insertedLog.getActionType()).isEqualTo(FIRST_ACTION); assertThat(insertedLog.getClusterNode()).isEqualTo("node1"); assertThat(insertedLog.getCallerClassName()).isEqualTo("CallerClass"); assertThat(insertedLog.getCallerMethodName()).isEqualTo("callerMethod"); assertThat(insertedLog.getProductVersion()).isEqualTo("6.3"); assertThat(insertedLog.getUserId()).isEqualTo("walter.bates"); } @Test public void calling_log_must_not_insert_log_if_is_not_logable() throws Exception { // given doReturn(false).when(loggerStrategy).isLoggable(FIRST_ACTION, SQueriableLogSeverity.INTERNAL); // when logService.log("CallerClass", "callerMethod", log1); // then verify(persistenceService, never()).insert(log1); } @Test public void calling_log_must_insert_all_logable_logs() throws Exception { // given doReturn(true).when(loggerStrategy).isLoggable(FIRST_ACTION, SQueriableLogSeverity.INTERNAL); doReturn(true).when(loggerStrategy).isLoggable(SECOND_ACTION, SQueriableLogSeverity.INTERNAL); // when logService.log("CallerClass", "callerMethod", log1, log2); // then verify(transactionService).registerBonitaSynchronization(synchroCaptor.capture()); BatchLogSynchronization value = synchroCaptor.getValue(); assertThat(value.getLogs()).extracting("actionType").containsExactly(FIRST_ACTION, SECOND_ACTION); } @Test public void getNumberOfLogs_should_return_number_of_logs_from_persistence_service() throws Exception { // given doReturn(15L).when(persistenceService).selectOne(selectOneCaptor.capture()); // when long numberOfLogs = logService.getNumberOfLogs(); // then assertThat(numberOfLogs).isEqualTo(15); assertThat(selectOneCaptor.getValue().getQueryName()).isEqualTo("getNumberOfLogs"); } @Test public void getLogs_should_return_logs_from_persitence_service() throws Exception { // given List persistenceLogs = Arrays.asList(log1, log2); doReturn(persistenceLogs).when(persistenceService).selectList(selectListCaptor.capture()); // when List logs = logService.getLogs(5, 10, "id", OrderByType.ASC); // then assertThat(logs).isEqualTo(persistenceLogs); SelectListDescriptor selectListDescriptor = selectListCaptor.getValue(); assertThat(selectListDescriptor.getPageSize()).isEqualTo(10); assertThat(selectListDescriptor.getStartIndex()).isEqualTo(5); } @Test public void getNumberOfEntities_should_return_number_of_entities_from_persistence_service() throws Exception { // given QueryOptions queryOptions = new QueryOptions(0, 10); doReturn(20L).when(persistenceService).getNumberOfEntities(SQueriableLog.class, queryOptions, null); // when long numberOfLogs = logService.getNumberOfLogs(queryOptions); // then assertThat(numberOfLogs).isEqualTo(20L); } @Test public void searchLogs_should_return_logs_from_persistence_service() throws Exception { // given QueryOptions queryOptions = new QueryOptions(0, 10); List persistenceLogs = Arrays.asList(log1, log2); doReturn(persistenceLogs).when(persistenceService).searchEntity(SQueriableLog.class, queryOptions, null); // when List logs = logService.searchLogs(queryOptions); // then assertThat(logs).isEqualTo(persistenceLogs); } @Test public void getLog_should_return_log_from_persistence_service() throws Exception { // given doReturn(log1).when(persistenceService).selectById(any()); // when SQueriableLog log = logService.getLog(100L); // then assertThat(log).isEqualTo(log1); } } ================================================ FILE: services/bonita-log/src/test/java/org/bonitasoft/engine/services/impl/SimpleQueriableLoggerStrategyTest.java ================================================ /** * 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.services.impl; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.junit.Before; import org.junit.Test; public class SimpleQueriableLoggerStrategyTest { private static final String DISABLE_LOG_KEY = "org.bonitasoft.engine.services.queryablelog.disable"; @Before public void setUp() { System.clearProperty(DISABLE_LOG_KEY); } @Test public void isLogable_shoud_return_false_if_system_property_queryablelogDisable_is_null() { System.setProperty(DISABLE_LOG_KEY, "true"); SimpleQueriableLoggerStrategy strategy = new SimpleQueriableLoggerStrategy(); assertThat(strategy.isLoggable("anyAction", SQueriableLogSeverity.BUSINESS)).isFalse(); } @Test public void isLogable_shoud_return_true_if_system_property_queryablelogDisable_is_not_null() { SimpleQueriableLoggerStrategy strategy = new SimpleQueriableLoggerStrategy(); assertThat(strategy.isLoggable("anyAction", SQueriableLogSeverity.BUSINESS)).isTrue(); } } ================================================ FILE: services/bonita-log/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: services/bonita-page/build.gradle ================================================ dependencies { api project(':services:bonita-log') api project(':services:bonita-builder') api project(':services:bonita-events') api project(':services:bonita-persistence') api project(':services:bonita-commons') api project(':services:bonita-authorization') api project(':bpm:bonita-common') testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.logback testImplementation testFixtures(project(':bpm:bonita-common')) annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/AbstractSPage.java ================================================ /** * 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.page; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; import org.bonitasoft.engine.persistence.PersistentObject; @Data @SuperBuilder @NoArgsConstructor @MappedSuperclass public class AbstractSPage implements PersistentObject { @Id private long id; private String name; private String description; private String displayName; private long installationDate; private long installedBy; private boolean provided; @Builder.Default private boolean editable = true; @Builder.Default private boolean removable = true; private long lastModificationDate; private long lastUpdatedBy; private String contentName; private String contentType; private long processDefinitionId; /** * A MD5 sum hash of the content of the page. * It is used only for verifying if a provided page has change or not. * It is not used (and not filled) for user pages */ private String pageHash; public AbstractSPage(final AbstractSPage sPage) { name = sPage.getName(); description = sPage.getDescription(); displayName = sPage.getDisplayName(); installationDate = sPage.getInstallationDate(); installedBy = sPage.getInstalledBy(); provided = sPage.isProvided(); lastModificationDate = sPage.getLastModificationDate(); lastUpdatedBy = sPage.getLastUpdatedBy(); contentName = sPage.getContentName(); contentType = sPage.getContentType(); processDefinitionId = sPage.getProcessDefinitionId(); editable = sPage.isEditable(); removable = sPage.isRemovable(); pageHash = sPage.getPageHash(); } public AbstractSPage(final String name, final long installationDate, final long installedBy, final boolean provided, final String contentName) { this.name = name; this.installationDate = installationDate; this.installedBy = installedBy; this.provided = provided; this.contentType = SContentType.PAGE; this.contentName = contentName; } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/AuthorizationRule.java ================================================ /** * 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.page; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SExecutionException; /** * Represent a rule to execute to grant access or not. * author Emmanuel Duchastenier */ public interface AuthorizationRule { /** * Execute this rule and, according to the context, says whether the rule is valid. * * @param key the page mapping key * @param context the information necessary to execute this rule. * @return true if allowed, false otherwise.If determination cannot be fulfilled, an Exception should be thrown. * @throws SExecutionException exception thrown if authorization cannot be determined. */ boolean isAllowed(String key, Map context) throws SExecutionException; /** * @return the identifier for this authorization rule */ String getId(); } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/PageMappingService.java ================================================ /** * 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.page; import java.io.Serializable; import java.util.List; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SDeletionException; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * Service that allows to map an arbitrary String key to a (Custom) Page or URL. * Also allows to process the URL or Page return by providing an URLAdapter implementation that will be called * automatically. * Also allows to execute Authorization checkings by specifying rules to execute before returning the Page or URL. * * @see URLAdapter * @author Baptiste Mesta * @author Emmanuel Duchastenier * @author Matthieu Chaffotte */ public interface PageMappingService { /** * @param key the key used to retrieve the mapping * @param pageId the id of the custom page * @param authorizationRules the names of the authorization rules to execute * @return the created page mapping * @throws SObjectCreationException when there is an issue while creating this object * @since 7.0.0 */ SPageMapping create(String key, Long pageId, List authorizationRules) throws SObjectCreationException; /** * @param key the key used to retrieve the mapping * @param url the external URL the mapping points to * @param urlAdapter the name of the URL adapter that transform the URL in case of an external URL. i.e. it can add * parameters * @param authorizationRules the names of the authorization rules to execute * @return the created page mapping * @throws SObjectCreationException when there is an issue while creating this object * @since 7.0.0 */ SPageMapping create(String key, String url, String urlAdapter, List authorizationRules) throws SObjectCreationException; /** * @param key the key of the page mapping to retrieve * @return the page mapping having this key * @throws SObjectNotFoundException when there is no mapping having this key */ SPageMapping get(String key) throws SObjectNotFoundException, SBonitaReadException; /** * @param pageMapping * @param context * @param executeAuthorizationRules * @return * @throws SExecutionException * @throws SAuthorizationException */ SPageURL resolvePageURL(SPageMapping pageMapping, Map context, boolean executeAuthorizationRules) throws SExecutionException, SAuthorizationException; /** * delete this page mapping * * @param SPageMapping the page mapping to delete */ void delete(SPageMapping SPageMapping) throws SDeletionException; /** * update the given page mapping * * @param pageMapping the pageMapping to update * @param pageId the id of the page or null * @throws SObjectModificationException */ void update(SPageMapping pageMapping, Long pageId) throws SObjectModificationException, SObjectNotFoundException, SBonitaReadException; /** * update the given page mapping * * @param pageMapping the pageMapping to update * @param url the URL or null * @param urlAdapter the new URL adapter to use * @throws SObjectModificationException */ void update(SPageMapping pageMapping, String url, String urlAdapter) throws SObjectModificationException, SObjectNotFoundException, SBonitaReadException; /** * Gets the paginated mappings of the page. * * @param pageId the page identifier * @param startIndex the start index * @param maxResults the max results * @return the paginated mappings of the page * @throws SBonitaReadException */ List get(long pageId, int startIndex, int maxResults) throws SBonitaReadException; } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/PageService.java ================================================ /** * 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.page; import java.util.List; import java.util.Properties; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Baptiste Mesta * @author Laurent Leseigneur */ public interface PageService { String PROPERTIES_FILE_NAME = "page.properties"; String PROPERTIES_DISPLAY_NAME = "displayName"; String PROPERTIES_DESCRIPTION = "description"; String PROPERTIES_CONTENT_TYPE = "contentType"; String PROPERTIES_NAME = "name"; String PAGE = "PAGE"; /** * add a page using the zip in parameters and the given properties * * @param page * @param content * @return * @throws SObjectCreationException * @throws SObjectAlreadyExistsException * @throws SInvalidPageZipException * @throws SInvalidPageTokenException */ SPage addPage(SPage page, byte[] content) throws SObjectCreationException, SObjectAlreadyExistsException, SInvalidPageZipException, SInvalidPageTokenException; SPage insertPage(SPage page, byte[] content) throws SObjectAlreadyExistsException, SObjectCreationException; SPage checkIfPageAlreadyExists(SPage page) throws SBonitaReadException; SPage getPage(long pageId) throws SBonitaReadException, SObjectNotFoundException; SPage getPageByName(String pageName) throws SBonitaReadException; SPage buildPage(byte[] content, String contentName, long userId, boolean provided, boolean removable, boolean editable) throws SInvalidPageZipException, SInvalidPageTokenException; /** * Read the content of a page in a zip * * @param content * the page content * @return * the properties of the page stored in the page.properties * @throws SInvalidPageZipMissingIndexException * if the page is missing an index.html or Index.groovy * @throws SInvalidPageZipMissingAPropertyException * if the page is missing mandatory field in the page.properties * @throws SInvalidPageZipInconsistentException * if the zip is not a valid zip file or unreadable * @throws SInvalidPageZipMissingPropertiesException */ Properties readPageZip(final byte[] content) throws SInvalidPageZipMissingIndexException, SInvalidPageZipMissingAPropertyException, SInvalidPageZipInconsistentException, SInvalidPageZipMissingPropertiesException, SInvalidPageTokenException; long getNumberOfPages(QueryOptions options) throws SBonitaReadException; void deletePage(long pageId) throws SObjectModificationException, SObjectNotFoundException; byte[] getPageContent(long pageId) throws SBonitaReadException, SObjectNotFoundException; List searchPages(QueryOptions options) throws SBonitaReadException; SPage updatePage(long pageId, EntityUpdateDescriptor updateDescriptor) throws SObjectModificationException, SObjectAlreadyExistsException, SInvalidPageTokenException; void updatePageContent(long pageId, byte[] content, String contentName) throws SBonitaException; /** * add a page using the zip in parameters, it get all informations from the page.properties file contain inside the * zip * * @param content * @param userId * @return * @throws SObjectCreationException * @throws SObjectAlreadyExistsException * @throws SInvalidPageZipException * @throws SInvalidPageTokenException */ SPage addPage(final byte[] content, final String contentName, long userId) throws SObjectCreationException, SObjectAlreadyExistsException, SInvalidPageZipException, SInvalidPageTokenException; /** * get a page attached to a process * * @param name * @param processDefinitionId * @return * @throws SBonitaReadException */ SPage getPageByNameAndProcessDefinitionId(String name, long processDefinitionId) throws SBonitaReadException; /** * get a list of page attached to a process * * @param processDefinitionId * @param fromIndex * @param numberOfResults * @return * @throws SBonitaReadException */ List getPageByProcessDefinitionId(long processDefinitionId, int fromIndex, int numberOfResults) throws SBonitaReadException; void updatePageContent(long pageId, byte[] content, String contentName, SPageUpdateBuilder pageUpdateBuilder) throws SObjectAlreadyExistsException, SObjectModificationException, SInvalidPageZipException, SInvalidPageTokenException; } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/PageServiceListener.java ================================================ /** * 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.page; import org.bonitasoft.engine.commons.exceptions.SDeletionException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Laurent Leseigneur * @author Matthieu Chaffotte * @since 7.0 */ public interface PageServiceListener { void pageInserted(SPage page, byte[] content) throws SObjectCreationException; void pageDeleted(SPage page) throws SDeletionException, SBonitaReadException; void pageUpdated(AbstractSPage page, byte[] content) throws SObjectModificationException; } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SAuthorizationException.java ================================================ /** * 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.page; /** * Thrown when access to a given page or URL is not allowed for a given key. * author Emmanuel Duchastenier */ public class SAuthorizationException extends Exception { public SAuthorizationException(String message) { super(message); } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SContentType.java ================================================ /** * 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.page; /** * @author Laurent Leseigneur */ public interface SContentType { String PAGE = "page"; String FORM = "form"; String API_EXTENSION = "apiExtension"; } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SInvalidPageTokenException.java ================================================ /** * 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.page; import org.bonitasoft.engine.commons.exceptions.SBonitaException; public class SInvalidPageTokenException extends SBonitaException { public SInvalidPageTokenException(final String message) { super(message); } private static final long serialVersionUID = 7161910286756340441L; } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SInvalidPageZipException.java ================================================ /** * 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.page; import java.io.IOException; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * content of the page is not valid * * @author Baptiste Mesta */ public abstract class SInvalidPageZipException extends SBonitaException { private static final long serialVersionUID = -7263291210428082852L; public SInvalidPageZipException(final String message) { super(message); } public SInvalidPageZipException(final String string, final IOException e) { super(string, e); } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SInvalidPageZipInconsistentException.java ================================================ /** * 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.page; import java.io.IOException; /** * @author Baptiste Mesta */ public class SInvalidPageZipInconsistentException extends SInvalidPageZipException { public SInvalidPageZipInconsistentException(String message) { super(message); } public SInvalidPageZipInconsistentException(String string, IOException e) { super(string, e); } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SInvalidPageZipMissingAPropertyException.java ================================================ /** * 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.page; /** * @author Baptiste Mesta */ public class SInvalidPageZipMissingAPropertyException extends SInvalidPageZipException { private String fields; public SInvalidPageZipMissingAPropertyException(String fields) { super("Missing fields in the page.properties: " + fields); this.fields = fields; } public String getFields() { return fields; } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SInvalidPageZipMissingIndexException.java ================================================ /** * 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.page; /** * @author Baptiste Mesta */ public class SInvalidPageZipMissingIndexException extends SInvalidPageZipException { public SInvalidPageZipMissingIndexException() { super("Missing Index.groovy or index.html"); } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SInvalidPageZipMissingPropertiesException.java ================================================ /** * 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.page; /** * @author Baptiste Mesta */ public class SInvalidPageZipMissingPropertiesException extends SInvalidPageZipException { public SInvalidPageZipMissingPropertiesException() { super("Missing page.properties"); } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPage.java ================================================ /** * 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.page; import javax.persistence.Entity; import javax.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import lombok.experimental.SuperBuilder; /** * @author Matthieu Chaffotte */ @Data @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) @SuperBuilder @NoArgsConstructor @Entity @Table(name = "page") public class SPage extends AbstractSPage { public static final String NAME = "name"; public static final String DESCRIPTION = "description"; public static final String INSTALLATION_DATE = "installationDate"; public static final String INSTALLED_BY = "installedBy"; public static final String CONTENT_TYPE = "contentType"; public static final String PROCESS_DEFINITION_ID = "processDefinitionId"; public static final String ID = "id"; public static final String PROVIDED = "provided"; public static final String DISPLAY_NAME = "displayName"; public static final String LAST_MODIFICATION_DATE = "lastModificationDate"; public static final String LAST_UPDATE_BY = "lastUpdateBy"; public SPage(final String name, final String description, final String displayName, final long installationDate, final long installedBy, final boolean provided, final long lastModificationDate, final long lastUpdatedBy, final String contentName) { super(name, installationDate, installedBy, provided, contentName); setDescription(description); setDisplayName(displayName); setProvided(provided); setLastModificationDate(lastModificationDate); setLastUpdatedBy(lastUpdatedBy); } public SPage(final String name, final String description, final String displayName, final long installationDate, final long installedBy, final boolean provided, boolean editable, final long lastModificationDate, final long lastUpdatedBy, final String contentName) { this(name, installationDate, installedBy, provided, contentName); setDescription(description); setDisplayName(displayName); setProvided(provided); setLastModificationDate(lastModificationDate); setLastUpdatedBy(lastUpdatedBy); setEditable(editable); } public SPage(final SPage sPage) { super(sPage); } public SPage(final String name, final long installationDate, final long installedBy, final boolean provided, final String contentName) { setName(name); setInstallationDate(installationDate); setInstalledBy(installedBy); setProvided(provided); setContentName(contentName); setContentType(SContentType.PAGE); } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageLogBuilder.java ================================================ /** * 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.page; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Baptiste Mesta */ public interface SPageLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageLogBuilderFactory.java ================================================ /** * 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.page; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Baptiste Mesta */ public interface SPageLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageMapping.java ================================================ /** * 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.page; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Transient; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Baptiste Mesta */ @Entity @Data @Builder @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(exclude = "authorizationRules") @Table(name = "page_mapping") public class SPageMapping implements PersistentObject { public static final String COMMA_DELIMITER = ","; @Id private long id; @Column(name = "key_") private String key; @Column(name = "pageid") private Long pageId; private String url; @Column(name = "urladapter") private String urlAdapter; @Column(name = "lastupdatedate") private long lastUpdateDate; @Column(name = "lastupdatedby") private long lastUpdatedBy; @Column(name = "page_authoriz_rules") private String pageAuthorizRules; @Transient @Builder.Default private List authorizationRules = new ArrayList<>(); private void parseRules() { if (pageAuthorizRules != null) { authorizationRules.clear(); for (StringTokenizer stringTk = new StringTokenizer(pageAuthorizRules, COMMA_DELIMITER, false); stringTk .hasMoreTokens();) { String rule = stringTk.nextToken(); authorizationRules.add(rule); } } } private void buildRulesAsString() { pageAuthorizRules = null; if (authorizationRules != null && !authorizationRules.isEmpty()) { pageAuthorizRules = ""; for (String authorizationRule : authorizationRules) { pageAuthorizRules += (authorizationRule + COMMA_DELIMITER); } } } public List getPageAuthorizationRules() { parseRules(); // Need to do it here because Hibernate does not call setter to set fields from DB to Object return authorizationRules; } public void setPageAuthorizationRules(List rules) { authorizationRules = rules; buildRulesAsString(); } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageURL.java ================================================ /** * 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.page; /** * author Emmanuel Duchastenier */ public class SPageURL { private String url; private Long pageId; public String getUrl() { return url; } public Long getPageId() { return pageId; } public SPageURL(String url, Long pageId) { this.url = url; this.pageId = pageId; } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageUpdateBuilder.java ================================================ /** * 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.page; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; public interface SPageUpdateBuilder { EntityUpdateDescriptor done(); /** * @deprecated since bonita 7.13 the update of name is no more supported, it will not be take into account on the * page update */ @Deprecated SPageUpdateBuilder updateName(String value); SPageUpdateBuilder updateDescription(String value); SPageUpdateBuilder updateDisplayName(String value); SPageUpdateBuilder updateLastModificationDate(long currentTimeMillis); SPageUpdateBuilder updateLastUpdatedBy(long userId); SPageUpdateBuilder updateContentName(String value); SPageUpdateBuilder updateContentType(String contentType); SPageUpdateBuilder updateProcessDefinitionId(Long processDedfinitionId); SPageUpdateBuilder updatePageHash(String hash); SPageUpdateBuilder markNonProvided(); } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageUpdateBuilderFactory.java ================================================ /** * 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.page; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; public interface SPageUpdateBuilderFactory { SPageUpdateBuilder createNewInstance(EntityUpdateDescriptor descriptor); } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageUpdateContentBuilder.java ================================================ /** * 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.page; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; public interface SPageUpdateContentBuilder { EntityUpdateDescriptor done(); SPageUpdateContentBuilder updateContent(byte[] content); } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/SPageWithContent.java ================================================ /** * 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.page; import javax.persistence.Entity; import javax.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import lombok.experimental.SuperBuilder; import org.hibernate.annotations.Type; @Data @SuperBuilder @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true, exclude = "content") @NoArgsConstructor @Entity @Table(name = "page") public class SPageWithContent extends AbstractSPage { @Type(type = "materialized_blob") private byte[] content; public SPageWithContent(final SPage sPage, final byte[] pagContent) { super(sPage); setContent(pagContent); } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/URLAdapter.java ================================================ /** * 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.page; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SExecutionException; /** * @author Baptiste Mesta */ public interface URLAdapter { /** * adapt an url based on a context * * @param url the base url * @param key the url key * @param context the provided context * @return the new url * @throws SExecutionException when the URL rewriting fails */ String adapt(String url, String key, Map context) throws SExecutionException; /** * @return the identifier for this url adapter */ String getId(); } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/ApiExtensionPageServiceListenerImpl.java ================================================ /** * 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.page.impl; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Properties; import org.bonitasoft.engine.commons.exceptions.SDeletionException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.page.AbstractSPage; import org.bonitasoft.engine.page.PageMappingService; import org.bonitasoft.engine.page.PageServiceListener; import org.bonitasoft.engine.page.SContentType; import org.bonitasoft.engine.page.SInvalidPageZipMissingPropertiesException; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.page.SPageMapping; import org.bonitasoft.engine.persistence.SBonitaReadException; /** * @author Laurent Leseigneur * @author Matthieu Chaffotte */ public class ApiExtensionPageServiceListenerImpl implements PageServiceListener { private static final int MAX_RESULTS = 100; private final PageMappingService pageMappingService; private final SPageContentHelper helper; public ApiExtensionPageServiceListenerImpl(final PageMappingService pageMappingService) { this(pageMappingService, new SPageContentHelper()); } public ApiExtensionPageServiceListenerImpl(final PageMappingService pageMappingService, final SPageContentHelper helper) { super(); this.pageMappingService = pageMappingService; this.helper = helper; } @Override public void pageInserted(final SPage sPage, final byte[] content) throws SObjectCreationException { if (SContentType.API_EXTENSION.equals(sPage.getContentType())) { try { addPageMapping(sPage, helper.loadPageProperties(content)); } catch (final IOException | SInvalidPageZipMissingPropertiesException e) { throw new SObjectCreationException(e); } } } private void addPageMapping(final SPage page, final Properties apiProperties) throws SObjectCreationException, IOException, SInvalidPageZipMissingPropertiesException { final List mappings = getKeysOfPageMappings(apiProperties); for (final String mapping : mappings) { pageMappingService.create(mapping, page.getId(), Collections. emptyList()); } } private String getMappingKey(final String method, final String pathTemplate) { return new StringBuilder().append("apiExtension|").append(method).append("|").append(pathTemplate).toString(); } private String getRequiredProperty(final Properties properties, final String propertyName) throws SObjectCreationException { final String property = (String) properties.get(propertyName); if (property == null || property.trim().length() == 0) { throw new SObjectCreationException("the property '" + propertyName + "' is missing or is empty"); } return property.trim(); } @Override public void pageDeleted(final SPage page) throws SBonitaReadException, SDeletionException { if (SContentType.API_EXTENSION.equals(page.getContentType())) { List mappings; do { mappings = pageMappingService.get(page.getId(), 0, MAX_RESULTS); for (final SPageMapping mapping : mappings) { pageMappingService.delete(mapping); } } while (mappings.size() == MAX_RESULTS); } } @Override public void pageUpdated(final AbstractSPage page, final byte[] content) throws SObjectModificationException { if (SContentType.API_EXTENSION.equals(page.getContentType())) { try { updateMappings(page, helper.loadPageProperties(content)); } catch (SBonitaReadException | SDeletionException | SObjectCreationException | SInvalidPageZipMissingPropertiesException | IOException e) { throw new SObjectModificationException(e); } } } private void updateMappings(final AbstractSPage page, final Properties apiProperties) throws SObjectCreationException, SBonitaReadException, SDeletionException { final List keys = getKeysOfPageMappings(apiProperties); final List existingKeys = new ArrayList<>(); List mappings; do { mappings = pageMappingService.get(page.getId(), 0, MAX_RESULTS); for (final SPageMapping mapping : mappings) { if (keys.contains(mapping.getKey())) { existingKeys.add(mapping.getKey()); } else { pageMappingService.delete(mapping); } } } while (mappings.size() == MAX_RESULTS); keys.removeAll(existingKeys); for (final String key : keys) { pageMappingService.create(key, page.getId(), Collections. emptyList()); } } private List getKeysOfPageMappings(final Properties apiProperties) throws SObjectCreationException { final List keys = new ArrayList<>(); final String apiExtensions = getRequiredProperty(apiProperties, "apiExtensions"); final String[] resourceNames = apiExtensions.split(","); for (final String resource : resourceNames) { final String resourceName = resource.trim(); final String method = getRequiredProperty(apiProperties, resourceName + ".method"); String pathTemplate = getRequiredProperty(apiProperties, resourceName + ".pathTemplate"); if (pathTemplate != null && pathTemplate.startsWith("/")) { pathTemplate = pathTemplate.substring(1); } try { getRequiredProperty(apiProperties, resourceName + ".className"); } catch (SObjectCreationException e) { // if the property 'className' isn't present, then the rest api might be used in legacy mode (!= compiled mode) -> we look for the property 'classFileName' getRequiredProperty(apiProperties, resourceName + ".classFileName"); } getRequiredProperty(apiProperties, resourceName + ".permissions"); keys.add(getMappingKey(method, pathTemplate)); } return keys; } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/AuthorizationRulesInjector.java ================================================ /** * 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.page.impl; import java.util.List; import org.bonitasoft.engine.page.AuthorizationRule; /** * author Emmanuel Duchastenier */ public class AuthorizationRulesInjector { private List authorizationRules; private PageMappingServiceImpl pageMappingService; public AuthorizationRulesInjector(PageMappingServiceImpl pageMappingService) { this.pageMappingService = pageMappingService; } /** * Called by Spring automatically * * @param authorizationRules */ public void setAuthorizationRules(List authorizationRules) { this.authorizationRules = authorizationRules; } public void injectAuthorizationRules() { if (authorizationRules != null) { pageMappingService.setAuthorizationRules(authorizationRules); } } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/PageMappingServiceImpl.java ================================================ /** * 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.page.impl; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SDeletionException; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.page.AuthorizationRule; import org.bonitasoft.engine.page.PageMappingService; import org.bonitasoft.engine.page.SAuthorizationException; import org.bonitasoft.engine.page.SPageMapping; import org.bonitasoft.engine.page.SPageURL; import org.bonitasoft.engine.page.URLAdapter; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.session.SSessionNotFoundException; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; import org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException; /** * @author Baptiste Mesta */ public class PageMappingServiceImpl implements PageMappingService { public static final String PAGE_MAPPING = "PAGE_MAPPING"; private final Recorder recorder; private final ReadPersistenceService persistenceService; private final SessionService sessionService; private final ReadSessionAccessor sessionAccessor; private final Map urlAdapterMap; private final Map authorizationRuleMap; public PageMappingServiceImpl(final Recorder recorder, final ReadPersistenceService persistenceService, final SessionService sessionService, final ReadSessionAccessor sessionAccessor) { this.recorder = recorder; this.persistenceService = persistenceService; this.sessionService = sessionService; this.sessionAccessor = sessionAccessor; urlAdapterMap = new HashMap<>(); authorizationRuleMap = new HashMap<>(); } public void setURLAdapters(final List urlAdapters) { for (final URLAdapter urlAdapter : urlAdapters) { urlAdapterMap.put(urlAdapter.getId(), urlAdapter); } } public void setAuthorizationRules(final List authorizationRules) { for (final AuthorizationRule authorizationRule : authorizationRules) { authorizationRuleMap.put(authorizationRule.getId(), authorizationRule); } } @Override public SPageMapping create(final String key, final Long pageId, final List authorizationRules) throws SObjectCreationException { SPageMapping pageMapping; try { pageMapping = findMapping(key); } catch (final SBonitaReadException e) { throw new SObjectCreationException(String.format("Failed to get page mapping %s", key), e); } if (pageMapping == null) { final SPageMapping entity = new SPageMapping(); entity.setPageId(pageId); entity.setPageAuthorizationRules(authorizationRules); entity.setKey(key); return insert(entity); } throw new SObjectCreationException( String.format("Mapping key %s already exists for page with id %s", key, pageMapping.getPageId())); } SPageMapping insert(final SPageMapping entity) throws SObjectCreationException { try { recorder.recordInsert(new InsertRecord(entity), PAGE_MAPPING); } catch (final SRecorderException e) { throw new SObjectCreationException(e); } return entity; } @Override public SPageMapping create(final String key, final String url, final String urlAdapter, final List authorizationRules) throws SObjectCreationException { SPageMapping pageMapping = null; try { pageMapping = findMapping(key); } catch (final SBonitaReadException e) { throw new SObjectCreationException(String.format("Failed to get page mapping %s", key), e); } if (pageMapping == null) { final SPageMapping entity = new SPageMapping(); entity.setUrl(url); entity.setUrlAdapter(urlAdapter); entity.setPageAuthorizationRules(authorizationRules); entity.setKey(key); return insert(entity); } throw new SObjectCreationException( String.format("Mapping key %s already exists for page with id %s", key, pageMapping.getPageId())); } @Override public SPageMapping get(final String key) throws SObjectNotFoundException, SBonitaReadException { final SPageMapping sPageMapping = findMapping(key); if (sPageMapping == null) { throw new SObjectNotFoundException("No page mapping found with key " + key); } return sPageMapping; } private SPageMapping findMapping(final String key) throws SBonitaReadException { return persistenceService.selectOne(new SelectOneDescriptor("getPageMappingByKey", Collections . singletonMap("key", key), SPageMapping.class)); } @Override public SPageURL resolvePageURL(final SPageMapping pageMapping, final Map context, final boolean executeAuthorizationRules) throws SExecutionException, SAuthorizationException { if (executeAuthorizationRules) { final List pageAuthorizationRules = pageMapping.getPageAuthorizationRules(); if (!isAllowedToAccess(pageMapping, context, pageAuthorizationRules)) { throw new SAuthorizationException( "Access to Page or URL with key " + pageMapping.getKey() + " is not allowed"); } } String url = pageMapping.getUrl(); final String urlAdapter = pageMapping.getUrlAdapter(); if (urlAdapter != null) { url = getUrlAdapter(urlAdapter).adapt(url, pageMapping.getKey(), context); } return new SPageURL(url, pageMapping.getPageId()); } protected boolean isAllowedToAccess(final SPageMapping pageMapping, final Map context, final List pageAuthorizationRules) throws SExecutionException { boolean authorized = true; for (final String rule : pageAuthorizationRules) { final AuthorizationRule authorizationRule = authorizationRuleMap.get(rule); if (authorizationRule == null) { throw new SExecutionException( "Authorization rule " + rule + " is not known. Cannot check if authorized or not."); } if (authorizationRule.isAllowed(pageMapping.getKey(), context)) { return true; } else { authorized = false; } } return authorized; } private URLAdapter getUrlAdapter(final String urlAdapterName) throws SExecutionException { final URLAdapter urlAdapter = urlAdapterMap.get(urlAdapterName); if (urlAdapter == null) { throw new SExecutionException( "unable to execute the url adapter " + urlAdapterName + " because it does not exists"); } return urlAdapter; } @Override public void delete(final SPageMapping sPageMapping) throws SDeletionException { try { recorder.recordDelete(new DeleteRecord(sPageMapping), PAGE_MAPPING); } catch (final SRecorderException e) { throw new SDeletionException("Unable to delete the page mapping with key " + sPageMapping.getKey(), e); } } @Override public void update(final SPageMapping pageMapping, final Long pageId) throws SObjectModificationException, SObjectNotFoundException, SBonitaReadException { update(pageMapping, pageId, null, null); } void update(final SPageMapping pageMapping, final Long pageId, final String url, final String urlAdapter) throws SObjectNotFoundException, SBonitaReadException, SObjectModificationException { try { update(pageMapping, getEntityUpdateDescriptor(pageId, url, urlAdapter)); } catch (SSessionNotFoundException | SessionIdNotSetException | SRecorderException e) { throw new SObjectModificationException(e); } } @Override public void update(final SPageMapping pageMapping, final String url, final String urlAdapter) throws SObjectModificationException, SObjectNotFoundException, SBonitaReadException { update(pageMapping, null, url, urlAdapter); } EntityUpdateDescriptor getEntityUpdateDescriptor(final Long pageId, final String url, final String urlAdapter) throws SSessionNotFoundException, SessionIdNotSetException { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); descriptor.addField("pageId", pageId); descriptor.addField("url", url); descriptor.addField("urlAdapter", urlAdapter); descriptor.addField("lastUpdatedBy", getSessionUserId()); descriptor.addField("lastUpdateDate", System.currentTimeMillis()); return descriptor; } private long getSessionUserId() throws SSessionNotFoundException, SessionIdNotSetException { return sessionService.getLoggedUserFromSession(sessionAccessor); } void update(final SPageMapping pageMapping, final EntityUpdateDescriptor descriptor) throws SObjectNotFoundException, SBonitaReadException, SRecorderException { recorder.recordUpdate(UpdateRecord.buildSetFields(pageMapping, descriptor), PAGE_MAPPING); } @Override public List get(final long pageId, final int startIndex, final int maxResults) throws SBonitaReadException { final QueryOptions options = new QueryOptions(startIndex, maxResults); final SelectListDescriptor listDescriptor = new SelectListDescriptor( "getPageMappingByPageId", Collections. singletonMap("pageId", pageId), SPageMapping.class, options); return persistenceService.selectList(listDescriptor); } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/PageServiceImpl.java ================================================ /** * 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.page.impl; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Properties; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.authorization.PermissionService; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.exceptions.SDeletionException; import org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.commons.io.IOUtil; import org.bonitasoft.engine.page.AbstractSPage; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.page.PageServiceListener; import org.bonitasoft.engine.page.SContentType; import org.bonitasoft.engine.page.SInvalidPageTokenException; import org.bonitasoft.engine.page.SInvalidPageZipException; import org.bonitasoft.engine.page.SInvalidPageZipInconsistentException; import org.bonitasoft.engine.page.SInvalidPageZipMissingAPropertyException; import org.bonitasoft.engine.page.SInvalidPageZipMissingIndexException; import org.bonitasoft.engine.page.SInvalidPageZipMissingPropertiesException; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.page.SPageLogBuilder; import org.bonitasoft.engine.page.SPageUpdateBuilder; import org.bonitasoft.engine.page.SPageUpdateBuilderFactory; import org.bonitasoft.engine.page.SPageWithContent; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadOnlySelectByIdDescriptor; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.QueriableLoggerService; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ @Slf4j @Service("pageService") public class PageServiceImpl implements PageService { private static final String QUERY_GET_PAGE_BY_NAME = "getPageByName"; private static final String QUERY_GET_PAGE_BY_NAME_AND_PROCESS_DEFINITION_ID = "getPageByNameAndProcessDefinitionId"; private static final String QUERY_GET_PAGE_BY_PROCESS_DEFINITION_ID = "getPageByProcessDefinitionId"; private static final String METHOD_DELETE_PAGE = "deletePage"; private static final String METHOD_NAME_ADD_PAGE = "addPage"; private static final String METHOD_UPDATE_PAGE = "updatePage"; public static final String PAGE_TOKEN_PREFIX = "custompage_"; public static final String INDEX_GROOVY = "Index.groovy"; public static final String INDEX_HTML = "index.html"; public static final String RESOURCES_INDEX_HTML = "resources/index.html"; private static final String API_EXTENSIONS = "apiExtensions"; // Refers to a compiled class inside a jar, used by REST API extensions in compiled mode private static final String COMPILED_CLASS_NAME = "className"; // Refers to a Groovy source file, used by REST API extensions in legacy mode (compilation is performed at runtime) private static final String GROOVY_CLASS_FILENAME = "classFileName"; private static final String THEME_CSS = "resources/theme.css"; private final ReadPersistenceService persistenceService; private final Recorder recorder; private final QueriableLoggerService queriableLoggerService; private final ReadSessionAccessor sessionAccessor; private final SessionService sessionService; private final PermissionService permissionService; private final SPageContentHelper helper; private List pageServiceListeners; public PageServiceImpl(final ReadPersistenceService persistenceService, final Recorder recorder, final QueriableLoggerService queriableLoggerService, ReadSessionAccessor sessionAccessor, SessionService sessionService, PermissionService permissionService) { this.persistenceService = persistenceService; this.recorder = recorder; this.queriableLoggerService = queriableLoggerService; this.sessionAccessor = sessionAccessor; this.sessionService = sessionService; this.permissionService = permissionService; helper = new SPageContentHelper(); } @Override public SPage addPage(final SPage page, final byte[] content) throws SObjectCreationException, SObjectAlreadyExistsException, SInvalidPageZipException, SInvalidPageTokenException { try { final Map zipContent = unzip(content); var pageProperties = getPreviousPageProperties(content); if (isAnAPIExtension(pageProperties)) { checkApiControllerExists(zipContent, pageProperties); } else { checkZipContainsRequiredEntries(zipContent); } checkPageNameIsValid(page.getName(), page.isProvided()); checkPageDisplayNameIsValid(page.getDisplayName()); return insertPage(page, content); } catch (final IOException e) { throw new SInvalidPageZipInconsistentException("Error while reading zip file", e); } } // @VisibleForTesting Map unzip(byte[] content) throws IOException { return IOUtil.unzip(content); } @Override public SPage addPage(final byte[] content, final String contentName, final long userId) throws SObjectCreationException, SObjectAlreadyExistsException, SInvalidPageZipException, SInvalidPageTokenException { SPage sPage = buildPage(content, contentName, userId, false, true, true); return insertPage(sPage, content); } @Override public SPage getPageByNameAndProcessDefinitionId(final String name, final long processDefinitionId) throws SBonitaReadException { final Map inputParameters = new HashMap<>(); inputParameters.put("pageName", name); inputParameters.put("processDefinitionId", processDefinitionId); return persistenceService.selectOne(new SelectOneDescriptor<>(QUERY_GET_PAGE_BY_NAME_AND_PROCESS_DEFINITION_ID, inputParameters, SPage.class)); } @Override public List getPageByProcessDefinitionId(final long processDefinitionId, final int fromIndex, final int numberOfResults) throws SBonitaReadException { final Map inputParameters = new HashMap<>(); inputParameters.put("processDefinitionId", processDefinitionId); final OrderByOption orderByOption = new OrderByOption(SPage.class, SPageFields.PAGE_NAME, OrderByType.ASC); final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfResults, Collections.singletonList(orderByOption)); return persistenceService.selectList( new SelectListDescriptor<>(QUERY_GET_PAGE_BY_PROCESS_DEFINITION_ID, inputParameters, SPage.class, queryOptions)); } @Override public SPage buildPage(final byte[] content, final String contentName, final long userId, final boolean provided, boolean removable, boolean editable) throws SInvalidPageZipException, SInvalidPageTokenException { final Properties pageProperties = readPageZip(content, provided); long currentTime = System.currentTimeMillis(); return SPage.builder().name(pageProperties.getProperty(PageService.PROPERTIES_NAME)) .description(pageProperties.getProperty(PageService.PROPERTIES_DESCRIPTION)) .displayName(pageProperties.getProperty(PageService.PROPERTIES_DISPLAY_NAME)) .installationDate(currentTime).installedBy(userId).provided(provided) .lastModificationDate(currentTime).lastUpdatedBy(userId) .removable(removable).editable(editable) .contentName(contentName) .contentType(pageProperties.getProperty(PROPERTIES_CONTENT_TYPE, SContentType.PAGE)).build(); } private void addPermissionsFromPageProperties(String pageName, Properties pageProperties) { permissionService.addPermissions(pageName, pageProperties); } private void removePermissionsDeclaredInPageProperties(Properties pageProperties) { permissionService.removePermissions(pageProperties); } @Override public Properties readPageZip(final byte[] content) throws SInvalidPageZipMissingIndexException, SInvalidPageZipMissingAPropertyException, SInvalidPageZipInconsistentException, SInvalidPageZipMissingPropertiesException, SInvalidPageTokenException { return readPageZip(content, false); } Properties readPageZip(final byte[] content, final boolean provided) throws SInvalidPageZipMissingIndexException, SInvalidPageZipMissingAPropertyException, SInvalidPageZipInconsistentException, SInvalidPageZipMissingPropertiesException, SInvalidPageTokenException { final Properties pageProperties; if (content == null) { throw new SInvalidPageZipInconsistentException("Content can't be null"); } try { pageProperties = getPreviousPageProperties(content); final Map zipContent = unzip(content); if (isAnAPIExtension(pageProperties)) { checkApiControllerExists(zipContent, pageProperties); } else { checkZipContainsRequiredEntries(zipContent); } checkPageNameIsValid(pageProperties.getProperty(PageService.PROPERTIES_NAME), provided); checkPageDisplayNameIsValid(pageProperties.getProperty(PageService.PROPERTIES_DISPLAY_NAME)); } catch (final IOException e) { throw new SInvalidPageZipInconsistentException("Error while reading zip file", e); } return pageProperties; } /** * For each declared API, the following verifications are performed: * - If a property 'className' is defined, then we assume that the required jar file is present in the lib folder * and no * further verification is performed. * - If the property 'className' isn't defined, then we look for the property 'classFileName' (legacy mode). The * property * points to a Groovy source file, which will be compiled at runtime. We verify that this Groovy file exists in the * archive. */ private void checkApiControllerExists(Map zipContent, Properties pageProperties) throws SInvalidPageZipInconsistentException, SInvalidPageZipMissingAPropertyException { final Set entrySet = zipContent.keySet(); final String declaredApis = pageProperties.getProperty(API_EXTENSIONS); if (declaredApis == null || declaredApis.isEmpty()) { throw new SInvalidPageZipMissingAPropertyException(API_EXTENSIONS); } final String[] apis = declaredApis.split(","); for (final String api : apis) { String className = pageProperties.getProperty(api.trim() + "." + COMPILED_CLASS_NAME); if (className == null) { final String classFileName = pageProperties.getProperty(api.trim() + "." + GROOVY_CLASS_FILENAME); if (classFileName == null || classFileName.isEmpty()) { throw new SInvalidPageZipMissingAPropertyException(api.trim() + "." + GROOVY_CLASS_FILENAME); } if (!entrySet.contains(classFileName.trim())) { throw new SInvalidPageZipInconsistentException( String.format("RestAPIController %s has not been found in archive.", classFileName.trim())); } } } } private boolean isAnAPIExtension(Properties pageProperties) { return Objects.equals(SContentType.API_EXTENSION, pageProperties.get(PageService.PROPERTIES_CONTENT_TYPE)); } @Override public SPage insertPage(final SPage page, final byte[] content) throws SObjectAlreadyExistsException, SObjectCreationException { final SPageLogBuilder logBuilder = getPageLog(ActionType.CREATED, "Adding a new page with name " + page.getName()); try { final SPageWithContent pageContent = new SPageWithContent(page, content); final SPage pageByName = checkIfPageAlreadyExists(page); if (null != pageByName) { initiateLogBuilder(page.getId(), SQueriableLog.STATUS_FAIL, logBuilder, METHOD_NAME_ADD_PAGE); throwAlreadyExistsException(pageByName.getName()); } recorder.recordInsert(new InsertRecord(pageContent), PAGE); addPermissionsFromPageProperties(page.getName(), getPreviousPageProperties(content)); page.setId(pageContent.getId()); notifyPageInsert(page, content); return page; } catch (SRecorderException | SBonitaReadException | IOException | SInvalidPageZipMissingPropertiesException re) { throw new SObjectCreationException(re); } } private void notifyPageInsert(final SPage page, final byte[] content) throws SObjectCreationException { for (final PageServiceListener pageServiceListener : pageServiceListeners) { pageServiceListener.pageInserted(page, content); } } @Override public SPage checkIfPageAlreadyExists(final SPage page) throws SBonitaReadException { SPage existingPage; if (page.getProcessDefinitionId() > 0) { existingPage = getPageByNameAndProcessDefinitionId(page.getName(), page.getProcessDefinitionId()); } else { existingPage = getPageByName(page.getName()); } return existingPage; } private void checkPageDisplayNameIsValid(final String displayName) throws SInvalidPageZipMissingAPropertyException { if (displayName == null || displayName.length() == 0) { throw new SInvalidPageZipMissingAPropertyException(PageService.PROPERTIES_DISPLAY_NAME); } } private void checkPageNameIsValid(final String name, final boolean provided) throws SInvalidPageTokenException { if (name == null || name.isEmpty() || !provided && !name.matches(PAGE_TOKEN_PREFIX + "\\p{Alnum}+")) { throw new SInvalidPageTokenException( "Page name is not valid, it must contains only alpha numeric characters and start with " + PAGE_TOKEN_PREFIX); } } void checkZipContainsRequiredEntries(final Map zipContent) throws SInvalidPageZipMissingIndexException { final Set entrySet = zipContent.keySet(); for (final String entry : entrySet) { if (INDEX_GROOVY.equals(entry) || INDEX_HTML.equalsIgnoreCase(entry) || RESOURCES_INDEX_HTML.equalsIgnoreCase(entry) || THEME_CSS.equalsIgnoreCase(entry)) { return; } } throw new SInvalidPageZipMissingIndexException(); } SPageLogBuilder getPageLog(final ActionType actionType, final String message) { final SPageLogBuilder logBuilder = new SPageLogBuilderImpl(); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); return logBuilder; } @Override public SPage getPage(final long pageId) throws SBonitaReadException, SObjectNotFoundException { final SPage page = persistenceService.selectById(new SelectByIdDescriptor<>(SPage.class, pageId)); if (page == null) { throw new SObjectNotFoundException("Page with id " + pageId + " not found"); } return page; } @Override public SPage getPageByName(final String pageName) throws SBonitaReadException { return persistenceService .selectOne(new SelectOneDescriptor<>(QUERY_GET_PAGE_BY_NAME, Collections.singletonMap("pageName", pageName), SPage.class)); } @Override public long getNumberOfPages(final QueryOptions options) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SPage.class, options, null); } @Override public List searchPages(final QueryOptions options) throws SBonitaReadException { return persistenceService.searchEntity(SPage.class, options, null); } @Override public void deletePage(final long pageId) throws SObjectModificationException, SObjectNotFoundException { try { final SPage page = getPage(pageId); deletePageIfRemovable(page); } catch (final SBonitaReadException sbe) { throw new SObjectModificationException(sbe); } } private void deletePageIfRemovable(final SPage sPage) throws SObjectModificationException { if (!sPage.isRemovable()) { throw new SObjectModificationException( "The page '" + sPage.getName() + "' cannot be deleted because it is non-removable"); } else { deletePage(sPage); } } private void deletePage(final SPage sPage) throws SObjectModificationException { final SPageLogBuilder logBuilder = getPageLog(ActionType.DELETED, "Deleting page named: " + sPage.getName()); try { // Need to read previous version permissions from page properties before deleting the page: Properties pageProperties = getPreviousPageProperties(sPage); for (final PageServiceListener pageServiceListener : pageServiceListeners) { pageServiceListener.pageDeleted(sPage); } recorder.recordDelete(new DeleteRecord(sPage), PAGE); removePermissionsDeclaredInPageProperties(pageProperties); initiateLogBuilder(sPage.getId(), SQueriableLog.STATUS_OK, logBuilder, METHOD_DELETE_PAGE); } catch (SRecorderException | SBonitaReadException | SDeletionException | SObjectNotFoundException | SInvalidPageZipMissingPropertiesException | IOException re) { initiateLogBuilder(sPage.getId(), SQueriableLog.STATUS_FAIL, logBuilder, METHOD_DELETE_PAGE); throw new SObjectModificationException(re); } } private void initializeLogBuilder(final T logBuilder, final String message) { logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message); } private void updateLog(final ActionType actionType, final T logBuilder) { logBuilder.setActionType(actionType); } void initiateLogBuilder(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder, final String methodName) { logBuilder.actionScope(String.valueOf(objectId)); logBuilder.actionStatus(sQueriableLogStatus); logBuilder.objectId(objectId); final SQueriableLog log = logBuilder.build(); if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) { queriableLoggerService.log(this.getClass().getName(), methodName, log); } } @Override public byte[] getPageContent(final long pageId) throws SBonitaReadException, SObjectNotFoundException { final SPageWithContent page = persistenceService .selectById(new ReadOnlySelectByIdDescriptor<>(SPageWithContent.class, pageId)); if (page == null) { throw new SObjectNotFoundException("Page with id " + pageId + " not found"); } final byte[] content = page.getContent(); try { final Map contentAsMap = unzip(content); final byte[] bytes = contentAsMap.get(PROPERTIES_FILE_NAME); final Properties pageProperties = new Properties(); if (bytes != null) { pageProperties.load(new ByteArrayInputStream(bytes)); } pageProperties.put(PROPERTIES_NAME, page.getName()); pageProperties.put(PROPERTIES_DISPLAY_NAME, page.getDisplayName()); if (page.getDescription() != null) { pageProperties.put(PROPERTIES_DESCRIPTION, page.getDescription()); } contentAsMap.put(PROPERTIES_FILE_NAME, IOUtil.getPropertyAsString(pageProperties, "The name must start with 'custompage_'")); return IOUtil.zip(contentAsMap); } catch (final IOException e) { throw new SBonitaReadException("the page is not a valid zip file", e); } } @Override public SPage updatePage(final long pageId, final EntityUpdateDescriptor entityUpdateDescriptor) throws SObjectModificationException, SObjectAlreadyExistsException, SInvalidPageTokenException { try { final SPage sPage = persistenceService.selectById(new SelectByIdDescriptor<>(SPage.class, pageId)); updatePage(entityUpdateDescriptor, sPage); return sPage; } catch (SBonitaReadException e) { throw new SObjectModificationException(e); } } AbstractSPage updatePage(EntityUpdateDescriptor entityUpdateDescriptor, AbstractSPage sPage) throws SObjectModificationException, SObjectAlreadyExistsException { long pageId = sPage.getId(); final SPageLogBuilder logBuilder = getPageLog(ActionType.UPDATED, "Update a page with id " + pageId); final String logMethodName = METHOD_UPDATE_PAGE; try { if (!sPage.isEditable() && !isSystemSession()) { // Only non-editable pages can be updated (by the system) throw new SObjectModificationException( "The page '" + sPage.getName() + "' cannot be modified because it is not modifiable"); } if (entityUpdateDescriptor.getFields().containsKey(SPageFields.PAGE_PROCESS_DEFINITION_ID)) { checkPageDuplicateForProcessDefinition(sPage, logBuilder, Long.parseLong( entityUpdateDescriptor.getFields().get(SPageFields.PAGE_PROCESS_DEFINITION_ID).toString())); } recorder.recordUpdate(UpdateRecord.buildSetFields(sPage, entityUpdateDescriptor), PAGE); initiateLogBuilder(pageId, SQueriableLog.STATUS_OK, logBuilder, logMethodName); return sPage; } catch (SRecorderException | SBonitaReadException e) { initiateLogBuilder(pageId, SQueriableLog.STATUS_FAIL, logBuilder, logMethodName); throw new SObjectModificationException(e); } } // @VisibleForTesting protected boolean isSystemSession() { return sessionService.getLoggedUserFromSession(sessionAccessor) == SessionService.SYSTEM_ID; } protected void checkPageDuplicateForProcessDefinition( final AbstractSPage sPage, final SPageLogBuilder logBuilder, long sPageProcessDefinitionId) throws SBonitaReadException, SObjectAlreadyExistsException { String sPageName = sPage.getName(); if (sPageProcessDefinitionId > 0) { SPage page = getPageByNameAndProcessDefinitionId(sPageName, sPageProcessDefinitionId); if (page != null && page.getId() != sPage.getId()) { initiateLogBuilder(sPage.getId(), SQueriableLog.STATUS_FAIL, logBuilder, METHOD_UPDATE_PAGE); throwAlreadyExistsException(page.getName()); } } } private void throwAlreadyExistsException(final String pageName) throws SObjectAlreadyExistsException { throw new SObjectAlreadyExistsException("page with name " + pageName); } @Override public void updatePageContent(final long pageId, final byte[] content, final String contentName) throws SObjectModificationException, SInvalidPageZipException, SInvalidPageTokenException, SObjectAlreadyExistsException { updatePageContent(pageId, content, contentName, null); } @Override public void updatePageContent(long pageId, byte[] content, String contentName, SPageUpdateBuilder pageUpdateBuilder) throws SObjectModificationException, SInvalidPageZipException, SInvalidPageTokenException, SObjectAlreadyExistsException { final SPageLogBuilder logBuilder = getPageLog(ActionType.UPDATED, "Update a page with name " + pageId); final Properties pageProperties = readPageZip(content, false); final SPageWithContent sPageContent; try { sPageContent = persistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, pageId)); Properties previousPageProperties; if (sPageContent.getContent() == null || sPageContent.getContent().length == 0) { previousPageProperties = null; } else { // Need to read previous version permissions from page properties before deleting the page: try { previousPageProperties = getPreviousPageProperties(sPageContent.getContent()); } catch (SInvalidPageZipMissingPropertiesException | IOException e) { previousPageProperties = null; } } EntityUpdateDescriptor entityUpdateDescriptor = new EntityUpdateDescriptor(); entityUpdateDescriptor.addField("content", content); recorder.recordUpdate(UpdateRecord.buildSetFields(sPageContent, entityUpdateDescriptor), PAGE); // remove previous permissions before adding the new ones: if (previousPageProperties != null) { // this can happen when update-tool puts empty content in new inserted pages, // so that Engine startup update the page content, as it is detected as newer: removePermissionsDeclaredInPageProperties(previousPageProperties); } addPermissionsFromPageProperties(pageProperties.getProperty(PROPERTIES_NAME), pageProperties); initiateLogBuilder(pageId, SQueriableLog.STATUS_OK, logBuilder, METHOD_UPDATE_PAGE); } catch (SRecorderException | SBonitaReadException re) { initiateLogBuilder(pageId, SQueriableLog.STATUS_FAIL, logBuilder, METHOD_UPDATE_PAGE); throw new SObjectModificationException(re); } if (pageUpdateBuilder == null) { pageUpdateBuilder = BuilderFactory.get(SPageUpdateBuilderFactory.class) .createNewInstance(new EntityUpdateDescriptor()); } if (contentName == null) { pageUpdateBuilder.updateContentName(sPageContent.getContentName()); } else { pageUpdateBuilder.updateContentName(contentName); } pageUpdateBuilder.updateDescription(pageProperties.getProperty(PROPERTIES_DESCRIPTION)); pageUpdateBuilder.updateDisplayName(pageProperties.getProperty(PROPERTIES_DISPLAY_NAME)); pageUpdateBuilder.updateContentType(pageProperties.getProperty(PROPERTIES_CONTENT_TYPE, SContentType.PAGE)); if (sPageContent.isProvided()) { //update the md5 sum of the page only if it is provided pageUpdateBuilder.updatePageHash(DigestUtils.md5DigestAsHex(content)); } final AbstractSPage sPage = updatePage(pageUpdateBuilder.done(), sPageContent); for (final PageServiceListener pageServiceListener : pageServiceListeners) { pageServiceListener.pageUpdated(sPage, content); } } Properties getPreviousPageProperties(SPage sPage) throws IOException, SInvalidPageZipMissingPropertiesException, SBonitaReadException, SObjectNotFoundException { return getPreviousPageProperties(getPageContent(sPage.getId())); } Properties getPreviousPageProperties(byte[] content) throws IOException, SInvalidPageZipMissingPropertiesException { return helper.loadPageProperties(content); } @Autowired public void setPageServiceListeners(final List pageServiceListeners) { this.pageServiceListeners = pageServiceListeners; } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageContentFields.java ================================================ /** * 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.page.impl; public interface SPageContentFields { String PAGE_ID = "id"; String PAGE_CONTENT = "content"; } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageContentHelper.java ================================================ /** * 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.page.impl; import java.io.IOException; import java.io.StringReader; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.Properties; import org.bonitasoft.engine.commons.io.IOUtil; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.page.SInvalidPageZipMissingPropertiesException; /** * @author Laurent Leseigneur */ public class SPageContentHelper { public Properties loadPageProperties(final byte[] zipContent) throws IOException, SInvalidPageZipMissingPropertiesException { final Map content = IOUtil.unzip(zipContent); return loadPageProperties(content); } public Properties loadPageProperties(final Map content) throws SInvalidPageZipMissingPropertiesException, IOException { final byte[] pagePropertiesContent = content.get(PageService.PROPERTIES_FILE_NAME); if (pagePropertiesContent == null) { throw new SInvalidPageZipMissingPropertiesException(); } final Properties pageProperties = new Properties(); try (StringReader reader = new StringReader(new String(pagePropertiesContent, StandardCharsets.UTF_8))) { pageProperties.load(reader); } return pageProperties; } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageFields.java ================================================ /** * 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.page.impl; public interface SPageFields { String PAGE_ID = "id"; String PAGE_NAME = "name"; String PAGE_DISPLAY_NAME = "displayName"; String PAGE_DESCRIPTION = "description"; String PAGE_LAST_MODIFICATION_DATE = "lastModificationDate"; String PAGE_LAST_UPDATED_BY = "lastUpdatedBy"; String PAGE_CONTENT_NAME = "contentName"; String PAGE_CONTENT_TYPE = "contentType"; String PAGE_PROCESS_DEFINITION_ID = "processDefinitionId"; String PAGE_HASH = "pageHash"; String IS_PROVIDED = "provided"; } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageLogBuilderFactoryImpl.java ================================================ /** * 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.page.impl; import org.bonitasoft.engine.page.SPageLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Matthieu Chaffotte */ public class SPageLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SPageLogBuilderFactory { public static final int PAGE_INDEX = 1; public static final String PAGE_INDEX_NAME = "numericIndex2"; @Override public String getObjectIdKey() { return PAGE_INDEX_NAME; } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageLogBuilderImpl.java ================================================ /** * 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.page.impl; import org.bonitasoft.engine.page.SPageLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Baptiste Mesta */ public class SPageLogBuilderImpl extends CRUDELogBuilder implements SPageLogBuilder { private static final String PREFIX = "PAGE"; @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(SPageLogBuilderFactoryImpl.PAGE_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return PREFIX; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(SPageLogBuilderFactoryImpl.PAGE_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: page identifier"); } } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageUpdateBuilderFactoryImpl.java ================================================ /** * 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.page.impl; import org.bonitasoft.engine.page.SPageUpdateBuilder; import org.bonitasoft.engine.page.SPageUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; public class SPageUpdateBuilderFactoryImpl implements SPageUpdateBuilderFactory { @Override public SPageUpdateBuilder createNewInstance(EntityUpdateDescriptor descriptor) { return new SPageUpdateBuilderImpl(descriptor); } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageUpdateBuilderImpl.java ================================================ /** * 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.page.impl; import org.bonitasoft.engine.page.SPageUpdateBuilder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; public class SPageUpdateBuilderImpl implements SPageUpdateBuilder { private final EntityUpdateDescriptor descriptor; public SPageUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) { super(); this.descriptor = descriptor; } @Override public SPageUpdateBuilder updateName(final String value) { descriptor.addField(SPageFields.PAGE_NAME, value); return this; } @Override public SPageUpdateBuilder updateDescription(final String value) { descriptor.addField(SPageFields.PAGE_DESCRIPTION, value); return this; } @Override public SPageUpdateBuilder updateDisplayName(final String value) { descriptor.addField(SPageFields.PAGE_DISPLAY_NAME, value); return this; } @Override public SPageUpdateBuilder updateLastModificationDate(final long currentTimeMillis) { descriptor.addField(SPageFields.PAGE_LAST_MODIFICATION_DATE, currentTimeMillis); return this; } @Override public SPageUpdateBuilder updateLastUpdatedBy(final long userId) { descriptor.addField(SPageFields.PAGE_LAST_UPDATED_BY, userId); return this; } @Override public SPageUpdateBuilder updateContentName(final String value) { descriptor.addField(SPageFields.PAGE_CONTENT_NAME, value); return this; } @Override public SPageUpdateBuilder updateContentType(String contentType) { descriptor.addField(SPageFields.PAGE_CONTENT_TYPE, contentType); return this; } @Override public SPageUpdateBuilder updateProcessDefinitionId(Long processDefinitionId) { descriptor.addField(SPageFields.PAGE_PROCESS_DEFINITION_ID, processDefinitionId); return this; } @Override public SPageUpdateBuilder updatePageHash(String hash) { descriptor.addField(SPageFields.PAGE_HASH, hash); return this; } @Override public SPageUpdateBuilder markNonProvided() { descriptor.addField(SPageFields.IS_PROVIDED, false); return this; } @Override public EntityUpdateDescriptor done() { return descriptor; } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/SPageUpdateContentBuilderImpl.java ================================================ /** * 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.page.impl; import org.bonitasoft.engine.page.SPageUpdateContentBuilder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; public class SPageUpdateContentBuilderImpl implements SPageUpdateContentBuilder { private final EntityUpdateDescriptor descriptor; public SPageUpdateContentBuilderImpl(final EntityUpdateDescriptor descriptor) { super(); this.descriptor = descriptor; } @Override public EntityUpdateDescriptor done() { return descriptor; } @Override public SPageUpdateContentBuilder updateContent(byte[] content) { descriptor.addField("content", content); return null; } } ================================================ FILE: services/bonita-page/src/main/java/org/bonitasoft/engine/page/impl/UrlAdapterInjector.java ================================================ /** * 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.page.impl; import java.util.List; import org.bonitasoft.engine.page.URLAdapter; /** * @author Baptiste Mesta */ public class UrlAdapterInjector { private List urlAdapters; private PageMappingServiceImpl pageMappingService; public UrlAdapterInjector(PageMappingServiceImpl pageMappingService) { this.pageMappingService = pageMappingService; } public void setURLAdapters(List urlAdapters) { this.urlAdapters = urlAdapters; } public void injectUrlAdapters() { if (urlAdapters != null) { pageMappingService.setURLAdapters(urlAdapters); } } } ================================================ FILE: services/bonita-page/src/main/resources/org/bonitasoft/engine/page/impl/hibernate/page.queries.hbm.xml ================================================ SELECT page FROM org.bonitasoft.engine.page.SPage AS page WHERE page.id = :id SELECT page FROM org.bonitasoft.engine.page.SPage AS page WHERE page.name = :pageName AND page.processDefinitionId = 0 SELECT page FROM org.bonitasoft.engine.page.SPage AS page WHERE page.name = :pageName AND page.processDefinitionId = :processDefinitionId SELECT page FROM org.bonitasoft.engine.page.SPage AS page WHERE page.processDefinitionId = :processDefinitionId SELECT COUNT(page.id) FROM org.bonitasoft.engine.page.SPage AS page SELECT page FROM org.bonitasoft.engine.page.SPage AS page SELECT pagecontent FROM org.bonitasoft.engine.page.SPageWithContent AS pagecontent WHERE pagecontent.id = :id SELECT pagemapping FROM org.bonitasoft.engine.page.SPageMapping AS pagemapping WHERE pagemapping.key = :key SELECT pagemapping FROM org.bonitasoft.engine.page.SPageMapping AS pagemapping WHERE pagemapping.pageId = :pageId ================================================ FILE: services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/ApiExtensionPageServiceListenerImplTest.java ================================================ /** * 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.page.impl; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Properties; import org.bonitasoft.engine.commons.exceptions.SDeletionException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.page.PageMappingService; import org.bonitasoft.engine.page.SContentType; import org.bonitasoft.engine.page.SInvalidPageZipMissingPropertiesException; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.page.SPageMapping; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ApiExtensionPageServiceListenerImplTest { @Mock private PageMappingService pageMappingService; @Mock private SPageContentHelper helper; @InjectMocks private ApiExtensionPageServiceListenerImpl listener; private SPage buildPage(final long id) { final SPage page = new SPage(); page.setId(id); page.setContentType(SContentType.API_EXTENSION); return page; } @Rule public ExpectedException exception = ExpectedException.none(); @Test public void pageInserted_should_only_care_of_api_extension() throws Exception { final SPage page = new SPage(); page.setId(2L); page.setContentType(SContentType.PAGE); final byte[] content = new byte[] { 1, 0, 0 }; listener.pageInserted(page, content); verifyNoInteractions(pageMappingService, helper); } @Test public void pageInserted_should_add_api_resources_in_compiled_mode() throws Exception { final long pageId = 1983L; final SPage page = buildPage(pageId); final byte[] content = new byte[] { 1, 0, 0 }; final Properties properties = new Properties(); properties.setProperty("apiExtensions", "employee, address"); properties.setProperty("employee.method", "GET"); properties.setProperty("employee.pathTemplate", "employees"); properties.setProperty("employee.className", "com.company.Index"); properties.setProperty("employee.permissions", "myPermission"); properties.setProperty("address.method", "GET"); properties.setProperty("address.pathTemplate", "employees/{employeeId}/address"); properties.setProperty("address.className", "com.company.Index1"); properties.setProperty("address.permissions", "myPermission"); when(helper.loadPageProperties(content)).thenReturn(properties); listener.pageInserted(page, content); verify(pageMappingService).create("apiExtension|GET|employees", pageId, Collections. emptyList()); verify(pageMappingService).create("apiExtension|GET|employees/{employeeId}/address", pageId, Collections. emptyList()); verifyNoMoreInteractions(pageMappingService); } @Test public void pageInserted_should_add_api_resources_with_absolute_path() throws Exception { final long pageId = 1983L; final SPage page = buildPage(pageId); final byte[] content = new byte[] { 1, 0, 0 }; final Properties properties = new Properties(); properties.setProperty("apiExtensions", "employee, address"); properties.setProperty("employee.method", "GET"); properties.setProperty("employee.pathTemplate", "employees"); properties.setProperty("employee.className", "com.company.Index"); properties.setProperty("employee.permissions", "myPermission"); properties.setProperty("address.method", "GET"); properties.setProperty("address.pathTemplate", "/employees/{employeeId}/address"); properties.setProperty("address.className", "com.company.Index1"); properties.setProperty("address.permissions", "myPermission"); when(helper.loadPageProperties(content)).thenReturn(properties); listener.pageInserted(page, content); verify(pageMappingService).create("apiExtension|GET|employees", pageId, Collections. emptyList()); verify(pageMappingService).create("apiExtension|GET|employees/{employeeId}/address", pageId, Collections. emptyList()); verifyNoMoreInteractions(pageMappingService); } @Test public void pageInserted_should_add_api_resources_in_legacy_mode() throws Exception { final long pageId = 1983L; final SPage page = buildPage(pageId); final byte[] content = new byte[] { 1, 0, 0 }; final Properties properties = new Properties(); properties.setProperty("apiExtensions", "employee, address"); properties.setProperty("employee.method", "GET"); properties.setProperty("employee.pathTemplate", "employees"); properties.setProperty("employee.classFileName", "Index.groovy"); properties.setProperty("employee.permissions", "myPermission"); properties.setProperty("address.method", "GET"); properties.setProperty("address.pathTemplate", "employees/{employeeId}/address"); properties.setProperty("address.classFileName", "Index1.groovy"); properties.setProperty("address.permissions", "myPermission"); when(helper.loadPageProperties(content)).thenReturn(properties); listener.pageInserted(page, content); verify(pageMappingService).create("apiExtension|GET|employees", pageId, Collections. emptyList()); verify(pageMappingService).create("apiExtension|GET|employees/{employeeId}/address", pageId, Collections. emptyList()); verifyNoMoreInteractions(pageMappingService); } @Test public void pageInserted_should_throw_an_exception_if_the_file_does_not_exists() throws Exception { //given final SPage page = buildPage(10L); final byte[] content = new byte[] { 1, 0, 0 }; when(helper.loadPageProperties(content)).thenThrow(new SInvalidPageZipMissingPropertiesException()); //then exception.expect(SObjectCreationException.class); exception.expectMessage("Missing page.properties"); //when listener.pageInserted(page, content); } @Test public void pageInserted_should_throw_an_exception_if_an_io_exception_occurs() throws Exception { //given final SPage page = buildPage(10L); final byte[] content = new byte[] { 1, 0, 0 }; when(helper.loadPageProperties(content)).thenThrow(new IOException()); //then exception.expect(SObjectCreationException.class); //when listener.pageInserted(page, content); } @Test public void pageInserted_should_throw_an_exception_if_the_resource_path_template_is_missing() throws Exception { //given final long pageId = 1983L; final SPage page = buildPage(pageId); final byte[] content = new byte[] { 1, 0, 0 }; final Properties properties = new Properties(); properties.setProperty("apiExtensions", "employee"); properties.setProperty("employee.method", "GET"); properties.setProperty("employee.classFileName", "Index.groovy"); when(helper.loadPageProperties(content)).thenReturn(properties); //then exception.expect(SObjectCreationException.class); exception.expectMessage("the property 'employee.pathTemplate' is missing or is empty"); //when listener.pageInserted(page, content); } @Test public void pageInserted_should_throw_an_exception_if_the_resource_path_template_is_empty() throws Exception { //given final long pageId = 1983L; final SPage page = buildPage(pageId); final byte[] content = new byte[] { 1, 0, 0 }; final Properties properties = new Properties(); properties.setProperty("apiExtensions", "employee"); properties.setProperty("employee.method", "GET"); properties.setProperty("employee.pathTemplate", " "); properties.setProperty("employee.classFileName", "Index.groovy"); when(helper.loadPageProperties(content)).thenReturn(properties); //then exception.expect(SObjectCreationException.class); exception.expectMessage("the property 'employee.pathTemplate' is missing or is empty"); //when listener.pageInserted(page, content); } @Test public void pageInserted_should_throw_an_exception_if_the_method_is_missing() throws Exception { //given final long pageId = 1983L; final SPage page = buildPage(pageId); final byte[] content = new byte[] { 1, 0, 0 }; final Properties properties = new Properties(); properties.setProperty("apiExtensions", "employee"); properties.setProperty("employee.pathTemplate", "employees/{id}"); properties.setProperty("employee.classFileName", "Index.groovy"); when(helper.loadPageProperties(content)).thenReturn(properties); //then exception.expect(SObjectCreationException.class); exception.expectMessage("the property 'employee.method' is missing or is empty"); //when listener.pageInserted(page, content); } @Test public void pageInserted_should_throw_an_exception_if_the_class_file_name_is_missing() throws Exception { //given final long pageId = 1983L; final SPage page = buildPage(pageId); final byte[] content = new byte[] { 1, 0, 0 }; final Properties properties = new Properties(); properties.setProperty("apiExtensions", "employee "); properties.setProperty("employee.method", "GET"); properties.setProperty("employee.pathTemplate", "employees"); when(helper.loadPageProperties(content)).thenReturn(properties); //then exception.expect(SObjectCreationException.class); exception.expectMessage("the property 'employee.classFileName' is missing or is empty"); //when listener.pageInserted(page, content); } @Test public void pageInserted_should_throw_an_exception_if_the_permissions_are_missing() throws Exception { //given final long pageId = 1983L; final SPage page = buildPage(pageId); final byte[] content = new byte[] { 1, 0, 0 }; final Properties properties = new Properties(); properties.setProperty("apiExtensions", "employee "); properties.setProperty("employee.method", "GET"); properties.setProperty("employee.pathTemplate", "employees"); properties.setProperty("employee.classFileName", "Index.groovy"); when(helper.loadPageProperties(content)).thenReturn(properties); //then exception.expect(SObjectCreationException.class); exception.expectMessage("the property 'employee.permissions' is missing or is empty"); //when listener.pageInserted(page, content); } @Test public void pageInserted_should_throw_an_exception_if_api_extension_is_missing() throws Exception { final long pageId = 1983L; final SPage page = buildPage(pageId); final byte[] content = new byte[] { 1, 0, 0 }; final Properties properties = new Properties(); properties.setProperty("employee.method", "GET"); properties.setProperty("employee.classFileName", "Index.groovy"); when(helper.loadPageProperties(content)).thenReturn(properties); //then exception.expect(SObjectCreationException.class); exception.expectMessage("the property 'apiExtensions' is missing or is empty"); //when listener.pageInserted(page, content); } @Test public void pageDeleted_should_only_care_of_api_extension() throws Exception { final SPage page = new SPage(); page.setId(2L); page.setContentType(SContentType.PAGE); listener.pageDeleted(page); verifyNoInteractions(pageMappingService, helper); } @Test public void pageDeleted_should_delete_all_mappings() throws Exception { //given final long pageId = 10L; final SPage page = buildPage(pageId); final List mappings = buildPageMappings(100); final List mappings2 = buildPageMappings(53); when(pageMappingService.get(pageId, 0, 100)).thenReturn(mappings, mappings2); //when listener.pageDeleted(page); //then verify(pageMappingService, times(153)).delete(any(SPageMapping.class)); } private List buildPageMappings(final int numberOfResults) { final List mappings = new ArrayList<>(); for (int i = 0; i < numberOfResults; i++) { mappings.add(new SPageMapping()); } return mappings; } @Test public void pageDeleted_should_throw_an_exception_when_an_exception_occurs_when_getting_mappings() throws Exception { //given final long pageId = 10L; final SPage page = buildPage(pageId); when(pageMappingService.get(pageId, 0, 100)).thenThrow(new SBonitaReadException("exception")); //then exception.expect(SBonitaReadException.class); exception.expectMessage("exception"); //when listener.pageDeleted(page); } @Test public void pageDeleted_should_throw_an_exception_when_an_exception_occurs_when_deleting_mappings() throws Exception { //given final long pageId = 10L; final SPage page = buildPage(pageId); final List mappings = buildPageMappings(10); when(pageMappingService.get(pageId, 0, 100)).thenReturn(mappings); doThrow(new SDeletionException("message")).when(pageMappingService).delete(any(SPageMapping.class)); //then exception.expect(SDeletionException.class); exception.expectMessage("message"); //when listener.pageDeleted(page); } @Test public void pageUpdated_should_delete_old_mappings_and_add_new_ones() throws Exception { final long pageId = 10L; final SPage page = buildPage(pageId); final byte[] content = new byte[] { 1, 0, 0 }; final Properties properties = new Properties(); properties.setProperty("apiExtensions", "employees, employee "); properties.setProperty("employees.method", "GET"); properties.setProperty("employees.pathTemplate", "employees"); properties.setProperty("employees.classFileName", "Index.groovy"); properties.setProperty("employees.permissions", "myPermission"); properties.setProperty("employee.method", "PUT"); properties.setProperty("employee.pathTemplate", "employees"); properties.setProperty("employee.classFileName", "Index.groovy"); properties.setProperty("employee.permissions", "myPermission"); when(helper.loadPageProperties(content)).thenReturn(properties); final SPageMapping pageMapping1 = buildPageMapping("apiExtension|POST|employees"); final SPageMapping pageMapping2 = buildPageMapping("apiExtension|GET|employees"); final List mappings = new ArrayList<>(); mappings.add(pageMapping1); mappings.add(pageMapping2); when(pageMappingService.get(pageId, 0, 100)).thenReturn(mappings); listener.pageUpdated(page, content); verify(pageMappingService).delete(pageMapping1); verify(pageMappingService, never()).delete(pageMapping2); verify(pageMappingService).create("apiExtension|PUT|employees", pageId, Collections. emptyList()); verify(pageMappingService, never()).create(pageMapping2.getKey(), pageId, Collections. emptyList()); } private SPageMapping buildPageMapping(final String key) { final SPageMapping pageMapping = new SPageMapping(); pageMapping.setKey(key); return pageMapping; } @Test public void pageUpdated_should_throw_an_update_exception_if_an_internal_exception_occurs() throws Exception { //given final long pageId = 10L; final SPage page = buildPage(pageId); final byte[] content = new byte[] { 1, 0, 0 }; when(helper.loadPageProperties(content)).thenThrow(new IOException("exception")); //then exception.expect(SObjectModificationException.class); exception.expectMessage("exception"); //when listener.pageUpdated(page, content); } @Test public void pageUpdated_should_only_care_of_api_extension() throws Exception { final SPage page = new SPage(); page.setId(2L); page.setContentType(SContentType.PAGE); final byte[] content = new byte[] { 1, 0, 0 }; listener.pageUpdated(page, content); verifyNoInteractions(pageMappingService, helper); } } ================================================ FILE: services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/PageMappingServiceImplTest.java ================================================ /** * 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.page.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.util.Lists.newArrayList; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; import java.util.Collections; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.page.SPageMapping; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; 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 PageMappingServiceImplTest { @Mock private Recorder recorder; @Mock private ReadPersistenceService persistenceService; @Mock private SessionService sessionService; @Mock private ReadSessionAccessor sessionAccessor; @InjectMocks private PageMappingServiceImpl pageMappingService; @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void get_should_return_page_mappings() throws Exception { final QueryOptions options = new QueryOptions(0, 2); final SelectListDescriptor listDescriptor = new SelectListDescriptor( "getPageMappingByPageId", Collections. singletonMap("pageId", 1983L), SPageMapping.class, options); pageMappingService.get(1983L, 0, 2); verify(persistenceService).selectList(listDescriptor); } @Test(expected = SBonitaReadException.class) public void get_should_throw_an_exception() throws Exception { when(persistenceService.selectList(any(SelectListDescriptor.class))) .thenThrow(new SBonitaReadException("exception")); pageMappingService.get(1983L, 0, 2); } @Test public void should_throw_a_SObjectCreationException_when_create_a_page_mapping_with_a_key_that_already_exists() throws Exception { final SPageMapping pageMapping = mock(SPageMapping.class); when(pageMapping.getPageId()).thenReturn(5L); when(persistenceService.selectOne(new SelectOneDescriptor("getPageMappingByKey", Collections . singletonMap("key", "aMappinKey"), SPageMapping.class))).thenReturn(pageMapping); expectedException.expect(SObjectCreationException.class); expectedException.expectMessage("Mapping key aMappinKey already exists for page with id 5"); pageMappingService.create("aMappinKey", 1L, newArrayList("rule1")); } @Test public void should_throw_a_SObjectCreationException_when_create_a_page_mapping_with_a_key_that_already_exists_for_an_url() throws Exception { final SPageMapping pageMapping = mock(SPageMapping.class); when(pageMapping.getPageId()).thenReturn(5L); when(persistenceService.selectOne(new SelectOneDescriptor("getPageMappingByKey", Collections . singletonMap("key", "aMappinKey"), SPageMapping.class))).thenReturn(pageMapping); expectedException.expect(SObjectCreationException.class); expectedException.expectMessage("Mapping key aMappinKey already exists for page with id 5"); pageMappingService.create("aMappinKey", "http://bonitasoft.com", "adapter", newArrayList("rule1")); } @Test public void should_create_a_page_mapping_for_a_given_page() throws Exception { when(persistenceService.selectOne(new SelectOneDescriptor("getPageMappingByKey", Collections . singletonMap("key", "aMappinKey"), SPageMapping.class))).thenReturn(null); pageMappingService.create("aMappinKey", 1L, newArrayList("rule1")); final ArgumentCaptor captor = ArgumentCaptor.forClass(InsertRecord.class); verify(recorder).recordInsert(captor.capture(), anyString()); final InsertRecord insertRecord = captor.getValue(); final PersistentObject entity = insertRecord.getEntity(); assertThat(entity).isInstanceOf(SPageMapping.class); final SPageMapping mapping = (SPageMapping) entity; assertThat(mapping.getKey()).isEqualTo("aMappinKey"); assertThat(mapping.getPageId()).isEqualTo(1L); assertThat(mapping.getPageAuthorizationRules()).containsOnly("rule1"); } @Test public void should_create_a_page_mapping_for_a_given_url() throws Exception { when(persistenceService.selectOne(new SelectOneDescriptor("getPageMappingByKey", Collections . singletonMap("key", "aMappinKey"), SPageMapping.class))).thenReturn(null); pageMappingService.create("aMappinKey", "http://bonitasoft.com", "adapter", newArrayList("rule1")); final ArgumentCaptor captor = ArgumentCaptor.forClass(InsertRecord.class); verify(recorder).recordInsert(captor.capture(), anyString()); final InsertRecord insertRecord = captor.getValue(); final PersistentObject entity = insertRecord.getEntity(); assertThat(entity).isInstanceOf(SPageMapping.class); final SPageMapping mapping = (SPageMapping) entity; assertThat(mapping.getKey()).isEqualTo("aMappinKey"); assertThat(mapping.getUrl()).isEqualTo("http://bonitasoft.com"); assertThat(mapping.getUrlAdapter()).isEqualTo("adapter"); assertThat(mapping.getPageAuthorizationRules()).containsOnly("rule1"); } } ================================================ FILE: services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/PageServiceImplTest.java ================================================ /** * 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.page.impl; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.*; import static org.bonitasoft.engine.commons.Pair.pair; import static org.bonitasoft.engine.io.FileAndContentUtils.file; import static org.bonitasoft.engine.io.FileAndContentUtils.zip; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.time.Instant; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Properties; import org.bonitasoft.engine.authorization.PermissionService; import org.bonitasoft.engine.commons.Pair; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectAlreadyExistsException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.commons.io.IOUtil; import org.bonitasoft.engine.page.AbstractSPage; import org.bonitasoft.engine.page.PageService; import org.bonitasoft.engine.page.PageServiceListener; import org.bonitasoft.engine.page.SContentType; import org.bonitasoft.engine.page.SInvalidPageTokenException; import org.bonitasoft.engine.page.SInvalidPageZipException; import org.bonitasoft.engine.page.SInvalidPageZipInconsistentException; import org.bonitasoft.engine.page.SInvalidPageZipMissingAPropertyException; import org.bonitasoft.engine.page.SInvalidPageZipMissingIndexException; import org.bonitasoft.engine.page.SInvalidPageZipMissingPropertiesException; import org.bonitasoft.engine.page.SPage; import org.bonitasoft.engine.page.SPageLogBuilder; import org.bonitasoft.engine.page.SPageWithContent; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadOnlySelectByIdDescriptor; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.services.QueriableLoggerService; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.mockito.stubbing.Answer; import org.springframework.util.DigestUtils; @RunWith(MockitoJUnitRunner.class) public class PageServiceImplTest { private static final String PAGE_PROPERTIES = "page.properties"; private static final String INDEX_HTML = "index.html"; private static final String INDEX_GROOVY = "Index.groovy"; private static final String THEME_CSS = "resources/theme.css"; private static final String CONTENT_NAME = "content.zip"; private static final boolean PROVIDED_TRUE = true; private static final int INSTALLED_BY_ID = 45; private static final int INSTALLATION_DATE_AS_LONG = 123456; private static final String PAGE_NAME = "custompage_pageName"; public static final long PROCESS_DEFINITION_ID = 846L; public static final long USER_ID = 98989L; @Mock private Recorder recorder; @Mock private ReadPersistenceService readPersistenceService; @Mock private QueryOptions queryOptions; @Mock private QueriableLoggerService queriableLoggerService; @Mock private SPageLogBuilder pageLogBuilder; @Mock private ReadSessionAccessor sessionAccessor; @Mock private SessionService sessionService; @Mock private PermissionService permissionService; @Mock private EntityUpdateDescriptor entityUpdateDescriptor; @Captor private ArgumentCaptor entityUpdateDescriptorCaptor; private PageServiceImpl pageServiceImpl; @Mock PageServiceListener apiExtensionPageServiceListener; @Captor ArgumentCaptor pageArgumentCaptor; @Before public void before() { pageServiceImpl = spy( new PageServiceImpl(readPersistenceService, recorder, queriableLoggerService, sessionAccessor, sessionService, permissionService)); doReturn(pageLogBuilder).when(pageServiceImpl).getPageLog(any(ActionType.class), anyString()); doNothing().when(pageServiceImpl).initiateLogBuilder(anyLong(), anyInt(), any(SPersistenceLogBuilder.class), anyString()); final List listeners = singletonList(apiExtensionPageServiceListener); pageServiceImpl.setPageServiceListeners(listeners); } @Test public void getNumberOfPages() throws SBonitaException { // given final long expected = 50; when(readPersistenceService.getNumberOfEntities(SPage.class, queryOptions, null)).thenReturn(expected); // when final long numberOfPages = pageServiceImpl.getNumberOfPages(queryOptions); // then Assert.assertEquals(expected, numberOfPages); } @Test(expected = SBonitaReadException.class) public void getNumberOfPagesThrowsException() throws SBonitaException { // given // when when(readPersistenceService.getNumberOfEntities(SPage.class, queryOptions, null)) .thenThrow(new SBonitaReadException("ouch!")); pageServiceImpl.getNumberOfPages(queryOptions); // then // exception; } @Test(expected = SInvalidPageTokenException.class) public void createPage_should_throw_exception_when_name_is_empty() throws SBonitaException, IOException { final long pageId = 15; final SPage pageWithEmptyName = new SPage("", 123456, 45, true, CONTENT_NAME); pageWithEmptyName.setDisplayName("plop"); pageWithEmptyName.setId(pageId); pageServiceImpl.addPage(pageWithEmptyName, validPageContent("plop")); } @Test(expected = SObjectAlreadyExistsException.class) public void addPage_should_throw_exception_when_already_exist() throws Exception { // given final SPage newPage = new SPage(PAGE_NAME, INSTALLATION_DATE_AS_LONG, INSTALLED_BY_ID, PROVIDED_TRUE, CONTENT_NAME); newPage.setDisplayName("plop"); // when when(pageServiceImpl.getPageByName(PAGE_NAME)).thenReturn(newPage); pageServiceImpl.addPage(newPage, validPageContent(PAGE_NAME)); // then exception } @Test(expected = SObjectAlreadyExistsException.class) public void should_create_page_throw_exception_when_name_exists() throws Exception { // given final SPage newPage = new SPage(PAGE_NAME, 123456, 45, true, CONTENT_NAME); newPage.setDisplayName("display Name"); // when when(pageServiceImpl.getPageByName(PAGE_NAME)).thenReturn(newPage); final byte[] validContent = validPageContent(PAGE_NAME); pageServiceImpl.addPage(newPage, validContent); // then exception } @Test public void should_create_page_with_process_scope_when_name_exists() throws Exception { // given final SPage newPage = new SPage(PAGE_NAME, 123456, 45, false, CONTENT_NAME); newPage.setDisplayName("display Name"); final byte[] validContent = validPageContent(PAGE_NAME); when(pageServiceImpl.getPageByName(PAGE_NAME)).thenReturn(null); pageServiceImpl.addPage(newPage, validContent); // when final SPage newProcessPage = new SPage(PAGE_NAME, 123456, 45, false, CONTENT_NAME); newProcessPage.setContentType(SContentType.FORM); newProcessPage.setProcessDefinitionId(PROCESS_DEFINITION_ID); newProcessPage.setDisplayName("display Name"); final SPage insertedPage = pageServiceImpl.addPage(newProcessPage, validContent); //then assertThat(insertedPage).isNotNull().isEqualToComparingFieldByField(newProcessPage); } @Test(expected = SObjectAlreadyExistsException.class) public void should_create_page_with_processDefinitionId_throw_exception_when_name_exists() throws Exception { // given final SPage newPage = new SPage(PAGE_NAME, 123456, 45, true, CONTENT_NAME); newPage.setContentType(SContentType.FORM); newPage.setProcessDefinitionId(PROCESS_DEFINITION_ID); newPage.setDisplayName("display Name"); // when when(pageServiceImpl.getPageByNameAndProcessDefinitionId(PAGE_NAME, PROCESS_DEFINITION_ID)).thenReturn(newPage); final byte[] validContent = validPageContent(PAGE_NAME); pageServiceImpl.addPage(newPage, validContent); // then exception } @SuppressWarnings("unchecked") private byte[] validPageContent(final String pageName) throws IOException { return IOUtil.zip(pair("Index.groovy", "content of the groovy".getBytes()), pair(PAGE_PROPERTIES, ("name=custompage_" + pageName + "\ndisplayName=mypage display name\ndescription=mypage description\n").getBytes())); } @Test public void getPage() throws SBonitaException { final long pageId = 15; final SPage expected = new SPage("page1", 123456, 45, true, CONTENT_NAME); expected.setId(pageId); when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPage.class, pageId))).thenReturn(expected); // when final SPage page = pageServiceImpl.getPage(pageId); // then Assert.assertEquals(expected, page); } @Test(expected = SObjectNotFoundException.class) public void getPageThrowsPageNotFoundException() throws SBonitaException { final long pageId = 15; final SPage expected = new SPage("page1", 123456, 45, true, CONTENT_NAME); expected.setId(pageId); when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPage.class, pageId))).thenReturn(null); pageServiceImpl.getPage(pageId); } @Test public void getPageByNameReturnsNullWhenNotFound() throws SBonitaException { // given: page does not exists // when final SPage pageByName = pageServiceImpl.getPageByName("unknown"); // then assertNull(pageByName); } @Test(expected = SBonitaReadException.class) public void getPageThrowsException() throws SBonitaException { final long pageId = 15; final SPage expected = new SPage("page1", 123456, 45, true, CONTENT_NAME); expected.setId(pageId); when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPage.class, pageId))).thenThrow( new SBonitaReadException("ouch!")); pageServiceImpl.getPage(pageId); } @Test public void getPageContent_should_add_properties_in_the_zip() throws SBonitaException, IOException { // given: a zip without properties final SPageWithContent page = new SPageWithContent(); page.setName("mypage"); page.setDescription("mypage description"); page.setDisplayName("mypage display name"); page.setId(12); final byte[] content = IOUtil.zip(Collections.singletonMap("Index.groovy", "content of the groovy".getBytes())); page.setContent(content); doReturn(page).when(readPersistenceService) .selectById(new ReadOnlySelectByIdDescriptor<>(SPageWithContent.class, 12)); // when final byte[] result = pageServiceImpl.getPageContent(12); // then final Map unzip = IOUtil.unzip(result); assertThat(unzip.size()).isEqualTo(2); final Properties pageProperties = new Properties(); pageProperties.load(new ByteArrayInputStream(unzip.get(PAGE_PROPERTIES))); assertThat(pageProperties.get("name")).isEqualTo("mypage"); assertThat(pageProperties.get("displayName")).isEqualTo("mypage display name"); assertThat(pageProperties.get("description")).isEqualTo("mypage description"); } @Test public void getPageContent_should_add_properties_in_the_zip_with_non_mandatory_metadata() throws SBonitaException, IOException { final SPageWithContent page = new SPageWithContent(); page.setName("mypage"); page.setDisplayName("mypage display name"); //no description page.setId(12); page.setContent(IOUtil.zip(Collections.singletonMap("Index.groovy", "content of the groovy".getBytes()))); doReturn(page).when(readPersistenceService) .selectById(new ReadOnlySelectByIdDescriptor<>(SPageWithContent.class, 12)); // when final byte[] result = pageServiceImpl.getPageContent(12); // then final Properties pageProperties = new Properties(); pageProperties.load(new ByteArrayInputStream(IOUtil.unzip(result).get(PAGE_PROPERTIES))); assertThat(pageProperties.get("name")).isEqualTo("mypage"); assertThat(pageProperties.get("displayName")).isEqualTo("mypage display name"); assertThat(pageProperties.get("description")).isNull(); } @Test public void getPageContent_should_update_properties_in_the_zip_if_exists_and_keep_others() throws SBonitaException, IOException { // given: a zip with outdated properties final SPageWithContent page = new SPageWithContent(); page.setName("mypageUpdated"); page.setDescription("mypageUpdated description"); page.setDisplayName("mypageUpdated display name"); page.setId(12); @SuppressWarnings("unchecked") final byte[] content = IOUtil.zip( pair("Index.groovy", "content of the groovy".getBytes()), pair(PAGE_PROPERTIES, "name=custompage_mypage\ndisplayName=mypage display name\ndescription=mypage description\naCustomProperty=plop\n" .getBytes())); page.setContent(content); doReturn(page).when(readPersistenceService) .selectById(new ReadOnlySelectByIdDescriptor<>(SPageWithContent.class, 12)); // when final byte[] result = pageServiceImpl.getPageContent(12); // then final Map unzip = IOUtil.unzip(result); assertThat(unzip.size()).isEqualTo(2); final Properties pageProperties = new Properties(); pageProperties.load(new ByteArrayInputStream(unzip.get(PAGE_PROPERTIES))); assertThat(pageProperties.get("name")).isEqualTo("mypageUpdated"); assertThat(pageProperties.get("displayName")).isEqualTo("mypageUpdated display name"); assertThat(pageProperties.get("description")).isEqualTo("mypageUpdated description"); assertThat(pageProperties.get("aCustomProperty")).isEqualTo("plop"); } @Test(expected = SObjectNotFoundException.class) public void should_getPageContent_throw_not_found() throws SBonitaException { pageServiceImpl.getPageContent(12); } @Test public void deletePage_should_call_delete_on_recorder() throws Exception { final long pageId = 15; final SPage expected = new SPage("page1", 123456, 45, true, CONTENT_NAME); expected.setId(pageId); doReturn(new Properties()).when(pageServiceImpl).getPreviousPageProperties(expected); doNothing().when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); doReturn(expected).when(pageServiceImpl).getPage(pageId); pageServiceImpl.deletePage(pageId); verify(recorder, times(1)).recordDelete(any(DeleteRecord.class), nullable(String.class)); } @Test public void deletePageThrowsPageNotFoundException() throws Exception { final long pageId = 15L; final SPage expected = new SPage("page1", 123456, 45, true, CONTENT_NAME); expected.setId(pageId); doReturn(new Properties()).when(pageServiceImpl).getPreviousPageProperties(expected); doThrow(new SRecorderException("ouch !")).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPage.class, pageId))).thenReturn(expected); assertThrows(SObjectModificationException.class, () -> pageServiceImpl.deletePage(pageId)); } @Test public void updatePageContent_should_check_zip_content() { assertThrows(SInvalidPageZipException.class, () -> pageServiceImpl.updatePageContent(15L, "aaa".getBytes(), CONTENT_NAME)); } @Test public void addPage_should_check_zip_content() { // given final SPage sPage = new SPage("page1", 123456, 45, false, CONTENT_NAME); final byte[] content = "invalid content".getBytes(); assertThrows(SInvalidPageZipException.class, () -> pageServiceImpl.addPage(sPage, content)); } @Test public void zipTest_not_a_zip() { assertThrows(SInvalidPageZipException.class, () -> pageServiceImpl.readPageZip("badContent".getBytes(), false)); } @Test public void zipTest_Bad_Content() throws Exception { // given final byte[] content = IOUtil.zip(pair("aFile.txt", "hello".getBytes()), pair(PAGE_PROPERTIES, "name=custompage_mypage\ndisplayName=My Page\ndescription=mypage description\n\ncontentType=page" .getBytes())); // when assertThrows("Missing Index.groovy or index.html", SInvalidPageZipMissingIndexException.class, () -> pageServiceImpl.readPageZip(content, false)); // then // exception } @Test public void should_throw_an_SInvalidPageZipMissingAPropertyException_for_apiExtension_without_apis() throws Exception { // given final byte[] content = IOUtil.zip(pair("MyController.groovy", "content of the groovy".getBytes()), pair(PAGE_PROPERTIES, "name=custompage_mypage\ndisplayName=My Page\ndescription=mypage description\n\ncontentType=apiExtension" .getBytes())); // when assertThrows("Missing fields in the page.properties: apiExtensions", SInvalidPageZipMissingAPropertyException.class, () -> pageServiceImpl.readPageZip(content, false)); } @Test public void should_throw_an_SInvalidPageZipMissingAPropertyException_for_api_extension_without_classFilename() throws Exception { // given final byte[] content = IOUtil.zip(pair("MyController.groovy", "content of the groovy".getBytes()), pair(PAGE_PROPERTIES, "name=custompage_mypage\ndisplayName=My Page\ndescription=mypage description\n\ncontentType=apiExtension\napiExtensions=myApi" .getBytes())); // when assertThrows("Missing fields in the page.properties: myApi.classFileName", SInvalidPageZipMissingAPropertyException.class, () -> pageServiceImpl.readPageZip(content, false)); } @Test public void should_throw_an_SInvalidPageZipInconsistentException_for_api_extension_with_classFilename_not_in_archive() throws Exception { // given final byte[] content = IOUtil.zip(Collections.singletonMap(PAGE_PROPERTIES, "name=custompage_mypage\ndisplayName=My Page\ndescription=mypage description\n\ncontentType=apiExtension\napiExtensions=myApi\nmyApi.classFileName=MyController.groovy" .getBytes())); // when assertThrows("RestAPIController MyController.groovy has not been found in archive.", SInvalidPageZipInconsistentException.class, () -> pageServiceImpl.readPageZip(content, false)); } @Test public void should_throw_an_SInvalidPageZipMissingAPropertyException_for_api_extension_with_empty_classFilename() throws Exception { // given final byte[] content = IOUtil.zip(pair("MyController.groovy", "content of the groovy".getBytes()), pair(PAGE_PROPERTIES, "name=custompage_mypage\ndisplayName=My Page\ndescription=mypage description\n\ncontentType=apiExtension\napiExtensions=myApi\nmyApi.classFileName=" .getBytes())); // when assertThrows("Missing fields in the page.properties: myApi.classFileName", SInvalidPageZipMissingAPropertyException.class, () -> pageServiceImpl.readPageZip(content, false)); } @Test public void should_throw_an_SInvalidPageZipMissingAPropertyException_if_apiExtensions_is_empty() throws Exception { // given final byte[] content = IOUtil.zip(pair("MyController.groovy", "content of the groovy".getBytes()), pair(PAGE_PROPERTIES, "name=custompage_mypage\ndisplayName=My Page\ndescription=mypage description\n\ncontentType=apiExtension\napiExtensions=" .getBytes())); // when assertThrows("Missing fields in the page.properties: apiExtensions", SInvalidPageZipMissingAPropertyException.class, () -> pageServiceImpl.readPageZip(content, false)); } @Test public void should_read_page_with_an_api_extension() throws Exception { // given final byte[] content = IOUtil.zip(pair("MyController.groovy", "content of the groovy".getBytes()), pair(PAGE_PROPERTIES, "name=custompage_mypage\ndisplayName=My Page\ndescription=mypage description\n\ncontentType=apiExtension\napiExtensions=myApi\nmyApi.classFileName=MyController.groovy" .getBytes())); // when pageServiceImpl.readPageZip(content, false); } @Test public void should_read_page_with_many_api_extensions() throws Exception { // given final byte[] content = IOUtil.zip(pair("MyController.groovy", "content of the groovy".getBytes()), pair("org/MyOtherController.groovy", "content of the groovy".getBytes()), pair(PAGE_PROPERTIES, "name=custompage_mypage\ndisplayName=My Page\ndescription=mypage description\n\ncontentType=apiExtension\napiExtensions=myApi, myOtherApi\nmyApi.classFileName=MyController.groovy\nmyOtherApi.classFileName=org/MyOtherController.groovy" .getBytes())); // when pageServiceImpl.readPageZip(content, false); } @Test public void zipTest_Throws_exception() throws Exception { // given final byte[] content = IOUtil.zip(pair("aFile.txt", "hello".getBytes()), pair(PAGE_PROPERTIES, "name=custompage_mypage\ndisplayName=My Page\ndescription=mypage description\n\ncontentType=page" .getBytes())); doThrow(SInvalidPageZipMissingIndexException.class).when(pageServiceImpl) .checkZipContainsRequiredEntries(anyMap()); //when Throwable throwable = catchThrowable(() -> pageServiceImpl.readPageZip(content, false)); //then assertThat(throwable) .isExactlyInstanceOf(SInvalidPageZipMissingIndexException.class); } @Test public void zipTest_Content_7_0_With_index_html_in_resources_folder() throws Exception { // given final Map zipContent = Collections.singletonMap("resources/index.html", "hello".getBytes()); // when pageServiceImpl.checkZipContainsRequiredEntries(zipContent); // then } @Test public void zipTest_valid_Groovy() throws Exception { // given @SuppressWarnings("unchecked") final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair()); // when then pageServiceImpl.readPageZip(content, false); // expected no exception } @Test public void zipTest_page_properties_invalid_name() throws Exception { // given @SuppressWarnings("unchecked") final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), pair(PAGE_PROPERTIES, "name=mypage\ndisplayName=mypage display name\ndescription=mypage description\n".getBytes())); // when then assertThrows(SInvalidPageTokenException.class, () -> pageServiceImpl.readPageZip(content, false)); } @Test public void zipTest_page_properties_no_name() throws Exception { // given @SuppressWarnings("unchecked") final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), pair(PAGE_PROPERTIES, "displayName=mypage display name\ndescription=mypage description\n".getBytes())); // when then assertThrows(SInvalidPageTokenException.class, () -> pageServiceImpl.readPageZip(content, false)); } @Test public void zipTest_page_properties_invalid_display_name() throws Exception { // given @SuppressWarnings("unchecked") final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), pair(PAGE_PROPERTIES, "name=custompage_mypage\ndisplayName=\ndescription=mypage description\n".getBytes())); // when then assertThrows(SInvalidPageZipException.class, () -> pageServiceImpl.readPageZip(content, false)); } @Test public void zipTest_page_properties_no_display_name() throws Exception { // given @SuppressWarnings("unchecked") final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), pair(PAGE_PROPERTIES, "name=custompage_mypage\ndescription=mypage description\n".getBytes())); // when assertThrows("Missing fields in the page.properties: " + PageService.PROPERTIES_DISPLAY_NAME, SInvalidPageZipMissingAPropertyException.class, () -> pageServiceImpl.readPageZip(content, false)); // then exception } @Test public void zipTestGroovyWithWrongName() throws Exception { // given @SuppressWarnings("unchecked") final byte[] content = IOUtil.zip(pair("index.groovy", "content of the groovy".getBytes()), getPagePropertiesContentPair()); // when then assertThrows(SInvalidPageZipMissingIndexException.class, () -> pageServiceImpl.readPageZip(content, false)); } @Test public void zipTest_valid_Html() throws Exception { // given @SuppressWarnings("unchecked") final byte[] content = IOUtil.zip(pair(INDEX_HTML, "content of the groovy".getBytes()), pair(PAGE_PROPERTIES, "name=custompage_mypage\ndisplayName=mypage final display name\ndescription=final mypage description\n" .getBytes())); // when then pageServiceImpl.readPageZip(content, false); } @Test public void zipTest_valid_Theme() throws Exception { // given @SuppressWarnings("unchecked") final byte[] content = IOUtil.zip(pair(THEME_CSS, "h1 { font-size:14pt; }".getBytes()), pair(PAGE_PROPERTIES, "name=custompage_mypage\ndisplayName=mypage final display name\ndescription=final mypage description\n" .getBytes())); // when then pageServiceImpl.readPageZip(content, false); } @Test public void zipTest_no_page_properties() throws Exception { // given @SuppressWarnings("unchecked") final byte[] content = IOUtil.zip(pair(INDEX_HTML, "content of the groovy".getBytes())); // when then assertThrows("Missing page.properties", SInvalidPageZipMissingPropertiesException.class, () -> pageServiceImpl.readPageZip(content, false)); } @Test public void checkPageContentIsValid_null() { assertThrows(SInvalidPageZipException.class, () -> pageServiceImpl.readPageZip(null, false)); } @Test public void checkPageContentIsValid_badZip() { assertThrows(SInvalidPageZipException.class, () -> pageServiceImpl.readPageZip("not a zip".getBytes(), false)); } @Test public void checkPageContentIsValid_validZip() throws Exception { // given @SuppressWarnings("unchecked") final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair()); // when pageServiceImpl.readPageZip(content, false); // then no exception } @Test public void should_redPageZip_call_the_internal_with_provided_false() throws SInvalidPageTokenException, SInvalidPageZipInconsistentException, SInvalidPageZipMissingAPropertyException, SInvalidPageZipMissingPropertiesException, SInvalidPageZipMissingIndexException { final byte[] content = { 0, 1, 2 }; doReturn(null).when(pageServiceImpl).readPageZip(content, false); //when pageServiceImpl.readPageZip(content); //then verify(pageServiceImpl).readPageZip(content, false); } @Test public void should_add_page_throw_exception_when_invalid_zip() throws Exception { //given final SPage sPage = new SPage("page", 123456, 45, true, CONTENT_NAME); final byte[] badContent = "not_a_zip".getBytes(); doThrow(IOException.class).when(pageServiceImpl).unzip(badContent); //when Throwable throwable = catchThrowable(() -> pageServiceImpl.addPage(sPage, badContent)); //then assertThat(throwable) .isExactlyInstanceOf(SInvalidPageZipInconsistentException.class) .hasMessage("Error while reading zip file") .hasCauseExactlyInstanceOf(IOException.class); } @Test public void should_add_page_insertPage() throws Exception { //given final SPage sPage = new SPage("page", 123456, 45, true, CONTENT_NAME); sPage.setDisplayName("displayName1"); final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair()); //when pageServiceImpl.addPage(sPage, content); //then verify(pageServiceImpl).insertPage(sPage, content); } @Test public void should_add_page_with_correct_fields() throws Exception { //given byte[] content = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair()); doAnswer(invocation -> invocation.getArguments()[0]).when(pageServiceImpl) .insertPage(pageArgumentCaptor.capture(), any()); Instant now = Instant.now(); //when SPage insertedPage = pageServiceImpl.addPage(content, CONTENT_NAME, 45); //then long pageDate = insertedPage.getLastModificationDate(); SPage expected = new SPage("custompage_mypage", "mypage description", "mypage display name", pageDate, 45, false, pageDate, 45, CONTENT_NAME); expected.setRemovable(true); expected.setEditable(true); assertThat(insertedPage).isEqualTo(expected); assertThat(insertedPage.getLastModificationDate()).isGreaterThanOrEqualTo(now.toEpochMilli()); List insertedPages = pageArgumentCaptor.getAllValues(); assertThat(insertedPages).hasSize(1).containsExactly(expected); } @Test public void should_add_page_return_default_content_type() throws Exception { //given final byte[] content1 = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair()); //when final SPage insertedPage1 = pageServiceImpl.addPage(content1, CONTENT_NAME, USER_ID); //then SPageAssert.assertThat(insertedPage1).hasContentType(SContentType.PAGE); } @Test public void should_rest_api_extension_record_page_mapping() throws Exception { //given final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair("contentType=" + SContentType.API_EXTENSION, "apiExtensions=myApi", "myApi.classFileName=Index.groovy")); //when final SPage insertedPage = pageServiceImpl.addPage(content, CONTENT_NAME, USER_ID); //then SPageAssert.assertThat(insertedPage).hasContentType(SContentType.API_EXTENSION); } @Test public void should_read_rest_api_extension_in_compile_mode() throws Exception { //given final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair("contentType=" + SContentType.API_EXTENSION, "apiExtensions=myApi", "myApi.className=com.company.Index")); //when final SPage insertedPage = pageServiceImpl.addPage(content, CONTENT_NAME, USER_ID); //then SPageAssert.assertThat(insertedPage).hasContentType(SContentType.API_EXTENSION); } private Pair getPagePropertiesContentPair(final String... otherProperties) throws UnsupportedEncodingException { final StringBuilder stringBuilder = new StringBuilder() .append("name=custompage_mypage\ndisplayName=mypage display name\ndescription=mypage description\n"); for (final String property : otherProperties) { stringBuilder.append(property).append("\n"); } return pair(PAGE_PROPERTIES, stringBuilder.toString().getBytes("UTF-8")); } private Pair getIndexGroovyContentPair() { return pair(INDEX_GROOVY, "content of the groovy".getBytes()); } @Test public void addPage_should_execute_listener() throws Exception { final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair("contentType=" + SContentType.PAGE)); final SPage insertedPage = pageServiceImpl.addPage(content, CONTENT_NAME, USER_ID); verify(apiExtensionPageServiceListener).pageInserted(insertedPage, content); } @Test public void updatePage_should_not_execute_listener() throws Exception { final SPage page = new SPage("name", 10201983L, 2005L, false, "contentName"); when(readPersistenceService.selectById(any(SelectByIdDescriptor.class))).thenReturn(page); pageServiceImpl.updatePage(page.getId(), entityUpdateDescriptor); verifyNoInteractions(apiExtensionPageServiceListener); } @Test public void updatePage_should_execute_listener() throws Exception { final SPage page = new SPage("name", 10201983L, 2005L, false, "contentName"); page.setId(45L); final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair("contentType=" + SContentType.PAGE)); final SPageWithContent pageContent = new SPageWithContent(page, content); when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, page.getId()))) .thenReturn(pageContent); pageServiceImpl.updatePageContent(page.getId(), content, "contentName"); verify(apiExtensionPageServiceListener).pageUpdated(pageContent, content); } @Test public void updatePageContent_should_update_page_content_type() throws Exception { verifyPageUpdateContent(getPagePropertiesContentPair("contentType=" + SContentType.FORM), SContentType.FORM); } @Test public void updatePageContent_should_update_to_default_content_type() throws Exception { verifyPageUpdateContent(getPagePropertiesContentPair(), SContentType.PAGE); } protected void verifyPageUpdateContent(Pair pagePropertiesContentPair, final String expectedContentType) throws Exception { //given final SPage sPage = new SPage("name", 10201983L, 2005L, false, "contentName"); sPage.setId(45L); final byte[] content = IOUtil.zip(getIndexGroovyContentPair(), pagePropertiesContentPair); final SPageWithContent pageContent = new SPageWithContent(); pageContent.setContent(content); when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, sPage.getId()))) .thenReturn(pageContent); //then doAnswer((Answer) invocation -> { final EntityUpdateDescriptor entityUpdateDescriptor = (EntityUpdateDescriptor) invocation .getArguments()[0]; assertThat(entityUpdateDescriptor.getFields()).containsOnly( entry("description", "mypage description"), entry("contentName", "contentName"), entry("displayName", "mypage display name"), entry("contentType", expectedContentType)); return null; }).when(pageServiceImpl).updatePage(any(EntityUpdateDescriptor.class), any(AbstractSPage.class)); //when pageServiceImpl.updatePageContent(sPage.getId(), content, "contentName"); } @Test public void should_update_page_content_hash_of_provided_page() throws Exception { SPage page = new SPage("userPage", 10201983L, 2005L, false, "contentName"); page.setId(55L); page.setProvided(true); byte[] content = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair("contentType=" + SContentType.PAGE)); SPageWithContent pageWithContent = new SPageWithContent(page, content); pageWithContent.setProvided(true); when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, page.getId()))) .thenReturn(pageWithContent); doReturn(pageWithContent).when(pageServiceImpl).updatePage(entityUpdateDescriptorCaptor.capture(), eq(pageWithContent)); pageServiceImpl.updatePageContent(page.getId(), content, "contentName"); assertThat(entityUpdateDescriptorCaptor.getValue().getFields()) .contains(entry("pageHash", DigestUtils.md5DigestAsHex(content))); } @Test public void should_not_update_page_content_hash_of_not_provided_pages() throws Exception { SPage page = new SPage("userPage", 10201983L, 2005L, false, "contentName"); page.setId(55L); final byte[] zip = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair("contentType=" + SContentType.PAGE)); final SPageWithContent pageWithContent = new SPageWithContent(); pageWithContent.setContent(zip); when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, page.getId()))) .thenReturn(pageWithContent); doReturn(page).when(pageServiceImpl).updatePage(entityUpdateDescriptorCaptor.capture(), any()); pageServiceImpl.updatePageContent(page.getId(), zip, "contentName"); assertThat(entityUpdateDescriptorCaptor.getValue().getFields()).doesNotContainKey("pageHash"); } @Test public void deletePage_should_execute_listener() throws Exception { final SPage page = new SPage("name", 10201983L, 2005L, false, "contentName"); when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPage.class, 1983L))) .thenReturn(page); doReturn(new Properties()).when(pageServiceImpl).getPreviousPageProperties(page); pageServiceImpl.deletePage(1983L); verify(apiExtensionPageServiceListener).pageDeleted(page); } @Test public void deletePage_on_non_removable_page_should_raise_exception() throws Exception { //given final SPage page = new SPage("a page name", 10201983L, 2005L, false, "contentName"); page.setRemovable(false); when(readPersistenceService.selectById(any())).thenReturn(page); //when String exceptionMessage = assertThrows("Not the right exception", SObjectModificationException.class, () -> pageServiceImpl.deletePage(1983L)).getMessage(); //then assertThat(exceptionMessage).contains("The page 'a page name' cannot be deleted because it is non-removable"); } @Test public void update_non_editable_should_raise_exception_when_session_is_not_system() throws Exception { //given final SPage page = new SPage("a page name", 10201983L, 2005L, /* boolean provided: */false, "contentName"); page.setEditable(false); when(readPersistenceService.selectById(any())).thenReturn(page); doReturn(false).when(pageServiceImpl).isSystemSession(); //when String exceptionMessage = assertThrows("Not the right exception", SObjectModificationException.class, () -> pageServiceImpl.updatePage(page.getId(), entityUpdateDescriptor)).getMessage(); //then assertThat(exceptionMessage) .contains("The page 'a page name' cannot be modified because it is not modifiable"); } @Test public void update_non_editable_page_should_update_content_when_session_is_system() throws Exception { //given final SPage page = new SPage("the page", 999888777L, 2021L, /* boolean provided: */true, "contentName"); page.setEditable(false); when(readPersistenceService.selectById(any())).thenReturn(page); doReturn(true).when(pageServiceImpl).isSystemSession(); //when pageServiceImpl.updatePage(page.getId(), entityUpdateDescriptor); //then verify(recorder).recordUpdate(any(), any()); } @Test public void add_page_should_call_permission_service_with_the_parsed_properties() throws Exception { // given: SPage page = new SPage(); page.setName("custompage_test"); page.setDisplayName("My Custom Page"); byte[] zip = zip( file("page.properties", "name=custompage_test\ncontentType=page"), file("resources/index.html", "someContent")); Properties properties = new Properties(); properties.put("name", "custompage_test"); properties.put("contentType", "page"); // when: pageServiceImpl.addPage(page, zip); // then: verify(permissionService).addPermissions(page.getName(), properties); } @Test public void add_page_from_content_should_call_permission_service_with_the_parsed_properties() throws Exception { // given: SPage page = new SPage(); page.setName("custompage_test"); page.setDisplayName("My Custom Page"); byte[] zip = zip( file("page.properties", "name=custompage_test\ncontentType=page\ndisplayName=My Custom page"), file("resources/index.html", "someContent")); Properties properties = new Properties(); properties.put("name", "custompage_test"); properties.put("contentType", "page"); properties.put("displayName", "My Custom page"); // when: pageServiceImpl.addPage(page, zip); // then: verify(permissionService).addPermissions(page.getName(), properties); } @Test public void update_page_should_call_permission_service_with_the_parsed_properties() throws Exception { // given: SPage page = new SPage(); page.setId(174L); page.setName("custompage_test"); page.setDisplayName("My Custom Page"); byte[] zip = zip( file("page.properties", "name=custompage_test\ncontentType=page\ndisplayName=My Custom page"), file("resources/index.html", "someContent")); SPageWithContent sPageContent = new SPageWithContent(); sPageContent.setContent(zip); Properties properties = new Properties(); properties.put("name", "custompage_test"); properties.put("contentType", "page"); properties.put("displayName", "My Custom page"); when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, 174L))) .thenReturn(sPageContent); // when: pageServiceImpl.updatePageContent(174L, zip, "myNewContent.zip"); // then: verify(permissionService).addPermissions(page.getName(), properties); } @Test public void updatePageContent_should_not_try_to_remove_permissions_if_previous_page_content_was_empty() throws Exception { final SPage page = new SPage("name", 10201983L, 2005L, false, "contentName"); page.setId(45L); final SPageWithContent previousPageContent = new SPageWithContent(page, new byte[] {}); when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, page.getId()))) .thenReturn(previousPageContent); final byte[] newContent = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair("contentType=" + SContentType.PAGE)); pageServiceImpl.updatePageContent(page.getId(), newContent, "contentName"); verify(permissionService, never()).removePermissions(any()); } @Test public void updatePageContent_should_not_try_to_remove_permissions_if_previous_page_content_was_null() throws Exception { final SPage page = new SPage("name", 10201983L, 2005L, false, "contentName"); page.setId(45L); final SPageWithContent previousPageContent = new SPageWithContent(page, null); when(readPersistenceService.selectById(new SelectByIdDescriptor<>(SPageWithContent.class, page.getId()))) .thenReturn(previousPageContent); final byte[] newContent = IOUtil.zip(getIndexGroovyContentPair(), getPagePropertiesContentPair("contentType=" + SContentType.PAGE)); pageServiceImpl.updatePageContent(page.getId(), newContent, "contentName"); verify(permissionService, never()).removePermissions(any()); } } ================================================ FILE: services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/SPageAssert.java ================================================ /** * 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.page.impl; import static java.lang.String.format; import org.assertj.core.api.AbstractAssert; import org.bonitasoft.engine.page.SPage; /** * {@link SPage} specific assertions - Generated by CustomAssertionGenerator. */ public class SPageAssert extends AbstractAssert { /** * Creates a new {@link SPageAssert} to make assertions on actual SPage. * * @param actual the SPage we want to make assertions on. */ public SPageAssert(SPage actual) { super(actual, SPageAssert.class); } /** * An entry point for SPageAssert to follow AssertJ standard assertThat() statements.
      * With a static import, one's can write directly : assertThat(mySPage) and get specific assertion with * code completion. * * @param actual the SPage we want to make assertions on. * @return a new {@link SPageAssert} */ public static SPageAssert assertThat(SPage actual) { return new SPageAssert(actual); } /** * Verifies that the actual SPage's contentName is equal to the given one. * * @param contentName the given contentName to compare the actual SPage's contentName to. * @return this assertion object. * @throws AssertionError - if the actual SPage's contentName is not equal to the given one. */ public SPageAssert hasContentName(String contentName) { // check that actual SPage we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> contentName to be:\n <%s>\n but was:\n <%s>", actual, contentName, actual.getContentName()); // check if (!actual.getContentName().equals(contentName)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPage's contentType is equal to the given one. * * @param contentType the given contentType to compare the actual SPage's contentType to. * @return this assertion object. * @throws AssertionError - if the actual SPage's contentType is not equal to the given one. */ public SPageAssert hasContentType(String contentType) { // check that actual SPage we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> contentType to be:\n <%s>\n but was:\n <%s>", actual, contentType, actual.getContentType()); // check if (!actual.getContentType().equals(contentType)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPage's description is equal to the given one. * * @param description the given description to compare the actual SPage's description to. * @return this assertion object. * @throws AssertionError - if the actual SPage's description is not equal to the given one. */ public SPageAssert hasDescription(String description) { // check that actual SPage we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> description to be:\n <%s>\n but was:\n <%s>", actual, description, actual.getDescription()); // check if (!actual.getDescription().equals(description)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPage's displayName is equal to the given one. * * @param displayName the given displayName to compare the actual SPage's displayName to. * @return this assertion object. * @throws AssertionError - if the actual SPage's displayName is not equal to the given one. */ public SPageAssert hasDisplayName(String displayName) { // check that actual SPage we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> displayName to be:\n <%s>\n but was:\n <%s>", actual, displayName, actual.getDisplayName()); // check if (!actual.getDisplayName().equals(displayName)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPage's installationDate is equal to the given one. * * @param installationDate the given installationDate to compare the actual SPage's installationDate to. * @return this assertion object. * @throws AssertionError - if the actual SPage's installationDate is not equal to the given one. */ public SPageAssert hasInstallationDate(long installationDate) { // check that actual SPage we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> installationDate to be:\n <%s>\n but was:\n <%s>", actual, installationDate, actual.getInstallationDate()); // check if (actual.getInstallationDate() != installationDate) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPage's installedBy is equal to the given one. * * @param installedBy the given installedBy to compare the actual SPage's installedBy to. * @return this assertion object. * @throws AssertionError - if the actual SPage's installedBy is not equal to the given one. */ public SPageAssert hasInstalledBy(long installedBy) { // check that actual SPage we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> installedBy to be:\n <%s>\n but was:\n <%s>", actual, installedBy, actual.getInstalledBy()); // check if (actual.getInstalledBy() != installedBy) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPage's lastModificationDate is equal to the given one. * * @param lastModificationDate the given lastModificationDate to compare the actual SPage's lastModificationDate to. * @return this assertion object. * @throws AssertionError - if the actual SPage's lastModificationDate is not equal to the given one. */ public SPageAssert hasLastModificationDate(long lastModificationDate) { // check that actual SPage we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> lastModificationDate to be:\n <%s>\n but was:\n <%s>", actual, lastModificationDate, actual.getLastModificationDate()); // check if (actual.getLastModificationDate() != lastModificationDate) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPage's lastUpdatedBy is equal to the given one. * * @param lastUpdatedBy the given lastUpdatedBy to compare the actual SPage's lastUpdatedBy to. * @return this assertion object. * @throws AssertionError - if the actual SPage's lastUpdatedBy is not equal to the given one. */ public SPageAssert hasLastUpdatedBy(long lastUpdatedBy) { // check that actual SPage we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> lastUpdatedBy to be:\n <%s>\n but was:\n <%s>", actual, lastUpdatedBy, actual.getLastUpdatedBy()); // check if (actual.getLastUpdatedBy() != lastUpdatedBy) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPage's name is equal to the given one. * * @param name the given name to compare the actual SPage's name to. * @return this assertion object. * @throws AssertionError - if the actual SPage's name is not equal to the given one. */ public SPageAssert hasName(String name) { // check that actual SPage we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> name to be:\n <%s>\n but was:\n <%s>", actual, name, actual.getName()); // check if (!actual.getName().equals(name)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPage's processDefinitionId is equal to the given one. * * @param processDefinitionId the given processDefinitionId to compare the actual SPage's processDefinitionId to. * @return this assertion object. * @throws AssertionError - if the actual SPage's processDefinitionId is not equal to the given one. */ public SPageAssert hasProcessDefinitionId(long processDefinitionId) { // check that actual SPage we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> processDefinitionId to be:\n <%s>\n but was:\n <%s>", actual, processDefinitionId, actual.getProcessDefinitionId()); // check if (actual.getProcessDefinitionId() != processDefinitionId) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPage is provided. * * @return this assertion object. * @throws AssertionError - if the actual SPage is not provided. */ public SPageAssert isProvided() { // check that actual SPage we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("Expected actual SPage to be provided but was not.", actual); // check if (!actual.isProvided()) throw new AssertionError(errorMessage); // return the current assertion for method chaining return this; } /** * Verifies that the actual SPage is not provided. * * @return this assertion object. * @throws AssertionError - if the actual SPage is provided. */ public SPageAssert isNotProvided() { // check that actual SPage we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("Expected actual SPage not to be provided but was.", actual); // check if (actual.isProvided()) throw new AssertionError(errorMessage); // return the current assertion for method chaining return this; } } ================================================ FILE: services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/SPageImplAssert.java ================================================ /** * 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.page.impl; import static java.lang.String.format; import org.assertj.core.api.AbstractAssert; import org.bonitasoft.engine.page.SPage; /** * {@link SPage} specific assertions - Generated by CustomAssertionGenerator. */ public class SPageImplAssert extends AbstractAssert { /** * Creates a new {@link SPageImplAssert} to make assertions on actual SPageImpl. * * @param actual the SPageImpl we want to make assertions on. */ public SPageImplAssert(SPage actual) { super(actual, SPageImplAssert.class); } /** * An entry point for SPageImplAssert to follow AssertJ standard assertThat() statements.
      * With a static import, one's can write directly : assertThat(mySPageImpl) and get specific assertion * with code completion. * * @param actual the SPageImpl we want to make assertions on. * @return a new {@link SPageImplAssert} */ public static SPageImplAssert assertThat(SPage actual) { return new SPageImplAssert(actual); } /** * Verifies that the actual SPageImpl's contentName is equal to the given one. * * @param contentName the given contentName to compare the actual SPageImpl's contentName to. * @return this assertion object. * @throws AssertionError - if the actual SPageImpl's contentName is not equal to the given one. */ public SPageImplAssert hasContentName(String contentName) { // check that actual SPageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> contentName to be:\n <%s>\n but was:\n <%s>", actual, contentName, actual.getContentName()); // check if (!actual.getContentName().equals(contentName)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPageImpl's contentType is equal to the given one. * * @param contentType the given contentType to compare the actual SPageImpl's contentType to. * @return this assertion object. * @throws AssertionError - if the actual SPageImpl's contentType is not equal to the given one. */ public SPageImplAssert hasContentType(String contentType) { // check that actual SPageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> contentType to be:\n <%s>\n but was:\n <%s>", actual, contentType, actual.getContentType()); // check if (!actual.getContentType().equals(contentType)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPageImpl's description is equal to the given one. * * @param description the given description to compare the actual SPageImpl's description to. * @return this assertion object. * @throws AssertionError - if the actual SPageImpl's description is not equal to the given one. */ public SPageImplAssert hasDescription(String description) { // check that actual SPageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> description to be:\n <%s>\n but was:\n <%s>", actual, description, actual.getDescription()); // check if (!actual.getDescription().equals(description)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPageImpl's displayName is equal to the given one. * * @param displayName the given displayName to compare the actual SPageImpl's displayName to. * @return this assertion object. * @throws AssertionError - if the actual SPageImpl's displayName is not equal to the given one. */ public SPageImplAssert hasDisplayName(String displayName) { // check that actual SPageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> displayName to be:\n <%s>\n but was:\n <%s>", actual, displayName, actual.getDisplayName()); // check if (!actual.getDisplayName().equals(displayName)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPageImpl's id is equal to the given one. * * @param id the given id to compare the actual SPageImpl's id to. * @return this assertion object. * @throws AssertionError - if the actual SPageImpl's id is not equal to the given one. */ public SPageImplAssert hasId(long id) { // check that actual SPageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> id to be:\n <%s>\n but was:\n <%s>", actual, id, actual.getId()); // check if (actual.getId() != id) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPageImpl's installationDate is equal to the given one. * * @param installationDate the given installationDate to compare the actual SPageImpl's installationDate to. * @return this assertion object. * @throws AssertionError - if the actual SPageImpl's installationDate is not equal to the given one. */ public SPageImplAssert hasInstallationDate(long installationDate) { // check that actual SPageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> installationDate to be:\n <%s>\n but was:\n <%s>", actual, installationDate, actual.getInstallationDate()); // check if (actual.getInstallationDate() != installationDate) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPageImpl's installedBy is equal to the given one. * * @param installedBy the given installedBy to compare the actual SPageImpl's installedBy to. * @return this assertion object. * @throws AssertionError - if the actual SPageImpl's installedBy is not equal to the given one. */ public SPageImplAssert hasInstalledBy(long installedBy) { // check that actual SPageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> installedBy to be:\n <%s>\n but was:\n <%s>", actual, installedBy, actual.getInstalledBy()); // check if (actual.getInstalledBy() != installedBy) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPageImpl's lastModificationDate is equal to the given one. * * @param lastModificationDate the given lastModificationDate to compare the actual SPageImpl's lastModificationDate * to. * @return this assertion object. * @throws AssertionError - if the actual SPageImpl's lastModificationDate is not equal to the given one. */ public SPageImplAssert hasLastModificationDate(long lastModificationDate) { // check that actual SPageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> lastModificationDate to be:\n <%s>\n but was:\n <%s>", actual, lastModificationDate, actual.getLastModificationDate()); // check if (actual.getLastModificationDate() != lastModificationDate) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPageImpl's lastUpdatedBy is equal to the given one. * * @param lastUpdatedBy the given lastUpdatedBy to compare the actual SPageImpl's lastUpdatedBy to. * @return this assertion object. * @throws AssertionError - if the actual SPageImpl's lastUpdatedBy is not equal to the given one. */ public SPageImplAssert hasLastUpdatedBy(long lastUpdatedBy) { // check that actual SPageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> lastUpdatedBy to be:\n <%s>\n but was:\n <%s>", actual, lastUpdatedBy, actual.getLastUpdatedBy()); // check if (actual.getLastUpdatedBy() != lastUpdatedBy) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPageImpl's name is equal to the given one. * * @param name the given name to compare the actual SPageImpl's name to. * @return this assertion object. * @throws AssertionError - if the actual SPageImpl's name is not equal to the given one. */ public SPageImplAssert hasName(String name) { // check that actual SPageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> name to be:\n <%s>\n but was:\n <%s>", actual, name, actual.getName()); // check if (!actual.getName().equals(name)) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPageImpl's processDefinitionId is equal to the given one. * * @param processDefinitionId the given processDefinitionId to compare the actual SPageImpl's processDefinitionId * to. * @return this assertion object. * @throws AssertionError - if the actual SPageImpl's processDefinitionId is not equal to the given one. */ public SPageImplAssert hasProcessDefinitionId(long processDefinitionId) { // check that actual SPageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("\nExpected <%s> processDefinitionId to be:\n <%s>\n but was:\n <%s>", actual, processDefinitionId, actual.getProcessDefinitionId()); // check if (actual.getProcessDefinitionId() != processDefinitionId) { throw new AssertionError(errorMessage); } // return the current assertion for method chaining return this; } /** * Verifies that the actual SPageImpl is provided. * * @return this assertion object. * @throws AssertionError - if the actual SPageImpl is not provided. */ public SPageImplAssert isProvided() { // check that actual SPageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("Expected actual SPageImpl to be provided but was not.", actual); // check if (!actual.isProvided()) throw new AssertionError(errorMessage); // return the current assertion for method chaining return this; } /** * Verifies that the actual SPageImpl is not provided. * * @return this assertion object. * @throws AssertionError - if the actual SPageImpl is provided. */ public SPageImplAssert isNotProvided() { // check that actual SPageImpl we want to make assertions on is not null. isNotNull(); // we overrides the default error message with a more explicit one String errorMessage = format("Expected actual SPageImpl not to be provided but was.", actual); // check if (actual.isProvided()) throw new AssertionError(errorMessage); // return the current assertion for method chaining return this; } } ================================================ FILE: services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/SPageImplTest.java ================================================ /** * 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.page.impl; import static org.assertj.core.api.Assertions.assertThat; import org.bonitasoft.engine.page.SContentType; import org.bonitasoft.engine.page.SPage; import org.junit.Test; public class SPageImplTest { private static final String DESCRIPTION = "description"; private static final boolean PROVIDED = true; private static final String NAME = "name"; private static final String DISPLAY_NAME = "display name"; public static final long PROCESS_DEFINITION_ID = 456789L; public static final long INSTALLATION_DATE = 1L; public static final long LAST_MODIFICATION_DATE = 2L; public static final String CONTENT_ZIP = "content.zip"; private static final long LAST_UPDATED_BY = 3L; public static final long INSTALLED_BY = 4L; public static final long TENANT_ID = 5L; public static final long ID = 6L; @Test public void should_set_all_fields() { //given final SPage sPage = new SPage(NAME, INSTALLATION_DATE, INSTALLED_BY, PROVIDED, CONTENT_ZIP); sPage.setId(ID); sPage.setDisplayName(DISPLAY_NAME); sPage.setDescription(DESCRIPTION); sPage.setLastModificationDate(LAST_MODIFICATION_DATE); sPage.setLastUpdatedBy(LAST_UPDATED_BY); //when then SPageImplAssert.assertThat(sPage) .hasName(NAME) .hasDisplayName(DISPLAY_NAME) .isProvided() .hasDescription(DESCRIPTION) .hasInstallationDate(INSTALLATION_DATE) .hasLastModificationDate(LAST_MODIFICATION_DATE) .hasContentName(CONTENT_ZIP) .hasContentType(SContentType.PAGE) .hasLastUpdatedBy(LAST_UPDATED_BY) .hasInstalledBy(INSTALLED_BY) .hasId(ID); } @Test public void should_set_all_fields_from_sPage() { //given final SPage sPage = new SPage(NAME, DESCRIPTION, DISPLAY_NAME, INSTALLATION_DATE, INSTALLED_BY, PROVIDED, LAST_MODIFICATION_DATE, LAST_UPDATED_BY, CONTENT_ZIP); //when SPage sPage2 = new SPage(sPage); //then assertThat(sPage2).isEqualToComparingFieldByField(sPage); } @Test public void should_set_content_type_and_process_definition() { //given final SPage sPage = new SPage(NAME, DESCRIPTION, DISPLAY_NAME, INSTALLATION_DATE, INSTALLED_BY, PROVIDED, LAST_MODIFICATION_DATE, LAST_UPDATED_BY, CONTENT_ZIP); //when sPage.setProcessDefinitionId(PROCESS_DEFINITION_ID); sPage.setContentType(SContentType.FORM); //then SPageImplAssert.assertThat(sPage) .hasProcessDefinitionId(PROCESS_DEFINITION_ID) .hasContentType(SContentType.FORM); assertThat(sPage.toString()).contains(String.valueOf(PROCESS_DEFINITION_ID)).contains(SContentType.FORM); } } ================================================ FILE: services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/SPageMappingImplTest.java ================================================ /** * 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.page.impl; import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; import org.bonitasoft.engine.page.SPageMapping; import org.junit.Test; /** * @author Baptiste Mesta */ public class SPageMappingImplTest { @Test public void testEquals() { SPageMapping sPageMapping1 = new SPageMapping(); SPageMapping sPageMapping2 = new SPageMapping(); setValues(sPageMapping1, sPageMapping2); assertThat(sPageMapping1).isEqualTo(sPageMapping2); } @Test public void testHashCode() { SPageMapping sPageMapping1 = new SPageMapping(); SPageMapping sPageMapping2 = new SPageMapping(); setValues(sPageMapping1, sPageMapping2); assertThat(sPageMapping1.hashCode()).isEqualTo(sPageMapping2.hashCode()); } @Test public void testToString() { SPageMapping sPageMapping1 = new SPageMapping(); SPageMapping sPageMapping2 = new SPageMapping(); setValues(sPageMapping1, sPageMapping2); assertThat(sPageMapping1.toString()).isEqualTo(sPageMapping2.toString()); } void setValues(SPageMapping sPageMapping1, SPageMapping sPageMapping2) { sPageMapping1.setKey("myKey"); sPageMapping1.setUrlAdapter("urlAdapter"); sPageMapping1.setPageAuthorizRules("net.comp.Rule"); sPageMapping1.setUrl("myUrl"); sPageMapping1.setPageId(11L); sPageMapping2.setKey("myKey"); sPageMapping2.setUrlAdapter("urlAdapter"); sPageMapping2.setPageAuthorizRules("net.comp.Rule"); sPageMapping2.setUrl("myUrl"); sPageMapping2.setPageId(11L); } @Test public void parseRulesShouldSetListOfRules() { SPageMapping mapping = new SPageMapping(); mapping.setPageAuthorizRules(",,toto,titi,tutu,"); assertThat(mapping.getPageAuthorizationRules()).containsExactly("toto", "titi", "tutu"); } @Test public void buildRulesAsStringShouldConcatRulesWithComma() { SPageMapping mapping = new SPageMapping(); mapping.setPageAuthorizationRules(Arrays.asList("toto", "titi", "tata")); assertThat(mapping.getPageAuthorizRules()).contains("toto,titi,tata"); } } ================================================ FILE: services/bonita-page/src/test/java/org/bonitasoft/engine/page/impl/SPageMappingServiceImplTest.java ================================================ /** * 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.page.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.anyMap; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.nullable; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SDeletionException; import org.bonitasoft.engine.commons.exceptions.SExecutionException; import org.bonitasoft.engine.commons.exceptions.SObjectCreationException; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.page.AuthorizationRule; import org.bonitasoft.engine.page.SAuthorizationException; import org.bonitasoft.engine.page.SPageMapping; import org.bonitasoft.engine.page.SPageURL; import org.bonitasoft.engine.page.URLAdapter; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; import org.junit.Before; 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; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class SPageMappingServiceImplTest { public static final long PAGE_ID = 2812l; @Rule public ExpectedException expectedException = ExpectedException.none(); @Mock private Recorder recorder; @Mock private ReadPersistenceService persistenceService; @Mock private SessionService sessionService; @Mock private ReadSessionAccessor readSessionAccessor; @InjectMocks private PageMappingServiceImpl pageMappingService; @Before public void before() { URLAdapter urlAdapter = new URLAdapter() { @Override public String adapt(String url, String key, Map context) { return url + "_adapted_" + context.size(); } @Override public String getId() { return "testAdapter"; } }; pageMappingService.setURLAdapters(Collections.singletonList(urlAdapter)); } @Test public void should_create_return_the_created_element() throws Exception { //given //when final List authorizationRules = new ArrayList<>(2); authorizationRules.add("toto"); authorizationRules.add("titi"); SPageMapping sPageMapping = pageMappingService.create("theKey", PAGE_ID, authorizationRules); //then assertThat(sPageMapping).isNotNull(); assertThat(sPageMapping.getKey()).isEqualTo("theKey"); assertThat(sPageMapping.getPageId()).isEqualTo(PAGE_ID); assertThat(sPageMapping.getUrl()).isEqualTo(null); assertThat(sPageMapping.getPageAuthorizationRules()).isEqualTo(authorizationRules); final ArgumentCaptor insertRecord = ArgumentCaptor.forClass(InsertRecord.class); verify(recorder).recordInsert(insertRecord.capture(), eq("PAGE_MAPPING")); assertThat(insertRecord.getValue().getEntity()).isEqualTo(sPageMapping); } @Test public void should_create_throw_creation_exception() throws Exception { //given doThrow(SRecorderException.class).when(recorder).recordInsert(any(InsertRecord.class), anyString()); //when expectedException.expect(SObjectCreationException.class); pageMappingService.create("theKey", PAGE_ID, Collections. emptyList()); } @Test public void should_create_return_external_mapping() throws Exception { //given //when SPageMapping sPageMapping = pageMappingService.create("theKey", "http://www.mycompagny.com/aResource/page.html", "myAdapter", Collections. emptyList()); //then assertThat(sPageMapping).isNotNull(); assertThat(sPageMapping.getKey()).isEqualTo("theKey"); assertThat(sPageMapping.getUrl()).isEqualTo("http://www.mycompagny.com/aResource/page.html"); assertThat(sPageMapping.getPageId()).isNull(); assertThat(sPageMapping.getUrlAdapter()).isEqualTo("myAdapter"); final ArgumentCaptor insertRecord = ArgumentCaptor.forClass(InsertRecord.class); verify(recorder).recordInsert(insertRecord.capture(), eq("PAGE_MAPPING")); assertThat(insertRecord.getValue().getEntity()).isEqualTo(sPageMapping); } @Test public void should_get_return_the_object() throws Exception { SPageMapping pageMapping = new SPageMapping(); pageMapping.setKey("myKey"); doReturn(pageMapping).when(persistenceService).selectOne(any(SelectOneDescriptor.class)); SPageMapping pageHavingMyKey = pageMappingService.get("myKey"); assertThat(pageHavingMyKey).isEqualTo(pageMapping); } @Test public void should_get_throw_not_found() throws Exception { expectedException.expect(SObjectNotFoundException.class); pageMappingService.get("unknown"); } @Test public void should_delete_call_the_recorder() throws Exception { //given SPageMapping sPageMapping = new SPageMapping(); //when pageMappingService.delete(sPageMapping); //then final ArgumentCaptor deleteRecord = ArgumentCaptor.forClass(DeleteRecord.class); verify(recorder).recordDelete(deleteRecord.capture(), eq("PAGE_MAPPING")); assertThat(deleteRecord.getValue().getEntity()).isEqualTo(sPageMapping); } @Test public void should_delete_throw_exception() throws Exception { //given doThrow(SRecorderException.class).when(recorder).recordDelete(any(DeleteRecord.class), anyString()); //when expectedException.expect(SDeletionException.class); pageMappingService.delete(new SPageMapping()); } @Test public void should_update_pageId_set_null_on_url() throws Exception { //given SPageMapping pageMapping = new SPageMapping(); pageMapping.setKey("myKey"); //when pageMappingService.update(pageMapping, 124l); //then ArgumentCaptor updateRecord = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder).recordUpdate(updateRecord.capture(), eq("PAGE_MAPPING")); assertThat(updateRecord.getValue().getEntity()).isEqualTo(pageMapping); assertThat(updateRecord.getValue().getFields()).contains(entry("pageId", 124l), entry("url", null), entry("urlAdapter", null), entry("lastUpdatedBy", 0L)); } @Test public void should_update_throw_exception() throws Exception { //given SPageMapping pageMapping = new SPageMapping(); pageMapping.setKey("myKey"); doThrow(SRecorderException.class).when(recorder).recordUpdate(any(UpdateRecord.class), anyString()); //when expectedException.expect(SObjectModificationException.class); pageMappingService.update(pageMapping, 124l); } @Test public void should_update_url_set_null_on_pageId() throws Exception { //given SPageMapping pageMapping = new SPageMapping(); pageMapping.setKey("myKey"); //when pageMappingService.update(pageMapping, "myNewUrl", "urlAdapter"); //then ArgumentCaptor updateRecord = ArgumentCaptor.forClass(UpdateRecord.class); verify(recorder).recordUpdate(updateRecord.capture(), eq("PAGE_MAPPING")); assertThat(updateRecord.getValue().getEntity()).isEqualTo(pageMapping); assertThat(updateRecord.getValue().getFields()).contains(entry("pageId", null), entry("url", "myNewUrl"), entry("urlAdapter", "urlAdapter"), entry("lastUpdatedBy", 0L)); } @Test public void should_resolveUrl_return_page_id() throws Exception { SPageMapping pageMapping = new SPageMapping(); pageMapping.setPageId(56l); SPageURL sPageURL = pageMappingService.resolvePageURL(pageMapping, Collections. emptyMap(), true); assertThat(sPageURL.getPageId()).isEqualTo(56l); assertThat(sPageURL.getUrl()).isEqualTo(null); } @Test public void should_resolveUrl_return_url_with_no_adapter() throws Exception { SPageMapping pageMapping = new SPageMapping(); pageMapping.setUrl("theUrl"); SPageURL sPageURL = pageMappingService.resolvePageURL(pageMapping, Collections. emptyMap(), true); assertThat(sPageURL.getPageId()).isEqualTo(null); assertThat(sPageURL.getUrl()).isEqualTo("theUrl"); } @Test public void should_resolveUrl_return_call_adapter_with_no_url() throws Exception { SPageMapping pageMapping = new SPageMapping(); pageMapping.setUrl(null); pageMapping.setUrlAdapter("testAdapter"); SPageURL sPageURL = pageMappingService.resolvePageURL(pageMapping, Collections. emptyMap(), true); assertThat(sPageURL.getPageId()).isEqualTo(null); assertThat(sPageURL.getUrl()).isEqualTo("null_adapted_0"); } @Test public void should_resolveUrl_return_url_should_execute_adapter() throws Exception { SPageMapping pageMapping = new SPageMapping(); pageMapping.setUrl("theUrl"); pageMapping.setUrlAdapter("testAdapter"); SPageURL sPageURL = pageMappingService.resolvePageURL(pageMapping, Collections. singletonMap("test", "test"), true); assertThat(sPageURL.getPageId()).isEqualTo(null); assertThat(sPageURL.getUrl()).isEqualTo("theUrl_adapted_1");/* 1 is the map size */ } @Test(expected = SExecutionException.class) public void should_resolveUrl_with_unknown_adapter() throws Exception { SPageMapping pageMapping = new SPageMapping(); pageMapping.setUrl("theUrl"); pageMapping.setUrlAdapter("unknown"); pageMappingService.resolvePageURL(pageMapping, Collections. singletonMap("test", "test"), true); } class ValidRule implements AuthorizationRule { private String validRuleKey; public ValidRule(String validRuleKey) { this.validRuleKey = validRuleKey; } @Override public boolean isAllowed(String key, Map context) { return true; } @Override public String getId() { return validRuleKey; } } class NeverValidRule implements AuthorizationRule { private String invalidRuleKey; public NeverValidRule(String invalidRuleKey) { this.invalidRuleKey = invalidRuleKey; } @Override public boolean isAllowed(String key, Map context) { return false; } @Override public String getId() { return invalidRuleKey; } } @Test public void resolvePageURL_shouldExecuteAuthorizationRule() throws Exception { SPageMapping pageMapping = new SPageMapping(); final String validRuleKey = "validRule"; final List rules = new ArrayList<>(1); rules.add(validRuleKey); pageMapping.setPageAuthorizationRules(rules); final AuthorizationRule authorizationRule = spy(new ValidRule(validRuleKey)); pageMappingService.setAuthorizationRules(Collections.singletonList(authorizationRule)); pageMappingService.resolvePageURL(pageMapping, Collections.emptyMap(), true); verify(authorizationRule).isAllowed(nullable(String.class), anyMap()); } @Test public void resolvePageURL_shouldNotExecuteAuthorizationRule() throws Exception { SPageMapping pageMapping = new SPageMapping(); final String validRuleKey = "validRule"; final List rules = new ArrayList<>(1); rules.add(validRuleKey); pageMapping.setPageAuthorizationRules(rules); final AuthorizationRule authorizationRule = spy(new ValidRule(validRuleKey)); pageMappingService.setAuthorizationRules(Collections.singletonList(authorizationRule)); pageMappingService.resolvePageURL(pageMapping, Collections.emptyMap(), false); verify(authorizationRule, never()).isAllowed(nullable(String.class), anyMap()); } @Test(expected = SAuthorizationException.class) public void resolvePageURL_shouldThrowExceptionIfAuthorizationRuleInvalid() throws Exception { SPageMapping pageMapping = new SPageMapping(); final String invalidRuleKey = "invalidRuleKey"; final List rules = new ArrayList<>(1); rules.add(invalidRuleKey); pageMapping.setPageAuthorizationRules(rules); final AuthorizationRule authorizationRule = new NeverValidRule(invalidRuleKey); pageMappingService.setAuthorizationRules(Arrays. asList(authorizationRule)); pageMappingService.resolvePageURL(pageMapping, Collections. emptyMap(), true); } @Test public void resolvePageURL_shouldAllowAsSoonAsOneRuleIsValid() throws Exception { SPageMapping pageMapping = new SPageMapping(); final String invalidRuleKey = "invalidRule"; final String validKey = "validRule"; final String invalidRuleKey2 = "invalidRule2"; final List rules = new ArrayList<>(2); rules.add(invalidRuleKey); rules.add(validKey); pageMapping.setPageAuthorizationRules(rules); final NeverValidRule neverValidRule = spy(new NeverValidRule(invalidRuleKey)); final ValidRule validRule = spy(new ValidRule(validKey)); final NeverValidRule neverValidRule2 = spy(new NeverValidRule(invalidRuleKey2)); pageMappingService.setAuthorizationRules(Arrays. asList(neverValidRule, validRule)); pageMappingService.resolvePageURL(pageMapping, Collections.emptyMap(), true); verify(neverValidRule).isAllowed(nullable(String.class), anyMap()); verify(validRule).isAllowed(nullable(String.class), anyMap()); verifyNoInteractions(neverValidRule2); } @Test public void resolvePageURL_shouldThrowExecutionExceptionIfAuthorizationRuleIsNotKnown() throws Exception { SPageMapping pageMapping = new SPageMapping(); final String unknownRuleKey = "unknownRuleKey"; final List rules = new ArrayList<>(1); rules.add(unknownRuleKey); pageMapping.setPageAuthorizationRules(rules); expectedException.expect(SExecutionException.class); expectedException.expectMessage("Authorization rule " + unknownRuleKey); pageMappingService.resolvePageURL(pageMapping, Collections. emptyMap(), true); } } ================================================ FILE: services/bonita-persistence/build.gradle ================================================ dependencies { api libs.commonsLang api project(':services:bonita-events') api(libs.hibernateCore) { exclude(module: "jboss-transaction-api_1.2_spec") exclude(group: "javax.activation") //replaced by jakarta } api libs.jakartaActivation api project(':services:bonita-session') api project(':services:bonita-commons') api project(':services:bonita-lock') api libs.slf4jApi implementation(libs.javaxAnnotations) compileOnly libs.jakartaTransactionApi annotationProcessor libs.lombok compileOnly libs.lombok testImplementation libs.mockitoCore testImplementation libs.assertj testImplementation libs.systemRules testAnnotationProcessor libs.lombok testImplementation libs.lombok testRuntimeOnly libs.logback } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/AbstractSelectDescriptor.java ================================================ /** * 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.persistence; import java.util.Map; /** * @author Charles Souillard * @author Matthieu Chaffotte */ public abstract class AbstractSelectDescriptor { private final String queryName; private final Class entityType; private final Class returnType; public AbstractSelectDescriptor(final String queryName, final Class entityType, final Class returnType) { this.entityType = entityType; this.queryName = queryName; this.returnType = returnType; } public String getQueryName() { return queryName; } public Class getEntityType() { return entityType; } public Class getReturnType() { return returnType; } @Override public String toString() { return "AbstractSelectDescriptor [entityType=" + entityType + ", queryName=" + queryName + ", returnType=" + returnType + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((entityType == null) ? 0 : entityType.hashCode()); result = prime * result + ((queryName == null) ? 0 : queryName.hashCode()); result = prime * result + ((returnType == null) ? 0 : returnType.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final AbstractSelectDescriptor other = (AbstractSelectDescriptor) obj; if (entityType == null) { if (other.entityType != null) { return false; } } else if (!entityType.equals(other.entityType)) { return false; } if (queryName == null) { if (other.queryName != null) { return false; } } else if (!queryName.equals(other.queryName)) { return false; } if (returnType == null) { if (other.returnType != null) { return false; } } else if (!returnType.equals(other.returnType)) { return false; } return true; } public abstract Map getInputParameters(); public abstract int getStartIndex(); public abstract int getPageSize(); public abstract boolean hasAFilter(); public abstract QueryOptions getQueryOptions(); public abstract boolean hasOrderByParameters(); } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/AbstractSelectWithParametersDescriptor.java ================================================ /** * 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.persistence; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * @author Charles Souillard * @author Matthieu Chaffotte */ public abstract class AbstractSelectWithParametersDescriptor extends AbstractSelectDescriptor { private Map inputParameters; public AbstractSelectWithParametersDescriptor(final String queryName, final Map inputParameters, final Class entityType) { super(queryName, entityType, (Class) entityType); this.inputParameters = inputParameters; } public AbstractSelectWithParametersDescriptor(final String queryName, final Map inputParameters, final Class entityType, final Class returnType) { super(queryName, entityType, returnType); this.inputParameters = inputParameters; } public Map getInputParameters() { if (this.inputParameters == null) { return Collections.unmodifiableMap(new HashMap()); } return Collections.unmodifiableMap(inputParameters); } public Object getInputParameter(final String key) { return getInputParameters().get(key); } public void addInputParameter(final String key, final Object value) { if (inputParameters == null) { this.inputParameters = new HashMap(); } this.inputParameters.put(key, value); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/ArchivedPersistentObject.java ================================================ /** * Copyright (C) 2025 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.persistence; /** * Interface for all {@link ArchivedPersistentObject}. * * @see PersistentObject */ public interface ArchivedPersistentObject extends PersistentObject { long getArchiveDate(); /** * Get the source object id from which this ArchivedPersistentObject originates. * * @return the id of the source object */ long getSourceObjectId(); Class getPersistentObjectInterface(); } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/Atomikos3HibernateJtaPlatform.java ================================================ /** * 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.persistence; import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; import org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform; /** * @author Charles Souillard */ public class Atomikos3HibernateJtaPlatform extends AbstractJtaPlatform { private static final long serialVersionUID = 4893085097625997082L; private final TransactionManager transactionManager; public Atomikos3HibernateJtaPlatform() throws Exception { final Class userTransactionManagerClass = Class.forName("com.atomikos.icatch.jta.UserTransactionManager"); this.transactionManager = (TransactionManager) userTransactionManagerClass.newInstance(); } @Override protected TransactionManager locateTransactionManager() { return this.transactionManager; } @Override protected UserTransaction locateUserTransaction() { return (UserTransaction) this.transactionManager; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/ConfigurationException.java ================================================ /** * 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.persistence; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Charles Souillard */ public class ConfigurationException extends SBonitaException { private static final long serialVersionUID = 6612093958515809124L; public ConfigurationException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/CustomDataTypesRegistration.java ================================================ /** * 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.persistence; import java.util.HashSet; import java.util.Set; import lombok.Getter; import org.hibernate.boot.SessionFactoryBuilder; import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.SessionFactoryBuilderFactory; import org.hibernate.boot.spi.SessionFactoryBuilderImplementor; import org.hibernate.type.BasicType; import org.slf4j.LoggerFactory; public class CustomDataTypesRegistration implements SessionFactoryBuilderFactory { private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CustomDataTypesRegistration.class); @Getter private static Set typeOverrides = new HashSet<>(); @Override public SessionFactoryBuilder getSessionFactoryBuilder(final MetadataImplementor metadata, final SessionFactoryBuilderImplementor defaultBuilder) { for (BasicType typeOverride : typeOverrides) { logger.debug("Registering custom Hibernate data type {}", typeOverride); metadata.getTypeResolver().registerTypeOverride(typeOverride); } return defaultBuilder; } public static void addTypeOverride(BasicType type) { typeOverrides.add(type); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/DateStoredAsLongUserType.java ================================================ /** * 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.persistence; import java.io.Serializable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; import org.hibernate.HibernateException; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.type.LongType; import org.hibernate.usertype.UserType; /** * @author Emmanuel Duchastenier */ public class DateStoredAsLongUserType implements UserType, Serializable { private static final long serialVersionUID = 1L; private static final LongType type = new LongType(); private static int[] sqlTypes = new int[] { type.sqlType() }; @Override public Class returnedClass() { return Date.class; } public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException { final Object identifier = type.get(rs, names[0], session); if (identifier == null) { return null; } return new Date((Long) identifier); } @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException { try { if (value == null) { st.setNull(index, type.sqlType()); } else { type.set(st, ((Date) value).getTime(), index, session); } } catch (final Exception e) { } } @Override public int[] sqlTypes() { return sqlTypes; } @Override public Object assemble(final Serializable cached, final Object owner) { return cached; } @Override public Object deepCopy(final Object value) { return value; } @Override public Serializable disassemble(final Object value) { return (Serializable) value; } @Override public boolean equals(final Object x, final Object y) { return x == y; } @Override public int hashCode(final Object x) { return x.hashCode(); } @Override public boolean isMutable() { return false; } @Override public Object replace(final Object original, final Object target, final Object owner) { return original; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/DefaultOrderByBuilder.java ================================================ /** * 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.persistence; /** * @author Laurent Leseigneur */ public class DefaultOrderByBuilder implements OrderByBuilder { @Override public void appendOrderBy(StringBuilder builder, String fieldName, OrderByType orderByType) { builder.append(fieldName).append(" ").append(orderByType.getSqlKeyword()); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/DefaultQueryOptions.java ================================================ /** * 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.persistence; /** * @author Charles Souillard */ public class DefaultQueryOptions { private static final QueryOptions queryOptions = new QueryOptions(0, 20); public static QueryOptions getInstance() { return queryOptions; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/FilterOption.java ================================================ /** * 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.persistence; import java.io.Serializable; import lombok.EqualsAndHashCode; import lombok.ToString; import org.bonitasoft.engine.persistence.search.FilterOperationType; /** * @author Matthieu Chaffotte * @author Emmanuel Duchastenier */ @EqualsAndHashCode @ToString public class FilterOption implements Serializable { private static final long serialVersionUID = -5043588187864921664L; private Class persistentClass; private String fieldName; private Object value; private Object to; private Object from; private FilterOperationType operationType; public FilterOption(final Class persistentClass, final String fieldName, final Object value, final FilterOperationType operatorType) { this(persistentClass, fieldName); this.value = value; operationType = operatorType; } public FilterOption(final Class persistentClass, final String fieldName, final Object value) { // EQUALS operation by default: this(persistentClass, fieldName, value, FilterOperationType.EQUALS); } public FilterOption(final Class persistentClass, final String fieldName) { this.persistentClass = persistentClass; this.fieldName = fieldName; } public FilterOption(final Class persistentClass, final String fieldName, final Object from, final Object to) { // EQUALS operation by default: this(persistentClass, fieldName, null, FilterOperationType.BETWEEN); this.from = from; this.to = to; } public FilterOption(final FilterOperationType operatorType) { operationType = operatorType; } public Class getPersistentClass() { return persistentClass; } public String getFieldName() { return fieldName; } public Object getValue() { return value; } public Object getFrom() { return from; } public void setFrom(final Object from) { this.from = from; } public Object getTo() { return to; } public FilterOption like(final Object value) { this.value = value; operationType = FilterOperationType.LIKE; return this; } public FilterOption between(final Object from, final Object to) { this.from = from; this.to = to; operationType = FilterOperationType.BETWEEN; return this; } public FilterOperationType getFilterOperationType() { return operationType; } public static FilterOption or() { return new FilterOption(FilterOperationType.OR); } public static FilterOption and() { return new FilterOption(FilterOperationType.AND); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HQLQueryBuilder.java ================================================ /** * 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.persistence; import java.util.Collection; import java.util.Map; import org.hibernate.Session; import org.hibernate.query.ParameterMetadata; import org.hibernate.query.Query; import org.hibernate.query.QueryParameter; import org.hibernate.type.CustomType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Baptiste Mesta */ public class HQLQueryBuilder extends QueryBuilder { private static final Logger logger = LoggerFactory.getLogger(HQLQueryBuilder.class); HQLQueryBuilder(Session session, Query baseQuery, OrderByBuilder orderByBuilder, Map classAliasMappings, char likeEscapeCharacter, OrderByCheckingMode orderByCheckingMode, SelectListDescriptor selectDescriptor) { super(session, baseQuery, orderByBuilder, classAliasMappings, likeEscapeCharacter, orderByCheckingMode, selectDescriptor); } @Override Query rebuildQuery(AbstractSelectDescriptor selectDescriptor, Session session, Query query) { Query generatedQuery = session.createQuery(stringQueryBuilder.toString()); ParameterMetadata parameterMetadata = generatedQuery.getParameterMetadata(); for (Map.Entry parameter : getQueryParameters().entrySet()) { if (parameter.getValue() instanceof Collection) { generatedQuery.setParameterList(parameter.getKey(), (Collection) parameter.getValue()); } else { generatedQuery.setParameter(parameter.getKey(), convertParameterValueToFieldType(parameter, parameterMetadata)); } } return generatedQuery; } private Object convertParameterValueToFieldType(Map.Entry parameter, ParameterMetadata parameterMetadata) { QueryParameter namedParameterDescriptor = parameterMetadata.getQueryParameter(parameter.getKey()); Object convertedParameterValue = parameter.getValue(); if (convertedParameterValue != null) { String parameterValueType = convertedParameterValue.getClass().getSimpleName(); String expectedType = namedParameterDescriptor.getHibernateType().getName(); if (!expectedType.equals(parameterValueType.toLowerCase())) { logger.debug("Trying to convert from {} to expected type {} ", parameterValueType, expectedType); switch (expectedType) { case "long": convertedParameterValue = convertToLong(parameterValueType.toLowerCase(), convertedParameterValue); break; case "string": convertedParameterValue = convertToString(parameterValueType.toLowerCase(), convertedParameterValue); break; case "org.hibernate.type.EnumType": convertedParameterValue = convertToEnum(parameterValueType.toLowerCase(), convertedParameterValue, (((CustomType) namedParameterDescriptor.getHibernateType()) .getUserType()).returnedClass()); break; default: logger.debug("Not converting from {} to {}", parameterValueType, expectedType); break; } } } return convertedParameterValue; } private Object convertToEnum(String parameterValueType, Object parameterValue, Class expectedTypeClass) { if (parameterValueType.equals("string")) { return Enum.valueOf(expectedTypeClass, (String) parameterValue); } return parameterValue; } private Long convertToLong(String parameterValueType, Object parameterValue) { if (parameterValueType.equals("string")) { return Long.valueOf((String) parameterValue); } else if (parameterValueType.equals("integer")) { return Long.valueOf((Integer) parameterValue); } else { throw new IllegalArgumentException( "Unsupported type " + parameterValueType + ", cannot convert it to 'long'."); } } private String convertToString(String parameterValueType, Object parameterValue) { if (parameterValueType.equals("long")) { return String.valueOf(parameterValue); } else if (parameterValueType.equals("integer")) { return String.valueOf(parameterValue); } else { throw new IllegalArgumentException( "Unsupported type " + parameterValueType + ", cannot convert it to 'String'"); } } @Override protected void addConstantsAsParameters(Query query) { // nothing to do, Native queries require to inject constant parameters here } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernateConfigurationProvider.java ================================================ /** * 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.persistence; import java.util.List; import java.util.Map; import java.util.Properties; import org.bonitasoft.engine.services.Vendor; import org.hibernate.SessionFactory; /** * @author Celine Souchet */ public interface HibernateConfigurationProvider { Map getClassAliasMappings(); List getMappingExclusions(); Map getCacheQueries(); void bootstrap(Properties extraHibernateProperties); Vendor getVendor(); SessionFactory getSessionFactory(); List> getMappedClasses(); } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernateConfigurationProviderImpl.java ================================================ /** * 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.persistence; import static org.bonitasoft.engine.services.Vendor.POSTGRES; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Properties; import javax.persistence.SharedCacheMode; import lombok.Getter; import org.bonitasoft.engine.services.Vendor; import org.hibernate.Interceptor; import org.hibernate.SessionFactory; import org.hibernate.boot.Metadata; import org.hibernate.boot.MetadataBuilder; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.SessionFactoryBuilder; import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.mapping.PersistentClass; import org.hibernate.service.ServiceRegistry; import org.hibernate.type.BasicType; /** * @author Charles Souillard * @author Baptiste Mesta * @author Celine Souchet */ public class HibernateConfigurationProviderImpl implements HibernateConfigurationProvider { private final HibernateResourcesConfigurationProvider hibernateResourcesConfigurationProvider; protected final Properties properties; private final List mappingExclusions; @Getter protected Vendor vendor; @Getter private SessionFactory sessionFactory; @Getter private final List> mappedClasses = new ArrayList<>(); public HibernateConfigurationProviderImpl(final Properties properties, final HibernateResourcesConfigurationProvider hibernateResourcesConfigurationProvider, final List mappingExclusions) { this.properties = properties; this.hibernateResourcesConfigurationProvider = hibernateResourcesConfigurationProvider; this.mappingExclusions = mappingExclusions; } @Override public Map getClassAliasMappings() { return hibernateResourcesConfigurationProvider.getClassAliasMappings(); } @Override public List getMappingExclusions() { return Collections.unmodifiableList(mappingExclusions); } @Override public Map getCacheQueries() { return null; } @Override public void bootstrap(Properties extraHibernateProperties) { StandardServiceRegistryBuilder standardRegistryBuilder = new StandardServiceRegistryBuilder( new BootstrapServiceRegistryBuilder().build()); Properties allProps = gatherAllProperties(extraHibernateProperties, standardRegistryBuilder); StandardServiceRegistry standardRegistry = standardRegistryBuilder.build(); this.vendor = Vendor.fromHibernateDialectProperty(allProps.getProperty("hibernate.dialect")); setCustomHibernateDataTypesAndProperties(); Metadata metadata = buildHibernateMetadata(standardRegistry); this.sessionFactory = applyInterceptors(metadata, allProps).build(); for (PersistentClass entityBinding : metadata.getEntityBindings()) { mappedClasses.add(entityBinding.getMappedClass()); } } /** * Set custom Hibernate data types using {@link CustomDataTypesRegistration} and set Hibernate system properties. */ protected void setCustomHibernateDataTypesAndProperties() { switch (vendor) { case POSTGRES -> { CustomDataTypesRegistration.addTypeOverride(new PostgresMaterializedBlobType()); CustomDataTypesRegistration.addTypeOverride(new PostgresMaterializedClobType()); CustomDataTypesRegistration.addTypeOverride(new PostgresXMLType()); } case OTHER -> CustomDataTypesRegistration.addTypeOverride(new XMLType()); default -> throw new IllegalStateException("Unsupported vendor: " + vendor); } } /** * Build the hibernate metadata from the provided resources and entities. * * @param standardRegistry the standard registry of services needed to create metadata sources * @return the hibernate metadata */ private Metadata buildHibernateMetadata(ServiceRegistry standardRegistry) { MetadataSources metadataSources = new MetadataSources(standardRegistry) { @Override public MetadataBuilder getMetadataBuilder() { MetadataBuilder metadataBuilder = super.getMetadataBuilder(); for (BasicType typeOverride : CustomDataTypesRegistration.getTypeOverrides()) { metadataBuilder.applyBasicType(typeOverride); } applyCacheMode(metadataBuilder); return metadataBuilder; } }; metadataSources.addPackage("org.bonitasoft.engine.persistence"); for (final String resource : hibernateResourcesConfigurationProvider.getResources()) { metadataSources.addResource(resource); } for (Class entity : hibernateResourcesConfigurationProvider.getEntities()) { metadataSources.addAnnotatedClass(entity); } return metadataSources.buildMetadata(); } /** * Apply interceptors to a new session factory. * * @param metadata the hibernate metadata * @param allProps the hibernate properties * @return a new session factory builder * @throws IllegalStateException if an interceptor class is unknown or cannot be instantiated */ protected SessionFactoryBuilder applyInterceptors(Metadata metadata, Properties allProps) throws IllegalStateException { SessionFactoryBuilder sessionFactoryBuilder = metadata.getSessionFactoryBuilder(); final String className = allProps.getProperty("hibernate.interceptor"); if (className != null && !className.isEmpty()) { try { sessionFactoryBuilder.applyInterceptor( (Interceptor) Class.forName(className).getDeclaredConstructor().newInstance()); } catch (final ReflectiveOperationException e) { throw new IllegalStateException("Unknown interceptor class " + className, e); } } if (vendor == POSTGRES) { sessionFactoryBuilder.applyInterceptor(new PostgresInterceptor()); } return sessionFactoryBuilder; } protected void applyCacheMode(MetadataBuilder metadataBuilder) { metadataBuilder.applySharedCacheMode(SharedCacheMode.NONE); } protected Properties gatherAllProperties(Properties extraHibernateProperties, StandardServiceRegistryBuilder standardRegistryBuilder) { Properties allProps = new Properties(); allProps.putAll(properties); allProps.putAll(extraHibernateProperties); for (Map.Entry prop : allProps.entrySet()) { standardRegistryBuilder.applySetting(prop.getKey().toString(), prop.getValue()); } return allProps; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernateMetricsBinder.java ================================================ /** * 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.persistence; import org.hibernate.SessionFactory; public interface HibernateMetricsBinder { void bindMetrics(SessionFactory sessionFactory); } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernatePersistenceService.java ================================================ /** * 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.persistence; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.SortedSet; import java.util.TreeSet; import javax.annotation.PreDestroy; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.ClassReflector; import org.bonitasoft.engine.commons.exceptions.SRetryableException; import org.bonitasoft.engine.persistence.search.FilterOperationType; import org.bonitasoft.engine.sequence.SequenceManager; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.SPersistenceException; import org.bonitasoft.engine.services.UpdateDescriptor; import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.StaleStateException; import org.hibernate.exception.LockAcquisitionException; import org.hibernate.query.Query; import org.hibernate.stat.Statistics; import org.slf4j.Logger; /** * Hibernate implementation of the persistence service * * @author Charles Souillard * @author Nicolas Chabanoles * @author Yanyan Liu * @author Matthieu Chaffotte * @author Celine Souchet * @author Laurent Vaills * @author Guillaume Rosinosky */ @Slf4j public class HibernatePersistenceService implements PersistenceService { @Getter private final SessionFactory sessionFactory; @Getter private final Map classAliasMappings; @Getter // for testing purposes private final Map cacheQueries; private final List> classMapping; private final List mappingExclusions; private final Statistics statistics; private final SequenceManager sequenceManager; private int stat_display_count; private final QueryBuilderFactory queryBuilderFactory; public HibernatePersistenceService(final HibernateConfigurationProvider hbmConfigurationProvider, final Properties extraHibernateProperties, final SequenceManager sequenceManager, final QueryBuilderFactory queryBuilderFactory, HibernateMetricsBinder hibernateMetricsBinder) { this.sequenceManager = sequenceManager; hbmConfigurationProvider.bootstrap(extraHibernateProperties); sessionFactory = hbmConfigurationProvider.getSessionFactory(); this.queryBuilderFactory = queryBuilderFactory; statistics = sessionFactory.getStatistics(); classMapping = hbmConfigurationProvider.getMappedClasses(); classAliasMappings = hbmConfigurationProvider.getClassAliasMappings(); mappingExclusions = hbmConfigurationProvider.getMappingExclusions(); cacheQueries = hbmConfigurationProvider.getCacheQueries(); hibernateMetricsBinder.bindMetrics(getSessionFactory()); } /** * Log synthetic information about cache every 10.000 sessions, if hibernate.gather_statistics, is enabled. */ private void logStats() { if (!statistics.isStatisticsEnabled() || !getLogger().isInfoEnabled()) { return; } if (stat_display_count == 10 || stat_display_count == 100 || stat_display_count == 1000 || stat_display_count % 10000 == 0) { final long query_cache_hit = statistics.getQueryCacheHitCount(); final long query_cache_miss = statistics.getQueryCacheMissCount(); final long query_cache_put = statistics.getQueryCachePutCount(); final long level_2_cache_hit = statistics.getSecondLevelCacheHitCount(); final long level_2_cache_miss = statistics.getSecondLevelCacheMissCount(); final long level_2_put = statistics.getSecondLevelCachePutCount(); getLogger().info("Query Cache Ratio " + (int) ((double) query_cache_hit / (query_cache_hit + query_cache_miss) * 100) + "% " + query_cache_hit + " hits " + query_cache_miss + " miss " + query_cache_put + " puts"); getLogger().info("2nd Level Cache Ratio " + (int) ((double) level_2_cache_hit / (level_2_cache_hit + level_2_cache_miss) * 100) + "% " + level_2_cache_hit + " hits " + level_2_cache_miss + " miss " + level_2_put + " puts"); } stat_display_count++; } protected Session getSession() throws SPersistenceException { logStats(); try { return sessionFactory.getCurrentSession(); } catch (final HibernateException e) { throw new SPersistenceException(e); } } public void flushStatements() throws SPersistenceException { getSession().flush(); } @Override public void delete(final T entity) throws SPersistenceException { if (getLogger().isDebugEnabled()) { getLogger().debug("Deleting instance of class {} with id={}", entity.getClass().getSimpleName(), entity.getId()); } final Session session = getSession(); try { if (session.contains(entity)) { session.delete(entity); } else { final Class mappedClass = getMappedClass(entity.getClass()); // Deletion must be performed on the session entity and not on a potential transitional entity. final Object pe = session.get(mappedClass, entity.getId()); session.delete(pe); } } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) { throw new SRetryableException(e); } catch (final HibernateException e) { throw new SPersistenceException(e); } } @Override public int update(final String updateQueryName) throws SPersistenceException { return update(updateQueryName, null); } @Override public int update(final String updateQueryName, final Map inputParameters) throws SPersistenceException { final Query query = getSession().getNamedQuery(updateQueryName); try { if (inputParameters != null) { setParameters(query, inputParameters); } return query.executeUpdate(); } catch (final HibernateException he) { throw new SPersistenceException(he); } } @Override public void refresh(final PersistentObject entity) throws SPersistenceException { if (entity != null) { try { getSession().refresh(entity); } catch (final HibernateException e) { throw new SPersistenceException("Failed to refresh entity " + entity.getClass().getSimpleName() + " (id=" + entity.getId() + ")", e); } } } @Override public void deleteAll(final Class entityClass) throws SPersistenceException { final Class mappedClass = getMappedClass(entityClass); final Query query = getSession().getNamedQuery("deleteAll" + mappedClass.getSimpleName()); try { query.executeUpdate(); } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) { throw new SRetryableException(e); } catch (final HibernateException he) { throw new SPersistenceException(he); } } @Override public T insert(final T entity) throws SPersistenceException { final Class entityClass = entity.getClass(); checkClassMapping(entityClass); final Session session = getSession(); setId(entity); try { session.save(entity); return entity; } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) { throw new SRetryableException(e); } catch (final HibernateException he) { throw new SPersistenceException(he); } } @Override public List insertInBatch(final List entities) throws SPersistenceException { if (!entities.isEmpty()) { final Session session = getSession(); for (final PersistentObject entity : entities) { final Class entityClass = entity.getClass(); checkClassMapping(entityClass); setId(entity); session.save(entity); } } return entities; } @Override public void update(final UpdateDescriptor updateDescriptor) throws SPersistenceException { // FIXME: deal with disconnected objects: final Class entityClass = updateDescriptor.getEntity().getClass(); checkClassMapping(entityClass); final PersistentObject entity = updateDescriptor.getEntity(); final Session session = getSession(); if (!session.contains(entity)) { throw new SPersistenceException("The object cannot be updated because it's disconnected " + entity); } for (final Map.Entry field : updateDescriptor.getFields().entrySet()) { setField(entity, field.getKey(), field.getValue()); } } private void setField(final PersistentObject entity, final String fieldName, final Object parameterValue) throws SPersistenceException { Long id = null; try { id = entity.getId(); ClassReflector.setField(entity, fieldName, parameterValue); } catch (final Exception e) { throw new SPersistenceException("Problem while updating entity: " + entity + " with id: " + id, e); } } @Override public T selectOne(final SelectOneDescriptor selectDescriptor) throws SBonitaReadException { try { return selectOne(getSession(), selectDescriptor); } catch (final SPersistenceException e) { throw new SBonitaReadException(e, selectDescriptor); } } Class getMappedClass(final Class entityClass) throws SPersistenceException { if (classMapping.contains(entityClass)) { return entityClass; } throw new SPersistenceException("Unable to locate class " + entityClass + " in Hibernate configuration"); } private void checkClassMapping(final Class entityClass) throws SPersistenceException { if (!classMapping.contains(entityClass) && !mappingExclusions.contains(entityClass.getName())) { throw new SPersistenceException("Unable to locate class " + entityClass + " in Hibernate configuration"); } } @Override public T selectById(final SelectByIdDescriptor selectDescriptor) throws SBonitaReadException { try { final Session session = getSession(); final T object = this.selectById(session, selectDescriptor); if (selectDescriptor.isReadOnly()) { disconnectEntityFromSession(session, object); } return object; } catch (final SPersistenceException e) { throw new SBonitaReadException(e, selectDescriptor); } } private static void disconnectEntityFromSession(Session session, T object) { if (object != null) { session.evict(object); } } @SuppressWarnings("unchecked") T selectObjectById(final Session session, final SelectByIdDescriptor selectDescriptor) throws SBonitaReadException { Class mappedClass; try { mappedClass = getMappedClass(selectDescriptor.getEntityType()); } catch (final SPersistenceException e) { throw new SBonitaReadException(e); } try { return (T) session.get(mappedClass, selectDescriptor.getId()); } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) { throw new SRetryableException(e); } catch (final HibernateException he) { throw new SBonitaReadException(he); } } @SuppressWarnings("unchecked") T selectById(final Session session, final SelectByIdDescriptor selectDescriptor) throws SBonitaReadException { return selectObjectById(session, selectDescriptor); } @SuppressWarnings("unchecked") private T selectOne(final Session session, final SelectOneDescriptor selectDescriptor) throws SBonitaReadException { try { checkClassMapping(selectDescriptor.getEntityType()); } catch (final SPersistenceException e) { throw new SBonitaReadException(e); } final Query query = session.getNamedQuery(selectDescriptor.getQueryName()); setQueryCache(query, selectDescriptor.getQueryName()); final Map parameters = selectDescriptor.getInputParameters(); if (parameters != null) { setParameters(query, parameters); } query.setMaxResults(1); try { return disconnectIfReadOnly((T) query.uniqueResult(), query, session); } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) { throw new SRetryableException(e); } catch (final HibernateException he) { throw new SBonitaReadException(he); } } private boolean isCacheEnabled(String queryName) { return cacheQueries != null && cacheQueries.containsKey(queryName); } private void setQueryCache(final Query query, final String name) { if (isCacheEnabled(name)) { query.setCacheable(true); } } @Override public List selectList(final SelectListDescriptor selectDescriptor) throws SBonitaReadException { try { final Class entityClass = selectDescriptor.getEntityType(); checkClassMapping(entityClass); final Session session = getSession(); org.hibernate.query.Query query = queryBuilderFactory.createQueryBuilderFor(session, selectDescriptor) .cache(isCacheEnabled(selectDescriptor.getQueryName())) .build(); @SuppressWarnings("unchecked") final List list = query.list(); if (list != null) { disconnectIfReadOnly(list, query, session); return list; } return Collections.emptyList(); } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) { throw new SRetryableException(e); } catch (final HibernateException | SPersistenceException e) { throw new SBonitaReadException(e, selectDescriptor); } } private static void disconnectIfReadOnly(List list, Query query, Session session) { if (query.isReadOnly()) { for (T t : list) { disconnectEntityFromSession(session, t); } } } private static T disconnectIfReadOnly(T object, Query query, Session session) { if (query.isReadOnly()) { disconnectEntityFromSession(session, object); } return object; } private void setParameters(final Query query, final Map inputParameters) { for (final Map.Entry entry : inputParameters.entrySet()) { final Object value = entry.getValue(); if (value instanceof Collection) { query.setParameterList(entry.getKey(), (Collection) value); } else { query.setParameter(entry.getKey(), value); } } } @Override public void delete(final long id, final Class entityClass) throws SPersistenceException { final Class mappedClass = getMappedClass(entityClass); final Query query = getSession().getNamedQuery("delete" + mappedClass.getSimpleName()); query.setParameter("id", id); try { query.executeUpdate(); } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) { throw new SRetryableException(e); } catch (final HibernateException he) { throw new SPersistenceException(he); } } @Override public void delete(final List ids, final Class entityClass) throws SPersistenceException { final Class mappedClass = getMappedClass(entityClass); final Query query = getSession().getNamedQuery("deleteByIds" + mappedClass.getSimpleName()); query.setParameterList("ids", ids); try { query.executeUpdate(); } catch (final AssertionFailure | LockAcquisitionException | StaleStateException e) { throw new SRetryableException(e); } catch (final HibernateException sse) { throw new SPersistenceException(sse); } } @PreDestroy public void destroy() { getLogger().info( "Closing Hibernate session factory of " + getClass().getName()); sessionFactory.close(); } @Override public long getNumberOfEntities(final Class entityClass, final QueryOptions options, final Map parameters) throws SBonitaReadException { return getNumberOfEntities(entityClass, null, options, parameters); } @Override public long getNumberOfEntities(final Class entityClass, final String querySuffix, final QueryOptions options, final Map parameters) throws SBonitaReadException { List filters; if (options == null) { filters = Collections.emptyList(); } else { filters = options.getFilters(); } final String queryName = getQueryName("getNumberOf", querySuffix, entityClass, filters); final SelectListDescriptor descriptor = new SelectListDescriptor<>(queryName, parameters, entityClass, Long.class, options); return selectList(descriptor).get(0); } @Override public List searchEntity(final Class entityClass, final QueryOptions options, final Map parameters) throws SBonitaReadException { return searchEntity(entityClass, null, options, parameters); } @Override public List searchEntity(final Class entityClass, final String querySuffix, final QueryOptions options, final Map parameters) throws SBonitaReadException { final String queryName = getQueryName("search", querySuffix, entityClass, options.getFilters()); final SelectListDescriptor descriptor = new SelectListDescriptor<>(queryName, parameters, entityClass, options); return selectList(descriptor); } private String getQueryName(final String prefix, final String suffix, final Class entityClass, final List filters) { final SortedSet query = new TreeSet<>(); for (final FilterOption filter : filters) { // if filter is just an operator, PersistentClass is not defined: if (filter.getPersistentClass() != null) { query.add(filter.getPersistentClass().getSimpleName()); } } final String searchOnClassName = entityClass.getSimpleName(); query.remove(searchOnClassName); final StringBuilder builder = new StringBuilder(prefix); builder.append(searchOnClassName); if (!query.isEmpty()) { builder.append("with"); } for (final String entity : query) { builder.append(entity); } if (suffix != null) { builder.append(suffix); } return builder.toString(); } protected void setId(final PersistentObject entity) throws SPersistenceException { if (entity == null) { return; } // if this entity has no id, set it Long id = null; try { id = entity.getId(); } catch (final Exception e) { // this is a new object to save } if (id == null || id == -1 || id == 0) { try { id = sequenceManager.getNextId(entity.getClass().getName()); ClassReflector.invokeSetter(entity, "setId", long.class, id); } catch (final Exception e) { throw new SPersistenceException("Problem while saving entity: " + entity + " with id: " + id, e); } } } protected Logger getLogger() { return log; } @Override public void deleteAll(final Class entityClass, final List filters) throws SPersistenceException { final Session session = getSession(); final String entityClassName = entityClass.getCanonicalName(); boolean hasFilters = filters != null && !filters.isEmpty(); Map parameters = new HashMap<>(); String baseQuery = "DELETE FROM " + entityClassName + " " + (hasFilters ? getClassAliasMappings().get(entityClassName) : ""); if (hasFilters) { if (filters.stream().anyMatch(f -> f.getFilterOperationType() == FilterOperationType.LIKE)) { throw new IllegalStateException("Delete queries do not support queries with LIKE"); } QueryGeneratorForFilters.QueryGeneratedFilters whereClause = new QueryGeneratorForFilters( getClassAliasMappings(), '%'/* there is no 'like' in these delete queries */) .generate(filters); parameters.putAll(whereClause.getParameters()); baseQuery += " WHERE ( " + whereClause.getFilters() + " )"; } Query query = session.createQuery(baseQuery); parameters.forEach(query::setParameter); query.executeUpdate(); if (getLogger().isDebugEnabled()) { getLogger().debug("Deleting all instances of class {} matching the provided filters", entityClass.getSimpleName()); } } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernateResourcesConfigurationProvider.java ================================================ /** * 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.persistence; import java.util.List; import java.util.Map; import java.util.Set; /** * @author Celine Souchet */ public interface HibernateResourcesConfigurationProvider { void setHbmResources(final List resources); Set getEntities(); Set getResources(); Map getClassAliasMappings(); } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernateResourcesConfigurationProviderImpl.java ================================================ /** * 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.persistence; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * @author Charles Souillard * @author Celine Souchet * @author Matthieu Chaffotte */ public class HibernateResourcesConfigurationProviderImpl implements HibernateResourcesConfigurationProvider { private final Set resources = new HashSet<>(); private final Map classAliasMappings = new HashMap<>(); private Set entities = new HashSet<>(); public HibernateResourcesConfigurationProviderImpl() { super(); } @Override public void setHbmResources(final List resources) { for (final HibernateResourcesProvider hibernateResourcesProvider : resources) { for (final String resource : hibernateResourcesProvider.getResources()) { this.resources.add(resource); } classAliasMappings.putAll(hibernateResourcesProvider.getClassAliasMappings()); for (String entity : hibernateResourcesProvider.getEntities()) { try { entities.add(Class.forName(entity)); } catch (ClassNotFoundException e) { throw new IllegalArgumentException(String.format("Entity class %s not found", entity)); } } } } @Override public Set getEntities() { return entities; } @Override public Set getResources() { return resources; } @Override public Map getClassAliasMappings() { return classAliasMappings; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/HibernateResourcesProvider.java ================================================ /** * 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.persistence; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * @author Baptiste Mesta * @author Celine Souchet */ public class HibernateResourcesProvider { private Set resources = new HashSet<>(); private Set entities = new HashSet<>(); private Map classAliasMappings = new HashMap<>(); public void setResources(final Set resources) { final Set hashSet = new HashSet<>(resources.size()); for (final String resource : resources) { hashSet.add(resource.trim()); } this.resources = hashSet; } public void setEntities(Set resources) { entities.addAll(resources); } public Set getResources() { return resources; } public Map getClassAliasMappings() { return classAliasMappings; } public void setClassAliasMappings(final Map classAliasMappings) { this.classAliasMappings = classAliasMappings; } public Set getEntities() { return entities; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/JNDIBitronixJtaPlatform.java ================================================ /** * 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.persistence; import javax.transaction.TransactionManager; import org.hibernate.engine.transaction.jta.platform.internal.BitronixJtaPlatform; /** * @author Laurent Vaills */ public class JNDIBitronixJtaPlatform extends BitronixJtaPlatform { private static final long serialVersionUID = 4893085097625997082L; @Override protected TransactionManager locateTransactionManager() { // Force the lookup to JNDI to find the TransactionManager : since we share it between // Hibernate and Quartz, I prefer to force the JNDI lookup in order to be sure that // they are using the same instance. return (TransactionManager) jndiService().locate("java:comp/UserTransaction"); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/Narayana5HibernateJtaPlatform.java ================================================ /** * 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.persistence; import java.lang.reflect.Method; import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; import org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform; /** * @author Charles Souillard */ public class Narayana5HibernateJtaPlatform extends AbstractJtaPlatform { private static final long serialVersionUID = 4893085097625997082L; private final TransactionManager transactionManager; private final Method getUserTransactionMethod; private final Object jtaEnvironmentBeanInstance; public Narayana5HibernateJtaPlatform() throws Exception { final Class jtaPropertyManagerClass = Class.forName("com.arjuna.ats.jta.common.jtaPropertyManager"); final Method getJTAEnvironmentBeanMethod = jtaPropertyManagerClass.getMethod("getJTAEnvironmentBean"); jtaEnvironmentBeanInstance = getJTAEnvironmentBeanMethod.invoke(null); final Class jtaEnvironmentBeanClass = jtaEnvironmentBeanInstance.getClass(); final Method getTransactionManagerMethod = jtaEnvironmentBeanClass.getMethod("getTransactionManager"); transactionManager = (TransactionManager) getTransactionManagerMethod.invoke(jtaEnvironmentBeanInstance); getUserTransactionMethod = jtaEnvironmentBeanClass.getMethod("getUserTransaction"); } @Override protected TransactionManager locateTransactionManager() { return transactionManager; } @Override protected UserTransaction locateUserTransaction() { try { return (UserTransaction) getUserTransactionMethod.invoke(jtaEnvironmentBeanInstance); } catch (Exception e) { throw new RuntimeException(e); } } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/OrderAndField.java ================================================ /** * 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.persistence; /** * @author Baptiste Mesta */ public class OrderAndField { private OrderByType order; private String field; public OrderAndField(final OrderByType order, final String field) { super(); this.order = order; this.field = field; } public OrderByType getOrder() { return order; } public void setOrder(final OrderByType order) { this.order = order; } public String getField() { return field; } public void setField(final String field) { this.field = field; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (field == null ? 0 : field.hashCode()); result = prime * result + (order == null ? 0 : order.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final OrderAndField other = (OrderAndField) obj; if (field == null) { if (other.field != null) { return false; } } else if (!field.equals(other.field)) { return false; } if (order != other.order) { return false; } return true; } @Override public String toString() { return "OrderAndField [order=" + order + ", field=" + field + "]"; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/OrderByBuilder.java ================================================ /** * 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.persistence; /** * @author Laurent Leseigneur */ public interface OrderByBuilder { void appendOrderBy(StringBuilder builder, String fieldName, OrderByType orderByType); } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/OrderByCheckingMode.java ================================================ /** * 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.persistence; /** * It's used when we check if the queries who return a list have an "Order by" clause. * * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ public enum OrderByCheckingMode { /** * Do nothing */ NONE, /** * Log a warning message */ WARNING, /** * Throw an IllegalArgumentException */ STRICT } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/OrderByOption.java ================================================ /** * 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.persistence; import java.io.Serializable; /** * @author Charles Souillard */ public class OrderByOption implements Serializable { private static final long serialVersionUID = -3903433577282357734L; private final Class clazz; private final String fieldName; private final OrderByType orderByType; public OrderByOption(final Class clazz, final String fieldName, final OrderByType orderByType) { super(); this.clazz = clazz; this.fieldName = fieldName; this.orderByType = orderByType; } public Class getClazz() { return clazz; } public String getFieldName() { return fieldName; } public OrderByType getOrderByType() { return orderByType; } @Override public String toString() { return "OrderByOption [clazz=" + clazz + ", fieldName=" + fieldName + ", orderByType=" + orderByType + "]"; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof OrderByOption that)) return false; if (clazz != null ? !clazz.equals(that.clazz) : that.clazz != null) return false; if (fieldName != null ? !fieldName.equals(that.fieldName) : that.fieldName != null) return false; return orderByType == that.orderByType; } @Override public int hashCode() { int result = clazz != null ? clazz.hashCode() : 0; result = 31 * result + (fieldName != null ? fieldName.hashCode() : 0); result = 31 * result + (orderByType != null ? orderByType.hashCode() : 0); return result; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/OrderByType.java ================================================ /** * 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.persistence; /** * @author Charles Souillard */ public enum OrderByType { ASC, DESC, ASC_NULLS_LAST, DESC_NULLS_FIRST, ASC_NULLS_FIRST, DESC_NULLS_LAST; private String sqlKeyword; static { ASC.sqlKeyword = "ASC"; DESC.sqlKeyword = "DESC"; ASC_NULLS_LAST.sqlKeyword = "ASC NULLS LAST"; DESC_NULLS_FIRST.sqlKeyword = "DESC NULLS FIRST"; ASC_NULLS_FIRST.sqlKeyword = "ASC NULLS FIRST"; DESC_NULLS_LAST.sqlKeyword = "DESC NULLS LAST"; } public String getSqlKeyword() { return sqlKeyword; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/PersistentObject.java ================================================ /** * Copyright (C) 2023 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.persistence; import java.io.Serializable; /** * Interface for all {@link PersistentObject}. * * @author Emmanuel Duchastenier */ public interface PersistentObject extends Serializable { long getId(); void setId(long id); } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/PostgresInterceptor.java ================================================ /** * 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.persistence; import org.hibernate.EmptyInterceptor; /** * Make search case insensitive in postgres by using `ilike` instead of `like` */ public class PostgresInterceptor extends EmptyInterceptor { private static final long serialVersionUID = -6720122264417020259L; @Override public String onPrepareStatement(final String sql) { return sql //replace like by ilike in sql generated without prepared statements .replace("like '", "ilike '").replace("LIKE '", "ilike '") //replace like by ilike in sql generated with prepared statements .replace("like ?", "ilike ?").replace("LIKE ?", "ilike ?"); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/PostgresMaterializedBlobType.java ================================================ /** * 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.persistence; import org.hibernate.type.AbstractSingleColumnStandardBasicType; import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor; import org.hibernate.type.descriptor.sql.BinaryTypeDescriptor; /** * @author Guillaume Rosinosky */ public class PostgresMaterializedBlobType extends AbstractSingleColumnStandardBasicType { public PostgresMaterializedBlobType() { // mapping binary to byte[] (for bytea type in Postgres) super(BinaryTypeDescriptor.INSTANCE, PrimitiveByteArrayTypeDescriptor.INSTANCE); } @Override public String getName() { return "materialized_blob"; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/PostgresMaterializedClobType.java ================================================ /** * 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.persistence; import org.hibernate.type.AbstractSingleColumnStandardBasicType; import org.hibernate.type.descriptor.java.StringTypeDescriptor; import org.hibernate.type.descriptor.sql.LongVarcharTypeDescriptor; /** * Custom Hibernate type for PostgreSQL TEXT fields. * In Hibernate 5.6+, PostgreSQL CLOB mapping changed from TEXT to OID type, * which causes issues with large text storage. This type forces TEXT columns * by using LongVarcharTypeDescriptor which maps to TEXT in PostgreSQL. * * @author Guillaume Rosinosky */ public class PostgresMaterializedClobType extends AbstractSingleColumnStandardBasicType { public PostgresMaterializedClobType() { // Use LongVarcharTypeDescriptor which maps to TEXT type in PostgreSQL (not OID) super(LongVarcharTypeDescriptor.INSTANCE, StringTypeDescriptor.INSTANCE); } @Override public String getName() { return "materialized_clob"; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/PostgresXMLType.java ================================================ /** * 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.persistence; import java.io.Serializable; import org.hibernate.type.AbstractSingleColumnStandardBasicType; import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor; public class PostgresXMLType extends AbstractSingleColumnStandardBasicType { public static final PostgresXMLType INSTANCE = new PostgresXMLType(); public PostgresXMLType() { // forcing VARCHAR to String as there is no real CLOB in PSQL super(VarcharTypeDescriptor.INSTANCE, new XMLTypeDescriptor()); } public String getName() { return "xml_blob"; } @Override protected boolean registerUnderJavaType() { return true; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/QueryBuilder.java ================================================ /** * 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.persistence; import static java.util.Collections.emptySet; import java.util.*; import lombok.extern.slf4j.Slf4j; import org.hibernate.Session; import org.hibernate.query.Query; /** * @author Baptiste Mesta */ @Slf4j abstract class QueryBuilder { private final Query baseQuery; private final OrderByCheckingMode orderByCheckingMode; private final AbstractSelectDescriptor selectDescriptor; private final QueryGeneratorForFilters queryGeneratorForFilters; private final QueryGeneratorForSearchTerm queryGeneratorForSearchTerm; private final QueryGeneratorForOrderBy queryGeneratorForOrderBy; StringBuilder stringQueryBuilder; private Map classAliasMappings; private Session session; private boolean cacheEnabled; private Map parameters = new HashMap<>(); QueryBuilder(Session session, Query baseQuery, OrderByBuilder orderByBuilder, Map classAliasMappings, char likeEscapeCharacter, OrderByCheckingMode orderByCheckingMode, AbstractSelectDescriptor selectDescriptor) { this.session = session; this.classAliasMappings = classAliasMappings; stringQueryBuilder = new StringBuilder(baseQuery.getQueryString()); this.baseQuery = baseQuery; this.orderByCheckingMode = orderByCheckingMode; this.selectDescriptor = selectDescriptor; this.queryGeneratorForFilters = new QueryGeneratorForFilters(classAliasMappings, likeEscapeCharacter); this.queryGeneratorForSearchTerm = new QueryGeneratorForSearchTerm(likeEscapeCharacter); this.queryGeneratorForOrderBy = new QueryGeneratorForOrderBy(classAliasMappings, orderByBuilder); } public String getQuery() { return stringQueryBuilder.toString(); } void appendFilters(List filters, SearchFields multipleFilter) { Set specificFilters = emptySet(); if (!filters.isEmpty()) { if (!hasWHEREInRootQuery(stringQueryBuilder.toString())) { stringQueryBuilder.append(" WHERE ("); } else { stringQueryBuilder.append(" AND ("); } QueryGeneratorForFilters.QueryGeneratedFilters whereClause = queryGeneratorForFilters.generate(filters); specificFilters = whereClause.getSpecificFilters(); stringQueryBuilder.append(whereClause.getFilters()); stringQueryBuilder.append(")"); parameters.putAll(whereClause.getParameters()); } if (multipleFilter != null && multipleFilter.getTerms() != null && !multipleFilter.getTerms().isEmpty()) { handleMultipleFilters(stringQueryBuilder, multipleFilter, specificFilters); } } static boolean hasWHEREInRootQuery(String query) { // We simply remove all blocks that are in parenthesis in order to remove all subqueries // Then we check if there is the `where` word here return removeAllParenthesisBlocks(query.toLowerCase()).contains("where"); } private static String removeAllParenthesisBlocks(String q) { StringBuilder stringBuilder = new StringBuilder(q.length()); int depthCounter = 0; for (char c : q.toCharArray()) { switch (c) { case '(': depthCounter++; break; case ')': depthCounter--; break; default: if (depthCounter == 0) { stringBuilder.append(c); } } } return stringBuilder.toString(); } private void handleMultipleFilters(final StringBuilder builder, final SearchFields multipleFilter, final Set specificFilters) { final Map, Set> allTextFields = multipleFilter.getFields(); final Set fields = new HashSet<>(); for (final Map.Entry, Set> entry : allTextFields.entrySet()) { final String alias = classAliasMappings.get(entry.getKey().getName()); for (final String field : entry.getValue()) { fields.add(alias + '.' + field); } } fields.removeAll(specificFilters); if (!fields.isEmpty()) { final List terms = multipleFilter.getTerms(); applyFiltersOnQuery(builder, fields, terms); } } private void applyFiltersOnQuery(final StringBuilder queryBuilder, final Set fields, final List terms) { if (!hasWHEREInRootQuery(queryBuilder.toString())) { queryBuilder.append(" WHERE "); } else { queryBuilder.append(" AND "); } queryBuilder.append("("); QueryGeneratorForSearchTerm.QueryGeneratedSearchTerms result = queryGeneratorForSearchTerm.generate(fields, terms); queryBuilder.append(result.getSearch()); queryBuilder.append(")"); parameters.putAll(result.getParameters()); } void appendOrderByClause(List orderByOptions, Class entityType) throws SBonitaReadException { String result = queryGeneratorForOrderBy.generate(orderByOptions, entityType); stringQueryBuilder.append(result); } boolean hasChanged() { return !baseQuery.getQueryString().equals(stringQueryBuilder.toString()); } abstract Query rebuildQuery(AbstractSelectDescriptor selectDescriptor, Session session, Query query); void manageFiltersAndParameters(AbstractSelectDescriptor selectDescriptor) throws SBonitaReadException { if (selectDescriptor.hasAFilter()) { final QueryOptions queryOptions = selectDescriptor.getQueryOptions(); appendFilters(queryOptions.getFilters(), queryOptions.getMultipleFilter()); } if (selectDescriptor.hasOrderByParameters()) { appendOrderByClause(selectDescriptor.getQueryOptions().getOrderByOptions(), selectDescriptor.getEntityType()); } } public QueryBuilder cache(boolean cacheEnabled) { this.cacheEnabled = cacheEnabled; return this; } private void setParameters(final Query query, final Map inputParameters) { for (final Map.Entry entry : inputParameters.entrySet()) { final Object value = entry.getValue(); if (value instanceof Collection) { query.setParameterList(entry.getKey(), (Collection) value); } else { query.setParameter(entry.getKey(), value); } } } public Query build() throws SBonitaReadException { manageFiltersAndParameters(selectDescriptor); Query query = baseQuery; if (hasChanged()) { query = rebuildQuery(selectDescriptor, session, baseQuery); } addConstantsAsParameters(query); setParameters(query, selectDescriptor.getInputParameters()); query.setFirstResult(selectDescriptor.getStartIndex()); query.setMaxResults(selectDescriptor.getPageSize()); query.setCacheable(cacheEnabled); checkOrderByClause(query); return query; } protected abstract void addConstantsAsParameters(Query query); private void checkOrderByClause(final Query query) { if (!query.getQueryString().toLowerCase().contains("order by")) { switch (orderByCheckingMode) { case NONE: break; case WARNING: log.warn( "Query '{}' does not contain 'ORDER BY' clause. It's better to modify your query to order" + " the result, especially if you use the pagination.", query.getQueryString()); break; case STRICT: default: throw new IllegalArgumentException("Query " + query.getQueryString() + " does not contain 'ORDER BY' clause hence is not allowed. Please specify ordering before re-sending the query"); } } } /* * escape for other things than like */ static String escapeString(final String term) { // 1) escape ' character by adding another ' character return term.replaceAll("'", "''"); } /* * escape for like */ static String escapeTerm(final String term, String likeEscapeCharacter) { // 1) protect escape character if this character is used in data // 2) escape % character (sql query wildcard) by adding escape character // 3) escape _ character (sql query wildcard) by adding escape character return term .replace(likeEscapeCharacter, likeEscapeCharacter + likeEscapeCharacter) .replace("%", likeEscapeCharacter + "%") .replace("_", likeEscapeCharacter + "_"); } Map getQueryParameters() { return parameters; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/QueryBuilderFactory.java ================================================ /** * 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.persistence; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.hibernate.Session; import org.hibernate.query.NativeQuery; import org.hibernate.query.Query; /** * @author Baptiste Mesta */ @Slf4j public class QueryBuilderFactory { private OrderByCheckingMode orderByCheckingMode; private OrderByBuilder orderByBuilder = new DefaultOrderByBuilder(); private Map classAliasMappings; private char likeEscapeCharacter; public QueryBuilderFactory(OrderByCheckingMode orderByCheckingMode, Map classAliasMappings, char likeEscapeCharacter) throws Exception { this.orderByCheckingMode = orderByCheckingMode; this.classAliasMappings = classAliasMappings; this.likeEscapeCharacter = likeEscapeCharacter; } public QueryBuilder createQueryBuilderFor(Session session, SelectListDescriptor selectDescriptor) { Query query = session.getNamedQuery(selectDescriptor.getQueryName()); if (query instanceof NativeQuery) { return new SQLQueryBuilder<>(session, query, orderByBuilder, classAliasMappings, likeEscapeCharacter, orderByCheckingMode, selectDescriptor); } else { return new HQLQueryBuilder<>(session, query, orderByBuilder, classAliasMappings, likeEscapeCharacter, orderByCheckingMode, selectDescriptor); } } public void setOrderByBuilder(OrderByBuilder orderByBuilder) { this.orderByBuilder = orderByBuilder; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/QueryGeneratorForFilters.java ================================================ /** * Copyright (C) 2020 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.persistence; import static org.bonitasoft.engine.persistence.QueryBuilder.escapeTerm; import static org.bonitasoft.engine.persistence.search.FilterOperationType.L_PARENTHESIS; import static org.bonitasoft.engine.persistence.search.FilterOperationType.R_PARENTHESIS; import static org.bonitasoft.engine.persistence.search.FilterOperationType.isNormalOperator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import lombok.AllArgsConstructor; import lombok.Data; import org.bonitasoft.engine.persistence.search.FilterOperationType; class QueryGeneratorForFilters { private Map classAliasMappings; private String likeEscapeCharacter; private int parameterCounter = 1; private Map parameters = new HashMap<>(); QueryGeneratorForFilters(Map classAliasMappings, char likeEscapeCharacter) { this.classAliasMappings = classAliasMappings; this.likeEscapeCharacter = String.valueOf(likeEscapeCharacter); } private StringBuilder appendFilterClause(final StringBuilder clause, final FilterOption filterOption) { final FilterOperationType type = filterOption.getFilterOperationType(); StringBuilder completeField = null; if (filterOption.getPersistentClass() != null) { completeField = new StringBuilder(classAliasMappings.get(filterOption.getPersistentClass().getName())) .append('.').append( filterOption.getFieldName()); } Object fieldValue = filterOption.getValue(); switch (type) { case EQUALS: if (fieldValue == null) { clause.append(completeField).append(" IS NULL"); } else { clause.append(completeField).append(" = ").append(createParameter(fieldValue)); } break; case GREATER: clause.append(completeField).append(" > ").append(createParameter(fieldValue)); break; case GREATER_OR_EQUALS: clause.append(completeField).append(" >= ").append(createParameter(fieldValue)); break; case LESS: clause.append(completeField).append(" < ").append(createParameter(fieldValue)); break; case LESS_OR_EQUALS: clause.append(completeField).append(" <= ").append(createParameter(fieldValue)); break; case DIFFERENT: if (fieldValue == null) { clause.append(completeField).append(" IS NOT NULL"); } else { clause.append(completeField).append(" != ").append(createParameter(fieldValue)); } break; case BETWEEN: // eg. ('fromValue' <= p.myField AND p.myField <= 'toValue') clause.append("(").append(createParameter(filterOption.getFrom())).append(" <= ").append(completeField); clause.append(" AND ").append(completeField).append(" <= ") .append(createParameter(filterOption.getTo())).append(")"); break; case LIKE: clause.append(completeField).append(" LIKE ") .append(createParameter( "%" + escapeTerm((String) filterOption.getValue(), likeEscapeCharacter) + "%")) .append(" ESCAPE '").append(likeEscapeCharacter).append("'"); break; case L_PARENTHESIS: clause.append(" ("); break; case R_PARENTHESIS: clause.append(" )"); break; case AND: clause.append(" AND "); break; case OR: clause.append(" OR "); break; default: break; } return completeField; } private String createParameter(Object fieldValue) { final String parameterName = "f" + parameterCounter++; parameters.put(parameterName, fieldValue); return ":" + parameterName; } /** * generate a HQL/SQL condition given the filters * * @return a tuple containing the genereted where clause and the fields it filters on */ public QueryGeneratedFilters generate(List filters) { Set specificFilters = new HashSet<>(); FilterOption previousFilter = null; StringBuilder filtersStringBuilder = new StringBuilder(); for (final FilterOption filterOption : filters) { if (previousFilter != null) { final FilterOperationType prevOp = previousFilter.getFilterOperationType(); final FilterOperationType currOp = filterOption.getFilterOperationType(); // Auto add AND if previous operator was normal op or ')' and that current op is normal op or '(' : if ((isNormalOperator(prevOp) || prevOp == R_PARENTHESIS) && (isNormalOperator(currOp) || currOp == L_PARENTHESIS)) { filtersStringBuilder.append(" AND "); } } final StringBuilder aliasBuilder = appendFilterClause(filtersStringBuilder, filterOption); if (aliasBuilder != null) { specificFilters.add(aliasBuilder.toString()); } previousFilter = filterOption; } return new QueryGeneratedFilters(filtersStringBuilder.toString(), specificFilters, parameters); } @Data @AllArgsConstructor static final class QueryGeneratedFilters { private String filters; private Set specificFilters; private Map parameters; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/QueryGeneratorForOrderBy.java ================================================ /** * Copyright (C) 2020 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.persistence; import java.util.List; import java.util.Map; public class QueryGeneratorForOrderBy { private Map classAliasMappings; private OrderByBuilder orderByBuilder; public QueryGeneratorForOrderBy(Map classAliasMappings, OrderByBuilder orderByBuilder) { this.classAliasMappings = classAliasMappings; this.orderByBuilder = orderByBuilder; } void appendClassAlias(final StringBuilder builder, final Class clazz) throws SBonitaReadException { final String className = clazz.getName(); final String classAlias = classAliasMappings.get(className); if (classAlias == null || classAlias.trim().isEmpty()) { throw new SBonitaReadException("No class alias found for class " + className); } builder.append(classAlias); builder.append('.'); } String generate(List orderByOptions, Class entityType) throws SBonitaReadException { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(" ORDER BY "); boolean startWithComma = false; boolean sortedById = false; for (final OrderByOption orderByOption : orderByOptions) { if (startWithComma) { stringBuilder.append(','); } StringBuilder fieldNameBuilder = new StringBuilder(); final Class clazz = orderByOption.getClazz(); if (clazz != null) { appendClassAlias(fieldNameBuilder, clazz); } final String fieldName = orderByOption.getFieldName(); if ("id".equalsIgnoreCase(fieldName) || "sourceObjectId".equalsIgnoreCase(fieldName)) { sortedById = true; } fieldNameBuilder.append(fieldName); orderByBuilder.appendOrderBy(stringBuilder, fieldNameBuilder.toString(), orderByOption.getOrderByType()); startWithComma = true; } if (!sortedById) { if (startWithComma) { stringBuilder.append(','); } appendClassAlias(stringBuilder, entityType); stringBuilder.append("id"); stringBuilder.append(' '); stringBuilder.append("ASC"); } return stringBuilder.toString(); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/QueryGeneratorForSearchTerm.java ================================================ /** * Copyright (C) 2020 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.persistence; import static org.bonitasoft.engine.persistence.QueryBuilder.escapeTerm; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import lombok.AllArgsConstructor; import lombok.Data; class QueryGeneratorForSearchTerm { private String likeEscapeCharacter; private int parameterCounter = 1; private Map parameters = new HashMap<>(); QueryGeneratorForSearchTerm(char likeEscapeCharacter) { this.likeEscapeCharacter = String.valueOf(likeEscapeCharacter); } private String createParameter(Object fieldValue) { final String parameterName = "s" + parameterCounter++; parameters.put(parameterName, fieldValue); return ":" + parameterName; } /** * Get like clause for given term with escaped sql query wildcards and escape character */ private String buildLikeEscapeClause(String term) { return " LIKE " + createParameter(term) + " ESCAPE '" + likeEscapeCharacter + "'"; } void buildLikeClauseForOneFieldOneTerm(final StringBuilder queryBuilder, final String currentField, final String currentTerm) { // We do not want to search for %currentTerm% to ensure we can use Lucene-like library. queryBuilder.append(currentField) .append(buildLikeEscapeClause("%" + escapeTerm(currentTerm, likeEscapeCharacter) + "%")); } private void buildLikeClauseForOneFieldMultipleTerms(final StringBuilder queryBuilder, final String currentField, final List terms) { final Iterator termIterator = terms.iterator(); while (termIterator.hasNext()) { final String currentTerm = termIterator.next(); buildLikeClauseForOneFieldOneTerm(queryBuilder, currentField, currentTerm); if (termIterator.hasNext()) { queryBuilder.append(" OR "); } } } QueryGeneratedSearchTerms generate(Set fields, List terms) { StringBuilder stringBuilder = new StringBuilder(); final Iterator fieldIterator = fields.iterator(); while (fieldIterator.hasNext()) { buildLikeClauseForOneFieldMultipleTerms(stringBuilder, fieldIterator.next(), terms); if (fieldIterator.hasNext()) { stringBuilder.append(" OR "); } } return new QueryGeneratedSearchTerms(stringBuilder.toString(), parameters); } @Data @AllArgsConstructor static final class QueryGeneratedSearchTerms { private String search; private Map parameters; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/QueryOptions.java ================================================ /** * 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.persistence; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * @author Charles Souillard * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros */ public class QueryOptions implements Serializable { private static final long serialVersionUID = 8923754215920928153L; private final int fromIndex; private final int numberOfResults; private final List filters; private final SearchFields multipleFilter; private final List orderByOptions; public static final int UNLIMITED_NUMBER_OF_RESULTS = Integer.MAX_VALUE; public static final QueryOptions ALL_RESULTS = new QueryOptions(0, UNLIMITED_NUMBER_OF_RESULTS); public static final QueryOptions ONE_RESULT = new QueryOptions(0, 1); public QueryOptions(final QueryOptions queryOptions) { super(); fromIndex = queryOptions.getFromIndex(); numberOfResults = queryOptions.getNumberOfResults(); orderByOptions = queryOptions.getOrderByOptions(); filters = queryOptions.getFilters(); multipleFilter = queryOptions.getMultipleFilter(); } /** * Just for get number of elements on a table, or if the request is already ordered */ public QueryOptions(final int fromIndex, final int numberOfResults) { super(); this.fromIndex = fromIndex; this.numberOfResults = numberOfResults; orderByOptions = Collections.emptyList(); filters = Collections.emptyList(); multipleFilter = null; } public QueryOptions(final int fromIndex, final int numberOfResults, final List orderByOptions) { super(); this.fromIndex = fromIndex; this.numberOfResults = numberOfResults; this.orderByOptions = orderByOptions; filters = Collections.emptyList(); multipleFilter = null; } public QueryOptions(final int fromIndex, final int numberOfResults, final List orderByOptions, final List filters, final SearchFields multipleFilter) { super(); this.fromIndex = fromIndex; this.numberOfResults = numberOfResults; this.orderByOptions = orderByOptions; this.filters = filters; this.multipleFilter = multipleFilter; } /** * Just for get number of elements on a table */ public QueryOptions(final List filters, final SearchFields multipleFilter) { this(0, UNLIMITED_NUMBER_OF_RESULTS, Collections. emptyList(), filters, multipleFilter); } public QueryOptions(final int fromIndex, final int numberOfResults, final Class clazz, final String fieldName, final OrderByType orderByType) { super(); this.fromIndex = fromIndex; this.numberOfResults = numberOfResults; if (fieldName == null || orderByType == null) { orderByOptions = Collections.emptyList(); } else { orderByOptions = new ArrayList(); orderByOptions.add(new OrderByOption(clazz, fieldName, orderByType)); } filters = Collections.emptyList(); multipleFilter = null; } @Deprecated public QueryOptions(final List orderByOptions) { super(); fromIndex = 0; numberOfResults = UNLIMITED_NUMBER_OF_RESULTS; this.orderByOptions = orderByOptions; filters = Collections.emptyList(); multipleFilter = null; } @Deprecated public QueryOptions(final Class clazz, final String fieldName, final OrderByType orderByType) { super(); fromIndex = 0; numberOfResults = UNLIMITED_NUMBER_OF_RESULTS; orderByOptions = new ArrayList(); orderByOptions.add(new OrderByOption(clazz, fieldName, orderByType)); filters = Collections.emptyList(); multipleFilter = null; } public int getFromIndex() { return fromIndex; } public int getNumberOfResults() { return numberOfResults; } public List getFilters() { return filters; } public SearchFields getMultipleFilter() { return multipleFilter; } public List getOrderByOptions() { return orderByOptions; } public boolean hasOrderByOptions() { return orderByOptions != null && !orderByOptions.isEmpty(); } /** * Just for get number of elements on a table */ public static QueryOptions countQueryOptions() { return ALL_RESULTS; } public static QueryOptions getNextPage(final QueryOptions queryOptions) { return new QueryOptions(queryOptions.getFromIndex() + queryOptions.getNumberOfResults(), queryOptions.getNumberOfResults(), queryOptions.getOrderByOptions(), queryOptions.getFilters(), queryOptions.getMultipleFilter()); } @Override public String toString() { return "QueryOptions [fromIndex=" + fromIndex + ", numberOfResults=" + numberOfResults + ", orderByOptions=" + orderByOptions + "]"; } public boolean hasAFilter() { return filters != null && !filters.isEmpty() || multipleFilter != null; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if (!(o instanceof QueryOptions that)) { return false; } if (fromIndex != that.fromIndex) { return false; } if (numberOfResults != that.numberOfResults) { return false; } if (filters != null ? !filters.equals(that.filters) : that.filters != null) { return false; } if (multipleFilter != null ? !multipleFilter.equals(that.multipleFilter) : that.multipleFilter != null) { return false; } if (orderByOptions != null ? !orderByOptions.equals(that.orderByOptions) : that.orderByOptions != null) { return false; } return true; } @Override public int hashCode() { int result = fromIndex; result = 31 * result + numberOfResults; result = 31 * result + (filters != null ? filters.hashCode() : 0); result = 31 * result + (multipleFilter != null ? multipleFilter.hashCode() : 0); result = 31 * result + (orderByOptions != null ? orderByOptions.hashCode() : 0); return result; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/ReadOnlySelectByIdDescriptor.java ================================================ /** * Copyright (C) 2017 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.persistence; /** * @author Emmanuel Duchastenier */ public class ReadOnlySelectByIdDescriptor extends SelectByIdDescriptor { public ReadOnlySelectByIdDescriptor(Class entityType, long id) { super(entityType, id, true); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/ReadPersistenceService.java ================================================ /** * 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.persistence; import java.util.List; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SRetryableException; /** * @author Charles Souillard * @author Matthieu Chaffotte */ public interface ReadPersistenceService { /** * @param selectDescriptor * @return * @throws SBonitaReadException * @throws SRetryableException */ T selectById(SelectByIdDescriptor selectDescriptor) throws SBonitaReadException; /** * @param selectDescriptor * @return * @throws SBonitaReadException * @throws SRetryableException */ T selectOne(SelectOneDescriptor selectDescriptor) throws SBonitaReadException; /** * @param selectDescriptor * @return * @throws SBonitaReadException * @throws SRetryableException */ List selectList(SelectListDescriptor selectDescriptor) throws SBonitaReadException; /** * @param entityClass * @param options * @param parameters * @return * @throws SBonitaReadException * @throws SRetryableException */ long getNumberOfEntities(Class entityClass, QueryOptions options, Map parameters) throws SBonitaReadException; /** * @param entityClass * @param querySuffix * @param options * @param parameters * @return * @throws SBonitaReadException * @throws SRetryableException */ long getNumberOfEntities(Class entityClass, String querySuffix, QueryOptions options, Map parameters) throws SBonitaReadException; /** * @param entityClass * @param options * @param parameters * @return * @throws SBonitaReadException * @throws SRetryableException */ List searchEntity(Class entityClass, QueryOptions options, Map parameters) throws SBonitaReadException; /** * @param entityClass * class of the object we want to search on * @param querySuffix * Used to define customized search query * @param options * query options * @param parameters * @return * @throws SBonitaReadException * @throws SRetryableException */ List searchEntity(Class entityClass, String querySuffix, QueryOptions options, Map parameters) throws SBonitaReadException; } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/SBonitaReadException.java ================================================ /** * 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.persistence; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Charles Souillard */ public class SBonitaReadException extends SBonitaException { private static final long serialVersionUID = 1L; private final AbstractSelectDescriptor selectDescriptor; public SBonitaReadException(final Throwable cause, final AbstractSelectDescriptor selectDescriptor) { super(cause); this.selectDescriptor = selectDescriptor; } public SBonitaReadException(final String message, final Throwable cause, final AbstractSelectDescriptor selectDescriptor) { super(message, cause); this.selectDescriptor = selectDescriptor; } public SBonitaReadException(final String message) { super(message); selectDescriptor = null; } public SBonitaReadException(final Throwable cause) { super(cause); selectDescriptor = null; } public SBonitaReadException(final String message, final Throwable cause) { super(message, cause); selectDescriptor = null; } public AbstractSelectDescriptor getSelectDescriptor() { return selectDescriptor; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/SQLQueryBuilder.java ================================================ /** * 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.persistence; import java.util.HashMap; import java.util.List; import java.util.Map; import org.hibernate.Session; import org.hibernate.engine.query.spi.sql.NativeSQLQueryReturn; import org.hibernate.engine.query.spi.sql.NativeSQLQueryRootReturn; import org.hibernate.engine.query.spi.sql.NativeSQLQueryScalarReturn; import org.hibernate.query.NativeQuery; import org.hibernate.query.Query; /** * @author Baptiste Mesta */ public class SQLQueryBuilder extends QueryBuilder { public static final String TRUE_VALUE_PARAMETER = "trueValue"; private static Map hqlToSqlAlias = new HashMap<>(); static { hqlToSqlAlias.put("user", "user_"); } SQLQueryBuilder(Session session, Query baseQuery, OrderByBuilder orderByBuilder, Map classAliasMappings, char likeEscapeCharacter, OrderByCheckingMode orderByCheckingMode, SelectListDescriptor selectDescriptor) { super(session, baseQuery, orderByBuilder, classAliasMappings, likeEscapeCharacter, orderByCheckingMode, selectDescriptor); } public void addConstantsAsParameters(Query sqlQuery) { if (sqlQuery.getQueryString().contains(":" + TRUE_VALUE_PARAMETER)) { // there is no need to convert the true value to a integer for Oracle and Sqlserver, hibernate does that already. sqlQuery.setParameter(TRUE_VALUE_PARAMETER, true); } } private String replaceHQLAliasesBySQLAliases(String builtQuery) { for (Map.Entry aliasToReplace : hqlToSqlAlias.entrySet()) { if (builtQuery.contains(aliasToReplace.getKey() + ".")) { builtQuery = builtQuery.replace(aliasToReplace.getKey() + ".", aliasToReplace.getValue() + "."); } } return builtQuery; } @Override Query rebuildQuery(AbstractSelectDescriptor selectDescriptor, Session session, Query query) { String builtQuery = stringQueryBuilder.toString(); builtQuery = replaceHQLAliasesBySQLAliases(builtQuery); NativeQuery generatedSqlQuery = session.createSQLQuery(builtQuery); for (Map.Entry parameter : getQueryParameters().entrySet()) { generatedSqlQuery.setParameter(parameter.getKey(), parameter.getValue()); } for (NativeSQLQueryReturn queryReturn : (List) ((NativeQuery) query) .getQueryReturns()) { if (queryReturn instanceof NativeSQLQueryScalarReturn) { generatedSqlQuery.addScalar(((NativeSQLQueryScalarReturn) queryReturn).getColumnAlias(), ((NativeSQLQueryScalarReturn) queryReturn).getType()); } else if (queryReturn instanceof NativeSQLQueryRootReturn) { generatedSqlQuery.addEntity(((NativeSQLQueryRootReturn) queryReturn).getAlias(), ((NativeSQLQueryRootReturn) queryReturn).getReturnEntityName()); } else { throw new IllegalStateException( "Not yet implemented. Query return type " + queryReturn.getClass().getName()); } } return generatedSqlQuery; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/SQLTransformer.java ================================================ /** * 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.persistence; /** * @author Baptiste Mesta */ public abstract class SQLTransformer { public SQLTransformer() { } /** * @return */ public String getDeleteScript() { return null; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/SearchFields.java ================================================ /** * 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.persistence; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.Set; /** * @author Matthieu Chaffotte */ public class SearchFields implements Serializable { private static final long serialVersionUID = -9157314460334148998L; private final List terms; private final Map, Set> fields; public SearchFields(final List terms, final Map, Set> fields) { super(); this.terms = terms; this.fields = fields; } public List getTerms() { return terms; } public Map, Set> getFields() { return fields; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/SelectByIdDescriptor.java ================================================ /** * 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.persistence; import static java.util.Collections.emptyMap; import static org.bonitasoft.engine.persistence.QueryOptions.ONE_RESULT; import java.util.Map; /** * @author Charles Souillard * @author Matthieu Chaffotte * @author Emmanuel Duchastenier */ public class SelectByIdDescriptor extends AbstractSelectDescriptor { private final long id; private boolean readonly; public SelectByIdDescriptor(final Class entityType, final long id) { this(entityType, id, false); } public SelectByIdDescriptor(final Class entityType, final long id, final boolean readonly) { super(null, entityType, entityType); this.id = id; this.readonly = readonly; } public long getId() { return id; } boolean isReadOnly() { return readonly; } @Override public Map getInputParameters() { return emptyMap(); } @Override public int getStartIndex() { return 0; } @Override public int getPageSize() { return 1; } @Override public boolean hasAFilter() { return false; } @Override public QueryOptions getQueryOptions() { return ONE_RESULT; } @Override public boolean hasOrderByParameters() { return false; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/SelectListDescriptor.java ================================================ /** * 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.persistence; import java.util.Map; /** * @author Charles Souillard * @author Matthieu Chaffotte */ public class SelectListDescriptor extends AbstractSelectWithParametersDescriptor { private final QueryOptions queryOptions; public SelectListDescriptor(final String queryName, final Map inputParameters, final Class entityType, final QueryOptions queryOptions) { super(queryName, inputParameters, entityType, (Class) entityType); if (queryOptions != null) { this.queryOptions = queryOptions; } else { throw new IllegalArgumentException("Need to have a query option to paginate and order the results."); } } public SelectListDescriptor(final String queryName, final Map inputParameters, final Class entityType, final Class returnType, final QueryOptions queryOptions) { super(queryName, inputParameters, entityType, returnType); if (queryOptions != null) { this.queryOptions = queryOptions; } else { throw new IllegalArgumentException("Need to have a query option to paginate and order the results."); } } public QueryOptions getQueryOptions() { return queryOptions; } public boolean hasOrderByParameters() { return queryOptions.hasOrderByOptions(); } public int getStartIndex() { return queryOptions.getFromIndex(); } public int getPageSize() { return queryOptions.getNumberOfResults(); } public boolean hasAFilter() { return queryOptions.hasAFilter(); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/SelectOneDescriptor.java ================================================ /** * 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.persistence; import java.util.Map; /** * @author Charles Souillard */ public final class SelectOneDescriptor extends AbstractSelectWithParametersDescriptor { public SelectOneDescriptor(final String queryName, final Map inputParameters, final Class entityType) { super(queryName, inputParameters, entityType, (Class) entityType); } public SelectOneDescriptor(final String queryName, final Map inputParameters, final Class entityType, final Class returnType) { super(queryName, inputParameters, entityType, returnType); } @Override public int getStartIndex() { return 0; } @Override public int getPageSize() { return 1; } @Override public boolean hasAFilter() { return false; } @Override public QueryOptions getQueryOptions() { return QueryOptions.ONE_RESULT; } @Override public boolean hasOrderByParameters() { return false; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/XMLType.java ================================================ /** * 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.persistence; import java.io.Serializable; import org.hibernate.type.AbstractSingleColumnStandardBasicType; import org.hibernate.type.descriptor.sql.ClobTypeDescriptor; public class XMLType extends AbstractSingleColumnStandardBasicType { public static final XMLType INSTANCE = new XMLType(); public XMLType() { super(ClobTypeDescriptor.DEFAULT, new XMLTypeDescriptor()); } public String getName() { return "xml_blob"; } @Override protected boolean registerUnderJavaType() { return true; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/XMLTypeDescriptor.java ================================================ /** * 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.persistence; import java.io.Reader; import java.io.Serializable; import java.io.StringReader; import java.sql.Clob; import java.sql.SQLException; import org.bonitasoft.engine.data.instance.model.impl.XStreamFactory; import org.hibernate.HibernateException; import org.hibernate.engine.jdbc.CharacterStream; import org.hibernate.engine.jdbc.internal.CharacterStreamImpl; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.AbstractTypeDescriptor; import org.hibernate.type.descriptor.java.DataHelper; public class XMLTypeDescriptor extends AbstractTypeDescriptor { public XMLTypeDescriptor() { super(Serializable.class); } @Override public boolean areEqual(Serializable one, Serializable another) { if (one == another) { return true; } if (one == null || another == null) { return false; } return one.equals(another); } @Override public String toString(Serializable value) { return XStreamFactory.getXStream().toXML(value); } @Override public Serializable fromString(String string) { return ((Serializable) XStreamFactory.getXStream().fromXML((string))); } @SuppressWarnings({ "unchecked" }) @Override public X unwrap(Serializable value, Class type, WrapperOptions options) { if (value == null) { return null; } if (String.class.isAssignableFrom(type)) { return (X) toString(value); } if (Reader.class.isAssignableFrom(type)) { return (X) new StringReader(toString(value)); } if (CharacterStream.class.isAssignableFrom(type)) { return (X) new CharacterStreamImpl(toString(value)); } if (Clob.class.isAssignableFrom(type)) { return (X) options.getLobCreator().createClob(toString(value)); } if (DataHelper.isNClob(type)) { return (X) options.getLobCreator().createNClob(toString(value)); } throw unknownUnwrap(type); } @Override public Serializable wrap(X value, WrapperOptions options) { if (value == null) { return null; } if (String.class.isAssignableFrom(value.getClass())) { return fromString((String) value); } if (Reader.class.isInstance(value)) { return fromReader((Reader) value); } if (Clob.class.isAssignableFrom(value.getClass())) { return fromReader(extractReader((Clob) value)); } throw unknownWrap(value.getClass()); } private Reader extractReader(Clob value) { Reader reader; try { reader = value.getCharacterStream(); } catch (SQLException e) { throw new HibernateException("Unable to get the Clob value", e); } return reader; } private Serializable fromReader(Reader characterStream) { return (Serializable) XStreamFactory.getXStream().fromXML(characterStream); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/persistence/search/FilterOperationType.java ================================================ /** * 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.persistence.search; /** * @author Emmanuel Duchastenier */ public enum FilterOperationType { BETWEEN, EQUALS, LIKE, GREATER, LESS, GREATER_OR_EQUALS, LESS_OR_EQUALS, DIFFERENT, L_PARENTHESIS, R_PARENTHESIS, AND, OR; public static boolean isNormalOperator(final FilterOperationType type) { return type == BETWEEN || type == EQUALS || type == LIKE || type == GREATER || type == LESS || type == GREATER_OR_EQUALS || type == LESS_OR_EQUALS || type == DIFFERENT; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/Recorder.java ================================================ /** * 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.recorder; import org.bonitasoft.engine.recorder.model.DeleteAllRecord; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; public interface Recorder { /** * Add a record to database * * @param record * the record for insert * @param type * @throws SRecorderException * @since 6.0 */ void recordInsert(InsertRecord record, String type) throws SRecorderException; /** * Delete a record from database * * @param record * the record for insert * @param type * @throws SRecorderException * @since 6.0 */ void recordDelete(DeleteRecord record, String type) throws SRecorderException; /** * Update a record from database * * @param record * the record for insert * @param type * @throws SRecorderException * @since 6.0 */ void recordUpdate(UpdateRecord record, String type) throws SRecorderException; /** * Update a record from database with a named query * If no rows have been updated the event is not thrown * * @param record * the record for insert * @param type * Object type * @param query * NamedQuery to be used * @return number of updated rows * @throws SRecorderException * @since 7.6 */ int recordUpdateWithQuery(final UpdateRecord record, String type, String query) throws SRecorderException; /** * Delete all records for a table from database, for the connected tenant * * @param record * table to clean * @throws SRecorderException * @since 6.1 */ void recordDeleteAll(DeleteAllRecord record) throws SRecorderException; } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/SRecorderException.java ================================================ /** * 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.recorder; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SRecorderException extends SBonitaException { private static final long serialVersionUID = 1L; public SRecorderException(final Throwable cause) { super(cause); } public SRecorderException(final String message) { super(message); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/impl/RecorderImpl.java ================================================ /** * 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.recorder.impl; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.ExceptionUtils; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.events.model.SDeleteEvent; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.events.model.SFireEventException; import org.bonitasoft.engine.events.model.SInsertEvent; import org.bonitasoft.engine.events.model.SUpdateEvent; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteAllRecord; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.UpdateDescriptor; /** * @author Charles Souillard * @author Baptiste Mesta * @author Celine Souchet */ @Slf4j public class RecorderImpl implements Recorder { private final PersistenceService persistenceService; private final EventService eventService; public RecorderImpl(final PersistenceService persistenceService, final EventService eventService) { this.persistenceService = persistenceService; this.eventService = eventService; } @Override public void recordInsert(final InsertRecord insertRecord, String type) throws SRecorderException { try { var entity = persistenceService.insert(insertRecord.getEntity()); eventService.fireEvent(createInsertEvent(entity, type)); } catch (final Exception e) { logExceptionsFromHandlers(e); throw new SRecorderException(e); } } private SInsertEvent createInsertEvent(PersistentObject entity, String type) { SInsertEvent sInsertEvent = new SInsertEvent(type + SEvent.CREATED); sInsertEvent.setObject(entity); return sInsertEvent; } private SDeleteEvent createDeleteEvent(PersistentObject entity, String type) { SDeleteEvent sDeleteEvent = new SDeleteEvent(type + SEvent.DELETED); sDeleteEvent.setObject(entity); return sDeleteEvent; } private SUpdateEvent createUpdateEvent(PersistentObject entity, Map updatedFields, String type) { SUpdateEvent sUpdateEvent = new SUpdateEvent(type + SEvent.UPDATED); sUpdateEvent.setObject(entity); sUpdateEvent.setUpdatedFields(updatedFields); return sUpdateEvent; } @Override public void recordDelete(final DeleteRecord deleteRecord, String type) throws SRecorderException { try { persistenceService.delete(deleteRecord.getEntity()); eventService.fireEvent(createDeleteEvent(deleteRecord.getEntity(), type)); } catch (final Exception e) { logExceptionsFromHandlers(e); throw new SRecorderException(e); } } @Override public void recordDeleteAll(final DeleteAllRecord deleteAllRecord) throws SRecorderException { try { persistenceService.deleteAll(deleteAllRecord.entityClass(), deleteAllRecord.filters()); } catch (final Exception e) { logExceptionsFromHandlers(e); throw new SRecorderException(e); } } @Override public void recordUpdate(final UpdateRecord updateRecord, String type) throws SRecorderException { final UpdateDescriptor desc = UpdateDescriptor.buildSetFields(updateRecord.getEntity(), updateRecord.getFields()); try { persistenceService.update(desc); eventService.fireEvent(createUpdateEvent(updateRecord.getEntity(), updateRecord.getFields(), type)); } catch (final Exception e) { logExceptionsFromHandlers(e); throw new SRecorderException(e); } } @Override public int recordUpdateWithQuery(final UpdateRecord updateRecord, String type, String query) throws SRecorderException { try { int updateCount = persistenceService.update(query, updateRecord.getFields()); if (updateCount > 0) eventService.fireEvent(createUpdateEvent(updateRecord.getEntity(), updateRecord.getFields(), type)); return updateCount; } catch (final Exception e) { logExceptionsFromHandlers(e); throw new SRecorderException(e); } } private void logExceptionsFromHandlers(final Exception e) { if (!(e instanceof SFireEventException)) { return; } if (log.isDebugEnabled()) { final List handlerExceptions = ((SFireEventException) e).getHandlerExceptions(); for (Exception exception : handlerExceptions) { log.debug("error while executing handler e {}", ExceptionUtils.printLightWeightStacktrace(exception)); } } } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/model/DeleteAllRecord.java ================================================ /** * 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.recorder.model; import static java.util.Collections.unmodifiableList; import static java.util.Objects.requireNonNullElseGet; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Celine Souchet */ public record DeleteAllRecord(Class entityClass, List filters) { @Override public List filters() { return unmodifiableList(requireNonNullElseGet(filters, ArrayList::new)); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/model/DeleteRecord.java ================================================ /** * 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.recorder.model; import org.bonitasoft.engine.persistence.PersistentObject; public class DeleteRecord extends Record { public DeleteRecord(final PersistentObject entity) { super(entity); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/model/EntityUpdateDescriptor.java ================================================ /** * 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.recorder.model; import java.util.HashMap; import java.util.Map; import lombok.EqualsAndHashCode; /** * @author Baptiste Mesta */ @EqualsAndHashCode public class EntityUpdateDescriptor { Map fields = new HashMap<>(); public Map getFields() { return fields; } public EntityUpdateDescriptor addField(final String name, final Object value) { fields.put(name, value); return this; } @Override public String toString() { return "EntityUpdateDescriptor [fields=" + fields + "]"; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/model/InsertRecord.java ================================================ /** * 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.recorder.model; import org.bonitasoft.engine.persistence.PersistentObject; public class InsertRecord extends Record { public InsertRecord(final PersistentObject entity) { super(entity); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/model/Record.java ================================================ /** * 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.recorder.model; import org.bonitasoft.engine.commons.NullCheckingUtil; import org.bonitasoft.engine.persistence.PersistentObject; public abstract class Record { private final PersistentObject entity; // ServerProcessInstance, CaseInstance public Record(final PersistentObject entity) { NullCheckingUtil.checkArgsNotNull(entity); this.entity = entity; } public PersistentObject getEntity() { return entity; } @Override public String toString() { return "Record [entity=" + entity + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((entity == null) ? 0 : entity.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Record other = (Record) obj; if (entity == null) { if (other.entity != null) { return false; } } else if (!entity.equals(other.entity)) { return false; } return true; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/recorder/model/UpdateRecord.java ================================================ /** * 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.recorder.model; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.commons.NullCheckingUtil; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Charles Souillard * @author Matthieu Chaffotte * @author Celine Souchet * @author Baptiste Mesta */ public final class UpdateRecord extends Record { private Map fields; private UpdateRecord(final PersistentObject entity) { super(entity); } public static UpdateRecord buildSetFields(final PersistentObject entity, final Map fields) { final UpdateRecord updateRecord = new UpdateRecord(entity); updateRecord.addFields(fields); return updateRecord; } public static UpdateRecord buildSetFields(final PersistentObject entity, final EntityUpdateDescriptor descriptor) { NullCheckingUtil.checkArgsNotNull(descriptor); final UpdateRecord updateRecord = new UpdateRecord(entity); updateRecord.addFields(descriptor.getFields()); return updateRecord; } public void addField(final String fieldName, final Object fieldValue) { if (fields == null) { fields = new HashMap(); } fields.put(fieldName, fieldValue); } public void addFields(final Map fields) { if (this.fields == null) { this.fields = new HashMap(); } this.fields.putAll(fields); } public Map getFields() { return fields; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/sequence/SequenceDAO.java ================================================ /** * 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.sequence; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; public class SequenceDAO { static final String NEXT_ID = "nextid"; static final String SELECT_BY_ID = "SELECT * FROM sequence WHERE id = ?"; static final String UPDATE_SEQUENCE = "UPDATE sequence SET nextId = ? WHERE id = ?"; private final Connection connection; public SequenceDAO(Connection connection) { this.connection = connection; } protected void updateSequence(long nextSequenceId, long id) throws SQLException { try (PreparedStatement updateSequencePreparedStatement = connection.prepareStatement(UPDATE_SEQUENCE)) { updateSequencePreparedStatement.setObject(1, nextSequenceId); updateSequencePreparedStatement.setObject(2, id); updateSequencePreparedStatement.executeUpdate(); } } protected long selectById(long id) throws SQLException, SObjectNotFoundException { PreparedStatement selectByIdPreparedStatement = null; ResultSet resultSet = null; try { selectByIdPreparedStatement = connection.prepareStatement(SELECT_BY_ID); selectByIdPreparedStatement.setLong(1, id); resultSet = selectByIdPreparedStatement.executeQuery(); return getNextId(id, resultSet); } finally { if (selectByIdPreparedStatement != null) { selectByIdPreparedStatement.close(); } if (resultSet != null) { resultSet.close(); } } } private long getNextId(final long id, final ResultSet resultSet) throws SQLException, SObjectNotFoundException { try { if (resultSet.next()) { final long nextId = resultSet.getLong(NEXT_ID); if (resultSet.wasNull()) { throw new SQLException("Did not expect a null value for the column " + NEXT_ID); } if (resultSet.next()) { throw new SQLException( "Did not expect more than one value for id: " + id); } return nextId; } } finally { closeResultSet(resultSet); } throw new SObjectNotFoundException("Found no row for id: " + id); } private void closeResultSet(final ResultSet resultSet) { try { if (resultSet != null) { resultSet.close(); } } catch (final SQLException e) { // can't do anything } } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/sequence/SequenceManager.java ================================================ /** * 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.sequence; import org.bonitasoft.engine.commons.exceptions.SObjectModificationException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; /** * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface SequenceManager { long getNextId(String entityName) throws SObjectNotFoundException, SObjectModificationException; } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/sequence/SequenceManagerImpl.java ================================================ /** * 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.sequence; import java.sql.Connection; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import java.util.Optional; import javax.sql.DataSource; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.lock.BonitaLock; import org.bonitasoft.engine.lock.LockService; import org.bonitasoft.engine.lock.SLockException; import org.bonitasoft.engine.lock.SLockTimeoutException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * @author Charles Souillard * @author Matthieu Chaffotte * @author Baptiste Mesta * @author Emmanuel Duchastenier */ @Component public class SequenceManagerImpl implements SequenceManager { private static final Logger logger = LoggerFactory.getLogger(SequenceManagerImpl.class); static final String SEQUENCE = "SEQUENCE"; private final Map sequences = new HashMap<>(); private final Map sequenceIdToRangeSize; private final SequenceMappingProvider sequenceMappingProvider; private final Map classNameToSequenceId; private final int retries; private final int delay; private final int delayFactor; private final DataSource datasource; private final LockService lockService; public SequenceManagerImpl(final LockService lockService, final SequenceMappingProvider sequenceMappingProvider, @Qualifier("bonitaNonXaDataSource") final DataSource datasource, @Value("${bonita.platform.sequence.retries}") final int retries, @Value("${bonita.platform.sequence.delay}") final int delay, @Value("${bonita.platform.sequence.delayFactor}") final int delayFactor) { this.lockService = lockService; this.sequenceMappingProvider = sequenceMappingProvider; this.sequenceIdToRangeSize = getSequenceIdToRangeSizeMap(); this.classNameToSequenceId = getClassNameToSequenceIdMap(); this.retries = retries; this.delay = delay; this.delayFactor = delayFactor; this.datasource = datasource; } private Map getClassNameToSequenceIdMap() { final Map result = new HashMap<>(); for (SequenceMapping sequenceMapping : sequenceMappingProvider.getSequenceMappings()) { for (String className : sequenceMapping.getClassNames()) { result.put(className, sequenceMapping.getSequenceId()); } } return result; } private Map getSequenceIdToRangeSizeMap() { final Map result = new HashMap<>(); for (SequenceMapping sequenceMapping : sequenceMappingProvider.getSequenceMappings()) { final long sequenceId = sequenceMapping.getSequenceId(); if (result.containsKey(sequenceId)) { throw new RuntimeException("SequenceMapping for id <" + sequenceId + "> is duplicated. Please make sure there is only one configuration for this sequence."); } result.put(sequenceId, sequenceMapping.getRangeSize()); } return result; } @Override public long getNextId(final String entityName) throws SObjectNotFoundException { final long sequenceId = getSequenceId(entityName); SequenceRange sequence = getSequence(sequenceId); Optional nextAvailableId = sequence.getNextAvailableId(); if (nextAvailableId.isPresent()) { return nextAvailableId.get(); } //synchronize on the sequence object itself (we will read/update only on this one) synchronized (sequence) { nextAvailableId = sequence.getNextAvailableId(); int loopCounter = 0; // set a max number of retries to 100: while (nextAvailableId.isEmpty() && loopCounter < 100) { if (loopCounter > 0) { logger.debug("Could not get an Id after updating to next range. Retrying..."); } sequence.updateToNextRange(setNewRange(sequenceId)); nextAvailableId = sequence.getNextAvailableId(); loopCounter++; } return nextAvailableId.orElseThrow( () -> new IllegalStateException("No new available id found for sequence " + entityName)); } } private SequenceRange getSequence(Long sequenceId) { if (!sequences.containsKey(sequenceId)) { synchronized (this) { if (!sequences.containsKey(sequenceId)) { sequences.put(sequenceId, new SequenceRange(sequenceIdToRangeSize.get(sequenceId))); } } } return sequences.get(sequenceId); } private Long getSequenceId(String entityName) throws SObjectNotFoundException { final Long sequenceId = classNameToSequenceId.get(entityName); if (sequenceId == null) { throw new SObjectNotFoundException("No sequence id found for " + entityName); } return sequenceId; } /** * get the next available id of a sequence and update in database its value * * @return the next available id of the sequence */ private long setNewRange(final long sequenceId) throws SObjectNotFoundException { BonitaLock lock = createLock(sequenceId); Exception lastException = null; try { int attempt = 1; long sleepTime = delay; while (attempt <= retries) { if (attempt > 1) { logger.info("Retry #{} to retrieve next sequence id of sequence {}", attempt, sequenceId); } Connection connection = getConnection(); try { connection.setAutoCommit(false); SequenceDAO sequenceDAO = createDao(connection); long nextAvailableId = sequenceDAO.selectById(sequenceId); sequenceDAO.updateSequence(nextAvailableId + sequenceIdToRangeSize.get(sequenceId), sequenceId); connection.commit(); return nextAvailableId; } catch (final SObjectNotFoundException t) { rollback(connection); throw t; } catch (final Exception t) { attempt++; rollback(connection); lastException = t; manageException(attempt, sleepTime, t); sleepTime *= delayFactor; } finally { close(connection); } } } finally { unlock(lock); } throw new SObjectNotFoundException( "Unable to get a sequence id for " + sequenceId, lastException); } private void unlock(BonitaLock lock) { try { lockService.unlock(lock); } catch (SLockException e) { throw new SBonitaRuntimeException( "Unable to unlock the lock require to get next id of sequences from database", e); } } private BonitaLock createLock(long sequenceId) { BonitaLock lock; try { lock = lockService.lock(sequenceId, SEQUENCE); } catch (SLockException | SLockTimeoutException e) { throw new SBonitaRuntimeException( "Unable to acquire lock in order to update get the next id from database of the sequence " + sequenceId, e); } return lock; } private void close(Connection connection) { try { connection.close(); } catch (final SQLException e) { throw new SBonitaRuntimeException( "Next id of sequence correctly updated, but unable to close the connection", e); } } private Connection getConnection() { try { return datasource.getConnection(); } catch (SQLException e) { throw new SBonitaRuntimeException("Unable to acquire connection to retrieve next id of the sequence", e); } } private void rollback(Connection connection) { try { connection.rollback(); } catch (final SQLException e) { throw new SBonitaRuntimeException( "Unable to rollback the transaction that get/update next sequence id from database", e); } } SequenceDAO createDao(Connection connection) { return new SequenceDAO(connection); } private static void manageException(int attempt, final long sleepTime, final Exception t) { logger.error("Unable to retrieve and update sequence in database because: {}." + "( attempt #{} ). Will sleep {} millis before retrying. ", t.getMessage(), attempt, sleepTime); logger.debug("Cause:", t); try { Thread.sleep(sleepTime); } catch (final InterruptedException ignored) { logger.error("Interrupted while sleeping before retry"); } } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/sequence/SequenceMapping.java ================================================ /** * 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.sequence; import java.util.Collections; import java.util.Set; /** * @author Charles Souillard */ public class SequenceMapping { private final Set classNames; private final long sequenceId; private final int rangeSize; public SequenceMapping(String className, long sequenceId, final int rangeSize) { this(Collections.singleton(className), sequenceId, rangeSize); } public SequenceMapping(Set classNames, long sequenceId, final int rangeSize) { this.classNames = classNames; this.sequenceId = sequenceId; this.rangeSize = rangeSize; } public long getSequenceId() { return sequenceId; } public Set getClassNames() { return classNames; } public int getRangeSize() { return rangeSize; } @Override public String toString() { return "SequenceMapping{" + "sequenceId=" + sequenceId + ", classNames='" + classNames + '\'' + ", rangeSize=" + rangeSize + '}'; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/sequence/SequenceMappingProvider.java ================================================ /** * 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.sequence; import java.util.List; import lombok.Getter; import lombok.Setter; /** * @author Charles Souillard */ @Setter @Getter public class SequenceMappingProvider { private List sequenceMappings; public SequenceMappingProvider() { } @Override public String toString() { return "SequenceMappingProvider{sequenceMappings=" + sequenceMappings + '}'; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/sequence/SequenceRange.java ================================================ /** * 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.sequence; import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; public class SequenceRange { private AtomicLong nextAvailableId; private long lastIdInRange; private final int rangeSize; public SequenceRange(int rangeSize) { this.rangeSize = rangeSize; } public Optional getNextAvailableId() { if (nextAvailableId == null) { // Range is not initialized yet: return Optional.empty(); } long nextId = nextAvailableId.getAndUpdate(current -> { if (current == -1 || current >= lastIdInRange) { return -1; // -1 means no more Id available } else { return current + 1; } }); if (nextId < 0) { return Optional.empty(); } return Optional.of(nextId); } public void updateToNextRange(long nextAvailableIdFromDatabase) { nextAvailableId = new AtomicLong(nextAvailableIdFromDatabase); lastIdInRange = nextAvailableIdFromDatabase + rangeSize - 1; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/sequence/exceptions/SequenceManagerException.java ================================================ /** * 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.sequence.exceptions; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SequenceManagerException extends SBonitaException { private static final long serialVersionUID = 8661525835047162806L; /** * */ public SequenceManagerException() { super(); } /** * @param arguments */ public SequenceManagerException(final Object... arguments) { super(arguments); } /** * @param message * @param cause */ public SequenceManagerException(final String message, final Throwable cause) { super(message, cause); } /** * @param message */ public SequenceManagerException(final String message) { super(message); } /** * @param cause * @param arguments */ public SequenceManagerException(final Throwable cause, final Object... arguments) { super(cause, arguments); } /** * @param cause */ public SequenceManagerException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/services/PersistenceService.java ================================================ /** * 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.services; import java.util.List; import java.util.Map; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.ReadPersistenceService; /** * @author Charles Souillard * @author Elias Ricken de Medeiros * @author Celine Souchet * @author Matthieu Chaffotte */ public interface PersistenceService extends ReadPersistenceService { // on save, the service MUST generate a unique ID and set it in id attribute if this attribute is equals to -1 // else keep the already set id /** * Add a record into the table by given PersistentObject. * * @since 6.0 */ T insert(final T entity) throws SPersistenceException; /** * */ List insertInBatch(final List entities) throws SPersistenceException; /** * Delete a record from the table by given PersistentObject. * * @since 6.0 */ void delete(final T entity) throws SPersistenceException; /** * Delete all records belong to the given entity class from the table. * * @param entityClass The class which extends PersistentObject * @since 6.0 */ void deleteAll(final Class entityClass) throws SPersistenceException; /** * Executes a query update. * * @param updateQueryName the name of the declared query that represent the update. * @return the number of updated rows, as returned by the underlining persistence implementation. * @throws SPersistenceException if a persistence problem occurs when executing the update query. */ int update(final String updateQueryName) throws SPersistenceException; /** * Executes a query update. */ int update(String updateQueryName, Map inputParameters) throws SPersistenceException; /** * Delete all elements of a specific table * * @param entityClass Entity class corresponding to the table to empty * @param filters filter options to restrict the deletion * @since 6.1 */ void deleteAll(Class entityClass, List filters) throws SPersistenceException; /** */ void update(final UpdateDescriptor desc) throws SPersistenceException; void flushStatements() throws SPersistenceException; /** * Refresh a persistent object from the database, reloading its state. * This is useful after executing bulk HQL updates (via named queries) to synchronize * the in-memory managed entity with the actual database state, preventing Hibernate's * dirty-checking from overwriting the atomic SQL update with stale values. * The entity remains managed in the session after refresh. * * @param entity the entity to refresh from the database */ void refresh(final PersistentObject entity) throws SPersistenceException; /** * Delete a record from the table by id and its class type. * * @param id entity's id * @param entityClass The class which extends PersistentObject * @since 6.0 */ void delete(final long id, final Class entityClass) throws SPersistenceException; /** * Delete records from the table. * * @param ids A list contains entity ids * @param entityClass The class which extends PersistentObject * @since 6.0 */ void delete(final List ids, final Class entityClass) throws SPersistenceException; } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/services/SPersistenceException.java ================================================ /** * 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.services; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Charles Souillard */ public class SPersistenceException extends SBonitaException { private static final long serialVersionUID = -3172887017042060160L; public SPersistenceException(final Throwable t) { super(t); } public SPersistenceException(final String message) { super(message); } public SPersistenceException(final String message, final Throwable t) { super(message, t); } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/services/UpdateDescriptor.java ================================================ /** * 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.services; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Charles Souillard * @author Emmanuel Duchastenier */ public class UpdateDescriptor { private static final String ID = "id"; private final PersistentObject entity; private Map fields; public UpdateDescriptor(final PersistentObject entity) { super(); this.entity = entity; } public static UpdateDescriptor buildSetField(final PersistentObject entity, final String fieldName, final Object fieldValue) { final UpdateDescriptor updateDescriptor = new UpdateDescriptor(entity); updateDescriptor.addField(fieldName, fieldValue); return updateDescriptor; } public static UpdateDescriptor buildSetFields(final PersistentObject entity, final Map fields) { final UpdateDescriptor updateDescriptor = new UpdateDescriptor(entity); updateDescriptor.addFields(fields); return updateDescriptor; } public void addField(final String fieldName, final Object fieldValue) { if (fields == null) { fields = new HashMap(); } if (ID.equalsIgnoreCase(fieldName)) { throw new RuntimeException("Updating an object's " + ID + " field is forbidden"); } fields.put(fieldName, fieldValue); } public void addFields(final Map fields) { if (this.fields == null) { this.fields = new HashMap(); } if (fields.containsKey(ID)) { throw new RuntimeException("Updating an object's " + ID + " field is forbidden"); } this.fields.putAll(fields); } public PersistentObject getEntity() { return entity; } public Map getFields() { if (fields == null) { return Collections.emptyMap(); } return fields; } @Override public String toString() { return "UpdateDescriptor [entity=" + entity + ", fields=" + fields + "]"; } } ================================================ FILE: services/bonita-persistence/src/main/java/org/bonitasoft/engine/services/Vendor.java ================================================ /** * 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.services; import static org.apache.commons.lang3.StringUtils.containsIgnoreCase; import java.sql.DatabaseMetaData; import java.sql.SQLException; import org.hibernate.cfg.Configuration; /** * @author Baptiste Mesta */ public enum Vendor { ORACLE, SQLSERVER, POSTGRES, MYSQL, OTHER; /** * Get database vendor from databases metadata */ public static Vendor fromDatabaseMetadata(DatabaseMetaData metadata) throws SQLException { if (metadata != null) { String productName = metadata.getDatabaseProductName(); if (containsIgnoreCase(productName, "Oracle")) { return ORACLE; } if (containsIgnoreCase(productName, "Microsoft SQL Server")) { return SQLSERVER; } } return OTHER; } /** * Get database vendor from databases metadata */ public static Vendor fromHibernateConfiguration(Configuration configuration) { String hibernateDialect = configuration.getProperty("hibernate.dialect"); return fromHibernateDialectProperty(hibernateDialect); } public static Vendor fromHibernateDialectProperty(String hibernateDialect) { if (hibernateDialect != null) { if (hibernateDialect.toLowerCase().contains("postgresql")) { return POSTGRES; } else if (hibernateDialect.toLowerCase().contains("sqlserver")) { return SQLSERVER; } else if (hibernateDialect.toLowerCase().contains("oracle")) { return ORACLE; } else if (hibernateDialect.toLowerCase().contains("mysql")) { return MYSQL; } } return OTHER; } } ================================================ FILE: services/bonita-persistence/src/main/resources/META-INF/services/org.hibernate.boot.spi.SessionFactoryBuilderFactory ================================================ org.bonitasoft.engine.persistence.CustomDataTypesRegistration ================================================ FILE: services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/DefaultOrderByBuilderTest.java ================================================ /** * 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.persistence; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Before; import org.junit.Test; /** * @author Laurent Leseigneur */ public class DefaultOrderByBuilderTest { StringBuilder builder; DefaultOrderByBuilder defaultOrderByBuilder; @Before public void before() { //given builder = new StringBuilder(); defaultOrderByBuilder = new DefaultOrderByBuilder(); } @Test public void should_sort_asc() { //when defaultOrderByBuilder.appendOrderBy(builder, "field", OrderByType.ASC); //then assertThat(builder.toString()).isEqualTo("field ASC"); } @Test public void should_sort_desc() { //when defaultOrderByBuilder.appendOrderBy(builder, "field", OrderByType.DESC); //then assertThat(builder.toString()).isEqualTo("field DESC"); } @Test public void should_sort_asc_with_nulls_first() { //when defaultOrderByBuilder.appendOrderBy(builder, "field", OrderByType.ASC_NULLS_FIRST); //then assertThat(builder.toString()).isEqualTo("field ASC NULLS FIRST"); } @Test public void should_sort_asc_with_nulls_last() { //when defaultOrderByBuilder.appendOrderBy(builder, "field", OrderByType.ASC_NULLS_LAST); //then assertThat(builder.toString()).isEqualTo("field ASC NULLS LAST"); } @Test public void should_sort_desc_with_nulls_first() { //when defaultOrderByBuilder.appendOrderBy(builder, "field", OrderByType.DESC_NULLS_FIRST); //then assertThat(builder.toString()).isEqualTo("field DESC NULLS FIRST"); } @Test public void should_sort_desc_with_nulls_last() { //when defaultOrderByBuilder.appendOrderBy(builder, "field", OrderByType.DESC_NULLS_LAST); //then assertThat(builder.toString()).isEqualTo("field DESC NULLS LAST"); } } ================================================ FILE: services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/OrderByTypeTest.java ================================================ /** * 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.persistence; import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; import org.junit.Test; /** * @author Laurent Leseigneur */ public class OrderByTypeTest { @Test public void should_have_a_sql_keyword_for_each_orderBy() { for (OrderByType orderByType : Arrays.asList(OrderByType.values())) { assertThat(orderByType.getSqlKeyword()).as("should have a sql valid key word").isNotNull(); } } } ================================================ FILE: services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/QueryBuilderTest.java ================================================ /** * 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.persistence; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Arrays; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.bonitasoft.engine.commons.EnumToObjectConvertible; import org.bonitasoft.engine.persistence.search.FilterOperationType; import org.hibernate.Session; import org.hibernate.query.Query; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; /** * @author Baptiste Mesta */ public class QueryBuilderTest { private static final char LIKE_ESCAPE_CHARACTER = '§'; public Map classAliasMappings = singletonMap(TestObject.class.getName(), "testObj"); @Rule public SystemOutRule systemOutRule = new SystemOutRule().enableLog(); @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Mock private Session session; @Mock private Query query; @Before public void before() { when(session.createQuery(anyString())).thenAnswer(a -> { Query mock = mock(Query.class); doReturn(a.getArgument(0)).when(mock).getQueryString(); return mock; }); } @Test public void should_hasChanged_return_false_if_query_has_not_changed() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT TOTO FROM STUFF"); //when //then assertThat(queryBuilder.hasChanged()).isFalse(); } private QueryBuilder createQueryBuilder(String baseQuery) { return createQueryBuilder(baseQuery, null, OrderByCheckingMode.NONE); } private HQLQueryBuilder createQueryBuilder(String baseQuery, SelectListDescriptor selectListDescriptor, OrderByCheckingMode orderByCheckingMode) { doReturn(baseQuery).when(query).getQueryString(); return new HQLQueryBuilder<>(session, query, new DefaultOrderByBuilder(), classAliasMappings, LIKE_ESCAPE_CHARACTER, orderByCheckingMode, selectListDescriptor); } @Test public void should_hasChanged_return_true_if_query_has_changed() throws Exception { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT TOTO FROM STUFF"); //when queryBuilder.appendOrderByClause( Collections.singletonList(new OrderByOption(TestObject.class, "theValue", OrderByType.ASC)), TestObject.class); //then assertThat(queryBuilder.hasChanged()).isTrue(); } @Test public void should_generate_query_with_order_by() throws Exception { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendOrderByClause( Collections.singletonList(new OrderByOption(TestObject.class, "theValue", OrderByType.ASC)), TestObject.class); //then assertThat(queryBuilder.getQuery()) .isEqualTo("SELECT testObj.* FROM test_object testObj ORDER BY testObj.theValue ASC,testObj.id ASC"); } @Test public void should_generate_query_with_multiple_order_by() throws Exception { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendOrderByClause(Arrays.asList(new OrderByOption(TestObject.class, "theValue", OrderByType.ASC), new OrderByOption(TestObject.class, "id", OrderByType.ASC), new OrderByOption(TestObject.class, "lastName", OrderByType.DESC_NULLS_LAST)), TestObject.class); //then assertThat(queryBuilder.getQuery()) .isEqualTo( "SELECT testObj.* FROM test_object testObj ORDER BY testObj.theValue ASC,testObj.id ASC,testObj.lastName DESC NULLS LAST"); } @Test public void should_generate_query_with_filter() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters(Collections.singletonList(new FilterOption(TestObject.class, "theValue", 12)), null); //then assertThat(queryBuilder.getQuery()) .isEqualTo("SELECT testObj.* FROM test_object testObj WHERE (testObj.theValue = :f1)"); assertThat(queryBuilder.getQueryParameters().get("f1")).isEqualTo(12); } @Test public void should_generate_query_with_multiple_filters() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters( Arrays.asList(new FilterOption(TestObject.class, "age", 25), new FilterOption(TestObject.class, "lastname", "John")), null); //then assertThat(queryBuilder.getQuery()).isEqualTo( "SELECT testObj.* FROM test_object testObj WHERE (testObj.age = :f1 AND testObj.lastname = :f2)"); assertThat(queryBuilder.getQueryParameters().get("f1")).isEqualTo(25); assertThat(queryBuilder.getQueryParameters().get("f2")).isEqualTo("John"); } @Test public void should_generate_query_with_filter_on_query_containing_filters_already() { //given QueryBuilder queryBuilder = createQueryBuilder( "SELECT testObj.* FROM test_object testObj WHERE testObj.enabled = true"); //when queryBuilder.appendFilters(Collections.singletonList(new FilterOption(TestObject.class, "theValue", 12)), null); //then assertThat(queryBuilder.getQuery()).isEqualTo( "SELECT testObj.* FROM test_object testObj WHERE testObj.enabled = true AND (testObj.theValue = :f1)"); assertThat(queryBuilder.getQueryParameters().get("f1")).isEqualTo(12); } @Test public void should_generate_query_with_filter_and_order_clause() throws Exception { //given QueryBuilder queryBuilder = createQueryBuilder( "SELECT testObj.* FROM test_object testObj WHERE testObj.enabled = true"); //when queryBuilder.appendFilters(Collections.singletonList(new FilterOption(TestObject.class, "theValue", 12)), null); queryBuilder.appendOrderByClause( Collections.singletonList(new OrderByOption(TestObject.class, "theValue", OrderByType.ASC)), TestObject.class); //then assertThat(queryBuilder.getQuery()).isEqualTo( "SELECT testObj.* FROM test_object testObj WHERE testObj.enabled = true AND (testObj.theValue = :f1) ORDER BY testObj.theValue ASC,testObj.id ASC"); assertThat(queryBuilder.getQueryParameters().get("f1")).isEqualTo(12); } @Test public void should_generate_query_with_search_term() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters(Collections.emptyList(), new SearchFields(Collections.singletonList("toto"), Collections.singletonMap(TestObject.class, aSet("field1", "field2")))); //then assertThat(queryBuilder.getQuery()).matches( "SELECT testObj\\.\\* FROM test_object testObj WHERE \\(testObj.field1 LIKE :s1 ESCAPE '§' OR testObj.field2 LIKE :s2 ESCAPE '§'\\)"); assertThat(queryBuilder.getQueryParameters().get("s1")).isEqualTo("%toto%"); assertThat(queryBuilder.getQueryParameters().get("s2")).isEqualTo("%toto%"); } @Test public void should_generate_query_with_multiple_search_terms() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters(Collections.emptyList(), new SearchFields(Arrays.asList("toto", "tata"), Collections.singletonMap(TestObject.class, aSet("field1", "field2")))); //then assertThat(queryBuilder.getQuery()).isEqualTo( "SELECT testObj.* FROM test_object testObj WHERE (testObj.field1 LIKE :s1 ESCAPE '§' " + "OR testObj.field1 LIKE :s2 ESCAPE '§' " + "OR testObj.field2 LIKE :s3 ESCAPE '§' " + "OR testObj.field2 LIKE :s4 ESCAPE '§')"); assertThat(queryBuilder.getQueryParameters().get("s1")).isEqualTo("%toto%"); assertThat(queryBuilder.getQueryParameters().get("s2")).isEqualTo("%tata%"); assertThat(queryBuilder.getQueryParameters().get("s3")).isEqualTo("%toto%"); assertThat(queryBuilder.getQueryParameters().get("s4")).isEqualTo("%tata%"); } @Test public void should_generate_query_with_search_term_with_word_search() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters(Collections.emptyList(), new SearchFields(Collections.singletonList("toto"), Collections.singletonMap(TestObject.class, aSet("field1", "field2")))); //then assertThat(queryBuilder.getQuery()).isEqualTo( "SELECT testObj.* FROM test_object testObj WHERE " + "(testObj.field1 LIKE :s1 ESCAPE '§' OR testObj.field2 LIKE :s2 ESCAPE '§')"); assertThat(queryBuilder.getQueryParameters().get("s1")).isEqualTo("%toto%"); assertThat(queryBuilder.getQueryParameters().get("s2")).isEqualTo("%toto%"); } private Set aSet(String... fields) { return new TreeSet<>(Arrays.asList(fields)); } @Test public void should_generate_query_with_search_term_and_filters() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters(Collections.singletonList(new FilterOption(TestObject.class, "field1", "tata")), new SearchFields(Collections.singletonList("toto"), Collections.singletonMap(TestObject.class, aSet("field1", "field2")))); //then assertThat(queryBuilder.getQuery()).isEqualTo( "SELECT testObj.* FROM test_object testObj WHERE (testObj.field1 = :f1) AND (testObj.field2 LIKE :s1 ESCAPE '§')"); assertThat(queryBuilder.getQueryParameters().get("f1")).isEqualTo("tata"); assertThat(queryBuilder.getQueryParameters().get("s1")).isEqualTo("%toto%"); } @Test public void should_escape_special_chars_with_escape_character_in_search_terms() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters(Collections.emptyList(), new SearchFields(Collections.singletonList("the'value%with_special:_§§"), Collections.singletonMap(TestObject.class, aSet("field1")))); //then assertThat(queryBuilder.getQuery()) .isEqualTo( "SELECT testObj.* FROM test_object testObj WHERE (testObj.field1 LIKE :s1 ESCAPE '§')"); assertThat(queryBuilder.getQueryParameters().get("s1")).isEqualTo("%the'value§%with§_special:§_§§§§%"); } @Test public void should_generate_query_with_greater_or_equals_filter() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters( Collections.singletonList( new FilterOption(TestObject.class, "age", 25, FilterOperationType.GREATER_OR_EQUALS)), null); //then assertThat(queryBuilder.getQuery()) .isEqualTo("SELECT testObj.* FROM test_object testObj WHERE (testObj.age >= :f1)"); assertThat(queryBuilder.getQueryParameters().get("f1")).isEqualTo(25); } @Test public void should_generate_query_with_greater_filter() throws Exception { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters( Collections.singletonList(new FilterOption(TestObject.class, "age", 25, FilterOperationType.GREATER)), null); //then assertThat(queryBuilder.getQuery()) .isEqualTo("SELECT testObj.* FROM test_object testObj WHERE (testObj.age > :f1)"); assertThat(queryBuilder.getQueryParameters().get("f1")).isEqualTo(25); } @Test public void should_generate_query_with_less_filter() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters( Collections.singletonList(new FilterOption(TestObject.class, "age", 25, FilterOperationType.LESS)), null); //then assertThat(queryBuilder.getQuery()) .isEqualTo("SELECT testObj.* FROM test_object testObj WHERE (testObj.age < :f1)"); assertThat(queryBuilder.getQueryParameters().get("f1")).isEqualTo(25); } private QueryBuilder createBaseQueryBuilder() { return createQueryBuilder("SELECT testObj.* FROM test_object testObj"); } @Test public void should_generate_query_with_less_or_equals_filter() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters( Collections.singletonList( new FilterOption(TestObject.class, "age", 25, FilterOperationType.LESS_OR_EQUALS)), null); //then assertThat(queryBuilder.getQuery()) .isEqualTo("SELECT testObj.* FROM test_object testObj WHERE (testObj.age <= :f1)"); assertThat(queryBuilder.getQueryParameters().get("f1")).isEqualTo(25); } @Test public void should_generate_query_with_different_filter() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters( Collections.singletonList(new FilterOption(TestObject.class, "age", 25, FilterOperationType.DIFFERENT)), null); //then assertThat(queryBuilder.getQuery()) .isEqualTo("SELECT testObj.* FROM test_object testObj WHERE (testObj.age != :f1)"); assertThat(queryBuilder.getQueryParameters().get("f1")).isEqualTo(25); } @Test public void should_generate_query_with_between_filter() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters(Collections.singletonList(new FilterOption(TestObject.class, "age", 25, 27)), null); //then assertThat(queryBuilder.getQuery()).isEqualTo( "SELECT testObj.* FROM test_object testObj WHERE ((:f1 <= testObj.age AND testObj.age <= :f2))"); assertThat(queryBuilder.getQueryParameters().get("f1")).isEqualTo(25); assertThat(queryBuilder.getQueryParameters().get("f2")).isEqualTo(27); } @Test public void should_generate_query_with_parenthesis_filter() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters(Arrays.asList(new FilterOption(TestObject.class, "age", 12), new FilterOption(FilterOperationType.AND), new FilterOption(FilterOperationType.L_PARENTHESIS), new FilterOption(TestObject.class, "lastname", "john"), new FilterOption(FilterOperationType.OR), new FilterOption(TestObject.class, "lastname", "jack"), new FilterOption(FilterOperationType.R_PARENTHESIS)), null); //then assertThat(queryBuilder.getQuery()) .isEqualTo( "SELECT testObj.* FROM test_object testObj WHERE (testObj.age = :f1 AND (testObj.lastname = :f2 OR testObj.lastname = :f3 ))"); assertThat(queryBuilder.getQueryParameters().get("f1")).isEqualTo(12); assertThat(queryBuilder.getQueryParameters().get("f2")).isEqualTo("john"); assertThat(queryBuilder.getQueryParameters().get("f3")).isEqualTo("jack"); } @Test public void should_generate_query_with_like_filter() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters( Collections.singletonList( new FilterOption(TestObject.class, "lastname", "jack", FilterOperationType.LIKE)), null); //then assertThat(queryBuilder.getQuery()) .isEqualTo("SELECT testObj.* FROM test_object testObj WHERE (testObj.lastname LIKE :f1 ESCAPE '§')"); assertThat(queryBuilder.getQueryParameters().get("f1")).isEqualTo("%jack%"); } @Test public void should_generate_query_with_equals_filter_and_null_value() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters( Collections.singletonList( new FilterOption(TestObject.class, "lastname", null, FilterOperationType.EQUALS)), null); //then assertThat(queryBuilder.getQuery()) .isEqualTo("SELECT testObj.* FROM test_object testObj WHERE (testObj.lastname IS NULL)"); } @Test public void should_generate_query_with_filter_having_convertible_value() { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendFilters( Collections.singletonList( new FilterOption(TestObject.class, "lastname", TEST_ENUM.TEST1, FilterOperationType.EQUALS)), null); //then assertThat(queryBuilder.getQuery()) .isEqualTo("SELECT testObj.* FROM test_object testObj WHERE (testObj.lastname = :f1)"); assertThat(queryBuilder.getQueryParameters().get("f1")).isEqualTo(TEST_ENUM.TEST1); } @Test(expected = SBonitaReadException.class) public void should_throw_exception_if_class_is_not_mapped_in_filters() throws Exception { //given QueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj"); //when queryBuilder.appendOrderByClause( Collections.singletonList(new OrderByOption(PersistentObject.class, "theValue", OrderByType.ASC)), PersistentObject.class); queryBuilder.getQuery(); } private enum TEST_ENUM implements EnumToObjectConvertible { TEST1; @Override public int fromEnum() { return ordinal(); } } @Test public final void should_log_nothing_when_no_ORDER_BY_clause_in_query_and_no_checking_mode() throws Exception { HQLQueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj", descriptorWithoutOrderBy(), OrderByCheckingMode.NONE); queryBuilder.build(); assertThat(systemOutRule.getLog()).isEmpty(); } @Test public final void should_log_nothing_when_no_ORDER_BY_clause_in_query_and_checking_mode_is_NONE() throws Exception { HQLQueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj", descriptorWithoutOrderBy(), OrderByCheckingMode.NONE); queryBuilder.build(); assertThat(systemOutRule.getLog()).isEmpty(); } @Test public final void should_log_when_no_ORDER_BY_clause_in_query_and_checking_mode_is_WARNING() throws Exception { HQLQueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj", descriptorWithoutOrderBy(), OrderByCheckingMode.WARNING); queryBuilder.build(); assertThat(systemOutRule.getLog()) .contains("Query 'SELECT testObj.* FROM test_object testObj' does not contain 'ORDER BY' clause"); } @Test public final void should_log_nothing_when_ORDER_BY_clause_in_query_and_checking_mode_is_STRICT() throws Exception { HQLQueryBuilder queryBuilder = createQueryBuilder("SELECT testObj.* FROM test_object testObj", descriptorWithOrderBy(), OrderByCheckingMode.NONE); queryBuilder.build(); assertThat(systemOutRule.getLog()).isEmpty(); } private SelectListDescriptor descriptorWithOrderBy() { return new SelectListDescriptor<>("someQuery", emptyMap(), TestObject.class, new QueryOptions(0, 100, singletonList(new OrderByOption(TestObject.class, "someField", OrderByType.ASC)))); } private SelectListDescriptor descriptorWithoutOrderBy() { return new SelectListDescriptor<>("someQuery", emptyMap(), TestObject.class, new QueryOptions(0, 100)); } @Test public void should_escapeString_escape_quote() { new QueryGeneratorForFilters(emptyMap(), '%'); // 1) escape ' character by adding another ' character final String s = QueryBuilder.escapeString("toto'toto"); assertThat(s).isEqualTo("toto''toto"); } @Test public void should_escapeString_do_not_escape_like_wildcard() { new QueryGeneratorForFilters(emptyMap(), '%'); // 1) escape ' character by adding another ' character final String s = QueryBuilder.escapeString("%to'to%t_oto%"); assertThat(s).isEqualTo("%to''to%t_oto%"); } @Test public void should_detect_WHERE_when_in_root_query() { assertThat(QueryBuilder.hasWHEREInRootQuery("" + "Select * \n" + "from user_\n" + "WHERE\n" + "something\n" + "ORDER BY name\n")).isTrue(); } @Test public void should_detect_WHERE_when_in_root_query_when_lowercase() { assertThat(QueryBuilder.hasWHEREInRootQuery("" + "Select * \n" + "from user_\n" + "where\n" + "something\n" + "ORDER BY name\n")).isTrue(); } @Test public void should_not_detect_WHERE_when_only_in_sub_query() { assertThat(QueryBuilder.hasWHEREInRootQuery("" + "Select * \n" + "from (\n" + " Select * \n" + " from user_\n" + " WHERE\n" + " something)\n" + ")\n" + "ORDER BY name")).isFalse(); } @Test public void should_not_detect_WHERE_when_not_present() { assertThat(QueryBuilder.hasWHEREInRootQuery("" + "Select * \n" + "from user_\n" + "ORDER BY name\n")).isFalse(); } @Test public void should_detect_WHERE_when_select_has_parenthesis() { assertThat(QueryBuilder.hasWHEREInRootQuery("" + "Select count(*) \n" + "from user_\n" + "WHERE (something)\n" + "ORDER BY name\n")).isTrue(); } @Test public void should_not_detect_WHERE_with_subqueries() { assertThat(QueryBuilder.hasWHEREInRootQuery("" + "Select * \n" + "from (\n" + " Select * \n" + " from user_\n" + " WHERE\n" + " something\n" + ") user_\n" + "WHERE \n (somethings)")).isTrue(); } } ================================================ FILE: services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/QueryGeneratorForFiltersTest.java ================================================ /** * Copyright (C) 2020 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.persistence; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import java.util.Arrays; import java.util.Collections; import org.bonitasoft.engine.persistence.search.FilterOperationType; import org.junit.Test; public class QueryGeneratorForFiltersTest { @Test public void should_generate_query_with_one_boolean_filter() throws Exception { QueryGeneratorForFilters generator = new QueryGeneratorForFilters( singletonMap(TestObject.class.getName(), "testObject"), '%'); QueryGeneratorForFilters.QueryGeneratedFilters whereClause = generator.generate(Collections.singletonList( new FilterOption(TestObject.class, "enabled", true, FilterOperationType.EQUALS))); assertThat(whereClause.getFilters()).isEqualTo("testObject.enabled = :f1"); assertThat(whereClause.getParameters()).containsOnly(entry("f1", true)); assertThat(whereClause.getSpecificFilters()).containsOnly("testObject.enabled"); } @Test public void should_generate_query_with_multiple_filters() throws Exception { QueryGeneratorForFilters generator = new QueryGeneratorForFilters( singletonMap(TestObject.class.getName(), "testObject"), '%'); QueryGeneratorForFilters.QueryGeneratedFilters whereClause = generator.generate(Arrays.asList( new FilterOption(TestObject.class, "enabled", true, FilterOperationType.EQUALS), new FilterOption(TestObject.class, "name", "john", FilterOperationType.EQUALS))); assertThat(whereClause.getFilters()).isEqualTo("testObject.enabled = :f1 AND testObject.name = :f2"); assertThat(whereClause.getParameters()).containsOnly(entry("f1", true), entry("f2", "john")); assertThat(whereClause.getSpecificFilters()).containsOnly("testObject.enabled", "testObject.name"); } } ================================================ FILE: services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/QueryGeneratorForSearchTermTest.java ================================================ /** * Copyright (C) 2020 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.persistence; import static java.util.Collections.singletonList; import static java.util.stream.Collectors.toSet; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import java.util.stream.Stream; import org.junit.Test; public class QueryGeneratorForSearchTermTest { @Test public void should_getQueryFilters_append_OR_clause_when_wordSearch_is_enabled() { QueryGeneratorForSearchTerm generator = new QueryGeneratorForSearchTerm('$'); QueryGeneratorForSearchTerm.QueryGeneratedSearchTerms query = generator .generate(Stream.of("field1").collect(toSet()), singletonList("termA")); assertThat(query.getSearch()).isEqualTo("field1 LIKE :s1 ESCAPE '$'"); assertThat(query.getParameters()).containsOnly( entry("s1", "%termA%")); } @Test public void should_escape_special_chars() { QueryGeneratorForSearchTerm generator = new QueryGeneratorForSearchTerm('@'); QueryGeneratorForSearchTerm.QueryGeneratedSearchTerms query = generator .generate(Stream.of("field1").collect(toSet()), singletonList("100%")); assertThat(query.getSearch()).isEqualTo("field1 LIKE :s1 ESCAPE '@'"); assertThat(query.getParameters()).containsOnly( entry("s1", "%100@%%")); } } ================================================ FILE: services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/QueryOptionsTest.java ================================================ /** * 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.persistence; import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; import java.util.List; import org.junit.Test; public class QueryOptionsTest { @Test public void getNextPageShouldPreserveOrderOptions() throws Exception { // given: final QueryOptions queryOptions = new QueryOptions(0, 10, list(new OrderByOption(PersistentObject.class, "fieldName", OrderByType.ASC)), list(new FilterOption(PersistentObject.class, "fieldName")), null); // when: final QueryOptions nextPage = QueryOptions.getNextPage(queryOptions); // then: assertThat(nextPage.getOrderByOptions()).isNotNull(); assertThat(nextPage.getOrderByOptions()).hasSize(1); } @Test public void getNextPageShouldPreserveFilters() throws Exception { // given: final QueryOptions queryOptions = new QueryOptions(0, 10, list(new OrderByOption(PersistentObject.class, "fieldName", OrderByType.ASC)), list(new FilterOption(PersistentObject.class, "fieldName")), null); // when: final QueryOptions nextPage = QueryOptions.getNextPage(queryOptions); // then: assertThat(nextPage.getFilters()).isNotNull(); assertThat(nextPage.getFilters()).hasSize(1); } @Test public void getNextPageShouldPreserveSearchFields() throws Exception { // given: final QueryOptions queryOptions = new QueryOptions(0, 10, list(new OrderByOption(PersistentObject.class, "fieldName", OrderByType.ASC)), list(new FilterOption(PersistentObject.class, "fieldName")), new SearchFields(null, null)); // when: final QueryOptions nextPage = QueryOptions.getNextPage(queryOptions); // then: assertThat(nextPage.getMultipleFilter()).isNotNull(); } private List list(final Object o) { return Arrays.asList(o); } } ================================================ FILE: services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/SQLQueryBuilderTest.java ================================================ /** * 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.persistence; import static java.util.Collections.singletonMap; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import java.util.Map; import org.hibernate.query.NativeQuery; import org.hibernate.query.Query; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class SQLQueryBuilderTest { @Mock private NativeQuery mockedQuery; @Mock private Query query; private static final char LIKE_ESCAPE_CHARACTER = '§'; private Map classAliasMappings = singletonMap(TestObject.class.getName(), "testObj"); private SQLQueryBuilder createQueryBuilder(String baseQuery) { doReturn(baseQuery).when(query).getQueryString(); return new SQLQueryBuilder(null, query, new DefaultOrderByBuilder(), classAliasMappings, LIKE_ESCAPE_CHARACTER, OrderByCheckingMode.NONE, null); } @Test public void should_generate_query_with_boolean_parameter() throws Exception { //given String baseQuery = "SELECT testObj.* FROM test_object testObj WHERE testObj.enabled = :trueValue"; doReturn(baseQuery).when(mockedQuery).getQueryString(); SQLQueryBuilder queryBuilder = createQueryBuilder(baseQuery); //when queryBuilder.addConstantsAsParameters(mockedQuery); //then verify(mockedQuery).setParameter("trueValue", true); } } ================================================ FILE: services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/SelectListDescriptorTest.java ================================================ /** * 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.persistence; import java.util.Collections; import org.junit.Test; /** * @author Celine Souchet */ public class SelectListDescriptorTest { /** * Test method for * {@link org.bonitasoft.engine.persistence.SelectListDescriptor#SelectListDescriptor(java.lang.String, java.util.Map, java.lang.Class, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test(expected = IllegalArgumentException.class) public final void throw_exception_when_contruct_object_without_query_options() { new SelectListDescriptor("queryName", Collections. emptyMap(), PersistentObject.class, null); } /** * Test method for * {@link org.bonitasoft.engine.persistence.SelectListDescriptor#SelectListDescriptor(java.lang.String, java.util.Map, java.lang.Class, java.lang.Class, org.bonitasoft.engine.persistence.QueryOptions)} * . */ @Test(expected = IllegalArgumentException.class) public final void throw_exception_when_contruct_object_without_query_options2() { new SelectListDescriptor("queryName", Collections. emptyMap(), PersistentObject.class, Object.class, null); } } ================================================ FILE: services/bonita-persistence/src/test/java/org/bonitasoft/engine/persistence/TestObject.java ================================================ /** * 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.persistence; /** * @author Baptiste Mesta */ public class TestObject implements PersistentObject { @Override public long getId() { return 0; } @Override public void setId(long id) { } } ================================================ FILE: services/bonita-persistence/src/test/java/org/bonitasoft/engine/recorder/impl/RecorderImplTest.java ================================================ /** * 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.recorder.impl; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Collections; import java.util.UUID; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.PersistenceService; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.AdditionalAnswers; import org.mockito.ArgumentMatcher; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta. */ @RunWith(MockitoJUnitRunner.class) public class RecorderImplTest { @Mock private PersistenceService persistenceService; @Mock private EventService eventService; @InjectMocks private RecorderImpl recorder; @Test public void should_fire_event_when_recording_an_insert() throws Exception { when(persistenceService.insert(any())).thenAnswer(AdditionalAnswers.returnsFirstArg()); MyPersistentObject entity = entity(); recorder.recordInsert(insertRecord(entity), "theEvent"); verify(eventService).fireEvent(argThat(match("theEvent_CREATED", entity))); } @Test public void should_fire_event_when_recording_an_update() throws Exception { MyPersistentObject entity = entity(); recorder.recordUpdate(updateRecord(entity), "theEvent"); verify(eventService).fireEvent(argThat(match("theEvent_UPDATED", entity))); } @Test public void should_fire_event_when_recording_a_delete() throws Exception { MyPersistentObject entity = entity(); recorder.recordDelete(deleteRecord(entity), "theEvent"); verify(eventService).fireEvent(argThat(match("theEvent_DELETED", entity))); } protected ArgumentMatcher match(String type, Object entity) { return sEvent -> sEvent.getType().equals(type) && sEvent.getObject().equals(entity); } private InsertRecord insertRecord(PersistentObject entity) { return new InsertRecord(entity); } private UpdateRecord updateRecord(PersistentObject entity) { return UpdateRecord.buildSetFields(entity, Collections.emptyMap()); } private DeleteRecord deleteRecord(PersistentObject entity) { return new DeleteRecord(entity); } private MyPersistentObject entity() { return new MyPersistentObject(); } private static class MyPersistentObject implements PersistentObject { private final long id = UUID.randomUUID().getLeastSignificantBits(); @Override public long getId() { return id; } @Override public void setId(long id) { } } } ================================================ FILE: services/bonita-persistence/src/test/java/org/bonitasoft/engine/sequence/SequenceDAOTest.java ================================================ /** * 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.sequence; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.bonitasoft.engine.sequence.SequenceDAO.NEXT_ID; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; public class SequenceDAOTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Mock private Connection connection; @Mock private PreparedStatement preparedStatement; @Mock private ResultSet resultSet; @InjectMocks private SequenceDAO sequenceDAO; @Before public void before() { sequenceDAO = new SequenceDAO(connection); } @Test public void should_getNextId_from_database() throws Exception { doReturn(preparedStatement).when(connection).prepareStatement(SequenceDAO.SELECT_BY_ID); doReturn(resultSet).when(preparedStatement).executeQuery(); doReturn(true, false).when(resultSet).next(); doReturn(2349L).when(resultSet).getLong(NEXT_ID); long nextId = sequenceDAO.selectById(123L); verify(preparedStatement).setLong(1, 123L); assertThat(nextId).isEqualTo(2349); } @Test public void should_getNextId_fail_when_there_is_more_than_one_result() throws Exception { doReturn(preparedStatement).when(connection).prepareStatement(SequenceDAO.SELECT_BY_ID); doReturn(resultSet).when(preparedStatement).executeQuery(); doReturn(true, true, false).when(resultSet).next(); assertThatThrownBy(() -> sequenceDAO.selectById(123L)) .isInstanceOf(SQLException.class) .hasMessage("Did not expect more than one value for id: 123"); } @Test public void should_getNextId_fail_when_there_is_no_result() throws Exception { doReturn(preparedStatement).when(connection).prepareStatement(SequenceDAO.SELECT_BY_ID); doReturn(resultSet).when(preparedStatement).executeQuery(); doReturn(false).when(resultSet).next(); assertThatThrownBy(() -> sequenceDAO.selectById(123L)) .isInstanceOf(SObjectNotFoundException.class) .hasMessage("Found no row for id: 123"); } @Test public void should_getNextId_fail_when_the_result_is_null() throws Exception { doReturn(preparedStatement).when(connection).prepareStatement(SequenceDAO.SELECT_BY_ID); doReturn(resultSet).when(preparedStatement).executeQuery(); doReturn(true, false).when(resultSet).next(); doReturn(true).when(resultSet).wasNull(); assertThatThrownBy(() -> sequenceDAO.selectById(123L)) .isInstanceOf(SQLException.class) .hasMessage("Did not expect a null value for the column nextid"); } @Test public void should_update_database_with_next_sequence_id() throws Exception { doReturn(preparedStatement).when(connection).prepareStatement(SequenceDAO.UPDATE_SEQUENCE); doReturn(1).when(preparedStatement).executeUpdate(); sequenceDAO.updateSequence(11233L, 123L); verify(preparedStatement).setObject(1, 11233L); verify(preparedStatement).setObject(2, 123L); } } ================================================ FILE: services/bonita-persistence/src/test/java/org/bonitasoft/engine/sequence/SequenceManagerImplTest.java ================================================ /** * 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.sequence; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import javax.sql.DataSource; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.lock.BonitaLock; import org.bonitasoft.engine.lock.LockService; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; public class SequenceManagerImplTest { private static final String OBJECT_w_2 = "OBJECT_w_2"; private static final String OBJECT_w_5 = "OBJECT_w_5"; private static final String OBJECT_w_100 = "OBJECT_w_100"; private static final String OBJECT_w_1000 = "OBJECT_w_1000"; private static final long SEQUENCE_w_2 = 553L; private static final long SEQUENCE_w_5 = 554L; private static final long SEQUENCE_w_100 = 555L; private static final long SEQUENCE_w_1000 = 556L; private static final int RETRIES = 2; @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Mock private SequenceDAO sequenceDAO; @Mock private LockService lockService; @Mock private BonitaLock lock; @Mock private DataSource dataSource; @Mock private Connection connection; @Mock private SequenceMappingProvider sequenceMappingProvider; private SequenceManagerImpl sequenceManager; @Before public void before() throws Exception { doReturn(connection).when(dataSource).getConnection(); final List sequenceMappings = List.of( seq(OBJECT_w_2, SEQUENCE_w_2, 2), seq(OBJECT_w_5, SEQUENCE_w_5, 5), seq(OBJECT_w_100, SEQUENCE_w_100, 100), seq(OBJECT_w_1000, SEQUENCE_w_1000, 1000)); doReturn(sequenceMappings).when(sequenceMappingProvider).getSequenceMappings(); doReturn(lock).when(lockService).lock(anyLong(), anyString()); sequenceManager = new SequenceManagerImpl(lockService, sequenceMappingProvider, dataSource, RETRIES, 1, 1) { @Override SequenceDAO createDao(Connection connection) { return sequenceDAO; } }; } private SequenceMapping seq(String className, long sequenceId, int rangeSize) { return new SequenceMapping(className, sequenceId, rangeSize); } @Test public void should_get_next_id_from_database() throws Exception { doReturn(100L).when(sequenceDAO).selectById(SEQUENCE_w_100); long nextId = sequenceManager.getNextId(OBJECT_w_100); assertThat(nextId).isEqualTo(100); } @Test public void should_get_next_id_once_all_ids_is_range_are_used() throws Exception { doReturn(100L, 200L).when(sequenceDAO).selectById(SEQUENCE_w_5); assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(100); assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(101); assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(102); assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(103); assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(104); //all ids are taken, ask for new ones assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(200); } @Test public void should_get_next_id_once_all_ids_in_range_are_used_for_each_object() throws Exception { doReturn(100L, 200L).when(sequenceDAO).selectById(SEQUENCE_w_5); doReturn(1100L, 1200L).when(sequenceDAO).selectById(SEQUENCE_w_2); assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(100); assertThat(sequenceManager.getNextId(OBJECT_w_2)).isEqualTo(1100); assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(101); assertThat(sequenceManager.getNextId(OBJECT_w_2)).isEqualTo(1101); assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(102); assertThat(sequenceManager.getNextId(OBJECT_w_2)).isEqualTo(1200); assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(103); assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(104); assertThat(sequenceManager.getNextId(OBJECT_w_5)).isEqualTo(200); } @Test public void high_concurrency_should_always_return_a_next_id() throws Exception { // given: doReturn(100L).when(sequenceDAO).selectById(SEQUENCE_w_2); // when: final RunnableWithFailures runnable = new RunnableWithFailures(); List threads = new ArrayList<>(); for (int i = 0; i < 100; i++) { threads.add(new Thread(runnable)); } for (Thread thread : threads) { thread.start(); } for (Thread thread : threads) { thread.join(); } // then: assertThat(runnable.failures).isEqualTo(0); } private class RunnableWithFailures implements Runnable { int failures = 0; @Override public void run() { try { for (int i = 0; i < 100; i++) { sequenceManager.getNextId(OBJECT_w_2); } } catch (SObjectNotFoundException | IllegalStateException e) { failures++; } } } @Test public void should_update_sequence_inside_a_lock() throws Exception { doReturn(100L).when(sequenceDAO).selectById(SEQUENCE_w_5); InOrder inOrder = inOrder(lockService, connection, sequenceDAO); sequenceManager.getNextId(OBJECT_w_5); inOrder.verify(lockService).lock(SEQUENCE_w_5, SequenceManagerImpl.SEQUENCE); inOrder.verify(sequenceDAO).selectById(SEQUENCE_w_5); inOrder.verify(sequenceDAO).updateSequence(105L, SEQUENCE_w_5); inOrder.verify(connection).commit(); inOrder.verify(lockService).unlock(lock); } @Test public void should_update_sequence_with_id_of_next_range() throws Exception { doReturn(100L).when(sequenceDAO).selectById(SEQUENCE_w_5); doReturn(100L).when(sequenceDAO).selectById(SEQUENCE_w_1000); sequenceManager.getNextId(OBJECT_w_5); sequenceManager.getNextId(OBJECT_w_1000); verify(sequenceDAO).updateSequence(105L, SEQUENCE_w_5); verify(sequenceDAO).updateSequence(1100L, SEQUENCE_w_1000); } @Test public void should_retry_to_update_sequence_when_select_of_next_id_fails() throws Exception { doThrow(new SQLException("SQL error")).doReturn(200L).when(sequenceDAO).selectById(SEQUENCE_w_5); long nextId = sequenceManager.getNextId(OBJECT_w_5); assertThat(nextId).isEqualTo(200L); } @Test public void should_retry_to_update_sequence_when_update_of_next_range_fails() throws Exception { doReturn(200L).when(sequenceDAO).selectById(SEQUENCE_w_5); doThrow(new SQLException("SQL error")).doNothing().when(sequenceDAO).updateSequence(205, SEQUENCE_w_5); long nextId = sequenceManager.getNextId(OBJECT_w_5); assertThat(nextId).isEqualTo(200L); } @Test public void should_retry_to_update_sequence_when_commit_fails() throws Exception { doReturn(200L).when(sequenceDAO).selectById(SEQUENCE_w_5); doNothing().when(sequenceDAO).updateSequence(205, SEQUENCE_w_5); doThrow(new SQLException("commit error")).doNothing().when(connection).commit(); long nextId = sequenceManager.getNextId(OBJECT_w_5); assertThat(nextId).isEqualTo(200L); } @Test public void should_fail_to_update_sequence_when_select_of_next_id_fails() throws Exception { doThrow(new SQLException("SQL error")).doThrow(new SQLException("SQL error")).doReturn(200L).when(sequenceDAO) .selectById(SEQUENCE_w_5); assertThatThrownBy(() -> sequenceManager.getNextId(OBJECT_w_5)) .hasMessage("Unable to get a sequence id for 554"); } @Test public void should_fail_to_update_sequence_when_update_of_next_range_fails() throws Exception { doReturn(200L).when(sequenceDAO).selectById(SEQUENCE_w_5); doThrow(new SQLException("SQL error")).doThrow(new SQLException("SQL error")).doNothing().when(sequenceDAO) .updateSequence(205, SEQUENCE_w_5); assertThatThrownBy(() -> sequenceManager.getNextId(OBJECT_w_5)) .hasMessage("Unable to get a sequence id for 554"); } @Test public void should_fail_to_update_sequence_when_commit_fails() throws Exception { doReturn(200L).when(sequenceDAO).selectById(SEQUENCE_w_5); doNothing().when(sequenceDAO).updateSequence(205, SEQUENCE_w_5); doThrow(new SQLException("commit error")).doThrow(new SQLException("commit error")).doNothing().when(connection) .commit(); assertThatThrownBy(() -> sequenceManager.getNextId(OBJECT_w_5)) .hasMessage("Unable to get a sequence id for 554"); } @Test public void should_not_increase_next_id_when_we_are_unable_to_commit_transaction() throws Exception { doReturn(200L).when(sequenceDAO).selectById(SEQUENCE_w_5); doNothing().when(sequenceDAO).updateSequence(205, SEQUENCE_w_5); doThrow(new SQLException("commit error")).when(connection).commit(); assertThatThrownBy(() -> sequenceManager.getNextId(OBJECT_w_5)) .hasMessage("Unable to get a sequence id for 554"); // should also fail and not return 200 (saved in memory) assertThatThrownBy(() -> sequenceManager.getNextId(OBJECT_w_5)) .hasMessage("Unable to get a sequence id for 554"); } } ================================================ FILE: services/bonita-persistence/src/test/java/org/bonitasoft/engine/sequence/SequenceRangeTest.java ================================================ /** * 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.sequence; import static org.assertj.core.api.Assertions.assertThat; import java.util.Optional; import org.junit.Test; public class SequenceRangeTest { @Test public void should_give_next_id_in_the_sequence() { SequenceRange sequenceRange = new SequenceRange(4); sequenceRange.updateToNextRange(10); Optional nextAvailableId = sequenceRange.getNextAvailableId(); assertThat(nextAvailableId).isPresent().contains(10L); } @Test public void should_not_give_id_if_sequence_is_not_initialized() { SequenceRange sequenceRange = new SequenceRange(4); Optional nextAvailableId = sequenceRange.getNextAvailableId(); assertThat(nextAvailableId).isNotPresent(); } @Test public void should_give_all_if_from_range() { SequenceRange sequenceRange = new SequenceRange(3); sequenceRange.updateToNextRange(1); assertThat(sequenceRange.getNextAvailableId()).isPresent().contains(1L); assertThat(sequenceRange.getNextAvailableId()).isPresent().contains(2L); assertThat(sequenceRange.getNextAvailableId()).isPresent().contains(3L); assertThat(sequenceRange.getNextAvailableId()).isNotPresent(); } @Test public void should_not_give_id_when_sequence_is_completed() { SequenceRange sequenceRange = new SequenceRange(2); sequenceRange.updateToNextRange(1); assertThat(sequenceRange.getNextAvailableId()).isPresent(); assertThat(sequenceRange.getNextAvailableId()).isPresent(); assertThat(sequenceRange.getNextAvailableId()).isNotPresent(); assertThat(sequenceRange.getNextAvailableId()).isNotPresent(); assertThat(sequenceRange.getNextAvailableId()).isNotPresent(); } } ================================================ FILE: services/bonita-persistence/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: services/bonita-platform/build.gradle ================================================ dependencies { api project(':services:bonita-cache') api project(':services:bonita-builder') api project(':services:bonita-persistence') api project(':services:bonita-commons') testImplementation project(':services:bonita-events') testImplementation(libs.mockitoCore) testImplementation(libs.assertj) testImplementation(libs.logback) annotationProcessor(libs.lombok) compileOnly(libs.lombok) } group = 'org.bonitasoft.engine.platform' def generatePlatformProperties = tasks.register("generatePlatformProperties") { def outputDir = layout.buildDirectory.dir("generated/main/resources/org/bonitasoft/engine/platform/model/impl") def outputFile = layout.buildDirectory.file("generated/main/resources/org/bonitasoft/engine/platform/model/impl/platform.properties") def projectVersion = project.version doFirst{ outputDir.get().asFile.mkdirs() outputFile.get().asFile.text = "version $projectVersion" } inputs.property "version", projectVersion outputs.file outputFile } tasks.processResources.dependsOn generatePlatformProperties sourceSets { main { resources { srcDir(layout.buildDirectory.dir("generated/main/resources")) } } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/PlatformRetriever.java ================================================ /** * 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.platform; import org.bonitasoft.engine.platform.exception.SPlatformNotFoundException; import org.bonitasoft.engine.platform.model.SPlatform; /** * @author Elias Ricken de Medeiros */ public interface PlatformRetriever { SPlatform getPlatform() throws SPlatformNotFoundException; } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/PlatformService.java ================================================ /** * 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.platform; import org.bonitasoft.engine.platform.exception.*; import org.bonitasoft.engine.platform.model.SPlatform; import org.bonitasoft.engine.platform.model.SPlatformProperties; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Charles Souillard * @author Matthieu Chaffotte * @author Celine Souchet */ public interface PlatformService { String PLATFORM = "PLATFORM"; /** * Retrieve the platform from the cache * No need to be in a transaction * * @return sPlatform * @throws SPlatformNotFoundException * occurs when the identifier does not refer to an existing sPlatform * @since 6.0 */ SPlatform getPlatform() throws SPlatformNotFoundException; /** * Set status of the tenant into activated * * @throws STenantNotFoundException occurs when the identifier does not refer to an existing sTenant * @throws STenantActivationException occurs when an exception is thrown during activating sTenant * @since 6.0 */ void resumeServices() throws STenantNotFoundException, STenantActivationException, SPlatformNotFoundException, SPlatformUpdateException; /** * update status of tenant object to PAUSED */ void pauseServices() throws STenantUpdateException, STenantNotFoundException, SPlatformNotFoundException, SPlatformUpdateException; /** * Return true if the platform is created, else return false. * * @return true or false * @since 6.0 */ boolean isPlatformCreated(); /** * Return the platform properties * * @return The platform properties * @since 6.1 */ SPlatformProperties getSPlatformProperties(); void updatePlatform(EntityUpdateDescriptor descriptor) throws SPlatformUpdateException; } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/SDeletingActivatedTenantException.java ================================================ /** * 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.platform.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Baptiste Mesta */ public class SDeletingActivatedTenantException extends SBonitaException { private static final long serialVersionUID = 5003988198140435562L; public SDeletingActivatedTenantException() { super(); } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/SPlatformDeletionException.java ================================================ /** * 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.platform.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Exception related to the platform module * * @author Charles Souillard */ public class SPlatformDeletionException extends SBonitaException { private static final long serialVersionUID = 7615655279956204016L; public SPlatformDeletionException(final String message) { super(message); } public SPlatformDeletionException(final Throwable e) { super(e); } public SPlatformDeletionException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/SPlatformNotFoundException.java ================================================ /** * 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.platform.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Exception related to the platform module * * @author Charles Souillard */ public class SPlatformNotFoundException extends SBonitaException { private static final long serialVersionUID = 7615655279956204016L; public SPlatformNotFoundException(final String message) { super(message); } public SPlatformNotFoundException(final Throwable e) { super(e); } public SPlatformNotFoundException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/SPlatformUpdateException.java ================================================ /** * 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.platform.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Exception related to the platform module * * @author Charles Souillard */ public class SPlatformUpdateException extends SBonitaException { private static final long serialVersionUID = 7615655279956204016L; public SPlatformUpdateException(final String message) { super(message); } public SPlatformUpdateException(final Throwable e) { super(e); } public SPlatformUpdateException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantActivationException.java ================================================ /** * 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.platform.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Exception related to the platform module * * @author Lu Kai */ public class STenantActivationException extends SBonitaException { private static final long serialVersionUID = 5049049035847825930L; public STenantActivationException(final String message) { super(message); } public STenantActivationException(final Throwable e) { super(e); } public STenantActivationException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantAlreadyExistException.java ================================================ /** * 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.platform.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Exception related to the platform module * * @author Charles Souillard */ public class STenantAlreadyExistException extends SBonitaException { private static final long serialVersionUID = 7615655279956204016L; public STenantAlreadyExistException(final String message) { super(message); } public STenantAlreadyExistException(final Throwable e) { super(e); } public STenantAlreadyExistException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantCreationException.java ================================================ /** * 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.platform.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Exception related to the platform module * * @author Charles Souillard */ public class STenantCreationException extends SBonitaException { private static final long serialVersionUID = 7615655279956204016L; public STenantCreationException(final String message) { super(message); } public STenantCreationException(final Throwable e) { super(e); } public STenantCreationException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantDeactivationException.java ================================================ /** * 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.platform.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Exception related to the platform module * * @author Lu Kai */ public class STenantDeactivationException extends SBonitaException { private static final long serialVersionUID = 5049049035847825930L; public STenantDeactivationException(final String message) { super(message); } public STenantDeactivationException(final Throwable e) { super(e); } public STenantDeactivationException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantDeletionException.java ================================================ /** * 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.platform.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Exception related to the platform module * * @author Charles Souillard */ public class STenantDeletionException extends SBonitaException { private static final long serialVersionUID = 7615655279956204016L; public STenantDeletionException(final String message) { super(message); } public STenantDeletionException(final Throwable e) { super(e); } public STenantDeletionException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantException.java ================================================ /** * 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.platform.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Exception related to the platform module * * @author Charles Souillard */ public class STenantException extends SBonitaException { private static final long serialVersionUID = 7615655279956204016L; public STenantException(final String message) { super(message); } public STenantException(final Throwable e) { super(e); } public STenantException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantNotFoundException.java ================================================ /** * 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.platform.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Exception related to the platform module * * @author Charles Souillard */ public class STenantNotFoundException extends SBonitaException { private static final long serialVersionUID = 7615655279956204016L; public STenantNotFoundException(final String message) { super(message); } public STenantNotFoundException(final Throwable e) { super(e); } public STenantNotFoundException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/exception/STenantUpdateException.java ================================================ /** * 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.platform.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Exception related to the platform module * * @author Charles Souillard */ public class STenantUpdateException extends SBonitaException { private static final long serialVersionUID = 7615655279956204016L; public STenantUpdateException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/impl/PlatformRetrieverImpl.java ================================================ /** * 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.platform.impl; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.platform.PlatformRetriever; import org.bonitasoft.engine.platform.exception.SPlatformNotFoundException; import org.bonitasoft.engine.platform.model.SPlatform; import org.bonitasoft.engine.services.PersistenceService; /** * @author Elias Ricken de Medeiros */ public class PlatformRetrieverImpl implements PlatformRetriever { private final PersistenceService platformPersistenceService; private static final String QUERY_GET_PLATFORM = "getPlatform"; public PlatformRetrieverImpl(final PersistenceService platformPersistenceService) { this.platformPersistenceService = platformPersistenceService; } @Override public SPlatform getPlatform() throws SPlatformNotFoundException { try { SPlatform platform = platformPersistenceService .selectOne(new SelectOneDescriptor<>(QUERY_GET_PLATFORM, null, SPlatform.class)); if (platform == null) { throw new SPlatformNotFoundException("No platform found"); } return platform; } catch (final SBonitaReadException e) { throw new SPlatformNotFoundException("Unable to check if a platform already exists : " + e.getMessage(), e); } } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/impl/PlatformServiceImpl.java ================================================ /** * 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.platform.impl; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.platform.PlatformRetriever; import org.bonitasoft.engine.platform.PlatformService; import org.bonitasoft.engine.platform.exception.*; import org.bonitasoft.engine.platform.model.SPlatform; import org.bonitasoft.engine.platform.model.SPlatformProperties; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.SPersistenceException; import org.bonitasoft.engine.services.UpdateDescriptor; /** * @author Charles Souillard * @author Celine Souchet */ @Slf4j public class PlatformServiceImpl implements PlatformService { private final PersistenceService platformPersistenceService; private final SPlatformProperties sPlatformProperties; private final Recorder recorder; private final PlatformRetriever platformRetriever; public PlatformServiceImpl(final PersistenceService platformPersistenceService, PlatformRetriever platformRetriever, final Recorder recorder, final SPlatformProperties sPlatformProperties) { this.platformPersistenceService = platformPersistenceService; this.sPlatformProperties = sPlatformProperties; this.recorder = recorder; this.platformRetriever = platformRetriever; } @Override public SPlatform getPlatform() throws SPlatformNotFoundException { try { return platformRetriever.getPlatform(); } catch (SPlatformNotFoundException e) { throw e; } catch (final Exception e) { throw new SPlatformNotFoundException("Unable to check if a platform already exists : " + e.getMessage(), e); } } // FIXME: Not necessary anymore, as platform is always created by ScriptExecutor at startup @Override public boolean isPlatformCreated() { try { getPlatform(); return true; } catch (final SPlatformNotFoundException e) { return false; } } @Override public void resumeServices() throws SPlatformNotFoundException, SPlatformUpdateException { final SPlatform platform = getPlatform(); final UpdateDescriptor desc = new UpdateDescriptor(platform); desc.addField(SPlatform.MAINTENANCE_ENABLED, false); try { platformPersistenceService.update(desc); } catch (final SPersistenceException e) { throw new SPlatformUpdateException("Problem while activating services", e); } } @Override public void pauseServices() throws SPlatformNotFoundException, SPlatformUpdateException { final SPlatform platform = getPlatform(); final UpdateDescriptor desc = new UpdateDescriptor(platform); desc.addField(SPlatform.MAINTENANCE_ENABLED, true); try { platformPersistenceService.update(desc); } catch (final SPersistenceException e) { throw new SPlatformUpdateException("Unable to update platform status in database.", e); } } @Override public SPlatformProperties getSPlatformProperties() { return sPlatformProperties; } @Override public void updatePlatform(final EntityUpdateDescriptor descriptor) throws SPlatformUpdateException { try { recorder.recordUpdate(UpdateRecord.buildSetFields(getPlatform(), descriptor), PLATFORM); } catch (final SRecorderException | SPlatformNotFoundException e) { throw new SPlatformUpdateException("Problem while updating platform: ", e); } } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/model/SPlatform.java ================================================ /** * 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.platform.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; import org.hibernate.annotations.Type; @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "platform") public class SPlatform implements PersistentObject { public static final String CREATED_BY = "createdBy"; public static final String CREATED = "created"; public static final String ID = "id"; public static final String INITIAL_VERSION = "initialVersion"; public static final String PREVIOUS_VERSION = "previousVersion"; public static final String VERSION = "version"; public static final String INFORMATION = "information"; public static final String APPLICATION_VERSION = "applicationVersion"; public static final String MAINTENANCE_MESSAGE = "maintenanceMessage"; public static final String MAINTENANCE_MESSAGE_ACTIVE = "maintenanceMessageActive"; public static final String MAINTENANCE_ENABLED = "maintenanceEnabled"; public static final String PAUSED = "PAUSED"; private static final String RESUMED = "RESUMED"; @Id private long id; private long created; @Column(name = "created_by") private String createdBy; @Column(name = "initial_bonita_version") private String initialBonitaVersion; @Column(name = "version") private String dbSchemaVersion; @Type(type = "materialized_clob") private String information; @Column(name = "application_version") private String applicationVersion; @Column(name = "maintenance_message") private String maintenanceMessage; @Column(name = "maintenance_message_active") private boolean maintenanceMessageActive; @Column(name = "maintenance_enabled") private boolean maintenanceEnabled; public SPlatform(final String dbSchemaVersion, final String initialBonitaVersion, final String applicationVersion, final String maintenanceMessage, final boolean maintenanceMessageActive, final String createdBy, final long created, final boolean maintenanceEnabled) { this.dbSchemaVersion = dbSchemaVersion; this.initialBonitaVersion = initialBonitaVersion; this.applicationVersion = applicationVersion; this.maintenanceMessage = maintenanceMessage; this.maintenanceMessageActive = maintenanceMessageActive; this.createdBy = createdBy; this.created = created; this.maintenanceEnabled = maintenanceEnabled; } public String getPausedStatus() { return this.maintenanceEnabled ? PAUSED : RESUMED; } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/model/SPlatformProperties.java ================================================ /** * 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.platform.model; /** * @author Celine Souchet */ public interface SPlatformProperties { String getPlatformVersion(); } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/model/builder/SPlatformUpdateBuilder.java ================================================ /** * Copyright (C) 2023 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.platform.model.builder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Haroun EL ALAMI */ public interface SPlatformUpdateBuilder { String APPLICATION_VERSION = "applicationVersion"; String MAINTENANCE_MESSAGE = "maintenanceMessage"; String MAINTENANCE_MESSAGE_ACTIVE = "maintenanceMessageActive"; SPlatformUpdateBuilder setApplicationVersion(String version); SPlatformUpdateBuilder setMaintenanceMessageActive(boolean state); SPlatformUpdateBuilder setMaintenanceMessage(String name); EntityUpdateDescriptor done(); } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/model/builder/impl/SPlatformUpdateBuilderImpl.java ================================================ /** * Copyright (C) 2023 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.platform.model.builder.impl; import lombok.Builder; import org.bonitasoft.engine.platform.model.builder.SPlatformUpdateBuilder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Haroun EL ALAMI */ @Builder public class SPlatformUpdateBuilderImpl implements SPlatformUpdateBuilder { protected final EntityUpdateDescriptor descriptor; public SPlatformUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) { this.descriptor = descriptor; } @Override public SPlatformUpdateBuilder setApplicationVersion(String version) { descriptor.addField(APPLICATION_VERSION, version); return this; } @Override public SPlatformUpdateBuilder setMaintenanceMessage(final String name) { descriptor.addField(MAINTENANCE_MESSAGE, name); return this; } @Override public SPlatformUpdateBuilder setMaintenanceMessageActive(final boolean state) { descriptor.addField(MAINTENANCE_MESSAGE_ACTIVE, state); return this; } @Override public EntityUpdateDescriptor done() { return descriptor; } } ================================================ FILE: services/bonita-platform/src/main/java/org/bonitasoft/engine/platform/model/impl/SPlatformPropertiesImpl.java ================================================ /** * 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.platform.model.impl; import java.io.IOException; import java.net.URL; import java.util.Properties; import org.bonitasoft.engine.commons.io.PropertiesManager; import org.bonitasoft.engine.platform.model.SPlatformProperties; /** * @author Celine Souchet */ public class SPlatformPropertiesImpl implements SPlatformProperties { private final String platformVersion; public SPlatformPropertiesImpl() throws IOException { final URL resource = SPlatformPropertiesImpl.class.getResource("platform.properties"); final Properties properties = PropertiesManager.getProperties(resource); platformVersion = (String) properties.get("version"); } public SPlatformPropertiesImpl(String platformVersion) { this.platformVersion = platformVersion; } @Override public String getPlatformVersion() { return platformVersion; } } ================================================ FILE: services/bonita-platform/src/main/resources/org/bonitasoft/engine/platform/model/impl/hibernate/platform.queries.hbm.xml ================================================ SELECT platform FROM org.bonitasoft.engine.platform.model.SPlatform AS platform ================================================ FILE: services/bonita-platform/src/main/resources/org/bonitasoft/engine/platform/model/impl/platform.properties ================================================ version ${project.version} ================================================ FILE: services/bonita-platform/src/test/java/org/bonitasoft/engine/platform/PlatformServiceImplTest.java ================================================ /** * 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.platform; import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.platform.exception.SPlatformNotFoundException; import org.bonitasoft.engine.platform.impl.PlatformServiceImpl; import org.bonitasoft.engine.platform.model.SPlatform; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.UpdateDescriptor; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class PlatformServiceImplTest { @Mock private PersistenceService persistenceService; @Mock private PlatformRetriever platformRetriever; @Mock private Recorder recorder; @InjectMocks private PlatformServiceImpl platformServiceImpl; @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public final void getPlatform_should_throw_SPlatformNotFoundException_when_platformRetriever_throws_SPlatformNotFoundException() throws SBonitaException { //given SPlatformNotFoundException exception = new SPlatformNotFoundException("Not found"); given(platformRetriever.getPlatform()).willThrow(exception); //then expectedException.expect(SPlatformNotFoundException.class); expectedException.expectMessage(equalTo("Not found")); //when platformServiceImpl.getPlatform(); } @Test public final void isPlatformCreated() throws SBonitaException { final SPlatform sPlatform = buildPlatform(); when(platformServiceImpl.getPlatform()).thenReturn(sPlatform); assertTrue(platformServiceImpl.isPlatformCreated()); } @Test public void pause_should_update_platform_state_to_PAUSED() throws SBonitaException { // when platformServiceImpl.pauseServices(); // then verify(persistenceService).update(argThat(this::updateOnlyStatus)); } private boolean updateOnlyStatus(UpdateDescriptor u) { return u.getFields().size() == 1 && u.getFields().containsKey(SPlatform.MAINTENANCE_ENABLED) && u.getFields().containsValue(true); } private SPlatform buildPlatform() { return new SPlatform("1.0", "0.5", "0.0.0", null, false, "me", 654687344687645L, false); } } ================================================ FILE: services/bonita-platform/src/test/java/org/bonitasoft/engine/platform/impl/PlatformRetrieverImplTest.java ================================================ /** * 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.platform.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.core.IsEqual.equalTo; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.platform.exception.SPlatformNotFoundException; import org.bonitasoft.engine.platform.model.SPlatform; import org.bonitasoft.engine.services.PersistenceService; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class PlatformRetrieverImplTest { @Mock private PersistenceService persistenceService; @InjectMocks private PlatformRetrieverImpl platformRetriever; @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public final void getPlatform_should_return_result_of_persistence_service() throws SBonitaException { SPlatform platform = mock(SPlatform.class); given(persistenceService.selectOne(new SelectOneDescriptor("getPlatform", null, SPlatform.class))) .willReturn(platform); //when SPlatform retrievedPlatform = platformRetriever.getPlatform(); assertThat(retrievedPlatform).isEqualTo(platform); } @Test public final void getPlatform_should_throw_SPlatformNotFoundException_when_persistence_service_returns_null() throws SBonitaException { given(persistenceService.selectOne(new SelectOneDescriptor("getPlatform", null, SPlatform.class))) .willReturn(null); //then expectedException.expect(SPlatformNotFoundException.class); expectedException.expectMessage(equalTo("No platform found")); //when platformRetriever.getPlatform(); } @Test public final void getPlatform_should_throw_SPlatformNotFoundException_when_persistence_service_throws_Exception() throws SBonitaException { SBonitaReadException exception = new SBonitaReadException("Unable to access database"); given(persistenceService.selectOne(new SelectOneDescriptor("getPlatform", null, SPlatform.class))) .willThrow(exception); //then expectedException.expect(SPlatformNotFoundException.class); expectedException.expectMessage("Unable to check if a platform already exists : "); expectedException.expectCause(equalTo(exception)); //when platformRetriever.getPlatform(); } } ================================================ FILE: services/bonita-platform-authentication/build.gradle ================================================ dependencies { api project(':services:bonita-commons') testImplementation libs.mockitoCore testImplementation libs.logback } ================================================ FILE: services/bonita-platform-authentication/src/main/java/org/bonitasoft/engine/platform/authentication/PlatformAuthenticationService.java ================================================ /** * 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.platform.authentication; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface PlatformAuthenticationService { /** * @param username * The username of the platform * @param passwordHash * The hashed password of the platform * @throws SInvalidUserException * @throws SInvalidPasswordException */ void checkUserCredentials(final String username, final String passwordHash) throws SInvalidUserException, SInvalidPasswordException; } ================================================ FILE: services/bonita-platform-authentication/src/main/java/org/bonitasoft/engine/platform/authentication/SInvalidPasswordException.java ================================================ /** * 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.platform.authentication; /** * @author Elias Ricken de Medeiros */ public class SInvalidPasswordException extends SPlatformAuthenticationException { private static final long serialVersionUID = 2784260274211036580L; public SInvalidPasswordException(final String message) { super(message); } public SInvalidPasswordException(final String message, final Throwable cause) { super(message, cause); } public SInvalidPasswordException(final Object... arguments) { super(arguments); } public SInvalidPasswordException(final Throwable cause, final Object... arguments) { super(cause, arguments); } public SInvalidPasswordException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-platform-authentication/src/main/java/org/bonitasoft/engine/platform/authentication/SInvalidUserException.java ================================================ /** * 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.platform.authentication; /** * @author Elias Ricken de Medeiros */ public class SInvalidUserException extends SPlatformAuthenticationException { private static final long serialVersionUID = 2784260274211036580L; public SInvalidUserException(final String message) { super(message); } public SInvalidUserException(final String message, final Throwable cause) { super(message, cause); } public SInvalidUserException(final Object... arguments) { super(arguments); } public SInvalidUserException(final Throwable cause, final Object... arguments) { super(cause, arguments); } public SInvalidUserException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-platform-authentication/src/main/java/org/bonitasoft/engine/platform/authentication/SPlatformAuthenticationException.java ================================================ /** * 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.platform.authentication; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public abstract class SPlatformAuthenticationException extends SBonitaException { private static final long serialVersionUID = 7204454677855421061L; public SPlatformAuthenticationException(final String message) { super(message); } public SPlatformAuthenticationException(final String message, final Throwable cause) { super(message, cause); } public SPlatformAuthenticationException(final Object... arguments) { super(arguments); } public SPlatformAuthenticationException(final Throwable cause, final Object... arguments) { super(cause, arguments); } public SPlatformAuthenticationException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-platform-authentication/src/main/java/org/bonitasoft/engine/platform/authentication/impl/PlatformAuthenticationServiceImpl.java ================================================ /** * 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.platform.authentication.impl; import org.bonitasoft.engine.commons.LogUtil; import org.bonitasoft.engine.platform.authentication.PlatformAuthenticationService; import org.bonitasoft.engine.platform.authentication.SInvalidPasswordException; import org.bonitasoft.engine.platform.authentication.SInvalidUserException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Elias Ricken de Medeiros */ public class PlatformAuthenticationServiceImpl implements PlatformAuthenticationService { private Logger logger = LoggerFactory.getLogger(PlatformAuthenticationServiceImpl.class); private static final String USERNAME = "platformAdmin"; private static final String PASSWORD = "platform"; public PlatformAuthenticationServiceImpl() { super(); } @Override public void checkUserCredentials(final String username, final String password) throws SInvalidUserException, SInvalidPasswordException { final String methodName = "checkUserCredentials"; if (logger.isTraceEnabled()) { logger.trace( LogUtil.getLogBeforeMethod(this.getClass(), methodName)); } // FIXME read user and password from a configuration file if (!USERNAME.equals(username)) { logOnExceptionMethod(username, methodName); throw new SInvalidUserException("Invalid user : " + username); } if (!PASSWORD.equals(password)) { logOnExceptionMethod(username, methodName); throw new SInvalidPasswordException("Invalid password"); } if (logger.isTraceEnabled()) { logger.trace( LogUtil.getLogAfterMethod(this.getClass(), methodName)); } } private void logOnExceptionMethod(final String username, final String methodName) { if (logger.isTraceEnabled()) { logger.trace( LogUtil.getLogOnExceptionMethod(this.getClass(), methodName, "Invalid user : " + username)); } } } ================================================ FILE: services/bonita-platform-authentication/src/test/java/org/bonitasoft/engine/platform/authentication/impl/PlatformAuthenticationServiceImplTest.java ================================================ /** * 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.platform.authentication.impl; import org.bonitasoft.engine.platform.authentication.SInvalidPasswordException; import org.bonitasoft.engine.platform.authentication.SInvalidUserException; import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.MockitoAnnotations; /** * @author Celine Souchet */ public class PlatformAuthenticationServiceImplTest { @InjectMocks private PlatformAuthenticationServiceImpl platformAuthenticationServiceImpl; @Before public void setUp() { MockitoAnnotations.initMocks(this); } /** * Test method for * {@link org.bonitasoft.engine.platform.authentication.impl.PlatformAuthenticationServiceImpl#checkUserCredentials(java.lang.String, java.lang.String)}. */ @Test public final void checkUserCredentials() throws SInvalidUserException, SInvalidPasswordException { platformAuthenticationServiceImpl.checkUserCredentials("platformAdmin", "platform"); } @Test(expected = SInvalidUserException.class) public final void checkUserCredentialsWithBadUserName() throws SInvalidUserException, SInvalidPasswordException { platformAuthenticationServiceImpl.checkUserCredentials("plop", "platform"); } @Test(expected = SInvalidPasswordException.class) public final void checkUserCredentialsWithBadPassword() throws SInvalidUserException, SInvalidPasswordException { platformAuthenticationServiceImpl.checkUserCredentials("platformAdmin", "plop"); } } ================================================ FILE: services/bonita-platform-command/build.gradle ================================================ dependencies { api project(':services:bonita-log') api project(':services:bonita-commons') api project(':services:bonita-persistence') testImplementation libs.mockitoCore testImplementation libs.logback annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/PlatformCommandService.java ================================================ /** * 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.platform.command; import java.util.List; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.platform.command.model.SPlatformCommand; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Zhang Bole * @author Emmanuel Duchastenier */ public interface PlatformCommandService { /** * Create a sPlatformCommand * * @param command * @throws SPlatformCommandAlreadyExistsException * occurs when the sPlatformCommand has already been taken * @throws SPlatformCommandCreationException * occurs when an exception is thrown during sPlatformCommand creation * @throws SPlatformCommandGettingException * occurs when an exception is thrown during getting sPlatformCommand * @since 6.0 */ void create(SPlatformCommand command) throws SPlatformCommandAlreadyExistsException, SPlatformCommandCreationException, SPlatformCommandGettingException; /** * Delete a sPlatformCommand from its name * * @param name * the platform name * @throws SPlatformCommandNotFoundException * occurs when the identifier does not refer to an existing sPlatformCommand * @throws SPlatformCommandDeletionException * occurs when an exception is thrown during sPlatformCommand creation * @throws SPlatformCommandGettingException * occurs when an exception is thrown during getting sPlatformCommand * @since 6.0 */ void delete(String name) throws SPlatformCommandNotFoundException, SPlatformCommandDeletionException, SPlatformCommandGettingException; /** * Delete all sPlatformCommands * * @throws SPlatformCommandDeletionException * occurs when an exception is thrown during sPlatformCommand creation * @since 6.0 */ void deleteAll() throws SPlatformCommandDeletionException; /** * Get sPlatformCommand by its name * * @param name * @return an entity of sPlatformCommand * @throws SPlatformCommandNotFoundException * occurs when the identifier does not refer to an existing sPlatformCommand * @throws SPlatformCommandGettingException * occurs when an exception is thrown during getting sPlatformCommand * @since 6.0 */ SPlatformCommand getPlatformCommand(String name) throws SPlatformCommandNotFoundException, SPlatformCommandGettingException; /** * Get the sPlatformCommand having the given value for the given int index * * @param queryOptions * criteria * @return a list of sPlatformCommand * @throws SPlatformCommandGettingException * occurs when an exception is thrown during getting sPlatformCommand * @since 6.0 */ List getPlatformCommands(QueryOptions queryOptions) throws SPlatformCommandGettingException; /** * Update a sPlatformCommand with given sPlatformCommand and new content. * * @param command * @param updateDescriptor * @throws SPlatformCommandNotFoundException * occurs when the identifier does not refer to an existing sPlatformCommand * @throws SPlatformCommandUpdateException * occurs when an exception is thrown during sPlatformCommand update * @since 6.0 */ void update(SPlatformCommand command, EntityUpdateDescriptor updateDescriptor) throws SPlatformCommandNotFoundException, SPlatformCommandUpdateException; } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/SPlatformCommandAlreadyExistsException.java ================================================ /** * 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.platform.command; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Zhang Bole */ public class SPlatformCommandAlreadyExistsException extends SBonitaException { private static final long serialVersionUID = -3017980604615886777L; public SPlatformCommandAlreadyExistsException(final String message, final Throwable cause) { super(message, cause); } public SPlatformCommandAlreadyExistsException(final String message) { super(message); } public SPlatformCommandAlreadyExistsException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/SPlatformCommandCreationException.java ================================================ /** * 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.platform.command; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Zhang Bole */ public class SPlatformCommandCreationException extends SBonitaException { private static final long serialVersionUID = 634050853254691398L; public SPlatformCommandCreationException(final String message, final Throwable cause) { super(message, cause); } public SPlatformCommandCreationException(final String message) { super(message); } public SPlatformCommandCreationException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/SPlatformCommandDeletionException.java ================================================ /** * 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.platform.command; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Zhang Bole */ public class SPlatformCommandDeletionException extends SBonitaException { private static final long serialVersionUID = -1160779784480102515L; public SPlatformCommandDeletionException(final String message, final Throwable cause) { super(message, cause); } public SPlatformCommandDeletionException(final String message) { super(message); } public SPlatformCommandDeletionException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/SPlatformCommandGettingException.java ================================================ /** * 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.platform.command; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Zhang Bole */ public class SPlatformCommandGettingException extends SBonitaException { private static final long serialVersionUID = -4051373026112211800L; public SPlatformCommandGettingException(final String message, final Throwable cause) { super(message, cause); } public SPlatformCommandGettingException(final String message) { super(message); } public SPlatformCommandGettingException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/SPlatformCommandNotFoundException.java ================================================ /** * 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.platform.command; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Zhang Bole */ public class SPlatformCommandNotFoundException extends SBonitaException { private static final long serialVersionUID = -3017980604615886777L; public SPlatformCommandNotFoundException(final String message, final Throwable cause) { super(message, cause); } public SPlatformCommandNotFoundException(final String message) { super(message); } public SPlatformCommandNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/SPlatformCommandUpdateException.java ================================================ /** * 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.platform.command; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Zhang Bole */ public class SPlatformCommandUpdateException extends SBonitaException { private static final long serialVersionUID = 8667781514957065049L; public SPlatformCommandUpdateException(final String message, final Throwable cause) { super(message, cause); } public SPlatformCommandUpdateException(final String message) { super(message); } public SPlatformCommandUpdateException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/impl/PlatformCommandServiceImpl.java ================================================ /** * 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.platform.command.impl; import java.util.Collections; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.NullCheckingUtil; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.platform.command.PlatformCommandService; import org.bonitasoft.engine.platform.command.SPlatformCommandAlreadyExistsException; import org.bonitasoft.engine.platform.command.SPlatformCommandCreationException; import org.bonitasoft.engine.platform.command.SPlatformCommandDeletionException; import org.bonitasoft.engine.platform.command.SPlatformCommandGettingException; import org.bonitasoft.engine.platform.command.SPlatformCommandNotFoundException; import org.bonitasoft.engine.platform.command.SPlatformCommandUpdateException; import org.bonitasoft.engine.platform.command.model.SPlatformCommand; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.SPersistenceException; import org.bonitasoft.engine.services.UpdateDescriptor; /** * @author Zhang Bole * @author Matthieu Chaffotte */ @Slf4j public class PlatformCommandServiceImpl implements PlatformCommandService { private final PersistenceService platformPersistenceService; public PlatformCommandServiceImpl(final PersistenceService platformPersistenceService) { super(); this.platformPersistenceService = platformPersistenceService; } @Override public void create(final SPlatformCommand platformCommand) throws SPlatformCommandAlreadyExistsException, SPlatformCommandCreationException, SPlatformCommandGettingException { NullCheckingUtil.checkArgsNotNull(platformCommand); SPlatformCommand existedPlatformCommand = null; try { existedPlatformCommand = getPlatformCommand(platformCommand.getName()); } catch (final SPlatformCommandNotFoundException ignored) { } finally { if (existedPlatformCommand != null) { throw new SPlatformCommandAlreadyExistsException("platformCommand already existed"); } } try { platformPersistenceService.insert(platformCommand); } catch (final SPersistenceException pe) { throw new SPlatformCommandCreationException(pe); } } public void deletePlatformCommand(final SPlatformCommand sPlatformCommand) throws SPlatformCommandDeletionException { try { platformPersistenceService.delete(sPlatformCommand); } catch (final SPersistenceException pe) { throw new SPlatformCommandDeletionException(pe); } } @Override public void delete(final String platformCommandName) throws SPlatformCommandNotFoundException, SPlatformCommandDeletionException, SPlatformCommandGettingException { final SPlatformCommand sPlatformCommand = getPlatformCommand(platformCommandName); deletePlatformCommand(sPlatformCommand); } @Override public void deleteAll() throws SPlatformCommandDeletionException { final QueryOptions queryOptions = new QueryOptions(0, 100, SPlatformCommand.class, "id", OrderByType.ASC); List sPlatformCommands = null; do { try { sPlatformCommands = getPlatformCommands(queryOptions); } catch (final SPlatformCommandGettingException e) { throw new SPlatformCommandDeletionException(e); } for (final SPlatformCommand sPlatformCommand : sPlatformCommands) { deletePlatformCommand(sPlatformCommand); } } while (sPlatformCommands.size() == queryOptions.getNumberOfResults()); } @Override public List getPlatformCommands(final QueryOptions queryOptions) throws SPlatformCommandGettingException { final Map parameters = Collections.emptyMap(); try { return platformPersistenceService .selectList(new SelectListDescriptor( "getPlatformCommands", parameters, SPlatformCommand.class, queryOptions)); } catch (final SBonitaReadException bre) { throw new SPlatformCommandGettingException(bre); } } @Override public SPlatformCommand getPlatformCommand(final String platformCommandName) throws SPlatformCommandNotFoundException, SPlatformCommandGettingException { final Map parameters = Collections.singletonMap("name", platformCommandName); try { final SPlatformCommand sPlatformCommand = platformPersistenceService .selectOne(new SelectOneDescriptor( "getPlatformCommandByName", parameters, SPlatformCommand.class)); if (sPlatformCommand == null) { throw new SPlatformCommandNotFoundException( "No platformCommand exists using name: " + platformCommandName); } return sPlatformCommand; } catch (final SBonitaReadException bre) { throw new SPlatformCommandGettingException(bre); } } @Override public void update(final SPlatformCommand platformCommand, final EntityUpdateDescriptor updateDescriptor) throws SPlatformCommandUpdateException { final UpdateDescriptor desc = new UpdateDescriptor(platformCommand); desc.addFields(updateDescriptor.getFields()); try { platformPersistenceService.update(desc); } catch (final SPersistenceException pe) { throw new SPlatformCommandUpdateException(pe); } } } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/SPlatformCommand.java ================================================ /** * 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.platform.command.model; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Zhang Bole */ @Data @Builder @NoArgsConstructor @AllArgsConstructor @Entity @Table(name = "platformCommand") public class SPlatformCommand implements PersistentObject { public static final String ID = "id"; public static final String NAME = "name"; public static final String DESCRIPTION = "description"; public static final String IMPLEMENTATION = "implementation"; @Id private long id; private String name; private String description; private String implementation; public SPlatformCommand(final String name, final String description, final String implementation) { super(); this.name = name; this.description = description; this.implementation = implementation; } } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/SPlatformCommandCriterion.java ================================================ /** * 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.platform.command.model; /** * @author Zhang Bole */ public enum SPlatformCommandCriterion { NAME_ASC, NAME_DESC; } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/SPlatformCommandLogBuilder.java ================================================ /** * 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.platform.command.model; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; /** * @author Zhang Bole */ public interface SPlatformCommandLogBuilder extends SPersistenceLogBuilder, HasCRUDEAction { } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/SPlatformCommandLogBuilderFactory.java ================================================ /** * 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.platform.command.model; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEActionFactory; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; /** * @author Zhang Bole */ public interface SPlatformCommandLogBuilderFactory extends SPersistenceLogBuilderFactory, HasCRUDEActionFactory { SPlatformCommandLogBuilder createNewInstance(); } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/SPlatformCommandUpdateBuilder.java ================================================ /** * 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.platform.command.model; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Zhang Bole */ public interface SPlatformCommandUpdateBuilder { SPlatformCommandUpdateBuilder updateName(String name); SPlatformCommandUpdateBuilder updateDescription(String description); EntityUpdateDescriptor done(); } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/SPlatformCommandUpdateBuilderFactory.java ================================================ /** * 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.platform.command.model; /** * @author Zhang Bole */ public interface SPlatformCommandUpdateBuilderFactory { SPlatformCommandUpdateBuilder createNewInstance(); } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/impl/SPlatformCommandLogBuilderFactoryImpl.java ================================================ /** * 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.platform.command.model.impl; import org.bonitasoft.engine.platform.command.model.SPlatformCommandLogBuilder; import org.bonitasoft.engine.platform.command.model.SPlatformCommandLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Zhang Bole */ public class SPlatformCommandLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SPlatformCommandLogBuilderFactory { @Override public SPlatformCommandLogBuilder createNewInstance() { return new SPlatformCommandLogBuilderImpl(); } @Override public String getObjectIdKey() { return SPlatformCommandLogIndexesMapper.COMMAND_INDEX_NAME; } } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/impl/SPlatformCommandLogBuilderImpl.java ================================================ /** * 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.platform.command.model.impl; import org.bonitasoft.engine.platform.command.model.SPlatformCommandLogBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Zhang Bole */ public class SPlatformCommandLogBuilderImpl extends CRUDELogBuilder implements SPlatformCommandLogBuilder { @Override public SPersistenceLogBuilder objectId(final long objectId) { this.queriableLogBuilder.numericIndex(SPlatformCommandLogIndexesMapper.COMMAND_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return "COMMAND"; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL) { if (log.getNumericIndex(SPlatformCommandLogIndexesMapper.COMMAND_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + "Category Id"); } } } } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/impl/SPlatformCommandLogIndexesMapper.java ================================================ /** * 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.platform.command.model.impl; /** * @author Zhang Bole * @author Matthieu Chaffotte */ public class SPlatformCommandLogIndexesMapper { public static final int COMMAND_INDEX = 0; public static final String COMMAND_INDEX_NAME = "numericIndex1"; } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/impl/SPlatformCommandUpdateBuilderFactoryImpl.java ================================================ /** * 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.platform.command.model.impl; import org.bonitasoft.engine.platform.command.model.SPlatformCommandUpdateBuilder; import org.bonitasoft.engine.platform.command.model.SPlatformCommandUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Zhang Bole */ public class SPlatformCommandUpdateBuilderFactoryImpl implements SPlatformCommandUpdateBuilderFactory { public SPlatformCommandUpdateBuilder createNewInstance() { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); return new SPlatformCommandUpdateBuilderImpl(descriptor); } } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/model/impl/SPlatformCommandUpdateBuilderImpl.java ================================================ /** * 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.platform.command.model.impl; import org.bonitasoft.engine.platform.command.model.SPlatformCommand; import org.bonitasoft.engine.platform.command.model.SPlatformCommandUpdateBuilder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Zhang Bole */ public class SPlatformCommandUpdateBuilderImpl implements SPlatformCommandUpdateBuilder { private final EntityUpdateDescriptor descriptor; public SPlatformCommandUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) { super(); this.descriptor = descriptor; } @Override public SPlatformCommandUpdateBuilder updateName(final String name) { this.descriptor.addField(SPlatformCommand.NAME, name); return this; } @Override public SPlatformCommandUpdateBuilder updateDescription(final String description) { this.descriptor.addField(SPlatformCommand.DESCRIPTION, description); return this; } @Override public EntityUpdateDescriptor done() { return this.descriptor; } } ================================================ FILE: services/bonita-platform-command/src/main/java/org/bonitasoft/engine/platform/command/recorder/SelectDescriptorBuilder.java ================================================ /** * 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.platform.command.recorder; import java.util.Collections; import java.util.Map; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.platform.command.model.SPlatformCommand; /** * @author Bole Zhang */ public class SelectDescriptorBuilder { public static SelectOneDescriptor getPlatformCommandByName(final String platformCommandName) { final Map parameters = Collections.singletonMap("name", (Object) platformCommandName); return new SelectOneDescriptor("getPlatformCommandByName", parameters, SPlatformCommand.class); } } ================================================ FILE: services/bonita-platform-command/src/main/resources/org/bonitasoft/engine/platform/command/model/impl/hibernate/platformCommand.queries.hbm.xml ================================================ SELECT platformCommand FROM org.bonitasoft.engine.platform.command.model.SPlatformCommand AS platformCommand WHERE platformCommand.name = :name SELECT platformCommand FROM org.bonitasoft.engine.platform.command.model.SPlatformCommand AS platformCommand ================================================ FILE: services/bonita-platform-command/src/test/java/org/bonitasoft/engine/platform/command/impl/PlatformCommandServiceImplTest.java ================================================ /** * 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.platform.command.impl; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.List; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.platform.command.SPlatformCommandGettingException; import org.bonitasoft.engine.platform.command.SPlatformCommandNotFoundException; import org.bonitasoft.engine.platform.command.model.SPlatformCommand; import org.bonitasoft.engine.services.PersistenceService; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * @author Celine Souchet */ public class PlatformCommandServiceImplTest { @Mock private PersistenceService persistenceService; @InjectMocks private PlatformCommandServiceImpl platformCommandServiceImpl; @Before public void setUp() { MockitoAnnotations.initMocks(this); } /** * Test method for * {@link org.bonitasoft.engine.platform.command.impl.PlatformCommandServiceImpl#getPlatformCommand(java.lang.String)}. */ @Test public final void getPlatformCommandByName() throws SBonitaReadException, SPlatformCommandNotFoundException, SPlatformCommandGettingException { final SPlatformCommand sPlatformCommand = mock(SPlatformCommand.class); when(persistenceService.selectOne(ArgumentMatchers.> any())) .thenReturn(sPlatformCommand); Assert.assertEquals(sPlatformCommand, platformCommandServiceImpl.getPlatformCommand("name")); } @Test(expected = SPlatformCommandNotFoundException.class) public final void getPlatformCommandByNameNotExists() throws SBonitaReadException, SPlatformCommandNotFoundException, SPlatformCommandGettingException { when(persistenceService.selectOne(ArgumentMatchers.> any())) .thenReturn(null); platformCommandServiceImpl.getPlatformCommand("name"); } @Test(expected = SPlatformCommandGettingException.class) public final void getPlatformCommandByNameThrowException() throws SBonitaReadException, SPlatformCommandNotFoundException, SPlatformCommandGettingException { when(persistenceService.selectOne(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); platformCommandServiceImpl.getPlatformCommand("name"); } /** * Test method for * {@link org.bonitasoft.engine.platform.command.impl.PlatformCommandServiceImpl#getPlatformCommands(org.bonitasoft.engine.persistence.QueryOptions)}. */ @Test public final void getPlatformCommandsWithOptions() throws SBonitaReadException, SPlatformCommandGettingException { final List sPlatformCommands = new ArrayList(); sPlatformCommands.add(mock(SPlatformCommand.class)); final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(sPlatformCommands); Assert.assertEquals(sPlatformCommands, platformCommandServiceImpl.getPlatformCommands(options)); } @Test(expected = SPlatformCommandGettingException.class) public final void getPlatformCommandsWithOptionsThrowException() throws SBonitaReadException, SPlatformCommandGettingException { final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.selectList(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); platformCommandServiceImpl.getPlatformCommands(options); } } ================================================ FILE: services/bonita-platform-session/build.gradle ================================================ dependencies { api project(':services:bonita-commons') api project(':services:bonita-builder') } ================================================ FILE: services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/PlatformSessionProvider.java ================================================ /** * 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.platform.session; import org.bonitasoft.engine.platform.session.model.SPlatformSession; /** * @author Baptiste Mesta */ public interface PlatformSessionProvider { void addSession(SPlatformSession session) throws SSessionAlreadyExistsException; void removeSession(long sessionId) throws SSessionNotFoundException; SPlatformSession getSession(long sessionId) throws SSessionNotFoundException; void updateSession(SPlatformSession session) throws SSessionNotFoundException; } ================================================ FILE: services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/PlatformSessionService.java ================================================ /** * 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.platform.session; import org.bonitasoft.engine.platform.session.model.SPlatformSession; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public interface PlatformSessionService { /** * Create a new session for the given user; * * @param username * user name * @return a new session * @throws SSessionException * if some error arrives while creating the session * @@since 6.0 */ SPlatformSession createSession(String username) throws SSessionException; /** * Delete a session having the given id * * @param sessionId * the session's id * @throws SSessionNotFoundException * if no session exists for the given id * @@since 6.0 */ void deleteSession(long sessionId) throws SSessionNotFoundException; /** * Verify if a session is valid * * @param sessionId * the session's id * @return true if the session is valid, false otherwise * @since 6.0 */ boolean isValid(long sessionId) throws SSessionNotFoundException; /** * Retrieve a session by its id * * @param sessionId * the session's id * @return the session associated to the given id * @throws SSessionNotFoundException * if no session exists for the given id * @since 6.0 */ SPlatformSession getSession(long sessionId) throws SSessionNotFoundException; /** * Define how long new created sessions will be valid. This does not affect already created session * * @param duration * session's duration * @since 6.0 */ void setSessionDuration(long duration); /** * Retrieve the default sessions's duration * * @return the default sessions's duration * @since 6.0 */ long getDefaultSessionDuration(); /** * Retrieve the duration of new created sessions. If no duration was specified, the default duration will be used * * @return the duration of new created sessions * @since 6.0 */ long getSessionsDuration(); /** * Update the expiration and the last update dates of the session. * * @param sessionId * the session's id * @throws SSessionException * @since 6.0 */ void renewSession(long sessionId) throws SSessionException; } ================================================ FILE: services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/SSessionAlreadyExistsException.java ================================================ /** * 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.platform.session; /** * @author Elias Ricken de Medeiros */ public class SSessionAlreadyExistsException extends SSessionException { private static final long serialVersionUID = -3954242079366628148L; public SSessionAlreadyExistsException(final String message) { super(message); } public SSessionAlreadyExistsException(final String message, final Throwable cause) { super(message, cause); } public SSessionAlreadyExistsException(final Object... arguments) { super(arguments); } public SSessionAlreadyExistsException(final Throwable cause, final Object... arguments) { super(cause, arguments); } public SSessionAlreadyExistsException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/SSessionException.java ================================================ /** * 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.platform.session; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SSessionException extends SBonitaException { private static final long serialVersionUID = 7690456826693549647L; public SSessionException(final String message) { super(message); } public SSessionException(final String message, final Throwable cause) { super(message, cause); } public SSessionException(final Object... arguments) { super(arguments); } public SSessionException(final Throwable cause, final Object... arguments) { super(cause, arguments); } public SSessionException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/SSessionNotFoundException.java ================================================ /** * 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.platform.session; /** * @author Elias Ricken de Medeiros */ public class SSessionNotFoundException extends SSessionException { private static final long serialVersionUID = -5995789561034211161L; public SSessionNotFoundException(final String message) { super(message); } public SSessionNotFoundException(final String message, final Throwable cause) { super(message, cause); } public SSessionNotFoundException(final Object... arguments) { super(arguments); } public SSessionNotFoundException(final Throwable cause, final Object... arguments) { super(cause, arguments); } public SSessionNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/impl/PlatformSessionIdGenerator.java ================================================ /** * 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.platform.session.impl; import static java.lang.Math.abs; import java.security.SecureRandom; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public class PlatformSessionIdGenerator { private static volatile SecureRandom numberGenerator = null; public static long getNextId() { SecureRandom ng = numberGenerator; if (ng == null) { numberGenerator = ng = new SecureRandom(); } return abs(ng.nextLong()); } } ================================================ FILE: services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/impl/PlatformSessionProviderImpl.java ================================================ /** * 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.platform.session.impl; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.platform.session.PlatformSessionProvider; import org.bonitasoft.engine.platform.session.SSessionAlreadyExistsException; import org.bonitasoft.engine.platform.session.SSessionNotFoundException; import org.bonitasoft.engine.platform.session.model.SPlatformSession; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ @Component @ConditionalOnSingleCandidate(PlatformSessionProvider.class) public final class PlatformSessionProviderImpl implements PlatformSessionProvider { private static final Map platformSessions; static { platformSessions = new HashMap<>(); } public PlatformSessionProviderImpl() { } @Override public synchronized void addSession(final SPlatformSession session) throws SSessionAlreadyExistsException { final long id = session.getId(); if (platformSessions.containsKey(id)) { throw new SSessionAlreadyExistsException("A session wih id \"" + id + "\" already exists"); } platformSessions.put(id, session); } @Override public void removeSession(final long sessionId) throws SSessionNotFoundException { if (!platformSessions.containsKey(sessionId)) { throw new SSessionNotFoundException("No session found with id \"" + sessionId + "\""); } platformSessions.remove(sessionId); } @Override public SPlatformSession getSession(final long sessionId) throws SSessionNotFoundException { if (!platformSessions.containsKey(sessionId)) { throw new SSessionNotFoundException("No session found with id \"" + sessionId + "\""); } return platformSessions.get(sessionId); } @Override public void updateSession(final SPlatformSession session) throws SSessionNotFoundException { final long id = session.getId(); if (!platformSessions.containsKey(id)) { throw new SSessionNotFoundException("No session found with id \"" + id + "\""); } platformSessions.put(id, session); } } ================================================ FILE: services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/impl/PlatformSessionServiceImpl.java ================================================ /** * 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.platform.session.impl; import java.util.Date; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.commons.ClassReflector; import org.bonitasoft.engine.commons.exceptions.SReflectException; import org.bonitasoft.engine.platform.session.PlatformSessionProvider; import org.bonitasoft.engine.platform.session.PlatformSessionService; import org.bonitasoft.engine.platform.session.SSessionException; import org.bonitasoft.engine.platform.session.SSessionNotFoundException; import org.bonitasoft.engine.platform.session.model.SPlatformSession; import org.bonitasoft.engine.platform.session.model.builder.SPlatformSessionBuilderFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ @Component public class PlatformSessionServiceImpl implements PlatformSessionService { private Logger logger = LoggerFactory.getLogger(PlatformSessionServiceImpl.class); private static final long DEFAULT_SESSION_DURATION = 3600000; private final PlatformSessionProvider platformSessionProvider; private long sessionDuration = DEFAULT_SESSION_DURATION; public PlatformSessionServiceImpl( PlatformSessionProvider platformSessionProvider) { this.platformSessionProvider = platformSessionProvider; } @Override public SPlatformSession createSession(final String username) throws SSessionException { final long sessionId = PlatformSessionIdGenerator.getNextId(); final long duration = getSessionsDuration(); final SPlatformSession session = BuilderFactory.get(SPlatformSessionBuilderFactory.class) .createNewInstance(sessionId, duration, username).done(); platformSessionProvider.addSession(session); return session; } @Override public void deleteSession(final long sessionId) throws SSessionNotFoundException { platformSessionProvider.removeSession(sessionId); } @Override public boolean isValid(final long sessionId) throws SSessionNotFoundException { final SPlatformSession session = platformSessionProvider.getSession(sessionId); final Date now = new Date(); return session.getExpirationDate().after(now); } @Override public SPlatformSession getSession(final long sessionId) throws SSessionNotFoundException { final SPlatformSession session = platformSessionProvider.getSession(sessionId); return BuilderFactory.get(SPlatformSessionBuilderFactory.class).copy(session); } @Override public void setSessionDuration(final long duration) { if (duration <= 0) { throw new IllegalArgumentException("The duration must be greater then 0"); } sessionDuration = duration; } @Override public long getDefaultSessionDuration() { return DEFAULT_SESSION_DURATION; } @Override public long getSessionsDuration() { return sessionDuration; } @Override public void renewSession(final long sessionId) throws SSessionException { final SPlatformSession session = getSession(sessionId); try { ClassReflector.invokeSetter(session, "setLastRenewDate", Date.class, new Date()); platformSessionProvider.updateSession(session); } catch (final SReflectException e) { throw new SSessionException(e); } } } ================================================ FILE: services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/model/SPlatformSession.java ================================================ /** * 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.platform.session.model; import java.io.Serializable; import java.util.Date; /** * @author Elias Ricken de Medeiros */ public interface SPlatformSession extends Serializable { /** * Gets the session's id * * @return */ long getId(); /** * Gets the session's creation date (GMT+0) * * @return */ Date getCreationDate(); /** * Gets the session's last renew date (GMT+0) * * @return */ Date getLastRenewDate(); /** * Gets the session's duration * * @return */ long getDuration(); /** * Gets the session's expiration date (GMT+0) * * @return */ Date getExpirationDate(); /** * Gets the user name associated to this session * * @return */ String getUserName(); /** * Gets the user id associated to this session * * @return the user Id for this session */ long getUserId(); } ================================================ FILE: services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/model/builder/SPlatformSessionBuilder.java ================================================ /** * 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.platform.session.model.builder; import java.util.Date; import org.bonitasoft.engine.platform.session.model.SPlatformSession; /** * @author Elias Ricken de Medeiros */ public interface SPlatformSessionBuilder { SPlatformSessionBuilder lastRenewDate(final Date lastRenewDate); SPlatformSession done(); } ================================================ FILE: services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/model/builder/SPlatformSessionBuilderFactory.java ================================================ /** * 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.platform.session.model.builder; import org.bonitasoft.engine.platform.session.model.SPlatformSession; /** * @author Elias Ricken de Medeiros */ public interface SPlatformSessionBuilderFactory { SPlatformSession copy(SPlatformSession session); SPlatformSessionBuilder createNewInstance(final long id, final long duration, final String username); } ================================================ FILE: services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/model/builder/impl/SPlatformSessionBuilderFactoryImpl.java ================================================ /** * 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.platform.session.model.builder.impl; import org.bonitasoft.engine.platform.session.model.SPlatformSession; import org.bonitasoft.engine.platform.session.model.builder.SPlatformSessionBuilder; import org.bonitasoft.engine.platform.session.model.builder.SPlatformSessionBuilderFactory; import org.bonitasoft.engine.platform.session.model.impl.SPlatformSessionImpl; /** * @author Elias Ricken de Medeiros */ public class SPlatformSessionBuilderFactoryImpl implements SPlatformSessionBuilderFactory { @Override public SPlatformSessionBuilder createNewInstance(final long id, final long duration, final String username) { final SPlatformSessionImpl entity = new SPlatformSessionImpl(id, username); entity.setDuration(duration); return new SPlatformSessionBuilderImpl(entity); } @Override public SPlatformSession copy(final SPlatformSession session) { return new SPlatformSessionImpl(session); } } ================================================ FILE: services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/model/builder/impl/SPlatformSessionBuilderImpl.java ================================================ /** * 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.platform.session.model.builder.impl; import java.util.Date; import org.bonitasoft.engine.platform.session.model.SPlatformSession; import org.bonitasoft.engine.platform.session.model.builder.SPlatformSessionBuilder; import org.bonitasoft.engine.platform.session.model.impl.SPlatformSessionImpl; /** * @author Elias Ricken de Medeiros */ public class SPlatformSessionBuilderImpl implements SPlatformSessionBuilder { private final SPlatformSessionImpl entity; public SPlatformSessionBuilderImpl(final SPlatformSessionImpl entity) { super(); this.entity = entity; } @Override public SPlatformSessionBuilder lastRenewDate(final Date lastRenewDate) { this.entity.setLastRenewDate(lastRenewDate); return this; } @Override public SPlatformSession done() { final Date now = new Date(); this.entity.setCreationDate(now); this.entity.setLastRenewDate(now); return this.entity; } } ================================================ FILE: services/bonita-platform-session/src/main/java/org/bonitasoft/engine/platform/session/model/impl/SPlatformSessionImpl.java ================================================ /** * 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.platform.session.model.impl; import java.util.Date; import org.bonitasoft.engine.platform.session.model.SPlatformSession; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public class SPlatformSessionImpl implements SPlatformSession { private static final long serialVersionUID = 1L; private final long id; private Date creationDate; private long duration; private Date lastRenewDate; private final String userName; private long userId; public SPlatformSessionImpl(final long id, final String username) { this.id = id; this.userName = username; } public SPlatformSessionImpl(final SPlatformSession session) { id = session.getId(); creationDate = session.getCreationDate(); duration = session.getDuration(); lastRenewDate = session.getLastRenewDate(); userName = session.getUserName(); userId = session.getUserId(); } @Override public long getId() { return id; } @Override public Date getCreationDate() { return creationDate; } @Override public Date getLastRenewDate() { return lastRenewDate; } @Override public long getDuration() { return duration; } @Override public Date getExpirationDate() { return new Date(this.lastRenewDate.getTime() + duration); } @Override public String getUserName() { return userName; } @Override public long getUserId() { return userId; } public void setLastRenewDate(final Date lastRenewDate) { this.lastRenewDate = lastRenewDate; } public void setDuration(final long duration) { this.duration = duration; } public void setCreationDate(final Date creationDate) { this.creationDate = creationDate; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (creationDate == null ? 0 : creationDate.hashCode()); result = prime * result + (int) (duration ^ duration >>> 32); result = prime * result + (int) (id ^ id >>> 32); result = prime * result + (lastRenewDate == null ? 0 : lastRenewDate.hashCode()); result = prime * result + (int) (userId ^ userId >>> 32); result = prime * result + (userName == null ? 0 : userName.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final SPlatformSessionImpl other = (SPlatformSessionImpl) obj; if (creationDate == null) { if (other.creationDate != null) { return false; } } else if (!creationDate.equals(other.creationDate)) { return false; } if (duration != other.duration) { return false; } if (id != other.id) { return false; } if (lastRenewDate == null) { if (other.lastRenewDate != null) { return false; } } else if (!lastRenewDate.equals(other.lastRenewDate)) { return false; } if (userId != other.userId) { return false; } if (userName == null) { if (other.userName != null) { return false; } } else if (!userName.equals(other.userName)) { return false; } return true; } } ================================================ FILE: services/bonita-profile/build.gradle ================================================ dependencies { api project(':services:bonita-commons') api project(':services:bonita-persistence') api project(':services:bonita-log') api project(':services:bonita-session') api project(':services:bonita-events') api project(':services:bonita-builder') api project(':services:bonita-identity') testImplementation libs.mockitoCore annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/ProfileService.java ================================================ /** * 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.profile; import java.util.List; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.profile.exception.profile.SProfileCreationException; import org.bonitasoft.engine.profile.exception.profile.SProfileDeletionException; import org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException; import org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException; import org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberCreationException; import org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberDeletionException; import org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberNotFoundException; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.profile.model.SProfileMember; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros * @author Celine Souchet */ public interface ProfileService { String PROFILE = "PROFILE"; String PROFILE_MEMBER = "PROFILE_MEMBER"; /** * Get profile by its id * * @param profileId * @return sProfile * @throws SProfileNotFoundException * occurs when the identifier does not refer to an existing sProfile * @since 6.0 */ SProfile getProfile(long profileId) throws SProfileNotFoundException; /** * Get all profiles by given ids * * @param profileIds * a list contains many profile id * @return * @throws SProfileNotFoundException * occurs when the identifier does not refer to an existing sProfile * @since 6.0 */ List getProfiles(List profileIds) throws SProfileNotFoundException; /** * Add a new profile * * @param profile * @return sProfile * @throws SProfileCreationException * occurs when an exception is thrown during sProfile creation * @since 6.0 */ SProfile createProfile(SProfile profile) throws SProfileCreationException; /** * Update profile by given profile and new content * * @param profile * @param descriptor * @return The updated profile * @throws SProfileUpdateException * occurs when an exception is thrown during sProfile update * @since 6.0 */ SProfile updateProfile(SProfile profile, EntityUpdateDescriptor descriptor) throws SProfileUpdateException; /** * Delete profile by given sProfile * * @param profile * @throws SProfileNotFoundException * occurs when the identifier does not refer to an existing sProfile * @throws SProfileDeletionException * occurs when an exception is thrown during sProfile deletion * @since 6.0 */ void deleteProfile(SProfile profile) throws SProfileNotFoundException, SProfileDeletionException, SProfileMemberDeletionException; /** * Delete profile by its id * * @param profileId * @throws SProfileNotFoundException * occurs when the identifier does not refer to an existing sProfile * @throws SProfileDeletionException * occurs when an exception is thrown during sProfile deletion * @since 6.0 */ void deleteProfile(long profileId) throws SProfileNotFoundException, SProfileDeletionException, SProfileMemberDeletionException; /** * Get all profiles of the user by userId * * @param userId * @param fromIndex * first result to be considered(>=0) * @param numberOfElements * the max number of elementss to be returned (>=0) * @param field * @param order * @return A list of sProfile * @throws SBonitaReadException * @since 6.0 */ List searchProfilesOfUser(long userId, int fromIndex, int numberOfElements, String field, OrderByType order) throws SBonitaReadException; List getProfilesOfUser(long userId) throws SBonitaReadException; /** * Add a user to exist profile * * @param profileId * the identifier of profile * @param userId * the identifier of user * @param firstName * @param lastName * @param userName * @return sProfileMember * @throws SProfileMemberCreationException * @since 6.0 */ SProfileMember addUserToProfile(long profileId, long userId, String firstName, String lastName, String userName) throws SProfileMemberCreationException; /** * Add a group to exist profile * * @param profileId * the identifier of profile * @param groupId * the identifier of group * @param groupName * @param parentPath * @return sProfileMember * @throws SProfileMemberCreationException * TODO * @since 6.0 */ SProfileMember addGroupToProfile(long profileId, long groupId, String groupName, String parentPath) throws SProfileMemberCreationException; /** * Add a role to exist profile * * @param profileId * the identifier of profile * @param roleId * the identifier of role * @param roleName * @return sProfileMember * @throws SProfileMemberCreationException * TODO * @since 6.0 */ SProfileMember addRoleToProfile(long profileId, long roleId, String roleName) throws SProfileMemberCreationException; /** * Add a role and a group to exist profile * * @param profileId * the identifier of profile * @param roleId * the identifier of role * @param groupId * the identifier of group * @param roleName * @param groupName * @param groupParentPath * @return sProfileMember * @throws SProfileMemberCreationException * TODO * @since 6.0 */ SProfileMember addRoleAndGroupToProfile(long profileId, long roleId, long groupId, String roleName, String groupName, String groupParentPath) throws SProfileMemberCreationException; /** * Get all sProfileMembers with same user having the given value for the given int index * * @param userId * the identifier of user * @param fromIndex * first result to be considered(>=0) * @param numberOfElements * the max number of emelents to be returned (>=0) * @param field * @param order * @return sProfileMember * @throws SBonitaReadException * occurs when an exception is thrown during sProfileMember creation * @since 6.0 */ List getProfileMembersOfUser(final long userId, int fromIndex, int numberOfElements, String field, OrderByType order) throws SBonitaReadException; /** * Get all sProfileMembers with same group having the given value for the given int index * * @param groupId * the identifier of group * @param fromIndex * first result to be considered(>=0) * @param numberOfElements * the max number of elements to be returned (>=0) * @param field * @param order * @return sProfileMember * @throws SBonitaReadException * occurs when an exception is thrown during sProfileMember creation * @since 6.0 */ List getProfileMembersOfGroup(final long groupId, int fromIndex, int numberOfElements, String field, OrderByType order) throws SBonitaReadException; /** * Get all sProfileMembers with same group having the given value for the given int index * * @param roleId * the identifier of role * @param fromIndex * first result to be considered(>=0) * @param numberOfElements * the max number of elements to be returned (>=0) * @param field * @param order * @return sProfileMember * @throws SBonitaReadException * occurs when an exception is thrown during sProfileMember creation * @since 6.0 */ List getProfileMembersOfRole(final long roleId, int fromIndex, int numberOfElements, String field, OrderByType order) throws SBonitaReadException; /** * Delete a profile member by its id * * @param profileMemberId * @throws SProfileMemberDeletionException * occurs when an exception is thrown during sProfileMember deletion * @throws SProfileMemberNotFoundException * occurs when the identifier does not refer to an existing sProfileMember * @since 6.0 */ void deleteProfileMember(long profileMemberId) throws SProfileMemberDeletionException, SProfileMemberNotFoundException; /** * Delete a profile member by given sProfile member * * @param profileMember * @throws SProfileMemberDeletionException * occurs when an exception is thrown during sProfileMember deletion * @since 6.0 */ void deleteProfileMember(SProfileMember profileMember) throws SProfileMemberDeletionException; /** * Get the total number of profile members * * @param querySuffix * the suffix of query string * @param countOptions * The criterion used to search profileMembers * @return the total number of profile members * @throws SBonitaReadException * @since 6.0 */ long getNumberOfProfileMembers(String querySuffix, final QueryOptions countOptions) throws SBonitaReadException; /** * Get all sProfileMember by profileId and queryOptions * * @param querySuffix * the suffix of query string * @return all sProfileMember by profileId and queryOptions * @throws SBonitaReadException * @since 6.0 */ List searchProfileMembers(String querySuffix, final QueryOptions searchOptions) throws SBonitaReadException; /** * Get the total number of sProfileMember by a list contains profileIds * * @param profileIds * @return A list of * @throws SBonitaReadException * @since 6.0 */ List getProfileMembers(List profileIds) throws SBonitaReadException; /** * Get profile by given name * * @param profileName * @return sProfile * @throws SProfileNotFoundException * occurs when the identifier does not refer to an existing sProfile * @since 6.0 */ SProfile getProfileByName(String profileName) throws SProfileNotFoundException, SBonitaReadException; /** * Get a list of profileMembers by the given profileId * * @param profileId * @return a list of profileMembers * @throws SProfileMemberNotFoundException */ List getProfileMembers(long profileId, QueryOptions queryOptions) throws SProfileMemberNotFoundException; /** * Delete all profile members for the connected tenant * * @throws SProfileMemberDeletionException * @since 6.1 */ void deleteAllProfileMembers() throws SProfileMemberDeletionException; /** * @param queryOptions * @return */ long getNumberOfProfiles(QueryOptions queryOptions) throws SBonitaReadException; /** * @param queryOptions * @return */ List searchProfiles(QueryOptions queryOptions) throws SBonitaReadException; /** * @param profile * @throws SProfileMemberDeletionException * @since 6.3.1 */ void deleteAllProfileMembersOfProfile(SProfile profile) throws SProfileMemberDeletionException; /** * @param profileMemberId * @return * @throws SProfileMemberNotFoundException * @since 6.3.1 */ SProfileMember getProfileMemberWithoutDisplayName(final long profileMemberId) throws SProfileMemberNotFoundException; /** * updates profile metaData fields lastUpdateDate and lastUpdatedBy for a given profile * * @param profileId * @throws SProfileUpdateException * when given profileId is not found */ void updateProfileMetaData(final long profileId) throws SProfileUpdateException; } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/SProfileMemberUpdateBuilder.java ================================================ /** * 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.profile.builder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Celine Souchet */ public interface SProfileMemberUpdateBuilder { SProfileMemberUpdateBuilder setGroupId(final long groupId); SProfileMemberUpdateBuilder setRoleId(final long roleId); SProfileMemberUpdateBuilder setUserId(long userId); SProfileMemberUpdateBuilder setDisplayNamePart1(String displayNamePart1); SProfileMemberUpdateBuilder setDisplayNamePart2(String displayNamePart2); SProfileMemberUpdateBuilder setDisplayNamePart3(String displayNamePart3); EntityUpdateDescriptor done(); } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/SProfileMemberUpdateBuilderFactory.java ================================================ /** * 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.profile.builder; /** * @author Celine Souchet */ public interface SProfileMemberUpdateBuilderFactory { } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/SProfileUpdateBuilder.java ================================================ /** * 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.profile.builder; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Celine Souchet */ public interface SProfileUpdateBuilder { EntityUpdateDescriptor done(); SProfileUpdateBuilder setName(String name); SProfileUpdateBuilder setDescription(String description); SProfileUpdateBuilder setIconPath(String iconPath); SProfileUpdateBuilder setLastUpdateDate(long lastUpdateDate); SProfileUpdateBuilder setLastUpdatedBy(long lastUpdatedBy); } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/SProfileUpdateBuilderFactory.java ================================================ /** * 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.profile.builder; /** * @author Celine Souchet */ public interface SProfileUpdateBuilderFactory { SProfileUpdateBuilder createNewInstance(); } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileLogBuilderFactoryImpl.java ================================================ /** * 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.profile.builder.impl; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Matthieu Chaffotte */ public class SProfileLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SPersistenceLogBuilderFactory { public static final int PROFILE_INDEX = 1; public static final String PROFILE_INDEX_NAME = "numericIndex2"; @Override public String getObjectIdKey() { return PROFILE_INDEX_NAME; } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileLogBuilderImpl.java ================================================ /** * 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.profile.builder.impl; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Matthieu Chaffotte */ public class SProfileLogBuilderImpl extends CRUDELogBuilder implements SPersistenceLogBuilder { private static final String PREFIX = "PROFILE"; @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(SProfileLogBuilderFactoryImpl.PROFILE_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return PREFIX; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(SProfileLogBuilderFactoryImpl.PROFILE_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + "Profile id"); } } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileMemberLogBuilderFactoryImpl.java ================================================ /** * 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.profile.builder.impl; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilderFactory; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilderFactory; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public class SProfileMemberLogBuilderFactoryImpl extends CRUDELogBuilderFactory implements SPersistenceLogBuilderFactory { public static final String PROFILE_MEMBER_INDEX_NAME = "numericIndex3"; // TODO: add index for user, group and role @Override public String getObjectIdKey() { return PROFILE_MEMBER_INDEX_NAME; } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileMemberLogBuilderImpl.java ================================================ /** * 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.profile.builder.impl; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.CRUDELogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.impl.MissingMandatoryFieldsException; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ public class SProfileMemberLogBuilderImpl extends CRUDELogBuilder implements SPersistenceLogBuilder { private static final String PREFIX = "PROFILE_MEMBER"; private static final int PROFILE_MEMBER_INDEX = 2; // TODO: add index for user, group and role @Override public SPersistenceLogBuilder objectId(final long objectId) { queriableLogBuilder.numericIndex(PROFILE_MEMBER_INDEX, objectId); return this; } @Override protected String getActionTypePrefix() { return PREFIX; } @Override protected void checkExtraRules(final SQueriableLog log) { if (log.getActionStatus() != SQueriableLog.STATUS_FAIL && log.getNumericIndex(PROFILE_MEMBER_INDEX) == 0L) { throw new MissingMandatoryFieldsException("Some mandatory fields are missing: " + "Profile member id"); } } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileMemberUpdateBuilderFactoryImpl.java ================================================ /** * 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.profile.builder.impl; import org.bonitasoft.engine.profile.builder.SProfileMemberUpdateBuilder; import org.bonitasoft.engine.profile.model.SProfileMember; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Celine Souchet */ public class SProfileMemberUpdateBuilderFactoryImpl implements SProfileMemberUpdateBuilder { protected final EntityUpdateDescriptor descriptor; public SProfileMemberUpdateBuilderFactoryImpl() { descriptor = new EntityUpdateDescriptor(); } @Override public EntityUpdateDescriptor done() { return descriptor; } @Override public SProfileMemberUpdateBuilder setGroupId(final long groupId) { descriptor.addField(SProfileMember.GROUP_ID, groupId); return this; } @Override public SProfileMemberUpdateBuilder setRoleId(final long roleId) { descriptor.addField(SProfileMember.ROLE_ID, roleId); return this; } @Override public SProfileMemberUpdateBuilder setUserId(final long userId) { descriptor.addField(SProfileMember.USER_ID, userId); return this; } @Override public SProfileMemberUpdateBuilder setDisplayNamePart1(final String displayNamePart1) { descriptor.addField(SProfileMember.DISPLAY_NAME_PART1, displayNamePart1); return this; } @Override public SProfileMemberUpdateBuilder setDisplayNamePart2(final String displayNamePart2) { descriptor.addField(SProfileMember.DISPLAY_NAME_PART2, displayNamePart2); return this; } @Override public SProfileMemberUpdateBuilder setDisplayNamePart3(final String displayNamePart3) { descriptor.addField(SProfileMember.DISPLAY_NAME_PART3, displayNamePart3); return this; } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileMemberUpdateBuilderImpl.java ================================================ /** * 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.profile.builder.impl; import org.bonitasoft.engine.profile.builder.SProfileMemberUpdateBuilder; import org.bonitasoft.engine.profile.model.SProfileMember; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Celine Souchet */ public class SProfileMemberUpdateBuilderImpl implements SProfileMemberUpdateBuilder { protected final EntityUpdateDescriptor descriptor; public SProfileMemberUpdateBuilderImpl() { descriptor = new EntityUpdateDescriptor(); } @Override public EntityUpdateDescriptor done() { return descriptor; } @Override public SProfileMemberUpdateBuilder setGroupId(final long groupId) { descriptor.addField(SProfileMember.GROUP_ID, groupId); return this; } @Override public SProfileMemberUpdateBuilder setRoleId(final long roleId) { descriptor.addField(SProfileMember.ROLE_ID, roleId); return this; } @Override public SProfileMemberUpdateBuilder setUserId(final long userId) { descriptor.addField(SProfileMember.USER_ID, userId); return this; } @Override public SProfileMemberUpdateBuilder setDisplayNamePart1(final String displayNamePart1) { descriptor.addField(SProfileMember.DISPLAY_NAME_PART1, displayNamePart1); return this; } @Override public SProfileMemberUpdateBuilder setDisplayNamePart2(final String displayNamePart2) { descriptor.addField(SProfileMember.DISPLAY_NAME_PART2, displayNamePart2); return this; } @Override public SProfileMemberUpdateBuilder setDisplayNamePart3(final String displayNamePart3) { descriptor.addField(SProfileMember.DISPLAY_NAME_PART3, displayNamePart3); return this; } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileUpdateBuilderFactoryImpl.java ================================================ /** * 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.profile.builder.impl; import org.bonitasoft.engine.profile.builder.SProfileUpdateBuilder; import org.bonitasoft.engine.profile.builder.SProfileUpdateBuilderFactory; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Celine Souchet */ public class SProfileUpdateBuilderFactoryImpl implements SProfileUpdateBuilderFactory { public SProfileUpdateBuilder createNewInstance() { final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); return new SProfileUpdateBuilderImpl(descriptor); } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/builder/impl/SProfileUpdateBuilderImpl.java ================================================ /** * 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.profile.builder.impl; import org.bonitasoft.engine.profile.builder.SProfileUpdateBuilder; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; /** * @author Celine Souchet */ public class SProfileUpdateBuilderImpl implements SProfileUpdateBuilder { protected final EntityUpdateDescriptor descriptor; public SProfileUpdateBuilderImpl(final EntityUpdateDescriptor descriptor) { this.descriptor = descriptor; } @Override public SProfileUpdateBuilder setName(final String name) { descriptor.addField(SProfile.NAME, name); return this; } @Override public SProfileUpdateBuilder setDescription(final String description) { descriptor.addField(SProfile.DESCRIPTION, description); return this; } @Override public SProfileUpdateBuilder setIconPath(final String iconPath) { descriptor.addField(SProfile.ICON_PATH, iconPath); return this; } @Override public SProfileUpdateBuilder setLastUpdateDate(final long lastUpdateDate) { descriptor.addField(SProfile.LAST_UPDATE_DATE, lastUpdateDate); return this; } @Override public SProfileUpdateBuilder setLastUpdatedBy(final long lastUpdatedBy) { descriptor.addField(SProfile.LAST_UPDATED_BY, lastUpdatedBy); return this; } @Override public EntityUpdateDescriptor done() { return descriptor; } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profile/SProfileAlreadyExistsException.java ================================================ /** * 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.profile.exception.profile; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SProfileAlreadyExistsException extends SBonitaException { private static final long serialVersionUID = 2613865245278954372L; public SProfileAlreadyExistsException(final String message) { super(message); } public SProfileAlreadyExistsException(final Throwable cause) { super(cause); } public SProfileAlreadyExistsException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profile/SProfileCreationException.java ================================================ /** * 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.profile.exception.profile; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SProfileCreationException extends SBonitaException { private static final long serialVersionUID = 5352601047167402771L; public SProfileCreationException(final String message) { super(message); } public SProfileCreationException(final Throwable cause) { super(cause); } public SProfileCreationException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profile/SProfileDeletionException.java ================================================ /** * 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.profile.exception.profile; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SProfileDeletionException extends SBonitaException { private static final long serialVersionUID = 3465858452506324242L; public SProfileDeletionException(final String message) { super(message); } public SProfileDeletionException(final Throwable cause) { super(cause); } public SProfileDeletionException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profile/SProfileException.java ================================================ /** * 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.profile.exception.profile; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SProfileException extends SBonitaException { private static final long serialVersionUID = -4589920305652820696L; public SProfileException(final String message) { super(message); } public SProfileException(final Throwable cause) { super(cause); } public SProfileException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profile/SProfileGettingException.java ================================================ /** * 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.profile.exception.profile; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SProfileGettingException extends SBonitaException { private static final long serialVersionUID = 1089348511424383086L; public SProfileGettingException(final String message) { super(message); } public SProfileGettingException(final Throwable cause) { super(cause); } public SProfileGettingException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profile/SProfileNotFoundException.java ================================================ /** * 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.profile.exception.profile; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SProfileNotFoundException extends SBonitaException { private static final long serialVersionUID = -6787110496892352279L; public SProfileNotFoundException(final String message) { super(message); } public SProfileNotFoundException(final Throwable cause) { super(cause); } public SProfileNotFoundException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profile/SProfileUpdateException.java ================================================ /** * 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.profile.exception.profile; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SProfileUpdateException extends SBonitaException { private static final long serialVersionUID = 4179176029668815474L; public SProfileUpdateException(final String message) { super(message); } public SProfileUpdateException(final Throwable cause) { super(cause); } public SProfileUpdateException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profilemember/SProfileMemberCreationException.java ================================================ /** * 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.profile.exception.profilemember; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Celine Souchet */ public class SProfileMemberCreationException extends SBonitaException { private static final long serialVersionUID = 5352601047167402771L; public SProfileMemberCreationException(final String message) { super(message); } public SProfileMemberCreationException(final Throwable cause) { super(cause); } public SProfileMemberCreationException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profilemember/SProfileMemberDeletionException.java ================================================ /** * 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.profile.exception.profilemember; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SProfileMemberDeletionException extends SBonitaException { private static final long serialVersionUID = -733208211528042662L; public SProfileMemberDeletionException(final String message) { super(message); } public SProfileMemberDeletionException(final Throwable cause) { super(cause); } public SProfileMemberDeletionException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profilemember/SProfileMemberException.java ================================================ /** * 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.profile.exception.profilemember; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SProfileMemberException extends SBonitaException { private static final long serialVersionUID = -4589920305652820696L; public SProfileMemberException(final String message) { super(message); } public SProfileMemberException(final Throwable cause) { super(cause); } public SProfileMemberException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/exception/profilemember/SProfileMemberNotFoundException.java ================================================ /** * 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.profile.exception.profilemember; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SProfileMemberNotFoundException extends SBonitaException { private static final long serialVersionUID = -5699697434595954926L; public SProfileMemberNotFoundException(final String message) { super(message); } public SProfileMemberNotFoundException(final Throwable cause) { super(cause); } public SProfileMemberNotFoundException(final String message, final Throwable cause) { super(message, cause); } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/impl/ProfileServiceImpl.java ================================================ /** * 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.profile.impl; import java.util.*; import org.bonitasoft.engine.commons.NullCheckingUtil; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.persistence.*; import org.bonitasoft.engine.profile.ProfileService; import org.bonitasoft.engine.profile.builder.impl.SProfileLogBuilderImpl; import org.bonitasoft.engine.profile.builder.impl.SProfileMemberLogBuilderImpl; import org.bonitasoft.engine.profile.exception.profile.SProfileCreationException; import org.bonitasoft.engine.profile.exception.profile.SProfileDeletionException; import org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException; import org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException; import org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberCreationException; import org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberDeletionException; import org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberNotFoundException; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.profile.model.SProfileMember; import org.bonitasoft.engine.profile.persistence.SelectDescriptorBuilder; import org.bonitasoft.engine.queriablelogger.model.SQueriableLog; import org.bonitasoft.engine.queriablelogger.model.SQueriableLogSeverity; import org.bonitasoft.engine.queriablelogger.model.builder.ActionType; import org.bonitasoft.engine.queriablelogger.model.builder.HasCRUDEAction; import org.bonitasoft.engine.queriablelogger.model.builder.SLogBuilder; import org.bonitasoft.engine.queriablelogger.model.builder.SPersistenceLogBuilder; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.*; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.QueriableLoggerService; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; /** * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros */ public class ProfileServiceImpl implements ProfileService { private final PersistenceService persistenceService; private final Recorder recorder; private final QueriableLoggerService queriableLoggerService; private final SessionService sessionService; private final ReadSessionAccessor sessionAccessor; public ProfileServiceImpl(PersistenceService persistenceService, Recorder recorder, QueriableLoggerService queriableLoggerService, ReadSessionAccessor sessionAccessor, SessionService sessionService) { super(); this.persistenceService = persistenceService; this.recorder = recorder; this.queriableLoggerService = queriableLoggerService; this.sessionAccessor = sessionAccessor; this.sessionService = sessionService; } @Override public SProfile createProfile(final SProfile profile) throws SProfileCreationException { final SProfileLogBuilderImpl logBuilder = getSProfileLog(ActionType.CREATED, "Adding a new profile"); try { recorder.recordInsert(new InsertRecord(profile), PROFILE); log(profile.getId(), SQueriableLog.STATUS_OK, logBuilder, "createProfile"); return profile; } catch (final SRecorderException re) { log(profile.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "createProfile"); throw new SProfileCreationException(re); } } @Override public SProfile getProfile(final long profileId) throws SProfileNotFoundException { try { final SelectByIdDescriptor descriptor = SelectDescriptorBuilder.getElementById(SProfile.class, profileId); final SProfile profile = persistenceService.selectById(descriptor); if (profile == null) { throw new SProfileNotFoundException("No profile exists with id: " + profileId); } return profile; } catch (final SBonitaReadException e) { throw new SProfileNotFoundException(e); } } @Override public SProfile getProfileByName(final String profileName) throws SProfileNotFoundException, SBonitaReadException { final SelectOneDescriptor descriptor = SelectDescriptorBuilder .getElementByNameDescriptor(SProfile.class, "Profile", profileName); final SProfile profile = persistenceService.selectOne(descriptor); if (profile == null) { throw new SProfileNotFoundException("No profile exists with name: " + profileName); } return profile; } @Override public List getProfiles(final List profileIds) throws SProfileNotFoundException { final List profiles = new ArrayList<>(); if (profileIds != null) { for (final Long profileId : profileIds) { final SProfile profile = getProfile(profileId); profiles.add(profile); } } return profiles; } @Override public SProfile updateProfile(final SProfile sProfile, final EntityUpdateDescriptor descriptor) throws SProfileUpdateException { NullCheckingUtil.checkArgsNotNull(sProfile); final SProfileLogBuilderImpl logBuilder = getSProfileLog(ActionType.UPDATED, "Updating profile"); try { recorder.recordUpdate(UpdateRecord.buildSetFields(sProfile, descriptor), PROFILE); log(sProfile.getId(), SQueriableLog.STATUS_OK, logBuilder, "updateProfile"); } catch (final SRecorderException re) { log(sProfile.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "updateProfile"); throw new SProfileUpdateException(re); } return sProfile; } @Override public void deleteProfile(final SProfile profile) throws SProfileDeletionException, SProfileMemberDeletionException { NullCheckingUtil.checkArgsNotNull(profile); final SProfileLogBuilderImpl logBuilder = getSProfileLog(ActionType.DELETED, "Deleting profile"); try { deleteAllProfileMembersOfProfile(profile); recorder.recordDelete(new DeleteRecord(profile), PROFILE); log(profile.getId(), SQueriableLog.STATUS_OK, logBuilder, "deleteProfile"); } catch (final SRecorderException re) { log(profile.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "deleteProfile"); throw new SProfileDeletionException(re); } } @Override public void deleteAllProfileMembersOfProfile(final SProfile profile) throws SProfileMemberDeletionException { final QueryOptions queryOptions = new QueryOptions(0, 100, SProfileMember.class, "id", OrderByType.ASC); try { List sProfileMembers; do { sProfileMembers = getProfileMembers(profile.getId(), queryOptions); for (final SProfileMember profileUser : sProfileMembers) { deleteProfileMember(profileUser); } } while (!sProfileMembers.isEmpty()); } catch (final SProfileMemberNotFoundException e) { throw new SProfileMemberDeletionException(e); } } @Override public void deleteProfile(final long profileId) throws SProfileNotFoundException, SProfileDeletionException, SProfileMemberDeletionException { final SProfile profile = getProfile(profileId); deleteProfile(profile); } private SProfileMember buildProfileMember(final long profileId, final String displayNamePart1, final String displayNamePart2, final String displayNamePart3) { return SProfileMember.builder().profileId(profileId) .displayNamePart1(displayNamePart1) .displayNamePart2(displayNamePart2) .displayNamePart1(displayNamePart3).build(); } @Override public SProfileMember addUserToProfile(final long profileId, final long userId, final String firstName, final String lastName, final String userName) throws SProfileMemberCreationException { final SProfileMember profileMember = buildProfileMember(profileId, firstName, lastName, userName); profileMember.setUserId(userId); createProfileMember(profileMember); return profileMember; } private void createProfileMember(final SProfileMember profileMember) throws SProfileMemberCreationException { final String message = "Adding a new profile member"; final SProfileMemberLogBuilderImpl logBuilder = getProfileMemberLog(ActionType.CREATED, message); try { recorder.recordInsert(new InsertRecord(profileMember), PROFILE_MEMBER); log(profileMember.getId(), SQueriableLog.STATUS_OK, logBuilder, "insertProfileMember"); } catch (final SRecorderException re) { log(profileMember.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "insertProfileMember"); throw new SProfileMemberCreationException(re); } } @Override public SProfileMember addGroupToProfile(final long profileId, final long groupId, final String groupName, final String parentPath) throws SProfileMemberCreationException { final SProfileMember profileMember = buildProfileMember(profileId, groupName, parentPath, null); profileMember.setGroupId(groupId); createProfileMember(profileMember); return profileMember; } @Override public SProfileMember addRoleToProfile(final long profileId, final long roleId, final String roleName) throws SProfileMemberCreationException { final SProfileMember profileMember = buildProfileMember(profileId, roleName, null, null); profileMember.setRoleId(roleId); createProfileMember(profileMember); return profileMember; } @Override public SProfileMember addRoleAndGroupToProfile(final long profileId, final long roleId, final long groupId, final String roleName, final String groupName, final String groupParentPath) throws SProfileMemberCreationException { final SProfileMember profileMember = buildProfileMember(profileId, roleName, groupName, groupParentPath); profileMember.setGroupId(groupId); profileMember.setRoleId(roleId); createProfileMember(profileMember); return profileMember; } @Override public void deleteProfileMember(final long profileMemberId) throws SProfileMemberDeletionException, SProfileMemberNotFoundException { final SProfileMember profileMember = getProfileMemberWithoutDisplayName(profileMemberId); deleteProfileMember(profileMember); } @Override public SProfileMember getProfileMemberWithoutDisplayName(final long profileMemberId) throws SProfileMemberNotFoundException { final SelectByIdDescriptor selectByIdDescriptor = SelectDescriptorBuilder .getProfileMemberWithoutDisplayName(profileMemberId); try { final SProfileMember profileMember = persistenceService.selectById(selectByIdDescriptor); if (profileMember == null) { throw new SProfileMemberNotFoundException(profileMemberId + " does not refer to any profile member"); } return profileMember; } catch (final SBonitaReadException bre) { throw new SProfileMemberNotFoundException(bre); } } @Override public void deleteProfileMember(final SProfileMember profileMember) throws SProfileMemberDeletionException { final String message = "Deleting profile member for userId " + profileMember.getUserId() + " with roleId " + profileMember.getRoleId() + " in groupId " + profileMember.getGroupId(); final SProfileMemberLogBuilderImpl logBuilder = getProfileMemberLog(ActionType.DELETED, message); try { recorder.recordDelete(new DeleteRecord(profileMember), PROFILE_MEMBER); log(profileMember.getId(), SQueriableLog.STATUS_OK, logBuilder, "deleteProfileMember"); } catch (final SRecorderException re) { log(profileMember.getId(), SQueriableLog.STATUS_FAIL, logBuilder, "deleteProfileMember"); throw new SProfileMemberDeletionException(re); } } @Override public List getProfileMembersOfUser(final long userId, final int fromIndex, final int numberOfElements, final String field, final OrderByType order) throws SBonitaReadException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getDirectProfileMembersOfUser( userId, field, order, fromIndex, numberOfElements); return persistenceService.selectList(descriptor); } @Override public List getProfileMembersOfGroup(final long groupId, final int fromIndex, final int numberOfElements, final String field, final OrderByType order) throws SBonitaReadException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getDirectProfileMembersOfGroup( groupId, field, order, fromIndex, numberOfElements); return persistenceService.selectList(descriptor); } @Override public List getProfileMembersOfRole(final long roleId, final int fromIndex, final int numberOfElements, final String field, final OrderByType order) throws SBonitaReadException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getDirectProfileMembersOfRole( roleId, field, order, fromIndex, numberOfElements); return persistenceService.selectList(descriptor); } @Override public List searchProfilesOfUser(final long userId, final int fromIndex, final int numberOfElements, final String field, final OrderByType order) throws SBonitaReadException { final SelectListDescriptor descriptor = SelectDescriptorBuilder.getProfilesOfUser(userId, fromIndex, numberOfElements, field, order); return persistenceService.selectList(descriptor); } @Override public List getProfilesOfUser(long userId) throws SBonitaReadException { return searchProfilesOfUser(userId, 0, Integer.MAX_VALUE, null, null); } @Override public List getProfileMembers(final long profileId, final QueryOptions queryOptions) throws SProfileMemberNotFoundException { try { final SelectListDescriptor descriptor = SelectDescriptorBuilder .getSProfileMembersWithoutDisplayName(profileId, queryOptions); return persistenceService.selectList(descriptor); } catch (final SBonitaReadException bre) { throw new SProfileMemberNotFoundException(bre); } } @Override public List searchProfileMembers(final String querySuffix, final QueryOptions queryOptions) throws SBonitaReadException { return persistenceService.searchEntity(SProfileMember.class, querySuffix, queryOptions, null); } @Override public long getNumberOfProfileMembers(final String querySuffix, final QueryOptions countOptions) throws SBonitaReadException { return persistenceService.getNumberOfEntities(SProfileMember.class, querySuffix, countOptions, null); } @Override public List getProfileMembers(final List profileIds) throws SBonitaReadException { if (profileIds == null || profileIds.isEmpty()) { return Collections.emptyList(); } final QueryOptions queryOptions = new QueryOptions(0, QueryOptions.UNLIMITED_NUMBER_OF_RESULTS, SProfileMember.class, "id", OrderByType.ASC); final Map emptyMap = Collections.singletonMap("profileIds", profileIds); return persistenceService.selectList(new SelectListDescriptor<>("getProfileMembersFromProfileIds", emptyMap, SProfileMember.class, queryOptions)); } private void log(final long objectId, final int sQueriableLogStatus, final SPersistenceLogBuilder logBuilder, final String callerMethodName) { logBuilder.actionScope(String.valueOf(objectId)); logBuilder.actionStatus(sQueriableLogStatus); logBuilder.objectId(objectId); final SQueriableLog log = logBuilder.build(); if (queriableLoggerService.isLoggable(log.getActionType(), log.getSeverity())) { queriableLoggerService.log(this.getClass().getName(), callerMethodName, log); } } @Override public void deleteAllProfileMembers() throws SProfileMemberDeletionException { try { final DeleteAllRecord record = new DeleteAllRecord(SProfileMember.class, null); recorder.recordDeleteAll(record); } catch (final SRecorderException e) { throw new SProfileMemberDeletionException("Can't delete all profile members.", e); } } @Override public long getNumberOfProfiles(final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.emptyMap(); return persistenceService.getNumberOfEntities(SProfile.class, queryOptions, parameters); } @Override public List searchProfiles(final QueryOptions queryOptions) throws SBonitaReadException { final Map parameters = Collections.emptyMap(); return persistenceService.searchEntity(SProfile.class, queryOptions, parameters); } private SProfileLogBuilderImpl getSProfileLog(final ActionType actionType, final String message) { final SProfileLogBuilderImpl logBuilder = new SProfileLogBuilderImpl(); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); return logBuilder; } private SProfileMemberLogBuilderImpl getProfileMemberLog(final ActionType actionType, final String message) { final SProfileMemberLogBuilderImpl logBuilder = new SProfileMemberLogBuilderImpl(); this.initializeLogBuilder(logBuilder, message); this.updateLog(actionType, logBuilder); return logBuilder; } private void initializeLogBuilder(final T logBuilder, final String message) { logBuilder.actionStatus(SQueriableLog.STATUS_FAIL).severity(SQueriableLogSeverity.INTERNAL).rawMessage(message); } private void updateLog(final ActionType actionType, final T logBuilder) { logBuilder.setActionType(actionType); } @Override public void updateProfileMetaData(final long profileId) throws SProfileUpdateException { long userId; try { userId = getSessionUserId(); final Map params = new HashMap<>(); params.put("lastUpdateDate", System.currentTimeMillis()); params.put("lastUpdatedBy", userId); params.put("id", profileId); persistenceService.update("updateLastUpdateProfile", params); } catch (final SBonitaException e) { throw new SProfileUpdateException(e); } } private long getSessionUserId() { return sessionService.getLoggedUserFromSession(sessionAccessor); } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/model/SProfile.java ================================================ /** * 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.profile.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @AllArgsConstructor @Builder(toBuilder = true) @Entity @Table(name = "profile") public class SProfile implements PersistentObject { public static final String PROFILE_IDS = "profileIds"; public static final String ICON_PATH = "iconPath"; public static final String DESCRIPTION = "description"; public static final String NAME = "name"; public static final String ID = "id"; public static final String IS_DEFAULT = "isDefault"; public static final String CREATION_DATE = "creationDate"; public static final String CREATED_BY = "createdBy"; public static final String LAST_UPDATE_DATE = "lastUpdateDate"; public static final String LAST_UPDATED_BY = "lastUpdatedBy"; @Id private long id; @Column private boolean isDefault; @Column private String name; @Column private String description; @Column private long creationDate; @Column private long createdBy; @Column private long lastUpdateDate; @Column private long lastUpdatedBy; public SProfile(final SProfile profile) { super(); id = profile.getId(); isDefault = profile.isDefault(); name = profile.getName(); description = profile.getDescription(); creationDate = profile.getCreationDate(); createdBy = profile.getCreatedBy(); lastUpdateDate = profile.getLastUpdateDate(); lastUpdatedBy = profile.getLastUpdatedBy(); } } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/model/SProfileMember.java ================================================ /** * 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.profile.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "profilemember") public class SProfileMember implements PersistentObject { public static final String DISPLAY_NAME_PART3 = "displayNamePart3"; public static final String DISPLAY_NAME_PART2 = "displayNamePart2"; public static final String DISPLAY_NAME_PART1 = "displayNamePart1"; public static final String ROLE_ID = "roleId"; public static final String GROUP_ID = "groupId"; public static final String USER_ID = "userId"; public static final String PROFILE_ID = "profileId"; public static final String ID = "id"; @Id private long id; @Column private long profileId; @Column @Builder.Default private long userId = -1; @Column @Builder.Default private long groupId = -1; @Column @Builder.Default private long roleId = -1; private transient String displayNamePart1; private transient String displayNamePart2; private transient String displayNamePart3; } ================================================ FILE: services/bonita-profile/src/main/java/org/bonitasoft/engine/profile/persistence/SelectDescriptorBuilder.java ================================================ /** * 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.profile.persistence; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.profile.model.SProfileMember; /** * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros */ public class SelectDescriptorBuilder { private static final String ROLE_ID = "roleId"; private static final String GROUP_ID = "groupId"; private static final String USER_ID = "userId"; private static final String PROFILE_ID = "profileId"; public static SelectByIdDescriptor getElementById(final Class clazz, final long id) { return new SelectByIdDescriptor<>(clazz, id); } public static SelectOneDescriptor getElementByNameDescriptor(final Class clazz, final String elementName, final String name) { final Map parameters = Collections.singletonMap("name", name); return new SelectOneDescriptor<>("get" + elementName + "ByName", parameters, clazz); } public static SelectListDescriptor getDirectProfileMembersOfUser(final long userId, final String field, final OrderByType order, final int fromIndex, final int numberOfElements) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, SProfileMember.class, field, order); final Map parameters = new HashMap<>(1); parameters.put(USER_ID, userId); return new SelectListDescriptor<>("getDirectProfileMembersOfUser", parameters, SProfileMember.class, queryOptions); } public static SelectListDescriptor getDirectProfileMembersOfGroup(final long groupId, final String field, final OrderByType order, final int fromIndex, final int numberOfElements) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, SProfileMember.class, field, order); final Map parameters = new HashMap<>(1); parameters.put(GROUP_ID, groupId); return new SelectListDescriptor<>("getDirectProfileMembersOfGroup", parameters, SProfileMember.class, queryOptions); } public static SelectListDescriptor getDirectProfileMembersOfRole(final long roleId, final String field, final OrderByType order, final int fromIndex, final int numberOfElements) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, SProfileMember.class, field, order); final Map parameters = new HashMap<>(1); parameters.put(ROLE_ID, roleId); return new SelectListDescriptor<>("getDirectProfileMembersOfRole", parameters, SProfileMember.class, queryOptions); } public static SelectListDescriptor getProfilesOfUser(final long userId, final int fromIndex, final int numberOfElements, final String field, final OrderByType order) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, SProfile.class, field, order); final Map parameters = Collections.singletonMap(USER_ID, userId); return new SelectListDescriptor<>("getProfilesOfUser", parameters, SProfile.class, queryOptions); } public static SelectListDescriptor getProfilesWithNavigationOfUser(long userId, int fromIndex, int numberOfElements, String field, OrderByType order) { final QueryOptions queryOptions = new QueryOptions(fromIndex, numberOfElements, SProfile.class, field, order); final Map parameters = Collections.singletonMap(USER_ID, userId); return new SelectListDescriptor<>("getProfilesWithNavigationOfUser", parameters, SProfile.class, queryOptions); } public static SelectListDescriptor getSProfileMembersWithoutDisplayName(final long profileId, final QueryOptions queryOptions) { final Map parameters = Collections.singletonMap(PROFILE_ID, profileId); return new SelectListDescriptor<>("getSProfileMembersWithoutDisplayName", parameters, SProfileMember.class, queryOptions); } public static SelectByIdDescriptor getProfileMemberWithoutDisplayName(final long profileMemberId) { return new SelectByIdDescriptor<>(SProfileMember.class, profileMemberId); } } ================================================ FILE: services/bonita-profile/src/main/resources/org/bonitasoft/engine/profile/model/impl/hibernate/profile.queries.hbm.xml ================================================ SELECT profile FROM org.bonitasoft.engine.profile.model.SProfile AS profile WHERE profile.name = :name SELECT COUNT(profile.id) FROM org.bonitasoft.engine.profile.model.SProfile AS profile SELECT profile FROM org.bonitasoft.engine.profile.model.SProfile AS profile SELECT pm FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm WHERE pm.userId = :userId SELECT pm FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm WHERE pm.groupId = :groupId SELECT pm FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm WHERE pm.roleId = :roleId SELECT pm FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm WHERE pm.profileId = :profileId SELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, user.firstName AS displayNamePart1, user.lastName AS displayNamePart2, user.userName AS displayNamePart3) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SUser AS user WHERE pm.userId = user.id SELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, user.firstName AS displayNamePart1, user.lastName AS displayNamePart2, user.userName AS displayNamePart3) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SUser AS user WHERE pm.userId = user.id SELECT COUNT(*) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SUser AS user WHERE pm.userId = user.id SELECT COUNT(*) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SUser AS user WHERE pm.userId = user.id AND pm.userId >0 SELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, group_.name AS displayNamePart1, group_.parentPath AS displayNamePart2, '' AS displayNamePart3) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE pm.groupId = group_.id AND pm.roleId = -1 SELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, group_.name AS displayNamePart1, group_.parentPath AS displayNamePart2, '' AS displayNamePart3) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE pm.groupId = group_.id AND pm.roleId = -1 SELECT COUNT(pm.id) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE pm.groupId = group_.id AND pm.roleId = -1 SELECT COUNT(pm.id) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE pm.groupId = group_.id AND pm.roleId = -1 SELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, role.name AS displayNamePart1, '' AS displayNamePart2, '' AS displayNamePart3) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SRole AS role WHERE pm.roleId = role.id AND pm.groupId = -1 SELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, role.name AS displayNamePart1, '' AS displayNamePart2, '' AS displayNamePart3) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SRole AS role WHERE pm.roleId = role.id AND pm.groupId = -1 SELECT COUNT(pm.id) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SRole AS role WHERE pm.roleId = role.id AND pm.groupId = -1 SELECT COUNT(pm.id) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SRole AS role WHERE pm.roleId = role.id AND pm.groupId = -1 SELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, role.name AS displayNamePart1, group_.name AS displayNamePart2, group_.parentPath AS displayNamePart3) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE pm.roleId = role.id AND pm.groupId = group_.id SELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, role.name AS displayNamePart1, group_.name AS displayNamePart2, group_.parentPath AS displayNamePart3) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE pm.roleId = role.id AND pm.groupId = group_.id SELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, role.name AS displayNamePart1, group_.name AS displayNamePart2, group_.parentPath AS displayNamePart3) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE pm.roleId = role.id AND pm.groupId = group_.id SELECT new org.bonitasoft.engine.profile.model.SProfileMember(pm.id, pm.profileId, pm.userId, pm.groupId, pm.roleId, role.name AS displayNamePart1, group_.name AS displayNamePart2, group_.parentPath AS displayNamePart3) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE pm.roleId = role.id AND pm.groupId = group_.id SELECT COUNT(pm.id) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE pm.roleId = role.id AND pm.groupId = group_.id SELECT COUNT(pm.id) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE pm.roleId = role.id AND pm.groupId = group_.id SELECT COUNT(pm.id) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE pm.roleId = role.id AND pm.groupId = group_.id SELECT COUNT(pm.id) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SRole AS role, org.bonitasoft.engine.identity.model.SGroup AS group_ WHERE pm.roleId = role.id AND pm.groupId = group_.id SELECT DISTINCT profile FROM org.bonitasoft.engine.profile.model.SProfile AS profile, org.bonitasoft.engine.profile.model.SProfileMember AS pm WHERE pm.profileId = profile.id AND ( (pm.userId = :userId AND pm.roleId = -1 AND pm.groupId = -1) OR pm.id IN ( SELECT pm.id FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm, org.bonitasoft.engine.identity.model.SUserMembership AS um WHERE um.userId = :userId AND ( (pm.groupId = um.groupId AND pm.roleId = -1) OR (pm.roleId = um.roleId AND pm.groupId = -1) OR (pm.groupId = um.groupId AND pm.roleId = um.roleId) ) ) ) SELECT COUNT(pm.id) FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm WHERE pm.profileId = :profileId SELECT pm FROM org.bonitasoft.engine.profile.model.SProfileMember AS pm WHERE pm.profileId IN (:profileIds) UPDATE org.bonitasoft.engine.profile.model.SProfile AS p SET p.lastUpdateDate = :lastUpdateDate, p.lastUpdatedBy = :lastUpdatedBy WHERE p.id = :id ================================================ FILE: services/bonita-profile/src/test/java/org/bonitasoft/engine/profile/impl/ProfileServiceImplForProfileTest.java ================================================ /** * 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.profile.impl; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.builder.BuilderFactory; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.profile.builder.SProfileUpdateBuilder; import org.bonitasoft.engine.profile.builder.SProfileUpdateBuilderFactory; import org.bonitasoft.engine.profile.exception.profile.SProfileCreationException; import org.bonitasoft.engine.profile.exception.profile.SProfileDeletionException; import org.bonitasoft.engine.profile.exception.profile.SProfileNotFoundException; import org.bonitasoft.engine.profile.exception.profile.SProfileUpdateException; import org.bonitasoft.engine.profile.exception.profilemember.SProfileMemberDeletionException; import org.bonitasoft.engine.profile.model.SProfile; import org.bonitasoft.engine.profile.model.SProfileMember; import org.bonitasoft.engine.profile.persistence.SelectDescriptorBuilder; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.services.QueriableLoggerService; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.session.model.SSession; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class ProfileServiceImplForProfileTest { @Mock private PersistenceService persistenceService; @Mock private QueriableLoggerService queriableLoggerService; @Mock private Recorder recorder; @Mock private SessionService sessionService; @Mock private ReadSessionAccessor readSessionAccessor; @Mock private SSession sSession; @Mock private SProfile sProfile; @InjectMocks private ProfileServiceImpl profileServiceImpl; /** * Test method for * {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#getNumberOfProfiles(org.bonitasoft.engine.persistence.QueryOptions)}. */ @Test public void getNumberOfProfilesWithOptions() throws Exception { final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.getNumberOfEntities(SProfile.class, options, Collections.emptyMap())).thenReturn(1L); assertEquals(1L, profileServiceImpl.getNumberOfProfiles(options)); } @Test(expected = SBonitaReadException.class) public void getNumberOfProfilesWithOptionsThrowException() throws Exception { final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.getNumberOfEntities(SProfile.class, options, Collections.emptyMap())) .thenThrow(new SBonitaReadException("")); profileServiceImpl.getNumberOfProfiles(options); } /** * Test method for * {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#searchProfiles(org.bonitasoft.engine.persistence.QueryOptions)}. */ @Test public void searchProfilesWithOptions() throws Exception { final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.searchEntity(SProfile.class, options, Collections.emptyMap())) .thenReturn(new ArrayList<>()); assertNotNull(profileServiceImpl.searchProfiles(options)); } @Test(expected = SBonitaReadException.class) public void searchProfilesWithOptionsThrowException() throws Exception { final QueryOptions options = new QueryOptions(0, 10); when(persistenceService.searchEntity(SProfile.class, options, Collections.emptyMap())) .thenThrow(new SBonitaReadException("")); profileServiceImpl.searchProfiles(options); } /** * Test method for {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#getProfile(long)}. */ @Test public void getProfileById() throws Exception { final SProfile sProfile = mock(SProfile.class); doReturn(sProfile).when(persistenceService).selectById(ArgumentMatchers.> any()); assertEquals(sProfile, profileServiceImpl.getProfile(1)); } @Test(expected = SProfileNotFoundException.class) public void getNoProfileById() throws Exception { when(persistenceService.selectById(ArgumentMatchers.> any())).thenReturn(null); profileServiceImpl.getProfile(1); } @Test(expected = SProfileNotFoundException.class) public void getProfileByIdThrowException() throws Exception { when(persistenceService.selectById(ArgumentMatchers.> any())) .thenThrow(new SBonitaReadException("")); profileServiceImpl.getProfile(1); } /** * Test method for {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#getProfileByName(java.lang.String)}. */ @Test public void getProfileByName() throws Exception { final String profileName = "plop"; final SProfile sProfile = mock(SProfile.class); doReturn(sProfile).when(persistenceService).selectOne(ArgumentMatchers.> any()); assertEquals(sProfile, profileServiceImpl.getProfileByName(profileName)); } @Test(expected = SProfileNotFoundException.class) public void getNoProfileByName() throws Exception { when(persistenceService.selectOne(ArgumentMatchers.> any())).thenReturn(null); profileServiceImpl.getProfileByName("plop"); } @Test(expected = SBonitaReadException.class) public void getProfileByNameThrowException() throws Exception { when(persistenceService.selectOne(any())).thenThrow(new SBonitaReadException("")); profileServiceImpl.getProfileByName("plop"); } /** * Test method for {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#getProfiles(java.util.List)}. * * @throws SProfileNotFoundException * @throws SBonitaReadException */ @Test public final void getProfiles() throws SProfileNotFoundException, SBonitaReadException { final List profileIds = new ArrayList<>(); profileIds.add(1L); profileIds.add(2L); final List sProfiles = new ArrayList<>(); final SProfile sProfile = mock(SProfile.class); sProfiles.add(sProfile); sProfiles.add(sProfile); doReturn(sProfile).when(persistenceService).selectById(ArgumentMatchers.> any()); assertEquals(sProfiles, profileServiceImpl.getProfiles(profileIds)); } @Test(expected = SProfileNotFoundException.class) public final void getNoProfiles() throws SProfileNotFoundException, SBonitaReadException { final List profileIds = new ArrayList<>(); profileIds.add(1L); doReturn(null).when(persistenceService).selectById(ArgumentMatchers.> any()); profileServiceImpl.getProfiles(profileIds); } @Test public final void getProfilesWithEmptyList() throws SProfileNotFoundException { final List profileIds = new ArrayList<>(); assertTrue(profileServiceImpl.getProfiles(profileIds).isEmpty()); } @Test public final void getProfilesWithNullList() throws SProfileNotFoundException { assertTrue(profileServiceImpl.getProfiles(null).isEmpty()); } @Test public final void getProfilesOfUser() throws SBonitaReadException { final List sProfiles = new ArrayList<>(); final SProfile sProfile = mock(SProfile.class); sProfiles.add(sProfile); doReturn(sProfiles).when(persistenceService) .selectList(ArgumentMatchers.> any()); assertEquals(sProfiles, profileServiceImpl.searchProfilesOfUser(1, 0, 10, "name", OrderByType.ASC)); } @Test public final void getNoProfilesOfUser() throws SBonitaReadException { final List sProfiles = new ArrayList<>(); doReturn(sProfiles).when(persistenceService) .selectList(ArgumentMatchers.> any()); assertEquals(sProfiles, profileServiceImpl.searchProfilesOfUser(1, 0, 10, "name", OrderByType.ASC)); } @Test(expected = SBonitaReadException.class) public final void getProfilesOfUserThrowException() throws SBonitaReadException { doThrow(new SBonitaReadException("plop")).when(persistenceService) .selectList(ArgumentMatchers.> any()); profileServiceImpl.searchProfilesOfUser(1, 0, 10, "name", OrderByType.ASC); } /** * Test method for * {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#createProfile(org.bonitasoft.engine.profile.model.SProfile)}. * * @throws SProfileCreationException */ @Test public final void createProfile() throws SProfileCreationException { final SProfile sProfile = mock(SProfile.class); doReturn(1L).when(sProfile).getId(); final SProfile result = profileServiceImpl.createProfile(sProfile); assertNotNull(result); assertEquals(sProfile, result); } @Test(expected = IllegalArgumentException.class) public final void createNullProfile() throws Exception { profileServiceImpl.createProfile(null); } @Test(expected = SProfileCreationException.class) public final void createProfileThrowException() throws SRecorderException, SProfileCreationException { final SProfile sProfile = mock(SProfile.class); doReturn(1L).when(sProfile).getId(); doThrow(new SRecorderException("plop")).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); profileServiceImpl.createProfile(sProfile); } /** * Test method for * {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#updateProfile(org.bonitasoft.engine.profile.model.SProfile, org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor)} * * @throws SBonitaReadException * @throws SRecorderException * @throws SProfileUpdateException */ @Test public final void updateProfile() throws SProfileUpdateException { final SProfile sProfile = mock(SProfile.class); doReturn(3L).when(sProfile).getId(); final SProfileUpdateBuilder sProfileUpdateBuilder = BuilderFactory.get(SProfileUpdateBuilderFactory.class) .createNewInstance(); sProfileUpdateBuilder.setDescription("newDescription").setName("newName"); final SProfile result = profileServiceImpl.updateProfile(sProfile, sProfileUpdateBuilder.done()); assertNotNull(result); assertEquals(sProfile, result); } @Test(expected = IllegalArgumentException.class) public final void updateNullProfile() throws SProfileUpdateException { final SProfileUpdateBuilder sProfileUpdateBuilder = BuilderFactory.get(SProfileUpdateBuilderFactory.class) .createNewInstance(); profileServiceImpl.updateProfile(null, sProfileUpdateBuilder.done()); } @Test(expected = IllegalArgumentException.class) public final void updateProfileWithNullDescriptor() throws SProfileUpdateException { final SProfile sProfile = mock(SProfile.class); profileServiceImpl.updateProfile(sProfile, null); } @Test(expected = SProfileUpdateException.class) public final void updateProfileThrowException() throws SRecorderException, SProfileUpdateException { final SProfile sProfile = mock(SProfile.class); doReturn(3L).when(sProfile).getId(); final SProfileUpdateBuilder sProfileUpdateBuilder = BuilderFactory.get(SProfileUpdateBuilderFactory.class) .createNewInstance(); sProfileUpdateBuilder.setDescription("newDescription").setName("newName"); doThrow(new SRecorderException("plop")).when(recorder).recordUpdate(any(UpdateRecord.class), nullable(String.class)); profileServiceImpl.updateProfile(sProfile, sProfileUpdateBuilder.done()); } /** * Test method for {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#deleteProfile(long)}. */ @Test public final void deleteProfileById() throws Exception { final SProfile sProfile = mock(SProfile.class); doReturn(3L).when(sProfile).getId(); final List sProfileMembers = new ArrayList<>(); final SProfileMember sProfileMember = mock(SProfileMember.class); doReturn(4L).when(sProfileMember).getId(); sProfileMembers.add(sProfileMember); doReturn(sProfile).when(persistenceService).selectById(ArgumentMatchers.> any()); final QueryOptions queryOptions = new QueryOptions(0, 10, SProfile.class, "id", OrderByType.ASC); doReturn(sProfileMembers).doReturn(new ArrayList()).when(persistenceService) .selectList(SelectDescriptorBuilder.getSProfileMembersWithoutDisplayName(3L, queryOptions)); profileServiceImpl.deleteProfile(1L); } @Test(expected = SProfileNotFoundException.class) public final void deleteNoProfileById() throws SBonitaReadException, SProfileDeletionException, SProfileNotFoundException, SProfileMemberDeletionException { when(persistenceService.selectById(ArgumentMatchers.> any())).thenReturn(null); profileServiceImpl.deleteProfile(1); } @Test(expected = SProfileDeletionException.class) public void deleteProfileByIdThrowException() throws Exception { final SProfile sProfile = mock(SProfile.class); doReturn(3L).when(sProfile).getId(); doReturn(sProfile).when(persistenceService).selectById(any()); doThrow(new SRecorderException("")).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); profileServiceImpl.deleteProfile(1); } @Test public final void deleteProfileByIdWithNoMember() throws SProfileNotFoundException, SProfileDeletionException, SBonitaReadException, SProfileMemberDeletionException { final SProfile sProfile = mock(SProfile.class); doReturn(3L).when(sProfile).getId(); doReturn(sProfile).when(persistenceService).selectById(ArgumentMatchers.> any()); final QueryOptions queryOptions = new QueryOptions(0, 10, SProfile.class, "id", OrderByType.ASC); doReturn(new ArrayList()).when(persistenceService).selectList( SelectDescriptorBuilder.getSProfileMembersWithoutDisplayName(3, queryOptions)); profileServiceImpl.deleteProfile(1); } /** * Test method for * {@link org.bonitasoft.engine.profile.impl.ProfileServiceImpl#deleteProfile(org.bonitasoft.engine.profile.model.SProfile)}. */ @Test public final void deleteProfileByObject() throws Exception { final SProfile sProfile = mock(SProfile.class); doReturn(3L).when(sProfile).getId(); final List sProfileMembers = new ArrayList<>(); final SProfileMember sProfileMember = mock(SProfileMember.class); doReturn(4L).when(sProfileMember).getId(); sProfileMembers.add(sProfileMember); final QueryOptions queryOptions = new QueryOptions(0, 10, SProfile.class, "id", OrderByType.ASC); doReturn(sProfileMembers).doReturn(new ArrayList()).when(persistenceService) .selectList(SelectDescriptorBuilder.getSProfileMembersWithoutDisplayName(3, queryOptions)); profileServiceImpl.deleteProfile(sProfile); } @Test(expected = IllegalArgumentException.class) public final void deleteNoProfileByObject() throws Exception { profileServiceImpl.deleteProfile(null); } @Test(expected = SProfileDeletionException.class) public void deleteProfileByObjectThrowException() throws Exception { doReturn(3L).when(sProfile).getId(); doThrow(new SRecorderException("")).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); profileServiceImpl.deleteProfile(sProfile); } @Test public final void deleteProfileByObjectWithNoMember() throws Exception { doReturn(3L).when(sProfile).getId(); final QueryOptions queryOptions = new QueryOptions(0, 10, SProfile.class, "id", OrderByType.ASC); doReturn(new ArrayList()).when(persistenceService).selectList( SelectDescriptorBuilder.getSProfileMembersWithoutDisplayName(3L, queryOptions)); profileServiceImpl.deleteProfile(sProfile); } } ================================================ FILE: services/bonita-resources/build.gradle ================================================ dependencies { api project(':services:bonita-persistence') api project(':services:bonita-commons') testImplementation libs.mockitoCore testImplementation libs.systemRules testImplementation libs.assertj testRuntimeOnly libs.logback annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/AbstractSBARResource.java ================================================ /** * 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.resources; import javax.persistence.Column; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @MappedSuperclass public class AbstractSBARResource implements PersistentObject { @Id private long id; protected String name; @Enumerated(EnumType.STRING) protected BARResourceType type; @Column(name = "process_id") protected long processDefinitionId; } ================================================ FILE: services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/AbstractSTenantResource.java ================================================ /** * 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.resources; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Baptiste Mesta */ @Data @NoArgsConstructor @MappedSuperclass public class AbstractSTenantResource implements PersistentObject { @Id private long id; protected String name; protected long lastUpdatedBy; protected long lastUpdateDate; @Enumerated(EnumType.STRING) protected TenantResourceType type; @Enumerated(EnumType.STRING) protected STenantResourceState state; public AbstractSTenantResource(String name, TenantResourceType type, long lastUpdatedBy, long lastUpdateDate, STenantResourceState state) { this.name = name; this.type = type; this.lastUpdatedBy = lastUpdatedBy; this.lastUpdateDate = lastUpdateDate; this.state = state; } } ================================================ FILE: services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/BARResourceType.java ================================================ /** * 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.resources; /** * @author Baptiste Mesta */ public enum BARResourceType { DOCUMENT, EXTERNAL, CONNECTOR, USER_FILTER } ================================================ FILE: services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/ProcessResourcesService.java ================================================ /** * 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.resources; import java.util.List; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; /** * @author Baptiste Mesta */ public interface ProcessResourcesService { String BAR_RESOURCE = "BAR_RESOURCE"; void add(long processDefinitionId, String name, BARResourceType type, byte[] content) throws SRecorderException; void update(AbstractSBARResource resource, byte[] content) throws SRecorderException; void removeAll(long processDefinitionId, BARResourceType external) throws SBonitaReadException, SRecorderException; List get(long processDefinitionId, BARResourceType type, int from, int numberOfElements) throws SBonitaReadException; long count(long processDefinitionId, BARResourceType type) throws SBonitaReadException; SBARResource get(long processDefinitionId, BARResourceType type, String name) throws SBonitaReadException; void remove(AbstractSBARResource resource) throws SRecorderException; } ================================================ FILE: services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/ProcessResourcesServiceImpl.java ================================================ /** * 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.resources; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; /** * @author Baptiste Mesta */ public class ProcessResourcesServiceImpl implements ProcessResourcesService { private final Recorder recorder; private final ReadPersistenceService persistenceService; public ProcessResourcesServiceImpl(Recorder recorder, ReadPersistenceService persistenceService) { this.recorder = recorder; this.persistenceService = persistenceService; } @Override public void add(long processDefinitionId, String name, BARResourceType type, byte[] content) throws SRecorderException { SBARResource resource = new SBARResource(name, type, processDefinitionId, content); recorder.recordInsert(new InsertRecord(resource), BAR_RESOURCE); } @Override public void update(AbstractSBARResource resource, byte[] content) throws SRecorderException { recorder.recordUpdate(UpdateRecord.buildSetFields(resource, Collections.singletonMap("content", content)), BAR_RESOURCE); } @Override public void removeAll(long processDefinitionId, BARResourceType type) throws SBonitaReadException, SRecorderException { List resources; while (!(resources = getLight(processDefinitionId, type, 0, 100)).isEmpty()) { for (SBARResourceLight resource : resources) { remove(resource); } } } public List getLight(long processDefinitionId, BARResourceType type, int from, int numberOfElements) throws SBonitaReadException { Map inputParameters = new HashMap<>(2); inputParameters.put("processDefinitionId", processDefinitionId); inputParameters.put("type", type); return persistenceService.selectList(new SelectListDescriptor("getBARResourcesLightOfType", inputParameters, SBARResourceLight.class, new QueryOptions(from, numberOfElements))); } @Override public List get(long processDefinitionId, BARResourceType type, int from, int numberOfElements) throws SBonitaReadException { Map inputParameters = new HashMap<>(2); inputParameters.put("processDefinitionId", processDefinitionId); inputParameters.put("type", type); return persistenceService.selectList( new SelectListDescriptor("getBARResourcesOfType", inputParameters, SBARResource.class, new QueryOptions(from, numberOfElements))); } @Override public long count(long processDefinitionId, BARResourceType type) throws SBonitaReadException { Map inputParameters = new HashMap<>(2); inputParameters.put("processDefinitionId", processDefinitionId); inputParameters.put("type", type); return persistenceService.selectOne( new SelectOneDescriptor("getNumberOfBARResourcesOfType", inputParameters, SBARResource.class)); } @Override public SBARResource get(long processDefinitionId, BARResourceType type, String name) throws SBonitaReadException { Map inputParameters = new HashMap<>(2); inputParameters.put("processDefinitionId", processDefinitionId); inputParameters.put("type", type); inputParameters.put("name", name); return persistenceService.selectOne( new SelectOneDescriptor("getBARResource", inputParameters, SBARResource.class)); } @Override public void remove(AbstractSBARResource resource) throws SRecorderException { recorder.recordDelete(new DeleteRecord(resource), BAR_RESOURCE); } } ================================================ FILE: services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/SBARResource.java ================================================ /** * 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.resources; import javax.persistence.Entity; import javax.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import org.hibernate.annotations.Type; /** * @author Baptiste Mesta */ @Data @ToString(callSuper = true, exclude = { "content" }) @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @Table(name = "bar_resource") public class SBARResource extends AbstractSBARResource { @Type(type = "materialized_blob") private byte[] content; public SBARResource(String name, BARResourceType type, long processDefinitionId, byte[] content) { this.name = name; this.type = type; this.processDefinitionId = processDefinitionId; this.content = content; } } ================================================ FILE: services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/SBARResourceLight.java ================================================ /** * 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.resources; import javax.persistence.Entity; import javax.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; /** * @author Baptiste Mesta */ @Data @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @Entity @Table(name = "bar_resource") public class SBARResourceLight extends AbstractSBARResource { } ================================================ FILE: services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/STenantResource.java ================================================ /** * Copyright (C) 2016 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.resources; import javax.persistence.Entity; import javax.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import org.hibernate.annotations.Type; /** * @author Baptiste Mesta */ @Data @ToString(callSuper = true, exclude = { "content" }) @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @Entity @Table(name = "tenant_resource") public class STenantResource extends AbstractSTenantResource { @Type(type = "materialized_blob") private byte[] content; public STenantResource(String name, TenantResourceType type, byte[] content, long lastUpdatedBy, long lastUpdateDate, STenantResourceState state) { super(name, type, lastUpdatedBy, lastUpdateDate, state); this.content = content; } } ================================================ FILE: services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/STenantResourceLight.java ================================================ /** * 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.resources; import javax.persistence.Entity; import javax.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; /** * @author Baptiste Mesta */ @Data @EqualsAndHashCode(callSuper = true) @NoArgsConstructor @Entity @Table(name = "tenant_resource") public class STenantResourceLight extends AbstractSTenantResource { public STenantResourceLight(String name, TenantResourceType type, long lastUpdatedBy, long lastUpdateDate, STenantResourceState state) { super(name, type, lastUpdatedBy, lastUpdateDate, state); } } ================================================ FILE: services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/STenantResourceState.java ================================================ /** * Copyright (C) 2017-2018 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.resources; /** * @author Danila Mazour */ public enum STenantResourceState { INSTALLED, INSTALLING } ================================================ FILE: services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/TenantResourceType.java ================================================ /** * Copyright (C) 2016-2018 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.resources; /** * @author Baptiste Mesta */ public enum TenantResourceType { //max 16 chars (DB) BDM, BDM_ACCESS_CTRL } ================================================ FILE: services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/TenantResourcesService.java ================================================ /** * 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.resources; import java.util.List; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.SRecorderException; /** * @author Baptiste Mesta */ public interface TenantResourcesService { String TENANT_RESOURCE = "TENANT_RESOURCE"; void add(String name, TenantResourceType type, byte[] content, long userId) throws SRecorderException; void removeAll(TenantResourceType external) throws SBonitaReadException, SRecorderException; List get(TenantResourceType type, int from, int numberOfElements) throws SBonitaReadException; long count(TenantResourceType type) throws SBonitaReadException; long count(TenantResourceType type, String name) throws SBonitaReadException; STenantResource get(TenantResourceType type, String name) throws SBonitaReadException; /** * Returns a single STenantResourceLight of the given type. This is the responsibility of the caller to only call * this method when he / she is sure that there are not more than one result of the query. * If the result is non unique, a SBonitaReadException is thrown. * * @param type the type of the resource to filter * @return the found resource if unique, null if none found * @throws SBonitaReadException if non unique result, or other Hibernate exception is issued */ STenantResourceLight getSingleLightResource(TenantResourceType type) throws SBonitaReadException; void remove(AbstractSTenantResource resource) throws SRecorderException; } ================================================ FILE: services/bonita-resources/src/main/java/org/bonitasoft/engine/resources/TenantResourcesServiceImpl.java ================================================ /** * 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.resources; import static org.bonitasoft.engine.resources.STenantResourceState.INSTALLED; import java.time.Instant; import java.util.HashMap; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.persistence.SelectOneDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; /** * @author Baptiste Mesta */ @Slf4j public class TenantResourcesServiceImpl implements TenantResourcesService { private final Recorder recorder; private final ReadPersistenceService persistenceService; public TenantResourcesServiceImpl(Recorder recorder, ReadPersistenceService persistenceService) { this.recorder = recorder; this.persistenceService = persistenceService; } @Override public void add(String name, TenantResourceType type, byte[] content, long userId) throws SRecorderException { if (content != null && content.length > 0) { STenantResource resource = new STenantResource(name, type, content, userId, Instant.now().toEpochMilli(), INSTALLED); recorder.recordInsert(new InsertRecord(resource), TENANT_RESOURCE); } else { log.warn( "Tenant resource file contains an empty file {} that will be ignored. Check that this is not a mistake.", name); } } @Override public void removeAll(TenantResourceType type) throws SBonitaReadException, SRecorderException { List resources; while (!(resources = getLight(type, 0, 100)).isEmpty()) { for (STenantResourceLight resource : resources) { remove(resource); } } } public List getLight(TenantResourceType type, int from, int numberOfElements) throws SBonitaReadException { Map inputParameters = new HashMap<>(); inputParameters.put("type", type); return persistenceService .selectList(new SelectListDescriptor<>("getTenantResourcesLightOfType", inputParameters, STenantResourceLight.class, new QueryOptions(from, numberOfElements))); } @Override public List get(TenantResourceType type, int from, int numberOfElements) throws SBonitaReadException { Map inputParameters = new HashMap<>(); inputParameters.put("type", type); return persistenceService.selectList( new SelectListDescriptor<>("getTenantResourcesOfType", inputParameters, STenantResource.class, new QueryOptions(from, numberOfElements))); } @Override public long count(TenantResourceType type) throws SBonitaReadException { Map inputParameters = new HashMap<>(); inputParameters.put("type", type); return persistenceService.selectOne(new SelectOneDescriptor<>("getNumberOfTenantResourcesOfType", inputParameters, STenantResource.class)); } @Override public long count(TenantResourceType type, String name) throws SBonitaReadException { Map inputParameters = new HashMap<>(); inputParameters.put("type", type); inputParameters.put("name", name); return persistenceService.selectOne(new SelectOneDescriptor<>("getNumberOfTenantResourcesOfTypeAndName", inputParameters, STenantResource.class)); } @Override public STenantResource get(TenantResourceType type, String name) throws SBonitaReadException { Map inputParameters = new HashMap<>(); inputParameters.put("type", type); inputParameters.put("name", name); return persistenceService.selectOne( new SelectOneDescriptor<>("getTenantResource", inputParameters, STenantResource.class)); } @Override public STenantResourceLight getSingleLightResource(TenantResourceType type) throws SBonitaReadException { Map inputParameters = new HashMap<>(); inputParameters.put("type", type); return persistenceService.selectOne(new SelectOneDescriptor<>( "getTenantResourcesLightOfType", inputParameters, STenantResourceLight.class)); } @Override public void remove(AbstractSTenantResource resource) throws SRecorderException { recorder.recordDelete(new DeleteRecord(resource), TENANT_RESOURCE); } } ================================================ FILE: services/bonita-resources/src/main/resources/org/bonitasoft/engine/resources/hibernate/resources.queries.hbm.xml ================================================ SELECT bar FROM org.bonitasoft.engine.resources.SBARResourceLight AS bar WHERE bar.processDefinitionId= :processDefinitionId AND bar.type = :type SELECT bar FROM org.bonitasoft.engine.resources.SBARResource AS bar WHERE bar.processDefinitionId= :processDefinitionId AND bar.type = :type SELECT count(bar.id) FROM org.bonitasoft.engine.resources.SBARResource AS bar WHERE bar.processDefinitionId= :processDefinitionId AND bar.type = :type SELECT bar FROM org.bonitasoft.engine.resources.SBARResource AS bar WHERE bar.processDefinitionId= :processDefinitionId AND bar.type = :type AND bar.name = :name SELECT r FROM org.bonitasoft.engine.resources.STenantResourceLight AS r WHERE r.type = :type SELECT r FROM org.bonitasoft.engine.resources.STenantResource AS r WHERE r.type = :type SELECT count(r.id) FROM org.bonitasoft.engine.resources.STenantResource AS r WHERE r.type = :type SELECT count(r.id) FROM org.bonitasoft.engine.resources.STenantResource AS r WHERE r.type = :type AND r.name = :name SELECT r FROM org.bonitasoft.engine.resources.STenantResource AS r WHERE r.type = :type AND r.name = :name ================================================ FILE: services/bonita-resources/src/test/java/org/bonitasoft/engine/resources/TenantResourcesServiceImplTest.java ================================================ /** * 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.resources; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Emmanuel Duchastenier */ @RunWith(MockitoJUnitRunner.Silent.class) public class TenantResourcesServiceImplTest { @Rule public final SystemOutRule systemOutRule = new SystemOutRule().enableLog().muteForSuccessfulTests(); @Mock Recorder recorder; @Mock ReadPersistenceService persistenceService; @InjectMocks TenantResourcesServiceImpl tenantResourcesService; @Before public void initMocks() throws Exception { doNothing().when(recorder).recordInsert(any(InsertRecord.class), anyString()); } @Test public void add_should_log_message_and_ignore_null_file_content() throws Exception { // when tenantResourcesService.add("resourceName", TenantResourceType.BDM, null, -1); // then verifyNoInteractions(recorder); } @Test public void add_should_log_message_and_ignore_empty_file_content() throws Exception { // when tenantResourcesService.add("resourceName", TenantResourceType.BDM, new byte[] {}, -1); // then verifyNoInteractions(recorder); } @Test public void add_should_work_for_valid_file_content() throws Exception { // when systemOutRule.clearLog(); tenantResourcesService.add("resourceName", TenantResourceType.BDM, "someValidContent".getBytes(), -1); // then verify(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); assertThat(systemOutRule.getLog()).isEmpty(); } } ================================================ FILE: services/bonita-resources/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: services/bonita-scheduler/build.gradle ================================================ dependencies { api project(':services:bonita-session') api project(':services:bonita-log') api project(':services:bonita-builder') api project(':services:bonita-events') api project(':services:bonita-persistence') api project(':services:bonita-commons') api project(':services:bonita-transaction') api project(':services:bonita-incident') api(libs.quartz) { exclude(group: "com.mchange") // c3p0 + transitive dep mchange-commons-java, because we do not use // default Quartz connection provider, as we have our own exclude(module: "HikariCP-java7") // Same reason } api libs.micrometerCore testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.logback annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/BonitaJobListener.java ================================================ /** * 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.scheduler; import java.io.Serializable; import java.util.Map; public interface BonitaJobListener extends Serializable { String BOS_JOB = "bosJob"; String JOB_DESCRIPTOR_ID = "jobDescriptorId"; String JOB_TYPE = "jobType"; String JOB_NAME = "jobName"; String JOB_GROUP = "jobGroup"; String TRIGGER_NAME = "triggerName"; String TRIGGER_GROUP = "triggerGroup"; String TRIGGER_PREVIOUS_FIRE_TIME = "triggerPreviousFireTime"; String TRIGGER_NEXT_FIRE_TIME = "triggerNextFireTime"; String REFIRE_COUNT = "refireCount"; String JOB_DATAS = "jobDatas"; String JOB_RESULT = "jobResult"; void jobToBeExecuted(); void jobExecutionVetoed(); void jobWasExecuted(Map context, Exception jobException); } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/JobChecker.java ================================================ /** * 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.scheduler; /** * @author Elias Ricken de Medeiros */ public interface JobChecker { boolean isLoggable(StatelessJob job); } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/JobIdentifier.java ================================================ /** * 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.scheduler; import java.io.Serializable; import lombok.EqualsAndHashCode; import lombok.Getter; /** * @author Baptiste Mesta */ @Getter @EqualsAndHashCode public class JobIdentifier implements Serializable { private static final long serialVersionUID = 5950749851853932753L; private final long id; private final String jobName; public JobIdentifier(final long jobId, final String jobName) { id = jobId; this.jobName = jobName; } @Override public String toString() { return "jobName=" + jobName + ";jobId=" + id; } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/JobService.java ================================================ /** * 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.scheduler; import java.util.List; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.scheduler.exception.failedJob.SFailedJobReadException; import org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorCreationException; import org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorDeletionException; import org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorNotFoundException; import org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorReadException; import org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogCreationException; import org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogDeletionException; import org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogUpdatingException; import org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterCreationException; import org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterDeletionException; import org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterNotFoundException; import org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterReadException; import org.bonitasoft.engine.scheduler.model.SFailedJob; import org.bonitasoft.engine.scheduler.model.SJobDescriptor; import org.bonitasoft.engine.scheduler.model.SJobLog; import org.bonitasoft.engine.scheduler.model.SJobParameter; /** * @author Celine Souchet * @since 6.1 */ public interface JobService { String JOB_DESCRIPTOR = "JOB_DESCRIPTOR"; String JOB_PARAMETER = "JOB_PARAMETER"; String JOB_LOG = "JOB_LOG"; /** * Create a new job descriptor * * @param sJobDescriptor JobDescriptor to create * @return The created jobDescriptor * @throws SJobDescriptorCreationException * @since 6.1 */ SJobDescriptor createJobDescriptor(SJobDescriptor sJobDescriptor) throws SJobDescriptorCreationException; /** * Delete the specified job descriptor * * @param id Identifier of job descriptor to delete * @throws SJobDescriptorReadException * @throws SJobDescriptorNotFoundException * @throws SJobDescriptorDeletionException * @since 6.1 */ void deleteJobDescriptor(long id) throws SJobDescriptorNotFoundException, SJobDescriptorReadException, SJobDescriptorDeletionException; /** * Delete the specified job descriptor * * @param sJobDescriptor JobDescriptor to delete * @throws SJobDescriptorDeletionException * @since 6.1 */ void deleteJobDescriptor(SJobDescriptor sJobDescriptor) throws SJobDescriptorDeletionException; /** * Delete all job descriptors * * @throws SJobDescriptorDeletionException * @since 6.4 */ void deleteAllJobDescriptors() throws SJobDescriptorDeletionException; /** * Delete a job descriptor corresponding to the given job name * * @param jobName name of job we want the jobDsecriptor to be deleted * @since 6.3 */ void deleteJobDescriptorByJobName(String jobName) throws SJobDescriptorDeletionException; /** * Get a specific job descriptor * * @param id Identifier of job descriptor * @return Null if the job descriptor doesn't exist, else the {@link SJobDescriptor} corresponding to the identifier * @throws SJobDescriptorReadException * @since 6.1 */ SJobDescriptor getJobDescriptor(long id) throws SJobDescriptorReadException; /** * Get total number of job descriptors * * @param queryOptions a map of specific parameters of a query * @return total number of job logs * @throws SBonitaReadException */ long getNumberOfJobDescriptors(QueryOptions queryOptions) throws SBonitaReadException; /** * Search all job descriptors according to specific criteria * * @param queryOptions a map of specific parameters of a query * @return A list of SJobParameter objects * @throws SBonitaReadException * @since 6.1 */ List searchJobDescriptors(QueryOptions queryOptions) throws SBonitaReadException; /** * Create new job parameters * * @param parameters JobParameters to create * @param jobDescriptorId Identifier of job descriptor * @return * @throws SJobParameterCreationException * @since 6.2 */ List createJobParameters(List parameters, long jobDescriptorId) throws SJobParameterCreationException; /** * Delete jobs parameters corresponding to the job descriptor, if exist. After, create new job parameters * * @param jobDescriptorId * @param parameters * @return A list of new SJobParameter objects * @throws SJobParameterCreationException * @since 6.1 */ List setJobParameters(long jobDescriptorId, List parameters) throws SJobParameterCreationException; /** * Create a new job parameter * * @param sJobParameter JobParameter to create * @param jobDescriptorId Identifier of job descriptor * @return * @throws SJobParameterCreationException * @since 6.2 */ SJobParameter createJobParameter(SJobParameter sJobParameter, long jobDescriptorId) throws SJobParameterCreationException; /** * Delete the specified job parameter * * @param id Identifier of job parameter to delete * @throws SJobParameterReadException * @throws SJobParameterNotFoundException * @throws SJobParameterDeletionException * @since 6.1 */ void deleteJobParameter(long id) throws SJobParameterNotFoundException, SJobParameterReadException, SJobParameterDeletionException; /** * Delete the specified job parameter * * @param sJobParameter JobParameter to delete * @throws SJobParameterDeletionException * @since 6.1 */ void deleteJobParameter(SJobParameter sJobParameter) throws SJobParameterDeletionException; /** * Get a specific job parameter * * @param id Identifier of job parameter * @return * @throws SJobParameterReadException * @throws SJobParameterNotFoundException * @since 6.1 */ SJobParameter getJobParameter(long id) throws SJobParameterNotFoundException, SJobParameterReadException; /** * get parameters of a job */ List getJobParameters(Long jobDescriptorId) throws SBonitaReadException; /** * Create a new job log * * @param sJobLog JobLog to create * @return * @throws SJobLogCreationException * @since 6.2 */ SJobLog createJobLog(SJobLog sJobLog) throws SJobLogCreationException; /** * Delete the specified job log * * @param id Identifier of job log to delete * @throws SBonitaReadException * @throws SJobLogDeletionException * @since 6.1 */ void deleteJobLog(long id) throws SJobLogDeletionException, SBonitaReadException; /** * Delete the specified job log * * @param sJobLog JobLog to delete * @throws SJobLogDeletionException * @since 6.1 */ void deleteJobLog(SJobLog sJobLog) throws SJobLogDeletionException; /** * Get a specific job log * * @param id Identifier of job log * @return * @throws SBonitaReadException * @since 6.1 */ SJobLog getJobLog(long id) throws SBonitaReadException; /** * Get total number of job logs * * @param queryOptions a map of specific parameters of a query * @return total number of job logs * @throws SBonitaReadException */ long getNumberOfJobLogs(QueryOptions queryOptions) throws SBonitaReadException; /** * Search all job logs according to specific criteria * * @param queryOptions a map of specific parameters of a query * @return A list of SJobLog objects * @throws SBonitaReadException * @since 6.1 */ List searchJobLogs(QueryOptions queryOptions) throws SBonitaReadException; /** * Get list of failed jobs * * @param startIndex * @param maxResults * @return A list of SFailedJob objects * @throws SFailedJobReadException * @since 6.2 */ List getFailedJobs(int startIndex, int maxResults) throws SFailedJobReadException; /** * Update a {@link SJobLog} * * @param jobLog The log to update * @param descriptor * @since 6.4.0 */ void updateJobLog(SJobLog jobLog, EntityUpdateDescriptor descriptor) throws SJobLogUpdatingException; /** * Delete all {@link SJobLog} of a specific {@link SJobDescriptor} * * @param jobDescriptorId The identifier of the {@link SJobDescriptor} * @throws SBonitaReadException * @throws SJobLogDeletionException * @since 6.4.0 */ void deleteJobLogs(long jobDescriptorId) throws SJobLogDeletionException, SBonitaReadException; /** * Get all {@link SJobLog} of a specific {@link SJobDescriptor} * * @param jobDescriptorId The identifier of the {@link SJobDescriptor} * @param fromIndex The index of the first element of the list * @param maxResults The nulber max of elements of the list * @return A list of {@link SJobLog} * @throws SBonitaReadException * @since 6.4.0 */ List getJobLogs(long jobDescriptorId, int fromIndex, int maxResults) throws SBonitaReadException; /** * log an error on a job * * @param jobException the exception * @param jobDescriptorId the id of the job * @throws SBonitaReadException * @throws SJobLogUpdatingException * @throws SJobLogCreationException * @throws SJobDescriptorReadException * @since 7.2.0 */ void logJobError(final Throwable jobException, final Long jobDescriptorId) throws SBonitaReadException, SJobLogUpdatingException, SJobLogCreationException, SJobDescriptorReadException; } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/SchedulerExecutor.java ================================================ /** * 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.scheduler; import java.util.Date; import java.util.List; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; import org.bonitasoft.engine.scheduler.impl.SchedulerServiceImpl; import org.bonitasoft.engine.scheduler.trigger.Trigger; /** * @author Matthieu Chaffotte * @author Celine Souchet */ public interface SchedulerExecutor { boolean isStarted() throws SSchedulerException; boolean isShutdown() throws SSchedulerException; /** * Note that once a scheduler is shutdown, it cannot be restarted without being re-instantiated. * * @throws SSchedulerException * @since 6.4.0 */ void start() throws SSchedulerException; /** * Note that once a scheduler is shutdown, it cannot be restarted without being re-instantiated. * * @throws SSchedulerException * @since 6.4.0 */ void shutdown() throws SSchedulerException; boolean mayFireAgain(String jobName) throws SSchedulerException; void rescheduleErroneousTriggers() throws SSchedulerException; boolean delete(String jobName) throws SSchedulerException; void deleteJobs() throws SSchedulerException; List getJobs() throws SSchedulerException; void setBOSSchedulerService(SchedulerServiceImpl schedulerService); void schedule(long jobId, String jobName, Trigger trigger, boolean disallowConcurrentExecution) throws SSchedulerException; void executeAgain(long jobId, String jobName, boolean disallowConcurrentExecution, int delayInMillis) throws SSchedulerException; void pauseJobs() throws SSchedulerException; void resumeJobs() throws SSchedulerException; /** * Remove (delete) the {@link org.quartz.Trigger} with the given key, and store the new given one - * which must be associated * with the same job (the new trigger must have the job name specified) * - however, the new trigger need not have the same name as the old trigger. * * @param triggerName * The name of the trigger to replace * @param triggerStartTime * The start date of the new trigger * @return null if a Trigger with the given * name was not found and removed from the store (and the * new trigger is therefore not stored), otherwise * the first fire time of the newly scheduled trigger is returned. * @throws SSchedulerException * @since 6.4.0 */ Date rescheduleJob(String triggerName, Date triggerStartTime) throws SSchedulerException; /** * Check if a job exists. * * @param jobName * The name of the job * @return True if the job exists, else False. * @throws SSchedulerException * @since 6.4.0 */ boolean isExistingJob(String jobName) throws SSchedulerException; } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/SchedulerService.java ================================================ /** * 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.scheduler; import java.util.Date; import java.util.List; import org.bonitasoft.engine.commons.PlatformLifecycleService; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; import org.bonitasoft.engine.scheduler.model.SJobDescriptor; import org.bonitasoft.engine.scheduler.model.SJobParameter; import org.bonitasoft.engine.scheduler.trigger.Trigger; /** * @author Matthieu Chaffotte * @since 6.0 */ public interface SchedulerService extends PlatformLifecycleService { /** * This service will fire the following events : *
        *
      • SCHEDULER_STARTED = "SCHEDULER_STARTED"
      • *
      • SCHEDULER_STOPPED = "SCHEDULER_STOPPED"
      • *
      • JOB_FAILED = "JOB_FAILED"
      • *
      */ String SCHEDULER_STARTED = "SCHEDULER_STARTED"; String SCHEDULER_STOPPED = "SCHEDULER_STOPPED"; String JOB_FAILED = "JOB_FAILED"; /** * Checks whether the service is started. * * @return true if the service is started; false otherwise. * @throws SSchedulerException * if an exception occurs. */ boolean isStarted() throws SSchedulerException; /** * Checks whether the service is shutdown. * * @return true if the service is shutdown; false otherwise. * @throws SSchedulerException */ boolean isStopped() throws SSchedulerException; /** * Schedules a job. * * @param jobDescriptor * @param trigger * @throws SSchedulerException * if an exception occurs. */ void schedule(SJobDescriptor jobDescriptor, Trigger trigger) throws SSchedulerException; /** * Schedules a job. * * @param jobDescriptor * @param parameters * @param trigger * @throws SSchedulerException * if an exception occurs. */ void schedule(SJobDescriptor jobDescriptor, List parameters, Trigger trigger) throws SSchedulerException; /** * Execute a job again * * @param jobDescriptorId the job to re execute * @param delayInMillis */ void executeAgain(long jobDescriptorId, int delayInMillis) throws SSchedulerException; /** * Retry a job once * In addition to executing the job again, this will also delete failed job logs * * @param jobDescriptorId the job to retry */ void retryJobThatFailed(long jobDescriptorId) throws SSchedulerException; /** * Change parameters of a job and retry it once * In addition to executing the job again, this will also delete failed job logs * * @param jobDescriptorId the job to retry * @param parameters the new parameters for the job */ void retryJobThatFailed(long jobDescriptorId, List parameters) throws SSchedulerException; /** * Deletes a job according to its name. * * @param jobName * the job name * @return true if delete a job, otherwise return false. * @throws SSchedulerException * if an exception occurs. */ boolean delete(String jobName) throws SSchedulerException; /** * Deletes all jobs. * * @throws SSchedulerException * if an exception occurs. */ void deleteJobs() throws SSchedulerException; /** * Get all jobs * * @return all jobs * @throws SSchedulerException * if an exception occurs. */ List getJobs() throws SSchedulerException; void rescheduleErroneousTriggers() throws SSchedulerException; /** * Pause all running jobs * * @throws SSchedulerException */ void pauseJobs() throws SSchedulerException; /** * Resume all paused jobs * * @throws SSchedulerException */ void resumeJobs() throws SSchedulerException; /** * Remove (delete) the Trigger with the * given key, and store the new given one - which must be associated * with the same job (the new trigger must have the job name specified) * - however, the new trigger need not have the same name as the old trigger. * * @param triggerName * The name of the trigger to replace * @param triggerStartTime * The start date of the new trigger * @return null if a Trigger with the given * name was not found and removed from the store (and the * new trigger is therefore not stored), otherwise * the first fire time of the newly scheduled trigger is returned. * @throws SSchedulerException * @since 6.4.0 */ Date rescheduleJob(String triggerName, Date triggerStartTime) throws SSchedulerException; /** * Note that once a scheduler is shutdown, it cannot be restarted without being re-instantiated. * * @throws SSchedulerException * @since 6.4.0 */ @Override void start() throws SBonitaException; /** * Note that once a scheduler is shutdown, it cannot be restarted without being re-instantiated. * * @throws SSchedulerException * @since 6.4.0 */ @Override void stop() throws SBonitaException; /** * Check if a job exists. * * @param jobName * The name of the job * @return True if the job exists, else False. * @throws SSchedulerException * @since 6.4.0 */ boolean isExistingJob(String jobName) throws SSchedulerException; boolean mayFireAgain(String jobName) throws SSchedulerException; } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/StatelessJob.java ================================================ /** * 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.scheduler; import java.io.Serializable; import java.util.Map; import org.bonitasoft.engine.events.model.SFireEventException; import org.bonitasoft.engine.scheduler.exception.SJobConfigurationException; import org.bonitasoft.engine.scheduler.exception.SJobExecutionException; /** * Interface of a scheduled job. A job is classified using a name and a group name. A job has a unique name and group * name. It * fires the following events : *
        *
      • JOB_EXECUTING = "JOB_EXECUTING"
      • *
      • JOB_COMPLETED = "JOB_COMPLETED"
      • *
      * * @author Matthieu Chaffotte */ public interface StatelessJob extends Serializable { String JOB_EXECUTING = "JOB_EXECUTING"; String JOB_COMPLETED = "JOB_COMPLETED"; String JOB_DESCRIPTOR_ID = "JOB_DESCRIPTOR_ID"; /** * Gets the job name. * * @return the job name * @since 6.0 */ String getName(); /** * Gets the description of the job. * * @return the job description * @since 6.0 */ String getDescription(); /** * Execute the content of the job. * * @throws SJobExecutionException * if an exception occurs * @throws SFireEventException * @since 6.0 */ void execute() throws SJobExecutionException, SFireEventException; /** * This method is called by the scheduler service before the execution of the job * * @param attributes * key is the name of the attribute * value is the value of the attribute * @throws SJobConfigurationException * @since 6.0 */ void setAttributes(Map attributes) throws SJobConfigurationException; } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/SJobConfigurationException.java ================================================ /** * 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.scheduler.exception; /** * @author Baptiste Mesta */ public class SJobConfigurationException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SJobConfigurationException(final String message) { super(message); } public SJobConfigurationException(final Exception e) { super(e); } public SJobConfigurationException(final String message, final Exception exception) { super(message, exception); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/SJobExecutionException.java ================================================ /** * 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.scheduler.exception; /** * @author Matthieu Chaffotte */ public class SJobExecutionException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SJobExecutionException(final String message) { super(message); } public SJobExecutionException(final Throwable e) { super(e); } public SJobExecutionException(final String message, final Exception exception) { super(message, exception); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/SJobListenerExecutionException.java ================================================ /** * 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.scheduler.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Celine Souchet */ public class SJobListenerExecutionException extends SBonitaException { private static final long serialVersionUID = 9185581023740886000L; public SJobListenerExecutionException(final String message) { super(message); } public SJobListenerExecutionException(final Throwable e) { super(e); } public SJobListenerExecutionException(final String message, final Exception e) { super(message, e); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/SSchedulerException.java ================================================ /** * 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.scheduler.exception; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Matthieu Chaffotte */ public class SSchedulerException extends SBonitaException { private static final long serialVersionUID = 9185581023740886000L; public SSchedulerException(final String message) { super(message); } public SSchedulerException(final Throwable e) { super(e); } public SSchedulerException(final String message, final Exception e) { super(message, e); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/failedJob/SFailedJobReadException.java ================================================ /** * 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.scheduler.exception.failedJob; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Celine Souchet */ public class SFailedJobReadException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SFailedJobReadException(final String message) { super(message); } public SFailedJobReadException(final Exception e) { super(e); } public SFailedJobReadException(final String message, final Exception exception) { super(message, exception); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobDescriptor/SJobDescriptorCreationException.java ================================================ /** * 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.scheduler.exception.jobDescriptor; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Celine Souchet */ public class SJobDescriptorCreationException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SJobDescriptorCreationException(final String message) { super(message); } public SJobDescriptorCreationException(final Exception e) { super(e); } public SJobDescriptorCreationException(final String message, final Exception exception) { super(message, exception); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobDescriptor/SJobDescriptorDeletionException.java ================================================ /** * 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.scheduler.exception.jobDescriptor; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Celine Souchet */ public class SJobDescriptorDeletionException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SJobDescriptorDeletionException(final String message) { super(message); } public SJobDescriptorDeletionException(final Exception e) { super(e); } public SJobDescriptorDeletionException(final String message, final Exception exception) { super(message, exception); } public SJobDescriptorDeletionException(final long id) { super("Can't find job descriptor with id = " + id); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobDescriptor/SJobDescriptorNotFoundException.java ================================================ /** * 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.scheduler.exception.jobDescriptor; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Celine Souchet */ public class SJobDescriptorNotFoundException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SJobDescriptorNotFoundException(final String message) { super(message); } public SJobDescriptorNotFoundException(final Exception e) { super(e); } public SJobDescriptorNotFoundException(final String message, final Exception exception) { super(message, exception); } public SJobDescriptorNotFoundException(final long id) { super("Can't find job descriptor with id = " + id); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobDescriptor/SJobDescriptorReadException.java ================================================ /** * 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.scheduler.exception.jobDescriptor; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Celine Souchet */ public class SJobDescriptorReadException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SJobDescriptorReadException(final String message) { super(message); } public SJobDescriptorReadException(final Exception e) { super(e); } public SJobDescriptorReadException(final String message, final Exception exception) { super(message, exception); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobLog/SJobLogCreationException.java ================================================ /** * 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.scheduler.exception.jobLog; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Celine Souchet */ public class SJobLogCreationException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SJobLogCreationException(final String message) { super(message); } public SJobLogCreationException(final Exception e) { super(e); } public SJobLogCreationException(final String message, final Exception exception) { super(message, exception); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobLog/SJobLogDeletionException.java ================================================ /** * 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.scheduler.exception.jobLog; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Celine Souchet */ public class SJobLogDeletionException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SJobLogDeletionException(final String message) { super(message); } public SJobLogDeletionException(final Exception e) { super(e); } public SJobLogDeletionException(final String message, final Exception exception) { super(message, exception); } public SJobLogDeletionException(final long id) { super("Can't delete job log with id = " + id); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobLog/SJobLogNotFoundException.java ================================================ /** * 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.scheduler.exception.jobLog; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Celine Souchet */ public class SJobLogNotFoundException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SJobLogNotFoundException(final String message) { super(message); } public SJobLogNotFoundException(final Exception e) { super(e); } public SJobLogNotFoundException(final String message, final Exception exception) { super(message, exception); } public SJobLogNotFoundException(long id) { super("Can't find job log with id = " + id); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobLog/SJobLogReadException.java ================================================ /** * 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.scheduler.exception.jobLog; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Celine Souchet */ public class SJobLogReadException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SJobLogReadException(final String message) { super(message); } public SJobLogReadException(final Exception e) { super(e); } public SJobLogReadException(final String message, final Exception exception) { super(message, exception); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobLog/SJobLogUpdatingException.java ================================================ /** * 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.scheduler.exception.jobLog; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Celine Souchet */ public class SJobLogUpdatingException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SJobLogUpdatingException(final String message) { super(message); } public SJobLogUpdatingException(final Exception e) { super(e); } public SJobLogUpdatingException(final String message, final Exception exception) { super(message, exception); } public SJobLogUpdatingException(final long id) { super("Can't update job log with id = " + id); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobParameter/SJobParameterCreationException.java ================================================ /** * 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.scheduler.exception.jobParameter; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Celine Souchet */ public class SJobParameterCreationException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SJobParameterCreationException(final String message) { super(message); } public SJobParameterCreationException(final Exception e) { super(e); } public SJobParameterCreationException(final String message, final Exception exception) { super(message, exception); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobParameter/SJobParameterDeletionException.java ================================================ /** * 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.scheduler.exception.jobParameter; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Celine Souchet */ public class SJobParameterDeletionException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SJobParameterDeletionException(final String message) { super(message); } public SJobParameterDeletionException(final Exception e) { super(e); } public SJobParameterDeletionException(final String message, final Exception exception) { super(message, exception); } public SJobParameterDeletionException(final long id) { super("Can't find job descriptor with id = " + id); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobParameter/SJobParameterNotFoundException.java ================================================ /** * 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.scheduler.exception.jobParameter; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Celine Souchet */ public class SJobParameterNotFoundException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SJobParameterNotFoundException(final String message) { super(message); } public SJobParameterNotFoundException(final Exception e) { super(e); } public SJobParameterNotFoundException(final String message, final Exception exception) { super(message, exception); } public SJobParameterNotFoundException(long id) { super("Can't find job parameter with id = " + id); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/exception/jobParameter/SJobParameterReadException.java ================================================ /** * 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.scheduler.exception.jobParameter; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; /** * @author Celine Souchet */ public class SJobParameterReadException extends SSchedulerException { private static final long serialVersionUID = -226779259333121029L; public SJobParameterReadException(final String message) { super(message); } public SJobParameterReadException(final Exception e) { super(e); } public SJobParameterReadException(final String message, final Exception exception) { super(message, exception); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/AbstractQuartzJob.java ================================================ /** * 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.scheduler.impl; import org.bonitasoft.engine.commons.exceptions.SRetryableException; import org.bonitasoft.engine.scheduler.JobIdentifier; import org.bonitasoft.engine.scheduler.StatelessJob; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Wraps a Bonita job. * * @author Matthieu Chaffotte * @author Baptsite Mesta * @author Celine Souchet */ public abstract class AbstractQuartzJob implements org.quartz.Job { private static Logger logger = LoggerFactory.getLogger(AbstractQuartzJob.class); private StatelessJob bosJob; private SchedulerServiceImpl schedulerService; private JobDetail jobDetail; @Override public void execute(final JobExecutionContext context) throws JobExecutionException { final JobIdentifier jobIdentifier = getJobIdentifier(jobDetail.getJobDataMap()); try { bosJob = retrieveJob(jobIdentifier); bosJob.execute(); } catch (final SRetryableException e) { logger.info("Job {} failed but it will be retried {}", jobIdentifier, e.getMessage()); try { schedulerService.executeAgain(jobIdentifier.getId(), 5000); } catch (SSchedulerException ex) { throw new JobExecutionException("Unable to reschedule job that could be retried", ex); } } catch (final Throwable e) { throw new JobExecutionException(e); } } private StatelessJob retrieveJob(JobIdentifier jobIdentifier) throws JobExecutionException { try { return schedulerService.getPersistedJob(jobIdentifier); } catch (final Throwable t) { throw new JobExecutionException("unable to create the BOS job", t); } } private JobIdentifier getJobIdentifier(JobDataMap jobDataMap) { final long jobId = Long.parseLong((String) jobDataMap.get("jobId")); final String jobName = (String) jobDataMap.get("jobName"); return new JobIdentifier(jobId, jobName); } StatelessJob getBosJob() { return bosJob; } void setSchedulerService(SchedulerServiceImpl schedulerService) { this.schedulerService = schedulerService; } void setJobDetails(JobDetail jobDetail) { this.jobDetail = jobDetail; } JobDetail getJobDetail() { return jobDetail; } SchedulerServiceImpl getSchedulerService() { return schedulerService; } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/BonitaJobStoreCMT.java ================================================ /** * 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.scheduler.impl; import java.sql.Connection; import org.quartz.JobDetail; import org.quartz.JobPersistenceException; import org.quartz.Trigger.CompletedExecutionInstruction; import org.quartz.impl.jdbcjobstore.JobStoreCMT; import org.quartz.spi.OperableTrigger; /** * @author Matthieu Chaffotte */ public class BonitaJobStoreCMT extends JobStoreCMT { @Override protected void triggeredJobComplete(final Connection conn, final OperableTrigger trigger, final JobDetail jobDetail, final CompletedExecutionInstruction triggerInstCode) throws JobPersistenceException { super.triggeredJobComplete(conn, trigger, jobDetail, triggerInstCode); if (CompletedExecutionInstruction.SET_TRIGGER_ERROR.equals(triggerInstCode) || CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_ERROR.equals(triggerInstCode)) { // the super method logs already this message but only in info level. (should be logged as error) if (!getLog().isInfoEnabled()) { getLog().error("All triggers of Job " + trigger.getKey() + " set to ERROR state."); } getLog().error( "In order to restart the triggers, you can either restart your node or call the platformAPI.rescheduleErroneousTriggers method."); } } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/BonitaScheduler.java ================================================ /** * Copyright (C) 2022 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.scheduler.impl; import org.quartz.core.QuartzScheduler; import org.quartz.impl.StdScheduler; /** * Custom Quartz scheduler implementation that exposes the internal QuartzScheduler. */ public class BonitaScheduler extends StdScheduler { private QuartzScheduler quartzScheduler; public BonitaScheduler(QuartzScheduler sched) { super(sched); this.quartzScheduler = sched; } public QuartzScheduler getQuartzScheduler() { return quartzScheduler; } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/BonitaSchedulerFactory.java ================================================ /** * 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.scheduler.impl; import java.util.Properties; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.core.QuartzScheduler; import org.quartz.core.QuartzSchedulerResources; import org.quartz.impl.StdSchedulerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta */ @Component @ConditionalOnSingleCandidate(BonitaSchedulerFactory.class) public class BonitaSchedulerFactory extends StdSchedulerFactory { private SchedulerServiceImpl schedulerService; public BonitaSchedulerFactory(@Qualifier("quartzProperties") Properties props) throws SchedulerException { super(props); } @Override public Scheduler getScheduler() throws SchedulerException { final Scheduler scheduler = super.getScheduler(); scheduler.setJobFactory(new TransactionalSimpleJobFactory(schedulerService)); return scheduler; } public void setBOSSchedulerService(final SchedulerServiceImpl schedulerService) { this.schedulerService = schedulerService; } @Override protected Scheduler instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs) { return new BonitaScheduler(qs); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/ConcurrentQuartzJob.java ================================================ /** * 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.scheduler.impl; public class ConcurrentQuartzJob extends AbstractQuartzJob { } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/JDBCJobListener.java ================================================ /** * 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.scheduler.impl; import java.io.Serializable; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.scheduler.BonitaJobListener; import org.bonitasoft.engine.scheduler.JobService; import org.bonitasoft.engine.scheduler.SchedulerService; import org.quartz.JobExecutionException; /** * @author Celine Souchet * @author Matthieu Chaffotte * @author Elias Ricken de Medeiros */ @Slf4j public class JDBCJobListener implements BonitaJobListener { private final JobService jobService; private SchedulerService schedulerService; public JDBCJobListener(final JobService jobService, SchedulerService schedulerService) { super(); this.jobService = jobService; this.schedulerService = schedulerService; } @Override public void jobToBeExecuted() { // nothing to do } @Override public void jobExecutionVetoed() { // nothing to do } @Override public void jobWasExecuted(final Map context, final Exception jobException) { if (jobException != null) { if (jobException instanceof JobExecutionException jobExecutionException && jobExecutionException.refireImmediately()) { log.debug("An exception occurs during the job execution but it will be retried.", jobException); } else { log.warn("An exception occurs during the job execution.", jobException); } return; } Long jobDescriptorId = (Long) context.get(JOB_DESCRIPTOR_ID); if (isNullOrEmpty(jobDescriptorId)) { log.warn("A quartz job was executed but is not a bonita Job, context: {}", context); return; } if (context.get(TRIGGER_NEXT_FIRE_TIME) == null) { //will not fire again deleteJobDescriptor(context, jobDescriptorId); } } private void deleteJobDescriptor(Map context, Long jobDescriptorId) { try { //delete job only if there is no other trigger boolean mayFireAgain = schedulerService.mayFireAgain((String) context.get(JOB_NAME)); log.debug("{} job descriptor of job {} because it may {}fire again.", mayFireAgain ? "Keeping" : "Deleting", context.get(JOB_NAME), mayFireAgain ? "" : "not "); if (!mayFireAgain) { jobService.deleteJobDescriptor(jobDescriptorId); } } catch (final Exception e) { log.warn("Unable to delete job descriptor {} of job {}", jobDescriptorId, context.get(JOB_NAME), e); } } private boolean isNullOrEmpty(final Long id) { return id == null || id == 0; } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/JobServiceImpl.java ================================================ /** * 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.scheduler.impl; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.FilterOption; import org.bonitasoft.engine.persistence.OrderByOption; import org.bonitasoft.engine.persistence.OrderByType; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.scheduler.JobService; import org.bonitasoft.engine.scheduler.exception.failedJob.SFailedJobReadException; import org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorCreationException; import org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorDeletionException; import org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorReadException; import org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogCreationException; import org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogDeletionException; import org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogUpdatingException; import org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterCreationException; import org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterDeletionException; import org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterNotFoundException; import org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterReadException; import org.bonitasoft.engine.scheduler.model.SFailedJob; import org.bonitasoft.engine.scheduler.model.SJobDescriptor; import org.bonitasoft.engine.scheduler.model.SJobLog; import org.bonitasoft.engine.scheduler.model.SJobParameter; import org.bonitasoft.engine.scheduler.recorder.SelectDescriptorBuilder; /** * @author Celine Souchet * @author Matthieu Chaffotte */ @Slf4j public class JobServiceImpl implements JobService { private final EventService eventService; private final Recorder recorder; private final ReadPersistenceService readPersistenceService; public JobServiceImpl(final EventService eventService, final Recorder recorder, final ReadPersistenceService readPersistenceService) { this.readPersistenceService = readPersistenceService; this.eventService = eventService; this.recorder = recorder; } @Override public SJobDescriptor createJobDescriptor(final SJobDescriptor sJobDescriptor) throws SJobDescriptorCreationException { if (sJobDescriptor == null) { throw new IllegalArgumentException("The job descriptor is null"); } else if (sJobDescriptor.getJobName() == null) { throw new IllegalArgumentException("The job name is null"); } final SJobDescriptor sJobDescriptorToRecord = new SJobDescriptor(sJobDescriptor.getJobClassName(), sJobDescriptor.getJobName(), sJobDescriptor.getDescription()); try { create(sJobDescriptorToRecord, JOB_DESCRIPTOR); } catch (final SRecorderException sre) { throw new SJobDescriptorCreationException(sre); } return sJobDescriptorToRecord; } @Override public void deleteJobDescriptor(final long id) throws SJobDescriptorReadException, SJobDescriptorDeletionException { final SJobDescriptor sJobDescriptor = getJobDescriptor(id); if (sJobDescriptor == null) { if (log.isTraceEnabled()) { final StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("jobDescriptor with id"); stringBuilder.append(id); stringBuilder.append(" already deleted, ignore it"); log.trace(stringBuilder.toString()); } } else { deleteJobDescriptor(sJobDescriptor); } } @Override public void deleteJobDescriptor(final SJobDescriptor sJobDescriptor) throws SJobDescriptorDeletionException { if (sJobDescriptor == null) { throw new IllegalArgumentException("The job descriptor is null"); } try { delete(sJobDescriptor, JOB_DESCRIPTOR); } catch (final SBonitaException e) { throw new SJobDescriptorDeletionException(e); } } @Override public SJobDescriptor getJobDescriptor(final long id) throws SJobDescriptorReadException { try { final SJobDescriptor sJobDescriptor = readPersistenceService .selectById(SelectDescriptorBuilder.getElementById(SJobDescriptor.class, "SJobDescriptor", id)); return sJobDescriptor; } catch (final SBonitaReadException sbre) { throw new SJobDescriptorReadException(sbre); } } @Override public long getNumberOfJobDescriptors(final QueryOptions queryOptions) throws SBonitaReadException { return readPersistenceService.getNumberOfEntities(SJobDescriptor.class, queryOptions, null); } @Override public List searchJobDescriptors(final QueryOptions queryOptions) throws SBonitaReadException { return readPersistenceService.searchEntity(SJobDescriptor.class, queryOptions, null); } @Override public List createJobParameters(final List sJobParameters, final long jobDescriptorId) throws SJobParameterCreationException { final List createdSJobParameters = new ArrayList<>(); if (sJobParameters != null) { for (final SJobParameter sJobParameter : sJobParameters) { createdSJobParameters.add(createJobParameter(sJobParameter, jobDescriptorId)); } } return createdSJobParameters; } @Override public List setJobParameters(final long jobDescriptorId, final List parameters) throws SJobParameterCreationException { deleteAllJobParameters(jobDescriptorId); return createJobParameters(parameters, jobDescriptorId); } protected void deleteAllJobParameters(final long jobDescriptorId) throws SJobParameterCreationException { try { for (SJobParameter jobParameter : getJobParameters(jobDescriptorId)) { deleteJobParameter(jobParameter); } } catch (final SBonitaException sbe) { throw new SJobParameterCreationException(sbe); } } @Override public SJobParameter createJobParameter(final SJobParameter sJobParameter, final long jobDescriptorId) throws SJobParameterCreationException { if (sJobParameter == null) { throw new IllegalArgumentException("The job descriptor is null"); } final SJobParameter sJobParameterToRecord = SJobParameter.builder() .key(sJobParameter.getKey()) .value(sJobParameter.getValue()).jobDescriptorId(jobDescriptorId).build(); try { create(sJobParameterToRecord, JOB_PARAMETER); } catch (final SRecorderException sre) { throw new SJobParameterCreationException(sre); } return sJobParameter; } @Override public void deleteJobParameter(final long id) throws SJobParameterNotFoundException, SJobParameterReadException, SJobParameterDeletionException { final SJobParameter sJobParameter = getJobParameter(id); deleteJobParameter(sJobParameter); } @Override public void deleteJobParameter(final SJobParameter sJobParameter) throws SJobParameterDeletionException { try { delete(sJobParameter, JOB_PARAMETER); } catch (final SBonitaException e) { throw new SJobParameterDeletionException(e); } } @Override public SJobParameter getJobParameter(final long id) throws SJobParameterNotFoundException, SJobParameterReadException { try { final SJobParameter sJobParameter = readPersistenceService .selectById(SelectDescriptorBuilder.getElementById(SJobParameter.class, "SJobParameter", id)); if (sJobParameter == null) { throw new SJobParameterNotFoundException(id); } return sJobParameter; } catch (final SBonitaReadException sbre) { throw new SJobParameterReadException(sbre); } } @Override public List getJobParameters(Long jobDescriptorId) throws SBonitaReadException { Map parameters = Collections.singletonMap("jobDescriptorId", jobDescriptorId); return readPersistenceService.selectList(new SelectListDescriptor<>("getJobParameters", parameters, SJobParameter.class, QueryOptions.countQueryOptions())); } @Override public SJobLog createJobLog(final SJobLog sJobLog) throws SJobLogCreationException { try { create(sJobLog, JOB_LOG); } catch (final SRecorderException sre) { throw new SJobLogCreationException(sre); } return sJobLog; } @Override public void deleteJobLog(final long id) throws SJobLogDeletionException, SBonitaReadException { final SJobLog sJobLog = getJobLog(id); if (sJobLog != null) { deleteJobLog(sJobLog); } } @Override public void deleteJobLog(final SJobLog sJobLog) throws SJobLogDeletionException { try { delete(sJobLog, JOB_LOG); } catch (final SBonitaException e) { throw new SJobLogDeletionException(e); } } @Override public void deleteJobLogs(final long jobDescriptorId) throws SJobLogDeletionException, SBonitaReadException { List jobLogs = getJobLogs(jobDescriptorId, 0, 100); while (!jobLogs.isEmpty()) { deleteJobLogs(jobLogs); jobLogs = getJobLogs(jobDescriptorId, 0, 100); } } private void deleteJobLogs(final List jobLogs) throws SJobLogDeletionException { for (final SJobLog sJobLog : jobLogs) { deleteJobLog(sJobLog); } } @Override public List getJobLogs(final long jobDescriptorId, final int fromIndex, final int maxResults) throws SBonitaReadException { final FilterOption filter = new FilterOption(SJobLog.class, "jobDescriptorId", jobDescriptorId); final OrderByOption orderByOption = new OrderByOption(SJobLog.class, "jobDescriptorId", OrderByType.ASC); final QueryOptions options = new QueryOptions(fromIndex, maxResults, Arrays.asList(orderByOption), Arrays.asList(filter), null); return searchJobLogs(options); } @Override public SJobLog getJobLog(final long id) throws SBonitaReadException { return readPersistenceService.selectById(SelectDescriptorBuilder.getElementById(SJobLog.class, "SJobLog", id)); } @Override public long getNumberOfJobLogs(final QueryOptions queryOptions) throws SBonitaReadException { return readPersistenceService.getNumberOfEntities(SJobLog.class, queryOptions, null); } @Override public List searchJobLogs(final QueryOptions queryOptions) throws SBonitaReadException { return readPersistenceService.searchEntity(SJobLog.class, queryOptions, null); } private void delete(final PersistentObject persistentObject, final String eventType) throws SRecorderException { recorder.recordDelete(new DeleteRecord(persistentObject), eventType); } private void create(final PersistentObject persistentObject, final String eventType) throws SRecorderException { recorder.recordInsert(new InsertRecord(persistentObject), eventType); } @Override public List getFailedJobs(final int startIndex, final int maxResults) throws SFailedJobReadException { final QueryOptions queryOptions = new QueryOptions(startIndex, maxResults); try { return readPersistenceService.selectList(SelectDescriptorBuilder.getFailedJobs(queryOptions)); } catch (final SBonitaReadException sbre) { throw new SFailedJobReadException(sbre); } } @Override public void deleteJobDescriptorByJobName(final String jobName) throws SJobDescriptorDeletionException { final List filters = new ArrayList<>(); filters.add(new FilterOption(SJobDescriptor.class, "jobName", jobName)); final List orders = Arrays .asList(new OrderByOption(SJobDescriptor.class, "id", OrderByType.ASC)); final QueryOptions queryOptions = new QueryOptions(0, 1, orders, filters, null); try { final List jobDescriptors = searchJobDescriptors(queryOptions); if (!jobDescriptors.isEmpty()) { deleteJobDescriptor(jobDescriptors.get(0)); } } catch (final SBonitaReadException e) { throw new SJobDescriptorDeletionException( "Job " + jobName + " not found, can't delete corresponding job descriptor"); } } @Override public void deleteAllJobDescriptors() throws SJobDescriptorDeletionException { final List filters = new ArrayList<>(); final QueryOptions queryOptions = new QueryOptions(0, 100, null, filters, null); try { final List jobDescriptors = searchJobDescriptors(queryOptions); for (final SJobDescriptor sJobDescriptor : jobDescriptors) { deleteJobDescriptor(sJobDescriptor); } } catch (final SBonitaReadException e) { throw new SJobDescriptorDeletionException(e); } } @Override public void updateJobLog(final SJobLog jobLog, final EntityUpdateDescriptor descriptor) throws SJobLogUpdatingException { try { recorder.recordUpdate(UpdateRecord.buildSetFields(jobLog, descriptor), JOB_LOG); } catch (final SRecorderException e) { throw new SJobLogUpdatingException(e); } } @Override public void logJobError(final Throwable jobException, final Long jobDescriptorId) throws SBonitaReadException, SJobLogUpdatingException, SJobLogCreationException, SJobDescriptorReadException { final List jobLogs = getJobLogs(jobDescriptorId, 0, 1); if (!jobLogs.isEmpty()) { final SJobLog jobLog = jobLogs.get(0); final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); final StringWriter exceptionWriter = new StringWriter(); jobException.printStackTrace(new PrintWriter(exceptionWriter)); descriptor.addField("lastMessage", exceptionWriter.toString()); descriptor.addField("lastUpdateDate", System.currentTimeMillis()); descriptor.addField("retryNumber", jobLog.getRetryNumber() + 1); updateJobLog(jobLog, descriptor); } else { createJobLog(jobException, jobDescriptorId); } } public void createJobLog(final Throwable jobException, final Long jobDescriptorId) throws SJobLogCreationException, SJobDescriptorReadException { SJobDescriptor jobDescriptor = getJobDescriptor(jobDescriptorId); if (jobDescriptor != null) { final SJobLog jobLog = new SJobLog(jobDescriptorId); jobLog.setLastMessage(getStackTrace(jobException)); jobLog.setRetryNumber(0L); jobLog.setLastUpdateDate(System.currentTimeMillis()); createJobLog(jobLog); } else { log.warn("Impossible to mark the job with id '" + jobDescriptorId + "' as failed because no job was found for this identifier. It was probably removed just after its failure and before this action."); } } private String getStackTrace(final Throwable jobException) { final StringWriter exceptionWriter = new StringWriter(); jobException.printStackTrace(new PrintWriter(exceptionWriter)); return exceptionWriter.toString(); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/JobWrapper.java ================================================ /** * 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.scheduler.impl; import java.io.Serial; import java.io.Serializable; import java.util.Map; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.exceptions.SRetryableException; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.events.model.SFireEventException; import org.bonitasoft.engine.scheduler.JobIdentifier; import org.bonitasoft.engine.scheduler.JobService; import org.bonitasoft.engine.scheduler.StatelessJob; import org.bonitasoft.engine.scheduler.exception.SJobConfigurationException; import org.bonitasoft.engine.scheduler.exception.SJobExecutionException; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.transaction.BonitaTransactionSynchronization; import org.bonitasoft.engine.transaction.STransactionException; import org.bonitasoft.engine.transaction.STransactionNotFoundException; import org.bonitasoft.engine.transaction.TransactionService; /** * @author Matthieu Chaffotte * @author Celine Souchet */ @Slf4j public class JobWrapper implements StatelessJob { @Serial private static final long serialVersionUID = 7145451610635400449L; private final StatelessJob statelessJob; private final SEvent jobExecuting; private final SEvent jobCompleted; private final EventService eventService; private final JobIdentifier jobIdentifier; private final TransactionService transactionService; private final PersistenceService persistenceService; private final JobService jobService; public JobWrapper(final JobIdentifier jobIdentifier, final StatelessJob statelessJob, final EventService eventService, final TransactionService transactionService, PersistenceService persistenceService, JobService jobService) { this.jobIdentifier = jobIdentifier; this.statelessJob = statelessJob; this.eventService = eventService; this.transactionService = transactionService; this.persistenceService = persistenceService; this.jobService = jobService; jobExecuting = new SEvent(JOB_EXECUTING); jobCompleted = new SEvent(JOB_COMPLETED); } @Override public String getName() { return jobIdentifier.getJobName(); } @Override public String getDescription() { return statelessJob.getDescription(); } @Override public void execute() throws SJobExecutionException, SFireEventException { try { if (eventService.hasHandlers(JOB_EXECUTING, null)) { jobExecuting.setObject(this); eventService.fireEvent(jobExecuting); } log.debug("Start execution of {}", statelessJob.getName()); statelessJob.execute(); //make sure hibernate flush everything we did before going back to quartz code persistenceService.flushStatements(); log.debug("Finished execution of {}", statelessJob.getName()); } catch (final SRetryableException e) { throw e; } catch (final Throwable e) { handleFailure(e); //throw an exception: if it's a "one shot" timer it should delete the timer trigger only if job succeeded (see TimerEventTriggerJobListener) throw new SJobExecutionException(e); } finally { if (eventService.hasHandlers(JOB_COMPLETED, null)) { jobCompleted.setObject(this); eventService.fireEvent(jobCompleted); } } } void handleFailure(Throwable e) { log.error("Error while executing job {} : {}", jobIdentifier, e.getMessage(), e); try { registerFailInAnOtherThread(e, jobIdentifier); transactionService.setRollbackOnly(); } catch (STransactionException | STransactionNotFoundException e1) { log.error("Unable to rollback transaction after fail on job {}", jobIdentifier.getId(), e); } } private void registerFailInAnOtherThread(final Throwable jobException, final JobIdentifier jobIdentifier) throws STransactionNotFoundException { transactionService.registerBonitaSynchronization((BonitaTransactionSynchronization) txState -> { Thread thread = new Thread(() -> { try { transactionService.executeInTransaction(() -> { jobService.logJobError(jobException, jobIdentifier.getId()); return null; }); } catch (Exception e) { log.error("Error while registering the error for the job {}", jobIdentifier.getId(), e); log.error("job exception was ", jobException); } }, "Job error handler"); thread.start(); try { thread.join(); } catch (InterruptedException e) { log.error("Thread to log error on job {} interrupted", jobIdentifier.getId(), e); } }); } @Override public void setAttributes(final Map attributes) throws SJobConfigurationException { statelessJob.setAttributes(attributes); } public StatelessJob getStatelessJob() { return statelessJob; } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/MonitoringJobListener.java ================================================ /** * 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.scheduler.impl; import java.io.Serializable; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import org.bonitasoft.engine.scheduler.BonitaJobListener; public class MonitoringJobListener implements BonitaJobListener { private static final long serialVersionUID = 2830540082890033377L; public static final String JOB_JOBS_RUNNING = "bonita.bpmengine.job.running"; public static final String JOB_JOBS_EXECUTED = "bonita.bpmengine.job.executed"; private AtomicLong runningJobs; private Counter executedCounter; private final MeterRegistry meterRegistry; public MonitoringJobListener(MeterRegistry meterRegistry) { this.meterRegistry = meterRegistry; } @Override public void jobToBeExecuted() { initializeOrGetRunningJob().incrementAndGet(); } private AtomicLong initializeOrGetRunningJob() { if (runningJobs == null) { synchronized (this) { runningJobs = new AtomicLong(); Gauge.builder(JOB_JOBS_RUNNING, runningJobs, AtomicLong::get) .baseUnit("jobs") .description("Number of jobs currently running") .register(meterRegistry); } } return runningJobs; } private Counter initializeOrGetExecutedJobs() { if (executedCounter == null) { synchronized (this) { executedCounter = meterRegistry.counter(JOB_JOBS_EXECUTED); } } return executedCounter; } @Override public void jobExecutionVetoed() { initializeOrGetRunningJob().decrementAndGet(); } @Override public void jobWasExecuted(final Map context, final Exception jobException) { initializeOrGetRunningJob().decrementAndGet(); initializeOrGetExecutedJobs().increment(); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/NonConcurrentQuartzJob.java ================================================ /** * 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.scheduler.impl; import org.quartz.DisallowConcurrentExecution; @DisallowConcurrentExecution public class NonConcurrentQuartzJob extends AbstractQuartzJob { } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/QuartzJobListener.java ================================================ /** * 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.scheduler.impl; import static org.bonitasoft.engine.scheduler.BonitaJobListener.*; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.scheduler.BonitaJobListener; import org.bonitasoft.engine.scheduler.StatelessJob; import org.bonitasoft.engine.scheduler.model.SJobData; import org.bonitasoft.engine.scheduler.model.impl.SJobDataImpl; import org.quartz.Job; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobKey; import org.quartz.JobListener; import org.quartz.Trigger; import org.quartz.TriggerKey; @Slf4j public class QuartzJobListener implements JobListener { private final List bonitaJobListeners; QuartzJobListener(final List bonitaJobListeners) { this.bonitaJobListeners = bonitaJobListeners; } @Override public String getName() { return "QuartzJobListener"; } List getBonitaJobListeners() { return bonitaJobListeners; } @Override public void jobToBeExecuted(final JobExecutionContext context) { executeCallable(() -> { for (final BonitaJobListener bonitaJobListener : bonitaJobListeners) { bonitaJobListener.jobToBeExecuted(); } return null; }); } @Override public void jobExecutionVetoed(final JobExecutionContext context) { executeCallable(() -> { for (final BonitaJobListener bonitaJobListener : bonitaJobListeners) { bonitaJobListener.jobExecutionVetoed(); } return null; }); } @Override public void jobWasExecuted(final JobExecutionContext context, final JobExecutionException jobException) { final Map mapContext = buildMapContext(context); executeCallable(() -> { for (final BonitaJobListener bonitaJobListener : bonitaJobListeners) { bonitaJobListener.jobWasExecuted(mapContext, jobException); } return null; }); } private void executeCallable(Callable callable) { try { callable.call(); } catch (Throwable e) { log.warn("Unable to execute job listener", e); } } private Long getJobDescriptorId(final JobDetail jobDetail) { return Long.valueOf((String) jobDetail.getJobDataMap().getWrappedMap().get("jobId")); } private StatelessJob getBosJob(final JobExecutionContext context) { final Job instance = context.getJobInstance(); if (instance instanceof AbstractQuartzJob job) { return job.getBosJob(); } return null; } private List getJobDataValueAndType(final JobDetail jobDetail) { final Set> entries = jobDetail.getJobDataMap().getWrappedMap().entrySet(); final List jobDatas = new ArrayList(entries.size()); for (final Map.Entry entry : entries) { jobDatas.add(new SJobDataImpl(entry)); } return jobDatas; } private String getJobType(final Job job) { final String jobType; final Class jobClass = job.getClass(); if (AbstractQuartzJob.class.isAssignableFrom(jobClass)) { final StatelessJob bosJob = ((AbstractQuartzJob) job).getBosJob(); if (bosJob != null) { if (bosJob instanceof JobWrapper) { jobType = ((JobWrapper) bosJob).getStatelessJob().getClass().getName(); } else { jobType = bosJob.getClass().getName(); } } else { return "null"; } } else { jobType = jobClass.getName(); } return jobType; } private Map buildMapContext(final JobExecutionContext context) { final JobDetail jobDetail = context.getJobDetail(); final Trigger trigger = context.getTrigger(); final TriggerKey triggerKey = trigger.getKey(); final JobKey jobKey = jobDetail.getKey(); final Map mapContext = new HashMap<>(); mapContext.put(BOS_JOB, getBosJob(context)); mapContext.put(JOB_DESCRIPTOR_ID, getJobDescriptorId(jobDetail)); mapContext.put(JOB_TYPE, getJobType(context.getJobInstance())); mapContext.put(JOB_NAME, jobKey.getName()); mapContext.put(JOB_GROUP, jobKey.getGroup()); mapContext.put(TRIGGER_NAME, triggerKey.getName()); mapContext.put(TRIGGER_GROUP, triggerKey.getGroup()); mapContext.put(TRIGGER_PREVIOUS_FIRE_TIME, trigger.getPreviousFireTime()); mapContext.put(TRIGGER_NEXT_FIRE_TIME, trigger.getNextFireTime()); mapContext.put(REFIRE_COUNT, context.getRefireCount()); mapContext.put(JOB_DATAS, (Serializable) getJobDataValueAndType(jobDetail)); mapContext.put(JOB_RESULT, String.valueOf(context.getResult())); return mapContext; } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/QuartzSchedulerExecutor.java ================================================ /** * 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.scheduler.impl; import static org.quartz.JobKey.jobKey; import static org.quartz.impl.matchers.GroupMatcher.anyJobGroup; import java.time.Instant; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.UUID; import javax.transaction.Status; import org.bonitasoft.engine.commons.ExceptionUtils; import org.bonitasoft.engine.scheduler.BonitaJobListener; import org.bonitasoft.engine.scheduler.SchedulerExecutor; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; import org.bonitasoft.engine.scheduler.trigger.CronTrigger; import org.bonitasoft.engine.scheduler.trigger.Trigger; import org.bonitasoft.engine.transaction.BonitaTransactionSynchronization; import org.bonitasoft.engine.transaction.STransactionNotFoundException; import org.bonitasoft.engine.transaction.TransactionService; import org.quartz.CronScheduleBuilder; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.JobKey; import org.quartz.ListenerManager; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger.TriggerState; import org.quartz.TriggerBuilder; import org.quartz.TriggerKey; import org.quartz.core.QuartzScheduler; import org.quartz.impl.matchers.GroupMatcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Matthieu Chaffotte * @author Yanyan Liu * @author Celine Souchet */ public class QuartzSchedulerExecutor implements SchedulerExecutor { private final Logger logger = LoggerFactory.getLogger(QuartzSchedulerExecutor.class); private Scheduler scheduler; private final BonitaSchedulerFactory schedulerFactory; private final TransactionService transactionService; private final boolean useOptimization; private QuartzScheduler quartzScheduler; private List jobListeners = new ArrayList<>(); public QuartzSchedulerExecutor(final BonitaSchedulerFactory schedulerFactory, final TransactionService transactionService, final boolean useOptimization) { this.transactionService = transactionService; this.useOptimization = useOptimization; this.schedulerFactory = schedulerFactory; } // autowired public void setJobListeners(List jobListeners) { this.jobListeners = jobListeners; } @Override public void setBOSSchedulerService(final SchedulerServiceImpl schedulerService) { schedulerFactory.setBOSSchedulerService(schedulerService); } @Override public void schedule(final long jobId, final String jobName, final Trigger trigger, final boolean disallowConcurrentExecution) throws SSchedulerException { try { checkSchedulerState(); final JobDetail jobDetail = createJobDetails(jobId, jobName, disallowConcurrentExecution); final JobKey jobKey = jobDetail.getKey(); final org.quartz.Trigger quartzTrigger = getQuartzTrigger(trigger, jobKey.getName()); scheduler.scheduleJob(jobDetail, quartzTrigger); if (useOptimization) { transactionService.registerBonitaSynchronization( new NotifyQuartzOfNewTrigger(trigger.getStartDate().getTime(), quartzScheduler)); } } catch (final Exception e) { throw new SSchedulerException(e); } } private static final class NotifyQuartzOfNewTrigger implements BonitaTransactionSynchronization { private final long time; private final QuartzScheduler quartzScheduler; public NotifyQuartzOfNewTrigger(final long time, final QuartzScheduler quartzScheduler) { super(); this.time = time; this.quartzScheduler = quartzScheduler; } @Override public void afterCompletion(final int txState) { if (Status.STATUS_COMMITTED == txState) { if (quartzScheduler != null) { quartzScheduler.getSchedulerSignaler().signalSchedulingChange(time); } } } } private JobDetail createJobDetails(final long jobId, final String jobName, final boolean disallowConcurrentExecution) { Class jobClass; if (disallowConcurrentExecution) { jobClass = NonConcurrentQuartzJob.class; } else { jobClass = ConcurrentQuartzJob.class; } final JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName).build(); jobDetail.getJobDataMap().put("jobId", String.valueOf(jobId)); jobDetail.getJobDataMap().put("jobName", jobName); return jobDetail; } @Override public void executeAgain(final long jobId, final String jobName, final boolean disallowConcurrentExecution, int delayInMillis) throws SSchedulerException { checkSchedulerState(); try { JobDetail jobDetail = scheduler.getJobDetail(new JobKey(jobName)); if (jobDetail == null) { logger.debug("Re-execute job {} named {}, there was no quartz job and no triggers left " + "(one shot triggered that failed and was deleted)", jobId, jobName); // The quartz job itself was deleted because the trigger that failed was the only one and was a one shot trigger scheduler.scheduleJob(createJobDetails(jobId, jobName, disallowConcurrentExecution), createOneShotTrigger(jobName, delayInMillis)); } else { List triggersOfJob = scheduler.getTriggersOfJob(jobDetail.getKey()); // We retrieve the first trigger that will not fire again Optional firstTriggerThatWillNotFire = triggersOfJob.stream() .filter(t -> !t.mayFireAgain()).findFirst(); if (firstTriggerThatWillNotFire.isPresent()) { // if there is one, we reschedule the job by replacing it logger.debug("Re-execute job {} named {}, reuse existing trigger {} because it will not fire again." + "(most likely a one shot trigger that failed and was not correctly deleted)", jobId, jobName, firstTriggerThatWillNotFire.get()); scheduler.rescheduleJob(firstTriggerThatWillNotFire.get().getKey(), createOneShotTrigger(jobName, delayInMillis)); } else { // in the other case we create a new trigger to schedule the job (it means other triggers are likely cron triggers) logger.debug("Re-execute job {} named {}, create a new trigger for that. " + "(The job that failed was most likely triggered by a cron trigger)", jobId, jobName); scheduler.scheduleJob(createOneShotTrigger(jobName, delayInMillis)); } } if (useOptimization) { transactionService.registerBonitaSynchronization( new NotifyQuartzOfNewTrigger(System.currentTimeMillis(), quartzScheduler)); } } catch (final Exception e) { throw new SSchedulerException(e); } } private org.quartz.Trigger createOneShotTrigger(String jobName, int delayInMillis) { return TriggerBuilder.newTrigger() .withIdentity("OneShotTrigger" + UUID.randomUUID().getLeastSignificantBits()) .forJob(jobName) .startAt(new Date(Instant.now().plusMillis(delayInMillis).toEpochMilli())).build(); } org.quartz.Trigger getQuartzTrigger(final Trigger trigger, final String jobName) { final TriggerBuilder triggerBuilder; final TriggerBuilder base = TriggerBuilder.newTrigger().forJob(jobName) .withIdentity(trigger.getName()) .startNow(); if (trigger instanceof CronTrigger cronTrigger) { final CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder .cronSchedule(cronTrigger.getExpression()); switch (cronTrigger.getMisfireHandlingPolicy()) { case NONE: cronScheduleBuilder.withMisfireHandlingInstructionDoNothing(); break; case ALL: cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires(); break; case ONE: cronScheduleBuilder.withMisfireHandlingInstructionFireAndProceed(); break; default: throw new IllegalStateException(); } triggerBuilder = base.withSchedule(cronScheduleBuilder).endAt(cronTrigger.getEndDate()); } else { triggerBuilder = base.startAt(trigger.getStartDate()); } return triggerBuilder.withPriority(trigger.getPriority()).build(); } @Override public boolean isStarted() throws SSchedulerException { try { return scheduler != null && scheduler.isStarted() && !scheduler.isShutdown(); } catch (final SchedulerException e) { throw new SSchedulerException(e); } } @Override public boolean isShutdown() throws SSchedulerException { try { return scheduler != null && scheduler.isShutdown(); } catch (final SchedulerException e) { throw new SSchedulerException(e); } } @Override public void start() throws SSchedulerException { try { if (isStarted()) { throw new SSchedulerException("The scheduler is already started."); } if (scheduler == null || scheduler.isShutdown()) { try { scheduler = schedulerFactory.getScheduler(); } catch (final SchedulerException e) { throw new SSchedulerException(e); } } scheduler.start(); addListeners(); if (useOptimization) { if (scheduler instanceof BonitaScheduler) { quartzScheduler = ((BonitaScheduler) scheduler).getQuartzScheduler(); } else { logger.warn( "Cannot access the QuartzScheduler implementation from {}. Scheduler optimization is disabled.", scheduler.getClass().getName()); } } } catch (final SchedulerException e) { throw new SSchedulerException(e); } } @Override public void shutdown() throws SSchedulerException { try { if (scheduler != null && !scheduler.isShutdown()) { scheduler.shutdown(true); } } catch (final SchedulerException e) { throw new SSchedulerException(e); } } private void checkSchedulerState() throws SSchedulerException { try { if (scheduler == null || scheduler.isShutdown()) { throw new SSchedulerException("The scheduler is not started"); } if (!transactionService.isTransactionActive()) { throw new SSchedulerException("The scheduler cannot be used without opening a transaction first"); } } catch (SchedulerException e) { throw new SSchedulerException("The scheduler is not started", e); } } @Override public boolean delete(final String jobName) throws SSchedulerException { try { checkSchedulerState(); final JobKey jobKey = jobKey(jobName); scheduler.pauseJob(jobKey); return scheduler.deleteJob(jobKey); } catch (final SchedulerException e) { throw new SSchedulerException(e); } } @Override public void deleteJobs() throws SSchedulerException { try { checkSchedulerState(); final Set jobNames = scheduler.getJobKeys(anyJobGroup()); for (final JobKey jobKey : jobNames) { scheduler.pauseJob(jobKey); scheduler.deleteJob(jobKey); } } catch (final SchedulerException e) { throw new SSchedulerException(e); } } @Override public boolean isExistingJob(final String jobName) throws SSchedulerException { try { checkSchedulerState(); final JobKey jobKey = jobKey(jobName); return scheduler.getJobDetail(jobKey) != null; } catch (final SchedulerException e) { throw new SSchedulerException(e); } } @Override public List getJobs() throws SSchedulerException { try { checkSchedulerState(); final Set jobKeys = scheduler.getJobKeys(anyJobGroup()); final List jobsNames = new ArrayList<>(jobKeys.size()); for (final JobKey jobKey : jobKeys) { jobsNames.add(jobKey.getName()); } return jobsNames; } catch (final SchedulerException e) { throw new SSchedulerException(e); } } @Override public boolean mayFireAgain(final String jobName) throws SSchedulerException { try { checkSchedulerState(); List triggersOfJob = scheduler .getTriggersOfJob(new JobKey(jobName)); return triggersOfJob.stream() .anyMatch(org.quartz.Trigger::mayFireAgain); } catch (final SchedulerException e) { throw new SSchedulerException(e); } } @Override public void rescheduleErroneousTriggers() throws SSchedulerException { checkSchedulerState(); try { for (final TriggerKey triggerKey : scheduler.getTriggerKeys(GroupMatcher.anyTriggerGroup())) { if (TriggerState.ERROR.equals(scheduler.getTriggerState(triggerKey))) { scheduler.pauseTrigger(triggerKey); scheduler.resumeTrigger(triggerKey); } } } catch (final SchedulerException e) { throw new SSchedulerException(e); } } @Override public void pauseJobs() throws SSchedulerException { checkSchedulerState(); try { scheduler.pauseTriggers(GroupMatcher.anyTriggerGroup()); } catch (final SchedulerException e) { throw new SSchedulerException("Unable to put jobs in pause", e); } } @Override public void resumeJobs() throws SSchedulerException { checkSchedulerState(); try { scheduler.resumeTriggers(GroupMatcher.anyTriggerGroup()); } catch (final SchedulerException e) { throw new SSchedulerException("Unable to resume jobs", e); } } @Override public Date rescheduleJob(final String triggerName, final Date triggerStartTime) throws SSchedulerException { checkSchedulerState(); final TriggerKey triggerKey = new TriggerKey(triggerName); try { final org.quartz.Trigger oldTrigger = scheduler.getTrigger(triggerKey); final org.quartz.Trigger newTrigger = oldTrigger.getTriggerBuilder().startAt(triggerStartTime).build(); Date date = scheduler.rescheduleJob(triggerKey, newTrigger); if (useOptimization) { try { transactionService.registerBonitaSynchronization( new NotifyQuartzOfNewTrigger(triggerStartTime.getTime(), quartzScheduler)); } catch (STransactionNotFoundException e) { logger.error("Unable to register synchronization to optimize Quartz rescheduling, {}", ExceptionUtils.printLightWeightStacktrace(e)); } } return date; } catch (final SchedulerException e) { throw new SSchedulerException("Can't get the trigger " + triggerKey, e); } } // For tests only public Set getPausedTriggerGroups() throws SSchedulerException { try { return quartzScheduler.getPausedTriggerGroups(); } catch (SchedulerException e) { throw new SSchedulerException(e); } } private void addListeners() throws SSchedulerException { try { final ListenerManager listenerManager = scheduler.getListenerManager(); listenerManager.addJobListener(new QuartzJobListener(jobListeners)); } catch (final SchedulerException e) { throw new SSchedulerException(e); } } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/SchedulerServiceImpl.java ================================================ /** * 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.scheduler.impl; import java.io.Serializable; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.events.model.SFireEventException; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.scheduler.JobIdentifier; import org.bonitasoft.engine.scheduler.JobService; import org.bonitasoft.engine.scheduler.SchedulerExecutor; import org.bonitasoft.engine.scheduler.SchedulerService; import org.bonitasoft.engine.scheduler.StatelessJob; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; import org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogDeletionException; import org.bonitasoft.engine.scheduler.model.SJobDescriptor; import org.bonitasoft.engine.scheduler.model.SJobParameter; import org.bonitasoft.engine.scheduler.trigger.Trigger; import org.bonitasoft.engine.service.ServicesResolver; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.transaction.TransactionService; /** * @author Matthieu Chaffotte * @author Baptiste Mesta * @author Elias Ricken de Medeiros * @author Yanyan Liu * @author Celine Souchet */ @Slf4j public class SchedulerServiceImpl implements SchedulerService { private final SchedulerExecutor schedulerExecutor; private final JobService jobService; private final EventService eventService; private final SEvent schedulerStarted; private final SEvent schedulerStopped; private final SEvent jobFailed; private final TransactionService transactionService; private final ServicesResolver servicesResolver; private final PersistenceService persistenceService; /** * Create a new instance of scheduler service. */ public SchedulerServiceImpl(final SchedulerExecutor schedulerExecutor, final JobService jobService, final EventService eventService, final TransactionService transactionService, final ServicesResolver servicesResolver, final PersistenceService persistenceService) { this.schedulerExecutor = schedulerExecutor; this.jobService = jobService; this.servicesResolver = servicesResolver; this.persistenceService = persistenceService; schedulerStarted = new SEvent(SCHEDULER_STARTED); schedulerStopped = new SEvent(SCHEDULER_STOPPED); jobFailed = new SEvent(JOB_FAILED); this.eventService = eventService; this.transactionService = transactionService; schedulerExecutor.setBOSSchedulerService(this); } @Override public void schedule(final SJobDescriptor jobDescriptor, final Trigger trigger) throws SSchedulerException { final SJobDescriptor createdJobDescriptor = createJobDescriptor(jobDescriptor, Collections.emptyList()); internalSchedule(createdJobDescriptor, trigger); } @Override public void schedule(final SJobDescriptor jobDescriptor, final List parameters, final Trigger trigger) throws SSchedulerException { if (trigger == null) { throw new SSchedulerException("The trigger is null"); } final SJobDescriptor createdJobDescriptor = createJobDescriptor(jobDescriptor, parameters); internalSchedule(createdJobDescriptor, trigger); } @Override public void executeAgain(final long jobDescriptorId, int delayInMillis) throws SSchedulerException { final SJobDescriptor jobDescriptor = jobService.getJobDescriptor(jobDescriptorId); schedulerExecutor.executeAgain(jobDescriptorId, jobDescriptor.getJobName(), false, delayInMillis); } @Override public void retryJobThatFailed(long jobDescriptorId) throws SSchedulerException { final SJobDescriptor jobDescriptor = jobService.getJobDescriptor(jobDescriptorId); deleteFailedJobs(jobDescriptorId); schedulerExecutor.executeAgain(jobDescriptorId, jobDescriptor.getJobName(), false, 0); } @Override public void retryJobThatFailed(final long jobDescriptorId, final List parameters) throws SSchedulerException { final SJobDescriptor jobDescriptor = jobService.getJobDescriptor(jobDescriptorId); jobService.setJobParameters(jobDescriptor.getId(), parameters); deleteFailedJobs(jobDescriptorId); schedulerExecutor.executeAgain(jobDescriptorId, jobDescriptor.getJobName(), false, 0); } private void deleteFailedJobs(long jobDescriptorId) throws SSchedulerException { try { jobService.deleteJobLogs(jobDescriptorId); } catch (SJobLogDeletionException | SBonitaReadException e) { throw new SSchedulerException("Unable to delete failed jobs logs", e); } } private SJobDescriptor createJobDescriptor(final SJobDescriptor sJobDescriptor, final List parameters) throws SSchedulerException { try { final SJobDescriptor createdJobDescriptor = jobService.createJobDescriptor(sJobDescriptor); jobService.createJobParameters(parameters, createdJobDescriptor.getId()); return createdJobDescriptor; } catch (final SBonitaException sbe) { throw new SSchedulerException(sbe); } } private void internalSchedule(final SJobDescriptor jobDescriptor, final Trigger trigger) throws SSchedulerException { try { schedulerExecutor.schedule(jobDescriptor.getId(), jobDescriptor.getJobName(), trigger, false); } catch (final Throwable e) { log.error("", e); try { eventService.fireEvent(jobFailed); } catch (final SFireEventException e1) { log.error("", e1); } throw new SSchedulerException(e); } } @Override public boolean isStarted() throws SSchedulerException { return schedulerExecutor.isStarted(); } @Override public boolean isStopped() throws SSchedulerException { return schedulerExecutor.isShutdown(); } @Override public void start() throws SSchedulerException, SFireEventException { log.info("Start scheduler"); schedulerExecutor.start(); eventService.fireEvent(schedulerStarted); } @Override public void stop() throws SSchedulerException, SFireEventException { schedulerExecutor.shutdown(); eventService.fireEvent(schedulerStopped); } @Override public void pauseJobs() throws SSchedulerException { schedulerExecutor.pauseJobs(); } @Override public void resumeJobs() throws SSchedulerException { schedulerExecutor.resumeJobs(); } @Override public boolean delete(final String jobName) throws SSchedulerException { final boolean delete = schedulerExecutor.delete(jobName); jobService.deleteJobDescriptorByJobName(jobName); return delete; } @Override public void deleteJobs() throws SSchedulerException { schedulerExecutor.deleteJobs(); jobService.deleteAllJobDescriptors(); } @Override public List getJobs() throws SSchedulerException { return schedulerExecutor.getJobs(); } /** * Get the persisted job from the database inside its own transaction. * * @return the newly created job * @throws SSchedulerException if the job cannot be created successfully */ StatelessJob getPersistedJob(final JobIdentifier jobIdentifier) throws SSchedulerException { try { return transactionService.executeInTransaction(new PersistedJobCallable(jobIdentifier)); } catch (final Exception e) { throw new SSchedulerException(e); } } private class PersistedJobCallable implements Callable { private final JobIdentifier jobIdentifier; PersistedJobCallable(final JobIdentifier jobIdentifier) { this.jobIdentifier = jobIdentifier; } @Override public JobWrapper call() throws Exception { final SJobDescriptor sJobDescriptor = jobService.getJobDescriptor(jobIdentifier.getId()); if (sJobDescriptor == null) { throw new SObjectNotFoundException(String .format("The job %s does not exist anymore. It might be already executed", jobIdentifier)); } final String jobClassName = sJobDescriptor.getJobClassName(); final Class jobClass = Class.forName(jobClassName); final StatelessJob statelessJob = (StatelessJob) jobClass.newInstance(); Map parameters = jobService.getJobParameters(jobIdentifier.getId()) .stream() .collect(Collectors.toMap(SJobParameter::getKey, SJobParameter::getValue)); parameters.put(StatelessJob.JOB_DESCRIPTOR_ID, jobIdentifier.getId()); statelessJob.setAttributes(parameters); if (servicesResolver != null) { servicesResolver.injectServices(statelessJob); } return new JobWrapper(jobIdentifier, statelessJob, eventService, transactionService, persistenceService, jobService); } } @Override public void pause() throws SBonitaException { pauseJobs(); } @Override public void resume() throws SBonitaException { resumeJobs(); } @Override public void rescheduleErroneousTriggers() throws SSchedulerException { schedulerExecutor.rescheduleErroneousTriggers(); } @Override public Date rescheduleJob(final String triggerName, final Date triggerStartTime) throws SSchedulerException { return schedulerExecutor.rescheduleJob(triggerName, triggerStartTime); } @Override public boolean isExistingJob(final String jobName) throws SSchedulerException { return schedulerExecutor.isExistingJob(jobName); } @Override public boolean mayFireAgain(String jobName) throws SSchedulerException { return schedulerExecutor.mayFireAgain(jobName); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/impl/TransactionalSimpleJobFactory.java ================================================ /** * 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.scheduler.impl; import org.quartz.Job; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.simpl.SimpleJobFactory; import org.quartz.spi.TriggerFiredBundle; /** * Job factory that inject the transaction service * Must modify this to inject the configuration service instead * * @author Baptiste Mesta * @author Matthieu Chaffotte * @author Celine Souchet */ public final class TransactionalSimpleJobFactory extends SimpleJobFactory { private final SchedulerServiceImpl schedulerService; public TransactionalSimpleJobFactory(final SchedulerServiceImpl schedulerService) { this.schedulerService = schedulerService; } @Override public Job newJob(final TriggerFiredBundle bundle, final Scheduler scheduler) throws SchedulerException { final Job newJob = super.newJob(bundle, scheduler); if (newJob instanceof AbstractQuartzJob) { ((AbstractQuartzJob) newJob).setJobDetails(bundle.getJobDetail()); ((AbstractQuartzJob) newJob).setSchedulerService(schedulerService); } return newJob; } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/model/SFailedJob.java ================================================ /** * 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.scheduler.model; /** * @author Matthieu Chaffotte */ public interface SFailedJob { long getJobDescriptorId(); String getJobName(); String getDescription(); String getLastMessage(); int getNumberOfFailures(); long getLastUpdateDate(); } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/model/SJobData.java ================================================ /** * 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.scheduler.model; import java.io.Serializable; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ public interface SJobData extends Serializable { String getKey(); Object getValue(); String getClassOfValue(); } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/model/SJobDescriptor.java ================================================ /** * 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.scheduler.model; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "job_desc") public class SJobDescriptor implements PersistentObject { public static final String JOB_NAME = "jobName"; public static final String JOB_CLASS_NAME = "jobClassName"; public static final String ID = "id"; public static final String DESCRIPTION = "description"; @Id private long id; private String jobClassName; private String jobName; private String description; public SJobDescriptor(final String jobClassName, final String jobName, final String description) { super(); this.jobClassName = jobClassName; this.jobName = jobName; this.description = description; } public SJobDescriptor(final String jobClassName, final String jobName) { super(); this.jobClassName = jobClassName; this.jobName = jobName; } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/model/SJobLog.java ================================================ /** * 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.scheduler.model; import javax.persistence.Cacheable; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Celine Souchet */ @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "job_log") @Cacheable(false) public class SJobLog implements PersistentObject { @Id private long id; private long jobDescriptorId; private long retryNumber; private Long lastUpdateDate; private String lastMessage; public SJobLog(final long jobDescriptorId) { super(); this.jobDescriptorId = jobDescriptorId; } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/model/SJobParameter.java ================================================ /** * 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.scheduler.model; import java.io.Serializable; import javax.persistence.Cacheable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.bonitasoft.engine.persistence.PersistentObject; import org.hibernate.annotations.Type; @Data @NoArgsConstructor @AllArgsConstructor @Builder @Entity @Table(name = "job_param") @Cacheable(false) public class SJobParameter implements PersistentObject { public static final String JOB_DESCRIPTOR_ID = "jobDescriptorId"; public static final String KEY = "key"; public static final String VALUE = "value"; @Id private long id; private long jobDescriptorId; @Column(name = "key_") private String key; @Column(name = "value_") @Type(type = "serializable") private Serializable value; public SJobParameter(final String key, final Serializable value) { this.key = key; this.value = value; } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/model/impl/SFailedJobImpl.java ================================================ /** * 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.scheduler.model.impl; import lombok.Data; import org.bonitasoft.engine.scheduler.model.SFailedJob; /** * @author Matthieu Chaffotte */ @Data public class SFailedJobImpl implements SFailedJob { private final long jobDescriptorId; private final String jobName; private final String description; private final int retryNumber; private final long lastUpdateDate; private final String lastMessage; public SFailedJobImpl(final long jobDescriptorId, final String jobName, final String description, long retryNumber, final long lastUpdateDate, final String lastMessage) { this.jobDescriptorId = jobDescriptorId; this.jobName = jobName; this.description = description; this.retryNumber = (int) retryNumber; this.lastUpdateDate = lastUpdateDate; this.lastMessage = lastMessage; } @Override public int getNumberOfFailures() { // we want the number of failures, not the number of retry return retryNumber + 1; } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/model/impl/SJobDataImpl.java ================================================ /** * 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.scheduler.model.impl; import java.util.Map.Entry; import lombok.Data; import org.bonitasoft.engine.scheduler.model.SJobData; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ @Data public class SJobDataImpl implements SJobData { private final String key; private final Object value; final String classOfValue; public SJobDataImpl(final Entry jobData) { key = jobData.getKey(); value = jobData.getValue(); classOfValue = value.getClass().getName(); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/recorder/JobDescriptorRecordType.java ================================================ /** * 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.scheduler.recorder; /** * List all possible change of elements of the scheduler service * * @author Baptiste Mesta */ public enum JobDescriptorRecordType { addJobDescriptor, addJobParameter, } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/recorder/SelectDescriptorBuilder.java ================================================ /** * 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.scheduler.recorder; import java.util.Collections; import java.util.Map; import org.bonitasoft.engine.persistence.PersistentObject; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.scheduler.model.SFailedJob; import org.bonitasoft.engine.scheduler.model.SJobDescriptor; /** * @author Celine Souchet */ public class SelectDescriptorBuilder { // FIXME put in a common model public static SelectByIdDescriptor getElementById(final Class clazz, final String elementName, final long id) { return new SelectByIdDescriptor(clazz, id); } public static SelectListDescriptor getFailedJobs(final QueryOptions queryOptions) { final Map parameters = Collections.emptyMap(); return new SelectListDescriptor("getFailedJobs", parameters, SJobDescriptor.class, queryOptions); } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/trigger/CronTrigger.java ================================================ /** * 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.scheduler.trigger; import java.util.Date; /** * @author Matthieu Chaffotte */ public interface CronTrigger extends Trigger { /** * Returns the expression as a unix-like cron expressions * "* * * * * ?" * 1. seconds * 2. minutes * 3. hours * 4. day of the month * 5. month * 6 day of the week * 7 year (optional) * * @return a unix-like CRON expression */ String getExpression(); /** * Returns when the trigger ends. It can be null which means that the trigger never ends * * @return */ Date getEndDate(); } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/trigger/OneShotTrigger.java ================================================ /** * 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.scheduler.trigger; import java.util.Date; /** * @author Matthieu Chaffotte */ public class OneShotTrigger implements Trigger { private final String name; private final Date startDate; private final int priority; private final MisfireRestartPolicy misfireHandlingPolicy; public OneShotTrigger(final String name, final Date startDate, final int priority) { this(name, startDate, priority, MisfireRestartPolicy.ALL); } public OneShotTrigger(final String name, final Date startDate, final int priority, final MisfireRestartPolicy misfireHandlingPolicy) { this.name = name; this.startDate = startDate; this.priority = priority; this.misfireHandlingPolicy = misfireHandlingPolicy; } public OneShotTrigger(final String name, final Date startDate, final MisfireRestartPolicy misfireHandlingPolicy) { this(name, startDate, 5, misfireHandlingPolicy); } public OneShotTrigger(final String name, final Date startDate) { this(name, startDate, 5, MisfireRestartPolicy.ALL); } @Override public String getName() { return name; } @Override public Date getStartDate() { return startDate; } @Override public int getPriority() { return priority; } @Override public MisfireRestartPolicy getMisfireHandlingPolicy() { return misfireHandlingPolicy; } } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/trigger/Trigger.java ================================================ /** * 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.scheduler.trigger; import java.util.Date; /** * @author Matthieu Chaffotte */ public interface Trigger { /** * Specify what to do when some job were not triggered in time. */ public enum MisfireRestartPolicy { /** * Restart now all instances that were missed */ ALL, /** * Restart now only one instance that was not trigger and continue normally */ ONE, /** * No instance that were not trigger in time are restarted */ NONE } /** * Gets the name of the trigger * * @return the name of the trigger * @since 6.0 */ String getName(); /** * Returns when the trigger must start * * @return a date when the trigger must start * @since 6.0 */ Date getStartDate(); /** * The trigger of the highest priority will be executed first. * * @return the trigger's priority * @since 6.0 */ int getPriority(); /** * Tell the scheduler how to handle jobs that were not executed in time. * * @return the MisfireHandlingPolicy for this trigger */ MisfireRestartPolicy getMisfireHandlingPolicy(); } ================================================ FILE: services/bonita-scheduler/src/main/java/org/bonitasoft/engine/scheduler/trigger/UnixCronTrigger.java ================================================ /** * 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.scheduler.trigger; import java.util.Date; /** * @author Matthieu Chaffotte */ public class UnixCronTrigger extends OneShotTrigger implements CronTrigger { private final String expression; private final Date endDate; public UnixCronTrigger(final String name, final Date startDate, final String expression) { super(name, startDate); this.expression = expression; this.endDate = null; } public UnixCronTrigger(final String name, final Date startDate, final String expression, final MisfireRestartPolicy misfireHandlingPolicy) { super(name, startDate, misfireHandlingPolicy); this.expression = expression; this.endDate = null; } @Override public String getExpression() { return this.expression; } @Override public Date getEndDate() { return this.endDate; } } ================================================ FILE: services/bonita-scheduler/src/main/resources/org/bonitasoft/engine/scheduler/impl/hibernate/schedulerimpl.queries.hbm.xml ================================================ SELECT COUNT(jd) FROM org.bonitasoft.engine.scheduler.model.SJobDescriptor AS jd SELECT jd FROM org.bonitasoft.engine.scheduler.model.SJobDescriptor AS jd SELECT COUNT(jp.id) FROM org.bonitasoft.engine.scheduler.model.SJobParameter AS jp SELECT jp FROM org.bonitasoft.engine.scheduler.model.SJobParameter AS jp WHERE jp.jobDescriptorId = :jobDescriptorId SELECT COUNT(jl.id) FROM org.bonitasoft.engine.scheduler.model.SJobLog AS jl SELECT jl FROM org.bonitasoft.engine.scheduler.model.SJobLog AS jl SELECT new org.bonitasoft.engine.scheduler.model.impl.SFailedJobImpl(jd.id, jd.jobName, jd.description, jl.retryNumber, jl.lastUpdateDate, jl.lastMessage) FROM org.bonitasoft.engine.scheduler.model.SJobLog AS jl, org.bonitasoft.engine.scheduler.model.SJobDescriptor AS jd WHERE jd.id = jl.jobDescriptorId ORDER BY jl.lastUpdateDate ASC ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/AbstractQuartzJobTest.java ================================================ /** * 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.scheduler.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.bonitasoft.engine.scheduler.impl.JobUtils.createJobDetails; import static org.bonitasoft.engine.scheduler.impl.JobUtils.jobThatFails; import static org.bonitasoft.engine.scheduler.impl.JobUtils.jobThatThrowASRetryableException; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.quartz.JobExecutionException; /** * @author Baptiste Mesta */ public class AbstractQuartzJobTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Mock private SchedulerServiceImpl schedulerService; private final ConcurrentQuartzJob abstractQuartzJob = new ConcurrentQuartzJob(); @Before public void before() { abstractQuartzJob.setSchedulerService(schedulerService); abstractQuartzJob.setJobDetails(createJobDetails(2)); } @Test public void should_not_unschedule_job_on_exception() throws Exception { // job should never throw an exception and be handled by the JobWrapper // we do not unschedule the job in that case. we don't want to lose the job doReturn(jobThatFails()).when(schedulerService).getPersistedJob(any()); try { abstractQuartzJob.execute(null); fail("should throw exception"); } catch (JobExecutionException e) { assertThat(e.unscheduleFiringTrigger()).isFalse(); assertThat(e.refireImmediately()).isFalse(); } } @Test public void should_retry_job_that_failed_with_SRetryable() throws Exception { doReturn(jobThatThrowASRetryableException()).when(schedulerService).getPersistedJob(any()); abstractQuartzJob.setSchedulerService(schedulerService); abstractQuartzJob.setJobDetails(createJobDetails(2)); abstractQuartzJob.execute(null); verify(schedulerService).executeAgain(2, 5000); } } ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/FailingJob.java ================================================ /** * 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.scheduler.impl; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class FailingJob implements Job { private FailingJob() { } @Override public void execute(final JobExecutionContext context) throws JobExecutionException { throw new JobExecutionException(); } } ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/JDBCJobListenerTest.java ================================================ /** * 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.scheduler.impl; import static org.bonitasoft.engine.scheduler.BonitaJobListener.TRIGGER_NEXT_FIRE_TIME; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.scheduler.BonitaJobListener; import org.bonitasoft.engine.scheduler.JobService; import org.bonitasoft.engine.scheduler.SchedulerService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Elias Ricken de Medeiros * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class JDBCJobListenerTest { private static final Long JOB_DESCRIPTOR_ID = 50L; private final Map context = new HashMap<>(); @Mock private JobService jobService; @Mock private SchedulerService schedulerService; private JDBCJobListener jdbcJobListener; @Before public void setUp() { context.put(BonitaJobListener.JOB_DESCRIPTOR_ID, JOB_DESCRIPTOR_ID); jdbcJobListener = new JDBCJobListener(jobService, schedulerService); } @Test public void should_delete_job_descriptor_when_job_may_not_fire_again() throws Exception { // Given context.put(BonitaJobListener.JOB_DESCRIPTOR_ID, JOB_DESCRIPTOR_ID); context.put(BonitaJobListener.JOB_NAME, "myJob"); doReturn(false).when(schedulerService).mayFireAgain("myJob"); // When jdbcJobListener.jobWasExecuted(context, null); // Then verify(jobService).deleteJobDescriptor(JOB_DESCRIPTOR_ID); } @Test public void should_not_delete_job_descriptor_when_job_may_not_fire_again_but_there_is_other_triggers() throws Exception { // Given context.put(BonitaJobListener.JOB_DESCRIPTOR_ID, JOB_DESCRIPTOR_ID); context.put(BonitaJobListener.JOB_NAME, "myJob"); doReturn(true).when(schedulerService).mayFireAgain("myJob"); // When jdbcJobListener.jobWasExecuted(context, null); // Then verify(jobService, never()).deleteJobDescriptor(JOB_DESCRIPTOR_ID); } @Test public void should_not_delete_job_descriptor_when_job_may_fire_again() throws Exception { // Given context.put(BonitaJobListener.JOB_DESCRIPTOR_ID, JOB_DESCRIPTOR_ID); context.put(TRIGGER_NEXT_FIRE_TIME, new Date()); // When jdbcJobListener.jobWasExecuted(context, null); // Then verify(jobService, never()).deleteJobDescriptor(JOB_DESCRIPTOR_ID); } @Test public void should_not_delete_job_descriptor_when_job_failed() throws Exception { // Given context.put(BonitaJobListener.JOB_DESCRIPTOR_ID, JOB_DESCRIPTOR_ID); context.put(TRIGGER_NEXT_FIRE_TIME, new Date()); // When jdbcJobListener.jobWasExecuted(context, new IllegalStateException()); // Then verify(jobService, never()).deleteJobDescriptor(JOB_DESCRIPTOR_ID); } @Test public void jobToBeExecuted_should_do_nothing() { // When jdbcJobListener.jobToBeExecuted(); // Then verifyNoInteractions(jobService); } } ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/JobServiceImplForFailedJobTest.java ================================================ /** * 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.scheduler.impl; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; import java.util.Collections; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectListDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.scheduler.exception.failedJob.SFailedJobReadException; import org.bonitasoft.engine.scheduler.model.SFailedJob; import org.bonitasoft.engine.services.QueriableLoggerService; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class JobServiceImplForFailedJobTest { @Mock private EventService eventService; @Mock private QueriableLoggerService queriableLoggerService; @Mock private ReadPersistenceService readPersistenceService; @Mock private Recorder recorder; @InjectMocks private JobServiceImpl jobServiceImpl; @Test public final void getFailedJobs() throws SBonitaReadException, SFailedJobReadException { // Given final SFailedJob sFailedJob = mock(SFailedJob.class); when(readPersistenceService.selectList(ArgumentMatchers.> any())) .thenReturn(Collections.singletonList(sFailedJob)); // When final SFailedJob result = jobServiceImpl.getFailedJobs(0, 10).get(0); // Then assertEquals(sFailedJob, result); verify(readPersistenceService).selectList(ArgumentMatchers.> any()); } @Test(expected = SFailedJobReadException.class) public void getFailedJobs_should_throw_exception_when_persistenceService_failed() throws SBonitaReadException, SFailedJobReadException { doThrow(new SBonitaReadException("")).when(readPersistenceService) .selectList(ArgumentMatchers.> any()); jobServiceImpl.getFailedJobs(0, 10); } } ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/JobServiceImplForJobDescriptorTest.java ================================================ /** * 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.scheduler.impl; import static java.util.Arrays.asList; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.util.Collections; import java.util.List; import java.util.Map; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorCreationException; import org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorDeletionException; import org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorReadException; import org.bonitasoft.engine.scheduler.model.SJobDescriptor; import org.bonitasoft.engine.scheduler.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.services.QueriableLoggerService; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class JobServiceImplForJobDescriptorTest { @Mock private EventService eventService; @Mock private QueriableLoggerService queriableLoggerService; @Mock private ReadPersistenceService readPersistenceService; @Mock private Recorder recorder; @Spy @InjectMocks private JobServiceImpl jobServiceImpl; @Test public final void createJobDescriptor_should_return_jobDescriptor() throws SJobDescriptorCreationException, SRecorderException { // Given final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class); doReturn("plop").when(sJobDescriptor).getJobName(); doNothing().when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); // When final SJobDescriptor result = jobServiceImpl.createJobDescriptor(sJobDescriptor); // Then assertNotNull(result); assertEquals(sJobDescriptor.getJobName(), result.getJobName()); assertEquals(sJobDescriptor.getDescription(), result.getDescription()); assertEquals(sJobDescriptor.getJobClassName(), result.getJobClassName()); verify(recorder, times(1)).recordInsert(any(InsertRecord.class), nullable(String.class)); } @Test(expected = IllegalArgumentException.class) public void createJobDescriptor_should_throw_an_exception_if_the_descriptor_is_null() throws Exception { jobServiceImpl.createJobDescriptor(null); } @Test(expected = IllegalArgumentException.class) public void createJobDescriptor_should_throw_an_exception_if_the_descriptor_name_is_null() throws Exception { // Given final SJobDescriptor jobDescriptor = mock(SJobDescriptor.class); // When jobServiceImpl.createJobDescriptor(jobDescriptor); } @Test(expected = SJobDescriptorCreationException.class) public void createJobDescriptor_should_throw_exception_when_recorder_failed() throws Exception { //given doThrow(new SRecorderException("plop")).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class); when(sJobDescriptor.getJobName()).thenReturn("jobName"); //when jobServiceImpl.createJobDescriptor(sJobDescriptor); //then exception } @Test public final void deleteJobDescriptor_by_id_should_delete_job_descriptor() throws Exception { //Given final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class); doReturn(sJobDescriptor).when(readPersistenceService) .selectById(ArgumentMatchers.> any()); // When jobServiceImpl.deleteJobDescriptor(3); //Then verify(jobServiceImpl, times(1)).deleteJobDescriptor(sJobDescriptor); } @Test public final void deleteJobDescriptor_by_id_should_do_nothing_when_job_descriptor_doesnt_exist() throws Exception { // Given when(readPersistenceService.selectById(ArgumentMatchers.> any())) .thenReturn(null); // When jobServiceImpl.deleteJobDescriptor(1); // Then verify(jobServiceImpl, never()).deleteJobDescriptor(any(SJobDescriptor.class)); } @Test public final void deleteJobDescriptor_by_id_should_do_nothing_when_job_descriptor_doesnt_exist_without_log() throws Exception { // Given when(readPersistenceService.selectById(ArgumentMatchers.> any())) .thenReturn(null); // When jobServiceImpl.deleteJobDescriptor(1); // Then verify(jobServiceImpl, never()).deleteJobDescriptor(any(SJobDescriptor.class)); } @Test(expected = SJobDescriptorDeletionException.class) public void deleteJobDescriptor_by_id_should_throw_exception_when_recorder_failed() throws Exception { // Given final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class); doReturn(sJobDescriptor).when(readPersistenceService).selectById(any()); doThrow(new SRecorderException("")).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); // When jobServiceImpl.deleteJobDescriptor(3); } @Test public void deleteJobDescriptor_by_name_should_delete_job_descriptor() throws Exception { //Given final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class); final List jobDescriptors = Collections.singletonList(sJobDescriptor); doReturn(jobDescriptors).when(jobServiceImpl).searchJobDescriptors(any(QueryOptions.class)); //when jobServiceImpl.deleteJobDescriptorByJobName("jobName"); //then verify(jobServiceImpl, times(1)).deleteJobDescriptor(any(SJobDescriptor.class)); } @Test public void deleteJobDescriptor_by_name_should_do_nothing_when_no_job_descriptor() throws Exception { //Given doReturn(Collections.EMPTY_LIST).when(jobServiceImpl).searchJobDescriptors(any(QueryOptions.class)); //when jobServiceImpl.deleteJobDescriptorByJobName("jobName"); //then verify(jobServiceImpl, never()).deleteJobDescriptor(any(SJobDescriptor.class)); } @Test(expected = SJobDescriptorDeletionException.class) public void deleteJobDescriptor_by_name_should_throw_exception_when_searchJobDescriptors_failed() throws Exception { //Given doThrow(new SBonitaReadException("toto")).when(jobServiceImpl).searchJobDescriptors(any(QueryOptions.class)); //when jobServiceImpl.deleteJobDescriptorByJobName("jobName"); } @Test(expected = SJobDescriptorDeletionException.class) public void deleteAllJobDescriptors_should_throw_exception_when_searchEntity_failed() throws Exception { //Given when(readPersistenceService.searchEntity(eq(SJobDescriptor.class), any(QueryOptions.class), nullable(Map.class))).thenThrow( new SBonitaReadException("error")); //When jobServiceImpl.deleteAllJobDescriptors(); } @Test public void deleteAllJobDescriptors_should_delete_all_job_descriptors() throws Exception { SJobDescriptor sJobDescriptor = new SJobDescriptor(); when(readPersistenceService.searchEntity(eq(SJobDescriptor.class), any(QueryOptions.class), nullable(Map.class))).thenReturn( asList(new SJobDescriptor(), new SJobDescriptor())); jobServiceImpl.deleteAllJobDescriptors(); verify(jobServiceImpl, times(2)).deleteJobDescriptor(sJobDescriptor); } @Test public final void deleteJobDescriptor_by_object_should_delete_job_descriptor() throws SRecorderException, SJobDescriptorDeletionException { // Given final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class); doNothing().when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); // When jobServiceImpl.deleteJobDescriptor(sJobDescriptor); //Then verify(recorder, times(1)).recordDelete(any(DeleteRecord.class), nullable(String.class)); } @Test(expected = IllegalArgumentException.class) public final void deleteJobDescriptor_by_object_should_throw_exception_when_job_descriptor_is_null() throws SJobDescriptorDeletionException { jobServiceImpl.deleteJobDescriptor(null); } @Test public void getJobDescriptor_by_id_should_return_jobDescriptor() throws SBonitaReadException, SJobDescriptorReadException { // Given final long jobDescriptorId = 1; final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class); when(readPersistenceService.selectById( SelectDescriptorBuilder.getElementById(SJobDescriptor.class, "SJobDescriptor", jobDescriptorId))) .thenReturn( sJobDescriptor); // When final SJobDescriptor jobDescriptor = jobServiceImpl.getJobDescriptor(jobDescriptorId); // Then Assert.assertEquals(sJobDescriptor, jobDescriptor); } @Test public void getJobDescriptor_by_id__should_return_null_when_no_job_descriptor() throws SBonitaReadException, SJobDescriptorReadException { // Given final long jobDescriptorId = 455; doReturn(null).when(readPersistenceService).selectById( SelectDescriptorBuilder.getElementById(SJobDescriptor.class, "SJobDescriptor", jobDescriptorId)); // When final SJobDescriptor result = jobServiceImpl.getJobDescriptor(jobDescriptorId); // Then assertNull(result); } @Test(expected = SJobDescriptorReadException.class) public void getJobDescriptor_by_id_should_throw_exception_when_selectById_failed() throws SBonitaReadException, SJobDescriptorReadException { final long jobDescriptorId = 1; doThrow(new SBonitaReadException("")).when(readPersistenceService).selectById( SelectDescriptorBuilder.getElementById(SJobDescriptor.class, "SJobDescriptor", jobDescriptorId)); jobServiceImpl.getJobDescriptor(jobDescriptorId); } @Test public void getNumberOfJobDescriptors() throws SBonitaReadException, SBonitaReadException { // Given final QueryOptions options = new QueryOptions(0, 10); when(readPersistenceService.getNumberOfEntities(SJobDescriptor.class, options, null)).thenReturn(1L); // When final long numberOfJobDescriptors = jobServiceImpl.getNumberOfJobDescriptors(options); // Then Assert.assertEquals(1L, numberOfJobDescriptors); verifyNoInteractions(recorder); } @Test(expected = SBonitaReadException.class) public void getNumberOfJobDescriptors_should_throw_exception() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(readPersistenceService.getNumberOfEntities(SJobDescriptor.class, options, null)) .thenThrow(new SBonitaReadException("")); // When jobServiceImpl.getNumberOfJobDescriptors(options); } @Test public void searchJobDescriptors() throws SBonitaReadException, SBonitaReadException { // Given final QueryOptions options = new QueryOptions(0, 10); final SJobDescriptor sJobDescriptor = mock(SJobDescriptor.class); when(readPersistenceService.searchEntity(SJobDescriptor.class, options, null)) .thenReturn(Collections.singletonList(sJobDescriptor)); // When final SJobDescriptor result = jobServiceImpl.searchJobDescriptors(options).get(0); // Then assertEquals(sJobDescriptor, result); verify(readPersistenceService).searchEntity(SJobDescriptor.class, options, null); } @Test(expected = SBonitaReadException.class) public void searchJobDescriptors_should_throw_exception() throws SBonitaReadException, SBonitaReadException { // Given final QueryOptions options = new QueryOptions(0, 10); doThrow(new SBonitaReadException("")).when(readPersistenceService).searchEntity(SJobDescriptor.class, options, null); // When jobServiceImpl.searchJobDescriptors(options).get(0); } } ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/JobServiceImplForJobLogTest.java ================================================ /** * 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.scheduler.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.QueryOptions; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.recorder.model.UpdateRecord; import org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogCreationException; import org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogDeletionException; import org.bonitasoft.engine.scheduler.exception.jobLog.SJobLogUpdatingException; import org.bonitasoft.engine.scheduler.model.SJobDescriptor; import org.bonitasoft.engine.scheduler.model.SJobLog; import org.bonitasoft.engine.scheduler.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.services.QueriableLoggerService; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class JobServiceImplForJobLogTest { @Mock private EventService eventService; @Mock private QueriableLoggerService queriableLoggerService; @Mock private ReadPersistenceService readPersistenceService; @Mock private Recorder recorder; @Spy @InjectMocks private JobServiceImpl jobServiceImpl; @Test public final void createJobLog() throws Exception { // Given final SJobLog sJobLog = mock(SJobLog.class); doNothing().when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); // When final SJobLog result = jobServiceImpl.createJobLog(sJobLog); // Then assertNotNull(result); assertEquals(sJobLog, result); verify(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); } @Test(expected = IllegalArgumentException.class) public final void createNullJobLog() throws Exception { jobServiceImpl.createJobLog(null); } @Test(expected = SJobLogCreationException.class) public final void createJobLog_should_throw_exception_when_recorder_failed() throws SJobLogCreationException, SRecorderException { // Given final SJobLog sJobLog = mock(SJobLog.class); doThrow(new SRecorderException("plop")).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); // When jobServiceImpl.createJobLog(sJobLog); } @Test public final void deleteJobLog_by_id() throws Exception { // Given final SJobLog sJobLog = mock(SJobLog.class); doReturn(sJobLog).when(readPersistenceService) .selectById(ArgumentMatchers.> any()); doNothing().when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); // When jobServiceImpl.deleteJobLog(3); // Then verify(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); } @Test public final void deleteJobLog_by_id_should_do_nothing_when_job_log_doesnt_exist() throws SBonitaReadException, SJobLogDeletionException { // Given when(readPersistenceService.selectById(ArgumentMatchers.> any())) .thenReturn(null); // When jobServiceImpl.deleteJobLog(1); // Then verify(jobServiceImpl, never()).deleteJobLog(any(SJobLog.class)); } @Test(expected = SJobLogDeletionException.class) public void deleteJobLog_by_id_should_throw_exception_when_recorder_failed() throws Exception { final SJobLog sJobLog = mock(SJobLog.class); doReturn(sJobLog).when(readPersistenceService) .selectById(ArgumentMatchers.> any()); doThrow(new SRecorderException("")).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); jobServiceImpl.deleteJobLog(3); } @Test public final void deleteJobLog_by_object() throws SRecorderException, SJobLogDeletionException { // Given final SJobLog sJobLog = mock(SJobLog.class); doNothing().when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); // When jobServiceImpl.deleteJobLog(sJobLog); // Then verify(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); } @Test(expected = IllegalArgumentException.class) public final void deleteJobLog_by_object_should_throw_exception_when_parameter_is_null() throws SJobLogDeletionException { jobServiceImpl.deleteJobLog(null); } @Test(expected = SJobLogDeletionException.class) public void deleteJobLog_by_object_should_throw_exception_when_recorder_failed() throws Exception { // Given final SJobLog sJobLog = mock(SJobLog.class); doThrow(new SRecorderException("")).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); // When jobServiceImpl.deleteJobLog(sJobLog); } @Test public void deleteJobLogs() throws Exception { // Given final long jobDescriptorId = 9L; final SJobLog sJobLog = mock(SJobLog.class); final List jobLogs = Collections.singletonList(sJobLog); doReturn(jobLogs).doReturn(Collections.EMPTY_LIST).when(jobServiceImpl).getJobLogs(jobDescriptorId, 0, 100); doNothing().when(jobServiceImpl).deleteJobLog(any(SJobLog.class)); // When jobServiceImpl.deleteJobLogs(jobDescriptorId); } @Test(expected = SJobLogDeletionException.class) public void deleteJobLogs_should_throw_exception_when_deleteJobLogs_failed() throws Exception { // Given final long jobDescriptorId = 9L; final SJobLog sJobLog = mock(SJobLog.class); final List jobLogs = Collections.singletonList(sJobLog); doReturn(jobLogs).when(jobServiceImpl).getJobLogs(jobDescriptorId, 0, 100); doThrow(SJobLogDeletionException.class).when(jobServiceImpl).deleteJobLog(any(SJobLog.class)); // When jobServiceImpl.deleteJobLogs(jobDescriptorId); } @Test(expected = SBonitaReadException.class) public void deleteJobLogs_should_throw_exception_when_getJobLogs_failed() throws Exception { // Given final long jobDescriptorId = 9L; doThrow(SBonitaReadException.class).when(jobServiceImpl).getJobLogs(jobDescriptorId, 0, 100); // When jobServiceImpl.deleteJobLogs(jobDescriptorId); } @Test public void getJobLog() throws SBonitaReadException { // Given final long jobLogId = 1; final SJobLog sJobLog = mock(SJobLog.class); when(readPersistenceService .selectById(SelectDescriptorBuilder.getElementById(SJobLog.class, "SJobLog", jobLogId))) .thenReturn(sJobLog); // When final SJobLog result = jobServiceImpl.getJobLog(jobLogId); // Then Assert.assertEquals(sJobLog, result); } @Test public void getJobLog_should_throw_exception_when_not_exist() throws SBonitaReadException { // Given final long jobLogId = 455; doReturn(null).when(readPersistenceService) .selectById(SelectDescriptorBuilder.getElementById(SJobLog.class, "SJobLog", jobLogId)); // When final SJobLog jobLog = jobServiceImpl.getJobLog(jobLogId); // Then assertNull(jobLog); } @Test(expected = SBonitaReadException.class) public void getJobLog_should_throw_exception_when_persistenceService_failed() throws SBonitaReadException { // Given final long jobLogId = 1; doThrow(new SBonitaReadException("")).when(readPersistenceService).selectById( SelectDescriptorBuilder.getElementById(SJobLog.class, "SJobLog", jobLogId)); // When jobServiceImpl.getJobLog(jobLogId); } @Test public void getJobLogs_should_call_searchJobLogs() throws Exception { // Given final long jobDescriptorId = 9L; final int fromIndex = 0; final int maxResults = 10; // When jobServiceImpl.getJobLogs(jobDescriptorId, fromIndex, maxResults); // Then verify(jobServiceImpl).searchJobLogs(any(QueryOptions.class)); } @Test public void getNumberOfJobLogs() throws SBonitaReadException, SBonitaReadException { // Given final QueryOptions options = new QueryOptions(0, 10); when(readPersistenceService.getNumberOfEntities(SJobLog.class, options, null)).thenReturn(1L); // When final long numberOfJobLogs = jobServiceImpl.getNumberOfJobLogs(options); // Then Assert.assertEquals(1L, numberOfJobLogs); verifyNoInteractions(recorder); } @Test(expected = SBonitaReadException.class) public void getNumberOfJobLog_should_throw_exception_when_persistenceService_failed() throws Exception { // Given final QueryOptions options = new QueryOptions(0, 10); when(readPersistenceService.getNumberOfEntities(SJobLog.class, options, null)) .thenThrow(new SBonitaReadException("")); // When jobServiceImpl.getNumberOfJobLogs(options); } @Test public void searchJobLogs() throws SBonitaReadException, SBonitaReadException { // Given final QueryOptions options = new QueryOptions(0, 10); final SJobLog sJobLog = mock(SJobLog.class); when(readPersistenceService.searchEntity(SJobLog.class, options, null)) .thenReturn(Collections.singletonList(sJobLog)); // When final SJobLog result = jobServiceImpl.searchJobLogs(options).get(0); // Then assertEquals(sJobLog, result); } @Test(expected = SBonitaReadException.class) public void searchJobLog_should_throw_exception_when_persistenceService_failed() throws SBonitaReadException, SBonitaReadException { // Given final QueryOptions options = new QueryOptions(0, 10); doThrow(new SBonitaReadException("")).when(readPersistenceService).searchEntity(SJobLog.class, options, null); // When jobServiceImpl.searchJobLogs(options).get(0); } @Test public void updateJobLog_should_update_job_log() throws Exception { // Given final SJobLog jobLog = new SJobLog(); final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); // When jobServiceImpl.updateJobLog(jobLog, descriptor); // Then verify(recorder).recordUpdate(any(UpdateRecord.class), anyString()); } @Test(expected = SJobLogUpdatingException.class) public void updateJobLog_should_throw_exception_when_recorder_failed() throws Exception { // Given final SJobLog jobLog = new SJobLog(); final EntityUpdateDescriptor descriptor = new EntityUpdateDescriptor(); doThrow(new SRecorderException("plop")).when(recorder).recordUpdate(any(UpdateRecord.class), nullable(String.class)); // When jobServiceImpl.updateJobLog(jobLog, descriptor); } @Test public void jobWasExecuted_should_update_jobLog_on_exception_if_previous_joblog() throws Exception { // Given final SJobLog jobLog = mock(SJobLog.class); doReturn(1L).when(jobLog).getRetryNumber(); final List jobLogs = Collections.singletonList((SJobLog) jobLog); doReturn(jobLogs).when(jobServiceImpl).getJobLogs(5L, 0, 1); long before = System.currentTimeMillis(); // When Exception jobException = new Exception("theException"); jobServiceImpl.logJobError(jobException, 5L); // Then ArgumentCaptor captor = ArgumentCaptor.forClass(EntityUpdateDescriptor.class); verify(jobServiceImpl).updateJobLog(eq(jobLogs.get(0)), captor.capture()); EntityUpdateDescriptor updateDescriptor = captor.getValue(); assertThat((String) updateDescriptor.getFields().get("lastMessage")).contains(jobException.getMessage()); assertThat((Long) updateDescriptor.getFields().get("lastUpdateDate")).isGreaterThanOrEqualTo(before); assertThat(updateDescriptor.getFields().get("retryNumber")).isEqualTo(2L); } @Test public void createJobLog_should_call_job_service_createJobLog_when_related_job_descriptor_exists() throws Exception { //given Exception exception = new Exception("Missing mandatory parameter"); long jobDescriptorId = 4L; long before = System.currentTimeMillis(); given(jobServiceImpl.getJobDescriptor(jobDescriptorId)).willReturn(mock(SJobDescriptor.class)); //when jobServiceImpl.createJobLog(exception, jobDescriptorId); //then ArgumentCaptor captor = ArgumentCaptor.forClass(SJobLog.class); verify(jobServiceImpl).createJobLog(captor.capture()); SJobLog jobLog = captor.getValue(); assertThat(jobLog.getRetryNumber()).isEqualTo(0); assertThat(jobLog.getJobDescriptorId()).isEqualTo(jobDescriptorId); assertThat(jobLog.getLastMessage()).contains(exception.getMessage()); assertThat(jobLog.getLastUpdateDate()).isGreaterThanOrEqualTo(before); } @Test public void createJobLog_should_not_create_job_log_when_related_job_descriptor_does_not_exist() throws Exception { //given Exception exception = new Exception("Missing mandatory parameter"); long jobDescriptorId = 4L; given(jobServiceImpl.getJobDescriptor(jobDescriptorId)).willReturn(null); //when jobServiceImpl.createJobLog(exception, jobDescriptorId); //then verify(jobServiceImpl, never()).createJobLog(any(SJobLog.class)); } } ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/JobServiceImplForJobParameterTest.java ================================================ /** * 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.scheduler.impl; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.entry; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.nullable; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import org.assertj.core.api.Assertions; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.persistence.ReadPersistenceService; import org.bonitasoft.engine.persistence.SBonitaReadException; import org.bonitasoft.engine.persistence.SelectByIdDescriptor; import org.bonitasoft.engine.recorder.Recorder; import org.bonitasoft.engine.recorder.SRecorderException; import org.bonitasoft.engine.recorder.model.DeleteRecord; import org.bonitasoft.engine.recorder.model.InsertRecord; import org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterCreationException; import org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterDeletionException; import org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterNotFoundException; import org.bonitasoft.engine.scheduler.exception.jobParameter.SJobParameterReadException; import org.bonitasoft.engine.scheduler.model.SJobParameter; import org.bonitasoft.engine.scheduler.recorder.SelectDescriptorBuilder; import org.bonitasoft.engine.services.QueriableLoggerService; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class JobServiceImplForJobParameterTest { @Mock private EventService eventService; @Mock private QueriableLoggerService queriableLoggerService; @Mock private ReadPersistenceService readPersistenceService; @Mock private Recorder recorder; @Spy @InjectMocks private JobServiceImpl jobServiceImpl; /** * method for * {@link org.bonitasoft.engine.scheduler.impl.JobServiceImpl#createJobParameters(java.util.List, long)}. * * @throws SJobParameterCreationException * @throws SRecorderException */ @Test public final void createJobParameters() throws SJobParameterCreationException { final long jobDescriptorId = 9; final SJobParameter sJobParameter = mock(SJobParameter.class); final List result = jobServiceImpl.createJobParameters(Collections.singletonList(sJobParameter), jobDescriptorId); assertNotNull(result); assertEquals(1, result.size()); assertEquals(sJobParameter, result.get(0)); } @Test public final void createJobParameters_with_null_parameters_should_return_empty_list() throws Exception { // Given final long jobDescriptorId = 9; // When final List result = jobServiceImpl.createJobParameters(null, jobDescriptorId); // Then assertNotNull(result); assertTrue(result.isEmpty()); } @Test public final void createJobParameter_with_empty_list_should_return_empty_list() throws Exception { // Given final long jobDescriptorId = 9; // When final List result = jobServiceImpl.createJobParameters(Collections.emptyList(), jobDescriptorId); // Then assertNotNull(result); assertTrue(result.isEmpty()); } @Test(expected = SJobParameterCreationException.class) public final void createJobParameters_should_throw_exception_when_recorder_failed() throws SJobParameterCreationException, SRecorderException { // Given final long jobDescriptorId = 9; final SJobParameter sJobParameter = mock(SJobParameter.class); doThrow(new SRecorderException("plop")).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); // When jobServiceImpl.createJobParameters(Collections.singletonList(sJobParameter), jobDescriptorId); } /** * method for * {@link org.bonitasoft.engine.scheduler.impl.JobServiceImpl#createJobParameter(org.bonitasoft.engine.scheduler.model.SJobParameter, long)}. * * @throws SJobParameterCreationException * @throws SRecorderException */ @Test public final void createJobParameter() throws SJobParameterCreationException { final long jobDescriptorId = 9; final SJobParameter sJobParameter = mock(SJobParameter.class); final SJobParameter result = jobServiceImpl.createJobParameter(sJobParameter, jobDescriptorId); assertNotNull(result); assertEquals(sJobParameter, result); } @Test public final void createJobParameter_with_null_parameters_should_throw_exception() { // Given final long jobDescriptorId = 9; // When assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> jobServiceImpl.createJobParameter(null, jobDescriptorId)); } @Test public final void createJobParameter_should_throw_exception_when_recorder_failed() throws Exception { // Given final long jobDescriptorId = 9; final SJobParameter sJobParameter = mock(SJobParameter.class); doThrow(new SRecorderException("plop")).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); // When assertThatExceptionOfType(SJobParameterCreationException.class) .isThrownBy(() -> jobServiceImpl.createJobParameter(sJobParameter, jobDescriptorId)); } /** * method for {@link org.bonitasoft.engine.scheduler.impl.JobServiceImpl#deleteJobParameter(long)}. * * @throws SBonitaReadException * @throws SRecorderException * @throws SJobParameterDeletionException * @throws SJobParameterReadException * @throws SJobParameterNotFoundException */ @Test public final void deleteJobParameter_by_id() throws Exception { // Given final SJobParameter sJobParameter = mock(SJobParameter.class); doReturn(sJobParameter).when(readPersistenceService) .selectById(ArgumentMatchers.> any()); // When jobServiceImpl.deleteJobParameter(3); // Then verify(jobServiceImpl).deleteJobParameter(sJobParameter); verify(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); } @Test(expected = SJobParameterNotFoundException.class) public final void deleteNotExistingJobParameter_by_id() throws Exception { // Given when(readPersistenceService.selectById(ArgumentMatchers.> any())) .thenReturn(null); // When jobServiceImpl.deleteJobParameter(1); } @Test(expected = SJobParameterDeletionException.class) public void deleteJobParameter_by_id_should_throw_exception_when_recorder_failed() throws Exception { // Given final SJobParameter sJobParameter = mock(SJobParameter.class); doReturn(sJobParameter).when(readPersistenceService) .selectById(ArgumentMatchers.> any()); doThrow(new SRecorderException("")).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); // When jobServiceImpl.deleteJobParameter(3); } /** * method for * {@link org.bonitasoft.engine.scheduler.impl.JobServiceImpl#deleteJobParameter(org.bonitasoft.engine.scheduler.model.SJobParameter)}. * * @throws SRecorderException * @throws SJobParameterDeletionException */ @Test public final void deleteJobParameter_by_object() throws SRecorderException, SJobParameterDeletionException { final SJobParameter sJobParameter = mock(SJobParameter.class); jobServiceImpl.deleteJobParameter(sJobParameter); } @Test(expected = IllegalArgumentException.class) public final void deleteNullJobParameter_by_object() throws SJobParameterDeletionException { jobServiceImpl.deleteJobParameter(null); } @Test(expected = SJobParameterDeletionException.class) public void deleteJobParameter_by_object_should_throw_exception_when_recorder_failed() throws Exception { final SJobParameter sJobParameter = mock(SJobParameter.class); doThrow(new SRecorderException("")).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); jobServiceImpl.deleteJobParameter(sJobParameter); } /** * Test method for {@link org.bonitasoft.engine.scheduler.impl.JobServiceImpl#getJobParameter(long)}. * * @throws SJobParameterReadException * @throws SJobParameterNotFoundException * @throws SBonitaReadException */ @Test public void getJobParameter_by_id() throws Exception { // Given final long jobParameterId = 1; final SJobParameter sJobParameter = mock(SJobParameter.class); when(readPersistenceService.selectById( SelectDescriptorBuilder.getElementById(SJobParameter.class, "SJobParameter", jobParameterId))) .thenReturn( sJobParameter); // When final SJobParameter result = jobServiceImpl.getJobParameter(jobParameterId); // Then Assert.assertEquals(sJobParameter, result); } @Test(expected = SJobParameterNotFoundException.class) public void getJobParameter_by_id_should_throw_exception_when_not_exist() throws SBonitaReadException, SJobParameterNotFoundException, SJobParameterReadException { // Given final long jobParameterId = 455; doReturn(null).when(readPersistenceService).selectById( SelectDescriptorBuilder.getElementById(SJobParameter.class, "SJobParameter", jobParameterId)); // When jobServiceImpl.getJobParameter(jobParameterId); } @Test(expected = SJobParameterReadException.class) public void getJobParameter_by_id_should_throw_exception_when_persistenceService_failed() throws Exception { // Given final long jobParameterId = 1; doThrow(new SBonitaReadException("")).when(readPersistenceService).selectById( SelectDescriptorBuilder.getElementById(SJobParameter.class, "SJobParameter", jobParameterId)); // When jobServiceImpl.getJobParameter(jobParameterId); } @Test public void should_list_parameters() throws Exception { when(readPersistenceService.selectList(any())) .thenReturn(Collections.singletonList(SJobParameter.builder().key("key").value("value").build())); List result = jobServiceImpl.getJobParameters(123L); Assertions.assertThat(result.stream().collect(Collectors.toMap(SJobParameter::getKey, SJobParameter::getValue))) .containsOnly(entry("key", "value")); } /** * method for * {@link org.bonitasoft.engine.scheduler.impl.JobServiceImpl#setJobParameters(long, java.util.List)}. */ @Test public final void setJobParameters() throws Exception { // Given final long jobDescriptorId = 8; final SJobParameter sJobParameter = mock(SJobParameter.class); final List sJobParameters = Collections.singletonList(sJobParameter); doReturn(sJobParameters).when(readPersistenceService).selectList(any()); // When final List result = jobServiceImpl.setJobParameters(jobDescriptorId, sJobParameters); // Then assertEquals(sJobParameters, result); verify(jobServiceImpl).deleteAllJobParameters(jobDescriptorId); verify(jobServiceImpl, times(sJobParameters.size())).deleteJobParameter(sJobParameter); verify(jobServiceImpl, times(sJobParameters.size())).createJobParameter(sJobParameter, jobDescriptorId); } @Test public void setJobParametersWithEmptyList() throws Exception { // Given final long jobDescriptorId = 8; // When final List result = jobServiceImpl.setJobParameters(jobDescriptorId, Collections.emptyList()); // Then assertNotNull(result); assertTrue(result.isEmpty()); } @Test public void setJobParametersWithNullList() throws Exception { // Given final long jobDescriptorId = 8; // When final List result = jobServiceImpl.setJobParameters(jobDescriptorId, null); // Then assertNotNull(result); assertTrue(result.isEmpty()); } @Test public void setJobParameters_should_throw_exception_when_search_failed() throws SBonitaReadException { // Given final long jobDescriptorId = 8; final SJobParameter sJobParameter = mock(SJobParameter.class); doThrow(new SBonitaReadException("")).when(readPersistenceService).selectList(any()); // When assertThatExceptionOfType(SJobParameterCreationException.class) .isThrownBy(() -> jobServiceImpl.setJobParameters(jobDescriptorId, Collections.singletonList(sJobParameter))); } @Test public void setJobParameters_should_throw_exception_when_delete_failed() throws Exception { // Given final long jobDescriptorId = 8; final SJobParameter sJobParameter = mock(SJobParameter.class); doReturn(Collections.singletonList(sJobParameter)).when(readPersistenceService).selectList(any()); doThrow(new SRecorderException("")).when(recorder).recordDelete(any(DeleteRecord.class), nullable(String.class)); // When assertThatExceptionOfType(SJobParameterCreationException.class).isThrownBy( () -> jobServiceImpl.setJobParameters(jobDescriptorId, Collections.singletonList(sJobParameter))); } @Test public final void setJobParameters_should_throw_exception_when_create_failed() throws Exception { // Given final long jobDescriptorId = 8; final SJobParameter sJobParameter = mock(SJobParameter.class); doThrow(new SRecorderException("plop")).when(recorder).recordInsert(any(InsertRecord.class), nullable(String.class)); // When assertThatExceptionOfType(SJobParameterCreationException.class).isThrownBy( () -> jobServiceImpl.setJobParameters(jobDescriptorId, Collections.singletonList(sJobParameter))); } } ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/JobUtils.java ================================================ /** * 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.scheduler.impl; import java.io.Serializable; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.commons.exceptions.SRetryableException; import org.bonitasoft.engine.events.model.SFireEventException; import org.bonitasoft.engine.scheduler.StatelessJob; import org.bonitasoft.engine.scheduler.exception.SJobConfigurationException; import org.bonitasoft.engine.scheduler.exception.SJobExecutionException; import org.quartz.JobDataMap; import org.quartz.impl.JobDetailImpl; public class JobUtils { static JobDetailImpl createJobDetails(long jobDescriptorId) { JobDetailImpl jobDetail = new JobDetailImpl(); jobDetail.setName("someName"); HashMap map = new HashMap<>(); map.put("jobId", String.valueOf(jobDescriptorId)); JobDataMap jobDataMap = new JobDataMap(map); jobDetail.setJobDataMap(jobDataMap); return jobDetail; } static StatelessJob jobThatFails() { return new StatelessJob() { @Override public String getName() { return null; } @Override public String getDescription() { return null; } @Override public void execute() throws SJobExecutionException, SFireEventException { throw new SJobExecutionException("exception"); } @Override public void setAttributes(Map attributes) throws SJobConfigurationException { } }; } static StatelessJob jobThatSucceed() { return new StatelessJob() { @Override public String getName() { return null; } @Override public String getDescription() { return null; } @Override public void execute() throws SJobExecutionException, SFireEventException { throw new SJobExecutionException("exception"); } @Override public void setAttributes(Map attributes) throws SJobConfigurationException { } }; } static StatelessJob jobThatThrowASRetryableException() { return new StatelessJob() { @Override public String getName() { return null; } @Override public String getDescription() { return null; } @Override public void execute() throws SJobExecutionException, SFireEventException { throw new SRetryableException("exception"); } @Override public void setAttributes(Map attributes) throws SJobConfigurationException { } }; } } ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/JobWrapperTest.java ================================================ /** * 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.scheduler.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.scheduler.JobIdentifier; import org.bonitasoft.engine.scheduler.JobService; import org.bonitasoft.engine.scheduler.StatelessJob; import org.bonitasoft.engine.scheduler.exception.SJobExecutionException; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.transaction.BonitaTransactionSynchronization; import org.bonitasoft.engine.transaction.TransactionService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta */ @RunWith(MockitoJUnitRunner.class) public class JobWrapperTest { private JobWrapper jobWrapper; @Mock private EventService eventService; @Mock private SessionAccessor sessionAccessor; @Mock private TransactionService transactionService; @Mock private PersistenceService persistenceService; @Mock private JobService jobService; @Mock private StatelessJob job; @Before public void before() { jobWrapper = new JobWrapper(new JobIdentifier(145, "MyJob"), job, eventService, transactionService, persistenceService, jobService); } @Test public void should_executeJob_execute_the_job() throws Exception { //when jobWrapper.execute(); //then verify(job).execute(); } @Test public void should_executeJob_call_flush_on_persistence() throws Exception { //when jobWrapper.execute(); //then verify(persistenceService).flushStatements(); } @Test public void should_execute_call_rollback_on_error() throws Exception { //given doThrow(new RuntimeException()).when(job).execute(); //when try { jobWrapper.execute(); fail("should have thrown exception"); } catch (SJobExecutionException e) { assertThat(e.getCause()).isInstanceOf(RuntimeException.class); } //then verify(transactionService).setRollbackOnly(); } @Test public void should_handleFailure_register_synchro() throws Exception { //given //when jobWrapper.handleFailure(new RuntimeException()); //then verify(transactionService).registerBonitaSynchronization(any(BonitaTransactionSynchronization.class)); } } ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/LogJob.java ================================================ /** * 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.scheduler.impl; import org.quartz.Job; import org.quartz.JobExecutionContext; public class LogJob implements Job { @Override public void execute(final JobExecutionContext context) { System.out.println("It works !!!!!!!!!!!! "); } } ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/MonitoringJobListenerTest.java ================================================ /** * 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.scheduler.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.scheduler.impl.MonitoringJobListener.JOB_JOBS_EXECUTED; import static org.bonitasoft.engine.scheduler.impl.MonitoringJobListener.JOB_JOBS_RUNNING; import java.time.Duration; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.junit.Test; public class MonitoringJobListenerTest { private final MeterRegistry meterRegistry = new SimpleMeterRegistry( // So that micrometer updates its counters every 1 ms: k -> k.equals("simple.step") ? Duration.ofMillis(1).toString() : null, Clock.SYSTEM); private final MonitoringJobListener monitoringJobListener = new MonitoringJobListener(meterRegistry); @Test public void should_count_executing_jobs() { monitoringJobListener.jobToBeExecuted(); monitoringJobListener.jobToBeExecuted(); monitoringJobListener.jobToBeExecuted(); monitoringJobListener.jobWasExecuted(null, null); var gauge = meterRegistry.find(JOB_JOBS_RUNNING).gauge(); assertThat(gauge).isNotNull(); assertThat(gauge.value()).isEqualTo(2); } @Test public void should_count_executed_jobs() { monitoringJobListener.jobToBeExecuted(); monitoringJobListener.jobToBeExecuted(); monitoringJobListener.jobToBeExecuted(); monitoringJobListener.jobWasExecuted(null, null); monitoringJobListener.jobWasExecuted(null, null); var counter = meterRegistry.find(JOB_JOBS_EXECUTED).counter(); assertThat(counter).isNotNull(); assertThat(counter.count()).isEqualTo(2); } } ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/QuartzJobListenerTest.java ================================================ /** * 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.scheduler.impl; import static java.util.Collections.singletonList; import static org.bonitasoft.engine.scheduler.impl.JobUtils.createJobDetails; import static org.mockito.Mockito.*; import java.util.Date; import org.bonitasoft.engine.scheduler.BonitaJobListener; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.quartz.DateBuilder.IntervalUnit; import org.quartz.Job; import org.quartz.JobBuilder; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.Scheduler; import org.quartz.impl.JobExecutionContextImpl; import org.quartz.impl.RemoteScheduler; import org.quartz.impl.calendar.WeeklyCalendar; import org.quartz.impl.triggers.CalendarIntervalTriggerImpl; import org.quartz.spi.OperableTrigger; import org.quartz.spi.TriggerFiredBundle; /** * @author Celine Souchet * @version 6.4.0 * @since 6.4.0 */ @RunWith(MockitoJUnitRunner.class) public class QuartzJobListenerTest { private QuartzJobListener quartzJobListener; @Mock private BonitaJobListener bonitaJobListener; private JobExecutionContext context; @Mock private SchedulerServiceImpl schedulerService; @Before public void setUp() { quartzJobListener = new QuartzJobListener(singletonList(bonitaJobListener)); final Scheduler scheduler = new RemoteScheduler("schedId", "host", 1589); final JobDataMap jobDataMap = new JobDataMap(); jobDataMap.getWrappedMap().put("jobId", "96"); final JobDetail jobDetail = JobBuilder.newJob().withIdentity("jobName", "jobGroup").setJobData(jobDataMap) .ofType(LogJob.class).build(); final OperableTrigger trigger = new CalendarIntervalTriggerImpl("name", "group", "jobName", "jobGroup", new Date(), new Date(), IntervalUnit.DAY, 6); final TriggerFiredBundle firedBundle = new TriggerFiredBundle(jobDetail, trigger, new WeeklyCalendar(), true, new Date(), new Date(), new Date(), new Date()); final Job job = new LogJob(); context = new JobExecutionContextImpl(scheduler, firedBundle, job); } /** * Test method for {@link QuartzJobListener#jobToBeExecuted(org.quartz.JobExecutionContext)}. */ @Test public final void jobToBeExecuted() { // When quartzJobListener.jobToBeExecuted(context); // then verify(bonitaJobListener).jobToBeExecuted(); } @Test public final void jobExecutionVetoed() { // Given final Scheduler scheduler = new RemoteScheduler("schedId", "host", 1589); final JobDataMap jobDataMap = new JobDataMap(); jobDataMap.getWrappedMap().put("jobId", "96"); final JobDetail jobDetail = JobBuilder.newJob().withIdentity("jobName", "jobGroup").setJobData(jobDataMap) .ofType(LogJob.class).build(); final OperableTrigger trigger = new CalendarIntervalTriggerImpl("name", "group", "jobName", "jobGroup", new Date(), new Date(), IntervalUnit.DAY, 6); final TriggerFiredBundle firedBundle = new TriggerFiredBundle(jobDetail, trigger, new WeeklyCalendar(), true, new Date(), new Date(), new Date(), new Date()); final ConcurrentQuartzJob job = new ConcurrentQuartzJob(); job.setSchedulerService(schedulerService); job.setJobDetails(createJobDetails(2)); final JobExecutionContext context = new JobExecutionContextImpl(scheduler, firedBundle, job); // When quartzJobListener.jobExecutionVetoed(context); // then verify(bonitaJobListener).jobExecutionVetoed(); } @Test public final void jobWasExecuted() { // When quartzJobListener.jobWasExecuted(context, null); // then verify(bonitaJobListener).jobWasExecuted(anyMap(), nullable(SSchedulerException.class)); } } ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/QuartzSchedulerExecutorTest.java ================================================ /** * 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.scheduler.impl; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.*; import static org.quartz.JobKey.jobKey; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bonitasoft.engine.scheduler.BonitaJobListener; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; import org.bonitasoft.engine.scheduler.trigger.OneShotTrigger; import org.bonitasoft.engine.scheduler.trigger.Trigger; import org.bonitasoft.engine.scheduler.trigger.Trigger.MisfireRestartPolicy; import org.bonitasoft.engine.scheduler.trigger.UnixCronTrigger; import org.bonitasoft.engine.transaction.BonitaTransactionSynchronization; import org.bonitasoft.engine.transaction.TransactionService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.quartz.CronTrigger; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.JobKey; import org.quartz.ListenerManager; import org.quartz.Scheduler; import org.quartz.SchedulerException; import org.quartz.Trigger.TriggerState; import org.quartz.TriggerBuilder; import org.quartz.TriggerKey; import org.quartz.impl.matchers.GroupMatcher; import org.quartz.impl.triggers.SimpleTriggerImpl; @RunWith(MockitoJUnitRunner.class) public class QuartzSchedulerExecutorTest { private static final String JOB_NAME = "jobName"; private static final long JOB_ID = 1L; @Mock private BonitaSchedulerFactory schedulerFactory; @Mock private TransactionService transactionService; @Mock private Scheduler scheduler; @Mock private ListenerManager listenerManager; private final JobDetail jobDetail = JobBuilder.newJob(ConcurrentQuartzJob.class).withIdentity(JOB_NAME).build(); @Mock private org.quartz.Trigger trigger1; @Mock private org.quartz.Trigger trigger2; private QuartzSchedulerExecutor quartzSchedulerExecutor; @Before public void before() throws Exception { when(schedulerFactory.getScheduler()).thenReturn(scheduler); when(scheduler.getListenerManager()).thenReturn(listenerManager); when(transactionService.isTransactionActive()).thenReturn(true); quartzSchedulerExecutor = initQuartzScheduler(false); } private QuartzSchedulerExecutor initQuartzScheduler(final boolean useOptimization) throws SSchedulerException { final QuartzSchedulerExecutor quartz = new QuartzSchedulerExecutor(schedulerFactory, transactionService, useOptimization); quartz.start(); return quartz; } private Set newSet(final JobKey... jobKeys) { final HashSet set = new HashSet<>(); set.addAll(asList(jobKeys)); return set; } @Test public void pauseTriggers_should_pause_jobs() throws Exception { quartzSchedulerExecutor.pauseJobs(); verify(scheduler).pauseTriggers(GroupMatcher.anyTriggerGroup()); } @SuppressWarnings("unchecked") @Test(expected = SSchedulerException.class) public void pauseJobs_should_throw_exception_if_error_occurs() throws Exception { doThrow(SchedulerException.class).when(scheduler).pauseTriggers(any(GroupMatcher.class)); quartzSchedulerExecutor.pauseJobs(); } @Test public void resumeJobs_should_resume_jobs() throws Exception { quartzSchedulerExecutor.resumeJobs(); verify(scheduler).resumeTriggers(GroupMatcher.anyTriggerGroup()); } @SuppressWarnings("unchecked") @Test(expected = SSchedulerException.class) public void resumeJobs_should_throw_exception_if_error_occurs_when_resuming_jobs() throws Exception { doThrow(SchedulerException.class).when(scheduler).resumeTriggers(any(GroupMatcher.class)); quartzSchedulerExecutor.resumeJobs(); } @Test public void getQuartzTrigger_should_getQuartzTrigger_with_restart_ALL_have_a_ignore_misfire_policy() { // given final Date triggerEndTime = new Date(System.currentTimeMillis() + 10000); final UnixCronTrigger unixCronTrigger = new UnixCronTrigger("MyTrigger", triggerEndTime, "0/5 * * * * ??", MisfireRestartPolicy.ALL); // when final CronTrigger quartzTrigger = (CronTrigger) quartzSchedulerExecutor.getQuartzTrigger(unixCronTrigger, "MyJob"); // then assertEquals(CronTrigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY, quartzTrigger.getMisfireInstruction()); } @Test public void getQuartzTrigger_should_getQuartzTrigger_with_restart_NONE_have_a_do_nothing_misfire_policy() { // given final Date triggerEndTime = new Date(System.currentTimeMillis() + 10000); final UnixCronTrigger unixCronTrigger = new UnixCronTrigger("MyTrigger", triggerEndTime, "0/5 * * * * ??", MisfireRestartPolicy.NONE); // when final CronTrigger quartzTrigger = (CronTrigger) quartzSchedulerExecutor.getQuartzTrigger(unixCronTrigger, "MyJob"); // then assertEquals(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING, quartzTrigger.getMisfireInstruction()); } @Test public void getQuartzTrigger_should_getQuartzTrigger_with_restart_ONE_have_a_fire_once_misfire_policy() { // given final Date triggerEndTime = new Date(System.currentTimeMillis() + 10000); final UnixCronTrigger unixCronTrigger = new UnixCronTrigger("MyTrigger", triggerEndTime, "0/5 * * * * ??", MisfireRestartPolicy.ONE); // when final CronTrigger quartzTrigger = (CronTrigger) quartzSchedulerExecutor.getQuartzTrigger(unixCronTrigger, "MyJob"); // then assertEquals(CronTrigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW, quartzTrigger.getMisfireInstruction()); } @Test public void delete_job_should_pause_and_delete_job_for_a_given_name() throws Exception { // Given final JobKey jobKey = jobKey("aName"); // When quartzSchedulerExecutor.delete(jobKey.getName()); // Then verify(scheduler).pauseJob(jobKey); verify(scheduler).deleteJob(jobKey); } @Test public void delete_should_throw_exception_when_error_occurs_in_job_deletion() throws Exception { final JobKey job = jobKey("aName"); when(scheduler.deleteJob(job)).thenThrow(new SchedulerException()); assertThatExceptionOfType(SSchedulerException.class) .isThrownBy(() -> quartzSchedulerExecutor.delete(job.getName())) .withCauseInstanceOf(SchedulerException.class); } @Test public void deleteJobs_should_delete_jobs() throws Exception { final JobKey toBeDeleted = jobKey("job1"); final JobKey toBeDeletedAlso = jobKey("job2"); when(scheduler.getJobKeys(GroupMatcher.anyJobGroup())).thenReturn(newSet(toBeDeleted, toBeDeletedAlso)); quartzSchedulerExecutor.deleteJobs(); verify(scheduler).deleteJob(toBeDeleted); verify(scheduler).deleteJob(toBeDeletedAlso); } @SuppressWarnings("unchecked") @Test(expected = SSchedulerException.class) public void deleteJobs_should_throw_exception_when_error_occurs_in_jobs_deletion() throws Exception { doThrow(SchedulerException.class).when(scheduler).getJobKeys(any(GroupMatcher.class)); quartzSchedulerExecutor.deleteJobs(); } @Test public void getJobs_should_get_job_names() throws Exception { final JobKey toBeRetrieved = jobKey("job1"); final JobKey toBeRetrievedAlso = jobKey("job2"); when(scheduler.getJobKeys(GroupMatcher.anyJobGroup())).thenReturn(newSet(toBeRetrieved, toBeRetrievedAlso)); final List jobs = quartzSchedulerExecutor.getJobs(); assertThat(jobs).containsOnly(toBeRetrieved.getName(), toBeRetrievedAlso.getName()); } @Test public void getJobs_should_throw_exception_when_error_occurs_on_job_names_fetching() throws Exception { doThrow(SchedulerException.class).when(scheduler).getJobKeys(GroupMatcher.anyJobGroup()); assertThatExceptionOfType(SSchedulerException.class) .isThrownBy(() -> quartzSchedulerExecutor.getJobs()) .withCauseInstanceOf(SchedulerException.class); } @Test public void getJobs_should_get_job_names_for_all_group_name() throws Exception { final JobKey job1 = jobKey("job1", Scheduler.DEFAULT_GROUP); final JobKey job2 = jobKey("job2", Scheduler.DEFAULT_GROUP); final JobKey job3 = jobKey("job3", "anotherGroup"); doReturn(newSet(job1, job2, job3)).when(scheduler).getJobKeys(GroupMatcher.anyJobGroup()); final List jobs = quartzSchedulerExecutor.getJobs(); assertThat(jobs).containsOnly(job1.getName(), job2.getName(), job3.getName()); } @Test(expected = SSchedulerException.class) public void start_should_not_start_twice() throws Exception { // given doReturn(true).when(scheduler).isStarted(); // when quartzSchedulerExecutor.start(); } @Test public void schedule_disallowConcurrentExecution() throws Exception { scheduleJob(quartzSchedulerExecutor, true, 0); } @Test public void schedule_allowConcurrentExecution() throws Exception { scheduleJob(quartzSchedulerExecutor, false, 0); } @Test public void schedule_with_optimisation() throws Exception { final QuartzSchedulerExecutor executor = quartzSchedulerExecutor = initQuartzScheduler(true); scheduleJob(executor, true, 1); } private void scheduleJob(final QuartzSchedulerExecutor executor, final boolean disallowConcurrentExecution, final int expectedOptimizationCall) throws Exception { // when executor.schedule(1L, JOB_NAME, new OneShotTrigger("oneShot", new Date(System.currentTimeMillis())), disallowConcurrentExecution); // then verify(scheduler).scheduleJob(any(JobDetail.class), any(org.quartz.Trigger.class)); verify(transactionService, times(expectedOptimizationCall)) .registerBonitaSynchronization(any(BonitaTransactionSynchronization.class)); } @Test public void schedule_with_exception() throws Exception { // given doThrow(SchedulerException.class).when(scheduler).scheduleJob(any(JobDetail.class), any(org.quartz.Trigger.class)); // when and then exception assertThatExceptionOfType(SSchedulerException.class) .isThrownBy(() -> quartzSchedulerExecutor.schedule(1L, JOB_NAME, new OneShotTrigger("oneShot", new Date(System.currentTimeMillis())), true)) .withCauseInstanceOf(SchedulerException.class); } @Test public void schedule_should_use_default_group_in_job_details() throws Exception { final Trigger trigger = new OneShotTrigger("trigger", new Date(), 1, MisfireRestartPolicy.NONE); quartzSchedulerExecutor.schedule(10L, "myJob", trigger, true); final ArgumentCaptor jobDetailCaptor = ArgumentCaptor.forClass(JobDetail.class); verify(scheduler).scheduleJob(jobDetailCaptor.capture(), any(org.quartz.Trigger.class)); final String group = jobDetailCaptor.getValue().getKey().getGroup(); assertThat(group).isEqualTo(Scheduler.DEFAULT_GROUP); } @Test public void executeAgain_should_schedule_job_when_no_more_job_is_registered() throws Exception { // given doReturn(null).when(scheduler).getJobDetail(any(JobKey.class)); // when quartzSchedulerExecutor.executeAgain(JOB_ID, JOB_NAME, true, 5000); // then: create a trigger and a job details verify(scheduler, never()).scheduleJob(any(org.quartz.Trigger.class)); verify(scheduler).scheduleJob(any(JobDetail.class), any(org.quartz.Trigger.class)); verify(scheduler, never()).rescheduleJob(any(org.quartz.TriggerKey.class), any(org.quartz.Trigger.class)); } @Test public void executeAgain_should_schedule_job_when_job_do_not_have_a_trigger_anymore() throws Exception { // given doReturn(jobDetail).when(scheduler).getJobDetail(any(JobKey.class)); doReturn(Collections.emptyList()).when(scheduler).getTriggersOfJob(any(JobKey.class)); // when quartzSchedulerExecutor.executeAgain(JOB_ID, JOB_NAME, true, 5000); // then: create a new trigger to execute it verify(scheduler).scheduleJob(any(org.quartz.Trigger.class)); verify(scheduler, never()).scheduleJob(any(JobDetail.class), any(org.quartz.Trigger.class)); verify(scheduler, never()).rescheduleJob(any(org.quartz.TriggerKey.class), any(org.quartz.Trigger.class)); } @Test public void executeAgain_should_schedule_job_when_job_have_no_trigger_that_may_not_fire_again() throws Exception { // given doReturn(jobDetail).when(scheduler).getJobDetail(any(JobKey.class)); doReturn(asList(triggerThatMayFireAgain(), triggerThatMayFireAgain())).when(scheduler) .getTriggersOfJob(any(JobKey.class)); // when quartzSchedulerExecutor.executeAgain(JOB_ID, JOB_NAME, true, 5000); // then: create a new trigger to execute it verify(scheduler).scheduleJob(any(org.quartz.Trigger.class)); verify(scheduler, never()).scheduleJob(any(JobDetail.class), any(org.quartz.Trigger.class)); verify(scheduler, never()).rescheduleJob(any(org.quartz.TriggerKey.class), any(org.quartz.Trigger.class)); } private SimpleTriggerImpl triggerThatMayFireAgain() { SimpleTriggerImpl simpleTrigger = (SimpleTriggerImpl) TriggerBuilder.newTrigger() .withIdentity(JOB_NAME).build(); simpleTrigger.setNextFireTime(new Date()); return simpleTrigger; } @Test public void executeAgain_should_reschedule_job_when_job_have_at_least_one_trigger_may_not_fire_again() throws Exception { // given doReturn(jobDetail).when(scheduler).getJobDetail(any(JobKey.class)); doReturn(asList(triggerThatMayFireAgain(), triggerThatMayNotFireAgain())).when(scheduler) .getTriggersOfJob(any(JobKey.class)); // when quartzSchedulerExecutor.executeAgain(JOB_ID, JOB_NAME, true, 5000); // then: create a new trigger to execute it verify(scheduler, never()).scheduleJob(any(org.quartz.Trigger.class)); verify(scheduler, never()).scheduleJob(any(JobDetail.class), any(org.quartz.Trigger.class)); verify(scheduler).rescheduleJob(any(org.quartz.TriggerKey.class), any(org.quartz.Trigger.class)); } private org.quartz.Trigger triggerThatMayNotFireAgain() { return TriggerBuilder.newTrigger().withIdentity(JOB_NAME).build(); } @Test public void executeAgain_should_schedule_job_when_job_have_a_trigger_that_may_not_fire_again() throws Exception { // given doReturn(jobDetail).when(scheduler).getJobDetail(any(JobKey.class)); doReturn(singletonList(triggerThatMayNotFireAgain())).when(scheduler).getTriggersOfJob(any(JobKey.class)); // when quartzSchedulerExecutor.executeAgain(JOB_ID, JOB_NAME, true, 5000); // then: update the trigger ( reschedule ) verify(scheduler, never()).scheduleJob(any(org.quartz.Trigger.class)); verify(scheduler, never()).scheduleJob(any(JobDetail.class), any(org.quartz.Trigger.class)); verify(scheduler).rescheduleJob(any(org.quartz.TriggerKey.class), any(org.quartz.Trigger.class)); } @Test public void executeAgain_should_schedule_job_when_job_have_a_trigger_that_may_fire_again() throws Exception { // given doReturn(jobDetail).when(scheduler).getJobDetail(any(JobKey.class)); doReturn(singletonList(triggerThatMayFireAgain())) .when(scheduler).getTriggersOfJob(any(JobKey.class)); // when quartzSchedulerExecutor.executeAgain(JOB_ID, JOB_NAME, true, 5000); // then: create a new trigger to execute it verify(scheduler).scheduleJob(any(org.quartz.Trigger.class)); verify(scheduler, never()).scheduleJob(any(JobDetail.class), any(org.quartz.Trigger.class)); verify(scheduler, never()).rescheduleJob(any(org.quartz.TriggerKey.class), any(org.quartz.Trigger.class)); } @Test public void executeAgain_using_optimization() throws Exception { // given quartzSchedulerExecutor = initQuartzScheduler(true); doReturn(jobDetail).when(scheduler).getJobDetail(any(JobKey.class)); // when quartzSchedulerExecutor.executeAgain(JOB_ID, JOB_NAME, true, 5000); // then verify(transactionService).registerBonitaSynchronization(any(BonitaTransactionSynchronization.class)); } @Test public void mayFireAgain_should_return_true_when_some_trigger_may_fire_again() throws Exception { when(trigger1.mayFireAgain()).thenReturn(false); when(trigger2.mayFireAgain()).thenReturn(true); doReturn(asList(trigger1, trigger2)).when(scheduler).getTriggersOfJob(any(JobKey.class)); boolean mayFireAgain = quartzSchedulerExecutor.mayFireAgain(JOB_NAME); assertThat(mayFireAgain).isTrue(); } @Test public void mayFireAgain_should_return_false_when_no_trigger_may_fire_again() throws Exception { when(trigger1.mayFireAgain()).thenReturn(false); when(trigger2.mayFireAgain()).thenReturn(false); doReturn(asList(trigger1, trigger2)).when(scheduler).getTriggersOfJob(any(JobKey.class)); boolean mayFireAgain = quartzSchedulerExecutor.mayFireAgain(JOB_NAME); assertThat(mayFireAgain).isFalse(); } @Test public void rescheduleErroneousTriggers_should_throw_exception() throws Exception { // given doThrow(SchedulerException.class).when(scheduler).getTriggerKeys(GroupMatcher.anyTriggerGroup()); // when and then exception assertThatExceptionOfType(SSchedulerException.class) .isThrownBy(() -> quartzSchedulerExecutor.rescheduleErroneousTriggers()) .withCauseInstanceOf(SchedulerException.class); } @Test public void rescheduleErroneousTriggers_should_pause_and_resume_trigger() throws Exception { check_reschedule(TriggerState.ERROR, 1); } @Test public void rescheduleErroneousTriggers_should_not_reschedule() throws Exception { check_reschedule(TriggerState.COMPLETE, 0); } private void check_reschedule(final TriggerState triggerState, final int expectedNumberOfInvocations) throws SchedulerException, SSchedulerException { final Set triggerKeys = new HashSet<>(); final TriggerKey triggerKey = new TriggerKey("name"); triggerKeys.add(triggerKey); // given doReturn(triggerState).when(scheduler).getTriggerState(triggerKey); doReturn(triggerKeys).when(scheduler).getTriggerKeys(GroupMatcher.anyTriggerGroup()); // when quartzSchedulerExecutor.rescheduleErroneousTriggers(); // then verify(scheduler, times(expectedNumberOfInvocations)).pauseTrigger(any(TriggerKey.class)); verify(scheduler, times(expectedNumberOfInvocations)).resumeTrigger(any(TriggerKey.class)); } @Test(expected = SSchedulerException.class) public void is_started_should_throw_exception() throws Exception { // given doThrow(SchedulerException.class).when(scheduler).isStarted(); // when quartzSchedulerExecutor.isStarted(); // then exception } @Test public void is_started_should_be_true_when_scheduler_is_stated() throws Exception { checkIsStarted(true, true, false); } @Test public void is_started_should_be_false_when_scheduler_is_shutdown() throws Exception { checkIsStarted(false, false, true); } @Test public void is_started_should_be_false_when_scheduler_is_in_transitionnal_state() throws Exception { checkIsStarted(false, true, true); } private void checkIsStarted(final boolean expectedResponse, final boolean schedulerStartStatus, final boolean schedulerSchutdownStatus) throws SchedulerException, SSchedulerException { // given doReturn(schedulerStartStatus).when(scheduler).isStarted(); doReturn(schedulerSchutdownStatus).when(scheduler).isShutdown(); // when final boolean started = quartzSchedulerExecutor.isStarted(); // then exception assertThat(started).isEqualTo(expectedResponse); } @Test public void is_shutdown_should_throw_exception() throws Exception { // given doThrow(SchedulerException.class).when(scheduler).isShutdown(); // when and then exception assertThatExceptionOfType(SSchedulerException.class) .isThrownBy(() -> quartzSchedulerExecutor.isShutdown()) .withCauseInstanceOf(SchedulerException.class); } @Test public void is_shutdown_should_be_true() throws Exception { doReturn(true).when(scheduler).isShutdown(); final boolean started = quartzSchedulerExecutor.isShutdown(); assertThat(started).isTrue(); } @Test public void is_shutdown_should_be_false() throws Exception { doReturn(false).when(scheduler).isShutdown(); boolean started = quartzSchedulerExecutor.isShutdown(); assertThat(started).isFalse(); } @Test public void rescheduleJob_should_throw_exception_when_rescheduleJob_failed() throws Exception { // Given final org.quartz.Trigger trigger = mock(org.quartz.Trigger.class); doReturn(TriggerBuilder.newTrigger()).when(trigger).getTriggerBuilder(); doReturn(trigger).when(scheduler).getTrigger(any(TriggerKey.class)); doThrow(SchedulerException.class).when(scheduler).rescheduleJob(any(TriggerKey.class), any(org.quartz.Trigger.class)); // When assertThatExceptionOfType(SSchedulerException.class) .isThrownBy(() -> quartzSchedulerExecutor.rescheduleJob("triggerName", new Date())) .withCauseInstanceOf(SchedulerException.class); } @Test public void rescheduleJob_should_rescheduleJob() throws Exception { // Given final org.quartz.Trigger trigger = mock(org.quartz.Trigger.class); doReturn(TriggerBuilder.newTrigger()).when(trigger).getTriggerBuilder(); doReturn(trigger).when(scheduler).getTrigger(any(TriggerKey.class)); // When quartzSchedulerExecutor.rescheduleJob("triggerName", new Date()); // Then verify(scheduler).rescheduleJob(any(TriggerKey.class), any(org.quartz.Trigger.class)); } @Test public void isExistingJob_should_throw_exception_when_getJobDetail_failed() throws Exception { // Given doThrow(SchedulerException.class).when(scheduler).getJobDetail(any(JobKey.class)); // When assertThatExceptionOfType(SSchedulerException.class) .isThrownBy(() -> quartzSchedulerExecutor.isExistingJob("name")) .withCauseInstanceOf(SchedulerException.class); } @Test public void isExistingJob_should_throw_exception_when_scheduler_is_shutdown() throws Exception { // Given doReturn(true).when(scheduler).isShutdown(); // When assertThatExceptionOfType(SSchedulerException.class) .isThrownBy(() -> quartzSchedulerExecutor.isExistingJob("name")) .withMessage("The scheduler is not started"); } @Test public void isExistingJob_should_return_false_if_doesnt_exist_in_quartz() throws Exception { // Given doReturn(null).when(scheduler).getJobDetail(any(JobKey.class)); // When final boolean existingJob = quartzSchedulerExecutor.isExistingJob("name"); // Then assertFalse(existingJob); } @Test public void isExistingJob_should_return_true_if_exists_in_quartz() throws Exception { // Given doReturn(mock(JobDetail.class)).when(scheduler).getJobDetail(any(JobKey.class)); // When final boolean existingJob = quartzSchedulerExecutor.isExistingJob("name"); // Then assertTrue(existingJob); } @Test(expected = SSchedulerException.class) public void shutdown_should_throw_exception_when_scheduler_failed() throws Exception { // Given doThrow(SchedulerException.class).when(scheduler).shutdown(true); quartzSchedulerExecutor.shutdown(); } @Test public void should_register_listeners_on_start() throws Exception { BonitaJobListener listener1 = mock(BonitaJobListener.class); BonitaJobListener listener2 = mock(BonitaJobListener.class); quartzSchedulerExecutor = new QuartzSchedulerExecutor(schedulerFactory, transactionService, false); quartzSchedulerExecutor.setJobListeners(asList(listener1, listener2)); quartzSchedulerExecutor.start(); verify(listenerManager).addJobListener(argThat((quartzListener) -> ((QuartzJobListener) quartzListener) .getBonitaJobListeners().containsAll(asList(listener1, listener2)))); } } ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/ReturnLogBuilder.java ================================================ /** * 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.scheduler.impl; import org.mockito.internal.stubbing.defaultanswers.ReturnsMocks; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; /** * @author Elias Ricken de Medeiros */ public class ReturnLogBuilder implements Answer { private Object object; public ReturnLogBuilder() { } public void setObject(final Object object) { this.object = object; } @Override public Object answer(final InvocationOnMock invocation) throws Throwable { if ("done".equals(invocation.getMethod().getName())) { return new ReturnsMocks().answer(invocation); } return object; } } ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/SchedulerServiceImplTest.java ================================================ /** * 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.scheduler.impl; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Random; import org.bonitasoft.engine.events.EventService; import org.bonitasoft.engine.events.model.SEvent; import org.bonitasoft.engine.scheduler.JobService; import org.bonitasoft.engine.scheduler.SchedulerExecutor; import org.bonitasoft.engine.scheduler.exception.SSchedulerException; import org.bonitasoft.engine.scheduler.exception.jobDescriptor.SJobDescriptorCreationException; import org.bonitasoft.engine.scheduler.model.SJobDescriptor; import org.bonitasoft.engine.scheduler.model.SJobParameter; import org.bonitasoft.engine.scheduler.trigger.Trigger; import org.bonitasoft.engine.service.ServicesResolver; import org.bonitasoft.engine.services.PersistenceService; import org.bonitasoft.engine.transaction.TransactionService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class SchedulerServiceImplTest { private static final long JOB_DESCRIPTOR_ID = 32187L; private SchedulerServiceImpl schedulerService; @Mock private SchedulerExecutor schedulerExecutor; @Mock private JobService jobService; @Mock private EventService eventService; @Mock private PersistenceService persistenceService; @Mock private ServicesResolver servicesResolver; @Mock private TransactionService transactionService; @Before public void setUp() { transactionService = mock(TransactionService.class); schedulerService = new SchedulerServiceImpl(schedulerExecutor, jobService, eventService, transactionService, servicesResolver, persistenceService); } @Test public void isStarted_return_true_if_schedulorExecutor_is_started() throws Exception { when(schedulerExecutor.isStarted()).thenReturn(true); final boolean started = schedulerService.isStarted(); assertThat(started).isTrue(); } @Test public void isStarted_return_false_if_schedulorExecutor_is_not_started() throws Exception { when(schedulerExecutor.isStarted()).thenReturn(false); final boolean started = schedulerService.isStarted(); assertThat(started).isFalse(); } @Test public void isStopped_return_true_if_executor_is_shutodown() throws Exception { when(schedulerExecutor.isShutdown()).thenReturn(true); final boolean stopped = schedulerService.isStopped(); assertThat(stopped).isTrue(); } @Test public void isStopped_return_false_if_executor_is_not_shutodown() throws Exception { when(schedulerExecutor.isShutdown()).thenReturn(false); final boolean stopped = schedulerService.isStopped(); assertThat(stopped).isFalse(); } @Test public void start_start_schedulerexecutor_and_fire_a_start_event() throws Exception { schedulerService.start(); verify(schedulerExecutor).start(); verify(eventService).fireEvent(any(SEvent.class)); } @Test public void stop_shutdown_schedulerexecutor_and_fire_a_stop_event() throws Exception { schedulerService.stop(); verify(schedulerExecutor).shutdown(); verify(eventService).fireEvent(any(SEvent.class)); } @Test public void delete_delete_job_and_jobDescription() throws Exception { final String jobName = "aJobName"; schedulerService.delete(jobName); verify(schedulerExecutor).delete(jobName); verify(jobService).deleteJobDescriptorByJobName(jobName); } @Test public void delete_return_schedulerexecutor_deletion_status() throws Exception { final boolean expectedDeletionStatus = new Random().nextBoolean(); final String jobName = "jobName"; when(schedulerExecutor.delete(jobName)).thenReturn(expectedDeletionStatus); final boolean deletionStatus = schedulerService.delete(jobName); assertThat(deletionStatus).isEqualTo(expectedDeletionStatus); } @Test(expected = SSchedulerException.class) public void cannot_execute_a_job_with_a_null_trigger() throws Exception { final SJobDescriptor jobDescriptor = mock(SJobDescriptor.class); final List parameters = new ArrayList<>(); schedulerService.schedule(jobDescriptor, parameters, null); } @Test(expected = SSchedulerException.class) public void cannot_schedule_a_null_job() throws Exception { final Trigger trigger = mock(Trigger.class); when(jobService.createJobDescriptor(nullable(SJobDescriptor.class))) .thenThrow(new SJobDescriptorCreationException("")); schedulerService.schedule(null, trigger); } @Test public void rescheduleErroneousTriggers_call_same_method_in_schedulerexecutor() throws Exception { schedulerService.rescheduleErroneousTriggers(); verify(schedulerExecutor).rescheduleErroneousTriggers(); } @Test public void should_pauseJobs_call_schedulerExecutor() throws Exception { schedulerService.resumeJobs(); verify(schedulerExecutor).resumeJobs(); } @Test public void should_pauseJobs_call_schedulerExecutor_rethrow_exception() throws Exception { doThrow(new SSchedulerException("My exception")).when(schedulerExecutor).resumeJobs(); assertThatExceptionOfType(SSchedulerException.class) .isThrownBy(() -> schedulerService.resumeJobs()) .withMessage("My exception"); } @Test public void schedule_should_store_jobDescriptor_store_parameters_and_call_executor_schedule() throws Exception { // given final long jogDescriptorId = 7L; final String jobName = "myJob"; final SJobDescriptor jobDescriptor = mock(SJobDescriptor.class); given(jobDescriptor.getId()).willReturn(jogDescriptorId); given(jobDescriptor.getJobName()).willReturn(jobName); given(jobService.createJobDescriptor(jobDescriptor)).willReturn(jobDescriptor); final Trigger trigger = mock(Trigger.class); final List parameters = Collections.singletonList(mock(SJobParameter.class)); // when schedulerService.schedule(jobDescriptor, parameters, trigger); // then verify(jobService).createJobDescriptor(jobDescriptor); verify(jobService).createJobParameters(parameters, jogDescriptorId); verify(schedulerExecutor).schedule(jogDescriptorId, jobName, trigger, false); } @Test public void should_delete_all_jobs() throws Exception { schedulerService.deleteJobs(); verify(schedulerExecutor).deleteJobs(); verify(jobService).deleteAllJobDescriptors(); } @Test public void rescheduleJob_should_call_rescheduleJob() throws Exception { // Given final String triggerName = "triggerName"; final Date triggerStartTime = new Date(); // When schedulerService.rescheduleJob(triggerName, triggerStartTime); // Then verify(schedulerExecutor).rescheduleJob(triggerName, triggerStartTime); } @Test public void should_execute_again_an_existing_job() throws Exception { SJobDescriptor jobDescriptor = SJobDescriptor.builder().jobClassName("jobClassName") .jobName("jobName") .id(JOB_DESCRIPTOR_ID).build(); doReturn(jobDescriptor) .when(jobService).getJobDescriptor(JOB_DESCRIPTOR_ID); schedulerService.executeAgain(JOB_DESCRIPTOR_ID, 5000); verify(jobService, never()).setJobParameters(anyLong(), any()); verify(schedulerExecutor).executeAgain(JOB_DESCRIPTOR_ID, "jobName", false, 5000); verify(jobService, never()).deleteJobLogs(JOB_DESCRIPTOR_ID); } @Test public void should_retry_a_job_that_failed() throws Exception { SJobDescriptor jobDescriptor = SJobDescriptor.builder().jobClassName("jobClassName") .jobName("jobName") .id(JOB_DESCRIPTOR_ID).build(); doReturn(jobDescriptor) .when(jobService).getJobDescriptor(JOB_DESCRIPTOR_ID); schedulerService.retryJobThatFailed(JOB_DESCRIPTOR_ID); verify(jobService, never()).setJobParameters(anyLong(), any()); verify(schedulerExecutor).executeAgain(JOB_DESCRIPTOR_ID, "jobName", false, 0); verify(jobService).deleteJobLogs(JOB_DESCRIPTOR_ID); } @Test public void should_retry_a_job_that_failed_while_changing_parameters() throws Exception { SJobDescriptor jobDescriptor = SJobDescriptor.builder().jobClassName("jobClassName") .jobName("jobName") .id(JOB_DESCRIPTOR_ID).build(); doReturn(jobDescriptor) .when(jobService).getJobDescriptor(JOB_DESCRIPTOR_ID); List parameters = asList(SJobParameter.builder().key("a").value(1).build(), SJobParameter.builder().key("b").value(2).build()); schedulerService.retryJobThatFailed(JOB_DESCRIPTOR_ID, parameters); verify(jobService).setJobParameters(JOB_DESCRIPTOR_ID, parameters); verify(schedulerExecutor).executeAgain(JOB_DESCRIPTOR_ID, "jobName", false, 0); verify(jobService).deleteJobLogs(JOB_DESCRIPTOR_ID); } } ================================================ FILE: services/bonita-scheduler/src/test/java/org/bonitasoft/engine/scheduler/impl/TransactionalSimpleJobFactoryTest.java ================================================ /** * 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.scheduler.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.scheduler.impl.JobUtils.createJobDetails; import static org.mockito.BDDMockito.given; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.quartz.Job; import org.quartz.Scheduler; import org.quartz.impl.JobDetailImpl; import org.quartz.spi.TriggerFiredBundle; @RunWith(MockitoJUnitRunner.class) public class TransactionalSimpleJobFactoryTest { @Mock private SchedulerServiceImpl schedulerServiceImpl; @Mock private TriggerFiredBundle bundle; @Mock private Scheduler scheduler; @InjectMocks TransactionalSimpleJobFactory factory; @Test public void should_inject_scheduler_and_job_details_on_new_job() throws Exception { JobDetailImpl jobDetails = createJobDetails(2); jobDetails.setJobClass(ConcurrentQuartzJob.class); given(bundle.getJobDetail()).willReturn(jobDetails); Job newJob = factory.newJob(bundle, scheduler); assertThat(newJob).isInstanceOf(AbstractQuartzJob.class); assertThat(((AbstractQuartzJob) newJob).getSchedulerService()).isEqualTo(schedulerServiceImpl); assertThat(((AbstractQuartzJob) newJob).getJobDetail()).isEqualTo(jobDetails); } } ================================================ FILE: services/bonita-scheduler/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: services/bonita-session/build.gradle ================================================ dependencies { api project(':services:bonita-commons') api project(':services:bonita-builder') testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.logback annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: services/bonita-session/src/main/java/org/bonitasoft/engine/session/SSessionAlreadyExistsException.java ================================================ /** * 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.session; /** * @author Elias Ricken de Medeiros */ public class SSessionAlreadyExistsException extends SSessionException { private static final long serialVersionUID = -3954242079366628148L; public SSessionAlreadyExistsException(final String message) { super(message); } public SSessionAlreadyExistsException(final String message, final Throwable cause) { super(message, cause); } public SSessionAlreadyExistsException(final Object... arguments) { super(arguments); } public SSessionAlreadyExistsException(final Throwable cause, final Object... arguments) { super(cause, arguments); } public SSessionAlreadyExistsException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-session/src/main/java/org/bonitasoft/engine/session/SSessionException.java ================================================ /** * 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.session; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Elias Ricken de Medeiros */ public class SSessionException extends SBonitaException { private static final long serialVersionUID = 7690456826693549647L; public SSessionException(final String message) { super(message); } public SSessionException(final String message, final Throwable cause) { super(message, cause); } public SSessionException(final Object... arguments) { super(arguments); } public SSessionException(final Throwable cause, final Object... arguments) { super(cause, arguments); } public SSessionException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-session/src/main/java/org/bonitasoft/engine/session/SSessionNotFoundException.java ================================================ /** * 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.session; /** * @author Elias Ricken de Medeiros */ public class SSessionNotFoundException extends SSessionException { private static final long serialVersionUID = -5995789561034211161L; public SSessionNotFoundException(final String message) { super(message); } public SSessionNotFoundException(final String message, final Throwable cause) { super(message, cause); } public SSessionNotFoundException(final Object... arguments) { super(arguments); } public SSessionNotFoundException(final Throwable cause, final Object... arguments) { super(cause, arguments); } public SSessionNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-session/src/main/java/org/bonitasoft/engine/session/SessionProvider.java ================================================ /** * 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.session; import org.bonitasoft.engine.session.model.SSession; public interface SessionProvider { void updateSession(SSession session) throws SSessionNotFoundException; void cleanInvalidSessions(); void removeSessions(); SSession getSession(final long sessionId) throws SSessionNotFoundException; void removeSession(final long sessionId) throws SSessionNotFoundException; void addSession(final SSession session) throws SSessionAlreadyExistsException; void deleteSessions(boolean keepTechnicalSessions); } ================================================ FILE: services/bonita-session/src/main/java/org/bonitasoft/engine/session/SessionService.java ================================================ /** * 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.session; import java.util.List; import java.util.Set; import org.bonitasoft.engine.session.model.SSession; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; /** * @author Elias Ricken de Medeiros * @author Feng Hui * @author Matthieu Chaffotte */ public interface SessionService { /** * name of the system user in the session */ String SYSTEM = "system"; /** * ID of the system * when something is done by the system, if a user id is required, this id is given */ long SYSTEM_ID = -1L; /** * Create a new session for the given user; * * @param userName userName * @return a new session * @throws SSessionException * if some error arrives while creating the session * @since 6.0 */ SSession createSession(String userName) throws SSessionException; SSession createSession(long userId, String userName, boolean technicalUser) throws SSessionException; SSession createSession(long userId, String userName, boolean technicalUser, List profiles, Set permissions) throws SSessionException; /** * Delete a session having the given id * * @param sessionId * the session's id * @throws SSessionNotFoundException * if no session exists for the given id * @since 6.0 */ void deleteSession(final long sessionId) throws SSessionNotFoundException; /** * Delete all invalid sessions * * @since 6.0 */ void cleanInvalidSessions(); /** * Verify if a session is valid * * @param sessionId * the session's id * @return true if the session is valid, false otherwise * @throws SSessionNotFoundException * if no session exists for the given id * @since 6.0 */ boolean isValid(long sessionId) throws SSessionNotFoundException; /** * Retrieve a session by its id * * @param sessionId * the session's id * @return the session associated to the given id * @throws SSessionNotFoundException * if no session exists for the given id * @since 6.0 */ SSession getSession(long sessionId) throws SSessionNotFoundException; /** * @param sessionAccessor * the sessionAccessor that contains the current session * @return the logged user or -1 if there is no session * @since 6.4 */ long getLoggedUserFromSession(ReadSessionAccessor sessionAccessor); /** * Define how long, in milliseconds, the created sessions will be valid. This does not affect already created * session * * @param duration * @since 6.0 */ void setSessionDuration(long duration); /** * Retrieve the default sessions's duration, in milliseconds. * * @return the default sessions's duration * @since 6.0 */ long getDefaultSessionDuration(); /** * Retrieve the duration, in milliseconds, of new created session. If no duration was specified, the default * duration will be used * * @return the duration of new created session. * @since 6.0 */ long getSessionDuration(); /** * Update the expiration and the last update dates of the session. * * @param sessionId * the session id * @throws SSessionException * if some error arrives while creating the session * @since 6.0 */ void renewSession(long sessionId) throws SSessionException; /** * Deletes all the sessions. */ void deleteSessions(); /** * Delete all sessions */ void deleteAllSessions(); /** * Delete all sessions of a tenant except the one of the technical user */ void deleteSessionsExceptTechnicalUser(); } ================================================ FILE: services/bonita-session/src/main/java/org/bonitasoft/engine/session/impl/AbstractSessionProvider.java ================================================ /** * 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.session.impl; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bonitasoft.engine.session.SSessionAlreadyExistsException; import org.bonitasoft.engine.session.SSessionNotFoundException; import org.bonitasoft.engine.session.SessionProvider; import org.bonitasoft.engine.session.model.SSession; /** * @author Baptiste Mesta */ public abstract class AbstractSessionProvider implements SessionProvider { protected abstract Map getSessions(); protected SSession putSession(final SSession session, final long id) { return getSessions().put(id, session); } @Override public synchronized void addSession(final SSession session) throws SSessionAlreadyExistsException { final long id = session.getId(); if (getSessions().containsKey(id)) { throw new SSessionAlreadyExistsException("A session wih id \"" + id + "\" already exists"); } putSession(session, id); } @Override public synchronized void removeSession(final long sessionId) throws SSessionNotFoundException { final SSession session = getSessions().remove(sessionId); if (session == null) { throw new SSessionNotFoundException("No session found with id \"" + sessionId + "\""); } } @Override public synchronized SSession getSession(final long sessionId) throws SSessionNotFoundException { final SSession session = getSessions().get(sessionId); if (session == null) { throw new SSessionNotFoundException("No session found with id \"" + sessionId + "\""); } return session; } @Override public synchronized void updateSession(final SSession session) throws SSessionNotFoundException { final long id = session.getId(); if (!getSessions().containsKey(id)) { throw new SSessionNotFoundException("No session found with id \"" + id + "\""); } putSession(session, id); } @Override public synchronized void cleanInvalidSessions() { final List invalidSessionIds = new ArrayList(); for (final SSession session : getSessions().values()) { if (!session.isValid()) { invalidSessionIds.add(session.getId()); } } for (final Long invalidSessionId : invalidSessionIds) { getSessions().remove(invalidSessionId); } } @Override public synchronized void removeSessions() { getSessions().clear(); } @Override public synchronized void deleteSessions(final boolean keepTechnicalSessions) { getSessions().values().removeIf(sSession -> !keepTechnicalSessions || !sSession.isTechnicalUser()); } } ================================================ FILE: services/bonita-session/src/main/java/org/bonitasoft/engine/session/impl/SessionIdGenerator.java ================================================ /** * 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.session.impl; import static java.lang.Math.abs; import java.security.SecureRandom; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte * @author Baptiste Mesta */ public class SessionIdGenerator { private static volatile SecureRandom numberGenerator = null; public static long getNextId() { SecureRandom ng = numberGenerator; if (ng == null) { numberGenerator = ng = new SecureRandom(); } return abs(ng.nextLong()); } } ================================================ FILE: services/bonita-session/src/main/java/org/bonitasoft/engine/session/impl/SessionProviderImpl.java ================================================ /** * 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.session.impl; import java.util.HashMap; import java.util.Map; import org.bonitasoft.engine.session.SessionProvider; import org.bonitasoft.engine.session.model.SSession; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; @Component @ConditionalOnSingleCandidate(SessionProvider.class) public final class SessionProviderImpl extends AbstractSessionProvider { private final Map sessions = new HashMap<>(); @Override protected Map getSessions() { return sessions; } } ================================================ FILE: services/bonita-session/src/main/java/org/bonitasoft/engine/session/impl/SessionServiceImpl.java ================================================ /** * 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.session.impl; import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; import java.util.Date; import java.util.List; import java.util.Set; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.session.SSessionException; import org.bonitasoft.engine.session.SSessionNotFoundException; import org.bonitasoft.engine.session.SessionProvider; import org.bonitasoft.engine.session.SessionService; import org.bonitasoft.engine.session.model.SSession; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; import org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException; /** * @author Elias Ricken de Medeiros * @author Matthieu Chaffotte */ @Slf4j public class SessionServiceImpl implements SessionService { private static final long DEFAULT_SESSION_DURATION = 3600000; private long sessionDuration = DEFAULT_SESSION_DURATION; private final SessionProvider sessionProvider; private final String applicationName; public SessionServiceImpl(final SessionProvider sessionProvider, final String applicationName) { this.sessionProvider = sessionProvider; this.applicationName = applicationName; } @Override public SSession createSession(final String userName) throws SSessionException { return this.createSession(-1, userName, false); } @Override public SSession createSession(final long userId, final String userName, final boolean isTechnicalUser) throws SSessionException { return createSession(userId, userName, isTechnicalUser, emptyList(), emptySet()); } @Override public SSession createSession(final long userId, final String userName, final boolean isTechnicalUser, List profiles, Set permissions) throws SSessionException { final long id = SessionIdGenerator.getNextId(); Date now = new Date(); SSession session = SSession.builder() .id(id) .duration(sessionDuration) .userName(userName) .applicationName(applicationName) .userId(userId) .technicalUser(isTechnicalUser) .creationDate(now) .lastRenewDate(now) .profiles(profiles) .userPermissions(permissions) .build(); sessionProvider.addSession(session); if (log.isTraceEnabled()) { log.trace("CreateSession with username = <{}>, id = <{}>", userName, id); } return session; } @Override public void deleteSession(final long sessionId) throws SSessionNotFoundException { sessionProvider.removeSession(sessionId); } @Override public boolean isValid(final long sessionId) throws SSessionNotFoundException { return sessionProvider.getSession(sessionId).getExpirationDate().getTime() > new Date().getTime(); } @Override public SSession getSession(final long sessionId) throws SSessionNotFoundException { return sessionProvider.getSession(sessionId).toBuilder().build(); } @Override public long getLoggedUserFromSession(ReadSessionAccessor sessionAccessor) { try { long sessionId = sessionAccessor.getSessionId(); return sessionProvider.getSession(sessionId).getUserId(); } catch (SessionIdNotSetException | SSessionNotFoundException e) { return -1; } } @Override public void setSessionDuration(final long duration) { if (duration <= 0) { throw new IllegalArgumentException("The duration must be greater than 0"); } sessionDuration = duration; log.debug("Session duration set to {}", sessionDuration); } @Override public long getDefaultSessionDuration() { return DEFAULT_SESSION_DURATION; } @Override public long getSessionDuration() { return sessionDuration; } @Override public void renewSession(final long sessionId) throws SSessionException { final SSession session = getSession(sessionId); SSession updatedSession = session.toBuilder().lastRenewDate(new Date()).build(); sessionProvider.updateSession(updatedSession); } @Override public void cleanInvalidSessions() { sessionProvider.cleanInvalidSessions(); } @Override public void deleteAllSessions() { sessionProvider.deleteSessions(false/* don't keep technical user */); } @Override public void deleteSessionsExceptTechnicalUser() { sessionProvider.deleteSessions(true/* keep technical user */); } @Override public void deleteSessions() { sessionProvider.removeSessions(); if (log.isTraceEnabled()) { log.trace("Sessions were deleted."); } } } ================================================ FILE: services/bonita-session/src/main/java/org/bonitasoft/engine/session/model/SSession.java ================================================ /** * 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.session.model; import java.io.Serializable; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Set; import lombok.AccessLevel; import lombok.Builder; import lombok.Data; import lombok.Setter; /** * @author Elias Ricken de Medeiros * @author Yanyan Liu * @author Matthieu Chaffotte */ @Data @Setter(AccessLevel.PRIVATE) // to ensure fields are immutable @Builder(toBuilder = true) public class SSession implements Serializable { private final long id; /** * creation date (GMT+0) */ private Date creationDate; private long duration; /** * last renew date (GMT+0) */ @Setter(AccessLevel.PUBLIC) // for cluster testing private Date lastRenewDate; private String userName; private long userId; //FIXME: remove it, it looks like it is never set private String clusterNode; private String applicationName; /** * true if the user is the technical user: false otherwise */ private boolean technicalUser; private List profiles; @Builder.Default private Set userPermissions = Collections.emptySet(); /** * @return the expiration date (GMT+0) */ public Date getExpirationDate() { return new Date(lastRenewDate.getTime() + duration); } /** * @return true if the session is still valid */ public boolean isValid() { return getExpirationDate().getTime() > System.currentTimeMillis(); } } ================================================ FILE: services/bonita-session/src/main/java/org/bonitasoft/engine/sessionaccessor/ReadSessionAccessor.java ================================================ /** * 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.sessionaccessor; /** * @author Yanyan Liu */ public interface ReadSessionAccessor { /** * Get current session id * * @return the identifier of current session * @throws SessionIdNotSetException * if no session exists for the given id, throw exception * @since 6.0 */ long getSessionId() throws SessionIdNotSetException; } ================================================ FILE: services/bonita-session/src/main/java/org/bonitasoft/engine/sessionaccessor/SessionAccessor.java ================================================ /** * 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.sessionaccessor; /** * @author Yanyan Liu */ public interface SessionAccessor extends ReadSessionAccessor { void setSessionId(long sessionId); void deleteSessionId(); } ================================================ FILE: services/bonita-session/src/main/java/org/bonitasoft/engine/sessionaccessor/SessionIdNotSetException.java ================================================ /** * 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.sessionaccessor; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Yanyan Liu */ public class SessionIdNotSetException extends SBonitaException { private static final long serialVersionUID = -5884549926492597499L; public SessionIdNotSetException(final String message) { super(message); } public SessionIdNotSetException(final Throwable e) { super(e); } public SessionIdNotSetException(final String message, final Throwable e) { super(message, e); } } ================================================ FILE: services/bonita-session/src/main/java/org/bonitasoft/engine/sessionaccessor/ThreadLocalSessionAccessor.java ================================================ /** * 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.sessionaccessor; import org.springframework.stereotype.Component; /** * Store and access the current session id of the current thread */ @Component public class ThreadLocalSessionAccessor implements SessionAccessor { private final ThreadLocal sessionData = new ThreadLocal<>(); @Override public long getSessionId() throws SessionIdNotSetException { Long sessionId = sessionData.get(); if (sessionId == null) { throw new SessionIdNotSetException("No session set."); } return sessionId; } @Override public void setSessionId(long sessionId) { if (sessionId <= 0) { throw new IllegalArgumentException("Session id is invalid: " + sessionId); } sessionData.set(sessionId); } @Override public void deleteSessionId() { sessionData.remove(); } } ================================================ FILE: services/bonita-session/src/test/java/org/bonitasoft/engine/session/impl/SessionProviderImplTest.java ================================================ /** * 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.session.impl; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertNotNull; import org.bonitasoft.engine.session.SSessionNotFoundException; import org.bonitasoft.engine.session.SessionProvider; import org.bonitasoft.engine.session.model.SSession; import org.junit.Before; import org.junit.Test; public class SessionProviderImplTest { private final SessionProvider sessionProvider = new SessionProviderImpl(); @Before public void cleanSession() { //session provider have a static map of sessions sessionProvider.removeSessions(); } @Test public void testAddSession() throws Exception { sessionProvider.addSession(SSession.builder().id(12L).userName("john").userId(12).build()); assertNotNull(sessionProvider.getSession(12)); } @Test public void testRemoveSession() throws Exception { sessionProvider.addSession(SSession.builder().id(12L).userName("john").userId(12).build()); sessionProvider.removeSession(12L); assertThatThrownBy(() -> sessionProvider.getSession(12L)).isInstanceOf(SSessionNotFoundException.class); } @Test public void get_non_existing_session_should_throw_exception() { assertThatThrownBy(() -> sessionProvider.getSession(10L)).isInstanceOf(SSessionNotFoundException.class); } @Test public void should_deleteSessions_keep_technical_sessions() throws Exception { sessionProvider.removeSessions(); sessionProvider.addSession(SSession.builder().id(54L).userName("john").userId(12).build()); sessionProvider.addSession(SSession.builder().id(55L).userName("john").userId(12).technicalUser(true).build()); sessionProvider.deleteSessions(true /* keep technical sessions */); sessionProvider.getSession(55); assertThatThrownBy(() -> sessionProvider.getSession(54L)).isInstanceOf(SSessionNotFoundException.class); } @Test public void should_delete_all_sessions() throws Exception { sessionProvider.removeSessions(); sessionProvider.addSession(SSession.builder().id(55L).userName("tech").userId(13).technicalUser(true).build()); sessionProvider.addSession(SSession.builder().id(56L).userName("john").userId(14).build()); sessionProvider.deleteSessions(false /* keep technical sessions */); assertThatThrownBy(() -> sessionProvider.getSession(55L)).isInstanceOf(SSessionNotFoundException.class); assertThatThrownBy(() -> sessionProvider.getSession(56L)).isInstanceOf(SSessionNotFoundException.class); } } ================================================ FILE: services/bonita-session/src/test/java/org/bonitasoft/engine/session/impl/SessionServiceImplTest.java ================================================ /** * 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.session.impl; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; import org.bonitasoft.engine.session.SSessionNotFoundException; import org.bonitasoft.engine.session.SessionProvider; import org.bonitasoft.engine.session.model.SSession; import org.bonitasoft.engine.sessionaccessor.ReadSessionAccessor; import org.bonitasoft.engine.sessionaccessor.SessionIdNotSetException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Celine Souchet */ @RunWith(MockitoJUnitRunner.class) public class SessionServiceImplTest { private static final long SESSION_ID = 1258L; private static final String USER_NAME = "john"; private static final long USER_ID = 58L; @Mock private SessionProvider sessionProvider; @Mock private ReadSessionAccessor sessionAccessor; @InjectMocks private SessionServiceImpl sessionServiceImpl; private SSession sSession; @Before public void setUp() { sSession = SSession.builder().id(SESSION_ID).userName(USER_NAME).userId(USER_ID).build(); } /** * Test method for {@link org.bonitasoft.engine.session.impl.SessionServiceImpl#getDefaultSessionDuration()}. */ @Test public final void getDefaultSessionDuration() { assertEquals(3600000, sessionServiceImpl.getDefaultSessionDuration()); } /** * Test method for {@link org.bonitasoft.engine.session.impl.SessionServiceImpl#getSessionDuration()}. */ @Test public final void getSessionDuration() { assertEquals(3600000, sessionServiceImpl.getSessionDuration()); } /** * Test method for {@link org.bonitasoft.engine.session.impl.SessionServiceImpl#cleanInvalidSessions()}. */ @Test public final void cleanInvalidSessions() { sessionServiceImpl.cleanInvalidSessions(); verify(sessionProvider, times(1)).cleanInvalidSessions(); } /** * Test method for {@link org.bonitasoft.engine.session.impl.SessionServiceImpl#deleteSessions()}. */ @Test public final void deleteSessions() { sessionServiceImpl.deleteSessions(); verify(sessionProvider, times(1)).removeSessions(); } @Test public final void deleteAllSessions() { sessionServiceImpl.deleteAllSessions(); verify(sessionProvider, times(1)).deleteSessions(false); } @Test public final void deleteSessionsExceptTechnicalUser() { sessionServiceImpl.deleteSessionsExceptTechnicalUser(); verify(sessionProvider, times(1)).deleteSessions(true); } @Test public final void should_getLoggedUserFromSession_return_user_id_when_there_is_a_session() throws Exception { //given doReturn(SESSION_ID).when(sessionAccessor).getSessionId(); doReturn(sSession).when(sessionProvider).getSession(SESSION_ID); //when final long loggedUserFromSession = sessionServiceImpl.getLoggedUserFromSession(sessionAccessor); //when assertThat(loggedUserFromSession).isEqualTo(USER_ID); } @Test public final void should_getLoggedUserFromSession_return_minus_1_when_there_is_no_session() throws SessionIdNotSetException { //given doThrow(SessionIdNotSetException.class).when(sessionAccessor).getSessionId(); //when final long loggedUserFromSession = sessionServiceImpl.getLoggedUserFromSession(sessionAccessor); //when assertThat(loggedUserFromSession).isEqualTo(-1); } @Test public final void should_getLoggedUserFromSession_return_minus_1_when_the_session_is_not_found() throws SessionIdNotSetException, SSessionNotFoundException { //given doReturn(SESSION_ID).when(sessionAccessor).getSessionId(); doThrow(SSessionNotFoundException.class).when(sessionProvider).getSession(SESSION_ID); //when final long loggedUserFromSession = sessionServiceImpl.getLoggedUserFromSession(sessionAccessor); //when assertThat(loggedUserFromSession).isEqualTo(-1); } } ================================================ FILE: services/bonita-session/src/test/java/org/bonitasoft/engine/sessionaccessor/ThreadLocalSessionAccessorTest.java ================================================ /** * 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.sessionaccessor; import static org.assertj.core.api.Assertions.assertThat; import org.junit.Test; public class ThreadLocalSessionAccessorTest { private final ThreadLocalSessionAccessor threadLocalSessionAccessor = new ThreadLocalSessionAccessor(); @Test public void should_set_session_id() throws Exception { threadLocalSessionAccessor.setSessionId(12); assertThat(threadLocalSessionAccessor.getSessionId()).isEqualTo(12); } @Test(expected = SessionIdNotSetException.class) public void should_throw_SessionIfNotSet_when_session_is_not_set() throws Exception { threadLocalSessionAccessor.getSessionId(); } @Test(expected = SessionIdNotSetException.class) public void should_throw_SessionIfNotSet_when_only_tenant_is_set() throws Exception { threadLocalSessionAccessor.getSessionId(); } @Test(expected = IllegalArgumentException.class) public void should_throw_IllegalArgumentException_when_session_id_negative() { threadLocalSessionAccessor.setSessionId(-1); } @Test(expected = IllegalArgumentException.class) public void should_throw_IllegalArgumentException_when_session_id_0() { threadLocalSessionAccessor.setSessionId(0); } } ================================================ FILE: services/bonita-temporary-content/build.gradle ================================================ dependencies { api project(':services:bonita-persistence') api project(':services:bonita-commons') testImplementation libs.mockitoCore testImplementation libs.systemRules testImplementation libs.assertj testRuntimeOnly libs.logback annotationProcessor libs.lombok compileOnly libs.lombok } ================================================ FILE: services/bonita-temporary-content/src/main/java/org/bonitasoft/engine/temporary/content/STemporaryContent.java ================================================ /** * Copyright (C) 2023 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.temporary.content; import java.sql.Blob; import java.util.UUID; import javax.persistence.*; import lombok.Data; import lombok.NoArgsConstructor; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.bonitasoft.engine.persistence.PersistentObject; /** * @author Haroun EL ALAMI */ @Data @NoArgsConstructor @Entity @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @Table(name = "temporary_content") @Cacheable(false) public class STemporaryContent implements PersistentObject { @Id private long id; /** * Date when the temporary content was added */ @Column private long creationDate; /** * Unique identifier sent to the client after upload is complete to retrieve the file */ @Column(name = "key_") private String key; /** * Original uploaded file name */ @Column private String fileName; /** * The mimeType of the file */ @Column private String mimeType; /** * Content of the file uploaded (accessed with Streams) */ @Lob private Blob content; public STemporaryContent(final String fileName, Blob content, String mimeType) { super(); String fileExtension = FilenameUtils.getExtension(fileName); String keyUUID = UUID.randomUUID().toString(); this.key = StringUtils.isNotBlank(fileExtension) ? keyUUID + "." + fileExtension : keyUUID; this.fileName = fileName; this.content = content; this.creationDate = System.currentTimeMillis(); this.mimeType = mimeType; } } ================================================ FILE: services/bonita-temporary-content/src/main/resources/org/bonitasoft/engine/temporary/content/hibernate/temporary.content.queries.hbm.xml ================================================ SELECT u FROM org.bonitasoft.engine.temporary.content.STemporaryContent AS u WHERE u.key = :key DELETE FROM org.bonitasoft.engine.temporary.content.STemporaryContent AS u WHERE u.creationDate < :creationDate ================================================ FILE: services/bonita-temporary-content/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: services/bonita-time-tracker/build.gradle ================================================ dependencies { api project(':services:bonita-commons') api libs.commonsCollections testImplementation libs.mockitoCore testImplementation libs.assertj testImplementation libs.logback } description = 'Bonita Time Tracker' ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/AbstractFlushEventListener.java ================================================ /** * 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.tracking; /** * @author Charles Souillard */ public abstract class AbstractFlushEventListener implements FlushEventListener { private boolean active = false; protected AbstractFlushEventListener(final boolean activateByDefault) { this.active = activateByDefault; } @Override public String getStatus() { return getName() + ": \n" + "active: " + isActive(); } @Override public String getName() { return this.getClass().getName(); } @Override public boolean isActive() { return this.active; } @Override public void activate() { this.active = true; notifyStartTracking(); } @Override public void deactivate() { this.active = false; notifyStopTracking(); } } ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/Clock.java ================================================ /** * 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.tracking; public interface Clock { boolean sleep(final long millis) throws InterruptedException; } ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/FlushEvent.java ================================================ /** * 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.tracking; import java.util.Collections; import java.util.List; public final class FlushEvent { private final List records; private final long flushTime; public FlushEvent(final long flushTime, final List records) { if (records != null) { this.records = records; } else { this.records = Collections.emptyList(); } this.flushTime = flushTime; } public List getRecords() { return this.records; } public long getFlushTime() { return this.flushTime; } @Override public String toString() { return "FlushEvent{" + "records.size=" + this.records.size() + ", flushTime=" + this.flushTime + '}'; } } ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/FlushEventListener.java ================================================ /** * 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.tracking; public interface FlushEventListener { FlushEventListenerResult flush(final FlushEvent flushEvent) throws Exception; String getStatus(); String getName(); boolean isActive(); void notifyStopTracking(); void activate(); void deactivate(); void notifyStartTracking(); } ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/FlushEventListenerResult.java ================================================ /** * 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.tracking; public class FlushEventListenerResult { private final FlushEvent flushEvent; public FlushEventListenerResult(final FlushEvent flushEvent) { super(); this.flushEvent = flushEvent; } public FlushEvent getFlushEvent() { return this.flushEvent; } } ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/FlushResult.java ================================================ /** * 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.tracking; import java.util.List; /** * @author Charles Souillard */ public class FlushResult { private final long flushTime; private final List flushEventListenerResults; public FlushResult(final long flushTime, final List flushEventListenerResults) { this.flushTime = flushTime; this.flushEventListenerResults = flushEventListenerResults; } public List getFlushEventListenerResults() { return this.flushEventListenerResults; } public long getFlushTime() { return this.flushTime; } } ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/FlushThread.java ================================================ /** * 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.tracking; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class FlushThread extends Thread { private static final Logger log = LoggerFactory.getLogger(FlushThread.class); private final TimeTracker timeTracker; public FlushThread(final TimeTracker timeTracker) { super("Bonita-TimeTracker-FlushThread"); this.timeTracker = timeTracker; } @Override public void run() { log.info("Starting " + getName() + "..."); long lastFlushTimestamp = System.currentTimeMillis(); while (true) { final long now = System.currentTimeMillis(); try { final long sleepTime = getSleepTime(now, lastFlushTimestamp); log.debug("FlushThread: sleeping for: " + sleepTime + "ms"); this.timeTracker.getClock().sleep(sleepTime); } catch (final InterruptedException e) { // Make sure to propagate the interruption to cleanly stop the current thread. Thread.currentThread().interrupt(); break; } lastFlushTimestamp = flush(now); } log.info(getName() + " stopped."); } long getSleepTime(final long now, final long lastFlushTimestamp) throws InterruptedException { final long flushDuration = now - lastFlushTimestamp; return this.timeTracker.getFlushIntervalInMS() - flushDuration; } long flush(final long now) { try { final FlushResult flushResult = this.timeTracker.flush(); return flushResult.getFlushTime(); } catch (final Exception e) { log.warn("Exception caught while flushing: " + e.getMessage(), e); } return now; } public boolean isStarted() { return isAlive(); } } ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/Record.java ================================================ /** * 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.tracking; public class Record { private final long timestamp; private final TimeTrackerRecords name; private final String description; private final long duration; public Record(final long timestamp, final TimeTrackerRecords name, final String description, final long duration) { super(); this.timestamp = timestamp; this.name = name; this.description = description; this.duration = duration; } public long getTimestamp() { return this.timestamp; } public TimeTrackerRecords getName() { return this.name; } public String getDescription() { return this.description; } public long getDuration() { return this.duration; } } ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/ThreadSleepClockImpl.java ================================================ /** * 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.tracking; public class ThreadSleepClockImpl implements Clock { @Override public boolean sleep(final long millis) throws InterruptedException { Thread.sleep(millis); return true; } } ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/TimeTracker.java ================================================ /** * 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.tracking; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.collections4.queue.CircularFifoQueue; import org.bonitasoft.engine.commons.TenantLifecycleService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TimeTracker implements TenantLifecycleService { private static final Logger log = LoggerFactory.getLogger(TimeTracker.class); private final Set activatedRecords; private FlushThread flushThread; private final Map flushEventListeners; private final Queue records; private final Clock clock; private long flushIntervalInMS; private boolean startTracking = false; private boolean serviceStarted; private long lastFlushTimestamp = 0L; public TimeTracker( final boolean startTracking, final List flushEventListeners, final int maxSize, final int flushIntervalInSeconds, final String... activatedRecords) { this(new ThreadSleepClockImpl(), startTracking, flushEventListeners, maxSize, flushIntervalInSeconds * 1000, activatedRecords); } public TimeTracker( final Clock clock, final boolean startTracking, final List flushEventListeners, final int maxSize, final int flushIntervalInMS, final String... activatedRecords) { super(); this.startTracking = startTracking; this.clock = clock; this.flushIntervalInMS = flushIntervalInMS; this.records = new CircularFifoQueue<>(maxSize); this.serviceStarted = false; this.flushEventListeners = new ConcurrentHashMap<>(); if (flushEventListeners != null) { for (final FlushEventListener flushEventListener : flushEventListeners) { final String name = flushEventListener.getName(); if (this.flushEventListeners.containsKey(name)) { log.error("Duplicate entry for flushEventListener with name: " + name); } this.flushEventListeners.put(name, flushEventListener); } } if (activatedRecords == null || activatedRecords.length == 0) { this.activatedRecords = Collections.emptySet(); } else { this.activatedRecords = new HashSet<>(); for (final String activatedRecord : activatedRecords) { this.activatedRecords.add(TimeTrackerRecords.valueOf(activatedRecord)); } } log.info(getStatus()); } /** * get the list of Active Listener */ public List getActiveFlushEventListeners() { final List active = new ArrayList<>(); for (final FlushEventListener flushEventListener : this.flushEventListeners.values()) { if (flushEventListener.isActive()) { active.add(flushEventListener); } } return active; } /** * return all Event Listeners, active or not */ public List getFlushEventListeners() { return new ArrayList(this.flushEventListeners.values()); } /** * reference a new flushEventListener. The key of the reference is the flushEventListener.name(). * If a listener exist with this name, it will be replaced. */ public void addFlushEventListener(final FlushEventListener flushEventListener) { this.flushEventListeners.put(flushEventListener.getName(), flushEventListener); } /** * remove a flush event listener */ public void removeFlushEventListener(final String flushEventListenerName) { this.flushEventListeners.remove(flushEventListenerName); } public boolean activateFlushEventListener(final String flushEventListenerName) { final FlushEventListener flushEventListener = this.flushEventListeners.get(flushEventListenerName); if (flushEventListener == null) { return false; } flushEventListener.activate(); return true; } public boolean deactivateFlushEventListener(final String flushEventListenerName) { final FlushEventListener flushEventListener = this.flushEventListeners.get(flushEventListenerName); if (flushEventListener == null) { return false; } flushEventListener.deactivate(); return true; } public void activateRecord(final TimeTrackerRecords activatedRecord) { this.activatedRecords.add(activatedRecord); } public void deactivatedRecord(final TimeTrackerRecords activatedRecord) { this.activatedRecords.remove(activatedRecord); } public Set getActivatedRecords() { return Collections.unmodifiableSet(this.activatedRecords); } public void startTracking() { if (!this.serviceStarted) { log.warn("Cannot start Time tracker tracking because service is not started."); return; } this.startTracking = true; internalStartTracking(); } public void stopTracking() { this.startTracking = false; internalStopTracking(); } FlushThread createFlushThread() { return new FlushThread(this); } private void internalStartTracking() { if (this.startTracking) { log.warn("Starting Time tracker tracking..."); this.flushThread = createFlushThread(); this.flushThread.start(); for (final FlushEventListener listener : getActiveFlushEventListeners()) { listener.notifyStartTracking(); } log.warn( "Time tracker tracking is activated. This may not be used in production as performances may be strongly impacted."); } } private void internalStopTracking() { if (isTracking()) { log.warn("Stopping Time tracker tracking..."); if (this.flushThread.isStarted()) { this.flushThread.interrupt(); try { // Wait for the thread to die this.flushThread.join(); } catch (final InterruptedException e) { // We want this thread to be interrupted. No need to do extra work here, we are in the desired state. } } for (final FlushEventListener listener : getActiveFlushEventListeners()) { listener.notifyStopTracking(); } log.warn("Time tracker tracking is deactivated."); } } public boolean isTracking() { return this.flushThread != null && this.flushThread.isStarted(); } public long getFlushIntervalInMS() { return this.flushIntervalInMS; } public void setFlushIntervalInSeconds(final long flushIntervalInSeconds) { this.flushIntervalInMS = flushIntervalInSeconds * 1000; } public void setFlushIntervalInMS(final long flushIntervalInMS) { this.flushIntervalInMS = flushIntervalInMS; } public Clock getClock() { return this.clock; } public String getStatus() { final StringBuilder sb = new StringBuilder(); sb.append("-----"); sb.append("\n"); sb.append("Time Tracker '"); sb.append(this.getClass().getName()); sb.append("':"); sb.append("\n"); sb.append(" - trackingEnabled: "); sb.append(isTracking()); sb.append("\n"); sb.append(" - flushIntervalInSeconds: "); sb.append(this.flushIntervalInMS); sb.append("\n"); sb.append(" - activatedRecords: "); for (final TimeTrackerRecords activatedRecord : this.activatedRecords) { sb.append(activatedRecord.name()); sb.append(" "); } sb.append("\n"); sb.append(" - flushEventListeners: "); for (final FlushEventListener flushEventListener : this.flushEventListeners.values()) { sb.append(flushEventListener.getStatus()); sb.append(" "); } sb.append("\n"); sb.append(" - records.size: "); sb.append(this.records.size()); sb.append("\n"); sb.append(" - last flush occurrence: "); sb.append(new Date(this.lastFlushTimestamp).toString()); sb.append("\n"); sb.append("\n"); sb.append("-----"); return sb.toString(); } public boolean isTrackable(final TimeTrackerRecords recordName) { return isTracking() && this.activatedRecords.contains(recordName); } public void track(final TimeTrackerRecords recordName, final String recordDescription, final long duration) { if (!isTrackable(recordName)) { return; } final long timestamp = System.currentTimeMillis(); final Record record = new Record(timestamp, recordName, recordDescription, duration); log.debug("Tracking record: " + record); synchronized (this) { this.records.add(record); } } public FlushResult flush() { log.info("Flushing..."); this.lastFlushTimestamp = System.currentTimeMillis(); final List flushEventListenerResults = new ArrayList<>(); final FlushResult flushResult = new FlushResult(this.lastFlushTimestamp, flushEventListenerResults); final List records; synchronized (this) { records = getRecordsCopy(); clearRecords(); } final FlushEvent flushEvent = new FlushEvent(this.lastFlushTimestamp, records); flushListeners(flushEvent, flushEventListenerResults); log.info("Flush finished: " + flushEvent); return flushResult; } void flushListeners(final FlushEvent flushEvent, final List flushEventListenerResults) { if (this.flushEventListeners == null) { return; } for (final FlushEventListener listener : getActiveFlushEventListeners()) { try { final FlushEventListenerResult flushEventListenerResult = listener.flush(flushEvent); flushEventListenerResults.add(flushEventListenerResult); } catch (final Exception e) { log.warn("Exception while flushing: " + flushEvent + " on listener " + listener); } } } public List getRecordsCopy() { return Arrays.asList(this.records.toArray(new Record[this.records.size()])); } public void clearRecords() { this.records.clear(); } @Override public void start() { if (this.serviceStarted) { return; } log.info("Starting TimeTracker..."); this.serviceStarted = true; internalStartTracking(); log.info("TimeTracker started."); } @Override public void stop() { if (!this.serviceStarted) { return; } log.info("Stopping TimeTracker..."); this.serviceStarted = false; internalStopTracking(); log.info("TimeTracker stopped."); } @Override public void pause() { stop(); } @Override public void resume() { start(); } } ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/TimeTrackerRecords.java ================================================ /** * 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.tracking; public enum TimeTrackerRecords { /** * this key is used to track the connector execution (execute method only, not in/out parameters * processing) including the pool submission (that may have * additional impact if the pool is full). */ EXECUTE_CONNECTOR_INCLUDING_POOL_SUBMIT, /** * this key is used to track the connector execution (execute method only, not in/out parameters * processing), without potential pool submission impact */ EXECUTE_CONNECTOR_CALLABLE, /** * this key is used to track connector output parameters processing only (not pooling, not input, * not execute) */ EXECUTE_CONNECTOR_OUTPUT_OPERATIONS, /** * this key is used to track connector input parameters processing only (not pooling, not execute, * not output) */ EXECUTE_CONNECTOR_INPUT_EXPRESSIONS, /** * this key is used to track only the call to disconnect on a connector */ EXECUTE_CONNECTOR_DISCONNECT, /** * this key is used to track the whole connector execution including pooling, input, execute, * output and disconnect */ EXECUTE_CONNECTOR_WORK, /** * this key is used to track the whole expression evaluation including its context. See * ExpressionResolver. */ EVALUATE_EXPRESSION_INCLUDING_CONTEXT, /** * this key is used to track the expression evaluation "only", assuming the context is already * evaluated if necessary. See ExpressionService. */ EVALUATE_EXPRESSION, /** * this key is used to track the expression evaluation "only", assuming the context is already * evaluated if necessary. Evaluates a set of expression in one * measure. See ExpressionService. */ EVALUATE_EXPRESSIONS, } ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/csv/CSVFlushEventListener.java ================================================ /** * 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.tracking.csv; import java.io.File; import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.List; import org.bonitasoft.engine.tracking.AbstractFlushEventListener; import org.bonitasoft.engine.tracking.FlushEvent; import org.bonitasoft.engine.tracking.Record; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CSVFlushEventListener extends AbstractFlushEventListener { private static final Logger log = LoggerFactory.getLogger(CSVFlushEventListener.class); private final String csvSeparator; private final String outputFolder; public static final String FILE_PREFIX = "bonita_timetracker_"; public static final String FILE_SUFFIX = ".csv"; public CSVFlushEventListener(final boolean activateAtStart, final String outputFolder, final String csvSeparator) { super(activateAtStart); this.outputFolder = outputFolder; this.csvSeparator = csvSeparator; final File outputFolderFile = new File(outputFolder); if (!outputFolderFile.exists()) { throw new RuntimeException("Output folder does not exist: " + outputFolder); } if (!outputFolderFile.isDirectory()) { throw new RuntimeException("Output folder is not a directory: " + outputFolder); } } @Override public CSVFlushEventListenerResult flush(final FlushEvent flushEvent) throws Exception { final long flushTime = flushEvent.getFlushTime(); final List records = flushEvent.getRecords(); final List> csvContent = new ArrayList>(); for (final Record record : records) { final List row = getRow(record); csvContent.add(row); } final File outputFile = getDayFile(flushTime, this.outputFolder, FILE_PREFIX, FILE_SUFFIX); if (!outputFile.exists()) { log.info("Generating new csv file to: {} ", outputFile); CSVUtil.writeCSVRow(outputFile, getHeaderRow(), this.csvSeparator); } else { log.info("Reusing csv file: {}", outputFile); } CSVUtil.writeCSVRows(outputFile, csvContent, this.csvSeparator); return new CSVFlushEventListenerResult(flushEvent, outputFile); } @Override public String getStatus() { String status = super.getStatus() + "\n"; status += "outputFolder: " + this.outputFolder + "\n"; return status; } @Override public void notifyStopTracking() { //nothing to do } @Override public void notifyStartTracking() { //nothing to do } private List getRow(final Record record) { final long timestamp = record.getTimestamp(); final GregorianCalendar cal = new GregorianCalendar(); cal.setTimeInMillis(timestamp); final int year = cal.get(Calendar.YEAR); final int month = cal.get(Calendar.MONTH) + 1; final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH); final int hourOfDay = cal.get(Calendar.HOUR_OF_DAY); final int minute = cal.get(Calendar.MINUTE); final int second = cal.get(Calendar.SECOND); final int millisecond = cal.get(Calendar.MILLISECOND); final List row = new ArrayList(); row.add(String.valueOf(timestamp)); row.add(String.valueOf(year)); row.add(String.valueOf(month)); row.add(String.valueOf(dayOfMonth)); row.add(String.valueOf(hourOfDay)); row.add(String.valueOf(minute)); row.add(String.valueOf(second)); row.add(String.valueOf(millisecond)); row.add(String.valueOf(record.getDuration())); row.add(record.getName().name()); row.add(record.getDescription()); return row; } private List getHeaderRow() { final List header = new ArrayList(); header.add("timestamp"); header.add("year"); header.add("month"); header.add("dayOfMonth"); header.add("hourOfDay"); header.add("minute"); header.add("second"); header.add("millisecond"); header.add("duration"); header.add("name"); header.add("description"); return header; } private String getIntOnTwoNumbers(final int i) { if (i < 10) { return "0" + i; } return Integer.toString(i); } private File getDayFile(final long time, final String folder, final String filePrefix, final String fileSuffix) { final StringBuilder sb = new StringBuilder(); final GregorianCalendar c = new GregorianCalendar(); c.setTimeInMillis(time); sb.append(getIntOnTwoNumbers(c.get(Calendar.YEAR))); sb.append("_"); sb.append(getIntOnTwoNumbers(c.get(Calendar.MONTH) + 1)); sb.append("_"); sb.append(getIntOnTwoNumbers(c.get(Calendar.DAY_OF_MONTH))); final String timestamp = sb.toString(); final String fileName = filePrefix + timestamp + fileSuffix; return new File(folder, fileName); } } ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/csv/CSVFlushEventListenerResult.java ================================================ /** * 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.tracking.csv; import java.io.File; import org.bonitasoft.engine.tracking.FlushEvent; import org.bonitasoft.engine.tracking.FlushEventListenerResult; public class CSVFlushEventListenerResult extends FlushEventListenerResult { private final File outputFile; public CSVFlushEventListenerResult(final FlushEvent flushEvent, final File outputFile) { super(flushEvent); this.outputFile = outputFile; } public File getOutputFile() { return this.outputFile; } } ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/csv/CSVUtil.java ================================================ /** * 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.tracking.csv; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Scanner; public class CSVUtil { public static List> readCSV(final boolean excludeHeader, final File csvFile, final String csvSeparator) throws FileNotFoundException { final List> array = new ArrayList>(); final InputStream inputStream = new FileInputStream(csvFile); final Scanner scanner = new Scanner(inputStream); try { while (scanner.hasNextLine()) { final String line = scanner.nextLine(); final List lineElements = new ArrayList(); lineElements.addAll(Arrays.asList(line.split(csvSeparator))); array.add(lineElements); } } finally { scanner.close(); try { inputStream.close(); } catch (final IOException e) { throw new RuntimeException(e); } } if (excludeHeader) { array.remove(0); } return array; } public static void writeCSVRow(final FileWriter writer, final List row, final String csvSeparator) throws IOException { boolean first = true; for (final String value : row) { if (first) { writer.append(value); first = false; } else { writer.append(csvSeparator); writer.append(value); } } writer.append("\n"); writer.flush(); } public static void writeCSVRow(final File file, final List row, final String csvSeparator) throws IOException { final FileWriter writer = new FileWriter(file, true); writeCSVRow(writer, row, csvSeparator); writer.close(); } public static void writeCSVRows(final File file, final List> array, final String csvSeparator) throws IOException { final FileWriter writer = new FileWriter(file, true); for (final List row : array) { writeCSVRow(writer, row, csvSeparator); } writer.close(); } } ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/memory/DayRecord.java ================================================ /** * 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.tracking.memory; import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.List; import java.util.Queue; import org.apache.commons.collections4.queue.CircularFifoQueue; import org.bonitasoft.engine.tracking.Record; /** * @author Charles Souillard */ public class DayRecord { private final String dayKey; private final Queue records; public DayRecord(final long timestamp, final int maxSize) { this.dayKey = getDayKey(timestamp); this.records = new CircularFifoQueue(maxSize); } public String getDayKey() { return this.dayKey; } public List getRecordsCopy() { return new ArrayList<>(this.records); } public boolean isExpectedDayKey(final long timestamp) { final String expectedDayKey = getDayKey(timestamp); return this.dayKey.equals(expectedDayKey); } private String getDayKey(final long timestamp) { final GregorianCalendar cal = new GregorianCalendar(); cal.setTimeInMillis(timestamp); final int year = cal.get(Calendar.YEAR); final int month = cal.get(Calendar.MONTH) + 1; final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH); return String.valueOf(year) + String.valueOf(month) + String.valueOf(dayOfMonth); } public void addRecords(final List newRecords) { this.records.addAll(newRecords); } @Override public String toString() { return "DayRecord{" + "dayKey='" + this.dayKey + '\'' + ", records.size=" + this.records.size() + '}'; } } ================================================ FILE: services/bonita-time-tracker/src/main/java/org/bonitasoft/engine/tracking/memory/MemoryFlushEventListener.java ================================================ /** * 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.tracking.memory; import org.bonitasoft.engine.tracking.AbstractFlushEventListener; import org.bonitasoft.engine.tracking.FlushEvent; import org.bonitasoft.engine.tracking.FlushEventListenerResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class MemoryFlushEventListener extends AbstractFlushEventListener { private static final Logger log = LoggerFactory.getLogger(MemoryFlushEventListener.class); private final int maxSize; private DayRecord dayRecord; public MemoryFlushEventListener(final boolean activateAtStart, final int maxSize) { super(activateAtStart); this.maxSize = maxSize; } @Override public synchronized FlushEventListenerResult flush(final FlushEvent flushEvent) throws Exception { if (flushEvent.getRecords().size() == 0) { return new FlushEventListenerResult(flushEvent); } log.debug("Reusing csv file: FlushEvent received with {} records.", flushEvent.getRecords().size()); final long flushTime = flushEvent.getFlushTime(); if (this.dayRecord == null || !this.dayRecord.isExpectedDayKey(flushTime)) { this.dayRecord = new DayRecord(flushTime, this.maxSize); } this.dayRecord.addRecords(flushEvent.getRecords()); log.info("Adding '{}' records to DayRecord with dayKey '{}'", flushEvent.getRecords().size(), this.dayRecord.getDayKey()); return new FlushEventListenerResult(flushEvent); } @Override public String getStatus() { String status = super.getStatus() + "\n"; if (this.dayRecord == null) { status += "No DayRecord registered in memory."; } else { status += ", dayRecord: " + this.dayRecord.toString(); } return status; } @Override public void notifyStopTracking() { this.dayRecord = null; } @Override public void notifyStartTracking() { //nothing to do } public synchronized DayRecord getDayRecord() { return this.dayRecord; } public synchronized void clear() { this.dayRecord = null; } } ================================================ FILE: services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/AbstractFlushEventListenerTest.java ================================================ /** * 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.tracking; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class AbstractFlushEventListenerTest extends AbstractTimeTrackerTest { @Test public void should_deactivate_notifyStopTracking() { final AbstractFlushEventListener listener = spy(new AbstractFlushEventListener(true) { @Override public FlushEventListenerResult flush(final FlushEvent flushEvent) throws Exception { return null; } @Override public void notifyStopTracking() { } @Override public void notifyStartTracking() { } }); listener.deactivate(); verify(listener, times(1)).notifyStopTracking(); } @Test public void should_activate_notifyStartTracking() { final AbstractFlushEventListener listener = spy(new AbstractFlushEventListener(true) { @Override public FlushEventListenerResult flush(final FlushEvent flushEvent) throws Exception { return null; } @Override public void notifyStopTracking() { } @Override public void notifyStartTracking() { } }); listener.activate(); verify(listener, times(1)).notifyStartTracking(); } } ================================================ FILE: services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/AbstractTimeTrackerTest.java ================================================ /** * 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.tracking; public class AbstractTimeTrackerTest { protected void checkRecord(final Record expected, final Record actual) { RecordAssert.assertThat(expected).isEqualTo(actual); } } ================================================ FILE: services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/FlushThreadTest.java ================================================ /** * 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.tracking; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.ArrayList; import org.junit.Test; public class FlushThreadTest { @SuppressWarnings("unchecked") @Test public void should_flush_thread_flush_until_interruption() throws Exception { final TimeTracker timeTracker = mock(TimeTracker.class); final Clock clock = mock(Clock.class); final FlushResult flushResult = new FlushResult(System.currentTimeMillis(), new ArrayList<>()); when(timeTracker.getClock()).thenReturn(clock); when(timeTracker.getFlushIntervalInMS()).thenReturn(0L); when(timeTracker.flush()).thenReturn(flushResult); when(clock.sleep(anyLong())).thenReturn(true).thenReturn(true).thenReturn(true) .thenThrow(InterruptedException.class); final FlushThread flushThread = new FlushThread(timeTracker); flushThread.start(); // wait max 1 minute to not freeze CI in case of a bug flushThread.join(60000); verify(timeTracker, times(3)).flush(); verify(clock, times(4)).sleep(anyLong()); } @Test public void should_flush_thread_flush_on_latest_flush_interval() throws Exception { final TimeTracker timeTracker = mock(TimeTracker.class); final FlushThread flushThread = new FlushThread(timeTracker); final long now = 10; final long lastFlushTimestamp = 9; when(timeTracker.getFlushIntervalInMS()).thenReturn(4L); assertEquals(3, flushThread.getSleepTime(now, lastFlushTimestamp)); when(timeTracker.getFlushIntervalInMS()).thenReturn(3L); assertEquals(2, flushThread.getSleepTime(now, lastFlushTimestamp)); } @Test public void should_last_flush_timestamp_move_to_now() { final TimeTracker timeTracker = mock(TimeTracker.class); final FlushThread flushThread = new FlushThread(timeTracker); final long now = 10; when(timeTracker.flush()).thenThrow(RuntimeException.class); final long lastFlushTimestamp = flushThread.flush(now); assertEquals(now, lastFlushTimestamp); } } ================================================ FILE: services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/RecordAssert.java ================================================ /** * 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.tracking; import org.assertj.core.api.AbstractAssert; import org.assertj.core.util.Objects; /** * {@link Record} specific assertions - Generated by CustomAssertionGenerator. */ public class RecordAssert extends AbstractAssert { /** * Creates a new {@link RecordAssert} to make assertions on actual Record. * * @param actual the Record we want to make assertions on. */ public RecordAssert(final Record actual) { super(actual, RecordAssert.class); } /** * An entry point for RecordAssert to follow AssertJ standard assertThat() * statements.
      * With a static import, one can write directly: assertThat(myRecord) and get * specific assertion with code completion. * * @param actual the Record we want to make assertions on. * @return a new {@link RecordAssert} */ public static RecordAssert assertThat(final Record actual) { return new RecordAssert(actual); } /** * Verifies that the actual Record's description is equal to the given one. * * @param description the given description to compare the actual Record's description to. * @return this assertion object. * @throws AssertionError - if the actual Record's description is not equal to the given one. */ public RecordAssert hasDescription(final String description) { // check that actual Record we want to make assertions on is not null. isNotNull(); // overrides the default error message with a more explicit one final String assertjErrorMessage = "\nExpecting description of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>"; // null safe check final String actualDescription = this.actual.getDescription(); if (!Objects.areEqual(actualDescription, description)) { failWithMessage(assertjErrorMessage, this.actual, description, actualDescription); } // return the current assertion for method chaining return this; } /** * Verifies that the actual Record's duration is equal to the given one. * * @param duration the given duration to compare the actual Record's duration to. * @return this assertion object. * @throws AssertionError - if the actual Record's duration is not equal to the given one. */ public RecordAssert hasDuration(final long duration) { // check that actual Record we want to make assertions on is not null. isNotNull(); // overrides the default error message with a more explicit one final String assertjErrorMessage = "\nExpecting duration of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>"; // check final long actualDuration = this.actual.getDuration(); if (actualDuration != duration) { failWithMessage(assertjErrorMessage, this.actual, duration, actualDuration); } // return the current assertion for method chaining return this; } /** * Verifies that the actual Record's name is equal to the given one. * * @param name the given name to compare the actual Record's name to. * @return this assertion object. * @throws AssertionError - if the actual Record's name is not equal to the given one. */ public RecordAssert hasName(final TimeTrackerRecords name) { // check that actual Record we want to make assertions on is not null. isNotNull(); // overrides the default error message with a more explicit one final String assertjErrorMessage = "\nExpecting name of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>"; // null safe check final TimeTrackerRecords actualName = this.actual.getName(); if (!Objects.areEqual(actualName, name)) { failWithMessage(assertjErrorMessage, this.actual, name, actualName); } // return the current assertion for method chaining return this; } /** * Verifies that the actual Record's timestamp is equal to the given one. * * @param timestamp the given timestamp to compare the actual Record's timestamp to. * @return this assertion object. * @throws AssertionError - if the actual Record's timestamp is not equal to the given one. */ public RecordAssert hasTimestamp(final long timestamp) { // check that actual Record we want to make assertions on is not null. isNotNull(); // overrides the default error message with a more explicit one final String assertjErrorMessage = "\nExpecting timestamp of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>"; // check final long actualTimestamp = this.actual.getTimestamp(); if (actualTimestamp != timestamp) { failWithMessage(assertjErrorMessage, this.actual, timestamp, actualTimestamp); } // return the current assertion for method chaining return this; } } ================================================ FILE: services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/ThreadSleepClockImplTest.java ================================================ /** * 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.tracking; import static org.junit.Assert.assertTrue; import org.junit.Test; public class ThreadSleepClockImplTest { @Test public void should_sleep_given_time() throws Exception { final ThreadSleepClockImpl clock = new ThreadSleepClockImpl(); final long millis = 10L; final long startTime = System.currentTimeMillis(); clock.sleep(millis); final long endTime = System.currentTimeMillis(); assertTrue(endTime - startTime >= millis); } } ================================================ FILE: services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/TimeTrackerTest.java ================================================ /** * 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.tracking; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.*; import static org.mockito.Mockito.*; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.bonitasoft.engine.tracking.memory.MemoryFlushEventListener; import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class TimeTrackerTest extends AbstractTimeTrackerTest { private TimeTracker tracker; @Mock private FlushThread flushThread; private static final TimeTrackerRecords REC = TimeTrackerRecords.EVALUATE_EXPRESSION; private static final TimeTrackerRecords REC1 = TimeTrackerRecords.EVALUATE_EXPRESSION_INCLUDING_CONTEXT; private static final TimeTrackerRecords REC2 = TimeTrackerRecords.EXECUTE_CONNECTOR_CALLABLE; private static final TimeTrackerRecords REC3 = TimeTrackerRecords.EXECUTE_CONNECTOR_DISCONNECT; private static final TimeTrackerRecords INACTIVATED_REC = TimeTrackerRecords.EXECUTE_CONNECTOR_OUTPUT_OPERATIONS; @After public void tearDown() { // Make sure to clean tracker threads by stopping tracker. if (this.tracker != null) { this.tracker.stop(); } } TimeTracker createTimeTracker(final Clock clock, final List listeners, final boolean enabled, final int maxSize, final int flushIntervalInSeconds, final TimeTrackerRecords rec) { return new TimeTracker(clock, enabled, listeners, maxSize, flushIntervalInSeconds, rec.name()) { @Override FlushThread createFlushThread() { return TimeTrackerTest.this.flushThread; } }; } TimeTracker createTimeTracker(final boolean enabled, final List flushEventListeners, final int maxSize, final int flushIntervalInSeconds, final TimeTrackerRecords... records) { final List recordsAsString = new ArrayList<>(); for (final TimeTrackerRecords record : records) { recordsAsString.add(record.name()); } return new TimeTracker(enabled, flushEventListeners, maxSize, flushIntervalInSeconds, recordsAsString.toArray(new String[records.length])) { @Override FlushThread createFlushThread() { return TimeTrackerTest.this.flushThread; } }; } @Test public void should_isTrackable_returns_false_if_not_started() { this.tracker = createTimeTracker(true, null, 10, 2, REC); assertFalse(this.tracker.isTrackable(REC)); } @Test public void isTrackable() { when(this.flushThread.isStarted()).thenReturn(true); this.tracker = createTimeTracker(true, null, 10, 2, REC1, REC2); this.tracker.start(); assertTrue(this.tracker.isTrackable(REC1)); assertTrue(this.tracker.isTrackable(REC2)); assertFalse(this.tracker.isTrackable(REC3)); this.tracker.stop(); } @Test public void trackRecords() { when(this.flushThread.isStarted()).thenReturn(true); this.tracker = createTimeTracker(true, null, 10, 2, REC1, REC2); this.tracker.start(); this.tracker.track(REC1, "rec11Desc", 100); this.tracker.track(REC1, "rec12Desc", 200); this.tracker.track(REC2, "rec2Desc", 300); this.tracker.track(INACTIVATED_REC, "blabla", 1000); final Map> records = mapRecords(this.tracker.getRecordsCopy()); assertEquals(2, records.size()); assertEquals(2, records.get(REC1).size()); assertEquals(1, records.get(REC2).size()); final List rec1s = records.get(REC1); final Record rec11 = rec1s.get(0); assertEquals(REC1, rec11.getName()); assertEquals("rec11Desc", rec11.getDescription()); assertEquals(100L, rec11.getDuration()); final Record rec12 = rec1s.get(1); assertEquals(REC1, rec12.getName()); assertEquals("rec12Desc", rec12.getDescription()); assertEquals(200L, rec12.getDuration()); final List rec2s = records.get(REC2); final Record rec2 = rec2s.get(0); assertEquals(REC2, rec2.getName()); assertEquals("rec2Desc", rec2.getDescription()); assertEquals(300L, rec2.getDuration()); this.tracker.stop(); } @Test public void should_not_track_when_not_enabled() { this.tracker = createTimeTracker(false, null, 10, 2, REC1, REC2); this.tracker.start(); this.tracker.track(REC1, "rec11Desc", 100); this.tracker.track(REC1, "rec12Desc", 200); this.tracker.track(REC2, "rec2Desc", 300); this.tracker.track(INACTIVATED_REC, "blabla", 1000); assertTrue(this.tracker.getRecordsCopy().isEmpty()); } @Test public void timestamp() throws Exception { when(this.flushThread.isStarted()).thenReturn(true); this.tracker = createTimeTracker(true, null, 10, 2, REC1); this.tracker.start(); this.tracker.track(REC1, "desc2", 100); Thread.sleep(2); this.tracker.track(REC1, "desc2", 200); final Map> records = mapRecords(this.tracker.getRecordsCopy()); assertEquals(1, records.size()); assertEquals(2, records.get(REC1).size()); final List rec1s = records.get(REC1); assertTrue(rec1s.get(0).getTimestamp() < rec1s.get(1).getTimestamp()); this.tracker.stop(); } private Map> mapRecords(final List records) { final Map> result = new HashMap<>(); if (records != null) { for (final Record record : records) { final TimeTrackerRecords name = record.getName(); if (!result.containsKey(name)) { result.put(name, new ArrayList<>()); } result.get(name).add(record); } } return result; } @Test public void should_build_do_not_start_tracking() { this.tracker = createTimeTracker(true, null, 10, 2); assertFalse(this.flushThread.isStarted()); } @Test public void should_start_flush_thread_if_thread_is_stopped() { doReturn(false).when(this.flushThread).isStarted(); this.tracker = createTimeTracker(true, null, 10, 2); this.tracker.start(); verify(this.flushThread).start(); } @Test public void should_stop_flush_thread_is_running() { when(this.flushThread.isStarted()).thenReturn(true); this.tracker = createTimeTracker(true, null, 10, 2); this.tracker.start(); this.tracker.stop(); verify(this.flushThread).interrupt(); } @Test public void should_not_stop_flush_thread_is_running() { this.tracker = createTimeTracker(true, null, 10, 2); this.tracker.stop(); verify(this.flushThread, never()).interrupt(); } @Test public void should_flush_ignore_flush_listeners_exceptions() throws Exception { final Clock clock = mock(Clock.class); final FlushEventListener listener1 = mock(FlushEventListener.class); when(listener1.getName()).thenReturn("listener1"); when(listener1.isActive()).thenReturn(true); final FlushEventListener listener2 = mock(FlushEventListener.class); when(listener2.getName()).thenReturn("listener2"); when(listener2.isActive()).thenReturn(true); final FlushEventListener listener3 = mock(FlushEventListener.class); when(listener3.getName()).thenReturn("listener3"); when(listener3.isActive()).thenReturn(true); Mockito.when(listener2.flush(ArgumentMatchers.any(FlushEvent.class))).thenThrow(new Exception()); final List listeners = new ArrayList<>(); listeners.add(listener1); listeners.add(listener2); listeners.add(listener3); this.tracker = createTimeTracker(clock, listeners, true, 10, 1, REC); this.tracker.track(REC, "desc", 10); this.tracker.flush(); verify(listener1, times(1)).flush(ArgumentMatchers.any(FlushEvent.class)); verify(listener2, times(1)).flush(ArgumentMatchers.any(FlushEvent.class)); verify(listener3, times(1)).flush(ArgumentMatchers.any(FlushEvent.class)); } @Test public void should_flush_call_all_flush_listeners() throws Exception { final Clock clock = mock(Clock.class); final FlushEventListener listener1 = mock(FlushEventListener.class); when(listener1.getName()).thenReturn("listener1"); when(listener1.isActive()).thenReturn(true); final FlushEventListener listener2 = mock(FlushEventListener.class); when(listener2.getName()).thenReturn("listener2"); when(listener2.isActive()).thenReturn(true); final List listeners = new ArrayList<>(); listeners.add(listener1); listeners.add(listener2); this.tracker = createTimeTracker(clock, listeners, true, 10, 1, REC); this.tracker.track(REC, "desc", 10); this.tracker.flush(); verify(listener1, times(1)).flush(ArgumentMatchers.any(FlushEvent.class)); verify(listener2, times(1)).flush(ArgumentMatchers.any(FlushEvent.class)); } @Test public void rollingRecords() { when(this.flushThread.isStarted()).thenReturn(true); this.tracker = createTimeTracker(true, null, 2, 2, REC); this.tracker.start(); this.tracker.track(REC, "rec1", 100); assertEquals(1, this.tracker.getRecordsCopy().size()); this.tracker.track(REC, "rec2", 100); assertEquals(2, this.tracker.getRecordsCopy().size()); this.tracker.track(REC, "rec3", 100); assertEquals(2, this.tracker.getRecordsCopy().size()); assertEquals("rec2", this.tracker.getRecordsCopy().get(0).getDescription()); assertEquals("rec3", this.tracker.getRecordsCopy().get(1).getDescription()); this.tracker.stop(); } @Test public void testGetActiveListeners() { final FlushEventListener listener1 = mock(FlushEventListener.class); when(listener1.getName()).thenReturn("listener1"); when(listener1.isActive()).thenReturn(true); final FlushEventListener listener2 = mock(FlushEventListener.class); when(listener2.getName()).thenReturn("listener2"); when(listener2.isActive()).thenReturn(false); final List flushEventListeners = new ArrayList<>(); flushEventListeners.add(listener1); flushEventListeners.add(listener2); this.tracker = createTimeTracker(true, flushEventListeners, 2, 2, REC); assertEquals(1, this.tracker.getActiveFlushEventListeners().size()); } @Test public void testActivateListeners() { final FlushEventListener listener1 = mock(FlushEventListener.class); when(listener1.getName()).thenReturn("listener1"); final FlushEventListener listener2 = mock(FlushEventListener.class); when(listener2.getName()).thenReturn("listener2"); final List flushEventListeners = new ArrayList<>(); flushEventListeners.add(listener1); flushEventListeners.add(listener2); this.tracker = createTimeTracker(true, flushEventListeners, 2, 2, REC); this.tracker.activateFlushEventListener("listener2"); verify(listener2, times(1)).activate(); } @Test public void testDeactivateListeners() { final FlushEventListener listener1 = mock(FlushEventListener.class); when(listener1.getName()).thenReturn("listener1"); final List flushEventListeners = new ArrayList<>(); flushEventListeners.add(listener1); this.tracker = createTimeTracker(true, flushEventListeners, 2, 2, REC); this.tracker.deactivateFlushEventListener("listener1"); verify(listener1, times(1)).deactivate(); } @Test public void testActivatedRecords() { this.tracker = createTimeTracker(true, null, 2, 2, REC); assertEquals(1, this.tracker.getActivatedRecords().size()); this.tracker.activateRecord(REC2); assertEquals(2, this.tracker.getActivatedRecords().size()); //no duplicate this.tracker.activateRecord(REC2); assertEquals(2, this.tracker.getActivatedRecords().size()); this.tracker.deactivatedRecord(REC); assertEquals(1, this.tracker.getActivatedRecords().size()); } @Test public void testFlushInterval() { this.tracker = createTimeTracker(true, null, 2, 1, REC); assertEquals(1000, this.tracker.getFlushIntervalInMS()); this.tracker.setFlushIntervalInMS(111); assertEquals(111, this.tracker.getFlushIntervalInMS()); this.tracker.setFlushIntervalInSeconds(10); assertEquals(10000, this.tracker.getFlushIntervalInMS()); } @Test public void should_stop_tracking_interrupt_flush_thread_and_listeners() { final FlushEventListener listener1 = mock(FlushEventListener.class); when(listener1.getName()).thenReturn("listener1"); when(listener1.isActive()).thenReturn(true); final FlushEventListener listener2 = mock(FlushEventListener.class); when(listener2.getName()).thenReturn("listener2"); when(listener2.isActive()).thenReturn(true); final List flushEventListeners = new ArrayList<>(); flushEventListeners.add(listener1); flushEventListeners.add(listener2); this.tracker = createTimeTracker(true, flushEventListeners, 2, 2, REC); this.tracker.start(); when(this.flushThread.isStarted()).thenReturn(true); this.tracker.stopTracking(); verify(this.flushThread, times(1)).interrupt(); verify(listener1, times(1)).notifyStopTracking(); verify(listener2, times(1)).notifyStopTracking(); } @Test public void should_start_tracking_start_flush_thread_and_listeners() { final FlushEventListener listener1 = mock(FlushEventListener.class); when(listener1.getName()).thenReturn("listener1"); when(listener1.isActive()).thenReturn(true); final FlushEventListener listener2 = mock(FlushEventListener.class); when(listener2.getName()).thenReturn("listener2"); when(listener2.isActive()).thenReturn(true); final List flushEventListeners = new ArrayList<>(); flushEventListeners.add(listener1); flushEventListeners.add(listener2); this.tracker = createTimeTracker(true, flushEventListeners, 2, 2, REC); when(this.flushThread.isStarted()).thenReturn(false); this.tracker.start(); this.tracker.startTracking(); verify(this.flushThread, times(2)).start(); verify(listener1, times(2)).notifyStartTracking(); verify(listener2, times(2)).notifyStartTracking(); } @Test public void should_pause_resume_without_error() { //given this.tracker = new TimeTracker(true, new LinkedList<>(), 500, 1, (String[]) null); //when this.tracker.start(); int i = 0; try { // Error may not occur on the first execution. I did reproduce it in 4 to 8 executions. // To make myself confident, I execute the test 100 times. // Current implementation is deterministic, but previous one was not. Hence the loop in the test. while (i < 100) { i++; this.tracker.pause(); this.tracker.resume(); //No errors expected } } finally { System.err.println("Test ended after iteration: " + i); this.tracker.stop(); } } @Test public void should_start_method_be_reentrant() { //given this.tracker = new TimeTracker(true, new LinkedList<>(), 500, 1, (String[]) null); final Set initialThreadSet = Thread.getAllStackTraces().keySet(); this.tracker.start(); //expect final Set runningTimeTrackerThreadSet = Thread.getAllStackTraces().keySet(); assertThat(initialThreadSet.size() + 1).isEqualTo(runningTimeTrackerThreadSet.size()); //when this.tracker.start(); //then final Set stillRunningTimeTrackerThreadSet = Thread.getAllStackTraces().keySet(); assertThat(runningTimeTrackerThreadSet).isEqualTo(stillRunningTimeTrackerThreadSet); } @Test public void isTracking_should_be_false_if_tracking_not_started() { //given this.tracker = new TimeTracker(true, new LinkedList<>(), 500, 1, (String[]) null); //when final boolean trackingStatus = this.tracker.isTracking(); //then assertThat(trackingStatus).as("Tracking status must be FALSE if tracker has not yet been started.").isFalse(); } @Test public void isTracking_should_be_true_if_tracking_is_started() { //given this.tracker = new TimeTracker(true, new LinkedList<>(), 500, 1, (String[]) null); try { this.tracker.start(); //when final boolean trackingStatus = this.tracker.isTracking(); //then assertThat(trackingStatus).as("Tracking status must be TRUE after start method has been called.").isTrue(); } finally { this.tracker.stop(); } } @Test public void should_not_leave_unused_threads_when_stopped() { //given this.tracker = new TimeTracker(true, new LinkedList<>(), 500, 1, (String[]) null); final Set beforeTimeTrackerStartedThreadSet = Thread.getAllStackTraces().keySet(); this.tracker.start(); //when this.tracker.stop(); //then final Set afterTimeTrackerStoppedThreadSet = Thread.getAllStackTraces().keySet(); afterTimeTrackerStoppedThreadSet.removeAll(beforeTimeTrackerStartedThreadSet); //There should be no more threads than the ones existing prior to tracker startup assertThat(afterTimeTrackerStoppedThreadSet).isEqualTo(Collections.emptySet()); } @Test public void should_add_remove_listeners() { // create a tracker this.tracker = new TimeTracker(true, new LinkedList<>(), 500, 1); // no active listener assertThat(this.tracker.getActiveFlushEventListeners()).isEmpty(); // add a new listener final FlushEventListener flushEvent = new MemoryFlushEventListener(true, 10); this.tracker.addFlushEventListener(flushEvent); assertThat(this.tracker.getActiveFlushEventListeners()).containsOnly(flushEvent); // remove the same listener this.tracker.removeFlushEventListener(flushEvent.getName()); assertThat(this.tracker.getActiveFlushEventListeners()).isEmpty(); } @Test public void should_have_one_listener_based_on_listener_name() { // create a tracker this.tracker = new TimeTracker(true, new LinkedList<>(), 500, 1); // add a new listener final FlushEventListener flushEvent1 = new MemoryFlushEventListener(true, 10); final FlushEventListener flushEvent2 = new MemoryFlushEventListener(true, 10); this.tracker.addFlushEventListener(flushEvent1); this.tracker.addFlushEventListener(flushEvent2); // must have only one time assertThat(this.tracker.getActiveFlushEventListeners()).containsOnlyOnce(flushEvent2); } } ================================================ FILE: services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/csv/CSVFlushEventListenerTest.java ================================================ /** * 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.tracking.csv; import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import java.util.Arrays; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.List; import org.bonitasoft.engine.tracking.AbstractTimeTrackerTest; import org.bonitasoft.engine.tracking.FlushEvent; import org.bonitasoft.engine.tracking.Record; import org.bonitasoft.engine.tracking.RecordAssert; import org.bonitasoft.engine.tracking.TimeTrackerRecords; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class CSVFlushEventListenerTest extends AbstractTimeTrackerTest { private static final TimeTrackerRecords TIME_TRACKER_RECORDS = TimeTrackerRecords.EVALUATE_EXPRESSION; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void should_work_if_output_folder_is_a_folder() throws Exception { new CSVFlushEventListener(true, this.temporaryFolder.newFolder().getAbsolutePath(), ";"); } @Test public void should_fail_if_output_folder_unknown() { //then this.expectedException.expect(RuntimeException.class); this.expectedException.expectMessage("Output folder does not exist"); //when new CSVFlushEventListener(true, "unknownFolder", ";"); } @Test public void should_fail_if_outputfolder_is_a_file() throws Exception { //then this.expectedException.expect(RuntimeException.class); this.expectedException.expectMessage("Output folder is not a directory"); //when new CSVFlushEventListener(true, this.temporaryFolder.newFile().getAbsolutePath(), ";"); } @Test public void flushedCsv() throws Exception { //given final CSVFlushEventListener csvFlushEventListener = new CSVFlushEventListener(true, this.temporaryFolder.newFolder().getAbsolutePath(), ";"); final Record rec1 = new Record(System.currentTimeMillis(), TIME_TRACKER_RECORDS, "rec1Desc", 100); final Record rec2 = new Record(System.currentTimeMillis(), TIME_TRACKER_RECORDS, "rec2Desc", 200); //when final CSVFlushEventListenerResult csvFlushResult = csvFlushEventListener .flush(new FlushEvent(System.currentTimeMillis(), Arrays.asList(rec1, rec2))); final File csvFile = csvFlushResult.getOutputFile(); //then final List> csvValues = CSVUtil.readCSV(true, csvFile, ";"); assertThat(csvValues).as("should contains 2 records").hasSize(2); checkCSVRecord(rec1, csvValues.get(0)); checkCSVRecord(rec2, csvValues.get(1)); final List records = csvFlushResult.getFlushEvent().getRecords(); assertThat(records).as("should contains 2 records").hasSize(2); checkRecord(rec1, records.get(0)); checkRecord(rec2, records.get(1)); } private void checkCSVRecord(final Record record, final List csvValues) { // timestamp, year, month, day, hour, minute, second, millisecond, duration, name, description] assertThat(csvValues).hasSize(11); final long timestamp = record.getTimestamp(); final GregorianCalendar cal = new GregorianCalendar(); cal.setTimeInMillis(timestamp); final int year = cal.get(Calendar.YEAR); final int month = cal.get(Calendar.MONTH) + 1; final int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH); final int hourOfDay = cal.get(Calendar.HOUR_OF_DAY); final int minute = cal.get(Calendar.MINUTE); final int second = cal.get(Calendar.SECOND); final int millisecond = cal.get(Calendar.MILLISECOND); RecordAssert.assertThat(record) .hasTimestamp(Long.valueOf(csvValues.get(0)).longValue()) .hasDuration(Long.valueOf(csvValues.get(8)).longValue()) .hasDescription(csvValues.get(10)) .hasName(TimeTrackerRecords.EVALUATE_EXPRESSION); assertThat(timestamp).isEqualTo(Long.valueOf(csvValues.get(0)).longValue()); assertThat(year).isEqualTo(Integer.valueOf(csvValues.get(1)).intValue()); assertThat(month).isEqualTo(Integer.valueOf(csvValues.get(2)).intValue()); assertThat(dayOfMonth).isEqualTo(Integer.valueOf(csvValues.get(3)).intValue()); assertThat(hourOfDay).isEqualTo(Integer.valueOf(csvValues.get(4)).intValue()); assertThat(minute).isEqualTo(Integer.valueOf(csvValues.get(5)).intValue()); assertThat(second).isEqualTo(Integer.valueOf(csvValues.get(6)).intValue()); assertThat(millisecond).isEqualTo(Integer.valueOf(csvValues.get(7)).intValue()); } } ================================================ FILE: services/bonita-time-tracker/src/test/java/org/bonitasoft/engine/tracking/memory/MemoryFlushEventListenerTest.java ================================================ /** * 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.tracking.memory; import static org.junit.Assert.assertEquals; import java.util.Arrays; import org.bonitasoft.engine.tracking.FlushEvent; import org.bonitasoft.engine.tracking.Record; import org.bonitasoft.engine.tracking.TimeTrackerRecords; import org.junit.Test; /** * @author Charles Souillard */ public class MemoryFlushEventListenerTest { private static final TimeTrackerRecords REC = TimeTrackerRecords.EVALUATE_EXPRESSION; @Test public void should_day_record_keep_all_records_of_subsequent_flush() throws Exception { final MemoryFlushEventListener listener = new MemoryFlushEventListener(true, 10); final Record rec1 = new Record(System.currentTimeMillis(), REC, "rec1Desc", 100); listener.flush(new FlushEvent(System.currentTimeMillis(), Arrays.asList(rec1))); assertEquals(1, listener.getDayRecord().getRecordsCopy().size()); final Record rec2 = new Record(System.currentTimeMillis(), REC, "rec2Desc", 200); listener.flush(new FlushEvent(System.currentTimeMillis(), Arrays.asList(rec2))); assertEquals(2, listener.getDayRecord().getRecordsCopy().size()); } @Test public void should_day_record_never_exceed_maxSize() throws Exception { final MemoryFlushEventListener listener = new MemoryFlushEventListener(true, 1); final Record rec1 = new Record(System.currentTimeMillis(), REC, "rec1Desc", 100); listener.flush(new FlushEvent(System.currentTimeMillis(), Arrays.asList(rec1))); assertEquals(1, listener.getDayRecord().getRecordsCopy().size()); final Record rec2 = new Record(System.currentTimeMillis(), REC, "rec2Desc", 200); listener.flush(new FlushEvent(System.currentTimeMillis(), Arrays.asList(rec2))); assertEquals(1, listener.getDayRecord().getRecordsCopy().size()); } } ================================================ FILE: services/bonita-transaction/build.gradle ================================================ dependencies { api project(':services:bonita-commons') api libs.jakartaTransactionApi annotationProcessor libs.lombok compileOnly libs.lombok testImplementation libs.mockitoCore testImplementation libs.assertj testImplementation libs.narayanaJta testImplementation libs.jbossLogging testRuntimeOnly libs.logback } ================================================ FILE: services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/BonitaTransactionSynchronization.java ================================================ /** * 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.transaction; import javax.transaction.Synchronization; @FunctionalInterface public interface BonitaTransactionSynchronization extends Synchronization { @Override default void beforeCompletion() { } @Override void afterCompletion(int i); } ================================================ FILE: services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/JTATransactionServiceImpl.java ================================================ /** * 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.transaction; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicLong; import javax.transaction.*; import lombok.extern.slf4j.Slf4j; import org.bonitasoft.engine.commons.ExceptionUtils; import org.bonitasoft.engine.commons.exceptions.SBonitaRuntimeException; import org.bonitasoft.engine.mdc.MDCConstants; import org.slf4j.MDC; @Slf4j public class JTATransactionServiceImpl implements TransactionService { private final TransactionManager txManager; private final AtomicLong numberOfActiveTransactions = new AtomicLong(0); private final ThreadLocal txContextThreadLocal; private XAResourceRetriever xaResourceRetriever; public JTATransactionServiceImpl(final TransactionManager txManager) { this.txManager = txManager; txContextThreadLocal = new TransactionServiceContextThreadLocal(); xaResourceRetriever = new XAResourceRetriever(); } protected JTATransactionServiceImpl(final TransactionManager txManager, XAResourceRetriever xaResourceRetriever) { this.xaResourceRetriever = xaResourceRetriever; this.txManager = txManager; txContextThreadLocal = new TransactionServiceContextThreadLocal(); } @Override public void begin() throws STransactionCreationException { final TransactionServiceContext txContext = getTransactionServiceContext(); try { int status = txManager.getStatus(); checkForNestedBonitaTransaction(txContext, status); clearPreviousTransaction(status); initTxContext(txContext, status); if (txContext.externallyManaged) { //do not open transaction because it was open externally return; } if (log.isTraceEnabled()) { log.trace( "Beginning transaction in thread " + Thread.currentThread().getId() + " " + txManager.getTransaction()); txContext.stackTraceThatMadeLastBegin = generateCurrentStack(); } txManager.begin(); Optional transactionId = getTransactionId(txManager.getTransaction()); // we don't use AbstractMDC here, as we have the guarantee that complete() will close the transaction. transactionId.ifPresent(id -> MDC.put(MDCConstants.TRANSACTION_ID, id)); handleNumberOfActiveTransactions(); } catch (final STransactionCreationException e) { resetTxContext(txContext); throw e; } catch (final Throwable e) { resetTxContext(txContext); throw new STransactionCreationException(e); } } private Optional getTransactionId(Transaction transaction) { /* * Transaction is typically an instance of com.arjuna.ats.jta.transaction.Transaction with get_uid method. * Be lenient on implementation class as long as we have the get_uid method... */ try { var method = transaction.getClass().getMethod("get_uid"); return Optional.of(method.invoke(transaction).toString()); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { log.warn( "Transaction {} is an instance of {} which does not have an accessible 'get_uid' method like com.arjuna.ats.jta.transaction.Transaction", transaction, transaction.getClass().getName()); return Optional.empty(); } } private void clearPreviousTransaction(int status) throws STransactionCreationException { if (status != Status.STATUS_ACTIVE && status != Status.STATUS_NO_TRANSACTION) { //the transaction is in an inconsistent state, we try to rollback log.warn( "Starting a new transaction on the thread but there is already a transaction is state " + status + ". Will try to call rollback on the transaction manager to cleanup the thread from this transaction."); try { txManager.rollback(); } catch (SystemException e) { throw new STransactionCreationException( "A transaction was already associated with the thread. We tried to " + " rollback it but without success. If the transaction manager does not disassociate the thread with the transaction," + " restart the server. Status of the transaction was " + status, e); } } } private void handleNumberOfActiveTransactions() throws RollbackException, SystemException { numberOfActiveTransactions.getAndIncrement(); txManager.getTransaction() .registerSynchronization(new DecrementNumberOfActiveTransactionsSynchronization(this)); } private void initTxContext(TransactionServiceContext txContext, int status) { txContext.externallyManaged = (status == Status.STATUS_ACTIVE); txContext.isInScopeOfBonitaTransaction = true; txContext.beforeCommitCallables.clear(); } private void checkForNestedBonitaTransaction(TransactionServiceContext txContext, int status) throws STransactionCreationException { if (txContext.isInScopeOfBonitaTransaction) { String message = "We do not support nested calls to the transaction service. Current state is: " + status + ". "; if (log.isTraceEnabled()) { message += "Last begin made by: " + txContext.stackTraceThatMadeLastBegin; } throw new STransactionCreationException(message); } } private String generateCurrentStack() { StringBuilder sb = new StringBuilder(); sb.append(this.getClass().getName()).append("\nNew transaction started by: "); final StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); for (int i = 3; i < stackTraceElements.length; i++) { sb.append("\n at "); sb.append(stackTraceElements[i]); } return sb.toString(); } @Override public void complete() throws STransactionCommitException, STransactionRollbackException { // Depending of the txManager status we either commit or rollback. final TransactionServiceContext txContext = getTransactionServiceContext(); // Capture TX identity early String txId = null; try { txId = String.valueOf(txManager.getTransaction()); log.trace("Completing transaction in thread {} (txId={})", Thread.currentThread().getId(), txId); final int status = txManager.getStatus(); if (status == Status.STATUS_NO_TRANSACTION) { throw new STransactionCommitException("No transaction started."); } if (txContext.externallyManaged) { return; // We do not manage the transaction boundaries } if (status == Status.STATUS_MARKED_ROLLBACK) { log.trace("Rolling back transaction in thread {} (txId={})", Thread.currentThread().getId(), txId); txManager.rollback(); } else { try { executeBeforeCommitCallables(txContext); } finally { //even if there is an issue in before commit callables execution, we always call the commit to ensure end of the tx txManager.commit(); } } } catch (final SystemException | HeuristicMixedException | HeuristicRollbackException | RollbackException e) { log.warn("Transaction commit failed in thread {} (txId={}). " + "The 2-phase commit may have completed but a post-commit synchronization threw an exception. " + "Root cause: {}", Thread.currentThread().getId(), txId, ExceptionUtils.printRootCauseOnly(e)); log.debug("Full exception for transaction commit failure:", e); throw new STransactionCommitException(e); } finally { MDC.remove(MDCConstants.TRANSACTION_ID); resetTxContext(txContext); } } TransactionServiceContext getTransactionServiceContext() { return txContextThreadLocal.get(); } private void executeBeforeCommitCallables(TransactionServiceContext txContext) throws STransactionCommitException { final List> callables = txContext.beforeCommitCallables; for (final Callable callable : callables) { try { callable.call(); } catch (Exception e) { throw new STransactionCommitException("Exception while executing callable in beforeCommit phase", e); } } } void resetTxContext(TransactionServiceContext txContext) { txContext.isInScopeOfBonitaTransaction = false; txContext.externallyManaged = false; txContext.beforeCommitCallables.clear(); txContext.stackTraceThatMadeLastBegin = null; } @Override public boolean isTransactionActive() { try { return txManager.getStatus() == Status.STATUS_ACTIVE; } catch (final Throwable e) { log.warn( "Unable to check if there is an active transaction {}", e.getMessage()); return false; } } @Override public void setRollbackOnly() throws STransactionException { try { txManager.setRollbackOnly(); } catch (final IllegalStateException | SystemException e) { throw new STransactionException(e); } } @Override public boolean isRollbackOnly() throws STransactionException { try { return txManager.getStatus() == Status.STATUS_MARKED_ROLLBACK; } catch (final SystemException e) { throw new STransactionException("Error while trying to get the transaction's status.", e); } } @Override public void registerBonitaSynchronization(final Synchronization txSync) throws STransactionNotFoundException { try { final Transaction transaction = txManager.getTransaction(); if (transaction == null) { throw new STransactionNotFoundException("No active transaction."); } transaction.registerSynchronization(txSync); } catch (final IllegalStateException | SystemException | RollbackException e) { throw new STransactionNotFoundException(e); } } @Override public void registerBeforeCommitCallable(final Callable callable) throws STransactionNotFoundException { try { final Transaction transaction = txManager.getTransaction(); if (transaction == null) { throw new STransactionNotFoundException("No active transaction"); } getTransactionServiceContext().beforeCommitCallables.add(callable); } catch (final IllegalStateException | SystemException e) { throw new STransactionNotFoundException(e.getMessage()); } } @Override public T executeInTransaction(final Callable callable) throws Exception { begin(); try { return callable.call(); } catch (final Exception e) { log(callable, e); setRollbackOnly(); throw e; } catch (final Throwable t) { log(callable, t); setRollbackOnly(); throw new SBonitaRuntimeException(t); } finally { complete(); } } private void log(Callable callable, Throwable e) { if (log.isDebugEnabled()) { log.debug( "Setting rollbackOnly on current transaction because callable '{}' has thrown an exception: {} " + "cause {}", callable, e.getMessage(), ExceptionUtils.printLightWeightStacktrace(e)); } } @Override public long getNumberOfActiveTransactions() { return numberOfActiveTransactions.get(); } private static class DecrementNumberOfActiveTransactionsSynchronization implements Synchronization { private final JTATransactionServiceImpl txService; public DecrementNumberOfActiveTransactionsSynchronization(final JTATransactionServiceImpl txService) { this.txService = txService; } @Override public void beforeCompletion() { // Nothing to do } @Override public void afterCompletion(final int status) { // Whatever the status, decrement the number of active transactions txService.numberOfActiveTransactions.getAndDecrement(); } } static class TransactionServiceContext { /* * this flag means that we already called begin on the bonita transaction service whether or not the transaction * is managed externally */ boolean isInScopeOfBonitaTransaction = false; /* * true when the transaction was open outside of bonita */ boolean externallyManaged = false; /** * We maintain a list of Callables that must be executed just before the real commit (and before the * beforeCompletion method is called), so that we * ensure * that Hibernate has not already flushed its session. */ List> beforeCommitCallables = new ArrayList<>(); /** * for tracing only */ String stackTraceThatMadeLastBegin; } private static class TransactionServiceContextThreadLocal extends ThreadLocal { @Override protected TransactionServiceContext initialValue() { return new TransactionServiceContext(); } } @Override public Optional hasMultipleResources() { try { return xaResourceRetriever.retrieveResources(txManager.getTransaction()).map(List::size) .map(size -> size > 1); } catch (SystemException e) { throw new RuntimeException(e); } } } ================================================ FILE: services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/SBadTransactionStateException.java ================================================ /** * 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.transaction; import org.bonitasoft.engine.commons.exceptions.SBonitaException; public class SBadTransactionStateException extends SBonitaException { private static final long serialVersionUID = 1L; public SBadTransactionStateException(final String message) { super(message); } } ================================================ FILE: services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/STransactionCommitException.java ================================================ /** * 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.transaction; import java.util.List; public class STransactionCommitException extends STransactionException { private static final long serialVersionUID = 8650941405945187337L; public STransactionCommitException(final String message, final List commitExceptions) { super(message, commitExceptions); } public STransactionCommitException(final String message, final Throwable throwable) { super(message, throwable); } public STransactionCommitException(final Throwable cause) { super(cause); } public STransactionCommitException(String message) { super(message); } } ================================================ FILE: services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/STransactionCreationException.java ================================================ /** * 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.transaction; public class STransactionCreationException extends STransactionException { private static final long serialVersionUID = 1L; public STransactionCreationException(final Throwable t) { super(t); } public STransactionCreationException(final String message) { super(message); } public STransactionCreationException(String message, Exception exception) { super(message, exception); } } ================================================ FILE: services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/STransactionException.java ================================================ /** * 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.transaction; import java.io.PrintStream; import java.io.PrintWriter; import java.util.Collections; import java.util.List; import org.bonitasoft.engine.commons.exceptions.SBonitaException; public class STransactionException extends SBonitaException { private static final long serialVersionUID = 8650941405945187337L; private final List exceptions; public STransactionException(final String message, final List commitExceptions) { super(message); exceptions = Collections.unmodifiableList(commitExceptions); } public STransactionException(final String message, final Throwable throwable) { super(message, throwable); exceptions = Collections. emptyList(); } public STransactionException(final Throwable throwable) { super(throwable); exceptions = Collections. emptyList(); } public STransactionException(final String message) { super(message); exceptions = Collections. emptyList(); } public List getCommitExceptions() { return exceptions; } @Override public void printStackTrace() { super.printStackTrace(); for (final Throwable throwable : exceptions) { throwable.printStackTrace(); } } @Override public void printStackTrace(final PrintStream s) { super.printStackTrace(s); for (final Throwable throwable : exceptions) { throwable.printStackTrace(s); } } @Override public void printStackTrace(final PrintWriter s) { super.printStackTrace(s); for (final Throwable throwable : exceptions) { throwable.printStackTrace(s); } } /* * (non-Javadoc) * @see java.lang.Throwable#getCause() */ @Override public Throwable getCause() { Throwable cause = super.getCause(); if (cause == null && !exceptions.isEmpty()) { cause = exceptions.get(0); } return cause; } } ================================================ FILE: services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/STransactionNotFoundException.java ================================================ /** * 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.transaction; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Charles Souillard * @author Celine Souchet */ public class STransactionNotFoundException extends SBonitaException { private static final long serialVersionUID = -8750663884514717732L; private static final String DEFAULT_MESSAGE = "No transaction found."; public STransactionNotFoundException() { super(DEFAULT_MESSAGE); } public STransactionNotFoundException(final String message) { super(message); } public STransactionNotFoundException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/STransactionResourceException.java ================================================ /** * 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.transaction; import org.bonitasoft.engine.commons.exceptions.SBonitaException; public class STransactionResourceException extends SBonitaException { private static final long serialVersionUID = 2652979339415320810L; public STransactionResourceException(final String message) { super(message); } } ================================================ FILE: services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/STransactionRollbackException.java ================================================ /** * 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.transaction; import java.util.List; public class STransactionRollbackException extends STransactionException { private static final long serialVersionUID = 1L; public STransactionRollbackException(final String message, final List commitExceptions) { super(message, commitExceptions); } public STransactionRollbackException(final String message, final Throwable throwable) { super(message, throwable); } public STransactionRollbackException(final Throwable cause) { super(cause); } } ================================================ FILE: services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/TransactionService.java ================================================ /** * 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.transaction; /** * @author Matthieu Chaffotte * @author Laurent Vaills */ public interface TransactionService extends UserTransactionService { /** * Create a new transaction and associate it with the current thread. * * @throws STransactionCreationException */ void begin() throws STransactionCreationException; /** * Complete the transaction : either commit or rollback. * * @throws STransactionCommitException * @throws STransactionRollbackException */ void complete() throws STransactionCommitException, STransactionRollbackException; /** * Modify the transaction associated with the current thread such that * the only possible outcome of the transaction is to roll back the * transaction. * * @throws IllegalStateException Thrown if the current thread is * not associated with a transaction. * @throws STransactionException Thrown if the transaction manager * encounters an unexpected error condition. */ void setRollbackOnly() throws STransactionException; boolean isRollbackOnly() throws STransactionException; /** * Get the number of active transactions (i.e. transactions that opened but not yet completed or rolledback). * A transaction that was just mark as "rollbackOnly" is considered as an active one. * * @return the number of active transactions */ long getNumberOfActiveTransactions(); } ================================================ FILE: services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/UserTransactionService.java ================================================ /** * 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.transaction; import java.util.Optional; import java.util.concurrent.Callable; import javax.transaction.Synchronization; public interface UserTransactionService { /** * Execute the given callable inside a transaction. * * @param callable * @return * @throws Exception */ T executeInTransaction(Callable callable) throws Exception; /** * Register a synchronization object for the transaction currently * associated with the target object. The transaction manager invokes * the beforeCompletion method prior to starting the two-phase transaction * commit process. After the transaction is completed, the transaction * manager invokes the afterCompletion method. * * @param txSync * The Synchronization object for the transaction associated * with the target object. * @exception RollbackException * Thrown to indicate that * the transaction has been marked for rollback only. * @exception IllegalStateException * Thrown if the transaction in the * target object is in the prepared state or the transaction is * inactive. * @exception STransactionNotFoundException * Thrown if the transaction manager * encounters an unexpected error condition. */ void registerBonitaSynchronization(Synchronization txSync) throws STransactionNotFoundException; void registerBeforeCommitCallable(Callable callable) throws STransactionNotFoundException; boolean isTransactionActive(); Optional hasMultipleResources(); } ================================================ FILE: services/bonita-transaction/src/main/java/org/bonitasoft/engine/transaction/XAResourceRetriever.java ================================================ /** * Copyright (C) 2021 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.transaction; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import javax.transaction.Transaction; import javax.transaction.xa.XAResource; import lombok.extern.slf4j.Slf4j; @Slf4j public class XAResourceRetriever { private final Map> cache = new HashMap<>(); public Optional> retrieveResources(Transaction transaction) { return cache.computeIfAbsent(transaction.getClass().getName(), key -> getMethodByReflection(transaction)) .map(method -> getResources(transaction, method)) .filter(s -> !s.isEmpty()) .map(ArrayList::new); } private Optional getMethodByReflection(Transaction transaction) { try { return Optional.of(transaction.getClass().getMethod("getResources")); } catch (Exception e) { log.warn("Unable to find method to retrieve resources attached to the current transaction, " + "we will not try to reattempt to find the method until next restart of the server", e); return Optional.empty(); } } protected Set getResources(Transaction transaction, Method getResources) { try { return ((Map) getResources.invoke(transaction)).keySet(); } catch (Exception e) { log.warn("Unable to retrieve resources attached to the current transaction, " + "we will not try to reattempt to retrieve those resources until next restart of the server", e); cache.put(transaction.getClass().getName(), Optional.empty()); return Collections.emptySet(); } } } ================================================ FILE: services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/JTATransactionServiceImplTest.java ================================================ /** * 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.transaction; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.Collections; import java.util.Optional; import java.util.concurrent.Callable; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import javax.transaction.xa.XAResource; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class JTATransactionServiceImplTest { @Mock TransactionManager txManager; @Mock Transaction transaction; @Mock XAResourceRetriever xaResourceRetriever; @InjectMocks private JTATransactionServiceImpl txService; @Mock private Callable txContent; @Mock private Callable beforeCommitCallable; @Before public void before() throws Exception { doReturn(transaction).when(txManager).getTransaction(); } @Test public void beginTransaction() throws Exception { when(txManager.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION).thenReturn(Status.STATUS_ACTIVE); when(txManager.getTransaction()).thenReturn(mock(Transaction.class)); txService.begin(); verify(txManager, times(1)).begin(); assertEquals(1, txService.getNumberOfActiveTransactions()); } @Test(expected = STransactionCreationException.class) public void should_throw_exception_if_call_is_nested() throws Exception { when(txManager.getStatus()).thenReturn(Status.STATUS_ACTIVE); JTATransactionServiceImpl txService = new JTATransactionServiceImpl(txManager); txService.begin(); txService.begin(); } @Test public void should_fail_if_begin_fails() throws Exception { when(txManager.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION); doThrow(new SystemException("Mocked")).when(txManager).begin(); try { txService.begin(); fail("Thanks to the mock an exception must have been thrown"); } catch (STransactionCreationException e) { verify(txManager, times(1)).begin(); assertEquals(0, txService.getNumberOfActiveTransactions()); } } @Test public void should_increment_tx_counter_when_begin() throws Exception { doReturn(Status.STATUS_NO_TRANSACTION).when(txManager).getStatus(); txService.begin(); assertThat(txService.getNumberOfActiveTransactions()).isEqualTo(1); } @Test public void setRollbackOnly_call_setRollbackOnly_on_txManager() throws Exception { txService.setRollbackOnly(); verify(txManager).setRollbackOnly(); } @Test public void should_executeInTransaction_with_fail_on_execute_do_not_make_other_tx_to_fail() throws Exception { when(txManager.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION).thenReturn(Status.STATUS_ACTIVE); when(txManager.getTransaction()).thenReturn(mock(Transaction.class)); JTATransactionServiceImpl txService = spy(new JTATransactionServiceImpl(txManager)); Callable callable = mock(Callable.class); txService.executeInTransaction(callable); verify(txManager).begin(); verify(callable).call(); verify(txManager).commit(); } /** * The method call has to be executed between a transaction. * * @throws Exception */ @Test public void testExecuteInTransactionWithRollback() throws Exception { // First to allow to start the transaction, then to force to call rollback when(txManager.getStatus()).thenReturn(Status.STATUS_NO_TRANSACTION).thenReturn(Status.STATUS_MARKED_ROLLBACK); when(txManager.getTransaction()).thenReturn(mock(Transaction.class)); Callable callable = mock(Callable.class); when(callable.call()).thenThrow(new Exception("Mocked exception")); try { txService.executeInTransaction(callable); fail("An exception should have been thrown."); } catch (Exception e) { } verify(txManager).begin(); verify(callable).call(); verify(txManager).setRollbackOnly(); verify(txManager).rollback(); } @Test public void should_not_register_synchronization_to_decrement_numberOfActiveTransaction_when_tx_managed_externally() throws Exception { doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus(); txService.begin(); //we do not register synchro on number of transaction when managed externally because we cannot decrement correctly the counter at the end of the bonita tx verify(transaction, never()).registerSynchronization(any(Synchronization.class)); } @Test public void should_not_increment_tx_counter_when_managed_externally() throws Exception { doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus(); txService.begin(); //we do not register synchro on number of transaction when managed externally because we cannot decrement correctly the counter at the end of the bonita tx assertThat(txService.getNumberOfActiveTransactions()).isEqualTo(0); } @Test public void should_register_synchronization_to_decrement_numberOfActiveTransaction() throws Exception { doReturn(Status.STATUS_NO_TRANSACTION).when(txManager).getStatus(); txService.begin(); verify(transaction).registerSynchronization(any(Synchronization.class)); } @Test public void should_call_beforeCommitCallable_on_complete() throws Exception { doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus(); txService.registerBeforeCommitCallable(beforeCommitCallable); txService.complete(); verify(beforeCommitCallable).call(); } @Test public void should_not_call_beforeCommitCallable_when_transaction_is_rollbacked() throws Exception { doReturn(Status.STATUS_MARKED_ROLLBACK).when(txManager).getStatus(); txService.registerBeforeCommitCallable(beforeCommitCallable); txService.complete(); verify(beforeCommitCallable, never()).call(); } @Test public void should_reset_transaction_context_when_error_on_commit() throws Exception { doReturn(Status.STATUS_NO_TRANSACTION).doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus(); doThrow(SystemException.class).when(txManager).commit(); try { txService.begin(); txService.complete(); fail(); } catch (STransactionCommitException ignored) { } assertThat(txService.getTransactionServiceContext().isInScopeOfBonitaTransaction).isFalse(); } @Test public void should_reset_transaction_context_when_error_on_begin() throws Exception { doReturn(Status.STATUS_NO_TRANSACTION).doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus(); doThrow(SystemException.class).when(txManager).begin(); try { txService.begin(); fail(); } catch (STransactionCreationException ignored) { } assertThat(txService.getTransactionServiceContext().isInScopeOfBonitaTransaction).isFalse(); } @Test public void should_commit_even_when_exception_on_commit_callable() throws Exception { //given doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus(); txService.registerBeforeCommitCallable(beforeCommitCallable); doThrow(Exception.class).when(beforeCommitCallable).call(); //when try { txService.complete(); fail(); } catch (STransactionCommitException ignored) { } //then verify(txManager).commit(); } @Test public void should_not_call_begin_if_transaction_is_externally_managed() throws Exception { //given doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus(); //when txService.begin(); //then verify(txManager, never()).begin(); } @Test public void should_call_begin_when_tx_manager_do_not_have_transaction() throws Exception { //given doReturn(Status.STATUS_NO_TRANSACTION).when(txManager).getStatus(); //when txService.begin(); //then verify(txManager).begin(); } @Test public void should_not_call_commit_if_transaction_is_externally_managed() throws Exception { //given doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus(); txService.getTransactionServiceContext().externallyManaged = true; //when txService.complete(); //then verify(txManager, never()).commit(); } @Test public void should_call_commit_on_transaction_manager() throws Exception { //given doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus(); //when txService.complete(); //then verify(txManager).commit(); } @Test public void should_call_rollback_when_status_is_rollback_only() throws Exception { //given doReturn(Status.STATUS_MARKED_ROLLBACK).when(txManager).getStatus(); //when txService.complete(); //then verify(txManager).rollback(); } @Test(expected = STransactionCommitException.class) public void should_throw_exception_when_complete_on_no_transaction() throws Exception { doReturn(Status.STATUS_NO_TRANSACTION).when(txManager).getStatus(); txService.complete(); } @Test public void should_begin_execute_callable_and_complete() throws Exception { //given doReturn(Status.STATUS_NO_TRANSACTION).doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus(); //when txService.executeInTransaction(txContent); //then InOrder inOrder = inOrder(txManager, txContent); inOrder.verify(txManager).begin(); inOrder.verify(txContent).call(); inOrder.verify(txManager).commit(); } @Test public void should_trace_call_when_TRACE_active() throws Exception { doReturn(Status.STATUS_NO_TRANSACTION).doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus(); TransactionService txService = new JTATransactionServiceImpl(txManager); in_an_other_method_to_verify_the_stacktrace_contains_this_method_name(txService); try { txService.begin(); fail(); } catch (STransactionCreationException e) { assertThat(e.getMessage()) .contains("in_an_other_method_to_verify_the_stacktrace_contains_this_method_name"); } } private void in_an_other_method_to_verify_the_stacktrace_contains_this_method_name(TransactionService txService) throws STransactionCreationException { txService.begin(); } @Test public void isTransactionActive_should_return_true_when_there_is_an_active_transaction() throws Exception { doReturn(Status.STATUS_ACTIVE).when(txManager).getStatus(); boolean active = txService.isTransactionActive(); assertThat(active).isTrue(); } @Test public void isTransactionActive_should_return_false_when_transaction_manager_throws_exception() throws Exception { doThrow(SystemException.class).when(txManager).getStatus(); boolean active = txService.isTransactionActive(); assertThat(active).isFalse(); } @Test public void isTransactionActive_should_return_true_when_there_is_no_active_transaction() throws Exception { doReturn(Status.STATUS_UNKNOWN).when(txManager).getStatus(); boolean active = txService.isTransactionActive(); assertThat(active).isFalse(); } @Test public void hasMultipleResources_should_have_no_value_when_xaResourceRetriever_gives_no_resources() throws Exception { when(xaResourceRetriever.retrieveResources(any())).thenReturn(Optional.empty()); Optional hasMultipleResources = txService.hasMultipleResources(); assertThat(hasMultipleResources).isEmpty(); } @Test public void hasMultipleResources_should_return_false_when_transaction_has_one_resource() { doReturn(Optional.of(Collections.singletonList(mock(XAResource.class)))).when(xaResourceRetriever) .retrieveResources(any()); Optional hasMultipleResources = txService.hasMultipleResources(); assertThat(hasMultipleResources.get()).isFalse(); } @Test public void hasMultipleResources_should_return_true_when_transaction_has_multiple_resource() { doReturn(Optional.of(Arrays.asList(mock(XAResource.class), mock(XAResource.class)))).when(xaResourceRetriever) .retrieveResources(any()); Optional hasMultipleResources = txService.hasMultipleResources(); assertThat(hasMultipleResources.get()).isTrue(); } @Test(expected = RuntimeException.class) public void hasMultipleResources_should_throw_runtime_exception_when_getTransaction_throws_SystemException() throws SystemException { doThrow(SystemException.class).when(txManager).getTransaction(); txService.hasMultipleResources(); } } ================================================ FILE: services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/TransactionLifeCycleTest.java ================================================ /** * 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.transaction; import static javax.transaction.Status.STATUS_COMMITTED; import static javax.transaction.Status.STATUS_ROLLEDBACK; import static org.junit.Assert.*; import javax.transaction.Status; import javax.transaction.TransactionManager; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class TransactionLifeCycleTest { private static TransactionManager transactionManager; private TransactionService txService; @BeforeClass public static void setupTransactionManager() { transactionManager = com.arjuna.ats.jta.TransactionManager.transactionManager(); } @Before public void before() { txService = new JTATransactionServiceImpl(transactionManager); } @SuppressWarnings("deprecation") @After public void closeTransactions() throws Exception { // Do not forget to close the transaction if (txService.isTransactionActive()) { txService.complete(); } } @Test public void testActiveTransactionState() throws Exception { txService.begin(); assertEquals(Status.STATUS_ACTIVE, transactionManager.getStatus()); } @Test public void testCommittedTransactionState() throws Exception { TxSync sync = new TxSync(); txService.begin(); txService.registerBonitaSynchronization(sync); txService.complete(); assertEquals(STATUS_COMMITTED, sync.getTxCompletionState()); assertEquals(Status.STATUS_NO_TRANSACTION, transactionManager.getStatus()); } @Test public void testRollbackOnlyTransactionState() throws Exception { txService.begin(); txService.setRollbackOnly(); assertEquals(Status.STATUS_MARKED_ROLLBACK, transactionManager.getStatus()); txService.complete(); } @Test public void testRolledbackTransactionState() throws Exception { TxSync sync = new TxSync(); txService.begin(); txService.registerBonitaSynchronization(sync); txService.setRollbackOnly(); txService.complete(); assertEquals(STATUS_ROLLEDBACK, sync.getTxCompletionState()); assertEquals(Status.STATUS_NO_TRANSACTION, transactionManager.getStatus()); } @Test public void testGetState1() throws Exception { TxSync sync = new TxSync(); txService.begin(); txService.registerBonitaSynchronization(sync); assertEquals(Status.STATUS_ACTIVE, transactionManager.getStatus()); txService.setRollbackOnly(); assertEquals(Status.STATUS_MARKED_ROLLBACK, transactionManager.getStatus()); txService.complete(); assertEquals(STATUS_ROLLEDBACK, sync.getTxCompletionState()); assertEquals(Status.STATUS_NO_TRANSACTION, transactionManager.getStatus()); } @Test public void testGetState2() throws Exception { TxSync sync = new TxSync(); txService.begin(); txService.registerBonitaSynchronization(sync); assertEquals(Status.STATUS_ACTIVE, transactionManager.getStatus()); txService.complete(); assertEquals(STATUS_COMMITTED, sync.getTxCompletionState()); assertEquals(Status.STATUS_NO_TRANSACTION, transactionManager.getStatus()); } @Test public void testTransactionIsRollbackOnly() throws Exception { txService.begin(); txService.setRollbackOnly(); assertTrue(txService.isRollbackOnly()); txService.complete(); } @Test public void testSetRollbackOnlyOnActiveTx() throws Exception { txService.begin(); txService.setRollbackOnly(); assertEquals(Status.STATUS_MARKED_ROLLBACK, transactionManager.getStatus()); txService.complete(); } @Test public void testSetRollbackOnlyOnCommitedTx() throws Exception { TxSync sync = new TxSync(); txService.begin(); txService.registerBonitaSynchronization(sync); txService.complete(); try { txService.setRollbackOnly(); fail("Impossible to call setRollbackOnly on a tx with state COMMITTED"); } catch (final STransactionException e) { assertEquals(STATUS_COMMITTED, sync.getTxCompletionState()); assertEquals(Status.STATUS_NO_TRANSACTION, transactionManager.getStatus()); } } @Test public void testBeginActiveTx() throws Exception { txService.begin(); try { txService.begin(); fail("Impossible to begin a tx with state ACTIVE"); } catch (final STransactionCreationException e) { assertEquals(Status.STATUS_ACTIVE, transactionManager.getStatus()); } } @Test public void testBeginRollbackOnlyTx() throws Exception { txService.begin(); txService.setRollbackOnly(); try { txService.begin(); System.out.println("++++" + txService.getNumberOfActiveTransactions()); fail("Impossible to begin a tx with state ROLLBACKONLY"); } catch (final STransactionCreationException e) { assertEquals(Status.STATUS_MARKED_ROLLBACK, transactionManager.getStatus()); } finally { txService.complete(); } } // This is a dumb implementation of the BonitaTransactionSynchronization interface just to // keep a reference to transaction completion's state. private static class TxSync implements BonitaTransactionSynchronization { private int txCompletionState; public TxSync() { } public int getTxCompletionState() { return txCompletionState; } @Override public void afterCompletion(final int txState) { this.txCompletionState = txState; } } } ================================================ FILE: services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/TransactionServiceTest.java ================================================ /** * 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.transaction; import static org.junit.Assert.assertEquals; import java.util.concurrent.CountDownLatch; import javax.transaction.TransactionManager; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class TransactionServiceTest { private static TransactionManager transactionManager; private TransactionService txService; @BeforeClass public static void setupTransactionManager() { transactionManager = com.arjuna.ats.jta.TransactionManager.transactionManager(); } @Before public void before() { txService = new JTATransactionServiceImpl(transactionManager); } @After public void afterTest() { try { txService.complete(); } catch (final Exception e) { // Do nothing } } @Test(expected = STransactionCreationException.class) public void testCantCreateATransactionWithActiveTx() throws Exception { txService.begin(); txService.begin(); } @Test(expected = STransactionCreationException.class) public void testCantCreateATransactionWithCreatedTx() throws Exception { txService.begin(); txService.begin(); } @Test(expected = STransactionCreationException.class) public void testCantCreateATransactionWithRollbackOnlyTx() throws Exception { txService.begin(); txService.setRollbackOnly(); try { txService.begin(); } finally { txService.complete(); } } @Test public void getNumberOfActiveTransactionsWithTransactionOpen() throws Exception { txService.begin(); assertEquals(1, txService.getNumberOfActiveTransactions()); txService.complete(); assertEquals(0, txService.getNumberOfActiveTransactions()); } @Test public void getNumberOfActiveTransactionsWithTransactionMarkedAsRollback() throws Exception { txService.begin(); assertEquals(1, txService.getNumberOfActiveTransactions()); txService.setRollbackOnly(); assertEquals(1, txService.getNumberOfActiveTransactions()); txService.complete(); assertEquals(0, txService.getNumberOfActiveTransactions()); } @Test public void getNumberOfActiveTransactionsWith2TransactionsOpen() throws Exception { final CountDownLatch lock1 = new CountDownLatch(1); final CountDownLatch lock2 = new CountDownLatch(1); final CountDownLatch startSignal = new CountDownLatch(2); final TransactionWorker txWorker1 = new TransactionWorker(lock1, txService, startSignal); final TransactionWorker txWorker2 = new TransactionWorker(lock2, txService, startSignal); final Thread t1 = new Thread(txWorker1); t1.setName("txWorker1"); final Thread t2 = new Thread(txWorker2); t2.setName("txWorker2"); t1.start(); t2.start(); startSignal.await(); assertEquals(2, txService.getNumberOfActiveTransactions()); lock1.countDown(); t1.join(); assertEquals(1, txService.getNumberOfActiveTransactions()); lock2.countDown(); t2.join(); assertEquals(0, txService.getNumberOfActiveTransactions()); } private static class TransactionWorker implements Runnable { private final CountDownLatch lock; private final TransactionService transactionService; private final CountDownLatch startSignal; TransactionWorker(final CountDownLatch lock, final TransactionService transactionService, final CountDownLatch startSignal) { this.lock = lock; this.transactionService = transactionService; this.startSignal = startSignal; } @Override public void run() { try { transactionService.begin(); // Signal the caller that we are have started and the transaction has begun startSignal.countDown(); // Wait until the caller tells us to lock.await(); } catch (final Exception e) { throw new RuntimeException(e); } finally { try { transactionService.complete(); } catch (final STransactionCommitException e) { e.printStackTrace(); } catch (final STransactionRollbackException e) { e.printStackTrace(); } } } } } ================================================ FILE: services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/TransactionSynchronizationTest.java ================================================ /** * 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.transaction; import static org.junit.Assert.*; import javax.transaction.Status; import javax.transaction.TransactionManager; import org.bonitasoft.engine.transaction.synchronization.SimpleSynchronization; import org.bonitasoft.engine.transaction.synchronization.StaticSynchronization; import org.bonitasoft.engine.transaction.synchronization.StaticSynchronizationResult; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class TransactionSynchronizationTest { private static TransactionManager transactionManager; private TransactionService txService; @BeforeClass public static void setupTransactionManager() { transactionManager = com.arjuna.ats.jta.TransactionManager.transactionManager(); } @Before public void before() { txService = new JTATransactionServiceImpl(transactionManager); } @After public void closeTransactions() throws Exception { if (txService.isTransactionActive()) { txService.complete(); } } @Test public void testSimpleRegisterSynchronization() throws Exception { txService.begin(); final SimpleSynchronization simpleSynchronization = new SimpleSynchronization(); txService.registerBonitaSynchronization(simpleSynchronization); assertFalse(simpleSynchronization.isBeforeCompletion()); assertFalse(simpleSynchronization.isAfterCompletion()); assertEquals(Status.STATUS_NO_TRANSACTION, simpleSynchronization.getAfterCompletionStatus()); } private void testSynchronizationStatus(final boolean rollback, final int expectedStatus) throws Exception { txService.begin(); final SimpleSynchronization[] synchronizations = new SimpleSynchronization[] { new SimpleSynchronization(), new SimpleSynchronization() }; for (final SimpleSynchronization sync : synchronizations) { txService.registerBonitaSynchronization(sync); } for (final SimpleSynchronization sync : synchronizations) { assertFalse(sync.isBeforeCompletion()); assertFalse(sync.isAfterCompletion()); assertEquals(Status.STATUS_NO_TRANSACTION, sync.getAfterCompletionStatus()); } if (rollback) { txService.setRollbackOnly(); } txService.complete(); for (final SimpleSynchronization sync : synchronizations) { assertEquals(!rollback, sync.isBeforeCompletion()); assertTrue(sync.isAfterCompletion()); assertEquals(expectedStatus, sync.getAfterCompletionStatus()); } } @Test public void testSynchronizationStatusOnCommit() throws Exception { testSynchronizationStatus(false, Status.STATUS_COMMITTED); } @Test public void testSynchronizationStatusOnRollback() throws Exception { testSynchronizationStatus(true, Status.STATUS_ROLLEDBACK); } private void testRegisteredSynchronizationsOrder(final boolean rollback, final StaticSynchronization... synchronizations) throws Exception { // in fact the same on commit or rollback... Synchronizations are always called txService.begin(); StaticSynchronizationResult.reset(); final StringBuilder beforeBuilder = new StringBuilder(); final StringBuilder afterBuilder = new StringBuilder(); for (final StaticSynchronization synchronization : synchronizations) { txService.registerBonitaSynchronization(synchronization); if (!rollback) { beforeBuilder.append(synchronization.getBeforeCompletionComment()); } afterBuilder.append(synchronization.getAfterCompletionComment()); } final String expected = beforeBuilder.toString() + afterBuilder.toString(); if (rollback) { txService.setRollbackOnly(); } txService.complete(); assertEquals(expected, StaticSynchronizationResult.COMMENT); } private void testRegisteredSynchronizationsOrderOnFailure(final boolean rollback, final boolean failOnBefore, final boolean failOnAfter) throws Exception { testRegisteredSynchronizationsOrder(rollback, new StaticSynchronization(1, failOnBefore, failOnAfter), new StaticSynchronization(2), new StaticSynchronization(3)); testRegisteredSynchronizationsOrder(rollback, new StaticSynchronization(1), new StaticSynchronization(2, failOnBefore, failOnAfter), new StaticSynchronization(3)); testRegisteredSynchronizationsOrder(rollback, new StaticSynchronization(1), new StaticSynchronization(2), new StaticSynchronization(3, failOnBefore, failOnAfter)); testRegisteredSynchronizationsOrder(rollback, new StaticSynchronization(1, failOnBefore, failOnAfter), new StaticSynchronization(2, failOnBefore, failOnAfter), new StaticSynchronization(3, failOnBefore, failOnAfter)); } @Test(expected = STransactionCommitException.class) public void testRegisteredSynchronizationsOrderOnFailureDuringBeforeCompletionOnCommit() throws Exception { testRegisteredSynchronizationsOrderOnFailure(false, true, false); } @Test(expected = STransactionCommitException.class) public void testRegisteredSynchronizationsOrderOnFailureDuringBeforeAndAfterCompletionOnCommit() throws Exception { testRegisteredSynchronizationsOrderOnFailure(false, true, true); } } ================================================ FILE: services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/XAResourceRetrieverTest.java ================================================ /** * Copyright (C) 2021 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.transaction; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import javax.transaction.Transaction; import javax.transaction.xa.XAResource; import com.arjuna.ats.internal.jta.xa.TxInfo; import org.junit.Test; public class XAResourceRetrieverTest { @Test public void should_return_2_resources_when_arjuna_transaction_manager_has_2_resources() { com.arjuna.ats.jta.transaction.Transaction transaction = mock(com.arjuna.ats.jta.transaction.Transaction.class); Map xaResourceTxInfoMap = new HashMap<>(); xaResourceTxInfoMap.put(mock(XAResource.class), mock(TxInfo.class)); xaResourceTxInfoMap.put(mock(XAResource.class), mock(TxInfo.class)); doReturn(xaResourceTxInfoMap).when(transaction).getResources(); Optional> xaResources = new XAResourceRetriever().retrieveResources(transaction); assertThat(xaResources.get()).hasSize(2); } @Test public void should_return_1_resource_when_arjuna_transaction_manager_has_1_resource() { com.arjuna.ats.jta.transaction.Transaction transaction = mock(com.arjuna.ats.jta.transaction.Transaction.class); Map xaResourceTxInfoMap = new HashMap<>(); xaResourceTxInfoMap.put(mock(XAResource.class), mock(TxInfo.class)); doReturn(xaResourceTxInfoMap).when(transaction).getResources(); Optional> xaResources = new XAResourceRetriever().retrieveResources(transaction); assertThat(xaResources.get()).hasSize(1); } @Test public void should_return_no_value_when_transaction_manager_is_not_arjuna() throws NoSuchMethodException { XAResourceRetriever xaResourceRetriever = new XAResourceRetriever(); Transaction transaction = mock(Transaction.class); Optional> xaResources = xaResourceRetriever.retrieveResources(transaction); assertThat(xaResources).isEmpty(); } @Test public void should_return_no_value_resource_when_transaction_object_is_not_arjuna() { com.arjuna.ats.jta.transaction.Transaction transaction = mock(com.arjuna.ats.jta.transaction.Transaction.class); doThrow(new ClassCastException()).when(transaction).getResources(); Optional> xaResources = new XAResourceRetriever().retrieveResources(transaction); assertThat(xaResources).isEmpty(); } @Test public void should_not_retry_getResource_when_first_call_failed() { com.arjuna.ats.jta.transaction.Transaction transaction = mock(com.arjuna.ats.jta.transaction.Transaction.class); doThrow(new ClassCastException()).when(transaction).getResources(); XAResourceRetriever xaResourceRetriever = new XAResourceRetriever(); assertThat(xaResourceRetriever.retrieveResources(transaction)).isEmpty(); assertThat(xaResourceRetriever.retrieveResources(transaction)).isEmpty(); verify(transaction, times(1)).getResources(); } } ================================================ FILE: services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/synchronization/SimpleSynchronization.java ================================================ /** * 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.transaction.synchronization; import javax.transaction.Status; import org.bonitasoft.engine.transaction.BonitaTransactionSynchronization; public class SimpleSynchronization implements BonitaTransactionSynchronization { private boolean beforeCompletion = false; private boolean afterCompletion = false; private int afterCompletionStatus = Status.STATUS_NO_TRANSACTION; private final boolean failOnBeforeCompletion; private final boolean failOnAfterCompletion; public SimpleSynchronization() { this.failOnBeforeCompletion = false; this.failOnAfterCompletion = false; } @Override public void beforeCompletion() { if (this.failOnBeforeCompletion) { throw new RuntimeException(); } this.beforeCompletion = true; } @Override public void afterCompletion(final int status) { if (this.failOnAfterCompletion) { throw new RuntimeException(); } this.afterCompletion = true; this.afterCompletionStatus = status; } public boolean isBeforeCompletion() { return this.beforeCompletion; } public boolean isAfterCompletion() { return this.afterCompletion; } public int getAfterCompletionStatus() { return this.afterCompletionStatus; } } ================================================ FILE: services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/synchronization/StaticSynchronization.java ================================================ /** * 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.transaction.synchronization; import org.bonitasoft.engine.transaction.BonitaTransactionSynchronization; public class StaticSynchronization implements BonitaTransactionSynchronization { private final String beforeCompletionComment; private final String afterCompletionComment; private boolean failOnBefore = false; private boolean failOnAfter = false; public StaticSynchronization(final int id) { super(); this.beforeCompletionComment = "sync" + id + "Before"; this.afterCompletionComment = "sync" + id + "After"; } public StaticSynchronization(final int id, final boolean failOnBefore, final boolean failOnAfter) { this(id); this.failOnBefore = failOnBefore; this.failOnAfter = failOnAfter; } public String getBeforeCompletionComment() { return this.beforeCompletionComment; } public String getAfterCompletionComment() { return this.afterCompletionComment; } @Override public void beforeCompletion() { StaticSynchronizationResult.COMMENT += this.beforeCompletionComment; if (this.failOnBefore) { throw new RuntimeException(); } } @Override public void afterCompletion(final int status) { StaticSynchronizationResult.COMMENT += this.afterCompletionComment; if (this.failOnAfter) { throw new RuntimeException(); } } } ================================================ FILE: services/bonita-transaction/src/test/java/org/bonitasoft/engine/transaction/synchronization/StaticSynchronizationResult.java ================================================ /** * 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.transaction.synchronization; public class StaticSynchronizationResult { public static String COMMENT = ""; public static void reset() { COMMENT = ""; } } ================================================ FILE: services/bonita-transaction/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: services/bonita-work/build.gradle ================================================ dependencies { api project(':services:bonita-session') api project(':services:bonita-incident') api project(':services:bonita-commons') api project(':services:bonita-transaction') api libs.springJdbc api libs.springContext testImplementation libs.awaitility testImplementation libs.assertj testImplementation libs.mockitoCore testImplementation libs.mockitoJunitJupiter testImplementation libs.systemRules testRuntimeOnly libs.logback } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/BonitaExecutorService.java ================================================ /** * 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.work; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * This is the interface we use to wrap the ThreadPool that execute works * * @author Julien Reboul * @author Baptiste Mesta */ public interface BonitaExecutorService { /** * clear the queue of work */ void clearAllQueues(); /** * shutdown and handle the queue properly */ void shutdownAndEmptyQueue(); /** * Execute the work described by the work descriptor * * @param work */ Future submit(WorkDescriptor work); boolean awaitTermination(long workTerminationTimeout, TimeUnit seconds) throws InterruptedException; ThreadPoolExecutor getExecutor(); } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/BonitaExecutorServiceFactory.java ================================================ /** * 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.work; /** * A factory to create executor service that will be given to the WorkService * * @author Baptiste Mesta */ public interface BonitaExecutorServiceFactory { /** * Create a bonita executor service with the given {@link WorkExecutionCallback} * * @param workExecutionCallback this callback will be executed when a work complete. * @return the {@link BonitaExecutorService} */ BonitaExecutorService createExecutorService(WorkExecutionCallback workExecutionCallback); default void unbind() { } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/BonitaRunnable.java ================================================ /** * 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.work; import java.io.Serializable; /** * A runnable that notifies a listener of its state * * @author Baptiste Mesta */ public abstract class BonitaRunnable implements Runnable, Serializable { private static final long serialVersionUID = 1L; private final long tenantId; public BonitaRunnable(final long tenantId) { this.tenantId = tenantId; } @Override public void run() { innerRun(); } public abstract void innerRun(); public abstract void cancel(); public long getTenantId() { return tenantId; } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/BonitaWork.java ================================================ /** * 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.work; import java.util.Map; import java.util.UUID; import java.util.concurrent.CompletableFuture; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; /** * @author Baptiste Mesta */ public abstract class BonitaWork { private final String uuid = UUID.randomUUID().toString(); protected long tenantId; private BonitaWork parentWork; public String getUuid() { return uuid; } public abstract String getDescription(); /** * @return * how to restart the work if it fails */ public String getRecoveryProcedure() { return "No recovery procedure."; } /** * Execution code of the work * * @param context * a map of context that can be filled by a work to be given to a wrapped work * @throws Exception */ public abstract CompletableFuture work(Map context) throws Exception; public abstract void handleFailure(Throwable e, Map context) throws Exception; /** * @return true if the RecoveryService can recover this kind of failure */ public boolean canBeRecoveredByTheRecoveryMechanism() { return false; } public void setParent(final BonitaWork parentWork) { this.parentWork = parentWork; } public BonitaWork getParent() { return parentWork; } protected BonitaWork getRootWork() { BonitaWork rootWork = this; while (rootWork.getParent() != null) { rootWork = rootWork.getParent(); } return rootWork; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BonitaWork work = (BonitaWork) o; return new EqualsBuilder() .append(uuid, work.uuid) .append(parentWork, work.parentWork) .isEquals(); } @Override public int hashCode() { return new HashCodeBuilder(17, 37) .append(uuid) .append(parentWork) .toHashCode(); } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/BonitaWorkExecutorFactory.java ================================================ /** * 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.work; import java.util.concurrent.ThreadPoolExecutor; public interface BonitaWorkExecutorFactory { ThreadPoolExecutor create(); } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/DefaultBonitaExecutorService.java ================================================ /** * 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.work; import java.util.Collection; import java.util.HashMap; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import org.bonitasoft.engine.commons.time.EngineClock; import org.bonitasoft.engine.mdc.MDCHelper; import org.bonitasoft.engine.work.audit.WorkExecutionAuditor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DefaultBonitaExecutorService implements BonitaExecutorService { private static final Logger log = LoggerFactory.getLogger(DefaultBonitaExecutorService.class); public static final String NUMBER_OF_WORKS_PENDING = "bonita.bpmengine.work.pending"; public static final String NUMBER_OF_WORKS_RUNNING = "bonita.bpmengine.work.running"; public static final String NUMBER_OF_WORKS_EXECUTED = "bonita.bpmengine.work.executed"; public static final String WORKS_UNIT = "works"; private final WorkFactory workFactory; private final EngineClock engineClock; private final WorkExecutionCallback workExecutionCallback; private final WorkExecutionAuditor workExecutionAuditor; private final MeterRegistry meterRegistry; private final AtomicLong runningWorks = new AtomicLong(); private final Counter executedWorkCounter; private final Gauge numberOfWorksPending; private final Gauge numberOfWorksRunning; private final ThreadPoolExecutor executor; public DefaultBonitaExecutorService(final ThreadPoolExecutor executor, final WorkFactory workFactory, final EngineClock engineClock, final WorkExecutionCallback workExecutionCallback, final WorkExecutionAuditor workExecutionAuditor, final MeterRegistry meterRegistry) { this.executor = executor; this.workFactory = workFactory; this.engineClock = engineClock; this.workExecutionCallback = workExecutionCallback; this.workExecutionAuditor = workExecutionAuditor; this.meterRegistry = meterRegistry; numberOfWorksPending = Gauge.builder(NUMBER_OF_WORKS_PENDING, executor.getQueue(), Collection::size) .baseUnit(WORKS_UNIT).description("Works pending in the execution queue") .register(meterRegistry); numberOfWorksRunning = Gauge.builder(NUMBER_OF_WORKS_RUNNING, runningWorks, AtomicLong::get) .baseUnit(WORKS_UNIT).description("Works currently executing") .register(meterRegistry); executedWorkCounter = Counter.builder(NUMBER_OF_WORKS_EXECUTED) .baseUnit(WORKS_UNIT).description("total works executed since last server start") .register(meterRegistry); } public ThreadPoolExecutor getExecutor() { return executor; } @Override public void clearAllQueues() { executor.getQueue().clear(); } @Override public void shutdownAndEmptyQueue() { executor.shutdown(); log.info("Clearing queue of work, had {} elements", executor.getQueue().size()); executor.getQueue().clear(); meterRegistry.remove(numberOfWorksPending); meterRegistry.remove(numberOfWorksRunning); meterRegistry.remove(executedWorkCounter); } @Override public Future submit(WorkDescriptor work) { return executor.submit(() -> { if (isRequiringDelayedExecution(work)) { // Future implementation should use a real delay e.g. using a ScheduledThreadPoolExecutor // Will be executed later submit(work); return; } work.incrementExecutionCount(); workExecutionAuditor.detectAbnormalExecutionAndNotify(work); BonitaWork bonitaWork = workFactory.create(work); HashMap context = new HashMap<>(); CompletableFuture asyncResult; runningWorks.incrementAndGet(); try { asyncResult = bonitaWork.work(context); } catch (Exception e) { executedWorkCounter.increment(); runningWorks.decrementAndGet(); MDCHelper.tryWithMDC(e, () -> workExecutionCallback.onFailure(work, bonitaWork, context, e)); return; } asyncResult.handle((result, error) -> { executedWorkCounter.increment(); runningWorks.decrementAndGet(); if (error != null) { if (error instanceof CompletionException) { error = error.getCause(); } workExecutionCallback.onFailure(work, bonitaWork, context, error); } else { workExecutionCallback.onSuccess(work); } return null; }); }); } @Override public boolean awaitTermination(long workTerminationTimeout, TimeUnit seconds) throws InterruptedException { return executor.awaitTermination(workTerminationTimeout, seconds); } private boolean isRequiringDelayedExecution(WorkDescriptor work) { return work.getExecutionThreshold() != null && work.getExecutionThreshold().isAfter(engineClock.now()); } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/DefaultBonitaExecutorServiceFactory.java ================================================ /** * 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.work; import java.util.concurrent.ThreadPoolExecutor; import io.micrometer.core.instrument.MeterRegistry; import org.bonitasoft.engine.commons.time.EngineClock; import org.bonitasoft.engine.monitoring.ExecutorServiceMetricsProvider; import org.bonitasoft.engine.work.audit.WorkExecutionAuditor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * Use ThreadPoolExecutor as ExecutorService * The handling of threads relies on the JVM * The rules to create new thread are: * - If the number of threads is less than the corePoolSize, create a new Thread to run a new task. * - If the number of threads is equal (or greater than) the corePoolSize, put the task into the queue. * - If the queue is full, and the number of threads is less than the maxPoolSize, create a new thread to run tasks in. * - If the queue is full, and the number of threads is greater than or equal to maxPoolSize, reject the task. * When the current number of threads are > than corePoolSize, they are kept idle during keepAliveTimeSeconds * * @author Baptiste Mesta */ @Component("bonitaExecutorServiceFactory") public class DefaultBonitaExecutorServiceFactory implements BonitaExecutorServiceFactory { private static final String BONITA_WORK_EXECUTOR = "bonita-work-executor"; private final Logger logger = LoggerFactory.getLogger(DefaultBonitaExecutorServiceFactory.class); private final MeterRegistry meterRegistry; private final ExecutorServiceMetricsProvider executorServiceMetricsProvider; private final BonitaWorkExecutorFactory bonitaWorkExecutorFactory; private final EngineClock engineClock; private final WorkFactory workFactory; private final WorkExecutionAuditor workExecutionAuditor; public DefaultBonitaExecutorServiceFactory( MeterRegistry meterRegistry, EngineClock engineClock, WorkFactory workFactory, WorkExecutionAuditor workExecutionAuditor, ExecutorServiceMetricsProvider executorServiceMetricsProvider, BonitaWorkExecutorFactory bonitaWorkExecutorFactory) { this.meterRegistry = meterRegistry; this.workFactory = workFactory; this.workExecutionAuditor = workExecutionAuditor; this.engineClock = engineClock; this.executorServiceMetricsProvider = executorServiceMetricsProvider; this.bonitaWorkExecutorFactory = bonitaWorkExecutorFactory; } @Override public BonitaExecutorService createExecutorService(WorkExecutionCallback workExecutionCallback) { final ThreadPoolExecutor bonitaThreadPoolExecutor = bonitaWorkExecutorFactory.create(); final BonitaExecutorService bonitaExecutorService = new DefaultBonitaExecutorService(bonitaThreadPoolExecutor, workFactory, engineClock, workExecutionCallback, workExecutionAuditor, meterRegistry); logger.info( "Creating a new Thread pool to handle works: {}", bonitaThreadPoolExecutor); //TODO this returns the timed executor service, this should be used instead of the BonitaExecutorService but we should change it everywhere executorServiceMetricsProvider .bindMetricsOnly(meterRegistry, bonitaThreadPoolExecutor, BONITA_WORK_EXECUTOR); return bonitaExecutorService; } @Override public void unbind() { executorServiceMetricsProvider.unbind(meterRegistry, BONITA_WORK_EXECUTOR); } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/DefaultExceptionRetryabilityEvaluator.java ================================================ /** * Copyright (C) 2020 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.work; import static javax.transaction.xa.XAException.XA_RBTIMEOUT; import static javax.transaction.xa.XAException.XA_RBTRANSIENT; import static org.bonitasoft.engine.work.ExceptionRetryabilityEvaluator.Retryability.NOT_RETRYABLE; import static org.bonitasoft.engine.work.ExceptionRetryabilityEvaluator.Retryability.RETRYABLE; import static org.bonitasoft.engine.work.ExceptionRetryabilityEvaluator.Retryability.UNCERTAIN_COMPLETION_OF_COMMIT; import java.lang.reflect.Method; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Optional; import javax.transaction.xa.XAException; import org.bonitasoft.engine.transaction.STransactionCommitException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DataAccessException; import org.springframework.dao.RecoverableDataAccessException; import org.springframework.dao.TransientDataAccessException; import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator; /** * @author Baptiste Mesta. */ public class DefaultExceptionRetryabilityEvaluator implements ExceptionRetryabilityEvaluator { private static final Logger logger = LoggerFactory.getLogger(DefaultExceptionRetryabilityEvaluator.class); private static final List XA_RETRYABLE = Arrays.asList(XA_RBTRANSIENT, XA_RBTIMEOUT); private HashSet> exceptionClassesToRetry; private HashSet> exceptionClassesToNotRetry; public DefaultExceptionRetryabilityEvaluator(List exceptionClassesToRetry, List exceptionClassesToNotRetry) { this.exceptionClassesToRetry = toSetOfClasses(exceptionClassesToRetry); this.exceptionClassesToNotRetry = toSetOfClasses(exceptionClassesToNotRetry); } private HashSet> toSetOfClasses(List exceptionClassesToRetry) { ClassLoader classLoader = this.getClass().getClassLoader(); HashSet> classSet = new HashSet<>(); for (String className : exceptionClassesToRetry) { classSet.add(getCheckedThrowable(classLoader, className)); } return classSet; } private Class getCheckedThrowable(ClassLoader classLoader, String className) { Class throwable; try { throwable = classLoader.loadClass(className); } catch (ClassNotFoundException e) { throw new IllegalArgumentException(String.format("The class %s is not found", className), e); } if (!Throwable.class.isAssignableFrom(throwable)) { throw new IllegalArgumentException(String.format("The class %s is not a Throwable", className)); } return (Class) throwable; } @Override public Retryability evaluateRetryability(Throwable thrown) { List exceptions = getAllCauses(thrown); Optional notRetryableException = exceptions.stream().filter(this::isKnownAsNotRetryable).findFirst(); if (notRetryableException.isPresent()) { logger.debug("Exception is known as not retryable: {}", print(exceptions, notRetryableException.get())); return NOT_RETRYABLE; } Optional retryableException = exceptions.stream().filter(this::isRetryable).findFirst(); if (!retryableException.isPresent()) { logger.debug("No retryable exception found: {}", print(exceptions)); return NOT_RETRYABLE; } Throwable theRetryableException = retryableException.get(); if (isSqlConnectionIssue(theRetryableException) && exceptions.stream().anyMatch(e -> e instanceof STransactionCommitException)) { logger.debug( "Retryable exception found but exception happened on commit, uncertain completion of the transaction: {}", print(exceptions, theRetryableException)); return UNCERTAIN_COMPLETION_OF_COMMIT; } else { logger.debug("Retryable exception found: {}", print(exceptions, theRetryableException)); return RETRYABLE; } } private boolean isRetryable(Throwable thrown) { if (isInListedRetriableException(thrown)) { return true; } if (thrown instanceof SQLException) { DataAccessException dataAccessException = new SQLErrorCodeSQLExceptionTranslator().translate("", "", (SQLException) thrown); if (dataAccessException instanceof RecoverableDataAccessException || dataAccessException instanceof TransientDataAccessException) { return true; } } if (thrown instanceof XAException) { int errorCode = ((XAException) thrown).errorCode; if (XA_RETRYABLE.contains(errorCode)) { return true; } } return isSqlConnectionIssue(thrown); } private List getAllCauses(Throwable thrown) { List allExceptions = new ArrayList<>(); while (thrown != null) { allExceptions.add(thrown); List exceptions = getExceptions(thrown, "getExceptions", "getHandlerExceptions"); if (!exceptions.isEmpty()) { for (Exception exception : exceptions) { allExceptions.addAll(getAllCauses(exception)); } } thrown = thrown.getCause(); } return allExceptions; } private boolean isSqlConnectionIssue(Throwable thrown) { String name = thrown.getClass().getSimpleName().toLowerCase(); String message = thrown.getMessage() != null ? thrown.getMessage().toLowerCase() : ""; return (name.contains("sql") || name.contains("jdbc")) && (message.contains("connection") || message.contains("timeout") || message.contains("i/o error")); } private String print(List throwables, Throwable highlighted) { StringBuilder stringBuilder = new StringBuilder("Exceptions:\n"); for (Throwable throwable : throwables) { stringBuilder.append(" ↳"); stringBuilder.append(throwable.getClass().getName()); if (highlighted != null && throwable.getClass().equals(highlighted.getClass())) { stringBuilder.append(": "); stringBuilder.append(throwable.getMessage()); stringBuilder.append(" ☚"); } stringBuilder.append('\n'); } return stringBuilder.toString(); } private String print(List throwables) { return print(throwables, null); } private boolean isInListedRetriableException(Throwable e) { return isListedIn(e, exceptionClassesToRetry); } private boolean isKnownAsNotRetryable(Throwable e) { return isListedIn(e, exceptionClassesToNotRetry); } private boolean isListedIn(Throwable e, HashSet> exceptions) { for (Class throwable : exceptions) { if (throwable.isInstance(e)) { return true; } } return false; } private List getExceptions(Throwable thrown, String... methodNames) { for (String methodName : methodNames) { try { //for bitronix PhaseException and FireEventExceptions Method getExceptions = thrown.getClass().getDeclaredMethod(methodName); return (List) getExceptions.invoke(thrown); } catch (Throwable ignored) { } } return Collections.emptyList(); } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/ExceptionRetryabilityEvaluator.java ================================================ /** * Copyright (C) 2020 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.work; /** * @author Baptiste Mesta. */ public interface ExceptionRetryabilityEvaluator { enum Retryability { NOT_RETRYABLE, RETRYABLE, UNCERTAIN_COMPLETION_OF_COMMIT } Retryability evaluateRetryability(Throwable thrown); } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/FailureCallback.java ================================================ /** * 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.work; import java.util.Map; /** * @author Baptiste Mesta. */ @FunctionalInterface public interface FailureCallback { void onFailure(WorkDescriptor workDescriptor, BonitaWork bonitaWork, Map context, Exception thrown); } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/LockException.java ================================================ /** * 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.work; public class LockException extends Exception { public LockException(String message, Exception e) { super(message, e); } public LockException(String message) { super(message); } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/LockTimeoutException.java ================================================ /** * 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.work; /** * @author Baptiste Mesta. */ public class LockTimeoutException extends LockException { public LockTimeoutException(String message) { super(message); } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/RetryingWorkExecutorService.java ================================================ /** * Copyright (C) 2020 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.work; import static java.lang.String.format; import static org.bonitasoft.engine.commons.ExceptionUtils.printLightWeightStacktrace; import static org.bonitasoft.engine.commons.ExceptionUtils.printRootCauseOnly; import java.time.Instant; import java.util.Map; import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import org.bonitasoft.engine.commons.time.EngineClock; import org.bonitasoft.engine.incident.Incident; import org.bonitasoft.engine.incident.IncidentService; import org.bonitasoft.engine.mdc.MDCHelper; import org.bonitasoft.engine.work.audit.WorkExecutionAuditor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; /** * @author Baptiste Mesta. */ @Component @ConditionalOnSingleCandidate(WorkExecutorService.class) public class RetryingWorkExecutorService implements WorkExecutorService, WorkExecutionCallback { Logger logger = LoggerFactory.getLogger(RetryingWorkExecutorService.class); public static final String NUMBER_OF_WORKS_RETRIED = "bonita.bpmengine.work.retried"; private final EngineClock engineClock; private final int maxRetry; private final WorkExecutionAuditor workExecutionAuditor; private int delay; private final double delayFactor; private final ExceptionRetryabilityEvaluator exceptionRetryabilityEvaluator; private final AtomicLong retriedWorks = new AtomicLong(); private final BonitaExecutorServiceFactory bonitaExecutorServiceFactory; private final long workTerminationTimeout; private BonitaExecutorService executor; private final IncidentService incidentService; public int numberOfFramesToLogInExceptions = 3; private final Random random = new Random(); public RetryingWorkExecutorService(BonitaExecutorServiceFactory bonitaExecutorServiceFactory, EngineClock engineClock, @Value("${bonita.tenant.work.terminationTimeout}") long workTerminationTimeout, @Value("${bonita.tenant.work.maxRetry}") int maxRetry, @Value("${bonita.tenant.work.retry.delay}") int delay, @Value("${bonita.tenant.work.retry.factor}") double delayFactor, ExceptionRetryabilityEvaluator exceptionRetryabilityEvaluator, WorkExecutionAuditor workExecutionAuditor, MeterRegistry meterRegistry, IncidentService incidentService) { this.bonitaExecutorServiceFactory = bonitaExecutorServiceFactory; this.engineClock = engineClock; this.workTerminationTimeout = workTerminationTimeout; this.maxRetry = maxRetry; this.delay = delay; this.delayFactor = delayFactor; this.exceptionRetryabilityEvaluator = exceptionRetryabilityEvaluator; this.workExecutionAuditor = workExecutionAuditor; this.incidentService = incidentService; Gauge.builder(NUMBER_OF_WORKS_RETRIED, retriedWorks, AtomicLong::get) .baseUnit("works") .description("Works currently waiting for execution that have been retried at least once") .register(meterRegistry); } @Value("${bonita.tenant.work.exceptionsNumberOfFrameToLog:3}") public void setNumberOfFramesToLogInExceptions(int numberOfFramesToLogInExceptions) { this.numberOfFramesToLogInExceptions = numberOfFramesToLogInExceptions; } @Override public void onSuccess(WorkDescriptor work) { if (work.getRetryCount() > 0) { logger.info("Work {} was successfully retried after {} tries.", work, work.getRetryCount()); retriedWorks.decrementAndGet(); } logger.debug("Completed work {}", work); workExecutionAuditor.notifySuccess(work); } @Override public void onFailure(WorkDescriptor work, BonitaWork bonitaWork, Map context, Throwable thrown) { MDCHelper.tryWithMDC(thrown, () -> { if (thrown instanceof LockException) { if (thrown instanceof LockTimeoutException) { //Can happen frequently, only log in debug logger.debug("Tried to execute the work, but it was unable to acquire a lock {}", work); } else { //Caused logger.warn("Tried to execute the work, but it was unable to acquire a lock {}", bonitaWork.getDescription(), thrown); } execute(work); return; } logger.debug("Work {} failed because of ", work, thrown); switch (exceptionRetryabilityEvaluator.evaluateRetryability(thrown)) { case NOT_RETRYABLE: if (thrown instanceof SWorkPreconditionException) { logger.warn("Work was not executed because preconditions were not met, {} : {}", bonitaWork.getDescription(), thrown.getMessage()); decrementRetryCounterIfNeeded(work); } else { logger.warn("Work {} failed. The element will be marked as failed. Exception is: {}", bonitaWork.getDescription(), printLightWeightStacktrace(thrown, numberOfFramesToLogInExceptions)); handleFailure(work, bonitaWork, context, thrown); } break; case UNCERTAIN_COMPLETION_OF_COMMIT: // Do the same as retryable but add a warning log logger.warn( "Work {} has failed and will be retried but the issue happened during the commit. We are uncertain that the " + "commit was really completed. If the retry fails with a SWorkPreconditionException it might indicate that " + "the work was already completed and the recovery mechanism will restart it. No manual action is required.", bonitaWork.getDescription()); case RETRYABLE: if (work.getRetryCount() < maxRetry) { long delayInMillis = getDelayInMillis(work.getRetryCount()); logger.warn( "Work {} failed because of {}. It will be retried. Attempt {} of {} with a delay of {} ms", bonitaWork.getDescription(), printRootCauseOnly(thrown), work.getRetryCount() + 1, maxRetry, delayInMillis); incrementRetryCounterIfNeeded(work); retry(work, delayInMillis); } else { logger.warn("Work {} failed. It has already been retried {} times. " + "No more retries will be attempted, it will be marked as failed. Exception is: {}", bonitaWork.getDescription(), maxRetry, printLightWeightStacktrace(thrown, numberOfFramesToLogInExceptions)); handleFailure(work, bonitaWork, context, thrown); } break; default: throw new IllegalStateException( "Unexpected value: " + exceptionRetryabilityEvaluator.evaluateRetryability(thrown)); } }); } public void handleFailure(WorkDescriptor work, BonitaWork bonitaWork, Map context, Throwable thrown) { decrementRetryCounterIfNeeded(work); try { bonitaWork.handleFailure(thrown, context); } catch (Exception e) { if (bonitaWork.canBeRecoveredByTheRecoveryMechanism()) { logger.warn("Work {} failed and we were not able to mark the element as failed. " + "However, the element can be recovered by the recovery mechanism, it will be recovered automatically " + "the next time the recovery is executed. We were not able to mark it as failed because of {} ", bonitaWork.getDescription(), printLightWeightStacktrace(e, numberOfFramesToLogInExceptions)); logger.debug("Unable to put the element as failed because:", e); } else { logger.warn( "Work {} failed and we were not able to mark the element as failed. An incident will be reported. " + "We were not able to mark it as failed because of {}", bonitaWork.getDescription(), printLightWeightStacktrace(e, numberOfFramesToLogInExceptions)); incidentService.report( new Incident(bonitaWork.getDescription(), bonitaWork.getRecoveryProcedure(), thrown, e)); } } } private void incrementRetryCounterIfNeeded(WorkDescriptor work) { if (work.getRetryCount() == 0) { retriedWorks.incrementAndGet(); } } private void decrementRetryCounterIfNeeded(WorkDescriptor work) { if (work.getRetryCount() > 0) { retriedWorks.decrementAndGet(); } } private void retry(WorkDescriptor work, long delayInMillis) { Instant mustBeExecutedAfter = engineClock.now().plusMillis(delayInMillis); work.incrementRetryCount(); work.mustBeExecutedAfter(mustBeExecutedAfter); execute(work); } private long getDelayInMillis(int retryCount) { //we multiply by the delay each time // first time is delay // second time is delay * delayFactor // third time is delay * delayFactor * delayFactor // and so on // Also add a random scatter delay to avoid retry "waves" // when several jobs fail & are retried at exactly the same time, causing further fails... // see RUNTIME-302 for more details double factor = Math.pow(delayFactor, retryCount); return Math.round((delay * factor) * (random.nextFloat() + 1)); } //For testing purpose int getDelay() { return delay; } //For testing purpose void setDelay(int delay) { this.delay = delay; } @Override public void execute(WorkDescriptor work) { if (!isStopped()) { logger.debug("Submitted work"); executor.submit(work); } else { logger.debug("Ignored work submission (service stopped) {}", work); } } @Override public synchronized void stop() { // we don't throw exception just stop it and log if something happens try { if (isStopped()) { return; } bonitaExecutorServiceFactory.unbind(); shutdownExecutor(); awaitTermination(); } catch (final SWorkException e) { if (e.getCause() != null) { logger.warn(e.getMessage(), e.getCause()); } else { logger.warn(e.getMessage()); } } } @Override public synchronized void start() { if (isStopped()) { executor = bonitaExecutorServiceFactory.createExecutorService(this); } } @Override public synchronized void pause() throws SWorkException { if (isStopped()) { return; } bonitaExecutorServiceFactory.unbind(); shutdownExecutor(); // completely clear the queue because it's a global pause executor.clearAllQueues(); awaitTermination(); } @Override public synchronized void resume() { start(); } private void awaitTermination() throws SWorkException { try { if (!executor.awaitTermination(workTerminationTimeout, TimeUnit.SECONDS)) { throw new SWorkException(format("Waited termination of all work %ds but all tasks were not finished", workTerminationTimeout)); } } catch (final InterruptedException e) { throw new SWorkException("Interrupted while stopping the work service", e); } executor = null; } private void shutdownExecutor() { executor.shutdownAndEmptyQueue(); logger.info("Stopped executor service"); } public boolean isStopped() { return executor == null; } @Override public void notifyNodeStopped(String nodeName) { } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/SWorkException.java ================================================ /** * 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.work; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Charles Souillard * @author Baptiste Mesta * @author Celine Souchet */ public class SWorkException extends SBonitaException { private static final long serialVersionUID = 1L; public SWorkException(final String message, final Throwable t) { super(message, t); } public SWorkException(final Throwable t) { super(t); } public SWorkException(final String message) { super(message); } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/SWorkPreconditionException.java ================================================ /** * 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.work; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * Happens when a work could not be executed because its preconditions where not met. * e.g. NotifyChildFinishWork is executed on an unexisting or not finished flow node */ public class SWorkPreconditionException extends SBonitaException { public SWorkPreconditionException(String message, Exception cause) { super(message, cause); } public SWorkPreconditionException(String message) { super(message); } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/SWorkRegisterException.java ================================================ /** * 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.work; import org.bonitasoft.engine.commons.exceptions.SBonitaException; /** * @author Charles Souillard * @author Baptiste Mesta * @author Celine Souchet */ public class SWorkRegisterException extends SBonitaException { private static final long serialVersionUID = 1L; public SWorkRegisterException(final String message, final Throwable t) { super(message, t); } /** * @param string */ public SWorkRegisterException(final String message) { super(message); } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/SuccessCallback.java ================================================ /** * 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.work; /** * @author Baptiste Mesta. */ @FunctionalInterface public interface SuccessCallback { void onSuccess(WorkDescriptor workDescriptor); } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkDescriptor.java ================================================ /** * 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.work; import static org.apache.commons.lang3.builder.ToStringStyle.SHORT_PREFIX_STYLE; import java.io.Serializable; import java.time.Instant; import java.util.HashMap; import java.util.Map; import java.util.UUID; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; /** * @author Baptiste Mesta. */ public class WorkDescriptor implements Serializable { private final String uuid = UUID.randomUUID().toString(); private final String type; private final Map parameters; private int retryCount = 0; private Instant executionThreshold; private int executionCount = 0; private Instant registrationDate; private boolean abnormalExecutionDetected = false; public WorkDescriptor(String type) { this.type = type; this.parameters = new HashMap<>(); } public String getUuid() { return uuid; } public String getType() { return type; } public Serializable getParameter(String key) { if (!parameters.containsKey(key)) { throw new IllegalStateException( String.format("Parameter %s is not set on the work descriptor %s", key, this)); } return parameters.get(key); } public Long getLong(String key) { return (Long) getParameter(key); } public Integer getInteger(String key) { return (Integer) getParameter(key); } public Boolean getBoolean(String key) { return (Boolean) getParameter(key); } public String getString(String key) { return (String) getParameter(key); } public static WorkDescriptor create(String type) { return new WorkDescriptor(type); } public WorkDescriptor withParameter(String key, Serializable value) { parameters.put(key, value); return this; } public Instant getExecutionThreshold() { return executionThreshold; } public WorkDescriptor mustBeExecutedAfter(Instant mustBeExecutedAfter) { this.executionThreshold = mustBeExecutedAfter; return this; } public int getRetryCount() { return retryCount; } public void incrementRetryCount() { retryCount++; } public int getExecutionCount() { return executionCount; } public void incrementExecutionCount() { executionCount++; } public void setRegistrationDate(Instant registrationDate) { this.registrationDate = registrationDate; } public Instant getRegistrationDate() { return registrationDate; } public WorkDescriptor abnormalExecutionDetected() { this.abnormalExecutionDetected = true; return this; } public boolean isAbnormalExecutionDetected() { return abnormalExecutionDetected; } public String getDescription() { return new ToStringBuilder(this, SHORT_PREFIX_STYLE) .append(type) .append("parameters", parameters).toString(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; WorkDescriptor that = (WorkDescriptor) o; return new EqualsBuilder() .append(retryCount, that.retryCount) .append(uuid, that.uuid) .append(type, that.type) .append(parameters, that.parameters) .append(executionThreshold, that.executionThreshold) .append(executionCount, that.executionCount) .append(registrationDate, that.registrationDate) .append(abnormalExecutionDetected, that.abnormalExecutionDetected) .isEquals(); } @Override public int hashCode() { return new HashCodeBuilder(17, 37) .append(uuid) .append(type) .append(parameters) .append(retryCount) .append(executionThreshold) .append(executionCount) .append(registrationDate) .append(abnormalExecutionDetected) .toHashCode(); } @Override public String toString() { return new ToStringBuilder(this, SHORT_PREFIX_STYLE) .append("uuid", uuid) .append("type", type) .append("parameters", parameters) .append("retryCount", retryCount) .append("executionThreshold", executionThreshold) .append("executionCount", executionCount) .append("registrationDate", registrationDate) .append("abnormalExecutionDetected", abnormalExecutionDetected) .toString(); } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkExecutionCallback.java ================================================ /** * 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.work; import java.util.Map; /** * this a callback called when a work finish. * * @author Baptiste Mesta. */ public interface WorkExecutionCallback { void onSuccess(WorkDescriptor workDescriptor); void onFailure(WorkDescriptor work, BonitaWork bonitaWork, Map context, Throwable thrown); } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkExecutorService.java ================================================ /** * 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.work; import org.bonitasoft.engine.commons.TenantLifecycleService; /** * Trigger the asynchronous execution of a work * * @author Baptiste Mesta */ public interface WorkExecutorService extends TenantLifecycleService { boolean isStopped(); void notifyNodeStopped(String nodeName); void execute(WorkDescriptor work); } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkFactory.java ================================================ /** * 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.work; /** * @author Baptiste Mesta. */ public interface WorkFactory { BonitaWork create(WorkDescriptor workDescriptor); } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkService.java ================================================ /** * 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.work; import org.bonitasoft.engine.commons.TenantLifecycleService; /** * This service allows register the execution of a work at the end of the current transaction * * @author Charles Souillard * @author Baptiste Mesta * @author Matthieu Chaffotte */ public interface WorkService extends TenantLifecycleService { /** * This operation MUST be called within an active transaction. If no active transaction is found, a * SWorkRegisterException is thrown * * @param workDescriptor * @throws SWorkRegisterException * @since 6.0 */ void registerWork(WorkDescriptor workDescriptor) throws SWorkRegisterException; /** * @return true if the work service is stopped * @since 6.3 */ boolean isStopped(); } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkServiceImpl.java ================================================ /** * 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.work; import org.bonitasoft.engine.commons.time.EngineClock; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.transaction.STransactionNotFoundException; import org.bonitasoft.engine.transaction.UserTransactionService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; /** * Directly calls the WorkExecutorService * * @author Charles Souillard * @author Baptiste Mesta * @author Celine Souchet */ @Service("workService") public class WorkServiceImpl implements WorkService { private final Logger log = LoggerFactory.getLogger(WorkServiceImpl.class); private final UserTransactionService transactionService; private final SessionAccessor sessionAccessor; private final WorkExecutorService workExecutorService; private final EngineClock engineClock; private int workDelayOnMultipleXAResource; public WorkServiceImpl(UserTransactionService transactionService, SessionAccessor sessionAccessor, WorkExecutorService workExecutorService, EngineClock engineClock, @Value("${bonita.tenant.work.${db.vendor}.delayOnMultipleXAResource:0}") int workDelayOnMultipleXAResource) { this.transactionService = transactionService; this.sessionAccessor = sessionAccessor; this.workExecutorService = workExecutorService; this.engineClock = engineClock; this.workDelayOnMultipleXAResource = workDelayOnMultipleXAResource; } @Override public void registerWork(WorkDescriptor workDescriptor) throws SWorkRegisterException { if (isStopped()) { log.warn("Tried to register work {}, but the work service is stopped.", workDescriptor); return; } workDescriptor.setRegistrationDate(engineClock.now()); log.debug("Registering work {}", workDescriptor); createAndRegisterNewSynchronization(workDescriptor); log.debug("Work registered"); } private WorkSynchronization createAndRegisterNewSynchronization(WorkDescriptor workDescriptor) throws SWorkRegisterException { WorkSynchronization synchro = new WorkSynchronization(transactionService, workExecutorService, workDescriptor, workDelayOnMultipleXAResource); try { transactionService.registerBonitaSynchronization(synchro); } catch (final STransactionNotFoundException e) { throw new SWorkRegisterException(e.getMessage(), e); } return synchro; } @Override public boolean isStopped() { // the executor must handle elements when it's shutting down return workExecutorService.isStopped(); } @Override public synchronized void stop() { } @Override public synchronized void start() { if (workDelayOnMultipleXAResource > 0) { log.info( "The property 'delayOnMultipleXAResource' is set to {} ms, that delay will be added when triggering works from a transaction having multiple XA resources. This is the case when the BDM is updated.", workDelayOnMultipleXAResource); } } @Override public synchronized void resume() { start(); } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkSingleThreadPoolExecutorFactory.java ================================================ /** * 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.work; import java.util.concurrent.*; import org.bonitasoft.engine.mdc.MDCTransmitingThreadPoolExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.stereotype.Component; @Component @ConditionalOnSingleCandidate(BonitaWorkExecutorFactory.class) public class WorkSingleThreadPoolExecutorFactory implements BonitaWorkExecutorFactory { private final int queueCapacity; public WorkSingleThreadPoolExecutorFactory( @Value("${bonita.tenant.work.queueCapacity}") int queueCapacity) { this.queueCapacity = queueCapacity; } @Override public ThreadPoolExecutor create() { return new SingleThreadPoolExecutor(new ArrayBlockingQueue<>(queueCapacity), new WorkerThreadFactory("Bonita-Worker")); } public static class SingleThreadPoolExecutor extends MDCTransmitingThreadPoolExecutor { public SingleThreadPoolExecutor(final BlockingQueue workQueue, final ThreadFactory threadFactory) { super(1, 1, 0, TimeUnit.MILLISECONDS, workQueue, threadFactory, new QueueRejectedExecutionHandler()); } @Override public Future submit(final Runnable task) { // only submit if not shutdown if (!isShutdown()) { return super.submit(task); } return null; } } public static class QueueRejectedExecutionHandler implements RejectedExecutionHandler { private static final Logger logger = LoggerFactory.getLogger(QueueRejectedExecutionHandler.class); @Override public void rejectedExecution(final Runnable task, final ThreadPoolExecutor executor) { if (executor.isShutdown()) { logger.info( "Tried to run work {} but the work service is shutdown. work will be restarted with the node", task); } else { throw new RejectedExecutionException( "Unable to run the task " + task + "\n your work queue is full you might consider changing your configuration to scale more. See parameter 'bonita.tenant.work.queueCapacity' in bonita-tenant-community.properties configuration files."); } } } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkSynchronization.java ================================================ /** * 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.work; import java.time.Instant; import javax.transaction.Status; import org.bonitasoft.engine.transaction.BonitaTransactionSynchronization; import org.bonitasoft.engine.transaction.UserTransactionService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class WorkSynchronization implements BonitaTransactionSynchronization { private static final Logger LOG = LoggerFactory.getLogger(WorkSynchronization.class); private final WorkDescriptor work; private final WorkExecutorService workExecutorService; private final UserTransactionService transactionService; private final int workDelayOnMultipleXAResource; WorkSynchronization(final UserTransactionService transactionService, final WorkExecutorService workExecutorService, WorkDescriptor work, int workDelayOnMultipleXAResource) { this.transactionService = transactionService; this.workDelayOnMultipleXAResource = workDelayOnMultipleXAResource; this.work = work; this.workExecutorService = workExecutorService; } WorkDescriptor getWork() { return work; } @Override public void afterCompletion(final int transactionStatus) { if (Status.STATUS_COMMITTED == transactionStatus) { if (workDelayOnMultipleXAResource > 0) { transactionService.hasMultipleResources().ifPresentOrElse( hasMultiple -> { if (Boolean.TRUE.equals(hasMultiple)) { work.mustBeExecutedAfter(Instant.now().plusMillis(workDelayOnMultipleXAResource)); } }, // to be safe, if we are unable to know if there are multiple resources, we add the delay anyway. () -> work.mustBeExecutedAfter(Instant.now().plusMillis(workDelayOnMultipleXAResource))); } workExecutorService.execute(work); } else { LOG.debug("Transaction completion with state {} != COMMITTED. Not triggering the work: {}", transactionStatus, work); } } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/WorkerThreadFactory.java ================================================ /** * 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.work; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; /** * @author Baptiste Mesta * @author Celine Souchet */ public class WorkerThreadFactory implements ThreadFactory { private final AtomicInteger nbThread = new AtomicInteger(1); private final String name; private final int padding; public WorkerThreadFactory(final String name, final int maximumPoolSize) { this.name = name; this.padding = guessPadding(maximumPoolSize); } public WorkerThreadFactory(final String name) { this(name, 1); } /** * @param maximumPoolSize */ static int guessPadding(final int maximumPoolSize) { int tmpPadding = 0; int poolSize = maximumPoolSize; while (poolSize > 0) { poolSize /= 10; tmpPadding++; } return tmpPadding; } @Override public Thread newThread(final Runnable runnable) { final StringBuilder builder = new StringBuilder(); builder.append(name); builder.append("-"); builder.append("%0"); builder.append(padding); builder.append("d"); final String format = String.format(builder.toString(), nbThread.getAndIncrement()); return new Thread(runnable, format); } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/audit/AuditListener.java ================================================ /** * 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.work.audit; import org.bonitasoft.engine.work.WorkDescriptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * Default implementation backed to a slf4j logger. */ @Component public class AuditListener { private static final Logger WORK_AUDIT = LoggerFactory.getLogger("BONITA_WORK_AUDIT.EXECUTION"); public void detectionStarted(WorkDescriptor work) { WORK_AUDIT.debug("Start detection for execution #{} of {}", work.getExecutionCount(), work); } public void abnormalExecutionStatusDetected(WorkDescriptor work, ExecutionStatus executionStatus) { WORK_AUDIT.warn("Potential abnormal execution detected - cause {}. {}", executionStatus, work); } public void success(WorkDescriptor work) { WORK_AUDIT.info("Work successfully executed. {}", work); } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/audit/ExecutionStatus.java ================================================ /** * 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.work.audit; public enum ExecutionStatus { OK(true), TOO_MANY_EXECUTIONS(false), TOO_MUCH_TIME_ELAPSED_SINCE_REGISTRATION(false); private final boolean isNormalExecution; ExecutionStatus(boolean isNormalExecution) { this.isNormalExecution = isNormalExecution; } public boolean isNormalExecution() { return isNormalExecution; } } ================================================ FILE: services/bonita-work/src/main/java/org/bonitasoft/engine/work/audit/WorkExecutionAuditor.java ================================================ /** * 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.work.audit; import static org.bonitasoft.engine.work.audit.ExecutionStatus.*; import java.time.Duration; import java.time.temporal.ChronoUnit; import org.bonitasoft.engine.commons.time.EngineClock; import org.bonitasoft.engine.work.WorkDescriptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class WorkExecutionAuditor { private static final Logger log = LoggerFactory.getLogger(WorkExecutionAuditor.class); private final EngineClock engineClock; private final AuditListener auditListener; private boolean activated = true; private final int executionCountThreshold; private final Duration executionCountDurationThreshold; private final Duration registrationDurationElapsedThreshold; public WorkExecutionAuditor(EngineClock engineClock, AuditListener auditListener, RegistrationDurationElapsedCheckConfig registrationDurationElapsedCheckConfig, ExecutionCountCheckConfig executionCountCheckConfig) { this.engineClock = engineClock; this.auditListener = auditListener; this.registrationDurationElapsedThreshold = registrationDurationElapsedCheckConfig.duration; this.executionCountThreshold = executionCountCheckConfig.executionCountThreshold; this.executionCountDurationThreshold = executionCountCheckConfig.executionDurationThreshold; } @Value("${bonita.tenant.work.audit.activated:true}") public void setActivated(boolean activated) { this.activated = activated; } /** * Notify the listener in case of abnormal execution. Cases considered as 'abnormal execution' *
        *
      • large number of executions after a duration threshold since registration has elapsed
      • *
      • large duration since the work has been registered
      • *
      * NOTE: the listener receives the 'abnormal execution' status only once to avoid flooding it. * * @param work the work descriptor to inspect */ public void detectAbnormalExecutionAndNotify(WorkDescriptor work) { if (!activated) { return; } auditListener.detectionStarted(work); if (work.getRegistrationDate() == null) { log.warn("No registration date available, unable to detect abnormal work execution status. {}", work); return; } if (!work.isAbnormalExecutionDetected()) { final ExecutionStatus executionStatus = executionStatus(work); if (!executionStatus.isNormalExecution()) { work.abnormalExecutionDetected(); auditListener.abnormalExecutionStatusDetected(work, executionStatus); } } } /** * Only notify if execution has been detected as abnormal */ public void notifySuccess(WorkDescriptor work) { if (work.isAbnormalExecutionDetected()) { auditListener.success(work); } } // Visible for Testing ExecutionStatus executionStatus(WorkDescriptor work) { Duration durationSinceWorkRegistration = Duration.between(work.getRegistrationDate(), engineClock.now()); if (durationSinceWorkRegistration.compareTo(executionCountDurationThreshold) >= 0 && work.getExecutionCount() >= executionCountThreshold) { return TOO_MANY_EXECUTIONS; } if (durationSinceWorkRegistration.compareTo(registrationDurationElapsedThreshold) >= 0) { return TOO_MUCH_TIME_ELAPSED_SINCE_REGISTRATION; } return OK; } @Component public static class RegistrationDurationElapsedCheckConfig { private final Duration duration; public RegistrationDurationElapsedCheckConfig( @Value("${bonita.tenant.work.audit.abnormal.execution.threshold.elapsed_duration_since_registration_amount:30}") int amount, @Value("${bonita.tenant.work.audit.abnormal.execution.threshold.elapsed_duration_since_registration_unit:MINUTES}") ChronoUnit unit) { this.duration = Duration.of(amount, unit); } } @Component public static class ExecutionCountCheckConfig { private final int executionCountThreshold; private final Duration executionDurationThreshold; public ExecutionCountCheckConfig( @Value("${bonita.tenant.work.audit.abnormal.execution.threshold.execution_count:10}") int executionCountThreshold, @Value("${bonita.tenant.work.audit.abnormal.execution.threshold.execution_count_duration_amount:10}") int durationThresholdAmount, @Value("${bonita.tenant.work.audit.abnormal.execution.threshold.execution_count_duration_unit:MINUTES}") ChronoUnit durationThresholdUnit) { this.executionCountThreshold = executionCountThreshold; this.executionDurationThreshold = Duration.of(durationThresholdAmount, durationThresholdUnit); } } } ================================================ FILE: services/bonita-work/src/test/java/org/bonitasoft/engine/work/DefaultBonitaExecutorServiceFactoryTest.java ================================================ /** * 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.work; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.bonitasoft.engine.commons.time.DefaultEngineClock; import org.bonitasoft.engine.monitoring.DefaultExecutorServiceMetricsProvider; import org.bonitasoft.engine.work.audit.WorkExecutionAuditor; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class DefaultBonitaExecutorServiceFactoryTest { @Mock private WorkFactory workFactory; @Mock private WorkExecutionCallback workExecutionCallback; @Test public void createExecutorService_should_register_ExecutorServiceMetrics() { // given: final SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry(); DefaultBonitaExecutorServiceFactory defaultBonitaExecutorServiceFactory = new DefaultBonitaExecutorServiceFactory( meterRegistry, new DefaultEngineClock(), workFactory, mock(WorkExecutionAuditor.class), new DefaultExecutorServiceMetricsProvider(), new WorkSingleThreadPoolExecutorFactory(10)); // when: defaultBonitaExecutorServiceFactory.createExecutorService(workExecutionCallback); // then: assertThat( meterRegistry.find("executor.pool.size") .tag("name", "bonita-work-executor") .gauge()) .isNotNull(); } @Test public void should_not_have_metrics_when_unbind_is_called() { // given: final SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry(); DefaultBonitaExecutorServiceFactory defaultBonitaExecutorServiceFactory = new DefaultBonitaExecutorServiceFactory( meterRegistry, new DefaultEngineClock(), workFactory, mock(WorkExecutionAuditor.class), new DefaultExecutorServiceMetricsProvider(), new WorkSingleThreadPoolExecutorFactory(10)); // when: defaultBonitaExecutorServiceFactory.createExecutorService(workExecutionCallback); defaultBonitaExecutorServiceFactory.unbind(); // then: assertThat( meterRegistry.find("executor.pool.size") .tag("name", "bonita-work-executor") .gauge()) .isNull(); } } ================================================ FILE: services/bonita-work/src/test/java/org/bonitasoft/engine/work/DefaultBonitaExecutorServiceTest.java ================================================ /** * Copyright (C) 2017-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.work; import static java.time.temporal.ChronoUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import java.time.Duration; import java.time.Instant; import java.util.Map; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.bonitasoft.engine.commons.time.FixedEngineClock; import org.bonitasoft.engine.work.audit.WorkExecutionAuditor; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; /** * @author Baptiste Mesta. */ public class DefaultBonitaExecutorServiceTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Mock private WorkExecutionAuditor workExecutionAuditor; private final MyWorkExecutionCallback workExecutionCallback = new MyWorkExecutionCallback(); private DefaultBonitaExecutorService bonitaExecutorService; private final FixedEngineClock engineClock = new FixedEngineClock(Instant.now()); private final WorkFactory workFactory = new LocalWorkFactory(2); private final SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry( // So that micrometer updates its counters every 1 ms: k -> k.equals("simple.step") ? Duration.ofMillis(1).toString() : null, Clock.SYSTEM); @Before public void before() { var threadPoolExecutor = new WorkSingleThreadPoolExecutorFactory.SingleThreadPoolExecutor( new LinkedBlockingQueue<>(10), new WorkerThreadFactory("test-worker", 1)); bonitaExecutorService = new DefaultBonitaExecutorService(threadPoolExecutor, workFactory, engineClock, workExecutionCallback, workExecutionAuditor, meterRegistry); } @Test public void should_call_on_success_callback_when_work_executed_properly() { WorkDescriptor workDescriptor = WorkDescriptor.create("NORMAL"); bonitaExecutorService.submit(workDescriptor); await().until(workExecutionCallback::isOnSuccessCalled); assertThat(workExecutionCallback.isOnFailureCalled()).isFalse(); } @Test public void should_call_on_failure_callback_when_work_executed_properly() { WorkDescriptor workDescriptor = WorkDescriptor.create("EXCEPTION"); bonitaExecutorService.submit(workDescriptor); await().until(workExecutionCallback::isOnFailureCalled); assertThat(workExecutionCallback.isOnSuccessCalled()).isFalse(); } @Test public void should_execute_work_after_specified_date() { WorkDescriptor workDescriptor = WorkDescriptor.create("NORMAL"); workDescriptor.mustBeExecutedAfter(Instant.now().plus(5, SECONDS)); bonitaExecutorService.submit(workDescriptor); //should still not be executed engineClock.addTime(1, SECONDS); await().atLeast(50, TimeUnit.MILLISECONDS) .untilAsserted(() -> assertThat(workExecutionCallback.isOnSuccessCalled()).isFalse()); //add time, work should be executed engineClock.addTime(5, SECONDS); await().until(workExecutionCallback::isOnSuccessCalled); } @Test public void should_update_meter_when_work_executes() { Gauge currentWorkQueue = meterRegistry.find(DefaultBonitaExecutorService.NUMBER_OF_WORKS_PENDING).gauge(); for (int i = 0; i <= 4; i++) { WorkDescriptor workDescriptor = WorkDescriptor.create("SLEEP"); bonitaExecutorService.submit(workDescriptor); } await().untilAsserted(() -> assertThat(currentWorkQueue.value()).isPositive()); } @Test public void should_update_works_counters_when_enqueuing_workDescriptor_with_long_processing_time() throws Exception { //when: bonitaExecutorService.submit(WorkDescriptor.create("NORMAL")); bonitaExecutorService.submit(WorkDescriptor.create("SLEEP")); bonitaExecutorService.submit(WorkDescriptor.create("SLEEP")); bonitaExecutorService.submit(WorkDescriptor.create("SLEEP")); bonitaExecutorService.submit(WorkDescriptor.create("SLEEP")); bonitaExecutorService.submit(WorkDescriptor.create("NORMAL")); TimeUnit.MILLISECONDS.sleep(50); // give some time to the executor to process the work //then: // 1 executed because normal work is submitted first. assertThat(meterRegistry.find(DefaultBonitaExecutorService.NUMBER_OF_WORKS_EXECUTED).counter().count()) .as("Executed works number").isEqualTo(1); // 1 running works because we have 1 threads in the pool and sleeping works wait for 2s to execute: assertThat(meterRegistry.find(DefaultBonitaExecutorService.NUMBER_OF_WORKS_RUNNING).gauge().value()) .as("Running works number").isEqualTo(1); // 4 pending works because the single thread is busy, so the last 4 works are in the queue: assertThat(meterRegistry.find(DefaultBonitaExecutorService.NUMBER_OF_WORKS_PENDING).gauge().value()) .as("Pending works number").isEqualTo(4); } @Test public void should_have_no_meters_after_shutdown() { var work = bonitaExecutorService.submit(WorkDescriptor.create("NORMAL")); await().until(work::isDone); bonitaExecutorService.shutdownAndEmptyQueue(); assertThat(meterRegistry.getMeters()).isEmpty(); } @Test public void should_call_on_success_callback_only_when_async_work_executed_properly() { WorkDescriptor workDescriptor = WorkDescriptor.create("ASYNC"); var work = bonitaExecutorService.submit(workDescriptor); await().until(work::isDone); assertThat(workExecutionCallback.isOnSuccessCalled()).isFalse(); assertThat(workExecutionCallback.isOnFailureCalled()).isFalse(); await().until(workExecutionCallback::isOnSuccessCalled); assertThat(workExecutionCallback.isOnFailureCalled()).isFalse(); } @Test public void should_call_on_failure_callback_ony_when_async_work_executed_properly() { WorkDescriptor workDescriptor = WorkDescriptor.create("ASYNC_EXCEPTION"); var work = bonitaExecutorService.submit(workDescriptor); await().until(work::isDone); assertThat(workExecutionCallback.isOnSuccessCalled()).isFalse(); assertThat(workExecutionCallback.isOnFailureCalled()).isFalse(); await().until(workExecutionCallback::isOnFailureCalled); assertThat(workExecutionCallback.isOnSuccessCalled()).isFalse(); assertThat(workExecutionCallback.getThrown()).hasMessage("my exception").isInstanceOf(SWorkException.class); } // ================================================================================================================= // UTILS // ================================================================================================================= private static class MyWorkExecutionCallback implements WorkExecutionCallback { private final AtomicBoolean onSuccessCalled = new AtomicBoolean(false); private final AtomicBoolean onFailureCalled = new AtomicBoolean(false); private Throwable thrown; @Override public void onSuccess(WorkDescriptor workDescriptor) { onSuccessCalled.set(true); } @Override public void onFailure(WorkDescriptor work, BonitaWork bonitaWork, Map context, Throwable thrown) { this.thrown = thrown; onFailureCalled.set(true); } public boolean isOnSuccessCalled() { return onSuccessCalled.get(); } public boolean isOnFailureCalled() { return onFailureCalled.get(); } public Throwable getThrown() { return thrown; } } private record LocalWorkFactory(long workSleepPeriodInSeconds) implements WorkFactory { @Override public BonitaWork create(WorkDescriptor workDescriptor) { return new BonitaWork() { @Override public String getDescription() { return workDescriptor.toString(); } @Override public CompletableFuture work(Map context) throws Exception { switch (workDescriptor.getType()) { case "EXCEPTION": throw new Exception("classic exception"); case "SLEEP": TimeUnit.SECONDS.sleep(workSleepPeriodInSeconds); break; case "ASYNC": return CompletableFuture.supplyAsync(() -> { try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException ignored) { } return null; }, Executors.newSingleThreadExecutor()); case "ASYNC_EXCEPTION": return CompletableFuture.supplyAsync(() -> { try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException ignored) { } throw new CompletionException(new SWorkException("my exception")); }, Executors.newSingleThreadExecutor()); case "NORMAL": default: } return CompletableFuture.completedFuture(null); } @Override public void handleFailure(Throwable e, Map context) { // do nothing } }; } } } ================================================ FILE: services/bonita-work/src/test/java/org/bonitasoft/engine/work/DefaultExceptionRetryabilityEvaluatorTest.java ================================================ /** * Copyright (C) 2020 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.work; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.work.ExceptionRetryabilityEvaluator.Retryability.NOT_RETRYABLE; import static org.bonitasoft.engine.work.ExceptionRetryabilityEvaluator.Retryability.RETRYABLE; import static org.bonitasoft.engine.work.ExceptionRetryabilityEvaluator.Retryability.UNCERTAIN_COMPLETION_OF_COMMIT; import java.io.IOException; import java.net.PortUnreachableException; import java.sql.SQLException; import java.sql.SQLTransientException; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import javax.transaction.xa.XAException; import org.bonitasoft.engine.commons.exceptions.SRetryableException; import org.bonitasoft.engine.transaction.STransactionCommitException; import org.junit.Test; import org.springframework.jdbc.CannotGetJdbcConnectionException; /** * @author Baptiste Mesta. */ public class DefaultExceptionRetryabilityEvaluatorTest { private DefaultExceptionRetryabilityEvaluator defaultExceptionRetryabilityEvaluator; @Test public void should_be_retryable_if_exception_is_retryable() { initWith(SRetryableException.class); ExceptionRetryabilityEvaluator.Retryability shouldBeRetried = defaultExceptionRetryabilityEvaluator .evaluateRetryability(new SRetryableException(new IllegalStateException("a retryable exception"))); assertThat(shouldBeRetried).isEqualTo(RETRYABLE); } @Test public void should_be_retryable_if_some_exception_in_hierarchy_is_retryable() { initWith(SRetryableException.class); ExceptionRetryabilityEvaluator.Retryability shouldBeRetried = defaultExceptionRetryabilityEvaluator .evaluateRetryability(new IllegalStateException( (new SRetryableException(new IllegalStateException("a retryable exception"))))); assertThat(shouldBeRetried).isEqualTo(RETRYABLE); } private static class MySRetryableException extends SRetryableException { public MySRetryableException() { super(new IllegalArgumentException()); } } @Test public void should_be_retryable_if_some_exception_is_extending_a_retryable() { initWith(SRetryableException.class); SRetryableException myRetryable = new MySRetryableException(); ExceptionRetryabilityEvaluator.Retryability shouldBeRetried = defaultExceptionRetryabilityEvaluator .evaluateRetryability(new IllegalStateException( (myRetryable))); assertThat(shouldBeRetried).isEqualTo(RETRYABLE); } @Test public void should_be_retryable_if_exception_is_retryable_with_multiple_exception_in_configuration() { initWith(SRetryableException.class, PortUnreachableException.class); ExceptionRetryabilityEvaluator.Retryability shouldBeRetried = defaultExceptionRetryabilityEvaluator .evaluateRetryability(new IllegalStateException( (new PortUnreachableException("unreached port")))); assertThat(shouldBeRetried).isEqualTo(RETRYABLE); } @Test public void should_be_not_retryable_if_exception_is_not_a_retryable() { initWith(SRetryableException.class); ExceptionRetryabilityEvaluator.Retryability shouldBeRetried = defaultExceptionRetryabilityEvaluator .evaluateRetryability(new IllegalStateException("a retryable exception")); assertThat(shouldBeRetried).isEqualTo(NOT_RETRYABLE); } @Test(expected = IllegalArgumentException.class) public void should_constructor_fail_if_classname_do_not_exists() { new DefaultExceptionRetryabilityEvaluator(Collections.singletonList("unkown class"), Collections.emptyList()); } @Test(expected = IllegalArgumentException.class) public void should_constructor_fail_if_class_is_not_throwable() { new DefaultExceptionRetryabilityEvaluator(Collections.singletonList("java.util.List"), Collections.emptyList()); } @Test public void should_be_not_retryable_if_contains_retryable_exception_with_caused_by_blacklisted_exception() { initWithBlackListed(SRetryableException.class, IllegalStateException.class); ExceptionRetryabilityEvaluator.Retryability shouldBeRetried = defaultExceptionRetryabilityEvaluator .evaluateRetryability(new SRetryableException(new IllegalStateException())); assertThat(shouldBeRetried).isEqualTo(NOT_RETRYABLE); } @Test public void should_be_not_retryable_if_contains_blacklisted_exception_even_if_cause_is_retryable() { initWithBlackListed(SRetryableException.class, IllegalStateException.class); ExceptionRetryabilityEvaluator.Retryability shouldBeRetried = defaultExceptionRetryabilityEvaluator .evaluateRetryability(new IllegalStateException(new SRetryableException(new IOException()))); assertThat(shouldBeRetried).isEqualTo(NOT_RETRYABLE); } @Test public void should_retry_sql_connection_exceptions() { initWith(SRetryableException.class); assertThat(defaultExceptionRetryabilityEvaluator .evaluateRetryability(new IllegalStateException(new SQLException("connection issue")))) .isEqualTo(RETRYABLE); assertThat(defaultExceptionRetryabilityEvaluator .evaluateRetryability(new CannotGetJdbcConnectionException("I/O error"))).isEqualTo(RETRYABLE); } @Test public void should_be_UNCERTAIN_COMPLETION_OF_COMMIT_when_retryable_exception_happen_on_commit() { initWith(SRetryableException.class); assertThat(defaultExceptionRetryabilityEvaluator .evaluateRetryability(new STransactionCommitException(new SQLException("connection issue")))) .isEqualTo(UNCERTAIN_COMPLETION_OF_COMMIT); } @Test public void should_not_be_UNCERTAIN_COMPLETION_OF_COMMIT_when_retryable_exception_happen_on_commit_but_wat_not_connection_issue() { initWith(SRetryableException.class); assertThat(defaultExceptionRetryabilityEvaluator.evaluateRetryability( new STransactionCommitException(new SRetryableException(new IOException("some file issue"))))) .isEqualTo(RETRYABLE); } @Test public void should_retry_retryable_xa_exceptions() { initWithBlackListed(SRetryableException.class, IllegalStateException.class); assertThat( defaultExceptionRetryabilityEvaluator.evaluateRetryability(new XAException(XAException.XA_RBTIMEOUT))) .isEqualTo(RETRYABLE); } @Test public void should_retry_when_exception_is_nested() { initWithBlackListed(SQLTransientException.class, IllegalStateException.class); assertThat(defaultExceptionRetryabilityEvaluator .evaluateRetryability(new ExceptionThatHasNested(new SQLTransientException()))).isEqualTo(RETRYABLE); } @SafeVarargs private final void initWith(Class... throwables) { defaultExceptionRetryabilityEvaluator = new DefaultExceptionRetryabilityEvaluator(toListNames(throwables), Collections.emptyList()); } @SafeVarargs private final void initWithBlackListed(Class toRetry, Class... blackListed) { defaultExceptionRetryabilityEvaluator = new DefaultExceptionRetryabilityEvaluator( toListNames(toRetry), toListNames(blackListed)); } private List toListNames(Class... blackListed) { return Arrays.asList(blackListed).stream().map(Class::getName).collect(Collectors.toList()); } class ExceptionThatHasNested extends Exception { List exceptions; ExceptionThatHasNested(Throwable... exceptions) { super("exception with nested"); this.exceptions = Arrays.asList(exceptions); } public List getExceptions() { return exceptions; } } } ================================================ FILE: services/bonita-work/src/test/java/org/bonitasoft/engine/work/RetryingWorkExecutorServiceTest.java ================================================ /** * Copyright (C) 2020 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.work; import static java.util.Collections.emptyMap; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.work.ExceptionRetryabilityEvaluator.Retryability.*; import static org.bonitasoft.engine.work.RetryingWorkExecutorService.NUMBER_OF_WORKS_RETRIED; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.time.Duration; import java.time.Instant; import java.util.Collections; import java.util.Map; import java.util.concurrent.TimeUnit; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SRetryableException; import org.bonitasoft.engine.commons.time.FixedEngineClock; import org.bonitasoft.engine.incident.IncidentService; import org.bonitasoft.engine.transaction.STransactionCommitException; import org.bonitasoft.engine.work.audit.WorkExecutionAuditor; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; /** * @author Baptiste Mesta. */ public class RetryingWorkExecutorServiceTest { private static final int MAX_RETRY = 4; private static final int WORK_TERMINATION_TIMEOUT = 30; private static final int DELAY = 1000; private static final int DELAY_FACTOR = 2; @Rule public SystemOutRule systemOutRule = new SystemOutRule().enableLog(); @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); private RetryingWorkExecutorService workExecutorService; @Mock private BonitaExecutorServiceFactory bonitaExecutorServiceFactory; @Mock(lenient = true) private BonitaExecutorService bonitaExecutorService; @Mock private BonitaWork bonitaWork; private final WorkDescriptor workDescriptor = WorkDescriptor.create("myWork"); @Mock private WorkExecutionAuditor workExecutionAuditor; @Mock private IncidentService incidentService; @Mock(lenient = true) private ExceptionRetryabilityEvaluator retryabilityEvaluator; private final FixedEngineClock engineClock = new FixedEngineClock(Instant.EPOCH); private final MeterRegistry meterRegistry = new SimpleMeterRegistry( // So that micrometer updates its counters every 1 ms: k -> k.equals("simple.step") ? Duration.ofMillis(1).toString() : null, Clock.SYSTEM); @Before public void before() throws Exception { doReturn(bonitaExecutorService).when(bonitaExecutorServiceFactory).createExecutorService(any()); doReturn(RETRYABLE).when(retryabilityEvaluator) .evaluateRetryability(argThat(t -> t instanceof SRetryableException)); doReturn(NOT_RETRYABLE).when(retryabilityEvaluator) .evaluateRetryability(argThat(t -> !(t instanceof SRetryableException))); workExecutorService = new RetryingWorkExecutorService( bonitaExecutorServiceFactory, engineClock, WORK_TERMINATION_TIMEOUT, MAX_RETRY, DELAY, DELAY_FACTOR, retryabilityEvaluator, workExecutionAuditor, meterRegistry, incidentService); doReturn(true).when(bonitaExecutorService).awaitTermination(anyLong(), any(TimeUnit.class)); workExecutorService.start(); } @Test public void should_submit_work_on_the_executor() throws Exception { workExecutorService.execute(workDescriptor); verify(bonitaExecutorService).submit(eq(workDescriptor)); } @Test public void should_pause_shutdown_ThreadPool_and_clear_queue() throws InterruptedException, SBonitaException { final InOrder inOrder = inOrder(bonitaExecutorService); // given workExecutorService.start(); // when workExecutorService.pause(); // then inOrder.verify(bonitaExecutorService).shutdownAndEmptyQueue(); inOrder.verify(bonitaExecutorService).clearAllQueues(); inOrder.verify(bonitaExecutorService).awaitTermination(anyLong(), any(TimeUnit.class)); } @Test public void should_stop_shutdown_ThreadPool_and_not_clear_queue() throws InterruptedException { final InOrder inOrder = inOrder(bonitaExecutorService); // given workExecutorService.start(); // when workExecutorService.stop(); // then inOrder.verify(bonitaExecutorService).shutdownAndEmptyQueue(); inOrder.verify(bonitaExecutorService).awaitTermination(anyLong(), any(TimeUnit.class)); } @Test public void pauseShouldStopWorkservice() throws SBonitaException { // given workExecutorService.start(); // when workExecutorService.pause(); // then assertThat(workExecutorService.isStopped()).as("WorkService should be deactivated").isTrue(); } @Test public void resumeShouldAllowToExecuteWork() throws SBonitaException { // given workExecutorService.start(); workExecutorService.pause(); // when workExecutorService.resume(); workExecutorService.execute(workDescriptor); // then verify(bonitaExecutorService, times(1)).submit(eq(workDescriptor)); } @Test public void should_start_do_nothing_when_already_started() { // given workExecutorService.start(); // when workExecutorService.start(); // then: will only be started one time verify(bonitaExecutorServiceFactory, times(1)).createExecutorService(workExecutorService); } @Test public void should_stop_do_nothing_when_already_stopped() { // given workExecutorService.start(); workExecutorService.stop(); // when workExecutorService.stop(); // then: will only be started one time verify(bonitaExecutorService, times(1)).shutdownAndEmptyQueue(); } @Test(expected = SWorkException.class) public void should_pause_throw_exception_on_timeout() throws SWorkException, InterruptedException { // given workExecutorService.start(); doReturn(false).when(bonitaExecutorService).awaitTermination(anyLong(), any(TimeUnit.class)); // when workExecutorService.pause(); // then: exception } @Test public void should_stop_do_not_throw_exception_on_timeout() throws InterruptedException { // given workExecutorService.start(); doReturn(false).when(bonitaExecutorService).awaitTermination(anyLong(), any(TimeUnit.class)); // when workExecutorService.stop(); // then: will only be started one time assertThat(systemOutRule.getLog()).contains("Waited"); } @Test(expected = SWorkException.class) public void should_pause_throw_exception_on_interrupted() throws InterruptedException, SBonitaException { // given workExecutorService.start(); doThrow(InterruptedException.class).when(bonitaExecutorService).awaitTermination(anyLong(), any(TimeUnit.class)); // when workExecutorService.pause(); // then: exception } @Test public void should_stop_do_not_throw_exception_on_interrupted() throws InterruptedException { // given workExecutorService.start(); doThrow(InterruptedException.class).when(bonitaExecutorService).awaitTermination(anyLong(), any(TimeUnit.class)); // when workExecutorService.stop(); // then: will only be started one time assertThat(systemOutRule.getLog()).contains("Interrupted").contains("InterruptedException"); } @Test public void checkStartStatus() { // when workExecutorService.start(); // then assertThat(workExecutorService.isStopped()).isFalse(); } @Test public void checkStopStatus() { // given workExecutorService.start(); // when workExecutorService.stop(); // then assertThat(workExecutorService.isStopped()).isTrue(); } @Test public void should_reexecute_work_when_it_fails_because_of_lock_timeout() { workExecutorService.onFailure(workDescriptor, bonitaWork, Collections.emptyMap(), new LockTimeoutException("lock timeout")); verify(bonitaExecutorService).submit(eq(workDescriptor)); } @Test public void should_warn_and_reexecute_work_when_it_fails_because_of_lock_exception() { workExecutorService.onFailure(workDescriptor, bonitaWork, Collections.emptyMap(), new LockException("lock timeout", new Exception())); verify(bonitaExecutorService).submit(eq(workDescriptor)); assertThat(systemOutRule.getLog()).contains("WARN"); } @Test public void should_log_on_success() { workExecutorService.onSuccess(workDescriptor); assertThat(systemOutRule.getLog()).contains("Completed work "); } @Test public void should_await_specified_time_when_stopping_the_executor() throws Exception { workExecutorService.stop(); verify(bonitaExecutorService).awaitTermination(WORK_TERMINATION_TIMEOUT, TimeUnit.SECONDS); } @Test public void should_ignored_work_because_of_precondition_not_verified() { workExecutorService.onFailure(workDescriptor, bonitaWork, Collections.emptyMap(), new SWorkPreconditionException("My precondition")); verifyNoMoreInteractions(bonitaExecutorService); assertThat(systemOutRule.getLog()).contains("Work was not executed because preconditions were not met,"); } @Test public void should_execute_work_normally() { WorkDescriptor workDescriptor = WorkDescriptor.create("MY_WORK"); workExecutorService.execute(workDescriptor); verify(bonitaExecutorService).submit(eq(workDescriptor)); } @Test public void should_reattempt_work_on_accepted_failure() throws Exception { WorkDescriptor workDescriptor = WorkDescriptor.create("MY_WORK"); workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), new SRetryableException(new Exception("rootCause"))); verify(bonitaExecutorService).submit(eq(workDescriptor)); verify(bonitaWork, never()).handleFailure(nullable(Exception.class), anyMap()); assertThat(workDescriptor.getRetryCount()).isEqualTo(1); } @Test public void should_reattempt_work_on_when_issue_happened_during_commit() throws Exception { WorkDescriptor workDescriptor = WorkDescriptor.create("MY_WORK"); STransactionCommitException duringCommitException = new STransactionCommitException("exception during commit"); doReturn(UNCERTAIN_COMPLETION_OF_COMMIT).when(retryabilityEvaluator) .evaluateRetryability(duringCommitException); workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), duringCommitException); verify(bonitaExecutorService).submit(eq(workDescriptor)); verify(bonitaWork, never()).handleFailure(nullable(Exception.class), anyMap()); assertThat(workDescriptor.getRetryCount()).isEqualTo(1); } @Test public void should_reset_retry_counter_on_non_retryable_failure() { WorkDescriptor workDescriptor = WorkDescriptor.create("A_WORK"); workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), new SRetryableException(new Exception("root_cause"))); assertThat(getNumberOfRetries()).isEqualTo(1); workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), new SRetryableException(new Exception("other_cause"))); assertThat(getNumberOfRetries()) .as("Should not update counter for work that has already be counted").isEqualTo(1); workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), new Exception("not retry-able")); assertThat(getNumberOfRetries()).isEqualTo(0); } @Test public void should_reset_retry_counter_on_precondition_issue() { WorkDescriptor workDescriptor = WorkDescriptor.create("A_WORK"); workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), new SRetryableException(new Exception("root_cause"))); assertThat(getNumberOfRetries()).isEqualTo(1); workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), new SWorkPreconditionException("precondition")); assertThat(getNumberOfRetries()).isEqualTo(0); } @Test public void should_reset_retry_counter_on_success() { WorkDescriptor workDescriptor = WorkDescriptor.create("A_WORK"); workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), new SRetryableException(new Exception("root_cause"))); assertThat(getNumberOfRetries()).isEqualTo(1); workExecutorService.onSuccess(workDescriptor); assertThat(getNumberOfRetries()).isEqualTo(0); } @Test public void should_not_reattempt_work_on_other_failure() throws Exception { WorkDescriptor workDescriptor = WorkDescriptor.create("MY_WORK"); Exception rootCause = new Exception("rootCause"); Map context = emptyMap(); workExecutorService.onFailure(workDescriptor, bonitaWork, context, rootCause); verify(bonitaExecutorService, never()).submit(eq(workDescriptor)); verify(bonitaWork).handleFailure(rootCause, context); assertThat(workDescriptor.getRetryCount()).isEqualTo(0); } @Test public void should_report_incident_when_we_are_unable_to_handle_the_failure_and_the_work_is_not_handled_by_the_recovery() throws Exception { WorkDescriptor workDescriptor = WorkDescriptor.create("MY_WORK"); doThrow(new Exception("HandleFailureError")).when(bonitaWork).handleFailure(any(), anyMap()); Exception rootCause = new Exception("rootCause"); Map context = emptyMap(); workExecutorService.onFailure(workDescriptor, bonitaWork, context, rootCause); verify(bonitaWork).handleFailure(any(), anyMap()); verify(incidentService).report(any()); } @Test public void should_do_nothing_when_we_are_unable_to_handle_the_failure_and_the_work_is_handled_by_the_recovery() throws Exception { WorkDescriptor workDescriptor = WorkDescriptor.create("MY_WORK"); doThrow(new Exception("HandleFailureError")).when(bonitaWork).handleFailure(any(), anyMap()); doReturn(true).when(bonitaWork).canBeRecoveredByTheRecoveryMechanism(); Map context = emptyMap(); workExecutorService.onFailure(workDescriptor, bonitaWork, context, new Exception("rootCause")); verify(bonitaWork).handleFailure(any(), anyMap()); verify(incidentService, never()).report(any()); } @Test public void should_not_reattempt_work_when_retry_count_is_exceeded() throws Exception { WorkDescriptor workDescriptor = WorkDescriptor.create("MY_WORK"); //max retry is 3 workDescriptor.incrementRetryCount(); workDescriptor.incrementRetryCount(); workDescriptor.incrementRetryCount(); workDescriptor.incrementRetryCount(); Map context = emptyMap(); SRetryableException exception = new SRetryableException(new Exception("rootCause")); workExecutorService.onFailure(workDescriptor, bonitaWork, context, exception); verify(bonitaExecutorService, never()).submit(eq(workDescriptor)); verify(bonitaWork).handleFailure(exception, context); } @Test public void should_set_the_date_after_delay_on_work_when_retrying() { WorkDescriptor workDescriptor = WorkDescriptor.create("MY_WORK"); SRetryableException exception = new SRetryableException(new Exception("rootCause")); Instant now = engineClock.now(); workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), exception); verify(bonitaExecutorService).submit(eq(workDescriptor)); assertThat(workDescriptor.getExecutionThreshold()).isAfterOrEqualTo(now.plusMillis(DELAY)); assertThat(workDescriptor.getExecutionThreshold()).isBeforeOrEqualTo(now.plusMillis(DELAY + DELAY)); } @Test public void should_log_the_delay_used_to_retry_the_work() { WorkDescriptor workDescriptor = WorkDescriptor.create("MY_WORK"); SRetryableException exception = new SRetryableException(new Exception("rootCause")); Instant now = engineClock.now(); systemOutRule.clearLog(); workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), exception); verify(bonitaExecutorService).submit(workDescriptor); assertThat(workDescriptor.getExecutionThreshold()).isAfterOrEqualTo(now.plusMillis(DELAY)); assertThat(workDescriptor.getExecutionThreshold()).isBeforeOrEqualTo(now.plusMillis(DELAY + DELAY)); assertThat(systemOutRule.getLog()).contains("It will be retried. Attempt 1 of 4 with a delay of " + Duration.between(now, workDescriptor.getExecutionThreshold()).toMillis()); } @Test public void should_set_the_date_after_delay_and_factor_on_work_when_retrying_multiple_times() { SRetryableException exception = new SRetryableException(new Exception("rootCause")); Instant now = engineClock.now(); workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), exception); workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), exception); workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), exception); verify(bonitaExecutorService, times(3)).submit(eq(workDescriptor)); //date after delay is: delay * factor * factor because there is 3 retry assertThat(workDescriptor.getExecutionThreshold()) .isAfterOrEqualTo(now.plusMillis(DELAY * DELAY_FACTOR * DELAY_FACTOR)); assertThat(workDescriptor.getExecutionThreshold()) .isBeforeOrEqualTo(now.plusMillis(DELAY * DELAY_FACTOR * DELAY_FACTOR * 2)); } @Test public void should_just_requeue_work_when_the_failure_is_a_work_lock_issue() throws Exception { Exception rootCause = new LockTimeoutException("rootCause"); Map context = emptyMap(); workExecutorService.onFailure(workDescriptor, bonitaWork, context, rootCause); verify(bonitaExecutorService).submit(eq(workDescriptor)); verify(bonitaWork, never()).handleFailure(rootCause, context); assertThat(workDescriptor.getRetryCount()).isEqualTo(0); } @Test public void should_abandon_execution_when_failure_is_a_precondition_issue() throws Exception { Exception rootCause = new SWorkPreconditionException("precondition failed"); Map context = emptyMap(); workExecutorService.onFailure(workDescriptor, bonitaWork, context, rootCause); verifyNoInteractions(bonitaExecutorService); verify(bonitaWork, never()).handleFailure(rootCause, emptyMap()); assertThat(workDescriptor.getRetryCount()).isEqualTo(0); } @Test public void should_retry_PreconditionException_when_retryability_is_RETRYABLE() throws Exception { Exception rootCause = new SWorkPreconditionException("precondition failed"); doReturn(RETRYABLE).when(retryabilityEvaluator).evaluateRetryability(rootCause); workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), rootCause); verify(bonitaExecutorService).submit(eq(workDescriptor)); verify(bonitaWork, never()).handleFailure(rootCause, emptyMap()); assertThat(workDescriptor.getRetryCount()).isEqualTo(1); } @Test public void should_retry_PreconditionException_when_retryability_is_UNCERTAIN_COMPLETION_OF_COMMIT() throws Exception { Exception rootCause = new SWorkPreconditionException("precondition failed"); doReturn(UNCERTAIN_COMPLETION_OF_COMMIT).when(retryabilityEvaluator).evaluateRetryability(rootCause); workExecutorService.onFailure(workDescriptor, bonitaWork, emptyMap(), rootCause); verify(bonitaExecutorService).submit(eq(workDescriptor)); verify(bonitaWork, never()).handleFailure(rootCause, emptyMap()); assertThat(workDescriptor.getRetryCount()).isEqualTo(1); } protected double getNumberOfRetries() { return meterRegistry.find(NUMBER_OF_WORKS_RETRIED).gauge().value(); } } ================================================ FILE: services/bonita-work/src/test/java/org/bonitasoft/engine/work/SingleThreadPoolExecutorTest.java ================================================ /** * Copyright (C) 2017-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.work; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import java.time.Duration; import java.time.Instant; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import org.bonitasoft.engine.commons.time.FixedEngineClock; import org.bonitasoft.engine.work.audit.WorkExecutionAuditor; import org.junit.jupiter.api.AfterEach; 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.junit.jupiter.MockitoExtension; /** * Tests for ThreadPoolExecutor with pool size 1 (single thread). * Documents that the pool correctly replaces its worker thread after exceptions, * so that subsequent work is not stuck forever. */ @ExtendWith(MockitoExtension.class) class SingleThreadPoolExecutorTest { private static final long TENANT_ID = 13L; @Mock private WorkExecutionAuditor workExecutionAuditor; private final ResettableWorkExecutionCallback workExecutionCallback = new ResettableWorkExecutionCallback(); private DefaultBonitaExecutorService bonitaExecutorService; private final FixedEngineClock engineClock = new FixedEngineClock(Instant.now()); private final WorkFactory workFactory = new LocalWorkFactory(2); private final SimpleMeterRegistry meterRegistry = new SimpleMeterRegistry( // So that micrometer updates its counters every 1 ms: k -> k.equals("simple.step") ? Duration.ofMillis(1).toString() : null, Clock.SYSTEM); @BeforeEach void setUp() { var threadPoolExecutor = new WorkSingleThreadPoolExecutorFactory.SingleThreadPoolExecutor( new LinkedBlockingQueue<>(10), new WorkerThreadFactory("test-worker", 1)); bonitaExecutorService = new DefaultBonitaExecutorService(threadPoolExecutor, workFactory, engineClock, workExecutionCallback, workExecutionAuditor, meterRegistry); } @AfterEach void tearDown() { bonitaExecutorService.shutdownAndEmptyQueue(); } @Test void submit_should_continue_processing_after_sync_exception() { // Submit failing work bonitaExecutorService.submit(WorkDescriptor.create("EXCEPTION")); await().until(workExecutionCallback::isOnFailureCalled); // Reset and submit normal work — must succeed even with pool size 1 workExecutionCallback.reset(); bonitaExecutorService.submit(WorkDescriptor.create("NORMAL")); await().until(workExecutionCallback::isOnSuccessCalled); } @Test void submit_should_preserve_work_failure_handling() { bonitaExecutorService.submit(WorkDescriptor.create("EXCEPTION")); await().until(workExecutionCallback::isOnFailureCalled); assertThat(workExecutionCallback.getThrown()) .isInstanceOf(Exception.class) .hasMessageContaining("classic exception"); // Verify pool still works workExecutionCallback.reset(); bonitaExecutorService.submit(WorkDescriptor.create("NORMAL")); await().until(workExecutionCallback::isOnSuccessCalled); } @Test void submit_should_maintain_accurate_metrics_after_exception() { // Submit failing work bonitaExecutorService.submit(WorkDescriptor.create("EXCEPTION")); await().until(workExecutionCallback::isOnFailureCalled); // Submit normal work workExecutionCallback.reset(); bonitaExecutorService.submit(WorkDescriptor.create("NORMAL")); await().until(workExecutionCallback::isOnSuccessCalled); assertThat(meterRegistry.find(DefaultBonitaExecutorService.NUMBER_OF_WORKS_EXECUTED).counter().count()) .as("Both works (failed + successful) should be counted as executed") .isEqualTo(2); } @Test void submit_should_continue_processing_after_async_exception() { // Submit async failing work bonitaExecutorService.submit(WorkDescriptor.create("ASYNC_EXCEPTION")); await().until(workExecutionCallback::isOnFailureCalled); // Reset and submit normal work — must succeed even with pool size 1 workExecutionCallback.reset(); bonitaExecutorService.submit(WorkDescriptor.create("NORMAL")); await().until(workExecutionCallback::isOnSuccessCalled); } // ================================================================================================================= // UTILS // ================================================================================================================= private static class ResettableWorkExecutionCallback implements WorkExecutionCallback { private final AtomicBoolean onSuccessCalled = new AtomicBoolean(false); private final AtomicBoolean onFailureCalled = new AtomicBoolean(false); private volatile Throwable thrown; @Override public void onSuccess(WorkDescriptor workDescriptor) { onSuccessCalled.set(true); } @Override public void onFailure(WorkDescriptor work, BonitaWork bonitaWork, Map context, Throwable thrown) { this.thrown = thrown; onFailureCalled.set(true); } boolean isOnSuccessCalled() { return onSuccessCalled.get(); } boolean isOnFailureCalled() { return onFailureCalled.get(); } Throwable getThrown() { return thrown; } void reset() { onSuccessCalled.set(false); onFailureCalled.set(false); thrown = null; } } private static class LocalWorkFactory implements WorkFactory { private final long workSleepPeriodInSeconds; private LocalWorkFactory(long workSleepPeriodInSeconds) { this.workSleepPeriodInSeconds = workSleepPeriodInSeconds; } @Override public BonitaWork create(WorkDescriptor workDescriptor) { return new BonitaWork() { @Override public String getDescription() { return workDescriptor.toString(); } @Override public CompletableFuture work(Map context) throws Exception { switch (workDescriptor.getType()) { case "EXCEPTION": throw new Exception("classic exception"); case "SLEEP": TimeUnit.SECONDS.sleep(workSleepPeriodInSeconds); break; case "ASYNC": return CompletableFuture.supplyAsync(() -> { try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException ignored) { } return null; }, Executors.newSingleThreadExecutor()); case "ASYNC_EXCEPTION": return CompletableFuture.supplyAsync(() -> { try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException ignored) { } throw new CompletionException(new SWorkException("my exception")); }, Executors.newSingleThreadExecutor()); case "NORMAL": default: } return CompletableFuture.completedFuture(null); } @Override public void handleFailure(Throwable e, Map context) { // do nothing } }; } } } ================================================ FILE: services/bonita-work/src/test/java/org/bonitasoft/engine/work/WorkServiceImplTest.java ================================================ /** * 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.work; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; import java.time.Instant; import java.time.temporal.ChronoUnit; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.time.EngineClock; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.transaction.UserTransactionService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class WorkServiceImplTest { private WorkServiceImpl workService; @Mock private UserTransactionService transactionService; @Mock private SessionAccessor sessionAccessor; @Mock private WorkExecutorService workExecutorService; @Mock private EngineClock engineClock; @Before public void before() { workService = new WorkServiceImpl(transactionService, sessionAccessor, workExecutorService, engineClock, 0); } @Test public void should_register_work_set_the_work_registration_instant() throws SBonitaException { Instant registrationInstant = Instant.now().minus(25, ChronoUnit.HOURS); doReturn(registrationInstant).when(engineClock).now(); // given WorkDescriptor workDescriptor = WorkDescriptor.create("MY_WORK"); // when workService.registerWork(workDescriptor); // then assertThat(workDescriptor.getRegistrationDate()).isEqualTo(registrationInstant); } @Test public void should_register_a_new_synchronization_for_each_work() throws SBonitaException { // given WorkDescriptor workDescriptor1 = WorkDescriptor.create("MY_WORK1"); WorkDescriptor workDescriptor2 = WorkDescriptor.create("MY_WORK2"); // when workService.registerWork(workDescriptor1); workService.registerWork(workDescriptor2); // then verify(transactionService).registerBonitaSynchronization( argThat(s -> ((WorkSynchronization) s).getWork().getType().equals("MY_WORK1"))); verify(transactionService).registerBonitaSynchronization( argThat(s -> ((WorkSynchronization) s).getWork().getType().equals("MY_WORK2"))); } } ================================================ FILE: services/bonita-work/src/test/java/org/bonitasoft/engine/work/WorkSynchronizationTest.java ================================================ /** * 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.work; import static javax.transaction.Status.STATUS_COMMITTED; import static javax.transaction.Status.STATUS_ROLLEDBACK; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; import java.util.Optional; import org.bonitasoft.engine.sessionaccessor.SessionAccessor; import org.bonitasoft.engine.transaction.UserTransactionService; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; /** * @author Baptiste Mesta. */ @RunWith(MockitoJUnitRunner.class) public class WorkSynchronizationTest { @Mock private WorkExecutorService workExecutorService; @Mock private SessionAccessor sessionAccessor; @Mock private UserTransactionService userTransactionService; private final WorkDescriptor workDescriptor1 = WorkDescriptor.create("myWork1"); @Test public void should_submit_work_on_commit() { WorkSynchronization workSynchronization = new WorkSynchronization(userTransactionService, workExecutorService, workDescriptor1, 0); workSynchronization.afterCompletion(STATUS_COMMITTED); verify(workExecutorService).execute(workDescriptor1); } @Test public void should_not_submit_work_on_transaction_not_in_committed_state() { WorkSynchronization workSynchronization = new WorkSynchronization(userTransactionService, workExecutorService, workDescriptor1, 0); workSynchronization.afterCompletion(STATUS_ROLLEDBACK); verify(workExecutorService, never()).execute(workDescriptor1); } @Test public void should_not_add_delay_when_the_workDelayOnMultipleXAResource_equal_0() { WorkSynchronization workSynchronization = new WorkSynchronization(userTransactionService, workExecutorService, workDescriptor1, 0); workSynchronization.afterCompletion(STATUS_COMMITTED); assertThat(workDescriptor1.getExecutionThreshold()).isNull(); verify(workExecutorService).execute(workDescriptor1); } @Test public void should_add_delay_when_the_workDelayOnMultipleXAResource_greater_than_0_and_multiple_resources() { WorkSynchronization workSynchronization = new WorkSynchronization(userTransactionService, workExecutorService, workDescriptor1, 10); when(userTransactionService.hasMultipleResources()).thenReturn(Optional.of(true)); workSynchronization.afterCompletion(STATUS_COMMITTED); assertThat(workDescriptor1.getExecutionThreshold()).isNotNull(); verify(workExecutorService).execute(workDescriptor1); } @Test public void should_not_add_delay_when_the_workDelayOnMultipleXAResource_greater_than_0_and_no_multiple_resources() { WorkSynchronization workSynchronization = new WorkSynchronization(userTransactionService, workExecutorService, workDescriptor1, 10); when(userTransactionService.hasMultipleResources()).thenReturn(Optional.of(false)); workSynchronization.afterCompletion(STATUS_COMMITTED); assertThat(workDescriptor1.getExecutionThreshold()).isNull(); verify(workExecutorService).execute(workDescriptor1); } @Test public void should_add_delay_when_the_workDelayOnMultipleXAResource_greater_than_0_and_multiple_resources_not_defined() { WorkSynchronization workSynchronization = new WorkSynchronization(userTransactionService, workExecutorService, workDescriptor1, 10); when(userTransactionService.hasMultipleResources()).thenReturn(Optional.empty()); workSynchronization.afterCompletion(STATUS_COMMITTED); assertThat(workDescriptor1.getExecutionThreshold()).isNotNull(); verify(workExecutorService).execute(workDescriptor1); } } ================================================ FILE: services/bonita-work/src/test/java/org/bonitasoft/engine/work/WorkerThreadFactoryTest.java ================================================ /** * 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.work; import static org.bonitasoft.engine.work.WorkerThreadFactory.guessPadding; import static org.junit.Assert.assertEquals; import org.junit.Test; public class WorkerThreadFactoryTest { @Test public void newThreadStartsWithName() { WorkerThreadFactory threadFactory = new WorkerThreadFactory("foo", 1); Thread newThread = threadFactory.newThread(buildRunnable()); assertEquals("foo-1", newThread.getName()); } @Test public void newThreadNameNumberIsPadded() { WorkerThreadFactory threadFactory = new WorkerThreadFactory("foo", 100); Thread newThread = threadFactory.newThread(buildRunnable()); assertEquals("foo-001", newThread.getName()); } @Test public void testPadding() { assertEquals(1, guessPadding(1)); assertEquals(2, guessPadding(10)); assertEquals(3, guessPadding(100)); assertEquals(4, guessPadding(1000)); } /** * @return */ private Runnable buildRunnable() { return new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }; } } ================================================ FILE: services/bonita-work/src/test/java/org/bonitasoft/engine/work/audit/WorkExecutionAuditorTest.java ================================================ /** * 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.work.audit; import static java.time.Instant.now; import static java.time.temporal.ChronoUnit.*; import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.engine.work.audit.ExecutionStatus.*; import static org.mockito.Mockito.*; import java.time.Instant; import java.util.stream.IntStream; import org.bonitasoft.engine.commons.time.FixedEngineClock; import org.bonitasoft.engine.work.WorkDescriptor; import org.bonitasoft.engine.work.audit.WorkExecutionAuditor.ExecutionCountCheckConfig; import org.bonitasoft.engine.work.audit.WorkExecutionAuditor.RegistrationDurationElapsedCheckConfig; import org.junit.Test; import org.mockito.InOrder; public class WorkExecutionAuditorTest { // ================================================================================================================= // execution status computation // ================================================================================================================= @Test public void should_execution_status_be_ok_when_no_threshold_is_reached() { // given: WorkExecutionAuditor auditor = new WorkExecutionAuditor(new FixedEngineClock(now()), new AuditListener(), new RegistrationDurationElapsedCheckConfig(3, DAYS), new ExecutionCountCheckConfig(50, 3, DAYS)); // when: final ExecutionStatus executionStatus = auditor.executionStatus(workDescriptor(now(), 10)); // then: assertThat(executionStatus).isEqualTo(OK); } @Test public void should_execution_status_be_ko_when_a_large_number_of_executions_have_been_performed() { // given: WorkExecutionAuditor auditor = new WorkExecutionAuditor(new FixedEngineClock(now()), new AuditListener(), new RegistrationDurationElapsedCheckConfig(3, DAYS), new ExecutionCountCheckConfig(50, 3, MILLIS)); // when: final ExecutionStatus executionStatus = auditor.executionStatus(workDescriptor(now().minus(12, MINUTES), 200)); // then: assertThat(executionStatus).isEqualTo(TOO_MANY_EXECUTIONS); } @Test public void should_execution_status_be_ok_when_a_large_number_of_executions_have_been_performed_but_execution_count_duration_threshold_is_not_elapsed() { // given: WorkExecutionAuditor auditor = new WorkExecutionAuditor(new FixedEngineClock(now()), new AuditListener(), new RegistrationDurationElapsedCheckConfig(3, DAYS), new ExecutionCountCheckConfig(50, 3, HOURS)); // when: final ExecutionStatus executionStatus = auditor.executionStatus(workDescriptor(now().minus(12, MINUTES), 200)); // then: assertThat(executionStatus).isEqualTo(OK); } @Test public void should_execution_status_be_ko_when_too_much_time_elapsed_since_work_registration() { // given: final Instant registrationDate = now().minus(12, MINUTES); WorkExecutionAuditor auditor = new WorkExecutionAuditor(new FixedEngineClock(registrationDate.plusSeconds(250)), new AuditListener(), new RegistrationDurationElapsedCheckConfig(30, MILLIS), new ExecutionCountCheckConfig(50, 3, DAYS)); // when: final ExecutionStatus executionStatus = auditor.executionStatus(workDescriptor(registrationDate, 1)); // then: assertThat(executionStatus).isEqualTo(TOO_MUCH_TIME_ELAPSED_SINCE_REGISTRATION); } // ================================================================================================================= // detection of abnormal execution // ================================================================================================================= @Test public void should_trigger_listener_when_execution_status_is_abnormal() { //given: final AuditListener auditListener = mock(AuditListener.class); WorkExecutionAuditor auditor = auditorDetectingTooMuchExecutions(auditListener); final WorkDescriptor work = workDescriptor(now().minus(12, MINUTES), 10_000); //when: auditor.detectAbnormalExecutionAndNotify(work); //then: final InOrder inOrder = inOrder(auditListener); inOrder.verify(auditListener).detectionStarted(work); inOrder.verify(auditListener).abnormalExecutionStatusDetected(work, TOO_MANY_EXECUTIONS); inOrder.verifyNoMoreInteractions(); } @Test public void should_not_trigger_listener_when_work_has_already_been_detected_with_abnormal_execution() { //given: final AuditListener auditListener = mock(AuditListener.class); WorkExecutionAuditor auditor = auditorDetectingTooMuchExecutions(auditListener); final WorkDescriptor work = workDescriptor(); //when: auditor.detectAbnormalExecutionAndNotify(work); //then: final InOrder inOrder1stRun = inOrder(auditListener); inOrder1stRun.verify(auditListener).detectionStarted(work); inOrder1stRun.verify(auditListener).abnormalExecutionStatusDetected(work, TOO_MANY_EXECUTIONS); inOrder1stRun.verifyNoMoreInteractions(); //when: reset(auditListener); // forget about previous calls auditor.detectAbnormalExecutionAndNotify(work); //then: final InOrder inOrder2ndRun = inOrder(auditListener); inOrder2ndRun.verify(auditListener).detectionStarted(work); inOrder2ndRun.verifyNoMoreInteractions(); } // ================================================================================================================= // notify success // ================================================================================================================= @Test public void should_not_notify_success_when_execution_is_normal() { //given: final AuditListener auditListener = mock(AuditListener.class); final WorkDescriptor work = abnormallyExecutedWorkDescriptor(); //when: auditorDetectingTooMuchExecutions(auditListener).notifySuccess(work); //then: verify(auditListener).success(work); verifyNoMoreInteractions(auditListener); } @Test public void should_notify_success_when_execution_is_normal() { //given: final AuditListener auditListener = mock(AuditListener.class); //when: auditorDetectingTooMuchExecutions(auditListener).notifySuccess(workDescriptor()); //then: verifyNoMoreInteractions(auditListener); } // ================================================================================================================= // UTILS // ================================================================================================================= private static WorkDescriptor workDescriptor(Instant registrationDate, int executionCount) { WorkDescriptor workDescriptor = WorkDescriptor.create("NORMAL"); workDescriptor.setRegistrationDate(registrationDate); IntStream.rangeClosed(1, executionCount).forEach(i -> workDescriptor.incrementExecutionCount()); return workDescriptor; } private static WorkDescriptor abnormallyExecutedWorkDescriptor() { return workDescriptor().abnormalExecutionDetected(); } private static WorkDescriptor workDescriptor() { return workDescriptor(now().minus(10, SECONDS), 3); } private static WorkExecutionAuditor auditorDetectingTooMuchExecutions(AuditListener auditListener) { return new WorkExecutionAuditor( new FixedEngineClock(now().plus(250, DAYS)), auditListener, new RegistrationDurationElapsedCheckConfig(30, MILLIS), new ExecutionCountCheckConfig(0, 3, MILLIS)); } } ================================================ FILE: services/bonita-work/src/test/resources/logback-test.xml ================================================ |%d{HH:mm:ss.SSS}|%thread|%-5level|%logger{16}| %msg%n ================================================ FILE: settings.gradle ================================================ rootProject.name = 'bonita-engine' apply from: 'engine-settings.gradle'